summaryrefslogtreecommitdiff
path: root/chromium/components
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2022-09-07 13:12:05 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2022-11-09 10:02:59 +0000
commit33fc33aa94d4add0878ec30dc818e34e1dd3cc2a (patch)
treef6af110909c79b2759136554f1143d8b0572af0a /chromium/components
parent7d2c5d177e9813077a621df8d18c0deda73099b3 (diff)
downloadqtwebengine-chromium-33fc33aa94d4add0878ec30dc818e34e1dd3cc2a.tar.gz
BASELINE: Update Chromium to 104.0.5112.120
Change-Id: I5d2726c2ab018d75d055739b6ba64317904f05bb Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/438935 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components')
-rw-r--r--chromium/components/BUILD.gn53
-rw-r--r--chromium/components/OWNERS1
-rw-r--r--chromium/components/about_ui/PRESUBMIT.py27
-rw-r--r--chromium/components/about_ui/android/BUILD.gn5
-rw-r--r--chromium/components/access_code_cast/common/access_code_cast_metrics.cc49
-rw-r--r--chromium/components/access_code_cast/common/access_code_cast_metrics.h74
-rw-r--r--chromium/components/access_code_cast/common/access_code_cast_metrics_unittest.cc120
-rw-r--r--chromium/components/account_manager_core/account_manager_facade.h25
-rw-r--r--chromium/components/account_manager_core/account_manager_facade_impl.cc18
-rw-r--r--chromium/components/account_manager_core/account_manager_facade_impl.h3
-rw-r--r--chromium/components/account_manager_core/account_manager_facade_impl_unittest.cc5
-rw-r--r--chromium/components/account_manager_core/mock_account_manager_facade.h2
-rw-r--r--chromium/components/accuracy_tips/accuracy_service.cc11
-rw-r--r--chromium/components/accuracy_tips/accuracy_web_contents_observer.cc5
-rw-r--r--chromium/components/android_autofill/browser/BUILD.gn2
-rw-r--r--chromium/components/android_autofill/browser/android_autofill_manager.cc61
-rw-r--r--chromium/components/android_autofill/browser/android_autofill_manager.h61
-rw-r--r--chromium/components/android_autofill/browser/autofill_provider.h14
-rw-r--r--chromium/components/android_autofill/browser/autofill_provider_android.cc7
-rw-r--r--chromium/components/android_autofill/browser/autofill_provider_android.h5
-rw-r--r--chromium/components/android_autofill/browser/autofill_provider_unittest.cc23
-rw-r--r--chromium/components/android_autofill/browser/form_field_data_android.cc2
-rw-r--r--chromium/components/android_autofill/browser/test_autofill_provider.h14
-rw-r--r--chromium/components/android_autofill/browser/test_support/BUILD.gn2
-rw-r--r--chromium/components/android_autofill/browser/test_support/autofill_provider_test_helper.cc2
-rw-r--r--chromium/components/android_system_error_page/error_page_populator.cc4
-rw-r--r--chromium/components/apdu/apdu_command.cc2
-rw-r--r--chromium/components/app_restore/BUILD.gn3
-rw-r--r--chromium/components/app_restore/DEPS1
-rw-r--r--chromium/components/app_restore/app_launch_info.h7
-rw-r--r--chromium/components/app_restore/app_restore_data.cc5
-rw-r--r--chromium/components/app_restore/app_restore_data.h7
-rw-r--r--chromium/components/app_restore/app_restore_utils.cc26
-rw-r--r--chromium/components/app_restore/app_restore_utils.h10
-rw-r--r--chromium/components/app_restore/desk_template_read_handler.cc4
-rw-r--r--chromium/components/app_restore/features.cc7
-rw-r--r--chromium/components/app_restore/features.h6
-rw-r--r--chromium/components/app_restore/full_restore_read_and_save_unittest.cc41
-rw-r--r--chromium/components/app_restore/full_restore_read_handler.cc13
-rw-r--r--chromium/components/app_restore/full_restore_read_handler.h5
-rw-r--r--chromium/components/app_restore/full_restore_save_handler.cc12
-rw-r--r--chromium/components/app_restore/full_restore_save_handler.h9
-rw-r--r--chromium/components/app_restore/lacros_read_handler.cc18
-rw-r--r--chromium/components/app_restore/lacros_read_handler.h8
-rw-r--r--chromium/components/app_restore/lacros_save_handler.cc33
-rw-r--r--chromium/components/app_restore/lacros_save_handler.h8
-rw-r--r--chromium/components/app_restore/restore_data_unittest.cc91
-rw-r--r--chromium/components/app_restore/tab_group_info.cc62
-rw-r--r--chromium/components/app_restore/tab_group_info.h65
-rw-r--r--chromium/components/app_restore/window_properties.cc2
-rw-r--r--chromium/components/app_restore/window_properties.h4
-rw-r--r--chromium/components/arc/BUILD.gn3
-rw-r--r--chromium/components/arc/DEPS2
-rw-r--r--chromium/components/arc/common/BUILD.gn3
-rw-r--r--chromium/components/arc/common/intent_helper/arc_intent_helper_mojo_delegate.h2
-rw-r--r--chromium/components/assist_ranker/base_predictor_unittest.cc49
-rw-r--r--chromium/components/assist_ranker/predictor_config_definitions.cc35
-rw-r--r--chromium/components/assist_ranker/ranker_example_util.cc2
-rw-r--r--chromium/components/autofill/DEPS2
-rw-r--r--chromium/components/autofill/OWNERS6
-rw-r--r--chromium/components/autofill/PRESUBMIT.py25
-rw-r--r--chromium/components/autofill/android/BUILD.gn1
-rw-r--r--chromium/components/autofill/android/OWNERS1
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver.cc179
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver.h112
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_factory.cc74
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_factory.h49
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.cc11
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.h9
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc238
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc91
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_router.cc38
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_router.h9
-rw-r--r--chromium/components/autofill/content/browser/form_forest_test_api.h3
-rw-r--r--chromium/components/autofill/content/browser/form_forest_unittest.cc2
-rw-r--r--chromium/components/autofill/content/common/mojom/autofill_agent.mojom6
-rw-r--r--chromium/components/autofill/content/common/mojom/autofill_driver.mojom26
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.cc133
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.h26
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc42
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.cc538
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.h37
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc395
-rw-r--r--chromium/components/autofill/content/renderer/form_cache.cc202
-rw-r--r--chromium/components/autofill/content/renderer/form_cache.h48
-rw-r--r--chromium/components/autofill/content/renderer/form_cache_browsertest.cc164
-rw-r--r--chromium/components/autofill/content/renderer/form_cache_test_api.h8
-rw-r--r--chromium/components/autofill/content/renderer/form_tracker.cc4
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.cc122
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.h11
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.cc13
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.cc52
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.h6
-rw-r--r--chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc2
-rw-r--r--chromium/components/autofill/core/browser/BUILD.gn72
-rw-r--r--chromium/components/autofill/core/browser/address_normalizer.h2
-rw-r--r--chromium/components/autofill/core/browser/address_normalizer_impl.cc5
-rw-r--r--chromium/components/autofill/core/browser/address_profile_save_manager.cc7
-rw-r--r--chromium/components/autofill/core/browser/autocomplete_history_manager.cc48
-rw-r--r--chromium/components/autofill/core/browser/autocomplete_history_manager.h58
-rw-r--r--chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc167
-rw-r--r--chromium/components/autofill/core/browser/autofill_ablation_study.cc10
-rw-r--r--chromium/components/autofill/core/browser/autofill_ablation_study.h3
-rw-r--r--chromium/components/autofill/core/browser/autofill_address_policy_handler.cc2
-rw-r--r--chromium/components/autofill/core/browser/autofill_address_util.cc70
-rw-r--r--chromium/components/autofill/core/browser/autofill_address_util.h3
-rw-r--r--chromium/components/autofill/core/browser/autofill_address_util_unittest.cc53
-rw-r--r--chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html12
-rw-r--r--chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js5
-rw-r--r--chromium/components/autofill/core/browser/autofill_client.cc10
-rw-r--r--chromium/components/autofill/core/browser/autofill_client.h26
-rw-r--r--chromium/components/autofill/core/browser/autofill_credit_card_policy_handler.cc2
-rw-r--r--chromium/components/autofill/core/browser/autofill_data_util.cc2
-rw-r--r--chromium/components/autofill/core/browser/autofill_data_util.h2
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_manager.cc31
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc56
-rw-r--r--chromium/components/autofill/core/browser/autofill_driver.h37
-rw-r--r--chromium/components/autofill/core/browser/autofill_experiments.cc78
-rw-r--r--chromium/components/autofill/core/browser/autofill_experiments_unittest.cc70
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.cc43
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.h2
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc193
-rw-r--r--chromium/components/autofill/core/browser/autofill_field.cc32
-rw-r--r--chromium/components/autofill/core/browser/autofill_field.h14
-rw-r--r--chromium/components/autofill/core/browser/autofill_form_test_utils.cc1
-rw-r--r--chromium/components/autofill/core/browser/autofill_form_test_utils.h1
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.cc71
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.h109
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager_unittest.cc142
-rw-r--r--chromium/components/autofill/core/browser/autofill_merge_unittest.cc9
-rw-r--r--chromium/components/autofill/core/browser/autofill_policy_handler.cc6
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_import_process.cc2
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_save_strike_database_unittest.cc6
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_update_strike_database_unittest.cc6
-rw-r--r--chromium/components/autofill/core/browser/autofill_regexes.cc17
-rw-r--r--chromium/components/autofill/core/browser/autofill_regexes.h7
-rw-r--r--chromium/components/autofill/core/browser/autofill_regexes_unittest.cc39
-rw-r--r--chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc13
-rw-r--r--chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h4
-rw-r--r--chromium/components/autofill/core/browser/autofill_subject.cc2
-rw-r--r--chromium/components/autofill/core/browser/autofill_subject.h4
-rw-r--r--chromium/components/autofill/core/browser/autofill_suggestion_generator.cc224
-rw-r--r--chromium/components/autofill/core/browser/autofill_suggestion_generator.h43
-rw-r--r--chromium/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc108
-rw-r--r--chromium/components/autofill/core/browser/autofill_test_utils.cc94
-rw-r--r--chromium/components/autofill/core/browser/autofill_test_utils.h15
-rw-r--r--chromium/components/autofill/core/browser/autofill_type.cc2
-rw-r--r--chromium/components/autofill/core/browser/browser_autofill_manager.cc447
-rw-r--r--chromium/components/autofill/core/browser/browser_autofill_manager.h237
-rw-r--r--chromium/components/autofill/core/browser/browser_autofill_manager_unittest.cc831
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_data_model.cc38
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_data_model.h27
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_data_model_unittest.cc137
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_offer_data.cc135
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_offer_data.h122
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_profile.cc38
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_profile.h4
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc62
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.h3
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc43
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_structured_address.h2
-rw-r--r--chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.h4
-rw-r--r--chromium/components/autofill/core/browser/data_model/contact_info.cc3
-rw-r--r--chromium/components/autofill/core/browser/data_model/credit_card.cc30
-rw-r--r--chromium/components/autofill/core/browser/data_model/credit_card.h5
-rw-r--r--chromium/components/autofill/core/browser/data_model/credit_card_test_api.h3
-rw-r--r--chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc77
-rw-r--r--chromium/components/autofill/core/browser/data_model/data_model_utils.cc6
-rw-r--r--chromium/components/autofill/core/browser/data_model/phone_number.cc54
-rw-r--r--chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc124
-rw-r--r--chromium/components/autofill/core/browser/data_model/test_autofill_data_model.cc35
-rw-r--r--chromium/components/autofill/core/browser/data_model/test_autofill_data_model.h37
-rw-r--r--chromium/components/autofill/core/browser/field_filler.cc53
-rw-r--r--chromium/components/autofill/core/browser/field_filler.h20
-rw-r--r--chromium/components/autofill/core/browser/field_filler_unittest.cc487
-rw-r--r--chromium/components/autofill/core/browser/field_types.cc10
-rw-r--r--chromium/components/autofill/core/browser/field_types.h7
-rw-r--r--chromium/components/autofill/core/browser/field_types_unittest.cc2
-rw-r--r--chromium/components/autofill/core/browser/form_data_importer.cc444
-rw-r--r--chromium/components/autofill/core/browser/form_data_importer.h92
-rw-r--r--chromium/components/autofill/core/browser/form_data_importer_unittest.cc380
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/address_field.cc148
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/address_field.h48
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/address_field_unittest.cc66
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc58
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/credit_card_field.h10
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc437
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/email_field.cc3
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/email_field.h1
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/field_candidates.cc20
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/field_candidates.h18
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/form_field.cc56
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/form_field.h6
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/form_field_unittest.cc96
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc3
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h1
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc35
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/name_field.cc105
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/name_field.h1
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/name_field_unittest.cc62
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc33
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.h26
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/phone_field.cc372
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/phone_field.h44
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc639
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/price_field.cc3
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/price_field.h1
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/price_field_unittest.cc18
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/regex_patterns.cc82
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/regex_patterns.h42
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc319
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json (renamed from chromium/components/autofill/core/browser/form_parsing/resources/regex_patterns.json)16
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/search_field.cc3
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/search_field.h1
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/search_field_unittest.cc18
-rwxr-xr-xchromium/components/autofill/core/browser/form_parsing/transpile_regex_patterns.py269
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/travel_field.cc9
-rw-r--r--chromium/components/autofill/core/browser/form_parsing/travel_field.h1
-rw-r--r--chromium/components/autofill/core/browser/form_processing/name_processing_util.cc2
-rw-r--r--chromium/components/autofill/core/browser/form_structure.cc593
-rw-r--r--chromium/components/autofill/core/browser/form_structure.h49
-rw-r--r--chromium/components/autofill/core/browser/form_structure_process_query_response_fuzzer.cc5
-rw-r--r--chromium/components/autofill/core/browser/form_structure_test_api.h69
-rw-r--r--chromium/components/autofill/core/browser/form_structure_unittest.cc153
-rw-r--r--chromium/components/autofill/core/browser/geo/address_i18n.cc20
-rw-r--r--chromium/components/autofill/core/browser/geo/address_i18n.h3
-rw-r--r--chromium/components/autofill/core/browser/geo/address_i18n_unittest.cc43
-rw-r--r--chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc6
-rw-r--r--chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h7
-rw-r--r--chromium/components/autofill/core/browser/geo/autofill_country.cc53
-rw-r--r--chromium/components/autofill/core/browser/geo/autofill_country.h37
-rw-r--r--chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc32
-rw-r--r--chromium/components/autofill/core/browser/geo/country_names_for_locale.cc3
-rw-r--r--chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.cc4
-rw-r--r--chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.h4
-rw-r--r--chromium/components/autofill/core/browser/geo/phone_number_i18n.cc17
-rw-r--r--chromium/components/autofill/core/browser/geo/phone_number_i18n.h4
-rw-r--r--chromium/components/autofill/core/browser/geo/region_data_loader_impl.h2
-rw-r--r--chromium/components/autofill/core/browser/geo/test_region_data_loader.cc4
-rw-r--r--chromium/components/autofill/core/browser/geo/test_region_data_loader.h13
-rw-r--r--chromium/components/autofill/core/browser/logging/log_router.cc2
-rw-r--r--chromium/components/autofill/core/browser/merchant_promo_code_manager.cc171
-rw-r--r--chromium/components/autofill/core/browser/merchant_promo_code_manager.h111
-rw-r--r--chromium/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc436
-rw-r--r--chromium/components/autofill/core/browser/metrics/autofill_metrics.cc171
-rw-r--r--chromium/components/autofill/core/browser/metrics/autofill_metrics.h91
-rw-r--r--chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc331
-rw-r--r--chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.h123
-rw-r--r--chromium/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc4223
-rw-r--r--chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc29
-rw-r--r--chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h1
-rw-r--r--chromium/components/autofill/core/browser/metrics/form_events/form_events.h4
-rw-r--r--chromium/components/autofill/core/browser/metrics/form_interactions_counter.cc31
-rw-r--r--chromium/components/autofill/core/browser/metrics/form_interactions_counter.h44
-rw-r--r--chromium/components/autofill/core/browser/metrics/payments/manage_cards_prompt_metrics.cc20
-rw-r--r--chromium/components/autofill/core/browser/metrics/payments/manage_cards_prompt_metrics.h30
-rw-r--r--chromium/components/autofill/core/browser/metrics/payments/offers_metrics.cc89
-rw-r--r--chromium/components/autofill/core/browser/metrics/payments/offers_metrics.h78
-rw-r--r--chromium/components/autofill/core/browser/metrics/payments/offers_metrics_unittest.cc56
-rw-r--r--chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc27
-rw-r--r--chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h12
-rw-r--r--chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.cc93
-rw-r--r--chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.h38
-rw-r--r--chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics_unittest.cc216
-rw-r--r--chromium/components/autofill/core/browser/mock_autocomplete_history_manager.h12
-rw-r--r--chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.cc13
-rw-r--r--chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.h52
-rw-r--r--chromium/components/autofill/core/browser/mock_single_field_form_fill_router.cc6
-rw-r--r--chromium/components/autofill/core/browser/mock_single_field_form_fill_router.h38
-rw-r--r--chromium/components/autofill/core/browser/payments/OWNERS1
-rw-r--r--chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc27
-rw-r--r--chromium/components/autofill/core/browser/payments/autofill_offer_manager.h4
-rw-r--r--chromium/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc94
-rw-r--r--chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.cc2
-rw-r--r--chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.h2
-rw-r--r--chromium/components/autofill/core/browser/payments/card_unmask_challenge_option.h6
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc90
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_access_manager.h19
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc285
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h5
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc72
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h50
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc17
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc2
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc32
-rw-r--r--chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc345
-rw-r--r--chromium/components/autofill/core/browser/payments/full_card_request.cc11
-rw-r--r--chromium/components/autofill/core/browser/payments/full_card_request.h5
-rw-r--r--chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc2
-rw-r--r--chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc6
-rw-r--r--chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc195
-rw-r--r--chromium/components/autofill/core/browser/payments/offer_notification_handler.cc6
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_client.cc839
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_client.h148
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_client_unittest.cc42
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request.cc13
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request_unittest.cc40
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc98
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.h54
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.cc179
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.h76
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.cc141
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.h59
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/opt_change_request.cc122
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/opt_change_request.h52
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/payments_request.cc110
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/payments_request.h43
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc22
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc220
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.h52
-rw-r--r--chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc28
-rw-r--r--chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h12
-rw-r--r--chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.cc2
-rw-r--r--chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.h5
-rw-r--r--chromium/components/autofill/core/browser/payments/test_legal_message_line.h10
-rw-r--r--chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.cc8
-rw-r--r--chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.h24
-rw-r--r--chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc309
-rw-r--r--chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h64
-rw-r--r--chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc424
-rw-r--r--chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.cc12
-rw-r--r--chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.h2
-rw-r--r--chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database_unittest.cc6
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.cc68
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.h15
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_cleaner.cc25
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_cleaner.h6
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_observer.h6
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_unittest.cc196
-rw-r--r--chromium/components/autofill/core/browser/rationalization_util.cc4
-rw-r--r--chromium/components/autofill/core/browser/single_field_form_fill_router.cc111
-rw-r--r--chromium/components/autofill/core/browser/single_field_form_fill_router.h51
-rw-r--r--chromium/components/autofill/core/browser/single_field_form_fill_router_unittest.cc260
-rw-r--r--chromium/components/autofill/core/browser/single_field_form_filler.cc15
-rw-r--r--chromium/components/autofill/core/browser/single_field_form_filler.h65
-rw-r--r--chromium/components/autofill/core/browser/strike_database_integrator_base.cc42
-rw-r--r--chromium/components/autofill/core/browser/strike_database_integrator_base.h27
-rw-r--r--chromium/components/autofill/core/browser/strike_database_integrator_test_strike_database_unittest.cc55
-rw-r--r--chromium/components/autofill/core/browser/suggestions_context.cc13
-rw-r--r--chromium/components/autofill/core/browser/suggestions_context.h68
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_client.cc23
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_client.h34
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_driver.cc16
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_driver.h15
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_external_delegate.cc5
-rw-r--r--chromium/components/autofill/core/browser/test_browser_autofill_manager.cc76
-rw-r--r--chromium/components/autofill/core/browser/test_browser_autofill_manager.h35
-rw-r--r--chromium/components/autofill/core/browser/test_form_structure.cc31
-rw-r--r--chromium/components/autofill/core/browser/test_form_structure.h14
-rw-r--r--chromium/components/autofill/core/browser/test_utils/test_profiles.cc2
-rw-r--r--chromium/components/autofill/core/browser/test_utils/test_profiles.h10
-rw-r--r--chromium/components/autofill/core/browser/touch_to_fill_delegate.cc19
-rw-r--r--chromium/components/autofill/core/browser/touch_to_fill_delegate.h31
-rw-r--r--chromium/components/autofill/core/browser/ui/accessory_sheet_data.cc12
-rw-r--r--chromium/components/autofill/core/browser/ui/accessory_sheet_data.h18
-rw-r--r--chromium/components/autofill/core/browser/ui/address_combobox_model.cc10
-rw-r--r--chromium/components/autofill/core/browser/ui/address_combobox_model.h5
-rw-r--r--chromium/components/autofill/core/browser/ui/autofill_image_fetcher.cc10
-rw-r--r--chromium/components/autofill/core/browser/ui/autofill_popup_delegate.h10
-rw-r--r--chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.cc37
-rw-r--r--chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h11
-rw-r--r--chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc20
-rw-r--r--chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc37
-rw-r--r--chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h7
-rw-r--r--chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc22
-rw-r--r--chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc6
-rw-r--r--chromium/components/autofill/core/browser/ui/payments/virtual_card_enroll_bubble_controller.h13
-rw-r--r--chromium/components/autofill/core/browser/ui/popup_item_ids.h7
-rw-r--r--chromium/components/autofill/core/browser/ui/region_combobox_model.cc10
-rw-r--r--chromium/components/autofill/core/browser/ui/region_combobox_model.h5
-rw-r--r--chromium/components/autofill/core/browser/ui/suggestion.cc47
-rw-r--r--chromium/components/autofill/core/browser/ui/suggestion.h80
-rw-r--r--chromium/components/autofill/core/browser/ui/suggestion_selection.cc56
-rw-r--r--chromium/components/autofill/core/browser/ui/suggestion_selection.h8
-rw-r--r--chromium/components/autofill/core/browser/ui/suggestion_selection_unittest.cc72
-rw-r--r--chromium/components/autofill/core/browser/ui/suggestion_test_helpers.h8
-rw-r--r--chromium/components/autofill/core/browser/validation.cc3
-rw-r--r--chromium/components/autofill/core/browser/webdata/OWNERS3
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc6
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc50
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h55
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc91
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h2
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc34
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.cc70
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.h2
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc143
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc1
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc4
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h2
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_util.cc2
-rw-r--r--chromium/components/autofill/core/common/BUILD.gn1
-rw-r--r--chromium/components/autofill/core/common/aliases.h20
-rw-r--r--chromium/components/autofill/core/common/autofill_clock.cc1
-rw-r--r--chromium/components/autofill/core/common/autofill_features.cc178
-rw-r--r--chromium/components/autofill/core/common/autofill_features.h59
-rw-r--r--chromium/components/autofill/core/common/autofill_internals/log_message.h5
-rw-r--r--chromium/components/autofill/core/common/autofill_internals/logging_scope.h4
-rw-r--r--chromium/components/autofill/core/common/autofill_payments_features.cc81
-rw-r--r--chromium/components/autofill/core/common/autofill_payments_features.h14
-rw-r--r--chromium/components/autofill/core/common/autofill_prefs.cc24
-rw-r--r--chromium/components/autofill/core/common/autofill_prefs.h4
-rw-r--r--chromium/components/autofill/core/common/autofill_tick_clock.cc1
-rw-r--r--chromium/components/autofill/core/common/form_data.cc57
-rw-r--r--chromium/components/autofill/core/common/form_data.h11
-rw-r--r--chromium/components/autofill/core/common/form_field_data.cc8
-rw-r--r--chromium/components/autofill/core/common/form_field_data.h20
-rw-r--r--chromium/components/autofill/core/common/logging/log_buffer.cc30
-rw-r--r--chromium/components/autofill/core/common/mojom/BUILD.gn5
-rw-r--r--chromium/components/autofill/core/common/mojom/autofill_types.mojom35
-rw-r--r--chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc11
-rw-r--r--chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h22
-rw-r--r--chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc26
-rw-r--r--chromium/components/autofill/core/common/password_generation_util.cc2
-rw-r--r--chromium/components/autofill/core/common/password_generation_util.h4
-rw-r--r--chromium/components/autofill/core/common/save_password_progress_logger.cc14
-rw-r--r--chromium/components/autofill/core/common/signatures.cc4
-rw-r--r--chromium/components/autofill/core/common/signatures.h4
-rw-r--r--chromium/components/autofill/ios/OWNERS1
-rw-r--r--chromium/components/autofill/ios/browser/BUILD.gn10
-rw-r--r--chromium/components/autofill/ios/browser/autofill_agent.mm9
-rw-r--r--chromium/components/autofill/ios/browser/autofill_agent_unittests.mm6
-rw-r--r--chromium/components/autofill/ios/browser/autofill_driver_ios.h19
-rw-r--r--chromium/components/autofill/ios/browser/autofill_driver_ios.mm14
-rw-r--r--chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.h28
-rw-r--r--chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.mm16
-rw-r--r--chromium/components/autofill/ios/browser/autofill_java_script_feature.mm2
-rw-r--r--chromium/components/autofill/ios/browser/resources/autofill_controller.js10
-rw-r--r--chromium/components/autofill/ios/browser/resources/suggestion_controller.js33
-rw-r--r--chromium/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm2
-rw-r--r--chromium/components/autofill/ios/form_util/BUILD.gn14
-rw-r--r--chromium/components/autofill/ios/form_util/form_handlers_java_script_feature.mm2
-rw-r--r--chromium/components/autofill/ios/form_util/form_util_java_script_feature.mm4
-rw-r--r--chromium/components/autofill/ios/form_util/resources/fill.js72
-rw-r--r--chromium/components/autofill/ios/form_util/resources/form.js6
-rw-r--r--chromium/components/autofill/ios/form_util/resources/form_handlers.js8
-rw-r--r--chromium/components/autofill_assistant/android/BUILD.gn11
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_circle_background.xml2
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_rounded_corner_background.xml2
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter.xml4
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter_input.xml2
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_header.xml2
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_loading_spinner.xml16
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_section_title.xml4
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/res/values-v17/dimens.xml3
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/android_chrome_autofill_assistant_strings.grd3
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/android_chrome_autofill_assistant_strings_grd/IDS_AUTOFILL_ASSISTANT_TTS_BUTTON.png.sha11
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_af.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_am.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ar.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_as.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_az.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_be.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bg.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bn.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bs.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ca.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cs.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cy.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_da.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_de.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_el.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_en-GB.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es-419.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_et.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_eu.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fa.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fi.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fil.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr-CA.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gl.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gu.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hi.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hr.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hu.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hy.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_id.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_is.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_it.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_iw.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ja.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ka.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kk.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_km.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kn.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ko.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ky.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lo.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lt.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lv.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mk.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ml.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mn.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mr.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ms.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_my.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ne.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_nl.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_no.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_or.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pa.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pl.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-BR.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-PT.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ro.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ru.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_si.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sk.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sl.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sq.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr-Latn.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sv.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sw.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ta.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_te.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_th.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_tr.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uk.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ur.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uz.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_vi.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-CN.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-HK.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-TW.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zu.xtb1
-rw-r--r--chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantAutofillCreditCard.java10
-rw-r--r--chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantBrowserControls.java5
-rw-r--r--chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantContactEditorAccount.java76
-rw-r--r--chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantDependencies.java5
-rw-r--r--chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantStaticDependencies.java4
-rw-r--r--chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandler.java6
-rw-r--r--chromium/components/autofill_assistant/browser/BUILD.gn51
-rw-r--r--chromium/components/autofill_assistant/browser/DEPS8
-rw-r--r--chromium/components/autofill_assistant/browser/action_value.proto35
-rw-r--r--chromium/components/autofill_assistant/browser/actions/action.cc9
-rw-r--r--chromium/components/autofill_assistant/browser/actions/action.h1
-rw-r--r--chromium/components/autofill_assistant/browser/actions/action_delegate.h36
-rw-r--r--chromium/components/autofill_assistant/browser/actions/action_delegate_util.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/action_delegate_util.h3
-rw-r--r--chromium/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc4
-rw-r--r--chromium/components/autofill_assistant/browser/actions/action_test_utils.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/action_test_utils.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/check_element_tag_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/check_element_tag_action_unittest.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/check_option_element_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/check_option_element_action_unittest.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc121
-rw-r--r--chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h10
-rw-r--r--chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc373
-rw-r--r--chromium/components/autofill_assistant/browser/actions/execute_js_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/execute_js_action_unittest.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/external_action.cc201
-rw-r--r--chromium/components/autofill_assistant/browser/actions/external_action.h80
-rw-r--r--chromium/components/autofill_assistant/browser/actions/external_action_unittest.cc359
-rw-r--r--chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/get_element_status_action.cc3
-rw-r--r--chromium/components/autofill_assistant/browser/actions/get_element_status_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc3
-rw-r--r--chromium/components/autofill_assistant/browser/actions/js_flow_action.cc13
-rw-r--r--chromium/components/autofill_assistant/browser/actions/js_flow_action_unittest.cc44
-rw-r--r--chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h23
-rw-r--r--chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action_unittest.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc107
-rw-r--r--chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action.cc49
-rw-r--r--chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action.h35
-rw-r--r--chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action_unittest.cc78
-rw-r--r--chromium/components/autofill_assistant/browser/actions/save_generated_password_action.cc3
-rw-r--r--chromium/components/autofill_assistant/browser/actions/save_generated_password_action_unittest.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/save_submitted_password_action.cc3
-rw-r--r--chromium/components/autofill_assistant/browser/actions/save_submitted_password_action_unittest.cc30
-rw-r--r--chromium/components/autofill_assistant/browser/actions/select_option_action.cc1
-rw-r--r--chromium/components/autofill_assistant/browser/actions/select_option_action.h1
-rw-r--r--chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action.h3
-rw-r--r--chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action_unittest.cc6
-rw-r--r--chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc1
-rw-r--r--chromium/components/autofill_assistant/browser/actions/set_attribute_action.h1
-rw-r--r--chromium/components/autofill_assistant/browser/actions/show_cast_action.cc1
-rw-r--r--chromium/components/autofill_assistant/browser/actions/show_cast_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc6
-rw-r--r--chromium/components/autofill_assistant/browser/actions/upload_dom_action.h1
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_dom_test_base.cc96
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_dom_test_base.h81
-rw-r--r--chromium/components/autofill_assistant/browser/android/BUILD.gn5
-rw-r--r--chromium/components/autofill_assistant/browser/android/DEPS12
-rw-r--r--chromium/components/autofill_assistant/browser/android/assistant_header_model.cc3
-rw-r--r--chromium/components/autofill_assistant/browser/android/assistant_header_model.h4
-rw-r--r--chromium/components/autofill_assistant/browser/android/client_android.cc133
-rw-r--r--chromium/components/autofill_assistant/browser/android/client_android.h24
-rw-r--r--chromium/components/autofill_assistant/browser/android/dependencies_android.cc (renamed from chromium/components/autofill_assistant/browser/android/dependencies.cc)31
-rw-r--r--chromium/components/autofill_assistant/browser/android/dependencies_android.h (renamed from chromium/components/autofill_assistant/browser/android/dependencies.h)48
-rw-r--r--chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.cc14
-rw-r--r--chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.h4
-rw-r--r--chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.cc4
-rw-r--r--chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.h4
-rw-r--r--chromium/components/autofill_assistant/browser/android/interaction_handler_android.cc4
-rw-r--r--chromium/components/autofill_assistant/browser/android/interaction_handler_android.h6
-rw-r--r--chromium/components/autofill_assistant/browser/android/starter_delegate_android.cc33
-rw-r--r--chromium/components/autofill_assistant/browser/android/starter_delegate_android.h9
-rw-r--r--chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.cc5
-rw-r--r--chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.h4
-rw-r--r--chromium/components/autofill_assistant/browser/android/ui_controller_android.cc116
-rw-r--r--chromium/components/autofill_assistant/browser/android/ui_controller_android.h8
-rw-r--r--chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.cc10
-rw-r--r--chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.h4
-rw-r--r--chromium/components/autofill_assistant/browser/autofill_assistant_factory.cc12
-rw-r--r--chromium/components/autofill_assistant/browser/autofill_assistant_impl.cc51
-rw-r--r--chromium/components/autofill_assistant/browser/autofill_assistant_impl.h43
-rw-r--r--chromium/components/autofill_assistant/browser/autofill_assistant_impl_unittest.cc28
-rw-r--r--chromium/components/autofill_assistant/browser/autofill_assistant_tts_controller_unittest.cc1
-rw-r--r--chromium/components/autofill_assistant/browser/basic_interactions.cc8
-rw-r--r--chromium/components/autofill_assistant/browser/batch_element_checker.cc20
-rw-r--r--chromium/components/autofill_assistant/browser/batch_element_checker.h15
-rw-r--r--chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/client.h10
-rw-r--r--chromium/components/autofill_assistant/browser/client_context.cc25
-rw-r--r--chromium/components/autofill_assistant/browser/client_context.h8
-rw-r--r--chromium/components/autofill_assistant/browser/client_context_unittest.cc46
-rw-r--r--chromium/components/autofill_assistant/browser/client_settings.cc4
-rw-r--r--chromium/components/autofill_assistant/browser/client_settings.h4
-rw-r--r--chromium/components/autofill_assistant/browser/common_dependencies.cc11
-rw-r--r--chromium/components/autofill_assistant/browser/common_dependencies.h77
-rw-r--r--chromium/components/autofill_assistant/browser/controller.cc109
-rw-r--r--chromium/components/autofill_assistant/browser/controller.h14
-rw-r--r--chromium/components/autofill_assistant/browser/controller_observer.h3
-rw-r--r--chromium/components/autofill_assistant/browser/controller_unittest.cc234
-rw-r--r--chromium/components/autofill_assistant/browser/dependencies_util.cc23
-rw-r--r--chromium/components/autofill_assistant/browser/dependencies_util.h18
-rw-r--r--chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.cc37
-rw-r--r--chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.h13
-rw-r--r--chromium/components/autofill_assistant/browser/details.cc8
-rw-r--r--chromium/components/autofill_assistant/browser/devtools/value_conversions.h2
-rw-r--r--chromium/components/autofill_assistant/browser/dom_action.proto9
-rw-r--r--chromium/components/autofill_assistant/browser/external_action_extension_test.proto19
-rw-r--r--chromium/components/autofill_assistant/browser/fake_script_executor_delegate.cc22
-rw-r--r--chromium/components/autofill_assistant/browser/fake_script_executor_delegate.h10
-rw-r--r--chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc35
-rw-r--r--chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h30
-rw-r--r--chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.cc14
-rw-r--r--chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.h4
-rw-r--r--chromium/components/autofill_assistant/browser/field_formatter.cc28
-rw-r--r--chromium/components/autofill_assistant/browser/field_formatter_unittest.cc97
-rw-r--r--chromium/components/autofill_assistant/browser/full_card_requester.cc10
-rw-r--r--chromium/components/autofill_assistant/browser/full_card_requester_unittest.cc5
-rw-r--r--chromium/components/autofill_assistant/browser/headless/client_headless.cc132
-rw-r--r--chromium/components/autofill_assistant/browser/headless/client_headless.h33
-rw-r--r--chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.cc55
-rw-r--r--chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.h41
-rw-r--r--chromium/components/autofill_assistant/browser/headless/headless_ui_controller.cc37
-rw-r--r--chromium/components/autofill_assistant/browser/headless/headless_ui_controller.h19
-rw-r--r--chromium/components/autofill_assistant/browser/js_flow_devtools_wrapper.cc172
-rw-r--r--chromium/components/autofill_assistant/browser/js_flow_devtools_wrapper.h98
-rw-r--r--chromium/components/autofill_assistant/browser/js_flow_executor_impl.cc92
-rw-r--r--chromium/components/autofill_assistant/browser/js_flow_executor_impl.h24
-rw-r--r--chromium/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc185
-rw-r--r--chromium/components/autofill_assistant/browser/js_flow_util.cc59
-rw-r--r--chromium/components/autofill_assistant/browser/js_flow_util.h31
-rw-r--r--chromium/components/autofill_assistant/browser/js_flow_util_unittest.cc36
-rw-r--r--chromium/components/autofill_assistant/browser/metrics.cc16
-rw-r--r--chromium/components/autofill_assistant/browser/metrics.h9
-rw-r--r--chromium/components/autofill_assistant/browser/mock_client.h5
-rw-r--r--chromium/components/autofill_assistant/browser/mock_client_context.h15
-rw-r--r--chromium/components/autofill_assistant/browser/mock_common_dependencies.cc13
-rw-r--r--chromium/components/autofill_assistant/browser/mock_common_dependencies.h55
-rw-r--r--chromium/components/autofill_assistant/browser/mock_controller_observer.h1
-rw-r--r--chromium/components/autofill_assistant/browser/mock_script_executor_delegate.cc1
-rw-r--r--chromium/components/autofill_assistant/browser/mock_script_executor_delegate.h8
-rw-r--r--chromium/components/autofill_assistant/browser/mock_ui_controller_observer.h2
-rw-r--r--chromium/components/autofill_assistant/browser/parse_jspb.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/platform_dependencies.cc11
-rw-r--r--chromium/components/autofill_assistant/browser/platform_dependencies.h25
-rw-r--r--chromium/components/autofill_assistant/browser/protocol_utils.cc48
-rw-r--r--chromium/components/autofill_assistant/browser/protocol_utils.h5
-rw-r--r--chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc36
-rw-r--r--chromium/components/autofill_assistant/browser/public/BUILD.gn23
-rw-r--r--chromium/components/autofill_assistant/browser/public/autofill_assistant.h13
-rw-r--r--chromium/components/autofill_assistant/browser/public/autofill_assistant_factory.h15
-rw-r--r--chromium/components/autofill_assistant/browser/public/external_action.proto52
-rw-r--r--chromium/components/autofill_assistant/browser/public/external_action_delegate.h43
-rw-r--r--chromium/components/autofill_assistant/browser/public/external_script_controller.h8
-rw-r--r--chromium/components/autofill_assistant/browser/public/mock_autofill_assistant.h3
-rw-r--r--chromium/components/autofill_assistant/browser/public/mock_external_script_controller.cc13
-rw-r--r--chromium/components/autofill_assistant/browser/public/mock_external_script_controller.h28
-rw-r--r--chromium/components/autofill_assistant/browser/script_executor.cc89
-rw-r--r--chromium/components/autofill_assistant/browser/script_executor.h20
-rw-r--r--chromium/components/autofill_assistant/browser/script_executor_browsertest.cc481
-rw-r--r--chromium/components/autofill_assistant/browser/script_executor_delegate.h11
-rw-r--r--chromium/components/autofill_assistant/browser/script_executor_ui_delegate.h18
-rw-r--r--chromium/components/autofill_assistant/browser/script_executor_unittest.cc297
-rw-r--r--chromium/components/autofill_assistant/browser/script_parameters.cc15
-rw-r--r--chromium/components/autofill_assistant/browser/script_parameters.h2
-rw-r--r--chromium/components/autofill_assistant/browser/script_parameters_unittest.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/selector.cc5
-rw-r--r--chromium/components/autofill_assistant/browser/service.proto114
-rw-r--r--chromium/components/autofill_assistant/browser/service/java_service.cc1
-rw-r--r--chromium/components/autofill_assistant/browser/service/java_service.h1
-rw-r--r--chromium/components/autofill_assistant/browser/service/java_service_request_sender.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/service/java_service_request_sender.h2
-rw-r--r--chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.cc3
-rw-r--r--chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.h1
-rw-r--r--chromium/components/autofill_assistant/browser/service/mock_service.h14
-rw-r--r--chromium/components/autofill_assistant/browser/service/mock_service_request_sender.h5
-rw-r--r--chromium/components/autofill_assistant/browser/service/service.h7
-rw-r--r--chromium/components/autofill_assistant/browser/service/service_impl.cc46
-rw-r--r--chromium/components/autofill_assistant/browser/service/service_impl.h13
-rw-r--r--chromium/components/autofill_assistant/browser/service/service_impl_unittest.cc26
-rw-r--r--chromium/components/autofill_assistant/browser/service/service_request_sender.h4
-rw-r--r--chromium/components/autofill_assistant/browser/service/service_request_sender_impl.cc32
-rw-r--r--chromium/components/autofill_assistant/browser/service/service_request_sender_impl.h8
-rw-r--r--chromium/components/autofill_assistant/browser/service/service_request_sender_impl_unittest.cc190
-rw-r--r--chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.cc3
-rw-r--r--chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.h2
-rw-r--r--chromium/components/autofill_assistant/browser/starter.cc23
-rw-r--r--chromium/components/autofill_assistant/browser/starter.h5
-rw-r--r--chromium/components/autofill_assistant/browser/starter_heuristic.cc11
-rw-r--r--chromium/components/autofill_assistant/browser/starter_heuristic.h2
-rw-r--r--chromium/components/autofill_assistant/browser/starter_platform_delegate.h8
-rw-r--r--chromium/components/autofill_assistant/browser/starter_unittest.cc101
-rw-r--r--chromium/components/autofill_assistant/browser/suppress_keyboard_raii.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/tab_helper.cc9
-rw-r--r--chromium/components/autofill_assistant/browser/tab_helper.h7
-rw-r--r--chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc4
-rw-r--r--chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc8
-rw-r--r--chromium/components/autofill_assistant/browser/ui_controller.cc111
-rw-r--r--chromium/components/autofill_assistant/browser/ui_controller.h17
-rw-r--r--chromium/components/autofill_assistant/browser/ui_controller_observer.h5
-rw-r--r--chromium/components/autofill_assistant/browser/ui_controller_unittest.cc65
-rw-r--r--chromium/components/autofill_assistant/browser/user_data.h18
-rw-r--r--chromium/components/autofill_assistant/browser/user_data_util.cc31
-rw-r--r--chromium/components/autofill_assistant/browser/user_data_util.h10
-rw-r--r--chromium/components/autofill_assistant/browser/user_data_util_unittest.cc108
-rw-r--r--chromium/components/autofill_assistant/browser/wait_for_document_operation.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/wait_for_document_operation.h2
-rw-r--r--chromium/components/autofill_assistant/browser/wait_for_dom_operation.cc32
-rw-r--r--chromium/components/autofill_assistant/browser/wait_for_dom_operation.h2
-rw-r--r--chromium/components/autofill_assistant/browser/web/base_element_finder.cc11
-rw-r--r--chromium/components/autofill_assistant/browser/web/base_element_finder.h41
-rw-r--r--chromium/components/autofill_assistant/browser/web/batch_element_checker_browsertest.cc17
-rw-r--r--chromium/components/autofill_assistant/browser/web/check_on_top_worker.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/web/check_on_top_worker.h2
-rw-r--r--chromium/components/autofill_assistant/browser/web/click_or_tap_worker.cc1
-rw-r--r--chromium/components/autofill_assistant/browser/web/click_or_tap_worker.h2
-rw-r--r--chromium/components/autofill_assistant/browser/web/css_element_finder.cc754
-rw-r--r--chromium/components/autofill_assistant/browser/web/css_element_finder.h269
-rw-r--r--chromium/components/autofill_assistant/browser/web/element.cc24
-rw-r--r--chromium/components/autofill_assistant/browser/web/element.h4
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_action_util.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_action_util.h3
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_action_util_unittest.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_finder.cc988
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_finder.h430
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_finder_result.cc19
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_finder_result.h101
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_finder_result_type.h32
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_rect_getter.cc3
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_rect_getter.h3
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_store.cc3
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_store.h2
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_store_unittest.cc15
-rw-r--r--chromium/components/autofill_assistant/browser/web/fake_element_store.cc2
-rw-r--r--chromium/components/autofill_assistant/browser/web/js_filter_builder.cc10
-rw-r--r--chromium/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.cc37
-rw-r--r--chromium/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h51
-rw-r--r--chromium/components/autofill_assistant/browser/web/mock_web_controller.h1
-rw-r--r--chromium/components/autofill_assistant/browser/web/selector_observer.cc40
-rw-r--r--chromium/components/autofill_assistant/browser/web/selector_observer.h44
-rw-r--r--chromium/components/autofill_assistant/browser/web/selector_observer_script.h22
-rw-r--r--chromium/components/autofill_assistant/browser/web/semantic_element_finder.cc246
-rw-r--r--chromium/components/autofill_assistant/browser/web/semantic_element_finder.h105
-rw-r--r--chromium/components/autofill_assistant/browser/web/semantic_element_finder_browsertest.cc470
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller.cc109
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller.h26
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc387
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller_util.cc35
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller_util.h16
-rw-r--r--chromium/components/autofill_assistant/browser/website_login_manager_impl.cc3
-rw-r--r--chromium/components/autofill_assistant/content/browser/BUILD.gn1
-rw-r--r--chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.cc43
-rw-r--r--chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.h28
-rw-r--r--chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc17
-rw-r--r--chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.h1
-rw-r--r--chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver_unittest.cc48
-rw-r--r--chromium/components/autofill_assistant/content/common/BUILD.gn2
-rw-r--r--chromium/components/autofill_assistant/content/common/autofill_assistant_agent.mojom9
-rw-r--r--chromium/components/autofill_assistant/content/common/autofill_assistant_driver.mojom4
-rw-r--r--chromium/components/autofill_assistant/content/common/switches.cc13
-rw-r--r--chromium/components/autofill_assistant/content/common/switches.h15
-rw-r--r--chromium/components/autofill_assistant/content/renderer/BUILD.gn2
-rw-r--r--chromium/components/autofill_assistant/content/renderer/DEPS1
-rw-r--r--chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc147
-rw-r--r--chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.h10
-rw-r--r--chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc129
-rw-r--r--chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.cc148
-rw-r--r--chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.h28
-rw-r--r--chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor_unittest.cc86
-rw-r--r--chromium/components/autofill_assistant/guided_browsing/OWNERS2
-rw-r--r--chromium/components/autofill_assistant/guided_browsing/android/BUILD.gn50
-rw-r--r--chromium/components/autofill_assistant/guided_browsing/android/DEPS7
-rw-r--r--chromium/components/autofill_assistant_strings.grdp2
-rw-r--r--chromium/components/autofill_assistant_strings_grdp/IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings.grdp39
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_TITLE.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_VALUE_PROP_TEXT.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MOBILE_SAVE_CARD_TO_CLOUD_CONFIRMATION_DIALOG_EXPLANATION_WITH_ACCOUNT_NAME.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_DESCRIPTION_TEXT.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_PRIMARY_BUTTON_TEXT.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_TITLE.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED.png.sha11
-rw-r--r--chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_DIALOG_CONTENT_LABEL.png.sha12
-rw-r--r--chromium/components/autofill_strings.grdp73
-rw-r--r--chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_ADDRESSES.png.sha11
-rw-r--r--chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_PASSWORDS.png.sha11
-rw-r--r--chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_PAYMENT_METHODS.png.sha11
-rw-r--r--chromium/components/background_fetch/background_fetch_delegate_base.cc15
-rw-r--r--chromium/components/background_fetch/background_fetch_delegate_base.h5
-rw-r--r--chromium/components/background_fetch/job_details.cc4
-rw-r--r--chromium/components/background_fetch/job_details.h3
-rw-r--r--chromium/components/background_sync/background_sync_controller_impl.cc14
-rw-r--r--chromium/components/background_sync/background_sync_permission_context.cc1
-rw-r--r--chromium/components/background_sync/background_sync_permission_context.h1
-rw-r--r--chromium/components/background_sync/background_sync_permission_context_unittest.cc6
-rw-r--r--chromium/components/background_task_scheduler/internal/BUILD.gn4
-rw-r--r--chromium/components/blocked_content/popup_blocker_tab_helper.cc4
-rw-r--r--chromium/components/blocked_content/popup_blocker_tab_helper_unittest.cc14
-rw-r--r--chromium/components/blocked_content/popup_opener_tab_helper.cc3
-rw-r--r--chromium/components/blocked_content/popup_tracker.cc3
-rw-r--r--chromium/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc7
-rw-r--r--chromium/components/blocklist/OWNERS2
-rw-r--r--chromium/components/bookmarks/browser/bookmark_load_details.cc3
-rw-r--r--chromium/components/bookmarks/browser/bookmark_load_details.h7
-rw-r--r--chromium/components/bookmarks/browser/bookmark_model.cc5
-rw-r--r--chromium/components/bookmarks/browser/bookmark_storage.cc9
-rw-r--r--chromium/components/bookmarks/browser/bookmark_storage.h3
-rw-r--r--chromium/components/bookmarks/browser/bookmark_storage_unittest.cc36
-rw-r--r--chromium/components/bookmarks/browser/bookmark_utils.cc6
-rw-r--r--chromium/components/bookmarks/browser/model_loader.cc52
-rw-r--r--chromium/components/bookmarks/browser/url_index.cc7
-rw-r--r--chromium/components/bookmarks/browser/url_index.h21
-rw-r--r--chromium/components/bookmarks/common/BUILD.gn3
-rw-r--r--chromium/components/bookmarks/common/android/BUILD.gn2
-rw-r--r--chromium/components/bookmarks/common/bookmark_metrics.cc77
-rw-r--r--chromium/components/bookmarks/common/bookmark_metrics.h34
-rw-r--r--chromium/components/bookmarks/common/url_load_stats.h30
-rw-r--r--chromium/components/breadcrumbs/core/application_breadcrumbs_logger.cc4
-rw-r--r--chromium/components/browser_sync/signin_confirmation_helper.cc6
-rw-r--r--chromium/components/browser_sync/sync_api_component_factory_impl.cc103
-rw-r--r--chromium/components/browser_sync/sync_api_component_factory_impl.h2
-rw-r--r--chromium/components/browser_ui/accessibility/DEPS1
-rw-r--r--chromium/components/browser_ui/accessibility/android/BUILD.gn44
-rw-r--r--chromium/components/browser_ui/bottomsheet/android/BUILD.gn2
-rw-r--r--chromium/components/browser_ui/bottomsheet/android/internal/BUILD.gn1
-rw-r--r--chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java46
-rw-r--r--chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java6
-rw-r--r--chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java59
-rw-r--r--chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java72
-rw-r--r--chromium/components/browser_ui/client_certificate/android/BUILD.gn2
-rw-r--r--chromium/components/browser_ui/contacts_picker/android/BUILD.gn3
-rw-r--r--chromium/components/browser_ui/http_auth/android/BUILD.gn1
-rw-r--r--chromium/components/browser_ui/media/android/BUILD.gn1
-rw-r--r--chromium/components/browser_ui/modaldialog/android/BUILD.gn3
-rw-r--r--chromium/components/browser_ui/photo_picker/android/BUILD.gn4
-rw-r--r--chromium/components/browser_ui/settings/android/BUILD.gn7
-rw-r--r--chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java4
-rw-r--r--chromium/components/browser_ui/share/android/BUILD.gn1
-rw-r--r--chromium/components/browser_ui/site_settings/android/BUILD.gn11
-rw-r--r--chromium/components/browser_ui/site_settings/android/website_preference_bridge.cc22
-rw-r--r--chromium/components/browser_ui/sms/android/BUILD.gn1
-rw-r--r--chromium/components/browser_ui/strings/android/browser_ui_strings.grd78
-rw-r--r--chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT.png.sha11
-rw-r--r--chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT_INCOGNITO.png.sha11
-rw-r--r--chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONNECTED_TO_BLUETOOTH_DEVICE_NOTIFICATION_TITLE.png.sha11
-rw-r--r--chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONNECTED_TO_USB_DEVICE_NOTIFICATION_TITLE.png.sha11
-rw-r--r--chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha11
-rw-r--r--chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_SCANNING_FOR_BLUETOOTH_DEVICES_NOTIFICATION_TITLE.png.sha11
-rw-r--r--chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_USB_NOTIFICATION_CONTENT_TEXT.png.sha11
-rw-r--r--chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_USB_NOTIFICATION_CONTENT_TEXT_INCOGNITO.png.sha11
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_af.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_am.xtb8
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb6
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_az.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_be.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bg.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bn.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bs.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ca.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cs.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cy.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_da.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_de.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_el.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_en-GB.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es-419.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_et.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_eu.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb6
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fi.xtb6
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fil.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr-CA.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gl.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gu.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hi.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hr.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hy.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_id.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_is.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_it.xtb6
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ja.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kk.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_km.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kn.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ko.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ky.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lo.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lt.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lv.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mk.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ml.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mr.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ms.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_my.xtb14
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ne.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_nl.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_no.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_or.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pa.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pl.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-BR.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-PT.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ro.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ru.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_si.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sk.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sl.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sq.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr-Latn.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sv.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sw.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ta.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_th.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_tr.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uk.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ur.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uz.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-CN.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-TW.xtb4
-rw-r--r--chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zu.xtb4
-rw-r--r--chromium/components/browser_ui/styles/android/BUILD.gn30
-rw-r--r--chromium/components/browser_ui/theme/android/BUILD.gn2
-rw-r--r--chromium/components/browser_ui/util/android/BUILD.gn1
-rw-r--r--chromium/components/browser_ui/webshare/android/BUILD.gn1
-rw-r--r--chromium/components/browser_ui/widget/android/BUILD.gn12
-rw-r--r--chromium/components/browsing_data/content/BUILD.gn1
-rw-r--r--chromium/components/browsing_data/content/DEPS1
-rw-r--r--chromium/components/browsing_data/content/browsing_data_helper.cc4
-rw-r--r--chromium/components/browsing_data/content/file_system_helper_unittest.cc5
-rw-r--r--chromium/components/browsing_data/content/indexed_db_helper.cc4
-rw-r--r--chromium/components/browsing_data/content/indexed_db_helper.h2
-rw-r--r--chromium/components/browsing_topics/BUILD.gn2
-rw-r--r--chromium/components/browsing_topics/browsing_topics_calculator.cc18
-rw-r--r--chromium/components/browsing_topics/browsing_topics_calculator.h11
-rw-r--r--chromium/components/browsing_topics/browsing_topics_calculator_unittest.cc187
-rw-r--r--chromium/components/browsing_topics/browsing_topics_page_load_data_tracker_unittest.cc4
-rw-r--r--chromium/components/browsing_topics/browsing_topics_service.h5
-rw-r--r--chromium/components/browsing_topics/browsing_topics_service_impl.cc74
-rw-r--r--chromium/components/browsing_topics/browsing_topics_service_impl.h3
-rw-r--r--chromium/components/browsing_topics/browsing_topics_service_impl_unittest.cc168
-rw-r--r--chromium/components/browsing_topics/epoch_topics.cc1
-rw-r--r--chromium/components/browsing_topics/mojom/BUILD.gn12
-rw-r--r--chromium/components/browsing_topics/mojom/OWNERS (renamed from chromium/components/contextual_search/content/common/mojom/OWNERS)0
-rw-r--r--chromium/components/browsing_topics/mojom/browsing_topics_internals.mojom165
-rw-r--r--chromium/components/browsing_topics/test_util.h5
-rw-r--r--chromium/components/captive_portal/core/features.gni2
-rw-r--r--chromium/components/cast/named_message_port_connector/named_message_port_connector.js2
-rw-r--r--chromium/components/cast_certificate/cast_crl.cc1
-rw-r--r--chromium/components/cast_streaming/DEPS2
-rw-r--r--chromium/components/cast_streaming/browser/BUILD.gn36
-rw-r--r--chromium/components/cast_streaming/browser/cast_streaming_session.cc242
-rw-r--r--chromium/components/cast_streaming/browser/cast_streaming_session.h59
-rw-r--r--chromium/components/cast_streaming/browser/demuxer_stream_client.h32
-rw-r--r--chromium/components/cast_streaming/browser/demuxer_stream_data_provider.h64
-rw-r--r--chromium/components/cast_streaming/browser/demuxer_stream_data_provider_unittest.cc45
-rw-r--r--chromium/components/cast_streaming/browser/playback_command_dispatcher.cc211
-rw-r--r--chromium/components/cast_streaming/browser/playback_command_dispatcher.h55
-rw-r--r--chromium/components/cast_streaming/browser/public/receiver_session.h10
-rw-r--r--chromium/components/cast_streaming/browser/receiver_session_impl.cc100
-rw-r--r--chromium/components/cast_streaming/browser/receiver_session_impl.h36
-rw-r--r--chromium/components/cast_streaming/browser/remoting_session_client.h37
-rw-r--r--chromium/components/cast_streaming/browser/renderer_rpc_call_translator.cc67
-rw-r--r--chromium/components/cast_streaming/browser/renderer_rpc_call_translator.h41
-rw-r--r--chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler.cc224
-rw-r--r--chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler.h159
-rw-r--r--chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler_unittests.cc342
-rw-r--r--chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.cc7
-rw-r--r--chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.h17
-rw-r--r--chromium/components/cast_streaming/browser/stream_consumer.cc28
-rw-r--r--chromium/components/cast_streaming/browser/stream_consumer.h16
-rw-r--r--chromium/components/cast_streaming/browser/streaming_initialization_info.cc66
-rw-r--r--chromium/components/cast_streaming/browser/streaming_initialization_info.h92
-rw-r--r--chromium/components/cast_streaming/public/mojom/BUILD.gn2
-rw-r--r--chromium/components/cast_streaming/public/mojom/demuxer_connector.mojom (renamed from chromium/components/cast_streaming/public/mojom/cast_streaming_session.mojom)11
-rw-r--r--chromium/components/cast_streaming/public/remoting_message_factories.cc40
-rw-r--r--chromium/components/cast_streaming/public/remoting_message_factories.h19
-rw-r--r--chromium/components/cast_streaming/public/remoting_message_factories_unittest.cc33
-rw-r--r--chromium/components/cast_streaming/public/remoting_proto_enum_utils.cc16
-rw-r--r--chromium/components/cast_streaming/public/rpc_call_message_handler.cc94
-rw-r--r--chromium/components/cast_streaming/public/rpc_call_message_handler.h29
-rw-r--r--chromium/components/cast_streaming/public/rpc_call_message_handler_unittest.cc199
-rw-r--r--chromium/components/cast_streaming/renderer/BUILD.gn72
-rw-r--r--chromium/components/cast_streaming/renderer/DEPS2
-rw-r--r--chromium/components/cast_streaming/renderer/cast_streaming_demuxer.cc43
-rw-r--r--chromium/components/cast_streaming/renderer/cast_streaming_demuxer.h12
-rw-r--r--chromium/components/cast_streaming/renderer/cast_streaming_receiver.h71
-rw-r--r--chromium/components/cast_streaming/renderer/cast_streaming_render_frame_observer.cc42
-rw-r--r--chromium/components/cast_streaming/renderer/cast_streaming_render_frame_observer.h50
-rw-r--r--chromium/components/cast_streaming/renderer/decoder_buffer_reader.cc8
-rw-r--r--chromium/components/cast_streaming/renderer/decoder_buffer_reader.h2
-rw-r--r--chromium/components/cast_streaming/renderer/demuxer_connector.cc (renamed from chromium/components/cast_streaming/renderer/cast_streaming_receiver.cc)51
-rw-r--r--chromium/components/cast_streaming/renderer/demuxer_connector.h64
-rw-r--r--chromium/components/cast_streaming/renderer/demuxer_provider.cc61
-rw-r--r--chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc5
-rw-r--r--chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.h1
-rw-r--r--chromium/components/cast_streaming/renderer/public/demuxer_provider.h63
-rw-r--r--chromium/components/cast_streaming/renderer/public/renderer_controller_proxy.h57
-rw-r--r--chromium/components/cast_streaming/renderer/public/renderer_controller_proxy_factory.h19
-rw-r--r--chromium/components/cast_streaming/renderer/public/resource_provider.h75
-rw-r--r--chromium/components/cast_streaming/renderer/public/resource_provider_factory.h18
-rw-r--r--chromium/components/cast_streaming/renderer/public/wrapping_renderer_factory_selector.h8
-rw-r--r--chromium/components/cast_streaming/renderer/renderer_controller_proxy.cc56
-rw-r--r--chromium/components/cast_streaming/renderer/renderer_controller_proxy.h73
-rw-r--r--chromium/components/cast_streaming/renderer/renderer_controller_proxy_impl.cc113
-rw-r--r--chromium/components/cast_streaming/renderer/renderer_controller_proxy_impl.h110
-rw-r--r--chromium/components/cast_streaming/renderer/resource_provider.cc13
-rw-r--r--chromium/components/cast_streaming/renderer/resource_provider_impl.cc92
-rw-r--r--chromium/components/cast_streaming/renderer/resource_provider_impl.h99
-rw-r--r--chromium/components/cast_streaming/renderer/wrapping_renderer_factory_selector.cc15
-rw-r--r--chromium/components/cbor/OWNERS1
-rw-r--r--chromium/components/cdm/DEPS1
-rw-r--r--chromium/components/cdm/browser/cdm_message_filter_android.cc20
-rw-r--r--chromium/components/cdm/browser/media_drm_storage_impl_unittest.cc2
-rw-r--r--chromium/components/cdm/renderer/android_key_systems.cc71
-rw-r--r--chromium/components/cdm/renderer/external_clear_key_key_system_properties.cc4
-rw-r--r--chromium/components/cdm/renderer/external_clear_key_key_system_properties.h3
-rw-r--r--chromium/components/cdm/renderer/widevine_key_system_properties.cc72
-rw-r--r--chromium/components/cdm/renderer/widevine_key_system_properties.h10
-rw-r--r--chromium/components/certificate_matching/OWNERS2
-rw-r--r--chromium/components/certificate_matching/certificate_principal_pattern.cc8
-rw-r--r--chromium/components/certificate_matching/certificate_principal_pattern.h7
-rw-r--r--chromium/components/certificate_matching/certificate_principal_pattern_unittest.cc20
-rw-r--r--chromium/components/certificate_transparency/chrome_ct_policy_enforcer.cc26
-rw-r--r--chromium/components/certificate_transparency/chrome_ct_policy_enforcer.h8
-rw-r--r--chromium/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc240
-rw-r--r--chromium/components/certificate_transparency/chrome_require_ct_delegate.cc2
-rw-r--r--chromium/components/certificate_transparency/chrome_require_ct_delegate.h4
-rw-r--r--chromium/components/certificate_transparency/ct_features.cc7
-rw-r--r--chromium/components/certificate_transparency/ct_features.h15
-rw-r--r--chromium/components/certificate_transparency/data/log_list.json272
-rw-r--r--chromium/components/certificate_transparency/tools/PRESUBMIT.py6
-rw-r--r--chromium/components/chrome_cleaner/public/constants/BUILD.gn16
-rw-r--r--chromium/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.cc23
-rw-r--r--chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.cc15
-rw-r--r--chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.h10
-rw-r--r--chromium/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc8
-rw-r--r--chromium/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc33
-rw-r--r--chromium/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc12
-rw-r--r--chromium/components/client_hints/OWNERS3
-rw-r--r--chromium/components/client_hints/README.md31
-rw-r--r--chromium/components/client_hints/browser/client_hints.cc19
-rw-r--r--chromium/components/client_hints/browser/client_hints.h4
-rw-r--r--chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.cc6
-rw-r--r--chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.h4
-rw-r--r--chromium/components/cloud_devices/common/cloud_device_description.cc14
-rw-r--r--chromium/components/cloud_devices/common/cloud_device_description.h4
-rw-r--r--chromium/components/cloud_devices/common/printer_description.cc3
-rw-r--r--chromium/components/cloud_devices/common/printer_description_unittest.cc31
-rw-r--r--chromium/components/commerce/content/browser/BUILD.gn (renamed from chromium/components/commerce/content/BUILD.gn)13
-rw-r--r--chromium/components/commerce/content/browser/commerce_tab_helper.cc61
-rw-r--r--chromium/components/commerce/content/browser/commerce_tab_helper.h71
-rw-r--r--chromium/components/commerce/content/browser/hint/commerce_hint_tab_helper.cc (renamed from chromium/components/commerce/content/hint/commerce_hint_tab_helper.cc)2
-rw-r--r--chromium/components/commerce/content/browser/hint/commerce_hint_tab_helper.h (renamed from chromium/components/commerce/content/hint/commerce_hint_tab_helper.h)6
-rw-r--r--chromium/components/commerce/content/browser/web_contents_wrapper.cc47
-rw-r--r--chromium/components/commerce/content/browser/web_contents_wrapper.h51
-rw-r--r--chromium/components/commerce/content/metrics/commerce_metrics_tab_helper.cc62
-rw-r--r--chromium/components/commerce/content/metrics/commerce_metrics_tab_helper.h65
-rw-r--r--chromium/components/commerce/core/BUILD.gn132
-rw-r--r--chromium/components/commerce/core/DEPS2
-rw-r--r--chromium/components/commerce/core/android/BUILD.gn3
-rw-r--r--chromium/components/commerce/core/android/shopping_service_android.cc45
-rw-r--r--chromium/components/commerce/core/android/shopping_service_android.h17
-rw-r--r--chromium/components/commerce/core/commerce_feature_list.cc15
-rw-r--r--chromium/components/commerce/core/commerce_feature_list.h59
-rw-r--r--chromium/components/commerce/core/commerce_heuristics_data_metrics_helper.cc25
-rw-r--r--chromium/components/commerce/core/commerce_heuristics_data_metrics_helper.h38
-rw-r--r--chromium/components/commerce/core/heuristics/commerce_heuristics_provider.cc18
-rw-r--r--chromium/components/commerce/core/metrics/metrics_utils.cc4
-rw-r--r--chromium/components/commerce/core/metrics/metrics_utils.h2
-rw-r--r--chromium/components/commerce/core/pdp_metrics_unittest.cc111
-rw-r--r--chromium/components/commerce/core/proto/merchant_trust.proto34
-rw-r--r--chromium/components/commerce/core/resources/query_shopping_meta.js22
-rw-r--r--chromium/components/commerce/core/shopping_bookmark_model_observer.cc47
-rw-r--r--chromium/components/commerce/core/shopping_bookmark_model_observer.h58
-rw-r--r--chromium/components/commerce/core/shopping_service.cc329
-rw-r--r--chromium/components/commerce/core/shopping_service.h172
-rw-r--r--chromium/components/commerce/core/shopping_service_test_base.cc186
-rw-r--r--chromium/components/commerce/core/shopping_service_test_base.h142
-rw-r--r--chromium/components/commerce/core/shopping_service_unittest.cc246
-rw-r--r--chromium/components/commerce/core/web_wrapper.h41
-rw-r--r--chromium/components/commerce/ios/DEPS3
-rw-r--r--chromium/components/commerce/ios/browser/BUILD.gn21
-rw-r--r--chromium/components/commerce/ios/browser/commerce_tab_helper.h78
-rw-r--r--chromium/components/commerce/ios/browser/commerce_tab_helper.mm79
-rw-r--r--chromium/components/commerce/ios/browser/web_state_wrapper.h47
-rw-r--r--chromium/components/commerce/ios/browser/web_state_wrapper.mm61
-rw-r--r--chromium/components/commerce_strings.grdp9
-rw-r--r--chromium/components/component_updater/android/BUILD.gn4
-rw-r--r--chromium/components/component_updater/component_updater_service.cc56
-rw-r--r--chromium/components/component_updater/configurator_impl.cc5
-rw-r--r--chromium/components/component_updater/crl_set_remover.cc5
-rw-r--r--chromium/components/component_updater/installer_policies/BUILD.gn8
-rw-r--r--chromium/components/component_updater/installer_policies/DEPS1
-rw-r--r--chromium/components/component_updater/installer_policies/url_param_classification_component_installer.cc244
-rw-r--r--chromium/components/component_updater/installer_policies/url_param_classification_component_installer.h91
-rw-r--r--chromium/components/component_updater/installer_policies/url_param_classification_component_installer_unittest.cc304
-rw-r--r--chromium/components/components_chromium_strings.grd3
-rw-r--r--chromium/components/components_google_chrome_strings.grd3
-rw-r--r--chromium/components/components_strings.grd1
-rw-r--r--chromium/components/constrained_window/BUILD.gn1
-rw-r--r--chromium/components/constrained_window/DEPS1
-rw-r--r--chromium/components/constrained_window/constrained_window_views.cc19
-rw-r--r--chromium/components/constrained_window/constrained_window_views.h4
-rw-r--r--chromium/components/content_capture/android/BUILD.gn2
-rw-r--r--chromium/components/content_capture/android/junit/BUILD.gn1
-rw-r--r--chromium/components/content_capture/android/test_support/BUILD.gn4
-rw-r--r--chromium/components/content_capture/android/test_support/content_capture_test_support_android.cc4
-rw-r--r--chromium/components/content_capture/browser/content_capture_receiver.cc5
-rw-r--r--chromium/components/content_capture/browser/content_capture_receiver_browsertest.cc55
-rw-r--r--chromium/components/content_capture/browser/content_capture_receiver_test.cc10
-rw-r--r--chromium/components/content_capture/browser/onscreen_content_provider.cc8
-rw-r--r--chromium/components/content_creation/notes/android/BUILD.gn2
-rw-r--r--chromium/components/content_creation/reactions/android/BUILD.gn2
-rw-r--r--chromium/components/content_settings/android/BUILD.gn3
-rw-r--r--chromium/components/content_settings/browser/page_specific_content_settings.h4
-rw-r--r--chromium/components/content_settings/browser/page_specific_content_settings_unittest.cc89
-rw-r--r--chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.cc6
-rw-r--r--chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.h2
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_default_provider.cc12
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_policy_provider.cc9
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_pref_provider.cc12
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc2
-rw-r--r--chromium/components/content_settings/core/browser/website_settings_registry.cc18
-rw-r--r--chromium/components/content_settings/core/common/content_settings.cc3
-rw-r--r--chromium/components/content_settings/core/common/content_settings_pattern_parser.cc62
-rw-r--r--chromium/components/content_settings/core/common/content_settings_pattern_parser_fuzzer.cc5
-rw-r--r--chromium/components/content_settings/core/common/content_settings_pattern_parser_unittest.cc33
-rw-r--r--chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc2
-rw-r--r--chromium/components/content_settings/core/common/content_settings_types.h15
-rw-r--r--chromium/components/content_settings/core/common/pref_names.cc6
-rw-r--r--chromium/components/content_settings/core/common/pref_names.h3
-rw-r--r--chromium/components/content_settings/renderer/content_settings_agent_impl_browsertest.cc1
-rw-r--r--chromium/components/contextual_search/README.md40
-rw-r--r--chromium/components/contextual_search/content/BUILD.gn34
-rw-r--r--chromium/components/contextual_search/content/DEPS4
-rw-r--r--chromium/components/contextual_search/content/browser/contextual_search_js_api_handler.h49
-rw-r--r--chromium/components/contextual_search/content/browser/contextual_search_js_api_service_impl.cc49
-rw-r--r--chromium/components/contextual_search/content/browser/contextual_search_js_api_service_impl.h60
-rw-r--r--chromium/components/contextual_search/content/common/mojom/BUILD.gn11
-rw-r--r--chromium/components/contextual_search/content/common/mojom/contextual_search_js_api_service.mojom44
-rw-r--r--chromium/components/contextual_search/content/renderer/DEPS8
-rw-r--r--chromium/components/contextual_search/content/renderer/contextual_search_wrapper.cc109
-rw-r--r--chromium/components/contextual_search/content/renderer/contextual_search_wrapper.h68
-rw-r--r--chromium/components/contextual_search/content/renderer/overlay_js_render_frame_observer.cc59
-rw-r--r--chromium/components/contextual_search/content/renderer/overlay_js_render_frame_observer.h63
-rw-r--r--chromium/components/contextual_search/core/BUILD.gn15
-rw-r--r--chromium/components/contextual_search/core/browser/ctr_aggregator.cc121
-rw-r--r--chromium/components/contextual_search/core/browser/ctr_aggregator.h117
-rw-r--r--chromium/components/contextual_search/core/browser/ctr_aggregator_unittest.cc176
-rw-r--r--chromium/components/contextual_search/core/browser/weekly_activity_storage.cc98
-rw-r--r--chromium/components/contextual_search/core/browser/weekly_activity_storage.h76
-rw-r--r--chromium/components/continuous_search/browser/search_result_extractor_client.cc9
-rw-r--r--chromium/components/continuous_search/browser/search_result_extractor_client_unittest.cc2
-rw-r--r--chromium/components/crash/OWNERS1
-rw-r--r--chromium/components/crash/android/BUILD.gn8
-rw-r--r--chromium/components/crash/core/app/breakpad_linux.cc44
-rw-r--r--chromium/components/crash/core/app/crashpad.cc18
-rw-r--r--chromium/components/crash/core/app/crashpad.h4
-rw-r--r--chromium/components/crash/core/app/crashpad_ios.mm5
-rw-r--r--chromium/components/crash/core/app/crashpad_linux.cc6
-rw-r--r--chromium/components/crash/core/app/crashpad_win.cc26
-rw-r--r--chromium/components/crash/core/browser/crashes_ui_util.cc24
-rw-r--r--chromium/components/crash/core/browser/crashes_ui_util.h6
-rw-r--r--chromium/components/crash/core/common/crash_key_base_support.cc1
-rw-r--r--chromium/components/cronet/BUILD.gn4
-rw-r--r--chromium/components/cronet/android/BUILD.gn38
-rw-r--r--chromium/components/crx_file/crx_creator.cc3
-rw-r--r--chromium/components/crx_file/crx_file.h2
-rw-r--r--chromium/components/crx_file/crx_verifier.cc2
-rw-r--r--chromium/components/custom_handlers/protocol_handler.cc4
-rw-r--r--chromium/components/custom_handlers/protocol_handler_registry.cc17
-rw-r--r--chromium/components/custom_handlers/protocol_handler_registry.h21
-rw-r--r--chromium/components/custom_handlers/protocol_handler_registry_browsertest.cc2
-rw-r--r--chromium/components/custom_handlers/simple_protocol_handler_registry_factory.cc10
-rw-r--r--chromium/components/data_reduction_proxy/COMMON_METADATA3
-rw-r--r--chromium/components/data_reduction_proxy/DEPS16
-rw-r--r--chromium/components/data_reduction_proxy/DIR_METADATA1
-rw-r--r--chromium/components/data_reduction_proxy/OWNERS54
-rw-r--r--chromium/components/data_reduction_proxy/README14
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/BUILD.gn52
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/DEPS8
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h26
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc64
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h78
-rw-r--r--chromium/components/data_reduction_proxy/core/common/BUILD.gn33
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc14
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h19
-rw-r--r--chromium/components/dbus/menu/BUILD.gn1
-rw-r--r--chromium/components/dbus/menu/menu_property_list_unittest.cc5
-rw-r--r--chromium/components/desks_storage/BUILD.gn23
-rw-r--r--chromium/components/desks_storage/DEPS1
-rw-r--r--chromium/components/desks_storage/OWNERS1
-rw-r--r--chromium/components/desks_storage/core/desk_model.cc3
-rw-r--r--chromium/components/desks_storage/core/desk_model.h37
-rw-r--r--chromium/components/desks_storage/core/desk_model_wrapper.cc192
-rw-r--r--chromium/components/desks_storage/core/desk_model_wrapper.h89
-rw-r--r--chromium/components/desks_storage/core/desk_model_wrapper_unittests.cc854
-rw-r--r--chromium/components/desks_storage/core/desk_sync_bridge.cc535
-rw-r--r--chromium/components/desks_storage/core/desk_sync_bridge.h27
-rw-r--r--chromium/components/desks_storage/core/desk_sync_bridge_unittest.cc497
-rw-r--r--chromium/components/desks_storage/core/desk_sync_service.cc2
-rw-r--r--chromium/components/desks_storage/core/desk_sync_service.h2
-rw-r--r--chromium/components/desks_storage/core/desk_template_conversion.cc335
-rw-r--r--chromium/components/desks_storage/core/desk_template_conversion.h17
-rw-r--r--chromium/components/desks_storage/core/desk_template_conversion_unittests.cc200
-rw-r--r--chromium/components/desks_storage/core/desk_template_util.cc46
-rw-r--r--chromium/components/desks_storage/core/desk_template_util.h12
-rw-r--r--chromium/components/desks_storage/core/desk_template_util_unittests.cc65
-rw-r--r--chromium/components/desks_storage/core/desk_test_util.cc95
-rw-r--r--chromium/components/desks_storage/core/desk_test_util.h36
-rw-r--r--chromium/components/desks_storage/core/desk_test_util_unittests.cc30
-rw-r--r--chromium/components/desks_storage/core/local_desk_data_manager.cc585
-rw-r--r--chromium/components/desks_storage/core/local_desk_data_manager.h108
-rw-r--r--chromium/components/desks_storage/core/local_desks_data_manager_unittests.cc663
-rw-r--r--chromium/components/desks_storage/core/saved_desk_builder.cc108
-rw-r--r--chromium/components/desks_storage/core/saved_desk_builder.h84
-rw-r--r--chromium/components/desks_storage/core/saved_desk_test_util.cc52
-rw-r--r--chromium/components/desks_storage/core/saved_desk_test_util.h40
-rw-r--r--chromium/components/device_signals/OWNERS2
-rw-r--r--chromium/components/device_signals/README.md12
-rw-r--r--chromium/components/device_signals/core/BUILD.gn11
-rw-r--r--chromium/components/device_signals/core/browser/BUILD.gn80
-rw-r--r--chromium/components/device_signals/core/browser/DEPS5
-rw-r--r--chromium/components/device_signals/core/browser/mock_signals_collector.cc12
-rw-r--r--chromium/components/device_signals/core/browser/mock_signals_collector.h34
-rw-r--r--chromium/components/device_signals/core/browser/mock_system_signals_service_host.cc15
-rw-r--r--chromium/components/device_signals/core/browser/mock_system_signals_service_host.h45
-rw-r--r--chromium/components/device_signals/core/browser/mock_user_delegate.cc12
-rw-r--r--chromium/components/device_signals/core/browser/mock_user_delegate.h25
-rw-r--r--chromium/components/device_signals/core/browser/mock_user_permission_service.cc12
-rw-r--r--chromium/components/device_signals/core/browser/mock_user_permission_service.h27
-rw-r--r--chromium/components/device_signals/core/browser/signals_aggregator.h39
-rw-r--r--chromium/components/device_signals/core/browser/signals_aggregator_impl.cc106
-rw-r--r--chromium/components/device_signals/core/browser/signals_aggregator_impl.h53
-rw-r--r--chromium/components/device_signals/core/browser/signals_aggregator_impl_unittest.cc229
-rw-r--r--chromium/components/device_signals/core/browser/signals_collector.h37
-rw-r--r--chromium/components/device_signals/core/browser/system_signals_service_host.h26
-rw-r--r--chromium/components/device_signals/core/browser/user_context.cc13
-rw-r--r--chromium/components/device_signals/core/browser/user_context.h21
-rw-r--r--chromium/components/device_signals/core/browser/user_delegate.h29
-rw-r--r--chromium/components/device_signals/core/browser/user_permission_service.h54
-rw-r--r--chromium/components/device_signals/core/browser/user_permission_service_impl.cc80
-rw-r--r--chromium/components/device_signals/core/browser/user_permission_service_impl.h52
-rw-r--r--chromium/components/device_signals/core/browser/user_permission_service_impl_unittest.cc201
-rw-r--r--chromium/components/device_signals/core/browser/win/BUILD.gn30
-rw-r--r--chromium/components/device_signals/core/browser/win/win_signals_collector.cc102
-rw-r--r--chromium/components/device_signals/core/browser/win/win_signals_collector.h78
-rw-r--r--chromium/components/device_signals/core/browser/win/win_signals_collector_unittest.cc122
-rw-r--r--chromium/components/device_signals/core/common/BUILD.gn17
-rw-r--r--chromium/components/device_signals/core/common/mojom/BUILD.gn41
-rw-r--r--chromium/components/device_signals/core/common/mojom/OWNERS5
-rw-r--r--chromium/components/device_signals/core/common/mojom/system_signals.mojom81
-rw-r--r--chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc95
-rw-r--r--chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.h59
-rw-r--r--chromium/components/device_signals/core/common/signals_constants.cc160
-rw-r--r--chromium/components/device_signals/core/common/signals_constants.h62
-rw-r--r--chromium/components/device_signals/core/common/win/BUILD.gn65
-rw-r--r--chromium/components/device_signals/core/common/win/com_fakes.cc369
-rw-r--r--chromium/components/device_signals/core/common/win/com_fakes.h234
-rw-r--r--chromium/components/device_signals/core/common/win/mock_wmi_client.cc12
-rw-r--r--chromium/components/device_signals/core/common/win/mock_wmi_client.h23
-rw-r--r--chromium/components/device_signals/core/common/win/mock_wsc_client.cc12
-rw-r--r--chromium/components/device_signals/core/common/win/mock_wsc_client.h23
-rw-r--r--chromium/components/device_signals/core/common/win/win_types.cc25
-rw-r--r--chromium/components/device_signals/core/common/win/win_types.h47
-rw-r--r--chromium/components/device_signals/core/common/win/win_types_unittest.cc40
-rw-r--r--chromium/components/device_signals/core/common/win/wmi_client.cc16
-rw-r--r--chromium/components/device_signals/core/common/win/wmi_client.h50
-rw-r--r--chromium/components/device_signals/core/common/win/wmi_client_impl.cc109
-rw-r--r--chromium/components/device_signals/core/common/win/wmi_client_impl.h42
-rw-r--r--chromium/components/device_signals/core/common/win/wmi_client_impl_unittest.cc120
-rw-r--r--chromium/components/device_signals/core/common/win/wsc_client.cc15
-rw-r--r--chromium/components/device_signals/core/common/win/wsc_client.h59
-rw-r--r--chromium/components/device_signals/core/common/win/wsc_client_impl.cc125
-rw-r--r--chromium/components/device_signals/core/common/win/wsc_client_impl.h39
-rw-r--r--chromium/components/device_signals/core/common/win/wsc_client_impl_unittest.cc251
-rw-r--r--chromium/components/digital_asset_links/digital_asset_links_handler.cc2
-rw-r--r--chromium/components/digital_goods/DEPS4
-rw-r--r--chromium/components/digital_goods/OWNERS3
-rw-r--r--chromium/components/digital_goods/README.md1
-rw-r--r--chromium/components/digital_goods/mojom/BUILD.gn22
-rw-r--r--chromium/components/digital_goods/mojom/OWNERS (renamed from chromium/components/data_reduction_proxy/core/common/OWNERS)0
-rw-r--r--chromium/components/digital_goods/mojom/digital_goods.mojom87
-rw-r--r--chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc12
-rw-r--r--chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h2
-rw-r--r--chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc2
-rw-r--r--chromium/components/dom_distiller/content/browser/android/BUILD.gn3
-rw-r--r--chromium/components/dom_distiller/content/browser/distillable_page_utils.cc2
-rw-r--r--chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc4
-rw-r--r--chromium/components/dom_distiller/content/renderer/distillability_agent.cc6
-rw-r--r--chromium/components/dom_distiller/core/android/BUILD.gn2
-rw-r--r--chromium/components/dom_distiller/core/javascript/dom_distiller_viewer.js4
-rw-r--r--chromium/components/dom_distiller/core/viewer.cc2
-rw-r--r--chromium/components/dom_distiller/ios/BUILD.gn1
-rw-r--r--chromium/components/dom_distiller/ios/distiller_page_ios.mm11
-rw-r--r--chromium/components/download/internal/background_service/BUILD.gn1
-rw-r--r--chromium/components/download/internal/background_service/config.cc1
-rw-r--r--chromium/components/download/internal/background_service/logger_impl.cc4
-rw-r--r--chromium/components/download/internal/common/BUILD.gn2
-rw-r--r--chromium/components/download/internal/common/download_item_impl.cc30
-rw-r--r--chromium/components/download/internal/common/download_item_impl_delegate.cc3
-rw-r--r--chromium/components/download/internal/common/download_item_impl_unittest.cc85
-rw-r--r--chromium/components/download/internal/common/download_response_handler.cc22
-rw-r--r--chromium/components/download/internal/common/in_progress_download_manager.cc14
-rw-r--r--chromium/components/download/internal/common/resource_downloader.cc5
-rw-r--r--chromium/components/download/network/BUILD.gn3
-rw-r--r--chromium/components/download/public/common/BUILD.gn1
-rw-r--r--chromium/components/download/public/common/download_features.cc3
-rw-r--r--chromium/components/download/public/common/download_features.h4
-rw-r--r--chromium/components/download/public/common/download_item.h8
-rw-r--r--chromium/components/download/public/common/download_item_impl.h6
-rw-r--r--chromium/components/download/public/common/download_item_impl_delegate.h1
-rw-r--r--chromium/components/download/public/common/download_response_handler.h2
-rw-r--r--chromium/components/download/public/common/mock_download_item.h2
-rw-r--r--chromium/components/download/public/common/mock_download_item_impl.h5
-rw-r--r--chromium/components/download/public/task/BUILD.gn5
-rw-r--r--chromium/components/download/resources/download_internals/download_internals.html2
-rw-r--r--chromium/components/download/resources/download_internals/download_internals_browser_proxy.js1
-rw-r--r--chromium/components/embedder_support/OWNERS3
-rw-r--r--chromium/components/embedder_support/android/BUILD.gn19
-rw-r--r--chromium/components/embedder_support/android/metrics/BUILD.gn1
-rw-r--r--chromium/components/embedder_support/android/metrics/android_metrics_service_client.cc7
-rw-r--r--chromium/components/embedder_support/android/metrics/android_metrics_service_client.h2
-rw-r--r--chromium/components/embedder_support/android/util/android_stream_reader_url_loader.cc27
-rw-r--r--chromium/components/embedder_support/android/util/url_utilities.cc4
-rw-r--r--chromium/components/embedder_support/pref_names.cc4
-rw-r--r--chromium/components/embedder_support/pref_names.h1
-rw-r--r--chromium/components/embedder_support/user_agent_utils.cc97
-rw-r--r--chromium/components/embedder_support/user_agent_utils.h55
-rw-r--r--chromium/components/embedder_support/user_agent_utils_unittest.cc471
-rw-r--r--chromium/components/endpoint_fetcher/BUILD.gn41
-rw-r--r--chromium/components/endpoint_fetcher/DEPS8
-rw-r--r--chromium/components/endpoint_fetcher/OWNERS4
-rw-r--r--chromium/components/endpoint_fetcher/README.md3
-rw-r--r--chromium/components/endpoint_fetcher/endpoint_fetcher.cc288
-rw-r--r--chromium/components/endpoint_fetcher/endpoint_fetcher.h169
-rw-r--r--chromium/components/endpoint_fetcher/endpoint_fetcher_unittest.cc167
-rw-r--r--chromium/components/enterprise/BUILD.gn28
-rw-r--r--chromium/components/enterprise/DEPS3
-rw-r--r--chromium/components/enterprise/browser/controller/browser_dm_token_storage.cc2
-rw-r--r--chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.cc42
-rw-r--r--chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h6
-rw-r--r--chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_helper.cc6
-rw-r--r--chromium/components/enterprise/browser/reporting/chrome_profile_request_generator_unittest.cc80
-rw-r--r--chromium/components/enterprise/browser/reporting/fake_browser_report_generator_delegate.cc122
-rw-r--r--chromium/components/enterprise/browser/reporting/fake_browser_report_generator_delegate.h106
-rw-r--r--chromium/components/enterprise/browser/reporting/policy_info.cc11
-rw-r--r--chromium/components/enterprise/browser/reporting/policy_info.h5
-rw-r--r--chromium/components/enterprise/browser/reporting/profile_report_generator.cc2
-rw-r--r--chromium/components/enterprise/browser/reporting/profile_report_generator.h2
-rw-r--r--chromium/components/enterprise/browser/reporting/real_time_uploader.cc4
-rw-r--r--chromium/components/enterprise/browser/reporting/real_time_uploader.h5
-rw-r--r--chromium/components/enterprise/browser/reporting/real_time_uploader_unittest.cc7
-rw-r--r--chromium/components/enterprise/browser/reporting/report_uploader_unittest.cc3
-rw-r--r--chromium/components/enterprise/common/proto/connectors.proto25
-rw-r--r--chromium/components/enterprise/common/proto/connectors_unittest.cc57
-rw-r--r--chromium/components/enterprise/content/clipboard_restriction_service.h5
-rw-r--r--chromium/components/enterprise/content/clipboard_restriction_service_unittest.cc22
-rw-r--r--chromium/components/enterprise/content/copy_prevention_settings_policy_handler.cc10
-rw-r--r--chromium/components/error_page/common/localized_error.cc11
-rw-r--r--chromium/components/error_page/content/browser/net_error_auto_reloader.cc2
-rw-r--r--chromium/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc2
-rw-r--r--chromium/components/exo/BUILD.gn21
-rw-r--r--chromium/components/exo/DEPS8
-rw-r--r--chromium/components/exo/buffer.cc40
-rw-r--r--chromium/components/exo/client_controlled_shell_surface.cc60
-rw-r--r--chromium/components/exo/client_controlled_shell_surface.h6
-rw-r--r--chromium/components/exo/client_controlled_shell_surface_unittest.cc71
-rw-r--r--chromium/components/exo/common_utils.cc27
-rw-r--r--chromium/components/exo/common_utils.h14
-rw-r--r--chromium/components/exo/extended_drag_source.cc59
-rw-r--r--chromium/components/exo/extended_drag_source.h10
-rw-r--r--chromium/components/exo/extended_drag_source_unittest.cc94
-rw-r--r--chromium/components/exo/fullscreen_shell_surface.cc307
-rw-r--r--chromium/components/exo/fullscreen_shell_surface.h106
-rw-r--r--chromium/components/exo/fullscreen_shell_surface_unittest.cc293
-rw-r--r--chromium/components/exo/keyboard.cc51
-rw-r--r--chromium/components/exo/keyboard_unittest.cc80
-rw-r--r--chromium/components/exo/layer_tree_frame_sink_holder.cc52
-rw-r--r--chromium/components/exo/layer_tree_frame_sink_holder.h23
-rw-r--r--chromium/components/exo/pointer.cc6
-rw-r--r--chromium/components/exo/pointer_unittest.cc39
-rw-r--r--chromium/components/exo/seat.cc9
-rw-r--r--chromium/components/exo/seat.h6
-rw-r--r--chromium/components/exo/shell_surface.cc22
-rw-r--r--chromium/components/exo/shell_surface.h1
-rw-r--r--chromium/components/exo/shell_surface_base.cc77
-rw-r--r--chromium/components/exo/shell_surface_base.h22
-rw-r--r--chromium/components/exo/shell_surface_unittest.cc99
-rw-r--r--chromium/components/exo/sub_surface.cc6
-rw-r--r--chromium/components/exo/sub_surface.h2
-rw-r--r--chromium/components/exo/sub_surface_unittest.cc8
-rw-r--r--chromium/components/exo/surface.cc188
-rw-r--r--chromium/components/exo/surface.h28
-rw-r--r--chromium/components/exo/surface_delegate.h8
-rw-r--r--chromium/components/exo/surface_tree_host.cc17
-rw-r--r--chromium/components/exo/surface_tree_host.h6
-rw-r--r--chromium/components/exo/surface_unittest.cc23
-rw-r--r--chromium/components/exo/text_input.cc4
-rw-r--r--chromium/components/exo/text_input.h4
-rw-r--r--chromium/components/exo/touch.cc4
-rw-r--r--chromium/components/exo/touch_unittest.cc33
-rw-r--r--chromium/components/exo/ui_lock_controller.cc153
-rw-r--r--chromium/components/exo/ui_lock_controller.h49
-rw-r--r--chromium/components/exo/ui_lock_controller_unittest.cc595
-rw-r--r--chromium/components/exo/wayland/BUILD.gn31
-rw-r--r--chromium/components/exo/wayland/clients/capability_binding_test.cc190
-rw-r--r--chromium/components/exo/wayland/clients/client_base.cc66
-rw-r--r--chromium/components/exo/wayland/clients/client_base.h6
-rw-r--r--chromium/components/exo/wayland/clients/client_helper.cc2
-rw-r--r--chromium/components/exo/wayland/clients/client_helper.h2
-rw-r--r--chromium/components/exo/wayland/clients/info.cc34
-rw-r--r--chromium/components/exo/wayland/clients/wayland_client_integration_tests.cc215
-rw-r--r--chromium/components/exo/wayland/protocol/aura-shell.xml74
-rw-r--r--chromium/components/exo/wayland/protocol/surface-augmenter.xml19
-rw-r--r--chromium/components/exo/wayland/server.cc29
-rw-r--r--chromium/components/exo/wayland/surface_augmenter.cc21
-rw-r--r--chromium/components/exo/wayland/surface_augmenter.h2
-rw-r--r--chromium/components/exo/wayland/wayland_display_observer.cc80
-rw-r--r--chromium/components/exo/wayland/wayland_display_observer.h18
-rw-r--r--chromium/components/exo/wayland/wayland_display_observer_unittest.cc93
-rw-r--r--chromium/components/exo/wayland/wayland_display_util.cc31
-rw-r--r--chromium/components/exo/wayland/wayland_display_util.h22
-rw-r--r--chromium/components/exo/wayland/wayland_display_util_unittest.cc29
-rw-r--r--chromium/components/exo/wayland/wayland_positioner.cc97
-rw-r--r--chromium/components/exo/wayland/wayland_positioner_unittest.cc31
-rw-r--r--chromium/components/exo/wayland/weston_test.cc3
-rw-r--r--chromium/components/exo/wayland/wl_shell.cc2
-rw-r--r--chromium/components/exo/wayland/xdg_shell.cc27
-rw-r--r--chromium/components/exo/wayland/xdg_shell.h17
-rw-r--r--chromium/components/exo/wayland/zaura_shell.cc204
-rw-r--r--chromium/components/exo/wayland/zaura_shell.h30
-rw-r--r--chromium/components/exo/wayland/zaura_shell_unittest.cc101
-rw-r--r--chromium/components/exo/wayland/zcr_color_manager.cc137
-rw-r--r--chromium/components/exo/wayland/zcr_gaming_input.cc19
-rw-r--r--chromium/components/exo/wayland/zcr_keyboard_configuration.cc56
-rw-r--r--chromium/components/exo/wayland/zcr_remote_shell.cc2
-rw-r--r--chromium/components/exo/wayland/zcr_remote_shell_v2.cc2
-rw-r--r--chromium/components/exo/wayland/zwp_fullscreen_shell.cc67
-rw-r--r--chromium/components/exo/wayland/zwp_fullscreen_shell.h23
-rw-r--r--chromium/components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.cc101
-rw-r--r--chromium/components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.h21
-rw-r--r--chromium/components/exo/wayland/zwp_text_input_manager.cc39
-rw-r--r--chromium/components/exo/wayland/zxdg_output_manager.cc11
-rw-r--r--chromium/components/exo/wayland/zxdg_shell.cc18
-rw-r--r--chromium/components/exo/wm_helper.cc8
-rw-r--r--chromium/components/exo/wm_helper.h13
-rw-r--r--chromium/components/exo/wm_helper_chromeos.cc46
-rw-r--r--chromium/components/exo/wm_helper_chromeos.h15
-rw-r--r--chromium/components/external_intents/android/BUILD.gn14
-rw-r--r--chromium/components/external_intents/android/external_intents_features.cc35
-rw-r--r--chromium/components/external_intents/android/external_intents_features.h17
-rw-r--r--chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java223
-rw-r--r--chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java32
-rw-r--r--chromium/components/favicon/android/BUILD.gn3
-rw-r--r--chromium/components/favicon/content/content_favicon_driver_unittest.cc29
-rw-r--r--chromium/components/favicon_base/favicon_url_parser.cc12
-rw-r--r--chromium/components/feature_engagement/README.md10
-rw-r--r--chromium/components/feature_engagement/internal/BUILD.gn2
-rw-r--r--chromium/components/feature_engagement/internal/chrome_variations_configuration.cc48
-rw-r--r--chromium/components/feature_engagement/internal/system_time_provider.cc1
-rw-r--r--chromium/components/feature_engagement/public/BUILD.gn1
-rw-r--r--chromium/components/feature_engagement/public/event_constants.cc3
-rw-r--r--chromium/components/feature_engagement/public/event_constants.h5
-rw-r--r--chromium/components/feature_engagement/public/feature_configurations.cc40
-rw-r--r--chromium/components/feature_engagement/public/feature_constants.cc15
-rw-r--r--chromium/components/feature_engagement/public/feature_constants.h8
-rw-r--r--chromium/components/feature_engagement/public/feature_list.cc6
-rw-r--r--chromium/components/feature_engagement/public/feature_list.h15
-rw-r--r--chromium/components/feed/content/renderer/rss_link_reader.cc10
-rw-r--r--chromium/components/feed/core/common/pref_names.cc4
-rw-r--r--chromium/components/feed/core/common/pref_names.h4
-rw-r--r--chromium/components/feed/core/proto/BUILD.gn1
-rw-r--r--chromium/components/feed/core/proto/v2/store.proto3
-rw-r--r--chromium/components/feed/core/proto/v2/wire/capability.proto3
-rw-r--r--chromium/components/feed/core/proto/v2/wire/chrome_fulfillment_info.proto3
-rw-r--r--chromium/components/feed/core/proto/v2/wire/client_info.proto4
-rw-r--r--chromium/components/feed/core/proto/v2/wire/client_user_profiles.proto3
-rw-r--r--chromium/components/feed/core/proto/v2/wire/feed_query.proto5
-rw-r--r--chromium/components/feed/core/proto/v2/wire/feed_response.proto2
-rw-r--r--chromium/components/feed/core/proto/v2/wire/info_card.proto29
-rw-r--r--chromium/components/feed/core/proto/v2/wire/request_schedule.proto4
-rw-r--r--chromium/components/feed/core/proto/v2/wire/web_feeds.proto10
-rw-r--r--chromium/components/feed/core/v2/BUILD.gn6
-rw-r--r--chromium/components/feed/core/v2/algorithm_unittest.cc3
-rw-r--r--chromium/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc6
-rw-r--r--chromium/components/feed/core/v2/api_test/feed_api_stream_unittest.cc673
-rw-r--r--chromium/components/feed/core/v2/api_test/feed_api_test.cc8
-rw-r--r--chromium/components/feed/core/v2/api_test/feed_api_test.h6
-rw-r--r--chromium/components/feed/core/v2/config.cc30
-rw-r--r--chromium/components/feed/core/v2/config.h13
-rw-r--r--chromium/components/feed/core/v2/enums.cc5
-rw-r--r--chromium/components/feed/core/v2/enums.h26
-rw-r--r--chromium/components/feed/core/v2/feed_network_impl.cc3
-rw-r--r--chromium/components/feed/core/v2/feed_network_impl_unittest.cc12
-rw-r--r--chromium/components/feed/core/v2/feed_stream.cc173
-rw-r--r--chromium/components/feed/core/v2/feed_stream.h40
-rw-r--r--chromium/components/feed/core/v2/feedstore_util.cc8
-rw-r--r--chromium/components/feed/core/v2/feedstore_util.h3
-rw-r--r--chromium/components/feed/core/v2/metrics_reporter.cc95
-rw-r--r--chromium/components/feed/core/v2/metrics_reporter.h18
-rw-r--r--chromium/components/feed/core/v2/metrics_reporter_unittest.cc118
-rw-r--r--chromium/components/feed/core/v2/proto_util.cc24
-rw-r--r--chromium/components/feed/core/v2/proto_util_unittest.cc76
-rw-r--r--chromium/components/feed/core/v2/protocol_translator.cc6
-rw-r--r--chromium/components/feed/core/v2/public/common_enums.cc26
-rw-r--r--chromium/components/feed/core/v2/public/common_enums.h37
-rw-r--r--chromium/components/feed/core/v2/public/feed_api.h35
-rw-r--r--chromium/components/feed/core/v2/public/feed_service.cc3
-rw-r--r--chromium/components/feed/core/v2/public/types.h4
-rw-r--r--chromium/components/feed/core/v2/scheduling.cc31
-rw-r--r--chromium/components/feed/core/v2/scheduling.h13
-rw-r--r--chromium/components/feed/core/v2/scheduling_unittest.cc149
-rw-r--r--chromium/components/feed/core/v2/stream/info_card_tracker.cc175
-rw-r--r--chromium/components/feed/core/v2/stream/info_card_tracker.h64
-rw-r--r--chromium/components/feed/core/v2/stream/info_card_tracker_unittest.cc257
-rw-r--r--chromium/components/feed/core/v2/stream/notice_card_tracker.cc151
-rw-r--r--chromium/components/feed/core/v2/stream/notice_card_tracker.h79
-rw-r--r--chromium/components/feed/core/v2/stream/notice_card_tracker_unittest.cc176
-rw-r--r--chromium/components/feed/core/v2/stream_model.h12
-rw-r--r--chromium/components/feed/core/v2/stream_model_unittest.cc2
-rw-r--r--chromium/components/feed/core/v2/surface_updater.cc1
-rw-r--r--chromium/components/feed/core/v2/tasks/load_stream_from_store_task.cc17
-rw-r--r--chromium/components/feed/core/v2/tasks/load_stream_from_store_task.h3
-rw-r--r--chromium/components/feed/core/v2/tasks/load_stream_task.cc9
-rw-r--r--chromium/components/feed/core/v2/tasks/load_stream_task.h1
-rw-r--r--chromium/components/feed/core/v2/tasks/prefetch_images_task.cc3
-rw-r--r--chromium/components/feed/core/v2/types.h3
-rw-r--r--chromium/components/feed/core/v2/user_actions_collector.h2
-rw-r--r--chromium/components/feed/core/v2/web_feed_subscription_coordinator.h2
-rw-r--r--chromium/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc29
-rw-r--r--chromium/components/feed/feed_feature_list.cc16
-rw-r--r--chromium/components/feed/feed_feature_list.h21
-rw-r--r--chromium/components/feed/mojom/OWNERS2
-rw-r--r--chromium/components/feedback/OWNERS4
-rw-r--r--chromium/components/feedback/feedback_report.cc3
-rw-r--r--chromium/components/feedback/feedback_uploader.cc1
-rw-r--r--chromium/components/feedback/pii_types.h33
-rw-r--r--chromium/components/feedback/redaction_tool.cc22
-rw-r--r--chromium/components/feedback/redaction_tool_unittest.cc32
-rw-r--r--chromium/components/find_in_page/android/BUILD.gn2
-rw-r--r--chromium/components/find_in_page/find_tab_helper.cc5
-rw-r--r--chromium/components/flags_ui/PRESUBMIT.py27
-rw-r--r--chromium/components/flags_ui/feature_entry.cc112
-rw-r--r--chromium/components/flags_ui/feature_entry.h53
-rw-r--r--chromium/components/flags_ui/feature_entry_macros.h14
-rw-r--r--chromium/components/flags_ui/flags_state.cc105
-rw-r--r--chromium/components/flags_ui/flags_state.h14
-rw-r--r--chromium/components/flags_ui/flags_state_unittest.cc8
-rw-r--r--chromium/components/flags_ui/resources/flags.css2
-rw-r--r--chromium/components/flags_ui/resources/flags.js2
-rw-r--r--chromium/components/fuchsia_component_support/BUILD.gn59
-rw-r--r--chromium/components/fuchsia_component_support/DEPS3
-rw-r--r--chromium/components/fuchsia_component_support/DIR_METADATA1
-rw-r--r--chromium/components/fuchsia_component_support/OWNERS1
-rw-r--r--chromium/components/fuchsia_component_support/README.md5
-rw-r--r--chromium/components/fuchsia_component_support/config_reader.cc83
-rw-r--r--chromium/components/fuchsia_component_support/config_reader.h30
-rw-r--r--chromium/components/fuchsia_component_support/config_reader_unittest.cc98
-rw-r--r--chromium/components/fuchsia_component_support/feedback_registration.cc57
-rw-r--r--chromium/components/fuchsia_component_support/feedback_registration.h30
-rw-r--r--chromium/components/fuchsia_component_support/inspect.cc28
-rw-r--r--chromium/components/fuchsia_component_support/inspect.h20
-rw-r--r--chromium/components/fuchsia_component_support/inspect_unittest.cc98
-rw-r--r--chromium/components/fuchsia_legacymetrics/BUILD.gn43
-rw-r--r--chromium/components/fuchsia_legacymetrics/DIR_METADATA1
-rw-r--r--chromium/components/fuchsia_legacymetrics/OWNERS2
-rw-r--r--chromium/components/fuchsia_legacymetrics/legacymetrics_client.cc299
-rw-r--r--chromium/components/fuchsia_legacymetrics/legacymetrics_client.h124
-rw-r--r--chromium/components/fuchsia_legacymetrics/legacymetrics_client_unittest.cc707
-rw-r--r--chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.cc92
-rw-r--r--chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.h20
-rw-r--r--chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener_unittest.cc167
-rw-r--r--chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.cc48
-rw-r--r--chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.h44
-rw-r--r--chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder_unittest.cc90
-rw-r--r--chromium/components/fullscreen_control/subtle_notification_view.cc6
-rw-r--r--chromium/components/gcm_driver/android/BUILD.gn2
-rw-r--r--chromium/components/gcm_driver/crypto/BUILD.gn3
-rw-r--r--chromium/components/gcm_driver/crypto/encryption_header_parsers.cc12
-rw-r--r--chromium/components/gcm_driver/gcm_profile_service.cc6
-rw-r--r--chromium/components/gcm_driver/instance_id/android/BUILD.gn4
-rw-r--r--chromium/components/global_media_controls/public/media_session_item_producer.cc1
-rw-r--r--chromium/components/global_media_controls/public/media_session_notification_item.cc7
-rw-r--r--chromium/components/global_media_controls/public/media_session_notification_item.h3
-rw-r--r--chromium/components/global_media_controls/public/views/media_item_ui_view_unittest.cc2
-rw-r--r--chromium/components/google/core/common/google_util.cc6
-rw-r--r--chromium/components/guest_view/browser/guest_view_base.cc13
-rw-r--r--chromium/components/guest_view/browser/guest_view_manager.cc2
-rw-r--r--chromium/components/gwp_asan/client/sampling_partitionalloc_shims.cc5
-rw-r--r--chromium/components/heap_profiling/in_process/heap_profiler_controller.cc50
-rw-r--r--chromium/components/heap_profiling/in_process/heap_profiler_controller.h39
-rw-r--r--chromium/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc5
-rw-r--r--chromium/components/heap_profiling/multi_process/BUILD.gn2
-rw-r--r--chromium/components/history/core/DEPS2
-rw-r--r--chromium/components/history/core/browser/BUILD.gn15
-rw-r--r--chromium/components/history/core/browser/OWNERS3
-rw-r--r--chromium/components/history/core/browser/browsing_history_driver.h17
-rw-r--r--chromium/components/history/core/browser/browsing_history_service.h3
-rw-r--r--chromium/components/history/core/browser/expire_history_backend_unittest.cc7
-rw-r--r--chromium/components/history/core/browser/features.cc39
-rw-r--r--chromium/components/history/core/browser/features.h21
-rw-r--r--chromium/components/history/core/browser/history_backend.cc192
-rw-r--r--chromium/components/history/core/browser/history_backend.h66
-rw-r--r--chromium/components/history/core/browser/history_backend_db_unittest.cc89
-rw-r--r--chromium/components/history/core/browser/history_backend_notifier.h4
-rw-r--r--chromium/components/history/core/browser/history_backend_observer.h6
-rw-r--r--chromium/components/history/core/browser/history_backend_unittest.cc175
-rw-r--r--chromium/components/history/core/browser/history_database.cc43
-rw-r--r--chromium/components/history/core/browser/history_database.h25
-rw-r--r--chromium/components/history/core/browser/history_service.cc32
-rw-r--r--chromium/components/history/core/browser/history_service.h19
-rw-r--r--chromium/components/history/core/browser/history_service_observer.h6
-rw-r--r--chromium/components/history/core/browser/history_service_unittest.cc4
-rw-r--r--chromium/components/history/core/browser/history_types.cc56
-rw-r--r--chromium/components/history/core/browser/history_types.h105
-rw-r--r--chromium/components/history/core/browser/in_memory_history_backend.cc1
-rw-r--r--chromium/components/history/core/browser/in_memory_history_backend.h1
-rw-r--r--chromium/components/history/core/browser/keyword_id.h6
-rw-r--r--chromium/components/history/core/browser/keyword_search_term.cc53
-rw-r--r--chromium/components/history/core/browser/keyword_search_term.h80
-rw-r--r--chromium/components/history/core/browser/keyword_search_term_util.cc272
-rw-r--r--chromium/components/history/core/browser/keyword_search_term_util.h66
-rw-r--r--chromium/components/history/core/browser/sync/delete_directive_handler.cc62
-rw-r--r--chromium/components/history/core/browser/sync/delete_directive_handler_unittest.cc3
-rw-r--r--chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.cc14
-rw-r--r--chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.h13
-rw-r--r--chromium/components/history/core/browser/sync/history_model_type_controller_helper.cc44
-rw-r--r--chromium/components/history/core/browser/sync/history_model_type_controller_helper.h53
-rw-r--r--chromium/components/history/core/browser/sync/history_sync_bridge.cc87
-rw-r--r--chromium/components/history/core/browser/sync/history_sync_bridge.h75
-rw-r--r--chromium/components/history/core/browser/sync/history_sync_metadata_database.cc188
-rw-r--r--chromium/components/history/core/browser/sync/history_sync_metadata_database.h86
-rw-r--r--chromium/components/history/core/browser/sync/history_sync_metadata_database_unittest.cc207
-rw-r--r--chromium/components/history/core/browser/sync/typed_url_model_type_controller.cc64
-rw-r--r--chromium/components/history/core/browser/sync/typed_url_model_type_controller.h21
-rw-r--r--chromium/components/history/core/browser/sync/typed_url_sync_bridge.cc64
-rw-r--r--chromium/components/history/core/browser/sync/typed_url_sync_bridge.h1
-rw-r--r--chromium/components/history/core/browser/sync/typed_url_sync_bridge_unittest.cc62
-rw-r--r--chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.cc70
-rw-r--r--chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.h28
-rw-r--r--chromium/components/history/core/browser/sync/typed_url_sync_metadata_database_unittest.cc62
-rw-r--r--chromium/components/history/core/browser/top_sites_impl.cc164
-rw-r--r--chromium/components/history/core/browser/top_sites_impl.h26
-rw-r--r--chromium/components/history/core/browser/top_sites_impl_unittest.cc163
-rw-r--r--chromium/components/history/core/browser/url_database.cc138
-rw-r--r--chromium/components/history/core/browser/url_database.h47
-rw-r--r--chromium/components/history/core/browser/url_database_unittest.cc371
-rw-r--r--chromium/components/history/core/browser/url_row.cc6
-rw-r--r--chromium/components/history/core/browser/url_row.h5
-rw-r--r--chromium/components/history/core/browser/visit_annotations_database.cc30
-rw-r--r--chromium/components/history/core/browser/visit_annotations_database.h4
-rw-r--r--chromium/components/history/core/browser/visit_annotations_database_unittest.cc18
-rw-r--r--chromium/components/history/core/browser/visit_database.cc96
-rw-r--r--chromium/components/history/core/browser/visit_database.h13
-rw-r--r--chromium/components/history/core/browser/visit_database_unittest.cc9
-rw-r--r--chromium/components/history/core/browser/visitsegment_database.cc12
-rw-r--r--chromium/components/history/core/browser/visitsegment_database.h7
-rw-r--r--chromium/components/history_clusters/OWNERS1
-rw-r--r--chromium/components/history_clusters/core/BUILD.gn107
-rw-r--r--chromium/components/history_clusters/core/cluster_metrics_utils.cc59
-rw-r--r--chromium/components/history_clusters/core/cluster_metrics_utils.h39
-rw-r--r--chromium/components/history_clusters/core/clusterer.cc10
-rw-r--r--chromium/components/history_clusters/core/clusterer_unittest.cc17
-rw-r--r--chromium/components/history_clusters/core/clustering_test_utils.cc2
-rw-r--r--chromium/components/history_clusters/core/config.cc104
-rw-r--r--chromium/components/history_clusters/core/config.h92
-rw-r--r--chromium/components/history_clusters/core/features.cc5
-rw-r--r--chromium/components/history_clusters/core/features.h5
-rw-r--r--chromium/components/history_clusters/core/history_clusters_db_tasks.cc217
-rw-r--r--chromium/components/history_clusters/core/history_clusters_db_tasks.h59
-rw-r--r--chromium/components/history_clusters/core/history_clusters_db_tasks_unittest.cc6
-rw-r--r--chromium/components/history_clusters/core/history_clusters_debug_jsons.cc174
-rw-r--r--chromium/components/history_clusters/core/history_clusters_debug_jsons.h35
-rw-r--r--chromium/components/history_clusters/core/history_clusters_service.cc340
-rw-r--r--chromium/components/history_clusters/core/history_clusters_service.h84
-rw-r--r--chromium/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.cc148
-rw-r--r--chromium/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.h89
-rw-r--r--chromium/components/history_clusters/core/history_clusters_service_test_api.cc5
-rw-r--r--chromium/components/history_clusters/core/history_clusters_service_test_api.h11
-rw-r--r--chromium/components/history_clusters/core/history_clusters_service_unittest.cc256
-rw-r--r--chromium/components/history_clusters/core/history_clusters_types.h53
-rw-r--r--chromium/components/history_clusters/core/history_clusters_util.cc204
-rw-r--r--chromium/components/history_clusters/core/history_clusters_util.h19
-rw-r--r--chromium/components/history_clusters/core/history_clusters_util_unittest.cc161
-rw-r--r--chromium/components/history_clusters/core/keyword_cluster_finalizer.cc229
-rw-r--r--chromium/components/history_clusters/core/keyword_cluster_finalizer.h17
-rw-r--r--chromium/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc108
-rw-r--r--chromium/components/history_clusters/core/label_cluster_finalizer.cc51
-rw-r--r--chromium/components/history_clusters/core/label_cluster_finalizer_unittest.cc18
-rw-r--r--chromium/components/history_clusters/core/on_device_clustering_backend.cc197
-rw-r--r--chromium/components/history_clusters/core/on_device_clustering_backend.h32
-rw-r--r--chromium/components/history_clusters/core/on_device_clustering_backend_unittest.cc213
-rw-r--r--chromium/components/history_clusters/core/on_device_clustering_features.cc5
-rw-r--r--chromium/components/history_clusters/core/on_device_clustering_features.h3
-rw-r--r--chromium/components/history_clusters/core/on_device_clustering_util.cc2
-rw-r--r--chromium/components/history_clusters/core/query_clusters_state.cc54
-rw-r--r--chromium/components/history_clusters/core/query_clusters_state.h23
-rw-r--r--chromium/components/history_clusters/core/query_clusters_state_unittest.cc210
-rw-r--r--chromium/components/history_clusters/core/ranking_cluster_finalizer.cc1
-rw-r--r--chromium/components/history_clusters/core/similar_visit_deduper_cluster_finalizer_unittest.cc2
-rw-r--r--chromium/components/history_clusters/core/single_domain_cluster_finalizer.cc38
-rw-r--r--chromium/components/history_clusters/core/single_domain_cluster_finalizer.h25
-rw-r--r--chromium/components/history_clusters/core/single_domain_cluster_finalizer_unittest.cc90
-rw-r--r--chromium/components/history_clusters/core/url_deduper_cluster_finalizer.cc48
-rw-r--r--chromium/components/history_clusters/core/url_deduper_cluster_finalizer.h24
-rw-r--r--chromium/components/history_clusters/core/url_deduper_cluster_finalizer_unittest.cc168
-rw-r--r--chromium/components/history_clusters/history_clusters_internals/resources/BUILD.gn20
-rw-r--r--chromium/components/history_clusters_strings.grdp8
-rw-r--r--chromium/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_ACTION_MENU_DESCRIPTION.png.sha11
-rw-r--r--chromium/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_SEARCH_YOUR_JOURNEYS.png.sha11
-rw-r--r--chromium/components/image_fetcher/BUILD.gn4
-rw-r--r--chromium/components/image_fetcher/DEPS1
-rw-r--r--chromium/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherBridgeTest.java19
-rw-r--r--chromium/components/image_fetcher/core/BUILD.gn2
-rw-r--r--chromium/components/image_fetcher/core/cached_image_fetcher.cc3
-rw-r--r--chromium/components/image_fetcher/core/fake_image_decoder.cc1
-rw-r--r--chromium/components/image_fetcher/core/fake_image_decoder.h1
-rw-r--r--chromium/components/image_fetcher/core/features.cc17
-rw-r--r--chromium/components/image_fetcher/core/features.h21
-rw-r--r--chromium/components/image_fetcher/core/image_decoder.h10
-rw-r--r--chromium/components/image_fetcher/core/image_fetcher.h18
-rw-r--r--chromium/components/image_fetcher/core/image_fetcher_impl.cc2
-rw-r--r--chromium/components/image_fetcher/core/image_fetcher_metrics_reporter.cc1
-rw-r--r--chromium/components/image_fetcher/core/mock_image_decoder.h3
-rw-r--r--chromium/components/image_fetcher/image_fetcher_bridge.cc26
-rw-r--r--chromium/components/image_fetcher/image_fetcher_bridge.h2
-rw-r--r--chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm2
-rw-r--r--chromium/components/image_fetcher/ios/ios_image_decoder_impl_unittest.mm4
-rw-r--r--chromium/components/infobars/android/BUILD.gn4
-rw-r--r--chromium/components/installedapp/android/BUILD.gn3
-rw-r--r--chromium/components/invalidation/impl/BUILD.gn4
-rw-r--r--chromium/components/javascript_dialogs/android/BUILD.gn2
-rw-r--r--chromium/components/javascript_dialogs/app_modal_dialog_manager.cc5
-rw-r--r--chromium/components/javascript_dialogs/tab_modal_dialog_manager.cc10
-rw-r--r--chromium/components/js_injection/browser/js_communication_host.cc8
-rw-r--r--chromium/components/js_injection/browser/js_to_browser_messaging.cc7
-rw-r--r--chromium/components/js_injection/browser/web_message_reply_proxy.h5
-rw-r--r--chromium/components/keyed_service/core/BUILD.gn1
-rw-r--r--chromium/components/keyed_service/core/dependency_manager.cc4
-rw-r--r--chromium/components/keyed_service/core/dependency_manager.h3
-rw-r--r--chromium/components/keyed_service/core/keyed_service.cc11
-rw-r--r--chromium/components/keyed_service/core/keyed_service.h12
-rw-r--r--chromium/components/keyed_service/core/keyed_service_base_factory.h3
-rw-r--r--chromium/components/keyed_service/core/keyed_service_factory.cc4
-rw-r--r--chromium/components/keyed_service/core/keyed_service_factory.h1
-rw-r--r--chromium/components/keyed_service/core/refcounted_keyed_service_factory.cc4
-rw-r--r--chromium/components/keyed_service/core/refcounted_keyed_service_factory.h1
-rw-r--r--chromium/components/language/android/BUILD.gn22
-rw-r--r--chromium/components/language/android/geo_language_provider_bridge.cc15
-rw-r--r--chromium/components/language/content/browser/geo_language_provider.cc31
-rw-r--r--chromium/components/language/content/browser/geo_language_provider.h1
-rw-r--r--chromium/components/language/content/browser/geo_language_provider_unittest.cc24
-rw-r--r--chromium/components/language/core/browser/BUILD.gn4
-rw-r--r--chromium/components/language/core/browser/accept_languages_service.cc72
-rw-r--r--chromium/components/language/core/browser/accept_languages_service.h59
-rw-r--r--chromium/components/language/core/browser/accept_languages_service_unittest.cc73
-rw-r--r--chromium/components/language/core/browser/language_prefs_test_util.h3
-rw-r--r--chromium/components/language/core/common/language_experiments.cc4
-rw-r--r--chromium/components/language/core/common/language_experiments.h7
-rw-r--r--chromium/components/language/core/common/language_util.cc21
-rw-r--r--chromium/components/language/core/common/language_util_unittest.cc14
-rw-r--r--chromium/components/lens/OWNERS3
-rw-r--r--chromium/components/lens/lens_entrypoints.cc5
-rw-r--r--chromium/components/lens/lens_entrypoints.h1
-rw-r--r--chromium/components/lens/lens_entrypoints_unittest.cc17
-rw-r--r--chromium/components/lens/lens_features.cc73
-rw-r--r--chromium/components/lens/lens_features.h42
-rw-r--r--chromium/components/live_caption/BUILD.gn2
-rw-r--r--chromium/components/live_caption/DEPS2
-rw-r--r--chromium/components/live_caption/caption_bubble_controller.h2
-rw-r--r--chromium/components/live_caption/caption_util.cc11
-rw-r--r--chromium/components/live_caption/live_caption_controller.cc2
-rw-r--r--chromium/components/live_caption/live_caption_controller.h2
-rw-r--r--chromium/components/live_caption/views/caption_bubble.cc20
-rw-r--r--chromium/components/live_caption/views/caption_bubble.h1
-rw-r--r--chromium/components/live_caption/views/caption_bubble_controller_views.h1
-rw-r--r--chromium/components/local_state/BUILD.gn43
-rw-r--r--chromium/components/local_state/DEPS (renamed from chromium/components/contextual_search/core/DEPS)2
-rw-r--r--chromium/components/local_state/OWNERS1
-rw-r--r--chromium/components/local_state/README.md2
-rw-r--r--chromium/components/local_state/local_state.html15
-rw-r--r--chromium/components/local_state/local_state.ts26
-rw-r--r--chromium/components/local_state/local_state_utils.cc68
-rw-r--r--chromium/components/local_state/local_state_utils.h32
-rw-r--r--chromium/components/local_state/local_state_utils_unittest.cc34
-rw-r--r--chromium/components/location/android/BUILD.gn2
-rw-r--r--chromium/components/lookalikes/core/BUILD.gn1
-rw-r--r--chromium/components/lookalikes/core/features.cc7
-rw-r--r--chromium/components/lookalikes/core/features.h8
-rw-r--r--chromium/components/lookalikes/core/lookalike_url_util.cc20
-rw-r--r--chromium/components/management/resources/images/enterprise_icon.svg2
-rw-r--r--chromium/components/media_control/renderer/BUILD.gn4
-rw-r--r--chromium/components/media_message_center/media_notification_view_modern_impl.cc6
-rw-r--r--chromium/components/media_router/browser/android/BUILD.gn6
-rw-r--r--chromium/components/media_router/browser/media_router_base.h2
-rw-r--r--chromium/components/media_router/browser/media_router_dialog_controller.cc2
-rw-r--r--chromium/components/media_router/browser/media_router_metrics.h20
-rw-r--r--chromium/components/media_router/browser/route_message_util.cc5
-rw-r--r--chromium/components/media_router/common/discovery/media_sink_internal.cc2
-rw-r--r--chromium/components/media_router/common/discovery/media_sink_internal.h15
-rw-r--r--chromium/components/media_router/common/discovery/media_sink_service_base.cc4
-rw-r--r--chromium/components/media_router/common/discovery/media_sink_service_base.h1
-rw-r--r--chromium/components/media_router/common/mojom/media_router_mojom_traits.cc14
-rw-r--r--chromium/components/media_router/common/providers/cast/cast_media_source.cc12
-rw-r--r--chromium/components/media_router/test/android/cast_emulator/BUILD.gn1
-rw-r--r--chromium/components/memory_pressure/system_memory_pressure_evaluator_fuchsia_unittest.cc4
-rw-r--r--chromium/components/memory_pressure/system_memory_pressure_evaluator_mac.cc10
-rw-r--r--chromium/components/messages/android/BUILD.gn2
-rw-r--r--chromium/components/messages/android/internal/BUILD.gn2
-rw-r--r--chromium/components/messages/android/internal/java/res/layout/message_banner_view.xml11
-rw-r--r--chromium/components/messages/android/internal/java/res/values/dimens.xml1
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinator.java73
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java15
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerView.java22
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewBinder.java5
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewTest.java317
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManager.java72
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java19
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageScopeChange.java13
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java22
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java58
-rw-r--r--chromium/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java20
-rw-r--r--chromium/components/messages/android/message_enums.h12
-rw-r--r--chromium/components/messages/android/message_wrapper.cc7
-rw-r--r--chromium/components/messages/android/message_wrapper.h4
-rw-r--r--chromium/components/messages/android/messages_feature.cc10
-rw-r--r--chromium/components/messages/android/messages_feature.h6
-rw-r--r--chromium/components/messages/android/test/BUILD.gn2
-rw-r--r--chromium/components/metal_util/device_removal.mm31
-rw-r--r--chromium/components/metal_util/hdr_copier_layer.mm5
-rw-r--r--chromium/components/metrics/child_call_stack_profile_collector_unittest.cc30
-rw-r--r--chromium/components/metrics/clean_exit_beacon.cc167
-rw-r--r--chromium/components/metrics/clean_exit_beacon.h33
-rw-r--r--chromium/components/metrics/clean_exit_beacon_unittest.cc267
-rw-r--r--chromium/components/metrics/content/subprocess_metrics_provider_browsertest.cc10
-rw-r--r--chromium/components/metrics/daily_event.h1
-rw-r--r--chromium/components/metrics/field_trials_provider.cc3
-rw-r--r--chromium/components/metrics/field_trials_provider.h6
-rw-r--r--chromium/components/metrics/field_trials_provider_unittest.cc100
-rw-r--r--chromium/components/metrics/file_metrics_provider.cc6
-rw-r--r--chromium/components/metrics/generate_expired_histograms_array.gni1
-rw-r--r--chromium/components/metrics/machine_id_provider_nonwin.cc1
-rw-r--r--chromium/components/metrics/metrics_data_validation.cc7
-rw-r--r--chromium/components/metrics/metrics_data_validation.h6
-rw-r--r--chromium/components/metrics/metrics_data_validation_unittest.cc6
-rw-r--r--chromium/components/metrics/metrics_log_unittest.cc1
-rw-r--r--chromium/components/metrics/metrics_pref_names.cc41
-rw-r--r--chromium/components/metrics/metrics_pref_names.h8
-rw-r--r--chromium/components/metrics/metrics_service.cc27
-rw-r--r--chromium/components/metrics/metrics_service.h32
-rw-r--r--chromium/components/metrics/metrics_service_accessor.cc13
-rw-r--r--chromium/components/metrics/metrics_service_accessor.h8
-rw-r--r--chromium/components/metrics/metrics_service_unittest.cc55
-rw-r--r--chromium/components/metrics/metrics_state_manager.cc119
-rw-r--r--chromium/components/metrics/metrics_state_manager.h22
-rw-r--r--chromium/components/metrics/metrics_state_manager_unittest.cc52
-rw-r--r--chromium/components/metrics/metrics_switches.cc2
-rw-r--r--chromium/components/metrics/net/cellular_logic_helper.cc13
-rw-r--r--chromium/components/metrics/net/net_metrics_log_uploader.cc1
-rw-r--r--chromium/components/metrics/serialization/serialization_utils.cc10
-rw-r--r--chromium/components/metrics/stability_metrics_helper.cc9
-rw-r--r--chromium/components/metrics/structured/event_base.cc9
-rw-r--r--chromium/components/metrics/structured/event_base.h8
-rw-r--r--chromium/components/metrics/structured/key_data.cc48
-rw-r--r--chromium/components/metrics/structured/key_data.h22
-rw-r--r--chromium/components/metrics/structured/key_data_unittest.cc125
-rw-r--r--chromium/components/metrics/structured/mojom/event_mojom_traits.cc25
-rw-r--r--chromium/components/metrics/structured/project_validator.cc6
-rw-r--r--chromium/components/metrics/structured/project_validator.h5
-rw-r--r--chromium/components/metrics/structured/recorder.cc6
-rw-r--r--chromium/components/metrics/structured/recorder.h5
-rw-r--r--chromium/components/metrics/structured/structured_metrics_features.cc6
-rw-r--r--chromium/components/metrics/structured/structured_metrics_features.h3
-rw-r--r--chromium/components/metrics/structured/structured_metrics_provider.cc27
-rw-r--r--chromium/components/metrics/structured/structured_metrics_provider.h5
-rw-r--r--chromium/components/metrics/structured/structured_metrics_provider_unittest.cc57
-rw-r--r--chromium/components/metrics/system_memory_stats_recorder_linux.cc8
-rw-r--r--chromium/components/metrics/unsent_log_store.cc20
-rw-r--r--chromium/components/metrics/unsent_log_store_unittest.cc38
-rw-r--r--chromium/components/metrics_services_manager/metrics_services_manager.cc27
-rw-r--r--chromium/components/minidump_uploader/BUILD.gn2
-rw-r--r--chromium/components/minidump_uploader/OWNERS2
-rw-r--r--chromium/components/minidump_uploader/rewrite_minidumps_as_mimes.cc4
-rw-r--r--chromium/components/mirroring/browser/single_client_video_capture_host_unittest.cc8
-rw-r--r--chromium/components/mirroring/mojom/mirroring_service.mojom2
-rw-r--r--chromium/components/mirroring/mojom/resource_provider.mojom5
-rw-r--r--chromium/components/mirroring/service/BUILD.gn1
-rw-r--r--chromium/components/mirroring/service/captured_audio_input_unittest.cc3
-rw-r--r--chromium/components/mirroring/service/mirroring_features.cc5
-rw-r--r--chromium/components/mirroring/service/remoting_sender.cc48
-rw-r--r--chromium/components/mirroring/service/remoting_sender.h15
-rw-r--r--chromium/components/mirroring/service/remoting_sender_unittest.cc12
-rw-r--r--chromium/components/mirroring/service/session.cc2
-rw-r--r--chromium/components/mirroring/service/video_capture_client.cc66
-rw-r--r--chromium/components/mirroring/service/video_capture_client.h20
-rw-r--r--chromium/components/mirroring/service/video_capture_client_unittest.cc5
-rw-r--r--chromium/components/ml/mojom/web_platform_model.mojom6
-rw-r--r--chromium/components/module_installer/android/BUILD.gn4
-rw-r--r--chromium/components/module_installer/android/module_desc_java.gni5
-rw-r--r--chromium/components/nacl/features.gni4
-rw-r--r--chromium/components/navigation_interception/android/BUILD.gn3
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_delegate.cc21
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_delegate.h7
-rw-r--r--chromium/components/navigation_metrics/navigation_metrics.cc3
-rw-r--r--chromium/components/net_log/chrome_net_log.cc26
-rw-r--r--chromium/components/net_log/chrome_net_log.h7
-rw-r--r--chromium/components/net_log/net_export_file_writer.cc17
-rw-r--r--chromium/components/net_log/net_export_file_writer.h4
-rw-r--r--chromium/components/net_log/net_export_file_writer_unittest.cc51
-rw-r--r--chromium/components/neterror/resources/neterror.js4
-rw-r--r--chromium/components/neterror/resources/offline.js5
-rw-r--r--chromium/components/network_session_configurator/browser/network_session_configurator.cc55
-rw-r--r--chromium/components/network_session_configurator/browser/network_session_configurator_unittest.cc36
-rw-r--r--chromium/components/network_time/network_time_test_utils.cc71
-rw-r--r--chromium/components/network_time/network_time_test_utils.h14
-rw-r--r--chromium/components/network_time/network_time_tracker.cc193
-rw-r--r--chromium/components/network_time/network_time_tracker.h42
-rw-r--r--chromium/components/network_time/network_time_tracker_unittest.cc176
-rw-r--r--chromium/components/new_or_sad_tab_strings.grdp4
-rw-r--r--chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.cc10
-rw-r--r--chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.h2
-rw-r--r--chromium/components/no_state_prefetch/browser/no_state_prefetch_link_manager.h2
-rw-r--r--chromium/components/no_state_prefetch/browser/no_state_prefetch_manager.cc7
-rw-r--r--chromium/components/no_state_prefetch/browser/prerender_history.cc19
-rw-r--r--chromium/components/no_state_prefetch/browser/prerender_history.h9
-rw-r--r--chromium/components/no_state_prefetch/browser/prerender_history_unittest.cc56
-rw-r--r--chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc13
-rw-r--r--chromium/components/ntp_snippets/remote/cached_image_fetcher.cc1
-rw-r--r--chromium/components/ntp_snippets/remote/json_request.cc50
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc17
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl.cc11
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl.h8
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc32
-rw-r--r--chromium/components/offline_items_collection/core/BUILD.gn4
-rw-r--r--chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.cc3
-rw-r--r--chromium/components/offline_items_collection/core/android/offline_item_bridge.cc2
-rw-r--r--chromium/components/offline_items_collection/core/offline_content_aggregator.cc3
-rw-r--r--chromium/components/offline_pages/content/background_loader/DEPS1
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents.cc4
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc27
-rw-r--r--chromium/components/offline_pages/core/model/offline_page_model_taskified.cc16
-rw-r--r--chromium/components/offline_pages/core/model/offline_page_model_taskified.h6
-rw-r--r--chromium/components/offline_pages/core/offline_page_archive_publisher.h5
-rw-r--r--chromium/components/offline_pages/core/offline_page_test_archive_publisher.cc7
-rw-r--r--chromium/components/offline_pages/core/offline_page_test_archive_publisher.h6
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc2
-rw-r--r--chromium/components/offline_pages/resources/renovations.js2
-rw-r--r--chromium/components/omnibox/browser/BUILD.gn49
-rw-r--r--chromium/components/omnibox/browser/test_java_sources.gni8
-rw-r--r--chromium/components/omnibox_strings.grdp6
-rw-r--r--chromium/components/omnibox_strings_grdp/IDS_SEARCH_ENGINES_STARTER_PACK_TABS_KEYWORD.png.sha11
-rw-r--r--chromium/components/omnibox_strings_grdp/IDS_SEARCH_ENGINES_STARTER_PACK_TABS_NAME.png.sha11
-rw-r--r--chromium/components/optimization_guide/BUILD.gn6
-rw-r--r--chromium/components/optimization_guide/content/browser/BUILD.gn4
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.cc485
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.h137
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager_unittest.cc626
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_service.cc333
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_service.h95
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc45
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_validator.cc139
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_validator.h57
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_validator_unittest.cc325
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.cc204
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.h23
-rw-r--r--chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer_unittest.cc241
-rw-r--r--chromium/components/optimization_guide/content/browser/page_text_dump_result.cc3
-rw-r--r--chromium/components/optimization_guide/content/browser/page_text_dump_result_unittest.cc12
-rw-r--r--chromium/components/optimization_guide/content/browser/page_text_observer.cc5
-rw-r--r--chromium/components/optimization_guide/content/browser/page_text_observer_unittest.cc157
-rw-r--r--chromium/components/optimization_guide/content/browser/test_page_content_annotator.cc8
-rw-r--r--chromium/components/optimization_guide/content/browser/test_page_content_annotator.h14
-rw-r--r--chromium/components/optimization_guide/core/BUILD.gn14
-rw-r--r--chromium/components/optimization_guide/core/base_model_executor.h27
-rw-r--r--chromium/components/optimization_guide/core/bert_model_executor.cc12
-rw-r--r--chromium/components/optimization_guide/core/bert_model_executor.h3
-rw-r--r--chromium/components/optimization_guide/core/bert_model_executor_unittest.cc8
-rw-r--r--chromium/components/optimization_guide/core/bert_model_handler.cc1
-rw-r--r--chromium/components/optimization_guide/core/entity_annotator_native_library.cc46
-rw-r--r--chromium/components/optimization_guide/core/entity_annotator_native_library.h15
-rw-r--r--chromium/components/optimization_guide/core/entity_metadata.cc268
-rw-r--r--chromium/components/optimization_guide/core/entity_metadata.h116
-rw-r--r--chromium/components/optimization_guide/core/hints_fetcher.cc2
-rw-r--r--chromium/components/optimization_guide/core/hints_fetcher_unittest.cc9
-rw-r--r--chromium/components/optimization_guide/core/hints_manager.cc96
-rw-r--r--chromium/components/optimization_guide/core/hints_manager.h8
-rw-r--r--chromium/components/optimization_guide/core/hints_manager_unittest.cc85
-rw-r--r--chromium/components/optimization_guide/core/insertion_ordered_set.h2
-rw-r--r--chromium/components/optimization_guide/core/local_page_entities_metadata_provider_unittest.cc3
-rw-r--r--chromium/components/optimization_guide/core/model_execution_timeout_watchdog.h73
-rw-r--r--chromium/components/optimization_guide/core/model_executor.h3
-rw-r--r--chromium/components/optimization_guide/core/model_handler.h12
-rw-r--r--chromium/components/optimization_guide/core/model_util.cc85
-rw-r--r--chromium/components/optimization_guide/core/model_util.h11
-rw-r--r--chromium/components/optimization_guide/core/model_util_unittest.cc172
-rw-r--r--chromium/components/optimization_guide/core/model_validator.cc1
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_features.cc156
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_features.h64
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_features_unittest.cc138
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_logger.cc6
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_logger.h3
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_permissions_util.cc2
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_permissions_util.h3
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_permissions_util_unittest.cc15
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_prefs.cc5
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_prefs.h1
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_store.cc3
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_switches.cc123
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_switches.h35
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_test_util.cc4
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_util.cc72
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_util.h7
-rw-r--r--chromium/components/optimization_guide/core/optimization_guide_util_unittest.cc142
-rw-r--r--chromium/components/optimization_guide/core/page_content_annotation_type.cc25
-rw-r--r--chromium/components/optimization_guide/core/page_content_annotation_type.h37
-rw-r--r--chromium/components/optimization_guide/core/page_content_annotations_common.cc79
-rw-r--r--chromium/components/optimization_guide/core/page_content_annotations_common.h33
-rw-r--r--chromium/components/optimization_guide/core/page_entities_model_executor.cc32
-rw-r--r--chromium/components/optimization_guide/core/page_entities_model_executor.h34
-rw-r--r--chromium/components/optimization_guide/core/page_entities_model_executor_impl.cc44
-rw-r--r--chromium/components/optimization_guide/core/page_entities_model_executor_impl.h15
-rw-r--r--chromium/components/optimization_guide/core/page_entities_model_executor_impl_unittest.cc100
-rw-r--r--chromium/components/optimization_guide/core/page_topics_model_executor.cc40
-rw-r--r--chromium/components/optimization_guide/core/page_topics_model_executor.h10
-rw-r--r--chromium/components/optimization_guide/core/page_topics_model_executor_unittest.cc76
-rw-r--r--chromium/components/optimization_guide/core/page_visibility_model_executor_unittest.cc3
-rw-r--r--chromium/components/optimization_guide/core/prediction_manager.cc143
-rw-r--r--chromium/components/optimization_guide/core/prediction_manager.h21
-rw-r--r--chromium/components/optimization_guide/core/prediction_manager_unittest.cc27
-rw-r--r--chromium/components/optimization_guide/core/prediction_model_download_manager.cc50
-rw-r--r--chromium/components/optimization_guide/core/prediction_model_download_manager.h20
-rw-r--r--chromium/components/optimization_guide/core/prediction_model_download_manager_unittest.cc1
-rw-r--r--chromium/components/optimization_guide/core/prediction_model_override.cc161
-rw-r--r--chromium/components/optimization_guide/core/prediction_model_override.h24
-rw-r--r--chromium/components/optimization_guide/core/store_update_data.cc2
-rw-r--r--chromium/components/optimization_guide/core/store_update_data_unittest.cc40
-rw-r--r--chromium/components/optimization_guide/core/test_model_executor.h1
-rw-r--r--chromium/components/optimization_guide/core/test_model_handler.h1
-rw-r--r--chromium/components/optimization_guide/core/test_tflite_model_handler.h1
-rw-r--r--chromium/components/optimization_guide/core/tflite_model_executor.h39
-rw-r--r--chromium/components/optimization_guide/core/tflite_model_executor_unittest.cc36
-rw-r--r--chromium/components/optimization_guide/core/tflite_op_resolver.cc14
-rw-r--r--chromium/components/optimization_guide/optimization_guide_internals/resources/BUILD.gn17
-rw-r--r--chromium/components/optimization_guide/proto/models.proto5
-rw-r--r--chromium/components/optimization_guide/proto/page_entities_metadata.proto4
-rw-r--r--chromium/components/os_crypt/BUILD.gn11
-rw-r--r--chromium/components/os_crypt/key_storage_keyring_unittest.cc20
-rw-r--r--chromium/components/os_crypt/key_storage_util_linux.cc1
-rw-r--r--chromium/components/os_crypt/libsecret_util_linux.cc1
-rw-r--r--chromium/components/os_crypt/os_crypt.h160
-rw-r--r--chromium/components/os_crypt/os_crypt_linux.cc199
-rw-r--r--chromium/components/os_crypt/os_crypt_linux_unittest.cc3
-rw-r--r--chromium/components/os_crypt/os_crypt_mac.mm152
-rw-r--r--chromium/components/os_crypt/os_crypt_mocker_linux.cc5
-rw-r--r--chromium/components/os_crypt/os_crypt_win.cc125
-rw-r--r--chromium/components/ownership/BUILD.gn2
-rw-r--r--chromium/components/page_info/DEPS1
-rw-r--r--chromium/components/page_info/android/BUILD.gn10
-rw-r--r--chromium/components/page_info/core/BUILD.gn1
-rw-r--r--chromium/components/page_info/core/about_this_site_service.cc17
-rw-r--r--chromium/components/page_info/core/about_this_site_service_unittest.cc15
-rw-r--r--chromium/components/page_info/core/about_this_site_validation.cc23
-rw-r--r--chromium/components/page_info/core/about_this_site_validation.h5
-rw-r--r--chromium/components/page_info/core/about_this_site_validation_unittest.cc48
-rw-r--r--chromium/components/page_info/core/features.cc20
-rw-r--r--chromium/components/page_info/core/features.h7
-rw-r--r--chromium/components/page_info/core/proto/about_this_site_metadata.proto20
-rw-r--r--chromium/components/page_info/page_info.cc186
-rw-r--r--chromium/components/page_info/page_info.h3
-rw-r--r--chromium/components/page_info/page_info_ui.cc50
-rw-r--r--chromium/components/page_info/page_info_ui.h6
-rw-r--r--chromium/components/page_info/page_info_ui_delegate.h3
-rw-r--r--chromium/components/page_info_strings.grdp7
-rw-r--r--chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha11
-rw-r--r--chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP.png.sha11
-rw-r--r--chromium/components/page_load_metrics/OWNERS2
-rw-r--r--chromium/components/page_load_metrics/browser/BUILD.gn3
-rw-r--r--chromium/components/page_load_metrics/browser/DEPS1
-rw-r--r--chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc28
-rw-r--r--chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h5
-rw-r--r--chromium/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc50
-rw-r--r--chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.cc115
-rw-r--r--chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.h12
-rw-r--r--chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc27
-rw-r--r--chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.cc17
-rw-r--r--chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.h22
-rw-r--r--chromium/components/page_load_metrics/browser/observers/ad_metrics/frame_tree_data.cc9
-rw-r--r--chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.cc211
-rw-r--r--chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.h69
-rw-r--r--chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker_unittest.cc284
-rw-r--r--chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc8
-rw-r--r--chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h3
-rw-r--r--chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer_unittest.cc6
-rw-r--r--chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.cc156
-rw-r--r--chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h18
-rw-r--r--chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.cc8
-rw-r--r--chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.h3
-rw-r--r--chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer_unittest.cc34
-rw-r--r--chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.cc8
-rw-r--r--chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.h3
-rw-r--r--chromium/components/page_load_metrics/browser/observers/fenced_frames_page_load_metrics_observer_unittest.cc8
-rw-r--r--chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.cc8
-rw-r--r--chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.h3
-rw-r--r--chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.cc16
-rw-r--r--chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h2
-rw-r--r--chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc52
-rw-r--r--chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h1
-rw-r--r--chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.cc54
-rw-r--r--chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.h6
-rw-r--r--chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc12
-rw-r--r--chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc45
-rw-r--r--chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h46
-rw-r--r--chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer_unittest.cc38
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.cc76
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.h17
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer_unittest.cc4
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_memory_tracker_unittest.cc2
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc17
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_observer.h570
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h1
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_observer_interface.cc12
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_observer_interface.h537
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc110
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h20
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc58
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h19
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_tracker.cc239
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_tracker.h29
-rw-r--r--chromium/components/page_load_metrics/browser/page_load_tracker_unittest.cc103
-rw-r--r--chromium/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc31
-rw-r--r--chromium/components/page_load_metrics/common/page_load_metrics.mojom35
-rw-r--r--chromium/components/page_load_metrics/renderer/BUILD.gn2
-rw-r--r--chromium/components/page_load_metrics/renderer/DEPS1
-rw-r--r--chromium/components/page_load_metrics/renderer/fake_page_timing_sender.cc21
-rw-r--r--chromium/components/page_load_metrics/renderer/fake_page_timing_sender.h20
-rw-r--r--chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc61
-rw-r--r--chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h13
-rw-r--r--chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc26
-rw-r--r--chromium/components/page_load_metrics/renderer/page_resource_data_use.cc8
-rw-r--r--chromium/components/page_load_metrics/renderer/page_resource_data_use.h9
-rw-r--r--chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.cc63
-rw-r--r--chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.h27
-rw-r--r--chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder_unittest.cc122
-rw-r--r--chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc20
-rw-r--r--chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.h7
-rw-r--r--chromium/components/page_load_metrics/renderer/page_timing_metrics_sender_unittest.cc22
-rw-r--r--chromium/components/paint_preview/DEPS1
-rw-r--r--chromium/components/paint_preview/browser/paint_preview_base_service.cc4
-rw-r--r--chromium/components/paint_preview/browser/paint_preview_client.cc13
-rw-r--r--chromium/components/paint_preview/browser/paint_preview_client_unittest.cc2
-rw-r--r--chromium/components/paint_preview/common/BUILD.gn2
-rw-r--r--chromium/components/paint_preview/common/mojom/paint_preview_types_mojom_traits.cc6
-rw-r--r--chromium/components/paint_preview/common/subset_font.cc7
-rw-r--r--chromium/components/paint_preview/player/android/BUILD.gn7
-rw-r--r--chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java6
-rw-r--r--chromium/components/paint_preview/player/player_compositor_delegate.cc6
-rw-r--r--chromium/components/paint_preview/player/player_compositor_delegate.h11
-rw-r--r--chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc3
-rw-r--r--chromium/components/password_manager/content/browser/BUILD.gn4
-rw-r--r--chromium/components/password_manager/content/browser/DEPS3
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver.cc2
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver.h2
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_factory.cc13
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_unittest.cc22
-rw-r--r--chromium/components/password_manager/content/browser/password_change_success_tracker_factory.cc9
-rw-r--r--chromium/components/password_manager/content/browser/password_change_success_tracker_factory_unittest.cc126
-rw-r--r--chromium/components/password_manager/core/browser/BUILD.gn71
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc11
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.cc15
-rw-r--r--chromium/components/password_manager/core/browser/android_backend_error.h4
-rw-r--r--chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc130
-rw-r--r--chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.h19
-rw-r--r--chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc94
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_impl.cc4
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_impl_unittest.cc7
-rw-r--r--chromium/components/password_manager/core/browser/export/password_csv_writer.cc20
-rw-r--r--chromium/components/password_manager/core/browser/export/password_csv_writer.h15
-rw-r--r--chromium/components/password_manager/core/browser/export/password_csv_writer_unittest.cc32
-rw-r--r--chromium/components/password_manager/core/browser/export/password_manager_exporter.cc42
-rw-r--r--chromium/components/password_manager/core/browser/export/password_manager_exporter.h9
-rw-r--r--chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc116
-rw-r--r--chromium/components/password_manager/core/browser/fake_password_store_backend.cc12
-rw-r--r--chromium/components/password_manager/core/browser/fake_password_store_backend.h12
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/form_parser.cc12
-rw-r--r--chromium/components/password_manager/core/browser/form_saver_impl.cc2
-rw-r--r--chromium/components/password_manager/core/browser/import/csv_password.cc113
-rw-r--r--chromium/components/password_manager/core/browser/import/csv_password.h31
-rw-r--r--chromium/components/password_manager/core/browser/import/csv_password_iterator.cc3
-rw-r--r--chromium/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc16
-rw-r--r--chromium/components/password_manager/core/browser/import/csv_password_sequence_unittest.cc4
-rw-r--r--chromium/components/password_manager/core/browser/import/csv_password_unittest.cc23
-rw-r--r--chromium/components/password_manager/core/browser/import/password_csv_reader_fuzzer.cc6
-rw-r--r--chromium/components/password_manager/core/browser/import/password_importer_unittest.cc2
-rw-r--r--chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl.cc6
-rw-r--r--chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl_unittest.cc16
-rw-r--r--chromium/components/password_manager/core/browser/leak_detection_delegate_helper.cc24
-rw-r--r--chromium/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc55
-rw-r--r--chromium/components/password_manager/core/browser/leak_detection_dialog_utils.cc64
-rw-r--r--chromium/components/password_manager/core/browser/leak_detection_dialog_utils.h218
-rw-r--r--chromium/components/password_manager/core/browser/leak_detection_dialog_utils_unittest.cc109
-rw-r--r--chromium/components/password_manager/core/browser/login_database.cc70
-rw-r--r--chromium/components/password_manager/core/browser/login_database.h18
-rw-r--r--chromium/components/password_manager/core/browser/login_database_async_helper.cc116
-rw-r--r--chromium/components/password_manager/core/browser/login_database_async_helper.h36
-rw-r--r--chromium/components/password_manager/core/browser/login_database_unittest.cc52
-rw-r--r--chromium/components/password_manager/core/browser/manage_passwords_referrer.h8
-rw-r--r--chromium/components/password_manager/core/browser/mock_password_change_success_tracker.h41
-rw-r--r--chromium/components/password_manager/core/browser/mock_password_manager_settings_service.cc10
-rw-r--r--chromium/components/password_manager/core/browser/mock_password_manager_settings_service.h27
-rw-r--r--chromium/components/password_manager/core/browser/mock_password_store_backend.h12
-rw-r--r--chromium/components/password_manager/core/browser/move_password_to_account_store_helper.cc37
-rw-r--r--chromium/components/password_manager/core/browser/move_password_to_account_store_helper.h7
-rw-r--r--chromium/components/password_manager/core/browser/password_access_authenticator.cc6
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager.cc27
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager.h3
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc111
-rw-r--r--chromium/components/password_manager/core/browser/password_bubble_experiment.cc5
-rw-r--r--chromium/components/password_manager/core/browser/password_bubble_experiment.h3
-rw-r--r--chromium/components/password_manager/core/browser/password_change_success_tracker.h141
-rw-r--r--chromium/components/password_manager/core/browser/password_change_success_tracker_impl.cc381
-rw-r--r--chromium/components/password_manager/core/browser/password_change_success_tracker_impl.h164
-rw-r--r--chromium/components/password_manager/core/browser/password_change_success_tracker_impl_unittest.cc696
-rw-r--r--chromium/components/password_manager/core/browser/password_form.cc34
-rw-r--r--chromium/components/password_manager/core/browser/password_form.h15
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager.cc47
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager.h5
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager_unittest.cc148
-rw-r--r--chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc6
-rw-r--r--chromium/components/password_manager/core/browser/password_form_metrics_recorder.h6
-rw-r--r--chromium/components/password_manager/core/browser/password_form_prediction_waiter.h3
-rw-r--r--chromium/components/password_manager/core/browser/password_manager.cc47
-rw-r--r--chromium/components/password_manager/core/browser/password_manager.h9
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_client.cc10
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_client.h8
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_client_helper.cc4
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_client_helper_unittest.cc5
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_constants.cc7
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_constants.h11
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_features_util.cc5
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_util.cc98
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_util.h113
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_util_unittest.cc131
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_setting.h25
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_settings_service.h29
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_unittest.cc19
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util.cc54
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util.h12
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util_unittest.cc146
-rw-r--r--chromium/components/password_manager/core/browser/password_notes_table.cc54
-rw-r--r--chromium/components/password_manager/core/browser/password_notes_table.h12
-rw-r--r--chromium/components/password_manager/core/browser/password_notes_table_unittest.cc47
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector.cc8
-rw-r--r--chromium/components/password_manager/core/browser/password_save_manager_impl.cc3
-rw-r--r--chromium/components/password_manager/core/browser/password_save_manager_impl_unittest.cc2
-rw-r--r--chromium/components/password_manager/core/browser/password_scripts_fetcher.h3
-rw-r--r--chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.cc32
-rw-r--r--chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.h16
-rw-r--r--chromium/components/password_manager/core/browser/password_scripts_fetcher_impl_unittest.cc (renamed from chromium/components/password_manager/core/browser/password_scripts_fetcher_impl_unittests.cc)107
-rw-r--r--chromium/components/password_manager/core/browser/password_store.cc86
-rw-r--r--chromium/components/password_manager/core/browser/password_store_backend.h34
-rw-r--r--chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.cc135
-rw-r--r--chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.h54
-rw-r--r--chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder_unittest.cc109
-rw-r--r--chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.cc24
-rw-r--r--chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.h12
-rw-r--r--chromium/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc50
-rw-r--r--chromium/components/password_manager/core/browser/password_store_built_in_backend.cc132
-rw-r--r--chromium/components/password_manager/core/browser/password_store_built_in_backend.h12
-rw-r--r--chromium/components/password_manager/core/browser/password_store_built_in_backend_unittest.cc141
-rw-r--r--chromium/components/password_manager/core/browser/password_store_proxy_backend.cc193
-rw-r--r--chromium/components/password_manager/core/browser/password_store_proxy_backend.h33
-rw-r--r--chromium/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc203
-rw-r--r--chromium/components/password_manager/core/browser/password_store_unittest.cc10
-rw-r--r--chromium/components/password_manager/core/browser/password_store_util.cc29
-rw-r--r--chromium/components/password_manager/core/browser/password_store_util.h17
-rw-r--r--chromium/components/password_manager/core/browser/password_sync_util.cc7
-rw-r--r--chromium/components/password_manager/core/browser/protos/list_passwords_result.proto24
-rw-r--r--chromium/components/password_manager/core/browser/protos/password_with_local_data.proto48
-rw-r--r--chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.cc17
-rw-r--r--chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h1
-rw-r--r--chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher_unittest.cc32
-rw-r--r--chromium/components/password_manager/core/browser/site_affiliation/asset_link_data_unittest.cc22
-rw-r--r--chromium/components/password_manager/core/browser/site_affiliation/hash_affiliation_fetcher_unittest.cc31
-rw-r--r--chromium/components/password_manager/core/browser/store_metrics_reporter.cc40
-rw-r--r--chromium/components/password_manager/core/browser/store_metrics_reporter_unittest.cc96
-rw-r--r--chromium/components/password_manager/core/browser/sync/password_model_type_controller.h1
-rw-r--r--chromium/components/password_manager/core/browser/sync/password_proto_utils.cc137
-rw-r--r--chromium/components/password_manager/core/browser/sync/password_proto_utils.h24
-rw-r--r--chromium/components/password_manager/core/browser/sync/password_proto_utils_unittest.cc123
-rw-r--r--chromium/components/password_manager/core/browser/sync/password_sync_bridge.cc132
-rw-r--r--chromium/components/password_manager/core/browser/sync/password_sync_bridge.h13
-rw-r--r--chromium/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc79
-rw-r--r--chromium/components/password_manager/core/browser/ui/credential_ui_entry.cc56
-rw-r--r--chromium/components/password_manager/core/browser/ui/credential_ui_entry.h97
-rw-r--r--chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.cc101
-rw-r--r--chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.h16
-rw-r--r--chromium/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc379
-rw-r--r--chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.cc171
-rw-r--r--chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.h16
-rw-r--r--chromium/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc223
-rw-r--r--chromium/components/password_manager/core/common/password_manager_features.cc33
-rw-r--r--chromium/components/password_manager/core/common/password_manager_features.h5
-rw-r--r--chromium/components/password_manager/core/common/password_manager_pref_names.cc12
-rw-r--r--chromium/components/password_manager/core/common/password_manager_pref_names.h24
-rw-r--r--chromium/components/password_manager/ios/BUILD.gn6
-rw-r--r--chromium/components/password_manager/ios/password_manager_java_script_feature.mm25
-rw-r--r--chromium/components/password_manager/ios/resources/password_controller.js28
-rw-r--r--chromium/components/password_manager_strings.grdp5
-rw-r--r--chromium/components/password_manager_strings_grdp/IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED.png.sha12
-rw-r--r--chromium/components/password_manager_strings_grdp/IDS_CREDENTIAL_LEAK_DONT_CHANGE_AUTOMATICALLY.png.sha11
-rw-r--r--chromium/components/payments/content/BUILD.gn3
-rw-r--r--chromium/components/payments/content/android/BUILD.gn10
-rw-r--r--chromium/components/payments/content/android/jni_payment_app.cc5
-rw-r--r--chromium/components/payments/content/android/jni_payment_app.h2
-rw-r--r--chromium/components/payments/content/android/journey_logger_android.cc4
-rw-r--r--chromium/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java40
-rw-r--r--chromium/components/payments/content/android_payment_app_factory_unittest.cc12
-rw-r--r--chromium/components/payments/content/android_payment_app_unittest.cc2
-rw-r--r--chromium/components/payments/content/content_payment_request_delegate.h4
-rw-r--r--chromium/components/payments/content/developer_console_logger.cc8
-rw-r--r--chromium/components/payments/content/installable_payment_app_crawler.cc4
-rw-r--r--chromium/components/payments/content/payment_app.cc4
-rw-r--r--chromium/components/payments/content/payment_method_manifest_table.cc17
-rw-r--r--chromium/components/payments/content/payment_method_manifest_table_unittest.cc90
-rw-r--r--chromium/components/payments/content/payment_request.cc99
-rw-r--r--chromium/components/payments/content/payment_request.h12
-rw-r--r--chromium/components/payments/content/payment_request_dialog.h4
-rw-r--r--chromium/components/payments/content/payment_request_state.h7
-rw-r--r--chromium/components/payments/content/payment_request_state_unittest.cc2
-rw-r--r--chromium/components/payments/content/payment_request_web_contents_manager_unittest.cc2
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_app.cc3
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_app_factory.cc6
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_app_unittest.cc2
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_controller.cc37
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_controller.h3
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_model.h28
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_model_unittest.cc15
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_no_creds.cc26
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_no_creds.h9
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_no_creds_model.cc20
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_no_creds_model.h71
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_no_creds_model_unittest.cc53
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_no_creds_view.h14
-rw-r--r--chromium/components/payments/content/secure_payment_confirmation_view.h8
-rw-r--r--chromium/components/payments/content/test_content_payment_request_delegate.cc4
-rw-r--r--chromium/components/payments/content/test_content_payment_request_delegate.h4
-rw-r--r--chromium/components/payments/core/error_strings.cc1
-rw-r--r--chromium/components/payments/core/error_strings.h3
-rw-r--r--chromium/components/payments_strings.grdp12
-rw-r--r--chromium/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LABEL.png.sha11
-rw-r--r--chromium/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LINK_LABEL.png.sha11
-rw-r--r--chromium/components/pdf/README.md10
-rw-r--r--chromium/components/pdf/browser/mock_url_loader_client.h4
-rw-r--r--chromium/components/pdf/browser/pdf_web_contents_helper_browsertest.cc2
-rw-r--r--chromium/components/pdf/browser/plugin_response_writer.cc4
-rw-r--r--chromium/components/pdf/browser/plugin_response_writer_unittest.cc12
-rw-r--r--chromium/components/pdf/renderer/BUILD.gn3
-rw-r--r--chromium/components/pdf/renderer/DEPS3
-rw-r--r--chromium/components/pdf/renderer/internal_plugin_renderer_helpers.cc6
-rw-r--r--chromium/components/pdf/renderer/pdf_accessibility_tree.cc39
-rw-r--r--chromium/components/pdf/renderer/pdf_accessibility_tree.h22
-rw-r--r--chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc52
-rw-r--r--chromium/components/pdf/renderer/pdf_view_web_plugin_client.cc217
-rw-r--r--chromium/components/pdf/renderer/pdf_view_web_plugin_client.h52
-rw-r--r--chromium/components/performance_manager/BUILD.gn9
-rw-r--r--chromium/components/performance_manager/DEPS2
-rw-r--r--chromium/components/performance_manager/OWNERS3
-rw-r--r--chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.cc55
-rw-r--r--chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.h14
-rw-r--r--chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator_unittest.cc194
-rw-r--r--chromium/components/performance_manager/decorators/tab_properties_decorator.cc95
-rw-r--r--chromium/components/performance_manager/decorators/tab_properties_decorator_unittest.cc40
-rw-r--r--chromium/components/performance_manager/embedder/performance_manager_registry.h5
-rw-r--r--chromium/components/performance_manager/features.cc9
-rw-r--r--chromium/components/performance_manager/graph/graph_impl_unittest.cc9
-rw-r--r--chromium/components/performance_manager/graph/page_node.cc13
-rw-r--r--chromium/components/performance_manager/graph/page_node_impl.cc15
-rw-r--r--chromium/components/performance_manager/graph/page_node_impl.h8
-rw-r--r--chromium/components/performance_manager/graph/page_node_impl_describer.cc2
-rw-r--r--chromium/components/performance_manager/graph/page_node_impl_unittest.cc1
-rw-r--r--chromium/components/performance_manager/graph/policies/process_priority_policy_unittest.cc2
-rw-r--r--chromium/components/performance_manager/graph/process_node_impl.cc19
-rw-r--r--chromium/components/performance_manager/graph/process_node_impl.h17
-rw-r--r--chromium/components/performance_manager/graph/process_node_impl_describer.cc23
-rw-r--r--chromium/components/performance_manager/graph/process_node_impl_unittest.cc17
-rw-r--r--chromium/components/performance_manager/graph_features.cc3
-rw-r--r--chromium/components/performance_manager/graph_features_unittest.cc4
-rw-r--r--chromium/components/performance_manager/metrics/metrics_collector.cc82
-rw-r--r--chromium/components/performance_manager/metrics/metrics_collector_unittest.cc199
-rw-r--r--chromium/components/performance_manager/metrics/metrics_provider.cc72
-rw-r--r--chromium/components/performance_manager/metrics/metrics_provider_unittest.cc137
-rw-r--r--chromium/components/performance_manager/performance_manager.cc4
-rw-r--r--chromium/components/performance_manager/performance_manager_browsertest.cc6
-rw-r--r--chromium/components/performance_manager/performance_manager_lifetime.cc1
-rw-r--r--chromium/components/performance_manager/performance_manager_registry_impl.cc31
-rw-r--r--chromium/components/performance_manager/performance_manager_registry_impl.h8
-rw-r--r--chromium/components/performance_manager/performance_manager_tab_helper.cc15
-rw-r--r--chromium/components/performance_manager/performance_manager_tab_helper_unittest.cc10
-rw-r--r--chromium/components/performance_manager/performance_manager_unittest.cc2
-rw-r--r--chromium/components/performance_manager/prerendering_browsertest.cc2
-rw-r--r--chromium/components/performance_manager/public/decorators/tab_properties_decorator.h65
-rw-r--r--chromium/components/performance_manager/public/features.h11
-rw-r--r--chromium/components/performance_manager/public/graph/page_node.h19
-rw-r--r--chromium/components/performance_manager/public/graph/process_node.h25
-rw-r--r--chromium/components/performance_manager/public/metrics/metrics_collector.h5
-rw-r--r--chromium/components/performance_manager/public/metrics/metrics_provider.h55
-rw-r--r--chromium/components/performance_manager/public/user_tuning/prefs.h6
-rw-r--r--chromium/components/performance_manager/render_process_host_proxy_browsertest.cc2
-rw-r--r--chromium/components/performance_manager/render_process_user_data.cc27
-rw-r--r--chromium/components/performance_manager/render_process_user_data.h5
-rw-r--r--chromium/components/performance_manager/service_worker_context_adapter.cc15
-rw-r--r--chromium/components/performance_manager/service_worker_context_adapter.h3
-rw-r--r--chromium/components/performance_manager/test_support/mock_graphs.cc9
-rw-r--r--chromium/components/performance_manager/test_support/mock_graphs.h2
-rw-r--r--chromium/components/performance_manager/user_tuning/prefs.cc10
-rw-r--r--chromium/components/performance_manager/v8_memory/v8_context_tracker_browsertest.cc8
-rw-r--r--chromium/components/performance_manager/worker_watcher.cc9
-rw-r--r--chromium/components/permissions/BUILD.gn22
-rw-r--r--chromium/components/permissions/DEPS2
-rw-r--r--chromium/components/permissions/README.md51
-rw-r--r--chromium/components/permissions/android/BUILD.gn5
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_dialog.cc25
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_dialog.h30
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_dialog_delegate.cc (renamed from chromium/components/permissions/android/permission_dialog_delegate.cc)4
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_dialog_delegate.h (renamed from chromium/components/permissions/android/permission_dialog_delegate.h)8
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_dialog_delegate_unittest.cc (renamed from chromium/components/permissions/android/permission_dialog_delegate_unittest.cc)2
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_infobar.cc71
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_infobar.h49
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_message.cc36
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_message.h43
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_prompt_android.cc124
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_prompt_android.h (renamed from chromium/components/permissions/android/permission_prompt_android.h)41
-rw-r--r--chromium/components/permissions/android/permission_prompt/permission_prompt_android_factory.cc32
-rw-r--r--chromium/components/permissions/android/permission_prompt_android.cc220
-rw-r--r--chromium/components/permissions/bluetooth_delegate_impl.cc8
-rw-r--r--chromium/components/permissions/bluetooth_delegate_impl.h13
-rw-r--r--chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.cc45
-rw-r--r--chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.h1
-rw-r--r--chromium/components/permissions/contexts/geolocation_permission_context.cc7
-rw-r--r--chromium/components/permissions/contexts/geolocation_permission_context.h6
-rw-r--r--chromium/components/permissions/contexts/geolocation_permission_context_android.cc7
-rw-r--r--chromium/components/permissions/contexts/geolocation_permission_context_android.h3
-rw-r--r--chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc173
-rw-r--r--chromium/components/permissions/contexts/local_fonts_permission_context.cc8
-rw-r--r--chromium/components/permissions/contexts/midi_sysex_permission_context_unittest.cc6
-rw-r--r--chromium/components/permissions/contexts/nfc_permission_context.cc3
-rw-r--r--chromium/components/permissions/contexts/nfc_permission_context.h3
-rw-r--r--chromium/components/permissions/contexts/nfc_permission_context_unittest.cc31
-rw-r--r--chromium/components/permissions/contexts/payment_handler_permission_context.cc1
-rw-r--r--chromium/components/permissions/contexts/payment_handler_permission_context.h1
-rw-r--r--chromium/components/permissions/contexts/webxr_permission_context.cc27
-rw-r--r--chromium/components/permissions/contexts/webxr_permission_context.h4
-rw-r--r--chromium/components/permissions/contexts/window_placement_permission_context.cc11
-rw-r--r--chromium/components/permissions/contexts/window_placement_permission_context.h7
-rw-r--r--chromium/components/permissions/features.cc16
-rw-r--r--chromium/components/permissions/features.h6
-rw-r--r--chromium/components/permissions/notifications_engagement_service.cc154
-rw-r--r--chromium/components/permissions/notifications_engagement_service.h64
-rw-r--r--chromium/components/permissions/permission_context_base.cc39
-rw-r--r--chromium/components/permissions/permission_context_base.h6
-rw-r--r--chromium/components/permissions/permission_context_base_unittest.cc69
-rw-r--r--chromium/components/permissions/permission_decision_auto_blocker.cc98
-rw-r--r--chromium/components/permissions/permission_decision_auto_blocker.h22
-rw-r--r--chromium/components/permissions/permission_decision_auto_blocker_unittest.cc223
-rw-r--r--chromium/components/permissions/permission_manager.cc548
-rw-r--r--chromium/components/permissions/permission_manager.h176
-rw-r--r--chromium/components/permissions/permission_manager_unittest.cc435
-rw-r--r--chromium/components/permissions/permission_prompt.h2
-rw-r--r--chromium/components/permissions/permission_request_manager.cc21
-rw-r--r--chromium/components/permissions/permission_request_manager.h1
-rw-r--r--chromium/components/permissions/permission_request_manager_unittest.cc146
-rw-r--r--chromium/components/permissions/permission_uma_util.cc9
-rw-r--r--chromium/components/permissions/permission_uma_util.h4
-rw-r--r--chromium/components/permissions/permission_util.cc108
-rw-r--r--chromium/components/permissions/permission_util.h23
-rw-r--r--chromium/components/permissions/permissions_client.cc2
-rw-r--r--chromium/components/permissions/permissions_client.h2
-rw-r--r--chromium/components/permissions/prediction_service/prediction_model_handler.cc1
-rw-r--r--chromium/components/permissions_strings.grdp6
-rw-r--r--chromium/components/permissions_strings_grdp/IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT.png.sha11
-rw-r--r--chromium/components/plugins/renderer/loadable_plugin_placeholder.cc1
-rw-r--r--chromium/components/plugins/renderer/plugin_placeholder.cc1
-rw-r--r--chromium/components/policy/BUILD.gn3
-rw-r--r--chromium/components/policy/ENTERPRISE_POLICY_OWNERS1
-rw-r--r--chromium/components/policy/android/BUILD.gn5
-rw-r--r--chromium/components/policy/core/browser/browser_policy_connector.cc35
-rw-r--r--chromium/components/policy/core/browser/browser_policy_connector.h5
-rw-r--r--chromium/components/policy/core/browser/cloud/message_util.cc2
-rw-r--r--chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.cc1
-rw-r--r--chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.h5
-rw-r--r--chromium/components/policy/core/browser/configuration_policy_handler.cc6
-rw-r--r--chromium/components/policy/core/browser/policy_conversions.cc239
-rw-r--r--chromium/components/policy/core/browser/policy_conversions.h93
-rw-r--r--chromium/components/policy/core/browser/policy_conversions_client.cc101
-rw-r--r--chromium/components/policy/core/browser/policy_conversions_client.h21
-rw-r--r--chromium/components/policy/core/browser/policy_conversions_client_unittest.cc34
-rw-r--r--chromium/components/policy/core/browser/policy_error_map.cc13
-rw-r--r--chromium/components/policy/core/browser/policy_error_map.h12
-rw-r--r--chromium/components/policy/core/browser/url_blocklist_manager.cc2
-rw-r--r--chromium/components/policy/core/browser/url_blocklist_manager.h5
-rw-r--r--chromium/components/policy/core/browser/webui/json_generation.cc48
-rw-r--r--chromium/components/policy/core/browser/webui/json_generation.h13
-rw-r--r--chromium/components/policy/core/browser/webui/policy_status_provider.cc29
-rw-r--r--chromium/components/policy/core/common/BUILD.gn6
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_client.cc92
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_client.h39
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc8
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.h4
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_client_unittest.cc168
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_constants.cc6
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_constants.h2
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_core.cc11
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_core.h3
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_core_unittest.cc115
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.cc53
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h35
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_observer.h32
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_unittest.cc89
-rw-r--r--chromium/components/policy/core/common/cloud/cloud_policy_util.cc26
-rw-r--r--chromium/components/policy/core/common/cloud/component_cloud_policy_service.cc21
-rw-r--r--chromium/components/policy/core/common/cloud/component_cloud_policy_service.h11
-rw-r--r--chromium/components/policy/core/common/cloud/component_cloud_policy_service_observer.h9
-rw-r--r--chromium/components/policy/core/common/cloud/component_cloud_policy_store.cc66
-rw-r--r--chromium/components/policy/core/common/cloud/component_cloud_policy_store.h11
-rw-r--r--chromium/components/policy/core/common/cloud/device_management_service.cc8
-rw-r--r--chromium/components/policy/core/common/cloud/device_management_service.h6
-rw-r--r--chromium/components/policy/core/common/cloud/device_management_service_unittest.cc96
-rw-r--r--chromium/components/policy/core/common/cloud/dm_token.cc2
-rw-r--r--chromium/components/policy/core/common/cloud/dmserver_job_configurations.cc155
-rw-r--r--chromium/components/policy/core/common/cloud/dmserver_job_configurations.h7
-rw-r--r--chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc241
-rw-r--r--chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.h35
-rw-r--r--chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration_unittest.cc308
-rw-r--r--chromium/components/policy/core/common/cloud/mock_device_management_service.cc21
-rw-r--r--chromium/components/policy/core/common/cloud/mock_device_management_service.h2
-rw-r--r--chromium/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc30
-rw-r--r--chromium/components/policy/core/common/cloud/reporting_job_configuration_base.cc19
-rw-r--r--chromium/components/policy/core/common/cloud/reporting_job_configuration_base.h4
-rw-r--r--chromium/components/policy/core/common/cloud/user_cloud_policy_store.cc8
-rw-r--r--chromium/components/policy/core/common/cloud/user_info_fetcher.cc9
-rw-r--r--chromium/components/policy/core/common/cloud/user_info_fetcher.h8
-rw-r--r--chromium/components/policy/core/common/cloud/user_info_fetcher_unittest.cc14
-rw-r--r--chromium/components/policy/core/common/configuration_policy_provider_test.cc10
-rw-r--r--chromium/components/policy/core/common/default_chrome_apps_migrator.cc34
-rw-r--r--chromium/components/policy/core/common/default_chrome_apps_migrator.h15
-rw-r--r--chromium/components/policy/core/common/default_chrome_apps_migrator_unittest.cc157
-rw-r--r--chromium/components/policy/core/common/features.cc15
-rw-r--r--chromium/components/policy/core/common/features.h14
-rw-r--r--chromium/components/policy/core/common/legacy_chrome_policy_migrator.cc34
-rw-r--r--chromium/components/policy/core/common/legacy_chrome_policy_migrator.h40
-rw-r--r--chromium/components/policy/core/common/legacy_chrome_policy_migrator_unittest.cc124
-rw-r--r--chromium/components/policy/core/common/mac_util.cc6
-rw-r--r--chromium/components/policy/core/common/mac_util_unittest.cc6
-rw-r--r--chromium/components/policy/core/common/management/platform_management_status_provider_mac.cc25
-rw-r--r--chromium/components/policy/core/common/policy_loader_common.cc9
-rw-r--r--chromium/components/policy/core/common/policy_loader_ios.mm4
-rw-r--r--chromium/components/policy/core/common/policy_loader_ios_unittest.mm12
-rw-r--r--chromium/components/policy/core/common/policy_loader_lacros.cc61
-rw-r--r--chromium/components/policy/core/common/policy_loader_lacros.h27
-rw-r--r--chromium/components/policy/core/common/policy_loader_lacros_unittest.cc16
-rw-r--r--chromium/components/policy/core/common/policy_loader_mac.mm51
-rw-r--r--chromium/components/policy/core/common/policy_loader_mac_unittest.cc46
-rw-r--r--chromium/components/policy/core/common/policy_loader_win.cc4
-rw-r--r--chromium/components/policy/core/common/policy_loader_win_unittest.cc4
-rw-r--r--chromium/components/policy/core/common/policy_pref_names.cc13
-rw-r--r--chromium/components/policy/core/common/policy_pref_names.h5
-rw-r--r--chromium/components/policy/core/common/policy_proto_decoders.cc43
-rw-r--r--chromium/components/policy/core/common/policy_proto_decoders.h11
-rw-r--r--chromium/components/policy/core/common/policy_statistics_collector.cc118
-rw-r--r--chromium/components/policy/core/common/policy_statistics_collector.h18
-rw-r--r--chromium/components/policy/core/common/policy_statistics_collector_unittest.cc135
-rw-r--r--chromium/components/policy/core/common/preferences_mac.cc19
-rw-r--r--chromium/components/policy/core/common/preferences_mac.h43
-rw-r--r--chromium/components/policy/core/common/preferences_mac.mm119
-rw-r--r--chromium/components/policy/core/common/preferences_mock_mac.cc18
-rw-r--r--chromium/components/policy/core/common/preferences_mock_mac.h17
-rw-r--r--chromium/components/policy/core/common/registry_dict.cc9
-rw-r--r--chromium/components/policy/core/common/registry_dict_unittest.cc12
-rw-r--r--chromium/components/policy/core/common/schema.cc23
-rw-r--r--chromium/components/policy/core/common/schema.h5
-rw-r--r--chromium/components/policy/core/common/schema_fuzzer.cc3
-rw-r--r--chromium/components/policy/core/common/schema_unittest.cc42
-rw-r--r--chromium/components/policy/core/common/values_util.cc8
-rw-r--r--chromium/components/policy/core/common/values_util.h13
-rw-r--r--chromium/components/policy/proto/BUILD.gn5
-rw-r--r--chromium/components/policy/proto/chrome_device_policy.proto3
-rw-r--r--chromium/components/policy/proto/device_management_backend.proto47
-rw-r--r--chromium/components/policy/resources/webui/BUILD.gn15
-rw-r--r--chromium/components/policy/test_support/policy_storage.h14
-rw-r--r--chromium/components/policy/test_support/request_handler_for_policy.cc10
-rwxr-xr-xchromium/components/policy/tools/generate_policy_source.py43
-rwxr-xr-xchromium/components/policy/tools/generate_policy_source_test.py25
-rw-r--r--chromium/components/policy/tools/generate_policy_source_test_data.py19
-rwxr-xr-xchromium/components/policy/tools/syntax_check_policy_template_json.py21
-rwxr-xr-xchromium/components/policy/tools/template_writers/PRESUBMIT.py6
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/ios_app_config_writer.py4
-rwxr-xr-xchromium/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py7
-rw-r--r--chromium/components/policy_strings.grdp3
-rw-r--r--chromium/components/policy_strings_grdp/IDS_POLICY_DM_STATUS_SERVICE_DEVICE_NEEDS_RESET.png.sha11
-rw-r--r--chromium/components/power_metrics/resource_coalition_mac_unittest.mm2
-rw-r--r--chromium/components/prefs/android/BUILD.gn4
-rw-r--r--chromium/components/prefs/pref_member_unittest.cc17
-rw-r--r--chromium/components/prefs/pref_registry.h20
-rw-r--r--chromium/components/prefs/pref_registry_simple.cc12
-rw-r--r--chromium/components/prefs/pref_registry_simple.h10
-rw-r--r--chromium/components/prefs/pref_service.cc20
-rw-r--r--chromium/components/prefs/pref_service.h6
-rw-r--r--chromium/components/prefs/scoped_user_pref_update_unittest.cc19
-rw-r--r--chromium/components/printing/browser/print_manager.cc3
-rw-r--r--chromium/components/printing/browser/print_manager.h3
-rw-r--r--chromium/components/printing/browser/print_to_pdf/BUILD.gn2
-rw-r--r--chromium/components/printing/browser/print_to_pdf/pdf_print_manager.cc84
-rw-r--r--chromium/components/printing/browser/print_to_pdf/pdf_print_manager.h8
-rw-r--r--chromium/components/printing/browser/print_to_pdf/pdf_print_utils.cc40
-rw-r--r--chromium/components/printing/browser/print_to_pdf/pdf_print_utils.h9
-rw-r--r--chromium/components/printing/browser/print_to_pdf/pdf_print_utils_unittest.cc96
-rw-r--r--chromium/components/printing/common/print.mojom22
-rw-r--r--chromium/components/printing/renderer/print_render_frame_helper.cc200
-rw-r--r--chromium/components/printing/renderer/print_render_frame_helper.h16
-rw-r--r--chromium/components/privacy_sandbox/privacy_sandbox_features.cc10
-rw-r--r--chromium/components/privacy_sandbox/privacy_sandbox_features.h16
-rw-r--r--chromium/components/privacy_sandbox/privacy_sandbox_settings.cc12
-rw-r--r--chromium/components/privacy_sandbox/privacy_sandbox_settings.h6
-rw-r--r--chromium/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc40
-rw-r--r--chromium/components/proxy_config/proxy_policy_handler.cc6
-rw-r--r--chromium/components/query_tiles/BUILD.gn2
-rw-r--r--chromium/components/query_tiles/internal/tile_iterator.cc1
-rw-r--r--chromium/components/query_tiles/internal/tile_service_scheduler_unittest.cc1
-rw-r--r--chromium/components/query_tiles/internal/tile_utils.cc4
-rw-r--r--chromium/components/quirks/quirks_client.cc6
-rw-r--r--chromium/components/remote_cocoa/app_shim/alert.mm6
-rw-r--r--chromium/components/remote_cocoa/app_shim/application_bridge.h2
-rw-r--r--chromium/components/remote_cocoa/app_shim/application_bridge.mm3
-rw-r--r--chromium/components/remote_cocoa/app_shim/bridged_content_view.mm8
-rw-r--r--chromium/components/remote_cocoa/app_shim/bridged_content_view_touch_bar.mm3
-rw-r--r--chromium/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm8
-rw-r--r--chromium/components/remote_cocoa/app_shim/certificate_viewer.mm15
-rw-r--r--chromium/components/remote_cocoa/app_shim/mouse_capture.mm18
-rw-r--r--chromium/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h6
-rw-r--r--chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h5
-rw-r--r--chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm151
-rw-r--r--chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h73
-rw-r--r--chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm315
-rw-r--r--chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.h8
-rw-r--r--chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.mm38
-rw-r--r--chromium/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm20
-rw-r--r--chromium/components/remote_cocoa/app_shim/views_nswindow_delegate.mm33
-rw-r--r--chromium/components/remote_cocoa/app_shim/window_move_loop.mm16
-rw-r--r--chromium/components/remote_cocoa/app_shim/window_touch_bar_delegate.h2
-rw-r--r--chromium/components/remote_cocoa/common/BUILD.gn1
-rw-r--r--chromium/components/remote_cocoa/common/application.mojom5
-rw-r--r--chromium/components/remote_cocoa/common/native_widget_ns_window.mojom20
-rw-r--r--chromium/components/renderer_context_menu/context_menu_content_type.cc4
-rw-r--r--chromium/components/renderer_context_menu/context_menu_content_type.h3
-rw-r--r--chromium/components/renderer_context_menu/render_view_context_menu_base.cc2
-rw-r--r--chromium/components/reporting/client/mock_dm_token_retriever.cc4
-rw-r--r--chromium/components/reporting/client/mock_dm_token_retriever.h2
-rw-r--r--chromium/components/reporting/client/mock_report_queue.cc28
-rw-r--r--chromium/components/reporting/client/mock_report_queue.h31
-rw-r--r--chromium/components/reporting/client/mock_report_queue_provider.cc2
-rw-r--r--chromium/components/reporting/client/report_queue.cc32
-rw-r--r--chromium/components/reporting/client/report_queue.h37
-rw-r--r--chromium/components/reporting/client/report_queue_factory.cc74
-rw-r--r--chromium/components/reporting/client/report_queue_factory.h19
-rw-r--r--chromium/components/reporting/client/report_queue_factory_unittest.cc31
-rw-r--r--chromium/components/reporting/client/report_queue_impl.cc177
-rw-r--r--chromium/components/reporting/client/report_queue_impl.h88
-rw-r--r--chromium/components/reporting/client/report_queue_impl_unittest.cc181
-rw-r--r--chromium/components/reporting/client/report_queue_provider_unittest.cc51
-rw-r--r--chromium/components/reporting/compression/BUILD.gn2
-rw-r--r--chromium/components/reporting/compression/compression_module.cc7
-rw-r--r--chromium/components/reporting/compression/compression_module.h12
-rw-r--r--chromium/components/reporting/compression/compression_module_unittest.cc20
-rw-r--r--chromium/components/reporting/compression/decompression.cc3
-rw-r--r--chromium/components/reporting/compression/decompression.h4
-rw-r--r--chromium/components/reporting/compression/test_compression_module.cc8
-rw-r--r--chromium/components/reporting/compression/test_compression_module.h7
-rw-r--r--chromium/components/reporting/encryption/BUILD.gn4
-rw-r--r--chromium/components/reporting/encryption/decryption.h1
-rw-r--r--chromium/components/reporting/encryption/encryption.cc1
-rw-r--r--chromium/components/reporting/encryption/encryption_module_interface.cc4
-rw-r--r--chromium/components/reporting/encryption/encryption_module_unittest.cc4
-rw-r--r--chromium/components/reporting/encryption/primitives.cc1
-rw-r--r--chromium/components/reporting/encryption/test_encryption_module.cc3
-rw-r--r--chromium/components/reporting/encryption/testing_primitives.cc1
-rw-r--r--chromium/components/reporting/metrics/fake_metric_report_queue.cc13
-rw-r--r--chromium/components/reporting/metrics/fake_metric_report_queue.h8
-rw-r--r--chromium/components/reporting/metrics/fake_sampler.cc8
-rw-r--r--chromium/components/reporting/metrics/fake_sampler.h11
-rw-r--r--chromium/components/reporting/metrics/metric_data_collector.cc70
-rw-r--r--chromium/components/reporting/metrics/metric_data_collector.h24
-rw-r--r--chromium/components/reporting/metrics/metric_data_collector_unittest.cc198
-rw-r--r--chromium/components/reporting/metrics/metric_event_observer_manager.cc11
-rw-r--r--chromium/components/reporting/metrics/metric_event_observer_manager.h3
-rw-r--r--chromium/components/reporting/metrics/metric_event_observer_manager_unittest.cc65
-rw-r--r--chromium/components/reporting/metrics/metric_report_queue.cc5
-rw-r--r--chromium/components/reporting/metrics/metric_report_queue.h2
-rw-r--r--chromium/components/reporting/metrics/metric_report_queue_unittest.cc47
-rw-r--r--chromium/components/reporting/metrics/sampler.h5
-rw-r--r--chromium/components/reporting/proto/BUILD.gn21
-rw-r--r--chromium/components/reporting/proto/interface.proto164
-rw-r--r--chromium/components/reporting/proto/synced/health.proto75
-rw-r--r--chromium/components/reporting/proto/synced/interface.proto8
-rw-r--r--chromium/components/reporting/proto/synced/metric_data.proto17
-rw-r--r--chromium/components/reporting/proto/synced/record.proto3
-rw-r--r--chromium/components/reporting/resources/BUILD.gn5
-rw-r--r--chromium/components/reporting/resources/disk_resource_impl.cc15
-rw-r--r--chromium/components/reporting/resources/disk_resource_impl.h11
-rw-r--r--chromium/components/reporting/resources/memory_resource_impl.cc16
-rw-r--r--chromium/components/reporting/resources/memory_resource_impl.h11
-rw-r--r--chromium/components/reporting/resources/resource_interface.cc11
-rw-r--r--chromium/components/reporting/resources/resource_interface.h24
-rw-r--r--chromium/components/reporting/resources/resource_interface_unittest.cc31
-rw-r--r--chromium/components/reporting/storage/BUILD.gn7
-rw-r--r--chromium/components/reporting/storage/missive_storage_module.cc10
-rw-r--r--chromium/components/reporting/storage/missive_storage_module.h11
-rw-r--r--chromium/components/reporting/storage/missive_storage_module_delegate_impl.cc7
-rw-r--r--chromium/components/reporting/storage/missive_storage_module_delegate_impl.h9
-rw-r--r--chromium/components/reporting/storage/storage.cc37
-rw-r--r--chromium/components/reporting/storage/storage.h4
-rw-r--r--chromium/components/reporting/storage/storage_configuration.cc9
-rw-r--r--chromium/components/reporting/storage/storage_configuration.h43
-rw-r--r--chromium/components/reporting/storage/storage_module.cc6
-rw-r--r--chromium/components/reporting/storage/storage_module.h5
-rw-r--r--chromium/components/reporting/storage/storage_module_interface.h8
-rw-r--r--chromium/components/reporting/storage/storage_queue.cc240
-rw-r--r--chromium/components/reporting/storage/storage_queue.h19
-rw-r--r--chromium/components/reporting/storage/storage_queue_stress_test.cc9
-rw-r--r--chromium/components/reporting/storage/storage_queue_unittest.cc63
-rw-r--r--chromium/components/reporting/storage/storage_unittest.cc112
-rw-r--r--chromium/components/reporting/storage/storage_uploader_interface.h2
-rw-r--r--chromium/components/reporting/storage/test_storage_module.cc14
-rw-r--r--chromium/components/reporting/storage/test_storage_module.h10
-rw-r--r--chromium/components/reporting/storage_selector/storage_selector.cc24
-rw-r--r--chromium/components/reporting/storage_selector/storage_selector.h9
-rw-r--r--chromium/components/reporting/util/BUILD.gn10
-rw-r--r--chromium/components/reporting/util/disconnectable_client.cc21
-rw-r--r--chromium/components/reporting/util/disconnectable_client.h4
-rw-r--r--chromium/components/reporting/util/file.cc100
-rw-r--r--chromium/components/reporting/util/file.h22
-rw-r--r--chromium/components/reporting/util/file_unittest.cc115
-rw-r--r--chromium/components/reporting/util/shared_queue_unittest.cc2
-rw-r--r--chromium/components/reporting/util/shared_vector.h199
-rw-r--r--chromium/components/reporting/util/shared_vector_unittest.cc334
-rw-r--r--chromium/components/reporting/util/status.h2
-rw-r--r--chromium/components/reporting/util/status_macros.h2
-rw-r--r--chromium/components/reporting/util/status_macros_unittest.cc2
-rw-r--r--chromium/components/reporting/util/status_unittest.cc1
-rw-r--r--chromium/components/reporting/util/statusor.h7
-rw-r--r--chromium/components/reporting/util/task_runner_context.h1
-rw-r--r--chromium/components/reporting/util/task_runner_context_unittest.cc1
-rw-r--r--chromium/components/reporting/util/test_support_callbacks.h2
-rw-r--r--chromium/components/resources/BUILD.gn5
-rw-r--r--chromium/components/resources/OWNERS1
-rw-r--r--chromium/components/resources/commerce_resources.grdp (renamed from chromium/components/resources/commerce_heuristics.grdp)6
-rw-r--r--chromium/components/resources/components_resources.grd4
-rw-r--r--chromium/components/resources/default_100_percent/autofill/virtual_card_enroll.pngbin2947 -> 2717 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/virtual_card_enroll_dark.pngbin3011 -> 2765 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/virtual_card_enroll.pngbin5715 -> 5257 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/virtual_card_enroll_dark.pngbin5764 -> 5315 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/virtual_card_enroll.pngbin8316 -> 7597 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/virtual_card_enroll_dark.pngbin8541 -> 7662 bytes
-rw-r--r--chromium/components/resources/dev_ui_components_resources.grd1
-rw-r--r--chromium/components/resources/local_state.grdp5
-rw-r--r--chromium/components/resources/policy_resources.grdp5
-rw-r--r--chromium/components/safe_browsing/BUILD.gn13
-rw-r--r--chromium/components/safe_browsing/DEPS1
-rw-r--r--chromium/components/safe_browsing/android/BUILD.gn16
-rw-r--r--chromium/components/safe_browsing/android/remote_database_manager.cc36
-rw-r--r--chromium/components/safe_browsing/android/remote_database_manager.h1
-rw-r--r--chromium/components/safe_browsing/android/remote_database_manager_unittest.cc27
-rw-r--r--chromium/components/safe_browsing/android/safe_browsing_api_handler.cc22
-rw-r--r--chromium/components/safe_browsing/android/safe_browsing_api_handler.h48
-rw-r--r--chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc90
-rw-r--r--chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.h61
-rw-r--r--chromium/components/safe_browsing/android/safe_browsing_api_handler_util.h4
-rw-r--r--chromium/components/safe_browsing/buildflags.gni2
-rw-r--r--chromium/components/safe_browsing/content/browser/BUILD.gn2
-rw-r--r--chromium/components/safe_browsing/content/browser/DEPS1
-rw-r--r--chromium/components/safe_browsing/content/browser/base_ui_manager.cc6
-rw-r--r--chromium/components/safe_browsing/content/browser/base_ui_manager.h8
-rw-r--r--chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc1
-rw-r--r--chromium/components/safe_browsing/content/browser/client_side_detection_host.cc117
-rw-r--r--chromium/components/safe_browsing/content/browser/client_side_detection_host.h31
-rw-r--r--chromium/components/safe_browsing/content/browser/client_side_detection_service.cc50
-rw-r--r--chromium/components/safe_browsing/content/browser/client_side_detection_service.h16
-rw-r--r--chromium/components/safe_browsing/content/browser/client_side_phishing_model.cc75
-rw-r--r--chromium/components/safe_browsing/content/browser/client_side_phishing_model.h7
-rw-r--r--chromium/components/safe_browsing/content/browser/client_side_phishing_model_unittest.cc18
-rw-r--r--chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc3
-rw-r--r--chromium/components/safe_browsing/content/browser/password_protection/password_protection_request_content.cc2
-rw-r--r--chromium/components/safe_browsing/content/browser/password_protection/password_protection_service.cc2
-rw-r--r--chromium/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc5
-rw-r--r--chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.cc5
-rw-r--r--chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.h4
-rw-r--r--chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer.cc2
-rw-r--r--chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.cc7
-rw-r--r--chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_unittest.cc17
-rw-r--r--chromium/components/safe_browsing/content/browser/safe_browsing_network_context.cc19
-rw-r--r--chromium/components/safe_browsing/content/browser/safe_browsing_tab_observer.cc4
-rw-r--r--chromium/components/safe_browsing/content/browser/threat_details.cc42
-rw-r--r--chromium/components/safe_browsing/content/browser/threat_details.h8
-rw-r--r--chromium/components/safe_browsing/content/browser/threat_details_cache.cc2
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/BUILD.gn2
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.cc8
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.h4
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger_unittest.cc17
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/mock_trigger_manager.h8
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.cc7
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.h4
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger_unittest.cc28
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/trigger_manager.cc10
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/trigger_manager.h4
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/trigger_manager_unittest.cc4
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.cc53
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.h8
-rw-r--r--chromium/components/safe_browsing/content/browser/triggers/trigger_throttler_unittest.cc37
-rw-r--r--chromium/components/safe_browsing/content/browser/ui_manager.cc21
-rw-r--r--chromium/components/safe_browsing/content/browser/ui_manager.h9
-rw-r--r--chromium/components/safe_browsing/content/browser/ui_manager_unittest.cc3
-rw-r--r--chromium/components/safe_browsing/content/browser/web_api_handshake_checker.cc1
-rw-r--r--chromium/components/safe_browsing/content/browser/web_ui/BUILD.gn1
-rw-r--r--chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.html253
-rw-r--r--chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.js34
-rw-r--r--chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc304
-rw-r--r--chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h47
-rw-r--r--chromium/components/safe_browsing/content/common/file_type_policies_policy_util.cc2
-rw-r--r--chromium/components/safe_browsing/content/common/safe_browsing.mojom32
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/BUILD.gn2
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc8
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h6
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.cc56
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.h8
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc89
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.h36
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_dom_feature_extractor.cc6
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_model_setter_impl.cc81
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_model_setter_impl.h51
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_term_feature_extractor.cc5
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc8
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h6
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.cc44
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.h32
-rw-r--r--chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer_unittest.cc55
-rw-r--r--chromium/components/safe_browsing/content/resources/download_file_types.asciipb35
-rw-r--r--chromium/components/safe_browsing/content/resources/download_file_types_experiment.asciipb35
-rw-r--r--chromium/components/safe_browsing/core/browser/BUILD.gn14
-rw-r--r--chromium/components/safe_browsing/core/browser/db/database_manager.cc5
-rw-r--r--chromium/components/safe_browsing/core/browser/db/database_manager.h7
-rw-r--r--chromium/components/safe_browsing/core/browser/db/fake_database_manager.cc4
-rw-r--r--chromium/components/safe_browsing/core/browser/db/fake_database_manager.h1
-rw-r--r--chromium/components/safe_browsing/core/browser/db/prefix_iterator.h11
-rw-r--r--chromium/components/safe_browsing/core/browser/db/test_database_manager.cc5
-rw-r--r--chromium/components/safe_browsing/core/browser/db/test_database_manager.h1
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_database_unittest.cc2
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.cc22
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.h4
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager_unittest.cc52
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.cc21
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.h1
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_local_database_manager_unittest.cc5
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.cc8
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.h9
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util_unittest.cc2
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_store.cc24
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_store.h6
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_test_util.cc2
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_test_util.h2
-rw-r--r--chromium/components/safe_browsing/core/browser/db/v4_update_protocol_manager_unittest.cc2
-rw-r--r--chromium/components/safe_browsing/core/browser/password_protection/password_protection_request.cc4
-rw-r--r--chromium/components/safe_browsing/core/browser/password_protection/password_protection_service_base.cc4
-rw-r--r--chromium/components/safe_browsing/core/browser/ping_manager.cc67
-rw-r--r--chromium/components/safe_browsing/core/browser/ping_manager.h55
-rw-r--r--chromium/components/safe_browsing/core/browser/ping_manager_unittest.cc16
-rw-r--r--chromium/components/safe_browsing/core/browser/realtime/BUILD.gn1
-rw-r--r--chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc24
-rw-r--r--chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.h4
-rw-r--r--chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc36
-rw-r--r--chromium/components/safe_browsing/core/browser/tailored_security_service/OWNERS1
-rw-r--r--chromium/components/safe_browsing/core/browser/tailored_security_service/tailored_security_service.h2
-rw-r--r--chromium/components/safe_browsing/core/browser/test_safe_browsing_token_fetcher.cc27
-rw-r--r--chromium/components/safe_browsing/core/browser/test_safe_browsing_token_fetcher.h31
-rw-r--r--chromium/components/safe_browsing/core/browser/verdict_cache_manager.cc29
-rw-r--r--chromium/components/safe_browsing/core/browser/verdict_cache_manager.h2
-rw-r--r--chromium/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc23
-rw-r--r--chromium/components/safe_browsing/core/common/fbs/client_model.fbs1
-rw-r--r--chromium/components/safe_browsing/core/common/features.cc40
-rw-r--r--chromium/components/safe_browsing/core/common/features.h36
-rw-r--r--chromium/components/safe_browsing/core/common/proto/client_model.proto10
-rw-r--r--chromium/components/safe_browsing/core/common/proto/csd.proto17
-rw-r--r--chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc15
-rw-r--r--chromium/components/safe_browsing/core/common/safe_browsing_prefs.h16
-rw-r--r--chromium/components/safe_browsing/core/common/utils.cc2
-rw-r--r--chromium/components/safe_search_api/safe_search/safe_search_url_checker_client.cc4
-rw-r--r--chromium/components/safe_search_api/safe_search/safe_search_url_checker_client_unittest.cc14
-rw-r--r--chromium/components/safe_search_api/stub_url_checker.cc14
-rw-r--r--chromium/components/scheduling_metrics/task_duration_metric_reporter.h1
-rw-r--r--chromium/components/search/ntp_features.cc44
-rw-r--r--chromium/components/search/ntp_features.h6
-rw-r--r--chromium/components/search_engines/BUILD.gn1
-rw-r--r--chromium/components/search_engines/DEPS1
-rw-r--r--chromium/components/search_engines/android/BUILD.gn2
-rw-r--r--chromium/components/search_engines/default_search_manager.cc2
-rw-r--r--chromium/components/search_engines/default_search_manager.h1
-rw-r--r--chromium/components/search_engines/default_search_manager_unittest.cc56
-rw-r--r--chromium/components/search_engines/default_search_policy_handler.cc1
-rw-r--r--chromium/components/search_engines/keyword_table.cc1
-rw-r--r--chromium/components/search_engines/keyword_web_data_service.cc1
-rw-r--r--chromium/components/search_engines/keyword_web_data_service.h4
-rw-r--r--chromium/components/search_engines/prepopulated_engines.json3
-rw-r--r--chromium/components/search_engines/prepopulated_engines_schema.json4
-rw-r--r--chromium/components/search_engines/search_engine_type.h3
-rw-r--r--chromium/components/search_engines/search_host_to_urls_map.cc11
-rw-r--r--chromium/components/search_engines/search_host_to_urls_map.h4
-rw-r--r--chromium/components/search_engines/search_host_to_urls_map_unittest.cc22
-rw-r--r--chromium/components/search_engines/template_url.cc36
-rw-r--r--chromium/components/search_engines/template_url_data.cc30
-rw-r--r--chromium/components/search_engines/template_url_data.h11
-rw-r--r--chromium/components/search_engines/template_url_data_unittest.cc2
-rw-r--r--chromium/components/search_engines/template_url_data_util.cc23
-rw-r--r--chromium/components/search_engines/template_url_id.h5
-rw-r--r--chromium/components/search_engines/template_url_parser.cc2
-rw-r--r--chromium/components/search_engines/template_url_service.cc92
-rw-r--r--chromium/components/search_engines/template_url_service.h29
-rw-r--r--chromium/components/search_engines/template_url_service_util_unittest.cc3
-rw-r--r--chromium/components/search_engines/template_url_starter_pack_data.cc59
-rw-r--r--chromium/components/search_engines/template_url_starter_pack_data.h25
-rw-r--r--chromium/components/search_engines/template_url_unittest.cc12
-rw-r--r--chromium/components/search_engines/util.cc213
-rw-r--r--chromium/components/search_engines/util.h70
-rw-r--r--chromium/components/search_provider_logos/logo_service_impl.cc4
-rw-r--r--chromium/components/search_provider_logos/logo_service_impl_unittest.cc1
-rw-r--r--chromium/components/security_interstitials/DEPS1
-rw-r--r--chromium/components/security_interstitials/content/android/BUILD.gn2
-rw-r--r--chromium/components/security_interstitials/content/captive_portal_blocking_page.cc4
-rw-r--r--chromium/components/security_interstitials/content/certificate_error_report_unittest.cc3
-rw-r--r--chromium/components/security_interstitials/content/insecure_form_navigation_throttle.cc31
-rw-r--r--chromium/components/security_interstitials/content/insecure_form_tab_storage.h14
-rw-r--r--chromium/components/security_interstitials/content/security_interstitial_tab_helper_unittest.cc5
-rw-r--r--chromium/components/security_interstitials/content/ssl_error_handler.cc2
-rw-r--r--chromium/components/security_interstitials/content/ssl_error_handler_unittest.cc16
-rw-r--r--chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc89
-rw-r--r--chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.h15
-rw-r--r--chromium/components/security_interstitials/core/BUILD.gn5
-rw-r--r--chromium/components/security_interstitials/core/blocked_interception_ui.cc2
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/images/blocked.svg2
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/images/heavy_ad.svg2
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/images/light_bulb_grey.svg2
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/images/light_bulb_white.svg2
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_large.js4
-rw-r--r--chromium/components/security_interstitials/core/https_only_mode_allowlist.cc120
-rw-r--r--chromium/components/security_interstitials/core/https_only_mode_allowlist.h84
-rw-r--r--chromium/components/security_interstitials/core/mitm_software_ui.cc10
-rw-r--r--chromium/components/security_interstitials/core/omnibox_https_upgrade_metrics.cc11
-rw-r--r--chromium/components/security_interstitials/core/omnibox_https_upgrade_metrics.h37
-rw-r--r--chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc6
-rw-r--r--chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc2
-rw-r--r--chromium/components/security_state/content/android/BUILD.gn3
-rw-r--r--chromium/components/security_state/core/BUILD.gn15
-rw-r--r--chromium/components/security_state/core/features.cc19
-rw-r--r--chromium/components/security_state/core/features.h34
-rw-r--r--chromium/components/security_state/core/security_state.cc12
-rw-r--r--chromium/components/security_state/core/security_state.h4
-rw-r--r--chromium/components/security_state/core/security_state_unittest.cc6
-rw-r--r--chromium/components/segmentation_platform/components_unittests.filter2
-rw-r--r--chromium/components/segmentation_platform/content/BUILD.gn64
-rw-r--r--chromium/components/segmentation_platform/content/DEPS4
-rw-r--r--chromium/components/segmentation_platform/content/page_load_trigger_context.cc34
-rw-r--r--chromium/components/segmentation_platform/content/page_load_trigger_context.h37
-rw-r--r--chromium/components/segmentation_platform/content/segmentation_platform_tab_helper.cc42
-rw-r--r--chromium/components/segmentation_platform/content/segmentation_platform_tab_helper.h50
-rw-r--r--chromium/components/segmentation_platform/internal/BUILD.gn105
-rw-r--r--chromium/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.cc36
-rw-r--r--chromium/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h32
-rw-r--r--chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc57
-rw-r--r--chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.h12
-rw-r--r--chromium/components/segmentation_platform/internal/constants.cc3
-rw-r--r--chromium/components/segmentation_platform/internal/constants.h3
-rw-r--r--chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.cc2
-rw-r--r--chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.h1
-rw-r--r--chromium/components/segmentation_platform/internal/data_collection/training_data_collector.cc6
-rw-r--r--chromium/components/segmentation_platform/internal/data_collection/training_data_collector.h15
-rw-r--r--chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc224
-rw-r--r--chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h56
-rw-r--r--chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc190
-rw-r--r--chromium/components/segmentation_platform/internal/database/database_maintenance_impl.cc7
-rw-r--r--chromium/components/segmentation_platform/internal/database/database_maintenance_impl.h21
-rw-r--r--chromium/components/segmentation_platform/internal/database/database_maintenance_impl_unittest.cc26
-rw-r--r--chromium/components/segmentation_platform/internal/database/mock_signal_storage_config.h2
-rw-r--r--chromium/components/segmentation_platform/internal/database/mock_ukm_database.cc2
-rw-r--r--chromium/components/segmentation_platform/internal/database/mock_ukm_database.h9
-rw-r--r--chromium/components/segmentation_platform/internal/database/segment_info_database.cc12
-rw-r--r--chromium/components/segmentation_platform/internal/database/segment_info_database.h17
-rw-r--r--chromium/components/segmentation_platform/internal/database/segment_info_database_unittest.cc19
-rw-r--r--chromium/components/segmentation_platform/internal/database/signal_database_impl.cc2
-rw-r--r--chromium/components/segmentation_platform/internal/database/signal_key_internal.cc1
-rw-r--r--chromium/components/segmentation_platform/internal/database/signal_storage_config.cc135
-rw-r--r--chromium/components/segmentation_platform/internal/database/signal_storage_config.h15
-rw-r--r--chromium/components/segmentation_platform/internal/database/signal_storage_config_unittest.cc56
-rw-r--r--chromium/components/segmentation_platform/internal/database/storage_service.cc43
-rw-r--r--chromium/components/segmentation_platform/internal/database/storage_service.h23
-rw-r--r--chromium/components/segmentation_platform/internal/database/test_segment_info_database.cc124
-rw-r--r--chromium/components/segmentation_platform/internal/database/test_segment_info_database.h45
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_database.cc51
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_database.h62
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_database_backend.cc160
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_database_backend.h24
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_database_backend_unittest.cc261
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_database_impl.cc80
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_database_impl.h51
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_metrics_table.cc47
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_metrics_table.h11
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_metrics_table_unittest.cc54
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_types.cc48
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_types.h58
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_url_table.cc35
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_url_table.h9
-rw-r--r--chromium/components/segmentation_platform/internal/database/ukm_url_table_unittest.cc51
-rw-r--r--chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc22
-rw-r--r--chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.h9
-rw-r--r--chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.cc16
-rw-r--r--chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.h10
-rw-r--r--chromium/components/segmentation_platform/internal/execution/custom_input_processor.cc123
-rw-r--r--chromium/components/segmentation_platform/internal/execution/custom_input_processor_unittest.cc115
-rw-r--r--chromium/components/segmentation_platform/internal/execution/default_model_manager.cc30
-rw-r--r--chromium/components/segmentation_platform/internal/execution/default_model_manager.h25
-rw-r--r--chromium/components/segmentation_platform/internal/execution/default_model_manager_unittest.cc17
-rw-r--r--chromium/components/segmentation_platform/internal/execution/execution_request.cc12
-rw-r--r--chromium/components/segmentation_platform/internal/execution/execution_request.h53
-rw-r--r--chromium/components/segmentation_platform/internal/execution/feature_list_query_processor.cc116
-rw-r--r--chromium/components/segmentation_platform/internal/execution/feature_processor_state.cc66
-rw-r--r--chromium/components/segmentation_platform/internal/execution/mock_feature_list_query_processor.cc14
-rw-r--r--chromium/components/segmentation_platform/internal/execution/mock_feature_list_query_processor.h30
-rw-r--r--chromium/components/segmentation_platform/internal/execution/mock_model_provider.cc8
-rw-r--r--chromium/components/segmentation_platform/internal/execution/mock_model_provider.h21
-rw-r--r--chromium/components/segmentation_platform/internal/execution/model_execution_manager.h5
-rw-r--r--chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc29
-rw-r--r--chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.h18
-rw-r--r--chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc28
-rw-r--r--chromium/components/segmentation_platform/internal/execution/model_executor.h7
-rw-r--r--chromium/components/segmentation_platform/internal/execution/model_executor_impl.cc56
-rw-r--r--chromium/components/segmentation_platform/internal/execution/model_executor_impl.h33
-rw-r--r--chromium/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc53
-rw-r--r--chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc22
-rw-r--r--chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h14
-rw-r--r--chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc18
-rw-r--r--chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h4
-rw-r--r--chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc55
-rw-r--r--chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h2
-rw-r--r--chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc21
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc249
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor.h (renamed from chromium/components/segmentation_platform/internal/execution/custom_input_processor.h)57
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor_unittest.cc294
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator.h (renamed from chromium/components/segmentation_platform/internal/execution/feature_aggregator.h)10
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.cc (renamed from chromium/components/segmentation_platform/internal/execution/feature_aggregator_impl.cc)8
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.h (renamed from chromium/components/segmentation_platform/internal/execution/feature_aggregator_impl.h)12
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator_impl_unittest.cc (renamed from chromium/components/segmentation_platform/internal/execution/feature_aggregator_impl_unittest.cc)8
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc167
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h (renamed from chromium/components/segmentation_platform/internal/execution/feature_list_query_processor.h)59
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc (renamed from chromium/components/segmentation_platform/internal/execution/feature_list_query_processor_unittest.cc)154
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/feature_processor_state.cc96
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/feature_processor_state.h (renamed from chromium/components/segmentation_platform/internal/execution/feature_processor_state.h)60
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/input_delegate.cc30
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/input_delegate.h60
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/mock_feature_aggregator.cc (renamed from chromium/components/segmentation_platform/internal/execution/mock_feature_aggregator.cc)6
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/mock_feature_aggregator.h (renamed from chromium/components/segmentation_platform/internal/execution/mock_feature_aggregator.h)12
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.cc16
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h32
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/query_processor.h (renamed from chromium/components/segmentation_platform/internal/execution/query_processor.h)19
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor.cc (renamed from chromium/components/segmentation_platform/internal/execution/sql_feature_processor.cc)90
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor.h (renamed from chromium/components/segmentation_platform/internal/execution/sql_feature_processor.h)40
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor_unittest.cc193
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc (renamed from chromium/components/segmentation_platform/internal/execution/uma_feature_processor.cc)12
-rw-r--r--chromium/components/segmentation_platform/internal/execution/processing/uma_feature_processor.h (renamed from chromium/components/segmentation_platform/internal/execution/uma_feature_processor.h)20
-rw-r--r--chromium/components/segmentation_platform/internal/input_context.cc12
-rw-r--r--chromium/components/segmentation_platform/internal/input_context.h40
-rw-r--r--chromium/components/segmentation_platform/internal/local_state_helper_impl.cc35
-rw-r--r--chromium/components/segmentation_platform/internal/local_state_helper_impl.h38
-rw-r--r--chromium/components/segmentation_platform/internal/metadata/metadata_utils.cc (renamed from chromium/components/segmentation_platform/internal/database/metadata_utils.cc)22
-rw-r--r--chromium/components/segmentation_platform/internal/metadata/metadata_utils.h (renamed from chromium/components/segmentation_platform/internal/database/metadata_utils.h)16
-rw-r--r--chromium/components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc (renamed from chromium/components/segmentation_platform/internal/database/metadata_utils_unittest.cc)26
-rw-r--r--chromium/components/segmentation_platform/internal/metadata/metadata_writer.cc88
-rw-r--r--chromium/components/segmentation_platform/internal/metadata/metadata_writer.h111
-rw-r--r--chromium/components/segmentation_platform/internal/metric_filter_utils.cc33
-rw-r--r--chromium/components/segmentation_platform/internal/metric_filter_utils.h33
-rw-r--r--chromium/components/segmentation_platform/internal/mock_ukm_data_manager.h18
-rw-r--r--chromium/components/segmentation_platform/internal/proto/BUILD.gn3
-rw-r--r--chromium/components/segmentation_platform/internal/proto/model_metadata.proto39
-rw-r--r--chromium/components/segmentation_platform/internal/proto/model_prediction.proto4
-rw-r--r--chromium/components/segmentation_platform/internal/proto/signal_storage_config.proto6
-rw-r--r--chromium/components/segmentation_platform/internal/proto/types.proto1
-rw-r--r--chromium/components/segmentation_platform/internal/scheduler/execution_service.cc102
-rw-r--r--chromium/components/segmentation_platform/internal/scheduler/execution_service.h70
-rw-r--r--chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler.h10
-rw-r--r--chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc33
-rw-r--r--chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h30
-rw-r--r--chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc85
-rw-r--r--chromium/components/segmentation_platform/internal/segment_id_convertor.cc27
-rw-r--r--chromium/components/segmentation_platform/internal/segment_id_convertor.h24
-rw-r--r--chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.cc193
-rw-r--r--chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.h101
-rw-r--r--chromium/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc136
-rw-r--r--chromium/components/segmentation_platform/internal/segmentation_platform_service_test_base.cc45
-rw-r--r--chromium/components/segmentation_platform/internal/segmentation_ukm_helper.cc48
-rw-r--r--chromium/components/segmentation_platform/internal/segmentation_ukm_helper.h50
-rw-r--r--chromium/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc141
-rw-r--r--chromium/components/segmentation_platform/internal/selection/experimental_group_recorder.cc55
-rw-r--r--chromium/components/segmentation_platform/internal/selection/experimental_group_recorder.h47
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_result_provider.cc301
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_result_provider.h44
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc255
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_score_provider.cc8
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_score_provider.h8
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_score_provider_unittest.cc10
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_selector.h15
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_selector_impl.cc130
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_selector_impl.h49
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segment_selector_unittest.cc233
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc5
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.h10
-rw-r--r--chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs_unittest.cc11
-rw-r--r--chromium/components/segmentation_platform/internal/service_proxy_impl.cc44
-rw-r--r--chromium/components/segmentation_platform/internal/service_proxy_impl.h19
-rw-r--r--chromium/components/segmentation_platform/internal/service_proxy_impl_unittest.cc78
-rw-r--r--chromium/components/segmentation_platform/internal/signals/history_delegate_impl.cc19
-rw-r--r--chromium/components/segmentation_platform/internal/signals/history_service_observer.cc73
-rw-r--r--chromium/components/segmentation_platform/internal/signals/history_service_observer.h29
-rw-r--r--chromium/components/segmentation_platform/internal/signals/signal_filter_processor.cc46
-rw-r--r--chromium/components/segmentation_platform/internal/signals/signal_filter_processor.h28
-rw-r--r--chromium/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc130
-rw-r--r--chromium/components/segmentation_platform/internal/signals/signal_handler.cc37
-rw-r--r--chromium/components/segmentation_platform/internal/signals/signal_handler.h21
-rw-r--r--chromium/components/segmentation_platform/internal/signals/ukm_observer.cc46
-rw-r--r--chromium/components/segmentation_platform/internal/signals/ukm_observer.h30
-rw-r--r--chromium/components/segmentation_platform/internal/signals/ukm_observer_unittest.cc70
-rw-r--r--chromium/components/segmentation_platform/internal/signals/url_signal_handler.cc5
-rw-r--r--chromium/components/segmentation_platform/internal/signals/url_signal_handler.h2
-rw-r--r--chromium/components/segmentation_platform/internal/signals/url_signal_handler_unittest.cc4
-rw-r--r--chromium/components/segmentation_platform/internal/signals/user_action_signal_handler.cc4
-rw-r--r--chromium/components/segmentation_platform/internal/stats.cc225
-rw-r--r--chromium/components/segmentation_platform/internal/stats.h64
-rw-r--r--chromium/components/segmentation_platform/internal/stats_unittest.cc38
-rw-r--r--chromium/components/segmentation_platform/internal/tools/create_class.py181
-rw-r--r--chromium/components/segmentation_platform/internal/ukm_data_manager.h37
-rw-r--r--chromium/components/segmentation_platform/internal/ukm_data_manager_impl.cc133
-rw-r--r--chromium/components/segmentation_platform/internal/ukm_data_manager_impl.h31
-rw-r--r--chromium/components/segmentation_platform/internal/ukm_data_manager_impl_unittest.cc94
-rw-r--r--chromium/components/segmentation_platform/public/BUILD.gn29
-rw-r--r--chromium/components/segmentation_platform/public/config.h22
-rw-r--r--chromium/components/segmentation_platform/public/features.cc36
-rw-r--r--chromium/components/segmentation_platform/public/features.h20
-rw-r--r--chromium/components/segmentation_platform/public/field_trial_register.h42
-rw-r--r--chromium/components/segmentation_platform/public/local_state_helper.h34
-rw-r--r--chromium/components/segmentation_platform/public/model_provider.cc5
-rw-r--r--chromium/components/segmentation_platform/public/model_provider.h20
-rw-r--r--chromium/components/segmentation_platform/public/proto/BUILD.gn21
-rw-r--r--chromium/components/segmentation_platform/public/proto/segmentation_platform.proto61
-rw-r--r--chromium/components/segmentation_platform/public/segment_selection_result.h6
-rw-r--r--chromium/components/segmentation_platform/public/segmentation_platform_service.h28
-rw-r--r--chromium/components/segmentation_platform/public/service_proxy.cc4
-rw-r--r--chromium/components/segmentation_platform/public/service_proxy.h21
-rw-r--r--chromium/components/segmentation_platform/public/trigger.h19
-rw-r--r--chromium/components/segmentation_platform/public/trigger_context.cc28
-rw-r--r--chromium/components/segmentation_platform/public/trigger_context.h31
-rw-r--r--chromium/components/send_tab_to_self/BUILD.gn29
-rw-r--r--chromium/components/send_tab_to_self/DEPS3
-rw-r--r--chromium/components/send_tab_to_self/entry_point_display_reason.cc62
-rw-r--r--chromium/components/send_tab_to_self/entry_point_display_reason.h47
-rw-r--r--chromium/components/send_tab_to_self/entry_point_display_reason_unittest.cc197
-rw-r--r--chromium/components/send_tab_to_self/features.cc44
-rw-r--r--chromium/components/send_tab_to_self/features.h14
-rw-r--r--chromium/components/send_tab_to_self/features_unittest.cc93
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_bridge.cc51
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_bridge.h4
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc92
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_entry.cc20
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_entry.h6
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc43
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_model.h5
-rw-r--r--chromium/components/send_tab_to_self/test_send_tab_to_self_model.cc3
-rw-r--r--chromium/components/send_tab_to_self/test_send_tab_to_self_model.h3
-rw-r--r--chromium/components/services/app_service/BUILD.gn3
-rw-r--r--chromium/components/services/app_service/app_service_mojom_impl.cc96
-rw-r--r--chromium/components/services/app_service/app_service_mojom_impl.h34
-rw-r--r--chromium/components/services/app_service/app_service_mojom_impl_unittest.cc58
-rw-r--r--chromium/components/services/app_service/public/cpp/BUILD.gn22
-rw-r--r--chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc5
-rw-r--r--chromium/components/services/app_service/public/cpp/app_types.cc3
-rw-r--r--chromium/components/services/app_service/public/cpp/app_types.h6
-rw-r--r--chromium/components/services/app_service/public/cpp/app_update.cc48
-rw-r--r--chromium/components/services/app_service/public/cpp/app_update.h6
-rw-r--r--chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc6
-rw-r--r--chromium/components/services/app_service/public/cpp/app_update_unittest.cc62
-rw-r--r--chromium/components/services/app_service/public/cpp/features.cc5
-rw-r--r--chromium/components/services/app_service/public/cpp/features.h10
-rw-r--r--chromium/components/services/app_service/public/cpp/instance_registry.h2
-rw-r--r--chromium/components/services/app_service/public/cpp/instance_registry_unittest.cc15
-rw-r--r--chromium/components/services/app_service/public/cpp/intent.cc91
-rw-r--r--chromium/components/services/app_service/public/cpp/intent.h27
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter.cc145
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter.h33
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter_util.cc164
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter_util.h14
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc436
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_util.cc53
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_util.h24
-rw-r--r--chromium/components/services/app_service/public/cpp/intent_util_unittest.cc176
-rw-r--r--chromium/components/services/app_service/public/cpp/permission.cc14
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_app.cc189
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_app.h86
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc66
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_converter.h9
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc23
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_impl.cc (renamed from chromium/components/services/app_service/public/cpp/preferred_apps.cc)166
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_impl.h (renamed from chromium/components/services/app_service/public/cpp/preferred_apps.h)89
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_list.cc88
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_list.h33
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_list_handle.h9
-rw-r--r--chromium/components/services/app_service/public/cpp/preferred_apps_list_unittest.cc367
-rw-r--r--chromium/components/services/app_service/public/mojom/app_service.mojom6
-rw-r--r--chromium/components/services/filesystem/file_impl_unittest.cc7
-rw-r--r--chromium/components/services/quarantine/BUILD.gn2
-rw-r--r--chromium/components/services/screen_ai/BUILD.gn47
-rw-r--r--chromium/components/services/screen_ai/DEPS9
-rw-r--r--chromium/components/services/screen_ai/buildflags/BUILD.gn11
-rw-r--r--chromium/components/services/screen_ai/buildflags/features.gni12
-rw-r--r--chromium/components/services/screen_ai/proto/BUILD.gn27
-rw-r--r--chromium/components/services/screen_ai/proto/chrome_screen_ai.proto158
-rw-r--r--chromium/components/services/screen_ai/proto/dimension.proto29
-rw-r--r--chromium/components/services/screen_ai/proto/proto_convertor.cc528
-rw-r--r--chromium/components/services/screen_ai/proto/proto_convertor.h31
-rw-r--r--chromium/components/services/screen_ai/proto/proto_convertor_unittest.cc256
-rw-r--r--chromium/components/services/screen_ai/proto/view_hierarchy.proto109
-rw-r--r--chromium/components/services/screen_ai/public/cpp/BUILD.gn16
-rw-r--r--chromium/components/services/screen_ai/public/cpp/screen_ai_service_router.cc42
-rw-r--r--chromium/components/services/screen_ai/public/cpp/screen_ai_service_router.h37
-rw-r--r--chromium/components/services/screen_ai/public/cpp/screen_ai_service_router_factory.cc41
-rw-r--r--chromium/components/services/screen_ai/public/cpp/screen_ai_service_router_factory.h39
-rw-r--r--chromium/components/services/screen_ai/public/cpp/utilities.cc24
-rw-r--r--chromium/components/services/screen_ai/public/cpp/utilities.h11
-rw-r--r--chromium/components/services/screen_ai/public/mojom/BUILD.gn2
-rw-r--r--chromium/components/services/screen_ai/public/mojom/screen_ai_service.mojom66
-rw-r--r--chromium/components/services/screen_ai/sandbox/screen_ai_sandbox_hook_linux.cc25
-rw-r--r--chromium/components/services/screen_ai/screen_ai_service_impl.cc128
-rw-r--r--chromium/components/services/screen_ai/screen_ai_service_impl.h45
-rw-r--r--chromium/components/services/storage/BUILD.gn2
-rw-r--r--chromium/components/services/storage/dom_storage/local_storage_impl.cc147
-rw-r--r--chromium/components/services/storage/dom_storage/local_storage_impl.h18
-rw-r--r--chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.cc34
-rw-r--r--chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.h4
-rw-r--r--chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager_unittest.cc37
-rw-r--r--chromium/components/services/storage/indexed_db/locks/leveled_lock_range.h2
-rw-r--r--chromium/components/services/storage/privileged/mojom/BUILD.gn21
-rw-r--r--chromium/components/services/storage/privileged/mojom/OWNERS2
-rw-r--r--chromium/components/services/storage/privileged/mojom/indexed_db_control.mojom (renamed from chromium/components/services/storage/public/mojom/indexed_db_control.mojom)30
-rw-r--r--chromium/components/services/storage/privileged/mojom/indexed_db_control_test.mojom (renamed from chromium/components/services/storage/public/mojom/indexed_db_control_test.mojom)32
-rw-r--r--chromium/components/services/storage/public/cpp/buckets/BUILD.gn6
-rw-r--r--chromium/components/services/storage/public/cpp/buckets/bucket_info.cc9
-rw-r--r--chromium/components/services/storage/public/cpp/buckets/bucket_info.h13
-rw-r--r--chromium/components/services/storage/public/cpp/buckets/bucket_init_params.cc31
-rw-r--r--chromium/components/services/storage/public/cpp/buckets/bucket_init_params.h50
-rw-r--r--chromium/components/services/storage/public/cpp/buckets/bucket_locator.h4
-rw-r--r--chromium/components/services/storage/public/cpp/quota_error_or.h2
-rw-r--r--chromium/components/services/storage/public/mojom/BUILD.gn2
-rw-r--r--chromium/components/services/storage/public/mojom/service_worker_database.mojom2
-rw-r--r--chromium/components/services/storage/service_worker/service_worker_database.h2
-rw-r--r--chromium/components/services/storage/shared_storage/async_shared_storage_database.h36
-rw-r--r--chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.cc65
-rw-r--r--chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.h24
-rw-r--r--chromium/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc652
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_database.cc205
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_database.h80
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_database_unittest.cc265
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_manager.cc91
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_manager.h35
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_manager_unittest.cc286
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_options.cc31
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_options.h50
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_test_utils.cc46
-rw-r--r--chromium/components/services/storage/shared_storage/shared_storage_test_utils.h22
-rw-r--r--chromium/components/services/unzip/BUILD.gn4
-rw-r--r--chromium/components/services/unzip/public/cpp/unzip.cc111
-rw-r--r--chromium/components/services/unzip/public/cpp/unzip.h7
-rw-r--r--chromium/components/services/unzip/public/cpp/unzip_unittest.cc106
-rw-r--r--chromium/components/services/unzip/public/mojom/unzipper.mojom25
-rw-r--r--chromium/components/services/unzip/unzipper_impl.cc86
-rw-r--r--chromium/components/services/unzip/unzipper_impl.h8
-rw-r--r--chromium/components/sessions/core/base_session_service_commands.cc31
-rw-r--r--chromium/components/sessions/core/base_session_service_commands.h14
-rw-r--r--chromium/components/sessions/core/live_tab_context.h1
-rw-r--r--chromium/components/sessions/core/session_id_generator.cc4
-rw-r--r--chromium/components/sessions/core/session_id_generator.h3
-rw-r--r--chromium/components/sessions/core/session_service_commands.cc38
-rw-r--r--chromium/components/sessions/core/tab_restore_service.h3
-rw-r--r--chromium/components/sessions/core/tab_restore_service_client.h3
-rw-r--r--chromium/components/sessions/core/tab_restore_service_helper.cc11
-rw-r--r--chromium/components/sessions/core/tab_restore_service_impl.cc63
-rw-r--r--chromium/components/shared_highlighting/OWNERS1
-rw-r--r--chromium/components/shared_highlighting/core/common/BUILD.gn1
-rw-r--r--chromium/components/shared_highlighting/core/common/disabled_sites.cc44
-rw-r--r--chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.cc15
-rw-r--r--chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.h20
-rw-r--r--chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test_results.h23
-rw-r--r--chromium/components/shared_highlighting/core/common/shared_highlighting_features.cc20
-rw-r--r--chromium/components/shared_highlighting/core/common/shared_highlighting_features.h15
-rw-r--r--chromium/components/shared_highlighting/core/common/text_fragment.cc4
-rw-r--r--chromium/components/signin/core/browser/BUILD.gn21
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor.cc126
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor.h2
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor_delegate.cc29
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor_delegate.h60
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor_unittest.cc88
-rw-r--r--chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.cc10
-rw-r--r--chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.h6
-rw-r--r--chromium/components/signin/core/browser/chrome_connected_header_helper.cc6
-rw-r--r--chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc180
-rw-r--r--chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h31
-rw-r--r--chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc29
-rw-r--r--chromium/components/signin/core/browser/dice_header_helper.cc2
-rw-r--r--chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc12
-rw-r--r--chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h6
-rw-r--r--chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.cc48
-rw-r--r--chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.h18
-rw-r--r--chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate_unittest.cc140
-rw-r--r--chromium/components/signin/core/browser/signin_header_helper.cc14
-rw-r--r--chromium/components/signin/core/browser/signin_header_helper_unittest.cc4
-rw-r--r--chromium/components/signin/core/browser/signin_internals_util.h22
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider.cc161
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider.h104
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider_base.cc31
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider_base.h60
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.cc23
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.h67
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider_helpers.cc51
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider_helpers.h53
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider_helpers_unittest.cc79
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider_unittest.cc80
-rw-r--r--chromium/components/signin/internal/identity_manager/BUILD.gn13
-rw-r--r--chromium/components/signin/internal/identity_manager/account_capabilities_list.h4
-rw-r--r--chromium/components/signin/internal/identity_manager/account_tracker_service.cc35
-rw-r--r--chromium/components/signin/internal/identity_manager/account_tracker_service.h17
-rw-r--r--chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc59
-rw-r--r--chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.cc3
-rw-r--r--chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h3
-rw-r--r--chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc8
-rw-r--r--chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h5
-rw-r--r--chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc120
-rw-r--r--chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc252
-rw-r--r--chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h11
-rw-r--r--chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc188
-rw-r--r--chromium/components/signin/internal/identity_manager/primary_account_manager.cc29
-rw-r--r--chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc37
-rw-r--r--chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc20
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.cc5
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.h4
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc46
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h14
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc3
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h3
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc25
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h3
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc32
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h12
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc106
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h3
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm3
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm23
-rw-r--r--chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_unittest.cc3
-rw-r--r--chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.cc5
-rw-r--r--chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.h3
-rw-r--r--chromium/components/signin/ios/browser/account_consistency_service.mm6
-rw-r--r--chromium/components/signin/public/android/BUILD.gn5
-rw-r--r--chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java38
-rw-r--r--chromium/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java7
-rw-r--r--chromium/components/signin/public/base/signin_client.cc4
-rw-r--r--chromium/components/signin/public/base/signin_client.h10
-rw-r--r--chromium/components/signin/public/base/signin_metrics.cc211
-rw-r--r--chromium/components/signin/public/base/signin_metrics.h30
-rw-r--r--chromium/components/signin/public/base/signin_metrics_unittest.cc41
-rw-r--r--chromium/components/signin/public/base/signin_pref_names.cc5
-rw-r--r--chromium/components/signin/public/base/signin_pref_names.h5
-rw-r--r--chromium/components/signin/public/base/signin_switches.cc13
-rw-r--r--chromium/components/signin/public/base/signin_switches.h2
-rw-r--r--chromium/components/signin/public/base/test_signin_client.cc6
-rw-r--r--chromium/components/signin/public/base/test_signin_client.h3
-rw-r--r--chromium/components/signin/public/identity_manager/BUILD.gn4
-rw-r--r--chromium/components/signin/public/identity_manager/account_capabilities.cc4
-rw-r--r--chromium/components/signin/public/identity_manager/account_capabilities.h3
-rw-r--r--chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.cc4
-rw-r--r--chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.h4
-rw-r--r--chromium/components/signin/public/identity_manager/account_capabilities_unittest.cc12
-rw-r--r--chromium/components/signin/public/identity_manager/identity_manager.cc9
-rw-r--r--chromium/components/signin/public/identity_manager/identity_manager.h12
-rw-r--r--chromium/components/signin/public/identity_manager/identity_manager_builder.cc16
-rw-r--r--chromium/components/signin/public/identity_manager/identity_manager_builder.h10
-rw-r--r--chromium/components/signin/public/identity_manager/identity_manager_builder_unittest.cc10
-rw-r--r--chromium/components/signin/public/identity_manager/identity_manager_unittest.cc23
-rw-r--r--chromium/components/signin/public/identity_manager/identity_test_environment.cc6
-rw-r--r--chromium/components/signin/public/identity_manager/identity_test_utils.cc32
-rw-r--r--chromium/components/signin/public/identity_manager/identity_test_utils.h15
-rw-r--r--chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc28
-rw-r--r--chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h70
-rw-r--r--chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc71
-rw-r--r--chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc8
-rw-r--r--chromium/components/site_engagement/content/android/BUILD.gn2
-rw-r--r--chromium/components/site_engagement/content/site_engagement_metrics.cc9
-rw-r--r--chromium/components/site_engagement/content/site_engagement_metrics.h1
-rw-r--r--chromium/components/site_engagement/content/site_engagement_score.h5
-rw-r--r--chromium/components/site_engagement/content/site_engagement_service.cc20
-rw-r--r--chromium/components/site_engagement/content/site_engagement_service.h4
-rw-r--r--chromium/components/site_isolation/site_isolation_policy_unittest.cc1
-rw-r--r--chromium/components/site_settings_strings.grdp6
-rw-r--r--chromium/components/site_settings_strings_grdp/IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API.png.sha11
-rw-r--r--chromium/components/site_settings_strings_grdp/IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API_MID_SENTENCE.png.sha11
-rw-r--r--chromium/components/site_settings_strings_grdp/OWNERS2
-rw-r--r--chromium/components/spellcheck/browser/android/BUILD.gn6
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_platform_mac.mm21
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider.cc2
-rw-r--r--chromium/components/ssl_errors/error_info.cc4
-rw-r--r--chromium/components/storage_monitor/BUILD.gn8
-rw-r--r--chromium/components/storage_monitor/image_capture_device_manager.mm7
-rw-r--r--chromium/components/strings/components_strings_af.xtb55
-rw-r--r--chromium/components/strings/components_strings_am.xtb85
-rw-r--r--chromium/components/strings/components_strings_ar.xtb59
-rw-r--r--chromium/components/strings/components_strings_as.xtb55
-rw-r--r--chromium/components/strings/components_strings_az.xtb55
-rw-r--r--chromium/components/strings/components_strings_be.xtb55
-rw-r--r--chromium/components/strings/components_strings_bg.xtb55
-rw-r--r--chromium/components/strings/components_strings_bn.xtb55
-rw-r--r--chromium/components/strings/components_strings_bs.xtb61
-rw-r--r--chromium/components/strings/components_strings_ca.xtb55
-rw-r--r--chromium/components/strings/components_strings_cs.xtb55
-rw-r--r--chromium/components/strings/components_strings_cy.xtb55
-rw-r--r--chromium/components/strings/components_strings_da.xtb57
-rw-r--r--chromium/components/strings/components_strings_de.xtb59
-rw-r--r--chromium/components/strings/components_strings_el.xtb57
-rw-r--r--chromium/components/strings/components_strings_en-GB.xtb55
-rw-r--r--chromium/components/strings/components_strings_es-419.xtb55
-rw-r--r--chromium/components/strings/components_strings_es.xtb55
-rw-r--r--chromium/components/strings/components_strings_et.xtb55
-rw-r--r--chromium/components/strings/components_strings_eu.xtb57
-rw-r--r--chromium/components/strings/components_strings_fa.xtb59
-rw-r--r--chromium/components/strings/components_strings_fi.xtb55
-rw-r--r--chromium/components/strings/components_strings_fil.xtb55
-rw-r--r--chromium/components/strings/components_strings_fr-CA.xtb55
-rw-r--r--chromium/components/strings/components_strings_fr.xtb59
-rw-r--r--chromium/components/strings/components_strings_gl.xtb55
-rw-r--r--chromium/components/strings/components_strings_gu.xtb57
-rw-r--r--chromium/components/strings/components_strings_hi.xtb59
-rw-r--r--chromium/components/strings/components_strings_hr.xtb55
-rw-r--r--chromium/components/strings/components_strings_hu.xtb57
-rw-r--r--chromium/components/strings/components_strings_hy.xtb55
-rw-r--r--chromium/components/strings/components_strings_id.xtb59
-rw-r--r--chromium/components/strings/components_strings_is.xtb55
-rw-r--r--chromium/components/strings/components_strings_it.xtb59
-rw-r--r--chromium/components/strings/components_strings_iw.xtb59
-rw-r--r--chromium/components/strings/components_strings_ja.xtb57
-rw-r--r--chromium/components/strings/components_strings_ka.xtb55
-rw-r--r--chromium/components/strings/components_strings_kk.xtb55
-rw-r--r--chromium/components/strings/components_strings_km.xtb55
-rw-r--r--chromium/components/strings/components_strings_kn.xtb55
-rw-r--r--chromium/components/strings/components_strings_ko.xtb55
-rw-r--r--chromium/components/strings/components_strings_ky.xtb57
-rw-r--r--chromium/components/strings/components_strings_lo.xtb55
-rw-r--r--chromium/components/strings/components_strings_lt.xtb55
-rw-r--r--chromium/components/strings/components_strings_lv.xtb55
-rw-r--r--chromium/components/strings/components_strings_mk.xtb57
-rw-r--r--chromium/components/strings/components_strings_ml.xtb65
-rw-r--r--chromium/components/strings/components_strings_mn.xtb55
-rw-r--r--chromium/components/strings/components_strings_mr.xtb63
-rw-r--r--chromium/components/strings/components_strings_ms.xtb55
-rw-r--r--chromium/components/strings/components_strings_my.xtb55
-rw-r--r--chromium/components/strings/components_strings_ne.xtb55
-rw-r--r--chromium/components/strings/components_strings_nl.xtb55
-rw-r--r--chromium/components/strings/components_strings_no.xtb55
-rw-r--r--chromium/components/strings/components_strings_or.xtb58
-rw-r--r--chromium/components/strings/components_strings_pa.xtb59
-rw-r--r--chromium/components/strings/components_strings_pl.xtb57
-rw-r--r--chromium/components/strings/components_strings_pt-BR.xtb55
-rw-r--r--chromium/components/strings/components_strings_pt-PT.xtb57
-rw-r--r--chromium/components/strings/components_strings_ro.xtb55
-rw-r--r--chromium/components/strings/components_strings_ru.xtb57
-rw-r--r--chromium/components/strings/components_strings_si.xtb55
-rw-r--r--chromium/components/strings/components_strings_sk.xtb57
-rw-r--r--chromium/components/strings/components_strings_sl.xtb55
-rw-r--r--chromium/components/strings/components_strings_sq.xtb59
-rw-r--r--chromium/components/strings/components_strings_sr-Latn.xtb57
-rw-r--r--chromium/components/strings/components_strings_sr.xtb57
-rw-r--r--chromium/components/strings/components_strings_sv.xtb57
-rw-r--r--chromium/components/strings/components_strings_sw.xtb55
-rw-r--r--chromium/components/strings/components_strings_ta.xtb55
-rw-r--r--chromium/components/strings/components_strings_te.xtb77
-rw-r--r--chromium/components/strings/components_strings_th.xtb57
-rw-r--r--chromium/components/strings/components_strings_tr.xtb55
-rw-r--r--chromium/components/strings/components_strings_uk.xtb55
-rw-r--r--chromium/components/strings/components_strings_ur.xtb57
-rw-r--r--chromium/components/strings/components_strings_uz.xtb63
-rw-r--r--chromium/components/strings/components_strings_vi.xtb59
-rw-r--r--chromium/components/strings/components_strings_zh-CN.xtb55
-rw-r--r--chromium/components/strings/components_strings_zh-HK.xtb55
-rw-r--r--chromium/components/strings/components_strings_zh-TW.xtb55
-rw-r--r--chromium/components/strings/components_strings_zu.xtb55
-rw-r--r--chromium/components/subresource_filter/android/BUILD.gn2
-rw-r--r--chromium/components/subresource_filter/content/browser/ad_tagging_browser_test_utils.cc4
-rw-r--r--chromium/components/subresource_filter/content/browser/ads_blocked_message_delegate.cc6
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc18
-rw-r--r--chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.cc3
-rw-r--r--chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h1
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc2
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.cc1
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features.cc15
-rw-r--r--chromium/components/sync/BUILD.gn3
-rw-r--r--chromium/components/sync/android/BUILD.gn5
-rw-r--r--chromium/components/sync/base/BUILD.gn2
-rw-r--r--chromium/components/sync/driver/resources/BUILD.gn5
-rw-r--r--chromium/components/sync/engine/BUILD.gn2
-rw-r--r--chromium/components/sync/protocol/protocol_sources.gni3
-rw-r--r--chromium/components/sync_bookmarks/BUILD.gn1
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_merger.cc2
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_merger_unittest.cc23
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_observer_impl.cc27
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_observer_impl.h3
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_type_processor.cc11
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc215
-rw-r--r--chromium/components/sync_bookmarks/bookmark_remote_updates_handler.cc10
-rw-r--r--chromium/components/sync_bookmarks/bookmark_sync_service.h2
-rw-r--r--chromium/components/sync_bookmarks/parent_guid_preprocessing.cc3
-rw-r--r--chromium/components/sync_bookmarks/synced_bookmark_tracker.cc32
-rw-r--r--chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc31
-rw-r--r--chromium/components/sync_device_info/device_info.h1
-rw-r--r--chromium/components/sync_device_info/device_info_sync_bridge.cc7
-rw-r--r--chromium/components/sync_device_info/device_info_sync_bridge_unittest.cc12
-rw-r--r--chromium/components/sync_device_info/device_info_sync_client.h3
-rw-r--r--chromium/components/sync_device_info/device_info_sync_service.h2
-rw-r--r--chromium/components/sync_device_info/device_info_sync_service_impl.cc2
-rw-r--r--chromium/components/sync_device_info/device_info_sync_service_impl.h1
-rw-r--r--chromium/components/sync_device_info/fake_device_info_sync_service.h1
-rw-r--r--chromium/components/sync_device_info/fake_device_info_tracker.cc18
-rw-r--r--chromium/components/sync_device_info/fake_local_device_info_provider.cc3
-rw-r--r--chromium/components/sync_device_info/local_device_info_provider_impl_unittest.cc2
-rw-r--r--chromium/components/sync_device_info/local_device_info_util_linux.cc3
-rw-r--r--chromium/components/sync_preferences/pref_model_associator.cc73
-rw-r--r--chromium/components/sync_preferences/pref_model_associator.h2
-rw-r--r--chromium/components/sync_preferences/pref_model_associator_unittest.cc10
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable.cc6
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable_unittest.cc12
-rw-r--r--chromium/components/sync_sessions/BUILD.gn1
-rw-r--r--chromium/components/sync_sessions/DEPS1
-rw-r--r--chromium/components/sync_sessions/local_session_event_handler_impl.cc9
-rw-r--r--chromium/components/sync_sessions/local_session_event_handler_impl_unittest.cc4
-rw-r--r--chromium/components/sync_sessions/open_tabs_ui_delegate_impl.cc9
-rw-r--r--chromium/components/sync_sessions/proxy_tabs_data_type_controller.cc14
-rw-r--r--chromium/components/sync_sessions/proxy_tabs_data_type_controller.h17
-rw-r--r--chromium/components/sync_sessions/session_model_type_controller.cc24
-rw-r--r--chromium/components/sync_sessions/session_model_type_controller.h11
-rw-r--r--chromium/components/sync_sessions/session_store_unittest.cc1
-rw-r--r--chromium/components/sync_sessions/session_sync_bridge.cc2
-rw-r--r--chromium/components/sync_sessions/session_sync_bridge_unittest.cc4
-rw-r--r--chromium/components/sync_sessions/session_sync_test_helper.cc6
-rw-r--r--chromium/components/sync_sessions/sessions_global_id_mapper_unittest.cc2
-rw-r--r--chromium/components/sync_sessions/synced_session.cc132
-rw-r--r--chromium/components/sync_sessions/synced_session_tracker.cc51
-rw-r--r--chromium/components/sync_sessions/synced_session_unittest.cc3
-rw-r--r--chromium/components/sync_sessions/tab_node_pool_unittest.cc1
-rw-r--r--chromium/components/sync_sessions/test_synced_window_delegates_getter.cc33
-rw-r--r--chromium/components/sync_user_events/user_event_service_impl_unittest.cc1
-rw-r--r--chromium/components/sync_user_events/user_event_sync_bridge_unittest.cc1
-rw-r--r--chromium/components/system_media_controls/mac/remote_command_center_delegate.h2
-rw-r--r--chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.h1
-rw-r--r--chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.mm1
-rw-r--r--chromium/components/thin_webview/BUILD.gn2
-rw-r--r--chromium/components/thin_webview/internal/BUILD.gn2
-rw-r--r--chromium/components/thin_webview/internal/compositor_view_impl.cc4
-rw-r--r--chromium/components/tracing/BUILD.gn33
-rw-r--r--chromium/components/tracing/DEPS6
-rw-r--r--chromium/components/tracing/common/background_tracing_metrics_provider.cc11
-rw-r--r--chromium/components/tracing/common/background_tracing_state_manager.cc219
-rw-r--r--chromium/components/tracing/common/background_tracing_state_manager.h116
-rw-r--r--chromium/components/tracing/common/background_tracing_state_manager_unittest.cc205
-rw-r--r--chromium/components/tracing/common/background_tracing_utils.cc183
-rw-r--r--chromium/components/tracing/common/background_tracing_utils.h67
-rw-r--r--chromium/components/tracing/common/background_tracing_utils_unittest.cc230
-rw-r--r--chromium/components/tracing/common/pref_names.cc17
-rw-r--r--chromium/components/tracing/common/pref_names.h22
-rw-r--r--chromium/components/translate/content/android/BUILD.gn5
-rw-r--r--chromium/components/translate/core/browser/BUILD.gn3
-rw-r--r--chromium/components/translate/core/language_detection/BUILD.gn1
-rw-r--r--chromium/components/translate/ios/browser/BUILD.gn15
-rw-r--r--chromium/components/translate_strings.grdp23
-rw-r--r--chromium/components/translate_strings_grdp/IDS_TRANSLATE_MESSAGE_DESCRIPTION.png.sha12
-rw-r--r--chromium/components/translate_strings_grdp/IDS_TRANSLATE_MESSAGE_UNDO_BUTTON.png.sha11
-rw-r--r--chromium/components/ui_devtools/views/overlay_agent_unittest.cc2
-rw-r--r--chromium/components/ui_devtools/views/view_element_unittest.cc6
-rw-r--r--chromium/components/ukm/android/BUILD.gn3
-rw-r--r--chromium/components/ukm/android/ukm_recorder.cc5
-rw-r--r--chromium/components/ukm/content/source_url_recorder.cc8
-rw-r--r--chromium/components/ukm/content/source_url_recorder.h5
-rw-r--r--chromium/components/ukm/content/source_url_recorder_browsertest.cc8
-rw-r--r--chromium/components/ukm/content/source_url_recorder_test.cc2
-rw-r--r--chromium/components/ukm/debug/ukm_internals.js2
-rw-r--r--chromium/components/ukm/observers/ukm_consent_state_observer_unittest.cc6
-rw-r--r--chromium/components/ukm/test_ukm_recorder.cc7
-rw-r--r--chromium/components/ukm/test_ukm_recorder.h4
-rw-r--r--chromium/components/ukm/ukm_recorder_impl.cc18
-rw-r--r--chromium/components/undo/undo_manager.cc10
-rw-r--r--chromium/components/undo/undo_manager.h2
-rw-r--r--chromium/components/update_client/background_downloader_win.cc6
-rw-r--r--chromium/components/update_client/configurator.h3
-rw-r--r--chromium/components/update_client/protocol_serializer.cc1
-rw-r--r--chromium/components/update_client/request_sender.cc6
-rw-r--r--chromium/components/update_client/request_sender_unittest.cc1
-rw-r--r--chromium/components/update_client/test_configurator.cc10
-rw-r--r--chromium/components/update_client/test_configurator.h3
-rw-r--r--chromium/components/update_client/update_checker.cc110
-rw-r--r--chromium/components/update_client/update_checker.h13
-rw-r--r--chromium/components/update_client/update_checker_unittest.cc179
-rw-r--r--chromium/components/update_client/update_client_unittest.cc261
-rw-r--r--chromium/components/update_client/update_engine.cc4
-rw-r--r--chromium/components/update_client/update_query_params.cc6
-rw-r--r--chromium/components/url_formatter/android/BUILD.gn3
-rw-r--r--chromium/components/url_formatter/elide_url.cc94
-rw-r--r--chromium/components/url_formatter/elide_url.h29
-rw-r--r--chromium/components/url_formatter/elide_url_unittest.cc25
-rw-r--r--chromium/components/url_formatter/spoof_checks/idn_spoof_checker.cc9
-rw-r--r--chromium/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc80
-rw-r--r--chromium/components/url_formatter/spoof_checks/skeleton_generator.cc64
-rw-r--r--chromium/components/url_formatter/spoof_checks/skeleton_generator.h8
-rw-r--r--chromium/components/url_formatter/url_fixer.cc19
-rw-r--r--chromium/components/url_formatter/url_formatter.cc104
-rw-r--r--chromium/components/url_formatter/url_formatter.h18
-rw-r--r--chromium/components/url_formatter/url_formatter_android.cc12
-rw-r--r--chromium/components/url_formatter/url_formatter_unittest.cc269
-rw-r--r--chromium/components/url_matcher/BUILD.gn6
-rw-r--r--chromium/components/url_matcher/regex_set_matcher.cc23
-rw-r--r--chromium/components/url_matcher/regex_set_matcher.h21
-rw-r--r--chromium/components/url_matcher/regex_set_matcher_unittest.cc27
-rw-r--r--chromium/components/url_matcher/string_pattern.cc28
-rw-r--r--chromium/components/url_matcher/string_pattern.h48
-rw-r--r--chromium/components/url_matcher/string_pattern_unittest.cc25
-rw-r--r--chromium/components/url_matcher/substring_set_matcher.cc316
-rw-r--r--chromium/components/url_matcher/substring_set_matcher.h190
-rw-r--r--chromium/components/url_matcher/substring_set_matcher_perftest.cc85
-rw-r--r--chromium/components/url_matcher/substring_set_matcher_unittest.cc180
-rw-r--r--chromium/components/url_matcher/url_matcher.cc206
-rw-r--r--chromium/components/url_matcher/url_matcher.h110
-rw-r--r--chromium/components/url_matcher/url_matcher_factory.cc11
-rw-r--r--chromium/components/url_matcher/url_matcher_factory.h10
-rw-r--r--chromium/components/url_matcher/url_matcher_factory_unittest.cc82
-rw-r--r--chromium/components/url_matcher/url_matcher_unittest.cc568
-rw-r--r--chromium/components/url_matcher/url_util.cc18
-rw-r--r--chromium/components/url_matcher/url_util.h10
-rw-r--r--chromium/components/url_param_filter/DEPS9
-rw-r--r--chromium/components/url_param_filter/OWNERS1
-rw-r--r--chromium/components/url_param_filter/README.md11
-rw-r--r--chromium/components/url_param_filter/content/BUILD.gn45
-rw-r--r--chromium/components/url_param_filter/content/DEPS9
-rw-r--r--chromium/components/url_param_filter/content/cross_otr_observer.cc202
-rw-r--r--chromium/components/url_param_filter/content/cross_otr_observer.h97
-rw-r--r--chromium/components/url_param_filter/content/cross_otr_observer_unittest.cc766
-rw-r--r--chromium/components/url_param_filter/content/url_param_filter_throttle.cc125
-rw-r--r--chromium/components/url_param_filter/content/url_param_filter_throttle.h55
-rw-r--r--chromium/components/url_param_filter/content/url_param_filter_throttle_unittest.cc369
-rw-r--r--chromium/components/url_param_filter/core/BUILD.gn62
-rw-r--r--chromium/components/url_param_filter/core/DEPS4
-rw-r--r--chromium/components/url_param_filter/core/features.cc14
-rw-r--r--chromium/components/url_param_filter/core/features.h16
-rw-r--r--chromium/components/url_param_filter/core/url_param_classifications_loader.cc204
-rw-r--r--chromium/components/url_param_filter/core/url_param_classifications_loader.h76
-rw-r--r--chromium/components/url_param_filter/core/url_param_classifications_loader_unittest.cc829
-rw-r--r--chromium/components/url_param_filter/core/url_param_filter_classification.proto35
-rw-r--r--chromium/components/url_param_filter/core/url_param_filter_test_helper.cc248
-rw-r--r--chromium/components/url_param_filter/core/url_param_filter_test_helper.h119
-rw-r--r--chromium/components/url_param_filter/core/url_param_filterer.cc181
-rw-r--r--chromium/components/url_param_filter/core/url_param_filterer.h51
-rw-r--r--chromium/components/url_param_filter/core/url_param_filterer_unittest.cc738
-rw-r--r--chromium/components/url_param_filter/ios/BUILD.gn37
-rw-r--r--chromium/components/url_param_filter/ios/DEPS5
-rw-r--r--chromium/components/url_param_filter/ios/cross_otr_tab_helper.h74
-rw-r--r--chromium/components/url_param_filter/ios/cross_otr_tab_helper.mm126
-rw-r--r--chromium/components/url_param_filter/ios/cross_otr_tab_helper_unittest.mm270
-rw-r--r--chromium/components/url_pattern_index/fuzzy_pattern_matching.cc2
-rw-r--r--chromium/components/url_pattern_index/ngram_extractor.h8
-rw-r--r--chromium/components/url_pattern_index/string_splitter.h9
-rw-r--r--chromium/components/url_pattern_index/url_pattern_index.cc7
-rw-r--r--chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager.cc2
-rw-r--r--chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager_browsertest.cc3
-rw-r--r--chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation.cc12
-rw-r--r--chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation_unittest.cc4
-rw-r--r--chromium/components/url_rewrite/common/url_loader_throttle.cc14
-rw-r--r--chromium/components/user_education/DEPS10
-rw-r--r--chromium/components/user_education/DIR_METADATA3
-rw-r--r--chromium/components/user_education/OWNERS6
-rw-r--r--chromium/components/user_education/README.md0
-rw-r--r--chromium/components/user_education/common/BUILD.gn78
-rw-r--r--chromium/components/user_education/common/feature_promo_controller.cc551
-rw-r--r--chromium/components/user_education/common/feature_promo_controller.h395
-rw-r--r--chromium/components/user_education/common/feature_promo_registry.cc40
-rw-r--r--chromium/components/user_education/common/feature_promo_registry.h59
-rw-r--r--chromium/components/user_education/common/feature_promo_snooze_service.cc143
-rw-r--r--chromium/components/user_education/common/feature_promo_snooze_service.h100
-rw-r--r--chromium/components/user_education/common/feature_promo_snooze_service_unittest.cc120
-rw-r--r--chromium/components/user_education/common/feature_promo_specification.cc182
-rw-r--r--chromium/components/user_education/common/feature_promo_specification.h231
-rw-r--r--chromium/components/user_education/common/help_bubble.cc68
-rw-r--r--chromium/components/user_education/common/help_bubble.h76
-rw-r--r--chromium/components/user_education/common/help_bubble_factory.h43
-rw-r--r--chromium/components/user_education/common/help_bubble_factory_registry.cc90
-rw-r--r--chromium/components/user_education/common/help_bubble_factory_registry.h85
-rw-r--r--chromium/components/user_education/common/help_bubble_factory_registry_unittest.cc249
-rw-r--r--chromium/components/user_education/common/help_bubble_params.cc21
-rw-r--r--chromium/components/user_education/common/help_bubble_params.h99
-rw-r--r--chromium/components/user_education/common/scoped_new_badge_tracker_base.cc58
-rw-r--r--chromium/components/user_education/common/scoped_new_badge_tracker_base.h134
-rw-r--r--chromium/components/user_education/common/tutorial.cc361
-rw-r--r--chromium/components/user_education/common/tutorial.h154
-rw-r--r--chromium/components/user_education/common/tutorial_description.cc60
-rw-r--r--chromium/components/user_education/common/tutorial_description.h197
-rw-r--r--chromium/components/user_education/common/tutorial_identifier.h15
-rw-r--r--chromium/components/user_education/common/tutorial_registry.cc52
-rw-r--r--chromium/components/user_education/common/tutorial_registry.h51
-rw-r--r--chromium/components/user_education/common/tutorial_service.cc193
-rw-r--r--chromium/components/user_education/common/tutorial_service.h143
-rw-r--r--chromium/components/user_education/common/tutorial_unittest.cc365
-rw-r--r--chromium/components/user_education/common/user_education_class_properties.cc11
-rw-r--r--chromium/components/user_education/common/user_education_class_properties.h22
-rw-r--r--chromium/components/user_education/test/BUILD.gn28
-rw-r--r--chromium/components/user_education/views/BUILD.gn72
-rw-r--r--chromium/components/user_education/views/DEPS4
-rw-r--r--chromium/components/user_education/views/help_bubble_delegate.h53
-rw-r--r--chromium/components/user_education/views/help_bubble_factory_mac.h35
-rw-r--r--chromium/components/user_education/views/help_bubble_factory_mac.mm49
-rw-r--r--chromium/components/user_education/views/help_bubble_factory_views.cc180
-rw-r--r--chromium/components/user_education/views/help_bubble_factory_views.h96
-rw-r--r--chromium/components/user_education/views/help_bubble_view.cc729
-rw-r--r--chromium/components/user_education/views/help_bubble_view.h118
-rw-r--r--chromium/components/user_education/views/help_bubble_view_unittest.cc211
-rw-r--r--chromium/components/user_education/views/new_badge_label.cc180
-rw-r--r--chromium/components/user_education/views/new_badge_label.h98
-rw-r--r--chromium/components/user_education/views/new_badge_label_unittest.cc143
-rw-r--r--chromium/components/user_education_strings.grdp39
-rw-r--r--chromium/components/user_education_strings_grdp/IDS_PROMO_DISMISS_BUTTON.png.sha11
-rw-r--r--chromium/components/user_education_strings_grdp/IDS_PROMO_SHOW_TUTORIAL_BUTTON.png.sha11
-rw-r--r--chromium/components/user_education_strings_grdp/IDS_PROMO_SNOOZE_BUTTON.png.sha11
-rw-r--r--chromium/components/user_education_strings_grdp/IDS_TUTORIAL_CLOSE_TUTORIAL.png.sha11
-rw-r--r--chromium/components/user_education_strings_grdp/IDS_TUTORIAL_RESTART_TUTORIAL.png.sha11
-rw-r--r--chromium/components/user_manager/user_manager.cc4
-rw-r--r--chromium/components/user_manager/user_manager.h11
-rw-r--r--chromium/components/user_manager/user_manager_base.cc20
-rw-r--r--chromium/components/user_manager/user_manager_base.h4
-rw-r--r--chromium/components/user_notes/README.md3
-rw-r--r--chromium/components/user_notes/browser/BUILD.gn21
-rw-r--r--chromium/components/user_notes/browser/DEPS5
-rw-r--r--chromium/components/user_notes/browser/frame_user_note_changes.cc85
-rw-r--r--chromium/components/user_notes/browser/frame_user_note_changes.h70
-rw-r--r--chromium/components/user_notes/browser/frame_user_note_changes_unittest.cc179
-rw-r--r--chromium/components/user_notes/browser/user_note_base_test.cc136
-rw-r--r--chromium/components/user_notes/browser/user_note_base_test.h65
-rw-r--r--chromium/components/user_notes/browser/user_note_instance.cc68
-rw-r--r--chromium/components/user_notes/browser/user_note_instance.h71
-rw-r--r--chromium/components/user_notes/browser/user_note_instance_unittest.cc131
-rw-r--r--chromium/components/user_notes/browser/user_note_manager.cc91
-rw-r--r--chromium/components/user_notes/browser/user_note_manager.h95
-rw-r--r--chromium/components/user_notes/browser/user_note_manager_unittest.cc207
-rw-r--r--chromium/components/user_notes/browser/user_note_service.cc158
-rw-r--r--chromium/components/user_notes/browser/user_note_service.h66
-rw-r--r--chromium/components/user_notes/browser/user_note_service_unittest.cc253
-rw-r--r--chromium/components/user_notes/browser/user_note_utils.cc98
-rw-r--r--chromium/components/user_notes/browser/user_note_utils.h33
-rw-r--r--chromium/components/user_notes/browser/user_note_utils_unittest.cc755
-rw-r--r--chromium/components/user_notes/browser/user_notes_change.cc36
-rw-r--r--chromium/components/user_notes/browser/user_notes_change.h53
-rw-r--r--chromium/components/user_notes/browser/user_notes_manager.cc72
-rw-r--r--chromium/components/user_notes/browser/user_notes_manager.h81
-rw-r--r--chromium/components/user_notes/browser/user_notes_manager_unittest.cc312
-rw-r--r--chromium/components/user_notes/interfaces/BUILD.gn2
-rw-r--r--chromium/components/user_notes/interfaces/user_note_metadata_snapshot.cc44
-rw-r--r--chromium/components/user_notes/interfaces/user_note_metadata_snapshot.h68
-rw-r--r--chromium/components/user_notes/interfaces/user_note_service_delegate.h23
-rw-r--r--chromium/components/user_notes/interfaces/user_note_storage.h31
-rw-r--r--chromium/components/user_notes/interfaces/user_notes_ui.h4
-rw-r--r--chromium/components/user_notes/interfaces/user_notes_ui_delegate.h7
-rw-r--r--chromium/components/user_notes/model/user_note.cc2
-rw-r--r--chromium/components/user_notes/model/user_note.h3
-rw-r--r--chromium/components/user_notes/model/user_note_model_test_utils.cc8
-rw-r--r--chromium/components/user_notes/model/user_note_model_test_utils.h5
-rw-r--r--chromium/components/user_notes/model/user_note_target.h2
-rw-r--r--chromium/components/user_notes/storage/BUILD.gn15
-rw-r--r--chromium/components/user_notes/storage/user_note_database.cc339
-rw-r--r--chromium/components/user_notes/storage/user_note_database.h38
-rw-r--r--chromium/components/user_notes/storage/user_note_database_unittest.cc197
-rw-r--r--chromium/components/user_notes/storage/user_note_storage_impl.cc18
-rw-r--r--chromium/components/user_notes/storage/user_note_storage_impl.h14
-rw-r--r--chromium/components/user_prefs/android/BUILD.gn3
-rw-r--r--chromium/components/variations/BUILD.gn13
-rw-r--r--chromium/components/variations/active_field_trials.cc26
-rw-r--r--chromium/components/variations/active_field_trials.h8
-rw-r--r--chromium/components/variations/android/BUILD.gn2
-rw-r--r--chromium/components/variations/field_trial_config/field_trial_util.cc4
-rw-r--r--chromium/components/variations/net/variations_command_line.cc2
-rw-r--r--chromium/components/variations/processed_study.cc18
-rw-r--r--chromium/components/variations/processed_study.h17
-rw-r--r--chromium/components/variations/processed_study_unittest.cc241
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator.cc10
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator_unittest.cc12
-rw-r--r--chromium/components/variations/synthetic_trial_registry.cc36
-rw-r--r--chromium/components/variations/synthetic_trial_registry.h8
-rw-r--r--chromium/components/variations/synthetic_trial_registry_unittest.cc46
-rw-r--r--chromium/components/variations/synthetic_trials.cc23
-rw-r--r--chromium/components/variations/synthetic_trials.h51
-rw-r--r--chromium/components/variations/synthetic_trials_active_group_id_provider.cc2
-rw-r--r--chromium/components/variations/variations_associated_data.cc1
-rw-r--r--chromium/components/variations/variations_crash_keys.cc2
-rw-r--r--chromium/components/variations/variations_crash_keys.h2
-rw-r--r--chromium/components/variations/variations_crash_keys_unittest.cc9
-rw-r--r--chromium/components/variations/variations_ids_provider.cc14
-rw-r--r--chromium/components/variations/variations_ids_provider.h9
-rw-r--r--chromium/components/vector_icons/BUILD.gn1
-rw-r--r--chromium/components/vector_icons/account_circle.icon71
-rw-r--r--chromium/components/version_info/OWNERS1
-rw-r--r--chromium/components/version_info/android/BUILD.gn3
-rw-r--r--chromium/components/version_info/version_info.cc4
-rw-r--r--chromium/components/version_ui/PRESUBMIT.py27
-rw-r--r--chromium/components/version_ui_strings.grdp2
-rw-r--r--chromium/components/version_ui_strings_grdp/IDS_VERSION_UI_VARIATIONS.png.sha11
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_writer.cc3
-rw-r--r--chromium/components/visitedlink/common/visitedlink_common.cc2
-rw-r--r--chromium/components/viz/BUILD.gn13
-rw-r--r--chromium/components/viz/OWNERS2
-rw-r--r--chromium/components/viz/README.md13
-rw-r--r--chromium/components/viz/common/BUILD.gn36
-rw-r--r--chromium/components/viz/common/DEPS2
-rw-r--r--chromium/components/viz/common/display/renderer_settings.h1
-rw-r--r--chromium/components/viz/common/features.cc40
-rw-r--r--chromium/components/viz/common/features.h7
-rw-r--r--chromium/components/viz/common/frame_sinks/README.md4
-rw-r--r--chromium/components/viz/common/frame_sinks/blit_request.cc13
-rw-r--r--chromium/components/viz/common/frame_sinks/blit_request.h30
-rw-r--r--chromium/components/viz/common/frame_sinks/copy_output_request.cc1
-rw-r--r--chromium/components/viz/common/frame_sinks/copy_output_request.h14
-rw-r--r--chromium/components/viz/common/gl_i420_converter.cc209
-rw-r--r--chromium/components/viz/common/gl_i420_converter.h195
-rw-r--r--chromium/components/viz/common/gl_i420_converter_pixeltest.cc129
-rw-r--r--chromium/components/viz/common/gl_i420_converter_unittest.cc77
-rw-r--r--chromium/components/viz/common/gl_nv12_converter.cc195
-rw-r--r--chromium/components/viz/common/gl_nv12_converter.h200
-rw-r--r--chromium/components/viz/common/gl_nv12_converter_pixeltest.cc154
-rw-r--r--chromium/components/viz/common/gl_scaler.cc1661
-rw-r--r--chromium/components/viz/common/gl_scaler.h538
-rw-r--r--chromium/components/viz/common/gl_scaler_overscan_pixeltest.cc532
-rw-r--r--chromium/components/viz/common/gl_scaler_pixeltest.cc628
-rw-r--r--chromium/components/viz/common/gl_scaler_shader_pixeltest.cc785
-rw-r--r--chromium/components/viz/common/gl_scaler_unittest.cc236
-rw-r--r--chromium/components/viz/common/gpu/context_cache_controller.cc1
-rw-r--r--chromium/components/viz/common/overlay_state/win/DEPS8
-rw-r--r--chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.cc28
-rw-r--r--chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.h29
-rw-r--r--chromium/components/viz/common/overlay_state/win/overlay_state_service.cc184
-rw-r--r--chromium/components/viz/common/overlay_state/win/overlay_state_service.h83
-rw-r--r--chromium/components/viz/common/overlay_state/win/overlay_state_service_unittest.cc195
-rw-r--r--chromium/components/viz/common/quads/compositor_render_pass_unittest.cc2
-rw-r--r--chromium/components/viz/common/quads/debug_border_draw_quad.cc9
-rw-r--r--chromium/components/viz/common/quads/debug_border_draw_quad.h2
-rw-r--r--chromium/components/viz/common/quads/draw_quad_unittest.cc9
-rw-r--r--chromium/components/viz/common/quads/render_pass_io.cc95
-rw-r--r--chromium/components/viz/common/quads/shared_quad_state.cc12
-rw-r--r--chromium/components/viz/common/quads/solid_color_draw_quad.cc8
-rw-r--r--chromium/components/viz/common/quads/solid_color_draw_quad.h4
-rw-r--r--chromium/components/viz/common/quads/surface_draw_quad.cc6
-rw-r--r--chromium/components/viz/common/quads/surface_draw_quad.h2
-rw-r--r--chromium/components/viz/common/quads/texture_draw_quad.cc6
-rw-r--r--chromium/components/viz/common/quads/texture_draw_quad.h2
-rw-r--r--chromium/components/viz/common/resources/resource_format_utils.cc38
-rw-r--r--chromium/components/viz/common/resources/resource_format_utils.h2
-rw-r--r--chromium/components/viz/common/resources/transferable_resource.h26
-rw-r--r--chromium/components/viz/common/surfaces/local_surface_id_unittest.cc2
-rw-r--r--chromium/components/viz/common/switches.cc18
-rw-r--r--chromium/components/viz/common/switches.h2
-rw-r--r--chromium/components/viz/common/transition_utils.cc23
-rw-r--r--chromium/components/viz/common/yuv_readback_unittest.cc14
-rw-r--r--chromium/components/viz/demo/demo_main.cc2
-rw-r--r--chromium/components/viz/demo/service/demo_service.cc2
-rw-r--r--chromium/components/viz/host/client_frame_sink_video_capturer.h4
-rw-r--r--chromium/components/viz/host/gpu_host_impl.cc11
-rw-r--r--chromium/components/viz/host/gpu_host_impl.h5
-rw-r--r--chromium/components/viz/host/hit_test/hit_test_query.cc31
-rw-r--r--chromium/components/viz/host/hit_test/hit_test_query.h3
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager.cc9
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager.h2
-rw-r--r--chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc4
-rw-r--r--chromium/components/viz/host/renderer_settings_creation.cc1
-rw-r--r--chromium/components/viz/service/BUILD.gn220
-rw-r--r--chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc3
-rw-r--r--chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h3
-rw-r--r--chromium/components/viz/service/display/DEPS2
-rw-r--r--chromium/components/viz/service/display/ca_layer_overlay.cc19
-rw-r--r--chromium/components/viz/service/display/ca_layer_overlay.h11
-rw-r--r--chromium/components/viz/service/display/dc_layer_overlay.cc45
-rw-r--r--chromium/components/viz/service/display/dc_layer_overlay.h5
-rw-r--r--chromium/components/viz/service/display/direct_renderer.cc89
-rw-r--r--chromium/components/viz/service/display/direct_renderer.h45
-rw-r--r--chromium/components/viz/service/display/display.cc74
-rw-r--r--chromium/components/viz/service/display/display.h3
-rw-r--r--chromium/components/viz/service/display/display_damage_tracker.cc5
-rw-r--r--chromium/components/viz/service/display/display_damage_tracker.h1
-rw-r--r--chromium/components/viz/service/display/display_perftest.cc6
-rw-r--r--chromium/components/viz/service/display/display_resource_provider.cc10
-rw-r--r--chromium/components/viz/service/display/display_resource_provider.h34
-rw-r--r--chromium/components/viz/service/display/display_resource_provider_gl.cc425
-rw-r--r--chromium/components/viz/service/display/display_resource_provider_gl.h165
-rw-r--r--chromium/components/viz/service/display/display_resource_provider_gl_unittest.cc718
-rw-r--r--chromium/components/viz/service/display/display_resource_provider_skia.cc31
-rw-r--r--chromium/components/viz/service/display/display_resource_provider_skia.h9
-rw-r--r--chromium/components/viz/service/display/display_resource_provider_skia_unittest.cc283
-rw-r--r--chromium/components/viz/service/display/display_scheduler.cc25
-rw-r--r--chromium/components/viz/service/display/display_scheduler_unittest.cc4
-rw-r--r--chromium/components/viz/service/display/display_unittest.cc879
-rw-r--r--chromium/components/viz/service/display/dynamic_geometry_binding.cc68
-rw-r--r--chromium/components/viz/service/display/dynamic_geometry_binding.h40
-rw-r--r--chromium/components/viz/service/display/external_use_client.h8
-rw-r--r--chromium/components/viz/service/display/frame_rate_decider_unittest.cc4
-rw-r--r--chromium/components/viz/service/display/geometry_binding.cc75
-rw-r--r--chromium/components/viz/service/display/geometry_binding.h64
-rw-r--r--chromium/components/viz/service/display/gl_renderer.cc4514
-rw-r--r--chromium/components/viz/service/display/gl_renderer.h525
-rw-r--r--chromium/components/viz/service/display/gl_renderer_copier.cc1298
-rw-r--r--chromium/components/viz/service/display/gl_renderer_copier.h485
-rw-r--r--chromium/components/viz/service/display/gl_renderer_copier_perftest.cc338
-rw-r--r--chromium/components/viz/service/display/gl_renderer_copier_pixeltest.cc958
-rw-r--r--chromium/components/viz/service/display/gl_renderer_copier_unittest.cc224
-rw-r--r--chromium/components/viz/service/display/gl_renderer_draw_cache.cc13
-rw-r--r--chromium/components/viz/service/display/gl_renderer_draw_cache.h62
-rw-r--r--chromium/components/viz/service/display/gl_renderer_unittest.cc5195
-rw-r--r--chromium/components/viz/service/display/layer_quad.cc121
-rw-r--r--chromium/components/viz/service/display/layer_quad.h110
-rw-r--r--chromium/components/viz/service/display/layer_quad_unittest.cc69
-rw-r--r--chromium/components/viz/service/display/null_renderer.h1
-rw-r--r--chromium/components/viz/service/display/output_surface.cc9
-rw-r--r--chromium/components/viz/service/display/output_surface.h74
-rw-r--r--chromium/components/viz/service/display/output_surface_client.h6
-rw-r--r--chromium/components/viz/service/display/overlay_ca_unittest.cc73
-rw-r--r--chromium/components/viz/service/display/overlay_candidate.cc470
-rw-r--r--chromium/components/viz/service/display/overlay_candidate.h201
-rw-r--r--chromium/components/viz/service/display/overlay_dc_unittest.cc87
-rw-r--r--chromium/components/viz/service/display/overlay_processor_delegated.cc18
-rw-r--r--chromium/components/viz/service/display/overlay_processor_interface.h7
-rw-r--r--chromium/components/viz/service/display/overlay_processor_ozone.cc61
-rw-r--r--chromium/components/viz/service/display/overlay_processor_ozone.h2
-rw-r--r--chromium/components/viz/service/display/overlay_processor_ozone_unittest.cc35
-rw-r--r--chromium/components/viz/service/display/overlay_processor_using_strategy.cc47
-rw-r--r--chromium/components/viz/service/display/overlay_processor_using_strategy.h2
-rw-r--r--chromium/components/viz/service/display/overlay_strategy_fullscreen.cc18
-rw-r--r--chromium/components/viz/service/display/overlay_strategy_single_on_top.cc20
-rw-r--r--chromium/components/viz/service/display/overlay_strategy_underlay.cc27
-rw-r--r--chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc52
-rw-r--r--chromium/components/viz/service/display/overlay_unittest.cc262
-rw-r--r--chromium/components/viz/service/display/program_binding.cc301
-rw-r--r--chromium/components/viz/service/display/program_binding.h480
-rw-r--r--chromium/components/viz/service/display/renderer_perftest.cc117
-rw-r--r--chromium/components/viz/service/display/renderer_pixeltest.cc381
-rw-r--r--chromium/components/viz/service/display/resource_fence.h10
-rw-r--r--chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc96
-rw-r--r--chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.h49
-rw-r--r--chromium/components/viz/service/display/scoped_render_pass_texture.cc127
-rw-r--r--chromium/components/viz/service/display/scoped_render_pass_texture.h61
-rw-r--r--chromium/components/viz/service/display/shader.cc1168
-rw-r--r--chromium/components/viz/service/display/shader.h324
-rw-r--r--chromium/components/viz/service/display/shader_unittest.cc58
-rw-r--r--chromium/components/viz/service/display/skia_output_surface.h31
-rw-r--r--chromium/components/viz/service/display/skia_readback_pixeltest.cc50
-rw-r--r--chromium/components/viz/service/display/skia_renderer.cc409
-rw-r--r--chromium/components/viz/service/display/skia_renderer.h119
-rw-r--r--chromium/components/viz/service/display/software_renderer.cc32
-rw-r--r--chromium/components/viz/service/display/software_renderer_unittest.cc79
-rw-r--r--chromium/components/viz/service/display/static_geometry_binding.cc74
-rw-r--r--chromium/components/viz/service/display/static_geometry_binding.h41
-rw-r--r--chromium/components/viz/service/display/surface_aggregator.cc141
-rw-r--r--chromium/components/viz/service/display/surface_aggregator.h28
-rw-r--r--chromium/components/viz/service/display/surface_aggregator_pixeltest.cc4
-rw-r--r--chromium/components/viz/service/display/surface_aggregator_unittest.cc9
-rw-r--r--chromium/components/viz/service/display/sync_query_collection.cc151
-rw-r--r--chromium/components/viz/service/display/sync_query_collection.h42
-rw-r--r--chromium/components/viz/service/display/texture_deleter.cc87
-rw-r--r--chromium/components/viz/service/display/texture_deleter.h64
-rw-r--r--chromium/components/viz/service/display/texture_deleter_unittest.cc84
-rw-r--r--chromium/components/viz/service/display/viz_pixel_test.cc3
-rw-r--r--chromium/components/viz/service/display/viz_pixel_test.h4
-rw-r--r--chromium/components/viz/service/display_embedder/DEPS6
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_gpu_thread.cc30
-rw-r--r--chromium/components/viz/service/display_embedder/compositor_gpu_thread.h12
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface.cc265
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface.h114
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_android.cc26
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_android.h32
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc314
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h111
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_unittest.cc359
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.cc25
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.h33
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.cc139
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.h57
-rw-r--r--chromium/components/viz/service/display_embedder/image_context_impl.h4
-rw-r--r--chromium/components/viz/service/display_embedder/output_presenter.cc10
-rw-r--r--chromium/components/viz/service/display_embedder/output_presenter_gl.cc35
-rw-r--r--chromium/components/viz/service/display_embedder/output_surface_provider.h3
-rw-r--r--chromium/components/viz/service/display_embedder/output_surface_provider_impl.cc119
-rw-r--r--chromium/components/viz/service/display_embedder/output_surface_provider_impl.h30
-rw-r--r--chromium/components/viz/service/display_embedder/output_surface_unified.cc16
-rw-r--r--chromium/components/viz/service/display_embedder/output_surface_unified.h12
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_device.cc9
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc10
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_device_gl.cc2
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_device_webview.cc7
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_device_webview.h2
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc195
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl.h37
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc650
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h108
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc62
-rw-r--r--chromium/components/viz/service/display_embedder/skia_render_copy_results.cc46
-rw-r--r--chromium/components/viz/service/display_embedder/skia_render_copy_results.h66
-rw-r--r--chromium/components/viz/service/display_embedder/software_output_surface.cc41
-rw-r--r--chromium/components/viz/service/display_embedder/software_output_surface.h12
-rw-r--r--chromium/components/viz/service/display_embedder/viz_process_context_provider.cc348
-rw-r--r--chromium/components/viz/service/display_embedder/viz_process_context_provider.h136
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc14
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h3
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc40
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h12
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc82
-rw-r--r--chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc6
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc197
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc53
-rw-r--r--chromium/components/viz/service/gl/DEPS1
-rw-r--r--chromium/components/viz/service/gl/gpu_service_impl.cc67
-rw-r--r--chromium/components/viz/service/gl/gpu_service_impl.h28
-rw-r--r--chromium/components/viz/service/main/viz_compositor_thread_runner.h20
-rw-r--r--chromium/components/viz/service/main/viz_compositor_thread_runner_impl.cc25
-rw-r--r--chromium/components/viz/service/main/viz_compositor_thread_runner_impl.h3
-rw-r--r--chromium/components/viz/service/main/viz_main_impl.cc45
-rw-r--r--chromium/components/viz/service/main/viz_main_impl.h23
-rw-r--r--chromium/components/viz/service/main/viz_main_impl_unittest.cc7
-rw-r--r--chromium/components/viz/service/performance_hint/hint_session.cc17
-rw-r--r--chromium/components/viz/service/performance_hint/hint_session.h2
-rw-r--r--chromium/components/viz/service/surfaces/surface.cc149
-rw-r--r--chromium/components/viz/service/surfaces/surface.h81
-rw-r--r--chromium/components/viz/service/surfaces/surface_allocation_group.h2
-rw-r--r--chromium/components/viz/service/surfaces/surface_client.h10
-rw-r--r--chromium/components/viz/service/surfaces/surface_manager.cc45
-rw-r--r--chromium/components/viz/service/surfaces/surface_manager.h20
-rw-r--r--chromium/components/viz/service/surfaces/surface_observer.h3
-rw-r--r--chromium/components/viz/service/transitions/surface_animation_manager.cc32
-rw-r--r--chromium/components/viz/service/transitions/surface_animation_manager.h3
-rw-r--r--chromium/components/viz/test/BUILD.gn1
-rw-r--r--chromium/components/viz/viz.gni7
-rw-r--r--chromium/components/web_app_resources/web_app_default_offline.html4
-rw-r--r--chromium/components/web_cache/OWNERS1
-rw-r--r--chromium/components/web_cache/browser/web_cache_manager.cc53
-rw-r--r--chromium/components/web_cache/browser/web_cache_manager.h11
-rw-r--r--chromium/components/web_package/OWNERS1
-rw-r--r--chromium/components/web_package/web_bundle_builder.cc3
-rw-r--r--chromium/components/web_package/web_bundle_utils.cc3
-rw-r--r--chromium/components/web_resource/BUILD.gn14
-rw-r--r--chromium/components/web_resource/DEPS5
-rw-r--r--chromium/components/web_resource/OWNERS6
-rw-r--r--chromium/components/web_resource/web_resource_service.cc210
-rw-r--r--chromium/components/web_resource/web_resource_service.h146
-rw-r--r--chromium/components/web_resource/web_resource_service_unittest.cc164
-rw-r--r--chromium/components/webapk/OWNERS3
-rw-r--r--chromium/components/webapk/android/libs/client/BUILD.gn1
-rw-r--r--chromium/components/webapps/browser/BUILD.gn4
-rw-r--r--chromium/components/webapps/browser/android/BUILD.gn3
-rw-r--r--chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher.cc15
-rw-r--r--chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc171
-rw-r--r--chromium/components/webapps/browser/android/android_webapps_strings.grd6
-rw-r--r--chromium/components/webapps/browser/android/android_webapps_strings_grd/IDS_WEBAPK_INSTALL_FAILED.png.sha11
-rw-r--r--chromium/components/webapps/browser/android/android_webapps_strings_grd/IDS_WEBAPK_INSTALL_IN_PROGRESS.png.sha11
-rw-r--r--chromium/components/webapps/browser/android/app_banner_manager_android.cc7
-rw-r--r--chromium/components/webapps/browser/android/features.cc24
-rw-r--r--chromium/components/webapps/browser/android/installable/installable_ambient_badge_infobar_delegate.cc2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_af.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_am.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ar.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_as.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_az.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_be.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_bg.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_bn.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_bs.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ca.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_cs.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_cy.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_da.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_de.xtb4
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_el.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_en-GB.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_es-419.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_es.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_et.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_eu.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_fa.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_fi.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_fil.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_fr-CA.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_fr.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_gl.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_gu.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_hi.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_hr.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_hu.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_hy.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_id.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_is.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_it.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_iw.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ja.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ka.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_kk.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_km.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_kn.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ko.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ky.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_lo.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_lt.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_lv.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_mk.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ml.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_mn.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_mr.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ms.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_my.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ne.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_nl.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_no.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_or.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_pa.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_pl.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-BR.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-PT.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ro.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ru.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_si.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_sk.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_sl.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_sq.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_sr-Latn.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_sr.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_sv.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_sw.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ta.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_te.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_th.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_tr.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_uk.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_ur.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_uz.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_vi.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-CN.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-HK.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-TW.xtb2
-rw-r--r--chromium/components/webapps/browser/android/translations/android_webapps_strings_zu.xtb2
-rw-r--r--chromium/components/webapps/browser/android/webapk/webapk_icon_hasher_unittest.cc2
-rw-r--r--chromium/components/webapps/browser/android/webapk/webapk_types.h20
-rw-r--r--chromium/components/webapps/browser/android/webapps_utils.cc8
-rw-r--r--chromium/components/webapps/browser/android/webapps_utils.h6
-rw-r--r--chromium/components/webapps/browser/banners/app_banner_manager.cc47
-rw-r--r--chromium/components/webapps/browser/features.cc45
-rw-r--r--chromium/components/webapps/browser/features.h (renamed from chromium/components/webapps/browser/android/features.h)16
-rw-r--r--chromium/components/webapps/browser/install_result_code.cc2
-rw-r--r--chromium/components/webapps/browser/install_result_code.h5
-rw-r--r--chromium/components/webapps/browser/installable/installable_data.cc17
-rw-r--r--chromium/components/webapps/browser/installable/installable_data.h15
-rw-r--r--chromium/components/webapps/browser/installable/installable_logging.cc4
-rw-r--r--chromium/components/webapps/browser/installable/installable_logging.h1
-rw-r--r--chromium/components/webapps/browser/installable/installable_manager.cc20
-rw-r--r--chromium/components/webapps/browser/installable/installable_manager.h2
-rw-r--r--chromium/components/webapps/browser/installable/installable_metrics.cc6
-rw-r--r--chromium/components/webapps/browser/installable/installable_metrics.h6
-rw-r--r--chromium/components/webapps/browser/pwa_install_path_tracker.cc2
-rw-r--r--chromium/components/webapps/common/android/BUILD.gn2
-rw-r--r--chromium/components/webapps/renderer/web_page_metadata_extraction.cc13
-rw-r--r--chromium/components/webauthn/android/BUILD.gn9
-rw-r--r--chromium/components/webauthn/android/webauthn_browser_bridge.cc110
-rw-r--r--chromium/components/webauthn/android/webauthn_browser_bridge.h32
-rw-r--r--chromium/components/webauthn/android/webauthn_client_android.cc32
-rw-r--r--chromium/components/webauthn/android/webauthn_client_android.h43
-rw-r--r--chromium/components/webcrypto/BUILD.gn9
-rw-r--r--chromium/components/webcrypto/algorithm_dispatch.cc32
-rw-r--r--chromium/components/webcrypto/algorithm_dispatch.h20
-rw-r--r--chromium/components/webcrypto/algorithm_implementation.cc45
-rw-r--r--chromium/components/webcrypto/algorithm_implementation.h18
-rw-r--r--chromium/components/webcrypto/algorithm_implementations.h2
-rw-r--r--chromium/components/webcrypto/algorithm_registry.cc10
-rw-r--r--chromium/components/webcrypto/algorithms/aes.cc16
-rw-r--r--chromium/components/webcrypto/algorithms/aes.h8
-rw-r--r--chromium/components/webcrypto/algorithms/aes_cbc.cc15
-rw-r--r--chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc43
-rw-r--r--chromium/components/webcrypto/algorithms/aes_ctr.cc158
-rw-r--r--chromium/components/webcrypto/algorithms/aes_ctr_unittest.cc30
-rw-r--r--chromium/components/webcrypto/algorithms/aes_gcm.cc15
-rw-r--r--chromium/components/webcrypto/algorithms/aes_gcm_unittest.cc5
-rw-r--r--chromium/components/webcrypto/algorithms/aes_kw.cc21
-rw-r--r--chromium/components/webcrypto/algorithms/aes_kw_unittest.cc159
-rw-r--r--chromium/components/webcrypto/algorithms/asymmetric_key_util.cc32
-rw-r--r--chromium/components/webcrypto/algorithms/asymmetric_key_util.h12
-rw-r--r--chromium/components/webcrypto/algorithms/ec.cc34
-rw-r--r--chromium/components/webcrypto/algorithms/ec.h12
-rw-r--r--chromium/components/webcrypto/algorithms/ecdh.cc2
-rw-r--r--chromium/components/webcrypto/algorithms/ecdh_unittest.cc24
-rw-r--r--chromium/components/webcrypto/algorithms/ecdsa.cc23
-rw-r--r--chromium/components/webcrypto/algorithms/ecdsa_unittest.cc50
-rw-r--r--chromium/components/webcrypto/algorithms/ed25519.cc44
-rw-r--r--chromium/components/webcrypto/algorithms/hkdf.cc11
-rw-r--r--chromium/components/webcrypto/algorithms/hmac.cc45
-rw-r--r--chromium/components/webcrypto/algorithms/hmac_unittest.cc168
-rw-r--r--chromium/components/webcrypto/algorithms/pbkdf2.cc10
-rw-r--r--chromium/components/webcrypto/algorithms/rsa.cc94
-rw-r--r--chromium/components/webcrypto/algorithms/rsa.h10
-rw-r--r--chromium/components/webcrypto/algorithms/rsa_oaep.cc18
-rw-r--r--chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc361
-rw-r--r--chromium/components/webcrypto/algorithms/rsa_pss.cc6
-rw-r--r--chromium/components/webcrypto/algorithms/rsa_pss_unittest.cc54
-rw-r--r--chromium/components/webcrypto/algorithms/rsa_sign.cc15
-rw-r--r--chromium/components/webcrypto/algorithms/rsa_sign.h9
-rw-r--r--chromium/components/webcrypto/algorithms/rsa_ssa.cc6
-rw-r--r--chromium/components/webcrypto/algorithms/rsa_ssa_unittest.cc142
-rw-r--r--chromium/components/webcrypto/algorithms/secret_key_util.cc19
-rw-r--r--chromium/components/webcrypto/algorithms/secret_key_util.h8
-rw-r--r--chromium/components/webcrypto/algorithms/sha.cc9
-rw-r--r--chromium/components/webcrypto/algorithms/sha_unittest.cc4
-rw-r--r--chromium/components/webcrypto/algorithms/test_helpers.cc46
-rw-r--r--chromium/components/webcrypto/algorithms/test_helpers.h13
-rw-r--r--chromium/components/webcrypto/algorithms/util.cc30
-rw-r--r--chromium/components/webcrypto/algorithms/util.h13
-rw-r--r--chromium/components/webcrypto/algorithms/x25519.cc36
-rw-r--r--chromium/components/webcrypto/blink_key_handle.cc51
-rw-r--r--chromium/components/webcrypto/blink_key_handle.h20
-rw-r--r--chromium/components/webcrypto/crypto_data.cc28
-rw-r--r--chromium/components/webcrypto/crypto_data.h43
-rw-r--r--chromium/components/webcrypto/fuzzer_support.cc10
-rw-r--r--chromium/components/webcrypto/jwk.cc21
-rw-r--r--chromium/components/webcrypto/jwk.h12
-rw-r--r--chromium/components/webcrypto/status_unittest.cc1
-rw-r--r--chromium/components/webcrypto/webcrypto_impl.cc66
-rw-r--r--chromium/components/webrtc/fake_ssl_client_socket.cc5
-rw-r--r--chromium/components/webrtc/fake_ssl_client_socket.h4
-rw-r--r--chromium/components/webrtc/fake_ssl_client_socket_unittest.cc3
-rw-r--r--chromium/components/webrtc/media_stream_device_enumerator.h4
-rw-r--r--chromium/components/webrtc/media_stream_device_enumerator_impl.cc7
-rw-r--r--chromium/components/webrtc/media_stream_device_enumerator_impl.h3
-rw-r--r--chromium/components/webrtc/media_stream_devices_controller.cc222
-rw-r--r--chromium/components/webrtc/media_stream_devices_controller.h28
-rw-r--r--chromium/components/webrtc/thread_wrapper.cc2
-rw-r--r--chromium/components/webrtc_logging/common/partial_circular_buffer.cc4
-rw-r--r--chromium/components/webrtc_logging/common/partial_circular_buffer.h4
-rw-r--r--chromium/components/webrtc_logging/logging_unittest.cc10
-rw-r--r--chromium/components/webxr/android/BUILD.gn4
-rw-r--r--chromium/components/webxr/android/DEPS1
-rw-r--r--chromium/components/wifi/fake_wifi_service.cc38
-rw-r--r--chromium/components/wifi/fake_wifi_service.h14
-rw-r--r--chromium/components/wifi/network_properties.cc73
-rw-r--r--chromium/components/wifi/network_properties.h4
-rw-r--r--chromium/components/wifi/wifi_service.h14
-rw-r--r--chromium/components/wifi/wifi_service_fuchsia.cc14
-rw-r--r--chromium/components/wifi/wifi_service_mac.mm85
-rw-r--r--chromium/components/wifi/wifi_service_win.cc138
-rw-r--r--chromium/components/wifi/wifi_test.cc26
-rw-r--r--chromium/components/zoom/zoom_controller.cc26
4316 files changed, 128270 insertions, 82873 deletions
diff --git a/chromium/components/BUILD.gn b/chromium/components/BUILD.gn
index 9ca056537da..98635a28951 100644
--- a/chromium/components/BUILD.gn
+++ b/chromium/components/BUILD.gn
@@ -9,6 +9,7 @@ import("//build/config/ui.gni")
import("//components/nacl/features.gni")
import("//components/optimization_guide/features.gni")
import("//components/safe_browsing/buildflags.gni")
+import("//components/services/screen_ai/buildflags/features.gni")
import("//extensions/buildflags/buildflags.gni")
import("//media/media_options.gni")
import("//pdf/features.gni")
@@ -82,13 +83,16 @@ test("components_unittests") {
}
if (is_fuchsia) {
- use_cfv2 = false
- additional_manifest_fragments = [
- "//build/config/fuchsia/test/font_capabilities.test-cmx",
+ use_cfv1 = false
+
+ # TODO(https://crbug.com/1185811): Investigate removing the requirement for
+ # job_policy_ambient_mark_vmo_exec for the sake of V8's allocator in tests.
+ test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
- # TODO(crbug.com/1185811): Figure out why jit_capabilities is needed.
- "//build/config/fuchsia/test/jit_capabilities.test-cmx",
- "//build/config/fuchsia/test/network_capabilities.test-cmx",
+ additional_manifest_fragments = [
+ "//build/config/fuchsia/test/fonts.shard.test-cml",
+ "//build/config/fuchsia/test/network.shard.test-cml",
+ "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml",
]
}
@@ -116,6 +120,7 @@ test("components_unittests") {
"//components/certificate_matching:unit_tests",
"//components/client_update_protocol:unit_tests",
"//components/cloud_devices/common:unit_tests",
+ "//components/commerce/core:shopping_service_unit_tests",
"//components/component_updater:unit_tests",
"//components/component_updater/installer_policies:unit_tests",
"//components/consent_auditor:unit_tests",
@@ -125,6 +130,7 @@ test("components_unittests") {
"//components/crash/core/common:unit_tests",
"//components/crx_file:unit_tests",
"//components/device_event_log:unit_tests",
+ "//components/device_signals/core:unit_tests",
"//components/dom_distiller/core:unit_tests",
"//components/download:unit_tests",
"//components/encrypted_messages:unit_tests",
@@ -142,7 +148,6 @@ test("components_unittests") {
"//components/history/core/browser:unit_tests",
"//components/history/core/common:unit_tests",
"//components/history/metrics:unit_tests",
- "//components/history_clusters/core:unit_tests",
"//components/image_fetcher/core:unit_tests",
"//components/keyed_service/core:unit_tests",
"//components/language/core/browser:unit_tests",
@@ -150,6 +155,7 @@ test("components_unittests") {
"//components/language/core/language_model:unit_tests",
"//components/lens:unit_tests",
"//components/leveldb_proto:unit_tests",
+ "//components/local_state:unit_tests",
"//components/lookalikes/core:unit_tests",
"//components/memory_pressure:unit_tests",
"//components/metrics:unit_tests",
@@ -227,6 +233,7 @@ test("components_unittests") {
"//components/url_formatter/spoof_checks/common_words:unit_tests",
"//components/url_formatter/spoof_checks/top_domains:unit_tests",
"//components/url_matcher:unit_tests",
+ "//components/url_param_filter/core:unit_tests",
"//components/url_pattern_index:unit_tests",
"//components/variations:unit_tests",
"//components/variations/field_trial_config:unit_tests",
@@ -236,6 +243,10 @@ test("components_unittests") {
"//components/webdata_services:unit_tests",
]
+ if (enable_screen_ai_service) {
+ deps += [ "//components/services/screen_ai:unit_tests" ]
+ }
+
if (build_with_internal_optimization_guide && is_android) {
loadable_module_deps = [
"//components/optimization_guide/internal:optimization_guide_internal",
@@ -251,6 +262,7 @@ test("components_unittests") {
"//components/global_media_controls:unit_tests",
"//components/media_message_center:unit_tests",
"//components/ui_devtools:unit_tests",
+ "//components/user_education/views:unit_tests",
]
}
@@ -262,7 +274,13 @@ test("components_unittests") {
deps += [ "//components/nacl/browser:unit_tests" ]
}
- if (!is_fuchsia) {
+ if (is_fuchsia) {
+ deps += [
+ "//components/fuchsia_component_support:unit_tests",
+ "//components/fuchsia_legacymetrics:unit_tests",
+ ]
+ } else {
+ # TODO(crbug.com/1290514): Enable all relevant tests on Fuchsia too.
deps += [
"//components/browser_sync:unit_tests",
"//components/send_tab_to_self:unit_tests",
@@ -289,6 +307,7 @@ test("components_unittests") {
"//components/signin/public/identity_manager/objc:unit_tests",
"//components/translate/ios/browser:unit_tests",
"//components/ukm/ios:unit_tests",
+ "//components/url_param_filter/ios:unit_tests",
]
} else { #!is_ios
deps += [
@@ -296,6 +315,7 @@ test("components_unittests") {
"//components/autofill/content/browser:unit_tests",
"//components/autofill/content/renderer:unit_tests",
"//components/autofill/core/common/mojom:unit_tests",
+ "//components/autofill_assistant/browser:unit_tests",
"//components/background_sync:unit_tests",
"//components/blocked_content:unit_tests",
"//components/browsing_data/content:unit_tests",
@@ -307,7 +327,6 @@ test("components_unittests") {
"//components/certificate_transparency:unit_tests",
"//components/content_capture/browser:unit_tests",
"//components/content_settings/browser:unit_tests",
- "//components/contextual_search/core:unit_tests",
"//components/continuous_search/browser:unit_tests",
"//components/continuous_search/common:unit_tests",
"//components/custom_handlers:unit_tests",
@@ -319,12 +338,14 @@ test("components_unittests") {
"//components/domain_reliability:unit_tests",
"//components/drive:unit_tests",
"//components/embedder_support:unit_tests",
+ "//components/endpoint_fetcher:unit_tests",
"//components/enterprise/content:unit_tests",
"//components/favicon/content:unit_tests",
"//components/feed:unit_tests",
"//components/gcm_driver/instance_id:unit_tests",
"//components/heavy_ad_intervention:unit_tests",
"//components/history/content/browser:unit_tests",
+ "//components/history_clusters/core:unit_tests",
"//components/invalidation/impl:unit_tests",
"//components/javascript_dialogs:unit_tests",
"//components/js_injection/common:unit_tests",
@@ -386,7 +407,9 @@ test("components_unittests") {
"//components/translate/content/browser:unit_tests",
"//components/translate/content/renderer:unit_tests",
"//components/ukm/content:unit_tests",
+ "//components/url_param_filter/content:unit_tests",
"//components/url_rewrite:unit_tests",
+ "//components/user_education/common:unit_tests",
"//components/value_store:unit_tests",
"//components/visitedlink/test:unit_tests",
"//components/web_cache/browser:unit_tests",
@@ -430,7 +453,6 @@ test("components_unittests") {
deps += [
"//base:base_java_unittest_support",
"//components/android_autofill/browser:unit_tests",
- "//components/autofill_assistant/browser:unit_tests",
"//components/autofill_assistant/content/browser:unit_tests",
"//components/autofill_assistant/content/renderer:unit_tests",
"//components/browser_ui/sms/android:unit_tests",
@@ -624,7 +646,10 @@ test("components_unittests") {
}
if (is_win || is_mac || is_linux || is_chromeos) {
- deps += [ "//components/user_notes/browser:unit_tests" ]
+ deps += [
+ "//components/user_notes/browser:unit_tests",
+ "//components/user_notes/storage:unit_tests",
+ ]
}
# On LaCrOS, tests use ash - chrome as a window manager, thus the dependency.
@@ -853,11 +878,15 @@ if (!is_ios) {
"autofill_assistant/browser/js_flow_executor_impl_browsertest.cc",
"autofill_assistant/browser/mock_script_executor_delegate.cc",
"autofill_assistant/browser/mock_script_executor_delegate.h",
+ "autofill_assistant/browser/script_executor_browsertest.cc",
"autofill_assistant/browser/service/mock_service.cc",
"autofill_assistant/browser/service/mock_service.h",
"autofill_assistant/browser/web/batch_element_checker_browsertest.cc",
+ "autofill_assistant/browser/web/mock_autofill_assistant_agent.cc",
+ "autofill_assistant/browser/web/mock_autofill_assistant_agent.h",
"autofill_assistant/browser/web/mock_web_controller.cc",
"autofill_assistant/browser/web/mock_web_controller.h",
+ "autofill_assistant/browser/web/semantic_element_finder_browsertest.cc",
"autofill_assistant/browser/web/web_controller_browsertest.cc",
"browser_ui/client_certificate/android/ssl_client_certificate_request_browsertest.cc",
"test/android/browsertests_apk/components_browser_tests_jni_onload.cc",
@@ -950,7 +979,6 @@ if (!is_ios) {
"omnibox/browser/history_quick_provider_performance_unittest.cc",
"subresource_filter/core/common/perftests/indexed_ruleset_perftest.cc",
"test/run_all_perftests.cc",
- "url_matcher/substring_set_matcher_perftest.cc",
"visitedlink/test/visitedlink_perftest.cc",
]
@@ -966,7 +994,6 @@ if (!is_ios) {
"//components/subresource_filter/core/common",
"//components/subresource_filter/tools:tools_lib",
"//components/test:test_support",
- "//components/url_matcher",
"//components/visitedlink/browser",
"//testing/perf",
"//url",
diff --git a/chromium/components/OWNERS b/chromium/components/OWNERS
index 90f56a36df5..ce40fb735a2 100644
--- a/chromium/components/OWNERS
+++ b/chromium/components/OWNERS
@@ -1,7 +1,6 @@
set noparent
blundell@chromium.org
caitkp@chromium.org
-jochen@chromium.org
tedchoc@chromium.org
# For build configuration things and simple changes.
diff --git a/chromium/components/about_ui/PRESUBMIT.py b/chromium/components/about_ui/PRESUBMIT.py
deleted file mode 100644
index 864735f0efc..00000000000
--- a/chromium/components/about_ui/PRESUBMIT.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-USE_PYTHON3 = True
-
-
-def _CommonChecks(input_api, output_api):
- results = []
- try:
- import sys
- old_sys_path = sys.path[:]
- cwd = input_api.PresubmitLocalPath()
- sys.path += [input_api.os_path.join(cwd, '..', '..', 'tools')]
- import web_dev_style.presubmit_support
- results += web_dev_style.presubmit_support.CheckStyle(input_api, output_api)
- finally:
- sys.path = old_sys_path
- return results
-
-
-def CheckChangeOnUpload(input_api, output_api):
- return _CommonChecks(input_api, output_api)
-
-
-def CheckChangeOnCommit(input_api, output_api):
- return _CommonChecks(input_api, output_api)
diff --git a/chromium/components/about_ui/android/BUILD.gn b/chromium/components/about_ui/android/BUILD.gn
index 8c9ea3fcb1e..670e2ab87fd 100644
--- a/chromium/components/about_ui/android/BUILD.gn
+++ b/chromium/components/about_ui/android/BUILD.gn
@@ -9,6 +9,9 @@ generate_jni("about_ui_jni_headers") {
android_library("aboutui_java") {
sources = [ "java/src/org/chromium/components/aboutui/CreditUtils.java" ]
- deps = [ "//base:base_java" ]
+ deps = [
+ "//base:jni_java",
+ "//build/android:build_java",
+ ]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
}
diff --git a/chromium/components/access_code_cast/common/access_code_cast_metrics.cc b/chromium/components/access_code_cast/common/access_code_cast_metrics.cc
index 0a1270f6225..0a07ac49d62 100644
--- a/chromium/components/access_code_cast/common/access_code_cast_metrics.cc
+++ b/chromium/components/access_code_cast/common/access_code_cast_metrics.cc
@@ -10,11 +10,58 @@ AccessCodeCastMetrics::AccessCodeCastMetrics() = default;
AccessCodeCastMetrics::~AccessCodeCastMetrics() = default;
// static
+const char AccessCodeCastMetrics::kHistogramAddSinkResultNew[] =
+ "AccessCodeCast.Discovery.AddSinkResult.New";
+const char AccessCodeCastMetrics::kHistogramAddSinkResultRemembered[] =
+ "AccessCodeCast.Discovery.AddSinkResult.Remembered";
+const char AccessCodeCastMetrics::kHistogramCastModeOnSuccess[] =
+ "AccessCodeCast.Discovery.CastModeOnSuccess";
+const char AccessCodeCastMetrics::kHistogramDialogCloseReason[] =
+ "AccessCodeCast.Ui.DialogCloseReason";
+const char AccessCodeCastMetrics::kHistogramDialogLoadTime[] =
+ "AccessCodeCast.Ui.DialogLoadTime";
const char AccessCodeCastMetrics::kHistogramDialogOpenLocation[] =
"AccessCodeCast.Ui.DialogOpenLocation";
+const char AccessCodeCastMetrics::kHistogramRememberedDevicesCount[] =
+ "AccessCodeCast.Discovery.RememberedDevicesCount";
+
+// static
+void AccessCodeCastMetrics::OnCastSessionResult(int route_request_result_code,
+ AccessCodeCastCastMode mode) {
+ if (route_request_result_code == 1 /* ResultCode::OK */) {
+ base::UmaHistogramEnumeration(kHistogramCastModeOnSuccess, mode);
+ }
+}
+
+// static
+void AccessCodeCastMetrics::RecordAddSinkResult(
+ bool is_remembered,
+ AccessCodeCastAddSinkResult result) {
+ if (is_remembered) {
+ base::UmaHistogramEnumeration(kHistogramAddSinkResultRemembered, result);
+ } else {
+ base::UmaHistogramEnumeration(kHistogramAddSinkResultNew, result);
+ }
+}
+
+// static
+void AccessCodeCastMetrics::RecordDialogCloseReason(
+ AccessCodeCastDialogCloseReason reason) {
+ base::UmaHistogramEnumeration(kHistogramDialogCloseReason, reason);
+}
+
+// static
+void AccessCodeCastMetrics::RecordDialogLoadTime(base::TimeDelta load_time) {
+ base::UmaHistogramTimes(kHistogramDialogLoadTime, load_time);
+}
// static
void AccessCodeCastMetrics::RecordDialogOpenLocation(
AccessCodeCastDialogOpenLocation location) {
base::UmaHistogramEnumeration(kHistogramDialogOpenLocation, location);
-} \ No newline at end of file
+}
+
+// static
+void AccessCodeCastMetrics::RecordRememberedDevicesCount(int count) {
+ base::UmaHistogramCounts100(kHistogramRememberedDevicesCount, count);
+}
diff --git a/chromium/components/access_code_cast/common/access_code_cast_metrics.h b/chromium/components/access_code_cast/common/access_code_cast_metrics.h
index 7944aceb0c4..dccbd25d14a 100644
--- a/chromium/components/access_code_cast/common/access_code_cast_metrics.h
+++ b/chromium/components/access_code_cast/common/access_code_cast_metrics.h
@@ -5,12 +5,61 @@
#ifndef COMPONENTS_ACCESS_CODE_CAST_COMMON_ACCESS_CODE_CAST_METRICS_H_
#define COMPONENTS_ACCESS_CODE_CAST_COMMON_ACCESS_CODE_CAST_METRICS_H_
+#include "base/time/time.h"
+
// NOTE: Do not renumber enums as that would confuse interpretation of
// previously logged data. When making changes, also update the enum list
// in tools/metrics/histograms/enums.xml to keep it in sync.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
+enum class AccessCodeCastAddSinkResult {
+ kUnknownError = 0,
+ kOk = 1,
+ kAuthError = 2,
+ kHttpResponseCodeError = 3,
+ kResponseMalformed = 4,
+ kEmptyResponse = 5,
+ kInvalidAccessCode = 6,
+ kAccessCodeNotFound = 7,
+ kTooManyRequests = 8,
+ kServiceNotPresent = 9,
+ kServerError = 10,
+ kSinkCreationError = 11,
+ kChannelOpenError = 12,
+ kProfileSyncError = 13,
+
+ // NOTE: Do not reorder existing entries, and add entries only immediately
+ // above this line.
+ kMaxValue = kProfileSyncError
+};
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class AccessCodeCastCastMode {
+ kPresentation = 0,
+ kTabMirror = 1,
+ kDesktopMirror = 2,
+
+ // NOTE: Do not reorder existing entries, and add entries only immediately
+ // above this line.
+ kMaxValue = kDesktopMirror
+};
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class AccessCodeCastDialogCloseReason {
+ kFocus = 0,
+ kCancel = 1,
+ kCastSuccess = 2,
+
+ // NOTE: Do not reorder existing entries, and add entries only immediately
+ // above this line.
+ kMaxValue = kCastSuccess
+};
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
enum class AccessCodeCastDialogOpenLocation {
kBrowserCastMenu = 0,
kSystemTrayCastFeaturePod = 1,
@@ -27,11 +76,36 @@ class AccessCodeCastMetrics {
~AccessCodeCastMetrics();
// UMA histogram names.
+ static const char kHistogramAddSinkResultNew[];
+ static const char kHistogramAddSinkResultRemembered[];
+ static const char kHistogramCastModeOnSuccess[];
+ static const char kHistogramDialogCloseReason[];
+ static const char kHistogramDialogLoadTime[];
static const char kHistogramDialogOpenLocation[];
+ static const char kHistogramRememberedDevicesCount[];
+
+ // Records metrics relating to starting a cast session (route). Mode is
+ // media_router::MediaCastMode.
+ static void OnCastSessionResult(int route_request_result_code,
+ AccessCodeCastCastMode mode);
+
+ // Records the result of adding an access code sink.
+ static void RecordAddSinkResult(bool is_remembered,
+ AccessCodeCastAddSinkResult result);
+
+ // Records the time it takes for the AccessCodeCast dialog to load.
+ static void RecordDialogLoadTime(base::TimeDelta load_time);
+
+ // Records the reason that the AccessCodeCast dialog closed.
+ static void RecordDialogCloseReason(AccessCodeCastDialogCloseReason reason);
// Records where the user clicked to open the AccessCodeCast dialog.
static void RecordDialogOpenLocation(
AccessCodeCastDialogOpenLocation location);
+
+ // Records the count of cast devices which are currently being remembered
+ // being the AccessCodeCastSinkService.
+ static void RecordRememberedDevicesCount(int count);
};
#endif // COMPONENTS_ACCESS_CODE_CAST_COMMON_ACCESS_CODE_CAST_METRICS_H_
diff --git a/chromium/components/access_code_cast/common/access_code_cast_metrics_unittest.cc b/chromium/components/access_code_cast/common/access_code_cast_metrics_unittest.cc
index 878a8806afc..27d39d7e6f7 100644
--- a/chromium/components/access_code_cast/common/access_code_cast_metrics_unittest.cc
+++ b/chromium/components/access_code_cast/common/access_code_cast_metrics_unittest.cc
@@ -4,6 +4,7 @@
#include "components/access_code_cast/common/access_code_cast_metrics.h"
+#include "base/time/time.h"
#include "base/test/metrics/histogram_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -27,3 +28,122 @@ TEST(AccessCodeCastMetricsTest, RecordDialogOpenLocation) {
1);
histogram_tester.ExpectTotalCount("AccessCodeCast.Ui.DialogOpenLocation", 3);
}
+
+TEST(AccessCodeCastMetricsTest, RecordAddSinkResult) {
+ base::HistogramTester histogram_tester;
+
+ AccessCodeCastMetrics::RecordAddSinkResult(
+ false, AccessCodeCastAddSinkResult::kUnknownError);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.AddSinkResult.New", 0, 1);
+ AccessCodeCastMetrics::RecordAddSinkResult(false,
+ AccessCodeCastAddSinkResult::kOk);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.AddSinkResult.New", 1, 1);
+ AccessCodeCastMetrics::RecordAddSinkResult(
+ false, AccessCodeCastAddSinkResult::kAuthError);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.AddSinkResult.New", 2, 1);
+ histogram_tester.ExpectTotalCount(
+ "AccessCodeCast.Discovery.AddSinkResult.New", 3);
+
+ AccessCodeCastMetrics::RecordAddSinkResult(
+ true, AccessCodeCastAddSinkResult::kUnknownError);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.AddSinkResult.Remembered", 0, 1);
+ AccessCodeCastMetrics::RecordAddSinkResult(true,
+ AccessCodeCastAddSinkResult::kOk);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.AddSinkResult.Remembered", 1, 1);
+ AccessCodeCastMetrics::RecordAddSinkResult(
+ true, AccessCodeCastAddSinkResult::kAuthError);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.AddSinkResult.Remembered", 2, 1);
+ histogram_tester.ExpectTotalCount(
+ "AccessCodeCast.Discovery.AddSinkResult.New", 3);
+ histogram_tester.ExpectTotalCount(
+ "AccessCodeCast.Discovery.AddSinkResult.Remembered", 3);
+}
+
+TEST(AccessCodeCastMetricsTest, OnCastSessionResult) {
+ base::HistogramTester histogram_tester;
+
+ AccessCodeCastMetrics::OnCastSessionResult(
+ 0 /* ResultCode::UNKNOWN_ERROR */, AccessCodeCastCastMode::kPresentation);
+ histogram_tester.ExpectTotalCount(
+ "AccessCodeCast.Discovery.CastModeOnSuccess", 0);
+
+ AccessCodeCastMetrics::OnCastSessionResult(
+ 1 /* RouteRequest::OK */, AccessCodeCastCastMode::kPresentation);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.CastModeOnSuccess", 0, 1);
+ AccessCodeCastMetrics::OnCastSessionResult(
+ 1 /* RouteRequest::OK */, AccessCodeCastCastMode::kTabMirror);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.CastModeOnSuccess", 1, 1);
+ AccessCodeCastMetrics::OnCastSessionResult(
+ 1 /* RouteRequest::OK */, AccessCodeCastCastMode::kDesktopMirror);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.CastModeOnSuccess", 2, 1);
+ histogram_tester.ExpectTotalCount(
+ "AccessCodeCast.Discovery.CastModeOnSuccess", 3);
+}
+
+TEST(AccessCodeCastMetricsTest, RecordDialogLoadTime) {
+ base::HistogramTester histogram_tester;
+
+ AccessCodeCastMetrics::RecordDialogLoadTime(base::Milliseconds(10));
+ histogram_tester.ExpectBucketCount("AccessCodeCast.Ui.DialogLoadTime", 10, 1);
+
+ // Ten seconds (10,000 ms) is the max for UmaHistogramTimes.
+ AccessCodeCastMetrics::RecordDialogLoadTime(base::Seconds(10));
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Ui.DialogLoadTime", 10000, 1);
+ AccessCodeCastMetrics::RecordDialogLoadTime(base::Seconds(20));
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Ui.DialogLoadTime", 10000, 2);
+
+ histogram_tester.ExpectTotalCount("AccessCodeCast.Ui.DialogLoadTime", 3);
+}
+
+TEST(AccessCodeCastMetricsTest, RecordDialogCloseReason) {
+ base::HistogramTester histogram_tester;
+
+ AccessCodeCastMetrics::RecordDialogCloseReason(
+ AccessCodeCastDialogCloseReason::kFocus);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Ui.DialogCloseReason", 0, 1);
+
+ AccessCodeCastMetrics::RecordDialogCloseReason(
+ AccessCodeCastDialogCloseReason::kCancel);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Ui.DialogCloseReason", 1, 1);
+
+ AccessCodeCastMetrics::RecordDialogCloseReason(
+ AccessCodeCastDialogCloseReason::kCastSuccess);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Ui.DialogCloseReason", 2, 1);
+
+ histogram_tester.ExpectTotalCount("AccessCodeCast.Ui.DialogCloseReason", 3);
+}
+
+TEST(AccessCodeCastMetricsTest, RecordRememberedDevicesCount) {
+ base::HistogramTester histogram_tester;
+
+ AccessCodeCastMetrics::RecordRememberedDevicesCount(0);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.RememberedDevicesCount", 0, 1);
+
+ AccessCodeCastMetrics::RecordRememberedDevicesCount(1);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.RememberedDevicesCount", 1, 1);
+
+ AccessCodeCastMetrics::RecordRememberedDevicesCount(100);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.RememberedDevicesCount", 100, 1);
+
+ // Over 100 should be reported as 100.
+ AccessCodeCastMetrics::RecordRememberedDevicesCount(500);
+ histogram_tester.ExpectBucketCount(
+ "AccessCodeCast.Discovery.RememberedDevicesCount", 100, 2);
+}
diff --git a/chromium/components/account_manager_core/account_manager_facade.h b/chromium/components/account_manager_core/account_manager_facade.h
index 35f41c6c8ed..d164d5148d2 100644
--- a/chromium/components/account_manager_core/account_manager_facade.h
+++ b/chromium/components/account_manager_core/account_manager_facade.h
@@ -51,9 +51,9 @@ class COMPONENT_EXPORT(ACCOUNT_MANAGER_CORE) AccountManagerFacade {
// Note: Please update |AccountManagerAccountAdditionSource| in enums.xml
// after adding new values.
enum class AccountAdditionSource : int {
- // Settings > Add account button.
+ // OS Settings > Add account button.
kSettingsAddAccountButton = 0,
- // Settings > Sign in again button.
+ // OS Settings > Sign in again button.
kSettingsReauthAccountButton = 1,
// Launched from an ARC application.
kArc = 2,
@@ -70,12 +70,24 @@ class COMPONENT_EXPORT(ACCOUNT_MANAGER_CORE) AccountManagerFacade {
kChromeProfileCreation = 7,
// Account addition flow launched by the user from One Google Bar.
kOgbAddAccount = 8,
- // Avatar bubble -> Signin again button.
+ // Avatar bubble -> Sign in again button.
kAvatarBubbleReauthAccountButton = 9,
// A Chrome extension required account re-authentication.
kChromeExtensionReauth = 10,
-
- kMaxValue = kChromeExtensionReauth
+ // Sync promo with an account that requires re-authentication.
+ kChromeSyncPromoReauth = 11,
+ // Chrome Settings > Sign in again button.
+ kChromeSettingsReauthAccountButton = 12,
+ // Avatar bubble -> Turn on sync button.
+ kAvatarBubbleTurnOnSyncAddAccount = 13,
+ // A Chrome extension required a new account.
+ kChromeExtensionAddAccount = 14,
+ // Sync promo with a new account.
+ kChromeSyncPromoAddAccount = 15,
+ // Chrome Settings > Turn on Sync.
+ kChromeSettingsTurnOnSyncButton = 16,
+
+ kMaxValue = kChromeSettingsTurnOnSyncButton
};
AccountManagerFacade();
@@ -115,7 +127,8 @@ class COMPONENT_EXPORT(ACCOUNT_MANAGER_CORE) AccountManagerFacade {
// Launches account reauthentication dialog for provided `email`.
virtual void ShowReauthAccountDialog(AccountAdditionSource source,
- const std::string& email) = 0;
+ const std::string& email,
+ base::OnceClosure callback) = 0;
// Launches OS Settings > Accounts.
virtual void ShowManageAccountsSettings() = 0;
diff --git a/chromium/components/account_manager_core/account_manager_facade_impl.cc b/chromium/components/account_manager_core/account_manager_facade_impl.cc
index 22eb4aa58d5..168c17b8251 100644
--- a/chromium/components/account_manager_core/account_manager_facade_impl.cc
+++ b/chromium/components/account_manager_core/account_manager_facade_impl.cc
@@ -86,6 +86,14 @@ bool GetIsAvailableInArcBySource(
// Accounts added from the browser should not be available in ARC.
case AccountManagerFacade::AccountAdditionSource::kChromeProfileCreation:
case AccountManagerFacade::AccountAdditionSource::kOgbAddAccount:
+ case AccountManagerFacade::AccountAdditionSource::
+ kAvatarBubbleTurnOnSyncAddAccount:
+ case AccountManagerFacade::AccountAdditionSource::
+ kChromeExtensionAddAccount:
+ case AccountManagerFacade::AccountAdditionSource::
+ kChromeSyncPromoAddAccount:
+ case AccountManagerFacade::AccountAdditionSource::
+ kChromeSettingsTurnOnSyncButton:
return false;
// These are reauthentication cases. ARC visibility shouldn't change for
// reauthentication.
@@ -95,6 +103,9 @@ bool GetIsAvailableInArcBySource(
case AccountManagerFacade::AccountAdditionSource::
kAvatarBubbleReauthAccountButton:
case AccountManagerFacade::AccountAdditionSource::kChromeExtensionReauth:
+ case AccountManagerFacade::AccountAdditionSource::kChromeSyncPromoReauth:
+ case AccountManagerFacade::AccountAdditionSource::
+ kChromeSettingsReauthAccountButton:
NOTREACHED();
return false;
// Unused enums that cannot be deleted.
@@ -354,18 +365,21 @@ void AccountManagerFacadeImpl::ShowAddAccountDialog(
void AccountManagerFacadeImpl::ShowReauthAccountDialog(
AccountAdditionSource source,
- const std::string& email) {
+ const std::string& email,
+ base::OnceClosure callback) {
if (!account_manager_remote_ ||
remote_version_ < RemoteMinVersions::kShowReauthAccountDialogMinVersion) {
LOG(WARNING) << "Found remote at: " << remote_version_ << ", expected: "
<< RemoteMinVersions::kShowReauthAccountDialogMinVersion
<< " for ShowReauthAccountDialog.";
+ if (callback)
+ std::move(callback).Run();
return;
}
base::UmaHistogramEnumeration(kAccountAdditionSource, source);
- account_manager_remote_->ShowReauthAccountDialog(email, base::DoNothing());
+ account_manager_remote_->ShowReauthAccountDialog(email, std::move(callback));
}
void AccountManagerFacadeImpl::ShowManageAccountsSettings() {
diff --git a/chromium/components/account_manager_core/account_manager_facade_impl.h b/chromium/components/account_manager_core/account_manager_facade_impl.h
index 6fe4a04e201..7e3851d0678 100644
--- a/chromium/components/account_manager_core/account_manager_facade_impl.h
+++ b/chromium/components/account_manager_core/account_manager_facade_impl.h
@@ -62,7 +62,8 @@ class COMPONENT_EXPORT(ACCOUNT_MANAGER_CORE) AccountManagerFacadeImpl
base::OnceCallback<void(const account_manager::AccountAdditionResult&
result)> callback) override;
void ShowReauthAccountDialog(AccountAdditionSource source,
- const std::string& email) override;
+ const std::string& email,
+ base::OnceClosure callback) override;
void ShowManageAccountsSettings() override;
std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher(
const AccountKey& account,
diff --git a/chromium/components/account_manager_core/account_manager_facade_impl_unittest.cc b/chromium/components/account_manager_core/account_manager_facade_impl_unittest.cc
index 9683f5d3dab..47810c7eb3e 100644
--- a/chromium/components/account_manager_core/account_manager_facade_impl_unittest.cc
+++ b/chromium/components/account_manager_core/account_manager_facade_impl_unittest.cc
@@ -555,7 +555,7 @@ TEST_F(AccountManagerFacadeImplTest, ShowReauthAccountDialogCallsMojo) {
account_manager_facade->ShowReauthAccountDialog(
account_manager::AccountManagerFacade::AccountAdditionSource::
kSettingsAddAccountButton,
- kTestAccountEmail);
+ kTestAccountEmail, base::OnceClosure());
account_manager_facade->FlushMojoForTesting();
EXPECT_EQ(1, account_manager().show_reauth_account_dialog_calls());
}
@@ -566,7 +566,8 @@ TEST_F(AccountManagerFacadeImplTest, ShowReauthAccountDialogUMA) {
CreateFacade();
auto source = AccountManagerFacade::AccountAdditionSource::kContentAreaReauth;
- account_manager_facade->ShowReauthAccountDialog(source, kTestAccountEmail);
+ account_manager_facade->ShowReauthAccountDialog(source, kTestAccountEmail,
+ base::OnceClosure());
account_manager_facade->FlushMojoForTesting();
// Check that UMA stats were sent.
diff --git a/chromium/components/account_manager_core/mock_account_manager_facade.h b/chromium/components/account_manager_core/mock_account_manager_facade.h
index 6e4c97043f9..66a3957eb82 100644
--- a/chromium/components/account_manager_core/mock_account_manager_facade.h
+++ b/chromium/components/account_manager_core/mock_account_manager_facade.h
@@ -51,7 +51,7 @@ class MockAccountManagerFacade : public account_manager::AccountManagerFacade {
(override));
MOCK_METHOD(void,
ShowReauthAccountDialog,
- (AccountAdditionSource, const std::string&),
+ (AccountAdditionSource, const std::string&, base::OnceClosure),
(override));
MOCK_METHOD(void, ShowManageAccountsSettings, (), (override));
MOCK_METHOD(std::unique_ptr<OAuth2AccessTokenFetcher>,
diff --git a/chromium/components/accuracy_tips/accuracy_service.cc b/chromium/components/accuracy_tips/accuracy_service.cc
index 5d90a9693da..9d3a2a8ba14 100644
--- a/chromium/components/accuracy_tips/accuracy_service.cc
+++ b/chromium/components/accuracy_tips/accuracy_service.cc
@@ -23,7 +23,6 @@
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "components/unified_consent/pref_names.h"
#include "content/public/browser/web_contents.h"
#include "services/metrics/public/cpp/metrics_utils.h"
@@ -157,7 +156,8 @@ void AccuracyService::MaybeShowAccuracyTip(content::WebContents* web_contents) {
if (disable_ui_) {
return OnAccuracyTipClosed(
- base::TimeTicks(), ukm::GetSourceIdForWebContentsDocument(web_contents),
+ base::TimeTicks(),
+ web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId(),
AccuracyTipInteraction::kDisabledByExperiment);
}
@@ -172,9 +172,10 @@ void AccuracyService::MaybeShowAccuracyTip(content::WebContents* web_contents) {
delegate_->ShowAccuracyTip(
web_contents, AccuracyTipStatus::kShowAccuracyTip,
/*show_opt_out=*/show_opt_out,
- base::BindOnce(&AccuracyService::OnAccuracyTipClosed,
- weak_factory_.GetWeakPtr(), base::TimeTicks::Now(),
- ukm::GetSourceIdForWebContentsDocument(web_contents)));
+ base::BindOnce(
+ &AccuracyService::OnAccuracyTipClosed, weak_factory_.GetWeakPtr(),
+ base::TimeTicks::Now(),
+ web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId()));
for (Observer& observer : observers_)
observer.OnAccuracyTipShown();
}
diff --git a/chromium/components/accuracy_tips/accuracy_web_contents_observer.cc b/chromium/components/accuracy_tips/accuracy_web_contents_observer.cc
index a4dd5a3460a..8aa14768386 100644
--- a/chromium/components/accuracy_tips/accuracy_web_contents_observer.cc
+++ b/chromium/components/accuracy_tips/accuracy_web_contents_observer.cc
@@ -9,7 +9,6 @@
#include "components/accuracy_tips/accuracy_service.h"
#include "components/accuracy_tips/accuracy_tip_status.h"
#include "components/safe_browsing/core/common/features.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/common/page_visibility_state.h"
@@ -44,7 +43,7 @@ void AccuracyWebContentsObserver::DidFinishNavigation(
return;
}
- if (web_contents()->GetMainFrame()->GetVisibilityState() !=
+ if (web_contents()->GetPrimaryMainFrame()->GetVisibilityState() !=
content::PageVisibilityState::kVisible) {
return;
}
@@ -76,7 +75,7 @@ void AccuracyWebContentsObserver::OnAccuracyStatusObtained(
UMA_HISTOGRAM_ENUMERATION("Privacy.AccuracyTip.PageStatus", result);
ukm::builders::AccuracyTipStatus(
- ukm::GetSourceIdForWebContentsDocument(web_contents()))
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId())
.SetStatus(static_cast<int>(result))
.Record(ukm::UkmRecorder::Get());
diff --git a/chromium/components/android_autofill/browser/BUILD.gn b/chromium/components/android_autofill/browser/BUILD.gn
index 78122578fc7..edd9130e20a 100644
--- a/chromium/components/android_autofill/browser/BUILD.gn
+++ b/chromium/components/android_autofill/browser/BUILD.gn
@@ -18,6 +18,8 @@ android_library("java") {
srcjar_deps = [ ":autofill_aidl" ]
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/autofill/android:autofill_java",
"//components/autofill/core/common/mojom:mojo_types_java",
"//components/version_info/android:version_constants_java",
diff --git a/chromium/components/android_autofill/browser/android_autofill_manager.cc b/chromium/components/android_autofill/browser/android_autofill_manager.cc
index d42e84aefd3..43683869359 100644
--- a/chromium/components/android_autofill/browser/android_autofill_manager.cc
+++ b/chromium/components/android_autofill/browser/android_autofill_manager.cc
@@ -14,27 +14,56 @@ namespace autofill {
using base::TimeTicks;
-// static
-std::unique_ptr<AutofillManager> AndroidAutofillManager::Create(
- AutofillDriver* driver,
+void AndroidDriverInitHook(
AutofillClient* client,
- const std::string& /*app_locale*/,
- AutofillManager::AutofillDownloadManagerState enable_download_manager) {
- return base::WrapUnique(
- new AndroidAutofillManager(driver, client, enable_download_manager));
+ AutofillManager::EnableDownloadManager enable_download_manager,
+ ContentAutofillDriver* driver) {
+ driver->set_autofill_manager(base::WrapUnique(
+ new AndroidAutofillManager(driver, client, enable_download_manager)));
+ driver->GetAutofillAgent()->SetUserGestureRequired(false);
+ driver->GetAutofillAgent()->SetSecureContextRequired(true);
+ driver->GetAutofillAgent()->SetFocusRequiresScroll(false);
+ driver->GetAutofillAgent()->SetQueryPasswordSuggestion(true);
}
AndroidAutofillManager::AndroidAutofillManager(
AutofillDriver* driver,
AutofillClient* client,
- AutofillManager::AutofillDownloadManagerState enable_download_manager)
+ EnableDownloadManager enable_download_manager)
: AutofillManager(driver,
client,
- enable_download_manager,
- version_info::Channel::UNKNOWN) {}
+ version_info::Channel::UNKNOWN,
+ enable_download_manager) {}
AndroidAutofillManager::~AndroidAutofillManager() = default;
+AutofillOfferManager* AndroidAutofillManager::GetOfferManager() {
+ return nullptr;
+}
+
+CreditCardAccessManager* AndroidAutofillManager::GetCreditCardAccessManager() {
+ return nullptr;
+}
+
+bool AndroidAutofillManager::ShouldClearPreviewedForm() {
+ return false;
+}
+
+void AndroidAutofillManager::FillCreditCardForm(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const CreditCard& credit_card,
+ const std::u16string& cvc) {
+ NOTREACHED();
+}
+
+void AndroidAutofillManager::FillProfileForm(
+ const autofill::AutofillProfile& profile,
+ const FormData& form,
+ const FormFieldData& field) {
+ NOTREACHED();
+}
+
void AndroidAutofillManager::OnFormSubmittedImpl(
const FormData& form,
bool known_success,
@@ -65,10 +94,12 @@ void AndroidAutofillManager::OnAskForValuesToFillImpl(
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) {
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) {
if (auto* provider = GetAutofillProvider()) {
provider->OnAskForValuesToFill(this, query_id, form, field, bounding_box,
- autoselect_first_suggestion);
+ autoselect_first_suggestion,
+ touch_to_fill_eligible);
}
}
@@ -117,8 +148,12 @@ void AndroidAutofillManager::OnHidePopup() {
void AndroidAutofillManager::SelectFieldOptionsDidChange(const FormData& form) {
}
+void AndroidAutofillManager::JavaScriptChangedAutofilledValue(
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) {}
+
void AndroidAutofillManager::PropagateAutofillPredictions(
- content::RenderFrameHost* rfh,
const std::vector<FormStructure*>& forms) {
has_server_prediction_ = true;
if (auto* provider = GetAutofillProvider())
diff --git a/chromium/components/android_autofill/browser/android_autofill_manager.h b/chromium/components/android_autofill/browser/android_autofill_manager.h
index 0a1896194f0..fd6358bc7e7 100644
--- a/chromium/components/android_autofill/browser/android_autofill_manager.h
+++ b/chromium/components/android_autofill/browser/android_autofill_manager.h
@@ -13,21 +13,43 @@
namespace autofill {
class AutofillProvider;
+class ContentAutofillDriver;
+
+// Creates an AndroidAutofillManager and attaches it to the `driver`.
+//
+// This hook is to be passed to CreateForWebContentsAndDelegate().
+// It is the glue between ContentAutofillDriver[Factory] and
+// AndroidAutofillManager.
+//
+// Other embedders (which don't want to use AndroidAutofillManager) shall use
+// other implementations.
+void AndroidDriverInitHook(
+ AutofillClient* client,
+ AutofillManager::EnableDownloadManager enable_download_manager,
+ ContentAutofillDriver* driver);
// This class forwards AutofillManager calls to AutofillProvider.
class AndroidAutofillManager : public AutofillManager {
public:
- static std::unique_ptr<AutofillManager> Create(
- AutofillDriver* driver,
- AutofillClient* client,
- const std::string& app_locale,
- AutofillManager::AutofillDownloadManagerState enable_download_manager);
-
AndroidAutofillManager(const AndroidAutofillManager&) = delete;
AndroidAutofillManager& operator=(const AndroidAutofillManager&) = delete;
~AndroidAutofillManager() override;
+ AutofillOfferManager* GetOfferManager() override;
+ CreditCardAccessManager* GetCreditCardAccessManager() override;
+
+ bool ShouldClearPreviewedForm() override;
+
+ void FillCreditCardForm(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const CreditCard& credit_card,
+ const std::u16string& cvc) override;
+ void FillProfileForm(const autofill::AutofillProfile& profile,
+ const FormData& form,
+ const FormFieldData& field) override;
+
void OnFocusNoLongerOnForm(bool had_interacted_form) override;
void OnDidFillAutofillFormData(const FormData& form,
@@ -40,6 +62,8 @@ class AndroidAutofillManager : public AutofillManager {
void Reset() override;
+ void ReportAutofillWebOTPMetrics(bool used_web_otp) override {}
+
base::WeakPtr<AndroidAutofillManager> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
@@ -52,10 +76,15 @@ class AndroidAutofillManager : public AutofillManager {
const FormData& form);
protected:
+ friend void AndroidDriverInitHook(
+ AutofillClient* client,
+ AutofillManager::EnableDownloadManager enable_download_manager,
+ ContentAutofillDriver* driver);
+
AndroidAutofillManager(
AutofillDriver* driver,
AutofillClient* client,
- AutofillManager::AutofillDownloadManagerState enable_download_manager);
+ AutofillManager::EnableDownloadManager enable_download_manager);
void OnFormSubmittedImpl(const FormData& form,
bool known_success,
@@ -70,11 +99,13 @@ class AndroidAutofillManager : public AutofillManager {
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
- void OnAskForValuesToFillImpl(int query_id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) override;
+ void OnAskForValuesToFillImpl(
+ int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) override;
void OnFocusOnFormFieldImpl(const FormData& form,
const FormFieldData& field,
@@ -84,6 +115,11 @@ class AndroidAutofillManager : public AutofillManager {
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
+ void JavaScriptChangedAutofilledValue(
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) override;
+
bool ShouldParseForms(const std::vector<FormData>& forms) override;
void OnBeforeProcessParsedForms() override {}
@@ -95,7 +131,6 @@ class AndroidAutofillManager : public AutofillManager {
const DenseSet<FormType>& form_types) override {}
void PropagateAutofillPredictions(
- content::RenderFrameHost* rfh,
const std::vector<FormStructure*>& forms) override;
void OnServerRequestError(FormSignature form_signature,
diff --git a/chromium/components/android_autofill/browser/autofill_provider.h b/chromium/components/android_autofill/browser/autofill_provider.h
index dca4a6b7fb8..685d1b9ca5f 100644
--- a/chromium/components/android_autofill/browser/autofill_provider.h
+++ b/chromium/components/android_autofill/browser/autofill_provider.h
@@ -34,12 +34,14 @@ class AutofillProvider : public content::WebContentsUserData<AutofillProvider> {
static bool is_download_manager_disabled_for_testing();
static void set_is_download_manager_disabled_for_testing();
- virtual void OnAskForValuesToFill(AndroidAutofillManager* manager,
- int32_t id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) = 0;
+ virtual void OnAskForValuesToFill(
+ AndroidAutofillManager* manager,
+ int32_t query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) = 0;
virtual void OnTextFieldDidChange(AndroidAutofillManager* manager,
const FormData& form,
diff --git a/chromium/components/android_autofill/browser/autofill_provider_android.cc b/chromium/components/android_autofill/browser/autofill_provider_android.cc
index f8b9fe7708c..01768e85cad 100644
--- a/chromium/components/android_autofill/browser/autofill_provider_android.cc
+++ b/chromium/components/android_autofill/browser/autofill_provider_android.cc
@@ -102,16 +102,17 @@ void AutofillProviderAndroid::DetachFromJavaAutofillProvider(JNIEnv* env) {
void AutofillProviderAndroid::OnAskForValuesToFill(
AndroidAutofillManager* manager,
- int32_t id,
+ int32_t query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool /*unused_autoselect_first_suggestion*/) {
+ bool /*unused_autoselect_first_suggestion*/,
+ TouchToFillEligible /*unused_touch_to_fill_eligible*/) {
// The id isn't passed to Java side because Android API guarantees the
// response is always for current session, so we just use the current id
// in response, see OnAutofillAvailable.
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- id_ = id;
+ id_ = query_id;
// Focus or field value change will also trigger the query, so it should be
// ignored if the form is same.
diff --git a/chromium/components/android_autofill/browser/autofill_provider_android.h b/chromium/components/android_autofill/browser/autofill_provider_android.h
index b8c405a2a0c..873d77f7cbb 100644
--- a/chromium/components/android_autofill/browser/autofill_provider_android.h
+++ b/chromium/components/android_autofill/browser/autofill_provider_android.h
@@ -49,11 +49,12 @@ class AutofillProviderAndroid : public AutofillProvider {
// AutofillProvider:
void OnAskForValuesToFill(
AndroidAutofillManager* manager,
- int32_t id,
+ int32_t query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool /*unused_autoselect_first_suggestion*/) override;
+ bool /*unused_autoselect_first_suggestion*/,
+ TouchToFillEligible /*unused_touch_to_fill_eligible*/) override;
void OnTextFieldDidChange(AndroidAutofillManager* manager,
const FormData& form,
const FormFieldData& field,
diff --git a/chromium/components/android_autofill/browser/autofill_provider_unittest.cc b/chromium/components/android_autofill/browser/autofill_provider_unittest.cc
index 05685586c9e..b205f8663fa 100644
--- a/chromium/components/android_autofill/browser/autofill_provider_unittest.cc
+++ b/chromium/components/android_autofill/browser/autofill_provider_unittest.cc
@@ -15,19 +15,18 @@ namespace autofill {
class AndroidAutofillManagerTestHelper : public AndroidAutofillManager {
public:
explicit AndroidAutofillManagerTestHelper(AutofillProvider* autofill_provider)
- : AndroidAutofillManager(nullptr,
- nullptr,
- DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {
+ : AndroidAutofillManager(nullptr, nullptr, EnableDownloadManager(false)) {
set_autofill_provider_for_testing(autofill_provider);
}
void SimulatePropagateAutofillPredictions() {
- PropagateAutofillPredictions(nullptr, std::vector<FormStructure*>());
+ PropagateAutofillPredictions({});
}
void SimulateOnAskForValuesToFillImpl() {
OnAskForValuesToFillImpl(0, FormData(), FormFieldData(), gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ /*autoselect_first_suggestion=*/false,
+ TouchToFillEligible(false));
}
};
@@ -40,12 +39,14 @@ class AutofillProviderTestHelper : public TestAutofillProvider {
private:
// AutofillProvider
- void OnAskForValuesToFill(AndroidAutofillManager* manager,
- int32_t id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) override {
+ void OnAskForValuesToFill(
+ AndroidAutofillManager* manager,
+ int32_t query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) override {
manager_ = manager;
}
void OnServerQueryRequestError(AndroidAutofillManager* manager,
diff --git a/chromium/components/android_autofill/browser/form_field_data_android.cc b/chromium/components/android_autofill/browser/form_field_data_android.cc
index b91ff6a6694..b0f8e5bbb1b 100644
--- a/chromium/components/android_autofill/browser/form_field_data_android.cc
+++ b/chromium/components/android_autofill/browser/form_field_data_android.cc
@@ -98,7 +98,7 @@ ScopedJavaLocalRef<jobject> FormFieldDataAndroid::GetJavaPeer() {
jheuristic_type, jserver_type, jcomputed_type, jserver_predictions,
field_ptr_->bounds.x(), field_ptr_->bounds.y(),
field_ptr_->bounds.right(), field_ptr_->bounds.bottom(),
- jdatalist_values, jdatalist_labels, field_ptr_->IsVisible());
+ jdatalist_values, jdatalist_labels, field_ptr_->IsFocusable());
java_ref_ = JavaObjectWeakGlobalRef(env, obj);
}
return obj;
diff --git a/chromium/components/android_autofill/browser/test_autofill_provider.h b/chromium/components/android_autofill/browser/test_autofill_provider.h
index dcb28fa9815..666b67ef60d 100644
--- a/chromium/components/android_autofill/browser/test_autofill_provider.h
+++ b/chromium/components/android_autofill/browser/test_autofill_provider.h
@@ -20,12 +20,14 @@ class TestAutofillProvider : public AutofillProvider {
~TestAutofillProvider() override = default;
// AutofillProvider:
- void OnAskForValuesToFill(AndroidAutofillManager* manager,
- int32_t id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) override {}
+ void OnAskForValuesToFill(
+ AndroidAutofillManager* manager,
+ int32_t query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) override {}
void OnTextFieldDidChange(AndroidAutofillManager* manager,
const FormData& form,
const FormFieldData& field,
diff --git a/chromium/components/android_autofill/browser/test_support/BUILD.gn b/chromium/components/android_autofill/browser/test_support/BUILD.gn
index b9cfb47f865..98f6a97e80f 100644
--- a/chromium/components/android_autofill/browser/test_support/BUILD.gn
+++ b/chromium/components/android_autofill/browser/test_support/BUILD.gn
@@ -14,9 +14,9 @@ android_library("component_autofill_provider_java_test_support") {
"java/src/org/chromium/components/autofill/AutofillProviderTestHelper.java",
]
deps = [
- "//base:base_java",
"//base:base_java_test_support",
"//base:jni_java",
+ "//build/android:build_java",
"//components/android_autofill/browser:autofill_aidl",
"//components/android_autofill/browser:java",
"//content/public/android:content_java",
diff --git a/chromium/components/android_autofill/browser/test_support/autofill_provider_test_helper.cc b/chromium/components/android_autofill/browser/test_support/autofill_provider_test_helper.cc
index 603eaa1a267..0fb6829f4dd 100644
--- a/chromium/components/android_autofill/browser/test_support/autofill_provider_test_helper.cc
+++ b/chromium/components/android_autofill/browser/test_support/autofill_provider_test_helper.cc
@@ -39,7 +39,7 @@ AutofillManager* ToMainFrameAutofillManager(
content::WebContents::FromJavaWebContents(jweb_contents);
CHECK(web_contents);
AutofillManager* autofill_manager =
- GetAutofillManager(web_contents, web_contents->GetMainFrame());
+ GetAutofillManager(web_contents, web_contents->GetPrimaryMainFrame());
CHECK(autofill_manager);
return autofill_manager;
}
diff --git a/chromium/components/android_system_error_page/error_page_populator.cc b/chromium/components/android_system_error_page/error_page_populator.cc
index a0378b603f9..9409e323f5e 100644
--- a/chromium/components/android_system_error_page/error_page_populator.cc
+++ b/chromium/components/android_system_error_page/error_page_populator.cc
@@ -5,11 +5,11 @@
#include "components/android_system_error_page/error_page_populator.h"
#include "base/i18n/rtl.h"
+#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/grit/components_resources.h"
#include "components/strings/grit/components_strings.h"
-#include "net/base/escape.h"
#include "net/base/net_errors.h"
#include "third_party/blink/public/platform/web_url_error.h"
#include "ui/base/l10n/l10n_util.h"
@@ -43,7 +43,7 @@ void PopulateErrorPageHtml(const blink::WebURLError& error,
if (err.empty())
reason_id = IDS_ANDROID_ERROR_PAGE_WEBPAGE_TEMPORARILY_DOWN;
- std::string escaped_url = net::EscapeForHTML(url_string);
+ std::string escaped_url = base::EscapeForHTML(url_string);
std::vector<std::string> replacements;
replacements.push_back(
l10n_util::GetStringUTF8(IDS_ANDROID_ERROR_PAGE_WEBPAGE_NOT_AVAILABLE));
diff --git a/chromium/components/apdu/apdu_command.cc b/chromium/components/apdu/apdu_command.cc
index fedbcc7b8fe..de0edbeff3d 100644
--- a/chromium/components/apdu/apdu_command.cc
+++ b/chromium/components/apdu/apdu_command.cc
@@ -4,6 +4,8 @@
#include "components/apdu/apdu_command.h"
+#include "base/check_op.h"
+
namespace apdu {
namespace {
diff --git a/chromium/components/app_restore/BUILD.gn b/chromium/components/app_restore/BUILD.gn
index 8d73031c675..117457c69be 100644
--- a/chromium/components/app_restore/BUILD.gn
+++ b/chromium/components/app_restore/BUILD.gn
@@ -37,6 +37,8 @@ component("app_restore") {
"lacros_save_handler.h",
"restore_data.cc",
"restore_data.h",
+ "tab_group_info.cc",
+ "tab_group_info.h",
"window_info.cc",
"window_info.h",
"window_properties.cc",
@@ -55,6 +57,7 @@ component("app_restore") {
"//components/services/app_service/public/cpp:intents",
"//components/services/app_service/public/mojom",
"//components/sessions:session_id",
+ "//components/tab_groups",
"//ui/aura",
"//ui/views",
]
diff --git a/chromium/components/app_restore/DEPS b/chromium/components/app_restore/DEPS
index 48bc9b6b18a..510d8d22bfb 100644
--- a/chromium/components/app_restore/DEPS
+++ b/chromium/components/app_restore/DEPS
@@ -5,6 +5,7 @@ include_rules = [
"+components/account_id/account_id.h",
"+components/services/app_service/public",
"+components/sessions/core/session_id.h",
+ "+components/tab_groups",
"+content/public/test",
"+ui",
]
diff --git a/chromium/components/app_restore/app_launch_info.h b/chromium/components/app_restore/app_launch_info.h
index 7398aca63b7..284849631af 100644
--- a/chromium/components/app_restore/app_launch_info.h
+++ b/chromium/components/app_restore/app_launch_info.h
@@ -9,6 +9,7 @@
#include "base/component_export.h"
#include "base/files/file_path.h"
+#include "components/app_restore/tab_group_info.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/window_open_disposition.h"
@@ -56,6 +57,7 @@ struct COMPONENT_EXPORT(APP_RESTORE) AppLaunchInfo {
~AppLaunchInfo();
+ // TODO(1326250): Remove optional wrappers around vector fields.
std::string app_id;
absl::optional<int32_t> window_id;
absl::optional<int32_t> event_flag;
@@ -70,6 +72,11 @@ struct COMPONENT_EXPORT(APP_RESTORE) AppLaunchInfo {
absl::optional<apps::mojom::IntentPtr> intent;
absl::optional<bool> app_type_browser;
absl::optional<std::string> app_name;
+ // For Browsers only, represents tab groups associated with this browser
+ // instance if there are any. This is only used in Desks Storage, tab groups
+ // in full restore are persistsed by sessions. This field is not converted to
+ // base::Value in base value conversions.
+ absl::optional<std::vector<TabGroupInfo>> tab_group_infos;
};
} // namespace app_restore
diff --git a/chromium/components/app_restore/app_restore_data.cc b/chromium/components/app_restore/app_restore_data.cc
index c5762da1471..acaea5a402a 100644
--- a/chromium/components/app_restore/app_restore_data.cc
+++ b/chromium/components/app_restore/app_restore_data.cc
@@ -273,6 +273,7 @@ AppRestoreData::AppRestoreData(std::unique_ptr<AppLaunchInfo> app_launch_info) {
intent = std::move(app_launch_info->intent);
app_type_browser = std::move(app_launch_info->app_type_browser);
app_name = std::move(app_launch_info->app_name);
+ tab_group_infos = std::move(app_launch_info->tab_group_infos);
}
AppRestoreData::~AppRestoreData() = default;
@@ -349,6 +350,9 @@ std::unique_ptr<AppRestoreData> AppRestoreData::Clone() const {
if (status_bar_color.has_value())
data->status_bar_color = status_bar_color.value();
+ if (tab_group_infos.has_value())
+ data->tab_group_infos = tab_group_infos.value();
+
return data;
}
@@ -529,6 +533,7 @@ std::unique_ptr<AppLaunchInfo> AppRestoreData::GetAppLaunchInfo(
app_launch_info->intent = intent->Clone();
app_launch_info->app_type_browser = app_type_browser;
app_launch_info->app_name = app_name;
+ app_launch_info->tab_group_infos = tab_group_infos;
return app_launch_info;
}
diff --git a/chromium/components/app_restore/app_restore_data.h b/chromium/components/app_restore/app_restore_data.h
index 8be25bcf255..8fe05147de8 100644
--- a/chromium/components/app_restore/app_restore_data.h
+++ b/chromium/components/app_restore/app_restore_data.h
@@ -10,6 +10,7 @@
#include "base/component_export.h"
#include "chromeos/ui/base/window_state_type.h"
+#include "components/app_restore/tab_group_info.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/ui_base_types.h"
@@ -81,6 +82,7 @@ struct COMPONENT_EXPORT(APP_RESTORE) AppRestoreData {
apps::mojom::WindowInfoPtr GetAppWindowInfo() const;
// App launch parameters.
+ // TODO(crbug.com/1326250): Remove optional wrappers around vector fields.
absl::optional<int32_t> event_flag;
absl::optional<int32_t> container;
absl::optional<int32_t> disposition;
@@ -105,6 +107,11 @@ struct COMPONENT_EXPORT(APP_RESTORE) AppRestoreData {
// `snap_percentage` of 60 when the display is in portrait means the height is
// 60 percent of the work area height.
absl::optional<uint32_t> snap_percentage;
+ // For Browsers only, represents tab groups associtated with this browser
+ // instance if there are any. This is only used in Desks Storage, tab groups
+ // in full restore are persistsed by sessions. This field is not converted to
+ // base::value in base value conversions.
+ absl::optional<std::vector<TabGroupInfo>> tab_group_infos;
// Extra ARC window's information.
absl::optional<gfx::Size> minimum_size;
diff --git a/chromium/components/app_restore/app_restore_utils.cc b/chromium/components/app_restore/app_restore_utils.cc
index 15e6e4caf82..9da30f8c716 100644
--- a/chromium/components/app_restore/app_restore_utils.cc
+++ b/chromium/components/app_restore/app_restore_utils.cc
@@ -5,7 +5,6 @@
#include "components/app_restore/app_restore_utils.h"
#include "ash/constants/app_types.h"
-#include "ash/constants/ash_features.h"
#include "base/bind.h"
#include "components/app_restore/app_restore_info.h"
#include "components/app_restore/desk_template_read_handler.h"
@@ -27,10 +26,8 @@ static int32_t session_id_counter = kArcSessionIdOffsetForRestoredLaunching;
// Always use the full restore ARC data if ARC apps for desks templates is not
// enabled.
bool ShouldUseFullRestoreArcData() {
- return ash::features::AreDesksTemplatesEnabled()
- ? full_restore::FullRestoreReadHandler::GetInstance()
- ->IsFullRestoreRunning()
- : true;
+ return full_restore::FullRestoreReadHandler::GetInstance()
+ ->IsFullRestoreRunning();
}
} // namespace
@@ -73,11 +70,11 @@ void ApplyProperties(app_restore::WindowInfo* window_info,
// are shown are activated by default. Force the widget to not be
// activatable; the activation will be restored in ash once the window is
// launched.
- property_handler->SetProperty(app_restore::kLaunchedFromFullRestoreKey,
+ property_handler->SetProperty(app_restore::kLaunchedFromAppRestoreKey,
true);
}
if (window_info->pre_minimized_show_state_type) {
- property_handler->SetProperty(aura::client::kPreMinimizedShowStateKey,
+ property_handler->SetProperty(aura::client::kRestoreShowStateKey,
*window_info->pre_minimized_show_state_type);
}
}
@@ -223,19 +220,4 @@ int32_t GetLacrosRestoreWindowId(const std::string& lacros_window_id) {
->GetLacrosRestoreWindowId(lacros_window_id);
}
-void OnLacrosWindowAdded(aura::Window* const window,
- uint32_t browser_session_id,
- uint32_t restored_browser_session_id,
- bool is_browser_app) {
- if (!IsLacrosWindow(window))
- return;
-
- full_restore::FullRestoreReadHandler::GetInstance()
- ->OnLacrosBrowserWindowAdded(window, restored_browser_session_id);
-
- full_restore::FullRestoreSaveHandler::GetInstance()
- ->OnLacrosBrowserWindowAdded(window, browser_session_id,
- restored_browser_session_id, is_browser_app);
-}
-
} // namespace app_restore
diff --git a/chromium/components/app_restore/app_restore_utils.h b/chromium/components/app_restore/app_restore_utils.h
index a61fbcd557f..a3e9d8c9ca2 100644
--- a/chromium/components/app_restore/app_restore_utils.h
+++ b/chromium/components/app_restore/app_restore_utils.h
@@ -89,16 +89,6 @@ const std::string GetLacrosWindowId(aura::Window* window);
COMPONENT_EXPORT(APP_RESTORE)
int32_t GetLacrosRestoreWindowId(const std::string& lacros_window_id);
-// Invoked when Lacros window is created. `browser_session_id` is the
-// current browser session id. `restored_browser_session_id` is the restored
-// browser session id. `is_browser_app` is true if it's an app type Lacros
-// browser window.
-COMPONENT_EXPORT(APP_RESTORE)
-void OnLacrosWindowAdded(aura::Window* const window,
- uint32_t browser_session_id,
- uint32_t restored_browser_session_id,
- bool is_browser_app);
-
} // namespace app_restore
#endif // COMPONENTS_APP_RESTORE_APP_RESTORE_UTILS_H_
diff --git a/chromium/components/app_restore/desk_template_read_handler.cc b/chromium/components/app_restore/desk_template_read_handler.cc
index 1b7815dff3c..ae0ff6e43bf 100644
--- a/chromium/components/app_restore/desk_template_read_handler.cc
+++ b/chromium/components/app_restore/desk_template_read_handler.cc
@@ -5,7 +5,6 @@
#include "components/app_restore/desk_template_read_handler.h"
#include "ash/constants/app_types.h"
-#include "ash/constants/ash_features.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/no_destructor.h"
@@ -63,9 +62,6 @@ void DeskTemplateReadHandler::SetRestoreData(
restore_data_[launch_id] = std::move(restore_data);
- if (!ash::features::AreDesksTemplatesEnabled())
- return;
-
// Set up mapping from restore window IDs to launch ID. Create an ARC read
// handler and add restore data to it if we have at least one ARC app.
for (const auto& [app_id, launch_list] : rd->app_id_to_launch_list()) {
diff --git a/chromium/components/app_restore/features.cc b/chromium/components/app_restore/features.cc
index 5ed1e9aa8d6..27a60ac9098 100644
--- a/chromium/components/app_restore/features.cc
+++ b/chromium/components/app_restore/features.cc
@@ -7,19 +7,12 @@
namespace full_restore {
namespace features {
-const base::Feature kArcGhostWindow{"ArcGhostWindow",
- base::FEATURE_ENABLED_BY_DEFAULT};
-
const base::Feature kArcWindowPredictor{"ArcWindowPredictor",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kFullRestoreForLacros{"FullRestoreForLacros",
base::FEATURE_ENABLED_BY_DEFAULT};
-bool IsArcGhostWindowEnabled() {
- return base::FeatureList::IsEnabled(kArcGhostWindow);
-}
-
bool IsArcWindowPredictorEnabled() {
return base::FeatureList::IsEnabled(kArcWindowPredictor);
}
diff --git a/chromium/components/app_restore/features.h b/chromium/components/app_restore/features.h
index e0d35ae562a..c27f545b1da 100644
--- a/chromium/components/app_restore/features.h
+++ b/chromium/components/app_restore/features.h
@@ -11,10 +11,6 @@
namespace full_restore {
namespace features {
-// Enables the pre-load app window for ARC++ app during ARCVM booting stage on
-// full restore process.
-COMPONENT_EXPORT(APP_RESTORE) extern const base::Feature kArcGhostWindow;
-
// Enables the window state and bounds predictor and full ghost window for ARC++
// apps.
COMPONENT_EXPORT(APP_RESTORE) extern const base::Feature kArcWindowPredictor;
@@ -23,8 +19,6 @@ COMPONENT_EXPORT(APP_RESTORE) extern const base::Feature kArcWindowPredictor;
// restore apps and app windows opened with Lacros after a crash or reboot.
COMPONENT_EXPORT(APP_RESTORE) extern const base::Feature kFullRestoreForLacros;
-COMPONENT_EXPORT(APP_RESTORE) bool IsArcGhostWindowEnabled();
-
COMPONENT_EXPORT(APP_RESTORE) bool IsArcWindowPredictorEnabled();
COMPONENT_EXPORT(APP_RESTORE) bool IsFullRestoreForLacrosEnabled();
diff --git a/chromium/components/app_restore/full_restore_read_and_save_unittest.cc b/chromium/components/app_restore/full_restore_read_and_save_unittest.cc
index 5853f5a11e3..132be9d9a00 100644
--- a/chromium/components/app_restore/full_restore_read_and_save_unittest.cc
+++ b/chromium/components/app_restore/full_restore_read_and_save_unittest.cc
@@ -281,7 +281,9 @@ class FullRestoreReadAndSaveTest : public testing::Test {
}
std::unique_ptr<views::Widget> CreateLacrosWidget(
- const std::string& lacros_window_id) {
+ const std::string& lacros_window_id,
+ int32_t restore_session_id,
+ int32_t restore_window_id) {
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(5, 5, 20, 20);
@@ -292,6 +294,12 @@ class FullRestoreReadAndSaveTest : public testing::Test {
aura::client::kAppType, static_cast<int>(ash::AppType::LACROS));
params.init_properties_container.SetProperty(app_restore::kLacrosWindowId,
lacros_window_id);
+
+ params.init_properties_container.SetProperty(app_restore::kWindowIdKey,
+ restore_session_id);
+ params.init_properties_container.SetProperty(
+ app_restore::kRestoreWindowIdKey, restore_window_id);
+
auto widget = std::make_unique<views::Widget>();
widget->Init(std::move(params));
@@ -300,13 +308,17 @@ class FullRestoreReadAndSaveTest : public testing::Test {
}
std::unique_ptr<aura::Window> CreateLacrosWindow(
- const std::string& lacros_window_id) {
+ const std::string& lacros_window_id,
+ int32_t restore_session_id,
+ int32_t restore_window_id) {
auto window = std::make_unique<aura::Window>(
nullptr, aura::client::WINDOW_TYPE_NORMAL);
window->SetProperty(aura::client::kAppType,
static_cast<int>(ash::AppType::LACROS));
window->SetProperty(app_restore::kLacrosWindowId,
std::string(kLacrosWindowId));
+ window->SetProperty(app_restore::kWindowIdKey, restore_session_id);
+ window->SetProperty(app_restore::kRestoreWindowIdKey, restore_window_id);
return window;
}
@@ -800,6 +812,11 @@ TEST_F(FullRestoreReadAndSaveTest, ArcWindowRestore) {
ASSERT_TRUE(restore_data);
FullRestoreReadHandler* read_handler = FullRestoreReadHandler::GetInstance();
+ // The following is necessary for making `ShouldUseFullRestoreArcData()` and
+ // `read_handler->IsFullRestoreRunning()` return true;
+ read_handler->SetActiveProfilePath(GetPath());
+ read_handler->SetStartTimeForProfile(GetPath());
+
FullRestoreReadHandlerTestApi read_test_api(read_handler);
ASSERT_TRUE(read_test_api.GetArcReadHander());
EXPECT_EQ(1u, read_test_api.GetArcWindowIdMap().size());
@@ -948,12 +965,10 @@ TEST_F(FullRestoreReadAndSaveTest, LacrosBrowserWindowSavingCreateWindowFirst) {
ASSERT_TRUE(lacros_save_handler);
// Create a browser window first, then OnLacrosWindowAdded is called later.
- auto widget = CreateLacrosWidget(kLacrosWindowId);
+ auto widget = CreateLacrosWidget(kLacrosWindowId, kBrowserSessionId,
+ /*restored_browser_session_id=*/0);
auto* window = widget->GetNativeWindow();
SaveWindowInfo(window, kActivationIndex1);
- app_restore::OnLacrosWindowAdded(window, kBrowserSessionId,
- /*restored_browser_session_id=*/0,
- /*is_browser_app=*/false);
// Verify the browser window is saved.
EXPECT_EQ(app_constants::kLacrosAppId,
@@ -984,7 +999,7 @@ TEST_F(FullRestoreReadAndSaveTest, LacrosBrowserWindowSavingCreateWindowFirst) {
EXPECT_TRUE(restore_data->app_id_to_launch_list().empty());
}
-// Verify the Lacros Chrome app window is saved correctly when
+// Verify the Lacros browser window is saved correctly when
// OnLacrosWindowAdded is called first, then the window is init later.
TEST_F(FullRestoreReadAndSaveTest,
LacrosBrowserWindowSavingOnLacrosWindowAddedCalledFirst) {
@@ -997,10 +1012,8 @@ TEST_F(FullRestoreReadAndSaveTest,
ASSERT_TRUE(lacros_save_handler);
// OnLacrosWindowAdded is called first, then init the browser window later.
- auto window = CreateLacrosWindow(kLacrosWindowId);
- app_restore::OnLacrosWindowAdded(window.get(), kBrowserSessionId,
- /*restored_browser_session_id=*/0,
- /*is_browser_app=*/false);
+ auto window = CreateLacrosWindow(kLacrosWindowId, kBrowserSessionId,
+ /*restored_browser_session_id=*/0);
window->Init(ui::LAYER_NOT_DRAWN);
SaveWindowInfo(window.get(), kActivationIndex1);
@@ -1052,7 +1065,8 @@ TEST_F(FullRestoreReadAndSaveTest,
// Create a Chrome app window first, then the crosapi OnAppWindowAdded is
// called later.
- auto widget = CreateLacrosWidget(kLacrosWindowId);
+ auto widget = CreateLacrosWidget(kLacrosWindowId, kBrowserSessionId,
+ /*restored_browser_session_id=*/0);
auto* window = widget->GetNativeWindow();
EXPECT_FALSE(test_api.GetLacrosWindowCandidates().empty());
SaveWindowInfo(window, kActivationIndex1);
@@ -1117,7 +1131,8 @@ TEST_F(FullRestoreReadAndSaveTest,
// window later.
OnLacrosChromeAppWindowAdded(kAppId, kLacrosWindowId);
EXPECT_FALSE(test_api.GetLacrosWindowIdToAppIdMap().empty());
- auto widget = CreateLacrosWidget(kLacrosWindowId);
+ auto widget = CreateLacrosWidget(kLacrosWindowId, kBrowserSessionId,
+ /*restored_browser_session_id=*/0);
auto* window = widget->GetNativeWindow();
EXPECT_FALSE(test_api.GetLacrosWindowCandidates().empty());
SaveWindowInfo(window, kActivationIndex1);
diff --git a/chromium/components/app_restore/full_restore_read_handler.cc b/chromium/components/app_restore/full_restore_read_handler.cc
index 6e4454103e4..c694cef4d5f 100644
--- a/chromium/components/app_restore/full_restore_read_handler.cc
+++ b/chromium/components/app_restore/full_restore_read_handler.cc
@@ -78,6 +78,10 @@ void FullRestoreReadHandler::OnWindowInitialized(aura::Window* window) {
window_id == app_restore::kParentToHiddenContainer) {
observed_windows_.AddObservation(window);
}
+
+ if (lacros_read_handler_)
+ lacros_read_handler_->OnWindowInitialized(window);
+
return;
}
@@ -164,15 +168,6 @@ void FullRestoreReadHandler::OnTaskDestroyed(int32_t task_id) {
arc_read_handler_->OnTaskDestroyed(task_id);
}
-void FullRestoreReadHandler::OnLacrosBrowserWindowAdded(
- aura::Window* const window,
- uint32_t restored_browser_session_id) {
- if (lacros_read_handler_) {
- lacros_read_handler_->OnLacrosBrowserWindowAdded(
- window, restored_browser_session_id);
- }
-}
-
void FullRestoreReadHandler::OnLacrosChromeAppWindowAdded(
const std::string& app_id,
const std::string& window_id) {
diff --git a/chromium/components/app_restore/full_restore_read_handler.h b/chromium/components/app_restore/full_restore_read_handler.h
index d84075ea5f2..5d6c04e911d 100644
--- a/chromium/components/app_restore/full_restore_read_handler.h
+++ b/chromium/components/app_restore/full_restore_read_handler.h
@@ -95,11 +95,6 @@ class COMPONENT_EXPORT(APP_RESTORE) FullRestoreReadHandler
int32_t session_id) override;
void OnTaskDestroyed(int32_t task_id) override;
- // Invoked when Lacros window is created. `restored_browser_session_id` is the
- // restored browser session id.
- void OnLacrosBrowserWindowAdded(aura::Window* const window,
- uint32_t restored_browser_session_id);
-
// Invoked when an Chrome app Lacros window is created. `app_id` is the
// AppService id, and `window_id` is the wayland app_id property for the
// window.
diff --git a/chromium/components/app_restore/full_restore_save_handler.cc b/chromium/components/app_restore/full_restore_save_handler.cc
index 78eb80329e8..daef016b7a7 100644
--- a/chromium/components/app_restore/full_restore_save_handler.cc
+++ b/chromium/components/app_restore/full_restore_save_handler.cc
@@ -326,18 +326,6 @@ void FullRestoreSaveHandler::SaveWindowInfo(
ModifyWindowInfo(window_id, window_info);
}
-void FullRestoreSaveHandler::OnLacrosBrowserWindowAdded(
- aura::Window* const window,
- uint32_t browser_session_id,
- uint32_t restored_browser_session_id,
- bool is_browser_app) {
- if (lacros_save_handler_) {
- lacros_save_handler_->OnBrowserWindowAdded(window, browser_session_id,
- restored_browser_session_id,
- is_browser_app);
- }
-}
-
void FullRestoreSaveHandler::OnLacrosChromeAppWindowAdded(
const std::string& app_id,
const std::string& window_id) {
diff --git a/chromium/components/app_restore/full_restore_save_handler.h b/chromium/components/app_restore/full_restore_save_handler.h
index b131ecea7b6..6df1ab246ad 100644
--- a/chromium/components/app_restore/full_restore_save_handler.h
+++ b/chromium/components/app_restore/full_restore_save_handler.h
@@ -110,15 +110,6 @@ class COMPONENT_EXPORT(APP_RESTORE) FullRestoreSaveHandler
// Saves |window_info| to |profile_path_to_restore_data_|.
void SaveWindowInfo(const app_restore::WindowInfo& window_info);
- // Invoked when Lacros window is created. `browser_session_id` is the
- // current browser session id for `window`.`restored_browser_session_id` is
- // the restored browser session id. `is_browser_app` is true if it's an app
- // type Lacros browser window.
- void OnLacrosBrowserWindowAdded(aura::Window* const window,
- uint32_t browser_session_id,
- uint32_t restored_browser_session_id,
- bool is_browser_app);
-
// Invoked when an Chrome app Lacros window is created. `app_id` is the
// AppService id, and `window_id` is the wayland app_id property for the
// window.
diff --git a/chromium/components/app_restore/lacros_read_handler.cc b/chromium/components/app_restore/lacros_read_handler.cc
index 4b82de875c5..4dcbad96624 100644
--- a/chromium/components/app_restore/lacros_read_handler.cc
+++ b/chromium/components/app_restore/lacros_read_handler.cc
@@ -21,17 +21,14 @@ LacrosReadHandler::LacrosReadHandler(const base::FilePath& profile_path)
LacrosReadHandler::~LacrosReadHandler() = default;
-void LacrosReadHandler::AddRestoreData(const std::string& app_id,
- int32_t window_id) {
- restore_window_id_to_app_id_[window_id] = app_id;
-}
-
-void LacrosReadHandler::OnLacrosBrowserWindowAdded(
- aura::Window* const window,
- int32_t restored_browser_session_id) {
+void LacrosReadHandler::OnWindowInitialized(aura::Window* window) {
+ // TODO(sophiewen): Test this in full_restore_read_and_save_unittest.
if (!IsLacrosWindow(window))
return;
+ int32_t restored_browser_session_id =
+ window->GetProperty(app_restore::kRestoreWindowIdKey);
+
auto it = window_to_window_data_.find(window);
if (it != window_to_window_data_.end() &&
it->second.restore_window_id == restored_browser_session_id) {
@@ -54,6 +51,11 @@ void LacrosReadHandler::OnLacrosBrowserWindowAdded(
UpdateWindow(window);
}
+void LacrosReadHandler::AddRestoreData(const std::string& app_id,
+ int32_t window_id) {
+ restore_window_id_to_app_id_[window_id] = app_id;
+}
+
void LacrosReadHandler::OnAppWindowAdded(const std::string& app_id,
const std::string& lacros_window_id) {
lacros_window_id_to_app_id_[lacros_window_id] = app_id;
diff --git a/chromium/components/app_restore/lacros_read_handler.h b/chromium/components/app_restore/lacros_read_handler.h
index c354c97fae7..932ee0a2f24 100644
--- a/chromium/components/app_restore/lacros_read_handler.h
+++ b/chromium/components/app_restore/lacros_read_handler.h
@@ -48,15 +48,13 @@ class COMPONENT_EXPORT(APP_RESTORE) LacrosReadHandler {
LacrosReadHandler& operator=(const LacrosReadHandler&) = delete;
~LacrosReadHandler();
+ // Invoked when `window` is initialized.
+ void OnWindowInitialized(aura::Window* window);
+
// Sets `app_id` and `window_id` to `restore_window_id_to_app_id_` to record
// that there is a restore data for `app_id` and `window_id`.
void AddRestoreData(const std::string& app_id, int32_t window_id);
- // Invoked when Lacros window is created. `restored_browser_session_id` is the
- // restored browser session id.
- void OnLacrosBrowserWindowAdded(aura::Window* const window,
- int32_t restored_browser_session_id);
-
// Invoked when an Chrome app Lacros window is created. `app_id` is the
// AppService id, and `window_id` is the wayland app_id property for the
// window.
diff --git a/chromium/components/app_restore/lacros_save_handler.cc b/chromium/components/app_restore/lacros_save_handler.cc
index 8a4bbd8c22c..c5832e417a5 100644
--- a/chromium/components/app_restore/lacros_save_handler.cc
+++ b/chromium/components/app_restore/lacros_save_handler.cc
@@ -24,19 +24,12 @@ LacrosSaveHandler::~LacrosSaveHandler() = default;
void LacrosSaveHandler::OnWindowInitialized(aura::Window* window) {
const std::string lacros_window_id = app_restore::GetLacrosWindowId(window);
- // If `window` has been saved by OnBrowserWindowAdded, we don't need to save
- // again.
- if (base::Contains(window_candidates_, lacros_window_id))
- return;
-
std::string app_id;
int32_t window_id = ++window_id_;
std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info;
auto it = lacros_window_id_to_app_id_.find(lacros_window_id);
if (it != lacros_window_id_to_app_id_.end()) {
- // For Chrome app windows, get the app launch info and set the Chrome app
- // id.
app_id = it->second;
app_launch_info = FullRestoreSaveHandler::GetInstance()->FetchAppLaunchInfo(
profile_path_, app_id);
@@ -52,6 +45,15 @@ void LacrosSaveHandler::OnWindowInitialized(aura::Window* window) {
window_candidates_[lacros_window_id].app_id = app_id;
window_candidates_[lacros_window_id].window_id = window_id;
+ // Don't overwrite window info if `window_id` was already saved by
+ // OnAppWindowAdded.
+ if (it == lacros_window_id_to_app_id_.end()) {
+ // TODO(sophiewen): Move logic to OnWindowInitialized instead of calling
+ // OnBrowserWindowAdded.
+ OnBrowserWindowAdded(window, false);
+ return;
+ }
+
FullRestoreSaveHandler::GetInstance()->AddAppLaunchInfo(
profile_path_, std::move(app_launch_info));
}
@@ -70,16 +72,16 @@ void LacrosSaveHandler::OnWindowDestroyed(aura::Window* window) {
window_candidates_.erase(it);
}
-void LacrosSaveHandler::OnBrowserWindowAdded(
- aura::Window* const window,
- uint32_t browser_session_id,
- uint32_t restored_browser_session_id,
- bool is_browser_app) {
+void LacrosSaveHandler::OnBrowserWindowAdded(aura::Window* const window,
+ bool is_browser_app) {
const std::string lacros_window_id = app_restore::GetLacrosWindowId(window);
std::unique_ptr<app_restore::WindowInfo> window_info;
auto* save_handler = FullRestoreSaveHandler::GetInstance();
DCHECK(save_handler);
+ uint32_t browser_session_id =
+ static_cast<uint32_t>(window->GetProperty(app_restore::kWindowIdKey));
+
auto it = window_candidates_.find(lacros_window_id);
if (it != window_candidates_.end() &&
it->second.window_id != browser_session_id) {
@@ -96,13 +98,6 @@ void LacrosSaveHandler::OnBrowserWindowAdded(
window_candidates_[lacros_window_id].app_id = app_constants::kLacrosAppId;
window_candidates_[lacros_window_id].window_id = browser_session_id;
- // TODO(xdai): Remove this once crbug.com/1291799 is fixed. These two window
- // properties are supposed to be set correctly before the widnow is created.
- window->SetProperty(app_restore::kWindowIdKey,
- static_cast<int32_t>(browser_session_id));
- window->SetProperty(app_restore::kRestoreWindowIdKey,
- static_cast<int32_t>(restored_browser_session_id));
-
std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info =
std::make_unique<app_restore::AppLaunchInfo>(app_constants::kLacrosAppId,
browser_session_id);
diff --git a/chromium/components/app_restore/lacros_save_handler.h b/chromium/components/app_restore/lacros_save_handler.h
index 242d0d8b52e..fcc5ec67048 100644
--- a/chromium/components/app_restore/lacros_save_handler.h
+++ b/chromium/components/app_restore/lacros_save_handler.h
@@ -35,12 +35,8 @@ class COMPONENT_EXPORT(APP_RESTORE) LacrosSaveHandler {
// Invoked when `window` is destroyed.
void OnWindowDestroyed(aura::Window* window);
- // Invoked when Lacros browser window is created. `browser_session_id` is the
- // current browser session id.
- void OnBrowserWindowAdded(aura::Window* const window,
- uint32_t browser_session_id,
- uint32_t restored_browser_session_id,
- bool is_browser_app);
+ // Invoked when Lacros browser window is created.
+ void OnBrowserWindowAdded(aura::Window* const window, bool is_browser_app);
// Invoked when an Chrome app Lacros window is created. `app_id` is the
// AppService id, and `window_id` is the wayland app_id property for the
diff --git a/chromium/components/app_restore/restore_data_unittest.cc b/chromium/components/app_restore/restore_data_unittest.cc
index 87ea0d10462..77fa3f03a6c 100644
--- a/chromium/components/app_restore/restore_data_unittest.cc
+++ b/chromium/components/app_restore/restore_data_unittest.cc
@@ -13,8 +13,12 @@
#include "components/app_constants/constants.h"
#include "components/app_restore/app_launch_info.h"
#include "components/app_restore/app_restore_data.h"
+#include "components/app_restore/tab_group_info.h"
#include "components/app_restore/window_info.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "components/tab_groups/tab_group_color.h"
+#include "components/tab_groups/tab_group_visual_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/base/window_open_disposition.h"
@@ -95,6 +99,32 @@ constexpr char16_t kTitle2[] = u"test title2";
constexpr gfx::Rect kBoundsInRoot1(11, 21, 111, 121);
constexpr gfx::Rect kBoundsInRoot2(31, 41, 131, 141);
+constexpr char16_t kTestTabGroupTitleOne[] = u"sample_tab_group_1";
+constexpr char16_t kTestTabGroupTitleTwo[] = u"sample_tab_group_2";
+constexpr char16_t kTestTabGroupTitleThree[] = u"sample_tab_group_3";
+const tab_groups::TabGroupColorId kTestTabGroupColorOne =
+ tab_groups::TabGroupColorId::kGrey;
+const tab_groups::TabGroupColorId kTestTabGroupColorTwo =
+ tab_groups::TabGroupColorId::kBlue;
+const tab_groups::TabGroupColorId kTestTabGroupColorThree =
+ tab_groups::TabGroupColorId::kGreen;
+const gfx::Range kTestTabGroupTabRange(1, 2);
+
+TabGroupInfo MakeTestTabGroup(const char16_t* title,
+ tab_groups::TabGroupColorId color) {
+ return TabGroupInfo(kTestTabGroupTabRange,
+ tab_groups::TabGroupVisualData(title, color));
+}
+
+void PopulateTestTabgroups(std::vector<TabGroupInfo>& out_tab_groups) {
+ out_tab_groups.push_back(
+ MakeTestTabGroup(kTestTabGroupTitleOne, kTestTabGroupColorOne));
+ out_tab_groups.push_back(
+ MakeTestTabGroup(kTestTabGroupTitleTwo, kTestTabGroupColorTwo));
+ out_tab_groups.push_back(
+ MakeTestTabGroup(kTestTabGroupTitleThree, kTestTabGroupColorThree));
+}
+
} // namespace
// Unit tests for restore data.
@@ -133,6 +163,8 @@ class RestoreDataTest : public testing::Test {
std::vector<base::FilePath>{base::FilePath(kFilePath2)},
CreateIntent(kIntentActionView, kMimeType, kShareText2));
app_launch_info2->app_type_browser = kAppTypeBrower2;
+ app_launch_info2->tab_group_infos.emplace();
+ PopulateTestTabgroups(app_launch_info2->tab_group_infos.value());
std::unique_ptr<AppLaunchInfo> app_launch_info3 =
std::make_unique<AppLaunchInfo>(
@@ -210,7 +242,9 @@ class RestoreDataTest : public testing::Test {
absl::optional<std::u16string> title,
absl::optional<gfx::Rect> bounds_in_root,
uint32_t primary_color,
- uint32_t status_bar_color) {
+ uint32_t status_bar_color,
+ std::vector<TabGroupInfo> expected_tab_group_infos,
+ bool test_tab_group_infos = true) {
EXPECT_TRUE(data->container.has_value());
EXPECT_EQ(static_cast<int>(container), data->container.value());
@@ -308,9 +342,28 @@ class RestoreDataTest : public testing::Test {
} else {
EXPECT_FALSE(data->status_bar_color.has_value());
}
+
+ // Only test tab group infos in tests that don't concern serialization
+ // or deserialization as the logic for serializing tab group infos
+ // exists in the desks_storage component. This is because tab group
+ // infos are only utilized by save and recall and desk template features.
+ if (expected_tab_group_infos.size() > 0 && test_tab_group_infos) {
+ // If we're passing a non-empty expceted vector then we expect the
+ // object under test to have tab group infos.
+ EXPECT_TRUE(data->tab_group_infos.has_value());
+
+ // Parameter vector and data vector should always have the same size
+ // as they should be instantiated from the same function.
+ EXPECT_EQ(expected_tab_group_infos.size(),
+ data->tab_group_infos.value().size());
+
+ EXPECT_THAT(expected_tab_group_infos, testing::UnorderedElementsAreArray(
+ data->tab_group_infos.value()));
+ }
}
- void VerifyRestoreData(const RestoreData& restore_data) {
+ void VerifyRestoreData(const RestoreData& restore_data,
+ bool test_tab_group_infos = true) {
EXPECT_EQ(2u, app_id_to_launch_list(restore_data).size());
// Verify for |kAppId1|.
@@ -332,9 +385,11 @@ class RestoreDataTest : public testing::Test {
kAppTypeBrower1, kActivationIndex1, kDeskId1, kCurrentBounds1,
kWindowStateType1, kPreMinimizedWindowStateType1, /*snap_percentage=*/0,
kMaxSize1, kMinSize1, std::u16string(kTitle1), kBoundsInRoot1,
- kPrimaryColor1, kStatusBarColor1);
+ kPrimaryColor1, kStatusBarColor1, /*tab_group_infos=*/{});
const auto app_restore_data_it2 = launch_list_it1->second.find(kWindowId2);
+ std::vector<TabGroupInfo> expected_tab_group_infos;
+ PopulateTestTabgroups(expected_tab_group_infos);
EXPECT_TRUE(app_restore_data_it2 != launch_list_it1->second.end());
VerifyAppRestoreData(
app_restore_data_it2->second,
@@ -345,7 +400,8 @@ class RestoreDataTest : public testing::Test {
kAppTypeBrower2, kActivationIndex2, kDeskId2, kCurrentBounds2,
kWindowStateType2, kPreMinimizedWindowStateType2, /*snap_percentage=*/0,
absl::nullopt, kMinSize2, std::u16string(kTitle2), kBoundsInRoot2,
- kPrimaryColor2, kStatusBarColor2);
+ kPrimaryColor2, kStatusBarColor2, std::move(expected_tab_group_infos),
+ test_tab_group_infos);
// Verify for |kAppId2|.
const auto launch_list_it2 =
@@ -362,7 +418,8 @@ class RestoreDataTest : public testing::Test {
CreateIntent(kIntentActionView, kMimeType, kShareText1),
kAppTypeBrower3, kActivationIndex3, kDeskId3, kCurrentBounds3,
kWindowStateType3, kPreMinimizedWindowStateType3, kSnapPercentage,
- absl::nullopt, absl::nullopt, absl::nullopt, absl::nullopt, 0, 0);
+ absl::nullopt, absl::nullopt, absl::nullopt, absl::nullopt, 0, 0,
+ /*tab_group_infos=*/{});
}
RestoreData& restore_data() { return restore_data_; }
@@ -417,16 +474,16 @@ TEST_F(RestoreDataTest, ModifyWindowId) {
// Verify the restore data for |kWindowId2| is migrated to |kWindowId4|.
const auto app_restore_data_it4 = launch_list_it1->second.find(kWindowId4);
EXPECT_TRUE(app_restore_data_it4 != launch_list_it1->second.end());
- VerifyAppRestoreData(app_restore_data_it4->second,
- apps::mojom::LaunchContainer::kLaunchContainerTab,
- WindowOpenDisposition::NEW_FOREGROUND_TAB, kDisplayId1,
- std::vector<base::FilePath>{base::FilePath(kFilePath2)},
- CreateIntent(kIntentActionView, kMimeType, kShareText2),
- kAppTypeBrower2, kActivationIndex2, kDeskId2,
- kCurrentBounds2, kWindowStateType2,
- kPreMinimizedWindowStateType2, /*snap_percentage=*/0,
- absl::nullopt, kMinSize2, std::u16string(kTitle2),
- kBoundsInRoot2, kPrimaryColor2, kStatusBarColor2);
+ VerifyAppRestoreData(
+ app_restore_data_it4->second,
+ apps::mojom::LaunchContainer::kLaunchContainerTab,
+ WindowOpenDisposition::NEW_FOREGROUND_TAB, kDisplayId1,
+ std::vector<base::FilePath>{base::FilePath(kFilePath2)},
+ CreateIntent(kIntentActionView, kMimeType, kShareText2), kAppTypeBrower2,
+ kActivationIndex2, kDeskId2, kCurrentBounds2, kWindowStateType2,
+ kPreMinimizedWindowStateType2, /*snap_percentage=*/0, absl::nullopt,
+ kMinSize2, std::u16string(kTitle2), kBoundsInRoot2, kPrimaryColor2,
+ kStatusBarColor2, /*tab_group_infos=*/{});
// Verify the restore data for |kAppId2| still exists.
const auto launch_list_it2 =
@@ -544,7 +601,9 @@ TEST_F(RestoreDataTest, Convert) {
std::make_unique<base::Value>(restore_data().ConvertToValue());
std::unique_ptr<RestoreData> restore_data =
std::make_unique<RestoreData>(std::move(value));
- VerifyRestoreData(*restore_data);
+ // Full restore is not responsible for serializing or deseraizling
+ // TabGroupInfos.
+ VerifyRestoreData(*restore_data, /*test_tab_group_infos=*/false);
}
TEST_F(RestoreDataTest, ConvertNullData) {
diff --git a/chromium/components/app_restore/tab_group_info.cc b/chromium/components/app_restore/tab_group_info.cc
new file mode 100644
index 00000000000..594e0e84923
--- /dev/null
+++ b/chromium/components/app_restore/tab_group_info.cc
@@ -0,0 +1,62 @@
+// Copyright 2022 The Chromium 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/app_restore/tab_group_info.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/tab_groups/tab_group_color.h"
+
+namespace app_restore {
+
+std::string TabGroupColorToString(tab_groups::TabGroupColorId color) {
+ switch (color) {
+ case tab_groups::TabGroupColorId::kGrey:
+ return kTabGroupColorGrey;
+ case tab_groups::TabGroupColorId::kBlue:
+ return kTabGroupColorBlue;
+ case tab_groups::TabGroupColorId::kRed:
+ return kTabGroupColorRed;
+ case tab_groups::TabGroupColorId::kYellow:
+ return kTabGroupColorYellow;
+ case tab_groups::TabGroupColorId::kGreen:
+ return kTabGroupColorGreen;
+ case tab_groups::TabGroupColorId::kPink:
+ return kTabGroupColorYellow;
+ case tab_groups::TabGroupColorId::kPurple:
+ return kTabGroupColorPurple;
+ case tab_groups::TabGroupColorId::kCyan:
+ return kTabGroupColorCyan;
+ case tab_groups::TabGroupColorId::kOrange:
+ return kTabGroupColorOrange;
+ }
+}
+
+TabGroupInfo::TabGroupInfo(const gfx::Range& tab_range,
+ const tab_groups::TabGroupVisualData& visual_data)
+ : tab_range(tab_range), visual_data(visual_data) {}
+
+TabGroupInfo::TabGroupInfo(TabGroupInfo&& other)
+ : tab_range(other.tab_range), visual_data(other.visual_data) {}
+
+TabGroupInfo::TabGroupInfo(const TabGroupInfo& other) = default;
+TabGroupInfo& TabGroupInfo::operator=(const TabGroupInfo& other) = default;
+TabGroupInfo::~TabGroupInfo() = default;
+
+bool TabGroupInfo::operator==(const TabGroupInfo& other) const {
+ return tab_range == other.tab_range && visual_data == other.visual_data;
+}
+
+std::string TabGroupInfo::ToString() const {
+ std::string result =
+ "{\n\tname: " + base::UTF16ToUTF8(visual_data.title()) + "\n";
+ result += "\tcolor: " + TabGroupColorToString(visual_data.color()) + "\n";
+ std::string is_collapsed_string =
+ visual_data.is_collapsed() ? "TRUE" : "FALSE";
+ result += "\tis_collapsed: " + is_collapsed_string + "\n";
+ result += "\trange_start: " + tab_range.ToString() + "\n}";
+
+ return result;
+}
+
+} // namespace app_restore \ No newline at end of file
diff --git a/chromium/components/app_restore/tab_group_info.h b/chromium/components/app_restore/tab_group_info.h
new file mode 100644
index 00000000000..718107188bf
--- /dev/null
+++ b/chromium/components/app_restore/tab_group_info.h
@@ -0,0 +1,65 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_APP_RESTORE_TAB_GROUP_INFO_H_
+#define COMPONENTS_APP_RESTORE_TAB_GROUP_INFO_H_
+
+#include "base/component_export.h"
+#include "components/tab_groups/tab_group_visual_data.h"
+#include "ui/gfx/range/range.h"
+
+namespace app_restore {
+
+// String kConstants used by TabGroupColorToString.
+constexpr char kTabGroupColorUnknown[] = "UNKNONW";
+constexpr char kTabGroupColorGrey[] = "GREY";
+constexpr char kTabGroupColorBlue[] = "BLUE";
+constexpr char kTabGroupColorRed[] = "RED";
+constexpr char kTabGroupColorYellow[] = "YELLOW";
+constexpr char kTabGroupColorGreen[] = "GREEN";
+constexpr char kTabGroupColorPink[] = "PINK";
+constexpr char kTabGroupColorPurple[] = "PURPLE";
+constexpr char kTabGroupColorCyan[] = "CYAN";
+constexpr char kTabGroupColorOrange[] = "ORANGE";
+
+// Used in ToString as well as in Conversion Logic for
+// components/desks_storage/core/desk_template_conversion.cc
+std::string COMPONENT_EXPORT(APP_RESTORE)
+ TabGroupColorToString(tab_groups::TabGroupColorId color);
+
+// Tab group info is a structure representing a tab group that
+// is associated with a specific browser window. This struct lives
+// in a list of instances of its kind located under the tab_group_infos
+// field of an AppRestoreData struct. This structure is used by saved desks
+// to store data relating to tab groups and is not directly used by full
+// restore.
+struct COMPONENT_EXPORT(APP_RESTORE) TabGroupInfo {
+ TabGroupInfo(const gfx::Range& tab_range,
+ const tab_groups::TabGroupVisualData& visual_data);
+
+ TabGroupInfo(const TabGroupInfo&);
+ TabGroupInfo& operator=(const TabGroupInfo& other);
+
+ // Move constructor used for vector allocation.
+ TabGroupInfo(TabGroupInfo&& other);
+
+ ~TabGroupInfo();
+
+ // Checks whether or not two TabGroupInfos are semantically equivalent.
+ // Used in testing.
+ bool operator==(const TabGroupInfo& other) const;
+
+ // Produces a string representation of this tab group used in debugging.
+ std::string ToString() const;
+
+ // Range of tabs this group is associated with.
+ gfx::Range tab_range;
+
+ // Human readable data associated with this tab group.
+ tab_groups::TabGroupVisualData visual_data;
+};
+
+} // namespace app_restore
+
+#endif // COMPONENTS_APP_RESTORE_TAB_GROUP_INFO_H_ \ No newline at end of file
diff --git a/chromium/components/app_restore/window_properties.cc b/chromium/components/app_restore/window_properties.cc
index 5cda130db6d..d5efb26e1d1 100644
--- a/chromium/components/app_restore/window_properties.cc
+++ b/chromium/components/app_restore/window_properties.cc
@@ -18,7 +18,7 @@ DEFINE_UI_CLASS_PROPERTY_KEY(bool, kAppTypeBrowser, false)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kBrowserAppNameKey, nullptr)
DEFINE_UI_CLASS_PROPERTY_KEY(int32_t, kGhostWindowSessionIdKey, 0)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kLacrosWindowId, nullptr)
-DEFINE_UI_CLASS_PROPERTY_KEY(bool, kLaunchedFromFullRestoreKey, false)
+DEFINE_UI_CLASS_PROPERTY_KEY(bool, kLaunchedFromAppRestoreKey, false)
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kParentToHiddenContainerKey, false)
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kRealArcTaskWindow, true)
DEFINE_UI_CLASS_PROPERTY_KEY(int32_t, kRestoreWindowIdKey, 0)
diff --git a/chromium/components/app_restore/window_properties.h b/chromium/components/app_restore/window_properties.h
index 551813adcb5..23564907aa7 100644
--- a/chromium/components/app_restore/window_properties.h
+++ b/chromium/components/app_restore/window_properties.h
@@ -45,10 +45,10 @@ extern const ui::ClassProperty<int32_t>* const kGhostWindowSessionIdKey;
COMPONENT_EXPORT(APP_RESTORE)
extern const ui::ClassProperty<std::string*>* const kLacrosWindowId;
-// A property key indicating whether a window was launched from full restore.
+// A property key indicating whether a window was launched from app restore.
// These windows will not be activatable until they are shown.
COMPONENT_EXPORT(APP_RESTORE)
-extern const ui::ClassProperty<bool>* const kLaunchedFromFullRestoreKey;
+extern const ui::ClassProperty<bool>* const kLaunchedFromAppRestoreKey;
// A property key to add the window to a hidden container, if the ARC task is
// not created when the window is initialized.
diff --git a/chromium/components/arc/BUILD.gn b/chromium/components/arc/BUILD.gn
index fb538443395..04e517c9d5b 100644
--- a/chromium/components/arc/BUILD.gn
+++ b/chromium/components/arc/BUILD.gn
@@ -20,6 +20,9 @@ static_library("arc") {
"intent_helper/open_url_delegate.h",
]
+ # Enable VLOG(1).
+ defines = [ "ENABLED_VLOG_LEVEL=1" ]
+
deps = [
"//ash/components/arc",
"//ash/public/cpp",
diff --git a/chromium/components/arc/DEPS b/chromium/components/arc/DEPS
index 11e8d5c8a71..0e869011b18 100644
--- a/chromium/components/arc/DEPS
+++ b/chromium/components/arc/DEPS
@@ -3,9 +3,9 @@ include_rules = [
"+ash/components/cryptohome",
"+ash/constants",
"+ash/public/cpp",
+ "+chromeos/ash/components/memory",
"+chromeos/components/sensors",
"+chromeos/dbus",
- "+chromeos/memory",
"+chromeos/system",
"+components/guest_os",
"+components/account_id",
diff --git a/chromium/components/arc/common/BUILD.gn b/chromium/components/arc/common/BUILD.gn
index f14d54dbf78..ff97ddc2bf6 100644
--- a/chromium/components/arc/common/BUILD.gn
+++ b/chromium/components/arc/common/BUILD.gn
@@ -18,6 +18,9 @@ static_library("common") {
"intent_helper/link_handler_model.h",
]
+ # Enable VLOG(1).
+ defines = [ "ENABLED_VLOG_LEVEL=1" ]
+
deps = [
":arc_intent_helper_constants",
"//base",
diff --git a/chromium/components/arc/common/intent_helper/arc_intent_helper_mojo_delegate.h b/chromium/components/arc/common/intent_helper/arc_intent_helper_mojo_delegate.h
index 92f439f7fb5..8c798f7043e 100644
--- a/chromium/components/arc/common/intent_helper/arc_intent_helper_mojo_delegate.h
+++ b/chromium/components/arc/common/intent_helper/arc_intent_helper_mojo_delegate.h
@@ -91,7 +91,7 @@ class ArcIntentHelperMojoDelegate {
bool is_preferred,
absl::optional<std::string> fallback_url);
IntentHandlerInfo(const IntentHandlerInfo& other);
- IntentHandlerInfo& operator=(const IntentHandlerInfo&) = delete;
+ IntentHandlerInfo& operator=(const IntentHandlerInfo&) = default;
~IntentHandlerInfo();
// The name of the package used as a description text.
diff --git a/chromium/components/assist_ranker/base_predictor_unittest.cc b/chromium/components/assist_ranker/base_predictor_unittest.cc
index cfbd551c6bb..65411baeaf9 100644
--- a/chromium/components/assist_ranker/base_predictor_unittest.cc
+++ b/chromium/components/assist_ranker/base_predictor_unittest.cc
@@ -33,21 +33,9 @@ const char kTestUmaPrefixName[] = "Test.Ranker";
const char kTestUrlParamName[] = "ranker-model-url";
const char kTestDefaultModelUrl[] = "https://foo.bar/model.bin";
-// The allowed features must be metrics of kTestLoggingName in ukm.xml,
-// though the types do not need to match.
-const char kBoolFeature[] = "DidOptIn";
-const char kIntFeature[] = "DurationAfterScrollMs";
-const char kFloatFeature[] = "FontSize";
-const char kStringFeature[] = "IsEntity";
-const char kStringListFeature[] = "IsEntityEligible";
-const char kFeatureNotAllowed[] = "not_allowed";
-
const char kTestNavigationUrl[] = "https://foo.com";
-const base::flat_set<std::string> kFeatureAllowlist({kBoolFeature, kIntFeature,
- kFloatFeature,
- kStringFeature,
- kStringListFeature});
+const base::flat_set<std::string> kFeatureAllowlist;
const base::Feature kTestRankerQuery{"TestRankerQuery",
base::FEATURE_ENABLED_BY_DEFAULT};
@@ -159,41 +147,6 @@ TEST_F(BasePredictorTest, QueryDisabled) {
EXPECT_FALSE(predictor->IsReady());
}
-TEST_F(BasePredictorTest, LogExampleToUkm) {
- auto predictor = FakePredictor::Create();
- RankerExample example;
- auto& features = *example.mutable_features();
- features[kBoolFeature].set_bool_value(true);
- features[kIntFeature].set_int32_value(42);
- features[kFloatFeature].set_float_value(42.0f);
- features[kStringFeature].set_string_value("42");
- features[kStringListFeature].mutable_string_list()->add_string_value("42");
-
- // This feature will not be logged.
- features[kFeatureNotAllowed].set_bool_value(false);
-
- predictor->LogExampleToUkm(example, GetSourceId());
-
- EXPECT_EQ(1U, GetTestUkmRecorder()->sources_count());
- EXPECT_EQ(1U, GetTestUkmRecorder()->entries_count());
- std::vector<const ukm::mojom::UkmEntry*> entries =
- GetTestUkmRecorder()->GetEntriesByName(kTestLoggingName);
- EXPECT_EQ(1U, entries.size());
- GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kBoolFeature,
- 72057594037927937);
- GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kIntFeature,
- 216172782113783850);
- GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kFloatFeature,
- 144115189185773568);
- GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kStringFeature,
- 288230377208836903);
- GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kStringListFeature,
- 360287971246764839);
-
- EXPECT_FALSE(
- GetTestUkmRecorder()->EntryHasMetric(entries[0], kFeatureNotAllowed));
-}
-
TEST_F(BasePredictorTest, GetPredictThresholdReplacement) {
float altered_threshold = 0.78f; // Arbitrary value.
const PredictorConfig altered_threshold_config{
diff --git a/chromium/components/assist_ranker/predictor_config_definitions.cc b/chromium/components/assist_ranker/predictor_config_definitions.cc
index 0d2c38c79c4..a46a3a72524 100644
--- a/chromium/components/assist_ranker/predictor_config_definitions.cc
+++ b/chromium/components/assist_ranker/predictor_config_definitions.cc
@@ -47,40 +47,7 @@ float GetContextualSearchRankerThresholdFeatureParam() {
// the UKM generated API.
const base::flat_set<std::string>* GetContextualSearchFeatureAllowlist() {
static auto* kContextualSearchFeatureAllowlist =
- new base::flat_set<std::string>({"DidOptIn",
- "DurationAfterScrollMs",
- "EntityImpressionsCount",
- "EntityOpensCount",
- "FontSize",
- "IsEntity",
- "IsEntityEligible",
- "IsHttp",
- "IsLanguageMismatch",
- "IsLongWord",
- "IsSecondTapOverride",
- "IsShortWord",
- "IsWordEdge",
- "OpenCount",
- "OutcomeRankerDidPredict",
- "OutcomeRankerPrediction",
- "OutcomeRankerPredictionScore",
- "OutcomeWasCardsDataShown",
- "OutcomeWasPanelOpened",
- "OutcomeWasQuickActionClicked",
- "OutcomeWasQuickAnswerSeen",
- "PortionOfElement",
- "Previous28DayCtrPercent",
- "Previous28DayImpressionsCount",
- "PreviousWeekCtrPercent",
- "PreviousWeekImpressionsCount",
- "QuickActionImpressionsCount",
- "QuickActionsIgnored",
- "QuickActionsTaken",
- "QuickAnswerCount",
- "ScreenTopDps",
- "TapCount",
- "TapDurationMs",
- "WasScreenBottom"});
+ new base::flat_set<std::string>();
return kContextualSearchFeatureAllowlist;
}
diff --git a/chromium/components/assist_ranker/ranker_example_util.cc b/chromium/components/assist_ranker/ranker_example_util.cc
index ea9ed5ab311..2707c024286 100644
--- a/chromium/components/assist_ranker/ranker_example_util.cc
+++ b/chromium/components/assist_ranker/ranker_example_util.cc
@@ -26,7 +26,7 @@ int32_t StringToIntBits(const std::string& str) {
int32_t FloatToIntBits(float f) {
if (std::numeric_limits<float>::is_iec559) {
// Directly bit_cast if float follows ieee754 standard.
- return bit_cast<int32_t>(f);
+ return base::bit_cast<int32_t>(f);
} else {
// Otherwise, manually calculate sign, exp and mantissa.
// For sign.
diff --git a/chromium/components/autofill/DEPS b/chromium/components/autofill/DEPS
index d3318af1995..51fd81751ef 100644
--- a/chromium/components/autofill/DEPS
+++ b/chromium/components/autofill/DEPS
@@ -2,7 +2,7 @@ include_rules = [
"+components/autofill/android/jni_headers",
"+components/prefs",
"+components/strings/grit/components_strings.h",
- "+google_apis/gaia/gaia_urls.h",
+ "+google_apis/gaia",
"+net",
"+mojo/public",
"+third_party/protobuf",
diff --git a/chromium/components/autofill/OWNERS b/chromium/components/autofill/OWNERS
index 437fe5393d1..e256481adb3 100644
--- a/chromium/components/autofill/OWNERS
+++ b/chromium/components/autofill/OWNERS
@@ -1,11 +1,5 @@
battre@chromium.org
cfroussios@chromium.org
-dvadym@chromium.org
-estade@chromium.org
-ftirelo@chromium.org
koerber@google.com
schwering@google.com
kolos@chromium.org
-mahmadi@chromium.org
-sebsg@chromium.org
-mamir@chromium.org
diff --git a/chromium/components/autofill/PRESUBMIT.py b/chromium/components/autofill/PRESUBMIT.py
index bd85cb024bd..68d395017f8 100644
--- a/chromium/components/autofill/PRESUBMIT.py
+++ b/chromium/components/autofill/PRESUBMIT.py
@@ -87,6 +87,30 @@ def _CheckFeatureNames(input_api, output_api):
return warnings
+def _CheckWebViewExposedExperiments(input_api, output_api):
+ """Checks that changes to autofill features are exposed to webview."""
+
+ _PRODUCTION_SUPPORT_FILE = ('android_webview/java/src/org/chromium/' +
+ 'android_webview/common/ProductionSupportedFlagList.java')
+
+ def is_autofill_features_file(f):
+ return (f.LocalPath().startswith('components/autofill/') and
+ f.LocalPath().endswith('features.cc'))
+
+ def is_webview_features_file(f):
+ return f.LocalPath() == _PRODUCTION_SUPPORT_FILE
+
+ def any_file_matches(matcher):
+ return any(matcher(f) for f in input_api.change.AffectedTestableFiles())
+
+ warnings = []
+ if (any_file_matches(is_autofill_features_file)
+ and not any_file_matches(is_webview_features_file)):
+ warnings += [ output_api.PresubmitPromptWarning(
+ 'You may need to modify {} if your feature affects WebView.'
+ .format(_PRODUCTION_SUPPORT_FILE)) ]
+
+ return warnings
def _CommonChecks(input_api, output_api):
"""Checks common to both upload and commit."""
@@ -94,6 +118,7 @@ def _CommonChecks(input_api, output_api):
results.extend(_CheckNoBaseTimeCalls(input_api, output_api))
results.extend(_CheckNoServerFieldTypeCasts(input_api, output_api))
results.extend(_CheckFeatureNames(input_api, output_api))
+ results.extend(_CheckWebViewExposedExperiments(input_api, output_api))
return results
def CheckChangeOnUpload(input_api, output_api):
diff --git a/chromium/components/autofill/android/BUILD.gn b/chromium/components/autofill/android/BUILD.gn
index 7e080415381..6c2f0b55ac7 100644
--- a/chromium/components/autofill/android/BUILD.gn
+++ b/chromium/components/autofill/android/BUILD.gn
@@ -94,7 +94,6 @@ android_library("main_autofill_java") {
deps = [
":autofill_java_resources",
":payments_autofill_java",
- "//base:base_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
diff --git a/chromium/components/autofill/android/OWNERS b/chromium/components/autofill/android/OWNERS
new file mode 100644
index 00000000000..2329f15a406
--- /dev/null
+++ b/chromium/components/autofill/android/OWNERS
@@ -0,0 +1 @@
+lizapopova@google.com
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc
index cc91f7d0b2d..3e47876252d 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc
@@ -9,13 +9,11 @@
#include <utility>
#include "base/feature_list.h"
-#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "components/autofill/content/browser/bad_message.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/autofill/content/browser/content_autofill_router.h"
#include "components/autofill/core/browser/autofill_client.h"
-#include "components/autofill/core/browser/browser_autofill_manager.h"
#include "components/autofill/core/browser/data_model/autofillable_data.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/payments/payments_service_url.h"
@@ -44,53 +42,11 @@
namespace autofill {
-namespace {
-
-bool ShouldEnableHeavyFormDataScraping(const version_info::Channel channel) {
- switch (channel) {
- case version_info::Channel::CANARY:
- case version_info::Channel::DEV:
- return true;
- case version_info::Channel::STABLE:
- case version_info::Channel::BETA:
- case version_info::Channel::UNKNOWN:
- return false;
- }
- NOTREACHED();
- return false;
-}
-
-} // namespace
-
ContentAutofillDriver::ContentAutofillDriver(
content::RenderFrameHost* render_frame_host,
- AutofillClient* client,
- const std::string& app_locale,
- ContentAutofillRouter* autofill_router,
- AutofillManager::AutofillDownloadManagerState enable_download_manager,
- AutofillManager::AutofillManagerFactoryCallback
- autofill_manager_factory_callback)
+ ContentAutofillRouter* autofill_router)
: render_frame_host_(render_frame_host),
- autofill_router_(autofill_router),
- browser_autofill_manager_(nullptr),
- log_manager_(client->GetLogManager()) {
- // AutofillManager isn't used if provider is valid, Autofill provider is
- // currently used by Android WebView only.
- if (autofill_manager_factory_callback) {
- autofill_manager_ = autofill_manager_factory_callback.Run(
- this, client, app_locale, enable_download_manager);
- GetAutofillAgent()->SetUserGestureRequired(false);
- GetAutofillAgent()->SetSecureContextRequired(true);
- GetAutofillAgent()->SetFocusRequiresScroll(false);
- GetAutofillAgent()->SetQueryPasswordSuggestion(true);
- } else {
- SetBrowserAutofillManager(std::make_unique<BrowserAutofillManager>(
- this, client, app_locale, enable_download_manager));
- }
- if (client && ShouldEnableHeavyFormDataScraping(client->GetChannel())) {
- GetAutofillAgent()->EnableHeavyFormDataScraping();
- }
-}
+ autofill_router_(autofill_router) {}
ContentAutofillDriver::~ContentAutofillDriver() {
if (autofill_router_) // Can be nullptr only in tests.
@@ -157,11 +113,11 @@ bool ContentAutofillDriver::RendererIsAvailable() {
webauthn::InternalAuthenticator*
ContentAutofillDriver::GetOrCreateCreditCardInternalAuthenticator() {
- if (!authenticator_impl_ && browser_autofill_manager_ &&
- browser_autofill_manager_->client()) {
+ if (!authenticator_impl_ && autofill_manager_ &&
+ autofill_manager_->client()) {
authenticator_impl_ =
- browser_autofill_manager_->client()
- ->CreateCreditCardInternalAuthenticator(render_frame_host_);
+ autofill_manager_->client()->CreateCreditCardInternalAuthenticator(
+ render_frame_host_);
}
return authenticator_impl_.get();
}
@@ -169,8 +125,7 @@ ContentAutofillDriver::GetOrCreateCreditCardInternalAuthenticator() {
void ContentAutofillDriver::PopupHidden() {
// If the unmask prompt is shown, keep showing the preview. The preview
// will be cleared when the prompt closes.
- if (browser_autofill_manager_ &&
- browser_autofill_manager_->ShouldClearPreviewedForm()) {
+ if (autofill_manager_ && autofill_manager_->ShouldClearPreviewedForm()) {
RendererShouldClearPreviewedForm();
}
}
@@ -212,15 +167,6 @@ void ContentAutofillDriver::FillOrPreviewFormImpl(
GetAutofillAgent()->FillOrPreviewForm(query_id, data, action);
}
-void ContentAutofillDriver::PropagateAutofillPredictions(
- const std::vector<FormStructure*>& forms) {
- AutofillManager* manager = browser_autofill_manager_
- ? browser_autofill_manager_.get()
- : autofill_manager_.get();
- DCHECK(manager);
- manager->PropagateAutofillPredictions(render_frame_host_, forms);
-}
-
void ContentAutofillDriver::HandleParsedForms(
const std::vector<const FormData*>& forms) {
// No op.
@@ -385,13 +331,15 @@ void ContentAutofillDriver::SelectControlDidChangeImpl(
}
void ContentAutofillDriver::AskForValuesToFillImpl(
- int32_t id,
+ int32_t query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) {
- autofill_manager_->OnAskForValuesToFill(id, form, field, bounding_box,
- autoselect_first_suggestion);
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) {
+ autofill_manager_->OnAskForValuesToFill(query_id, form, field, bounding_box,
+ autoselect_first_suggestion,
+ touch_to_fill_eligible);
}
void ContentAutofillDriver::HidePopupImpl() {
@@ -429,16 +377,22 @@ void ContentAutofillDriver::SelectFieldOptionsDidChangeImpl(
autofill_manager_->SelectFieldOptionsDidChange(form);
}
+void ContentAutofillDriver::JavaScriptChangedAutofilledValueImpl(
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) {
+ autofill_manager_->JavaScriptChangedAutofilledValue(form, field, old_value);
+}
+
void ContentAutofillDriver::FillFormForAssistantImpl(
const AutofillableData& fill_data,
const FormData& form,
const FormFieldData& field) {
- DCHECK(browser_autofill_manager_);
+ DCHECK(autofill_manager_);
if (fill_data.is_profile()) {
- browser_autofill_manager_->FillProfileForm(fill_data.profile(), form,
- field);
+ autofill_manager_->FillProfileForm(fill_data.profile(), form, field);
} else if (fill_data.is_credit_card()) {
- browser_autofill_manager_->FillCreditCardForm(
+ autofill_manager_->FillCreditCardForm(
/*query_id=*/kNoQueryId, form, field, fill_data.credit_card(),
fill_data.cvc());
} else {
@@ -533,20 +487,21 @@ void ContentAutofillDriver::SelectControlDidChange(
}
void ContentAutofillDriver::AskForValuesToFill(
- int32_t id,
+ int32_t query_id,
const FormData& raw_form,
const FormFieldData& raw_field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) {
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) {
if (!bad_message::CheckFrameNotPrerendering(render_frame_host_))
return;
FormData form = raw_form;
FormFieldData field = raw_field;
SetFrameAndFormMetaData(form, &field);
GetAutofillRouter().AskForValuesToFill(
- this, id, form, field,
+ this, query_id, form, field,
TransformBoundingBoxToViewportCoordinates(bounding_box),
- autoselect_first_suggestion);
+ autoselect_first_suggestion, touch_to_fill_eligible);
}
void ContentAutofillDriver::HidePopup() {
@@ -602,6 +557,19 @@ void ContentAutofillDriver::SelectFieldOptionsDidChange(
this, GetFormWithFrameAndFormMetaData(raw_form));
}
+void ContentAutofillDriver::JavaScriptChangedAutofilledValue(
+ const FormData& raw_form,
+ const FormFieldData& raw_field,
+ const std::u16string& old_value) {
+ if (!bad_message::CheckFrameNotPrerendering(render_frame_host_))
+ return;
+ FormData form = raw_form;
+ FormFieldData field = raw_field;
+ SetFrameAndFormMetaData(form, &field);
+ GetAutofillRouter().JavaScriptChangedAutofilledValue(this, form, field,
+ old_value);
+}
+
void ContentAutofillDriver::FillFormForAssistant(
const AutofillableData& fill_data,
const FormData& raw_form,
@@ -617,8 +585,8 @@ void ContentAutofillDriver::DidNavigateFrame(
if (navigation_handle->IsSameDocument()) {
// On page refresh, reset the rate limiter for fetching authentication
// details for credit card unmasking.
- if (browser_autofill_manager_) {
- browser_autofill_manager_->credit_card_access_manager()
+ if (autofill_manager_ && autofill_manager_->GetCreditCardAccessManager()) {
+ autofill_manager_->GetCreditCardAccessManager()
->SignalCanFetchUnmaskDetails();
}
return;
@@ -627,10 +595,10 @@ void ContentAutofillDriver::DidNavigateFrame(
// If the navigation happened in the main frame and the BrowserAutofillManager
// exists (not in Android Webview), and the AutofillOfferManager exists (not
// in Incognito windows), notifies the navigation event.
- if (navigation_handle->IsInPrimaryMainFrame() && browser_autofill_manager_ &&
- browser_autofill_manager_->offer_manager()) {
- browser_autofill_manager_->offer_manager()->OnDidNavigateFrame(
- browser_autofill_manager_->client());
+ if (navigation_handle->IsInPrimaryMainFrame() && autofill_manager_ &&
+ autofill_manager_->GetOfferManager()) {
+ autofill_manager_->GetOfferManager()->OnDidNavigateFrame(
+ autofill_manager_->client());
}
// When IsServedFromBackForwardCache or IsPrerendererdPageActivation, the form
@@ -649,18 +617,6 @@ void ContentAutofillDriver::DidNavigateFrame(
autofill_manager_->Reset();
}
-void ContentAutofillDriver::SetBrowserAutofillManager(
- std::unique_ptr<BrowserAutofillManager> manager) {
- autofill_manager_ = std::move(manager);
- browser_autofill_manager_ =
- static_cast<BrowserAutofillManager*>(autofill_manager_.get());
-}
-
-ContentAutofillDriver::ContentAutofillDriver(content::RenderFrameHost* rfh)
- : render_frame_host_(rfh),
- browser_autofill_manager_(nullptr),
- log_manager_(nullptr) {}
-
const mojo::AssociatedRemote<mojom::AutofillAgent>&
ContentAutofillDriver::GetAutofillAgent() {
// Here is a lazy binding, and will not reconnect after connection error.
@@ -745,47 +701,6 @@ FormData ContentAutofillDriver::GetFormWithFrameAndFormMetaData(
SetFrameAndFormMetaData(form, nullptr);
return form;
}
-
-bool ContentAutofillDriver::DocumentUsedWebOTP() const {
- return render_frame_host_->DocumentUsedWebOTP();
-}
-
-void ContentAutofillDriver::MaybeReportAutofillWebOTPMetrics() {
- // In tests, the browser_autofill_manager_ may be unset or destroyed before
- // |this|.
- if (!browser_autofill_manager_)
- return;
- // It's possible that a frame without any form uses WebOTP. e.g. a server may
- // send the verification code to a phone number that was collected beforehand
- // and uses the WebOTP API for authentication purpose without user manually
- // entering the code.
- if (!browser_autofill_manager_->has_parsed_forms() && !DocumentUsedWebOTP())
- return;
-
- ReportAutofillWebOTPMetrics(DocumentUsedWebOTP());
-}
-
-void ContentAutofillDriver::ReportAutofillWebOTPMetrics(
- bool document_used_webotp) {
- if (browser_autofill_manager_->has_observed_phone_number_field())
- phone_collection_metric_state_ |= phone_collection_metric::kPhoneCollected;
- if (browser_autofill_manager_->has_observed_one_time_code_field())
- phone_collection_metric_state_ |= phone_collection_metric::kOTCUsed;
- if (document_used_webotp)
- phone_collection_metric_state_ |= phone_collection_metric::kWebOTPUsed;
-
- ukm::UkmRecorder* recorder =
- browser_autofill_manager_->client()->GetUkmRecorder();
- ukm::SourceId source_id =
- browser_autofill_manager_->client()->GetUkmSourceId();
- AutofillMetrics::LogWebOTPPhoneCollectionMetricStateUkm(
- recorder, source_id, phone_collection_metric_state_);
-
- UMA_HISTOGRAM_ENUMERATION(
- "Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
- static_cast<PhoneCollectionMetricState>(phone_collection_metric_state_));
-}
-
ContentAutofillRouter& ContentAutofillDriver::GetAutofillRouter() {
DCHECK(content::RenderFrameHost::LifecycleState::kPrerendering !=
render_frame_host_->GetLifecycleState());
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h
index cb3a7e2246b..ab8995b1cee 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver.h
+++ b/chromium/components/autofill/content/browser/content_autofill_driver.h
@@ -15,7 +15,7 @@
#include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
#include "components/autofill/content/common/mojom/autofill_driver.mojom.h"
#include "components/autofill/core/browser/autofill_driver.h"
-#include "components/autofill/core/browser/browser_autofill_manager.h"
+#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/webauthn/core/browser/internal_authenticator.h"
#include "content/public/browser/render_frame_host.h"
@@ -32,29 +32,8 @@ class RenderFrameHost;
namespace autofill {
-class AutofillClient;
class AutofillableData;
class ContentAutofillRouter;
-class LogManager;
-
-// Use <Phone><WebOTP><OTC> as the bit pattern to identify the metrics state.
-enum class PhoneCollectionMetricState {
- kNone = 0, // Site did not collect phone, not use OTC, not use WebOTP
- kOTC = 1, // Site used OTC only
- kWebOTP = 2, // Site used WebOTP only
- kWebOTPPlusOTC = 3, // Site used WebOTP and OTC
- kPhone = 4, // Site collected phone, not used neither WebOTP nor OTC
- kPhonePlusOTC = 5, // Site collected phone number and used OTC
- kPhonePlusWebOTP = 6, // Site collected phone number and used WebOTP
- kPhonePlusWebOTPPlusOTC = 7, // Site collected phone number and used both
- kMaxValue = kPhonePlusWebOTPPlusOTC,
-};
-
-namespace phone_collection_metric {
-constexpr uint32_t kOTCUsed = 1 << 0;
-constexpr uint32_t kWebOTPUsed = 1 << 1;
-constexpr uint32_t kPhoneCollected = 1 << 2;
-} // namespace phone_collection_metric
// ContentAutofillDriver drives the Autofill flow in the browser process based
// on communication from the renderer and from the external world.
@@ -132,18 +111,23 @@ class ContentAutofillDriver : public AutofillDriver,
static ContentAutofillDriver* GetForRenderFrameHost(
content::RenderFrameHost* render_frame_host);
- ContentAutofillDriver(
- content::RenderFrameHost* render_frame_host,
- AutofillClient* client,
- const std::string& app_locale,
- ContentAutofillRouter* autofill_router,
- AutofillManager::AutofillDownloadManagerState enable_download_manager,
- AutofillManager::AutofillManagerFactoryCallback
- autofill_manager_factory_callback);
+ // Partially constructs the ContentAutofillDriver. The ContentAutofillDriver
+ // needs an AutofillManager that should be set via set_autofill_manager() (for
+ // Android Autofill) or set_browser_autofill_manager (for Chromium).
+ // Outside of unittests, ContentAutofillDriverFactory is instantiated and set
+ // up by the ContentAutofillDriverFactory.
+ ContentAutofillDriver(content::RenderFrameHost* render_frame_host,
+ ContentAutofillRouter* autofill_router);
ContentAutofillDriver(const ContentAutofillDriver&) = delete;
ContentAutofillDriver& operator=(const ContentAutofillDriver&) = delete;
~ContentAutofillDriver() override;
+ void set_autofill_manager(std::unique_ptr<AutofillManager> autofill_manager) {
+ autofill_manager_ = std::move(autofill_manager);
+ }
+
+ AutofillManager* autofill_manager() { return autofill_manager_.get(); }
+
void BindPendingReceiver(
mojo::PendingAssociatedReceiver<mojom::AutofillDriver> pending_receiver);
@@ -157,8 +141,6 @@ class ContentAutofillDriver : public AutofillDriver,
bool RendererIsAvailable() override;
webauthn::InternalAuthenticator* GetOrCreateCreditCardInternalAuthenticator()
override;
- void PropagateAutofillPredictions(
- const std::vector<autofill::FormStructure*>& forms) override;
void HandleParsedForms(const std::vector<const FormData*>& forms) override;
void PopupHidden() override;
net::IsolationInfo IsolationInfo() override;
@@ -239,11 +221,12 @@ class ContentAutofillDriver : public AutofillDriver,
void SelectControlDidChange(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
- void AskForValuesToFill(int32_t id,
+ void AskForValuesToFill(int32_t query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) override;
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) override;
void HidePopup() override;
void FocusNoLongerOnForm(bool had_interacted_form) override;
void FocusOnFormField(const FormData& form,
@@ -254,6 +237,10 @@ class ContentAutofillDriver : public AutofillDriver,
void DidPreviewAutofillFormData() override;
void DidEndTextFieldEditing() override;
void SelectFieldOptionsDidChange(const FormData& form) override;
+ void JavaScriptChangedAutofilledValue(
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) override;
// Implementations of the mojom::AutofillDriver functions called by the
// renderer. These functions are called by ContentAutofillRouter.
@@ -273,11 +260,12 @@ class ContentAutofillDriver : public AutofillDriver,
void SelectControlDidChangeImpl(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box);
- void AskForValuesToFillImpl(int32_t id,
+ void AskForValuesToFillImpl(int32_t query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion);
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible);
void HidePopupImpl();
void FocusNoLongerOnFormImpl(bool had_interacted_form);
void FocusOnFormFieldImpl(const FormData& form,
@@ -288,6 +276,9 @@ class ContentAutofillDriver : public AutofillDriver,
void DidPreviewAutofillFormDataImpl();
void DidEndTextFieldEditingImpl();
void SelectFieldOptionsDidChangeImpl(const FormData& form);
+ void JavaScriptChangedAutofilledValueImpl(const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value);
// Triggers filling of |fill_data| into |raw_form| and |raw_field|. This event
// is called only by Autofill Assistant on the browser side and provides the
@@ -332,11 +323,6 @@ class ContentAutofillDriver : public AutofillDriver,
// navigation occurs in that specific frame.
void DidNavigateFrame(content::NavigationHandle* navigation_handle);
- BrowserAutofillManager* browser_autofill_manager() {
- return browser_autofill_manager_;
- }
- AutofillManager* autofill_manager() { return autofill_manager_.get(); }
-
content::RenderFrameHost* render_frame_host() { return render_frame_host_; }
const mojo::AssociatedRemote<mojom::AutofillAgent>& GetAutofillAgent();
@@ -359,25 +345,6 @@ class ContentAutofillDriver : public AutofillDriver,
const content::RenderWidgetHost::KeyPressEventCallback& handler);
void UnsetKeyPressHandlerImpl();
- // Sets the manager to |manager|. Takes ownership of |manager|.
- void SetBrowserAutofillManager(
- std::unique_ptr<BrowserAutofillManager> manager);
-
- // Reports whether a document collects phone numbers, uses one time code, uses
- // WebOTP. There are cases that the reporting is not expected:
- // 1. some unit tests do not set necessary members,
- // |browser_autofill_manager_|
- // 2. there is no form and WebOTP is not used
- // |MaybeReportAutofillWebOTPMetrics| is to exclude the cases above.
- // |ReportAutofillWebOTPMetrics| is visible for unit tests where the
- // |render_frame_host_| is not set.
- void MaybeReportAutofillWebOTPMetrics();
- void ReportAutofillWebOTPMetrics(bool document_used_webotp);
-
- protected:
- // Constructor for TestAutofillDriver.
- explicit ContentAutofillDriver(content::RenderFrameHost* rfh = nullptr);
-
private:
friend class ContentAutofillDriverTestApi;
@@ -391,10 +358,6 @@ class ContentAutofillDriver : public AutofillDriver,
FormFieldData* optional_field) const;
[[nodiscard]] FormData GetFormWithFrameAndFormMetaData(FormData form) const;
- // Returns whether navigator.credentials.get({otp: {transport:"sms"}}) has
- // been used.
- bool DocumentUsedWebOTP() const;
-
// Returns the AutofillRouter and confirms that it may be accessed (we should
// not be using the router if we're prerendering).
ContentAutofillRouter& GetAutofillRouter();
@@ -419,35 +382,16 @@ class ContentAutofillDriver : public AutofillDriver,
// AutofillManager instance via which this object drives the shared Autofill
// code.
- std::unique_ptr<AutofillManager> autofill_manager_;
-
- // The pointer to autofill_manager_ if it is BrowserAutofillManager instance.
- // TODO: unify autofill_manager_ and browser_autofill_manager_ to a single
- // pointer to a common root.
- raw_ptr<BrowserAutofillManager> browser_autofill_manager_;
+ std::unique_ptr<AutofillManager> autofill_manager_ = nullptr;
// Pointer to an implementation of InternalAuthenticator.
std::unique_ptr<webauthn::InternalAuthenticator> authenticator_impl_;
content::RenderWidgetHost::KeyPressEventCallback key_press_handler_;
- const raw_ptr<LogManager> log_manager_;
-
mojo::AssociatedReceiver<mojom::AutofillDriver> receiver_{this};
mojo::AssociatedRemote<mojom::AutofillAgent> autofill_agent_;
-
- // Helps with measuring whether phone number is collected and whether it is in
- // conjunction with WebOTP or OneTimeCode (OTC).
- // value="0" label="Phone Not Collected, WebOTP Not Used, OTC Not Used"
- // value="1" label="Phone Not Collected, WebOTP Not Used, OTC Used"
- // value="2" label="Phone Not Collected, WebOTP Used, OTC Not Used"
- // value="3" label="Phone Not Collected, WebOTP Used, OTC Used"
- // value="4" label="Phone Collected, WebOTP Not Used, OTC Not Used"
- // value="5" label="Phone Collected, WebOTP Not Used, OTC Used"
- // value="6" label="Phone Collected, WebOTP Used, OTC Not Used"
- // value="7" label="Phone Collected, WebOTP Used, OTC Used"
- uint32_t phone_collection_metric_state_ = 0;
};
} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc
index 161e7c6c551..7992fb1794d 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc
@@ -21,6 +21,34 @@
namespace autofill {
+namespace {
+
+bool ShouldEnableHeavyFormDataScraping(const version_info::Channel channel) {
+ switch (channel) {
+ case version_info::Channel::CANARY:
+ case version_info::Channel::DEV:
+ return true;
+ case version_info::Channel::STABLE:
+ case version_info::Channel::BETA:
+ case version_info::Channel::UNKNOWN:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace
+
+void BrowserDriverInitHook(AutofillClient* client,
+ const std::string& app_locale,
+ ContentAutofillDriver* driver) {
+ driver->set_autofill_manager(std::make_unique<BrowserAutofillManager>(
+ driver, client, app_locale,
+ AutofillManager::EnableDownloadManager(true)));
+ if (client && ShouldEnableHeavyFormDataScraping(client->GetChannel()))
+ driver->GetAutofillAgent()->EnableHeavyFormDataScraping();
+}
+
const char ContentAutofillDriverFactory::
kContentAutofillDriverFactoryWebContentsUserDataKey[] =
"web_contents_autofill_driver_factory";
@@ -29,19 +57,13 @@ const char ContentAutofillDriverFactory::
void ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
content::WebContents* contents,
AutofillClient* client,
- const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager,
- AutofillManager::AutofillManagerFactoryCallback
- autofill_manager_factory_callback) {
+ DriverInitCallback driver_init_hook) {
if (FromWebContents(contents))
return;
- contents->SetUserData(
- kContentAutofillDriverFactoryWebContentsUserDataKey,
- base::WrapUnique(new ContentAutofillDriverFactory(
- contents, client, app_locale, enable_download_manager,
- std::move(autofill_manager_factory_callback))));
+ contents->SetUserData(kContentAutofillDriverFactoryWebContentsUserDataKey,
+ base::WrapUnique(new ContentAutofillDriverFactory(
+ contents, client, std::move(driver_init_hook))));
}
// static
@@ -75,20 +97,20 @@ void ContentAutofillDriverFactory::BindAutofillDriver(
ContentAutofillDriverFactory::ContentAutofillDriverFactory(
content::WebContents* web_contents,
AutofillClient* client,
- const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager,
- AutofillManager::AutofillManagerFactoryCallback
- autofill_manager_factory_callback)
+ DriverInitCallback driver_init_hook)
: content::WebContentsObserver(web_contents),
client_(client),
- app_locale_(app_locale),
- enable_download_manager_(enable_download_manager),
- autofill_manager_factory_callback_(
- std::move(autofill_manager_factory_callback)) {}
+ driver_init_hook_(std::move(driver_init_hook)) {}
ContentAutofillDriverFactory::~ContentAutofillDriverFactory() = default;
+std::unique_ptr<ContentAutofillDriver>
+ContentAutofillDriverFactory::CreateDriver(content::RenderFrameHost* rfh) {
+ auto driver = std::make_unique<ContentAutofillDriver>(rfh, &router_);
+ driver_init_hook_.Run(driver.get());
+ return driver;
+}
+
ContentAutofillDriver* ContentAutofillDriverFactory::DriverForFrame(
content::RenderFrameHost* render_frame_host) {
// Within fenced frames and their descendants, Password Manager should for now
@@ -115,10 +137,8 @@ ContentAutofillDriver* ContentAutofillDriverFactory::DriverForFrame(
// 3. `SomeOtherWebContentsObserver::RenderFrameDeleted(render_frame_host)`
// calls `DriverForFrame(render_frame_host)`.
// 5. `render_frame_host->~RenderFrameHostImpl()` finishes.
- if (render_frame_host->IsRenderFrameCreated()) {
- driver = std::make_unique<ContentAutofillDriver>(
- render_frame_host, client(), app_locale_, &router_,
- enable_download_manager_, autofill_manager_factory_callback_);
+ if (render_frame_host->IsRenderFrameLive()) {
+ driver = CreateDriver(render_frame_host);
DCHECK_EQ(driver_map_.find(render_frame_host)->second.get(),
driver.get());
} else {
@@ -141,8 +161,10 @@ void ContentAutofillDriverFactory::RenderFrameDeleted(
DCHECK(driver);
if (render_frame_host->GetLifecycleState() !=
- content::RenderFrameHost::LifecycleState::kPrerendering) {
- driver->MaybeReportAutofillWebOTPMetrics();
+ content::RenderFrameHost::LifecycleState::kPrerendering &&
+ driver->autofill_manager()) {
+ driver->autofill_manager()->ReportAutofillWebOTPMetrics(
+ render_frame_host->DocumentUsedWebOTP());
}
// If the popup menu has been triggered from within an iframe and that
@@ -219,8 +241,6 @@ void ContentAutofillDriverFactory::ReadyToCommitNavigation(
content::RenderFrameHost::LifecycleState::kPrerendering) {
return;
}
- if (auto* driver = DriverForFrame(render_frame_host))
- driver->MaybeReportAutofillWebOTPMetrics();
}
} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h
index 354b66e92d6..33aa10fa79b 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h
@@ -24,29 +24,37 @@ namespace autofill {
class ContentAutofillDriver;
+// Creates an BrowserAutofillManager and attaches it to the `driver`.
+//
+// This hook is to be passed to CreateForWebContentsAndDelegate().
+// It is the glue between ContentAutofillDriver[Factory] and
+// BrowserAutofillManager.
+//
+// Other embedders (which don't want to use BrowserAutofillManager) shall use
+// other implementations.
+void BrowserDriverInitHook(AutofillClient* client,
+ const std::string& app_locale,
+ ContentAutofillDriver* driver);
+
// Manages lifetime of ContentAutofillDriver. One Factory per WebContents
// creates one Driver per RenderFrame.
class ContentAutofillDriverFactory : public content::WebContentsObserver,
public base::SupportsUserData::Data {
public:
+ using DriverInitCallback =
+ base::RepeatingCallback<void(ContentAutofillDriver*)>;
+
static const char kContentAutofillDriverFactoryWebContentsUserDataKey[];
// Creates a factory for a WebContents object.
//
- // The |autofill_manager_factory_callback| is eventually called by
- // ContentAutofillDriver's constructor. Chrome passes a null callback and
- // ContentAutofillDriver calls SetBrowserAutofillManager() in this case.
- //
- // TODO(crbug.com/1200511): Remove default parameter and pass proper callback
- // and remove ContentAutofillDriver::SetBrowserAutofillManager().
+ // The `driver_init_hook` is called whenever a driver is constructed, so it
+ // may configure the driver. In particular, it may create and set the driver's
+ // AutofillManager.
static void CreateForWebContentsAndDelegate(
content::WebContents* contents,
AutofillClient* client,
- const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager,
- AutofillManager::AutofillManagerFactoryCallback
- autofill_manager_factory_callback = {});
+ DriverInitCallback driver_init_hook);
static ContentAutofillDriverFactory* FromWebContents(
content::WebContents* contents);
@@ -78,20 +86,15 @@ class ContentAutofillDriverFactory : public content::WebContentsObserver,
private:
friend class ContentAutofillDriverFactoryTestApi;
- ContentAutofillDriverFactory(
- content::WebContents* web_contents,
- AutofillClient* client,
- const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager,
- AutofillManager::AutofillManagerFactoryCallback
- autofill_manager_factory_callback);
+ ContentAutofillDriverFactory(content::WebContents* web_contents,
+ AutofillClient* client,
+ DriverInitCallback driver_init_hook);
+
+ std::unique_ptr<ContentAutofillDriver> CreateDriver(
+ content::RenderFrameHost* rfh);
const raw_ptr<AutofillClient> client_;
- std::string app_locale_;
- BrowserAutofillManager::AutofillDownloadManagerState enable_download_manager_;
- AutofillManager::AutofillManagerFactoryCallback
- autofill_manager_factory_callback_;
+ DriverInitCallback driver_init_hook_;
// Routes events between different drivers.
// Must be destroyed after |driver_map_|'s elements.
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.cc b/chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.cc
index fe8583e0888..5808fd8e89a 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.cc
@@ -11,14 +11,9 @@ std::unique_ptr<ContentAutofillDriverFactory>
ContentAutofillDriverFactoryTestApi::Create(
content::WebContents* web_contents,
AutofillClient* client,
- const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager,
- AutofillManager::AutofillManagerFactoryCallback
- autofill_manager_factory_callback) {
- return base::WrapUnique(new ContentAutofillDriverFactory(
- web_contents, client, app_locale, enable_download_manager,
- autofill_manager_factory_callback));
+ ContentAutofillDriverFactory::DriverInitCallback driver_init_hook) {
+ return base::WrapUnique(
+ new ContentAutofillDriverFactory(web_contents, client, driver_init_hook));
}
ContentAutofillDriverFactoryTestApi::ContentAutofillDriverFactoryTestApi(
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.h b/chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.h
index 8c1c4fcd5bb..006dd6bc0ec 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.h
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory_test_api.h
@@ -9,6 +9,7 @@
#include <string>
#include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
namespace autofill {
@@ -18,11 +19,7 @@ class ContentAutofillDriverFactoryTestApi {
static std::unique_ptr<ContentAutofillDriverFactory> Create(
content::WebContents* web_contents,
AutofillClient* client,
- const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager,
- AutofillManager::AutofillManagerFactoryCallback
- autofill_manager_factory_callback);
+ ContentAutofillDriverFactory::DriverInitCallback driver_init_hook);
explicit ContentAutofillDriverFactoryTestApi(
ContentAutofillDriverFactory* factory);
@@ -34,7 +31,7 @@ class ContentAutofillDriverFactoryTestApi {
ContentAutofillRouter& router() { return factory_->router_; }
private:
- ContentAutofillDriverFactory* factory_;
+ raw_ptr<ContentAutofillDriverFactory> factory_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc b/chromium/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
index 8a57f13e3a9..cae01dbd367 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
@@ -19,11 +19,15 @@
#include "content/public/common/content_features.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/features.h"
using testing::_;
+using testing::AtLeast;
using testing::Between;
namespace autofill {
@@ -35,29 +39,117 @@ class MockAutofillClient : public TestAutofillClient {
MOCK_METHOD(void, HideAutofillPopup, (PopupHidingReason), (override));
};
+class MockAutofillAgent : public mojom::AutofillAgent {
+ public:
+ void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle) {
+ receivers_.Add(this, mojo::PendingAssociatedReceiver<mojom::AutofillAgent>(
+ std::move(handle)));
+ }
+
+ MOCK_METHOD(void, TriggerReparse, (), (override));
+ MOCK_METHOD(void,
+ FillOrPreviewForm,
+ (int32_t query_id,
+ const FormData& form,
+ mojom::RendererFormDataAction action),
+ (override));
+ MOCK_METHOD(void,
+ FieldTypePredictionsAvailable,
+ (const std::vector<FormDataPredictions>& forms),
+ (override));
+ MOCK_METHOD(void, ClearSection, (), (override));
+ MOCK_METHOD(void, ClearPreviewedForm, (), (override));
+ MOCK_METHOD(void,
+ FillFieldWithValue,
+ (FieldRendererId field, const std::u16string& value),
+ (override));
+ MOCK_METHOD(void,
+ PreviewFieldWithValue,
+ (FieldRendererId field, const ::std::u16string& value),
+ (override));
+ MOCK_METHOD(void,
+ SetSuggestionAvailability,
+ (FieldRendererId field, mojom::AutofillState type),
+ (override));
+ MOCK_METHOD(void,
+ AcceptDataListSuggestion,
+ (FieldRendererId field, const ::std::u16string& value),
+ (override));
+ MOCK_METHOD(void,
+ FillPasswordSuggestion,
+ (const ::std::u16string& username,
+ const ::std::u16string& password),
+ (override));
+ MOCK_METHOD(void,
+ PreviewPasswordSuggestion,
+ (const ::std::u16string& username,
+ const ::std::u16string& password),
+ (override));
+ MOCK_METHOD(void, SetUserGestureRequired, (bool required), (override));
+ MOCK_METHOD(void, SetSecureContextRequired, (bool required), (override));
+ MOCK_METHOD(void, SetFocusRequiresScroll, (bool require), (override));
+ MOCK_METHOD(void, SetQueryPasswordSuggestion, (bool query), (override));
+ MOCK_METHOD(void,
+ GetElementFormAndFieldDataForDevToolsNodeId,
+ (int32_t backend_node_id,
+ GetElementFormAndFieldDataForDevToolsNodeIdCallback callback),
+ (override));
+ MOCK_METHOD(void,
+ SetAssistantKeyboardSuppressState,
+ (bool suppress),
+ (override));
+ MOCK_METHOD(void, EnableHeavyFormDataScraping, (), (override));
+ MOCK_METHOD(void,
+ SetFieldsEligibleForManualFilling,
+ (const std::vector<FieldRendererId>& fields),
+ (override));
+
+ private:
+ mojo::AssociatedReceiverSet<mojom::AutofillAgent> receivers_;
+};
+
} // namespace
// Test case with one frame.
class ContentAutofillDriverFactoryTest
: public content::RenderViewHostTestHarness {
public:
- ContentAutofillDriverFactoryTest() = default;
+ explicit ContentAutofillDriverFactoryTest(
+ version_info::Channel channel = version_info::Channel::UNKNOWN)
+ : channel_(channel) {}
void SetUp() override {
content::RenderViewHostTestHarness::SetUp();
+
+ client_ = std::make_unique<MockAutofillClient>();
+ client_->set_channel_for_testing(channel_);
+
+ agent_ = std::make_unique<MockAutofillAgent>();
+ blink::AssociatedInterfaceProvider* remote_interfaces =
+ web_contents()->GetPrimaryMainFrame()->GetRemoteAssociatedInterfaces();
+ remote_interfaces->OverrideBinderForTesting(
+ mojom::AutofillAgent::Name_,
+ base::BindRepeating(&MockAutofillAgent::BindPendingReceiver,
+ base::Unretained(agent_.get())));
+
factory_ = ContentAutofillDriverFactoryTestApi::Create(
- web_contents(), &client_, "en_US",
- BrowserAutofillManager::AutofillDownloadManagerState::
- ENABLE_AUTOFILL_DOWNLOAD_MANAGER,
- AutofillManager::AutofillManagerFactoryCallback());
- // One call of HideAutofillPopup() comes from ContentAutofillDriverFactory,
- // the second one from BrowserAutofillManager::Reset(). One of them may be
- // redundant.
- EXPECT_CALL(client_, HideAutofillPopup(_)).Times(Between(1, 2));
- NavigateMainFrame("https://a.com/");
+ web_contents(), client_.get(),
+ base::BindRepeating(&autofill::BrowserDriverInitHook, client_.get(),
+ "en-US"));
+ }
+
+ void TearDown() override {
+ factory_.reset();
+ client_.reset();
+ agent_.reset();
+ content::RenderViewHostTestHarness::TearDown();
}
void NavigateMainFrame(base::StringPiece url) {
+ // One call of HideAutofillPopup() comes from ContentAutofillDriverFactory.
+ // A second one may come from BrowserAutofillManager::Reset().
+ EXPECT_CALL(*client_, HideAutofillPopup(PopupHidingReason::kNavigation))
+ .Times(Between(1, 2));
content::NavigationSimulator::CreateBrowserInitiated(GURL(url),
web_contents())
->Commit();
@@ -68,11 +160,14 @@ class ContentAutofillDriverFactoryTest
}
protected:
- MockAutofillClient client_;
+ version_info::Channel channel_;
+ std::unique_ptr<MockAutofillAgent> agent_;
+ std::unique_ptr<MockAutofillClient> client_;
std::unique_ptr<ContentAutofillDriverFactory> factory_;
};
TEST_F(ContentAutofillDriverFactoryTest, MainDriver) {
+ NavigateMainFrame("https://a.com/");
ContentAutofillDriver* main_driver = factory_test_api().GetDriver(main_rfh());
EXPECT_TRUE(main_driver);
EXPECT_EQ(factory_test_api().num_drivers(), 1u);
@@ -86,14 +181,8 @@ TEST_F(ContentAutofillDriverFactoryTest, MainDriver) {
class ContentAutofillDriverFactoryTest_WithTwoFrames
: public ContentAutofillDriverFactoryTest {
public:
- void SetUp() override {
- ContentAutofillDriverFactoryTest::SetUp();
- CHECK(main_rfh());
- NavigateChildFrame("https://b.com/");
- CHECK(child_rfh_);
- }
-
void NavigateChildFrame(base::StringPiece url) {
+ CHECK(main_rfh());
if (!child_rfh_) {
child_rfh_ = content::RenderFrameHostTester::For(main_rfh())
->AppendChild(std::string("child"));
@@ -109,6 +198,8 @@ class ContentAutofillDriverFactoryTest_WithTwoFrames
};
TEST_F(ContentAutofillDriverFactoryTest_WithTwoFrames, TwoDrivers) {
+ NavigateMainFrame("https://a.com/");
+ NavigateChildFrame("https://b.com/");
ASSERT_TRUE(main_rfh());
ASSERT_TRUE(child_rfh());
ContentAutofillDriver* main_driver = factory_->DriverForFrame(main_rfh());
@@ -138,13 +229,15 @@ class ContentAutofillDriverFactoryTest_WithTwoFrames_PickOne
}
};
-INSTANTIATE_TEST_SUITE_P(,
+INSTANTIATE_TEST_SUITE_P(ContentAutofillDriverFactoryTest,
ContentAutofillDriverFactoryTest_WithTwoFrames_PickOne,
testing::Bool());
// Tests that a driver is removed in RenderFrameDeleted().
TEST_P(ContentAutofillDriverFactoryTest_WithTwoFrames_PickOne,
RenderFrameDeleted) {
+ NavigateMainFrame("https://a.com/");
+ NavigateChildFrame("https://b.com/");
ASSERT_TRUE(picked_rfh() == main_rfh() || picked_rfh() == child_rfh());
ContentAutofillDriver* main_driver = factory_->DriverForFrame(main_rfh());
ContentAutofillDriver* child_driver = factory_->DriverForFrame(child_rfh());
@@ -161,7 +254,8 @@ TEST_P(ContentAutofillDriverFactoryTest_WithTwoFrames_PickOne,
// Tests that OnVisibilityChanged() hides the popup.
TEST_F(ContentAutofillDriverFactoryTest, TabHidden) {
- EXPECT_CALL(client_, HideAutofillPopup(PopupHidingReason::kTabGone));
+ NavigateMainFrame("https://a.com/");
+ EXPECT_CALL(*client_, HideAutofillPopup(PopupHidingReason::kTabGone));
factory_->OnVisibilityChanged(content::Visibility::HIDDEN);
}
@@ -191,7 +285,7 @@ class ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes
};
INSTANTIATE_TEST_SUITE_P(
- ,
+ ContentAutofillDriverFactoryTest,
ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes,
testing::Combine(testing::Bool(), testing::Bool()));
@@ -199,17 +293,10 @@ INSTANTIATE_TEST_SUITE_P(
// router.
TEST_P(ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes,
SameDocumentNavigation) {
+ NavigateMainFrame("https://a.com/");
content::RenderFrameHost* orig_rfh = main_rfh();
ContentAutofillDriver* orig_driver = factory_->DriverForFrame(orig_rfh);
-
- // One call of HideAutofillPopup() comes from ContentAutofillDriverFactory,
- // the second one from BrowserAutofillManager::Reset(). One of them may be
- // redundant.
- EXPECT_CALL(client_, HideAutofillPopup(PopupHidingReason::kNavigation))
- .Times(Between(1, 2));
-
NavigateMainFrame("https://a.com/#same-site");
-
ASSERT_EQ(orig_rfh, main_rfh());
EXPECT_EQ(factory_test_api().GetDriver(orig_rfh), orig_driver);
EXPECT_EQ(factory_test_api().num_drivers(), 1u);
@@ -225,6 +312,7 @@ TEST_P(ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes,
// afterwards.
TEST_P(ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes,
SameOriginNavigation) {
+ NavigateMainFrame("https://a.com/");
content::RenderFrameHost* orig_rfh = main_rfh();
ContentAutofillDriver* orig_driver = factory_->DriverForFrame(orig_rfh);
@@ -232,14 +320,7 @@ TEST_P(ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes,
// AutofillManager::Reset(), which is blocked by ContentAutofillDriver's use
// of the factory callback.
- // One call of HideAutofillPopup() comes from ContentAutofillDriverFactory,
- // the second one from BrowserAutofillManager::Reset(). One of them may be
- // redundant.
- EXPECT_CALL(client_, HideAutofillPopup(PopupHidingReason::kNavigation))
- .Times(Between(1, 2));
-
NavigateMainFrame("https://a.com/after-navigation");
-
EXPECT_EQ(factory_test_api().GetDriver(orig_rfh), orig_driver);
// If BFCache is enabled, there will be 2 drivers as the old document is still
// around.
@@ -250,6 +331,7 @@ TEST_P(ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes,
// cross-origin navigation.
TEST_P(ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes,
CrossOriginNavigation) {
+ NavigateMainFrame("https://a.com/");
content::RenderFrameHost* orig_rfh = main_rfh();
content::GlobalRenderFrameHostId orig_rfh_id = orig_rfh->GetGlobalId();
ContentAutofillDriver* orig_driver = factory_->DriverForFrame(orig_rfh);
@@ -258,12 +340,6 @@ TEST_P(ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes,
EXPECT_EQ(factory_test_api().GetDriver(orig_rfh), orig_driver);
EXPECT_EQ(factory_test_api().num_drivers(), 1u);
- // One call of HideAutofillPopup() comes from ContentAutofillDriverFactory,
- // the second one from BrowserAutofillManager::Reset(). One of them may be
- // redundant.
- EXPECT_CALL(client_, HideAutofillPopup(PopupHidingReason::kNavigation))
- .Times(Between(1, 2));
-
NavigateMainFrame("https://different-origin-after-navigation.com/");
ASSERT_NE(orig_rfh_id, main_rfh()->GetGlobalId());
@@ -282,11 +358,11 @@ TEST_P(ContentAutofillDriverFactoryTest_WithOrWithoutBfCacheAndIframes,
// Fixture for testing that Autofill is enabled in fenced frames unless
// AutofillEnableWithinFencedFrame is enabled. The bool parameter
// enables/disables that feature.
-class ContentAutofillDriverFactoryFencedFramesTest
- : public content::RenderViewHostTestHarness,
+class ContentAutofillDriverFactoryTest_FencedFrames
+ : public ContentAutofillDriverFactoryTest,
public ::testing::WithParamInterface<bool> {
public:
- ContentAutofillDriverFactoryFencedFramesTest() {
+ ContentAutofillDriverFactoryTest_FencedFrames() {
std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled;
std::vector<base::Feature> disabled;
enabled.push_back(
@@ -299,57 +375,65 @@ class ContentAutofillDriverFactoryFencedFramesTest
scoped_feature_list_.InitWithFeaturesAndParameters(enabled, disabled);
}
- ~ContentAutofillDriverFactoryFencedFramesTest() override = default;
-
- void SetUp() override {
- content::RenderViewHostTestHarness::SetUp();
- factory_ = ContentAutofillDriverFactoryTestApi::Create(
- web_contents(), &client_, "en_US",
- BrowserAutofillManager::AutofillDownloadManagerState::
- ENABLE_AUTOFILL_DOWNLOAD_MANAGER,
- AutofillManager::AutofillManagerFactoryCallback());
- }
-
- void NavigateAndCommitInFrame(const std::string& url,
- content::RenderFrameHost* rfh) {
- auto navigation =
- content::NavigationSimulator::CreateRendererInitiated(GURL(url), rfh);
- // These tests simulate loading events manually.
- navigation->SetKeepLoading(true);
- navigation->Start();
- navigation->Commit();
- }
+ ~ContentAutofillDriverFactoryTest_FencedFrames() override = default;
bool autofill_enabled_in_fencedframe() const { return GetParam(); }
- ContentAutofillDriverFactory& factory() { return *factory_; }
-
private:
base::test::ScopedFeatureList scoped_feature_list_;
- TestAutofillClient client_;
- std::unique_ptr<ContentAutofillDriverFactory> factory_;
};
-TEST_P(ContentAutofillDriverFactoryFencedFramesTest,
+TEST_P(ContentAutofillDriverFactoryTest_FencedFrames,
DisableAutofillWithinFencedFrame) {
- NavigateAndCommitInFrame("http://test.org", main_rfh());
+ NavigateMainFrame("http://test.org");
content::RenderFrameHost* fenced_frame_root =
content::RenderFrameHostTester::For(main_rfh())->AppendFencedFrame();
content::RenderFrameHost* fenced_frame_subframe =
content::RenderFrameHostTester::For(fenced_frame_root)
->AppendChild("iframe");
- EXPECT_NE(nullptr, factory().DriverForFrame(main_rfh()));
+ EXPECT_NE(nullptr, factory_->DriverForFrame(main_rfh()));
if (autofill_enabled_in_fencedframe()) {
- EXPECT_NE(nullptr, factory().DriverForFrame(fenced_frame_root));
- EXPECT_NE(nullptr, factory().DriverForFrame(fenced_frame_subframe));
+ EXPECT_NE(nullptr, factory_->DriverForFrame(fenced_frame_root));
+ EXPECT_NE(nullptr, factory_->DriverForFrame(fenced_frame_subframe));
} else {
- EXPECT_EQ(nullptr, factory().DriverForFrame(fenced_frame_root));
- EXPECT_EQ(nullptr, factory().DriverForFrame(fenced_frame_subframe));
+ EXPECT_EQ(nullptr, factory_->DriverForFrame(fenced_frame_root));
+ EXPECT_EQ(nullptr, factory_->DriverForFrame(fenced_frame_subframe));
}
}
INSTANTIATE_TEST_SUITE_P(ContentAutofillDriverFactoryTest,
- ContentAutofillDriverFactoryFencedFramesTest,
+ ContentAutofillDriverFactoryTest_FencedFrames,
testing::Bool());
+struct AgentSetupParam {
+ version_info::Channel channel;
+ bool heavy_scraping_enabled;
+};
+
+class ContentAutofillDriverFactoryTest_AgentSetup
+ : public ContentAutofillDriverFactoryTest,
+ public ::testing::WithParamInterface<AgentSetupParam> {
+ public:
+ ContentAutofillDriverFactoryTest_AgentSetup()
+ : ContentAutofillDriverFactoryTest(GetParam().channel) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ ContentAutofillDriverFactoryTest,
+ ContentAutofillDriverFactoryTest_AgentSetup,
+ testing::Values(AgentSetupParam{version_info::Channel::CANARY, true},
+ AgentSetupParam{version_info::Channel::DEV, true},
+ AgentSetupParam{version_info::Channel::UNKNOWN, false},
+ AgentSetupParam{version_info::Channel::BETA, false},
+ AgentSetupParam{version_info::Channel::STABLE, false}));
+
+TEST_P(ContentAutofillDriverFactoryTest_AgentSetup,
+ EnableHeavyFormDataScraping) {
+ EXPECT_CALL(*agent_, EnableHeavyFormDataScraping())
+ .Times(GetParam().heavy_scraping_enabled ? 1 : 0);
+ NavigateMainFrame("https://a.com/");
+ base::RunLoop().RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(agent_.get());
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc
index 98e69e740a1..1764571296a 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc
@@ -55,8 +55,6 @@ using ::testing::SaveArg;
namespace {
const char kAppLocale[] = "en-US";
-const BrowserAutofillManager::AutofillDownloadManagerState kDownloadState =
- BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER;
class FakeAutofillAgent : public mojom::AutofillAgent {
public:
@@ -159,9 +157,6 @@ class FakeAutofillAgent : public mojom::AutofillAgent {
return true;
}
- // Mocked mojom::AutofillAgent methods:
- MOCK_METHOD(void, EnableHeavyFormDataScraping, (), (override));
-
private:
void CallDone() {
if (!quit_closure_.is_null())
@@ -171,14 +166,14 @@ class FakeAutofillAgent : public mojom::AutofillAgent {
// mojom::AutofillAgent:
void TriggerReparse() override {}
- void FillOrPreviewForm(int32_t id,
+ void FillOrPreviewForm(int32_t query_id,
const FormData& form,
mojom::RendererFormDataAction action) override {
if (action == mojom::RendererFormDataAction::kPreview) {
- preview_form_id_ = id;
+ preview_form_id_ = query_id;
preview_form_form_ = form;
} else {
- fill_form_id_ = id;
+ fill_form_id_ = query_id;
fill_form_form_ = form;
}
CallDone();
@@ -231,6 +226,8 @@ class FakeAutofillAgent : public mojom::AutofillAgent {
CallDone();
}
+ void EnableHeavyFormDataScraping() override {}
+
void FillPasswordSuggestion(const std::u16string& username,
const std::u16string& password) override {}
@@ -287,35 +284,29 @@ class FakeAutofillAgent : public mojom::AutofillAgent {
class MockBrowserAutofillManager : public BrowserAutofillManager {
public:
MockBrowserAutofillManager(AutofillDriver* driver, AutofillClient* client)
- : BrowserAutofillManager(driver, client, kAppLocale, kDownloadState) {}
- ~MockBrowserAutofillManager() override {}
+ : BrowserAutofillManager(driver,
+ client,
+ kAppLocale,
+ EnableDownloadManager(false)) {}
+ ~MockBrowserAutofillManager() override = default;
MOCK_METHOD(void, Reset, (), (override));
MOCK_METHOD(bool, ShouldParseForms, (const std::vector<FormData>&), ());
};
-class MockAutofillClient : public TestAutofillClient {};
-
class TestContentAutofillDriver : public ContentAutofillDriver {
public:
TestContentAutofillDriver(content::RenderFrameHost* rfh,
- AutofillClient* client,
- ContentAutofillRouter* router)
- : ContentAutofillDriver(
- rfh,
- client,
- kAppLocale,
- router,
- kDownloadState,
- AutofillManager::AutofillManagerFactoryCallback()) {
- std::unique_ptr<MockBrowserAutofillManager> autofill_manager(
- new MockBrowserAutofillManager(this, client));
- SetBrowserAutofillManager(std::move(autofill_manager));
+ ContentAutofillRouter* router,
+ AutofillClient* client)
+ : ContentAutofillDriver(rfh, router) {
+ set_autofill_manager(
+ std::make_unique<MockBrowserAutofillManager>(this, client));
}
~TestContentAutofillDriver() override = default;
virtual MockBrowserAutofillManager* mock_browser_autofill_manager() {
- return static_cast<MockBrowserAutofillManager*>(browser_autofill_manager());
+ return static_cast<MockBrowserAutofillManager*>(autofill_manager());
}
using ContentAutofillDriver::DidNavigateFrame;
@@ -345,14 +336,14 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness,
// happy for when AppendChild is called.
NavigateAndCommit(GURL("about:blank"));
- test_autofill_client_ = std::make_unique<MockAutofillClient>();
+ test_autofill_client_ = std::make_unique<TestAutofillClient>();
router_ = std::make_unique<ContentAutofillRouter>();
driver_ = std::make_unique<TestContentAutofillDriver>(
- web_contents()->GetMainFrame(), test_autofill_client_.get(),
- router_.get());
+ web_contents()->GetPrimaryMainFrame(), router_.get(),
+ test_autofill_client_.get());
blink::AssociatedInterfaceProvider* remote_interfaces =
- web_contents()->GetMainFrame()->GetRemoteAssociatedInterfaces();
+ web_contents()->GetPrimaryMainFrame()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::AutofillAgent::Name_,
base::BindRepeating(&FakeAutofillAgent::BindPendingReceiver,
@@ -399,7 +390,7 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness,
bool autofill_across_iframes_ = false;
base::test::ScopedFeatureList scoped_feature_list_;
- std::unique_ptr<MockAutofillClient> test_autofill_client_;
+ std::unique_ptr<TestAutofillClient> test_autofill_client_;
std::unique_ptr<ContentAutofillRouter> router_;
std::unique_ptr<TestContentAutofillDriver> driver_;
@@ -437,17 +428,19 @@ TEST_P(ContentAutofillDriverTest, SetFrameAndFormMetaDataOfForm) {
EXPECT_EQ(
form.host_frame,
- LocalFrameToken(web_contents()->GetMainFrame()->GetFrameToken().value()));
+ LocalFrameToken(
+ web_contents()->GetPrimaryMainFrame()->GetFrameToken().value()));
EXPECT_EQ(form.url, GURL("https://hostname/path"));
EXPECT_EQ(form.full_url, GURL());
EXPECT_EQ(form.main_frame_origin,
- web_contents()->GetMainFrame()->GetLastCommittedOrigin());
+ web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
EXPECT_EQ(form.main_frame_origin,
url::Origin::CreateFromNormalizedTuple("https", "hostname", 443));
ASSERT_EQ(form.fields.size(), 1u);
EXPECT_EQ(
form.fields.front().host_frame,
- LocalFrameToken(web_contents()->GetMainFrame()->GetFrameToken().value()));
+ LocalFrameToken(
+ web_contents()->GetPrimaryMainFrame()->GetFrameToken().value()));
EXPECT_EQ(form2.host_frame, form.host_frame);
EXPECT_EQ(form2.url, form.url);
@@ -499,7 +492,7 @@ TEST_P(ContentAutofillDriverTest,
content::RenderFrameHostTester::For(child_rfh)->AppendChild(
"grandchild"));
auto grandchild_driver = std::make_unique<TestContentAutofillDriver>(
- grandchild_rfh, test_autofill_client_.get(), router_.get());
+ grandchild_rfh, router_.get(), test_autofill_client_.get());
ASSERT_TRUE(child_rfh->GetLastCommittedURL().IsAboutBlank());
ASSERT_TRUE(grandchild_rfh->GetLastCommittedURL().IsAboutBlank());
@@ -524,7 +517,8 @@ TEST_P(ContentAutofillDriverTest, SetFrameAndFormMetaDataOfField) {
EXPECT_NE(signature_without_meta_data, CalculateFormSignature(form));
EXPECT_EQ(
field.host_frame,
- LocalFrameToken(web_contents()->GetMainFrame()->GetFrameToken().value()));
+ LocalFrameToken(
+ web_contents()->GetPrimaryMainFrame()->GetFrameToken().value()));
EXPECT_EQ(field.host_form_id, form.unique_renderer_id);
EXPECT_EQ(field.host_form_signature, CalculateFormSignature(form));
@@ -687,33 +681,6 @@ TEST_P(ContentAutofillDriverTest, PreviewFieldWithValue) {
EXPECT_EQ(input_value, output_value);
}
-TEST_P(ContentAutofillDriverTest, EnableHeavyFormDataScraping) {
- struct TestCase {
- version_info::Channel channel;
- bool heavy_scraping_enabled;
- } kTestCases[] = {{version_info::Channel::CANARY, true},
- {version_info::Channel::DEV, true},
- {version_info::Channel::UNKNOWN, false},
- {version_info::Channel::BETA, false},
- {version_info::Channel::STABLE, false}};
-
- for (auto test_case : kTestCases) {
- SCOPED_TRACE(testing::Message()
- << "channel: "
- << version_info::GetChannelString(test_case.channel));
- test_autofill_client_->set_channel_for_testing(test_case.channel);
- EXPECT_CALL(fake_agent_, EnableHeavyFormDataScraping())
- .Times(test_case.heavy_scraping_enabled ? 1 : 0);
-
- std::unique_ptr<ContentAutofillDriver> driver(new TestContentAutofillDriver(
- web_contents()->GetMainFrame(), test_autofill_client_.get(),
- router_.get()));
-
- base::RunLoop().RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(&fake_agent_);
- }
-}
-
INSTANTIATE_TEST_SUITE_P(ContentAutofillDriverTest,
ContentAutofillDriverTest,
testing::Bool());
diff --git a/chromium/components/autofill/content/browser/content_autofill_router.cc b/chromium/components/autofill/content/browser/content_autofill_router.cc
index 229be2eb19b..3b8038f0caa 100644
--- a/chromium/components/autofill/content/browser/content_autofill_router.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_router.cc
@@ -349,14 +349,16 @@ void ContentAutofillRouter::SelectControlDidChange(
void ContentAutofillRouter::AskForValuesToFill(
ContentAutofillDriver* source,
- int32_t id,
+ int32_t query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) {
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) {
if (!base::FeatureList::IsEnabled(features::kAutofillAcrossIframes)) {
- source->AskForValuesToFillImpl(id, form, field, bounding_box,
- autoselect_first_suggestion);
+ source->AskForValuesToFillImpl(query_id, form, field, bounding_box,
+ autoselect_first_suggestion,
+ touch_to_fill_eligible);
return;
}
@@ -372,8 +374,9 @@ void ContentAutofillRouter::AskForValuesToFill(
AFCHECK(target, return );
SetLastQueriedSource(source);
SetLastQueriedTarget(target);
- target->AskForValuesToFillImpl(id, browser_form, field, bounding_box,
- autoselect_first_suggestion);
+ target->AskForValuesToFillImpl(query_id, browser_form, field, bounding_box,
+ autoselect_first_suggestion,
+ touch_to_fill_eligible);
}
void ContentAutofillRouter::HidePopup(ContentAutofillDriver* source) {
@@ -529,6 +532,29 @@ void ContentAutofillRouter::SelectFieldOptionsDidChange(
target->SelectFieldOptionsDidChangeImpl(browser_form);
}
+void ContentAutofillRouter::JavaScriptChangedAutofilledValue(
+ ContentAutofillDriver* source,
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) {
+ if (!base::FeatureList::IsEnabled(features::kAutofillAcrossIframes)) {
+ source->JavaScriptChangedAutofilledValueImpl(form, field, old_value);
+ return;
+ }
+
+ some_rfh_for_debugging_ = source->render_frame_host()->GetGlobalId();
+
+ form_forest_.UpdateTreeOfRendererForm(form, source);
+
+ TriggerReparseExcept(source);
+
+ const FormData& browser_form =
+ form_forest_.GetBrowserFormOfRendererForm(form);
+ auto* target = DriverOfFrame(browser_form.host_frame);
+ AFCHECK(target, return);
+ target->JavaScriptChangedAutofilledValueImpl(browser_form, field, old_value);
+}
+
void ContentAutofillRouter::FillFormForAssistant(
ContentAutofillDriver* source,
const AutofillableData& fill_data,
diff --git a/chromium/components/autofill/content/browser/content_autofill_router.h b/chromium/components/autofill/content/browser/content_autofill_router.h
index ddeb5dbd51f..61d97cdc6ea 100644
--- a/chromium/components/autofill/content/browser/content_autofill_router.h
+++ b/chromium/components/autofill/content/browser/content_autofill_router.h
@@ -189,11 +189,12 @@ class ContentAutofillRouter {
const FormFieldData& field,
const gfx::RectF& bounding_box);
void AskForValuesToFill(ContentAutofillDriver* source_driver,
- int32_t id,
+ int32_t query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion);
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible);
void HidePopup(ContentAutofillDriver* source_driver);
void FocusNoLongerOnForm(ContentAutofillDriver* source_driver,
bool had_interacted_form);
@@ -208,6 +209,10 @@ class ContentAutofillRouter {
void DidEndTextFieldEditing(ContentAutofillDriver* source_driver);
void SelectFieldOptionsDidChange(ContentAutofillDriver* source_driver,
const FormData& form);
+ void JavaScriptChangedAutofilledValue(ContentAutofillDriver* source,
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value);
// Event called by Autofill Assistant as if it was called by the renderer.
void FillFormForAssistant(ContentAutofillDriver* source_driver,
diff --git a/chromium/components/autofill/content/browser/form_forest_test_api.h b/chromium/components/autofill/content/browser/form_forest_test_api.h
index 97ab33cd989..84be1100ea9 100644
--- a/chromium/components/autofill/content/browser/form_forest_test_api.h
+++ b/chromium/components/autofill/content/browser/form_forest_test_api.h
@@ -6,6 +6,7 @@
#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_FORM_FOREST_TEST_API_H_
#include "base/containers/stack.h"
+#include "base/memory/raw_ptr.h"
#include "components/autofill/content/browser/form_forest.h"
namespace autofill {
@@ -67,7 +68,7 @@ class FormForestTestApi {
void ExpandForm(base::stack<FrameForm>& frontier, FrameForm frame_and_form);
// Non-null pointer to wrapped FormForest.
- FormForest* ff_;
+ raw_ptr<FormForest> ff_;
};
template <typename UnaryFunction>
diff --git a/chromium/components/autofill/content/browser/form_forest_unittest.cc b/chromium/components/autofill/content/browser/form_forest_unittest.cc
index 6031aaf509d..0e20d765940 100644
--- a/chromium/components/autofill/content/browser/form_forest_unittest.cc
+++ b/chromium/components/autofill/content/browser/form_forest_unittest.cc
@@ -273,7 +273,7 @@ std::vector<std::vector<T>> FlattenedPermutations(
class MockContentAutofillDriver : public ContentAutofillDriver {
public:
explicit MockContentAutofillDriver(content::RenderFrameHost* rfh)
- : ContentAutofillDriver(rfh) {}
+ : ContentAutofillDriver(rfh, /*autofill_router=*/nullptr) {}
LocalFrameToken token() { return Token(render_frame_host()); }
diff --git a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom
index 0e35c55bc15..65a019dddf3 100644
--- a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom
+++ b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom
@@ -16,8 +16,10 @@ interface AutofillAgent {
// Instructs the renderer to fill or preview the active form with the given
// form data. Refer to AutofillDriver.AskForValuesToFill for comments
- // about the |id|.
- FillOrPreviewForm(int32 id, FormData form, RendererFormDataAction action);
+ // about the |query_id|.
+ FillOrPreviewForm(int32 query_id,
+ FormData form,
+ RendererFormDataAction action);
// Sends the heuristic and server field type predictions to the renderer.
FieldTypePredictionsAvailable(array<FormDataPredictions> forms);
diff --git a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom
index 05db5f9fd1f..5db3e7b4486 100644
--- a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom
+++ b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom
@@ -52,12 +52,16 @@ interface AutofillDriver {
// Queries the browser for Autofill suggestions for a form input field.
// For autofill this means asking the user which values to fill.
- // |id| is the request ID which is used to map responses correctly.
- AskForValuesToFill(int32 id,
- FormData form,
- FormFieldData field,
- gfx.mojom.RectF bounding_box,
- bool autoselect_first_suggestion);
+ // |query_id| is the request ID which is used to map responses correctly.
+ // |touch_to_fill_eligible| indicates if the Touch To Fill surface could
+ // be used for showing suggestions (e.g. when the user taps an input element
+ // but not on text change).
+ AskForValuesToFill(int32 query_id,
+ FormData form,
+ FormFieldData field,
+ gfx.mojom.RectF bounding_box,
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible);
// Instructs the browser to hide the Autofill popup if it is open.
HidePopup();
@@ -82,6 +86,15 @@ interface AutofillDriver {
// Sent when a text field is done editing.
DidEndTextFieldEditing();
+
+ // Sent when |field| was in autofilled state but JavaScript modified the
+ // value. Note that from a renderer's perspective, modifying the value with
+ // JavaScript leads to a state where the field is not considered autofilled
+ // anymore. So this notification won't be sent again until the field gets
+ // autofilled again.
+ JavaScriptChangedAutofilledValue(FormData form,
+ FormFieldData field,
+ mojo_base.mojom.String16 old_value);
};
// There is one instance of this interface per web contents in the browser
@@ -139,6 +152,7 @@ interface PasswordManagerDriver {
// Instructs the browser to show the Touch To Fill UI and whether the form is
// ready for submission after filling.
+ [EnableIf=is_android]
ShowTouchToFill(SubmissionReadinessState submission_readiness);
// Checks the safe browsing reputation of the website where the focused
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc
index ddd2cf1543e..88d7f1dd32f 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/autofill_agent.cc
@@ -176,13 +176,14 @@ class AutofillAgent::DeferringAutofillDriver : public mojom::AutofillDriver {
void SelectFieldOptionsDidChange(const FormData& form) override {
DeferMsg(&mojom::AutofillDriver::SelectFieldOptionsDidChange, form);
}
- void AskForValuesToFill(int32_t id,
+ void AskForValuesToFill(int32_t query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) override {
- DeferMsg(&mojom::AutofillDriver::AskForValuesToFill, id, form, field,
- bounding_box, autoselect_first_suggestion);
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) override {
+ DeferMsg(&mojom::AutofillDriver::AskForValuesToFill, query_id, form, field,
+ bounding_box, autoselect_first_suggestion, touch_to_fill_eligible);
}
void HidePopup() override { DeferMsg(&mojom::AutofillDriver::HidePopup); }
void FocusNoLongerOnForm(bool had_interacted_form) override {
@@ -204,17 +205,18 @@ class AutofillAgent::DeferringAutofillDriver : public mojom::AutofillDriver {
void DidEndTextFieldEditing() override {
DeferMsg(&mojom::AutofillDriver::DidEndTextFieldEditing);
}
+ void JavaScriptChangedAutofilledValue(
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) override {
+ DeferMsg(&mojom::AutofillDriver::JavaScriptChangedAutofilledValue, form,
+ field, old_value);
+ }
AutofillAgent* agent_ = nullptr;
base::WeakPtrFactory<DeferringAutofillDriver> weak_ptr_factory_{this};
};
-AutofillAgent::ShowSuggestionsOptions::ShowSuggestionsOptions()
- : autofill_on_empty_values(false),
- requires_caret_at_end(false),
- show_full_suggestion_list(false),
- autoselect_first_suggestion(false) {}
-
AutofillAgent::AutofillAgent(content::RenderFrame* render_frame,
PasswordAutofillAgent* password_autofill_agent,
PasswordGenerationAgent* password_generation_agent,
@@ -443,9 +445,7 @@ void AutofillAgent::OnTextFieldDidChange(const WebInputElement& element) {
return;
}
- ShowSuggestionsOptions options;
- options.requires_caret_at_end = true;
- ShowSuggestions(element, options);
+ ShowSuggestions(element, {.requires_caret_at_end = true});
FormData form;
FormFieldData field;
@@ -465,21 +465,17 @@ void AutofillAgent::TextFieldDidReceiveKeyDown(const WebInputElement& element,
if (event.windows_key_code == ui::VKEY_DOWN ||
event.windows_key_code == ui::VKEY_UP) {
- ShowSuggestionsOptions options;
- options.autofill_on_empty_values = true;
- options.requires_caret_at_end = true;
- options.autoselect_first_suggestion =
- ShouldAutoselectFirstSuggestionOnArrowDown();
- ShowSuggestions(element, options);
+ ShowSuggestions(element,
+ {.autofill_on_empty_values = true,
+ .requires_caret_at_end = true,
+ .autoselect_first_suggestion =
+ ShouldAutoselectFirstSuggestionOnArrowDown()});
}
}
void AutofillAgent::OpenTextDataListChooser(const WebInputElement& element) {
DCHECK(IsOwnedByFrame(element, render_frame()));
-
- ShowSuggestionsOptions options;
- options.autofill_on_empty_values = true;
- ShowSuggestions(element, options);
+ ShowSuggestions(element, {.autofill_on_empty_values = true});
}
// Notifies the AutofillDriver about changes in the <datalist> options in
@@ -538,14 +534,14 @@ void AutofillAgent::TriggerRefillIfNeeded(const FormData& form) {
}
// mojom::AutofillAgent:
-void AutofillAgent::FillOrPreviewForm(int32_t id,
+void AutofillAgent::FillOrPreviewForm(int32_t query_id,
const FormData& form,
mojom::RendererFormDataAction action) {
// If |element_| is null or not focused, a Autofill was triggered from another
// frame. In this case, set |element_| to some form field as if Autofill had
// been triggered from that field. This is necessary because currently
// AutofillAgent's relies on |elemet_| in many places.
- if (id == kCrossFrameFill && !form.fields.empty() &&
+ if (query_id == kCrossFrameFill && !form.fields.empty() &&
(element_.IsNull() || !element_.Focused())) {
WebDocument document = render_frame()->GetWebFrame()->GetDocument();
element_ = form_util::FindFormControlElementByUniqueRendererId(
@@ -555,8 +551,9 @@ void AutofillAgent::FillOrPreviewForm(int32_t id,
if (element_.IsNull())
return;
- if (id != autofill_query_id_ && id != kCrossFrameFill &&
- (action == mojom::RendererFormDataAction::kPreview || id != kNoQueryId)) {
+ if (query_id != autofill_query_id_ && query_id != kCrossFrameFill &&
+ (action == mojom::RendererFormDataAction::kPreview ||
+ query_id != kNoQueryId)) {
return;
}
@@ -571,7 +568,7 @@ void AutofillAgent::FillOrPreviewForm(int32_t id,
was_last_action_fill_ = true;
// If this is a re-fill, replace the triggering element if it's invalid.
- if (id == kNoQueryId)
+ if (query_id == kNoQueryId)
ReplaceElementIfNowInvalid(form);
query_node_autofill_state_ = element_.GetAutofillState();
@@ -634,10 +631,8 @@ void AutofillAgent::FillFieldWithValue(FieldRendererId field_id,
}
WebInputElement input_element = element_.DynamicTo<WebInputElement>();
- if (!input_element.IsNull()) {
- DoFillFieldWithValue(value, input_element);
- input_element.SetAutofillState(WebAutofillState::kAutofilled);
- }
+ if (!input_element.IsNull())
+ DoFillFieldWithValue(value, input_element, WebAutofillState::kAutofilled);
}
void AutofillAgent::PreviewFieldWithValue(FieldRendererId field_id,
@@ -663,17 +658,17 @@ void AutofillAgent::SetSuggestionAvailability(
WebInputElement input_element = element_.DynamicTo<WebInputElement>();
if (!input_element.IsNull()) {
switch (state) {
- case autofill::mojom::AutofillState::kAutofillAvailable:
+ case mojom::AutofillState::kAutofillAvailable:
WebAXObject::FromWebNode(input_element)
.HandleAutofillStateChanged(
blink::WebAXAutofillState::kAutofillAvailable);
return;
- case autofill::mojom::AutofillState::kAutocompleteAvailable:
+ case mojom::AutofillState::kAutocompleteAvailable:
WebAXObject::FromWebNode(input_element)
.HandleAutofillStateChanged(
blink::WebAXAutofillState::kAutocompleteAvailable);
return;
- case autofill::mojom::AutofillState::kNoSuggestions:
+ case mojom::AutofillState::kNoSuggestions:
WebAXObject::FromWebNode(input_element)
.HandleAutofillStateChanged(
blink::WebAXAutofillState::kNoSuggestions);
@@ -726,7 +721,7 @@ void AutofillAgent::AcceptDataListSuggestion(
new_value = base::JoinString(parts, u",");
}
- DoFillFieldWithValue(new_value, input_element);
+ DoFillFieldWithValue(new_value, input_element, WebAutofillState::kNotFilled);
}
void AutofillAgent::FillPasswordSuggestion(const std::u16string& username,
@@ -835,7 +830,8 @@ void AutofillAgent::ShowSuggestions(const WebFormControlElement& element,
return;
}
- QueryAutofillSuggestions(element, options.autoselect_first_suggestion);
+ QueryAutofillSuggestions(element, options.autoselect_first_suggestion,
+ options.touch_to_fill_eligible);
}
void AutofillAgent::SetQueryPasswordSuggestion(bool query) {
@@ -902,7 +898,8 @@ void AutofillAgent::SetFieldsEligibleForManualFilling(
void AutofillAgent::QueryAutofillSuggestions(
const WebFormControlElement& element,
- bool autoselect_first_suggestion) {
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) {
blink::WebLocalFrame* frame = element.GetDocument().GetFrame();
if (!frame)
return;
@@ -947,17 +944,18 @@ void AutofillAgent::QueryAutofillSuggestions(
}
is_popup_possibly_visible_ = true;
- GetAutofillDriver().AskForValuesToFill(autofill_query_id_, form, field,
- field.bounds,
- autoselect_first_suggestion);
+ GetAutofillDriver().AskForValuesToFill(
+ autofill_query_id_, form, field, field.bounds,
+ autoselect_first_suggestion, touch_to_fill_eligible);
}
void AutofillAgent::DoFillFieldWithValue(const std::u16string& value,
- WebInputElement& node) {
+ WebInputElement& node,
+ WebAutofillState autofill_state) {
DCHECK(IsOwnedByFrame(node, render_frame()));
form_tracker_.set_ignore_control_changes(true);
- node.SetAutofillValue(blink::WebString::FromUTF16(value));
+ node.SetAutofillValue(blink::WebString::FromUTF16(value), autofill_state);
password_autofill_agent_->UpdateStateForTextChange(node);
form_tracker_.set_ignore_control_changes(false);
}
@@ -969,7 +967,6 @@ void AutofillAgent::DoPreviewFieldWithValue(const std::u16string& value,
ClearPreviewedForm();
query_node_autofill_state_ = element_.GetAutofillState();
node.SetSuggestedValue(blink::WebString::FromUTF16(value));
- node.SetAutofillState(WebAutofillState::kPreviewed);
form_util::PreviewSuggestion(node.SuggestedValue().Utf16(),
node.Value().Utf16(), &node);
previewed_elements_.push_back(node);
@@ -985,7 +982,7 @@ void AutofillAgent::TriggerReparse() {
void AutofillAgent::ProcessForms() {
FormCache::UpdateFormCacheResult cache =
- form_cache_.ExtractNewForms(field_data_manager_.get());
+ form_cache_.UpdateFormCache(field_data_manager_.get());
// Always communicate to browser process for topmost frame.
if (!cache.updated_forms.empty() || !cache.removed_forms.empty() ||
@@ -1098,9 +1095,12 @@ void AutofillAgent::BatchSelectOptionChange(
bool AutofillAgent::ShouldSuppressKeyboard(
const WebFormControlElement& element) {
// Note: Consider supporting other autofill types in the future as well.
- return password_autofill_agent_->ShouldSuppressKeyboard() ||
- (autofill_assistant_agent_ &&
- autofill_assistant_agent_->ShouldSuppressKeyboard());
+#if BUILDFLAG(IS_ANDROID)
+ if (password_autofill_agent_->ShouldSuppressKeyboard())
+ return true;
+#endif
+ return autofill_assistant_agent_ &&
+ autofill_assistant_agent_->ShouldSuppressKeyboard();
}
void AutofillAgent::FormElementReset(const WebFormElement& form) {
@@ -1130,20 +1130,19 @@ void AutofillAgent::FormControlElementClicked(
if (input_element.IsNull() && !form_util::IsTextAreaElement(element))
return;
-#if defined(ANDROID)
+#if BUILDFLAG(IS_ANDROID)
password_autofill_agent_->TryToShowTouchToFill(element);
#endif
- ShowSuggestionsOptions options;
- options.autofill_on_empty_values = true;
- // Even if the user has not edited an input element, it may still contain a
- // value: A default value filled by the website. In that case, we don't want
- // to elide suggestions that don't have a common prefix with the default
- // value.
- options.show_full_suggestion_list =
- element.IsAutofilled() || !element.UserHasEditedTheField();
-
- ShowSuggestions(element, options);
+ ShowSuggestions(
+ element, {.autofill_on_empty_values = true,
+ // Even if the user has not edited an input element, it may
+ // still contain a value: A default value filled by the website.
+ // In that case, we don't want to elide suggestions that don't
+ // have a common prefix with the default value.
+ .show_full_suggestion_list =
+ element.IsAutofilled() || !element.UserHasEditedTheField(),
+ .touch_to_fill_eligible = TouchToFillEligible(true)});
SendPotentiallySubmittedFormToBrowser();
}
@@ -1173,6 +1172,20 @@ void AutofillAgent::AjaxSucceeded() {
SendPotentiallySubmittedFormToBrowser();
}
+void AutofillAgent::JavaScriptChangedAutofilledValue(
+ const blink::WebFormControlElement& element,
+ const blink::WebString& old_value) {
+ if (old_value == element.Value())
+ return;
+ FormData form;
+ FormFieldData field;
+ if (FindFormAndFieldForFormControlElement(element, field_data_manager_.get(),
+ &form, &field)) {
+ GetAutofillDriver().JavaScriptChangedAutofilledValue(form, field,
+ old_value.Utf16());
+ }
+}
+
void AutofillAgent::OnProvisionallySaveForm(
const WebFormElement& form,
const WebFormControlElement& element,
@@ -1201,7 +1214,7 @@ void AutofillAgent::OnProvisionallySaveForm(
WebFormControlElement field =
form_util::FindFormControlElementByUniqueRendererId(
doc, field_id, /*form_to_be_searched =*/FormRendererId());
- return !field.IsNull() && form_util::IsWebElementVisible(field);
+ return !field.IsNull() && form_util::IsWebElementFocusable(field);
});
}
formless_elements_user_edited_.insert(
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h
index 5fb4867be2c..4de7f06270c 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/autofill_agent.h
@@ -92,7 +92,7 @@ class AutofillAgent : public content::RenderFrameObserver,
// mojom::AutofillAgent:
void TriggerReparse() override;
- void FillOrPreviewForm(int32_t id,
+ void FillOrPreviewForm(int32_t query_id,
const FormData& form,
mojom::RendererFormDataAction action) override;
void FieldTypePredictionsAvailable(
@@ -162,25 +162,26 @@ class AutofillAgent : public content::RenderFrameObserver,
// Flags passed to ShowSuggestions.
struct ShowSuggestionsOptions {
- // All fields are default initialized to false.
- ShowSuggestionsOptions();
-
// Specifies that suggestions should be shown when |element| contains no
// text.
- bool autofill_on_empty_values;
+ bool autofill_on_empty_values{false};
// Specifies that suggestions should be shown when the caret is not
// after the last character in the element.
- bool requires_caret_at_end;
+ bool requires_caret_at_end{false};
// Specifies that all autofill suggestions should be shown and none should
// be elided because of the current value of |element| (relevant for inline
// autocomplete).
- bool show_full_suggestion_list;
+ bool show_full_suggestion_list{false};
// Specifies that the first suggestion must be auto-selected when the
// dropdown is shown. Enabled when the user presses ARROW_DOWN on a field.
- bool autoselect_first_suggestion;
+ bool autoselect_first_suggestion{false};
+
+ // Specifies that suggestions are triggered in a way it makes sense to
+ // prompt the Touch To Fill surface first (e.g. on click).
+ TouchToFillEligible touch_to_fill_eligible{false};
};
// content::RenderFrameObserver:
@@ -213,6 +214,9 @@ class AutofillAgent : public content::RenderFrameObserver,
void DataListOptionsChanged(const blink::WebInputElement& element) override;
void UserGestureObserved() override;
void AjaxSucceeded() override;
+ void JavaScriptChangedAutofilledValue(
+ const blink::WebFormControlElement& element,
+ const blink::WebString& old_value) override;
void DidCompleteFocusChangeInFrame() override;
void DidReceiveLeftMouseDownOrGestureTapInNode(
const blink::WebNode& node) override;
@@ -245,7 +249,8 @@ class AutofillAgent : public content::RenderFrameObserver,
// Queries the browser for Autocomplete and Autofill suggestions for the given
// |element|.
void QueryAutofillSuggestions(const blink::WebFormControlElement& element,
- bool autoselect_first_suggestion);
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible);
// Sets the selected value of the the field identified by |field_id| to
// |suggested_value|.
@@ -254,7 +259,8 @@ class AutofillAgent : public content::RenderFrameObserver,
// Set |node| to display the given |value|.
void DoFillFieldWithValue(const std::u16string& value,
- blink::WebInputElement& node);
+ blink::WebInputElement& node,
+ blink::WebAutofillState autofill_state);
// Set |node| to display the given |value| as a preview. The preview is
// visible on screen to the user, but not visible to the page via the DOM or
diff --git a/chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc b/chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc
index 083ad4e6931..ecd2e892178 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc
+++ b/chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc
@@ -85,12 +85,19 @@ class MockAutofillDriver : public mojom::AutofillDriver {
(const FormData& form),
(override));
MOCK_METHOD(void,
+ JavaScriptChangedAutofilledValue,
+ (const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value),
+ (override));
+ MOCK_METHOD(void,
AskForValuesToFill,
- (int32_t id,
+ (int32_t query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion),
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible),
(override));
MOCK_METHOD(void, HidePopup, (), (override));
MOCK_METHOD(void,
@@ -195,39 +202,28 @@ class AutofillAgentTest : public content::RenderViewTest {
std::unique_ptr<AutofillAssistantAgent> autofill_assistant_agent_;
};
-// The parameter indicates if kAutofillDisplaceRemovedForms is enabled.
-class AutofillAgentTestWithFeatures
- : public AutofillAgentTest,
- public ::testing::WithParamInterface<bool> {
+// Enables AutofillAcrossIframes.
+class AutofillAgentTestWithFeatures : public AutofillAgentTest {
public:
AutofillAgentTestWithFeatures() {
- std::vector<base::Feature> enabled;
- std::vector<base::Feature> disabled;
- enabled.push_back(features::kAutofillAcrossIframes);
- (GetParam() ? enabled : disabled)
- .push_back(features::kAutofillDisplaceRemovedForms);
- scoped_features_.InitWithFeatures(enabled, disabled);
+ scoped_features_.InitAndEnableFeature(features::kAutofillAcrossIframes);
}
private:
base::test::ScopedFeatureList scoped_features_;
};
-INSTANTIATE_TEST_SUITE_P(AutofillAgentTest,
- AutofillAgentTestWithFeatures,
- ::testing::Bool());
-
-TEST_P(AutofillAgentTestWithFeatures, FormsSeen_Empty) {
+TEST_F(AutofillAgentTestWithFeatures, FormsSeen_Empty) {
EXPECT_CALL(autofill_driver_, FormsSeen(SizeIs(0), SizeIs(0)));
LoadHTML(R"(<body> </body>)");
}
-TEST_P(AutofillAgentTestWithFeatures, FormsSeen_NoEmpty) {
+TEST_F(AutofillAgentTestWithFeatures, FormsSeen_NoEmpty) {
EXPECT_CALL(autofill_driver_, FormsSeen(SizeIs(0), SizeIs(0)));
LoadHTML(R"(<body> <form></form> </body>)");
}
-TEST_P(AutofillAgentTestWithFeatures, FormsSeen_NewFormUnowned) {
+TEST_F(AutofillAgentTestWithFeatures, FormsSeen_NewFormUnowned) {
EXPECT_CALL(autofill_driver_,
FormsSeen(HasSingleElementWhich(HasFormId(0), HasNumFields(1),
HasNumChildFrames(0)),
@@ -235,7 +231,7 @@ TEST_P(AutofillAgentTestWithFeatures, FormsSeen_NewFormUnowned) {
LoadHTML(R"(<body> <input> </body>)");
}
-TEST_P(AutofillAgentTestWithFeatures, FormsSeen_NewForm) {
+TEST_F(AutofillAgentTestWithFeatures, FormsSeen_NewForm) {
EXPECT_CALL(autofill_driver_,
FormsSeen(HasSingleElementWhich(HasFormId(1), HasNumFields(1),
HasNumChildFrames(0)),
@@ -243,7 +239,7 @@ TEST_P(AutofillAgentTestWithFeatures, FormsSeen_NewForm) {
LoadHTML(R"(<body> <form><input></form> </body>)");
}
-TEST_P(AutofillAgentTestWithFeatures, FormsSeen_NewIframe) {
+TEST_F(AutofillAgentTestWithFeatures, FormsSeen_NewIframe) {
EXPECT_CALL(autofill_driver_,
FormsSeen(HasSingleElementWhich(HasFormId(1), HasNumFields(0),
HasNumChildFrames(1)),
@@ -251,7 +247,7 @@ TEST_P(AutofillAgentTestWithFeatures, FormsSeen_NewIframe) {
LoadHTML(R"(<body> <form><iframe></iframe></form> </body>)");
}
-TEST_P(AutofillAgentTestWithFeatures, FormsSeen_UpdatedForm) {
+TEST_F(AutofillAgentTestWithFeatures, FormsSeen_UpdatedForm) {
{
EXPECT_CALL(autofill_driver_,
FormsSeen(HasSingleElementWhich(HasFormId(1), HasNumFields(1),
@@ -272,7 +268,7 @@ TEST_P(AutofillAgentTestWithFeatures, FormsSeen_UpdatedForm) {
}
}
-TEST_P(AutofillAgentTestWithFeatures, FormsSeen_RemovedForm) {
+TEST_F(AutofillAgentTestWithFeatures, FormsSeen_RemovedForm) {
{
EXPECT_CALL(autofill_driver_, FormsSeen(SizeIs(1), SizeIs(0)));
LoadHTML(R"(<body> <form><input></form> </body>)");
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc
index 36bcb5f13a7..d6a787ff5bc 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.cc
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc
@@ -324,8 +324,8 @@ bool IsLabelValid(base::StringPiece16 inferred_label) {
// Shared function for InferLabelFromPrevious() and InferLabelFromNext().
bool InferLabelFromSibling(const WebFormControlElement& element,
bool forward,
- std::u16string* label,
- FormFieldData::LabelSource* label_source) {
+ std::u16string& label,
+ FormFieldData::LabelSource& label_source) {
std::u16string inferred_label;
FormFieldData::LabelSource inferred_label_source =
FormFieldData::LabelSource::kUnknown;
@@ -395,8 +395,8 @@ bool InferLabelFromSibling(const WebFormControlElement& element,
base::TrimWhitespace(inferred_label, base::TRIM_ALL, &inferred_label);
if (IsLabelValid(inferred_label)) {
- *label = std::move(inferred_label);
- *label_source = inferred_label_source;
+ label = std::move(inferred_label);
+ label_source = inferred_label_source;
return true;
}
return false;
@@ -473,16 +473,16 @@ void FindElementsWithButtonFeatures(const WebElementCollection& elements,
// or Some Text <img><input ...>
// or <b>Some Text</b><br/> <input ...>.
bool InferLabelFromPrevious(const WebFormControlElement& element,
- std::u16string* label,
- FormFieldData::LabelSource* label_source) {
+ std::u16string& label,
+ FormFieldData::LabelSource& label_source) {
return InferLabelFromSibling(element, /*forward=*/false, label, label_source);
}
// Same as InferLabelFromPrevious(), but in the other direction.
// Useful for cases like: <span><input type="checkbox">Label For Checkbox</span>
bool InferLabelFromNext(const WebFormControlElement& element,
- std::u16string* label,
- FormFieldData::LabelSource* label_source) {
+ std::u16string& label,
+ FormFieldData::LabelSource& label_source) {
return InferLabelFromSibling(element, /*forward=*/true, label, label_source);
}
@@ -519,62 +519,20 @@ std::u16string InferLabelFromValueAttr(const WebFormControlElement& element) {
return std::u16string();
}
-// Helper for |InferLabelForElement()| that infers a label, if possible, from
-// enclosing list item,
-// e.g. <li>Some Text<input ...><input ...><input ...></li>
-std::u16string InferLabelFromListItem(const WebFormControlElement& element) {
- WebNode parent = element.ParentNode();
- static base::NoDestructor<WebString> kListItem("li");
- while (!parent.IsNull() && parent.IsElementNode() &&
- !parent.To<WebElement>().HasHTMLTagName(*kListItem)) {
- parent = parent.ParentNode();
- }
-
- if (!parent.IsNull() && HasTagName(parent, *kListItem))
- return FindChildText(parent);
-
- return std::u16string();
-}
-
-// Helper for |InferLabelForElement()| that infers a label, if possible, from
-// enclosing label,
-// e.g. <label>Some Text<input ...><input ...><input ...></label>
-std::u16string InferLabelFromEnclosingLabel(
- const WebFormControlElement& element) {
- WebNode parent = element.ParentNode();
- static base::NoDestructor<WebString> kLabel("label");
- while (!parent.IsNull() && parent.IsElementNode() &&
- !parent.To<WebElement>().HasHTMLTagName(*kLabel)) {
- parent = parent.ParentNode();
- }
-
- if (!parent.IsNull() && HasTagName(parent, *kLabel))
- return FindChildText(parent);
-
- return std::u16string();
-}
-
-// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// Helper for `InferLabelForElement()` that infers a label, if possible, from
// surrounding table structure,
// e.g. <tr><td>Some Text</td><td><input ...></td></tr>
// or <tr><th>Some Text</th><td><input ...></td></tr>
// or <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr>
// or <tr><th><b>Some Text</b></th><td><b><input ...></b></td></tr>
-std::u16string InferLabelFromTableColumn(const WebFormControlElement& element) {
+// `cell` represents the <td> tag containing the input element.
+std::u16string InferLabelFromTableColumn(const WebNode& cell) {
static base::NoDestructor<WebString> kTableCell("td");
- WebNode parent = element.ParentNode();
- while (!parent.IsNull() && parent.IsElementNode() &&
- !parent.To<WebElement>().HasHTMLTagName(*kTableCell)) {
- parent = parent.ParentNode();
- }
-
- if (parent.IsNull())
- return std::u16string();
-
+ DCHECK(HasTagName(cell, *kTableCell));
// Check all previous siblings, skipping non-element nodes, until we find a
// non-empty text block.
std::u16string inferred_label;
- WebNode previous = parent.PreviousSibling();
+ WebNode previous = cell.PreviousSibling();
static base::NoDestructor<WebString> kTableHeader("th");
while (inferred_label.empty() && !previous.IsNull()) {
if (HasTagName(previous, *kTableCell) ||
@@ -587,8 +545,8 @@ std::u16string InferLabelFromTableColumn(const WebFormControlElement& element) {
return inferred_label;
}
-// Helper for |InferLabelForElement()| that infers a label, if possible, from
-// surrounding table structure,
+// Helper for `InferLabelForElement()` that infers a label, if possible, from
+// surrounding table structure.
//
// If there are multiple cells and the row with the input matches up with the
// previous row, then look for a specific cell within the previous row.
@@ -597,25 +555,13 @@ std::u16string InferLabelFromTableColumn(const WebFormControlElement& element) {
//
// Otherwise, just look in the entire previous row.
// e.g. <tr><td>Some Text</td></tr><tr><td><input ...></td></tr>
-std::u16string InferLabelFromTableRow(const WebFormControlElement& element) {
+// `cell` represents the <td> tag containing the input element.
+std::u16string InferLabelFromTableRow(const WebNode& cell) {
static base::NoDestructor<WebString> kTableCell("td");
+ DCHECK(HasTagName(cell, *kTableCell));
std::u16string inferred_label;
- // First find the <td> that contains |element|.
- WebNode cell = element.ParentNode();
- while (!cell.IsNull()) {
- if (cell.IsElementNode() &&
- cell.To<WebElement>().HasHTMLTagName(*kTableCell)) {
- break;
- }
- cell = cell.ParentNode();
- }
-
- // Not in a cell - bail out.
- if (cell.IsNull())
- return inferred_label;
-
- // Count the cell holding |element|.
+ // Count the cell holding the input element.
size_t cell_count = CalculateTableCellColumnSpan(cell.To<WebElement>());
size_t cell_position = 0;
size_t cell_position_end = cell_count - 1;
@@ -623,8 +569,7 @@ std::u16string InferLabelFromTableRow(const WebFormControlElement& element) {
// Count cells to the left to figure out |element|'s cell's position.
for (WebNode cell_it = cell.PreviousSibling(); !cell_it.IsNull();
cell_it = cell_it.PreviousSibling()) {
- if (cell_it.IsElementNode() &&
- cell_it.To<WebElement>().HasHTMLTagName(*kTableCell)) {
+ if (HasTagName(cell_it, *kTableCell)) {
cell_position += CalculateTableCellColumnSpan(cell_it.To<WebElement>());
}
}
@@ -632,8 +577,7 @@ std::u16string InferLabelFromTableRow(const WebFormControlElement& element) {
// Count cells to the right.
for (WebNode cell_it = cell.NextSibling(); !cell_it.IsNull();
cell_it = cell_it.NextSibling()) {
- if (cell_it.IsElementNode() &&
- cell_it.To<WebElement>().HasHTMLTagName(*kTableCell)) {
+ if (HasTagName(cell_it, *kTableCell)) {
cell_count += CalculateTableCellColumnSpan(cell_it.To<WebElement>());
}
}
@@ -644,22 +588,16 @@ std::u16string InferLabelFromTableRow(const WebFormControlElement& element) {
// Find the current row.
static base::NoDestructor<WebString> kTableRow("tr");
- WebNode parent = element.ParentNode();
- while (!parent.IsNull() && parent.IsElementNode() &&
- !parent.To<WebElement>().HasHTMLTagName(*kTableRow)) {
+ WebNode parent = cell.ParentNode();
+ while (!parent.IsNull() && !HasTagName(parent, *kTableRow)) {
parent = parent.ParentNode();
}
-
if (parent.IsNull())
return inferred_label;
// Now find the previous row.
WebNode row_it = parent.PreviousSibling();
- while (!row_it.IsNull()) {
- if (row_it.IsElementNode() &&
- row_it.To<WebElement>().HasHTMLTagName(*kTableRow)) {
- break;
- }
+ while (!row_it.IsNull() && !HasTagName(row_it, *kTableRow)) {
row_it = row_it.PreviousSibling();
}
@@ -700,14 +638,13 @@ std::u16string InferLabelFromTableRow(const WebFormControlElement& element) {
while (inferred_label.empty() && !previous.IsNull()) {
if (HasTagName(previous, *kTableRow))
inferred_label = FindChildText(previous);
-
previous = previous.PreviousSibling();
}
return inferred_label;
}
-// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// Helper for `InferLabelForElement()` that infers a label, if possible, from
// a surrounding div table,
// e.g. <div>Some Text<span><input ...></span></div>
// e.g. <div>Some Text</div><div><input ...></div>
@@ -762,53 +699,82 @@ std::u16string InferLabelFromDivTable(const WebFormControlElement& element) {
return inferred_label;
}
-// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// Helper for `InferLabelForElement()` that infers a label, if possible, from
// a surrounding definition list,
// e.g. <dl><dt>Some Text</dt><dd><input ...></dd></dl>
// e.g. <dl><dt><b>Some Text</b></dt><dd><b><input ...></b></dd></dl>
-std::u16string InferLabelFromDefinitionList(
- const WebFormControlElement& element) {
- static base::NoDestructor<WebString> kDefinitionData("dd");
- WebNode parent = element.ParentNode();
- while (!parent.IsNull() && parent.IsElementNode() &&
- !parent.To<WebElement>().HasHTMLTagName(*kDefinitionData))
- parent = parent.ParentNode();
-
- if (parent.IsNull() || !HasTagName(parent, *kDefinitionData))
- return std::u16string();
+std::u16string InferLabelFromDefinitionList(const WebNode& dd) {
+ static base::NoDestructor<WebString> kDefinitionDescriptionTag("dd");
+ static base::NoDestructor<WebString> kDefinitionTermTag("dt");
+ DCHECK(HasTagName(dd, *kDefinitionDescriptionTag));
// Skip by any intervening text nodes.
- WebNode previous = parent.PreviousSibling();
+ WebNode previous = dd.PreviousSibling();
while (!previous.IsNull() && previous.IsTextNode())
previous = previous.PreviousSibling();
- static base::NoDestructor<WebString> kDefinitionTag("dt");
- if (previous.IsNull() || !HasTagName(previous, *kDefinitionTag))
+ if (previous.IsNull() || !HasTagName(previous, *kDefinitionTermTag))
return std::u16string();
-
return FindChildText(previous);
}
-// Returns the element type for all ancestor nodes in CAPS, starting with the
-// parent node.
-std::vector<std::string> AncestorTagNames(
- const WebFormControlElement& element) {
- std::vector<std::string> tag_names;
- for (WebNode parent_node = element.ParentNode(); !parent_node.IsNull();
- parent_node = parent_node.ParentNode()) {
- if (!parent_node.IsElementNode())
+// Helper for `InferLabelForElement()` that infers a label, if possible, from
+// the first surrounding <label>, <div>, <td>, <dd> or <li> tag (if any).
+// See `FindChildText()`, `InferLabelFromDivTable()`,
+// `InferLabelFromTableColumn()`, `InferLabelFromTableRow()` and
+// `InferLabelFromDefinitionList()` for examples how a label is extracted from
+// the different tags.
+bool InferLabelFromAncestors(const WebFormControlElement& element,
+ std::u16string& label,
+ FormFieldData::LabelSource& label_source) {
+ std::set<std::string> seen_tag_names;
+ for (WebNode parent = element.ParentNode(); !parent.IsNull();
+ parent = parent.ParentNode()) {
+ if (!parent.IsElementNode())
+ continue;
+
+ std::string tag_name = parent.To<WebElement>().TagName().Utf8();
+ if (base::Contains(seen_tag_names, tag_name))
continue;
+ seen_tag_names.insert(tag_name);
- tag_names.push_back(parent_node.To<WebElement>().TagName().Utf8());
+ FormFieldData::LabelSource ancestor_label_source;
+ std::u16string inferred_label;
+ if (tag_name == "LABEL") {
+ ancestor_label_source = FormFieldData::LabelSource::kLabelTag;
+ inferred_label = FindChildText(parent);
+ } else if (tag_name == "DIV") {
+ ancestor_label_source = FormFieldData::LabelSource::kDivTable;
+ inferred_label = InferLabelFromDivTable(element);
+ } else if (tag_name == "TD") {
+ ancestor_label_source = FormFieldData::LabelSource::kTdTag;
+ inferred_label = InferLabelFromTableColumn(parent);
+ if (!IsLabelValid(inferred_label))
+ inferred_label = InferLabelFromTableRow(parent);
+ } else if (tag_name == "DD") {
+ ancestor_label_source = FormFieldData::LabelSource::kDdTag;
+ inferred_label = InferLabelFromDefinitionList(parent);
+ } else if (tag_name == "LI") {
+ ancestor_label_source = FormFieldData::LabelSource::kLiTag;
+ inferred_label = FindChildText(parent);
+ } else if (tag_name == "FIELDSET") {
+ break;
+ }
+
+ if (IsLabelValid(inferred_label)) {
+ label_source = ancestor_label_source;
+ label = std::move(inferred_label);
+ return true;
+ }
}
- return tag_names;
+ return false;
}
-// Infers corresponding label for |element| from surrounding context in the DOM,
+// Infers corresponding label for `element` from surrounding context in the DOM,
// e.g. the contents of the preceding <p> tag or text element.
bool InferLabelForElement(const WebFormControlElement& element,
- std::u16string* label,
- FormFieldData::LabelSource* label_source) {
+ std::u16string& label,
+ FormFieldData::LabelSource& label_source) {
if (IsCheckableElement(element.DynamicTo<WebInputElement>())) {
if (InferLabelFromNext(element, label, label_source))
return true;
@@ -820,63 +786,28 @@ bool InferLabelForElement(const WebFormControlElement& element,
// If we didn't find a label, check for placeholder text.
std::u16string inferred_label = InferLabelFromPlaceholder(element);
if (IsLabelValid(inferred_label)) {
- *label_source = FormFieldData::LabelSource::kPlaceHolder;
- *label = std::move(inferred_label);
+ label_source = FormFieldData::LabelSource::kPlaceHolder;
+ label = std::move(inferred_label);
return true;
}
// If we didn't find a placeholder, check for aria-label text.
inferred_label = InferLabelFromAriaLabel(element);
if (IsLabelValid(inferred_label)) {
- *label_source = FormFieldData::LabelSource::kAriaLabel;
- *label = std::move(inferred_label);
+ label_source = FormFieldData::LabelSource::kAriaLabel;
+ label = std::move(inferred_label);
return true;
}
- // For all other searches that involve traversing up the tree, the search
- // order is based on which tag is the closest ancestor to |element|.
- std::vector<std::string> tag_names = AncestorTagNames(element);
- std::set<std::string> seen_tag_names;
- FormFieldData::LabelSource ancestor_label_source =
- FormFieldData::LabelSource::kUnknown;
- for (const std::string& tag_name : tag_names) {
- if (base::Contains(seen_tag_names, tag_name))
- continue;
-
- seen_tag_names.insert(tag_name);
- if (tag_name == "LABEL") {
- ancestor_label_source = FormFieldData::LabelSource::kLabelTag;
- inferred_label = InferLabelFromEnclosingLabel(element);
- } else if (tag_name == "DIV") {
- ancestor_label_source = FormFieldData::LabelSource::kDivTable;
- inferred_label = InferLabelFromDivTable(element);
- } else if (tag_name == "TD") {
- ancestor_label_source = FormFieldData::LabelSource::kTdTag;
- inferred_label = InferLabelFromTableColumn(element);
- if (!IsLabelValid(inferred_label))
- inferred_label = InferLabelFromTableRow(element);
- } else if (tag_name == "DD") {
- ancestor_label_source = FormFieldData::LabelSource::kDdTag;
- inferred_label = InferLabelFromDefinitionList(element);
- } else if (tag_name == "LI") {
- ancestor_label_source = FormFieldData::LabelSource::kLiTag;
- inferred_label = InferLabelFromListItem(element);
- } else if (tag_name == "FIELDSET") {
- break;
- }
-
- if (IsLabelValid(inferred_label)) {
- *label_source = ancestor_label_source;
- *label = std::move(inferred_label);
- return true;
- }
- }
+ // If we didn't find a label, check the `element`'s ancestors.
+ if (InferLabelFromAncestors(element, label, label_source))
+ return true;
// If we didn't find a label, check the value attr used as the placeholder.
inferred_label = InferLabelFromValueAttr(element);
if (IsLabelValid(inferred_label)) {
- *label_source = FormFieldData::LabelSource::kValue;
- *label = std::move(inferred_label);
+ label_source = FormFieldData::LabelSource::kValue;
+ label = std::move(inferred_label);
return true;
}
return false;
@@ -1139,32 +1070,45 @@ std::vector<WebFormControlElement> ForEachMatchingFormFieldCommon(
continue;
}
- if (element.GetAutofillState() == WebAutofillState::kAutofilled)
+ if (element.GetAutofillState() == WebAutofillState::kAutofilled &&
+ !data.fields[i].force_override) {
continue;
+ }
+
+ const std::u16string current_element_value = element.Value().Utf16();
- // A text field, with a non-empty value that is entered by the user,
- // and is NOT the value of the input field's "value" or "placeholder"
- // attribute, is skipped. Some sites fill the fields with formatting
- // string. To tell the difference between the values entered by the user
+ // A text field is skipped if it has a non-empty value that is entered by
+ // the user and is NOT the value of the input field's "value" or
+ // "placeholder" attribute. (The "value" attribute in <input value="foo">
+ // indicates the value of the input element at loading time, not its runtime
+ // value after the user entered something into the field.)
+ //
+ // Some sites fill the fields with a formatting string like (___)-___-____.
+ // To tell the difference between the values entered by the user
// and the site, we'll sanitize the value. If the sanitized value is
// empty, it means that the site has filled the field, in this case, the
// field is not skipped. Nevertheless the below condition does not hold
// for sites set the |kValue| attribute to the user-input value.
+ auto HasAttributeWithValue = [&element](const auto& attribute,
+ const auto& value) {
+ return element.HasAttribute(attribute) &&
+ base::i18n::ToLower(element.GetAttribute(attribute).Utf16()) ==
+ base::i18n::ToLower(value);
+ };
if ((IsAutofillableInputElement(input_element) ||
IsTextAreaElement(element)) &&
element.UserHasEditedTheField() &&
- !SanitizedFieldIsEmpty(element.Value().Utf16()) &&
- (!element.HasAttribute(*kValue) ||
- element.GetAttribute(*kValue) != element.Value()) &&
- (!element.HasAttribute(*kPlaceholder) ||
- base::i18n::ToLower(element.GetAttribute(*kPlaceholder).Utf16()) !=
- base::i18n::ToLower(element.Value().Utf16()))) {
+ !SanitizedFieldIsEmpty(current_element_value) &&
+ !data.fields[i].force_override &&
+ !HasAttributeWithValue(*kValue, current_element_value) &&
+ !HasAttributeWithValue(*kPlaceholder, current_element_value)) {
continue;
}
// Check if we should autofill/preview/clear a select element or leave it.
if (IsSelectElement(element) && element.UserHasEditedTheField() &&
- !SanitizedFieldIsEmpty(element.Value().Utf16())) {
+ !SanitizedFieldIsEmpty(current_element_value) &&
+ !data.fields[i].force_override) {
continue;
}
@@ -1252,7 +1196,8 @@ void FillFormField(const FormFieldData& data,
WebInputElement input_element = field->DynamicTo<WebInputElement>();
if (IsCheckableElement(input_element)) {
- input_element.SetChecked(IsChecked(data.check_status), true);
+ input_element.SetChecked(IsChecked(data.check_status), true,
+ WebAutofillState::kAutofilled);
} else {
std::u16string value = data.value;
if (IsTextInput(input_element) || IsMonthInput(input_element)) {
@@ -1260,15 +1205,14 @@ void FillFormField(const FormFieldData& data,
// returns the default maxlength value.
TruncateString(&value, input_element.MaxLength());
}
- field->SetAutofillValue(blink::WebString::FromUTF16(value));
+ field->SetAutofillValue(blink::WebString::FromUTF16(value),
+ WebAutofillState::kAutofilled);
}
// Setting the form might trigger JavaScript, which is capable of
// destroying the frame.
if (!field->GetDocument().GetFrame())
return;
- field->SetAutofillState(WebAutofillState::kAutofilled);
-
if (is_initiating_node &&
((IsTextInput(input_element) || IsMonthInput(input_element)) ||
IsTextAreaElement(*field))) {
@@ -1300,10 +1244,8 @@ void PreviewFormField(const FormFieldData& data,
// returns the default maxlength value.
input_element.SetSuggestedValue(blink::WebString::FromUTF16(
data.value.substr(0, input_element.MaxLength())));
- input_element.SetAutofillState(WebAutofillState::kPreviewed);
} else if (IsTextAreaElement(*field) || IsSelectElement(*field)) {
field->SetSuggestedValue(blink::WebString::FromUTF16(data.value));
- field->SetAutofillState(WebAutofillState::kPreviewed);
}
if (is_initiating_node &&
@@ -1542,7 +1484,7 @@ bool FormOrFieldsetsToFormData(
control_elements[element_index];
FormFieldData& field = form->fields[field_index++];
if (field.label.empty())
- InferLabelForElement(control_element, &field.label, &field.label_source);
+ InferLabelForElement(control_element, field.label, field.label_source);
TruncateString(&field.label, kMaxDataLength);
if (optional_field && *form_control_element == control_element) {
@@ -1677,6 +1619,10 @@ void FindFormElementUpShadowRoots(const WebElement& element,
} // namespace
+// TODO(crbug.com/1335257): This check is very similar to IsWebElementVisible()
+// (see the documentation there for the subtle differences: zoom factor and
+// scroll size). We can probably merge them but should do a Finch experiment
+// about it.
bool IsVisibleIframe(const WebElement& element) {
DCHECK(element.HasHTMLTagName("iframe"));
// It is common for not-humanly-visible elements to have very small yet
@@ -1791,53 +1737,28 @@ bool ExtractFormData(const WebFormElement& form_element,
bool IsSomeControlElementVisible(
blink::WebLocalFrame* frame,
const std::set<FieldRendererId>& control_elements) {
- SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
- "Autofill.IsSomeControlElementVisibleDuration");
-
WebDocument doc = frame->GetDocument();
if (doc.IsNull())
return false;
- if (base::FeatureList::IsEnabled(
- features::kAutofillUseUnassociatedListedElements)) {
- // Returns true iff at least one element from |fields| is visible and there
- // exists an element in |control_elements| with the same field renderer id.
- // The average case time complexity is O(N log M), where N is the number of
- // elements in |fields| and M is the number of elements in
- // |control_elements|.
- auto ContainsVisibleField =
- [&](const WebVector<WebFormControlElement>& fields) {
- return base::ranges::any_of(
- fields, [&](const WebFormControlElement& field) {
- return IsWebElementVisible(field) &&
- base::Contains(control_elements,
- GetFieldRendererId(field));
- });
- };
-
- return base::ranges::any_of(doc.Forms(), ContainsVisibleField,
- &WebFormElement::GetFormControlElements) ||
- ContainsVisibleField(doc.UnassociatedFormControls());
- } else {
- // This is basically a set intersection of |control_elements| and the form
- // controls on the website.
- // Iterating over all form controls on the website and checking their
- // existence in control_elements makes this O(|DOM| + N log M), where N is
- // the number of form controls on the website and M the number of elements
- // in |control_elements|.
- WebElementCollection elements = doc.All();
-
- for (WebElement element = elements.FirstItem(); !element.IsNull();
- element = elements.NextItem()) {
- if (!element.IsFormControlElement() || !IsWebElementVisible(element))
- continue;
- WebFormControlElement control = element.To<WebFormControlElement>();
- FieldRendererId field_renderer_id(control.UniqueRendererFormControlId());
- if (control_elements.find(field_renderer_id) != control_elements.end())
- return true;
- }
- return false;
- }
+ // Returns true iff at least one element from |fields| is visible and there
+ // exists an element in |control_elements| with the same field renderer id.
+ // The average case time complexity is O(N log M), where N is the number of
+ // elements in |fields| and M is the number of elements in
+ // |control_elements|.
+ auto ContainsVisibleField =
+ [&](const WebVector<WebFormControlElement>& fields) {
+ return base::ranges::any_of(
+ fields, [&](const WebFormControlElement& field) {
+ return IsWebElementFocusable(field) &&
+ base::Contains(control_elements,
+ GetFieldRendererId(field));
+ });
+ };
+
+ return base::ranges::any_of(doc.Forms(), ContainsVisibleField,
+ &WebFormElement::GetFormControlElements) ||
+ ContainsVisibleField(doc.UnassociatedFormControls());
}
GURL GetCanonicalActionForForm(const WebFormElement& form) {
@@ -1882,6 +1803,10 @@ bool IsCheckableElement(const WebInputElement& element) {
return element.IsCheckbox() || element.IsRadioButton();
}
+bool IsCheckableElement(const WebElement& element) {
+ return IsCheckableElement(element.DynamicTo<WebInputElement>());
+}
+
bool IsAutofillableInputElement(const WebInputElement& element) {
return IsTextInput(element) || IsMonthInput(element) ||
IsCheckableElement(element);
@@ -1893,10 +1818,20 @@ bool IsAutofillableElement(const WebFormControlElement& element) {
IsSelectElement(element) || IsTextAreaElement(element);
}
-bool IsWebElementVisible(const blink::WebElement& element) {
+bool IsWebElementFocusable(const blink::WebElement& element) {
return element.IsFocusable();
}
+bool IsWebElementVisible(blink::WebElement element) {
+ auto HasMinSize = [](auto size) {
+ constexpr int kMinPixelSize = 10;
+ return size.width() >= kMinPixelSize && size.height() >= kMinPixelSize;
+ };
+ return !element.IsNull() && IsWebElementFocusable(element) &&
+ (IsCheckableElement(element) || HasMinSize(element.GetClientSize()) ||
+ HasMinSize(element.GetScrollSize()));
+}
+
std::u16string GetFormIdentifier(const WebFormElement& form) {
std::u16string identifier = form.GetName().Utf16();
if (identifier.empty())
@@ -1971,8 +1906,8 @@ void WebFormControlElementToFormField(
field->form_control_ax_id = element.GetAxId();
field->form_control_type = element.FormControlTypeForAutofill().Utf8();
field->autocomplete_attribute = GetAutocompleteAttribute(element);
- if (base::LowerCaseEqualsASCII(element.GetAttribute(*kRole).Utf16(),
- "presentation")) {
+ if (base::EqualsCaseInsensitiveASCII(element.GetAttribute(*kRole).Utf16(),
+ "presentation")) {
field->role = FormFieldData::RoleAttribute::kPresentation;
}
@@ -2031,7 +1966,8 @@ void WebFormControlElementToFormField(
IsSelectElement(element)) {
// The browser doesn't need to differentiate between preview and autofill.
field->is_autofilled = element.IsAutofilled();
- field->is_focusable = IsWebElementVisible(element);
+ field->is_focusable = IsWebElementFocusable(element);
+ field->is_visible = IsWebElementVisible(element);
field->should_autocomplete = element.AutoComplete();
field->text_direction = GetTextDirectionForElement(element);
@@ -2160,8 +2096,7 @@ bool WebFormElementToFormData(
extract_mask, form, field);
}
-std::vector<WebFormControlElement>
-GetUnownedFormFieldElementsWithListedElements(
+std::vector<WebFormControlElement> GetUnownedFormFieldElements(
const WebDocument& document,
std::vector<WebElement>* fieldsets) {
std::vector<WebFormControlElement> unowned_form_field_elements =
@@ -2178,33 +2113,6 @@ GetUnownedFormFieldElementsWithListedElements(
return unowned_form_field_elements;
}
-std::vector<WebFormControlElement> GetUnownedFormFieldElements(
- const WebDocument& document,
- std::vector<WebElement>* fieldsets) {
- SCOPED_UMA_HISTOGRAM_TIMER_MICROS("Autofill.GetUnownedFormFieldsDuration");
- if (base::FeatureList::IsEnabled(
- features::kAutofillUseUnassociatedListedElements)) {
- return GetUnownedFormFieldElementsWithListedElements(document, fieldsets);
- }
- std::vector<WebFormControlElement> unowned_fieldset_children;
- const WebElementCollection& elements = document.All();
- for (WebElement element = elements.FirstItem(); !element.IsNull();
- element = elements.NextItem()) {
- if (element.IsFormControlElement()) {
- WebFormControlElement control = element.To<WebFormControlElement>();
- if (control.Form().IsNull())
- unowned_fieldset_children.push_back(control);
- }
-
- if (fieldsets && element.HasHTMLTagName("fieldset") &&
- !IsElementInsideFormOrFieldSet(element,
- /*consider_fieldset_tags=*/true)) {
- fieldsets->push_back(element);
- }
- }
- return unowned_fieldset_children;
-}
-
std::vector<WebFormControlElement> GetUnownedAutofillableFormFieldElements(
const WebDocument& document,
std::vector<WebElement>* fieldsets) {
@@ -2453,13 +2361,9 @@ ButtonTitleList GetButtonTitles(const WebFormElement& web_form,
if (web_form.IsNull()) {
const WebElement& body = document.Body();
if (!body.IsNull()) {
- SCOPED_UMA_HISTOGRAM_TIMER(
- "PasswordManager.ButtonTitlePerformance.NoFormTag");
button_titles = InferButtonTitlesForForm(body);
}
} else {
- SCOPED_UMA_HISTOGRAM_TIMER(
- "PasswordManager.ButtonTitlePerformance.HasFormTag");
button_titles = InferButtonTitlesForForm(web_form);
}
form_position->second = std::move(button_titles);
@@ -2473,8 +2377,8 @@ std::u16string FindChildTextWithIgnoreListForTesting(
}
bool InferLabelForElementForTesting(const WebFormControlElement& element,
- std::u16string* label,
- FormFieldData::LabelSource* label_source) {
+ std::u16string& label,
+ FormFieldData::LabelSource& label_source) {
return InferLabelForElement(element, label, label_source);
}
@@ -2491,95 +2395,57 @@ WebFormControlElement FindFormControlElementByUniqueRendererId(
const WebDocument& doc,
FieldRendererId queried_form_control,
absl::optional<FormRendererId> form_to_be_searched /*= absl::nullopt*/) {
- SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
- "Autofill.FindFormControlElementByUniqueRendererIdDuration");
-
- if (base::FeatureList::IsEnabled(
- features::kAutofillUseUnassociatedListedElements)) {
- auto FindField = [&](const WebVector<WebFormControlElement>& fields) {
- auto it =
- base::ranges::find(fields, queried_form_control, GetFieldRendererId);
- return it != fields.end() ? *it : WebFormControlElement();
- };
-
- auto IsCandidate = [&form_to_be_searched](
- const FormRendererId& expected_form_renderer_id) {
- return !form_to_be_searched.has_value() ||
- form_to_be_searched.value() == expected_form_renderer_id;
- };
-
- if (IsCandidate(FormRendererId())) {
- // Search the unowned form.
- WebFormControlElement e = FindField(doc.UnassociatedFormControls());
- if (form_to_be_searched == FormRendererId() || !e.IsNull())
- return e;
- }
- for (const WebFormElement& form : doc.Forms()) {
- // If the |form_to_be_searched| is specified, skip this form if it is not
- // the right one.
- if (!IsCandidate(GetFormRendererId(form))) {
- continue;
- }
- WebFormControlElement e = FindField(form.GetFormControlElements());
- if (form_to_be_searched == GetFormRendererId(form) || !e.IsNull())
- return e;
- }
- return WebFormControlElement();
- } else {
- WebElementCollection elements = doc.All();
+ auto FindField = [&](const WebVector<WebFormControlElement>& fields) {
+ auto it =
+ base::ranges::find(fields, queried_form_control, GetFieldRendererId);
+ return it != fields.end() ? *it : WebFormControlElement();
+ };
- for (WebElement element = elements.FirstItem(); !element.IsNull();
- element = elements.NextItem()) {
- if (!element.IsFormControlElement())
- continue;
- WebFormControlElement control = element.To<WebFormControlElement>();
- if (queried_form_control == GetFieldRendererId(control))
- return control;
+ auto IsCandidate =
+ [&form_to_be_searched](const FormRendererId& expected_form_renderer_id) {
+ return !form_to_be_searched.has_value() ||
+ form_to_be_searched.value() == expected_form_renderer_id;
+ };
+
+ if (IsCandidate(FormRendererId())) {
+ // Search the unowned form.
+ WebFormControlElement e = FindField(doc.UnassociatedFormControls());
+ if (form_to_be_searched == FormRendererId() || !e.IsNull())
+ return e;
+ }
+ for (const WebFormElement& form : doc.Forms()) {
+ // If the |form_to_be_searched| is specified, skip this form if it is not
+ // the right one.
+ if (!IsCandidate(GetFormRendererId(form))) {
+ continue;
}
- return WebFormControlElement();
+ WebFormControlElement e = FindField(form.GetFormControlElements());
+ if (form_to_be_searched == GetFormRendererId(form) || !e.IsNull())
+ return e;
}
+ return WebFormControlElement();
}
std::vector<WebFormControlElement> FindFormControlElementsByUniqueRendererId(
const WebDocument& doc,
const std::vector<FieldRendererId>& queried_form_controls) {
- SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
- "Autofill.FindFormControlElementsByUniqueRendererIdDuration");
-
std::vector<WebFormControlElement> result(queried_form_controls.size());
auto renderer_id_to_index_map = BuildRendererIdToIndex(queried_form_controls);
- if (base::FeatureList::IsEnabled(
- features::kAutofillUseUnassociatedListedElements)) {
- auto AddToResultIfQueried = [&](const WebFormControlElement& field) {
- auto it = renderer_id_to_index_map.find(GetFieldRendererId(field));
- if (it != renderer_id_to_index_map.end())
- result[it->second] = field;
- };
+ auto AddToResultIfQueried = [&](const WebFormControlElement& field) {
+ auto it = renderer_id_to_index_map.find(GetFieldRendererId(field));
+ if (it != renderer_id_to_index_map.end())
+ result[it->second] = field;
+ };
- for (const auto& form : doc.Forms()) {
- for (const auto& field : form.GetFormControlElements())
- AddToResultIfQueried(field);
- }
- for (const auto& field : doc.UnassociatedFormControls()) {
+ for (const auto& form : doc.Forms()) {
+ for (const auto& field : form.GetFormControlElements())
AddToResultIfQueried(field);
- }
- return result;
- } else {
- WebElementCollection elements = doc.All();
-
- for (WebElement element = elements.FirstItem(); !element.IsNull();
- element = elements.NextItem()) {
- if (!element.IsFormControlElement())
- continue;
- WebFormControlElement control = element.To<WebFormControlElement>();
- auto it = renderer_id_to_index_map.find(GetFieldRendererId(control));
- if (it == renderer_id_to_index_map.end())
- continue;
- result[it->second] = control;
- }
- return result;
}
+ for (const auto& field : doc.UnassociatedFormControls()) {
+ AddToResultIfQueried(field);
+ }
+ return result;
}
std::vector<WebFormControlElement> FindFormControlElementsByUniqueRendererId(
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h
index 19b97e12368..c9ae9cee1bc 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.h
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.h
@@ -85,7 +85,7 @@ enum ExtractMask {
//
// Future potential improvements include:
// * Detect potential visibility of elements with "overflow: visible".
-// (See Element::scrollWidth().)
+// (See WebElement::GetScrollSize().)
// * Detect invisibility of elements with
// - "position: absolute; {left,top,right,bottol}: -100px"
// - "opacity: 0.0"
@@ -168,10 +168,33 @@ bool IsAutofillableInputElement(const blink::WebInputElement& element);
// {Text, Radiobutton, Checkbox, Select, TextArea}.
bool IsAutofillableElement(const blink::WebFormControlElement& element);
-// True if this node can take focus. If layout is blocked, then the function
-// checks if the element takes up space in the layout, ie. this element or a
-// descendant has a non-empty bounding bounding client rect.
-bool IsWebElementVisible(const blink::WebElement& element);
+// True if this node can take focus. If the layout is blocked, then the function
+// checks if the element takes up space in the layout, i.e., this element or a
+// descendant has a non-empty bounding client rect.
+bool IsWebElementFocusable(const blink::WebElement& element);
+
+// A heuristic visibility detection. See crbug.com/1335257 for an overview of
+// relevant aspects.
+//
+// Note that WebElement::BoundsInViewport(), WebElement::GetClientSize(), and
+// WebElement::GetScrollSize() include the padding but do not include the border
+// and margin. BoundsInViewport() additionally scales the dimensions according
+// to the zoom factor.
+//
+// It seems that invisible fields on websites typically have dimensions between
+// 0 and 10 pixels, before the zoom factor. Therefore choosing `kMinPixelSize`
+// is easier without including the zoom factor. For that reason, this function
+// prefers GetClientSize() over BoundsInViewport().
+//
+// This function does not check the position in the viewport because fields in
+// iframes commonly are visible despite the body having height zero. Therefore,
+// `e.GetDocument().Body().BoundsInViewport().Intersects(e.BoundsInViewport())`
+// yields false negatives.
+//
+// Exposed for testing purposes.
+//
+// TODO(crbug.com/1335257): Can input fields or iframes actually overflow?
+bool IsWebElementVisible(blink::WebElement element);
// Returns the form's |name| attribute if non-empty; otherwise the form's |id|
// attribute.
@@ -343,8 +366,8 @@ std::u16string FindChildTextWithIgnoreListForTesting(
const blink::WebNode& node,
const std::set<blink::WebNode>& divs_to_skip);
bool InferLabelForElementForTesting(const blink::WebFormControlElement& element,
- std::u16string* label,
- FormFieldData::LabelSource* label_source);
+ std::u16string& label,
+ FormFieldData::LabelSource& label_source);
// Returns the form element by unique renderer id. Returns the null element if
// there is no form with the |form_renderer_id|.
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc
index 2781cc6e897..ba5a2e5c28b 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc
+++ b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc
@@ -54,106 +54,12 @@ namespace autofill {
namespace form_util {
namespace {
-struct AutofillFieldLabelSourceCase {
- const char* html;
- const FormFieldData::LabelSource label_source;
-};
-
struct AutofillFieldUtilCase {
const char* description;
const char* html;
const char16_t* expected_label;
};
-const char kElevenChildren[] =
- "<div id='target'>"
- "<div>child0</div>"
- "<div>child1</div>"
- "<div>child2</div>"
- "<div>child3</div>"
- "<div>child4</div>"
- "<div>child5</div>"
- "<div>child6</div>"
- "<div>child7</div>"
- "<div>child8</div>"
- "<div>child9</div>"
- "<div>child10</div>"
- "</div>";
-const char16_t kElevenChildrenExpected[] =
- u"child0child1child2child3child4child5child6child7child8";
-
-const char kElevenChildrenNested[] =
- "<div id='target'>"
- "<div>child0"
- "<div>child1"
- "<div>child2"
- "<div>child3"
- "<div>child4"
- "<div>child5"
- "<div>child6"
- "<div>child7"
- "<div>child8"
- "<div>child9"
- "<div>child10"
- "</div></div></div></div></div></div></div></div></div></div></div></div>";
-// Take 10 elements -1 for target element, -1 as text is a leaf element.
-const char16_t kElevenChildrenNestedExpected[] =
- u"child0child1child2child3child4";
-
-const char kSkipElement[] =
- "<div id='target'>"
- "<div>child0</div>"
- "<div class='skip'>child1</div>"
- "<div>child2</div>"
- "</div>";
-// TODO(crbug.com/796918): Should be child0child2
-const char16_t kSkipElementExpected[] = u"child0";
-
-const char kDivTableExample1[] =
- "<div>"
- "<div>label</div><div><input id='target'/></div>"
- "</div>";
-const char16_t kDivTableExample1Expected[] = u"label";
-
-const char kDivTableExample2[] =
- "<div>"
- "<div>label</div>"
- "<div>should be skipped<input/></div>"
- "<div><input id='target'/></div>"
- "</div>";
-const char16_t kDivTableExample2Expected[] = u"label";
-
-const char kDivTableExample3[] =
- "<div>"
- "<div>should be skipped<input/></div>"
- "<div>label</div>"
- "<div><input id='target'/></div>"
- "</div>";
-const char16_t kDivTableExample3Expected[] = u"label";
-
-const char kDivTableExample4[] =
- "<div>"
- "<div>should be skipped<input/></div>"
- "label"
- "<div><input id='target'/></div>"
- "</div>";
-// TODO(crbug.com/796918): Should be label
-const char16_t kDivTableExample4Expected[] = u"";
-
-const char kDivTableExample5[] =
- "<div>"
- "<div>label<div><input id='target'/></div>behind</div>"
- "</div>";
-// TODO(crbug.com/796918): Should be label
-const char16_t kDivTableExample5Expected[] = u"labelbehind";
-
-const char kDivTableExample6[] =
- "<div>"
- "<div>label<div><div>-<div><input id='target'/></div></div>"
- "</div>";
-// TODO(crbug.com/796918): Should be "label" or "label-"
-const char16_t kDivTableExample6Expected[] = u"";
-
void VerifyButtonTitleCache(const WebFormElement& form_target,
const ButtonTitleList& expected_button_titles,
const ButtonTitlesCache& actual_cache) {
@@ -184,14 +90,43 @@ TEST_F(FormAutofillUtilsTest, FindChildTextTest) {
{"simple test", "<div id='target'>test</div>", u"test"},
{"Concatenate test", "<div id='target'><span>one</span>two</div>",
u"onetwo"},
- // TODO(crbug.com/796918): should be "onetwo"
+ // Test that "two" is not inferred, because for the purpose of label
+ // extraction, we only care about text before the input element.
{"Ignore input", "<div id='target'>one<input value='test'/>two</div>",
u"one"},
{"Trim", "<div id='target'> one<span>two </span></div>", u"onetwo"},
- {"eleven children", kElevenChildren, kElevenChildrenExpected},
- // TODO(crbug.com/796918): Depth is only 5 elements
- {"eleven children nested", kElevenChildrenNested,
- kElevenChildrenNestedExpected},
+ {"eleven children",
+ "<div id='target'>"
+ "<div>child0</div>"
+ "<div>child1</div>"
+ "<div>child2</div>"
+ "<div>child3</div>"
+ "<div>child4</div>"
+ "<div>child5</div>"
+ "<div>child6</div>"
+ "<div>child7</div>"
+ "<div>child8</div>"
+ "<div>child9</div>"
+ "<div>child10</div>",
+ u"child0child1child2child3child4child5child6child7child8"},
+ // TODO(crbug.com/796918): Depth is only 5 elements instead of 10. This
+ // happens because every div and every text node decrease the depth.
+ {"eleven children nested",
+ "<div id='target'>"
+ "<div>child0"
+ "<div>child1"
+ "<div>child2"
+ "<div>child3"
+ "<div>child4"
+ "<div>child5"
+ "<div>child6"
+ "<div>child7"
+ "<div>child8"
+ "<div>child9"
+ "<div>child10"
+ "</div></div></div></div></div></div></div></div></div></div></div></"
+ "div>",
+ u"child0child1child2child3child4"},
};
for (auto test_case : test_cases) {
SCOPED_TRACE(test_case.description);
@@ -205,7 +140,14 @@ TEST_F(FormAutofillUtilsTest, FindChildTextTest) {
TEST_F(FormAutofillUtilsTest, FindChildTextSkipElementTest) {
static const AutofillFieldUtilCase test_cases[] = {
- {"Skip div element", kSkipElement, kSkipElementExpected},
+ // Test that everything after the "skip" div is discarded.
+ {"Skip div element", R"(
+ <div id=target>
+ <div>child0</div>
+ <div class=skip>child1</div>
+ <div>child2</div>
+ </div>)",
+ u"child0"},
};
for (auto test_case : test_cases) {
SCOPED_TRACE(test_case.description);
@@ -227,12 +169,48 @@ TEST_F(FormAutofillUtilsTest, FindChildTextSkipElementTest) {
TEST_F(FormAutofillUtilsTest, InferLabelForElementTest) {
static const AutofillFieldUtilCase test_cases[] = {
- {"DIV table test 1", kDivTableExample1, kDivTableExample1Expected},
- {"DIV table test 2", kDivTableExample2, kDivTableExample2Expected},
- {"DIV table test 3", kDivTableExample3, kDivTableExample3Expected},
- {"DIV table test 4", kDivTableExample4, kDivTableExample4Expected},
- {"DIV table test 5", kDivTableExample5, kDivTableExample5Expected},
- {"DIV table test 6", kDivTableExample6, kDivTableExample6Expected},
+ {"DIV table test 1", R"(
+ <div>
+ <div>label</div><div><input id=target></div>
+ </div>)",
+ u"label"},
+ {"DIV table test 2", R"(
+ <div>
+ <div>label</div>
+ <div>should be skipped<input></div>
+ <div><input id=target></div>
+ </div>)",
+ u"label"},
+ {"DIV table test 3", R"(
+ <div>
+ <div>should be skipped<input></div>
+ <div>label</div>
+ <div><input id=target></div>
+ </div>)",
+ u"label"},
+ // TODO(crbug.com/796918): Should be label
+ {"DIV table test 4", R"(
+ <div>
+ <div>should be skipped<input></div>
+ label
+ <div><input id=target></div>
+ </div>)",
+ u""},
+ // TODO(crbug.com/796918): Should be label
+ {"DIV table test 5",
+ "<div>"
+ "<div>label<div><input id='target'/></div>behind</div>"
+ "</div>",
+ u"labelbehind"},
+ {"DIV table test 6", R"(
+ <div>
+ label
+ <div>-</div>
+ <div><input id='target'></div>
+ </div>)",
+ // TODO(crbug.com/796918): Should be "label" or "label-". This happens
+ // because "-" is inferred, but discarded because `!IsLabelValid()`.
+ u""},
};
for (auto test_case : test_cases) {
SCOPED_TRACE(test_case.description);
@@ -245,12 +223,16 @@ TEST_F(FormAutofillUtilsTest, InferLabelForElementTest) {
FormFieldData::LabelSource label_source =
FormFieldData::LabelSource::kUnknown;
std::u16string label;
- InferLabelForElementForTesting(form_target, &label, &label_source);
+ InferLabelForElementForTesting(form_target, label, label_source);
EXPECT_EQ(test_case.expected_label, label);
}
}
TEST_F(FormAutofillUtilsTest, InferLabelSourceTest) {
+ struct AutofillFieldLabelSourceCase {
+ const char* html;
+ const FormFieldData::LabelSource label_source;
+ };
const char16_t kLabelSourceExpectedLabel[] = u"label";
static const AutofillFieldLabelSourceCase test_cases[] = {
{"<div><div>label</div><div><input id='target'/></div></div>",
@@ -287,7 +269,7 @@ TEST_F(FormAutofillUtilsTest, InferLabelSourceTest) {
FormFieldData::LabelSource::kUnknown;
std::u16string label;
EXPECT_TRUE(autofill::form_util::InferLabelForElementForTesting(
- form_target, &label, &label_source));
+ form_target, label, label_source));
EXPECT_EQ(kLabelSourceExpectedLabel, label);
EXPECT_EQ(test_case.label_source, label_source);
}
@@ -520,8 +502,8 @@ TEST_F(FormAutofillUtilsTest, IsFocusable) {
control_elements.push_back(
GetFormControlElementById(web_frame->GetDocument(), "name2"));
- EXPECT_TRUE(autofill::form_util::IsWebElementVisible(control_elements[0]));
- EXPECT_FALSE(autofill::form_util::IsWebElementVisible(control_elements[1]));
+ EXPECT_TRUE(autofill::form_util::IsWebElementFocusable(control_elements[0]));
+ EXPECT_FALSE(autofill::form_util::IsWebElementFocusable(control_elements[1]));
std::vector<WebElement> iframe_elements;
@@ -549,26 +531,6 @@ TEST_F(FormAutofillUtilsTest, FindFormByUniqueId) {
EXPECT_TRUE(FindFormByUniqueRendererId(doc, non_existing_id).IsNull());
}
-// Tests FindFormControlElementByUniqueRendererId().
-// TODO(crbug/1201875): Delete once
-// `features::kAutofillUseUnassociatedListedElements` is enabled.
-TEST_F(FormAutofillUtilsTest,
- FindFormControlElementByUniqueRendererId_WithoutFeature) {
- LoadHTML(
- "<body><form id='form1'><input id='i1'></form><input id='i2'></body>");
- WebDocument doc = GetMainFrame()->GetDocument();
- auto input1 = GetFormControlElementById(doc, "i1");
- auto input2 = GetFormControlElementById(doc, "i2");
- FieldRendererId non_existing_id(input2.UniqueRendererFormControlId() + 1000);
-
- EXPECT_EQ(input1, FindFormControlElementByUniqueRendererId(
- doc, GetFieldRendererId(input1)));
- EXPECT_EQ(input2, FindFormControlElementByUniqueRendererId(
- doc, GetFieldRendererId(input2)));
- EXPECT_TRUE(
- FindFormControlElementByUniqueRendererId(doc, non_existing_id).IsNull());
-}
-
// Used in ParameterizedFindFormControlByRendererIdTest.
struct FindFormControlTestParam {
std::string queried_field;
@@ -576,20 +538,10 @@ struct FindFormControlTestParam {
bool expectation;
};
-// Tests FindFormControlElementByUniqueRendererId() with
-// `features::kAutofillUseUnassociatedListedElements` enabled.
+// Tests FindFormControlElementByUniqueRendererId().
class ParameterizedFindFormControlByRendererIdTest
: public FormAutofillUtilsTest,
- public testing::WithParamInterface<FindFormControlTestParam> {
- public:
- ParameterizedFindFormControlByRendererIdTest() {
- scoped_features_.InitAndEnableFeature(
- features::kAutofillUseUnassociatedListedElements);
- }
-
- private:
- base::test::ScopedFeatureList scoped_features_;
-};
+ public testing::WithParamInterface<FindFormControlTestParam> {};
TEST_P(ParameterizedFindFormControlByRendererIdTest,
FindFormControlElementByUniqueRendererId) {
@@ -985,6 +937,9 @@ TEST_F(FormAutofillUtilsTest, NotExtractDataList) {
}
// Tests the visibility detection of iframes.
+// This test checks many scenarios. It's intentionally not a parameterized test
+// for performance reasons.
+// This test is very similar to the IsWebElementVisibleTest test.
TEST_F(FormAutofillUtilsTest, IsVisibleIframeTest) {
// Test cases of <iframe> elements with different styles.
//
@@ -998,10 +953,10 @@ TEST_F(FormAutofillUtilsTest, IsVisibleIframeTest) {
// IsVisibleIframe() but invisible to the human).
//
// The `data-false="{POSITIVE,NEGATIVE}"` attribute indicates whether the test
- // case to be a false positive/negative compared to human visibility
- // perception. In such a case, not meeting the expectation actually indicates
- // an improvement of IsVisibleIframe(), as it means a false positive/negative
- // has been fixed.
+ // case is a false positive/negative compared to human visibility perception.
+ // In such a case, not meeting the expectation actually indicates an
+ // improvement of IsVisibleIframe(), as it means a false positive/negative has
+ // been fixed.
//
// The sole purpose of the `data-false` attribute is to document this and to
// print a message when such a test fails.
@@ -1035,7 +990,7 @@ TEST_F(FormAutofillUtilsTest, IsVisibleIframeTest) {
<iframe srcdoc="<input>" data-visible style="width: 100px; height: 100px; position: absolute; right: -200px;" data-false="POSITIVE"></iframe>
<iframe srcdoc="<input>" data-visible style="width: 100px; height: 100px; position: absolute; bottom: -200px;" data-false="POSITIVE"></iframe>
- <iframe srcdoc="<input>" data-visible style=""></iframe> <!-- Finish with a visible frame to make sure all <iframe>s have been closed -->
+ <iframe srcdoc="<input>" data-visible style=""></iframe> <!-- Finish with a visible frame to make sure all <iframe> tags have been closed -->
<div style="width: 10000; height: 10000"></div>
</body>)");
@@ -1053,7 +1008,7 @@ TEST_F(FormAutofillUtilsTest, IsVisibleIframeTest) {
}
return result;
}();
- ASSERT_GE(iframes.size(), 16u);
+ ASSERT_GE(iframes.size(), 23u);
auto RunTestCases = [](const std::vector<WebElement>& iframes) {
for (WebElement iframe : iframes) {
@@ -1086,6 +1041,125 @@ TEST_F(FormAutofillUtilsTest, IsVisibleIframeTest) {
}
}
+// Tests the visibility detection of iframes.
+// This test checks many scenarios. It's intentionally not a parameterized test
+// for performance reasons.
+// This test is very similar to the IsVisibleIframeTest test.
+TEST_F(FormAutofillUtilsTest, IsWebElementVisibleTest) {
+ // Test cases of <input> elements with different types and styles.
+ //
+ // The `data-[in]visible` attribute represents whether IsWebElementVisible()
+ // is expected to classify the input as [in]visible.
+ //
+ // Since IsWebElementVisible() falls short of what the human user will
+ // consider visible or invisible, there are false positives and false
+ // negatives. For example, IsWebElementVisible() does not check opacity, so
+ // <input style="opacity: 0.0"> is a false positive (it's visible to
+ // IsWebElementVisible() but invisible to the human).
+ //
+ // The `data-false="{POSITIVE,NEGATIVE}"` attribute indicates whether the test
+ // case is a false positive/negative compared to human visibility perception.
+ // In such a case, not meeting the expectation actually indicates an
+ // improvement of IsWebElementVisible(), as it means a false positive/negative
+ // has been fixed.
+ //
+ // The sole purpose of the `data-false` attribute is to document this and to
+ // print a message when such a test fails.
+ LoadHTML(R"(
+ <body>
+ <input type="text" data-visible style="">
+ <input type="text" data-visible style="display: block;">
+ <input type="text" data-visible style="visibility: visible;">
+
+ <input type="text" data-invisible style="display: none;">
+ <input type="text" data-invisible style="visibility: hidden;">
+ <div style="display: none;"> <input type="text" data-invisible></div>
+ <div style="visibility: hidden;"><input type="text" data-invisible></div>
+
+ <input type="text" data-visible style="width: 15px; height: 15px;">
+ <input type="text" data-invisible style="width: 15px; height: 5px;">
+ <input type="text" data-invisible style="width: 5px; height: 15px;">
+ <input type="text" data-invisible style="width: 5px; height: 5px;">
+
+ <input type="text" data-invisible style="width: 1px; height: 1px;">
+ <input type="text" data-invisible style="width: 1px; height: 1px; overflow: visible;" data-false="NEGATIVE">
+
+ <input type="text" data-visible style="opacity: 0.0;" data-false="POSITIVE">
+ <input type="text" data-visible style="opacity: 0.0;" data-false="POSITIVE">
+ <input type="text" data-visible style="position: absolute; clip: rect(0,0,0,0);" data-false="POSITIVE">
+
+ <input type="text" data-visible style="width: 100px; position: absolute; left: -75px;">
+ <input type="text" data-visible style="width: 100px; position: absolute; top: -75px;">
+ <input type="text" data-visible style="width: 100px; position: absolute; left: -200px;" data-false="POSITIVE">
+ <input type="text" data-visible style="width: 100px; position: absolute; top: -200px;" data-false="POSITIVE">
+ <input type="text" data-visible style="width: 100px; position: absolute; right: -200px;" data-false="POSITIVE">
+ <input type="text" data-visible style="width: 100px; position: absolute; bottom: -200px;" data-false="POSITIVE">
+
+ <input type="checkbox" data-visible style="">
+ <input type="checkbox" data-invisible style="display: none;">
+ <input type="checkbox" data-invisible style="visibility: hidden;">
+ <input type="checkbox" data-visible style="width: 15px; height: 15px;">
+ <input type="checkbox" data-visible style="width: 15px; height: 5px;">
+ <input type="checkbox" data-visible style="width: 5px; height: 15px;">
+ <input type="checkbox" data-visible style="width: 5px; height: 5px;">
+
+ <input type="radio" data-visible style="">
+ <input type="radio" data-invisible style="display: none;">
+ <input type="radio" data-invisible style="visibility: hidden;">
+ <input type="radio" data-visible style="width: 15px; height: 15px;">
+ <input type="radio" data-visible style="width: 15px; height: 5px;">
+ <input type="radio" data-visible style="width: 5px; height: 15px;">
+ <input type="radio" data-visible style="width: 5px; height: 5px;">
+
+ <div style="width: 10000; height: 10000"></div>
+ </body>)");
+
+ // Ensure that Android runs at default page scale.
+ web_view_->SetPageScaleFactor(1.0);
+
+ std::vector<WebElement> inputs = [this] {
+ WebDocument doc = GetMainFrame()->GetDocument();
+ std::vector<WebElement> result;
+ WebElementCollection inputs = doc.GetElementsByHTMLTagName("input");
+ for (WebElement input = inputs.FirstItem(); !input.IsNull();
+ input = inputs.NextItem()) {
+ result.push_back(input);
+ }
+ return result;
+ }();
+ ASSERT_GE(inputs.size(), 36u);
+
+ auto RunTestCases = [](const std::vector<WebElement>& inputs) {
+ for (WebElement input : inputs) {
+ gfx::Rect bounds = input.BoundsInViewport();
+ bool expectation = input.HasAttribute("data-visible");
+ SCOPED_TRACE(
+ testing::Message()
+ << "Iframe with style \n " << input.GetAttribute("style").Ascii()
+ << "\nwith dimensions w=" << bounds.width()
+ << ",h=" << bounds.height() << " and position x=" << bounds.x()
+ << ",y=" << bounds.y()
+ << (input.HasAttribute("data-false") ? "\nwhich used to be a FALSE "
+ : "")
+ << input.GetAttribute("data-false").Ascii());
+ ASSERT_TRUE(input.HasAttribute("data-visible") !=
+ input.HasAttribute("data-invisible"));
+ EXPECT_EQ(IsWebElementVisible(input), expectation);
+ }
+ };
+
+ RunTestCases(inputs);
+
+ {
+ ExecuteJavaScriptForTests(
+ "window.scrollTo(document.body.scrollWidth,document.body.scrollHeight)"
+ ";");
+ content::RunAllTasksUntilIdle();
+ SCOPED_TRACE(testing::Message() << "Scrolled to bottom right");
+ RunTestCases(inputs);
+ }
+}
+
// Tests `GetClosestAncestorFormElement(element)`.
TEST_F(FormAutofillUtilsTest, GetClosestAncestorFormElement) {
LoadHTML(R"(
@@ -1215,29 +1289,10 @@ class FieldFramesTest
~FieldFramesTest() override = default;
};
-// Test getting the unowned form control elements from the WebDocument with the
-// old or the new version of GetUnownedFormFieldElements().
-// TODO(crbug.com/1201875): Remove when the
-// kAutofillUseUnassociatedListedElements feature is deleted.
-class FormAutofillUtilsTestUnownedFormFields
- : public FormAutofillUtilsTest,
- public testing::WithParamInterface<bool> {
- public:
- FormAutofillUtilsTestUnownedFormFields() {
- bool use_new_get_unowned_form_field_elements = GetParam();
- scoped_features_.InitWithFeatureState(
- features::kAutofillUseUnassociatedListedElements,
- use_new_get_unowned_form_field_elements);
- }
-
- private:
- base::test::ScopedFeatureList scoped_features_;
-};
-
// Check if the unowned form control elements are properly extracted.
// Form control elements are button, fieldset, input, textarea, output and
// select elements.
-TEST_P(FormAutofillUtilsTestUnownedFormFields, GetUnownedFormFieldElements) {
+TEST_F(FormAutofillUtilsTest, GetUnownedFormFieldElements) {
LoadHTML(R"(
<button id='unowned_button'>Unowned button</button>
<fieldset id='unowned_fieldset'>
@@ -1284,10 +1339,6 @@ TEST_P(FormAutofillUtilsTestUnownedFormFields, GetUnownedFormFieldElements) {
ElementsAre(GetFormControlElementById(doc, "unowned_fieldset")));
}
-INSTANTIATE_TEST_SUITE_P(FormAutofillUtilsTest,
- FormAutofillUtilsTestUnownedFormFields,
- testing::Bool());
-
// Tests that FormData::fields and FormData::child_frames are extracted fully
// and in the correct relative order.
TEST_P(FieldFramesTest, ExtractFieldsAndFrames) {
diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc
index 1126220117a..be8cf7d64e2 100644
--- a/chromium/components/autofill/content/renderer/form_cache.cc
+++ b/chromium/components/autofill/content/renderer/form_cache.cc
@@ -110,16 +110,8 @@ FormCache::UpdateFormCacheResult::~UpdateFormCacheResult() = default;
FormCache::FormCache(WebLocalFrame* frame) : frame_(frame) {}
FormCache::~FormCache() = default;
-void FormCache::MaybeUpdateParsedFormsPeak() {
- peak_size_of_parsed_forms_ = std::max(
- peak_size_of_parsed_forms_,
- std::max(parsed_forms_by_renderer_id_.size(), parsed_forms_.size()));
-}
-
FormCache::UpdateFormCacheResult FormCache::UpdateFormCache(
const FieldDataManager* field_data_manager) {
- DCHECK(base::FeatureList::IsEnabled(features::kAutofillDisplaceRemovedForms));
-
initial_checked_state_.clear();
initial_select_values_.clear();
@@ -127,12 +119,12 @@ FormCache::UpdateFormCacheResult FormCache::UpdateFormCache(
// Log an error message for deprecated attributes, but only the first time
// the form is parsed.
- bool log_deprecation_messages = parsed_forms_by_renderer_id_.empty();
+ bool log_deprecation_messages = parsed_forms_.empty();
- // |parsed_forms_by_renderer_id_| is re-populated below in ProcessForm().
+ // |parsed_forms_| is re-populated below in ProcessForm().
std::map<FormRendererId, FormData> old_parsed_forms =
- std::move(parsed_forms_by_renderer_id_);
- parsed_forms_by_renderer_id_.clear();
+ std::move(parsed_forms_);
+ parsed_forms_.clear();
UpdateFormCacheResult r;
r.removed_forms = base::MakeFlatSet<FormRendererId>(
@@ -170,8 +162,7 @@ FormCache::UpdateFormCacheResult FormCache::UpdateFormCache(
// Store only forms that contain iframes or fields.
if (IsFormInteresting(form, num_editable_elements)) {
FormRendererId form_id = form.unique_renderer_id;
- DCHECK(parsed_forms_by_renderer_id_.find(form_id) ==
- parsed_forms_by_renderer_id_.end());
+ DCHECK(parsed_forms_.find(form_id) == parsed_forms_.end());
auto it = old_parsed_forms.find(form_id);
if (it == old_parsed_forms.end() ||
!FormData::DeepEqual(std::move(it->second), form)) {
@@ -179,7 +170,7 @@ FormCache::UpdateFormCacheResult FormCache::UpdateFormCache(
r.updated_forms.push_back(form);
}
r.removed_forms.erase(form_id);
- parsed_forms_by_renderer_id_[form_id] = std::move(form);
+ parsed_forms_[form_id] = std::move(form);
}
return true;
};
@@ -189,10 +180,8 @@ FormCache::UpdateFormCacheResult FormCache::UpdateFormCache(
form_util::EXTRACT_OPTIONS);
WebDocument document = frame_->GetDocument();
- if (document.IsNull()) {
- MaybeUpdateParsedFormsPeak();
+ if (document.IsNull())
return r;
- }
for (const WebFormElement& form_element : document.Forms()) {
FormData form;
@@ -205,7 +194,6 @@ FormCache::UpdateFormCacheResult FormCache::UpdateFormCache(
std::move(form),
form_util::ExtractAutofillableElementsInForm(form_element))) {
PruneInitialValueCaches(observed_unique_renderer_ids);
- MaybeUpdateParsedFormsPeak();
return r;
}
}
@@ -223,173 +211,27 @@ FormCache::UpdateFormCacheResult FormCache::UpdateFormCache(
fieldsets, control_elements, iframe_elements, nullptr, document,
field_data_manager, extract_mask, &synthetic_form, nullptr)) {
PruneInitialValueCaches(observed_unique_renderer_ids);
- MaybeUpdateParsedFormsPeak();
return r;
}
if (!ProcessForm(std::move(synthetic_form), control_elements)) {
PruneInitialValueCaches(observed_unique_renderer_ids);
- MaybeUpdateParsedFormsPeak();
- return r;
- }
-
- PruneInitialValueCaches(observed_unique_renderer_ids);
- MaybeUpdateParsedFormsPeak();
- return r;
-}
-
-FormCache::UpdateFormCacheResult FormCache::ExtractNewForms(
- const FieldDataManager* field_data_manager) {
- if (base::FeatureList::IsEnabled(features::kAutofillDisplaceRemovedForms)) {
- return UpdateFormCache(field_data_manager);
- }
-
- UpdateFormCacheResult r;
- r.removed_forms = base::MakeFlatSet<FormRendererId>(
- parsed_forms_, {}, &FormData::unique_renderer_id);
-
- WebDocument document = frame_->GetDocument();
- if (document.IsNull()) {
- MaybeUpdateParsedFormsPeak();
- return r;
- }
-
- initial_checked_state_.clear();
- initial_select_values_.clear();
-
- std::set<FieldRendererId> observed_unique_renderer_ids;
-
- // Log an error message for deprecated attributes, but only the first time
- // the form is parsed.
- bool log_deprecation_messages = parsed_forms_.empty();
-
- const form_util::ExtractMask extract_mask =
- static_cast<form_util::ExtractMask>(form_util::EXTRACT_VALUE |
- form_util::EXTRACT_OPTIONS);
-
- size_t num_fields_seen = 0;
- size_t num_frames_seen = 0;
- for (const WebFormElement& form_element : document.Forms()) {
- std::vector<WebFormControlElement> control_elements =
- form_util::ExtractAutofillableElementsInForm(form_element);
-
- FormData form;
- if (!WebFormElementToFormData(form_element, WebFormControlElement(),
- field_data_manager, extract_mask, &form,
- nullptr)) {
- continue;
- }
-
- for (const auto& field : form.fields)
- observed_unique_renderer_ids.insert(field.unique_renderer_id);
-
- num_fields_seen += form.fields.size();
- num_frames_seen += form.child_frames.size();
-
- if (num_fields_seen > kMaxParseableFields) {
- PruneInitialValueCaches(observed_unique_renderer_ids);
- MaybeUpdateParsedFormsPeak();
- return r;
- }
-
- if (num_frames_seen > kMaxParseableChildFrames)
- form.child_frames.clear();
-
- size_t num_editable_elements =
- ScanFormControlElements(control_elements, log_deprecation_messages);
-
- if (!IsFormInteresting(form, num_editable_elements))
- continue;
-
- // The form is in the DOM and is interesting, so has not been removed.
- r.removed_forms.erase(form.unique_renderer_id);
-
- if (!base::Contains(parsed_forms_, form)) {
- for (auto it = parsed_forms_.begin(); it != parsed_forms_.end(); ++it) {
- if (it->SameFormAs(form)) {
- parsed_forms_.erase(it);
- break;
- }
- }
-
- SaveInitialValues(control_elements);
- r.updated_forms.push_back(form);
- parsed_forms_.insert(form);
- }
- }
-
- // Look for more parseable fields outside of forms.
- std::vector<WebElement> fieldsets;
- std::vector<WebFormControlElement> control_elements =
- form_util::GetUnownedAutofillableFormFieldElements(document, &fieldsets);
- std::vector<WebElement> iframe_elements =
- form_util::GetUnownedIframeElements(document);
-
- FormData synthetic_form;
- if (!UnownedFormElementsAndFieldSetsToFormData(
- fieldsets, control_elements, iframe_elements, nullptr, document,
- field_data_manager, extract_mask, &synthetic_form, nullptr)) {
- PruneInitialValueCaches(observed_unique_renderer_ids);
- MaybeUpdateParsedFormsPeak();
- return r;
- }
-
- for (const auto& field : synthetic_form.fields)
- observed_unique_renderer_ids.insert(field.unique_renderer_id);
-
- num_fields_seen += synthetic_form.fields.size();
- num_frames_seen += synthetic_form.child_frames.size();
- if (num_fields_seen > kMaxParseableFields) {
- PruneInitialValueCaches(observed_unique_renderer_ids);
- MaybeUpdateParsedFormsPeak();
return r;
}
- if (num_frames_seen > kMaxParseableChildFrames)
- synthetic_form.child_frames.clear();
-
- size_t num_editable_elements =
- ScanFormControlElements(control_elements, log_deprecation_messages);
-
- if (!IsFormInteresting(synthetic_form, num_editable_elements)) {
- PruneInitialValueCaches(observed_unique_renderer_ids);
- MaybeUpdateParsedFormsPeak();
- return r;
- }
-
- // The form is in the DOM and is interesting, so has not been removed.
- r.removed_forms.erase(synthetic_form.unique_renderer_id);
-
- if (!base::Contains(parsed_forms_, synthetic_form)) {
- SaveInitialValues(control_elements);
- r.updated_forms.push_back(synthetic_form);
- parsed_forms_.insert(synthetic_form);
- parsed_forms_.erase(synthetic_form_);
- synthetic_form_ = synthetic_form;
- }
-
PruneInitialValueCaches(observed_unique_renderer_ids);
- MaybeUpdateParsedFormsPeak();
return r;
}
void FormCache::Reset() {
- // Record the size of the cached parsed forms every time it reaches its peak
- // size. The peak size is reached right before the cache is cleared.
- UMA_HISTOGRAM_COUNTS_1000("Autofill.FormCacheSize",
- peak_size_of_parsed_forms_);
-
synthetic_form_ = FormData();
parsed_forms_.clear();
- // TODO(crbug/1215333): Remove after the `AutofillUseNewFormExtraction`
- // feature is deleted.
- parsed_forms_by_renderer_id_.clear();
initial_select_values_.clear();
initial_checked_state_.clear();
fields_eligible_for_manual_filling_.clear();
}
void FormCache::ClearElement(WebFormControlElement& control_element,
- const WebFormControlElement& element) {
+ const WebFormControlElement& trigger_element) {
// Don't modify the value of disabled fields.
if (!control_element.IsEnabled())
return;
@@ -398,25 +240,30 @@ void FormCache::ClearElement(WebFormControlElement& control_element,
if (!control_element.IsAutofilled())
return;
- if (control_element.AutofillSection() != element.AutofillSection())
+ if (control_element.AutofillSection() != trigger_element.AutofillSection())
return;
- control_element.SetAutofillState(WebAutofillState::kNotFilled);
+ if (!form_util::IsAutofillableElement(control_element)) {
+ NOTREACHED();
+ return;
+ }
WebInputElement web_input_element =
control_element.DynamicTo<WebInputElement>();
if (form_util::IsTextInput(web_input_element) ||
form_util::IsMonthInput(web_input_element)) {
- web_input_element.SetAutofillValue(blink::WebString());
+ web_input_element.SetAutofillValue(blink::WebString(),
+ WebAutofillState::kNotFilled);
// Clearing the value in the focused node (above) can cause the selection
// to be lost. We force the selection range to restore the text cursor.
- if (element == web_input_element) {
+ if (trigger_element == web_input_element) {
size_t length = web_input_element.Value().length();
web_input_element.SetSelectionRange(length, length);
}
} else if (form_util::IsTextAreaElement(control_element)) {
- control_element.SetAutofillValue(blink::WebString());
+ control_element.SetAutofillValue(blink::WebString(),
+ WebAutofillState::kNotFilled);
} else if (form_util::IsSelectElement(control_element)) {
WebSelectElement select_element = control_element.To<WebSelectElement>();
auto initial_value_iter = initial_select_values_.find(
@@ -424,18 +271,23 @@ void FormCache::ClearElement(WebFormControlElement& control_element,
if (initial_value_iter != initial_select_values_.end() &&
select_element.Value().Utf16() != initial_value_iter->second) {
select_element.SetAutofillValue(
- blink::WebString::FromUTF16(initial_value_iter->second));
+ blink::WebString::FromUTF16(initial_value_iter->second),
+ blink::WebAutofillState::kNotFilled);
select_element.SetUserHasEditedTheField(false);
+ } else {
+ select_element.SetAutofillState(WebAutofillState::kNotFilled);
}
- } else {
+ } else if (form_util::IsCheckableElement(web_input_element)) {
WebInputElement input_element = control_element.To<WebInputElement>();
- DCHECK(form_util::IsCheckableElement(input_element));
auto checkable_element_it = initial_checked_state_.find(
FieldRendererId(input_element.UniqueRendererFormControlId()));
if (checkable_element_it != initial_checked_state_.end() &&
input_element.IsChecked() != checkable_element_it->second) {
- input_element.SetChecked(checkable_element_it->second, true);
+ input_element.SetChecked(checkable_element_it->second, true,
+ WebAutofillState::kNotFilled);
}
+ } else {
+ NOTREACHED();
}
}
diff --git a/chromium/components/autofill/content/renderer/form_cache.h b/chromium/components/autofill/content/renderer/form_cache.h
index 42bd9f68e05..8f8ffd7bc98 100644
--- a/chromium/components/autofill/content/renderer/form_cache.h
+++ b/chromium/components/autofill/content/renderer/form_cache.h
@@ -53,24 +53,6 @@ class FormCache {
~FormCache();
- // Scans the DOM in |frame_| extracting and storing forms that have not been
- // seen before. Returns the extracted forms.
- //
- // Note that modified forms are considered new forms.
- //
- // To reduce the computational cost, we limit the number of fields and frames
- // summed over all forms, in addition to the per-form limits in
- // form_util::FormOrFieldsetsToFormData():
- // - if the number of fields over all forms exceeds |kMaxParseableFields|,
- // only a subset of forms is returned which does not exceed the limit;
- // - if the number of frames over all forms exceeds kMaxParseableFrames, all
- // forms are returned but only a subset of them have non-empty
- // FormData::child_frames.
- // In either case, the subset is chosen so that the returned list of forms
- // does not exceed the limits of fields and frames.
- UpdateFormCacheResult ExtractNewForms(
- const FieldDataManager* field_data_manager);
-
// Returns the diff of forms since the last call to UpdateFormCache(): the new
// forms, the still present but changed forms, and the removed forms.
//
@@ -99,12 +81,7 @@ class FormCache {
// In either case, the subset is chosen so that the returned list of forms
// does not exceed the limits of fields and frames.
//
- // Updates |parsed_forms_by_renderer_id_| to contain the forms that are
- // currently in the DOM.
- //
- // TODO(crbug/1215333):/ Modified version of ExtractNewForms(). It is used
- // only if `AutofillUseNewFormExtraction` feature is enabled. Remove
- // ExtractNewForms() after the feature is deleted.
+ // Updates |parsed_forms_| to contain the forms that are currently in the DOM.
UpdateFormCacheResult UpdateFormCache(
const FieldDataManager* field_data_manager);
@@ -143,37 +120,22 @@ class FormCache {
const std::vector<blink::WebFormControlElement>& control_elements);
// Clears the value of the |control_element|.
+ // |trigger_element| is the element on which the user triggered a request
+ // to clear the form.
void ClearElement(blink::WebFormControlElement& control_element,
- const blink::WebFormControlElement& element);
+ const blink::WebFormControlElement& trigger_element);
// Clears all entries from |initial_select_values_| and
// |initial_checked_state_| whose keys not contained in |ids_to_retain|.
void PruneInitialValueCaches(const std::set<FieldRendererId>& ids_to_retain);
- // Update the peak size of the cached forms stored in
- // |peak_size_of_parsed_forms_|.
- // TODO(crbug/1215333): Remove after `Autofill.FormCacheSize` experiment is
- // completed.
- void MaybeUpdateParsedFormsPeak();
-
// The frame this FormCache is associated with. Weak reference.
blink::WebLocalFrame* frame_;
- // The cached forms. Used to prevent re-extraction of forms.
- // TODO(crbug/896689) Move to std::map<unique_rederer_id, FormData>.
- std::set<FormData, FormData::IdentityComparator> parsed_forms_;
-
// Same as |parsed_forms_|, but moved to a different type. It is used only if
// `AutofillUseNewFormExtraction` feature is enabled.
// TODO(crbug/1215333): Remove |parsed_forms_| after the feature is deleted.
- std::map<FormRendererId, FormData> parsed_forms_by_renderer_id_;
-
- // Stores the peak size of the cached forms for `Autofill.FormCacheSize`
- // metric. The cached forms are stored in |parsed_forms_| or
- // |parsed_forms_by_renderer_id_| depending on the
- // `AutofillUseNewFormExtraction` feature.
- // TODO(crbug/1215333): Remove after the experiment is completed.
- size_t peak_size_of_parsed_forms_ = 0;
+ std::map<FormRendererId, FormData> parsed_forms_;
// The synthetic FormData is for all the fieldsets in the document without a
// form owner.
diff --git a/chromium/components/autofill/content/renderer/form_cache_browsertest.cc b/chromium/components/autofill/content/renderer/form_cache_browsertest.cc
index 5a071b95b32..8e6aa080c37 100644
--- a/chromium/components/autofill/content/renderer/form_cache_browsertest.cc
+++ b/chromium/components/autofill/content/renderer/form_cache_browsertest.cc
@@ -78,24 +78,7 @@ class FormCacheBrowserTest : public content::RenderViewTest {
std::unique_ptr<test::FocusTestUtils> focus_test_utils_;
};
-class ParameterizedFormCacheBrowserTest
- : public FormCacheBrowserTest,
- public testing::WithParamInterface<bool> {
- public:
- ParameterizedFormCacheBrowserTest() {
- bool use_new_form_extraction = GetParam();
- std::vector<base::Feature> enabled;
- std::vector<base::Feature> disabled;
- (use_new_form_extraction ? &enabled : &disabled)
- ->push_back(features::kAutofillDisplaceRemovedForms);
- scoped_features_.InitWithFeatures(enabled, disabled);
- }
-
- private:
- base::test::ScopedFeatureList scoped_features_;
-};
-
-TEST_P(ParameterizedFormCacheBrowserTest, UpdatedForms) {
+TEST_F(FormCacheBrowserTest, UpdatedForms) {
LoadHTML(R"(
<form id="form1">
<input type="text" name="foo1">
@@ -106,7 +89,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, UpdatedForms) {
)");
FormCache form_cache(GetMainFrame());
- FormCache::UpdateFormCacheResult forms = form_cache.ExtractNewForms(nullptr);
+ FormCache::UpdateFormCacheResult forms = form_cache.UpdateFormCache(nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasId(FormRendererId()), HasName("form1")));
@@ -123,7 +106,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, UpdatedForms) {
EXPECT_TRUE(unowned_form->child_frames.empty());
}
-TEST_P(ParameterizedFormCacheBrowserTest, RemovedForms) {
+TEST_F(FormCacheBrowserTest, RemovedForms) {
LoadHTML(R"(
<form id="form1">
<input type="text" name="foo1">
@@ -139,7 +122,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, RemovedForms) {
)");
FormCache form_cache(GetMainFrame());
- FormCache::UpdateFormCacheResult forms = form_cache.ExtractNewForms(nullptr);
+ FormCache::UpdateFormCacheResult forms = form_cache.UpdateFormCache(nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasId(FormRendererId()), HasName("form1"),
@@ -151,7 +134,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, RemovedForms) {
document.getElementById("form2").innerHTML = "";
)");
- forms = form_cache.ExtractNewForms(nullptr);
+ forms = form_cache.UpdateFormCache(nullptr);
EXPECT_TRUE(forms.updated_forms.empty());
EXPECT_THAT(forms.removed_forms,
@@ -161,16 +144,10 @@ TEST_P(ParameterizedFormCacheBrowserTest, RemovedForms) {
document.getElementById("unowned_element").remove();
)");
- forms = form_cache.ExtractNewForms(nullptr);
+ forms = form_cache.UpdateFormCache(nullptr);
EXPECT_TRUE(forms.updated_forms.empty());
- if (base::FeatureList::IsEnabled(features::kAutofillDisplaceRemovedForms)) {
- EXPECT_THAT(forms.removed_forms, ElementsAre(FormRendererId()));
- } else {
- EXPECT_THAT(
- forms.removed_forms,
- ElementsAre(FormRendererId(), FormRendererId(1), FormRendererId(2)));
- }
+ EXPECT_THAT(forms.removed_forms, ElementsAre(FormRendererId()));
ExecuteJavaScriptForTests(R"(
document.getElementById("form2").innerHTML = `
@@ -180,18 +157,10 @@ TEST_P(ParameterizedFormCacheBrowserTest, RemovedForms) {
`;
)");
- forms = form_cache.ExtractNewForms(nullptr);
-
- if (base::FeatureList::IsEnabled(features::kAutofillDisplaceRemovedForms)) {
- EXPECT_THAT(forms.updated_forms, ElementsAre(HasName("form2")));
- EXPECT_TRUE(forms.removed_forms.empty());
- } else {
- // Unfortunately, this does not contain FormRendererId(2) because
- // ExtractNewForms() does not remove its old forms.
- EXPECT_TRUE(forms.updated_forms.empty());
- EXPECT_THAT(forms.removed_forms,
- ElementsAre(FormRendererId(), FormRendererId(1)));
- }
+ forms = form_cache.UpdateFormCache(nullptr);
+
+ EXPECT_THAT(forms.updated_forms, ElementsAre(HasName("form2")));
+ EXPECT_TRUE(forms.removed_forms.empty());
ExecuteJavaScriptForTests(R"(
document.getElementById("form2").innerHTML = `
@@ -202,19 +171,14 @@ TEST_P(ParameterizedFormCacheBrowserTest, RemovedForms) {
`;
)");
- forms = form_cache.ExtractNewForms(nullptr);
+ forms = form_cache.UpdateFormCache(nullptr);
EXPECT_THAT(forms.updated_forms, ElementsAre(HasName("form2")));
- if (base::FeatureList::IsEnabled(features::kAutofillDisplaceRemovedForms)) {
- EXPECT_TRUE(forms.removed_forms.empty());
- } else {
- EXPECT_THAT(forms.removed_forms,
- ElementsAre(FormRendererId(), FormRendererId(1)));
- }
+ EXPECT_TRUE(forms.removed_forms.empty());
}
// Test if the form gets re-extracted after a label change.
-TEST_P(ParameterizedFormCacheBrowserTest, ExtractFormAfterDynamicFieldChange) {
+TEST_F(FormCacheBrowserTest, ExtractFormAfterDynamicFieldChange) {
LoadHTML(R"(
<form id="f"><input></form>
<form id="g"> <label id="label">Name</label><input></form>
@@ -222,7 +186,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, ExtractFormAfterDynamicFieldChange) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasName("f"), HasName("g")));
EXPECT_TRUE(forms.removed_forms.empty());
@@ -231,12 +195,12 @@ TEST_P(ParameterizedFormCacheBrowserTest, ExtractFormAfterDynamicFieldChange) {
document.getElementById("label").innerHTML = "Last Name";
)");
- forms = form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ forms = form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms, ElementsAre(HasName("g")));
EXPECT_TRUE(forms.removed_forms.empty());
}
-class FormCacheIframeBrowserTest : public ParameterizedFormCacheBrowserTest {
+class FormCacheIframeBrowserTest : public FormCacheBrowserTest {
public:
FormCacheIframeBrowserTest() {
scoped_feature_list_.InitAndEnableFeature(features::kAutofillAcrossIframes);
@@ -247,7 +211,7 @@ class FormCacheIframeBrowserTest : public ParameterizedFormCacheBrowserTest {
base::test::ScopedFeatureList scoped_feature_list_;
};
-TEST_P(FormCacheIframeBrowserTest, ExtractFrames) {
+TEST_F(FormCacheIframeBrowserTest, ExtractFrames) {
LoadHTML(R"(
<form id="form1">
<iframe id="frame1"></iframe>
@@ -262,7 +226,7 @@ TEST_P(FormCacheIframeBrowserTest, ExtractFrames) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasId(FormRendererId()), HasName("form1")));
@@ -280,7 +244,7 @@ TEST_P(FormCacheIframeBrowserTest, ExtractFrames) {
ElementsAre(AllOf(IsToken(frame2_token, -1))));
}
-TEST_P(ParameterizedFormCacheBrowserTest, ExtractFormsTwice) {
+TEST_F(FormCacheBrowserTest, ExtractFormsTwice) {
LoadHTML(R"(
<form id="form1">
<input type="text" name="foo1">
@@ -292,19 +256,19 @@ TEST_P(ParameterizedFormCacheBrowserTest, ExtractFormsTwice) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasId(FormRendererId()), HasName("form1")));
EXPECT_TRUE(forms.removed_forms.empty());
- forms = form_cache.ExtractNewForms(nullptr);
+ forms = form_cache.UpdateFormCache(nullptr);
// As nothing has changed, there are no new or removed forms.
EXPECT_TRUE(forms.updated_forms.empty());
EXPECT_TRUE(forms.removed_forms.empty());
}
-TEST_P(FormCacheIframeBrowserTest, ExtractFramesTwice) {
+TEST_F(FormCacheIframeBrowserTest, ExtractFramesTwice) {
LoadHTML(R"(
<form id="form1">
<iframe></iframe>
@@ -314,20 +278,20 @@ TEST_P(FormCacheIframeBrowserTest, ExtractFramesTwice) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasId(FormRendererId()), HasName("form1")));
EXPECT_TRUE(forms.removed_forms.empty());
- forms = form_cache.ExtractNewForms(nullptr);
+ forms = form_cache.UpdateFormCache(nullptr);
// As nothing has changed, there are no new or removed forms.
EXPECT_TRUE(forms.updated_forms.empty());
EXPECT_TRUE(forms.removed_forms.empty());
}
// TODO(crbug.com/1117028) Adjust expectations when we omit invisible iframes.
-TEST_P(FormCacheIframeBrowserTest, ExtractFramesAfterVisibilityChange) {
+TEST_F(FormCacheIframeBrowserTest, ExtractFramesAfterVisibilityChange) {
LoadHTML(R"(
<form id="form1">
<iframe id="frame1" style="display: none;"></iframe>
@@ -351,7 +315,7 @@ TEST_P(FormCacheIframeBrowserTest, ExtractFramesAfterVisibilityChange) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasId(FormRendererId()), HasName("form1")));
EXPECT_TRUE(forms.removed_forms.empty());
@@ -364,7 +328,7 @@ TEST_P(FormCacheIframeBrowserTest, ExtractFramesAfterVisibilityChange) {
ASSERT_GT(GetSize(iframe2), 0);
ASSERT_GT(GetSize(iframe3), 0);
- forms = form_cache.ExtractNewForms(nullptr);
+ forms = form_cache.UpdateFormCache(nullptr);
EXPECT_TRUE(forms.updated_forms.empty());
EXPECT_TRUE(forms.removed_forms.empty());
@@ -375,12 +339,12 @@ TEST_P(FormCacheIframeBrowserTest, ExtractFramesAfterVisibilityChange) {
ASSERT_LE(GetSize(iframe2), 0);
ASSERT_LE(GetSize(iframe3), 0);
- forms = form_cache.ExtractNewForms(nullptr);
+ forms = form_cache.UpdateFormCache(nullptr);
EXPECT_TRUE(forms.updated_forms.empty());
EXPECT_TRUE(forms.removed_forms.empty());
}
-TEST_P(ParameterizedFormCacheBrowserTest, ExtractFormsAfterModification) {
+TEST_F(FormCacheBrowserTest, ExtractFormsAfterModification) {
LoadHTML(R"(
<form id="form1">
<input type="text" name="foo1">
@@ -392,7 +356,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, ExtractFormsAfterModification) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasId(FormRendererId()), HasName("form1")));
EXPECT_TRUE(forms.removed_forms.empty());
@@ -412,7 +376,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, ExtractFormsAfterModification) {
document.body.appendChild(new_input_2);
)");
- forms = form_cache.ExtractNewForms(nullptr);
+ forms = form_cache.UpdateFormCache(nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasId(FormRendererId()), HasName("form1")));
EXPECT_TRUE(forms.removed_forms.empty());
@@ -426,7 +390,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, ExtractFormsAfterModification) {
EXPECT_EQ(2u, unowned_form->fields.size());
}
-TEST_P(ParameterizedFormCacheBrowserTest, FillAndClear) {
+TEST_F(FormCacheBrowserTest, FillAndClear) {
LoadHTML(R"(
<input type="text" name="text" id="text">
<input type="checkbox" checked name="checkbox" id="checkbox">
@@ -438,7 +402,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, FillAndClear) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms, ElementsAre(HasId(FormRendererId())));
EXPECT_TRUE(forms.removed_forms.empty());
@@ -475,7 +439,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, FillAndClear) {
// Tests that correct focus, change and blur events are emitted during the
// autofilling and clearing of the form with an initially focused element.
-TEST_P(ParameterizedFormCacheBrowserTest,
+TEST_F(FormCacheBrowserTest,
VerifyFocusAndBlurEventsAfterAutofillAndClearingWithFocusElement) {
// Load a form.
LoadHTML(
@@ -489,7 +453,7 @@ TEST_P(ParameterizedFormCacheBrowserTest,
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms,
UnorderedElementsAre(HasId(FormRendererId()), HasName("myForm")));
@@ -529,7 +493,7 @@ TEST_P(ParameterizedFormCacheBrowserTest,
EXPECT_EQ(GetFocusLog(), "c0b0f1c1b1f0c0b0f1c1b1f0");
}
-TEST_P(ParameterizedFormCacheBrowserTest, FreeDataOnElementRemoval) {
+TEST_F(FormCacheBrowserTest, FreeDataOnElementRemoval) {
LoadHTML(R"(
<div id="container">
<input type="text" name="text" id="text">
@@ -543,7 +507,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, FreeDataOnElementRemoval) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms, ElementsAre(HasId(FormRendererId())));
EXPECT_TRUE(forms.removed_forms.empty());
@@ -558,7 +522,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, FreeDataOnElementRemoval) {
}
)");
- forms = form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ forms = form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_TRUE(forms.updated_forms.empty());
EXPECT_THAT(forms.removed_forms, ElementsAre(FormRendererId()));
EXPECT_EQ(0u, FormCacheTestApi(&form_cache).initial_select_values_size());
@@ -567,8 +531,7 @@ TEST_P(ParameterizedFormCacheBrowserTest, FreeDataOnElementRemoval) {
// Test that the select element's user edited field state is set
// to false after clearing the form.
-TEST_P(ParameterizedFormCacheBrowserTest,
- ClearFormSelectElementEditedStateReset) {
+TEST_F(FormCacheBrowserTest, ClearFormSelectElementEditedStateReset) {
LoadHTML(R"(
<input type="text" name="text" id="text">
<select name="date" id="date">
@@ -585,7 +548,7 @@ TEST_P(ParameterizedFormCacheBrowserTest,
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms, ElementsAre(HasId(FormRendererId())));
EXPECT_TRUE(forms.removed_forms.empty());
@@ -639,8 +602,7 @@ TEST_P(ParameterizedFormCacheBrowserTest,
EXPECT_TRUE(select_month.UserHasEditedTheField());
}
-TEST_P(ParameterizedFormCacheBrowserTest,
- IsFormElementEligibleForManualFilling) {
+TEST_F(FormCacheBrowserTest, IsFormElementEligibleForManualFilling) {
// Load a form.
LoadHTML(
"<html><form id='myForm'>"
@@ -656,7 +618,7 @@ TEST_P(ParameterizedFormCacheBrowserTest,
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_THAT(forms.updated_forms, ElementsAre(HasName("myForm")));
EXPECT_TRUE(forms.removed_forms.empty());
@@ -683,31 +645,22 @@ TEST_P(ParameterizedFormCacheBrowserTest,
// Test that the FormCache does not contain empty forms.
TEST_F(FormCacheBrowserTest, DoNotStoreEmptyForms) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- features::kAutofillDisplaceRemovedForms);
-
LoadHTML(R"(<form></form>)");
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_TRUE(forms.updated_forms.empty());
EXPECT_TRUE(forms.removed_forms.empty());
EXPECT_EQ(1u, GetMainFrame()->GetDocument().Forms().size());
- EXPECT_EQ(0u,
- FormCacheTestApi(&form_cache).parsed_forms_by_renderer_id_size());
+ EXPECT_EQ(0u, FormCacheTestApi(&form_cache).parsed_forms_size());
}
// Test that the FormCache never contains more than |kMaxParseableFields|
// non-empty parsed forms.
TEST_F(FormCacheBrowserTest, FormCacheSizeUpperBound) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- features::kAutofillDisplaceRemovedForms);
-
// Create a HTML page that contains `kMaxParseableFields + 1` non-empty
// forms.
std::string html;
@@ -718,7 +671,7 @@ TEST_F(FormCacheBrowserTest, FormCacheSizeUpperBound) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_EQ(forms.updated_forms.size(), kMaxParseableFields);
EXPECT_TRUE(forms.removed_forms.empty());
@@ -726,12 +679,12 @@ TEST_F(FormCacheBrowserTest, FormCacheSizeUpperBound) {
EXPECT_EQ(kMaxParseableFields + 1,
GetMainFrame()->GetDocument().Forms().size());
EXPECT_EQ(kMaxParseableFields,
- FormCacheTestApi(&form_cache).parsed_forms_by_renderer_id_size());
+ FormCacheTestApi(&form_cache).parsed_forms_size());
}
-// Test that FormCache::ExtractNewForms() limits the number of total fields by
+// Test that FormCache::UpdateFormCache() limits the number of total fields by
// skipping any additional forms.
-TEST_P(ParameterizedFormCacheBrowserTest, FieldLimit) {
+TEST_F(FormCacheBrowserTest, FieldLimit) {
std::string html;
for (unsigned int i = 0; i < kMaxParseableFields + 1; i++)
html += "<form><input></form>";
@@ -742,15 +695,15 @@ TEST_P(ParameterizedFormCacheBrowserTest, FieldLimit) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_EQ(kMaxParseableFields, forms.updated_forms.size());
EXPECT_TRUE(forms.removed_forms.empty());
}
-// Test that FormCache::ExtractNewForms() limits the number of total frames by
+// Test that FormCache::UpdateFormCache() limits the number of total frames by
// clearing their frames and skipping the then-empty forms.
-TEST_P(FormCacheIframeBrowserTest, FrameLimit) {
+TEST_F(FormCacheIframeBrowserTest, FrameLimit) {
std::string html;
for (unsigned int i = 0; i < kMaxParseableChildFrames + 1; i++)
html += "<form><iframe></iframe></form>";
@@ -761,13 +714,13 @@ TEST_P(FormCacheIframeBrowserTest, FrameLimit) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_EQ(kMaxParseableChildFrames, forms.updated_forms.size());
EXPECT_TRUE(forms.removed_forms.empty());
}
-// Test that FormCache::ExtractNewForms() limits the number of total fields and
+// Test that FormCache::UpdateFormCache() limits the number of total fields and
// total frames:
// - the forms [0, kMaxParseableChildFrames) should be unchanged,
// - the forms [kMaxParseableChildFrames, kMaxParseableFields) should have
@@ -779,7 +732,7 @@ TEST_P(FormCacheIframeBrowserTest, FrameLimit) {
#else
#define MAYBE_FieldAndFrameLimit FieldAndFrameLimit
#endif
-TEST_P(FormCacheIframeBrowserTest, MAYBE_FieldAndFrameLimit) {
+TEST_F(FormCacheIframeBrowserTest, MAYBE_FieldAndFrameLimit) {
ASSERT_LE(kMaxParseableChildFrames, kMaxParseableFields);
std::string html;
@@ -792,7 +745,7 @@ TEST_P(FormCacheIframeBrowserTest, MAYBE_FieldAndFrameLimit) {
FormCache form_cache(GetMainFrame());
FormCache::UpdateFormCacheResult forms =
- form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+ form_cache.UpdateFormCache(/*field_data_manager=*/nullptr);
EXPECT_EQ(forms.updated_forms.size(), kMaxParseableFields);
EXPECT_TRUE(base::ranges::none_of(forms.updated_forms,
@@ -808,9 +761,4 @@ TEST_P(FormCacheIframeBrowserTest, MAYBE_FieldAndFrameLimit) {
EXPECT_TRUE(forms.removed_forms.empty());
}
-INSTANTIATE_TEST_SUITE_P(All,
- ParameterizedFormCacheBrowserTest,
- testing::Bool());
-INSTANTIATE_TEST_SUITE_P(All, FormCacheIframeBrowserTest, testing::Bool());
-
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/form_cache_test_api.h b/chromium/components/autofill/content/renderer/form_cache_test_api.h
index 9889921d019..c0d88adb6c0 100644
--- a/chromium/components/autofill/content/renderer/form_cache_test_api.h
+++ b/chromium/components/autofill/content/renderer/form_cache_test_api.h
@@ -39,16 +39,10 @@ class FormCacheTestApi {
size_t parsed_forms_size() { return form_cache_->parsed_forms_.size(); }
- // TODO(crbug/1215333): Remove once the `AutofillUseNewFormExtraction` feature
- // is launched.
- size_t parsed_forms_by_renderer_id_size() {
- return form_cache_->parsed_forms_by_renderer_id_.size();
- }
-
private:
FormCache* form_cache_;
};
} // namespace autofill
-#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_FORM_CACHE_TEST_API_H_ \ No newline at end of file
+#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_FORM_CACHE_TEST_API_H_
diff --git a/chromium/components/autofill/content/renderer/form_tracker.cc b/chromium/components/autofill/content/renderer/form_tracker.cc
index 57cb4669643..1cd65a47621 100644
--- a/chromium/components/autofill/content/renderer/form_tracker.cc
+++ b/chromium/components/autofill/content/renderer/form_tracker.cc
@@ -250,9 +250,9 @@ bool FormTracker::CanInferFormSubmitted() {
// user has interacted with are gone, to decide if submission has occurred.
if (!last_interacted_form_.IsNull()) {
return !base::ranges::any_of(last_interacted_form_.GetFormControlElements(),
- &form_util::IsWebElementVisible);
+ &form_util::IsWebElementFocusable);
} else if (!last_interacted_formless_element_.IsNull())
- return !form_util::IsWebElementVisible(last_interacted_formless_element_);
+ return !form_util::IsWebElementFocusable(last_interacted_formless_element_);
return false;
}
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
index 281e17f22f5..b302034d368 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
@@ -82,7 +82,7 @@ using form_util::FindFormControlElementByUniqueRendererId;
using form_util::FindFormControlElementsByUniqueRendererId;
using form_util::GetFieldRendererId;
using form_util::GetFormRendererId;
-using form_util::IsWebElementVisible;
+using form_util::IsWebElementFocusable;
using mojom::FocusedFieldType;
using mojom::SubmissionIndicatorEvent;
@@ -294,8 +294,9 @@ void AnnotateFieldsWithSignatures(
base::NumberToString(field_signature.value()));
SetAttributeAsync(control_element, kDebugAttributeForFormSignature,
form_signature);
- SetAttributeAsync(control_element, kDebugAttributeForVisibility,
- IsWebElementVisible(control_element) ? "true" : "false");
+ SetAttributeAsync(
+ control_element, kDebugAttributeForVisibility,
+ IsWebElementFocusable(control_element) ? "true" : "false");
}
}
@@ -304,36 +305,17 @@ void AnnotateFieldsWithSignatures(
// Instead, we can iterate through the fields of the forms and the unowned
// fields, both of which are cached in the Document.
bool HasPasswordField(const WebLocalFrame& frame) {
- SCOPED_UMA_HISTOGRAM_TIMER_MICROS("Autofill.HasPasswordFieldDuration");
+ static base::NoDestructor<WebString> kPassword("password");
- if (base::FeatureList::IsEnabled(
- features::kAutofillUseUnassociatedListedElements)) {
- static base::NoDestructor<WebString> kPassword("password");
-
- auto ContainsPasswordField = [&](const auto& fields) {
- return base::Contains(fields, *kPassword,
- &WebFormControlElement::FormControlTypeForAutofill);
- };
+ auto ContainsPasswordField = [&](const auto& fields) {
+ return base::Contains(fields, *kPassword,
+ &WebFormControlElement::FormControlTypeForAutofill);
+ };
- WebDocument doc = frame.GetDocument();
- return base::ranges::any_of(doc.Forms(), ContainsPasswordField,
- &WebFormElement::GetFormControlElements) ||
- ContainsPasswordField(doc.UnassociatedFormControls());
- } else {
- static base::NoDestructor<WebString> kPassword("password");
-
- const WebElementCollection elements = frame.GetDocument().All();
- for (WebElement element = elements.FirstItem(); !element.IsNull();
- element = elements.NextItem()) {
- if (element.IsFormControlElement()) {
- const WebFormControlElement& control =
- element.To<WebFormControlElement>();
- if (control.FormControlTypeForAutofill() == *kPassword)
- return true;
- }
- }
- return false;
- }
+ WebDocument doc = frame.GetDocument();
+ return base::ranges::any_of(doc.Forms(), ContainsPasswordField,
+ &WebFormElement::GetFormControlElements) ||
+ ContainsPasswordField(doc.UnassociatedFormControls());
}
// Returns the closest visible autocompletable non-password text element
@@ -361,7 +343,7 @@ WebInputElement FindUsernameElementPrecedingPasswordElement(
const WebInputElement input = iter->DynamicTo<WebInputElement>();
if (!input.IsNull() && input.IsTextField() &&
!input.IsPasswordFieldForAutofill() && IsElementEditable(input) &&
- IsWebElementVisible(input)) {
+ IsWebElementFocusable(input)) {
return input;
}
}
@@ -478,7 +460,7 @@ mojom::SubmissionReadinessState CalculateSubmissionReadiness(
}
auto ShouldIgnoreField = [](const FormFieldData& field) {
- if (!field.IsVisible())
+ if (!field.IsFocusable())
return true;
// Don't treat a checkbox (e.g. "remember me") as an input field that may
// block a form submission. Note: Don't use |check_status !=
@@ -590,11 +572,13 @@ class PasswordAutofillAgent::DeferringPasswordManagerDriver
DeferMsg(&mojom::PasswordManagerDriver::ShowPasswordSuggestions,
text_direction, typed_username, options, bounds);
}
+#if BUILDFLAG(IS_ANDROID)
void ShowTouchToFill(
- autofill::mojom::SubmissionReadinessState submission_readiness) override {
+ mojom::SubmissionReadinessState submission_readiness) override {
DeferMsg(&mojom::PasswordManagerDriver::ShowTouchToFill,
submission_readiness);
}
+#endif
void CheckSafeBrowsingReputation(const GURL& form_action,
const GURL& frame_url) override {
DeferMsg(&mojom::PasswordManagerDriver::CheckSafeBrowsingReputation,
@@ -686,7 +670,7 @@ PasswordAutofillAgent::FocusStateNotifier::FocusStateNotifier(
PasswordAutofillAgent::FocusStateNotifier::~FocusStateNotifier() = default;
void PasswordAutofillAgent::FocusStateNotifier::FocusedInputChanged(
- autofill::FieldRendererId focused_field_id,
+ FieldRendererId focused_field_id,
FocusedFieldType focused_field_type) {
// Forward the request if the type changed or the field is fillable.
if (focused_field_id_ != focused_field_id ||
@@ -733,18 +717,12 @@ void PasswordAutofillAgent::PasswordValueGatekeeper::Reset() {
void PasswordAutofillAgent::PasswordValueGatekeeper::ShowValue(
WebInputElement* element) {
- if (!element->IsNull() && !element->SuggestedValue().IsEmpty()) {
+ if (!element->IsNull() && !element->SuggestedValue().IsEmpty())
element->SetAutofillValue(element->SuggestedValue());
- element->SetAutofillState(WebAutofillState::kAutofilled);
- }
}
bool PasswordAutofillAgent::TextDidChangeInTextField(
const WebInputElement& element) {
- // TODO(crbug.com/415449): Do this through const WebInputElement.
- WebInputElement mutable_element = element; // We need a non-const.
- mutable_element.SetAutofillState(WebAutofillState::kNotFilled);
-
auto iter = web_input_to_password_info_.find(element);
if (iter != web_input_to_password_info_.end()) {
iter->second.password_was_edited_last = false;
@@ -836,9 +814,24 @@ bool PasswordAutofillAgent::FillSuggestion(
FillField(&username_element, username);
}
- if (!password_element.IsNull())
+ if (!password_element.IsNull()) {
FillPasswordFieldAndSave(&password_element, password);
+ // TODO(crbug.com/1319364): As Touch-To-Fill and auto-submission don't
+ // currently support filling single username fields, the code below is
+ // within |!password_element.IsNull()|. Support such fields too and move the
+ // code out the condition.
+ // If the |username_element| is visible/focusable and the |password_element|
+ // is not, trigger submission on the former as the latter unlikely has an
+ // Enter listener.
+ if (!username_element.IsNull() && username_element.IsFocusable() &&
+ !password_element.IsFocusable()) {
+ field_renderer_id_to_submit_ = GetFieldRendererId(username_element);
+ } else {
+ field_renderer_id_to_submit_ = GetFieldRendererId(password_element);
+ }
+ }
+
element.SetSelectionRange(element.Value().length(), element.Value().length());
return true;
@@ -862,7 +855,6 @@ void PasswordAutofillAgent::FillField(WebInputElement* input,
DCHECK(input);
DCHECK(!input->IsNull());
input->SetAutofillValue(WebString::FromUTF16(credential));
- input->SetAutofillState(WebAutofillState::kAutofilled);
const FieldRendererId input_id(input->UniqueRendererFormControlId());
field_data_manager_->UpdateFieldDataMap(
input_id, credential, FieldPropertiesFlags::kAutofilledOnUserTrigger);
@@ -905,14 +897,12 @@ bool PasswordAutofillAgent::PreviewSuggestion(
username_autofill_state_ = username_element.GetAutofillState();
username_element.SetSuggestedValue(username);
- username_element.SetAutofillState(WebAutofillState::kPreviewed);
form_util::PreviewSuggestion(username_element.SuggestedValue().Utf16(),
username_query_prefix_, &username_element);
}
if (!password_element.IsNull()) {
password_autofill_state_ = password_element.GetAutofillState();
password_element.SetSuggestedValue(password);
- password_element.SetAutofillState(WebAutofillState::kPreviewed);
}
return true;
@@ -1016,6 +1006,7 @@ void PasswordAutofillAgent::MaybeCheckSafeBrowsingReputation(
#endif
}
+#if BUILDFLAG(IS_ANDROID)
bool PasswordAutofillAgent::ShouldSuppressKeyboard() {
// The keyboard should be suppressed if we are showing the Touch To Fill UI.
return touch_to_fill_state_ == TouchToFillState::kIsShowing;
@@ -1056,9 +1047,6 @@ bool PasswordAutofillAgent::TryToShowTouchToFill(
focused_input_element_ = input_element;
-// TODO(crbug.com/1299430): Consider to disable |TryToShowTouchToFill| and
-// |ShowTouchToFill| on Desktop.
-#if BUILDFLAG(IS_ANDROID)
WebFormElement form = password_element.Form();
std::unique_ptr<FormData> form_data =
form.IsNull() ? GetFormDataFromUnownedInputElements()
@@ -1067,14 +1055,11 @@ bool PasswordAutofillAgent::TryToShowTouchToFill(
form_data ? CalculateSubmissionReadiness(*form_data, username_element,
password_element)
: mojom::SubmissionReadinessState::kNoInformation);
-#else
- GetPasswordManagerDriver().ShowTouchToFill(
- mojom::SubmissionReadinessState::kNoInformation);
-#endif
touch_to_fill_state_ = TouchToFillState::kIsShowing;
return true;
}
+#endif
bool PasswordAutofillAgent::ShowSuggestions(
const WebInputElement& element,
@@ -1105,12 +1090,14 @@ bool PasswordAutofillAgent::ShowSuggestions(
if (generation_popup_showing)
return false;
+#if BUILDFLAG(IS_ANDROID)
// Don't call ShowSuggestionPopup if Touch To Fill is currently showing. Since
// Touch To Fill in spirit is very similar to a suggestion pop-up, return true
// so that the AutofillAgent does not try to show other autofill suggestions
// instead.
if (touch_to_fill_state_ == TouchToFillState::kIsShowing)
return true;
+#endif
if (!HasDocumentWithValidFrame(element))
return false;
@@ -1187,7 +1174,7 @@ void PasswordAutofillAgent::FireSubmissionIfFormDisappear(
.IsNull()) {
fields = {field};
}
- if (base::ranges::any_of(fields, IsWebElementVisible))
+ if (base::ranges::any_of(fields, IsWebElementFocusable))
return;
}
}
@@ -1269,7 +1256,7 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) {
for (const WebFormElement& form : forms) {
if (only_visible) {
bool is_form_visible = base::ranges::any_of(form.GetFormControlElements(),
- &IsWebElementVisible);
+ &IsWebElementFocusable);
LogHTMLForm(logger.get(), Logger::STRING_FORM_FOUND_ON_PAGE, form);
LogBoolean(logger.get(), Logger::STRING_FORM_IS_VISIBLE, is_form_visible);
@@ -1313,7 +1300,7 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) {
form_util::GetUnownedAutofillableFormFieldElements(frame->GetDocument(),
nullptr);
add_unowned_inputs =
- base::ranges::any_of(control_elements, &IsWebElementVisible);
+ base::ranges::any_of(control_elements, &IsWebElementFocusable);
LogBoolean(logger.get(), Logger::STRING_UNOWNED_INPUTS_VISIBLE,
add_unowned_inputs);
}
@@ -1504,12 +1491,11 @@ void PasswordAutofillAgent::InformNoSavedCredentials(
for (WebFormControlElement& element : elements) {
if (element.IsNull())
continue;
- element.SetSuggestedValue(blink::WebString());
// Don't clear the actual value of fields that the user has edited manually
// (which changes the autofill state back to kNotFilled).
if (element.GetAutofillState() == WebAutofillState::kAutofilled)
element.SetValue(blink::WebString());
- element.SetAutofillState(WebAutofillState::kNotFilled);
+ element.SetSuggestedValue(blink::WebString());
}
all_autofilled_elements_.clear();
@@ -1557,7 +1543,7 @@ void PasswordAutofillAgent::TriggerFormSubmission() {
// Find the last interacted element to simulate an enter keystroke at.
WebFormControlElement form_control = FindFormControlElementByUniqueRendererId(
render_frame()->GetWebFrame()->GetDocument(),
- last_updated_field_renderer_id_, last_updated_form_renderer_id_);
+ field_renderer_id_to_submit_);
if (form_control.IsNull()) {
// The target field doesn't exist anymore. Don't try to submit it.
return;
@@ -1565,17 +1551,8 @@ void PasswordAutofillAgent::TriggerFormSubmission() {
// |form_control| can only be |WebInputElement|, not |WebSelectElement|.
WebInputElement input = form_control.To<WebInputElement>();
-
- // TODO(crbug.com/1283004): Support filling single username fields too.
- DCHECK(input.IsPasswordFieldForAutofill())
- << "Form submission attempt for a non-password element";
-
- // TODO(crbug.com/1283004): Ideally, |CalculateSubmissionReadiness| should be
- // called to check all criteria. Use the DCHECK just for a sanity check now
- // and remove it later.
- DCHECK(input.IsLastInputElementInForm())
- << "Form is not ready for submission";
input.DispatchSimulatedEnter();
+ field_renderer_id_to_submit_ = FieldRendererId();
}
#endif
@@ -1745,7 +1722,10 @@ void PasswordAutofillAgent::CleanupOnDocumentShutdown() {
all_autofilled_elements_.clear();
last_updated_field_renderer_id_ = FieldRendererId();
last_updated_form_renderer_id_ = FormRendererId();
+ field_renderer_id_to_submit_ = FieldRendererId();
+#if BUILDFLAG(IS_ANDROID)
touch_to_fill_state_ = TouchToFillState::kShouldShow;
+#endif
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
page_passwords_analyser_.Reset();
#endif
@@ -2152,6 +2132,10 @@ void PasswordAutofillAgent::TryFixAutofilledForm(
if (cached_element == autofilled_elements_cache_.end())
continue;
+ // autofilled_elements_cache_ stores values filled at page load time and
+ // gets wiped when we observe a user gesture. During this time, the
+ // username/password fields can be in preview state and we restore this
+ // state if JavaScript modifies the field's value.
const WebString& cached_value = cached_element->second;
if (cached_value != element.SuggestedValue())
element.SetSuggestedValue(cached_value);
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h
index 74864a1a8cf..77fa8ac77be 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h
@@ -192,6 +192,7 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// no check request were sent from this frame load.
void MaybeCheckSafeBrowsingReputation(const blink::WebInputElement& element);
+#if BUILDFLAG(IS_ANDROID)
// Returns whether the soft keyboard should be suppressed.
bool ShouldSuppressKeyboard();
@@ -199,6 +200,7 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// whether the agent was able to do so.
bool TryToShowTouchToFill(
const blink::WebFormControlElement& control_element);
+#endif
// Shows an Autofill popup with username suggestions for |element|. If
// |show_all| is |true|, will show all possible suggestions for that element,
@@ -572,7 +574,8 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
bool prefilled_username_metrics_logged_ = false;
- // Keeps autofilled values for the form elements.
+ // Keeps autofilled values for the form elements until a user gesture
+ // is observed. At that point, the map is cleared.
std::map<FieldRendererId, blink::WebString> autofilled_elements_cache_;
std::set<FieldRendererId> all_autofilled_elements_;
// Keeps forms structure (amount of elements, element types etc).
@@ -598,9 +601,15 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// Contains renderer id of the form of the last updated input element.
FormRendererId last_updated_form_renderer_id_;
+ // Contains render id of the field where a form submission should be
+ // triggered.
+ FieldRendererId field_renderer_id_to_submit_;
+
+#if BUILDFLAG(IS_ANDROID)
// Current state of Touch To Fill. This is reset during
// CleanupOnDocumentShutdown.
TouchToFillState touch_to_fill_state_ = TouchToFillState::kShouldShow;
+#endif
};
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
index 9367f0b5900..dac874438f6 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -11,7 +11,7 @@
#include "components/autofill/content/renderer/html_based_username_detector.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/unique_ids.h"
-#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/gaia_auth_util.h"
#include "net/base/url_util.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/web/web_document.h"
@@ -64,13 +64,6 @@ std::vector<FieldRendererId> GetUsernamePredictions(
control_elements, form_data, username_detector_cache, form);
}
-bool HasGaiaSchemeAndHost(const WebFormElement& form) {
- GURL form_url = form.GetDocument().Url();
- GURL gaia_url = GaiaUrls::GetInstance()->gaia_url();
- return form_url.scheme() == gaia_url.scheme() &&
- form_url.host() == gaia_url.host();
-}
-
} // namespace
re2::RE2* CreateMatcher(void* instance, const char* pattern) {
@@ -84,7 +77,7 @@ re2::RE2* CreateMatcher(void* instance, const char* pattern) {
}
bool IsGaiaReauthenticationForm(const blink::WebFormElement& form) {
- if (!HasGaiaSchemeAndHost(form))
+ if (!gaia::HasGaiaSchemeHostPort(form.GetDocument().Url()))
return false;
bool has_rart_field = false;
@@ -113,7 +106,7 @@ bool IsGaiaReauthenticationForm(const blink::WebFormElement& form) {
}
bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form) {
- if (!HasGaiaSchemeAndHost(form))
+ if (!gaia::HasGaiaSchemeHostPort(form.GetDocument().Url()))
return false;
GURL url(form.GetDocument().Url());
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc
index ffdaef209c5..e780918243d 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc
@@ -47,8 +47,8 @@ namespace autofill {
namespace {
-using autofill::form_util::GetTextDirectionForElement;
-using Logger = autofill::SavePasswordProgressLogger;
+using ::autofill::form_util::GetTextDirectionForElement;
+using Logger = ::autofill::SavePasswordProgressLogger;
// Returns the renderer id of the next password field in |control_elements|
// after |new_password|. This field is likely to be the confirmation field.
@@ -308,7 +308,6 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted(
// frame.
if (!render_frame())
return;
- password_element.SetAutofillState(WebAutofillState::kAutofilled);
password_agent_->TrackAutofilledElement(password_element);
// Advance focus to the next input field. We assume password fields in
// an account creation form are always adjacent.
@@ -378,19 +377,17 @@ void PasswordGenerationAgent::TriggeredGeneratePassword(
// |IsPasswordFieldForAutofill()| is deliberately not used.
bool is_generation_element_password_type =
current_generation_item_->generation_element_.IsPasswordField();
- autofill::password_generation::PasswordGenerationUIData
- password_generation_ui_data(
- render_frame()->ElementBoundsInWindow(
- current_generation_item_->generation_element_),
- current_generation_item_->generation_element_.MaxLength(),
- current_generation_item_->generation_element_.NameForAutofill()
- .Utf16(),
- FieldRendererId(current_generation_item_->generation_element_
- .UniqueRendererFormControlId()),
- is_generation_element_password_type,
- GetTextDirectionForElement(
- current_generation_item_->generation_element_),
- current_generation_item_->form_data_);
+ password_generation::PasswordGenerationUIData password_generation_ui_data(
+ render_frame()->ElementBoundsInWindow(
+ current_generation_item_->generation_element_),
+ current_generation_item_->generation_element_.MaxLength(),
+ current_generation_item_->generation_element_.NameForAutofill().Utf16(),
+ FieldRendererId(current_generation_item_->generation_element_
+ .UniqueRendererFormControlId()),
+ is_generation_element_password_type,
+ GetTextDirectionForElement(
+ current_generation_item_->generation_element_),
+ current_generation_item_->form_data_);
std::move(callback).Run(std::move(password_generation_ui_data));
current_generation_item_->generation_popup_shown_ = true;
} else {
@@ -599,19 +596,16 @@ void PasswordGenerationAgent::AutomaticGenerationAvailable() {
// |IsPasswordFieldForAutofill()| is deliberately not used.
bool is_generation_element_password_type =
current_generation_item_->generation_element_.IsPasswordField();
- autofill::password_generation::PasswordGenerationUIData
- password_generation_ui_data(
- render_frame()->ElementBoundsInWindow(
- current_generation_item_->generation_element_),
- current_generation_item_->generation_element_.MaxLength(),
- current_generation_item_->generation_element_.NameForAutofill()
- .Utf16(),
- FieldRendererId(current_generation_item_->generation_element_
- .UniqueRendererFormControlId()),
- is_generation_element_password_type,
- GetTextDirectionForElement(
- current_generation_item_->generation_element_),
- current_generation_item_->form_data_);
+ password_generation::PasswordGenerationUIData password_generation_ui_data(
+ render_frame()->ElementBoundsInWindow(
+ current_generation_item_->generation_element_),
+ current_generation_item_->generation_element_.MaxLength(),
+ current_generation_item_->generation_element_.NameForAutofill().Utf16(),
+ FieldRendererId(current_generation_item_->generation_element_
+ .UniqueRendererFormControlId()),
+ is_generation_element_password_type,
+ GetTextDirectionForElement(current_generation_item_->generation_element_),
+ current_generation_item_->form_data_);
current_generation_item_->generation_popup_shown_ = true;
GetPasswordGenerationDriver().AutomaticGenerationAvailable(
password_generation_ui_data);
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.h b/chromium/components/autofill/content/renderer/password_generation_agent.h
index daf71c78427..3251afc919c 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.h
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.h
@@ -86,7 +86,7 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
bool ShouldIgnoreBlur() const;
#if defined(UNIT_TEST)
- // This method requests the autofill::mojom::PasswordManagerClient which binds
+ // This method requests the mojom::PasswordManagerClient which binds
// requests the binding if it wasn't bound yet.
void RequestPasswordManagerClientForTesting() {
GetPasswordGenerationDriver();
@@ -145,8 +145,8 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
blink::WebInputElement element,
FieldRendererId confirmation_password_renderer_id);
- void LogMessage(autofill::SavePasswordProgressLogger::StringID message_id);
- void LogBoolean(autofill::SavePasswordProgressLogger::StringID message_id,
+ void LogMessage(SavePasswordProgressLogger::StringID message_id);
+ void LogBoolean(SavePasswordProgressLogger::StringID message_id,
bool truth_value);
// Creates a FormData to presave a generated password. It copies behavior
diff --git a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc
index f679571b2e0..6475369839a 100644
--- a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc
+++ b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc
@@ -62,9 +62,11 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver {
int options,
const gfx::RectF& bounds) override {}
+#if BUILDFLAG(IS_ANDROID)
void ShowTouchToFill(
autofill::mojom::SubmissionReadinessState submission_readiness) override {
}
+#endif
void RecordSavePasswordProgress(const std::string& log) override {
called_record_save_ = true;
diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn
index 7a2907f094a..04a0eb767c9 100644
--- a/chromium/components/autofill/core/browser/BUILD.gn
+++ b/chromium/components/autofill/core/browser/BUILD.gn
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/buildflag_header.gni")
import("//build/config/chrome_build.gni")
import("//build/util/version.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
@@ -21,12 +22,37 @@ grit("autofill_address_rewriter_resources") {
output_dir = "$root_gen_dir/components/autofill/core/browser"
}
+declare_args() {
+ # Normally, only branded builds use Google-internal sets of parsing patterns.
+ #
+ # Setting the variable 'use_internal_autofill_patterns' in args.gn overrides
+ # this default.
+ use_internal_autofill_patterns = is_chrome_branded
+}
+
+buildflag_header("buildflags") {
+ header = "form_parsing/buildflags.h"
+ flags = [ "USE_INTERNAL_AUTOFILL_HEADERS=$use_internal_autofill_patterns" ]
+}
+
action("regex_patterns_inl_h") {
visibility = [ ":*" ]
- sources = [ "form_parsing/resources/regex_patterns.json" ]
+
+ # The order of sources must be so that it matches the PatternSource enum
+ # values in regex_patterns.h.
+ if (!use_internal_autofill_patterns) {
+ sources = [ "form_parsing/resources/legacy_regex_patterns.json" ]
+ } else {
+ sources = [
+ "form_parsing/internal_resources/0_default_regex_patterns.json",
+ "form_parsing/internal_resources/1_experimental_regex_patterns.json",
+ "form_parsing/internal_resources/2_nextgen_regex_patterns.json",
+ "form_parsing/resources/legacy_regex_patterns.json",
+ ]
+ }
script = "form_parsing/transpile_regex_patterns.py"
outputs = [ "$target_gen_dir/form_parsing/regex_patterns_inl.h" ]
- args = rebase_path(sources, root_build_dir) +
+ args = [ "--input" ] + rebase_path(sources, root_build_dir) + [ "--output" ] +
rebase_path(outputs, root_build_dir)
}
@@ -209,6 +235,8 @@ static_library("browser") {
"logging/log_receiver.h",
"logging/log_router.cc",
"logging/log_router.h",
+ "merchant_promo_code_manager.cc",
+ "merchant_promo_code_manager.h",
"metrics/autofill_metrics.cc",
"metrics/autofill_metrics.h",
"metrics/form_events/address_form_event_logger.cc",
@@ -218,10 +246,18 @@ static_library("browser") {
"metrics/form_events/form_event_logger_base.cc",
"metrics/form_events/form_event_logger_base.h",
"metrics/form_events/form_events.h",
+ "metrics/form_interactions_counter.cc",
+ "metrics/form_interactions_counter.h",
+ "metrics/payments/manage_cards_prompt_metrics.cc",
+ "metrics/payments/manage_cards_prompt_metrics.h",
+ "metrics/payments/offers_metrics.cc",
+ "metrics/payments/offers_metrics.h",
"metrics/payments/save_credit_card_prompt_metrics.cc",
"metrics/payments/save_credit_card_prompt_metrics.h",
"metrics/payments/virtual_card_enrollment_metrics.cc",
"metrics/payments/virtual_card_enrollment_metrics.h",
+ "metrics/shadow_prediction_metrics.cc",
+ "metrics/shadow_prediction_metrics.h",
"payments/account_info_getter.h",
"payments/autofill_offer_manager.cc",
"payments/autofill_offer_manager.h",
@@ -253,6 +289,14 @@ static_library("browser") {
"payments/payments_customer_data.h",
"payments/payments_requests/get_details_for_enrollment_request.cc",
"payments/payments_requests/get_details_for_enrollment_request.h",
+ "payments/payments_requests/get_unmask_details_request.cc",
+ "payments/payments_requests/get_unmask_details_request.h",
+ "payments/payments_requests/get_upload_details_request.cc",
+ "payments/payments_requests/get_upload_details_request.h",
+ "payments/payments_requests/migrate_cards_request.cc",
+ "payments/payments_requests/migrate_cards_request.h",
+ "payments/payments_requests/opt_change_request.cc",
+ "payments/payments_requests/opt_change_request.h",
"payments/payments_requests/payments_request.cc",
"payments/payments_requests/payments_request.h",
"payments/payments_requests/select_challenge_option_request.cc",
@@ -261,6 +305,8 @@ static_library("browser") {
"payments/payments_requests/unmask_card_request.h",
"payments/payments_requests/update_virtual_card_enrollment_request.cc",
"payments/payments_requests/update_virtual_card_enrollment_request.h",
+ "payments/payments_requests/upload_card_request.cc",
+ "payments/payments_requests/upload_card_request.h",
"payments/payments_service_url.cc",
"payments/payments_service_url.h",
"payments/payments_util.cc",
@@ -295,7 +341,11 @@ static_library("browser") {
"strike_database_integrator_base.h",
"strike_database_integrator_test_strike_database.cc",
"strike_database_integrator_test_strike_database.h",
+ "suggestions_context.cc",
+ "suggestions_context.h",
"sync_utils.h",
+ "touch_to_fill_delegate.cc",
+ "touch_to_fill_delegate.h",
"ui/accessory_sheet_data.cc",
"ui/accessory_sheet_data.h",
"ui/accessory_sheet_enums.h",
@@ -342,8 +392,6 @@ static_library("browser") {
"webdata/autofill_change.h",
"webdata/autofill_entry.cc",
"webdata/autofill_entry.h",
- "webdata/autofill_profile_model_type_controller.cc",
- "webdata/autofill_profile_model_type_controller.h",
"webdata/autofill_profile_sync_bridge.cc",
"webdata/autofill_profile_sync_bridge.h",
"webdata/autofill_profile_sync_difference_tracker.cc",
@@ -445,6 +493,7 @@ static_library("browser") {
"//skia",
"//third_party/abseil-cpp:absl",
"//third_party/libaddressinput",
+ "//third_party/libaddressinput:strings_grit",
"//ui/base",
"//ui/gfx/geometry",
"//ui/gfx/range",
@@ -452,6 +501,7 @@ static_library("browser") {
]
deps = [
":autofill_address_rewriter_resources",
+ ":buildflags",
":regex_patterns_inl_h",
"//base",
"//base:i18n",
@@ -548,6 +598,9 @@ static_library("test_support") {
"autofill_form_test_utils.h",
"autofill_test_utils.cc",
"autofill_test_utils.h",
+ "data_model/test_autofill_data_model.cc",
+ "data_model/test_autofill_data_model.h",
+ "form_structure_test_api.h",
"geo/alternative_state_name_map_test_utils.cc",
"geo/alternative_state_name_map_test_utils.h",
"geo/mock_alternative_state_name_map_updater.cc",
@@ -556,8 +609,12 @@ static_library("test_support") {
"geo/test_region_data_loader.h",
"logging/stub_log_manager.cc",
"logging/stub_log_manager.h",
+ "metrics/autofill_metrics_test_base.cc",
+ "metrics/autofill_metrics_test_base.h",
"mock_autocomplete_history_manager.cc",
"mock_autocomplete_history_manager.h",
+ "mock_merchant_promo_code_manager.cc",
+ "mock_merchant_promo_code_manager.h",
"mock_single_field_form_fill_router.cc",
"mock_single_field_form_fill_router.h",
"payments/test/test_credit_card_otp_authenticator.cc",
@@ -636,6 +693,7 @@ static_library("test_support") {
"//services/network:test_support",
"//services/network/public/cpp",
"//skia",
+ "//testing/gmock",
"//testing/gtest",
"//third_party/libaddressinput:test_support",
"//third_party/libaddressinput:util",
@@ -776,7 +834,10 @@ source_set("unit_tests") {
"logging/log_buffer_submitter_unittest.cc",
"logging/log_manager_unittest.cc",
"logging/log_router_unittest.cc",
+ "merchant_promo_code_manager_unittest.cc",
"metrics/autofill_metrics_unittest.cc",
+ "metrics/payments/offers_metrics_unittest.cc",
+ "metrics/shadow_prediction_metrics_unittest.cc",
"payments/autofill_offer_manager_unittest.cc",
"payments/credit_card_access_manager_unittest.cc",
"payments/credit_card_cvc_authenticator_unittest.cc",
@@ -795,6 +856,7 @@ source_set("unit_tests") {
"personal_data_manager_unittest.cc",
"randomized_encoder_unittest.cc",
"rationalization_util_unittest.cc",
+ "single_field_form_fill_router_unittest.cc",
"strike_database_integrator_test_strike_database_unittest.cc",
"strike_database_unittest.cc",
"test_utils/test_profiles.cc",
@@ -859,6 +921,8 @@ source_set("unit_tests") {
deps = [
":autofill_address_rewriter_resources",
":browser",
+ ":buildflags",
+ ":regex_patterns_inl_h",
":test_support",
":unit_tests_bundle_data",
"//base",
diff --git a/chromium/components/autofill/core/browser/address_normalizer.h b/chromium/components/autofill/core/browser/address_normalizer.h
index 7e2a9e5722b..1e775bb6e67 100644
--- a/chromium/components/autofill/core/browser/address_normalizer.h
+++ b/chromium/components/autofill/core/browser/address_normalizer.h
@@ -15,7 +15,7 @@ namespace autofill {
class AutofillProfile;
// A class used to normalize addresses.
-class AddressNormalizer : public autofill::LoadRulesListener {
+class AddressNormalizer : public LoadRulesListener {
public:
using NormalizationCallback =
base::OnceCallback<void(bool /*success*/,
diff --git a/chromium/components/autofill/core/browser/address_normalizer_impl.cc b/chromium/components/autofill/core/browser/address_normalizer_impl.cc
index a0deef48439..bbdd7291edf 100644
--- a/chromium/components/autofill/core/browser/address_normalizer_impl.cc
+++ b/chromium/components/autofill/core/browser/address_normalizer_impl.cc
@@ -43,8 +43,7 @@ bool NormalizeProfileWithValidator(AutofillProfile* profile,
// Create the AddressData from the profile.
::i18n::addressinput::AddressData address_data =
- *autofill::i18n::CreateAddressDataFromAutofillProfile(*profile,
- app_locale);
+ *i18n::CreateAddressDataFromAutofillProfile(*profile, app_locale);
// Normalize the address.
if (!address_validator->NormalizeAddress(&address_data))
@@ -64,7 +63,7 @@ bool NormalizeProfileWithValidator(AutofillProfile* profile,
void FormatPhoneNumberToE164(AutofillProfile* profile,
const std::string& region_code,
const std::string& app_locale) {
- const std::string formatted_number = autofill::i18n::FormatPhoneForResponse(
+ const std::string formatted_number = i18n::FormatPhoneForResponse(
base::UTF16ToUTF8(
profile->GetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), app_locale)),
region_code);
diff --git a/chromium/components/autofill/core/browser/address_profile_save_manager.cc b/chromium/components/autofill/core/browser/address_profile_save_manager.cc
index 69a18b373ec..3c61cf58fe3 100644
--- a/chromium/components/autofill/core/browser/address_profile_save_manager.cc
+++ b/chromium/components/autofill/core/browser/address_profile_save_manager.cc
@@ -7,6 +7,7 @@
#include "base/stl_util.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/form_data_importer.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/common/autofill_features.h"
@@ -155,6 +156,12 @@ void AddressProfileSaveManager::FinalizeProfileImport(
}
}
+ // If an import is declined, all multi-step candidates should be cleared to
+ // avoid showing a similar import prompt again.
+ if (import_process->UserDeclined() && client_->GetFormDataImporter()) {
+ client_->GetFormDataImporter()->ClearMultiStepImportCandidates();
+ }
+
import_process->CollectMetrics();
ClearPendingImport(std::move(import_process));
}
diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc
index ffe6d409ff7..de40d0d5282 100644
--- a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc
+++ b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc
@@ -16,6 +16,7 @@
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_regexes.h"
#include "components/autofill/core/browser/metrics/autofill_metrics.h"
+#include "components/autofill/core/browser/suggestions_context.h"
#include "components/autofill/core/browser/ui/suggestion.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/browser/webdata/autofill_entry.h"
@@ -26,9 +27,10 @@
#include "components/prefs/pref_service.h"
#include "components/version_info/version_info.h"
-using NotificationType = autofill::AutofillObserver::NotificationType;
-
namespace autofill {
+
+using NotificationType = AutofillObserver::NotificationType;
+
namespace {
// Limit on the number of suggestions to appear in the pop-up menu under an
@@ -83,7 +85,8 @@ void AutocompleteHistoryManager::OnGetSingleFieldSuggestions(
const std::u16string& name,
const std::u16string& prefix,
const std::string& form_control_type,
- base::WeakPtr<SuggestionsHandler> handler) {
+ base::WeakPtr<SuggestionsHandler> handler,
+ const SuggestionsContext& context) {
CancelPendingQueries(handler.get());
if (!IsMeaningfulFieldName(name) || !is_autocomplete_enabled ||
@@ -108,24 +111,22 @@ void AutocompleteHistoryManager::OnGetSingleFieldSuggestions(
}
}
-void AutocompleteHistoryManager::OnWillSubmitForm(
- const FormData& form,
+void AutocompleteHistoryManager::OnWillSubmitFormWithFields(
+ const std::vector<FormFieldData>& fields,
bool is_autocomplete_enabled) {
if (!is_autocomplete_enabled || is_off_the_record_) {
Notify(NotificationType::AutocompleteFormSkipped);
return;
}
-
- std::vector<FormFieldData> values;
- for (const FormFieldData& field : form.fields) {
+ std::vector<FormFieldData> autocomplete_saveable_fields;
+ autocomplete_saveable_fields.reserve(fields.size());
+ for (const FormFieldData& field : fields) {
if (IsFieldValueSaveable(field)) {
- values.push_back(field);
+ autocomplete_saveable_fields.push_back(field);
}
}
-
- if (!values.empty() && profile_database_.get()) {
- profile_database_->AddFormFields(values);
-
+ if (!autocomplete_saveable_fields.empty() && profile_database_.get()) {
+ profile_database_->AddFormFields(autocomplete_saveable_fields);
Notify(NotificationType::AutocompleteFormSubmitted);
}
}
@@ -147,13 +148,15 @@ void AutocompleteHistoryManager::CancelPendingQueries(
void AutocompleteHistoryManager::OnRemoveCurrentSingleFieldSuggestion(
const std::u16string& field_name,
- const std::u16string& value) {
+ const std::u16string& value,
+ int frontend_id) {
if (profile_database_)
profile_database_->RemoveFormValueForElementName(field_name, value);
}
void AutocompleteHistoryManager::OnSingleFieldSuggestionSelected(
- const std::u16string& value) {
+ const std::u16string& value,
+ int frontend_id) {
// Try to find the AutofillEntry associated with the given suggestion.
auto last_entries_iter = last_entries_.find(value);
if (last_entries_iter == last_entries_.end()) {
@@ -257,21 +260,6 @@ void AutocompleteHistoryManager::UMARecorder::OnWebDataServiceRequestDone(
AutofillMetrics::LogAutocompleteSuggestions(has_suggestion);
}
-AutocompleteHistoryManager::QueryHandler::QueryHandler(
- int client_query_id,
- bool autoselect_first_suggestion,
- std::u16string prefix,
- base::WeakPtr<SuggestionsHandler> handler)
- : client_query_id_(client_query_id),
- autoselect_first_suggestion_(autoselect_first_suggestion),
- prefix_(prefix),
- handler_(std::move(handler)) {}
-
-AutocompleteHistoryManager::QueryHandler::QueryHandler(
- const QueryHandler& original) = default;
-
-AutocompleteHistoryManager::QueryHandler::~QueryHandler() = default;
-
void AutocompleteHistoryManager::SendSuggestions(
const std::vector<AutofillEntry>& entries,
const QueryHandler& query_handler) {
diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager.h b/chromium/components/autofill/core/browser/autocomplete_history_manager.h
index a5d7faf8d47..bfb446ce9ee 100644
--- a/chromium/components/autofill/core/browser/autocomplete_history_manager.h
+++ b/chromium/components/autofill/core/browser/autocomplete_history_manager.h
@@ -25,6 +25,8 @@
namespace autofill {
+struct SuggestionsContext;
+
// Per-profile Autocomplete history manager. Handles receiving form data
// from the renderers and the storing and retrieving of form data
// through WebDataServiceBase.
@@ -42,21 +44,22 @@ class AutocompleteHistoryManager : public SingleFieldFormFiller,
~AutocompleteHistoryManager() override;
// SingleFieldFormFiller overrides:
- void OnGetSingleFieldSuggestions(
- int query_id,
- bool is_autocomplete_enabled,
- bool autoselect_first_suggestion,
- const std::u16string& name,
- const std::u16string& prefix,
- const std::string& form_control_type,
- base::WeakPtr<SuggestionsHandler> handler) override;
- void OnWillSubmitForm(const FormData& form,
- bool is_autocomplete_enabled) override;
+ void OnGetSingleFieldSuggestions(int query_id,
+ bool is_autocomplete_enabled,
+ bool autoselect_first_suggestion,
+ const std::u16string& name,
+ const std::u16string& prefix,
+ const std::string& form_control_type,
+ base::WeakPtr<SuggestionsHandler> handler,
+ const SuggestionsContext& context) override;
+ void OnWillSubmitFormWithFields(const std::vector<FormFieldData>& fields,
+ bool is_autocomplete_enabled) override;
void CancelPendingQueries(const SuggestionsHandler* handler) override;
- void OnRemoveCurrentSingleFieldSuggestion(
- const std::u16string& field_name,
- const std::u16string& value) override;
- void OnSingleFieldSuggestionSelected(const std::u16string& value) override;
+ void OnRemoveCurrentSingleFieldSuggestion(const std::u16string& field_name,
+ const std::u16string& value,
+ int frontend_id) override;
+ void OnSingleFieldSuggestionSelected(const std::u16string& value,
+ int frontend_id) override;
// Initializes the instance with the given parameters.
// |profile_database_| is a profile-scope DB used to access autocomplete data.
@@ -108,33 +111,6 @@ class AutocompleteHistoryManager : public SingleFieldFormFiller,
std::u16string measuring_name_;
};
- // Internal data object used to keep a request's context to associate it
- // with the appropriate response.
- struct QueryHandler {
- QueryHandler(int client_query_id,
- bool autoselect_first_suggestion,
- std::u16string prefix,
- base::WeakPtr<SuggestionsHandler> handler);
- QueryHandler(const QueryHandler& original);
- ~QueryHandler();
-
- // Query ID living in the handler's scope, which is NOT the same as the
- // database query ID. This ID is unique per frame, but not per profile.
- int client_query_id_;
-
- // Determines whether we should auto-select the first suggestion when
- // returning. This value was given by the handler when requesting
- // suggestions.
- bool autoselect_first_suggestion_;
-
- // Prefix used to search suggestions, submitted by the handler.
- std::u16string prefix_;
-
- // Weak pointer to the handler instance which will be called-back when
- // we get the response for the associate query.
- base::WeakPtr<SuggestionsHandler> handler_;
- };
-
// Sends the autocomplete |suggestions| to the |query_handler|'s handler for
// display in the associated Autofill popup. The parameter may be empty if
// there are no new autocomplete additions.
diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
index 920e2474972..aae2f13d31c 100644
--- a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
@@ -18,6 +18,7 @@
#include "build/build_config.h"
#include "components/autofill/core/browser/autocomplete_history_manager.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/suggestions_context.h"
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/test_autofill_clock.h"
#include "components/autofill/core/browser/webdata/autofill_entry.h"
@@ -163,8 +164,9 @@ TEST_F(AutocompleteHistoryManagerTest, CreditCardNumberValue) {
form.fields.push_back(valid_cc);
EXPECT_CALL(*(web_data_service_.get()), AddFormFields(_)).Times(0);
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/true);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/true);
}
// Contrary test to AutocompleteHistoryManagerTest.CreditCardNumberValue. The
@@ -186,8 +188,9 @@ TEST_F(AutocompleteHistoryManagerTest, NonCreditCardNumberValue) {
form.fields.push_back(invalid_cc);
EXPECT_CALL(*(web_data_service_.get()), AddFormFields(_));
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/true);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/true);
}
// Tests that SSNs are not sent to the WebDatabase to be saved.
@@ -206,8 +209,9 @@ TEST_F(AutocompleteHistoryManagerTest, SSNValue) {
form.fields.push_back(ssn);
EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/true);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/true);
}
// Verify that autocomplete text is saved for search fields.
@@ -227,8 +231,9 @@ TEST_F(AutocompleteHistoryManagerTest, SearchField) {
form.fields.push_back(search_field);
EXPECT_CALL(*(web_data_service_.get()), AddFormFields(_));
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/true);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/true);
}
TEST_F(AutocompleteHistoryManagerTest, AutocompleteFeatureOff) {
@@ -247,8 +252,9 @@ TEST_F(AutocompleteHistoryManagerTest, AutocompleteFeatureOff) {
form.fields.push_back(search_field);
EXPECT_CALL(*(web_data_service_.get()), AddFormFields(_)).Times(0);
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/false);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/false);
}
// Verify that we don't save invalid values in Autocomplete.
@@ -286,8 +292,9 @@ TEST_F(AutocompleteHistoryManagerTest, InvalidValues) {
form.fields.push_back(search_field);
EXPECT_CALL(*(web_data_service_.get()), AddFormFields(_)).Times(0);
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/true);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/true);
}
// Tests that text entered into fields specifying autocomplete="off" is not sent
@@ -311,8 +318,9 @@ TEST_F(AutocompleteHistoryManagerTest, FieldWithAutocompleteOff) {
form.fields.push_back(field);
EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/true);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/true);
}
// Shouldn't save entries when in Incognito mode.
@@ -334,8 +342,9 @@ TEST_F(AutocompleteHistoryManagerTest, Incognito) {
form.fields.push_back(search_field);
EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/true);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/true);
}
#if !BUILDFLAG(IS_IOS)
@@ -359,8 +368,9 @@ TEST_F(AutocompleteHistoryManagerTest, UserInputNotFocusable) {
form.fields.push_back(search_field);
EXPECT_CALL(*(web_data_service_.get()), AddFormFields(_));
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/true);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/true);
}
#endif
@@ -383,8 +393,9 @@ TEST_F(AutocompleteHistoryManagerTest, PresentationField) {
form.fields.push_back(field);
EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
- autocomplete_manager_->OnWillSubmitForm(form,
- /*is_autocomplete_enabled=*/true);
+ autocomplete_manager_->OnWillSubmitFormWithFields(
+ form.fields,
+ /*is_autocomplete_enabled=*/true);
}
// Tests that the Init function will trigger the Autocomplete Retention Policy
@@ -468,7 +479,7 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that DB response triggers a call to the handler's
// OnSuggestionsReturned
@@ -501,7 +512,7 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that DB response does not trigger a call to the
// handler's OnSuggestionsReturned.
@@ -530,7 +541,7 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that DB response does not trigger a call to the
// handler's OnSuggestionsReturned.
@@ -565,7 +576,7 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that DB response triggers a call to the handler's
EXPECT_CALL(*suggestions_handler.get(),
@@ -600,7 +611,7 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that DB response triggers a call to the handler's
EXPECT_CALL(*suggestions_handler.get(),
@@ -635,14 +646,16 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that DB response triggers a call to the handler's
EXPECT_CALL(*suggestions_handler.get(),
OnSuggestionsReturned(
test_query_id, /*autoselect_first_suggestion=*/false,
UnorderedElementsAre(Field(
- &Suggestion::value, expected_values[0].key().value()))));
+ &Suggestion::main_text,
+ Suggestion::Text(expected_values[0].key().value(),
+ Suggestion::Text::IsPrimary(true))))));
// Simulate response from DB.
autocomplete_manager_->OnWebDataServiceRequestDone(mocked_db_query_id,
@@ -675,14 +688,16 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/true, test_name, test_prefix, "Some Type",
- suggestions_handler->GetWeakPtr());
+ suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that DB response triggers a call to the handler's
EXPECT_CALL(*suggestions_handler.get(),
OnSuggestionsReturned(
test_query_id, /*autoselect_first_suggestion=*/true,
UnorderedElementsAre(Field(
- &Suggestion::value, expected_values[0].key().value()))));
+ &Suggestion::main_text,
+ Suggestion::Text(expected_values[0].key().value(),
+ Suggestion::Text::IsPrimary(true))))));
// Simulate response from DB.
autocomplete_manager_->OnWebDataServiceRequestDone(mocked_db_query_id,
@@ -715,7 +730,7 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that DB response triggers a call to the handler's
EXPECT_CALL(*suggestions_handler.get(),
@@ -754,14 +769,16 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that DB response triggers a call to the handler's
EXPECT_CALL(*suggestions_handler.get(),
OnSuggestionsReturned(
test_query_id, /*autoselect_first_suggestion=*/false,
UnorderedElementsAre(Field(
- &Suggestion::value, expected_values[0].key().value()))));
+ &Suggestion::main_text,
+ Suggestion::Text(expected_values[0].key().value(),
+ Suggestion::Text::IsPrimary(true))))));
// Simulate response from DB.
autocomplete_manager_->OnWebDataServiceRequestDone(mocked_db_query_id,
@@ -804,7 +821,7 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Simulate response from DB.
autocomplete_manager_->OnWebDataServiceRequestDone(mocked_db_query_id,
@@ -814,7 +831,8 @@ TEST_F(AutocompleteHistoryManagerTest,
// Now simulate one autocomplete entry being selected, and expect a metric
// being logged for that value alone.
- autocomplete_manager_->OnSingleFieldSuggestionSelected(test_value);
+ autocomplete_manager_->OnSingleFieldSuggestionSelected(
+ test_value, POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
histogram_tester.ExpectBucketCount("Autocomplete.DaysSinceLastUse",
days_since_last_use, 1);
@@ -853,7 +871,7 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id_first, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Simulate request for the second suggestions (this will cancel the first
// one).
@@ -862,15 +880,16 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id_second, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler->GetWeakPtr());
+ "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext());
// Setting up mock to verify that we can get the second response first.
- EXPECT_CALL(
- *suggestions_handler.get(),
- OnSuggestionsReturned(
- test_query_id_second, /*autoselect_first_suggestion=*/false,
- UnorderedElementsAre(Field(
- &Suggestion::value, expected_values_second[0].key().value()))));
+ EXPECT_CALL(*suggestions_handler.get(),
+ OnSuggestionsReturned(
+ test_query_id_second, /*autoselect_first_suggestion=*/false,
+ UnorderedElementsAre(Field(
+ &Suggestion::main_text,
+ Suggestion::Text(expected_values_second[0].key().value(),
+ Suggestion::Text::IsPrimary(true))))));
// Simulate response from DB, second request comes back before.
autocomplete_manager_->OnWebDataServiceRequestDone(
@@ -922,33 +941,37 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id_first, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler_first->GetWeakPtr());
+ "Some Type", suggestions_handler_first->GetWeakPtr(),
+ SuggestionsContext());
// Simulate request for the second suggestions.
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id_second, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler_second->GetWeakPtr());
+ "Some Type", suggestions_handler_second->GetWeakPtr(),
+ SuggestionsContext());
// Setting up mock to verify that we get the second response first.
- EXPECT_CALL(
- *suggestions_handler_second.get(),
- OnSuggestionsReturned(
- test_query_id_second, /*autoselect_first_suggestion=*/false,
- UnorderedElementsAre(Field(
- &Suggestion::value, expected_values_second[0].key().value()))));
+ EXPECT_CALL(*suggestions_handler_second.get(),
+ OnSuggestionsReturned(
+ test_query_id_second, /*autoselect_first_suggestion=*/false,
+ UnorderedElementsAre(Field(
+ &Suggestion::main_text,
+ Suggestion::Text(expected_values_second[0].key().value(),
+ Suggestion::Text::IsPrimary(true))))));
// Simulate response from DB, second request comes back before.
autocomplete_manager_->OnWebDataServiceRequestDone(
mocked_db_query_id_second, std::move(mocked_results_second));
// Setting up mock to verify that we get the first response second.
- EXPECT_CALL(
- *suggestions_handler_first.get(),
- OnSuggestionsReturned(
- test_query_id_first, /*autoselect_first_suggestion=*/false,
- UnorderedElementsAre(Field(&Suggestion::value,
- expected_values_first[0].key().value()))));
+ EXPECT_CALL(*suggestions_handler_first.get(),
+ OnSuggestionsReturned(
+ test_query_id_first, /*autoselect_first_suggestion=*/false,
+ UnorderedElementsAre(Field(
+ &Suggestion::main_text,
+ Suggestion::Text(expected_values_first[0].key().value(),
+ Suggestion::Text::IsPrimary(true))))));
// Simulate response from DB, first request comes back after.
autocomplete_manager_->OnWebDataServiceRequestDone(
@@ -989,13 +1012,13 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id_one, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler_one->GetWeakPtr());
+ "Some Type", suggestions_handler_one->GetWeakPtr(), SuggestionsContext());
// Simlate second handler request for autocomplete suggestions.
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id_two, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler_two->GetWeakPtr());
+ "Some Type", suggestions_handler_two->GetWeakPtr(), SuggestionsContext());
// Simlate first handler cancelling its request.
EXPECT_CALL(*web_data_service_, CancelRequest(mocked_db_query_id_one))
@@ -1003,12 +1026,13 @@ TEST_F(AutocompleteHistoryManagerTest,
autocomplete_manager_->CancelPendingQueries(suggestions_handler_one.get());
// Simulate second handler receiving the suggestions.
- EXPECT_CALL(
- *suggestions_handler_two.get(),
- OnSuggestionsReturned(
- test_query_id_two, /*autoselect_first_suggestion=*/false,
- UnorderedElementsAre(Field(&Suggestion::value,
- expected_values_two[0].key().value()))));
+ EXPECT_CALL(*suggestions_handler_two.get(),
+ OnSuggestionsReturned(
+ test_query_id_two, /*autoselect_first_suggestion=*/false,
+ UnorderedElementsAre(Field(
+ &Suggestion::main_text,
+ Suggestion::Text(expected_values_two[0].key().value(),
+ Suggestion::Text::IsPrimary(true))))));
autocomplete_manager_->OnWebDataServiceRequestDone(
mocked_db_query_id_two, std::move(mocked_results_two));
@@ -1042,7 +1066,8 @@ TEST_F(AutocompleteHistoryManagerTest, NoAutocompleteSuggestionsForTextarea) {
autocomplete_manager_->OnGetSingleFieldSuggestions(
0, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, field.name, field.value,
- field.form_control_type, suggestions_handler->GetWeakPtr());
+ field.form_control_type, suggestions_handler->GetWeakPtr(),
+ SuggestionsContext());
histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 0, 1);
histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 1, 0);
@@ -1071,7 +1096,8 @@ TEST_F(AutocompleteHistoryManagerTest, AutocompleteUMAQueryCreated) {
autocomplete_manager_->OnGetSingleFieldSuggestions(
0, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, field.name, field.value,
- field.form_control_type, suggestions_handler->GetWeakPtr());
+ field.form_control_type, suggestions_handler->GetWeakPtr(),
+ SuggestionsContext());
histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 1, 1);
histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 0, 0);
@@ -1101,7 +1127,8 @@ TEST_F(AutocompleteHistoryManagerTest, AutocompleteUMAQueryCreated) {
autocomplete_manager_->OnGetSingleFieldSuggestions(
0, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, field.name, field.value,
- field.form_control_type, suggestions_handler->GetWeakPtr());
+ field.form_control_type, suggestions_handler->GetWeakPtr(),
+ SuggestionsContext());
histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 1, 2);
histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 0, 0);
@@ -1137,13 +1164,15 @@ TEST_F(AutocompleteHistoryManagerTest, DestructorCancelsRequests) {
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id_first, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler_first->GetWeakPtr());
+ "Some Type", suggestions_handler_first->GetWeakPtr(),
+ SuggestionsContext());
// Simulate request for the second suggestions.
autocomplete_manager_->OnGetSingleFieldSuggestions(
test_query_id_second, /*is_autocomplete_enabled=*/true,
/*autoselect_first_suggestion=*/false, test_name, test_prefix,
- "Some Type", suggestions_handler_second->GetWeakPtr());
+ "Some Type", suggestions_handler_second->GetWeakPtr(),
+ SuggestionsContext());
// Expect cancel calls for both requests.
EXPECT_CALL(*web_data_service_, CancelRequest(mocked_db_query_id_first))
diff --git a/chromium/components/autofill/core/browser/autofill_ablation_study.cc b/chromium/components/autofill/core/browser/autofill_ablation_study.cc
index 0dcc131af00..1a158bf9cde 100644
--- a/chromium/components/autofill/core/browser/autofill_ablation_study.cc
+++ b/chromium/components/autofill/core/browser/autofill_ablation_study.cc
@@ -21,11 +21,11 @@
namespace autofill {
-using autofill::features::kAutofillAblationStudyAblationWeightPerMilleParam;
-using autofill::features::kAutofillAblationStudyEnabledForAddressesParam;
-using autofill::features::kAutofillAblationStudyEnabledForPaymentsParam;
-using autofill::features::kAutofillEnableAblationStudy;
-using autofill::features::kAutofillShowTypePredictions;
+using ::autofill::features::kAutofillAblationStudyAblationWeightPerMilleParam;
+using ::autofill::features::kAutofillAblationStudyEnabledForAddressesParam;
+using ::autofill::features::kAutofillAblationStudyEnabledForPaymentsParam;
+using ::autofill::features::kAutofillEnableAblationStudy;
+using ::autofill::features::kAutofillShowTypePredictions;
namespace {
diff --git a/chromium/components/autofill/core/browser/autofill_ablation_study.h b/chromium/components/autofill/core/browser/autofill_ablation_study.h
index 9ff3f7ad1a8..6bf95b3c04b 100644
--- a/chromium/components/autofill/core/browser/autofill_ablation_study.h
+++ b/chromium/components/autofill/core/browser/autofill_ablation_study.h
@@ -45,8 +45,7 @@ uint64_t GetAblationHash(const std::string& seed,
// combination of [site * browsing session * day]: Different sites may have
// ablation configurations. Restarting the browser or waiting for the next day
// may lead to different ablation configurations as well.
-// The ablation is controlled by
-// autofill::features::kAutofillEnableAblationStudy.
+// The ablation is controlled by features::kAutofillEnableAblationStudy.
class AutofillAblationStudy {
public:
AutofillAblationStudy();
diff --git a/chromium/components/autofill/core/browser/autofill_address_policy_handler.cc b/chromium/components/autofill/core/browser/autofill_address_policy_handler.cc
index 3072e4c8d7b..6b95fc78455 100644
--- a/chromium/components/autofill/core/browser/autofill_address_policy_handler.cc
+++ b/chromium/components/autofill/core/browser/autofill_address_policy_handler.cc
@@ -24,7 +24,7 @@ void AutofillAddressPolicyHandler::ApplyPolicySettings(
const base::Value* value =
policies.GetValue(policy_name(), base::Value::Type::BOOLEAN);
if (value && !value->GetBool())
- prefs->SetBoolean(autofill::prefs::kAutofillProfileEnabled, false);
+ prefs->SetBoolean(prefs::kAutofillProfileEnabled, false);
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_address_util.cc b/chromium/components/autofill/core/browser/autofill_address_util.cc
index a75d900adee..3000d73edd8 100644
--- a/chromium/components/autofill/core/browser/autofill_address_util.cc
+++ b/chromium/components/autofill/core/browser/autofill_address_util.cc
@@ -16,6 +16,7 @@
#include "base/values.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/autofill_profile_comparator.h"
+#include "components/autofill/core/browser/geo/address_i18n.h"
#include "components/autofill/core/browser/geo/autofill_country.h"
#include "components/autofill/core/browser/ui/country_combobox_model.h"
#include "components/autofill/core/common/autofill_features.h"
@@ -26,8 +27,6 @@
#include "third_party/re2/src/re2/re2.h"
#include "ui/base/l10n/l10n_util.h"
-using autofill::AutofillCountry;
-using autofill::ServerFieldType;
using i18n::addressinput::AddressField;
using i18n::addressinput::AddressUiComponent;
using i18n::addressinput::Localization;
@@ -36,6 +35,39 @@ namespace autofill {
namespace {
+// Extend `components` using Autofill's address format extensions. These are
+// used to add support for fields that are not strictly required for a valid
+// address, and thus not provided by libaddressinput, but still commonly appear
+// in forms.
+void ExtendAddressComponents(std::vector<AddressUiComponent>& components,
+ const std::string& country_code,
+ const Localization& localization) {
+ AutofillCountry country(country_code);
+ for (const AutofillCountry::AddressFormatExtension& rule :
+ country.address_format_extensions()) {
+ // Find the location of `rule.placed_after` in `components`.
+ // `components.field` is only valid if `components.literal.empty()`.
+ auto prev_component = base::ranges::find_if(
+ components, [&rule](const AddressUiComponent& component) {
+ return component.literal.empty() &&
+ component.field == rule.placed_after;
+ });
+ DCHECK(prev_component != components.end());
+
+ // Insert the separator and `rule.type` afterwards.
+ components.insert(
+ ++prev_component,
+ {AddressUiComponent{.literal =
+ std::string(rule.separator_before_label)},
+ AddressUiComponent{
+ .field = rule.type,
+ .name = localization.GetString(rule.label_id),
+ .length_hint = rule.large_sized
+ ? AddressUiComponent::HINT_LONG
+ : AddressUiComponent::HINT_SHORT}});
+ }
+}
+
// Returns a vector of AddressUiComponent for `country_code` when using
// `ui_language_code`. If no components are available for `country_code`, it
// defaults back to the US. If `ui_language_code` is not valid, the default
@@ -53,8 +85,10 @@ std::vector<AddressUiComponent> GetAddressComponents(
std::vector<AddressUiComponent> components =
::i18n::addressinput::BuildComponentsWithLiterals(
country, localization, ui_language_code, components_language_code);
- if (!components.empty())
+ if (!components.empty()) {
+ ExtendAddressComponents(components, country, localization);
return components;
+ }
}
NOTREACHED();
return {};
@@ -62,29 +96,6 @@ std::vector<AddressUiComponent> GetAddressComponents(
} // namespace
-ServerFieldType AddressFieldToServerFieldType(AddressField address_field) {
- switch (address_field) {
- case ::i18n::addressinput::COUNTRY:
- return ADDRESS_HOME_COUNTRY;
- case ::i18n::addressinput::ADMIN_AREA:
- return ADDRESS_HOME_STATE;
- case ::i18n::addressinput::LOCALITY:
- return ADDRESS_HOME_CITY;
- case ::i18n::addressinput::DEPENDENT_LOCALITY:
- return ADDRESS_HOME_DEPENDENT_LOCALITY;
- case ::i18n::addressinput::SORTING_CODE:
- return ADDRESS_HOME_SORTING_CODE;
- case ::i18n::addressinput::POSTAL_CODE:
- return ADDRESS_HOME_ZIP;
- case ::i18n::addressinput::STREET_ADDRESS:
- return ADDRESS_HOME_STREET_ADDRESS;
- case ::i18n::addressinput::ORGANIZATION:
- return COMPANY_NAME;
- case ::i18n::addressinput::RECIPIENT:
- return NAME_FULL;
- }
-}
-
void GetAddressComponents(
const std::string& country_code,
const std::string& ui_language_code,
@@ -146,8 +157,7 @@ std::u16string GetEnvelopeStyleAddress(const AutofillProfile& profile,
component.field == ::i18n::addressinput::RECIPIENT) {
continue;
}
- ServerFieldType type =
- autofill::AddressFieldToServerFieldType(component.field);
+ ServerFieldType type = i18n::TypeForField(component.field);
if (type == NAME_FULL)
type = NAME_FULL_WITH_HONORIFIC_PREFIX;
address += base::UTF16ToUTF8(profile.GetInfo(type, ui_language_code));
@@ -208,8 +218,8 @@ std::vector<ProfileValueDifference> GetProfileDifferenceForUi(
base::flat_map<ServerFieldType, std::pair<std::u16string, std::u16string>>
differences = AutofillProfileComparator::GetProfileDifferenceMap(
first_profile, second_profile,
- autofill::ServerFieldTypeSet(std::begin(kTypeToCompare),
- std::end(kTypeToCompare)),
+ ServerFieldTypeSet(std::begin(kTypeToCompare),
+ std::end(kTypeToCompare)),
app_locale);
std::u16string first_address = GetEnvelopeStyleAddress(
diff --git a/chromium/components/autofill/core/browser/autofill_address_util.h b/chromium/components/autofill/core/browser/autofill_address_util.h
index 54012523564..31cdd6fe172 100644
--- a/chromium/components/autofill/core/browser/autofill_address_util.h
+++ b/chromium/components/autofill/core/browser/autofill_address_util.h
@@ -16,9 +16,6 @@ namespace autofill {
class AutofillProfile;
class PersonalDataManager;
-ServerFieldType AddressFieldToServerFieldType(
- ::i18n::addressinput::AddressField address_field);
-
// |address_components| is a 2D array for the address components in each line.
// Fills |address_components| with the address UI components that should be used
// to input an address for |country_code| when UI BCP 47 language code is
diff --git a/chromium/components/autofill/core/browser/autofill_address_util_unittest.cc b/chromium/components/autofill/core/browser/autofill_address_util_unittest.cc
index 96d451d98e2..047b9e4588b 100644
--- a/chromium/components/autofill/core/browser/autofill_address_util_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_address_util_unittest.cc
@@ -6,7 +6,9 @@
#include "base/guid.h"
#include "base/memory/raw_ptr.h"
+#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h"
@@ -50,6 +52,33 @@ TEST_F(AddressFormattingTest, GetAddressComponentsSkipsEmptyLines) {
base::ranges::any_of(lines, [](auto line) { return line.empty(); }));
}
+// Tests that address field extensions are applied to `GetAddressComponents()`,
+// by checking that Great Britain's address format is extended by a state field.
+TEST_F(AddressFormattingTest, GetAddressComponentsWithExtensions) {
+ base::test::ScopedFeatureList address_extension_feature;
+ address_extension_feature.InitAndEnableFeature(
+ features::kAutofillEnableExtendedAddressFormats);
+
+ std::vector<std::vector<::i18n::addressinput::AddressUiComponent>> lines;
+ std::string components_language_code;
+ autofill::GetAddressComponents("GB", GetLocale(), /*include_literals=*/false,
+ &lines, &components_language_code);
+
+ // Expect to find a line consisting solely of a state field.
+ // Because `include_literals=false`, accessing `.field` is valid.
+ auto state_line = base::ranges::find_if(lines, [](const auto& line) {
+ return line.size() == 1 &&
+ line[0].field == ::i18n::addressinput::AddressField::ADMIN_AREA;
+ });
+ ASSERT_NE(state_line, lines.end());
+ EXPECT_EQ((*state_line)[0].length_hint,
+ ::i18n::addressinput::AddressUiComponent::HINT_LONG);
+ // The prior component on the previous line should be the postal code.
+ ASSERT_NE(state_line, lines.begin());
+ EXPECT_EQ((--state_line)->back().field,
+ ::i18n::addressinput::AddressField::POSTAL_CODE);
+}
+
TEST_F(AddressFormattingTest, GetEnvelopeStyleAddressSanity) {
AutofillProfile profile = test::GetFullProfile();
std::u16string address =
@@ -142,6 +171,30 @@ TEST_F(
EXPECT_EQ(address.find(u" "), std::string::npos);
}
+// Tests that address field extensions are applied to
+// `GetEnvelopeStyleAddress()`, by checking that Great Britain's address format
+// is extended by a state field.
+TEST_F(AddressFormattingTest, GetEnvelopeStyleAddressWithExtensions) {
+ base::test::ScopedFeatureList address_extension_feature;
+ address_extension_feature.InitAndEnableFeature(
+ features::kAutofillEnableExtendedAddressFormats);
+
+ AutofillProfile profile(base::GenerateGUID(), /*origin=*/"");
+ test::SetProfileInfo(&profile, "FirstName", "MiddleName", "LastName",
+ "johndoe@hades.com", /*company=*/"", "666 Erebus St.",
+ "Apt 8", "Elysium", /*state=*/"Greater London",
+ "WC2H 8AG", "GB", "+44 20 7031 3000");
+
+ std::u16string address =
+ GetEnvelopeStyleAddress(profile, GetLocale(), /*include_recipient=*/true,
+ /*include_country=*/true);
+ // Expect the zip code, followed by a new line and the state.
+ EXPECT_NE(
+ address.find(profile.GetInfo(ADDRESS_HOME_ZIP, GetLocale()) + u"\n" +
+ profile.GetInfo(ADDRESS_HOME_STATE, GetLocale())),
+ std::string::npos);
+}
+
TEST_F(AddressFormattingTest,
GetEnvelopeStyleAddressHasNoDifferencesBetweenIdenticalProfiles) {
AutofillProfile profile = test::GetFullProfile();
diff --git a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
index bf866489bc4..f3e66bf71c5 100644
--- a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
+++ b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
@@ -39,9 +39,10 @@ html {
.fake-button {
background-color: lightgray;
border: 1px solid black;
+ cursor: pointer;
margin-inline-end: 1em;
padding: .5ex;
- cursor: pointer;
+ white-space: nowrap;
}
#logging-note {
@@ -156,6 +157,10 @@ html {
background-color: #F8BBD0;
}
+.log-entry[scope='WebsiteModifiedFieldValue'] {
+ background-color: #FF7C5D;
+}
+
/*
* Checkboxes add/remove hide-<Scope> classes to the #log-entries. Hiding of the
* relevant <div>'s and adjacent <hr>'s is implemented by these classes.
@@ -211,6 +216,11 @@ html {
display: none;
}
+.hide-WebsiteModifiedFieldValue .log-entry[scope=WebsiteModifiedFieldValue],
+.hide-WebsiteModifiedFieldValue .log-entry[scope=WebsiteModifiedFieldValue] + hr {
+ display: none;
+}
+
.form {
border: 1px black solid;
margin: 3px;
diff --git a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js
index 8dea262ac8f..91503420ffe 100644
--- a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js
+++ b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js
@@ -204,6 +204,10 @@ function setUpMarker() {
if (scrollAfterInsert) {
scrollDown();
// Focus marker div, set caret at end of line.
+ const logDiv = document.getElementById('log-entries');
+ if (!logDiv) {
+ return;
+ }
const markerNode = logDiv.lastChild;
const textNode = markerNode.lastChild;
markerNode.focus();
@@ -255,6 +259,7 @@ function setUpLogDisplayConfig() {
'AutofillServer',
'Metrics',
'AddressProfileFormImport',
+ 'WebsiteModifiedFieldValue',
];
const logDiv = document.getElementById('log-entries');
const autoScrollInput = document.getElementById('enable-autoscroll');
diff --git a/chromium/components/autofill/core/browser/autofill_client.cc b/chromium/components/autofill/core/browser/autofill_client.cc
index babc0f11685..1bb339fa7f1 100644
--- a/chromium/components/autofill/core/browser/autofill_client.cc
+++ b/chromium/components/autofill/core/browser/autofill_client.cc
@@ -19,7 +19,7 @@ AutofillClient::PopupOpenArgs::PopupOpenArgs() = default;
AutofillClient::PopupOpenArgs::PopupOpenArgs(
const gfx::RectF& element_bounds,
base::i18n::TextDirection text_direction,
- std::vector<autofill::Suggestion> suggestions,
+ std::vector<Suggestion> suggestions,
AutoselectFirstSuggestion autoselect_first_suggestion,
PopupType popup_type)
: element_bounds(element_bounds),
@@ -41,10 +41,14 @@ version_info::Channel AutofillClient::GetChannel() const {
return version_info::Channel::UNKNOWN;
}
+MerchantPromoCodeManager* AutofillClient::GetMerchantPromoCodeManager() {
+ return nullptr;
+}
+
std::unique_ptr<SingleFieldFormFillRouter>
AutofillClient::GetSingleFieldFormFillRouter() {
return std::make_unique<SingleFieldFormFillRouter>(
- GetAutocompleteHistoryManager());
+ GetAutocompleteHistoryManager(), GetMerchantPromoCodeManager());
}
AutofillOfferManager* AutofillClient::GetAutofillOfferManager() {
@@ -76,7 +80,7 @@ void AutofillClient::DismissUnmaskAuthenticatorSelectionDialog(
// ChromeAutofillClient (Chrome Desktop and Clank) implements this.
}
-raw_ptr<VirtualCardEnrollmentManager>
+VirtualCardEnrollmentManager*
AutofillClient::GetVirtualCardEnrollmentManager() {
// This is overridden by platform subclasses. Currently only
// ChromeAutofillClient (Chrome Desktop and Clank) implements this.
diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h
index 65448113702..d7994f5f4a4 100644
--- a/chromium/components/autofill/core/browser/autofill_client.h
+++ b/chromium/components/autofill/core/browser/autofill_client.h
@@ -59,7 +59,8 @@ namespace autofill {
class AddressNormalizer;
class AutofillAblationStudy;
-struct AutofillOfferData;
+class AutofillDriver;
+class AutofillOfferData;
class AutofillProfile;
class AutocompleteHistoryManager;
class AutofillOfferManager;
@@ -72,6 +73,7 @@ class FormDataImporter;
class FormStructure;
class LogManager;
class MigratableCreditCard;
+class MerchantPromoCodeManager;
class OtpUnmaskDelegate;
enum class OtpUnmaskResult;
class PersonalDataManager;
@@ -251,7 +253,7 @@ class AutofillClient : public RiskDataLoader {
PopupOpenArgs();
PopupOpenArgs(const gfx::RectF& element_bounds,
base::i18n::TextDirection text_direction,
- std::vector<autofill::Suggestion> suggestions,
+ std::vector<Suggestion> suggestions,
AutoselectFirstSuggestion autoselect_first_suggestion,
PopupType popup_type);
PopupOpenArgs(const PopupOpenArgs&);
@@ -263,7 +265,7 @@ class AutofillClient : public RiskDataLoader {
gfx::RectF element_bounds;
base::i18n::TextDirection text_direction =
base::i18n::TextDirection::UNKNOWN_DIRECTION;
- std::vector<autofill::Suggestion> suggestions;
+ std::vector<Suggestion> suggestions;
AutoselectFirstSuggestion autoselect_first_suggestion{false};
PopupType popup_type = PopupType::kUnspecified;
};
@@ -303,7 +305,7 @@ class AutofillClient : public RiskDataLoader {
using AddressProfileSavePromptCallback =
base::OnceCallback<void(SaveAddressProfileOfferUserDecision,
- autofill::AutofillProfile profile)>;
+ AutofillProfile profile)>;
~AutofillClient() override = default;
@@ -316,9 +318,13 @@ class AutofillClient : public RiskDataLoader {
// Gets the PersonalDataManager instance associated with the client.
virtual PersonalDataManager* GetPersonalDataManager() = 0;
- // Gets the AutocompleteHistoryManager instance associate with the client.
+ // Gets the AutocompleteHistoryManager instance associated with the client.
virtual AutocompleteHistoryManager* GetAutocompleteHistoryManager() = 0;
+ // Gets the MerchantPromoCodeManager instance associated with the
+ // client (can be null for unsupported platforms).
+ virtual MerchantPromoCodeManager* GetMerchantPromoCodeManager();
+
// Creates and returns a SingleFieldFormFillRouter using the
// AutocompleteHistoryManager instance associated with the client.
std::unique_ptr<SingleFieldFormFillRouter> GetSingleFieldFormFillRouter();
@@ -423,8 +429,7 @@ class AutofillClient : public RiskDataLoader {
// AutofillClient. VirtualCardEnrollmentManager is used for virtual card
// enroll and unenroll related flows. This function may return a nullptr on
// some platforms.
- virtual raw_ptr<VirtualCardEnrollmentManager>
- GetVirtualCardEnrollmentManager();
+ virtual VirtualCardEnrollmentManager* GetVirtualCardEnrollmentManager();
// Shows a dialog for the user to enroll in a virtual card.
virtual void ShowVirtualCardEnrollDialog(
@@ -654,7 +659,7 @@ class AutofillClient : public RiskDataLoader {
// Pass the form structures to the password manager to choose correct username
// and to the password generation manager to detect account creation forms.
virtual void PropagateAutofillPredictions(
- content::RenderFrameHost* rfh,
+ AutofillDriver* driver,
const std::vector<FormStructure*>& forms) = 0;
// Inform the client that the field has been filled.
@@ -685,6 +690,11 @@ class AutofillClient : public RiskDataLoader {
// Checks whether the current query is the most recent one.
virtual bool IsQueryIDRelevant(int query_id) = 0;
#endif
+
+ // Navigates to |url| in a new tab. |url| links to the promo code offer
+ // details page for the offers in a promo code suggestions popup. Every offer
+ // in a promo code suggestions popup links to the same offer details page.
+ virtual void OpenPromoCodeOfferDetailsURL(const GURL& url) = 0;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_credit_card_policy_handler.cc b/chromium/components/autofill/core/browser/autofill_credit_card_policy_handler.cc
index 14610c6225f..7ba3d92efe1 100644
--- a/chromium/components/autofill/core/browser/autofill_credit_card_policy_handler.cc
+++ b/chromium/components/autofill/core/browser/autofill_credit_card_policy_handler.cc
@@ -24,7 +24,7 @@ void AutofillCreditCardPolicyHandler::ApplyPolicySettings(
const base::Value* value =
policies.GetValue(policy_name(), base::Value::Type::BOOLEAN);
if (value && !value->GetBool())
- prefs->SetBoolean(autofill::prefs::kAutofillCreditCardEnabled, false);
+ prefs->SetBoolean(prefs::kAutofillCreditCardEnabled, false);
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_data_util.cc b/chromium/components/autofill/core/browser/autofill_data_util.cc
index c76a024b17d..ef6c5882a85 100644
--- a/chromium/components/autofill/core/browser/autofill_data_util.cc
+++ b/chromium/components/autofill/core/browser/autofill_data_util.cc
@@ -109,7 +109,7 @@ bool ContainsString(const char* const set[],
base::TrimString(element, u".", base::TRIM_ALL);
for (size_t i = 0; i < set_size; ++i) {
- if (base::LowerCaseEqualsASCII(trimmed_element, set[i]))
+ if (base::EqualsCaseInsensitiveASCII(trimmed_element, set[i]))
return true;
}
diff --git a/chromium/components/autofill/core/browser/autofill_data_util.h b/chromium/components/autofill/core/browser/autofill_data_util.h
index 316a6358571..76250948c77 100644
--- a/chromium/components/autofill/core/browser/autofill_data_util.h
+++ b/chromium/components/autofill/core/browser/autofill_data_util.h
@@ -121,7 +121,7 @@ bool IsValidCountryCode(const std::u16string& country_code);
// Returns a country code to be used when validating this profile. If the
// profile has a valid country code set, it is returned. If not, a country code
// associated with |app_locale| is used as a fallback.
-std::string GetCountryCodeWithFallback(const autofill::AutofillProfile& profile,
+std::string GetCountryCodeWithFallback(const AutofillProfile& profile,
const std::string& app_locale);
} // namespace data_util
diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.cc b/chromium/components/autofill/core/browser/autofill_download_manager.cc
index 4539acbab75..611d90111bb 100644
--- a/chromium/components/autofill/core/browser/autofill_download_manager.cc
+++ b/chromium/components/autofill/core/browser/autofill_download_manager.cc
@@ -235,8 +235,8 @@ void LogExponentialBackoffDelay(
}
net::NetworkTrafficAnnotationTag GetNetworkTrafficAnnotation(
- const autofill::AutofillDownloadManager::RequestType& request_type) {
- if (request_type == autofill::AutofillDownloadManager::REQUEST_QUERY) {
+ const AutofillDownloadManager::RequestType& request_type) {
+ if (request_type == AutofillDownloadManager::REQUEST_QUERY) {
return net::DefineNetworkTrafficAnnotation("autofill_query", R"(
semantics {
sender: "Autofill"
@@ -268,7 +268,7 @@ net::NetworkTrafficAnnotationTag GetNetworkTrafficAnnotation(
})");
}
- DCHECK_EQ(request_type, autofill::AutofillDownloadManager::REQUEST_UPLOAD);
+ DCHECK_EQ(request_type, AutofillDownloadManager::REQUEST_UPLOAD);
return net::DefineNetworkTrafficAnnotation("autofill_upload", R"(
semantics {
sender: "Autofill"
@@ -321,7 +321,7 @@ const char* RequestTypeToString(AutofillDownloadManager::RequestType type) {
}
std::ostream& operator<<(std::ostream& out,
- const autofill::AutofillPageQueryRequest& query) {
+ const AutofillPageQueryRequest& query) {
out << "client_version: " << query.client_version();
for (const auto& form : query.forms()) {
out << "\nForm\n signature: " << form.signature();
@@ -337,7 +337,7 @@ std::ostream& operator<<(std::ostream& out,
}
std::ostream& operator<<(std::ostream& out,
- const autofill::AutofillUploadContents& upload) {
+ const AutofillUploadContents& upload) {
out << "client_version: " << upload.client_version() << "\n";
out << "form_signature: " << upload.form_signature() << "\n";
out << "data_present: " << upload.data_present() << "\n";
@@ -381,8 +381,7 @@ std::string FieldTypeToString(int type) {
AutofillType(ToSafeServerFieldType(type, UNKNOWN_TYPE)).ToString()});
}
-LogBuffer& operator<<(LogBuffer& out,
- const autofill::AutofillUploadContents& upload) {
+LogBuffer& operator<<(LogBuffer& out, const AutofillUploadContents& upload) {
if (!out.active())
return out;
out << Tag{"div"} << Attrib{"class", "form"};
@@ -1019,14 +1018,22 @@ void AutofillDownloadManager::InitActiveExperiments() {
variations::VariationsIdsProvider::GetInstance();
DCHECK(variations_ids_provider != nullptr);
- delete active_experiments_;
- active_experiments_ = new std::vector<variations::VariationID>(
+ // TODO(crbug.com/1331322): Retire the hardcoded GWS ID ranges and only read
+ // the finch parameter.
+ base::flat_set<int> active_experiments(
variations_ids_provider->GetVariationsVector(
{variations::GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
variations::GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY}));
- base::EraseIf(*active_experiments_, [](variations::VariationID id) {
- return !IsAutofillExperimentId(id);
- });
+ base::EraseIf(active_experiments, base::not_fn(&IsAutofillExperimentId));
+ if (base::FeatureList::IsEnabled(
+ autofill::features::kAutofillServerBehaviors)) {
+ active_experiments.insert(
+ autofill::features::kAutofillServerBehaviorsParam.Get());
+ }
+
+ delete active_experiments_;
+ active_experiments_ = new std::vector<int>(active_experiments.begin(),
+ active_experiments.end());
std::sort(active_experiments_->begin(), active_experiments_->end());
}
diff --git a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc
index b1608590e29..9f53d43dc35 100644
--- a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc
@@ -57,11 +57,12 @@
#include "third_party/re2/src/re2/re2.h"
#include "url/third_party/mozilla/url_parse.h"
-using base::UTF8ToUTF16;
-using net::test_server::BasicHttpResponse;
-using net::test_server::EmbeddedTestServer;
-using net::test_server::HttpRequest;
-using net::test_server::HttpResponse;
+using ::base::UTF8ToUTF16;
+using ::net::test_server::BasicHttpResponse;
+using ::net::test_server::EmbeddedTestServer;
+using ::net::test_server::HttpRequest;
+using ::net::test_server::HttpResponse;
+using ::testing::ElementsAre;
namespace autofill {
using mojom::SubmissionSource;
@@ -1728,6 +1729,51 @@ TEST_P(AutofillQueryTest, SendsExperiment) {
}
}
+TEST_P(AutofillQueryTest, SendsExperimentFromFeatureParam) {
+ FormFieldData field;
+ field.label = u"First Name:";
+ field.name = u"firstname";
+ field.form_control_type = "text";
+
+ FormData form;
+ form.fields.push_back(field);
+
+ std::vector<std::unique_ptr<FormStructure>> form_structures;
+ form_structures.push_back(std::make_unique<FormStructure>(form));
+
+ {
+ SCOPED_TRACE("Query without experiment");
+ call_count_ = 0;
+ payloads_.clear();
+ ASSERT_TRUE(SendQueryRequest(form_structures));
+ EXPECT_EQ(1u, call_count_);
+
+ ASSERT_EQ(1u, payloads_.size());
+ AutofillPageQueryRequest query_contents;
+ ASSERT_TRUE(query_contents.ParseFromString(payloads_[0]));
+ EXPECT_THAT(query_contents.experiments(), ElementsAre());
+ }
+
+ {
+ SCOPED_TRACE("Query with experiment");
+
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeatureWithParameters(
+ features::kAutofillServerBehaviors,
+ {{"server_prediction_source", "19890601"}});
+
+ call_count_ = 0;
+ payloads_.clear();
+ ASSERT_TRUE(SendQueryRequest(form_structures));
+ EXPECT_EQ(1u, call_count_);
+
+ ASSERT_EQ(1u, payloads_.size());
+ AutofillPageQueryRequest query_contents;
+ ASSERT_TRUE(query_contents.ParseFromString(payloads_[0]));
+ EXPECT_THAT(query_contents.experiments(), ElementsAre(19890601));
+ }
+}
+
TEST_P(AutofillQueryTest, ExpiredCacheInResponse) {
FormFieldData field;
field.label = u"First Name:";
diff --git a/chromium/components/autofill/core/browser/autofill_driver.h b/chromium/components/autofill/core/browser/autofill_driver.h
index fbbcca166c8..59815c14e1a 100644
--- a/chromium/components/autofill/core/browser/autofill_driver.h
+++ b/chromium/components/autofill/core/browser/autofill_driver.h
@@ -29,12 +29,36 @@ namespace autofill {
class FormStructure;
-// Interface that allows Autofill core code to interact with its driver (i.e.,
-// obtain information from it and give information to it). A concrete
-// implementation must be provided by the driver.
+// AutofillDriver is Autofill's lowest-level abstraction of a frame that is
+// shared among all platforms.
+//
+// Most notably, it is a gateway for all communication from the browser code to
+// the DOM, or more precisely, to the AutofillAgent (which are different classes
+// of the same name in non-iOS vs iOS).
+//
+// The reverse communication, from the AutofillAgent to the browser code, goes
+// through mojom::AutofillDriver on non-iOS, and directly to AutofillManager on
+// iOS.
+//
+// An AutofillDriver corresponds to a frame, rather than a document, in the
+// sense that it may survive navigations.
+//
+// AutofillDriver has two implementations:
+// - AutofillDriverIOS for iOS,
+// - ContentAutofillDriver for all other platforms.
+//
+// An AutofillDriver's lifetime should be contained by the associated frame.
+//
+// AutofillDriverIOS is co-owned by AutofillDriverIOSWebFrame, which itself is
+// owned by the WebFrame, and the iOS-specific AutofillAgent, which extends the
+// driver's lifetime beyond the WebFrame's (crbug.com/892612).
+//
+// ContentAutofillDriver is owned by ContentAutofillDriverFactory, which ensures
+// that the associated RenderFrameHost's lifetime contains the driver's
+// lifetime.
class AutofillDriver {
public:
- virtual ~AutofillDriver() {}
+ virtual ~AutofillDriver() = default;
// Returns whether the user is currently operating in an incognito context.
virtual bool IsIncognito() const = 0;
@@ -92,11 +116,6 @@ class AutofillDriver {
const url::Origin& triggered_origin,
const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) = 0;
- // Pass the form structures to the password manager to choose correct username
- // and to the password generation manager to detect account creation forms.
- virtual void PropagateAutofillPredictions(
- const std::vector<autofill::FormStructure*>& forms) = 0;
-
// Forwards parsed |forms| to the embedder.
virtual void HandleParsedForms(const std::vector<const FormData*>& forms) = 0;
diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc
index ae24637c54e..710f91c976a 100644
--- a/chromium/components/autofill/core/browser/autofill_experiments.cc
+++ b/chromium/components/autofill/core/browser/autofill_experiments.cc
@@ -4,7 +4,9 @@
#include "components/autofill/core/browser/autofill_experiments.h"
+#include <algorithm>
#include <string>
+#include <vector>
#include "base/command_line.h"
#include "base/feature_list.h"
@@ -55,6 +57,16 @@ void LogCardUploadEnabled(LogManager* log_manager) {
<< LogMessage::kCreditCardUploadEnabled << CTag{};
}
}
+
+// Given an email account domain, returns the contents before the first dot.
+std::string GetFirstSegmentFromDomain(const std::string& domain) {
+ size_t separator_pos = domain.find('.');
+ if (separator_pos != domain.npos)
+ return domain.substr(0, separator_pos);
+
+ NOTREACHED() << "'.' not found in email domain: " << domain;
+ return std::string();
+}
} // namespace
// The list of countries for which the credit card upload save feature is fully
@@ -67,6 +79,31 @@ const char* const kAutofillUpstreamLaunchedCountries[] = {
"NC", "NL", "NO", "NZ", "PA", "PL", "PR", "PT", "RE", "RO", "RU", "SE",
"SG", "SI", "SK", "TH", "TR", "TT", "TW", "UA", "US", "VI", "VN", "ZA"};
+// The list of supported additional email domains for credit card upload if the
+// AutofillUpstreamAllowAdditionalEmailDomains flag is enabled. Specifically
+// contains only the first part of the domain, so example.com, example.co.uk,
+// example.fr, etc., are all allowed for "example".
+const char* const kSupportedAdditionalDomains[] = {"aol",
+ "att",
+ "btinternet",
+ "comcast",
+ "gmx",
+ "hotmail",
+ "icloud",
+ /*libero.it*/ "libero",
+ "live",
+ "me",
+ "msn",
+ /*orange.fr*/ "orange",
+ "outlook",
+ "sbcglobal",
+ /*seznam.cz*/ "seznam",
+ "sky",
+ "verizon",
+ /*wp.pl*/ "wp",
+ "yahoo",
+ "ymail"};
+
bool IsCreditCardUploadEnabled(const PrefService* pref_service,
const syncer::SyncService* sync_service,
const std::string& user_email,
@@ -153,7 +190,7 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service,
return false;
}
- // Check that the user is logged into a supported domain.
+ // Check that the user's account email address is known.
if (user_email.empty()) {
AutofillMetrics::LogCardUploadEnabledMetric(
AutofillMetrics::CardUploadEnabledMetric::EMAIL_EMPTY, sync_state);
@@ -161,17 +198,30 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service,
return false;
}
+ // Check that the user is logged into a supported domain.
std::string domain = gaia::ExtractDomainName(user_email);
- // If the "allow all email domains" flag is off, restrict credit card upload
- // only to Google Accounts with @googlemail, @gmail, @google, or @chromium
- // domains.
+ std::string domain_first_segment = GetFirstSegmentFromDomain(domain);
+ // If the flag to allow all email domains is enabled, any domain is accepted.
+ bool all_domains_supported = base::FeatureList::IsEnabled(
+ features::kAutofillUpstreamAllowAllEmailDomains);
+ // If the flag to allow select email domains is enabled, domains from popular
+ // account providers are accepted.
+ bool using_supported_additional_domain =
+ base::FeatureList::IsEnabled(
+ features::kAutofillUpstreamAllowAdditionalEmailDomains) &&
+ std::find(std::begin(kSupportedAdditionalDomains),
+ std::end(kSupportedAdditionalDomains),
+ domain_first_segment) != std::end(kSupportedAdditionalDomains);
+ // Otherwise, restrict credit card upload only to Google Accounts with
+ // @googlemail, @gmail, @google, or @chromium domains.
// example.com is on the list because ChromeOS tests rely on using this. That
// should be fine, since example.com is an IANA reserved domain.
- if (!base::FeatureList::IsEnabled(
- features::kAutofillUpstreamAllowAllEmailDomains) &&
- !(domain == "googlemail.com" || domain == "gmail.com" ||
- domain == "google.com" || domain == "chromium.org" ||
- domain == "example.com")) {
+ bool using_google_domain = domain == "googlemail.com" ||
+ domain == "gmail.com" || domain == "google.com" ||
+ domain == "chromium.org" ||
+ domain == "example.com";
+ if (!all_domains_supported && !using_supported_additional_domain &&
+ !using_google_domain) {
AutofillMetrics::LogCardUploadEnabledMetric(
AutofillMetrics::CardUploadEnabledMetric::EMAIL_DOMAIN_NOT_SUPPORTED,
sync_state);
@@ -226,7 +276,7 @@ bool IsCreditCardMigrationEnabled(PersonalDataManager* personal_data_manager,
return false;
}
- if (!autofill::payments::HasGooglePaymentsAccount(personal_data_manager))
+ if (!payments::HasGooglePaymentsAccount(personal_data_manager))
return false;
switch (personal_data_manager->GetSyncSigninState()) {
@@ -255,13 +305,9 @@ bool IsCreditCardFidoAuthenticationEnabled() {
if (base::FeatureList::IsEnabled(features::kAutofillCreditCardAuthentication))
return true;
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
- // Better Auth project is fully launched on Windows and Clank.
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC)
+ // Better Auth project is fully launched on Windows, Android, and the Mac.
return true;
-#elif BUILDFLAG(IS_MAC)
- // Mac OS X 10.12 and earlier has a OS-level bug that causes crashes,
- // therefore only enable for 10.13+.
- return base::mac::IsAtLeastOS10_13();
#else
return false;
#endif
diff --git a/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc b/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc
index 7b8a706b05c..78b8e2e3a2e 100644
--- a/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc
@@ -235,7 +235,8 @@ TEST_F(
AutofillMetrics::CardUploadEnabledMetric::ENABLED_FOR_COUNTRY, 1);
}
-TEST_F(AutofillExperimentsTest, IsCardUploadEnabled_UserEmailWithGoogleDomain) {
+TEST_F(AutofillExperimentsTest,
+ IsCardUploadEnabled_UserEmailWithGoogleDomain_IsAllowed) {
EXPECT_TRUE(IsCreditCardUploadEnabled(
"john.smith@gmail.com",
AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
@@ -257,18 +258,45 @@ TEST_F(AutofillExperimentsTest, IsCardUploadEnabled_UserEmailWithGoogleDomain) {
}
TEST_F(AutofillExperimentsTest,
- IsCardUploadEnabled_UserEmailWithNonGoogleDomain) {
+ IsCardUploadEnabled_UserEmailWithSupportedAdditionalDomain_IsAllowed) {
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillUpstreamAllowAdditionalEmailDomains);
+ EXPECT_TRUE(IsCreditCardUploadEnabled(
+ "cool.user@hotmail.com",
+ AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
+ EXPECT_TRUE(IsCreditCardUploadEnabled(
+ "cool.british.user@hotmail.co.uk",
+ AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
+ EXPECT_TRUE(IsCreditCardUploadEnabled(
+ "telecom.user@verizon.net",
+ AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
+ EXPECT_TRUE(IsCreditCardUploadEnabled(
+ "youve.got.mail@aol.com",
+ AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.CardUploadEnabled",
+ AutofillMetrics::CardUploadEnabledMetric::ENABLED_FOR_COUNTRY, 4);
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.CardUploadEnabled.SignedInAndSyncFeatureEnabled",
+ AutofillMetrics::CardUploadEnabledMetric::ENABLED_FOR_COUNTRY, 4);
+}
+
+TEST_F(
+ AutofillExperimentsTest,
+ IsCardUploadEnabled_UserEmailWithSupportedAdditionalDomain_NotAllowedIfFlagOff) {
+ scoped_feature_list_.InitAndDisableFeature(
+ features::kAutofillUpstreamAllowAdditionalEmailDomains);
EXPECT_FALSE(IsCreditCardUploadEnabled(
"cool.user@hotmail.com",
AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
EXPECT_FALSE(IsCreditCardUploadEnabled(
- "john.smith@johnsmith.com",
+ "cool.british.user@hotmail.co.uk",
AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
EXPECT_FALSE(IsCreditCardUploadEnabled(
- "fake.googler@google.net",
+ "telecom.user@verizon.net",
AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
EXPECT_FALSE(IsCreditCardUploadEnabled(
- "fake.committer@chromium.com",
+ "youve.got.mail@aol.com",
AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
histogram_tester.ExpectUniqueSample(
"Autofill.CardUploadEnabled",
@@ -279,14 +307,14 @@ TEST_F(AutofillExperimentsTest,
}
TEST_F(AutofillExperimentsTest,
- IsCardUploadEnabled_UserEmailWithNonGoogleDomainIfExperimentEnabled) {
+ IsCardUploadEnabled_UserEmailWithOtherDomain_IsAllowed) {
scoped_feature_list_.InitAndEnableFeature(
features::kAutofillUpstreamAllowAllEmailDomains);
EXPECT_TRUE(IsCreditCardUploadEnabled(
- "cool.user@hotmail.com",
+ "grad.student@university.edu",
AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
EXPECT_TRUE(IsCreditCardUploadEnabled(
- "john.smith@johnsmith.com",
+ "some.ceo@bigcorporation.com",
AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
EXPECT_TRUE(IsCreditCardUploadEnabled(
"fake.googler@google.net",
@@ -302,4 +330,30 @@ TEST_F(AutofillExperimentsTest,
AutofillMetrics::CardUploadEnabledMetric::ENABLED_FOR_COUNTRY, 4);
}
+TEST_F(AutofillExperimentsTest,
+ IsCardUploadEnabled_UserEmailWithOtherDomain_NotAllowedIfFlagOff) {
+ scoped_feature_list_.InitWithFeatures(
+ /*enabled_features=*/{features::
+ kAutofillUpstreamAllowAdditionalEmailDomains},
+ /*disabled_features=*/{features::kAutofillUpstreamAllowAllEmailDomains});
+ EXPECT_FALSE(IsCreditCardUploadEnabled(
+ "grad.student@university.edu",
+ AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
+ EXPECT_FALSE(IsCreditCardUploadEnabled(
+ "some.ceo@bigcorporation.com",
+ AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
+ EXPECT_FALSE(IsCreditCardUploadEnabled(
+ "fake.googler@google.net",
+ AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
+ EXPECT_FALSE(IsCreditCardUploadEnabled(
+ "fake.committer@chromium.com",
+ AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled));
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.CardUploadEnabled",
+ AutofillMetrics::CardUploadEnabledMetric::EMAIL_DOMAIN_NOT_SUPPORTED, 4);
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.CardUploadEnabled.SignedInAndSyncFeatureEnabled",
+ AutofillMetrics::CardUploadEnabledMetric::EMAIL_DOMAIN_NOT_SUPPORTED, 4);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
index 930ea6fdd6a..1423b9902fe 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
@@ -29,6 +29,7 @@
#include "components/autofill/core/common/autofill_util.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/strings/grit/components_strings.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
#include "ui/accessibility/platform/ax_platform_node.h"
#include "ui/base/l10n/l10n_util.h"
@@ -153,7 +154,7 @@ void AutofillExternalDelegate::OnSuggestionsReturned(
// Send to display.
if (query_field_.is_focusable && driver_->CanShowAutofillUi()) {
- autofill::AutofillClient::PopupOpenArgs open_args(
+ AutofillClient::PopupOpenArgs open_args(
element_bounds_, query_field_.text_direction, suggestions,
AutoselectFirstSuggestion(autoselect_first_suggestion), popup_type_);
manager_->client()->ShowAutofillPopup(open_args, GetWeakPtr());
@@ -218,7 +219,8 @@ void AutofillExternalDelegate::DidSelectSuggestion(
// Only preview the data if it is a profile or a virtual card.
if (frontend_id > 0) {
FillAutofillFormData(frontend_id, true);
- } else if (frontend_id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY) {
+ } else if (frontend_id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY ||
+ frontend_id == POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY) {
driver_->RendererShouldPreviewFieldWithValue(query_field_.global_id(),
value);
} else if (frontend_id == POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY) {
@@ -231,7 +233,7 @@ void AutofillExternalDelegate::DidSelectSuggestion(
void AutofillExternalDelegate::DidAcceptSuggestion(
const std::u16string& value,
int frontend_id,
- const std::string& backend_id,
+ const Suggestion::Payload& payload,
int position) {
if (frontend_id == POPUP_ITEM_ID_AUTOFILL_OPTIONS) {
// User selected 'Autofill Options'.
@@ -248,11 +250,16 @@ void AutofillExternalDelegate::DidAcceptSuggestion(
} else if (frontend_id == POPUP_ITEM_ID_DATALIST_ENTRY) {
driver_->RendererShouldAcceptDataListSuggestion(query_field_.global_id(),
value);
- } else if (frontend_id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY) {
- // User selected an Autocomplete, so we fill directly.
+ } else if (frontend_id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY ||
+ frontend_id == POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY) {
+ // User selected an Autocomplete or Merchant Promo Code field, so we fill
+ // directly.
driver_->RendererShouldFillFieldWithValue(query_field_.global_id(), value);
- AutofillMetrics::LogAutocompleteSuggestionAcceptedIndex(position);
- manager_->OnSingleFieldSuggestionSelected(value);
+
+ if (frontend_id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY)
+ AutofillMetrics::LogAutocompleteSuggestionAcceptedIndex(position);
+
+ manager_->OnSingleFieldSuggestionSelected(value, frontend_id);
} else if (frontend_id == POPUP_ITEM_ID_SCAN_CREDIT_CARD) {
manager_->client()->ScanCreditCard(base::BindOnce(
&AutofillExternalDelegate::OnCreditCardScanned, GetWeakPtr()));
@@ -269,10 +276,16 @@ void AutofillExternalDelegate::DidAcceptSuggestion(
} else if (frontend_id == POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY) {
// There can be multiple virtual credit cards that all rely on
// POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY as a frontend_id. In this case,
- // the backend_id identifies the actually chosen credit card.
+ // the payload contains the backend id, which is a GUID that identifies the
+ // actually chosen credit card.
+ DCHECK(absl::holds_alternative<std::string>(payload));
manager_->FillOrPreviewVirtualCardInformation(
- mojom::RendererFormDataAction::kFill, backend_id, query_id_,
- query_form_, query_field_);
+ mojom::RendererFormDataAction::kFill, absl::get<std::string>(payload),
+ query_id_, query_form_, query_field_);
+ } else if (frontend_id == POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS) {
+ DCHECK(absl::holds_alternative<GURL>(payload));
+ manager_->OnSeePromoCodeOfferDetailsSelected(absl::get<GURL>(payload),
+ value, frontend_id);
} else {
if (frontend_id > 0) { // Denotes an Autofill suggestion.
AutofillMetrics::LogAutofillSuggestionAcceptedIndex(
@@ -310,7 +323,8 @@ bool AutofillExternalDelegate::RemoveSuggestion(const std::u16string& value,
return manager_->RemoveAutofillProfileOrCreditCard(frontend_id);
if (frontend_id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY) {
- manager_->RemoveCurrentSingleFieldSuggestion(query_field_.name, value);
+ manager_->RemoveCurrentSingleFieldSuggestion(query_field_.name, value,
+ frontend_id);
return true;
}
@@ -392,7 +406,7 @@ void AutofillExternalDelegate::ApplyAutofillOptions(
// yet.
// TODO(crbug.com/1274134): Clean up once improvements are launched.
if (base::FeatureList::IsEnabled(
- autofill::features::kAutofillVisualImprovementsForSuggestionUi) &&
+ features::kAutofillVisualImprovementsForSuggestionUi) &&
!suggestions->empty()) {
suggestions->push_back(Suggestion());
suggestions->back().frontend_id = POPUP_ITEM_ID_SEPARATOR;
@@ -451,7 +465,7 @@ void AutofillExternalDelegate::InsertDataListValues(
data_list_values_.end());
base::EraseIf(*suggestions, [&data_list_set](const Suggestion& suggestion) {
return suggestion.frontend_id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY &&
- base::Contains(data_list_set, suggestion.value);
+ base::Contains(data_list_set, suggestion.main_text.value);
});
#if !BUILDFLAG(IS_ANDROID)
@@ -467,7 +481,8 @@ void AutofillExternalDelegate::InsertDataListValues(
suggestions->insert(suggestions->begin(), data_list_values_.size(),
Suggestion());
for (size_t i = 0; i < data_list_values_.size(); i++) {
- (*suggestions)[i].value = data_list_values_[i];
+ (*suggestions)[i].main_text = Suggestion::Text(
+ data_list_values_[i], Suggestion::Text::IsPrimary(true));
(*suggestions)[i].label = data_list_labels_[i];
(*suggestions)[i].frontend_id = POPUP_ITEM_ID_DATALIST_ENTRY;
}
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.h b/chromium/components/autofill/core/browser/autofill_external_delegate.h
index 268b63e84d4..c6af1bb013f 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate.h
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.h
@@ -51,7 +51,7 @@ class AutofillExternalDelegate : public AutofillPopupDelegate {
const std::string& backend_id) override;
void DidAcceptSuggestion(const std::u16string& value,
int frontend_id,
- const std::string& backend_id,
+ const Suggestion::Payload& payload,
int position) override;
bool GetDeletionConfirmationText(const std::u16string& value,
int frontend_id,
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index 648401c7bdc..3a3a5a102a8 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -22,6 +22,7 @@
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/test_autofill_driver.h"
#include "components/autofill/core/browser/ui/popup_item_ids.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
#include "components/autofill/core/browser/ui/suggestion_test_helpers.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_payments_features.h"
@@ -92,6 +93,10 @@ class MockAutofillClient : public TestAutofillClient {
(override));
MOCK_METHOD(void, HideAutofillPopup, (PopupHidingReason), (override));
MOCK_METHOD(void, ExecuteCommand, (int), (override));
+ MOCK_METHOD(void,
+ OpenPromoCodeOfferDetailsURL,
+ (const GURL& url),
+ (override));
// Mock the client query ID check.
bool IsQueryIDRelevant(int query_id) { return query_id == kRecentQueryId; }
@@ -100,10 +105,10 @@ class MockAutofillClient : public TestAutofillClient {
class MockBrowserAutofillManager : public BrowserAutofillManager {
public:
MockBrowserAutofillManager(AutofillDriver* driver, MockAutofillClient* client)
- // Force to use the constructor designated for unit test.
: BrowserAutofillManager(driver,
client,
- client->GetPersonalDataManager()) {}
+ "en-US",
+ EnableDownloadManager(false)) {}
MockBrowserAutofillManager(const MockBrowserAutofillManager&) = delete;
MockBrowserAutofillManager& operator=(const MockBrowserAutofillManager&) =
delete;
@@ -202,7 +207,7 @@ class AutofillExternalDelegateUnitTest : public testing::Test {
void IssueOnSuggestionsReturned(int query_id) {
std::vector<Suggestion> suggestions;
- suggestions.push_back(Suggestion());
+ suggestions.emplace_back();
suggestions[0].frontend_id = kAutofillProfileId;
external_delegate_->OnSuggestionsReturned(
query_id, suggestions, /*autoselect_first_suggestion=*/false);
@@ -243,7 +248,7 @@ TEST_F(AutofillExternalDelegateUnitTest, TestExternalDelegateVirtualCalls) {
// This should call ShowAutofillPopup.
std::vector<Suggestion> autofill_item;
- autofill_item.push_back(Suggestion());
+ autofill_item.emplace_back();
autofill_item[0].frontend_id = kAutofillProfileId;
external_delegate_->OnSuggestionsReturned(
kRecentQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
@@ -259,9 +264,9 @@ TEST_F(AutofillExternalDelegateUnitTest, TestExternalDelegateVirtualCalls) {
// This should trigger a call to hide the popup since we've selected an
// option.
- external_delegate_->DidAcceptSuggestion(autofill_item[0].value,
+ external_delegate_->DidAcceptSuggestion(autofill_item[0].main_text.value,
autofill_item[0].frontend_id,
- autofill_item[0].backend_id, 0);
+ autofill_item[0].payload, 0);
}
// Test that our external delegate does not add the signin promo and its
@@ -284,7 +289,7 @@ TEST_F(AutofillExternalDelegateUnitTest,
// This should call ShowAutofillPopup.
std::vector<Suggestion> autofill_item;
- autofill_item.push_back(Suggestion());
+ autofill_item.emplace_back();
autofill_item[0].frontend_id = kAutofillProfileId;
external_delegate_->OnSuggestionsReturned(
kRecentQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
@@ -302,9 +307,9 @@ TEST_F(AutofillExternalDelegateUnitTest,
// This should trigger a call to hide the popup since we've selected an
// option.
- external_delegate_->DidAcceptSuggestion(autofill_item[0].value,
+ external_delegate_->DidAcceptSuggestion(autofill_item[0].main_text.value,
autofill_item[0].frontend_id,
- autofill_item[0].backend_id, 0);
+ autofill_item[0].payload, 0);
}
// Test that our external delegate properly adds the signin promo and no
@@ -344,8 +349,8 @@ TEST_F(AutofillExternalDelegateUnitTest,
// This should trigger a call to start the signin flow and hide the popup
// since we've selected the sign-in promo option.
external_delegate_->DidAcceptSuggestion(
- std::u16string(), POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO, std::string(),
- 0);
+ std::u16string(), POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO,
+ Suggestion::Payload{}, 0);
}
// Test that data list elements for a node will appear in the Autofill popup.
@@ -353,7 +358,7 @@ TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateDataList) {
IssueOnQuery(kRecentQueryId);
std::vector<std::u16string> data_list_items;
- data_list_items.push_back(std::u16string());
+ data_list_items.emplace_back();
EXPECT_CALL(autofill_client_, UpdateAutofillPopupDataListValues(
data_list_items, data_list_items));
@@ -374,7 +379,7 @@ TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateDataList) {
// This should call ShowAutofillPopup.
std::vector<Suggestion> autofill_item;
- autofill_item.push_back(Suggestion());
+ autofill_item.emplace_back();
autofill_item[0].frontend_id = kAutofillProfileId;
external_delegate_->OnSuggestionsReturned(
kRecentQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
@@ -408,7 +413,7 @@ TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) {
// Make sure just setting the data list values doesn't cause the popup to
// appear.
std::vector<std::u16string> data_list_items;
- data_list_items.push_back(std::u16string());
+ data_list_items.emplace_back();
EXPECT_CALL(autofill_client_, UpdateAutofillPopupDataListValues(
data_list_items, data_list_items));
@@ -429,7 +434,7 @@ TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) {
// Ensure the popup is displayed.
std::vector<Suggestion> autofill_item;
- autofill_item.push_back(Suggestion());
+ autofill_item.emplace_back();
autofill_item[0].frontend_id = kAutofillProfileId;
external_delegate_->OnSuggestionsReturned(
kRecentQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
@@ -442,7 +447,7 @@ TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) {
external_delegate_->OnPopupShown();
// Update the current data list and ensure the popup is updated.
- data_list_items.push_back(std::u16string());
+ data_list_items.emplace_back();
// The enums must be cast to ints to prevent compile errors on linux_rel.
EXPECT_CALL(autofill_client_, UpdateAutofillPopupDataListValues(
@@ -480,8 +485,9 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutofillDatalistValues) {
// Have an Autofill item that is identical to one of the datalist entries.
std::vector<Suggestion> autofill_item;
- autofill_item.push_back(Suggestion());
- autofill_item[0].value = u"Rick";
+ autofill_item.emplace_back();
+ autofill_item[0].main_text =
+ Suggestion::Text(u"Rick", Suggestion::Text::IsPrimary(true));
autofill_item[0].label = u"Deckard";
autofill_item[0].frontend_id = kAutofillProfileId;
external_delegate_->OnSuggestionsReturned(
@@ -521,11 +527,13 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutocompleteDatalistValues) {
// Have an Autocomplete item that is identical to one of the datalist entries
// and one that is distinct.
std::vector<Suggestion> autocomplete_items;
- autocomplete_items.push_back(Suggestion());
- autocomplete_items[0].value = u"Rick";
+ autocomplete_items.emplace_back();
+ autocomplete_items[0].main_text =
+ Suggestion::Text(u"Rick", Suggestion::Text::IsPrimary(true));
autocomplete_items[0].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY;
- autocomplete_items.push_back(Suggestion());
- autocomplete_items[1].value = u"Cain";
+ autocomplete_items.emplace_back();
+ autocomplete_items[1].main_text =
+ Suggestion::Text(u"Cain", Suggestion::Text::IsPrimary(true));
autocomplete_items[1].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY;
external_delegate_->OnSuggestionsReturned(
kRecentQueryId, autocomplete_items,
@@ -547,7 +555,7 @@ TEST_F(AutofillExternalDelegateUnitTest, AutofillWarnings) {
// This should call ShowAutofillPopup.
std::vector<Suggestion> autofill_item;
- autofill_item.push_back(Suggestion());
+ autofill_item.emplace_back();
autofill_item[0].frontend_id =
POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
external_delegate_->OnSuggestionsReturned(
@@ -575,11 +583,12 @@ TEST_F(AutofillExternalDelegateUnitTest,
// This should call ShowAutofillPopup.
std::vector<Suggestion> suggestions;
- suggestions.push_back(Suggestion());
+ suggestions.emplace_back();
suggestions[0].frontend_id =
POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
- suggestions.push_back(Suggestion());
- suggestions[1].value = u"Rick";
+ suggestions.emplace_back();
+ suggestions[1].main_text =
+ Suggestion::Text(u"Rick", Suggestion::Text::IsPrimary(true));
suggestions[1].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY;
external_delegate_->OnSuggestionsReturned(
kRecentQueryId, suggestions, /*autoselect_first_suggestion=*/false);
@@ -606,8 +615,58 @@ TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateInvalidUniqueId) {
HideAutofillPopup(PopupHidingReason::kAcceptSuggestion));
EXPECT_CALL(*browser_autofill_manager_, FillOrPreviewForm(_, _, _, _, _))
.Times(0);
- external_delegate_->DidAcceptSuggestion(std::u16string(), -1, std::string(),
- 0);
+ external_delegate_->DidAcceptSuggestion(std::u16string(), -1,
+ Suggestion::Payload{}, 0);
+}
+
+// Test that the Autofill delegate still allows previewing and filling
+// specifically of the negative ID for POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY.
+TEST_F(AutofillExternalDelegateUnitTest,
+ ExternalDelegateFillsMerchantPromoCodeEntry) {
+ IssueOnQuery(kRecentQueryId);
+
+ AutofillClient::PopupOpenArgs open_args;
+ EXPECT_CALL(autofill_client_, ShowAutofillPopup)
+ .WillOnce(testing::SaveArg<0>(&open_args));
+
+ // This should call ShowAutofillPopup.
+ std::vector<Suggestion> suggestions;
+ suggestions.emplace_back();
+ std::u16string promo_code_value = u"PROMOCODE1234";
+ suggestions[0].main_text.value = promo_code_value;
+ suggestions[0].label = u"12.34% off your purchase!";
+ suggestions[0].frontend_id = POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY;
+ external_delegate_->OnSuggestionsReturned(
+ kRecentQueryId, suggestions, /*autoselect_first_suggestion=*/false);
+
+ // The enums must be cast to ints to prevent compile errors on linux_rel.
+ EXPECT_THAT(open_args.suggestions,
+ SuggestionVectorIdsAre(testing::ElementsAre(
+ static_cast<int>(POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY))));
+
+ EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1);
+ EXPECT_CALL(*autofill_driver_,
+ RendererShouldPreviewFieldWithValue(field_id_, promo_code_value));
+ external_delegate_->DidSelectSuggestion(
+ promo_code_value, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY, "");
+ EXPECT_CALL(autofill_client_,
+ HideAutofillPopup(PopupHidingReason::kAcceptSuggestion));
+ EXPECT_CALL(*autofill_driver_,
+ RendererShouldFillFieldWithValue(field_id_, promo_code_value));
+ external_delegate_->DidAcceptSuggestion(
+ promo_code_value, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY,
+ Suggestion::Payload{}, 0);
+}
+
+// Test that the Autofill delegate routes the merchant promo code suggestions
+// footer redirect logic correctly.
+TEST_F(AutofillExternalDelegateUnitTest,
+ ExternalDelegateMerchantPromoCodeSuggestionsFooter) {
+ const GURL gurl{"https://example.com/"};
+ absl::variant<std::string, GURL> payload(absl::in_place_type<GURL>, gurl);
+ EXPECT_CALL(autofill_client_, OpenPromoCodeOfferDetailsURL(gurl));
+ external_delegate_->DidAcceptSuggestion(
+ u"baz foo", POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS, payload, 0);
}
// Test that the ClearPreview call is only sent if the form was being previewed
@@ -665,7 +724,7 @@ TEST_F(AutofillExternalDelegateUnitTest,
EXPECT_CALL(*autofill_driver_,
RendererShouldAcceptDataListSuggestion(field_id_, dummy_string));
external_delegate_->DidAcceptSuggestion(
- dummy_string, POPUP_ITEM_ID_DATALIST_ENTRY, std::string(), 0);
+ dummy_string, POPUP_ITEM_ID_DATALIST_ENTRY, Suggestion::Payload{}, 0);
}
// Test that an accepted autofill suggestion will fill the form.
@@ -678,7 +737,7 @@ TEST_F(AutofillExternalDelegateUnitTest,
FillOrPreviewForm(mojom::RendererFormDataAction::kFill, _, _, _,
kAutofillProfileId));
external_delegate_->DidAcceptSuggestion(dummy_string, kAutofillProfileId,
- std::string(),
+ Suggestion::Payload{},
2); // Row 2
}
@@ -690,7 +749,7 @@ TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateClearForm) {
EXPECT_CALL(*autofill_driver_, RendererShouldClearFilledSection());
external_delegate_->DidAcceptSuggestion(
- std::u16string(), POPUP_ITEM_ID_CLEAR_FORM, std::string(), 0);
+ std::u16string(), POPUP_ITEM_ID_CLEAR_FORM, Suggestion::Payload{}, 0);
}
// Test that autofill client will scan a credit card after use accepted the
@@ -699,8 +758,9 @@ TEST_F(AutofillExternalDelegateUnitTest, ScanCreditCardMenuItem) {
EXPECT_CALL(autofill_client_, ScanCreditCard(_));
EXPECT_CALL(autofill_client_,
HideAutofillPopup(PopupHidingReason::kAcceptSuggestion));
- external_delegate_->DidAcceptSuggestion(
- std::u16string(), POPUP_ITEM_ID_SCAN_CREDIT_CARD, std::string(), 0);
+ external_delegate_->DidAcceptSuggestion(std::u16string(),
+ POPUP_ITEM_ID_SCAN_CREDIT_CARD,
+ Suggestion::Payload{}, 0);
}
TEST_F(AutofillExternalDelegateUnitTest, ScanCreditCardPromptMetricsTest) {
@@ -723,8 +783,9 @@ TEST_F(AutofillExternalDelegateUnitTest, ScanCreditCardPromptMetricsTest) {
IssueOnQuery(kRecentQueryId);
IssueOnSuggestionsReturned(kRecentQueryId);
external_delegate_->OnPopupShown();
- external_delegate_->DidAcceptSuggestion(
- std::u16string(), POPUP_ITEM_ID_SCAN_CREDIT_CARD, std::string(), 0);
+ external_delegate_->DidAcceptSuggestion(std::u16string(),
+ POPUP_ITEM_ID_SCAN_CREDIT_CARD,
+ Suggestion::Payload{}, 0);
histogram.ExpectBucketCount("Autofill.ScanCreditCardPrompt",
AutofillMetrics::SCAN_CARD_ITEM_SHOWN, 1);
histogram.ExpectBucketCount("Autofill.ScanCreditCardPrompt",
@@ -742,7 +803,7 @@ TEST_F(AutofillExternalDelegateUnitTest, ScanCreditCardPromptMetricsTest) {
IssueOnSuggestionsReturned(kRecentQueryId);
external_delegate_->OnPopupShown();
external_delegate_->DidAcceptSuggestion(
- std::u16string(), POPUP_ITEM_ID_CLEAR_FORM, std::string(), 0);
+ std::u16string(), POPUP_ITEM_ID_CLEAR_FORM, Suggestion::Payload{}, 0);
histogram.ExpectBucketCount("Autofill.ScanCreditCardPrompt",
AutofillMetrics::SCAN_CARD_ITEM_SHOWN, 1);
histogram.ExpectBucketCount("Autofill.ScanCreditCardPrompt",
@@ -771,8 +832,8 @@ TEST_F(AutofillExternalDelegateUnitTest, SigninPromoMenuItem) {
EXPECT_CALL(autofill_client_,
HideAutofillPopup(PopupHidingReason::kAcceptSuggestion));
external_delegate_->DidAcceptSuggestion(
- std::u16string(), POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO, std::string(),
- 0);
+ std::u16string(), POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO,
+ Suggestion::Payload{}, 0);
}
MATCHER_P(CreditCardMatches, card, "") {
@@ -799,7 +860,7 @@ TEST_F(AutofillExternalDelegateUnitTest, IgnoreAutocompleteOffForAutofill) {
external_delegate_->OnQuery(kRecentQueryId, form, field, gfx::RectF());
std::vector<Suggestion> autofill_items;
- autofill_items.push_back(Suggestion());
+ autofill_items.emplace_back();
autofill_items[0].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY;
// Ensure the popup tries to show itself, despite autocomplete="off".
@@ -812,19 +873,32 @@ TEST_F(AutofillExternalDelegateUnitTest, IgnoreAutocompleteOffForAutofill) {
TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateFillFieldWithValue) {
EXPECT_CALL(autofill_client_,
- HideAutofillPopup(PopupHidingReason::kAcceptSuggestion));
+ HideAutofillPopup(PopupHidingReason::kAcceptSuggestion))
+ .Times(2);
IssueOnQuery(456);
std::u16string dummy_string(u"baz foo");
EXPECT_CALL(*autofill_driver_,
RendererShouldFillFieldWithValue(field_id_, dummy_string));
EXPECT_CALL(*autofill_client_.GetMockAutocompleteHistoryManager(),
- OnSingleFieldSuggestionSelected(dummy_string))
+ OnSingleFieldSuggestionSelected(dummy_string,
+ POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY))
.Times(1);
base::HistogramTester histogram_tester;
external_delegate_->DidAcceptSuggestion(
dummy_string, POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY, std::string(), 0);
histogram_tester.ExpectUniqueSample(
"Autofill.SuggestionAcceptedIndex.Autocomplete", 0, 1);
+
+ // Test that merchant promo code offers get autofilled.
+ EXPECT_CALL(*autofill_driver_,
+ RendererShouldFillFieldWithValue(field_id_, dummy_string));
+ EXPECT_CALL(*autofill_client_.GetMockMerchantPromoCodeManager(),
+ OnSingleFieldSuggestionSelected(
+ dummy_string, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY))
+ .Times(1);
+ external_delegate_->DidAcceptSuggestion(
+ dummy_string, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY,
+ absl::variant<std::string, GURL>(), 0);
}
TEST_F(AutofillExternalDelegateUnitTest, ShouldShowGooglePayIcon) {
@@ -883,8 +957,10 @@ TEST_F(AutofillExternalDelegateUnitTest,
TEST_F(AutofillExternalDelegateUnitTest, ShouldUseNewSettingName) {
IssueOnQuery(kRecentQueryId);
- auto element_values = testing::ElementsAre(
- std::u16string(), l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE));
+ auto element_main_texts = testing::ElementsAre(
+ Suggestion::Text(std::u16string(), Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE),
+ Suggestion::Text::IsPrimary(true)));
AutofillClient::PopupOpenArgs open_args;
EXPECT_CALL(autofill_client_, ShowAutofillPopup)
.WillOnce(testing::SaveArg<0>(&open_args));
@@ -892,11 +968,13 @@ TEST_F(AutofillExternalDelegateUnitTest, ShouldUseNewSettingName) {
std::vector<Suggestion> autofill_item;
autofill_item.emplace_back();
autofill_item[0].frontend_id = kAutofillProfileId;
+ autofill_item[0].main_text.is_primary = Suggestion::Text::IsPrimary(true);
// This should call ShowAutofillPopup.
external_delegate_->OnSuggestionsReturned(
kRecentQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
- EXPECT_THAT(open_args.suggestions, SuggestionVectorValuesAre(element_values));
+ EXPECT_THAT(open_args.suggestions,
+ SuggestionVectorMainTextsAre(element_main_texts));
EXPECT_FALSE(open_args.autoselect_first_suggestion);
EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
}
@@ -909,8 +987,8 @@ TEST_F(AutofillExternalDelegateUnitTest, AcceptVirtualCardOptionItem) {
FillOrPreviewVirtualCardInformation(
mojom::RendererFormDataAction::kFill, _, _, _, _));
external_delegate_->DidAcceptSuggestion(
- std::u16string(), POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY, std::string(),
- 0);
+ std::u16string(), POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY,
+ Suggestion::Payload{}, 0);
}
TEST_F(AutofillExternalDelegateUnitTest, SelectVirtualCardOptionItem) {
@@ -928,10 +1006,13 @@ TEST_F(AutofillExternalDelegateCardsFromAccountTest,
ShouldShowCardsFromAccountOptionWithCards) {
IssueOnQuery(kRecentQueryId);
- auto element_values = testing::ElementsAre(
- std::u16string(),
- l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ACCOUNT_CARDS),
- l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE));
+ auto element_main_texts = testing::ElementsAre(
+ Suggestion::Text(std::u16string(), Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ACCOUNT_CARDS),
+ Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE),
+ Suggestion::Text::IsPrimary(true)));
AutofillClient::PopupOpenArgs open_args;
EXPECT_CALL(autofill_client_, ShowAutofillPopup)
.WillOnce(testing::SaveArg<0>(&open_args));
@@ -939,10 +1020,12 @@ TEST_F(AutofillExternalDelegateCardsFromAccountTest,
std::vector<Suggestion> autofill_item;
autofill_item.emplace_back();
autofill_item[0].frontend_id = kAutofillProfileId;
+ autofill_item[0].main_text.is_primary = Suggestion::Text::IsPrimary(true);
external_delegate_->OnSuggestionsReturned(
kRecentQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
- EXPECT_THAT(open_args.suggestions, SuggestionVectorValuesAre(element_values));
+ EXPECT_THAT(open_args.suggestions,
+ SuggestionVectorMainTextsAre(element_main_texts));
EXPECT_FALSE(open_args.autoselect_first_suggestion);
EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
}
@@ -954,8 +1037,9 @@ TEST_F(AutofillExternalDelegateCardsFromAccountTest,
ShouldShowCardsFromAccountOptionWithoutCards) {
IssueOnQuery(kRecentQueryId);
- auto element_values = testing::ElementsAre(
- l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ACCOUNT_CARDS));
+ auto element_main_texts = testing::ElementsAre(Suggestion::Text(
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ACCOUNT_CARDS),
+ Suggestion::Text::IsPrimary(true)));
AutofillClient::PopupOpenArgs open_args;
EXPECT_CALL(autofill_client_, ShowAutofillPopup)
.WillOnce(testing::SaveArg<0>(&open_args));
@@ -963,7 +1047,8 @@ TEST_F(AutofillExternalDelegateCardsFromAccountTest,
external_delegate_->OnSuggestionsReturned(
kRecentQueryId, std::vector<Suggestion>(),
/*autoselect_first_suggestion=*/false);
- EXPECT_THAT(open_args.suggestions, SuggestionVectorValuesAre(element_values));
+ EXPECT_THAT(open_args.suggestions,
+ SuggestionVectorMainTextsAre(element_main_texts));
EXPECT_FALSE(open_args.autoselect_first_suggestion);
EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
}
diff --git a/chromium/components/autofill/core/browser/autofill_field.cc b/chromium/components/autofill/core/browser/autofill_field.cc
index 5184b3bec32..d53d042400a 100644
--- a/chromium/components/autofill/core/browser/autofill_field.cc
+++ b/chromium/components/autofill/core/browser/autofill_field.cc
@@ -42,10 +42,16 @@ std::unique_ptr<AutofillField> AutofillField::CreateForPasswordManagerUpload(
}
ServerFieldType AutofillField::heuristic_type() const {
- ServerFieldType type = get_prediction(PredictionSource::kDefaultHeuristics);
+ return heuristic_type(GetActivePatternSource());
+}
+
+ServerFieldType AutofillField::heuristic_type(PatternSource s) const {
+ ServerFieldType type = local_type_predictions_[static_cast<size_t>(s)];
// `NO_SERVER_DATA` would mean that there is no heuristic type. Client code
// presumes there is a prediction, therefore we coalesce to `UNKNOWN_TYPE`.
- return type > 0 ? type : UNKNOWN_TYPE;
+ // Shadow predictions however are not used and we care whether the type is
+ // `UNKNOWN_TYPE` or whether we never ran the heuristics.
+ return (type > 0 || s != GetActivePatternSource()) ? type : UNKNOWN_TYPE;
}
ServerFieldType AutofillField::server_type() const {
@@ -60,17 +66,17 @@ bool AutofillField::server_type_prediction_is_override() const {
: server_predictions_[0].override();
}
-void AutofillField::set_heuristic_type(ServerFieldType type) {
- if (type >= 0 && type < MAX_VALID_FIELD_TYPE &&
- type != FIELD_WITH_DEFAULT_VALUE) {
- set_prediction(PredictionSource::kDefaultHeuristics, type);
- } else {
+void AutofillField::set_heuristic_type(PatternSource s, ServerFieldType type) {
+ if (type < 0 || type > MAX_VALID_FIELD_TYPE ||
+ type == FIELD_WITH_DEFAULT_VALUE) {
NOTREACHED();
// This case should not be reachable; but since this has potential
// implications on data uploaded to the server, better safe than sorry.
- set_prediction(PredictionSource::kDefaultHeuristics, UNKNOWN_TYPE);
+ type = UNKNOWN_TYPE;
}
- overall_type_ = AutofillType(NO_SERVER_DATA);
+ local_type_predictions_[static_cast<size_t>(s)] = type;
+ if (s == GetActivePatternSource())
+ overall_type_ = AutofillType(NO_SERVER_DATA);
}
void AutofillField::add_possible_types_validities(
@@ -133,8 +139,8 @@ AutofillType AutofillField::ComputedType() const {
return AutofillType(server_type());
}
- // If the explicit type is cc-exp and either the server or heuristics agree on
- // a 2 vs 4 digit specialization of cc-exp, use that specialization.
+ // If the explicit type is cc-exp and either the server or heuristics agree
+ // on a 2 vs 4 digit specialization of cc-exp, use that specialization.
if (html_type_ == HTML_TYPE_CREDIT_CARD_EXP) {
if (server_type() == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR ||
server_type() == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) {
@@ -195,8 +201,8 @@ AutofillType AutofillField::ComputedType() const {
(heuristic_type() == NAME_LAST_SECOND ||
heuristic_type() == NAME_LAST_FIRST));
- // For new address tokens the heuristic predictions get precedence over the
- // server predictions.
+ // For new address tokens the heuristic predictions get precedence over
+ // the server predictions.
// TODO(crbug.com/1098943): Remove feature check once launched.
believe_server =
believe_server &&
diff --git a/chromium/components/autofill/core/browser/autofill_field.h b/chromium/components/autofill/core/browser/autofill_field.h
index a7ca5dd4e63..920b0959882 100644
--- a/chromium/components/autofill/core/browser/autofill_field.h
+++ b/chromium/components/autofill/core/browser/autofill_field.h
@@ -15,7 +15,7 @@
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/form_parsing/field_candidates.h"
+#include "components/autofill/core/browser/form_parsing/regex_patterns.h"
#include "components/autofill/core/browser/proto/api_v1.pb.h"
#include "components/autofill/core/browser/proto/password_requirements.pb.h"
#include "components/autofill/core/common/form_field_data.h"
@@ -53,10 +53,8 @@ class AutofillField : public FormFieldData {
FieldSignature field_signature);
ServerFieldType heuristic_type() const;
+ ServerFieldType heuristic_type(PatternSource s) const;
ServerFieldType server_type() const;
- ServerFieldType get_prediction(PredictionSource s) const {
- return local_type_predictions_[static_cast<size_t>(s)];
- }
bool server_type_prediction_is_override() const;
const std::vector<
AutofillQueryResponse::FormSuggestion::FieldSuggestion::FieldPrediction>&
@@ -79,15 +77,12 @@ class AutofillField : public FormFieldData {
bool only_fill_when_focused() const { return only_fill_when_focused_; }
// Setters for the detected types.
- void set_heuristic_type(ServerFieldType type);
+ void set_heuristic_type(PatternSource s, ServerFieldType t);
void add_possible_types_validities(
const ServerFieldTypeValidityStateMap& possible_types_validities);
void set_server_predictions(
std::vector<AutofillQueryResponse::FormSuggestion::FieldSuggestion::
FieldPrediction> predictions);
- void set_prediction(PredictionSource s, ServerFieldType t) {
- local_type_predictions_[static_cast<size_t>(s)] = t;
- }
void set_may_use_prefilled_placeholder(bool may_use_prefilled_placeholder) {
may_use_prefilled_placeholder_ = may_use_prefilled_placeholder;
@@ -258,8 +253,7 @@ class AutofillField : public FormFieldData {
// Predictions which where calculated on the client. This is initialized to
// `NO_SERVER_DATA`, which means "NO_DATA", i.e. no classification was
// attempted.
- std::array<ServerFieldType,
- static_cast<size_t>(PredictionSource::kMaxValue) + 1>
+ std::array<ServerFieldType, static_cast<size_t>(PatternSource::kMaxValue) + 1>
local_type_predictions_;
// The type of the field. Overrides all other types (html_type_,
diff --git a/chromium/components/autofill/core/browser/autofill_form_test_utils.cc b/chromium/components/autofill/core/browser/autofill_form_test_utils.cc
index e151487a517..3acab8e9ef3 100644
--- a/chromium/components/autofill/core/browser/autofill_form_test_utils.cc
+++ b/chromium/components/autofill/core/browser/autofill_form_test_utils.cc
@@ -103,6 +103,7 @@ FormData GetFormData(const FormDataDescription& d) {
ff.unique_renderer_id =
dd.unique_renderer_id.value_or(MakeFieldRendererId());
ff.is_focusable = dd.is_focusable;
+ ff.is_visible = dd.is_visible;
if (!dd.autocomplete_attribute.empty())
ff.autocomplete_attribute = dd.autocomplete_attribute;
if (dd.label)
diff --git a/chromium/components/autofill/core/browser/autofill_form_test_utils.h b/chromium/components/autofill/core/browser/autofill_form_test_utils.h
index b8c68f858a2..8da4714b0f9 100644
--- a/chromium/components/autofill/core/browser/autofill_form_test_utils.h
+++ b/chromium/components/autofill/core/browser/autofill_form_test_utils.h
@@ -37,6 +37,7 @@ struct FieldDataDescription {
absl::optional<LocalFrameToken> host_frame;
absl::optional<FieldRendererId> unique_renderer_id;
bool is_focusable = true;
+ bool is_visible = true;
absl::optional<std::u16string> label;
absl::optional<std::u16string> name;
absl::optional<std::u16string> value;
diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc
index 5f8fc52fb25..858cec0d810 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager.cc
@@ -4,6 +4,7 @@
#include "components/autofill/core/browser/autofill_manager.h"
+#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/feature_list.h"
#include "components/autofill/core/browser/form_structure.h"
@@ -102,28 +103,15 @@ bool AutofillManager::IsRawMetadataUploadingEnabled(
channel == version_info::Channel::DEV;
}
-AutofillManager::AutofillManager(
- AutofillDriver* driver,
- AutofillClient* client,
- AutofillDownloadManagerState enable_download_manager)
- : AutofillManager(driver,
- client,
- enable_download_manager,
- client->GetChannel()) {
- DCHECK(driver);
- DCHECK(client);
-}
-
-AutofillManager::AutofillManager(
- AutofillDriver* driver,
- AutofillClient* client,
- AutofillDownloadManagerState enable_download_manager,
- version_info::Channel channel)
+AutofillManager::AutofillManager(AutofillDriver* driver,
+ AutofillClient* client,
+ version_info::Channel channel,
+ EnableDownloadManager enable_download_manager)
: driver_(driver),
client_(client),
log_manager_(client ? client->GetLogManager() : nullptr),
form_interactions_ukm_logger_(CreateFormInteractionsUkmLogger()) {
- if (enable_download_manager == ENABLE_AUTOFILL_DOWNLOAD_MANAGER) {
+ if (enable_download_manager) {
download_manager_ = std::make_unique<AutofillDownloadManager>(
driver, this, GetAPIKeyForUrl(channel),
AutofillDownloadManager::IsRawMetadataUploadingEnabled(
@@ -173,18 +161,19 @@ void AutofillManager::OnFormSubmitted(const FormData& form,
mojom::SubmissionSource source) {
if (IsValidFormData(form))
OnFormSubmittedImpl(form, known_success, source);
+
+ for (Observer& observer : observers_)
+ observer.OnFormSubmitted();
}
void AutofillManager::OnFormsSeen(
const std::vector<FormData>& updated_forms,
const std::vector<FormGlobalId>& removed_forms) {
- if (base::FeatureList::IsEnabled(features::kAutofillDisplaceRemovedForms)) {
- // Erase forms that have been removed from the DOM. This prevents
- // |form_structures_| from growing up its upper bound
- // kAutofillManagerMaxFormCacheSize.
- for (FormGlobalId removed_form : removed_forms)
- form_structures_.erase(removed_form);
- }
+ // Erase forms that have been removed from the DOM. This prevents
+ // |form_structures_| from growing up its upper bound
+ // kAutofillManagerMaxFormCacheSize.
+ for (FormGlobalId removed_form : removed_forms)
+ form_structures_.erase(removed_form);
if (!IsValidFormDataVector(updated_forms) || !driver_->RendererIsAvailable())
return;
@@ -290,6 +279,10 @@ void AutofillManager::OnTextFieldDidChange(const FormData& form,
return;
OnTextFieldDidChangeImpl(form, field, bounding_box, timestamp);
+
+ for (Observer& observer : observers_) {
+ observer.OnTextFieldDidChange();
+ }
}
void AutofillManager::OnTextFieldDidScroll(const FormData& form,
@@ -299,6 +292,9 @@ void AutofillManager::OnTextFieldDidScroll(const FormData& form,
return;
OnTextFieldDidScrollImpl(form, field, bounding_box);
+
+ for (Observer& observer : observers_)
+ observer.OnTextFieldDidScroll();
}
void AutofillManager::OnSelectControlDidChange(const FormData& form,
@@ -308,18 +304,23 @@ void AutofillManager::OnSelectControlDidChange(const FormData& form,
return;
OnSelectControlDidChangeImpl(form, field, bounding_box);
+
+ for (Observer& observer : observers_)
+ observer.OnSelectControlDidChange();
}
-void AutofillManager::OnAskForValuesToFill(int query_id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) {
+void AutofillManager::OnAskForValuesToFill(
+ int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) {
if (!IsValidFormData(form) || !IsValidFormFieldData(field))
return;
OnAskForValuesToFillImpl(query_id, form, field, bounding_box,
- autoselect_first_suggestion);
+ autoselect_first_suggestion, touch_to_fill_eligible);
}
void AutofillManager::OnFocusOnFormField(const FormData& form,
@@ -339,7 +340,6 @@ bool AutofillManager::GetCachedFormAndField(const FormData& form,
// Maybe find an existing FormStructure that corresponds to |form|.
FormStructure* cached_form = FindCachedFormByRendererId(form.global_id());
if (cached_form) {
- DCHECK(cached_form);
if (!CachedFormNeedsUpdate(form, *cached_form)) {
// There is no data to return if there are no auto-fillable fields.
if (!cached_form->autofill_count())
@@ -423,8 +423,9 @@ FormStructure* AutofillManager::ParseForm(const FormData& form,
form_structure->RetrieveFromCache(*cached_form,
/*should_keep_cached_value=*/true,
/*only_server_and_autofill_state=*/true);
- if (observer_for_testing_)
- observer_for_testing_->OnFormParsed();
+
+ for (Observer& observer : observers_)
+ observer.OnFormParsed();
if (form_structure.get()->value_from_dynamic_change_form())
value_from_dynamic_change_form_ = true;
@@ -505,7 +506,7 @@ void AutofillManager::OnLoadedServerPredictions(
// Forward form structures to the password generation manager to detect
// account creation forms.
- driver()->PropagateAutofillPredictions(queried_forms);
+ PropagateAutofillPredictions(queried_forms);
}
void AutofillManager::OnServerRequestError(
diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h
index 508238f2d57..9997b3401c0 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.h
+++ b/chromium/components/autofill/core/browser/autofill_manager.h
@@ -12,8 +12,10 @@
#include "base/cancelable_callback.h"
#include "base/memory/raw_ptr.h"
+#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
+#include "base/types/strong_alias.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_download_manager.h"
#include "components/autofill/core/browser/autofill_driver.h"
@@ -34,6 +36,8 @@ class RectF;
namespace autofill {
class AutofillField;
+class AutofillOfferManager;
+class CreditCardAccessManager;
struct FormData;
struct FormFieldData;
class FormStructure;
@@ -41,30 +45,37 @@ class LogManager;
// This class defines the interface should be implemented by autofill
// implementation in browser side to interact with AutofillDriver.
+//
+// AutofillManager has two implementations:
+// - AndroidAutofillManager for WebView and WebLayer,
+// - BrowserAutofillManager for Chrome.
+//
+// It is owned by the AutofillDriver.
class AutofillManager
: public AutofillDownloadManager::Observer,
public translate::TranslateDriver::LanguageDetectionObserver {
public:
- enum AutofillDownloadManagerState {
- ENABLE_AUTOFILL_DOWNLOAD_MANAGER,
- DISABLE_AUTOFILL_DOWNLOAD_MANAGER,
- };
-
// An observer class used by browsertests that gets notified whenever
// particular actions occur.
- class ObserverForTest {
+ class Observer : public base::CheckedObserver {
public:
- virtual void OnFormParsed() = 0;
+ virtual void OnFormParsed(){};
+
+ // See |AutofillManager::OnTextFieldDidChange|.
+ virtual void OnTextFieldDidChange(){};
+
+ // See |AutofillManager::OnTextFieldDidScroll|.
+ virtual void OnTextFieldDidScroll(){};
+
+ // See |AutofillManager::OnSelectControlDidChange|.
+ virtual void OnSelectControlDidChange(){};
+
+ // See |AutofillManager::OnFormSubmitted|.
+ virtual void OnFormSubmitted(){};
};
- // The factory method for the embedder to create the subclass of
- // AutofillManager in ContentAutofillDriver.
- using AutofillManagerFactoryCallback =
- base::RepeatingCallback<std::unique_ptr<AutofillManager>(
- AutofillDriver*,
- AutofillClient*,
- const std::string& app_locale,
- AutofillManager::AutofillDownloadManagerState)>;
+ using EnableDownloadManager =
+ base::StrongAlias<struct EnableDownloadManagerTag, bool>;
// Raw metadata uploading enabled iff this Chrome instance is on Canary or Dev
// channel.
@@ -93,6 +104,15 @@ class AutofillManager
return client_;
}
+ // May return nullptr.
+ virtual AutofillOfferManager* GetOfferManager() = 0;
+
+ // May return nullptr.
+ virtual CreditCardAccessManager* GetCreditCardAccessManager() = 0;
+
+ // Returns true only if the previewed form should be cleared.
+ virtual bool ShouldClearPreviewedForm() = 0;
+
// Invoked when the value of textfield is changed.
// |bounding_box| are viewport coordinates.
void OnTextFieldDidChange(const FormData& form,
@@ -115,11 +135,15 @@ class AutofillManager
// Invoked when the |form| needs to be autofilled, the |bounding_box| is
// a window relative value of |field|.
// |bounding_box| are viewport coordinates.
+ // |touch_to_fill_eligible| indicates if the Touch To Fill surface could be
+ // used for showing suggestion. Note that it doesn't guarantee the given form
+ // input field is eligible for autofilling.
void OnAskForValuesToFill(int query_id,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion);
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible);
// Invoked when |form|'s |field| has focus.
// |bounding_box| are viewport coordinates.
@@ -134,6 +158,15 @@ class AutofillManager
bool known_success,
mojom::SubmissionSource source);
+ virtual void FillCreditCardForm(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const CreditCard& credit_card,
+ const std::u16string& cvc) = 0;
+ virtual void FillProfileForm(const AutofillProfile& profile,
+ const FormData& form,
+ const FormFieldData& field) = 0;
+
// Invoked when changes of the forms have been detected: the forms in
// |updated_forms| are either new or have changed, and the forms in
// |removed_forms| have been removed from the DOM (but may be re-added to the
@@ -162,12 +195,23 @@ class AutofillManager
// Invoked when the options of a select element in the |form| changed.
virtual void SelectFieldOptionsDidChange(const FormData& form) = 0;
+ // Invoked after JavaScript set the value of |field| in |form|. Only called
+ // if |field| was in autofilled state. Note that from a renderer's
+ // perspective, modifying the value with JavaScript leads to a state where
+ // the field is not considered autofilled anymore. So this notification won't
+ // be sent again until the field gets autofilled again.
+ virtual void JavaScriptChangedAutofilledValue(
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) = 0;
+
// Invoked when the field type predictions are downloaded from the autofill
// server.
virtual void PropagateAutofillPredictions(
- content::RenderFrameHost* rfh,
const std::vector<FormStructure*>& forms) = 0;
+ virtual void ReportAutofillWebOTPMetrics(bool used_web_otp) = 0;
+
// Resets cache.
virtual void Reset();
@@ -197,8 +241,10 @@ class AutofillManager
// Returns the number of forms this Autofill handler is aware of.
size_t NumFormsDetected() const { return form_structures_.size(); }
- void SetEventObserverForTesting(ObserverForTest* observer) {
- observer_for_testing_ = observer;
+ void AddObserver(Observer* observer) { observers_.AddObserver(observer); }
+
+ void RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
}
// Returns the present form structures seen by Autofill handler.
@@ -233,6 +279,7 @@ class AutofillManager
int http_error) {
OnServerRequestError(form_signature, request_type, http_error);
}
+
#ifdef UNIT_TEST
// A public wrapper that calls |mutable_form_structures| for testing purposes
// only.
@@ -245,17 +292,13 @@ class AutofillManager
FormStructure* ParseFormForTest(const FormData& form) {
return ParseForm(form, nullptr);
}
-
#endif // UNIT_TEST
protected:
AutofillManager(AutofillDriver* driver,
AutofillClient* client,
- AutofillDownloadManagerState enable_download_manager);
- AutofillManager(AutofillDriver* driver,
- AutofillClient* client,
- AutofillDownloadManagerState enable_download_manager,
- version_info::Channel channel);
+ version_info::Channel channel,
+ EnableDownloadManager enable_download_manager);
LogManager* log_manager() { return log_manager_; }
@@ -284,11 +327,13 @@ class AutofillManager
const FormFieldData& field,
const gfx::RectF& bounding_box) = 0;
- virtual void OnAskForValuesToFillImpl(int query_id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion) = 0;
+ virtual void OnAskForValuesToFillImpl(
+ int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) = 0;
virtual void OnFocusOnFormFieldImpl(const FormData& form,
const FormFieldData& field,
@@ -387,8 +432,8 @@ class AutofillManager
std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger>
form_interactions_ukm_logger_;
- // Will be not null only for |SaveCardBubbleViewsFullFormBrowserTest|.
- raw_ptr<ObserverForTest> observer_for_testing_ = nullptr;
+ // Observers that listen to updates of this instance.
+ base::ObserverList<Observer> observers_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
index a112e86d294..dd63441f56a 100644
--- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -18,6 +18,7 @@
#include "components/autofill/core/browser/test_autofill_driver.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_features.h"
+#include "components/autofill/core/common/autofill_tick_clock.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -50,7 +51,30 @@ class MockAutofillDriver : public TestAutofillDriver {
class MockAutofillManager : public AutofillManager {
public:
MockAutofillManager(AutofillDriver* driver, AutofillClient* client)
- : AutofillManager(driver, client, DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {}
+ : AutofillManager(driver,
+ client,
+ client->GetChannel(),
+ EnableDownloadManager(false)) {}
+ MOCK_METHOD(bool, ShouldClearPreviewedForm, (), (override));
+ MOCK_METHOD(AutofillOfferManager*, GetOfferManager, (), (override));
+ MOCK_METHOD(CreditCardAccessManager*,
+ GetCreditCardAccessManager,
+ (),
+ (override));
+ MOCK_METHOD(void,
+ FillCreditCardForm,
+ (int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const CreditCard& credit_card,
+ const std::u16string& cvc),
+ (override));
+ MOCK_METHOD(void,
+ FillProfileForm,
+ (const autofill::AutofillProfile& profile,
+ const FormData& form,
+ const FormFieldData& field),
+ (override));
MOCK_METHOD(void,
OnFocusNoLongerOnForm,
(bool had_interacted_form),
@@ -67,9 +91,14 @@ class MockAutofillManager : public AutofillManager {
(const FormData& form),
(override));
MOCK_METHOD(void,
+ JavaScriptChangedAutofilledValue,
+ (const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value),
+ (override));
+ MOCK_METHOD(void,
PropagateAutofillPredictions,
- (content::RenderFrameHost * rfh,
- const std::vector<FormStructure*>& forms),
+ (const std::vector<FormStructure*>& forms),
(override));
MOCK_METHOD(void,
OnFormSubmittedImpl,
@@ -96,7 +125,8 @@ class MockAutofillManager : public AutofillManager {
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
- bool autoselect_first_suggestion),
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible),
(override));
MOCK_METHOD(void,
OnFocusOnFormFieldImpl,
@@ -123,6 +153,28 @@ class MockAutofillManager : public AutofillManager {
OnAfterProcessParsedForms,
(const DenseSet<FormType>& form_types),
(override));
+ MOCK_METHOD(void,
+ ReportAutofillWebOTPMetrics,
+ (bool used_web_otp),
+ (override));
+};
+
+class MockAutofillObserver : public AutofillManager::Observer {
+ public:
+ MockAutofillObserver() = default;
+ MockAutofillObserver(const MockAutofillObserver&) = delete;
+ MockAutofillObserver& operator=(const MockAutofillObserver&) = delete;
+ ~MockAutofillObserver() override = default;
+
+ MOCK_METHOD(void, OnFormParsed, (), (override));
+
+ MOCK_METHOD(void, OnTextFieldDidChange, (), (override));
+
+ MOCK_METHOD(void, OnTextFieldDidScroll, (), (override));
+
+ MOCK_METHOD(void, OnSelectControlDidChange, (), (override));
+
+ MOCK_METHOD(void, OnFormSubmitted, (), (override));
};
// Creates a vector of test forms which differ in their FormGlobalIds
@@ -202,32 +254,20 @@ class AutofillManagerTest : public testing::Test {
std::unique_ptr<MockAutofillManager> manager_;
};
-// The test parameters en-/disable kAutofillDisplaceRemovedForms and set the
-// number of forms, respectively.
-class AutofillManagerTest_WithOrWithoutCacheFix_WithIntParam
+// The test parameter sets the number of forms to be generated.
+class AutofillManagerTest_WithIntParam
: public AutofillManagerTest,
- public ::testing::WithParamInterface<std::tuple<bool, size_t>> {
+ public ::testing::WithParamInterface<size_t> {
public:
- AutofillManagerTest_WithOrWithoutCacheFix_WithIntParam() {
- scoped_feature_list_.InitWithFeatureState(
- features::kAutofillDisplaceRemovedForms, displace_removed_forms());
- }
-
- bool displace_removed_forms() const { return std::get<0>(GetParam()); }
- size_t num_forms() const { return std::get<1>(GetParam()); }
-
- protected:
- base::test::ScopedFeatureList scoped_feature_list_;
+ size_t num_forms() const { return GetParam(); }
};
-INSTANTIATE_TEST_SUITE_P(
- AutofillManagerTest,
- AutofillManagerTest_WithOrWithoutCacheFix_WithIntParam,
- testing::Combine(testing::Bool(), testing::Values(0, 1, 5, 10, 100, 110)));
+INSTANTIATE_TEST_SUITE_P(AutofillManagerTest,
+ AutofillManagerTest_WithIntParam,
+ testing::Values(0, 1, 5, 10, 100, 110));
// Tests that the cache size is bounded by kAutofillManagerMaxFormCacheSize.
-TEST_P(AutofillManagerTest_WithOrWithoutCacheFix_WithIntParam,
- CacheBoundFormsSeen) {
+TEST_P(AutofillManagerTest_WithIntParam, CacheBoundFormsSeen) {
size_t num_exp_forms =
std::min(num_forms(), kAutofillManagerMaxFormCacheSize);
std::vector<FormData> forms = CreateTestForms(num_forms());
@@ -235,33 +275,21 @@ TEST_P(AutofillManagerTest_WithOrWithoutCacheFix_WithIntParam,
OnFormsSeenWithExpectations(*manager_, forms, {}, exp_forms);
}
-// Enables kAutofillDisplaceRemovedForms.
-class AutofillManagerTest_WithCacheFix : public AutofillManagerTest {
- public:
- AutofillManagerTest_WithCacheFix() {
- scoped_feature_list_.InitAndEnableFeature(
- features::kAutofillDisplaceRemovedForms);
- }
-
- protected:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
// Tests that removing unseen forms has no effect.
-TEST_F(AutofillManagerTest_WithCacheFix, RemoveUnseenForms) {
+TEST_F(AutofillManagerTest, RemoveUnseenForms) {
std::vector<FormData> forms = CreateTestForms(9);
OnFormsSeenWithExpectations(*manager_, {}, GetFormIds(forms), {});
}
// Tests that all forms can be removed at once.
-TEST_F(AutofillManagerTest_WithCacheFix, RemoveAllForms) {
+TEST_F(AutofillManagerTest, RemoveAllForms) {
std::vector<FormData> forms = CreateTestForms(9);
OnFormsSeenWithExpectations(*manager_, forms, {}, forms);
OnFormsSeenWithExpectations(*manager_, {}, GetFormIds(forms), {});
}
// Tests that removing some forms leaves the other forms untouched.
-TEST_F(AutofillManagerTest_WithCacheFix, RemoveSomeForms) {
+TEST_F(AutofillManagerTest, RemoveSomeForms) {
std::vector<FormData> forms = CreateTestForms(9);
auto range = [&](size_t begin, size_t end) {
return std::vector<FormData>(forms.begin() + begin, forms.begin() + end);
@@ -272,10 +300,44 @@ TEST_F(AutofillManagerTest_WithCacheFix, RemoveSomeForms) {
}
// Tests that adding and removing the same forms has no effect.
-TEST_F(AutofillManagerTest_WithCacheFix, UpdateAndRemoveSameForms) {
+TEST_F(AutofillManagerTest, UpdateAndRemoveSameForms) {
std::vector<FormData> forms = CreateTestForms(9);
OnFormsSeenWithExpectations(*manager_, forms, GetFormIds(forms), forms);
OnFormsSeenWithExpectations(*manager_, forms, GetFormIds(forms), forms);
}
+TEST_F(AutofillManagerTest, ObserverReceiveCalls) {
+ FormData form = CreateTestForms(1).front();
+ FormFieldData field = form.fields.front();
+ gfx::RectF bounds;
+ base::TimeTicks time = AutofillTickClock::NowTicks();
+
+ MockAutofillObserver observer;
+ manager_->AddObserver(&observer);
+ // Reset the manager, the observers should stick around.
+ manager_->Reset();
+
+ EXPECT_CALL(observer, OnTextFieldDidChange()).Times(1);
+ manager_->OnTextFieldDidChange(form, field, bounds, time);
+ EXPECT_CALL(observer, OnTextFieldDidChange()).Times(0);
+
+ EXPECT_CALL(observer, OnTextFieldDidScroll()).Times(1);
+ manager_->OnTextFieldDidScroll(form, field, bounds);
+ EXPECT_CALL(observer, OnTextFieldDidScroll()).Times(0);
+
+ EXPECT_CALL(observer, OnSelectControlDidChange()).Times(1);
+ manager_->OnSelectControlDidChange(form, field, bounds);
+ EXPECT_CALL(observer, OnSelectControlDidChange()).Times(0);
+
+ EXPECT_CALL(observer, OnFormSubmitted()).Times(1);
+ manager_->OnFormSubmitted(form, true,
+ mojom::SubmissionSource::FORM_SUBMISSION);
+ EXPECT_CALL(observer, OnFormSubmitted()).Times(0);
+
+ // Remove observer from manager, the observer should no longer receive pings.
+ manager_->RemoveObserver(&observer);
+ EXPECT_CALL(observer, OnTextFieldDidChange()).Times(0);
+ manager_->OnTextFieldDidChange(form, field, bounds, time);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
index 7a3c022a50d..6fb3b5f666d 100644
--- a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
@@ -23,6 +23,7 @@
#include "components/autofill/core/browser/data_model/autofill_profile_comparator.h"
#include "components/autofill/core/browser/form_data_importer.h"
#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/form_structure_test_api.h"
#include "components/autofill/core/browser/geo/country_names.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/test_autofill_client.h"
@@ -170,6 +171,10 @@ std::vector<AutofillProfile*> PersonalDataManagerMock::GetProfiles() const {
return result;
}
+FormStructureTestApi test_api(FormStructure* form_structure) {
+ return FormStructureTestApi(form_structure);
+}
+
} // namespace
// A data-driven test for verifying merging of Autofill profiles. Each input is
@@ -292,9 +297,9 @@ void AutofillMergeTest::MergeProfiles(const std::string& profiles,
const_cast<AutofillField*>(form_structure.field(j));
ServerFieldType type =
StringToFieldType(base::UTF16ToUTF8(field->name));
- field->set_heuristic_type(type);
+ field->set_heuristic_type(GetActivePatternSource(), type);
}
- form_structure.IdentifySections(false);
+ test_api(&form_structure).IdentifySections(false);
// Import the profile.
std::unique_ptr<CreditCard> imported_credit_card;
diff --git a/chromium/components/autofill/core/browser/autofill_policy_handler.cc b/chromium/components/autofill/core/browser/autofill_policy_handler.cc
index dd14b749729..31cf85fd4b7 100644
--- a/chromium/components/autofill/core/browser/autofill_policy_handler.cc
+++ b/chromium/components/autofill/core/browser/autofill_policy_handler.cc
@@ -33,10 +33,10 @@ void AutofillPolicyHandler::ApplyPolicySettings(
const base::Value* value =
policies.GetValue(policy_name(), base::Value::Type::BOOLEAN);
if (value && !value->GetBool()) {
- prefs->SetBoolean(autofill::prefs::kAutofillEnabledDeprecated, false);
+ prefs->SetBoolean(prefs::kAutofillEnabledDeprecated, false);
// Disable the fine-grained prefs if the main pref is disabled by policy.
- prefs->SetBoolean(autofill::prefs::kAutofillCreditCardEnabled, false);
- prefs->SetBoolean(autofill::prefs::kAutofillProfileEnabled, false);
+ prefs->SetBoolean(prefs::kAutofillCreditCardEnabled, false);
+ prefs->SetBoolean(prefs::kAutofillProfileEnabled, false);
}
}
diff --git a/chromium/components/autofill/core/browser/autofill_profile_import_process.cc b/chromium/components/autofill/core/browser/autofill_profile_import_process.cc
index fd2740f57de..bb0095a5f56 100644
--- a/chromium/components/autofill/core/browser/autofill_profile_import_process.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile_import_process.cc
@@ -107,7 +107,7 @@ void ProfileImportProcess::DetermineProfileImportType() {
// merge can be considered as a silent update that does not need to get
// user confirmation.
if (AutofillProfileComparator::ProfilesHaveDifferentSettingsVisibleValues(
- *existing_profile, merged_profile)) {
+ *existing_profile, merged_profile, app_locale_)) {
if (allow_only_silent_updates_) {
++number_of_unchanged_profiles;
continue;
diff --git a/chromium/components/autofill/core/browser/autofill_profile_save_strike_database_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_save_strike_database_unittest.cc
index 8c4ca3120a8..2f9bd16a437 100644
--- a/chromium/components/autofill/core/browser/autofill_profile_save_strike_database_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile_save_strike_database_unittest.cc
@@ -69,15 +69,15 @@ class AutofillProfileSaveStrikeDatabaseTest : public ::testing::Test {
TEST_F(AutofillProfileSaveStrikeDatabaseTest, AddAndRemoveStrikes) {
strike_database_->AddStrike(test_host1);
EXPECT_EQ(strike_database_->GetStrikes(test_host1), 1);
- EXPECT_FALSE(strike_database_->IsMaxStrikesLimitReached(test_host1));
+ EXPECT_FALSE(strike_database_->ShouldBlockFeature(test_host1));
strike_database_->AddStrikes(2, test_host1);
EXPECT_EQ(strike_database_->GetStrikes(test_host1), 3);
- EXPECT_TRUE(strike_database_->IsMaxStrikesLimitReached(test_host1));
+ EXPECT_TRUE(strike_database_->ShouldBlockFeature(test_host1));
strike_database_->RemoveStrike(test_host1);
EXPECT_EQ(strike_database_->GetStrikes(test_host1), 2);
- EXPECT_FALSE(strike_database_->IsMaxStrikesLimitReached(test_host1));
+ EXPECT_FALSE(strike_database_->ShouldBlockFeature(test_host1));
}
TEST_F(AutofillProfileSaveStrikeDatabaseTest,
diff --git a/chromium/components/autofill/core/browser/autofill_profile_update_strike_database_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_update_strike_database_unittest.cc
index d1b23d3b7ad..f4516288db7 100644
--- a/chromium/components/autofill/core/browser/autofill_profile_update_strike_database_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile_update_strike_database_unittest.cc
@@ -55,15 +55,15 @@ TEST_F(AutofillProfileUpdateStrikeDatabaseTest, AddAndRemoveStrikes) {
std::string test_guid = "a21f010a-eac1-41fc-aee9-c06bbedfb292";
strike_database_->AddStrike(test_guid);
EXPECT_EQ(strike_database_->GetStrikes(test_guid), 1);
- EXPECT_FALSE(strike_database_->IsMaxStrikesLimitReached(test_guid));
+ EXPECT_FALSE(strike_database_->ShouldBlockFeature(test_guid));
strike_database_->AddStrikes(2, test_guid);
EXPECT_EQ(strike_database_->GetStrikes(test_guid), 3);
- EXPECT_TRUE(strike_database_->IsMaxStrikesLimitReached(test_guid));
+ EXPECT_TRUE(strike_database_->ShouldBlockFeature(test_guid));
strike_database_->RemoveStrike(test_guid);
EXPECT_EQ(strike_database_->GetStrikes(test_guid), 2);
- EXPECT_FALSE(strike_database_->IsMaxStrikesLimitReached(test_guid));
+ EXPECT_FALSE(strike_database_->ShouldBlockFeature(test_guid));
}
} // namespace
diff --git a/chromium/components/autofill/core/browser/autofill_regexes.cc b/chromium/components/autofill/core/browser/autofill_regexes.cc
index b9d44591b32..f5aa6a9b59b 100644
--- a/chromium/components/autofill/core/browser/autofill_regexes.cc
+++ b/chromium/components/autofill/core/browser/autofill_regexes.cc
@@ -69,8 +69,7 @@ namespace autofill {
bool MatchesPattern(const base::StringPiece16& input,
const base::StringPiece16& pattern,
- std::u16string* match,
- int32_t group_to_be_captured) {
+ std::vector<std::u16string>* groups) {
if (input.size() > kMaxStringLength)
return false;
@@ -86,11 +85,15 @@ bool MatchesPattern(const base::StringPiece16& input,
UBool matched = matcher->find(0, status);
DCHECK(U_SUCCESS(status));
- if (matched && match) {
- icu::UnicodeString match_unicode =
- matcher->group(group_to_be_captured, status);
- DCHECK(U_SUCCESS(status));
- *match = base::i18n::UnicodeStringToString16(match_unicode);
+ if (matched && groups) {
+ int32_t matched_groups = matcher->groupCount();
+ groups->resize(matched_groups + 1);
+
+ for (int32_t i = 0; i < matched_groups + 1; ++i) {
+ icu::UnicodeString match_unicode = matcher->group(i, status);
+ DCHECK(U_SUCCESS(status));
+ (*groups)[i] = base::i18n::UnicodeStringToString16(match_unicode);
+ }
}
return matched;
diff --git a/chromium/components/autofill/core/browser/autofill_regexes.h b/chromium/components/autofill/core/browser/autofill_regexes.h
index 355e17c6917..75d72047e8f 100644
--- a/chromium/components/autofill/core/browser/autofill_regexes.h
+++ b/chromium/components/autofill/core/browser/autofill_regexes.h
@@ -6,6 +6,7 @@
#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_
#include <string>
+#include <vector>
#include "base/strings/string_piece.h"
@@ -14,11 +15,11 @@ namespace autofill {
// Case-insensitive regular expression matching.
// Returns true if |pattern| is found in |input|.
-// The |group_to_be_captured| numbered group is captured into |match|.
+// If |groups| is non-null, it gets resized and the found capture groups
+// are written into it.
bool MatchesPattern(const base::StringPiece16& input,
const base::StringPiece16& pattern,
- std::u16string* match = nullptr,
- int32_t group_to_be_captured = 0);
+ std::vector<std::u16string>* groups = nullptr);
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc b/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc
index 6a8f321d349..236b9fc5288 100644
--- a/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc
@@ -13,6 +13,7 @@
#include <string>
#include "components/autofill/core/browser/autofill_regex_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace autofill {
@@ -75,6 +76,44 @@ INSTANTIATE_TEST_SUITE_P(AutofillRegexes,
InputPatternTestCase{u"string", u"ring "},
InputPatternTestCase{u"string", u"rin$"}));
+// Tests for capture groups.
+struct CapturePatternTestCase {
+ const char16_t* const input;
+ const char16_t* const pattern;
+ const bool matches;
+ const std::vector<std::u16string> groups;
+};
+
+class CaptureTest : public testing::TestWithParam<CapturePatternTestCase> {};
+
+TEST_P(CaptureTest, SampleRegexes) {
+ auto test_case = GetParam();
+ std::vector<std::u16string> groups;
+ EXPECT_EQ(test_case.matches,
+ MatchesPattern(test_case.input, test_case.pattern, &groups));
+ EXPECT_THAT(groups, testing::Eq(test_case.groups));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AutofillRegexes,
+ CaptureTest,
+ testing::Values(
+ // Find substrings in the input.
+ CapturePatternTestCase{u"Foo abcde Bar",
+ u"a(b+)c(d+)e",
+ true,
+ {u"abcde", u"b", u"d"}},
+ // Deal with optional capture groups.
+ CapturePatternTestCase{u"Foo acde Bar",
+ u"a(b+)?c(d+)e", // There is no b in the input.
+ true,
+ {u"acde", u"", u"d"}},
+ // Deal with non-matching capture groups.
+ CapturePatternTestCase{u"Foo acde Bar",
+ u"a(b+)c(d+)e", // There is no b in the input.
+ false,
+ {}}));
+
struct InputTestCase {
const char16_t* const input;
};
diff --git a/chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc b/chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc
index c43af8e384a..5b688b2fa83 100644
--- a/chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc
+++ b/chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc
@@ -99,12 +99,12 @@ AutofillSaveUpdateAddressProfileDelegateIOS::GetMessageActionText() const {
: IDS_IOS_AUTOFILL_SAVE_ADDRESS_MESSAGE_PRIMARY_ACTION);
}
-const autofill::AutofillProfile*
-AutofillSaveUpdateAddressProfileDelegateIOS::GetProfile() const {
+const AutofillProfile* AutofillSaveUpdateAddressProfileDelegateIOS::GetProfile()
+ const {
return &profile_;
}
-const autofill::AutofillProfile*
+const AutofillProfile*
AutofillSaveUpdateAddressProfileDelegateIOS::GetOriginalProfile() const {
return base::OptionalOrNullptr(original_profile_);
}
@@ -146,16 +146,15 @@ void AutofillSaveUpdateAddressProfileDelegateIOS::SetProfileInfo(
const std::u16string& value) {
// Since the country field is a text field, we should use SetInfo() to make
// sure they get converted to country codes.
- if (type == autofill::ADDRESS_HOME_COUNTRY) {
+ if (type == ADDRESS_HOME_COUNTRY) {
profile_.SetInfoWithVerificationStatus(
type, value, locale_,
- autofill::structured_address::VerificationStatus::kUserVerified);
+ structured_address::VerificationStatus::kUserVerified);
return;
}
profile_.SetRawInfoWithVerificationStatus(
- type, value,
- autofill::structured_address::VerificationStatus::kUserVerified);
+ type, value, structured_address::VerificationStatus::kUserVerified);
}
bool AutofillSaveUpdateAddressProfileDelegateIOS::Accept() {
diff --git a/chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h b/chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h
index 710b07a83c8..e0873795219 100644
--- a/chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h
+++ b/chromium/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h
@@ -69,8 +69,8 @@ class AutofillSaveUpdateAddressProfileDelegateIOS
// Updates |profile_| |type| value to |value|.
void SetProfileInfo(const ServerFieldType& type, const std::u16string& value);
- const autofill::AutofillProfile* GetProfile() const;
- const autofill::AutofillProfile* GetOriginalProfile() const;
+ const AutofillProfile* GetProfile() const;
+ const AutofillProfile* GetOriginalProfile() const;
// ConfirmInfoBarDelegate
int GetIconId() const override;
diff --git a/chromium/components/autofill/core/browser/autofill_subject.cc b/chromium/components/autofill/core/browser/autofill_subject.cc
index 80b5eefd331..f8159bd7730 100644
--- a/chromium/components/autofill/core/browser/autofill_subject.cc
+++ b/chromium/components/autofill/core/browser/autofill_subject.cc
@@ -9,8 +9,6 @@
#include "base/observer_list.h"
#include "components/autofill/core/browser/autofill_observer.h"
-using NotificationType = autofill::AutofillObserver::NotificationType;
-
namespace autofill {
AutofillSubject::AutofillSubject() = default;
diff --git a/chromium/components/autofill/core/browser/autofill_subject.h b/chromium/components/autofill/core/browser/autofill_subject.h
index 2e4f222fa59..c96bf14dc4e 100644
--- a/chromium/components/autofill/core/browser/autofill_subject.h
+++ b/chromium/components/autofill/core/browser/autofill_subject.h
@@ -10,14 +10,14 @@
#include "base/observer_list.h"
#include "components/autofill/core/browser/autofill_observer.h"
-using NotificationType = autofill::AutofillObserver::NotificationType;
-
namespace autofill {
// Subject that can emit notifications of specific types to observers that were
// opted-in.
class AutofillSubject {
public:
+ using NotificationType = AutofillObserver::NotificationType;
+
AutofillSubject();
~AutofillSubject();
diff --git a/chromium/components/autofill/core/browser/autofill_suggestion_generator.cc b/chromium/components/autofill/core/browser/autofill_suggestion_generator.cc
index 1bc88e27d45..0dbe739c53f 100644
--- a/chromium/components/autofill/core/browser/autofill_suggestion_generator.cc
+++ b/chromium/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -2,14 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <string>
-
#include "components/autofill/core/browser/autofill_suggestion_generator.h"
+#include <string>
+
+#include "base/feature_list.h"
+#include "base/guid.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_browser_util.h"
#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/field_filler.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/metrics/autofill_metrics.h"
#include "components/autofill/core/browser/payments/autofill_offer_manager.h"
@@ -22,6 +27,8 @@
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/feature_engagement/public/feature_constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
namespace autofill {
@@ -50,12 +57,53 @@ AutofillSuggestionGenerator::AutofillSuggestionGenerator(
PersonalDataManager* personal_data)
: autofill_client_(autofill_client), personal_data_(personal_data) {}
+AutofillSuggestionGenerator::~AutofillSuggestionGenerator() = default;
+
+std::vector<Suggestion> AutofillSuggestionGenerator::GetSuggestionsForProfiles(
+ const FormStructure& form,
+ const FormFieldData& field,
+ const AutofillField& autofill_field,
+ const std::string& app_locale) {
+ std::vector<ServerFieldType> field_types(form.field_count());
+ for (size_t i = 0; i < form.field_count(); ++i) {
+ field_types.push_back(form.field(i)->Type().GetStorableType());
+ }
+
+ std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
+ autofill_field.Type(), field.value, field.is_autofilled, field_types);
+
+ // Adjust phone number to display in prefix/suffix case.
+ if (autofill_field.Type().group() == FieldTypeGroup::kPhoneHome) {
+ for (auto& suggestion : suggestions) {
+ const AutofillProfile* profile = personal_data_->GetProfileByGUID(
+ suggestion.GetPayload<std::string>());
+ if (profile) {
+ const std::u16string phone_home_city_and_number =
+ profile->GetInfo(PHONE_HOME_CITY_AND_NUMBER, app_locale);
+ suggestion.main_text =
+ Suggestion::Text(FieldFiller::GetPhoneNumberValueForInput(
+ autofill_field, suggestion.main_text.value,
+ phone_home_city_and_number, field),
+ Suggestion::Text::IsPrimary(true));
+ }
+ }
+ }
+
+ for (auto& suggestion : suggestions) {
+ suggestion.frontend_id =
+ MakeFrontendId(std::string(), suggestion.GetPayload<std::string>());
+ }
+
+ return suggestions;
+}
+
std::vector<Suggestion>
AutofillSuggestionGenerator::GetSuggestionsForCreditCards(
const FormStructure& form_structure,
const FormFieldData& field,
const AutofillType& type,
- const std::string& app_locale) {
+ const std::string& app_locale,
+ bool* should_display_gpay_logo) {
std::vector<Suggestion> suggestions;
DCHECK(personal_data_);
@@ -63,6 +111,9 @@ AutofillSuggestionGenerator::GetSuggestionsForCreditCards(
personal_data_->GetCreditCardsToSuggest(
autofill_client_->AreServerCardsSupported());
+ *should_display_gpay_logo = base::ranges::all_of(
+ cards_to_suggest, base::not_fn(&CreditCard::IsLocalCard));
+
// The field value is sanitized before attempting to match it to the user's
// data.
auto field_contents = SanitizeCreditCardFieldValue(field.value);
@@ -88,7 +139,7 @@ AutofillSuggestionGenerator::GetSuggestionsForCreditCards(
if (suggestion_selection::IsValidSuggestionForFieldContents(
base::i18n::ToLower(creditcard_field_value), field_contents_lower,
type, credit_card->record_type() == CreditCard::MASKED_SERVER_CARD,
- &prefix_matched_suggestion)) {
+ field.is_autofilled, &prefix_matched_suggestion)) {
if (ShouldShowVirtualCardOption(credit_card, form_structure)) {
suggestions.push_back(CreateCreditCardSuggestion(
*credit_card, type, prefix_matched_suggestion,
@@ -118,6 +169,58 @@ AutofillSuggestionGenerator::GetSuggestionsForCreditCards(
autofill_client_->GetLastCommittedURL(), suggestions);
}
+ for (Suggestion& suggestion : suggestions) {
+ if (suggestion.frontend_id == 0) {
+ suggestion.frontend_id =
+ MakeFrontendId(suggestion.GetPayload<std::string>(), std::string());
+ }
+ }
+
+ return suggestions;
+}
+
+// static
+std::vector<Suggestion>
+AutofillSuggestionGenerator::GetPromoCodeSuggestionsFromPromoCodeOffers(
+ const std::vector<const AutofillOfferData*>& promo_code_offers) {
+ std::vector<Suggestion> suggestions;
+ GURL footer_offer_details_url;
+ for (const AutofillOfferData* promo_code_offer : promo_code_offers) {
+ // For each promo code, create a suggestion.
+ suggestions.emplace_back(
+ base::ASCIIToUTF16(promo_code_offer->GetPromoCode()));
+ Suggestion& suggestion = suggestions.back();
+ suggestion.label = base::ASCIIToUTF16(
+ promo_code_offer->GetDisplayStrings().value_prop_text);
+ suggestion.payload = base::NumberToString(promo_code_offer->GetOfferId());
+ suggestion.frontend_id = POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY;
+
+ // Every offer for a given merchant leads to the same GURL, so we grab the
+ // first offer's offer details url as the payload for the footer to set
+ // later.
+ if (footer_offer_details_url.is_empty() &&
+ !promo_code_offer->GetOfferDetailsUrl().is_empty() &&
+ promo_code_offer->GetOfferDetailsUrl().is_valid()) {
+ footer_offer_details_url = promo_code_offer->GetOfferDetailsUrl();
+ }
+ }
+
+ // Ensure that there are suggestions and that we were able to find at least
+ // one suggestion with a valid offer details url before adding the footer.
+ DCHECK(suggestions.size() > 0);
+ if (!footer_offer_details_url.is_empty()) {
+ suggestions.emplace_back(l10n_util::GetStringUTF16(
+ IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT));
+ Suggestion& suggestion = suggestions.back();
+ suggestion.frontend_id = POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS;
+
+ // We set the payload for the footer as |footer_offer_details_url|, which is
+ // the offer details url of the first offer we had for this merchant. We
+ // will navigate to the url in |footer_offer_details_url| if the footer is
+ // selected in AutofillExternalDelegate::DidAcceptSuggestion().
+ suggestion.payload = std::move(footer_offer_details_url);
+ suggestion.trailing_icon = "google";
+ }
return suggestions;
}
@@ -127,13 +230,17 @@ void AutofillSuggestionGenerator::RemoveExpiredCreditCardsNotUsedSinceTimestamp(
base::Time min_last_used,
std::vector<CreditCard*>* cards) {
const size_t original_size = cards->size();
- // Split the vector into [unexpired-or-expired-but-after-timestamp,
- // expired-and-before-timestamp], then delete the latter.
+ // Split the vector into two groups
+ // 1. All server cards, unexpired local cards, or local cards that have been
+ // used after |min_last_used|;
+ // 2. Expired local cards that have not been used since |min_last_used|;
+ // then delete the latter.
cards->erase(std::stable_partition(
cards->begin(), cards->end(),
[comparison_time, min_last_used](const CreditCard* c) {
return !c->IsExpired(comparison_time) ||
- c->use_date() >= min_last_used;
+ c->use_date() >= min_last_used ||
+ c->record_type() != CreditCard::LOCAL_CARD;
}),
cards->end());
const size_t num_cards_supressed = original_size - cards->size();
@@ -160,6 +267,43 @@ std::u16string AutofillSuggestionGenerator::GetDisplayNicknameForCreditCard(
return card.nickname();
}
+// When sending IDs (across processes) to the renderer we pack credit card and
+// profile IDs into a single integer. Credit card IDs are sent in the high
+// word and profile IDs are sent in the low word.
+int AutofillSuggestionGenerator::MakeFrontendId(
+ const std::string& cc_backend_id,
+ const std::string& profile_backend_id) const {
+ InternalId cc_int_id = BackendIdToInternalId(cc_backend_id);
+ InternalId profile_int_id = BackendIdToInternalId(profile_backend_id);
+
+ // Should fit in signed 16-bit integers. We use 16-bits each when combining
+ // below, and negative frontend IDs have special meaning so we can never use
+ // the high bit.
+ DCHECK(cc_int_id.value() <= std::numeric_limits<int16_t>::max());
+ DCHECK(profile_int_id.value() <= std::numeric_limits<int16_t>::max());
+
+ // Put CC in the high half of the bits.
+ return (cc_int_id.value() << std::numeric_limits<uint16_t>::digits) |
+ profile_int_id.value();
+}
+
+// When receiving IDs (across processes) from the renderer we unpack credit
+// card and profile IDs from a single integer. Credit card IDs are stored in
+// the high word and profile IDs are stored in the low word.
+void AutofillSuggestionGenerator::SplitFrontendId(
+ int frontend_id,
+ std::string* cc_backend_id,
+ std::string* profile_backend_id) const {
+ InternalId cc_int_id =
+ InternalId((frontend_id >> std::numeric_limits<uint16_t>::digits) &
+ std::numeric_limits<uint16_t>::max());
+ InternalId profile_int_id =
+ InternalId(frontend_id & std::numeric_limits<uint16_t>::max());
+
+ *cc_backend_id = InternalIdToBackendId(cc_int_id);
+ *profile_backend_id = InternalIdToBackendId(profile_int_id);
+}
+
Suggestion AutofillSuggestionGenerator::CreateCreditCardSuggestion(
const CreditCard& credit_card,
const AutofillType& type,
@@ -168,7 +312,8 @@ Suggestion AutofillSuggestionGenerator::CreateCreditCardSuggestion(
const std::string& app_locale) const {
Suggestion suggestion;
- suggestion.value = credit_card.GetInfo(type, app_locale);
+ suggestion.main_text = Suggestion::Text(credit_card.GetInfo(type, app_locale),
+ Suggestion::Text::IsPrimary(true));
suggestion.icon = credit_card.CardIconStringForAutofillSuggestion();
std::string backend_id = credit_card.guid();
suggestion.match = prefix_matched_suggestion ? Suggestion::PREFIX_MATCH
@@ -187,7 +332,7 @@ Suggestion AutofillSuggestionGenerator::CreateCreditCardSuggestion(
server_duplicate_card->card_art_url();
backend_id = server_duplicate_card->guid();
}
- suggestion.backend_id = backend_id;
+ suggestion.payload = backend_id;
// Get the nickname for the card suggestion, which may not be the same as
// the card's nickname if there are duplicates of the card on file.
@@ -196,23 +341,27 @@ Suggestion AutofillSuggestionGenerator::CreateCreditCardSuggestion(
// The kAutofillKeyboardAccessory feature is only available on Android. So for
// other platforms, we'd always use the obfuscation_length of 4.
- int obfuscation_length = base::FeatureList::IsEnabled(
- autofill::features::kAutofillKeyboardAccessory)
- ? 2
- : 4;
+ int obfuscation_length =
+ base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory) ? 2
+ : 4;
// If the value is the card number, the label is the expiration date.
// Otherwise the label is the card number, or if that is empty the
// cardholder name. The label should never repeat the value.
if (type.GetStorableType() == CREDIT_CARD_NUMBER) {
- suggestion.value = credit_card.CardIdentifierStringForAutofillDisplay(
- suggestion_nickname, obfuscation_length);
+ suggestion.main_text =
+ Suggestion::Text(credit_card.CardIdentifierStringForAutofillDisplay(
+ suggestion_nickname, obfuscation_length),
+ Suggestion::Text::IsPrimary(true));
+ if (!base::FeatureList::IsEnabled(
+ features::kAutofillRemoveCardExpiryFromDownstreamSuggestion)) {
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
- suggestion.label = credit_card.GetInfo(
- AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale);
+ suggestion.label = credit_card.GetInfo(
+ AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale);
#else
- suggestion.label = credit_card.DescriptiveExpiration(app_locale);
+ suggestion.label = credit_card.DescriptiveExpiration(app_locale);
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+ }
} else if (credit_card.number().empty()) {
DCHECK_EQ(credit_card.record_type(), CreditCard::LOCAL_CARD);
@@ -248,6 +397,10 @@ Suggestion AutofillSuggestionGenerator::CreateCreditCardSuggestion(
#endif // BUILDFLAG(IS_ANDROID)
suggestion.frontend_id = POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY;
+ suggestion.minor_text.value = suggestion.main_text.value;
+ suggestion.main_text.value = l10n_util::GetStringUTF16(
+ IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE);
+
suggestion.feature_for_iph =
feature_engagement::kIPHAutofillVirtualCardSuggestionFeature.name;
@@ -263,12 +416,6 @@ Suggestion AutofillSuggestionGenerator::CreateCreditCardSuggestion(
bool AutofillSuggestionGenerator::ShouldShowVirtualCardOption(
const CreditCard* candidate_card,
const FormStructure& form_structure) const {
- // If virtual card experiment is disabled:
- if (!base::FeatureList::IsEnabled(
- features::kAutofillEnableMerchantBoundVirtualCards)) {
- return false;
- }
-
// If the form is an incomplete form and the incomplete form experiment is
// disabled, do not offer a virtual card option. We will likely not be able to
// fill in all information, and the user doesn't have the info either.
@@ -318,4 +465,33 @@ const CreditCard* AutofillSuggestionGenerator::GetServerCardForLocalCard(
return nullptr;
}
+InternalId AutofillSuggestionGenerator::BackendIdToInternalId(
+ const std::string& backend_id) const {
+ if (!base::IsValidGUID(backend_id))
+ return InternalId(0);
+
+ const auto found = backend_to_int_map_.find(backend_id);
+ if (found == backend_to_int_map_.end()) {
+ // Unknown one, make a new entry.
+ InternalId int_id = InternalId(backend_to_int_map_.size() + 1);
+ backend_to_int_map_[backend_id] = int_id;
+ int_to_backend_map_[int_id] = backend_id;
+ return int_id;
+ }
+ return InternalId(found->second);
+}
+
+std::string AutofillSuggestionGenerator::InternalIdToBackendId(
+ InternalId int_id) const {
+ if (int_id.value() == 0)
+ return std::string();
+
+ const auto found = int_to_backend_map_.find(int_id);
+ if (found == int_to_backend_map_.end()) {
+ NOTREACHED();
+ return std::string();
+ }
+ return found->second;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_suggestion_generator.h b/chromium/components/autofill/core/browser/autofill_suggestion_generator.h
index 585378ed16c..d2caa220b73 100644
--- a/chromium/components/autofill/core/browser/autofill_suggestion_generator.h
+++ b/chromium/components/autofill/core/browser/autofill_suggestion_generator.h
@@ -5,11 +5,13 @@
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SUGGESTION_GENERATOR_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SUGGESTION_GENERATOR_H_
+#include <map>
#include <string>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
+#include "base/types/strong_alias.h"
namespace base {
class Time;
@@ -18,6 +20,8 @@ class Time;
namespace autofill {
class AutofillClient;
+class AutofillField;
+class AutofillOfferData;
class AutofillType;
class CreditCard;
struct FormFieldData;
@@ -25,23 +29,38 @@ class FormStructure;
class PersonalDataManager;
struct Suggestion;
+using InternalId = base::StrongAlias<class InternalIdTag, int>;
+
// Helper class to generate Autofill suggestions, such as for credit card and
// address profile Autofill.
class AutofillSuggestionGenerator {
public:
explicit AutofillSuggestionGenerator(AutofillClient* autofill_client,
PersonalDataManager* personal_data);
- ~AutofillSuggestionGenerator() = default;
+ ~AutofillSuggestionGenerator();
AutofillSuggestionGenerator(const AutofillSuggestionGenerator&) = delete;
AutofillSuggestionGenerator& operator=(const AutofillSuggestionGenerator&) =
delete;
+ // Generates suggestions for all available profiles.
+ std::vector<Suggestion> GetSuggestionsForProfiles(
+ const FormStructure& form,
+ const FormFieldData& field,
+ const AutofillField& autofill_field,
+ const std::string& app_locale);
+
// Generates suggestions for all available credit cards.
std::vector<Suggestion> GetSuggestionsForCreditCards(
const FormStructure& form_structure,
const FormFieldData& field,
const AutofillType& type,
- const std::string& app_locale);
+ const std::string& app_locale,
+ bool* should_display_gpay_logo);
+
+ // Converts the vector of promo code offers that is passed in to a vector of
+ // suggestions that can be displayed to the user for a promo code field.
+ static std::vector<Suggestion> GetPromoCodeSuggestionsFromPromoCodeOffers(
+ const std::vector<const AutofillOfferData*>& promo_code_offers);
// Remove credit cards that are expired at |comparison_time| and not used
// since |min_last_used| from |cards|. The relative ordering of |cards| is
@@ -57,6 +76,14 @@ class AutofillSuggestionGenerator {
// one copy has a nickname, take that.
std::u16string GetDisplayNicknameForCreditCard(const CreditCard& card) const;
+ // Methods for packing and unpacking credit card and profile IDs for sending
+ // and receiving to and from the renderer process.
+ int MakeFrontendId(const std::string& cc_backend_id,
+ const std::string& profile_backend_id) const;
+ void SplitFrontendId(int frontend_id,
+ std::string* cc_backend_id,
+ std::string* profile_backend_id) const;
+
private:
FRIEND_TEST_ALL_PREFIXES(AutofillSuggestionGeneratorTest,
CreateCreditCardSuggestion_LocalCard);
@@ -89,12 +116,24 @@ class AutofillSuggestionGenerator {
const CreditCard* GetServerCardForLocalCard(
const CreditCard* local_card) const;
+ // Maps suggestion backend ID to and from an integer identifying it. Two of
+ // these intermediate integers are packed by MakeFrontendID to make the IDs
+ // that this class generates for the UI and for IPC.
+ InternalId BackendIdToInternalId(const std::string& backend_id) const;
+ std::string InternalIdToBackendId(InternalId int_id) const;
+
// autofill_client_ and the generator are both one per tab, and have the same
// lifecycle.
raw_ptr<AutofillClient> autofill_client_;
// personal_data_ should outlive the generator.
raw_ptr<PersonalDataManager> personal_data_;
+
+ // Suggestion backend ID to ID mapping. We keep two maps to convert back and
+ // forth. These should be used only by BackendIDToInt and IntToBackendID.
+ // Note that the integers are not frontend IDs.
+ mutable std::map<std::string, InternalId> backend_to_int_map_;
+ mutable std::map<InternalId, std::string> int_to_backend_map_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc b/chromium/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
index 5f8adc071e4..222d4f876b6 100644
--- a/chromium/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
@@ -19,7 +19,9 @@
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_payments_features.h"
+#include "components/strings/grit/components_strings.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_unittest_util.h"
@@ -33,8 +35,6 @@ class AutofillSuggestionGeneratorTest : public testing::Test {
AutofillSuggestionGeneratorTest() = default;
void SetUp() override {
- scoped_feature_list_.InitAndEnableFeature(
- features::kAutofillEnableMerchantBoundVirtualCards);
autofill_client_.SetPrefs(test::PrefServiceForTesting());
personal_data_.Init(/*profile_database=*/database_,
/*account_database=*/nullptr,
@@ -218,6 +218,27 @@ TEST_F(AutofillSuggestionGeneratorTest,
histogram_tester.ExpectTotalCount(kHistogramName, 1);
histogram_tester.ExpectBucketCount(kHistogramName, kNumCards, 1);
}
+
+ // Verify all expired and disused server cards are not removed.
+ {
+ // Create a working copy of the card pointers. And set one card to be a
+ // masked server card.
+ std::vector<CreditCard*> cards(all_card_ptrs);
+ for (auto it = all_card_ptrs.begin(); it < all_card_ptrs.end(); it++) {
+ (*it)->SetExpirationYear(2001);
+ }
+ cards[0]->set_record_type(CreditCard::MASKED_SERVER_CARD);
+
+ // Filter the cards while capturing histograms.
+ base::HistogramTester histogram_tester;
+ AutofillSuggestionGenerator::RemoveExpiredCreditCardsNotUsedSinceTimestamp(
+ kNow, kNow + base::Days(1), &cards);
+
+ // Validate that we get the expected filtered cards and histograms.
+ EXPECT_EQ(1U, cards.size());
+ histogram_tester.ExpectTotalCount(kHistogramName, 1);
+ histogram_tester.ExpectBucketCount(kHistogramName, kNumCards - 1, 1);
+ }
}
TEST_F(AutofillSuggestionGeneratorTest, GetServerCardForLocalCard) {
@@ -266,7 +287,7 @@ TEST_F(AutofillSuggestionGeneratorTest, CreateCreditCardSuggestion_ServerCard) {
EXPECT_EQ(virtual_card_suggestion.frontend_id,
POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY);
- EXPECT_EQ(virtual_card_suggestion.backend_id,
+ EXPECT_EQ(absl::get<std::string>(virtual_card_suggestion.payload),
"00000000-0000-0000-0000-000000000001");
Suggestion real_card_suggestion =
@@ -276,7 +297,7 @@ TEST_F(AutofillSuggestionGeneratorTest, CreateCreditCardSuggestion_ServerCard) {
"");
EXPECT_EQ(real_card_suggestion.frontend_id, 0);
- EXPECT_EQ(real_card_suggestion.backend_id,
+ EXPECT_EQ(absl::get<std::string>(real_card_suggestion.payload),
"00000000-0000-0000-0000-000000000001");
}
@@ -305,7 +326,7 @@ TEST_F(AutofillSuggestionGeneratorTest, CreateCreditCardSuggestion_LocalCard) {
EXPECT_EQ(virtual_card_suggestion.frontend_id,
POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY);
- EXPECT_EQ(virtual_card_suggestion.backend_id,
+ EXPECT_EQ(absl::get<std::string>(virtual_card_suggestion.payload),
"00000000-0000-0000-0000-000000000001");
Suggestion real_card_suggestion =
@@ -315,7 +336,7 @@ TEST_F(AutofillSuggestionGeneratorTest, CreateCreditCardSuggestion_LocalCard) {
"");
EXPECT_EQ(real_card_suggestion.frontend_id, 0);
- EXPECT_EQ(real_card_suggestion.backend_id,
+ EXPECT_EQ(absl::get<std::string>(real_card_suggestion.payload),
"00000000-0000-0000-0000-000000000002");
EXPECT_TRUE(real_card_suggestion.custom_icon.IsEmpty());
}
@@ -383,4 +404,79 @@ TEST_F(AutofillSuggestionGeneratorTest, ShouldShowVirtualCardOption) {
&local_card, form_structure));
}
+TEST_F(AutofillSuggestionGeneratorTest,
+ GetPromoCodeSuggestionsFromPromoCodeOffers_ValidPromoCodes) {
+ std::vector<const AutofillOfferData*> promo_code_offers;
+
+ base::Time expiry = AutofillClock::Now() + base::Days(2);
+ std::vector<GURL> merchant_origins;
+ DisplayStrings display_strings;
+ display_strings.value_prop_text = "test_value_prop_text_1";
+ std::string promo_code = "test_promo_code_1";
+ AutofillOfferData offer1 = AutofillOfferData::FreeListingCouponOffer(
+ /*offer_id=*/1, expiry, merchant_origins,
+ /*offer_details_url=*/GURL("https://offer-details-url.com/"),
+ display_strings, promo_code);
+
+ promo_code_offers.push_back(&offer1);
+
+ DisplayStrings display_strings2;
+ display_strings2.value_prop_text = "test_value_prop_text_2";
+ std::string promo_code2 = "test_promo_code_2";
+ AutofillOfferData offer2 = AutofillOfferData::FreeListingCouponOffer(
+ /*offer_id=*/2, expiry, merchant_origins,
+ /*offer_details_url=*/GURL("https://offer-details-url.com/"),
+ display_strings2, promo_code2);
+
+ promo_code_offers.push_back(&offer2);
+
+ std::vector<Suggestion> promo_code_suggestions =
+ AutofillSuggestionGenerator::GetPromoCodeSuggestionsFromPromoCodeOffers(
+ promo_code_offers);
+ EXPECT_TRUE(promo_code_suggestions.size() == 3);
+
+ EXPECT_EQ(promo_code_suggestions[0].main_text.value, u"test_promo_code_1");
+ EXPECT_EQ(promo_code_suggestions[0].label, u"test_value_prop_text_1");
+ EXPECT_EQ(absl::get<std::string>(promo_code_suggestions[0].payload), "1");
+ EXPECT_EQ(promo_code_suggestions[0].frontend_id,
+ POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY);
+
+ EXPECT_EQ(promo_code_suggestions[1].main_text.value, u"test_promo_code_2");
+ EXPECT_EQ(promo_code_suggestions[1].label, u"test_value_prop_text_2");
+ EXPECT_EQ(absl::get<std::string>(promo_code_suggestions[1].payload), "2");
+ EXPECT_EQ(promo_code_suggestions[1].frontend_id,
+ POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY);
+
+ EXPECT_EQ(promo_code_suggestions[2].main_text.value,
+ l10n_util::GetStringUTF16(
+ IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT));
+ EXPECT_EQ(absl::get<GURL>(promo_code_suggestions[2].payload),
+ offer1.GetOfferDetailsUrl().spec());
+ EXPECT_EQ(promo_code_suggestions[2].frontend_id,
+ POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS);
+}
+
+TEST_F(AutofillSuggestionGeneratorTest,
+ GetPromoCodeSuggestionsFromPromoCodeOffers_InvalidPromoCodeURL) {
+ std::vector<const AutofillOfferData*> promo_code_offers;
+ AutofillOfferData offer;
+ offer.SetPromoCode("test_promo_code_1");
+ offer.SetValuePropTextInDisplayStrings("test_value_prop_text_1");
+ offer.SetOfferIdForTesting(1);
+ offer.SetOfferDetailsUrl(GURL("invalid-url"));
+ promo_code_offers.push_back(&offer);
+
+ std::vector<Suggestion> promo_code_suggestions =
+ AutofillSuggestionGenerator::GetPromoCodeSuggestionsFromPromoCodeOffers(
+ promo_code_offers);
+ EXPECT_TRUE(promo_code_suggestions.size() == 1);
+
+ EXPECT_EQ(promo_code_suggestions[0].main_text.value, u"test_promo_code_1");
+ EXPECT_EQ(promo_code_suggestions[0].label, u"test_value_prop_text_1");
+ EXPECT_FALSE(
+ absl::holds_alternative<GURL>(promo_code_suggestions[0].payload));
+ EXPECT_EQ(promo_code_suggestions[0].frontend_id,
+ POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.cc b/chromium/components/autofill/core/browser/autofill_test_utils.cc
index 162c32f0d35..ec8485a3b14 100644
--- a/chromium/components/autofill/core/browser/autofill_test_utils.cc
+++ b/chromium/components/autofill/core/browser/autofill_test_utils.cc
@@ -582,6 +582,8 @@ CreditCard GetVirtualCard() {
"5555555555554444", // Mastercard
"10", test::NextYear().c_str(), "1");
credit_card.set_record_type(CreditCard::RecordType::VIRTUAL_CARD);
+ credit_card.set_virtual_card_enrollment_state(
+ CreditCard::VirtualCardEnrollmentState::ENROLLED);
CreditCardTestApi(&credit_card).set_network_for_virtual_card(kMasterCard);
return credit_card;
}
@@ -642,52 +644,60 @@ CreditCardCloudTokenData GetCreditCardCloudTokenData2() {
return data;
}
-AutofillOfferData GetCardLinkedOfferData1() {
- AutofillOfferData data;
- data.offer_id = 111;
+AutofillOfferData GetCardLinkedOfferData1(int64_t offer_id) {
// Sets the expiry to be 45 days later.
- data.expiry = AutofillClock::Now() + base::Days(45);
- data.offer_details_url = GURL("http://www.example1.com");
- data.merchant_origins.emplace_back("http://www.example1.com");
- data.display_strings.value_prop_text = "Get 5% off your purchase";
- data.display_strings.see_details_text = "See details";
- data.display_strings.usage_instructions_text =
+ base::Time expiry = AutofillClock::Now() + base::Days(45);
+ GURL offer_details_url = GURL("http://www.example1.com");
+ std::vector<GURL> merchant_origins{offer_details_url};
+ DisplayStrings display_strings;
+ display_strings.value_prop_text = "Get 5% off your purchase";
+ display_strings.see_details_text = "See details";
+ display_strings.usage_instructions_text =
"Check out with this card to activate";
- data.offer_reward_amount = "5%";
- data.eligible_instrument_id.emplace_back(111111);
- return data;
+ std::string offer_reward_amount = "5%";
+ std::vector<int64_t> eligible_instrument_id{111111};
+
+ return AutofillOfferData::GPayCardLinkedOffer(
+ offer_id, expiry, merchant_origins, offer_details_url, display_strings,
+ eligible_instrument_id, offer_reward_amount);
}
-AutofillOfferData GetCardLinkedOfferData2() {
- AutofillOfferData data;
- data.offer_id = 222;
+AutofillOfferData GetCardLinkedOfferData2(int64_t offer_id) {
// Sets the expiry to be 40 days later.
- data.expiry = AutofillClock::Now() + base::Days(40);
- data.offer_details_url = GURL("http://www.example2.com");
- data.merchant_origins.emplace_back("http://www.example2.com");
- data.display_strings.value_prop_text = "Get $10 off your purchase";
- data.display_strings.see_details_text = "See details";
- data.display_strings.usage_instructions_text =
+ base::Time expiry = AutofillClock::Now() + base::Days(40);
+ GURL offer_details_url = GURL("http://www.example2.com");
+ std::vector<GURL> merchant_origins{offer_details_url};
+ DisplayStrings display_strings;
+ display_strings.value_prop_text = "Get $10 off your purchase";
+ display_strings.see_details_text = "See details";
+ display_strings.usage_instructions_text =
"Check out with this card to activate";
- data.offer_reward_amount = "$10";
- data.eligible_instrument_id.emplace_back(222222);
- return data;
+ std::string offer_reward_amount = "$10";
+ std::vector<int64_t> eligible_instrument_id{222222};
+
+ return AutofillOfferData::GPayCardLinkedOffer(
+ offer_id, expiry, merchant_origins, offer_details_url, display_strings,
+ eligible_instrument_id, offer_reward_amount);
}
-AutofillOfferData GetPromoCodeOfferData(GURL origin, bool is_expired) {
- AutofillOfferData data;
- data.offer_id = 333;
+AutofillOfferData GetPromoCodeOfferData(GURL origin,
+ bool is_expired,
+ int64_t offer_id) {
// Sets the expiry to be later if not expired, or earlier if expired.
- data.expiry = is_expired ? AutofillClock::Now() - base::Days(1)
- : AutofillClock::Now() + base::Days(35);
- data.offer_details_url = GURL("http://www.example.com");
- data.merchant_origins.emplace_back(origin);
- data.display_strings.value_prop_text = "5% off on shoes. Up to $50.";
- data.display_strings.see_details_text = "See details";
- data.display_strings.usage_instructions_text =
+ base::Time expiry = is_expired ? AutofillClock::Now() - base::Days(1)
+ : AutofillClock::Now() + base::Days(35);
+ std::vector<GURL> merchant_origins{origin};
+ DisplayStrings display_strings;
+ display_strings.value_prop_text = "5% off on shoes. Up to $50.";
+ display_strings.see_details_text = "See details";
+ display_strings.usage_instructions_text =
"Click the promo code field at checkout to autofill it.";
- data.promo_code = "5PCTOFFSHOES";
- return data;
+ std::string promo_code = "5PCTOFFSHOES";
+ GURL offer_details_url = GURL("https://pay.google.com");
+
+ return AutofillOfferData::GPayPromoCodeOffer(
+ offer_id, expiry, merchant_origins, offer_details_url, display_strings,
+ promo_code);
}
void SetProfileInfo(AutofillProfile* profile,
@@ -977,9 +987,9 @@ std::vector<FormSignature> GetEncodedSignatures(
}
void AddFieldSuggestionToForm(
- const autofill::FormFieldData& field_data,
+ const FormFieldData& field_data,
ServerFieldType field_type,
- ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion) {
+ AutofillQueryResponse_FormSuggestion* form_suggestion) {
auto* field_suggestion = form_suggestion->add_field_suggestions();
field_suggestion->set_field_signature(
CalculateFieldSignatureForField(field_data).value());
@@ -987,9 +997,9 @@ void AddFieldSuggestionToForm(
}
void AddFieldPredictionsToForm(
- const autofill::FormFieldData& field_data,
+ const FormFieldData& field_data,
const std::vector<int>& field_types,
- ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion) {
+ AutofillQueryResponse_FormSuggestion* form_suggestion) {
std::vector<ServerFieldType> types;
for (auto type : field_types) {
types.emplace_back(ToSafeServerFieldType(type, UNKNOWN_TYPE));
@@ -998,9 +1008,9 @@ void AddFieldPredictionsToForm(
}
void AddFieldPredictionsToForm(
- const autofill::FormFieldData& field_data,
+ const FormFieldData& field_data,
const std::vector<ServerFieldType>& field_types,
- ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion) {
+ AutofillQueryResponse_FormSuggestion* form_suggestion) {
auto* field_suggestion = form_suggestion->add_field_suggestions();
field_suggestion->set_field_signature(
CalculateFieldSignatureForField(field_data).value());
diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.h b/chromium/components/autofill/core/browser/autofill_test_utils.h
index 9f000123c99..bbeea1f9057 100644
--- a/chromium/components/autofill/core/browser/autofill_test_utils.h
+++ b/chromium/components/autofill/core/browser/autofill_test_utils.h
@@ -240,18 +240,21 @@ CreditCardCloudTokenData GetCreditCardCloudTokenData1();
// one above.
CreditCardCloudTokenData GetCreditCardCloudTokenData2();
-// Returns an Autofill card-linked offer data full of dummy info.
-AutofillOfferData GetCardLinkedOfferData1();
+// Returns an Autofill card-linked offer data full of dummy info. Use
+// |offer_id| to optionally set the offer id.
+AutofillOfferData GetCardLinkedOfferData1(int64_t offer_id = 111);
// Returns an Autofill card-linked offer data full of dummy info, different from
-// the one above.
-AutofillOfferData GetCardLinkedOfferData2();
+// the one above. Use |offer_id| to optionally set the offer id.
+AutofillOfferData GetCardLinkedOfferData2(int64_t offer_id = 222);
// Returns an Autofill promo code offer data full of dummy info, using |origin|
-// if provided and expired if |is_expired| is true.
+// if provided and expired if |is_expired| is true. Use |offer_id| to optionally
+// set the offer id.
AutofillOfferData GetPromoCodeOfferData(
GURL origin = GURL("http://www.example.com"),
- bool is_expired = false);
+ bool is_expired = false,
+ int64_t offer_id = 333);
// A unit testing utility that is common to a number of the Autofill unit
// tests. |SetProfileInfo| provides a quick way to populate a profile with
diff --git a/chromium/components/autofill/core/browser/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc
index 06716590f4b..ec58ca421cb 100644
--- a/chromium/components/autofill/core/browser/autofill_type.cc
+++ b/chromium/components/autofill/core/browser/autofill_type.cc
@@ -37,6 +37,8 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) {
return FieldTypeGroup::kEmail;
case PHONE_HOME_NUMBER:
+ case PHONE_HOME_NUMBER_PREFIX:
+ case PHONE_HOME_NUMBER_SUFFIX:
case PHONE_HOME_CITY_CODE:
case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
case PHONE_HOME_COUNTRY_CODE:
diff --git a/chromium/components/autofill/core/browser/browser_autofill_manager.cc b/chromium/components/autofill/core/browser/browser_autofill_manager.cc
index bc16e80e580..2dad433c7de 100644
--- a/chromium/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/chromium/components/autofill/core/browser/browser_autofill_manager.cc
@@ -31,6 +31,7 @@
#include "base/i18n/rtl.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/path_service.h"
#include "base/ranges/algorithm.h"
@@ -51,6 +52,7 @@
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_external_delegate.h"
#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_regexes.h"
#include "components/autofill/core/browser/autofill_suggestion_generator.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/browser_autofill_manager_test_delegate.h"
@@ -72,6 +74,7 @@
#include "components/autofill/core/browser/payments/payments_client.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/randomized_encoder.h"
+#include "components/autofill/core/browser/suggestions_context.h"
#include "components/autofill/core/browser/ui/popup_item_ids.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_clock.h"
@@ -180,10 +183,8 @@ ValuePatternsMetric GetValuePattern(const std::u16string& value) {
void LogValuePatternsMetric(const FormData& form) {
for (const FormFieldData& field : form.fields) {
- if (!field.IsVisible()) {
- // Ignore hidden fields.
+ if (!field.IsFocusable())
continue;
- }
std::u16string value;
base::TrimWhitespace(field.value, base::TRIM_ALL, &value);
if (value.empty())
@@ -297,7 +298,7 @@ AutofillField* GetBestPossibleCVCFieldForUpload(
// Some autofill types are detected based on values and not based on form
// features. We may decide that it's an autofill form after submission.
-bool ContainsAutofillableValue(const autofill::FormStructure& form) {
+bool ContainsAutofillableValue(const FormStructure& form) {
return base::ranges::any_of(form, [](const auto& field) {
return base::Contains(field->possible_types(), UPI_VPA) ||
IsUPIVirtualPaymentAddress(field->value);
@@ -411,24 +412,16 @@ BrowserAutofillManager::BrowserAutofillManager(
AutofillDriver* driver,
AutofillClient* client,
const std::string& app_locale,
- AutofillDownloadManagerState enable_download_manager)
- : BrowserAutofillManager(driver,
- client,
- client->GetPersonalDataManager(),
- app_locale,
- enable_download_manager) {}
-
-BrowserAutofillManager::BrowserAutofillManager(
- AutofillDriver* driver,
- AutofillClient* client,
- PersonalDataManager* personal_data,
- const std::string app_locale,
- AutofillDownloadManagerState enable_download_manager)
- : AutofillManager(driver, client, enable_download_manager),
+ EnableDownloadManager enable_download_manager)
+ : AutofillManager(driver,
+ client,
+ client->GetChannel(),
+ enable_download_manager),
external_delegate_(
std::make_unique<AutofillExternalDelegate>(this, driver)),
+ touch_to_fill_delegate_(std::make_unique<TouchToFillDelegate>()),
app_locale_(app_locale),
- personal_data_(personal_data),
+ personal_data_(client->GetPersonalDataManager()),
field_filler_(app_locale, client->GetAddressNormalizer()),
single_field_form_fill_router_(client->GetSingleFieldFormFillRouter()),
suggestion_generator_(
@@ -445,6 +438,8 @@ BrowserAutofillManager::BrowserAutofillManager(
CountryNames::SetLocaleString(app_locale_);
offer_manager_ = client->GetAutofillOfferManager();
+
+ form_interactions_counter_ = std::make_unique<FormInteractionsCounter>();
}
BrowserAutofillManager::~BrowserAutofillManager() {
@@ -459,6 +454,14 @@ BrowserAutofillManager::~BrowserAutofillManager() {
single_field_form_fill_router_->CancelPendingQueries(this);
}
+AutofillOfferManager* BrowserAutofillManager::GetOfferManager() {
+ return offer_manager_;
+}
+
+CreditCardAccessManager* BrowserAutofillManager::GetCreditCardAccessManager() {
+ return credit_card_access_manager_.get();
+}
+
void BrowserAutofillManager::ShowAutofillSettings(
bool show_credit_card_settings) {
client()->ShowAutofillSettings(show_credit_card_settings);
@@ -674,7 +677,7 @@ void BrowserAutofillManager::OnFormSubmittedImpl(const FormData& form,
std::unique_ptr<FormStructure> submitted_form = ValidateSubmittedForm(form);
if (!submitted_form) {
single_field_form_fill_router_->OnWillSubmitForm(
- form, client()->IsAutocompleteEnabled());
+ form, submitted_form.get(), client()->IsAutocompleteEnabled());
return;
}
@@ -717,7 +720,8 @@ void BrowserAutofillManager::OnFormSubmittedImpl(const FormData& form,
}
}
single_field_form_fill_router_->OnWillSubmitForm(
- form_for_autocomplete, client()->IsAutocompleteEnabled());
+ form_for_autocomplete, submitted_form.get(),
+ client()->IsAutocompleteEnabled());
if (IsAutofillProfileEnabled()) {
address_form_event_logger_->OnWillSubmitForm(sync_state_, *submitted_form);
@@ -777,7 +781,7 @@ bool BrowserAutofillManager::MaybeStartVoteUploadProcess(
}
const std::vector<CreditCard*>& credit_cards =
- credit_card_access_manager_->GetCreditCards();
+ personal_data_->GetCreditCards();
if (profiles.empty() && credit_cards.empty())
return false;
@@ -932,6 +936,9 @@ void BrowserAutofillManager::OnTextFieldDidChangeImpl(
}
UpdateInitialInteractionTimestamp(timestamp);
+
+ form_interactions_counter_->OnTextFieldDidChange(
+ autofill_field->GetFieldSignature());
}
bool BrowserAutofillManager::IsFormNonSecure(const FormData& form) const {
@@ -943,7 +950,8 @@ void BrowserAutofillManager::OnAskForValuesToFillImpl(
const FormData& form,
const FormFieldData& field,
const gfx::RectF& transformed_box,
- bool autoselect_first_suggestion) {
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) {
if (base::FeatureList::IsEnabled(features::kAutofillDisableFilling)) {
return;
}
@@ -1043,12 +1051,17 @@ void BrowserAutofillManager::OnAskForValuesToFillImpl(
single_field_form_fill_router_->OnGetSingleFieldSuggestions(
query_id, client()->IsAutocompleteEnabled(),
autoselect_first_suggestion, field.name, field.value,
- field.form_control_type, weak_ptr_factory_.GetWeakPtr());
+ field.form_control_type, weak_ptr_factory_.GetWeakPtr(), context);
return;
}
- // Send Autofill suggestions (could be an empty list).
single_field_form_fill_router_->CancelPendingQueries(this);
+ if (touch_to_fill_eligible &&
+ touch_to_fill_delegate_->TryToShowTouchToFill(query_id, form, field)) {
+ // Touch To Fill is shown.
+ return;
+ }
+ // Send Autofill suggestions (could be an empty list).
external_delegate_->OnSuggestionsReturned(query_id, suggestions,
autoselect_first_suggestion,
context.should_display_gpay_logo);
@@ -1125,7 +1138,8 @@ void BrowserAutofillManager::FillOrPreviewProfileForm(
if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field))
return;
FillOrPreviewDataModelForm(action, query_id, form, field, &profile,
- /*cvc=*/nullptr, form_structure, autofill_field);
+ /*optional_cvc=*/nullptr, form_structure,
+ autofill_field);
}
void BrowserAutofillManager::FillOrPreviewForm(
@@ -1171,10 +1185,9 @@ void BrowserAutofillManager::FillCreditCardForm(int query_id,
autofill_field);
}
-void BrowserAutofillManager::FillProfileForm(
- const autofill::AutofillProfile& profile,
- const FormData& form,
- const FormFieldData& field) {
+void BrowserAutofillManager::FillProfileForm(const AutofillProfile& profile,
+ const FormData& form,
+ const FormFieldData& field) {
FillOrPreviewProfileForm(mojom::RendererFormDataAction::kFill,
/*query_id=*/kNoQueryId, form, field, profile);
}
@@ -1328,7 +1341,7 @@ void BrowserAutofillManager::DidShowSuggestions(bool has_autofill_suggestions,
}
if (autofill_field->Type().group() == FieldTypeGroup::kCreditCard &&
- ::autofill::IsCreditCardFidoAuthenticationEnabled()) {
+ IsCreditCardFidoAuthenticationEnabled()) {
credit_card_access_manager_->PrepareToFetchCreditCard();
}
}
@@ -1412,14 +1425,18 @@ bool BrowserAutofillManager::RemoveAutofillProfileOrCreditCard(int unique_id) {
void BrowserAutofillManager::RemoveCurrentSingleFieldSuggestion(
const std::u16string& name,
- const std::u16string& value) {
- single_field_form_fill_router_->OnRemoveCurrentSingleFieldSuggestion(name,
- value);
+ const std::u16string& value,
+ int frontend_id) {
+ single_field_form_fill_router_->OnRemoveCurrentSingleFieldSuggestion(
+ name, value, frontend_id);
}
void BrowserAutofillManager::OnSingleFieldSuggestionSelected(
- const std::u16string& value) {
- single_field_form_fill_router_->OnSingleFieldSuggestionSelected(value);
+ const std::u16string& value,
+ int frontend_id) {
+ single_field_form_fill_router_->OnSingleFieldSuggestionSelected(value,
+ frontend_id);
+ form_interactions_counter_->OnAutocompleteFill();
}
void BrowserAutofillManager::OnUserHideSuggestions(const FormData& form,
@@ -1438,18 +1455,6 @@ bool BrowserAutofillManager::ShouldClearPreviewedForm() {
return credit_card_access_manager_->ShouldClearPreviewedForm();
}
-payments::FullCardRequest*
-BrowserAutofillManager::GetOrCreateFullCardRequest() {
- return credit_card_access_manager_->GetOrCreateCVCAuthenticator()
- ->GetFullCardRequest();
-}
-
-base::WeakPtr<payments::FullCardRequest::UIDelegate>
-BrowserAutofillManager::GetAsFullCardRequestUIDelegate() {
- return credit_card_access_manager_->GetOrCreateCVCAuthenticator()
- ->GetAsFullCardRequestUIDelegate();
-}
-
void BrowserAutofillManager::SetTestDelegate(
BrowserAutofillManagerTestDelegate* delegate) {
test_delegate_ = delegate;
@@ -1480,10 +1485,109 @@ void BrowserAutofillManager::SelectFieldOptionsDidChange(const FormData& form) {
TriggerRefill(form);
}
+void BrowserAutofillManager::JavaScriptChangedAutofilledValue(
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) {
+ // Log to chrome://autofill-internals that a field's value was set by
+ // JavaScript.
+ if (log_manager()) {
+ auto StructureOfString = [](std::u16string str) {
+ for (auto& c : str) {
+ if (base::IsAsciiAlpha(c)) {
+ c = 'a';
+ } else if (base::IsAsciiDigit(c)) {
+ c = '0';
+ } else if (base::IsAsciiWhitespace(c)) {
+ c = ' ';
+ } else {
+ c = '$';
+ }
+ }
+ return str;
+ };
+ std::string field_number = "unknown";
+ for (size_t i = 0; i < form.fields.size(); ++i) {
+ if (form.fields[i].global_id() == field.global_id()) {
+ field_number = base::StringPrintf("Field %zu", i);
+ }
+ }
+ LogBuffer change;
+ change << Tag{"div"} << Attrib{"class", "form"};
+ change << field << Br{};
+ change << "Old value structure: '"
+ << StructureOfString(old_value.substr(0, 80)) << "'" << Br{};
+ change << "New value structure: '"
+ << StructureOfString(field.value.substr(0, 80)) << "'";
+ log_manager()->Log() << LoggingScope::kWebsiteModifiedFieldValue
+ << LogMessage::kJavaScriptChangedAutofilledValue
+ << Br{} << Tag{"table"} << Tr{} << field_number
+ << std::move(change);
+ }
+
+ MaybeTriggerRefillForExpirationDate(form, field, old_value);
+}
+
+void BrowserAutofillManager::MaybeTriggerRefillForExpirationDate(
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) {
+ // TODO(crbug.com/1314360): Remove these lines once launched.
+ if (!base::FeatureList::IsEnabled(
+ features::kAutofillRefillModifiedCreditCardExpirationDates)) {
+ return;
+ }
+
+ // We currently support a single case of refilling credit card expiration
+ // dates: If we filled the expiration date in a format "05/2023" and the
+ // website turned it into "05 / 20" (i.e. it broke the year by cutting the
+ // last two digits instead of stripping the first two digits).
+ constexpr size_t kSupportedLength = base::StringPiece("MM/YYYY").size();
+ if (old_value.length() != kSupportedLength)
+ return;
+ if (old_value == field.value)
+ return;
+
+ const char16_t* kFormatRegEx = uR"(^(\d\d)(\s?[/-]?\s?)?(\d\d|\d\d\d\d)$)";
+ std::vector<std::u16string> old_groups;
+ if (!MatchesPattern(old_value, kFormatRegEx, &old_groups))
+ return;
+ DCHECK_EQ(old_groups.size(), 4u);
+
+ std::vector<std::u16string> new_groups;
+ if (!MatchesPattern(field.value, kFormatRegEx, &new_groups))
+ return;
+ DCHECK_EQ(new_groups.size(), 4u);
+
+ int old_month, old_year, new_month, new_year;
+ if (!base::StringToInt(old_groups[1], &old_month) ||
+ !base::StringToInt(old_groups[3], &old_year) ||
+ !base::StringToInt(new_groups[1], &new_month) ||
+ !base::StringToInt(new_groups[3], &new_year) ||
+ old_groups[3].size() != 4 || new_groups[3].size() != 2 ||
+ old_month != new_month ||
+ // We need to refill if the first two digits of the year were preserved.
+ old_year / 100 != new_year) {
+ return;
+ }
+
+ std::u16string refill_value = field.value;
+ CHECK(refill_value.size() >= 2);
+ refill_value[refill_value.size() - 1] = '0' + (old_year % 10);
+ refill_value[refill_value.size() - 2] = '0' + ((old_year % 100) / 10);
+
+ FormStructure* form_structure = FindCachedFormByRendererId(form.global_id());
+ if (form_structure && ShouldTriggerRefill(*form_structure)) {
+ FillingContext* filling_context = GetFillingContext(*form_structure);
+ DCHECK(filling_context); // This is enforced by ShouldTriggerRefill.
+ filling_context->forced_fill_values[field.global_id()] = refill_value;
+ ScheduleRefill(form);
+ }
+}
+
void BrowserAutofillManager::PropagateAutofillPredictions(
- content::RenderFrameHost* rfh,
const std::vector<FormStructure*>& forms) {
- client()->PropagateAutofillPredictions(rfh, forms);
+ client()->PropagateAutofillPredictions(driver(), forms);
}
void BrowserAutofillManager::OnCreditCardFetched(CreditCardFetchResult result,
@@ -1532,11 +1636,11 @@ bool BrowserAutofillManager::IsAutofillEnabled() const {
}
bool BrowserAutofillManager::IsAutofillProfileEnabled() const {
- return ::autofill::prefs::IsAutofillProfileEnabled(client()->GetPrefs());
+ return prefs::IsAutofillProfileEnabled(client()->GetPrefs());
}
bool BrowserAutofillManager::IsAutofillCreditCardEnabled() const {
- return ::autofill::prefs::IsAutofillCreditCardEnabled(client()->GetPrefs());
+ return prefs::IsAutofillCreditCardEnabled(client()->GetPrefs());
}
const FormData& BrowserAutofillManager::last_query_form() const {
@@ -1572,7 +1676,7 @@ void BrowserAutofillManager::UploadFormDataAsyncCallback(
submitted_form->LogQualityMetrics(
submitted_form->form_parsed_timestamp(), interaction_time,
submission_time, form_interactions_ukm_logger(), did_show_suggestions_,
- observed_submission);
+ observed_submission, form_interactions_counter_->GetCounts());
}
if (submitted_form->ShouldBeUploaded())
UploadFormData(*submitted_form, observed_submission);
@@ -1632,6 +1736,7 @@ void BrowserAutofillManager::Reset() {
initial_interaction_timestamp_ = TimeTicks();
external_delegate_->Reset();
filling_context_.clear();
+ form_interactions_counter_ = std::make_unique<FormInteractionsCounter>();
}
bool BrowserAutofillManager::RefreshDataModels() {
@@ -1658,23 +1763,24 @@ bool BrowserAutofillManager::RefreshDataModels() {
local_record_type_count);
}
- return !profiles.empty() ||
- !credit_card_access_manager_->GetCreditCards().empty();
+ return !profiles.empty() || !personal_data_->GetCreditCards().empty();
}
CreditCard* BrowserAutofillManager::GetCreditCard(int unique_id) {
// Unpack the |unique_id| into component parts.
std::string credit_card_id;
std::string profile_id;
- SplitFrontendID(unique_id, &credit_card_id, &profile_id);
- return credit_card_access_manager_->GetCreditCard(credit_card_id);
+ suggestion_generator_->SplitFrontendId(unique_id, &credit_card_id,
+ &profile_id);
+ return personal_data_->GetCreditCardByGUID(credit_card_id);
}
AutofillProfile* BrowserAutofillManager::GetProfile(int unique_id) {
// Unpack the |unique_id| into component parts.
std::string credit_card_id;
std::string profile_id;
- SplitFrontendID(unique_id, &credit_card_id, &profile_id);
+ suggestion_generator_->SplitFrontendId(unique_id, &credit_card_id,
+ &profile_id);
if (base::IsValidGUID(profile_id))
return personal_data_->GetProfileByGUID(profile_id);
@@ -1723,8 +1829,7 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm(
// Only record the types that are filled for an eventual refill if all the
// following are satisfied:
- // The refilling feature is enabled.
- // A form with the given name is already filled.
+ // The form is already filled.
// A refill has not been attempted for that form yet.
// This fill is not a refill attempt.
FillingContext* filling_context = GetFillingContext(*form_structure);
@@ -1732,7 +1837,7 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm(
!filling_context->attempted_refill && !is_refill;
// Counts the number of times a type was seen in the section to be filled.
- // This is used to limit the maximum number fills per value.
+ // This is used to limit the maximum number of fills per value.
base::flat_map<ServerFieldType, size_t> type_count;
type_count.reserve(form_structure->field_count());
@@ -1766,9 +1871,9 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm(
AutofillField* cached_field = form_structure->field(i);
FieldTypeGroup field_group_type = cached_field->Type().group();
- // Don't fill hidden fields, with the exception of <select> fields, for
+ // Don't fill unfocusable fields, with the exception of <select> fields, for
// the sake of filling the synthetic fields.
- if (!cached_field->IsVisible()) {
+ if (!cached_field->IsFocusable()) {
bool skip = result.fields[i].form_control_type != "select-one";
form_interactions_ukm_logger()
->LogHiddenRepresentationalFieldSkipDecision(*form_structure,
@@ -1779,6 +1884,10 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm(
}
}
+ bool has_override =
+ filling_context && base::Contains(filling_context->forced_fill_values,
+ form.fields[i].global_id());
+
// Do not override prefilled text/input field values. Selection fields are
// excluded from this check because they may have a non-empty value.
// If the initiating element had a prefilled value but the autofill
@@ -1788,7 +1897,7 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm(
if (base::FeatureList::IsEnabled(
features::kAutofillPreventOverridingPrefilledValues)) {
if (form.fields[i].form_control_type != "select-one" &&
- !form.fields[i].value.empty() &&
+ !form.fields[i].value.empty() && !has_override &&
!FormFieldData::DeepEqual(form.fields[i], field)) {
buffer << Tr{} << field_number << "Skipped: value is prefilled";
std::string unused_failure_to_fill;
@@ -1889,13 +1998,18 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm(
const std::u16string kEmptyCvc{};
std::string failure_to_fill; // Reason for failing to fill.
+ const std::map<FieldGlobalId, std::u16string>& forced_fill_values =
+ filling_context ? filling_context->forced_fill_values
+ : std::map<FieldGlobalId, std::u16string>();
+
// Fill the non-empty value from |profile_or_credit_card| into the |result|
// form, which will be sent to the renderer. FillFieldWithValue() may also
// fill a field if it had been autofilled or manually filled before, and
// also returns true in such a case; however, such fields don't reach this
// code.
bool is_newly_autofilled = FillFieldWithValue(
- cached_field, profile_or_credit_card, &result.fields[i], should_notify,
+ cached_field, profile_or_credit_card, forced_fill_values,
+ &result.fields[i], should_notify,
optional_cvc ? *optional_cvc : kEmptyCvc,
data_util::DetermineGroups(*form_structure), action, &failure_to_fill);
if (is_newly_autofilled)
@@ -1910,7 +2024,7 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm(
has_value_before, has_value_after, is_autofilled_before,
is_autofilled_after, failure_to_fill.c_str());
- if (!cached_field->IsVisible() && result.fields[i].is_autofilled)
+ if (!cached_field->IsFocusable() && result.fields[i].is_autofilled)
AutofillMetrics::LogHiddenOrPresentationalSelectFieldsFilled();
}
buffer << CTag{"table"};
@@ -1945,18 +2059,26 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm(
credit_card_form_event_logger_->OnDidFillSuggestion(
credit_card_, *form_structure, *autofill_field, newly_filled_fields,
base::flat_set<FieldGlobalId>(std::move(safe_fields)), sync_state_);
+ form_interactions_counter_->OnAutofillFill();
}
if (!is_credit_card) {
address_form_event_logger_->OnDidFillSuggestion(
*absl::get<const AutofillProfile*>(profile_or_credit_card),
*form_structure, *autofill_field, sync_state_);
+ form_interactions_counter_->OnAutofillFill();
}
}
// Note that this may invalidate |profile_or_credit_card|.
if (action == mojom::RendererFormDataAction::kFill && !is_refill)
personal_data_->RecordUseOf(profile_or_credit_card);
+
+ if (filling_context) {
+ // When a new preview/fill starts, previously forced_fill_values should be
+ // ignored the operation could be for a different card or address.
+ filling_context->forced_fill_values.clear();
+ }
}
std::unique_ptr<FormStructure> BrowserAutofillManager::ValidateSubmittedForm(
@@ -2016,34 +2138,8 @@ std::vector<Suggestion> BrowserAutofillManager::GetProfileSuggestions(
const AutofillField& autofill_field) const {
address_form_event_logger_->OnDidPollSuggestions(field, sync_state_);
- std::vector<ServerFieldType> field_types(form.field_count());
- for (size_t i = 0; i < form.field_count(); ++i) {
- field_types.push_back(form.field(i)->Type().GetStorableType());
- }
-
- std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
- autofill_field.Type(), field.value, field.is_autofilled, field_types);
-
- // Adjust phone number to display in prefix/suffix case.
- if (autofill_field.Type().group() == FieldTypeGroup::kPhoneHome) {
- for (auto& suggestion : suggestions) {
- const AutofillProfile* profile =
- personal_data_->GetProfileByGUID(suggestion.backend_id);
- if (profile) {
- const std::u16string phone_home_city_and_number =
- profile->GetInfo(PHONE_HOME_CITY_AND_NUMBER, app_locale_);
- suggestion.value = FieldFiller::GetPhoneNumberValueForInput(
- autofill_field, suggestion.value, phone_home_city_and_number,
- field);
- }
- }
- }
-
- for (size_t i = 0; i < suggestions.size(); ++i) {
- suggestions[i].frontend_id =
- MakeFrontendID(std::string(), suggestions[i].backend_id);
- }
- return suggestions;
+ return suggestion_generator_->GetSuggestionsForProfiles(
+ form, field, autofill_field, app_locale_);
}
std::vector<Suggestion> BrowserAutofillManager::GetCreditCardSuggestions(
@@ -2053,22 +2149,10 @@ std::vector<Suggestion> BrowserAutofillManager::GetCreditCardSuggestions(
bool* should_display_gpay_logo) const {
credit_card_form_event_logger_->OnDidPollSuggestions(field, sync_state_);
- *should_display_gpay_logo =
- credit_card_access_manager_->ShouldDisplayGPayLogo();
-
std::vector<Suggestion> suggestions;
if (!IsInAutofillSuggestionsDisabledExperiment()) {
suggestions = suggestion_generator_->GetSuggestionsForCreditCards(
- form_structure, field, type, app_locale_);
- }
-
- // TODO(crbug.com/1196021): Once the profile suggestion creation is moved to
- // AutofillSuggestionGenerator, move this part as well.
- for (Suggestion& suggestion : suggestions) {
- if (suggestion.frontend_id == 0) {
- suggestion.frontend_id =
- MakeFrontendID(suggestion.backend_id, std::string());
- }
+ form_structure, field, type, app_locale_, should_display_gpay_logo);
}
credit_card_form_event_logger_->set_suggestions(suggestions);
@@ -2118,21 +2202,8 @@ void BrowserAutofillManager::OnFormProcessed(
// If a form with the same name was previously filled, and there has not
// been a refill attempt on that form yet, start the process of triggering a
// refill.
- if (ShouldTriggerRefill(form_structure)) {
- FillingContext* filling_context = GetFillingContext(form_structure);
- DCHECK(filling_context != nullptr);
-
- // If a timer for the refill was already running, it means the form
- // changed again. Stop the timer and start it again.
- if (filling_context->on_refill_timer.IsRunning())
- filling_context->on_refill_timer.AbandonAndStop();
-
- // Start a new timer to trigger refill.
- filling_context->on_refill_timer.Start(
- FROM_HERE, kWaitTimeForDynamicForms,
- base::BindRepeating(&BrowserAutofillManager::TriggerRefill,
- weak_ptr_factory_.GetWeakPtr(), form));
- }
+ if (ShouldTriggerRefill(form_structure))
+ ScheduleRefill(form);
}
void BrowserAutofillManager::OnAfterProcessParsedForms(
@@ -2149,68 +2220,6 @@ void BrowserAutofillManager::OnAfterProcessParsedForms(
#endif
}
-int BrowserAutofillManager::BackendIDToInt(
- const std::string& backend_id) const {
- if (!base::IsValidGUID(backend_id))
- return 0;
-
- const auto found = backend_to_int_map_.find(backend_id);
- if (found == backend_to_int_map_.end()) {
- // Unknown one, make a new entry.
- int int_id = backend_to_int_map_.size() + 1;
- backend_to_int_map_[backend_id] = int_id;
- int_to_backend_map_[int_id] = backend_id;
- return int_id;
- }
- return found->second;
-}
-
-std::string BrowserAutofillManager::IntToBackendID(int int_id) const {
- if (int_id == 0)
- return std::string();
-
- const auto found = int_to_backend_map_.find(int_id);
- if (found == int_to_backend_map_.end()) {
- NOTREACHED();
- return std::string();
- }
- return found->second;
-}
-
-// When sending IDs (across processes) to the renderer we pack credit card and
-// profile IDs into a single integer. Credit card IDs are sent in the high
-// word and profile IDs are sent in the low word.
-int BrowserAutofillManager::MakeFrontendID(
- const std::string& cc_backend_id,
- const std::string& profile_backend_id) const {
- int cc_int_id = BackendIDToInt(cc_backend_id);
- int profile_int_id = BackendIDToInt(profile_backend_id);
-
- // Should fit in signed 16-bit integers. We use 16-bits each when combining
- // below, and negative frontend IDs have special meaning so we can never use
- // the high bit.
- DCHECK(cc_int_id <= std::numeric_limits<int16_t>::max());
- DCHECK(profile_int_id <= std::numeric_limits<int16_t>::max());
-
- // Put CC in the high half of the bits.
- return (cc_int_id << std::numeric_limits<uint16_t>::digits) | profile_int_id;
-}
-
-// When receiving IDs (across processes) from the renderer we unpack credit card
-// and profile IDs from a single integer. Credit card IDs are stored in the
-// high word and profile IDs are stored in the low word.
-void BrowserAutofillManager::SplitFrontendID(
- int frontend_id,
- std::string* cc_backend_id,
- std::string* profile_backend_id) const {
- int cc_int_id = (frontend_id >> std::numeric_limits<uint16_t>::digits) &
- std::numeric_limits<uint16_t>::max();
- int profile_int_id = frontend_id & std::numeric_limits<uint16_t>::max();
-
- *cc_backend_id = IntToBackendID(cc_int_id);
- *profile_backend_id = IntToBackendID(profile_int_id);
-}
-
void BrowserAutofillManager::UpdateInitialInteractionTimestamp(
const TimeTicks& interaction_timestamp) {
if (initial_interaction_timestamp_.is_null() ||
@@ -2421,15 +2430,16 @@ bool BrowserAutofillManager::FillFieldWithValue(
AutofillField* autofill_field,
absl::variant<const AutofillProfile*, const CreditCard*>
profile_or_credit_card,
+ const std::map<FieldGlobalId, std::u16string>& forced_fill_values,
FormFieldData* field_data,
bool should_notify,
const std::u16string& cvc,
uint32_t profile_form_bitmask,
mojom::RendererFormDataAction action,
std::string* failure_to_fill) {
- bool filled_field =
- field_filler_.FillFormField(*autofill_field, profile_or_credit_card,
- field_data, cvc, action, failure_to_fill);
+ bool filled_field = field_filler_.FillFormField(
+ *autofill_field, profile_or_credit_card, forced_fill_values, field_data,
+ cvc, action, failure_to_fill);
if (filled_field) {
if (failure_to_fill)
*failure_to_fill = "Decided to fill";
@@ -2451,7 +2461,8 @@ bool BrowserAutofillManager::FillFieldWithValue(
const AutofillProfile* profile =
absl::get<const AutofillProfile*>(profile_or_credit_card);
client()->DidFillOrPreviewField(
- /*value=*/profile->GetInfo(autofill_field->Type(), app_locale_),
+ /*autofilled_value=*/profile->GetInfo(autofill_field->Type(),
+ app_locale_),
/*profile_full_name=*/profile->GetInfo(AutofillType(NAME_FULL),
app_locale_));
}
@@ -2493,6 +2504,26 @@ bool BrowserAutofillManager::ShouldTriggerRefill(
return !filling_context->attempted_refill && delta < kLimitBeforeRefill;
}
+void BrowserAutofillManager::ScheduleRefill(const FormData& form) {
+ FormStructure* form_structure = FindCachedFormByRendererId(form.global_id());
+ if (!form_structure)
+ return;
+
+ FillingContext* filling_context = GetFillingContext(*form_structure);
+ DCHECK(filling_context != nullptr);
+
+ // If a timer for the refill was already running, it means the form
+ // changed again. Stop the timer and start it again.
+ if (filling_context->on_refill_timer.IsRunning())
+ filling_context->on_refill_timer.AbandonAndStop();
+
+ // Start a new timer to trigger refill.
+ filling_context->on_refill_timer.Start(
+ FROM_HERE, kWaitTimeForDynamicForms,
+ base::BindRepeating(&BrowserAutofillManager::TriggerRefill,
+ weak_ptr_factory_.GetWeakPtr(), form));
+}
+
void BrowserAutofillManager::TriggerRefill(const FormData& form) {
FormStructure* form_structure = FindCachedFormByRendererId(form.global_id());
if (!form_structure)
@@ -2526,12 +2557,13 @@ void BrowserAutofillManager::TriggerRefill(const FormData& form) {
}
// If the field was deleted, look for one with a matching signature. Prefer
- // visible and newer fields over invisible or older ones.
+ // focusable and newer fields over invisible or older ones.
if (autofill_field == nullptr) {
auto is_better = [](const AutofillField& f, const AutofillField& g) {
- return std::forward_as_tuple(f.IsVisible(),
+ return std::forward_as_tuple(f.IsFocusable(),
f.unique_renderer_id.value()) >
- std::forward_as_tuple(g.IsVisible(), g.unique_renderer_id.value());
+ std::forward_as_tuple(g.IsFocusable(),
+ g.unique_renderer_id.value());
};
for (const std::unique_ptr<AutofillField>& field : *form_structure) {
@@ -2822,4 +2854,37 @@ void BrowserAutofillManager::PreProcessStateMatchingTypes(
}
}
+void BrowserAutofillManager::ReportAutofillWebOTPMetrics(bool used_web_otp) {
+ // It's possible that a frame without any form uses WebOTP. e.g. a server may
+ // send the verification code to a phone number that was collected beforehand
+ // and uses the WebOTP API for authentication purpose without user manually
+ // entering the code.
+ if (!has_parsed_forms() && !used_web_otp)
+ return;
+
+ if (has_observed_phone_number_field())
+ phone_collection_metric_state_ |= phone_collection_metric::kPhoneCollected;
+ if (has_observed_one_time_code_field())
+ phone_collection_metric_state_ |= phone_collection_metric::kOTCUsed;
+ if (used_web_otp)
+ phone_collection_metric_state_ |= phone_collection_metric::kWebOTPUsed;
+
+ ukm::UkmRecorder* recorder = client()->GetUkmRecorder();
+ ukm::SourceId source_id = client()->GetUkmSourceId();
+ AutofillMetrics::LogWebOTPPhoneCollectionMetricStateUkm(
+ recorder, source_id, phone_collection_metric_state_);
+
+ base::UmaHistogramEnumeration(
+ "Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
+ static_cast<PhoneCollectionMetricState>(phone_collection_metric_state_));
+}
+
+void BrowserAutofillManager::OnSeePromoCodeOfferDetailsSelected(
+ const GURL& offer_details_url,
+ const std::u16string& value,
+ int frontend_id) {
+ client()->OpenPromoCodeOfferDetailsURL(offer_details_url);
+ OnSingleFieldSuggestionSelected(value, frontend_id);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/browser_autofill_manager.h b/chromium/components/autofill/core/browser/browser_autofill_manager.h
index 0040d007eea..d6d33df9090 100644
--- a/chromium/components/autofill/core/browser/browser_autofill_manager.h
+++ b/chromium/components/autofill/core/browser/browser_autofill_manager.h
@@ -29,6 +29,7 @@
#include "components/autofill/core/browser/form_types.h"
#include "components/autofill/core/browser/metrics/form_events/address_form_event_logger.h"
#include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h"
+#include "components/autofill/core/browser/metrics/form_interactions_counter.h"
#include "components/autofill/core/browser/payments/autofill_offer_manager.h"
#include "components/autofill/core/browser/payments/card_unmask_delegate.h"
#include "components/autofill/core/browser/payments/credit_card_access_manager.h"
@@ -36,6 +37,7 @@
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/single_field_form_fill_router.h"
#include "components/autofill/core/browser/sync_utils.h"
+#include "components/autofill/core/browser/touch_to_fill_delegate.h"
#include "components/autofill/core/browser/ui/popup_types.h"
#include "components/autofill/core/common/dense_set.h"
#include "components/autofill/core/common/form_data.h"
@@ -59,6 +61,30 @@ class FormStructureBrowserTest;
struct FormData;
struct FormFieldData;
+struct SuggestionsContext;
+
+// Use <Phone><WebOTP><OTC> as the bit pattern to identify the metrics state.
+enum class PhoneCollectionMetricState {
+ kNone = 0, // Site did not collect phone, not use OTC, not use WebOTP
+ kOTC = 1, // Site used OTC only
+ kWebOTP = 2, // Site used WebOTP only
+ kWebOTPPlusOTC = 3, // Site used WebOTP and OTC
+ kPhone = 4, // Site collected phone, not used neither WebOTP nor OTC
+ kPhonePlusOTC = 5, // Site collected phone number and used OTC
+ kPhonePlusWebOTP = 6, // Site collected phone number and used WebOTP
+ kPhonePlusWebOTPPlusOTC = 7, // Site collected phone number and used both
+ kMaxValue = kPhonePlusWebOTPPlusOTC,
+};
+
+namespace phone_collection_metric {
+constexpr uint32_t kOTCUsed = 1 << 0;
+constexpr uint32_t kWebOTPUsed = 1 << 1;
+constexpr uint32_t kPhoneCollected = 1 << 2;
+} // namespace phone_collection_metric
+
+namespace metrics {
+class AutofillMetricsBaseTest;
+}
// We show the credit card signin promo only a certain number of times.
constexpr int kCreditCardSigninPromoImpressionLimit = 3;
@@ -81,7 +107,7 @@ class BrowserAutofillManager : public AutofillManager,
BrowserAutofillManager(AutofillDriver* driver,
AutofillClient* client,
const std::string& app_locale,
- AutofillDownloadManagerState enable_download_manager);
+ EnableDownloadManager enable_download_manager);
BrowserAutofillManager(const BrowserAutofillManager&) = delete;
BrowserAutofillManager& operator=(const BrowserAutofillManager&) = delete;
@@ -136,20 +162,20 @@ class BrowserAutofillManager : public AutofillManager,
const FormData& form,
const FormFieldData& field,
int unique_id);
- virtual void FillCreditCardForm(int query_id,
- const FormData& form,
- const FormFieldData& field,
- const CreditCard& credit_card,
- const std::u16string& cvc);
+ void FillCreditCardForm(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const CreditCard& credit_card,
+ const std::u16string& cvc) override;
void DidShowSuggestions(bool has_autofill_suggestions,
const FormData& form,
const FormFieldData& field);
// Called only from Autofill Assistant through
// ContentAutofillDriver::FillFormForAssistant().
- virtual void FillProfileForm(const autofill::AutofillProfile& profile,
- const FormData& form,
- const FormFieldData& field);
+ void FillProfileForm(const AutofillProfile& profile,
+ const FormData& form,
+ const FormFieldData& field) override;
// Fetches the related virtual card information given the related actual card
// |guid| and fills the information into the form.
@@ -171,33 +197,22 @@ class BrowserAutofillManager : public AutofillManager,
// from the database. Returns true if deletion is allowed.
bool RemoveAutofillProfileOrCreditCard(int unique_id);
- // Remove the specified suggestion from single field filling.
+ // Remove the specified suggestion from single field filling. |frontend_id| is
+ // the PopupItemId of the suggestion.
void RemoveCurrentSingleFieldSuggestion(const std::u16string& name,
- const std::u16string& value);
+ const std::u16string& value,
+ int frontend_id);
// Invoked when the user selected |value| in a suggestions list from single
- // field filling.
- void OnSingleFieldSuggestionSelected(const std::u16string& value);
+ // field filling. |frontend_id| is the PopupItemId of the suggestion.
+ void OnSingleFieldSuggestionSelected(const std::u16string& value,
+ int frontend_id);
// Invoked when the user selects the "Hide Suggestions" item in the
// Autocomplete drop-down.
virtual void OnUserHideSuggestions(const FormData& form,
const FormFieldData& field);
- // Returns true only if the previewed form should be cleared.
- bool ShouldClearPreviewedForm();
-
- AutofillOfferManager* offer_manager() { return offer_manager_; }
-
- CreditCardAccessManager* credit_card_access_manager() {
- return credit_card_access_manager_.get();
- }
-
- payments::FullCardRequest* GetOrCreateFullCardRequest();
-
- base::WeakPtr<payments::FullCardRequest::UIDelegate>
- GetAsFullCardRequestUIDelegate();
-
const std::string& app_locale() const { return app_locale_; }
// Only for testing.
@@ -223,6 +238,9 @@ class BrowserAutofillManager : public AutofillManager,
void DidSuppressPopup(const FormData& form, const FormFieldData& field);
// AutofillManager:
+ AutofillOfferManager* GetOfferManager() override;
+ CreditCardAccessManager* GetCreditCardAccessManager() override;
+ bool ShouldClearPreviewedForm() override;
void OnFocusNoLongerOnForm(bool had_interacted_form) override;
void OnFocusOnFormFieldImpl(const FormData& form,
const FormFieldData& field,
@@ -233,8 +251,11 @@ class BrowserAutofillManager : public AutofillManager,
void OnDidEndTextFieldEditing() override;
void OnHidePopup() override;
void SelectFieldOptionsDidChange(const FormData& form) override;
+ void JavaScriptChangedAutofilledValue(
+ const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value) override;
void PropagateAutofillPredictions(
- content::RenderFrameHost* rfh,
const std::vector<FormStructure*>& forms) override;
void Reset() override;
@@ -272,12 +293,31 @@ class BrowserAutofillManager : public AutofillManager,
return has_observed_one_time_code_field_;
}
+ // Reports whether a document collects phone numbers, uses one time code, uses
+ // WebOTP. There are cases that the reporting is not expected:
+ // 1. some unit tests do not set necessary members,
+ // |browser_autofill_manager_|
+ // 2. there is no form and WebOTP is not used
+ void ReportAutofillWebOTPMetrics(bool used_web_otp) override;
+
+ // Handles the logic for when the user selects to see promo code offer
+ // details. It opens a new tab and navigates to the offer details page, and
+ // then logs that the promo code suggestions footer was selected.
+ void OnSeePromoCodeOfferDetailsSelected(const GURL& offer_details_url,
+ const std::u16string& value,
+ int frontend_id);
+
#if defined(UNIT_TEST)
void SetExternalDelegateForTest(
std::unique_ptr<AutofillExternalDelegate> external_delegate) {
external_delegate_ = std::move(external_delegate);
}
+ void SetTouchToFillDelegateForTest(
+ std::unique_ptr<TouchToFillDelegate> touch_to_fill_delegate) {
+ touch_to_fill_delegate_ = std::move(touch_to_fill_delegate);
+ }
+
// A public wrapper that calls |DeterminePossibleFieldTypesForUpload| for
// testing purposes only.
static void DeterminePossibleFieldTypesForUploadForTest(
@@ -291,12 +331,6 @@ class BrowserAutofillManager : public AutofillManager,
app_locale, submitted_form);
}
- // A public wrapper that calls |MakeFrontendID| for testing purposes only.
- int MakeFrontendIDForTest(const std::string& cc_backend_id,
- const std::string& profile_backend_id) const {
- return MakeFrontendID(cc_backend_id, profile_backend_id);
- }
-
// A public wrapper that calls |ShouldTriggerRefill| for testing purposes
// only.
bool ShouldTriggerRefillForTest(const FormStructure& form_structure) {
@@ -313,17 +347,13 @@ class BrowserAutofillManager : public AutofillManager,
FormStructure* form_structure) {
PreProcessStateMatchingTypes(profiles, form_structure);
}
-#endif
- protected:
- // Test code should prefer to use this constructor.
- BrowserAutofillManager(AutofillDriver* driver,
- AutofillClient* client,
- PersonalDataManager* personal_data,
- const std::string app_locale = "en-US",
- AutofillDownloadManagerState enable_download_manager =
- DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+ AutofillSuggestionGenerator* suggestion_generator() {
+ return suggestion_generator_.get();
+ }
+#endif // defined(UNIT_TEST)
+ protected:
// Uploads the form data to the Autofill server. |observed_submission|
// indicates that upload is the result of a submission event.
virtual void UploadFormData(const FormStructure& submitted_form,
@@ -338,20 +368,6 @@ class BrowserAutofillManager : public AutofillManager,
const base::TimeTicks& submission_time,
bool observed_submission);
- // Maps suggestion backend ID to and from an integer identifying it. Two of
- // these intermediate integers are packed by MakeFrontendID to make the IDs
- // that this class generates for the UI and for IPC.
- virtual int BackendIDToInt(const std::string& backend_id) const;
- virtual std::string IntToBackendID(int int_id) const;
-
- // Methods for packing and unpacking credit card and profile IDs for sending
- // and receiving to and from the renderer process.
- int MakeFrontendID(const std::string& cc_backend_id,
- const std::string& profile_backend_id) const;
- void SplitFrontendID(int frontend_id,
- std::string* cc_backend_id,
- std::string* profile_backend_id) const;
-
// AutofillManager:
void OnFormSubmittedImpl(const FormData& form,
bool known_success,
@@ -363,11 +379,13 @@ class BrowserAutofillManager : public AutofillManager,
void OnTextFieldDidScrollImpl(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) override {}
- void OnAskForValuesToFillImpl(int query_id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& transformed_box,
- bool autoselect_first_suggestion) override;
+ void OnAskForValuesToFillImpl(
+ int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& transformed_box,
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) override;
void OnSelectControlDidChangeImpl(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
@@ -433,57 +451,9 @@ class BrowserAutofillManager : public AutofillManager,
base::OneShotTimer on_refill_timer;
// The field type groups that were initially filled.
std::set<FieldTypeGroup> type_groups_originally_filled;
- };
-
- // Indicates the reason why autofill suggestions are suppressed.
- enum class SuppressReason {
- kNotSuppressed,
- // Suggestions are not shown because an ablation experiment is enabled.
- kAblation,
- // Address suggestions are not shown because the field is annotated with
- // autocomplete=off and the directive is being observed by the browser.
- kAutocompleteOff,
- // Suggestions are not shown because this form is on a secure site, but
- // submits insecurely. This is only used when the user has started typing,
- // otherwise a warning is shown.
- kInsecureForm,
- // Suggestions are not shown because the field is annotated with
- // an unrecognized autocompelte attribute and the field is not credit card
- // related. For credit card fields, the unrecognized attribute is ignored.
- kAutocompleteUnrecognized,
- };
-
- // The context for the list of suggestions available for a given field to be
- // returned by GetAvailableSuggestions().
- struct SuggestionsContext {
- FormStructure* form_structure = nullptr;
- AutofillField* focused_field = nullptr;
- bool is_autofill_available = false;
- bool is_context_secure = false;
- bool is_filling_credit_card = false;
- // Flag to indicate whether all suggestions come from Google Payments.
- bool should_display_gpay_logo = false;
- SuppressReason suppress_reason = SuppressReason::kNotSuppressed;
- // Indicates whether the form filling is under ablation, meaning that
- // autofill popups are suppressed.
- AblationGroup ablation_group = AblationGroup::kDefault;
- // Indicates whether the form filling is under ablation, under the condition
- // that the user has data to fill on file. All users that don't have data
- // to fill are in the AbationGroup::kDefault.
- // Note that it is possible (due to implementation details) that this is
- // incorrectly set to kDefault: If the user has typed some characters into a
- // text field, it may look like no suggestions are available, but in
- // practice the suggestions are just filtered out (Autofill only suggests
- // matches that start with the typed prefix). Any consumers of the
- // conditional_ablation_group attribute should monitor it over time.
- // Any transitions of conditional_ablation_group from {kAblation,
- // kControl} to kDefault should just be ignored and the previously reported
- // value should be used. As the ablation experience is stable within a day,
- // such a transition typically indicates that the user has type a prefix
- // which led to the filtering of all autofillable data. In short: once
- // either kAblation or kControl were reported, consumers should stick to
- // that.
- AblationGroup conditional_ablation_group = AblationGroup::kDefault;
+ // If populated, this map determines which values will be filled into a
+ // field (it does not matter whether the field already contains a value).
+ std::map<FieldGlobalId, std::u16string> forced_fill_values;
};
// CreditCardAccessManager::Accessor
@@ -495,6 +465,8 @@ class BrowserAutofillManager : public AutofillManager,
// Returns false if Autofill is disabled or if no Autofill data is available.
bool RefreshDataModels();
+ // TODO(crbug.com/1249665): Change unique_id to frontend_id and move the
+ // functions to AutofillSuggestionGenerator.
// Gets the card referred to by the guid |unique_id|. Returns |nullptr| if
// card does not exist.
CreditCard* GetCreditCard(int unique_id);
@@ -621,6 +593,7 @@ class BrowserAutofillManager : public AutofillManager,
AutofillField* autofill_field,
absl::variant<const AutofillProfile*, const CreditCard*>
profile_or_credit_card,
+ const std::map<FieldGlobalId, std::u16string>& forced_fill_values,
FormFieldData* field_data,
bool should_notify,
const std::u16string& cvc,
@@ -641,10 +614,21 @@ class BrowserAutofillManager : public AutofillManager,
// It's been less than kLimitBeforeRefillMs since the original fill.
bool ShouldTriggerRefill(const FormStructure& form_structure);
+ // Schedules a call of TriggerRefill. Virtual for testing.
+ virtual void ScheduleRefill(const FormData& form);
+
// Attempts to refill the form that was changed dynamically. Should only be
// called if ShouldTriggerRefill returns true.
void TriggerRefill(const FormData& form);
+ // This function is called by JavaScriptChangedAutofilledValue and may trigger
+ // a refill in case the website used JavaScript to reformat an expiration date
+ // like "05/2023" into "05 / 20" (i.e. it broke the year by cutting the last
+ // two digits instead of stripping the first two digits).
+ void MaybeTriggerRefillForExpirationDate(const FormData& form,
+ const FormFieldData& field,
+ const std::u16string& old_value);
+
// Replaces the contents of |suggestions| with available suggestions for
// |field|. |context| will contain additional information about the
// suggestions, such as if they correspond to credit card suggestions and
@@ -675,9 +659,10 @@ class BrowserAutofillManager : public AutofillManager,
void SetDataList(const std::vector<std::u16string>& values,
const std::vector<std::u16string>& labels);
- // Delegate to perform external processing (display, selection) on
+ // Delegates to perform external processing (display, selection) on
// our behalf.
std::unique_ptr<AutofillExternalDelegate> external_delegate_;
+ std::unique_ptr<TouchToFillDelegate> touch_to_fill_delegate_;
std::string app_locale_;
@@ -748,12 +733,6 @@ class BrowserAutofillManager : public AutofillManager,
CreditCard credit_card_;
std::u16string last_unlocked_credit_card_cvc_;
- // Suggestion backend ID to ID mapping. We keep two maps to convert back and
- // forth. These should be used only by BackendIDToInt and IntToBackendID.
- // Note that the integers are not frontend IDs.
- mutable std::map<std::string, int> backend_to_int_map_;
- mutable std::map<int, std::string> int_to_backend_map_;
-
// Delegate used in test to get notifications on certain events.
raw_ptr<BrowserAutofillManagerTestDelegate> test_delegate_ = nullptr;
@@ -765,11 +744,29 @@ class BrowserAutofillManager : public AutofillManager,
// interaction and re-used throughout the context of this manager.
AutofillSyncSigninState sync_state_ = AutofillSyncSigninState::kNumSyncStates;
+ // Used to keep track of user interactions with text fields, Autocomplete and
+ // Autofill.
+ std::unique_ptr<FormInteractionsCounter> form_interactions_counter_;
+
+ // Helps with measuring whether phone number is collected and whether it is in
+ // conjunction with WebOTP or OneTimeCode (OTC).
+ // value="0" label="Phone Not Collected, WebOTP Not Used, OTC Not Used"
+ // value="1" label="Phone Not Collected, WebOTP Not Used, OTC Used"
+ // value="2" label="Phone Not Collected, WebOTP Used, OTC Not Used"
+ // value="3" label="Phone Not Collected, WebOTP Used, OTC Used"
+ // value="4" label="Phone Collected, WebOTP Not Used, OTC Not Used"
+ // value="5" label="Phone Collected, WebOTP Not Used, OTC Used"
+ // value="6" label="Phone Collected, WebOTP Used, OTC Not Used"
+ // value="7" label="Phone Collected, WebOTP Used, OTC Used"
+ uint32_t phone_collection_metric_state_ = 0;
+
base::WeakPtrFactory<BrowserAutofillManager> weak_ptr_factory_{this};
friend class AutofillAssistantTest;
+ friend class AutofillMetricsCrossFrameFormTest;
friend class BrowserAutofillManagerTest;
friend class AutofillMetricsTest;
+ friend class metrics::AutofillMetricsBaseTest;
friend class FormStructureBrowserTest;
friend class GetMatchingTypesTest;
friend class CreditCardAccessoryControllerTest;
diff --git a/chromium/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index 152b7c9f964..b287800c248 100644
--- a/chromium/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -35,12 +35,14 @@
#include "build/chromeos_buildflags.h"
#include "components/autofill/core/browser/autocomplete_history_manager.h"
#include "components/autofill/core/browser/autofill_download_manager.h"
+#include "components/autofill/core/browser/autofill_suggestion_generator.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h"
#include "components/autofill/core/browser/metrics/form_events/form_events.h"
#include "components/autofill/core/browser/mock_autocomplete_history_manager.h"
+#include "components/autofill/core/browser/mock_merchant_promo_code_manager.h"
#include "components/autofill/core/browser/mock_single_field_form_fill_router.h"
#include "components/autofill/core/browser/payments/test_credit_card_save_manager.h"
#include "components/autofill/core/browser/payments/test_credit_card_save_strike_database.h"
@@ -104,6 +106,7 @@ using testing::UnorderedElementsAre;
namespace autofill {
+using features::kAutofillRemoveCardExpiryFromDownstreamSuggestion;
using mojom::SubmissionIndicatorEvent;
using mojom::SubmissionSource;
@@ -160,6 +163,19 @@ class MockAutofillDownloadManager : public TestAutofillDownloadManager {
(override));
};
+class MockTouchToFillDelegate : public TouchToFillDelegate {
+ public:
+ MockTouchToFillDelegate() = default;
+ MockTouchToFillDelegate(const MockTouchToFillDelegate&) = delete;
+ MockTouchToFillDelegate& operator=(const MockTouchToFillDelegate&) = delete;
+ ~MockTouchToFillDelegate() override = default;
+
+ MOCK_METHOD(bool,
+ TryToShowTouchToFill,
+ (int query_id, const FormData& form, const FormFieldData& field),
+ (override));
+};
+
void ExpectFilledField(const char* expected_label,
const char* expected_name,
const char* expected_value,
@@ -348,17 +364,17 @@ class BrowserAutofillManagerTest : public testing::Test {
void SetUp() override {
autofill_client_.SetPrefs(test::PrefServiceForTesting());
- personal_data_.set_auto_accept_address_imports_for_testing(true);
- personal_data_.Init(/*profile_database=*/database_,
- /*account_database=*/nullptr,
- /*pref_service=*/autofill_client_.GetPrefs(),
- /*local_state=*/autofill_client_.GetPrefs(),
- /*identity_manager=*/nullptr,
- /*history_service=*/nullptr,
- /*strike_database=*/nullptr,
- /*image_fetcher=*/nullptr,
- /*is_off_the_record=*/false);
- personal_data_.SetPrefService(autofill_client_.GetPrefs());
+ personal_data().set_auto_accept_address_imports_for_testing(true);
+ personal_data().Init(/*profile_database=*/database_,
+ /*account_database=*/nullptr,
+ /*pref_service=*/autofill_client_.GetPrefs(),
+ /*local_state=*/autofill_client_.GetPrefs(),
+ /*identity_manager=*/nullptr,
+ /*history_service=*/nullptr,
+ /*strike_database=*/nullptr,
+ /*image_fetcher=*/nullptr,
+ /*is_off_the_record=*/false);
+ personal_data().SetPrefService(autofill_client_.GetPrefs());
autocomplete_history_manager_ =
std::make_unique<NiceMock<MockAutocompleteHistoryManager>>();
@@ -366,31 +382,35 @@ class BrowserAutofillManagerTest : public testing::Test {
/*profile_database=*/database_,
/*pref_service=*/autofill_client_.GetPrefs(),
/*is_off_the_record=*/false);
+ merchant_promo_code_manager_ =
+ std::make_unique<NiceMock<MockMerchantPromoCodeManager>>();
+ merchant_promo_code_manager_->Init(&personal_data(),
+ /*is_off_the_record=*/false);
- autofill_driver_ =
- std::make_unique<testing::NiceMock<MockAutofillDriver>>();
+ autofill_driver_ = std::make_unique<NiceMock<MockAutofillDriver>>();
auto payments_client = std::make_unique<payments::TestPaymentsClient>(
autofill_driver_->GetURLLoaderFactory(),
- autofill_client_.GetIdentityManager(), &personal_data_);
+ autofill_client_.GetIdentityManager(), &personal_data());
payments_client_ = payments_client.get();
autofill_client_.set_test_payments_client(std::move(payments_client));
TestCreditCardSaveManager* credit_card_save_manager =
new TestCreditCardSaveManager(autofill_driver_.get(), &autofill_client_,
- payments_client_, &personal_data_);
+ payments_client_, &personal_data());
credit_card_save_manager->SetCreditCardUploadEnabled(true);
TestFormDataImporter* test_form_data_importer = new TestFormDataImporter(
&autofill_client_, payments_client_,
std::unique_ptr<CreditCardSaveManager>(credit_card_save_manager),
- &personal_data_, "en-US");
+ &personal_data(), "en-US");
autofill_client_.set_test_form_data_importer(
std::unique_ptr<autofill::TestFormDataImporter>(
test_form_data_importer));
browser_autofill_manager_ = std::make_unique<TestBrowserAutofillManager>(
- autofill_driver_.get(), &autofill_client_, &personal_data_);
+ autofill_driver_.get(), &autofill_client_);
auto single_field_form_fill_router =
std::make_unique<NiceMock<MockSingleFieldFormFillRouter>>(
- autocomplete_history_manager_.get());
+ autocomplete_history_manager_.get(),
+ merchant_promo_code_manager_.get());
single_field_form_fill_router_ = single_field_form_fill_router.get();
browser_autofill_manager_->set_single_field_form_fill_router_for_test(
std::move(single_field_form_fill_router));
@@ -408,6 +428,11 @@ class BrowserAutofillManagerTest : public testing::Test {
browser_autofill_manager_->SetExternalDelegateForTest(
std::move(external_delegate));
+ auto touch_to_fill_delegate = std::make_unique<MockTouchToFillDelegate>();
+ touch_to_fill_delegate_ = touch_to_fill_delegate.get();
+ browser_autofill_manager_->SetTouchToFillDelegateForTest(
+ std::move(touch_to_fill_delegate));
+
auto test_strike_database = std::make_unique<TestStrikeDatabase>();
strike_database_ = test_strike_database.get();
autofill_client_.set_test_strike_database(std::move(test_strike_database));
@@ -418,7 +443,7 @@ class BrowserAutofillManagerTest : public testing::Test {
}
void CreateTestServerCreditCards() {
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard masked_server_card;
test::SetCreditCardInfo(&masked_server_card, "Elvis Presley",
@@ -426,7 +451,7 @@ class BrowserAutofillManagerTest : public testing::Test {
"04", "2999", "1");
masked_server_card.set_guid("00000000-0000-0000-0000-000000000007");
masked_server_card.set_record_type(CreditCard::MASKED_SERVER_CARD);
- personal_data_.AddServerCreditCard(masked_server_card);
+ personal_data().AddServerCreditCard(masked_server_card);
CreditCard full_server_card;
test::SetCreditCardInfo(&full_server_card, "Buddy Holly",
@@ -434,11 +459,11 @@ class BrowserAutofillManagerTest : public testing::Test {
"10", "2998", "1");
full_server_card.set_guid("00000000-0000-0000-0000-000000000008");
full_server_card.set_record_type(CreditCard::FULL_SERVER_CARD);
- personal_data_.AddServerCreditCard(full_server_card);
+ personal_data().AddServerCreditCard(full_server_card);
}
void CreateTestServerAndLocalCreditCards() {
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard masked_server_card;
test::SetCreditCardInfo(&masked_server_card, "Elvis Presley",
@@ -446,7 +471,7 @@ class BrowserAutofillManagerTest : public testing::Test {
"04", "2999", "1");
masked_server_card.set_guid("00000000-0000-0000-0000-000000000007");
masked_server_card.set_record_type(CreditCard::MASKED_SERVER_CARD);
- personal_data_.AddServerCreditCard(masked_server_card);
+ personal_data().AddServerCreditCard(masked_server_card);
CreditCard full_server_card;
test::SetCreditCardInfo(&full_server_card, "Buddy Holly",
@@ -454,7 +479,7 @@ class BrowserAutofillManagerTest : public testing::Test {
"10", "2998", "1");
full_server_card.set_guid("00000000-0000-0000-0000-000000000008");
full_server_card.set_record_type(CreditCard::FULL_SERVER_CARD);
- personal_data_.AddServerCreditCard(full_server_card);
+ personal_data().AddServerCreditCard(full_server_card);
CreditCard local_card;
test::SetCreditCardInfo(&local_card, "Elvis Presley",
@@ -462,7 +487,7 @@ class BrowserAutofillManagerTest : public testing::Test {
"04", "2999", "1");
local_card.set_guid("00000000-0000-0000-0000-000000000009");
local_card.set_record_type(CreditCard::LOCAL_CARD);
- personal_data_.AddCreditCard(local_card);
+ personal_data().AddCreditCard(local_card);
}
void TearDown() override {
@@ -470,8 +495,8 @@ class BrowserAutofillManagerTest : public testing::Test {
// PersonalDataManager to be around when it gets destroyed.
browser_autofill_manager_.reset();
- personal_data_.SetPrefService(nullptr);
- personal_data_.ClearCreditCards();
+ personal_data().SetPrefService(nullptr);
+ personal_data().ClearCreditCards();
}
void GetAutofillSuggestions(int query_id,
@@ -479,7 +504,7 @@ class BrowserAutofillManagerTest : public testing::Test {
const FormFieldData& field) {
browser_autofill_manager_->OnAskForValuesToFill(
query_id, form, field, gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ /*autoselect_first_suggestion=*/false, TouchToFillEligible(false));
}
void GetAutofillSuggestions(const FormData& form,
@@ -487,6 +512,15 @@ class BrowserAutofillManagerTest : public testing::Test {
GetAutofillSuggestions(kDefaultPageID, form, field);
}
+ void TryToShowTouchToFill(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ TouchToFillEligible touch_to_fill_eligible) {
+ browser_autofill_manager_->OnAskForValuesToFill(
+ query_id, form, field, gfx::RectF(),
+ /*autoselect_first_suggestion=*/false, touch_to_fill_eligible);
+ }
+
void AutocompleteSuggestionsReturned(
const std::vector<std::u16string>& results,
int query_id = kDefaultPageID) {
@@ -551,9 +585,10 @@ class BrowserAutofillManagerTest : public testing::Test {
action, guid, input_query_id, input_form, input_field);
}
- int MakeFrontendID(const std::string& cc_sid,
+ int MakeFrontendId(const std::string& cc_sid,
const std::string& profile_sid) const {
- return browser_autofill_manager_->MakeFrontendID(cc_sid, profile_sid);
+ return browser_autofill_manager_->suggestion_generator()->MakeFrontendId(
+ cc_sid, profile_sid);
}
bool WillFillCreditCardNumber(const FormData& form,
@@ -704,28 +739,33 @@ class BrowserAutofillManagerTest : public testing::Test {
// |browser_autofill_manager_| owns the |single_field_form_fill_router_| and
// clears it upon being recreated. Clear it first and then give it a new
// SingleFieldFormFillRouter to avoid referencing deleted memory.
- browser_autofill_manager_.reset();
browser_autofill_manager_ = std::make_unique<TestBrowserAutofillManager>(
- autofill_driver_.get(), &autofill_client_, &personal_data_);
+ autofill_driver_.get(), &autofill_client_);
auto single_field_form_fill_router =
std::make_unique<NiceMock<MockSingleFieldFormFillRouter>>(
- autocomplete_history_manager_.get());
+ autocomplete_history_manager_.get(),
+ merchant_promo_code_manager_.get());
single_field_form_fill_router_ = single_field_form_fill_router.get();
browser_autofill_manager_->set_single_field_form_fill_router_for_test(
std::move(single_field_form_fill_router));
}
protected:
+ TestPersonalDataManager& personal_data() {
+ return *autofill_client_.GetPersonalDataManager();
+ }
+
base::test::TaskEnvironment task_environment_;
NiceMock<MockAutofillClient> autofill_client_;
std::unique_ptr<MockAutofillDriver> autofill_driver_;
std::unique_ptr<TestBrowserAutofillManager> browser_autofill_manager_;
raw_ptr<TestAutofillExternalDelegate> external_delegate_;
+ raw_ptr<MockTouchToFillDelegate> touch_to_fill_delegate_;
scoped_refptr<AutofillWebDataService> database_;
raw_ptr<MockAutofillDownloadManager> download_manager_;
- TestPersonalDataManager personal_data_;
std::unique_ptr<MockAutocompleteHistoryManager> autocomplete_history_manager_;
+ std::unique_ptr<MockMerchantPromoCodeManager> merchant_promo_code_manager_;
raw_ptr<MockSingleFieldFormFillRouter> single_field_form_fill_router_;
base::test::ScopedFeatureList scoped_feature_list_;
raw_ptr<TestStrikeDatabase> strike_database_;
@@ -748,20 +788,20 @@ class BrowserAutofillManagerTest : public testing::Test {
"Apt. 10", "Memphis", "Tennessee", "38116", "US",
"12345678901");
profile1.set_guid("00000000-0000-0000-0000-000000000001");
- personal_data_.AddProfile(profile1);
+ personal_data().AddProfile(profile1);
AutofillProfile profile2;
test::SetProfileInfo(&profile2, "Charles", "Hardin", "Holley",
"buddy@gmail.com", "Decca", "123 Apple St.", "unit 6",
"Lubbock", "Texas", "79401", "US", "23456789012");
profile2.set_guid("00000000-0000-0000-0000-000000000002");
- personal_data_.AddProfile(profile2);
+ personal_data().AddProfile(profile2);
AutofillProfile profile3;
test::SetProfileInfo(&profile3, "", "", "", "", "", "", "", "", "", "", "",
"");
profile3.set_guid("00000000-0000-0000-0000-000000000003");
- personal_data_.AddProfile(profile3);
+ personal_data().AddProfile(profile3);
}
void CreateTestCreditCards() {
@@ -772,7 +812,7 @@ class BrowserAutofillManagerTest : public testing::Test {
credit_card1.set_guid("00000000-0000-0000-0000-000000000004");
credit_card1.set_use_count(10);
credit_card1.set_use_date(AutofillClock::Now() - base::Days(5));
- personal_data_.AddCreditCard(credit_card1);
+ personal_data().AddCreditCard(credit_card1);
CreditCard credit_card2;
test::SetCreditCardInfo(&credit_card2, "Buddy Holly",
@@ -781,12 +821,12 @@ class BrowserAutofillManagerTest : public testing::Test {
credit_card2.set_guid("00000000-0000-0000-0000-000000000005");
credit_card2.set_use_count(5);
credit_card2.set_use_date(AutofillClock::Now() - base::Days(4));
- personal_data_.AddCreditCard(credit_card2);
+ personal_data().AddCreditCard(credit_card2);
CreditCard credit_card3;
test::SetCreditCardInfo(&credit_card3, "", "", "", "", "");
credit_card3.set_guid("00000000-0000-0000-0000-000000000006");
- personal_data_.AddCreditCard(credit_card3);
+ personal_data().AddCreditCard(credit_card3);
}
};
@@ -1182,10 +1222,12 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
OnSingleFieldSuggestionSelected) {
std::u16string test_value = u"TestValue";
EXPECT_CALL(*single_field_form_fill_router_,
- OnSingleFieldSuggestionSelected(test_value))
+ OnSingleFieldSuggestionSelected(test_value,
+ POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY))
.Times(1);
- browser_autofill_manager_->OnSingleFieldSuggestionSelected(test_value);
+ browser_autofill_manager_->OnSingleFieldSuggestionSelected(
+ test_value, POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
}
// Test that we return all address profile suggestions when all form fields
@@ -1280,21 +1322,21 @@ TEST_P(SuggestionMatchingTest,
profile1.SetInfo(NAME_FIRST, u"Robin", "en-US");
profile1.SetInfo(NAME_LAST, u"Grimes", "en-US");
profile1.SetInfo(ADDRESS_HOME_LINE1, u"1234 Smith Blvd.", "en-US");
- personal_data_.AddProfile(profile1);
+ personal_data().AddProfile(profile1);
AutofillProfile profile2;
profile2.set_guid("00000000-0000-0000-0000-000000000124");
profile2.SetInfo(NAME_FIRST, u"Carl", "en-US");
profile2.SetInfo(NAME_LAST, u"Grimes", "en-US");
profile2.SetInfo(ADDRESS_HOME_LINE1, u"1234 Smith Blvd.", "en-US");
- personal_data_.AddProfile(profile2);
+ personal_data().AddProfile(profile2);
AutofillProfile profile3;
profile3.set_guid("00000000-0000-0000-0000-000000000126");
profile3.SetInfo(NAME_FIRST, u"Aaron", "en-US");
profile3.SetInfo(NAME_LAST, u"Googler", "en-US");
profile3.SetInfo(ADDRESS_HOME_LINE1, u"1600 Amphitheater pkwy", "en-US");
- personal_data_.AddProfile(profile3);
+ personal_data().AddProfile(profile3);
FormFieldData field;
test::CreateTestFormField("Last Name", "lastname", "G", "text", &field);
@@ -1387,9 +1429,9 @@ TEST_P(SuggestionMatchingTest, GetProfileSuggestions_WithDuplicates) {
FormsSeen(forms);
// Add a duplicate profile.
- AutofillProfile duplicate_profile = *(personal_data_.GetProfileWithGUID(
+ AutofillProfile duplicate_profile = *(personal_data().GetProfileWithGUID(
"00000000-0000-0000-0000-000000000001"));
- personal_data_.AddProfile(duplicate_profile);
+ personal_data().AddProfile(duplicate_profile);
const FormFieldData& field = form.fields[0];
GetAutofillSuggestions(form, field);
@@ -1614,7 +1656,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
"5255667890123123", // Mastercard
"08", "2017", "1");
credit_card.set_guid("00000000-0000-0000-0000-000000000007");
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
// Set up our form data.
FormData form;
@@ -1675,7 +1717,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// field is the credit card number field.
TEST_P(CreditCardSuggestionTest, GetCreditCardSuggestions_CCNumber) {
// Set nickname with the corresponding guid of the Mastercard 8765.
- personal_data_.SetNicknameForCardWithGUID(
+ personal_data().SetNicknameForCardWithGUID(
"00000000-0000-0000-0000-000000000005", kArbitraryNickname);
// Set up our form data.
FormData form;
@@ -1719,7 +1761,7 @@ TEST_P(CreditCardSuggestionTest, GetCreditCardSuggestions_CCNumber) {
// field is not the credit card number field.
TEST_P(CreditCardSuggestionTest, GetCreditCardSuggestions_NonCCNumber) {
// Set nickname with the corresponding guid of the Mastercard 8765.
- personal_data_.SetNicknameForCardWithGUID(
+ personal_data().SetNicknameForCardWithGUID(
"00000000-0000-0000-0000-000000000005", kArbitraryNickname);
// Set up our form data.
FormData form;
@@ -1780,7 +1822,7 @@ TEST_P(CreditCardSuggestionTest, GetCreditCardSuggestions_NonCCNumber) {
// AutofillExternalDelegateTest that test whether the promo is added.
TEST_P(BrowserAutofillManagerStructuredProfileTest,
GetCreditCardSuggestions_OnlySigninPromo) {
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
// Set up our form data.
FormData form;
@@ -1832,7 +1874,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// Clear the test credit cards and try again -- we should still show the
// mixed form warning.
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
GetAutofillSuggestions(form, field);
CheckSuggestions(
kDefaultPageID,
@@ -1924,7 +1966,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
"05", "2999", "1");
credit_card.set_guid("00000000-0000-0000-0000-000000000007");
credit_card.set_use_date(AutofillClock::Now() - base::Days(15));
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
// Set up our form data.
FormData form;
@@ -1967,7 +2009,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
TEST_P(BrowserAutofillManagerStructuredProfileTest,
GetCreditCardSuggestions_MaskedCardWithMoreThan6Digits) {
// Add a masked server card.
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard masked_server_card;
test::SetCreditCardInfo(&masked_server_card, "Elvis Presley",
@@ -1975,8 +2017,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
"04", "2999", "1");
masked_server_card.set_guid("00000000-0000-0000-0000-000000000007");
masked_server_card.set_record_type(CreditCard::MASKED_SERVER_CARD);
- personal_data_.AddServerCreditCard(masked_server_card);
- EXPECT_EQ(1U, personal_data_.GetCreditCards().size());
+ personal_data().AddServerCreditCard(masked_server_card);
+ EXPECT_EQ(1U, personal_data().GetCreditCards().size());
// Set up our form data.
FormData form;
@@ -1991,11 +2033,11 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
external_delegate_->CheckNoSuggestions(kDefaultPageID);
}
-// Test that expired cards are ordered by frecency and are always suggested
-// after non expired cards even if they have a higher frecency score.
+// Test that expired cards are ordered by their ranking score and are always
+// suggested after non expired cards even if they have a higher ranking score.
TEST_P(BrowserAutofillManagerStructuredProfileTest,
GetCreditCardSuggestions_ExpiredCards) {
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
// Add a never used non expired credit card.
CreditCard credit_card0("002149C1-EE28-4213-A3B9-DA243FFF021B",
@@ -2004,9 +2046,9 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
"5105105105105100" /* Mastercard */, "04", "2055",
"1");
credit_card0.set_guid("00000000-0000-0000-0000-000000000001");
- personal_data_.AddCreditCard(credit_card0);
+ personal_data().AddCreditCard(credit_card0);
- // Add an expired card with a higher frecency score.
+ // Add an expired card with a higher ranking score.
CreditCard credit_card1("287151C8-6AB1-487C-9095-28E80BE5DA15",
test::kEmptyOrigin);
test::SetCreditCardInfo(&credit_card1, "Clyde Barrow",
@@ -2015,9 +2057,9 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
credit_card1.set_guid("00000000-0000-0000-0000-000000000002");
credit_card1.set_use_count(300);
credit_card1.set_use_date(AutofillClock::Now() - base::Days(10));
- personal_data_.AddCreditCard(credit_card1);
+ personal_data().AddCreditCard(credit_card1);
- // Add an expired card with a lower frecency score.
+ // Add an expired card with a lower ranking score.
CreditCard credit_card2("1141084B-72D7-4B73-90CF-3D6AC154673B",
test::kEmptyOrigin);
credit_card2.set_use_count(3);
@@ -2025,9 +2067,9 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
test::SetCreditCardInfo(&credit_card2, "John Dillinger",
"4234567890123456" /* Visa */, "01", "2011", "1");
credit_card2.set_guid("00000000-0000-0000-0000-000000000003");
- personal_data_.AddCreditCard(credit_card2);
+ personal_data().AddCreditCard(credit_card2);
- ASSERT_EQ(3U, personal_data_.GetCreditCards().size());
+ ASSERT_EQ(3U, personal_data().GetCreditCards().size());
// Set up our form data.
FormData form;
@@ -2067,47 +2109,37 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// enabled and the input field is empty.
TEST_P(BrowserAutofillManagerStructuredProfileTest,
GetCreditCardSuggestions_SuppressDisusedCreditCardsOnEmptyField) {
- personal_data_.ClearCreditCards();
- ASSERT_EQ(0U, personal_data_.GetCreditCards().size());
+ personal_data().ClearCreditCards();
+ ASSERT_EQ(0U, personal_data().GetCreditCards().size());
// Add a never used non expired local credit card.
- CreditCard credit_card0("002149C1-EE28-4213-A3B9-DA243FFF021B",
+ CreditCard credit_card0("00000000-0000-0000-0000-000000000000",
test::kEmptyOrigin);
test::SetCreditCardInfo(&credit_card0, "Bonnie Parker",
"5105105105105100" /* Mastercard */, "04", "2999",
"1");
- credit_card0.set_guid("00000000-0000-0000-0000-000000000000");
- personal_data_.AddCreditCard(credit_card0);
+ personal_data().AddCreditCard(credit_card0);
auto now = AutofillClock::Now();
- // Add an expired unmasked card last used 10 days ago
- CreditCard credit_card1(CreditCard::FULL_SERVER_CARD, "c789");
+ // Add an expired local card last used 10 days ago
+ CreditCard credit_card1("00000000-0000-0000-0000-000000000001",
+ test::kEmptyOrigin);
test::SetCreditCardInfo(&credit_card1, "Clyde Barrow",
"4234567890123456" /* Visa */, "04", "2010", "1");
credit_card1.set_use_date(now - base::Days(10));
- credit_card1.set_guid("00000000-0000-0000-0000-000000000001");
- personal_data_.AddServerCreditCard(credit_card1);
-
- // Add an expired masked card last used 180 days ago.
- CreditCard credit_card2(CreditCard::MASKED_SERVER_CARD, "c987");
- test::SetCreditCardInfo(&credit_card2, "Jane Doe", "6543", "01", "2010", "1");
- credit_card2.set_use_date(now - base::Days(181));
- credit_card2.SetNetworkForMaskedCard(kVisaCard);
- credit_card2.set_guid("00000000-0000-0000-0000-000000000002");
- personal_data_.AddServerCreditCard(credit_card2);
+ personal_data().AddCreditCard(credit_card1);
// Add an expired local card last used 180 days ago.
- CreditCard credit_card3("1141084B-72D7-4B73-90CF-3D6AC154673B",
+ CreditCard credit_card2("00000000-0000-0000-0000-000000000002",
test::kEmptyOrigin);
- credit_card3.set_use_date(now - base::Days(182));
- test::SetCreditCardInfo(&credit_card3, "John Dillinger",
+ credit_card2.set_use_date(now - base::Days(182));
+ test::SetCreditCardInfo(&credit_card2, "John Dillinger",
"378282246310005" /* American Express */, "01",
"2010", "1");
- credit_card3.set_guid("00000000-0000-0000-0000-000000000003");
- personal_data_.AddCreditCard(credit_card3);
+ personal_data().AddCreditCard(credit_card2);
- ASSERT_EQ(4U, personal_data_.GetCreditCards().size());
+ ASSERT_EQ(3U, personal_data().GetCreditCards().size());
// Set up our form data.
FormData form;
@@ -2194,7 +2226,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
browser_autofill_manager_->GetPackedCreditCardID(1)));
}
- // Query with name prefix for card3 returns card3.
+ // Query with name prefix for card2 returns card2.
{
FormFieldData field = form.fields[0];
field.value = u"Jo";
@@ -2214,35 +2246,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
CheckSuggestions(
kDefaultPageID,
Suggestion("John Dillinger", amex_label, kAmericanExpressCard,
- browser_autofill_manager_->GetPackedCreditCardID(3)));
- }
-
- // Query with card number prefix for card1 returns card1 and card2.
- // Expired masked card2 is shown when user starts to type credit card
- // number because we are not sure if it is the masked card that they want.
- {
- FormFieldData field = form.fields[1];
- field.value = u"4234";
- GetAutofillSuggestions(form, field);
-
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
- const std::string visa_label1 = std::string("04/10");
- const std::string visa_label2 = std::string("01/10");
-#else
- const std::string visa_label1 = std::string("Expires on 04/10");
- const std::string visa_label2 = std::string("Expires on 01/10");
-#endif
-
- CheckSuggestions(
- kDefaultPageID,
- Suggestion(
- std::string("Visa ") + test::ObfuscatedCardDigitsAsUTF8("3456"),
- visa_label1, kVisaCard,
- browser_autofill_manager_->GetPackedCreditCardID(1)),
- Suggestion(
- std::string("Visa ") + test::ObfuscatedCardDigitsAsUTF8("6543"),
- visa_label2, kVisaCard,
- browser_autofill_manager_->GetPackedCreditCardID(2)));
+ browser_autofill_manager_->GetPackedCreditCardID(2)));
}
}
@@ -2253,8 +2257,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
GetCreditCardSuggestions_NumberMissing) {
// Create one normal credit card and one credit card with the number
// missing.
- personal_data_.ClearCreditCards();
- ASSERT_EQ(0U, personal_data_.GetCreditCards().size());
+ personal_data().ClearCreditCards();
+ ASSERT_EQ(0U, personal_data().GetCreditCards().size());
CreditCard credit_card0("287151C8-6AB1-487C-9095-28E80BE5DA15",
test::kEmptyOrigin);
@@ -2262,16 +2266,16 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
"378282246310005" /* American Express */, "04",
"2999", "1");
credit_card0.set_guid("00000000-0000-0000-0000-000000000001");
- personal_data_.AddCreditCard(credit_card0);
+ personal_data().AddCreditCard(credit_card0);
CreditCard credit_card1("1141084B-72D7-4B73-90CF-3D6AC154673B",
test::kEmptyOrigin);
test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2999",
"1");
credit_card1.set_guid("00000000-0000-0000-0000-000000000002");
- personal_data_.AddCreditCard(credit_card1);
+ personal_data().AddCreditCard(credit_card1);
- ASSERT_EQ(2U, personal_data_.GetCreditCards().size());
+ ASSERT_EQ(2U, personal_data().GetCreditCards().size());
// Set up our form data.
FormData form;
@@ -2321,6 +2325,44 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
browser_autofill_manager_->GetPackedCreditCardID(1)));
}
+// Test that a suggestion does not have label (expiration date) if the
+// suggestion is poped up from a credit card number field.
+TEST_F(BrowserAutofillManagerTest,
+ GetCreditCardSuggestions_NoLabelForCCNumberField) {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillRemoveCardExpiryFromDownstreamSuggestion);
+
+ personal_data().ClearCreditCards();
+ ASSERT_EQ(0U, personal_data().GetCreditCards().size());
+
+ CreditCard credit_card0("287151C8-6AB1-487C-9095-28E80BE5DA15",
+ test::kEmptyOrigin);
+ test::SetCreditCardInfo(&credit_card0, "Clyde Barrow",
+ "378282246310005" /* American Express */, "04",
+ "2999", "1");
+ credit_card0.set_guid("00000000-0000-0000-0000-000000000001");
+ personal_data().AddCreditCard(credit_card0);
+
+ ASSERT_EQ(1U, personal_data().GetCreditCards().size());
+
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Query by card number field.
+ FormFieldData field = form.fields[1];
+ GetAutofillSuggestions(form, field);
+
+ CheckSuggestions(
+ kDefaultPageID,
+ Suggestion(
+ std::string("Amex ") + test::ObfuscatedCardDigitsAsUTF8("0005"),
+ std::string(), kAmericanExpressCard,
+ browser_autofill_manager_->GetPackedCreditCardID(1)));
+}
+
// Test that we return profile and credit card suggestions for combined forms.
TEST_P(SuggestionMatchingTest, GetAddressAndCreditCardSuggestions) {
// Set up our form data.
@@ -2418,7 +2460,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
"", "", -1));
// Clear the test credit cards and try again -- we shouldn't return a warning.
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
GetAutofillSuggestions(form, field);
external_delegate_->CheckNoSuggestions(kDefaultPageID);
}
@@ -2489,7 +2531,7 @@ TEST_F(BrowserAutofillManagerTest, FillTriggeredSection) {
}
const char guid[] = "00000000-0000-0000-0000-000000000001";
- AutofillProfile* profile = personal_data_.GetProfileWithGUID(guid);
+ AutofillProfile* profile = personal_data().GetProfileWithGUID(guid);
ASSERT_TRUE(profile);
EXPECT_EQ(1U, profile->use_count());
EXPECT_NE(base::Time(), profile->use_date());
@@ -2498,7 +2540,7 @@ TEST_F(BrowserAutofillManagerTest, FillTriggeredSection) {
FormData response_data;
FillAutofillFormDataAndSaveResults(
kDefaultPageID, form, form.fields[index_of_trigger_field],
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
// Extract the sections into individual forms to reduce boiler plate code.
size_t mid = response_data.fields.size() / 2;
FormData section1 = response_data;
@@ -2535,7 +2577,7 @@ TEST_F(BrowserAutofillManagerTest, DoNotFillIfFormFieldChanged) {
*it = FormFieldData();
const char guid[] = "00000000-0000-0000-0000-000000000001";
- AutofillProfile* profile = personal_data_.GetProfileWithGUID(guid);
+ AutofillProfile* profile = personal_data().GetProfileWithGUID(guid);
ASSERT_TRUE(profile);
int response_query_id = 0;
@@ -2573,7 +2615,7 @@ TEST_F(BrowserAutofillManagerTest, DoNotFillIfFormFieldRemoved) {
form.fields.pop_back();
const char guid[] = "00000000-0000-0000-0000-000000000001";
- AutofillProfile* profile = personal_data_.GetProfileWithGUID(guid);
+ AutofillProfile* profile = personal_data().GetProfileWithGUID(guid);
ASSERT_TRUE(profile);
EXPECT_CALL(*autofill_driver_, FillOrPreviewForm(_, _, _, _, _)).Times(0);
@@ -2702,8 +2744,8 @@ TEST_P(BrowserAutofillManagerLogAblationTest, TestLogging) {
<< static_cast<int>(form_type));
if (!params.run_with_data_on_file) {
- personal_data_.ClearAllServerData();
- personal_data_.ClearAllLocalData();
+ personal_data().ClearAllServerData();
+ personal_data().ClearAllLocalData();
}
DisableAutofillViaAblation(scoped_feature_list_, /*for_addresses=*/true,
@@ -3048,7 +3090,7 @@ TEST_P(SuggestionMatchingTest, GetFieldSuggestionsWithDuplicateValues) {
test::SetProfileInfo(&profile, "Elvis", "", "", "", "", "", "", "", "", "",
"", "");
profile.set_guid("00000000-0000-0000-0000-000000000101");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
FormFieldData& field = form.fields[0];
field.is_autofilled = true;
@@ -3081,7 +3123,7 @@ TEST_P(SuggestionMatchingTest, GetProfileSuggestions_FancyPhone) {
profile.set_guid("00000000-0000-0000-0000-000000000103");
profile.SetInfo(NAME_FULL, u"Natty Bumppo", "en-US");
profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"1800PRAIRIE");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
const FormFieldData& field = form.fields[9];
GetAutofillSuggestions(form, field);
@@ -3169,11 +3211,11 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
std::vector<FormData> forms(1, form);
FormsSeen(forms);
- personal_data_.ClearProfiles();
+ personal_data().ClearProfiles();
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000104");
profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"1800FLOWERS");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
const FormFieldData& phone_prefix = form.fields[2];
GetAutofillSuggestions(form, phone_prefix);
@@ -3203,8 +3245,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
profile.set_guid("00000000-0000-0000-0000-000000000103");
profile.SetInfo(NAME_FULL, u"Natty Bumppo", "en-US");
profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"+886123456789");
- personal_data_.ClearProfiles();
- personal_data_.AddProfile(profile);
+ personal_data().ClearProfiles();
+ personal_data().AddProfile(profile);
const FormFieldData& field = form.fields[9];
GetAutofillSuggestions(form, field);
@@ -3247,12 +3289,12 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
std::vector<FormData> forms(1, form);
FormsSeen(forms);
- personal_data_.ClearProfiles();
+ personal_data().ClearProfiles();
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000103");
profile.SetRawInfo(NAME_FULL, u"Natty Bumppo");
profile.SetRawInfo(EMAIL_ADDRESS, u"test@example.com");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
GetAutofillSuggestions(form, form.fields[2]);
CheckSuggestions(kDefaultPageID,
@@ -3268,7 +3310,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillAddressForm) {
FormsSeen(forms);
const char guid[] = "00000000-0000-0000-0000-000000000001";
- AutofillProfile* profile = personal_data_.GetProfileWithGUID(guid);
+ AutofillProfile* profile = personal_data().GetProfileWithGUID(guid);
ASSERT_TRUE(profile);
EXPECT_EQ(1U, profile->use_count());
EXPECT_NE(base::Time(), profile->use_date());
@@ -3276,7 +3318,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillAddressForm) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
ExpectFilledAddressFormElvis(response_page_id, response_data, kDefaultPageID,
false);
@@ -3295,13 +3337,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, WillFillCreditCardNumber) {
FormFieldData* number_field = nullptr;
FormFieldData* name_field = nullptr;
FormFieldData* month_field = nullptr;
- for (size_t i = 0; i < form.fields.size(); ++i) {
- if (form.fields[i].name == u"cardnumber")
- number_field = &form.fields[i];
- else if (form.fields[i].name == u"nameoncard")
- name_field = &form.fields[i];
- else if (form.fields[i].name == u"ccmonth")
- month_field = &form.fields[i];
+ for (auto& field : form.fields) {
+ if (field.name == u"cardnumber")
+ number_field = &field;
+ else if (field.name == u"nameoncard")
+ name_field = &field;
+ else if (field.name == u"ccmonth")
+ month_field = &field;
}
// Empty form - whole form is Autofilled.
@@ -3350,7 +3392,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FormData response_data;
base::HistogramTester histogram_tester;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
// Cardholder name, card number, expiration data were autofilled but cvc was
// not be autofilled.
@@ -3370,7 +3412,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillCreditCardForm_Simple) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledCreditCardFormElvis(response_page_id, response_data,
kDefaultPageID, false);
@@ -3381,13 +3423,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillCreditCardForm_StripCardNumberWhitespace) {
// Same as the SetUp(), but generate Elvis card with whitespace in credit
// card number. |credit_card| will be owned by the TestPersonalDataManager.
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard credit_card;
test::SetCreditCardInfo(&credit_card, "Elvis Presley",
"4234 5678 9012 3456", // Visa
"04", "2999", "1");
credit_card.set_guid("00000000-0000-0000-0000-000000000008");
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
// Set up our form data.
FormData form;
CreateTestCreditCardFormData(&form, true, false);
@@ -3398,7 +3440,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledCreditCardFormElvis(response_page_id, response_data,
kDefaultPageID, false);
@@ -3410,13 +3452,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// Same as the SetUp(), but generate Elvis card with separator characters in
// credit card number. |credit_card| will be owned by the
// TestPersonalDataManager.
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard credit_card;
test::SetCreditCardInfo(&credit_card, "Elvis Presley",
"4234-5678-9012-3456", // Visa
"04", "2999", "1");
credit_card.set_guid("00000000-0000-0000-0000-000000000009");
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
// Set up our form data.
FormData form;
CreateTestCreditCardFormData(&form, true, false);
@@ -3427,7 +3469,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledCreditCardFormElvis(response_page_id, response_data,
kDefaultPageID, false);
@@ -3437,13 +3479,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// Test 1 of 4: Empty month, empty year
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillCreditCardForm_NoYearNoMonth) {
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard credit_card;
test::SetCreditCardInfo(&credit_card, "Miku Hatsune",
"4234567890654321", // Visa
"", "", "1");
credit_card.set_guid("00000000-0000-0000-0000-000000000007");
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
// Set up our form data.
FormData form;
CreateTestCreditCardFormData(&form, true, true);
@@ -3454,7 +3496,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledCreditCardYearMonthWithYearMonth(response_page_id, response_data,
kDefaultPageID, false, "", "");
@@ -3464,13 +3506,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// Test 2 of 4: Non-empty month, empty year
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillCreditCardForm_NoYearMonth) {
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard credit_card;
test::SetCreditCardInfo(&credit_card, "Miku Hatsune",
"4234567890654321", // Visa
"04", "", "1");
credit_card.set_guid("00000000-0000-0000-0000-000000000007");
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
// Set up our form data.
FormData form;
CreateTestCreditCardFormData(&form, true, true);
@@ -3481,7 +3523,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledCreditCardYearMonthWithYearMonth(response_page_id, response_data,
kDefaultPageID, false, "", "04");
@@ -3493,13 +3535,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillCreditCardForm_YearNoMonth) {
// Same as the SetUp(), but generate 4 credit cards with year month
// combination.
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard credit_card;
test::SetCreditCardInfo(&credit_card, "Miku Hatsune",
"4234567890654321", // Visa
"", "2999", "1");
credit_card.set_guid("00000000-0000-0000-0000-000000000007");
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
// Set up our form data.
FormData form;
CreateTestCreditCardFormData(&form, true, true);
@@ -3510,7 +3552,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledCreditCardYearMonthWithYearMonth(
response_page_id, response_data, kDefaultPageID, false, "2999", "");
@@ -3520,13 +3562,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// Test 4 of 4: Non-empty month, non-empty year
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillCreditCardForm_YearMonth) {
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard credit_card;
test::SetCreditCardInfo(&credit_card, "Miku Hatsune",
"4234567890654321", // Visa
"04", "2999", "1");
credit_card.set_guid("00000000-0000-0000-0000-000000000007");
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
// Set up our form data.
FormData form;
CreateTestCreditCardFormData(&form, true, true);
@@ -3537,7 +3579,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledCreditCardYearMonthWithYearMonth(
response_page_id, response_data, kDefaultPageID, false, "2999", "04");
@@ -3576,7 +3618,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledField("Card Name", "cardname", "Elvis", "text",
response_data.fields[0]);
@@ -3628,7 +3670,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledField("Card Name", "cardname", "Elvis", "text",
response_data.fields[0]);
@@ -3680,7 +3722,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledField("Card Name", "cardname", "Elvis", "text",
response_data.fields[0]);
@@ -3731,7 +3773,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
ExpectFilledField("Card Name", "cardname", "Elvis", "text",
response_data.fields[0]);
@@ -3805,12 +3847,12 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
"Apt. 10", "Memphis", "Tennessee", "38116", "US",
"12345678901");
profile.set_guid(guid);
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
// Verify the correct filling of the name entries.
@@ -3863,7 +3905,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
{
SCOPED_TRACE("Address");
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
ExpectFilledAddressFormElvis(response_page_id, response_data,
kDefaultPageID, true);
@@ -3875,7 +3917,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
response_page_id = 0;
{
FillAutofillFormDataAndSaveResults(kPageID2, form, form.fields.back(),
- MakeFrontendID(guid2, std::string()),
+ MakeFrontendId(guid2, std::string()),
&response_page_id, &response_data);
SCOPED_TRACE("Credit card");
ExpectFilledCreditCardFormElvis(response_page_id, response_data, kPageID2,
@@ -3912,7 +3954,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FormData response_data;
FillAutofillFormDataAndSaveResults(
kDefaultPageID, address_form, address_form.fields[0],
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
// The fist and middle names should be filled.
ExpectFilledField("First name", "firstname", "Elvis", "text",
@@ -3955,7 +3997,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FormData response_data;
FillAutofillFormDataAndSaveResults(
kDefaultPageID, address_form, address_form.fields[0],
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
// All fields should be filled.
ExpectFilledField("First name", "firstname", "Elvis", "text",
@@ -3998,13 +4040,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
"Apt. 10", "Memphis", "Tennessee", "38116", "US",
"12345678901");
profile.set_guid(guid);
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(
kDefaultPageID, address_form, *address_form.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
// All the fields should be filled except the company.
ExpectFilledField("First name", "firstname", "Elvis", "text",
@@ -4046,7 +4088,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FormData response_data;
FillAutofillFormDataAndSaveResults(
kDefaultPageID, address_form, address_form.fields[0],
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
// All the fields should be filled.
ExpectFilledField("First name", "firstname", "Elvis", "text",
@@ -4087,7 +4129,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
// The credit card name and number should be filled.
@@ -4120,7 +4162,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
// All fields should be filled.
@@ -4132,13 +4174,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// expiration date.
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillCreditCardForm_ExpiredCard) {
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard expired_card;
test::SetCreditCardInfo(&expired_card, "Homer Simpson",
"4234567890654321", // Visa
"05", "2000", "1");
expired_card.set_guid("00000000-0000-0000-0000-000000000009");
- personal_data_.AddCreditCard(expired_card);
+ personal_data().AddCreditCard(expired_card);
// Set up the form data.
FormData form;
@@ -4173,7 +4215,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()),
+ MakeFrontendId(guid, std::string()),
&response_page_id, &response_data);
// The credit card name, type and number should be filled.
@@ -4193,9 +4235,9 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
TEST_P(BrowserAutofillManagerStructuredProfileTest,
PreviewCreditCardForm_VirtualCard) {
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard virtual_card = test::GetVirtualCard();
- personal_data_.AddServerCreditCard(virtual_card);
+ personal_data().AddServerCreditCard(virtual_card);
// Set up our form data.
FormData form;
CreateTestCreditCardFormData(&form, true, false);
@@ -4266,7 +4308,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
// All the visible fields should be filled as all the fields belong to the
@@ -4305,7 +4347,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
{
SCOPED_TRACE("Address 1");
@@ -4329,7 +4371,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
response_page_id = 0;
FillAutofillFormDataAndSaveResults(
kPageID2, form, form.fields[kAddressFormSize + 9],
- MakeFrontendID(std::string(), guid2), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid2), &response_page_id, &response_data);
{
SCOPED_TRACE("Address 2");
ASSERT_EQ(response_data.fields.size(), form.fields.size());
@@ -4420,7 +4462,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[1],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
{
SCOPED_TRACE("Unnamed section");
@@ -4451,7 +4493,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
const char guid2[] = "00000000-0000-0000-0000-000000000001";
response_page_id = 0;
FillAutofillFormDataAndSaveResults(kPageID2, form, form.fields[0],
- MakeFrontendID(std::string(), guid2),
+ MakeFrontendId(std::string(), guid2),
&response_page_id, &response_data);
{
SCOPED_TRACE("Billing address");
@@ -4482,7 +4524,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
response_page_id = 0;
FillAutofillFormDataAndSaveResults(
kPageID3, form, form.fields[form.fields.size() - 2],
- MakeFrontendID(guid3, std::string()), &response_page_id, &response_data);
+ MakeFrontendId(guid3, std::string()), &response_page_id, &response_data);
{
SCOPED_TRACE("Credit card");
EXPECT_EQ(kPageID3, response_page_id);
@@ -4526,7 +4568,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
// The second email address should be filled.
@@ -4544,8 +4586,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillAutofilledForm) {
FormData form;
test::CreateTestAddressFormData(&form);
// Mark the address fields as autofilled.
- for (auto iter = form.fields.begin(); iter != form.fields.end(); ++iter) {
- iter->is_autofilled = true;
+ for (auto& field : form.fields) {
+ field.is_autofilled = true;
}
CreateTestCreditCardFormData(&form, true, false);
@@ -4557,7 +4599,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillAutofilledForm) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
{
SCOPED_TRACE("Address");
@@ -4571,7 +4613,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillAutofilledForm) {
const char guid2[] = "00000000-0000-0000-0000-000000000004";
response_page_id = 0;
FillAutofillFormDataAndSaveResults(kPageID2, form, form.fields.back(),
- MakeFrontendID(guid2, std::string()),
+ MakeFrontendId(guid2, std::string()),
&response_page_id, &response_data);
{
SCOPED_TRACE("Credit card 1");
@@ -4581,15 +4623,15 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillAutofilledForm) {
// Now set the credit card fields to also be auto-filled, and try again to
// fill the credit card data
- for (auto iter = form.fields.begin(); iter != form.fields.end(); ++iter) {
- iter->is_autofilled = true;
+ for (auto& field : form.fields) {
+ field.is_autofilled = true;
}
const int kPageID3 = 3;
response_page_id = 0;
FillAutofillFormDataAndSaveResults(
kPageID3, form, form.fields[form.fields.size() - 2],
- MakeFrontendID(guid2, std::string()), &response_page_id, &response_data);
+ MakeFrontendId(guid2, std::string()), &response_page_id, &response_data);
{
SCOPED_TRACE("Credit card 2");
ExpectFilledForm(response_page_id, response_data, kPageID3, "", "", "", "",
@@ -4619,7 +4661,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPartlyAutofilledForm) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
{
SCOPED_TRACE("Address");
@@ -4634,7 +4676,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPartlyAutofilledForm) {
const char guid2[] = "00000000-0000-0000-0000-000000000004";
response_page_id = 0;
FillAutofillFormDataAndSaveResults(kPageID2, form, form.fields.back(),
- MakeFrontendID(guid2, std::string()),
+ MakeFrontendId(guid2, std::string()),
&response_page_id, &response_data);
{
SCOPED_TRACE("Credit card 1");
@@ -4669,7 +4711,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
{
SCOPED_TRACE("Address");
@@ -4685,7 +4727,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
const char guid2[] = "00000000-0000-0000-0000-000000000004";
response_page_id = 0;
FillAutofillFormDataAndSaveResults(kPageID2, form, form.fields.back(),
- MakeFrontendID(guid2, std::string()),
+ MakeFrontendId(guid2, std::string()),
&response_page_id, &response_data);
{
SCOPED_TRACE("Credit card 1");
@@ -4739,8 +4781,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) {
FormsSeen(forms);
// We should be able to fill prefix and suffix fields for US numbers.
- AutofillProfile* work_profile =
- personal_data_.GetProfileWithGUID("00000000-0000-0000-0000-000000000002");
+ AutofillProfile* work_profile = personal_data().GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
ASSERT_TRUE(work_profile != nullptr);
work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"16505554567");
@@ -4751,7 +4793,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) {
FillAutofillFormDataAndSaveResults(
page_id, form_with_us_number_max_length,
*form_with_us_number_max_length.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data1);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data1);
EXPECT_EQ(1, response_page_id);
ASSERT_EQ(5U, response_data1.fields.size());
@@ -4766,7 +4808,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) {
FormData response_data2;
FillAutofillFormDataAndSaveResults(page_id, form_with_autocompletetype,
*form_with_autocompletetype.fields.begin(),
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data2);
EXPECT_EQ(2, response_page_id);
@@ -4789,7 +4831,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) {
FillAutofillFormDataAndSaveResults(
page_id, form_with_us_number_max_length,
*form_with_us_number_max_length.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data3);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data3);
EXPECT_EQ(3, response_page_id);
ASSERT_EQ(5U, response_data3.fields.size());
@@ -4804,7 +4846,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) {
FormData response_data4;
FillAutofillFormDataAndSaveResults(page_id, form_with_autocompletetype,
*form_with_autocompletetype.fields.begin(),
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data4);
EXPECT_EQ(4, response_page_id);
@@ -4818,8 +4860,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) {
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillFirstPhoneNumber_ComponentizedNumbers) {
- AutofillProfile* work_profile =
- personal_data_.GetProfileWithGUID("00000000-0000-0000-0000-000000000002");
+ AutofillProfile* work_profile = personal_data().GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
ASSERT_TRUE(work_profile != nullptr);
work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"16505554567");
@@ -4869,7 +4911,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillAutofillFormDataAndSaveResults(
page_id, form_with_multiple_componentized_phone_fields,
*form_with_multiple_componentized_phone_fields.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
EXPECT_EQ(1, response_page_id);
// Verify only the first complete set of phone number fields are filled.
@@ -4886,8 +4928,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillFirstPhoneNumber_WholeNumbers) {
- AutofillProfile* work_profile =
- personal_data_.GetProfileWithGUID("00000000-0000-0000-0000-000000000002");
+ AutofillProfile* work_profile = personal_data().GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
ASSERT_TRUE(work_profile != nullptr);
work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"16505554567");
@@ -4923,7 +4965,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillAutofillFormDataAndSaveResults(
page_id, form_with_multiple_whole_number_fields,
*form_with_multiple_whole_number_fields.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
EXPECT_EQ(1, response_page_id);
// Verify only the first complete set of phone number fields are filled.
@@ -4936,8 +4978,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillFirstPhoneNumber_FillPartsOnceOnly) {
- AutofillProfile* work_profile =
- personal_data_.GetProfileWithGUID("00000000-0000-0000-0000-000000000002");
+ AutofillProfile* work_profile = personal_data().GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
ASSERT_TRUE(work_profile != nullptr);
work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"16505554567");
@@ -4989,7 +5031,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillAutofillFormDataAndSaveResults(
page_id, form_with_multiple_componentized_phone_fields,
*form_with_multiple_componentized_phone_fields.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
EXPECT_EQ(1, response_page_id);
// Verify only the first complete set of phone number fields are filled,
@@ -5009,8 +5051,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// phone field, we do not fill anything to extension field.
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillFirstPhoneNumber_NotFillMisclassifiedExtention) {
- AutofillProfile* work_profile =
- personal_data_.GetProfileWithGUID("00000000-0000-0000-0000-000000000002");
+ AutofillProfile* work_profile = personal_data().GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
ASSERT_TRUE(work_profile != nullptr);
work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"16505554567");
@@ -5054,7 +5096,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillAutofillFormDataAndSaveResults(
page_id, form_with_misclassified_extension,
*form_with_misclassified_extension.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
EXPECT_EQ(1, response_page_id);
// Verify the misclassified extension field is not filled.
@@ -5066,11 +5108,14 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
EXPECT_EQ(std::u16string(), response_data.fields[4].value);
}
-// Verify when no complete number can be found, we do best-effort filling.
+// Verify that phone number fields annotated with the autocomplete attribute
+// are filled best-effort.
+// Phone number local heuristics only succeed if a PHONE_HOME_NUMBER field is
+// present.
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillFirstPhoneNumber_BestEfforFilling) {
- AutofillProfile* work_profile =
- personal_data_.GetProfileWithGUID("00000000-0000-0000-0000-000000000002");
+ AutofillProfile* work_profile = personal_data().GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
ASSERT_TRUE(work_profile != nullptr);
work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"16505554567");
@@ -5110,7 +5155,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillAutofillFormDataAndSaveResults(
page_id, form_with_no_complete_number,
*form_with_no_complete_number.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
EXPECT_EQ(1, response_page_id);
// Verify when there is no complete phone number fields, we do best effort
@@ -5126,8 +5171,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// entire form, both first phone field and second phone field included.
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillFirstPhoneNumber_FocusOnSecondPhoneNumber) {
- AutofillProfile* work_profile =
- personal_data_.GetProfileWithGUID("00000000-0000-0000-0000-000000000002");
+ AutofillProfile* work_profile = personal_data().GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
ASSERT_TRUE(work_profile != nullptr);
work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"16505554567");
@@ -5165,7 +5210,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
std::advance(it, 3);
FillAutofillFormDataAndSaveResults(
page_id, form_with_multiple_whole_number_fields, *it,
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
EXPECT_EQ(1, response_page_id);
// Verify when the second phone number field is being focused, we fill
@@ -5179,8 +5224,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillFirstPhoneNumber_HiddenFieldShouldNotCount) {
- AutofillProfile* work_profile =
- personal_data_.GetProfileWithGUID("00000000-0000-0000-0000-000000000002");
+ AutofillProfile* work_profile = personal_data().GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
ASSERT_TRUE(work_profile != nullptr);
work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"16505554567");
@@ -5218,7 +5263,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillAutofillFormDataAndSaveResults(
page_id, form_with_multiple_whole_number_fields,
*form_with_multiple_whole_number_fields.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
EXPECT_EQ(1, response_page_id);
// Verify hidden/non-focusable phone field is set to only_fill_when_focused.
@@ -5283,7 +5328,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
base::HistogramTester histogram_tester;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
histogram_tester.ExpectTotalCount(
"Autofill.HiddenOrPresentationalSelectFieldsFilled", 2);
@@ -5303,8 +5348,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillFirstPhoneNumber_MultipleSectionFilledCorrectly) {
- AutofillProfile* work_profile =
- personal_data_.GetProfileWithGUID("00000000-0000-0000-0000-000000000002");
+ AutofillProfile* work_profile = personal_data().GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
ASSERT_TRUE(work_profile != nullptr);
work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"16505554567");
@@ -5355,7 +5400,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FillAutofillFormDataAndSaveResults(
page_id, form_with_multiple_sections,
*form_with_multiple_sections.fields.begin(),
- MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
+ MakeFrontendId(std::string(), guid), &response_page_id, &response_data);
EXPECT_EQ(1, response_page_id);
// Verify first section is filled with rationalization.
@@ -5375,7 +5420,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
std::advance(it, 6); // Pointing to second section.
FillAutofillFormDataAndSaveResults(page_id, form_with_multiple_sections, *it,
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
EXPECT_EQ(1, response_page_id);
@@ -5413,7 +5458,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormChangesRemoveField) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
ExpectFilledAddressFormElvis(response_page_id, response_data, kDefaultPageID,
false);
@@ -5443,7 +5488,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormChangesAddField) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
ExpectFilledAddressFormElvis(response_page_id, response_data, kDefaultPageID,
false);
@@ -5486,7 +5531,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
ASSERT_EQ(5U, response_data.fields.size());
@@ -5510,7 +5555,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
const char guid2[] = "00000000-0000-0000-0000-000000000002";
FillAutofillFormDataAndSaveResults(kDefaultPageID, response_data,
response_data.fields[4],
- MakeFrontendID(std::string(), guid2),
+ MakeFrontendId(std::string(), guid2),
&response_page_id, &later_response_data);
ASSERT_EQ(5U, later_response_data.fields.size());
ExpectFilledField("First Name", "first_name", "Elvis", "text",
@@ -5538,7 +5583,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormSubmitted) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
ExpectFilledAddressFormElvis(response_page_id, response_data, kDefaultPageID,
false);
@@ -5546,7 +5591,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormSubmitted) {
// Simulate form submission. We should call into the PDM to try to save the
// filled data.
FormSubmitted(response_data);
- EXPECT_EQ(1, personal_data_.num_times_save_imported_profile_called());
+ EXPECT_EQ(1, personal_data().num_times_save_imported_profile_called());
}
// Test that we are saving form data when the FormSubmitted event is sent.
@@ -5562,14 +5607,14 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormSubmittedSaveData) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
ExpectFilledAddressFormElvis(response_page_id, response_data, kDefaultPageID,
false);
browser_autofill_manager_->OnFormSubmitted(response_data, false,
SubmissionSource::FORM_SUBMISSION);
- EXPECT_EQ(1, personal_data_.num_times_save_imported_profile_called());
+ EXPECT_EQ(1, personal_data().num_times_save_imported_profile_called());
}
// Test that when Autocomplete is enabled and Autofill is disabled, form
@@ -5583,7 +5628,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
FormData form;
test::CreateTestAddressFormData(&form);
- EXPECT_CALL(*(single_field_form_fill_router_), OnWillSubmitForm(_, true));
+ EXPECT_CALL(*(single_field_form_fill_router_), OnWillSubmitForm(_, _, true));
FormSubmitted(form);
}
@@ -6120,7 +6165,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormSubmittedServerTypes) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
ExpectFilledAddressFormElvis(response_page_id, response_data, kDefaultPageID,
false);
@@ -6128,7 +6173,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormSubmittedServerTypes) {
// Simulate form submission. We should call into the PDM to try to save the
// filled data.
FormSubmitted(response_data);
- EXPECT_EQ(1, personal_data_.num_times_save_imported_profile_called());
+ EXPECT_EQ(1, personal_data().num_times_save_imported_profile_called());
}
// Test that we are able to save form data after the possible types have been
@@ -6147,23 +6192,23 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
ExpectFilledAddressFormElvis(response_page_id, response_data, kDefaultPageID,
false);
- personal_data_.ClearProfiles();
+ personal_data().ClearProfiles();
// The default credit card is a Elvis card. It must be removed because name
// fields would be detected. However at least one profile or card is needed to
// start the upload process, which is why this other card is created.
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard credit_card;
test::SetCreditCardInfo(&credit_card, "Miku Hatsune",
"4234567890654321", // Visa
"04", "2999", "1");
credit_card.set_guid("00000000-0000-0000-0000-000000000007");
- personal_data_.AddCreditCard(credit_card);
- ASSERT_EQ(0u, personal_data_.GetProfiles().size());
+ personal_data().AddCreditCard(credit_card);
+ ASSERT_EQ(0u, personal_data().GetProfiles().size());
// Simulate form submission. The first submission should not count the data
// towards possible types. Therefore we expect all UNKNOWN_TYPE entries.
@@ -6173,12 +6218,12 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
type_set);
browser_autofill_manager_->SetExpectedSubmittedFieldTypes(unknown_types);
FormSubmitted(response_data);
- ASSERT_EQ(1u, personal_data_.GetProfiles().size());
+ ASSERT_EQ(1u, personal_data().GetProfiles().size());
// The second submission should now have data by which to infer types.
browser_autofill_manager_->SetExpectedSubmittedFieldTypes(expected_types);
FormSubmitted(response_data);
- ASSERT_EQ(1u, personal_data_.GetProfiles().size());
+ ASSERT_EQ(1u, personal_data().GetProfiles().size());
}
// Test that the form signature for an uploaded form always matches the form
@@ -6230,13 +6275,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[3],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
// Simulate form submission. We should call into the PDM to try to save the
// filled data.
FormSubmitted(response_data);
- EXPECT_EQ(1, personal_data_.num_times_save_imported_profile_called());
+ EXPECT_EQ(1, personal_data().num_times_save_imported_profile_called());
// Set the address field's value back to the default value.
response_data.fields[3].value = u"Enter your address";
@@ -6244,7 +6289,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
// Simulate form submission. We should not call into the PDM to try to save
// the filled data, since the filled form is effectively missing an address.
FormSubmitted(response_data);
- EXPECT_EQ(1, personal_data_.num_times_save_imported_profile_called());
+ EXPECT_EQ(1, personal_data().num_times_save_imported_profile_called());
}
struct ProfileMatchingTypesTestCase {
@@ -7041,13 +7086,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, RemoveProfile) {
AutofillProfile profile;
const char guid[] = "00000000-0000-0000-0000-000000000102";
profile.set_guid(guid);
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
- int id = MakeFrontendID(std::string(), guid);
+ int id = MakeFrontendId(std::string(), guid);
browser_autofill_manager_->RemoveAutofillProfileOrCreditCard(id);
- EXPECT_FALSE(personal_data_.GetProfileWithGUID(guid));
+ EXPECT_FALSE(personal_data().GetProfileWithGUID(guid));
}
TEST_P(BrowserAutofillManagerStructuredProfileTest, RemoveCreditCard) {
@@ -7055,13 +7100,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, RemoveCreditCard) {
CreditCard credit_card;
const char guid[] = "00000000-0000-0000-0000-000000100007";
credit_card.set_guid(guid);
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
- int id = MakeFrontendID(guid, std::string());
+ int id = MakeFrontendId(guid, std::string());
browser_autofill_manager_->RemoveAutofillProfileOrCreditCard(id);
- EXPECT_FALSE(personal_data_.GetCreditCardWithGUID(guid));
+ EXPECT_FALSE(personal_data().GetCreditCardWithGUID(guid));
}
// Test our external delegate is called at the right time.
@@ -7347,7 +7392,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
TEST_P(BrowserAutofillManagerStructuredProfileTest,
DontSaveCvcInAutocompleteHistory) {
FormData form_seen_by_ahm;
- EXPECT_CALL(*(single_field_form_fill_router_), OnWillSubmitForm(_, true))
+ EXPECT_CALL(*(single_field_form_fill_router_), OnWillSubmitForm(_, _, true))
.WillOnce(SaveArg<0>(&form_seen_by_ahm));
FormData form;
@@ -7393,15 +7438,15 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
PrepareForRealPanResponse(&form, &card);
// Manually fill out |form| so we can use it in OnFormSubmitted.
- for (size_t i = 0; i < form.fields.size(); ++i) {
- if (form.fields[i].name == u"cardnumber")
- form.fields[i].value = u"4012888888881881";
- else if (form.fields[i].name == u"nameoncard")
- form.fields[i].value = u"John H Dillinger";
- else if (form.fields[i].name == u"ccmonth")
- form.fields[i].value = u"01";
- else if (form.fields[i].name == u"ccyear")
- form.fields[i].value = u"2017";
+ for (auto& field : form.fields) {
+ if (field.name == u"cardnumber")
+ field.value = u"4012888888881881";
+ else if (field.name == u"nameoncard")
+ field.value = u"John H Dillinger";
+ else if (field.name == u"ccmonth")
+ field.value = u"01";
+ else if (field.name == u"ccyear")
+ field.value = u"2017";
}
CardUnmaskDelegate::UserProvidedUnmaskDetails details;
@@ -7444,7 +7489,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
EXPECT_CALL(*autofill_driver_, FillOrPreviewForm(_, _, _, _, _)).Times(0);
FillAutofillFormData(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(std::string(), guid));
+ MakeFrontendId(std::string(), guid));
}
TEST_P(BrowserAutofillManagerStructuredProfileTest,
@@ -7481,7 +7526,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
EXPECT_CALL(*autofill_driver_, FillOrPreviewForm(_, _, _, _, _)).Times(0);
FillAutofillFormData(kDefaultPageID, form, *form.fields.begin(),
- MakeFrontendID(guid, std::string()));
+ MakeFrontendId(guid, std::string()));
}
TEST_P(BrowserAutofillManagerStructuredProfileTest,
@@ -7633,7 +7678,7 @@ TEST_P(CreditCardSuggestionTest,
"01", "2030", "1");
credit_card.set_guid(guid);
credit_card.SetNickname(kArbitraryNickname16);
- personal_data_.AddCreditCard(credit_card);
+ personal_data().AddCreditCard(credit_card);
#if BUILDFLAG(IS_ANDROID)
// When keyboard accessary is enabled, always show "7777".
@@ -7660,7 +7705,7 @@ TEST_P(CreditCardSuggestionTest,
GetAutofillSuggestions(form, field);
CheckSuggestions(kDefaultPageID,
Suggestion("Nancy Drew", visa_label, kVisaCard,
- MakeFrontendID(guid, std::string())));
+ MakeFrontendId(guid, std::string())));
}
// Verify that typing "lvis" will not match any of the credit card name when
@@ -7903,7 +7948,7 @@ TEST_P(SuggestionMatchingTest,
profile1.SetInfo(NAME_MIDDLE, u"Adam Smith", "en-US");
profile1.SetInfo(NAME_LAST, u"Grimes", "en-US");
profile1.SetInfo(ADDRESS_HOME_LINE1, u"1234 Smith Blvd.", "en-US");
- personal_data_.AddProfile(profile1);
+ personal_data().AddProfile(profile1);
AutofillProfile profile2;
profile2.set_guid("00000000-0000-0000-0000-000000000124");
@@ -7911,7 +7956,7 @@ TEST_P(SuggestionMatchingTest,
profile2.SetInfo(NAME_MIDDLE, u"Shawn Smith", "en-US");
profile2.SetInfo(NAME_LAST, u"Grimes", "en-US");
profile2.SetInfo(ADDRESS_HOME_LINE1, u"1234 Smith Blvd.", "en-US");
- personal_data_.AddProfile(profile2);
+ personal_data().AddProfile(profile2);
FormFieldData field;
test::CreateTestFormField("Middle Name", "middlename", "S", "text", &field);
@@ -8204,11 +8249,10 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
TEST_P(BrowserAutofillManagerStructuredProfileTest,
GetCreditCardSuggestions_VirtualCard) {
base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatures(
- {features::kAutofillEnableMerchantBoundVirtualCards},
- {features::kAutofillSuggestVirtualCardsOnIncompleteForm});
+ scoped_feature_list.InitAndDisableFeature(
+ features::kAutofillSuggestVirtualCardsOnIncompleteForm);
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard masked_server_card(CreditCard::MASKED_SERVER_CARD,
/*server_id=*/"a123");
test::SetCreditCardInfo(&masked_server_card, "Elvis Presley",
@@ -8218,7 +8262,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
masked_server_card.set_guid("00000000-0000-0000-0000-000000000007");
masked_server_card.set_virtual_card_enrollment_state(CreditCard::ENROLLED);
masked_server_card.SetNickname(u"nickname");
- personal_data_.AddServerCreditCard(masked_server_card);
+ personal_data().AddServerCreditCard(masked_server_card);
// Set up our form data.
FormData form;
@@ -8237,6 +8281,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
#endif
Suggestion virtual_card_suggestion = Suggestion(
+ "Virtual card",
std::string("nickname ") + test::ObfuscatedCardDigitsAsUTF8("3456"),
label, kVisaCard, autofill::POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY);
@@ -8261,7 +8306,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
#endif
virtual_card_suggestion =
- Suggestion(std::string("Elvis Presley"), label, kVisaCard,
+ Suggestion("Virtual card", std::string("Elvis Presley"), label, kVisaCard,
autofill::POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY);
CheckSuggestions(
@@ -8998,9 +9043,9 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest,
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
// The feature is not implemented for mobile.
- EXPECT_EQ(0, personal_data_.num_times_save_upi_id_called());
+ EXPECT_EQ(0, personal_data().num_times_save_upi_id_called());
#else
- EXPECT_EQ(1, personal_data_.num_times_save_upi_id_called());
+ EXPECT_EQ(1, personal_data().num_times_save_upi_id_called());
#endif
}
@@ -9031,7 +9076,7 @@ TEST_F(BrowserAutofillManagerTest, DontImportUpiIdWhenIncognito) {
form.fields[0].value = u"user@indianbank";
FormSubmitted(form);
- EXPECT_EQ(0, personal_data_.num_times_save_upi_id_called());
+ EXPECT_EQ(0, personal_data().num_times_save_upi_id_called());
}
TEST_F(BrowserAutofillManagerTest, PageLanguageGetsCorrectlySet) {
@@ -9256,7 +9301,7 @@ TEST_F(BrowserAutofillManagerTest, PreventOverridingOfPrefilledValues) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
EXPECT_EQ(response_data.fields[0].value, u"Elvis Aaron Presley");
EXPECT_EQ(response_data.fields[1].value, u"Test City");
@@ -9300,7 +9345,7 @@ TEST_F(BrowserAutofillManagerTest, PreventOverridingOfPrefilledValues) {
autofill::features::kAutofillPreventOverridingPrefilledValues);
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
EXPECT_EQ(response_data.fields[0].value, u"Elvis Aaron Presley");
EXPECT_EQ(response_data.fields[1].value, u"Memphis");
@@ -9346,7 +9391,7 @@ TEST_F(BrowserAutofillManagerTest, AutofillOverridePrefilledValue) {
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
- MakeFrontendID(std::string(), guid),
+ MakeFrontendId(std::string(), guid),
&response_page_id, &response_data);
EXPECT_EQ(response_data.fields[0].value, u"Elvis Aaron Presley");
EXPECT_EQ(response_data.fields[1].value, u"Test City");
@@ -9354,6 +9399,35 @@ TEST_F(BrowserAutofillManagerTest, AutofillOverridePrefilledValue) {
EXPECT_EQ(response_data.fields[3].value, u"Test Country");
}
+// Tests that Autofill suggestions are not shown if TTF is eligible and shown.
+TEST_F(BrowserAutofillManagerTest, AutofillSuggestionsOrTouchToFill) {
+ FormData form;
+ CreateTestCreditCardFormData(&form, /*is_https=*/true,
+ /*use_month_type=*/false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+ const FormFieldData& field = form.fields[1];
+ int query_id = 1;
+
+ // TTF not eligible, Autofill suggestions shown.
+ EXPECT_CALL(*touch_to_fill_delegate_, TryToShowTouchToFill(query_id, _, _))
+ .Times(0);
+ TryToShowTouchToFill(query_id++, form, field, TouchToFillEligible(false));
+ EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen());
+
+ // TTF not shown, Autofill suggestions shown.
+ EXPECT_CALL(*touch_to_fill_delegate_, TryToShowTouchToFill(query_id, _, _))
+ .WillOnce(Return(false));
+ TryToShowTouchToFill(query_id++, form, field, TouchToFillEligible(true));
+ EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen());
+
+ // TTF eligible and shown, Autofill suggestions not shown
+ EXPECT_CALL(*touch_to_fill_delegate_, TryToShowTouchToFill(query_id, _, _))
+ .WillOnce(Return(true));
+ TryToShowTouchToFill(query_id++, form, field, TouchToFillEligible(true));
+ EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen());
+}
+
// Desktop only tests.
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
class BrowserAutofillManagerTestForVirtualCardOption
@@ -9374,7 +9448,7 @@ class BrowserAutofillManagerTestForVirtualCardOption
// Add only one server card so the second suggestion (if any) must be the
// "Use a virtual card number" option.
- personal_data_.ClearCreditCards();
+ personal_data().ClearCreditCards();
CreditCard masked_server_card(CreditCard::MASKED_SERVER_CARD,
/*server_id=*/"a123");
// TODO(crbug.com/1020740): Replace all the hard-coded expiration year in
@@ -9384,7 +9458,7 @@ class BrowserAutofillManagerTestForVirtualCardOption
"04", "2999", "1");
masked_server_card.SetNetworkForMaskedCard(kVisaCard);
masked_server_card.set_guid("00000000-0000-0000-0000-000000000007");
- personal_data_.AddServerCreditCard(masked_server_card);
+ personal_data().AddServerCreditCard(masked_server_card);
}
void CreateCompleteFormAndGetSuggestions() {
@@ -9400,10 +9474,10 @@ class BrowserAutofillManagerTestForVirtualCardOption
// Adds a CreditCardCloudTokenData to PersonalDataManager. This needs to be
// called before suggestions are fetched.
void CreateCloudTokenDataForDefaultCard() {
- personal_data_.ClearCloudTokenData();
+ personal_data().ClearCloudTokenData();
CreditCardCloudTokenData data1 = test::GetCreditCardCloudTokenData1();
data1.masked_card_id = "a123";
- personal_data_.AddCloudTokenData(data1);
+ personal_data().AddCloudTokenData(data1);
}
void VerifyNoVirtualCardSuggestions() {
@@ -9506,7 +9580,7 @@ TEST_F(BrowserAutofillManagerTestForVirtualCardOption,
CreateCloudTokenDataForDefaultCard();
CreditCardCloudTokenData data2 = test::GetCreditCardCloudTokenData2();
data2.masked_card_id = "a123";
- personal_data_.AddCloudTokenData(data2);
+ personal_data().AddCloudTokenData(data2);
CreateCompleteFormAndGetSuggestions();
VerifyNoVirtualCardSuggestions();
@@ -9605,13 +9679,13 @@ TEST_F(BrowserAutofillManagerTestForVirtualCardOption,
"04", "2999", "1");
masked_server_card.SetNetworkForMaskedCard(kVisaCard);
masked_server_card.set_guid("00000000-0000-0000-0000-000000000008");
- personal_data_.AddServerCreditCard(masked_server_card);
+ personal_data().AddServerCreditCard(masked_server_card);
CreditCardCloudTokenData data1 = test::GetCreditCardCloudTokenData1();
data1.masked_card_id = "a456";
- personal_data_.AddCloudTokenData(data1);
+ personal_data().AddCloudTokenData(data1);
CreditCardCloudTokenData data2 = test::GetCreditCardCloudTokenData2();
data2.masked_card_id = "a456";
- personal_data_.AddCloudTokenData(data2);
+ personal_data().AddCloudTokenData(data2);
CreateCompleteFormAndGetSuggestions();
@@ -9877,12 +9951,12 @@ INSTANTIATE_TEST_SUITE_P(,
TEST_P(BrowserAutofillManagerTestForSharingNickname,
VerifySuggestion_DuplicateCards) {
- personal_data_.ClearCreditCards();
- ASSERT_EQ(0U, personal_data_.GetCreditCards().size());
+ personal_data().ClearCreditCards();
+ ASSERT_EQ(0U, personal_data().GetCreditCards().size());
CreditCard local_card = GetLocalCard();
- personal_data_.AddCreditCard(local_card);
- personal_data_.AddServerCreditCard(GetServerCard());
- ASSERT_EQ(2U, personal_data_.GetCreditCards().size());
+ personal_data().AddCreditCard(local_card);
+ personal_data().AddServerCreditCard(GetServerCard());
+ ASSERT_EQ(2U, personal_data().GetCreditCards().size());
// Set up our form data.
FormData form;
@@ -9911,18 +9985,18 @@ TEST_P(BrowserAutofillManagerTestForSharingNickname,
TEST_P(BrowserAutofillManagerTestForSharingNickname,
VerifySuggestion_UnrelatedCards) {
- personal_data_.ClearCreditCards();
- ASSERT_EQ(0U, personal_data_.GetCreditCards().size());
+ personal_data().ClearCreditCards();
+ ASSERT_EQ(0U, personal_data().GetCreditCards().size());
CreditCard local_card = GetLocalCard();
- personal_data_.AddCreditCard(local_card);
+ personal_data().AddCreditCard(local_card);
std::vector<CreditCard> server_cards;
CreditCard server_card = GetServerCard();
// Make sure the cards are different by giving a different card number.
server_card.SetNumber(u"371449635398431");
- personal_data_.AddServerCreditCard(server_card);
+ personal_data().AddServerCreditCard(server_card);
- ASSERT_EQ(2U, personal_data_.GetCreditCards().size());
+ ASSERT_EQ(2U, personal_data().GetCreditCards().size());
// Set up our form data.
FormData form;
@@ -9954,4 +10028,139 @@ TEST_P(BrowserAutofillManagerTestForSharingNickname,
browser_autofill_manager_->GetPackedCreditCardID(2)));
}
+// The following Refill Tests ensure that Autofill can handle the situation
+// where it fills a credit card form with an expiration date like 04/2999
+// and the website tries to reformat the input with whitespaces around the
+// slash and then sacrifices the wrong digits in the expiration date. I.e.,
+// the website replaces "04/2099" with "04 / 20". The tests ensure that this
+// triggers a refill with "04 / 29".
+struct RefillTestCase {
+ // The value that JavaScript owned by the website sets for the expiration
+ // date filed.
+ std::u16string exp_date_from_js;
+ // Whether we expect a refill from in this test case.
+ bool triggers_refill;
+ // What value we expect in the refill.
+ const char* refilled_exp_date = nullptr;
+};
+
+class BrowserAutofillManagerRefillTest
+ : public BrowserAutofillManagerTest,
+ public testing::WithParamInterface<RefillTestCase> {};
+
+TEST_P(BrowserAutofillManagerRefillTest,
+ RefillModifiedCreditCardExpirationDates) {
+ RefillTestCase test_case = GetParam();
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillRefillModifiedCreditCardExpirationDates);
+
+ // Set up a CC form with name, cc number and expiration date.
+ FormData form;
+ form.url = GURL("https://myform.com/form.html");
+ form.action = GURL("https://myform.com/submit.html");
+ FormFieldData field;
+ test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Expiration date", "exp_date", "", "text", &field);
+ form.fields.push_back(field);
+
+ // Notify BrowserAutofillManager of the form.
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Simulate filling and store the data to be filled in |first_fill_data|.
+ const char guid[] = "00000000-0000-0000-0000-000000000004";
+ int response_page_id = 0;
+ FormData first_fill_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
+ MakeFrontendId(guid, std::string()),
+ &response_page_id, &first_fill_data);
+ ASSERT_EQ(3u, first_fill_data.fields.size());
+ ExpectFilledField("Name on Card", "nameoncard", "Elvis Presley", "text",
+ first_fill_data.fields[0]);
+ ExpectFilledField("Card Number", "cardnumber", "4234567890123456", "text",
+ first_fill_data.fields[1]);
+ ExpectFilledField("Expiration date", "exp_date", "04/2999", "text",
+ first_fill_data.fields[2]);
+
+ FormData refilled_form;
+ if (test_case.triggers_refill) {
+ // Prepare intercepting the filling operation to the driver and capture
+ // the re-filled form data.
+ EXPECT_CALL(*autofill_driver_, FillOrPreviewForm(_, _, _, _, _))
+ .Times(1)
+ .WillOnce(DoAll(testing::SaveArg<2>(&refilled_form),
+ testing::Return(std::vector<FieldGlobalId>{})));
+ } else {
+ EXPECT_CALL(*autofill_driver_, FillOrPreviewForm(_, _, _, _, _)).Times(0);
+ }
+ // Simulate that JavaScript modifies the expiration date field.
+ FormData form_after_js_modification = first_fill_data;
+ form_after_js_modification.fields[2].value = test_case.exp_date_from_js;
+ browser_autofill_manager_->JavaScriptChangedAutofilledValue(
+ form_after_js_modification, form_after_js_modification.fields[2],
+ u"04/2999");
+
+ testing::Mock::VerifyAndClearExpectations(autofill_driver_.get());
+
+ if (test_case.triggers_refill) {
+ ASSERT_EQ(3u, refilled_form.fields.size());
+ ExpectFilledField("Name on Card", "nameoncard", "Elvis Presley", "text",
+ refilled_form.fields[0]);
+ EXPECT_FALSE(refilled_form.fields[0].force_override);
+ ExpectFilledField("Card Number", "cardnumber", "4234567890123456", "text",
+ refilled_form.fields[1]);
+ EXPECT_FALSE(refilled_form.fields[1].force_override);
+ ExpectFilledField("Expiration date", "exp_date",
+ test_case.refilled_exp_date, "text",
+ refilled_form.fields[2]);
+ EXPECT_TRUE(refilled_form.fields[2].force_override);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ BrowserAutofillManagerRefillTest,
+ testing::Values(
+ // This is the classic case: Autofill filled 04/2999, website overrode
+ // 04 / 29, we need to fix this to 04 / 99.
+ RefillTestCase{.exp_date_from_js = u"04 / 29",
+ .triggers_refill = true,
+ .refilled_exp_date = "04 / 99"},
+ // Maybe the website replaced the separator and added whitespaces.
+ RefillTestCase{.exp_date_from_js = u"04 - 29",
+ .triggers_refill = true,
+ .refilled_exp_date = "04 - 99"},
+ // Maybe the website only replaced the separator.
+ RefillTestCase{.exp_date_from_js = u"04-29",
+ .triggers_refill = true,
+ .refilled_exp_date = "04-99"},
+ // Maybe the website was smart and dropped the correct digits.
+ RefillTestCase{
+ .exp_date_from_js = u"04 / 99",
+ .triggers_refill = false,
+ },
+ // Maybe the website did not modify the values at all.
+ RefillTestCase{
+ .exp_date_from_js = u"04/2999",
+ .triggers_refill = false,
+ },
+ // Maybe the website did something we don't support.
+ RefillTestCase{
+ .exp_date_from_js = u"April / 2999",
+ .triggers_refill = false,
+ },
+ // Maybe the website just added some whitespaces.
+ RefillTestCase{
+ .exp_date_from_js = u"04 / 2999",
+ .triggers_refill = false,
+ },
+ // Don't trigger refill on 3 digit years.
+ RefillTestCase{
+ .exp_date_from_js = u"04 / 299",
+ .triggers_refill = false,
+ }));
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_data_model.cc b/chromium/components/autofill/core/browser/data_model/autofill_data_model.cc
index 865f1e7d6f6..631f9191bf0 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_data_model.cc
+++ b/chromium/components/autofill/core/browser/data_model/autofill_data_model.cc
@@ -10,6 +10,7 @@
#include "components/autofill/core/browser/data_model/autofill_metadata.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "url/gurl.h"
namespace autofill {
@@ -22,10 +23,31 @@ AutofillDataModel::AutofillDataModel(const std::string& guid,
}
AutofillDataModel::~AutofillDataModel() {}
+int AutofillDataModel::GetDaysSinceLastUse(base::Time current_time) const {
+ if (current_time <= use_date_)
+ return 0;
+
+ return (current_time - use_date_).InDays();
+}
+
bool AutofillDataModel::IsVerified() const {
return !origin_.empty() && !GURL(origin_).is_valid();
}
+double AutofillDataModel::GetRankingScore(base::Time current_time) const {
+ if (base::FeatureList::IsEnabled(features::kAutofillEnableRankingFormula)) {
+ // Exponentially decay the use count by the days since the data model was
+ // last used.
+ return log10(use_count_ + 1) *
+ exp(-GetDaysSinceLastUse(current_time) /
+ features::kAutofillRankingFormulaUsageHalfLife.Get());
+ }
+
+ // Default to legacy frecency scoring.
+ return -log(static_cast<double>(GetDaysSinceLastUse(current_time)) + 2) /
+ log(use_count_ + 1);
+}
+
// TODO(crbug.com/629507): Add support for injected mock clock for testing.
void AutofillDataModel::RecordUse() {
++use_count_;
@@ -37,11 +59,11 @@ bool AutofillDataModel::UseDateEqualsInSeconds(
return !((other->use_date() - use_date()).InSeconds());
}
-bool AutofillDataModel::HasGreaterFrecencyThan(
+bool AutofillDataModel::HasGreaterRankingThan(
const AutofillDataModel* other,
base::Time comparison_time) const {
- double score = GetFrecencyScore(comparison_time);
- double other_score = other->GetFrecencyScore(comparison_time);
+ double score = GetRankingScore(comparison_time);
+ double other_score = other->GetRankingScore(comparison_time);
// Ties are broken by MRU, then by GUID comparison.
const double kEpsilon = 0.00001;
@@ -67,16 +89,6 @@ bool AutofillDataModel::SetMetadata(const AutofillMetadata metadata) {
return true;
}
-double AutofillDataModel::GetFrecencyScore(base::Time time) const {
- // The formula calculates a score based on both the frequency and the recency
- // of the profile and leveraging the properties of the logarithmic function.
- // DaysSinceLastUse() and |use_count_| are offset because their minimum values
- // are respectively 0 and 1 but the formula requires at least a value of 2.
- // Please update getFrecencyScore in ChromePaymentRequestService.java as well
- // if below formula needs update.
- return -log((time - use_date_).InDays() + 2) / log(use_count_ + 1);
-}
-
bool AutofillDataModel::IsDeletable() const {
return IsAutofillEntryWithUseDateDeletable(use_date_);
}
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_data_model.h b/chromium/components/autofill/core/browser/data_model/autofill_data_model.h
index 9cf913a6f49..0219cd73ffa 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_data_model.h
+++ b/chromium/components/autofill/core/browser/data_model/autofill_data_model.h
@@ -44,6 +44,10 @@ class AutofillDataModel : public FormGroup {
AutofillDataModel(const std::string& guid, const std::string& origin);
~AutofillDataModel() override;
+ // Calculates the number of days since the model was last used by subtracting
+ // the model's last recent |use_date_| from the |current_time|.
+ int GetDaysSinceLastUse(base::Time current_time) const;
+
// Returns true if the data in this model was entered directly by the user,
// rather than automatically aggregated.
bool IsVerified() const;
@@ -71,12 +75,13 @@ class AutofillDataModel : public FormGroup {
modification_date_ = time;
}
- // Compares two data models according to their frecency score. The score uses
- // a combination of frequency and recency to determine the relevance of the
- // profile. |comparison_time_| allows consistent sorting throughout the
- // comparisons.
- bool HasGreaterFrecencyThan(const AutofillDataModel* other,
- base::Time comparison_time) const;
+ // Compares two data models according to their ranking score. The score uses
+ // a combination of use count and days since last use to determine the
+ // relevance of the profile. |comparison_time_| allows consistent sorting
+ // throughout the comparisons. A greater ranking score corresponds to a higher
+ // ranking on the suggestion list.
+ bool HasGreaterRankingThan(const AutofillDataModel* other,
+ base::Time comparison_time) const;
// Gets the metadata associated with this autofill data model.
virtual AutofillMetadata GetMetadata() const;
@@ -90,16 +95,14 @@ class AutofillDataModel : public FormGroup {
virtual bool IsDeletable() const;
protected:
+ // Calculate the ranking score of a card or profile depending on their use
+ // count and most recent use date.
+ virtual double GetRankingScore(base::Time current_time) const;
+
// Called to update |use_count_| and |use_date_| when this data model is
// the subject of user interaction (usually, when it's used to fill a form).
void RecordUse();
- // Returns a score based on both the recency (relative to |time|) and
- // frequency for the model. The score is a negative number where a higher
- // value is more relevant. |time| is passed as a parameter to ensure
- // consistent results.
- double GetFrecencyScore(base::Time time) const;
-
private:
// A globally unique ID for this object.
std::string guid_;
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_data_model_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_data_model_unittest.cc
index 47ba09fbc19..231858d0532 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_data_model_unittest.cc
+++ b/chromium/components/autofill/core/browser/data_model/autofill_data_model_unittest.cc
@@ -7,11 +7,15 @@
#include <stddef.h>
#include "base/compiler_specific.h"
+#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/autofill_metadata.h"
+#include "components/autofill/core/browser/data_model/test_autofill_data_model.h"
#include "components/autofill/core/browser/test_autofill_clock.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace autofill {
@@ -22,35 +26,6 @@ namespace {
const base::Time kArbitraryTime = base::Time::FromDoubleT(25);
-// Provides concrete implementations for pure virtual methods.
-class TestAutofillDataModel : public AutofillDataModel {
- public:
- TestAutofillDataModel(const std::string& guid, const std::string& origin)
- : AutofillDataModel(guid, origin) {}
- TestAutofillDataModel(const std::string& guid,
- size_t use_count,
- base::Time use_date)
- : AutofillDataModel(guid, std::string()) {
- set_use_count(use_count);
- set_use_date(use_date);
- }
-
- TestAutofillDataModel(const TestAutofillDataModel&) = delete;
- TestAutofillDataModel& operator=(const TestAutofillDataModel&) = delete;
-
- ~TestAutofillDataModel() override {}
-
- private:
- std::u16string GetRawInfo(ServerFieldType type) const override {
- return std::u16string();
- }
- void SetRawInfoWithVerificationStatus(
- ServerFieldType type,
- const std::u16string& value,
- structured_address::VerificationStatus status) override {}
- void GetSupportedTypes(ServerFieldTypeSet* supported_types) const override {}
-};
-
} // namespace
TEST(AutofillDataModelTest, IsVerified) {
@@ -117,7 +92,7 @@ TEST(AutofillDataModelTest, IsDeletable) {
}
enum Expectation { GREATER, LESS };
-struct HasGreaterFrecencyThanTestCase {
+struct AutofillDataModelRankingTestCase {
const std::string guid_a;
const int use_count_a;
const base::Time use_date_a;
@@ -129,8 +104,62 @@ struct HasGreaterFrecencyThanTestCase {
base::Time now = AutofillClock::Now();
+class AutofillDataModelRankingTest
+ : public testing::TestWithParam<AutofillDataModelRankingTestCase> {};
+
+TEST_P(AutofillDataModelRankingTest, HasGreaterRankingThan) {
+ // Enable kAutofillEnableRankingFormula so that it uses the new formula
+ // instead of frecency.
+ base::test::ScopedFeatureList feature_list_;
+ feature_list_.InitAndEnableFeature(features::kAutofillEnableRankingFormula);
+
+ auto test_case = GetParam();
+ TestAutofillDataModel model_a(test_case.guid_a, test_case.use_count_a,
+ test_case.use_date_a);
+ TestAutofillDataModel model_b(test_case.guid_b, test_case.use_count_b,
+ test_case.use_date_b);
+
+ EXPECT_EQ(test_case.expectation == GREATER,
+ model_a.HasGreaterRankingThan(&model_b, now));
+ EXPECT_NE(test_case.expectation == GREATER,
+ model_b.HasGreaterRankingThan(&model_a, now));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AutofillDataModelTest,
+ AutofillDataModelRankingTest,
+ testing::Values(
+ // Same ranking score, model_a has a smaller GUID (tie breaker).
+ AutofillDataModelRankingTestCase{"guid_a", 8, now, "guid_b", 8, now,
+ LESS},
+ // Same days since last use, model_a has a bigger use count.
+ AutofillDataModelRankingTestCase{"guid_a", 10, now, "guid_b", 8, now,
+ GREATER},
+ // Same days since last use, model_a has a smaller use count.
+ AutofillDataModelRankingTestCase{"guid_a", 8, now, "guid_b", 10, now,
+ LESS},
+ // Same days since last use, model_a has larger use count.
+ AutofillDataModelRankingTestCase{"guid_a", 8, now, "guid_b", 8,
+ now - base::Days(1), GREATER},
+ // Same use count, model_a has smaller days since last use.
+ AutofillDataModelRankingTestCase{"guid_a", 8, now - base::Days(1),
+ "guid_b", 8, now, LESS},
+ // Special case: occasional profiles. A profile with relatively low
+ // usage and used recently (model_b) should not rank higher than a more
+ // used profile that has been unused for a short amount of time
+ // (model_a).
+ AutofillDataModelRankingTestCase{"guid_a", 300, now - base::Days(5),
+ "guid_b", 10, now - base::Days(1),
+ GREATER},
+ // Special case: moving. A new profile used frequently (model_b) should
+ // rank higher than a profile with more usage that has not been used for
+ // a while (model_a).
+ AutofillDataModelRankingTestCase{"guid_a", 90, now - base::Days(20),
+ "guid_b", 10, now - base::Days(5),
+ LESS}));
+
class HasGreaterFrecencyThanTest
- : public testing::TestWithParam<HasGreaterFrecencyThanTestCase> {};
+ : public testing::TestWithParam<AutofillDataModelRankingTestCase> {};
TEST_P(HasGreaterFrecencyThanTest, HasGreaterFrecencyThan) {
auto test_case = GetParam();
@@ -140,42 +169,42 @@ TEST_P(HasGreaterFrecencyThanTest, HasGreaterFrecencyThan) {
test_case.use_date_b);
EXPECT_EQ(test_case.expectation == GREATER,
- model_a.HasGreaterFrecencyThan(&model_b, now));
+ model_a.HasGreaterRankingThan(&model_b, now));
EXPECT_NE(test_case.expectation == GREATER,
- model_b.HasGreaterFrecencyThan(&model_a, now));
+ model_b.HasGreaterRankingThan(&model_a, now));
}
INSTANTIATE_TEST_SUITE_P(
AutofillDataModelTest,
HasGreaterFrecencyThanTest,
testing::Values(
- // Same frecency, model_a has a smaller GUID (tie breaker).
- HasGreaterFrecencyThanTestCase{"guid_a", 8, now, "guid_b", 8, now,
- LESS},
- // Same recency, model_a has a bigger frequency.
- HasGreaterFrecencyThanTestCase{"guid_a", 10, now, "guid_b", 8, now,
- GREATER},
- // Same recency, model_a has a smaller frequency.
- HasGreaterFrecencyThanTestCase{"guid_a", 8, now, "guid_b", 10, now,
- LESS},
- // Same frequency, model_a is more recent.
- HasGreaterFrecencyThanTestCase{"guid_a", 8, now, "guid_b", 8,
- now - base::Days(1), GREATER},
- // Same frequency, model_a is less recent.
- HasGreaterFrecencyThanTestCase{"guid_a", 8, now - base::Days(1),
- "guid_b", 8, now, LESS},
+ // Same ranking score, model_a has a smaller GUID (tie breaker).
+ AutofillDataModelRankingTestCase{"guid_a", 8, now, "guid_b", 8, now,
+ LESS},
+ // Same days since last use, model_a has a bigger use count.
+ AutofillDataModelRankingTestCase{"guid_a", 10, now, "guid_b", 8, now,
+ GREATER},
+ // Same days since last use, model_a has a smaller use count.
+ AutofillDataModelRankingTestCase{"guid_a", 8, now, "guid_b", 10, now,
+ LESS},
+ // Same days since last use, model_a has larger use count.
+ AutofillDataModelRankingTestCase{"guid_a", 8, now, "guid_b", 8,
+ now - base::Days(1), GREATER},
+ // Same use count, model_a has smaller days since last use.
+ AutofillDataModelRankingTestCase{"guid_a", 8, now - base::Days(1),
+ "guid_b", 8, now, LESS},
// Special case: occasional profiles. A profile with relatively low
// usage and used recently (model_b) should not rank higher than a more
// used profile that has been unused for a short amount of time
// (model_a).
- HasGreaterFrecencyThanTestCase{"guid_a", 300, now - base::Days(5),
- "guid_b", 10, now - base::Days(1),
- GREATER},
+ AutofillDataModelRankingTestCase{"guid_a", 300, now - base::Days(5),
+ "guid_b", 10, now - base::Days(1),
+ GREATER},
// Special case: moving. A new profile used frequently (model_b) should
// rank higher than a profile with more usage that has not been used for
// a while (model_a).
- HasGreaterFrecencyThanTestCase{"guid_a", 300, now - base::Days(15),
- "guid_b", 10, now - base::Days(1),
- LESS}));
+ AutofillDataModelRankingTestCase{"guid_a", 300, now - base::Days(15),
+ "guid_b", 10, now - base::Days(1),
+ LESS}));
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_offer_data.cc b/chromium/components/autofill/core/browser/data_model/autofill_offer_data.cc
index 8135c7886c1..5a4e37fda7d 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_offer_data.cc
+++ b/chromium/components/autofill/core/browser/data_model/autofill_offer_data.cc
@@ -11,6 +11,46 @@
namespace autofill {
+// static
+AutofillOfferData AutofillOfferData::GPayCardLinkedOffer(
+ int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::vector<int64_t>& eligible_instrument_id,
+ const std::string& offer_reward_amount) {
+ return AutofillOfferData(offer_id, expiry, merchant_origins,
+ offer_details_url, display_strings,
+ eligible_instrument_id, offer_reward_amount);
+}
+
+// static
+AutofillOfferData AutofillOfferData::FreeListingCouponOffer(
+ int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::string& promo_code) {
+ return AutofillOfferData(OfferType::FREE_LISTING_COUPON_OFFER, offer_id,
+ expiry, merchant_origins, offer_details_url,
+ display_strings, promo_code);
+}
+
+// static
+AutofillOfferData AutofillOfferData::GPayPromoCodeOffer(
+ int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::string& promo_code) {
+ return AutofillOfferData(OfferType::GPAY_PROMO_CODE_OFFER, offer_id, expiry,
+ merchant_origins, offer_details_url, display_strings,
+ promo_code);
+}
+
AutofillOfferData::AutofillOfferData() = default;
AutofillOfferData::~AutofillOfferData() = default;
@@ -32,28 +72,28 @@ bool AutofillOfferData::operator!=(
int AutofillOfferData::Compare(
const AutofillOfferData& other_offer_data) const {
- int comparison = offer_id - other_offer_data.offer_id;
+ int comparison = offer_id_ - other_offer_data.offer_id_;
if (comparison != 0)
return comparison;
comparison =
- offer_reward_amount.compare(other_offer_data.offer_reward_amount);
+ offer_reward_amount_.compare(other_offer_data.offer_reward_amount_);
if (comparison != 0)
return comparison;
- if (expiry < other_offer_data.expiry)
+ if (expiry_ < other_offer_data.expiry_)
return -1;
- if (expiry > other_offer_data.expiry)
+ if (expiry_ > other_offer_data.expiry_)
return 1;
- comparison = offer_details_url.spec().compare(
- other_offer_data.offer_details_url.spec());
+ comparison = offer_details_url_.spec().compare(
+ other_offer_data.offer_details_url_.spec());
if (comparison != 0)
return comparison;
- std::vector<GURL> merchant_origins_copy = merchant_origins;
+ std::vector<GURL> merchant_origins_copy = merchant_origins_;
std::vector<GURL> other_merchant_origins_copy =
- other_offer_data.merchant_origins;
+ other_offer_data.merchant_origins_;
std::sort(merchant_origins_copy.begin(), merchant_origins_copy.end());
std::sort(other_merchant_origins_copy.begin(),
other_merchant_origins_copy.end());
@@ -62,9 +102,9 @@ int AutofillOfferData::Compare(
if (merchant_origins_copy > other_merchant_origins_copy)
return 1;
- std::vector<int64_t> eligible_instrument_id_copy = eligible_instrument_id;
+ std::vector<int64_t> eligible_instrument_id_copy = eligible_instrument_id_;
std::vector<int64_t> other_eligible_instrument_id_copy =
- other_offer_data.eligible_instrument_id;
+ other_offer_data.eligible_instrument_id_;
std::sort(eligible_instrument_id_copy.begin(),
eligible_instrument_id_copy.end());
std::sort(other_eligible_instrument_id_copy.begin(),
@@ -74,55 +114,80 @@ int AutofillOfferData::Compare(
if (eligible_instrument_id_copy > other_eligible_instrument_id_copy)
return 1;
- comparison = promo_code.compare(other_offer_data.promo_code);
+ comparison = promo_code_.compare(other_offer_data.promo_code_);
if (comparison != 0)
return comparison;
- comparison = display_strings.value_prop_text.compare(
- other_offer_data.display_strings.value_prop_text);
+ comparison = display_strings_.value_prop_text.compare(
+ other_offer_data.display_strings_.value_prop_text);
if (comparison != 0)
return comparison;
- comparison = display_strings.see_details_text.compare(
- other_offer_data.display_strings.see_details_text);
+ comparison = display_strings_.see_details_text.compare(
+ other_offer_data.display_strings_.see_details_text);
if (comparison != 0)
return comparison;
- comparison = display_strings.usage_instructions_text.compare(
- other_offer_data.display_strings.usage_instructions_text);
+ comparison = display_strings_.usage_instructions_text.compare(
+ other_offer_data.display_strings_.usage_instructions_text);
if (comparison != 0)
return comparison;
return 0;
}
-AutofillOfferData::OfferType AutofillOfferData::GetOfferType() const {
- // Card-linked offers have at least one |eligible_instrument_id|.
- if (!eligible_instrument_id.empty())
- return OfferType::GPAY_CARD_LINKED_OFFER;
-
- // Promo code offers have the promo code field populated.
- // TODO(crbug.com/1203811): When GPay-activated promo codes become available,
- // save this OfferType as a class member variable (so as to differentiate
- // GPay and FLC promo codes) and simply return that value for this
- // function instead.
- if (!promo_code.empty())
- return OfferType::FREE_LISTING_COUPON_OFFER;
-
- return OfferType::UNKNOWN;
-}
-
bool AutofillOfferData::IsCardLinkedOffer() const {
return GetOfferType() == OfferType::GPAY_CARD_LINKED_OFFER;
}
bool AutofillOfferData::IsPromoCodeOffer() const {
+ return GetOfferType() == OfferType::GPAY_PROMO_CODE_OFFER ||
+ GetOfferType() == OfferType::FREE_LISTING_COUPON_OFFER;
+}
+
+bool AutofillOfferData::IsGPayPromoCodeOffer() const {
+ return GetOfferType() == OfferType::GPAY_PROMO_CODE_OFFER;
+}
+
+bool AutofillOfferData::IsFreeListingCouponOffer() const {
return GetOfferType() == OfferType::FREE_LISTING_COUPON_OFFER;
}
bool AutofillOfferData::IsActiveAndEligibleForOrigin(const GURL& origin) const {
- return expiry > AutofillClock::Now() &&
- base::ranges::count(merchant_origins, origin) > 0;
+ return expiry_ > AutofillClock::Now() &&
+ base::ranges::count(merchant_origins_, origin) > 0;
}
+AutofillOfferData::AutofillOfferData(
+ int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::vector<int64_t>& eligible_instrument_id,
+ const std::string& offer_reward_amount)
+ : offer_type_(OfferType::GPAY_CARD_LINKED_OFFER),
+ offer_id_(offer_id),
+ expiry_(expiry),
+ offer_details_url_(offer_details_url),
+ merchant_origins_(merchant_origins),
+ display_strings_(display_strings),
+ offer_reward_amount_(offer_reward_amount),
+ eligible_instrument_id_(eligible_instrument_id) {}
+
+AutofillOfferData::AutofillOfferData(OfferType offer_type,
+ int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::string& promo_code)
+ : offer_type_(offer_type),
+ offer_id_(offer_id),
+ expiry_(expiry),
+ offer_details_url_(offer_details_url),
+ merchant_origins_(merchant_origins),
+ display_strings_(display_strings),
+ promo_code_(promo_code) {}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_offer_data.h b/chromium/components/autofill/core/browser/data_model/autofill_offer_data.h
index 87efcece3c8..f3f5fa167cc 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_offer_data.h
+++ b/chromium/components/autofill/core/browser/data_model/autofill_offer_data.h
@@ -30,21 +30,46 @@ struct DisplayStrings {
// with certain cards, and the unique ids of those cards are stored in
// |eligible_instrument_id|. Promo code offers are redeemable with autofillable
// promo codes. Merchants are determined by |merchant_origins|.
-struct AutofillOfferData {
+class AutofillOfferData {
public:
// The specific type of offer.
- // TODO(crbug.com/1203811): Add GPAY_PROMO_CODE_OFFER once GPay-activated
- // promo codes become available, and create a way to differentiate them
- // from free-listing coupon codes.
enum class OfferType {
// Default value, should not be used.
UNKNOWN,
// GPay-activated card linked offer.
GPAY_CARD_LINKED_OFFER,
+ // GPay-activated promo code offer.
+ GPAY_PROMO_CODE_OFFER,
// Promo code offer from the FreeListingCouponService.
FREE_LISTING_COUPON_OFFER,
};
+ // Returns an AutofillOfferData for a GPay card-linked offer.
+ static AutofillOfferData GPayCardLinkedOffer(
+ int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::vector<int64_t>& eligible_instrument_id,
+ const std::string& offer_reward_amount);
+ // Returns an AutofillOfferData for a free-listing coupon offer.
+ static AutofillOfferData FreeListingCouponOffer(
+ int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::string& promo_code);
+ // Returns an AutofillOfferData for a GPay promo code offer.
+ static AutofillOfferData GPayPromoCodeOffer(
+ int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::string& promo_code);
+
AutofillOfferData();
~AutofillOfferData();
AutofillOfferData(const AutofillOfferData&);
@@ -57,52 +82,115 @@ struct AutofillOfferData {
// result of first found difference.
int Compare(const AutofillOfferData& other_offer_data) const;
- // Returns the specific type of the offer, which will inform decisions made by
- // other classes, such as UI rendering or metrics.
- OfferType GetOfferType() const;
-
// Returns true if the current offer is a card-linked offer.
bool IsCardLinkedOffer() const;
- // Returns true if the current offer is a promo code offer.
+ // Returns true if the current offer is a GPay promo code offer or an offer
+ // from the FreeListingCouponService.
bool IsPromoCodeOffer() const;
+ // Returns true if the current offer is a GPay promo code offer.
+ bool IsGPayPromoCodeOffer() const;
+
+ // Returns true if the current offer is an offer from the
+ // FreeListingCouponService.
+ bool IsFreeListingCouponOffer() const;
+
// Returns true if the current offer is 1) not expired and 2) contains the
// given |origin| in the list of |merchant_origins|.
bool IsActiveAndEligibleForOrigin(const GURL& origin) const;
+ OfferType GetOfferType() const { return offer_type_; }
+ int64_t GetOfferId() const { return offer_id_; }
+ const base::Time& GetExpiry() const { return expiry_; }
+ const std::vector<GURL>& GetMerchantOrigins() const {
+ return merchant_origins_;
+ }
+ const GURL& GetOfferDetailsUrl() const { return offer_details_url_; }
+ const DisplayStrings& GetDisplayStrings() const { return display_strings_; }
+ const std::string& GetOfferRewardAmount() const {
+ return offer_reward_amount_;
+ }
+ const std::vector<int64_t>& GetEligibleInstrumentIds() const {
+ return eligible_instrument_id_;
+ }
+ const std::string& GetPromoCode() const { return promo_code_; }
+
+#ifdef UNIT_TEST
+ void SetOfferIdForTesting(int64_t offer_id) { offer_id_ = offer_id; }
+ void SetMerchantOriginForTesting(const std::vector<GURL>& merchant_origins) {
+ merchant_origins_ = merchant_origins;
+ }
+ void SetEligibleInstrumentIdForTesting(
+ const std::vector<int64_t>& eligible_instrument_id) {
+ eligible_instrument_id_ = eligible_instrument_id;
+ }
+
+ void SetPromoCode(const std::string& promo_code) { promo_code_ = promo_code; }
+
+ void SetValuePropTextInDisplayStrings(const std::string& value_prop_text) {
+ display_strings_.value_prop_text = value_prop_text;
+ }
+
+ void SetOfferDetailsUrl(const GURL& offer_details_url) {
+ offer_details_url_ = offer_details_url;
+ }
+#endif
+
+ private:
+ // Constructs an AutofillOfferData for a card-linked offer.
+ AutofillOfferData(int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::vector<int64_t>& eligible_instrument_id,
+ const std::string& offer_reward_amount);
+ // Constructs an AutofillOfferData for a promo code offer (GPay or FLC).
+ AutofillOfferData(OfferType offer_type,
+ int64_t offer_id,
+ const base::Time& expiry,
+ const std::vector<GURL>& merchant_origins,
+ const GURL& offer_details_url,
+ const DisplayStrings& display_strings,
+ const std::string& promo_code);
+
+ // The specific type of offer, which informs decisions made by other classes,
+ // such as UI rendering or metrics.
+ OfferType offer_type_;
+
// The unique server ID for this offer data.
- int64_t offer_id;
+ int64_t offer_id_;
// The timestamp when the offer will expire. Expired offers will not be shown
// in the frontend.
- base::Time expiry;
+ base::Time expiry_;
// The URL that contains the offer details.
- GURL offer_details_url;
+ GURL offer_details_url_;
// The merchants' URL origins (path not included) where this offer can be
// redeemed.
- std::vector<GURL> merchant_origins;
+ std::vector<GURL> merchant_origins_;
// Optional server-driven strings for certain offer elements. Generally most
// useful for promo code offers, but could potentially apply to card-linked
// offers as well.
- DisplayStrings display_strings;
+ DisplayStrings display_strings_;
/* Card-linked offer-specific fields */
// The string including the reward details of the offer. Could be either
// percentage off (XXX%) or fixed amount off ($XXX).
- std::string offer_reward_amount;
+ std::string offer_reward_amount_;
// The ids of the cards this offer can be applied to.
- std::vector<int64_t> eligible_instrument_id;
+ std::vector<int64_t> eligible_instrument_id_;
/* Promo code offer-specific fields */
// A promo/gift/coupon code that can be applied at checkout with the merchant.
- std::string promo_code;
+ std::string promo_code_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile.cc
index 95dd4ecf0a1..a6a61be5b64 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_profile.cc
+++ b/chromium/components/autofill/core/browser/data_model/autofill_profile.cc
@@ -49,7 +49,6 @@
#include "third_party/libaddressinput/chromium/addressinput_util.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h"
-#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_metadata.h"
#include "ui/base/l10n/l10n_util.h"
using base::ASCIIToUTF16;
@@ -320,19 +319,15 @@ void AutofillProfile::GetMatchingTypes(
std::u16string AutofillProfile::GetRawInfo(ServerFieldType type) const {
const FormGroup* form_group = FormGroupForType(AutofillType(type));
- if (!form_group) {
- NOTREACHED();
+ if (!form_group)
return std::u16string();
- }
return form_group->GetRawInfo(type);
}
int AutofillProfile::GetRawInfoAsInt(ServerFieldType type) const {
const FormGroup* form_group = FormGroupForType(AutofillType(type));
- if (!form_group) {
- NOTREACHED();
+ if (!form_group)
return 0;
- }
return form_group->GetRawInfoAsInt(type);
}
@@ -341,11 +336,9 @@ void AutofillProfile::SetRawInfoWithVerificationStatus(
const std::u16string& value,
VerificationStatus status) {
FormGroup* form_group = MutableFormGroupForType(AutofillType(type));
- if (!form_group) {
- NOTREACHED();
- return;
+ if (form_group) {
+ form_group->SetRawInfoWithVerificationStatus(type, value, status);
}
- form_group->SetRawInfoWithVerificationStatus(type, value, status);
}
void AutofillProfile::SetRawInfoAsIntWithVerificationStatus(
@@ -353,11 +346,9 @@ void AutofillProfile::SetRawInfoAsIntWithVerificationStatus(
int value,
VerificationStatus status) {
FormGroup* form_group = MutableFormGroupForType(AutofillType(type));
- if (!form_group) {
- NOTREACHED();
- return;
+ if (form_group) {
+ form_group->SetRawInfoAsIntWithVerificationStatus(type, value, status);
}
- form_group->SetRawInfoAsIntWithVerificationStatus(type, value, status);
}
void AutofillProfile::GetSupportedTypes(
@@ -387,7 +378,8 @@ bool AutofillProfile::IsPresentButInvalid(ServerFieldType type) const {
return country == "US" && !IsValidZip(data);
case PHONE_HOME_WHOLE_NUMBER:
- return !i18n::PhoneObject(data, country).IsValidNumber();
+ return !i18n::PhoneObject(data, country, /*infer_country_code=*/false)
+ .IsValidNumber();
case EMAIL_ADDRESS:
return !IsValidEmailAddress(data);
@@ -711,7 +703,7 @@ bool AutofillProfile::MergeDataFrom(const AutofillProfile& profile,
// Update the use-count to be the max of the two merge-counts. Alternatively,
// we could have summed the two merge-counts. We don't sum because it skews
- // the frecency value on merge and double counts usage on profile reuse.
+ // the ranking score value on merge and double counts usage on profile reuse.
// Profile reuse is accounted for on RecordUseOf() on selection of a profile
// in the autofill drop-down; we don't need to account for that here. Further,
// a similar, fully-typed submission that merges to an existing profile should
@@ -864,13 +856,13 @@ std::u16string AutofillProfile::ConstructInferredLabel(
AutofillProfile trimmed_profile(guid(), origin());
trimmed_profile.SetInfo(region_code_type, profile_region_code, app_locale);
trimmed_profile.set_language_code(language_code());
+ AutofillCountry country(address_region_code);
std::vector<ServerFieldType> remaining_fields;
for (size_t i = 0; i < included_fields_size && num_fields_to_use > 0; ++i) {
::i18n::addressinput::AddressField address_field;
if (!i18n::FieldForType(included_fields[i], &address_field) ||
- !::i18n::addressinput::IsFieldUsed(address_field,
- address_region_code) ||
+ !country.IsAddressFieldSettingAccessible(address_field) ||
address_field == ::i18n::addressinput::COUNTRY) {
remaining_fields.push_back(included_fields[i]);
continue;
@@ -1197,16 +1189,16 @@ bool AutofillProfile::HasStructuredData() {
ServerFieldTypeSet AutofillProfile::FindInaccessibleProfileValues(
const std::string& country_code) const {
ServerFieldTypeSet inaccessible_fields;
+ AutofillCountry country(country_code);
// Consider only AddressFields which are invisible in the settings for some
// countries.
for (const AddressField& field_type :
{AddressField::ADMIN_AREA, AddressField::LOCALITY,
AddressField::DEPENDENT_LOCALITY, AddressField::POSTAL_CODE,
AddressField::SORTING_CODE}) {
- ServerFieldType server_field_type =
- AddressFieldToServerFieldType(field_type);
- if (!GetRawInfo(server_field_type).empty() &&
- !::i18n::addressinput::IsFieldUsed(field_type, country_code)) {
+ ServerFieldType server_field_type = i18n::TypeForField(field_type);
+ if (HasRawInfo(server_field_type) &&
+ !country.IsAddressFieldSettingAccessible(field_type)) {
inaccessible_fields.insert(server_field_type);
}
}
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile.h b/chromium/components/autofill/core/browser/data_model/autofill_profile.h
index 758258e6512..6099221f2f9 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_profile.h
+++ b/chromium/components/autofill/core/browser/data_model/autofill_profile.h
@@ -10,12 +10,9 @@
#include <array>
#include <iosfwd>
#include <list>
-#include <map>
#include <string>
#include <vector>
-#include "base/compiler_specific.h"
-#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/data_model/address.h"
@@ -23,7 +20,6 @@
#include "components/autofill/core/browser/data_model/birthdate.h"
#include "components/autofill/core/browser/data_model/contact_info.h"
#include "components/autofill/core/browser/data_model/phone_number.h"
-#include "components/autofill/core/browser/proto/server.pb.h"
namespace autofill {
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc
index 81722c99995..f24164ecae5 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc
+++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc
@@ -197,13 +197,13 @@ void CopyAddressLineInformationFromProfile(const AutofillProfile& source,
source.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS));
}
-// Sorts |profiles| by frecency.
-void SortProfilesByFrecency(std::vector<AutofillProfile*>* profiles) {
+// Sorts |profiles| by ranking score.
+void SortProfilesByRankingScore(std::vector<AutofillProfile*>* profiles) {
base::Time comparison_time = AutofillClock::Now();
std::sort(
profiles->begin(), profiles->end(),
[comparison_time](const AutofillProfile* a, const AutofillProfile* b) {
- return a->HasGreaterFrecencyThan(b, comparison_time);
+ return a->HasGreaterRankingThan(b, comparison_time);
});
}
@@ -675,6 +675,8 @@ bool AutofillProfileComparator::MergePhoneNumbers(
}
// Figure out a country code hint.
+ // TODO(crbug.com/1313862) |GetNonEmptyOf()| prefers |p1| in case both are
+ // non empty.
const AutofillType kCountryCode(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE);
std::string region = UTF16ToUTF8(GetNonEmptyOf(p1, p2, kCountryCode));
if (region.empty())
@@ -695,9 +697,25 @@ bool AutofillProfileComparator::MergePhoneNumbers(
return false;
}
+ // `country_code()` defaults to the provided `region`. But if one of the
+ // numbers is in international format, we should prefer that country code.
+ auto HasInternationalCountryCode =
+ [](const ::i18n::phonenumbers::PhoneNumber& number) {
+ return number.country_code_source() !=
+ ::i18n::phonenumbers::PhoneNumber::FROM_DEFAULT_COUNTRY;
+ };
+
::i18n::phonenumbers::PhoneNumber merged_number;
- DCHECK_EQ(n1.country_code(), n2.country_code());
- merged_number.set_country_code(n1.country_code());
+ // There are three cases for country codes:
+ // - Both numbers are in international format, so because the numbers are
+ // mergeable, they are equal.
+ // - Both are not in international format, so their country codes both default
+ // to `region`.
+ // - One of them is in international format, so we prefer that country code.
+ DCHECK(HasInternationalCountryCode(n1) != HasInternationalCountryCode(n2) ||
+ n1.country_code() == n2.country_code());
+ merged_number.set_country_code(
+ HasInternationalCountryCode(n1) ? n1.country_code() : n2.country_code());
merged_number.set_national_number(
std::max(n1.national_number(), n2.national_number()));
if (n1.has_extension() && !n1.extension().empty()) {
@@ -714,9 +732,15 @@ bool AutofillProfileComparator::MergePhoneNumbers(
std::max(n1.number_of_leading_zeros(), n2.number_of_leading_zeros()));
}
+ // Format the `merged_number` in international format only if at least one
+ // of the country codes was derived from the number itself. This is done
+ // consistently with `::autofill::i18n::FormatValidatedNumber()` and
+ // `::autofill::i18n::ParsePhoneNumber()`, which backs the `PhoneNumber`
+ // implementation.
PhoneNumberUtil::PhoneNumberFormat format =
- region.empty() ? PhoneNumberUtil::NATIONAL
- : PhoneNumberUtil::INTERNATIONAL;
+ HasInternationalCountryCode(n1) || HasInternationalCountryCode(n2)
+ ? PhoneNumberUtil::INTERNATIONAL
+ : PhoneNumberUtil::NATIONAL;
std::string new_number;
phone_util->Format(merged_number, format, &new_number);
@@ -1005,12 +1029,12 @@ bool AutofillProfileComparator::MergeBirthdates(const AutofillProfile& p1,
bool AutofillProfileComparator::ProfilesHaveDifferentSettingsVisibleValues(
const AutofillProfile& p1,
- const AutofillProfile& p2) {
-
+ const AutofillProfile& p2,
+ const std::string& app_locale) {
// Return true if at least one value corresponding to the settings visible
// types is different between the two profiles.
return base::ranges::any_of(GetUserVisibleTypes(), [&](const auto type) {
- return p1.GetRawInfo(type) != p2.GetRawInfo(type);
+ return p1.GetInfo(type, app_locale) != p2.GetInfo(type, app_locale);
});
}
@@ -1033,8 +1057,8 @@ bool AutofillProfileComparator::IsMergeCandidate(
// If the two profiles have at least one settings-visible value that is
// different, |existing_profile| is a merge candidate.
- return ProfilesHaveDifferentSettingsVisibleValues(merged_profile,
- existing_profile);
+ return ProfilesHaveDifferentSettingsVisibleValues(
+ merged_profile, existing_profile, app_locale);
}
// static
@@ -1047,8 +1071,8 @@ AutofillProfileComparator::GetAutofillProfileMergeCandidate(
// effects.
std::vector<AutofillProfile*> existing_profiles_copies = existing_profiles;
- // Sort the profiles by frecency.
- SortProfilesByFrecency(&existing_profiles_copies);
+ // Sort the profiles by ranking score.
+ SortProfilesByRankingScore(&existing_profiles_copies);
// Find and return the first profile that classifies as a merge candidate. If
// not profile classifies, return |absl::nullopt|.
@@ -1078,10 +1102,10 @@ std::string AutofillProfileComparator::MergeProfile(
for (const auto& profile : existing_profiles)
existing_profile_copies.push_back(*profile.get());
- // Sort the existing profiles in decreasing order of frecency, so the "best"
- // profiles are checked first. Put the verified profiles last so the non
- // verified profiles get deduped among themselves before reaching the verified
- // profiles.
+ // Sort the existing profiles in decreasing order of ranking score, so the
+ // "best" profiles are checked first. Put the verified profiles last so the
+ // non verified profiles get deduped among themselves before reaching the
+ // verified profiles.
// TODO(crbug.com/620521): Remove the check for verified from the sort.
base::Time comparison_time = AutofillClock::Now();
std::sort(
@@ -1089,7 +1113,7 @@ std::string AutofillProfileComparator::MergeProfile(
[comparison_time](const AutofillProfile& a, const AutofillProfile& b) {
if (a.IsVerified() != b.IsVerified())
return !a.IsVerified();
- return a.HasGreaterFrecencyThan(&b, comparison_time);
+ return a.HasGreaterRankingThan(&b, comparison_time);
});
// Set to true if |existing_profile_copies| already contains an equivalent
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.h b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.h
index fa06e398077..ca5efa44afc 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.h
+++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.h
@@ -82,7 +82,8 @@ class AutofillProfileComparator {
// settings-visible value that is different.
static bool ProfilesHaveDifferentSettingsVisibleValues(
const AutofillProfile& p1,
- const AutofillProfile& p2);
+ const AutofillProfile& p2,
+ const std::string& app_locale);
// Returns true if |text| is empty or contains only skippable characters. A
// character is skippable if it is punctuation or white space.
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
index a5674f70ef8..21015691393 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
+++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
@@ -1088,9 +1088,10 @@ TEST_P(AutofillProfileComparatorTest, MergePhoneNumbers_NA) {
static const char16_t kPhoneF16[] = u"1-800-555-0199 #321";
static const char kPhoneG[] = "+1 (800) 555.0199;ext=321";
static const char16_t kPhoneG16[] = u"+1 (800) 555.0199;ext=321";
- static const char16_t kMergedShortNumber[] = u"5550199";
- static const char16_t kMergedShortNumberExt[] = u"5550199 ext. 321";
- static const char16_t kMergedFullNumber[] = u"+1 800-555-0199";
+ static const char16_t kMergedShortNumber[] = u"555-0199";
+ static const char16_t kMergedShortNumberExt[] = u"555-0199 ext. 321";
+ static const char16_t kMergedNationalNumber[] = u"(800) 555-0199";
+ static const char16_t kMergedNationalNumberExt[] = u"(800) 555-0199 ext. 321";
static const char16_t kMergedFullNumberExt[] = u"+1 800-555-0199 ext. 321";
AutofillProfile profile_a = CreateProfileWithPhoneNumber(kPhoneA);
@@ -1105,8 +1106,8 @@ TEST_P(AutofillProfileComparatorTest, MergePhoneNumbers_NA) {
MergePhoneNumbersAndExpect(profile_a, profile_a, kPhoneA16);
MergePhoneNumbersAndExpect(profile_a, profile_b, kMergedShortNumber);
MergePhoneNumbersAndExpect(profile_a, profile_c, kMergedShortNumberExt);
- MergePhoneNumbersAndExpect(profile_a, profile_d, kMergedFullNumber);
- MergePhoneNumbersAndExpect(profile_a, profile_e, kMergedFullNumberExt);
+ MergePhoneNumbersAndExpect(profile_a, profile_d, kMergedNationalNumber);
+ MergePhoneNumbersAndExpect(profile_a, profile_e, kMergedNationalNumberExt);
MergePhoneNumbersAndExpect(profile_a, profile_f, kMergedFullNumberExt);
MergePhoneNumbersAndExpect(profile_a, profile_g, kMergedFullNumberExt);
@@ -1114,8 +1115,8 @@ TEST_P(AutofillProfileComparatorTest, MergePhoneNumbers_NA) {
MergePhoneNumbersAndExpect(profile_b, profile_a, kMergedShortNumber);
MergePhoneNumbersAndExpect(profile_b, profile_b, kPhoneB16);
MergePhoneNumbersAndExpect(profile_b, profile_c, kMergedShortNumberExt);
- MergePhoneNumbersAndExpect(profile_b, profile_d, kMergedFullNumber);
- MergePhoneNumbersAndExpect(profile_b, profile_e, kMergedFullNumberExt);
+ MergePhoneNumbersAndExpect(profile_b, profile_d, kMergedNationalNumber);
+ MergePhoneNumbersAndExpect(profile_b, profile_e, kMergedNationalNumberExt);
MergePhoneNumbersAndExpect(profile_b, profile_f, kMergedFullNumberExt);
MergePhoneNumbersAndExpect(profile_b, profile_g, kMergedFullNumberExt);
@@ -1123,25 +1124,25 @@ TEST_P(AutofillProfileComparatorTest, MergePhoneNumbers_NA) {
MergePhoneNumbersAndExpect(profile_c, profile_a, kMergedShortNumberExt);
MergePhoneNumbersAndExpect(profile_c, profile_b, kMergedShortNumberExt);
MergePhoneNumbersAndExpect(profile_c, profile_c, kPhoneC16);
- MergePhoneNumbersAndExpect(profile_c, profile_d, kMergedFullNumberExt);
- MergePhoneNumbersAndExpect(profile_c, profile_e, kMergedFullNumberExt);
+ MergePhoneNumbersAndExpect(profile_c, profile_d, kMergedNationalNumberExt);
+ MergePhoneNumbersAndExpect(profile_c, profile_e, kMergedNationalNumberExt);
MergePhoneNumbersAndExpect(profile_c, profile_f, kMergedFullNumberExt);
MergePhoneNumbersAndExpect(profile_c, profile_g, kMergedFullNumberExt);
// Profile D
- MergePhoneNumbersAndExpect(profile_d, profile_a, kMergedFullNumber);
- MergePhoneNumbersAndExpect(profile_d, profile_b, kMergedFullNumber);
- MergePhoneNumbersAndExpect(profile_d, profile_c, kMergedFullNumberExt);
+ MergePhoneNumbersAndExpect(profile_d, profile_a, kMergedNationalNumber);
+ MergePhoneNumbersAndExpect(profile_d, profile_b, kMergedNationalNumber);
+ MergePhoneNumbersAndExpect(profile_d, profile_c, kMergedNationalNumberExt);
MergePhoneNumbersAndExpect(profile_d, profile_d, kPhoneD16);
- MergePhoneNumbersAndExpect(profile_d, profile_e, kMergedFullNumberExt);
+ MergePhoneNumbersAndExpect(profile_d, profile_e, kMergedNationalNumberExt);
MergePhoneNumbersAndExpect(profile_d, profile_f, kMergedFullNumberExt);
MergePhoneNumbersAndExpect(profile_d, profile_g, kMergedFullNumberExt);
// Profile E
- MergePhoneNumbersAndExpect(profile_e, profile_a, kMergedFullNumberExt);
- MergePhoneNumbersAndExpect(profile_e, profile_b, kMergedFullNumberExt);
- MergePhoneNumbersAndExpect(profile_e, profile_c, kMergedFullNumberExt);
- MergePhoneNumbersAndExpect(profile_e, profile_d, kMergedFullNumberExt);
+ MergePhoneNumbersAndExpect(profile_e, profile_a, kMergedNationalNumberExt);
+ MergePhoneNumbersAndExpect(profile_e, profile_b, kMergedNationalNumberExt);
+ MergePhoneNumbersAndExpect(profile_e, profile_c, kMergedNationalNumberExt);
+ MergePhoneNumbersAndExpect(profile_e, profile_d, kMergedNationalNumberExt);
MergePhoneNumbersAndExpect(profile_e, profile_e, kPhoneE16);
MergePhoneNumbersAndExpect(profile_e, profile_f, kMergedFullNumberExt);
MergePhoneNumbersAndExpect(profile_e, profile_g, kMergedFullNumberExt);
@@ -1423,7 +1424,7 @@ TEST_P(AutofillProfileComparatorTest,
// values.
EXPECT_FALSE(
AutofillProfileComparator::ProfilesHaveDifferentSettingsVisibleValues(
- existing_profile, existing_profile));
+ existing_profile, existing_profile, kLocale));
// Test for most settings visible types that a change is correctly recognized.
for (ServerFieldType changed_type :
@@ -1433,7 +1434,7 @@ TEST_P(AutofillProfileComparatorTest,
AutofillProfile new_profile = existing_profile;
EXPECT_FALSE(
AutofillProfileComparator::ProfilesHaveDifferentSettingsVisibleValues(
- existing_profile, new_profile));
+ existing_profile, new_profile, kLocale));
// Change one of the settings visible values and test that the function
// returns true.
@@ -1442,7 +1443,7 @@ TEST_P(AutofillProfileComparatorTest,
changed_type, existing_profile.GetRawInfo(changed_type) + u"_edited");
EXPECT_TRUE(
AutofillProfileComparator::ProfilesHaveDifferentSettingsVisibleValues(
- existing_profile, new_profile));
+ existing_profile, new_profile, kLocale));
}
// The rest of the test is only applicable for structured names.
@@ -1457,7 +1458,7 @@ TEST_P(AutofillProfileComparatorTest,
NAME_FIRST, base::ToUpperASCII(existing_profile.GetRawInfo(NAME_FIRST)));
EXPECT_FALSE(
AutofillProfileComparator::ProfilesHaveDifferentSettingsVisibleValues(
- existing_profile, new_profile));
+ existing_profile, new_profile, kLocale));
}
TEST_P(AutofillProfileComparatorTest, GetProfileDifference) {
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address.h b/chromium/components/autofill/core/browser/data_model/autofill_structured_address.h
index 0aed291b5cd..971d418b2de 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address.h
+++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address.h
@@ -10,8 +10,6 @@
#include "components/autofill/core/browser/data_model/autofill_structured_address_component.h"
-using autofill::structured_address::AddressComponent;
-
namespace autofill {
namespace structured_address {
diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.h b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.h
index 108c024cb7f..8a225d57f87 100644
--- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.h
+++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.h
@@ -14,8 +14,6 @@ namespace re2 {
class RE2;
} // namespace re2
-using autofill::structured_address::AddressComponent;
-
namespace autofill {
namespace structured_address {
@@ -53,7 +51,7 @@ class NameMiddle : public AddressComponent {
bool ConvertAndSetValueForAdditionalFieldTypeName(
const std::string& type_name,
const std::u16string& value,
- const VerificationStatus& status) override;
+ const structured_address::VerificationStatus& status) override;
};
// Atomic component that represents the first part of a last name.
diff --git a/chromium/components/autofill/core/browser/data_model/contact_info.cc b/chromium/components/autofill/core/browser/data_model/contact_info.cc
index a642c04bd2e..1e734b70666 100644
--- a/chromium/components/autofill/core/browser/data_model/contact_info.cc
+++ b/chromium/components/autofill/core/browser/data_model/contact_info.cc
@@ -25,7 +25,8 @@ namespace autofill {
namespace {
// Factory for the structured tree to be used in NameInfo.
-std::unique_ptr<AddressComponent> CreateStructuredNameTree() {
+std::unique_ptr<structured_address::AddressComponent>
+CreateStructuredNameTree() {
if (structured_address::HonorificPrefixEnabled()) {
return std::make_unique<structured_address::NameFullWithPrefix>();
}
diff --git a/chromium/components/autofill/core/browser/data_model/credit_card.cc b/chromium/components/autofill/core/browser/data_model/credit_card.cc
index ac94e6d7a00..b9e126b5f38 100644
--- a/chromium/components/autofill/core/browser/data_model/credit_card.cc
+++ b/chromium/components/autofill/core/browser/data_model/credit_card.cc
@@ -43,8 +43,6 @@ using base::ASCIIToUTF16;
namespace autofill {
-using structured_address::VerificationStatus;
-
// Unicode characters used in card number obfuscation:
// - \u2022 - Bullet.
// - \u2006 - SIX-PER-EM SPACE (small space between bullets).
@@ -172,7 +170,7 @@ CreditCard::CreditCard(const CreditCard& credit_card) : CreditCard() {
operator=(credit_card);
}
-CreditCard::~CreditCard() {}
+CreditCard::~CreditCard() = default;
// static
const std::u16string CreditCard::StripSeparators(const std::u16string& number) {
@@ -376,6 +374,11 @@ std::u16string CreditCard::GetMidlineEllipsisDots(size_t num_dots) {
return dots;
}
+// static
+bool CreditCard::IsLocalCard(const CreditCard* card) {
+ return card && card->record_type() == CreditCard::LOCAL_CARD;
+}
+
void CreditCard::SetNetworkForMaskedCard(base::StringPiece network) {
DCHECK_EQ(MASKED_SERVER_CARD, record_type());
network_ = std::string(network);
@@ -388,6 +391,18 @@ AutofillMetadata CreditCard::GetMetadata() const {
return metadata;
}
+double CreditCard::GetRankingScore(base::Time current_time) const {
+ int virtual_card_boost = 0;
+ if (virtual_card_enrollment_state_ == VirtualCardEnrollmentState::ENROLLED) {
+ virtual_card_boost =
+ features::kAutofillRankingFormulaVirtualCardBoost.Get() *
+ exp(-GetDaysSinceLastUse(current_time) /
+ features::kAutofillRankingFormulaVirtualCardBoostHalfLife.Get());
+ }
+
+ return AutofillDataModel::GetRankingScore(current_time) + virtual_card_boost;
+}
+
bool CreditCard::SetMetadata(const AutofillMetadata metadata) {
// Make sure the ids matches.
if (metadata.id != (record_type_ == LOCAL_CARD ? guid() : server_id_))
@@ -458,9 +473,10 @@ std::u16string CreditCard::GetRawInfo(ServerFieldType type) const {
}
}
-void CreditCard::SetRawInfoWithVerificationStatus(ServerFieldType type,
- const std::u16string& value,
- VerificationStatus status) {
+void CreditCard::SetRawInfoWithVerificationStatus(
+ ServerFieldType type,
+ const std::u16string& value,
+ structured_address::VerificationStatus status) {
DCHECK_EQ(FieldTypeGroup::kCreditCard, AutofillType(type).group());
switch (type) {
case CREDIT_CARD_NAME_FULL:
@@ -1063,7 +1079,7 @@ bool CreditCard::SetInfoWithVerificationStatusImpl(
const AutofillType& type,
const std::u16string& value,
const std::string& app_locale,
- VerificationStatus status) {
+ structured_address::VerificationStatus status) {
ServerFieldType storable_type = type.GetStorableType();
if (storable_type == CREDIT_CARD_EXP_MONTH)
return SetExpirationMonthFromString(value, app_locale);
diff --git a/chromium/components/autofill/core/browser/data_model/credit_card.h b/chromium/components/autofill/core/browser/data_model/credit_card.h
index 8af68f0927e..44a74f8ae28 100644
--- a/chromium/components/autofill/core/browser/data_model/credit_card.h
+++ b/chromium/components/autofill/core/browser/data_model/credit_card.h
@@ -11,6 +11,7 @@
#include "base/gtest_prod_util.h"
#include "base/strings/string_piece_forward.h"
+#include "base/time/time.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/data_model/autofill_data_model.h"
#include "url/gurl.h"
@@ -123,12 +124,16 @@ class CreditCard : public AutofillDataModel {
// Returns string of dots for hidden card information.
static std::u16string GetMidlineEllipsisDots(size_t num_dots);
+ // Returns whether the card is a local card.
+ static bool IsLocalCard(const CreditCard* card);
+
// Network issuer strings are defined at the bottom of this file, e.g.
// kVisaCard.
void SetNetworkForMaskedCard(base::StringPiece network);
// AutofillDataModel:
AutofillMetadata GetMetadata() const override;
+ double GetRankingScore(base::Time current_time) const override;
bool SetMetadata(const AutofillMetadata metadata) override;
// Returns whether the card is deletable: if it is expired and has not been
// used for longer than |kDisusedCreditCardDeletionTimeDelta|.
diff --git a/chromium/components/autofill/core/browser/data_model/credit_card_test_api.h b/chromium/components/autofill/core/browser/data_model/credit_card_test_api.h
index 52a35c75fc0..ce87b6a7bda 100644
--- a/chromium/components/autofill/core/browser/data_model/credit_card_test_api.h
+++ b/chromium/components/autofill/core/browser/data_model/credit_card_test_api.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_CREDIT_CARD_TEST_API_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_CREDIT_CARD_TEST_API_H_
+#include "base/memory/raw_ptr.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
namespace autofill {
@@ -21,7 +22,7 @@ class CreditCardTestApi {
}
private:
- CreditCard* creditcard_;
+ raw_ptr<CreditCard> creditcard_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc
index 40ad9c2f6bb..7b8c201f425 100644
--- a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc
+++ b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc
@@ -18,6 +18,7 @@
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/data_model/autofill_metadata.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/data_model/test_autofill_data_model.h"
#include "components/autofill/core/browser/test_autofill_clock.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_clock.h"
@@ -1319,6 +1320,9 @@ const CreditCardMatchingTypesCase kCreditCardMatchingTypesTestCases[] = {
{"19", "01", "2019", LOCAL_CARD, {CREDIT_CARD_EXP_2_DIGIT_YEAR}},
{"01/2019", "01", "2019", LOCAL_CARD, {CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}},
{"01-2019", "01", "2019", LOCAL_CARD, {CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}},
+ {"01/19", "01", "2019", LOCAL_CARD, {CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}},
+ {"01-19", "01", "2019", LOCAL_CARD, {CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}},
+ {"01 / 19", "01", "2019", LOCAL_CARD, {CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}},
{"01/2020", "01", "2019", LOCAL_CARD, ServerFieldTypeSet()},
{"20", "01", "2019", LOCAL_CARD, ServerFieldTypeSet()},
{"2021", "01", "2019", LOCAL_CARD, ServerFieldTypeSet()},
@@ -1757,4 +1761,77 @@ TEST_F(CreditCardTestForKeyboardAccessory, GetObfuscatedStringForCardDigits) {
}
#endif // BUILDFLAG(IS_ANDROID)
+enum Expectation { GREATER, LESS };
+
+struct VirtualCardRankingTestCase {
+ const std::string guid_a;
+ const int use_count_a;
+ const base::Time use_date_a;
+ const std::string guid_b;
+ const int use_count_b;
+ const base::Time use_date_b;
+ Expectation expectation;
+};
+
+base::Time current_time = AutofillClock::Now();
+
+class VirtualCardRankingTest
+ : public testing::TestWithParam<VirtualCardRankingTestCase> {};
+
+TEST_P(VirtualCardRankingTest, HasGreaterRankingThan) {
+ // Enable kAutofillEnableRankingFormula so that it uses new formula instead of
+ // frecency.
+ base::test::ScopedFeatureList feature_list_;
+ feature_list_.InitAndEnableFeature(features::kAutofillEnableRankingFormula);
+
+ auto test_case = GetParam();
+
+ CreditCard model_a = test::GetVirtualCard();
+ model_a.set_guid(test_case.guid_a);
+ model_a.set_use_count(test_case.use_count_a);
+ model_a.set_use_date(test_case.use_date_a);
+
+ TestAutofillDataModel model_b(test_case.guid_b, test_case.use_count_b,
+ test_case.use_date_b);
+
+ EXPECT_EQ(test_case.expectation == GREATER,
+ model_a.HasGreaterRankingThan(&model_b, current_time));
+ EXPECT_NE(test_case.expectation == GREATER,
+ model_b.HasGreaterRankingThan(&model_a, current_time));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ CreditCardTest,
+ VirtualCardRankingTest,
+ testing::Values(
+ // Same days since last use and use count, but model A is a virtual card
+ // and ranked higher.
+ VirtualCardRankingTestCase{"guid_a", 10, current_time, "guid_b", 10,
+ current_time, GREATER},
+ // Same days since last use and although model A has a smaller use
+ // count, it is a virtual card and ranked higher.
+ VirtualCardRankingTestCase{"guid_a", 1, current_time, "guid_b", 10,
+ current_time, GREATER},
+ // Model A has a larger use count but smaller days since last use. model
+ // A is ranked higher due to virtual card boost.
+ VirtualCardRankingTestCase{"guid_a", 10, current_time - base::Days(10),
+ "guid_b", 5, current_time, GREATER},
+ // Model A has a larger use count but also a much larger days since last
+ // use. Due to this, model B is ranked higher despite the virtual card
+ // boost and greater use count of model A.
+ VirtualCardRankingTestCase{"guid_a", 10, current_time - base::Days(40),
+ "guid_b", 3, current_time, LESS},
+ // Model A only has a use count of 1 but due to its virtual card bost
+ // and much smaller days since last use it is ranked higher than model
+ // B.
+ VirtualCardRankingTestCase{"guid_a", 1, current_time - base::Days(30),
+ "guid_b", 300, current_time - base::Days(90),
+ GREATER},
+ // Model B only has a use count of 1 but due to its much smaller day
+ // since last use it is ranked higher than model A despite its virtual
+ // card boost and much higher use count.
+ VirtualCardRankingTestCase{"guid_a", 300, current_time - base::Days(90),
+ "guid_b", 1, current_time - base::Days(30),
+ LESS}));
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/data_model/data_model_utils.cc b/chromium/components/autofill/core/browser/data_model/data_model_utils.cc
index c472f168dfd..12d2e4069e8 100644
--- a/chromium/components/autofill/core/browser/data_model/data_model_utils.cc
+++ b/chromium/components/autofill/core/browser/data_model/data_model_utils.cc
@@ -142,11 +142,11 @@ bool SetExpirationYear(int value, int* expiration_year) {
}
std::u16string FindPossiblePhoneCountryCode(const std::u16string& text) {
- std::u16string candidate;
if (text.find(u"00") != std::u16string::npos ||
text.find('+') != std::u16string::npos) {
- if (MatchesPattern(text, kAugmentedPhoneCountryCodeRe, &candidate, 1))
- return candidate;
+ std::vector<std::u16string> captures;
+ if (MatchesPattern(text, kAugmentedPhoneCountryCodeRe, &captures))
+ return captures[1];
}
return std::u16string();
diff --git a/chromium/components/autofill/core/browser/data_model/phone_number.cc b/chromium/components/autofill/core/browser/data_model/phone_number.cc
index 451200f7fbd..ab2cb1424c2 100644
--- a/chromium/components/autofill/core/browser/data_model/phone_number.cc
+++ b/chromium/components/autofill/core/browser/data_model/phone_number.cc
@@ -217,6 +217,28 @@ std::u16string PhoneNumber::GetInfoImpl(const AutofillType& type,
case PHONE_HOME_NUMBER:
return cached_parsed_phone_.number();
+ case PHONE_HOME_NUMBER_PREFIX: {
+ const std::u16string number = GetInfo(PHONE_HOME_NUMBER, app_locale);
+ const std::u16string number_suffix =
+ GetInfo(PHONE_HOME_NUMBER_SUFFIX, app_locale);
+ DCHECK(number.size() >= number_suffix.size());
+ // As PHONE_HOME_NUMBER = PHONE_HOME_NUMBER_PREFIX +
+ // PHONE_HOME_NUMBER_SUFFIX, extract the appropriate prefix from `number`.
+ return number.substr(0, number.size() - number_suffix.size());
+ }
+
+ case PHONE_HOME_NUMBER_SUFFIX: {
+ const std::u16string number = GetInfo(PHONE_HOME_NUMBER, app_locale);
+ // Libphonenumber doesn't provide functionality to split PHONE_HOME_NUMBER
+ // further, and the HTML standard doesn't specify which suffix
+ // autocomplete="tel-local-suffix" corresponds to. In all countries using
+ // this format that we are aware of (see unit tests), the suffix consists
+ // of the last 4 digits, while the length of the prefix varies.
+ constexpr int kSuffixLength = 4;
+ DCHECK(number.size() >= kSuffixLength);
+ return number.substr(number.size() - kSuffixLength);
+ }
+
case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
return GetTrunkPrefix() + cached_parsed_phone_.city_code();
@@ -270,24 +292,36 @@ bool PhoneNumber::SetInfoWithVerificationStatusImpl(
if (number_.empty())
return true;
- // Store a formatted (i.e., pretty printed) version of the number if either
- // the number doesn't contain formatting marks.
+ // `SetRawInfoWithVerificationStatus()` invalidated `cached_parsed_phone_` and
+ // calling `UpdateCacheIfNeeded()` will thus try parsing the `number_` here.
UpdateCacheIfNeeded(app_locale);
+ // If the number invalid, setting fails and `GetRawInfo()` and `GetInfo()`
+ // should return an empty string. Clear both representations of the number.
+ if (!cached_parsed_phone_.IsValidNumber()) {
+ number_.clear();
+ cached_parsed_phone_ = i18n::PhoneObject();
+ return false;
+ }
+ // Store a formatted (i.e., pretty printed) version of the number if it
+ // doesn't contain formatting marks.
if (base::ContainsOnlyChars(number_, u"+0123456789")) {
number_ = cached_parsed_phone_.GetFormattedNumber();
- } else if (i18n::NormalizePhoneNumber(number_,
- GetRegion(*profile_, app_locale))
- .empty()) {
- // The number doesn't make sense for this region; clear it.
- number_.clear();
}
- return !number_.empty();
+ return true;
}
void PhoneNumber::UpdateCacheIfNeeded(const std::string& app_locale) const {
std::string region = GetRegion(*profile_, app_locale);
- if (!number_.empty() && cached_parsed_phone_.region() != region)
- cached_parsed_phone_ = i18n::PhoneObject(number_, region);
+ if (!number_.empty() && cached_parsed_phone_.region() != region) {
+ // To enable filling of country calling codes for nationally formatted
+ // numbers, infer it from the `profile_`'s country information while parsing
+ // the number.
+ cached_parsed_phone_ = i18n::PhoneObject(
+ number_, region,
+ /*infer_country_code=*/profile_->HasInfo(ADDRESS_HOME_COUNTRY) &&
+ base::FeatureList::IsEnabled(
+ features::kAutofillInferCountryCallingCode));
+ }
}
PhoneNumber::PhoneCombineHelper::PhoneCombineHelper() {}
diff --git a/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc b/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc
index b7dd49c5014..52bd3f6aa0b 100644
--- a/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc
+++ b/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc
@@ -115,51 +115,99 @@ TEST(PhoneNumberTest, Matcher_TrunkTypes_DE) {
{u"01741234567", {PHONE_HOME_CITY_AND_NUMBER}}});
}
-// Verify that PhoneNumber::SetInfo() correctly formats the incoming number.
+// Verify that `PhoneNumber::SetInfo()` correctly formats the incoming number.
TEST(PhoneNumberTest, SetInfo) {
AutofillProfile profile;
profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US");
+ const char kLocale[] = "US"; // Irrelevant, as `profile` has a country.
PhoneNumber phone(&profile);
- EXPECT_EQ(std::u16string(), phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_TRUE(phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER).empty());
+ EXPECT_TRUE(phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale).empty());
// Set the formatted info directly.
- EXPECT_TRUE(phone.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER),
- u"(650) 234-5678", "US"));
+ EXPECT_TRUE(
+ phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"(650) 234-5678", kLocale));
EXPECT_EQ(u"(650) 234-5678", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_EQ(u"6502345678", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
// Unformatted numbers should be formatted.
- EXPECT_TRUE(phone.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER),
- u"8887776666", "US"));
+ EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"8887776666", kLocale));
EXPECT_EQ(u"(888) 777-6666", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
- EXPECT_TRUE(phone.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER),
- u"+18887776666", "US"));
+ EXPECT_EQ(u"8887776666", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+
+ EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"+18887776666", kLocale));
EXPECT_EQ(u"1 888-777-6666", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_EQ(u"18887776666", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
// Differently formatted numbers should be left alone.
- EXPECT_TRUE(phone.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER),
- u"800-432-8765", "US"));
+ EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"800-432-8765", kLocale));
EXPECT_EQ(u"800-432-8765", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_EQ(u"8004328765", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
// SetRawInfo should not try to format.
phone.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"8004328765");
EXPECT_EQ(u"8004328765", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
- // Invalid numbers should not be stored. In the US, phone numbers cannot
- // start with the digit '1'.
- EXPECT_FALSE(
- phone.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), u"650111111", "US"));
- EXPECT_EQ(std::u16string(), phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+ // Invalid numbers should not be stored. In the US, phone numbers cannot start
+ // with the digit '1'.
+ EXPECT_FALSE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"650111111", kLocale));
+ EXPECT_TRUE(phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER).empty());
+ EXPECT_TRUE(phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale).empty());
- // If the stored number is invalid due to metadata mismatch(non-existing
+ // If the stored number is invalid due to metadata mismatch (non-existing
// carrier code for example), but otherwise is a possible number and can be
// parsed into different components, we should respond to queries with best
// effort as if it is a valid number.
- EXPECT_TRUE(phone.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER),
- u"5141231234", "US"));
- EXPECT_EQ(u"5141231234", phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, "CA"));
- EXPECT_EQ(u"5141231234", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, "CA"));
- EXPECT_EQ(u"514", phone.GetInfo(PHONE_HOME_CITY_CODE, "CA"));
+ EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"5141231234", kLocale));
+ EXPECT_EQ(u"5141231234", phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, kLocale));
+ EXPECT_EQ(u"5141231234", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+ EXPECT_EQ(u"514", phone.GetInfo(PHONE_HOME_CITY_CODE, kLocale));
+}
+
+TEST(PhoneNumberTest, InferCountryCallingCode) {
+ base::test::ScopedFeatureList complement_calling_code_enabled;
+ complement_calling_code_enabled.InitAndEnableFeature(
+ features::kAutofillInferCountryCallingCode);
+
+ AutofillProfile profile;
+ PhoneNumber phone(&profile);
+ const char kLocale[] = "US";
+
+ // No country information available and thus no calling code inferred.
+ EXPECT_TRUE(
+ phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"(650) 234-5678", kLocale));
+ EXPECT_TRUE(phone.GetInfo(PHONE_HOME_COUNTRY_CODE, kLocale).empty());
+ EXPECT_EQ(u"6502345678", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+ EXPECT_EQ(u"(650) 234-5678", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_EQ(u"6502345678", phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, kLocale));
+
+ // With country information available, the calling code is inferred.
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US");
+ EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"6502345678", kLocale));
+ EXPECT_EQ(u"1", phone.GetInfo(PHONE_HOME_COUNTRY_CODE, kLocale));
+ EXPECT_EQ(u"16502345678", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+ EXPECT_EQ(u"1 650-234-5678", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_EQ(u"6502345678", phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, kLocale));
+
+ // Pre-formatted number.
+ // In this case the raw info is kept as-is, while the calling code is inferred
+ // for the filling information.
+ EXPECT_TRUE(
+ phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"(650) 234-5678", kLocale));
+ EXPECT_EQ(u"1", phone.GetInfo(PHONE_HOME_COUNTRY_CODE, kLocale));
+ EXPECT_EQ(u"16502345678", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+ EXPECT_EQ(u"(650) 234-5678", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_EQ(u"6502345678", phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, kLocale));
+
+ // Different country.
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"DE");
+ EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"015787912345", kLocale));
+ EXPECT_EQ(u"49", phone.GetInfo(PHONE_HOME_COUNTRY_CODE, kLocale));
+ EXPECT_EQ(u"+4915787912345", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, kLocale));
+ EXPECT_EQ(u"+49 1578 7912345", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_EQ(u"015787912345",
+ phone.GetInfo(PHONE_HOME_CITY_AND_NUMBER, kLocale));
}
// Test that cached phone numbers are correctly invalidated and updated.
@@ -276,7 +324,7 @@ TEST(PhoneNumberTest, TrunkPrefix) {
const std::u16string& city_code_without_trunk,
const std::u16string& city_number_with_trunk,
const std::u16string& city_number_without_trunk) {
- // Irrelevant, as the `profile` has country information.
+ // The `locale` is irrelevant, as the `profile` has country information.
const std::string locale = "en-US";
PhoneNumber phone_number(&profile);
phone_number.SetInfo(PHONE_HOME_WHOLE_NUMBER, number, locale);
@@ -324,6 +372,38 @@ TEST(PhoneNumberTest, TrunkPrefix) {
}
}
+// Tests that PHONE_HOME_NUMBER_PREFIX and PHONE_HOME_NUMBER_PREFIX are
+// extracted correctly.
+TEST(PhoneNumberTest, NumberPreAndSuffixes) {
+ AutofillProfile profile;
+
+ // Constructs a `PhoneNumber` object from `number` and verifies that the
+ // pre- and suffix match the expectation.
+ auto TestNumber = [&](const std::u16string& number,
+ const std::u16string& prefix,
+ const std::u16string& suffix) {
+ // The `locale` is irrelevant, as the `profile` has country information.
+ const std::string locale = "en-US";
+ PhoneNumber phone_number(&profile);
+ phone_number.SetInfo(PHONE_HOME_WHOLE_NUMBER, number, locale);
+ EXPECT_EQ(prefix, phone_number.GetInfo(PHONE_HOME_NUMBER_PREFIX, locale));
+ EXPECT_EQ(suffix, phone_number.GetInfo(PHONE_HOME_NUMBER_SUFFIX, locale));
+ };
+
+ // US
+ {
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US");
+ TestNumber(u"(650) 234-5678", u"234", u"5678");
+ }
+ // JP
+ {
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"JP");
+ TestNumber(u"03-3224-9999", u"3224", u"9999"); // Landline
+ TestNumber(u"090-1234-5678", u"1234", u"5678"); // Mobile
+ TestNumber(u"+81 824-86-3123", u"86", u"3123"); // Different length prefix
+ }
+}
+
// Tests whether the |PHONE_HOME_COUNTRY_CODE| is added to the set of matching
// types.
TEST(PhoneNumberTest, CountryCodeInMatchingTypes) {
diff --git a/chromium/components/autofill/core/browser/data_model/test_autofill_data_model.cc b/chromium/components/autofill/core/browser/data_model/test_autofill_data_model.cc
new file mode 100644
index 00000000000..cbd7782b074
--- /dev/null
+++ b/chromium/components/autofill/core/browser/data_model/test_autofill_data_model.cc
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/data_model/test_autofill_data_model.h"
+
+namespace autofill {
+
+TestAutofillDataModel::TestAutofillDataModel(const std::string& guid,
+ const std::string& origin)
+ : AutofillDataModel(guid, origin) {}
+
+TestAutofillDataModel::TestAutofillDataModel(const std::string& guid,
+ size_t use_count,
+ base::Time use_date)
+ : AutofillDataModel(guid, std::string()) {
+ set_use_count(use_count);
+ set_use_date(use_date);
+}
+
+TestAutofillDataModel::~TestAutofillDataModel() = default;
+
+std::u16string TestAutofillDataModel::GetRawInfo(ServerFieldType type) const {
+ return std::u16string();
+}
+
+void TestAutofillDataModel::SetRawInfoWithVerificationStatus(
+ ServerFieldType type,
+ const std::u16string& value,
+ structured_address::VerificationStatus status) {}
+
+void TestAutofillDataModel::GetSupportedTypes(
+ ServerFieldTypeSet* supported_types) const {}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/data_model/test_autofill_data_model.h b/chromium/components/autofill/core/browser/data_model/test_autofill_data_model.h
new file mode 100644
index 00000000000..b175e04d98d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/data_model/test_autofill_data_model.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_TEST_AUTOFILL_DATA_MODEL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_TEST_AUTOFILL_DATA_MODEL_H_
+
+#include "components/autofill/core/browser/data_model/autofill_data_model.h"
+#include "components/autofill/core/browser/data_model/autofill_structured_address_component.h"
+#include "components/autofill/core/browser/field_types.h"
+
+namespace autofill {
+
+class TestAutofillDataModel : public AutofillDataModel {
+ public:
+ TestAutofillDataModel(const std::string& guid, const std::string& origin);
+ TestAutofillDataModel(const std::string& guid,
+ size_t use_count,
+ base::Time use_date);
+
+ TestAutofillDataModel(const TestAutofillDataModel&) = delete;
+ TestAutofillDataModel& operator=(const TestAutofillDataModel&) = delete;
+
+ ~TestAutofillDataModel() override;
+
+ private:
+ std::u16string GetRawInfo(ServerFieldType type) const override;
+ void SetRawInfoWithVerificationStatus(
+ ServerFieldType type,
+ const std::u16string& value,
+ structured_address::VerificationStatus status) override;
+ void GetSupportedTypes(ServerFieldTypeSet* supported_types) const override;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_TEST_AUTOFILL_DATA_MODEL_H_
diff --git a/chromium/components/autofill/core/browser/field_filler.cc b/chromium/components/autofill/core/browser/field_filler.cc
index a2931e766f8..f7309c0055e 100644
--- a/chromium/components/autofill/core/browser/field_filler.cc
+++ b/chromium/components/autofill/core/browser/field_filler.cc
@@ -10,6 +10,7 @@
#include "base/feature_list.h"
#include "base/i18n/case_conversion.h"
#include "base/i18n/string_search.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -17,6 +18,7 @@
#include "components/autofill/core/browser/address_normalizer.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_regexes.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/data_model/autofill_data_model.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
@@ -735,9 +737,10 @@ std::u16string GetExpirationYearForVirtualCardPreviewInput(
}
// Returns the appropriate expiration date from |credit_card| for the field
-// based on the |field_type|. Uses the |field|'s max_length attribute to
-// determine if the |value| needs to be truncated or padded. Returns an empty
-// string in case of a failure.
+// based on the |field_type|. If the field contains a recognized date format
+// string, the function follows that format. Otherwise, it uses the |field|'s
+// max_length attribute to determine if the |value| needs to be truncated or
+// padded. Returns an empty string in case of a failure.
std::u16string GetExpirationDateForInput(const CreditCard& credit_card,
const AutofillField& field,
ServerFieldType field_type,
@@ -747,6 +750,38 @@ std::u16string GetExpirationDateForInput(const CreditCard& credit_card,
std::u16string two_digit_year = credit_card.Expiration2DigitYearAsString();
std::u16string four_digit_year = credit_card.Expiration4DigitYearAsString();
+ // Check whether we find one of the standard format descriptors like
+ // "mm/yy", "mm/yyyy", "mm / yy", "mm-yyyy", ... in one of the human
+ // readable labels. In that case, follow the specified pattern.
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillFillCreditCardAsPerFormatString)) {
+ std::vector<std::u16string> groups;
+ const char16_t* kFormatRegEx = u"mm(\\s?[/-]?\\s?)?yy(yy)?";
+ // ^^^^ optional white space
+ // ^^^^^ optional separator
+ // ^^^ optional white space
+ // ^^^^^ 4 digit year?
+ if (MatchesPattern(field.placeholder, kFormatRegEx, &groups) ||
+ MatchesPattern(field.label, kFormatRegEx, &groups)) {
+ bool is_two_digit_year = groups[2].empty();
+ std::u16string expiration_candidate =
+ base::StrCat({month, groups[1],
+ is_two_digit_year ? two_digit_year : four_digit_year});
+ if (field.max_length == 0 ||
+ expiration_candidate.size() <= field.max_length) {
+ return expiration_candidate;
+ }
+ // Try once more with a stripped version of the separator if the previous
+ // version did not fit.
+ expiration_candidate =
+ base::StrCat({month, base::TrimWhitespace(groups[1], base::TRIM_ALL),
+ is_two_digit_year ? two_digit_year : four_digit_year});
+ if (field.max_length == 0 ||
+ expiration_candidate.size() <= field.max_length) {
+ return expiration_candidate;
+ }
+ }
+ }
switch (field.max_length) {
case 1:
@@ -1003,14 +1038,20 @@ bool FieldFiller::FillFormField(
const AutofillField& field,
absl::variant<const AutofillProfile*, const CreditCard*>
profile_or_credit_card,
+ const std::map<FieldGlobalId, std::u16string>& forced_fill_values,
FormFieldData* field_data,
const std::u16string& cvc,
mojom::RendererFormDataAction action,
std::string* failure_to_fill) {
const AutofillType type = field.Type();
- std::u16string value = GetValueForFilling(
- field, profile_or_credit_card, field_data, cvc, action, failure_to_fill);
+ auto it = forced_fill_values.find(field.global_id());
+ bool value_is_an_override = it != forced_fill_values.end();
+ std::u16string value =
+ value_is_an_override
+ ? it->second
+ : GetValueForFilling(field, profile_or_credit_card, field_data, cvc,
+ action, failure_to_fill);
// Do not attempt to fill empty values as it would skew the metrics.
if (value.empty()) {
@@ -1023,6 +1064,8 @@ bool FieldFiller::FillFormField(
field_data, address_normalizer_, failure_to_fill);
}
field_data->value = value;
+ if (value_is_an_override)
+ field_data->force_override = true;
return true;
}
diff --git a/chromium/components/autofill/core/browser/field_filler.h b/chromium/components/autofill/core/browser/field_filler.h
index 7871af3c15e..504f9451efa 100644
--- a/chromium/components/autofill/core/browser/field_filler.h
+++ b/chromium/components/autofill/core/browser/field_filler.h
@@ -40,20 +40,24 @@ class FieldFiller {
// Set |field_data|'s value to the right value in |profile_or_credit_card|.
// Uses |field| to determine which field type should be filled, and
// |app_locale_| as hint when filling exceptional cases like phone number
- // values. If |action| indicates that the value will be used for the
+ // values. If |forced_fill_values| contains a string for the field to be
+ // filled, this value will be used unconditionally.
+ // If |action| indicates that the value will be used for the
// autofill preview (aka. suggestion) state, the data to be filled may be
// obfuscated.
//
// Returns |true| if the field has been filled, false otherwise. This is
// independent of whether the field was filled or autofilled before. If
// |failure_to_fill| is not null, errors are reported to that string.
- bool FillFormField(const AutofillField& field,
- absl::variant<const AutofillProfile*, const CreditCard*>
- profile_or_credit_card,
- FormFieldData* field_data,
- const std::u16string& cvc,
- mojom::RendererFormDataAction action,
- std::string* failure_to_fill = nullptr);
+ bool FillFormField(
+ const AutofillField& field,
+ absl::variant<const AutofillProfile*, const CreditCard*>
+ profile_or_credit_card,
+ const std::map<FieldGlobalId, std::u16string>& forced_fill_values,
+ FormFieldData* field_data,
+ const std::u16string& cvc,
+ mojom::RendererFormDataAction action,
+ std::string* failure_to_fill = nullptr);
// Returns the phone number value for the given |field|. The returned value
// might be |number|, or |phone_home_city_and_number|, or could possibly be a
diff --git a/chromium/components/autofill/core/browser/field_filler_unittest.cc b/chromium/components/autofill/core/browser/field_filler_unittest.cc
index 39d83690471..195b28ad333 100644
--- a/chromium/components/autofill/core/browser/field_filler_unittest.cc
+++ b/chromium/components/autofill/core/browser/field_filler_unittest.cc
@@ -93,7 +93,7 @@ void TestFillingExpirationMonth(const std::vector<const char*>& values,
AutofillField field;
test::CreateTestSelectField("", "", "", values, contents, select_size,
&field);
- field.set_heuristic_type(CREDIT_CARD_EXP_MONTH);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_EXP_MONTH);
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
@@ -101,14 +101,16 @@ void TestFillingExpirationMonth(const std::vector<const char*>& values,
// Try a single-digit month.
CreditCard card = test::GetCreditCard();
card.SetExpirationMonth(3);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
content_index = GetIndexOfValue(field.options, field.value);
EXPECT_EQ(u"Mar", field.options[content_index].content);
// Try a two-digit month.
card.SetExpirationMonth(11);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
content_index = GetIndexOfValue(field.options, field.value);
EXPECT_EQ(u"Nov", field.options[content_index].content);
@@ -160,7 +162,7 @@ TEST_F(AutofillFieldFillerTest, Type) {
EXPECT_EQ(UNKNOWN_TYPE, field.Type().GetStorableType());
// Set the heuristic type and check it.
- field.set_heuristic_type(NAME_FIRST);
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
EXPECT_EQ(NAME_FIRST, field.Type().GetStorableType());
EXPECT_EQ(FieldTypeGroup::kName, field.Type().group());
@@ -193,7 +195,7 @@ TEST_F(AutofillFieldFillerTest, Type) {
EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group());
// Set the heuristic type and check it and reset overall Type.
- field.set_heuristic_type(NAME_FIRST);
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
EXPECT_EQ(NAME_FIRST, field.Type().GetStorableType());
EXPECT_EQ(FieldTypeGroup::kName, field.Type().group());
}
@@ -206,18 +208,18 @@ TEST_F(AutofillFieldFillerTest, Type_CreditCardOverrideHtml_Heuristics) {
field.SetHtmlType(HTML_TYPE_UNRECOGNIZED, HTML_MODE_NONE);
// A credit card heuristic prediction overrides the unrecognized type.
- field.set_heuristic_type(CREDIT_CARD_NUMBER);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER);
EXPECT_EQ(CREDIT_CARD_NUMBER, field.Type().GetStorableType());
// A non credit card heuristic prediction doesn't override the unrecognized
// type.
- field.set_heuristic_type(NAME_FIRST);
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
EXPECT_EQ(UNKNOWN_TYPE, field.Type().GetStorableType());
// A credit card heuristic prediction doesn't override a known specified html
// type.
field.SetHtmlType(HTML_TYPE_NAME, HTML_MODE_NONE);
- field.set_heuristic_type(CREDIT_CARD_NUMBER);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER);
EXPECT_EQ(NAME_FULL, field.Type().GetStorableType());
}
@@ -320,7 +322,7 @@ TEST_F(AutofillFieldFillerTest, FieldSignatureAsStr) {
EXPECT_EQ("502192749", field.FieldSignatureAsStr());
// Heuristic type does not affect FieldSignature.
- field.set_heuristic_type(NAME_FIRST);
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
EXPECT_EQ("502192749", field.FieldSignatureAsStr());
// Server type does not affect FieldSignature.
@@ -339,17 +341,17 @@ TEST_F(AutofillFieldFillerTest, IsFieldFillable) {
EXPECT_FALSE(field.IsFieldFillable());
// Only heuristic type is set.
- field.set_heuristic_type(NAME_FIRST);
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
EXPECT_TRUE(field.IsFieldFillable());
// Only server type is set.
- field.set_heuristic_type(UNKNOWN_TYPE);
+ field.set_heuristic_type(GetActivePatternSource(), UNKNOWN_TYPE);
prediction.set_type(NAME_LAST);
field.set_server_predictions({prediction});
EXPECT_TRUE(field.IsFieldFillable());
// Both types set.
- field.set_heuristic_type(NAME_FIRST);
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
prediction.set_type(NAME_LAST);
field.set_server_predictions({prediction});
EXPECT_TRUE(field.IsFieldFillable());
@@ -367,29 +369,49 @@ TEST_F(AutofillFieldFillerTest,
FillFormField_AutocompleteOffNotRespected_AddressField) {
AutofillField field;
field.should_autocomplete = false;
- field.set_heuristic_type(NAME_FIRST);
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
// Non credit card related field.
address()->SetRawInfo(NAME_FIRST, u"Test");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, address(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, address(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
// Verify that the field is filled in all circumstances.
EXPECT_EQ(u"Test", field.value);
}
+// Verify that a forced fill_value takes precedence.
+TEST_F(AutofillFieldFillerTest, FillFormField_ForcedFillValues) {
+ AutofillField field;
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
+
+ // Non credit card related field.
+ address()->SetRawInfo(NAME_FIRST, u"Test");
+ std::map<FieldGlobalId, std::u16string> forced_fill_values;
+ forced_fill_values[field.global_id()] = u"Test2";
+ FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
+ filler.FillFormField(field, address(), forced_fill_values, &field,
+ /*cvc=*/std::u16string(),
+ mojom::RendererFormDataAction::kFill);
+
+ // Verify that the field is filled with the overridden value.
+ EXPECT_EQ(u"Test2", field.value);
+}
+
// Verify that credit card related fields with the autocomplete attribute
// set to off get filled.
TEST_F(AutofillFieldFillerTest, FillFormField_AutocompleteOff_CreditCardField) {
AutofillField field;
field.should_autocomplete = false;
- field.set_heuristic_type(CREDIT_CARD_NUMBER);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER);
// Credit card related field.
credit_card()->SetNumber(u"4111111111111111");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, credit_card(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, credit_card(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
// Verify that the field is filled.
@@ -403,12 +425,13 @@ TEST_F(AutofillFieldFillerTest,
AutofillField field;
field.max_length = 30;
field.set_credit_card_number_offset(2);
- field.set_heuristic_type(CREDIT_CARD_NUMBER);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER);
// Credit card related field.
credit_card()->SetNumber(u"0123456789999999");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, credit_card(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, credit_card(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
// Verify that the field is filled with the fourth digit of the credit card
@@ -423,12 +446,13 @@ TEST_F(AutofillFieldFillerTest,
AutofillField field;
field.max_length = 18;
field.set_credit_card_number_offset(30);
- field.set_heuristic_type(CREDIT_CARD_NUMBER);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER);
// Credit card related field.
credit_card()->SetNumber(u"0123456789999999");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, credit_card(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, credit_card(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
// Verify that the field is filled with the full credit card number.
@@ -442,12 +466,13 @@ TEST_F(AutofillFieldFillerTest,
AutofillField field;
field.max_length = 1;
field.set_credit_card_number_offset(3);
- field.set_heuristic_type(CREDIT_CARD_NUMBER);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER);
// Credit card related field.
credit_card()->SetNumber(u"0123456789999999");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, credit_card(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, credit_card(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
// Verify that the field is filled with the third digit of the credit card
@@ -459,12 +484,13 @@ TEST_F(AutofillFieldFillerTest,
TEST_F(AutofillFieldFillerTest, FillFormField_MaxLength_CreditCardField) {
AutofillField field;
field.max_length = 1;
- field.set_heuristic_type(CREDIT_CARD_NUMBER);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER);
// Credit card related field.
credit_card()->SetNumber(u"4111111111111111");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, credit_card(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, credit_card(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
// Verify that the field is filled with only the first digit of the credit
@@ -475,12 +501,13 @@ TEST_F(AutofillFieldFillerTest, FillFormField_MaxLength_CreditCardField) {
// Test that in the preview credit card numbers are obfuscated.
TEST_F(AutofillFieldFillerTest, FillFormField_Preview_CreditCardField) {
AutofillField field;
- field.set_heuristic_type(CREDIT_CARD_NUMBER);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER);
// Credit card related field.
credit_card()->SetNumber(u"4111111111111111");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, credit_card(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, credit_card(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview);
// Verify that the field contains 4 but no more than 4 digits.
@@ -531,7 +558,8 @@ TEST_P(PhoneNumberTest, FillPhoneNumber) {
address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER,
test_case.phone_home_whole_number_value);
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(test_case.expected_value, field.value);
}
@@ -596,7 +624,8 @@ TEST_P(ExpirationYearTest, FillExpirationYearInput) {
CreditCard card = test::GetCreditCard();
card.SetExpirationDateFromString(u"12/2023");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(test_case.expected_value, field.value);
}
@@ -641,6 +670,7 @@ struct FillUtilExpirationDateTestCase {
size_t field_max_length;
std::u16string expected_value;
bool expected_response;
+ const char* opt_label = nullptr;
};
class ExpirationDateTest
@@ -656,12 +686,22 @@ TEST_P(ExpirationDateTest, FillExpirationDateInput) {
field.SetHtmlType(test_case.field_type, HtmlFieldMode());
field.max_length = test_case.field_max_length;
+ base::test::ScopedFeatureList enabled;
+ if (test_case.opt_label) {
+ field.label = base::UTF8ToUTF16(test_case.opt_label);
+ // We take the addition of label also as an indication to test the
+ // features::kAutofillFillCreditCardAsPerFormatString feature. When this
+ // lands, we can just drop all references to |enabled| from this function.
+ enabled.InitAndEnableFeature(
+ features::kAutofillFillCreditCardAsPerFormatString);
+ }
+
CreditCard card = test::GetCreditCard();
card.SetExpirationDateFromString(u"03/2022");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- bool response = filler.FillFormField(field, &card, &field,
- /*cvc=*/std::u16string(),
- mojom::RendererFormDataAction::kFill);
+ bool response = filler.FillFormField(
+ field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(), mojom::RendererFormDataAction::kFill);
EXPECT_EQ(test_case.expected_value, field.value);
EXPECT_EQ(response, test_case.expected_response);
}
@@ -730,8 +770,51 @@ INSTANTIATE_TEST_SUITE_P(
FillUtilExpirationDateTestCase{
HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 7, u"03/2022", true},
FillUtilExpirationDateTestCase{
- HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 12, u"03/2022",
- true}));
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 12, u"03/2022", true},
+
+ // Tests for features::kAutofillFillCreditCardAsPerFormatString:
+
+ // Base case works regardless of captialization.
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, u"03/22", true,
+ "mm/yy"},
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, u"03/22", true,
+ "MM/YY"},
+ // Even if we expect a 4 digit expiration date, we follow the
+ // placeholder.
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 0, u"03/22", true,
+ "MM/YY"},
+ // Whitespaces are respected.
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, u"03 / 22", true,
+ "MM / YY"},
+ // Whitespaces are stripped if that makes the string fit.
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 5, u"03/22", true,
+ "MM / YY"},
+ // Different separators work.
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, u"03-22", true,
+ "MM-YY"},
+ // Four year expiration years work.
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, u"03-2022", true,
+ "MM-YYYY"},
+ // Some extra text around the pattern does not matter.
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, u"03/22", true,
+ "Credit card in format MM/YY."},
+ // Fallback to the length based filling in case the maxlength is too
+ // low.
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 5, u"03/22", true,
+ "MM/YYYY"},
+ // Empty strings are handled gracefully.
+ FillUtilExpirationDateTestCase{
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 5, u"03/22", true,
+ ""}));
TEST_F(AutofillFieldFillerTest, FillSelectControlByValue) {
std::vector<const char*> kOptions = {
@@ -743,7 +826,7 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlByValue) {
AutofillField field;
test::CreateTestSelectField(kOptions, &field);
- field.set_heuristic_type(NAME_FIRST);
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
// Set semantically empty contents for each option, so that only the values
// can be used for matching.
@@ -752,7 +835,8 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlByValue) {
address()->SetRawInfo(NAME_FIRST, u"Meenie");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, address(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, address(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"Meenie", field.value);
}
@@ -766,7 +850,7 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlByContents) {
};
AutofillField field;
test::CreateTestSelectField(kOptions, &field);
- field.set_heuristic_type(NAME_FIRST);
+ field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
// Set semantically empty values for each option, so that only the contents
// can be used for matching.
@@ -775,7 +859,8 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlByContents) {
address()->SetRawInfo(NAME_FIRST, u"Miney");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, address(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, address(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"2", field.value); // Corresponds to "Miney".
}
@@ -837,13 +922,14 @@ TEST_P(AutofillSelectWithStatesTest, FillSelectWithStates) {
auto test_case = GetParam();
AutofillField field;
test::CreateTestSelectField(test_case.select_values, &field);
- field.set_heuristic_type(ADDRESS_HOME_STATE);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE);
// Without a normalizer.
AutofillProfile address = test::GetFullProfile();
address.SetRawInfo(ADDRESS_HOME_STATE, test_case.input_value);
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
// nullptr means we expect them not to match without normalization.
if (test_case.expected_value_without_normalization != nullptr) {
@@ -855,9 +941,9 @@ TEST_P(AutofillSelectWithStatesTest, FillSelectWithStates) {
canadian_address.SetRawInfo(ADDRESS_HOME_STATE, test_case.input_value);
// Fill a first time without loading the rules for the region.
FieldFiller canadian_filler(/*app_locale=*/"en-US", normalizer());
- canadian_filler.FillFormField(field, &canadian_address, &field,
- /*cvc=*/std::u16string(),
- mojom::RendererFormDataAction::kFill);
+ canadian_filler.FillFormField(
+ field, &canadian_address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(), mojom::RendererFormDataAction::kFill);
// If the expectation with normalization is nullptr, this means that the same
// result than without a normalizer is expected.
if (test_case.expected_value_with_normalization == nullptr) {
@@ -869,9 +955,9 @@ TEST_P(AutofillSelectWithStatesTest, FillSelectWithStates) {
// Load the rules and try again.
normalizer()->LoadRulesForRegion("CA");
- canadian_filler.FillFormField(field, &canadian_address, &field,
- /*cvc=*/std::u16string(),
- mojom::RendererFormDataAction::kFill);
+ canadian_filler.FillFormField(
+ field, &canadian_address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(), mojom::RendererFormDataAction::kFill);
EXPECT_EQ(test_case.expected_value_with_normalization, field.value);
}
}
@@ -926,19 +1012,18 @@ INSTANTIATE_TEST_SUITE_P(
u"North Carolina."},
FillSelectTestCase{{"NC - North Carolina", "CA - California"},
u"CA",
- u"CA - California"},
- // These are not states.
- FillSelectTestCase{{"NCNCA", "SCNCA"}, u"NC", u""}));
+ u"CA - California"}));
TEST_F(AutofillFieldFillerTest, FillSelectWithCountries) {
AutofillField field;
test::CreateTestSelectField({"Albania", "Canada"}, &field);
- field.set_heuristic_type(ADDRESS_HOME_COUNTRY);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_COUNTRY);
AutofillProfile address = test::GetFullProfile();
address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"CA");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"Canada", field.value);
}
@@ -1060,12 +1145,13 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithAbbreviatedMonthName) {
};
AutofillField field;
test::CreateTestSelectField(kMonthsAbbreviated, &field);
- field.set_heuristic_type(CREDIT_CARD_EXP_MONTH);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_EXP_MONTH);
CreditCard card = test::GetCreditCard();
card.SetExpirationMonth(4);
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"Apr", field.value);
}
@@ -1077,12 +1163,13 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithMonthName) {
};
AutofillField field;
test::CreateTestSelectField(kMonthsFull, &field);
- field.set_heuristic_type(CREDIT_CARD_EXP_MONTH);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_EXP_MONTH);
CreditCard card = test::GetCreditCard();
card.SetExpirationMonth(4);
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"April", field.value);
}
@@ -1095,12 +1182,13 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithMonthNameAndDigits) {
};
AutofillField field;
test::CreateTestSelectField(kMonthsFullWithDigits, &field);
- field.set_heuristic_type(CREDIT_CARD_EXP_MONTH);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_EXP_MONTH);
CreditCard card = test::GetCreditCard();
card.SetExpirationMonth(4);
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"April (04)", field.value);
}
@@ -1123,16 +1211,18 @@ TEST_F(AutofillFieldFillerTest,
};
AutofillField field;
test::CreateTestSelectField(kMonthsFullWithDigits, &field);
- field.set_heuristic_type(CREDIT_CARD_EXP_MONTH);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_EXP_MONTH);
CreditCard card = test::GetCreditCard();
card.SetExpirationMonth(8);
FieldFiller filler(/*app_locale=*/"fr-FR", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"08 - AOÛT", field.value);
card.SetExpirationMonth(12);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"12 - DECEMBRE", field.value);
}
@@ -1142,22 +1232,25 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithMonthName_French) {
"décembre"};
AutofillField field;
test::CreateTestSelectField(kMonthsFrench, &field);
- field.set_heuristic_type(CREDIT_CARD_EXP_MONTH);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_EXP_MONTH);
CreditCard card = test::GetCreditCard();
card.SetExpirationMonth(2);
FieldFiller filler(/*app_locale=*/"fr-FR", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"FÉVR.", field.value);
card.SetExpirationMonth(1);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"JANV", field.value);
card.SetExpirationMonth(12);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"décembre", field.value);
}
@@ -1169,12 +1262,13 @@ TEST_F(AutofillFieldFillerTest,
};
AutofillField field;
test::CreateTestSelectField(kMonthsNumeric, &field);
- field.set_heuristic_type(CREDIT_CARD_EXP_MONTH);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_EXP_MONTH);
CreditCard card = test::GetCreditCard();
card.SetExpirationMonth(4);
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"4", field.value);
}
@@ -1184,12 +1278,14 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithTwoDigitCreditCardYear) {
"16", "17", "18", "19"};
AutofillField field;
test::CreateTestSelectField(kYears, &field);
- field.set_heuristic_type(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_EXP_2_DIGIT_YEAR);
CreditCard card = test::GetCreditCard();
card.SetExpirationYear(2017);
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"17", field.value);
}
@@ -1199,31 +1295,35 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithCreditCardType) {
"discover"};
AutofillField field;
test::CreateTestSelectField(kCreditCardTypes, &field);
- field.set_heuristic_type(CREDIT_CARD_TYPE);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_TYPE);
CreditCard card = test::GetCreditCard();
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
// Normal case:
card.SetNumber(u"4111111111111111"); // Visa number.
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"Visa", field.value);
// Filling should be able to handle intervening whitespace:
card.SetNumber(u"5555555555554444"); // MC number.
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"Mastercard", field.value);
// American Express is sometimes abbreviated as AmEx:
card.SetNumber(u"378282246310005"); // Amex number.
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"AmEx", field.value);
// Case insensitivity:
card.SetNumber(u"6011111111111117"); // Discover number.
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"discover", field.value);
}
@@ -1232,18 +1332,21 @@ TEST_F(AutofillFieldFillerTest, FillMonthControl) {
AutofillField field;
field.form_control_type = "month";
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_EXP_4_DIGIT_YEAR);
// Try a month with two digits.
CreditCard card = test::GetCreditCard();
card.SetExpirationDateFromString(u"12/2017");
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"2017-12", field.value);
// Try a month with a leading zero.
card.SetExpirationDateFromString(u"03/2019");
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"2019-03", field.value);
}
@@ -1252,11 +1355,13 @@ TEST_F(AutofillFieldFillerTest, FillStreetAddressTextArea) {
AutofillField field;
field.form_control_type = "textarea";
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(ADDRESS_HOME_STREET_ADDRESS);
+ field.set_heuristic_type(GetActivePatternSource(),
+ ADDRESS_HOME_STREET_ADDRESS);
std::u16string value = u"123 Fake St.\nApt. 42";
address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), value, "en-US");
- filler.FillFormField(field, address(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, address(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(value, field.value);
@@ -1264,7 +1369,8 @@ TEST_F(AutofillFieldFillerTest, FillStreetAddressTextArea) {
address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), ja_value,
"ja-JP");
address()->set_language_code("ja-JP");
- filler.FillFormField(field, address(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, address(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(ja_value, field.value);
}
@@ -1280,7 +1386,8 @@ TEST_F(AutofillFieldFillerTest, FillStreetAddressTextField) {
std::u16string value = u"123 Fake St.\nApt. 42";
address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), value, "en-US");
- filler.FillFormField(field, address(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, address(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"123 Fake St., Apt. 42", field.value);
@@ -1288,7 +1395,8 @@ TEST_F(AutofillFieldFillerTest, FillStreetAddressTextField) {
address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), ja_value,
"ja-JP");
address()->set_language_code("ja-JP");
- filler.FillFormField(field, address(), &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, address(), /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"桜丘町26-1セルリアンタワー6階", field.value);
}
@@ -1296,13 +1404,14 @@ TEST_F(AutofillFieldFillerTest, FillStreetAddressTextField) {
TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithoutSplits) {
// Case 1: card number without any split.
AutofillField cc_number_full;
- cc_number_full.set_heuristic_type(CREDIT_CARD_NUMBER);
+ cc_number_full.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_NUMBER);
credit_card()->SetNumber(u"41111111111111111");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(cc_number_full, credit_card(), &cc_number_full,
- /*cvc=*/std::u16string(),
- mojom::RendererFormDataAction::kFill);
+ filler.FillFormField(
+ cc_number_full, credit_card(), /*forced_fill_values=*/{}, &cc_number_full,
+ /*cvc=*/std::u16string(), mojom::RendererFormDataAction::kFill);
// Verify that full card-number shall get filled properly.
EXPECT_EQ(u"41111111111111111", cc_number_full.value);
@@ -1320,13 +1429,15 @@ TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithEqualSizeSplits) {
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
for (size_t i = 0; i < test.total_splits_; ++i) {
AutofillField cc_number_part;
- cc_number_part.set_heuristic_type(CREDIT_CARD_NUMBER);
+ cc_number_part.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_NUMBER);
cc_number_part.max_length = test.splits_[i];
cc_number_part.set_credit_card_number_offset(4 * i);
// Fill with a card-number; should fill just the card_number_part.
credit_card()->SetNumber(test.card_number_);
- filler.FillFormField(cc_number_part, credit_card(), &cc_number_part,
+ filler.FillFormField(cc_number_part, credit_card(),
+ /*forced_fill_values=*/{}, &cc_number_part,
/*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
@@ -1337,12 +1448,13 @@ TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithEqualSizeSplits) {
// Verify that full card-number shall get fill properly as well.
AutofillField cc_number_full;
- cc_number_full.set_heuristic_type(CREDIT_CARD_NUMBER);
+ cc_number_full.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_NUMBER);
credit_card()->SetNumber(test.card_number_);
- filler.FillFormField(cc_number_full, credit_card(), &cc_number_full,
- /*cvc=*/std::u16string(),
- mojom::RendererFormDataAction::kFill);
+ filler.FillFormField(
+ cc_number_full, credit_card(), /*forced_fill_values=*/{}, &cc_number_full,
+ /*cvc=*/std::u16string(), mojom::RendererFormDataAction::kFill);
// Verify for expected results.
EXPECT_EQ(test.card_number_, cc_number_full.value);
@@ -1365,13 +1477,15 @@ TEST_F(AutofillFieldFillerTest, PreviewCreditCardNumberWithEqualSizeSplits) {
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
for (size_t i = 0; i < test.total_splits_; ++i) {
AutofillField cc_number_part;
- cc_number_part.set_heuristic_type(CREDIT_CARD_NUMBER);
+ cc_number_part.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_NUMBER);
cc_number_part.max_length = test.splits_[i];
cc_number_part.set_credit_card_number_offset(4 * i);
// Fill with a card-number; should fill just the card_number_part.
credit_card()->SetNumber(test.card_number_);
- filler.FillFormField(cc_number_part, credit_card(), &cc_number_part,
+ filler.FillFormField(cc_number_part, credit_card(),
+ /*forced_fill_values=*/{}, &cc_number_part,
/*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview);
@@ -1382,12 +1496,13 @@ TEST_F(AutofillFieldFillerTest, PreviewCreditCardNumberWithEqualSizeSplits) {
// Verify that full card-number shall get fill properly as well.
AutofillField cc_number_full;
- cc_number_full.set_heuristic_type(CREDIT_CARD_NUMBER);
+ cc_number_full.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_NUMBER);
credit_card()->SetNumber(test.card_number_);
- filler.FillFormField(cc_number_full, credit_card(), &cc_number_full,
- /*cvc=*/std::u16string(),
- mojom::RendererFormDataAction::kPreview);
+ filler.FillFormField(
+ cc_number_full, credit_card(), /*forced_fill_values=*/{}, &cc_number_full,
+ /*cvc=*/std::u16string(), mojom::RendererFormDataAction::kPreview);
// Verify for expected results.
EXPECT_EQ(obfuscated_card_number, cc_number_full.value);
@@ -1406,13 +1521,15 @@ TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithUnequalSizeSplits) {
// Start executing test cases to verify parts and full credit card number.
for (size_t i = 0; i < test.total_splits_; ++i) {
AutofillField cc_number_part;
- cc_number_part.set_heuristic_type(CREDIT_CARD_NUMBER);
+ cc_number_part.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_NUMBER);
cc_number_part.max_length = test.splits_[i];
cc_number_part.set_credit_card_number_offset(GetNumberOffset(i, test));
// Fill with a card-number; should fill just the card_number_part.
credit_card()->SetNumber(test.card_number_);
- filler.FillFormField(cc_number_part, credit_card(), &cc_number_part,
+ filler.FillFormField(cc_number_part, credit_card(),
+ /*forced_fill_values=*/{}, &cc_number_part,
/*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
@@ -1424,11 +1541,12 @@ TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithUnequalSizeSplits) {
// Verify that full card-number shall get fill properly as well.
AutofillField cc_number_full;
- cc_number_full.set_heuristic_type(CREDIT_CARD_NUMBER);
+ cc_number_full.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_NUMBER);
credit_card()->SetNumber(test.card_number_);
- filler.FillFormField(cc_number_full, credit_card(), &cc_number_full,
- /*cvc=*/std::u16string(),
- mojom::RendererFormDataAction::kFill);
+ filler.FillFormField(
+ cc_number_full, credit_card(), /*forced_fill_values=*/{}, &cc_number_full,
+ /*cvc=*/std::u16string(), mojom::RendererFormDataAction::kFill);
// Verify for expected results.
EXPECT_EQ(test.card_number_, cc_number_full.value);
@@ -1455,13 +1573,15 @@ TEST_F(AutofillFieldFillerTest, PreviewCreditCardNumberWithUnequalSizeSplits) {
// Start executing test cases to verify parts and full credit card number.
for (size_t i = 0; i < test.total_splits_; ++i) {
AutofillField cc_number_part;
- cc_number_part.set_heuristic_type(CREDIT_CARD_NUMBER);
+ cc_number_part.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_NUMBER);
cc_number_part.max_length = test.splits_[i];
cc_number_part.set_credit_card_number_offset(GetNumberOffset(i, test));
// Fill with a card-number; should fill just the card_number_part.
credit_card()->SetNumber(test.card_number_);
- filler.FillFormField(cc_number_part, credit_card(), &cc_number_part,
+ filler.FillFormField(cc_number_part, credit_card(),
+ /*forced_fill_values=*/{}, &cc_number_part,
/*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview);
@@ -1473,11 +1593,12 @@ TEST_F(AutofillFieldFillerTest, PreviewCreditCardNumberWithUnequalSizeSplits) {
// Verify that full card-number shall get fill properly as well.
AutofillField cc_number_full;
- cc_number_full.set_heuristic_type(CREDIT_CARD_NUMBER);
+ cc_number_full.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_NUMBER);
credit_card()->SetNumber(test.card_number_);
- filler.FillFormField(cc_number_full, credit_card(), &cc_number_full,
- /*cvc=*/std::u16string(),
- mojom::RendererFormDataAction::kPreview);
+ filler.FillFormField(
+ cc_number_full, credit_card(), /*forced_fill_values=*/{}, &cc_number_full,
+ /*cvc=*/std::u16string(), mojom::RendererFormDataAction::kPreview);
// Verify for expected results.
EXPECT_EQ(obfuscated_card_number, cc_number_full.value);
@@ -1550,9 +1671,9 @@ TEST_P(AutofillStateTextTest, FillStateText) {
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
AutofillProfile address = test::GetFullProfile();
address.SetRawInfo(ADDRESS_HOME_STATE, test_case.value_to_fill);
- bool has_filled = filler.FillFormField(field, &address, &field,
- /*cvc=*/std::u16string(),
- mojom::RendererFormDataAction::kFill);
+ bool has_filled = filler.FillFormField(
+ field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(), mojom::RendererFormDataAction::kFill);
EXPECT_EQ(test_case.should_fill, has_filled);
EXPECT_EQ(test_case.expected_value, field.value);
@@ -1601,12 +1722,13 @@ TEST_F(AutofillFieldFillerTest,
std::vector<const char*> kPhoneCountryCode = {"91", "1", "20", "49"};
AutofillField field;
test::CreateTestSelectField(kPhoneCountryCode, &field);
- field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE);
+ field.set_heuristic_type(GetActivePatternSource(), PHONE_HOME_COUNTRY_CODE);
AutofillProfile address;
address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"+15145554578");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"1", field.value);
}
@@ -1623,12 +1745,13 @@ TEST_F(AutofillFieldFillerTest,
std::vector<const char*> kPhoneCountryCode = {"+91", "+1", "+20", "+49"};
AutofillField field;
test::CreateTestSelectField(kPhoneCountryCode, &field);
- field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE);
+ field.set_heuristic_type(GetActivePatternSource(), PHONE_HOME_COUNTRY_CODE);
AutofillProfile address;
address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"+918890888888");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"+91", field.value);
}
@@ -1645,12 +1768,13 @@ TEST_F(AutofillFieldFillerTest,
std::vector<const char*> kPhoneCountryCode = {"0091", "001", "0020", "0049"};
AutofillField field;
test::CreateTestSelectField(kPhoneCountryCode, &field);
- field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE);
+ field.set_heuristic_type(GetActivePatternSource(), PHONE_HOME_COUNTRY_CODE);
AutofillProfile address;
address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"+918890888888");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"0091", field.value);
}
@@ -1667,12 +1791,13 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlAugmentedPhoneCountryCode) {
"+20 (Egypt)", "+49 (Germany)"};
AutofillField field;
test::CreateTestSelectField(kPhoneCountryCode, &field);
- field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE);
+ field.set_heuristic_type(GetActivePatternSource(), PHONE_HOME_COUNTRY_CODE);
AutofillProfile address;
address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"+49151669087345");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"+49 (Germany)", field.value);
}
@@ -1690,12 +1815,13 @@ TEST_F(AutofillFieldFillerTest,
"(00 20) Egypt", "(00 49) Germany"};
AutofillField field;
test::CreateTestSelectField(kPhoneCountryCode, &field);
- field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE);
+ field.set_heuristic_type(GetActivePatternSource(), PHONE_HOME_COUNTRY_CODE);
AutofillProfile address;
address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"+49151669087345");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"(00 49) Germany", field.value);
}
@@ -1714,12 +1840,13 @@ TEST_F(AutofillFieldFillerTest,
"(0020) Egypt", "(0049) Germany"};
AutofillField field;
test::CreateTestSelectField(kPhoneCountryCode, &field);
- field.set_heuristic_type(PHONE_HOME_COUNTRY_CODE);
+ field.set_heuristic_type(GetActivePatternSource(), PHONE_HOME_COUNTRY_CODE);
AutofillProfile address;
address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"+49151669087345");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"(0049) Germany", field.value);
}
@@ -1735,14 +1862,15 @@ TEST_F(AutofillFieldFillerTest, FillSelectAbbreviatedState) {
AutofillField field;
test::CreateTestSelectField(kState, &field);
- field.set_heuristic_type(ADDRESS_HOME_STATE);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE);
AutofillProfile address;
address.SetRawInfo(ADDRESS_HOME_STATE, u"Bavaria");
address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"DE");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"BY", field.value);
}
@@ -1759,14 +1887,15 @@ TEST_F(AutofillFieldFillerTest, FillSelectLocalizedState) {
AutofillField field;
test::CreateTestSelectField(kState, &field);
- field.set_heuristic_type(ADDRESS_HOME_STATE);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE);
AutofillProfile address;
address.SetRawInfo(ADDRESS_HOME_STATE, u"Bavaria");
address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"DE");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"Bayern", field.value);
}
@@ -1783,14 +1912,15 @@ TEST_F(AutofillFieldFillerTest, FillSelectLocalizedStateSubstring) {
AutofillField field;
test::CreateTestSelectField(kState, &field);
- field.set_heuristic_type(ADDRESS_HOME_STATE);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE);
AutofillProfile address;
address.SetRawInfo(ADDRESS_HOME_STATE, u"Bavaria");
address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"DE");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"Bavaria Has Munich", field.value);
}
@@ -1806,7 +1936,7 @@ TEST_F(AutofillFieldFillerTest, FillStateAbbreviationInTextField) {
AutofillField field;
test::CreateTestFormField("State", "state", "", "text", &field);
- field.set_heuristic_type(ADDRESS_HOME_STATE);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE);
field.max_length = 4;
AutofillProfile address;
@@ -1814,7 +1944,8 @@ TEST_F(AutofillFieldFillerTest, FillStateAbbreviationInTextField) {
address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"DE");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"BY", field.value);
}
@@ -1831,14 +1962,15 @@ TEST_F(AutofillFieldFillerTest, FillStateFieldWithSavedValueInProfile) {
AutofillField field;
test::CreateTestSelectField(kState, &field);
- field.set_heuristic_type(ADDRESS_HOME_STATE);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE);
AutofillProfile address;
address.SetRawInfo(ADDRESS_HOME_STATE, u"Bavari");
address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"DE");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"Bavari", field.value);
}
@@ -1860,14 +1992,15 @@ TEST_F(AutofillFieldFillerTest, FillStateFieldWhenStateIsNotInOptions) {
AutofillField field;
test::CreateTestSelectField(kState, &field);
- field.set_heuristic_type(ADDRESS_HOME_STATE);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE);
AutofillProfile address;
address.SetRawInfo(ADDRESS_HOME_STATE, u"CO");
address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"", field.value);
}
@@ -1884,14 +2017,15 @@ TEST_F(AutofillFieldFillerTest,
AutofillField field;
test::CreateTestSelectField(kState, &field);
- field.set_heuristic_type(ADDRESS_HOME_STATE);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE);
AutofillProfile address;
address.SetRawInfo(ADDRESS_HOME_STATE, u"CO");
address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"Colorado", field.value);
}
@@ -1910,7 +2044,7 @@ TEST_F(AutofillFieldFillerTest, FillUpperCaseAbbreviationInStateTextField) {
AutofillField field;
test::CreateTestFormField("State", "state", "", "text", &field);
- field.set_heuristic_type(ADDRESS_HOME_STATE);
+ field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE);
field.max_length = 4;
AutofillProfile address;
@@ -1918,7 +2052,8 @@ TEST_F(AutofillFieldFillerTest, FillUpperCaseAbbreviationInStateTextField) {
address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"DE");
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kFill);
EXPECT_EQ(u"BY", field.value);
}
@@ -1927,19 +2062,21 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualMonth) {
AutofillField field;
field.form_control_type = "text";
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(CREDIT_CARD_EXP_MONTH);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_EXP_MONTH);
// A month with two digits should return two dots.
CreditCard card = test::GetVirtualCard();
card.SetExpirationDateFromString(u"12/2017");
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
EXPECT_EQ(kMidlineEllipsis2Dots, field.value);
// A month with one digit should still return two dots.
card.SetExpirationDateFromString(u"03/2019");
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
EXPECT_EQ(kMidlineEllipsis2Dots, field.value);
@@ -1949,17 +2086,21 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualYear) {
AutofillField field;
field.form_control_type = "text";
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_EXP_4_DIGIT_YEAR);
CreditCard card = test::GetVirtualCard();
card.SetExpirationDateFromString(u"12/2017");
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
EXPECT_EQ(kMidlineEllipsis4Dots, field.value);
- field.set_heuristic_type(CREDIT_CARD_EXP_2_DIGIT_YEAR);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_EXP_2_DIGIT_YEAR);
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
EXPECT_EQ(kMidlineEllipsis2Dots, field.value);
@@ -1971,11 +2112,13 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualShortenedYear) {
field.max_length = 2;
field.form_control_type = "text";
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_EXP_4_DIGIT_YEAR);
CreditCard card = test::GetVirtualCard();
card.SetExpirationDateFromString(u"12/2017");
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
EXPECT_EQ(kMidlineEllipsis2Dots, field.value);
@@ -1985,14 +2128,16 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualDate) {
AutofillField field;
field.form_control_type = "text";
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
field.max_length = 7;
// A date that has a year containing four digits should return two dots for
// month and four dots for year.
CreditCard card = test::GetVirtualCard();
card.SetExpirationDateFromString(u"12/2017");
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
std::u16string slash = u"/";
@@ -2002,9 +2147,11 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualDate) {
// A date that has a year containing two digits should return two dots for
// month and two for year.
- field.set_heuristic_type(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR);
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR);
field.max_length = 5;
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
@@ -2018,11 +2165,13 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualShortenedDate) {
field.form_control_type = "text";
field.max_length = 4;
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
CreditCard card = test::GetVirtualCard();
card.SetExpirationDateFromString(u"12/2017");
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
@@ -2032,7 +2181,8 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualShortenedDate) {
field.max_length = 5;
std::u16string slash = u"/";
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
@@ -2041,7 +2191,8 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualShortenedDate) {
EXPECT_EQ(expected, field.value);
field.max_length = 6;
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
@@ -2050,7 +2201,8 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualShortenedDate) {
EXPECT_EQ(expected, field.value);
field.max_length = 7;
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
@@ -2063,11 +2215,13 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualCVC) {
AutofillField field;
field.form_control_type = "text";
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(CREDIT_CARD_VERIFICATION_CODE);
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_VERIFICATION_CODE);
CreditCard card = test::GetVirtualCard();
CreditCardTestApi(&card).set_network_for_virtual_card(kMasterCard);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
EXPECT_EQ(kMidlineEllipsis3Dots, field.value);
@@ -2078,11 +2232,13 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualCVCAmericanExpress) {
AutofillField field;
field.form_control_type = "text";
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(CREDIT_CARD_VERIFICATION_CODE);
+ field.set_heuristic_type(GetActivePatternSource(),
+ CREDIT_CARD_VERIFICATION_CODE);
CreditCard card = test::GetVirtualCard();
CreditCardTestApi(&card).set_network_for_virtual_card(kAmericanExpressCard);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
EXPECT_EQ(kMidlineEllipsis4Dots, field.value);
@@ -2090,7 +2246,7 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualCVCAmericanExpress) {
TEST_F(AutofillFieldFillerTest, PreviewVirtualCardNumber) {
AutofillField field;
- field.set_heuristic_type(CREDIT_CARD_NUMBER);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER);
field.set_credit_card_number_offset(50);
field.form_control_type = "text";
const char kMasterCard[] = "masterCardCC";
@@ -2099,7 +2255,7 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualCardNumber) {
card.SetNumber(u"5454545454545454");
CreditCardTestApi(&card).set_network_for_virtual_card(kMasterCard);
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- filler.FillFormField(field, &card, &field,
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
/*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
@@ -2120,13 +2276,14 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualCardholderName) {
AutofillField field;
field.form_control_type = "text";
FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
- field.set_heuristic_type(CREDIT_CARD_NAME_FULL);
+ field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NAME_FULL);
CreditCard card = test::GetVirtualCard();
card.SetRawInfoWithVerificationStatus(
CREDIT_CARD_NAME_FULL, name,
structured_address::VerificationStatus::kFormatted);
- filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string(),
+ filler.FillFormField(field, &card, /*forced_fill_values=*/{}, &field,
+ /*cvc=*/std::u16string(),
mojom::RendererFormDataAction::kPreview,
/*failure_to_fill*/ nullptr);
EXPECT_EQ(name, field.value);
diff --git a/chromium/components/autofill/core/browser/field_types.cc b/chromium/components/autofill/core/browser/field_types.cc
index 9fce573eeb8..15d1af444c1 100644
--- a/chromium/components/autofill/core/browser/field_types.cc
+++ b/chromium/components/autofill/core/browser/field_types.cc
@@ -48,9 +48,13 @@ bool IsFillableFieldType(ServerFieldType field_type) {
case EMAIL_ADDRESS:
case USERNAME_AND_EMAIL_ADDRESS:
case PHONE_HOME_NUMBER:
+ case PHONE_HOME_NUMBER_PREFIX:
+ case PHONE_HOME_NUMBER_SUFFIX:
case PHONE_HOME_CITY_CODE:
+ case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
case PHONE_HOME_COUNTRY_CODE:
case PHONE_HOME_CITY_AND_NUMBER:
+ case PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX:
case PHONE_HOME_WHOLE_NUMBER:
case PHONE_HOME_EXTENSION:
case ADDRESS_HOME_LINE1:
@@ -154,8 +158,6 @@ bool IsFillableFieldType(ServerFieldType field_type) {
case PHONE_FAX_COUNTRY_CODE:
case PHONE_FAX_CITY_AND_NUMBER:
case PHONE_FAX_WHOLE_NUMBER:
- case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
- case PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX:
case FIELD_WITH_DEFAULT_VALUE:
case MERCHANT_EMAIL_SIGNUP:
case PRICE:
@@ -219,6 +221,10 @@ base::StringPiece FieldTypeToStringPiece(ServerFieldType type) {
return "EMAIL_ADDRESS";
case PHONE_HOME_NUMBER:
return "PHONE_HOME_NUMBER";
+ case PHONE_HOME_NUMBER_PREFIX:
+ return "PHONE_HOME_NUMBER_PREFIX";
+ case PHONE_HOME_NUMBER_SUFFIX:
+ return "PHONE_HOME_NUMBER_SUFFIX";
case PHONE_HOME_CITY_CODE:
return "PHONE_HOME_CITY_CODE";
case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h
index 0ecd1bda571..adc66c8bec1 100644
--- a/chromium/components/autofill/core/browser/field_types.h
+++ b/chromium/components/autofill/core/browser/field_types.h
@@ -256,9 +256,14 @@ enum ServerFieldType {
// combination with a PHONE_HOME_COUNTRY_CODE field.
PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX = 122,
+ // PHONE_HOME_NUMBER = PHONE_HOME_NUMBER_PREFIX + PHONE_HOME_NUMBER_SUFFIX.
+ // For the US numbers (650) 234-5678 the types correspond to 234 and 5678.
+ PHONE_HOME_NUMBER_PREFIX = 123,
+ PHONE_HOME_NUMBER_SUFFIX = 124,
+
// No new types can be added without a corresponding change to the Autofill
// server.
- MAX_VALID_FIELD_TYPE = 123,
+ MAX_VALID_FIELD_TYPE = 125,
};
// The list of all HTML autocomplete field type hints supported by Chrome.
diff --git a/chromium/components/autofill/core/browser/field_types_unittest.cc b/chromium/components/autofill/core/browser/field_types_unittest.cc
index 30e1780c59e..f4afb70f6f9 100644
--- a/chromium/components/autofill/core/browser/field_types_unittest.cc
+++ b/chromium/components/autofill/core/browser/field_types_unittest.cc
@@ -21,6 +21,8 @@ TEST(FieldTypesTest, IsValidServerFieldType) {
NAME_SUFFIX,
EMAIL_ADDRESS,
PHONE_HOME_NUMBER,
+ PHONE_HOME_NUMBER_PREFIX,
+ PHONE_HOME_NUMBER_SUFFIX,
PHONE_HOME_CITY_CODE,
PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX,
PHONE_HOME_COUNTRY_CODE,
diff --git a/chromium/components/autofill/core/browser/form_data_importer.cc b/chromium/components/autofill/core/browser/form_data_importer.cc
index f696d514f28..e87e427c1e8 100644
--- a/chromium/components/autofill/core/browser/form_data_importer.cc
+++ b/chromium/components/autofill/core/browser/form_data_importer.cc
@@ -16,6 +16,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/containers/contains.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
@@ -23,11 +24,13 @@
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/data_model/autofill_profile_comparator.h"
#include "components/autofill/core/browser/data_model/autofill_structured_address_name.h"
#include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/data_model/phone_number.h"
#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/form_types.h"
#include "components/autofill/core/browser/geo/autofill_country.h"
#include "components/autofill/core/browser/geo/phone_number_i18n.h"
#include "components/autofill/core/browser/logging/log_manager.h"
@@ -35,6 +38,7 @@
#include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_internals/log_message.h"
#include "components/autofill/core/common/autofill_internals/logging_scope.h"
@@ -107,7 +111,8 @@ bool IsValidFieldTypeAndValue(const ServerFieldTypeSet types_seen,
bool IsMinimumAddress(const AutofillProfile& profile,
const std::string& predicted_country_code,
const std::string& app_locale,
- LogBuffer* import_log_buffer) {
+ LogBuffer* import_log_buffer,
+ bool collect_metrics) {
AutofillCountry country(predicted_country_code, app_locale);
// Include the details of the country to the log.
@@ -116,9 +121,8 @@ bool IsMinimumAddress(const AutofillProfile& profile,
// Check the |ADDRESS_HOME_LINE1| requirement.
bool is_line1_missing = false;
- if (country.requires_line1() &&
- profile.GetRawInfo(ADDRESS_HOME_LINE1).empty() &&
- profile.GetRawInfo(ADDRESS_HOME_STREET_NAME).empty()) {
+ if (country.requires_line1() && !profile.HasRawInfo(ADDRESS_HOME_LINE1) &&
+ !profile.HasRawInfo(ADDRESS_HOME_STREET_NAME)) {
if (import_log_buffer) {
*import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
<< "Missing required ADDRESS_HOME_LINE1." << CTag{};
@@ -128,8 +132,7 @@ bool IsMinimumAddress(const AutofillProfile& profile,
// Check the |ADDRESS_HOME_CITY| requirement.
bool is_city_missing = false;
- if (country.requires_city() &&
- profile.GetRawInfo(ADDRESS_HOME_CITY).empty()) {
+ if (country.requires_city() && !profile.HasRawInfo(ADDRESS_HOME_CITY)) {
if (import_log_buffer) {
*import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
<< "Missing required ADDRESS_HOME_CITY." << CTag{};
@@ -139,8 +142,7 @@ bool IsMinimumAddress(const AutofillProfile& profile,
// Check the |ADDRESS_HOME_STATE| requirement.
bool is_state_missing = false;
- if (country.requires_state() &&
- profile.GetRawInfo(ADDRESS_HOME_STATE).empty()) {
+ if (country.requires_state() && !profile.HasRawInfo(ADDRESS_HOME_STATE)) {
if (import_log_buffer) {
*import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
<< "Missing required ADDRESS_HOME_STATE." << CTag{};
@@ -150,7 +152,7 @@ bool IsMinimumAddress(const AutofillProfile& profile,
// Check the |ADDRESS_HOME_ZIP| requirement.
bool is_zip_missing = false;
- if (country.requires_zip() && profile.GetRawInfo(ADDRESS_HOME_ZIP).empty()) {
+ if (country.requires_zip() && !profile.HasRawInfo(ADDRESS_HOME_ZIP)) {
if (import_log_buffer) {
*import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
<< "Missing required ADDRESS_HOME_ZIP." << CTag{};
@@ -160,8 +162,8 @@ bool IsMinimumAddress(const AutofillProfile& profile,
bool is_zip_or_state_requirement_violated = false;
if (country.requires_zip_or_state() &&
- profile.GetRawInfo(ADDRESS_HOME_ZIP).empty() &&
- profile.GetRawInfo(ADDRESS_HOME_STATE).empty()) {
+ !profile.HasRawInfo(ADDRESS_HOME_ZIP) &&
+ !profile.HasRawInfo(ADDRESS_HOME_STATE)) {
if (import_log_buffer) {
*import_log_buffer
<< LogMessage::kImportAddressProfileFromFormFailed
@@ -173,8 +175,8 @@ bool IsMinimumAddress(const AutofillProfile& profile,
bool is_line1_or_house_number_violated = false;
if (country.requires_line1_or_house_number() &&
- profile.GetRawInfo(ADDRESS_HOME_LINE1).empty() &&
- profile.GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER).empty()) {
+ !profile.HasRawInfo(ADDRESS_HOME_LINE1) &&
+ !profile.HasRawInfo(ADDRESS_HOME_HOUSE_NUMBER)) {
if (import_log_buffer) {
*import_log_buffer
<< LogMessage::kImportAddressProfileFromFormFailed
@@ -185,29 +187,33 @@ bool IsMinimumAddress(const AutofillProfile& profile,
}
// Collect metrics regarding the requirements.
- AutofillMetrics::LogAddressFormImportRequirementMetric(
- is_line1_missing ? AddressImportRequirement::LINE1_REQUIREMENT_VIOLATED
- : AddressImportRequirement::LINE1_REQUIREMENT_FULFILLED);
-
- AutofillMetrics::LogAddressFormImportRequirementMetric(
- is_city_missing ? AddressImportRequirement::CITY_REQUIREMENT_VIOLATED
- : AddressImportRequirement::CITY_REQUIREMENT_FULFILLED);
-
- AutofillMetrics::LogAddressFormImportRequirementMetric(
- is_state_missing ? AddressImportRequirement::STATE_REQUIREMENT_VIOLATED
- : AddressImportRequirement::STATE_REQUIREMENT_FULFILLED);
-
- AutofillMetrics::LogAddressFormImportRequirementMetric(
- is_zip_missing ? AddressImportRequirement::ZIP_REQUIREMENT_VIOLATED
- : AddressImportRequirement::ZIP_REQUIREMENT_FULFILLED);
-
- AutofillMetrics::LogAddressFormImportRequirementMetric(
- is_zip_or_state_requirement_violated
- ? AddressImportRequirement::ZIP_OR_STATE_REQUIREMENT_VIOLATED
- : AddressImportRequirement::ZIP_OR_STATE_REQUIREMENT_FULFILLED);
-
- AutofillMetrics::LogAddressFormImportCountrySpecificFieldRequirementsMetric(
- is_zip_missing, is_state_missing, is_city_missing, is_line1_missing);
+ if (collect_metrics) {
+ AutofillMetrics::LogAddressFormImportRequirementMetric(
+ is_line1_missing
+ ? AddressImportRequirement::LINE1_REQUIREMENT_VIOLATED
+ : AddressImportRequirement::LINE1_REQUIREMENT_FULFILLED);
+
+ AutofillMetrics::LogAddressFormImportRequirementMetric(
+ is_city_missing ? AddressImportRequirement::CITY_REQUIREMENT_VIOLATED
+ : AddressImportRequirement::CITY_REQUIREMENT_FULFILLED);
+
+ AutofillMetrics::LogAddressFormImportRequirementMetric(
+ is_state_missing
+ ? AddressImportRequirement::STATE_REQUIREMENT_VIOLATED
+ : AddressImportRequirement::STATE_REQUIREMENT_FULFILLED);
+
+ AutofillMetrics::LogAddressFormImportRequirementMetric(
+ is_zip_missing ? AddressImportRequirement::ZIP_REQUIREMENT_VIOLATED
+ : AddressImportRequirement::ZIP_REQUIREMENT_FULFILLED);
+
+ AutofillMetrics::LogAddressFormImportRequirementMetric(
+ is_zip_or_state_requirement_violated
+ ? AddressImportRequirement::ZIP_OR_STATE_REQUIREMENT_VIOLATED
+ : AddressImportRequirement::ZIP_OR_STATE_REQUIREMENT_FULFILLED);
+
+ AutofillMetrics::LogAddressFormImportCountrySpecificFieldRequirementsMetric(
+ is_zip_missing, is_state_missing, is_city_missing, is_line1_missing);
+ }
// Return true if all requirements are fulfilled.
return !(is_line1_missing || is_city_missing || is_state_missing ||
@@ -245,9 +251,14 @@ FormDataImporter::FormDataImporter(AutofillClient* client,
std::make_unique<VirtualCardEnrollmentManager>(personal_data_manager,
payments_client,
client)) {
+ if (personal_data_manager_)
+ personal_data_manager_->AddObserver(this);
}
-FormDataImporter::~FormDataImporter() = default;
+FormDataImporter::~FormDataImporter() {
+ if (personal_data_manager_)
+ personal_data_manager_->RemoveObserver(this);
+};
void FormDataImporter::ImportFormData(const FormStructure& submitted_form,
bool profile_autofill_enabled,
@@ -337,13 +348,6 @@ bool FormDataImporter::IsValidLearnableProfile(
const std::string& predicted_country_code,
const std::string& app_locale,
LogBuffer* import_log_buffer) {
- // Check if the imported address qualifies as a minimum address.
- bool is_not_minimum_address = false;
- if (!IsMinimumAddress(profile, predicted_country_code, app_locale,
- import_log_buffer)) {
- is_not_minimum_address = true;
- }
-
// Check that the email address is valid if it is supplied.
bool is_email_invalid = false;
std::u16string email = profile.GetRawInfo(EMAIL_ADDRESS);
@@ -394,8 +398,85 @@ bool FormDataImporter::IsValidLearnableProfile(
: AddressImportRequirement::ZIP_VALID_REQUIREMENT_FULFILLED);
// Return true if none of the requirements is violated.
- return !(is_not_minimum_address || is_email_invalid || is_state_invalid ||
- is_zip_invalid);
+ return !(is_email_invalid || is_state_invalid || is_zip_invalid);
+}
+
+bool FormDataImporter::ComplementCountry(
+ AutofillProfile& profile,
+ const std::string& predicted_country_code) {
+ // TODO(crbug.com/1297032): Cleanup `kAutofillComplementCountryCodeOnImport`
+ // check when launched.
+ bool should_complement_country =
+ !profile.HasRawInfo(ADDRESS_HOME_COUNTRY) &&
+ base::FeatureList::IsEnabled(
+ features::kAutofillAddressProfileSavePrompt) &&
+ base::FeatureList::IsEnabled(
+ features::kAutofillComplementCountryCodeOnImport);
+ return should_complement_country &&
+ profile.SetInfoWithVerificationStatus(
+ AutofillType(ADDRESS_HOME_COUNTRY),
+ base::ASCIIToUTF16(predicted_country_code), app_locale_,
+ VerificationStatus::kObserved);
+}
+
+bool FormDataImporter::SetPhoneNumber(
+ AutofillProfile& profile,
+ PhoneNumber::PhoneCombineHelper& combined_phone,
+ const std::string& predicted_country_code) {
+ if (combined_phone.IsEmpty()) {
+ return true;
+ }
+ const std::string predicted_country_code_without_variation =
+ GetPredictedCountryCode(profile, "", app_locale_, nullptr);
+ auto SetWithRegion = [&](const std::string& region) {
+ std::u16string constructed_number;
+ // `ParseNumber()` implicity accepts both a country code and a locale. This
+ // will be refactored with crbug.com/1296077. The parameter for
+ // `SetInfoWithVerificationStatus()` has to be consistent with
+ // `ParseNumber()`.
+ return combined_phone.ParseNumber(profile, region, &constructed_number) &&
+ profile.SetInfoWithVerificationStatus(
+ PHONE_HOME_WHOLE_NUMBER, constructed_number,
+ /*app_locale=*/region, VerificationStatus::kObserved);
+ };
+ // If `AutofillConsiderVariationCountryCodeForPhoneNumbers` is enabled,
+ // a consistent country code prediction for addresses and phone numbers is
+ // used. Otherwise the variation service state is not considered for phone
+ // numbers. This makes a difference, if the country code cannot be found
+ // in the `profile`.
+ // TODO(crbug.com/1295721): Cleanup when launched.
+ bool success_with_locale = SetWithRegion(app_locale_);
+ if (predicted_country_code == predicted_country_code_without_variation ||
+ !base::FeatureList::IsEnabled(
+ features::kAutofillConsiderVariationCountryCodeForPhoneNumbers))
+ return success_with_locale;
+ // AutofillConsiderVariationCountryCodeForPhoneNumbers is enabled and makes
+ // a difference for the region used. Parse the number with the new region and
+ // check if this actually changes the parsing outcome to measure the impact.
+ bool success_with_variation_code = SetWithRegion(predicted_country_code);
+ AutofillMetrics::LogPhoneNumberImportParsingResult(
+ success_with_variation_code, success_with_locale);
+ // Keep the current state, even if the parsing worked with the locale but not
+ // the variation country code. Because once
+ // `AutofillConsiderVariationCountryCodeForPhoneNumbers` is launched, only
+ // region = `predicted_country_code` will be used for parsing.
+ return success_with_variation_code;
+}
+
+void FormDataImporter::RemoveInaccessibleProfileValues(
+ AutofillProfile& profile,
+ const std::string& predicted_country_code) {
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillRemoveInaccessibleProfileValues)) {
+ const ServerFieldTypeSet inaccessible_fields =
+ profile.FindInaccessibleProfileValues(predicted_country_code);
+ profile.ClearFields(inaccessible_fields);
+ AutofillMetrics::LogRemovedSettingInaccessibleFields(
+ !inaccessible_fields.empty());
+ for (const ServerFieldType inaccessible_field : inaccessible_fields) {
+ AutofillMetrics::LogRemovedSettingInaccessibleField(inaccessible_field);
+ }
+ }
}
void FormDataImporter::CacheFetchedVirtualCard(
@@ -488,20 +569,20 @@ bool FormDataImporter::ImportAddressProfiles(
// Run the import on the union of the section if the import was not
// successful and if there is more than one section.
if (num_complete_profiles > 0) {
- AutofillMetrics::LogAddressFormImportStatustMetric(
+ AutofillMetrics::LogAddressFormImportStatusMetric(
AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT);
} else if (sections.size() > 1) {
// Try to import by combining all sections.
if (ImportAddressProfileForSection(form, "", import_candidates,
&import_log_buffer)) {
num_complete_profiles++;
- AutofillMetrics::LogAddressFormImportStatustMetric(
+ AutofillMetrics::LogAddressFormImportStatusMetric(
AutofillMetrics::AddressProfileImportStatusMetric::
SECTION_UNION_IMPORT);
}
}
if (num_complete_profiles == 0) {
- AutofillMetrics::LogAddressFormImportStatustMetric(
+ AutofillMetrics::LogAddressFormImportStatusMetric(
AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT);
}
}
@@ -552,6 +633,9 @@ bool FormDataImporter::ImportAddressProfileForSection(
// Metadata about the way we construct candidate_profile.
ProfileImportMetadata import_metadata;
+ // Tracks if any of the fields belongs to FormType::kAddressForm.
+ bool has_address_related_fields = false;
+
// Go through each |form| field and attempt to constitute a valid profile.
for (const auto& field : form) {
// Reject fields that are not within the specified |section|.
@@ -579,6 +663,9 @@ bool FormDataImporter::ImportAddressProfileForSection(
if (field_type.group() == FieldTypeGroup::kCreditCard)
continue;
+ has_address_related_fields |=
+ FieldTypeGroupToFormType(field_type.group()) == FormType::kAddressForm;
+
// There can be multiple email fields (e.g. in the case of 'confirm email'
// fields) but they must all contain the same value, else the profile is
// invalid.
@@ -632,7 +719,7 @@ bool FormDataImporter::ImportAddressProfileForSection(
// Reject profiles with invalid country information.
if (server_field_type == ADDRESS_HOME_COUNTRY &&
- candidate_profile.GetRawInfo(ADDRESS_HOME_COUNTRY).empty()) {
+ !candidate_profile.HasRawInfo(ADDRESS_HOME_COUNTRY)) {
// The country code was not successfully determined from the value in
// the country field. This can be caused by a localization that does not
// match the |app_locale|. Try setting the value again using the
@@ -649,7 +736,7 @@ bool FormDataImporter::ImportAddressProfileForSection(
field_type, value, page_language, VerificationStatus::kObserved);
}
// Check if the country code was still not determined correctly.
- if (candidate_profile.GetRawInfo(ADDRESS_HOME_COUNTRY).empty()) {
+ if (!candidate_profile.HasRawInfo(ADDRESS_HOME_COUNTRY)) {
if (import_log_buffer) {
*import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
<< "Missing country." << CTag{};
@@ -659,93 +746,68 @@ bool FormDataImporter::ImportAddressProfileForSection(
}
}
- const std::string predicted_country_code = GetPredictedCountryCode(
- candidate_profile, client_->GetVariationConfigCountryCode(), app_locale_,
- import_log_buffer);
- // If the form doesn't contain a country field, complement the profile using
- // |predicted_country_code|. To give users the opportunity to edit, this is
- // only done with explicit save prompts enabled.
- // TODO(crbug.com/1297032): Cleanup kAutofillComplementCountryCodeOnImport
- // check when launched.
- if (!has_invalid_country &&
- candidate_profile.GetRawInfo(ADDRESS_HOME_COUNTRY).empty() &&
- base::FeatureList::IsEnabled(
- features::kAutofillAddressProfileSavePrompt) &&
- base::FeatureList::IsEnabled(
- features::kAutofillComplementCountryCodeOnImport)) {
- candidate_profile.SetInfoWithVerificationStatus(
- AutofillType(ADDRESS_HOME_COUNTRY),
- base::ASCIIToUTF16(predicted_country_code), app_locale_,
- VerificationStatus::kObserved);
- import_metadata.did_complement_country = true;
- }
-
- // Construct the phone number. Reject the whole profile if the number is
- // invalid, unless |kAutofillRemoveInvalidPhoneNumberOnImport| is enabled.
- if (!combined_phone.IsEmpty()) {
- const std::string predicted_country_code_without_variation =
- GetPredictedCountryCode(candidate_profile, "", app_locale_, nullptr);
- // If kAutofillConsiderVariationCountryCodeForPhoneNumbers is enabled,
- // a consistent country code prediction for addresses and phone numbers is
- // used. Otherwise the variation service state is not considered for phone
- // numbers. This makes a difference, if the country code cannot be found
- // in the profile.
- // ParseNumber() implicity accepts both a country code and a locale. This
- // will be refactored with crbug/1296077. The parameter for
- // SetInfoWithVerificationStatus() has to be consistent with ParseNumber().
- // TODO(crbug.com/1295721): Cleanup when launched.
- const std::string& phone_number_region =
- predicted_country_code != predicted_country_code_without_variation &&
- base::FeatureList::IsEnabled(
- features::
- kAutofillConsiderVariationCountryCodeForPhoneNumbers)
- ? predicted_country_code
- : app_locale_;
- std::u16string constructed_number;
- if (!combined_phone.ParseNumber(candidate_profile, phone_number_region,
- &constructed_number) ||
- !candidate_profile.SetInfoWithVerificationStatus(
- AutofillType(PHONE_HOME_WHOLE_NUMBER), constructed_number,
- phone_number_region, VerificationStatus::kObserved)) {
- if (base::FeatureList::IsEnabled(
- features::kAutofillRemoveInvalidPhoneNumberOnImport)) {
- DCHECK(candidate_profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER).empty());
- import_metadata.did_remove_invalid_phone_number = true;
- } else {
- if (import_log_buffer) {
- *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
- << "Invalid phone number." << CTag{};
- }
- has_invalid_phone_number = true;
+ const std::string variation_country_code =
+ client_->GetVariationConfigCountryCode();
+ std::string predicted_country_code =
+ GetPredictedCountryCode(candidate_profile, variation_country_code,
+ app_locale_, import_log_buffer);
+
+ if (!SetPhoneNumber(candidate_profile, combined_phone,
+ predicted_country_code)) {
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillRemoveInvalidPhoneNumberOnImport)) {
+ candidate_profile.ClearFields({PHONE_HOME_WHOLE_NUMBER});
+ import_metadata.did_remove_invalid_phone_number = true;
+ } else {
+ has_invalid_phone_number = true;
+ if (import_log_buffer) {
+ *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed
+ << "Invalid phone number." << CTag{};
}
}
}
- // Filter unexpected values that are not shown in the settings.
- if (base::FeatureList::IsEnabled(
- features::kAutofillRemoveInaccessibleProfileValues)) {
- const ServerFieldTypeSet inaccessible_fields =
- candidate_profile.FindInaccessibleProfileValues(predicted_country_code);
- candidate_profile.ClearFields(inaccessible_fields);
- AutofillMetrics::LogRemovedSettingInaccessibleFields(
- !inaccessible_fields.empty());
- for (const ServerFieldType inaccessible_field : inaccessible_fields) {
- AutofillMetrics::LogRemovedSettingInaccessibleField(
- predicted_country_code, inaccessible_field);
- }
- }
+ // This is done prior to checking the validity of the profile, because multi-
+ // step import profile merging requires the profile to be finalized. Ideally
+ // we would return false here if it fails, but that breaks the metrics.
+ bool finalized_import = candidate_profile.FinalizeAfterImport();
- // Reject the profile if minimum address and validation requirements are not
- // met.
- bool is_invalid_learnable_profile =
+ // Reject the profile if the validation requirements are not met.
+ // |IsValidLearnableProfile()| goes first to collect metrics.
+ bool has_invalid_information =
!IsValidLearnableProfile(candidate_profile, predicted_country_code,
- app_locale_, import_log_buffer);
+ app_locale_, import_log_buffer) ||
+ has_multiple_distinct_email_addresses || has_invalid_field_types ||
+ has_invalid_country || has_invalid_phone_number;
+
+ // Profiles with valid information qualify for multi-step imports.
+ // This requires the profile to be finalized to apply the merging logic.
+ if (finalized_import && has_address_related_fields &&
+ !has_invalid_information) {
+ ProcessMultiStepImport(candidate_profile, import_metadata,
+ url::Origin::Create(form.source_url()));
+ // The predicted country code has possibly changed, if |candidate_profile|
+ // was merged with a profile containing country information.
+ predicted_country_code =
+ GetPredictedCountryCode(candidate_profile, variation_country_code,
+ app_locale_, /*import_log_buffer=*/nullptr);
+ }
+
+ // Only complement the country if no invalid country was entered in the form.
+ // For multi-step imports, |did_complement_country| might be set twice, but as
+ // the metric is only logged if it wasn't present before, this is fine.
+ import_metadata.did_complement_country =
+ !has_invalid_country &&
+ ComplementCountry(candidate_profile, predicted_country_code);
+
+ RemoveInaccessibleProfileValues(candidate_profile, predicted_country_code);
// Do not import a profile if any of the requirements is violated.
+ // |IsMinimumAddress()| goes first to collect metrics.
bool all_fulfilled =
- !(has_multiple_distinct_email_addresses || has_invalid_field_types ||
- has_invalid_country || has_invalid_phone_number ||
- is_invalid_learnable_profile);
+ IsMinimumAddress(candidate_profile, predicted_country_code, app_locale_,
+ import_log_buffer, /*collect_metrics=*/true) &&
+ !has_invalid_information;
// Collect metrics regarding the requirements for an address profile import.
AutofillMetrics::LogAddressFormImportRequirementMetric(
@@ -783,10 +845,7 @@ bool FormDataImporter::ImportAddressProfileForSection(
// If the profile does not fulfill import requirements but contains the
// structured address or name information, it is eligible for silently
// updating the existing profiles.
- if (!all_fulfilled && !candidate_has_structured_data)
- return false;
-
- if (!candidate_profile.FinalizeAfterImport())
+ if (!finalized_import || (!all_fulfilled && !candidate_has_structured_data))
return false;
// At this stage, the saving of the profile can only be omitted by the
@@ -871,7 +930,7 @@ bool FormDataImporter::ProcessCreditCardImportCandidate(
if (imported_credit_card &&
imported_credit_card->virtual_card_enrollment_state() ==
CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE) {
- virtual_card_enrollment_manager_->OfferVirtualCardEnroll(
+ virtual_card_enrollment_manager_->InitVirtualCardEnroll(
*imported_credit_card, VirtualCardEnrollmentSource::kDownstream);
return true;
}
@@ -1095,7 +1154,7 @@ CreditCard FormDataImporter::ExtractCreditCardFromForm(
types_seen.insert(server_field_type);
}
// If |field| is an HTML5 month input, handle it as a special case.
- if (base::LowerCaseEqualsASCII(field->form_control_type, "month")) {
+ if (base::EqualsCaseInsensitiveASCII(field->form_control_type, "month")) {
DCHECK_EQ(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, server_field_type);
candidate_credit_card.SetInfoForMonthInputType(value);
continue;
@@ -1158,4 +1217,119 @@ bool FormDataImporter::ShouldOfferUploadCardOrLocalCardSave(
return true;
}
+void FormDataImporter::ProcessMultiStepImport(
+ AutofillProfile& profile,
+ ProfileImportMetadata& import_metadata,
+ const url::Origin& origin) {
+ if (!base::FeatureList::IsEnabled(
+ features::kAutofillEnableMultiStepImports)) {
+ return;
+ }
+
+ RemoveOutdatedMultiStepCandidates(origin);
+ bool has_min_address_requirements =
+ MergeProfileWithMultiStepCandidates(profile, import_metadata, origin);
+
+ if (!has_min_address_requirements ||
+ features::kAutofillEnableMultiStepImportComplements.Get()) {
+ // Add |profile| as a |multistep_candidate|. This happens for incomplete
+ // profiles, which can then be complemented in later steps. When
+ // |kAutofillEnableMultiStepImportComplements| is enabled, complete profiles
+ // are stored too, which enables updating them in later steps.
+ // In the latter case, Autofill tries to import the `profile`. This logs
+ // metrics depending on `import_metadata`. To prevent double counting,
+ // an we store an empty `ProfileImportMetadata` object in this case.
+ multistep_candidates_.push_front(MultiStepFormProfileCandidate{
+ .profile = profile,
+ .import_metadata = has_min_address_requirements
+ ? ProfileImportMetadata()
+ : import_metadata,
+ .timestamp = AutofillClock::Now()});
+ multistep_candidates_origin_ = origin;
+ }
+}
+
+void FormDataImporter::RemoveOutdatedMultiStepCandidates(
+ const url::Origin& origin) {
+ // All |multistep_candidates| share |multistep_candidates_origin|.
+ if (multistep_candidates_origin_.has_value() &&
+ multistep_candidates_origin_.value() != origin) {
+ multistep_candidates_.clear();
+ } else {
+ // Remove candidates that reached their TTL.
+ const base::TimeDelta ttl =
+ features::kAutofillMultiStepImportCandidateTTL.Get();
+ const base::Time now = AutofillClock::Now();
+ while (!multistep_candidates_.empty() &&
+ now - multistep_candidates_.back().timestamp > ttl) {
+ multistep_candidates_.pop_back();
+ }
+ }
+ if (multistep_candidates_.empty()) {
+ multistep_candidates_origin_.reset();
+ }
+}
+
+bool FormDataImporter::MergeProfileWithMultiStepCandidates(
+ AutofillProfile& profile,
+ ProfileImportMetadata& import_metadata,
+ const url::Origin& origin) {
+ // Greedily merge with a prefix of |multistep_candidates|.
+ AutofillProfileComparator comparator(app_locale_);
+ std::deque<MultiStepFormProfileCandidate>::iterator merge_candidate =
+ multistep_candidates_.begin();
+ AutofillProfile completed_profile = profile;
+ ProfileImportMetadata completed_metadata = import_metadata;
+ // Country completion has not happened yet, so this field can be ignored.
+ DCHECK(!completed_metadata.did_remove_invalid_phone_number);
+ while (
+ merge_candidate != multistep_candidates_.end() &&
+ comparator.AreMergeable(completed_profile, merge_candidate->profile) &&
+ completed_profile.MergeDataFrom(merge_candidate->profile, app_locale_)) {
+ // ProfileImportMetadata is only relevant for metrics. If the phone number
+ // was removed from a partial profile, we still want that removal to appear
+ // in the metrics, because it would have hindered that partial profile from
+ // import and merging.
+ completed_metadata.did_remove_invalid_phone_number |=
+ merge_candidate->import_metadata.did_remove_invalid_phone_number;
+ merge_candidate++;
+ }
+
+ // The minimum address requirements depend on the country, which has possibly
+ // changed as a result of the merge.
+ if (IsMinimumAddress(
+ completed_profile,
+ GetPredictedCountryCode(completed_profile,
+ client_->GetVariationConfigCountryCode(),
+ app_locale_, /*import_log_buffer=*/nullptr),
+ app_locale_,
+ /*import_log_buffer=*/nullptr, /*collect_metrics=*/false)) {
+ profile = std::move(completed_profile);
+ import_metadata = std::move(completed_metadata);
+ multistep_candidates_.clear();
+ return true;
+ } else {
+ // Remove all profiles that couldn't be merged.
+ multistep_candidates_.erase(merge_candidate, multistep_candidates_.end());
+ return false;
+ }
+}
+
+void FormDataImporter::OnBrowsingHistoryCleared(
+ const history::DeletionInfo& deletion_info) {
+ // Delete all multi-step import candidates when:
+ // - The entire browsing history is cleared, or
+ // - At least one URL from the same origin as `multistep_candidates_origin_`
+ // is deleted.
+ if (deletion_info.IsAllHistory() ||
+ (multistep_candidates_origin_.has_value() &&
+ base::Contains(deletion_info.deleted_rows(),
+ *multistep_candidates_origin_,
+ [](const history::URLRow& url_row) {
+ return url::Origin::Create(url_row.url());
+ }))) {
+ ClearMultiStepImportCandidates();
+ }
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_data_importer.h b/chromium/components/autofill/core/browser/form_data_importer.h
index cbc8fa00d45..70abf5e3f42 100644
--- a/chromium/components/autofill/core/browser/form_data_importer.h
+++ b/chromium/components/autofill/core/browser/form_data_importer.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_DATA_IMPORTER_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_DATA_IMPORTER_H_
+#include <deque>
#include <map>
#include <memory>
#include <string>
@@ -12,6 +13,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_profile_import_process.h"
@@ -33,7 +35,7 @@ class AddressProfileSaveManager;
// Manages logic for importing address profiles and credit card information from
// web forms into the user's Autofill profile via the PersonalDataManager.
// Owned by BrowserAutofillManager.
-class FormDataImporter {
+class FormDataImporter : public PersonalDataManagerObserver {
public:
// Record type of the credit card imported from the form, if one exists.
enum ImportedCreditCardRecordType {
@@ -57,7 +59,7 @@ class FormDataImporter {
FormDataImporter(const FormDataImporter&) = delete;
FormDataImporter& operator=(const FormDataImporter&) = delete;
- virtual ~FormDataImporter();
+ ~FormDataImporter() override;
// Imports the form data, submitted by the user, into
// |personal_data_manager_|. If a new credit card was detected and
@@ -100,10 +102,19 @@ class FormDataImporter {
}
#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
- raw_ptr<VirtualCardEnrollmentManager> GetVirtualCardEnrollmentManager() {
+ VirtualCardEnrollmentManager* GetVirtualCardEnrollmentManager() {
return virtual_card_enrollment_manager_.get();
}
+ void ClearMultiStepImportCandidates() {
+ multistep_candidates_.clear();
+ multistep_candidates_origin_.reset();
+ }
+
+ // PersonalDataManagerObserver
+ void OnBrowsingHistoryCleared(
+ const history::DeletionInfo& deletion_info) override;
+
protected:
// Exposed for testing.
void set_credit_card_save_manager(
@@ -131,10 +142,6 @@ class FormDataImporter {
// Metadata about the import, used for metric collection in
// ProfileImportProcess after the user's decision.
ProfileImportMetadata import_metadata;
- AddressProfileImportCandidate(AddressProfileImportCandidate&& other) =
- default;
- AddressProfileImportCandidate& operator=(
- AddressProfileImportCandidate&& other) = default;
};
// Scans the given |form| for importable Autofill data. If the form includes
@@ -232,6 +239,56 @@ class FormDataImporter {
const CreditCard* imported_credit_card,
bool is_credit_card_upload_enabled);
+ // If `kAutofillComplementCountryCodeOnImport` is enabled and the `profile`'s
+ // country is not empty, complements it with `predicted_country_code`. To give
+ // users the opportunity to edit, this is only done with explicit save prompts
+ // enabled.
+ // Returns true if the country was complemented.
+ bool ComplementCountry(AutofillProfile& profile,
+ const std::string& predicted_country_code);
+
+ // Sets the `profile`'s PHONE_HOME_WHOLE_NUMBER to the `combined_phone`, if
+ // possible. Deduces the region based on `predicted_country_code`.
+ // Returns false if the provided `combined_phone` is invalid.
+ // TODO(crbug.com/1297032): Remove `predicted_country_code` when launched.
+ bool SetPhoneNumber(AutofillProfile& profile,
+ PhoneNumber::PhoneCombineHelper& combined_phone,
+ const std::string& predicted_country_code);
+
+ // Clears all setting-inaccessible values from `profile` if
+ // `kAutofillRemoveInaccessibleProfileValues` is enabled.
+ // TODO(crbug.com/1297032): Remove `predicted_country_code` when launched.
+ void RemoveInaccessibleProfileValues(
+ AutofillProfile& profile,
+ const std::string& predicted_country_code);
+
+ // Removes updated multi-step candidates, merges |profile| with multi-step
+ // candidates and potentially stores it as a multi-step candidate itself.
+ // |profile| and |import_metadata| are updated accordingly, if the profile can
+ // be merged. See |MergeProfileWithMultiStepCandidates()| for details.
+ // Only applicable when |kAutofillEnableMultiStepImports| is enabled.
+ void ProcessMultiStepImport(AutofillProfile& profile,
+ ProfileImportMetadata& import_metadata,
+ const url::Origin& origin);
+
+ // Removes any MultiStepFormProfileCandidate from |multistep_candidates_| that
+ // reached their TTL or have a different |origin|.
+ void RemoveOutdatedMultiStepCandidates(const url::Origin& origin);
+
+ // Merges a given |profile| stepwise with |multistep_candidates_| to
+ // complete it. |profile| is assumed to contain no invalid information.
+ // Returns true if the resulting profile satisfies the minimum address
+ // requirements. |profile| and |import_metadata| are updated in this case with
+ // the result of merging all relevant candidates.
+ // Returns false otherwise and leaves |profile| and |import_metadata|
+ // unchanged.
+ // Any merged or colliding |multistep_candidates_| are cleared.
+ // |origin|: The origin of the form where |profile| was imported from.
+ bool MergeProfileWithMultiStepCandidates(
+ AutofillProfile& profile,
+ ProfileImportMetadata& import_metadata,
+ const url::Origin& origin);
+
// Whether a dynamic change form is imported.
bool from_dynamic_change_form_ = false;
@@ -276,6 +333,23 @@ class FormDataImporter {
std::unique_ptr<VirtualCardEnrollmentManager>
virtual_card_enrollment_manager_;
+ // Represents a submitted form, stored to be considered as a merge candidate
+ // for other candidate profiles in future submits in a multi-step import flow.
+ struct MultiStepFormProfileCandidate {
+ // The import candidate.
+ AutofillProfile profile;
+ // Metadata about how |profile| was constructed.
+ ProfileImportMetadata import_metadata;
+ // Timestamp when the submit happened.
+ base::Time timestamp;
+ };
+ // Current multi-step import candidates, in increasing order of their
+ // |timestamp|.
+ std::deque<MultiStepFormProfileCandidate> multistep_candidates_;
+ // All |multistep_candidates_| share the same origin. Has a value iff
+ // |multistep_candidates_| is not empty.
+ absl::optional<url::Origin> multistep_candidates_origin_;
+
friend class AutofillMergeTest;
friend class FormDataImporterTest;
friend class FormDataImporterTestBase;
@@ -284,6 +358,10 @@ class FormDataImporter {
friend class SaveCardInfobarEGTestHelper;
friend class ::SaveCardOfferObserver;
FRIEND_TEST_ALL_PREFIXES(AutofillMergeTest, MergeProfiles);
+ FRIEND_TEST_ALL_PREFIXES(FormDataImporterNonParameterizedTest,
+ ProcessCreditCardImportCandidate_EmptyCreditCard);
+ FRIEND_TEST_ALL_PREFIXES(FormDataImporterNonParameterizedTest,
+ ShouldOfferUploadCardOrLocalCardSave);
FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
AllowDuplicateMaskedServerCardIfFlagEnabled);
FRIEND_TEST_ALL_PREFIXES(
diff --git a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc
index 210ae7e40cf..feeaed88ff5 100644
--- a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -19,7 +19,6 @@
#include "base/feature_list.h"
#include "base/guid.h"
#include "base/memory/raw_ptr.h"
-#include "base/ranges/ranges.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
@@ -38,6 +37,7 @@
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/personal_data_manager_observer.h"
#include "components/autofill/core/browser/test_autofill_client.h"
+#include "components/autofill/core/browser/test_autofill_clock.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/common/autofill_constants.h"
@@ -50,6 +50,7 @@
#include "components/autofill/core/common/form_field_data.h"
#include "components/os_crypt/os_crypt_mocker.h"
#include "components/prefs/pref_service.h"
+#include "components/sync/driver/test_sync_service.h"
#include "components/webdata/common/web_data_service_base.h"
#include "components/webdata/common/web_database_service.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -215,6 +216,28 @@ GetDefaultProfileTypeValuePairs() {
};
}
+// Same as |GetDefaultProfileTypeValuePairs()|, but split into two parts to test
+// multi-step imports. No part by itself satisfies the import requirements.
+// |part| specifies the requested half and can be either 1 or 2.
+std::vector<std::pair<ServerFieldType, std::string>>
+GetSplitDefaultProfileTypeValuePairs(int part) {
+ DCHECK(part == 1 || part == 2);
+ if (part == 1) {
+ return {
+ {NAME_FIRST, kDefaultFirstName}, {NAME_LAST, kDefaultLastName},
+ {EMAIL_ADDRESS, kDefaultMail}, {ADDRESS_HOME_CITY, kDefaultCity},
+ {ADDRESS_HOME_STATE, kDefaultState},
+ };
+ } else {
+ return {
+ {PHONE_HOME_WHOLE_NUMBER, kDefaultPhone},
+ {ADDRESS_HOME_LINE1, kDefaultAddressLine1},
+ {ADDRESS_HOME_DEPENDENT_LOCALITY, kDefaultDependentLocality},
+ {ADDRESS_HOME_ZIP, kDefaultZip},
+ };
+ }
+}
+
// Same as |GetDefaultProfileTypeValuePairs()| but with ADDRESS_HOME_COUNTRY
// set to |country|.
std::vector<std::pair<ServerFieldType, std::string>>
@@ -281,12 +304,20 @@ AutofillProfile ConstructThirdProfile() {
// Returns a form with the default profile. The AutofillProfile that is imported
// from this form should be similar to the profile create by calling
-// |ConstructDefaultProfile()|
+// |ConstructDefaultProfile()|.
std::unique_ptr<FormStructure> ConstructDefaultProfileFormStructure() {
return ConstructFormStructureFromTypeValuePairs(
GetDefaultProfileTypeValuePairs());
}
+// Same as |ConstructDefaultFormStructure()| but split into two parts to test
+// multi-step imports (see |GetSplitDefaultProfileTypeValuePairs()|).
+std::unique_ptr<FormStructure> ConstructSplitDefaultProfileFormStructure(
+ int part) {
+ return ConstructFormStructureFromTypeValuePairs(
+ GetSplitDefaultProfileTypeValuePairs(part));
+}
+
// Same as |ConstructDefaultFormStructure()| but with |country|.
std::unique_ptr<FormStructure> ConstructDefaultProfileFormStructureWithCountry(
const std::string& country) {
@@ -312,6 +343,13 @@ FormData ConstructDefaultFormData() {
return ConstructFormDateFromTypeValuePairs(GetDefaultProfileTypeValuePairs());
}
+// Same as |ConstructDefaultFormData()| but split into two parts to test multi-
+// step imports (see |GetSplitDefaultProfileTypeValuePairs()|).
+FormData ConstructSplitDefaultFormData(int part) {
+ return ConstructFormDateFromTypeValuePairs(
+ GetSplitDefaultProfileTypeValuePairs(part));
+}
+
ACTION_P(QuitMessageLoop, loop) {
loop->Quit();
}
@@ -361,6 +399,11 @@ class FormDataImporterTestBase {
FormDataImporterTestBase() : autofill_table_(nullptr) {}
void ResetPersonalDataManager(UserMode user_mode) {
+ // Before invalidating the `personal_data_manager_`, the
+ // `form_data_importer` needs to be reset, because it stores a weak
+ // reference to `personal_data_manager_` that otherwise points to garbage.
+ form_data_importer_.reset();
+
personal_data_manager_ = std::make_unique<PersonalDataManager>("en", "US");
personal_data_manager_->set_auto_accept_address_imports_for_testing(true);
personal_data_manager_->Init(
@@ -377,6 +420,40 @@ class FormDataImporterTestBase {
personal_data_manager_->OnSyncServiceInitialized(nullptr);
WaitForOnPersonalDataChanged();
+
+ // Reconstruct the `form_data_importer_` with the new
+ // `personal_data_manager_`.
+ form_data_importer_ =
+ std::make_unique<FormDataImporter>(autofill_client_.get(),
+ /*payments::PaymentsClient=*/nullptr,
+ personal_data_manager_.get(), "en");
+ }
+
+ void SetUpHelper() {
+ prefs_ = test::PrefServiceForTesting();
+ base::FilePath path(WebDatabase::kInMemoryPath);
+ web_database_ =
+ new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(),
+ base::ThreadTaskRunnerHandle::Get());
+
+ // Hacky: hold onto a pointer but pass ownership.
+ autofill_table_ = new AutofillTable;
+ web_database_->AddTable(std::unique_ptr<WebDatabaseTable>(autofill_table_));
+ web_database_->LoadDatabase();
+ autofill_database_service_ = new AutofillWebDataService(
+ web_database_, base::ThreadTaskRunnerHandle::Get(),
+ base::ThreadTaskRunnerHandle::Get());
+ autofill_database_service_->Init(base::NullCallback());
+
+ autofill_client_ = std::make_unique<TestAutofillClient>();
+
+ test::DisableSystemServices(prefs_.get());
+ // This will also initialize the `form_data_importer_`.
+ ResetPersonalDataManager(USER_MODE_NORMAL);
+
+ // Reset the deduping pref to its default value.
+ personal_data_manager_->pref_service_->SetInteger(
+ prefs::kAutofillLastVersionDeduped, 0);
}
// Helper method that will add credit card fields in |form|, according to the
@@ -573,42 +650,7 @@ class FormDataImporterTest
private:
void SetUp() override {
InitializeFeatures();
- OSCryptMocker::SetUp();
- prefs_ = test::PrefServiceForTesting();
- base::FilePath path(WebDatabase::kInMemoryPath);
- web_database_ =
- new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get());
-
- // Hacky: hold onto a pointer but pass ownership.
- autofill_table_ = new AutofillTable;
- web_database_->AddTable(std::unique_ptr<WebDatabaseTable>(autofill_table_));
- web_database_->LoadDatabase();
- autofill_database_service_ = new AutofillWebDataService(
- web_database_, base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get());
- autofill_database_service_->Init(base::NullCallback());
-
- autofill_client_ = std::make_unique<TestAutofillClient>();
-
- test::DisableSystemServices(prefs_.get());
- ResetPersonalDataManager(USER_MODE_NORMAL);
-
- form_data_importer_ =
- std::make_unique<FormDataImporter>(autofill_client_.get(),
- /*payments::PaymentsClient=*/nullptr,
- personal_data_manager_.get(), "en");
-
- // Reset the deduping pref to its default value.
- personal_data_manager_->pref_service_->SetInteger(
- prefs::kAutofillLastVersionDeduped, 0);
- }
-
- void TearDown() override {
- // Order of destruction is important as BrowserAutofillManager relies on
- // PersonalDataManager to be around when it gets destroyed.
- test::ReenableSystemServices();
- OSCryptMocker::TearDown();
+ SetUpHelper();
}
void InitializeFeatures() {
@@ -698,11 +740,10 @@ TEST_P(FormDataImporterTest, ComplementCountry) {
TEST_P(FormDataImporterTest, InvalidPhoneNumber) {
std::vector<std::pair<ServerFieldType, std::string>>
profile_with_invalid_phone_number = GetDefaultProfileTypeValuePairs();
- auto phone_number_it =
- base::ranges::find(profile_with_invalid_phone_number,
- std::pair<ServerFieldType, std::string>(
- PHONE_HOME_WHOLE_NUMBER, kDefaultPhone));
- phone_number_it->second = "invalid";
+ const int phone_number_index = 3;
+ ASSERT_EQ(profile_with_invalid_phone_number[phone_number_index].first,
+ PHONE_HOME_WHOLE_NUMBER);
+ profile_with_invalid_phone_number[phone_number_index].second = "invalid";
std::unique_ptr<FormStructure> form_structure =
ConstructFormStructureFromTypeValuePairs(
profile_with_invalid_phone_number);
@@ -724,13 +765,71 @@ TEST_P(FormDataImporterTest, InvalidPhoneNumber) {
remove_invalid_phone_number_feature.InitAndEnableFeature(
features::kAutofillRemoveInvalidPhoneNumberOnImport);
- profile_with_invalid_phone_number.erase(phone_number_it);
+ profile_with_invalid_phone_number.erase(
+ profile_with_invalid_phone_number.begin() + phone_number_index);
ImportAddressProfilesAndVerifyExpectation(
*form_structure, {ConstructProfileFromTypeValuePairs(
profile_with_invalid_phone_number)});
}
}
+TEST_P(FormDataImporterTest, PhoneNumberRegionMetrics) {
+ // This test is only applicable if the feature is enabled.
+ if (!ConsiderVariationCountryCodeForPhoneNumbers())
+ return;
+
+ auto ImportWithPhoneNumber =
+ [this](const std::string& number,
+ AutofillMetrics::PhoneNumberImportParsingResult expected_result) {
+ // Remove existing profiles, to prevent an update instead of an import.
+ personal_data_manager_->ClearAllLocalData();
+
+ std::vector<std::pair<ServerFieldType, std::string>>
+ profile_with_invalid_phone_number =
+ GetDefaultProfileTypeValuePairs();
+ ASSERT_EQ(profile_with_invalid_phone_number[3].first,
+ PHONE_HOME_WHOLE_NUMBER);
+ profile_with_invalid_phone_number[3].second = number;
+ std::unique_ptr<FormStructure> form_structure =
+ ConstructFormStructureFromTypeValuePairs(
+ profile_with_invalid_phone_number);
+
+ // Profiles with invalid phone number are rejected.
+ bool expect_success =
+ expected_result == AutofillMetrics::PhoneNumberImportParsingResult::
+ PARSED_WITH_VARIATION_COUNTRY_CODE ||
+ expected_result == AutofillMetrics::PhoneNumberImportParsingResult::
+ PARSED_WITH_BOTH;
+ base::HistogramTester histogram_tester;
+ ImportAddressProfiles(expect_success, *form_structure);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "Autofill.ProfileImport.PhoneNumberParsingResult"),
+ testing::UnorderedElementsAre(base::Bucket(expected_result, 1)));
+ };
+
+ // `form_data_importer_` is initialized with app locale set to "en".
+ autofill_client_->SetVariationConfigCountryCode("DE");
+
+ ImportWithPhoneNumber(
+ "invalid", AutofillMetrics::PhoneNumberImportParsingResult::CANNOT_PARSE);
+
+ // The German phone number validation is very lenient and accepts all US
+ // numbers we could find. Thus no test for
+ // AutofillMetrics::PhoneNumberImportParsingResult::PARSED_WITH_APP_LOCALE.
+
+ // A German phone number only parses with the German variation country code.
+ ImportWithPhoneNumber("01578 7912345",
+ AutofillMetrics::PhoneNumberImportParsingResult::
+ PARSED_WITH_VARIATION_COUNTRY_CODE);
+
+ // For phone numbers in international format the region is ignored. So this
+ // Austrian phone number parses as both.
+ ImportWithPhoneNumber(
+ "+43 650 3847567",
+ AutofillMetrics::PhoneNumberImportParsingResult::PARSED_WITH_BOTH);
+}
+
// ImportAddressProfiles tests.
TEST_P(FormDataImporterTest, ImportStructuredNameProfile) {
base::test::ScopedFeatureList structured_addresses_feature;
@@ -1439,15 +1538,16 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_SameProfileWithConflict) {
ConstructFormStructureFromTypeValuePairs(conflicting_type_value_pairs);
std::vector<std::pair<ServerFieldType, std::string>>
- resulting_type_value_pairs{{NAME_FULL, kDefaultFullName},
- {ADDRESS_HOME_LINE1, kDefaultAddressLine1},
- {ADDRESS_HOME_CITY, kDefaultCity},
- {ADDRESS_HOME_STATE, kDefaultState},
- {ADDRESS_HOME_ZIP, kDefaultZip},
- // The phone number is spelled differently.
- {PHONE_HOME_WHOLE_NUMBER, kDefaultPhone},
- // Country information is added.
- {ADDRESS_HOME_COUNTRY, "US"}};
+ resulting_type_value_pairs{
+ {NAME_FULL, kDefaultFullName},
+ {ADDRESS_HOME_LINE1, kDefaultAddressLine1},
+ {ADDRESS_HOME_CITY, kDefaultCity},
+ {ADDRESS_HOME_STATE, kDefaultState},
+ {ADDRESS_HOME_ZIP, kDefaultZip},
+ // The phone number remains in domestic format.
+ {PHONE_HOME_WHOLE_NUMBER, kDefaultPhoneDomesticFormatting},
+ // Country information is added.
+ {ADDRESS_HOME_COUNTRY, "US"}};
// Verify that importing the conflicting profile will result in an update of
// the existing profile rather than creating a new one.
@@ -4225,10 +4325,114 @@ TEST_P(FormDataImporterTest, RemoveInaccessibleProfileValuesMetrics) {
"Autofill.ProfileImport.InaccessibleFieldsRemoved.";
histogram_tester.ExpectUniqueSample(metric + "Total", true, 1);
histogram_tester.ExpectUniqueSample(
- metric + "DE",
+ metric + "ByFieldType",
AutofillMetrics::SettingsVisibleFieldTypeForMetrics::kState, 1);
}
+// Tests a 2-page multi-step import.
+TEST_P(FormDataImporterTest, MultiStepImport) {
+ base::test::ScopedFeatureList multistep_import_feature;
+ multistep_import_feature.InitAndEnableFeature(
+ features::kAutofillEnableMultiStepImports);
+
+ std::unique_ptr<FormStructure> form_structure =
+ ConstructSplitDefaultProfileFormStructure(/*part=*/1);
+ ImportAddressProfilesAndVerifyExpectation(*form_structure, {});
+
+ form_structure = ConstructSplitDefaultProfileFormStructure(/*part=*/2);
+ ImportAddressProfileAndVerifyImportOfDefaultProfile(*form_structure);
+}
+
+// Tests that imported profiles remain multi-step candidates if
+// |kAutofillEnableMultiStepImportComplements| is true, which enables
+// complementing the profile with additional information on further pages.
+TEST_P(FormDataImporterTest, MultiStepImportComplement) {
+ base::test::ScopedFeatureList multistep_import_with_complement_feature;
+ multistep_import_with_complement_feature.InitAndEnableFeatureWithParameters(
+ features::kAutofillEnableMultiStepImports,
+ {{features::kAutofillEnableMultiStepImportComplements.name, "true"}});
+
+ // Import the default profile without an email address.
+ std::vector<std::pair<ServerFieldType, std::string>> type_value_pairs =
+ GetDefaultProfileTypeValuePairs();
+ EXPECT_EQ(type_value_pairs[2].first, EMAIL_ADDRESS);
+ type_value_pairs.erase(type_value_pairs.begin() + 2);
+
+ std::unique_ptr<FormStructure> form_structure =
+ ConstructFormStructureFromTypeValuePairs(type_value_pairs);
+ ImportAddressProfilesAndVerifyExpectation(
+ *form_structure, {ConstructProfileFromTypeValuePairs(type_value_pairs)});
+
+ // Import the email address in a separate form. Without multi-step imports,
+ // this information cannot be associated to a profile. The resulting profile
+ // is the default one.
+ // The autocomplete attribute is set manually, because for small forms (number
+ // of fields < kMinRequiredFieldsForHeuristics), no heuristics are used.
+ FormData form =
+ ConstructFormDateFromTypeValuePairs({{EMAIL_ADDRESS, kDefaultMail}});
+ form.fields[0].autocomplete_attribute = "email";
+ form_structure = ConstructFormStructureFromFormData(form);
+ ImportAddressProfileAndVerifyImportOfDefaultProfile(*form_structure);
+}
+
+// Tests that multi-step candidate profiles from different origins are not
+// merged.
+TEST_P(FormDataImporterTest, MultiStepImportDifferentOrigin) {
+ base::test::ScopedFeatureList multistep_import_feature;
+ multistep_import_feature.InitAndEnableFeature(
+ features::kAutofillEnableMultiStepImports);
+
+ FormData form = ConstructSplitDefaultFormData(/*part=*/1);
+ form.url = GURL("https://wwww.foo.com");
+ std::unique_ptr<FormStructure> form_structure =
+ ConstructFormStructureFromFormData(form);
+ ImportAddressProfilesAndVerifyExpectation(*form_structure, {});
+
+ form = ConstructSplitDefaultFormData(/*part=*/2);
+ form.url = GURL("https://wwww.bar.com");
+ form_structure = ConstructFormStructureFromFormData(form);
+ ImportAddressProfilesAndVerifyExpectation(*form_structure, {});
+}
+
+// Tests that multi-step candidates profiles are invalidated after some TTL.
+TEST_P(FormDataImporterTest, MultiStepImportTTL) {
+ base::test::ScopedFeatureList multistep_import_feature_set_ttl;
+ multistep_import_feature_set_ttl.InitAndEnableFeatureWithParameters(
+ features::kAutofillEnableMultiStepImports,
+ {{features::kAutofillMultiStepImportCandidateTTL.name, "30m"}});
+ TestAutofillClock test_clock;
+
+ std::unique_ptr<FormStructure> form_structure =
+ ConstructSplitDefaultProfileFormStructure(/*part=*/1);
+ ImportAddressProfilesAndVerifyExpectation(*form_structure, {});
+
+ test_clock.Advance(base::Minutes(31));
+
+ form_structure = ConstructSplitDefaultProfileFormStructure(/*part=*/2);
+ ImportAddressProfilesAndVerifyExpectation(*form_structure, {});
+}
+
+// Tests that multi-step candidates profiles are cleared if the browsing history
+// is deleted.
+TEST_P(FormDataImporterTest, MultiStepImportDeleteOnBrowsingHistoryCleared) {
+ base::test::ScopedFeatureList multistep_import_feature;
+ multistep_import_feature.InitAndEnableFeature(
+ features::kAutofillEnableMultiStepImports);
+
+ std::unique_ptr<FormStructure> form_structure =
+ ConstructSplitDefaultProfileFormStructure(/*part=*/1);
+ ImportAddressProfilesAndVerifyExpectation(*form_structure, {});
+
+ personal_data_manager_->OnURLsDeleted(
+ /*history_service=*/nullptr,
+ history::DeletionInfo::ForUrls(
+ {history::URLRow(form_structure->source_url())},
+ /*favicon_urls=*/{}));
+
+ form_structure = ConstructSplitDefaultProfileFormStructure(/*part=*/2);
+ ImportAddressProfilesAndVerifyExpectation(*form_structure, {});
+}
+
// Runs the suite with the feature |kAutofillEnableSupportForApartmentNumbers|,
// |kAutofillEnableDependentLocalityParsing| and
// |kAutofillConsiderVariationCountryCodeForPhoneNumbers| enabled and disabled.
@@ -4240,4 +4444,72 @@ INSTANTIATE_TEST_SUITE_P(,
testing::Bool(),
testing::Bool()));
+class FormDataImporterNonParameterizedTest : public FormDataImporterTestBase,
+ public testing::Test {
+ private:
+ void SetUp() override { SetUpHelper(); }
+};
+
+TEST_F(FormDataImporterNonParameterizedTest,
+ ProcessCreditCardImportCandidate_EmptyCreditCard) {
+ std::unique_ptr<CreditCard> imported_credit_card;
+ FormData form;
+ AddFullCreditCardForm(&form, "Clyde Barrow", "378282246310005", "04", "2999");
+
+ // |form_data_importer_|'s |imported_credit_card_record_type_| is set to
+ // LOCAL_CARD because we need to make sure we do not return early in the
+ // NEW_CARD case, and LOCAL_CARD with upstream enabled but empty
+ // |imported_credit_card| is the most likely scenario for a crash.
+ form_data_importer_->imported_credit_card_record_type_ =
+ FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD;
+
+ // We need a sync service so that
+ // LocalCardMigrationManager::ShouldOfferLocalCardMigration() does not crash.
+ syncer::TestSyncService sync_service;
+ personal_data_manager_->OnSyncServiceInitialized(&sync_service);
+
+ EXPECT_FALSE(form_data_importer_->ProcessCreditCardImportCandidate(
+ FormStructure(form), std::move(imported_credit_card),
+ /*detected_upi_id=*/"",
+ /*credit_card_autofill_enabled=*/true,
+ /*is_credit_card_upstream_enabled=*/true));
+ personal_data_manager_->OnSyncServiceInitialized(nullptr);
+}
+
+TEST_F(FormDataImporterNonParameterizedTest,
+ ShouldOfferUploadCardOrLocalCardSave) {
+ // Should not offer save for null cards.
+ std::unique_ptr<CreditCard> imported_credit_card;
+ EXPECT_FALSE(form_data_importer_->ShouldOfferUploadCardOrLocalCardSave(
+ imported_credit_card.get(),
+ /*is_credit_card_upload_enabled=*/false));
+
+ imported_credit_card = std::make_unique<CreditCard>(test::GetCreditCard());
+
+ // Should not offer save for local cards if upstream is not enabled.
+ form_data_importer_->imported_credit_card_record_type_ =
+ FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD;
+ EXPECT_FALSE(form_data_importer_->ShouldOfferUploadCardOrLocalCardSave(
+ imported_credit_card.get(), /*is_credit_card_upload_enabled=*/false));
+
+ // Should offer save for local cards if upstream is enabled.
+ EXPECT_TRUE(form_data_importer_->ShouldOfferUploadCardOrLocalCardSave(
+ imported_credit_card.get(), /*is_credit_card_upload_enabled=*/true));
+
+ // Should not offer save for server cards.
+ form_data_importer_->imported_credit_card_record_type_ =
+ FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD;
+ EXPECT_FALSE(form_data_importer_->ShouldOfferUploadCardOrLocalCardSave(
+ imported_credit_card.get(), /*is_credit_card_upload_enabled=*/true));
+
+ // Should always offer save for new cards; upload save if it is enabled, local
+ // save otherwise.
+ form_data_importer_->imported_credit_card_record_type_ =
+ FormDataImporter::ImportedCreditCardRecordType::NEW_CARD;
+ EXPECT_TRUE(form_data_importer_->ShouldOfferUploadCardOrLocalCardSave(
+ imported_credit_card.get(), /*is_credit_card_upload_enabled=*/true));
+ EXPECT_TRUE(form_data_importer_->ShouldOfferUploadCardOrLocalCardSave(
+ imported_credit_card.get(), /*is_credit_card_upload_enabled=*/false));
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_parsing/address_field.cc b/chromium/components/autofill/core/browser/form_parsing/address_field.cc
index 883012e2074..32cacb9bace 100644
--- a/chromium/components/autofill/core/browser/form_parsing/address_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/address_field.cc
@@ -88,6 +88,7 @@ constexpr MatchParams kStateMatchType =
std::unique_ptr<FormField> AddressField::Parse(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
if (scanner->IsEnd())
return nullptr;
@@ -97,19 +98,19 @@ std::unique_ptr<FormField> AddressField::Parse(
size_t saved_cursor = scanner->SaveCursor();
base::span<const MatchPatternRef> email_patterns =
- GetMatchPatterns("EMAIL_ADDRESS", page_language);
+ GetMatchPatterns("EMAIL_ADDRESS", page_language, pattern_source);
base::span<const MatchPatternRef> address_patterns =
- GetMatchPatterns("ADDRESS_LOOKUP", page_language);
+ GetMatchPatterns("ADDRESS_LOOKUP", page_language, pattern_source);
base::span<const MatchPatternRef> address_ignore_patterns =
- GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language);
+ GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
base::span<const MatchPatternRef> attention_ignore_patterns =
- GetMatchPatterns("ATTENTION_IGNORED", page_language);
+ GetMatchPatterns("ATTENTION_IGNORED", page_language, pattern_source);
base::span<const MatchPatternRef> region_ignore_patterns =
- GetMatchPatterns("REGION_IGNORED", page_language);
+ GetMatchPatterns("REGION_IGNORED", page_language, pattern_source);
// Allow address fields to appear in any order.
size_t begin_trailing_non_labeled_fields = 0;
@@ -131,10 +132,12 @@ std::unique_ptr<FormField> AddressField::Parse(
return WithFieldType(p, MatchFieldType::kTextArea);
})) {
continue;
- } else if (address_field->ParseAddress(scanner, page_language) ||
+ } else if (address_field->ParseAddress(scanner, page_language,
+ pattern_source) ||
address_field->ParseDependentLocalityCityStateCountryZipCode(
- scanner, page_language) ||
- address_field->ParseCompany(scanner, page_language)) {
+ scanner, page_language, pattern_source) ||
+ address_field->ParseCompany(scanner, page_language,
+ pattern_source)) {
has_trailing_non_labeled_fields = false;
continue;
} else if (ParseField(scanner, kAttentionIgnoredRe,
@@ -227,20 +230,21 @@ void AddressField::AddClassifications(
}
bool AddressField::ParseCompany(AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (company_)
return false;
base::span<const MatchPatternRef> company_patterns =
- GetMatchPatterns("COMPANY_NAME", page_language);
+ GetMatchPatterns("COMPANY_NAME", page_language, pattern_source);
return ParseField(scanner, kCompanyRe, company_patterns, &company_,
{log_manager_, "kCompanyRe"});
}
-bool AddressField::ParseAddressFieldSequence(
- AutofillScanner* scanner,
- const LanguageCode& page_language) {
+bool AddressField::ParseAddressFieldSequence(AutofillScanner* scanner,
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
// Search for a sequence of a street name field followed by a house number
// field. Only if both are found in an abitrary order, the parsing is
// considered successful.
@@ -254,12 +258,12 @@ bool AddressField::ParseAddressFieldSequence(
const size_t cursor_position = scanner->CursorPosition();
base::span<const MatchPatternRef> street_name_patterns =
- GetMatchPatterns(ADDRESS_HOME_STREET_NAME, page_language);
+ GetMatchPatterns(ADDRESS_HOME_STREET_NAME, page_language, pattern_source);
- base::span<const MatchPatternRef> house_number_patterns =
- GetMatchPatterns(ADDRESS_HOME_HOUSE_NUMBER, page_language);
+ base::span<const MatchPatternRef> house_number_patterns = GetMatchPatterns(
+ ADDRESS_HOME_HOUSE_NUMBER, page_language, pattern_source);
base::span<const MatchPatternRef> apartment_number_patterns =
- GetMatchPatterns(ADDRESS_HOME_APT_NUM, page_language);
+ GetMatchPatterns(ADDRESS_HOME_APT_NUM, page_language, pattern_source);
while (!scanner->IsEnd()) {
if (!street_name_ &&
@@ -307,19 +311,23 @@ bool AddressField::ParseAddressFieldSequence(
}
bool AddressField::ParseAddress(AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (street_name_ && house_number_) {
return false;
}
// Do not inline these calls: After passing an address field sequence, there
// might be an additional address line 2 to parse afterwards.
- bool has_field_sequence = ParseAddressFieldSequence(scanner, page_language);
- bool has_address_lines = ParseAddressLines(scanner, page_language);
+ bool has_field_sequence =
+ ParseAddressFieldSequence(scanner, page_language, pattern_source);
+ bool has_address_lines =
+ ParseAddressLines(scanner, page_language, pattern_source);
return has_field_sequence || has_address_lines;
}
bool AddressField::ParseAddressLines(AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
// We only match the string "address" in page text, not in element names,
// because sometimes every element in a group of address fields will have
// a name containing the string "address"; for example, on the page
@@ -335,7 +343,7 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner,
std::u16string label_pattern = kAddressLine1LabelRe;
base::span<const MatchPatternRef> address_line1_patterns =
- GetMatchPatterns("ADDRESS_LINE_1", page_language);
+ GetMatchPatterns("ADDRESS_LINE_1", page_language, pattern_source);
// TODO(crbug.com/1121990): Remove duplicate calls when launching
// AutofillParsingPatternProvider. The old code calls ParseFieldSpecifics()
@@ -383,7 +391,7 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner,
label_pattern = kAddressLine2LabelRe;
base::span<const MatchPatternRef> address_line2_patterns =
- GetMatchPatterns("ADDRESS_LINE_2", page_language);
+ GetMatchPatterns("ADDRESS_LINE_2", page_language, pattern_source);
if (!ParseField(scanner, pattern, address_line2_patterns, &address2_,
{log_manager_, "kAddressLine2Re"}) &&
@@ -395,7 +403,7 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner,
return true;
base::span<const MatchPatternRef> address_line_extra_patterns =
- GetMatchPatterns("ADDRESS_LINE_EXTRA", page_language);
+ GetMatchPatterns("ADDRESS_LINE_EXTRA", page_language, pattern_source);
// Optionally parse address line 3. This uses the same label regexp as
// address 2 above.
@@ -424,14 +432,15 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner,
}
bool AddressField::ParseCountry(AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (country_)
return false;
base::span<const MatchPatternRef> country_patterns =
- GetMatchPatterns("COUNTRY", page_language);
+ GetMatchPatterns("COUNTRY", page_language, pattern_source);
base::span<const MatchPatternRef> country_patternsl =
- GetMatchPatterns("COUNTRY_LOCATION", page_language);
+ GetMatchPatterns("COUNTRY_LOCATION", page_language, pattern_source);
scanner->SaveCursor();
if (ParseFieldSpecifics(scanner, kCountryRe,
@@ -453,15 +462,16 @@ bool AddressField::ParseCountry(AutofillScanner* scanner,
}
bool AddressField::ParseZipCode(AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (zip_)
return false;
base::span<const MatchPatternRef> zip_code_patterns =
- GetMatchPatterns("ZIP_CODE", page_language);
+ GetMatchPatterns("ZIP_CODE", page_language, pattern_source);
base::span<const MatchPatternRef> four_digit_zip_code_patterns =
- GetMatchPatterns("ZIP_4", page_language);
+ GetMatchPatterns("ZIP_4", page_language, pattern_source);
if (!ParseFieldSpecifics(scanner, kZipCodeRe, kZipCodeMatchType,
zip_code_patterns, &zip_,
{log_manager_, "kZipCodeRe"})) {
@@ -477,7 +487,8 @@ bool AddressField::ParseZipCode(AutofillScanner* scanner,
}
bool AddressField::ParseDependentLocality(AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
const bool is_enabled_dependent_locality_parsing =
base::FeatureList::IsEnabled(
features::kAutofillEnableDependentLocalityParsing);
@@ -486,7 +497,8 @@ bool AddressField::ParseDependentLocality(AutofillScanner* scanner,
return false;
base::span<const MatchPatternRef> dependent_locality_patterns =
- GetMatchPatterns("ADDRESS_HOME_DEPENDENT_LOCALITY", page_language);
+ GetMatchPatterns("ADDRESS_HOME_DEPENDENT_LOCALITY", page_language,
+ pattern_source);
return ParseFieldSpecifics(scanner, kDependentLocalityRe,
kDependentLocalityMatchType,
dependent_locality_patterns, &dependent_locality_,
@@ -494,23 +506,25 @@ bool AddressField::ParseDependentLocality(AutofillScanner* scanner,
}
bool AddressField::ParseCity(AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (city_)
return false;
base::span<const MatchPatternRef> city_patterns =
- GetMatchPatterns("CITY", page_language);
+ GetMatchPatterns("CITY", page_language, pattern_source);
return ParseFieldSpecifics(scanner, kCityRe, kCityMatchType, city_patterns,
&city_, {log_manager_, "kCityRe"});
}
bool AddressField::ParseState(AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (state_)
return false;
base::span<const MatchPatternRef> patterns_state =
- GetMatchPatterns("STATE", page_language);
+ GetMatchPatterns("STATE", page_language, pattern_source);
return ParseFieldSpecifics(scanner, kStateRe, kStateMatchType, patterns_state,
&state_, {log_manager_, "kStateRe"});
}
@@ -554,7 +568,8 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelSeparately(
bool AddressField::ParseDependentLocalityCityStateCountryZipCode(
AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
// The |scanner| is not pointing at a field.
if (scanner->IsEnd())
return false;
@@ -573,36 +588,37 @@ bool AddressField::ParseDependentLocalityCityStateCountryZipCode(
// Exactly one field type is missing.
if (num_of_missing_types == 1) {
if (!dependent_locality_)
- return ParseDependentLocality(scanner, page_language);
+ return ParseDependentLocality(scanner, page_language, pattern_source);
if (!city_)
- return ParseCity(scanner, page_language);
+ return ParseCity(scanner, page_language, pattern_source);
if (!state_)
- return ParseState(scanner, page_language);
+ return ParseState(scanner, page_language, pattern_source);
if (!country_)
- return ParseCountry(scanner, page_language);
+ return ParseCountry(scanner, page_language, pattern_source);
if (!zip_)
- return ParseZipCode(scanner, page_language);
+ return ParseZipCode(scanner, page_language, pattern_source);
}
// Check for matches to both the name and the label.
ParseNameLabelResult dependent_locality_result =
- ParseNameAndLabelForDependentLocality(scanner, page_language);
+ ParseNameAndLabelForDependentLocality(scanner, page_language,
+ pattern_source);
if (dependent_locality_result == RESULT_MATCH_NAME_LABEL)
return true;
ParseNameLabelResult city_result =
- ParseNameAndLabelForCity(scanner, page_language);
+ ParseNameAndLabelForCity(scanner, page_language, pattern_source);
if (city_result == RESULT_MATCH_NAME_LABEL)
return true;
ParseNameLabelResult state_result =
- ParseNameAndLabelForState(scanner, page_language);
+ ParseNameAndLabelForState(scanner, page_language, pattern_source);
if (state_result == RESULT_MATCH_NAME_LABEL)
return true;
ParseNameLabelResult country_result =
- ParseNameAndLabelForCountry(scanner, page_language);
+ ParseNameAndLabelForCountry(scanner, page_language, pattern_source);
if (country_result == RESULT_MATCH_NAME_LABEL)
return true;
ParseNameLabelResult zip_result =
- ParseNameAndLabelForZipCode(scanner, page_language);
+ ParseNameAndLabelForZipCode(scanner, page_language, pattern_source);
if (zip_result == RESULT_MATCH_NAME_LABEL)
return true;
@@ -624,7 +640,7 @@ bool AddressField::ParseDependentLocalityCityStateCountryZipCode(
if (country_result != RESULT_MATCH_NONE)
return SetFieldAndAdvanceCursor(scanner, &country_);
if (zip_result != RESULT_MATCH_NONE)
- return ParseZipCode(scanner, page_language);
+ return ParseZipCode(scanner, page_language, pattern_source);
}
// If there is a clash between the country and the state, set the type of
@@ -654,7 +670,7 @@ bool AddressField::ParseDependentLocalityCityStateCountryZipCode(
if (country_result == result)
return SetFieldAndAdvanceCursor(scanner, &country_);
if (zip_result == result)
- return ParseZipCode(scanner, page_language);
+ return ParseZipCode(scanner, page_language, pattern_source);
}
return false;
@@ -662,15 +678,16 @@ bool AddressField::ParseDependentLocalityCityStateCountryZipCode(
AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForZipCode(
AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (zip_)
return RESULT_MATCH_NONE;
base::span<const MatchPatternRef> zip_code_patterns =
- GetMatchPatterns("ZIP_CODE", page_language);
+ GetMatchPatterns("ZIP_CODE", page_language, pattern_source);
base::span<const MatchPatternRef> four_digit_zip_code_patterns =
- GetMatchPatterns("ZIP_4", page_language);
+ GetMatchPatterns("ZIP_4", page_language, pattern_source);
ParseNameLabelResult result = ParseNameAndLabelSeparately(
scanner, kZipCodeRe, kZipCodeMatchType, zip_code_patterns, &zip_,
@@ -680,12 +697,12 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForZipCode(
return result;
size_t saved_cursor = scanner->SaveCursor();
- bool found_non_zip4 = ParseCity(scanner, page_language);
+ bool found_non_zip4 = ParseCity(scanner, page_language, pattern_source);
if (found_non_zip4)
city_ = nullptr;
scanner->RewindTo(saved_cursor);
if (!found_non_zip4) {
- found_non_zip4 = ParseState(scanner, page_language);
+ found_non_zip4 = ParseState(scanner, page_language, pattern_source);
if (found_non_zip4)
state_ = nullptr;
scanner->RewindTo(saved_cursor);
@@ -704,7 +721,8 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForZipCode(
AddressField::ParseNameLabelResult
AddressField::ParseNameAndLabelForDependentLocality(
AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
const bool is_enabled_dependent_locality_parsing =
base::FeatureList::IsEnabled(
features::kAutofillEnableDependentLocalityParsing);
@@ -713,7 +731,8 @@ AddressField::ParseNameAndLabelForDependentLocality(
return RESULT_MATCH_NONE;
base::span<const MatchPatternRef> dependent_locality_patterns =
- GetMatchPatterns("ADDRESS_HOME_DEPENDENT_LOCALITY", page_language);
+ GetMatchPatterns("ADDRESS_HOME_DEPENDENT_LOCALITY", page_language,
+ pattern_source);
return ParseNameAndLabelSeparately(
scanner, kDependentLocalityRe, kDependentLocalityMatchType,
dependent_locality_patterns, &dependent_locality_,
@@ -722,12 +741,13 @@ AddressField::ParseNameAndLabelForDependentLocality(
AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCity(
AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (city_)
return RESULT_MATCH_NONE;
base::span<const MatchPatternRef> city_patterns =
- GetMatchPatterns("CITY", page_language);
+ GetMatchPatterns("CITY", page_language, pattern_source);
return ParseNameAndLabelSeparately(scanner, kCityRe, kCityMatchType,
city_patterns, &city_,
{log_manager_, "kCityRe"});
@@ -735,12 +755,13 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCity(
AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForState(
AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (state_)
return RESULT_MATCH_NONE;
base::span<const MatchPatternRef> patterns_state =
- GetMatchPatterns("STATE", page_language);
+ GetMatchPatterns("STATE", page_language, pattern_source);
return ParseNameAndLabelSeparately(scanner, kStateRe, kStateMatchType,
patterns_state, &state_,
{log_manager_, "kStateRe"});
@@ -748,15 +769,16 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForState(
AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCountry(
AutofillScanner* scanner,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (country_)
return RESULT_MATCH_NONE;
base::span<const MatchPatternRef> country_patterns =
- GetMatchPatterns("COUNTRY", page_language);
+ GetMatchPatterns("COUNTRY", page_language, pattern_source);
base::span<const MatchPatternRef> country_location_patterns =
- GetMatchPatterns("COUNTRY_LOCATION", page_language);
+ GetMatchPatterns("COUNTRY_LOCATION", page_language, pattern_source);
ParseNameLabelResult country_result = ParseNameAndLabelSeparately(
scanner, kCountryRe,
diff --git a/chromium/components/autofill/core/browser/form_parsing/address_field.h b/chromium/components/autofill/core/browser/form_parsing/address_field.h
index d6b8f0e5ab4..5b485650c75 100644
--- a/chromium/components/autofill/core/browser/form_parsing/address_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/address_field.h
@@ -26,6 +26,7 @@ class AddressField : public FormField {
public:
static std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
AddressField(const AddressField&) = delete;
@@ -46,36 +47,48 @@ class AddressField : public FormField {
explicit AddressField(LogManager* log_manager);
bool ParseCompany(AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
bool ParseAddress(AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
bool ParseAddressFieldSequence(AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
bool ParseAddressLines(AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
bool ParseCountry(AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
bool ParseZipCode(AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
bool ParseDependentLocality(AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
- bool ParseCity(AutofillScanner* scanner, const LanguageCode& page_language);
+ bool ParseCity(AutofillScanner* scanner,
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
- bool ParseState(AutofillScanner* scanner, const LanguageCode& page_language);
+ bool ParseState(AutofillScanner* scanner,
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
// Parses the current field pointed to by |scanner|, if it exists, and tries
// to determine if the field's type corresponds to one of the following:
// dependent locality, city, state, country, zip, or none of those.
bool ParseDependentLocalityCityStateCountryZipCode(
AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
// Like ParseFieldSpecifics(), but applies |pattern| against the name and
// label of the current field separately. If the return value is
@@ -95,23 +108,28 @@ class AddressField : public FormField {
// Otherwise |scanner| rewinds and the field is cleared.
ParseNameLabelResult ParseNameAndLabelForZipCode(
AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
ParseNameLabelResult ParseNameAndLabelForDependentLocality(
AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
ParseNameLabelResult ParseNameAndLabelForCity(
AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
ParseNameLabelResult ParseNameAndLabelForCountry(
AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
ParseNameLabelResult ParseNameAndLabelForState(
AutofillScanner* scanner,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
raw_ptr<LogManager> log_manager_;
AutofillField* company_ = nullptr;
diff --git a/chromium/components/autofill/core/browser/form_parsing/address_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/address_field_unittest.cc
index 6dc10120dfe..64dd965bc42 100644
--- a/chromium/components/autofill/core/browser/form_parsing/address_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/address_field_unittest.cc
@@ -14,47 +14,55 @@
namespace autofill {
-class AddressFieldTest : public FormFieldTest {
+class AddressFieldTest
+ : public FormFieldTestBase,
+ public ::testing::TestWithParam<PatternProviderFeatureState> {
public:
- AddressFieldTest() = default;
+ AddressFieldTest() : FormFieldTestBase(GetParam()) {}
AddressFieldTest(const AddressFieldTest&) = delete;
AddressFieldTest& operator=(const AddressFieldTest&) = delete;
protected:
std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language) override {
- return AddressField::Parse(scanner, page_language, nullptr);
+ return AddressField::Parse(scanner, page_language, GetActivePatternSource(),
+ /*log_manager=*/nullptr);
}
};
-TEST_F(AddressFieldTest, Empty) {
+INSTANTIATE_TEST_SUITE_P(
+ AddressFieldTest,
+ AddressFieldTest,
+ ::testing::ValuesIn(PatternProviderFeatureState::All()));
+
+TEST_P(AddressFieldTest, Empty) {
ClassifyAndVerify(ParseResult::NOT_PARSED);
}
-TEST_F(AddressFieldTest, NonParse) {
+TEST_P(AddressFieldTest, NonParse) {
AddTextFormFieldData("", "", UNKNOWN_TYPE);
ClassifyAndVerify(ParseResult::NOT_PARSED);
}
-TEST_F(AddressFieldTest, ParseOneLineAddress) {
+TEST_P(AddressFieldTest, ParseOneLineAddress) {
AddTextFormFieldData("address", "Address", ADDRESS_HOME_LINE1);
ClassifyAndVerify();
}
-TEST_F(AddressFieldTest, ParseTwoLineAddress) {
+TEST_P(AddressFieldTest, ParseTwoLineAddress) {
AddTextFormFieldData("address", "Address", ADDRESS_HOME_LINE1);
AddTextFormFieldData("address2", "Address", ADDRESS_HOME_LINE2);
ClassifyAndVerify();
}
-TEST_F(AddressFieldTest, ParseThreeLineAddress) {
+TEST_P(AddressFieldTest, ParseThreeLineAddress) {
AddTextFormFieldData("Address1", "Address Line 1", ADDRESS_HOME_LINE1);
AddTextFormFieldData("Address1", "Address Line 2", ADDRESS_HOME_LINE2);
AddTextFormFieldData("Address1", "Address Line 3", ADDRESS_HOME_LINE3);
ClassifyAndVerify();
}
-TEST_F(AddressFieldTest, ParseStreetAddressFromTextArea) {
+TEST_P(AddressFieldTest, ParseStreetAddressFromTextArea) {
AddFormFieldData("textarea", "address", "Address",
ADDRESS_HOME_STREET_ADDRESS);
ClassifyAndVerify();
@@ -63,7 +71,7 @@ TEST_F(AddressFieldTest, ParseStreetAddressFromTextArea) {
// Tests that fields are classified as |ADDRESS_HOME_STREET_NAME| and
// |ADDRESS_HOME_HOUSE_NUMBER| when they are labeled accordingly and
// both are present.
-TEST_F(AddressFieldTest, ParseStreetNameAndHouseNumber) {
+TEST_P(AddressFieldTest, ParseStreetNameAndHouseNumber) {
// TODO(crbug.com/1125978): Remove once launched.
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
@@ -78,7 +86,7 @@ TEST_F(AddressFieldTest, ParseStreetNameAndHouseNumber) {
// Tests that fields are classified as |ADDRESS_HOME_STREET_NAME|, and
// |ADDRESS_HOME_HOUSE_NUMBER| |ADDRESS_HOME_APT_NUM| when they are labeled
// accordingly and all are present.
-TEST_F(AddressFieldTest, ParseStreetNameAndHouseNumberAndApartmentNumber) {
+TEST_P(AddressFieldTest, ParseStreetNameAndHouseNumberAndApartmentNumber) {
// TODO(crbug.com/1125978): Remove once launched.
base::test::ScopedFeatureList enabled;
enabled.InitWithFeatures(
@@ -96,7 +104,7 @@ TEST_F(AddressFieldTest, ParseStreetNameAndHouseNumberAndApartmentNumber) {
// Tests that an address field after a |ADDRESS_HOME_STREET_NAME|,
// |ADDRESS_HOME_HOUSE_NUMBER| combination is classified as
// |ADDRESS_HOME_LINE2| instead of |ADDRESS_HOME_LINE1|.
-TEST_F(AddressFieldTest, ParseAsAddressLine2AfterStreetName) {
+TEST_P(AddressFieldTest, ParseAsAddressLine2AfterStreetName) {
// TODO(crbug.com/1125978): Remove once launched.
base::test::ScopedFeatureList structured_addresses;
structured_addresses.InitAndEnableFeature(
@@ -111,7 +119,7 @@ TEST_F(AddressFieldTest, ParseAsAddressLine2AfterStreetName) {
// Tests that the field is not classified as |ADDRESS_HOME_STREET_NAME| when
// it is labeled accordingly but an adjacent field classified as
// |ADDRESS_HOME_HOUSE_NUMBER| is absent.
-TEST_F(AddressFieldTest, NotParseStreetNameWithoutHouseNumber) {
+TEST_P(AddressFieldTest, NotParseStreetNameWithoutHouseNumber) {
// TODO(crbug.com/1125978): Remove once launched.
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
@@ -123,7 +131,7 @@ TEST_F(AddressFieldTest, NotParseStreetNameWithoutHouseNumber) {
// Tests that the field is not classified as |ADDRESS_HOME_HOUSE_NUMBER| when
// it is labeled accordingly but adjacent field classified as
// |ADDRESS_HOME_STREET_NAME| is absent.
-TEST_F(AddressFieldTest, NotParseHouseNumberWithoutStreetName) {
+TEST_P(AddressFieldTest, NotParseHouseNumberWithoutStreetName) {
// TODO(crbug.com/1125978): Remove once launched.
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
@@ -135,7 +143,7 @@ TEST_F(AddressFieldTest, NotParseHouseNumberWithoutStreetName) {
// Tests that the dependent locality is correctly classified with
// an unambiguous field name and label.
-TEST_F(AddressFieldTest, ParseDependentLocality) {
+TEST_P(AddressFieldTest, ParseDependentLocality) {
// TODO(crbug.com/1157405): Remove once launched.
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
@@ -146,28 +154,28 @@ TEST_F(AddressFieldTest, ParseDependentLocality) {
ClassifyAndVerify();
}
-TEST_F(AddressFieldTest, ParseCity) {
+TEST_P(AddressFieldTest, ParseCity) {
AddTextFormFieldData("city", "City", ADDRESS_HOME_CITY);
ClassifyAndVerify();
}
-TEST_F(AddressFieldTest, ParseState) {
+TEST_P(AddressFieldTest, ParseState) {
AddTextFormFieldData("state", "State", ADDRESS_HOME_STATE);
ClassifyAndVerify();
}
-TEST_F(AddressFieldTest, ParseZip) {
+TEST_P(AddressFieldTest, ParseZip) {
AddTextFormFieldData("zip", "Zip", ADDRESS_HOME_ZIP);
ClassifyAndVerify();
}
-TEST_F(AddressFieldTest, ParseZipFileExtension) {
+TEST_P(AddressFieldTest, ParseZipFileExtension) {
AddTextFormFieldData("filename", "Supported formats: .zip, .rar",
UNKNOWN_TYPE);
ClassifyAndVerify(ParseResult::NOT_PARSED);
}
-TEST_F(AddressFieldTest, ParseStateAndZipOneLabel) {
+TEST_P(AddressFieldTest, ParseStateAndZipOneLabel) {
AddTextFormFieldData("state", "State/Province, Zip/Postal Code",
ADDRESS_HOME_STATE);
AddTextFormFieldData("zip", "State/Province, Zip/Postal Code",
@@ -175,19 +183,19 @@ TEST_F(AddressFieldTest, ParseStateAndZipOneLabel) {
ClassifyAndVerify();
}
-TEST_F(AddressFieldTest, ParseCountry) {
+TEST_P(AddressFieldTest, ParseCountry) {
AddTextFormFieldData("country", "Country", ADDRESS_HOME_COUNTRY);
ClassifyAndVerify();
}
-TEST_F(AddressFieldTest, ParseCompany) {
+TEST_P(AddressFieldTest, ParseCompany) {
AddTextFormFieldData("company", "Company", COMPANY_NAME);
ClassifyAndVerify();
}
// Tests that the dependent locality, city, state, country and zip-code
// fields are correctly classfied with unambiguous field names and labels.
-TEST_F(AddressFieldTest,
+TEST_P(AddressFieldTest,
ParseDependentLocalityCityStateCountryZipcodeTogether) {
// TODO(crbug.com/1157405): Remove once launched.
base::test::ScopedFeatureList enabled;
@@ -205,14 +213,14 @@ TEST_F(AddressFieldTest,
// Tests that the field is classified as |ADDRESS_HOME_COUNTRY| when the field
// label contains 'Region'.
-TEST_F(AddressFieldTest, ParseCountryLabelRegion) {
+TEST_P(AddressFieldTest, ParseCountryLabelRegion) {
AddTextFormFieldData("country", "Country/Region", ADDRESS_HOME_COUNTRY);
ClassifyAndVerify();
}
// Tests that the field is classified as |ADDRESS_HOME_COUNTRY| when the field
// name contains 'region'.
-TEST_F(AddressFieldTest, ParseCountryNameRegion) {
+TEST_P(AddressFieldTest, ParseCountryNameRegion) {
AddTextFormFieldData("client_region", "Land", ADDRESS_HOME_COUNTRY);
ClassifyAndVerify();
}
@@ -220,7 +228,7 @@ TEST_F(AddressFieldTest, ParseCountryNameRegion) {
// Tests that city and state fields are classified correctly when their names
// contain keywords for different types. This is achieved by giving the priority
// to the label over the name for pages in Turkish.
-TEST_F(AddressFieldTest, ParseTurkishCityStateWithLabelPrecedence) {
+TEST_P(AddressFieldTest, ParseTurkishCityStateWithLabelPrecedence) {
// TODO(crbug.com/1156315): Remove once launched.
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
@@ -232,21 +240,21 @@ TEST_F(AddressFieldTest, ParseTurkishCityStateWithLabelPrecedence) {
}
// Tests that address name is not misclassified as address.
-TEST_F(AddressFieldTest, NotParseAddressName) {
+TEST_P(AddressFieldTest, NotParseAddressName) {
AddTextFormFieldData("address", "Adres Başlığı", UNKNOWN_TYPE);
ClassifyAndVerify(ParseResult::NOT_PARSED, LanguageCode("tr"));
}
// Tests that the address components sequence in a label is classified
// as |ADDRESS_HOME_LINE1|.
-TEST_F(AddressFieldTest, ParseAddressComponentsSequenceAsAddressLine1) {
+TEST_P(AddressFieldTest, ParseAddressComponentsSequenceAsAddressLine1) {
AddTextFormFieldData("detail", "Улица, дом, квартира", ADDRESS_HOME_LINE1);
ClassifyAndVerify(ParseResult::PARSED, LanguageCode("ru"));
}
// Tests that the address components sequence in a label is classified
// as |ADDRESS_HOME_STREET_ADDRESS|.
-TEST_F(AddressFieldTest, ParseAddressComponentsSequenceAsStreetAddress) {
+TEST_P(AddressFieldTest, ParseAddressComponentsSequenceAsStreetAddress) {
AddFormFieldData("textarea", "detail",
"Mahalle, sokak, cadde ve diÄŸer bilgilerinizi girin",
ADDRESS_HOME_STREET_ADDRESS);
diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc
index 98b6217e92b..806c8b74ae7 100644
--- a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc
@@ -64,6 +64,7 @@ bool FieldCanFitDataForFieldType(int max_length, ServerFieldType type) {
std::unique_ptr<FormField> CreditCardField::Parse(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
if (scanner->IsEnd())
return nullptr;
@@ -74,23 +75,24 @@ std::unique_ptr<FormField> CreditCardField::Parse(
bool cardholder_name_match_has_low_confidence = false;
base::span<const MatchPatternRef> name_on_card_patterns =
- GetMatchPatterns("NAME_ON_CARD", page_language);
+ GetMatchPatterns("NAME_ON_CARD", page_language, pattern_source);
base::span<const MatchPatternRef> name_on_card_contextual_patterns =
- GetMatchPatterns("NAME_ON_CARD_CONTEXTUAL", page_language);
+ GetMatchPatterns("NAME_ON_CARD_CONTEXTUAL", page_language,
+ pattern_source);
base::span<const MatchPatternRef> last_name_patterns =
- GetMatchPatterns("LAST_NAME", page_language);
+ GetMatchPatterns("LAST_NAME", page_language, pattern_source);
- base::span<const MatchPatternRef> cvc_patterns =
- GetMatchPatterns(CREDIT_CARD_VERIFICATION_CODE, page_language);
+ base::span<const MatchPatternRef> cvc_patterns = GetMatchPatterns(
+ CREDIT_CARD_VERIFICATION_CODE, page_language, pattern_source);
// Credit card fields can appear in many different orders.
// We loop until no more credit card related fields are found, see |break| at
// the bottom of the loop.
for (int fields = 0; !scanner->IsEnd(); ++fields) {
// Ignore gift card fields.
- if (IsGiftCardField(scanner, log_manager, page_language))
+ if (IsGiftCardField(scanner, log_manager, page_language, pattern_source))
break;
if (!credit_card_field->cardholder_) {
@@ -189,7 +191,7 @@ std::unique_ptr<FormField> CreditCardField::Parse(
// doesn't have bad side effects.
AutofillField* current_number_field;
base::span<const MatchPatternRef> patterns =
- GetMatchPatterns(CREDIT_CARD_NUMBER, page_language);
+ GetMatchPatterns(CREDIT_CARD_NUMBER, page_language, pattern_source);
if (ParseFieldSpecifics(scanner, kCardNumberRe, kMatchNumTelAndPwd,
patterns, &current_number_field,
{log_manager, "kCardNumberRe"})) {
@@ -217,7 +219,7 @@ std::unique_ptr<FormField> CreditCardField::Parse(
}
if (credit_card_field->ParseExpirationDate(scanner, log_manager,
- page_language)) {
+ page_language, pattern_source)) {
nb_unknown_fields = 0;
continue;
}
@@ -325,7 +327,8 @@ bool CreditCardField::LikelyCardMonthSelectField(AutofillScanner* scanner) {
bool CreditCardField::LikelyCardYearSelectField(
AutofillScanner* scanner,
LogManager* log_manager,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (scanner->IsEnd())
return false;
@@ -347,7 +350,7 @@ bool CreditCardField::LikelyCardYearSelectField(
// Another way to eliminate days - filter out 'day' fields.
base::span<const MatchPatternRef> day_patterns =
- GetMatchPatterns("DAY", page_language);
+ GetMatchPatterns("DAY", page_language, pattern_source);
if (FormField::ParseFieldSpecifics(
scanner, kDayRe, kDefaultMatchParamsWith<MatchFieldType::kSelect>,
day_patterns, nullptr, {log_manager, "kDayRe"})) {
@@ -415,7 +418,8 @@ bool CreditCardField::LikelyCardTypeSelectField(AutofillScanner* scanner) {
// static
bool CreditCardField::IsGiftCardField(AutofillScanner* scanner,
LogManager* log_manager,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
if (scanner->IsEnd())
return false;
@@ -428,13 +432,13 @@ bool CreditCardField::IsGiftCardField(AutofillScanner* scanner,
size_t saved_cursor = scanner->SaveCursor();
base::span<const MatchPatternRef> debit_cards_patterns =
- GetMatchPatterns("DEBIT_CARD", page_language);
+ GetMatchPatterns("DEBIT_CARD", page_language, pattern_source);
base::span<const MatchPatternRef> debit_gift_card_patterns =
- GetMatchPatterns("DEBIT_GIFT_CARD", page_language);
+ GetMatchPatterns("DEBIT_GIFT_CARD", page_language, pattern_source);
base::span<const MatchPatternRef> gift_card_patterns =
- GetMatchPatterns("GIFT_CARD", page_language);
+ GetMatchPatterns("GIFT_CARD", page_language, pattern_source);
if (ParseFieldSpecifics(scanner, kDebitCardRe, kMatchFieldType,
debit_cards_patterns, nullptr,
@@ -508,8 +512,9 @@ void CreditCardField::AddClassifications(
bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner,
LogManager* log_manager,
- const LanguageCode& page_language) {
- if (!expiration_date_ && base::LowerCaseEqualsASCII(
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
+ if (!expiration_date_ && base::EqualsCaseInsensitiveASCII(
scanner->Cursor()->form_control_type, "month")) {
expiration_date_ = scanner->Cursor();
expiration_month_ = nullptr;
@@ -528,7 +533,8 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner,
if (LikelyCardMonthSelectField(scanner)) {
expiration_month_ = scanner->Cursor();
scanner->Advance();
- if (LikelyCardYearSelectField(scanner, log_manager, page_language)) {
+ if (LikelyCardYearSelectField(scanner, log_manager, page_language,
+ pattern_source)) {
expiration_year_ = scanner->Cursor();
scanner->Advance();
return true;
@@ -545,16 +551,18 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner,
MatchFieldType::kSelect, MatchFieldType::kSearch>;
base::span<const MatchPatternRef> cc_exp_month_patterns =
- GetMatchPatterns(CREDIT_CARD_EXP_MONTH, page_language);
+ GetMatchPatterns(CREDIT_CARD_EXP_MONTH, page_language, pattern_source);
base::span<const MatchPatternRef> cc_exp_year_patterns =
- GetMatchPatterns("CREDIT_CARD_EXP_YEAR", page_language);
+ GetMatchPatterns("CREDIT_CARD_EXP_YEAR", page_language, pattern_source);
base::span<const MatchPatternRef> cc_exp_month_before_year_patterns =
- GetMatchPatterns("CREDIT_CARD_EXP_MONTH_BEFORE_YEAR", page_language);
+ GetMatchPatterns("CREDIT_CARD_EXP_MONTH_BEFORE_YEAR", page_language,
+ pattern_source);
base::span<const MatchPatternRef> cc_exp_year_after_month_patterns =
- GetMatchPatterns("CREDIT_CARD_EXP_YEAR_AFTER_MONTH", page_language);
+ GetMatchPatterns("CREDIT_CARD_EXP_YEAR_AFTER_MONTH", page_language,
+ pattern_source);
if (ParseFieldSpecifics(scanner, kExpirationMonthRe, kMatchCCType,
cc_exp_month_patterns, &expiration_month_,
@@ -588,7 +596,8 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner,
// Try to look for a 2-digit year expiration date.
base::span<const MatchPatternRef> cc_exp_2digit_year_patterns =
- GetMatchPatterns(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, page_language);
+ GetMatchPatterns(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, page_language,
+ pattern_source);
if (ParseFieldSpecifics(scanner, kExpirationDate2DigitYearRe, kMatchCCType,
cc_exp_2digit_year_patterns, &expiration_date_,
{log_manager_, "kExpirationDate2DigitYearRe"})) {
@@ -599,7 +608,7 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner,
// Try to look for a generic expiration date field. (2 or 4 digit year)
base::span<const MatchPatternRef> cc_exp_date_patterns =
- GetMatchPatterns("CREDIT_CARD_EXP_DATE", page_language);
+ GetMatchPatterns("CREDIT_CARD_EXP_DATE", page_language, pattern_source);
if (ParseFieldSpecifics(scanner, kExpirationDateRe, kMatchCCType,
cc_exp_date_patterns, &expiration_date_,
{log_manager_, "kExpirationDateRe"})) {
@@ -616,7 +625,8 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner,
// Try to look for a 4-digit year expiration date.
base::span<const MatchPatternRef> cc_exp_date_4_digit_year_patterns =
- GetMatchPatterns(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, page_language);
+ GetMatchPatterns(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, page_language,
+ pattern_source);
if (FieldCanFitDataForFieldType(current_field_max_length,
CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) &&
ParseFieldSpecifics(scanner, kExpirationDate4DigitYearRe, kMatchCCType,
diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h
index 91e8e731c0e..b2b8dc7c100 100644
--- a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h
@@ -30,6 +30,7 @@ class CreditCardField : public FormField {
~CreditCardField() override;
static std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
protected:
@@ -48,7 +49,8 @@ class CreditCardField : public FormField {
// to chrome://autofill-internals
static bool LikelyCardYearSelectField(AutofillScanner* scanner,
LogManager* log_manager,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
// Returns true if |scanner| points to a <select> field that contains credit
// card type options.
@@ -60,13 +62,15 @@ class CreditCardField : public FormField {
// a credit card.
static bool IsGiftCardField(AutofillScanner* scanner,
LogManager* log_manager,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
// Parses the expiration month/year/date fields. Returns true if it finds
// something new.
bool ParseExpirationDate(AutofillScanner* scanner,
LogManager* log_manager,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
// For the combined expiration field we return |exp_year_type_|; otherwise if
// |expiration_year_| is having year with |max_length| of 2-digits we return
diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc
index 12f12a0a750..15bf02bde14 100644
--- a/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc
@@ -80,7 +80,9 @@ std::vector<SelectOption> WithNoise(std::vector<SelectOption> options) {
class CreditCardFieldTestBase : public FormFieldTestBase {
public:
- CreditCardFieldTestBase() = default;
+ explicit CreditCardFieldTestBase(
+ PatternProviderFeatureState pattern_provider_feature_state)
+ : FormFieldTestBase(pattern_provider_feature_state) {}
CreditCardFieldTestBase(const CreditCardFieldTestBase&) = delete;
CreditCardFieldTestBase& operator=(const CreditCardFieldTestBase&) = delete;
@@ -88,7 +90,8 @@ class CreditCardFieldTestBase : public FormFieldTestBase {
std::unique_ptr<FormField> Parse(
AutofillScanner* scanner,
const LanguageCode& page_language = LanguageCode("us")) override {
- return CreditCardField::Parse(scanner, page_language, nullptr);
+ return CreditCardField::Parse(scanner, page_language,
+ GetActivePatternSource(), nullptr);
}
// Runs multiple parsing attempts until the end of the form is reached.
@@ -107,40 +110,48 @@ class CreditCardFieldTestBase : public FormFieldTestBase {
}
TestClassificationExpectations();
}
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
};
-class CreditCardFieldTest : public CreditCardFieldTestBase,
- public testing::Test {
+class CreditCardFieldTest
+ : public CreditCardFieldTestBase,
+ public ::testing::TestWithParam<PatternProviderFeatureState> {
public:
- CreditCardFieldTest() = default;
+ CreditCardFieldTest() : CreditCardFieldTestBase(GetParam()) {}
CreditCardFieldTest(const CreditCardFieldTest&) = delete;
CreditCardFieldTest& operator=(const CreditCardFieldTest&) = delete;
};
-TEST_F(CreditCardFieldTest, Empty) {
+INSTANTIATE_TEST_SUITE_P(CreditCardFieldTest,
+ CreditCardFieldTest,
+ testing::ValuesIn(PatternProviderFeatureState::All()));
+
+TEST_P(CreditCardFieldTest, Empty) {
ClassifyAndVerify(ParseResult::NOT_PARSED);
}
-TEST_F(CreditCardFieldTest, NonParse) {
+TEST_P(CreditCardFieldTest, NonParse) {
AddTextFormFieldData("", "", UNKNOWN_TYPE);
ClassifyAndVerify(ParseResult::NOT_PARSED);
}
-TEST_F(CreditCardFieldTest, ParseCreditCardNoNumber) {
+TEST_P(CreditCardFieldTest, ParseCreditCardNoNumber) {
AddTextFormFieldData("ccmonth", "Exp Month", UNKNOWN_TYPE);
AddTextFormFieldData("ccyear", "Exp Year", UNKNOWN_TYPE);
ClassifyAndVerify(ParseResult::NOT_PARSED);
}
-TEST_F(CreditCardFieldTest, ParseCreditCardNoDate) {
+TEST_P(CreditCardFieldTest, ParseCreditCardNoDate) {
AddTextFormFieldData("card_number", "Card Number", UNKNOWN_TYPE);
ClassifyAndVerify(ParseResult::NOT_PARSED);
}
-TEST_F(CreditCardFieldTest, ParseMiniumCreditCard) {
+TEST_P(CreditCardFieldTest, ParseMiniumCreditCard) {
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH);
AddTextFormFieldData("ccyear", "Exp Year", CREDIT_CARD_EXP_4_DIGIT_YEAR);
@@ -153,23 +164,33 @@ struct CreditCardFieldYearTestCase {
ServerFieldType expected_type;
};
-std::vector<SelectOption> MakeOptionVector(
- const CreditCardFieldYearTestCase& test_case) {
- std::vector<SelectOption> options;
- if (test_case.expected_type == CREDIT_CARD_EXP_2_DIGIT_YEAR) {
- options = Get2DigitYears();
- } else {
- options = Get4DigitYears();
- }
- if (test_case.with_noise) {
- options = WithNoise(options);
- }
- return options;
-}
-
class CreditCardFieldYearTest
: public CreditCardFieldTestBase,
- public testing::TestWithParam<CreditCardFieldYearTestCase> {};
+ public testing::TestWithParam<std::tuple<PatternProviderFeatureState,
+ CreditCardFieldYearTestCase>> {
+ public:
+ CreditCardFieldYearTest()
+ : CreditCardFieldTestBase(std::get<0>(GetParam())) {}
+
+ bool with_noise() const { return std::get<1>(GetParam()).with_noise; }
+
+ ServerFieldType expected_type() const {
+ return std::get<1>(GetParam()).expected_type;
+ }
+
+ std::vector<SelectOption> MakeOptionVector() const {
+ std::vector<SelectOption> options;
+ if (expected_type() == CREDIT_CARD_EXP_2_DIGIT_YEAR) {
+ options = Get2DigitYears();
+ } else {
+ options = Get4DigitYears();
+ }
+ if (with_noise()) {
+ options = WithNoise(options);
+ }
+ return options;
+ }
+};
TEST_P(CreditCardFieldYearTest, ParseMinimumCreditCardWithExpiryDateOptions) {
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
@@ -177,22 +198,24 @@ TEST_P(CreditCardFieldYearTest, ParseMinimumCreditCardWithExpiryDateOptions) {
CREDIT_CARD_EXP_MONTH);
AddSelectOneFormFieldDataWithLength(
"Random Label", "Random Label",
- GetParam().expected_type == CREDIT_CARD_EXP_2_DIGIT_YEAR ? 2 : 4,
- MakeOptionVector(GetParam()), GetParam().expected_type);
+ expected_type() == CREDIT_CARD_EXP_2_DIGIT_YEAR ? 2 : 4,
+ MakeOptionVector(), expected_type());
ClassifyAndVerify(ParseResult::PARSED);
}
INSTANTIATE_TEST_SUITE_P(
- ,
+ CreditCardFieldTest,
CreditCardFieldYearTest,
- testing::Values(
- CreditCardFieldYearTestCase{false, CREDIT_CARD_EXP_2_DIGIT_YEAR},
- CreditCardFieldYearTestCase{false, CREDIT_CARD_EXP_4_DIGIT_YEAR},
- CreditCardFieldYearTestCase{true, CREDIT_CARD_EXP_2_DIGIT_YEAR},
- CreditCardFieldYearTestCase{true, CREDIT_CARD_EXP_4_DIGIT_YEAR}));
-
-TEST_F(CreditCardFieldTest, ParseFullCreditCard) {
+ testing::Combine(
+ testing::ValuesIn(PatternProviderFeatureState::All()),
+ testing::Values(
+ CreditCardFieldYearTestCase{false, CREDIT_CARD_EXP_2_DIGIT_YEAR},
+ CreditCardFieldYearTestCase{false, CREDIT_CARD_EXP_4_DIGIT_YEAR},
+ CreditCardFieldYearTestCase{true, CREDIT_CARD_EXP_2_DIGIT_YEAR},
+ CreditCardFieldYearTestCase{true, CREDIT_CARD_EXP_4_DIGIT_YEAR})));
+
+TEST_P(CreditCardFieldTest, ParseFullCreditCard) {
AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL);
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH);
@@ -205,7 +228,7 @@ TEST_F(CreditCardFieldTest, ParseFullCreditCard) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(CreditCardFieldTest, ParseExpMonthYear) {
+TEST_P(CreditCardFieldTest, ParseExpMonthYear) {
AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL);
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
AddTextFormFieldData("ExpDate", "ExpDate Month / Year",
@@ -216,7 +239,7 @@ TEST_F(CreditCardFieldTest, ParseExpMonthYear) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(CreditCardFieldTest, ParseExpMonthYear2) {
+TEST_P(CreditCardFieldTest, ParseExpMonthYear2) {
AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL);
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
AddTextFormFieldData("ExpDate", "Expiration date Month / Year",
@@ -227,7 +250,7 @@ TEST_F(CreditCardFieldTest, ParseExpMonthYear2) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(CreditCardFieldTest, ParseGiftCard) {
+TEST_P(CreditCardFieldTest, ParseGiftCard) {
AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL);
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
AddTextFormFieldData("gift.certificate", "Gift certificate", UNKNOWN_TYPE);
@@ -236,33 +259,39 @@ TEST_F(CreditCardFieldTest, ParseGiftCard) {
ClassifyAndVerify(ParseResult::PARSED);
}
-typedef struct {
+struct ParseExpFieldTestCase {
const std::string cc_fields_form_control_type;
const std::string label;
const int max_length;
const ServerFieldType expected_prediction;
-} ParseExpFieldTestCase;
+};
+
+class ParseExpFieldTest
+ : public CreditCardFieldTestBase,
+ public testing::TestWithParam<
+ std::tuple<PatternProviderFeatureState, ParseExpFieldTestCase>> {
+ public:
+ ParseExpFieldTest() : CreditCardFieldTestBase(std::get<0>(GetParam())) {}
-class ParseExpFieldTest : public CreditCardFieldTestBase,
- public testing::TestWithParam<ParseExpFieldTestCase> {
+ const ParseExpFieldTestCase& test_case() const {
+ return std::get<1>(GetParam());
+ }
};
TEST_P(ParseExpFieldTest, ParseExpField) {
- auto test_case = GetParam();
-
AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL);
- AddFormFieldData(test_case.cc_fields_form_control_type, "card_number",
+ AddFormFieldData(test_case().cc_fields_form_control_type, "card_number",
"Card Number", CREDIT_CARD_NUMBER);
- AddFormFieldDataWithLength(test_case.cc_fields_form_control_type, "cc_exp",
- test_case.label, test_case.max_length,
- test_case.expected_prediction);
+ AddFormFieldDataWithLength(test_case().cc_fields_form_control_type, "cc_exp",
+ test_case().label, test_case().max_length,
+ test_case().expected_prediction);
- // Assists in identifing which case has failed.
- SCOPED_TRACE(test_case.expected_prediction);
- SCOPED_TRACE(test_case.max_length);
- SCOPED_TRACE(test_case.label);
+ // Assists in identifying which case has failed.
+ SCOPED_TRACE(test_case().expected_prediction);
+ SCOPED_TRACE(test_case().max_length);
+ SCOPED_TRACE(test_case().label);
- if (test_case.expected_prediction == UNKNOWN_TYPE) {
+ if (test_case().expected_prediction == UNKNOWN_TYPE) {
// Expect failure and continue to next test case.
// The expiry date is a required field for credit card forms, and thus the
// parse sets |field_| to nullptr.
@@ -274,153 +303,155 @@ TEST_P(ParseExpFieldTest, ParseExpField) {
}
INSTANTIATE_TEST_SUITE_P(
- ,
+ CreditCardFieldTest,
ParseExpFieldTest,
- testing::Values(
- // CC fields input_type="text"
- // General label, no maxlength.
- ParseExpFieldTestCase{"text", "Expiration Date", 0,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // General label, maxlength 4.
- ParseExpFieldTestCase{"text", "Expiration Date", 4,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // General label, maxlength 5.
- ParseExpFieldTestCase{"text", "Expiration Date", 5,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // General label, maxlength 6.
- ParseExpFieldTestCase{"text", "Expiration Date", 6,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // General label, maxlength 7.
- ParseExpFieldTestCase{"text", "Expiration Date", 7,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // General label, large maxlength.
- ParseExpFieldTestCase{"text", "Expiration Date", 12,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
-
- // Unsupported maxlength, general label.
- ParseExpFieldTestCase{"text", "Expiration Date", 3, UNKNOWN_TYPE},
- // Unsupported maxlength, two digit year label.
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 3,
- UNKNOWN_TYPE},
- // Unsupported maxlength, four digit year label.
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 3,
- UNKNOWN_TYPE},
-
- // Two digit year, simple label.
- ParseExpFieldTestCase{"text", "MM / YY", 0,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Two digit year, with slash (MM/YY).
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 0,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Two digit year, no slash (MMYY).
- ParseExpFieldTestCase{"text", "Expiration Date (MMYY)", 4,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Two digit year, with slash and maxlength (MM/YY).
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 5,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Two digit year, with slash and large maxlength (MM/YY).
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 12,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
-
- // Four digit year, simple label.
- ParseExpFieldTestCase{"text", "MM / YYYY", 0,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // Four digit year, with slash (MM/YYYY).
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 0,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // Four digit year, no slash (MMYYYY).
- ParseExpFieldTestCase{"text", "Expiration Date (MMYYYY)", 6,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // Four digit year, with slash and maxlength (MM/YYYY).
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 7,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // Four digit year, with slash and large maxlength (MM/YYYY).
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 12,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
-
- // Four digit year label with restrictive maxlength (4).
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 4,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Four digit year label with restrictive maxlength (5).
- ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 5,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
-
- // CC fields input_type="number"
- // General label, no maxlength.
- ParseExpFieldTestCase{"number", "Expiration Date", 0,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // General label, maxlength 4.
- ParseExpFieldTestCase{"number", "Expiration Date", 4,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // General label, maxlength 5.
- ParseExpFieldTestCase{"number", "Expiration Date", 5,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // General label, maxlength 6.
- ParseExpFieldTestCase{"number", "Expiration Date", 6,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // General label, maxlength 7.
- ParseExpFieldTestCase{"number", "Expiration Date", 7,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // General label, large maxlength.
- ParseExpFieldTestCase{"number", "Expiration Date", 12,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
-
- // Unsupported maxlength, general label.
- ParseExpFieldTestCase{"number", "Expiration Date", 3, UNKNOWN_TYPE},
- // Unsupported maxlength, two digit year label.
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 3,
- UNKNOWN_TYPE},
- // Unsupported maxlength, four digit year label.
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 3,
- UNKNOWN_TYPE},
-
- // Two digit year, simple label.
- ParseExpFieldTestCase{"number", "MM / YY", 0,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Two digit year, with slash (MM/YY).
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 0,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Two digit year, no slash (MMYY).
- ParseExpFieldTestCase{"number", "Expiration Date (MMYY)", 4,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Two digit year, with slash and maxlength (MM/YY).
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 5,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Two digit year, with slash and large maxlength (MM/YY).
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 12,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
-
- // Four digit year, simple label.
- ParseExpFieldTestCase{"number", "MM / YYYY", 0,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // Four digit year, with slash (MM/YYYY).
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 0,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // Four digit year, no slash (MMYYYY).
- ParseExpFieldTestCase{"number", "Expiration Date (MMYYYY)", 6,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // Four digit year, with slash and maxlength (MM/YYYY).
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 7,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
- // Four digit year, with slash and large maxlength (MM/YYYY).
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 12,
- CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
-
- // Four digit year label with restrictive maxlength (4).
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 4,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
- // Four digit year label with restrictive maxlength (5).
- ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 5,
- CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}));
-
-TEST_F(CreditCardFieldTest, ParseCreditCardHolderNameWithCCFullName) {
+ testing::Combine(
+ testing::ValuesIn(PatternProviderFeatureState::All()),
+ testing::Values(
+ // CC fields input_type="text"
+ // General label, no maxlength.
+ ParseExpFieldTestCase{"text", "Expiration Date", 0,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // General label, maxlength 4.
+ ParseExpFieldTestCase{"text", "Expiration Date", 4,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // General label, maxlength 5.
+ ParseExpFieldTestCase{"text", "Expiration Date", 5,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // General label, maxlength 6.
+ ParseExpFieldTestCase{"text", "Expiration Date", 6,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // General label, maxlength 7.
+ ParseExpFieldTestCase{"text", "Expiration Date", 7,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // General label, large maxlength.
+ ParseExpFieldTestCase{"text", "Expiration Date", 12,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+
+ // Unsupported maxlength, general label.
+ ParseExpFieldTestCase{"text", "Expiration Date", 3, UNKNOWN_TYPE},
+ // Unsupported maxlength, two digit year label.
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 3,
+ UNKNOWN_TYPE},
+ // Unsupported maxlength, four digit year label.
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 3,
+ UNKNOWN_TYPE},
+
+ // Two digit year, simple label.
+ ParseExpFieldTestCase{"text", "MM / YY", 0,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Two digit year, with slash (MM/YY).
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 0,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Two digit year, no slash (MMYY).
+ ParseExpFieldTestCase{"text", "Expiration Date (MMYY)", 4,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Two digit year, with slash and maxlength (MM/YY).
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 5,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Two digit year, with slash and large maxlength (MM/YY).
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 12,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+
+ // Four digit year, simple label.
+ ParseExpFieldTestCase{"text", "MM / YYYY", 0,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // Four digit year, with slash (MM/YYYY).
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 0,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // Four digit year, no slash (MMYYYY).
+ ParseExpFieldTestCase{"text", "Expiration Date (MMYYYY)", 6,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // Four digit year, with slash and maxlength (MM/YYYY).
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 7,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // Four digit year, with slash and large maxlength (MM/YYYY).
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 12,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+
+ // Four digit year label with restrictive maxlength (4).
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 4,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Four digit year label with restrictive maxlength (5).
+ ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 5,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+
+ // CC fields input_type="number"
+ // General label, no maxlength.
+ ParseExpFieldTestCase{"number", "Expiration Date", 0,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // General label, maxlength 4.
+ ParseExpFieldTestCase{"number", "Expiration Date", 4,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // General label, maxlength 5.
+ ParseExpFieldTestCase{"number", "Expiration Date", 5,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // General label, maxlength 6.
+ ParseExpFieldTestCase{"number", "Expiration Date", 6,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // General label, maxlength 7.
+ ParseExpFieldTestCase{"number", "Expiration Date", 7,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // General label, large maxlength.
+ ParseExpFieldTestCase{"number", "Expiration Date", 12,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+
+ // Unsupported maxlength, general label.
+ ParseExpFieldTestCase{"number", "Expiration Date", 3, UNKNOWN_TYPE},
+ // Unsupported maxlength, two digit year label.
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 3,
+ UNKNOWN_TYPE},
+ // Unsupported maxlength, four digit year label.
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 3,
+ UNKNOWN_TYPE},
+
+ // Two digit year, simple label.
+ ParseExpFieldTestCase{"number", "MM / YY", 0,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Two digit year, with slash (MM/YY).
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 0,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Two digit year, no slash (MMYY).
+ ParseExpFieldTestCase{"number", "Expiration Date (MMYY)", 4,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Two digit year, with slash and maxlength (MM/YY).
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 5,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Two digit year, with slash and large maxlength (MM/YY).
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 12,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+
+ // Four digit year, simple label.
+ ParseExpFieldTestCase{"number", "MM / YYYY", 0,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // Four digit year, with slash (MM/YYYY).
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 0,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // Four digit year, no slash (MMYYYY).
+ ParseExpFieldTestCase{"number", "Expiration Date (MMYYYY)", 6,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // Four digit year, with slash and maxlength (MM/YYYY).
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 7,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ // Four digit year, with slash and large maxlength (MM/YYYY).
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 12,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+
+ // Four digit year label with restrictive maxlength (4).
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 4,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ // Four digit year label with restrictive maxlength (5).
+ ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 5,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR})));
+
+TEST_P(CreditCardFieldTest, ParseCreditCardHolderNameWithCCFullName) {
AddTextFormFieldData("ccfullname", "Name", CREDIT_CARD_NAME_FULL);
ClassifyAndVerify(ParseResult::PARSED);
}
// Verifies that <input type="month"> controls are able to be parsed correctly.
-TEST_F(CreditCardFieldTest, ParseMonthControl) {
+TEST_P(CreditCardFieldTest, ParseMonthControl) {
AddTextFormFieldData("ccnumber", "Card number:", CREDIT_CARD_NUMBER);
AddFormFieldData("month", "ccexp",
"Expiration date:", CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
@@ -430,7 +461,7 @@ TEST_F(CreditCardFieldTest, ParseMonthControl) {
// Verify that heuristics <input name="ccyear" maxlength="2"/> considers
// *maxlength* attribute while parsing 2 Digit expiration year.
-TEST_F(CreditCardFieldTest, ParseCreditCardExpYear_2DigitMaxLength) {
+TEST_P(CreditCardFieldTest, ParseCreditCardExpYear_2DigitMaxLength) {
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
AddTextFormFieldData("ccmonth", "Expiration Date", CREDIT_CARD_EXP_MONTH);
AddFormFieldDataWithLength("text", "ccyear", "Expiration Date", 2,
@@ -439,7 +470,7 @@ TEST_F(CreditCardFieldTest, ParseCreditCardExpYear_2DigitMaxLength) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(CreditCardFieldTest, ParseCreditCardNumberWithSplit) {
+TEST_P(CreditCardFieldTest, ParseCreditCardNumberWithSplit) {
FormFieldData field;
field.form_control_type = "text";
AddFormFieldDataWithLength("text", "card_number_q1", "Card Number", 4,
@@ -467,7 +498,7 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNumberWithSplit) {
EXPECT_EQ(list_[3]->credit_card_number_offset(), 12U);
}
-TEST_F(CreditCardFieldTest, ParseMultipleCreditCardNumbers) {
+TEST_P(CreditCardFieldTest, ParseMultipleCreditCardNumbers) {
AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL);
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
AddTextFormFieldData("confirm_card_number", "Confirm Card Number",
@@ -478,7 +509,7 @@ TEST_F(CreditCardFieldTest, ParseMultipleCreditCardNumbers) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(CreditCardFieldTest, ParseFirstAndLastNames) {
+TEST_P(CreditCardFieldTest, ParseFirstAndLastNames) {
AddTextFormFieldData("cc-fname", "First Name on Card",
CREDIT_CARD_NAME_FIRST);
AddTextFormFieldData("cc-lname", "Last Name", CREDIT_CARD_NAME_LAST);
@@ -489,7 +520,7 @@ TEST_F(CreditCardFieldTest, ParseFirstAndLastNames) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(CreditCardFieldTest, ParseConsecutiveCvc) {
+TEST_P(CreditCardFieldTest, ParseConsecutiveCvc) {
AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL);
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH);
@@ -502,7 +533,7 @@ TEST_F(CreditCardFieldTest, ParseConsecutiveCvc) {
ClassifyAndVerifyWithMultipleParses();
}
-TEST_F(CreditCardFieldTest, ParseNonConsecutiveCvc) {
+TEST_P(CreditCardFieldTest, ParseNonConsecutiveCvc) {
AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL);
AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER);
AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH);
@@ -514,14 +545,14 @@ TEST_F(CreditCardFieldTest, ParseNonConsecutiveCvc) {
ClassifyAndVerifyWithMultipleParses();
}
-TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameNotCard) {
+TEST_P(CreditCardFieldTest, ParseCreditCardContextualNameNotCard) {
AddTextFormFieldData("accNum", "Account ID", UNKNOWN_TYPE);
AddTextFormFieldData("name", "Account Name", UNKNOWN_TYPE);
AddTextFormFieldData("toAcctNum", "Move to Account ID", UNKNOWN_TYPE);
ClassifyAndVerify(ParseResult::NOT_PARSED);
}
-TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameNotCardAcctMatch) {
+TEST_P(CreditCardFieldTest, ParseCreditCardContextualNameNotCardAcctMatch) {
// TODO(crbug.com/1167977): This should be not parseable, but waiting before
// changing kNameOnCardRe to use word boundaries.
AddTextFormFieldData("acctNum", "Account ID", CREDIT_CARD_NUMBER);
@@ -530,7 +561,7 @@ TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameNotCardAcctMatch) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameWithExpiration) {
+TEST_P(CreditCardFieldTest, ParseCreditCardContextualNameWithExpiration) {
AddTextFormFieldData("acctNum", "Account ID", CREDIT_CARD_NUMBER);
AddTextFormFieldData("name", "Account Name", CREDIT_CARD_NAME_FULL);
AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH);
@@ -538,7 +569,7 @@ TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameWithExpiration) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameWithVerification) {
+TEST_P(CreditCardFieldTest, ParseCreditCardContextualNameWithVerification) {
AddTextFormFieldData("acctNum", "Account ID", CREDIT_CARD_NUMBER);
AddTextFormFieldData("name", "Account Name", CREDIT_CARD_NAME_FULL);
AddTextFormFieldData("cvv", "Verification", CREDIT_CARD_VERIFICATION_CODE);
diff --git a/chromium/components/autofill/core/browser/form_parsing/email_field.cc b/chromium/components/autofill/core/browser/form_parsing/email_field.cc
index 868a71d5ac3..864642a4893 100644
--- a/chromium/components/autofill/core/browser/form_parsing/email_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/email_field.cc
@@ -13,10 +13,11 @@ namespace autofill {
// static
std::unique_ptr<FormField> EmailField::Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
AutofillField* field;
base::span<const MatchPatternRef> email_patterns =
- GetMatchPatterns("EMAIL_ADDRESS", page_language);
+ GetMatchPatterns("EMAIL_ADDRESS", page_language, pattern_source);
if (ParseFieldSpecifics(scanner, kEmailRe,
kDefaultMatchParamsWith<MatchFieldType::kEmail>,
email_patterns, &field, {log_manager, "kEmailRe"})) {
diff --git a/chromium/components/autofill/core/browser/form_parsing/email_field.h b/chromium/components/autofill/core/browser/form_parsing/email_field.h
index 32f3daa23fa..8fc4af8dde7 100644
--- a/chromium/components/autofill/core/browser/form_parsing/email_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/email_field.h
@@ -20,6 +20,7 @@ class EmailField : public FormField {
public:
static std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
explicit EmailField(const AutofillField* field);
diff --git a/chromium/components/autofill/core/browser/form_parsing/field_candidates.cc b/chromium/components/autofill/core/browser/form_parsing/field_candidates.cc
index af092e5fa47..a58adee710d 100644
--- a/chromium/components/autofill/core/browser/form_parsing/field_candidates.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/field_candidates.cc
@@ -5,32 +5,35 @@
#include "components/autofill/core/browser/form_parsing/field_candidates.h"
#include <algorithm>
-#include <vector>
+#include <array>
#include "base/logging.h"
+#include "components/autofill/core/common/autofill_features.h"
namespace autofill {
FieldCandidate::FieldCandidate(ServerFieldType field_type, float field_score)
: type(field_type), score(field_score) {}
-FieldCandidates::FieldCandidates() {}
+FieldCandidates::FieldCandidates() = default;
-FieldCandidates::FieldCandidates(const FieldCandidates& other) = default;
+FieldCandidates::FieldCandidates(FieldCandidates&& other) = default;
+FieldCandidates& FieldCandidates::operator=(FieldCandidates&& other) = default;
-FieldCandidates::~FieldCandidates() {}
+FieldCandidates::~FieldCandidates() = default;
void FieldCandidates::AddFieldCandidate(ServerFieldType type, float score) {
field_candidates_.emplace_back(type, score);
}
-// We currently select the type with the biggest sum.
+// We currently select a type with the maximum score sum.
ServerFieldType FieldCandidates::BestHeuristicType() const {
if (field_candidates_.empty())
return UNKNOWN_TYPE;
// Scores for each type. The index is their ServerFieldType enum value.
- std::vector<float> type_scores(MAX_VALID_FIELD_TYPE, 0.0f);
+ std::array<float, MAX_VALID_FIELD_TYPE> type_scores;
+ type_scores.fill(0.0f);
for (const auto& field_candidate : field_candidates_) {
VLOG(1) << "type: " << field_candidate.type
@@ -38,9 +41,8 @@ ServerFieldType FieldCandidates::BestHeuristicType() const {
type_scores[field_candidate.type] += field_candidate.score;
}
- const auto best_type_iter =
- std::max_element(type_scores.begin(), type_scores.end());
- const size_t index = std::distance(type_scores.begin(), best_type_iter);
+ const auto* best_type_iter = base::ranges::max_element(type_scores);
+ const size_t index = std::distance(type_scores.cbegin(), best_type_iter);
return ToSafeServerFieldType(index, NO_SERVER_DATA);
}
diff --git a/chromium/components/autofill/core/browser/form_parsing/field_candidates.h b/chromium/components/autofill/core/browser/form_parsing/field_candidates.h
index e34340338e4..46bb340501b 100644
--- a/chromium/components/autofill/core/browser/form_parsing/field_candidates.h
+++ b/chromium/components/autofill/core/browser/form_parsing/field_candidates.h
@@ -10,16 +10,10 @@
#include "base/containers/flat_map.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/common/unique_ids.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace autofill {
-enum class PredictionSource {
- kDefaultHeuristics,
- kExperimentalHeuristics,
- kNextGenHeuristics,
- kMaxValue = kNextGenHeuristics
-};
-
// Represents a possible type for a given field.
struct FieldCandidate {
FieldCandidate(ServerFieldType field_type, float field_score);
@@ -37,7 +31,8 @@ struct FieldCandidate {
class FieldCandidates {
public:
FieldCandidates();
- FieldCandidates(const FieldCandidates& other);
+ FieldCandidates(FieldCandidates&& other);
+ FieldCandidates& operator=(FieldCandidates&& other);
~FieldCandidates();
// Includes a possible |type| for a given field.
@@ -52,13 +47,6 @@ class FieldCandidates {
// Determines the best type based on the current possible types.
ServerFieldType BestHeuristicType() const;
- absl::optional<ServerFieldType> GetHypotheticalType(
- PredictionSource prediction_source) const {
- DCHECK_NE(prediction_source, PredictionSource::kDefaultHeuristics);
- // TODO(crbug.com/1310255): Implement experimental types.
- return absl::nullopt;
- }
-
private:
// Internal storage for all the possible types for a given field.
std::vector<FieldCandidate> field_candidates_;
diff --git a/chromium/components/autofill/core/browser/form_parsing/form_field.cc b/chromium/components/autofill/core/browser/form_parsing/form_field.cc
index d403ebdf81b..9cd4d3699a9 100644
--- a/chromium/components/autofill/core/browser/form_parsing/form_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/form_field.cc
@@ -51,47 +51,50 @@ FieldCandidatesMap FormField::ParseFormFields(
const std::vector<std::unique_ptr<AutofillField>>& fields,
const LanguageCode& page_language,
bool is_form_tag,
+ PatternSource pattern_source,
LogManager* log_manager) {
std::vector<AutofillField*> processed_fields = RemoveCheckableFields(fields);
FieldCandidatesMap field_candidates;
// Email pass.
ParseFormFieldsPass(EmailField::Parse, processed_fields, &field_candidates,
- page_language, log_manager);
+ page_language, pattern_source, log_manager);
const size_t email_count = field_candidates.size();
// Merchant promo code pass.
ParseFormFieldsPass(MerchantPromoCodeField::Parse, processed_fields,
- &field_candidates, page_language, log_manager);
+ &field_candidates, page_language, pattern_source,
+ log_manager);
const size_t promo_code_count = field_candidates.size() - email_count;
// Phone pass.
ParseFormFieldsPass(PhoneField::Parse, processed_fields, &field_candidates,
- page_language, log_manager);
+ page_language, pattern_source, log_manager);
// Travel pass.
ParseFormFieldsPass(TravelField::Parse, processed_fields, &field_candidates,
- page_language, log_manager);
+ page_language, pattern_source, log_manager);
// Address pass.
- ParseFormFieldsPass(autofill::AddressField::Parse, processed_fields,
- &field_candidates, page_language, log_manager);
+ ParseFormFieldsPass(AddressField::Parse, processed_fields, &field_candidates,
+ page_language, pattern_source, log_manager);
// Credit card pass.
ParseFormFieldsPass(CreditCardField::Parse, processed_fields,
- &field_candidates, page_language, log_manager);
+ &field_candidates, page_language, pattern_source,
+ log_manager);
// Price pass.
ParseFormFieldsPass(PriceField::Parse, processed_fields, &field_candidates,
- page_language, log_manager);
+ page_language, pattern_source, log_manager);
// Name pass.
ParseFormFieldsPass(NameField::Parse, processed_fields, &field_candidates,
- page_language, log_manager);
+ page_language, pattern_source, log_manager);
// Search pass.
ParseFormFieldsPass(SearchField::Parse, processed_fields, &field_candidates,
- page_language, log_manager);
+ page_language, pattern_source, log_manager);
size_t fillable_fields = 0;
if (base::FeatureList::IsEnabled(features::kAutofillFixFillableFieldTypes)) {
@@ -148,13 +151,15 @@ FieldCandidatesMap FormField::ParseFormFieldsForPromoCodes(
const std::vector<std::unique_ptr<AutofillField>>& fields,
const LanguageCode& page_language,
bool is_form_tag,
+ PatternSource pattern_source,
LogManager* log_manager) {
std::vector<AutofillField*> processed_fields = RemoveCheckableFields(fields);
FieldCandidatesMap field_candidates;
// Merchant promo code pass.
ParseFormFieldsPass(MerchantPromoCodeField::Parse, processed_fields,
- &field_candidates, page_language, log_manager);
+ &field_candidates, page_language, pattern_source,
+ log_manager);
return field_candidates;
}
@@ -321,7 +326,9 @@ bool FormField::Match(const AutofillField* field,
bool found_match = false;
base::StringPiece match_type_string;
base::StringPiece16 value;
- std::u16string match;
+ std::vector<std::u16string> matches;
+ std::vector<std::u16string>* capture_destination =
+ logging.log_manager ? &matches : nullptr;
// TODO(crbug/1165780): Remove once shared labels are launched.
const std::u16string& label =
@@ -332,26 +339,38 @@ bool FormField::Match(const AutofillField* field,
const std::u16string& name = field->parseable_name();
- if (match_type.attributes.contains(MatchAttribute::kLabel) &&
- MatchesPattern(label, pattern, &match)) {
+ const bool match_label =
+ match_type.attributes.contains(MatchAttribute::kLabel);
+ if (match_label && MatchesPattern(label, pattern, capture_destination)) {
found_match = true;
match_type_string = "Match in label";
value = label;
} else if (match_type.attributes.contains(MatchAttribute::kName) &&
- MatchesPattern(name, pattern, &match)) {
+ MatchesPattern(name, pattern, capture_destination)) {
found_match = true;
match_type_string = "Match in name";
value = name;
+ } else if (match_label &&
+ base::FeatureList::IsEnabled(
+ features::kAutofillConsiderPlaceholderForParsing) &&
+ MatchesPattern(field->placeholder, pattern, capture_destination)) {
+ // TODO(crbug.com/1317961): The label and placeholder cases should logically
+ // be grouped together. Placeholder is currently last, because for the finch
+ // study we want the group assignment to happen as late as possible.
+ // Reorder once the change is rolled out.
+ found_match = true;
+ match_type_string = "Match in placeholder";
+ value = field->placeholder;
}
if (found_match && logging.log_manager) {
LogBuffer table_rows;
table_rows << Tr{} << "Match type:" << match_type_string;
table_rows << Tr{} << "RegEx:" << logging.regex_name;
- table_rows << Tr{} << "Value: " << HighlightValue(value, match);
+ table_rows << Tr{} << "Value: " << HighlightValue(value, matches[0]);
// The matched substring is reported once more as the highlighting is not
// particularly copy&paste friendly.
- table_rows << Tr{} << "Matched substring: " << match;
+ table_rows << Tr{} << "Matched substring: " << matches[0];
logging.log_manager->Log()
<< LoggingScope::kParsing << LogMessage::kLocalHeuristicRegExMatched
<< Tag{"table"} << std::move(table_rows) << CTag{"table"};
@@ -365,11 +384,12 @@ void FormField::ParseFormFieldsPass(ParseFunction parse,
const std::vector<AutofillField*>& fields,
FieldCandidatesMap* field_candidates,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
AutofillScanner scanner(fields);
while (!scanner.IsEnd()) {
std::unique_ptr<FormField> form_field =
- parse(&scanner, page_language, log_manager);
+ parse(&scanner, page_language, pattern_source, log_manager);
if (form_field == nullptr) {
scanner.Advance();
} else {
diff --git a/chromium/components/autofill/core/browser/form_parsing/form_field.h b/chromium/components/autofill/core/browser/form_parsing/form_field.h
index 36685e51b96..12af574a32e 100644
--- a/chromium/components/autofill/core/browser/form_parsing/form_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/form_field.h
@@ -50,6 +50,7 @@ class FormField {
const std::vector<std::unique_ptr<AutofillField>>& fields,
const LanguageCode& page_language,
bool is_form_tag,
+ PatternSource pattern_source,
LogManager* log_manager = nullptr);
// Looks for a promo code field in |fields|. Each field has a derived unique
@@ -58,6 +59,7 @@ class FormField {
const std::vector<std::unique_ptr<AutofillField>>& fields,
const LanguageCode& page_language,
bool is_form_tag,
+ PatternSource pattern_source,
LogManager* log_manager = nullptr);
#if defined(UNIT_TEST)
@@ -137,6 +139,7 @@ class FormField {
typedef std::unique_ptr<FormField> ParseFunction(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
static bool ParseFieldSpecificsWithNewPatterns(
@@ -189,7 +192,8 @@ class FormField {
const std::vector<AutofillField*>& fields,
FieldCandidatesMap* field_candidates,
const LanguageCode& page_language,
- LogManager* log_manager = nullptr);
+ PatternSource pattern_source,
+ LogManager* log_manager);
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_parsing/form_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/form_field_unittest.cc
index 7355e2c635d..60c40e83b39 100644
--- a/chromium/components/autofill/core/browser/form_parsing/form_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/form_field_unittest.cc
@@ -9,6 +9,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/form_parsing/buildflags.h"
#include "components/autofill/core/browser/form_parsing/form_field.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/autofill_features.h"
@@ -33,7 +34,40 @@ void SetFieldLabels(AutofillField* field, const std::u16string& label) {
} // namespace
-TEST(FormFieldTest, Match) {
+class FormFieldTest
+ : public testing::TestWithParam<std::tuple<bool, PatternSource>> {
+ public:
+ FormFieldTest() {
+ scoped_feature_list_.InitWithFeatureState(
+ features::kAutofillParsingPatternProvider,
+ enable_parsing_pattern_provider());
+ }
+ FormFieldTest(const FormFieldTest&) = delete;
+ FormFieldTest& operator=(const FormFieldTest&) = delete;
+ ~FormFieldTest() override = default;
+
+ bool enable_parsing_pattern_provider() const {
+ return std::get<0>(GetParam());
+ }
+
+ PatternSource pattern_source() const { return std::get<1>(GetParam()); }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(FormFieldTest,
+ FormFieldTest,
+ ::testing::Combine(::testing::Bool(),
+ ::testing::Values(
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ PatternSource::kDefault,
+ PatternSource::kExperimental,
+ PatternSource::kNextGen,
+#endif
+ PatternSource::kLegacy)));
+
+TEST_P(FormFieldTest, Match) {
constexpr MatchParams kMatchLabel{{MatchAttribute::kLabel}, {}};
AutofillField field;
@@ -116,7 +150,7 @@ TEST(FormFieldTest, Match) {
}
// Test that we ignore checkable elements.
-TEST(FormFieldTest, ParseFormFields) {
+TEST_P(FormFieldTest, ParseFormFields) {
std::vector<std::unique_ptr<AutofillField>> fields;
FormFieldData field_data;
field_data.form_control_type = "text";
@@ -129,8 +163,10 @@ TEST(FormFieldTest, ParseFormFields) {
// Does not parse since there are only field and it's checkable.
// An empty page_language means the language is unknown and patterns of all
// languages are used.
- EXPECT_TRUE(
- FormField::ParseFormFields(fields, LanguageCode(""), true).empty());
+ EXPECT_TRUE(FormField::ParseFormFields(fields, LanguageCode(""),
+ /*is_form_tag=*/true, pattern_source(),
+ /*log_manager=*/nullptr)
+ .empty());
// reset |is_checkable| to false.
field_data.check_status = FormFieldData::CheckStatus::kNotCheckable;
@@ -140,7 +176,10 @@ TEST(FormFieldTest, ParseFormFields) {
// Parse a single address line 1 field.
ASSERT_EQ(0u,
- FormField::ParseFormFields(fields, LanguageCode(""), true).size());
+ FormField::ParseFormFields(fields, LanguageCode(""),
+ /*is_form_tag=*/true, pattern_source(),
+ /*log_manager=*/nullptr)
+ .size());
// Parses address line 1 and 2.
field_data.label = u"Address line2";
@@ -150,12 +189,15 @@ TEST(FormFieldTest, ParseFormFields) {
// An empty page_language means the language is unknown and patterns of
// all languages are used.
ASSERT_EQ(0u,
- FormField::ParseFormFields(fields, LanguageCode(""), true).size());
+ FormField::ParseFormFields(fields, LanguageCode(""),
+ /*is_form_tag=*/true, pattern_source(),
+ /*log_manager=*/nullptr)
+ .size());
}
// Test that the minimum number of required fields for the heuristics considers
// whether a field is actually fillable.
-TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) {
+TEST_P(FormFieldTest, ParseFormFieldEnforceMinFillableFields) {
std::vector<std::unique_ptr<AutofillField>> fields;
FormFieldData field_data;
field_data.form_control_type = "text";
@@ -172,7 +214,10 @@ TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) {
// An empty page_language means the language is unknown and patterns of all
// languages are used.
EXPECT_EQ(0u,
- FormField::ParseFormFields(fields, LanguageCode(""), true).size());
+ FormField::ParseFormFields(fields, LanguageCode(""),
+ /*is_form_tag=*/true, pattern_source(),
+ /*log_manager=*/nullptr)
+ .size());
field_data.label = u"Search";
field_data.unique_renderer_id = MakeFieldRendererId();
@@ -185,8 +230,10 @@ TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) {
feature_list.InitAndDisableFeature(kAutofillFixFillableFieldTypes);
// An empty page_language means the language is unknown and patterns of all
// languages are used.
- EXPECT_EQ(
- 3u, FormField::ParseFormFields(fields, LanguageCode(""), true).size());
+ EXPECT_EQ(3u, FormField::ParseFormFields(
+ fields, LanguageCode(""), /*is_form_tag=*/true,
+ pattern_source(), /*log_manager=*/nullptr)
+ .size());
}
// With the fix, we don't parse the form because search fields are not
@@ -196,15 +243,18 @@ TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) {
feature_list.InitAndEnableFeature(kAutofillFixFillableFieldTypes);
// An empty page_language means the language is unknown and patterns of all
// languages are used.
- const FieldCandidatesMap field_candidates_map =
- FormField::ParseFormFields(fields, LanguageCode(""), true);
- EXPECT_EQ(
- 0u, FormField::ParseFormFields(fields, LanguageCode(""), true).size());
+ const FieldCandidatesMap field_candidates_map = FormField::ParseFormFields(
+ fields, LanguageCode(""), /*is_form_tag=*/true, pattern_source(),
+ /*log_manager=*/nullptr);
+ EXPECT_EQ(0u, FormField::ParseFormFields(
+ fields, LanguageCode(""), /*is_form_tag=*/true,
+ pattern_source(), /*log_manager=*/nullptr)
+ .size());
}
}
// Test that the parseable label is used when the feature is enabled.
-TEST(FormFieldTest, TestParseableLabels) {
+TEST_P(FormFieldTest, TestParseableLabels) {
FormFieldData field_data;
field_data.form_control_type = "text";
@@ -229,7 +279,7 @@ TEST(FormFieldTest, TestParseableLabels) {
}
// Test that |ParseFormFieldsForPromoCodes| parses single field promo codes.
-TEST(FormFieldTest, ParseFormFieldsForPromoCodes) {
+TEST_P(FormFieldTest, ParseFormFieldsForPromoCodes) {
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeature(
features::kAutofillParseMerchantPromoCodeFields);
@@ -243,9 +293,10 @@ TEST(FormFieldTest, ParseFormFieldsForPromoCodes) {
field_data.unique_renderer_id = MakeFieldRendererId();
fields.push_back(std::make_unique<AutofillField>(field_data));
- EXPECT_EQ(1u, FormField::ParseFormFieldsForPromoCodes(fields,
- LanguageCode(""), true)
- .size());
+ EXPECT_EQ(
+ 1u, FormField::ParseFormFieldsForPromoCodes(
+ fields, LanguageCode(""), /*is_form_tag=*/true, pattern_source())
+ .size());
// Don't parse other fields.
field_data.label = u"Address line 1";
@@ -253,8 +304,9 @@ TEST(FormFieldTest, ParseFormFieldsForPromoCodes) {
fields.push_back(std::make_unique<AutofillField>(field_data));
// Still only the promo code field should be parsed.
- EXPECT_EQ(1u, FormField::ParseFormFieldsForPromoCodes(fields,
- LanguageCode(""), true)
- .size());
+ EXPECT_EQ(
+ 1u, FormField::ParseFormFieldsForPromoCodes(
+ fields, LanguageCode(""), /*is_form_tag=*/true, pattern_source())
+ .size());
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc
index 0929c7eca41..aa18677c381 100644
--- a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc
@@ -15,6 +15,7 @@ namespace autofill {
std::unique_ptr<FormField> MerchantPromoCodeField::Parse(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
if (!base::FeatureList::IsEnabled(
features::kAutofillParseMerchantPromoCodeFields)) {
@@ -23,7 +24,7 @@ std::unique_ptr<FormField> MerchantPromoCodeField::Parse(
AutofillField* field;
base::span<const MatchPatternRef> merchant_promo_code_patterns =
- GetMatchPatterns("MERCHANT_PROMO_CODE", page_language);
+ GetMatchPatterns("MERCHANT_PROMO_CODE", page_language, pattern_source);
if (ParseFieldSpecifics(scanner, kMerchantPromoCodeRe,
kDefaultMatchParamsWith<MatchFieldType::kNumber,
diff --git a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h
index 8d579af12ca..1b918aea03f 100644
--- a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h
@@ -25,6 +25,7 @@ class MerchantPromoCodeField : public FormField {
public:
static std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
explicit MerchantPromoCodeField(const AutofillField* field);
diff --git a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc
index f835c3d96f7..f16d20fe52e 100644
--- a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc
@@ -12,9 +12,11 @@ using base::ASCIIToUTF16;
namespace autofill {
-class MerchantPromoCodeFieldTest : public FormFieldTest {
+class MerchantPromoCodeFieldTest
+ : public FormFieldTestBase,
+ public testing::TestWithParam<PatternProviderFeatureState> {
public:
- MerchantPromoCodeFieldTest() = default;
+ MerchantPromoCodeFieldTest() : FormFieldTestBase(GetParam()) {}
MerchantPromoCodeFieldTest(const MerchantPromoCodeFieldTest&) = delete;
MerchantPromoCodeFieldTest& operator=(const MerchantPromoCodeFieldTest&) =
delete;
@@ -25,17 +27,24 @@ class MerchantPromoCodeFieldTest : public FormFieldTest {
}
protected:
- base::test::ScopedFeatureList scoped_feature_list_;
-
std::unique_ptr<FormField> Parse(
AutofillScanner* scanner,
const LanguageCode& page_language = LanguageCode("en")) override {
- return MerchantPromoCodeField::Parse(scanner, page_language, nullptr);
+ return MerchantPromoCodeField::Parse(scanner, page_language,
+ GetActivePatternSource(),
+ /*log_manager=*/nullptr);
}
+
+ base::test::ScopedFeatureList scoped_feature_list_;
};
+INSTANTIATE_TEST_SUITE_P(
+ MerchantPromoCodeFieldTest,
+ MerchantPromoCodeFieldTest,
+ ::testing::ValuesIn(PatternProviderFeatureState::All()));
+
// Match promo(tion|tional)?[-_. ]*code
-TEST_F(MerchantPromoCodeFieldTest, ParsePromoCode) {
+TEST_P(MerchantPromoCodeFieldTest, ParsePromoCode) {
AddTextFormFieldData("Enter promo code here", "promoCodeField",
MERCHANT_PROMO_CODE);
@@ -43,7 +52,7 @@ TEST_F(MerchantPromoCodeFieldTest, ParsePromoCode) {
}
// Match promo(tion|tional)?[-_. ]*code
-TEST_F(MerchantPromoCodeFieldTest, ParsePromotionalCode) {
+TEST_P(MerchantPromoCodeFieldTest, ParsePromotionalCode) {
AddTextFormFieldData("Use the promotional code here", "promoCodeField",
MERCHANT_PROMO_CODE);
@@ -51,7 +60,7 @@ TEST_F(MerchantPromoCodeFieldTest, ParsePromotionalCode) {
}
// Match promo(tion|tional)?[-_. ]*code
-TEST_F(MerchantPromoCodeFieldTest, ParsePromoCodeWithPrefixAndSuffix) {
+TEST_P(MerchantPromoCodeFieldTest, ParsePromoCodeWithPrefixAndSuffix) {
AddTextFormFieldData("mypromocodefield", "promoCodeField",
MERCHANT_PROMO_CODE);
@@ -59,7 +68,7 @@ TEST_F(MerchantPromoCodeFieldTest, ParsePromoCodeWithPrefixAndSuffix) {
}
// Match coupon[-_. ]*code
-TEST_F(MerchantPromoCodeFieldTest, ParseCouponCode) {
+TEST_P(MerchantPromoCodeFieldTest, ParseCouponCode) {
AddTextFormFieldData("Enter new coupon__code", "couponCodeField",
MERCHANT_PROMO_CODE);
@@ -67,7 +76,7 @@ TEST_F(MerchantPromoCodeFieldTest, ParseCouponCode) {
}
// Match gift[-_. ]*code
-TEST_F(MerchantPromoCodeFieldTest, ParseGiftCode) {
+TEST_P(MerchantPromoCodeFieldTest, ParseGiftCode) {
AddTextFormFieldData("Check out with gift.codes", "giftCodeField",
MERCHANT_PROMO_CODE);
@@ -75,14 +84,14 @@ TEST_F(MerchantPromoCodeFieldTest, ParseGiftCode) {
}
// Match discount[-_. ]*code
-TEST_F(MerchantPromoCodeFieldTest, ParseDiscountCode) {
+TEST_P(MerchantPromoCodeFieldTest, ParseDiscountCode) {
AddTextFormFieldData("Check out with discount-code", "discountCodeField",
MERCHANT_PROMO_CODE);
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(MerchantPromoCodeFieldTest, ParseNonPromoCode) {
+TEST_P(MerchantPromoCodeFieldTest, ParseNonPromoCode) {
// Regex relies on "promo/coupon/gift" + "code" together.
AddTextFormFieldData("Field for gift card or promo details", "otherField",
UNKNOWN_TYPE);
@@ -90,7 +99,7 @@ TEST_F(MerchantPromoCodeFieldTest, ParseNonPromoCode) {
ClassifyAndVerify(ParseResult::NOT_PARSED);
}
-TEST_F(MerchantPromoCodeFieldTest, ParsePromoCodeFlagOff) {
+TEST_P(MerchantPromoCodeFieldTest, ParsePromoCodeFlagOff) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
features::kAutofillParseMerchantPromoCodeFields);
diff --git a/chromium/components/autofill/core/browser/form_parsing/name_field.cc b/chromium/components/autofill/core/browser/form_parsing/name_field.cc
index 339b6d48706..858750b27bb 100644
--- a/chromium/components/autofill/core/browser/form_parsing/name_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/name_field.cc
@@ -24,6 +24,7 @@ class FullNameField : public NameField {
public:
static std::unique_ptr<FullNameField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
explicit FullNameField(AutofillField* field);
@@ -44,10 +45,12 @@ class FirstTwoLastNamesField : public NameField {
static std::unique_ptr<FirstTwoLastNamesField> ParseComponentNames(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
static std::unique_ptr<FirstTwoLastNamesField> Parse(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
FirstTwoLastNamesField(const FirstTwoLastNamesField&) = delete;
@@ -75,6 +78,7 @@ class FirstLastNameField : public NameField {
static std::unique_ptr<FirstLastNameField> ParseNameSurnameLabelSequence(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
// Tries to match a series of fields with a shared label: The first field
@@ -83,6 +87,7 @@ class FirstLastNameField : public NameField {
static std::unique_ptr<FirstLastNameField> ParseSharedNameLabelSequence(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
// Tries to match a series of fields with patterns that are specific to the
@@ -91,6 +96,7 @@ class FirstLastNameField : public NameField {
static std::unique_ptr<FirstLastNameField> ParseSpecificComponentSequence(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
// Probes the matching strategies defined above. Returns the result of the
@@ -98,6 +104,7 @@ class FirstLastNameField : public NameField {
static std::unique_ptr<FirstLastNameField> Parse(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
FirstLastNameField(const FirstLastNameField&) = delete;
@@ -121,6 +128,7 @@ class FirstLastNameField : public NameField {
// static
std::unique_ptr<FormField> NameField::Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
if (scanner->IsEnd())
return nullptr;
@@ -129,12 +137,18 @@ std::unique_ptr<FormField> NameField::Parse(AutofillScanner* scanner,
// more specific.
std::unique_ptr<FormField> field;
if (!field && base::FeatureList::IsEnabled(
- features::kAutofillEnableSupportForMoreStructureInNames))
- field = FirstTwoLastNamesField::Parse(scanner, page_language, log_manager);
- if (!field)
- field = FirstLastNameField::Parse(scanner, page_language, log_manager);
- if (!field)
- field = FullNameField::Parse(scanner, page_language, log_manager);
+ features::kAutofillEnableSupportForMoreStructureInNames)) {
+ field = FirstTwoLastNamesField::Parse(scanner, page_language,
+ pattern_source, log_manager);
+ }
+ if (!field) {
+ field = FirstLastNameField::Parse(scanner, page_language, pattern_source,
+ log_manager);
+ }
+ if (!field) {
+ field = FullNameField::Parse(scanner, page_language, pattern_source,
+ log_manager);
+ }
return field;
}
@@ -146,13 +160,14 @@ void NameField::AddClassifications(FieldCandidatesMap* field_candidates) const {
std::unique_ptr<FullNameField> FullNameField::Parse(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
// Exclude e.g. "username" or "nickname" fields.
scanner->SaveCursor();
base::span<const MatchPatternRef> name_ignored_patterns =
- GetMatchPatterns("NAME_IGNORED", page_language);
+ GetMatchPatterns("NAME_IGNORED", page_language, pattern_source);
base::span<const MatchPatternRef> address_name_ignored_patterns =
- GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language);
+ GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
bool should_ignore =
ParseField(scanner, kNameIgnoredRe, name_ignored_patterns, nullptr,
{log_manager, "kNameIgnoredRe"}) ||
@@ -168,7 +183,7 @@ std::unique_ptr<FullNameField> FullNameField::Parse(
AutofillField* field = nullptr;
base::span<const MatchPatternRef> name_patterns =
- GetMatchPatterns("FULL_NAME", page_language);
+ GetMatchPatterns("FULL_NAME", page_language, pattern_source);
if (ParseField(scanner, kFullNameRe, name_patterns, &field,
{log_manager, "kFullNameRe"}))
return std::make_unique<FullNameField>(field);
@@ -189,32 +204,35 @@ FirstTwoLastNamesField::FirstTwoLastNamesField() = default;
std::unique_ptr<FirstTwoLastNamesField> FirstTwoLastNamesField::Parse(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
- return ParseComponentNames(scanner, page_language, log_manager);
+ return ParseComponentNames(scanner, page_language, pattern_source,
+ log_manager);
}
// static
std::unique_ptr<FirstTwoLastNamesField>
FirstTwoLastNamesField::ParseComponentNames(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
auto v = base::WrapUnique(new FirstTwoLastNamesField());
scanner->SaveCursor();
base::span<const MatchPatternRef> honorific_prefix_patterns =
- GetMatchPatterns("HONORIFIC_PREFIX", page_language);
+ GetMatchPatterns("HONORIFIC_PREFIX", page_language, pattern_source);
base::span<const MatchPatternRef> name_ignored_patterns =
- GetMatchPatterns("NAME_IGNORED", page_language);
+ GetMatchPatterns("NAME_IGNORED", page_language, pattern_source);
base::span<const MatchPatternRef> address_name_ignored_patterns =
- GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language);
+ GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
base::span<const MatchPatternRef> first_name_patterns =
- GetMatchPatterns("FIRST_NAME", page_language);
+ GetMatchPatterns("FIRST_NAME", page_language, pattern_source);
base::span<const MatchPatternRef> middle_name_patterns =
- GetMatchPatterns("MIDDLE_NAME", page_language);
+ GetMatchPatterns("MIDDLE_NAME", page_language, pattern_source);
base::span<const MatchPatternRef> first_last_name_patterns =
- GetMatchPatterns("LAST_NAME_FIRST", page_language);
+ GetMatchPatterns("LAST_NAME_FIRST", page_language, pattern_source);
base::span<const MatchPatternRef> second_last_name_patterns =
- GetMatchPatterns("LAST_NAME_SECOND", page_language);
+ GetMatchPatterns("LAST_NAME_SECOND", page_language, pattern_source);
// Allow name fields to appear in any order.
while (!scanner->IsEnd()) {
@@ -303,6 +321,7 @@ std::unique_ptr<FirstLastNameField>
FirstLastNameField::ParseNameSurnameLabelSequence(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
// Some pages have a generic name label that corresponds to a first name
// followed by a last name label.
@@ -310,16 +329,17 @@ FirstLastNameField::ParseNameSurnameLabelSequence(
auto v = base::WrapUnique(new FirstLastNameField());
base::span<const MatchPatternRef> name_specific_patterns =
- GetMatchPatterns("NAME_GENERIC", page_language);
-
+ GetMatchPatterns("NAME_GENERIC", page_language, pattern_source);
+ base::span<const MatchPatternRef> middle_name_patterns =
+ GetMatchPatterns("MIDDLE_NAME", page_language, pattern_source);
base::span<const MatchPatternRef> last_name_patterns =
- GetMatchPatterns("LAST_NAME", page_language);
+ GetMatchPatterns("LAST_NAME", page_language, pattern_source);
// Check that the field should not be ignored.
base::span<const MatchPatternRef> name_ignored_patterns =
- GetMatchPatterns("NAME_IGNORED", page_language);
+ GetMatchPatterns("NAME_IGNORED", page_language, pattern_source);
base::span<const MatchPatternRef> address_name_ignored_patterns =
- GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language);
+ GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
scanner->SaveCursor();
bool should_ignore =
@@ -335,10 +355,14 @@ FirstLastNameField::ParseNameSurnameLabelSequence(
return nullptr;
if (ParseField(scanner, kNameGenericRe, name_specific_patterns,
- &v->first_name_, {log_manager, "kNameGenericRe"}) &&
- ParseField(scanner, kLastNameRe, last_name_patterns, &v->last_name_,
- {log_manager, "kLastNameRe"})) {
- return v;
+ &v->first_name_, {log_manager, "kNameGenericRe"})) {
+ // Check for an optional middle name field.
+ ParseField(scanner, kMiddleNameRe, middle_name_patterns, &v->middle_name_,
+ {log_manager, "kMiddleNameRe"});
+ if (ParseField(scanner, kLastNameRe, last_name_patterns, &v->last_name_,
+ {log_manager, "kLastNameRe"})) {
+ return v;
+ }
}
scanner->Rewind();
@@ -349,6 +373,7 @@ std::unique_ptr<FirstLastNameField>
FirstLastNameField::ParseSharedNameLabelSequence(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
// Some pages (e.g. Overstock_comBilling.html, SmithsonianCheckout.html)
// have the label "Name" followed by two or three text fields.
@@ -357,7 +382,7 @@ FirstLastNameField::ParseSharedNameLabelSequence(
AutofillField* next = nullptr;
base::span<const MatchPatternRef> name_specific_patterns =
- GetMatchPatterns("NAME_GENERIC", page_language);
+ GetMatchPatterns("NAME_GENERIC", page_language, pattern_source);
if (ParseField(scanner, kNameGenericRe, name_specific_patterns,
&v->first_name_, {log_manager, "kNameGenericRe"}) &&
@@ -383,6 +408,7 @@ std::unique_ptr<FirstLastNameField>
FirstLastNameField::ParseSpecificComponentSequence(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
auto v = base::WrapUnique(new FirstLastNameField());
scanner->SaveCursor();
@@ -400,19 +426,19 @@ FirstLastNameField::ParseSpecificComponentSequence(
// Allow name fields to appear in any order.
base::span<const MatchPatternRef> honorific_prefix_patterns =
- GetMatchPatterns("HONORIFIC_PREFIX", page_language);
+ GetMatchPatterns("HONORIFIC_PREFIX", page_language, pattern_source);
base::span<const MatchPatternRef> name_ignored_patterns =
- GetMatchPatterns("NAME_IGNORED", page_language);
+ GetMatchPatterns("NAME_IGNORED", page_language, pattern_source);
base::span<const MatchPatternRef> address_name_ignored_patterns =
- GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language);
+ GetMatchPatterns("ADDRESS_NAME_IGNORED", page_language, pattern_source);
base::span<const MatchPatternRef> first_name_patterns =
- GetMatchPatterns("FIRST_NAME", page_language);
+ GetMatchPatterns("FIRST_NAME", page_language, pattern_source);
base::span<const MatchPatternRef> middle_name_initial_patterns =
- GetMatchPatterns("MIDDLE_INITIAL", page_language);
+ GetMatchPatterns("MIDDLE_INITIAL", page_language, pattern_source);
base::span<const MatchPatternRef> middle_name_patterns =
- GetMatchPatterns("MIDDLE_NAME", page_language);
+ GetMatchPatterns("MIDDLE_NAME", page_language, pattern_source);
base::span<const MatchPatternRef> last_name_patterns =
- GetMatchPatterns("LAST_NAME", page_language);
+ GetMatchPatterns("LAST_NAME", page_language, pattern_source);
while (!scanner->IsEnd()) {
// Skip over address label fields, which can have misleading names
@@ -493,16 +519,19 @@ FirstLastNameField::ParseSpecificComponentSequence(
std::unique_ptr<FirstLastNameField> FirstLastNameField::Parse(
AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
- std::unique_ptr<FirstLastNameField> field =
- ParseSharedNameLabelSequence(scanner, page_language, log_manager);
+ std::unique_ptr<FirstLastNameField> field = ParseSharedNameLabelSequence(
+ scanner, page_language, pattern_source, log_manager);
if (!field && base::FeatureList::IsEnabled(
features::kAutofillEnableNameSurenameParsing)) {
- field = ParseNameSurnameLabelSequence(scanner, page_language, log_manager);
+ field = ParseNameSurnameLabelSequence(scanner, page_language,
+ pattern_source, log_manager);
}
if (!field) {
- field = ParseSpecificComponentSequence(scanner, page_language, log_manager);
+ field = ParseSpecificComponentSequence(scanner, page_language,
+ pattern_source, log_manager);
}
return field;
}
diff --git a/chromium/components/autofill/core/browser/form_parsing/name_field.h b/chromium/components/autofill/core/browser/form_parsing/name_field.h
index 1c7e6fefa35..d3b23f5c5da 100644
--- a/chromium/components/autofill/core/browser/form_parsing/name_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/name_field.h
@@ -23,6 +23,7 @@ class NameField : public FormField {
public:
static std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
NameField(const NameField&) = delete;
diff --git a/chromium/components/autofill/core/browser/form_parsing/name_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/name_field_unittest.cc
index 495d00d9bfd..db913d825ae 100644
--- a/chromium/components/autofill/core/browser/form_parsing/name_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/name_field_unittest.cc
@@ -16,9 +16,11 @@
namespace autofill {
-class NameFieldTest : public FormFieldTest {
+class NameFieldTest
+ : public FormFieldTestBase,
+ public testing::TestWithParam<PatternProviderFeatureState> {
public:
- NameFieldTest() = default;
+ NameFieldTest() : FormFieldTestBase(GetParam()) {}
NameFieldTest(const NameFieldTest&) = delete;
NameFieldTest& operator=(const NameFieldTest&) = delete;
@@ -26,11 +28,17 @@ class NameFieldTest : public FormFieldTest {
std::unique_ptr<FormField> Parse(
AutofillScanner* scanner,
const LanguageCode& page_language = LanguageCode("us")) override {
- return NameField::Parse(scanner, page_language, nullptr);
+ return NameField::Parse(scanner, page_language, GetActivePatternSource(),
+ /*log_manager=*/nullptr);
}
};
-TEST_F(NameFieldTest, FirstMiddleLast) {
+INSTANTIATE_TEST_SUITE_P(
+ NameFieldTest,
+ NameFieldTest,
+ ::testing::ValuesIn(PatternProviderFeatureState::All()));
+
+TEST_P(NameFieldTest, FirstMiddleLast) {
AddTextFormFieldData("First Name", "First", NAME_FIRST);
AddTextFormFieldData("Name Middle", "Middle", NAME_MIDDLE);
AddTextFormFieldData("Last Name", "Last", NAME_LAST);
@@ -38,7 +46,7 @@ TEST_F(NameFieldTest, FirstMiddleLast) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, FirstMiddleLast2) {
+TEST_P(NameFieldTest, FirstMiddleLast2) {
AddTextFormFieldData("firstName", "", NAME_FIRST);
AddTextFormFieldData("middleName", "", NAME_MIDDLE);
AddTextFormFieldData("lastName", "", NAME_LAST);
@@ -47,7 +55,7 @@ TEST_F(NameFieldTest, FirstMiddleLast2) {
}
// Test that a field for a honorific title is parsed correctly.
-TEST_F(NameFieldTest, HonorificPrefixFirstLast) {
+TEST_P(NameFieldTest, HonorificPrefixFirstLast) {
// With support for two last names, the parsing should find the first name
// field and the two last name fields.
// TODO(crbug.com/1098943): Remove once launched.
@@ -62,25 +70,37 @@ TEST_F(NameFieldTest, HonorificPrefixFirstLast) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, FirstLast) {
+TEST_P(NameFieldTest, FirstLast) {
AddTextFormFieldData("first_name", "", NAME_FIRST);
AddTextFormFieldData("last_name", "", NAME_LAST);
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, NameSurname) {
+TEST_P(NameFieldTest, NameSurname) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ features::kAutofillEnableNameSurenameParsing);
+
+ AddTextFormFieldData("name", "name", NAME_FIRST);
+ AddTextFormFieldData("surename", "surname", NAME_LAST);
+
+ ClassifyAndVerify(ParseResult::PARSED);
+}
+
+TEST_P(NameFieldTest, NameSurnameWithMiddleName) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kAutofillEnableNameSurenameParsing);
AddTextFormFieldData("name", "name", NAME_FIRST);
+ AddTextFormFieldData("middlename", "middlename", NAME_MIDDLE);
AddTextFormFieldData("surename", "surname", NAME_LAST);
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, NameSurname_DE) {
+TEST_P(NameFieldTest, NameSurname_DE) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kAutofillEnableNameSurenameParsing);
@@ -91,14 +111,14 @@ TEST_F(NameFieldTest, NameSurname_DE) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, FirstLast2) {
+TEST_P(NameFieldTest, FirstLast2) {
AddTextFormFieldData("first_name", "Name", NAME_FIRST);
AddTextFormFieldData("last_name", "Name", NAME_LAST);
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, FirstLastMiddleWithSpaces) {
+TEST_P(NameFieldTest, FirstLastMiddleWithSpaces) {
AddTextFormFieldData("fist_name", "First Name", NAME_FIRST);
AddTextFormFieldData("middle_name", "Middle Name", NAME_MIDDLE);
AddTextFormFieldData("last_name", "Last Name", NAME_LAST);
@@ -106,14 +126,14 @@ TEST_F(NameFieldTest, FirstLastMiddleWithSpaces) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, FirstLastEmpty) {
+TEST_P(NameFieldTest, FirstLastEmpty) {
AddTextFormFieldData("first_name", "Name", NAME_FIRST);
AddTextFormFieldData("last_name", "", NAME_LAST);
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, FirstMiddleLastEmpty) {
+TEST_P(NameFieldTest, FirstMiddleLastEmpty) {
AddTextFormFieldData("first_name", "Name", NAME_FIRST);
AddTextFormFieldData("middle_name", "", NAME_MIDDLE_INITIAL);
AddTextFormFieldData("last_name", "", NAME_LAST);
@@ -121,7 +141,7 @@ TEST_F(NameFieldTest, FirstMiddleLastEmpty) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, MiddleInitial) {
+TEST_P(NameFieldTest, MiddleInitial) {
AddTextFormFieldData("first_name", "Name", NAME_FIRST);
AddTextFormFieldData("middle_name", "MI", NAME_MIDDLE_INITIAL);
AddTextFormFieldData("last_name", "", NAME_LAST);
@@ -129,7 +149,7 @@ TEST_F(NameFieldTest, MiddleInitial) {
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(NameFieldTest, MiddleInitialNoLastName) {
+TEST_P(NameFieldTest, MiddleInitialNoLastName) {
AddTextFormFieldData("first_name", "First Name", UNKNOWN_TYPE);
AddTextFormFieldData("middle_name", "MI", UNKNOWN_TYPE);
@@ -138,7 +158,7 @@ TEST_F(NameFieldTest, MiddleInitialNoLastName) {
// Tests that a website with a first and second surname field is parsed
// correctly.
-TEST_F(NameFieldTest, HonorificPrefixAndFirstNameAndHispanicLastNames) {
+TEST_P(NameFieldTest, HonorificPrefixAndFirstNameAndHispanicLastNames) {
// With support for two last names, the parsing should find the first name
// field and the two last name fields.
// TODO(crbug.com/1098943): Remove once launched.
@@ -157,7 +177,7 @@ TEST_F(NameFieldTest, HonorificPrefixAndFirstNameAndHispanicLastNames) {
// Tests that a website with a first and second surname field is parsed
// correctly.
-TEST_F(NameFieldTest, FirstNameAndOptionalMiddleNameAndHispanicLastNames) {
+TEST_P(NameFieldTest, FirstNameAndOptionalMiddleNameAndHispanicLastNames) {
// With support for two last names, the parsing should find the first name
// field and the two last name fields.
base::test::ScopedFeatureList scoped_feature_list;
@@ -175,7 +195,7 @@ TEST_F(NameFieldTest, FirstNameAndOptionalMiddleNameAndHispanicLastNames) {
// This case is from the dell.com checkout page. The middle initial "mi" string
// came at the end following other descriptive text. http://crbug.com/45123.
-TEST_F(NameFieldTest, MiddleInitialAtEnd) {
+TEST_P(NameFieldTest, MiddleInitialAtEnd) {
AddTextFormFieldData("XXXnameXXXfirst", "", NAME_FIRST);
AddTextFormFieldData("XXXnameXXXmi", "", NAME_MIDDLE_INITIAL);
AddTextFormFieldData("XXXnameXXXlast", "", NAME_LAST);
@@ -184,7 +204,7 @@ TEST_F(NameFieldTest, MiddleInitialAtEnd) {
}
// Test the coverage of all found strings for first and second last names.
-TEST_F(NameFieldTest, HispanicLastNameRegexConverage) {
+TEST_P(NameFieldTest, HispanicLastNameRegexConverage) {
std::vector<std::u16string> first_last_name_strings = {
u"Primer apellido", u"apellidoPaterno", u"apellido_paterno",
u"first_surname", u"first surname", u"apellido1"};
@@ -217,7 +237,7 @@ TEST_F(NameFieldTest, HispanicLastNameRegexConverage) {
}
// Tests that address name is not misclassified as name or honorific prefix.
-TEST_F(NameFieldTest, NotAddressName) {
+TEST_P(NameFieldTest, NotAddressName) {
AddTextFormFieldData("name", "Identificação do Endereço", UNKNOWN_TYPE);
AddTextFormFieldData("title", "Adres Adı", UNKNOWN_TYPE);
@@ -225,7 +245,7 @@ TEST_F(NameFieldTest, NotAddressName) {
}
// Tests that contact name is classified as full name.
-TEST_F(NameFieldTest, ContactNameFull) {
+TEST_P(NameFieldTest, ContactNameFull) {
AddTextFormFieldData("contact", "Контактное лицо", NAME_FULL);
ClassifyAndVerify(ParseResult::PARSED);
diff --git a/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc
index 46d1e6d74da..e4930640356 100644
--- a/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc
@@ -3,10 +3,39 @@
// found in the LICENSE file.
#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h"
+#include "components/autofill/core/browser/form_parsing/buildflags.h"
+#include "components/autofill/core/common/autofill_features.h"
namespace autofill {
-FormFieldTestBase::FormFieldTestBase() = default;
+std::vector<PatternProviderFeatureState> PatternProviderFeatureState::All() {
+ return {
+ {.enable = false, .active_source = nullptr},
+ {.enable = true, .active_source = "legacy"},
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ {.enable = true, .active_source = "default"},
+ {.enable = true, .active_source = "experimental"},
+ {.enable = true, .active_source = "nextgen"},
+#endif
+ };
+}
+
+FormFieldTestBase::FormFieldTestBase(
+ PatternProviderFeatureState pattern_provider_feature_state) {
+ std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled;
+ std::vector<base::Feature> disabled;
+ if (pattern_provider_feature_state.enable) {
+ enabled.emplace_back(
+ features::kAutofillParsingPatternProvider,
+ base::FieldTrialParams{
+ {features::kAutofillParsingPatternActiveSource.name,
+ pattern_provider_feature_state.active_source}});
+ } else {
+ disabled.push_back(features::kAutofillParsingPatternProvider);
+ }
+ scoped_feature_list_.InitWithFeaturesAndParameters(enabled, disabled);
+}
+
FormFieldTestBase::~FormFieldTestBase() = default;
void FormFieldTestBase::AddFormFieldData(std::string control_type,
@@ -114,6 +143,4 @@ FieldRendererId FormFieldTestBase::MakeFieldRendererId() {
return FieldRendererId(++id_counter_);
}
-FormFieldTest::FormFieldTest() = default;
-FormFieldTest::~FormFieldTest() = default;
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.h b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.h
index 5f842e8aa9d..84f3cd9e217 100644
--- a/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.h
+++ b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.h
@@ -10,6 +10,7 @@
#include <vector>
#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_parsing/address_field.h"
@@ -28,12 +29,24 @@ enum class ParseResult {
kMaxValue = NOT_PARSED
};
+// Represents the intended state of features::kAutofillParsingPatternProvider.
+struct PatternProviderFeatureState {
+ // A list of all available configurations, depending on the build config.
+ static std::vector<PatternProviderFeatureState> All();
+
+ // Whether features::kAutofillParsingPatternProvider should be enabled.
+ bool enable = false;
+ // The desired value of features::kAutofillParsingPatternActiveSource.
+ const char* active_source = nullptr;
+};
+
class FormFieldTestBase {
public:
+ explicit FormFieldTestBase(
+ PatternProviderFeatureState pattern_provider_feature_state);
FormFieldTestBase(const FormFieldTestBase&) = delete;
- FormFieldTestBase();
- ~FormFieldTestBase();
FormFieldTestBase& operator=(const FormFieldTestBase&) = delete;
+ ~FormFieldTestBase();
protected:
// Add a field with |control_type|, the |name|, the |label| the expected
@@ -92,17 +105,10 @@ class FormFieldTestBase {
std::map<FieldGlobalId, ServerFieldType> expected_classifications_;
private:
+ base::test::ScopedFeatureList scoped_feature_list_;
uint64_t id_counter_ = 0;
};
-class FormFieldTest : public FormFieldTestBase, public testing::Test {
- public:
- FormFieldTest(const FormFieldTest&) = delete;
- FormFieldTest();
- ~FormFieldTest() override;
- FormFieldTest& operator=(const FormFieldTest&) = delete;
-};
-
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_PARSING_TEST_UTILS_H_
diff --git a/chromium/components/autofill/core/browser/form_parsing/phone_field.cc b/chromium/components/autofill/core/browser/form_parsing/phone_field.cc
index 911e5b0eafc..523d186dcb0 100644
--- a/chromium/components/autofill/core/browser/form_parsing/phone_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/phone_field.cc
@@ -4,13 +4,14 @@
#include "components/autofill/core/browser/form_parsing/phone_field.h"
-#include <string.h>
-
+#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include "base/check.h"
+#include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
@@ -51,11 +52,8 @@ std::u16string GetAreaRegex() {
} // namespace
-PhoneField::~PhoneField() {}
-
-// Phone field grammars - first matched grammar will be parsed. Grammars are
-// separated by { REGEX_SEPARATOR, FIELD_NONE, 0 }. Suffix and extension are
-// parsed separately unless they are necessary parts of the match.
+// Phone field grammars - first matched grammar will be parsed. Suffix and
+// extension are parsed separately unless they are necessary parts of the match.
// The following notation is used to describe the patterns:
// <cc> - country code field.
// <ac> - area code field.
@@ -64,90 +62,80 @@ PhoneField::~PhoneField() {}
// <ext> - extension.
// :N means field is limited to N characters, otherwise it is unlimited.
// (pattern <field>)? means pattern is optional and matched separately.
-const PhoneField::Parser PhoneField::kPhoneFieldGrammars[] = {
- // Country code: <cc> Area Code: <ac> Phone: <phone> (- <suffix>
- // (Ext: <ext>)?)?
- {REGEX_COUNTRY, FIELD_COUNTRY_CODE, 0},
- {REGEX_AREA, FIELD_AREA_CODE, 0},
- {REGEX_PHONE, FIELD_PHONE, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // \( <ac> \) <phone>:3 <suffix>:4 (Ext: <ext>)?
- {REGEX_AREA_NOTEXT, FIELD_AREA_CODE, 3},
- {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3},
- {REGEX_PHONE, FIELD_SUFFIX, 4},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <cc> <ac>:3 - <phone>:3 - <suffix>:4 (Ext: <ext>)?
- {REGEX_PHONE, FIELD_COUNTRY_CODE, 0},
- {REGEX_PHONE, FIELD_AREA_CODE, 3},
- {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3},
- {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <cc>:3 <ac>:3 <phone>:3 <suffix>:4 (Ext: <ext>)?
- {REGEX_PHONE, FIELD_COUNTRY_CODE, 3},
- {REGEX_PHONE, FIELD_AREA_CODE, 3},
- {REGEX_PHONE, FIELD_PHONE, 3},
- {REGEX_PHONE, FIELD_SUFFIX, 4},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Area Code: <ac> Phone: <phone> (- <suffix> (Ext: <ext>)?)?
- {REGEX_AREA, FIELD_AREA_CODE, 0},
- {REGEX_PHONE, FIELD_PHONE, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <ac> <phone>:3 <suffix>:4 (Ext: <ext>)?
- {REGEX_PHONE, FIELD_AREA_CODE, 0},
- {REGEX_PHONE, FIELD_PHONE, 3},
- {REGEX_PHONE, FIELD_SUFFIX, 4},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <cc> \( <ac> \) <phone> (- <suffix> (Ext: <ext>)?)?
- {REGEX_PHONE, FIELD_COUNTRY_CODE, 0},
- {REGEX_AREA_NOTEXT, FIELD_AREA_CODE, 0},
- {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: \( <ac> \) <phone> (- <suffix> (Ext: <ext>)?)?
- {REGEX_PHONE, FIELD_COUNTRY_CODE, 0},
- {REGEX_AREA_NOTEXT, FIELD_AREA_CODE, 0},
- {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <cc> - <ac> - <phone> - <suffix> (Ext: <ext>)?
- {REGEX_PHONE, FIELD_COUNTRY_CODE, 0},
- {REGEX_PREFIX_SEPARATOR, FIELD_AREA_CODE, 0},
- {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 0},
- {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Area code: <ac>:3 Prefix: <prefix>:3 Suffix: <suffix>:4 (Ext: <ext>)?
- {REGEX_AREA, FIELD_AREA_CODE, 3},
- {REGEX_PREFIX, FIELD_PHONE, 3},
- {REGEX_SUFFIX, FIELD_SUFFIX, 4},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <ac> Prefix: <phone> Suffix: <suffix> (Ext: <ext>)?
- {REGEX_PHONE, FIELD_AREA_CODE, 0},
- {REGEX_PREFIX, FIELD_PHONE, 0},
- {REGEX_SUFFIX, FIELD_SUFFIX, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <ac> - <phone>:3 - <suffix>:4 (Ext: <ext>)?
- {REGEX_PHONE, FIELD_AREA_CODE, 0},
- {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3},
- {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <cc> - <ac> - <phone> (Ext: <ext>)?
- {REGEX_PHONE, FIELD_COUNTRY_CODE, 0},
- {REGEX_PREFIX_SEPARATOR, FIELD_AREA_CODE, 0},
- {REGEX_SUFFIX_SEPARATOR, FIELD_PHONE, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <ac> - <phone> (Ext: <ext>)?
- {REGEX_AREA, FIELD_AREA_CODE, 0},
- {REGEX_PHONE, FIELD_PHONE, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <cc>:3 - <phone>:10 (Ext: <ext>)?
- {REGEX_PHONE, FIELD_COUNTRY_CODE, 3},
- {REGEX_PHONE, FIELD_PHONE, 14},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Ext: <ext>
- {REGEX_EXTENSION, FIELD_EXTENSION, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
- // Phone: <phone> (Ext: <ext>)?
- {REGEX_PHONE, FIELD_PHONE, 0},
- {REGEX_SEPARATOR, FIELD_NONE, 0},
-};
+// static
+const std::vector<PhoneField::PhoneGrammar>& PhoneField::GetPhoneGrammars() {
+ static const base::NoDestructor<std::vector<PhoneGrammar>> grammars({
+ // Country code: <cc> Area Code: <ac> Phone: <phone> (- <suffix>
+ // (Ext: <ext>)?)?
+ {{REGEX_COUNTRY, FIELD_COUNTRY_CODE},
+ {REGEX_AREA, FIELD_AREA_CODE},
+ {REGEX_PHONE, FIELD_PHONE}},
+ // \( <ac> \) <phone>:3 <suffix>:4 (Ext: <ext>)?
+ {{REGEX_AREA_NOTEXT, FIELD_AREA_CODE, 3},
+ {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3},
+ {REGEX_PHONE, FIELD_SUFFIX, 4}},
+ // Phone: <cc> <ac>:3 - <phone>:3 - <suffix>:4 (Ext: <ext>)?
+ {{REGEX_PHONE, FIELD_COUNTRY_CODE},
+ {REGEX_PHONE, FIELD_AREA_CODE, 3},
+ {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3},
+ {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4}},
+ // Phone: <cc>:3 <ac>:3 <phone>:3 <suffix>:4 (Ext: <ext>)?
+ {{REGEX_PHONE, FIELD_COUNTRY_CODE, 3},
+ {REGEX_PHONE, FIELD_AREA_CODE, 3},
+ {REGEX_PHONE, FIELD_PHONE, 3},
+ {REGEX_PHONE, FIELD_SUFFIX, 4}},
+ // Area Code: <ac> Phone: <phone> (- <suffix> (Ext: <ext>)?)?
+ {{REGEX_AREA, FIELD_AREA_CODE}, {REGEX_PHONE, FIELD_PHONE}},
+ // Phone: <ac> <phone>:3 <suffix>:4 (Ext: <ext>)?
+ {{REGEX_PHONE, FIELD_AREA_CODE},
+ {REGEX_PHONE, FIELD_PHONE, 3},
+ {REGEX_PHONE, FIELD_SUFFIX, 4}},
+ // Phone: <cc> \( <ac> \) <phone> (- <suffix> (Ext: <ext>)?)?
+ {{REGEX_PHONE, FIELD_COUNTRY_CODE},
+ {REGEX_AREA_NOTEXT, FIELD_AREA_CODE},
+ {REGEX_PREFIX_SEPARATOR, FIELD_PHONE}},
+ // Phone: \( <ac> \) <phone> (- <suffix> (Ext: <ext>)?)?
+ {{REGEX_PHONE, FIELD_COUNTRY_CODE},
+ {REGEX_AREA_NOTEXT, FIELD_AREA_CODE},
+ {REGEX_PREFIX_SEPARATOR, FIELD_PHONE}},
+ // Phone: <cc> - <ac> - <phone> - <suffix> (Ext: <ext>)?
+ {{REGEX_PHONE, FIELD_COUNTRY_CODE},
+ {REGEX_PREFIX_SEPARATOR, FIELD_AREA_CODE},
+ {REGEX_PREFIX_SEPARATOR, FIELD_PHONE},
+ {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX}},
+ // Area code: <ac>:3 Prefix: <prefix>:3 Suffix: <suffix>:4 (Ext: <ext>)?
+ {{REGEX_AREA, FIELD_AREA_CODE, 3},
+ {REGEX_PREFIX, FIELD_PHONE, 3},
+ {REGEX_SUFFIX, FIELD_SUFFIX, 4}},
+ // Phone: <ac> Prefix: <phone> Suffix: <suffix> (Ext: <ext>)?
+ {{REGEX_PHONE, FIELD_AREA_CODE},
+ {REGEX_PREFIX, FIELD_PHONE},
+ {REGEX_SUFFIX, FIELD_SUFFIX}},
+ // Phone: <ac> - <phone>:3 - <suffix>:4 (Ext: <ext>)?
+ {{REGEX_PHONE, FIELD_AREA_CODE},
+ {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3},
+ {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4}},
+ // Phone: <cc> - <ac> - <phone> (Ext: <ext>)?
+ {{REGEX_PHONE, FIELD_COUNTRY_CODE},
+ {REGEX_PREFIX_SEPARATOR, FIELD_AREA_CODE},
+ {REGEX_SUFFIX_SEPARATOR, FIELD_PHONE}},
+ // Phone: <ac> - <phone> (Ext: <ext>)?
+ {{REGEX_AREA, FIELD_AREA_CODE}, {REGEX_PHONE, FIELD_PHONE}},
+ // Phone: <cc>:3 - <phone> (Ext: <ext>)?
+ {{REGEX_PHONE, FIELD_COUNTRY_CODE, 3}, {REGEX_PHONE, FIELD_PHONE}},
+ // Phone: <cc> <ac> <phone> (Ext: <ext>)?
+ // Indistinguishable from <area> <prefix> <suffix>
+ {{REGEX_PHONE, FIELD_COUNTRY_CODE},
+ {EMPTY_LABEL, FIELD_AREA_CODE},
+ {EMPTY_LABEL, FIELD_PHONE}},
+ // Phone: <cc> <phone> (Ext: <ext>)?
+ // Indistinguishable from <area> <phone>
+ {{REGEX_PHONE, FIELD_COUNTRY_CODE}, {EMPTY_LABEL, FIELD_PHONE}},
+ // Phone: <phone> (Ext: <ext>)?
+ {{REGEX_PHONE, FIELD_PHONE}},
+ });
+ return *grammars;
+}
// static
bool PhoneField::LikelyAugmentedPhoneCountryCode(
@@ -209,116 +197,112 @@ bool PhoneField::LikelyAugmentedPhoneCountryCode(
}
// static
+bool PhoneField::ParseGrammar(const PhoneGrammar& grammar,
+ ParsedPhoneFields& parsed_fields,
+ AutofillScanner* scanner,
+ const LanguageCode& page_language,
+ PatternSource pattern_source,
+ LogManager* log_manager) {
+ for (const auto& rule : grammar) {
+ const bool is_country_code_field = rule.phone_part == FIELD_COUNTRY_CODE;
+
+ // The field length comparison with |rule.max_size| is not required in case
+ // of the selection boxes that are of phone country code type.
+ if (is_country_code_field &&
+ LikelyAugmentedPhoneCountryCode(scanner,
+ &parsed_fields[FIELD_COUNTRY_CODE])) {
+ continue;
+ }
+
+ bool is_empty_label = rule.regex == EMPTY_LABEL;
+ if (is_empty_label &&
+ !base::FeatureList::IsEnabled(
+ features::kAutofillEnableParsingEmptyPhoneNumberLabels)) {
+ // This `grammar` contains empty labels and doesn't apply when
+ // `kAutofillEnableParsingEmptyPhoneNumberLabels` is disabled.
+ return false;
+ }
+ // Try parsing either a field with an empty label or a field matching the
+ // regex of this rule.
+ bool parsed =
+ is_empty_label
+ ? ParseEmptyLabel(scanner, &parsed_fields[rule.phone_part])
+ : ParsePhoneField(scanner, GetRegExp(rule.regex),
+ &parsed_fields[rule.phone_part],
+ {log_manager, GetRegExpName(rule.regex)},
+ is_country_code_field,
+ GetJSONFieldType(rule.regex), page_language,
+ pattern_source);
+ if (!parsed)
+ return false;
+
+ if (rule.max_size != 0 &&
+ (parsed_fields[rule.phone_part]->max_length == 0 ||
+ rule.max_size < parsed_fields[rule.phone_part]->max_length)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// static
std::unique_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
if (scanner->IsEnd())
return nullptr;
size_t start_cursor = scanner->SaveCursor();
-
- // The form owns the following variables, so they should not be deleted.
- AutofillField* parsed_fields[FIELD_MAX];
-
- for (size_t i = 0; i < std::size(kPhoneFieldGrammars); ++i) {
- memset(parsed_fields, 0, sizeof(parsed_fields));
- size_t saved_cursor = scanner->SaveCursor();
-
- // Attempt to parse according to the next grammar.
- for (; i < std::size(kPhoneFieldGrammars) &&
- kPhoneFieldGrammars[i].regex != REGEX_SEPARATOR;
- ++i) {
- const bool is_country_code_field =
- kPhoneFieldGrammars[i].phone_part == FIELD_COUNTRY_CODE;
-
- // The field length comparison with |kPhoneFieldGrammars[i].max_size| is
- // not required in case of the selection boxes that are of phone country
- // code type.
- if (is_country_code_field &&
- LikelyAugmentedPhoneCountryCode(scanner,
- &parsed_fields[FIELD_COUNTRY_CODE]))
- continue;
-
- if (!ParsePhoneField(
- scanner, GetRegExp(kPhoneFieldGrammars[i].regex),
- &parsed_fields[kPhoneFieldGrammars[i].phone_part],
- {log_manager, GetRegExpName(kPhoneFieldGrammars[i].regex)},
- is_country_code_field,
- GetJSONFieldType(kPhoneFieldGrammars[i].regex), page_language))
- break;
- if (kPhoneFieldGrammars[i].max_size &&
- (!parsed_fields[kPhoneFieldGrammars[i].phone_part]->max_length ||
- kPhoneFieldGrammars[i].max_size <
- parsed_fields[kPhoneFieldGrammars[i].phone_part]->max_length)) {
- break;
- }
- }
-
- if (i >= std::size(kPhoneFieldGrammars)) {
- scanner->RewindTo(saved_cursor);
- return nullptr; // Parsing failed.
- }
- if (kPhoneFieldGrammars[i].regex == REGEX_SEPARATOR)
- break; // Parsing succeeded.
-
- // Proceed to the next grammar.
- do {
- ++i;
- } while (i < std::size(kPhoneFieldGrammars) &&
- kPhoneFieldGrammars[i].regex != REGEX_SEPARATOR);
-
- scanner->RewindTo(saved_cursor);
- if (i + 1 == std::size(kPhoneFieldGrammars)) {
- return nullptr; // Tried through all the possibilities - did not match.
+ ParsedPhoneFields parsed_fields;
+
+ // Find the first matching grammar.
+ bool found_matching_grammar = false;
+ for (const PhoneGrammar& grammar : GetPhoneGrammars()) {
+ std::fill(parsed_fields.begin(), parsed_fields.end(), nullptr);
+ if (ParseGrammar(grammar, parsed_fields, scanner, page_language,
+ pattern_source, log_manager)) {
+ found_matching_grammar = true;
+ break;
}
- }
-
- if (!parsed_fields[FIELD_PHONE]) {
scanner->RewindTo(start_cursor);
- return nullptr;
}
-
- std::unique_ptr<PhoneField> phone_field(new PhoneField);
- for (int i = 0; i < FIELD_MAX; ++i)
- phone_field->parsed_phone_fields_[i] = parsed_fields[i];
-
- // Look for optional fields.
-
- // Look for a third text box.
- if (!phone_field->parsed_phone_fields_[FIELD_SUFFIX]) {
- if (!ParsePhoneField(scanner, kPhoneSuffixRe,
- &phone_field->parsed_phone_fields_[FIELD_SUFFIX],
- {log_manager, "kPhoneSuffixRe"},
- /*is_country_code_field=*/false, "PHONE_SUFFIX",
- page_language)) {
- ParsePhoneField(scanner, kPhoneSuffixSeparatorRe,
- &phone_field->parsed_phone_fields_[FIELD_SUFFIX],
- {log_manager, "kPhoneSuffixSeparatorRe"},
- /*is_country_code_field=*/false, "PHONE_SUFFIX_SEPARATOR",
- page_language);
- }
+ if (!found_matching_grammar)
+ return nullptr;
+ // No grammar without FIELD_PHONE should be defined.
+ DCHECK(parsed_fields[FIELD_PHONE] != nullptr);
+
+ // Look for a suffix field using two different regex.
+ if (!parsed_fields[FIELD_SUFFIX]) {
+ ParsePhoneField(scanner, kPhoneSuffixRe, &parsed_fields[FIELD_SUFFIX],
+ {log_manager, "kPhoneSuffixRe"},
+ /*is_country_code_field=*/false, "PHONE_SUFFIX",
+ page_language, pattern_source) ||
+ ParsePhoneField(
+ scanner, kPhoneSuffixSeparatorRe, &parsed_fields[FIELD_SUFFIX],
+ {log_manager, "kPhoneSuffixSeparatorRe"},
+ /*is_country_code_field=*/false, "PHONE_SUFFIX_SEPARATOR",
+ page_language, pattern_source);
}
-
// Now look for an extension.
// The extension is not actually used, so this just eats the field so other
// parsers do not mistaken it for something else.
- ParsePhoneField(scanner, kPhoneExtensionRe,
- &phone_field->parsed_phone_fields_[FIELD_EXTENSION],
+ ParsePhoneField(scanner, kPhoneExtensionRe, &parsed_fields[FIELD_EXTENSION],
{log_manager, "kPhoneExtensionRe"},
/*is_country_code_field=*/false, "PHONE_EXTENSION",
- page_language);
+ page_language, pattern_source);
- return std::move(phone_field);
+ return base::WrapUnique(new PhoneField(std::move(parsed_fields)));
}
void PhoneField::AddClassifications(
FieldCandidatesMap* field_candidates) const {
DCHECK(parsed_phone_fields_[FIELD_PHONE]); // Phone was correctly parsed.
- if ((parsed_phone_fields_[FIELD_COUNTRY_CODE]) ||
- (parsed_phone_fields_[FIELD_AREA_CODE]) ||
- (parsed_phone_fields_[FIELD_SUFFIX])) {
- if (parsed_phone_fields_[FIELD_COUNTRY_CODE]) {
+ bool has_country_code = parsed_phone_fields_[FIELD_COUNTRY_CODE] != nullptr;
+ if (has_country_code || parsed_phone_fields_[FIELD_AREA_CODE] ||
+ parsed_phone_fields_[FIELD_SUFFIX]) {
+ if (has_country_code) {
AddClassification(parsed_phone_fields_[FIELD_COUNTRY_CODE],
PHONE_HOME_COUNTRY_CODE, kBasePhoneParserScore,
field_candidates);
@@ -326,13 +310,22 @@ void PhoneField::AddClassifications(
ServerFieldType field_number_type = PHONE_HOME_NUMBER;
if (parsed_phone_fields_[FIELD_AREA_CODE]) {
- AddClassification(parsed_phone_fields_[FIELD_AREA_CODE],
- PHONE_HOME_CITY_CODE, kBasePhoneParserScore,
- field_candidates);
- } else if (parsed_phone_fields_[FIELD_COUNTRY_CODE]) {
+ ServerFieldType area_code_type =
+ has_country_code ||
+ !base::FeatureList::IsEnabled(
+ features::kAutofillEnableSupportForPhoneNumberTrunkTypes)
+ ? PHONE_HOME_CITY_CODE
+ : PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX;
+ AddClassification(parsed_phone_fields_[FIELD_AREA_CODE], area_code_type,
+ kBasePhoneParserScore, field_candidates);
+ } else if (has_country_code) {
// Only if we can find country code without city code, it means the phone
// number include city code.
- field_number_type = PHONE_HOME_CITY_AND_NUMBER;
+ field_number_type =
+ base::FeatureList::IsEnabled(
+ features::kAutofillEnableSupportForPhoneNumberTrunkTypes)
+ ? PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX
+ : PHONE_HOME_CITY_AND_NUMBER;
}
// We tag the prefix as PHONE_HOME_NUMBER, then when filling the form
// we fill only the prefix depending on the size of the input field.
@@ -357,9 +350,8 @@ void PhoneField::AddClassifications(
}
}
-PhoneField::PhoneField() {
- memset(parsed_phone_fields_, 0, sizeof(parsed_phone_fields_));
-}
+PhoneField::PhoneField(ParsedPhoneFields fields)
+ : parsed_phone_fields_(std::move(fields)) {}
// static
std::u16string PhoneField::GetRegExp(RegexType regex_id) {
@@ -382,6 +374,7 @@ std::u16string PhoneField::GetRegExp(RegexType regex_id) {
return kPhoneSuffixRe;
case REGEX_EXTENSION:
return kPhoneExtensionRe;
+ case EMPTY_LABEL:
default:
NOTREACHED();
break;
@@ -410,6 +403,7 @@ const char* PhoneField::GetRegExpName(RegexType regex_id) {
return "kPhoneSuffixRe";
case REGEX_EXTENSION:
return "kPhoneExtensionRe";
+ case EMPTY_LABEL:
default:
NOTREACHED();
break;
@@ -439,6 +433,7 @@ std::string PhoneField::GetJSONFieldType(RegexType phonetype_id) {
return "PHONE_SUFFIX";
case REGEX_EXTENSION:
return "PHONE_EXTENSION";
+ case EMPTY_LABEL:
default:
NOTREACHED();
break;
@@ -453,7 +448,8 @@ bool PhoneField::ParsePhoneField(AutofillScanner* scanner,
const RegExLogging& logging,
const bool is_country_code_field,
const std::string& json_field_type,
- const LanguageCode& page_language) {
+ const LanguageCode& page_language,
+ PatternSource pattern_source) {
MatchParams match_type = kDefaultMatchParamsWith<MatchFieldType::kTelephone,
MatchFieldType::kNumber>;
// Include the selection boxes too for the matching of the phone country code.
@@ -464,7 +460,7 @@ bool PhoneField::ParsePhoneField(AutofillScanner* scanner,
}
base::span<const MatchPatternRef> patterns =
- GetMatchPatterns(json_field_type, page_language);
+ GetMatchPatterns(json_field_type, page_language, pattern_source);
return ParseFieldSpecifics(scanner, regex, match_type, patterns, field,
logging);
diff --git a/chromium/components/autofill/core/browser/form_parsing/phone_field.h b/chromium/components/autofill/core/browser/form_parsing/phone_field.h
index 7bb6abaf547..68f9a02024a 100644
--- a/chromium/components/autofill/core/browser/form_parsing/phone_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/phone_field.h
@@ -5,8 +5,7 @@
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_PHONE_FIELD_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_PHONE_FIELD_H_
-#include <stddef.h>
-
+#include <array>
#include <memory>
#include <string>
@@ -30,12 +29,12 @@ class LogManager;
// - number
class PhoneField : public FormField {
public:
- ~PhoneField() override;
PhoneField(const PhoneField&) = delete;
PhoneField& operator=(const PhoneField&) = delete;
static std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
#if defined(UNIT_TEST)
@@ -62,32 +61,33 @@ class PhoneField : public FormField {
REGEX_SUFFIX_SEPARATOR,
REGEX_SUFFIX,
REGEX_EXTENSION,
-
- // Separates regexps in grammar.
- REGEX_SEPARATOR,
+ // Don't use any regex and match an empty label. This is helpful for inputs
+ // like "Phone <input><input>", where only the first fields has a label.
+ EMPTY_LABEL,
};
// Parsed fields.
enum PhonePart {
- FIELD_NONE = -1,
FIELD_COUNTRY_CODE,
FIELD_AREA_CODE,
FIELD_PHONE,
FIELD_SUFFIX,
FIELD_EXTENSION,
-
FIELD_MAX,
};
+ using ParsedPhoneFields = std::array<AutofillField*, FIELD_MAX>;
- struct Parser {
- RegexType regex; // Field matching reg-ex.
- PhonePart phone_part; // Index of the field.
- size_t max_size; // Max size of the field to match. 0 means any.
- };
+ explicit PhoneField(ParsedPhoneFields fields);
- static const Parser kPhoneFieldGrammars[];
+ struct Rule {
+ RegexType regex; // The regex used to match this `phone_part`.
+ PhonePart phone_part; // The type/index of the field.
+ size_t max_size = 0; // Max size of the field to match. 0 means any.
+ };
+ using PhoneGrammar = std::vector<Rule>;
- PhoneField();
+ // Returns all the `PhoneGrammar`s used for parsing.
+ static const std::vector<PhoneGrammar>& GetPhoneGrammars();
// Returns the regular expression string corresponding to |regex_id|
static std::u16string GetRegExp(RegexType regex_id);
@@ -107,7 +107,17 @@ class PhoneField : public FormField {
const RegExLogging& logging,
const bool is_country_code_field,
const std::string& json_field_type,
- const LanguageCode& page_language);
+ const LanguageCode& page_language,
+ PatternSource pattern_source);
+
+ // Tries parsing the given `grammar` into `parsed_fields` and returns true
+ // if it succeeded.
+ static bool ParseGrammar(const PhoneGrammar& grammar,
+ ParsedPhoneFields& parsed_fields,
+ AutofillScanner* scanner,
+ const LanguageCode& page_language,
+ PatternSource pattern_source,
+ LogManager* log_manager);
// Returns true if |scanner| points to a <select> field that appears to be the
// phone country code by looking at its option contents.
@@ -118,7 +128,7 @@ class PhoneField : public FormField {
// FIELD_PHONE is always present; holds suffix if prefix is present.
// The rest could be NULL.
- AutofillField* parsed_phone_fields_[FIELD_MAX];
+ ParsedPhoneFields parsed_phone_fields_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc
index c595bc0086b..05edafbe122 100644
--- a/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc
@@ -15,6 +15,7 @@
#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/form_parsing/autofill_scanner.h"
+#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/form_field_data.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -33,9 +34,24 @@ const char* const kFieldTypes[] = {
} // namespace
-class PhoneFieldTest : public testing::Test {
+class PhoneFieldTest
+ : public testing::TestWithParam<PatternProviderFeatureState> {
public:
- PhoneFieldTest() = default;
+ PhoneFieldTest() {
+ std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled;
+ std::vector<base::Feature> disabled;
+ if (GetParam().enable) {
+ enabled.emplace_back(
+ features::kAutofillParsingPatternProvider,
+ base::FieldTrialParams{
+ {features::kAutofillParsingPatternActiveSource.name,
+ GetParam().active_source}});
+ } else {
+ disabled.push_back(features::kAutofillParsingPatternProvider);
+ }
+ scoped_feature_list_.InitWithFeaturesAndParameters(enabled, disabled);
+ }
+
PhoneFieldTest(const PhoneFieldTest&) = delete;
PhoneFieldTest& operator=(const PhoneFieldTest&) = delete;
@@ -45,392 +61,268 @@ class PhoneFieldTest : public testing::Test {
// An empty page_language means the language is unknown and patterns of all
// languages are used.
std::unique_ptr<FormField> field =
- PhoneField::Parse(scanner, LanguageCode(""), nullptr);
+ PhoneField::Parse(scanner, LanguageCode(""), GetActivePatternSource(),
+ /*log_manager=*/nullptr);
return std::unique_ptr<PhoneField>(
static_cast<PhoneField*>(field.release()));
}
- void Clear() {
- list_.clear();
- field_.reset();
- field_candidates_map_.clear();
- }
-
- void CheckField(const FieldGlobalId id, ServerFieldType expected_type) const {
- auto it = field_candidates_map_.find(id);
- ASSERT_TRUE(it != field_candidates_map_.end());
- EXPECT_EQ(expected_type, it->second.BestHeuristicType());
- }
-
- // Populates a select |field| with the |label|, the |name| and the |contents|.
- void CreateTestSelectField(const char* label,
- const char* name,
- const std::vector<const char*>& contents,
- FormFieldData* field) {
- field->label = ASCIIToUTF16(label);
- field->name = ASCIIToUTF16(name);
- field->form_control_type = "select-one";
-
- for (auto* const element : contents) {
- field->options.push_back(
- {.value = u"", .content = base::UTF8ToUTF16(element)});
- }
- }
+ // Checks if the field with `id` was classified as `expected_type`.
+ void CheckField(const FieldGlobalId id, ServerFieldType expected_type) const;
+
+ struct TestFieldData {
+ std::string type;
+ std::u16string label;
+ std::u16string name;
+ ServerFieldType expected_type;
+ // Rarely used fields. Placed at the end to simplify common use cases.
+ uint64_t max_length = 0;
+ // Options of a "select-one" `type` element.
+ std::vector<const char*> options;
+ };
+
+ // Creates a `FormFieldData` object with the provided properties, assigns a
+ // unique renderer id and appends it to `list_`. The `FieldGlobalId` of the
+ // object is returned.
+ autofill::FieldGlobalId AppendField(const TestFieldData& field_data);
+
+ // Clears the state, creates fields as specified in `fields` and tries to
+ // parse it.
+ // If `expect_success` is true, the `expected_type` of every field is
+ // verified. Otherwise it is checked that nothing was parsed.
+ // In case `expected_success` is false, `expected_types` can be omitted.
+ void RunParsingTest(const std::vector<TestFieldData>& fields,
+ bool expect_success = true);
+
+ FieldRendererId MakeFieldRendererId();
+
+ void Clear();
std::vector<std::unique_ptr<AutofillField>> list_;
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<PhoneField> field_;
+ uint64_t id_counter_ = 0;
FieldCandidatesMap field_candidates_map_;
+};
+
+void PhoneFieldTest::CheckField(const FieldGlobalId id,
+ ServerFieldType expected_type) const {
+ auto it = field_candidates_map_.find(id);
+ ASSERT_TRUE(it != field_candidates_map_.end());
+ EXPECT_EQ(expected_type, it->second.BestHeuristicType());
+}
- FieldRendererId MakeFieldRendererId() {
- return FieldRendererId(++id_counter_);
+autofill::FieldGlobalId PhoneFieldTest::AppendField(
+ const TestFieldData& field_data) {
+ FormFieldData field;
+ field.form_control_type = field_data.type;
+ field.label = field_data.label;
+ field.name = field_data.name;
+ field.max_length = field_data.max_length;
+ for (auto* const element : field_data.options) {
+ field.options.push_back(
+ {.value = u"", .content = base::UTF8ToUTF16(element)});
}
+ field.unique_renderer_id = MakeFieldRendererId();
+ list_.push_back(std::make_unique<AutofillField>(field));
+ return list_.back()->global_id();
+}
- private:
- uint64_t id_counter_ = 0;
-};
+void PhoneFieldTest::RunParsingTest(const std::vector<TestFieldData>& fields,
+ bool expect_success) {
+ Clear();
+
+ // Construct all the test fields.
+ std::vector<autofill::FieldGlobalId> global_ids;
+ for (const TestFieldData& field : fields) {
+ global_ids.push_back(AppendField(field));
+ }
-TEST_F(PhoneFieldTest, Empty) {
+ // Parse.
AutofillScanner scanner(list_);
field_ = Parse(&scanner);
- ASSERT_EQ(nullptr, field_.get());
+ ASSERT_EQ(expect_success, field_.get() != nullptr);
+
+ // Verify expecations.
+ if (expect_success) {
+ field_->AddClassificationsForTesting(&field_candidates_map_);
+ for (size_t i = 0; i < fields.size(); i++) {
+ CheckField(global_ids[i], fields[i].expected_type);
+ }
+ }
}
-TEST_F(PhoneFieldTest, NonParse) {
- list_.push_back(std::make_unique<AutofillField>());
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_EQ(nullptr, field_.get());
+FieldRendererId PhoneFieldTest::MakeFieldRendererId() {
+ return FieldRendererId(++id_counter_);
}
-TEST_F(PhoneFieldTest, ParseOneLinePhone) {
- FormFieldData field;
+void PhoneFieldTest::Clear() {
+ list_.clear();
+ field_.reset();
+ field_candidates_map_.clear();
+}
- for (const char* field_type : kFieldTypes) {
- Clear();
-
- field.form_control_type = field_type;
- field.label = u"Phone";
- field.name = u"phone";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId phone1 = list_.back()->global_id();
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(phone1, PHONE_HOME_WHOLE_NUMBER);
- }
+INSTANTIATE_TEST_SUITE_P(
+ PhoneFieldTest,
+ PhoneFieldTest,
+ ::testing::ValuesIn(PatternProviderFeatureState::All()));
+
+TEST_P(PhoneFieldTest, Empty) {
+ RunParsingTest({}, /*expect_success=*/false);
}
-TEST_F(PhoneFieldTest, ParseTwoLinePhone) {
- FormFieldData field;
+TEST_P(PhoneFieldTest, NonParse) {
+ list_.push_back(std::make_unique<AutofillField>());
+ RunParsingTest({}, /*expect_success=*/false);
+}
+TEST_P(PhoneFieldTest, ParseOneLinePhone) {
for (const char* field_type : kFieldTypes) {
- Clear();
-
- field.form_control_type = field_type;
- field.label = u"Area Code";
- field.name = u"area code";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId areacode1 = list_.back()->global_id();
-
- field.label = u"Phone";
- field.name = u"phone";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId phone2 = list_.back()->global_id();
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(areacode1, PHONE_HOME_CITY_CODE);
- CheckField(phone2, PHONE_HOME_NUMBER);
+ RunParsingTest({{field_type, u"Phone", u"phone", PHONE_HOME_WHOLE_NUMBER}});
}
}
-TEST_F(PhoneFieldTest, ThreePartPhoneNumber) {
- // Phone in format <field> - <field> - <field> could be either
- // <area code> - <prefix> - <suffix>, or
- // <country code> - <area code> - <phone>. The only distinguishing feature is
- // size: <prefix> is no bigger than 3 characters, and <suffix> is no bigger
- // than 4.
- FormFieldData field;
+TEST_P(PhoneFieldTest, ParseTwoLinePhone) {
+ for (const char* field_type : kFieldTypes) {
+ RunParsingTest(
+ {{field_type, u"Area Code", u"area code", PHONE_HOME_CITY_CODE},
+ {field_type, u"Phone", u"phone", PHONE_HOME_NUMBER}});
+ }
+}
+// Phone in format <field> - <field> - <field> could be either
+// <area code> - <prefix> - <suffix>, or
+// <country code> - <area code> - <phone>. The only distinguishing feature is
+// size: <prefix> is no bigger than 3 characters, and <suffix> is no bigger
+// than 4.
+TEST_P(PhoneFieldTest, ThreePartPhoneNumber) {
for (const char* field_type : kFieldTypes) {
- Clear();
-
- field.form_control_type = field_type;
- field.label = u"Phone:";
- field.name = u"dayphone1";
- field.max_length = 0;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId areacode1 = list_.back()->global_id();
-
- field.label = u"-";
- field.name = u"dayphone2";
- field.max_length = 3;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId prefix2 = list_.back()->global_id();
-
- field.label = u"-";
- field.name = u"dayphone3";
- field.max_length = 4;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId suffix3 = list_.back()->global_id();
-
- field.label = u"ext.:";
- field.name = u"dayphone4";
- field.max_length = 0;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId ext4 = list_.back()->global_id();
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(areacode1, PHONE_HOME_CITY_CODE);
- CheckField(prefix2, PHONE_HOME_NUMBER);
- CheckField(suffix3, PHONE_HOME_NUMBER);
- EXPECT_TRUE(base::Contains(field_candidates_map_, ext4));
+ RunParsingTest(
+ {{field_type, u"Phone:", u"dayphone1", PHONE_HOME_CITY_CODE},
+ {field_type, u"-", u"dayphone2", PHONE_HOME_NUMBER, /*max_length=*/3},
+ {field_type, u"-", u"dayphone3", PHONE_HOME_NUMBER, /*max_length=*/4},
+ {field_type, u"ext.:", u"dayphone4", PHONE_HOME_EXTENSION}});
}
}
// This scenario of explicitly labeled "prefix" and "suffix" phone numbers
// encountered in http://crbug.com/40694 with page
// https://www.wrapables.com/jsp/Signup.jsp.
-TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix) {
- FormFieldData field;
-
+TEST_P(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix) {
for (const char* field_type : kFieldTypes) {
- Clear();
-
- field.form_control_type = field_type;
- field.label = u"Phone:";
- field.name = u"area";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId areacode1 = list_.back()->global_id();
-
- field.label = std::u16string();
- field.name = u"prefix";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId prefix2 = list_.back()->global_id();
-
- field.label = std::u16string();
- field.name = u"suffix";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId suffix3 = list_.back()->global_id();
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(areacode1, PHONE_HOME_CITY_CODE);
- CheckField(prefix2, PHONE_HOME_NUMBER);
- CheckField(suffix3, PHONE_HOME_NUMBER);
+ RunParsingTest({{field_type, u"Phone:", u"area", PHONE_HOME_CITY_CODE},
+ {field_type, u"", u"prefix", PHONE_HOME_NUMBER},
+ {field_type, u"", u"suffix", PHONE_HOME_NUMBER,
+ /*max_length=*/4}});
}
}
-TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix2) {
- FormFieldData field;
-
+TEST_P(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix2) {
for (const char* field_type : kFieldTypes) {
- Clear();
-
- field.form_control_type = field_type;
- field.label = u"(";
- field.name = u"phone1";
- field.max_length = 3;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId phone1 = list_.back()->global_id();
-
- field.label = u")";
- field.name = u"phone2";
- field.max_length = 3;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId phone2 = list_.back()->global_id();
-
- field.label = std::u16string();
- field.name = u"phone3";
- field.max_length = 4;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId phone3 = list_.back()->global_id();
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(phone1, PHONE_HOME_CITY_CODE);
- CheckField(phone2, PHONE_HOME_NUMBER);
- CheckField(phone3, PHONE_HOME_NUMBER);
+ RunParsingTest(
+ {{field_type, u"(", u"phone1", PHONE_HOME_CITY_CODE, /*max_length=*/3},
+ {field_type, u")", u"phone2", PHONE_HOME_NUMBER, /*max_length=*/3},
+ {field_type, u"", u"phone3", PHONE_HOME_NUMBER,
+ /*max_length=*/4}});
}
}
-TEST_F(PhoneFieldTest, CountryAndCityAndPhoneNumber) {
- // Phone in format <country code>:3 - <city and number>:10
- // The |maxlength| is considered, otherwise it's too broad.
- FormFieldData field;
-
+// Phone in format <country code> - <city and number>
+TEST_P(PhoneFieldTest, CountryAndCityAndPhoneNumber) {
for (const char* field_type : kFieldTypes) {
- Clear();
-
- field.form_control_type = field_type;
- field.label = u"Phone Number";
- field.name = u"CountryCode";
- field.max_length = 3;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId country = list_.back()->global_id();
-
- field.label = u"Phone Number";
- field.name = u"PhoneNumber";
- field.max_length = 10;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId phone = list_.back()->global_id();
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(country, PHONE_HOME_COUNTRY_CODE);
- CheckField(phone, PHONE_HOME_CITY_AND_NUMBER);
+ RunParsingTest({{field_type, u"Phone Number", u"CountryCode",
+ PHONE_HOME_COUNTRY_CODE, /*max_length=*/3},
+ {field_type, u"Phone Number", u"PhoneNumber",
+ PHONE_HOME_CITY_AND_NUMBER}});
}
}
-TEST_F(PhoneFieldTest, CountryAndCityAndPhoneNumberWithLongerMaxLength) {
- // Phone in format <country code>:3 - <city and number>:14
- // The |maxlength| is considered, otherwise it's too broad.
- FormFieldData field;
+TEST_P(PhoneFieldTest, EmptyLabels) {
+ base::test::ScopedFeatureList enabled_features;
+ enabled_features.InitWithFeatures(
+ /*enabled_features=*/
+ {features::kAutofillEnableSupportForPhoneNumberTrunkTypes,
+ features::kAutofillEnableParsingEmptyPhoneNumberLabels},
+ /*disabled_features=*/{});
+
+ // Phone: <input><input>
+ RunParsingTest(
+ {{"text", u"Phone", u"", PHONE_HOME_COUNTRY_CODE},
+ {"text", u"", u"", PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX}});
+
+ // Phone: <input><input><input>
+ RunParsingTest({{"text", u"Phone", u"", PHONE_HOME_COUNTRY_CODE},
+ {"text", u"", u"", PHONE_HOME_CITY_CODE},
+ {"text", u"", u"", PHONE_HOME_NUMBER}});
+}
- for (const char* field_type : kFieldTypes) {
- Clear();
-
- field.form_control_type = field_type;
- field.label = u"Phone Number";
- field.name = u"CountryCode";
- field.max_length = 3;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId country = list_.back()->global_id();
-
- // Verify if websites expect a longer formatted number like:
- // (514)-123-1234, autofill is able to classify correctly.
- field.label = u"Phone Number";
- field.name = u"PhoneNumber";
- field.max_length = 14;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId phone = list_.back()->global_id();
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(country, PHONE_HOME_COUNTRY_CODE);
- CheckField(phone, PHONE_HOME_CITY_AND_NUMBER);
- }
+TEST_P(PhoneFieldTest, TrunkPrefixTypes) {
+ base::test::ScopedFeatureList trunk_types_enabled;
+ trunk_types_enabled.InitAndEnableFeature(
+ features::kAutofillEnableSupportForPhoneNumberTrunkTypes);
+
+ // Whole number instead of city-and-number.
+ RunParsingTest({{"text", u"Phone", u"phone", PHONE_HOME_WHOLE_NUMBER}});
+
+ // In presence of a country code, city-and-number without a trunk prefix
+ // is chosen.
+ RunParsingTest(
+ {{"text", u"Phone", u"ccode", PHONE_HOME_COUNTRY_CODE, /*max_length=*/3},
+ {"text", u"Phone", u"phone",
+ PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX}});
+
+ // Similarly, city codes don't require a trunk prefix when a country code
+ // is present.
+ RunParsingTest(
+ {{"text", u"Phone", u"ccode", PHONE_HOME_COUNTRY_CODE, /*max_length=*/3},
+ {"text", u"Phone", u"areacode", PHONE_HOME_CITY_CODE},
+ {"text", u"Phone", u"phone", PHONE_HOME_NUMBER}});
+
+ // Without a country code, the city code requires a trunk prefix.
+ RunParsingTest(
+ {{"text", u"Phone", u"areacode", PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX},
+ {"text", u"Phone", u"phone", PHONE_HOME_NUMBER}});
}
// Tests if the country code, city code and phone number fields are correctly
// classified by the heuristic when the phone code is a select element.
-TEST_F(PhoneFieldTest, CountryCodeIsSelectElement) {
- FormFieldData field;
-
- field.label = u"Phone Country Code";
- field.name = u"ccode";
- field.form_control_type = "select-one";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId country_code = list_.back()->global_id();
-
- field.label = u"Phone City Code";
- field.name = u"areacode";
- field.form_control_type = "text";
- field.max_length = 3;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId cityCode = list_.back()->global_id();
-
- field.label = u"Phone Number";
- field.name = u"phonenumber";
- field.max_length = 0;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId phoneNumber = list_.back()->global_id();
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(country_code, PHONE_HOME_COUNTRY_CODE);
- CheckField(cityCode, PHONE_HOME_CITY_CODE);
- CheckField(phoneNumber, PHONE_HOME_NUMBER);
+TEST_P(PhoneFieldTest, CountryCodeIsSelectElement) {
+ RunParsingTest(
+ {{"select-one", u"Phone Country Code", u"ccode", PHONE_HOME_COUNTRY_CODE},
+ {"text", u"Phone City Code", u"areacode", PHONE_HOME_CITY_CODE,
+ /*max_length=*/3},
+ {"text", u"Phone Number", u"phonenumber", PHONE_HOME_NUMBER}});
}
// Tests if the country code, city code and phone number fields are correctly
// classified by the heuristic when the phone code field is a select element
// consisting of valid options.
-TEST_F(PhoneFieldTest, CountryCodeWithOptions) {
+TEST_P(PhoneFieldTest, CountryCodeWithOptions) {
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
features::kAutofillEnableAugmentedPhoneCountryCode);
- FormFieldData field;
-
// Options consisting of the country code followed by the country names.
std::vector<const char*> augmented_field_options_list = {
"(+91) India", "(+49) Germany", "(+1) United States", "(+20) Egypt",
"(+1242) Bahamas", "(+593) Ecuador", "(+7) Russia"};
- CreateTestSelectField("PC", "PC", augmented_field_options_list, &field);
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId country_code = list_.back()->global_id();
-
- field.label = u"Phone City Code";
- field.name = u"areacode";
- field.form_control_type = "text";
- field.max_length = 3;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId cityCode = list_.back()->global_id();
- field.label = u"Phone Number";
- field.name = u"phonenumber";
- field.max_length = 0;
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId phoneNumber = list_.back()->global_id();
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(country_code, PHONE_HOME_COUNTRY_CODE);
- CheckField(cityCode, PHONE_HOME_CITY_CODE);
- CheckField(phoneNumber, PHONE_HOME_NUMBER);
+ RunParsingTest(
+ {{"select-one", u"PC", u"PC", PHONE_HOME_COUNTRY_CODE, 0,
+ augmented_field_options_list},
+ {"text", u"Phone City Code", u"areacode", PHONE_HOME_CITY_CODE},
+ {"text", u"Phone Number", u"phonenumber", PHONE_HOME_NUMBER}});
}
// Tests if the country code field is correctly classified by the heuristic when
// the phone code is a select element and consists of valid options.
-TEST_F(PhoneFieldTest, IsPhoneCountryCodeField) {
+TEST_P(PhoneFieldTest, IsPhoneCountryCodeField) {
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
features::kAutofillEnableAugmentedPhoneCountryCode);
- FormFieldData field;
std::vector<std::vector<const char*>> augmented_field_options_list = {
// Options with the country name followed by the country code in brackets.
{"India(+91) ", "Germany(+49)", "United States(+1)", "Egypt(+20)",
@@ -483,40 +375,24 @@ TEST_F(PhoneFieldTest, IsPhoneCountryCodeField) {
{"0091", "0049", "001", "0020", "001242", "00593", "007"}};
for (size_t i = 0; i < augmented_field_options_list.size(); ++i) {
- // TODO(crbug/1151473): The below CheckField() call fails in iteration 4.
+ // TODO(crbug/1151473): The country code check fails in iteration 4.
if (i == 4)
continue;
- list_.clear();
SCOPED_TRACE(testing::Message() << "i = " << i);
- const auto& options_list = augmented_field_options_list[i];
- CreateTestSelectField("PC", "PC", options_list, &field);
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
- FieldGlobalId country_code = list_.back()->global_id();
-
- field.label = u"Phone Number";
- field.name = u"phonenumber";
- field.max_length = 14;
- field.form_control_type = "text";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassificationsForTesting(&field_candidates_map_);
- CheckField(country_code, PHONE_HOME_COUNTRY_CODE);
+ RunParsingTest({{"select-one", u"PC", u"PC", PHONE_HOME_COUNTRY_CODE, 0,
+ augmented_field_options_list[i]},
+ {"text", u"Phone Number", u"phonenumber",
+ PHONE_HOME_CITY_AND_NUMBER}});
}
}
// Tests that the month field is not classified as |PHONE_HOME_COUNTRY_CODE|.
-TEST_F(PhoneFieldTest, IsMonthField) {
+TEST_P(PhoneFieldTest, IsMonthField) {
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
features::kAutofillEnableAugmentedPhoneCountryCode);
- FormFieldData field;
std::vector<std::vector<const char*>> augmented_field_options_list = {
// Month options in numeric.
{"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"},
@@ -528,31 +404,20 @@ TEST_F(PhoneFieldTest, IsMonthField) {
for (size_t i = 0; i < augmented_field_options_list.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const auto& options_list = augmented_field_options_list[i];
- CreateTestSelectField("Month", "Month", options_list, &field);
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
-
- field.label = u"Phone Number";
- field.name = u"phonenumber";
- field.max_length = 14;
- field.form_control_type = "text";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_EQ(nullptr, field_.get());
+ // Expected types don't matter, as parsing is expected to be unsuccessful.
+ RunParsingTest({{"select-one", u"Month", u"Month", UNKNOWN_TYPE, 0,
+ augmented_field_options_list[i]},
+ {"text", u"Phone Number", u"phonenumber"}},
+ /*expect_success=*/false);
}
}
// Tests that the day field is not classified as |PHONE_HOME_COUNTRY_CODE|.
-TEST_F(PhoneFieldTest, IsDayField) {
+TEST_P(PhoneFieldTest, IsDayField) {
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
features::kAutofillEnableAugmentedPhoneCountryCode);
- FormFieldData field;
std::vector<std::vector<const char*>> augmented_field_options_list = {
// Numeric day options.
{"01", "02", "03", "04", "05", "06", "07", "08", "09", "10",
@@ -594,31 +459,20 @@ TEST_F(PhoneFieldTest, IsDayField) {
for (size_t i = 0; i < augmented_field_options_list.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const auto& options_list = augmented_field_options_list[i];
- CreateTestSelectField("Field", "Field", options_list, &field);
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
-
- field.label = u"Phone Number";
- field.name = u"phonenumber";
- field.max_length = 14;
- field.form_control_type = "text";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_EQ(nullptr, field_.get());
+ // Expected types don't matter, as parsing is expected to be unsuccessful.
+ RunParsingTest({{"select-one", u"Field", u"Field", UNKNOWN_TYPE, 0,
+ augmented_field_options_list[i]},
+ {"text", u"Phone Number", u"phonenumber"}},
+ /*expect_success=*/false);
}
}
// Tests that the field is not classified as |PHONE_HOME_COUNTRY_CODE|.
-TEST_F(PhoneFieldTest, IsYearField) {
+TEST_P(PhoneFieldTest, IsYearField) {
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
features::kAutofillEnableAugmentedPhoneCountryCode);
- FormFieldData field;
std::vector<std::vector<const char*>> augmented_field_options_list = {
// Numeric four digit year options.
{"1990", "1991", "1992", "1993", "1994", "1995", "1996",
@@ -646,31 +500,20 @@ TEST_F(PhoneFieldTest, IsYearField) {
for (size_t i = 0; i < augmented_field_options_list.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const auto& options_list = augmented_field_options_list[i];
- CreateTestSelectField("Field", "Field", options_list, &field);
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
-
- field.label = u"Phone Number";
- field.name = u"phonenumber";
- field.max_length = 14;
- field.form_control_type = "text";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_EQ(nullptr, field_.get());
+ // Expected types don't matter, as parsing is expected to be unsuccessful.
+ RunParsingTest({{"select-one", u"Field", u"Field", UNKNOWN_TYPE, 0,
+ augmented_field_options_list[i]},
+ {"text", u"Phone Number", u"phonenumber"}},
+ /*expect_success=*/false);
}
}
// Tests that the timezone field is not classified as |PHONE_HOME_COUNTRY_CODE|.
-TEST_F(PhoneFieldTest, IsTimeZoneField) {
+TEST_P(PhoneFieldTest, IsTimeZoneField) {
base::test::ScopedFeatureList enabled;
enabled.InitAndEnableFeature(
features::kAutofillEnableAugmentedPhoneCountryCode);
- FormFieldData field;
std::vector<std::vector<const char*>> augmented_field_options_list = {
// Time Zone options.
{"Yemen (UTC+03:00)", "Uruguay (UTC−03:00)", "UAE (UTC+04:00)",
@@ -684,21 +527,11 @@ TEST_F(PhoneFieldTest, IsTimeZoneField) {
for (size_t i = 0; i < augmented_field_options_list.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const auto& options_list = augmented_field_options_list[i];
- CreateTestSelectField("Time Zone", "TimeZone", options_list, &field);
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
-
- field.label = u"Phone Number";
- field.name = u"phonenumber";
- field.max_length = 14;
- field.form_control_type = "text";
- field.unique_renderer_id = MakeFieldRendererId();
- list_.push_back(std::make_unique<AutofillField>(field));
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_EQ(nullptr, field_.get());
+ // Expected types don't matter, as parsing is expected to be unsuccessful.
+ RunParsingTest({{"select-one", u"Time Zone", u"TimeZone", UNKNOWN_TYPE, 0,
+ augmented_field_options_list[i]},
+ {"text", u"Phone Number", u"phonenumber"}},
+ /*expect_success=*/false);
}
}
diff --git a/chromium/components/autofill/core/browser/form_parsing/price_field.cc b/chromium/components/autofill/core/browser/form_parsing/price_field.cc
index 9cbf985e744..a91e7a34e14 100644
--- a/chromium/components/autofill/core/browser/form_parsing/price_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/price_field.cc
@@ -14,10 +14,11 @@ namespace autofill {
// static
std::unique_ptr<FormField> PriceField::Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
AutofillField* field;
base::span<const MatchPatternRef> price_patterns =
- GetMatchPatterns("PRICE", page_language);
+ GetMatchPatterns("PRICE", page_language, pattern_source);
if (ParseFieldSpecifics(
scanner, kPriceRe,
diff --git a/chromium/components/autofill/core/browser/form_parsing/price_field.h b/chromium/components/autofill/core/browser/form_parsing/price_field.h
index 6f40fb6624f..e550b750d1d 100644
--- a/chromium/components/autofill/core/browser/form_parsing/price_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/price_field.h
@@ -25,6 +25,7 @@ class PriceField : public FormField {
public:
static std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
explicit PriceField(const AutofillField* field);
diff --git a/chromium/components/autofill/core/browser/form_parsing/price_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/price_field_unittest.cc
index 5243ac9ffb8..56380de0c4e 100644
--- a/chromium/components/autofill/core/browser/form_parsing/price_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/price_field_unittest.cc
@@ -10,9 +10,11 @@ using base::ASCIIToUTF16;
namespace autofill {
-class PriceFieldTest : public FormFieldTest {
+class PriceFieldTest
+ : public FormFieldTestBase,
+ public testing::TestWithParam<PatternProviderFeatureState> {
public:
- PriceFieldTest() = default;
+ PriceFieldTest() : FormFieldTestBase(GetParam()) {}
PriceFieldTest(const PriceFieldTest&) = delete;
PriceFieldTest& operator=(const PriceFieldTest&) = delete;
@@ -20,17 +22,23 @@ class PriceFieldTest : public FormFieldTest {
std::unique_ptr<FormField> Parse(
AutofillScanner* scanner,
const LanguageCode& page_language = LanguageCode("en")) override {
- return PriceField::Parse(scanner, page_language, nullptr);
+ return PriceField::Parse(scanner, page_language, GetActivePatternSource(),
+ /*log_manager=*/nullptr);
}
};
-TEST_F(PriceFieldTest, ParsePrice) {
+INSTANTIATE_TEST_SUITE_P(
+ PriceFieldTest,
+ PriceFieldTest,
+ ::testing::ValuesIn(PatternProviderFeatureState::All()));
+
+TEST_P(PriceFieldTest, ParsePrice) {
AddTextFormFieldData("name your price", "userPrice", PRICE);
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(PriceFieldTest, ParseNonPrice) {
+TEST_P(PriceFieldTest, ParseNonPrice) {
AddTextFormFieldData("firstName", "Name", UNKNOWN_TYPE);
ClassifyAndVerify(ParseResult::NOT_PARSED);
diff --git a/chromium/components/autofill/core/browser/form_parsing/regex_patterns.cc b/chromium/components/autofill/core/browser/form_parsing/regex_patterns.cc
index 6fc181c3149..84463d7d455 100644
--- a/chromium/components/autofill/core/browser/form_parsing/regex_patterns.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/regex_patterns.cc
@@ -5,31 +5,99 @@
#include "components/autofill/core/browser/form_parsing/regex_patterns.h"
#include "base/check.h"
+#include "base/notreached.h"
#include "components/autofill/core/browser/form_parsing/regex_patterns_inl.h"
+#include "components/autofill/core/common/autofill_features.h"
namespace autofill {
+namespace {
+
+// Returns the span of MatchPatternRefs for the given pattern name, language
+// code, and pattern source.
+//
+// Hits a CHECK if the given pattern source contains no patterns for the given
+// name.
+//
+// Falls back to the union of all patterns of a the given name in the given
+// pattern source if there are no patterns for the given language.
base::span<const MatchPatternRef> GetMatchPatterns(
base::StringPiece name,
- base::StringPiece language_code) {
+ base::StringPiece language_code,
+ PatternSource pattern_source) {
auto* it = kPatternMap.find(std::make_pair(name, language_code));
if (!language_code.empty() && it == kPatternMap.end())
it = kPatternMap.find(std::make_pair(name, ""));
CHECK(it != kPatternMap.end());
- return it->second;
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ switch (pattern_source) {
+ case PatternSource::kDefault:
+ return it->second[0];
+ case PatternSource::kExperimental:
+ return it->second[1];
+ case PatternSource::kNextGen:
+ return it->second[2];
+ case PatternSource::kLegacy:
+ return it->second[3];
+ }
+#else
+ switch (pattern_source) {
+ case PatternSource::kLegacy:
+ return it->second[0];
+ }
+#endif
+ NOTREACHED();
+ return {};
+}
+
+} // namespace
+
+PatternSource GetActivePatternSource() {
+#if !BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ return PatternSource::kLegacy;
+#else
+ if (!base::FeatureList::IsEnabled(features::kAutofillParsingPatternProvider))
+ return PatternSource::kLegacy;
+ const std::string& source =
+ features::kAutofillParsingPatternActiveSource.Get();
+ DCHECK(source == "default" || source == "experimental" ||
+ source == "nextgen" || source == "legacy");
+ return source == "default" ? PatternSource::kDefault
+ : source == "experimental" ? PatternSource::kExperimental
+ : source == "nextgen" ? PatternSource::kNextGen
+ : source == "legacy" ? PatternSource::kLegacy
+ : PatternSource::kDefault;
+#endif
+}
+
+DenseSet<PatternSource> GetNonActivePatternSources() {
+#if !BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ return {};
+#else
+ if (!base::FeatureList::IsEnabled(features::kAutofillParsingPatternProvider))
+ return {};
+ DenseSet<PatternSource> sources{
+ PatternSource::kLegacy, PatternSource::kDefault,
+ PatternSource::kExperimental, PatternSource::kNextGen};
+ sources.erase(GetActivePatternSource());
+ return sources;
+#endif
}
base::span<const MatchPatternRef> GetMatchPatterns(
base::StringPiece name,
- absl::optional<LanguageCode> language_code) {
- return language_code ? GetMatchPatterns(name, **language_code)
- : GetMatchPatterns(name, "");
+ absl::optional<LanguageCode> language_code,
+ PatternSource pattern_source) {
+ return language_code ? GetMatchPatterns(name, **language_code, pattern_source)
+ : GetMatchPatterns(name, "", pattern_source);
}
base::span<const MatchPatternRef> GetMatchPatterns(
ServerFieldType type,
- absl::optional<LanguageCode> language_code) {
- return GetMatchPatterns(FieldTypeToStringPiece(type), language_code);
+ absl::optional<LanguageCode> language_code,
+ PatternSource pattern_source) {
+ return GetMatchPatterns(FieldTypeToStringPiece(type), language_code,
+ pattern_source);
}
// The dereferencing operator implements the distinction between ordinary and
diff --git a/chromium/components/autofill/core/browser/form_parsing/regex_patterns.h b/chromium/components/autofill/core/browser/form_parsing/regex_patterns.h
index b3030c28e84..61fe2db9b65 100644
--- a/chromium/components/autofill/core/browser/form_parsing/regex_patterns.h
+++ b/chromium/components/autofill/core/browser/form_parsing/regex_patterns.h
@@ -7,13 +7,12 @@
#include "base/containers/span.h"
#include "base/strings/string_piece.h"
-#include "base/types/strong_alias.h"
#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h"
+#include "components/autofill/core/browser/form_parsing/buildflags.h"
#include "components/autofill/core/common/language_code.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h"
-
namespace autofill {
// A MatchPatternRef dereferences to a MatchingPattern.
@@ -51,9 +50,8 @@ class MatchPatternRef {
using UnderlyingType = int16_t;
// A wrapper of the constructor used by the code generation.
- friend constexpr inline MatchPatternRef MakeMatchPatternRef(
- bool is_supplementary,
- UnderlyingType index);
+ friend constexpr MatchPatternRef MakeMatchPatternRef(bool is_supplementary,
+ UnderlyingType index);
friend class MatchPatternRefTestApi;
constexpr MatchPatternRef(bool supplementary, UnderlyingType index)
@@ -69,6 +67,32 @@ class MatchPatternRef {
UnderlyingType value_;
};
+// The different sets of patterns that are available.
+// Each enum constant corresponds to a JSON file.
+enum class PatternSource {
+#if !BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ // Patterns whose stability is above suspicion.
+ kLegacy,
+ kMaxValue = kLegacy
+#else
+ // Patterns whose stability is above suspicion.
+ kLegacy,
+ // The patterns applied for most users.
+ kDefault,
+ // Patterns that are being verified experimentally.
+ kExperimental,
+ // One step before `kExperimental`. These patterns are used only for
+ // non-user-visible metrics.
+ kNextGen,
+ kMaxValue = kNextGen
+#endif
+};
+
+// The active pattern and the available patterns depend on the build config and
+// the Finch config.
+PatternSource GetActivePatternSource();
+DenseSet<PatternSource> GetNonActivePatternSources();
+
// Looks up the patterns for the given name and language.
// The name is typically a field type.
//
@@ -81,11 +105,13 @@ class MatchPatternRef {
// decreasing order.
base::span<const MatchPatternRef> GetMatchPatterns(
base::StringPiece name,
- absl::optional<LanguageCode> language);
+ absl::optional<LanguageCode> language,
+ PatternSource pattern_source);
base::span<const MatchPatternRef> GetMatchPatterns(
ServerFieldType type,
- absl::optional<LanguageCode> language);
+ absl::optional<LanguageCode> language,
+ PatternSource pattern_source);
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc
index 32585183d49..875821b3f8c 100644
--- a/chromium/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc
@@ -16,14 +16,16 @@
#include "base/logging.h"
#include "base/ranges/ranges.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/autofill_regexes.h"
+#include "components/autofill/core/browser/form_parsing/buildflags.h"
#include "components/autofill/core/browser/form_parsing/regex_patterns_inl.h"
#include "components/autofill/core/common/autofill_features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using ::testing::Contains;
using ::testing::Each;
+using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::IsSupersetOf;
using ::testing::Not;
@@ -33,6 +35,8 @@ namespace autofill {
class MatchPatternRefTestApi {
public:
+ using UnderlyingType = MatchPatternRef::UnderlyingType;
+
explicit MatchPatternRefTestApi(MatchPatternRef p) : p_(p) {}
absl::optional<MatchPatternRef> MakeSupplementary() const {
@@ -41,11 +45,9 @@ class MatchPatternRefTestApi {
return MatchPatternRef(true, index());
}
- MatchPatternRef::UnderlyingType is_supplementary() const {
- return p_.is_supplementary();
- }
+ UnderlyingType is_supplementary() const { return p_.is_supplementary(); }
- MatchPatternRef::UnderlyingType index() const { return p_.index(); }
+ UnderlyingType index() const { return p_.index(); }
private:
MatchPatternRef p_;
@@ -92,20 +94,86 @@ bool IsEmpty(const char* s) {
} // namespace
bool operator==(MatchPatternRef a, MatchPatternRef b) {
- return test_api(a).is_supplementary() == test_api(b).is_supplementary() ||
+ return test_api(a).is_supplementary() == test_api(b).is_supplementary() &&
test_api(a).index() == test_api(b).index();
}
+bool operator!=(MatchPatternRef a, MatchPatternRef b) {
+ return !(a == b);
+}
+
void PrintTo(MatchPatternRef p, std::ostream* os) {
*os << "MatchPatternRef(" << test_api(p).is_supplementary() << ","
<< test_api(p).index() << ")";
}
-class RegexPatternsTest : public testing::Test {};
+// The parameter is the PatternSource to pass to GetMatchPatterns().
+class RegexPatternsTest : public testing::TestWithParam<PatternSource> {
+ public:
+ PatternSource pattern_source() const { return GetParam(); }
+};
+
+INSTANTIATE_TEST_SUITE_P(RegexPatternsTest,
+ RegexPatternsTest,
+ ::testing::Values(
+#if !BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ PatternSource::kLegacy
+#else
+ PatternSource::kLegacy,
+ PatternSource::kDefault,
+ PatternSource::kExperimental,
+ PatternSource::kNextGen
+#endif
+ ));
+
+// The parameter is the index of a MatchPatternRef.
+class MatchPatternRefInternalsTest
+ : public ::testing::TestWithParam<MatchPatternRefTestApi::UnderlyingType> {
+ public:
+ MatchPatternRefTestApi::UnderlyingType index() const { return GetParam(); }
+};
+
+INSTANTIATE_TEST_SUITE_P(RegexPatternsTest,
+ MatchPatternRefInternalsTest,
+ ::testing::Values(0, 1, 2, 123, 1000, 2000));
+
+// Tests MatchPatternRef's index() and is_supplementary().
+TEST_P(MatchPatternRefInternalsTest, MatchPatternRef) {
+ MatchPatternRef a = MakeMatchPatternRef(false, index());
+ MatchPatternRef b = MakeMatchPatternRef(true, index());
+ EXPECT_EQ(a, a);
+ EXPECT_EQ(b, b);
+ EXPECT_NE(a, b);
+ EXPECT_EQ(test_api(a).index(), index());
+ EXPECT_EQ(test_api(b).index(), index());
+ EXPECT_FALSE(test_api(a).is_supplementary());
+ EXPECT_TRUE(test_api(b).is_supplementary());
+}
+
+// Tests MatchPatternRef's dereference operator.
+//
+// Since we want to test that supplementary patterns only contain
+// MatchAttribute::kName, choose `index` such that `kPatterns[0]` contains
+// MatchAttribute::kLabel.
+TEST_F(RegexPatternsTest, MatchPatternRefDereference) {
+ MatchPatternRefTestApi::UnderlyingType index = 0;
+ ASSERT_TRUE(
+ kPatterns[0].match_field_attributes.contains(MatchAttribute::kLabel));
+ MatchPatternRef a = MakeMatchPatternRef(false, index);
+ MatchPatternRef b = MakeMatchPatternRef(true, index);
+ EXPECT_TRUE((*a).positive_pattern);
+ EXPECT_TRUE((*a).negative_pattern);
+ EXPECT_EQ((*a).positive_pattern, (*b).positive_pattern);
+ EXPECT_EQ((*a).negative_pattern, (*b).negative_pattern);
+ EXPECT_EQ((*a).positive_score, (*b).positive_score);
+ EXPECT_EQ((*a).match_field_input_types, (*b).match_field_input_types);
+ EXPECT_THAT((*a).match_field_attributes, Contains(MatchAttribute::kLabel));
+ EXPECT_THAT((*b).match_field_attributes, ElementsAre(MatchAttribute::kName));
+}
// Tests that for a given pattern name, the pseudo-language-code "" contains the
// patterns of all real languages.
-TEST_F(RegexPatternsTest, PseudoLanguageIsUnionOfLanguages) {
+TEST_P(RegexPatternsTest, PseudoLanguageIsUnionOfLanguages) {
const std::string kSomeName = "ADDRESS_LINE_1";
const base::flat_set<std::string> kLanguagesOfPattern = [&] {
std::vector<std::string> vec;
@@ -121,34 +189,39 @@ TEST_F(RegexPatternsTest, PseudoLanguageIsUnionOfLanguages) {
// The expected patterns are the patterns of all languages for `kSomeName`.
std::vector<MatchPatternRef> expected;
for (const std::string& lang : kLanguagesOfPattern) {
- const auto& patterns = GetMatchPatterns(kSomeName, LanguageCode(lang));
+ const auto& patterns =
+ GetMatchPatterns(kSomeName, LanguageCode(lang), pattern_source());
expected.insert(expected.end(), patterns.begin(), patterns.end());
}
base::EraseIf(expected,
[](auto p) { return test_api(p).is_supplementary(); });
- EXPECT_THAT(GetMatchPatterns(kSomeName, absl::nullopt),
+ EXPECT_THAT(GetMatchPatterns(kSomeName, absl::nullopt, pattern_source()),
UnorderedElementsAreArray(expected));
- EXPECT_THAT(GetMatchPatterns(kSomeName, absl::nullopt),
+ EXPECT_THAT(GetMatchPatterns(kSomeName, absl::nullopt, pattern_source()),
Each(Not(IsSupplementary)));
}
// Tests that for a given pattern name, if the language doesn't isn't known we
// use the union of all patterns.
-TEST_F(RegexPatternsTest, FallbackToPseudoLanguageIfLanguageDoesNotExist) {
+TEST_P(RegexPatternsTest, FallbackToPseudoLanguageIfLanguageDoesNotExist) {
const std::string kSomeName = "ADDRESS_LINE_1";
const LanguageCode kNonexistingLanguage("foo");
- EXPECT_THAT(GetMatchPatterns(kSomeName, kNonexistingLanguage),
- ElementsAreArray(GetMatchPatterns(kSomeName, absl::nullopt)));
+ EXPECT_THAT(
+ GetMatchPatterns(kSomeName, kNonexistingLanguage, pattern_source()),
+ ElementsAreArray(
+ GetMatchPatterns(kSomeName, absl::nullopt, pattern_source())));
}
// Tests that for a given pattern name, the non-English languages are
// supplemented with the English patterns.
-TEST_F(RegexPatternsTest,
+TEST_P(RegexPatternsTest,
EnglishPatternsAreAddedToOtherLanguagesAsSupplementaryPatterns) {
const std::string kSomeName = "ADDRESS_LINE_1";
- auto de_patterns = GetMatchPatterns(kSomeName, LanguageCode("de"));
- auto en_patterns = GetMatchPatterns(kSomeName, LanguageCode("en"));
+ auto de_patterns =
+ GetMatchPatterns(kSomeName, LanguageCode("de"), pattern_source());
+ auto en_patterns =
+ GetMatchPatterns(kSomeName, LanguageCode("en"), pattern_source());
ASSERT_FALSE(de_patterns.empty());
ASSERT_FALSE(en_patterns.empty());
@@ -169,6 +242,9 @@ TEST_F(RegexPatternsTest,
}
struct PatternTestCase {
+ // The set of patterns. In non-branded builds, only the default set is
+ // supported.
+ PatternSource pattern_source = PatternSource::kLegacy;
// Reference to the pattern name in the resources/regex_patterns.json file.
const char* pattern_name;
// Language selector for the pattern, refers to the detected language of a
@@ -180,31 +256,27 @@ struct PatternTestCase {
};
class RegexPatternsTestWithSamples
- : public RegexPatternsTest,
- public testing::WithParamInterface<PatternTestCase> {};
+ : public testing::TestWithParam<PatternTestCase> {};
TEST_P(RegexPatternsTestWithSamples, TestPositiveAndNegativeCases) {
- base::test::ScopedFeatureList scoped_feature_list;
- base::FieldTrialParams feature_parameters{
- {features::kAutofillParsingWithLanguageSpecificPatternsParam.name,
- "true"}};
- scoped_feature_list.InitAndEnableFeatureWithParameters(
- features::kAutofillParsingPatternProvider, feature_parameters);
-
PatternTestCase test_case = GetParam();
for (const std::string& sample : test_case.positive_samples) {
EXPECT_THAT(sample,
MatchesAny(GetMatchPatterns(test_case.pattern_name,
- LanguageCode(test_case.language))))
+ LanguageCode(test_case.language),
+ test_case.pattern_source)))
+ << "pattern_source=" << static_cast<int>(test_case.pattern_source)
+ << ","
<< "pattern_name=" << test_case.pattern_name << ","
<< "language=" << test_case.language;
}
for (const std::string& sample : test_case.negative_samples) {
EXPECT_THAT(sample,
- ::testing::Not(MatchesAny(GetMatchPatterns(
- test_case.pattern_name, LanguageCode(test_case.language)))))
+ Not(MatchesAny(GetMatchPatterns(
+ test_case.pattern_name, LanguageCode(test_case.language),
+ test_case.pattern_source))))
<< "pattern_name=" << test_case.pattern_name << ","
<< "language=" << test_case.language;
}
@@ -214,7 +286,178 @@ INSTANTIATE_TEST_SUITE_P(
RegexPatternsTest,
RegexPatternsTestWithSamples,
testing::Values(
+#if !BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ PatternTestCase{
+ .pattern_source = PatternSource::kLegacy,
+ .pattern_name = "PATTERN_SOURCE_DUMMY",
+ .language = "en",
+ .positive_samples = {"legacy"},
+ .negative_samples = {"default", "experimental", "nextgen"}},
+#else
+ PatternTestCase{
+ .pattern_source = PatternSource::kDefault,
+ .pattern_name = "PATTERN_SOURCE_DUMMY",
+ .language = "en",
+ .positive_samples = {"default"},
+ .negative_samples = {"legacy", "experimental", "nextgen"}},
+ PatternTestCase{.pattern_source = PatternSource::kExperimental,
+ .pattern_name = "PATTERN_SOURCE_DUMMY",
+ .language = "en",
+ .positive_samples = {"experimental"},
+ .negative_samples = {"default", "legacy", "nextgen"}},
+ PatternTestCase{
+ .pattern_source = PatternSource::kNextGen,
+ .pattern_name = "PATTERN_SOURCE_DUMMY",
+ .language = "en",
+ .positive_samples = {"nextgen"},
+ .negative_samples = {"default", "legacy", "experimental"}},
+ PatternTestCase{
+ .pattern_source = PatternSource::kLegacy,
+ .pattern_name = "PATTERN_SOURCE_DUMMY",
+ .language = "en",
+ .positive_samples = {"legacy"},
+ .negative_samples = {"default", "experimental", "nextgen"}},
+#endif
+ PatternTestCase{
+ .pattern_source = PatternSource::kLegacy,
+ .pattern_name = "CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR",
+ .language = "en",
+ .positive_samples =
+ {"mm / yy", "mm/ yy", "mm /yy", "mm/yy", "mm - yy", "mm- yy",
+ "mm -yy", "mm-yy", "mmyy",
+ // Complex two year cases
+ "Expiration Date (MM / YY)", "Expiration Date (MM/YY)",
+ "Expiration Date (MM - YY)", "Expiration Date (MM-YY)",
+ "Expiration Date MM / YY", "Expiration Date MM/YY",
+ "Expiration Date MM - YY", "Expiration Date MM-YY",
+ "expiration date yy", "Exp Date (MM / YY)"},
+ .negative_samples =
+ {"", "Look, ma' -- an invalid string!", "mmfavouritewordyy",
+ "mm a yy", "mm a yyyy",
+ // Simple four year cases
+ "mm / yyyy", "mm/ yyyy", "mm /yyyy", "mm/yyyy", "mm - yyyy",
+ "mm- yyyy", "mm -yyyy", "mm-yyyy", "mmyyyy",
+ // Complex four year cases
+ "Expiration Date (MM / YYYY)", "Expiration Date (MM/YYYY)",
+ "Expiration Date (MM - YYYY)", "Expiration Date (MM-YYYY)",
+ "Expiration Date MM / YYYY", "Expiration Date MM/YYYY",
+ "Expiration Date MM - YYYY", "Expiration Date MM-YYYY",
+ "expiration date yyyy", "Exp Date (MM / YYYY)"}},
+ PatternTestCase{
+ .pattern_source = PatternSource::kLegacy,
+ .pattern_name = "CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR",
+ .language = "en",
+ .positive_samples =
+ {// Simple four year cases
+ "mm / yyyy", "mm/ yyyy", "mm /yyyy", "mm/yyyy", "mm - yyyy",
+ "mm- yyyy", "mm -yyyy", "mm-yyyy", "mmyyyy",
+ // Complex four year cases
+ "Expiration Date (MM / YYYY)", "Expiration Date (MM/YYYY)",
+ "Expiration Date (MM - YYYY)", "Expiration Date (MM-YYYY)",
+ "Expiration Date MM / YYYY", "Expiration Date MM/YYYY",
+ "Expiration Date MM - YYYY", "Expiration Date MM-YYYY",
+ "expiration date yyyy", "Exp Date (MM / YYYY)"},
+ .negative_samples =
+ {"", "Look, ma' -- an invalid string!", "mmfavouritewordyy",
+ "mm a yy", "mm a yyyy",
+ // Simple two year cases
+ "mm / yy", "mm/ yy", "mm /yy", "mm/yy", "mm - yy", "mm- yy",
+ "mm -yy", "mm-yy", "mmyy",
+ // Complex two year cases
+ "Expiration Date (MM / YY)", "Expiration Date (MM/YY)",
+ "Expiration Date (MM - YY)", "Expiration Date (MM-YY)",
+ "Expiration Date MM / YY", "Expiration Date MM/YY",
+ "Expiration Date MM - YY", "Expiration Date MM-YY",
+ "expiration date yy", "Exp Date (MM / YY)"}},
+ PatternTestCase{.pattern_source = PatternSource::kLegacy,
+ .pattern_name = "ZIP_CODE",
+ .language = "en",
+ .positive_samples = {"Zip code", "postal code"},
+ .negative_samples =
+ {// Not matching for "en" language:
+ "postleitzahl",
+ // Not referring to a ZIP code:
+ "Supported file formats: .docx, .rar, .zip."}},
+ PatternTestCase{.pattern_source = PatternSource::kLegacy,
+ .pattern_name = "ZIP_CODE",
+ .language = "de",
+ .positive_samples =
+ {// Inherited from "en":
+ "Zip code", "postal code",
+ // Specifically added for "de":
+ "postleitzahl"}}
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ ,
+ PatternTestCase{
+ .pattern_source = PatternSource::kExperimental,
+ .pattern_name = "CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR",
+ .language = "en",
+ .positive_samples =
+ {"mm / yy", "mm/ yy", "mm /yy", "mm/yy", "mm - yy", "mm- yy",
+ "mm -yy", "mm-yy", "mmyy",
+ // Complex two year cases
+ "Expiration Date (MM / YY)", "Expiration Date (MM/YY)",
+ "Expiration Date (MM - YY)", "Expiration Date (MM-YY)",
+ "Expiration Date MM / YY", "Expiration Date MM/YY",
+ "Expiration Date MM - YY", "Expiration Date MM-YY",
+ "expiration date yy", "Exp Date (MM / YY)"},
+ .negative_samples =
+ {"", "Look, ma' -- an invalid string!", "mmfavouritewordyy",
+ "mm a yy", "mm a yyyy",
+ // Simple four year cases
+ "mm / yyyy", "mm/ yyyy", "mm /yyyy", "mm/yyyy", "mm - yyyy",
+ "mm- yyyy", "mm -yyyy", "mm-yyyy", "mmyyyy",
+ // Complex four year cases
+ "Expiration Date (MM / YYYY)", "Expiration Date (MM/YYYY)",
+ "Expiration Date (MM - YYYY)", "Expiration Date (MM-YYYY)",
+ "Expiration Date MM / YYYY", "Expiration Date MM/YYYY",
+ "Expiration Date MM - YYYY", "Expiration Date MM-YYYY",
+ "expiration date yyyy", "Exp Date (MM / YYYY)"}},
+ PatternTestCase{
+ .pattern_source = PatternSource::kExperimental,
+ .pattern_name = "CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR",
+ .language = "en",
+ .positive_samples =
+ {// Simple four year cases
+ "mm / yyyy", "mm/ yyyy", "mm /yyyy", "mm/yyyy", "mm - yyyy",
+ "mm- yyyy", "mm -yyyy", "mm-yyyy", "mmyyyy",
+ // Complex four year cases
+ "Expiration Date (MM / YYYY)", "Expiration Date (MM/YYYY)",
+ "Expiration Date (MM - YYYY)", "Expiration Date (MM-YYYY)",
+ "Expiration Date MM / YYYY", "Expiration Date MM/YYYY",
+ "Expiration Date MM - YYYY", "Expiration Date MM-YYYY",
+ "expiration date yyyy", "Exp Date (MM / YYYY)"},
+ .negative_samples =
+ {"", "Look, ma' -- an invalid string!", "mmfavouritewordyy",
+ "mm a yy", "mm a yyyy",
+ // Simple two year cases
+ "mm / yy", "mm/ yy", "mm /yy", "mm/yy", "mm - yy", "mm- yy",
+ "mm -yy", "mm-yy", "mmyy",
+ // Complex two year cases
+ "Expiration Date (MM / YY)", "Expiration Date (MM/YY)",
+ "Expiration Date (MM - YY)", "Expiration Date (MM-YY)",
+ "Expiration Date MM / YY", "Expiration Date MM/YY",
+ "Expiration Date MM - YY", "Expiration Date MM-YY",
+ "expiration date yy", "Exp Date (MM / YY)"}},
+ PatternTestCase{.pattern_source = PatternSource::kExperimental,
+ .pattern_name = "ZIP_CODE",
+ .language = "en",
+ .positive_samples = {"Zip code", "postal code"},
+ .negative_samples =
+ {// Not matching for "en" language:
+ "postleitzahl",
+ // Not referring to a ZIP code:
+ "Supported file formats: .docx, .rar, .zip."}},
+ PatternTestCase{.pattern_source = PatternSource::kExperimental,
+ .pattern_name = "ZIP_CODE",
+ .language = "de",
+ .positive_samples =
+ {// Inherited from "en":
+ "Zip code", "postal code",
+ // Specifically added for "de":
+ "postleitzahl"}},
PatternTestCase{
+ .pattern_source = PatternSource::kNextGen,
.pattern_name = "CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR",
.language = "en",
.positive_samples =
@@ -239,6 +482,7 @@ INSTANTIATE_TEST_SUITE_P(
"Expiration Date MM - YYYY", "Expiration Date MM-YYYY",
"expiration date yyyy", "Exp Date (MM / YYYY)"}},
PatternTestCase{
+ .pattern_source = PatternSource::kNextGen,
.pattern_name = "CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR",
.language = "en",
.positive_samples =
@@ -263,7 +507,8 @@ INSTANTIATE_TEST_SUITE_P(
"Expiration Date MM / YY", "Expiration Date MM/YY",
"Expiration Date MM - YY", "Expiration Date MM-YY",
"expiration date yy", "Exp Date (MM / YY)"}},
- PatternTestCase{.pattern_name = "ZIP_CODE",
+ PatternTestCase{.pattern_source = PatternSource::kNextGen,
+ .pattern_name = "ZIP_CODE",
.language = "en",
.positive_samples = {"Zip code", "postal code"},
.negative_samples =
@@ -271,11 +516,15 @@ INSTANTIATE_TEST_SUITE_P(
"postleitzahl",
// Not referring to a ZIP code:
"Supported file formats: .docx, .rar, .zip."}},
- PatternTestCase{.pattern_name = "ZIP_CODE",
+ PatternTestCase{.pattern_source = PatternSource::kNextGen,
+ .pattern_name = "ZIP_CODE",
.language = "de",
- .positive_samples = {// Inherited from "en":
- "Zip code", "postal code",
- // Specifically added for "de":
- "postleitzahl"}}));
+ .positive_samples =
+ {// Inherited from "en":
+ "Zip code", "postal code",
+ // Specifically added for "de":
+ "postleitzahl"}}
+#endif
+ ));
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_parsing/resources/regex_patterns.json b/chromium/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json
index 0f8419f171e..a4217b5a21d 100644
--- a/chromium/components/autofill/core/browser/form_parsing/resources/regex_patterns.json
+++ b/chromium/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json
@@ -1,4 +1,20 @@
{
+ "__comment__": [
+ "This file is identified by PatternSource::kLegacy in the code.",
+ "See go/autofill-internal-parsing-patterns for instructions on how to update patterns."
+ ],
+ "PATTERN_SOURCE_DUMMY": {
+ "en": [
+ {
+ "pattern_identifier": "legacy",
+ "positive_pattern": "legacy",
+ "positive_score": 0.0,
+ "negative_pattern": null,
+ "match_field_attributes": [],
+ "match_field_input_types": []
+ }
+ ]
+ },
"ADDRESS_HOME_STREET_NAME": {
"en" : [
{
diff --git a/chromium/components/autofill/core/browser/form_parsing/search_field.cc b/chromium/components/autofill/core/browser/form_parsing/search_field.cc
index b487cb76f26..26867e2f81d 100644
--- a/chromium/components/autofill/core/browser/form_parsing/search_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/search_field.cc
@@ -14,10 +14,11 @@ namespace autofill {
// static
std::unique_ptr<FormField> SearchField::Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
AutofillField* field;
base::span<const MatchPatternRef> patterns =
- GetMatchPatterns(SEARCH_TERM, page_language);
+ GetMatchPatterns(SEARCH_TERM, page_language, pattern_source);
if (ParseFieldSpecifics(scanner, kSearchTermRe,
kDefaultMatchParamsWith<MatchFieldType::kSearch,
diff --git a/chromium/components/autofill/core/browser/form_parsing/search_field.h b/chromium/components/autofill/core/browser/form_parsing/search_field.h
index 268182f24d8..91b7766bb69 100644
--- a/chromium/components/autofill/core/browser/form_parsing/search_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/search_field.h
@@ -25,6 +25,7 @@ class SearchField : public FormField {
public:
static std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
SearchField(const AutofillField* field);
diff --git a/chromium/components/autofill/core/browser/form_parsing/search_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/search_field_unittest.cc
index 7fc31841bb9..290ddd0d265 100644
--- a/chromium/components/autofill/core/browser/form_parsing/search_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/search_field_unittest.cc
@@ -11,9 +11,11 @@
namespace autofill {
-class SearchFieldTest : public FormFieldTest {
+class SearchFieldTest
+ : public FormFieldTestBase,
+ public testing::TestWithParam<PatternProviderFeatureState> {
public:
- SearchFieldTest() = default;
+ SearchFieldTest() : FormFieldTestBase(GetParam()) {}
SearchFieldTest(const SearchFieldTest&) = delete;
SearchFieldTest& operator=(const SearchFieldTest&) = delete;
@@ -21,17 +23,23 @@ class SearchFieldTest : public FormFieldTest {
std::unique_ptr<FormField> Parse(
AutofillScanner* scanner,
const LanguageCode& page_language = LanguageCode("en")) override {
- return SearchField::Parse(scanner, page_language, nullptr);
+ return SearchField::Parse(scanner, page_language, GetActivePatternSource(),
+ /*log_manager=*/nullptr);
}
};
-TEST_F(SearchFieldTest, ParseSearchTerm) {
+INSTANTIATE_TEST_SUITE_P(
+ SearchFieldTest,
+ SearchFieldTest,
+ ::testing::ValuesIn(PatternProviderFeatureState::All()));
+
+TEST_P(SearchFieldTest, ParseSearchTerm) {
AddTextFormFieldData("search", "Search", SEARCH_TERM);
ClassifyAndVerify(ParseResult::PARSED);
}
-TEST_F(SearchFieldTest, ParseNonSearchTerm) {
+TEST_P(SearchFieldTest, ParseNonSearchTerm) {
AddTextFormFieldData("address", "Address", UNKNOWN_TYPE);
ClassifyAndVerify(ParseResult::NOT_PARSED);
diff --git a/chromium/components/autofill/core/browser/form_parsing/transpile_regex_patterns.py b/chromium/components/autofill/core/browser/form_parsing/transpile_regex_patterns.py
index 3eac392d83a..ebcd89a6000 100755
--- a/chromium/components/autofill/core/browser/form_parsing/transpile_regex_patterns.py
+++ b/chromium/components/autofill/core/browser/form_parsing/transpile_regex_patterns.py
@@ -4,6 +4,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import argparse
+from collections import defaultdict
import io
import json
import sys
@@ -11,19 +13,29 @@ import sys
# Generates a set of C++ constexpr constants to facilitate lookup of a set of
# MatchingPatterns by a given tuple (pattern name, language code).
#
+# id_to_name_to_lang_to_patterns is a
+# map from pattern source IDs to a
+# map from pattern names to a
+# map from language codes to a
+# list of patterns,
+# where the pattern source IDs are consecutive natural numbers identifying the
+# input JSON files.
+#
# The constants are:
#
# - kPatterns is an array of MatchingPatterns without duplicates.
# The array is not sorted. The patterns are condensed (see below).
-# - kPatterns_<pattern name>_<language code> contains the indices of the
-# pattterns that for that pattern name and language code.
-# - kPatternMap is a fixed flat map from (pattern name, language code) to
-# a span of MatchPatternRefs.
+# - kPatterns__<pattern source id>__<pattern name>__<language code> contains
+# the indices of the pattterns that from the pattern source for that pattern
+# name and language code.
+# - kPatternMap is a fixed flat map from (pattern name, language code) tuples
+# to arrays of spans of MatchPatternRefs; the indices of the array correspond
+# to the pattern sources.
#
# This representation has larger binary size on Android than using nested
# lookup pattern name -> language code -> span of MatchPatternRefs, but it's
# significantly simpler.
-def generate_cpp_constants(name_to_lang_to_patterns):
+def generate_cpp_constants(id_to_name_to_lang_to_patterns):
# The enum constant of the MatchAttribute::kName.
kName = 1
yield f'static_assert(MatchAttribute::kName == To<MatchAttribute,{kName}>());'
@@ -83,101 +95,150 @@ def generate_cpp_constants(name_to_lang_to_patterns):
f'}}'
# Name of the auxiliary C++ constant.
- def kPatterns(name, lang):
- if lang:
- return f"kPatterns__{name}__{lang.replace('-', '_')}"
- else:
- return f"kPatterns__{name}"
-
- # Validate the JSON input.
- for name, lang_to_patterns in name_to_lang_to_patterns.items():
- if '' in lang_to_patterns:
- raise Exception('JSON format error: language is ""')
-
- # Remember each pattern's language.
- #
- # To ease debugging, we shall sort patterns of equal positive_score by their
- # language.
- for lang_to_patterns in name_to_lang_to_patterns.values():
- for lang, patterns in lang_to_patterns.items():
- for pattern in patterns:
- pattern['lang'] = lang
-
- # For each name, collect the items of all languages and add them as a
- # separate entry for the pseudo-language ''.
- for name, lang_to_patterns in name_to_lang_to_patterns.items():
- lang_to_patterns[''] = [
- p.copy() for ps in lang_to_patterns.values() for p in ps
- ]
-
- # Add the English patterns to all languages except for English itself and the
- # catch-all language ''.
- #
- # The idea is that these patterns should be applied (only) to the HTML source
- # code, i.e., not the user-visible labels. To this end, we mark the English
- # patterns here "supplementary", which will in the subsequent step be encoded
- # in the MatchPatternRef().
- for lang_to_patterns in name_to_lang_to_patterns.values():
- if 'en' not in lang_to_patterns:
- continue
- def make_supplementary_pattern(p):
- p = p.copy()
- p['supplementary'] = True
- return p
- for patterns in (patterns for lang, patterns in lang_to_patterns.items()
- if lang not in ['', 'en']):
- patterns.extend(
- make_supplementary_pattern(p)
- for p in lang_to_patterns['en']
- if kName in p['match_field_attributes'])
+ def kPatterns(id, name, lang):
+ return (f"kPatterns__{id}__{name}__{lang.replace('-', '_')}"
+ if lang else f'kPatterns__{id}__{name}')
+
+ # Preprocess the patterns.
+ for id, name_to_lang_to_patterns in id_to_name_to_lang_to_patterns.items():
+ if "__comment__" in name_to_lang_to_patterns:
+ del name_to_lang_to_patterns["__comment__"]
+
+ # Validate the JSON input.
+ for name, lang_to_patterns in name_to_lang_to_patterns.items():
+ if '' in lang_to_patterns:
+ raise Exception('JSON format error: language is ""')
+
+ # Remember each pattern's language.
+ #
+ # To ease debugging, we shall sort patterns of equal positive_score by their
+ # language.
+ for lang_to_patterns in name_to_lang_to_patterns.values():
+ for lang, patterns in lang_to_patterns.items():
+ for pattern in patterns:
+ pattern['lang'] = lang
+
+ # For each name, collect the items of all languages and add them as a
+ # separate entry for the pseudo-language ''.
+ for name, lang_to_patterns in name_to_lang_to_patterns.items():
+ lang_to_patterns[''] = [
+ p.copy() for ps in lang_to_patterns.values() for p in ps
+ ]
+
+ # Add the English patterns to all languages except for English itself and
+ # the catch-all language ''.
+ #
+ # The idea is that these patterns should be applied (only) to the HTML
+ # source code, i.e., not the user-visible labels. To this end, we mark the
+ # English patterns here "supplementary", which will in the subsequent step
+ # be encoded in the MatchPatternRef().
+ for lang_to_patterns in name_to_lang_to_patterns.values():
+ if 'en' not in lang_to_patterns:
+ continue
+ def make_supplementary_pattern(p):
+ assert kName in p['match_field_attributes']
+ p = p.copy()
+ p['supplementary'] = True
+ return p
+ for patterns in (patterns for lang, patterns in lang_to_patterns.items()
+ if lang not in ['', 'en']):
+ patterns.extend(
+ make_supplementary_pattern(p)
+ for p in lang_to_patterns['en']
+ if kName in p['match_field_attributes'])
# Populate the two maps:
# - a map from C++ MatchingPattern expressions to their index.
- # - a map from names and languages to the their MatchingPatterns, represented
- # as list of tuples (is_supplementary, pattern_index).
+ # - a map from names and languages and IDs to the their MatchingPatterns,
+ # represented as list of tuples (is_supplementary, pattern_index).
pattern_to_index = {}
- name_to_lang_to_patternrefs = {
- name: {lang: [] for lang in lang_to_patterns.keys()
- } for name, lang_to_patterns in name_to_lang_to_patterns.items()
- }
- for name, lang_to_patterns in sorted(name_to_lang_to_patterns.items()):
- for lang, patterns in sorted(lang_to_patterns.items()):
- patternrefs = name_to_lang_to_patternrefs[name][lang]
- sort_key = lambda p: (-p['positive_score'], p['lang'])
- for pattern in sorted(patterns, key=sort_key):
- is_supplementary = ('supplementary' in pattern and
- pattern['supplementary'])
- index = memoize(json_to_cpp_pattern(pattern), pattern_to_index)
- patternrefs.append((is_supplementary, index))
+ name_to_lang_to_id_to_patternrefs = defaultdict(
+ lambda: defaultdict(lambda: defaultdict(list)))
+ for id, name_to_lang_to_patterns in id_to_name_to_lang_to_patterns.items():
+ for name, lang_to_patterns in sorted(name_to_lang_to_patterns.items()):
+ for lang, patterns in sorted(lang_to_patterns.items()):
+ patternrefs = name_to_lang_to_id_to_patternrefs[name][lang][id]
+ sort_key = lambda p: (-p['positive_score'], p['lang'])
+ for pattern in sorted(patterns, key=sort_key):
+ is_supplementary = ('supplementary' in pattern and
+ pattern['supplementary'])
+ pattern_index = memoize(
+ json_to_cpp_pattern(pattern), pattern_to_index)
+ patternrefs.append((is_supplementary, pattern_index))
# Generate the C++ constants.
yield '// The patterns. Referred to by their index in MatchPatternRef.'
- yield 'constexpr std::array kPatterns{'
+ yield '//'
+ yield '// We use C-style arrays rather than std::array because the template'
+ yield '// parameter inference may exceed the default bracket depth limit'
+ yield '// on some (?) clang versions. See crbug.com/1319987.'
+ yield 'constexpr MatchingPattern kPatterns[] {'
for cpp_expr, index in sorted(
pattern_to_index.items(), key=lambda item: item[1]):
yield f'/*[{index}]=*/{cpp_expr},'
yield '};'
+ yield ''
+
+ min_pattern_id = min(id_to_name_to_lang_to_patterns.keys())
+ max_pattern_id = max(id_to_name_to_lang_to_patterns.keys())
- yield '\n// The patterns for field types and languages.'
+ yield '// The patterns for field types and languages.'
yield '// They are sorted by the patterns MatchingPattern::positive_score.'
- for name, lang_to_patternrefs in name_to_lang_to_patternrefs.items():
- for lang, patternrefs in lang_to_patternrefs.items():
- yield (f'constexpr std::array {kPatterns(name, lang)}{{' + ', '.join(
- f'MakeMatchPatternRef({python_bool_to_cpp(is_supplementary)},{index})'
- for is_supplementary, index in patternrefs) + f'}};')
-
- yield '\n// The lookup map for field types and langs.'
- yield '// The keys in the map are essentially const char* pairs.'
+ for name, lang_to_id_to_patternrefs in (
+ name_to_lang_to_id_to_patternrefs.items()):
+ for lang, id_to_patternrefs in lang_to_id_to_patternrefs.items():
+ for id, patternrefs in id_to_patternrefs.items():
+ # If another pattern source has the same patterns for this `name` and
+ # `lang`, we don't need to a new array; a reference suffices.
+ ids_with_the_same_patternrefs = [
+ i for i in range(min_pattern_id, id)
+ if i in name_to_lang_to_id_to_patternrefs[name][lang] and
+ patternrefs == name_to_lang_to_id_to_patternrefs[name][lang][i]
+ ]
+ if ids_with_the_same_patternrefs != []:
+ other_id = ids_with_the_same_patternrefs[0]
+ yield (f'constexpr auto {kPatterns(id, name, lang)} = '
+ f'base::make_span({kPatterns(other_id, name, lang)});')
+ else:
+ yield (f'constexpr MatchPatternRef {kPatterns(id, name, lang)}[] {{' +
+ f', '.join(
+ f'MakeMatchPatternRef('+
+ f'{python_bool_to_cpp(is_supplementary)}, {index})'
+ for is_supplementary, index in patternrefs) + f'}};')
+ yield ''
+
+ yield '// The lookup map for field types and langs.'
+ yield '//'
+ yield '// The key type in the map is essentially a pair of const char*.'
yield '// It also allows for lookup by base::StringPiece pairs (because the'
- yield '// comparator is transparently accepts base::StringPiece pairs).'
+ yield '// comparator transparently accepts base::StringPiece pairs).'
+ yield '//'
+ yield '// The value type is an array of spans of MatchPatternRefs. The'
+ yield '// indices of the array correspond to the pattern source: the patterns'
+ yield '// from the first input JSON file are stored at index 0, etc.'
+ yield '//'
+ yield '// This design exploits that the different JSON files by and large'
+ yield '// contain the same pattern names and languages. If instead we'
+ yield '// generated an individual map for each JSON file, then, assuming four'
+ yield '// JSON files, the duplicate keys would cause 60% overhead, which'
+ yield '// adds up to >10K binary size on Android.'
yield 'constexpr auto kPatternMap = base::MakeFixedFlatMap<' \
- 'NameAndLanguage, base::span<const MatchPatternRef>>({'
- for name, lang_to_patternrefs in sorted(name_to_lang_to_patternrefs.items()):
- for lang in sorted(lang_to_patternrefs.keys()):
- yield f' {{{{"{name}", "{lang}"}}, {kPatterns(name, lang)}}},'
+ 'NameAndLanguage, '\
+ 'std::array<base::span<const MatchPatternRef>, ' \
+ f'{max_pattern_id - min_pattern_id + 1}' \
+ '>>({'
+ for name, lang_to_id_to_patternrefs in sorted(
+ name_to_lang_to_id_to_patternrefs.items()):
+ for lang, id_to_patternrefs in sorted(lang_to_id_to_patternrefs.items()):
+ pattern_array = [
+ kPatterns(id, name, lang)
+ if id in id_to_patternrefs else 'base::span<const MatchPatternRef>{}'
+ for id in range(min_pattern_id, max_pattern_id + 1)
+ ]
+ yield f' {{{{"{name}", "{lang}"}}, {{{", ".join(pattern_array)}}}}},'
yield '}, NameAndLanguageComparator());'
-def generate_cpp_lines(input_json):
+def generate_cpp_lines(id_to_name_to_lang_to_patterns):
yield """// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -251,26 +312,48 @@ struct NameAndLanguageComparator {
}
};
"""
- yield from generate_cpp_constants(input_json)
+ yield from generate_cpp_constants(id_to_name_to_lang_to_patterns)
yield """
-
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_REGEX_PATTERNS_INL_H_
"""
-def build_cpp_file(input_json, output_handle):
- for line in generate_cpp_lines(input_json):
+def build_cpp_file(id_to_name_to_lang_to_patterns, output_handle):
+ for line in generate_cpp_lines(id_to_name_to_lang_to_patterns):
line += '\n'
# unicode() exists and is necessary only in Python 2, not in Python 3.
if sys.version_info[0] < 3:
line = unicode(s, 'utf-8')
output_handle.write(line)
+def parse_json(input_files, output_file):
+ id_to_name_to_lang_to_patterns = {}
+ for index, input_file in enumerate(input_files):
+ with io.open(input_file, 'r', encoding='utf-8') as input_handle:
+ id_to_name_to_lang_to_patterns[index] = json.load(input_handle)
+
+ with io.open(output_file, 'w', encoding='utf-8') as output_handle:
+ build_cpp_file(id_to_name_to_lang_to_patterns, output_handle)
+
if __name__ == '__main__':
- input_file = sys.argv[1]
- output_file = sys.argv[2]
- with io.open(input_file, 'r', encoding='utf-8') as input_handle:
- input_json = json.load(input_handle)
- with io.open(output_file, 'w', encoding='utf-8') as output_handle:
- build_cpp_file(input_json, output_handle)
+ parser = argparse.ArgumentParser(
+ description='Transpiles parsing patterns from JSON to C++.')
+ parser.add_argument(
+ '--input',
+ metavar='json-file',
+ required=True,
+ type=str,
+ nargs='+',
+ help='JSON file(s) containing patterns')
+ parser.add_argument(
+ '--output',
+ metavar='header-file',
+ required=True,
+ type=str,
+ help='C++ header file to be generated')
+ args = parser.parse_args()
+ if not args.input or not args.output:
+ parser.print_help()
+ else:
+ parse_json(args.input, args.output)
diff --git a/chromium/components/autofill/core/browser/form_parsing/travel_field.cc b/chromium/components/autofill/core/browser/form_parsing/travel_field.cc
index c27f51d21a4..c6a696b0296 100644
--- a/chromium/components/autofill/core/browser/form_parsing/travel_field.cc
+++ b/chromium/components/autofill/core/browser/form_parsing/travel_field.cc
@@ -17,18 +17,19 @@ TravelField::~TravelField() = default;
// static
std::unique_ptr<FormField> TravelField::Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager) {
if (!scanner || scanner->IsEnd())
return nullptr;
base::span<const MatchPatternRef> passport_patterns =
- GetMatchPatterns("PASSPORT", page_language);
+ GetMatchPatterns("PASSPORT", page_language, pattern_source);
base::span<const MatchPatternRef> travel_origin_patterns =
- GetMatchPatterns("TRAVEL_ORIGIN", page_language);
+ GetMatchPatterns("TRAVEL_ORIGIN", page_language, pattern_source);
base::span<const MatchPatternRef> travel_destination_patterns =
- GetMatchPatterns("TRAVEL_DESTINATION", page_language);
+ GetMatchPatterns("TRAVEL_DESTINATION", page_language, pattern_source);
base::span<const MatchPatternRef> flight_patterns =
- GetMatchPatterns("FLIGHT", page_language);
+ GetMatchPatterns("FLIGHT", page_language, pattern_source);
auto travel_field = std::make_unique<TravelField>();
if (ParseField(scanner, kPassportRe, passport_patterns,
diff --git a/chromium/components/autofill/core/browser/form_parsing/travel_field.h b/chromium/components/autofill/core/browser/form_parsing/travel_field.h
index 89477788afc..17784dcc45e 100644
--- a/chromium/components/autofill/core/browser/form_parsing/travel_field.h
+++ b/chromium/components/autofill/core/browser/form_parsing/travel_field.h
@@ -21,6 +21,7 @@ class TravelField : public FormField {
static std::unique_ptr<FormField> Parse(AutofillScanner* scanner,
const LanguageCode& page_language,
+ PatternSource pattern_source,
LogManager* log_manager);
protected:
diff --git a/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc b/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc
index b6c5dffeaad..f4699090542 100644
--- a/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc
+++ b/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc
@@ -82,7 +82,7 @@ size_t FindLongestCommonPrefixLengthInStringsWithMinimalLength(
}
// Returns true if |parseable_name| is a valid parseable_name. Current criterion
-// is the |autofill::kParseableNameValidationRe| regex.
+// is the |kParseableNameValidationRe| regex.
bool IsValidParseableName(const base::StringPiece16 parseable_name) {
static const std::u16string kParseableNameValidationPattern =
kParseableNameValidationRe;
diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc
index aba7a2f9d76..d7d3c5a898a 100644
--- a/chromium/components/autofill/core/browser/form_structure.cc
+++ b/chromium/components/autofill/core/browser/form_structure.cc
@@ -38,12 +38,13 @@
#include "components/autofill/core/browser/autofill_regexes.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/form_parsing/field_candidates.h"
+#include "components/autofill/core/browser/form_parsing/buildflags.h"
#include "components/autofill/core/browser/form_parsing/form_field.h"
#include "components/autofill/core/browser/form_processing/label_processing_util.h"
#include "components/autofill/core/browser/form_processing/name_processing_util.h"
#include "components/autofill/core/browser/logging/log_manager.h"
#include "components/autofill/core/browser/metrics/autofill_metrics.h"
+#include "components/autofill/core/browser/metrics/shadow_prediction_metrics.h"
#include "components/autofill/core/browser/randomized_encoder.h"
#include "components/autofill/core/browser/rationalization_util.h"
#include "components/autofill/core/browser/validation.h"
@@ -152,229 +153,138 @@ bool ContactTypeHintMatchesFieldType(const std::string& token,
return false;
}
-// Returns the Chrome Autofill-supported field type corresponding to the given
-// |autocomplete_attribute_value|, if there is one, in the context of the given
-// |field|. Chrome Autofill supports a subset of the field types listed at
-// http://is.gd/whatwg_autocomplete
+// Rationalizes the HTML `type` of `field`, based on the fields properties. At
+// the moment only `max_length` is considered. For example, a max_length of 4
+// might indicate a 4 digit year.
+// In case no rationalization rule applies, the original type is returned.
+HtmlFieldType RationalizeAutocompleteType(HtmlFieldType type,
+ const AutofillField& field) {
+ // (original-type, max-length) -> new-type
+ static constexpr auto rules =
+ base::MakeFixedFlatMap<std::pair<HtmlFieldType, uint64_t>, HtmlFieldType>(
+ {
+ {{HTML_TYPE_ADDITIONAL_NAME, 1},
+ HTML_TYPE_ADDITIONAL_NAME_INITIAL},
+ {{HTML_TYPE_CREDIT_CARD_EXP, 5},
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
+ {{HTML_TYPE_CREDIT_CARD_EXP, 7},
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
+ {{HTML_TYPE_CREDIT_CARD_EXP_YEAR, 2},
+ HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR},
+ {{HTML_TYPE_CREDIT_CARD_EXP_YEAR, 4},
+ HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR},
+ });
+
+ auto* it = rules.find(std::make_pair(type, field.max_length));
+ return it == rules.end() ? type : it->second;
+}
+
+// Chrome Autofill supports a subset of the field types listed at
+// http://is.gd/whatwg_autocomplete. Returns the corresponding HtmlFieldType, if
+// `value` matches any of them.
+absl::optional<HtmlFieldType> ParseStandardizedAutocompleteAttribute(
+ base::StringPiece value) {
+ static constexpr auto standardized_attributes =
+ base::MakeFixedFlatMap<base::StringPiece, HtmlFieldType>({
+ {"additional-name", HTML_TYPE_ADDITIONAL_NAME},
+ {"address-level1", HTML_TYPE_ADDRESS_LEVEL1},
+ {"address-level2", HTML_TYPE_ADDRESS_LEVEL2},
+ {"address-level3", HTML_TYPE_ADDRESS_LEVEL3},
+ {"address-line1", HTML_TYPE_ADDRESS_LINE1},
+ {"address-line2", HTML_TYPE_ADDRESS_LINE2},
+ {"address-line3", HTML_TYPE_ADDRESS_LINE3},
+ {"cc-csc", HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE},
+ {"cc-exp", HTML_TYPE_CREDIT_CARD_EXP},
+ {"cc-exp-month", HTML_TYPE_CREDIT_CARD_EXP_MONTH},
+ {"cc-exp-year", HTML_TYPE_CREDIT_CARD_EXP_YEAR},
+ {"cc-family-name", HTML_TYPE_CREDIT_CARD_NAME_LAST},
+ {"cc-given-name", HTML_TYPE_CREDIT_CARD_NAME_FIRST},
+ {"cc-name", HTML_TYPE_CREDIT_CARD_NAME_FULL},
+ {"cc-number", HTML_TYPE_CREDIT_CARD_NUMBER},
+ {"cc-type", HTML_TYPE_CREDIT_CARD_TYPE},
+ {"country", HTML_TYPE_COUNTRY_CODE},
+ {"country-name", HTML_TYPE_COUNTRY_NAME},
+ {"email", HTML_TYPE_EMAIL},
+ {"family-name", HTML_TYPE_FAMILY_NAME},
+ {"given-name", HTML_TYPE_GIVEN_NAME},
+ {"honorific-prefix", HTML_TYPE_HONORIFIC_PREFIX},
+ {"name", HTML_TYPE_NAME},
+ {"one-time-code", HTML_TYPE_ONE_TIME_CODE},
+ {"organization", HTML_TYPE_ORGANIZATION},
+ {"postal-code", HTML_TYPE_POSTAL_CODE},
+ {"street-address", HTML_TYPE_STREET_ADDRESS},
+ {"tel-area-code", HTML_TYPE_TEL_AREA_CODE},
+ {"tel-country-code", HTML_TYPE_TEL_COUNTRY_CODE},
+ {"tel-extension", HTML_TYPE_TEL_EXTENSION},
+ {"tel", HTML_TYPE_TEL},
+ {"tel-local", HTML_TYPE_TEL_LOCAL},
+ {"tel-local-prefix", HTML_TYPE_TEL_LOCAL_PREFIX},
+ {"tel-local-suffix", HTML_TYPE_TEL_LOCAL_SUFFIX},
+ {"tel-national", HTML_TYPE_TEL_NATIONAL},
+ {"transaction-amount", HTML_TYPE_TRANSACTION_AMOUNT},
+ {"transaction-currency", HTML_TYPE_TRANSACTION_CURRENCY},
+ });
+
+ auto* it = standardized_attributes.find(value);
+ return it != standardized_attributes.end()
+ ? absl::optional<HtmlFieldType>(it->second)
+ : absl::nullopt;
+}
+
+// Tries mapping a non-standardized html autocomplete `value` to an
+// HtmlFieldType.
+absl::optional<HtmlFieldType> ParseAutocompleteAttributeExtensions(
+ base::StringPiece value) {
+ static constexpr auto extensions =
+ base::MakeFixedFlatMap<base::StringPiece, HtmlFieldType>({
+ {"address", HTML_TYPE_STREET_ADDRESS},
+ {"company", HTML_TYPE_ORGANIZATION},
+ {"coupon-code", HTML_TYPE_MERCHANT_PROMO_CODE},
+ {"first-name", HTML_TYPE_GIVEN_NAME},
+ {"gift-code", HTML_TYPE_MERCHANT_PROMO_CODE},
+ {"locality", HTML_TYPE_ADDRESS_LEVEL2},
+ {"promo-code", HTML_TYPE_MERCHANT_PROMO_CODE},
+ {"promotional-code", HTML_TYPE_MERCHANT_PROMO_CODE},
+ {"promotion-code", HTML_TYPE_MERCHANT_PROMO_CODE},
+ {"region", HTML_TYPE_ADDRESS_LEVEL1},
+ {"tel-ext", HTML_TYPE_TEL_EXTENSION},
+ {"upi", HTML_TYPE_UPI_VPA},
+ {"upi-vpa", HTML_TYPE_UPI_VPA},
+ {"username", HTML_TYPE_EMAIL},
+ });
+
+ auto* it = extensions.find(value);
+ return it != extensions.end() ? absl::optional<HtmlFieldType>(it->second)
+ : absl::nullopt;
+}
+
+// Returns the Chrome Autofill-supported field type corresponding to a given
+// autocomplete `value`, if there is one, in the context of the given
+// `field`.
HtmlFieldType FieldTypeFromAutocompleteAttributeValue(
- const std::string& autocomplete_attribute_value,
+ std::string value,
const AutofillField& field) {
- if (autocomplete_attribute_value == "")
+ if (value.empty())
return HTML_TYPE_UNSPECIFIED;
- if (autocomplete_attribute_value == "name")
- return HTML_TYPE_NAME;
-
- if (autocomplete_attribute_value == "honorific-prefix")
- return HTML_TYPE_HONORIFIC_PREFIX;
-
- if (autocomplete_attribute_value == "given-name" ||
- autocomplete_attribute_value == "given_name" ||
- autocomplete_attribute_value == "first-name" ||
- autocomplete_attribute_value == "first_name")
- return HTML_TYPE_GIVEN_NAME;
-
- if (autocomplete_attribute_value == "additional-name" ||
- autocomplete_attribute_value == "additional_name") {
- if (field.max_length == 1)
- return HTML_TYPE_ADDITIONAL_NAME_INITIAL;
- return HTML_TYPE_ADDITIONAL_NAME;
- }
-
- if (autocomplete_attribute_value == "family-name" ||
- autocomplete_attribute_value == "family_name")
- return HTML_TYPE_FAMILY_NAME;
+ // We are lenient and accept '_' instead of '-' as a separator. E.g.
+ // "given_name" is treated like "given-name".
+ base::ReplaceChars(value, "_", "-", &value);
+ // We accept e.g. "phone-country" instead of "tel-country".
+ if (base::StartsWith(value, "phone"))
+ base::ReplaceFirstSubstringAfterOffset(&value, 0, "phone", "tel");
- if (autocomplete_attribute_value == "organization" ||
- autocomplete_attribute_value == "company")
- return HTML_TYPE_ORGANIZATION;
+ absl::optional<HtmlFieldType> type =
+ ParseStandardizedAutocompleteAttribute(value);
+ if (!type.has_value())
+ type = ParseAutocompleteAttributeExtensions(value);
- if (autocomplete_attribute_value == "street-address" ||
- autocomplete_attribute_value == "street_address" ||
- autocomplete_attribute_value == "address")
- return HTML_TYPE_STREET_ADDRESS;
-
- if (autocomplete_attribute_value == "address-line1" ||
- autocomplete_attribute_value == "address_line1")
- return HTML_TYPE_ADDRESS_LINE1;
-
- if (autocomplete_attribute_value == "address-line2" ||
- autocomplete_attribute_value == "address_line2")
- return HTML_TYPE_ADDRESS_LINE2;
-
- if (autocomplete_attribute_value == "address-line3" ||
- autocomplete_attribute_value == "address_line3")
- return HTML_TYPE_ADDRESS_LINE3;
-
- // TODO(estade): remove support for "locality" and "region".
- if (autocomplete_attribute_value == "locality")
- return HTML_TYPE_ADDRESS_LEVEL2;
-
- if (autocomplete_attribute_value == "region")
- return HTML_TYPE_ADDRESS_LEVEL1;
-
- if (autocomplete_attribute_value == "address-level1" ||
- autocomplete_attribute_value == "address_level1")
- return HTML_TYPE_ADDRESS_LEVEL1;
-
- if (autocomplete_attribute_value == "address-level2" ||
- autocomplete_attribute_value == "address_level2")
- return HTML_TYPE_ADDRESS_LEVEL2;
-
- if (autocomplete_attribute_value == "address-level3" ||
- autocomplete_attribute_value == "address_level3")
- return HTML_TYPE_ADDRESS_LEVEL3;
-
- if (autocomplete_attribute_value == "country")
- return HTML_TYPE_COUNTRY_CODE;
-
- if (autocomplete_attribute_value == "country-name" ||
- autocomplete_attribute_value == "country_name")
- return HTML_TYPE_COUNTRY_NAME;
-
- if (autocomplete_attribute_value == "postal-code" ||
- autocomplete_attribute_value == "postal_code")
- return HTML_TYPE_POSTAL_CODE;
-
- // content_switches.h isn't accessible from here, hence we have
- // to copy the string literal. This should be removed soon anyway.
- if (autocomplete_attribute_value == "address" &&
- base::CommandLine::ForCurrentProcess()->HasSwitch(
- "enable-experimental-web-platform-features")) {
- return HTML_TYPE_FULL_ADDRESS;
- }
-
- if (autocomplete_attribute_value == "cc-name" ||
- autocomplete_attribute_value == "cc_name")
- return HTML_TYPE_CREDIT_CARD_NAME_FULL;
-
- if (autocomplete_attribute_value == "cc-given-name" ||
- autocomplete_attribute_value == "cc_given_name")
- return HTML_TYPE_CREDIT_CARD_NAME_FIRST;
-
- if (autocomplete_attribute_value == "cc-family-name" ||
- autocomplete_attribute_value == "cc_family_name")
- return HTML_TYPE_CREDIT_CARD_NAME_LAST;
-
- if (autocomplete_attribute_value == "cc-number" ||
- autocomplete_attribute_value == "cc_number")
- return HTML_TYPE_CREDIT_CARD_NUMBER;
-
- if (autocomplete_attribute_value == "cc-exp" ||
- autocomplete_attribute_value == "cc_exp") {
- if (field.max_length == 5)
- return HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
- if (field.max_length == 7)
- return HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
- return HTML_TYPE_CREDIT_CARD_EXP;
- }
-
- if (autocomplete_attribute_value == "cc-exp-month" ||
- autocomplete_attribute_value == "cc_exp_month")
- return HTML_TYPE_CREDIT_CARD_EXP_MONTH;
-
- if (autocomplete_attribute_value == "cc-exp-year" ||
- autocomplete_attribute_value == "cc_exp_year") {
- if (field.max_length == 2)
- return HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR;
- if (field.max_length == 4)
- return HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR;
- return HTML_TYPE_CREDIT_CARD_EXP_YEAR;
- }
-
- if (autocomplete_attribute_value == "cc-csc" ||
- autocomplete_attribute_value == "cc_csc")
- return HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE;
-
- if (autocomplete_attribute_value == "cc-type" ||
- autocomplete_attribute_value == "cc_type")
- return HTML_TYPE_CREDIT_CARD_TYPE;
-
- if (autocomplete_attribute_value == "transaction-amount" ||
- autocomplete_attribute_value == "transaction_amount")
- return HTML_TYPE_TRANSACTION_AMOUNT;
-
- if (autocomplete_attribute_value == "transaction-currency" ||
- autocomplete_attribute_value == "transaction_currency")
- return HTML_TYPE_TRANSACTION_CURRENCY;
-
- if (autocomplete_attribute_value == "tel" ||
- autocomplete_attribute_value == "phone")
- return HTML_TYPE_TEL;
-
- if (autocomplete_attribute_value == "tel-country-code" ||
- autocomplete_attribute_value == "phone-country-code" ||
- autocomplete_attribute_value == "tel_country_code" ||
- autocomplete_attribute_value == "phone_country_code")
- return HTML_TYPE_TEL_COUNTRY_CODE;
-
- if (autocomplete_attribute_value == "tel-national" ||
- autocomplete_attribute_value == "phone-national" ||
- autocomplete_attribute_value == "tel_national" ||
- autocomplete_attribute_value == "phone_national")
- return HTML_TYPE_TEL_NATIONAL;
-
- if (autocomplete_attribute_value == "tel-area-code" ||
- autocomplete_attribute_value == "phone-area-code" ||
- autocomplete_attribute_value == "tel_area_code" ||
- autocomplete_attribute_value == "phone_area_code")
- return HTML_TYPE_TEL_AREA_CODE;
-
- if (autocomplete_attribute_value == "tel-local" ||
- autocomplete_attribute_value == "phone-local" ||
- autocomplete_attribute_value == "tel_local" ||
- autocomplete_attribute_value == "phone_local")
- return HTML_TYPE_TEL_LOCAL;
-
- if (autocomplete_attribute_value == "tel-local-prefix" ||
- autocomplete_attribute_value == "phone-local-prefix" ||
- autocomplete_attribute_value == "tel_local_prefix" ||
- autocomplete_attribute_value == "phone_local_prefix")
- return HTML_TYPE_TEL_LOCAL_PREFIX;
-
- if (autocomplete_attribute_value == "tel-local-suffix" ||
- autocomplete_attribute_value == "phone-local-suffix" ||
- autocomplete_attribute_value == "tel_local_suffix" ||
- autocomplete_attribute_value == "phone_local_suffix")
- return HTML_TYPE_TEL_LOCAL_SUFFIX;
-
- if (autocomplete_attribute_value == "tel-extension" ||
- autocomplete_attribute_value == "phone-extension" ||
- autocomplete_attribute_value == "phone-ext" ||
- autocomplete_attribute_value == "tel_extension" ||
- autocomplete_attribute_value == "phone_extension" ||
- autocomplete_attribute_value == "phone_ext")
- return HTML_TYPE_TEL_EXTENSION;
-
- if (autocomplete_attribute_value == "email" ||
- autocomplete_attribute_value == "username")
- return HTML_TYPE_EMAIL;
-
- if (autocomplete_attribute_value == "upi-vpa" ||
- autocomplete_attribute_value == "upi_vpa" ||
- autocomplete_attribute_value == "upi")
- return HTML_TYPE_UPI_VPA;
-
- if (autocomplete_attribute_value == "one-time-code")
- return HTML_TYPE_ONE_TIME_CODE;
-
- if (autocomplete_attribute_value == "promo-code" ||
- autocomplete_attribute_value == "promo_code" ||
- autocomplete_attribute_value == "promotion-code" ||
- autocomplete_attribute_value == "promotion_code" ||
- autocomplete_attribute_value == "promotional-code" ||
- autocomplete_attribute_value == "promotional_code" ||
- autocomplete_attribute_value == "coupon-code" ||
- autocomplete_attribute_value == "coupon_code" ||
- autocomplete_attribute_value == "gift-code" ||
- autocomplete_attribute_value == "gift_code")
- return HTML_TYPE_MERCHANT_PROMO_CODE;
-
- return HTML_TYPE_UNRECOGNIZED;
+ return type.has_value() ? RationalizeAutocompleteType(type.value(), field)
+ : HTML_TYPE_UNRECOGNIZED;
}
std::ostream& operator<<(std::ostream& out,
- const autofill::AutofillQueryResponse& response) {
+ const AutofillQueryResponse& response) {
for (const auto& form : response.form_suggestions()) {
out << "\nForm";
for (const auto& field : form.field_suggestions()) {
@@ -539,18 +449,21 @@ void PopulateRandomizedFieldMetadata(
}
}
-// Creates the type relationship rules map. The keys represent the type that has
-// rules, and the value represents the list of required types for the given
-// key. In order to respect the rule, only one of the required types is needed.
-// For example, for Autofill to support fields of type
-// "PHONE_HOME_COUNTRY_CODE", there would need to be at least one other field
-// of type "PHONE_HOME_NUMBER" or "PHONE_HOME_CITY_AND_NUMBER".
-const auto& GetTypeRelationshipMap() {
- static const auto rules =
- base::MakeFixedFlatMap<ServerFieldType, ServerFieldTypeSet>(
- {{PHONE_HOME_COUNTRY_CODE,
- {PHONE_HOME_NUMBER, PHONE_HOME_CITY_AND_NUMBER}}});
- return rules;
+// Defines necessary types for the rationalization logic, meaning that fields of
+// `type` are only filled if at least one field of some `GetNecessaryTypesFor()`
+// is present.
+// TODO(crbug.com/1311937) Cleanup PHONE_HOME_CITY_AND_NUMBER when launched.
+ServerFieldTypeSet GetNecessaryTypesFor(ServerFieldType type) {
+ switch (type) {
+ case PHONE_HOME_COUNTRY_CODE:
+ return {PHONE_HOME_NUMBER,
+ base::FeatureList::IsEnabled(
+ features::kAutofillEnableSupportForPhoneNumberTrunkTypes)
+ ? PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX
+ : PHONE_HOME_CITY_AND_NUMBER};
+ default:
+ return {};
+ }
}
LogBufferSubmitter LogRationalization(LogManager* log_manager) {
@@ -665,7 +578,7 @@ FormStructure::FormStructure(const FormData& form)
fields_.push_back(std::make_unique<AutofillField>(field));
}
- form_signature_ = autofill::CalculateFormSignature(form);
+ form_signature_ = CalculateFormSignature(form);
// Do further processing on the fields, as needed.
ProcessExtractedFields();
}
@@ -683,71 +596,24 @@ FormStructure::~FormStructure() = default;
void FormStructure::DetermineHeuristicTypes(
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
LogManager* log_manager) {
- const auto determine_heuristic_types_start_time =
- AutofillTickClock::NowTicks();
+ SCOPED_UMA_HISTOGRAM_TIMER("Autofill.Timing.DetermineHeuristicTypes");
- // First, try to detect field types based on each field's |autocomplete|
- // attribute value.
ParseFieldTypesFromAutocompleteAttributes();
-
- // Then if there are enough active fields, and if we are dealing with either a
- // proper <form> or a <form>-less checkout, run the heuristics and server
- // prediction routines.
- FieldCandidatesMap field_type_map;
- if (ShouldRunHeuristics()) {
- field_type_map = FormField::ParseFormFields(fields_, current_page_language_,
- is_form_tag_, log_manager);
- } else if (ShouldRunPromoCodeHeuristics()) {
- field_type_map = FormField::ParseFormFieldsForPromoCodes(
- fields_, current_page_language_, is_form_tag_, log_manager);
- }
- if (!field_type_map.empty()) {
- for (const auto& field : fields_) {
- auto iter = field_type_map.find(field->global_id());
- if (iter != field_type_map.end()) {
- const FieldCandidates& candidates = iter->second;
- field->set_heuristic_type(candidates.BestHeuristicType());
-
- auto set_hypothetical_type =
- [&field, &candidates](PredictionSource source) -> void {
- absl::optional<ServerFieldType> type =
- candidates.GetHypotheticalType(source);
- if (type)
- field->set_prediction(source, *type);
- };
- set_hypothetical_type(PredictionSource::kExperimentalHeuristics);
- set_hypothetical_type(PredictionSource::kNextGenHeuristics);
- }
- }
+ ParseFieldTypesWithPatterns(GetActivePatternSource(), log_manager);
+ if (!base::FeatureList::IsEnabled(
+ features::kAutofillDisableShadowHeuristics)) {
+ for (PatternSource shadow_source : GetNonActivePatternSources())
+ ParseFieldTypesWithPatterns(shadow_source, log_manager);
}
UpdateAutofillCount();
IdentifySections(has_author_specified_sections_);
- developer_engagement_metrics_ = 0;
- if (IsAutofillable()) {
- AutofillMetrics::DeveloperEngagementMetric metric =
- has_author_specified_types_
- ? AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS
- : AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS;
- developer_engagement_metrics_ |= 1 << metric;
- AutofillMetrics::LogDeveloperEngagementMetric(metric);
- }
-
- if (has_author_specified_upi_vpa_hint_) {
- AutofillMetrics::LogDeveloperEngagementMetric(
- AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT);
- developer_engagement_metrics_ |=
- 1 << AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT;
- }
-
- if (base::FeatureList::IsEnabled(features::kAutofillPageLanguageDetection)) {
+ if (base::FeatureList::IsEnabled(features::kAutofillPageLanguageDetection))
RationalizeRepeatedFields(form_interactions_ukm_logger, log_manager);
- }
RationalizeFieldTypePredictions(log_manager);
- AutofillMetrics::LogDetermineHeuristicTypesTiming(
- AutofillTickClock::NowTicks() - determine_heuristic_types_start_time);
+ LogDetermineHeuristicTypesMetrics();
}
std::vector<AutofillUploadContents> FormStructure::EncodeUploadRequest(
@@ -778,7 +644,7 @@ std::vector<AutofillUploadContents> FormStructure::EncodeUploadRequest(
? submission_event_
: ToSubmissionIndicatorEvent(submission_source_);
- DCHECK(autofill::mojom::IsKnownEnumValue(triggering_event));
+ DCHECK(mojom::IsKnownEnumValue(triggering_event));
upload.set_submission_event(
static_cast<AutofillUploadContents_SubmissionIndicatorEvent>(
triggering_event));
@@ -1059,7 +925,7 @@ std::vector<FieldGlobalId> FormStructure::FindFieldsEligibleForManualFilling(
for (const auto* form : forms) {
for (const auto& field : form->fields_) {
FieldTypeGroup field_type_group =
- autofill::GroupTypeOfServerFieldType(field->server_type());
+ GroupTypeOfServerFieldType(field->server_type());
// In order to trigger the payments bottom sheet that assists users to
// manually fill the form, credit card form fields are marked eligible for
// manual filling. Also, if a field is not classified to a type, we can
@@ -1230,7 +1096,10 @@ void FormStructure::RetrieveFromCache(
if (!only_server_and_autofill_state) {
// Transfer attributes of the cached AutofillField to the newly created
// AutofillField.
- field->set_heuristic_type(cached_field->heuristic_type());
+ for (int i = 0; i <= static_cast<int>(PatternSource::kMaxValue); ++i) {
+ PatternSource s = static_cast<PatternSource>(i);
+ field->set_heuristic_type(s, cached_field->heuristic_type(s));
+ }
field->SetHtmlType(cached_field->html_type(),
cached_field->html_mode());
field->section = cached_field->section;
@@ -1292,7 +1161,8 @@ void FormStructure::LogQualityMetrics(
const base::TimeTicks& submission_time,
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
bool did_show_suggestions,
- bool observed_submission) const {
+ bool observed_submission,
+ const FormInteractionCounts& form_interaction_counts) const {
// Use the same timestamp on UKM Metrics generated within this method's scope.
AutofillMetrics::UkmTimestampPin timestamp_pin(form_interactions_ukm_logger);
@@ -1334,8 +1204,7 @@ void FormStructure::LogQualityMetrics(
observed_submission ? AutofillMetrics::TYPE_SUBMISSION
: AutofillMetrics::TYPE_NO_SUBMISSION;
- for (size_t i = 0; i < field_count(); ++i) {
- auto* const field = this->field(i);
+ for (auto& field : *this) {
AutofillType type = field->Type();
if (IsUPIVirtualPaymentAddress(field->value)) {
@@ -1355,6 +1224,7 @@ void FormStructure::LogQualityMetrics(
form_interactions_ukm_logger, *this, *field, metric_type);
AutofillMetrics::LogOverallPredictionQualityMetrics(
form_interactions_ukm_logger, *this, *field, metric_type);
+ autofill::metrics::LogShadowPredictionComparison(*field);
// We count fields that were autofilled but later modified, regardless of
// whether the data now in the field is recognized.
if (field->previously_autofilled())
@@ -1513,7 +1383,8 @@ void FormStructure::LogQualityMetrics(
AutofillMetrics::LogAutofillFormSubmittedState(
state, is_for_credit_card, has_upi_vpa_field, GetFormTypes(),
- form_parsed_timestamp_, form_signature(), form_interactions_ukm_logger);
+ form_parsed_timestamp_, form_signature(), form_interactions_ukm_logger,
+ form_interaction_counts);
// The perfect filling metric is only recorded if Autofill was used on at
// least one field. This conditions this metric on Assistance, Readiness and
@@ -1562,6 +1433,25 @@ void FormStructure::LogQualityMetricsBasedOnAutocomplete(
}
}
+void FormStructure::LogDetermineHeuristicTypesMetrics() {
+ developer_engagement_metrics_ = 0;
+ if (IsAutofillable()) {
+ AutofillMetrics::DeveloperEngagementMetric metric =
+ has_author_specified_types_
+ ? AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS
+ : AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS;
+ developer_engagement_metrics_ |= 1 << metric;
+ AutofillMetrics::LogDeveloperEngagementMetric(metric);
+ }
+
+ if (has_author_specified_upi_vpa_hint_) {
+ AutofillMetrics::LogDeveloperEngagementMetric(
+ AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT);
+ developer_engagement_metrics_ |=
+ 1 << AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT;
+ }
+}
+
void FormStructure::ParseFieldTypesFromAutocompleteAttributes() {
if (was_parsed_for_autocomplete_attributes_)
return;
@@ -1673,6 +1563,30 @@ void FormStructure::ParseFieldTypesFromAutocompleteAttributes() {
was_parsed_for_autocomplete_attributes_ = true;
}
+void FormStructure::ParseFieldTypesWithPatterns(PatternSource pattern_source,
+ LogManager* log_manager) {
+ FieldCandidatesMap field_type_map;
+ if (ShouldRunHeuristics()) {
+ field_type_map =
+ FormField::ParseFormFields(fields_, current_page_language_,
+ is_form_tag_, pattern_source, log_manager);
+ } else if (ShouldRunPromoCodeHeuristics()) {
+ field_type_map = FormField::ParseFormFieldsForPromoCodes(
+ fields_, current_page_language_, is_form_tag_, pattern_source,
+ log_manager);
+ }
+ if (field_type_map.empty())
+ return;
+
+ for (const auto& field : fields_) {
+ auto iter = field_type_map.find(field->global_id());
+ if (iter == field_type_map.end())
+ continue;
+ const FieldCandidates& candidates = iter->second;
+ field->set_heuristic_type(pattern_source, candidates.BestHeuristicType());
+ }
+}
+
const AutofillField* FormStructure::field(size_t index) const {
if (index >= fields_.size()) {
NOTREACHED();
@@ -1889,7 +1803,38 @@ void FormStructure::RationalizeCreditCardFieldPredictions(
}
}
-void FormStructure::RationalizePhoneNumbersInSection(std::string section) {
+void FormStructure::RationalizeStreetAddressAndAddressLine(
+ LogManager* log_manager) {
+ if (fields_.size() < 2)
+ return;
+ for (auto field = fields_.begin() + 1; field != fields_.end(); ++field) {
+ if ((*field)->ComputedType().GetStorableType() != ADDRESS_HOME_LINE2)
+ continue;
+ // Rationalize a preceding street address belonging to the same section
+ // unless it's a server override.
+ AutofillField& previous_field = **(field - 1);
+ if (previous_field.ComputedType().GetStorableType() !=
+ ADDRESS_HOME_STREET_ADDRESS ||
+ previous_field.section != (*field)->section ||
+ previous_field.server_type_prediction_is_override()) {
+ continue;
+ }
+ // TODO(crbug.com/1326425): Remove once feature is lanuched.
+ if (!base::FeatureList::IsEnabled(
+ features::kAutofillRationalizeStreetAddressAndAddressLine)) {
+ continue;
+ }
+ if (log_manager) {
+ LogRationalization(log_manager)
+ << "Street Address Rationalization: Converting sequence of (street "
+ "address, address line 2) to (address line 1, address line 2)";
+ }
+ previous_field.SetTypeTo(AutofillType(ADDRESS_HOME_LINE1));
+ }
+}
+
+void FormStructure::RationalizePhoneNumbersInSection(
+ const std::string& section) {
if (phone_rationalized_[section])
return;
std::vector<AutofillField*> fields;
@@ -1969,12 +1914,13 @@ void FormStructure::ApplyRationalizationsToHiddenSelects(
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) {
ServerFieldType old_type = fields_[field_index]->Type().GetStorableType();
- // Walk on the hidden select fields right after the field_index which share
- // the same type with the field_index, and apply the rationalization to them
- // as well. These fields, if any, function as one field with the field_index.
+ // Walk on the unfocusable select fields right after the field_index which
+ // share the same type with the field_index, and apply the rationalization to
+ // them as well. These fields, if any, function as one field with the
+ // field_index.
for (auto current_index = field_index + 1; current_index < fields_.size();
current_index++) {
- if (fields_[current_index]->IsVisible() ||
+ if (fields_[current_index]->IsFocusable() ||
fields_[current_index]->form_control_type != "select-one" ||
fields_[current_index]->Type().GetStorableType() != old_type)
break;
@@ -1987,7 +1933,7 @@ void FormStructure::ApplyRationalizationsToHiddenSelects(
if (field_index == 0)
return;
for (auto current_index = field_index - 1;; current_index--) {
- if (fields_[current_index]->IsVisible() ||
+ if (fields_[current_index]->IsFocusable() ||
fields_[current_index]->form_control_type != "select-one" ||
fields_[current_index]->Type().GetStorableType() != old_type)
break;
@@ -2022,11 +1968,11 @@ void FormStructure::ApplyRationalizationsToFields(
ServerFieldType upper_type,
ServerFieldType lower_type,
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) {
- // Hidden fields are ignored during the rationalization, but 'select' hidden
- // fields also get autofilled to support their corresponding visible
+ // Unfocusable fields are ignored during the rationalization, but unfocusable
+ // 'select' fields also get autofilled to support their corresponding visible
// 'synthetic fields'. So, if a field's type is rationalized, we should make
- // sure that the rationalization is also applied to its corresponding hidden
- // fields, if any.
+ // sure that the rationalization is also applied to its corresponding
+ // unfocusable fields, if any.
ApplyRationalizationsToHiddenSelects(upper_index, upper_type,
form_interactions_ukm_logger);
ApplyRationalizationsToFieldAndLog(upper_index, upper_type,
@@ -2043,7 +1989,7 @@ bool FormStructure::FieldShouldBeRationalizedToCountry(size_t upper_index) {
// in its section. Otherwise, the upper field is a state, and the lower one
// is a country.
for (int field_index = upper_index - 1; field_index >= 0; --field_index) {
- if (fields_[field_index]->IsVisible() &&
+ if (fields_[field_index]->IsFocusable() &&
AutofillType(fields_[field_index]->Type().GetStorableType()).group() ==
FieldTypeGroup::kAddressHome &&
fields_[field_index]->section == fields_[upper_index]->section) {
@@ -2159,8 +2105,9 @@ void FormStructure::RationalizeRepeatedFields(
for (size_t i = 0; i < fields_.size(); ++i) {
const AutofillField& field = *fields_[i];
- // The hidden fields are not considered when rationalizing.
- if (!field.IsVisible())
+ // The unfocusable fields are considered invisible and therefore not
+ // considered when rationalizing.
+ if (!field.IsFocusable())
continue;
// The billing and non-billing types are aggregated.
auto current_type = field.Type().GetStorableType();
@@ -2192,6 +2139,7 @@ void FormStructure::RationalizeRepeatedFields(
void FormStructure::RationalizeFieldTypePredictions(LogManager* log_manager) {
RationalizeCreditCardFieldPredictions(log_manager);
+ RationalizeStreetAddressAndAddressLine(log_manager);
for (const auto& field : fields_) {
field->SetTypeTo(field->Type());
}
@@ -2386,7 +2334,7 @@ void FormStructure::IdentifySectionsWithNewMethod() {
already_saw_current_type |= (seen_types.count(NAME_LAST) > 0);
}
- bool ignored_field = !field->IsVisible();
+ bool ignored_field = !field->IsFocusable();
// This is the first visible field after a hidden section. Consider it as
// the continuation of the last visible section.
@@ -2554,7 +2502,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) {
already_saw_current_type |= (seen_types.count(NAME_LAST) > 0);
}
- bool ignored_field = !field->IsVisible();
+ bool ignored_field = !field->IsFocusable();
// This is the first visible field after a hidden section. Consider it as
// the continuation of the last visible section.
@@ -2659,7 +2607,7 @@ void FormStructure::ExtractParseableFieldLabels() {
field_labels.reserve(field_count());
for (const auto& field : *this) {
// Skip fields that are not a text input or not visible.
- if (!field->IsTextInputElement() || !field->IsVisible()) {
+ if (!field->IsTextInputElement() || !field->IsFocusable()) {
continue;
}
field_labels.push_back(field->label);
@@ -2676,7 +2624,7 @@ void FormStructure::ExtractParseableFieldLabels() {
size_t idx = 0;
for (auto& field : *this) {
- if (!field->IsTextInputElement() || !field->IsVisible()) {
+ if (!field->IsTextInputElement() || !field->IsFocusable()) {
// For those fields, set the original label.
field->set_parseable_label(field->label);
continue;
@@ -2691,7 +2639,7 @@ void FormStructure::ExtractParseableFieldNames() {
std::vector<base::StringPiece16> names;
names.reserve(field_count());
for (const auto& field : *this) {
- names.push_back(base::StringPiece16(field->name));
+ names.emplace_back(field->name);
}
// Determine the parseable names and write them into the corresponding field.
@@ -2723,23 +2671,17 @@ void FormStructure::RationalizeTypeRelationships(LogManager* log_manager) {
types.insert(field->Type().GetStorableType());
}
- const auto& type_relationship_rules = GetTypeRelationshipMap();
-
for (const auto& field : fields_) {
ServerFieldType field_type = field->Type().GetStorableType();
- const auto* ruleset_iterator = type_relationship_rules.find(field_type);
- if (ruleset_iterator != type_relationship_rules.end()) {
- // We have relationship rules for this type. Verify that at least one of
- // the required related type is present.
- if (!types.contains_any(ruleset_iterator->second)) {
- // No required type was found, the current field failed the relationship
- // requirements for its type. Disabling Autofill for this field.
- field->SetTypeTo(AutofillType(UNKNOWN_TYPE));
- LogRationalization(log_manager)
- << "RationalizeTypeRelationships: Fields of type "
- << FieldTypeToStringPiece(field_type)
- << " can only exist if other fields of specific types exist.";
- }
+ ServerFieldTypeSet necessary_types = GetNecessaryTypesFor(field_type);
+ if (!necessary_types.empty() && !types.contains_any(necessary_types)) {
+ // We have relationship rules for this type, but no `neccessary_type` was
+ // found. Disabling Autofill for this field.
+ field->SetTypeTo(AutofillType(UNKNOWN_TYPE));
+ LogRationalization(log_manager)
+ << "RationalizeTypeRelationships: Fields of type "
+ << FieldTypeToStringPiece(field_type)
+ << " can only exist if other fields of specific types exist.";
}
}
}
@@ -2858,6 +2800,7 @@ LogBuffer& operator<<(LogBuffer& buffer, const FormStructure& form) {
base::NumberToString(
HashFormSignature(field->host_form_signature))});
buffer << Tr{} << "Name:" << field->parseable_name();
+ buffer << Tr{} << "Placeholder:" << field->placeholder;
auto type = field->Type().ToString();
auto heuristic_type = AutofillType(field->heuristic_type()).ToString();
@@ -2887,6 +2830,10 @@ LogBuffer& operator<<(LogBuffer& buffer, const FormStructure& form) {
buffer << Tr{} << "Label:" << truncated_label;
buffer << Tr{} << "Is empty:" << (field->IsEmpty() ? "Yes" : "No");
+ buffer << Tr{} << "Is focusable:"
+ << (field->IsFocusable() ? "Yes (focusable)" : "No (unfocusable)");
+ buffer << Tr{} << "Is visible:"
+ << (field->is_visible ? "Yes (visible)" : "No (invisible)");
buffer << CTag{"table"};
buffer << CTag{"td"};
buffer << CTag{"tr"};
diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h
index bcf9460ad10..ca9a21f1382 100644
--- a/chromium/components/autofill/core/browser/form_structure.h
+++ b/chromium/components/autofill/core/browser/form_structure.h
@@ -19,8 +19,10 @@
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_parsing/field_candidates.h"
#include "components/autofill/core/browser/form_types.h"
#include "components/autofill/core/browser/metrics/autofill_metrics.h"
+#include "components/autofill/core/browser/metrics/form_interactions_counter.h"
#include "components/autofill/core/browser/proto/api_v1.pb.h"
#include "components/autofill/core/common/dense_set.h"
#include "components/autofill/core/common/language_code.h"
@@ -120,7 +122,7 @@ class FormStructure {
// included in |query| and |queried_form_signatures|.
static bool EncodeQueryRequest(
const std::vector<FormStructure*>& forms,
- autofill::AutofillPageQueryRequest* query,
+ AutofillPageQueryRequest* query,
std::vector<FormSignature>* queried_form_signatures);
// Parses `payload` as AutofillQueryResponse proto and calls
@@ -207,7 +209,8 @@ class FormStructure {
const base::TimeTicks& submission_time,
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
bool did_show_suggestions,
- bool observed_submission) const;
+ bool observed_submission,
+ const FormInteractionCounts& form_interaction_counts) const;
// Log the quality of the heuristics and server predictions for this form
// structure, if autocomplete attributes are present on the fields (they are
@@ -216,6 +219,8 @@ class FormStructure {
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger)
const;
+ void LogDetermineHeuristicTypesMetrics();
+
// Classifies each field in |fields_| based upon its |autocomplete| attribute,
// if the attribute is available. The association is stored into the field's
// |heuristic_type|.
@@ -226,6 +231,10 @@ class FormStructure {
// specifies a section for at least one field.
void ParseFieldTypesFromAutocompleteAttributes();
+ // Classifies each field in |fields_| using the regular expressions.
+ void ParseFieldTypesWithPatterns(PatternSource pattern_source,
+ LogManager* log_manager);
+
// Returns the values that can be filled into the form structure for the
// given type. For example, there's no way to fill in a value of "The Moon"
// into ADDRESS_HOME_STATE if the form only has a
@@ -237,7 +246,7 @@ class FormStructure {
// Rationalize phone number fields in a given section, that is only fill
// the fields that are considered composing a first complete phone number.
- void RationalizePhoneNumbersInSection(std::string section);
+ void RationalizePhoneNumbersInSection(const std::string& section);
// Overrides server predictions with specific heuristic predictions:
// * NAME_LAST_SECOND heuristic predictions are unconditionally used.
@@ -263,6 +272,7 @@ class FormStructure {
std::vector<std::unique_ptr<AutofillField>>::const_iterator begin() const {
return fields_.begin();
}
+
std::vector<std::unique_ptr<AutofillField>>::const_iterator end() const {
return fields_.end();
}
@@ -367,7 +377,7 @@ class FormStructure {
void set_overall_field_type_for_testing(size_t field_index,
ServerFieldType type) {
if (field_index < fields_.size() && type > 0 && type < MAX_VALID_FIELD_TYPE)
- fields_[field_index]->set_heuristic_type(type);
+ fields_[field_index]->set_heuristic_type(GetActivePatternSource(), type);
}
// Set the server field type for |fields_[field_index]| to |type| for testing
// purposes.
@@ -430,20 +440,6 @@ class FormStructure {
FormVersion version() const { return version_; }
- bool ShouldSkipFieldVisibleForTesting(const FormFieldData& field) const {
- return ShouldSkipField(field);
- }
-
- static void ProcessQueryResponseForTesting(
- const AutofillQueryResponse& response,
- const std::vector<FormStructure*>& forms,
- const std::vector<FormSignature>& queried_form_signatures,
- AutofillMetrics::FormInteractionsUkmLogger*
- form_interactions_ukm_logger) {
- ProcessQueryResponse(response, forms, queried_form_signatures,
- form_interactions_ukm_logger, nullptr);
- }
-
void set_single_username_data(
AutofillUploadContents::SingleUsernameData single_username_data) {
single_username_data_ = single_username_data;
@@ -454,15 +450,7 @@ class FormStructure {
}
private:
- friend class AutofillMergeTest;
- friend class FormStructureTestImpl;
- friend class ParameterizedFormStructureTest;
- FRIEND_TEST_ALL_PREFIXES(AutofillDownloadTest, QueryAndUploadTest);
- FRIEND_TEST_ALL_PREFIXES(FormStructureTestImpl, FindLongestCommonPrefix);
- FRIEND_TEST_ALL_PREFIXES(FormStructureTestImpl, FindLongestCommonAffixLength);
- FRIEND_TEST_ALL_PREFIXES(FormStructureTestImpl, IsValidParseableName);
- FRIEND_TEST_ALL_PREFIXES(FormStructureTestImpl,
- RationalizePhoneNumber_RunsOncePerSection);
+ friend class FormStructureTestApi;
// This class wraps a vector of vectors of field indices. The indices of a
// vector belong to the same group.
@@ -487,6 +475,11 @@ class FormStructure {
// unlikely to be correct, the function will override that prediction.
void RationalizeCreditCardFieldPredictions(LogManager* log_manager);
+ // A function to rewrite sequences of (street address, address_line2) into
+ // (address_line1, address_line2) as server predictions sometimes introduce
+ // wrong street address predictions.
+ void RationalizeStreetAddressAndAddressLine(LogManager* log_manager);
+
// The rationalization is based on the visible fields, but should be applied
// to the hidden select fields. This is because hidden 'select' fields are
// also autofilled to take care of the synthetic fields.
@@ -559,7 +552,7 @@ class FormStructure {
void EncodeFormFieldsForUpload(
bool is_raw_metadata_uploading_enabled,
absl::optional<FormGlobalId> filter_renderer_form_id,
- autofill::AutofillUploadContents* upload) const;
+ AutofillUploadContents* upload) const;
// Returns true if the form has no fields, or too many.
bool IsMalformed() const;
diff --git a/chromium/components/autofill/core/browser/form_structure_process_query_response_fuzzer.cc b/chromium/components/autofill/core/browser/form_structure_process_query_response_fuzzer.cc
index 85e57200c81..170c598eb1e 100644
--- a/chromium/components/autofill/core/browser/form_structure_process_query_response_fuzzer.cc
+++ b/chromium/components/autofill/core/browser/form_structure_process_query_response_fuzzer.cc
@@ -9,6 +9,7 @@
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/form_structure_test_api.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "testing/libfuzzer/proto/lpm_interface.h"
@@ -36,7 +37,7 @@ void AddField(const std::string& label,
// forms vectors, so it can be changed if needed.
DEFINE_BINARY_PROTO_FUZZER(const AutofillQueryResponse& response) {
std::vector<FormStructure*> forms;
- FormStructure::ProcessQueryResponseForTesting(
+ FormStructureTestApi::ProcessQueryResponse(
response, forms, test::GetEncodedSignatures(forms), nullptr);
FormData form_data;
@@ -45,7 +46,7 @@ DEFINE_BINARY_PROTO_FUZZER(const AutofillQueryResponse& response) {
FormStructure form(form_data);
forms.push_back(&form);
- FormStructure::ProcessQueryResponseForTesting(
+ FormStructureTestApi::ProcessQueryResponse(
response, forms, test::GetEncodedSignatures(forms), nullptr);
}
diff --git a/chromium/components/autofill/core/browser/form_structure_test_api.h b/chromium/components/autofill/core/browser/form_structure_test_api.h
new file mode 100644
index 00000000000..08894272f87
--- /dev/null
+++ b/chromium/components/autofill/core/browser/form_structure_test_api.h
@@ -0,0 +1,69 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_TEST_API_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_TEST_API_H_
+
+#include <string>
+
+#include "base/memory/raw_ptr.h"
+#include "base/strings/string_piece.h"
+#include "components/autofill/core/browser/form_structure.h"
+
+namespace autofill {
+
+// Exposes some testing operations for FormStructure.
+class FormStructureTestApi {
+ public:
+ static void ParseApiQueryResponse(
+ base::StringPiece payload,
+ const std::vector<FormStructure*>& forms,
+ const std::vector<FormSignature>& queried_form_signatures,
+ AutofillMetrics::FormInteractionsUkmLogger* ukm_logger,
+ LogManager* log_manager = nullptr) {
+ FormStructure::ParseApiQueryResponse(
+ payload, forms, queried_form_signatures, ukm_logger, log_manager);
+ }
+
+ static void ProcessQueryResponse(
+ const AutofillQueryResponse& response,
+ const std::vector<FormStructure*>& forms,
+ const std::vector<FormSignature>& queried_form_signatures,
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ LogManager* log_manager = nullptr) {
+ FormStructure::ProcessQueryResponse(
+ response, forms, queried_form_signatures, form_interactions_ukm_logger,
+ log_manager);
+ }
+
+ explicit FormStructureTestApi(FormStructure* form_structure)
+ : form_structure_(form_structure) {
+ DCHECK(form_structure_);
+ }
+
+ const std::vector<std::unique_ptr<AutofillField>>& fields() {
+ return form_structure_->fields_;
+ }
+
+ void IdentifySections(bool has_author_specified_sections) {
+ form_structure_->IdentifySections(has_author_specified_sections);
+ }
+
+ bool phone_rationalized(const std::string& section) const {
+ auto it = form_structure_->phone_rationalized_.find(section);
+ return it != form_structure_->phone_rationalized_.end() && it->second;
+ }
+
+ void ParseFieldTypesWithPatterns(PatternSource pattern_source) {
+ return form_structure_->ParseFieldTypesWithPatterns(pattern_source,
+ nullptr);
+ }
+
+ private:
+ raw_ptr<FormStructure> form_structure_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_TEST_API_H_
diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc
index d000ae26b8a..8bfe6ca5cd3 100644
--- a/chromium/components/autofill/core/browser/form_structure_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc
@@ -19,6 +19,8 @@
#include "components/autofill/core/browser/autofill_form_test_utils.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_parsing/buildflags.h"
+#include "components/autofill/core/browser/form_structure_test_api.h"
#include "components/autofill/core/browser/proto/api_v1.pb.h"
#include "components/autofill/core/browser/randomized_encoder.h"
#include "components/autofill/core/common/autofill_features.h"
@@ -35,18 +37,23 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
-using base::ASCIIToUTF16;
+using ::base::ASCIIToUTF16;
+using ::testing::AllOf;
+using ::testing::Each;
using ::testing::ElementsAre;
+using ::testing::IsEmpty;
using ::testing::Not;
+using ::testing::Pointee;
+using ::testing::ResultOf;
using ::testing::Truly;
using ::testing::UnorderedElementsAre;
-using version_info::GetProductNameAndVersionForUserAgent;
+using ::version_info::GetProductNameAndVersionForUserAgent;
namespace autofill {
-using features::kAutofillLabelAffixRemoval;
-using mojom::SubmissionIndicatorEvent;
-using mojom::SubmissionSource;
+using autofill::features::kAutofillLabelAffixRemoval;
+using autofill::mojom::SubmissionIndicatorEvent;
+using autofill::mojom::SubmissionSource;
namespace {
@@ -109,6 +116,19 @@ auto UnorderedElementsSerializeSameAs(Matchers... element_matchers) {
return UnorderedElementsAre(SerializesSameAs(element_matchers)...);
}
+FormStructureTestApi test_api(FormStructure* form_structure) {
+ return FormStructureTestApi(form_structure);
+}
+
+constexpr DenseSet<PatternSource> kAllPatternSources {
+#if !BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ PatternSource::kLegacy
+#else
+ PatternSource::kLegacy, PatternSource::kDefault, PatternSource::kExperimental,
+ PatternSource::kNextGen
+#endif
+};
+
} // namespace
class FormStructureTestImpl : public test::FormStructureTest {
@@ -153,6 +173,49 @@ class ParameterizedFormStructureTest
: public FormStructureTestImpl,
public testing::WithParamInterface<bool> {};
+class FormStructureTest_ForPatternSource
+ : public FormStructureTestImpl,
+ public testing::WithParamInterface<PatternSource> {
+ public:
+ FormStructureTest_ForPatternSource() {
+ scoped_feature_list_.InitWithFeaturesAndParameters(
+ {base::test::ScopedFeatureList::FeatureAndParams(
+ features::kAutofillParsingPatternProvider,
+ {{"prediction_source", pattern_source_as_string()}})},
+ {});
+ }
+
+ PatternSource pattern_source() const { return GetParam(); }
+
+ std::string pattern_source_as_string() const {
+ switch (pattern_source()) {
+ case PatternSource::kLegacy:
+ return "legacy";
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ case PatternSource::kDefault:
+ return "default";
+ case PatternSource::kExperimental:
+ return "experimental";
+ case PatternSource::kNextGen:
+ return "nextgen";
+#endif
+ }
+ }
+
+ DenseSet<PatternSource> other_pattern_sources() const {
+ DenseSet<PatternSource> patterns = kAllPatternSources;
+ patterns.erase(pattern_source());
+ return patterns;
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(FormStructureTest,
+ FormStructureTest_ForPatternSource,
+ ::testing::ValuesIn(kAllPatternSources));
+
TEST_F(FormStructureTestImpl, FieldCount) {
CheckFormStructureTestData({{{.description_for_logging = "FieldCount",
.fields = {{.role = ServerFieldType::USERNAME},
@@ -6187,9 +6250,11 @@ TEST_F(FormStructureTestImpl, RationalizePhoneNumber_RunsOncePerSection) {
test::GetEncodedSignatures(forms),
nullptr, nullptr);
- EXPECT_FALSE(form_structure.phone_rationalized_["fullName_0_11-default"]);
+ EXPECT_FALSE(
+ test_api(&form_structure).phone_rationalized("fullName_0_11-default"));
form_structure.RationalizePhoneNumbersInSection("fullName_0_11-default");
- EXPECT_TRUE(form_structure.phone_rationalized_["fullName_0_11-default"]);
+ EXPECT_TRUE(
+ test_api(&form_structure).phone_rationalized("fullName_0_11-default"));
ASSERT_EQ(1U, forms.size());
ASSERT_EQ(4U, forms[0]->field_count());
EXPECT_EQ(NAME_FULL, forms[0]->field(0)->server_type());
@@ -6202,6 +6267,54 @@ TEST_F(FormStructureTestImpl, RationalizePhoneNumber_RunsOncePerSection) {
EXPECT_TRUE(forms[0]->field(3)->only_fill_when_focused());
}
+TEST_F(FormStructureTestImpl, RationalizeStreetAddressAndAddressLine) {
+ base::test::ScopedFeatureList enabled;
+ enabled.InitAndEnableFeature(
+ features::kAutofillRationalizeStreetAddressAndAddressLine);
+
+ FormData form;
+ form.url = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.label = u"Full Name";
+ field.name = u"fullName";
+ field.unique_renderer_id = MakeFieldRendererId();
+ form.fields.push_back(field);
+
+ field.label = u"Address1";
+ field.name = u"address1";
+ field.unique_renderer_id = MakeFieldRendererId();
+ form.fields.push_back(field);
+
+ field.label = u"Address2";
+ field.name = u"address2";
+ field.unique_renderer_id = MakeFieldRendererId();
+ form.fields.push_back(field);
+
+ AutofillQueryResponse response;
+ auto* form_suggestion = response.add_form_suggestions();
+ AddFieldSuggestionToForm(form_suggestion, form.fields[0], NAME_FULL);
+ AddFieldSuggestionToForm(form_suggestion, form.fields[1],
+ ADDRESS_HOME_STREET_ADDRESS);
+ AddFieldSuggestionToForm(form_suggestion, form.fields[2], ADDRESS_HOME_LINE2);
+
+ std::string response_string = SerializeAndEncode(response);
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms = {&form_structure};
+ FormStructure::ParseApiQueryResponse(response_string, forms,
+ test::GetEncodedSignatures(forms),
+ nullptr, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(3U, forms[0]->field_count());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_LINE1, forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_LINE2, forms[0]->field(2)->Type().GetStorableType());
+}
+
// Tests that a form that has only one address predicted as
// ADDRESS_HOME_STREET_ADDRESS is not modified by the address rationalization.
TEST_F(FormStructureTestImpl, RationalizeRepeatedFields_OneAddress) {
@@ -8541,4 +8654,30 @@ TEST_F(FormStructureTestImpl, FindFieldsEligibleForManualFilling) {
FormStructure::FindFieldsEligibleForManualFilling(forms));
}
+// Tests that ParseFieldTypesWithPatterns() sets (only) the PatternSource.
+TEST_P(FormStructureTest_ForPatternSource, ParseFieldTypesWithPatterns) {
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ FormStructure form_structure(form);
+ test_api(&form_structure).ParseFieldTypesWithPatterns(pattern_source());
+ ASSERT_THAT(test_api(&form_structure).fields(), Not(IsEmpty()));
+
+ auto get_heuristic_type = [&](const AutofillField& field) {
+ return field.heuristic_type(pattern_source());
+ };
+ EXPECT_THAT(
+ test_api(&form_structure).fields(),
+ Each(Pointee(ResultOf(get_heuristic_type,
+ AllOf(Not(NO_SERVER_DATA), Not(UNKNOWN_TYPE))))));
+
+ for (PatternSource other_pattern_source : other_pattern_sources()) {
+ auto get_heuristic_type = [&](const AutofillField& field) {
+ return field.heuristic_type(other_pattern_source);
+ };
+ EXPECT_THAT(test_api(&form_structure).fields(),
+ Each(Pointee(ResultOf(get_heuristic_type, NO_SERVER_DATA))))
+ << "PatternSource = " << static_cast<int>(other_pattern_source);
+ }
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/geo/address_i18n.cc b/chromium/components/autofill/core/browser/geo/address_i18n.cc
index 3843023f34c..fb38157dcfb 100644
--- a/chromium/components/autofill/core/browser/geo/address_i18n.cc
+++ b/chromium/components/autofill/core/browser/geo/address_i18n.cc
@@ -67,28 +67,26 @@ CreateAddressDataFromAutofillProfile(const AutofillProfile& profile,
return address_data;
}
-ServerFieldType TypeForField(AddressField address_field, bool billing) {
+ServerFieldType TypeForField(AddressField address_field) {
switch (address_field) {
case ::i18n::addressinput::COUNTRY:
- return billing ? ADDRESS_BILLING_COUNTRY : ADDRESS_HOME_COUNTRY;
+ return ADDRESS_HOME_COUNTRY;
case ::i18n::addressinput::ADMIN_AREA:
- return billing ? ADDRESS_BILLING_STATE : ADDRESS_HOME_STATE;
+ return ADDRESS_HOME_STATE;
case ::i18n::addressinput::LOCALITY:
- return billing ? ADDRESS_BILLING_CITY : ADDRESS_HOME_CITY;
+ return ADDRESS_HOME_CITY;
case ::i18n::addressinput::DEPENDENT_LOCALITY:
- return billing ? ADDRESS_BILLING_DEPENDENT_LOCALITY
- : ADDRESS_HOME_DEPENDENT_LOCALITY;
+ return ADDRESS_HOME_DEPENDENT_LOCALITY;
case ::i18n::addressinput::POSTAL_CODE:
- return billing ? ADDRESS_BILLING_ZIP : ADDRESS_HOME_ZIP;
+ return ADDRESS_HOME_ZIP;
case ::i18n::addressinput::SORTING_CODE:
- return billing ? ADDRESS_BILLING_SORTING_CODE : ADDRESS_HOME_SORTING_CODE;
+ return ADDRESS_HOME_SORTING_CODE;
case ::i18n::addressinput::STREET_ADDRESS:
- return billing ? ADDRESS_BILLING_STREET_ADDRESS
- : ADDRESS_HOME_STREET_ADDRESS;
+ return ADDRESS_HOME_STREET_ADDRESS;
case ::i18n::addressinput::ORGANIZATION:
return COMPANY_NAME;
case ::i18n::addressinput::RECIPIENT:
- return billing ? NAME_BILLING_FULL : NAME_FULL;
+ return NAME_FULL;
}
NOTREACHED();
return UNKNOWN_TYPE;
diff --git a/chromium/components/autofill/core/browser/geo/address_i18n.h b/chromium/components/autofill/core/browser/geo/address_i18n.h
index 16b5cc8db89..be4b237dd0a 100644
--- a/chromium/components/autofill/core/browser/geo/address_i18n.h
+++ b/chromium/components/autofill/core/browser/geo/address_i18n.h
@@ -37,8 +37,7 @@ CreateAddressDataFromAutofillProfile(const AutofillProfile& profile,
const std::string& app_locale);
// Returns the corresponding Autofill server type for |field|.
-ServerFieldType TypeForField(::i18n::addressinput::AddressField field,
- bool billing);
+ServerFieldType TypeForField(::i18n::addressinput::AddressField field);
// Sets |field| to the corresponding address field for the Autofill
// |server_type|. Returns |true| if |server_type| can be represented as an
diff --git a/chromium/components/autofill/core/browser/geo/address_i18n_unittest.cc b/chromium/components/autofill/core/browser/geo/address_i18n_unittest.cc
index 59e7357450f..bd9f81602a7 100644
--- a/chromium/components/autofill/core/browser/geo/address_i18n_unittest.cc
+++ b/chromium/components/autofill/core/browser/geo/address_i18n_unittest.cc
@@ -35,7 +35,6 @@ using ::i18n::addressinput::SORTING_CODE;
using ::i18n::addressinput::STREET_ADDRESS;
struct FieldTypeMirrorConversionsTestCase {
- bool billing;
ServerFieldType server_field;
AddressField address_field;
};
@@ -49,8 +48,7 @@ TEST_P(FieldTypeMirrorConversionsTest, FieldTypeMirrorConversions) {
EXPECT_TRUE(FieldForType(test_data.server_field, &address_field));
EXPECT_EQ(test_data.address_field, address_field);
- ServerFieldType server_field =
- TypeForField(test_data.address_field, test_data.billing);
+ ServerFieldType server_field = TypeForField(test_data.address_field);
EXPECT_EQ(test_data.server_field, server_field);
}
@@ -58,37 +56,18 @@ INSTANTIATE_TEST_SUITE_P(
AddressI18nTest,
FieldTypeMirrorConversionsTest,
testing::Values(
- FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_COUNTRY,
- COUNTRY},
- FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_STATE,
- ADMIN_AREA},
- FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_CITY,
- LOCALITY},
- FieldTypeMirrorConversionsTestCase{
- true, ADDRESS_BILLING_DEPENDENT_LOCALITY, DEPENDENT_LOCALITY},
- FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_SORTING_CODE,
+ FieldTypeMirrorConversionsTestCase{ADDRESS_HOME_COUNTRY, COUNTRY},
+ FieldTypeMirrorConversionsTestCase{ADDRESS_HOME_STATE, ADMIN_AREA},
+ FieldTypeMirrorConversionsTestCase{ADDRESS_HOME_CITY, LOCALITY},
+ FieldTypeMirrorConversionsTestCase{ADDRESS_HOME_DEPENDENT_LOCALITY,
+ DEPENDENT_LOCALITY},
+ FieldTypeMirrorConversionsTestCase{ADDRESS_HOME_SORTING_CODE,
SORTING_CODE},
- FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_ZIP,
- POSTAL_CODE},
- FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_STREET_ADDRESS,
+ FieldTypeMirrorConversionsTestCase{ADDRESS_HOME_ZIP, POSTAL_CODE},
+ FieldTypeMirrorConversionsTestCase{ADDRESS_HOME_STREET_ADDRESS,
STREET_ADDRESS},
- FieldTypeMirrorConversionsTestCase{true, COMPANY_NAME, ORGANIZATION},
- FieldTypeMirrorConversionsTestCase{true, NAME_BILLING_FULL, RECIPIENT},
- FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_COUNTRY,
- COUNTRY},
- FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_STATE,
- ADMIN_AREA},
- FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_CITY, LOCALITY},
- FieldTypeMirrorConversionsTestCase{
- false, ADDRESS_HOME_DEPENDENT_LOCALITY, DEPENDENT_LOCALITY},
- FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_SORTING_CODE,
- SORTING_CODE},
- FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_ZIP,
- POSTAL_CODE},
- FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_STREET_ADDRESS,
- STREET_ADDRESS},
- FieldTypeMirrorConversionsTestCase{false, COMPANY_NAME, ORGANIZATION},
- FieldTypeMirrorConversionsTestCase{false, NAME_FULL, RECIPIENT}));
+ FieldTypeMirrorConversionsTestCase{COMPANY_NAME, ORGANIZATION},
+ FieldTypeMirrorConversionsTestCase{NAME_FULL, RECIPIENT}));
struct FieldTypeUnidirectionalConversionsTestCase {
ServerFieldType server_field;
diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc
index 9369fbde7c2..ad8a507b835 100644
--- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc
+++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc
@@ -125,7 +125,11 @@ void AlternativeStateNameMapUpdater::LoadStatesData(
PrefService* pref_service,
base::OnceClosure done_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(pref_service);
+
+ // Would be null in the case of tests.
+ if (!pref_service) {
+ return;
+ }
// Get the states data installation path from |pref_service| which is set by
// the component updater once it downloads the states data and should be safe
diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h
index 2286c8b9670..73f6d9c73f2 100644
--- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h
+++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h
@@ -87,6 +87,11 @@ class AlternativeStateNameMapUpdater : public PersonalDataManagerObserver {
return ContainsState(stripped_alternative_state_names,
stripped_state_value_from_profile);
}
+
+ // Setter for |local_state_| used for testing purposes.
+ void set_local_state_for_testing(PrefService* pref_service) {
+ local_state_ = pref_service;
+ }
#endif // defined(UNIT_TEST)
private:
@@ -133,7 +138,7 @@ class AlternativeStateNameMapUpdater : public PersonalDataManagerObserver {
const raw_ptr<PersonalDataManager> personal_data_manager_ = nullptr;
// The browser local_state that stores the states data installation path.
- const raw_ptr<PrefService> local_state_ = nullptr;
+ raw_ptr<PrefService> local_state_ = nullptr;
// In case of concurrent requests to load states data, the callbacks are
// queued in |pending_init_done_callbacks_| and triggered once the
diff --git a/chromium/components/autofill/core/browser/geo/autofill_country.cc b/chromium/components/autofill/core/browser/geo/autofill_country.cc
index 4a95ddd481a..5f0cdae2611 100644
--- a/chromium/components/autofill/core/browser/geo/autofill_country.cc
+++ b/chromium/components/autofill/core/browser/geo/autofill_country.cc
@@ -5,16 +5,23 @@
#include "components/autofill/core/browser/geo/autofill_country.h"
#include <stddef.h>
+#include <array>
#include "base/containers/contains.h"
+#include "base/containers/fixed_flat_map.h"
#include "base/strings/string_util.h"
#include "components/autofill/core/browser/geo/country_data.h"
#include "components/autofill/core/browser/geo/country_names.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_internals/log_message.h"
#include "components/autofill/core/common/logging/log_buffer.h"
#include "third_party/icu/source/common/unicode/locid.h"
+#include "third_party/libaddressinput/messages.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_metadata.h"
#include "ui/base/l10n/l10n_util.h"
+using ::i18n::addressinput::AddressField;
+
namespace autofill {
namespace {
@@ -88,4 +95,50 @@ LogBuffer& operator<<(LogBuffer& buffer, const AutofillCountry& country) {
buffer << CTag{};
return buffer;
}
+
+base::span<const AutofillCountry::AddressFormatExtension>
+AutofillCountry::address_format_extensions() const {
+ if (!base::FeatureList::IsEnabled(
+ features::kAutofillEnableExtendedAddressFormats)) {
+ return {};
+ }
+
+ // TODO(crbug.com/1300548): Extend more countries. FR and GB are used to test
+ // the feature, because libaddressinput already provides string literals.
+ static constexpr std::array<AddressFormatExtension, 1> fr_extensions{
+ {{.type = AddressField::ADMIN_AREA,
+ .label_id = IDS_LIBADDRESSINPUT_PROVINCE,
+ .placed_after = AddressField::LOCALITY,
+ .separator_before_label = "\n",
+ .large_sized = true}}};
+ static constexpr std::array<AddressFormatExtension, 1> gb_extensions{
+ {{.type = AddressField::ADMIN_AREA,
+ .label_id = IDS_LIBADDRESSINPUT_COUNTY,
+ .placed_after = AddressField::POSTAL_CODE,
+ .separator_before_label = "\n",
+ .large_sized = true}}};
+
+ static constexpr auto extensions =
+ base::MakeFixedFlatMap<base::StringPiece,
+ base::span<const AddressFormatExtension>>({
+ {"FR", fr_extensions},
+ {"GB", gb_extensions},
+ });
+
+ auto* it = extensions.find(country_code_);
+ if (it != extensions.end())
+ return it->second;
+ return {};
+}
+
+bool AutofillCountry::IsAddressFieldSettingAccessible(
+ AddressField address_field) const {
+ // Check if `address_field` is part of libaddressinputs native address format
+ // or part of the Autofill's address extensions.
+ return ::i18n::addressinput::IsFieldUsed(address_field, country_code_) ||
+ base::Contains(
+ address_format_extensions(), address_field,
+ [](const AddressFormatExtension& rule) { return rule.type; });
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/geo/autofill_country.h b/chromium/components/autofill/core/browser/geo/autofill_country.h
index 3ac5a3c0d0f..3a46edff14c 100644
--- a/chromium/components/autofill/core/browser/geo/autofill_country.h
+++ b/chromium/components/autofill/core/browser/geo/autofill_country.h
@@ -7,7 +7,10 @@
#include <string>
+#include "base/containers/span.h"
+#include "base/strings/string_piece.h"
#include "components/autofill/core/browser/geo/country_data.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_field.h"
namespace autofill {
@@ -18,14 +21,44 @@ class LogBuffer;
class AutofillCountry {
public:
// Returns country data corresponding to the two-letter ISO code
- // |country_code|.
- AutofillCountry(const std::string& country_code, const std::string& locale);
+ // `country_code`.
+ // `locale` is used translate the `name()` appropriately and can be ignored
+ // if the name is not queried.
+ explicit AutofillCountry(const std::string& country_code,
+ const std::string& locale = "en");
AutofillCountry(const AutofillCountry&) = delete;
AutofillCountry& operator=(const AutofillCountry&) = delete;
~AutofillCountry();
+ // Autofill relies on libaddressinput for its address format.
+ // AddressFormatExtensions are used to extend this format on a country-by-
+ // country basis. This is needed because while some field types are not
+ // strictly required for a valid address, we nonetheless see them in practise
+ // and want to offer filling support.
+ // This struct defines that a certain `type` is considered part of the address
+ // format in Autofill, specifies its `label` and placment after the existing
+ // type `placed_after` in the settings-UI.
+ // `large_sized` indicates if the field stretches the entire line (true) or
+ // half the line (false).
+ struct AddressFormatExtension {
+ ::i18n::addressinput::AddressField type;
+ int label_id;
+ ::i18n::addressinput::AddressField placed_after;
+ // Usually " " or "\n". Should not be empty.
+ base::StringPiece separator_before_label;
+ bool large_sized;
+ };
+
+ // Gets all the `AddressFormatExtension`s available for `country_code()`.
+ base::span<const AddressFormatExtension> address_format_extensions() const;
+
+ // Returns true if the given `address_field` is part of Autofill's address
+ // format for `country_code()`.
+ bool IsAddressFieldSettingAccessible(
+ ::i18n::addressinput::AddressField address_field) const;
+
// Returns the likely country code for |locale|, or "US" as a fallback if no
// mapping from the locale is available.
static const std::string CountryCodeForLocale(const std::string& locale);
diff --git a/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc b/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc
index 93c068ffc14..b9ce7000597 100644
--- a/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc
+++ b/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc
@@ -7,9 +7,12 @@
#include "base/containers/contains.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/geo/autofill_country.h"
#include "components/autofill/core/browser/geo/country_data.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_metadata.h"
#if defined(ANDROID)
#include "base/android/build_info.h"
#endif
@@ -153,10 +156,37 @@ TEST(AutofillCountryTest, AliasMappingsForCountryData) {
country_data_map->GetCountryCodeForAlias("does_not_exist");
EXPECT_EQ(expected_country_code, actual_country_code);
- // GB should map the UK.
+ // UK should map the GB.
expected_country_code = "GB";
actual_country_code = country_data_map->GetCountryCodeForAlias("UK");
EXPECT_EQ(expected_country_code, actual_country_code);
}
+// Verifies that all address format extensions correspond to types that are
+// not part of libaddressinputs expected types, but that they are placed
+// after a field that is present in libaddressinput.
+TEST(AutofillCountryTest, VerifyAddressFormatExtensions) {
+ base::test::ScopedFeatureList address_extension_feature;
+ address_extension_feature.InitAndEnableFeature(
+ features::kAutofillEnableExtendedAddressFormats);
+
+ CountryDataMap* country_data_map = CountryDataMap::GetInstance();
+ for (const std::string& country_code : country_data_map->country_codes()) {
+ AutofillCountry country(country_code);
+ for (const AutofillCountry::AddressFormatExtension& rule :
+ country.address_format_extensions()) {
+ // The separator should not be empty.
+ EXPECT_FALSE(rule.separator_before_label.empty());
+ // `rule.type` is not part of `country_code`'s address format, but
+ // `rule.placed_after` is.
+ EXPECT_FALSE(::i18n::addressinput::IsFieldUsed(rule.type, country_code));
+ EXPECT_TRUE(
+ ::i18n::addressinput::IsFieldUsed(rule.placed_after, country_code));
+ // `IsAddressFieldSettingAccessible` considers `rule.type`
+ // setting-accessible.
+ EXPECT_TRUE(country.IsAddressFieldSettingAccessible(rule.type));
+ }
+ }
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/geo/country_names_for_locale.cc b/chromium/components/autofill/core/browser/geo/country_names_for_locale.cc
index 7a9ad244fd6..b65e0588b39 100644
--- a/chromium/components/autofill/core/browser/geo/country_names_for_locale.cc
+++ b/chromium/components/autofill/core/browser/geo/country_names_for_locale.cc
@@ -46,8 +46,7 @@ const std::string GetSortKey(const icu::Collator& collator,
// Creates collator for |locale| and sets its attributes as needed.
std::unique_ptr<icu::Collator> CreateCollator(const icu::Locale& locale) {
- std::unique_ptr<icu::Collator> collator(
- autofill::l10n::GetCollatorForLocale(locale));
+ std::unique_ptr<icu::Collator> collator(l10n::GetCollatorForLocale(locale));
if (!collator)
return nullptr;
diff --git a/chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.cc b/chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.cc
index c7aa1220f99..32ad37f3933 100644
--- a/chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.cc
+++ b/chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.cc
@@ -15,13 +15,13 @@ MockAlternativeStateNameMapUpdater::~MockAlternativeStateNameMapUpdater() =
MockAlternativeStateNameMapUpdater::MockAlternativeStateNameMapUpdater(
base::OnceClosure callback,
PrefService* local_state,
- autofill::PersonalDataManager* personal_data_manager)
+ PersonalDataManager* personal_data_manager)
: AlternativeStateNameMapUpdater(local_state, personal_data_manager),
callback_(std::move(callback)) {}
void MockAlternativeStateNameMapUpdater::OnPersonalDataFinishedProfileTasks() {
if (base::FeatureList::IsEnabled(
- autofill::features::kAutofillUseAlternativeStateNameMap)) {
+ features::kAutofillUseAlternativeStateNameMap)) {
PopulateAlternativeStateNameMap(std::move(callback_));
}
}
diff --git a/chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.h b/chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.h
index 15fa29ef90a..43b75ad4b52 100644
--- a/chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.h
+++ b/chromium/components/autofill/core/browser/geo/mock_alternative_state_name_map_updater.h
@@ -11,13 +11,13 @@ namespace autofill {
// MockAlternativeStateNameMapUpdater for testing AlternativeStateNameMap
class MockAlternativeStateNameMapUpdater
- : public autofill::AlternativeStateNameMapUpdater {
+ : public AlternativeStateNameMapUpdater {
public:
~MockAlternativeStateNameMapUpdater() override;
MockAlternativeStateNameMapUpdater(
base::OnceClosure callback,
PrefService* local_state,
- autofill::PersonalDataManager* personal_data_manager);
+ PersonalDataManager* personal_data_manager);
// PersonalDataManagerObserver
void OnPersonalDataFinishedProfileTasks() override;
diff --git a/chromium/components/autofill/core/browser/geo/phone_number_i18n.cc b/chromium/components/autofill/core/browser/geo/phone_number_i18n.cc
index d936d62e091..8368f0d69ff 100644
--- a/chromium/components/autofill/core/browser/geo/phone_number_i18n.cc
+++ b/chromium/components/autofill/core/browser/geo/phone_number_i18n.cc
@@ -323,7 +323,7 @@ std::u16string GetFormattedPhoneNumberForDisplay(const AutofillProfile& profile,
// Always favor the tentative international phone number if it's determined as
// being a valid number.
const std::string country_code =
- autofill::data_util::GetCountryCodeWithFallback(profile, locale);
+ data_util::GetCountryCodeWithFallback(profile, locale);
if (IsValidPhoneNumber(tentative_intl_phone, country_code)) {
return base::UTF8ToUTF16(
FormatPhoneNumber(tentative_intl_phone, country_code,
@@ -367,13 +367,9 @@ std::string FormatPhoneForResponse(const std::string& phone_number,
}
PhoneObject::PhoneObject(const std::u16string& number,
- const std::string& region) {
+ const std::string& region,
+ bool infer_country_code) {
DCHECK_EQ(2u, region.size());
- // TODO(isherman): Autofill profiles should always have a |region| set, but in
- // some cases it should be marked as implicit. Otherwise, phone numbers
- // might behave differently when they are synced across computers:
- // [ http://crbug.com/100845 ]. Once the bug is fixed, add a DCHECK here to
- // verify.
std::unique_ptr<::i18n::phonenumbers::PhoneNumber> i18n_number(
new ::i18n::phonenumbers::PhoneNumber);
@@ -383,6 +379,13 @@ PhoneObject::PhoneObject(const std::u16string& number,
// The formatted and normalized versions will be set on the first call to
// the coresponding methods.
i18n_number_ = std::move(i18n_number);
+ // `ParsePhoneNumber()` only sets `country_code_` for internationally
+ // formatted numbers. `i18n_number_`'s country_code defaults to `region` in,
+ // this case. If `infer_country_code` is true, fall back to that.
+ if (infer_country_code && country_code_.empty() &&
+ i18n_number_->has_country_code()) {
+ country_code_ = base::NumberToString16(i18n_number_->country_code());
+ }
} else {
// Parsing failed. Store passed phone "as is" into |whole_number_|.
whole_number_ = number;
diff --git a/chromium/components/autofill/core/browser/geo/phone_number_i18n.h b/chromium/components/autofill/core/browser/geo/phone_number_i18n.h
index 678c61539f8..bb9b996daa9 100644
--- a/chromium/components/autofill/core/browser/geo/phone_number_i18n.h
+++ b/chromium/components/autofill/core/browser/geo/phone_number_i18n.h
@@ -117,7 +117,9 @@ std::string FormatPhoneForResponse(const std::string& phone_number,
// The cached phone number, does parsing only once, improves performance.
class PhoneObject {
public:
- PhoneObject(const std::u16string& number, const std::string& default_region);
+ PhoneObject(const std::u16string& number,
+ const std::string& default_region,
+ bool infer_country_code);
PhoneObject(const PhoneObject&);
PhoneObject();
~PhoneObject();
diff --git a/chromium/components/autofill/core/browser/geo/region_data_loader_impl.h b/chromium/components/autofill/core/browser/geo/region_data_loader_impl.h
index 5f04985c0d8..b7b5e309a62 100644
--- a/chromium/components/autofill/core/browser/geo/region_data_loader_impl.h
+++ b/chromium/components/autofill/core/browser/geo/region_data_loader_impl.h
@@ -37,7 +37,7 @@ class RegionDataLoaderImpl : public RegionDataLoader {
~RegionDataLoaderImpl() override;
- // autofill::RegionDataLoader.
+ // RegionDataLoader.
void LoadRegionData(const std::string& country_code,
RegionDataLoader::RegionDataLoaded callback) override;
void ClearCallback() override;
diff --git a/chromium/components/autofill/core/browser/geo/test_region_data_loader.cc b/chromium/components/autofill/core/browser/geo/test_region_data_loader.cc
index 2b2e43b9d69..3a020a14d3f 100644
--- a/chromium/components/autofill/core/browser/geo/test_region_data_loader.cc
+++ b/chromium/components/autofill/core/browser/geo/test_region_data_loader.cc
@@ -14,7 +14,7 @@ TestRegionDataLoader::~TestRegionDataLoader() = default;
void TestRegionDataLoader::LoadRegionData(
const std::string& country_code,
- autofill::RegionDataLoader::RegionDataLoaded callback) {
+ RegionDataLoader::RegionDataLoaded callback) {
if (synchronous_callback_) {
SendRegionData(regions_, callback);
} else {
@@ -42,7 +42,7 @@ void TestRegionDataLoader::SendAsynchronousData(
void TestRegionDataLoader::SendRegionData(
const std::vector<std::pair<std::string, std::string>>& regions,
- autofill::RegionDataLoader::RegionDataLoaded callback) {
+ RegionDataLoader::RegionDataLoaded callback) {
::i18n::addressinput::RegionData root_region("");
for (const auto& [key, value] : regions)
root_region.AddSubRegion(key, value);
diff --git a/chromium/components/autofill/core/browser/geo/test_region_data_loader.h b/chromium/components/autofill/core/browser/geo/test_region_data_loader.h
index 52edd93167c..0dad6c507d7 100644
--- a/chromium/components/autofill/core/browser/geo/test_region_data_loader.h
+++ b/chromium/components/autofill/core/browser/geo/test_region_data_loader.h
@@ -20,15 +20,12 @@ class TestRegionDataLoader : public RegionDataLoader {
~TestRegionDataLoader() override;
// RegionDataLoader.
- void LoadRegionData(
- const std::string& country_code,
- autofill::RegionDataLoader::RegionDataLoaded callback) override;
+ void LoadRegionData(const std::string& country_code,
+ RegionDataLoader::RegionDataLoaded callback) override;
void ClearCallback() override;
std::string country_code() { return country_code_; }
- const autofill::RegionDataLoader::RegionDataLoaded& callback() {
- return callback_;
- }
+ const RegionDataLoader::RegionDataLoaded& callback() { return callback_; }
void set_synchronous_callback(bool synchronous_callback) {
synchronous_callback_ = synchronous_callback;
}
@@ -44,11 +41,11 @@ class TestRegionDataLoader : public RegionDataLoader {
private:
void SendRegionData(
const std::vector<std::pair<std::string, std::string>>& regions,
- autofill::RegionDataLoader::RegionDataLoaded callback);
+ RegionDataLoader::RegionDataLoaded callback);
std::vector<std::pair<std::string, std::string>> regions_;
std::string country_code_;
- autofill::RegionDataLoader::RegionDataLoaded callback_;
+ RegionDataLoader::RegionDataLoaded callback_;
bool synchronous_callback_{false};
};
diff --git a/chromium/components/autofill/core/browser/logging/log_router.cc b/chromium/components/autofill/core/browser/logging/log_router.cc
index 28922c42136..52ebb49d8ce 100644
--- a/chromium/components/autofill/core/browser/logging/log_router.cc
+++ b/chromium/components/autofill/core/browser/logging/log_router.cc
@@ -5,11 +5,11 @@
#include "components/autofill/core/browser/logging/log_router.h"
#include "base/observer_list.h"
+#include "base/strings/escape.h"
#include "base/strings/string_split.h"
#include "components/autofill/core/browser/logging/log_manager.h"
#include "components/autofill/core/browser/logging/log_receiver.h"
#include "components/autofill/core/common/logging/log_buffer.h"
-#include "net/base/escape.h"
namespace autofill {
diff --git a/chromium/components/autofill/core/browser/merchant_promo_code_manager.cc b/chromium/components/autofill/core/browser/merchant_promo_code_manager.cc
new file mode 100644
index 00000000000..0c78f120478
--- /dev/null
+++ b/chromium/components/autofill/core/browser/merchant_promo_code_manager.cc
@@ -0,0 +1,171 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/merchant_promo_code_manager.h"
+
+#include "components/autofill/core/browser/autofill_suggestion_generator.h"
+#include "components/autofill/core/browser/browser_autofill_manager.h"
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
+#include "components/autofill/core/browser/metrics/payments/offers_metrics.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/suggestions_context.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
+
+namespace autofill {
+
+MerchantPromoCodeManager::MerchantPromoCodeManager() = default;
+
+MerchantPromoCodeManager::~MerchantPromoCodeManager() = default;
+
+void MerchantPromoCodeManager::OnGetSingleFieldSuggestions(
+ int query_id,
+ bool is_autocomplete_enabled,
+ bool autoselect_first_suggestion,
+ const std::u16string& name,
+ const std::u16string& prefix,
+ const std::string& form_control_type,
+ base::WeakPtr<SuggestionsHandler> handler,
+ const SuggestionsContext& context) {
+ // If merchant promo code offers are available for the given site, and the
+ // profile is not OTR, show the promo code offers.
+ if (!is_off_the_record_ && personal_data_manager_) {
+ std::vector<const AutofillOfferData*> promo_code_offers =
+ personal_data_manager_->GetActiveAutofillPromoCodeOffersForOrigin(
+ context.form_structure->main_frame_origin().GetURL());
+ if (!promo_code_offers.empty()) {
+ SendPromoCodeSuggestions(
+ promo_code_offers,
+ QueryHandler(query_id, autoselect_first_suggestion, prefix, handler));
+ uma_recorder_.OnOffersSuggestionsShown(name, promo_code_offers);
+ return;
+ }
+ }
+}
+
+void MerchantPromoCodeManager::OnWillSubmitFormWithFields(
+ const std::vector<FormFieldData>& fields,
+ bool is_autocomplete_enabled) {}
+
+void MerchantPromoCodeManager::CancelPendingQueries(
+ const SuggestionsHandler* handler) {}
+
+void MerchantPromoCodeManager::OnRemoveCurrentSingleFieldSuggestion(
+ const std::u16string& field_name,
+ const std::u16string& value,
+ int frontend_id) {}
+
+void MerchantPromoCodeManager::OnSingleFieldSuggestionSelected(
+ const std::u16string& value,
+ int frontend_id) {
+ uma_recorder_.OnOfferSuggestionSelected(frontend_id);
+}
+
+void MerchantPromoCodeManager::Init(PersonalDataManager* personal_data_manager,
+ bool is_off_the_record) {
+ personal_data_manager_ = personal_data_manager;
+ is_off_the_record_ = is_off_the_record;
+}
+
+base::WeakPtr<MerchantPromoCodeManager> MerchantPromoCodeManager::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
+void MerchantPromoCodeManager::UMARecorder::OnOffersSuggestionsShown(
+ const std::u16string& name,
+ std::vector<const AutofillOfferData*>& offers) {
+ // Log metrics related to the showing of overall offers suggestions popup.
+ autofill_metrics::LogOffersSuggestionsPopupShown(
+ /*first_time_being_logged=*/most_recent_suggestions_shown_field_name_ !=
+ name);
+
+ // Log metrics related to the showing of individual offers in the offers
+ // suggestions popup.
+ for (const AutofillOfferData* offer : offers) {
+ // We log every time an individual offer suggestion is shown, regardless if
+ // the user is repeatedly clicking the same field.
+ autofill_metrics::LogIndividualOfferSuggestionEvent(
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown,
+ offer->GetOfferType());
+
+ // We log that this individual offer suggestion was shown once for this
+ // field while autofilling if it is the first time being logged.
+ if (most_recent_suggestions_shown_field_name_ != name) {
+ autofill_metrics::LogIndividualOfferSuggestionEvent(
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce,
+ offer->GetOfferType());
+ }
+ }
+
+ most_recent_suggestions_shown_field_name_ = name;
+}
+
+void MerchantPromoCodeManager::UMARecorder::OnOfferSuggestionSelected(
+ int frontend_id) {
+ if (frontend_id == PopupItemId::POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY) {
+ // We log every time an individual offer suggestion is selected, regardless
+ // if the user is repeatedly autofilling the same field.
+ autofill_metrics::LogIndividualOfferSuggestionEvent(
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelected,
+ AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER);
+
+ // We log that this individual offer suggestion was selected once for this
+ // field while autofilling if it is the first time being logged.
+ if (most_recent_suggestion_selected_field_name_ !=
+ most_recent_suggestions_shown_field_name_) {
+ autofill_metrics::LogIndividualOfferSuggestionEvent(
+ autofill_metrics::OffersSuggestionsEvent::
+ kOfferSuggestionSelectedOnce,
+ AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER);
+ }
+ } else if (frontend_id == PopupItemId::POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS) {
+ // We log every time the see offer details suggestion in the footer is
+ // selected, regardless if the user is repeatedly autofilling the same
+ // field.
+ autofill_metrics::LogIndividualOfferSuggestionEvent(
+ autofill_metrics::OffersSuggestionsEvent::
+ kOfferSuggestionSeeOfferDetailsSelected,
+ AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER);
+
+ // We log that this individual see offer details suggestion in the footer
+ // was selected once for this field while autofilling if it is the first
+ // time being logged.
+ if (most_recent_suggestion_selected_field_name_ !=
+ most_recent_suggestions_shown_field_name_) {
+ autofill_metrics::LogIndividualOfferSuggestionEvent(
+ autofill_metrics::OffersSuggestionsEvent::
+ kOfferSuggestionSeeOfferDetailsSelectedOnce,
+ AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER);
+ }
+ }
+
+ most_recent_suggestion_selected_field_name_ =
+ most_recent_suggestions_shown_field_name_;
+}
+
+void MerchantPromoCodeManager::SendPromoCodeSuggestions(
+ const std::vector<const AutofillOfferData*>& promo_code_offers,
+ const QueryHandler& query_handler) {
+ if (!query_handler.handler_) {
+ // Either the handler has been destroyed, or it is invalid.
+ return;
+ }
+
+ // If the input box content equals any of the available promo codes, then
+ // assume the promo code has been filled, and don't show any suggestions.
+ for (const AutofillOfferData* promo_code_offer : promo_code_offers) {
+ if (query_handler.prefix_ ==
+ base::ASCIIToUTF16(promo_code_offer->GetPromoCode())) {
+ return;
+ }
+ }
+
+ // Return suggestions to query handler.
+ query_handler.handler_->OnSuggestionsReturned(
+ query_handler.client_query_id_,
+ query_handler.autoselect_first_suggestion_,
+ AutofillSuggestionGenerator::GetPromoCodeSuggestionsFromPromoCodeOffers(
+ promo_code_offers));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/merchant_promo_code_manager.h b/chromium/components/autofill/core/browser/merchant_promo_code_manager.h
new file mode 100644
index 00000000000..d461ae9cae9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/merchant_promo_code_manager.h
@@ -0,0 +1,111 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_MERCHANT_PROMO_CODE_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_MERCHANT_PROMO_CODE_MANAGER_H_
+
+#include "components/autofill/core/browser/autofill_subject.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/single_field_form_filler.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+
+namespace autofill {
+
+class AutofillOfferData;
+class PersonalDataManager;
+struct SuggestionsContext;
+
+// Per-profile Merchant Promo Code Manager. This class handles promo code
+// related functionality such as retrieving promo code offer data, managing
+// promo code suggestions, filling promo code fields, and handling form
+// submission data when there is a merchant promo code field present.
+class MerchantPromoCodeManager : public SingleFieldFormFiller,
+ public KeyedService,
+ public AutofillSubject {
+ public:
+ MerchantPromoCodeManager();
+
+ MerchantPromoCodeManager(const MerchantPromoCodeManager&) = delete;
+ MerchantPromoCodeManager& operator=(const MerchantPromoCodeManager&) = delete;
+
+ ~MerchantPromoCodeManager() override;
+
+ // SingleFieldFormFiller overrides:
+ void OnGetSingleFieldSuggestions(int query_id,
+ bool is_autocomplete_enabled,
+ bool autoselect_first_suggestion,
+ const std::u16string& name,
+ const std::u16string& prefix,
+ const std::string& form_control_type,
+ base::WeakPtr<SuggestionsHandler> handler,
+ const SuggestionsContext& context) override;
+ void OnWillSubmitFormWithFields(const std::vector<FormFieldData>& fields,
+ bool is_autocomplete_enabled) override;
+ void CancelPendingQueries(const SuggestionsHandler* handler) override;
+ void OnRemoveCurrentSingleFieldSuggestion(const std::u16string& field_name,
+ const std::u16string& value,
+ int frontend_id) override;
+ void OnSingleFieldSuggestionSelected(const std::u16string& value,
+ int frontend_id) override;
+
+ // Initializes the instance with the given parameters. |personal_data_manager|
+ // is a profile-scope data manager used to retrieve promo code offers from the
+ // local autofill table. |is_off_the_record| indicates whether the user is
+ // currently operating in an off-the-record context (i.e. incognito).
+ void Init(PersonalDataManager* personal_data_manager, bool is_off_the_record);
+
+ // Returns a weak pointer to the current MerchantPromoCodeManager
+ // instance.
+ base::WeakPtr<MerchantPromoCodeManager> GetWeakPtr();
+
+ private:
+ friend class MerchantPromoCodeManagerTest;
+ FRIEND_TEST_ALL_PREFIXES(MerchantPromoCodeManagerTest,
+ DoesNotShowPromoCodeOffersForOffTheRecord);
+ FRIEND_TEST_ALL_PREFIXES(
+ MerchantPromoCodeManagerTest,
+ DoesNotShowPromoCodeOffersIfPersonalDataManagerDoesNotExist);
+
+ // Records metrics related to the offers suggestions popup.
+ class UMARecorder {
+ public:
+ UMARecorder() = default;
+
+ UMARecorder(const UMARecorder&) = delete;
+ UMARecorder& operator=(const UMARecorder&) = delete;
+
+ ~UMARecorder() = default;
+
+ void OnOffersSuggestionsShown(
+ const std::u16string& name,
+ std::vector<const AutofillOfferData*>& offers);
+ void OnOfferSuggestionSelected(int frontend_id);
+
+ private:
+ // The name of the field that most recently had suggestions shown.
+ std::u16string most_recent_suggestions_shown_field_name_;
+
+ // The name of the field that most recently had a suggestion selected.
+ std::u16string most_recent_suggestion_selected_field_name_;
+ };
+
+ // Sends suggestions for |promo_code_offers| to the |query_handler|'s handler
+ // for display in the associated Autofill popup.
+ void SendPromoCodeSuggestions(
+ const std::vector<const AutofillOfferData*>& promo_code_offers,
+ const QueryHandler& query_handler);
+
+ raw_ptr<PersonalDataManager> personal_data_manager_ = nullptr;
+
+ bool is_off_the_record_ = false;
+
+ UMARecorder uma_recorder_;
+
+ base::WeakPtrFactory<MerchantPromoCodeManager> weak_ptr_factory_{this};
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_MERCHANT_PROMO_CODE_MANAGER_H_
diff --git a/chromium/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc b/chromium/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
new file mode 100644
index 00000000000..b6543802b03
--- /dev/null
+++ b/chromium/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
@@ -0,0 +1,436 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/merchant_promo_code_manager.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/metrics/payments/offers_metrics.h"
+#include "components/autofill/core/browser/suggestions_context.h"
+#include "components/autofill/core/browser/test_form_structure.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/autofill/core/common/autofill_payments_features.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/strings/grit/components_strings.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using testing::Field;
+using testing::UnorderedElementsAre;
+
+namespace autofill {
+
+namespace {
+
+class MockSuggestionsHandler
+ : public MerchantPromoCodeManager::SuggestionsHandler {
+ public:
+ MockSuggestionsHandler() = default;
+ MockSuggestionsHandler(const MockSuggestionsHandler&) = delete;
+ MockSuggestionsHandler& operator=(const MockSuggestionsHandler&) = delete;
+ ~MockSuggestionsHandler() override = default;
+
+ MOCK_METHOD(void,
+ OnSuggestionsReturned,
+ (int query_id,
+ bool autoselect_first_suggestion,
+ const std::vector<Suggestion>& suggestions),
+ (override));
+
+ base::WeakPtr<MockSuggestionsHandler> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ private:
+ base::WeakPtrFactory<MockSuggestionsHandler> weak_ptr_factory_{this};
+};
+} // namespace
+
+class MerchantPromoCodeManagerTest : public testing::Test {
+ protected:
+ MerchantPromoCodeManagerTest() {
+ personal_data_manager_ = std::make_unique<TestPersonalDataManager>();
+ merchant_promo_code_manager_ = std::make_unique<MerchantPromoCodeManager>();
+ merchant_promo_code_manager_->Init(personal_data_manager_.get(),
+ /*is_off_the_record=*/false);
+ }
+
+ // Sets up the TestPersonalDataManager with a promo code offer for the given
+ // |origin|, and sets the offer details url of the offer to
+ // |offer_details_url|. Returns the promo code inserted in case the test wants
+ // to match it against returned suggestions.
+ std::string SetUpPromoCodeOffer(std::string origin,
+ const GURL& offer_details_url) {
+ personal_data_manager_.get()->SetAutofillWalletImportEnabled(true);
+ AutofillOfferData testPromoCodeOfferData =
+ test::GetPromoCodeOfferData(GURL(origin));
+ testPromoCodeOfferData.SetOfferDetailsUrl(offer_details_url);
+ personal_data_manager_.get()->AddOfferDataForTest(
+ std::make_unique<AutofillOfferData>(testPromoCodeOfferData));
+ return testPromoCodeOfferData.GetPromoCode();
+ }
+
+ std::unique_ptr<MerchantPromoCodeManager> merchant_promo_code_manager_;
+ std::unique_ptr<TestPersonalDataManager> personal_data_manager_;
+};
+
+TEST_F(MerchantPromoCodeManagerTest, ShowsPromoCodeSuggestions) {
+ base::HistogramTester histogram_tester;
+ auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+ int test_query_id = 2;
+ std::u16string test_name = u"Some Field Name";
+ std::u16string test_prefix = u"SomePrefix";
+ bool autoselect_first_suggestion = false;
+ bool is_autocomplete_enabled = true;
+ std::string form_control_type = "Some Type";
+ std::string last_committed_origin_url = "https://www.example.com";
+ FormData form_data;
+ form_data.main_frame_origin =
+ url::Origin::Create(GURL(last_committed_origin_url));
+ TestFormStructure form_structure{form_data};
+ SuggestionsContext context;
+ context.form_structure = &form_structure;
+ std::string promo_code = SetUpPromoCodeOffer(
+ last_committed_origin_url, GURL("https://offer-details-url.com/"));
+ Suggestion promo_code_suggestion = Suggestion(base::ASCIIToUTF16(promo_code));
+ Suggestion footer_suggestion = Suggestion(l10n_util::GetStringUTF16(
+ IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT));
+
+ // Setting up mock to verify that the handler is returned a list of
+ // promo-code-based suggestions and the promo code details line.
+ EXPECT_CALL(
+ *suggestions_handler.get(),
+ OnSuggestionsReturned(
+ test_query_id, autoselect_first_suggestion,
+ UnorderedElementsAre(
+ Field(&Suggestion::main_text, promo_code_suggestion.main_text),
+ Field(&Suggestion::main_text, footer_suggestion.main_text))))
+ .Times(3);
+
+ // Simulate request for suggestions.
+ // Because all criteria are met, active promo code suggestions for the given
+ // merchant site will be displayed instead of requesting Autocomplete
+ // suggestions.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+ test_name, test_prefix, form_control_type,
+ suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ // Trigger offers suggestions popup again to be able to test that we do not
+ // log metrics twice for the same field.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+ test_name, test_prefix, form_control_type,
+ suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ // Trigger offers suggestions popup again to be able to test that we log
+ // metrics more than once if it is a different field.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+ u"other_name", test_prefix, form_control_type,
+ suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShownOnce,
+ 2);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShown,
+ 3);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 2);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 3);
+}
+
+TEST_F(MerchantPromoCodeManagerTest,
+ DoesNotShowPromoCodeOffersForOffTheRecord) {
+ base::HistogramTester histogram_tester;
+ auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+ std::string last_committed_origin_url = "https://www.example.com";
+ std::string promo_code = SetUpPromoCodeOffer(
+ last_committed_origin_url, GURL("https://offer-details-url.com/"));
+ FormData form_data;
+ form_data.main_frame_origin =
+ url::Origin::Create(GURL(last_committed_origin_url));
+ TestFormStructure form_structure{form_data};
+ SuggestionsContext context;
+ context.form_structure = &form_structure;
+ merchant_promo_code_manager_->is_off_the_record_ = true;
+
+ // Setting up mock to verify that suggestions returning is not triggered if
+ // the user is off the record.
+ EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0);
+
+ // Simulate request for suggestions.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ /*query_id=*/2, /*is_autocomplete_enabled=*/true,
+ /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name",
+ /*prefix=*/u"SomePrefix", "Some Type", suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ // Ensure that no metrics were logged.
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShownOnce,
+ 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShown,
+ 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0);
+}
+
+TEST_F(MerchantPromoCodeManagerTest,
+ DoesNotShowPromoCodeOffersIfPersonalDataManagerDoesNotExist) {
+ base::HistogramTester histogram_tester;
+ auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+ std::string last_committed_origin_url = "https://www.example.com";
+ FormData form_data;
+ form_data.main_frame_origin =
+ url::Origin::Create(GURL(last_committed_origin_url));
+ TestFormStructure form_structure{form_data};
+ SuggestionsContext context;
+ context.form_structure = &form_structure;
+ merchant_promo_code_manager_->personal_data_manager_ = nullptr;
+
+ // Setting up mock to verify that suggestions returning is not triggered if
+ // personal data manager does not exist.
+ EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0);
+
+ // Simulate request for suggestions.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ /*query_id=*/2, /*is_autocomplete_enabled=*/true,
+ /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name",
+ /*prefix=*/u"SomePrefix", "Some Type", suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ // Ensure that no metrics were logged.
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShownOnce,
+ 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShown,
+ 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0);
+}
+
+TEST_F(MerchantPromoCodeManagerTest, NoPromoCodeOffers) {
+ base::HistogramTester histogram_tester;
+ auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+ std::string last_committed_origin_url = "https://www.example.com";
+ personal_data_manager_.get()->SetAutofillWalletImportEnabled(true);
+ FormData form_data;
+ form_data.main_frame_origin =
+ url::Origin::Create(GURL(last_committed_origin_url));
+ TestFormStructure form_structure{form_data};
+ SuggestionsContext context;
+ context.form_structure = &form_structure;
+
+ // Setting up mock to verify that suggestions returning is not triggered if
+ // there are no promo code offers to suggest.
+ EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0);
+
+ // Simulate request for suggestions.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ /*query_id=*/2, /*is_autocomplete_enabled=*/true,
+ /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name",
+ /*prefix=*/u"SomePrefix", "Some Type", suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ // Ensure that no metrics were logged.
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShownOnce,
+ 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShown,
+ 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0);
+}
+
+TEST_F(MerchantPromoCodeManagerTest,
+ OnSingleFieldSuggestion_GPayPromoCodeOfferSuggestion) {
+ // Set up the test.
+ base::HistogramTester histogram_tester;
+ auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+ int test_query_id = 2;
+ std::u16string test_name = u"Some Field Name";
+ std::u16string test_prefix = u"SomePrefix";
+ std::u16string test_promo_code = u"test_promo_code";
+ bool autoselect_first_suggestion = false;
+ bool is_autocomplete_enabled = true;
+ std::string form_control_type = "Some Type";
+ std::string last_committed_origin_url = "https://www.example.com";
+ FormData form_data;
+ form_data.main_frame_origin =
+ url::Origin::Create(GURL(last_committed_origin_url));
+ TestFormStructure form_structure{form_data};
+ SuggestionsContext context;
+ context.form_structure = &form_structure;
+ SetUpPromoCodeOffer(last_committed_origin_url,
+ GURL("https://offer-details-url.com/"));
+
+ // Check that non promo code frontend id's do not log as offer suggestion
+ // selected.
+ merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+ test_promo_code, POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelected, 0);
+
+ // Simulate showing the promo code offers suggestions popup.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+ test_name, test_prefix, form_control_type,
+ suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ // Simulate selecting a promo code offer suggestion.
+ merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+ test_promo_code, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY);
+
+ // Check that the histograms logged correctly.
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelected, 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelectedOnce,
+ 1);
+
+ // Simulate showing the promo code offers suggestions popup.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+ test_name, test_prefix, form_control_type,
+ suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ // Simulate selecting a promo code offer suggestion.
+ merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+ test_promo_code, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY);
+
+ // Check that the histograms logged correctly.
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelected, 2);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelectedOnce,
+ 1);
+}
+
+TEST_F(MerchantPromoCodeManagerTest,
+ OnSingleFieldSuggestion_GPayPromoCodeOfferFooter) {
+ // Set up the test.
+ base::HistogramTester histogram_tester;
+ auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+ int test_query_id = 2;
+ std::u16string test_name = u"Some Field Name";
+ std::u16string test_prefix = u"SomePrefix";
+ std::u16string test_promo_code = u"test_promo_code";
+ bool autoselect_first_suggestion = false;
+ bool is_autocomplete_enabled = true;
+ std::string form_control_type = "Some Type";
+ std::string last_committed_origin_url = "https://www.example.com";
+ FormData form_data;
+ form_data.main_frame_origin =
+ url::Origin::Create(GURL(last_committed_origin_url));
+ TestFormStructure form_structure{form_data};
+ SuggestionsContext context;
+ context.form_structure = &form_structure;
+ SetUpPromoCodeOffer(last_committed_origin_url,
+ GURL("https://offer-details-url.com/"));
+
+ // Check that non promo code footer frontend id's do not log as offer
+ // suggestions footer selected.
+ merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+ test_promo_code, POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::
+ kOfferSuggestionSeeOfferDetailsSelected,
+ 0);
+
+ // Simulate showing the promo code offers suggestions popup.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+ test_name, test_prefix, form_control_type,
+ suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ // Simulate selecting a promo code offer suggestion.
+ merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+ test_promo_code, POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS);
+
+ // Check that the histograms logged correctly.
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::
+ kOfferSuggestionSeeOfferDetailsSelected,
+ 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::
+ kOfferSuggestionSeeOfferDetailsSelectedOnce,
+ 1);
+
+ // Simulate showing the promo code offers suggestions popup.
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+ test_name, test_prefix, form_control_type,
+ suggestions_handler->GetWeakPtr(),
+ /*context=*/context);
+
+ // Simulate selecting a promo code offer suggestion.
+ merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+ test_promo_code, POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS);
+
+ // Check that the histograms logged correctly.
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::
+ kOfferSuggestionSeeOfferDetailsSelected,
+ 2);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+ autofill_metrics::OffersSuggestionsEvent::
+ kOfferSuggestionSeeOfferDetailsSelectedOnce,
+ 1);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics.cc b/chromium/components/autofill/core/browser/metrics/autofill_metrics.cc
index 382c2a32722..8fea197ba19 100644
--- a/chromium/components/autofill/core/browser/metrics/autofill_metrics.cc
+++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -33,6 +33,7 @@
#include "components/autofill/core/common/form_data.h"
#include "components/language/core/browser/language_usage_metrics.h"
#include "services/metrics/public/cpp/metrics_utils.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
namespace autofill {
@@ -43,6 +44,9 @@ namespace {
// Exponential bucket spacing for UKM event data.
constexpr double kAutofillEventDataBucketSpacing = 2.0;
+// Overflow bucket for form user interactions
+constexpr int64_t kFormUserInteractionsOverflowBucket = 20;
+
// Translates structured name types into simple names that are used for
// naming histograms.
constexpr auto kStructuredNameTypeToNameMap =
@@ -323,6 +327,8 @@ int GetFieldTypeGroupPredictionQualityMetric(
case NAME_SUFFIX:
case EMAIL_ADDRESS:
case PHONE_HOME_NUMBER:
+ case PHONE_HOME_NUMBER_PREFIX:
+ case PHONE_HOME_NUMBER_SUFFIX:
case PHONE_HOME_CITY_CODE:
case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
case PHONE_HOME_COUNTRY_CODE:
@@ -1096,17 +1102,6 @@ void AutofillMetrics::LogCreditCardUploadFeedbackMetric(
}
// static
-void AutofillMetrics::LogManageCardsPromptMetric(ManageCardsPromptMetric metric,
- bool is_upload_save) {
- DCHECK_LT(metric, NUM_MANAGE_CARDS_PROMPT_METRICS);
- std::string destination = is_upload_save ? ".Upload" : ".Local";
- std::string metric_with_destination =
- "Autofill.ManageCardsPrompt" + destination;
- base::UmaHistogramEnumeration(metric_with_destination, metric,
- NUM_MANAGE_CARDS_PROMPT_METRICS);
-}
-
-// static
void AutofillMetrics::LogScanCreditCardPromptMetric(
ScanCreditCardPromptMetric metric) {
DCHECK_LT(metric, NUM_SCAN_CREDIT_CARD_PROMPT_METRICS);
@@ -1237,6 +1232,9 @@ void AutofillMetrics::LogOfferNotificationBubbleOfferMetric(
case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER:
histogram_name += "CardLinkedOffer";
break;
+ case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER:
+ histogram_name += "GPayPromoCodeOffer";
+ break;
case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER:
histogram_name += "FreeListingCouponOffer";
break;
@@ -1259,6 +1257,9 @@ void AutofillMetrics::LogOfferNotificationBubbleResultMetric(
case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER:
histogram_name += "CardLinkedOffer.";
break;
+ case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER:
+ histogram_name += "GPayPromoCodeOffer.";
+ break;
case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER:
histogram_name += "FreeListingCouponOffer.";
break;
@@ -1279,6 +1280,9 @@ void AutofillMetrics::LogOfferNotificationBubblePromoCodeButtonClicked(
// Switch to different sub-histogram depending on offer type being displayed.
// Card-linked offers do not have a promo code button.
switch (offer_type) {
+ case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER:
+ histogram_name += "GPayPromoCodeOffer";
+ break;
case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER:
histogram_name += "FreeListingCouponOffer";
break;
@@ -1297,6 +1301,9 @@ void AutofillMetrics::LogOfferNotificationBubbleSuppressed(
// Switch to different sub-histogram depending on offer type being suppressed.
// Card-linked offers will not be suppressed.
switch (offer_type) {
+ case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER:
+ histogram_name += "GPayPromoCodeOffer";
+ break;
case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER:
histogram_name += "FreeListingCouponOffer";
break;
@@ -1379,33 +1386,11 @@ void AutofillMetrics::LogCardUnmaskDurationAfterWebauthn(
const base::TimeDelta& duration,
AutofillClient::PaymentsRpcResult result,
AutofillClient::PaymentsRpcCardType card_type) {
- std::string result_suffix;
-
- switch (result) {
- case AutofillClient::PaymentsRpcResult::kSuccess:
- result_suffix = "Success";
- break;
- case AutofillClient::PaymentsRpcResult::kTryAgainFailure:
- case AutofillClient::PaymentsRpcResult::kPermanentFailure:
- result_suffix = "Failure";
- break;
- case AutofillClient::PaymentsRpcResult::kNetworkError:
- result_suffix = "NetworkError";
- break;
- case AutofillClient::PaymentsRpcResult::kVcnRetrievalTryAgainFailure:
- case AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure:
- result_suffix = "VcnRetrievalFailure";
- break;
- case AutofillClient::PaymentsRpcResult::kNone:
- NOTREACHED();
- return;
- }
-
base::UmaHistogramLongTimes("Autofill.BetterAuth.CardUnmaskDuration.Fido",
duration);
base::UmaHistogramLongTimes("Autofill.BetterAuth.CardUnmaskDuration.Fido" +
- GetCreditCardTypeSuffix(card_type) + "." +
- result_suffix,
+ GetCreditCardTypeSuffix(card_type) +
+ PaymentsRpcResultToMetricsSuffix(result),
duration);
}
@@ -2251,21 +2236,6 @@ void AutofillMetrics::LogStoredCreditCardMetrics(
}
// static
-void AutofillMetrics::LogStoredOfferMetrics(
- const std::vector<std::unique_ptr<AutofillOfferData>>& offers) {
- base::UmaHistogramCounts1000("Autofill.Offer.StoredOfferCount",
- offers.size());
-
- for (const std::unique_ptr<AutofillOfferData>& offer : offers) {
- base::UmaHistogramCounts1000(
- "Autofill.Offer.StoredOfferRelatedMerchantCount",
- offer->merchant_origins.size());
- base::UmaHistogramCounts1000("Autofill.Offer.StoredOfferRelatedCardCount",
- offer->eligible_instrument_id.size());
- }
-}
-
-// static
void AutofillMetrics::LogSyncedOfferDataBeingValid(bool valid) {
base::UmaHistogramBoolean("Autofill.Offer.SyncedOfferDataBeingValid", valid);
}
@@ -2388,7 +2358,8 @@ void AutofillMetrics::LogAutofillFormSubmittedState(
const DenseSet<FormType>& form_types,
const base::TimeTicks& form_parsed_timestamp,
FormSignature form_signature,
- AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) {
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ const FormInteractionCounts& form_interaction_counts) {
UMA_HISTOGRAM_ENUMERATION("Autofill.FormSubmittedState", state,
AUTOFILL_FORM_SUBMITTED_STATE_ENUM_SIZE);
@@ -2424,7 +2395,7 @@ void AutofillMetrics::LogAutofillFormSubmittedState(
}
form_interactions_ukm_logger->LogFormSubmitted(
is_for_credit_card, has_upi_vpa_field, form_types, state,
- form_parsed_timestamp, form_signature);
+ form_parsed_timestamp, form_signature, form_interaction_counts);
}
// static
@@ -2551,7 +2522,8 @@ uint8_t AutofillMetrics::CreditCardSeamlessness::BitmaskMetric() const {
void AutofillMetrics::LogCreditCardSeamlessnessAtFillTime(
const LogCreditCardSeamlessnessParam& p) {
auto GetSeamlessness = [&p](bool only_newly_filled_fields,
- bool only_after_security_policy) {
+ bool only_after_security_policy,
+ bool only_visible_fields) {
ServerFieldTypeSet autofilled_types;
for (const auto& field : p.form) {
FieldGlobalId id = field->global_id();
@@ -2559,6 +2531,8 @@ void AutofillMetrics::LogCreditCardSeamlessnessAtFillTime(
continue;
if (only_after_security_policy && !p.safe_fields.contains(id))
continue;
+ if (only_visible_fields && !field->is_visible)
+ continue;
autofilled_types.insert(field->Type().GetStorableType());
}
return CreditCardSeamlessness(autofilled_types);
@@ -2571,29 +2545,53 @@ void AutofillMetrics::LogCreditCardSeamlessnessAtFillTime(
s.BitmaskExclusiveMax());
};
- if (auto s = GetSeamlessness(false, false)) {
+ if (auto s = GetSeamlessness(false, false, false)) {
RecordUma("Fillable.AtFillTimeBeforeSecurityPolicy", s);
p.builder.SetFillable_BeforeSecurity_Bitmask(s.BitmaskMetric());
p.builder.SetFillable_BeforeSecurity_Qualitative(
s.QualitativeMetricAsInt());
p.event_logger.Log(s.QualitativeFillableFormEvent(), p.form);
}
- if (auto s = GetSeamlessness(false, true)) {
+ if (auto s = GetSeamlessness(false, true, false)) {
RecordUma("Fillable.AtFillTimeAfterSecurityPolicy", s);
p.builder.SetFillable_AfterSecurity_Bitmask(s.BitmaskMetric());
p.builder.SetFillable_AfterSecurity_Qualitative(s.QualitativeMetricAsInt());
}
- if (auto s = GetSeamlessness(true, false)) {
+ if (auto s = GetSeamlessness(true, false, false)) {
RecordUma("Fills.AtFillTimeBeforeSecurityPolicy", s);
p.builder.SetFilled_BeforeSecurity_Bitmask(s.BitmaskMetric());
p.builder.SetFilled_BeforeSecurity_Qualitative(s.QualitativeMetricAsInt());
}
- if (auto s = GetSeamlessness(true, true)) {
+ if (auto s = GetSeamlessness(true, true, false)) {
RecordUma("Fills.AtFillTimeAfterSecurityPolicy", s);
p.builder.SetFilled_AfterSecurity_Bitmask(s.BitmaskMetric());
p.builder.SetFilled_AfterSecurity_Qualitative(s.QualitativeMetricAsInt());
p.event_logger.Log(s.QualitativeFillFormEvent(), p.form);
}
+ if (auto s = GetSeamlessness(false, false, true)) {
+ RecordUma("Fillable.AtFillTimeBeforeSecurityPolicy.Visible", s);
+ p.builder.SetFillable_BeforeSecurity_Visible_Bitmask(s.BitmaskMetric());
+ p.builder.SetFillable_BeforeSecurity_Visible_Qualitative(
+ s.QualitativeMetricAsInt());
+ }
+ if (auto s = GetSeamlessness(false, true, true)) {
+ RecordUma("Fillable.AtFillTimeAfterSecurityPolicy.Visible", s);
+ p.builder.SetFillable_AfterSecurity_Visible_Bitmask(s.BitmaskMetric());
+ p.builder.SetFillable_AfterSecurity_Visible_Qualitative(
+ s.QualitativeMetricAsInt());
+ }
+ if (auto s = GetSeamlessness(true, false, true)) {
+ RecordUma("Fills.AtFillTimeBeforeSecurityPolicy.Visible", s);
+ p.builder.SetFilled_BeforeSecurity_Visible_Bitmask(s.BitmaskMetric());
+ p.builder.SetFilled_BeforeSecurity_Visible_Qualitative(
+ s.QualitativeMetricAsInt());
+ }
+ if (auto s = GetSeamlessness(true, true, true)) {
+ RecordUma("Fills.AtFillTimeAfterSecurityPolicy.Visible", s);
+ p.builder.SetFilled_AfterSecurity_Visible_Bitmask(s.BitmaskMetric());
+ p.builder.SetFilled_AfterSecurity_Visible_Qualitative(
+ s.QualitativeMetricAsInt());
+ }
// In a multi-frame form, a cross-origin field is filled only if
// shared-autofill is enabled in the field's frame. Here, we log whether
@@ -2674,12 +2672,6 @@ void AutofillMetrics::LogCreditCardSeamlessnessAtSubmissionTime(
}
// static
-void AutofillMetrics::LogDetermineHeuristicTypesTiming(
- const base::TimeDelta& duration) {
- UMA_HISTOGRAM_TIMES("Autofill.Timing.DetermineHeuristicTypes", duration);
-}
-
-// static
void AutofillMetrics::LogParseFormTiming(const base::TimeDelta& duration) {
UMA_HISTOGRAM_TIMES("Autofill.Timing.ParseForm", duration);
}
@@ -3081,7 +3073,8 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted(
const DenseSet<FormType>& form_types,
AutofillFormSubmittedState state,
const base::TimeTicks& form_parsed_timestamp,
- FormSignature form_signature) {
+ FormSignature form_signature,
+ const FormInteractionCounts& form_interaction_counts) {
if (!CanLog())
return;
@@ -3090,7 +3083,14 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted(
.SetIsForCreditCard(is_for_credit_card)
.SetHasUpiVpaField(has_upi_vpa_field)
.SetFormTypes(FormTypesToBitVector(form_types))
- .SetFormSignature(HashFormSignature(form_signature));
+ .SetFormSignature(HashFormSignature(form_signature))
+ .SetFormElementUserModifications(
+ std::min(form_interaction_counts.form_element_user_modifications,
+ kFormUserInteractionsOverflowBucket))
+ .SetAutofillFills(std::min(form_interaction_counts.autofill_fills,
+ kFormUserInteractionsOverflowBucket))
+ .SetAutocompleteFills(std::min(form_interaction_counts.autocomplete_fills,
+ kFormUserInteractionsOverflowBucket));
if (form_parsed_timestamp.is_null())
DCHECK(state == NON_FILLABLE_FORM_OR_NEW_DATA ||
state == FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS)
@@ -3177,7 +3177,7 @@ void AutofillMetrics::
}
// static
-void AutofillMetrics::LogAddressFormImportStatustMetric(
+void AutofillMetrics::LogAddressFormImportStatusMetric(
AutofillMetrics::AddressProfileImportStatusMetric metric) {
base::UmaHistogramEnumeration("Autofill.AddressProfileImportStatus", metric);
}
@@ -3402,13 +3402,22 @@ void AutofillMetrics::LogRemovedSettingInaccessibleFields(bool did_remove) {
// static
void AutofillMetrics::LogRemovedSettingInaccessibleField(
- const std::string& country_code,
ServerFieldType field) {
base::UmaHistogramEnumeration(
- "Autofill.ProfileImport.InaccessibleFieldsRemoved." + country_code,
+ "Autofill.ProfileImport.InaccessibleFieldsRemoved.ByFieldType",
ConvertSettingsVisibleFieldTypeForMetrics(field));
}
+// static
+void AutofillMetrics::LogPhoneNumberImportParsingResult(
+ bool with_variation_country_code,
+ bool with_app_locale) {
+ base::UmaHistogramEnumeration(
+ "Autofill.ProfileImport.PhoneNumberParsingResult",
+ static_cast<AutofillMetrics::PhoneNumberImportParsingResult>(
+ (with_variation_country_code << 1) | with_app_locale));
+}
+
void AutofillMetrics::LogVerificationStatusOfNameTokensOnProfileUsage(
const AutofillProfile& profile) {
constexpr base::StringPiece base_histogram_name =
@@ -3551,4 +3560,30 @@ void AutofillMetrics::
is_same);
}
+const std::string PaymentsRpcResultToMetricsSuffix(
+ AutofillClient::PaymentsRpcResult result) {
+ std::string result_suffix;
+
+ switch (result) {
+ case AutofillClient::PaymentsRpcResult::kSuccess:
+ result_suffix = ".Success";
+ break;
+ case AutofillClient::PaymentsRpcResult::kTryAgainFailure:
+ case AutofillClient::PaymentsRpcResult::kPermanentFailure:
+ result_suffix = ".Failure";
+ break;
+ case AutofillClient::PaymentsRpcResult::kNetworkError:
+ result_suffix = ".NetworkError";
+ break;
+ case AutofillClient::PaymentsRpcResult::kVcnRetrievalTryAgainFailure:
+ case AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure:
+ result_suffix = ".VcnRetrievalFailure";
+ break;
+ case AutofillClient::PaymentsRpcResult::kNone:
+ NOTREACHED();
+ }
+
+ return result_suffix;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics.h b/chromium/components/autofill/core/browser/metrics/autofill_metrics.h
index ac9eea66f59..3259088da19 100644
--- a/chromium/components/autofill/core/browser/metrics/autofill_metrics.h
+++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics.h
@@ -14,31 +14,38 @@
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
+#include "base/strings/string_piece_forward.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_profile_import_process.h"
#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_types.h"
#include "components/autofill/core/browser/metrics/form_events/form_events.h"
+#include "components/autofill/core/browser/metrics/form_interactions_counter.h"
#include "components/autofill/core/browser/sync_utils.h"
#include "components/autofill/core/browser/ui/popup_types.h"
#include "components/autofill/core/common/dense_set.h"
-#include "components/autofill/core/common/form_field_data.h"
-#include "components/autofill/core/common/mojom/autofill_types.mojom.h"
+#include "components/autofill/core/common/mojom/autofill_types.mojom-forward.h"
#include "components/autofill/core/common/signatures.h"
+#include "components/autofill/core/common/unique_ids.h"
#include "components/security_state/core/security_state.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+
+class GURL;
+
+namespace ukm::builders {
+class Autofill_CreditCardFill;
+}
namespace autofill {
class AutofillField;
+class AutofillOfferData;
class CreditCard;
class FormEventLoggerBase;
-struct AutofillOfferData;
// A given maximum is enforced to minimize the number of buckets generated.
extern const int kMaxBucketsCount;
@@ -352,20 +359,6 @@ class AutofillMetrics {
NUM_CREDIT_CARD_UPLOAD_FEEDBACK_METRICS,
};
- // Metrics to measure user interaction with the Manage Cards view
- // shown when user clicks on the save card icon after accepting
- // to save a card.
- enum ManageCardsPromptMetric {
- // The manage cards promo was shown.
- MANAGE_CARDS_SHOWN,
- // The user clicked on [Done].
- MANAGE_CARDS_DONE,
- // The user clicked on [Manage cards].
- MANAGE_CARDS_MANAGE_CARDS,
-
- NUM_MANAGE_CARDS_PROMPT_METRICS
- };
-
// Metrics to measure user interaction with the virtual card manual fallback
// bubble after it has appeared upon unmasking and filling a virtual card.
enum class VirtualCardManualFallbackBubbleResultMetric {
@@ -934,14 +927,13 @@ class AutofillMetrics {
};
// For measuring the frequency of "required actions" returned by the Wallet
- // server. This is similar to the autofill::wallet::RequiredAction enum;
- // but unlike that enum, the values in this one must remain constant over
- // time, so that the metrics can be consistently interpreted on the
- // server-side.
+ // server. This is similar to the wallet::RequiredAction enum; but unlike
+ // that enum, the values in this one must remain constant over time, so that
+ // the metrics can be consistently interpreted on the server-side.
enum WalletRequiredActionMetric {
// Baseline metric: Issued a request to the Wallet server.
WALLET_REQUIRED_ACTION_BASELINE_ISSUED_REQUEST = 0,
- // Values from the autofill::wallet::RequiredAction enum:
+ // Values from the wallet::RequiredAction enum:
UNKNOWN_REQUIRED_ACTION, // Catch all type.
GAIA_AUTH,
PASSIVE_GAIA_AUTH,
@@ -1073,6 +1065,18 @@ class AutofillMetrics {
kMaxValue = SECTION_UNION_IMPORT,
};
+ // When parsing a nationally formatted phone number on profile import, a
+ // region has to be assumed. This enum represents if a phone number could be
+ // parsed by assuming the app locale and/or the variation country code as its
+ // region.
+ enum class PhoneNumberImportParsingResult {
+ CANNOT_PARSE = 0,
+ PARSED_WITH_APP_LOCALE = 1,
+ PARSED_WITH_VARIATION_COUNTRY_CODE = 2,
+ PARSED_WITH_BOTH = 3,
+ kMaxValue = PARSED_WITH_BOTH,
+ };
+
// To record the source of the autofilled state field.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
@@ -1284,7 +1288,8 @@ class AutofillMetrics {
const DenseSet<FormType>& form_types,
AutofillFormSubmittedState state,
const base::TimeTicks& form_parsed_timestamp,
- FormSignature form_signature);
+ FormSignature form_signature,
+ const FormInteractionCounts& form_interaction_counts);
void LogFormEvent(FormEvent form_event,
const DenseSet<FormType>& form_types,
const base::TimeTicks& form_parsed_timestamp);
@@ -1411,8 +1416,6 @@ class AutofillMetrics {
static void LogCreditCardUploadLegalMessageLinkClicked();
static void LogCreditCardUploadFeedbackMetric(
CreditCardUploadFeedbackMetric metric);
- static void LogManageCardsPromptMetric(ManageCardsPromptMetric metric,
- bool is_uploading);
static void LogScanCreditCardPromptMetric(ScanCreditCardPromptMetric metric);
static void LogLocalCardMigrationDecisionMetric(
LocalCardMigrationDecisionMetric metric);
@@ -1705,11 +1708,6 @@ class AutofillMetrics {
size_t server_card_count_with_card_art_image,
base::TimeDelta disused_data_threshold);
- // Logs metrics about the offer data associated with a profile. This should be
- // called each time a chrome profile is launched.
- static void LogStoredOfferMetrics(
- const std::vector<std::unique_ptr<AutofillOfferData>>& offers);
-
// Logs whether the synced autofill offer data is valid.
static void LogSyncedOfferDataBeingValid(bool invalid);
@@ -1798,7 +1796,8 @@ class AutofillMetrics {
const DenseSet<FormType>& form_types,
const base::TimeTicks& form_parsed_timestamp,
FormSignature form_signature,
- FormInteractionsUkmLogger* form_interactions_ukm_logger);
+ FormInteractionsUkmLogger* form_interactions_ukm_logger,
+ const FormInteractionCounts& form_interaction_counts);
// Logs if every non-empty field in a submitted form was filled by Autofill.
// If |is_address| an address was filled, otherwise it was a credit card.
@@ -1836,10 +1835,6 @@ class AutofillMetrics {
static void LogCreditCardSeamlessnessAtSubmissionTime(
const ServerFieldTypeSet& autofilled_types);
- // This should be called when determining the heuristic types for a form's
- // fields.
- static void LogDetermineHeuristicTypesTiming(const base::TimeDelta& duration);
-
// This should be called when parsing each form.
static void LogParseFormTiming(const base::TimeDelta& duration);
@@ -1932,7 +1927,8 @@ class AutofillMetrics {
const FormStructure& form,
const AutofillField& field);
- static void LogAddressFormImportStatustMetric(
+ // Logs the overall status of an address import upon form submission.
+ static void LogAddressFormImportStatusMetric(
AddressProfileImportStatusMetric metric);
// Records if the page was translated upon form submission.
@@ -2042,14 +2038,18 @@ class AutofillMetrics {
int number_of_affected_fields,
AutofillClient::SaveAddressProfileOfferUserDecision decision);
- // Records if at least one setting-inaccessible field was removed on import.
+ // Logs if at least one setting-inaccessible field was removed on import.
static void LogRemovedSettingInaccessibleFields(bool did_remove);
- // Records that |field| was removed in a profile of |country| on import,
- // because |field| is inaccessible in the |country|-specific settings.
- static void LogRemovedSettingInaccessibleField(
- const std::string& country_code,
- ServerFieldType field);
+ // Logs that `field` was removed from a profile on import, because it is
+ // setting-inaccessible in the profile's country.
+ static void LogRemovedSettingInaccessibleField(ServerFieldType field);
+
+ // Logs the outcome of parsing a phone number on profile import when assuming
+ // either the variation country code or the app locale as its region.
+ static void LogPhoneNumberImportParsingResult(
+ bool with_variation_country_code,
+ bool with_app_locale);
// Logs when the virtual card metadata for one card have been updated.
static void LogVirtualCardMetadataSynced(bool existing_card);
@@ -2124,5 +2124,8 @@ int GetFieldTypeUserEditStatusMetric(
AutofillMetrics::AutofilledFieldUserEditingStatusMetric metric);
#endif
+const std::string PaymentsRpcResultToMetricsSuffix(
+ AutofillClient::PaymentsRpcResult result);
+
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_AUTOFILL_METRICS_H_
diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc b/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc
new file mode 100644
index 00000000000..158a195d005
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc
@@ -0,0 +1,331 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/metrics/autofill_metrics_test_base.h"
+
+#include "components/autofill/core/browser/autofill_form_test_utils.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/payments/credit_card_access_manager.h"
+#include "components/autofill/core/common/autofill_clock.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+#if !BUILDFLAG(IS_IOS)
+#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
+#endif
+
+namespace autofill::metrics {
+
+namespace {
+void SetProfileTestData(AutofillProfile* profile) {
+ test::SetProfileInfo(profile, "Elvis", "Aaron", "Presley",
+ "theking@gmail.com", "RCA", "3734 Elvis Presley Blvd.",
+ "Apt. 10", "Memphis", "Tennessee", "38116", "US",
+ "12345678901");
+ profile->set_guid(kTestGuid);
+}
+} // namespace
+
+MockAutofillClient::MockAutofillClient() = default;
+MockAutofillClient::~MockAutofillClient() = default;
+
+AutofillMetricsBaseTest::AutofillMetricsBaseTest(bool is_in_any_main_frame)
+ : is_in_any_main_frame_(is_in_any_main_frame) {}
+
+AutofillMetricsBaseTest::~AutofillMetricsBaseTest() = default;
+
+void AutofillMetricsBaseTest::SetUp() {
+ autofill_client_ = std::make_unique<MockAutofillClient>();
+ autofill_client_->SetPrefs(test::PrefServiceForTesting());
+ test_ukm_recorder_ = autofill_client_->GetTestUkmRecorder();
+
+ personal_data().set_auto_accept_address_imports_for_testing(true);
+ personal_data().SetPrefService(autofill_client_->GetPrefs());
+ personal_data().OnSyncServiceInitialized(&sync_service_);
+
+ autofill_driver_ = std::make_unique<TestAutofillDriver>();
+ autofill_driver_->SetIsInAnyMainFrame(is_in_any_main_frame_);
+
+ payments::TestPaymentsClient* payments_client =
+ new payments::TestPaymentsClient(autofill_driver_->GetURLLoaderFactory(),
+ autofill_client_->GetIdentityManager(),
+ &personal_data());
+ autofill_client_->set_test_payments_client(
+ std::unique_ptr<payments::TestPaymentsClient>(payments_client));
+ auto credit_card_save_manager = std::make_unique<TestCreditCardSaveManager>(
+ autofill_driver_.get(), autofill_client_.get(), payments_client,
+ &personal_data());
+ autofill_client_->set_test_form_data_importer(
+ std::make_unique<TestFormDataImporter>(
+ autofill_client_.get(), payments_client,
+ std::move(credit_card_save_manager), &personal_data(), "en-US"));
+ autofill_client_->set_autofill_offer_manager(
+ std::make_unique<AutofillOfferManager>(
+ &personal_data(), /*coupon_service_delegate=*/nullptr));
+
+ auto browser_autofill_manager = std::make_unique<TestBrowserAutofillManager>(
+ autofill_driver_.get(), autofill_client_.get());
+ autofill_driver_->set_autofill_manager(std::move(browser_autofill_manager));
+
+ auto external_delegate = std::make_unique<AutofillExternalDelegate>(
+ &autofill_manager(), autofill_driver_.get());
+ external_delegate_ = external_delegate.get();
+ autofill_manager().SetExternalDelegateForTest(std::move(external_delegate));
+
+#if !BUILDFLAG(IS_IOS)
+ autofill_manager()
+ .GetCreditCardAccessManager()
+ ->set_fido_authenticator_for_testing(
+ std::make_unique<TestCreditCardFIDOAuthenticator>(
+ autofill_driver_.get(), autofill_client_.get()));
+#endif
+
+ // Initialize the TestPersonalDataManager with some default data.
+ CreateTestAutofillProfiles();
+}
+
+void AutofillMetricsBaseTest::TearDown() {
+ test_ukm_recorder_->Purge();
+ autofill_driver_.reset();
+ autofill_client_.reset();
+}
+
+void AutofillMetricsBaseTest::PurgeUKM() {
+ autofill_manager().Reset();
+ test_ukm_recorder_->Purge();
+ autofill_client_->InitializeUKMSources();
+}
+
+void AutofillMetricsBaseTest::CreateAmbiguousProfiles() {
+ personal_data().ClearProfiles();
+ CreateTestAutofillProfiles();
+
+ AutofillProfile profile;
+ test::SetProfileInfo(&profile, "John", "Decca", "Public", "john@gmail.com",
+ "Company", "123 Main St.", "unit 7", "Springfield",
+ "Texas", "79401", "US", "2345678901");
+ profile.set_guid("00000000-0000-0000-0000-000000000003");
+ personal_data().AddProfile(profile);
+ personal_data().Refresh();
+}
+
+void AutofillMetricsBaseTest::RecreateProfile(bool is_server) {
+ personal_data().ClearProfiles();
+
+ if (is_server) {
+ AutofillProfile profile(AutofillProfile::SERVER_PROFILE, "server_id");
+ SetProfileTestData(&profile);
+ personal_data().AddProfile(profile);
+ } else {
+ AutofillProfile profile;
+ SetProfileTestData(&profile);
+ personal_data().AddProfile(profile);
+ }
+
+ personal_data().Refresh();
+}
+
+void AutofillMetricsBaseTest::SetFidoEligibility(bool is_verifiable) {
+ CreditCardAccessManager* access_manager =
+ autofill_manager().GetCreditCardAccessManager();
+#if !BUILDFLAG(IS_IOS)
+ static_cast<TestCreditCardFIDOAuthenticator*>(
+ access_manager->GetOrCreateFIDOAuthenticator())
+ ->SetUserVerifiable(is_verifiable);
+#endif
+ static_cast<payments::TestPaymentsClient*>(
+ autofill_client_->GetPaymentsClient())
+ ->AllowFidoRegistration(true);
+ access_manager->is_authentication_in_progress_ = false;
+ access_manager->can_fetch_unmask_details_ = true;
+ access_manager->is_user_verifiable_ = absl::nullopt;
+}
+
+void AutofillMetricsBaseTest::OnDidGetRealPan(
+ AutofillClient::PaymentsRpcResult result,
+ const std::string& real_pan,
+ bool is_virtual_card) {
+ payments::FullCardRequest* full_card_request =
+ autofill_manager()
+ .credit_card_access_manager_->GetOrCreateCVCAuthenticator()
+ ->full_card_request_.get();
+ DCHECK(full_card_request);
+
+ // Fake user response.
+ payments::FullCardRequest::UserProvidedUnmaskDetails details;
+ details.cvc = u"123";
+ full_card_request->OnUnmaskPromptAccepted(details);
+
+ payments::PaymentsClient::UnmaskResponseDetails response;
+ response.card_type = is_virtual_card
+ ? AutofillClient::PaymentsRpcCardType::kVirtualCard
+ : AutofillClient::PaymentsRpcCardType::kServerCard;
+ full_card_request->OnDidGetRealPan(result, response.with_real_pan(real_pan));
+}
+
+void AutofillMetricsBaseTest::OnDidGetRealPanWithNonHttpOkResponse() {
+ payments::FullCardRequest* full_card_request =
+ autofill_manager()
+ .credit_card_access_manager_->GetOrCreateCVCAuthenticator()
+ ->full_card_request_.get();
+ DCHECK(full_card_request);
+
+ // Fake user response.
+ payments::FullCardRequest::UserProvidedUnmaskDetails details;
+ details.cvc = u"123";
+ full_card_request->OnUnmaskPromptAccepted(details);
+
+ payments::PaymentsClient::UnmaskResponseDetails response;
+ // Don't set |response.card_type|, so that it stays as kUnknown.
+ full_card_request->OnDidGetRealPan(
+ AutofillClient::PaymentsRpcResult::kPermanentFailure, response);
+}
+
+void AutofillMetricsBaseTest::OnCreditCardFetchingSuccessful(
+ const std::u16string& real_pan,
+ bool is_virtual_card) {
+ credit_card_.set_record_type(
+ is_virtual_card ? CreditCard::RecordType::VIRTUAL_CARD
+ : CreditCard::RecordType::MASKED_SERVER_CARD);
+ credit_card_.SetNumber(real_pan);
+
+ autofill_manager().OnCreditCardFetched(CreditCardFetchResult::kSuccess,
+ &credit_card_, u"123");
+}
+
+void AutofillMetricsBaseTest::OnCreditCardFetchingFailed() {
+ autofill_manager().OnCreditCardFetched(CreditCardFetchResult::kPermanentError,
+ nullptr, u"");
+}
+
+void AutofillMetricsBaseTest::RecreateCreditCards(
+ bool include_local_credit_card,
+ bool include_masked_server_credit_card,
+ bool include_full_server_credit_card,
+ bool masked_card_is_enrolled_for_virtual_card) {
+ personal_data().ClearCreditCards();
+ if (include_local_credit_card) {
+ CreditCard local_credit_card;
+ test::SetCreditCardInfo(&local_credit_card, "Test User",
+ "4111111111111111" /* Visa */, "11", "2022", "1");
+ local_credit_card.set_guid("10000000-0000-0000-0000-000000000001");
+ personal_data().AddCreditCard(local_credit_card);
+ }
+ if (include_masked_server_credit_card) {
+ CreditCard masked_server_credit_card(CreditCard::MASKED_SERVER_CARD,
+ "server_id_1");
+ masked_server_credit_card.set_guid("10000000-0000-0000-0000-000000000002");
+ masked_server_credit_card.set_instrument_id(1);
+ masked_server_credit_card.SetNetworkForMaskedCard(kDiscoverCard);
+ masked_server_credit_card.SetNumber(u"9424");
+ if (masked_card_is_enrolled_for_virtual_card) {
+ masked_server_credit_card.set_virtual_card_enrollment_state(
+ CreditCard::ENROLLED);
+ }
+ personal_data().AddServerCreditCard(masked_server_credit_card);
+ }
+ if (include_full_server_credit_card) {
+ CreditCard full_server_credit_card(CreditCard::FULL_SERVER_CARD,
+ "server_id_2");
+ full_server_credit_card.set_guid("10000000-0000-0000-0000-000000000003");
+ full_server_credit_card.set_instrument_id(2);
+ personal_data().AddFullServerCreditCard(full_server_credit_card);
+ }
+ personal_data().Refresh();
+}
+
+std::string AutofillMetricsBaseTest::CreateLocalMasterCard(
+ bool clear_existing_cards) {
+ if (clear_existing_cards) {
+ personal_data().ClearCreditCards();
+ }
+ std::string guid("10000000-0000-0000-0000-000000000003");
+ CreditCard local_credit_card;
+ test::SetCreditCardInfo(&local_credit_card, "Test User",
+ "5454545454545454" /* Mastercard */, "08", "2022",
+ "1");
+ local_credit_card.set_guid(guid);
+ personal_data().AddCreditCard(local_credit_card);
+ return guid;
+}
+
+std::vector<std::string>
+AutofillMetricsBaseTest::CreateLocalAndDuplicateServerCreditCard() {
+ personal_data().ClearCreditCards();
+
+ // Local credit card creation.
+ CreditCard local_credit_card;
+ test::SetCreditCardInfo(&local_credit_card, "Test User",
+ "4111111111111111" /* Visa */, "11", "2022", "1");
+ std::string local_card_guid("10000000-0000-0000-0000-000000000001");
+ local_credit_card.set_guid(local_card_guid);
+ personal_data().AddCreditCard(local_credit_card);
+
+ // Duplicate masked server card with same card information as local card.
+ CreditCard masked_server_credit_card(CreditCard::MASKED_SERVER_CARD,
+ "server_id_2");
+ std::string server_card_guid("10000000-0000-0000-0000-000000000002");
+ masked_server_credit_card.set_guid(server_card_guid);
+ test::SetCreditCardInfo(&masked_server_credit_card, "Test User",
+ "4111111111111111" /* Visa */, "11", "2022", "1");
+ masked_server_credit_card.set_instrument_id(1);
+ masked_server_credit_card.SetNetworkForMaskedCard(kVisaCard);
+ masked_server_credit_card.SetNumber(u"1111");
+ personal_data().AddServerCreditCard(masked_server_credit_card);
+
+ personal_data().Refresh();
+ return {local_card_guid, server_card_guid};
+}
+
+void AutofillMetricsBaseTest::AddMaskedServerCreditCardWithOffer(
+ std::string guid,
+ std::string offer_reward_amount,
+ GURL url,
+ int64_t id,
+ bool offer_expired) {
+ CreditCard masked_server_credit_card(CreditCard::MASKED_SERVER_CARD,
+ "server_id_offer");
+ masked_server_credit_card.set_guid(guid);
+ masked_server_credit_card.set_instrument_id(id);
+ masked_server_credit_card.SetNetworkForMaskedCard(kDiscoverCard);
+ masked_server_credit_card.SetNumber(u"9424");
+ personal_data().AddServerCreditCard(masked_server_credit_card);
+
+ int64_t offer_id = id;
+ base::Time expiry = offer_expired ? AutofillClock::Now() - base::Days(2)
+ : AutofillClock::Now() + base::Days(2);
+ std::vector<GURL> merchant_origins = {GURL{url}};
+ GURL offer_details_url = GURL(url);
+ DisplayStrings display_strings;
+ display_strings.value_prop_text = "Get 5% off your purchase";
+ display_strings.see_details_text = "See details";
+ display_strings.usage_instructions_text =
+ "Check out with this card to activate";
+ std::vector<int64_t> eligible_instrument_id = {
+ masked_server_credit_card.instrument_id()};
+
+ AutofillOfferData offer_data = AutofillOfferData::GPayCardLinkedOffer(
+ offer_id, expiry, merchant_origins, offer_details_url, display_strings,
+ eligible_instrument_id, offer_reward_amount);
+ personal_data().AddAutofillOfferData(offer_data);
+ personal_data().Refresh();
+}
+
+void AutofillMetricsBaseTest::CreateTestAutofillProfiles() {
+ AutofillProfile profile1;
+ test::SetProfileInfo(&profile1, "Elvis", "Aaron", "Presley",
+ "theking@gmail.com", "RCA", "3734 Elvis Presley Blvd.",
+ "Apt. 10", "Memphis", "Tennessee", "38116", "US",
+ "12345678901");
+ profile1.set_guid(kTestGuid);
+ personal_data().AddProfile(profile1);
+
+ AutofillProfile profile2;
+ test::SetProfileInfo(&profile2, "Charles", "Hardin", "Holley",
+ "buddy@gmail.com", "Decca", "123 Apple St.", "unit 6",
+ "Lubbock", "Texas", "79401", "US", "2345678901");
+ profile2.set_guid("00000000-0000-0000-0000-000000000002");
+ personal_data().AddProfile(profile2);
+}
+
+} // namespace autofill::metrics
diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.h b/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.h
new file mode 100644
index 00000000000..c869713d05d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.h
@@ -0,0 +1,123 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_AUTOFILL_METRICS_TEST_BASE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_AUTOFILL_METRICS_TEST_BASE_H_
+
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/payments/test_credit_card_save_manager.h"
+#include "components/autofill/core/browser/payments/test_payments_client.h"
+#include "components/autofill/core/browser/test_autofill_client.h"
+#include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/browser/test_browser_autofill_manager.h"
+#include "components/sync/driver/test_sync_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill::metrics {
+
+constexpr char kTestGuid[] = "00000000-0000-0000-0000-000000000001";
+
+class MockAutofillClient : public TestAutofillClient {
+ public:
+ MockAutofillClient();
+ ~MockAutofillClient() override;
+ MOCK_METHOD(void, ExecuteCommand, (int), (override));
+};
+
+class AutofillMetricsBaseTest : public testing::Test {
+ public:
+ explicit AutofillMetricsBaseTest(bool is_in_any_main_frame = true);
+ ~AutofillMetricsBaseTest() override;
+
+ void SetUp() override;
+ void TearDown() override;
+
+ protected:
+ void CreateAmbiguousProfiles();
+
+ // Removes all existing profiles and creates one profile.
+ // |is_server| allows creation of |SERVER_PROFILE|.
+ void RecreateProfile(bool is_server);
+
+ // Removes all existing credit cards and creates a local, masked server,
+ // full server, and/or virtual credit card, according to the parameters.
+ // TODO(crbug/1216615): Migrate this to a params builder pattern or
+ // something.
+ void RecreateCreditCards(bool include_local_credit_card,
+ bool include_masked_server_credit_card,
+ bool include_full_server_credit_card,
+ bool masked_card_is_enrolled_for_virtual_card);
+
+ // Creates a local card to existing card deck or clear them all and then add a
+ // new local card.
+ // The GUID for the card created is returned as a string.
+ std::string CreateLocalMasterCard(bool clear_existing_cards = false);
+
+ // Creates a local card and then a duplicate server card with the same
+ // credentials/info.
+ // The GUIDs for the cards crated are returned as a vector of strings.
+ std::vector<std::string> CreateLocalAndDuplicateServerCreditCard();
+
+ void AddMaskedServerCreditCardWithOffer(std::string guid,
+ std::string offer_reward_amount,
+ GURL url,
+ int64_t id,
+ bool offer_expired = false);
+
+ // If set to true, then user is capable of using FIDO authentication for
+ // card unmasking.
+ void SetFidoEligibility(bool is_verifiable);
+
+ // Mocks a RPC response from Payments.
+ void OnDidGetRealPan(AutofillClient::PaymentsRpcResult result,
+ const std::string& real_pan,
+ bool is_virtual_card = false);
+
+ // Mocks a RPC response from Payments, but where a non-HTTP_OK response
+ // stopped it from parsing a valid response.
+ void OnDidGetRealPanWithNonHttpOkResponse();
+
+ // Purge recorded UKM metrics for running more tests.
+ void PurgeUKM();
+
+ void ResetDriverToCommitMetrics() { autofill_driver_.reset(); }
+
+ // Mocks a credit card fetching was completed. This mock starts from the
+ // BrowserAutofillManager. Use these if your test does not depends on
+ // OnDidGetRealPan but just need to mock the card fetching result (so that
+ // you don't need to branch on what auth method was used).
+ void OnCreditCardFetchingSuccessful(const std::u16string& real_pan,
+ bool is_virtual_card = false);
+ void OnCreditCardFetchingFailed();
+
+ TestBrowserAutofillManager& autofill_manager() {
+ return static_cast<TestBrowserAutofillManager&>(
+ *autofill_driver_->autofill_manager());
+ }
+
+ TestPersonalDataManager& personal_data() {
+ return *autofill_client_->GetPersonalDataManager();
+ }
+
+ const bool is_in_any_main_frame_ = true;
+ base::test::TaskEnvironment task_environment_;
+ std::unique_ptr<MockAutofillClient> autofill_client_;
+ raw_ptr<ukm::TestUkmRecorder> test_ukm_recorder_;
+ syncer::TestSyncService sync_service_;
+ std::unique_ptr<TestAutofillDriver> autofill_driver_;
+ raw_ptr<AutofillExternalDelegate> external_delegate_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+
+ private:
+ void CreateTestAutofillProfiles();
+
+ CreditCard credit_card_ = test::GetMaskedServerCard();
+};
+
+} // namespace autofill::metrics
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_AUTOFILL_METRICS_TEST_BASE_H_
diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
index e12a81dbe82..12031ebda35 100644
--- a/chromium/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
+++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -12,6 +12,7 @@
#include <vector>
#include "base/base64.h"
+#include "base/containers/cxx20_erase.h"
#include "base/containers/fixed_flat_map.h"
#include "base/feature_list.h"
#include "base/ios/ios_util.h"
@@ -19,6 +20,7 @@
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/statistics_recorder.h"
#include "base/strings/strcat.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
@@ -29,12 +31,15 @@
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_external_delegate.h"
#include "components/autofill/core/browser/autofill_form_test_utils.h"
+#include "components/autofill/core/browser/autofill_suggestion_generator.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/metrics/autofill_metrics_test_base.h"
#include "components/autofill/core/browser/metrics/form_events/address_form_event_logger.h"
#include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h"
#include "components/autofill/core/browser/metrics/form_events/form_events.h"
+#include "components/autofill/core/browser/metrics/form_interactions_counter.h"
#include "components/autofill/core/browser/payments/credit_card_access_manager.h"
#include "components/autofill/core/browser/payments/test_credit_card_save_manager.h"
#include "components/autofill/core/browser/payments/test_payments_client.h"
@@ -77,11 +82,15 @@
#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
#endif
-using base::ASCIIToUTF16;
-using base::Bucket;
-using base::TimeTicks;
+using ::autofill::metrics::kTestGuid;
+using ::base::ASCIIToUTF16;
+using ::base::Bucket;
+using ::base::BucketsAre;
+using ::base::BucketsInclude;
+using ::base::TimeTicks;
using ::testing::ElementsAre;
using ::testing::HasSubstr;
+using ::testing::IsSupersetOf;
using ::testing::Matcher;
using ::testing::NiceMock;
using ::testing::UnorderedPointwise;
@@ -117,7 +126,6 @@ using AddressImportRequirements =
AutofillMetrics::AddressProfileImportRequirementMetric;
const int kDefaultPageID = 137;
-const char* kTestGuid = "00000000-0000-0000-0000-000000000001";
FormSignature Collapse(FormSignature sig) {
return FormSignature(sig.value() % 1021);
@@ -191,12 +199,14 @@ void VerifyUkm(const ukm::TestUkmRecorder* ukm_recorder,
}
}
-void VerifySubmitFormUkm(const ukm::TestUkmRecorder* ukm_recorder,
- const FormData& form,
- AutofillMetrics::AutofillFormSubmittedState state,
- bool is_for_credit_card,
- bool has_upi_vpa_field,
- const DenseSet<FormType>& form_types) {
+void VerifySubmitFormUkm(
+ const ukm::TestUkmRecorder* ukm_recorder,
+ const FormData& form,
+ AutofillMetrics::AutofillFormSubmittedState state,
+ bool is_for_credit_card,
+ bool has_upi_vpa_field,
+ const DenseSet<FormType>& form_types,
+ const FormInteractionCounts& form_interaction_counts = {}) {
VerifyUkm(ukm_recorder, form, UkmFormSubmittedType::kEntryName,
{{{UkmFormSubmittedType::kAutofillFormSubmittedStateName, state},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
@@ -205,7 +215,13 @@ void VerifySubmitFormUkm(const ukm::TestUkmRecorder* ukm_recorder,
{UkmFormSubmittedType::kFormTypesName,
AutofillMetrics::FormTypesToBitVector(form_types)},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}}});
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName,
+ form_interaction_counts.form_element_user_modifications},
+ {UkmFormSubmittedType::kAutofillFillsName,
+ form_interaction_counts.autofill_fills},
+ {UkmFormSubmittedType::kAutocompleteFillsName,
+ form_interaction_counts.autocomplete_fills}}});
}
void AppendFieldFillStatusUkm(const FormData& form,
@@ -263,14 +279,6 @@ void AppendFieldTypeUkm(const FormData& form,
}
}
-void SetProfileTestData(AutofillProfile* profile) {
- test::SetProfileInfo(profile, "Elvis", "Aaron", "Presley",
- "theking@gmail.com", "RCA", "3734 Elvis Presley Blvd.",
- "Apt. 10", "Memphis", "Tennessee", "38116", "US",
- "12345678901");
- profile->set_guid(kTestGuid);
-}
-
// For a single submission, test if the right bucket was filled.
void TestAddressProfileImportRequirements(
base::HistogramTester* histogram_tester,
@@ -323,36 +331,6 @@ std::string SerializeAndEncode(const AutofillQueryResponse& response) {
return response_string;
}
-class MockAutofillClient : public TestAutofillClient {
- public:
- MockAutofillClient() {}
- MOCK_METHOD(void, ExecuteCommand, (int), (override));
-};
-
-template <typename T>
-struct HistogramBucketExpectation {
- T bucket;
- size_t count;
-};
-
-// Checks that the given buckets have the given counts.
-// Additionally checks that the overall count is `total_count`, which defaults
-// to the sum of `expectations` counts.
-template <typename T>
-void ExpectBuckets(const base::HistogramTester& histogram_tester,
- base::StringPiece metric,
- std::vector<HistogramBucketExpectation<T>> expectations,
- absl::optional<size_t> total_size = absl::nullopt) {
- if (!total_size) {
- total_size = 0;
- for (const auto& e : expectations)
- *total_size += e.count;
- }
- histogram_tester.ExpectTotalCount(metric, *total_size);
- for (const auto& e : expectations)
- histogram_tester.ExpectBucketCount(metric, e.bucket, e.count);
-}
-
} // namespace
// This is defined in the autofill_metrics.cc implementation file.
@@ -360,349 +338,25 @@ int GetFieldTypeGroupPredictionQualityMetric(
ServerFieldType field_type,
AutofillMetrics::FieldTypeQualityMetric metric);
-class AutofillMetricsTest : public testing::Test {
+class AutofillMetricsTest : public metrics::AutofillMetricsBaseTest {
public:
- AutofillMetricsTest();
- ~AutofillMetricsTest() override;
-
- void SetUp() override;
- void TearDown() override;
-
- protected:
- void CreateAmbiguousProfiles();
-
- // Removes all existing profiles and creates one profile.
- // |is_server| allows creation of |SERVER_PROFILE|.
- void RecreateProfile(bool is_server);
-
- // Removes all existing credit cards and creates a local, masked server,
- // full server, and/or virtual credit card, according to the parameters.
- // TODO(crbug/1216615): Migrate this to a params builder pattern or something.
- void RecreateCreditCards(bool include_local_credit_card,
- bool include_masked_server_credit_card,
- bool include_full_server_credit_card,
- bool masked_card_is_enrolled_for_virtual_card);
-
- void AddMaskedServerCreditCardWithOffer(std::string guid,
- std::string offer_reward_amount,
- GURL url,
- int64_t id,
- bool offer_expired = false);
-
- // If set to true, then user is capable of using FIDO authentication for card
- // unmasking.
- void SetFidoEligibility(bool is_verifiable);
-
- // Mocks a RPC response from Payments.
- void OnDidGetRealPan(AutofillClient::PaymentsRpcResult result,
- const std::string& real_pan,
- bool is_virtual_card = false);
-
- // Mocks a RPC response from Payments, but where a non-HTTP_OK response
- // stopped it from parsing a valid response.
- void OnDidGetRealPanWithNonHttpOkResponse();
-
- // Purge recorded UKM metrics for running more tests.
- void PurgeUKM();
-
- // Mocks a credit card fetching was completed. This mock starts from the
- // BrowserAutofillManager. Use these if your test does not depends on
- // OnDidGetRealPan but just need to mock the card fetching result (so that
- // you don't need to branch on what auth method was used).
- void OnCreditCardFetchingSuccessful(const std::u16string& real_pan,
- bool is_virtual_card = false);
- void OnCreditCardFetchingFailed();
-
- base::test::TaskEnvironment task_environment_;
- MockAutofillClient autofill_client_;
- raw_ptr<ukm::TestUkmRecorder> test_ukm_recorder_;
- syncer::TestSyncService sync_service_;
- std::unique_ptr<TestAutofillDriver> autofill_driver_;
- std::unique_ptr<TestBrowserAutofillManager> browser_autofill_manager_;
- std::unique_ptr<TestPersonalDataManager> personal_data_;
- raw_ptr<AutofillExternalDelegate> external_delegate_;
- base::test::ScopedFeatureList scoped_feature_list_;
-
- private:
- void CreateTestAutofillProfiles();
-
- CreditCard credit_card_ = test::GetMaskedServerCard();
+ AutofillMetricsTest() = default;
+ ~AutofillMetricsTest() override = default;
};
-AutofillMetricsTest::AutofillMetricsTest() {
- autofill_driver_ = std::make_unique<TestAutofillDriver>();
- test_ukm_recorder_ = autofill_client_.GetTestUkmRecorder();
-}
-
-AutofillMetricsTest::~AutofillMetricsTest() {
- // Order of destruction is important as BrowserAutofillManager relies on
- // PersonalDataManager to be around when it gets destroyed.
- browser_autofill_manager_.reset();
-}
-
-void AutofillMetricsTest::SetUp() {
- autofill_client_.SetPrefs(test::PrefServiceForTesting());
-
- personal_data_ = std::make_unique<TestPersonalDataManager>();
- personal_data_->set_auto_accept_address_imports_for_testing(true);
- personal_data_->SetPrefService(autofill_client_.GetPrefs());
- personal_data_->OnSyncServiceInitialized(&sync_service_);
-
- payments::TestPaymentsClient* payments_client =
- new payments::TestPaymentsClient(autofill_driver_->GetURLLoaderFactory(),
- autofill_client_.GetIdentityManager(),
- personal_data_.get());
- autofill_client_.set_test_payments_client(
- std::unique_ptr<payments::TestPaymentsClient>(payments_client));
- TestCreditCardSaveManager* credit_card_save_manager =
- new TestCreditCardSaveManager(autofill_driver_.get(), &autofill_client_,
- payments_client, personal_data_.get());
- autofill::TestFormDataImporter* test_form_data_importer =
- new TestFormDataImporter(
- &autofill_client_, payments_client,
- std::unique_ptr<CreditCardSaveManager>(credit_card_save_manager),
- personal_data_.get(), "en-US");
- autofill_client_.set_test_form_data_importer(
- std::unique_ptr<TestFormDataImporter>(test_form_data_importer));
- autofill_client_.set_autofill_offer_manager(
- std::make_unique<AutofillOfferManager>(
- personal_data_.get(), /*coupon_service_delegate=*/nullptr));
-
- browser_autofill_manager_ = std::make_unique<TestBrowserAutofillManager>(
- autofill_driver_.get(), &autofill_client_, personal_data_.get());
- auto external_delegate = std::make_unique<AutofillExternalDelegate>(
- browser_autofill_manager_.get(), autofill_driver_.get());
- external_delegate_ = external_delegate.get();
- browser_autofill_manager_->SetExternalDelegateForTest(
- std::move(external_delegate));
-
-#if !BUILDFLAG(IS_IOS)
- browser_autofill_manager_->credit_card_access_manager()
- ->set_fido_authenticator_for_testing(
- std::make_unique<TestCreditCardFIDOAuthenticator>(
- autofill_driver_.get(), &autofill_client_));
-#endif
-
- // Initialize the TestPersonalDataManager with some default data.
- CreateTestAutofillProfiles();
-}
-
-void AutofillMetricsTest::TearDown() {
- // Order of destruction is important as BrowserAutofillManager and
- // AutofillOfferManager rely on PersonalDataManager to be around when they
- // gets destroyed.
- browser_autofill_manager_.reset();
- autofill_driver_.reset();
- autofill_client_.set_autofill_offer_manager(nullptr);
- personal_data_.reset();
- test_ukm_recorder_->Purge();
-}
-
-void AutofillMetricsTest::PurgeUKM() {
- browser_autofill_manager_->Reset();
- test_ukm_recorder_->Purge();
- autofill_client_.InitializeUKMSources();
-}
-
-void AutofillMetricsTest::CreateAmbiguousProfiles() {
- personal_data_->ClearProfiles();
- CreateTestAutofillProfiles();
-
- AutofillProfile profile;
- test::SetProfileInfo(&profile, "John", "Decca", "Public", "john@gmail.com",
- "Company", "123 Main St.", "unit 7", "Springfield",
- "Texas", "79401", "US", "2345678901");
- profile.set_guid("00000000-0000-0000-0000-000000000003");
- personal_data_->AddProfile(profile);
- personal_data_->Refresh();
-}
-
-void AutofillMetricsTest::RecreateProfile(bool is_server) {
- personal_data_->ClearProfiles();
-
- if (is_server) {
- AutofillProfile profile(AutofillProfile::SERVER_PROFILE, "server_id");
- SetProfileTestData(&profile);
- personal_data_->AddProfile(profile);
- } else {
- AutofillProfile profile;
- SetProfileTestData(&profile);
- personal_data_->AddProfile(profile);
- }
-
- personal_data_->Refresh();
-}
-
-void AutofillMetricsTest::SetFidoEligibility(bool is_verifiable) {
- CreditCardAccessManager* access_manager =
- browser_autofill_manager_->credit_card_access_manager();
-#if !BUILDFLAG(IS_IOS)
- static_cast<TestCreditCardFIDOAuthenticator*>(
- access_manager->GetOrCreateFIDOAuthenticator())
- ->SetUserVerifiable(is_verifiable);
-#endif
- static_cast<payments::TestPaymentsClient*>(
- autofill_client_.GetPaymentsClient())
- ->AllowFidoRegistration(true);
- access_manager->is_authentication_in_progress_ = false;
- access_manager->can_fetch_unmask_details_ = true;
- access_manager->is_user_verifiable_ = absl::nullopt;
-}
-
-void AutofillMetricsTest::OnDidGetRealPan(
- AutofillClient::PaymentsRpcResult result,
- const std::string& real_pan,
- bool is_virtual_card) {
- payments::FullCardRequest* full_card_request =
- browser_autofill_manager_->credit_card_access_manager_
- ->GetOrCreateCVCAuthenticator()
- ->full_card_request_.get();
- DCHECK(full_card_request);
-
- // Fake user response.
- payments::FullCardRequest::UserProvidedUnmaskDetails details;
- details.cvc = u"123";
- full_card_request->OnUnmaskPromptAccepted(details);
-
- payments::PaymentsClient::UnmaskResponseDetails response;
- response.card_type = is_virtual_card
- ? AutofillClient::PaymentsRpcCardType::kVirtualCard
- : AutofillClient::PaymentsRpcCardType::kServerCard;
- full_card_request->OnDidGetRealPan(result, response.with_real_pan(real_pan));
-}
-
-void AutofillMetricsTest::OnDidGetRealPanWithNonHttpOkResponse() {
- payments::FullCardRequest* full_card_request =
- browser_autofill_manager_->credit_card_access_manager_
- ->GetOrCreateCVCAuthenticator()
- ->full_card_request_.get();
- DCHECK(full_card_request);
-
- // Fake user response.
- payments::FullCardRequest::UserProvidedUnmaskDetails details;
- details.cvc = u"123";
- full_card_request->OnUnmaskPromptAccepted(details);
-
- payments::PaymentsClient::UnmaskResponseDetails response;
- // Don't set |response.card_type|, so that it stays as kUnknown.
- full_card_request->OnDidGetRealPan(
- AutofillClient::PaymentsRpcResult::kPermanentFailure, response);
-}
-
-void AutofillMetricsTest::OnCreditCardFetchingSuccessful(
- const std::u16string& real_pan,
- bool is_virtual_card) {
- credit_card_.set_record_type(
- is_virtual_card ? CreditCard::RecordType::VIRTUAL_CARD
- : CreditCard::RecordType::MASKED_SERVER_CARD);
- credit_card_.SetNumber(real_pan);
-
- browser_autofill_manager_->OnCreditCardFetched(
- CreditCardFetchResult::kSuccess, &credit_card_, u"123");
-}
-
-void AutofillMetricsTest::OnCreditCardFetchingFailed() {
- browser_autofill_manager_->OnCreditCardFetched(
- CreditCardFetchResult::kPermanentError, nullptr, u"");
-}
-
-void AutofillMetricsTest::RecreateCreditCards(
- bool include_local_credit_card,
- bool include_masked_server_credit_card,
- bool include_full_server_credit_card,
- bool masked_card_is_enrolled_for_virtual_card) {
- personal_data_->ClearCreditCards();
- if (include_local_credit_card) {
- CreditCard local_credit_card;
- test::SetCreditCardInfo(&local_credit_card, "Test User",
- "4111111111111111" /* Visa */, "11", "2022", "1");
- local_credit_card.set_guid("10000000-0000-0000-0000-000000000001");
- personal_data_->AddCreditCard(local_credit_card);
- }
- if (include_masked_server_credit_card) {
- CreditCard masked_server_credit_card(CreditCard::MASKED_SERVER_CARD,
- "server_id_1");
- masked_server_credit_card.set_guid("10000000-0000-0000-0000-000000000002");
- masked_server_credit_card.set_instrument_id(1);
- masked_server_credit_card.SetNetworkForMaskedCard(kDiscoverCard);
- masked_server_credit_card.SetNumber(u"9424");
- if (masked_card_is_enrolled_for_virtual_card) {
- masked_server_credit_card.set_virtual_card_enrollment_state(
- CreditCard::ENROLLED);
- }
- personal_data_->AddServerCreditCard(masked_server_credit_card);
- }
- if (include_full_server_credit_card) {
- CreditCard full_server_credit_card(CreditCard::FULL_SERVER_CARD,
- "server_id_2");
- full_server_credit_card.set_guid("10000000-0000-0000-0000-000000000003");
- full_server_credit_card.set_instrument_id(2);
- personal_data_->AddFullServerCreditCard(full_server_credit_card);
- }
- personal_data_->Refresh();
-}
-
-void AutofillMetricsTest::AddMaskedServerCreditCardWithOffer(
- std::string guid,
- std::string offer_reward_amount,
- GURL url,
- int64_t id,
- bool offer_expired) {
- CreditCard masked_server_credit_card(CreditCard::MASKED_SERVER_CARD,
- "server_id_offer");
- masked_server_credit_card.set_guid(guid);
- masked_server_credit_card.set_instrument_id(id);
- masked_server_credit_card.SetNetworkForMaskedCard(kDiscoverCard);
- masked_server_credit_card.SetNumber(u"9424");
- personal_data_->AddServerCreditCard(masked_server_credit_card);
-
- AutofillOfferData offer_data;
- offer_data.offer_id = id;
- offer_data.offer_reward_amount = offer_reward_amount;
- if (offer_expired) {
- offer_data.expiry = AutofillClock::Now() - base::Days(2);
- } else {
- offer_data.expiry = AutofillClock::Now() + base::Days(2);
- }
- offer_data.merchant_origins = {url};
- offer_data.eligible_instrument_id = {
- masked_server_credit_card.instrument_id()};
- personal_data_->AddAutofillOfferData(offer_data);
- personal_data_->Refresh();
-}
-
-void AutofillMetricsTest::CreateTestAutofillProfiles() {
- AutofillProfile profile1;
- test::SetProfileInfo(&profile1, "Elvis", "Aaron", "Presley",
- "theking@gmail.com", "RCA", "3734 Elvis Presley Blvd.",
- "Apt. 10", "Memphis", "Tennessee", "38116", "US",
- "12345678901");
- profile1.set_guid(kTestGuid);
- personal_data_->AddProfile(profile1);
-
- AutofillProfile profile2;
- test::SetProfileInfo(&profile2, "Charles", "Hardin", "Holley",
- "buddy@gmail.com", "Decca", "123 Apple St.", "unit 6",
- "Lubbock", "Texas", "79401", "US", "2345678901");
- profile2.set_guid("00000000-0000-0000-0000-000000000002");
- personal_data_->AddProfile(profile2);
-}
-
// Test parameter indicates if the metrics are being logged for a form in an
// iframe or the main frame. True means the form is in the main frame.
-class AutofillMetricsIFrameTest : public AutofillMetricsTest,
- public testing::WithParamInterface<bool> {
+class AutofillMetricsIFrameTest : public testing::WithParamInterface<bool>,
+ public metrics::AutofillMetricsBaseTest {
public:
AutofillMetricsIFrameTest()
- : is_in_any_main_frame_(GetParam()),
+ : metrics::AutofillMetricsBaseTest(
+ /*is_in_any_main_frame=*/GetParam()),
credit_card_form_events_frame_histogram_(
std::string("Autofill.FormEvents.CreditCard.") +
- (is_in_any_main_frame_ ? "IsInMainFrame" : "IsInIFrame")) {
- autofill_driver_->SetIsInAnyMainFrame(is_in_any_main_frame_);
- }
+ (is_in_any_main_frame_ ? "IsInMainFrame" : "IsInIFrame")) {}
protected:
- const bool is_in_any_main_frame_;
const std::string credit_card_form_events_frame_histogram_;
};
@@ -742,7 +396,7 @@ TEST_F(AutofillMetricsTest, NumberOfAutofilledFieldsAtSubmission) {
.is_autofilled = true}},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {
NAME_FULL, PHONE_HOME_NUMBER, NAME_FULL,
@@ -752,17 +406,17 @@ TEST_F(AutofillMetricsTest, NumberOfAutofilledFieldsAtSubmission) {
EMAIL_ADDRESS, NO_SERVER_DATA, PHONE_HOME_CITY_AND_NUMBER};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate user changing the second field of the form.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(),
+ TimeTicks());
form.fields.at(1).is_autofilled = false;
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Test that the correct bucket for the number of filled fields received a
// count while the others remain at zero counts.
@@ -822,7 +476,7 @@ TEST_F(AutofillMetricsTest,
.is_autofilled = true}},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {
NAME_FULL, PHONE_HOME_NUMBER, NAME_FULL,
@@ -832,17 +486,17 @@ TEST_F(AutofillMetricsTest,
EMAIL_ADDRESS, NO_SERVER_DATA, PHONE_HOME_CITY_AND_NUMBER};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate user changing the second and forth field of the form.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(),
+ TimeTicks());
form.fields.at(1).is_autofilled = false;
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Test that the correct bucket for the number of filled fields with an
// unrecognized autocomplete attriute received a count while the others remain
@@ -894,7 +548,7 @@ TEST_F(AutofillMetricsTest, PerfectFillingForAddresses_AllAutofillFilled) {
},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {NAME_FULL, EMAIL_ADDRESS,
ADDRESS_HOME_CITY};
@@ -902,22 +556,21 @@ TEST_F(AutofillMetricsTest, PerfectFillingForAddresses_AllAutofillFilled) {
ADDRESS_HOME_CITY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Here, it is expected that there is a count for perfect filling for
// addresses.
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 0, 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 1, 1);
-
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 0,
- 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 1,
- 0);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 0)));
}
// Test that we log the perfect filling metric correctly for an address form in
@@ -944,7 +597,7 @@ TEST_F(AutofillMetricsTest,
},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {NAME_FULL, EMAIL_ADDRESS,
ADDRESS_HOME_CITY};
@@ -952,22 +605,21 @@ TEST_F(AutofillMetricsTest,
ADDRESS_HOME_CITY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Here, it is expected that there is a count for perfect filling for
// addresses.
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 0, 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 1, 1);
-
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 0,
- 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 1,
- 0);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 0)));
}
// Test that we log the perfect filling metric correctly for an address form in
@@ -993,7 +645,7 @@ TEST_F(AutofillMetricsTest, PerfectFillingForAddresses_NotAllAutofilled) {
},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {NAME_FULL, EMAIL_ADDRESS,
ADDRESS_HOME_CITY};
@@ -1001,22 +653,21 @@ TEST_F(AutofillMetricsTest, PerfectFillingForAddresses_NotAllAutofilled) {
ADDRESS_HOME_CITY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Here, it is expected that there is a count for non-perfect filling for
// addresses.
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 0, 1);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 1, 0);
-
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 0,
- 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 1,
- 0);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"),
+ BucketsAre(Bucket(false, 1), Bucket(true, 0)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 0)));
}
// Test that we log the perfect filling metric correctly for a credit card form
@@ -1038,7 +689,7 @@ TEST_F(AutofillMetricsTest, PerfectFillingForCreditCards_AllAutofilled) {
},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {CREDIT_CARD_NAME_FULL,
CREDIT_CARD_NUMBER};
@@ -1046,22 +697,21 @@ TEST_F(AutofillMetricsTest, PerfectFillingForCreditCards_AllAutofilled) {
CREDIT_CARD_NUMBER};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Here, it is expected that there is a count for perfect filling for credit
// cards.
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 0, 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 1, 0);
-
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 0,
- 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 1,
- 1);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 0)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 1)));
}
// Test that we log the perfect filling metric correctly for a credit card form
@@ -1083,7 +733,7 @@ TEST_F(AutofillMetricsTest, PerfectFillingForCreditCards_NotAllAutofilled) {
},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {CREDIT_CARD_NAME_FULL,
CREDIT_CARD_NUMBER};
@@ -1091,22 +741,21 @@ TEST_F(AutofillMetricsTest, PerfectFillingForCreditCards_NotAllAutofilled) {
CREDIT_CARD_NUMBER};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Here, it is expected that there is a count for non-perfect filling for
// credit cards.
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 0, 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 1, 0);
-
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 0,
- 1);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 1,
- 0);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 0)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"),
+ BucketsAre(Bucket(false, 1), Bucket(true, 0)));
}
// Test that we log the perfect filling metric correctly for a form that
@@ -1129,29 +778,28 @@ TEST_F(AutofillMetricsTest, PerfectFillingForMixedForm_AllAutofilled) {
},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {NAME_FULL,
CREDIT_CARD_NUMBER};
std::vector<ServerFieldType> server_types = {NAME_FULL, CREDIT_CARD_NUMBER};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Here, it is expected that there is a count for perfect filling for credit
// cards and for addresses.
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 0, 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 1, 1);
-
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 0,
- 0);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 1,
- 1);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"),
+ BucketsAre(Bucket(false, 0), Bucket(true, 1)));
}
// Test that we log the perfect filling metric correctly for a form that
@@ -1175,29 +823,28 @@ TEST_F(AutofillMetricsTest, PerfectFillingForMixedForm_NotAllAutofilled) {
},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {NAME_FULL,
CREDIT_CARD_NUMBER};
std::vector<ServerFieldType> server_types = {NAME_FULL, CREDIT_CARD_NUMBER};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Here, it is expected that there is a count for non-perfect filling for
// credit cards and for addresses.
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 0, 1);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.Addresses", 1, 0);
-
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 0,
- 1);
- histogram_tester.ExpectBucketCount("Autofill.PerfectFilling.CreditCards", 1,
- 0);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"),
+ BucketsAre(Bucket(false, 1), Bucket(true, 0)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"),
+ BucketsAre(Bucket(false, 1), Bucket(true, 0)));
}
// Test that we log quality metrics appropriately.
@@ -1232,7 +879,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics) {
.is_autofilled = true}},
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
- autofill_client_.form_origin())});
+ autofill_client_->form_origin())});
std::vector<ServerFieldType> heuristic_types = {
NAME_FULL, PHONE_HOME_NUMBER, NAME_FULL,
@@ -1242,141 +889,72 @@ TEST_F(AutofillMetricsTest, QualityMetrics) {
EMAIL_ADDRESS, NO_SERVER_DATA, PHONE_HOME_CITY_AND_NUMBER};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+
+ // Auxiliary function for GetAllSamples() expectations.
+ auto b = [](ServerFieldType field_type,
+ AutofillMetrics::FieldTypeQualityMetric metric,
+ base::HistogramBase::Count count) {
+ return Bucket(GetFieldTypeGroupPredictionQualityMetric(field_type, metric),
+ count);
+ };
// Heuristic predictions.
- {
- std::string aggregate_histogram =
- "Autofill.FieldPredictionQuality.Aggregate.Heuristic";
- std::string by_field_type_histogram =
- "Autofill.FieldPredictionQuality.ByFieldType.Heuristic";
-
- // Unknown:
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
- 1);
- // Match:
- histogram_tester.ExpectBucketCount(aggregate_histogram,
- AutofillMetrics::TRUE_POSITIVE, 2);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- NAME_FULL, AutofillMetrics::TRUE_POSITIVE),
- 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- PHONE_HOME_CITY_AND_NUMBER, AutofillMetrics::TRUE_POSITIVE),
- 1);
- // Mismatch:
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- EMAIL_ADDRESS, AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
- 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_MISMATCH),
- 1);
- // False Positive Unknown:
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_UNKNOWN),
- 1);
- // False Positive Empty:
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- NAME_FULL, AutofillMetrics::FALSE_POSITIVE_EMPTY),
- 1);
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.Aggregate.Heuristic"),
+ BucketsAre(Bucket(AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1),
+ Bucket(AutofillMetrics::TRUE_POSITIVE, 2),
+ Bucket(AutofillMetrics::FALSE_POSITIVE_EMPTY, 1),
+ Bucket(AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1),
+ Bucket(AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.ByFieldType.Heuristic"),
+ BucketsAre(
+ b(ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1),
+ b(NAME_FULL, AutofillMetrics::TRUE_POSITIVE, 1),
+ b(PHONE_HOME_CITY_AND_NUMBER, AutofillMetrics::TRUE_POSITIVE, 1),
+ b(EMAIL_ADDRESS, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1),
+ b(PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_MISMATCH, 1),
+ b(PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1),
+ b(NAME_FULL, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1)));
+
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.Aggregate.Server"),
+ BucketsAre(Bucket(AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1),
+ Bucket(AutofillMetrics::TRUE_POSITIVE, 2),
+ Bucket(AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1),
+ Bucket(AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1),
+ Bucket(AutofillMetrics::FALSE_POSITIVE_EMPTY, 1)));
- // Sanity Check:
- histogram_tester.ExpectTotalCount(aggregate_histogram, 6);
- histogram_tester.ExpectTotalCount(by_field_type_histogram, 7);
- }
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.ByFieldType.Server"),
+ BucketsAre(
+ b(ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1),
+ b(EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE, 1),
+ b(PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::TRUE_POSITIVE, 1),
+ b(NAME_FULL, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1),
+ b(NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_MISMATCH, 1),
+ b(EMAIL_ADDRESS, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1),
+ b(NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1)));
// Server overrides heuristic so Overall and Server are the same predictions
// (as there were no test fields where server == NO_SERVER_DATA and heuristic
// != UNKNOWN_TYPE).
- for (const std::string source : {"Server", "Overall"}) {
- std::string aggregate_histogram =
- "Autofill.FieldPredictionQuality.Aggregate." + source;
- std::string by_field_type_histogram =
- "Autofill.FieldPredictionQuality.ByFieldType." + source;
-
- // Unknown:
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
- 1);
- // Match:
- histogram_tester.ExpectBucketCount(aggregate_histogram,
- AutofillMetrics::TRUE_POSITIVE, 2);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE),
- 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::TRUE_POSITIVE),
- 1);
- // Mismatch:
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- NAME_FULL, AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
- 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_MISMATCH),
- 1);
-
- // False Positive Unknown:
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- EMAIL_ADDRESS, AutofillMetrics::FALSE_POSITIVE_UNKNOWN),
- 1);
- // False Positive Empty:
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_EMPTY),
- 1);
-
- // Sanity Check:
- histogram_tester.ExpectTotalCount(aggregate_histogram, 6);
- histogram_tester.ExpectTotalCount(by_field_type_histogram, 7);
- }
+ EXPECT_EQ(histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.Aggregate.Server"),
+ histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.Aggregate.Overall"));
+ EXPECT_EQ(histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.ByFieldType.Server"),
+ histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.ByFieldType.Overall"));
}
// Test that the ProfileImportStatus logs a no import.
@@ -1410,28 +988,24 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_NoImport) {
ADDRESS_HOME_COUNTRY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
- std::string histogram = "Autofill.AddressProfileImportStatus";
- histogram_tester.ExpectBucketCount(
- histogram,
- AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT, 0);
- histogram_tester.ExpectBucketCount(
- histogram, AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT,
- 1);
- histogram_tester.ExpectBucketCount(
- histogram,
- AutofillMetrics::AddressProfileImportStatusMetric::SECTION_UNION_IMPORT,
- 0);
+ using Metric = AutofillMetrics::AddressProfileImportStatusMetric;
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.AddressProfileImportStatus"),
+ BucketsAre(Bucket(Metric::REGULAR_IMPORT, 0),
+ Bucket(Metric::NO_IMPORT, 1),
+ Bucket(Metric::SECTION_UNION_IMPORT, 0)));
}
// Test that the ProfileImportStatus logs a regular import.
@@ -1462,28 +1036,24 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_RegularImport) {
ADDRESS_HOME_COUNTRY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
- std::string histogram = "Autofill.AddressProfileImportStatus";
- histogram_tester.ExpectBucketCount(
- histogram,
- AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT, 1);
- histogram_tester.ExpectBucketCount(
- histogram, AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT,
- 0);
- histogram_tester.ExpectBucketCount(
- histogram,
- AutofillMetrics::AddressProfileImportStatusMetric::SECTION_UNION_IMPORT,
- 0);
+ using Metric = AutofillMetrics::AddressProfileImportStatusMetric;
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.AddressProfileImportStatus"),
+ BucketsAre(Bucket(Metric::REGULAR_IMPORT, 1),
+ Bucket(Metric::NO_IMPORT, 0),
+ Bucket(Metric::SECTION_UNION_IMPORT, 0)));
}
// Test that the ProfileImportStatus logs a section union mport.
@@ -1526,29 +1096,24 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_UnionImport) {
ADDRESS_HOME_STATE};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
base::HistogramTester histogram_tester;
- std::string histogram = "Autofill.AddressProfileImportStatus";
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
- // Verify that one profile was imported using the union of the two sections.
- histogram_tester.ExpectBucketCount(
- histogram,
- AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT, 0);
- histogram_tester.ExpectBucketCount(
- histogram, AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT,
- 0);
- histogram_tester.ExpectBucketCount(
- histogram,
- AutofillMetrics::AddressProfileImportStatusMetric::SECTION_UNION_IMPORT,
- 1);
+ using Metric = AutofillMetrics::AddressProfileImportStatusMetric;
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.AddressProfileImportStatus"),
+ BucketsAre(Bucket(Metric::REGULAR_IMPORT, 0),
+ Bucket(Metric::NO_IMPORT, 0),
+ Bucket(Metric::SECTION_UNION_IMPORT, 1)));
}
// Test that the ProfileImportRequirements are all counted as fulfilled for a
@@ -1580,16 +1145,17 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_AllFulfilled) {
ADDRESS_HOME_COUNTRY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
std::vector<AddressProfileImportRequirementExpectations> expectations = {
{AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true},
@@ -1660,16 +1226,17 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_MissingHomeLineOne) {
ADDRESS_HOME_COUNTRY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
std::vector<AddressProfileImportRequirementExpectations> expectations = {
{AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true},
@@ -1744,16 +1311,17 @@ TEST_F(AutofillMetricsTest,
ADDRESS_HOME_COUNTRY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
std::vector<AddressProfileImportRequirementExpectations> expectations = {
{AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true},
@@ -1834,16 +1402,17 @@ TEST_F(AutofillMetricsTest,
EMAIL_ADDRESS};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
std::vector<AddressProfileImportRequirementExpectations> expectations = {
{AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, false},
@@ -1927,16 +1496,17 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_NonUniqueEmail) {
EMAIL_ADDRESS};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
std::vector<AddressProfileImportRequirementExpectations> expectations = {
{AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true},
@@ -2008,16 +1578,17 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_OnlyAddressLineOne) {
ADDRESS_HOME_COUNTRY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
std::vector<AddressProfileImportRequirementExpectations> expectations = {
{AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true},
@@ -2118,19 +1689,20 @@ TEST_F(AutofillMetricsTest,
base::UserActionTester user_action_tester;
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
// Trigger phone number rationalization at filling time.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
EXPECT_EQ(
1, user_action_tester.GetActionCount("Autofill_FilledProfileSuggestion"));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Rationalization quality.
{
@@ -2185,19 +1757,20 @@ TEST_F(AutofillMetricsTest,
base::UserActionTester user_action_tester;
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
// Trigger phone number rationalization at filling time.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
EXPECT_EQ(
1, user_action_tester.GetActionCount("Autofill_FilledProfileSuggestion"));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Rationalization quality.
{
@@ -2222,7 +1795,7 @@ TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -2264,16 +1837,17 @@ TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) {
FormSignature form_signature = Collapse(CalculateFormSignature(form));
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate filling form.
{
base::UserActionTester user_action_tester;
std::string guid(kTestGuid); // local profile.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
}
VerifyUkm(
@@ -2359,8 +1933,8 @@ TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) {
namespace {
void AddFieldSuggestionToForm(
- ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion,
- autofill::FormFieldData field_data,
+ AutofillQueryResponse_FormSuggestion* form_suggestion,
+ FormFieldData field_data,
ServerFieldType field_type) {
auto* field_suggestion = form_suggestion->add_field_suggestions();
field_suggestion->set_field_signature(
@@ -2378,7 +1952,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FieldSignature field_signature[2];
@@ -2410,8 +1984,8 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) {
field_types.push_back(UNKNOWN_TYPE);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
AutofillQueryResponse response;
auto* form_suggestion = response.add_form_suggestions();
@@ -2424,7 +1998,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) {
std::string response_string = SerializeAndEncode(response);
FormStructure::ParseApiQueryResponse(
response_string, forms, test::GetEncodedSignatures(forms),
- browser_autofill_manager_->form_interactions_ukm_logger(), nullptr);
+ autofill_manager().form_interactions_ukm_logger(), nullptr);
ASSERT_EQ(test_ukm_recorder_
->GetEntriesByName(
@@ -2486,7 +2060,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FieldSignature field_signature[3];
@@ -2527,8 +2101,8 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) {
field_types.push_back(UNKNOWN_TYPE);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
AutofillQueryResponse response;
auto* form_suggestion = response.add_form_suggestions();
@@ -2543,7 +2117,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) {
std::string response_string = SerializeAndEncode(response);
FormStructure::ParseApiQueryResponse(
response_string, forms, test::GetEncodedSignatures(forms),
- browser_autofill_manager_->form_interactions_ukm_logger(), nullptr);
+ autofill_manager().form_interactions_ukm_logger(), nullptr);
ASSERT_EQ(test_ukm_recorder_
->GetEntriesByName(
@@ -2660,19 +2234,20 @@ TEST_F(AutofillMetricsTest,
base::UserActionTester user_action_tester;
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
// Trigger phone number rationalization at filling time.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
EXPECT_EQ(
1, user_action_tester.GetActionCount("Autofill_FilledProfileSuggestion"));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Rationalization quality.
{
@@ -2743,116 +2318,77 @@ TEST_F(AutofillMetricsTest,
base::UserActionTester user_action_tester;
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
std::string guid(kTestGuid);
// Trigger phone number rationalization at filling time.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
EXPECT_EQ(
1, user_action_tester.GetActionCount("Autofill_FilledProfileSuggestion"));
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+
+ // Auxiliary function for GetAllSamples() expectations.
+ auto b = [](ServerFieldType field_type,
+ AutofillMetrics::FieldTypeQualityMetric metric,
+ base::HistogramBase::Count count) {
+ return Bucket(GetFieldTypeGroupPredictionQualityMetric(field_type, metric),
+ count);
+ };
// Rationalization quality.
- {
- std::string rationalization_histogram =
- "Autofill.RationalizationQuality.PhoneNumber";
- histogram_tester.ExpectBucketCount(
- rationalization_histogram, AutofillMetrics::RATIONALIZATION_GOOD, 1);
- histogram_tester.ExpectBucketCount(rationalization_histogram,
- AutofillMetrics::RATIONALIZATION_OK, 1);
- histogram_tester.ExpectBucketCount(rationalization_histogram,
- AutofillMetrics::RATIONALIZATION_BAD, 1);
- }
-
- // Heuristic predictions.
- {
- std::string aggregate_histogram =
- "Autofill.FieldPredictionQuality.Aggregate.Heuristic";
- std::string by_field_type_histogram =
- "Autofill.FieldPredictionQuality.ByFieldType.Heuristic";
-
- // TRUE_POSITIVE:
- histogram_tester.ExpectBucketCount(aggregate_histogram,
- AutofillMetrics::TRUE_POSITIVE, 4);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- NAME_FULL, AutofillMetrics::TRUE_POSITIVE),
- 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- ADDRESS_HOME_LINE1, AutofillMetrics::TRUE_POSITIVE),
- 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- PHONE_HOME_CITY_AND_NUMBER, AutofillMetrics::TRUE_POSITIVE),
- 2);
- // TRUE_NEGATIVE_EMPTY
- histogram_tester.ExpectBucketCount(aggregate_histogram,
- AutofillMetrics::TRUE_NEGATIVE_EMPTY, 1);
- // FALSE_NEGATIVE_MISMATCH
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
- 1);
- // Sanity Check:
- histogram_tester.ExpectTotalCount(aggregate_histogram, 6);
- histogram_tester.ExpectTotalCount(by_field_type_histogram, 5);
- }
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Autofill.RationalizationQuality.PhoneNumber"),
+ BucketsAre(Bucket(AutofillMetrics::RATIONALIZATION_GOOD, 1),
+ Bucket(AutofillMetrics::RATIONALIZATION_OK, 1),
+ Bucket(AutofillMetrics::RATIONALIZATION_BAD, 1)));
+
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.Aggregate.Heuristic"),
+ BucketsAre(Bucket(AutofillMetrics::TRUE_POSITIVE, 4),
+ Bucket(AutofillMetrics::TRUE_NEGATIVE_EMPTY, 1),
+ Bucket(AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.ByFieldType.Heuristic"),
+ BucketsAre(
+ b(NAME_FULL, AutofillMetrics::TRUE_POSITIVE, 1),
+ b(ADDRESS_HOME_LINE1, AutofillMetrics::TRUE_POSITIVE, 1),
+ b(PHONE_HOME_CITY_AND_NUMBER, AutofillMetrics::TRUE_POSITIVE, 2),
+ b(PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::FALSE_NEGATIVE_MISMATCH,
+ 1)));
+
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.Aggregate.Server"),
+ BucketsAre(Bucket(AutofillMetrics::TRUE_POSITIVE, 4),
+ Bucket(AutofillMetrics::TRUE_NEGATIVE_EMPTY, 1),
+ Bucket(AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.ByFieldType.Server"),
+ BucketsAre(
+ b(NAME_FULL, AutofillMetrics::TRUE_POSITIVE, 1),
+ b(ADDRESS_HOME_LINE1, AutofillMetrics::TRUE_POSITIVE, 1),
+ b(PHONE_HOME_CITY_AND_NUMBER, AutofillMetrics::TRUE_POSITIVE, 2),
+ b(PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::FALSE_NEGATIVE_MISMATCH,
+ 1)));
// Server overrides heuristic so Overall and Server are the same predictions
// (as there were no test fields where server == NO_SERVER_DATA and heuristic
// != UNKNOWN_TYPE).
- for (const std::string source : {"Server", "Overall"}) {
- std::string aggregate_histogram =
- "Autofill.FieldPredictionQuality.Aggregate." + source;
- std::string by_field_type_histogram =
- "Autofill.FieldPredictionQuality.ByFieldType." + source;
-
- // TRUE_POSITIVE:
- histogram_tester.ExpectBucketCount(aggregate_histogram,
- AutofillMetrics::TRUE_POSITIVE, 4);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- NAME_FULL, AutofillMetrics::TRUE_POSITIVE),
- 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- ADDRESS_HOME_LINE1, AutofillMetrics::TRUE_POSITIVE),
- 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- PHONE_HOME_CITY_AND_NUMBER, AutofillMetrics::TRUE_POSITIVE),
- 2);
- // TRUE_NEGATIVE_EMPTY
- histogram_tester.ExpectBucketCount(aggregate_histogram,
- AutofillMetrics::TRUE_NEGATIVE_EMPTY, 1);
- // FALSE_NEGATIVE_MISMATCHFALSE_NEGATIVE_MATCH
- histogram_tester.ExpectBucketCount(
- aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1);
- histogram_tester.ExpectBucketCount(
- by_field_type_histogram,
- GetFieldTypeGroupPredictionQualityMetric(
- PHONE_HOME_CITY_AND_NUMBER,
- AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
- 1);
- // Sanity Check:
- histogram_tester.ExpectTotalCount(aggregate_histogram, 6);
- histogram_tester.ExpectTotalCount(by_field_type_histogram, 5);
- }
+ EXPECT_EQ(histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.Aggregate.Server"),
+ histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.Aggregate.Overall"));
+ EXPECT_EQ(histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.FieldType.Server"),
+ histogram_tester.GetAllSamples(
+ "Autofill.FieldPredictionQuality.FieldType.Overall"));
}
// Tests the true negatives (empty + no prediction and unknown + no prediction)
@@ -3004,7 +2540,7 @@ TEST_P(QualityMetricsTest, Classification) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
std::vector<ServerFieldType> heuristic_types, server_types, actual_types;
AutofillField field;
@@ -3041,12 +2577,12 @@ TEST_P(QualityMetricsTest, Classification) {
actual_types.push_back(actual_field_type);
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Run the form submission code while tracking the histograms.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
ExpectedUkmMetrics expected_ukm_metrics;
AppendFieldTypeUkm(form, heuristic_types, server_types, actual_types,
@@ -3202,8 +2738,8 @@ TEST_F(AutofillMetricsTest, TimingMetrics) {
// Simulate a OnFormsSeen() call that should trigger the recording.
std::vector<FormData> forms;
forms.push_back(form);
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
// Because these metrics are related to timing, it is not possible to know in
// advance which bucket the sample will fall into, so we just need to make
@@ -3225,7 +2761,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
std::vector<ServerFieldType> heuristic_types, server_types;
FormFieldData field;
@@ -3269,16 +2805,16 @@ TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) {
server_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate text input on one of the fields.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[0],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(),
+ TimeTicks());
// Trigger a form upload and metrics by Resetting the manager.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
// Heuristic predictions.
{
@@ -3445,7 +2981,8 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) {
TestFormStructure* form_structure_ptr = form_structure.get();
form_structure->DetermineHeuristicTypes(nullptr, nullptr);
ASSERT_TRUE(
- browser_autofill_manager_->mutable_form_structures_for_test()
+ autofill_manager()
+ .mutable_form_structures_for_test()
->emplace(form_structure_ptr->global_id(), std::move(form_structure))
.second);
@@ -3462,7 +2999,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) {
std::string response_string = SerializeAndEncode(response);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnLoadedServerPredictionsForTest(
+ autofill_manager().OnLoadedServerPredictionsForTest(
response_string, test::GetEncodedSignatures(*form_structure_ptr));
// Verify that FormStructure::ParseApiQueryResponse was called (here and
@@ -3559,12 +3096,12 @@ TEST_F(AutofillMetricsTest, UpiVirtualPaymentAddress) {
server_types.push_back(ADDRESS_HOME_LINE1);
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.UserHappiness", AutofillMetrics::USER_DID_ENTER_UPI_VPA, 1);
@@ -3617,8 +3154,8 @@ TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) {
server_types.push_back(UNKNOWN_TYPE);
// Simulate having seen this form with the desired heuristic and server types.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Add a field and re-arrange the remaining form fields before submitting.
std::vector<FormFieldData> cached_fields = form.fields;
@@ -3633,8 +3170,8 @@ TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) {
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
for (const std::string source : {"Heuristic", "Server", "Overall"}) {
std::string aggregate_histogram =
@@ -3705,10 +3242,10 @@ TEST_F(AutofillMetricsTest, StoredProfileCountAutofillableFormSubmission) {
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// An autofillable form was submitted, and the number of stored profiles is
// logged.
@@ -3740,10 +3277,10 @@ TEST_F(AutofillMetricsTest, StoredProfileCountNonAutofillableFormSubmission) {
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// A non-autofillable form was submitted, and number of stored profiles is NOT
// logged.
@@ -3785,7 +3322,7 @@ TEST_F(AutofillMetricsTest, TypeOfEditedAutofilledFieldsUkmLogging) {
heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
server_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Verify that there are no counts before form submission.
@@ -3793,12 +3330,12 @@ TEST_F(AutofillMetricsTest, TypeOfEditedAutofilledFieldsUkmLogging) {
base::HistogramTester histogram_tester;
// Simulate text input in the first and second fields.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[0],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(),
+ TimeTicks());
// Simulate form submission.
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
ExpectedUkmMetricsRecord name_field_ukm_record{
{UkmEditedAutofilledFieldAtSubmission::kFieldSignatureName,
Collapse(CalculateFieldSignatureForField(form.fields[0])).value()},
@@ -3846,18 +3383,18 @@ TEST_F(AutofillMetricsTest, TypeOfEditedAutofilledFieldsUmaLogging) {
heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
server_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
base::HistogramTester histogram_tester;
// Simulate text input in the first and second fields.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[0],
- gfx::RectF(), TimeTicks());
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(),
+ TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(),
+ TimeTicks());
// Simulate form submission.
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// The |NAME_FULL| field was edited (bucket 112).
histogram_tester.ExpectBucketCount(
@@ -3926,18 +3463,18 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields) {
heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
server_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
base::HistogramTester histogram_tester;
// Simulate text input in the first and second fields.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[0],
- gfx::RectF(), TimeTicks());
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(),
+ TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(),
+ TimeTicks());
// Simulate form submission.
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// An autofillable form was submitted, and the number of edited autofilled
// fields is logged.
@@ -3955,7 +3492,7 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields_NoSubmission) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
std::vector<ServerFieldType> heuristic_types, server_types;
@@ -3981,15 +3518,15 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields_NoSubmission) {
heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
server_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
base::HistogramTester histogram_tester;
// Simulate text input in the first field.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[0],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(),
+ TimeTicks());
// We expect metrics to be logged when the manager is reset.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
// An autofillable form was uploaded, and the number of edited autofilled
// fields is logged.
@@ -4006,7 +3543,7 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -4020,9 +3557,9 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) {
// number of fields enforced).
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->Reset();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().Reset();
histogram_tester.ExpectTotalCount("Autofill.DeveloperEngagement", 0);
}
@@ -4033,9 +3570,9 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) {
// Expect the "form parsed without hints" metric to be logged.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->Reset();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().Reset();
histogram_tester.ExpectUniqueSample(
"Autofill.DeveloperEngagement",
AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS, 1);
@@ -4059,9 +3596,9 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) {
// Expect the "form parsed with field type hints" metric to be logged.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->Reset();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().Reset();
histogram_tester.ExpectBucketCount(
"Autofill.DeveloperEngagement",
AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, 1);
@@ -4080,9 +3617,9 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) {
// "author-specified upi-vpa type" metric to be logged.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->Reset();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().Reset();
histogram_tester.ExpectBucketCount(
"Autofill.DeveloperEngagement",
AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, 1);
@@ -4103,7 +3640,7 @@ TEST_F(AutofillMetricsTest,
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -4115,9 +3652,9 @@ TEST_F(AutofillMetricsTest,
// Ensure no entries are logged when loading a non-fillable form.
{
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->Reset();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().Reset();
EXPECT_EQ(0ul, test_ukm_recorder_->entries_count());
}
@@ -4129,9 +3666,9 @@ TEST_F(AutofillMetricsTest,
// Expect the "form parsed without field type hints" metric and the
// "form loaded" form interaction event to be logged.
{
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->Reset();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().Reset();
VerifyDeveloperEngagementUkm(
test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false,
@@ -4150,7 +3687,7 @@ TEST_F(AutofillMetricsTest,
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -4182,9 +3719,9 @@ TEST_F(AutofillMetricsTest,
// Expect the "form parsed without field type hints" metric and the
// "form loaded" form interaction event to be logged.
{
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->Reset();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().Reset();
VerifyDeveloperEngagementUkm(
test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false,
@@ -4202,7 +3739,7 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -4220,8 +3757,8 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) {
{
SCOPED_TRACE("VPA and other autocomplete hint present");
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
VerifyDeveloperEngagementUkm(
test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false,
@@ -4417,39 +3954,14 @@ TEST_F(AutofillMetricsTest, LogStoredCreditCardWithNicknameMetrics) {
"Autofill.StoredCreditCardCount.Server.Masked.WithNickname", 2, 1);
}
-TEST_F(AutofillMetricsTest, LogStoredOfferMetrics) {
- std::vector<std::unique_ptr<AutofillOfferData>> offers;
- AutofillOfferData offer1 = test::GetCardLinkedOfferData1();
- AutofillOfferData offer2 = test::GetCardLinkedOfferData2();
- offer2.eligible_instrument_id.emplace_back(999999);
- offer2.eligible_instrument_id.emplace_back(888888);
- offer2.merchant_origins.emplace_back("https://www.example3.com/");
- offers.push_back(std::make_unique<AutofillOfferData>(offer1));
- offers.push_back(std::make_unique<AutofillOfferData>(offer2));
-
- base::HistogramTester histogram_tester;
- AutofillMetrics::LogStoredOfferMetrics(offers);
-
- // Validate the count metrics.
- histogram_tester.ExpectBucketCount("Autofill.Offer.StoredOfferCount", 2, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.Offer.StoredOfferRelatedMerchantCount", 1, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.Offer.StoredOfferRelatedMerchantCount", 2, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.Offer.StoredOfferRelatedCardCount", 1, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.Offer.StoredOfferRelatedCardCount", 3, 1);
-}
-
// Test that we correctly log when Profile Autofill is enabled at startup.
TEST_F(AutofillMetricsTest, AutofillProfileIsEnabledAtStartup) {
base::HistogramTester histogram_tester;
- personal_data_->SetAutofillProfileEnabled(true);
- personal_data_->Init(scoped_refptr<AutofillWebDataService>(nullptr),
+ personal_data().SetAutofillProfileEnabled(true);
+ personal_data().Init(scoped_refptr<AutofillWebDataService>(nullptr),
/*account_database=*/nullptr,
- /*pref_service=*/autofill_client_.GetPrefs(),
- /*local_state=*/autofill_client_.GetPrefs(),
+ /*pref_service=*/autofill_client_->GetPrefs(),
+ /*local_state=*/autofill_client_->GetPrefs(),
/*identity_manager=*/nullptr,
/*history_service=*/nullptr,
/*strike_database=*/nullptr,
@@ -4462,11 +3974,11 @@ TEST_F(AutofillMetricsTest, AutofillProfileIsEnabledAtStartup) {
// Test that we correctly log when Profile Autofill is disabled at startup.
TEST_F(AutofillMetricsTest, AutofillProfileIsDisabledAtStartup) {
base::HistogramTester histogram_tester;
- personal_data_->SetAutofillProfileEnabled(false);
- personal_data_->Init(scoped_refptr<AutofillWebDataService>(nullptr),
+ personal_data().SetAutofillProfileEnabled(false);
+ personal_data().Init(scoped_refptr<AutofillWebDataService>(nullptr),
/*account_database=*/nullptr,
- /*pref_service=*/autofill_client_.GetPrefs(),
- /*local_state=*/autofill_client_.GetPrefs(),
+ /*pref_service=*/autofill_client_->GetPrefs(),
+ /*local_state=*/autofill_client_->GetPrefs(),
/*identity_manager=*/nullptr,
/*history_service=*/nullptr,
/*strike_database=*/nullptr,
@@ -4479,11 +3991,11 @@ TEST_F(AutofillMetricsTest, AutofillProfileIsDisabledAtStartup) {
// Test that we correctly log when CreditCard Autofill is enabled at startup.
TEST_F(AutofillMetricsTest, AutofillCreditCardIsEnabledAtStartup) {
base::HistogramTester histogram_tester;
- personal_data_->SetAutofillCreditCardEnabled(true);
- personal_data_->Init(scoped_refptr<AutofillWebDataService>(nullptr),
+ personal_data().SetAutofillCreditCardEnabled(true);
+ personal_data().Init(scoped_refptr<AutofillWebDataService>(nullptr),
/*account_database=*/nullptr,
- /*pref_service=*/autofill_client_.GetPrefs(),
- /*local_state=*/autofill_client_.GetPrefs(),
+ /*pref_service=*/autofill_client_->GetPrefs(),
+ /*local_state=*/autofill_client_->GetPrefs(),
/*identity_manager=*/nullptr,
/*history_service=*/nullptr,
/*strike_database=*/nullptr,
@@ -4496,11 +4008,11 @@ TEST_F(AutofillMetricsTest, AutofillCreditCardIsEnabledAtStartup) {
// Test that we correctly log when CreditCard Autofill is disabled at startup.
TEST_F(AutofillMetricsTest, AutofillCreditCardIsDisabledAtStartup) {
base::HistogramTester histogram_tester;
- personal_data_->SetAutofillCreditCardEnabled(false);
- personal_data_->Init(scoped_refptr<AutofillWebDataService>(nullptr),
+ personal_data().SetAutofillCreditCardEnabled(false);
+ personal_data().Init(scoped_refptr<AutofillWebDataService>(nullptr),
/*account_database=*/nullptr,
- /*pref_service=*/autofill_client_.GetPrefs(),
- /*local_state=*/autofill_client_.GetPrefs(),
+ /*pref_service=*/autofill_client_->GetPrefs(),
+ /*local_state=*/autofill_client_->GetPrefs(),
/*identity_manager=*/nullptr,
/*history_service=*/nullptr,
/*strike_database=*/nullptr,
@@ -4519,7 +4031,7 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) {
form.name = u"TestForm";
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -4534,14 +4046,13 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) {
field_types.push_back(PHONE_HOME_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate activating the autofill popup for the phone field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample("Autofill.AddressSuggestionsCount", 2,
1);
}
@@ -4551,34 +4062,31 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) {
// No new metric should be logged, since we're still on the same page.
test::CreateTestFormField("Email", "email", "b", "email", &field);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectTotalCount("Autofill.AddressSuggestionsCount", 0);
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate activating the autofill popup for the email field after typing.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample("Autofill.AddressSuggestionsCount", 1,
1);
}
// Reset the autofill manager state again.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate activating the autofill popup for the email field after a fill.
form.fields[0].is_autofilled = true;
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectTotalCount("Autofill.AddressSuggestionsCount", 1);
}
}
@@ -4593,7 +4101,7 @@ TEST_F(AutofillMetricsTest, CompanyNameSuggestions) {
form.name = u"TestForm";
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -4608,15 +4116,13 @@ TEST_F(AutofillMetricsTest, CompanyNameSuggestions) {
field_types.push_back(COMPANY_NAME);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate activating the autofill popup for the phone field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
-
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample("Autofill.AddressSuggestionsCount", 2,
1);
}
@@ -4636,7 +4142,7 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
form.name = u"TestForm";
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -4651,14 +4157,13 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
field_types.push_back(CREDIT_CARD_EXP_MONTH);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate an Autofill query on a credit card field.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
}
@@ -4666,8 +4171,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
// Simulate showing a credit card suggestion polled from "Name on card" field.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[0]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[0]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_ShowedCreditCardSuggestions"));
}
@@ -4676,8 +4181,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
// field.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[1]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[1]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_ShowedCreditCardSuggestions"));
}
@@ -4689,7 +4194,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
external_delegate_->OnQuery(0, form, form.fields.front(), gfx::RectF());
external_delegate_->DidAcceptSuggestion(
u"Test",
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()),
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()),
guid, 0);
EXPECT_EQ(1,
user_action_tester.GetActionCount("Autofill_SelectedSuggestion"));
@@ -4699,8 +4205,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
// field along with a "Clear form" footer suggestion.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[1]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[1]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_ShowedCreditCardSuggestions"));
}
@@ -4719,8 +4225,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
// field, this time to submit the form.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[1]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[1]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_ShowedCreditCardSuggestions"));
}
@@ -4732,7 +4238,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
external_delegate_->OnQuery(0, form, form.fields.front(), gfx::RectF());
external_delegate_->DidAcceptSuggestion(
u"Test",
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()),
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()),
guid, 0);
EXPECT_EQ(1,
user_action_tester.GetActionCount("Autofill_SelectedSuggestion"));
@@ -4742,9 +4249,10 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
{
base::UserActionTester user_action_tester;
std::string guid("10000000-0000-0000-0000-000000000001"); // local card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_FilledCreditCardSuggestion"));
}
@@ -4752,10 +4260,9 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
// Simulate submitting the credit card form.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
EXPECT_EQ(1,
user_action_tester.GetActionCount("Autofill_OnWillSubmitForm"));
EXPECT_EQ(1, user_action_tester.GetActionCount(
@@ -4788,7 +4295,7 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
// Expect 3 |FORM_EVENT_LOCAL_SUGGESTION_FILLED| events. First, from
// call to |external_delegate_->DidAcceptSuggestion|. Second and third, from
- // ExpectedUkmMetrics |browser_autofill_manager_->FillOrPreviewForm|.
+ // ExpectedUkmMetrics |autofill_manager().FillOrPreviewForm|.
ExpectedUkmMetricsRecord from_did_accept_suggestion{
{UkmSuggestionFilledType::kRecordTypeName, CreditCard::LOCAL_CARD},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
@@ -4814,7 +4321,7 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
/*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false,
- {FormType::kCreditCardForm});
+ {FormType::kCreditCardForm}, {0, 3, 0});
}
// Test that the UPI Checkout flow form submit is correctly logged
@@ -4825,7 +4332,7 @@ TEST_F(AutofillMetricsTest, UpiVpaUkmTest) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Enter VPA", "upi-vpa", "unique_id@upi", "text",
&field);
@@ -4834,8 +4341,8 @@ TEST_F(AutofillMetricsTest, UpiVpaUkmTest) {
std::vector<FormData> forms(1, form);
{
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
VerifySubmitFormUkm(test_ukm_recorder_, forms.back(),
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
@@ -4859,7 +4366,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
form.name = u"TestForm";
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -4874,14 +4381,13 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate an Autofill query on a profile field.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
}
@@ -4889,8 +4395,8 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
// Simulate showing a profile suggestion polled from "State" field.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[0]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[0]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_ShowedProfileSuggestions"));
}
@@ -4898,8 +4404,8 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
// Simulate showing a profile suggestion polled from "City" field.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[1]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[1]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_ShowedProfileSuggestions"));
}
@@ -4911,7 +4417,8 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
external_delegate_->OnQuery(0, form, form.fields.front(), gfx::RectF());
external_delegate_->DidAcceptSuggestion(
u"Test",
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid),
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid),
guid, 0);
EXPECT_EQ(1,
user_action_tester.GetActionCount("Autofill_SelectedSuggestion"));
@@ -4921,9 +4428,10 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
{
base::UserActionTester user_action_tester;
std::string guid(kTestGuid); // local profile.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_FilledProfileSuggestion"));
}
@@ -4931,10 +4439,9 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
// Simulate submitting the profile form.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
EXPECT_EQ(1,
user_action_tester.GetActionCount("Autofill_OnWillSubmitForm"));
EXPECT_EQ(1, user_action_tester.GetActionCount(
@@ -4961,7 +4468,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
Collapse(CalculateFormSignature(form)).value()}}});
// Expect 2 |FORM_EVENT_LOCAL_SUGGESTION_FILLED| events. First, from
// call to |external_delegate_->DidAcceptSuggestion|. Second, from call to
- // |browser_autofill_manager_->FillOrPreviewForm|.
+ // |autofill_manager().FillOrPreviewForm|.
VerifyUkm(test_ukm_recorder_, form, UkmSuggestionFilledType::kEntryName,
{{{UkmSuggestionFilledType::kRecordTypeName,
AutofillProfile::LOCAL_PROFILE},
@@ -4983,10 +4490,10 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
Collapse(CalculateFormSignature(form)).value()}}});
// Expect |NON_FILLABLE_FORM_OR_NEW_DATA| in |AutofillFormSubmittedState|
// because |field.value| is empty in |DeterminePossibleFieldTypesForUpload|.
- VerifySubmitFormUkm(test_ukm_recorder_, form,
- AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
- /*is_for_credit_card=*/false,
- /* has_upi_vpa_field=*/false, {FormType::kAddressForm});
+ VerifySubmitFormUkm(
+ test_ukm_recorder_, form, AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
+ /*is_for_credit_card=*/false,
+ /* has_upi_vpa_field=*/false, {FormType::kAddressForm}, {0, 2, 0});
}
// Tests that the Autofill_PolledCreditCardSuggestions user action is only
@@ -5017,37 +4524,29 @@ TEST_F(AutofillMetricsTest, PolledCreditCardSuggestions_DebounceLogs) {
field_types.push_back(CREDIT_CARD_EXP_MONTH);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate an Autofill query on a credit card field. A poll should be logged.
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
// Simulate a second query on the same field. There should still only be one
// logged poll.
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
// Simulate a query to another field. There should be a second poll logged.
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[1], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[1]);
EXPECT_EQ(2, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
// Simulate a query back to the initial field. There should be a third poll
// logged.
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]);
EXPECT_EQ(3, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
}
@@ -5067,7 +4566,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5090,43 +4589,39 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) {
// In order to test that the QueriedCreditCardFormIsSecure is logged as
// false, we need to set the main frame origin, otherwise this fill is
// skipped due to the form being detected as mixed content.
- GURL client_form_origin = autofill_client_.form_origin();
+ GURL client_form_origin = autofill_client_->form_origin();
GURL::Replacements replacements;
replacements.SetSchemeStr(url::kHttpScheme);
- autofill_client_.set_form_origin(
+ autofill_client_->set_form_origin(
client_form_origin.ReplaceComponents(replacements));
form.main_frame_origin =
- url::Origin::Create(autofill_client_.form_origin());
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ url::Origin::Create(autofill_client_->form_origin());
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate an Autofill query on a credit card field (HTTP, non-secure
// form).
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[1], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[1]);
histogram_tester.ExpectUniqueSample(
"Autofill.QueriedCreditCardFormIsSecure", false, 1);
// Reset the main frame origin to secure for other tests
- autofill_client_.set_form_origin(client_form_origin);
+ autofill_client_->set_form_origin(client_form_origin);
}
{
// Simulate having seen this secure form on page load.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
form.host_frame = test::MakeLocalFrameToken();
form.unique_renderer_id = test::MakeFormRendererId();
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
form.main_frame_origin =
- url::Origin::Create(autofill_client_.form_origin());
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ url::Origin::Create(autofill_client_->form_origin());
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate an Autofill query on a credit card field (HTTPS form).
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[1], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[1]);
histogram_tester.ExpectUniqueSample(
"Autofill.QueriedCreditCardFormIsSecure", true, 1);
}
@@ -5144,7 +4639,7 @@ TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) {
form.name = u"TestForm";
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5159,37 +4654,29 @@ TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate an Autofill query on a profile field. A poll should be logged.
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
// Simulate a second query on the same field. There should still only be poll
// logged.
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
// Simulate a query to another field. There should be a second poll logged.
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[1], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[1]);
EXPECT_EQ(2, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
// Simulate a query back to the initial field. There should be a third poll
// logged.
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]);
EXPECT_EQ(3, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
}
@@ -5203,7 +4690,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardParsedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5222,8 +4709,8 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardParsedFormEvents) {
forms.push_back(form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithNoData", FORM_EVENT_DID_PARSE_FORM,
1);
@@ -5238,7 +4725,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardInteractedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5253,14 +4740,13 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardInteractedFormEvents) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample("Autofill.FormEvents.CreditCard",
FORM_EVENT_INTERACTED_ONCE, 1);
histogram_tester.ExpectUniqueSample(
@@ -5269,16 +4755,14 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardInteractedFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate activating the autofill popup for the credit card field twice.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnAskForValuesToFill(
- 1, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field, /*query_id=*/0);
+ autofill_manager().OnAskForValuesToFillTest(form, field, /*query_id=*/1);
histogram_tester.ExpectUniqueSample("Autofill.FormEvents.CreditCard",
FORM_EVENT_INTERACTED_ONCE, 1);
histogram_tester.ExpectUniqueSample(
@@ -5296,7 +4780,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardPopupSuppressedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5311,13 +4795,13 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardPopupSuppressedFormEvents) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating popup being suppressed.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidSuppressPopup(form, field);
+ autofill_manager().DidSuppressPopup(form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_POPUP_SUPPRESSED, 1);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
@@ -5328,14 +4812,14 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardPopupSuppressedFormEvents) {
FORM_EVENT_POPUP_SUPPRESSED_ONCE, 1);
}
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating popup being suppressed.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidSuppressPopup(form, field);
- browser_autofill_manager_->DidSuppressPopup(form, field);
+ autofill_manager().DidSuppressPopup(form, field);
+ autofill_manager().DidSuppressPopup(form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_POPUP_SUPPRESSED, 2);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
@@ -5356,7 +4840,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardShownFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5371,14 +4855,13 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardShownFormEvents) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating new popup being shown.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTIONS_SHOWN, 1);
histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
@@ -5390,16 +4873,14 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardShownFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating two popups in the same page load.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTIONS_SHOWN, 2);
histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
@@ -5411,37 +4892,34 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardShownFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating same popup being refreshed.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(false /* is_new_popup */,
- form, field);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
- FORM_EVENT_SUGGESTIONS_SHOWN, 0);
- histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
- FORM_EVENT_SUGGESTIONS_SHOWN, 0);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
- FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0);
- histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
- FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0);
+ autofill_manager().DidShowSuggestions(false /* is_new_popup */, form,
+ field);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"),
+ BucketsAre(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 0),
+ Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ credit_card_form_events_frame_histogram_),
+ BucketsAre(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 0),
+ Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0)));
}
}
// Test that we log specific suggestion shown form events for virtual credit
// cards.
TEST_P(AutofillMetricsIFrameTest, VirtualCreditCardShownFormEvents) {
- scoped_feature_list_.InitAndEnableFeature(
- features::kAutofillEnableMerchantBoundVirtualCards);
-
// Set up our form data. https:// is required.
FormData form;
form.name = u"TestForm";
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5465,16 +4943,14 @@ TEST_P(AutofillMetricsIFrameTest, VirtualCreditCardShownFormEvents) {
true /* include_virtual_credit_card */);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate new popup being shown.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTIONS_SHOWN, 1);
histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
@@ -5498,18 +4974,15 @@ TEST_P(AutofillMetricsIFrameTest, VirtualCreditCardShownFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating two popups in the same page load.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTIONS_SHOWN, 2);
histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
@@ -5533,16 +5006,15 @@ TEST_P(AutofillMetricsIFrameTest, VirtualCreditCardShownFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating same popup being refreshed.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(false /* is_new_popup */,
- form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(false /* is_new_popup */, form,
+ field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTIONS_SHOWN, 0);
histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
@@ -5572,19 +5044,16 @@ TEST_P(AutofillMetricsIFrameTest, VirtualCreditCardShownFormEvents) {
false /* include_virtual_credit_card */);
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating two popups in the same page load. Suggestions shown should be
// logged, but suggestions shown with virtual card should not.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTIONS_SHOWN, 2);
histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
@@ -5622,7 +5091,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSelectedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5637,17 +5106,18 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSelectedFormEvents) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating selecting a masked server card suggestion.
base::HistogramTester histogram_tester;
std::string guid(
"10000000-0000-0000-0000-000000000002"); // masked server card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields[2],
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1);
@@ -5663,20 +5133,22 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSelectedFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating selecting a masked server card multiple times.
base::HistogramTester histogram_tester;
std::string guid(
"10000000-0000-0000-0000-000000000002"); // masked server card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields[2],
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields[2],
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 2);
@@ -5692,15 +5164,15 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSelectedFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating selecting a virtual server suggestion by selecting the
// option based on the enrolled masked card.
base::HistogramTester histogram_tester;
std::string guid("10000000-0000-0000-0000-000000000002"); // masked card
- browser_autofill_manager_->FillOrPreviewVirtualCardInformation(
+ autofill_manager().FillOrPreviewVirtualCardInformation(
mojom::RendererFormDataAction::kFill, guid, kDefaultPageID, form,
form.fields[2]);
OnCreditCardFetchingSuccessful(u"6011000990139424",
@@ -5720,19 +5192,19 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSelectedFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating selecting a virtual card multiple times.
base::HistogramTester histogram_tester;
std::string guid("10000000-0000-0000-0000-000000000002"); // masked card
- browser_autofill_manager_->FillOrPreviewVirtualCardInformation(
+ autofill_manager().FillOrPreviewVirtualCardInformation(
mojom::RendererFormDataAction::kFill, guid, kDefaultPageID, form,
form.fields[2]);
OnCreditCardFetchingSuccessful(u"6011000990139424",
/*is_virtual_card=*/true);
- browser_autofill_manager_->FillOrPreviewVirtualCardInformation(
+ autofill_manager().FillOrPreviewVirtualCardInformation(
mojom::RendererFormDataAction::kFill, guid, kDefaultPageID, form,
form.fields[2]);
OnCreditCardFetchingSuccessful(u"6011000990139424",
@@ -5766,7 +5238,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5781,16 +5253,17 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating filling a local card suggestion.
base::HistogramTester histogram_tester;
std::string guid("10000000-0000-0000-0000-000000000001"); // local card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1);
histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
@@ -5804,15 +5277,15 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating filling a virtual card suggestion by selecting the option
// based on the enrolled masked card.
base::HistogramTester histogram_tester;
std::string guid("10000000-0000-0000-0000-000000000002"); // masked card
- browser_autofill_manager_->FillOrPreviewVirtualCardInformation(
+ autofill_manager().FillOrPreviewVirtualCardInformation(
mojom::RendererFormDataAction::kFill, guid, kDefaultPageID, form,
form.fields.front());
OnCreditCardFetchingSuccessful(u"6011000990139424",
@@ -5832,20 +5305,21 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating filling a masked card server suggestion.
base::HistogramTester histogram_tester;
std::string guid(
"10000000-0000-0000-0000-000000000002"); // masked server card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnCreditCardFetchingSuccessful(u"6011000990139424");
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1);
@@ -5868,17 +5342,18 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) {
true /* masked_card_is_enrolled_for_virtual_card */);
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating filling a full card server suggestion.
base::HistogramTester histogram_tester;
std::string guid(
"10000000-0000-0000-0000-000000000003"); // full server card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_SERVER_SUGGESTION_FILLED, 1);
histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
@@ -5892,19 +5367,21 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating filling multiple times.
base::HistogramTester histogram_tester;
std::string guid("10000000-0000-0000-0000-000000000001"); // local card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_LOCAL_SUGGESTION_FILLED, 2);
histogram_tester.ExpectBucketCount(credit_card_form_events_frame_histogram_,
@@ -5918,6 +5395,98 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) {
}
}
+// Test to log when an unique local card is autofilled, when other duplicated
+// server cards exist.
+TEST_P(
+ AutofillMetricsIFrameTest,
+ CreditCardFilledFormEventsUsingUniqueLocalCardWhenOtherDuplicateServerCardsPresent) {
+ CreateLocalAndDuplicateServerCreditCard();
+ // Creating a local mastercard credit card.
+ std::string local_guid = CreateLocalMasterCard();
+
+ // Set up our form data.
+ FormData form = test::GetFormData(
+ {.description_for_logging = "PaymentProfileImportRequirements",
+ .fields = {
+ {.role = ServerFieldType::CREDIT_CARD_EXP_MONTH, .value = u""},
+ {.role = ServerFieldType::CREDIT_CARD_EXP_2_DIGIT_YEAR,
+ .value = u""},
+ {.role = ServerFieldType::CREDIT_CARD_NUMBER, .value = u""}}});
+ std::vector<ServerFieldType> field_types = {
+ CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER};
+
+ autofill_manager().AddSeenForm(form, field_types, field_types);
+ // Simulate filling a unique local card suggestion.
+ base::HistogramTester histogram_tester;
+ autofill_manager().FillOrPreviewForm(
+ mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
+ autofill_manager().suggestion_generator()->MakeFrontendId(local_guid,
+ std::string()));
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"),
+ BucketsInclude(
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1),
+ Bucket(
+ FORM_EVENT_LOCAL_SUGGESTION_FILLED_FOR_AN_EXISTING_SERVER_CARD_ONCE,
+ 0)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(credit_card_form_events_frame_histogram_),
+ BucketsInclude(
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1),
+ Bucket(
+ FORM_EVENT_LOCAL_SUGGESTION_FILLED_FOR_AN_EXISTING_SERVER_CARD_ONCE,
+ 0)));
+}
+
+// Test to log when a local card is autofilled and its duplicated
+// server card exists.
+TEST_P(AutofillMetricsIFrameTest,
+ CreditCardFilledFormEventsUsingDuplicateServerCard) {
+ // Creating a local and a duplicate server card.
+ std::vector<std::string> local_and_duplicate_server_card_guids =
+ CreateLocalAndDuplicateServerCreditCard();
+ // Set up our form data.
+ FormData form = test::GetFormData(
+ {.description_for_logging = "PaymentProfileImportRequirements",
+ .fields = {
+ {.role = ServerFieldType::CREDIT_CARD_EXP_MONTH, .value = u""},
+ {.role = ServerFieldType::CREDIT_CARD_EXP_2_DIGIT_YEAR,
+ .value = u""},
+ {.role = ServerFieldType::CREDIT_CARD_NUMBER, .value = u""}}});
+ std::vector<ServerFieldType> field_types = {
+ CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER};
+
+ autofill_manager().AddSeenForm(form, field_types, field_types);
+ // Simulate filling a local card suggestion with a duplicate server card.
+ base::HistogramTester histogram_tester;
+ // Local card with a duplicate server card present at index 0.
+ std::string local_guid(local_and_duplicate_server_card_guids.at(0));
+ autofill_manager().FillOrPreviewForm(
+ mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
+ autofill_manager().suggestion_generator()->MakeFrontendId(local_guid,
+ std::string()));
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"),
+ BucketsInclude(
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1),
+ Bucket(
+ FORM_EVENT_LOCAL_SUGGESTION_FILLED_FOR_AN_EXISTING_SERVER_CARD_ONCE,
+ 1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(credit_card_form_events_frame_histogram_),
+ BucketsInclude(
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1),
+ Bucket(
+ FORM_EVENT_LOCAL_SUGGESTION_FILLED_FOR_AN_EXISTING_SERVER_CARD_ONCE,
+ 1)));
+}
+
// Test that we log preflight calls for credit card unmasking.
TEST_F(AutofillMetricsTest, CreditCardUnmaskingPreflightCall) {
scoped_feature_list_.InitAndEnableFeature(
@@ -5936,8 +5505,8 @@ TEST_F(AutofillMetricsTest, CreditCardUnmaskingPreflightCall) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Create local cards and set user as eligible for FIDO authentication.
@@ -5947,8 +5516,8 @@ TEST_F(AutofillMetricsTest, CreditCardUnmaskingPreflightCall) {
false /* include_full_server_credit_card */,
false /* masked_card_is_enrolled_for_virtual_card */);
SetFidoEligibility(true);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[0]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[0]);
// If no masked server cards are available, then no preflight call is made.
histogram_tester.ExpectTotalCount(preflight_call_metric, 0);
histogram_tester.ExpectTotalCount(preflight_latency_metric, 0);
@@ -5963,8 +5532,8 @@ TEST_F(AutofillMetricsTest, CreditCardUnmaskingPreflightCall) {
false /* include_full_server_credit_card */,
false /* masked_card_is_enrolled_for_virtual_card */);
SetFidoEligibility(false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[0]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[0]);
// If user is not verifiable, then no preflight call is made.
histogram_tester.ExpectTotalCount(preflight_call_metric, 0);
histogram_tester.ExpectTotalCount(preflight_latency_metric, 0);
@@ -5979,8 +5548,8 @@ TEST_F(AutofillMetricsTest, CreditCardUnmaskingPreflightCall) {
true /* include_full_server_credit_card */,
false /* masked_card_is_enrolled_for_virtual_card */);
SetFidoEligibility(false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[0]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[0]);
// If no masked server cards are available, then no preflight call is made.
histogram_tester.ExpectTotalCount(preflight_call_metric, 0);
histogram_tester.ExpectTotalCount(preflight_latency_metric, 0);
@@ -5995,13 +5564,14 @@ TEST_F(AutofillMetricsTest, CreditCardUnmaskingPreflightCall) {
false /* include_full_server_credit_card */,
false /* masked_card_is_enrolled_for_virtual_card */);
SetFidoEligibility(true);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[0]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[0]);
std::string guid(
"10000000-0000-0000-0000-000000000002"); // masked server card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
// Preflight call is made only if a masked server card is available and the
// user is eligible for FIDO authentication (except iOS).
#if BUILDFLAG(IS_IOS)
@@ -6022,13 +5592,14 @@ TEST_F(AutofillMetricsTest, CreditCardUnmaskingPreflightCall) {
true /* include_full_server_credit_card */,
false /* masked_card_is_enrolled_for_virtual_card */);
SetFidoEligibility(true);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[0]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[0]);
std::string guid(
"10000000-0000-0000-0000-000000000002"); // masked server card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
// Preflight call is made only if a masked server card is available and the
// user is eligible for FIDO authentication (except iOS).
#if BUILDFLAG(IS_IOS)
@@ -6055,7 +5626,7 @@ TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration_ServerCard) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -6070,17 +5641,18 @@ TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration_ServerCard) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating filling a masked card server suggestion.
base::HistogramTester histogram_tester;
// Masked server card.
std::string guid("10000000-0000-0000-0000-000000000002");
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess,
"6011000990139424");
histogram_tester.ExpectTotalCount(
@@ -6090,8 +5662,8 @@ TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration_ServerCard) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Creating masked card
RecreateCreditCards(false /* include_local_credit_card */,
true /* include_masked_server_credit_card */,
@@ -6103,9 +5675,10 @@ TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration_ServerCard) {
base::HistogramTester histogram_tester;
// Masked server card.
std::string guid("10000000-0000-0000-0000-000000000002");
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kPermanentFailure,
std::string());
histogram_tester.ExpectTotalCount(
@@ -6135,17 +5708,18 @@ TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration_BadServerResponse) {
ASSERT_EQ(form.fields.size(), field_types.size());
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating filling a masked card server suggestion.
base::HistogramTester histogram_tester;
// Masked server card.
std::string guid("10000000-0000-0000-0000-000000000002");
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnDidGetRealPanWithNonHttpOkResponse();
histogram_tester.ExpectTotalCount(
"Autofill.UnmaskPrompt.GetRealPanDuration", 1);
@@ -6255,7 +5829,7 @@ TEST_F(AutofillMetricsTest,
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -6270,17 +5844,15 @@ TEST_F(AutofillMetricsTest,
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_NO_CARD, 1);
@@ -6315,17 +5887,15 @@ TEST_P(AutofillMetricsIFrameTest,
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_WRONG_SIZE_CARD, 1);
@@ -6364,17 +5934,15 @@ TEST_P(AutofillMetricsIFrameTest,
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_FAIL_LUHN_CHECK_CARD, 1);
@@ -6398,7 +5966,7 @@ TEST_P(AutofillMetricsIFrameTest,
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -6414,17 +5982,15 @@ TEST_P(AutofillMetricsIFrameTest,
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, 1);
@@ -6448,7 +6014,7 @@ TEST_P(AutofillMetricsIFrameTest,
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -6464,17 +6030,15 @@ TEST_P(AutofillMetricsIFrameTest,
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 1);
@@ -6498,7 +6062,7 @@ TEST_P(AutofillMetricsIFrameTest,
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -6514,22 +6078,21 @@ TEST_P(AutofillMetricsIFrameTest,
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulating submission with suggestion shown and selected.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
std::string guid("10000000-0000-0000-0000-000000000001");
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(guid,
+ std::string()));
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 0);
@@ -6560,7 +6123,7 @@ TEST_F(AutofillMetricsTest, ShouldNotLogFormEventNoCardForAddressForm) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -6575,17 +6138,15 @@ TEST_F(AutofillMetricsTest, ShouldNotLogFormEventNoCardForAddressForm) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_NO_CARD, 0);
@@ -6605,7 +6166,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -6620,16 +6181,15 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
@@ -6654,17 +6214,15 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
// Reset the autofill manager state and purge UKM logs.
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with suggestion shown.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1);
@@ -6699,21 +6257,19 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
// Reset the autofill manager state and purge UKM logs.
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with suggestion shown. Form is submmitted and
// autofill manager is reset before UploadFormDataAsyncCallback is
// triggered.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Trigger UploadFormDataAsyncCallback.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1);
@@ -6748,19 +6304,19 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
// Reset the autofill manager state and purge UKM logs.
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
std::string guid("10000000-0000-0000-0000-000000000001"); // local card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -6788,28 +6344,27 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
/*is_for_credit_card=*/true,
/* has_upi_vpa_field=*/false,
- {FormType::kCreditCardForm});
+ {FormType::kCreditCardForm}, {0, 1, 0});
}
// Reset the autofill manager state and purge UKM logs.
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled virtual card data by selecting the
// option based on the enrolled masked card.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
std::string guid("10000000-0000-0000-0000-000000000002"); // masked card
- browser_autofill_manager_->FillOrPreviewVirtualCardInformation(
+ autofill_manager().FillOrPreviewVirtualCardInformation(
mojom::RendererFormDataAction::kFill, guid, kDefaultPageID, form,
form.fields.front());
OnCreditCardFetchingSuccessful(u"6011000990139424",
/*is_virtual_card=*/true);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_VIRTUAL_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -6837,26 +6392,26 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
/*is_for_credit_card=*/true,
/* has_upi_vpa_field=*/false,
- {FormType::kCreditCardForm});
+ {FormType::kCreditCardForm}, {0, 1, 0});
}
// Reset the autofill manager state and purge UKM logs.
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled server data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
std::string guid(
"10000000-0000-0000-0000-000000000003"); // full server card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -6884,25 +6439,26 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
/*is_for_credit_card=*/true,
/* has_upi_vpa_field=*/false,
- {FormType::kCreditCardForm});
+ {FormType::kCreditCardForm}, {0, 1, 0});
}
// Reset the autofill manager state and purge UKM logs.
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with a masked card server suggestion.
base::HistogramTester histogram_tester;
std::string guid(
"10000000-0000-0000-0000-000000000002"); // masked server card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnCreditCardFetchingSuccessful(u"6011000990139424");
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1);
@@ -6930,7 +6486,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
/*is_for_credit_card=*/true,
/* has_upi_vpa_field=*/false,
- {FormType::kCreditCardForm});
+ {FormType::kCreditCardForm}, {0, 1, 0});
}
// Reset the autofill manager state and purge UKM logs.
@@ -6944,16 +6500,15 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
true /* masked_card_is_enrolled_for_virtual_card */);
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating multiple submissions.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
VerifySubmitFormUkm(test_ukm_recorder_, form,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
@@ -6961,8 +6516,8 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
/* has_upi_vpa_field=*/false,
{FormType::kCreditCardForm});
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
VerifyUkm(
test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName,
@@ -6974,7 +6529,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
{UkmFormSubmittedType::kFormTypesName,
AutofillMetrics::FormTypesToBitVector({FormType::kCreditCardForm})},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}},
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName, 0},
+ {UkmFormSubmittedType::kAutofillFillsName, 0},
+ {UkmFormSubmittedType::kAutocompleteFillsName, 0}},
{{UkmFormSubmittedType::kAutofillFormSubmittedStateName,
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
@@ -6983,7 +6541,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
{UkmFormSubmittedType::kFormTypesName,
AutofillMetrics::FormTypesToBitVector({FormType::kCreditCardForm})},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}}});
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName, 0},
+ {UkmFormSubmittedType::kAutofillFillsName, 0},
+ {UkmFormSubmittedType::kAutocompleteFillsName, 0}}});
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
@@ -7050,16 +6611,15 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) {
// Reset the autofill manager state and purge UKM logs.
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with suggestion shown but without previous
// interaction.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
@@ -7155,7 +6715,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -7170,16 +6730,15 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -7195,18 +6754,16 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with suggestion shown.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1);
@@ -7222,20 +6779,20 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
std::string guid("10000000-0000-0000-0000-000000000001"); // local card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -7251,23 +6808,22 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled virtual card data by selecting the
// option based on the enrolled masked card.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
std::string guid("10000000-0000-0000-0000-000000000002"); // masked card
- browser_autofill_manager_->FillOrPreviewVirtualCardInformation(
+ autofill_manager().FillOrPreviewVirtualCardInformation(
mojom::RendererFormDataAction::kFill, guid, kDefaultPageID, form,
form.fields.front());
OnCreditCardFetchingSuccessful(u"6011000990139424",
/*is_virtual_card=*/true);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_VIRTUAL_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -7283,21 +6839,21 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled server data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
// Full server card.
std::string guid("10000000-0000-0000-0000-000000000003");
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -7313,17 +6869,18 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with a masked card server suggestion.
base::HistogramTester histogram_tester;
// Masked server card.
std::string guid("10000000-0000-0000-0000-000000000002");
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnCreditCardFetchingSuccessful(u"6011000990139424");
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
@@ -7347,18 +6904,17 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) {
true /* masked_card_is_enrolled_for_virtual_card */);
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating multiple submissions.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -7422,17 +6978,16 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with suggestion shown but without previous
// interaction.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
@@ -7503,7 +7058,7 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
form.name = u"TestForm";
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -7524,20 +7079,19 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
false /* masked_card_is_enrolled_for_virtual_card */);
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating activating the autofill popup for the credit card field, new
// popup being shown and filling a local card suggestion.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
std::string guid("10000000-0000-0000-0000-000000000001"); // local card
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTIONS_SHOWN, 1);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
@@ -7566,11 +7120,12 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
// Add another masked server card, this time with a linked offer.
std::string guid("12340000-0000-0000-0000-000000000001");
- AddMaskedServerCreditCardWithOffer(guid, "$4", autofill_client_.form_origin(),
+ AddMaskedServerCreditCardWithOffer(guid, "$4",
+ autofill_client_->form_origin(),
/*id=*/0x4fff);
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// A masked server card with linked offers.
@@ -7579,39 +7134,29 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
// submitting the form. Verify that all related form events are correctly
// logged to offer sub-histogram.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
// Select the masked server card with the linked offer.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess,
"6011000990139424");
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_SUGGESTIONS_SHOWN, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 1);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "Autofill.FormEvents.CreditCard.WithOffer"),
+ IsSupersetOf(
+ {Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1),
+ Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 1)}));
// Ensure we count the correct number of offers shown.
histogram_tester.ExpectUniqueSample(
@@ -7626,8 +7171,8 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// A masked server card with linked offers.
@@ -7636,20 +7181,19 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
// submitting the form. Verify that all related form events are correctly
// logged to offer sub-histogram.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
// Select another card, and still log to offer
// sub-histogram because user has another masked server card with offer.
guid = "10000000-0000-0000-0000-000000000002";
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess,
"6011000990139424");
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard.WithOffer",
FORM_EVENT_SUGGESTIONS_SHOWN, 1);
@@ -7690,30 +7234,30 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
true /* include_full_server_credit_card */,
false /* masked_card_is_enrolled_for_virtual_card */);
guid = "12340000-0000-0000-0000-000000000002";
- AddMaskedServerCreditCardWithOffer(guid, "$4", autofill_client_.form_origin(),
- /*id=*/0x3fff, /*expired=*/true);
+ AddMaskedServerCreditCardWithOffer(guid, "$4",
+ autofill_client_->form_origin(),
+ /*id=*/0x3fff, /*offer_expired=*/true);
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating activating the autofill popup for the credit card field,
// new popup being shown and filling a local card suggestion.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
// Select the card with linked offer, though metrics should not record it
// since the offer is expired.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess,
"6011000990139424");
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Histograms without ".WithOffer" should be recorded.
histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_SUGGESTIONS_SHOWN, 1);
@@ -7761,12 +7305,13 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
true /* include_full_server_credit_card */,
false /* masked_card_is_enrolled_for_virtual_card */);
guid = "12340000-0000-0000-0000-000000000003";
- AddMaskedServerCreditCardWithOffer(guid, "$5", autofill_client_.form_origin(),
+ AddMaskedServerCreditCardWithOffer(guid, "$5",
+ autofill_client_->form_origin(),
/*id=*/0x5fff);
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// A masked server card with linked offers.
@@ -7777,25 +7322,22 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
// sub-histogram. Making suggestions reappear tests confirmation of a fix
// for crbug/1198751.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
// Select the masked server card with the linked offer.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess,
"6011000990139424");
// Simulate user showing suggestions but then submitting form with
// previously filled card info.
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard.WithOffer",
FORM_EVENT_SUGGESTIONS_SHOWN, 2);
@@ -7831,8 +7373,8 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// A masked server card with linked offers.
@@ -7841,21 +7383,20 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
// failing the CVC check and submitting the form anyways. Verify that all
// related form events are correctly logged to offer sub-histogram.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
// Select the masked server card with the linked offer, but fail the CVC
// check.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kPermanentFailure,
std::string());
// Submitting the form without the filled suggestion.
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard.WithOffer",
FORM_EVENT_SUGGESTIONS_SHOWN, 1);
@@ -7891,8 +7432,8 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// A masked server card with linked offers.
@@ -7903,48 +7444,37 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) {
base::HistogramTester histogram_tester;
// Show suggestions and select the card with offer.
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess,
"6011000990139424");
// Show suggestions again, and select a local card instead.
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
guid = "10000000-0000-0000-0000-000000000001";
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_SUGGESTIONS_SHOWN, 2);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.CreditCard.WithOffer",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 1);
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "Autofill.FormEvents.CreditCard.WithOffer"),
+ IsSupersetOf(
+ {Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2),
+ Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 1)}));
// Ensure we count the correct number of offers shown.
histogram_tester.ExpectBucketCount("Autofill.Offer.SuggestedCardsHaveOffer",
@@ -7969,7 +7499,7 @@ TEST_F(AutofillMetricsTest, MixedParsedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -7997,8 +7527,8 @@ TEST_F(AutofillMetricsTest, MixedParsedFormEvents) {
forms.push_back(form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address.WithNoData",
FORM_EVENT_DID_PARSE_FORM, 1);
histogram_tester.ExpectUniqueSample(
@@ -8015,7 +7545,7 @@ TEST_F(AutofillMetricsTest, AddressParsedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -8034,8 +7564,8 @@ TEST_F(AutofillMetricsTest, AddressParsedFormEvents) {
forms.push_back(form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address.WithNoData",
FORM_EVENT_DID_PARSE_FORM, 1);
@@ -8060,7 +7590,7 @@ TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -8075,14 +7605,13 @@ TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate activating the autofill popup for the street field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address",
FORM_EVENT_INTERACTED_ONCE, 1);
@@ -8100,17 +7629,15 @@ TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate activating the autofill popup for the street field twice.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnAskForValuesToFill(
- 1, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field, /*query_id=*/0);
+ autofill_manager().OnAskForValuesToFillTest(form, field, /*query_id=*/1);
histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address",
FORM_EVENT_INTERACTED_ONCE, 1);
// Check if FormEvent UKM is logged properly
@@ -8138,7 +7665,7 @@ TEST_F(AutofillMetricsTest, AddressSuppressedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -8153,13 +7680,13 @@ TEST_F(AutofillMetricsTest, AddressSuppressedFormEvents) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating new popup being shown.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidSuppressPopup(form, field);
+ autofill_manager().DidSuppressPopup(form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
FORM_EVENT_POPUP_SUPPRESSED, 1);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
@@ -8184,15 +7711,15 @@ TEST_F(AutofillMetricsTest, AddressSuppressedFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating two popups in the same page load.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidSuppressPopup(form, field);
- browser_autofill_manager_->DidSuppressPopup(form, field);
+ autofill_manager().DidSuppressPopup(form, field);
+ autofill_manager().DidSuppressPopup(form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
FORM_EVENT_POPUP_SUPPRESSED, 2);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
@@ -8233,7 +7760,7 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -8248,14 +7775,13 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating new popup being shown.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
FORM_EVENT_SUGGESTIONS_SHOWN, 1);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
@@ -8279,17 +7805,15 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating two popups in the same page load.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
FORM_EVENT_SUGGESTIONS_SHOWN, 2);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
@@ -8318,15 +7842,15 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating same popup being refreshed.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(false /* is_new_popup */,
- form, field);
+ autofill_manager().DidShowSuggestions(false /* is_new_popup */, form,
+ field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
FORM_EVENT_SUGGESTIONS_SHOWN, 0);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
@@ -8349,7 +7873,7 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -8364,16 +7888,17 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating selecting/filling a local profile suggestion.
base::HistogramTester histogram_tester;
std::string guid(kTestGuid); // local profile
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
@@ -8398,20 +7923,22 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating selecting/filling a local profile suggestion more than once.
base::HistogramTester histogram_tester;
std::string guid(kTestGuid); // local profile
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
FORM_EVENT_LOCAL_SUGGESTION_FILLED, 2);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
@@ -8421,16 +7948,17 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) {
// Create a server profile and reset the autofill manager state.
RecreateProfile(/*is_server=*/true);
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate selecting/filling a server profile suggestion.
base::HistogramTester histogram_tester;
std::string guid(kTestGuid); // server profile
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
FORM_EVENT_SERVER_SUGGESTION_FILLED, 1);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
@@ -8438,19 +7966,21 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) {
1);
}
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulate selecting/filling a server profile suggestion more than once.
base::HistogramTester histogram_tester;
std::string guid(kTestGuid); // server profile
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
FORM_EVENT_SERVER_SUGGESTION_FILLED, 2);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
@@ -8470,7 +8000,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -8485,16 +8015,15 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -8511,19 +8040,18 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
// Reset the autofill manager state and purge UKM logs.
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with no filled data. Form is submmitted and
// autofill manager is reset before UploadFormDataAsyncCallback is
// triggered.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
// Trigger UploadFormDataAsyncCallback.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -8540,17 +8068,15 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
// Reset the autofill manager state and purge UKM logs.
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with suggestion shown.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1);
@@ -8560,21 +8086,21 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
std::string guid(kTestGuid); // local profile
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -8584,89 +8110,58 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating multiple submissions.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE,
- 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.Address"),
+ BucketsInclude(Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1),
+ Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1),
+ Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0)));
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with suggestion show but without previous
// interaction.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE,
- 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.Address"),
+ BucketsInclude(
+ Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+ 0),
+ Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 0)));
// Check if FormEvent UKM is logged properly
auto entries =
@@ -8686,7 +8181,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -8701,16 +8196,15 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -8720,19 +8214,17 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with suggestion shown.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1);
@@ -8742,21 +8234,21 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
std::string guid(kTestGuid); // local profile
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1);
@@ -8766,49 +8258,33 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating multiple submissions.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE,
- 1);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.Address"),
+ BucketsInclude(
+ Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+ 0),
+ Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 0)));
// Check if FormEvent UKM is logged properly
auto entries =
test_ukm_recorder_->GetEntriesByName(UkmFormEventType::kEntryName);
@@ -8816,48 +8292,32 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
{
// Simulating submission with suggestion shown but without previous
// interaction.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- field);
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE,
- 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
- histogram_tester.ExpectBucketCount(
- "Autofill.FormEvents.Address",
- FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.Address"),
+ BucketsInclude(
+ Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+ 0),
+ Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0),
+ Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 0)));
// Check if FormEvent UKM is logged properly
auto entries =
test_ukm_recorder_->GetEntriesByName(UkmFormEventType::kEntryName);
@@ -8874,18 +8334,17 @@ TEST_F(AutofillMetricsTest, RecordStandalonePhoneField) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
test::CreateTestFormField("Phone", "phone", "", "tel", &field);
form.fields.push_back(field);
field_types.push_back(PHONE_HOME_NUMBER);
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address.PhoneOnly",
FORM_EVENT_INTERACTED_ONCE, 1);
}
@@ -8899,7 +8358,7 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -8914,8 +8373,8 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
field_types.push_back(CREDIT_CARD_NUMBER);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
RecreateCreditCards(false /* include_local_credit_card */,
false /* include_masked_server_credit_card */,
false /* include_full_server_credit_card */,
@@ -8924,17 +8383,16 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithNoData", FORM_EVENT_INTERACTED_ONCE,
1);
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
RecreateCreditCards(true /* include_local_credit_card */,
false /* include_masked_server_credit_card */,
false /* include_full_server_credit_card */,
@@ -8943,17 +8401,16 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithOnlyLocalData",
FORM_EVENT_INTERACTED_ONCE, 1);
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
RecreateCreditCards(false /* include_local_credit_card */,
true /* include_masked_server_credit_card */,
false /* include_full_server_credit_card */,
@@ -8962,17 +8419,16 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithOnlyServerData",
FORM_EVENT_INTERACTED_ONCE, 1);
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
RecreateCreditCards(false /* include_local_credit_card */,
false /* include_masked_server_credit_card */,
true /* include_full_server_credit_card */,
@@ -8981,17 +8437,16 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithOnlyServerData",
FORM_EVENT_INTERACTED_ONCE, 1);
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
PurgeUKM();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
RecreateCreditCards(true /* include_local_credit_card */,
false /* include_masked_server_credit_card */,
true /* include_full_server_credit_card */,
@@ -9000,8 +8455,7 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithBothServerAndLocalData",
FORM_EVENT_INTERACTED_ONCE, 1);
@@ -9017,7 +8471,7 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -9032,30 +8486,28 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
- personal_data_->ClearProfiles();
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
+ personal_data().ClearProfiles();
{
// Simulate activating the autofill popup for the street field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.Address.WithNoData", FORM_EVENT_INTERACTED_ONCE,
1);
}
// Reset the autofill manager state.
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
RecreateProfile(/*is_server=*/false);
{
// Simulate activating the autofill popup for the street field.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.Address.WithOnlyLocalData",
FORM_EVENT_INTERACTED_ONCE, 1);
@@ -9065,9 +8517,9 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) {
// Test that we log that Profile Autofill is enabled when filling a form.
TEST_F(AutofillMetricsTest, AutofillProfileIsEnabledAtPageLoad) {
base::HistogramTester histogram_tester;
- browser_autofill_manager_->SetAutofillProfileEnabled(true);
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{},
- /*removed_forms=*/{});
+ autofill_manager().SetAutofillProfileEnabled(true);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{},
+ /*removed_forms=*/{});
histogram_tester.ExpectUniqueSample("Autofill.Address.IsEnabled.PageLoad",
true, 1);
}
@@ -9075,9 +8527,9 @@ TEST_F(AutofillMetricsTest, AutofillProfileIsEnabledAtPageLoad) {
// Test that we log that Profile Autofill is disabled when filling a form.
TEST_F(AutofillMetricsTest, AutofillProfileIsDisabledAtPageLoad) {
base::HistogramTester histogram_tester;
- browser_autofill_manager_->SetAutofillProfileEnabled(false);
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{},
- /*removed_forms=*/{});
+ autofill_manager().SetAutofillProfileEnabled(false);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{},
+ /*removed_forms=*/{});
histogram_tester.ExpectUniqueSample("Autofill.Address.IsEnabled.PageLoad",
false, 1);
}
@@ -9085,9 +8537,9 @@ TEST_F(AutofillMetricsTest, AutofillProfileIsDisabledAtPageLoad) {
// Test that we log that CreditCard Autofill is enabled when filling a form.
TEST_F(AutofillMetricsTest, AutofillCreditCardIsEnabledAtPageLoad) {
base::HistogramTester histogram_tester;
- browser_autofill_manager_->SetAutofillCreditCardEnabled(true);
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{},
- /*removed_forms=*/{});
+ autofill_manager().SetAutofillCreditCardEnabled(true);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{},
+ /*removed_forms=*/{});
histogram_tester.ExpectUniqueSample("Autofill.CreditCard.IsEnabled.PageLoad",
true, 1);
}
@@ -9095,9 +8547,9 @@ TEST_F(AutofillMetricsTest, AutofillCreditCardIsEnabledAtPageLoad) {
// Test that we log that CreditCard Autofill is disabled when filling a form.
TEST_F(AutofillMetricsTest, AutofillCreditCardIsDisabledAtPageLoad) {
base::HistogramTester histogram_tester;
- browser_autofill_manager_->SetAutofillCreditCardEnabled(false);
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{},
- /*removed_forms=*/{});
+ autofill_manager().SetAutofillCreditCardEnabled(false);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{},
+ /*removed_forms=*/{});
histogram_tester.ExpectUniqueSample("Autofill.CreditCard.IsEnabled.PageLoad",
false, 1);
}
@@ -9220,7 +8672,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -9236,8 +8688,8 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
// Expect no notifications when the form is first seen.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
histogram_tester.ExpectTotalCount("Autofill.FormSubmittedState", 0);
VerifyDeveloperEngagementUkm(
@@ -9253,8 +8705,8 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
{
base::HistogramTester histogram_tester;
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FormSubmittedState",
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1);
@@ -9271,7 +8723,10 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
AutofillMetrics::FormTypesToBitVector(
{FormType::kAddressForm, FormType::kUnknownFormType})},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}});
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName, 0},
+ {UkmFormSubmittedType::kAutofillFillsName, 0},
+ {UkmFormSubmittedType::kAutocompleteFillsName, 0}});
VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName,
expected_form_submission_ukm_metrics);
@@ -9288,8 +8743,8 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
{
base::HistogramTester histogram_tester;
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FormSubmittedState",
AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1);
@@ -9306,7 +8761,10 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
AutofillMetrics::FormTypesToBitVector(
{FormType::kAddressForm, FormType::kUnknownFormType})},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}});
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName, 0},
+ {UkmFormSubmittedType::kAutofillFillsName, 0},
+ {UkmFormSubmittedType::kAutocompleteFillsName, 0}});
VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName,
expected_form_submission_ukm_metrics);
@@ -9325,8 +8783,8 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
{
base::HistogramTester histogram_tester;
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FormSubmittedState",
AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS,
@@ -9345,7 +8803,10 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
AutofillMetrics::FormTypesToBitVector(
{FormType::kAddressForm, FormType::kUnknownFormType})},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}});
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName, 0},
+ {UkmFormSubmittedType::kAutofillFillsName, 0},
+ {UkmFormSubmittedType::kAutocompleteFillsName, 0}});
VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName,
expected_form_submission_ukm_metrics);
@@ -9356,12 +8817,12 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
}
// Autofilled none with suggestions shown.
- browser_autofill_manager_->DidShowSuggestions(true, form, form.fields[2]);
+ autofill_manager().DidShowSuggestions(true, form, form.fields[2]);
{
base::HistogramTester histogram_tester;
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FormSubmittedState",
AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS, 1);
@@ -9391,7 +8852,10 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
AutofillMetrics::FormTypesToBitVector(
{FormType::kAddressForm, FormType::kUnknownFormType})},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}});
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName, 0},
+ {UkmFormSubmittedType::kAutofillFillsName, 0},
+ {UkmFormSubmittedType::kAutocompleteFillsName, 0}});
VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName,
expected_form_submission_ukm_metrics);
@@ -9408,8 +8872,8 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
{
base::HistogramTester histogram_tester;
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FormSubmittedState",
AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME, 1);
@@ -9426,7 +8890,10 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
AutofillMetrics::FormTypesToBitVector(
{FormType::kAddressForm, FormType::kUnknownFormType})},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}});
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName, 0},
+ {UkmFormSubmittedType::kAutofillFillsName, 0},
+ {UkmFormSubmittedType::kAutocompleteFillsName, 0}});
VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName,
expected_form_submission_ukm_metrics);
@@ -9444,8 +8911,8 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
{
base::HistogramTester histogram_tester;
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FormSubmittedState",
AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL, 1);
@@ -9462,7 +8929,10 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
AutofillMetrics::FormTypesToBitVector(
{FormType::kAddressForm, FormType::kUnknownFormType})},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}});
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName, 0},
+ {UkmFormSubmittedType::kAutofillFillsName, 0},
+ {UkmFormSubmittedType::kAutocompleteFillsName, 0}});
VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName,
expected_form_submission_ukm_metrics);
@@ -9487,7 +8957,7 @@ TEST_F(
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -9507,8 +8977,8 @@ TEST_F(
{
base::HistogramTester histogram_tester;
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
VerifyDeveloperEngagementUkm(
test_ukm_recorder_, form, /*is_for_credit_card=*/false,
{FormType::kAddressForm},
@@ -9522,8 +8992,8 @@ TEST_F(
form.fields[2].value = u"12345678901";
form.fields[2].is_autofilled = true;
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FormSubmittedState",
AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL, 1);
@@ -9542,7 +9012,10 @@ TEST_F(
{UkmFormSubmittedType::kFormTypesName,
AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})},
{UkmFormSubmittedType::kFormSignatureName,
- Collapse(CalculateFormSignature(form)).value()}});
+ Collapse(CalculateFormSignature(form)).value()},
+ {UkmFormSubmittedType::kFormElementUserModificationsName, 0},
+ {UkmFormSubmittedType::kAutofillFillsName, 0},
+ {UkmFormSubmittedType::kAutocompleteFillsName, 0}});
VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName,
expected_form_submission_ukm_metrics);
@@ -9625,15 +9098,15 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_EmptyForm) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
std::vector<FormData> forms(1, form);
// Expect a notification when the form is first seen.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
histogram_tester.ExpectTotalCount("Autofill.UserHappiness", 0);
histogram_tester.ExpectTotalCount("Autofill.UserHappiness.CreditCard", 0);
histogram_tester.ExpectTotalCount("Autofill.UserHappiness.Address", 0);
@@ -9655,7 +9128,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
form.name = u"TestForm";
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
// Construct a valid credit card form.
FormFieldData field;
@@ -9676,8 +9149,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
{
SCOPED_TRACE("First seen");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
AutofillMetrics::FORMS_LOADED, 1);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.CreditCard",
@@ -9688,23 +9161,23 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
{
SCOPED_TRACE("Initial typing");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(), TimeTicks());
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
AutofillMetrics::USER_DID_TYPE, 1);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.CreditCard",
AutofillMetrics::USER_DID_TYPE, 1);
}
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate suggestions shown twice with separate popups.
{
SCOPED_TRACE("Separate pop-ups");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true, form, field);
- browser_autofill_manager_->DidShowSuggestions(true, form, field);
+ autofill_manager().DidShowSuggestions(true, form, field);
+ autofill_manager().DidShowSuggestions(true, form, field);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::SUGGESTIONS_SHOWN, 2);
histogram_tester.ExpectBucketCount(
@@ -9716,16 +9189,16 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
1);
}
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().Reset();
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate suggestions shown twice for a single edit (i.e. multiple
// keystrokes in a single field).
{
SCOPED_TRACE("Multiple keystrokes");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true, form, field);
- browser_autofill_manager_->DidShowSuggestions(false, form, field);
+ autofill_manager().DidShowSuggestions(true, form, field);
+ autofill_manager().DidShowSuggestions(false, form, field);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::SUGGESTIONS_SHOWN, 1);
histogram_tester.ExpectBucketCount(
@@ -9741,7 +9214,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
{
SCOPED_TRACE("Different field");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true, form, form.fields[1]);
+ autofill_manager().DidShowSuggestions(true, form, form.fields[1]);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
AutofillMetrics::SUGGESTIONS_SHOWN, 1);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.CreditCard",
@@ -9752,7 +9225,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
{
SCOPED_TRACE("Invoke autofill");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnDidFillAutofillFormData(form, TimeTicks());
+ autofill_manager().OnDidFillAutofillFormData(form, TimeTicks());
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::USER_DID_AUTOFILL, 1);
histogram_tester.ExpectBucketCount(
@@ -9769,14 +9242,15 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
SCOPED_TRACE("Edit autofilled field");
base::HistogramTester histogram_tester;
std::string guid("10000000-0000-0000-0000-000000000001");
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(guid, std::string()));
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- gfx::RectF(), TimeTicks());
+ autofill_manager().suggestion_generator()->MakeFrontendId(
+ guid, std::string()));
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(), TimeTicks());
// Simulate a second keystroke; make sure we don't log the metric twice.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(), TimeTicks());
histogram_tester.ExpectBucketCount(
"Autofill.UserHappiness",
AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1);
@@ -9795,7 +9269,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
{
SCOPED_TRACE("Invoke autofill again");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnDidFillAutofillFormData(form, TimeTicks());
+ autofill_manager().OnDidFillAutofillFormData(form, TimeTicks());
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
AutofillMetrics::USER_DID_AUTOFILL, 1);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.CreditCard",
@@ -9806,8 +9280,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
{
SCOPED_TRACE("Edit another autofilled field");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(),
+ TimeTicks());
histogram_tester.ExpectUniqueSample(
"Autofill.UserHappiness",
AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1);
@@ -9827,7 +9301,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -9842,8 +9316,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
// Expect a notification when the form is first seen.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
AutofillMetrics::FORMS_LOADED, 1);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.Address",
@@ -9853,8 +9327,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
// Simulate typing.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(), TimeTicks());
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
AutofillMetrics::USER_DID_TYPE, 1);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.Address",
@@ -9864,8 +9338,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
// Simulate suggestions shown twice with separate popups.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true, form, field);
- browser_autofill_manager_->DidShowSuggestions(true, form, field);
+ autofill_manager().DidShowSuggestions(true, form, field);
+ autofill_manager().DidShowSuggestions(true, form, field);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::SUGGESTIONS_SHOWN, 2);
histogram_tester.ExpectBucketCount(
@@ -9877,15 +9351,15 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
1);
}
- browser_autofill_manager_->Reset();
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().Reset();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
// Simulate suggestions shown twice for a single edit (i.e. multiple
// keystrokes in a single field).
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true, form, field);
- browser_autofill_manager_->DidShowSuggestions(false, form, field);
+ autofill_manager().DidShowSuggestions(true, form, field);
+ autofill_manager().DidShowSuggestions(false, form, field);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::SUGGESTIONS_SHOWN, 1);
histogram_tester.ExpectBucketCount(
@@ -9900,7 +9374,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
// Simulate suggestions shown for a different field.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->DidShowSuggestions(true, form, form.fields[1]);
+ autofill_manager().DidShowSuggestions(true, form, form.fields[1]);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
AutofillMetrics::SUGGESTIONS_SHOWN, 1);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.Address",
@@ -9910,7 +9384,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
// Simulate invoking autofill.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnDidFillAutofillFormData(form, TimeTicks());
+ autofill_manager().OnDidFillAutofillFormData(form, TimeTicks());
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::USER_DID_AUTOFILL, 1);
histogram_tester.ExpectBucketCount(
@@ -9926,14 +9400,15 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
{
base::HistogramTester histogram_tester;
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- gfx::RectF(), TimeTicks());
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(), TimeTicks());
// Simulate a second keystroke; make sure we don't log the metric twice.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(), TimeTicks());
histogram_tester.ExpectBucketCount(
"Autofill.UserHappiness",
AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1);
@@ -9951,7 +9426,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
// Simulate invoking autofill again.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnDidFillAutofillFormData(form, TimeTicks());
+ autofill_manager().OnDidFillAutofillFormData(form, TimeTicks());
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
AutofillMetrics::USER_DID_AUTOFILL, 1);
histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.Address",
@@ -9961,8 +9436,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
// Simulate editing another autofilled field.
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(),
+ TimeTicks());
histogram_tester.ExpectUniqueSample(
"Autofill.UserHappiness",
AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1);
@@ -9971,7 +9446,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1);
}
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
VerifyUkm(test_ukm_recorder_, form, UkmInteractedWithFormType::kEntryName,
{{{UkmInteractedWithFormType::kIsForCreditCardName, false},
@@ -10088,7 +9563,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -10125,14 +9600,15 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
{
SCOPED_TRACE("Test 1");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- base::TimeTicks parse_time = browser_autofill_manager_->form_structures()
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ base::TimeTicks parse_time = autofill_manager()
+ .form_structures()
.begin()
->second->form_parsed_timestamp();
test_clock.SetNowTicks(parse_time + base::Microseconds(17));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectTotalCount(
"Autofill.FillDuration.FromLoad.WithAutofill", 0);
@@ -10143,24 +9619,25 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
histogram_tester.ExpectTotalCount(
"Autofill.FillDuration.FromInteraction.WithoutAutofill", 0);
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
}
// Expect metric to be logged if the user manually edited a form field.
{
SCOPED_TRACE("Test 2");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- base::TimeTicks parse_time = browser_autofill_manager_->form_structures()
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ base::TimeTicks parse_time = autofill_manager()
+ .form_structures()
.begin()
->second->form_parsed_timestamp();
- browser_autofill_manager_->OnTextFieldDidChange(
- form, form.fields.front(), gfx::RectF(),
- parse_time + base::Microseconds(3));
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(),
+ parse_time + base::Microseconds(3));
test_clock.SetNowTicks(parse_time + base::Microseconds(17));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectTotalCount(
"Autofill.FillDuration.FromLoad.WithAutofill", 0);
@@ -10172,7 +9649,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
"Autofill.FillDuration.FromInteraction.WithoutAutofill", 14, 1);
// We expected an upload to be triggered when the manager is reset.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
}
// Expect metric to be logged if the user autofilled the form.
@@ -10180,16 +9657,17 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
{
SCOPED_TRACE("Test 3");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- base::TimeTicks parse_time = browser_autofill_manager_->form_structures()
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ base::TimeTicks parse_time = autofill_manager()
+ .form_structures()
.begin()
->second->form_parsed_timestamp();
- browser_autofill_manager_->OnDidFillAutofillFormData(
+ autofill_manager().OnDidFillAutofillFormData(
form, parse_time + base::Microseconds(5));
test_clock.SetNowTicks(parse_time + base::Microseconds(17));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FillDuration.FromLoad.WithAutofill", 16, 1);
@@ -10201,7 +9679,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
"Autofill.FillDuration.FromInteraction.WithoutAutofill", 0);
// We expected an upload to be triggered when the manager is reset.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
}
// Expect metric to be logged if the user both manually filled some fields
@@ -10211,20 +9689,21 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
SCOPED_TRACE("Test 4");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- base::TimeTicks parse_time = browser_autofill_manager_->form_structures()
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ base::TimeTicks parse_time = autofill_manager()
+ .form_structures()
.begin()
->second->form_parsed_timestamp();
- browser_autofill_manager_->OnDidFillAutofillFormData(
+ autofill_manager().OnDidFillAutofillFormData(
form, parse_time + base::Microseconds(5));
- browser_autofill_manager_->OnTextFieldDidChange(
- form, form.fields.front(), gfx::RectF(),
- parse_time + base::Microseconds(3));
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(),
+ parse_time + base::Microseconds(3));
test_clock.SetNowTicks(parse_time + base::Microseconds(17));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FillDuration.FromLoad.WithAutofill", 16, 1);
@@ -10236,7 +9715,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
"Autofill.FillDuration.FromInteraction.WithoutAutofill", 0);
// We expected an upload to be triggered when the manager is reset.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
}
// Make sure that loading another form doesn't affect metrics from the first
@@ -10244,21 +9723,22 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
{
SCOPED_TRACE("Test 5");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- base::TimeTicks parse_time = browser_autofill_manager_->form_structures()
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ base::TimeTicks parse_time = autofill_manager()
+ .form_structures()
.begin()
->second->form_parsed_timestamp();
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/second_forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->OnDidFillAutofillFormData(
+ autofill_manager().OnFormsSeen(/*updated_forms=*/second_forms,
+ /*removed_forms=*/{});
+ autofill_manager().OnDidFillAutofillFormData(
form, parse_time + base::Microseconds(5));
- browser_autofill_manager_->OnTextFieldDidChange(
- form, form.fields.front(), gfx::RectF(),
- parse_time + base::Microseconds(3));
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(),
+ parse_time + base::Microseconds(3));
test_clock.SetNowTicks(parse_time + base::Microseconds(17));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.FillDuration.FromLoad.WithAutofill", 16, 1);
@@ -10270,7 +9750,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
"Autofill.FillDuration.FromInteraction.WithoutAutofill", 0);
// We expected an upload to be triggered when the manager is reset.
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
}
// Make sure that submitting a form that was loaded later will report the
@@ -10278,19 +9758,18 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
{
SCOPED_TRACE("Test 6");
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/second_forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/second_forms,
+ /*removed_forms=*/{});
base::TimeTicks parse_time{};
- for (const auto& kv : browser_autofill_manager_->form_structures()) {
+ for (const auto& kv : autofill_manager().form_structures()) {
if (kv.second->form_parsed_timestamp() > parse_time)
parse_time = kv.second->form_parsed_timestamp();
}
test_clock.SetNowTicks(parse_time + base::Microseconds(17));
- browser_autofill_manager_->OnFormSubmitted(
- second_form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(second_form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectTotalCount(
"Autofill.FillDuration.FromLoad.WithAutofill", 0);
@@ -10301,7 +9780,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
histogram_tester.ExpectTotalCount(
"Autofill.FillDuration.FromInteraction.WithoutAutofill", 0);
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
}
}
@@ -10524,7 +10003,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
// Create the form's fields.
FormFieldData field;
@@ -10591,59 +10070,51 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) {
// Expect to log NEW_PROFILE_CREATED for the metric since a new profile is
// submitted.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::NEW_PROFILE_CREATED, 1);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::EXISTING_PROFILE_USED, 0);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::EXISTING_PROFILE_UPDATED,
- 0);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.ProfileActionOnFormSubmitted"),
+ BucketsAre(Bucket(AutofillMetrics::NEW_PROFILE_CREATED, 1),
+ Bucket(AutofillMetrics::EXISTING_PROFILE_USED, 0),
+ Bucket(AutofillMetrics::EXISTING_PROFILE_UPDATED, 0)));
// Expect to log EXISTING_PROFILE_USED for the metric since the same profile
// is submitted.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/second_forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->OnFormSubmitted(
- second_form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::NEW_PROFILE_CREATED, 1);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::EXISTING_PROFILE_USED, 1);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::EXISTING_PROFILE_UPDATED,
- 0);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/second_forms,
+ /*removed_forms=*/{});
+ autofill_manager().OnFormSubmitted(second_form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.ProfileActionOnFormSubmitted"),
+ BucketsAre(Bucket(AutofillMetrics::NEW_PROFILE_CREATED, 1),
+ Bucket(AutofillMetrics::EXISTING_PROFILE_USED, 1),
+ Bucket(AutofillMetrics::EXISTING_PROFILE_UPDATED, 0)));
// Expect to log NEW_PROFILE_CREATED for the metric since a new profile is
// submitted.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/third_forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->OnFormSubmitted(
- third_form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::NEW_PROFILE_CREATED, 2);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::EXISTING_PROFILE_USED, 1);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::EXISTING_PROFILE_UPDATED,
- 0);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/third_forms,
+ /*removed_forms=*/{});
+ autofill_manager().OnFormSubmitted(third_form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.ProfileActionOnFormSubmitted"),
+ BucketsAre(Bucket(AutofillMetrics::NEW_PROFILE_CREATED, 2),
+ Bucket(AutofillMetrics::EXISTING_PROFILE_USED, 1),
+ Bucket(AutofillMetrics::EXISTING_PROFILE_UPDATED, 0)));
// Expect to log EXISTING_PROFILE_UPDATED for the metric since the profile was
// updated.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/fourth_forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->OnFormSubmitted(
- fourth_form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::NEW_PROFILE_CREATED, 2);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::EXISTING_PROFILE_USED, 1);
- histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted",
- AutofillMetrics::EXISTING_PROFILE_UPDATED,
- 1);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/fourth_forms,
+ /*removed_forms=*/{});
+ autofill_manager().OnFormSubmitted(fourth_form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.ProfileActionOnFormSubmitted"),
+ BucketsAre(Bucket(AutofillMetrics::NEW_PROFILE_CREATED, 2),
+ Bucket(AutofillMetrics::EXISTING_PROFILE_USED, 1),
+ Bucket(AutofillMetrics::EXISTING_PROFILE_UPDATED, 1)));
}
// Test class that shares setup code for testing ParseQueryResponse.
@@ -10697,8 +10168,8 @@ class AutofillMetricsParseQueryResponseTest : public testing::Test {
namespace {
void AddFieldSuggestionToForm(
- ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion,
- autofill::FieldSignature field_signature,
+ AutofillQueryResponse_FormSuggestion* form_suggestion,
+ FieldSignature field_signature,
int field_type) {
auto* field_suggestion = form_suggestion->add_field_suggestions();
field_suggestion->set_field_signature(field_signature.value());
@@ -10818,7 +10289,7 @@ TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) {
form.action = GURL("http://example.com/submit.html");
GURL frame_origin("http://example_root.com/form.html");
form.main_frame_origin = url::Origin::Create(frame_origin);
- autofill_client_.set_form_origin(frame_origin);
+ autofill_client_->set_form_origin(frame_origin);
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -10833,14 +10304,13 @@ TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) {
field_types.push_back(CREDIT_CARD_EXP_MONTH);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate an Autofill query on a credit card field.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
}
@@ -10848,8 +10318,8 @@ TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) {
// Simulate submitting the credit card form.
{
base::HistogramTester histograms;
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histograms.ExpectBucketCount(
"Autofill.FormEvents.CreditCard.OnNonsecurePage",
FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
@@ -10895,14 +10365,13 @@ TEST_F(AutofillMetricsTest,
field_types.push_back(CREDIT_CARD_EXP_MONTH);
// Simulate having seen this form on page load.
- // |form_structure| will be owned by |browser_autofill_manager_|.
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ // |form_structure| will be owned by `autofill_manager()`.
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate an Autofill query on a credit card field.
{
base::UserActionTester user_action_tester;
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, field);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
}
@@ -10910,8 +10379,8 @@ TEST_F(AutofillMetricsTest,
// Simulate submitting the credit card form.
{
base::HistogramTester histograms;
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histograms.ExpectBucketCount("Autofill.FormEvents.CreditCard",
FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
histograms.ExpectBucketCount("Autofill.FormEvents.CreditCard",
@@ -10928,10 +10397,10 @@ TEST_F(AutofillMetricsTest,
TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric) {
GURL url("https://www.google.com");
int upload_decision = 1;
- autofill_client_.set_form_origin(url);
+ autofill_client_->set_form_origin(url);
AutofillMetrics::LogCardUploadDecisionsUkm(test_ukm_recorder_,
- autofill_client_.GetUkmSourceId(),
+ autofill_client_->GetUkmSourceId(),
url, upload_decision);
auto entries = test_ukm_recorder_->GetEntriesByName(
UkmCardUploadDecisionType::kEntryName);
@@ -10949,10 +10418,10 @@ TEST_F(AutofillMetricsTest, RecordDeveloperEngagementMetric) {
GURL url("https://www.google.com");
int form_structure_metric = 1;
FormSignature form_signature(100);
- autofill_client_.set_form_origin(url);
+ autofill_client_->set_form_origin(url);
AutofillMetrics::LogDeveloperEngagementUkm(
- test_ukm_recorder_, autofill_client_.GetUkmSourceId(), url, true,
+ test_ukm_recorder_, autofill_client_->GetUkmSourceId(), url, true,
{FormType::kCreditCardForm}, form_structure_metric, form_signature);
auto entries = test_ukm_recorder_->GetEntriesByName(
UkmDeveloperEngagementType::kEntryName);
@@ -11006,7 +10475,7 @@ TEST_F(AutofillMetricsTest, DISABLED_AutofillSuggestionShownTest) {
form.name = u"TestForm";
form.url = GURL("http://example_cc.com/form.html");
form.action = GURL("http://example_cc.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -11019,11 +10488,11 @@ TEST_F(AutofillMetricsTest, DISABLED_AutofillSuggestionShownTest) {
test::CreateTestFormField("Month", "card_month", "", "text", &field);
form.fields.push_back(field);
field_types.push_back(CREDIT_CARD_EXP_MONTH);
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
// Simulate and Autofill query on credit card name field.
- browser_autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form,
- form.fields[0]);
+ autofill_manager().DidShowSuggestions(true /* is_new_popup */, form,
+ form.fields[0]);
VerifyUkm(
test_ukm_recorder_, form, UkmSuggestionsShownType::kEntryName,
{{{UkmSuggestionsShownType::kMillisecondsSinceFormParsedName, 0},
@@ -11058,50 +10527,45 @@ TEST_F(AutofillMetricsTest, DynamicFormMetrics) {
// Simulate seeing.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->AddSeenForm(form, field_types, field_types);
+ autofill_manager().AddSeenForm(form, field_types, field_types);
std::string guid(kTestGuid);
// Simulate checking whether to fill a dynamic form before the form was filled
// initially.
FormStructure form_structure(form);
- browser_autofill_manager_->ShouldTriggerRefillForTest(form_structure);
+ autofill_manager().ShouldTriggerRefillForTest(form_structure);
histogram_tester.ExpectTotalCount("Autofill.FormEvents.Address", 0);
// Simulate filling the form.
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Simulate checking whether to fill a dynamic form after the form was filled
// initially.
- browser_autofill_manager_->ShouldTriggerRefillForTest(form_structure);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_DID_SEE_FILLABLE_DYNAMIC_FORM,
- 1);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_DID_DYNAMIC_REFILL, 0);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_DYNAMIC_CHANGE_AFTER_REFILL, 0);
+ autofill_manager().ShouldTriggerRefillForTest(form_structure);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.Address"),
+ BucketsInclude(Bucket(FORM_EVENT_DID_SEE_FILLABLE_DYNAMIC_FORM, 1),
+ Bucket(FORM_EVENT_DID_DYNAMIC_REFILL, 0),
+ Bucket(FORM_EVENT_DYNAMIC_CHANGE_AFTER_REFILL, 0)));
// Trigger a refill, the refill metric should be updated.
- browser_autofill_manager_->TriggerRefillForTest(form);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_DID_SEE_FILLABLE_DYNAMIC_FORM,
- 1);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_DID_DYNAMIC_REFILL, 1);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_DYNAMIC_CHANGE_AFTER_REFILL, 0);
+ autofill_manager().TriggerRefillForTest(form);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.Address"),
+ BucketsInclude(Bucket(FORM_EVENT_DID_SEE_FILLABLE_DYNAMIC_FORM, 1),
+ Bucket(FORM_EVENT_DID_DYNAMIC_REFILL, 1),
+ Bucket(FORM_EVENT_DYNAMIC_CHANGE_AFTER_REFILL, 0)));
// Trigger a check to see whether a refill should happen. The
- browser_autofill_manager_->ShouldTriggerRefillForTest(form_structure);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_DID_SEE_FILLABLE_DYNAMIC_FORM,
- 2);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_DID_DYNAMIC_REFILL, 1);
- histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address",
- FORM_EVENT_DYNAMIC_CHANGE_AFTER_REFILL, 1);
+ autofill_manager().ShouldTriggerRefillForTest(form_structure);
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("Autofill.FormEvents.Address"),
+ BucketsInclude(Bucket(FORM_EVENT_DID_SEE_FILLABLE_DYNAMIC_FORM, 2),
+ Bucket(FORM_EVENT_DID_DYNAMIC_REFILL, 1),
+ Bucket(FORM_EVENT_DYNAMIC_CHANGE_AFTER_REFILL, 1)));
}
// Tests that the LogUserHappinessBySecurityLevel are recorded correctly.
@@ -11169,7 +10633,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -11184,10 +10648,10 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) {
// Simulate seeing the form.
{
base::HistogramTester histogram_tester;
- autofill_client_.set_security_level(
+ autofill_client_->set_security_level(
security_state::SecurityLevel::DANGEROUS);
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
histogram_tester.ExpectBucketCount(
"Autofill.UserHappiness.Address.DANGEROUS",
AutofillMetrics::FORMS_LOADED, 1);
@@ -11196,9 +10660,10 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) {
// Simulate suggestions shown twice with separate popups.
{
base::HistogramTester histogram_tester;
- autofill_client_.set_security_level(security_state::SecurityLevel::WARNING);
- browser_autofill_manager_->DidShowSuggestions(true, form, field);
- browser_autofill_manager_->DidShowSuggestions(true, form, field);
+ autofill_client_->set_security_level(
+ security_state::SecurityLevel::WARNING);
+ autofill_manager().DidShowSuggestions(true, form, field);
+ autofill_manager().DidShowSuggestions(true, form, field);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Address.WARNING",
AutofillMetrics::SUGGESTIONS_SHOWN, 2);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Address.WARNING",
@@ -11461,7 +10926,7 @@ TEST_F(AutofillMetricsTest,
// frame has no form.
TEST_F(AutofillMetricsTest, FrameHasNoForm) {
base::HistogramTester histogram_tester;
- browser_autofill_manager_.reset();
+ autofill_driver_.reset();
histogram_tester.ExpectTotalCount(
"Autofill.WebOTP.OneTimeCode.FromAutocomplete", 0);
}
@@ -11475,7 +10940,7 @@ TEST_F(AutofillMetricsTest, FrameHasAutocompleteOneTimeCode) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
@@ -11488,9 +10953,9 @@ TEST_F(AutofillMetricsTest, FrameHasAutocompleteOneTimeCode) {
forms_with_one_time_code.back().fields.push_back(field);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(
+ autofill_manager().OnFormsSeen(
/*updated_forms=*/forms_with_one_time_code, /*removed_forms=*/{});
- browser_autofill_manager_.reset();
+ autofill_driver_.reset();
// Verifies that autocomplete="one-time-code" in a form is correctly recorded.
histogram_tester.ExpectBucketCount(
"Autofill.WebOTP.OneTimeCode.FromAutocomplete",
@@ -11509,7 +10974,7 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHaveAutocompleteOneTimeCode) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<FormData> forms_without_one_time_code(1, form);
@@ -11518,9 +10983,9 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHaveAutocompleteOneTimeCode) {
forms_without_one_time_code.back().fields.push_back(field);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(
+ autofill_manager().OnFormsSeen(
/*updated_forms=*/forms_without_one_time_code, /*removed_forms=*/{});
- browser_autofill_manager_.reset();
+ autofill_driver_.reset();
histogram_tester.ExpectBucketCount(
"Autofill.WebOTP.OneTimeCode.FromAutocomplete",
/* has_one_time_code */ 0,
@@ -11538,7 +11003,7 @@ TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithoutAutocomplete) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
@@ -11554,9 +11019,9 @@ TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithoutAutocomplete) {
forms_with_phone_number.back().fields.push_back(field);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(
+ autofill_manager().OnFormsSeen(
/*updated_forms=*/forms_with_phone_number, /*removed_forms=*/{});
- browser_autofill_manager_.reset();
+ autofill_driver_.reset();
histogram_tester.ExpectBucketCount(
"Autofill.WebOTP.PhoneNumberCollection.ParseResult",
/* has_phone_number_field */ 1,
@@ -11574,7 +11039,7 @@ TEST_F(AutofillMetricsTest, FrameHasSinglePhoneNumberFieldWithoutAutocomplete) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<FormData> forms_with_single_phone_number_field(1, form);
@@ -11585,10 +11050,10 @@ TEST_F(AutofillMetricsTest, FrameHasSinglePhoneNumberFieldWithoutAutocomplete) {
forms_with_single_phone_number_field.back().fields.push_back(field);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(
+ autofill_manager().OnFormsSeen(
/*updated_forms=*/forms_with_single_phone_number_field,
/*removed_forms=*/{});
- browser_autofill_manager_.reset();
+ autofill_driver_.reset();
histogram_tester.ExpectBucketCount(
"Autofill.WebOTP.PhoneNumberCollection.ParseResult",
/* has_phone_number_field */ 0,
@@ -11601,14 +11066,14 @@ TEST_F(AutofillMetricsTest, FrameHasSinglePhoneNumberFieldWithoutAutocomplete) {
// autocomplete attribute.
TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithAutocomplete) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
AddAutoCompleteFieldToForm("phone", form);
std::vector<FormData> forms_with_phone_number(1, form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(
+ autofill_manager().OnFormsSeen(
/*updated_forms=*/forms_with_phone_number, /*removed_forms=*/{});
- browser_autofill_manager_.reset();
+ autofill_driver_.reset();
histogram_tester.ExpectBucketCount(
"Autofill.WebOTP.PhoneNumberCollection.ParseResult",
/* has_phone_number_field */ 1,
@@ -11626,7 +11091,7 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHavePhoneNumberField) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<FormData> forms_without_phone_number(1, form);
@@ -11635,9 +11100,9 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHavePhoneNumberField) {
forms_without_phone_number.back().fields.push_back(field);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(
+ autofill_manager().OnFormsSeen(
/*updated_forms=*/forms_without_phone_number, /*removed_forms=*/{});
- browser_autofill_manager_.reset();
+ autofill_driver_.reset();
histogram_tester.ExpectBucketCount(
"Autofill.WebOTP.PhoneNumberCollection.ParseResult",
/* has_phone_number_field */ 0,
@@ -11652,17 +11117,14 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHavePhoneNumberField) {
// Verify that we correctly log PhoneCollectionMetricState::kNone.
TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateNone) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
AddAutoCompleteFieldToForm("password", form);
std::vector<FormData> forms(1, form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- autofill_driver_->SetBrowserAutofillManager(
- std::move(browser_autofill_manager_));
- static_cast<ContentAutofillDriver*>(autofill_driver_.get())
- ->ReportAutofillWebOTPMetrics(false);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().ReportAutofillWebOTPMetrics(false);
histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
PhoneCollectionMetricState::kNone, 1);
histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
@@ -11672,17 +11134,14 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateNone) {
// Verify that we correctly log PhoneCollectionMetricState::kOTC.
TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateOTC) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
AddAutoCompleteFieldToForm("one-time-code", form);
std::vector<FormData> forms(1, form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- autofill_driver_->SetBrowserAutofillManager(
- std::move(browser_autofill_manager_));
- static_cast<ContentAutofillDriver*>(autofill_driver_.get())
- ->ReportAutofillWebOTPMetrics(false);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().ReportAutofillWebOTPMetrics(false);
histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
PhoneCollectionMetricState::kOTC, 1);
histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
@@ -11694,10 +11153,7 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateWebOTP) {
// If WebOTP is used, even if there is no form on the page we still need to
// report it.
base::HistogramTester histogram_tester;
- autofill_driver_->SetBrowserAutofillManager(
- std::move(browser_autofill_manager_));
- static_cast<ContentAutofillDriver*>(autofill_driver_.get())
- ->ReportAutofillWebOTPMetrics(true);
+ autofill_manager().ReportAutofillWebOTPMetrics(true);
histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
PhoneCollectionMetricState::kWebOTP, 1);
histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
@@ -11707,17 +11163,14 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateWebOTP) {
// Verify that we correctly log PhoneCollectionMetricState::kWebOTPPlusOTC.
TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateWebOTPPlusOTC) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
AddAutoCompleteFieldToForm("one-time-code", form);
std::vector<FormData> forms(1, form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- autofill_driver_->SetBrowserAutofillManager(
- std::move(browser_autofill_manager_));
- static_cast<ContentAutofillDriver*>(autofill_driver_.get())
- ->ReportAutofillWebOTPMetrics(true);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().ReportAutofillWebOTPMetrics(true);
histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
PhoneCollectionMetricState::kWebOTPPlusOTC,
1);
@@ -11728,17 +11181,14 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateWebOTPPlusOTC) {
// Verify that we correctly log PhoneCollectionMetricState::kPhone.
TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhone) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
AddAutoCompleteFieldToForm("tel", form);
std::vector<FormData> forms(1, form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- autofill_driver_->SetBrowserAutofillManager(
- std::move(browser_autofill_manager_));
- static_cast<ContentAutofillDriver*>(autofill_driver_.get())
- ->ReportAutofillWebOTPMetrics(false);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().ReportAutofillWebOTPMetrics(false);
histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
PhoneCollectionMetricState::kPhone, 1);
histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
@@ -11748,18 +11198,15 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhone) {
// Verify that we correctly log PhoneCollectionMetricState::kPhonePlusOTC.
TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhonePlusOTC) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
AddAutoCompleteFieldToForm("tel", form);
AddAutoCompleteFieldToForm("one-time-code", form);
std::vector<FormData> forms(1, form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- autofill_driver_->SetBrowserAutofillManager(
- std::move(browser_autofill_manager_));
- static_cast<ContentAutofillDriver*>(autofill_driver_.get())
- ->ReportAutofillWebOTPMetrics(false);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().ReportAutofillWebOTPMetrics(false);
histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
PhoneCollectionMetricState::kPhonePlusOTC,
1);
@@ -11770,17 +11217,14 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhonePlusOTC) {
// Verify that we correctly log PhoneCollectionMetricState::kPhonePlusWebOTP.
TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhonePlusWebOTP) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
AddAutoCompleteFieldToForm("tel", form);
std::vector<FormData> forms(1, form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- autofill_driver_->SetBrowserAutofillManager(
- std::move(browser_autofill_manager_));
- static_cast<ContentAutofillDriver*>(autofill_driver_.get())
- ->ReportAutofillWebOTPMetrics(true);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().ReportAutofillWebOTPMetrics(true);
histogram_tester.ExpectBucketCount(
"Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
PhoneCollectionMetricState::kPhonePlusWebOTP, 1);
@@ -11793,18 +11237,15 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhonePlusWebOTP) {
TEST_F(AutofillMetricsTest,
WebOTPPhoneCollectionMetricsStatePhonePlusWebOTPPlusOTC) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
AddAutoCompleteFieldToForm("tel", form);
AddAutoCompleteFieldToForm("one-time-code", form);
std::vector<FormData> forms(1, form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- autofill_driver_->SetBrowserAutofillManager(
- std::move(browser_autofill_manager_));
- static_cast<ContentAutofillDriver*>(autofill_driver_.get())
- ->ReportAutofillWebOTPMetrics(true);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().ReportAutofillWebOTPMetrics(true);
histogram_tester.ExpectBucketCount(
"Autofill.WebOTP.PhonePlusWebOTPPlusOTC",
PhoneCollectionMetricState::kPhonePlusWebOTPPlusOTC, 1);
@@ -11819,7 +11260,7 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateLoggedToUKM) {
ASSERT_TRUE(entries.empty());
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
// Document collects phone number
AddAutoCompleteFieldToForm("tel", form);
// Document uses OntTimeCode
@@ -11827,12 +11268,9 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateLoggedToUKM) {
std::vector<FormData> forms(1, form);
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- autofill_driver_->SetBrowserAutofillManager(
- std::move(browser_autofill_manager_));
- static_cast<ContentAutofillDriver*>(autofill_driver_.get())
- ->ReportAutofillWebOTPMetrics(/* Document uses WebOTP */ true);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().ReportAutofillWebOTPMetrics(true);
entries = test_ukm_recorder_->GetEntriesByName(
ukm::builders::WebOTPImpact::kEntryName);
@@ -11855,7 +11293,7 @@ TEST_F(AutofillMetricsTest, AutocompleteOneTimeCodeFormFilledDuration) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
test::CreateTestFormField("", "", "", "password", &field);
@@ -11867,41 +11305,43 @@ TEST_F(AutofillMetricsTest, AutocompleteOneTimeCodeFormFilledDuration) {
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- base::TimeTicks parse_time = browser_autofill_manager_->form_structures()
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ base::TimeTicks parse_time = autofill_manager()
+ .form_structures()
.begin()
->second->form_parsed_timestamp();
test_clock.SetNowTicks(parse_time + base::Microseconds(17));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectTotalCount(
"Autofill.WebOTP.OneTimeCode.FillDuration.FromLoad", 1);
histogram_tester.ExpectUniqueSample(
"Autofill.WebOTP.OneTimeCode.FillDuration.FromLoad", 16, 1);
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
}
{
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- base::TimeTicks parse_time = browser_autofill_manager_->form_structures()
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ base::TimeTicks parse_time = autofill_manager()
+ .form_structures()
.begin()
->second->form_parsed_timestamp();
- browser_autofill_manager_->OnDidFillAutofillFormData(
+ autofill_manager().OnDidFillAutofillFormData(
form, parse_time + base::Microseconds(5));
- browser_autofill_manager_->OnTextFieldDidChange(
- form, form.fields.front(), gfx::RectF(),
- parse_time + base::Microseconds(3));
+ autofill_manager().OnTextFieldDidChange(form, form.fields.front(),
+ gfx::RectF(),
+ parse_time + base::Microseconds(3));
test_clock.SetNowTicks(parse_time + base::Microseconds(17));
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.WebOTP.OneTimeCode.FillDuration.FromInteraction", 14, 1);
- browser_autofill_manager_->Reset();
+ autofill_manager().Reset();
}
}
@@ -12011,9 +11451,9 @@ TEST_F(AutofillMetricsTest, FormEventMetrics_BySyncState) {
FormData form;
FormStructure form_structure(form);
std::vector<FormData> forms(1, form);
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/forms,
- /*removed_forms=*/{});
- browser_autofill_manager_->Reset();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/forms,
+ /*removed_forms=*/{});
+ autofill_manager().Reset();
{
base::HistogramTester histogram_tester;
@@ -12111,7 +11551,7 @@ TEST_P(AutofillMetricsFunnelTest, LogFunnelMetrics) {
form.name = u"TestForm";
form.url = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -12134,44 +11574,41 @@ TEST_P(AutofillMetricsFunnelTest, LogFunnelMetrics) {
const bool user_submitted_form = GetParam() >= 4;
// Simulate that the autofill manager has seen this form on page load.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{form},
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{form},
+ /*removed_forms=*/{});
if (!user_saw_suggestion) {
// Remove the profile to prevent suggestion from being shown.
- personal_data_->ClearProfiles();
+ personal_data().ClearProfiles();
}
// Simulate interacting with the form.
if (user_interacted_with_form) {
- browser_autofill_manager_->OnAskForValuesToFill(
- /*query_id=*/0, form, form.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]);
}
// Simulate seeing a suggestion.
if (user_saw_suggestion) {
- browser_autofill_manager_->DidShowSuggestions(
+ autofill_manager().DidShowSuggestions(
/*has_autofill_suggestions=*/true, form, form.fields[0]);
}
// Simulate filling the form.
if (user_accepted_suggestion) {
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, /*query_id=*/0, form,
form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(),
- kTestGuid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ kTestGuid));
}
// Simulate form submission.
if (user_submitted_form) {
- browser_autofill_manager_->OnFormSubmitted(
- form, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
}
- // Reset |browser_autofill_manager_| to commit UMA metrics.
- browser_autofill_manager_.reset();
+ ResetDriverToCommitMetrics();
// Phase 2: Validate Funnel expectations.
histogram_tester.ExpectBucketCount("Autofill.Funnel.ParsedAsType.Address", 1,
@@ -12267,7 +11704,7 @@ TEST_F(AutofillMetricsFunnelTest, AblationState) {
form.name = u"TestForm";
form.url = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -12284,24 +11721,21 @@ TEST_F(AutofillMetricsFunnelTest, AblationState) {
base::HistogramTester histogram_tester;
// Simulate that the autofill manager has seen this form on page load.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{form},
- /*removed_forms=*/{});
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{form},
+ /*removed_forms=*/{});
// Simulate interacting with the form.
- browser_autofill_manager_->OnAskForValuesToFill(
- /*query_id=*/0, form, form.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]);
// Don't simulate a suggestion but simulate the user typing.
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[0],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(),
+ TimeTicks());
// Simulate form submission.
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
- // Reset |browser_autofill_manager_| to commit UMA metrics.
- browser_autofill_manager_.reset();
+ ResetDriverToCommitMetrics();
// Phase 2: Validate Funnel expectations.
const char* kMetrics[] = {
@@ -12346,7 +11780,8 @@ void AutofillMetricsKeyMetricsTest::SetUp() {
form_.name = u"TestForm";
form_.url = GURL("http://example.com/form.html");
form_.action = GURL("http://example.com/submit.html");
- form_.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
+ form_.main_frame_origin =
+ url::Origin::Create(autofill_client_->form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -12361,7 +11796,7 @@ void AutofillMetricsKeyMetricsTest::SetUp() {
field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form_, field_types, field_types);
+ autofill_manager().AddSeenForm(form_, field_types, field_types);
}
// Validate Autofill.KeyMetrics.* in case the user submits the empty form.
@@ -12371,18 +11806,15 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogEmptyForm) {
base::HistogramTester histogram_tester;
// Simulate page load.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{form_},
- /*removed_forms=*/{});
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form_, form_.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{form_},
+ /*removed_forms=*/{});
+ autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]);
// Simulate form submission.
- browser_autofill_manager_->OnFormSubmitted(form_, false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form_, false,
+ SubmissionSource::FORM_SUBMISSION);
- // Reset |browser_autofill_manager_| to commit UMA metrics.
- browser_autofill_manager_.reset();
+ ResetDriverToCommitMetrics();
histogram_tester.ExpectBucketCount(
"Autofill.KeyMetrics.FillingReadiness.Address", 1, 1);
@@ -12402,25 +11834,22 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogNoProfile) {
base::HistogramTester histogram_tester;
// Simulate that no data is available.
- personal_data_->ClearProfiles();
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{form_},
- /*removed_forms=*/{});
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form_, form_.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ personal_data().ClearProfiles();
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{form_},
+ /*removed_forms=*/{});
+ autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]);
// Simulate user typing the address.
- browser_autofill_manager_->OnTextFieldDidChange(form_, form_.fields[0],
- gfx::RectF(), TimeTicks());
- browser_autofill_manager_->OnTextFieldDidChange(form_, form_.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form_, form_.fields[0], gfx::RectF(),
+ TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form_, form_.fields[1], gfx::RectF(),
+ TimeTicks());
// Simulate form submission.
- browser_autofill_manager_->OnFormSubmitted(form_, false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form_, false,
+ SubmissionSource::FORM_SUBMISSION);
- // Reset |browser_autofill_manager_| to commit UMA metrics.
- browser_autofill_manager_.reset();
+ ResetDriverToCommitMetrics();
histogram_tester.ExpectBucketCount(
"Autofill.KeyMetrics.FillingReadiness.Address", 0, 1);
@@ -12439,26 +11868,23 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserDoesNotAcceptSuggestion) {
base::HistogramTester histogram_tester;
// Simulate that suggestion is shown but user does not accept it.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{form_},
- /*removed_forms=*/{});
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form_, form_.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{form_},
+ /*removed_forms=*/{});
+ autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]);
+ autofill_manager().DidShowSuggestions(
/*has_autofill_suggestions=*/true, form_, form_.fields[0]);
// Simulate user typing the address.
- browser_autofill_manager_->OnTextFieldDidChange(form_, form_.fields[0],
- gfx::RectF(), TimeTicks());
- browser_autofill_manager_->OnTextFieldDidChange(form_, form_.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form_, form_.fields[0], gfx::RectF(),
+ TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form_, form_.fields[1], gfx::RectF(),
+ TimeTicks());
// Simulate form submission.
- browser_autofill_manager_->OnFormSubmitted(form_, false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form_, false,
+ SubmissionSource::FORM_SUBMISSION);
- // Reset |browser_autofill_manager_| to commit UMA metrics.
- browser_autofill_manager_.reset();
+ ResetDriverToCommitMetrics();
histogram_tester.ExpectBucketCount(
"Autofill.KeyMetrics.FillingReadiness.Address", 1, 1);
@@ -12477,28 +11903,25 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledData) {
base::HistogramTester histogram_tester;
// Simulate that suggestion is shown and user accepts it.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{form_},
- /*removed_forms=*/{});
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form_, form_.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{form_},
+ /*removed_forms=*/{});
+ autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]);
+ autofill_manager().DidShowSuggestions(
/*has_autofill_suggestions=*/true, form_, form_.fields[0]);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form_, form_.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(),
- kTestGuid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ kTestGuid));
// Simulate user fixing the address.
- browser_autofill_manager_->OnTextFieldDidChange(form_, form_.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form_, form_.fields[1], gfx::RectF(),
+ TimeTicks());
// Simulate form submission.
- browser_autofill_manager_->OnFormSubmitted(form_, false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form_, false,
+ SubmissionSource::FORM_SUBMISSION);
- // Reset |browser_autofill_manager_| to commit UMA metrics.
- browser_autofill_manager_.reset();
+ ResetDriverToCommitMetrics();
histogram_tester.ExpectBucketCount(
"Autofill.KeyMetrics.FillingReadiness.Address", 1, 1);
@@ -12518,26 +11941,23 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledDataButDoesNotSubmit) {
base::HistogramTester histogram_tester;
// Simulate that suggestion is shown and user accepts it.
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{form_},
- /*removed_forms=*/{});
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form_, form_.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{form_},
+ /*removed_forms=*/{});
+ autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]);
+ autofill_manager().DidShowSuggestions(
/*has_autofill_suggestions=*/true, form_, form_.fields[0]);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form_, form_.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(),
- kTestGuid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ kTestGuid));
// Simulate user fixing the address.
- browser_autofill_manager_->OnTextFieldDidChange(form_, form_.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form_, form_.fields[1], gfx::RectF(),
+ TimeTicks());
// Don't submit form.
- // Reset |browser_autofill_manager_| to commit UMA metrics.
- browser_autofill_manager_.reset();
+ ResetDriverToCommitMetrics();
histogram_tester.ExpectTotalCount(
"Autofill.KeyMetrics.FillingReadiness.Address", 0);
@@ -12560,8 +11980,7 @@ TEST_F(AutofillMetricsTest, GetFieldTypeUserEditStatusMetric) {
AUTOFILLED_FIELD_WAS_NOT_EDITED;
int expected_result = 0b10'0100'0001;
- int actual_result =
- autofill::GetFieldTypeUserEditStatusMetric(server_type, metric);
+ int actual_result = GetFieldTypeUserEditStatusMetric(server_type, metric);
EXPECT_EQ(expected_result, actual_result);
}
@@ -12569,20 +11988,20 @@ TEST_F(AutofillMetricsTest, GetFieldTypeUserEditStatusMetric) {
// |AutofillClient| and logged upon form submission.
TEST_F(AutofillMetricsTest, PageLanguageMetricsExpectedCase) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
// Set up language state.
translate::LanguageDetectionDetails language_detection_details;
language_detection_details.adopted_language = "ub";
- browser_autofill_manager_->OnLanguageDetermined(language_detection_details);
- autofill_client_.GetLanguageState()->SetSourceLanguage("ub");
- autofill_client_.GetLanguageState()->SetCurrentLanguage("ub");
+ autofill_manager().OnLanguageDetermined(language_detection_details);
+ autofill_client_->GetLanguageState()->SetSourceLanguage("ub");
+ autofill_client_->GetLanguageState()->SetCurrentLanguage("ub");
int language_code = 'u' * 256 + 'b';
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.ParsedFieldTypesUsingTranslatedPageLanguage", language_code, 1);
@@ -12594,19 +12013,19 @@ TEST_F(AutofillMetricsTest, PageLanguageMetricsExpectedCase) {
// get logged as invalid.
TEST_F(AutofillMetricsTest, PageLanguageMetricsInvalidLanguage) {
FormData form;
- CreateSimpleForm(autofill_client_.form_origin(), form);
+ CreateSimpleForm(autofill_client_->form_origin(), form);
// Set up language state.
translate::LanguageDetectionDetails language_detection_details;
language_detection_details.adopted_language = "en";
- browser_autofill_manager_->OnLanguageDetermined(language_detection_details);
- autofill_client_.GetLanguageState()->SetSourceLanguage("en");
- autofill_client_.GetLanguageState()->SetCurrentLanguage("other");
+ autofill_manager().OnLanguageDetermined(language_detection_details);
+ autofill_client_->GetLanguageState()->SetSourceLanguage("en");
+ autofill_client_->GetLanguageState()->SetCurrentLanguage("other");
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.ParsedFieldTypesUsingTranslatedPageLanguage", 0, 1);
@@ -12645,12 +12064,12 @@ TEST_F(AutofillMetricsTest, AutofilledStateFieldSource) {
ADDRESS_HOME_COUNTRY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectUniqueSample(
"Autofill.AutofilledFieldAtSubmission.ByStateSelectionField",
@@ -12682,7 +12101,7 @@ TEST_F(AutofillMetricsTest,
IsValueNotAutofilledOverExistingValueSameAsSubmittedValue) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeature(
- autofill::features::kAutofillPreventOverridingPrefilledValues);
+ features::kAutofillPreventOverridingPrefilledValues);
RecreateProfile(false);
FormData form = test::GetFormData(
@@ -12710,35 +12129,34 @@ TEST_F(AutofillMetricsTest,
ADDRESS_HOME_ZIP, PHONE_HOME_WHOLE_NUMBER, ADDRESS_HOME_COUNTRY};
// Simulate having seen this form on page load.
- browser_autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
- browser_autofill_manager_->OnAskForValuesToFill(
- 0, form, form.fields[0], gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
- browser_autofill_manager_->DidShowSuggestions(
+ autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]);
+ autofill_manager().DidShowSuggestions(
/*has_autofill_suggestions=*/true, form, form.fields[0]);
std::string guid(kTestGuid);
- browser_autofill_manager_->FillOrPreviewForm(
+ autofill_manager().FillOrPreviewForm(
mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
- browser_autofill_manager_->MakeFrontendIDForTest(std::string(), guid));
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
// Case #1: Change submitted value to expected autofilled value for the field.
// The histogram should emit true for this.
form.fields[1].value = u"Memphis";
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[1],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(),
+ TimeTicks());
// Case #2: Change submitted value such that it different than expected
// autofilled value for the field. The histogram should emit false for this.
form.fields[3].value = u"00001";
- browser_autofill_manager_->OnTextFieldDidChange(form, form.fields[3],
- gfx::RectF(), TimeTicks());
+ autofill_manager().OnTextFieldDidChange(form, form.fields[3], gfx::RectF(),
+ TimeTicks());
// Simulate form submission.
base::HistogramTester histogram_tester;
- browser_autofill_manager_->OnFormSubmitted(form, /*known_success=*/false,
- SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
histogram_tester.ExpectBucketCount(
"Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue",
@@ -12748,6 +12166,67 @@ TEST_F(AutofillMetricsTest,
false, 1);
}
+TEST_F(AutofillMetricsTest, FormInteractionsAreCounted) {
+ // GIVEN
+ FormData form =
+ test::GetFormData({.fields = {{.role = ServerFieldType::NAME_FULL}}});
+ CreateSimpleForm(autofill_client_->form_origin(), form);
+
+ std::vector<ServerFieldType> field_types = {NAME_FULL};
+ autofill_manager().AddSeenForm(form, field_types, field_types);
+
+ // WHEN
+ // Simulate manual text field change.
+ const auto field = form.fields[0];
+ autofill_manager().OnTextFieldDidChange(form, field, gfx::RectF(),
+ TimeTicks());
+ // Simulate Autocomplete filling twice.
+ autofill_manager().OnSingleFieldSuggestionSelected(
+ u"", POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
+ autofill_manager().OnSingleFieldSuggestionSelected(
+ u"", POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
+ // Simulate Autofill filling.
+ std::string guid(kTestGuid);
+ autofill_manager().FillOrPreviewForm(
+ mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(),
+ autofill_manager().suggestion_generator()->MakeFrontendId(std::string(),
+ guid));
+ // Simulate form submission.
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+
+ // THEN
+ VerifySubmitFormUkm(
+ test_ukm_recorder_, form, AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
+ /*is_for_credit_card=*/false,
+ /* has_upi_vpa_field=*/false, {FormType::kAddressForm},
+ {/*form_element_user_modifications=*/1, /*autofill_fills=*/1,
+ /*autocomplete_fills=*/2});
+}
+
+TEST_F(AutofillMetricsTest, FormInteractionsAreInitiallyZero) {
+ // GIVEN
+ FormData form =
+ test::GetFormData({.fields = {{.role = ServerFieldType::NAME_FULL}}});
+ CreateSimpleForm(autofill_client_->form_origin(), form);
+
+ std::vector<ServerFieldType> field_types = {NAME_FULL};
+ autofill_manager().AddSeenForm(form, field_types, field_types);
+
+ // WHEN
+ // Simulate form submission.
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+
+ // THEN
+ VerifySubmitFormUkm(
+ test_ukm_recorder_, form, AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA,
+ /*is_for_credit_card=*/false,
+ /* has_upi_vpa_field=*/false, {FormType::kAddressForm},
+ {/*form_element_user_modifications=*/0, /*autofill_fills=*/0,
+ /*autocomplete_fills=*/0});
+}
+
// Base class for cross-frame filling metrics, in particular for
// Autofill.CreditCard.SeamlessFills.*.
class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest {
@@ -12777,8 +12256,10 @@ class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest {
false /* masked_card_is_enrolled_for_virtual_card */);
credit_card_with_cvc_ = {
- .credit_card = *browser_autofill_manager_->credit_card_access_manager()
- ->GetCreditCardsToSuggest()
+ .credit_card = *autofill_manager()
+ .personal_data_
+ ->GetCreditCardsToSuggest(
+ autofill_client_->AreServerCardsSupported())
.front(),
.cvc = u"123"};
@@ -12799,7 +12280,8 @@ class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest {
{.label = u"ExpDate",
.name = u"expdate",
.is_autofilled = false},
- {.label = u"CVC",
+ {.is_visible = false,
+ .label = u"CVC",
.name = u"cvc",
.is_autofilled = false,
.origin = other_origin},
@@ -12828,9 +12310,9 @@ class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest {
std::vector<ServerFieldType> field_types = {
CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER,
CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, CREDIT_CARD_VERIFICATION_CODE};
- browser_autofill_manager_->AddSeenForm(form_, field_types, field_types);
- browser_autofill_manager_->OnFormsSeen(/*updated_forms=*/{form_},
- /*removed_forms=*/{});
+ autofill_manager().AddSeenForm(form_, field_types, field_types);
+ autofill_manager().OnFormsSeen(/*updated_forms=*/{form_},
+ /*removed_forms=*/{});
}
CreditCardAndCvc& fill_data() { return credit_card_with_cvc_; }
@@ -12838,7 +12320,7 @@ class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest {
// Any call to FillForm() should be followed by a SetFormValues() call to
// mimic its effect on |form_|.
void FillForm(const FormFieldData& triggering_field) {
- browser_autofill_manager_->FillCreditCardForm(
+ autofill_manager().FillCreditCardForm(
0, form_, triggering_field, fill_data().credit_card, fill_data().cvc);
}
@@ -12870,8 +12352,8 @@ class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest {
}
void SubmitForm() {
- browser_autofill_manager_->OnFormSubmitted(
- form_, /*known_success=*/false, SubmissionSource::FORM_SUBMISSION);
+ autofill_manager().OnFormSubmitted(form_, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
}
FormFieldData& GetFieldById(FieldGlobalId field) {
@@ -12881,16 +12363,55 @@ class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest {
return *it;
}
- void CommitMetrics() { browser_autofill_manager_.reset(); }
-
base::test::ScopedFeatureList scoped_feature_list_;
FormData form_;
CreditCardAndCvc credit_card_with_cvc_;
};
+// This fixture adds utilities for the seamlessness metric names.
+//
+// These metric names get very long, and with >16 variants the tests become
+// unreadable otherwise.
+class AutofillMetricsSeamlessnessTest
+ : public AutofillMetricsCrossFrameFormTest {
+ public:
+ struct MetricName {
+ enum class Fill { kFills, kFillable };
+ enum class Time { kBefore, kAfter, kSubmission };
+ enum class Visibility { kAll, kVisible };
+ enum class Variant { kQualitative, kBitmask };
+
+ Fill fill;
+ Time time;
+ Visibility visibility;
+ Variant variant;
+
+ std::string str() const {
+ return base::StringPrintf(
+ "Autofill.CreditCard.Seamless%s.%s%s%s",
+ fill == Fill::kFills ? "Fills" : "Fillable",
+ time == Time::kSubmission ? "AtSubmissionTime"
+ : time == Time::kBefore ? "AtFillTimeBeforeSecurityPolicy"
+ : "AtFillTimeAfterSecurityPolicy",
+ visibility == Visibility::kAll ? "" : ".Visible",
+ variant == Variant::kQualitative ? "" : ".Bitmask");
+ }
+ };
+
+ static constexpr auto kFills = MetricName::Fill::kFills;
+ static constexpr auto kFillable = MetricName::Fill::kFillable;
+ static constexpr auto kBefore = MetricName::Time::kBefore;
+ static constexpr auto kAfter = MetricName::Time::kAfter;
+ static constexpr auto kSubmission = MetricName::Time::kSubmission;
+ static constexpr auto kAll = MetricName::Visibility::kAll;
+ static constexpr auto kVisible = MetricName::Visibility::kVisible;
+ static constexpr auto kQualitative = MetricName::Variant::kQualitative;
+ static constexpr auto kBitmask = MetricName::Variant::kBitmask;
+};
+
// Tests that Autofill.CreditCard.SeamlessFills.* is not emitted for manual
// fills.
-TEST_F(AutofillMetricsCrossFrameFormTest,
+TEST_F(AutofillMetricsSeamlessnessTest,
DoNotLogCreditCardSeamlessFillsMetricIfNotAutofilled) {
using UkmBuilder = ukm::builders::Autofill_CreditCardFill;
base::HistogramTester histogram_tester;
@@ -12906,40 +12427,24 @@ TEST_F(AutofillMetricsCrossFrameFormTest,
// This fills nothing because all fields have been manually filled.
FillForm(FormFieldData());
SubmitForm();
- CommitMetrics();
-
- histogram_tester.ExpectTotalCount(
- "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy", 0);
- histogram_tester.ExpectTotalCount(
- "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy."
- "Bitmask",
- 0);
-
- histogram_tester.ExpectTotalCount(
- "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy", 0);
- histogram_tester.ExpectTotalCount(
- "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy.Bitmask",
- 0);
-
- histogram_tester.ExpectTotalCount(
- "Autofill.CreditCard.SeamlessFillable.AtFillTimeBeforeSecurityPolicy", 0);
- histogram_tester.ExpectTotalCount(
- "Autofill.CreditCard.SeamlessFillable.AtFillTimeBeforeSecurityPolicy."
- "Bitmask",
- 0);
-
- histogram_tester.ExpectTotalCount(
- "Autofill.CreditCard.SeamlessFillable.AtFillTimeAfterSecurityPolicy", 0);
- histogram_tester.ExpectTotalCount(
- "Autofill.CreditCard.SeamlessFillable.AtFillTimeAfterSecurityPolicy."
- "Bitmask",
- 0);
+ ResetDriverToCommitMetrics();
+
+ for (auto fill : {kFills, kFillable}) {
+ for (auto time : {kBefore, kAfter, kSubmission}) {
+ for (auto visibility : {kAll, kVisible}) {
+ for (auto variant : {kQualitative, kBitmask}) {
+ histogram_tester.ExpectTotalCount(
+ MetricName{fill, time, visibility, variant}.str(), 0);
+ }
+ }
+ }
+ }
VerifyUkm(test_ukm_recorder_, form_, UkmBuilder::kEntryName, {});
}
// Tests that Autofill.CreditCard.SeamlessFills.* are emitted.
-TEST_F(AutofillMetricsCrossFrameFormTest,
+TEST_F(AutofillMetricsSeamlessnessTest,
LogCreditCardSeamlessFillsMetricIfAutofilledWithoutCvc) {
using Metric = AutofillMetrics::CreditCardSeamlessness::Metric;
using UkmBuilder = ukm::builders::Autofill_CreditCardFill;
@@ -12962,6 +12467,10 @@ TEST_F(AutofillMetricsCrossFrameFormTest,
};
base::HistogramTester histogram_tester;
+ auto SamplesOf = [&histogram_tester](MetricName metric) {
+ return histogram_tester.GetAllSamples(metric.str());
+ };
+
SeeForm();
fill_data().cvc = u"";
@@ -12972,6 +12481,7 @@ TEST_F(AutofillMetricsCrossFrameFormTest,
// - after security and assuming a complete profile: kPartialFill;
// - after security and without a CVC: kPartialFill;
// because due to the security policy, only NAME and EXP_DATE are filled.
+ // The CVC field is invisible.
FillForm(form_.fields[0]);
SetFormValues({CREDIT_CARD_NAME_FULL, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
/*is_autofilled=*/true, /*is_user_typed=*/false);
@@ -12983,59 +12493,59 @@ TEST_F(AutofillMetricsCrossFrameFormTest,
// - after security and without a CVC: kPartialFill;
// because the due to the security policy, only NUMBER and CVC could be
// filled.
+ // The CVC field is invisible.
FillForm(form_.fields[1]);
SetFormValues({CREDIT_CARD_NUMBER},
/*is_autofilled=*/true, /*is_user_typed=*/false);
SubmitForm();
- CommitMetrics();
-
- ExpectBuckets<Metric>(
- histogram_tester,
- "Autofill.CreditCard.SeamlessFillable.AtFillTimeBeforeSecurityPolicy",
- {{Metric::kFullFill, 2}});
- ExpectBuckets<int>(
- histogram_tester,
- "Autofill.CreditCard.SeamlessFillable.AtFillTimeBeforeSecurityPolicy."
- "Bitmask",
- {{kName | kNumber | kExp | kCvc, 2}});
-
- ExpectBuckets<Metric>(
- histogram_tester,
- "Autofill.CreditCard.SeamlessFillable.AtFillTimeAfterSecurityPolicy",
- {{Metric::kPartialFill, 2}});
- ExpectBuckets<int>(
- histogram_tester,
- "Autofill.CreditCard.SeamlessFillable.AtFillTimeAfterSecurityPolicy."
- "Bitmask",
- {{kName | kExp, 1}, {kNumber | kCvc, 1}});
-
- ExpectBuckets<Metric>(
- histogram_tester,
- "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy",
- {{Metric::kOptionalCvcMissing, 1}, {Metric::kPartialFill, 1}});
- ExpectBuckets<int>(
- histogram_tester,
- "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy."
- "Bitmask",
- {{kName | kNumber | kExp, 1}, {kNumber, 1}});
-
- ExpectBuckets<Metric>(
- histogram_tester,
- "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy",
- {{Metric::kPartialFill, 2}});
- ExpectBuckets<int>(
- histogram_tester,
- "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy.Bitmask",
- {{kName | kExp, 1}, {kNumber, 1}});
-
- ExpectBuckets<Metric>(histogram_tester,
- "Autofill.CreditCard.SeamlessFills.AtSubmissionTime",
- {{Metric::kOptionalCvcMissing, 1}});
- ExpectBuckets<int>(
- histogram_tester,
- "Autofill.CreditCard.SeamlessFills.AtSubmissionTime.Bitmask",
- {{kName | kNumber | kExp, 1}});
+ ResetDriverToCommitMetrics();
+
+ // Bitmask metrics.
+ EXPECT_THAT(SamplesOf({kFillable, kBefore, kAll, kBitmask}),
+ BucketsAre(Bucket(kName | kNumber | kExp | kCvc, 2)));
+ EXPECT_THAT(SamplesOf({kFillable, kAfter, kAll, kBitmask}),
+ BucketsAre(Bucket(kName | kExp, 1), Bucket(kNumber | kCvc, 1)));
+ EXPECT_THAT(
+ SamplesOf({kFills, kBefore, kAll, kBitmask}),
+ BucketsAre(Bucket(kName | kNumber | kExp, 1), Bucket(kNumber, 1)));
+ EXPECT_THAT(SamplesOf({kFills, kAfter, kAll, kBitmask}),
+ BucketsAre(Bucket(kName | kExp, 1), Bucket(kNumber, 1)));
+ EXPECT_THAT(SamplesOf({kFills, kSubmission, kAll, kBitmask}),
+ BucketsAre(Bucket(kName | kNumber | kExp, 1)));
+ // Bitmask metrics restricted to visible fields.
+ EXPECT_THAT(SamplesOf({kFillable, kBefore, kVisible, kBitmask}),
+ BucketsAre(Bucket(kName | kNumber | kExp, 2)));
+ EXPECT_THAT(SamplesOf({kFillable, kAfter, kVisible, kBitmask}),
+ BucketsAre(Bucket(kName | kExp, 1), Bucket(kNumber, 1)));
+ EXPECT_THAT(
+ SamplesOf({kFills, kBefore, kVisible, kBitmask}),
+ BucketsAre(Bucket(kName | kNumber | kExp, 1), Bucket(kNumber, 1)));
+ EXPECT_THAT(SamplesOf({kFills, kAfter, kVisible, kBitmask}),
+ BucketsAre(Bucket(kName | kExp, 1), Bucket(kNumber, 1)));
+
+ // Qualitative metrics.
+ EXPECT_THAT(SamplesOf({kFillable, kBefore, kAll, kQualitative}),
+ BucketsAre(Bucket(Metric::kFullFill, 2)));
+ EXPECT_THAT(SamplesOf({kFillable, kAfter, kAll, kQualitative}),
+ BucketsAre(Bucket(Metric::kPartialFill, 2)));
+ EXPECT_THAT(SamplesOf({kFills, kBefore, kAll, kQualitative}),
+ BucketsAre(Bucket(Metric::kOptionalCvcMissing, 1),
+ Bucket(Metric::kPartialFill, 1)));
+ EXPECT_THAT(SamplesOf({kFills, kAfter, kAll, kQualitative}),
+ BucketsAre(Bucket(Metric::kPartialFill, 2)));
+ EXPECT_THAT(SamplesOf({kFills, kSubmission, kAll, kQualitative}),
+ BucketsAre(Bucket(Metric::kOptionalCvcMissing, 1)));
+ // Qualitative metrics restricted to visible fields.
+ EXPECT_THAT(SamplesOf({kFillable, kBefore, kVisible, kQualitative}),
+ BucketsAre(Bucket(Metric::kOptionalCvcMissing, 2)));
+ EXPECT_THAT(SamplesOf({kFillable, kAfter, kVisible, kQualitative}),
+ BucketsAre(Bucket(Metric::kPartialFill, 2)));
+ EXPECT_THAT(SamplesOf({kFills, kBefore, kVisible, kQualitative}),
+ BucketsAre(Bucket(Metric::kOptionalCvcMissing, 1),
+ Bucket(Metric::kPartialFill, 1)));
+ EXPECT_THAT(SamplesOf({kFills, kAfter, kVisible, kQualitative}),
+ BucketsAre(Bucket(Metric::kPartialFill, 2)));
VerifyUkm(
test_ukm_recorder_, form_, UkmBuilder::kEntryName,
@@ -13053,6 +12563,24 @@ TEST_F(AutofillMetricsCrossFrameFormTest,
kName | kNumber | kExp},
{UkmBuilder::kFilled_AfterSecurity_BitmaskName, kName | kExp},
+ {UkmBuilder::kFillable_BeforeSecurity_Visible_QualitativeName,
+ kOptionalCvcMissing},
+ {UkmBuilder::kFillable_AfterSecurity_Visible_QualitativeName,
+ kPartialFill},
+ {UkmBuilder::kFilled_BeforeSecurity_Visible_QualitativeName,
+ kOptionalCvcMissing},
+ {UkmBuilder::kFilled_AfterSecurity_Visible_QualitativeName,
+ kPartialFill},
+
+ {UkmBuilder::kFillable_BeforeSecurity_Visible_BitmaskName,
+ kName | kNumber | kExp},
+ {UkmBuilder::kFillable_AfterSecurity_Visible_BitmaskName,
+ kName | kExp},
+ {UkmBuilder::kFilled_BeforeSecurity_Visible_BitmaskName,
+ kName | kNumber | kExp},
+ {UkmBuilder::kFilled_AfterSecurity_Visible_BitmaskName,
+ kName | kExp},
+
{UkmBuilder::kSharedAutofillName, kSharedAutofillWouldHelp},
{UkmBuilder::kFormSignatureName,
@@ -13070,6 +12598,21 @@ TEST_F(AutofillMetricsCrossFrameFormTest,
{UkmBuilder::kFilled_BeforeSecurity_BitmaskName, kNumber},
{UkmBuilder::kFilled_AfterSecurity_BitmaskName, kNumber},
+ {UkmBuilder::kFillable_BeforeSecurity_Visible_QualitativeName,
+ kOptionalCvcMissing},
+ {UkmBuilder::kFillable_AfterSecurity_Visible_QualitativeName,
+ kPartialFill},
+ {UkmBuilder::kFilled_BeforeSecurity_Visible_QualitativeName,
+ kPartialFill},
+ {UkmBuilder::kFilled_AfterSecurity_Visible_QualitativeName,
+ kPartialFill},
+
+ {UkmBuilder::kFillable_BeforeSecurity_Visible_BitmaskName,
+ kName | kNumber | kExp},
+ {UkmBuilder::kFillable_AfterSecurity_Visible_BitmaskName, kNumber},
+ {UkmBuilder::kFilled_BeforeSecurity_Visible_BitmaskName, kNumber},
+ {UkmBuilder::kFilled_AfterSecurity_Visible_BitmaskName, kNumber},
+
{UkmBuilder::kSharedAutofillName, kSharedAutofillIsIrrelevant},
{UkmBuilder::kFormSignatureName,
diff --git a/chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc b/chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
index a90c2c7a1e8..b1858523006 100644
--- a/chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
+++ b/chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
@@ -15,6 +15,7 @@
#include "components/autofill/core/browser/payments/credit_card_access_manager.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_payments_features.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
namespace autofill {
@@ -149,6 +150,12 @@ void CreditCardFormEventLogger::OnDidFillSuggestion(
record_type == CreditCard::VIRTUAL_CARD;
switch (record_type) {
case CreditCard::LOCAL_CARD:
+ // Check if the local card is a duplicate of an existing server card
+ // and log an additional metric if so.
+ if (IsLocalDuplicateOfServerCard(credit_card)) {
+ Log(FORM_EVENT_LOCAL_SUGGESTION_FILLED_FOR_AN_EXISTING_SERVER_CARD_ONCE,
+ form);
+ }
Log(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, form);
break;
case CreditCard::MASKED_SERVER_CARD:
@@ -215,7 +222,6 @@ void CreditCardFormEventLogger::LogFormSubmitted(const FormStructure& form) {
if (!has_logged_suggestion_filled_) {
Log(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, form);
} else if (logged_suggestion_filled_was_masked_server_card_) {
- DCHECK_NE(current_authentication_flow_, UnmaskAuthFlowType::kNone);
Log(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, form);
// Log BetterAuth.FlowEvents.
@@ -227,11 +233,8 @@ void CreditCardFormEventLogger::LogFormSubmitted(const FormStructure& form) {
// Log BetterAuth.FlowEvents.
RecordCardUnmaskFlowEvent(current_authentication_flow_,
UnmaskAuthFlowEvent::kFormSubmitted);
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication)) {
- AutofillMetrics::LogServerCardUnmaskFormSubmission(
- AutofillClient::PaymentsRpcCardType::kVirtualCard);
- }
+ AutofillMetrics::LogServerCardUnmaskFormSubmission(
+ AutofillClient::PaymentsRpcCardType::kVirtualCard);
} else if (logged_suggestion_filled_was_server_data_) {
Log(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, form);
} else {
@@ -287,6 +290,17 @@ void CreditCardFormEventLogger::OnLog(const std::string& name,
}
}
+bool CreditCardFormEventLogger::IsLocalDuplicateOfServerCard(
+ const CreditCard& credit_card) {
+ // Get the list of all the server credit cards for the user and see if any
+ // card in the list matches/isDuplicateOf the local card.
+ return base::ranges::any_of(
+ personal_data_manager_->GetServerCreditCards(),
+ [&credit_card](CreditCard* card_from_list) {
+ return credit_card.IsLocalDuplicateOfServerCard(*card_from_list);
+ });
+}
+
FormEvent CreditCardFormEventLogger::GetCardNumberStatusFormEvent(
const CreditCard& credit_card) {
const std::u16string number = credit_card.number();
@@ -348,8 +362,9 @@ void CreditCardFormEventLogger::RecordCardUnmaskFlowEvent(
bool CreditCardFormEventLogger::DoesCardHaveOffer(
const CreditCard& credit_card) {
for (auto& suggestion : suggestions_) {
- if (suggestion.backend_id == credit_card.guid())
+ if (suggestion.GetPayload<std::string>() == credit_card.guid()) {
return !suggestion.offer_label.empty();
+ }
}
return false;
}
diff --git a/chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h b/chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h
index 928a537fa75..17ccdbe32f0 100644
--- a/chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h
+++ b/chromium/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h
@@ -116,6 +116,7 @@ class CreditCardFormEventLogger : public FormEventLoggerBase {
using FormEventLoggerBase::Log;
private:
+ bool IsLocalDuplicateOfServerCard(const CreditCard& credit_card);
FormEvent GetCardNumberStatusFormEvent(const CreditCard& credit_card);
void RecordCardUnmaskFlowEvent(UnmaskAuthFlowType flow,
UnmaskAuthFlowEvent event);
diff --git a/chromium/components/autofill/core/browser/metrics/form_events/form_events.h b/chromium/components/autofill/core/browser/metrics/form_events/form_events.h
index 05a68e1c307..da269f89e86 100644
--- a/chromium/components/autofill/core/browser/metrics/form_events/form_events.h
+++ b/chromium/components/autofill/core/browser/metrics/form_events/form_events.h
@@ -135,6 +135,10 @@ enum FormEvent {
FORM_EVENT_CREDIT_CARD_SEAMLESS_FILLABLE_FULL_FILL_BUT_EXPDATE_MISSING = 54,
FORM_EVENT_CREDIT_CARD_SEAMLESS_FILLABLE_PARTIAL_FILL = 55,
+ // Metric logged when a local card with a matching deduplicated server
+ // suggestion was filled.
+ FORM_EVENT_LOCAL_SUGGESTION_FILLED_FOR_AN_EXISTING_SERVER_CARD_ONCE = 56,
+
NUM_FORM_EVENTS,
};
diff --git a/chromium/components/autofill/core/browser/metrics/form_interactions_counter.cc b/chromium/components/autofill/core/browser/metrics/form_interactions_counter.cc
new file mode 100644
index 00000000000..0919568a831
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/form_interactions_counter.cc
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/metrics/form_interactions_counter.h"
+
+namespace autofill {
+
+FormInteractionsCounter::~FormInteractionsCounter() = default;
+
+void FormInteractionsCounter::OnTextFieldDidChange(
+ const FieldSignature& field_signature) {
+ if (field_signature != last_field_signature_modified_by_user_) {
+ form_interaction_counts_.form_element_user_modifications++;
+ last_field_signature_modified_by_user_ = field_signature;
+ }
+}
+
+void FormInteractionsCounter::OnAutofillFill() {
+ form_interaction_counts_.autofill_fills++;
+}
+
+void FormInteractionsCounter::OnAutocompleteFill() {
+ form_interaction_counts_.autocomplete_fills++;
+}
+
+const FormInteractionCounts& FormInteractionsCounter::GetCounts() const {
+ return form_interaction_counts_;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/metrics/form_interactions_counter.h b/chromium/components/autofill/core/browser/metrics/form_interactions_counter.h
new file mode 100644
index 00000000000..5c2a10e6235
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/form_interactions_counter.h
@@ -0,0 +1,44 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_FORM_INTERACTIONS_COUNTER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_FORM_INTERACTIONS_COUNTER_H_
+
+#include "components/autofill/core/common/signatures.h"
+
+namespace autofill {
+
+struct FormInteractionCounts {
+ int64_t form_element_user_modifications = 0;
+ int64_t autofill_fills = 0;
+ int64_t autocomplete_fills = 0;
+};
+
+// Holds and increments counts of user interactions with autofillable form
+// elements, Autofill and Autocomplete
+class FormInteractionsCounter {
+ public:
+ FormInteractionsCounter() = default;
+ FormInteractionsCounter(const FormInteractionsCounter&) = delete;
+ FormInteractionsCounter& operator=(const FormInteractionsCounter&) = delete;
+
+ virtual ~FormInteractionsCounter();
+
+ void OnTextFieldDidChange(const FieldSignature& field_signature);
+
+ void OnAutofillFill();
+
+ void OnAutocompleteFill();
+
+ const FormInteractionCounts& GetCounts() const;
+
+ private:
+ FieldSignature last_field_signature_modified_by_user_;
+
+ FormInteractionCounts form_interaction_counts_ = {};
+};
+
+} // namespace autofill
+
+#endif
diff --git a/chromium/components/autofill/core/browser/metrics/payments/manage_cards_prompt_metrics.cc b/chromium/components/autofill/core/browser/metrics/payments/manage_cards_prompt_metrics.cc
new file mode 100644
index 00000000000..072a45332f2
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/payments/manage_cards_prompt_metrics.cc
@@ -0,0 +1,20 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/metrics/payments/manage_cards_prompt_metrics.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+
+namespace autofill {
+
+void LogManageCardsPromptMetric(ManageCardsPromptMetric metric,
+ bool is_upload_save) {
+ std::string destination = is_upload_save ? ".Upload" : ".Local";
+ std::string metric_with_destination =
+ "Autofill.ManageCardsPrompt" + destination;
+ base::UmaHistogramEnumeration(metric_with_destination, metric);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/metrics/payments/manage_cards_prompt_metrics.h b/chromium/components/autofill/core/browser/metrics/payments/manage_cards_prompt_metrics.h
new file mode 100644
index 00000000000..baae50f4cc9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/payments/manage_cards_prompt_metrics.h
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_MANAGE_CARDS_PROMPT_METRICS_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_MANAGE_CARDS_PROMPT_METRICS_H_
+
+namespace autofill {
+
+// Metrics to measure user interaction with the Manage Cards view shown when
+// user clicks on the save card icon after accepting to save a card.
+enum class ManageCardsPromptMetric {
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
+
+ // The manage cards promo was shown.
+ kManageCardsShown = 0,
+ // The user clicked on [Done].
+ kManageCardsDone = 1,
+ // The user clicked on [Manage cards].
+ kManageCardsManageCards = 2,
+ kMaxValue = kManageCardsManageCards
+};
+
+void LogManageCardsPromptMetric(ManageCardsPromptMetric metric,
+ bool is_upload_save);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_MANAGE_CARDS_PROMPT_METRICS_H_
diff --git a/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.cc b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.cc
new file mode 100644
index 00000000000..4a7b997d46d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.cc
@@ -0,0 +1,89 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/metrics/payments/offers_metrics.h"
+
+#include <unordered_map>
+
+#include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
+
+namespace autofill::autofill_metrics {
+
+void LogStoredOfferMetrics(
+ const std::vector<std::unique_ptr<AutofillOfferData>>& offers) {
+ std::unordered_map<AutofillOfferData::OfferType, int> offer_count;
+ for (const std::unique_ptr<AutofillOfferData>& offer : offers) {
+ // This function should only be run when the profile is loaded, which means
+ // the only offers that should be available are the ones that are stored on
+ // disk. Since free listing coupons are not stored on disk, we should never
+ // have any loaded here.
+ DCHECK_NE(offer->GetOfferType(),
+ AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER);
+
+ offer_count[offer->GetOfferType()]++;
+
+ if (offer->GetOfferType() ==
+ AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER) {
+ base::UmaHistogramCounts1000(
+ "Autofill.Offer.StoredOfferRelatedMerchantCount",
+ offer->GetMerchantOrigins().size());
+ base::UmaHistogramCounts1000("Autofill.Offer.StoredOfferRelatedCardCount",
+ offer->GetEligibleInstrumentIds().size());
+ }
+ }
+
+ if (offer_count[AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER] > 0) {
+ base::UmaHistogramCounts1000(
+ "Autofill.Offer.StoredOfferCount.GPayPromoCodeOffer",
+ offer_count[AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER]);
+ }
+
+ if (offer_count[AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER] > 0) {
+ base::UmaHistogramCounts1000(
+ "Autofill.Offer.StoredOfferCount.CardLinkedOffer",
+ offer_count[AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER]);
+ }
+}
+
+void LogOffersSuggestionsPopupShown(bool first_time_being_logged) {
+ if (first_time_being_logged) {
+ // We log that the offers suggestions popup was shown once for this field
+ // while autofilling if it is the first time being logged.
+ base::UmaHistogramEnumeration(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill::autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShownOnce);
+ }
+
+ // We log every time the offers suggestions popup is shown, regardless if the
+ // user is repeatedly clicking the same field.
+ base::UmaHistogramEnumeration(
+ "Autofill.Offer.SuggestionsPopupShown",
+ autofill::autofill_metrics::OffersSuggestionsPopupEvent::
+ kOffersSuggestionsPopupShown);
+}
+
+void LogIndividualOfferSuggestionEvent(
+ OffersSuggestionsEvent event,
+ AutofillOfferData::OfferType offer_type) {
+ std::string histogram_name = "Autofill.Offer.Suggestion";
+
+ // Switch to different sub-histogram depending on offer type being displayed.
+ switch (offer_type) {
+ case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER:
+ histogram_name += ".GPayPromoCodeOffer";
+ break;
+ case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER:
+ case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER:
+ case AutofillOfferData::OfferType::UNKNOWN:
+ NOTREACHED();
+ return;
+ }
+
+ base::UmaHistogramEnumeration(histogram_name, event);
+}
+
+} // namespace autofill::autofill_metrics
diff --git a/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.h b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.h
new file mode 100644
index 00000000000..38cc516e439
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.h
@@ -0,0 +1,78 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_OFFERS_METRICS_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_OFFERS_METRICS_H_
+
+#include <memory>
+#include <vector>
+
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
+
+namespace autofill::autofill_metrics {
+
+// Logs the offer data associated with a profile. This should be called each
+// time a Chrome profile is launched.
+void LogStoredOfferMetrics(
+ const std::vector<std::unique_ptr<AutofillOfferData>>& offers);
+
+// Metrics to track events related to the offers suggestions popup.
+enum class OffersSuggestionsPopupEvent {
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
+
+ // Default value, should never be used.
+ kUndefined = 0,
+ // The offer suggestions popup was shown. One event is logged in this metric
+ // bucket per time that the overall popup is shown.
+ kOffersSuggestionsPopupShown = 1,
+ // The offers suggestions popup was shown. Logged once if the user repeatedly
+ // displays suggestions for the same field.
+ kOffersSuggestionsPopupShownOnce = 2,
+ kMaxValue = kOffersSuggestionsPopupShownOnce,
+};
+
+// Metrics to track events related to individual offer suggestions in the
+// offers suggestions popup.
+enum class OffersSuggestionsEvent {
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
+
+ // Default value, should never be used.
+ kUndefined = 0,
+ // An individual offer suggestion was shown. One event is logged in this
+ // metric bucket for each individual offer suggestion in the suggestions
+ // popup. For instance, if there are three offers suggested, this bucket would
+ // log three times.
+ kOfferSuggestionShown = 1,
+ // An individual offer suggestion was shown. Logged once if the user
+ // repeatedly displays suggestions for the same field.
+ kOfferSuggestionShownOnce = 2,
+ // An individual offer suggestion was selected.
+ kOfferSuggestionSelected = 3,
+ // An individual offer suggestion was selected. Logged once if the user
+ // repeatedly selects suggestions for the same field.
+ kOfferSuggestionSelectedOnce = 4,
+ // The user selected to see the offer details page in the footer.
+ kOfferSuggestionSeeOfferDetailsSelected = 5,
+ // The user selected to see the offer details page in the footer. Logged once
+ // if the user repeatedly selects to see the offer details page in the footer
+ // for the same field.
+ kOfferSuggestionSeeOfferDetailsSelectedOnce = 6,
+ kMaxValue = kOfferSuggestionSeeOfferDetailsSelectedOnce,
+};
+
+// Log that the offers suggestions popup was shown. If |first_time_being_logged|
+// is true, it represents that it has not been logged yet for the promo code
+// offer field that the user is on, so additional logging is needed for the
+// histogram that denotes showing the offers suggestions popup once for a field.
+void LogOffersSuggestionsPopupShown(bool first_time_being_logged);
+
+// Log the offers suggestions popup |event| for the corresponding |offer_type|.
+void LogIndividualOfferSuggestionEvent(OffersSuggestionsEvent event,
+ AutofillOfferData::OfferType offer_type);
+
+} // namespace autofill::autofill_metrics
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_OFFERS_METRICS_H_
diff --git a/chromium/components/autofill/core/browser/metrics/payments/offers_metrics_unittest.cc b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics_unittest.cc
new file mode 100644
index 00000000000..d30bdc96067
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium 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 <vector>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
+#include "components/autofill/core/browser/metrics/autofill_metrics_test_base.h"
+#include "components/autofill/core/browser/metrics/payments/offers_metrics.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::base::Bucket;
+using ::base::BucketsAre;
+
+namespace autofill::autofill_metrics {
+
+class OffersMetricsTest : public metrics::AutofillMetricsBaseTest {
+ public:
+ OffersMetricsTest() = default;
+ ~OffersMetricsTest() override = default;
+};
+
+TEST_F(OffersMetricsTest, LogStoredOfferMetrics) {
+ std::vector<std::unique_ptr<AutofillOfferData>> offers;
+ AutofillOfferData offer1 = test::GetCardLinkedOfferData1();
+ AutofillOfferData offer2 = test::GetCardLinkedOfferData2();
+ AutofillOfferData offer3 = test::GetPromoCodeOfferData();
+ offer2.SetEligibleInstrumentIdForTesting({222222, 999999, 888888});
+ offer2.SetMerchantOriginForTesting(
+ {GURL("http://www.example2.com"), GURL("https://www.example3.com/")});
+ offers.push_back(std::make_unique<AutofillOfferData>(offer1));
+ offers.push_back(std::make_unique<AutofillOfferData>(offer2));
+ offers.push_back(std::make_unique<AutofillOfferData>(offer3));
+
+ base::HistogramTester histogram_tester;
+
+ autofill_metrics::LogStoredOfferMetrics(offers);
+
+ auto SamplesOf = [&histogram_tester](base::StringPiece metric) {
+ return histogram_tester.GetAllSamples(metric);
+ };
+
+ // Validate the count metrics.
+ EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferCount.CardLinkedOffer"),
+ BucketsAre(Bucket(2, 1)));
+ EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferCount.GPayPromoCodeOffer"),
+ BucketsAre(Bucket(1, 1)));
+ EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferRelatedMerchantCount"),
+ BucketsAre(Bucket(1, 1), Bucket(2, 1)));
+ EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferRelatedCardCount"),
+ BucketsAre(Bucket(1, 1), Bucket(3, 1)));
+}
+
+} // namespace autofill::autofill_metrics
diff --git a/chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc b/chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc
index dcb073d25cf..429e4640aed 100644
--- a/chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc
+++ b/chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc
@@ -9,6 +9,7 @@
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
+#include "components/autofill/core/browser/metrics/autofill_metrics.h"
#include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h"
namespace autofill {
@@ -73,6 +74,17 @@ void LogGetDetailsForEnrollmentRequestResult(VirtualCardEnrollmentSource source,
succeeded);
}
+void LogGetDetailsForEnrollmentRequestLatency(
+ VirtualCardEnrollmentSource source,
+ AutofillClient::PaymentsRpcResult result,
+ base::TimeDelta latency) {
+ base::UmaHistogramMediumTimes(
+ "Autofill.VirtualCard.GetDetailsForEnrollment.Latency." +
+ VirtualCardEnrollmentSourceToMetricSuffix(source) +
+ PaymentsRpcResultToMetricsSuffix(result),
+ latency);
+}
+
void LogUpdateVirtualCardEnrollmentRequestAttempt(
VirtualCardEnrollmentSource source,
VirtualCardEnrollmentRequestType type) {
@@ -137,6 +149,21 @@ void LogVirtualCardEnrollBubbleLatencySinceUpstream(
"Autofill.VirtualCardEnrollBubble.LatencySinceUpstream", latency);
}
+void LogVirtualCardEnrollmentNotOfferedDueToMaxStrikes(
+ VirtualCardEnrollmentSource source) {
+ base::UmaHistogramEnumeration(
+ "Autofill.StrikeDatabase.VirtualCardEnrollmentNotOfferedDueToMaxStrikes",
+ source);
+}
+
+void LogVirtualCardEnrollmentNotOfferedDueToRequiredDelay(
+ VirtualCardEnrollmentSource source) {
+ base::UmaHistogramEnumeration(
+ "Autofill.StrikeDatabase."
+ "VirtualCardEnrollmentNotOfferedDueToRequiredDelay",
+ source);
+}
+
std::string VirtualCardEnrollmentBubbleSourceToMetricSuffix(
VirtualCardEnrollmentBubbleSource source) {
switch (source) {
diff --git a/chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h b/chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
index 3ffc1604d29..d5d78fa3d51 100644
--- a/chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
+++ b/chromium/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
@@ -7,6 +7,7 @@
#include <string>
+#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h"
namespace base {
@@ -100,6 +101,10 @@ void LogGetDetailsForEnrollmentRequestAttempt(
VirtualCardEnrollmentSource source);
void LogGetDetailsForEnrollmentRequestResult(VirtualCardEnrollmentSource source,
bool succeeded);
+void LogGetDetailsForEnrollmentRequestLatency(
+ VirtualCardEnrollmentSource source,
+ AutofillClient::PaymentsRpcResult result,
+ base::TimeDelta latency);
// UpdateVirtualCardEnrollmentRequest related metrics. Attempts and results
// should be 1:1 mapping.
@@ -138,6 +143,13 @@ void LogVirtualCardEnrollBubbleCardArtAvailable(
void LogVirtualCardEnrollBubbleLatencySinceUpstream(
const base::TimeDelta& latency);
+// Logs the reason from strikedatabase perspective why virtual card enrollment
+// is not offered.
+void LogVirtualCardEnrollmentNotOfferedDueToMaxStrikes(
+ VirtualCardEnrollmentSource source);
+void LogVirtualCardEnrollmentNotOfferedDueToRequiredDelay(
+ VirtualCardEnrollmentSource source);
+
// Helper function used to convert VirtualCardEnrollmentBubbleSource enum to
// name suffix.
std::string VirtualCardEnrollmentBubbleSourceToMetricSuffix(
diff --git a/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.cc b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.cc
new file mode 100644
index 00000000000..9c5f350b4fe
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.cc
@@ -0,0 +1,93 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/metrics/shadow_prediction_metrics.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/sparse_histogram.h"
+#include "components/autofill/core/browser/form_parsing/buildflags.h"
+#include "components/autofill/core/browser/form_parsing/field_candidates.h"
+
+namespace autofill::metrics {
+
+namespace {
+
+// The number of basic comparison results (i.e. without offsetting to encode the
+// field type).
+constexpr int kBaseComparisonRange = 6;
+
+// Encode `prediction` into `comparison_base`.
+int GetTypeSpecificComparison(ServerFieldType prediction, int comparison_base) {
+ DCHECK_LE(comparison_base, kDifferentPredictionsValueAgreesWithBoth);
+ DCHECK_NE(comparison_base, kNoPrediction);
+
+ return static_cast<int>(prediction) * kBaseComparisonRange + comparison_base;
+}
+
+// Get the comparison between the predictions, without the prediction type being
+// encoded in the returned value. The returned value is in the range [0,6]
+// inclusive.
+int GetBaseComparison(ServerFieldType current,
+ ServerFieldType next,
+ const ServerFieldTypeSet& submitted_types) {
+ if (current == NO_SERVER_DATA || next == NO_SERVER_DATA) {
+ return kNoPrediction;
+ } else if (current == next) {
+ return submitted_types.contains(current) ? kSamePredictionValueAgrees
+ : kSamePredictionValueDisagrees;
+ } else if (submitted_types.contains_all({current, next})) {
+ return kDifferentPredictionsValueAgreesWithBoth;
+ } else if (submitted_types.contains(current)) {
+ return kDifferentPredictionsValueAgreesWithOld;
+ } else if (submitted_types.contains(next)) {
+ return kDifferentPredictionsValueAgreesWithNew;
+ } else {
+ return kDifferentPredictionsValueAgreesWithNeither;
+ }
+}
+
+} // namespace
+
+int GetShadowPrediction(ServerFieldType current,
+ ServerFieldType next,
+ const ServerFieldTypeSet& submitted_types) {
+ // `NO_SERVER_DATA` means that we didn't actually run any heuristics.
+ if (current == NO_SERVER_DATA || next == NO_SERVER_DATA)
+ return kNoPrediction;
+
+ int comparison = GetBaseComparison(current, next, submitted_types);
+
+ // If we compared the predictions, offset them by the field type, so that the
+ // type is included in the enum.
+ if (comparison != kNoPrediction)
+ comparison = GetTypeSpecificComparison(current, comparison);
+
+ return comparison;
+}
+
+void LogShadowPredictionComparison(const AutofillField& field) {
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ const auto& submitted_types = field.possible_types();
+
+ base::UmaHistogramSparse(
+ "Autofill.ShadowPredictions.ExperimentalToDefault",
+ GetShadowPrediction(field.heuristic_type(PatternSource::kDefault),
+ field.heuristic_type(PatternSource::kExperimental),
+ submitted_types));
+
+ base::UmaHistogramSparse(
+ "Autofill.ShadowPredictions.NextGenToDefault",
+ GetShadowPrediction(field.heuristic_type(PatternSource::kDefault),
+ field.heuristic_type(PatternSource::kNextGen),
+ submitted_types));
+
+ base::UmaHistogramSparse(
+ "Autofill.ShadowPredictions.NextGenToExperimental",
+ GetShadowPrediction(field.heuristic_type(PatternSource::kExperimental),
+ field.heuristic_type(PatternSource::kNextGen),
+ submitted_types));
+#endif
+}
+
+} // namespace autofill::metrics
diff --git a/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.h b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.h
new file mode 100644
index 00000000000..099a9af9755
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.h
@@ -0,0 +1,38 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_SHADOW_PREDICTION_METRICS_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_SHADOW_PREDICTION_METRICS_H_
+
+#include "components/autofill/core/browser/form_structure.h"
+
+namespace autofill::metrics {
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. These mirror the first entries of
+// `AutofillPredictionsComparisonResult` in
+// tools/metrics/histograms/metadata/autofill/histograms.xml
+constexpr int kNoPrediction = 0;
+constexpr int kSamePredictionValueAgrees = 1;
+constexpr int kSamePredictionValueDisagrees = 2;
+constexpr int kDifferentPredictionsValueAgreesWithOld = 3;
+constexpr int kDifferentPredictionsValueAgreesWithNew = 4;
+constexpr int kDifferentPredictionsValueAgreesWithNeither = 5;
+constexpr int kDifferentPredictionsValueAgreesWithBoth = 6;
+
+// Gets a 3-way comparison between
+// * the `current` prediction
+// * the `next` (shadow) prediction
+// * the types detected in the field `submitted_types` during submission
+int GetShadowPrediction(ServerFieldType current,
+ ServerFieldType next,
+ const ServerFieldTypeSet& submitted_types);
+
+// Logs Autofill.ShadowPredictions.* metrics by comparing the submitted
+// values to the actual and hypothetical predictions.
+void LogShadowPredictionComparison(const AutofillField& field);
+
+} // namespace autofill::metrics
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_SHADOW_PREDICTION_METRICS_H_
diff --git a/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics_unittest.cc b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics_unittest.cc
new file mode 100644
index 00000000000..07fd12addd7
--- /dev/null
+++ b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics_unittest.cc
@@ -0,0 +1,216 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/metrics/shadow_prediction_metrics.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "components/autofill/core/browser/autofill_form_test_utils.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_parsing/buildflags.h"
+#include "components/autofill/core/browser/metrics/autofill_metrics_test_base.h"
+#include "components/autofill/core/common/autofill_features.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::autofill::mojom::SubmissionSource;
+using ::base::Bucket;
+using ::testing::IsEmpty;
+using ::testing::UnorderedElementsAre;
+
+namespace autofill::metrics {
+
+// These constants mirror the similarly named values in
+// `AutofillPredictionsComparisonResult` in
+// tools/metrics/histograms/metadata/autofill/histograms.xml.
+constexpr int kNameFirstSamePredictionValueAgrees = 19;
+constexpr int kNameFirstSamePredictionValueDisagrees = 20;
+constexpr int kNameFirstDifferentPredictionsValueAgreesWithOld = 21;
+constexpr int kNameFirstDifferentPredictionsValueAgreesWithBoth = 24;
+constexpr int kNameFirstDifferentPredictionsValueAgreesWithNeither = 23;
+constexpr int kEmailAddressDifferentPredictionsValueAgreesWithNew = 58;
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+constexpr int kNameFullSamePredictionValueAgrees = 43;
+constexpr int kNameFullDifferentPredictionsValueAgreesWithOld = 45;
+constexpr int kEmailAddressDifferentPredictionsValueAgreesWithOld = 57;
+constexpr int kSearchTermSamePredictionValueDisagrees = 584;
+constexpr int kSearchTermDifferentPredictionsValueAgreesWithNew = 586;
+#endif
+
+namespace {
+
+// Get a form with 2 fields.
+FormData GetFormWith2Fields(const GURL& form_origin) {
+ return test::GetFormData(
+ {.description_for_logging = "ShadowPredictions",
+ .fields =
+ {
+ {
+ .label = u"Name",
+ .name = u"name",
+ },
+ {
+ .label = u"Email",
+ .name = u"email",
+ },
+ },
+ .unique_renderer_id = test::MakeFormRendererId(),
+ .main_frame_origin = url::Origin::Create(form_origin)});
+}
+
+// Test that various combinations of predictions and values are mapped to the
+// correct value in the metric enum.
+TEST(AutofillShadowPredictionComparisonTest,
+ PredictionsMapToPredictionComparison) {
+ using ::autofill::metrics::GetShadowPrediction;
+
+ EXPECT_EQ(kNoPrediction, GetShadowPrediction(NO_SERVER_DATA, NO_SERVER_DATA,
+ {NO_SERVER_DATA}));
+
+ EXPECT_EQ(kNoPrediction,
+ GetShadowPrediction(NAME_FIRST, NO_SERVER_DATA, {NAME_FIRST}));
+
+ EXPECT_EQ(kNameFirstSamePredictionValueAgrees,
+ GetShadowPrediction(NAME_FIRST, NAME_FIRST, {NAME_FIRST}));
+
+ EXPECT_EQ(kNameFirstSamePredictionValueDisagrees,
+ GetShadowPrediction(NAME_FIRST, NAME_FIRST, {EMAIL_ADDRESS}));
+
+ EXPECT_EQ(kEmailAddressDifferentPredictionsValueAgreesWithNew,
+ GetShadowPrediction(EMAIL_ADDRESS, NAME_FIRST, {NAME_FIRST}));
+
+ EXPECT_EQ(kNameFirstDifferentPredictionsValueAgreesWithOld,
+ GetShadowPrediction(NAME_FIRST, EMAIL_ADDRESS, {NAME_FIRST}));
+
+ EXPECT_EQ(kNameFirstDifferentPredictionsValueAgreesWithNeither,
+ GetShadowPrediction(NAME_FIRST, EMAIL_ADDRESS, {NAME_LAST}));
+
+ EXPECT_EQ(kNameFirstDifferentPredictionsValueAgreesWithBoth,
+ GetShadowPrediction(NAME_FIRST, EMAIL_ADDRESS,
+ {NAME_FIRST, EMAIL_ADDRESS}));
+}
+
+// Test that all `ServerFieldType`s have corresponding values in the enum.
+TEST(AutofillShadowPredictionComparisonTest, ComparisonContainsAllTypes) {
+ // If this test fails after adding a type, update
+ // `AutofillPredictionsComparisonResult` in tools/metrics/histograms/enums.xml
+ // and set `last_known_type` to the last entry in the enum.
+ constexpr ServerFieldType last_known_type = PHONE_HOME_NUMBER_SUFFIX;
+ int max_comparison =
+ GetShadowPrediction(last_known_type, NAME_FIRST, {NAME_LAST});
+
+ for (int type_int = NO_SERVER_DATA; type_int <= MAX_VALID_FIELD_TYPE;
+ type_int++) {
+ auto type = ToSafeServerFieldType(type_int, NO_SERVER_DATA);
+ EXPECT_LE(GetShadowPrediction(type, NAME_FIRST, {NAME_LAST}),
+ max_comparison)
+ << FieldTypeToStringPiece(type) << " has no mapping.";
+ }
+}
+
+class AutofillShadowPredictionMetricsTest
+ : public autofill::metrics::AutofillMetricsBaseTest {
+ public:
+ AutofillShadowPredictionMetricsTest() {
+ scoped_feature_list_.InitWithFeaturesAndParameters(
+ {base::test::ScopedFeatureList::FeatureAndParams(
+ features::kAutofillParsingPatternProvider,
+ {{"prediction_source", "default"}})},
+ {});
+ }
+
+ ~AutofillShadowPredictionMetricsTest() override = default;
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// When shadow predictions are not calculated, the shadow prediction metrics
+// should report `0`.
+TEST_F(AutofillShadowPredictionMetricsTest,
+ SubmissionWithoutShadowPredictions) {
+ FormData form = GetFormWith2Fields(autofill_client_->form_origin());
+ form.fields[0].value = u"Elvis Aaron Presley"; // A known `NAME_FULL`.
+ form.fields[1].value = u"buddy@gmail.com"; // A known `EMAIL_ADDRESS`.
+
+ std::vector<ServerFieldType> heuristic_types = {NAME_FULL, EMAIL_ADDRESS};
+ std::vector<ServerFieldType> server_types = {NAME_FULL, EMAIL_ADDRESS};
+
+ // Simulate having seen this form on page load.
+ autofill_manager().AddSeenForm(form, heuristic_types, server_types);
+
+ // Simulate form submission.
+ base::HistogramTester histogram_tester;
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+ histogram_tester.ExpectBucketCount(
+ "Autofill.ShadowPredictions.ExperimentalToDefault", kNoPrediction, 2);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.ShadowPredictions.NextGenToDefault", kNoPrediction, 2);
+#else
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Autofill.ShadowPredictions.ExperimentalToDefault"),
+ IsEmpty());
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Autofill.ShadowPredictions.NextGenToDefault"),
+ IsEmpty());
+#endif
+}
+
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS)
+// Test that Autofill.ShadowPredictions.* describes the differences between the
+// predictions and the submitted values.
+TEST_F(AutofillShadowPredictionMetricsTest,
+ SubmissionWithAgreeingShadowPredictions) {
+ FormData form = GetFormWith2Fields(autofill_client_->form_origin());
+ form.fields[0].value = u"Elvis Aaron Presley"; // A known `NAME_FULL`.
+ form.fields[1].value = u"buddy@gmail.com"; // A known `EMAIL_ADDRESS`.
+
+ std::vector<ServerFieldType> server_types = {NAME_FULL, EMAIL_ADDRESS};
+
+ // Simulate having seen this form on page load.
+ autofill_manager().AddSeenForm(
+ form,
+ {// Field 0
+ {{PatternSource::kDefault, NAME_FULL},
+ {PatternSource::kExperimental, NAME_FULL},
+ {PatternSource::kNextGen, NAME_FIRST}},
+ // Field 1
+ {{PatternSource::kDefault, SEARCH_TERM},
+ {PatternSource::kExperimental, EMAIL_ADDRESS},
+ {PatternSource::kNextGen, SEARCH_TERM}}},
+ server_types);
+
+ // Simulate form submission.
+ base::HistogramTester histogram_tester;
+ autofill_manager().OnFormSubmitted(form, /*known_success=*/false,
+ SubmissionSource::FORM_SUBMISSION);
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "Autofill.ShadowPredictions.ExperimentalToDefault"),
+ UnorderedElementsAre(
+ Bucket(kNameFullSamePredictionValueAgrees, 1),
+ Bucket(kSearchTermDifferentPredictionsValueAgreesWithNew, 1)));
+
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "Autofill.ShadowPredictions.NextGenToDefault"),
+ UnorderedElementsAre(
+ Bucket(kNameFullDifferentPredictionsValueAgreesWithOld, 1),
+ Bucket(kSearchTermSamePredictionValueDisagrees, 1)));
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "Autofill.ShadowPredictions.NextGenToExperimental"),
+ UnorderedElementsAre(
+ Bucket(kNameFullDifferentPredictionsValueAgreesWithOld, 1),
+ Bucket(kEmailAddressDifferentPredictionsValueAgreesWithOld, 1)));
+}
+#endif
+
+} // namespace
+
+} // namespace autofill::metrics
diff --git a/chromium/components/autofill/core/browser/mock_autocomplete_history_manager.h b/chromium/components/autofill/core/browser/mock_autocomplete_history_manager.h
index 77a15617b32..81772fc85be 100644
--- a/chromium/components/autofill/core/browser/mock_autocomplete_history_manager.h
+++ b/chromium/components/autofill/core/browser/mock_autocomplete_history_manager.h
@@ -25,11 +25,13 @@ class MockAutocompleteHistoryManager : public AutocompleteHistoryManager {
const std::u16string& name,
const std::u16string& prefix,
const std::string& form_control_type,
- base::WeakPtr<AutocompleteHistoryManager::SuggestionsHandler> handler),
+ base::WeakPtr<AutocompleteHistoryManager::SuggestionsHandler> handler,
+ const SuggestionsContext& context),
(override));
MOCK_METHOD(void,
- OnWillSubmitForm,
- (const FormData& form, bool is_autocomplete_enabled),
+ OnWillSubmitFormWithFields,
+ (const std::vector<FormFieldData>& fields,
+ bool is_autocomplete_enabled),
(override));
MOCK_METHOD(void,
OnWebDataServiceRequestDone,
@@ -41,11 +43,11 @@ class MockAutocompleteHistoryManager : public AutocompleteHistoryManager {
(override));
MOCK_METHOD(void,
OnRemoveCurrentSingleFieldSuggestion,
- (const std::u16string&, const std::u16string&),
+ (const std::u16string&, const std::u16string&, int),
(override));
MOCK_METHOD(void,
OnSingleFieldSuggestionSelected,
- (const std::u16string&),
+ (const std::u16string&, int),
(override));
};
diff --git a/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.cc b/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.cc
new file mode 100644
index 00000000000..cd56ac4390e
--- /dev/null
+++ b/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.cc
@@ -0,0 +1,13 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/mock_merchant_promo_code_manager.h"
+
+namespace autofill {
+
+MockMerchantPromoCodeManager::MockMerchantPromoCodeManager() = default;
+
+MockMerchantPromoCodeManager::~MockMerchantPromoCodeManager() = default;
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.h b/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.h
new file mode 100644
index 00000000000..3a95b96c446
--- /dev/null
+++ b/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.h
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_MERCHANT_PROMO_CODE_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_MERCHANT_PROMO_CODE_MANAGER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/autofill/core/browser/merchant_promo_code_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill {
+
+class MockMerchantPromoCodeManager : public MerchantPromoCodeManager {
+ public:
+ MockMerchantPromoCodeManager();
+ ~MockMerchantPromoCodeManager() override;
+
+ MOCK_METHOD(
+ void,
+ OnGetSingleFieldSuggestions,
+ (int query_id,
+ bool is_autocomplete_enabled,
+ bool autoselect_first_suggestion,
+ const std::u16string& name,
+ const std::u16string& prefix,
+ const std::string& form_control_type,
+ base::WeakPtr<MerchantPromoCodeManager::SuggestionsHandler> handler,
+ const SuggestionsContext& context),
+ (override));
+ MOCK_METHOD(void,
+ OnWillSubmitFormWithFields,
+ (const std::vector<FormFieldData>& fields,
+ bool is_autocomplete_enabled),
+ (override));
+ MOCK_METHOD(void,
+ CancelPendingQueries,
+ (const MerchantPromoCodeManager::SuggestionsHandler*),
+ (override));
+ MOCK_METHOD(void,
+ OnRemoveCurrentSingleFieldSuggestion,
+ (const std::u16string&, const std::u16string&, int),
+ (override));
+ MOCK_METHOD(void,
+ OnSingleFieldSuggestionSelected,
+ (const std::u16string&, int),
+ (override));
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_MERCHANT_PROMO_CODE_MANAGER_H_
diff --git a/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.cc b/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.cc
index 286c6d9c79d..ff775cd2780 100644
--- a/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.cc
+++ b/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.cc
@@ -6,8 +6,10 @@
namespace autofill {
MockSingleFieldFormFillRouter::MockSingleFieldFormFillRouter(
- AutocompleteHistoryManager* autocomplete_history_manager)
- : SingleFieldFormFillRouter(autocomplete_history_manager) {}
+ AutocompleteHistoryManager* autocomplete_history_manager,
+ MerchantPromoCodeManager* merchant_promo_code_manager)
+ : SingleFieldFormFillRouter(autocomplete_history_manager,
+ merchant_promo_code_manager) {}
MockSingleFieldFormFillRouter::~MockSingleFieldFormFillRouter() = default;
diff --git a/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.h b/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.h
index ee3589b584f..65d2ec16f30 100644
--- a/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.h
+++ b/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.h
@@ -14,23 +14,31 @@ namespace autofill {
class MockSingleFieldFormFillRouter : public SingleFieldFormFillRouter {
public:
explicit MockSingleFieldFormFillRouter(
- AutocompleteHistoryManager* autocomplete_history_manager);
+ AutocompleteHistoryManager* autocomplete_history_manager,
+ MerchantPromoCodeManager* merchant_promo_code_manager);
~MockSingleFieldFormFillRouter() override;
- MOCK_METHOD(
- void,
- OnGetSingleFieldSuggestions,
- (int query_id,
- bool is_autocomplete_enabled,
- bool autoselect_first_suggestion,
- const std::u16string& name,
- const std::u16string& prefix,
- const std::string& form_control_type,
- base::WeakPtr<SingleFieldFormFiller::SuggestionsHandler> handler),
- (override));
MOCK_METHOD(void,
OnWillSubmitForm,
- (const FormData& form, bool is_autocomplete_enabled),
+ (const FormData& form,
+ const FormStructure* form_structure,
+ bool is_autocomplete_enabled),
+ (override));
+ MOCK_METHOD(void,
+ OnGetSingleFieldSuggestions,
+ (int query_id,
+ bool is_autocomplete_enabled,
+ bool autoselect_first_suggestion,
+ const std::u16string& name,
+ const std::u16string& prefix,
+ const std::string& form_control_type,
+ base::WeakPtr<SingleFieldFormFiller::SuggestionsHandler> handler,
+ const SuggestionsContext& context),
+ (override));
+ MOCK_METHOD(void,
+ OnWillSubmitFormWithFields,
+ (const std::vector<FormFieldData>& fields,
+ bool is_autocomplete_enabled),
(override));
MOCK_METHOD(void,
CancelPendingQueries,
@@ -38,11 +46,11 @@ class MockSingleFieldFormFillRouter : public SingleFieldFormFillRouter {
(override));
MOCK_METHOD(void,
OnRemoveCurrentSingleFieldSuggestion,
- (const std::u16string&, const std::u16string&),
+ (const std::u16string&, const std::u16string&, int),
(override));
MOCK_METHOD(void,
OnSingleFieldSuggestionSelected,
- (const std::u16string&),
+ (const std::u16string&, int),
(override));
};
diff --git a/chromium/components/autofill/core/browser/payments/OWNERS b/chromium/components/autofill/core/browser/payments/OWNERS
index 3d29c995654..fe1dfa24b52 100644
--- a/chromium/components/autofill/core/browser/payments/OWNERS
+++ b/chromium/components/autofill/core/browser/payments/OWNERS
@@ -1,5 +1,4 @@
jsaul@google.com
siyua@chromium.org
-per-file autofill_wallet_model_type_controller*=jkrcal@chromium.org
per-file autofill_wallet_model_type_controller*=file://components/sync/OWNERS
diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc
index ccafc384568..c94e756ad78 100644
--- a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc
+++ b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc
@@ -53,7 +53,7 @@ void AutofillOfferManager::UpdateSuggestionsWithOffers(
// Update |offer_label| for each suggestion.
for (auto& suggestion : suggestions) {
- std::string id = suggestion.backend_id;
+ std::string id = suggestion.GetPayload<std::string>();
if (eligible_offers_map.count(id)) {
suggestion.offer_label =
l10n_util::GetStringUTF16(IDS_AUTOFILL_OFFERS_CASHBACK);
@@ -61,16 +61,13 @@ void AutofillOfferManager::UpdateSuggestionsWithOffers(
}
// Sort the suggestions such that suggestions with offers are shown at the
// top.
- if (base::FeatureList::IsEnabled(
- features::kAutofillSortSuggestionsBasedOnOfferPresence)) {
- std::sort(suggestions.begin(), suggestions.end(),
- [](const Suggestion& a, const Suggestion& b) {
- if (!a.offer_label.empty() && b.offer_label.empty()) {
- return true;
- }
- return false;
- });
- }
+ std::sort(suggestions.begin(), suggestions.end(),
+ [](const Suggestion& a, const Suggestion& b) {
+ if (!a.offer_label.empty() && b.offer_label.empty()) {
+ return true;
+ }
+ return false;
+ });
}
bool AutofillOfferManager::IsUrlEligible(const GURL& last_committed_url) {
@@ -109,8 +106,8 @@ void AutofillOfferManager::UpdateEligibleMerchantDomains() {
std::vector<AutofillOfferData*> offers = personal_data_->GetAutofillOffers();
for (auto* offer : offers) {
- eligible_merchant_domains_.insert(offer->merchant_origins.begin(),
- offer->merchant_origins.end());
+ eligible_merchant_domains_.insert(offer->GetMerchantOrigins().begin(),
+ offer->GetMerchantOrigins().end());
}
}
@@ -136,8 +133,8 @@ AutofillOfferManager::OffersMap AutofillOfferManager::CreateCardLinkedOffersMap(
// If card has an offer, add the backend ID to the map. There is currently
// a one-to-one mapping between cards and offer data, however, this may
// change in the future.
- if (std::count(offer->eligible_instrument_id.begin(),
- offer->eligible_instrument_id.end(),
+ if (std::count(offer->GetEligibleInstrumentIds().begin(),
+ offer->GetEligibleInstrumentIds().end(),
card->instrument_id())) {
offers_map[card->guid()] = offer;
}
diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h
index 30041b62a03..a444c6ec6a8 100644
--- a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h
+++ b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h
@@ -22,7 +22,7 @@
namespace autofill {
class AutofillClient;
-struct AutofillOfferData;
+class AutofillOfferData;
class OfferNotificationHandler;
class PersonalDataManager;
struct Suggestion;
@@ -78,7 +78,7 @@ class AutofillOfferManager : public KeyedService,
CreateCardLinkedOffersMap_ReturnsOnlyCardLinkedOffers);
FRIEND_TEST_ALL_PREFIXES(AutofillOfferManagerTest, IsUrlEligible);
friend class OfferNotificationBubbleViewsInteractiveUiTest;
- friend class OfferNotificationInfoBarControllerImplBrowserTest;
+ friend class OfferNotificationControllerAndroidBrowserTest;
// Queries |personal_data_| to reset the elements of
// |eligible_merchant_domains_|
diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
index 1789e7a5950..609a8254ff8 100644
--- a/chromium/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
@@ -78,32 +78,39 @@ class AutofillOfferManagerTest : public testing::Test {
std::string offer_reward_amount,
bool expired = false,
std::vector<GURL> merchant_origins = {GURL(kTestUrl)}) {
- AutofillOfferData offer_data;
- offer_data.offer_id = 4444;
- offer_data.offer_reward_amount = offer_reward_amount;
- if (expired) {
- offer_data.expiry = AutofillClock::Now() - base::Days(2);
- } else {
- offer_data.expiry = AutofillClock::Now() + base::Days(2);
- }
- offer_data.merchant_origins = std::move(merchant_origins);
- offer_data.eligible_instrument_id = {card.instrument_id()};
- offer_data.offer_details_url = GURL(kOfferDetailsUrl);
+ int64_t offer_id = 4444;
+ base::Time expiry = expired ? AutofillClock::Now() - base::Days(2)
+ : AutofillClock::Now() + base::Days(2);
+ std::vector<int64_t> eligible_instrument_id = {card.instrument_id()};
+ GURL offer_details_url = GURL(kOfferDetailsUrl);
+ DisplayStrings display_strings;
+ display_strings.value_prop_text = "5% cash back when you use this card.";
+ display_strings.see_details_text = "Terms apply.";
+ display_strings.usage_instructions_text =
+ "Check out with this card to activate.";
+
+ AutofillOfferData offer_data = AutofillOfferData::GPayCardLinkedOffer(
+ offer_id, expiry, merchant_origins, offer_details_url, display_strings,
+ eligible_instrument_id, offer_reward_amount);
return offer_data;
}
AutofillOfferData CreatePromoCodeOffer(std::vector<GURL> merchant_origins = {
GURL(kTestUrl)}) {
- AutofillOfferData offer_data;
- offer_data.offer_id = 5555;
- offer_data.expiry = AutofillClock::Now() + base::Days(2);
- offer_data.merchant_origins = std::move(merchant_origins);
- offer_data.offer_details_url = GURL(kOfferDetailsUrl);
- offer_data.promo_code = "5PCTOFFSHOES";
- offer_data.display_strings.value_prop_text = "5% off on shoes. Up to $50.";
- offer_data.display_strings.see_details_text = "See details";
- offer_data.display_strings.usage_instructions_text =
+ int64_t offer_id = 5555;
+ base::Time expiry = AutofillClock::Now() + base::Days(2);
+ GURL offer_details_url = GURL(kOfferDetailsUrl);
+ std::string promo_code = "5PCTOFFSHOES";
+ DisplayStrings display_strings;
+ display_strings.value_prop_text = "5% off on shoes. Up to $50.";
+ display_strings.see_details_text = "See details";
+ display_strings.usage_instructions_text =
"Click the promo code field at checkout to autofill it.";
+ std::string offer_reward_amount = "5%";
+
+ AutofillOfferData offer_data = AutofillOfferData::GPayPromoCodeOffer(
+ offer_id, expiry, merchant_origins, offer_details_url, display_strings,
+ offer_reward_amount);
return offer_data;
}
@@ -131,7 +138,7 @@ TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_EligibleCashback) {
CreateCreditCardOfferForCard(card, "5%"));
std::vector<Suggestion> suggestions = {Suggestion()};
- suggestions[0].backend_id = kTestGuid;
+ suggestions[0].payload = kTestGuid;
autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
suggestions);
@@ -145,7 +152,7 @@ TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_ExpiredOffer) {
CreateCreditCardOfferForCard(card, "5%", /*expired=*/true));
std::vector<Suggestion> suggestions = {Suggestion()};
- suggestions[0].backend_id = kTestGuid;
+ suggestions[0].payload = kTestGuid;
autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
suggestions);
@@ -158,7 +165,7 @@ TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_WrongUrl) {
CreateCreditCardOfferForCard(card, "5%"));
std::vector<Suggestion> suggestions = {Suggestion()};
- suggestions[0].backend_id = kTestGuid;
+ suggestions[0].payload = kTestGuid;
autofill_offer_manager_->UpdateSuggestionsWithOffers(
GURL("http://wrongurl.com/"), suggestions);
@@ -174,8 +181,8 @@ TEST_F(AutofillOfferManagerTest,
CreateCreditCardOfferForCard(cardWithOffer, "5%"));
std::vector<Suggestion> suggestions = {Suggestion(), Suggestion()};
- suggestions[0].backend_id = kTestGuid;
- suggestions[1].backend_id = kTestGuid2;
+ suggestions[0].payload = kTestGuid;
+ suggestions[1].payload = kTestGuid2;
autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
suggestions);
@@ -183,33 +190,8 @@ TEST_F(AutofillOfferManagerTest,
// suggestion[0]
EXPECT_TRUE(!suggestions[0].offer_label.empty());
EXPECT_TRUE(suggestions[1].offer_label.empty());
- EXPECT_EQ(suggestions[0].backend_id, kTestGuid2);
- EXPECT_EQ(suggestions[1].backend_id, kTestGuid);
-}
-
-TEST_F(AutofillOfferManagerTest,
- UpdateSuggestionsWithOffer_SuggestionsNotSortedByOfferPresence_ExpOff) {
- scoped_feature_list_.Reset();
- scoped_feature_list_.InitAndDisableFeature(
- features::kAutofillSortSuggestionsBasedOnOfferPresence);
- CreditCard cardWithoutOffer = CreateCreditCard(kTestGuid);
- CreditCard cardWithOffer =
- CreateCreditCard(kTestGuid2, "4111111111111111", 100);
- personal_data_manager_.AddAutofillOfferData(
- CreateCreditCardOfferForCard(cardWithOffer, "5%"));
-
- std::vector<Suggestion> suggestions = {Suggestion(), Suggestion()};
- suggestions[0].backend_id = kTestGuid;
- suggestions[1].backend_id = kTestGuid2;
- autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
- suggestions);
-
- // offer_label was set on suggestions[1] and wasn't sorted because experiment
- // is turned off.
- EXPECT_TRUE(suggestions[0].offer_label.empty());
- EXPECT_TRUE(!suggestions[1].offer_label.empty());
- EXPECT_EQ(suggestions[0].backend_id, kTestGuid);
- EXPECT_EQ(suggestions[1].backend_id, kTestGuid2);
+ EXPECT_EQ(absl::get<std::string>(suggestions[0].payload), kTestGuid2);
+ EXPECT_EQ(absl::get<std::string>(suggestions[1].payload), kTestGuid);
}
TEST_F(AutofillOfferManagerTest,
@@ -222,13 +204,13 @@ TEST_F(AutofillOfferManagerTest,
CreateCreditCardOfferForCard(card2, "5%"));
std::vector<Suggestion> suggestions = {Suggestion(), Suggestion()};
- suggestions[0].backend_id = kTestGuid;
- suggestions[1].backend_id = kTestGuid2;
+ suggestions[0].payload = kTestGuid;
+ suggestions[1].payload = kTestGuid2;
autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
suggestions);
- EXPECT_EQ(suggestions[0].backend_id, kTestGuid);
- EXPECT_EQ(suggestions[1].backend_id, kTestGuid2);
+ EXPECT_EQ(absl::get<std::string>(suggestions[0].payload), kTestGuid);
+ EXPECT_EQ(absl::get<std::string>(suggestions[1].payload), kTestGuid2);
}
TEST_F(AutofillOfferManagerTest, IsUrlEligible) {
diff --git a/chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.cc b/chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.cc
index a4a6abf2132..93f5f962e7c 100644
--- a/chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.cc
+++ b/chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.cc
@@ -48,7 +48,7 @@ AutofillVirtualCardEnrollmentInfoBarDelegateMobile::GetLearnMoreLinkText()
return virtual_card_enroll_bubble_controller_->GetLearnMoreLinkText();
}
-raw_ptr<const gfx::ImageSkia>
+const gfx::ImageSkia*
AutofillVirtualCardEnrollmentInfoBarDelegateMobile::GetIssuerIcon() const {
return virtual_card_enroll_bubble_controller_
->GetVirtualCardEnrollmentFields()
diff --git a/chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.h b/chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.h
index 7c7dc114365..de5dbeb9081 100644
--- a/chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.h
+++ b/chromium/components/autofill/core/browser/payments/autofill_virtual_card_enrollment_infobar_delegate_mobile.h
@@ -44,7 +44,7 @@ class AutofillVirtualCardEnrollmentInfoBarDelegateMobile
std::u16string GetLearnMoreLinkText() const;
// Issuer icon for the card.
- raw_ptr<const gfx::ImageSkia> GetIssuerIcon() const;
+ const gfx::ImageSkia* GetIssuerIcon() const;
// The label for the card to show in the content of the infobar.
std::u16string GetCardLabel() const;
diff --git a/chromium/components/autofill/core/browser/payments/card_unmask_challenge_option.h b/chromium/components/autofill/core/browser/payments/card_unmask_challenge_option.h
index b047128d49e..19915750cfa 100644
--- a/chromium/components/autofill/core/browser/payments/card_unmask_challenge_option.h
+++ b/chromium/components/autofill/core/browser/payments/card_unmask_challenge_option.h
@@ -21,12 +21,6 @@ enum class CardUnmaskChallengeOptionType {
// The struct used by Autofill components to represent a card unmask challenge
// option.
struct CardUnmaskChallengeOption {
- CardUnmaskChallengeOption() = default;
- CardUnmaskChallengeOption(const CardUnmaskChallengeOption&) = default;
- ~CardUnmaskChallengeOption() = default;
- CardUnmaskChallengeOption& operator=(const CardUnmaskChallengeOption&) =
- default;
-
// The unique identifier for the challenge option.
std::string id = std::string();
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc
index c5529f871a9..ba148d01a98 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -49,9 +49,6 @@ constexpr int64_t kDelayForGetUnmaskDetails = 3 * 60 * 1000; // 3 min
// Suffix for server IDs in the cache indicating that a card is a virtual card.
const char kVirtualCardIdentifier[] = "_vcn";
-bool IsLocalCard(const CreditCard* card) {
- return card && card->record_type() == CreditCard::LOCAL_CARD;
-}
} // namespace
CreditCardAccessManager::CreditCardAccessManager(
@@ -65,10 +62,11 @@ CreditCardAccessManager::CreditCardAccessManager(
personal_data_manager_(personal_data_manager),
form_event_logger_(form_event_logger) {}
-CreditCardAccessManager::~CreditCardAccessManager() {}
+CreditCardAccessManager::~CreditCardAccessManager() = default;
void CreditCardAccessManager::UpdateCreditCardFormEventLogger() {
- std::vector<CreditCard*> credit_cards = GetCreditCards();
+ std::vector<CreditCard*> credit_cards =
+ personal_data_manager_->GetCreditCards();
size_t server_record_type_count = 0;
size_t local_record_type_count = 0;
@@ -83,23 +81,6 @@ void CreditCardAccessManager::UpdateCreditCardFormEventLogger() {
form_event_logger_->set_is_context_secure(client_->IsContextSecure());
}
-std::vector<CreditCard*> CreditCardAccessManager::GetCreditCards() {
- return personal_data_manager_->GetCreditCards();
-}
-
-std::vector<CreditCard*> CreditCardAccessManager::GetCreditCardsToSuggest() {
- const std::vector<CreditCard*> cards_to_suggest =
- personal_data_manager_->GetCreditCardsToSuggest(
- client_->AreServerCardsSupported());
-
- return cards_to_suggest;
-}
-
-bool CreditCardAccessManager::ShouldDisplayGPayLogo() {
- return base::ranges::all_of(GetCreditCardsToSuggest(),
- base::not_fn(&IsLocalCard));
-}
-
bool CreditCardAccessManager::UnmaskedCardCacheIsEmpty() {
return unmasked_card_cache_.empty();
}
@@ -118,14 +99,9 @@ bool CreditCardAccessManager::IsCardPresentInUnmaskedCache(
unmasked_card_cache_.end();
}
-bool CreditCardAccessManager::ServerCardsAvailable() {
- return base::ranges::any_of(GetCreditCardsToSuggest(),
- base::not_fn(&IsLocalCard));
-}
-
bool CreditCardAccessManager::DeleteCard(const CreditCard* card) {
// Server cards cannot be deleted from within Chrome.
- bool allowed_to_delete = IsLocalCard(card);
+ bool allowed_to_delete = CreditCard::IsLocalCard(card);
if (allowed_to_delete)
personal_data_manager_->DeleteLocalCreditCards({*card});
@@ -137,7 +113,7 @@ bool CreditCardAccessManager::GetDeletionConfirmationText(
const CreditCard* card,
std::u16string* title,
std::u16string* body) {
- if (!IsLocalCard(card))
+ if (!CreditCard::IsLocalCard(card))
return false;
if (title)
@@ -154,18 +130,14 @@ bool CreditCardAccessManager::ShouldClearPreviewedForm() {
return !is_authentication_in_progress_;
}
-CreditCard* CreditCardAccessManager::GetCreditCard(std::string guid) {
- if (base::IsValidGUID(guid)) {
- return personal_data_manager_->GetCreditCardByGUID(guid);
- }
- return nullptr;
-}
-
void CreditCardAccessManager::PrepareToFetchCreditCard() {
#if !BUILDFLAG(IS_IOS)
// No need to fetch details if there are no server cards.
- if (!ServerCardsAvailable())
+ if (!base::ranges::any_of(personal_data_manager_->GetCreditCardsToSuggest(
+ client_->AreServerCardsSupported()),
+ base::not_fn(&CreditCard::IsLocalCard))) {
return;
+ }
// Do not make a preflight call if unnecessary, such as if one is already in
// progress or a recently-returned call should be currently used.
@@ -276,9 +248,7 @@ void CreditCardAccessManager::FetchCreditCard(
return;
}
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication) &&
- card->record_type() == CreditCard::VIRTUAL_CARD) {
+ if (card->record_type() == CreditCard::VIRTUAL_CARD) {
AutofillMetrics::LogServerCardUnmaskAttempt(
AutofillClient::PaymentsRpcCardType::kVirtualCard);
}
@@ -294,9 +264,7 @@ void CreditCardAccessManager::FetchCreditCard(
? "Autofill.UsedCachedVirtualCard"
: "Autofill.UsedCachedServerCard";
base::UmaHistogramCounts1000(metrics_name, ++it->second.cache_uses);
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication) &&
- card->record_type() == CreditCard::VIRTUAL_CARD) {
+ if (card->record_type() == CreditCard::VIRTUAL_CARD) {
AutofillMetrics::LogServerCardUnmaskResult(
AutofillMetrics::ServerCardUnmaskResult::kLocalCacheHit,
AutofillClient::PaymentsRpcCardType::kVirtualCard,
@@ -327,13 +295,10 @@ void CreditCardAccessManager::FetchCreditCard(
accessor_ = accessor;
// Direct to different flows based on the card record type.
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication) &&
- card_->record_type() == CreditCard::VIRTUAL_CARD) {
+ if (card_->record_type() == CreditCard::VIRTUAL_CARD)
FetchVirtualCard();
- } else {
+ else
FetchMaskedServerCard();
- }
}
void CreditCardAccessManager::FIDOAuthOptChange(bool opt_in) {
@@ -382,13 +347,10 @@ void CreditCardAccessManager::GetAuthenticationType(bool fido_auth_enabled) {
// iOS either, so offer CVC auth immediately.
OnDidGetAuthenticationType(UnmaskAuthFlowType::kCvc);
#else
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication) &&
- card_->record_type() == CreditCard::VIRTUAL_CARD) {
+ if (card_->record_type() == CreditCard::VIRTUAL_CARD)
GetAuthenticationTypeForVirtualCard(fido_auth_enabled);
- } else {
+ else
GetAuthenticationTypeForMaskedServerCard(fido_auth_enabled);
- }
#endif
}
@@ -508,9 +470,7 @@ void CreditCardAccessManager::Authenticate() {
// UnmaskDetails.
base::Value fido_request_options;
absl::optional<std::string> context_token;
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication) &&
- card_->record_type() == CreditCard::VIRTUAL_CARD) {
+ if (card_->record_type() == CreditCard::VIRTUAL_CARD) {
context_token = virtual_card_unmask_response_details_.context_token;
fido_request_options = std::move(
virtual_card_unmask_response_details_.fido_request_options.value());
@@ -549,9 +509,7 @@ void CreditCardAccessManager::Authenticate() {
NOTREACHED();
accessor_->OnCreditCardFetched(CreditCardFetchResult::kTransientError);
client_->ShowVirtualCardErrorDialog(/*is_permanent_error=*/false);
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication) &&
- card_->record_type() == CreditCard::VIRTUAL_CARD) {
+ if (card_->record_type() == CreditCard::VIRTUAL_CARD) {
AutofillMetrics::LogServerCardUnmaskResult(
AutofillMetrics::ServerCardUnmaskResult::kUnexpectedError,
AutofillClient::PaymentsRpcCardType::kVirtualCard,
@@ -692,9 +650,7 @@ void CreditCardAccessManager::OnFIDOAuthenticationComplete(
response.card, response.cvc);
form_event_logger_->LogCardUnmaskAuthenticationPromptCompleted(
unmask_auth_flow_type_);
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication) &&
- card_->record_type() == CreditCard::VIRTUAL_CARD) {
+ if (card_->record_type() == CreditCard::VIRTUAL_CARD) {
AutofillMetrics::LogServerCardUnmaskResult(
AutofillMetrics::ServerCardUnmaskResult::kAuthenticationUnmasked,
AutofillClient::PaymentsRpcCardType::kVirtualCard,
@@ -719,9 +675,7 @@ void CreditCardAccessManager::OnFIDOAuthenticationComplete(
payments::FullCardRequest::VIRTUAL_CARD_RETRIEVAL_PERMANENT_FAILURE);
accessor_->OnCreditCardFetched(result);
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication) &&
- card_->record_type() == CreditCard::VIRTUAL_CARD) {
+ if (card_->record_type() == CreditCard::VIRTUAL_CARD) {
AutofillMetrics::LogServerCardUnmaskResult(
AutofillMetrics::ServerCardUnmaskResult::kVirtualCardRetrievalError,
AutofillClient::PaymentsRpcCardType::kVirtualCard,
@@ -732,9 +686,7 @@ void CreditCardAccessManager::OnFIDOAuthenticationComplete(
// If it is an authentication error, start the CVC authentication process
// for masked server cards or the OTP authentication process for virtual
// cards.
- if (card_->record_type() == CreditCard::VIRTUAL_CARD &&
- base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication)) {
+ if (card_->record_type() == CreditCard::VIRTUAL_CARD) {
GetAuthenticationTypeForVirtualCard(/*fido_auth_enabled=*/false);
} else {
unmask_auth_flow_type_ = UnmaskAuthFlowType::kCvcFallbackFromFido;
@@ -899,7 +851,7 @@ bool CreditCardAccessManager::ShouldOfferFidoOptInDialog(
// offer it.
if (GetOrCreateFIDOAuthenticator()
->GetOrCreateFidoAuthenticationStrikeDatabase()
- ->IsMaxStrikesLimitReached()) {
+ ->ShouldBlockFeature()) {
return false;
}
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h
index fabe489f1c1..8b693f8c7de 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h
+++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h
@@ -36,6 +36,10 @@ namespace autofill {
class BrowserAutofillManager;
enum class WebauthnDialogCallbackType;
+namespace metrics {
+class AutofillMetricsBaseTest;
+}
+
// Flow type denotes which card unmask authentication method was used.
enum class UnmaskAuthFlowType {
kNone = 0,
@@ -91,7 +95,7 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester,
public:
class Accessor {
public:
- virtual ~Accessor() {}
+ virtual ~Accessor() = default;
virtual void OnCreditCardFetched(
CreditCardFetchResult result,
const CreditCard* credit_card = nullptr,
@@ -111,12 +115,6 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester,
// Logs information about current credit card data.
void UpdateCreditCardFormEventLogger();
- // Returns all credit cards.
- std::vector<CreditCard*> GetCreditCards();
- // Returns credit cards in the order to be suggested to the user.
- std::vector<CreditCard*> GetCreditCardsToSuggest();
- // Returns true only if all cards are server cards.
- bool ShouldDisplayGPayLogo();
// Returns true when deletion is allowed. Only local cards can be deleted.
bool DeleteCard(const CreditCard* card);
// Returns true if the |card| is deletable. Fills out
@@ -128,9 +126,6 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester,
// Returns false only if some form of authentication is still in progress.
bool ShouldClearPreviewedForm();
- // Retrieves instance of CreditCard with given guid.
- CreditCard* GetCreditCard(std::string guid);
-
// Makes a call to Google Payments to retrieve authentication details.
void PrepareToFetchCreditCard();
@@ -216,6 +211,7 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester,
friend class AutofillAssistantTest;
friend class BrowserAutofillManagerTest;
friend class AutofillMetricsTest;
+ friend class metrics::AutofillMetricsBaseTest;
friend class CreditCardAccessManagerTest;
#if !BUILDFLAG(IS_IOS)
@@ -242,9 +238,6 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester,
// Returns whether or not unmasked card cache is empty. Exposed for testing.
bool UnmaskedCardCacheIsEmpty();
- // Returns false if all suggested cards are local cards, otherwise true.
- bool ServerCardsAvailable();
-
// Invoked from CreditCardFIDOAuthenticator::IsUserVerifiable().
// |is_user_verifiable| is set to true only if user has a verifying platform
// authenticator. e.g. Touch/Face ID, Windows Hello, Android fingerprint,
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 9bd4752d5ca..0b5758a70fc 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -107,7 +107,7 @@ std::string BytesToBase64(const std::vector<uint8_t> bytes) {
class TestAccessor : public CreditCardAccessManager::Accessor {
public:
- TestAccessor() {}
+ TestAccessor() = default;
base::WeakPtr<TestAccessor> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
@@ -172,34 +172,34 @@ class CreditCardAccessManagerTest : public testing::Test {
void SetUp() override {
autofill_client_.SetPrefs(test::PrefServiceForTesting());
- personal_data_manager_.Init(/*profile_database=*/database_,
- /*account_database=*/nullptr,
- /*pref_service=*/autofill_client_.GetPrefs(),
- /*local_state=*/autofill_client_.GetPrefs(),
- /*identity_manager=*/nullptr,
- /*history_service=*/nullptr,
- /*strike_database=*/nullptr,
- /*image_fetcher=*/nullptr,
- /*is_off_the_record=*/false);
- personal_data_manager_.SetPrefService(autofill_client_.GetPrefs());
+ personal_data().Init(/*profile_database=*/database_,
+ /*account_database=*/nullptr,
+ /*pref_service=*/autofill_client_.GetPrefs(),
+ /*local_state=*/autofill_client_.GetPrefs(),
+ /*identity_manager=*/nullptr,
+ /*history_service=*/nullptr,
+ /*strike_database=*/nullptr,
+ /*image_fetcher=*/nullptr,
+ /*is_off_the_record=*/false);
+ personal_data().SetPrefService(autofill_client_.GetPrefs());
accessor_ = std::make_unique<TestAccessor>();
autofill_driver_ = std::make_unique<TestAutofillDriver>();
payments_client_ = new payments::TestPaymentsClient(
autofill_driver_->GetURLLoaderFactory(),
- autofill_client_.GetIdentityManager(), &personal_data_manager_);
+ autofill_client_.GetIdentityManager(), &personal_data());
autofill_client_.set_test_payments_client(
std::unique_ptr<payments::TestPaymentsClient>(payments_client_));
autofill_client_.set_test_strike_database(
std::make_unique<TestStrikeDatabase>());
browser_autofill_manager_ = std::make_unique<TestBrowserAutofillManager>(
- autofill_driver_.get(), &autofill_client_, &personal_data_manager_);
+ autofill_driver_.get(), &autofill_client_);
credit_card_access_manager_ =
- browser_autofill_manager_->credit_card_access_manager();
+ browser_autofill_manager_->GetCreditCardAccessManager();
#if !BUILDFLAG(IS_IOS)
- autofill_driver_->SetBrowserAutofillManager(
+ autofill_driver_->set_autofill_manager(
std::move(browser_autofill_manager_));
autofill_driver_->SetAuthenticator(new TestInternalAuthenticator());
auto fido_authenticator = std::make_unique<TestCreditCardFIDOAuthenticator>(
@@ -220,8 +220,8 @@ class CreditCardAccessManagerTest : public testing::Test {
// PersonalDataManager to be around when it gets destroyed.
autofill_driver_.reset();
- personal_data_manager_.SetPrefService(nullptr);
- personal_data_manager_.ClearCreditCards();
+ personal_data().SetPrefService(nullptr);
+ personal_data().ClearCreditCards();
}
bool IsAuthenticationInProgress() {
@@ -235,7 +235,7 @@ class CreditCardAccessManagerTest : public testing::Test {
credit_card_access_manager_->is_user_verifiable_ = absl::nullopt;
}
- void ClearCards() { personal_data_manager_.ClearCreditCards(); }
+ void ClearCards() { personal_data().ClearCreditCards(); }
void CreateLocalCard(std::string guid, std::string number = std::string()) {
CreditCard local_card = CreditCard();
@@ -245,7 +245,7 @@ class CreditCardAccessManagerTest : public testing::Test {
local_card.set_guid(guid);
local_card.set_record_type(CreditCard::LOCAL_CARD);
- personal_data_manager_.AddCreditCard(local_card);
+ personal_data().AddCreditCard(local_card);
}
void CreateServerCard(std::string guid,
@@ -260,7 +260,7 @@ class CreditCardAccessManagerTest : public testing::Test {
server_card.set_record_type(masked ? CreditCard::MASKED_SERVER_CARD
: CreditCard::FULL_SERVER_CARD);
server_card.set_server_id(server_id);
- personal_data_manager_.AddServerCreditCard(server_card);
+ personal_data().AddServerCreditCard(server_card);
}
CreditCardCVCAuthenticator* GetCVCAuthenticator() {
@@ -420,13 +420,11 @@ class CreditCardAccessManagerTest : public testing::Test {
void WaitForCallbacks() { task_environment_.RunUntilIdle(); }
void SetCreditCardFIDOAuthEnabled(bool enabled) {
- ::autofill::prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(),
- enabled);
+ prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(), enabled);
}
bool IsCreditCardFIDOAuthEnabled() {
- return ::autofill::prefs::IsCreditCardFIDOAuthEnabled(
- autofill_client_.GetPrefs());
+ return prefs::IsCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs());
}
UnmaskAuthFlowType getUnmaskAuthFlowType() {
@@ -437,13 +435,10 @@ class CreditCardAccessManagerTest : public testing::Test {
bool fido_authenticator_is_user_opted_in,
bool is_user_verifiable) {
scoped_feature_list_.Reset();
- scoped_feature_list_.InitWithFeatures(
- {features::kAutofillEnableVirtualCardsRiskBasedAuthentication,
- features::kAutofillCreditCardAuthentication},
- {});
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillCreditCardAuthentication);
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
- CreditCard* virtual_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* virtual_card = personal_data().GetCreditCardByGUID(kTestGUID);
virtual_card->set_record_type(CreditCard::VIRTUAL_CARD);
#if !BUILDFLAG(IS_IOS)
@@ -519,6 +514,10 @@ class CreditCardAccessManagerTest : public testing::Test {
}
protected:
+ TestPersonalDataManager& personal_data() {
+ return *autofill_client_.GetPersonalDataManager();
+ }
+
std::unique_ptr<TestAccessor> accessor_;
base::test::TaskEnvironment task_environment_;
variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
@@ -527,7 +526,6 @@ class CreditCardAccessManagerTest : public testing::Test {
TestAutofillClient autofill_client_;
std::unique_ptr<TestAutofillDriver> autofill_driver_;
scoped_refptr<AutofillWebDataService> database_;
- TestPersonalDataManager personal_data_manager_;
// TODO(crbug.com/1249665): Remove this member variable and use test-local
// feature lists.
base::test::ScopedFeatureList scoped_feature_list_;
@@ -539,47 +537,33 @@ class CreditCardAccessManagerTest : public testing::Test {
#endif
};
-// Ensures GetCreditCard() successfully retrieves Card.
-TEST_F(CreditCardAccessManagerTest, GetCreditCardSuccess) {
- CreateLocalCard(kTestGUID);
-
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
- EXPECT_NE(card, nullptr);
-}
-
-// Ensures GetCreditCard() returns nullptr for invalid GUID.
-TEST_F(CreditCardAccessManagerTest, GetCreditCardFailure) {
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
- EXPECT_EQ(card, nullptr);
-}
-
// Ensures DeleteCard() successfully removes local cards.
TEST_F(CreditCardAccessManagerTest, RemoveLocalCreditCard) {
CreateLocalCard(kTestGUID);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
- EXPECT_TRUE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID));
+ EXPECT_TRUE(personal_data().GetCreditCardWithGUID(kTestGUID));
EXPECT_TRUE(credit_card_access_manager_->DeleteCard(card));
- EXPECT_FALSE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID));
+ EXPECT_FALSE(personal_data().GetCreditCardWithGUID(kTestGUID));
}
// Ensures DeleteCard() does nothing for server cards.
TEST_F(CreditCardAccessManagerTest, RemoveServerCreditCard) {
CreateServerCard(kTestGUID);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
- EXPECT_TRUE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID));
+ EXPECT_TRUE(personal_data().GetCreditCardWithGUID(kTestGUID));
EXPECT_FALSE(credit_card_access_manager_->DeleteCard(card));
// Cannot delete server cards.
- EXPECT_TRUE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID));
+ EXPECT_TRUE(personal_data().GetCreditCardWithGUID(kTestGUID));
}
// Ensures GetDeletionConfirmationText(~) returns correct values for local
// cards.
TEST_F(CreditCardAccessManagerTest, LocalCardGetDeletionConfirmationText) {
CreateLocalCard(kTestGUID);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
std::u16string title = std::u16string();
std::u16string body = std::u16string();
@@ -596,7 +580,7 @@ TEST_F(CreditCardAccessManagerTest, LocalCardGetDeletionConfirmationText) {
// Ensures GetDeletionConfirmationText(~) returns false for server cards.
TEST_F(CreditCardAccessManagerTest, ServerCardGetDeletionConfirmationText) {
CreateServerCard(kTestGUID);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
std::u16string title = std::u16string();
std::u16string body = std::u16string();
@@ -611,7 +595,7 @@ TEST_F(CreditCardAccessManagerTest, ServerCardGetDeletionConfirmationText) {
// Tests retrieving local cards.
TEST_F(CreditCardAccessManagerTest, FetchLocalCardSuccess) {
CreateLocalCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
@@ -624,7 +608,7 @@ TEST_F(CreditCardAccessManagerTest, FetchLocalCardSuccess) {
// Ensures that FetchCreditCard() reports a failure when a card does not exist.
TEST_F(CreditCardAccessManagerTest, FetchNullptrFailure) {
- personal_data_manager_.ClearCreditCards();
+ personal_data().ClearCreditCards();
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
@@ -638,7 +622,7 @@ TEST_F(CreditCardAccessManagerTest, FetchNullptrFailure) {
// response from payments.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCSuccess) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
base::HistogramTester histogram_tester;
std::string flow_events_histogram_name = "Autofill.BetterAuth.FlowEvents.Cvc";
@@ -665,7 +649,7 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCSuccess) {
// from the server.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCNetworkError) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
@@ -681,7 +665,7 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCNetworkError) {
// from the server.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCPermanentFailure) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
@@ -696,7 +680,7 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCPermanentFailure) {
// Ensures that a "try again" response from payments does not end the flow.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCTryAgainFailure) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
@@ -807,7 +791,7 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOSuccess) {
"Autofill.BetterAuth.FlowEvents.Fido";
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
payments_client_->AddFidoEligibleCard(card->server_id(), kCredentialId,
@@ -859,12 +843,11 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOSuccessWithDcvv) {
{features::kAutofillCreditCardAuthentication,
features::kAutofillAlwaysReturnCloudTokenizedCard},
{});
- ::autofill::prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(),
- true);
+ prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(), true);
// General setup.
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
payments_client_->AddFidoEligibleCard(card->server_id(), kCredentialId,
kGooglePaymentsRpid);
@@ -901,7 +884,7 @@ TEST_F(CreditCardAccessManagerTest,
"Autofill.BetterAuth.FlowEvents.CvcFallbackFromFido";
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
payments_client_->AddFidoEligibleCard(card->server_id(), kCredentialId,
@@ -959,7 +942,7 @@ TEST_F(CreditCardAccessManagerTest,
"Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication";
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
payments_client_->AddFidoEligibleCard(card->server_id(), kCredentialId,
@@ -1002,7 +985,7 @@ TEST_F(CreditCardAccessManagerTest,
TEST_F(CreditCardAccessManagerTest,
FetchServerCardBadRequestOptionsCVCFallback) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
// Don't set Credential ID.
@@ -1032,7 +1015,7 @@ TEST_F(CreditCardAccessManagerTest,
// Payments times out.
TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOTimeoutCVCFallback) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
@@ -1047,52 +1030,6 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOTimeoutCVCFallback) {
EXPECT_EQ(kTestCvc16, accessor_->cvc());
}
-// Ensures that CVC prompt is not invoked after payments returns an error from
-// GetRealPan via FIDO for a virtual card.
-TEST_F(CreditCardAccessManagerTest, FetchVirtualCardFIDOFailureNoCVCFallback) {
- scoped_feature_list_.Reset();
- scoped_feature_list_.InitWithFeatures(
- {features::kAutofillCreditCardAuthentication},
- {features::kAutofillEnableVirtualCardsRiskBasedAuthentication});
- base::HistogramTester histogram_tester;
- CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
- GetFIDOAuthenticator()->SetUserVerifiable(true);
- SetCreditCardFIDOAuthEnabled(true);
- payments_client_->AddFidoEligibleCard(card->server_id(), kCredentialId,
- kGooglePaymentsRpid);
-
- credit_card_access_manager_->PrepareToFetchCreditCard();
- WaitForCallbacks();
-
- card->set_record_type(CreditCard::VIRTUAL_CARD);
- credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
- WaitForCallbacks();
-
- // FIDO Failure.
- EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::AUTHENTICATION_FLOW,
- GetFIDOAuthenticator()->current_flow());
- TestCreditCardFIDOAuthenticator::GetAssertion(GetFIDOAuthenticator(),
- /*did_succeed=*/true);
- EXPECT_TRUE(GetRealPanForFIDOAuth(
- AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure,
- kTestNumber, std::string(), /*is_virtual_card=*/true));
- EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kPermanentError);
- EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::NONE_FLOW,
- GetFIDOAuthenticator()->current_flow());
- EXPECT_TRUE(autofill_client_.virtual_card_error_dialog_shown());
-
- histogram_tester.ExpectUniqueSample(
- "Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication",
- AutofillMetrics::WebauthnResultMetric::kSuccess, 1);
- histogram_tester.ExpectTotalCount(
- "Autofill.BetterAuth.CardUnmaskDuration.Fido", 1);
- histogram_tester.ExpectTotalCount(
- "Autofill.BetterAuth.CardUnmaskDuration.Fido.VirtualCard."
- "VcnRetrievalFailure",
- 1);
-}
-
// Ensures the existence of user-perceived latency during the preflight call is
// correctly logged.
TEST_F(CreditCardAccessManagerTest,
@@ -1102,10 +1039,8 @@ TEST_F(CreditCardAccessManagerTest,
std::string local_guid = "00000000-0000-0000-0000-000000000003";
CreateServerCard(server_guid, "4594299181086168");
CreateLocalCard(local_guid, "4409763681177079");
- CreditCard* server_card =
- credit_card_access_manager_->GetCreditCard(server_guid);
- CreditCard* local_card =
- credit_card_access_manager_->GetCreditCard(local_guid);
+ CreditCard* server_card = personal_data().GetCreditCardByGUID(server_guid);
+ CreditCard* local_card = personal_data().GetCreditCardByGUID(local_guid);
GetFIDOAuthenticator()->SetUserVerifiable(true);
for (bool user_is_opted_in : {true, false}) {
@@ -1189,8 +1124,7 @@ TEST_F(CreditCardAccessManagerTest, Metrics_LoggingTimedOutCvcFallback) {
// Setting up a FIDO-enabled user with a local card and a server card.
std::string server_guid = "00000000-0000-0000-0000-000000000001";
CreateServerCard(server_guid, "4594299181086168");
- CreditCard* server_card =
- credit_card_access_manager_->GetCreditCard(server_guid);
+ CreditCard* server_card = personal_data().GetCreditCardByGUID(server_guid);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
payments_client_->ShouldReturnUnmaskDetailsImmediately(false);
@@ -1265,7 +1199,7 @@ TEST_F(CreditCardAccessManagerTest, FIDONewCardAuthorization) {
"Autofill.BetterAuth.FlowEvents.CvcThenFido";
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
// Opt the user in, but don't include the card above.
std::string other_server_id = "00000000-0000-0000-0000-000000000034";
// Add other FIDO eligible card, it will return RequestOptions in unmask
@@ -1324,7 +1258,7 @@ TEST_F(CreditCardAccessManagerTest, FetchExpiredServerCardInvokesCvcPrompt) {
// Creating an expired server card and opting the user in with authorized
// card.
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
card->SetExpirationYearFromString(u"2010");
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
@@ -1349,7 +1283,7 @@ TEST_F(CreditCardAccessManagerTest, FetchExpiredServerCardInvokesCvcPrompt) {
TEST_F(CreditCardAccessManagerTest,
UnmaskAuthFlowEvent_AlsoLogsServerCardSubhistogram) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
base::HistogramTester histogram_tester;
std::string flow_events_histogram_name =
"Autofill.BetterAuth.FlowEvents.Cvc.ServerCard";
@@ -1380,7 +1314,7 @@ TEST_F(CreditCardAccessManagerTest, FIDOOptInSuccess_Android) {
"Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
@@ -1432,7 +1366,7 @@ TEST_F(CreditCardAccessManagerTest, FIDOOptInUserVerificationFailure) {
"Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
@@ -1473,7 +1407,7 @@ TEST_F(CreditCardAccessManagerTest, FIDOOptInUserVerificationFailure) {
// Ensures that enrollment does not happen if the server returns a failure.
TEST_F(CreditCardAccessManagerTest, FIDOOptInServerFailure) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
@@ -1511,7 +1445,7 @@ TEST_F(CreditCardAccessManagerTest, FIDOOptInServerFailure) {
// checkbox.
TEST_F(CreditCardAccessManagerTest, FIDOOptIn_CheckboxDeclined) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
@@ -1539,7 +1473,7 @@ TEST_F(CreditCardAccessManagerTest, FIDOOptIn_CheckboxDeclined) {
// opt-in request the next time the user downstreams a card.
TEST_F(CreditCardAccessManagerTest, FIDOSettingsPageOptInSuccess_Android) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
// Setting the local opt-in state as true and implying that Payments servers
@@ -1581,7 +1515,7 @@ TEST_F(CreditCardAccessManagerTest,
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
payments_client_->AllowFidoRegistration(true);
@@ -1640,7 +1574,7 @@ TEST_F(CreditCardAccessManagerTest, FIDOEnrollment_OfferDeclined_Desktop) {
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
payments_client_->AllowFidoRegistration(true);
@@ -1677,7 +1611,7 @@ TEST_F(CreditCardAccessManagerTest,
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
payments_client_->AllowFidoRegistration(true);
@@ -1715,7 +1649,7 @@ TEST_F(CreditCardAccessManagerTest,
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
payments_client_->AllowFidoRegistration(true);
@@ -1762,7 +1696,7 @@ TEST_F(CreditCardAccessManagerTest,
"Autofill.BetterAuth.OptInCalled.FromCheckoutFlow";
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
payments_client_->AllowFidoRegistration(true);
@@ -1882,7 +1816,7 @@ TEST_F(CreditCardAccessManagerTest,
base::HistogramTester histogram_tester;
// Setting up a FIDO-enabled user with a server card.
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
// The user is FIDO-enabled from Payments.
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
@@ -1927,7 +1861,7 @@ TEST_F(CreditCardAccessManagerTest,
TEST_F(CreditCardAccessManagerTest, IntentToOptOut_OptOutAfterUnmaskSucceeds) {
// Setting up a FIDO-enabled user with a server card.
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
// The user is FIDO-enabled from Payments.
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
@@ -1963,7 +1897,7 @@ TEST_F(CreditCardAccessManagerTest, IntentToOptOut_OptOutAfterUnmaskSucceeds) {
TEST_F(CreditCardAccessManagerTest, IntentToOptOut_OptOutAfterUnmaskFails) {
// Setting up a FIDO-enabled user with a server card.
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
// The user is FIDO-enabled from Payments.
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
@@ -1998,7 +1932,7 @@ TEST_F(CreditCardAccessManagerTest, IntentToOptOut_OptOutAfterUnmaskFails) {
TEST_F(CreditCardAccessManagerTest, IntentToOptOut_OptOutFailure) {
// Setting up a FIDO-enabled user with a server card.
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
// The user is FIDO-enabled from Payments.
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
@@ -2072,7 +2006,7 @@ TEST_F(CreditCardAccessManagerTest, PreflightCallRateLimited) {
// Ensures that |is_authentication_in_progress_| is set correctly.
TEST_F(CreditCardAccessManagerTest, AuthenticationInProgress) {
CreateServerCard(kTestGUID, kTestNumber);
- CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
EXPECT_FALSE(IsAuthenticationInProgress());
@@ -2088,14 +2022,12 @@ TEST_F(CreditCardAccessManagerTest, AuthenticationInProgress) {
TEST_F(CreditCardAccessManagerTest, FetchCreditCardUsesUnmaskedCardCache) {
base::HistogramTester histogram_tester;
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false);
- CreditCard* unmasked_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* unmasked_card = personal_data().GetCreditCardByGUID(kTestGUID);
credit_card_access_manager_->CacheUnmaskedCardInfo(*unmasked_card,
kTestCvc16);
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/true);
- CreditCard* masked_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* masked_card = personal_data().GetCreditCardByGUID(kTestGUID);
credit_card_access_manager_->FetchCreditCard(masked_card,
accessor_->GetWeakPtr());
@@ -2127,8 +2059,7 @@ TEST_F(CreditCardAccessManagerTest, GetCachedUnmaskedCards) {
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
CreateServerCard(kTestGUID2, kTestNumber2, /*masked=*/true, kTestServerId2);
// Add a card to the cache.
- CreditCard* unmasked_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* unmasked_card = personal_data().GetCreditCardByGUID(kTestGUID);
credit_card_access_manager_->CacheUnmaskedCardInfo(*unmasked_card,
kTestCvc16);
@@ -2142,8 +2073,7 @@ TEST_F(CreditCardAccessManagerTest, IsCardPresentInUnmaskedCache) {
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
CreateServerCard(kTestGUID2, kTestNumber2, /*masked=*/true, kTestServerId2);
// Add a card to the cache.
- CreditCard* unmasked_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* unmasked_card = personal_data().GetCreditCardByGUID(kTestGUID);
credit_card_access_manager_->CacheUnmaskedCardInfo(*unmasked_card,
kTestCvc16);
@@ -2151,13 +2081,12 @@ TEST_F(CreditCardAccessManagerTest, IsCardPresentInUnmaskedCache) {
EXPECT_TRUE(credit_card_access_manager_->IsCardPresentInUnmaskedCache(
*unmasked_card));
EXPECT_FALSE(credit_card_access_manager_->IsCardPresentInUnmaskedCache(
- *credit_card_access_manager_->GetCreditCard(kTestGUID2)));
+ *personal_data().GetCreditCardByGUID(kTestGUID2)));
}
TEST_F(CreditCardAccessManagerTest, IsVirtualCardPresentInUnmaskedCache) {
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
- CreditCard* unmasked_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* unmasked_card = personal_data().GetCreditCardByGUID(kTestGUID);
unmasked_card->set_record_type(CreditCard::VIRTUAL_CARD);
// Add the virtual card to the cache.
@@ -2171,12 +2100,8 @@ TEST_F(CreditCardAccessManagerTest, IsVirtualCardPresentInUnmaskedCache) {
TEST_F(CreditCardAccessManagerTest, RiskBasedVirtualCardUnmasking_Success) {
base::HistogramTester histogram_tester;
- scoped_feature_list_.Reset();
- scoped_feature_list_.InitAndEnableFeature(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication);
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
- CreditCard* virtual_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* virtual_card = personal_data().GetCreditCardByGUID(kTestGUID);
virtual_card->set_record_type(CreditCard::VIRTUAL_CARD);
credit_card_access_manager_->FetchCreditCard(virtual_card,
@@ -2247,13 +2172,10 @@ TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoOnly) {
base::HistogramTester histogram_tester;
scoped_feature_list_.Reset();
- scoped_feature_list_.InitWithFeatures(
- {features::kAutofillEnableVirtualCardsRiskBasedAuthentication,
- features::kAutofillCreditCardAuthentication},
- {});
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillCreditCardAuthentication);
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
- CreditCard* virtual_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* virtual_card = personal_data().GetCreditCardByGUID(kTestGUID);
virtual_card->set_record_type(CreditCard::VIRTUAL_CARD);
// TODO(crbug.com/1249665): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
@@ -2310,13 +2232,10 @@ TEST_F(
RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoAndOtp_PrefersFido) {
base::HistogramTester histogram_tester;
scoped_feature_list_.Reset();
- scoped_feature_list_.InitWithFeatures(
- {features::kAutofillEnableVirtualCardsRiskBasedAuthentication,
- features::kAutofillCreditCardAuthentication},
- {});
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillCreditCardAuthentication);
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
- CreditCard* virtual_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* virtual_card = personal_data().GetCreditCardByGUID(kTestGUID);
virtual_card->set_record_type(CreditCard::VIRTUAL_CARD);
// TODO(crbug.com/1249665): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
@@ -2435,13 +2354,10 @@ TEST_F(
RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoOnly_FidoNotOptedIn) {
base::HistogramTester histogram_tester;
scoped_feature_list_.Reset();
- scoped_feature_list_.InitWithFeatures(
- {features::kAutofillEnableVirtualCardsRiskBasedAuthentication,
- features::kAutofillCreditCardAuthentication},
- {});
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillCreditCardAuthentication);
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
- CreditCard* virtual_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* virtual_card = personal_data().GetCreditCardByGUID(kTestGUID);
virtual_card->set_record_type(CreditCard::VIRTUAL_CARD);
// TODO(crbug.com/1249665): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
@@ -2487,13 +2403,10 @@ TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_Failure_NoOptionReturned) {
base::HistogramTester histogram_tester;
scoped_feature_list_.Reset();
- scoped_feature_list_.InitWithFeatures(
- {features::kAutofillEnableVirtualCardsRiskBasedAuthentication,
- features::kAutofillCreditCardAuthentication},
- {});
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillCreditCardAuthentication);
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
- CreditCard* virtual_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* virtual_card = personal_data().GetCreditCardByGUID(kTestGUID);
virtual_card->set_record_type(CreditCard::VIRTUAL_CARD);
// TODO(crbug.com/1249665): Switch to SetUserVerifiable after moving all
// |is_user_verifiable_| related logic from CreditCardAccessManager to
@@ -2539,13 +2452,10 @@ TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_Failure_VirtualCardRetrievalError) {
base::HistogramTester histogram_tester;
scoped_feature_list_.Reset();
- scoped_feature_list_.InitWithFeatures(
- {features::kAutofillEnableVirtualCardsRiskBasedAuthentication,
- features::kAutofillCreditCardAuthentication},
- {});
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillCreditCardAuthentication);
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
- CreditCard* virtual_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* virtual_card = personal_data().GetCreditCardByGUID(kTestGUID);
virtual_card->set_record_type(CreditCard::VIRTUAL_CARD);
// TODO(crbug.com/1249665): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
@@ -2589,13 +2499,10 @@ TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_FlowCancelled) {
base::HistogramTester histogram_tester;
scoped_feature_list_.Reset();
- scoped_feature_list_.InitWithFeatures(
- {features::kAutofillEnableVirtualCardsRiskBasedAuthentication,
- features::kAutofillCreditCardAuthentication},
- {});
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillCreditCardAuthentication);
CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
- CreditCard* virtual_card =
- credit_card_access_manager_->GetCreditCard(kTestGUID);
+ CreditCard* virtual_card = personal_data().GetCreditCardByGUID(kTestGUID);
virtual_card->set_record_type(CreditCard::VIRTUAL_CARD);
// TODO(crbug.com/1249665): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
index 146323d36d8..702fc674c78 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
+++ b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
@@ -17,6 +17,10 @@
namespace autofill {
+namespace metrics {
+class AutofillMetricsBaseTest;
+}
+
// Authenticates credit card unmasking through CVC verification.
class CreditCardCVCAuthenticator
: public payments::FullCardRequest::ResultDelegate,
@@ -123,6 +127,7 @@ class CreditCardCVCAuthenticator
friend class AutofillAssistantTest;
friend class BrowserAutofillManagerTest;
friend class AutofillMetricsTest;
+ friend class metrics::AutofillMetricsBaseTest;
friend class CreditCardAccessManagerTest;
friend class CreditCardCVCAuthenticatorTest;
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
index 0d5820dcd19..9043fc665d6 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -146,8 +146,7 @@ void CreditCardFIDOAuthenticator::OptOut() {
void CreditCardFIDOAuthenticator::IsUserVerifiable(
base::OnceCallback<void(bool)> callback) {
- if (!::autofill::IsCreditCardFidoAuthenticationEnabled() ||
- !authenticator()) {
+ if (!IsCreditCardFidoAuthenticationEnabled() || !authenticator()) {
std::move(callback).Run(false);
return;
}
@@ -165,9 +164,8 @@ void CreditCardFIDOAuthenticator::IsUserVerifiable(
}
bool CreditCardFIDOAuthenticator::IsUserOptedIn() {
- return ::autofill::IsCreditCardFidoAuthenticationEnabled() &&
- ::autofill::prefs::IsCreditCardFIDOAuthEnabled(
- autofill_client_->GetPrefs());
+ return IsCreditCardFidoAuthenticationEnabled() &&
+ prefs::IsCreditCardFIDOAuthEnabled(autofill_client_->GetPrefs());
}
UserOptInIntention CreditCardFIDOAuthenticator::GetUserOptInIntention(
@@ -280,7 +278,7 @@ CreditCardFIDOAuthenticator::GetOrCreateFidoAuthenticationStrikeDatabase() {
}
void CreditCardFIDOAuthenticator::GetAssertion(
- PublicKeyCredentialRequestOptionsPtr request_options) {
+ blink::mojom::PublicKeyCredentialRequestOptionsPtr request_options) {
#if !BUILDFLAG(IS_ANDROID)
// On desktop, during an opt-in flow, close the WebAuthn offer dialog and get
// ready to show the OS level authentication dialog. If dialog is already
@@ -306,7 +304,7 @@ void CreditCardFIDOAuthenticator::GetAssertion(
}
void CreditCardFIDOAuthenticator::MakeCredential(
- PublicKeyCredentialCreationOptionsPtr creation_options) {
+ blink::mojom::PublicKeyCredentialCreationOptionsPtr creation_options) {
#if !BUILDFLAG(IS_ANDROID)
// On desktop, close the WebAuthn offer dialog and get ready to show the OS
// level authentication dialog. If dialog is already closed, then the offer
@@ -394,13 +392,13 @@ void CreditCardFIDOAuthenticator::OptChange(
}
void CreditCardFIDOAuthenticator::OnDidGetAssertion(
- AuthenticatorStatus status,
- GetAssertionAuthenticatorResponsePtr assertion_response,
- WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
+ blink::mojom::AuthenticatorStatus status,
+ blink::mojom::GetAssertionAuthenticatorResponsePtr assertion_response,
+ blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
LogWebauthnResult(status);
// End the flow if there was an authentication error.
- if (status != AuthenticatorStatus::SUCCESS) {
+ if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
// Report failure to |requester_| if card unmasking was requested.
if (current_flow_ == AUTHENTICATION_FLOW) {
FidoAuthenticationResponse response{.did_succeed = false};
@@ -470,13 +468,13 @@ void CreditCardFIDOAuthenticator::OnDidGetAssertion(
}
void CreditCardFIDOAuthenticator::OnDidMakeCredential(
- AuthenticatorStatus status,
- MakeCredentialAuthenticatorResponsePtr attestation_response,
- WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
+ blink::mojom::AuthenticatorStatus status,
+ blink::mojom::MakeCredentialAuthenticatorResponsePtr attestation_response,
+ blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
LogWebauthnResult(status);
// End the flow if there was an authentication error.
- if (status != AuthenticatorStatus::SUCCESS) {
+ if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
// Treat failure to perform user verification as a strong signal not to
// offer opt-in in the future.
if (current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW) {
@@ -558,10 +556,10 @@ void CreditCardFIDOAuthenticator::OnFullCardRequestFailed(
requester_->OnFIDOAuthenticationComplete(response);
}
-PublicKeyCredentialRequestOptionsPtr
+blink::mojom::PublicKeyCredentialRequestOptionsPtr
CreditCardFIDOAuthenticator::ParseRequestOptions(
const base::Value& request_options) {
- auto options = PublicKeyCredentialRequestOptions::New();
+ auto options = blink::mojom::PublicKeyCredentialRequestOptions::New();
const auto* rpid = request_options.FindStringKey("relying_party_id");
options->relying_party_id = rpid ? *rpid : std::string(kGooglePaymentsRpid);
@@ -575,7 +573,7 @@ CreditCardFIDOAuthenticator::ParseRequestOptions(
options->timeout =
base::Milliseconds(timeout ? timeout->GetInt() : kWebAuthnTimeoutMs);
- options->user_verification = UserVerificationRequirement::kRequired;
+ options->user_verification = device::UserVerificationRequirement::kRequired;
const auto* key_info_list =
request_options.FindKeyOfType("key_info", base::Value::Type::LIST);
@@ -587,10 +585,10 @@ CreditCardFIDOAuthenticator::ParseRequestOptions(
return options;
}
-PublicKeyCredentialCreationOptionsPtr
+blink::mojom::PublicKeyCredentialCreationOptionsPtr
CreditCardFIDOAuthenticator::ParseCreationOptions(
const base::Value& creation_options) {
- auto options = PublicKeyCredentialCreationOptions::New();
+ auto options = blink::mojom::PublicKeyCredentialCreationOptions::New();
const auto* rpid = creation_options.FindStringKey("relying_party_id");
options->relying_party.id = rpid ? *rpid : kGooglePaymentsRpid;
@@ -646,11 +644,11 @@ CreditCardFIDOAuthenticator::ParseCreationOptions(
const auto* attestation =
creation_options.FindStringKey("attestation_conveyance_preference");
if (!attestation || base::EqualsCaseInsensitiveASCII(*attestation, "NONE")) {
- options->attestation = AttestationConveyancePreference::kNone;
+ options->attestation = device::AttestationConveyancePreference::kNone;
} else if (base::EqualsCaseInsensitiveASCII(*attestation, "INDIRECT")) {
- options->attestation = AttestationConveyancePreference::kIndirect;
+ options->attestation = device::AttestationConveyancePreference::kIndirect;
} else if (base::EqualsCaseInsensitiveASCII(*attestation, "DIRECT")) {
- options->attestation = AttestationConveyancePreference::kDirect;
+ options->attestation = device::AttestationConveyancePreference::kDirect;
} else {
NOTREACHED();
}
@@ -676,7 +674,7 @@ CreditCardFIDOAuthenticator::ParseCreationOptions(
return options;
}
-PublicKeyCredentialDescriptor
+device::PublicKeyCredentialDescriptor
CreditCardFIDOAuthenticator::ParseCredentialDescriptor(
const base::Value& key_info) {
std::vector<uint8_t> credential_id;
@@ -684,12 +682,12 @@ CreditCardFIDOAuthenticator::ParseCredentialDescriptor(
DCHECK(id);
credential_id = Base64ToBytes(*id);
- base::flat_set<FidoTransportProtocol> authenticator_transports;
+ base::flat_set<device::FidoTransportProtocol> authenticator_transports;
const auto* transports = key_info.FindKeyOfType(
"authenticator_transport_support", base::Value::Type::LIST);
if (transports && !transports->GetListDeprecated().empty()) {
for (const base::Value& transport_type : transports->GetListDeprecated()) {
- absl::optional<FidoTransportProtocol> protocol =
+ absl::optional<device::FidoTransportProtocol> protocol =
device::ConvertToFidoTransportProtocol(
base::ToLowerASCII(transport_type.GetString()));
if (protocol.has_value())
@@ -697,12 +695,13 @@ CreditCardFIDOAuthenticator::ParseCredentialDescriptor(
}
}
- return PublicKeyCredentialDescriptor(CredentialType::kPublicKey,
- credential_id, authenticator_transports);
+ return device::PublicKeyCredentialDescriptor(
+ device::CredentialType::kPublicKey, credential_id,
+ authenticator_transports);
}
base::Value CreditCardFIDOAuthenticator::ParseAssertionResponse(
- GetAssertionAuthenticatorResponsePtr assertion_response) {
+ blink::mojom::GetAssertionAuthenticatorResponsePtr assertion_response) {
base::Value response = base::Value(base::Value::Type::DICTIONARY);
response.SetKey("credential_id",
BytesToBase64(assertion_response->info->raw_id));
@@ -715,7 +714,7 @@ base::Value CreditCardFIDOAuthenticator::ParseAssertionResponse(
}
base::Value CreditCardFIDOAuthenticator::ParseAttestationResponse(
- MakeCredentialAuthenticatorResponsePtr attestation_response) {
+ blink::mojom::MakeCredentialAuthenticatorResponsePtr attestation_response) {
base::Value response = base::Value(base::Value::Type::DICTIONARY);
base::Value fido_attestation_info =
@@ -729,7 +728,8 @@ base::Value CreditCardFIDOAuthenticator::ParseAttestationResponse(
base::Value authenticator_transport_list =
base::Value(base::Value::Type::LIST);
- for (FidoTransportProtocol protocol : attestation_response->transports) {
+ for (device::FidoTransportProtocol protocol :
+ attestation_response->transports) {
authenticator_transport_list.Append(
base::Value(base::ToUpperASCII(device::ToString(protocol))));
}
@@ -770,7 +770,7 @@ bool CreditCardFIDOAuthenticator::IsValidCreationOptions(
}
void CreditCardFIDOAuthenticator::LogWebauthnResult(
- AuthenticatorStatus status) {
+ blink::mojom::AuthenticatorStatus status) {
AutofillMetrics::WebauthnFlowEvent event;
switch (current_flow_) {
case AUTHENTICATION_FLOW:
@@ -792,10 +792,10 @@ void CreditCardFIDOAuthenticator::LogWebauthnResult(
// TODO(crbug.com/949269): Add metrics for revoked pending WebAuthn requests.
AutofillMetrics::WebauthnResultMetric metric;
switch (status) {
- case AuthenticatorStatus::SUCCESS:
+ case blink::mojom::AuthenticatorStatus::SUCCESS:
metric = AutofillMetrics::WebauthnResultMetric::kSuccess;
break;
- case AuthenticatorStatus::NOT_ALLOWED_ERROR:
+ case blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR:
metric = AutofillMetrics::WebauthnResultMetric::kNotAllowedError;
break;
default:
@@ -806,8 +806,8 @@ void CreditCardFIDOAuthenticator::LogWebauthnResult(
}
void CreditCardFIDOAuthenticator::UpdateUserPref() {
- ::autofill::prefs::SetCreditCardFIDOAuthEnabled(autofill_client_->GetPrefs(),
- user_is_opted_in_);
+ prefs::SetCreditCardFIDOAuthEnabled(autofill_client_->GetPrefs(),
+ user_is_opted_in_);
}
webauthn::InternalAuthenticator* CreditCardFIDOAuthenticator::authenticator() {
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
index 83966d92ed7..0bda2fe812c 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
+++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
@@ -25,24 +25,6 @@
namespace autofill {
-using blink::mojom::AuthenticatorStatus;
-using blink::mojom::GetAssertionAuthenticatorResponse;
-using blink::mojom::GetAssertionAuthenticatorResponsePtr;
-using blink::mojom::MakeCredentialAuthenticatorResponse;
-using blink::mojom::MakeCredentialAuthenticatorResponsePtr;
-using blink::mojom::PublicKeyCredentialCreationOptions;
-using blink::mojom::PublicKeyCredentialCreationOptionsPtr;
-using blink::mojom::PublicKeyCredentialRequestOptions;
-using blink::mojom::PublicKeyCredentialRequestOptionsPtr;
-using blink::mojom::WebAuthnDOMExceptionDetailsPtr;
-using device::AttestationConveyancePreference;
-using device::AuthenticatorAttachment;
-using device::AuthenticatorSelectionCriteria;
-using device::CredentialType;
-using device::FidoTransportProtocol;
-using device::PublicKeyCredentialDescriptor;
-using device::UserVerificationRequirement;
-
// Enum denotes user's intention to opt in/out.
enum class UserOptInIntention {
// Unspecified intention. No pref mismatch.
@@ -83,9 +65,6 @@ class CreditCardFIDOAuthenticator
// The response of FIDO authentication, including necessary information needed
// by the subclasses.
struct FidoAuthenticationResponse {
- FidoAuthenticationResponse() = default;
- ~FidoAuthenticationResponse() = default;
-
// Whether the authentication was successful.
bool did_succeed = false;
// The fetched credit card if the authentication was successful. Can be
@@ -187,12 +166,12 @@ class CreditCardFIDOAuthenticator
// Invokes the WebAuthn prompt to request user verification to sign the
// challenge in |request_options|.
virtual void GetAssertion(
- PublicKeyCredentialRequestOptionsPtr request_options);
+ blink::mojom::PublicKeyCredentialRequestOptionsPtr request_options);
// Invokes the WebAuthn prompt to request user verification to sign the
// challenge in |creation_options| and create a key-pair.
virtual void MakeCredential(
- PublicKeyCredentialCreationOptionsPtr creation_options);
+ blink::mojom::PublicKeyCredentialCreationOptionsPtr creation_options);
// Makes a request to payments to either opt-in or opt-out the user.
void OptChange(base::Value authenticator_response = base::Value());
@@ -201,17 +180,17 @@ class CreditCardFIDOAuthenticator
// |assertion_response|, which will be sent to Google Payments to retrieve
// card details.
void OnDidGetAssertion(
- AuthenticatorStatus status,
- GetAssertionAuthenticatorResponsePtr assertion_response,
- WebAuthnDOMExceptionDetailsPtr dom_exception_details);
+ blink::mojom::AuthenticatorStatus status,
+ blink::mojom::GetAssertionAuthenticatorResponsePtr assertion_response,
+ blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details);
// The callback invoked from the WebAuthn prompt including the
// |attestation_response|, which will be sent to Google Payments to enroll the
// credential for this user.
void OnDidMakeCredential(
- AuthenticatorStatus status,
- MakeCredentialAuthenticatorResponsePtr attestation_response,
- WebAuthnDOMExceptionDetailsPtr dom_exception_details);
+ blink::mojom::AuthenticatorStatus status,
+ blink::mojom::MakeCredentialAuthenticatorResponsePtr attestation_response,
+ blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details);
// Sets prefstore to enable credit card authentication if rpc was successful.
void OnDidGetOptChangeResult(
@@ -227,25 +206,26 @@ class CreditCardFIDOAuthenticator
payments::FullCardRequest::FailureType failure_type) override;
// Converts |request_options| from JSON to mojom pointer.
- PublicKeyCredentialRequestOptionsPtr ParseRequestOptions(
+ blink::mojom::PublicKeyCredentialRequestOptionsPtr ParseRequestOptions(
const base::Value& request_options);
// Converts |creation_options| from JSON to mojom pointer.
- PublicKeyCredentialCreationOptionsPtr ParseCreationOptions(
+ blink::mojom::PublicKeyCredentialCreationOptionsPtr ParseCreationOptions(
const base::Value& creation_options);
// Helper function to parse |key_info| sub-dictionary found in
// |request_options| and |creation_options|.
- PublicKeyCredentialDescriptor ParseCredentialDescriptor(
+ device::PublicKeyCredentialDescriptor ParseCredentialDescriptor(
const base::Value& key_info);
// Converts |assertion_response| from mojom pointer to JSON.
base::Value ParseAssertionResponse(
- GetAssertionAuthenticatorResponsePtr assertion_response);
+ blink::mojom::GetAssertionAuthenticatorResponsePtr assertion_response);
// Converts |attestation_response| from mojom pointer to JSON.
base::Value ParseAttestationResponse(
- MakeCredentialAuthenticatorResponsePtr attestation_response);
+ blink::mojom::MakeCredentialAuthenticatorResponsePtr
+ attestation_response);
// Returns true if |request_options| contains a challenge and has a non-empty
// list of keys that each have a Credential ID.
@@ -255,7 +235,7 @@ class CreditCardFIDOAuthenticator
bool IsValidCreationOptions(const base::Value& creation_options);
// Logs the result of a WebAuthn prompt.
- void LogWebauthnResult(AuthenticatorStatus status);
+ void LogWebauthnResult(blink::mojom::AuthenticatorStatus status);
// Updates the user preference to the value of |user_is_opted_in_|.
void UpdateUserPref();
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
index 26b3dde6073..35cee8b2f02 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
@@ -347,7 +347,7 @@ TEST_F(CreditCardFIDOAuthenticatorTest, ParseRequestOptions) {
base::Value request_options_json = GetTestRequestOptions(
kTestChallenge, kTestRelyingPartyId, kTestCredentialId);
- PublicKeyCredentialRequestOptionsPtr request_options_ptr =
+ blink::mojom::PublicKeyCredentialRequestOptionsPtr request_options_ptr =
fido_authenticator_->ParseRequestOptions(std::move(request_options_json));
EXPECT_EQ(kTestChallenge, BytesToBase64(request_options_ptr->challenge));
EXPECT_EQ(kTestRelyingPartyId, request_options_ptr->relying_party_id);
@@ -356,8 +356,8 @@ TEST_F(CreditCardFIDOAuthenticatorTest, ParseRequestOptions) {
}
TEST_F(CreditCardFIDOAuthenticatorTest, ParseAssertionResponse) {
- GetAssertionAuthenticatorResponsePtr assertion_response_ptr =
- GetAssertionAuthenticatorResponse::New();
+ blink::mojom::GetAssertionAuthenticatorResponsePtr assertion_response_ptr =
+ blink::mojom::GetAssertionAuthenticatorResponse::New();
assertion_response_ptr->info = blink::mojom::CommonCredentialInfo::New();
assertion_response_ptr->info->raw_id = Base64ToBytes(kTestCredentialId);
assertion_response_ptr->signature = Base64ToBytes(kTestSignature);
@@ -375,7 +375,7 @@ TEST_F(CreditCardFIDOAuthenticatorTest, ParseCreationOptions) {
base::Value creation_options_json =
GetTestCreationOptions(kTestChallenge, kTestRelyingPartyId);
- PublicKeyCredentialCreationOptionsPtr creation_options_ptr =
+ blink::mojom::PublicKeyCredentialCreationOptionsPtr creation_options_ptr =
fido_authenticator_->ParseCreationOptions(
std::move(creation_options_json));
EXPECT_EQ(kTestChallenge, BytesToBase64(creation_options_ptr->challenge));
@@ -383,16 +383,17 @@ TEST_F(CreditCardFIDOAuthenticatorTest, ParseCreationOptions) {
// Ensure only platform authenticators are allowed.
EXPECT_EQ(
- AuthenticatorAttachment::kPlatform,
+ device::AuthenticatorAttachment::kPlatform,
creation_options_ptr->authenticator_selection->authenticator_attachment);
- EXPECT_EQ(UserVerificationRequirement::kRequired,
+ EXPECT_EQ(device::UserVerificationRequirement::kRequired,
creation_options_ptr->authenticator_selection
->user_verification_requirement);
}
TEST_F(CreditCardFIDOAuthenticatorTest, ParseAttestationResponse) {
- MakeCredentialAuthenticatorResponsePtr attestation_response_ptr =
- MakeCredentialAuthenticatorResponse::New();
+ blink::mojom::MakeCredentialAuthenticatorResponsePtr
+ attestation_response_ptr =
+ blink::mojom::MakeCredentialAuthenticatorResponse::New();
attestation_response_ptr->info = blink::mojom::CommonCredentialInfo::New();
attestation_response_ptr->attestation_object = Base64ToBytes(kTestSignature);
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc b/chromium/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc
index 022ce0acf17..bfc8f4fad26 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc
+++ b/chromium/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc
@@ -190,8 +190,6 @@ void CreditCardOtpAuthenticator::ShowOtpDialog() {
// Before showing OTP dialog, let's load required risk data if it's not
// prepared. Risk data is only required for unmask request. Not required for
// select challenge option request.
- // TODO(crbug.com/1243475): Explore the possibility of sending one
- // LoadRiskData request per session.
if (risk_data_.empty()) {
autofill_client_->LoadRiskData(
base::BindOnce(&CreditCardOtpAuthenticator::OnDidGetUnmaskRiskData,
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc
index 6c5044a5623..954658510fa 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc
+++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc
@@ -117,9 +117,8 @@ bool CreditCardSaveManager::AttemptToOfferCardLocalSave(
return false;
// Query the Autofill StrikeDatabase on if we should pop up the
// offer-to-save prompt for this card.
- show_save_prompt_ =
- !GetCreditCardSaveStrikeDatabase()->IsMaxStrikesLimitReached(
- base::UTF16ToUTF8(local_card_save_candidate_.LastFourDigits()));
+ show_save_prompt_ = !GetCreditCardSaveStrikeDatabase()->ShouldBlockFeature(
+ base::UTF16ToUTF8(local_card_save_candidate_.LastFourDigits()));
OfferCardLocalSave();
return show_save_prompt_.value_or(false);
}
@@ -279,9 +278,20 @@ void CreditCardSaveManager::AttemptToOfferCardUploadSave(
// Query the Autofill StrikeDatabase on if we should pop up the
// offer-to-save prompt for this card.
- show_save_prompt_ =
- !GetCreditCardSaveStrikeDatabase()->IsMaxStrikesLimitReached(
- base::UTF16ToUTF8(upload_request_.card.LastFourDigits()));
+ show_save_prompt_ = !GetCreditCardSaveStrikeDatabase()->ShouldBlockFeature(
+ base::UTF16ToUTF8(upload_request_.card.LastFourDigits()));
+
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+
+ // Adding the Save Card UI Experiment to the active experiments in upload
+ // request if the experiment is active.
+ if (base::FeatureList::IsEnabled(features::kAutofillSaveCardUiExperiment) &&
+ features::kAutofillSaveCardUiExperimentSelectorInNumber.Get() != 0) {
+ upload_request_.active_experiments.push_back(
+ "AutofillSaveCardUiExperiment");
+ }
+#endif
+
payments_client_->GetUploadDetails(
country_only_profiles, upload_request_.detected_values,
upload_request_.active_experiments, app_locale_,
@@ -354,8 +364,10 @@ void CreditCardSaveManager::OnDidUploadCard(
upload_card_response_details.virtual_card_enrollment_state);
uploaded_card->set_instrument_id(
upload_card_response_details.instrument_id.value());
- client_->GetVirtualCardEnrollmentManager()->OfferVirtualCardEnroll(
- *uploaded_card, VirtualCardEnrollmentSource::kUpstream);
+ client_->GetVirtualCardEnrollmentManager()->InitVirtualCardEnroll(
+ *uploaded_card, VirtualCardEnrollmentSource::kUpstream,
+ std::move(upload_card_response_details
+ .get_details_for_enrollment_response_details));
}
}
} else if (show_save_prompt_.has_value() && show_save_prompt_.value()) {
@@ -896,7 +908,7 @@ void CreditCardSaveManager::OnUserDidAcceptUploadHelper(
// |should_request_name_from_user_|.
DCHECK(should_request_name_from_user_ ||
base::FeatureList::IsEnabled(
- autofill::features::kAutofillSaveCardInfobarEditSupport));
+ features::kAutofillSaveCardInfobarEditSupport));
#else
DCHECK(should_request_name_from_user_);
#endif
@@ -916,7 +928,7 @@ void CreditCardSaveManager::OnUserDidAcceptUploadHelper(
// |should_request_expiration_date_from_user_|.
DCHECK(should_request_expiration_date_from_user_ ||
base::FeatureList::IsEnabled(
- autofill::features::kAutofillSaveCardInfobarEditSupport));
+ features::kAutofillSaveCardInfobarEditSupport));
#else
DCHECK(should_request_expiration_date_from_user_);
#endif
diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
index 8933c9dad41..7c2bd346201 100644
--- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
@@ -34,7 +34,9 @@
#include "components/autofill/core/browser/payments/payments_util.h"
#include "components/autofill/core/browser/payments/test_credit_card_save_manager.h"
#include "components/autofill/core/browser/payments/test_credit_card_save_strike_database.h"
+#include "components/autofill/core/browser/payments/test_legal_message_line.h"
#include "components/autofill/core/browser/payments/test_payments_client.h"
+#include "components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/test_autofill_clock.h"
@@ -63,6 +65,7 @@
using base::ASCIIToUTF16;
using testing::_;
using testing::AtLeast;
+using testing::DoAll;
using testing::NiceMock;
using testing::Return;
using testing::SaveArg;
@@ -122,6 +125,45 @@ class MockPersonalDataManager : public TestPersonalDataManager {
MOCK_METHOD(void, OnUserAcceptedUpstreamOffer, (), (override));
};
+class MockAutofillClient : public TestAutofillClient {
+ public:
+ explicit MockAutofillClient(
+ std::unique_ptr<TestPersonalDataManager> pdm = nullptr)
+ : TestAutofillClient(pdm ? std::move(pdm)
+ : std::make_unique<TestPersonalDataManager>()){};
+ ~MockAutofillClient() override = default;
+ MOCK_METHOD(VirtualCardEnrollmentManager*,
+ GetVirtualCardEnrollmentManager,
+ (),
+ (override));
+};
+
+class MockVirtualCardEnrollmentManager
+ : public TestVirtualCardEnrollmentManager {
+ public:
+ MockVirtualCardEnrollmentManager(
+ TestPersonalDataManager* personal_data_manager,
+ payments::TestPaymentsClient* payments_client,
+ TestAutofillClient* autofill_client)
+ : TestVirtualCardEnrollmentManager(personal_data_manager,
+ payments_client,
+ autofill_client){};
+ MOCK_METHOD(
+ void,
+ InitVirtualCardEnroll,
+ (const CreditCard& credit_card,
+ VirtualCardEnrollmentSource virtual_card_enrollment_source,
+ absl::optional<
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails>
+ get_details_for_enrollment_response_details,
+ const raw_ptr<PrefService> user_prefs,
+ VirtualCardEnrollmentManager::RiskAssessmentFunction
+ risk_assessment_function,
+ VirtualCardEnrollmentManager::VirtualCardEnrollmentFieldsLoadedCallback
+ virtual_card_enrollment_fields_loaded_callback),
+ (override));
+};
+
class CreditCardSaveManagerTest : public testing::Test {
public:
void SetUp() override {
@@ -130,37 +172,43 @@ class CreditCardSaveManagerTest : public testing::Test {
std::make_unique<TestStrikeDatabase>();
strike_database_ = test_strike_database.get();
autofill_client_.set_test_strike_database(std::move(test_strike_database));
- personal_data_.set_auto_accept_address_imports_for_testing(true);
- personal_data_.Init(/*profile_database=*/database_,
- /*account_database=*/nullptr,
- /*pref_service=*/autofill_client_.GetPrefs(),
- /*local_state=*/autofill_client_.GetPrefs(),
- /*identity_manager=*/nullptr,
- /*history_service=*/nullptr,
- /*strike_database=*/nullptr,
- /*image_fetcher=*/nullptr,
- /*is_off_the_record=*/false);
- personal_data_.OnSyncServiceInitialized(&sync_service_);
+ personal_data().set_auto_accept_address_imports_for_testing(true);
+ personal_data().Init(/*profile_database=*/database_,
+ /*account_database=*/nullptr,
+ /*pref_service=*/autofill_client_.GetPrefs(),
+ /*local_state=*/autofill_client_.GetPrefs(),
+ /*identity_manager=*/nullptr,
+ /*history_service=*/nullptr,
+ /*strike_database=*/nullptr,
+ /*image_fetcher=*/nullptr,
+ /*is_off_the_record=*/false);
+ personal_data().OnSyncServiceInitialized(&sync_service_);
autofill_driver_ = std::make_unique<TestAutofillDriver>();
payments_client_ = new payments::TestPaymentsClient(
autofill_driver_->GetURLLoaderFactory(),
- autofill_client_.GetIdentityManager(), &personal_data_);
+ autofill_client_.GetIdentityManager(), &personal_data());
autofill_client_.set_test_payments_client(
std::unique_ptr<payments::TestPaymentsClient>(payments_client_));
+ virtual_card_enrollment_manager_ =
+ std::make_unique<MockVirtualCardEnrollmentManager>(
+ autofill_client_.GetPersonalDataManager(), payments_client_,
+ &autofill_client_);
+ ON_CALL(autofill_client_, GetVirtualCardEnrollmentManager())
+ .WillByDefault(testing::Return(virtual_card_enrollment_manager_.get()));
credit_card_save_manager_ =
new TestCreditCardSaveManager(autofill_driver_.get(), &autofill_client_,
- payments_client_, &personal_data_);
+ payments_client_, &personal_data());
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
autofill::TestFormDataImporter* test_form_data_importer =
new TestFormDataImporter(
&autofill_client_, payments_client_,
std::unique_ptr<CreditCardSaveManager>(credit_card_save_manager_),
- &personal_data_, "en-US");
+ &personal_data(), "en-US");
autofill_client_.set_test_form_data_importer(
std::unique_ptr<TestFormDataImporter>(test_form_data_importer));
autofill_client_.GetStrikeDatabase();
browser_autofill_manager_ = std::make_unique<TestBrowserAutofillManager>(
- autofill_driver_.get(), &autofill_client_, &personal_data_);
+ autofill_driver_.get(), &autofill_client_);
browser_autofill_manager_->SetExpectedObservedSubmission(true);
}
@@ -170,8 +218,8 @@ class CreditCardSaveManagerTest : public testing::Test {
browser_autofill_manager_.reset();
autofill_driver_.reset();
- personal_data_.SetPrefService(nullptr);
- personal_data_.ClearCreditCards();
+ personal_data().SetPrefService(nullptr);
+ personal_data().ClearCreditCards();
}
void FormsSeen(const std::vector<FormData>& forms) {
@@ -340,12 +388,19 @@ class CreditCardSaveManagerTest : public testing::Test {
}
protected:
+ MockPersonalDataManager& personal_data() {
+ return static_cast<MockPersonalDataManager&>(
+ *autofill_client_.GetPersonalDataManager());
+ }
+
base::test::TaskEnvironment task_environment_;
- TestAutofillClient autofill_client_;
+ MockAutofillClient autofill_client_{
+ std::make_unique<MockPersonalDataManager>()};
+ std::unique_ptr<MockVirtualCardEnrollmentManager>
+ virtual_card_enrollment_manager_;
std::unique_ptr<TestAutofillDriver> autofill_driver_;
std::unique_ptr<TestBrowserAutofillManager> browser_autofill_manager_;
scoped_refptr<AutofillWebDataService> database_;
- MockPersonalDataManager personal_data_;
syncer::TestSyncService sync_service_;
// TODO(crbug.com/1291003): Refactor to use the real CreditCardSaveManager.
// Ends up getting owned (and destroyed) by TestFormDataImporter:
@@ -511,7 +566,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_OnlyCountryInAddresses) {
// Verify that even though the full address profile was saved, only the
// country was included in the upload details request to payments.
- EXPECT_EQ(1U, personal_data_.GetProfiles().size());
+ EXPECT_EQ(1U, personal_data().GetProfiles().size());
AutofillProfile only_country;
only_country.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
EXPECT_EQ(1U, payments_client_->addresses_in_upload_details().size());
@@ -521,7 +576,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_OnlyCountryInAddresses) {
only_country));
// Server did not send a server_id, expect copy of card is not stored.
- EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+ EXPECT_TRUE(personal_data().GetCreditCards().empty());
// Verify that the correct histogram entry (and only that) was logged.
ExpectUniqueCardUploadDecision(histogram_tester,
@@ -535,7 +590,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_OnlyCountryInAddresses) {
// even though only countries were included in GetUploadDetails.
EXPECT_THAT(
payments_client_->addresses_in_upload_card(),
- testing::UnorderedElementsAreArray({*personal_data_.GetProfiles()[0]}));
+ testing::UnorderedElementsAreArray({*personal_data().GetProfiles()[0]}));
}
#endif
@@ -1138,7 +1193,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FirstAndLastName) {
EXPECT_TRUE(payments_client_->active_experiments_in_request().empty());
// Server did not send a server_id, expect copy of card is not stored.
- EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+ EXPECT_TRUE(personal_data().GetCreditCards().empty());
// Verify that the correct histogram entry (and only that) was logged.
ExpectUniqueCardUploadDecision(histogram_tester,
AutofillMetrics::UPLOAD_OFFERED);
@@ -1216,7 +1271,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_LastAndFirstName) {
EXPECT_TRUE(payments_client_->active_experiments_in_request().empty());
// Server did not send a server_id, expect copy of card is not stored.
- EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+ EXPECT_TRUE(personal_data().GetCreditCards().empty());
// Verify that the correct histogram entry (and only that) was logged.
ExpectUniqueCardUploadDecision(histogram_tester,
AutofillMetrics::UPLOAD_OFFERED);
@@ -1233,8 +1288,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_LastAndFirstName) {
#endif
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NotSavedLocally) {
- personal_data_.ClearCreditCards();
- personal_data_.ClearProfiles();
+ personal_data().ClearCreditCards();
+ personal_data().ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1270,7 +1325,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NotSavedLocally) {
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
// Don't keep a copy of the card on this device.
- EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+ EXPECT_TRUE(personal_data().GetCreditCards().empty());
}
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FeatureNotEnabled) {
@@ -1866,6 +1921,68 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoNameAvailable) {
}
#endif
+#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
+TEST_F(CreditCardSaveManagerTest,
+ AttemptToOfferCardUploadSave_SaveCardUiExperimentEnabled) {
+ // Setting the flag and params for the save card ui experiment.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kAutofillSaveCardUiExperiment,
+ {{"autofill_save_card_ui_experiment_selector_in_number", "2"}});
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions());
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = u"Flo Master";
+ credit_card_form.fields[1].value = u"4111111111111111";
+ credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear());
+ credit_card_form.fields[4].value = u"123";
+ FormSubmitted(credit_card_form);
+
+ EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled());
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Confirm that active experiments vector has the correct value.
+ std::vector<const char*> active_experiments_in_request =
+ payments_client_->active_experiments_in_request();
+ EXPECT_THAT(
+ active_experiments_in_request,
+ testing::Contains(testing::StrEq("AutofillSaveCardUiExperiment")));
+}
+
+TEST_F(CreditCardSaveManagerTest,
+ AttemptToOfferCardUploadSave_SaveCardUiExperimentDisabled) {
+ // Disabling the flag for the save card ui experiment.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kAutofillSaveCardUiExperiment);
+
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions());
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ credit_card_form.fields[0].value = u"Flo Master";
+ credit_card_form.fields[1].value = u"4111111111111111";
+ credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear());
+ credit_card_form.fields[4].value = u"123";
+ FormSubmitted(credit_card_form);
+
+ EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled());
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ std::vector<const char*> active_experiments_in_request =
+ payments_client_->active_experiments_in_request();
+ EXPECT_THAT(active_experiments_in_request,
+ testing::Not(testing::Contains(
+ testing::StrEq("AutofillSaveCardUiExperiment"))));
+}
+#endif
+
// TODO(crbug.com/1113034): Create an equivalent test for iOS, or skip
// permanently if the test doesn't apply to iOS flow.
#if !BUILDFLAG(IS_IOS)
@@ -1975,14 +2092,14 @@ TEST_F(CreditCardSaveManagerTest,
profile1.SetInfo(NAME_FULL, u"Flo Master", "en-US");
profile1.SetInfo(ADDRESS_HOME_ZIP, u"H3B2Y5", "en-US");
profile1.SetInfo(ADDRESS_HOME_COUNTRY, u"CA", "en-US");
- personal_data_.AddProfile(profile1);
+ personal_data().AddProfile(profile1);
AutofillProfile profile2;
profile2.set_guid("00000000-0000-0000-0000-000000000002");
profile2.SetInfo(NAME_FULL, u"Flo Master", "en-US");
profile2.SetInfo(ADDRESS_HOME_ZIP, u"h3b 2y5", "en-US");
profile2.SetInfo(ADDRESS_HOME_COUNTRY, u"CA", "en-US");
- personal_data_.AddProfile(profile2);
+ personal_data().AddProfile(profile2);
// Set up our credit card form data.
FormData credit_card_form;
@@ -2594,7 +2711,7 @@ TEST_F(
#if !BUILDFLAG(IS_IOS)
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Create, fill and submit an address form in order to establish a recent
@@ -2644,7 +2761,7 @@ TEST_F(
#if !BUILDFLAG(IS_IOS)
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Create, fill and submit an address form in order to establish a recent
@@ -2725,13 +2842,13 @@ TEST_F(
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Run through the form submit in exactly the same way (but now Chrome knows
// that the user is a Google Payments customer).
- personal_data_.ClearCreditCards();
- personal_data_.ClearProfiles();
+ personal_data().ClearCreditCards();
+ personal_data().ClearProfiles();
FormSubmitted(credit_card_form);
// Verify the |credit_card_save_manager_| is NOT requesting cardholder name.
@@ -2802,7 +2919,7 @@ TEST_F(
// submitting.
#if !BUILDFLAG(IS_IOS)
// Wallet Sync Transport is enabled.
- personal_data_.SetSyncAndSignInState(
+ personal_data().SetSyncAndSignInState(
AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled);
// Create, fill and submit an address form in order to establish a recent
@@ -2844,7 +2961,7 @@ TEST_F(
// submitting.
#if !BUILDFLAG(IS_IOS)
// Wallet Sync Transport is not enabled.
- personal_data_.SetSyncAndSignInState(
+ personal_data().SetSyncAndSignInState(
AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled);
// Create, fill and submit an address form in order to establish a recent
@@ -3271,7 +3388,7 @@ TEST_F(CreditCardSaveManagerTest, DuplicateMaskedCreditCard_NoUpload) {
test::NextMonth().c_str(), test::NextYear().c_str(),
"1");
credit_card.SetNetworkForMaskedCard(kVisaCard);
- personal_data_.AddServerCreditCard(credit_card);
+ personal_data().AddServerCreditCard(credit_card);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3372,7 +3489,7 @@ TEST_F(CreditCardSaveManagerTest, DetectAddressName) {
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000200");
profile.SetInfo(NAME_FULL, u"John Smith", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3401,7 +3518,7 @@ TEST_F(CreditCardSaveManagerTest, DetectCardholderAndAddressNameIfMatching) {
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000200");
profile.SetInfo(NAME_FULL, u"John Smith", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3431,7 +3548,7 @@ TEST_F(CreditCardSaveManagerTest, DetectNoUniqueNameIfNamesConflict) {
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000200");
profile.SetInfo(NAME_FULL, u"John Smith", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3459,7 +3576,7 @@ TEST_F(CreditCardSaveManagerTest, DetectPostalCode) {
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000200");
profile.SetInfo(ADDRESS_HOME_ZIP, u"94043", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3488,11 +3605,11 @@ TEST_F(CreditCardSaveManagerTest, DetectNoUniquePostalCodeIfZipsConflict) {
AutofillProfile profile1;
profile1.set_guid("00000000-0000-0000-0000-000000000200");
profile1.SetInfo(ADDRESS_HOME_ZIP, u"94043", "en-US");
- personal_data_.AddProfile(profile1);
+ personal_data().AddProfile(profile1);
AutofillProfile profile2;
profile2.set_guid("00000000-0000-0000-0000-000000000201");
profile2.SetInfo(ADDRESS_HOME_ZIP, u"95051", "en-US");
- personal_data_.AddProfile(profile2);
+ personal_data().AddProfile(profile2);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3517,7 +3634,7 @@ TEST_F(CreditCardSaveManagerTest, DetectAddressLine) {
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000200");
profile.SetInfo(ADDRESS_HOME_LINE1, u"123 Testing St.", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3546,7 +3663,7 @@ TEST_F(CreditCardSaveManagerTest, DetectLocality) {
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000200");
profile.SetInfo(ADDRESS_HOME_CITY, u"Mountain View", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3574,7 +3691,7 @@ TEST_F(CreditCardSaveManagerTest, DetectAdministrativeArea) {
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000200");
profile.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3603,7 +3720,7 @@ TEST_F(CreditCardSaveManagerTest, DetectCountryCode) {
AutofillProfile profile;
profile.set_guid("00000000-0000-0000-0000-000000000200");
profile.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3630,7 +3747,7 @@ TEST_F(CreditCardSaveManagerTest, DetectCountryCode) {
TEST_F(CreditCardSaveManagerTest, DetectHasGooglePaymentAccount) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Set up our credit card form data.
@@ -3665,7 +3782,7 @@ TEST_F(CreditCardSaveManagerTest, DetectEverythingAtOnce) {
profile.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
profile.SetInfo(ADDRESS_HOME_ZIP, u"94043", "en-US");
profile.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3704,7 +3821,7 @@ TEST_F(CreditCardSaveManagerTest, DetectSubsetOfPossibleFields) {
profile.SetInfo(ADDRESS_HOME_CITY, u"Mountain View", "en-US");
profile.SetInfo(ADDRESS_HOME_ZIP, u"94043", "en-US");
profile.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3739,19 +3856,19 @@ TEST_F(CreditCardSaveManagerTest, DetectAddressComponentsAcrossProfiles) {
AutofillProfile profile1;
profile1.set_guid("00000000-0000-0000-0000-000000000200");
profile1.SetInfo(ADDRESS_HOME_LINE1, u"123 Testing St.", "en-US");
- personal_data_.AddProfile(profile1);
+ personal_data().AddProfile(profile1);
AutofillProfile profile2;
profile2.set_guid("00000000-0000-0000-0000-000000000201");
profile2.SetInfo(ADDRESS_HOME_CITY, u"Mountain View", "en-US");
- personal_data_.AddProfile(profile2);
+ personal_data().AddProfile(profile2);
AutofillProfile profile3;
profile3.set_guid("00000000-0000-0000-0000-000000000202");
profile3.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
- personal_data_.AddProfile(profile3);
+ personal_data().AddProfile(profile3);
AutofillProfile profile4;
profile4.set_guid("00000000-0000-0000-0000-000000000203");
profile4.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile4);
+ personal_data().AddProfile(profile4);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3794,7 +3911,7 @@ TEST_F(CreditCardSaveManagerTest,
profile.SetInfo(ADDRESS_HOME_CITY, u"Mountain View", "en-US");
profile.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
profile.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3853,7 +3970,7 @@ TEST_F(
profile.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
profile.SetInfo(ADDRESS_HOME_ZIP, u"94043", "en-US");
profile.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -3893,7 +4010,7 @@ TEST_F(
profile.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
profile.SetInfo(ADDRESS_HOME_ZIP, u"94043", "en-US");
profile.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data with credit card first and last name
// fields.
@@ -3941,7 +4058,7 @@ TEST_F(
profile.SetInfo(ADDRESS_HOME_CITY, u"Mountain View", "en-US");
profile.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
profile.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -4126,7 +4243,7 @@ TEST_F(CreditCardSaveManagerTest,
profile.SetInfo(ADDRESS_HOME_CITY, u"Mountain View", "en-US");
profile.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
profile.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -4175,7 +4292,7 @@ TEST_F(CreditCardSaveManagerTest,
profile1.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
profile1.SetInfo(ADDRESS_HOME_ZIP, u"94043", "en-US");
profile1.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile1);
+ personal_data().AddProfile(profile1);
AutofillProfile profile2;
profile2.set_guid("00000000-0000-0000-0000-000000000201");
profile2.SetInfo(NAME_FULL, u"Flo Master", "en-US");
@@ -4184,7 +4301,7 @@ TEST_F(CreditCardSaveManagerTest,
profile2.SetInfo(ADDRESS_HOME_STATE, u"Stateland", "en-US");
profile2.SetInfo(ADDRESS_HOME_ZIP, u"12345", "en-US");
profile2.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile2);
+ personal_data().AddProfile(profile2);
// Set up our credit card form data.
FormData credit_card_form;
@@ -4231,7 +4348,7 @@ TEST_F(CreditCardSaveManagerTest,
profile.SetInfo(ADDRESS_HOME_CITY, u"Mountain View", "en-US");
profile.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US");
profile.SetInfo(ADDRESS_HOME_COUNTRY, u"US", "en-US");
- personal_data_.AddProfile(profile);
+ personal_data().AddProfile(profile);
// Set up our credit card form data.
FormData credit_card_form;
@@ -4284,7 +4401,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) {
test::NextMonth().c_str(), test::NextYear().c_str(),
"1");
local_card.set_record_type(CreditCard::LOCAL_CARD);
- personal_data_.AddCreditCard(local_card);
+ personal_data().AddCreditCard(local_card);
// Create, fill and submit an address form in order to establish a recent
// profile which can be selected for the upload request.
@@ -4380,7 +4497,7 @@ TEST_F(CreditCardSaveManagerTest,
test::NextMonth().c_str(), test::NextYear().c_str(),
"1");
local_card.set_record_type(CreditCard::LOCAL_CARD);
- personal_data_.AddCreditCard(local_card);
+ personal_data().AddCreditCard(local_card);
// Create, fill and submit an address form in order to establish a recent
// profile which can be selected for the upload request.
@@ -4481,7 +4598,7 @@ TEST_F(CreditCardSaveManagerTest,
UploadCreditCard_ShouldAddBillingCustomerNumberInRequest) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Create, fill and submit an address form in order to establish a recent
@@ -5098,7 +5215,7 @@ TEST_F(CreditCardSaveManagerTest,
// Make sure that the PersonalDataManager gets notified when the user accepts
// an upload offer.
TEST_F(CreditCardSaveManagerTest, OnUserDidAcceptUpload_NotifiesPDM) {
- EXPECT_CALL(personal_data_, OnUserAcceptedUpstreamOffer);
+ EXPECT_CALL(personal_data(), OnUserAcceptedUpstreamOffer);
// Simulate that the user has accepted the upload from the prompt.
UserHasAcceptedUpload({});
@@ -5149,7 +5266,7 @@ TEST_F(CreditCardSaveManagerTest, LocalSaveNotOfferedForSavedUnsupportedCard) {
test::NextMonth().c_str(), test::NextYear().c_str(),
"1");
local_card.set_record_type(CreditCard::LOCAL_CARD);
- personal_data_.AddCreditCard(local_card);
+ personal_data().AddCreditCard(local_card);
// Edit the data, and submit.
credit_card_form.fields[0].value = u"Flo Master";
@@ -5287,33 +5404,109 @@ TEST_F(CreditCardSaveManagerTest, OnDidUploadCard_VirtualCardEnrollment) {
upload_card_response_details.virtual_card_enrollment_state =
enrollment_state;
+ CreditCard arg_credit_card;
+ VirtualCardEnrollmentSource arg_virtual_card_enrollment_source;
+ if (is_update_virtual_card_enrollment_enabled &&
+ enrollment_state ==
+ CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE) {
+ EXPECT_CALL(autofill_client_, GetVirtualCardEnrollmentManager).Times(1);
+ EXPECT_CALL(*virtual_card_enrollment_manager_,
+ InitVirtualCardEnroll(_, _, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<0>(&arg_credit_card),
+ SaveArg<1>(&arg_virtual_card_enrollment_source)));
+ }
+
credit_card_save_manager_->set_upload_request_card(test::GetCreditCard());
credit_card_save_manager_->OnDidUploadCard(
AutofillClient::PaymentsRpcResult::kSuccess,
upload_card_response_details);
- CreditCard uploaded_card =
- credit_card_save_manager_->upload_request()->card;
-
// The condition inside of this if-statement is true if virtual card
// enrollment should be offered.
if (is_update_virtual_card_enrollment_enabled &&
enrollment_state ==
CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE) {
- EXPECT_EQ(uploaded_card.card_art_url(),
+ EXPECT_EQ(arg_credit_card.card_art_url(),
upload_card_response_details.card_art_url);
- EXPECT_EQ(uploaded_card.instrument_id(),
+ EXPECT_EQ(arg_credit_card.instrument_id(),
upload_card_response_details.instrument_id);
- EXPECT_EQ(uploaded_card.virtual_card_enrollment_state(),
+ EXPECT_EQ(arg_credit_card.virtual_card_enrollment_state(),
upload_card_response_details.virtual_card_enrollment_state);
+ EXPECT_EQ(arg_virtual_card_enrollment_source,
+ VirtualCardEnrollmentSource::kUpstream);
} else {
- EXPECT_TRUE(uploaded_card.card_art_url().is_empty());
- EXPECT_EQ(uploaded_card.instrument_id(), 0);
- EXPECT_EQ(uploaded_card.virtual_card_enrollment_state(),
+ EXPECT_TRUE(arg_credit_card.card_art_url().is_empty());
+ EXPECT_EQ(arg_credit_card.instrument_id(), 0);
+ EXPECT_EQ(arg_credit_card.virtual_card_enrollment_state(),
CreditCard::VirtualCardEnrollmentState::UNSPECIFIED);
}
}
}
}
+TEST_F(
+ CreditCardSaveManagerTest,
+ OnDidUploadCard_VirtualCardEnrollment_GetDetailsForEnrollmentResponseDetailsReturned) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(
+ features::kAutofillEnableUpdateVirtualCardEnrollment);
+ payments::PaymentsClient::UploadCardResponseDetails
+ upload_card_response_details;
+ upload_card_response_details.card_art_url = GURL("https://example.com/");
+ upload_card_response_details.instrument_id = 9223372036854775807;
+ upload_card_response_details.virtual_card_enrollment_state =
+ CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE;
+
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails
+ get_details_for_enrollment_response_details;
+ get_details_for_enrollment_response_details.vcn_context_token =
+ "test_context_token";
+ get_details_for_enrollment_response_details.google_legal_message = {
+ TestLegalMessageLine("test_google_legal_message")};
+ get_details_for_enrollment_response_details.issuer_legal_message = {
+ TestLegalMessageLine("test_issuer_legal_message")};
+ upload_card_response_details.get_details_for_enrollment_response_details =
+ get_details_for_enrollment_response_details;
+ credit_card_save_manager_->set_upload_request_card(test::GetCreditCard());
+
+ CreditCard arg_credit_card;
+ VirtualCardEnrollmentSource arg_virtual_card_enrollment_source;
+ absl::optional<
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails>
+ arg_get_details_for_enrollment_response_details;
+ EXPECT_CALL(autofill_client_, GetVirtualCardEnrollmentManager).Times(1);
+ EXPECT_CALL(*virtual_card_enrollment_manager_,
+ InitVirtualCardEnroll(_, _, _, _, _, _))
+ .WillOnce(
+ DoAll(SaveArg<0>(&arg_credit_card),
+ SaveArg<1>(&arg_virtual_card_enrollment_source),
+ SaveArg<2>(&arg_get_details_for_enrollment_response_details)));
+
+ credit_card_save_manager_->OnDidUploadCard(
+ AutofillClient::PaymentsRpcResult::kSuccess,
+ upload_card_response_details);
+
+ EXPECT_EQ(arg_credit_card.card_art_url(),
+ upload_card_response_details.card_art_url);
+ EXPECT_EQ(arg_credit_card.instrument_id(),
+ upload_card_response_details.instrument_id);
+ EXPECT_EQ(arg_credit_card.virtual_card_enrollment_state(),
+ upload_card_response_details.virtual_card_enrollment_state);
+ EXPECT_EQ(arg_virtual_card_enrollment_source,
+ VirtualCardEnrollmentSource::kUpstream);
+ EXPECT_EQ(
+ arg_get_details_for_enrollment_response_details.value().vcn_context_token,
+ get_details_for_enrollment_response_details.vcn_context_token);
+ EXPECT_TRUE(arg_get_details_for_enrollment_response_details.value()
+ .google_legal_message[0]
+ .text() == get_details_for_enrollment_response_details
+ .google_legal_message[0]
+ .text());
+ EXPECT_TRUE(arg_get_details_for_enrollment_response_details.value()
+ .issuer_legal_message[0]
+ .text() == get_details_for_enrollment_response_details
+ .issuer_legal_message[0]
+ .text());
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.cc b/chromium/components/autofill/core/browser/payments/full_card_request.cc
index 9ab757719b4..8d2f6121023 100644
--- a/chromium/components/autofill/core/browser/payments/full_card_request.cc
+++ b/chromium/components/autofill/core/browser/payments/full_card_request.cc
@@ -297,14 +297,9 @@ void FullCardRequest::OnDidGetRealPan(
// to avoid an unwanted registration prompt.
unmask_response_details_ = response_details;
- const std::u16string cvc =
- (base::FeatureList::IsEnabled(
- features::kAutofillAlwaysReturnCloudTokenizedCard) ||
- base::FeatureList::IsEnabled(
- features::kAutofillEnableMerchantBoundVirtualCards)) &&
- !response_details.dcvv.empty()
- ? base::UTF8ToUTF16(response_details.dcvv)
- : request_->user_response.cvc;
+ const std::u16string cvc = !response_details.dcvv.empty()
+ ? base::UTF8ToUTF16(response_details.dcvv)
+ : request_->user_response.cvc;
if (result_delegate_)
result_delegate_->OnFullCardRequestSucceeded(*this, request_->card,
cvc);
diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.h b/chromium/components/autofill/core/browser/payments/full_card_request.h
index e7a0f6a6809..8baab0e06df 100644
--- a/chromium/components/autofill/core/browser/payments/full_card_request.h
+++ b/chromium/components/autofill/core/browser/payments/full_card_request.h
@@ -25,6 +25,10 @@ class CreditCardCVCAuthenticatorTest;
class CreditCard;
class PersonalDataManager;
+namespace metrics {
+class AutofillMetricsBaseTest;
+}
+
namespace payments {
// Retrieves the full card details, including the pan and the cvc.
@@ -162,6 +166,7 @@ class FullCardRequest final : public CardUnmaskDelegate {
private:
friend class autofill::BrowserAutofillManagerTest;
friend class autofill::AutofillMetricsTest;
+ friend class autofill::metrics::AutofillMetricsBaseTest;
friend class autofill::CreditCardAccessManagerTest;
friend class autofill::CreditCardCVCAuthenticatorTest;
diff --git a/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc b/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc
index e81295defac..7abd030e5d7 100644
--- a/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc
@@ -381,8 +381,6 @@ TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForExpiredMaskedServerCard) {
// Verify getting the full PAN, the expiration and the dCVV for a virtual card.
TEST_F(FullCardRequestTest, GetFullCardPanAndExpirationAndDcvvForVirtualCard) {
- scoped_feature_list_.InitAndEnableFeature(
- features::kAutofillEnableMerchantBoundVirtualCards);
EXPECT_CALL(
*result_delegate(),
OnFullCardRequestSucceeded(testing::Ref(*request()),
diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc
index 30b27b9ea49..4909a677287 100644
--- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc
+++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc
@@ -80,7 +80,7 @@ bool LocalCardMigrationManager::ShouldOfferLocalCardMigration(
}
// Don't show the prompt if max strike count was reached.
- if (GetLocalCardMigrationStrikeDatabase()->IsMaxStrikesLimitReached()) {
+ if (GetLocalCardMigrationStrikeDatabase()->ShouldBlockFeature()) {
switch (imported_credit_card_record_type_) {
case FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD:
AutofillMetrics::LogLocalCardMigrationNotOfferedDueToMaxStrikesMetric(
@@ -309,10 +309,10 @@ void LocalCardMigrationManager::OnDidMigrateLocalCards(
if (it->second == kMigrationResultPermanentFailure ||
it->second == kMigrationResultTemporaryFailure) {
card.set_migration_status(
- autofill::MigratableCreditCard::MigrationStatus::FAILURE_ON_UPLOAD);
+ MigratableCreditCard::MigrationStatus::FAILURE_ON_UPLOAD);
} else if (it->second == kMigrationResultSuccess) {
card.set_migration_status(
- autofill::MigratableCreditCard::MigrationStatus::SUCCESS_ON_UPLOAD);
+ MigratableCreditCard::MigrationStatus::SUCCESS_ON_UPLOAD);
migrated_cards.push_back(card.credit_card());
} else {
NOTREACHED();
diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
index d1ed735f3f5..3182b70031e 100644
--- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
@@ -67,21 +67,21 @@ class LocalCardMigrationManagerTest : public testing::Test {
public:
void SetUp() override {
autofill_client_.SetPrefs(test::PrefServiceForTesting());
- personal_data_.SetPrefService(autofill_client_.GetPrefs());
- personal_data_.OnSyncServiceInitialized(&sync_service_);
+ personal_data().SetPrefService(autofill_client_.GetPrefs());
+ personal_data().OnSyncServiceInitialized(&sync_service_);
autofill_driver_ = std::make_unique<TestAutofillDriver>();
payments_client_ = new payments::TestPaymentsClient(
autofill_driver_->GetURLLoaderFactory(),
- autofill_client_.GetIdentityManager(), &personal_data_);
+ autofill_client_.GetIdentityManager(), &personal_data());
autofill_client_.set_test_payments_client(
std::unique_ptr<payments::TestPaymentsClient>(payments_client_));
credit_card_save_manager_ =
new TestCreditCardSaveManager(autofill_driver_.get(), &autofill_client_,
- payments_client_, &personal_data_);
+ payments_client_, &personal_data());
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
local_card_migration_manager_ = new TestLocalCardMigrationManager(
autofill_driver_.get(), &autofill_client_, payments_client_,
- &personal_data_);
+ &personal_data());
std::unique_ptr<TestStrikeDatabase> test_strike_database =
std::make_unique<TestStrikeDatabase>();
strike_database_ = test_strike_database.get();
@@ -90,13 +90,13 @@ class LocalCardMigrationManagerTest : public testing::Test {
new TestFormDataImporter(
&autofill_client_, payments_client_,
std::unique_ptr<CreditCardSaveManager>(credit_card_save_manager_),
- &personal_data_, "en-US",
+ &personal_data(), "en-US",
std::unique_ptr<LocalCardMigrationManager>(
local_card_migration_manager_));
autofill_client_.set_test_form_data_importer(
std::unique_ptr<TestFormDataImporter>(test_form_data_importer));
browser_autofill_manager_ = std::make_unique<TestBrowserAutofillManager>(
- autofill_driver_.get(), &autofill_client_, &personal_data_);
+ autofill_driver_.get(), &autofill_client_);
browser_autofill_manager_->SetExpectedObservedSubmission(true);
}
@@ -106,8 +106,8 @@ class LocalCardMigrationManagerTest : public testing::Test {
browser_autofill_manager_.reset();
autofill_driver_.reset();
- personal_data_.SetPrefService(nullptr);
- personal_data_.ClearCreditCards();
+ personal_data().SetPrefService(nullptr);
+ personal_data().ClearCreditCards();
}
void FormsSeen(const std::vector<FormData>& updated_forms) {
@@ -170,16 +170,16 @@ class LocalCardMigrationManagerTest : public testing::Test {
void UseNewCardWithLocalCardsOnFile() {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card (but it will not match what we will enter below).
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card (but it will not match what we will enter
// below).
- AddLocalCreditCard(personal_data_, "Flo Master", "4444333322221111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4444333322221111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -197,16 +197,16 @@ class LocalCardMigrationManagerTest : public testing::Test {
void UseLocalCardWithOtherLocalCardsOnFile() {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card whose |TypeAndLastFourDigits| matches what we
// will enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card.
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -224,20 +224,20 @@ class LocalCardMigrationManagerTest : public testing::Test {
void UseLocalCardWithInvalidLocalCardsOnFile() {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card whose |TypeAndLastFourDigits| matches what we
// will enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add other invalid local credit cards (invalid card number or expired), so
// it will not trigger migration.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111112", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111112", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::LastYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -255,7 +255,7 @@ class LocalCardMigrationManagerTest : public testing::Test {
void UseServerCardWithOtherValidLocalCardsOnFile() {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a masked server credit card whose |TypeAndLastFourDigits| matches
@@ -265,9 +265,9 @@ class LocalCardMigrationManagerTest : public testing::Test {
test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
test::NextYear().c_str(), "1");
credit_card.SetNetworkForMaskedCard(kVisaCard);
- personal_data_.AddServerCreditCard(credit_card);
+ personal_data().AddServerCreditCard(credit_card);
// Add one valid local credit card, so it will trigger migration
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -285,7 +285,7 @@ class LocalCardMigrationManagerTest : public testing::Test {
void UseServerCardWithInvalidLocalCardsOnFile() {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a masked credit card whose |TypeAndLastFourDigits| matches what we
@@ -295,13 +295,13 @@ class LocalCardMigrationManagerTest : public testing::Test {
test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
test::NextYear().c_str(), "1");
credit_card.SetNetworkForMaskedCard(kVisaCard);
- personal_data_.AddServerCreditCard(credit_card);
+ personal_data().AddServerCreditCard(credit_card);
// Add other invalid local credit cards (invalid card number or expired), so
// it will not trigger migration.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111112", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111112", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::LastYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -317,11 +317,14 @@ class LocalCardMigrationManagerTest : public testing::Test {
}
protected:
+ TestPersonalDataManager& personal_data() {
+ return *autofill_client_.GetPersonalDataManager();
+ }
+
base::test::TaskEnvironment task_environment_;
TestAutofillClient autofill_client_;
std::unique_ptr<TestAutofillDriver> autofill_driver_;
std::unique_ptr<TestBrowserAutofillManager> browser_autofill_manager_;
- TestPersonalDataManager personal_data_;
syncer::TestSyncService sync_service_;
base::test::ScopedFeatureList scoped_feature_list_;
// Ends up getting owned (and destroyed) by TestAutofillClient:
@@ -339,12 +342,12 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_UseLocalCardWithOneLocal) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -427,11 +430,11 @@ TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_SignInOnly) {
TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_NoPaymentsAccount) {
// Add a local credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card.
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -453,7 +456,7 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_LocalCardMatchMaskedServerCard) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a masked server card whose |TypeAndLastFourDigits| matches a local
@@ -462,14 +465,14 @@ TEST_F(LocalCardMigrationManagerTest,
test::SetCreditCardInfo(&server_card, "Flo Master", "1111", "11",
test::NextYear().c_str(), "1");
server_card.SetNetworkForMaskedCard(kVisaCard);
- personal_data_.AddServerCreditCard(server_card);
+ personal_data().AddServerCreditCard(server_card);
// Add a local card whose |TypeAndLastFourDigits| matches a masked server
// card.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -491,20 +494,20 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_LocalCardMatchFullServerCard) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a full server card whose number matches a local card.
CreditCard server_card(CreditCard::FULL_SERVER_CARD, "a123");
test::SetCreditCardInfo(&server_card, "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1");
- personal_data_.AddServerCreditCard(server_card);
+ personal_data().AddServerCreditCard(server_card);
// Add a local credit card whose number matches a full server card.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -524,16 +527,16 @@ TEST_F(LocalCardMigrationManagerTest,
TEST_F(LocalCardMigrationManagerTest, GetDetectedValues_AllWithCardHolderName) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card with a different cardholder name.
- AddLocalCreditCard(personal_data_, "John Smith", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "John Smith", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -557,16 +560,16 @@ TEST_F(LocalCardMigrationManagerTest,
GetDetectedValues_OneCardWithoutCardHolderName) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card without card holder name.
- AddLocalCreditCard(personal_data_, "", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Set up our credit card form data.
@@ -610,7 +613,7 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_ShouldAddMigrateCardsBillingCustomerNumberInRequest) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Use one local card with more valid local cards available.
@@ -636,12 +639,12 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_ShouldAddUploadCardSourceInRequest_SettingsPage) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card. One migratable credit card will still trigger
// migration on settings page.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -664,12 +667,12 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_TriggerFromSettingsPage) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card. One migratable credit card will still trigger
// migration on settings page.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -697,16 +700,16 @@ TEST_F(LocalCardMigrationManagerTest,
TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_MigrationSuccess) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card for migration.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Verify that it exists in the local database.
- EXPECT_TRUE(personal_data_.GetCreditCardByNumber("4111111111111111"));
+ EXPECT_TRUE(personal_data().GetCreditCardByNumber("4111111111111111"));
// Get the migratable credit cards.
local_card_migration_manager_->GetMigratableCreditCards();
@@ -729,7 +732,7 @@ TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_MigrationSuccess) {
autofill::MigratableCreditCard::MigrationStatus::SUCCESS_ON_UPLOAD);
// Local card should *not* be present as it is migrated already.
- EXPECT_FALSE(personal_data_.GetCreditCardByNumber("4111111111111111"));
+ EXPECT_FALSE(personal_data().GetCreditCardByNumber("4111111111111111"));
}
// Verify that given the parsed response from the payments client, the migration
@@ -738,17 +741,17 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_MigrationTemporaryFailure) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card. One migratable credit card will still trigger
// migration on settings page.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Verify that it exists in local database.
- EXPECT_TRUE(personal_data_.GetCreditCardByNumber("4111111111111111"));
+ EXPECT_TRUE(personal_data().GetCreditCardByNumber("4111111111111111"));
// Get the migratable credit cards.
local_card_migration_manager_->GetMigratableCreditCards();
@@ -772,7 +775,7 @@ TEST_F(LocalCardMigrationManagerTest,
autofill::MigratableCreditCard::MigrationStatus::FAILURE_ON_UPLOAD);
// Local card should be present as it is not migrated.
- EXPECT_TRUE(personal_data_.GetCreditCardByNumber("4111111111111111"));
+ EXPECT_TRUE(personal_data().GetCreditCardByNumber("4111111111111111"));
}
// Verify that given the parsed response from the payments client, the migration
@@ -781,17 +784,17 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_MigrationPermanentFailure) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card. One migratable credit card will still trigger
// migration on settings page.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Verify that it exists in local database.
- EXPECT_TRUE(personal_data_.GetCreditCardByNumber("4111111111111111"));
+ EXPECT_TRUE(personal_data().GetCreditCardByNumber("4111111111111111"));
// Get the migratable credit cards.
local_card_migration_manager_->GetMigratableCreditCards();
@@ -815,22 +818,22 @@ TEST_F(LocalCardMigrationManagerTest,
autofill::MigratableCreditCard::MigrationStatus::FAILURE_ON_UPLOAD);
// Local card should be present as it is not migrated.
- EXPECT_TRUE(personal_data_.GetCreditCardByNumber("4111111111111111"));
+ EXPECT_TRUE(personal_data().GetCreditCardByNumber("4111111111111111"));
}
// Verify selected cards are correctly passed to manager.
TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_ToggleIsChosen) {
const base::GUID guid1 = base::GUID::GenerateRandomV4();
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1", guid1);
const base::GUID guid2 = base::GUID::GenerateRandomV4();
- AddLocalCreditCard(personal_data_, "Flo Master", "5454545454545454", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5454545454545454", "11",
test::NextYear().c_str(), "1", guid2);
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
local_card_migration_manager_->GetMigratableCreditCards();
@@ -850,16 +853,16 @@ TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_ToggleIsChosen) {
TEST_F(LocalCardMigrationManagerTest, DeleteLocalCardViaMigrationDialog) {
const base::GUID guid = base::GUID::GenerateRandomV4();
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1", guid);
const std::string guid_str = guid.AsLowercaseString();
- EXPECT_TRUE(personal_data_.GetCreditCardWithGUID(guid_str.c_str()));
+ EXPECT_TRUE(personal_data().GetCreditCardWithGUID(guid_str.c_str()));
local_card_migration_manager_->OnUserDeletedLocalCardViaMigrationDialog(
guid_str);
- EXPECT_FALSE(personal_data_.GetCreditCardWithGUID(guid_str.c_str()));
+ EXPECT_FALSE(personal_data().GetCreditCardWithGUID(guid_str.c_str()));
}
// Use one local card with more valid local cards available, don't show prompt
@@ -916,16 +919,16 @@ TEST_F(LocalCardMigrationManagerTest,
// strike count is logged.
TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_StrikeCountUMALogged) {
const base::GUID guid1 = base::GUID::GenerateRandomV4();
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1", guid1);
const base::GUID guid2 = base::GUID::GenerateRandomV4();
- AddLocalCreditCard(personal_data_, "Flo Master", "5454545454545454", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5454545454545454", "11",
test::NextYear().c_str(), "1", guid2);
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
local_card_migration_manager_->GetMigratableCreditCards();
@@ -954,16 +957,16 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_MigrationAbortWhenUseUnsupportedLocalCard) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card.
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -991,16 +994,16 @@ TEST_F(LocalCardMigrationManagerTest,
MigrateCreditCard_MigrateWhenHasSupportedLocalCard) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card.
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -1037,7 +1040,7 @@ TEST_F(
MigrateCreditCard_MigrateWhenUseUnsupportedServerCardWithSupportedLocalCard) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a masked server credit card whose |TypeAndLastFourDigits| matches what
@@ -1046,9 +1049,9 @@ TEST_F(
test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
test::NextYear().c_str(), "1");
credit_card.SetNetworkForMaskedCard(kVisaCard);
- personal_data_.AddServerCreditCard(credit_card);
+ personal_data().AddServerCreditCard(credit_card);
// Add one valid local credit card, so it will trigger migration
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -1078,7 +1081,7 @@ TEST_F(
MigrateCreditCard_MigrateAbortWhenUseSupportedServerCardWithUnsupportedLocalCard) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a masked server credit card whose |TypeAndLastFourDigits| matches what
@@ -1087,9 +1090,9 @@ TEST_F(
test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
test::NextYear().c_str(), "1");
credit_card.SetNetworkForMaskedCard(kVisaCard);
- personal_data_.AddServerCreditCard(credit_card);
+ personal_data().AddServerCreditCard(credit_card);
// Add one valid local credit card, so it will trigger migration
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -1192,12 +1195,12 @@ TEST_F(LocalCardMigrationManagerTest,
LogMigrationOrigin_TriggerFromSettingsPage) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card. One migratable credit card will still trigger
// migration on settings page.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -1241,11 +1244,11 @@ TEST_F(LocalCardMigrationManagerTest,
base::HistogramTester histogram_tester;
// Add a local credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card.
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -1332,16 +1335,16 @@ TEST_F(LocalCardMigrationManagerTest,
LogMigrationDecisionMetric_UseUnsupportedLocalCard) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a local credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
- AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
// Add another local credit card.
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -1374,7 +1377,7 @@ TEST_F(LocalCardMigrationManagerTest,
LogMigrationDecisionMetric_NoSupportedCardsForSupportedServerCard) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a masked server credit card whose |TypeAndLastFourDigits| matches what
@@ -1383,9 +1386,9 @@ TEST_F(LocalCardMigrationManagerTest,
test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
test::NextYear().c_str(), "1");
credit_card.SetNetworkForMaskedCard(kVisaCard);
- personal_data_.AddServerCreditCard(credit_card);
+ personal_data().AddServerCreditCard(credit_card);
// Add one valid local credit card, so it will trigger migration
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
@@ -1418,7 +1421,7 @@ TEST_F(LocalCardMigrationManagerTest,
LogMigrationDecisionMetric_NoSupportedCardsForUnsupportedServerCard) {
// Set the billing_customer_number to designate existence of a Payments
// account.
- personal_data_.SetPaymentsCustomerData(
+ personal_data().SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
// Add a masked server credit card whose |TypeAndLastFourDigits| matches what
@@ -1427,9 +1430,9 @@ TEST_F(LocalCardMigrationManagerTest,
test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
test::NextYear().c_str(), "1");
credit_card.SetNetworkForMaskedCard(kVisaCard);
- personal_data_.AddServerCreditCard(credit_card);
+ personal_data().AddServerCreditCard(credit_card);
// Add one valid local credit card, so it will trigger migration
- AddLocalCreditCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11",
test::NextYear().c_str(), "1",
base::GUID::GenerateRandomV4());
diff --git a/chromium/components/autofill/core/browser/payments/offer_notification_handler.cc b/chromium/components/autofill/core/browser/payments/offer_notification_handler.cc
index 6a833fe7d2b..54cbe715153 100644
--- a/chromium/components/autofill/core/browser/payments/offer_notification_handler.cc
+++ b/chromium/components/autofill/core/browser/payments/offer_notification_handler.cc
@@ -18,7 +18,7 @@ bool IsOfferValid(AutofillOfferData* offer) {
if (!offer)
return false;
- if (offer->merchant_origins.empty())
+ if (offer->GetMerchantOrigins().empty())
return false;
if (offer->IsPromoCodeOffer() &&
@@ -63,8 +63,8 @@ void OfferNotificationHandler::UpdateOfferNotificationVisibility(
}
client->UpdateOfferNotification(
- offer, shown_notification_ids_.contains(offer->offer_id));
- shown_notification_ids_.insert(offer->offer_id);
+ offer, shown_notification_ids_.contains(offer->GetOfferId()));
+ shown_notification_ids_.insert(offer->GetOfferId());
}
void OfferNotificationHandler::ClearShownNotificationIdForTesting() {
diff --git a/chromium/components/autofill/core/browser/payments/payments_client.cc b/chromium/components/autofill/core/browser/payments/payments_client.cc
index d8735a9e178..d20f52a3f3b 100644
--- a/chromium/components/autofill/core/browser/payments/payments_client.cc
+++ b/chromium/components/autofill/core/browser/payments/payments_client.cc
@@ -13,12 +13,9 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_experiments.h"
@@ -28,18 +25,21 @@
#include "components/autofill/core/browser/payments/account_info_getter.h"
#include "components/autofill/core/browser/payments/local_card_migration_manager.h"
#include "components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request.h"
+#include "components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.h"
+#include "components/autofill/core/browser/payments/payments_requests/get_upload_details_request.h"
+#include "components/autofill/core/browser/payments/payments_requests/migrate_cards_request.h"
+#include "components/autofill/core/browser/payments/payments_requests/opt_change_request.h"
#include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
#include "components/autofill/core/browser/payments/payments_requests/select_challenge_option_request.h"
#include "components/autofill/core/browser/payments/payments_requests/unmask_card_request.h"
#include "components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request.h"
+#include "components/autofill/core/browser/payments/payments_requests/upload_card_request.h"
#include "components/autofill/core/browser/payments/payments_service_url.h"
#include "components/autofill/core/common/autofill_features.h"
-#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
#include "components/signin/public/identity_manager/scope_set.h"
#include "components/variations/net/variations_http_headers.h"
-#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
@@ -52,31 +52,6 @@ namespace autofill::payments {
namespace {
-const char kGetUnmaskDetailsRequestPath[] =
- "payments/apis/chromepaymentsservice/getdetailsforgetrealpan";
-
-const char kOptChangeRequestPath[] =
- "payments/apis/chromepaymentsservice/updateautofilluserpreference";
-
-const char kGetUploadDetailsRequestPath[] =
- "payments/apis/chromepaymentsservice/getdetailsforsavecard";
-
-const char kUploadCardRequestPath[] =
- "payments/apis-secure/chromepaymentsservice/savecard"
- "?s7e_suffix=chromewallet";
-const char kUploadCardRequestFormat[] =
- "requestContentType=application/json; charset=utf-8&request=%s"
- "&s7e_1_pan=%s&s7e_13_cvc=%s";
-const char kUploadCardRequestFormatWithoutCvc[] =
- "requestContentType=application/json; charset=utf-8&request=%s"
- "&s7e_1_pan=%s";
-
-const char kMigrateCardsRequestPath[] =
- "payments/apis-secure/chromepaymentsservice/migratecards"
- "?s7e_suffix=chromewallet";
-const char kMigrateCardsRequestFormat[] =
- "requestContentType=application/json; charset=utf-8&request=%s";
-
const char kTokenFetchId[] = "wallet_client";
const char kPaymentsOAuth2Scope[] =
"https://www.googleapis.com/auth/wallet.chrome";
@@ -99,795 +74,6 @@ GURL GetRequestUrl(const std::string& path) {
return GetBaseSecureUrl().Resolve(path);
}
-void SetStringIfNotEmpty(const AutofillDataModel& profile,
- const ServerFieldType& type,
- const std::string& app_locale,
- const std::string& path,
- base::Value& dictionary) {
- const std::u16string value = profile.GetInfo(AutofillType(type), app_locale);
- if (!value.empty())
- dictionary.SetKey(path, base::Value(value));
-}
-
-void AppendStringIfNotEmpty(const AutofillProfile& profile,
- const ServerFieldType& type,
- const std::string& app_locale,
- base::Value& list) {
- const std::u16string value = profile.GetInfo(type, app_locale);
- if (!value.empty())
- list.Append(value);
-}
-
-// Returns a dictionary with the structure expected by Payments RPCs, containing
-// each of the fields in |profile|, formatted according to |app_locale|. If
-// |include_non_location_data| is false, the name and phone number in |profile|
-// are not included.
-base::Value BuildAddressDictionary(const AutofillProfile& profile,
- const std::string& app_locale,
- bool include_non_location_data) {
- base::Value postal_address(base::Value::Type::DICTIONARY);
-
- if (include_non_location_data) {
- SetStringIfNotEmpty(profile, NAME_FULL, app_locale,
- PaymentsClient::kRecipientName, postal_address);
- }
-
- base::Value address_lines(base::Value::Type::LIST);
- AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE1, app_locale,
- address_lines);
- AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE2, app_locale,
- address_lines);
- AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE3, app_locale,
- address_lines);
- if (!address_lines.GetListDeprecated().empty())
- postal_address.SetKey("address_line", std::move(address_lines));
-
- SetStringIfNotEmpty(profile, ADDRESS_HOME_CITY, app_locale, "locality_name",
- postal_address);
- SetStringIfNotEmpty(profile, ADDRESS_HOME_STATE, app_locale,
- "administrative_area_name", postal_address);
- SetStringIfNotEmpty(profile, ADDRESS_HOME_ZIP, app_locale,
- "postal_code_number", postal_address);
-
- // Use GetRawInfo to get a country code instead of the country name:
- const std::u16string country_code = profile.GetRawInfo(ADDRESS_HOME_COUNTRY);
- if (!country_code.empty())
- postal_address.SetKey("country_name_code", base::Value(country_code));
-
- base::Value address(base::Value::Type::DICTIONARY);
- address.SetKey("postal_address", std::move(postal_address));
-
- if (include_non_location_data) {
- SetStringIfNotEmpty(profile, PHONE_HOME_WHOLE_NUMBER, app_locale,
- PaymentsClient::kPhoneNumber, address);
- }
-
- return address;
-}
-
-// Returns a dictionary of the credit card with the structure expected by
-// Payments RPCs, containing expiration month, expiration year and cardholder
-// name (if any) fields in |credit_card|, formatted according to |app_locale|.
-// |pan_field_name| is the field name for the encrypted pan. We use each credit
-// card's guid as the unique id.
-base::Value BuildCreditCardDictionary(const CreditCard& credit_card,
- const std::string& app_locale,
- const std::string& pan_field_name) {
- base::Value card(base::Value::Type::DICTIONARY);
- card.SetKey("unique_id", base::Value(credit_card.guid()));
-
- const std::u16string exp_month =
- credit_card.GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), app_locale);
- const std::u16string exp_year = credit_card.GetInfo(
- AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale);
- int value = 0;
- if (base::StringToInt(exp_month, &value))
- card.SetKey("expiration_month", base::Value(value));
- if (base::StringToInt(exp_year, &value))
- card.SetKey("expiration_year", base::Value(value));
- SetStringIfNotEmpty(credit_card, CREDIT_CARD_NAME_FULL, app_locale,
- "cardholder_name", card);
-
- if (credit_card.HasNonEmptyValidNickname())
- card.SetKey("nickname", base::Value(credit_card.nickname()));
-
- card.SetKey("encrypted_pan", base::Value("__param:" + pan_field_name));
- return card;
-}
-
-// Populates the list of active experiments that affect either the data sent in
-// payments RPCs or whether the RPCs are sent or not.
-void SetActiveExperiments(const std::vector<const char*>& active_experiments,
- base::Value& request_dict) {
- if (active_experiments.empty())
- return;
-
- base::Value active_chrome_experiments(base::Value::Type::LIST);
- for (const char* it : active_experiments)
- active_chrome_experiments.Append(it);
-
- request_dict.SetKey("active_chrome_experiments",
- std::move(active_chrome_experiments));
-}
-
-// TODO(crbug.com/1249665): Move requests to separate files.
-class GetUnmaskDetailsRequest : public PaymentsRequest {
- public:
- GetUnmaskDetailsRequest(
- base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
- PaymentsClient::UnmaskDetails&)> callback,
- const std::string& app_locale,
- const bool full_sync_enabled)
- : callback_(std::move(callback)),
- app_locale_(app_locale),
- full_sync_enabled_(full_sync_enabled) {}
-
- GetUnmaskDetailsRequest(const GetUnmaskDetailsRequest&) = delete;
- GetUnmaskDetailsRequest& operator=(const GetUnmaskDetailsRequest&) = delete;
-
- ~GetUnmaskDetailsRequest() override = default;
-
- std::string GetRequestUrlPath() override {
- return kGetUnmaskDetailsRequestPath;
- }
-
- std::string GetRequestContentType() override { return "application/json"; }
-
- std::string GetRequestContent() override {
- base::Value request_dict(base::Value::Type::DICTIONARY);
- base::Value context(base::Value::Type::DICTIONARY);
- context.SetKey("language_code", base::Value(app_locale_));
- context.SetKey("billable_service",
- base::Value(kUnmaskCardBillableServiceNumber));
- request_dict.SetKey("context", std::move(context));
-
- base::Value chrome_user_context(base::Value::Type::DICTIONARY);
- chrome_user_context.SetKey("full_sync_enabled",
- base::Value(full_sync_enabled_));
- request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
-
- std::string request_content;
- base::JSONWriter::Write(request_dict, &request_content);
- VLOG(3) << "getdetailsforgetrealpan request body: " << request_content;
- return request_content;
- }
-
- void ParseResponse(const base::Value& response) override {
- const auto* method = response.FindStringKey("authentication_method");
- if (method) {
- if (*method == "CVC") {
- unmask_details_.unmask_auth_method =
- AutofillClient::UnmaskAuthMethod::kCvc;
- } else if (*method == "FIDO") {
- unmask_details_.unmask_auth_method =
- AutofillClient::UnmaskAuthMethod::kFido;
- }
- }
-
- const auto* offer_fido_opt_in =
- response.FindKeyOfType("offer_fido_opt_in", base::Value::Type::BOOLEAN);
- unmask_details_.offer_fido_opt_in =
- offer_fido_opt_in && offer_fido_opt_in->GetBool();
-
- const auto* dictionary_value = response.FindKeyOfType(
- "fido_request_options", base::Value::Type::DICTIONARY);
- if (dictionary_value)
- unmask_details_.fido_request_options = dictionary_value->Clone();
-
- const auto* fido_eligible_card_ids = response.FindKeyOfType(
- "fido_eligible_card_id", base::Value::Type::LIST);
- if (fido_eligible_card_ids) {
- for (const base::Value& result :
- fido_eligible_card_ids->GetListDeprecated()) {
- unmask_details_.fido_eligible_card_ids.insert(result.GetString());
- }
- }
- }
-
- bool IsResponseComplete() override {
- return unmask_details_.unmask_auth_method !=
- AutofillClient::UnmaskAuthMethod::kUnknown;
- }
-
- void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
- std::move(callback_).Run(result, unmask_details_);
- }
-
- private:
- base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
- PaymentsClient::UnmaskDetails&)>
- callback_;
- std::string app_locale_;
- const bool full_sync_enabled_;
-
- // Suggested authentication method and other information to facilitate card
- // unmasking.
- payments::PaymentsClient::UnmaskDetails unmask_details_;
-};
-
-class OptChangeRequest : public PaymentsRequest {
- public:
- OptChangeRequest(
- const PaymentsClient::OptChangeRequestDetails& request_details,
- base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
- PaymentsClient::OptChangeResponseDetails&)>
- callback,
- const bool full_sync_enabled)
- : request_details_(request_details),
- callback_(std::move(callback)),
- full_sync_enabled_(full_sync_enabled) {}
-
- OptChangeRequest(const OptChangeRequest&) = delete;
- OptChangeRequest& operator=(const OptChangeRequest&) = delete;
-
- ~OptChangeRequest() override = default;
-
- std::string GetRequestUrlPath() override { return kOptChangeRequestPath; }
-
- std::string GetRequestContentType() override { return "application/json"; }
-
- std::string GetRequestContent() override {
- base::Value request_dict(base::Value::Type::DICTIONARY);
- base::Value context(base::Value::Type::DICTIONARY);
- context.SetKey("language_code", base::Value(request_details_.app_locale));
- context.SetKey("billable_service",
- base::Value(kUnmaskCardBillableServiceNumber));
- request_dict.SetKey("context", std::move(context));
-
- base::Value chrome_user_context(base::Value::Type::DICTIONARY);
- chrome_user_context.SetKey("full_sync_enabled",
- base::Value(full_sync_enabled_));
- request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
-
- std::string reason;
- switch (request_details_.reason) {
- case PaymentsClient::OptChangeRequestDetails::ENABLE_FIDO_AUTH:
- reason = "ENABLE_FIDO_AUTH";
- break;
- case PaymentsClient::OptChangeRequestDetails::DISABLE_FIDO_AUTH:
- reason = "DISABLE_FIDO_AUTH";
- break;
- case PaymentsClient::OptChangeRequestDetails::ADD_CARD_FOR_FIDO_AUTH:
- reason = "ADD_CARD_FOR_FIDO_AUTH";
- break;
- default:
- NOTREACHED();
- break;
- }
- request_dict.SetKey("reason", base::Value(reason));
-
- if (request_details_.fido_authenticator_response.has_value()) {
- base::Value fido_authentication_info(base::Value::Type::DICTIONARY);
-
- fido_authentication_info.SetKey(
- "fido_authenticator_response",
- std::move(request_details_.fido_authenticator_response.value()));
-
- if (!request_details_.card_authorization_token.empty()) {
- fido_authentication_info.SetKey(
- "card_authorization_token",
- base::Value(request_details_.card_authorization_token));
- }
-
- request_dict.SetKey("fido_authentication_info",
- std::move(fido_authentication_info));
- }
-
- std::string request_content;
- base::JSONWriter::Write(request_dict, &request_content);
- VLOG(3) << "updateautofilluserpreference request body: " << request_content;
- return request_content;
- }
-
- void ParseResponse(const base::Value& response) override {
- const auto* fido_authentication_info = response.FindKeyOfType(
- "fido_authentication_info", base::Value::Type::DICTIONARY);
- if (!fido_authentication_info)
- return;
-
- const auto* user_status =
- fido_authentication_info->FindStringKey("user_status");
- if (user_status && *user_status != "UNKNOWN_USER_STATUS")
- response_details_.user_is_opted_in =
- (*user_status == "FIDO_AUTH_ENABLED");
-
- const auto* fido_creation_options = fido_authentication_info->FindKeyOfType(
- "fido_creation_options", base::Value::Type::DICTIONARY);
- if (fido_creation_options)
- response_details_.fido_creation_options = fido_creation_options->Clone();
-
- const auto* fido_request_options = fido_authentication_info->FindKeyOfType(
- "fido_request_options", base::Value::Type::DICTIONARY);
- if (fido_request_options)
- response_details_.fido_request_options = fido_request_options->Clone();
- }
-
- bool IsResponseComplete() override {
- return response_details_.user_is_opted_in.has_value();
- }
-
- void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
- std::move(callback_).Run(result, response_details_);
- }
-
- private:
- PaymentsClient::OptChangeRequestDetails request_details_;
- base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
- PaymentsClient::OptChangeResponseDetails&)>
- callback_;
- const bool full_sync_enabled_;
- PaymentsClient::OptChangeResponseDetails response_details_;
-};
-
-class GetUploadDetailsRequest : public PaymentsRequest {
- public:
- GetUploadDetailsRequest(
- const std::vector<AutofillProfile>& addresses,
- const int detected_values,
- const std::vector<const char*>& active_experiments,
- const bool full_sync_enabled,
- const std::string& app_locale,
- base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
- const std::u16string&,
- std::unique_ptr<base::Value>,
- std::vector<std::pair<int, int>>)> callback,
- const int billable_service_number,
- const int64_t billing_customer_number,
- PaymentsClient::UploadCardSource upload_card_source)
- : addresses_(addresses),
- detected_values_(detected_values),
- active_experiments_(active_experiments),
- full_sync_enabled_(full_sync_enabled),
- app_locale_(app_locale),
- callback_(std::move(callback)),
- billable_service_number_(billable_service_number),
- upload_card_source_(upload_card_source),
- billing_customer_number_(billing_customer_number) {}
-
- GetUploadDetailsRequest(const GetUploadDetailsRequest&) = delete;
- GetUploadDetailsRequest& operator=(const GetUploadDetailsRequest&) = delete;
-
- ~GetUploadDetailsRequest() override = default;
-
- std::string GetRequestUrlPath() override {
- return kGetUploadDetailsRequestPath;
- }
-
- std::string GetRequestContentType() override { return "application/json"; }
-
- std::string GetRequestContent() override {
- base::Value request_dict(base::Value::Type::DICTIONARY);
- base::Value context(base::Value::Type::DICTIONARY);
- context.SetKey("language_code", base::Value(app_locale_));
- context.SetKey("billable_service", base::Value(billable_service_number_));
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableSendingBcnInGetUploadDetails) &&
- billing_customer_number_ != 0) {
- context.SetKey("customer_context",
- BuildCustomerContextDictionary(billing_customer_number_));
- }
- request_dict.SetKey("context", std::move(context));
-
- base::Value chrome_user_context(base::Value::Type::DICTIONARY);
- chrome_user_context.SetKey("full_sync_enabled",
- base::Value(full_sync_enabled_));
- request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
-
- base::Value addresses(base::Value::Type::LIST);
- for (const AutofillProfile& profile : addresses_) {
- // These addresses are used by Payments to (1) accurately determine the
- // user's country in order to show the correct legal documents and (2) to
- // verify that the addresses are valid for their purposes so that we don't
- // offer save in a case where it would definitely fail (e.g. P.O. boxes if
- // min address is not possible). The final parameter directs
- // BuildAddressDictionary to omit names and phone numbers, which aren't
- // useful for these purposes.
- addresses.Append(BuildAddressDictionary(profile, app_locale_, false));
- }
- request_dict.SetKey("address", std::move(addresses));
-
- // It's possible we may not have found name/address/CVC in the checkout
- // flow. The detected_values_ bitmask tells Payments what *was* found, and
- // Payments will decide if the provided data is enough to offer upload save.
- request_dict.SetKey("detected_values", base::Value(detected_values_));
-
- SetActiveExperiments(active_experiments_, request_dict);
-
- switch (upload_card_source_) {
- case PaymentsClient::UploadCardSource::UNKNOWN_UPLOAD_CARD_SOURCE:
- request_dict.SetKey("upload_card_source",
- base::Value("UNKNOWN_UPLOAD_CARD_SOURCE"));
- break;
- case PaymentsClient::UploadCardSource::UPSTREAM_CHECKOUT_FLOW:
- request_dict.SetKey("upload_card_source",
- base::Value("UPSTREAM_CHECKOUT_FLOW"));
- break;
- case PaymentsClient::UploadCardSource::UPSTREAM_SETTINGS_PAGE:
- request_dict.SetKey("upload_card_source",
- base::Value("UPSTREAM_SETTINGS_PAGE"));
- break;
- case PaymentsClient::UploadCardSource::UPSTREAM_CARD_OCR:
- request_dict.SetKey("upload_card_source",
- base::Value("UPSTREAM_CARD_OCR"));
- break;
- case PaymentsClient::UploadCardSource::LOCAL_CARD_MIGRATION_CHECKOUT_FLOW:
- request_dict.SetKey("upload_card_source",
- base::Value("LOCAL_CARD_MIGRATION_CHECKOUT_FLOW"));
- break;
- case PaymentsClient::UploadCardSource::LOCAL_CARD_MIGRATION_SETTINGS_PAGE:
- request_dict.SetKey("upload_card_source",
- base::Value("LOCAL_CARD_MIGRATION_SETTINGS_PAGE"));
- break;
- default:
- NOTREACHED();
- }
-
- std::string request_content;
- base::JSONWriter::Write(request_dict, &request_content);
- VLOG(3) << "getdetailsforsavecard request body: " << request_content;
- return request_content;
- }
-
- void ParseResponse(const base::Value& response) override {
- const auto* context_token = response.FindStringKey("context_token");
- context_token_ =
- context_token ? base::UTF8ToUTF16(*context_token) : std::u16string();
-
- const base::Value* dictionary_value =
- response.FindKeyOfType("legal_message", base::Value::Type::DICTIONARY);
- if (dictionary_value)
- legal_message_ = std::make_unique<base::Value>(dictionary_value->Clone());
-
- const auto* supported_card_bin_ranges_string =
- response.FindStringKey("supported_card_bin_ranges_string");
- supported_card_bin_ranges_ = ParseSupportedCardBinRangesString(
- supported_card_bin_ranges_string ? *supported_card_bin_ranges_string
- : base::EmptyString());
- }
-
- bool IsResponseComplete() override {
- return !context_token_.empty() && legal_message_;
- }
-
- void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
- std::move(callback_).Run(result, context_token_, std::move(legal_message_),
- supported_card_bin_ranges_);
- }
-
- private:
- // Helper for ParseResponse(). Input format should be :"1234,30000-55555,765",
- // where ranges are separated by commas and items separated with a dash means
- // the start and ends of the range. Items without a dash have the same start
- // and end (ex. 1234-1234)
- std::vector<std::pair<int, int>> ParseSupportedCardBinRangesString(
- const std::string& supported_card_bin_ranges_string) {
- std::vector<std::pair<int, int>> supported_card_bin_ranges;
- std::vector<std::string> range_strings =
- base::SplitString(supported_card_bin_ranges_string, ",",
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-
- for (std::string& range_string : range_strings) {
- std::vector<std::string> range = base::SplitString(
- range_string, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- DCHECK(range.size() <= 2);
- int start;
- base::StringToInt(range[0], &start);
- if (range.size() == 1) {
- supported_card_bin_ranges.emplace_back(start, start);
- } else {
- int end;
- base::StringToInt(range[1], &end);
- DCHECK_LE(start, end);
- supported_card_bin_ranges.emplace_back(start, end);
- }
- }
- return supported_card_bin_ranges;
- }
-
- const std::vector<AutofillProfile> addresses_;
- const int detected_values_;
- const std::vector<const char*> active_experiments_;
- const bool full_sync_enabled_;
- std::string app_locale_;
- base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
- const std::u16string&,
- std::unique_ptr<base::Value>,
- std::vector<std::pair<int, int>>)>
- callback_;
- std::u16string context_token_;
- std::unique_ptr<base::Value> legal_message_;
- std::vector<std::pair<int, int>> supported_card_bin_ranges_;
- const int billable_service_number_;
- PaymentsClient::UploadCardSource upload_card_source_;
- const int64_t billing_customer_number_;
-};
-
-class UploadCardRequest : public PaymentsRequest {
- public:
- UploadCardRequest(
- const PaymentsClient::UploadRequestDetails& request_details,
- const bool full_sync_enabled,
- base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
- const PaymentsClient::UploadCardResponseDetails&)>
- callback)
- : request_details_(request_details),
- full_sync_enabled_(full_sync_enabled),
- callback_(std::move(callback)) {}
-
- UploadCardRequest(const UploadCardRequest&) = delete;
- UploadCardRequest& operator=(const UploadCardRequest&) = delete;
-
- ~UploadCardRequest() override = default;
-
- std::string GetRequestUrlPath() override { return kUploadCardRequestPath; }
-
- std::string GetRequestContentType() override {
- return "application/x-www-form-urlencoded";
- }
-
- std::string GetRequestContent() override {
- base::Value request_dict(base::Value::Type::DICTIONARY);
- request_dict.SetKey("encrypted_pan", base::Value("__param:s7e_1_pan"));
- if (!request_details_.cvc.empty())
- request_dict.SetKey("encrypted_cvc", base::Value("__param:s7e_13_cvc"));
- request_dict.SetKey("risk_data_encoded",
- BuildRiskDictionary(request_details_.risk_data));
-
- const std::string& app_locale = request_details_.app_locale;
- base::Value context(base::Value::Type::DICTIONARY);
- context.SetKey("language_code", base::Value(app_locale));
- context.SetKey("billable_service",
- base::Value(kUploadCardBillableServiceNumber));
- if (request_details_.billing_customer_number != 0) {
- context.SetKey("customer_context",
- BuildCustomerContextDictionary(
- request_details_.billing_customer_number));
- }
- request_dict.SetKey("context", std::move(context));
-
- base::Value chrome_user_context(base::Value::Type::DICTIONARY);
- chrome_user_context.SetKey("full_sync_enabled",
- base::Value(full_sync_enabled_));
- request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
-
- SetStringIfNotEmpty(request_details_.card, CREDIT_CARD_NAME_FULL,
- app_locale, "cardholder_name", request_dict);
-
- base::Value addresses(base::Value::Type::LIST);
- for (const AutofillProfile& profile : request_details_.profiles) {
- addresses.Append(BuildAddressDictionary(profile, app_locale, true));
- }
- request_dict.SetKey("address", std::move(addresses));
-
- request_dict.SetKey("context_token",
- base::Value(request_details_.context_token));
-
- int value = 0;
- const std::u16string exp_month = request_details_.card.GetInfo(
- AutofillType(CREDIT_CARD_EXP_MONTH), app_locale);
- const std::u16string exp_year = request_details_.card.GetInfo(
- AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale);
- if (base::StringToInt(exp_month, &value))
- request_dict.SetKey("expiration_month", base::Value(value));
- if (base::StringToInt(exp_year, &value))
- request_dict.SetKey("expiration_year", base::Value(value));
-
- if (request_details_.card.HasNonEmptyValidNickname()) {
- request_dict.SetKey("nickname",
- base::Value(request_details_.card.nickname()));
- }
-
- SetActiveExperiments(request_details_.active_experiments, request_dict);
-
- const std::u16string pan = request_details_.card.GetInfo(
- AutofillType(CREDIT_CARD_NUMBER), app_locale);
- std::string json_request;
- base::JSONWriter::Write(request_dict, &json_request);
- std::string request_content;
- if (request_details_.cvc.empty()) {
- request_content = base::StringPrintf(
- kUploadCardRequestFormatWithoutCvc,
- net::EscapeUrlEncodedData(json_request, true).c_str(),
- net::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str());
- } else {
- request_content = base::StringPrintf(
- kUploadCardRequestFormat,
- net::EscapeUrlEncodedData(json_request, true).c_str(),
- net::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str(),
- net::EscapeUrlEncodedData(base::UTF16ToASCII(request_details_.cvc),
- true)
- .c_str());
- }
- VLOG(3) << "savecard request body: " << request_content;
- return request_content;
- }
-
- void ParseResponse(const base::Value& response) override {
- const std::string* credit_card_id =
- response.FindStringKey("credit_card_id");
- upload_card_response_details_.server_id =
- credit_card_id ? *credit_card_id : std::string();
-
- const std::string* response_instrument_id =
- response.FindStringKey("instrument_id");
- if (response_instrument_id) {
- int64_t instrument_id;
- if (base::StringToInt64(base::StringPiece(*response_instrument_id),
- &instrument_id)) {
- upload_card_response_details_.instrument_id = instrument_id;
- }
- }
-
- const auto* virtual_card_metadata = response.FindKeyOfType(
- "virtual_card_metadata", base::Value::Type::DICTIONARY);
- if (virtual_card_metadata) {
- const std::string* virtual_card_enrollment_status =
- virtual_card_metadata->FindStringKey("status");
- if (virtual_card_enrollment_status) {
- if (*virtual_card_enrollment_status == "ENROLLED") {
- upload_card_response_details_.virtual_card_enrollment_state =
- CreditCard::VirtualCardEnrollmentState::ENROLLED;
- } else if (*virtual_card_enrollment_status == "ENROLLMENT_ELIGIBLE") {
- upload_card_response_details_.virtual_card_enrollment_state =
- CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE;
- } else {
- upload_card_response_details_.virtual_card_enrollment_state =
- CreditCard::VirtualCardEnrollmentState::
- UNENROLLED_AND_NOT_ELIGIBLE;
- }
- }
- }
-
- const std::string* card_art_url = response.FindStringKey("card_art_url");
- upload_card_response_details_.card_art_url =
- card_art_url ? GURL(*card_art_url) : GURL();
- }
-
- bool IsResponseComplete() override { return true; }
-
- void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
- std::move(callback_).Run(result, upload_card_response_details_);
- }
-
- private:
- const PaymentsClient::UploadRequestDetails request_details_;
- const bool full_sync_enabled_;
- base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
- const PaymentsClient::UploadCardResponseDetails&)>
- callback_;
- PaymentsClient::UploadCardResponseDetails upload_card_response_details_;
-};
-
-class MigrateCardsRequest : public PaymentsRequest {
- public:
- MigrateCardsRequest(
- const PaymentsClient::MigrationRequestDetails& request_details,
- const std::vector<MigratableCreditCard>& migratable_credit_cards,
- const bool full_sync_enabled,
- MigrateCardsCallback callback)
- : request_details_(request_details),
- migratable_credit_cards_(migratable_credit_cards),
- full_sync_enabled_(full_sync_enabled),
- callback_(std::move(callback)) {}
-
- MigrateCardsRequest(const MigrateCardsRequest&) = delete;
- MigrateCardsRequest& operator=(const MigrateCardsRequest&) = delete;
-
- ~MigrateCardsRequest() override = default;
-
- std::string GetRequestUrlPath() override { return kMigrateCardsRequestPath; }
-
- std::string GetRequestContentType() override {
- return "application/x-www-form-urlencoded";
- }
-
- std::string GetRequestContent() override {
- base::Value request_dict(base::Value::Type::DICTIONARY);
-
- request_dict.SetKey("risk_data_encoded",
- BuildRiskDictionary(request_details_.risk_data));
-
- const std::string& app_locale = request_details_.app_locale;
- base::Value context(base::Value::Type::DICTIONARY);
- context.SetKey("language_code", base::Value(app_locale));
- context.SetKey("billable_service",
- base::Value(kMigrateCardsBillableServiceNumber));
- if (request_details_.billing_customer_number != 0) {
- context.SetKey("customer_context",
- BuildCustomerContextDictionary(
- request_details_.billing_customer_number));
- }
- request_dict.SetKey("context", std::move(context));
-
- base::Value chrome_user_context(base::Value::Type::DICTIONARY);
- chrome_user_context.SetKey("full_sync_enabled",
- base::Value(full_sync_enabled_));
- request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
-
- request_dict.SetKey("context_token",
- base::Value(request_details_.context_token));
-
- std::string all_pans_data = std::string();
- base::Value migrate_cards(base::Value::Type::LIST);
- for (size_t index = 0; index < migratable_credit_cards_.size(); ++index) {
- std::string pan_field_name = GetPanFieldName(index);
- // Generate credit card dictionary.
- migrate_cards.Append(BuildCreditCardDictionary(
- migratable_credit_cards_[index].credit_card(), app_locale,
- pan_field_name));
- // Append pan data to the |all_pans_data|.
- all_pans_data +=
- GetAppendPan(migratable_credit_cards_[index].credit_card(),
- app_locale, pan_field_name);
- }
- request_dict.SetKey("local_card", std::move(migrate_cards));
-
- std::string json_request;
- base::JSONWriter::Write(request_dict, &json_request);
- std::string request_content = base::StringPrintf(
- kMigrateCardsRequestFormat,
- net::EscapeUrlEncodedData(json_request, true).c_str());
- request_content += all_pans_data;
- return request_content;
- }
-
- void ParseResponse(const base::Value& response) override {
- const auto* found_list =
- response.FindKeyOfType("save_result", base::Value::Type::LIST);
- if (!found_list)
- return;
-
- save_result_ =
- std::make_unique<std::unordered_map<std::string, std::string>>();
- for (const base::Value& result : found_list->GetListDeprecated()) {
- if (result.is_dict()) {
- const std::string* unique_id = result.FindStringKey("unique_id");
- const std::string* status = result.FindStringKey("status");
- save_result_->insert(
- std::make_pair(unique_id ? *unique_id : std::string(),
- status ? *status : std::string()));
- }
- }
-
- const std::string* display_text =
- response.FindStringKey("value_prop_display_text");
- display_text_ = display_text ? *display_text : std::string();
- }
-
- bool IsResponseComplete() override {
- return !display_text_.empty() && save_result_;
- }
-
- void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
- std::move(callback_).Run(result, std::move(save_result_), display_text_);
- }
-
- private:
- // Return the pan field name for the encrypted pan based on the |index|.
- std::string GetPanFieldName(const size_t& index) {
- return "s7e_1_pan" + std::to_string(index);
- }
-
- // Return the formatted pan to append to the end of the request.
- std::string GetAppendPan(const CreditCard& credit_card,
- const std::string& app_locale,
- const std::string& pan_field_name) {
- const std::u16string pan =
- credit_card.GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale);
- std::string pan_str =
- net::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str();
- std::string append_pan = "&" + pan_field_name + "=" + pan_str;
- return append_pan;
- }
-
- const PaymentsClient::MigrationRequestDetails request_details_;
- const std::vector<MigratableCreditCard>& migratable_credit_cards_;
- const bool full_sync_enabled_;
- MigrateCardsCallback callback_;
- std::unique_ptr<std::unordered_map<std::string, std::string>> save_result_;
- std::string display_text_;
-};
-
} // namespace
const char PaymentsClient::kRecipientName[] = "recipient_name";
@@ -1025,6 +211,11 @@ PaymentsClient::GetDetailsForEnrollmentResponseDetails::
PaymentsClient::GetDetailsForEnrollmentResponseDetails::
~GetDetailsForEnrollmentResponseDetails() = default;
+PaymentsClient::UploadCardResponseDetails::UploadCardResponseDetails() =
+ default;
+PaymentsClient::UploadCardResponseDetails::~UploadCardResponseDetails() =
+ default;
+
PaymentsClient::UpdateVirtualCardEnrollmentRequestDetails::
UpdateVirtualCardEnrollmentRequestDetails() = default;
PaymentsClient::UpdateVirtualCardEnrollmentRequestDetails::
@@ -1247,15 +438,15 @@ void PaymentsClient::OnSimpleLoaderCompleteInternal(int response_code,
request_->ParseResponse(*message_value);
}
- if (base::LowerCaseEqualsASCII(error_api_error_reason,
- "virtual_card_temporary_error")) {
+ if (base::EqualsCaseInsensitiveASCII(error_api_error_reason,
+ "virtual_card_temporary_error")) {
result =
AutofillClient::PaymentsRpcResult::kVcnRetrievalTryAgainFailure;
- } else if (base::LowerCaseEqualsASCII(error_api_error_reason,
- "virtual_card_permanent_error")) {
+ } else if (base::EqualsCaseInsensitiveASCII(
+ error_api_error_reason, "virtual_card_permanent_error")) {
result =
AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure;
- } else if (base::LowerCaseEqualsASCII(error_code, "internal")) {
+ } else if (base::EqualsCaseInsensitiveASCII(error_code, "internal")) {
result = AutofillClient::PaymentsRpcResult::kTryAgainFailure;
} else if (!error_code.empty() || !request_->IsResponseComplete()) {
result = AutofillClient::PaymentsRpcResult::kPermanentFailure;
diff --git a/chromium/components/autofill/core/browser/payments/payments_client.h b/chromium/components/autofill/core/browser/payments/payments_client.h
index dcf49741938..7fe00e457fc 100644
--- a/chromium/components/autofill/core/browser/payments/payments_client.h
+++ b/chromium/components/autofill/core/browser/payments/payments_client.h
@@ -212,24 +212,6 @@ class PaymentsClient {
absl::optional<base::Value> fido_request_options;
};
- // A collection of the information required to make a credit card upload
- // request.
- struct UploadRequestDetails {
- UploadRequestDetails();
- UploadRequestDetails(const UploadRequestDetails& other);
- ~UploadRequestDetails();
-
- int64_t billing_customer_number = 0;
- int detected_values;
- CreditCard card;
- std::u16string cvc;
- std::vector<AutofillProfile> profiles;
- std::u16string context_token;
- std::string risk_data;
- std::string app_locale;
- std::vector<const char*> active_experiments;
- };
-
// A collection of the information required to make local credit cards
// migration request.
struct MigrationRequestDetails {
@@ -257,58 +239,6 @@ class PaymentsClient {
int64_t billing_customer_number = 0;
};
- // An enum set in the GetUploadDetailsRequest indicating the source of the
- // request when uploading a card to Google Payments. It should stay consistent
- // with the same enum in Google Payments server code.
- enum UploadCardSource {
- // Source unknown.
- UNKNOWN_UPLOAD_CARD_SOURCE,
- // Single card is being uploaded from the normal credit card offer-to-save
- // prompt during a checkout flow.
- UPSTREAM_CHECKOUT_FLOW,
- // Single card is being uploaded from chrome://settings/payments.
- UPSTREAM_SETTINGS_PAGE,
- // Single card is being uploaded after being scanned by OCR.
- UPSTREAM_CARD_OCR,
- // 1+ cards are being uploaded from a migration request that started during
- // a checkout flow.
- LOCAL_CARD_MIGRATION_CHECKOUT_FLOW,
- // 1+ cards are being uploaded from a migration request that was initiated
- // from chrome://settings/payments.
- LOCAL_CARD_MIGRATION_SETTINGS_PAGE,
- };
-
- // TODO(crbug.com/1285086): Remove the |server_id| field from
- // UploadCardResponseDetails since it is never used.
- // A collection of information received in the response for an
- // UploadCardRequest.
- struct UploadCardResponseDetails {
- std::string server_id;
- // |instrument_id| is used by the server as an identifier for the card that
- // was uploaded. Currently, we have it in the UploadCardResponseDetails so
- // that we can send it in the GetDetailsForEnrollRequest in the virtual card
- // enrollment flow. Will only not be populated in the case of an imperfect
- // conversion from string to int64_t, or if the server does not return an
- // instrument id.
- absl::optional<int64_t> instrument_id;
- // |virtual_card_enrollment_state| is used to determine whether we want to
- // pursue further action with the credit card that was uploaded regarding
- // virtual card enrollment. For example, if the state is
- // UNENROLLED_AND_ELIGIBLE we might offer the user the option to enroll the
- // card that was uploaded into virtual card.
- CreditCard::VirtualCardEnrollmentState virtual_card_enrollment_state =
- CreditCard::VirtualCardEnrollmentState::UNSPECIFIED;
- // |card_art_url| is the mapping that would be used by PersonalDataManager
- // to try to get the card art for the credit card that was uploaded. It is
- // used in flows where after uploading a card we want to display its card
- // art. Since chrome sync does not instantly sync the card art with the url,
- // the actual card art image might not always be present. Flows that use
- // |card_art_url| need to make sure they handle the case where the image has
- // not been synced yet. For virtual card eligible cards this should not be
- // empty. If using this field use DCHECKs to ensure it is populated.
- GURL card_art_url;
- };
-
// A collection of information needed for the
// UpdateVirtualCardEnrollmentRequest.
struct UpdateVirtualCardEnrollmentRequestDetails {
@@ -389,6 +319,84 @@ class PaymentsClient {
LegalMessageLines issuer_legal_message;
};
+ // A collection of the information required to make a credit card upload
+ // request.
+ struct UploadRequestDetails {
+ UploadRequestDetails();
+ UploadRequestDetails(const UploadRequestDetails& other);
+ ~UploadRequestDetails();
+
+ int64_t billing_customer_number = 0;
+ int detected_values;
+ CreditCard card;
+ std::u16string cvc;
+ std::vector<AutofillProfile> profiles;
+ std::u16string context_token;
+ std::string risk_data;
+ std::string app_locale;
+ std::vector<const char*> active_experiments;
+ };
+
+ // An enum set in the GetUploadDetailsRequest indicating the source of the
+ // request when uploading a card to Google Payments. It should stay consistent
+ // with the same enum in Google Payments server code.
+ enum UploadCardSource {
+ // Source unknown.
+ UNKNOWN_UPLOAD_CARD_SOURCE,
+ // Single card is being uploaded from the normal credit card offer-to-save
+ // prompt during a checkout flow.
+ UPSTREAM_CHECKOUT_FLOW,
+ // Single card is being uploaded from chrome://settings/payments.
+ UPSTREAM_SETTINGS_PAGE,
+ // Single card is being uploaded after being scanned by OCR.
+ UPSTREAM_CARD_OCR,
+ // 1+ cards are being uploaded from a migration request that started during
+ // a checkout flow.
+ LOCAL_CARD_MIGRATION_CHECKOUT_FLOW,
+ // 1+ cards are being uploaded from a migration request that was initiated
+ // from chrome://settings/payments.
+ LOCAL_CARD_MIGRATION_SETTINGS_PAGE,
+ };
+
+ // TODO(crbug.com/1285086): Remove the |server_id| field from
+ // UploadCardResponseDetails since it is never used.
+ // A collection of information received in the response for an
+ // UploadCardRequest.
+ struct UploadCardResponseDetails {
+ UploadCardResponseDetails();
+ ~UploadCardResponseDetails();
+ std::string server_id;
+ // |instrument_id| is used by the server as an identifier for the card that
+ // was uploaded. Currently, we have it in the UploadCardResponseDetails so
+ // that we can send it in the GetDetailsForEnrollRequest in the virtual card
+ // enrollment flow. Will only not be populated in the case of an imperfect
+ // conversion from string to int64_t, or if the server does not return an
+ // instrument id.
+ absl::optional<int64_t> instrument_id;
+ // |virtual_card_enrollment_state| is used to determine whether we want to
+ // pursue further action with the credit card that was uploaded regarding
+ // virtual card enrollment. For example, if the state is
+ // UNENROLLED_AND_ELIGIBLE we might offer the user the option to enroll the
+ // card that was uploaded into virtual card.
+ CreditCard::VirtualCardEnrollmentState virtual_card_enrollment_state =
+ CreditCard::VirtualCardEnrollmentState::UNSPECIFIED;
+ // |card_art_url| is the mapping that would be used by PersonalDataManager
+ // to try to get the card art for the credit card that was uploaded. It is
+ // used in flows where after uploading a card we want to display its card
+ // art. Since chrome sync does not instantly sync the card art with the url,
+ // the actual card art image might not always be present. Flows that use
+ // |card_art_url| need to make sure they handle the case where the image has
+ // not been synced yet. For virtual card eligible cards this should not be
+ // empty. If using this field use DCHECKs to ensure it is populated.
+ GURL card_art_url;
+ // If the uploaded card is VCN eligible,
+ // |get_details_for_enrollment_response_details| will be populated so that
+ // we can display the virtual card enrollment bubble without needing to do
+ // another GetDetailsForEnroll network call.
+ absl::optional<GetDetailsForEnrollmentResponseDetails>
+ get_details_for_enrollment_response_details = absl::nullopt;
+ };
+
// |url_loader_factory| is reference counted so it has no lifetime or
// ownership requirements. |identity_manager| and |account_info_getter| must
// all outlive |this|. Either delegate might be nullptr. |is_off_the_record|
diff --git a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc
index 189b720ede5..9abdd3d468f 100644
--- a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -162,11 +162,8 @@ class PaymentsClientTest : public testing::Test {
test_personal_data_.SetAccountInfoForPayments(
identity_test_env_.MakePrimaryAccountAvailable(
"example@gmail.com", signin::ConsentLevel::kSync));
- scoped_feature_list_.InitWithFeatures(
- /* enabled_features */
- {features::kAutofillEnableVirtualCardsRiskBasedAuthentication,
- features::kAutofillEnableSendingBcnInGetUploadDetails},
- /* disabled_features */ {});
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillEnableSendingBcnInGetUploadDetails);
}
void TearDown() override { client_.reset(); }
@@ -1356,6 +1353,41 @@ TEST_F(PaymentsClientTest, UploadSuccessVirtualCardEnrollmentStatePresent) {
}
}
+TEST_F(PaymentsClientTest,
+ UploadSuccessGetDetailsForEnrollmentResponseDetailsPresent) {
+ scoped_feature_list_.Reset();
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillEnableGetDetailsForEnrollParsingInUploadCardResponse);
+ StartUploading(/*include_cvc=*/true);
+ IssueOAuthToken();
+ ReturnResponse(net::HTTP_OK,
+ "{ \"virtual_card_metadata\": "
+ "{\"status\": \"ENROLLMENT_ELIGIBLE\", "
+ "\"virtual_card_enrollment_data\": { "
+ "\"google_legal_message\": { \"line\" : [{ "
+ "\"template\": \"This is the entire message.\" }] }, "
+ "\"external_legal_message\": {},"
+ "\"context_token\": \"some_token\"} } }");
+ EXPECT_EQ(AutofillClient::PaymentsRpcResult::kSuccess, result_);
+ EXPECT_EQ(upload_card_response_details_.virtual_card_enrollment_state,
+ CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE);
+ EXPECT_EQ(
+ upload_card_response_details_.get_details_for_enrollment_response_details
+ .value()
+ .google_legal_message[0]
+ .text(),
+ u"This is the entire message.");
+ EXPECT_TRUE(
+ upload_card_response_details_.get_details_for_enrollment_response_details
+ .value()
+ .issuer_legal_message.empty());
+ EXPECT_EQ(
+ upload_card_response_details_.get_details_for_enrollment_response_details
+ .value()
+ .vcn_context_token,
+ "some_token");
+}
+
TEST_F(PaymentsClientTest, UploadSuccessCardArtUrlPresent) {
StartUploading(/*include_cvc=*/true);
IssueOAuthToken();
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request.cc b/chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request.cc
index 5a079b9f1ad..3e55a92f282 100644
--- a/chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request.cc
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request.cc
@@ -81,6 +81,19 @@ std::string GetDetailsForEnrollmentRequest::GetRequestContent() {
BuildRiskDictionary(request_details_.risk_data));
}
+ switch (request_details_.source) {
+ case VirtualCardEnrollmentSource::kUpstream:
+ request_dict.SetKey("channel_type", base::Value("CHROME_UPSTREAM"));
+ break;
+ case VirtualCardEnrollmentSource::kDownstream:
+ case VirtualCardEnrollmentSource::kSettingsPage:
+ request_dict.SetKey("channel_type", base::Value("CHROME_DOWNSTREAM"));
+ break;
+ case VirtualCardEnrollmentSource::kNone:
+ NOTREACHED();
+ break;
+ }
+
std::string request_content;
base::JSONWriter::Write(request_dict, &request_content);
VLOG(3) << "GetDetailsForEnrollmentRequest request body: " << request_content;
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request_unittest.cc b/chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request_unittest.cc
index 4af47054d73..0690911ba16 100644
--- a/chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request_unittest.cc
@@ -12,7 +12,9 @@
namespace autofill::payments {
-class GetDetailsForEnrollmentRequestTest : public testing::Test {
+class GetDetailsForEnrollmentRequestTest
+ : public testing::Test,
+ public testing::WithParamInterface<VirtualCardEnrollmentSource> {
public:
GetDetailsForEnrollmentRequestTest() = default;
GetDetailsForEnrollmentRequestTest(
@@ -21,13 +23,13 @@ class GetDetailsForEnrollmentRequestTest : public testing::Test {
const GetDetailsForEnrollmentRequestTest&) = delete;
~GetDetailsForEnrollmentRequestTest() override = default;
- void CreateRequest() {
+ void SetUp() override {
PaymentsClient::GetDetailsForEnrollmentRequestDetails request_details;
request_details.instrument_id = 11223344;
request_details.app_locale = "en";
request_details.billing_customer_number = 55667788;
request_details.risk_data = "fake risk data";
- request_details.source = VirtualCardEnrollmentSource::kUpstream;
+ request_details.source = GetParam();
request_ = std::make_unique<GetDetailsForEnrollmentRequest>(
request_details, base::DoNothing());
}
@@ -43,8 +45,7 @@ class GetDetailsForEnrollmentRequestTest : public testing::Test {
std::unique_ptr<GetDetailsForEnrollmentRequest> request_;
};
-TEST_F(GetDetailsForEnrollmentRequestTest, GetRequestContent) {
- CreateRequest();
+TEST_P(GetDetailsForEnrollmentRequestTest, GetRequestContent) {
EXPECT_EQ(GetRequest()->GetRequestUrlPath(),
"payments/apis/virtualcardservice/getdetailsforenroll");
EXPECT_TRUE(!GetRequest()->GetRequestContent().empty());
@@ -58,10 +59,28 @@ TEST_F(GetDetailsForEnrollmentRequestTest, GetRequestContent) {
std::string::npos);
EXPECT_TRUE(GetRequest()->GetRequestContent().find("risk_data_encoded") !=
std::string::npos);
+ EXPECT_TRUE(GetRequest()->GetRequestContent().find("channel_type") !=
+ std::string::npos);
+
+ std::string channel_type;
+ switch (GetParam()) {
+ case VirtualCardEnrollmentSource::kUpstream:
+ channel_type = "CHROME_UPSTREAM";
+ break;
+ case VirtualCardEnrollmentSource::kDownstream:
+ case VirtualCardEnrollmentSource::kSettingsPage:
+ channel_type = "CHROME_DOWNSTREAM";
+ break;
+ case VirtualCardEnrollmentSource::kNone:
+ NOTREACHED();
+ ASSERT_TRUE(false);
+ break;
+ }
+ EXPECT_TRUE(GetRequest()->GetRequestContent().find(channel_type) !=
+ std::string::npos);
}
-TEST_F(GetDetailsForEnrollmentRequestTest, ParseResponse) {
- CreateRequest();
+TEST_P(GetDetailsForEnrollmentRequestTest, ParseResponse) {
absl::optional<base::Value> response = base::JSONReader::Read(
"{ \"google_legal_message\": {}, \"external_legal_message\": {}, "
"\"context_token\": \"some_token\" }");
@@ -74,4 +93,11 @@ TEST_F(GetDetailsForEnrollmentRequestTest, ParseResponse) {
EXPECT_FALSE(GetRequest()->IsResponseComplete());
}
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ GetDetailsForEnrollmentRequestTest,
+ testing::Values(VirtualCardEnrollmentSource::kUpstream,
+ VirtualCardEnrollmentSource::kDownstream,
+ VirtualCardEnrollmentSource::kSettingsPage));
+
} // namespace autofill::payments
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc b/chromium/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc
new file mode 100644
index 00000000000..88fc1e32312
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc
@@ -0,0 +1,98 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/payments/payments_requests/get_unmask_details_request.h"
+
+#include <string>
+
+#include "base/json/json_writer.h"
+
+namespace autofill::payments {
+
+namespace {
+const char kGetUnmaskDetailsRequestPath[] =
+ "payments/apis/chromepaymentsservice/getdetailsforgetrealpan";
+} // namespace
+
+GetUnmaskDetailsRequest::GetUnmaskDetailsRequest(
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ PaymentsClient::UnmaskDetails&)> callback,
+ const std::string& app_locale,
+ const bool full_sync_enabled)
+ : callback_(std::move(callback)),
+ app_locale_(app_locale),
+ full_sync_enabled_(full_sync_enabled) {}
+
+GetUnmaskDetailsRequest::~GetUnmaskDetailsRequest() = default;
+
+std::string GetUnmaskDetailsRequest::GetRequestUrlPath() {
+ return kGetUnmaskDetailsRequestPath;
+}
+
+std::string GetUnmaskDetailsRequest::GetRequestContentType() {
+ return "application/json";
+}
+
+std::string GetUnmaskDetailsRequest::GetRequestContent() {
+ base::Value request_dict(base::Value::Type::DICTIONARY);
+ base::Value context(base::Value::Type::DICTIONARY);
+ context.SetKey("language_code", base::Value(app_locale_));
+ context.SetKey("billable_service",
+ base::Value(kUnmaskCardBillableServiceNumber));
+ request_dict.SetKey("context", std::move(context));
+
+ base::Value chrome_user_context(base::Value::Type::DICTIONARY);
+ chrome_user_context.SetKey("full_sync_enabled",
+ base::Value(full_sync_enabled_));
+ request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
+
+ std::string request_content;
+ base::JSONWriter::Write(request_dict, &request_content);
+ VLOG(3) << "getdetailsforgetrealpan request body: " << request_content;
+ return request_content;
+}
+
+void GetUnmaskDetailsRequest::ParseResponse(const base::Value& response) {
+ const auto* method = response.FindStringKey("authentication_method");
+ if (method) {
+ if (*method == "CVC") {
+ unmask_details_.unmask_auth_method =
+ AutofillClient::UnmaskAuthMethod::kCvc;
+ } else if (*method == "FIDO") {
+ unmask_details_.unmask_auth_method =
+ AutofillClient::UnmaskAuthMethod::kFido;
+ }
+ }
+
+ const auto* offer_fido_opt_in =
+ response.FindKeyOfType("offer_fido_opt_in", base::Value::Type::BOOLEAN);
+ unmask_details_.offer_fido_opt_in =
+ offer_fido_opt_in && offer_fido_opt_in->GetBool();
+
+ const auto* dictionary_value = response.FindKeyOfType(
+ "fido_request_options", base::Value::Type::DICTIONARY);
+ if (dictionary_value)
+ unmask_details_.fido_request_options = dictionary_value->Clone();
+
+ const auto* fido_eligible_card_ids =
+ response.FindKeyOfType("fido_eligible_card_id", base::Value::Type::LIST);
+ if (fido_eligible_card_ids) {
+ for (const base::Value& result :
+ fido_eligible_card_ids->GetListDeprecated()) {
+ unmask_details_.fido_eligible_card_ids.insert(result.GetString());
+ }
+ }
+}
+
+bool GetUnmaskDetailsRequest::IsResponseComplete() {
+ return unmask_details_.unmask_auth_method !=
+ AutofillClient::UnmaskAuthMethod::kUnknown;
+}
+
+void GetUnmaskDetailsRequest::RespondToDelegate(
+ AutofillClient::PaymentsRpcResult result) {
+ std::move(callback_).Run(result, unmask_details_);
+}
+
+} // namespace autofill::payments
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.h b/chromium/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.h
new file mode 100644
index 00000000000..ddeebe56d7d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.h
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_GET_UNMASK_DETAILS_REQUEST_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_GET_UNMASK_DETAILS_REQUEST_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+#include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace autofill::payments {
+
+class GetUnmaskDetailsRequest : public PaymentsRequest {
+ public:
+ GetUnmaskDetailsRequest(
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ PaymentsClient::UnmaskDetails&)> callback,
+ const std::string& app_locale,
+ const bool full_sync_enabled);
+ GetUnmaskDetailsRequest(const GetUnmaskDetailsRequest&) = delete;
+ GetUnmaskDetailsRequest& operator=(const GetUnmaskDetailsRequest&) = delete;
+ ~GetUnmaskDetailsRequest() override;
+
+ // PaymentsRequest:
+ std::string GetRequestUrlPath() override;
+ std::string GetRequestContentType() override;
+ std::string GetRequestContent() override;
+ void ParseResponse(const base::Value& response) override;
+ bool IsResponseComplete() override;
+ void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override;
+
+ private:
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ PaymentsClient::UnmaskDetails&)>
+ callback_;
+ std::string app_locale_;
+ const bool full_sync_enabled_;
+
+ // Suggested authentication method and other information to facilitate card
+ // unmasking.
+ payments::PaymentsClient::UnmaskDetails unmask_details_;
+};
+
+} // namespace autofill::payments
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_GET_UNMASK_DETAILS_REQUEST_H_
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.cc b/chromium/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.cc
new file mode 100644
index 00000000000..99c0651ce3f
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.cc
@@ -0,0 +1,179 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/payments/payments_requests/get_upload_details_request.h"
+
+#include <string>
+
+#include "base/json/json_writer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace autofill::payments {
+
+namespace {
+const char kGetUploadDetailsRequestPath[] =
+ "payments/apis/chromepaymentsservice/getdetailsforsavecard";
+} // namespace
+
+GetUploadDetailsRequest::GetUploadDetailsRequest(
+ const std::vector<AutofillProfile>& addresses,
+ const int detected_values,
+ const std::vector<const char*>& active_experiments,
+ const bool full_sync_enabled,
+ const std::string& app_locale,
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ const std::u16string&,
+ std::unique_ptr<base::Value>,
+ std::vector<std::pair<int, int>>)> callback,
+ const int billable_service_number,
+ const int64_t billing_customer_number,
+ PaymentsClient::UploadCardSource upload_card_source)
+ : addresses_(addresses),
+ detected_values_(detected_values),
+ active_experiments_(active_experiments),
+ full_sync_enabled_(full_sync_enabled),
+ app_locale_(app_locale),
+ callback_(std::move(callback)),
+ billable_service_number_(billable_service_number),
+ upload_card_source_(upload_card_source),
+ billing_customer_number_(billing_customer_number) {}
+
+GetUploadDetailsRequest::~GetUploadDetailsRequest() = default;
+
+std::string GetUploadDetailsRequest::GetRequestUrlPath() {
+ return kGetUploadDetailsRequestPath;
+}
+
+std::string GetUploadDetailsRequest::GetRequestContentType() {
+ return "application/json";
+}
+
+std::string GetUploadDetailsRequest::GetRequestContent() {
+ base::Value request_dict(base::Value::Type::DICTIONARY);
+ base::Value context(base::Value::Type::DICTIONARY);
+ context.SetKey("language_code", base::Value(app_locale_));
+ context.SetKey("billable_service", base::Value(billable_service_number_));
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillEnableSendingBcnInGetUploadDetails) &&
+ billing_customer_number_ != 0) {
+ context.SetKey("customer_context",
+ BuildCustomerContextDictionary(billing_customer_number_));
+ }
+ request_dict.SetKey("context", std::move(context));
+
+ base::Value chrome_user_context(base::Value::Type::DICTIONARY);
+ chrome_user_context.SetKey("full_sync_enabled",
+ base::Value(full_sync_enabled_));
+ request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
+
+ base::Value addresses(base::Value::Type::LIST);
+ for (const AutofillProfile& profile : addresses_) {
+ // These addresses are used by Payments to (1) accurately determine the
+ // user's country in order to show the correct legal documents and (2) to
+ // verify that the addresses are valid for their purposes so that we don't
+ // offer save in a case where it would definitely fail (e.g. P.O. boxes if
+ // min address is not possible). The final parameter directs
+ // BuildAddressDictionary to omit names and phone numbers, which aren't
+ // useful for these purposes.
+ addresses.Append(BuildAddressDictionary(profile, app_locale_, false));
+ }
+ request_dict.SetKey("address", std::move(addresses));
+
+ // It's possible we may not have found name/address/CVC in the checkout
+ // flow. The detected_values_ bitmask tells Payments what *was* found, and
+ // Payments will decide if the provided data is enough to offer upload save.
+ request_dict.SetKey("detected_values", base::Value(detected_values_));
+
+ SetActiveExperiments(active_experiments_, request_dict);
+
+ switch (upload_card_source_) {
+ case PaymentsClient::UploadCardSource::UNKNOWN_UPLOAD_CARD_SOURCE:
+ request_dict.SetKey("upload_card_source",
+ base::Value("UNKNOWN_UPLOAD_CARD_SOURCE"));
+ break;
+ case PaymentsClient::UploadCardSource::UPSTREAM_CHECKOUT_FLOW:
+ request_dict.SetKey("upload_card_source",
+ base::Value("UPSTREAM_CHECKOUT_FLOW"));
+ break;
+ case PaymentsClient::UploadCardSource::UPSTREAM_SETTINGS_PAGE:
+ request_dict.SetKey("upload_card_source",
+ base::Value("UPSTREAM_SETTINGS_PAGE"));
+ break;
+ case PaymentsClient::UploadCardSource::UPSTREAM_CARD_OCR:
+ request_dict.SetKey("upload_card_source",
+ base::Value("UPSTREAM_CARD_OCR"));
+ break;
+ case PaymentsClient::UploadCardSource::LOCAL_CARD_MIGRATION_CHECKOUT_FLOW:
+ request_dict.SetKey("upload_card_source",
+ base::Value("LOCAL_CARD_MIGRATION_CHECKOUT_FLOW"));
+ break;
+ case PaymentsClient::UploadCardSource::LOCAL_CARD_MIGRATION_SETTINGS_PAGE:
+ request_dict.SetKey("upload_card_source",
+ base::Value("LOCAL_CARD_MIGRATION_SETTINGS_PAGE"));
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ std::string request_content;
+ base::JSONWriter::Write(request_dict, &request_content);
+ VLOG(3) << "getdetailsforsavecard request body: " << request_content;
+ return request_content;
+}
+
+void GetUploadDetailsRequest::ParseResponse(const base::Value& response) {
+ const auto* context_token = response.FindStringKey("context_token");
+ context_token_ =
+ context_token ? base::UTF8ToUTF16(*context_token) : std::u16string();
+
+ const base::Value* dictionary_value =
+ response.FindKeyOfType("legal_message", base::Value::Type::DICTIONARY);
+ if (dictionary_value)
+ legal_message_ = std::make_unique<base::Value>(dictionary_value->Clone());
+
+ const auto* supported_card_bin_ranges_string =
+ response.FindStringKey("supported_card_bin_ranges_string");
+ supported_card_bin_ranges_ = ParseSupportedCardBinRangesString(
+ supported_card_bin_ranges_string ? *supported_card_bin_ranges_string
+ : base::EmptyString());
+}
+
+bool GetUploadDetailsRequest::IsResponseComplete() {
+ return !context_token_.empty() && legal_message_;
+}
+
+void GetUploadDetailsRequest::RespondToDelegate(
+ AutofillClient::PaymentsRpcResult result) {
+ std::move(callback_).Run(result, context_token_, std::move(legal_message_),
+ supported_card_bin_ranges_);
+}
+
+std::vector<std::pair<int, int>>
+GetUploadDetailsRequest::ParseSupportedCardBinRangesString(
+ const std::string& supported_card_bin_ranges_string) {
+ std::vector<std::pair<int, int>> supported_card_bin_ranges;
+ std::vector<std::string> range_strings =
+ base::SplitString(supported_card_bin_ranges_string, ",",
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ for (std::string& range_string : range_strings) {
+ std::vector<std::string> range = base::SplitString(
+ range_string, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ DCHECK(range.size() <= 2);
+ int start;
+ base::StringToInt(range[0], &start);
+ if (range.size() == 1) {
+ supported_card_bin_ranges.emplace_back(start, start);
+ } else {
+ int end;
+ base::StringToInt(range[1], &end);
+ DCHECK_LE(start, end);
+ supported_card_bin_ranges.emplace_back(start, end);
+ }
+ }
+ return supported_card_bin_ranges;
+}
+
+} // namespace autofill::payments
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.h b/chromium/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.h
new file mode 100644
index 00000000000..673728df07b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.h
@@ -0,0 +1,76 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_GET_UPLOAD_DETAILS_REQUEST_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_GET_UPLOAD_DETAILS_REQUEST_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+#include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace autofill::payments {
+
+class GetUploadDetailsRequest : public PaymentsRequest {
+ public:
+ GetUploadDetailsRequest(
+ const std::vector<AutofillProfile>& addresses,
+ const int detected_values,
+ const std::vector<const char*>& active_experiments,
+ const bool full_sync_enabled,
+ const std::string& app_locale,
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ const std::u16string&,
+ std::unique_ptr<base::Value>,
+ std::vector<std::pair<int, int>>)> callback,
+ const int billable_service_number,
+ const int64_t billing_customer_number,
+ PaymentsClient::UploadCardSource upload_card_source);
+ GetUploadDetailsRequest(const GetUploadDetailsRequest&) = delete;
+ GetUploadDetailsRequest& operator=(const GetUploadDetailsRequest&) = delete;
+ ~GetUploadDetailsRequest() override;
+
+ // PaymentsRequest:
+ std::string GetRequestUrlPath() override;
+ std::string GetRequestContentType() override;
+ std::string GetRequestContent() override;
+ void ParseResponse(const base::Value& response) override;
+ bool IsResponseComplete() override;
+ void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override;
+
+ private:
+ // Helper for ParseResponse(). Input format should be :"1234,30000-55555,765",
+ // where ranges are separated by commas and items separated with a dash means
+ // the start and ends of the range. Items without a dash have the same start
+ // and end (ex. 1234-1234)
+ std::vector<std::pair<int, int>> ParseSupportedCardBinRangesString(
+ const std::string& supported_card_bin_ranges_string);
+
+ const std::vector<AutofillProfile> addresses_;
+ const int detected_values_;
+ const std::vector<const char*> active_experiments_;
+ const bool full_sync_enabled_;
+ std::string app_locale_;
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ const std::u16string&,
+ std::unique_ptr<base::Value>,
+ std::vector<std::pair<int, int>>)>
+ callback_;
+ std::u16string context_token_;
+ std::unique_ptr<base::Value> legal_message_;
+ std::vector<std::pair<int, int>> supported_card_bin_ranges_;
+ const int billable_service_number_;
+ PaymentsClient::UploadCardSource upload_card_source_;
+ const int64_t billing_customer_number_;
+};
+
+} // namespace autofill::payments
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_GET_UPLOAD_DETAILS_REQUEST_H_
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.cc b/chromium/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.cc
new file mode 100644
index 00000000000..f1365c485be
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.cc
@@ -0,0 +1,141 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/payments/payments_requests/migrate_cards_request.h"
+
+#include <string>
+
+#include "base/json/json_writer.h"
+#include "base/strings/escape.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/payments/local_card_migration_manager.h"
+
+namespace autofill::payments {
+
+namespace {
+const char kMigrateCardsRequestPath[] =
+ "payments/apis-secure/chromepaymentsservice/migratecards"
+ "?s7e_suffix=chromewallet";
+const char kMigrateCardsRequestFormat[] =
+ "requestContentType=application/json; charset=utf-8&request=%s";
+} // namespace
+
+MigrateCardsRequest::MigrateCardsRequest(
+ const PaymentsClient::MigrationRequestDetails& request_details,
+ const std::vector<MigratableCreditCard>& migratable_credit_cards,
+ const bool full_sync_enabled,
+ MigrateCardsCallback callback)
+ : request_details_(request_details),
+ migratable_credit_cards_(migratable_credit_cards),
+ full_sync_enabled_(full_sync_enabled),
+ callback_(std::move(callback)) {}
+
+MigrateCardsRequest::~MigrateCardsRequest() = default;
+
+std::string MigrateCardsRequest::GetRequestUrlPath() {
+ return kMigrateCardsRequestPath;
+}
+
+std::string MigrateCardsRequest::GetRequestContentType() {
+ return "application/x-www-form-urlencoded";
+}
+
+std::string MigrateCardsRequest::GetRequestContent() {
+ base::Value request_dict(base::Value::Type::DICTIONARY);
+
+ request_dict.SetKey("risk_data_encoded",
+ BuildRiskDictionary(request_details_.risk_data));
+
+ const std::string& app_locale = request_details_.app_locale;
+ base::Value context(base::Value::Type::DICTIONARY);
+ context.SetKey("language_code", base::Value(app_locale));
+ context.SetKey("billable_service",
+ base::Value(kMigrateCardsBillableServiceNumber));
+ if (request_details_.billing_customer_number != 0) {
+ context.SetKey("customer_context",
+ BuildCustomerContextDictionary(
+ request_details_.billing_customer_number));
+ }
+ request_dict.SetKey("context", std::move(context));
+
+ base::Value chrome_user_context(base::Value::Type::DICTIONARY);
+ chrome_user_context.SetKey("full_sync_enabled",
+ base::Value(full_sync_enabled_));
+ request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
+
+ request_dict.SetKey("context_token",
+ base::Value(request_details_.context_token));
+
+ std::string all_pans_data = std::string();
+ base::Value migrate_cards(base::Value::Type::LIST);
+ for (size_t index = 0; index < migratable_credit_cards_.size(); ++index) {
+ std::string pan_field_name = GetPanFieldName(index);
+ // Generate credit card dictionary.
+ migrate_cards.Append(
+ BuildCreditCardDictionary(migratable_credit_cards_[index].credit_card(),
+ app_locale, pan_field_name));
+ // Append pan data to the |all_pans_data|.
+ all_pans_data += GetAppendPan(migratable_credit_cards_[index].credit_card(),
+ app_locale, pan_field_name);
+ }
+ request_dict.SetKey("local_card", std::move(migrate_cards));
+
+ std::string json_request;
+ base::JSONWriter::Write(request_dict, &json_request);
+ std::string request_content = base::StringPrintf(
+ kMigrateCardsRequestFormat,
+ base::EscapeUrlEncodedData(json_request, true).c_str());
+ request_content += all_pans_data;
+ return request_content;
+}
+
+void MigrateCardsRequest::ParseResponse(const base::Value& response) {
+ const auto* found_list =
+ response.FindKeyOfType("save_result", base::Value::Type::LIST);
+ if (!found_list)
+ return;
+
+ save_result_ =
+ std::make_unique<std::unordered_map<std::string, std::string>>();
+ for (const base::Value& result : found_list->GetListDeprecated()) {
+ if (result.is_dict()) {
+ const std::string* unique_id = result.FindStringKey("unique_id");
+ const std::string* status = result.FindStringKey("status");
+ save_result_->insert(
+ std::make_pair(unique_id ? *unique_id : std::string(),
+ status ? *status : std::string()));
+ }
+ }
+
+ const std::string* display_text =
+ response.FindStringKey("value_prop_display_text");
+ display_text_ = display_text ? *display_text : std::string();
+}
+
+bool MigrateCardsRequest::IsResponseComplete() {
+ return !display_text_.empty() && save_result_;
+}
+
+void MigrateCardsRequest::RespondToDelegate(
+ AutofillClient::PaymentsRpcResult result) {
+ std::move(callback_).Run(result, std::move(save_result_), display_text_);
+}
+
+std::string MigrateCardsRequest::GetPanFieldName(const size_t& index) {
+ return "s7e_1_pan" + base::NumberToString(index);
+}
+
+std::string MigrateCardsRequest::GetAppendPan(
+ const CreditCard& credit_card,
+ const std::string& app_locale,
+ const std::string& pan_field_name) {
+ const std::u16string pan =
+ credit_card.GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale);
+ std::string pan_str =
+ base::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str();
+ std::string append_pan = "&" + pan_field_name + "=" + pan_str;
+ return append_pan;
+}
+
+} // namespace autofill::payments
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.h b/chromium/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.h
new file mode 100644
index 00000000000..67844228c50
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.h
@@ -0,0 +1,59 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_MIGRATE_CARDS_REQUEST_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_MIGRATE_CARDS_REQUEST_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+#include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace autofill::payments {
+
+class MigrateCardsRequest : public PaymentsRequest {
+ public:
+ MigrateCardsRequest(
+ const PaymentsClient::MigrationRequestDetails& request_details,
+ const std::vector<MigratableCreditCard>& migratable_credit_cards,
+ const bool full_sync_enabled,
+ MigrateCardsCallback callback);
+ MigrateCardsRequest(const MigrateCardsRequest&) = delete;
+ MigrateCardsRequest& operator=(const MigrateCardsRequest&) = delete;
+ ~MigrateCardsRequest() override;
+
+ // PaymentsRequest:
+ std::string GetRequestUrlPath() override;
+ std::string GetRequestContentType() override;
+ std::string GetRequestContent() override;
+ void ParseResponse(const base::Value& response) override;
+ bool IsResponseComplete() override;
+ void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override;
+
+ private:
+ // Return the pan field name for the encrypted pan based on the |index|.
+ std::string GetPanFieldName(const size_t& index);
+
+ // Return the formatted pan to append to the end of the request.
+ std::string GetAppendPan(const CreditCard& credit_card,
+ const std::string& app_locale,
+ const std::string& pan_field_name);
+
+ const PaymentsClient::MigrationRequestDetails request_details_;
+ const std::vector<MigratableCreditCard>& migratable_credit_cards_;
+ const bool full_sync_enabled_;
+ MigrateCardsCallback callback_;
+ std::unique_ptr<std::unordered_map<std::string, std::string>> save_result_;
+ std::string display_text_;
+};
+
+} // namespace autofill::payments
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_MIGRATE_CARDS_REQUEST_H_
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/opt_change_request.cc b/chromium/components/autofill/core/browser/payments/payments_requests/opt_change_request.cc
new file mode 100644
index 00000000000..e6545e016e7
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/opt_change_request.cc
@@ -0,0 +1,122 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/payments/payments_requests/opt_change_request.h"
+
+#include <string>
+
+#include "base/json/json_writer.h"
+
+namespace autofill::payments {
+
+namespace {
+const char kOptChangeRequestPath[] =
+ "payments/apis/chromepaymentsservice/updateautofilluserpreference";
+} // namespace
+
+OptChangeRequest::OptChangeRequest(
+ const PaymentsClient::OptChangeRequestDetails& request_details,
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ PaymentsClient::OptChangeResponseDetails&)>
+ callback,
+ const bool full_sync_enabled)
+ : request_details_(request_details),
+ callback_(std::move(callback)),
+ full_sync_enabled_(full_sync_enabled) {}
+
+OptChangeRequest::~OptChangeRequest() = default;
+
+std::string OptChangeRequest::GetRequestUrlPath() {
+ return kOptChangeRequestPath;
+}
+
+std::string OptChangeRequest::GetRequestContentType() {
+ return "application/json";
+}
+
+std::string OptChangeRequest::GetRequestContent() {
+ base::Value request_dict(base::Value::Type::DICTIONARY);
+ base::Value context(base::Value::Type::DICTIONARY);
+ context.SetKey("language_code", base::Value(request_details_.app_locale));
+ context.SetKey("billable_service",
+ base::Value(kUnmaskCardBillableServiceNumber));
+ request_dict.SetKey("context", std::move(context));
+
+ base::Value chrome_user_context(base::Value::Type::DICTIONARY);
+ chrome_user_context.SetKey("full_sync_enabled",
+ base::Value(full_sync_enabled_));
+ request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
+
+ std::string reason;
+ switch (request_details_.reason) {
+ case PaymentsClient::OptChangeRequestDetails::ENABLE_FIDO_AUTH:
+ reason = "ENABLE_FIDO_AUTH";
+ break;
+ case PaymentsClient::OptChangeRequestDetails::DISABLE_FIDO_AUTH:
+ reason = "DISABLE_FIDO_AUTH";
+ break;
+ case PaymentsClient::OptChangeRequestDetails::ADD_CARD_FOR_FIDO_AUTH:
+ reason = "ADD_CARD_FOR_FIDO_AUTH";
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ request_dict.SetKey("reason", base::Value(reason));
+
+ if (request_details_.fido_authenticator_response.has_value()) {
+ base::Value fido_authentication_info(base::Value::Type::DICTIONARY);
+
+ fido_authentication_info.SetKey(
+ "fido_authenticator_response",
+ std::move(request_details_.fido_authenticator_response.value()));
+
+ if (!request_details_.card_authorization_token.empty()) {
+ fido_authentication_info.SetKey(
+ "card_authorization_token",
+ base::Value(request_details_.card_authorization_token));
+ }
+
+ request_dict.SetKey("fido_authentication_info",
+ std::move(fido_authentication_info));
+ }
+
+ std::string request_content;
+ base::JSONWriter::Write(request_dict, &request_content);
+ VLOG(3) << "updateautofilluserpreference request body: " << request_content;
+ return request_content;
+}
+
+void OptChangeRequest::ParseResponse(const base::Value& response) {
+ const auto* fido_authentication_info = response.FindKeyOfType(
+ "fido_authentication_info", base::Value::Type::DICTIONARY);
+ if (!fido_authentication_info)
+ return;
+
+ const auto* user_status =
+ fido_authentication_info->FindStringKey("user_status");
+ if (user_status && *user_status != "UNKNOWN_USER_STATUS")
+ response_details_.user_is_opted_in = (*user_status == "FIDO_AUTH_ENABLED");
+
+ const auto* fido_creation_options = fido_authentication_info->FindKeyOfType(
+ "fido_creation_options", base::Value::Type::DICTIONARY);
+ if (fido_creation_options)
+ response_details_.fido_creation_options = fido_creation_options->Clone();
+
+ const auto* fido_request_options = fido_authentication_info->FindKeyOfType(
+ "fido_request_options", base::Value::Type::DICTIONARY);
+ if (fido_request_options)
+ response_details_.fido_request_options = fido_request_options->Clone();
+}
+
+bool OptChangeRequest::IsResponseComplete() {
+ return response_details_.user_is_opted_in.has_value();
+}
+
+void OptChangeRequest::RespondToDelegate(
+ AutofillClient::PaymentsRpcResult result) {
+ std::move(callback_).Run(result, response_details_);
+}
+
+} // namespace autofill::payments
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/opt_change_request.h b/chromium/components/autofill/core/browser/payments/payments_requests/opt_change_request.h
new file mode 100644
index 00000000000..457268327b5
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/opt_change_request.h
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_OPT_CHANGE_REQUEST_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_OPT_CHANGE_REQUEST_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+#include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace autofill::payments {
+
+class OptChangeRequest : public PaymentsRequest {
+ public:
+ OptChangeRequest(
+ const PaymentsClient::OptChangeRequestDetails& request_details,
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ PaymentsClient::OptChangeResponseDetails&)>
+ callback,
+ const bool full_sync_enabled);
+ OptChangeRequest(const OptChangeRequest&) = delete;
+ OptChangeRequest& operator=(const OptChangeRequest&) = delete;
+ ~OptChangeRequest() override;
+
+ // PaymentsRequest:
+ std::string GetRequestUrlPath() override;
+ std::string GetRequestContentType() override;
+ std::string GetRequestContent() override;
+ void ParseResponse(const base::Value& response) override;
+ bool IsResponseComplete() override;
+ void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override;
+
+ private:
+ PaymentsClient::OptChangeRequestDetails request_details_;
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ PaymentsClient::OptChangeResponseDetails&)>
+ callback_;
+ const bool full_sync_enabled_;
+ PaymentsClient::OptChangeResponseDetails response_details_;
+};
+
+} // namespace autofill::payments
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_OPT_CHANGE_REQUEST_H_
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/payments_request.cc b/chromium/components/autofill/core/browser/payments/payments_requests/payments_request.cc
index 0de08f93f93..b6c857885e3 100644
--- a/chromium/components/autofill/core/browser/payments/payments_requests/payments_request.cc
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/payments_request.cc
@@ -7,9 +7,9 @@
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
-namespace autofill {
-namespace payments {
+namespace autofill::payments {
PaymentsRequest::~PaymentsRequest() = default;
@@ -41,5 +41,107 @@ base::Value PaymentsRequest::BuildCustomerContextDictionary(
return customer_context;
}
-} // namespace payments
-} // namespace autofill
+void PaymentsRequest::SetActiveExperiments(
+ const std::vector<const char*>& active_experiments,
+ base::Value& request_dict) {
+ if (active_experiments.empty())
+ return;
+
+ base::Value active_chrome_experiments(base::Value::Type::LIST);
+ for (const char* it : active_experiments)
+ active_chrome_experiments.Append(it);
+
+ request_dict.SetKey("active_chrome_experiments",
+ std::move(active_chrome_experiments));
+}
+
+base::Value PaymentsRequest::BuildAddressDictionary(
+ const AutofillProfile& profile,
+ const std::string& app_locale,
+ bool include_non_location_data) {
+ base::Value postal_address(base::Value::Type::DICTIONARY);
+
+ if (include_non_location_data) {
+ SetStringIfNotEmpty(profile, NAME_FULL, app_locale,
+ PaymentsClient::kRecipientName, postal_address);
+ }
+
+ base::Value address_lines(base::Value::Type::LIST);
+ AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE1, app_locale,
+ address_lines);
+ AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE2, app_locale,
+ address_lines);
+ AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE3, app_locale,
+ address_lines);
+ if (!address_lines.GetListDeprecated().empty())
+ postal_address.SetKey("address_line", std::move(address_lines));
+
+ SetStringIfNotEmpty(profile, ADDRESS_HOME_CITY, app_locale, "locality_name",
+ postal_address);
+ SetStringIfNotEmpty(profile, ADDRESS_HOME_STATE, app_locale,
+ "administrative_area_name", postal_address);
+ SetStringIfNotEmpty(profile, ADDRESS_HOME_ZIP, app_locale,
+ "postal_code_number", postal_address);
+
+ // Use GetRawInfo to get a country code instead of the country name:
+ const std::u16string country_code = profile.GetRawInfo(ADDRESS_HOME_COUNTRY);
+ if (!country_code.empty())
+ postal_address.SetKey("country_name_code", base::Value(country_code));
+
+ base::Value address(base::Value::Type::DICTIONARY);
+ address.SetKey("postal_address", std::move(postal_address));
+
+ if (include_non_location_data) {
+ SetStringIfNotEmpty(profile, PHONE_HOME_WHOLE_NUMBER, app_locale,
+ PaymentsClient::kPhoneNumber, address);
+ }
+
+ return address;
+}
+
+base::Value PaymentsRequest::BuildCreditCardDictionary(
+ const CreditCard& credit_card,
+ const std::string& app_locale,
+ const std::string& pan_field_name) {
+ base::Value card(base::Value::Type::DICTIONARY);
+ card.SetKey("unique_id", base::Value(credit_card.guid()));
+
+ const std::u16string exp_month =
+ credit_card.GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), app_locale);
+ const std::u16string exp_year = credit_card.GetInfo(
+ AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale);
+ int value = 0;
+ if (base::StringToInt(exp_month, &value))
+ card.SetKey("expiration_month", base::Value(value));
+ if (base::StringToInt(exp_year, &value))
+ card.SetKey("expiration_year", base::Value(value));
+ SetStringIfNotEmpty(credit_card, CREDIT_CARD_NAME_FULL, app_locale,
+ "cardholder_name", card);
+
+ if (credit_card.HasNonEmptyValidNickname())
+ card.SetKey("nickname", base::Value(credit_card.nickname()));
+
+ card.SetKey("encrypted_pan", base::Value("__param:" + pan_field_name));
+ return card;
+}
+
+void PaymentsRequest::AppendStringIfNotEmpty(const AutofillProfile& profile,
+ const ServerFieldType& type,
+ const std::string& app_locale,
+ base::Value& list) {
+ const std::u16string value = profile.GetInfo(type, app_locale);
+ if (!value.empty())
+ list.Append(value);
+}
+
+void PaymentsRequest::SetStringIfNotEmpty(const AutofillDataModel& profile,
+ const ServerFieldType& type,
+ const std::string& app_locale,
+ const std::string& path,
+ base::Value& dictionary) {
+ const std::u16string value = profile.GetInfo(AutofillType(type), app_locale);
+ if (!value.empty())
+ dictionary.SetKey(path, base::Value(value));
+}
+
+} // namespace autofill::payments
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/payments_request.h b/chromium/components/autofill/core/browser/payments/payments_requests/payments_request.h
index be88bcab743..dd2f0174d24 100644
--- a/chromium/components/autofill/core/browser/payments/payments_requests/payments_request.h
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/payments_request.h
@@ -8,13 +8,15 @@
#include <string>
#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/common/autofill_payments_features.h"
namespace base {
class Value;
}
-namespace autofill {
-namespace payments {
+namespace autofill::payments {
// Shared class for the various Payments request types.
class PaymentsRequest {
@@ -47,9 +49,42 @@ class PaymentsRequest {
// Shared helper function to build the customer context sent in the request.
base::Value BuildCustomerContextDictionary(int64_t external_customer_id);
+
+ // Shared helper function that populates the list of active experiments that
+ // affect either the data sent in payments RPCs or whether the RPCs are sent
+ // or not.
+ void SetActiveExperiments(const std::vector<const char*>& active_experiments,
+ base::Value& request_dict);
+
+ // Shared helper functoin that returns a dictionary with the structure
+ // expected by Payments RPCs, containing each of the fields in |profile|,
+ // formatted according to |app_locale|. If |include_non_location_data| is
+ // false, the name and phone number in |profile| are not included.
+ base::Value BuildAddressDictionary(const AutofillProfile& profile,
+ const std::string& app_locale,
+ bool include_non_location_data);
+
+ // Shared helper function that returns a dictionary of the credit card with
+ // the structure expected by Payments RPCs, containing expiration month,
+ // expiration year and cardholder name (if any) fields in |credit_card|,
+ // formatted according to |app_locale|. |pan_field_name| is the field name for
+ // the encrypted pan. We use each credit card's guid as the unique id.
+ base::Value BuildCreditCardDictionary(const CreditCard& credit_card,
+ const std::string& app_locale,
+ const std::string& pan_field_name);
+
+ // Shared helper functions for string operations.
+ void AppendStringIfNotEmpty(const AutofillProfile& profile,
+ const ServerFieldType& type,
+ const std::string& app_locale,
+ base::Value& list);
+ void SetStringIfNotEmpty(const AutofillDataModel& profile,
+ const ServerFieldType& type,
+ const std::string& app_locale,
+ const std::string& path,
+ base::Value& dictionary);
};
-} // namespace payments
-} // namespace autofill
+} // namespace autofill::payments
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_PAYMENTS_REQUEST_H_
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc b/chromium/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
index c1a1c0db810..ec3d6f9a1ed 100644
--- a/chromium/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
@@ -5,12 +5,12 @@
#include "components/autofill/core/browser/payments/payments_requests/unmask_card_request.h"
#include "base/json/json_writer.h"
+#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/autofill/core/common/autofill_payments_features.h"
-#include "net/base/escape.h"
namespace autofill {
namespace payments {
@@ -170,22 +170,22 @@ std::string UnmaskCardRequest::GetRequestContent() {
if (is_cvc_auth) {
request_content = base::StringPrintf(
kUnmaskCardRequestFormatWithCvc,
- net::EscapeUrlEncodedData(json_request, true).c_str(),
- net::EscapeUrlEncodedData(
+ base::EscapeUrlEncodedData(json_request, true).c_str(),
+ base::EscapeUrlEncodedData(
base::UTF16ToASCII(request_details_.user_response.cvc), true)
.c_str());
} else if (is_otp_auth) {
request_content = base::StringPrintf(
kUnmaskCardRequestFormatWithOtp,
- net::EscapeUrlEncodedData(json_request, true).c_str(),
- net::EscapeUrlEncodedData(base::UTF16ToASCII(request_details_.otp),
- true)
+ base::EscapeUrlEncodedData(json_request, true).c_str(),
+ base::EscapeUrlEncodedData(base::UTF16ToASCII(request_details_.otp),
+ true)
.c_str());
} else {
// If neither cvc nor otp request, use the normal request format.
request_content = base::StringPrintf(
kUnmaskCardRequestFormat,
- net::EscapeUrlEncodedData(json_request, true).c_str());
+ base::EscapeUrlEncodedData(json_request, true).c_str());
}
VLOG(3) << "getrealpan request body: " << request_content;
@@ -273,12 +273,8 @@ bool UnmaskCardRequest::IsResponseComplete() {
// When pan is returned, it has to contain pan + expiry + cvv.
// When pan is not returned, it has to contain context token to indicate
// success.
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnableVirtualCardsRiskBasedAuthentication)) {
- return IsAllCardInformationValidIncludingDcvv() ||
- CanPerformVirtualCardAuth();
- }
- return IsAllCardInformationValidIncludingDcvv();
+ return IsAllCardInformationValidIncludingDcvv() ||
+ CanPerformVirtualCardAuth();
}
}
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc b/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc
new file mode 100644
index 00000000000..646142d1311
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc
@@ -0,0 +1,220 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/payments/payments_requests/upload_card_request.h"
+
+#include <string>
+
+#include "base/feature_list.h"
+#include "base/json/json_writer.h"
+#include "base/strings/escape.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/autofill_payments_features.h"
+
+namespace autofill::payments {
+
+namespace {
+const char kUploadCardRequestPath[] =
+ "payments/apis-secure/chromepaymentsservice/savecard"
+ "?s7e_suffix=chromewallet";
+const char kUploadCardRequestFormat[] =
+ "requestContentType=application/json; charset=utf-8&request=%s"
+ "&s7e_1_pan=%s&s7e_13_cvc=%s";
+const char kUploadCardRequestFormatWithoutCvc[] =
+ "requestContentType=application/json; charset=utf-8&request=%s"
+ "&s7e_1_pan=%s";
+} // namespace
+
+UploadCardRequest::UploadCardRequest(
+ const PaymentsClient::UploadRequestDetails& request_details,
+ const bool full_sync_enabled,
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ const PaymentsClient::UploadCardResponseDetails&)>
+ callback)
+ : request_details_(request_details),
+ full_sync_enabled_(full_sync_enabled),
+ callback_(std::move(callback)) {}
+
+UploadCardRequest::~UploadCardRequest() = default;
+
+std::string UploadCardRequest::GetRequestUrlPath() {
+ return kUploadCardRequestPath;
+}
+
+std::string UploadCardRequest::GetRequestContentType() {
+ return "application/x-www-form-urlencoded";
+}
+
+std::string UploadCardRequest::GetRequestContent() {
+ base::Value request_dict(base::Value::Type::DICTIONARY);
+ request_dict.SetKey("encrypted_pan", base::Value("__param:s7e_1_pan"));
+ if (!request_details_.cvc.empty())
+ request_dict.SetKey("encrypted_cvc", base::Value("__param:s7e_13_cvc"));
+ request_dict.SetKey("risk_data_encoded",
+ BuildRiskDictionary(request_details_.risk_data));
+
+ const std::string& app_locale = request_details_.app_locale;
+ base::Value context(base::Value::Type::DICTIONARY);
+ context.SetKey("language_code", base::Value(app_locale));
+ context.SetKey("billable_service",
+ base::Value(kUploadCardBillableServiceNumber));
+ if (request_details_.billing_customer_number != 0) {
+ context.SetKey("customer_context",
+ BuildCustomerContextDictionary(
+ request_details_.billing_customer_number));
+ }
+ request_dict.SetKey("context", std::move(context));
+
+ base::Value chrome_user_context(base::Value::Type::DICTIONARY);
+ chrome_user_context.SetKey("full_sync_enabled",
+ base::Value(full_sync_enabled_));
+ request_dict.SetKey("chrome_user_context", std::move(chrome_user_context));
+
+ SetStringIfNotEmpty(request_details_.card, CREDIT_CARD_NAME_FULL, app_locale,
+ "cardholder_name", request_dict);
+
+ base::Value addresses(base::Value::Type::LIST);
+ for (const AutofillProfile& profile : request_details_.profiles) {
+ addresses.Append(BuildAddressDictionary(profile, app_locale, true));
+ }
+ request_dict.SetKey("address", std::move(addresses));
+
+ request_dict.SetKey("context_token",
+ base::Value(request_details_.context_token));
+
+ int value = 0;
+ const std::u16string exp_month = request_details_.card.GetInfo(
+ AutofillType(CREDIT_CARD_EXP_MONTH), app_locale);
+ const std::u16string exp_year = request_details_.card.GetInfo(
+ AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale);
+ if (base::StringToInt(exp_month, &value))
+ request_dict.SetKey("expiration_month", base::Value(value));
+ if (base::StringToInt(exp_year, &value))
+ request_dict.SetKey("expiration_year", base::Value(value));
+
+ if (request_details_.card.HasNonEmptyValidNickname()) {
+ request_dict.SetKey("nickname",
+ base::Value(request_details_.card.nickname()));
+ }
+
+ SetActiveExperiments(request_details_.active_experiments, request_dict);
+
+ const std::u16string pan = request_details_.card.GetInfo(
+ AutofillType(CREDIT_CARD_NUMBER), app_locale);
+ std::string json_request;
+ base::JSONWriter::Write(request_dict, &json_request);
+ std::string request_content;
+ if (request_details_.cvc.empty()) {
+ request_content = base::StringPrintf(
+ kUploadCardRequestFormatWithoutCvc,
+ base::EscapeUrlEncodedData(json_request, true).c_str(),
+ base::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str());
+ } else {
+ request_content = base::StringPrintf(
+ kUploadCardRequestFormat,
+ base::EscapeUrlEncodedData(json_request, true).c_str(),
+ base::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str(),
+ base::EscapeUrlEncodedData(base::UTF16ToASCII(request_details_.cvc),
+ true)
+ .c_str());
+ }
+ VLOG(3) << "savecard request body: " << request_content;
+ return request_content;
+}
+
+void UploadCardRequest::ParseResponse(const base::Value& response) {
+ const std::string* credit_card_id = response.FindStringKey("credit_card_id");
+ upload_card_response_details_.server_id =
+ credit_card_id ? *credit_card_id : std::string();
+
+ const std::string* response_instrument_id =
+ response.FindStringKey("instrument_id");
+ if (response_instrument_id) {
+ int64_t instrument_id;
+ if (base::StringToInt64(base::StringPiece(*response_instrument_id),
+ &instrument_id)) {
+ upload_card_response_details_.instrument_id = instrument_id;
+ }
+ }
+
+ const std::string* card_art_url = response.FindStringKey("card_art_url");
+ upload_card_response_details_.card_art_url =
+ card_art_url ? GURL(*card_art_url) : GURL();
+
+ const auto* virtual_card_metadata = response.FindKeyOfType(
+ "virtual_card_metadata", base::Value::Type::DICTIONARY);
+ if (virtual_card_metadata) {
+ const std::string* virtual_card_enrollment_status =
+ virtual_card_metadata->FindStringKey("status");
+ if (virtual_card_enrollment_status) {
+ if (*virtual_card_enrollment_status == "ENROLLED") {
+ upload_card_response_details_.virtual_card_enrollment_state =
+ CreditCard::VirtualCardEnrollmentState::ENROLLED;
+ } else if (*virtual_card_enrollment_status == "ENROLLMENT_ELIGIBLE") {
+ upload_card_response_details_.virtual_card_enrollment_state =
+ CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE;
+ } else {
+ upload_card_response_details_.virtual_card_enrollment_state =
+ CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_NOT_ELIGIBLE;
+ }
+ }
+
+ if (base::FeatureList::IsEnabled(
+ features::
+ kAutofillEnableGetDetailsForEnrollParsingInUploadCardResponse) &&
+ !base::FeatureList::IsEnabled(
+ features::kAutofillEnableToolbarStatusChip) &&
+ !base::FeatureList::IsEnabled(
+ features::kAutofillCreditCardUploadFeedback) &&
+ upload_card_response_details_.virtual_card_enrollment_state ==
+ CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE) {
+ const auto* virtual_card_enrollment_data =
+ virtual_card_metadata->FindKeyOfType("virtual_card_enrollment_data",
+ base::Value::Type::DICTIONARY);
+ if (virtual_card_enrollment_data) {
+ PaymentsClient::GetDetailsForEnrollmentResponseDetails
+ get_details_for_enrollment_response_details;
+ const base::Value* google_legal_message =
+ virtual_card_enrollment_data->FindKeyOfType(
+ "google_legal_message", base::Value::Type::DICTIONARY);
+ if (google_legal_message) {
+ LegalMessageLine::Parse(
+ *google_legal_message,
+ &get_details_for_enrollment_response_details.google_legal_message,
+ /*escape_apostrophes=*/true);
+ }
+
+ const base::Value* external_legal_message =
+ virtual_card_enrollment_data->FindKeyOfType(
+ "external_legal_message", base::Value::Type::DICTIONARY);
+ if (external_legal_message) {
+ LegalMessageLine::Parse(
+ *external_legal_message,
+ &get_details_for_enrollment_response_details.issuer_legal_message,
+ /*escape_apostrophes=*/true);
+ }
+
+ const auto* context_token =
+ virtual_card_enrollment_data->FindStringKey("context_token");
+ get_details_for_enrollment_response_details.vcn_context_token =
+ context_token ? *context_token : std::string();
+
+ upload_card_response_details_
+ .get_details_for_enrollment_response_details =
+ get_details_for_enrollment_response_details;
+ }
+ }
+ }
+}
+
+bool UploadCardRequest::IsResponseComplete() {
+ return true;
+}
+
+void UploadCardRequest::RespondToDelegate(
+ AutofillClient::PaymentsRpcResult result) {
+ std::move(callback_).Run(result, upload_card_response_details_);
+}
+
+} // namespace autofill::payments
diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.h b/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.h
new file mode 100644
index 00000000000..80b0f86497a
--- /dev/null
+++ b/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.h
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_UPLOAD_CARD_REQUEST_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_UPLOAD_CARD_REQUEST_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+#include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace autofill::payments {
+
+class UploadCardRequest : public PaymentsRequest {
+ public:
+ UploadCardRequest(
+ const PaymentsClient::UploadRequestDetails& request_details,
+ const bool full_sync_enabled,
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ const PaymentsClient::UploadCardResponseDetails&)>
+ callback);
+ UploadCardRequest(const UploadCardRequest&) = delete;
+ UploadCardRequest& operator=(const UploadCardRequest&) = delete;
+ ~UploadCardRequest() override;
+
+ // PaymentsRequest:
+ std::string GetRequestUrlPath() override;
+ std::string GetRequestContentType() override;
+ std::string GetRequestContent() override;
+ void ParseResponse(const base::Value& response) override;
+ bool IsResponseComplete() override;
+ void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override;
+
+ private:
+ const PaymentsClient::UploadRequestDetails request_details_;
+ const bool full_sync_enabled_;
+ base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
+ const PaymentsClient::UploadCardResponseDetails&)>
+ callback_;
+ PaymentsClient::UploadCardResponseDetails upload_card_response_details_;
+};
+
+} // namespace autofill::payments
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_UPLOAD_CARD_REQUEST_H_
diff --git a/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc b/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc
index cc7d6c3f576..e6e4e14dfe1 100644
--- a/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc
+++ b/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc
@@ -33,13 +33,13 @@ void TestCreditCardFIDOAuthenticator::Authenticate(
}
void TestCreditCardFIDOAuthenticator::GetAssertion(
- PublicKeyCredentialRequestOptionsPtr request_options) {
+ blink::mojom::PublicKeyCredentialRequestOptionsPtr request_options) {
request_options_ = request_options->Clone();
CreditCardFIDOAuthenticator::GetAssertion(std::move(request_options));
}
void TestCreditCardFIDOAuthenticator::MakeCredential(
- PublicKeyCredentialCreationOptionsPtr creation_options) {
+ blink::mojom::PublicKeyCredentialCreationOptionsPtr creation_options) {
creation_options_ = creation_options->Clone();
CreditCardFIDOAuthenticator::MakeCredential(std::move(creation_options));
}
@@ -54,15 +54,15 @@ void TestCreditCardFIDOAuthenticator::GetAssertion(
CreditCardFIDOAuthenticator* fido_authenticator,
bool did_succeed) {
if (did_succeed) {
- GetAssertionAuthenticatorResponsePtr response =
- GetAssertionAuthenticatorResponse::New();
+ blink::mojom::GetAssertionAuthenticatorResponsePtr response =
+ blink::mojom::GetAssertionAuthenticatorResponse::New();
response->info = blink::mojom::CommonCredentialInfo::New();
- fido_authenticator->OnDidGetAssertion(AuthenticatorStatus::SUCCESS,
- std::move(response),
- /*dom_exception_details=*/nullptr);
+ fido_authenticator->OnDidGetAssertion(
+ blink::mojom::AuthenticatorStatus::SUCCESS, std::move(response),
+ /*dom_exception_details=*/nullptr);
} else {
fido_authenticator->OnDidGetAssertion(
- AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr,
+ blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr,
/*dom_exception_details=*/nullptr);
}
}
@@ -72,15 +72,15 @@ void TestCreditCardFIDOAuthenticator::MakeCredential(
CreditCardFIDOAuthenticator* fido_authenticator,
bool did_succeed) {
if (did_succeed) {
- MakeCredentialAuthenticatorResponsePtr response =
- MakeCredentialAuthenticatorResponse::New();
+ blink::mojom::MakeCredentialAuthenticatorResponsePtr response =
+ blink::mojom::MakeCredentialAuthenticatorResponse::New();
response->info = blink::mojom::CommonCredentialInfo::New();
- fido_authenticator->OnDidMakeCredential(AuthenticatorStatus::SUCCESS,
- std::move(response),
- /*dom_exception_details=*/nullptr);
+ fido_authenticator->OnDidMakeCredential(
+ blink::mojom::AuthenticatorStatus::SUCCESS, std::move(response),
+ /*dom_exception_details=*/nullptr);
} else {
fido_authenticator->OnDidMakeCredential(
- AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr,
+ blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr,
/*dom_exception_details=*/nullptr);
}
}
diff --git a/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h b/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h
index c4fda5e903e..016d6a2999e 100644
--- a/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h
+++ b/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h
@@ -37,10 +37,10 @@ class TestCreditCardFIDOAuthenticator : public CreditCardFIDOAuthenticator {
absl::optional<std::string> context_token) override;
void IsUserVerifiable(base::OnceCallback<void(bool)> callback) override;
bool IsUserOptedIn() override;
- void GetAssertion(
- PublicKeyCredentialRequestOptionsPtr request_options) override;
- void MakeCredential(
- PublicKeyCredentialCreationOptionsPtr creation_options) override;
+ void GetAssertion(blink::mojom::PublicKeyCredentialRequestOptionsPtr
+ request_options) override;
+ void MakeCredential(blink::mojom::PublicKeyCredentialCreationOptionsPtr
+ creation_options) override;
void OptOut() override;
// Invokes fido_authenticator->OnDidGetAssertion().
@@ -76,8 +76,8 @@ class TestCreditCardFIDOAuthenticator : public CreditCardFIDOAuthenticator {
friend class BrowserAutofillManagerTest;
friend class CreditCardAccessManagerTest;
- PublicKeyCredentialRequestOptionsPtr request_options_;
- PublicKeyCredentialCreationOptionsPtr creation_options_;
+ blink::mojom::PublicKeyCredentialRequestOptionsPtr request_options_;
+ blink::mojom::PublicKeyCredentialCreationOptionsPtr creation_options_;
bool is_user_verifiable_ = false;
absl::optional<bool> is_user_opted_in_;
bool opt_out_called_ = false;
diff --git a/chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.cc b/chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.cc
index 7d97e7b3d60..a0267639edc 100644
--- a/chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.cc
+++ b/chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.cc
@@ -47,7 +47,7 @@ void TestCreditCardSaveManager::set_upload_request_card(
upload_request_.card = std::move(card);
}
-raw_ptr<payments::PaymentsClient::UploadRequestDetails>
+payments::PaymentsClient::UploadRequestDetails*
TestCreditCardSaveManager::upload_request() {
return &upload_request_;
}
diff --git a/chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.h b/chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.h
index 4d54f6c8457..630327bb0d5 100644
--- a/chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.h
+++ b/chromium/components/autofill/core/browser/payments/test_credit_card_save_manager.h
@@ -46,7 +46,7 @@ class TestCreditCardSaveManager : public CreditCardSaveManager {
void set_upload_request_card(const CreditCard& card);
- raw_ptr<payments::PaymentsClient::UploadRequestDetails> upload_request();
+ payments::PaymentsClient::UploadRequestDetails* upload_request();
private:
void OnDidUploadCard(
@@ -59,6 +59,9 @@ class TestCreditCardSaveManager : public CreditCardSaveManager {
FRIEND_TEST_ALL_PREFIXES(CreditCardSaveManagerTest,
OnDidUploadCard_VirtualCardEnrollment);
+ FRIEND_TEST_ALL_PREFIXES(
+ CreditCardSaveManagerTest,
+ OnDidUploadCard_VirtualCardEnrollment_GetDetailsForEnrollmentResponseDetailsReturned);
FRIEND_TEST_ALL_PREFIXES(CreditCardSaveManagerTest,
UploadCreditCard_NumStrikesLoggedOnUploadNotSuccess);
};
diff --git a/chromium/components/autofill/core/browser/payments/test_legal_message_line.h b/chromium/components/autofill/core/browser/payments/test_legal_message_line.h
index b894206feef..79da420d000 100644
--- a/chromium/components/autofill/core/browser/payments/test_legal_message_line.h
+++ b/chromium/components/autofill/core/browser/payments/test_legal_message_line.h
@@ -12,14 +12,14 @@
namespace autofill {
-using Link = LegalMessageLine::Link;
-
// A legal message line that allows for modifications.
class TestLegalMessageLine : public LegalMessageLine {
public:
- TestLegalMessageLine() {}
+ TestLegalMessageLine() = default;
- TestLegalMessageLine(const std::string& ascii_text) { set_text(ascii_text); }
+ explicit TestLegalMessageLine(const std::string& ascii_text) {
+ set_text(ascii_text);
+ }
TestLegalMessageLine(const std::string& ascii_text, const Links& links) {
set_text(ascii_text);
@@ -29,7 +29,7 @@ class TestLegalMessageLine : public LegalMessageLine {
TestLegalMessageLine(const TestLegalMessageLine&) = delete;
TestLegalMessageLine& operator=(const TestLegalMessageLine&) = delete;
- ~TestLegalMessageLine() override {}
+ ~TestLegalMessageLine() override = default;
void set_text(const std::string& ascii_text) {
text_ = base::ASCIIToUTF16(ascii_text);
diff --git a/chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.cc b/chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.cc
index 8cf67a3db66..de9befcbc64 100644
--- a/chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.cc
+++ b/chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.cc
@@ -9,9 +9,9 @@
namespace autofill {
TestVirtualCardEnrollmentManager::TestVirtualCardEnrollmentManager(
- raw_ptr<TestPersonalDataManager> personal_data_manager,
- raw_ptr<payments::TestPaymentsClient> payments_client,
- raw_ptr<TestAutofillClient> autofill_client = nullptr)
+ TestPersonalDataManager* personal_data_manager,
+ payments::TestPaymentsClient* payments_client,
+ TestAutofillClient* autofill_client = nullptr)
: VirtualCardEnrollmentManager(personal_data_manager,
payments_client,
autofill_client) {}
@@ -19,7 +19,7 @@ TestVirtualCardEnrollmentManager::TestVirtualCardEnrollmentManager(
TestVirtualCardEnrollmentManager::~TestVirtualCardEnrollmentManager() = default;
void TestVirtualCardEnrollmentManager::LoadRiskDataAndContinueFlow(
- raw_ptr<PrefService> user_prefs,
+ PrefService* user_prefs,
base::OnceCallback<void(const std::string&)> callback) {
std::move(callback).Run("some risk data");
}
diff --git a/chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.h b/chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.h
index 652d080568a..5841c543887 100644
--- a/chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.h
+++ b/chromium/components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.h
@@ -5,7 +5,6 @@
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_TEST_VIRTUAL_CARD_ENROLLMENT_MANAGER_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_TEST_VIRTUAL_CARD_ENROLLMENT_MANAGER_H_
-#include "base/memory/raw_ptr.h"
#include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
#include "components/autofill/core/browser/test_autofill_client.h"
@@ -16,9 +15,9 @@ class TestPersonalDataManager;
class TestVirtualCardEnrollmentManager : public VirtualCardEnrollmentManager {
public:
TestVirtualCardEnrollmentManager(
- raw_ptr<TestPersonalDataManager> personal_data_manager,
- raw_ptr<payments::TestPaymentsClient> payments_client,
- raw_ptr<TestAutofillClient> autofill_client);
+ TestPersonalDataManager* personal_data_manager,
+ payments::TestPaymentsClient* payments_client,
+ TestAutofillClient* autofill_client);
TestVirtualCardEnrollmentManager(const TestVirtualCardEnrollmentManager&) =
delete;
TestVirtualCardEnrollmentManager& operator=(
@@ -27,10 +26,18 @@ class TestVirtualCardEnrollmentManager : public VirtualCardEnrollmentManager {
bool GetAvatarAnimationComplete() const { return avatar_animation_complete_; }
+ void SetAvatarAnimationComplete(bool avatar_animation_complete) {
+ avatar_animation_complete_ = avatar_animation_complete;
+ }
+
bool GetEnrollResponseDetailsReceived() const {
return enroll_response_details_received_;
}
+ void SetEnrollResponseDetailsReceived(bool enroll_response_details_received) {
+ enroll_response_details_received_ = enroll_response_details_received;
+ }
+
AutofillClient::PaymentsRpcResult GetPaymentsRpcResult() { return result_; }
void SetPaymentsRpcResult(AutofillClient::PaymentsRpcResult result) {
@@ -43,12 +50,13 @@ class TestVirtualCardEnrollmentManager : public VirtualCardEnrollmentManager {
bool GetBubbleShown() { return bubble_shown_; }
- raw_ptr<VirtualCardEnrollmentProcessState>
- GetVirtualCardEnrollmentProcessState() {
+ void SetBubbleShown(bool bubble_shown) { bubble_shown_ = bubble_shown; }
+
+ VirtualCardEnrollmentProcessState* GetVirtualCardEnrollmentProcessState() {
return &state_;
}
- void SetAutofillClient(raw_ptr<AutofillClient> autofill_client) {
+ void SetAutofillClient(AutofillClient* autofill_client) {
autofill_client_ = autofill_client;
}
@@ -63,7 +71,7 @@ class TestVirtualCardEnrollmentManager : public VirtualCardEnrollmentManager {
// VirtualCardEnrollmentManager:
void LoadRiskDataAndContinueFlow(
- raw_ptr<PrefService> user_prefs,
+ PrefService* user_prefs,
base::OnceCallback<void(const std::string&)> callback) override;
void OnDidGetUpdateVirtualCardEnrollmentResponse(
VirtualCardEnrollmentRequestType type,
diff --git a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
index 6d967e0dc2f..4a208e67f59 100644
--- a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
+++ b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -3,14 +3,17 @@
// found in the LICENSE file.
#include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
+
#include <string>
+#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h"
#include "components/autofill/core/browser/payments/payments_util.h"
#include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h"
+#include "components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/strike_database.h"
#include "components/autofill/core/browser/strike_database_base.h"
@@ -39,9 +42,9 @@ VirtualCardEnrollmentProcessState::~VirtualCardEnrollmentProcessState() =
default;
VirtualCardEnrollmentManager::VirtualCardEnrollmentManager(
- raw_ptr<PersonalDataManager> personal_data_manager,
- raw_ptr<payments::PaymentsClient> payments_client,
- raw_ptr<AutofillClient> autofill_client)
+ PersonalDataManager* personal_data_manager,
+ payments::PaymentsClient* payments_client,
+ AutofillClient* autofill_client)
: autofill_client_(autofill_client),
personal_data_manager_(personal_data_manager),
payments_client_(payments_client) {
@@ -58,9 +61,12 @@ VirtualCardEnrollmentManager::VirtualCardEnrollmentManager(
VirtualCardEnrollmentManager::~VirtualCardEnrollmentManager() = default;
-void VirtualCardEnrollmentManager::OfferVirtualCardEnroll(
+void VirtualCardEnrollmentManager::InitVirtualCardEnroll(
const CreditCard& credit_card,
VirtualCardEnrollmentSource virtual_card_enrollment_source,
+ absl::optional<
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails>
+ get_details_for_enrollment_response_details,
const raw_ptr<PrefService> user_prefs,
RiskAssessmentFunction risk_assessment_function,
VirtualCardEnrollmentFieldsLoadedCallback
@@ -68,42 +74,32 @@ void VirtualCardEnrollmentManager::OfferVirtualCardEnroll(
// If at strike limit, exit enrollment flow.
if (base::FeatureList::IsEnabled(
features::kAutofillEnableUpdateVirtualCardEnrollment) &&
- IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+ ShouldBlockVirtualCardEnrollment(
base::NumberToString(credit_card.instrument_id()),
virtual_card_enrollment_source)) {
return;
}
- Reset();
- DCHECK_NE(virtual_card_enrollment_source, VirtualCardEnrollmentSource::kNone);
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
- // Hide the bubble and icon if it is already showing for a previous enrollment
- // bubble.
- DCHECK(autofill_client_);
- autofill_client_->HideVirtualCardEnrollBubbleAndIconIfVisible();
-#endif
+ SetInitialVirtualCardEnrollFields(credit_card,
+ virtual_card_enrollment_source);
- state_.virtual_card_enrollment_fields.credit_card = credit_card;
- risk_assessment_function_ = std::move(risk_assessment_function);
- virtual_card_enrollment_fields_loaded_callback_ =
- std::move(virtual_card_enrollment_fields_loaded_callback);
-
- // The |card_art_image| might not be synced yet from the sync server which
- // will result in a nullptr. This situation can occur in the upstream flow.
- // If it is not synced, GetCreditCardArtImageForUrl() will send a fetch
- // request to sync the |card_art_image|, and before showing the
- // VirtualCardEnrollmentBubble we will try to fetch the |card_art_image|
- // from the local cache.
- raw_ptr<gfx::Image> card_art_image =
- personal_data_manager_->GetCreditCardArtImageForUrl(
- credit_card.card_art_url());
- if (card_art_image && !card_art_image->IsEmpty()) {
- state_.virtual_card_enrollment_fields.card_art_image =
- card_art_image->ToImageSkia();
+ if (get_details_for_enrollment_response_details.has_value() &&
+ IsValidGetDetailsForEnrollmentResponseDetails(
+ get_details_for_enrollment_response_details.value())) {
+ SetGetDetailsForEnrollmentResponseDetails(
+ get_details_for_enrollment_response_details.value());
}
- state_.virtual_card_enrollment_fields.virtual_card_enrollment_source =
- virtual_card_enrollment_source;
+ // |autofill_client_| being nullptr denotes that we are in the Clank settings
+ // page virtual card enrollment use case, so we will need to use
+ // |risk_assessment_function_| to load risk data as we do not have access to
+ // web contents, and |virtual_card_enrollment_fields_loaded_callback_| to
+ // display the UI.
+ if (!autofill_client_) {
+ risk_assessment_function_ = std::move(risk_assessment_function);
+ virtual_card_enrollment_fields_loaded_callback_ =
+ std::move(virtual_card_enrollment_fields_loaded_callback);
+ }
LoadRiskDataAndContinueFlow(
user_prefs,
@@ -117,8 +113,10 @@ void VirtualCardEnrollmentManager::OnCardSavedAnimationComplete() {
VirtualCardEnrollmentSource::kUpstream) {
avatar_animation_complete_ = true;
- if (enroll_response_details_received_)
+ if (enroll_response_details_received_) {
+ EnsureCardArtImageIsSetBeforeShowingUI();
ShowVirtualCardEnrollBubble();
+ }
}
}
@@ -179,10 +177,9 @@ void VirtualCardEnrollmentManager::Unenroll(int64_t instrument_id) {
VirtualCardEnrollmentRequestType::kUnenroll));
}
-bool VirtualCardEnrollmentManager::
- IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
- const std::string& instrument_id,
- VirtualCardEnrollmentSource virtual_card_enrollment_source) const {
+bool VirtualCardEnrollmentManager::ShouldBlockVirtualCardEnrollment(
+ const std::string& instrument_id,
+ VirtualCardEnrollmentSource virtual_card_enrollment_source) const {
if (virtual_card_enrollment_source ==
VirtualCardEnrollmentSource::kSettingsPage)
return false;
@@ -190,14 +187,30 @@ bool VirtualCardEnrollmentManager::
if (!GetVirtualCardEnrollmentStrikeDatabase())
return false;
- bool max_strikes_limit_reached =
- GetVirtualCardEnrollmentStrikeDatabase()->IsMaxStrikesLimitReached(
- instrument_id);
- if (max_strikes_limit_reached) {
- LogVirtualCardEnrollmentBubbleMaxStrikesLimitReached(
- virtual_card_enrollment_source);
+ VirtualCardEnrollmentStrikeDatabase::BlockedReason reason =
+ VirtualCardEnrollmentStrikeDatabase::kUnknown;
+ if (!GetVirtualCardEnrollmentStrikeDatabase()->ShouldBlockFeature(
+ instrument_id, &reason)) {
+ return false;
+ }
+
+ switch (reason) {
+ case VirtualCardEnrollmentStrikeDatabase::kMaxStrikeLimitReached:
+ LogVirtualCardEnrollmentBubbleMaxStrikesLimitReached(
+ virtual_card_enrollment_source);
+ LogVirtualCardEnrollmentNotOfferedDueToMaxStrikes(
+ virtual_card_enrollment_source);
+ break;
+ case VirtualCardEnrollmentStrikeDatabase::kRequiredDelayNotPassed:
+ LogVirtualCardEnrollmentNotOfferedDueToRequiredDelay(
+ virtual_card_enrollment_source);
+ break;
+ case VirtualCardEnrollmentStrikeDatabase::kUnknown:
+ NOTREACHED();
+ break;
}
- return max_strikes_limit_reached;
+
+ return true;
}
void VirtualCardEnrollmentManager::
@@ -221,6 +234,12 @@ void VirtualCardEnrollmentManager::
if (!GetVirtualCardEnrollmentStrikeDatabase())
return;
+ // Before we remove the existing strikes for the card, log the strike number
+ // first.
+ base::UmaHistogramCounts1000(
+ "Autofill.StrikeDatabase.StrikesPresentWhenVirtualCardEnrolled",
+ GetVirtualCardEnrollmentStrikeDatabase()->GetStrikes(instrument_id));
+
GetVirtualCardEnrollmentStrikeDatabase()->ClearStrikes(instrument_id);
// Log that strikes are being cleared.
@@ -259,7 +278,7 @@ VirtualCardEnrollmentManager::GetVirtualCardEnrollmentStrikeDatabase() const {
}
void VirtualCardEnrollmentManager::LoadRiskDataAndContinueFlow(
- raw_ptr<PrefService> user_prefs,
+ PrefService* user_prefs,
base::OnceCallback<void(const std::string&)> callback) {
if (autofill_client_) {
autofill_client_->LoadRiskData(std::move(callback));
@@ -268,8 +287,8 @@ void VirtualCardEnrollmentManager::LoadRiskDataAndContinueFlow(
// use case, so we load risk data using a method that does not require web
// contents to be present.
std::move(risk_assessment_function_)
- .Run(/*obfuscated_gaia_id=*/0, user_prefs, std::move(callback), nullptr,
- gfx::Rect());
+ .Run(/*obfuscated_gaia_id=*/0, user_prefs, std::move(callback),
+ /*web_contents=*/nullptr, gfx::Rect());
}
}
@@ -313,7 +332,41 @@ void VirtualCardEnrollmentManager::ShowVirtualCardEnrollBubble() {
void VirtualCardEnrollmentManager::OnRiskDataLoadedForVirtualCard(
const std::string& risk_data) {
state_.risk_data = risk_data;
- GetDetailsForEnroll();
+
+ // If we are in the upstream case and the
+ // GetDetailsForEnrollmentResponseDetails were already received, then we
+ // received it from the UploadCardResponseDetails. Thus, we can skip making
+ // another GetDetailsForEnrollmentRequest and go straight to showing the
+ // bubble if the avatar animation is complete.
+ if (state_.virtual_card_enrollment_fields.virtual_card_enrollment_source ==
+ VirtualCardEnrollmentSource::kUpstream &&
+ enroll_response_details_received_) {
+#if !BUILDFLAG(IS_ANDROID)
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillEnableToolbarStatusChip) &&
+ base::FeatureList::IsEnabled(
+ features::kAutofillCreditCardUploadFeedback) &&
+ !avatar_animation_complete_) {
+ // If status chip and upload feedback is enabled, we need to make sure
+ // we wait for the upload card animation to complete before showing the
+ // virtual card enroll bubble, or else we will have a conflict and the
+ // virtual card enroll bubble will not show.
+ return;
+ }
+#endif
+
+ // We are about to show the virtual card enroll bubble, so make sure the
+ // card art image is set to then display in the bubble.
+ EnsureCardArtImageIsSetBeforeShowingUI();
+
+ // Shows the virtual card enroll bubble.
+ ShowVirtualCardEnrollBubble();
+ } else {
+ // We are not in the upstream case where we received the
+ // GetDetailsForEnrollmentResponseDetails in the UploadCardResponseDetails,
+ // so we need to make a GetDetailsForEnroll request.
+ GetDetailsForEnroll();
+ }
}
void VirtualCardEnrollmentManager::GetDetailsForEnroll() {
@@ -327,6 +380,9 @@ void VirtualCardEnrollmentManager::GetDetailsForEnroll() {
state_.virtual_card_enrollment_fields.credit_card.instrument_id();
request_details.source =
state_.virtual_card_enrollment_fields.virtual_card_enrollment_source;
+
+ get_details_for_enrollment_request_sent_timestamp_ = AutofillClock::Now();
+
payments_client_->GetVirtualCardEnrollmentDetails(
request_details,
base::BindOnce(
@@ -340,7 +396,14 @@ void VirtualCardEnrollmentManager::OnDidGetDetailsForEnrollResponse(
AutofillClient::PaymentsRpcResult result,
const payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails&
response) {
- enroll_response_details_received_ = true;
+ if (get_details_for_enrollment_request_sent_timestamp_.has_value()) {
+ LogGetDetailsForEnrollmentRequestLatency(
+ state_.virtual_card_enrollment_fields.virtual_card_enrollment_source,
+ result,
+ AutofillClock::Now() -
+ get_details_for_enrollment_request_sent_timestamp_.value());
+ get_details_for_enrollment_request_sent_timestamp_.reset();
+ }
LogGetDetailsForEnrollmentRequestResult(
state_.virtual_card_enrollment_fields.virtual_card_enrollment_source,
@@ -357,43 +420,11 @@ void VirtualCardEnrollmentManager::OnDidGetDetailsForEnrollResponse(
return;
}
- state_.virtual_card_enrollment_fields.google_legal_message =
- std::move(response.google_legal_message);
- // Issuer legal message is empty for some issuers.
- if (!response.issuer_legal_message.empty()) {
- state_.virtual_card_enrollment_fields.issuer_legal_message =
- std::move(response.issuer_legal_message);
- }
-
- // The |vcn_context_token| will be used by the server to link the previous
- // GetDetailsForEnrollRequest to the future UpdateVirtualCardEnrollmentRequest
- // if the user decides to enroll |state_|'s |virtual_card_enrollment_fields|'s
- // |credit_card| as a virtual card.
- state_.vcn_context_token = response.vcn_context_token;
-
- // Tries to get the card art image again from the local cache. If the card art
- // image is not available, then we fall back to the network image instead. The
- // card art image might not be present in the upstream flow if the chrome sync
- // server has not synced down the card art url yet for the card just uploaded.
- if (!state_.virtual_card_enrollment_fields.card_art_image) {
- raw_ptr<gfx::Image> cached_card_art_image =
- personal_data_manager_->GetCachedCardArtImageForUrl(
- state_.virtual_card_enrollment_fields.credit_card.card_art_url());
- if (cached_card_art_image && !cached_card_art_image->IsEmpty()) {
- // We found a card art image in the cache, so set |state_|'s
- // |virtual_card_enrollment_fields|'s |card_art_image| to it.
- state_.virtual_card_enrollment_fields.card_art_image =
- cached_card_art_image->ToImageSkia();
- } else {
- // We did not find a card art image in the cache, so set |state_|'s
- // |virtual_card_enrollment_fields|'s |card_art_image| to the network
- // image instead.
- state_.virtual_card_enrollment_fields.card_art_image =
- ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
- CreditCard::IconResourceId(
- state_.virtual_card_enrollment_fields.credit_card.network()));
- }
- }
+ // The response is already checked in
+ // GetDetailsForEnrollmentRequest::IsResponseComplete(), so we should have a
+ // valid GetDetailsForEnrollmentResponseDetails here.
+ DCHECK(IsValidGetDetailsForEnrollmentResponseDetails(response));
+ SetGetDetailsForEnrollmentResponseDetails(response);
#if !BUILDFLAG(IS_ANDROID)
if (base::FeatureList::IsEnabled(
@@ -403,22 +434,124 @@ void VirtualCardEnrollmentManager::OnDidGetDetailsForEnrollResponse(
state_.virtual_card_enrollment_fields.virtual_card_enrollment_source ==
VirtualCardEnrollmentSource::kUpstream &&
!avatar_animation_complete_) {
+ // If status chip and upload feedback is enabled, we need to make sure
+ // we wait for the upload card animation to complete before showing the
+ // virtual card enroll bubble, or else we will have a conflict and the
+ // virtual card enroll bubble will not show.
return;
}
#endif
+ // We are about to show the UI for virtual card enrollment, so make sure the
+ // card art image is set to then display in the bubble.
+ EnsureCardArtImageIsSetBeforeShowingUI();
+
if (autofill_client_) {
ShowVirtualCardEnrollBubble();
} else {
- // If the `autofill_client_` is not present, it means that the request is
+ // If the |autofill_client_| is not present, it means that the request is
// from Android settings page, thus run the callback with the
- // `virtual_card_enrollment_fields_`, which would show the enrollment
+ // |virtual_card_enrollment_fields_|, which would show the enrollment
// dialog.
std::move(virtual_card_enrollment_fields_loaded_callback_)
.Run(&state_.virtual_card_enrollment_fields);
}
}
+void VirtualCardEnrollmentManager::SetGetDetailsForEnrollmentResponseDetails(
+ const payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails&
+ response) {
+ enroll_response_details_received_ = true;
+ state_.virtual_card_enrollment_fields.google_legal_message =
+ std::move(response.google_legal_message);
+
+ // Issuer legal message is empty for some issuers.
+ if (!response.issuer_legal_message.empty()) {
+ state_.virtual_card_enrollment_fields.issuer_legal_message =
+ std::move(response.issuer_legal_message);
+ }
+
+ // The |vcn_context_token| will be used by the server to link the previous
+ // GetDetailsForEnrollRequest to the future UpdateVirtualCardEnrollmentRequest
+ // if the user decides to enroll |state_|'s |virtual_card_enrollment_fields|'s
+ // |credit_card| as a virtual card.
+ state_.vcn_context_token = response.vcn_context_token;
+}
+
+void VirtualCardEnrollmentManager::EnsureCardArtImageIsSetBeforeShowingUI() {
+ if (state_.virtual_card_enrollment_fields.card_art_image)
+ return;
+
+ // Tries to get the card art image from the local cache. If the card art
+ // image is not available, then we fall back to the network image instead.
+ // The card art image might not be present in the upstream flow if the
+ // chrome sync server has not synced down the card art url yet for the card
+ // just uploaded.
+ raw_ptr<gfx::Image> cached_card_art_image =
+ personal_data_manager_->GetCachedCardArtImageForUrl(
+ state_.virtual_card_enrollment_fields.credit_card.card_art_url());
+ if (cached_card_art_image && !cached_card_art_image->IsEmpty()) {
+ // We found a card art image in the cache, so set |state_|'s
+ // |virtual_card_enrollment_fields|'s |card_art_image| to it.
+ state_.virtual_card_enrollment_fields.card_art_image =
+ cached_card_art_image->ToImageSkia();
+ } else {
+ // We did not find a card art image in the cache, so set |state_|'s
+ // |virtual_card_enrollment_fields|'s |card_art_image| to the network
+ // image instead.
+ state_.virtual_card_enrollment_fields.card_art_image =
+ ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+ CreditCard::IconResourceId(
+ state_.virtual_card_enrollment_fields.credit_card.network()));
+ }
+}
+
+void VirtualCardEnrollmentManager::SetInitialVirtualCardEnrollFields(
+ const CreditCard& credit_card,
+ VirtualCardEnrollmentSource virtual_card_enrollment_source) {
+ Reset();
+
+ DCHECK_NE(virtual_card_enrollment_source, VirtualCardEnrollmentSource::kNone);
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+ // Hide the bubble and icon if it is already showing for a previous enrollment
+ // bubble.
+ DCHECK(autofill_client_);
+ autofill_client_->HideVirtualCardEnrollBubbleAndIconIfVisible();
+#endif
+
+ state_.virtual_card_enrollment_fields.credit_card = credit_card;
+
+ // The |card_art_image| might not be synced yet from the sync server which
+ // will result in a nullptr. This situation can occur in the upstream flow.
+ // If it is not synced, GetCreditCardArtImageForUrl() will send a fetch
+ // request to sync the |card_art_image|, and before showing the
+ // VirtualCardEnrollmentBubble we will try to fetch the |card_art_image|
+ // from the local cache.
+ raw_ptr<gfx::Image> card_art_image =
+ personal_data_manager_->GetCreditCardArtImageForUrl(
+ credit_card.card_art_url());
+ if (card_art_image && !card_art_image->IsEmpty()) {
+ state_.virtual_card_enrollment_fields.card_art_image =
+ card_art_image->ToImageSkia();
+ }
+
+ state_.virtual_card_enrollment_fields.virtual_card_enrollment_source =
+ virtual_card_enrollment_source;
+}
+
+bool VirtualCardEnrollmentManager::
+ IsValidGetDetailsForEnrollmentResponseDetails(
+ const payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails&
+ get_details_for_enrollment_response_details) {
+ if (get_details_for_enrollment_response_details.google_legal_message.empty())
+ return false;
+
+ if (get_details_for_enrollment_response_details.vcn_context_token.empty())
+ return false;
+
+ return true;
+}
+
void VirtualCardEnrollmentManager::OnVirtualCardEnrollmentBubbleCancelled() {
if (base::FeatureList::IsEnabled(
features::kAutofillEnableUpdateVirtualCardEnrollment)) {
diff --git a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
index 725b0e1e3b3..26af151fea6 100644
--- a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
+++ b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
@@ -90,10 +90,9 @@ struct VirtualCardEnrollmentProcessState {
class VirtualCardEnrollmentManager {
public:
// The parameters should outlive the VirtualCardEnrollmentManager.
- VirtualCardEnrollmentManager(
- raw_ptr<PersonalDataManager> personal_data_manager,
- raw_ptr<payments::PaymentsClient> payments_client,
- raw_ptr<AutofillClient> autofill_client = nullptr);
+ VirtualCardEnrollmentManager(PersonalDataManager* personal_data_manager,
+ payments::PaymentsClient* payments_client,
+ AutofillClient* autofill_client = nullptr);
VirtualCardEnrollmentManager(const VirtualCardEnrollmentManager&) = delete;
VirtualCardEnrollmentManager& operator=(const VirtualCardEnrollmentManager&) =
delete;
@@ -115,9 +114,17 @@ class VirtualCardEnrollmentManager {
// |virtual_card_enrollment_source| will be used by
// ShowVirtualCardEnrollBubble() to differentiate different bubbles based on
// the source we originated from.
- void OfferVirtualCardEnroll(
+ virtual void InitVirtualCardEnroll(
const CreditCard& credit_card,
VirtualCardEnrollmentSource virtual_card_enrollment_source,
+ // |get_details_for_enrollment_response_details| will be populated if we
+ // are in the optimized upstream case, where we receive the
+ // GetDetailsForEnrollmentResponseDetails from the
+ // UploadCardResponseDetails, so we can then skip the
+ // GetDetailsForEnroll request in the Virtual Card Enrollment flow.
+ absl::optional<
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails>
+ get_details_for_enrollment_response_details = absl::nullopt,
// |user_prefs| will be populated if we are in the Android settings page,
// to then be used for loading risk data. Otherwise it will always be
// nullptr, and we should load risk data through |autofill_client_| as we
@@ -147,10 +154,13 @@ class VirtualCardEnrollmentManager {
// Unenrolls the card mapped to the given |instrument_id|.
void Unenroll(int64_t instrument_id);
- // Returns true if a credit card identified by its |instrument_id| is
+ // Returns true if a credit card identified by its |instrument_id| should be
// blocked for virtual card enrollment and is not attempting to enroll from
- // the settings page. Does nothing if the strike database is not available.
- bool IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+ // the settings page. Currently we block enrollment offer if the user has
+ // reached the limit of strikes or if the required delay time since last
+ // strike has not passed yet. Does nothing if the strike database is not
+ // available.
+ bool ShouldBlockVirtualCardEnrollment(
const std::string& instrument_id,
VirtualCardEnrollmentSource virtual_card_enrollment_source) const;
@@ -175,7 +185,7 @@ class VirtualCardEnrollmentManager {
// indicates the type of the request sent, i.e., enroll or unenroll.
// |result| represents the result from the server call to change the virtual
// card enrollment state for the credit card passed into
- // OfferVirtualCardEnroll().
+ // InitVirtualCardEnroll().
virtual void OnDidGetUpdateVirtualCardEnrollmentResponse(
VirtualCardEnrollmentRequestType type,
AutofillClient::PaymentsRpcResult result);
@@ -212,7 +222,7 @@ class VirtualCardEnrollmentManager {
// card enrollment flow. |user_prefs| will only be present in Clank settings
// page use cases, as we will not have access to web contents.
virtual void LoadRiskDataAndContinueFlow(
- raw_ptr<PrefService> user_prefs,
+ PrefService* user_prefs,
base::OnceCallback<void(const std::string&)> callback);
// Shows the VirtualCardEnrollmentBubble. |state_|'s
@@ -246,6 +256,10 @@ class VirtualCardEnrollmentManager {
StrikeDatabase_SettingsPageNotBlocked);
FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
VirtualCardEnrollmentFields_LastShow);
+ FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+ RequiredDelaySinceLastStrike_ExpOn);
+ FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+ RequiredDelaySinceLastStrike_ExpOff);
// Called once the risk data is loaded. The |risk_data| will be used with
// |state_|'s |virtual_card_enrollment_fields|'s |credit_card|'s
@@ -272,6 +286,33 @@ class VirtualCardEnrollmentManager {
const payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails&
response);
+ // Sets the corresponding fields in |state_| from the
+ // GetDetailsForEnrollmentResponseDetails in |response|. This function is used
+ // both when a GetDetailsForEnrollRequest gets a response, and when offering
+ // virtual card enrollment through the optimized upstream flow as the
+ // GetDetailsForEnrollmentResponseDetails is returned in the upload card
+ // response.
+ void SetGetDetailsForEnrollmentResponseDetails(
+ const payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails&
+ response);
+
+ // Should always be called right before showing virtual card enrollment UI.
+ // This function attempts to set the card art image in |state_|, and if the
+ // card art image is not synced yet from the chrome sync server, it will fall
+ // back to the network image.
+ void EnsureCardArtImageIsSetBeforeShowingUI();
+
+ // Helper function that is called any time we offer virtual card enroll.
+ void SetInitialVirtualCardEnrollFields(
+ const CreditCard& credit_card,
+ VirtualCardEnrollmentSource virtual_card_enrollment_source);
+
+ // Returns true if the passed in GetDetailsForEnrollmentResponseDetails is
+ // valid.
+ bool IsValidGetDetailsForEnrollmentResponseDetails(
+ const payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails&
+ get_details_for_enrollment_response_details);
+
// Cancels the entire Virtual Card Enrollment process.
void OnVirtualCardEnrollmentBubbleCancelled();
@@ -316,6 +357,9 @@ class VirtualCardEnrollmentManager {
// metric. |save_card_bubble_accepted_timestamp_| will then be reset.
absl::optional<base::Time> save_card_bubble_accepted_timestamp_;
+ // The timestamp when a GetDetailsForEnrollment request is sent.
+ absl::optional<base::Time> get_details_for_enrollment_request_sent_timestamp_;
+
base::WeakPtrFactory<VirtualCardEnrollmentManager> weak_ptr_factory_{this};
};
diff --git a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
index 83604d9b69f..9630ced0549 100644
--- a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
@@ -10,9 +10,11 @@
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
+#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/data_model/credit_card_art_image.h"
+#include "components/autofill/core/browser/metrics/autofill_metrics.h"
#include "components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h"
#include "components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request.h"
#include "components/autofill/core/browser/payments/payments_util.h"
@@ -125,8 +127,8 @@ class VirtualCardEnrollmentManagerTest : public testing::Test {
bool make_image_present) {
personal_data_manager_->ClearCreditCardArtImages();
SetUpCard();
- auto state = virtual_card_enrollment_manager_
- ->GetVirtualCardEnrollmentProcessState();
+ auto* state = virtual_card_enrollment_manager_
+ ->GetVirtualCardEnrollmentProcessState();
if (make_image_present) {
SetValidCardArtImageForCard(*card_);
} else {
@@ -142,7 +144,7 @@ class VirtualCardEnrollmentManagerTest : public testing::Test {
}
void SetUpStrikeDatabaseTest() {
- raw_ptr<VirtualCardEnrollmentProcessState> state =
+ VirtualCardEnrollmentProcessState* state =
virtual_card_enrollment_manager_
->GetVirtualCardEnrollmentProcessState();
state->vcn_context_token = kTestVcnContextToken;
@@ -151,17 +153,15 @@ class VirtualCardEnrollmentManagerTest : public testing::Test {
personal_data_manager_->SetPaymentsCustomerData(
std::make_unique<PaymentsCustomerData>("123456"));
EXPECT_FALSE(
- virtual_card_enrollment_manager_
- ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
- base::NumberToString(state->virtual_card_enrollment_fields
- .credit_card.instrument_id()),
- VirtualCardEnrollmentSource::kUpstream));
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(state->virtual_card_enrollment_fields
+ .credit_card.instrument_id()),
+ VirtualCardEnrollmentSource::kUpstream));
EXPECT_FALSE(
- virtual_card_enrollment_manager_
- ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
- base::NumberToString(state->virtual_card_enrollment_fields
- .credit_card.instrument_id()),
- VirtualCardEnrollmentSource::kDownstream));
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(state->virtual_card_enrollment_fields
+ .credit_card.instrument_id()),
+ VirtualCardEnrollmentSource::kDownstream));
}
protected:
@@ -181,7 +181,7 @@ class VirtualCardEnrollmentManagerTest : public testing::Test {
std::unique_ptr<CreditCard> card_;
};
-TEST_F(VirtualCardEnrollmentManagerTest, OfferVirtualCardEnroll) {
+TEST_F(VirtualCardEnrollmentManagerTest, InitVirtualCardEnroll) {
for (VirtualCardEnrollmentSource virtual_card_enrollment_source :
{VirtualCardEnrollmentSource::kUpstream,
VirtualCardEnrollmentSource::kDownstream,
@@ -193,8 +193,8 @@ TEST_F(VirtualCardEnrollmentManagerTest, OfferVirtualCardEnroll) {
<< ", make_image_present=" << make_image_present);
personal_data_manager_->ClearCreditCardArtImages();
SetUpCard();
- auto state = virtual_card_enrollment_manager_
- ->GetVirtualCardEnrollmentProcessState();
+ auto* state = virtual_card_enrollment_manager_
+ ->GetVirtualCardEnrollmentProcessState();
state->risk_data.reset();
state->virtual_card_enrollment_fields.card_art_image = nullptr;
if (make_image_present)
@@ -203,8 +203,8 @@ TEST_F(VirtualCardEnrollmentManagerTest, OfferVirtualCardEnroll) {
virtual_card_enrollment_manager_->SetAutofillClient(nullptr);
#endif
- virtual_card_enrollment_manager_->OfferVirtualCardEnroll(
- *card_, virtual_card_enrollment_source,
+ virtual_card_enrollment_manager_->InitVirtualCardEnroll(
+ *card_, virtual_card_enrollment_source, absl::nullopt,
virtual_card_enrollment_manager_->AutofillClientIsPresent()
? user_prefs_
: nullptr,
@@ -221,9 +221,53 @@ TEST_F(VirtualCardEnrollmentManagerTest, OfferVirtualCardEnroll) {
}
}
+TEST_F(VirtualCardEnrollmentManagerTest,
+ InitVirtualCardEnroll_GetDetailsForEnrollmentResponseReceived) {
+ personal_data_manager_->ClearCreditCardArtImages();
+ SetUpCard();
+ auto* state =
+ virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
+ state->risk_data.reset();
+ SetValidCardArtImageForCard(*card_);
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails
+ get_details_for_enrollment_response_details;
+ TestLegalMessageLine google_test_legal_message_line{
+ "google_test_legal_message"};
+ TestLegalMessageLine issuer_test_legal_message_line{
+ "issuer_test_legal_message"};
+ get_details_for_enrollment_response_details.google_legal_message = {
+ google_test_legal_message_line};
+ get_details_for_enrollment_response_details.issuer_legal_message = {
+ issuer_test_legal_message_line};
+ get_details_for_enrollment_response_details.vcn_context_token =
+ "vcn_context_token";
+ absl::optional<
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails>
+ get_details_for_enrollment_response_details_optional =
+ get_details_for_enrollment_response_details;
+ virtual_card_enrollment_manager_->InitVirtualCardEnroll(
+ *card_, VirtualCardEnrollmentSource::kUpstream,
+ get_details_for_enrollment_response_details_optional);
+
+ // CreditCard class overloads equality operator to check that GUIDs,
+ // origins, and the contents of the two cards are equal.
+ EXPECT_EQ(*card_, state->virtual_card_enrollment_fields.credit_card);
+ EXPECT_TRUE(state->virtual_card_enrollment_fields.card_art_image != nullptr);
+ EXPECT_TRUE(state->risk_data.has_value());
+ EXPECT_EQ(google_test_legal_message_line.text(),
+ get_details_for_enrollment_response_details.google_legal_message[0]
+ .text());
+ EXPECT_EQ(issuer_test_legal_message_line.text(),
+ get_details_for_enrollment_response_details.issuer_legal_message[0]
+ .text());
+ EXPECT_TRUE(state->vcn_context_token.has_value());
+ EXPECT_EQ(state->vcn_context_token.value(),
+ get_details_for_enrollment_response_details.vcn_context_token);
+}
+
TEST_F(VirtualCardEnrollmentManagerTest, OnRiskDataLoadedForVirtualCard) {
base::HistogramTester histogram_tester;
- raw_ptr<VirtualCardEnrollmentProcessState> state =
+ VirtualCardEnrollmentProcessState* state =
virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
state->virtual_card_enrollment_fields.virtual_card_enrollment_source =
VirtualCardEnrollmentSource::kUpstream;
@@ -254,6 +298,7 @@ TEST_F(VirtualCardEnrollmentManagerTest, OnRiskDataLoadedForVirtualCard) {
TEST_F(VirtualCardEnrollmentManagerTest, OnDidGetDetailsForEnrollResponse) {
base::HistogramTester histogram_tester;
+ TestAutofillClock test_autofill_clock(AutofillClock::Now());
const TestLegalMessageLine google_legal_message =
TestLegalMessageLine("google_test_legal_message");
const TestLegalMessageLine issuer_legal_message =
@@ -262,12 +307,22 @@ TEST_F(VirtualCardEnrollmentManagerTest, OnDidGetDetailsForEnrollResponse) {
{VirtualCardEnrollmentSource::kUpstream,
VirtualCardEnrollmentSource::kDownstream,
VirtualCardEnrollmentSource::kSettingsPage}) {
+// TODO(crbug.com/1320938): Makes the following test
+// PersonalDataManagerTest.AddUpdateRemoveCreditCards fail on iOS.
+// That other test fails when SetNetworkImageInResourceBundle is called here.
+#if BUILDFLAG(IS_IOS)
+ for (bool make_image_present : {true}) {
+#else
for (bool make_image_present : {true, false}) {
+#endif // BUILDFLAG(IS_IOS)
+ virtual_card_enrollment_manager_
+ ->get_details_for_enrollment_request_sent_timestamp_ =
+ AutofillClock::Now();
payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails
response = std::move(SetUpOnDidGetDetailsForEnrollResponse(
google_legal_message, issuer_legal_message, make_image_present));
- auto state = virtual_card_enrollment_manager_
- ->GetVirtualCardEnrollmentProcessState();
+ auto* state = virtual_card_enrollment_manager_
+ ->GetVirtualCardEnrollmentProcessState();
state->virtual_card_enrollment_fields.virtual_card_enrollment_source =
source;
@@ -279,6 +334,8 @@ TEST_F(VirtualCardEnrollmentManagerTest, OnDidGetDetailsForEnrollResponse) {
network_image);
}
+ test_autofill_clock.Advance(base::Milliseconds(5));
+
virtual_card_enrollment_manager_->OnDidGetDetailsForEnrollResponse(
AutofillClient::PaymentsRpcResult::kSuccess, response);
@@ -307,6 +364,12 @@ TEST_F(VirtualCardEnrollmentManagerTest, OnDidGetDetailsForEnrollResponse) {
"Autofill.VirtualCard.GetDetailsForEnrollment.Result." +
VirtualCardEnrollmentSourceToMetricSuffix(source),
/*sample=*/true, make_image_present ? 1 : 2);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.VirtualCard.GetDetailsForEnrollment.Latency." +
+ VirtualCardEnrollmentSourceToMetricSuffix(source) +
+ PaymentsRpcResultToMetricsSuffix(
+ AutofillClient::PaymentsRpcResult::kSuccess),
+ /*sample=*/5, make_image_present ? 1 : 2);
}
}
}
@@ -322,7 +385,7 @@ TEST_F(VirtualCardEnrollmentManagerTest,
std::move(SetUpOnDidGetDetailsForEnrollResponse(
google_legal_message, issuer_legal_message,
/*make_image_present=*/true));
- auto state =
+ auto* state =
virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
state->virtual_card_enrollment_fields.virtual_card_enrollment_source =
VirtualCardEnrollmentSource::kSettingsPage;
@@ -356,7 +419,7 @@ TEST_F(VirtualCardEnrollmentManagerTest,
TEST_F(VirtualCardEnrollmentManagerTest,
OnDidGetDetailsForEnrollResponse_Reset) {
base::HistogramTester histogram_tester;
- auto state =
+ auto* state =
virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
state->virtual_card_enrollment_fields.virtual_card_enrollment_source =
VirtualCardEnrollmentSource::kSettingsPage;
@@ -391,7 +454,7 @@ TEST_F(VirtualCardEnrollmentManagerTest,
}
TEST_F(VirtualCardEnrollmentManagerTest, Enroll) {
- raw_ptr<VirtualCardEnrollmentProcessState> state =
+ VirtualCardEnrollmentProcessState* state =
virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
state->vcn_context_token = kTestVcnContextToken;
SetUpCard();
@@ -517,39 +580,54 @@ TEST_F(VirtualCardEnrollmentManagerTest, Unenroll) {
#if !BUILDFLAG(IS_ANDROID)
TEST_F(VirtualCardEnrollmentManagerTest, UpstreamAnimationSync_AnimationFirst) {
- personal_data_manager_->ClearCreditCardArtImages();
- SetUpCard();
- SetValidCardArtImageForCard(*card_);
+ for (bool optimized_upstream : {true, false}) {
+ virtual_card_enrollment_manager_->SetBubbleShown(false);
+ virtual_card_enrollment_manager_->SetAvatarAnimationComplete(false);
+ virtual_card_enrollment_manager_->SetEnrollResponseDetailsReceived(false);
- raw_ptr<VirtualCardEnrollmentProcessState> state =
- virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
- state->virtual_card_enrollment_fields.credit_card = *card_;
- state->vcn_context_token = kTestVcnContextToken;
- state->virtual_card_enrollment_fields.virtual_card_enrollment_source =
- VirtualCardEnrollmentSource::kUpstream;
-
- payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails response;
- response.vcn_context_token = kTestVcnContextToken;
- response.issuer_legal_message = {
- TestLegalMessageLine("issuer_test_legal_message_line")};
- response.google_legal_message = {
- TestLegalMessageLine("google_test_legal_message_line")};
+ personal_data_manager_->ClearCreditCardArtImages();
+ SetUpCard();
+ SetValidCardArtImageForCard(*card_);
- // Update avatar animation complete boolean.
- virtual_card_enrollment_manager_->OnCardSavedAnimationComplete();
- EXPECT_TRUE(virtual_card_enrollment_manager_->GetAvatarAnimationComplete());
+ VirtualCardEnrollmentProcessState* state =
+ virtual_card_enrollment_manager_
+ ->GetVirtualCardEnrollmentProcessState();
+ state->virtual_card_enrollment_fields.credit_card = *card_;
+ state->vcn_context_token = kTestVcnContextToken;
+ state->virtual_card_enrollment_fields.virtual_card_enrollment_source =
+ VirtualCardEnrollmentSource::kUpstream;
- // Ensure bubble was not shown yet.
- EXPECT_FALSE(virtual_card_enrollment_manager_->GetBubbleShown());
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails response;
+ response.vcn_context_token = kTestVcnContextToken;
+ response.issuer_legal_message = {
+ TestLegalMessageLine("issuer_test_legal_message_line")};
+ response.google_legal_message = {
+ TestLegalMessageLine("google_test_legal_message_line")};
+
+ absl::optional<
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails>
+ response_optional = response;
+ // Update avatar animation complete boolean.
+ virtual_card_enrollment_manager_->OnCardSavedAnimationComplete();
+ EXPECT_TRUE(virtual_card_enrollment_manager_->GetAvatarAnimationComplete());
+
+ // Ensure bubble was not shown yet.
+ EXPECT_FALSE(virtual_card_enrollment_manager_->GetBubbleShown());
+
+ if (optimized_upstream) {
+ virtual_card_enrollment_manager_->InitVirtualCardEnroll(
+ *card_, VirtualCardEnrollmentSource::kUpstream, response_optional);
+ } else {
+ virtual_card_enrollment_manager_->OnDidGetDetailsForEnrollResponse(
+ AutofillClient::PaymentsRpcResult::kSuccess, response);
+ }
- // Update enrollment response complete boolean.
- virtual_card_enrollment_manager_->OnDidGetDetailsForEnrollResponse(
- AutofillClient::PaymentsRpcResult::kSuccess, response);
- EXPECT_TRUE(
- virtual_card_enrollment_manager_->GetEnrollResponseDetailsReceived());
+ EXPECT_TRUE(
+ virtual_card_enrollment_manager_->GetEnrollResponseDetailsReceived());
- // Ensure bubble was shown.
- EXPECT_TRUE(virtual_card_enrollment_manager_->GetBubbleShown());
+ // Ensure bubble was shown.
+ EXPECT_TRUE(virtual_card_enrollment_manager_->GetBubbleShown());
+ }
}
TEST_F(VirtualCardEnrollmentManagerTest, UpstreamAnimationSync_ResponseFirst) {
@@ -557,36 +635,50 @@ TEST_F(VirtualCardEnrollmentManagerTest, UpstreamAnimationSync_ResponseFirst) {
feature_list.InitWithFeatures({features::kAutofillEnableToolbarStatusChip,
features::kAutofillCreditCardUploadFeedback},
{});
- personal_data_manager_->ClearCreditCardArtImages();
- SetUpCard();
- SetValidCardArtImageForCard(*card_);
+ for (bool optimized_upstream : {true, false}) {
+ virtual_card_enrollment_manager_->Reset();
+ virtual_card_enrollment_manager_->SetBubbleShown(false);
+ virtual_card_enrollment_manager_->SetAvatarAnimationComplete(false);
+ personal_data_manager_->ClearCreditCardArtImages();
+ SetUpCard();
+ SetValidCardArtImageForCard(*card_);
- raw_ptr<VirtualCardEnrollmentProcessState> state =
- virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
- state->virtual_card_enrollment_fields.credit_card = *card_;
- state->vcn_context_token = kTestVcnContextToken;
- state->virtual_card_enrollment_fields.virtual_card_enrollment_source =
- VirtualCardEnrollmentSource::kUpstream;
+ VirtualCardEnrollmentProcessState* state =
+ virtual_card_enrollment_manager_
+ ->GetVirtualCardEnrollmentProcessState();
+ state->virtual_card_enrollment_fields.credit_card = *card_;
+ state->vcn_context_token = kTestVcnContextToken;
+ state->virtual_card_enrollment_fields.virtual_card_enrollment_source =
+ VirtualCardEnrollmentSource::kUpstream;
- payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails response;
- response.vcn_context_token = kTestVcnContextToken;
- response.issuer_legal_message = {
- TestLegalMessageLine("issuer_test_legal_message_line")};
- response.google_legal_message = {
- TestLegalMessageLine("google_test_legal_message_line")};
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails response;
+ response.vcn_context_token = kTestVcnContextToken;
+ response.issuer_legal_message = {
+ TestLegalMessageLine("issuer_test_legal_message_line")};
+ response.google_legal_message = {
+ TestLegalMessageLine("google_test_legal_message_line")};
+
+ absl::optional<
+ payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails>
+ response_optional = response;
+ if (optimized_upstream) {
+ virtual_card_enrollment_manager_->InitVirtualCardEnroll(
+ *card_, VirtualCardEnrollmentSource::kUpstream, response_optional);
+ } else {
+ virtual_card_enrollment_manager_->OnDidGetDetailsForEnrollResponse(
+ AutofillClient::PaymentsRpcResult::kSuccess, response);
+ }
- // Update enrollment response complete boolean.
- virtual_card_enrollment_manager_->OnDidGetDetailsForEnrollResponse(
- AutofillClient::PaymentsRpcResult::kSuccess, response);
- EXPECT_TRUE(
- virtual_card_enrollment_manager_->GetEnrollResponseDetailsReceived());
+ EXPECT_TRUE(
+ virtual_card_enrollment_manager_->GetEnrollResponseDetailsReceived());
- // Ensure bubble was not shown yet.
- EXPECT_FALSE(virtual_card_enrollment_manager_->GetBubbleShown());
+ // Ensure bubble was not shown yet.
+ EXPECT_FALSE(virtual_card_enrollment_manager_->GetBubbleShown());
- // Update avatar animation complete boolean.
- virtual_card_enrollment_manager_->OnCardSavedAnimationComplete();
- EXPECT_TRUE(virtual_card_enrollment_manager_->GetAvatarAnimationComplete());
+ // Update avatar animation complete boolean.
+ virtual_card_enrollment_manager_->OnCardSavedAnimationComplete();
+ EXPECT_TRUE(virtual_card_enrollment_manager_->GetAvatarAnimationComplete());
+ }
}
#endif // !BUILDFLAG(IS_ANDROID)
@@ -595,10 +687,14 @@ TEST_F(VirtualCardEnrollmentManagerTest, StrikeDatabase_BubbleAccepted) {
base::HistogramTester histogram_tester;
SetUpStrikeDatabaseTest();
- raw_ptr<VirtualCardEnrollmentProcessState> state =
+ VirtualCardEnrollmentProcessState* state =
virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
// Reject the bubble and log strike.
virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.StrikeDatabase.NthStrikeAdded.VirtualCardEnrollment",
+ /*sample=*/1, /*count=*/1);
EXPECT_EQ(
virtual_card_enrollment_manager_->GetVirtualCardEnrollmentStrikeDatabase()
->GetStrikes(
@@ -616,6 +712,8 @@ TEST_F(VirtualCardEnrollmentManagerTest, StrikeDatabase_BubbleAccepted) {
0);
histogram_tester.ExpectBucketCount(
+ "Autofill.StrikeDatabase.StrikesPresentWhenVirtualCardEnrolled", 1, 1);
+ histogram_tester.ExpectBucketCount(
"Autofill.VirtualCardEnrollmentStrikeDatabase." +
VirtualCardEnrollmentSourceToMetricSuffix(
state->virtual_card_enrollment_fields
@@ -632,7 +730,11 @@ TEST_F(VirtualCardEnrollmentManagerTest, StrikeDatabase_BubbleCanceled) {
// Reject the bubble and log strike.
virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
- raw_ptr<VirtualCardEnrollmentProcessState> state =
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.StrikeDatabase.NthStrikeAdded.VirtualCardEnrollment",
+ /*sample=*/1, /*count=*/1);
+
+ VirtualCardEnrollmentProcessState* state =
virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
// Ensure a strike has been logged.
EXPECT_EQ(
@@ -662,44 +764,58 @@ TEST_F(VirtualCardEnrollmentManagerTest, StrikeDatabase_BubbleBlocked) {
i++) {
// Reject the bubble and log strike.
virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+
+ histogram_tester.ExpectBucketCount(
+ "Autofill.StrikeDatabase.NthStrikeAdded.VirtualCardEnrollment",
+ /*sample=*/i + 1, /*count=*/1);
+
for (VirtualCardEnrollmentSource source :
{VirtualCardEnrollmentSource::kUpstream,
VirtualCardEnrollmentSource::kDownstream}) {
histogram_tester.ExpectBucketCount(
"Autofill.VirtualCardEnrollBubble.MaxStrikesLimitReached", source, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.StrikeDatabase."
+ "VirtualCardEnrollmentNotOfferedDueToMaxStrikes",
+ source, 0);
}
}
for (VirtualCardEnrollmentSource source :
{VirtualCardEnrollmentSource::kUpstream,
VirtualCardEnrollmentSource::kDownstream}) {
- virtual_card_enrollment_manager_->OfferVirtualCardEnroll(
- *card_, source,
+ virtual_card_enrollment_manager_->InitVirtualCardEnroll(
+ *card_, source, absl::nullopt,
virtual_card_enrollment_manager_->AutofillClientIsPresent()
? user_prefs_
: nullptr,
base::DoNothing());
histogram_tester.ExpectBucketCount(
"Autofill.VirtualCardEnrollBubble.MaxStrikesLimitReached", source, 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.StrikeDatabase."
+ "VirtualCardEnrollmentNotOfferedDueToMaxStrikes",
+ source, 1);
}
- raw_ptr<VirtualCardEnrollmentProcessState> state =
+ VirtualCardEnrollmentProcessState* state =
virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
- EXPECT_TRUE(virtual_card_enrollment_manager_
- ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
- base::NumberToString(state->virtual_card_enrollment_fields
- .credit_card.instrument_id()),
- VirtualCardEnrollmentSource::kUpstream));
- EXPECT_TRUE(virtual_card_enrollment_manager_
- ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
- base::NumberToString(state->virtual_card_enrollment_fields
- .credit_card.instrument_id()),
- VirtualCardEnrollmentSource::kDownstream));
+ EXPECT_TRUE(
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(state->virtual_card_enrollment_fields.credit_card
+ .instrument_id()),
+ VirtualCardEnrollmentSource::kUpstream));
+ EXPECT_TRUE(
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(state->virtual_card_enrollment_fields.credit_card
+ .instrument_id()),
+ VirtualCardEnrollmentSource::kDownstream));
}
TEST_F(VirtualCardEnrollmentManagerTest,
StrikeDatabase_SettingsPageNotBlocked) {
SetUpStrikeDatabaseTest();
+ base::HistogramTester histogram_tester;
for (int i = 0; i < virtual_card_enrollment_manager_
->GetVirtualCardEnrollmentStrikeDatabase()
@@ -707,24 +823,27 @@ TEST_F(VirtualCardEnrollmentManagerTest,
i++) {
// Reject the bubble and log strike.
virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+
+ histogram_tester.ExpectBucketCount(
+ "Autofill.StrikeDatabase.NthStrikeAdded.VirtualCardEnrollment",
+ /*sample=*/i + 1, /*count=*/1);
}
// Make sure enrollment is not blocked through settings page.
EXPECT_FALSE(
- virtual_card_enrollment_manager_
- ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
- base::NumberToString(virtual_card_enrollment_manager_
- ->GetVirtualCardEnrollmentProcessState()
- ->virtual_card_enrollment_fields
- .credit_card.instrument_id()),
- VirtualCardEnrollmentSource::kSettingsPage));
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(
+ virtual_card_enrollment_manager_
+ ->GetVirtualCardEnrollmentProcessState()
+ ->virtual_card_enrollment_fields.credit_card.instrument_id()),
+ VirtualCardEnrollmentSource::kSettingsPage));
}
// Test to ensure that the |last_show| inside a VirtualCardEnrollmentFields is
// set correctly.
TEST_F(VirtualCardEnrollmentManagerTest, VirtualCardEnrollmentFields_LastShow) {
base::HistogramTester histogram_tester;
- raw_ptr<VirtualCardEnrollmentProcessState> state =
+ VirtualCardEnrollmentProcessState* state =
virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
state->vcn_context_token = kTestVcnContextToken;
SetUpCard();
@@ -750,6 +869,10 @@ TEST_F(VirtualCardEnrollmentManagerTest, VirtualCardEnrollmentFields_LastShow) {
EXPECT_FALSE(state->virtual_card_enrollment_fields.last_show);
// Reject the bubble and log strike.
virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+
+ histogram_tester.ExpectBucketCount(
+ "Autofill.StrikeDatabase.NthStrikeAdded.VirtualCardEnrollment",
+ /*sample=*/i + 1, /*count=*/1);
}
// Show the bubble for the last time and ensures VirtualCardEnrollmentFields
@@ -757,6 +880,107 @@ TEST_F(VirtualCardEnrollmentManagerTest, VirtualCardEnrollmentFields_LastShow) {
virtual_card_enrollment_manager_->ShowVirtualCardEnrollBubble();
EXPECT_TRUE(state->virtual_card_enrollment_fields.last_show);
}
+
+// Test to ensure that the required delay since the last strike is respected
+// before Chrome offers another virtual card enrollment for the card, when the
+// |kAutofillEnforceDelaysInStrikeDatabase| is enabled.
+TEST_F(VirtualCardEnrollmentManagerTest, RequiredDelaySinceLastStrike_ExpOn) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ base::HistogramTester histogram_tester;
+ scoped_feature_list.InitAndEnableFeature(
+ features::kAutofillEnforceDelaysInStrikeDatabase);
+ SetUpStrikeDatabaseTest();
+ TestAutofillClock test_autofill_clock(AutofillClock::Now());
+ VirtualCardEnrollmentProcessState* state =
+ virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
+ SetUpCard();
+ card_->set_instrument_id(11223344);
+ state->virtual_card_enrollment_fields.credit_card = *card_;
+
+ virtual_card_enrollment_manager_->InitVirtualCardEnroll(
+ *card_, VirtualCardEnrollmentSource::kDownstream, absl::nullopt,
+ virtual_card_enrollment_manager_->AutofillClientIsPresent() ? user_prefs_
+ : nullptr,
+ base::DoNothing());
+
+ // Logs one strike for the card and makes sure that the enrollment offer is
+ // blocked.
+ virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.StrikeDatabase.NthStrikeAdded.VirtualCardEnrollment",
+ /*sample=*/1, /*count=*/1);
+ EXPECT_TRUE(
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(card_->instrument_id()),
+ VirtualCardEnrollmentSource::kDownstream));
+
+ // Advances the clock for
+ // |kAutofillVirtualCardEnrollDelayInStrikeDatabaseInDays - 1| days. Verifies
+ // that enrollment should still be blocked.
+ test_autofill_clock.Advance(base::Days(
+ features::kAutofillVirtualCardEnrollDelayInStrikeDatabaseInDays.Get() -
+ 1));
+ EXPECT_TRUE(
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(card_->instrument_id()),
+ VirtualCardEnrollmentSource::kDownstream));
+
+ // Makes sure that enrollment offer for another card is not blocked.
+ EXPECT_FALSE(
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(55667788),
+ VirtualCardEnrollmentSource::kDownstream));
+
+ // Advances the clock for another days. Verifies that enrollment should not
+ // be blocked.
+ test_autofill_clock.Advance(base::Days(1));
+ EXPECT_FALSE(
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(card_->instrument_id()),
+ VirtualCardEnrollmentSource::kDownstream));
+ histogram_tester.ExpectBucketCount(
+ "Autofill.StrikeDatabase."
+ "VirtualCardEnrollmentNotOfferedDueToRequiredDelay",
+ VirtualCardEnrollmentSource::kDownstream, 2);
+}
+
+// Test to ensure that the required delay since last strike is respected before
+// Chrome offers another virtual card enrollment for the card, when the
+// |kAutofillEnforceDelaysInStrikeDatabase| is disabled.
+TEST_F(VirtualCardEnrollmentManagerTest, RequiredDelaySinceLastStrike_ExpOff) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ base::HistogramTester histogram_tester;
+ scoped_feature_list.InitAndDisableFeature(
+ features::kAutofillEnforceDelaysInStrikeDatabase);
+ SetUpStrikeDatabaseTest();
+ TestAutofillClock test_autofill_clock;
+ test_autofill_clock.SetNow(AutofillClock::Now());
+ VirtualCardEnrollmentProcessState* state =
+ virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
+ SetUpCard();
+ card_->set_instrument_id(11223344);
+ state->virtual_card_enrollment_fields.credit_card = *card_;
+
+ virtual_card_enrollment_manager_->InitVirtualCardEnroll(
+ *card_, VirtualCardEnrollmentSource::kDownstream, absl::nullopt,
+ virtual_card_enrollment_manager_->AutofillClientIsPresent() ? user_prefs_
+ : nullptr,
+ base::DoNothing());
+
+ // Logs one strike for the card and makes sure that the enrollment offer is
+ // not blocked.
+ virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.StrikeDatabase.NthStrikeAdded.VirtualCardEnrollment",
+ /*sample=*/1, /*count=*/1);
+ EXPECT_FALSE(
+ virtual_card_enrollment_manager_->ShouldBlockVirtualCardEnrollment(
+ base::NumberToString(card_->instrument_id()),
+ VirtualCardEnrollmentSource::kDownstream));
+}
+
#endif // !BUILDFLAG(IS_IOS)
TEST_F(VirtualCardEnrollmentManagerTest, Metrics_LatencySinceUpstream) {
diff --git a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.cc b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.cc
index 9dc1387d6ee..e96a26ab370 100644
--- a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.cc
+++ b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.cc
@@ -4,6 +4,7 @@
#include "components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.h"
+#include "base/feature_list.h"
#include "components/autofill/core/browser/proto/strike_data.pb.h"
#include "components/autofill/core/common/autofill_payments_features.h"
@@ -68,4 +69,15 @@ bool VirtualCardEnrollmentStrikeDatabase::UniqueIdsRequired() const {
return true;
}
+absl::optional<base::TimeDelta>
+VirtualCardEnrollmentStrikeDatabase::GetRequiredDelaySinceLastStrike() const {
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillEnforceDelaysInStrikeDatabase)) {
+ return absl::optional<base::TimeDelta>(base::Days(
+ features::kAutofillVirtualCardEnrollDelayInStrikeDatabaseInDays.Get()));
+ }
+
+ return absl::nullopt;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.h b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.h
index 7a9d6ac013b..8d0cd932a9d 100644
--- a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.h
+++ b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.h
@@ -34,6 +34,8 @@ class VirtualCardEnrollmentStrikeDatabase
int GetMaxStrikesLimit() const override;
absl::optional<base::TimeDelta> GetExpiryTimeDelta() const override;
bool UniqueIdsRequired() const override;
+ absl::optional<base::TimeDelta> GetRequiredDelaySinceLastStrike()
+ const override;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database_unittest.cc b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database_unittest.cc
index eb4bd56643e..df37bbe36de 100644
--- a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database_unittest.cc
@@ -58,16 +58,16 @@ TEST_F(VirtualCardEnrollmentStrikeDatabaseTest, AddAndRemoveStrikes) {
EXPECT_EQ(strike_database_->GetStrikes(test_guid), 0);
strike_database_->AddStrike(test_guid);
EXPECT_EQ(strike_database_->GetStrikes(test_guid), 1);
- EXPECT_FALSE(strike_database_->IsMaxStrikesLimitReached(test_guid));
+ EXPECT_FALSE(strike_database_->ShouldBlockFeature(test_guid));
strike_database_->AddStrikes(max_strikes - 1, test_guid);
EXPECT_EQ(strike_database_->GetStrikes(test_guid), max_strikes);
EXPECT_EQ(strike_database_->GetMaxStrikesLimit(), max_strikes);
- EXPECT_TRUE(strike_database_->IsMaxStrikesLimitReached(test_guid));
+ EXPECT_TRUE(strike_database_->ShouldBlockFeature(test_guid));
strike_database_->RemoveStrike(test_guid);
EXPECT_EQ(strike_database_->GetStrikes(test_guid), max_strikes - 1);
- EXPECT_FALSE(strike_database_->IsMaxStrikesLimitReached(test_guid));
+ EXPECT_FALSE(strike_database_->ShouldBlockFeature(test_guid));
}
// Test to ensure that IsLastOffer works correctly.
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc
index 79e88c770e6..594176e85cd 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager.cc
@@ -18,6 +18,7 @@
#include "base/containers/contains.h"
#include "base/containers/cxx20_erase.h"
#include "base/feature_list.h"
+#include "base/guid.h"
#include "base/i18n/case_conversion.h"
#include "base/i18n/timezone.h"
#include "base/logging.h"
@@ -45,6 +46,7 @@
#include "components/autofill/core/browser/geo/country_names.h"
#include "components/autofill/core/browser/geo/phone_number_i18n.h"
#include "components/autofill/core/browser/metrics/autofill_metrics.h"
+#include "components/autofill/core/browser/metrics/payments/offers_metrics.h"
#include "components/autofill/core/browser/personal_data_manager_observer.h"
#include "components/autofill/core/browser/ui/autofill_image_fetcher.h"
#include "components/autofill/core/browser/ui/label_formatter.h"
@@ -387,12 +389,18 @@ void PersonalDataManager::OnSyncServiceInitialized(
sync_service && !sync_service_->IsSyncFeatureEnabled());
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
MigrateUserOptedInWalletSyncTransportIfNeeded();
+#endif
}
void PersonalDataManager::OnURLsDeleted(
history::HistoryService* /* history_service */,
const history::DeletionInfo& deletion_info) {
+ for (PersonalDataManagerObserver& observer : observers_) {
+ observer.OnBrowsingHistoryCleared(deletion_info);
+ }
+
if (!deletion_info.is_from_expiration() && deletion_info.IsAllHistory()) {
AutofillDownloadManager::ClearUploadHistory(pref_service_);
}
@@ -549,7 +557,7 @@ void PersonalDataManager::SyncStarted(syncer::ModelType model_type) {
void PersonalDataManager::OnStateChanged(syncer::SyncService* sync_service) {
if (base::FeatureList::IsEnabled(
- autofill::features::kAutofillEnableAccountWalletStorage)) {
+ features::kAutofillEnableAccountWalletStorage)) {
// Use the ephemeral account storage when the user didn't enable the sync
// feature explicitly.
database_helper_->SetUseAccountStorageForServerData(
@@ -587,7 +595,7 @@ bool PersonalDataManager::IsSyncFeatureEnabled() const {
void PersonalDataManager::OnAccountsCookieDeletedByUserAction() {
// Clear all the Sync Transport feature opt-ins.
- ::autofill::prefs::ClearSyncTransportOptIns(pref_service_);
+ prefs::ClearSyncTransportOptIns(pref_service_);
}
AutofillSyncSigninState PersonalDataManager::GetSyncSigninState() const {
@@ -1167,9 +1175,9 @@ PersonalDataManager::GetActiveAutofillPromoCodeOffersForOrigin(
return promo_code_offers_for_origin;
}
-raw_ptr<gfx::Image> PersonalDataManager::GetCreditCardArtImageForUrl(
+gfx::Image* PersonalDataManager::GetCreditCardArtImageForUrl(
const GURL& card_art_url) const {
- raw_ptr<gfx::Image> cached_image = GetCachedCardArtImageForUrl(card_art_url);
+ gfx::Image* cached_image = GetCachedCardArtImageForUrl(card_art_url);
if (cached_image)
return cached_image;
@@ -1177,7 +1185,7 @@ raw_ptr<gfx::Image> PersonalDataManager::GetCreditCardArtImageForUrl(
return nullptr;
}
-raw_ptr<gfx::Image> PersonalDataManager::GetCachedCardArtImageForUrl(
+gfx::Image* PersonalDataManager::GetCachedCardArtImageForUrl(
const GURL& card_art_url) const {
if (!IsAutofillWalletImportEnabled())
return nullptr;
@@ -1189,7 +1197,7 @@ raw_ptr<gfx::Image> PersonalDataManager::GetCachedCardArtImageForUrl(
// If the cache contains the image, return it.
if (images_iterator != credit_card_art_images_.end()) {
- raw_ptr<gfx::Image> image = images_iterator->second.get();
+ gfx::Image* image = images_iterator->second.get();
if (!image->IsEmpty())
return image;
}
@@ -1214,12 +1222,12 @@ std::vector<AutofillProfile*> PersonalDataManager::GetProfilesToSuggest()
std::vector<AutofillProfile*> profiles = GetProfiles();
- // Rank the suggestions by frecency.
+ // Rank the suggestions by ranking score.
const base::Time comparison_time = AutofillClock::Now();
std::sort(
profiles.begin(), profiles.end(),
[comparison_time](const AutofillProfile* a, const AutofillProfile* b) {
- return a->HasGreaterFrecencyThan(b, comparison_time);
+ return a->HasGreaterRankingThan(b, comparison_time);
});
return profiles;
@@ -1267,10 +1275,10 @@ std::vector<Suggestion> PersonalDataManager::GetProfileSuggestions(
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
use_formatter = base::FeatureList::IsEnabled(
- autofill::features::kAutofillUseImprovedLabelDisambiguation);
+ features::kAutofillUseImprovedLabelDisambiguation);
#else
use_formatter = base::FeatureList::IsEnabled(
- autofill::features::kAutofillUseMobileLabelDisambiguation);
+ features::kAutofillUseMobileLabelDisambiguation);
#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
// The formatter stores a constant reference to |unique_matched_profiles|.
@@ -1343,8 +1351,8 @@ const std::vector<CreditCard*> PersonalDataManager::GetCreditCardsToSuggest(
std::make_move_iterator(std::begin(cards_to_dedupe)),
std::make_move_iterator(std::end(cards_to_dedupe)));
- // Rank the cards by frecency (see AutofillDataModel for details). All expired
- // cards should be suggested last, also by frecency.
+ // Rank the cards by ranking score (see AutofillDataModel for details). All
+ // expired cards should be suggested last, also by ranking score.
base::Time comparison_time = AutofillClock::Now();
std::stable_sort(cards_to_suggest.begin(), cards_to_suggest.end(),
[comparison_time](const CreditCard* a, const CreditCard* b) {
@@ -1352,7 +1360,7 @@ const std::vector<CreditCard*> PersonalDataManager::GetCreditCardsToSuggest(
if (a_is_expired != b->IsExpired(comparison_time))
return !a_is_expired;
- return a->HasGreaterFrecencyThan(b, comparison_time);
+ return a->HasGreaterRankingThan(b, comparison_time);
});
return cards_to_suggest;
@@ -1363,15 +1371,15 @@ bool PersonalDataManager::IsAutofillEnabled() const {
}
bool PersonalDataManager::IsAutofillProfileEnabled() const {
- return ::autofill::prefs::IsAutofillProfileEnabled(pref_service_);
+ return prefs::IsAutofillProfileEnabled(pref_service_);
}
bool PersonalDataManager::IsAutofillCreditCardEnabled() const {
- return ::autofill::prefs::IsAutofillCreditCardEnabled(pref_service_);
+ return prefs::IsAutofillCreditCardEnabled(pref_service_);
}
bool PersonalDataManager::IsAutofillWalletImportEnabled() const {
- return ::autofill::prefs::IsPaymentsIntegrationEnabled(pref_service_);
+ return prefs::IsPaymentsIntegrationEnabled(pref_service_);
}
bool PersonalDataManager::ShouldSuggestServerCards() const {
@@ -1563,7 +1571,7 @@ bool PersonalDataManager::IsNewProfileImportBlockedForDomain(
return false;
}
- return GetProfileSaveStrikeDatabase()->IsMaxStrikesLimitReached(url.host());
+ return GetProfileSaveStrikeDatabase()->ShouldBlockFeature(url.host());
}
void PersonalDataManager::AddStrikeToBlockNewProfileImportForDomain(
@@ -1590,7 +1598,7 @@ bool PersonalDataManager::IsProfileUpdateBlocked(
return false;
}
- return GetProfileUpdateStrikeDatabase()->IsMaxStrikesLimitReached(guid);
+ return GetProfileUpdateStrikeDatabase()->ShouldBlockFeature(guid);
}
void PersonalDataManager::AddStrikeToBlockProfileUpdate(
@@ -1878,7 +1886,7 @@ void PersonalDataManager::LogStoredCreditCardMetrics() const {
void PersonalDataManager::LogStoredOfferMetrics() const {
if (!has_logged_stored_offer_metrics_) {
- AutofillMetrics::LogStoredOfferMetrics(autofill_offer_data_);
+ autofill_metrics::LogStoredOfferMetrics(autofill_offer_data_);
// Only log this info once per chrome user profile load.
has_logged_stored_offer_metrics_ = true;
}
@@ -1895,13 +1903,13 @@ std::string PersonalDataManager::MostCommonCountryCodeFromProfiles() const {
const std::vector<AutofillProfile*>& profiles = GetProfiles();
const std::vector<std::string>& country_codes =
CountryDataMap::GetInstance()->country_codes();
- for (size_t i = 0; i < profiles.size(); ++i) {
+ for (auto* profile : profiles) {
std::string country_code = base::ToUpperASCII(
- base::UTF16ToASCII(profiles[i]->GetRawInfo(ADDRESS_HOME_COUNTRY)));
+ base::UTF16ToASCII(profile->GetRawInfo(ADDRESS_HOME_COUNTRY)));
if (base::Contains(country_codes, country_code)) {
// Verified profiles count 100x more than unverified ones.
- votes[country_code] += profiles[i]->IsVerified() ? 100 : 1;
+ votes[country_code] += profile->IsVerified() ? 100 : 1;
}
}
@@ -1958,7 +1966,7 @@ bool PersonalDataManager::IsKnownCard(const CreditCard& credit_card) const {
bool PersonalDataManager::IsServerCard(const CreditCard* credit_card) const {
// Check whether the current card itself is a server card.
- if (credit_card->record_type() != autofill::CreditCard::LOCAL_CARD)
+ if (credit_card->record_type() != CreditCard::LOCAL_CARD)
return true;
std::vector<CreditCard*> server_credit_cards = GetServerCreditCards();
@@ -2256,6 +2264,7 @@ bool PersonalDataManager::HasPendingQueries() {
pending_offer_data_query_ != 0;
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
void PersonalDataManager::MigrateUserOptedInWalletSyncTransportIfNeeded() {
if (!sync_service_)
return;
@@ -2317,6 +2326,7 @@ void PersonalDataManager::MigrateUserOptedInWalletSyncTransportIfNeeded() {
prefs::SetUserOptedInWalletSyncTransport(pref_service_, primary_account_id,
/*opted_in=*/true);
}
+#endif
bool PersonalDataManager::IsSyncEnabledFor(syncer::ModelType model_type) {
return sync_service_ != nullptr && sync_service_->CanSyncFeatureStart() &&
@@ -2329,20 +2339,16 @@ scoped_refptr<AutofillWebDataService> PersonalDataManager::GetLocalDatabase() {
}
void PersonalDataManager::OnServerCreditCardsRefreshed() {
- ProcessVirtualCardMetadataChanges();
+ ProcessCardArtUrlChanges();
}
-void PersonalDataManager::ProcessVirtualCardMetadataChanges() {
+void PersonalDataManager::ProcessCardArtUrlChanges() {
std::vector<GURL> updated_urls;
for (auto& card : server_credit_cards_) {
- // If this card is not enrolled for virtual cards or the url is not valid,
- // continue.
- if (card->virtual_card_enrollment_state() != CreditCard::ENROLLED ||
- !card->card_art_url().is_valid()) {
+ if (!card->card_art_url().is_valid())
continue;
- }
- // Otherwise try to find the old entry with the same url.
+ // Try to find the old entry with the same url.
auto it = credit_card_art_images_.find(card->card_art_url());
// No existing entry found.
if (it == credit_card_art_images_.end())
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h
index 018972cf59b..c11ab0ed653 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.h
+++ b/chromium/components/autofill/core/browser/personal_data_manager.h
@@ -296,7 +296,7 @@ class PersonalDataManager : public KeyedService,
GetActiveAutofillPromoCodeOffersForOrigin(GURL origin) const;
// Returns the customized credit card art image for the |card_art_url|.
- virtual raw_ptr<gfx::Image> GetCreditCardArtImageForUrl(
+ virtual gfx::Image* GetCreditCardArtImageForUrl(
const GURL& card_art_url) const;
// Returns the cached card art image for the |card_art_url| if it was synced
@@ -305,8 +305,7 @@ class PersonalDataManager : public KeyedService,
// optimization for situations where a separate fetch request after trying to
// retrieve local card art images is not needed. If the card art image is not
// present in the cache, this function will return a nullptr.
- raw_ptr<gfx::Image> GetCachedCardArtImageForUrl(
- const GURL& card_art_url) const;
+ gfx::Image* GetCachedCardArtImageForUrl(const GURL& card_art_url) const;
// Returns the profiles to suggest to the user, ordered by frecency.
std::vector<AutofillProfile*> GetProfilesToSuggest() const;
@@ -558,7 +557,7 @@ class PersonalDataManager : public KeyedService,
friend class autofill::PersonalDataManagerCleaner;
friend class autofill::PersonalDataManagerFactory;
friend class AutofillMetricsTest;
- friend class FormDataImporterTest;
+ friend class FormDataImporterTestBase;
friend class PersonalDataManagerTest;
friend class PersonalDataManagerTestBase;
friend class PersonalDataManagerHelper;
@@ -781,9 +780,11 @@ class PersonalDataManager : public KeyedService,
// Returns if there are any pending queries to the web database.
bool HasPendingQueries();
+#if BUILDFLAG(IS_CHROMEOS_ASH)
// Migrates the user opted in to wallet sync transport. This is needed while
// migrating from using email to Gaia ID as th account identifier.
void MigrateUserOptedInWalletSyncTransportIfNeeded();
+#endif
// Returns true if the sync is enabled for |model_type|.
bool IsSyncEnabledFor(syncer::ModelType model_type);
@@ -794,9 +795,9 @@ class PersonalDataManager : public KeyedService,
// Invoked when server credit card cache is refreshed.
void OnServerCreditCardsRefreshed();
- // Checks whether any virtual card metadata for server cards is new and makes
- // corresponding changes.
- void ProcessVirtualCardMetadataChanges();
+ // Checks whether any new card art url is synced. If so, attempt to fetch the
+ // image based on the url.
+ void ProcessCardArtUrlChanges();
// Returns the number of server credit cards that have a valid credit card art
// image.
diff --git a/chromium/components/autofill/core/browser/personal_data_manager_cleaner.cc b/chromium/components/autofill/core/browser/personal_data_manager_cleaner.cc
index ae7772153cd..7accf295c7d 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager_cleaner.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager_cleaner.cc
@@ -100,10 +100,6 @@ void PersonalDataManagerCleaner::SyncStarted(syncer::ModelType model_type) {
}
void PersonalDataManagerCleaner::ApplyAddressFixesAndCleanups() {
- // TODO(crbug.com/1288863): Remove prefs in M102 or above.
- pref_service_->ClearPref(prefs::kAutofillLastVersionValidated);
- pref_service_->ClearPref(prefs::kAutofillProfileValidity);
-
// One-time fix, otherwise NOP.
RemoveOrphanAutofillTableRows();
@@ -157,7 +153,8 @@ void PersonalDataManagerCleaner::RemoveOrphanAutofillTableRows() {
void PersonalDataManagerCleaner::RemoveInaccessibleProfileValues() {
if (!base::FeatureList::IsEnabled(
- features::kAutofillRemoveInaccessibleProfileValues)) {
+ features::kAutofillRemoveInaccessibleProfileValues) ||
+ !features::kAutofillRemoveInaccessibleProfileValuesOnStartup.Get()) {
return;
}
@@ -247,20 +244,20 @@ void PersonalDataManagerCleaner::DedupeProfiles(
AutofillMetrics::LogNumberOfProfilesConsideredForDedupe(
existing_profiles->size());
- // Sort the profiles by frecency with all the verified profiles at the end.
- // That way the most relevant profiles will get merged into the less relevant
- // profiles, which keeps the syntax of the most relevant profiles data.
- // Verified profiles are put at the end because they do not merge into other
- // profiles, so the loop can be stopped when we reach those. However they need
- // to be in the vector because an unverified profile trying to merge into a
- // similar verified profile will be discarded.
+ // Sort the profiles by ranking score with all the verified profiles at the
+ // end. That way the most relevant profiles will get merged into the less
+ // relevant profiles, which keeps the syntax of the most relevant profiles
+ // data. Verified profiles are put at the end because they do not merge into
+ // other profiles, so the loop can be stopped when we reach those. However
+ // they need to be in the vector because an unverified profile trying to merge
+ // into a similar verified profile will be discarded.
base::Time comparison_time = AutofillClock::Now();
std::sort(existing_profiles->begin(), existing_profiles->end(),
[comparison_time](const std::unique_ptr<AutofillProfile>& a,
const std::unique_ptr<AutofillProfile>& b) {
if (a->IsVerified() != b->IsVerified())
return !a->IsVerified();
- return a->HasGreaterFrecencyThan(b.get(), comparison_time);
+ return a->HasGreaterRankingThan(b.get(), comparison_time);
});
AutofillProfileComparator comparator(personal_data_manager_->app_locale());
@@ -336,7 +333,7 @@ void PersonalDataManagerCleaner::UpdateCardsBillingAddressReference(
for (auto* credit_card : personal_data_manager_->GetCreditCards()) {
// If the credit card is not associated with a billing address, skip it.
if (credit_card->billing_address_id().empty())
- break;
+ continue;
// If the billing address profile associated with the card has been merged,
// replace it by the id of the profile in which it was merged. Repeat the
diff --git a/chromium/components/autofill/core/browser/personal_data_manager_cleaner.h b/chromium/components/autofill/core/browser/personal_data_manager_cleaner.h
index 87607607534..0ee2a297fba 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager_cleaner.h
+++ b/chromium/components/autofill/core/browser/personal_data_manager_cleaner.h
@@ -85,6 +85,12 @@ class PersonalDataManagerCleaner {
void ClearCreditCardNonSettingsOriginsForTesting() {
ClearCreditCardNonSettingsOrigins();
}
+
+ // Getter for |alternative_state_name_map_updater_| used for testing purposes.
+ AlternativeStateNameMapUpdater*
+ alternative_state_name_map_updater_for_testing() {
+ return alternative_state_name_map_updater_;
+ }
#endif // defined(UNIT_TEST)
private:
diff --git a/chromium/components/autofill/core/browser/personal_data_manager_observer.h b/chromium/components/autofill/core/browser/personal_data_manager_observer.h
index 4a40dbba366..df350a2e305 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager_observer.h
+++ b/chromium/components/autofill/core/browser/personal_data_manager_observer.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_OBSERVER_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_OBSERVER_H_
+#include "components/history/core/browser/history_types.h"
+
namespace autofill {
// An interface the PersonalDataManager uses to notify its clients (observers)
@@ -27,6 +29,10 @@ class PersonalDataManagerObserver {
virtual void OnCreditCardSaved(bool should_show_sign_in_promo_if_applicable) {
}
+ // Called when (part of) the browsing history is cleared.
+ virtual void OnBrowsingHistoryCleared(
+ const history::DeletionInfo& deletion_info) {}
+
protected:
virtual ~PersonalDataManagerObserver() {}
};
diff --git a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
index 30c57d01ed9..c0a220df582 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -48,6 +48,7 @@
#include "components/autofill/core/browser/test_autofill_clock.h"
#include "components/autofill/core/browser/test_inmemory_strike_database.h"
#include "components/autofill/core/browser/ui/label_formatter_utils.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
#include "components/autofill/core/browser/ui/suggestion_selection.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
@@ -575,21 +576,18 @@ class PersonalDataManagerTest : public PersonalDataManagerHelper,
void TearDown() override { TearDownTest(); }
};
+#if BUILDFLAG(IS_CHROMEOS_ASH)
class PersonalDataManagerMigrationTest : public PersonalDataManagerHelper,
public testing::Test {
public:
PersonalDataManagerMigrationTest()
- : PersonalDataManagerHelper(
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- { ::switches::kAccountIdMigration }
-#endif
- ) {
- }
+ : PersonalDataManagerHelper({::switches::kAccountIdMigration}) {}
protected:
void SetUp() override { SetUpTest(); }
void TearDown() override { TearDownTest(); }
};
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
class PersonalDataManagerMockTest : public PersonalDataManagerTestBase,
public testing::Test {
@@ -1191,9 +1189,8 @@ TEST_F(PersonalDataManagerTest, AddAndGetCreditCardArtImage) {
personal_data_->OnCardArtImagesFetched(std::move(images));
- raw_ptr<gfx::Image> actual_image =
- personal_data_->GetCreditCardArtImageForUrl(
- GURL("https://www.example.com"));
+ gfx::Image* actual_image = personal_data_->GetCreditCardArtImageForUrl(
+ GURL("https://www.example.com"));
ASSERT_TRUE(actual_image);
EXPECT_TRUE(gfx::test::AreImagesEqual(expected_image, *actual_image));
@@ -1201,23 +1198,18 @@ TEST_F(PersonalDataManagerTest, AddAndGetCreditCardArtImage) {
// and checking that PersonalDataManager::FetchImagesForUrls() does not get
// triggered when PersonalDataManager::GetCachedCardArtImageForUrl() is
// called.
- raw_ptr<gfx::Image> cached_image =
- personal_data_->GetCachedCardArtImageForUrl(
- GURL("https://www.example.com"));
+ gfx::Image* cached_image = personal_data_->GetCachedCardArtImageForUrl(
+ GURL("https://www.example.com"));
ASSERT_TRUE(cached_image);
EXPECT_TRUE(gfx::test::AreImagesEqual(expected_image, *cached_image));
}
-TEST_F(PersonalDataManagerMockTest, ProcessVirtualCardMetadataChanges) {
+TEST_F(PersonalDataManagerMockTest, ProcessCardArtUrlChanges) {
CreditCard card = test::GetFullServerCard();
- card.set_virtual_card_enrollment_state(
- CreditCard::VirtualCardEnrollmentState::UNENROLLED);
card.set_server_id("card_server_id");
personal_data_->AddFullServerCreditCard(card);
WaitForOnPersonalDataChanged();
- card.set_virtual_card_enrollment_state(
- CreditCard::VirtualCardEnrollmentState::ENROLLED);
card.set_server_id("card_server_id");
card.set_card_art_url(GURL("https://www.example.com/card1"));
std::vector<GURL> updated_urls;
@@ -2138,7 +2130,7 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions) {
std::vector<ServerFieldType>());
ASSERT_FALSE(suggestions.empty());
EXPECT_EQ(u"123 Zoo St., Second Line, Third line, unit 5",
- suggestions[0].value);
+ suggestions[0].main_text.value);
}
TEST_F(PersonalDataManagerTest,
@@ -2159,7 +2151,7 @@ TEST_F(PersonalDataManagerTest,
AutofillType(PHONE_HOME_WHOLE_NUMBER), u"234", false,
std::vector<ServerFieldType>());
ASSERT_FALSE(suggestions.empty());
- EXPECT_EQ(u"12345678910", suggestions[0].value);
+ EXPECT_EQ(u"12345678910", suggestions[0].main_text.value);
}
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
@@ -2181,7 +2173,7 @@ TEST_F(PersonalDataManagerTest,
AutofillType(PHONE_HOME_WHOLE_NUMBER), u"234", false,
std::vector<ServerFieldType>());
ASSERT_FALSE(suggestions.empty());
- EXPECT_EQ(u"(234) 567-8910", suggestions[0].value);
+ EXPECT_EQ(u"(234) 567-8910", suggestions[0].main_text.value);
}
#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
@@ -2265,7 +2257,7 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_ProfilesLimit) {
.c_str(),
"unit 5", "Hollywood", "CA", "91601", "US", "12345678910");
- // Set frecency such that they appear before the "last" profile (added
+ // Set ranking score such that they appear before the "last" profile (added
// next).
profile.set_use_count(12);
profile.set_use_date(AutofillClock::Now() - base::Days(1));
@@ -2292,14 +2284,14 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_ProfilesLimit) {
ASSERT_EQ(suggestion_selection::kMaxSuggestedProfilesCount + 1,
personal_data_->GetProfiles().size());
ASSERT_EQ(1U, suggestions.size());
- EXPECT_EQ(u"Marion", suggestions[0].value);
+ EXPECT_EQ(u"Marion", suggestions[0].main_text.value);
}
-// Tests that GetProfileSuggestions orders its suggestions based on the frecency
+// Tests that GetProfileSuggestions orders its suggestions based on the ranking
// formula.
TEST_F(PersonalDataManagerTest, GetProfileSuggestions_Ranking) {
// Set up the profiles. They are named with number suffixes X so the X is the
- // order in which they should be ordered by frecency.
+ // order in which they should be ordered by the ranking formula.
AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile3, "Marion3", "Mitchell", "Morrison",
"johnwayne@me.xyz", "Fox",
@@ -2331,9 +2323,9 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_Ranking) {
std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
AutofillType(NAME_FIRST), u"Ma", false, std::vector<ServerFieldType>());
ASSERT_EQ(3U, suggestions.size());
- EXPECT_EQ(suggestions[0].value, u"Marion1");
- EXPECT_EQ(suggestions[1].value, u"Marion2");
- EXPECT_EQ(suggestions[2].value, u"Marion3");
+ EXPECT_EQ(suggestions[0].main_text.value, u"Marion1");
+ EXPECT_EQ(suggestions[1].main_text.value, u"Marion2");
+ EXPECT_EQ(suggestions[2].main_text.value, u"Marion3");
}
// Tests that GetProfileSuggestions returns all profiles suggestions.
@@ -2415,7 +2407,7 @@ TEST_F(PersonalDataManagerTest,
std::vector<ServerFieldType>());
ASSERT_EQ(1U, suggestions.size());
EXPECT_EQ(u"123 Zoo St., Second Line, Third line, unit 5",
- suggestions[0].value);
+ suggestions[0].main_text.value);
}
// Query with prefix for profile2 returns profile2.
@@ -2425,7 +2417,7 @@ TEST_F(PersonalDataManagerTest,
std::vector<ServerFieldType>());
EXPECT_EQ(1U, suggestions.size());
EXPECT_EQ(u"456 Zoo St., Second Line, Third line, unit 5",
- suggestions[0].value);
+ suggestions[0].main_text.value);
}
}
@@ -2563,7 +2555,9 @@ TEST_F(PersonalDataManagerTest,
AutofillType(NAME_FIRST), std::u16string(), false,
std::vector<ServerFieldType>{NAME_FIRST, NAME_LAST, EMAIL_ADDRESS,
PHONE_HOME_WHOLE_NUMBER}),
- ElementsAre(testing::Field(&Suggestion::value, u"Hoa")));
+ ElementsAre(testing::Field(
+ &Suggestion::main_text,
+ Suggestion::Text(u"Hoa", Suggestion::Text::IsPrimary(true)))));
histogram_tester.ExpectUniqueSample(
"Autofill.ProfileSuggestionsMadeWithFormatter", true, 1);
}
@@ -2694,6 +2688,12 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_FormWithOneProfile) {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
TEST_F(PersonalDataManagerTest,
GetProfileSuggestions_AddressContactFormWithProfiles) {
+ base::test::ScopedFeatureList scoped_features;
+ scoped_features.InitWithFeatures(
+ /*enabled_features=*/{features::kAutofillEnableRankingFormula,
+ features::kAutofillUseImprovedLabelDisambiguation},
+ /*disabled_features=*/{});
+
AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile1, "Hoa", "", "Pham", "hoa.pham@comcast.net", "",
"401 Merrimack St", "", "Lowell", "MA", "01852", "US",
@@ -2714,15 +2714,11 @@ TEST_F(PersonalDataManagerTest,
profile2.set_use_date(AutofillClock::Now() - base::Days(10));
profile2.set_use_count(1);
- EXPECT_TRUE(profile1.HasGreaterFrecencyThan(&profile2, AutofillClock::Now()));
+ EXPECT_TRUE(profile1.HasGreaterRankingThan(&profile2, AutofillClock::Now()));
AddProfileToPersonalDataManager(profile1);
AddProfileToPersonalDataManager(profile2);
- base::test::ScopedFeatureList scoped_features;
- scoped_features.InitAndEnableFeature(
- features::kAutofillUseImprovedLabelDisambiguation);
-
EXPECT_THAT(
personal_data_->GetProfileSuggestions(
AutofillType(NAME_FULL), std::u16string(), false,
@@ -3963,8 +3959,8 @@ INSTANTIATE_TEST_SUITE_P(
{{COMPANY_NAME, u"Stark Inc."}}})));
// Tests that MergeProfile tries to merge the imported profile into the
-// existing profile in decreasing order of frecency.
-TEST_F(PersonalDataManagerTest, MergeProfile_Frecency) {
+// existing profile in decreasing order of ranking score.
+TEST_F(PersonalDataManagerTest, MergeProfile_Ranking) {
// Create two very similar profiles except with different company names.
std::unique_ptr<AutofillProfile> profile1 = std::make_unique<AutofillProfile>(
base::GenerateGUID(), test::kEmptyOrigin);
@@ -3977,7 +3973,7 @@ TEST_F(PersonalDataManagerTest, MergeProfile_Frecency) {
"homer.simpson@abc.com", "Fox", "742 Evergreen Terrace",
"", "Springfield", "IL", "91601", "US", "12345678910");
- // Give the "Fox" profile a bigger frecency score.
+ // Give the "Fox" profile a larger ranking score.
profile2->set_use_count(15);
// Create the |existing_profiles| vector.
@@ -4062,7 +4058,7 @@ TEST_F(PersonalDataManagerTest, MAYBE_MergeProfile_UsageStats) {
// delete after merging similar profiles.
TEST_F(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete) {
// Create the profile for which to find duplicates. It has the highest
- // frecency.
+ // ranking score.
AutofillProfile* profile1 =
new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(profile1, "Homer", "Jay", "Simpson",
@@ -4096,8 +4092,8 @@ TEST_F(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete) {
profile4->set_use_count(3);
// Create another profile similar to profile1. Since that one has the lowest
- // frecency, the result of the merge should be in this profile at the end of
- // the test.
+ // ranking score, the result of the merge should be in this profile at the end
+ // of the test.
AutofillProfile* profile5 =
new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(profile5, "Homer", "Jay", "Simpson",
@@ -4145,7 +4141,7 @@ TEST_F(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete) {
// id references.
TEST_F(PersonalDataManagerTest, DedupeProfiles_GuidsMergeMap) {
// Create the profile for which to find duplicates. It has the highest
- // frecency.
+ // ranking score.
AutofillProfile* profile1 =
new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(profile1, "Homer", "Jay", "Simpson",
@@ -4179,8 +4175,8 @@ TEST_F(PersonalDataManagerTest, DedupeProfiles_GuidsMergeMap) {
profile4->set_use_count(3);
// Create another profile similar to profile1. Since that one has the lowest
- // frecency, the result of the merge should be in this profile at the end of
- // the test.
+ // ranking score, the result of the merge should be in this profile at the end
+ // of the test.
AutofillProfile* profile5 =
new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(profile5, "Homer", "Jay", "Simpson",
@@ -4233,6 +4229,10 @@ TEST_F(PersonalDataManagerTest, UpdateCardsBillingAddressReference) {
guids_merge_map.insert(std::pair<std::string, std::string>("B", "E"));
guids_merge_map.insert(std::pair<std::string, std::string>("D", "E"));
+ // Create a credit card without a billing address id
+ CreditCard* credit_card0 =
+ new CreditCard(base::GenerateGUID(), test::kEmptyOrigin);
+
// Create cards that use A, D, E and F as their billing address id.
CreditCard* credit_card1 =
new CreditCard(base::GenerateGUID(), test::kEmptyOrigin);
@@ -4249,6 +4249,8 @@ TEST_F(PersonalDataManagerTest, UpdateCardsBillingAddressReference) {
// Add the credit cards to the database.
personal_data_->local_credit_cards_.push_back(
+ std::unique_ptr<CreditCard>(credit_card0));
+ personal_data_->local_credit_cards_.push_back(
std::unique_ptr<CreditCard>(credit_card1));
personal_data_->server_credit_cards_.push_back(
std::unique_ptr<CreditCard>(credit_card2));
@@ -4286,7 +4288,7 @@ TEST_F(PersonalDataManagerTest,
// verifying results.
// Create a set of 3 profiles to be merged together.
- // Create a profile with a higher frecency score.
+ // Create a profile with a higher ranking score.
AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile1, "Homer", "J", "Simpson",
"homer.simpson@abc.com", "", "742. Evergreen Terrace",
@@ -4294,7 +4296,7 @@ TEST_F(PersonalDataManagerTest,
profile1.set_use_count(12);
profile1.set_use_date(AutofillClock::Now() - base::Days(1));
- // Create a profile with a medium frecency score.
+ // Create a profile with a medium ranking score.
AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile2, "Homer", "Jay", "Simpson",
"homer.simpson@abc.com", "", "742 Evergreen Terrace", "",
@@ -4302,7 +4304,7 @@ TEST_F(PersonalDataManagerTest,
profile2.set_use_count(5);
profile2.set_use_date(AutofillClock::Now() - base::Days(3));
- // Create a profile with a lower frecency score.
+ // Create a profile with a lower ranking score.
AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile3, "Homer", "J", "Simpson",
"homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
@@ -4311,7 +4313,7 @@ TEST_F(PersonalDataManagerTest,
profile3.set_use_date(AutofillClock::Now() - base::Days(5));
// Create a set of two profiles to be merged together.
- // Create a profile with a higher frecency score.
+ // Create a profile with a higher ranking score.
AutofillProfile profile4(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile4, "Marge", "B", "Simpson",
"marge.simpson@abc.com", "", "742. Evergreen Terrace",
@@ -4319,7 +4321,7 @@ TEST_F(PersonalDataManagerTest,
profile4.set_use_count(11);
profile4.set_use_date(AutofillClock::Now() - base::Days(1));
- // Create a profile with a lower frecency score.
+ // Create a profile with a lower ranking score.
AutofillProfile profile5(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile5, "Marge", "B", "Simpson",
"marge.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
@@ -4335,7 +4337,7 @@ TEST_F(PersonalDataManagerTest,
profile6.set_use_count(10);
profile6.set_use_date(AutofillClock::Now() - base::Days(1));
- // Add three credit cards. Give them a frecency score so that they are
+ // Add three credit cards. Give them a ranking score so that they are
// suggested in order (1, 2, 3). This will ensure a deterministic order for
// verifying results.
CreditCard credit_card1(base::GenerateGUID(), test::kEmptyOrigin);
@@ -4382,8 +4384,8 @@ TEST_F(PersonalDataManagerTest,
->ApplyDedupingRoutineForTesting());
WaitForOnPersonalDataChanged();
- // Get the profiles and cards sorted by frecency to have a deterministic
- // order.
+ // Get the profiles and cards sorted by their ranking score to have a
+ // deterministic order.
std::vector<AutofillProfile*> profiles =
personal_data_->GetProfilesToSuggest();
std::vector<CreditCard*> credit_cards =
@@ -4412,12 +4414,12 @@ TEST_F(PersonalDataManagerTest,
// Tests that ApplyDedupingRoutine merges the profile values correctly, i.e.
// never lose information and keep the syntax of the profile with the higher
-// frecency score.
+// ranking score.
TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) {
base::test::ScopedFeatureList feature;
feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication);
- // Create a profile with a higher frecency score.
+ // Create a profile with a higher ranking score.
AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile1, "Homer", "J", "Simpson",
"homer.simpson@abc.com", "", "742. Evergreen Terrace",
@@ -4425,7 +4427,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) {
profile1.set_use_count(10);
profile1.set_use_date(AutofillClock::Now() - base::Days(1));
- // Create a profile with a medium frecency score.
+ // Create a profile with a medium ranking score.
AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile2, "Homer", "Jay", "Simpson",
"homer.simpson@abc.com", "", "742 Evergreen Terrace", "",
@@ -4433,7 +4435,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) {
profile2.set_use_count(5);
profile2.set_use_date(AutofillClock::Now() - base::Days(3));
- // Create a profile with a lower frecency score.
+ // Create a profile with a lower ranking score.
AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile3, "Homer", "J", "Simpson",
"homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
@@ -4467,16 +4469,16 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) {
histogram_tester.ExpectUniqueSample(
"Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1);
- // Since profiles with higher frecency scores are merged into profiles with
- // lower frecency scores, the result of the merge should be contained in
- // profile3 since it had a lower frecency score compared to profile1.
+ // Since profiles with higher ranking scores are merged into profiles with
+ // lower ranking scores, the result of the merge should be contained in
+ // profile3 since it had a lower ranking score compared to profile1.
EXPECT_EQ(profile3.guid(), profiles[0]->guid());
// The address syntax that results from the merge should be the one from the
- // imported profile (highest frecency).
+ // imported profile (highest ranking).
EXPECT_EQ(u"742. Evergreen Terrace",
profiles[0]->GetRawInfo(ADDRESS_HOME_LINE1));
// The middle name should be full, even if the profile with the higher
- // frecency only had an initial (no loss of information).
+ // ranking only had an initial (no loss of information).
EXPECT_EQ(u"Jay", profiles[0]->GetRawInfo(NAME_MIDDLE));
// The specified phone number from profile1 should be kept (no loss of
// information).
@@ -4497,12 +4499,12 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) {
// Tests that ApplyDedupingRoutine only keeps the verified profile with its
// original data when deduping with similar profiles, even if it has a higher
-// frecency score.
+// ranking score.
TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) {
base::test::ScopedFeatureList feature;
feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication);
- // Create a verified profile with a higher frecency score.
+ // Create a verified profile with a higher ranking score.
AutofillProfile profile1(base::GenerateGUID(), kSettingsOrigin);
test::SetProfileInfo(
&profile1, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "",
@@ -4512,7 +4514,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) {
profile1.set_use_count(7);
profile1.set_use_date(kMuchLaterTime);
- // Create a similar non verified profile with a medium frecency score.
+ // Create a similar non verified profile with a medium ranking score.
AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile2, "Homer", "J", "Simpson",
"homer.simpson@abc.com", "", "742. Evergreen Terrace",
@@ -4520,7 +4522,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) {
profile2.set_use_count(5);
profile2.set_use_date(kSomeLaterTime);
- // Create a similar non verified profile with a lower frecency score.
+ // Create a similar non verified profile with a lower ranking score.
AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile3, "Homer", "J", "Simpson",
"homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
@@ -4570,12 +4572,12 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) {
// Tests that ApplyDedupingRoutine only keeps the verified profile with its
// original data when deduping with similar profiles, even if it has a lower
-// frecency score.
+// ranking score.
TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) {
base::test::ScopedFeatureList feature;
feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication);
- // Create a profile to dedupe with a higher frecency score.
+ // Create a profile to dedupe with a higher ranking score.
AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile1, "Homer", "J", "Simpson",
"homer.simpson@abc.com", "", "742. Evergreen Terrace",
@@ -4583,7 +4585,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) {
profile1.set_use_count(5);
profile1.set_use_date(kMuchLaterTime);
- // Create a similar non verified profile with a medium frecency score.
+ // Create a similar non verified profile with a medium ranking score.
AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile2, "Homer", "J", "Simpson",
"homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
@@ -4591,7 +4593,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) {
profile2.set_use_count(5);
profile2.set_use_date(kSomeLaterTime);
- // Create a similar verified profile with a lower frecency score.
+ // Create a similar verified profile with a lower ranking score.
AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin);
test::SetProfileInfo(
&profile3, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "",
@@ -4640,7 +4642,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) {
base::test::ScopedFeatureList feature;
feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication);
- // Create a profile to dedupe with a higher frecency score.
+ // Create a profile to dedupe with a higher ranking score.
AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile1, "Homer", "J", "Simpson",
"homer.simpson@abc.com", "", "742. Evergreen Terrace",
@@ -4648,7 +4650,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) {
profile1.set_use_count(5);
profile1.set_use_date(kMuchLaterTime);
- // Create a similar verified profile with a medium frecency score.
+ // Create a similar verified profile with a medium ranking score.
AutofillProfile profile2(base::GenerateGUID(), kSettingsOrigin);
test::SetProfileInfo(
&profile2, "Homer", "J", "Simpson", "homer.simpson@abc.com", "Fox",
@@ -4659,7 +4661,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) {
profile2.set_use_count(5);
profile2.set_use_date(kSomeLaterTime);
- // Create a similar verified profile with a lower frecency score.
+ // Create a similar verified profile with a lower ranking score.
AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin);
test::SetProfileInfo(
&profile3, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "",
@@ -4682,7 +4684,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) {
->ApplyDedupingRoutineForTesting());
WaitForOnPersonalDataChanged();
- // Get the profiles, sorted by frecency to have a deterministic order.
+ // Get the profiles, sorted by ranking to have a deterministic order.
std::vector<AutofillProfile*> profiles =
personal_data_->GetProfilesToSuggest();
@@ -4694,7 +4696,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) {
structured_address::VerificationStatus::kParsed);
// |profile1| should have been discarded because the saved profile with the
- // highest frecency score is verified (|profile2|). Therefore, |profile1|'s
+ // highest ranking score is verified (|profile2|). Therefore, |profile1|'s
// data should not have been merged with |profile2|'s data. Then |profile2|
// should have been compared to |profile3| but they should not have merged
// because both profiles are verified.
@@ -4725,7 +4727,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) {
base::test::ScopedFeatureList feature;
feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication);
- // Create a Homer home profile with a higher frecency score than other Homer
+ // Create a Homer home profile with a higher ranking score than other Homer
// profiles.
AutofillProfile Homer1(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&Homer1, "Homer", "J", "Simpson",
@@ -4734,7 +4736,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) {
Homer1.set_use_count(10);
Homer1.set_use_date(AutofillClock::Now() - base::Days(1));
- // Create a Homer home profile with a medium frecency score compared to other
+ // Create a Homer home profile with a medium ranking score compared to other
// Homer profiles.
AutofillProfile Homer2(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&Homer2, "Homer", "Jay", "Simpson",
@@ -4743,7 +4745,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) {
Homer2.set_use_count(5);
Homer2.set_use_date(AutofillClock::Now() - base::Days(3));
- // Create a Homer home profile with a lower frecency score than other Homer
+ // Create a Homer home profile with a lower ranking score than other Homer
// profiles.
AutofillProfile Homer3(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&Homer3, "Homer", "J", "Simpson",
@@ -4760,7 +4762,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) {
Homer4.set_use_count(3);
Homer4.set_use_date(AutofillClock::Now() - base::Days(5));
- // Create a Marge profile with a lower frecency score that other Marge
+ // Create a Marge profile with a lower ranking score that other Marge
// profiles.
AutofillProfile Marge1(base::GenerateGUID(), kSettingsOrigin);
test::SetProfileInfo(&Marge1, "Marjorie", "J", "Simpson",
@@ -4769,7 +4771,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) {
Marge1.set_use_count(4);
Marge1.set_use_date(AutofillClock::Now() - base::Days(3));
- // Create a verified Marge home profile with a lower frecency score that the
+ // Create a verified Marge home profile with a lower ranking score that the
// other Marge profile.
AutofillProfile Marge2(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&Marge2, "Marjorie", "Jacqueline", "Simpson",
@@ -4807,11 +4809,11 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) {
->ApplyDedupingRoutineForTesting());
WaitForOnPersonalDataChanged();
- // Get the profiles, sorted by frecency to have a deterministic order.
+ // Get the profiles, sorted by ranking score to have a deterministic order.
std::vector<AutofillProfile*> profiles =
personal_data_->GetProfilesToSuggest();
- // The 2 duplicates Homer home profiles with the higher frecency and the
+ // The 2 duplicates Homer home profiles with the higher ranking score and the
// unverified Marge profile should have been deduped.
ASSERT_EQ(4U, profiles.size());
// 7 profiles were considered for dedupe.
@@ -4822,7 +4824,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) {
"Autofill.NumberOfProfilesRemovedDuringDedupe", 3, 1);
// The remaining profiles should be |Homer3|, |Marge1|, |Homer4| and |Barney|
- // in this order of frecency.
+ // in this order of ranking score.
EXPECT_EQ(Homer3.guid(), profiles[0]->guid());
EXPECT_EQ(Marge1.guid(), profiles[1]->guid());
EXPECT_EQ(Homer4.guid(), profiles[2]->guid());
@@ -4830,7 +4832,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) {
// |Homer3|'s data:
// The address should be saved with the syntax of |Homer1| since it has the
- // highest frecency score.
+ // highest ranking score.
EXPECT_EQ(u"742. Evergreen Terrace",
profiles[0]->GetRawInfo(ADDRESS_HOME_LINE1));
// The middle name should be the full version found in |Homer2|,
@@ -4933,11 +4935,13 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_OncePerVersion) {
}
// Tests that settings-inaccessible profile values are removed from every stored
-// profile.
-TEST_F(PersonalDataManagerTest, RemoveInaccessibleProfileValues) {
+// profile on startup.
+TEST_F(PersonalDataManagerTest, RemoveInaccessibleProfileValuesOnStartup) {
base::test::ScopedFeatureList feature;
- feature.InitAndEnableFeature(
- features::kAutofillRemoveInaccessibleProfileValues);
+ feature.InitAndEnableFeatureWithParameters(
+ features::kAutofillRemoveInaccessibleProfileValues,
+ {{features::kAutofillRemoveInaccessibleProfileValuesOnStartup.name,
+ "true"}});
// Add a German and a US profile.
AutofillProfile profile0(base::GenerateGUID(), test::kEmptyOrigin);
@@ -5183,7 +5187,7 @@ TEST_F(PersonalDataManagerTest,
const std::string kServerAddressId("server_address1");
// Add two different profiles, a local and a server one. Set the use stats so
- // the server profile has a higher frecency, to have a predictable ordering to
+ // the server profile has a higher ranking, to have a predictable ordering to
// validate results.
AutofillProfile local_profile(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&local_profile, "Josephine", "Alicia", "Saenz",
@@ -5247,7 +5251,7 @@ TEST_F(PersonalDataManagerTest,
// The conversion should be recorded in the Wallet address.
EXPECT_TRUE(personal_data_->GetServerProfiles().back()->has_converted());
- // Get the profiles, sorted by frecency to have a deterministic order.
+ // Get the profiles, sorted by ranking to have a deterministic order.
std::vector<AutofillProfile*> profiles =
personal_data_->GetProfilesToSuggest();
@@ -5282,7 +5286,7 @@ TEST_F(PersonalDataManagerTest,
const std::string kServerAddressId("server_address1");
// Add two similar profile, a local and a server one. Set the use stats so
- // the server card has a higher frecency, to have a predicatble ordering to
+ // the server card has a higher ranking, to have a predictable ordering to
// validate results.
// Add a local profile.
AutofillProfile local_profile(base::GenerateGUID(), test::kEmptyOrigin);
@@ -5417,7 +5421,7 @@ TEST_F(
const std::string kServerAddressId2("server_address2");
// Add a unique local profile and two similar server profiles. Set the use
- // stats to have a predicatble ordering to validate results.
+ // stats to have a predictable ordering to validate results.
// Add a local profile.
AutofillProfile local_profile(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&local_profile, "Bob", "", "Doe", "", "Fox",
@@ -5497,7 +5501,7 @@ TEST_F(
EXPECT_TRUE(personal_data_->GetServerProfiles()[0]->has_converted());
EXPECT_TRUE(personal_data_->GetServerProfiles()[1]->has_converted());
- // Get the profiles, sorted by frecency to have a deterministic order.
+ // Get the profiles, sorted by ranking to have a deterministic order.
std::vector<AutofillProfile*> profiles =
personal_data_->GetProfilesToSuggest();
@@ -5597,7 +5601,7 @@ TEST_F(
// The conversion should still be recorded in the Wallet address.
EXPECT_TRUE(personal_data_->GetServerProfiles().back()->has_converted());
- // Get the profiles, sorted by frecency to have a deterministic order.
+ // Get the profiles, sorted by ranking score to have a deterministic order.
profiles = personal_data_->GetProfilesToSuggest();
// Make sure that there is still only one profile.
@@ -6684,9 +6688,7 @@ TEST_F(PersonalDataManagerTest, ClearUrlsFromBrowsingHistoryInTimeRange) {
EXPECT_TRUE(personal_data_->IsNewProfileImportBlockedForDomain(second_url));
}
-// On mobile, no dedicated opt-in is required for WalletSyncTransport - the
-// user is always considered opted-in and thus this test doesn't make sense.
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(PersonalDataManagerMigrationTest,
MigrateUserOptedInWalletSyncTransportIfNeeded) {
ASSERT_EQ(
@@ -6705,7 +6707,7 @@ TEST_F(PersonalDataManagerMigrationTest,
EXPECT_TRUE(::autofill::prefs::IsUserOptedInWalletSyncTransport(
prefs_.get(), sync_service_.GetAccountInfo().account_id));
}
-#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) {
diff --git a/chromium/components/autofill/core/browser/rationalization_util.cc b/chromium/components/autofill/core/browser/rationalization_util.cc
index 10fea57a3c8..1d606e4c6ef 100644
--- a/chromium/components/autofill/core/browser/rationalization_util.cc
+++ b/chromium/components/autofill/core/browser/rationalization_util.cc
@@ -49,6 +49,7 @@ void RationalizePhoneNumberFields(
found_number_field_second = field;
phone_number_found = true;
break;
+ case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
case PHONE_HOME_CITY_CODE:
case PHONE_BILLING_CITY_CODE:
if (!found_city_code_field)
@@ -60,6 +61,7 @@ void RationalizePhoneNumberFields(
found_country_code_field = field;
break;
case PHONE_HOME_CITY_AND_NUMBER:
+ case PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX:
case PHONE_BILLING_CITY_AND_NUMBER:
DCHECK(!phone_number_found && !found_city_and_number_field);
found_city_and_number_field = field;
@@ -128,6 +130,7 @@ void RationalizePhoneNumberFields(
field->set_only_fill_when_focused(true);
break;
case PHONE_HOME_CITY_CODE:
+ case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
case PHONE_BILLING_CITY_CODE:
if (field != found_city_code_field)
field->set_only_fill_when_focused(true);
@@ -138,6 +141,7 @@ void RationalizePhoneNumberFields(
field->set_only_fill_when_focused(true);
break;
case PHONE_HOME_CITY_AND_NUMBER:
+ case PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX:
case PHONE_BILLING_CITY_AND_NUMBER:
if (field != found_city_and_number_field)
field->set_only_fill_when_focused(true);
diff --git a/chromium/components/autofill/core/browser/single_field_form_fill_router.cc b/chromium/components/autofill/core/browser/single_field_form_fill_router.cc
index 6c68e52bc61..9bbc8381b4c 100644
--- a/chromium/components/autofill/core/browser/single_field_form_fill_router.cc
+++ b/chromium/components/autofill/core/browser/single_field_form_fill_router.cc
@@ -4,21 +4,54 @@
#include "components/autofill/core/browser/single_field_form_fill_router.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/suggestions_context.h"
+
namespace autofill {
SingleFieldFormFillRouter::SingleFieldFormFillRouter(
- AutocompleteHistoryManager* autocomplete_history_manager) {
- autocomplete_history_manager_ = autocomplete_history_manager->GetWeakPtr();
-
- // Defaults to the |autocomplete_history_manager_| as the current filler upon
- // construction. This is generally the case anyway, but doing so explicitly
- // keeps unit tests happy who don't first call |OnGetSingleFieldSuggestions|.
- // TODO(crbug.com/1245457): Is this the best approach? Or change the tests?
- current_single_field_form_filler_ = autocomplete_history_manager_;
-}
+ AutocompleteHistoryManager* autocomplete_history_manager,
+ MerchantPromoCodeManager* merchant_promo_code_manager)
+ : autocomplete_history_manager_(autocomplete_history_manager->GetWeakPtr()),
+ merchant_promo_code_manager_(
+ merchant_promo_code_manager
+ ? merchant_promo_code_manager->GetWeakPtr()
+ : nullptr) {}
SingleFieldFormFillRouter::~SingleFieldFormFillRouter() = default;
+void SingleFieldFormFillRouter::OnWillSubmitForm(
+ const FormData& form,
+ const FormStructure* form_structure,
+ bool is_autocomplete_enabled) {
+ if (form_structure)
+ DCHECK(form.fields.size() == form_structure->field_count());
+ std::vector<FormFieldData> autocomplete_fields;
+ std::vector<FormFieldData> merchant_promo_code_fields;
+ autocomplete_fields.reserve(form.fields.size());
+ merchant_promo_code_fields.reserve(form.fields.size());
+ for (size_t i = 0; i < form.fields.size(); i++) {
+ // If |form_structure| is present, then the fields in |form_structure| and
+ // the fields in |form| should be 1:1. |form_structure| not being present
+ // indicates we may have fields that were not able to be parsed, so we route
+ // them to autocomplete functionality by default.
+ if (merchant_promo_code_manager_ && form_structure &&
+ form_structure->field(i)->Type().GetStorableType() ==
+ MERCHANT_PROMO_CODE) {
+ merchant_promo_code_fields.push_back(form.fields[i]);
+ } else {
+ autocomplete_fields.push_back(form.fields[i]);
+ }
+ }
+
+ if (merchant_promo_code_manager_) {
+ merchant_promo_code_manager_->OnWillSubmitFormWithFields(
+ merchant_promo_code_fields, is_autocomplete_enabled);
+ }
+ autocomplete_history_manager_->OnWillSubmitFormWithFields(
+ autocomplete_fields, is_autocomplete_enabled);
+}
+
void SingleFieldFormFillRouter::OnGetSingleFieldSuggestions(
int query_id,
bool is_autocomplete_enabled,
@@ -26,39 +59,59 @@ void SingleFieldFormFillRouter::OnGetSingleFieldSuggestions(
const std::u16string& name,
const std::u16string& prefix,
const std::string& form_control_type,
- base::WeakPtr<SingleFieldFormFiller::SuggestionsHandler> handler) {
+ base::WeakPtr<SingleFieldFormFiller::SuggestionsHandler> handler,
+ const SuggestionsContext& context) {
// Retrieving suggestions for a new field; select the appropriate filler.
- // NOTE: All single field form filling is currently handled by Autocomplete.
- // This will soon be extended to merchant promo codes as well.
- current_single_field_form_filler_ = autocomplete_history_manager_;
-
- current_single_field_form_filler_->OnGetSingleFieldSuggestions(
- query_id, is_autocomplete_enabled, autoselect_first_suggestion, name,
- prefix, form_control_type, handler);
+ if (merchant_promo_code_manager_ && context.focused_field &&
+ context.focused_field->Type().GetStorableType() == MERCHANT_PROMO_CODE) {
+ merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+ query_id, is_autocomplete_enabled, autoselect_first_suggestion, name,
+ prefix, form_control_type, handler, context);
+ } else {
+ autocomplete_history_manager_->OnGetSingleFieldSuggestions(
+ query_id, is_autocomplete_enabled, autoselect_first_suggestion, name,
+ prefix, form_control_type, handler, context);
+ }
}
-void SingleFieldFormFillRouter::OnWillSubmitForm(const FormData& form,
- bool is_autocomplete_enabled) {
- current_single_field_form_filler_->OnWillSubmitForm(form,
- is_autocomplete_enabled);
-}
+void SingleFieldFormFillRouter::OnWillSubmitFormWithFields(
+ const std::vector<FormFieldData>& fields,
+ bool is_autocomplete_enabled) {}
void SingleFieldFormFillRouter::CancelPendingQueries(
const SingleFieldFormFiller::SuggestionsHandler* handler) {
- if (current_single_field_form_filler_)
- current_single_field_form_filler_->CancelPendingQueries(handler);
+ if (autocomplete_history_manager_)
+ autocomplete_history_manager_->CancelPendingQueries(handler);
+ if (merchant_promo_code_manager_)
+ merchant_promo_code_manager_->CancelPendingQueries(handler);
}
void SingleFieldFormFillRouter::OnRemoveCurrentSingleFieldSuggestion(
const std::u16string& field_name,
- const std::u16string& value) {
- current_single_field_form_filler_->OnRemoveCurrentSingleFieldSuggestion(
- field_name, value);
+ const std::u16string& value,
+ int frontend_id) {
+ if (merchant_promo_code_manager_ &&
+ frontend_id == POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY) {
+ merchant_promo_code_manager_->OnRemoveCurrentSingleFieldSuggestion(
+ field_name, value, frontend_id);
+ } else {
+ autocomplete_history_manager_->OnRemoveCurrentSingleFieldSuggestion(
+ field_name, value, frontend_id);
+ }
}
void SingleFieldFormFillRouter::OnSingleFieldSuggestionSelected(
- const std::u16string& value) {
- current_single_field_form_filler_->OnSingleFieldSuggestionSelected(value);
+ const std::u16string& value,
+ int frontend_id) {
+ if (merchant_promo_code_manager_ &&
+ (frontend_id == POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY ||
+ frontend_id == POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS)) {
+ merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(value,
+ frontend_id);
+ } else {
+ autocomplete_history_manager_->OnSingleFieldSuggestionSelected(value,
+ frontend_id);
+ }
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/single_field_form_fill_router.h b/chromium/components/autofill/core/browser/single_field_form_fill_router.h
index 75b339bfe4a..28336d2187b 100644
--- a/chromium/components/autofill/core/browser/single_field_form_fill_router.h
+++ b/chromium/components/autofill/core/browser/single_field_form_fill_router.h
@@ -7,22 +7,40 @@
#include "base/memory/weak_ptr.h"
#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/merchant_promo_code_manager.h"
#include "components/autofill/core/browser/single_field_form_filler.h"
#include "components/autofill/core/common/form_data.h"
namespace autofill {
-// Routes single field form filling requests, such as choosing whether to direct
-// them to Autocomplete or merchant promo code filling functionality.
+class FormStructure;
+class MerchantPromoCodeManager;
+struct SuggestionsContext;
+
+// Owned by AutofillClient, and is one per tab. Routes single field form filling
+// requests, such as choosing whether to direct them to Autocomplete or merchant
+// promo code filling functionality.
class SingleFieldFormFillRouter : public SingleFieldFormFiller {
public:
explicit SingleFieldFormFillRouter(
- AutocompleteHistoryManager* autocomplete_history_manager);
+ AutocompleteHistoryManager* autocomplete_history_manager,
+ MerchantPromoCodeManager* merchant_promo_code_manager);
~SingleFieldFormFillRouter() override;
SingleFieldFormFillRouter(const SingleFieldFormFillRouter&) = delete;
SingleFieldFormFillRouter& operator=(const SingleFieldFormFillRouter&) =
delete;
+ // Routes every field in a form to its correct SingleFieldFormFiller, calling
+ // SingleFieldFormFiller::OnWillSubmitFormWithFields() with the vector of
+ // fields for that specific SingleFieldFormFiller. If |form_structure| is not
+ // nullptr, then the fields in |form| and |form_structure| should be 1:1. It
+ // is possible for |form_structure| to be nullptr while |form| has data, which
+ // means there were fields in the form that were not able to be parsed as
+ // autofill fields.
+ virtual void OnWillSubmitForm(const FormData& form,
+ const FormStructure* form_structure,
+ bool is_autocomplete_enabled);
+
// SingleFieldFormFiller overrides:
void OnGetSingleFieldSuggestions(
int query_id,
@@ -31,24 +49,25 @@ class SingleFieldFormFillRouter : public SingleFieldFormFiller {
const std::u16string& name,
const std::u16string& prefix,
const std::string& form_control_type,
- base::WeakPtr<SingleFieldFormFiller::SuggestionsHandler> handler)
- override;
- void OnWillSubmitForm(const FormData& form,
- bool is_autocomplete_enabled) override;
+ base::WeakPtr<SingleFieldFormFiller::SuggestionsHandler> handler,
+ const SuggestionsContext& context) override;
+ void OnWillSubmitFormWithFields(const std::vector<FormFieldData>& fields,
+ bool is_autocomplete_enabled) override;
void CancelPendingQueries(
const SingleFieldFormFiller::SuggestionsHandler* handler) override;
- void OnRemoveCurrentSingleFieldSuggestion(
- const std::u16string& field_name,
- const std::u16string& value) override;
- void OnSingleFieldSuggestionSelected(const std::u16string& value) override;
+ void OnRemoveCurrentSingleFieldSuggestion(const std::u16string& field_name,
+ const std::u16string& value,
+ int frontend_id) override;
+ void OnSingleFieldSuggestionSelected(const std::u16string& value,
+ int frontend_id) override;
private:
- // The SingleFieldFormFiller being used to fill the current field. Reset
- // whenever suggestions are requested for a new field.
- base::WeakPtr<SingleFieldFormFiller> current_single_field_form_filler_;
-
- // Available single field form fillers:
+ // Handles autocompleting single fields.
base::WeakPtr<AutocompleteHistoryManager> autocomplete_history_manager_;
+
+ // Handles autofilling merchant promo code fields (can be null for unsupported
+ // platforms).
+ base::WeakPtr<MerchantPromoCodeManager> merchant_promo_code_manager_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/single_field_form_fill_router_unittest.cc b/chromium/components/autofill/core/browser/single_field_form_fill_router_unittest.cc
new file mode 100644
index 00000000000..f091d5f6d6d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/single_field_form_fill_router_unittest.cc
@@ -0,0 +1,260 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/single_field_form_fill_router.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/mock_autocomplete_history_manager.h"
+#include "components/autofill/core/browser/mock_merchant_promo_code_manager.h"
+#include "components/autofill/core/browser/suggestions_context.h"
+#include "components/autofill/core/browser/test_form_structure.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/autofill/core/browser/webdata/mock_autofill_webdata_service.h"
+#include "components/autofill/core/common/autofill_features.h"
+#include "components/autofill/core/common/autofill_payments_features.h"
+#include "components/autofill/core/common/autofill_prefs.h"
+#include "components/version_info/version_info.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::SaveArg;
+
+namespace autofill {
+
+using FieldPrediction =
+ AutofillQueryResponse::FormSuggestion::FieldSuggestion::FieldPrediction;
+
+namespace {
+class MockSuggestionsHandler
+ : public SingleFieldFormFiller::SuggestionsHandler {
+ public:
+ MockSuggestionsHandler() = default;
+ MockSuggestionsHandler(const MockSuggestionsHandler&) = delete;
+ MockSuggestionsHandler& operator=(const MockSuggestionsHandler&) = delete;
+ ~MockSuggestionsHandler() override = default;
+
+ MOCK_METHOD(void,
+ OnSuggestionsReturned,
+ (int query_id,
+ bool autoselect_first_suggestion,
+ const std::vector<Suggestion>& suggestions),
+ (override));
+
+ base::WeakPtr<MockSuggestionsHandler> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ private:
+ base::WeakPtrFactory<MockSuggestionsHandler> weak_ptr_factory_{this};
+};
+} // namespace
+
+class SingleFieldFormFillRouterTest : public testing::Test {
+ protected:
+ SingleFieldFormFillRouterTest() {
+ prefs_ = test::PrefServiceForTesting();
+
+ // Mock such that we don't trigger the cleanup.
+ prefs_->SetInteger(prefs::kAutocompleteLastVersionRetentionPolicy,
+ CHROME_VERSION_MAJOR);
+ personal_data_manager_ = std::make_unique<TestPersonalDataManager>();
+ web_data_service_ = base::MakeRefCounted<MockAutofillWebDataService>();
+ autocomplete_history_manager_ =
+ std::make_unique<MockAutocompleteHistoryManager>();
+ autocomplete_history_manager_->Init(web_data_service_, prefs_.get(), false);
+ merchant_promo_code_manager_ =
+ std::make_unique<MockMerchantPromoCodeManager>();
+ merchant_promo_code_manager_->Init(personal_data_manager_.get(),
+ /*is_off_the_record=*/false);
+ single_field_form_fill_router_ =
+ std::make_unique<SingleFieldFormFillRouter>(
+ autocomplete_history_manager_.get(),
+ merchant_promo_code_manager_.get());
+ }
+
+ base::test::SingleThreadTaskEnvironment task_environment_;
+ std::unique_ptr<SingleFieldFormFillRouter> single_field_form_fill_router_;
+ std::unique_ptr<TestPersonalDataManager> personal_data_manager_;
+ scoped_refptr<MockAutofillWebDataService> web_data_service_;
+ std::unique_ptr<MockAutocompleteHistoryManager> autocomplete_history_manager_;
+ std::unique_ptr<MockMerchantPromoCodeManager> merchant_promo_code_manager_;
+ std::unique_ptr<PrefService> prefs_;
+};
+
+// Ensure that the router routes to AutocompleteHistoryManager for this
+// OnGetSingleFieldSuggestions call.
+TEST_F(SingleFieldFormFillRouterTest,
+ RouteToAutocompleteHistoryManager_OnGetSingleFieldSuggestions) {
+ auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+
+ EXPECT_CALL(*autocomplete_history_manager_, OnGetSingleFieldSuggestions);
+
+ single_field_form_fill_router_->OnGetSingleFieldSuggestions(
+ /*query_id=*/2, /*is_autocomplete_enabled=*/true,
+ /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name",
+ /*prefix=*/u"SomePrefix",
+ /*form_control_type=*/"SomeType", suggestions_handler->GetWeakPtr(),
+ SuggestionsContext());
+}
+
+// Ensure that the router routes to all SingleFieldFormFillers for this
+// OnWillSubmitForm call.
+TEST_F(SingleFieldFormFillRouterTest,
+ RouteToAllSingleFieldFormFillers_OnWillSubmitForm) {
+ FormData form_data;
+ std::vector<FormFieldData> fields;
+ size_t number_of_fields_for_testing = 3;
+ for (size_t i = 0; i < number_of_fields_for_testing; i++) {
+ fields.emplace_back();
+ }
+
+#if !BUILDFLAG(IS_IOS)
+ for (size_t i = 0; i < number_of_fields_for_testing; i++) {
+ fields.emplace_back();
+ }
+#endif // !BUILDFLAG(IS_IOS)
+
+ form_data.fields = fields;
+ TestFormStructure form_structure{form_data};
+
+ // Set the first |number_of_fields_for_testing| fields to be autocomplete
+ // fields.
+ for (size_t i = 0; i < number_of_fields_for_testing; i++) {
+ form_structure.set_server_field_type_for_testing(i, UNKNOWN_TYPE);
+ }
+
+#if !BUILDFLAG(IS_IOS)
+ // Set the next |number_of_fields_for_testing| fields to be merchant promo
+ // code fields.
+ for (size_t i = number_of_fields_for_testing;
+ i < number_of_fields_for_testing * 2; i++) {
+ form_structure.set_server_field_type_for_testing(i, MERCHANT_PROMO_CODE);
+ }
+#endif // !BUILDFLAG(IS_IOS)
+
+ std::vector<FormFieldData> submitted_autocomplete_fields;
+ bool autocomplete_fields_is_autocomplete_enabled = false;
+ EXPECT_CALL(*autocomplete_history_manager_, OnWillSubmitFormWithFields(_, _))
+ .WillOnce(
+ (DoAll(SaveArg<0>(&submitted_autocomplete_fields),
+ SaveArg<1>(&autocomplete_fields_is_autocomplete_enabled))));
+
+#if !BUILDFLAG(IS_IOS)
+ std::vector<FormFieldData> submitted_merchant_promo_code_fields;
+ bool merchant_promo_code_fields_is_autocomplete_enabled = false;
+ EXPECT_CALL(*merchant_promo_code_manager_, OnWillSubmitFormWithFields(_, _))
+ .WillOnce((DoAll(
+ SaveArg<0>(&submitted_merchant_promo_code_fields),
+ SaveArg<1>(&merchant_promo_code_fields_is_autocomplete_enabled))));
+#endif // !BUILDFLAG(IS_IOS)
+
+ single_field_form_fill_router_->OnWillSubmitForm(
+ form_data, &form_structure, /*is_autocomplete_enabled=*/true);
+
+ EXPECT_TRUE(submitted_autocomplete_fields.size() ==
+ number_of_fields_for_testing);
+ EXPECT_TRUE(autocomplete_fields_is_autocomplete_enabled);
+
+#if !BUILDFLAG(IS_IOS)
+ EXPECT_TRUE(submitted_merchant_promo_code_fields.size() ==
+ number_of_fields_for_testing);
+ EXPECT_TRUE(merchant_promo_code_fields_is_autocomplete_enabled);
+#endif // !BUILDFLAG(IS_IOS)
+}
+
+// Ensure that the router routes to SingleFieldFormFillers for this
+// CancelPendingQueries call.
+TEST_F(SingleFieldFormFillRouterTest,
+ RouteToAllSingleFieldFormFillers_CancelPendingQueries) {
+ auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+
+ EXPECT_CALL(*autocomplete_history_manager_, CancelPendingQueries);
+
+#if !BUILDFLAG(IS_IOS)
+ EXPECT_CALL(*merchant_promo_code_manager_, CancelPendingQueries);
+#endif // !BUILDFLAG(IS_IOS)
+
+ single_field_form_fill_router_->CancelPendingQueries(
+ suggestions_handler.get());
+}
+
+// Ensure that the router routes to AutocompleteHistoryManager for this
+// OnRemoveCurrentSingleFieldSuggestion call.
+TEST_F(SingleFieldFormFillRouterTest,
+ RouteToAutocompleteHistoryManager_OnRemoveCurrentSingleFieldSuggestion) {
+ EXPECT_CALL(*autocomplete_history_manager_,
+ OnRemoveCurrentSingleFieldSuggestion);
+
+ single_field_form_fill_router_->OnRemoveCurrentSingleFieldSuggestion(
+ /*field_name=*/u"Field Name", /*value=*/u"Value",
+ POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
+}
+
+// Ensure that the router routes to AutocompleteHistoryManager for this
+// OnSingleFieldSuggestionSelected call.
+TEST_F(SingleFieldFormFillRouterTest,
+ RouteToAutocompleteHistoryManager_OnSingleFieldSuggestionSelected) {
+ EXPECT_CALL(*autocomplete_history_manager_, OnSingleFieldSuggestionSelected);
+
+ single_field_form_fill_router_->OnSingleFieldSuggestionSelected(
+ /*value=*/u"Value", POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
+}
+
+#if !BUILDFLAG(IS_IOS)
+// Ensure that the router routes to MerchantPromoCodeManager for this
+// OnGetSingleFieldSuggestions call.
+TEST_F(SingleFieldFormFillRouterTest,
+ RouteToMerchantPromoCodeManager_OnGetSingleFieldSuggestions) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitWithFeatures({features::kAutofillFillMerchantPromoCodeFields,
+ features::kAutofillServerTypeTakesPrecedence},
+ {});
+ auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+
+ EXPECT_CALL(*merchant_promo_code_manager_, OnGetSingleFieldSuggestions);
+ std::vector<FieldPrediction> merchant_promo_code_field_predictions;
+ FieldPrediction merchant_promo_code_field_prediction;
+ merchant_promo_code_field_prediction.set_type(MERCHANT_PROMO_CODE);
+ merchant_promo_code_field_predictions.push_back(
+ merchant_promo_code_field_prediction);
+ SuggestionsContext context;
+ AutofillField autofill_field;
+ autofill_field.set_server_predictions(
+ std::move(merchant_promo_code_field_predictions));
+ context.focused_field = &autofill_field;
+ single_field_form_fill_router_->OnGetSingleFieldSuggestions(
+ /*query_id=*/2, /*is_autocomplete_enabled=*/true,
+ /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name",
+ /*prefix=*/u"SomePrefix",
+ /*form_control_type=*/"SomeType", suggestions_handler->GetWeakPtr(),
+ context);
+}
+
+// Ensure that the router routes to MerchantPromoCodeManager for this
+// OnRemoveCurrentSingleFieldSuggestion call.
+TEST_F(SingleFieldFormFillRouterTest,
+ RouteToMerchantPromoCodeManager_OnRemoveCurrentSingleFieldSuggestion) {
+ EXPECT_CALL(*merchant_promo_code_manager_,
+ OnRemoveCurrentSingleFieldSuggestion);
+
+ single_field_form_fill_router_->OnRemoveCurrentSingleFieldSuggestion(
+ /*field_name=*/u"Field Name", /*value=*/u"Value",
+ POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY);
+}
+
+// Ensure that the router routes to MerchantPromoCodeManager for this
+// OnSingleFieldSuggestionSelected call.
+TEST_F(SingleFieldFormFillRouterTest,
+ RouteToMerchantPromoCodeManager_OnSingleFieldSuggestionSelected) {
+ EXPECT_CALL(*merchant_promo_code_manager_, OnSingleFieldSuggestionSelected);
+
+ single_field_form_fill_router_->OnSingleFieldSuggestionSelected(
+ /*value=*/u"Value", POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY);
+}
+#endif // !BUILDFLAG(IS_IOS)
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/single_field_form_filler.cc b/chromium/components/autofill/core/browser/single_field_form_filler.cc
index a62d605cb1d..07075e5e206 100644
--- a/chromium/components/autofill/core/browser/single_field_form_filler.cc
+++ b/chromium/components/autofill/core/browser/single_field_form_filler.cc
@@ -10,4 +10,19 @@ SingleFieldFormFiller::SingleFieldFormFiller() = default;
SingleFieldFormFiller::~SingleFieldFormFiller() = default;
+SingleFieldFormFiller::QueryHandler::QueryHandler(
+ int client_query_id,
+ bool autoselect_first_suggestion,
+ std::u16string prefix,
+ base::WeakPtr<SuggestionsHandler> handler)
+ : client_query_id_(client_query_id),
+ autoselect_first_suggestion_(autoselect_first_suggestion),
+ prefix_(prefix),
+ handler_(std::move(handler)) {}
+
+SingleFieldFormFiller::QueryHandler::QueryHandler(
+ const QueryHandler& original) = default;
+
+SingleFieldFormFiller::QueryHandler::~QueryHandler() = default;
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/single_field_form_filler.h b/chromium/components/autofill/core/browser/single_field_form_filler.h
index c06a0756bad..41414abe32f 100644
--- a/chromium/components/autofill/core/browser/single_field_form_filler.h
+++ b/chromium/components/autofill/core/browser/single_field_form_filler.h
@@ -10,8 +10,10 @@
namespace autofill {
+struct SuggestionsContext;
+
// Interface for form-filling implementations that fill a single field at a
-// time, such as Autocomplete or merchant promo codes.
+// time, such as autocomplete or merchant promo codes.
class SingleFieldFormFiller {
public:
// Interface to be implemented by classes that want to fetch autocomplete
@@ -20,10 +22,9 @@ class SingleFieldFormFiller {
public:
virtual ~SuggestionsHandler() = default;
- // Function that will be called-back once SingleFieldFormFiller gets the
- // corresponding response from the DB.
- // |query_id| is the value given by the implementor when
- // OnGetSingleFieldSuggestions was called (it is not the DB query ID).
+ // Will be called-back once SingleFieldFormFiller gets the corresponding
+ // response from the DB. |query_id| is the value given by the implementor
+ // when OnGetSingleFieldSuggestions was called (it is not the DB query ID).
// |suggestions| is the list of fetched suggestions.
virtual void OnSuggestionsReturned(
int query_id,
@@ -56,29 +57,63 @@ class SingleFieldFormFiller {
const std::u16string& name,
const std::u16string& prefix,
const std::string& form_control_type,
- base::WeakPtr<SuggestionsHandler> handler) = 0;
+ base::WeakPtr<SuggestionsHandler> handler,
+ const SuggestionsContext& context) = 0;
// Runs when a form is going to be submitted. In the case of Autocomplete, it
- // saves the given input from the |form| as new or updated Autocomplete
- // entries, which can then be served in the future as suggestions. This update
- // is dependent on whether we are running in incognito and if Autocomplete is
- // enabled or not.
- virtual void OnWillSubmitForm(const FormData& form,
- bool is_autocomplete_enabled) = 0;
+ // saves the given |fields| that are eligible to be saved as new or updated
+ // Autocomplete entries, which can then be served in the future as
+ // suggestions. This update is dependent on whether we are running in
+ // incognito and if Autocomplete is enabled or not. |fields| can be empty.
+ virtual void OnWillSubmitFormWithFields(
+ const std::vector<FormFieldData>& fields,
+ bool is_autocomplete_enabled) = 0;
// Cancels the currently pending WebDataService queries associated with the
// given |handler|.
virtual void CancelPendingQueries(const SuggestionsHandler* handler) = 0;
// If applicable, removes the currently-selected suggestion from the database.
+ // |frontend_id| is the PopupItemId of the suggestion to be removed.
virtual void OnRemoveCurrentSingleFieldSuggestion(
const std::u16string& field_name,
- const std::u16string& value) = 0;
+ const std::u16string& value,
+ int frontend_id) = 0;
// Invoked when the user selects |value| in the list of suggestions. For
// Autocomplete, this function logs the DaysSinceLastUse of the Autocomplete
- // entry associated with |value|.
- virtual void OnSingleFieldSuggestionSelected(const std::u16string& value) = 0;
+ // entry associated with |value|. |frontend_id| is the PopupItemId of the
+ // suggestion selected.
+ virtual void OnSingleFieldSuggestionSelected(const std::u16string& value,
+ int frontend_id) = 0;
+
+ protected:
+ // Internal data object used to keep a request's context to associate it
+ // with the appropriate response.
+ struct QueryHandler {
+ QueryHandler(int client_query_id,
+ bool autoselect_first_suggestion,
+ std::u16string prefix,
+ base::WeakPtr<SuggestionsHandler> handler);
+ QueryHandler(const QueryHandler& original);
+ ~QueryHandler();
+
+ // Query ID living in the handler's scope, which is NOT the same as the
+ // database query ID. This ID is unique per frame, but not per profile.
+ int client_query_id_;
+
+ // Determines whether we should auto-select the first suggestion when
+ // returning. This value was given by the handler when requesting
+ // suggestions.
+ bool autoselect_first_suggestion_;
+
+ // Prefix used to search suggestions, submitted by the handler.
+ std::u16string prefix_;
+
+ // Weak pointer to the handler instance which will be called-back when
+ // we get the response for the associate query.
+ base::WeakPtr<SuggestionsHandler> handler_;
+ };
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/strike_database_integrator_base.cc b/chromium/components/autofill/core/browser/strike_database_integrator_base.cc
index dd0e9f19e90..f1237260a58 100644
--- a/chromium/components/autofill/core/browser/strike_database_integrator_base.cc
+++ b/chromium/components/autofill/core/browser/strike_database_integrator_base.cc
@@ -25,22 +25,40 @@ StrikeDatabaseIntegratorBase::StrikeDatabaseIntegratorBase(
StrikeDatabaseIntegratorBase::~StrikeDatabaseIntegratorBase() = default;
-bool StrikeDatabaseIntegratorBase::IsMaxStrikesLimitReached(
- const std::string& id) const {
+bool StrikeDatabaseIntegratorBase::ShouldBlockFeature(
+ const std::string& id,
+ BlockedReason* blocked_reason) const {
CheckIdUniqueness(id);
- return GetStrikes(id) >= GetMaxStrikesLimit();
-}
-bool StrikeDatabaseIntegratorBase::HasDelayPassedSinceLastStrike(
- const std::string& id) const {
- CheckIdUniqueness(id);
- if (!GetRequiredDelaySinceLastStrike().has_value())
+ // Returns whether or not strike count for |id| has reached the strike limit
+ // set by GetMaxStrikesLimit().
+ if (GetStrikes(id) >= GetMaxStrikesLimit()) {
+ if (blocked_reason)
+ *blocked_reason = BlockedReason::kMaxStrikeLimitReached;
+
+ return true;
+ }
+
+ // Returns whether or not |GetRequiredDelaySinceLastStrike()| has passed since
+ // the last strike was logged for candidate with |id|. Note that some features
+ // don't specify a required delay.
+ if (GetRequiredDelaySinceLastStrike().has_value() &&
+ (AutofillClock::Now() -
+ base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(
+ strike_database_->GetLastUpdatedTimestamp(GetKey(id))))) <
+ GetRequiredDelaySinceLastStrike()) {
+ if (blocked_reason)
+ *blocked_reason = BlockedReason::kRequiredDelayNotPassed;
+
return true;
+ }
+
+ return false;
+}
- return (AutofillClock::Now() -
- base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(
- strike_database_->GetLastUpdatedTimestamp(GetKey(id))))) >=
- GetRequiredDelaySinceLastStrike();
+bool StrikeDatabaseIntegratorBase::ShouldBlockFeature(
+ BlockedReason* blocked_reason) const {
+ return ShouldBlockFeature(kSharedId, blocked_reason);
}
int StrikeDatabaseIntegratorBase::AddStrike(const std::string& id) {
diff --git a/chromium/components/autofill/core/browser/strike_database_integrator_base.h b/chromium/components/autofill/core/browser/strike_database_integrator_base.h
index e1e8c76eef6..8f379372f6d 100644
--- a/chromium/components/autofill/core/browser/strike_database_integrator_base.h
+++ b/chromium/components/autofill/core/browser/strike_database_integrator_base.h
@@ -10,6 +10,7 @@
#include <string>
#include <vector>
+#include "base/check.h"
#include "base/gtest_prod_util.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/strike_database_base.h"
@@ -27,18 +28,26 @@ static const char kSharedId[] = "shared_id";
// be loaded once per browser session.
class StrikeDatabaseIntegratorBase {
public:
+ // Reason why the feature should be blocked.
+ enum BlockedReason {
+ // Unknown reason, default value.
+ kUnknown = 0,
+ // Feature not offered due to max strike limit has been reached.
+ kMaxStrikeLimitReached = 1,
+ // Feature not offered due to required delay since last strike has not
+ // passed yet.
+ kRequiredDelayNotPassed = 2,
+ };
+
explicit StrikeDatabaseIntegratorBase(StrikeDatabaseBase* strike_database);
virtual ~StrikeDatabaseIntegratorBase();
- // TODO(crbug.com/1304328): Rename this to ShouldBlockFeature...() since this
- // takes both strikes and time delays.
- // Returns whether or not strike count for |id| has reached the strike limit
- // set by GetMaxStrikesLimit().
- bool IsMaxStrikesLimitReached(const std::string& id = kSharedId) const;
-
- // Returns whether or not |GetOfferDelayTimeDelta()| has passed since last
- // strike was logged for candidate with |id|.
- bool HasDelayPassedSinceLastStrike(const std::string& id = kSharedId) const;
+ // Returns whether a particular feature should be blocked (not offered) for
+ // the given |id|. The |blocked_reason|, if provided, will be populated with
+ // the reason why the feature should be blocked.
+ bool ShouldBlockFeature(const std::string& id,
+ BlockedReason* blocked_reason = nullptr) const;
+ bool ShouldBlockFeature(BlockedReason* blocked_reason = nullptr) const;
// Increments in-memory cache and updates underlying ProtoDatabase.
int AddStrike(const std::string& id = kSharedId);
diff --git a/chromium/components/autofill/core/browser/strike_database_integrator_test_strike_database_unittest.cc b/chromium/components/autofill/core/browser/strike_database_integrator_test_strike_database_unittest.cc
index f6ef66b1e4e..1e9289ebf88 100644
--- a/chromium/components/autofill/core/browser/strike_database_integrator_test_strike_database_unittest.cc
+++ b/chromium/components/autofill/core/browser/strike_database_integrator_test_strike_database_unittest.cc
@@ -14,6 +14,7 @@
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/autofill/core/browser/proto/strike_data.pb.h"
+#include "components/autofill/core/browser/strike_database_integrator_base.h"
#include "components/autofill/core/browser/test_autofill_clock.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/leveldb_proto/public/proto_database.h"
@@ -67,13 +68,19 @@ class StrikeDatabaseIntegratorTestStrikeDatabaseTest : public ::testing::Test {
TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
MaxStrikesLimitReachedTest) {
- EXPECT_EQ(false, strike_database_->IsMaxStrikesLimitReached());
+ StrikeDatabaseIntegratorBase::BlockedReason reason =
+ StrikeDatabaseIntegratorBase::kUnknown;
+ EXPECT_EQ(false, strike_database_->ShouldBlockFeature(&reason));
+ EXPECT_EQ(StrikeDatabaseIntegratorBase::BlockedReason::kUnknown, reason);
// 3 strikes added.
strike_database_->AddStrikes(3);
- EXPECT_EQ(false, strike_database_->IsMaxStrikesLimitReached());
+ EXPECT_EQ(false, strike_database_->ShouldBlockFeature(&reason));
+ EXPECT_EQ(StrikeDatabaseIntegratorBase::BlockedReason::kUnknown, reason);
// 4 strike added, total strike count is 7.
strike_database_->AddStrikes(4);
- EXPECT_EQ(true, strike_database_->IsMaxStrikesLimitReached());
+ EXPECT_EQ(true, strike_database_->ShouldBlockFeature(&reason));
+ EXPECT_EQ(StrikeDatabaseIntegratorBase::BlockedReason::kMaxStrikeLimitReached,
+ reason);
}
TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
@@ -248,15 +255,21 @@ TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
MaxStrikesLimitReachedUniqueIdTest) {
+ StrikeDatabaseIntegratorBase::BlockedReason reason =
+ StrikeDatabaseIntegratorBase::kUnknown;
strike_database_->SetUniqueIdsRequired(true);
const std::string unique_id = "1234";
- EXPECT_EQ(false, strike_database_->IsMaxStrikesLimitReached(unique_id));
+ EXPECT_EQ(false, strike_database_->ShouldBlockFeature(unique_id, &reason));
+ EXPECT_EQ(StrikeDatabaseIntegratorBase::BlockedReason::kUnknown, reason);
// 1 strike added for |unique_id|.
strike_database_->AddStrike(unique_id);
- EXPECT_EQ(false, strike_database_->IsMaxStrikesLimitReached(unique_id));
+ EXPECT_EQ(false, strike_database_->ShouldBlockFeature(unique_id, &reason));
+ EXPECT_EQ(StrikeDatabaseIntegratorBase::BlockedReason::kUnknown, reason);
// 6 strikes added for |unique_id|.
strike_database_->AddStrikes(6, unique_id);
- EXPECT_EQ(true, strike_database_->IsMaxStrikesLimitReached(unique_id));
+ EXPECT_EQ(true, strike_database_->ShouldBlockFeature(unique_id, &reason));
+ EXPECT_EQ(StrikeDatabaseIntegratorBase::BlockedReason::kMaxStrikeLimitReached,
+ reason);
}
TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
@@ -427,26 +440,42 @@ TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
}
}
-// Test to ensure that |HasDelayPassedSinceLastStrike()| function works
-// correctly.
+// Test to ensure that |ShouldBlockFeature| function works correctly with the
+// required latency since last strike requirement.
TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
- HasDelayPassedSinceLastStrike) {
+ HasRequiredDelayPassedSinceLastStrike) {
autofill::TestAutofillClock test_clock{AutofillClock::Now()};
strike_database_->SetUniqueIdsRequired(true);
strike_database_->SetRequiredDelaySinceLastStrike(base::Days(7));
strike_database_->AddStrike("fake key");
ASSERT_EQ(1U, strike_database_->CountEntries());
- EXPECT_FALSE(strike_database_->HasDelayPassedSinceLastStrike("fake key"));
+
+ StrikeDatabaseIntegratorBase::BlockedReason reason =
+ StrikeDatabaseIntegratorBase::kUnknown;
+ EXPECT_TRUE(strike_database_->ShouldBlockFeature("fake key", &reason));
+ EXPECT_EQ(
+ reason,
+ StrikeDatabaseIntegratorBase::BlockedReason::kRequiredDelayNotPassed);
test_clock.Advance(base::Days(1));
- EXPECT_FALSE(strike_database_->HasDelayPassedSinceLastStrike("fake key"));
+ reason = StrikeDatabaseIntegratorBase::kUnknown;
+ EXPECT_TRUE(strike_database_->ShouldBlockFeature("fake key", &reason));
+ EXPECT_EQ(
+ reason,
+ StrikeDatabaseIntegratorBase::BlockedReason::kRequiredDelayNotPassed);
+ reason = StrikeDatabaseIntegratorBase::kUnknown;
test_clock.Advance(base::Days(7));
- EXPECT_TRUE(strike_database_->HasDelayPassedSinceLastStrike("fake key"));
+ EXPECT_FALSE(strike_database_->ShouldBlockFeature("fake key", &reason));
+ EXPECT_EQ(reason, StrikeDatabaseIntegratorBase::BlockedReason::kUnknown);
+ reason = StrikeDatabaseIntegratorBase::kUnknown;
strike_database_->AddStrike("fake key");
- EXPECT_FALSE(strike_database_->HasDelayPassedSinceLastStrike("fake key"));
+ EXPECT_TRUE(strike_database_->ShouldBlockFeature("fake key", &reason));
+ EXPECT_EQ(
+ reason,
+ StrikeDatabaseIntegratorBase::BlockedReason::kRequiredDelayNotPassed);
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/suggestions_context.cc b/chromium/components/autofill/core/browser/suggestions_context.cc
new file mode 100644
index 00000000000..6fd389dca4e
--- /dev/null
+++ b/chromium/components/autofill/core/browser/suggestions_context.cc
@@ -0,0 +1,13 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/suggestions_context.h"
+
+namespace autofill {
+
+SuggestionsContext::SuggestionsContext() = default;
+
+SuggestionsContext::~SuggestionsContext() = default;
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/suggestions_context.h b/chromium/components/autofill/core/browser/suggestions_context.h
new file mode 100644
index 00000000000..bdd475aca48
--- /dev/null
+++ b/chromium/components/autofill/core/browser/suggestions_context.h
@@ -0,0 +1,68 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_CONTEXT_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_CONTEXT_H_
+
+#include "components/autofill/core/browser/autofill_ablation_study.h"
+#include "components/autofill/core/browser/form_structure.h"
+
+namespace autofill {
+
+// Indicates the reason why autofill suggestions are suppressed.
+enum class SuppressReason {
+ kNotSuppressed,
+ // Suggestions are not shown because an ablation experiment is enabled.
+ kAblation,
+ // Address suggestions are not shown because the field is annotated with
+ // autocomplete=off and the directive is being observed by the browser.
+ kAutocompleteOff,
+ // Suggestions are not shown because this form is on a secure site, but
+ // submits insecurely. This is only used when the user has started typing,
+ // otherwise a warning is shown.
+ kInsecureForm,
+ // Suggestions are not shown because the field is annotated with
+ // an unrecognized autocompelte attribute and the field is not credit card
+ // related. For credit card fields, the unrecognized attribute is ignored.
+ kAutocompleteUnrecognized,
+};
+
+// The context for the list of suggestions available for a given field.
+struct SuggestionsContext {
+ SuggestionsContext();
+ ~SuggestionsContext();
+
+ FormStructure* form_structure = nullptr;
+ AutofillField* focused_field = nullptr;
+ bool is_autofill_available = false;
+ bool is_context_secure = false;
+ bool is_filling_credit_card = false;
+ // Flag to indicate whether all suggestions come from Google Payments.
+ bool should_display_gpay_logo = false;
+ SuppressReason suppress_reason = SuppressReason::kNotSuppressed;
+ // Indicates whether the form filling is under ablation, meaning that
+ // autofill popups are suppressed.
+ AblationGroup ablation_group = AblationGroup::kDefault;
+ // Indicates whether the form filling is under ablation, under the condition
+ // that the user has data to fill on file. All users that don't have data
+ // to fill are in the AbationGroup::kDefault.
+ // Note that it is possible (due to implementation details) that this is
+ // incorrectly set to kDefault: If the user has typed some characters into a
+ // text field, it may look like no suggestions are available, but in
+ // practice the suggestions are just filtered out (Autofill only suggests
+ // matches that start with the typed prefix). Any consumers of the
+ // conditional_ablation_group attribute should monitor it over time.
+ // Any transitions of conditional_ablation_group from {kAblation,
+ // kControl} to kDefault should just be ignored and the previously reported
+ // value should be used. As the ablation experience is stable within a day,
+ // such a transition typically indicates that the user has type a prefix
+ // which led to the filtering of all autofillable data. In short: once
+ // either kAblation or kControl were reported, consumers should stick to
+ // that.
+ AblationGroup conditional_ablation_group = AblationGroup::kDefault;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_CONTEXT_H_
diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc
index 8fda38442b5..55da3ffe7e0 100644
--- a/chromium/components/autofill/core/browser/test_autofill_client.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_client.cc
@@ -18,18 +18,21 @@
namespace autofill {
-TestAutofillClient::TestAutofillClient()
- : form_origin_(GURL("https://example.test")),
+TestAutofillClient::TestAutofillClient(
+ std::unique_ptr<TestPersonalDataManager> pdm)
+ : test_personal_data_manager_(
+ pdm ? std::move(pdm) : std::make_unique<TestPersonalDataManager>()),
+ form_origin_(GURL("https://example.test")),
last_committed_url_(GURL("https://example.test")) {}
-TestAutofillClient::~TestAutofillClient() {}
+TestAutofillClient::~TestAutofillClient() = default;
version_info::Channel TestAutofillClient::GetChannel() const {
return channel_for_testing_;
}
-PersonalDataManager* TestAutofillClient::GetPersonalDataManager() {
- return &test_personal_data_manager_;
+TestPersonalDataManager* TestAutofillClient::GetPersonalDataManager() {
+ return test_personal_data_manager_.get();
}
AutocompleteHistoryManager*
@@ -37,6 +40,10 @@ TestAutofillClient::GetAutocompleteHistoryManager() {
return &mock_autocomplete_history_manager_;
}
+MerchantPromoCodeManager* TestAutofillClient::GetMerchantPromoCodeManager() {
+ return &mock_merchant_promo_code_manager_;
+}
+
PrefService* TestAutofillClient::GetPrefs() {
return const_cast<PrefService*>(base::as_const(*this).GetPrefs());
}
@@ -123,7 +130,7 @@ void TestAutofillClient::ShowUnmaskPrompt(
void TestAutofillClient::OnUnmaskVerificationResult(PaymentsRpcResult result) {}
-raw_ptr<VirtualCardEnrollmentManager>
+VirtualCardEnrollmentManager*
TestAutofillClient::GetVirtualCardEnrollmentManager() {
return form_data_importer_->GetVirtualCardEnrollmentManager();
}
@@ -284,7 +291,7 @@ bool TestAutofillClient::IsPasswordManagerEnabled() {
}
void TestAutofillClient::PropagateAutofillPredictions(
- content::RenderFrameHost* rfh,
+ AutofillDriver* driver,
const std::vector<FormStructure*>& forms) {}
void TestAutofillClient::DidFillOrPreviewField(
@@ -306,6 +313,8 @@ bool TestAutofillClient::AreServerCardsSupported() const {
void TestAutofillClient::ExecuteCommand(int id) {}
+void TestAutofillClient::OpenPromoCodeOfferDetailsURL(const GURL& url) {}
+
void TestAutofillClient::LoadRiskData(
base::OnceCallback<void(const std::string&)> callback) {
std::move(callback).Run("some risk data");
diff --git a/chromium/components/autofill/core/browser/test_autofill_client.h b/chromium/components/autofill/core/browser/test_autofill_client.h
index 30261f3e8f9..a5b9d052531 100644
--- a/chromium/components/autofill/core/browser/test_autofill_client.h
+++ b/chromium/components/autofill/core/browser/test_autofill_client.h
@@ -12,10 +12,10 @@
#include "base/compiler_specific.h"
#include "base/i18n/rtl.h"
-#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/mock_autocomplete_history_manager.h"
+#include "components/autofill/core/browser/mock_merchant_promo_code_manager.h"
#include "components/autofill/core/browser/payments/autofill_offer_manager.h"
#include "components/autofill/core/browser/payments/legal_message_line.h"
#include "components/autofill/core/browser/payments/test_payments_client.h"
@@ -40,7 +40,8 @@ namespace autofill {
// This class is for easier writing of tests.
class TestAutofillClient : public AutofillClient {
public:
- TestAutofillClient();
+ explicit TestAutofillClient(
+ std::unique_ptr<TestPersonalDataManager> pdm = nullptr);
TestAutofillClient(const TestAutofillClient&) = delete;
TestAutofillClient& operator=(const TestAutofillClient&) = delete;
@@ -49,8 +50,9 @@ class TestAutofillClient : public AutofillClient {
// AutofillClient:
version_info::Channel GetChannel() const override;
- PersonalDataManager* GetPersonalDataManager() override;
+ TestPersonalDataManager* GetPersonalDataManager() override;
AutocompleteHistoryManager* GetAutocompleteHistoryManager() override;
+ MerchantPromoCodeManager* GetMerchantPromoCodeManager() override;
PrefService* GetPrefs() override;
const PrefService* GetPrefs() const override;
syncer::SyncService* GetSyncService() override;
@@ -77,8 +79,7 @@ class TestAutofillClient : public AutofillClient {
UnmaskCardReason reason,
base::WeakPtr<CardUnmaskDelegate> delegate) override;
void OnUnmaskVerificationResult(PaymentsRpcResult result) override;
- raw_ptr<VirtualCardEnrollmentManager> GetVirtualCardEnrollmentManager()
- override;
+ VirtualCardEnrollmentManager* GetVirtualCardEnrollmentManager() override;
void ShowVirtualCardEnrollDialog(
const VirtualCardEnrollmentFields& virtual_card_enrollment_fields,
base::OnceClosure accept_virtual_card_callback,
@@ -155,7 +156,7 @@ class TestAutofillClient : public AutofillClient {
bool IsAutocompleteEnabled() override;
bool IsPasswordManagerEnabled() override;
void PropagateAutofillPredictions(
- content::RenderFrameHost* rfh,
+ AutofillDriver* driver,
const std::vector<FormStructure*>& forms) override;
void DidFillOrPreviewField(const std::u16string& autofilled_value,
const std::u16string& profile_full_name) override;
@@ -166,6 +167,7 @@ class TestAutofillClient : public AutofillClient {
bool ShouldShowSigninPromo() override;
bool AreServerCardsSupported() const override;
void ExecuteCommand(int id) override;
+ void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
// RiskDataLoader:
void LoadRiskData(
@@ -184,6 +186,10 @@ class TestAutofillClient : public AutofillClient {
prefs_ = std::move(prefs);
}
+ void set_personal_data_manager(std::unique_ptr<TestPersonalDataManager> pdm) {
+ test_personal_data_manager_ = std::move(pdm);
+ }
+
void set_test_strike_database(
std::unique_ptr<TestStrikeDatabase> test_strike_database) {
test_strike_database_ = std::move(test_strike_database);
@@ -262,6 +268,11 @@ class TestAutofillClient : public AutofillClient {
return &mock_autocomplete_history_manager_;
}
+ ::testing::NiceMock<MockMerchantPromoCodeManager>*
+ GetMockMerchantPromoCodeManager() {
+ return &mock_merchant_promo_code_manager_;
+ }
+
void set_migration_card_selections(
const std::vector<std::string>& migration_card_selection) {
migration_card_selection_ = migration_card_selection;
@@ -285,16 +296,23 @@ class TestAutofillClient : public AutofillClient {
signin::IdentityTestEnvironment identity_test_env_;
raw_ptr<syncer::SyncService> test_sync_service_ = nullptr;
TestAddressNormalizer test_address_normalizer_;
- TestPersonalDataManager test_personal_data_manager_;
::testing::NiceMock<MockAutocompleteHistoryManager>
mock_autocomplete_history_manager_;
- std::unique_ptr<AutofillOfferManager> autofill_offer_manager_;
+ ::testing::NiceMock<MockMerchantPromoCodeManager>
+ mock_merchant_promo_code_manager_;
// NULL by default.
std::unique_ptr<PrefService> prefs_;
std::unique_ptr<TestStrikeDatabase> test_strike_database_;
std::unique_ptr<payments::PaymentsClient> payments_client_;
+
+ // AutofillOfferManager and TestFormDataImporter must be destroyed before
+ // TestPersonalDataManager, because the former's destructors refer to the
+ // latter.
+ std::unique_ptr<TestPersonalDataManager> test_personal_data_manager_;
+ std::unique_ptr<AutofillOfferManager> autofill_offer_manager_;
std::unique_ptr<TestFormDataImporter> form_data_importer_;
+
GURL form_origin_;
ukm::SourceId source_id_ = -1;
std::string variation_config_country_code_;
diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.cc b/chromium/components/autofill/core/browser/test_autofill_driver.cc
index 9cf492a3e54..80b5a85cea9 100644
--- a/chromium/components/autofill/core/browser/test_autofill_driver.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_driver.cc
@@ -15,11 +15,17 @@
namespace autofill {
TestAutofillDriver::TestAutofillDriver()
- : test_shared_loader_factory_(
+ :
+#if !BUILDFLAG(IS_IOS)
+ ContentAutofillDriver(/*render_frame_host=*/nullptr,
+ /*autofill_router=*/nullptr),
+#endif
+ test_shared_loader_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
- &test_url_loader_factory_)) {}
+ &test_url_loader_factory_)) {
+}
-TestAutofillDriver::~TestAutofillDriver() {}
+TestAutofillDriver::~TestAutofillDriver() = default;
bool TestAutofillDriver::IsIncognito() const {
return is_incognito_;
@@ -74,10 +80,6 @@ std::vector<FieldGlobalId> TestAutofillDriver::FillOrPreviewForm(
return result;
}
-void TestAutofillDriver::PropagateAutofillPredictions(
- const std::vector<FormStructure*>& forms) {
-}
-
void TestAutofillDriver::HandleParsedForms(
const std::vector<const FormData*>& forms) {}
diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.h b/chromium/components/autofill/core/browser/test_autofill_driver.h
index d95ed9fcae8..e69366b4cc2 100644
--- a/chromium/components/autofill/core/browser/test_autofill_driver.h
+++ b/chromium/components/autofill/core/browser/test_autofill_driver.h
@@ -10,6 +10,7 @@
#include "base/memory/scoped_refptr.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_driver.h"
+#include "components/autofill/core/browser/autofill_manager.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "services/network/test/test_url_loader_factory.h"
#include "url/origin.h"
@@ -33,6 +34,14 @@ class TestAutofillDriver : public ContentAutofillDriver {
TestAutofillDriver& operator=(const TestAutofillDriver&) = delete;
~TestAutofillDriver() override;
+#if BUILDFLAG(IS_IOS)
+ void set_autofill_manager(std::unique_ptr<AutofillManager> autofill_manager) {
+ autofill_manager_ = std::move(autofill_manager);
+ }
+
+ AutofillManager* autofill_manager() { return autofill_manager_.get(); }
+#endif
+
// AutofillDriver implementation overrides.
bool IsIncognito() const override;
bool IsInAnyMainFrame() const override;
@@ -54,8 +63,6 @@ class TestAutofillDriver : public ContentAutofillDriver {
const url::Origin& triggered_origin,
const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map)
override;
- void PropagateAutofillPredictions(
- const std::vector<autofill::FormStructure*>& forms) override;
void HandleParsedForms(const std::vector<const FormData*>& forms) override;
void SendAutofillTypePredictionsToRenderer(
const std::vector<FormStructure*>& forms) override;
@@ -105,6 +112,10 @@ class TestAutofillDriver : public ContentAutofillDriver {
bool(const url::Origin&, FieldGlobalId, ServerFieldType)>
field_type_map_filter_;
+#if BUILDFLAG(IS_IOS)
+ std::unique_ptr<AutofillManager> autofill_manager_;
+#endif
+
#if !BUILDFLAG(IS_IOS)
std::unique_ptr<webauthn::InternalAuthenticator> test_authenticator_;
#endif
diff --git a/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc b/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc
index 71339b17087..2f931a5c418 100644
--- a/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc
@@ -93,7 +93,10 @@ void TestAutofillExternalDelegate::CheckSuggestions(
ASSERT_LE(expected_num_suggestions, suggestions_.size());
for (size_t i = 0; i < expected_num_suggestions; ++i) {
SCOPED_TRACE(base::StringPrintf("i: %" PRIuS, i));
- EXPECT_EQ(expected_suggestions[i].value, suggestions_[i].value);
+ EXPECT_EQ(expected_suggestions[i].main_text.value,
+ suggestions_[i].main_text.value);
+ EXPECT_EQ(expected_suggestions[i].minor_text.value,
+ suggestions_[i].minor_text.value);
EXPECT_EQ(expected_suggestions[i].label, suggestions_[i].label);
EXPECT_EQ(expected_suggestions[i].icon, suggestions_[i].icon);
EXPECT_EQ(expected_suggestions[i].frontend_id, suggestions_[i].frontend_id);
diff --git a/chromium/components/autofill/core/browser/test_browser_autofill_manager.cc b/chromium/components/autofill/core/browser/test_browser_autofill_manager.cc
index 30e1f5c4163..c51a3563b23 100644
--- a/chromium/components/autofill/core/browser/test_browser_autofill_manager.cc
+++ b/chromium/components/autofill/core/browser/test_browser_autofill_manager.cc
@@ -6,8 +6,11 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_suggestion_generator.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/mock_single_field_form_fill_router.h"
+#include "components/autofill/core/browser/test_autofill_client.h"
+#include "components/autofill/core/browser/test_autofill_driver.h"
#include "components/autofill/core/browser/test_form_structure.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -16,13 +19,16 @@
namespace autofill {
TestBrowserAutofillManager::TestBrowserAutofillManager(
- AutofillDriver* driver,
- AutofillClient* client,
- TestPersonalDataManager* personal_data)
- : BrowserAutofillManager(driver, client, personal_data),
- personal_data_(personal_data) {}
+ TestAutofillDriver* driver,
+ TestAutofillClient* client)
+ : BrowserAutofillManager(driver,
+ client,
+ "en-US",
+ EnableDownloadManager(false)),
+ client_(client),
+ driver_(driver) {}
-TestBrowserAutofillManager::~TestBrowserAutofillManager() {}
+TestBrowserAutofillManager::~TestBrowserAutofillManager() = default;
bool TestBrowserAutofillManager::IsAutofillProfileEnabled() const {
return autofill_profile_enabled_;
@@ -41,6 +47,10 @@ void TestBrowserAutofillManager::UploadFormData(
BrowserAutofillManager::UploadFormData(submitted_form, observed_submission);
}
+void TestBrowserAutofillManager::ScheduleRefill(const FormData& form) {
+ TriggerRefillForTest(form);
+}
+
bool TestBrowserAutofillManager::MaybeStartVoteUploadProcess(
std::unique_ptr<FormStructure> form_structure,
bool observed_submission) {
@@ -75,10 +85,9 @@ void TestBrowserAutofillManager::UploadFormDataAsyncCallback(
submitted_form->field(i)->possible_types();
EXPECT_EQ(expected_submitted_field_types_[i].size(),
possible_types.size());
- for (auto it = expected_submitted_field_types_[i].begin();
- it != expected_submitted_field_types_[i].end(); ++it) {
- EXPECT_TRUE(possible_types.count(*it))
- << "Expected type: " << AutofillType(*it).ToString();
+ for (auto it : expected_submitted_field_types_[i]) {
+ EXPECT_TRUE(possible_types.count(it))
+ << "Expected type: " << AutofillType(it).ToString();
}
}
}
@@ -91,16 +100,35 @@ int TestBrowserAutofillManager::GetPackedCreditCardID(int credit_card_id) {
std::string credit_card_guid =
base::StringPrintf("00000000-0000-0000-0000-%012d", credit_card_id);
- return MakeFrontendID(credit_card_guid, std::string());
+ return suggestion_generator()->MakeFrontendId(credit_card_guid,
+ std::string());
}
void TestBrowserAutofillManager::AddSeenForm(
const FormData& form,
const std::vector<ServerFieldType>& heuristic_types,
const std::vector<ServerFieldType>& server_types) {
+ std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>
+ all_heuristic_types;
+
+ base::ranges::transform(
+ heuristic_types, std::back_inserter(all_heuristic_types),
+ [](ServerFieldType type)
+ -> std::vector<std::pair<PatternSource, ServerFieldType>> {
+ return {{GetActivePatternSource(), type}};
+ });
+
+ AddSeenForm(form, all_heuristic_types, server_types);
+}
+
+void TestBrowserAutofillManager::AddSeenForm(
+ const FormData& form,
+ const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>&
+ heuristic_types,
+ const std::vector<ServerFieldType>& server_types) {
FormData empty_form = form;
- for (size_t i = 0; i < empty_form.fields.size(); ++i) {
- empty_form.fields[i].value = std::u16string();
+ for (auto& field : empty_form.fields) {
+ field.value = std::u16string();
}
std::unique_ptr<TestFormStructure> form_structure =
@@ -126,20 +154,34 @@ const std::string TestBrowserAutofillManager::GetSubmittedFormSignature() {
return submitted_form_signature_;
}
+void TestBrowserAutofillManager::OnAskForValuesToFillTest(
+ const FormData& form,
+ const FormFieldData& field,
+ int query_id,
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion,
+ TouchToFillEligible touch_to_fill_eligible) {
+ BrowserAutofillManager::OnAskForValuesToFill(
+ query_id, form, field, bounding_box, autoselect_first_suggestion,
+ touch_to_fill_eligible);
+}
+
void TestBrowserAutofillManager::SetAutofillProfileEnabled(
bool autofill_profile_enabled) {
autofill_profile_enabled_ = autofill_profile_enabled;
- if (!autofill_profile_enabled_)
+ if (!autofill_profile_enabled_) {
// Profile data is refreshed when this pref is changed.
- personal_data_->ClearProfiles();
+ client()->GetPersonalDataManager()->ClearProfiles();
+ }
}
void TestBrowserAutofillManager::SetAutofillCreditCardEnabled(
bool autofill_credit_card_enabled) {
autofill_credit_card_enabled_ = autofill_credit_card_enabled;
- if (!autofill_credit_card_enabled_)
+ if (!autofill_credit_card_enabled_) {
// Credit card data is refreshed when this pref is changed.
- personal_data_->ClearCreditCards();
+ client()->GetPersonalDataManager()->ClearCreditCards();
+ }
}
void TestBrowserAutofillManager::SetExpectedSubmittedFieldTypes(
diff --git a/chromium/components/autofill/core/browser/test_browser_autofill_manager.h b/chromium/components/autofill/core/browser/test_browser_autofill_manager.h
index eb6abb9c120..422bf0e0d49 100644
--- a/chromium/components/autofill/core/browser/test_browser_autofill_manager.h
+++ b/chromium/components/autofill/core/browser/test_browser_autofill_manager.h
@@ -16,20 +16,17 @@
#include "components/autofill/core/browser/browser_autofill_manager.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-using base::TimeTicks;
-
namespace autofill {
-class AutofillClient;
-class AutofillDriver;
+class TestAutofillClient;
+class TestAutofillDriver;
class FormStructure;
class TestPersonalDataManager;
class TestBrowserAutofillManager : public BrowserAutofillManager {
public:
- TestBrowserAutofillManager(AutofillDriver* driver,
- AutofillClient* client,
- TestPersonalDataManager* personal_data);
+ TestBrowserAutofillManager(TestAutofillDriver* driver,
+ TestAutofillClient* client);
TestBrowserAutofillManager(const TestBrowserAutofillManager&) = delete;
TestBrowserAutofillManager& operator=(const TestBrowserAutofillManager&) =
@@ -37,6 +34,9 @@ class TestBrowserAutofillManager : public BrowserAutofillManager {
~TestBrowserAutofillManager() override;
+ TestAutofillClient* client() { return client_; }
+ TestAutofillDriver* driver() { return driver_; }
+
// BrowserAutofillManager overrides.
bool IsAutofillProfileEnabled() const override;
bool IsAutofillCreditCardEnabled() const override;
@@ -49,6 +49,8 @@ class TestBrowserAutofillManager : public BrowserAutofillManager {
const base::TimeTicks& interaction_time,
const base::TimeTicks& submission_time,
bool observed_submission) override;
+ // Immediately triggers the refill.
+ void ScheduleRefill(const FormData& form) override;
// Unique to TestBrowserAutofillManager:
@@ -58,12 +60,27 @@ class TestBrowserAutofillManager : public BrowserAutofillManager {
const std::vector<ServerFieldType>& heuristic_types,
const std::vector<ServerFieldType>& server_types);
+ void AddSeenForm(
+ const FormData& form,
+ const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>&
+ heuristic_types,
+ const std::vector<ServerFieldType>& server_types);
+
void AddSeenFormStructure(std::unique_ptr<FormStructure> form_structure);
void ClearFormStructures();
const std::string GetSubmittedFormSignature();
+ // Helper to skip irrelevant params.
+ void OnAskForValuesToFillTest(
+ const FormData& form,
+ const FormFieldData& field,
+ int query_id = 0,
+ const gfx::RectF& bounding_box = {},
+ bool autoselect_first_suggestion = false,
+ TouchToFillEligible touch_to_fill_eligible = TouchToFillEligible(false));
+
void SetAutofillProfileEnabled(bool profile_enabled);
void SetAutofillCreditCardEnabled(bool credit_card_enabled);
@@ -78,7 +95,9 @@ class TestBrowserAutofillManager : public BrowserAutofillManager {
using BrowserAutofillManager::pending_form_data;
private:
- raw_ptr<TestPersonalDataManager> personal_data_; // Weak reference.
+ raw_ptr<TestAutofillClient> client_;
+ raw_ptr<TestAutofillDriver> driver_;
+
bool autofill_profile_enabled_ = true;
bool autofill_credit_card_enabled_ = true;
bool call_parent_upload_form_data_ = false;
diff --git a/chromium/components/autofill/core/browser/test_form_structure.cc b/chromium/components/autofill/core/browser/test_form_structure.cc
index ebe6d1680e0..cbf12d6c15b 100644
--- a/chromium/components/autofill/core/browser/test_form_structure.cc
+++ b/chromium/components/autofill/core/browser/test_form_structure.cc
@@ -4,10 +4,17 @@
#include "components/autofill/core/browser/test_form_structure.h"
+#include "components/autofill/core/browser/form_parsing/field_candidates.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace autofill {
+using ::testing::_;
+using ::testing::Contains;
+using ::testing::Each;
+using ::testing::Pair;
+
TestFormStructure::TestFormStructure(const FormData& form)
: FormStructure(form) {}
@@ -16,13 +23,35 @@ TestFormStructure::~TestFormStructure() {}
void TestFormStructure::SetFieldTypes(
const std::vector<ServerFieldType>& heuristic_types,
const std::vector<ServerFieldType>& server_types) {
+ std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>
+ all_heuristic_types;
+
+ base::ranges::transform(
+ heuristic_types, std::back_inserter(all_heuristic_types),
+ [](ServerFieldType type)
+ -> std::vector<std::pair<PatternSource, ServerFieldType>> {
+ return {{GetActivePatternSource(), type}};
+ });
+
+ SetFieldTypes(all_heuristic_types, server_types);
+}
+
+void TestFormStructure::SetFieldTypes(
+ const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>&
+ heuristic_types,
+ const std::vector<ServerFieldType>& server_types) {
ASSERT_EQ(field_count(), heuristic_types.size());
ASSERT_EQ(field_count(), server_types.size());
+ ASSERT_THAT(heuristic_types,
+ Each(Contains(Pair(GetActivePatternSource(), _))))
+ << "There must be a default heuristic prediction for every field.";
for (size_t i = 0; i < field_count(); ++i) {
AutofillField* form_field = field(i);
ASSERT_TRUE(form_field);
- form_field->set_heuristic_type(heuristic_types[i]);
+
+ for (const auto& [source, type] : heuristic_types[i])
+ form_field->set_heuristic_type(source, type);
AutofillQueryResponse::FormSuggestion::FieldSuggestion::FieldPrediction
prediction;
prediction.set_type(server_types[i]);
diff --git a/chromium/components/autofill/core/browser/test_form_structure.h b/chromium/components/autofill/core/browser/test_form_structure.h
index da0b086fd70..b4fd0258e91 100644
--- a/chromium/components/autofill/core/browser/test_form_structure.h
+++ b/chromium/components/autofill/core/browser/test_form_structure.h
@@ -7,6 +7,7 @@
#include <vector>
+#include "components/autofill/core/browser/form_parsing/field_candidates.h"
#include "components/autofill/core/browser/form_structure.h"
namespace autofill {
@@ -20,8 +21,21 @@ class TestFormStructure : public FormStructure {
~TestFormStructure() override;
+ // Set the heuristic and server types for each field. The `heuristic_types`
+ // and `server_types` vectors must be aligned with the indices of the fields
+ // in the form.
void SetFieldTypes(const std::vector<ServerFieldType>& heuristic_types,
const std::vector<ServerFieldType>& server_types);
+
+ // Set the heuristic and server types for each field. The `heuristic_types`
+ // and `server_types` vectors must be aligned with the indices of the fields
+ // in the form. For each field in `heuristic_types` there must be exactly one
+ // `GetActivePatternSource()` prediction and any number of alternative
+ // predictions.
+ void SetFieldTypes(
+ const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>&
+ heuristic_types,
+ const std::vector<ServerFieldType>& server_types);
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_utils/test_profiles.cc b/chromium/components/autofill/core/browser/test_utils/test_profiles.cc
index deb3fee781f..576b83173fb 100644
--- a/chromium/components/autofill/core/browser/test_utils/test_profiles.cc
+++ b/chromium/components/autofill/core/browser/test_utils/test_profiles.cc
@@ -8,6 +8,8 @@
namespace autofill {
+using structured_address::VerificationStatus;
+
namespace test {
void SetProfileTestValues(AutofillProfile* profile,
diff --git a/chromium/components/autofill/core/browser/test_utils/test_profiles.h b/chromium/components/autofill/core/browser/test_utils/test_profiles.h
index b2741e9a687..86d352b803a 100644
--- a/chromium/components/autofill/core/browser/test_utils/test_profiles.h
+++ b/chromium/components/autofill/core/browser/test_utils/test_profiles.h
@@ -10,11 +10,7 @@
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/autofill_structured_address_test_utils.h"
-namespace autofill {
-
-using structured_address::VerificationStatus;
-
-namespace test {
+namespace autofill::test {
// Defines the |value| and |verification_status| for a specific Autofill
// |field_type|
@@ -58,8 +54,6 @@ AutofillProfile SubsetOfStandardProfile();
// This profile that is not similar to the standard profile.
AutofillProfile DifferentFromStandardProfile();
-} // namespace test
-
-} // namespace autofill
+} // namespace autofill::test
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_UTILS_TEST_PROFILES_H_
diff --git a/chromium/components/autofill/core/browser/touch_to_fill_delegate.cc b/chromium/components/autofill/core/browser/touch_to_fill_delegate.cc
new file mode 100644
index 00000000000..55af4f1d8d9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/touch_to_fill_delegate.cc
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium 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/autofill/core/browser/touch_to_fill_delegate.h"
+
+namespace autofill {
+
+TouchToFillDelegate::TouchToFillDelegate() = default;
+TouchToFillDelegate::~TouchToFillDelegate() = default;
+
+bool TouchToFillDelegate::TryToShowTouchToFill(int query_id,
+ const FormData& form,
+ const FormFieldData& field) {
+ // TODO(crbug.com/1247698): Add eligibility checks and trigger TTF.
+ return false;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/touch_to_fill_delegate.h b/chromium/components/autofill/core/browser/touch_to_fill_delegate.h
new file mode 100644
index 00000000000..2bea4e8cbe9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/touch_to_fill_delegate.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_TOUCH_TO_FILL_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_TOUCH_TO_FILL_DELEGATE_H_
+
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+
+namespace autofill {
+
+// Delegate for in-browser Touch To Fill (TTF) surface display and selection.
+// TODO(crbug.com/1324900): Consider using more descriptive name.
+class TouchToFillDelegate {
+ public:
+ TouchToFillDelegate();
+ TouchToFillDelegate(const TouchToFillDelegate&) = delete;
+ TouchToFillDelegate& operator=(const TouchToFillDelegate&) = delete;
+ virtual ~TouchToFillDelegate();
+
+ // Checks whether TTF is eligible for the given web form data. On success
+ // triggers the corresponding surface and returns |true|.
+ virtual bool TryToShowTouchToFill(int query_id,
+ const FormData& form,
+ const FormFieldData& field);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TOUCH_TO_FILL_DELEGATE_H_
diff --git a/chromium/components/autofill/core/browser/ui/accessory_sheet_data.cc b/chromium/components/autofill/core/browser/ui/accessory_sheet_data.cc
index 149ff395878..e032e5a7268 100644
--- a/chromium/components/autofill/core/browser/ui/accessory_sheet_data.cc
+++ b/chromium/components/autofill/core/browser/ui/accessory_sheet_data.cc
@@ -174,7 +174,7 @@ std::ostream& operator<<(std::ostream& os,
}
FooterCommand::FooterCommand(std::u16string display_text,
- autofill::AccessoryAction action)
+ AccessoryAction action)
: display_text_(std::move(display_text)),
accessory_action_(action),
estimated_memory_use_by_strings_(
@@ -208,7 +208,7 @@ std::ostream& operator<<(std::ostream& os, const FooterCommand& fc) {
OptionToggle::OptionToggle(std::u16string display_text,
bool enabled,
- autofill::AccessoryAction action)
+ AccessoryAction action)
: display_text_(display_text),
enabled_(enabled),
accessory_action_(action),
@@ -347,7 +347,7 @@ AccessorySheetData::Builder& AccessorySheetData::Builder::SetWarning(
AccessorySheetData::Builder&& AccessorySheetData::Builder::SetOptionToggle(
std::u16string display_text,
bool enabled,
- autofill::AccessoryAction action) && {
+ AccessoryAction action) && {
// Calls SetOptionToggle(...)& since |this| is an lvalue.
return std::move(SetOptionToggle(std::move(display_text), enabled, action));
}
@@ -355,7 +355,7 @@ AccessorySheetData::Builder&& AccessorySheetData::Builder::SetOptionToggle(
AccessorySheetData::Builder& AccessorySheetData::Builder::SetOptionToggle(
std::u16string display_text,
bool enabled,
- autofill::AccessoryAction action) & {
+ AccessoryAction action) & {
accessory_sheet_data_.set_option_toggle(
OptionToggle(std::move(display_text), enabled, action));
return *this;
@@ -464,14 +464,14 @@ AccessorySheetData::Builder& AccessorySheetData::Builder::AddPromoCodeInfo(
AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendFooterCommand(
std::u16string display_text,
- autofill::AccessoryAction action) && {
+ AccessoryAction action) && {
// Calls AppendFooterCommand(...)& since |this| is an lvalue.
return std::move(AppendFooterCommand(std::move(display_text), action));
}
AccessorySheetData::Builder& AccessorySheetData::Builder::AppendFooterCommand(
std::u16string display_text,
- autofill::AccessoryAction action) & {
+ AccessoryAction action) & {
accessory_sheet_data_.add_footer_command(
FooterCommand(std::move(display_text), action));
return *this;
diff --git a/chromium/components/autofill/core/browser/ui/accessory_sheet_data.h b/chromium/components/autofill/core/browser/ui/accessory_sheet_data.h
index 2667177cdea..ca3258c4a26 100644
--- a/chromium/components/autofill/core/browser/ui/accessory_sheet_data.h
+++ b/chromium/components/autofill/core/browser/ui/accessory_sheet_data.h
@@ -159,7 +159,7 @@ std::ostream& operator<<(std::ostream& out,
// Represents a command below the suggestions, such as "Manage password...".
class FooterCommand {
public:
- FooterCommand(std::u16string display_text, autofill::AccessoryAction action);
+ FooterCommand(std::u16string display_text, AccessoryAction action);
FooterCommand(const FooterCommand& footer_command);
FooterCommand(FooterCommand&& footer_command);
@@ -170,9 +170,7 @@ class FooterCommand {
const std::u16string& display_text() const { return display_text_; }
- autofill::AccessoryAction accessory_action() const {
- return accessory_action_;
- }
+ AccessoryAction accessory_action() const { return accessory_action_; }
bool operator==(const FooterCommand& fc) const;
@@ -184,7 +182,7 @@ class FooterCommand {
// IMPORTANT(https://crbug.com/1169167): Add the size of newly added strings
// to the memory estimation member!
std::u16string display_text_;
- autofill::AccessoryAction accessory_action_;
+ AccessoryAction accessory_action_;
size_t estimated_memory_use_by_strings_ = 0;
};
@@ -224,7 +222,7 @@ class OptionToggle {
// to the memory estimation member!
std::u16string display_text_;
bool enabled_;
- autofill::AccessoryAction accessory_action_;
+ AccessoryAction accessory_action_;
size_t estimated_memory_use_by_strings_ = 0;
};
@@ -328,10 +326,10 @@ class AccessorySheetData::Builder {
// Sets the option toggle in the accessory sheet.
Builder&& SetOptionToggle(std::u16string display_text,
bool enabled,
- autofill::AccessoryAction action) &&;
+ AccessoryAction action) &&;
Builder& SetOptionToggle(std::u16string display_text,
bool enabled,
- autofill::AccessoryAction action) &;
+ AccessoryAction action) &;
// Adds a new UserInfo object to |accessory_sheet_data_|.
Builder&& AddUserInfo(
@@ -379,9 +377,9 @@ class AccessorySheetData::Builder {
// Appends a new footer command to |accessory_sheet_data_|.
Builder&& AppendFooterCommand(std::u16string display_text,
- autofill::AccessoryAction action) &&;
+ AccessoryAction action) &&;
Builder& AppendFooterCommand(std::u16string display_text,
- autofill::AccessoryAction action) &;
+ AccessoryAction action) &;
// This class returns the constructed AccessorySheetData object. Since this
// would render the builder unusable, it's required to destroy the object
diff --git a/chromium/components/autofill/core/browser/ui/address_combobox_model.cc b/chromium/components/autofill/core/browser/ui/address_combobox_model.cc
index c7e6f906ff3..d52dc0e925c 100644
--- a/chromium/components/autofill/core/browser/ui/address_combobox_model.cc
+++ b/chromium/components/autofill/core/browser/ui/address_combobox_model.cc
@@ -84,14 +84,6 @@ int AddressComboboxModel::GetDefaultIndex() const {
return ui::ComboboxModel::GetDefaultIndex();
}
-void AddressComboboxModel::AddObserver(ui::ComboboxModelObserver* observer) {
- observers_.AddObserver(observer);
-}
-
-void AddressComboboxModel::RemoveObserver(ui::ComboboxModelObserver* observer) {
- observers_.RemoveObserver(observer);
-}
-
int AddressComboboxModel::AddNewProfile(const AutofillProfile& profile) {
profiles_cache_.push_back(std::make_unique<AutofillProfile>(profile));
UpdateAddresses();
@@ -131,7 +123,7 @@ void AddressComboboxModel::UpdateAddresses() {
for (size_t i = 0; i < profiles_cache_.size(); ++i)
addresses_.emplace_back(profiles_cache_[i]->guid(), labels[i]);
- for (auto& observer : observers_) {
+ for (auto& observer : observers()) {
observer.OnComboboxModelChanged(this);
}
}
diff --git a/chromium/components/autofill/core/browser/ui/address_combobox_model.h b/chromium/components/autofill/core/browser/ui/address_combobox_model.h
index 02d65209d27..3a41b30c925 100644
--- a/chromium/components/autofill/core/browser/ui/address_combobox_model.h
+++ b/chromium/components/autofill/core/browser/ui/address_combobox_model.h
@@ -40,8 +40,6 @@ class AddressComboboxModel : public ui::ComboboxModel {
std::u16string GetItemAt(int index) const override;
bool IsItemSeparatorAt(int index) const override;
int GetDefaultIndex() const override;
- void AddObserver(ui::ComboboxModelObserver* observer) override;
- void RemoveObserver(ui::ComboboxModelObserver* observer) override;
// Adds |profile| to model and return its combobox index. The lifespan of
// |profile| beyond this call is undefined so a copy must be made.
@@ -72,9 +70,6 @@ class AddressComboboxModel : public ui::ComboboxModel {
// If non empty, the guid of the address that should be selected by default.
std::string default_selected_guid_;
-
- // To be called when the data for the given country code was loaded.
- base::ObserverList<ui::ComboboxModelObserver> observers_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/ui/autofill_image_fetcher.cc b/chromium/components/autofill/core/browser/ui/autofill_image_fetcher.cc
index aa6c31f5bde..c2199eb0d36 100644
--- a/chromium/components/autofill/core/browser/ui/autofill_image_fetcher.cc
+++ b/chromium/components/autofill/core/browser/ui/autofill_image_fetcher.cc
@@ -19,7 +19,9 @@
namespace autofill {
namespace {
+
constexpr char kUmaClientName[] = "AutofillImageFetcher";
+
constexpr net::NetworkTrafficAnnotationTag kCardArtImageTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("autofill_image_fetcher_card_art_image",
R"(
@@ -48,6 +50,11 @@ constexpr net::NetworkTrafficAnnotationTag kCardArtImageTrafficAnnotation =
}
}
})");
+
+// Defines the expiration of the fetched image in the disk cache of the image
+// fetcher.
+constexpr base::TimeDelta kDiskCacheExpiry = base::Minutes(10);
+
} // namespace
ImageFetchOperation::ImageFetchOperation(size_t image_count,
@@ -121,8 +128,7 @@ void AutofillImageFetcher::FetchImageForUrl(
image_fetcher::ImageFetcherParams params(kCardArtImageTrafficAnnotation,
kUmaClientName);
- params.set_hold_for_expiration_interval(base::Minutes(
- features::kAutofillImageFetcherDiskCacheExpirationInMinutes.Get()));
+ params.set_hold_for_expiration_interval(kDiskCacheExpiry);
image_fetcher_->FetchImage(
card_art_url,
base::BindOnce(&AutofillImageFetcher::OnCardArtImageFetched, operation,
diff --git a/chromium/components/autofill/core/browser/ui/autofill_popup_delegate.h b/chromium/components/autofill/core/browser/ui/autofill_popup_delegate.h
index beef2b487f7..b3410873b5c 100644
--- a/chromium/components/autofill/core/browser/ui/autofill_popup_delegate.h
+++ b/chromium/components/autofill/core/browser/ui/autofill_popup_delegate.h
@@ -9,6 +9,7 @@
#include "base/callback_forward.h"
#include "components/autofill/core/browser/ui/popup_types.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
namespace password_manager {
@@ -46,11 +47,14 @@ class AutofillPopupDelegate {
// negative values (see popup_item_ids.h) which have special built-in meanings
// while others have positive values which represents the backend data model
// this suggestion relates to. See 'MakeFrontendID' in BrowserAutofillManager.
- // |backend_id| is the guid of the backend data model. |position| refers to
- // the index of the suggestion in the suggestion list.
+ // |payload| is the payload of the suggestion, and it represents the GUID of
+ // the backend data model. |position| refers to the index of the suggestion in
+ // the suggestion list.
+ // TODO(crbug.com/1335128): Refactor parameters to take in a Suggestion
+ // struct.
virtual void DidAcceptSuggestion(const std::u16string& value,
int frontend_id,
- const std::string& backend_id,
+ const Suggestion::Payload& payload,
int position) = 0;
// Returns whether the given value can be deleted, and if true,
diff --git a/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.cc b/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.cc
index 845c473b997..bb19f564e21 100644
--- a/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.cc
+++ b/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.cc
@@ -24,18 +24,7 @@ CardExpirationDateFixFlowControllerImpl::
CardExpirationDateFixFlowControllerImpl::
~CardExpirationDateFixFlowControllerImpl() {
- if (card_expiration_date_fix_flow_view_)
- card_expiration_date_fix_flow_view_->ControllerGone();
-
- if (shown_ && !had_user_interaction_) {
- AutofillMetrics::LogExpirationDateFixFlowPromptEvent(
- AutofillMetrics::ExpirationDateFixFlowPromptEvent::
- EXPIRATION_DATE_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION);
- LogSaveCreditCardPromptResult(
- SaveCreditCardPromptResult::kInteractedAndIgnored, true,
- AutofillClient::SaveCreditCardOptions()
- .with_should_request_expiration_date_from_user(true));
- }
+ MaybeDestroyExpirationDateFixFlowView(true);
}
void CardExpirationDateFixFlowControllerImpl::Show(
@@ -48,8 +37,7 @@ void CardExpirationDateFixFlowControllerImpl::Show(
card_label_ = card.CardIdentifierStringForAutofillDisplay();
- if (card_expiration_date_fix_flow_view_)
- card_expiration_date_fix_flow_view_->ControllerGone();
+ MaybeDestroyExpirationDateFixFlowView(false);
card_expiration_date_fix_flow_view_ = card_expiration_date_fix_flow_view;
upload_save_card_callback_ = std::move(callback);
@@ -57,6 +45,7 @@ void CardExpirationDateFixFlowControllerImpl::Show(
card_expiration_date_fix_flow_view_->Show();
AutofillMetrics::LogExpirationDateFixFlowPromptShown();
shown_ = true;
+ had_user_interaction_ = false;
}
void CardExpirationDateFixFlowControllerImpl::OnAccepted(
@@ -85,7 +74,7 @@ void CardExpirationDateFixFlowControllerImpl::OnDismissed() {
}
void CardExpirationDateFixFlowControllerImpl::OnDialogClosed() {
- card_expiration_date_fix_flow_view_ = nullptr;
+ MaybeDestroyExpirationDateFixFlowView(false);
}
int CardExpirationDateFixFlowControllerImpl::GetIconId() const {
@@ -132,4 +121,22 @@ std::u16string CardExpirationDateFixFlowControllerImpl::GetInvalidDateError()
IDS_AUTOFILL_SAVE_CARD_UPDATE_EXPIRATION_DATE_ERROR_TRY_AGAIN);
}
+void CardExpirationDateFixFlowControllerImpl::
+ MaybeDestroyExpirationDateFixFlowView(bool controller_gone) {
+ if (card_expiration_date_fix_flow_view_ == nullptr)
+ return;
+ if (controller_gone)
+ card_expiration_date_fix_flow_view_->ControllerGone();
+ if (shown_ && !had_user_interaction_) {
+ AutofillMetrics::LogExpirationDateFixFlowPromptEvent(
+ AutofillMetrics::ExpirationDateFixFlowPromptEvent::
+ EXPIRATION_DATE_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION);
+ LogSaveCreditCardPromptResult(
+ SaveCreditCardPromptResult::kInteractedAndIgnored, true,
+ AutofillClient::SaveCreditCardOptions()
+ .with_should_request_expiration_date_from_user(true));
+ }
+ card_expiration_date_fix_flow_view_ = nullptr;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h b/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h
index 981b4e53467..59a475b7c88 100644
--- a/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h
+++ b/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h
@@ -19,9 +19,7 @@ namespace autofill {
class CardExpirationDateFixFlowView;
// Enables the user to accept or deny expiration date fix flow prompt.
-// Only used on mobile. This class is responsible for its destruction.
-// Destruction is achieved by calling delete when the prompt is
-// dismissed.
+// Only used on mobile.
class CardExpirationDateFixFlowControllerImpl
: public CardExpirationDateFixFlowController {
public:
@@ -34,6 +32,9 @@ class CardExpirationDateFixFlowControllerImpl
~CardExpirationDateFixFlowControllerImpl() override;
+ // Show the card expiration date fix flow view. If another view is triggered
+ // when the current view has not been dismissed by user yet, the current
+ // view will be destroyed.
void Show(CardExpirationDateFixFlowView* card_expiration_date_fix_flow_view,
const CreditCard& card,
base::OnceCallback<void(const std::u16string&,
@@ -71,6 +72,10 @@ class CardExpirationDateFixFlowControllerImpl
// Label of the card describing the network and the last four digits.
std::u16string card_label_;
+
+ // Destroy |card_expiration_date_fix_flow_view_| if it is valid.
+ // |controller_gone| is true if |this| controller is being destroyed.
+ void MaybeDestroyExpirationDateFixFlowView(bool controller_gone);
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc b/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc
index 8e2aab40727..ad4508593e2 100644
--- a/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc
+++ b/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc
@@ -42,6 +42,8 @@ class CardExpirationDateFixFlowControllerImplGenericTest {
weak_ptr_factory_.GetWeakPtr()));
}
+ void OnDialogClosed() { controller_->OnDialogClosed(); }
+
protected:
std::unique_ptr<TestCardExpirationDateFixFlowView>
test_card_expiration_date_fix_flow_view_;
@@ -112,6 +114,24 @@ TEST_F(CardExpirationDateFixFlowControllerImplTest, LogDismissed) {
1);
}
+TEST_F(CardExpirationDateFixFlowControllerImplTest, LogIgnored) {
+ base::HistogramTester histogram_tester;
+ ShowPrompt();
+ ShowPrompt();
+
+ histogram_tester.ExpectBucketCount(
+ "Autofill.ExpirationDateFixFlowPrompt.Events",
+ AutofillMetrics::ExpirationDateFixFlowPromptEvent::
+ EXPIRATION_DATE_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION,
+ 1);
+ OnDialogClosed();
+ histogram_tester.ExpectBucketCount(
+ "Autofill.ExpirationDateFixFlowPrompt.Events",
+ AutofillMetrics::ExpirationDateFixFlowPromptEvent::
+ EXPIRATION_DATE_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION,
+ 2);
+}
+
TEST_F(CardExpirationDateFixFlowControllerImplTest, CardIdentifierString) {
CreditCard card = test::GetCreditCard();
card.SetNickname(u"nickname");
diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc
index 6f87f397406..7e3bb4eec03 100644
--- a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc
+++ b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc
@@ -24,18 +24,7 @@ namespace autofill {
CardNameFixFlowControllerImpl::CardNameFixFlowControllerImpl() {}
CardNameFixFlowControllerImpl::~CardNameFixFlowControllerImpl() {
- if (card_name_fix_flow_view_)
- card_name_fix_flow_view_->ControllerGone();
-
- if (shown_ && !had_user_interaction_) {
- AutofillMetrics::LogCardholderNameFixFlowPromptEvent(
- AutofillMetrics::
- CARDHOLDER_NAME_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION);
- LogSaveCreditCardPromptResult(
- SaveCreditCardPromptResult::kInteractedAndIgnored, true,
- AutofillClient::SaveCreditCardOptions()
- .with_should_request_name_from_user(true));
- }
+ MaybeDestroyCardNameFixFlowView(true);
}
void CardNameFixFlowControllerImpl::Show(
@@ -45,8 +34,7 @@ void CardNameFixFlowControllerImpl::Show(
DCHECK(!name_accepted_callback.is_null());
DCHECK(card_name_fix_flow_view);
- if (card_name_fix_flow_view_)
- card_name_fix_flow_view_->ControllerGone();
+ MaybeDestroyCardNameFixFlowView(false);
card_name_fix_flow_view_ = card_name_fix_flow_view;
name_accepted_callback_ = std::move(name_accepted_callback);
@@ -59,10 +47,11 @@ void CardNameFixFlowControllerImpl::Show(
AutofillMetrics::LogCardholderNameFixFlowPromptEvent(
AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_SHOWN);
shown_ = true;
+ had_user_interaction_ = false;
}
void CardNameFixFlowControllerImpl::OnConfirmNameDialogClosed() {
- card_name_fix_flow_view_ = nullptr;
+ MaybeDestroyCardNameFixFlowView(false);
}
void CardNameFixFlowControllerImpl::OnNameAccepted(const std::u16string& name) {
@@ -135,4 +124,22 @@ std::u16string CardNameFixFlowControllerImpl::GetTitleText() const {
IDS_AUTOFILL_SAVE_CARD_CARDHOLDER_NAME_FIX_FLOW_HEADER);
}
+void CardNameFixFlowControllerImpl::MaybeDestroyCardNameFixFlowView(
+ bool controller_gone) {
+ if (card_name_fix_flow_view_ == nullptr)
+ return;
+ if (controller_gone)
+ card_name_fix_flow_view_->ControllerGone();
+ if (shown_ && !had_user_interaction_) {
+ AutofillMetrics::LogCardholderNameFixFlowPromptEvent(
+ AutofillMetrics::
+ CARDHOLDER_NAME_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION);
+ LogSaveCreditCardPromptResult(
+ SaveCreditCardPromptResult::kInteractedAndIgnored, true,
+ AutofillClient::SaveCreditCardOptions()
+ .with_should_request_name_from_user(true));
+ }
+ card_name_fix_flow_view_ = nullptr;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h
index 0b5090e81d7..2094d849924 100644
--- a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h
+++ b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h
@@ -25,6 +25,9 @@ class CardNameFixFlowControllerImpl : public CardNameFixFlowController {
~CardNameFixFlowControllerImpl() override;
+ // Show the cardholder name fix flow view. If another view is triggered
+ // when the current view has not been dismissed by user yet, the current
+ // view will be destroyed.
void Show(CardNameFixFlowView* card_name_fix_flow_view,
const std::u16string& inferred_cardholder_name,
base::OnceCallback<void(const std::u16string&)> name_callback);
@@ -57,6 +60,10 @@ class CardNameFixFlowControllerImpl : public CardNameFixFlowController {
// Whether the user explicitly accepted or dismissed this prompt.
bool had_user_interaction_ = false;
+
+ // Destroy |card_name_fix_flow_view_| if it is valid. |controller_gone|
+ // is true if |this| controller is being destroyed.
+ void MaybeDestroyCardNameFixFlowView(bool controller_gone);
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc
index 249613c5ad5..c1d78128da4 100644
--- a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc
+++ b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc
@@ -46,6 +46,8 @@ class CardNameFixFlowControllerImplGenericTest {
void AcceptWithEditedName() { controller_->OnNameAccepted(u"Edited Name"); }
+ void OnDialogClosed() { controller_->OnConfirmNameDialogClosed(); }
+
protected:
std::unique_ptr<TestCardNameFixFlowView> test_card_name_fix_flow_view_;
std::unique_ptr<CardNameFixFlowControllerImpl> controller_;
@@ -138,6 +140,26 @@ TEST_F(CardNameFixFlowControllerImplTest, LogUserAcceptedEditedName) {
true, 1);
}
+TEST_F(CardNameFixFlowControllerImplTest, LogIgnored) {
+ base::HistogramTester histogram_tester;
+ ShowPromptWithInferredName();
+ ShowPromptWithInferredName();
+
+ histogram_tester.ExpectBucketCount(
+ "Autofill.CardholderNameFixFlowPrompt.Events",
+ AutofillMetrics::
+ CARDHOLDER_NAME_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION,
+ 1);
+
+ OnDialogClosed();
+
+ histogram_tester.ExpectBucketCount(
+ "Autofill.CardholderNameFixFlowPrompt.Events",
+ AutofillMetrics::
+ CARDHOLDER_NAME_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION,
+ 2);
+}
+
TEST_F(CardNameFixFlowControllerImplTest, LogDismissed) {
base::HistogramTester histogram_tester;
controller_->OnDismissed();
diff --git a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc
index 6463633b43b..d56e629d698 100644
--- a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc
+++ b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc
@@ -194,11 +194,11 @@ std::u16string CardUnmaskPromptControllerImpl::GetInstructionsMessage() const {
int ids;
if (reason_ == AutofillClient::UnmaskCardReason::kAutofill &&
ShouldRequestExpirationDate()) {
- ids = card_.record_type() == autofill::CreditCard::LOCAL_CARD
+ ids = card_.record_type() == CreditCard::LOCAL_CARD
? IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_EXPIRED_LOCAL_CARD
: IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_EXPIRED;
} else {
- ids = card_.record_type() == autofill::CreditCard::LOCAL_CARD
+ ids = card_.record_type() == CreditCard::LOCAL_CARD
? IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_LOCAL_CARD
: IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS;
}
@@ -208,7 +208,7 @@ std::u16string CardUnmaskPromptControllerImpl::GetInstructionsMessage() const {
ids, card_.CardIdentifierStringForAutofillDisplay());
#else
return l10n_util::GetStringUTF16(
- card_.record_type() == autofill::CreditCard::LOCAL_CARD
+ card_.record_type() == CreditCard::LOCAL_CARD
? IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_LOCAL_CARD
: IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS);
#endif
diff --git a/chromium/components/autofill/core/browser/ui/payments/virtual_card_enroll_bubble_controller.h b/chromium/components/autofill/core/browser/ui/payments/virtual_card_enroll_bubble_controller.h
index 4d5697619fb..1141e2cd060 100644
--- a/chromium/components/autofill/core/browser/ui/payments/virtual_card_enroll_bubble_controller.h
+++ b/chromium/components/autofill/core/browser/ui/payments/virtual_card_enroll_bubble_controller.h
@@ -8,6 +8,10 @@
#include "components/autofill/core/browser/ui/payments/payments_bubble_closed_reasons.h"
#include "url/gurl.h"
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/scoped_java_ref.h"
+#endif
+
#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_VIRTUAL_CARD_ENROLL_BUBBLE_CONTROLLER_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_VIRTUAL_CARD_ENROLL_BUBBLE_CONTROLLER_H_
@@ -68,6 +72,15 @@ class VirtualCardEnrollBubbleController {
// Returns whether the omnibox icon should be visible.
virtual bool IsIconVisible() const = 0;
+
+#if BUILDFLAG(IS_ANDROID)
+ // Returns either the fully initialized java delegate object or a is_null()
+ // reference if the creation failed. By using this method, the controller will
+ // try to recreate the java object if it failed previously (e.g. because there
+ // was no native window available).
+ virtual base::android::ScopedJavaGlobalRef<jobject>
+ GetOrCreateJavaDelegate() = 0;
+#endif
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/ui/popup_item_ids.h b/chromium/components/autofill/core/browser/ui/popup_item_ids.h
index d0724a462e7..10d1a3e3d82 100644
--- a/chromium/components/autofill/core/browser/ui/popup_item_ids.h
+++ b/chromium/components/autofill/core/browser/ui/popup_item_ids.h
@@ -35,7 +35,9 @@ enum PopupItemId {
POPUP_ITEM_ID_PASSWORD_ACCOUNT_STORAGE_EMPTY = -25,
POPUP_ITEM_ID_MIXED_FORM_MESSAGE = -26,
POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY = -27,
- POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL = -28
+ POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL = -28,
+ POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY = -29,
+ POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS = -30
};
// List of `PopupItemId` that trigger filling a value into an input element
@@ -47,7 +49,8 @@ constexpr PopupItemId kItemsTriggeringFieldFilling[] = {
POPUP_ITEM_ID_USERNAME_ENTRY,
POPUP_ITEM_ID_ACCOUNT_STORAGE_PASSWORD_ENTRY,
POPUP_ITEM_ID_ACCOUNT_STORAGE_USERNAME_ENTRY,
- POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY};
+ POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY,
+ POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/ui/region_combobox_model.cc b/chromium/components/autofill/core/browser/ui/region_combobox_model.cc
index 6439ccb1f07..42626aff427 100644
--- a/chromium/components/autofill/core/browser/ui/region_combobox_model.cc
+++ b/chromium/components/autofill/core/browser/ui/region_combobox_model.cc
@@ -67,14 +67,6 @@ bool RegionComboboxModel::IsItemSeparatorAt(int index) const {
return regions_[index].first.empty();
}
-void RegionComboboxModel::AddObserver(ui::ComboboxModelObserver* observer) {
- observers_.AddObserver(observer);
-}
-
-void RegionComboboxModel::RemoveObserver(ui::ComboboxModelObserver* observer) {
- observers_.RemoveObserver(observer);
-}
-
void RegionComboboxModel::OnRegionDataLoaded(
const std::vector<const ::i18n::addressinput::RegionData*>& regions) {
// The RegionDataLoader will eventually self destruct after this call.
@@ -95,7 +87,7 @@ void RegionComboboxModel::OnRegionDataLoaded(
failed_to_load_data_ = true;
}
- for (auto& observer : observers_) {
+ for (auto& observer : observers()) {
observer.OnComboboxModelChanged(this);
}
}
diff --git a/chromium/components/autofill/core/browser/ui/region_combobox_model.h b/chromium/components/autofill/core/browser/ui/region_combobox_model.h
index d1b5d8b9db0..52cffd5d006 100644
--- a/chromium/components/autofill/core/browser/ui/region_combobox_model.h
+++ b/chromium/components/autofill/core/browser/ui/region_combobox_model.h
@@ -54,8 +54,6 @@ class RegionComboboxModel : public ui::ComboboxModel {
int GetItemCount() const override;
std::u16string GetItemAt(int index) const override;
bool IsItemSeparatorAt(int index) const override;
- void AddObserver(ui::ComboboxModelObserver* observer) override;
- void RemoveObserver(ui::ComboboxModelObserver* observer) override;
private:
// Callback for the RegionDataLoader.
@@ -72,9 +70,6 @@ class RegionComboboxModel : public ui::ComboboxModel {
// List of <code, name> pairs for ADDRESS_HOME_STATE combobox values;
std::vector<std::pair<std::string, std::string>> regions_;
- // To be called when the data for the given country code was loaded.
- base::ObserverList<ui::ComboboxModelObserver> observers_;
-
// Weak pointer factory.
base::WeakPtrFactory<RegionComboboxModel> weak_factory_{this};
};
diff --git a/chromium/components/autofill/core/browser/ui/suggestion.cc b/chromium/components/autofill/core/browser/ui/suggestion.cc
index f344d2a5655..a61ee597167 100644
--- a/chromium/components/autofill/core/browser/ui/suggestion.cc
+++ b/chromium/components/autofill/core/browser/ui/suggestion.cc
@@ -10,21 +10,58 @@
namespace autofill {
+Suggestion::Text::Text() = default;
+
+Suggestion::Text::Text(std::u16string value,
+ IsPrimary is_primary,
+ ShouldTruncate should_truncate)
+ : value(value), is_primary(is_primary), should_truncate(should_truncate) {}
+
+Suggestion::Text::Text(const Text& other) = default;
+Suggestion::Text::Text(Text& other) = default;
+
+Suggestion::Text& Suggestion::Text::operator=(const Text& other) = default;
+Suggestion::Text& Suggestion::Text::operator=(Text&& other) = default;
+
+Suggestion::Text::~Text() = default;
+
+bool Suggestion::Text::operator==(const Suggestion::Text& text) const {
+ return value == text.value && is_primary == text.is_primary &&
+ should_truncate == text.should_truncate;
+}
+
+bool Suggestion::Text::operator!=(const Suggestion::Text& text) const {
+ return !operator==(text);
+}
+
Suggestion::Suggestion() = default;
-Suggestion::Suggestion(const Suggestion& other) = default;
-Suggestion::Suggestion(Suggestion&& other) = default;
-Suggestion::Suggestion(std::u16string value) : value(std::move(value)) {}
+Suggestion::Suggestion(std::u16string main_text)
+ : main_text(std::move(main_text), Text::IsPrimary(true)) {}
-Suggestion::Suggestion(base::StringPiece value,
+Suggestion::Suggestion(base::StringPiece main_text,
base::StringPiece label,
std::string icon,
int frontend_id)
: frontend_id(frontend_id),
- value(base::UTF8ToUTF16(value)),
+ main_text(base::UTF8ToUTF16(main_text), Text::IsPrimary(true)),
label(base::UTF8ToUTF16(label)),
icon(std::move(icon)) {}
+Suggestion::Suggestion(base::StringPiece main_text,
+ base::StringPiece minor_text,
+ base::StringPiece label,
+ std::string icon,
+ int frontend_id)
+ : frontend_id(frontend_id),
+ main_text(base::UTF8ToUTF16(main_text), Text::IsPrimary(true)),
+ minor_text(base::UTF8ToUTF16(minor_text)),
+ label(base::UTF8ToUTF16(label)),
+ icon(std::move(icon)) {}
+
+Suggestion::Suggestion(const Suggestion& other) = default;
+Suggestion::Suggestion(Suggestion&& other) = default;
+
Suggestion& Suggestion::operator=(const Suggestion& other) = default;
Suggestion& Suggestion::operator=(Suggestion&& other) = default;
diff --git a/chromium/components/autofill/core/browser/ui/suggestion.h b/chromium/components/autofill/core/browser/ui/suggestion.h
index 1d5665c4906..ad9eb7894ae 100644
--- a/chromium/components/autofill/core/browser/ui/suggestion.h
+++ b/chromium/components/autofill/core/browser/ui/suggestion.h
@@ -10,58 +10,106 @@
#include "base/strings/string_piece.h"
#include "base/types/strong_alias.h"
#include "build/build_config.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
+
namespace autofill {
struct Suggestion {
using IsLoading = base::StrongAlias<class IsLoadingTag, bool>;
+ using Payload = absl::variant<std::string, GURL>;
enum MatchMode {
PREFIX_MATCH, // for prefix matched suggestions;
SUBSTRING_MATCH // for substring matched suggestions;
};
- Suggestion();
- Suggestion(const Suggestion& other);
- Suggestion(Suggestion&& other);
+ // The text information shown on the UI layer for a Suggestion.
+ struct Text {
+ using IsPrimary = base::StrongAlias<class IsPrimaryTag, bool>;
+ using ShouldTruncate = base::StrongAlias<class ShouldTruncateTag, bool>;
+
+ Text();
+ explicit Text(std::u16string value,
+ IsPrimary is_primary = IsPrimary(false),
+ ShouldTruncate should_truncate = ShouldTruncate(false));
+ Text(const Text& other);
+ Text(Text& other);
+ Text& operator=(const Text& other);
+ Text& operator=(Text&& other);
+ ~Text();
+ bool operator==(const Suggestion::Text& text) const;
+ bool operator!=(const Suggestion::Text& text) const;
+
+ // The text value to be shown.
+ std::u16string value;
+
+ // Whether the text should be shown with a primary style.
+ IsPrimary is_primary = IsPrimary(false);
+
+ // Whether the text should be truncated if the bubble width is limited.
+ ShouldTruncate should_truncate = ShouldTruncate(false);
+ };
- explicit Suggestion(std::u16string value);
+ Suggestion();
+ explicit Suggestion(std::u16string main_text);
// Constructor for unit tests. It will convert the strings from UTF-8 to
// UTF-16.
- Suggestion(base::StringPiece value,
+ Suggestion(base::StringPiece main_text,
base::StringPiece label,
std::string icon,
int frontend_id);
-
+ Suggestion(base::StringPiece main_text,
+ base::StringPiece minor_text,
+ base::StringPiece label,
+ std::string icon,
+ int frontend_id);
+ Suggestion(const Suggestion& other);
+ Suggestion(Suggestion&& other);
Suggestion& operator=(const Suggestion& other);
Suggestion& operator=(Suggestion&& other);
-
~Suggestion();
- // GUID generated by the backend layer. This identifies the exact autofill
- // profile that generated this suggestion.
- std::string backend_id;
+ template <typename T>
+ T GetPayload() const {
+ return absl::holds_alternative<T>(payload) ? absl::get<T>(payload) : T{};
+ }
+
+ // Payload generated by the backend layer. This payload is either a GUID that
+ // identifies the exact autofill profile that generated this suggestion, or a
+ // GURL that the suggestion should navigate to upon being accepted.
+ Payload payload;
+ // TODO(crbug.com/1325509): Convert |frontend_id| from an int to a
+ // PopupItemId.
// ID for the frontend to use in identifying the particular result. Positive
// values are sent over IPC to identify the item selected. Negative values
// (see popup_item_ids.h) have special built-in meanings.
int frontend_id = 0;
- // The text that will be filled in to the focused field and is displayed as
- // the main text in the suggestion. Its style depends on |is_value_secondary|.
- std::u16string value;
+ // The texts that will be displayed on the first line in a suggestion. The
+ // order of showing the two texts on the first line depends on whether it is
+ // in RTL languages. The |main_text| includes the text value to be filled in
+ // the form, while the |minor_text| includes other supplementary text value to
+ // be shown also on the first line.
+ Text main_text;
+ Text minor_text;
// The text displayed on the second line in a suggestion.
std::u16string label;
+
// A label to be shown beneath |label| that will display information about any
// credit card offers or rewards.
std::u16string offer_label;
+
// Used only for passwords to show the password value.
// Also used to display an extra line of information if two line
// display is enabled.
std::u16string additional_label;
+
// Contains an image to display for the suggestion.
gfx::Image custom_icon;
@@ -74,6 +122,7 @@ struct Suggestion {
// TODO(crbug.com/1019660): Identify icons with enum instead of strings.
// If |custom_icon| is empty, the name of the fallback built-in icon.
std::string icon;
+
// An icon that appears after the suggestion in the suggestion view. For
// passwords, this icon string shows whether the suggestion originates from
// local or account store. It is also used on the settings entry for the
@@ -81,11 +130,12 @@ struct Suggestion {
// cards. It also holds Google Password Manager icon on the settings entry for
// the passwords Autofill popup.
std::string trailing_icon;
+
MatchMode match = PREFIX_MATCH;
- // Whether |value| should be displayed as secondary text.
- bool is_value_secondary = false;
+
// Whether suggestion was interacted with and is now in a loading state.
IsLoading is_loading = IsLoading(false);
+
// The In-Product-Help feature that should be shown for the suggestion.
std::string feature_for_iph;
diff --git a/chromium/components/autofill/core/browser/ui/suggestion_selection.cc b/chromium/components/autofill/core/browser/ui/suggestion_selection.cc
index 77930d5ab03..64a56ebac3c 100644
--- a/chromium/components/autofill/core/browser/ui/suggestion_selection.cc
+++ b/chromium/components/autofill/core/browser/ui/suggestion_selection.cc
@@ -87,7 +87,7 @@ std::vector<Suggestion> GetPrefixMatchedSuggestions(
profile->GetRawInfo(type.GetStorableType()) == raw_field_contents) {
continue;
}
-#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+#endif // BUILDFLAG(IS_ANDROID)
std::u16string value =
GetInfoInOneLine(profile, type, comparator.app_locale());
@@ -98,7 +98,8 @@ std::vector<Suggestion> GetPrefixMatchedSuggestions(
std::u16string suggestion_canon = comparator.NormalizeForComparison(value);
if (IsValidSuggestionForFieldContents(
suggestion_canon, field_contents_canon, type,
- /* is_masked_server_card= */ false, &prefix_matched_suggestion)) {
+ /* is_masked_server_card= */ false, field_is_autofilled,
+ &prefix_matched_suggestion)) {
matched_profiles->push_back(profile);
if (type.group() == FieldTypeGroup::kPhoneHome) {
@@ -106,10 +107,10 @@ std::vector<Suggestion> GetPrefixMatchedSuggestions(
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
format_phone = base::FeatureList::IsEnabled(
- autofill::features::kAutofillUseMobileLabelDisambiguation);
+ features::kAutofillUseMobileLabelDisambiguation);
#else
format_phone = base::FeatureList::IsEnabled(
- autofill::features::kAutofillUseImprovedLabelDisambiguation);
+ features::kAutofillUseImprovedLabelDisambiguation);
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
if (format_phone) {
@@ -125,7 +126,7 @@ std::vector<Suggestion> GetPrefixMatchedSuggestions(
}
suggestions.emplace_back(value);
- suggestions.back().backend_id = profile->guid();
+ suggestions.back().payload = profile->guid();
suggestions.back().match = prefix_matched_suggestion
? Suggestion::PREFIX_MATCH
: Suggestion::SUBSTRING_MATCH;
@@ -165,7 +166,8 @@ std::vector<Suggestion> GetUniqueSuggestions(
AutofillProfile* profile_b = matched_profiles[j];
// Check if profile A is a subset of profile B. If not, continue.
if (i == j ||
- !comparator.Compare(suggestions[i].value, suggestions[j].value) ||
+ !comparator.Compare(suggestions[i].main_text.value,
+ suggestions[j].main_text.value) ||
!profile_a->IsSubsetOfForFieldSet(comparator, *profile_b, app_locale,
types)) {
continue;
@@ -194,6 +196,7 @@ bool IsValidSuggestionForFieldContents(std::u16string suggestion_canon,
std::u16string field_contents_canon,
const AutofillType& type,
bool is_masked_server_card,
+ bool field_is_autofilled,
bool* is_prefix_matched) {
*is_prefix_matched = true;
@@ -209,12 +212,29 @@ bool IsValidSuggestionForFieldContents(std::u16string suggestion_canon,
// For card number fields, suggest the card if:
// - the number matches any part of the card, or
// - it's a masked card and there are 6 or fewer typed so far.
+ // - it's a masked card, field is autofilled, and the last 4 digits in the
+ // field match the last 4 digits of the card.
if (type.GetStorableType() == CREDIT_CARD_NUMBER) {
- if (suggestion_canon.find(field_contents_canon) == std::u16string::npos &&
- (!is_masked_server_card || field_contents_canon.size() >= 6)) {
- return false;
+ if (suggestion_canon.find(field_contents_canon) != std::u16string::npos) {
+ return true;
}
- return true;
+
+ if (is_masked_server_card) {
+ if (field_contents_canon.length() < 6) {
+ return true;
+ }
+ if (field_is_autofilled) {
+ int field_contents_length = field_contents_canon.length();
+ DCHECK(field_contents_length >= 4);
+ if (suggestion_canon.find(field_contents_canon.substr(
+ field_contents_length - 4, field_contents_length)) !=
+ std::u16string::npos) {
+ return true;
+ }
+ }
+ }
+
+ return false;
}
if (base::StartsWith(suggestion_canon, field_contents_canon,
@@ -271,12 +291,11 @@ void PrepareSuggestions(const std::vector<std::u16string>& labels,
for (size_t i = 0; i < labels.size(); ++i) {
std::u16string label = labels[i];
- bool text_inserted =
- suggestion_text
- .insert(comparator.NormalizeForComparison(
- (*suggestions)[i].value + label,
- autofill::AutofillProfileComparator::DISCARD_WHITESPACE))
- .second;
+ bool text_inserted = suggestion_text
+ .insert(comparator.NormalizeForComparison(
+ (*suggestions)[i].main_text.value + label,
+ AutofillProfileComparator::DISCARD_WHITESPACE))
+ .second;
if (text_inserted) {
if (index_to_add_suggestion != i) {
@@ -290,8 +309,9 @@ void PrepareSuggestions(const std::vector<std::u16string>& labels,
// cases, e.g. when a credit card form contains a zip code field and the
// user clicks on the zip code, a suggestion's value and the label
// produced for it may both be a zip code.
- if (!comparator.Compare((*suggestions)[index_to_add_suggestion].value,
- labels[i])) {
+ if (!comparator.Compare(
+ (*suggestions)[index_to_add_suggestion].main_text.value,
+ labels[i])) {
(*suggestions)[index_to_add_suggestion].label = labels[i];
}
++index_to_add_suggestion;
diff --git a/chromium/components/autofill/core/browser/ui/suggestion_selection.h b/chromium/components/autofill/core/browser/ui/suggestion_selection.h
index 1b4c650686d..389111faef0 100644
--- a/chromium/components/autofill/core/browser/ui/suggestion_selection.h
+++ b/chromium/components/autofill/core/browser/ui/suggestion_selection.h
@@ -50,13 +50,15 @@ std::vector<Suggestion> GetUniqueSuggestions(
std::vector<AutofillProfile*>* unique_matched_profiles);
// Returns whether the |suggestion_canon| is valid considering the
-// |field_contents_canon|, the |type| and |is_masked_server_card|. Assigns true
-// to |is_prefix_matched| if the |field_contents_canon| is a prefix to
-// |suggestion_canon|, assigns false otherwise.
+// |field_contents_canon|, the |type|, |is_masked_server_card|, and
+// |field_is_autofilled|. Assigns true to |is_prefix_matched| if the
+// |field_contents_canon| is a prefix to |suggestion_canon|, assigns false
+// otherwise.
bool IsValidSuggestionForFieldContents(std::u16string suggestion_canon,
std::u16string field_contents_canon,
const AutofillType& type,
bool is_masked_server_card,
+ bool field_is_autofilled,
bool* is_prefix_matched);
// Removes profiles that haven't been used after |min_last_used| from
diff --git a/chromium/components/autofill/core/browser/ui/suggestion_selection_unittest.cc b/chromium/components/autofill/core/browser/ui/suggestion_selection_unittest.cc
index 295586edc55..6d563712e7f 100644
--- a/chromium/components/autofill/core/browser/ui/suggestion_selection_unittest.cc
+++ b/chromium/components/autofill/core/browser/ui/suggestion_selection_unittest.cc
@@ -120,7 +120,11 @@ TEST_F(SuggestionSelectionTest,
ASSERT_EQ(1U, suggestions.size());
ASSERT_EQ(1U, matched_profiles.size());
- EXPECT_THAT(suggestions, ElementsAre(Field(&Suggestion::value, u"Marion")));
+ EXPECT_THAT(
+ suggestions,
+ ElementsAre(Field(
+ &Suggestion::main_text,
+ Suggestion::Text(u"Marion", Suggestion::Text::IsPrimary(true)))));
}
TEST_F(SuggestionSelectionTest, GetPrefixMatchedSuggestions_NoMatchingProfile) {
@@ -173,7 +177,10 @@ TEST_F(SuggestionSelectionTest, GetPrefixMatchedSuggestions_LimitProfiles) {
ASSERT_EQ(kMaxSuggestedProfilesCount, suggestions.size());
ASSERT_EQ(kMaxSuggestedProfilesCount, matched_profiles.size());
- EXPECT_THAT(suggestions, Each(Field(&Suggestion::value, Not(u"Marie"))));
+ EXPECT_THAT(suggestions,
+ Each(Field(&Suggestion::main_text,
+ Not(Suggestion::Text(
+ u"Marie", Suggestion::Text::IsPrimary(true))))));
EXPECT_THAT(matched_profiles,
Each(ResultOf(
@@ -201,8 +208,11 @@ TEST_F(SuggestionSelectionTest, GetUniqueSuggestions_SingleDedupe) {
ASSERT_EQ(1U, unique_suggestions.size());
ASSERT_EQ(1U, unique_matched_profiles.size());
- EXPECT_THAT(unique_suggestions,
- ElementsAre(Field(&Suggestion::value, u"Bob")));
+ EXPECT_THAT(
+ unique_suggestions,
+ ElementsAre(
+ Field(&Suggestion::main_text,
+ Suggestion::Text(u"Bob", Suggestion::Text::IsPrimary(true)))));
}
TEST_F(SuggestionSelectionTest, GetUniqueSuggestions_MultipleDedupe) {
@@ -227,10 +237,15 @@ TEST_F(SuggestionSelectionTest, GetUniqueSuggestions_MultipleDedupe) {
ASSERT_EQ(3U, unique_suggestions.size());
ASSERT_EQ(3U, unique_matched_profiles.size());
- EXPECT_THAT(unique_suggestions,
- ElementsAre(Field(&Suggestion::value, u"Bob"),
- Field(&Suggestion::value, u"Bob"),
- Field(&Suggestion::value, u"Mary")));
+ EXPECT_THAT(
+ unique_suggestions,
+ ElementsAre(
+ Field(&Suggestion::main_text,
+ Suggestion::Text(u"Bob", Suggestion::Text::IsPrimary(true))),
+ Field(&Suggestion::main_text,
+ Suggestion::Text(u"Bob", Suggestion::Text::IsPrimary(true))),
+ Field(&Suggestion::main_text,
+ Suggestion::Text(u"Mary", Suggestion::Text::IsPrimary(true)))));
}
TEST_F(SuggestionSelectionTest, GetUniqueSuggestions_DedupeLimit) {
@@ -261,7 +276,7 @@ TEST_F(SuggestionSelectionTest, GetUniqueSuggestions_DedupeLimit) {
// All profiles are different.
for (size_t i = 0; i < unique_suggestions.size(); i++) {
ASSERT_EQ(ASCIIToUTF16(base::StringPrintf("Bob %zu", i)),
- unique_suggestions[i].value);
+ unique_suggestions[i].main_text.value);
}
}
@@ -451,10 +466,15 @@ TEST_F(SuggestionSelectionTest,
// duplicates with a lower rank are removed.
EXPECT_THAT(
suggestions,
- ElementsAre(AllOf(Field(&Suggestion::value, u"Jon Snow"),
- Field(&Suggestion::label, u"2 Beyond-the-Wall Rd")),
- AllOf(Field(&Suggestion::value, u"Jon Snow"),
- Field(&Suggestion::label, u"1 Winterfell Ln"))));
+ ElementsAre(
+ AllOf(Field(&Suggestion::main_text,
+ Suggestion::Text(u"Jon Snow",
+ Suggestion::Text::IsPrimary(true))),
+ Field(&Suggestion::label, u"2 Beyond-the-Wall Rd")),
+ AllOf(Field(&Suggestion::main_text,
+ Suggestion::Text(u"Jon Snow",
+ Suggestion::Text::IsPrimary(true))),
+ Field(&Suggestion::label, u"1 Winterfell Ln"))));
}
TEST_F(SuggestionSelectionTest,
@@ -469,12 +489,19 @@ TEST_F(SuggestionSelectionTest,
EXPECT_THAT(
suggestions,
- ElementsAre(AllOf(Field(&Suggestion::value, u"Sansa"),
- Field(&Suggestion::label, u"1 Winterfell Ln")),
- AllOf(Field(&Suggestion::value, u"Sansa"),
- Field(&Suggestion::label, u"")),
- AllOf(Field(&Suggestion::value, u"Brienne"),
- Field(&Suggestion::label, u"1 Winterfell Ln"))));
+ ElementsAre(
+ AllOf(Field(&Suggestion::main_text,
+ Suggestion::Text(u"Sansa",
+ Suggestion::Text::IsPrimary(true))),
+ Field(&Suggestion::label, u"1 Winterfell Ln")),
+ AllOf(Field(&Suggestion::main_text,
+ Suggestion::Text(u"Sansa",
+ Suggestion::Text::IsPrimary(true))),
+ Field(&Suggestion::label, u"")),
+ AllOf(Field(&Suggestion::main_text,
+ Suggestion::Text(u"Brienne",
+ Suggestion::Text::IsPrimary(true))),
+ Field(&Suggestion::label, u"1 Winterfell Ln"))));
}
TEST_F(SuggestionSelectionTest, PrepareSuggestions_SameStringInValueAndLabel) {
@@ -484,8 +511,11 @@ TEST_F(SuggestionSelectionTest, PrepareSuggestions_SameStringInValueAndLabel) {
PrepareSuggestions(labels, &suggestions, comparator_);
EXPECT_THAT(suggestions,
- ElementsAre(AllOf(Field(&Suggestion::value, u"4 Mañana Road"),
- Field(&Suggestion::label, std::u16string()))));
+ ElementsAre(AllOf(
+ Field(&Suggestion::main_text,
+ Suggestion::Text(u"4 Mañana Road",
+ Suggestion::Text::IsPrimary(true))),
+ Field(&Suggestion::label, std::u16string()))));
}
} // namespace suggestion_selection
diff --git a/chromium/components/autofill/core/browser/ui/suggestion_test_helpers.h b/chromium/components/autofill/core/browser/ui/suggestion_test_helpers.h
index de639bc7c10..a751bdef0fe 100644
--- a/chromium/components/autofill/core/browser/ui/suggestion_test_helpers.h
+++ b/chromium/components/autofill/core/browser/ui/suggestion_test_helpers.h
@@ -60,13 +60,13 @@ inline testing::Matcher<const std::vector<Suggestion>&> SuggestionVectorIdsAre(
elts_are_matcher, &Suggestion::frontend_id));
}
-// Like SuggestionVectorIdsAre above, but tests the values.
+// Like SuggestionVectorIdsAre above, but tests the main_texts.
template <class EltsAreMatcher>
inline testing::Matcher<const std::vector<Suggestion>&>
-SuggestionVectorValuesAre(const EltsAreMatcher& elts_are_matcher) {
+SuggestionVectorMainTextsAre(const EltsAreMatcher& elts_are_matcher) {
return testing::MakeMatcher(
- new SuggestionVectorMembersAreMatcher<std::u16string>(
- elts_are_matcher, &Suggestion::value));
+ new SuggestionVectorMembersAreMatcher<Suggestion::Text>(
+ elts_are_matcher, &Suggestion::main_text));
}
// Like SuggestionVectorIdsAre above, but tests the labels.
diff --git a/chromium/components/autofill/core/browser/validation.cc b/chromium/components/autofill/core/browser/validation.cc
index eef75315494..53165336edc 100644
--- a/chromium/components/autofill/core/browser/validation.cc
+++ b/chromium/components/autofill/core/browser/validation.cc
@@ -134,8 +134,7 @@ bool IsValidCreditCardNumberForBasicCardNetworks(
// The type check is cheaper than the credit card number check.
const std::string basic_card_issuer_network =
- autofill::data_util::GetPaymentRequestData(
- CreditCard::GetCardNetwork(text))
+ data_util::GetPaymentRequestData(CreditCard::GetCardNetwork(text))
.basic_card_issuer_network;
if (!supported_basic_card_networks.count(basic_card_issuer_network)) {
*error_message = l10n_util::GetStringUTF16(
diff --git a/chromium/components/autofill/core/browser/webdata/OWNERS b/chromium/components/autofill/core/browser/webdata/OWNERS
index 44502b7a908..5eb1e8f63fa 100644
--- a/chromium/components/autofill/core/browser/webdata/OWNERS
+++ b/chromium/components/autofill/core/browser/webdata/OWNERS
@@ -1,8 +1,5 @@
-per-file *sync_bridge*=jkrcal@chromium.org
per-file *sync_bridge*=file://components/sync/OWNERS
-per-file autofill_profile_sync_difference_tracker*=jkrcal@chromium.org
per-file autofill_profile_sync_difference_tracker*=file://components/sync/OWNERS
-per-file *type_controller*=jkrcal@chromium.org
per-file *type_controller*=file://components/sync/OWNERS
diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
index 888ef391729..f4c74a05db9 100644
--- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
@@ -14,6 +14,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/memory/raw_ptr.h"
+#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/proto/autofill_sync.pb.h"
@@ -25,7 +26,6 @@
#include "components/sync/model/mutable_data_batch.h"
#include "components/sync/model/sync_metadata_store_change_list.h"
#include "components/sync/protocol/entity_data.h"
-#include "net/base/escape.h"
using absl::optional;
using base::Time;
@@ -61,9 +61,9 @@ void* AutocompleteSyncBridgeUserDataKey() {
}
std::string EscapeIdentifiers(const AutofillSpecifics& specifics) {
- return net::EscapePath(specifics.name()) +
+ return base::EscapePath(specifics.name()) +
std::string(kAutocompleteTagDelimiter) +
- net::EscapePath(specifics.value());
+ base::EscapePath(specifics.value());
}
std::unique_ptr<EntityData> CreateEntityData(const AutofillEntry& entry) {
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc
deleted file mode 100644
index f0ed95eb971..00000000000
--- a/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018 The Chromium 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/autofill/core/browser/webdata/autofill_profile_model_type_controller.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "components/autofill/core/common/autofill_prefs.h"
-#include "components/prefs/pref_service.h"
-#include "components/sync/driver/sync_service.h"
-
-namespace browser_sync {
-
-AutofillProfileModelTypeController::AutofillProfileModelTypeController(
- std::unique_ptr<syncer::ModelTypeControllerDelegate>
- delegate_for_full_sync_mode,
- PrefService* pref_service,
- syncer::SyncService* sync_service)
- : ModelTypeController(syncer::AUTOFILL_PROFILE,
- std::move(delegate_for_full_sync_mode)),
- pref_service_(pref_service),
- sync_service_(sync_service) {
- pref_registrar_.Init(pref_service_);
- pref_registrar_.Add(
- autofill::prefs::kAutofillProfileEnabled,
- base::BindRepeating(
- &AutofillProfileModelTypeController::OnUserPrefChanged,
- base::Unretained(this)));
-}
-
-AutofillProfileModelTypeController::~AutofillProfileModelTypeController() =
- default;
-
-syncer::DataTypeController::PreconditionState
-AutofillProfileModelTypeController::GetPreconditionState() const {
- DCHECK(CalledOnValidThread());
- // Require the user-visible pref to be enabled to sync Autofill Profile data.
- return autofill::prefs::IsAutofillProfileEnabled(pref_service_)
- ? PreconditionState::kPreconditionsMet
- : PreconditionState::kMustStopAndClearData;
-}
-
-void AutofillProfileModelTypeController::OnUserPrefChanged() {
- DCHECK(CalledOnValidThread());
- sync_service_->DataTypePreconditionChanged(type());
-}
-
-} // namespace browser_sync
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h b/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h
deleted file mode 100644
index a5ccc42a0a1..00000000000
--- a/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2018 The Chromium 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 COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_MODEL_TYPE_CONTROLLER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_MODEL_TYPE_CONTROLLER_H_
-
-#include <memory>
-
-#include "base/memory/raw_ptr.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/sync/driver/model_type_controller.h"
-
-class PrefService;
-
-namespace syncer {
-class ModelTypeControllerDelegate;
-class SyncService;
-} // namespace syncer
-
-namespace browser_sync {
-
-// Controls syncing of the AUTOFILL_PROFILE data type.
-class AutofillProfileModelTypeController : public syncer::ModelTypeController {
- public:
- AutofillProfileModelTypeController(
- std::unique_ptr<syncer::ModelTypeControllerDelegate>
- delegate_for_full_sync_mode,
- PrefService* pref_service,
- syncer::SyncService* sync_service);
-
- AutofillProfileModelTypeController(
- const AutofillProfileModelTypeController&) = delete;
- AutofillProfileModelTypeController& operator=(
- const AutofillProfileModelTypeController&) = delete;
-
- ~AutofillProfileModelTypeController() override;
-
- // DataTypeController overrides.
- PreconditionState GetPreconditionState() const override;
-
- private:
- // Callback for changes to the autofill pref.
- void OnUserPrefChanged();
-
- const raw_ptr<PrefService> pref_service_;
- const raw_ptr<syncer::SyncService> sync_service_;
-
- // Registrar for listening to prefs::kAutofillProfileEnabled.
- PrefChangeRegistrar pref_registrar_;
-};
-
-} // namespace browser_sync
-
-#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_MODEL_TYPE_CONTROLLER_H_
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
index cecd4e6645e..9df1228ce92 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
@@ -39,6 +39,8 @@ sync_pb::WalletMaskedCreditCard::WalletCardType WalletCardTypeFromCardNetwork(
return sync_pb::WalletMaskedCreditCard::UNIONPAY;
if (network == kVisaCard)
return sync_pb::WalletMaskedCreditCard::VISA;
+ if (network == kEloCard)
+ return sync_pb::WalletMaskedCreditCard::ELO;
// Some cards aren't supported by the client, so just return unknown.
return sync_pb::WalletMaskedCreditCard::UNKNOWN;
@@ -59,6 +61,8 @@ const char* CardNetworkFromWalletCardType(
return kUnionPay;
case sync_pb::WalletMaskedCreditCard::VISA:
return kVisaCard;
+ case sync_pb::WalletMaskedCreditCard::ELO:
+ return kEloCard;
// These aren't supported by the client, so just declare a generic card.
case sync_pb::WalletMaskedCreditCard::MAESTRO:
@@ -337,96 +341,105 @@ void SetAutofillOfferSpecificsFromOfferData(
const AutofillOfferData& offer_data,
sync_pb::AutofillOfferSpecifics* offer_specifics) {
// General offer data:
- offer_specifics->set_id(offer_data.offer_id);
- offer_specifics->set_offer_details_url(offer_data.offer_details_url.spec());
- for (const GURL& merchant_origin : offer_data.merchant_origins) {
+ offer_specifics->set_id(offer_data.GetOfferId());
+ offer_specifics->set_offer_details_url(
+ offer_data.GetOfferDetailsUrl().spec());
+ for (const GURL& merchant_origin : offer_data.GetMerchantOrigins()) {
offer_specifics->add_merchant_domain(merchant_origin.spec());
}
offer_specifics->set_offer_expiry_date(
- (offer_data.expiry - base::Time::UnixEpoch()).InSeconds());
+ (offer_data.GetExpiry() - base::Time::UnixEpoch()).InSeconds());
offer_specifics->mutable_display_strings()->set_value_prop_text(
- offer_data.display_strings.value_prop_text);
+ offer_data.GetDisplayStrings().value_prop_text);
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
offer_specifics->mutable_display_strings()->set_see_details_text_mobile(
- offer_data.display_strings.see_details_text);
+ offer_data.GetDisplayStrings().see_details_text);
offer_specifics->mutable_display_strings()
->set_usage_instructions_text_mobile(
- offer_data.display_strings.usage_instructions_text);
+ offer_data.GetDisplayStrings().usage_instructions_text);
#else
offer_specifics->mutable_display_strings()->set_see_details_text_desktop(
- offer_data.display_strings.see_details_text);
+ offer_data.GetDisplayStrings().see_details_text);
offer_specifics->mutable_display_strings()
->set_usage_instructions_text_desktop(
- offer_data.display_strings.usage_instructions_text);
+ offer_data.GetDisplayStrings().usage_instructions_text);
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
// Because card_linked_offer_data and promo_code_offer_data are a oneof,
// setting one will clear the other. We should figure out which one we care
// about.
- if (offer_data.promo_code == "") {
+ if (offer_data.GetPromoCode().empty()) {
// Card-linked offer fields (promo code is empty):
- for (int64_t instrument_id : offer_data.eligible_instrument_id) {
+ for (int64_t instrument_id : offer_data.GetEligibleInstrumentIds()) {
offer_specifics->mutable_card_linked_offer_data()->add_instrument_id(
instrument_id);
}
- if (offer_data.offer_reward_amount.find("%") != std::string::npos) {
+ if (offer_data.GetOfferRewardAmount().find("%") != std::string::npos) {
offer_specifics->mutable_percentage_reward()->set_percentage(
- offer_data.offer_reward_amount);
+ offer_data.GetOfferRewardAmount());
} else {
offer_specifics->mutable_fixed_amount_reward()->set_amount(
- offer_data.offer_reward_amount);
+ offer_data.GetOfferRewardAmount());
}
} else {
// Promo code offer fields:
offer_specifics->mutable_promo_code_offer_data()->set_promo_code(
- offer_data.promo_code);
+ offer_data.GetPromoCode());
}
}
AutofillOfferData AutofillOfferDataFromOfferSpecifics(
const sync_pb::AutofillOfferSpecifics& offer_specifics) {
DCHECK(IsOfferSpecificsValid(offer_specifics));
- AutofillOfferData offer_data;
// General offer data:
- offer_data.offer_id = offer_specifics.id();
- offer_data.expiry = base::Time::UnixEpoch() +
+ int64_t offer_id = offer_specifics.id();
+ base::Time expiry = base::Time::UnixEpoch() +
base::Seconds(offer_specifics.offer_expiry_date());
- offer_data.offer_details_url = GURL(offer_specifics.offer_details_url());
+ GURL offer_details_url = GURL(offer_specifics.offer_details_url());
+ std::vector<GURL> merchant_origins;
for (const std::string& domain : offer_specifics.merchant_domain()) {
const GURL gurl_domain = GURL(domain);
if (gurl_domain.is_valid())
- offer_data.merchant_origins.emplace_back(
- gurl_domain.DeprecatedGetOriginAsURL());
+ merchant_origins.emplace_back(gurl_domain.DeprecatedGetOriginAsURL());
}
- offer_data.display_strings.value_prop_text =
+ DisplayStrings display_strings;
+ display_strings.value_prop_text =
offer_specifics.display_strings().value_prop_text();
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
- offer_data.display_strings.see_details_text =
+ display_strings.see_details_text =
offer_specifics.display_strings().see_details_text_mobile();
- offer_data.display_strings.usage_instructions_text =
+ display_strings.usage_instructions_text =
offer_specifics.display_strings().usage_instructions_text_mobile();
#else
- offer_data.display_strings.see_details_text =
+ display_strings.see_details_text =
offer_specifics.display_strings().see_details_text_desktop();
- offer_data.display_strings.usage_instructions_text =
+ display_strings.usage_instructions_text =
offer_specifics.display_strings().usage_instructions_text_desktop();
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
- // Card-linked offer fields:
- offer_data.offer_reward_amount =
- offer_specifics.has_percentage_reward()
- ? offer_specifics.percentage_reward().percentage()
- : offer_specifics.fixed_amount_reward().amount();
- for (int64_t instrument_id :
- offer_specifics.card_linked_offer_data().instrument_id()) {
- offer_data.eligible_instrument_id.push_back(instrument_id);
- }
-
- // Promo code offer fields:
- offer_data.promo_code = offer_specifics.promo_code_offer_data().promo_code();
+ if (offer_specifics.promo_code_offer_data().promo_code().empty()) {
+ // Card-linked offer fields:
+ std::string offer_reward_amount =
+ offer_specifics.has_percentage_reward()
+ ? offer_specifics.percentage_reward().percentage()
+ : offer_specifics.fixed_amount_reward().amount();
+ std::vector<int64_t> eligible_instrument_id;
+ for (int64_t instrument_id :
+ offer_specifics.card_linked_offer_data().instrument_id()) {
+ eligible_instrument_id.push_back(instrument_id);
+ }
- return offer_data;
+ AutofillOfferData offer_data = AutofillOfferData::GPayCardLinkedOffer(
+ offer_id, expiry, merchant_origins, offer_details_url, display_strings,
+ eligible_instrument_id, offer_reward_amount);
+ return offer_data;
+ } else {
+ AutofillOfferData offer_data = AutofillOfferData::GPayPromoCodeOffer(
+ offer_id, expiry, merchant_origins, offer_details_url, display_strings,
+ offer_specifics.promo_code_offer_data().promo_code());
+ return offer_data;
+ }
}
AutofillProfile ProfileFromSpecifics(
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h
index 6a35801c90d..e54338c199a 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h
@@ -12,7 +12,7 @@
namespace autofill {
-struct AutofillOfferData;
+class AutofillOfferData;
class AutofillProfile;
class AutofillTable;
class CreditCard;
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
index 2b2d23f7847..ee340d51e74 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
@@ -271,28 +271,29 @@ TEST_F(AutofillSyncBridgeUtilTest, OfferSpecificsFromOfferData) {
AutofillOfferData offer_data = test::GetCardLinkedOfferData1();
SetAutofillOfferSpecificsFromOfferData(offer_data, &offer_specifics);
- EXPECT_EQ(offer_specifics.id(), offer_data.offer_id);
- EXPECT_EQ(offer_specifics.offer_details_url(), offer_data.offer_details_url);
+ EXPECT_EQ(offer_specifics.id(), offer_data.GetOfferId());
+ EXPECT_EQ(offer_specifics.offer_details_url(),
+ offer_data.GetOfferDetailsUrl());
EXPECT_EQ(offer_specifics.offer_expiry_date(),
- (offer_data.expiry - base::Time::UnixEpoch()).InSeconds());
+ (offer_data.GetExpiry() - base::Time::UnixEpoch()).InSeconds());
EXPECT_EQ(offer_specifics.merchant_domain().size(),
- (int)offer_data.merchant_origins.size());
+ (int)offer_data.GetMerchantOrigins().size());
for (int i = 0; i < offer_specifics.merchant_domain().size(); i++) {
EXPECT_EQ(offer_specifics.merchant_domain(i),
- offer_data.merchant_origins[i].spec());
+ offer_data.GetMerchantOrigins()[i].spec());
}
EXPECT_EQ(offer_specifics.display_strings().value_prop_text(),
- offer_data.display_strings.value_prop_text);
+ offer_data.GetDisplayStrings().value_prop_text);
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
EXPECT_EQ(offer_specifics.display_strings().see_details_text_mobile(),
- offer_data.display_strings.see_details_text);
+ offer_data.GetDisplayStrings().see_details_text);
EXPECT_EQ(offer_specifics.display_strings().usage_instructions_text_mobile(),
- offer_data.display_strings.usage_instructions_text);
+ offer_data.GetDisplayStrings().usage_instructions_text);
#else
EXPECT_EQ(offer_specifics.display_strings().see_details_text_desktop(),
- offer_data.display_strings.see_details_text);
+ offer_data.GetDisplayStrings().see_details_text);
EXPECT_EQ(offer_specifics.display_strings().usage_instructions_text_desktop(),
- offer_data.display_strings.usage_instructions_text);
+ offer_data.GetDisplayStrings().usage_instructions_text);
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
}
@@ -304,16 +305,16 @@ TEST_F(AutofillSyncBridgeUtilTest, OfferSpecificsFromCardLinkedOfferData) {
SetAutofillOfferSpecificsFromOfferData(offer_data, &offer_specifics);
EXPECT_TRUE(offer_specifics.percentage_reward().percentage() ==
- offer_data.offer_reward_amount ||
+ offer_data.GetOfferRewardAmount() ||
offer_specifics.fixed_amount_reward().amount() ==
- offer_data.offer_reward_amount);
+ offer_data.GetOfferRewardAmount());
EXPECT_EQ(offer_specifics.card_linked_offer_data().instrument_id().size(),
- (int)offer_data.eligible_instrument_id.size());
+ (int)offer_data.GetEligibleInstrumentIds().size());
for (int i = 0;
i < offer_specifics.card_linked_offer_data().instrument_id().size();
i++) {
EXPECT_EQ(offer_specifics.card_linked_offer_data().instrument_id(i),
- offer_data.eligible_instrument_id[i]);
+ offer_data.GetEligibleInstrumentIds()[i]);
}
}
@@ -325,7 +326,7 @@ TEST_F(AutofillSyncBridgeUtilTest, OfferSpecificsFromPromoCodeOfferData) {
SetAutofillOfferSpecificsFromOfferData(offer_data, &offer_specifics);
EXPECT_EQ(offer_specifics.promo_code_offer_data().promo_code(),
- offer_data.promo_code);
+ offer_data.GetPromoCode());
}
// Ensures that the ShouldResetAutofillWalletData function works correctly, if
@@ -343,7 +344,8 @@ TEST_F(AutofillSyncBridgeUtilTest,
new_offer_data.push_back(data1);
EXPECT_FALSE(AreAnyItemsDifferent(old_offer_data, new_offer_data));
- new_offer_data.at(0).offer_id += 456;
+ new_offer_data.at(0).SetOfferIdForTesting(new_offer_data.at(0).GetOfferId() +
+ 456);
EXPECT_TRUE(AreAnyItemsDifferent(old_offer_data, new_offer_data));
}
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
index 5250518c555..3366f595c0c 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
@@ -2105,39 +2105,40 @@ void AutofillTable::SetAutofillOffers(
"VALUES (?,?,?,?,?,?,?,?)"));
for (const AutofillOfferData& data : autofill_offer_data) {
- insert_offers.BindInt64(0, data.offer_id);
- insert_offers.BindString(1, data.offer_reward_amount);
+ insert_offers.BindInt64(0, data.GetOfferId());
+ insert_offers.BindString(1, data.GetOfferRewardAmount());
insert_offers.BindInt64(
- 2, data.expiry.ToDeltaSinceWindowsEpoch().InMilliseconds());
- insert_offers.BindString(3, data.offer_details_url.spec());
- insert_offers.BindString(4, data.promo_code);
- insert_offers.BindString(5, data.display_strings.value_prop_text);
- insert_offers.BindString(6, data.display_strings.see_details_text);
- insert_offers.BindString(7, data.display_strings.usage_instructions_text);
+ 2, data.GetExpiry().ToDeltaSinceWindowsEpoch().InMilliseconds());
+ insert_offers.BindString(3, data.GetOfferDetailsUrl().spec());
+ insert_offers.BindString(4, data.GetPromoCode());
+ insert_offers.BindString(5, data.GetDisplayStrings().value_prop_text);
+ insert_offers.BindString(6, data.GetDisplayStrings().see_details_text);
+ insert_offers.BindString(7,
+ data.GetDisplayStrings().usage_instructions_text);
insert_offers.Run();
insert_offers.Reset(true);
- for (const int64_t instrument_id : data.eligible_instrument_id) {
+ for (const int64_t instrument_id : data.GetEligibleInstrumentIds()) {
// Insert new offer_eligible_instrument values.
sql::Statement insert_offer_eligible_instruments(
db_->GetUniqueStatement("INSERT INTO offer_eligible_instrument("
"offer_id, " // 0
"instrument_id) " // 1
"VALUES (?,?)"));
- insert_offer_eligible_instruments.BindInt64(0, data.offer_id);
+ insert_offer_eligible_instruments.BindInt64(0, data.GetOfferId());
insert_offer_eligible_instruments.BindInt64(1, instrument_id);
insert_offer_eligible_instruments.Run();
insert_offer_eligible_instruments.Reset(true);
}
- for (const GURL& merchant_origin : data.merchant_origins) {
+ for (const GURL& merchant_origin : data.GetMerchantOrigins()) {
// Insert new offer_merchant_domain values.
sql::Statement insert_offer_merchant_domains(
db_->GetUniqueStatement("INSERT INTO offer_merchant_domain("
"offer_id, " // 0
"merchant_domain) " // 1
"VALUES (?,?)"));
- insert_offer_merchant_domains.BindInt64(0, data.offer_id);
+ insert_offer_merchant_domains.BindInt64(0, data.GetOfferId());
insert_offer_merchant_domains.BindString(1, merchant_origin.spec());
insert_offer_merchant_domains.Run();
insert_offer_merchant_domains.Reset(true);
@@ -2164,17 +2165,19 @@ bool AutofillTable::GetAutofillOffers(
while (s.Step()) {
int index = 0;
- std::unique_ptr<AutofillOfferData> data =
- std::make_unique<AutofillOfferData>();
- data->offer_id = s.ColumnInt64(index++);
- data->offer_reward_amount = s.ColumnString(index++);
- data->expiry = base::Time::FromDeltaSinceWindowsEpoch(
+ int64_t offer_id = s.ColumnInt64(index++);
+ std::string offer_reward_amount = s.ColumnString(index++);
+ base::Time expiry = base::Time::FromDeltaSinceWindowsEpoch(
base::Milliseconds(s.ColumnInt64(index++)));
- data->offer_details_url = GURL(s.ColumnString(index++));
- data->promo_code = s.ColumnString(index++);
- data->display_strings.value_prop_text = s.ColumnString(index++);
- data->display_strings.see_details_text = s.ColumnString(index++);
- data->display_strings.usage_instructions_text = s.ColumnString(index++);
+ GURL offer_details_url = GURL(s.ColumnString(index++));
+ std::string promo_code = s.ColumnString(index++);
+ std::string value_prop_text = s.ColumnString(index++);
+ std::string see_details_text = s.ColumnString(index++);
+ std::string usage_instructions_text = s.ColumnString(index++);
+ DisplayStrings display_strings = {value_prop_text, see_details_text,
+ usage_instructions_text};
+ std::vector<int64_t> eligible_instrument_id;
+ std::vector<GURL> merchant_origins;
sql::Statement s_offer_eligible_instrument(
db_->GetUniqueStatement("SELECT "
@@ -2182,11 +2185,11 @@ bool AutofillTable::GetAutofillOffers(
"instrument_id " // 1
"FROM offer_eligible_instrument "
"WHERE offer_id = ?"));
- s_offer_eligible_instrument.BindInt64(0, data->offer_id);
+ s_offer_eligible_instrument.BindInt64(0, offer_id);
while (s_offer_eligible_instrument.Step()) {
const int64_t instrument_id = s_offer_eligible_instrument.ColumnInt64(1);
if (instrument_id != 0) {
- data->eligible_instrument_id.push_back(instrument_id);
+ eligible_instrument_id.push_back(instrument_id);
}
}
@@ -2196,16 +2199,27 @@ bool AutofillTable::GetAutofillOffers(
"merchant_domain " // 1
"FROM offer_merchant_domain "
"WHERE offer_id = ?"));
- s_offer_merchant_domain.BindInt64(0, data->offer_id);
+ s_offer_merchant_domain.BindInt64(0, offer_id);
while (s_offer_merchant_domain.Step()) {
const std::string merchant_domain =
s_offer_merchant_domain.ColumnString(1);
if (!merchant_domain.empty()) {
- data->merchant_origins.emplace_back(merchant_domain);
+ merchant_origins.emplace_back(merchant_domain);
}
}
-
- autofill_offer_data->emplace_back(std::move(data));
+ if (promo_code.empty()) {
+ auto data = std::make_unique<AutofillOfferData>(
+ AutofillOfferData::GPayCardLinkedOffer(
+ offer_id, expiry, merchant_origins, offer_details_url,
+ display_strings, eligible_instrument_id, offer_reward_amount));
+ autofill_offer_data->emplace_back(std::move(data));
+ } else {
+ auto data = std::make_unique<AutofillOfferData>(
+ AutofillOfferData::GPayPromoCodeOffer(
+ offer_id, expiry, merchant_origins, offer_details_url,
+ display_strings, promo_code));
+ autofill_offer_data->emplace_back(std::move(data));
+ }
}
return s.Succeeded();
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h
index 28dd143ffc2..351bc47b789 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h
@@ -32,7 +32,7 @@ namespace autofill {
class AutofillChange;
class AutofillEntry;
struct AutofillMetadata;
-struct AutofillOfferData;
+class AutofillOfferData;
class AutofillProfile;
class AutofillTableEncryptor;
class AutofillTableTest;
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
index 8709dba9c65..48029c5ab8e 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -3574,80 +3574,81 @@ TEST_F(AutofillTableTest, GetAllUpiIds) {
}
TEST_F(AutofillTableTest, SetAndGetCreditCardOfferData) {
- // Create test data.
- AutofillOfferData credit_card_offer_1;
- AutofillOfferData credit_card_offer_2;
- AutofillOfferData credit_card_offer_3;
-
// Set Offer ID.
- credit_card_offer_1.offer_id = 1;
- credit_card_offer_2.offer_id = 2;
- credit_card_offer_3.offer_id = 3;
+ int64_t offer_id_1 = 1;
+ int64_t offer_id_2 = 2;
+ int64_t offer_id_3 = 3;
// Set reward amounts for card-linked offers on offer 1 and 2.
- credit_card_offer_1.offer_reward_amount = "$5";
- credit_card_offer_2.offer_reward_amount = "10%";
+ std::string offer_reward_amount_1 = "$5";
+ std::string offer_reward_amount_2 = "10%";
// Set promo code for offer 3.
- credit_card_offer_3.promo_code = "5PCTOFFSHOES";
+ std::string promo_code_3 = "5PCTOFFSHOES";
// Set expiry.
- credit_card_offer_1.expiry = base::Time::FromDoubleT(1000);
- credit_card_offer_2.expiry = base::Time::FromDoubleT(2000);
- credit_card_offer_3.expiry = base::Time::FromDoubleT(3000);
+ base::Time expiry_1 = base::Time::FromDoubleT(1000);
+ base::Time expiry_2 = base::Time::FromDoubleT(2000);
+ base::Time expiry_3 = base::Time::FromDoubleT(3000);
// Set details URL.
- credit_card_offer_1.offer_details_url =
- GURL("https://www.offer_1_example.com/");
- credit_card_offer_2.offer_details_url =
- GURL("https://www.offer_2_example.com/");
- credit_card_offer_3.offer_details_url =
- GURL("https://www.offer_3_example.com/");
+ GURL offer_details_url_1 = GURL("https://www.offer_1_example.com/");
+ GURL offer_details_url_2 = GURL("https://www.offer_2_example.com/");
+ GURL offer_details_url_3 = GURL("https://www.offer_3_example.com/");
// Set merchant domains for offer 1.
- credit_card_offer_1.merchant_origins.emplace_back(
- "http://www.merchant_domain_1_1.com/");
- credit_card_offer_1.merchant_origins.emplace_back(
- "http://www.merchant_domain_1_2.com/");
- credit_card_offer_1.merchant_origins.emplace_back(
- "http://www.merchant_domain_1_3.com/");
+ std::vector<GURL> merchant_origins_1;
+ merchant_origins_1.emplace_back("http://www.merchant_domain_1_1.com/");
+ std::vector<GURL> merchant_origins_2;
+ merchant_origins_2.emplace_back("http://www.merchant_domain_1_2.com/");
+ std::vector<GURL> merchant_origins_3;
+ merchant_origins_3.emplace_back("http://www.merchant_domain_1_3.com/");
// Set merchant domains for offer 2.
- credit_card_offer_2.merchant_origins.emplace_back(
- "http://www.merchant_domain_2_1.com/");
+ merchant_origins_2.emplace_back("http://www.merchant_domain_2_1.com/");
// Set merchant domains for offer 3.
- credit_card_offer_3.merchant_origins.emplace_back(
- "http://www.merchant_domain_3_1.com/");
- credit_card_offer_3.merchant_origins.emplace_back(
- "http://www.merchant_domain_3_2.com/");
+ merchant_origins_3.emplace_back("http://www.merchant_domain_3_1.com/");
+ merchant_origins_3.emplace_back("http://www.merchant_domain_3_2.com/");
+ DisplayStrings display_strings_1;
+ DisplayStrings display_strings_2;
+ DisplayStrings display_strings_3;
// Set display strings for all 3 offers.
- credit_card_offer_1.display_strings.value_prop_text = "$5 off your purchase";
- credit_card_offer_2.display_strings.value_prop_text = "10% off your purchase";
- credit_card_offer_3.display_strings.value_prop_text =
- "5% off shoes. Up to $50.";
- credit_card_offer_1.display_strings.see_details_text = "Terms apply.";
- credit_card_offer_2.display_strings.see_details_text = "Terms apply.";
- credit_card_offer_3.display_strings.see_details_text = "See details.";
- credit_card_offer_1.display_strings.usage_instructions_text =
+ display_strings_1.value_prop_text = "$5 off your purchase";
+ display_strings_2.value_prop_text = "10% off your purchase";
+ display_strings_3.value_prop_text = "5% off shoes. Up to $50.";
+ display_strings_1.see_details_text = "Terms apply.";
+ display_strings_2.see_details_text = "Terms apply.";
+ display_strings_3.see_details_text = "See details.";
+ display_strings_1.usage_instructions_text =
"Check out with this card to activate.";
- credit_card_offer_2.display_strings.usage_instructions_text =
+ display_strings_2.usage_instructions_text =
"Check out with this card to activate.";
- credit_card_offer_3.display_strings.usage_instructions_text =
+ display_strings_3.usage_instructions_text =
"Click the promo code field at checkout to autofill it.";
+ std::vector<int64_t> eligible_instrument_id_1;
+ std::vector<int64_t> eligible_instrument_id_2;
+ std::vector<int64_t> eligible_instrument_id_3;
+
// Set eligible card-linked instrument ID for offer 1.
- credit_card_offer_1.eligible_instrument_id.push_back(10);
- credit_card_offer_1.eligible_instrument_id.push_back(11);
+ eligible_instrument_id_1.push_back(10);
+ eligible_instrument_id_1.push_back(11);
// Set eligible card-linked instrument ID for offer 2.
- credit_card_offer_2.eligible_instrument_id.push_back(20);
- credit_card_offer_2.eligible_instrument_id.push_back(21);
- credit_card_offer_2.eligible_instrument_id.push_back(22);
+ eligible_instrument_id_2.push_back(20);
+ eligible_instrument_id_2.push_back(21);
+ eligible_instrument_id_2.push_back(22);
// Create vector of offer data.
std::vector<AutofillOfferData> autofill_offer_data;
- autofill_offer_data.push_back(credit_card_offer_1);
- autofill_offer_data.push_back(credit_card_offer_2);
- autofill_offer_data.push_back(credit_card_offer_3);
+ autofill_offer_data.push_back(AutofillOfferData::GPayCardLinkedOffer(
+ offer_id_1, expiry_1, merchant_origins_1, offer_details_url_1,
+ display_strings_2, eligible_instrument_id_1, offer_reward_amount_1));
+ autofill_offer_data.push_back(AutofillOfferData::GPayCardLinkedOffer(
+ offer_id_2, expiry_2, merchant_origins_2, offer_details_url_2,
+ display_strings_2, eligible_instrument_id_2, offer_reward_amount_2));
+ autofill_offer_data.push_back(AutofillOfferData::GPayPromoCodeOffer(
+ offer_id_3, expiry_3, merchant_origins_3, offer_details_url_3,
+ display_strings_3, promo_code_3));
table_->SetAutofillOffers(autofill_offer_data);
@@ -3660,7 +3661,7 @@ TEST_F(AutofillTableTest, SetAndGetCreditCardOfferData) {
// Find output data with corresponding Offer ID.
size_t output_index = 0;
while (output_index < output_offer_data.size()) {
- if (data.offer_id == output_offer_data[output_index]->offer_id) {
+ if (data.GetOfferId() == output_offer_data[output_index]->GetOfferId()) {
break;
}
output_index++;
@@ -3670,27 +3671,31 @@ TEST_F(AutofillTableTest, SetAndGetCreditCardOfferData) {
EXPECT_NE(output_index, output_offer_data.size());
// All corresponding fields must be equal.
- EXPECT_EQ(data.offer_id, output_offer_data[output_index]->offer_id);
- EXPECT_EQ(data.offer_reward_amount,
- output_offer_data[output_index]->offer_reward_amount);
- EXPECT_EQ(data.promo_code, output_offer_data[output_index]->promo_code);
- EXPECT_EQ(data.expiry, output_offer_data[output_index]->expiry);
- EXPECT_EQ(data.offer_details_url.spec(),
- output_offer_data[output_index]->offer_details_url.spec());
- EXPECT_EQ(data.display_strings.value_prop_text,
- output_offer_data[output_index]->display_strings.value_prop_text);
+ EXPECT_EQ(data.GetOfferId(), output_offer_data[output_index]->GetOfferId());
+ EXPECT_EQ(data.GetOfferRewardAmount(),
+ output_offer_data[output_index]->GetOfferRewardAmount());
+ EXPECT_EQ(data.GetPromoCode(),
+ output_offer_data[output_index]->GetPromoCode());
+ EXPECT_EQ(data.GetExpiry(), output_offer_data[output_index]->GetExpiry());
+ EXPECT_EQ(data.GetOfferDetailsUrl().spec(),
+ output_offer_data[output_index]->GetOfferDetailsUrl().spec());
EXPECT_EQ(
- data.display_strings.see_details_text,
- output_offer_data[output_index]->display_strings.see_details_text);
- EXPECT_EQ(data.display_strings.usage_instructions_text,
+ data.GetDisplayStrings().value_prop_text,
+ output_offer_data[output_index]->GetDisplayStrings().value_prop_text);
+ EXPECT_EQ(
+ data.GetDisplayStrings().see_details_text,
+ output_offer_data[output_index]->GetDisplayStrings().see_details_text);
+ EXPECT_EQ(data.GetDisplayStrings().usage_instructions_text,
output_offer_data[output_index]
- ->display_strings.usage_instructions_text);
- ASSERT_THAT(data.merchant_origins,
- testing::UnorderedElementsAreArray(
- output_offer_data[output_index]->merchant_origins));
- ASSERT_THAT(data.eligible_instrument_id,
+ ->GetDisplayStrings()
+ .usage_instructions_text);
+ ASSERT_THAT(data.GetMerchantOrigins(),
testing::UnorderedElementsAreArray(
- output_offer_data[output_index]->eligible_instrument_id));
+ output_offer_data[output_index]->GetMerchantOrigins()));
+ ASSERT_THAT(
+ data.GetEligibleInstrumentIds(),
+ testing::UnorderedElementsAreArray(
+ output_offer_data[output_index]->GetEligibleInstrumentIds()));
}
}
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc
index bdfb015f4bd..5aa6ca0f788 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc
@@ -204,7 +204,6 @@ void AutofillWalletOfferSyncBridge::MergeRemoteData(
web_data_backend_->CommitChanges();
if (offer_data_changed) {
- // TODO(crbug.com/1112095): Add enum to indicate what actually changed.
web_data_backend_->NotifyOfMultipleAutofillChanges();
}
}
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc
index 2edd5ea34e2..9bdf7467cb3 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc
@@ -239,7 +239,7 @@ TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetClientTag) {
AutofillOfferData data = test::GetCardLinkedOfferData1();
SetAutofillOfferSpecificsFromOfferData(data, &specifics);
EXPECT_EQ(bridge()->GetClientTag(SpecificsToEntity(specifics)),
- base::NumberToString(data.offer_id));
+ base::NumberToString(data.GetOfferId()));
}
TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetStorageKey) {
@@ -247,7 +247,7 @@ TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetStorageKey) {
AutofillOfferData data = test::GetCardLinkedOfferData1();
SetAutofillOfferSpecificsFromOfferData(data, &specifics);
EXPECT_EQ(bridge()->GetStorageKey(SpecificsToEntity(specifics)),
- base::NumberToString(data.offer_id));
+ base::NumberToString(data.GetOfferId()));
}
// Tests that when a new offer data is sent by the server, the client only keeps
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
index dcd5b16dc82..8e4f8448aad 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
@@ -155,7 +155,7 @@ class AutofillWebDataBackendImpl
// Updates Autofill entries in the web database.
WebDatabase::State UpdateAutofillEntries(
- const std::vector<autofill::AutofillEntry>& autofill_entries,
+ const std::vector<AutofillEntry>& autofill_entries,
WebDatabase* db);
// Adds a credit card to the web database. Valid only for local cards.
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_util.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_util.cc
index f11f4a222ef..cd84e8d64af 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_util.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_util.cc
@@ -177,7 +177,7 @@ void UpdateCardsBillingAddressReference(
for (std::unique_ptr<CreditCard>& credit_card : credit_cards) {
// If the credit card is not associated with a billing address, skip it.
if (credit_card->billing_address_id().empty())
- break;
+ continue;
// If the billing address profile associated with the card has been merged,
// replace it by the id of the profile in which it was merged. Repeat the
diff --git a/chromium/components/autofill/core/common/BUILD.gn b/chromium/components/autofill/core/common/BUILD.gn
index 1df8b629615..0660e3adc27 100644
--- a/chromium/components/autofill/core/common/BUILD.gn
+++ b/chromium/components/autofill/core/common/BUILD.gn
@@ -4,6 +4,7 @@
static_library("common") {
sources = [
+ "aliases.h",
"autofill_clock.cc",
"autofill_clock.h",
"autofill_constants.cc",
diff --git a/chromium/components/autofill/core/common/aliases.h b/chromium/components/autofill/core/common/aliases.h
new file mode 100644
index 00000000000..dfd1a0c6713
--- /dev/null
+++ b/chromium/components/autofill/core/common/aliases.h
@@ -0,0 +1,20 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_CORE_COMMON_ALIASES_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_ALIASES_H_
+
+#include "base/types/strong_alias.h"
+
+namespace autofill {
+
+// TODO(crbug.com/1326518): Use strong aliases for other primitives in mojom
+// files.
+
+using TouchToFillEligible =
+ base::StrongAlias<struct TouchToFillEligibleTag, bool>;
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_ALIASES_H_
diff --git a/chromium/components/autofill/core/common/autofill_clock.cc b/chromium/components/autofill/core/common/autofill_clock.cc
index 60169c8c7fb..0b9bfb6bc60 100644
--- a/chromium/components/autofill/core/common/autofill_clock.cc
+++ b/chromium/components/autofill/core/common/autofill_clock.cc
@@ -4,6 +4,7 @@
#include "components/autofill/core/common/autofill_clock.h"
+#include "base/check.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
diff --git a/chromium/components/autofill/core/common/autofill_features.cc b/chromium/components/autofill/core/common/autofill_features.cc
index 83bbf48d26e..f7fb7e74750 100644
--- a/chromium/components/autofill/core/common/autofill_features.cc
+++ b/chromium/components/autofill/core/common/autofill_features.cc
@@ -84,6 +84,12 @@ const base::Feature kAutofillAllowDuplicateFormSubmissions{
const base::Feature kAutofillAllowNonHttpActivation{
"AutofillAllowNonHttpActivation", base::FEATURE_DISABLED_BY_DEFAULT};
+// If enabled, the country calling code for nationally formatted phone numbers
+// is inferred from the profile's country, if available.
+// TODO(crbug.com/1311937): Cleanup when launched.
+const base::Feature kAutofillInferCountryCallingCode{
+ "AutofillInferCountryCallingCode", base::FEATURE_DISABLED_BY_DEFAULT};
+
// If enabled, whenever a form without a country field is parsed, the profile's
// country code is complemented with the predicted country code, used to
// determine the address requirements.
@@ -91,6 +97,10 @@ const base::Feature kAutofillAllowNonHttpActivation{
const base::Feature kAutofillComplementCountryCodeOnImport{
"AutofillComplementCountryCodeOnImport", base::FEATURE_DISABLED_BY_DEFAULT};
+// If enabled, local heuristics fall back to the fields placeholder attribute.
+const base::Feature kAutofillConsiderPlaceholderForParsing{
+ "AutofillConsiderPlaceholderForParsing", base::FEATURE_DISABLED_BY_DEFAULT};
+
// If enabled, the variation country code is used as the phone number's region,
// instead of defaulting to app locale.
// TODO(crbug.com/1295721): Cleanup when launched.
@@ -110,6 +120,14 @@ const base::Feature kAutofillCreateDataForTest{
const base::Feature kAutofillFillAndImportFromMoreFields{
"AutofillFillAndImportFromMoreFields", base::FEATURE_DISABLED_BY_DEFAULT};
+// If enabled, autofill searches for format strings (like "MM/YY", "MM / YY",
+// "MM/YYYY") in the label or placeholder of input elements and uses these
+// to fill expiration dates.
+// TODO(crbug.com/1326244): Cleanup when launched.
+const base::Feature kAutofillFillCreditCardAsPerFormatString{
+ "AutofillFillCreditCardAsPerFormatString",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// If enabled, AutofillPopupControllerImpl is destructed not immediately in its
// HideViewAndDie() function, but as a delayed task.
// TODO(crbug.com/1277218): Cleanup when launched.
@@ -120,16 +138,32 @@ const base::Feature kAutofillDelayPopupControllerDeletion{
const base::Feature kAutofillDisableFilling{"AutofillDisableFilling",
base::FEATURE_DISABLED_BY_DEFAULT};
-// Controls whether to displace removed forms in both FormCache and
-// AutofillManager.
-// TODO(crbug.com/1215333): Remove the feature when the experiment is completed.
-const base::Feature kAutofillDisplaceRemovedForms{
- "AutofillDisplaceRemovedForms", base::FEATURE_DISABLED_BY_DEFAULT};
-
// Kill switch for Autofill address import.
const base::Feature kAutofillDisableAddressImport{
"AutofillDisableAddressImport", base::FEATURE_DISABLED_BY_DEFAULT};
+// Kill switch for computing heuristics other than the active ones
+// (GetActivePatternSource()).
+const base::Feature kAutofillDisableShadowHeuristics{
+ "AutofillDisableShadowHeuristics", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// When enabled, autofill will use the new ranking algorithm for card and
+// profile autofill suggestions.
+const base::Feature kAutofillEnableRankingFormula{
+ "AutofillEnableRankingFormula", base::FEATURE_DISABLED_BY_DEFAULT};
+// The half life applied to the use count.
+const base::FeatureParam<int> kAutofillRankingFormulaUsageHalfLife{
+ &kAutofillEnableRankingFormula, "autofill_ranking_formula_usage_half_life",
+ 20};
+// The boost factor applied to ranking virtual cards.
+const base::FeatureParam<int> kAutofillRankingFormulaVirtualCardBoost{
+ &kAutofillEnableRankingFormula,
+ "autofill_ranking_formula_virtual_card_boost", 5};
+// The half life applied to the virtual card boost.
+const base::FeatureParam<int> kAutofillRankingFormulaVirtualCardBoostHalfLife{
+ &kAutofillEnableRankingFormula,
+ "autofill_ranking_formula_virtual_card_boost_half_life", 15};
+
// Controls if the heuristic field parsing utilizes shared labels.
// TODO(crbug.com/1165780): Remove once shared labels are launched.
const base::Feature kAutofillEnableSupportForParsingWithSharedLabels{
@@ -176,6 +210,12 @@ const base::Feature kAutofillEnableDependentLocalityParsing{
"AutofillEnableDependentLocalityParsing",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables the augmentation layer for setting-inaccessible fields, which allows
+// extending Autofill's address format by additional fields.
+// TODO(crbug.com/1300548) Remove when launched.
+const base::Feature kAutofillEnableExtendedAddressFormats{
+ "AutofillEnableExtendedAddressFormats", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Controls whether to save the first number in a form with multiple phone
// numbers instead of aborting the import.
// TODO(crbug.com/1167484) Remove once launched
@@ -183,6 +223,27 @@ const base::Feature kAutofillEnableImportWhenMultiplePhoneNumbers{
"AutofillEnableImportWhenMultiplePhoneNumbers",
base::FEATURE_DISABLED_BY_DEFAULT};
+// When enabled, candidate profiles are temporary stored on import, and merged
+// with future candidate profiles, to create an importable profile. This makes
+// importing from multi-step input flows possible.
+const base::Feature kAutofillEnableMultiStepImports{
+ "AutofillEnableMultiStepImports", base::FEATURE_DISABLED_BY_DEFAULT};
+// When enabled, imported profiles are stored as multi-step candidates too,
+// which enables complementing a recently imported profile during later steps of
+// a multi-step input flow.
+const base::FeatureParam<bool> kAutofillEnableMultiStepImportComplements{
+ &kAutofillEnableMultiStepImports, "enable_multistep_complement", false};
+// Configures the TTL of multi-step import candidates.
+const base::FeatureParam<base::TimeDelta> kAutofillMultiStepImportCandidateTTL{
+ &kAutofillEnableMultiStepImports, "multistep_candidate_ttl",
+ base::Minutes(30)};
+
+// When enabled, phone number local heuristics match empty labels when looking
+// for composite phone number inputs. E.g. Phone number <input><input>.
+const base::Feature kAutofillEnableParsingEmptyPhoneNumberLabels{
+ "AutofillEnableParsingEmptyPhoneNumberLabels",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// When enabled, the precedence is given to the field label over the name when
// they match different types. Applied only for parsing of address forms in
// Turkish.
@@ -228,11 +289,22 @@ const base::Feature kAutofillEnableSupportForHonorificPrefixes{
base::FEATURE_DISABLED_BY_DEFAULT};
// If enabled, trunk prefix-related phone number types are added to the
-// supported and matching types of |PhoneNumber|.
+// supported and matching types of |PhoneNumber|. Local heuristics for these
+// types are enabled as well.
const base::Feature kAutofillEnableSupportForPhoneNumberTrunkTypes{
"AutofillEnableSupportForPhoneNumberTrunkTypes",
base::FEATURE_DISABLED_BY_DEFAULT};
+// When enabled, Autofill monitors whether JavaScript modifies autofilled
+// credit card expiration dates and tries to fix a specific failure scenario.
+// After filling an expiration date "05/2023", some websites try to fix the
+// formatting and replace it with "05 / 20" instead of "05 / 23". When this
+// experiment is enabled, Chrome replaces the "05 / 20" with "05 / 23".
+// TODO(crbug.com/1314360): Remove once launched.
+const base::Feature kAutofillRefillModifiedCreditCardExpirationDates{
+ "AutofillRefillModifiedCreditCardExpirationDates",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Enables autofill to function within a FencedFrame, and is disabled by
// default.
// TODO(crbug.com/1294378): Remove once launched.
@@ -275,7 +347,7 @@ const base::Feature kAutofillFixServerQueriesIfPasswordManagerIsEnabled{
// shown. This is to prevent double clicks accidentally accepting suggestions.
// TODO(crbug/1279268): Remove once launched.
const base::Feature kAutofillIgnoreEarlyClicksOnPopup{
- "AutofillIgnoreEarlyClicksOnPopup", base::FEATURE_DISABLED_BY_DEFAULT};
+ "AutofillIgnoreEarlyClicksOnPopup", base::FEATURE_ENABLED_BY_DEFAULT};
// The duration for which clicks on the just-shown Autofill popup should be
// ignored if AutofillIgnoreEarlyClicksOnPopup is enabled.
@@ -284,7 +356,7 @@ const base::Feature kAutofillIgnoreEarlyClicksOnPopup{
const base::FeatureParam<base::TimeDelta>
kAutofillIgnoreEarlyClicksOnPopupDuration{
&kAutofillIgnoreEarlyClicksOnPopup, "duration",
- base::Milliseconds(500)};
+ base::Milliseconds(250)};
// When enabled, only changed values are highlighted in preview mode.
// TODO(crbug/1248585): Remove when launched.
@@ -338,26 +410,24 @@ extern const base::Feature kAutofillPreventOverridingPrefilledValues{
"AutofillPreventOverridingPrefilledValues",
base::FEATURE_DISABLED_BY_DEFAULT};
-// Uses the pattern provider to retrieve parsing patterns for the heuristic
-// field type detection.
+// If enabled, use the parsing patterns from a JSON file for heuristics, rather
+// than the hardcoded ones from autofill_regex_constants.cc.
+// The specific pattern set is controlled by the
+// `kAutofillParsingPatternActiveSource` parameter.
+//
+// This feature is intended to work with kAutofillPageLanguageDetection.
+//
+// Enabling this feature is also a prerequisite for emitting shadow metrics.
// TODO(crbug/1121990): Remove once launched.
const base::Feature kAutofillParsingPatternProvider{
"AutofillParsingPatternProvider", base::FEATURE_DISABLED_BY_DEFAULT};
-// Controls if language-specific patterns are used for the heuristic field type
-// detection.
-// For this to work, the feature kAutofillPageLanguageDetection must be enabled.
-// Otherwise the pattern provider will revert back to language unspecific
-// patterns.
-const base::FeatureParam<bool>
- kAutofillParsingWithLanguageSpecificPatternsParam{
- &kAutofillParsingPatternProvider, "use_language_specific_patterns",
- true};
-
-// Controls if patterns retrieved with the component updater are used.
-const base::FeatureParam<bool> kAutofillParsingWithRemotePatternsParam{
- &kAutofillParsingPatternProvider,
- "use_patterns_retrieved_with_the_component_udpater", false};
+// The specific pattern set is controlled by the `kAutofillParsingPatternActive`
+// parameter. One of "legacy", "default", "experimental", "nextgen". All other
+// values are equivalent to "default".
+// TODO(crbug/1248339): Remove once experiment is finished.
+const base::FeatureParam<std::string> kAutofillParsingPatternActiveSource{
+ &kAutofillParsingPatternProvider, "prediction_source", "default"};
// Enables detection of language from Translate.
// TODO(crbug/1150895): Cleanup when launched.
@@ -376,12 +446,26 @@ const base::Feature kAutofillProfileImportFromUnfocusableFields{
"AutofillProfileImportFromUnfocusableFields",
base::FEATURE_DISABLED_BY_DEFAULT};
-// Clear fields which are not visible in the settings for a profile's country,
-// both during profile import and on startup.
+// If we observe a sequence of fields of (street address, address line 2), these
+// get rationalized to (address line 1, address line 2).
+// TODO(crbug.com/1326425): Remove once feature is lanuched.
+const base::Feature kAutofillRationalizeStreetAddressAndAddressLine{
+ "AutofillRationalizeStreetAddressAndAddressLine",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Prevents Autofill from importing setting-inaccessible field types on profile
+// import.
// TODO(crbug.com/1299435): Cleanup when launched.
const base::Feature kAutofillRemoveInaccessibleProfileValues{
"AutofillRemoveInaccessibleProfileValues",
base::FEATURE_DISABLED_BY_DEFAULT};
+// If enabled, setting-inaccessible field types are additionally removed from
+// existing profiles on startup. These can exit in profile imported prior to
+// AutofillRemoveInaccessibleProfileValues.
+const base::FeatureParam<bool>
+ kAutofillRemoveInaccessibleProfileValuesOnStartup{
+ &kAutofillRemoveInaccessibleProfileValues,
+ "remove_inaccessible_fields_on_startup", false};
// If enabled, invalid phone numbers are removed on profile import, rather than
// invalidating the entire profile.
@@ -404,6 +488,15 @@ const base::Feature kAutofillSaveAndFillVPA{"AutofillSaveAndFillVPA",
const base::Feature kAutofillSectionUponRedundantNameInfo{
"AutofillSectionUponRedundantNameInfo", base::FEATURE_DISABLED_BY_DEFAULT};
+// Controls non-default Autofill API predictions. See crbug.com/1331322.
+const base::Feature kAutofillServerBehaviors{"AutofillServerBehaviors",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+// Chrome doesn't need to know the meaning of the value. Chrome only needs to
+// forward it to the Autofill API, to let the server know which group the client
+// belongs to.
+const base::FeatureParam<int> kAutofillServerBehaviorsParam{
+ &kAutofillServerBehaviors, "server_prediction_source", 0};
+
// Enables or Disables (mostly for hermetic testing) autofill server
// communication. The URL of the autofill server can further be controlled via
// the autofill-server-url param. The given URL should specify the complete
@@ -423,6 +516,13 @@ const base::Feature kAutofillSharedAutofill{"AutofillSharedAutofill",
const base::FeatureParam<bool> kAutofillSharedAutofillRelaxedParam{
&kAutofillSharedAutofill, "relax_shared_autofill", false};
+// Controls whether Manual fallbacks would be shown in the context menu for
+// filling. Used only in Desktop.
+// TODO(crbug.com/1326895): Clean up when launched.
+const base::Feature kAutofillShowManualFallbackInContextMenu{
+ "AutofillShowManualFallbackInContextMenu",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Controls attaching the autofill type predictions to their respective
// element in the DOM.
const base::Feature kAutofillShowTypePredictions{
@@ -453,7 +553,7 @@ const base::Feature kAutofillUploadThrottling{"AutofillUploadThrottling",
// the server.
// TODO(crbug.com/1143516): Remove the feature when the experiment is completed.
const base::Feature kAutofillUseAlternativeStateNameMap{
- "AutofillUseAlternativeStateNameMap", base::FEATURE_DISABLED_BY_DEFAULT};
+ "AutofillUseAlternativeStateNameMap", base::FEATURE_ENABLED_BY_DEFAULT};
// Controls whether suggestions' labels use the improved label disambiguation
// format.
@@ -472,13 +572,6 @@ const base::Feature kAutofillUseConsistentPopupSettingsIcons{
const base::Feature kAutofillUseNewSectioningMethod{
"AutofillUseNewSectioningMethod", base::FEATURE_DISABLED_BY_DEFAULT};
-// When enabled, to get the unowned control elements we call
-// Document::UnassociatedListedElements(). This way we can reduce the number of
-// DOM traversals.
-// TODO(crbug/1201875): Remove once experiment is finished.
-const base::Feature kAutofillUseUnassociatedListedElements{
- "AutofillUseUnassociatedListedElements", base::FEATURE_DISABLED_BY_DEFAULT};
-
// Introduces various visual improvements of the Autofill suggestion UI that is
// also used for the password manager.
const base::Feature kAutofillVisualImprovementsForSuggestionUi{
@@ -526,21 +619,10 @@ const char kAutofillUseMobileLabelDisambiguationParameterShowAll[] = "show-all";
const char kAutofillUseMobileLabelDisambiguationParameterShowOne[] = "show-one";
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-#if BUILDFLAG(IS_IOS)
-// Controls whether the creation of new address profiles is enabled in settings
-// on IOS.
-// TODO(crbug/1167105): Remove once it's launched.
-const base::Feature kAutofillEnableNewAddressProfileCreationInSettingsOnIOS{
- "AutofillEnableNewAddressProfileCreationInSettingsOnIOS",
- base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
#if BUILDFLAG(IS_ANDROID)
bool IsAutofillManualFallbackEnabled() {
- return base::FeatureList::IsEnabled(
- autofill::features::kAutofillKeyboardAccessory) &&
- base::FeatureList::IsEnabled(
- autofill::features::kAutofillManualFallbackAndroid);
+ return base::FeatureList::IsEnabled(kAutofillKeyboardAccessory) &&
+ base::FeatureList::IsEnabled(kAutofillManualFallbackAndroid);
}
#endif // BUILDFLAG(IS_ANDROID)
diff --git a/chromium/components/autofill/core/common/autofill_features.h b/chromium/components/autofill/core/common/autofill_features.h
index 574b02681fd..d8b3036b433 100644
--- a/chromium/components/autofill/core/common/autofill_features.h
+++ b/chromium/components/autofill/core/common/autofill_features.h
@@ -45,8 +45,12 @@ extern const base::Feature kAutofillAllowDuplicateFormSubmissions;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillAllowNonHttpActivation;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillInferCountryCallingCode;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillComplementCountryCodeOnImport;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillConsiderPlaceholderForParsing;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillConsiderVariationCountryCodeForPhoneNumbers;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillEnableWithinFencedFrame;
@@ -55,12 +59,14 @@ extern const base::Feature kAutofillCreateDataForTest;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillFillAndImportFromMoreFields;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillFillCreditCardAsPerFormatString;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillDelayPopupControllerDeletion;
COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillDisableFilling;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillDisableAddressImport;
COMPONENT_EXPORT(AUTOFILL)
-extern const base::Feature kAutofillDisplaceRemovedForms;
+extern const base::Feature kAutofillDisableShadowHeuristics;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillEnableAccountWalletStorage;
COMPONENT_EXPORT(AUTOFILL)
@@ -70,8 +76,28 @@ extern const base::Feature kAutofillEnableCompatibilitySupportForBirthdates;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillEnableDependentLocalityParsing;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillEnableExtendedAddressFormats;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillEnableImportWhenMultiplePhoneNumbers;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillEnableMultiStepImports;
+COMPONENT_EXPORT(AUTOFILL)
+extern const base::FeatureParam<bool> kAutofillEnableMultiStepImportComplements;
+COMPONENT_EXPORT(AUTOFILL)
+extern const base::FeatureParam<base::TimeDelta>
+ kAutofillMultiStepImportCandidateTTL;
+COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillEnableParsingEmptyPhoneNumberLabels;
+COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillEnableRankingFormula;
+COMPONENT_EXPORT(AUTOFILL)
+extern const base::FeatureParam<int> kAutofillRankingFormulaUsageHalfLife;
+COMPONENT_EXPORT(AUTOFILL)
+extern const base::FeatureParam<int> kAutofillRankingFormulaVirtualCardBoost;
+COMPONENT_EXPORT(AUTOFILL)
+extern const base::FeatureParam<int>
+ kAutofillRankingFormulaVirtualCardBoostHalfLife;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillEnableSupportForApartmentNumbers;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillEnableLabelPrecedenceForTurkishAddresses;
@@ -94,6 +120,8 @@ extern const base::Feature kAutofillExtractAllDatalists;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillEnableSupportForPhoneNumberTrunkTypes;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillRefillModifiedCreditCardExpirationDates;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillTypeSpecificPopupWidth;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillFixFillableFieldTypes;
@@ -121,14 +149,12 @@ COMPONENT_EXPORT(AUTOFILL)
extern const base::FeatureParam<int>
kAutofillMaxiumWidthPercentageToMoveSuggestionPopupToCenter;
COMPONENT_EXPORT(AUTOFILL)
-extern const base::Feature kAutofillParsingPatternProvider;
-COMPONENT_EXPORT(AUTOFILL)
-extern const base::FeatureParam<bool>
- kAutofillParsingWithLanguageSpecificPatternsParam;
+extern const base::Feature kAutofillPageLanguageDetection;
COMPONENT_EXPORT(AUTOFILL)
-extern const base::FeatureParam<bool> kAutofillParsingWithRemotePatternsParam;
+extern const base::Feature kAutofillParsingPatternProvider;
COMPONENT_EXPORT(AUTOFILL)
-extern const base::Feature kAutofillPageLanguageDetection;
+extern const base::FeatureParam<std::string>
+ kAutofillParsingPatternActiveSource;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillPreventOverridingPrefilledValues;
COMPONENT_EXPORT(AUTOFILL)
@@ -136,21 +162,32 @@ extern const base::Feature kAutofillProbableFormSubmissionInBrowser;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillProfileImportFromUnfocusableFields;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillRationalizeStreetAddressAndAddressLine;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillRemoveInvalidPhoneNumberOnImport;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillRemoveInaccessibleProfileValues;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::FeatureParam<bool>
+ kAutofillRemoveInaccessibleProfileValuesOnStartup;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillRetrieveOverallPredictionsFromCache;
COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillSaveAndFillVPA;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillSectionUponRedundantNameInfo;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillServerBehaviors;
+COMPONENT_EXPORT(AUTOFILL)
+extern const base::FeatureParam<int> kAutofillServerBehaviorsParam;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillServerCommunication;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillSharedAutofill;
COMPONENT_EXPORT(AUTOFILL)
extern const base::FeatureParam<bool> kAutofillSharedAutofillRelaxedParam;
COMPONENT_EXPORT(AUTOFILL)
+extern const base::Feature kAutofillShowManualFallbackInContextMenu;
+COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillShowTypePredictions;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillSilentProfileUpdateForInsufficientImport;
@@ -168,8 +205,6 @@ extern const base::Feature kAutofillUseNewSectioningMethod;
COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillUseConsistentPopupSettingsIcons;
COMPONENT_EXPORT(AUTOFILL)
-extern const base::Feature kAutofillUseUnassociatedListedElements;
-COMPONENT_EXPORT(AUTOFILL)
extern const base::Feature kAutofillVisualImprovementsForSuggestionUi;
COMPONENT_EXPORT(AUTOFILL)
@@ -211,12 +246,6 @@ COMPONENT_EXPORT(AUTOFILL)
bool IsMacViewsAutofillPopupExperimentEnabled();
#endif // BUILDFLAG(IS_APPLE)
-#if BUILDFLAG(IS_IOS)
-COMPONENT_EXPORT(AUTOFILL)
-extern const base::Feature
- kAutofillEnableNewAddressProfileCreationInSettingsOnIOS;
-#endif // BUILDFLAG(IS_IOS)
-
#if BUILDFLAG(IS_ANDROID)
COMPONENT_EXPORT(AUTOFILL)
bool IsAutofillManualFallbackEnabled();
diff --git a/chromium/components/autofill/core/common/autofill_internals/log_message.h b/chromium/components/autofill/core/common/autofill_internals/log_message.h
index 9430cb51a1b..8842eef624b 100644
--- a/chromium/components/autofill/core/common/autofill_internals/log_message.h
+++ b/chromium/components/autofill/core/common/autofill_internals/log_message.h
@@ -53,7 +53,10 @@ class LogBuffer;
T(CardUploadDecisionUploadNotOffered, "Credit card upload was not offered.") \
T(SuggestionSuppressed, "Autofill suggestion(s) suppressed.") \
T(Rationalization, "Rationalization: ") \
- T(ProcessingServerData, "Processing server data.")
+ T(ProcessingServerData, "Processing server data.") \
+ T(JavaScriptChangedAutofilledValue, \
+ "JavaScript set value of autofilled " \
+ "field: ")
// Log messages for chrome://autofill-internals.
diff --git a/chromium/components/autofill/core/common/autofill_internals/logging_scope.h b/chromium/components/autofill/core/common/autofill_internals/logging_scope.h
index fad91e60629..2abc842bdc5 100644
--- a/chromium/components/autofill/core/common/autofill_internals/logging_scope.h
+++ b/chromium/components/autofill/core/common/autofill_internals/logging_scope.h
@@ -36,7 +36,9 @@ class LogBuffer;
/* If credit card upload is either enabled or disabled. */ \
T(CreditCardUploadStatus) \
/* Whether or not card upload was offered to the user. */ \
- T(CardUploadDecision)
+ T(CardUploadDecision) \
+ /* The website modified a field */ \
+ T(WebsiteModifiedFieldValue)
// Define a bunch of logging scopes: kContext, kParsing, ...
#define AUTOFILL_TEMPLATE(NAME) k##NAME,
diff --git a/chromium/components/autofill/core/common/autofill_payments_features.cc b/chromium/components/autofill/core/common/autofill_payments_features.cc
index 2fd07976d4c..4ebd3a423e5 100644
--- a/chromium/components/autofill/core/common/autofill_payments_features.cc
+++ b/chromium/components/autofill/core/common/autofill_payments_features.cc
@@ -19,8 +19,7 @@
#include "components/prefs/pref_service.h"
#include "ui/base/l10n/l10n_util.h"
-namespace autofill {
-namespace features {
+namespace autofill::features {
// Features
@@ -54,16 +53,19 @@ const base::Feature kAutofillCreditCardAuthentication{
const base::Feature kAutofillCreditCardUploadFeedback{
"AutofillCreditCardUploadFeedback", base::FEATURE_DISABLED_BY_DEFAULT};
-// When enabled, merchant bound virtual cards will be offered when users
-// interact with a payment form.
-const base::Feature kAutofillEnableMerchantBoundVirtualCards{
- "AutofillEnableMerchantBoundVirtualCards",
- base::FEATURE_ENABLED_BY_DEFAULT};
+// When enabled, the GetDetailsForEnrollResponseDetails in the
+// UploadCardResponseDetails will be parsed, which will allow the Virtual Card
+// Enrollment flow to skip making a new GetDetailsForEnroll request. This is an
+// optimization to improve the latency of the Virtual Card Enrollment flow.
+const base::Feature
+ kAutofillEnableGetDetailsForEnrollParsingInUploadCardResponse{
+ "AutofillEnableGetDetailsForEnrollParsingInUploadCardResponse",
+ base::FEATURE_ENABLED_BY_DEFAULT};
// When enabled, enable manual falling component for virtual cards on Android.
const base::Feature kAutofillEnableManualFallbackForVirtualCards{
"AutofillEnableManualFallbackForVirtualCards",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
// When enabled, a notification will be displayed on page navigation if the
// domain has an eligible merchant promo code offer or reward.
@@ -105,8 +107,13 @@ const base::Feature kAutofillEnableUnmaskCardRequestSetInstrumentId{
// autofill flows (for example, downstream and upstream), and from the settings
// page.
const base::Feature kAutofillEnableUpdateVirtualCardEnrollment{
- "AutofillEnableUpdateVirtualCardEnrollment",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ "AutofillEnableUpdateVirtualCardEnrollment",
+#if BUILDFLAG(IS_IOS)
+ base::FEATURE_DISABLED_BY_DEFAULT
+#else
+ base::FEATURE_ENABLED_BY_DEFAULT
+#endif
+};
// When enabled, the option of using cloud token virtual card will be offered
// when all requirements are met.
@@ -133,12 +140,6 @@ const base::Feature kAutofillEnableVirtualCardManagementInDesktopSettingsPage{
const base::Feature kAutofillEnableVirtualCardMetadata{
"AutofillEnableVirtualCardMetadata", base::FEATURE_DISABLED_BY_DEFAULT};
-// When enabled, virtual card retrieval will pass an optional
-// authentication based on risk level.
-const base::Feature kAutofillEnableVirtualCardsRiskBasedAuthentication{
- "AutofillEnableVirtualCardsRiskBasedAuthentication",
- base::FEATURE_ENABLED_BY_DEFAULT};
-
// When enabled, if the previous feature offer was declined, a delay will be
// added before Chrome attempts to show offer again.
const base::Feature kAutofillEnforceDelaysInStrikeDatabase{
@@ -149,13 +150,6 @@ const base::Feature kAutofillEnforceDelaysInStrikeDatabase{
const base::Feature kAutofillFillMerchantPromoCodeFields{
"AutofillFillMerchantPromoCodeFields", base::FEATURE_DISABLED_BY_DEFAULT};
-// The merchant bound virtual card feature introduces new customized card art
-// images. This parameter defines the expiration of the fetched image in the
-// disk cache of the image fetcher.
-const base::FeatureParam<int> kAutofillImageFetcherDiskCacheExpirationInMinutes{
- &kAutofillEnableMerchantBoundVirtualCards,
- "autofill_image_fetcher_disk_cache_expiration_in_minutes", 10};
-
// When enabled, Autofill will attempt to find merchant promo/coupon/gift code
// fields when parsing forms.
const base::Feature kAutofillParseMerchantPromoCodeFields{
@@ -166,21 +160,33 @@ const base::Feature kAutofillParseMerchantPromoCodeFields{
const base::Feature kAutofillSaveCardDismissOnNavigation{
"AutofillSaveCardDismissOnNavigation", base::FEATURE_ENABLED_BY_DEFAULT};
+// When enabled, the expiration date of the card will not be shown in the
+// Autofill Suggestions.
+const base::Feature kAutofillRemoveCardExpiryFromDownstreamSuggestion{
+ "AutofillRemoveCardExpiryFromDownstreamSuggestion",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// When enabled, the Save Card infobar supports editing before submitting.
const base::Feature kAutofillSaveCardInfobarEditSupport{
"AutofillSaveCardInfobarEditSupport", base::FEATURE_ENABLED_BY_DEFAULT};
+// When enabled, Chrome will display experimental UI variants to the user
+// during the upload save card process.
+const base::Feature kAutofillSaveCardUiExperiment{
+ "AutofillSaveCardUiExperiment", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// This will select one of the options for the save card UI bubble which we
+// want to display to the user. The value will be an integer(number).
+const base::FeatureParam<int> kAutofillSaveCardUiExperimentSelectorInNumber{
+ &kAutofillSaveCardUiExperiment,
+ "autofill_save_card_ui_experiment_selector_in_number", 0};
+
// When enabled, the entire PAN and the CVC details of the unmasked cached card
// will be shown in the manual filling view.
const base::Feature kAutofillShowUnmaskedCachedCardInManualFillingView{
"AutofillShowUnmaskedCachedCardInManualFillingView",
base::FEATURE_DISABLED_BY_DEFAULT};
-// When enabled, suggestions with offers will be shown at the top.
-const base::Feature kAutofillSortSuggestionsBasedOnOfferPresence{
- "AutofillSortSuggestionsBasedOnOfferPresence",
- base::FEATURE_ENABLED_BY_DEFAULT};
-
// When enabled, merchant bound virtual cards will be suggested even if we don't
// detect all of the card number, exp date and CVC fields in the payment form.
const base::Feature kAutofillSuggestVirtualCardsOnIncompleteForm{
@@ -195,9 +201,25 @@ const base::Feature kAutofillSuggestVirtualCardsOnIncompleteForm{
const base::Feature kAutofillUpstream{"AutofillUpstream",
base::FEATURE_DISABLED_BY_DEFAULT};
+// When enabled, Chrome allows credit card upload to Google Payments if the
+// user's email domain is from a common email provider (thus unlikely to be an
+// enterprise or education user).
+const base::Feature kAutofillUpstreamAllowAdditionalEmailDomains{
+ "AutofillUpstreamAllowAdditionalEmailDomains",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+// When enabled, Chrome allows credit card upload to Google Payments, no matter
+// the user's email domain.
const base::Feature kAutofillUpstreamAllowAllEmailDomains{
"AutofillUpstreamAllowAllEmailDomains", base::FEATURE_DISABLED_BY_DEFAULT};
+// The delay required since the last strike before offering another virtual card
+// enrollment attempt.
+const base::FeatureParam<int>
+ kAutofillVirtualCardEnrollDelayInStrikeDatabaseInDays{
+ &kAutofillEnforceDelaysInStrikeDatabase,
+ "autofill_virtual_card_enroll_delay_in_strike_database_in_days", 7};
+
bool ShouldShowImprovedUserConsentForCreditCardSave() {
// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
@@ -211,5 +233,4 @@ bool ShouldShowImprovedUserConsentForCreditCardSave() {
#endif
}
-} // namespace features
-} // namespace autofill
+} // namespace autofill::features
diff --git a/chromium/components/autofill/core/common/autofill_payments_features.h b/chromium/components/autofill/core/common/autofill_payments_features.h
index da125251c33..96a96778243 100644
--- a/chromium/components/autofill/core/common/autofill_payments_features.h
+++ b/chromium/components/autofill/core/common/autofill_payments_features.h
@@ -21,8 +21,9 @@ extern const base::Feature kAutofillAlwaysReturnCloudTokenizedCard;
extern const base::Feature kAutofillAutoTriggerManualFallbackForCards;
extern const base::Feature kAutofillCreditCardAuthentication;
extern const base::Feature kAutofillCreditCardUploadFeedback;
+extern const base::Feature
+ kAutofillEnableGetDetailsForEnrollParsingInUploadCardResponse;
extern const base::Feature kAutofillEnableManualFallbackForVirtualCards;
-extern const base::Feature kAutofillEnableMerchantBoundVirtualCards;
extern const base::Feature kAutofillEnableOfferNotificationForPromoCodes;
extern const base::Feature kAutofillEnableOffersInClankKeyboardAccessory;
extern const base::Feature kAutofillEnableSendingBcnInGetUploadDetails;
@@ -35,19 +36,22 @@ extern const base::Feature kAutofillEnableVirtualCardFidoEnrollment;
extern const base::Feature
kAutofillEnableVirtualCardManagementInDesktopSettingsPage;
extern const base::Feature kAutofillEnableVirtualCardMetadata;
-extern const base::Feature kAutofillEnableVirtualCardsRiskBasedAuthentication;
extern const base::Feature kAutofillEnforceDelaysInStrikeDatabase;
extern const base::Feature kAutofillFillMerchantPromoCodeFields;
-extern const base::FeatureParam<int>
- kAutofillImageFetcherDiskCacheExpirationInMinutes;
extern const base::Feature kAutofillParseMerchantPromoCodeFields;
+extern const base::Feature kAutofillRemoveCardExpiryFromDownstreamSuggestion;
extern const base::Feature kAutofillSaveCardDismissOnNavigation;
extern const base::Feature kAutofillSaveCardInfobarEditSupport;
+extern const base::Feature kAutofillSaveCardUiExperiment;
+extern const base::FeatureParam<int>
+ kAutofillSaveCardUiExperimentSelectorInNumber;
extern const base::Feature kAutofillShowUnmaskedCachedCardInManualFillingView;
-extern const base::Feature kAutofillSortSuggestionsBasedOnOfferPresence;
extern const base::Feature kAutofillSuggestVirtualCardsOnIncompleteForm;
extern const base::Feature kAutofillUpstream;
+extern const base::Feature kAutofillUpstreamAllowAdditionalEmailDomains;
extern const base::Feature kAutofillUpstreamAllowAllEmailDomains;
+extern const base::FeatureParam<int>
+ kAutofillVirtualCardEnrollDelayInStrikeDatabaseInDays;
// Return whether a [No thanks] button and new messaging is shown in the save
// card bubbles. This will be called only on desktop platforms.
diff --git a/chromium/components/autofill/core/common/autofill_prefs.cc b/chromium/components/autofill/core/common/autofill_prefs.cc
index fb4dd6c532f..d2e741e5593 100644
--- a/chromium/components/autofill/core/common/autofill_prefs.cc
+++ b/chromium/components/autofill/core/common/autofill_prefs.cc
@@ -65,11 +65,6 @@ const char kAutofillJapanCityFieldMigratedDeprecated[] =
// was run. This routine will be run once per version.
const char kAutofillLastVersionDeduped[] = "autofill.last_version_deduped";
-// Integer that is set to the last version where the profile validation routine
-// was run. We validate profiles at least once per version to keep track of the
-// changes in the validation logic.
-const char kAutofillLastVersionValidated[] = "autofill.last_version_validated";
-
// Integer that is set to the last version where disused addresses were
// deleted. This deletion will be run once per version.
const char kAutofillLastVersionDisusedAddressesDeleted[] =
@@ -86,12 +81,6 @@ const char kAutofillOrphanRowsRemoved[] = "autofill.orphan_rows_removed";
// Boolean that is true if Autofill is enabled and allowed to save profile data.
const char kAutofillProfileEnabled[] = "autofill.profile_enabled";
-// The field type, validity state map of all profiles.
-// TODO(crbug.com/910596): Pref name is "autofill_" instead of "autofill."
-// because of a mismatch when the priorify prefs were generated. Consider
-// migrating this back to "autofill." in the future.
-const char kAutofillProfileValidity[] = "autofill_profile_validity";
-
// This pref stores the file path where the autofill states data is
// downloaded to.
const char kAutofillStatesDataDir[] = "autofill.states_data_dir";
@@ -139,9 +128,6 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterIntegerPref(
prefs::kAutofillLastVersionDeduped, 0,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
- registry->RegisterIntegerPref(
- prefs::kAutofillLastVersionValidated, 0,
- user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterIntegerPref(
prefs::kAutofillLastVersionDisusedAddressesDeleted, 0,
@@ -149,9 +135,6 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterBooleanPref(
prefs::kAutofillCreditCardEnabled, true,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
- registry->RegisterStringPref(
- prefs::kAutofillProfileValidity, "",
- user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
// Non-synced prefs. Used for per-device choices, e.g., signin promo.
registry->RegisterBooleanPref(prefs::kAutofillCreditCardFidoAuthEnabled,
@@ -262,13 +245,6 @@ void SetPaymentsIntegrationEnabled(PrefService* prefs, bool enabled) {
prefs->SetBoolean(kAutofillWalletImportEnabled, enabled);
}
-std::string GetAllProfilesValidityMapsEncodedString(const PrefService* prefs) {
- std::string value = prefs->GetString(kAutofillProfileValidity);
- if (base::Base64Decode(value, &value))
- return value;
- return std::string();
-}
-
void SetUserOptedInWalletSyncTransport(PrefService* prefs,
const CoreAccountId& account_id,
bool opted_in) {
diff --git a/chromium/components/autofill/core/common/autofill_prefs.h b/chromium/components/autofill/core/common/autofill_prefs.h
index d48feaf5d55..f086613bc5b 100644
--- a/chromium/components/autofill/core/common/autofill_prefs.h
+++ b/chromium/components/autofill/core/common/autofill_prefs.h
@@ -32,13 +32,11 @@ extern const char kAutofillCreditCardSigninPromoImpressionCount[];
extern const char kAutofillEnabledDeprecated[];
extern const char kAutofillJapanCityFieldMigratedDeprecated[];
extern const char kAutofillLastVersionDeduped[];
-extern const char kAutofillLastVersionValidated[];
extern const char kAutofillLastVersionDisusedAddressesDeleted[];
extern const char kAutofillLastVersionDisusedCreditCardsDeleted[];
extern const char kAutofillOrphanRowsRemoved[];
// Do not get/set the value of this pref directly. Use provided getter/setter.
extern const char kAutofillProfileEnabled[];
-extern const char kAutofillProfileValidity[];
extern const char kAutofillSyncTransportOptIn[];
extern const char kAutofillStatesDataDir[];
extern const char kAutofillUploadEncodingSeed[];
@@ -84,8 +82,6 @@ bool IsPaymentsIntegrationEnabled(const PrefService* prefs);
void SetPaymentsIntegrationEnabled(PrefService* prefs, bool enabled);
-std::string GetAllProfilesValidityMapsEncodedString(const PrefService* prefs);
-
void SetUserOptedInWalletSyncTransport(PrefService* prefs,
const CoreAccountId& account_id,
bool opted_in);
diff --git a/chromium/components/autofill/core/common/autofill_tick_clock.cc b/chromium/components/autofill/core/common/autofill_tick_clock.cc
index 594916e5c6f..483d099b799 100644
--- a/chromium/components/autofill/core/common/autofill_tick_clock.cc
+++ b/chromium/components/autofill/core/common/autofill_tick_clock.cc
@@ -4,6 +4,7 @@
#include "components/autofill/core/common/autofill_tick_clock.h"
+#include "base/check.h"
#include "base/time/default_tick_clock.h"
#include "base/time/tick_clock.h"
diff --git a/chromium/components/autofill/core/common/form_data.cc b/chromium/components/autofill/core/common/form_data.cc
index 89327407016..b78899ab47d 100644
--- a/chromium/components/autofill/core/common/form_data.cc
+++ b/chromium/components/autofill/core/common/form_data.cc
@@ -144,49 +144,6 @@ bool FormData::DynamicallySameFormAs(const FormData& form) const {
return true;
}
-bool FormData::IdentityComparator::operator()(const FormData& a,
- const FormData& b) const {
- // |unique_renderer_id| uniquely identifies the form, if and only if it is
- // set; the other members compared below together uniquely identify the form
- // as well.
- auto tie = [](const FormData& f) {
- return std::tie(f.host_frame, f.unique_renderer_id, f.name, f.id_attribute,
- f.name_attribute, f.url, f.action, f.is_form_tag);
- };
- if (tie(a) < tie(b))
- return true;
- if (tie(b) < tie(a))
- return false;
- // A less-than relation on FormData::child_frames.
- auto less_child_frames =
- [](const std::vector<FrameTokenWithPredecessor>& as,
- const std::vector<FrameTokenWithPredecessor>& bs) {
- return base::ranges::lexicographical_compare(
- as, bs, [](const auto& a, const auto& b) {
- return std::tie(a.token, a.predecessor) <
- std::tie(b.token, b.predecessor);
- });
- };
- if (less_child_frames(a.child_frames, b.child_frames))
- return true;
- if (less_child_frames(b.child_frames, a.child_frames))
- return false;
- return base::ranges::lexicographical_compare(
- a.fields, b.fields, FormFieldData::IdentityComparator());
-}
-
-// Used for `Autofill.ExtractNewForms.ShallowEqualityDiffersFromDeepEquality`
-// metric. These values are persisted to logs. Entries should not be renumbered
-// and numeric values should never be reused.
-// TODO(crbug/1215333): Remove after the `AutofillUseNewFormExtraction`
-// feature is deleted.
-enum class FormDataEquality {
- kShallowCompEnough = 0,
- kDeepCompNeeded = 1,
- kEqualForms = 2,
- kMaxValue = kEqualForms,
-};
-
// static
bool FormData::DeepEqual(const FormData& a, const FormData& b) {
// We compare all unique identifiers first, including the field renderer IDs,
@@ -196,9 +153,6 @@ bool FormData::DeepEqual(const FormData& a, const FormData& b) {
!base::ranges::equal(a.fields, b.fields, {},
&FormFieldData::unique_renderer_id,
&FormFieldData::unique_renderer_id)) {
- UMA_HISTOGRAM_ENUMERATION(
- "Autofill.ExtractNewForms.ShallowEqualityDiffersFromDeepEquality",
- FormDataEquality::kShallowCompEnough);
return false;
}
@@ -206,14 +160,8 @@ bool FormData::DeepEqual(const FormData& a, const FormData& b) {
a.name_attribute != b.name_attribute || a.url != b.url ||
a.action != b.action || a.is_form_tag != b.is_form_tag ||
!base::ranges::equal(a.fields, b.fields, &FormFieldData::DeepEqual)) {
- UMA_HISTOGRAM_ENUMERATION(
- "Autofill.ExtractNewForms.ShallowEqualityDiffersFromDeepEquality",
- FormDataEquality::kDeepCompNeeded);
return false;
}
- UMA_HISTOGRAM_ENUMERATION(
- "Autofill.ExtractNewForms.ShallowEqualityDiffersFromDeepEquality",
- FormDataEquality::kEqualForms);
return true;
}
@@ -338,9 +286,4 @@ LogBuffer& operator<<(LogBuffer& buffer, const FormData& form) {
return buffer;
}
-bool FormDataEqualForTesting(const FormData& lhs, const FormData& rhs) {
- FormData::IdentityComparator less;
- return !less(lhs, rhs) && !less(rhs, lhs);
-}
-
} // namespace autofill
diff --git a/chromium/components/autofill/core/common/form_data.h b/chromium/components/autofill/core/common/form_data.h
index 0555e0564b2..4574e8996fa 100644
--- a/chromium/components/autofill/core/common/form_data.h
+++ b/chromium/components/autofill/core/common/form_data.h
@@ -134,14 +134,6 @@ struct FrameTokenWithPredecessor {
// [4] https://html.spec.whatwg.org/multipage/input.html#attr-input-type
// clang-format on
struct FormData {
- // Less-than relation for STL containers. Compares only members needed to
- // uniquely identify a form.
- // TODO(crbug.com/1215333): Remove once `AutofillUseNewFormExtraction` is
- // launched.
- struct IdentityComparator {
- bool operator()(const FormData& a, const FormData& b) const;
- };
-
// Returns true if all members of forms |a| and |b| are identical.
static bool DeepEqual(const FormData& a, const FormData& b);
@@ -181,7 +173,6 @@ struct FormData {
// The name attribute of the form.
std::u16string name_attribute;
- // NOTE: update IdentityComparator when adding new a member.
// NOTE: update SameFormAs() if needed when adding new a member.
// NOTE: update SimilarFormAs() if needed when adding new a member.
// NOTE: update DynamicallySameFormAs() if needed when adding new a member.
@@ -287,8 +278,6 @@ bool DeserializeFormData(base::PickleIterator* iter, FormData* form_data);
LogBuffer& operator<<(LogBuffer& buffer, const FormData& form);
-bool FormDataEqualForTesting(const FormData& lhs, const FormData& rhs);
-
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_DATA_H_
diff --git a/chromium/components/autofill/core/common/form_field_data.cc b/chromium/components/autofill/core/common/form_field_data.cc
index abe5b863478..e0dd60f8d1f 100644
--- a/chromium/components/autofill/core/common/form_field_data.cc
+++ b/chromium/components/autofill/core/common/form_field_data.cc
@@ -223,7 +223,7 @@ auto SimilarityTuple(const FormFieldData& f) {
}
auto DynamicIdentityTuple(const FormFieldData& f) {
- return std::tuple_cat(CommonTuple(f), std::make_tuple(f.IsVisible()));
+ return std::tuple_cat(CommonTuple(f), std::make_tuple(f.IsFocusable()));
}
auto IdentityTuple(const FormFieldData& f) {
@@ -268,12 +268,6 @@ bool FormFieldData::DynamicallySameFieldAs(const FormFieldData& field) const {
return DynamicIdentityTuple(*this) == DynamicIdentityTuple(field);
}
-bool FormFieldData::IdentityComparator::operator()(
- const FormFieldData& a,
- const FormFieldData& b) const {
- return IdentityTuple(a) < IdentityTuple(b);
-}
-
bool FormFieldData::IsTextInputElement() const {
return form_control_type == "text" || form_control_type == "password" ||
form_control_type == "search" || form_control_type == "tel" ||
diff --git a/chromium/components/autofill/core/common/form_field_data.h b/chromium/components/autofill/core/common/form_field_data.h
index ba879c29a67..de784bca427 100644
--- a/chromium/components/autofill/core/common/form_field_data.h
+++ b/chromium/components/autofill/core/common/form_field_data.h
@@ -70,13 +70,6 @@ struct FormFieldData {
using RoleAttribute = mojom::FormFieldData_RoleAttribute;
using LabelSource = mojom::FormFieldData_LabelSource;
- // TODO(crbug/1211834): This comparator is deprecated.
- // Less-than relation for STL containers. Compares only members needed to
- // uniquely identify a field.
- struct IdentityComparator {
- bool operator()(const FormFieldData& a, const FormFieldData& b) const;
- };
-
// Returns true if all members of fields |a| and |b| are identical.
static bool DeepEqual(const FormFieldData& a, const FormFieldData& b);
@@ -124,8 +117,9 @@ struct FormFieldData {
bool IsPasswordInputElement() const;
- // Returns true if the field is visible to the user.
- bool IsVisible() const {
+ // Returns true if the field is focusable to the user.
+ // This is an approximation of visibility with false positives.
+ bool IsFocusable() const {
return is_focusable && role != RoleAttribute::kPresentation;
}
@@ -149,7 +143,6 @@ struct FormFieldData {
#define EXPECT_EQ_UNIQUE_ID(expected, actual)
#endif
- // NOTE: update IdentityComparator when adding new a member.
// NOTE: update SameFieldAs() if needed when adding new a member.
// NOTE: update SimilarFieldAs() if needed when adding new a member.
// NOTE: update DynamicallySameFieldAs() if needed when adding new a member.
@@ -214,6 +207,7 @@ struct FormFieldData {
bool is_autofilled = false;
CheckStatus check_status = CheckStatus::kNotCheckable;
bool is_focusable = true;
+ bool is_visible = true;
bool should_autocomplete = true;
RoleAttribute role = RoleAttribute::kOther;
base::i18n::TextDirection text_direction = base::i18n::UNKNOWN_DIRECTION;
@@ -246,6 +240,11 @@ struct FormFieldData {
// are handled very differently in Autofill.
std::vector<std::u16string> datalist_values;
std::vector<std::u16string> datalist_labels;
+
+ // When sent from browser to renderer, this bit indicates whether a field
+ // should be filled even though it is already considered autofilled OR
+ // user modified.
+ bool force_override = false;
};
// Serialize and deserialize FormFieldData. These are used when FormData objects
@@ -260,6 +259,7 @@ std::ostream& operator<<(std::ostream& os, const FormFieldData& field);
// Prefer to use this macro in place of |EXPECT_EQ()| for comparing
// |FormFieldData|s in test code.
+// TODO(crbug.com/1208354): Replace this with FormData::DeepEqual().
#define EXPECT_FORM_FIELD_DATA_EQUALS(expected, actual) \
do { \
EXPECT_EQ_UNIQUE_ID(expected, actual); \
diff --git a/chromium/components/autofill/core/common/logging/log_buffer.cc b/chromium/components/autofill/core/common/logging/log_buffer.cc
index 18037e67623..7f2dffbebf6 100644
--- a/chromium/components/autofill/core/common/logging/log_buffer.cc
+++ b/chromium/components/autofill/core/common/logging/log_buffer.cc
@@ -44,8 +44,8 @@ void AppendChildToLastNode(std::vector<base::Value>* buffer,
return;
}
- base::Value::ListStorage list;
- list.emplace_back(std::move(new_child));
+ base::Value::List list;
+ list.Append(std::move(new_child));
parent.SetKey("children", base::Value(std::move(list)));
}
@@ -75,9 +75,9 @@ bool TryCoalesceString(std::vector<base::Value>* buffer,
}
base::Value CreateEmptyFragment() {
- base::Value::DictStorage storage;
- storage.try_emplace("type", "fragment");
- return base::Value(storage);
+ base::Value::Dict dict;
+ dict.Set("type", "fragment");
+ return base::Value(std::move(dict));
}
} // namespace
@@ -115,10 +115,10 @@ LogBuffer& operator<<(LogBuffer& buf, Tag&& tag) {
if (!buf.active())
return buf;
- base::Value::DictStorage storage;
- storage.try_emplace("type", "element");
- storage.try_emplace("value", std::move(tag.name));
- buf.buffer_.emplace_back(std::move(storage));
+ base::Value::Dict dict;
+ dict.Set("type", "element");
+ dict.Set("value", std::move(tag.name));
+ buf.buffer_.emplace_back(std::move(dict));
return buf;
}
@@ -147,8 +147,8 @@ LogBuffer& operator<<(LogBuffer& buf, Attrib&& attrib) {
attributes->SetKey(std::move(attrib.name),
base::Value(std::move(attrib.value)));
} else {
- base::Value::DictStorage dict;
- dict.try_emplace(std::move(attrib.name), std::move(attrib.value));
+ base::Value::Dict dict;
+ dict.Set(attrib.name, std::move(attrib.value));
node.SetKey("attributes", base::Value(std::move(dict)));
}
@@ -171,12 +171,12 @@ LogBuffer& operator<<(LogBuffer& buf, base::StringPiece text) {
if (TryCoalesceString(&buf.buffer_, text))
return buf;
- base::Value::DictStorage storage;
- storage.try_emplace("type", "text");
+ base::Value::Dict dict;
+ dict.Set("type", "text");
// This text is not HTML escaped because the rest of the frame work takes care
// of that and it must not be escaped twice.
- storage.try_emplace("value", text);
- base::Value node_to_add(std::move(storage));
+ dict.Set("value", text);
+ base::Value node_to_add(std::move(dict));
AppendChildToLastNode(&buf.buffer_, std::move(node_to_add));
return buf;
}
diff --git a/chromium/components/autofill/core/common/mojom/BUILD.gn b/chromium/components/autofill/core/common/mojom/BUILD.gn
index 7a7eaa7bcac..f076b75e8cb 100644
--- a/chromium/components/autofill/core/common/mojom/BUILD.gn
+++ b/chromium/components/autofill/core/common/mojom/BUILD.gn
@@ -72,6 +72,11 @@ mojom("mojo_types") {
mojom = "autofill.mojom.PasswordGenerationUIData"
cpp = "::autofill::password_generation::PasswordGenerationUIData"
},
+ {
+ mojom = "autofill.mojom.TouchToFillEligible"
+ cpp = "::autofill::TouchToFillEligible"
+ copyable_pass_by_value = true
+ },
]
traits_headers = [ "autofill_types_mojom_traits.h" ]
traits_sources = [ "autofill_types_mojom_traits.cc" ]
diff --git a/chromium/components/autofill/core/common/mojom/autofill_types.mojom b/chromium/components/autofill/core/common/mojom/autofill_types.mojom
index d135727335d..a709ecc3fba 100644
--- a/chromium/components/autofill/core/common/mojom/autofill_types.mojom
+++ b/chromium/components/autofill/core/common/mojom/autofill_types.mojom
@@ -105,7 +105,7 @@ enum SubmissionReadinessState {
kTwoFields = 7,
};
-// autofill::FrameToken
+// autofill::FrameToken (components/autofill/core/common/unique_ids.h)
struct FrameToken {
// The token wrapped by a LocalFrameToken or a RemoteFrameToken.
mojo_base.mojom.UnguessableToken token;
@@ -113,29 +113,30 @@ struct FrameToken {
bool is_local;
};
-// autofill::
+// autofill::FrameTokenWithPredecessor
+// (components/autofill/core/common/form_data.h)
struct FrameTokenWithPredecessor {
FrameToken token;
int32 predecessor;
};
-// autofill::FormRendererId
+// autofill::FormRendererId (components/autofill/core/common/unique_ids.h)
struct FormRendererId {
- uint32 id;
+ uint64 id;
};
-// autofill::FieldRendererId
+// autofill::FieldRendererId (components/autofill/core/common/unique_ids.h)
struct FieldRendererId {
- uint32 id;
+ uint64 id;
};
-// autofill::SelectOption
+// autofill::SelectOption (components/autofill/core/common/form_field_data.h)
struct SelectOption {
mojo_base.mojom.String16 value;
mojo_base.mojom.String16 content;
};
-// autofill::FormFieldData
+// autofill::FormFieldData (components/autofill/core/common/form_field_data.h)
struct FormFieldData {
enum CheckStatus {
kNotCheckable,
@@ -187,6 +188,7 @@ struct FormFieldData {
string section;
CheckStatus check_status;
bool is_focusable;
+ bool is_visible;
bool should_autocomplete;
RoleAttribute role;
mojo_base.mojom.TextDirection text_direction;
@@ -202,14 +204,17 @@ struct FormFieldData {
array<mojo_base.mojom.String16> datalist_values;
array<mojo_base.mojom.String16> datalist_labels;
+
+ bool force_override;
};
+// autofill::ButtonTitleInfo (components/autofill/core/common/form_data.h)
struct ButtonTitleInfo {
mojo_base.mojom.String16 title;
ButtonTitleType type;
};
-// autofill::FormData
+// autofill::FormData (components/autofill/core/common/form_data.h)
struct FormData {
mojo_base.mojom.String16 id_attribute;
mojo_base.mojom.String16 name_attribute;
@@ -227,6 +232,7 @@ struct FormData {
};
// autofill::FormFieldDataPredictions
+// (components/autofill/core/common/form_field_data_predictions.h)
struct FormFieldDataPredictions {
string host_form_signature;
string signature;
@@ -238,6 +244,7 @@ struct FormFieldDataPredictions {
};
// autofill::FormDataPredictions
+// (components/autofill/core/common/form_data_predictions.h)
struct FormDataPredictions {
FormData data;
string signature;
@@ -245,6 +252,7 @@ struct FormDataPredictions {
};
// autofill::PasswordAndMetadata
+// (components/autofill/core/common/password_form_fill_data.h)
struct PasswordAndMetadata {
mojo_base.mojom.String16 username;
mojo_base.mojom.String16 password;
@@ -253,6 +261,7 @@ struct PasswordAndMetadata {
};
// autofill::PasswordFormFillData
+// (components/autofill/core/common/password_form_fill_data.h)
struct PasswordFormFillData {
FormRendererId form_renderer_id;
url.mojom.Url url;
@@ -267,12 +276,14 @@ struct PasswordFormFillData {
};
// autofill::PasswordFormGenerationData
+// (components/autofill/core/common/password_form_generation_data.h)
struct PasswordFormGenerationData {
FieldRendererId new_password_renderer_id;
FieldRendererId confirmation_password_renderer_id;
};
// autofill::password_generation::PasswordGenerationUIData
+// (components/autofill/core/common/password_generation_util.h)
struct PasswordGenerationUIData {
gfx.mojom.RectF bounds;
int32 max_length;
@@ -284,6 +295,7 @@ struct PasswordGenerationUIData {
};
// autofill::ParsingResult
+// (components/autofill/core/common/password_form_fill_data.h)
struct ParsingResult {
FieldRendererId username_renderer_id;
FieldRendererId password_renderer_id;
@@ -309,3 +321,8 @@ enum RendererFormDataAction {
kFill, // The renderer should fill the form data.
kPreview, // The renderer should preview the form data.
};
+
+// autofill::TouchToFillEligible (components/autofill/core/common/aliases.h)
+struct TouchToFillEligible {
+ bool eligible;
+};
diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
index 8c64af0c400..88fe33bc5f2 100644
--- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
+++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
@@ -120,6 +120,7 @@ bool StructTraits<
return false;
out->is_focusable = data.is_focusable();
+ out->is_visible = data.is_visible();
out->should_autocomplete = data.should_autocomplete();
if (!data.ReadRole(&out->role))
@@ -147,6 +148,8 @@ bool StructTraits<
if (!data.ReadDatalistLabels(&out->datalist_labels))
return false;
+ out->force_override = data.force_override();
+
return true;
}
@@ -311,4 +314,12 @@ bool StructTraits<
data.ReadConfirmPasswordRendererId(&out->confirm_password_renderer_id);
}
+bool StructTraits<autofill::mojom::TouchToFillEligibleDataView,
+ autofill::TouchToFillEligible>::
+ Read(autofill::mojom::TouchToFillEligibleDataView data,
+ autofill::TouchToFillEligible* out) {
+ *out = autofill::TouchToFillEligible(data.eligible());
+ return true;
+}
+
} // namespace mojo
diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
index ec8fa9db2b6..9060cf6a44a 100644
--- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
+++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/i18n/rtl.h"
+#include "components/autofill/core/common/aliases.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/form_field_data.h"
@@ -60,7 +61,7 @@ struct StructTraits<autofill::mojom::FrameTokenWithPredecessorDataView,
template <>
struct StructTraits<autofill::mojom::FormRendererIdDataView,
autofill::FormRendererId> {
- static uint32_t id(autofill::FormRendererId r) { return r.value(); }
+ static uint64_t id(autofill::FormRendererId r) { return r.value(); }
static bool Read(autofill::mojom::FormRendererIdDataView data,
autofill::FormRendererId* out);
@@ -69,7 +70,7 @@ struct StructTraits<autofill::mojom::FormRendererIdDataView,
template <>
struct StructTraits<autofill::mojom::FieldRendererIdDataView,
autofill::FieldRendererId> {
- static uint32_t id(autofill::FieldRendererId r) { return r.value(); }
+ static uint64_t id(autofill::FieldRendererId r) { return r.value(); }
static bool Read(autofill::mojom::FieldRendererIdDataView data,
autofill::FieldRendererId* out);
@@ -180,6 +181,10 @@ struct StructTraits<autofill::mojom::FormFieldDataDataView,
return r.is_focusable;
}
+ static bool is_visible(const autofill::FormFieldData& r) {
+ return r.is_visible;
+ }
+
static bool should_autocomplete(const autofill::FormFieldData& r) {
return r.should_autocomplete;
}
@@ -232,6 +237,10 @@ struct StructTraits<autofill::mojom::FormFieldDataDataView,
static bool Read(autofill::mojom::FormFieldDataDataView data,
autofill::FormFieldData* out);
+
+ static bool force_override(const autofill::FormFieldData& r) {
+ return r.force_override;
+ }
};
template <>
@@ -539,6 +548,15 @@ struct StructTraits<autofill::mojom::ParsingResultDataView,
autofill::ParsingResult* out);
};
+template <>
+struct StructTraits<autofill::mojom::TouchToFillEligibleDataView,
+ autofill::TouchToFillEligible> {
+ static bool eligible(autofill::TouchToFillEligible r) { return r.value(); }
+
+ static bool Read(autofill::mojom::TouchToFillEligibleDataView data,
+ autofill::TouchToFillEligible* out);
+};
+
} // namespace mojo
#endif // COMPONENTS_AUTOFILL_CORE_COMMON_MOJOM_AUTOFILL_TYPES_MOJOM_TRAITS_H_
diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
index 98bfa5d86e8..b3f8d8f0a82 100644
--- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
+++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
@@ -27,12 +27,6 @@ const std::vector<const char*> kOptions = {"Option1", "Option2", "Option3",
"Option4"};
namespace {
-template <typename T>
-bool EquivalentData(const T& a, const T& b) {
- typename T::IdentityComparator less;
- return !less(a, b) && !less(b, a);
-}
-
void CreateTestFieldDataPredictions(const std::string& signature,
FormFieldDataPredictions* field_predict) {
field_predict->host_form_signature = "TestHostFormSignature";
@@ -86,12 +80,12 @@ void CheckEqualPasswordFormFillData(const PasswordFormFillData& expected,
const PasswordFormFillData& actual) {
EXPECT_EQ(expected.form_renderer_id, actual.form_renderer_id);
EXPECT_EQ(expected.action, actual.action);
- EXPECT_TRUE(
- EquivalentData(test::WithoutUnserializedData(expected.username_field),
- actual.username_field));
- EXPECT_TRUE(
- EquivalentData(test::WithoutUnserializedData(expected.password_field),
- actual.password_field));
+ EXPECT_TRUE(FormFieldData::DeepEqual(
+ test::WithoutUnserializedData(expected.username_field),
+ actual.username_field));
+ EXPECT_TRUE(FormFieldData::DeepEqual(
+ test::WithoutUnserializedData(expected.password_field),
+ actual.password_field));
EXPECT_EQ(expected.preferred_realm, actual.preferred_realm);
EXPECT_EQ(expected.uses_account_store, actual.uses_account_store);
@@ -133,7 +127,7 @@ void CheckEqualPassPasswordGenerationUIData(
class AutofillTypeTraitsTestImpl : public testing::Test,
public mojom::TypeTraitsTest {
public:
- AutofillTypeTraitsTestImpl() {}
+ AutofillTypeTraitsTestImpl() = default;
mojo::PendingRemote<mojom::TypeTraitsTest> GetTypeTraitsTestRemote() {
mojo::PendingRemote<mojom::TypeTraitsTest> remote;
@@ -191,7 +185,8 @@ void ExpectFormFieldData(const FormFieldData& expected,
base::OnceClosure closure,
const FormFieldData& passed) {
EXPECT_TRUE(passed.host_frame.is_empty());
- EXPECT_TRUE(EquivalentData(test::WithoutUnserializedData(expected), passed));
+ EXPECT_TRUE(FormFieldData::DeepEqual(test::WithoutUnserializedData(expected),
+ passed));
EXPECT_EQ(expected.value, passed.value);
EXPECT_EQ(expected.user_input, passed.user_input);
std::move(closure).Run();
@@ -201,7 +196,8 @@ void ExpectFormData(const FormData& expected,
base::OnceClosure closure,
const FormData& passed) {
EXPECT_TRUE(passed.host_frame.is_empty());
- EXPECT_TRUE(EquivalentData(test::WithoutUnserializedData(expected), passed));
+ EXPECT_TRUE(
+ FormData::DeepEqual(test::WithoutUnserializedData(expected), passed));
std::move(closure).Run();
}
diff --git a/chromium/components/autofill/core/common/password_generation_util.cc b/chromium/components/autofill/core/common/password_generation_util.cc
index 8b223f1ca6f..4e2eee53780 100644
--- a/chromium/components/autofill/core/common/password_generation_util.cc
+++ b/chromium/components/autofill/core/common/password_generation_util.cc
@@ -19,7 +19,7 @@ PasswordGenerationUIData::PasswordGenerationUIData(
FieldRendererId generation_element_id,
bool is_generation_element_password_type,
base::i18n::TextDirection text_direction,
- const autofill::FormData& form_data)
+ const FormData& form_data)
: bounds(bounds),
max_length(max_length),
generation_element(generation_element),
diff --git a/chromium/components/autofill/core/common/password_generation_util.h b/chromium/components/autofill/core/common/password_generation_util.h
index a2a596037cb..3727a42b83f 100644
--- a/chromium/components/autofill/core/common/password_generation_util.h
+++ b/chromium/components/autofill/core/common/password_generation_util.h
@@ -115,7 +115,7 @@ struct PasswordGenerationUIData {
FieldRendererId generation_element_id,
bool is_generation_element_password_type,
base::i18n::TextDirection text_direction,
- const autofill::FormData& form_data);
+ const FormData& form_data);
PasswordGenerationUIData();
PasswordGenerationUIData(const PasswordGenerationUIData& rhs);
PasswordGenerationUIData(PasswordGenerationUIData&& rhs);
@@ -144,7 +144,7 @@ struct PasswordGenerationUIData {
base::i18n::TextDirection text_direction;
// The form associated with the password field.
- autofill::FormData form_data;
+ FormData form_data;
};
void LogPasswordGenerationEvent(PasswordGenerationEvent event);
diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.cc b/chromium/components/autofill/core/common/save_password_progress_logger.cc
index 06e0b5acd0f..eb4857da346 100644
--- a/chromium/components/autofill/core/common/save_password_progress_logger.cc
+++ b/chromium/components/autofill/core/common/save_password_progress_logger.cc
@@ -38,20 +38,18 @@ bool IsUnwantedInElementID(char c) {
SavePasswordProgressLogger::SavePasswordProgressLogger() = default;
SavePasswordProgressLogger::~SavePasswordProgressLogger() = default;
-std::string FormSignatureToDebugString(autofill::FormSignature form_signature) {
- return base::StrCat(
- {NumberToString(form_signature.value()), " - ",
- NumberToString(autofill::HashFormSignature(form_signature))});
+std::string FormSignatureToDebugString(FormSignature form_signature) {
+ return base::StrCat({NumberToString(form_signature.value()), " - ",
+ NumberToString(HashFormSignature(form_signature))});
}
void SavePasswordProgressLogger::LogFormData(
SavePasswordProgressLogger::StringID label,
const FormData& form_data) {
std::string message = GetStringFromID(label) + ": {\n";
- message +=
- GetStringFromID(STRING_FORM_SIGNATURE) + ": " +
- FormSignatureToDebugString(autofill::CalculateFormSignature(form_data)) +
- "\n";
+ message += GetStringFromID(STRING_FORM_SIGNATURE) + ": " +
+ FormSignatureToDebugString(CalculateFormSignature(form_data)) +
+ "\n";
message +=
GetStringFromID(STRING_ORIGIN) + ": " + ScrubURL(form_data.url) + "\n";
message +=
diff --git a/chromium/components/autofill/core/common/signatures.cc b/chromium/components/autofill/core/common/signatures.cc
index afbbfc1495a..1bf4e8419d5 100644
--- a/chromium/components/autofill/core/common/signatures.cc
+++ b/chromium/components/autofill/core/common/signatures.cc
@@ -134,11 +134,11 @@ uint32_t StrToHash32Bit(base::StringPiece str) {
return PackBytes(base::make_span(digest).subspan<0, 4>());
}
-int64_t HashFormSignature(autofill::FormSignature form_signature) {
+int64_t HashFormSignature(FormSignature form_signature) {
return static_cast<uint64_t>(form_signature.value()) % 1021;
}
-int64_t HashFieldSignature(autofill::FieldSignature field_signature) {
+int64_t HashFieldSignature(FieldSignature field_signature) {
return static_cast<uint64_t>(field_signature.value()) % 1021;
}
diff --git a/chromium/components/autofill/core/common/signatures.h b/chromium/components/autofill/core/common/signatures.h
index 607f37bc0f9..6a35e0255c6 100644
--- a/chromium/components/autofill/core/common/signatures.h
+++ b/chromium/components/autofill/core/common/signatures.h
@@ -52,10 +52,10 @@ uint64_t StrToHash64Bit(base::StringPiece str);
uint32_t StrToHash32Bit(base::StringPiece str);
// Reduce FieldSignature space (in UKM) to a small range for privacy reasons.
-int64_t HashFormSignature(autofill::FormSignature form_signature);
+int64_t HashFormSignature(FormSignature form_signature);
// Reduce FieldSignature space (in UKM) to a small range for privacy reasons.
-int64_t HashFieldSignature(autofill::FieldSignature field_signature);
+int64_t HashFieldSignature(FieldSignature field_signature);
} // namespace autofill
diff --git a/chromium/components/autofill/ios/OWNERS b/chromium/components/autofill/ios/OWNERS
index 3ebff38dca4..1f00f795aba 100644
--- a/chromium/components/autofill/ios/OWNERS
+++ b/chromium/components/autofill/ios/OWNERS
@@ -1 +1,2 @@
olivierrobin@chromium.org
+sebsg@chromium.org
diff --git a/chromium/components/autofill/ios/browser/BUILD.gn b/chromium/components/autofill/ios/browser/BUILD.gn
index b54e4965a76..213faa6a46c 100644
--- a/chromium/components/autofill/ios/browser/BUILD.gn
+++ b/chromium/components/autofill/ios/browser/BUILD.gn
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//ios/web/js_compile.gni")
+import("//ios/web/public/js_messaging/optimize_js.gni")
source_set("browser") {
configs += [ "//build/config/compiler:enable_arc" ]
@@ -77,13 +77,13 @@ source_set("util") {
]
}
-js_compile_bundle("autofill_js") {
- closure_entry_point = "__crWeb.autofill"
+optimize_js("autofill_js") {
+ primary_script = "resources/autofill_controller.js"
sources = [ "resources/autofill_controller.js" ]
}
-js_compile_bundle("suggestion_controller_js") {
- closure_entry_point = "__crWeb.suggestion"
+optimize_js("suggestion_controller_js") {
+ primary_script = "resources/suggestion_controller.js"
sources = [ "resources/suggestion_controller.js" ]
}
diff --git a/chromium/components/autofill/ios/browser/autofill_agent.mm b/chromium/components/autofill/ios/browser/autofill_agent.mm
index dd8b0bac42c..c195f976b35 100644
--- a/chromium/components/autofill/ios/browser/autofill_agent.mm
+++ b/chromium/components/autofill/ios/browser/autofill_agent.mm
@@ -333,7 +333,8 @@ void GetFormField(autofill::FormFieldData* field,
// -showAutofillPopup:popupDelegate:.
autofillManager->OnAskForValuesToFill(++_lastQueryID, form, field,
gfx::RectF(),
- /*autoselect_first_suggestion=*/false);
+ /*autoselect_first_suggestion=*/false,
+ autofill::TouchToFillEligible(false));
}
- (void)checkIfSuggestionsAvailableForForm:
@@ -581,16 +582,16 @@ void GetFormField(autofill::FormFieldData* field,
// Value will contain the text to be filled in the selected element while
// displayDescription will contain a summary of the data to be filled in
// the other elements.
- value = SysUTF16ToNSString(popup_suggestion.value);
+ value = SysUTF16ToNSString(popup_suggestion.main_text.value);
displayDescription = SysUTF16ToNSString(popup_suggestion.label);
} else if (popup_suggestion.frontend_id ==
autofill::POPUP_ITEM_ID_CLEAR_FORM) {
// Show the "clear form" button.
- value = SysUTF16ToNSString(popup_suggestion.value);
+ value = SysUTF16ToNSString(popup_suggestion.main_text.value);
} else if (popup_suggestion.frontend_id ==
autofill::POPUP_ITEM_ID_SHOW_ACCOUNT_CARDS) {
// Show opt-in for showing cards from account.
- value = SysUTF16ToNSString(popup_suggestion.value);
+ value = SysUTF16ToNSString(popup_suggestion.main_text.value);
}
if (!value)
diff --git a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm
index eb64f04b979..049f7f025b3 100644
--- a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm
+++ b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm
@@ -129,7 +129,7 @@ TEST_F(AutofillAgentTests,
std::string locale("en");
autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
&fake_web_state_, &client_, nil, locale,
- autofill::BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+ autofill::AutofillManager::EnableDownloadManager(false));
autofill::FormData form;
form.url = GURL("https://myform.com");
@@ -179,7 +179,7 @@ TEST_F(AutofillAgentTests,
fillFormData:form
inFrame:fake_web_state_.GetWebFramesManager()->GetMainWebFrame()];
fake_web_state_.WasShown();
- EXPECT_EQ("__gCrWeb.autofill.fillForm({\"fields\":{\"2\":{\"section\":\"\","
+ EXPECT_EQ(u"__gCrWeb.autofill.fillForm({\"fields\":{\"2\":{\"section\":\"\","
"\"value\":\"number_value\"},"
"\"3\":{\"section\":\"\",\"value\":\"name_value\"}},"
"\"formName\":\"CC form\",\"formRendererID\":1}, 0);",
@@ -369,7 +369,7 @@ TEST_F(AutofillAgentTests, FrameInitializationOrderFrames) {
std::string locale("en");
autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
&fake_web_state_, &client_, nil, locale,
- autofill::BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+ autofill::AutofillManager::EnableDownloadManager(false));
// Remove the current main frame.
RemoveWebFrame(fake_main_frame_->GetFrameId());
diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.h b/chromium/components/autofill/ios/browser/autofill_driver_ios.h
index 11c752c003b..1b183a73150 100644
--- a/chromium/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.h
@@ -34,8 +34,7 @@ class AutofillDriverIOS : public AutofillDriver {
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager);
+ AutofillManager::EnableDownloadManager enable_download_manager);
static AutofillDriverIOS* FromWebStateAndWebFrame(web::WebState* web_state,
web::WebFrame* web_frame);
@@ -55,8 +54,6 @@ class AutofillDriverIOS : public AutofillDriver {
const url::Origin& triggered_origin,
const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map)
override;
- void PropagateAutofillPredictions(
- const std::vector<autofill::FormStructure*>& forms) override;
void HandleParsedForms(const std::vector<const FormData*>& forms) override;
void SendAutofillTypePredictionsToRenderer(
const std::vector<FormStructure*>& forms) override;
@@ -87,13 +84,13 @@ class AutofillDriverIOS : public AutofillDriver {
void set_processed(bool processed) { processed_ = processed; }
protected:
- AutofillDriverIOS(web::WebState* web_state,
- web::WebFrame* web_frame,
- AutofillClient* client,
- id<AutofillDriverIOSBridge> bridge,
- const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager);
+ AutofillDriverIOS(
+ web::WebState* web_state,
+ web::WebFrame* web_frame,
+ AutofillClient* client,
+ id<AutofillDriverIOSBridge> bridge,
+ const std::string& app_locale,
+ AutofillManager::EnableDownloadManager enable_download_manager);
private:
// The WebState with which this object is associated.
diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm
index 79b81ebca91..853cc79f283 100644
--- a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -28,8 +28,7 @@ void AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager) {
+ AutofillManager::EnableDownloadManager enable_download_manager) {
// By the time this method is called, no web_frame is available. This method
// only prepares the factory and the AutofillDriverIOS will be created in the
// first call to FromWebStateAndWebFrame.
@@ -52,8 +51,7 @@ AutofillDriverIOS::AutofillDriverIOS(
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager)
+ AutofillManager::EnableDownloadManager enable_download_manager)
: web_state_(web_state),
bridge_(bridge),
browser_autofill_manager_(this,
@@ -63,7 +61,7 @@ AutofillDriverIOS::AutofillDriverIOS(
web_frame_id_ = web::GetWebFrameId(web_frame);
}
-AutofillDriverIOS::~AutofillDriverIOS() {}
+AutofillDriverIOS::~AutofillDriverIOS() = default;
bool AutofillDriverIOS::IsIncognito() const {
return web_state_->GetBrowserState()->IsOffTheRecord();
@@ -113,12 +111,6 @@ std::vector<FieldGlobalId> AutofillDriverIOS::FillOrPreviewForm(
return safe_fields;
}
-void AutofillDriverIOS::PropagateAutofillPredictions(
- const std::vector<autofill::FormStructure*>& forms) {
- browser_autofill_manager_.client()->PropagateAutofillPredictions(nullptr,
- forms);
-}
-
void AutofillDriverIOS::HandleParsedForms(
const std::vector<const FormData*>& forms) {
const std::map<FormGlobalId, std::unique_ptr<FormStructure>>& map =
diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.h b/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.h
index ad55589f43b..3143dfaa1fe 100644
--- a/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.h
+++ b/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.h
@@ -31,8 +31,7 @@ class AutofillDriverIOSWebFrameFactory
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager);
+ AutofillManager::EnableDownloadManager enable_download_manager);
~AutofillDriverIOSWebFrameFactory() override;
AutofillDriverIOSWebFrameFactory(
@@ -40,8 +39,7 @@ class AutofillDriverIOSWebFrameFactory
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager);
+ AutofillManager::EnableDownloadManager enable_download_manager);
// Returns a AutofillDriverIOSFromWebFrame for |web_frame|, creating it if
// needed.
@@ -55,7 +53,7 @@ class AutofillDriverIOSWebFrameFactory
AutofillClient* client_ = nullptr;
id<AutofillDriverIOSBridge> bridge_ = nil;
std::string app_locale_;
- BrowserAutofillManager::AutofillDownloadManagerState enable_download_manager_;
+ AutofillManager::EnableDownloadManager enable_download_manager_;
WEB_STATE_USER_DATA_KEY_DECL();
};
@@ -76,8 +74,7 @@ class AutofillDriverIOSRefCountable
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager);
+ AutofillManager::EnableDownloadManager enable_download_manager);
private:
friend class base::RefCountedThreadSafe<AutofillDriverIOSRefCountable>;
@@ -96,21 +93,20 @@ class AutofillDriverIOSWebFrame
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager);
+ AutofillManager::EnableDownloadManager enable_download_manager);
~AutofillDriverIOSWebFrame() override;
AutofillDriverIOS* driver() { return driver_.get(); }
scoped_refptr<AutofillDriverIOSRefCountable> GetRetainableDriver();
- AutofillDriverIOSWebFrame(web::WebState* web_state,
- web::WebFrame* web_frame,
- AutofillClient* client,
- id<AutofillDriverIOSBridge> bridge,
- const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager);
+ AutofillDriverIOSWebFrame(
+ web::WebState* web_state,
+ web::WebFrame* web_frame,
+ AutofillClient* client,
+ id<AutofillDriverIOSBridge> bridge,
+ const std::string& app_locale,
+ AutofillManager::EnableDownloadManager enable_download_manager);
scoped_refptr<AutofillDriverIOSRefCountable> driver_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.mm b/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.mm
index 67b30cf60dc..95f8fd8572d 100644
--- a/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.mm
+++ b/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.mm
@@ -12,8 +12,7 @@ void AutofillDriverIOSWebFrameFactory::CreateForWebStateAndDelegate(
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager) {
+ AutofillManager::EnableDownloadManager enable_download_manager) {
if (FromWebState(web_state))
return;
@@ -28,8 +27,7 @@ AutofillDriverIOSWebFrameFactory::AutofillDriverIOSWebFrameFactory(
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager)
+ AutofillManager::EnableDownloadManager enable_download_manager)
: web_state_(web_state),
client_(client),
bridge_(bridge),
@@ -54,8 +52,7 @@ void AutofillDriverIOSWebFrame::CreateForWebFrameAndDelegate(
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager) {
+ AutofillManager::EnableDownloadManager enable_download_manager) {
if (FromWebFrame(web_frame))
return;
@@ -71,9 +68,7 @@ AutofillDriverIOSRefCountable::AutofillDriverIOSRefCountable(
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager)
-
+ AutofillManager::EnableDownloadManager enable_download_manager)
: AutofillDriverIOS(web_state,
web_frame,
client,
@@ -87,8 +82,7 @@ AutofillDriverIOSWebFrame::AutofillDriverIOSWebFrame(
AutofillClient* client,
id<AutofillDriverIOSBridge> bridge,
const std::string& app_locale,
- BrowserAutofillManager::AutofillDownloadManagerState
- enable_download_manager)
+ AutofillManager::EnableDownloadManager enable_download_manager)
: driver_(base::MakeRefCounted<AutofillDriverIOSRefCountable>(
web_state,
web_frame,
diff --git a/chromium/components/autofill/ios/browser/autofill_java_script_feature.mm b/chromium/components/autofill/ios/browser/autofill_java_script_feature.mm
index f0acaf4ff5d..2663fe1be69 100644
--- a/chromium/components/autofill/ios/browser/autofill_java_script_feature.mm
+++ b/chromium/components/autofill/ios/browser/autofill_java_script_feature.mm
@@ -22,7 +22,7 @@
#endif
namespace {
-const char kScriptName[] = "autofill_js";
+const char kScriptName[] = "autofill_controller";
// The timeout for any JavaScript call in this file.
const int64_t kJavaScriptExecutionTimeoutInSeconds = 5;
diff --git a/chromium/components/autofill/ios/browser/resources/autofill_controller.js b/chromium/components/autofill/ios/browser/resources/autofill_controller.js
index 64718665434..efe852b1b15 100644
--- a/chromium/components/autofill/ios/browser/resources/autofill_controller.js
+++ b/chromium/components/autofill/ios/browser/resources/autofill_controller.js
@@ -15,7 +15,6 @@
* TODO(crbug.com/647084): Enable checkTypes error for this file.
* @suppress {checkTypes}
*/
-goog.provide('__crWeb.autofill');
/**
* The autofill data for a form.
@@ -28,9 +27,6 @@ goog.provide('__crWeb.autofill');
// eslint-disable-next-line no-var
var FormData;
-/* Beginning of anonymous object. */
-(function() {
-
/**
* Namespace for this file. It depends on |__gCrWeb| having already been
* injected.
@@ -411,7 +407,7 @@ __gCrWeb.autofill.extractNewForms = function(
continue;
}
- const form = new __gCrWeb['common'].JSONSafeObject;
+ const form = new __gCrWeb['common'].JSONSafeObject();
if (!__gCrWeb.fill.webFormElementToFormData(
window, formElement, null, extractMask, form, null /* field */)) {
continue;
@@ -435,7 +431,7 @@ __gCrWeb.autofill.extractNewForms = function(
const numEditableUnownedElements =
scanFormControlElements_(unownedControlElements);
if (numEditableUnownedElements > 0) {
- const unownedForm = new __gCrWeb['common'].JSONSafeObject;
+ const unownedForm = new __gCrWeb['common'].JSONSafeObject();
const hasUnownedForm =
__gCrWeb.fill.unownedFormElementsAndFieldSetsToFormData(
window, fieldsets, unownedControlElements, extractMask,
@@ -585,5 +581,3 @@ __gCrWeb.autofill['sanitizedFieldIsEmpty'] = function(value) {
// formatting characters.
return __gCrWeb.common.trim(value.replace(/[-_()/|]/g, '')) === '';
};
-
-}()); // End of anonymous object
diff --git a/chromium/components/autofill/ios/browser/resources/suggestion_controller.js b/chromium/components/autofill/ios/browser/resources/suggestion_controller.js
index 666115a4cc5..de02dcd6b44 100644
--- a/chromium/components/autofill/ios/browser/resources/suggestion_controller.js
+++ b/chromium/components/autofill/ios/browser/resources/suggestion_controller.js
@@ -9,10 +9,6 @@
* TODO(crbug.com/647084): Enable checkTypes error for this file.
* @suppress {checkTypes}
*/
-goog.provide('__crWeb.suggestion');
-
-/* Beginning of anonymous object. */
-(function() {
/**
* Namespace for this file. It depends on |__gCrWeb| having already been
@@ -199,6 +195,31 @@ __gCrWeb.suggestion.getFormElementAfter = function(
};
/**
+ * Tests an element's visibility. This test is expensive so should be used
+ * sparingly.
+ *
+ * @param {Element} element A DOM element.
+ * @return {boolean} true if the |element| is currently part of the visible
+ * DOM.
+ */
+const isElementVisible = function(element) {
+ /** @type {Node} */
+ let node = element;
+ while (node && node !== document) {
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ const style = window.getComputedStyle(/** @type {Element} */ (node));
+ if (style.display === 'none' || style.visibility === 'hidden') {
+ return false;
+ }
+ }
+ // Move up the tree and test again.
+ node = node.parentNode;
+ }
+ // Test reached the top of the DOM without finding a concealed ancestor.
+ return true;
+};
+
+/**
* Returns if an element is reachable in sequential navigation.
*
* @param {Element} element The element that is to be examined.
@@ -249,7 +270,7 @@ __gCrWeb.suggestion.isSequentiallyReachable = function(element) {
}
// Expensive, final check that the element is not concealed.
- return __gCrWeb['common'].isElementVisible(element);
+ return isElementVisible(element);
};
/**
@@ -367,5 +388,3 @@ __gCrWeb.suggestion['hasPreviousNextElements'] = function(formName, fieldName) {
__gCrWeb.suggestion['blurActiveElement'] = function() {
document.activeElement.blur();
};
-
-}()); // End of anonymous object
diff --git a/chromium/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm b/chromium/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm
index 4e485b5de87..409eb07e56a 100644
--- a/chromium/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm
+++ b/chromium/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm
@@ -21,7 +21,7 @@ namespace autofill {
namespace {
-const char kScriptName[] = "suggestion_controller_js";
+const char kScriptName[] = "suggestion_controller";
// The timeout for any JavaScript call in this file.
const int64_t kJavaScriptExecutionTimeoutInSeconds = 5;
diff --git a/chromium/components/autofill/ios/form_util/BUILD.gn b/chromium/components/autofill/ios/form_util/BUILD.gn
index a44bd46a013..fc516224e21 100644
--- a/chromium/components/autofill/ios/form_util/BUILD.gn
+++ b/chromium/components/autofill/ios/form_util/BUILD.gn
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//ios/web/js_compile.gni")
+import("//ios/web/public/js_messaging/optimize_js.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//testing/test.gni")
@@ -58,18 +58,18 @@ source_set("form_handler_feature") {
]
}
-js_compile_bundle("fill_js") {
- closure_entry_point = "__crWeb.fill"
+optimize_js("fill_js") {
+ primary_script = "resources/fill.js"
sources = [ "resources/fill.js" ]
}
-js_compile_bundle("form_js") {
- closure_entry_point = "__crWeb.form"
+optimize_js("form_js") {
+ primary_script = "resources/form.js"
sources = [ "resources/form.js" ]
}
-js_compile_bundle("form_handlers_js") {
- closure_entry_point = "__crWeb.formHandlers"
+optimize_js("form_handlers_js") {
+ primary_script = "resources/form_handlers.js"
sources = [ "resources/form_handlers.js" ]
}
diff --git a/chromium/components/autofill/ios/form_util/form_handlers_java_script_feature.mm b/chromium/components/autofill/ios/form_util/form_handlers_java_script_feature.mm
index 74949194d8e..9e75821ac10 100644
--- a/chromium/components/autofill/ios/form_util/form_handlers_java_script_feature.mm
+++ b/chromium/components/autofill/ios/form_util/form_handlers_java_script_feature.mm
@@ -16,7 +16,7 @@
#endif
namespace {
-constexpr char kScriptName[] = "form_handlers_js";
+constexpr char kScriptName[] = "form_handlers";
constexpr char kScriptMessageName[] = "FormHandlersMessage";
} // namespace
diff --git a/chromium/components/autofill/ios/form_util/form_util_java_script_feature.mm b/chromium/components/autofill/ios/form_util/form_util_java_script_feature.mm
index c26a6b3a329..7236facc6b9 100644
--- a/chromium/components/autofill/ios/form_util/form_util_java_script_feature.mm
+++ b/chromium/components/autofill/ios/form_util/form_util_java_script_feature.mm
@@ -13,8 +13,8 @@
#endif
namespace {
-const char kFillScriptName[] = "fill_js";
-const char kFormScriptName[] = "form_js";
+const char kFillScriptName[] = "fill";
+const char kFormScriptName[] = "form";
} // namespace
namespace autofill {
diff --git a/chromium/components/autofill/ios/form_util/resources/fill.js b/chromium/components/autofill/ios/form_util/resources/fill.js
index 0aec72ea5de..5c0b8f96188 100644
--- a/chromium/components/autofill/ios/form_util/resources/fill.js
+++ b/chromium/components/autofill/ios/form_util/resources/fill.js
@@ -4,9 +4,7 @@
// This file provides methods used to fill forms in JavaScript.
-goog.provide('__crWeb.fill');
-
-// Requires __crWeb.form.
+// Requires functions from form.js.
/**
* @typedef {{
@@ -51,9 +49,6 @@ __gCrWeb.fill = {};
// minification.
__gCrWeb['fill'] = __gCrWeb.fill;
-/* Beginning of anonymous object. */
-(function() {
-
/**
* The maximum length allowed for form data.
*
@@ -149,6 +144,25 @@ __gCrWeb.fill.RENDERER_ID_NOT_SET = '0';
__gCrWeb.fill.ID_SYMBOL = window.Symbol.for('__gChrome~uniqueID');
/**
+ * Acquires the specified DOM |attribute| from the DOM |element| and returns
+ * its lower-case value, or null if not present.
+ *
+ * @param {Element} element A DOM element.
+ * @param {string} attribute An attribute name.
+ * @return {?string} Lowercase value of DOM element or null if not present.
+ */
+function getLowerCaseAttribute_(element, attribute) {
+ if (!element) {
+ return null;
+ }
+ const value = element.getAttribute(attribute);
+ if (value) {
+ return value.toLowerCase();
+ }
+ return null;
+}
+
+/**
* Returns true if an element can be autocompleted.
*
* This method aims to provide the same logic as method
@@ -163,12 +177,10 @@ __gCrWeb.fill.autoComplete = function(element) {
if (!element) {
return false;
}
- if (__gCrWeb.common.getLowerCaseAttribute(element, 'autocomplete') ===
- 'off') {
+ if (getLowerCaseAttribute_(element, 'autocomplete') === 'off') {
return false;
}
- if (__gCrWeb.common.getLowerCaseAttribute(element.form, 'autocomplete') ==
- 'off') {
+ if (getLowerCaseAttribute_(element.form, 'autocomplete') == 'off') {
return false;
}
return true;
@@ -528,6 +540,30 @@ __gCrWeb.fill.createAndDispatchHTMLEvent = function(
};
/**
+ * Converts a relative URL into an absolute URL.
+ *
+ * @param {Object} doc Document.
+ * @param {string} relativeURL Relative URL.
+ * @return {string} Absolute URL.
+ */
+function absoluteURL_(doc, relativeURL) {
+ // In the case of data: URL-based pages, relativeURL === absoluteURL.
+ if (doc.location.protocol === 'data:') {
+ return doc.location.href;
+ }
+ let urlNormalizer = doc['__gCrWebURLNormalizer'];
+ if (!urlNormalizer) {
+ urlNormalizer = doc.createElement('a');
+ doc['__gCrWebURLNormalizer'] = urlNormalizer;
+ }
+
+ // Use the magical quality of the <a> element. It automatically converts
+ // relative URLs into absolute ones.
+ urlNormalizer.href = relativeURL;
+ return urlNormalizer.href;
+}
+
+/**
* Returns a canonical action for |formElement|. It works the same as upstream
* function GetCanonicalActionForForm.
* @param {HTMLFormElement} formElement
@@ -535,8 +571,7 @@ __gCrWeb.fill.createAndDispatchHTMLEvent = function(
*/
__gCrWeb.fill.getCanonicalActionForForm = function(formElement) {
const rawAction = formElement.getAttribute('action') || '';
- const absoluteUrl =
- __gCrWeb.common.absoluteURL(formElement.ownerDocument, rawAction);
+ const absoluteUrl = absoluteURL_(formElement.ownerDocument, rawAction);
return __gCrWeb.common.removeQueryAndReferenceFromURL(absoluteUrl);
};
@@ -586,7 +621,7 @@ function extractFieldsFromControlElements_(
// Create a new AutofillFormFieldData, fill it out and map it to the
// field's name.
- const formField = new __gCrWeb['common'].JSONSafeObject;
+ const formField = new __gCrWeb['common'].JSONSafeObject();
__gCrWeb.fill.webFormControlElementToFormField(
controlElement, extractMask, formField);
formFields.push(formField);
@@ -859,9 +894,9 @@ __gCrWeb.fill.webFormElementToFormData = function(
// Note different from form_autofill_util.cc version of this method, which
// computes |form.action| using document.completeURL(form_element.action())
// and falls back to formElement.action() if the computed action is invalid,
- // here the action returned by |__gCrWeb.common.absoluteURL| is always
- // valid, which is computed by creating a <a> element, and we don't check if
- // the action is valid.
+ // here the action returned by |absoluteURL_| is always valid, which is
+ // computed by creating a <a> element, and we don't check if the action is
+ // valid.
const controlElements = __gCrWeb.form.getFormControlElements(formElement);
@@ -2041,7 +2076,7 @@ __gCrWeb.fill.webFormControlElementToFormField = function(
* @return {string} a JSON encoded version of |form|
*/
__gCrWeb.fill.autofillSubmissionData = function(form) {
- const formData = new __gCrWeb['common'].JSONSafeObject;
+ const formData = new __gCrWeb['common'].JSONSafeObject();
const extractMask =
__gCrWeb.fill.EXTRACT_MASK_VALUE | __gCrWeb.fill.EXTRACT_MASK_OPTIONS;
__gCrWeb['fill'].webFormElementToFormData(
@@ -2338,6 +2373,3 @@ __gCrWeb.fill.getUniqueID = function(element) {
return __gCrWeb.fill.RENDERER_ID_NOT_SET;
}
};
-
-
-}()); // End of anonymous object
diff --git a/chromium/components/autofill/ios/form_util/resources/form.js b/chromium/components/autofill/ios/form_util/resources/form.js
index 0fb7851a7ce..53ec80241c5 100644
--- a/chromium/components/autofill/ios/form_util/resources/form.js
+++ b/chromium/components/autofill/ios/form_util/resources/form.js
@@ -6,8 +6,6 @@
* @fileoverview Contains method needed to access the forms and their elements.
*/
-goog.provide('__crWeb.form');
-
/**
* Namespace for this file. It depends on |__gCrWeb| having already been
* injected. String 'form' is used in |__gCrWeb['form']| as it needs to be
@@ -20,8 +18,6 @@ __gCrWeb.form = {};
// minification.
__gCrWeb['form'] = __gCrWeb.form;
-/** Beginning of anonymous object */
-(function() {
/**
* Prefix used in references to form elements that have no 'id' or 'name'
*/
@@ -305,5 +301,3 @@ __gCrWeb.form['fieldWasEditedByUser'] = function(element) {
}
return __gCrWeb.form.wasEditedByUser.get(element);
};
-
-}()); // End of anonymous object
diff --git a/chromium/components/autofill/ios/form_util/resources/form_handlers.js b/chromium/components/autofill/ios/form_util/resources/form_handlers.js
index 35bffdc2c24..b901a5c408e 100644
--- a/chromium/components/autofill/ios/form_util/resources/form_handlers.js
+++ b/chromium/components/autofill/ios/form_util/resources/form_handlers.js
@@ -8,9 +8,7 @@
* Autofill keyboard accessory.
*/
-goog.provide('__crWeb.formHandlers');
-
-// Requires __crWeb.fill and __crWeb.form.
+// Requires functions from fill.js and form.js.
/**
* Namespace for this file. It depends on |__gCrWeb| having already been
@@ -19,8 +17,6 @@ goog.provide('__crWeb.formHandlers');
*/
__gCrWeb.formHandlers = {};
-/** Beginning of anonymous object */
-(function() {
/**
* The MutationObserver tracking form related changes.
*/
@@ -474,5 +470,3 @@ __gCrWeb.formHandlers['toggleTrackingUserEditedFields'] = function(track) {
__gCrWeb.form.wasEditedByUser = null;
}
};
-
-}()); // End of anonymous object
diff --git a/chromium/components/autofill_assistant/android/BUILD.gn b/chromium/components/autofill_assistant/android/BUILD.gn
index c9a01527422..4570b138f6b 100644
--- a/chromium/components/autofill_assistant/android/BUILD.gn
+++ b/chromium/components/autofill_assistant/android/BUILD.gn
@@ -19,6 +19,8 @@ android_library("java") {
":public_dependencies_java",
":public_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/autofill/android:autofill_java",
"//components/autofill/android:prefeditor_autofill_java",
"//components/browser_ui/bottomsheet/android:java",
@@ -36,18 +38,18 @@ android_library("java") {
"//components/version_info/android:version_constants_java",
"//content/public/android:content_java",
"//mojo/public/java:bindings_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/android_deps:com_android_support_support_annotations_java",
"//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_collection_collection_java",
"//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java",
"//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_gridlayout_gridlayout_java",
- "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
"//third_party/androidx:androidx_lifecycle_lifecycle_runtime_java",
"//third_party/androidx:androidx_recyclerview_recyclerview_java",
+ "//third_party/androidx:androidx_swiperefreshlayout_swiperefreshlayout_java",
"//third_party/blink/public/mojom:android_mojo_bindings_java",
"//ui/android:ui_java",
"//url:gurl_java",
@@ -149,6 +151,7 @@ android_library("java") {
"java/src/org/chromium/components/autofill_assistant/user_data/AssistantDataOriginNotice.java",
"java/src/org/chromium/components/autofill_assistant/user_data/AssistantDateTime.java",
"java/src/org/chromium/components/autofill_assistant/user_data/AssistantInfoSection.java",
+ "java/src/org/chromium/components/autofill_assistant/user_data/AssistantLoadingSpinner.java",
"java/src/org/chromium/components/autofill_assistant/user_data/AssistantLoginChoice.java",
"java/src/org/chromium/components/autofill_assistant/user_data/AssistantLoginSection.java",
"java/src/org/chromium/components/autofill_assistant/user_data/AssistantPaymentMethodSection.java",
@@ -191,7 +194,6 @@ android_library("public_java") {
sources = [
"public/java/src/org/chromium/components/autofill_assistant/AssistantAddressEditorGms.java",
- "public/java/src/org/chromium/components/autofill_assistant/AssistantContactEditorAccount.java",
"public/java/src/org/chromium/components/autofill_assistant/AssistantFeatures.java",
"public/java/src/org/chromium/components/autofill_assistant/AssistantModuleInstallUi.java",
"public/java/src/org/chromium/components/autofill_assistant/AssistantOnboardingHelper.java",
@@ -335,7 +337,6 @@ android_library("autofill_assistant_public_java") {
deps = [
":animated_poodle_resources",
"//base:base_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//ui/android:ui_java",
]
@@ -359,7 +360,6 @@ android_library("autofill_assistant_public_impl_java") {
deps = [
":animated_poodle_resources",
"//base:base_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//ui/android:ui_java",
]
@@ -422,6 +422,7 @@ android_resources("java_resources") {
"internal/java/res/layout/autofill_assistant_form_selection_input.xml",
"internal/java/res/layout/autofill_assistant_header.xml",
"internal/java/res/layout/autofill_assistant_info_box.xml",
+ "internal/java/res/layout/autofill_assistant_loading_spinner.xml",
"internal/java/res/layout/autofill_assistant_login.xml",
"internal/java/res/layout/autofill_assistant_onboarding_no_button.xml",
"internal/java/res/layout/autofill_assistant_onboarding_terms.xml",
diff --git a/chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_circle_background.xml b/chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_circle_background.xml
index c1f7b3591a6..ec60e06b6a4 100644
--- a/chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_circle_background.xml
+++ b/chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_circle_background.xml
@@ -5,5 +5,5 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
- <solid android:color="@color/blue_when_enabled"/>
+ <solid android:color="@color/blue_when_enabled_list"/>
</shape> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_rounded_corner_background.xml b/chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_rounded_corner_background.xml
index 1b024cf3799..91e08ed2529 100644
--- a/chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_rounded_corner_background.xml
+++ b/chromium/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_rounded_corner_background.xml
@@ -7,5 +7,5 @@
<corners
android:radius="4dp" />
<solid
- android:color="@color/blue_when_enabled" />
+ android:color="@color/blue_when_enabled_list" />
</shape> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter.xml b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter.xml
index 0f46cf0e247..4cff57162a3 100644
--- a/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter.xml
+++ b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter.xml
@@ -36,7 +36,7 @@
android:layout_width="@dimen/autofill_assistant_minimum_touch_target_size"
android:layout_height="@dimen/autofill_assistant_minimum_touch_target_size"
android:padding="@dimen/autofill_assistant_form_counter_button_padding"
- android:tint="@color/blue_when_enabled"
+ android:tint="@color/blue_when_enabled_list"
android:contentDescription="@string/autofill_assistant_decrease_value"
app:srcCompat="@drawable/ic_remove_outline_white_24dp"/>
<TextView
@@ -50,7 +50,7 @@
android:layout_width="@dimen/autofill_assistant_minimum_touch_target_size"
android:layout_height="@dimen/autofill_assistant_minimum_touch_target_size"
android:padding="@dimen/autofill_assistant_form_counter_button_padding"
- android:tint="@color/blue_when_enabled"
+ android:tint="@color/blue_when_enabled_list"
android:contentDescription="@string/autofill_assistant_increase_value"
app:srcCompat="@drawable/ic_add_outline_white_24dp"/>
</LinearLayout>
diff --git a/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter_input.xml b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter_input.xml
index d8f1caa8116..0518d74cb86 100644
--- a/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter_input.xml
+++ b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_form_counter_input.xml
@@ -44,7 +44,7 @@
android:id="@+id/chevron"
android:layout_width="16dp"
android:layout_height="16dp"
- android:tint="@color/blue_when_enabled"
+ android:tint="@color/blue_when_enabled_list"
app:srcCompat="@drawable/ic_expand_more_black_24dp"
tools:ignore="ContentDescription"/>
</LinearLayout>
diff --git a/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_header.xml b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_header.xml
index 3195a882f56..91e3b7ef2b2 100644
--- a/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_header.xml
+++ b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_header.xml
@@ -39,7 +39,6 @@
android:ellipsize="end"
android:textAppearance="@style/TextAppearance.AssistantBlackTitle"/>
- <!-- Not adding contentDescription as we will hide the button in Talkback mode -->
<org.chromium.ui.widget.ChromeImageView
android:id="@+id/tts_button"
android:layout_width="@dimen/autofill_assistant_minimum_touch_target_size"
@@ -47,6 +46,7 @@
android:padding="@dimen/autofill_assistant_tts_button_padding"
android:visibility="gone"
android:tint="@macro/default_icon_color"
+ android:contentDescription="@string/autofill_assistant_tts_button"
app:srcCompat="@drawable/ic_volume_on_white_24dp"/>
<org.chromium.ui.widget.ChromeImageView
diff --git a/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_loading_spinner.xml b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_loading_spinner.xml
new file mode 100644
index 00000000000..224467b2f0e
--- /dev/null
+++ b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_loading_spinner.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 The Chromium 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"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/autofill_assistant_loading_spinner_padding"
+ android:paddingBottom="@dimen/autofill_assistant_loading_spinner_padding">
+ <org.chromium.components.autofill_assistant.user_data.AssistantLoadingSpinner
+ android:id="@+id/loading_spinner"
+ android:layout_width="@dimen/autofill_assistant_loading_spinner_size"
+ android:layout_height="@dimen/autofill_assistant_loading_spinner_size"/>
+</LinearLayout> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_section_title.xml b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_section_title.xml
index c3ed5d06a10..d7a8819f2a6 100644
--- a/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_section_title.xml
+++ b/chromium/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_section_title.xml
@@ -25,11 +25,11 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<org.chromium.ui.widget.ChromeImageView
+ android:id="@+id/section_title_add_button_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
- android:src="@drawable/plus"
- app:tint="@macro/default_icon_color_accent1"/>
+ android:src="@drawable/plus"/>
<Space android:layout_width="8dp" android:layout_height="0dp"/>
<TextView android:id="@+id/section_title_add_button_label"
android:layout_width="wrap_content"
diff --git a/chromium/components/autofill_assistant/android/internal/java/res/values-v17/dimens.xml b/chromium/components/autofill_assistant/android/internal/java/res/values-v17/dimens.xml
index 8dadd63b5c4..ad19668fa1e 100644
--- a/chromium/components/autofill_assistant/android/internal/java/res/values-v17/dimens.xml
+++ b/chromium/components/autofill_assistant/android/internal/java/res/values-v17/dimens.xml
@@ -56,4 +56,7 @@
<dimen name="autofill_assistant_form_line_height_3">88dp</dimen>
<dimen name="autofill_assistant_root_view_top_padding">12dp</dimen>
+
+ <dimen name="autofill_assistant_loading_spinner_size">20dp</dimen>
+ <dimen name="autofill_assistant_loading_spinner_padding">4dp</dimen>
</resources>
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/android_chrome_autofill_assistant_strings.grd b/chromium/components/autofill_assistant/android/internal/java/strings/android_chrome_autofill_assistant_strings.grd
index 63e425f44a5..38aa74f68da 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/android_chrome_autofill_assistant_strings.grd
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/android_chrome_autofill_assistant_strings.grd
@@ -252,6 +252,9 @@
<message name="IDS_AUTOFILL_ASSISTANT_OVERFLOW_OPTIONS" desc="Content description for the overflow icon displayed in the first prompt, which will open a popup menu with additional options.">
Preferences
</message>
+ <message name="IDS_AUTOFILL_ASSISTANT_TTS_BUTTON" desc="Content description for the text to speech button, which is used to start or stop voice instructions.">
+ Start or stop voice instructions
+ </message>
<message name="IDS_AUTOFILL_ASSISTANT_SPLIT_ONBOARDING_TERMS_TITLE" desc="The title of the terms and conditions popup dialog.">
Google Assistant needs your permission
</message>
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/android_chrome_autofill_assistant_strings_grd/IDS_AUTOFILL_ASSISTANT_TTS_BUTTON.png.sha1 b/chromium/components/autofill_assistant/android/internal/java/strings/android_chrome_autofill_assistant_strings_grd/IDS_AUTOFILL_ASSISTANT_TTS_BUTTON.png.sha1
new file mode 100644
index 00000000000..26841cb6e61
--- /dev/null
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/android_chrome_autofill_assistant_strings_grd/IDS_AUTOFILL_ASSISTANT_TTS_BUTTON.png.sha1
@@ -0,0 +1 @@
+7b4ed7fb2264e7a91721c405b3e0b7bfdfe4fb5b \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_af.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_af.xtb
index d51d7cf39e6..67018d072bf 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_af.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_af.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistent in Chrome.</translation>
<translation id="8500511870202433545">Huur 'n motor\ndeur net 'n paar keer te tik</translation>
<translation id="9084406551994160152">Jou Google Assistent maak dit makliker om fliekkaartjies te koop deur jou besonderhede wat veilig gestoor is, te gebruik</translation>
+<translation id="9202590983572380008">Begin of stop steminstruksies</translation>
<translation id="945522503751344254">Stuur terugvoer</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_am.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_am.xtb
index 75012024372..90e28089969 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_am.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_am.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">በChrome á‹áˆµáŒ¥ Google ረዳትá¢</translation>
<translation id="8500511870202433545">በጥቂት መታ\nማድረጎች ብቻ መኪና ይከራዩ</translation>
<translation id="9084406551994160152">የእርስዎ Google ረዳት ደህንáŠá‰³á‰¸á‹ በተጠበቀ áˆáŠ”ታ የተከማቹ á‹áˆ­á‹áˆ®á‰½á‹ŽáŠ• በመጠቀሠየáŠáˆáˆ ቲኬቶችን መáŒá‹›á‰µáŠ• ይበáˆáŒ¥ ቀላሠያደርገዋáˆ</translation>
+<translation id="9202590983572380008">የድáˆáŒ½ መመሪያዎችን ይጀáˆáˆ© ወይሠያá‰áˆ™</translation>
<translation id="945522503751344254">áŒá‰¥áˆ¨áˆ˜áˆáˆµ ላክ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ar.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ar.xtb
index ed0f71db7ba..228d4a61785 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ar.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ar.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">â€"مساعد Google" ÙÙŠ Chrome</translation>
<translation id="8500511870202433545">â€ÙŠÙ…كنك استئجار سيارة \nببضع نقرات Ùقط.</translation>
<translation id="9084406551994160152">â€ÙŠØ³Ù‡Ù‘ÙÙ„ "مساعد Google" عمليات شراء تذاكر الأÙلام باستخدام بياناتك المخزَّنة بشكل آمن.</translation>
+<translation id="9202590983572380008">تشغيل التعليمات الصوتية أو إيقاÙها</translation>
<translation id="945522503751344254">إرسال تعليقات</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_as.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_as.xtb
index 6c8ce4cf9ac..7078fbd1f36 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_as.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_as.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chromeত Google Assistant</translation>
<translation id="8500511870202433545">মাতà§à§° কেইবাৰমান টিপি\nà¦à¦–ন গাড়ী ভাড়াত লওক</translation>
<translation id="9084406551994160152">সà§à§°à¦•à§à¦·à¦¿à¦¤à¦­à¦¾à§±à§‡ ষà§à¦Ÿâ€™à§° কৰি ৰখা আপোনাৰ সবিশেষ বà§à¦¯à§±à¦¹à¦¾à§° কৰি আপোনাৰ Google Assistantঠচিনেমাৰ টিকেট কটাটো সহজ কৰি তোলে</translation>
+<translation id="9202590983572380008">কণà§à¦ à¦§à§à¦¬à¦¨à¦¿à§°à§‡ দিয়া নিৰà§à¦¦à§‡à¦¶à¦¨à¦¾ আৰমà§à¦­ অথবা বনà§à¦§ কৰক</translation>
<translation id="945522503751344254">মতামত পঠিয়াওক</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_az.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_az.xtb
index 65a34d1d582..0e31e010e2a 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_az.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_az.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistent Chrome'da.</translation>
<translation id="8500511870202433545">Sadəcə bir neçə toxunuşla\navtomobil icarəyə götürün</translation>
<translation id="9084406551994160152">Google Assistent təhlükəsiz şəkildə saxlanılan məlumatlarınızdan istifadə edərək kino biletləri almağı asanlaşdırır</translation>
+<translation id="9202590983572380008">Səsli təlimatları başladın və ya dayandırın</translation>
<translation id="945522503751344254">Geri əlaqə göndərin</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_be.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_be.xtb
index 66b46d3814d..11d4faf505a 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_be.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_be.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Памочнік Google у Chrome</translation>
<translation id="8500511870202433545">Ðфармленне пракату аўтамабілÑ\nÑž некалькі дотыкаў</translation>
<translation id="9084406551994160152">Памочнік Google дазвалÑе лёгка куплÑць білеты Ñž кіно, выкарыÑтоўваючы вашы звеÑткі, ÑÐºÑ–Ñ Ð½Ð°Ð´Ð·ÐµÐ¹Ð½Ð° захоўваюцца</translation>
+<translation id="9202590983572380008">Уключыць або выключыць галаÑÐ°Ð²Ñ‹Ñ Ñ–Ð½Ñтрукцыі</translation>
<translation id="945522503751344254">Ðдправіць водгук</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bg.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bg.xtb
index c4170b98cd9..2c81002e58d 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bg.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bg.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google ÐÑиÑтент в Chrome.</translation>
<translation id="8500511870202433545">Ðаемете автомобил\nÑамо Ñ Ð½Ñколко докоÑваниÑ</translation>
<translation id="9084406551994160152">С помощта на Google ÐÑиÑтент можете по-леÑно да купувате билети за кино поÑредÑтвом данните Ñи, ÑъхранÑвани на Ñигурно мÑÑто</translation>
+<translation id="9202590983572380008">Стартиране или Ñпиране на глаÑовите инÑтрукции</translation>
<translation id="945522503751344254">Изпращане на отзиви</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bn.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bn.xtb
index cac3e86a4f6..921bb214ba3 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bn.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bn.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome-ঠGoogle অà§à¦¯à¦¾à¦¸à¦¿à¦¸à§à¦Ÿà§à¦¯à¦¾à¦¨à§à¦Ÿà¥¤</translation>
<translation id="8500511870202433545">মাতà§à¦° কয়েক বার টà§à¦¯à¦¾à¦ª করেই\nগাড়ি ভাড়া করà§à¦¨</translation>
<translation id="9084406551994160152">আপনার নিরাপদে সà§à¦Ÿà§‹à¦° করা বিবরণ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে Google Assistant সিনেমার টিকিট কেনার বিষয়টি সহজ করে তোলে</translation>
+<translation id="9202590983572380008">ভয়েস নিরà§à¦¦à§‡à¦¶à¦¾à¦¬à¦²à§€ শà§à¦°à§ বা বনà§à¦§ করà§à¦¨</translation>
<translation id="945522503751344254">মতামত জানান</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bs.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bs.xtb
index 747f02fcca3..f2243218f96 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bs.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_bs.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Asistent u Chromeu.</translation>
<translation id="8500511870202433545">Iznajmite automobil\nu svega nekoliko dodira</translation>
<translation id="9084406551994160152">Google Asistent vam olakšava da kupite karte pomoću sigurno pohranjenih detalja</translation>
+<translation id="9202590983572380008">Pokrenite ili zaustavite glasovne upute</translation>
<translation id="945522503751344254">Pošaljite povratne informacije</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ca.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ca.xtb
index 462899e27b0..35bda99b5c3 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ca.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ca.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Assistent de Google a Chrome.</translation>
<translation id="8500511870202433545">Lloga un cotxe\namb només uns quants tocs</translation>
<translation id="9084406551994160152">Amb el teu Assistent de Google, és més fàcil comprar entrades per al cinema utilitzant els detalls emmagatzemats de manera segura</translation>
+<translation id="9202590983572380008">Inicia o atura les ordres de veu</translation>
<translation id="945522503751344254">Envia suggeriments</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cs.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cs.xtb
index 6c9bfdae52d..cd6dc824069 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cs.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cs.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Asistent Google v Chromu.</translation>
<translation id="8500511870202433545">PůjÄte si auto\npouhými nÄ›kolika klepnutími</translation>
<translation id="9084406551994160152">Asistent Google vám usnadní nákup lístků do kina prostÅ™ednictvím bezpeÄnÄ› uložených údajů</translation>
+<translation id="9202590983572380008">Spustit nebo zastavit hlasové pokyny</translation>
<translation id="945522503751344254">Odeslat zpětnou vazbu</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cy.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cy.xtb
index bd82dd6893b..69f41e0541b 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cy.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_cy.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistant yn Chrome.</translation>
<translation id="8500511870202433545">Rhentu car\nmewn ychydig o dapiau yn unig</translation>
<translation id="9084406551994160152">Mae eich Google Assistant yn ei gwneud yn haws prynu tocynnau ffilmiau gan ddefnyddio eich manylion sydd wedi'u storio'n ddiogel</translation>
+<translation id="9202590983572380008">Cychwyn neu stopio cyfarwyddiadau llais</translation>
<translation id="945522503751344254">Danfon adborth</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_da.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_da.xtb
index bc2c749b2ab..e4f39ab6175 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_da.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_da.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistent i Chrome</translation>
<translation id="8500511870202433545">Lej en bil\nmed nogle få tryk</translation>
<translation id="9084406551994160152">Google Assistent gør det nemmere for dig at købe filmbilletter ved hjælp af dine oplysninger, som opbevares sikkert</translation>
+<translation id="9202590983572380008">Start eller stop stemmekommandoer</translation>
<translation id="945522503751344254">Send feedback</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_de.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_de.xtb
index e32426165be..0514bda63ba 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_de.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_de.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistant für Chrome.</translation>
<translation id="8500511870202433545">Ganz einfach\nein Auto mieten</translation>
<translation id="9084406551994160152">Mit Google Assistant kannst du mithilfe deiner gespeicherten Daten ganz einfach Kinokarten kaufen</translation>
+<translation id="9202590983572380008">Sprachbefehle starten oder anhalten</translation>
<translation id="945522503751344254">Feedback geben</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_el.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_el.xtb
index f71db6e1285..a6f05839f32 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_el.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_el.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Βοηθός Google στο Chrome.</translation>
<translation id="8500511870202433545">Îοικιάστε ένα αυτοκίνητο\nμε λίγα μόνο πατήματα.</translation>
<translation id="9084406551994160152">Ο Βοηθός Google διευκολÏνει την αγοÏά εισιτηÏίων κινηματογÏάφου χÏησιμοποιώντας τα στοιχεία σας που αποθηκεÏονται με ασφάλεια</translation>
+<translation id="9202590983572380008">ΈναÏξη ή διακοπή φωνητικών οδηγιών</translation>
<translation id="945522503751344254">Αποστολή σχολίων</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_en-GB.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_en-GB.xtb
index c26aecbed67..6dce64b8bcc 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_en-GB.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_en-GB.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistant in Chrome.</translation>
<translation id="8500511870202433545">Hire a car\nin just a few taps</translation>
<translation id="9084406551994160152">Your Google Assistant makes it easier to buy cinema tickets using your securely stored details</translation>
+<translation id="9202590983572380008">Start or stop voice instructions</translation>
<translation id="945522503751344254">Send feedback</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es-419.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es-419.xtb
index 1527023012a..137aa449b76 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es-419.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es-419.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Asistente de Google en Chrome</translation>
<translation id="8500511870202433545">Alquila un automóvil\ncon unos pocos pasos</translation>
<translation id="9084406551994160152">Asistente de Google usa tus detalles almacenados de forma segura para que comprar entradas para el cine resulte más fácil</translation>
+<translation id="9202590983572380008">Iniciar o detener los comandos de voz</translation>
<translation id="945522503751344254">Enviar comentarios</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es.xtb
index 37953aee1ab..bf90600088a 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_es.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Asistente de Google en Chrome.</translation>
<translation id="8500511870202433545">Alquila un coche\ncon solo unos toques</translation>
<translation id="9084406551994160152">Tu Asistente de Google facilita la compra de entradas de cine usando tus datos guardados de forma segura</translation>
+<translation id="9202590983572380008">Iniciar o detener instrucciones de voz</translation>
<translation id="945522503751344254">Enviar comentarios</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_et.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_et.xtb
index 8dd9785e96e..a5099f038ce 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_et.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_et.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google'i assistent Chrome'is.</translation>
<translation id="8500511870202433545">Rentige auto \nvaid mõne puudutusega</translation>
<translation id="9084406551994160152">Google’i assistendi abil on lihtne kinopileteid osta, kasutades teie turvaliselt salvestatud andmeid</translation>
+<translation id="9202590983572380008">Hääljuhiste käivitamine või peatamine</translation>
<translation id="945522503751344254">Tagasiside saatmine</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_eu.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_eu.xtb
index cf0b4ee4739..883805b4b11 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_eu.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_eu.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome-ko Google-ren Laguntzailea.</translation>
<translation id="8500511870202433545">Alokatu auto bat\nsakatze gutxi batzuekin</translation>
<translation id="9084406551994160152">Google-ren Laguntzailea zerbitzuari esker, errazagoa da zinemarako sarrerak erostea segurtasun osoz gordetako xehetasunak erabilita</translation>
+<translation id="9202590983572380008">Abiarazi edo gelditu ahozko argibideak</translation>
<translation id="945522503751344254">Bidali oharrak</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fa.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fa.xtb
index bf7b39f4dcc..365a4a70bf7 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fa.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fa.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">â€Â«Ø¯Ø³ØªÛŒØ§Ø± Google» در Chrome.</translation>
<translation id="8500511870202433545">â€Ùقط با چند ضربهâ€\nخودرو اجاره کنید</translation>
<translation id="9084406551994160152">â€Â«Ø¯Ø³ØªÛŒØ§Ø± Google» خرید بلیت بااستÙاده از اطلاعات به‌طور ایمن ذخیره‌شده‌تان را آسان‌تر می‌کند</translation>
+<translation id="9202590983572380008">شروع یا توق٠دستورالعمل‌های صوتی</translation>
<translation id="945522503751344254">ارسال بازخورد</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fi.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fi.xtb
index ad65c1df165..f0a93c8f77a 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fi.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fi.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chromen Google Assistant</translation>
<translation id="8500511870202433545">Vuokraa auto\n parilla napautuksella</translation>
<translation id="9084406551994160152">Google Assistantin avulla voit ostaa elokuvalippuja helpommin käyttämällä suojatusti tallennettuja tietojasi</translation>
+<translation id="9202590983572380008">Aloita tai lopeta ääniohjeet</translation>
<translation id="945522503751344254">Lähetä palautetta</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fil.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fil.xtb
index 2349b2c43a5..f817a021625 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fil.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fil.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistant sa Chrome.</translation>
<translation id="8500511870202433545">Magrenta ng kotse\nsa ilang pag-tap lang</translation>
<translation id="9084406551994160152">Mas pinapadali ng Google Assistant mo ang pagbili ng mga ticket sa pelikula gamit ang iyong mga detalyeng secure na na-store</translation>
+<translation id="9202590983572380008">Simulan o ihinto ang mga tagubilin gamit ang boses</translation>
<translation id="945522503751344254">Magpadala ng feedback</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr-CA.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr-CA.xtb
index c86f76c0692..51ab7804c1c 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr-CA.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr-CA.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Assistant Google dans Chrome.</translation>
<translation id="8500511870202433545">Louez une voiture \nen quelques touchers seulement</translation>
<translation id="9084406551994160152">Votre Assistant Google facilite l'achat de billets de cinéma en utilisant vos renseignements stockés en toute sécurité</translation>
+<translation id="9202590983572380008">Démarrer ou arrêter les commandes vocales</translation>
<translation id="945522503751344254">Envoyer un commentaire à Google</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr.xtb
index 9d11c66eacf..ebe3a3167b2 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_fr.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Assistant Google dans Chrome.</translation>
<translation id="8500511870202433545">Louez une voiture\nen quelques gestes seulement</translation>
<translation id="9084406551994160152">L'Assistant Google vous permet d'acheter des places de cinéma facilement grâce à vos infos stockées de façon sécurisée</translation>
+<translation id="9202590983572380008">Activer ou désactiver les instructions vocales</translation>
<translation id="945522503751344254">Envoyer un commentaire</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gl.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gl.xtb
index f2a26c5e2ba..8682244593a 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gl.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gl.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Asistente de Google en Chrome.</translation>
<translation id="8500511870202433545">Aluga un coche\ncon só uns toques</translation>
<translation id="9084406551994160152">Co teu Asistente de Google, resultarache máis doado comprar entradas de cine usando os teus datos almacenados de xeito seguro</translation>
+<translation id="9202590983572380008">Iniciar ou parar instrucións de voz</translation>
<translation id="945522503751344254">Enviar comentarios</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gu.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gu.xtb
index e4b1693d402..20d90c73d1a 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gu.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_gu.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chromeમાં Google Assistant.</translation>
<translation id="8500511870202433545">માતà«àª° અમà«àª• ટૅપમાં જ\nકાર ભાડે લો</translation>
<translation id="9084406551994160152">તમારà«àª‚ Google Assistant તમારી સà«àª°àª•à«àª·àª¿àª¤ રીતે સà«àªŸà«‹àª° કરેલી વિગતો વડે મૂવી ટિકિટ ખરીદવાનà«àª‚ વધૠસરળ બનાવે છે</translation>
+<translation id="9202590983572380008">વૉઇસ સૂચનાઓ શરૂ અથવા બંધ કરો</translation>
<translation id="945522503751344254">પà«àª°àª¤àª¿àª¸àª¾àª¦ મોકલો</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hi.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hi.xtb
index 0d0dc8a3ee6..40eaa444e8d 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hi.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hi.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome में Google Assistant.</translation>
<translation id="8500511870202433545">सिरà¥à¤«à¤¼ कà¥à¤› टैप में कार\n किराये पर लें</translation>
<translation id="9084406551994160152">फ़िलà¥à¤® के टिकट आसानी से बà¥à¤• करने में, Google Assistant आपकी मदद कर सकती है. इसके लिà¤, यह सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ तरीके से सेव किठगठआपके कà¥à¤°à¥‡à¤¡à¥‡à¤‚शियल का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करती है</translation>
+<translation id="9202590983572380008">बोलकर निरà¥à¤¦à¥‡à¤¶ देने की सà¥à¤µà¤¿à¤§à¤¾ को चालू या बंद करें</translation>
<translation id="945522503751344254">फ़ीडबैक भेजें</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hr.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hr.xtb
index b5637d63d1f..c7a69de56f8 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hr.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hr.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google asistent u Chromeu.</translation>
<translation id="8500511870202433545">Unajmite automobil\nu samo nekoliko dodira</translation>
<translation id="9084406551994160152">Google asistent olakšava vam kupnju karata za kino pomoću sigurno pohranjenih podataka</translation>
+<translation id="9202590983572380008">Pokretanje ili zaustavljanje glasovnih uputa</translation>
<translation id="945522503751344254">Pošaljite povratne informacije</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hu.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hu.xtb
index c104071e420..a2118a3eb2b 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hu.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hu.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Segéd a Chrome-ban.</translation>
<translation id="8500511870202433545">Autót bérelhet\ncsupán néhány koppintással</translation>
<translation id="9084406551994160152">Google Segédje az Ön biztonságosan tárolt adatait felhasználva könnyítheti meg a mozijegyvásárlást</translation>
+<translation id="9202590983572380008">Hangutasítások elindítása vagy leállítása</translation>
<translation id="945522503751344254">Visszajelzés küldése</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hy.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hy.xtb
index 50180bb4b21..d7ef7433d03 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hy.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_hy.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Õ•Õ£Õ¶Õ¡Õ¯Õ¡Õ¶Õ¨ Chrome-Õ¸Ö‚Õ´:</translation>
<translation id="8500511870202433545">Õ„Õ¥Ö„Õ¥Õ¶Õ¡ Õ¾Õ¡Ö€Õ±Õ¥Ö„Õ\nÕ¯Õ¡Õ¿Õ¡Ö€Õ¥Õ¬Õ¸Õ¾ Õ´Õ« Ö„Õ¡Õ¶Õ« Õ°ÕºÕ¸Ö‚Õ´</translation>
<translation id="9084406551994160152">Google Õ•Õ£Õ¶Õ¡Õ¯Õ¡Õ¶Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Ö„ Õ°Õ¥Õ·Õ¿Õ¸Ö‚Õ©ÕµÕ¡Õ´Õ¢ Õ¯Õ«Õ¶Õ¸ÕµÕ« Õ¿Õ¸Õ´Õ½Õ¥Ö€ Õ£Õ¶Õ¥Õ¬Õ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥Õ¬Õ¸Õ¾ Õ±Õ¥Ö€ ÕºÕ¡Õ°Õ¾Õ¡Õ® Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨</translation>
+<translation id="9202590983572380008">Õ„Õ«Õ¡ÖÕ¶Õ¥Õ¬ Õ¯Õ¡Õ´ Õ¡Õ¶Õ»Õ¡Õ¿Õ¥Õ¬ Õ±Õ¡ÕµÕ¶Õ¡ÕµÕ«Õ¶ Õ°Õ¸Ö‚Õ·Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨</translation>
<translation id="945522503751344254">Ô¿Õ¡Ö€Õ®Õ«Ö„ Õ°Õ¡ÕµÕ¿Õ¶Õ¥Õ¬</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_id.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_id.xtb
index 663062249d1..e349c88cf76 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_id.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_id.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Asisten Google di Chrome.</translation>
<translation id="8500511870202433545">Sewa mobil\ncukup dengan beberapa kali ketuk</translation>
<translation id="9084406551994160152">Asisten Google memudahkan pembelian tiket film menggunakan detail Anda yang disimpan secara aman</translation>
+<translation id="9202590983572380008">Mulai atau hentikan perintah suara</translation>
<translation id="945522503751344254">Kirim masukan</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_is.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_is.xtb
index fde6e15d9c4..0911455f020 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_is.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_is.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google hjálpari í Chrome.</translation>
<translation id="8500511870202433545">Leigðu bíl\ní örfáum skrefum</translation>
<translation id="9084406551994160152">Google hjálpari auðveldar þér að kaupa bíómiða með upplýsingunum þínum sem eru vistaðar á öruggan hátt</translation>
+<translation id="9202590983572380008">Hefja eða stöðva raddleiðbeiningar</translation>
<translation id="945522503751344254">Senda ábendingu</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_it.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_it.xtb
index cb683cf705a..994bfc4a314 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_it.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_it.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Assistente Google in Chrome.</translation>
<translation id="8500511870202433545">Noleggia un'auto\ncon pochi tocchi</translation>
<translation id="9084406551994160152">Con l'Assistente Google è più facile acquistare biglietti per il cinema usando i tuoi dati memorizzati in modo sicuro</translation>
+<translation id="9202590983572380008">Avvia o interrompi istruzioni vocali</translation>
<translation id="945522503751344254">Invia feedback</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_iw.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_iw.xtb
index 374a95f9c86..6b41e0bbc68 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_iw.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_iw.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">â€Google Assistant ב-Chrome.</translation>
<translation id="8500511870202433545">â€×©×•×›×¨×™× רכב\nבכמה הקשות בלבד</translation>
<translation id="9084406551994160152">â€×‘עזרת Google Assistant, קל יותר לקנות ×›×¨×˜×™×¡×™× ×œ×¡×¨×˜ ב×מצעות ×¤×¨×˜×™× ×©×©×ž×•×¨×™× ×‘×ופן מ×ובטח</translation>
+<translation id="9202590983572380008">הפעלה ×ו הפסקה של הפקודות הקוליות</translation>
<translation id="945522503751344254"> שליחת משוב</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ja.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ja.xtb
index 970c0573c6c..919b9c21147 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ja.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ja.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome 㮠Google アシスタント。</translation>
<translation id="8500511870202433545">数回ã®ã‚¿ãƒƒãƒ—ã§\nレンタカーを予約ã§ãã¾ã™</translation>
<translation id="9084406551994160152">Google アシスタントを利用ã™ã‚‹ã¨ã€å®‰å…¨ã«ä¿å­˜ã•ã‚ŒãŸè©³ç´°æƒ…報を使用ã—ã¦ã€æ˜ ç”»ãƒã‚±ãƒƒãƒˆã‚’ç°¡å˜ã«è³¼å…¥ã§ãã¾ã™</translation>
+<translation id="9202590983572380008">音声コマンドを開始ã¾ãŸã¯åœæ­¢ã—ã¾ã™</translation>
<translation id="945522503751344254">フィードãƒãƒƒã‚¯ã‚’é€ä¿¡</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ka.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ka.xtb
index 281cdedfae9..30b7a3a634d 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ka.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ka.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google áƒáƒ¡áƒ˜áƒ¡áƒ¢áƒ”ნტი Chrome-ში.</translation>
<translation id="8500511870202433545">დáƒáƒ˜áƒ¥áƒ˜áƒ áƒáƒ•áƒ”თ მáƒáƒœáƒ¥áƒáƒœáƒ\nრáƒáƒ›áƒ“ენიმე შეხებით</translation>
<translation id="9084406551994160152">Google áƒáƒ¡áƒ˜áƒ¡áƒ¢áƒ”ნტის დáƒáƒ®áƒ›áƒáƒ áƒ”ბით უფრრმáƒáƒ áƒ¢áƒ˜áƒ•áƒáƒ“ იყიდით კინáƒáƒ—ეáƒáƒ¢áƒ áƒ”ბის ბილეთებს უსáƒáƒ¤áƒ áƒ—ხáƒáƒ“ შენáƒáƒ®áƒ£áƒšáƒ˜ თქვენი მáƒáƒœáƒáƒªáƒ”მების მეშვეáƒáƒ‘ით</translation>
+<translation id="9202590983572380008">ხმáƒáƒ•áƒáƒœáƒ˜ მითითებების დáƒáƒ¬áƒ§áƒ”ბრáƒáƒœ შეწყვეტáƒ</translation>
<translation id="945522503751344254">შეფáƒáƒ¡áƒ”ბის გáƒáƒ’ზáƒáƒ•áƒœáƒ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kk.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kk.xtb
index b2b95ff5915..0a6c1905707 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kk.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kk.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome браузеріндегі Google Assistant.</translation>
<translation id="8500511870202433545">Бірнеше рет түртіп қана,\nкөлікті жалға алыңыз.</translation>
<translation id="9084406551994160152">Google Assistant қызметі қауіпÑіз Ñақталған мәліметтеріңізді қолданып, киноға билет Ñатып алуды жеңілдетеді.</translation>
+<translation id="9202590983572380008">ДауыÑÑ‚Ñ‹Ò› нұÑқауларды қоÑу не өшіру</translation>
<translation id="945522503751344254">Пікір жіберу</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_km.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_km.xtb
index 3298e7a1706..52f018b8a08 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_km.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_km.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google ជំនួយការនៅក្នុង Chrome ។</translation>
<translation id="8500511870202433545">ជួល​រážáž™áž“្áž\nដោយគ្រាន់ážáŸ‚​ចុចពីរបីដង​ប៉ុណ្ណោះ</translation>
<translation id="9084406551994160152">Google Assistant របស់អ្នកធ្វើឱ្យ​កាន់ážáŸ‚ងាយស្រួល​ក្នុងការទិញ​សំបុážáŸ’រភាពយន្ហដោយប្រើពáŸážáŸŒáž˜áž¶áž“លម្អិážâ€‹ážŠáŸ‚លអ្នកបានរក្សាទុក​យ៉ាងមានសុវážáŸ’ážáž·áž—ាព</translation>
+<translation id="9202590983572380008">ចាប់ផ្ដើម ឬ​បញ្ឈប់​ការណែនាំ​ជាសំឡáŸáž„</translation>
<translation id="945522503751344254">ផ្ញើមážáž·</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kn.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kn.xtb
index f3431f639a6..d0e873521ba 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kn.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_kn.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome ನಲà³à²²à²¿ Google Assistant.</translation>
<translation id="8500511870202433545">ಕೆಲವೇ ಟà³à²¯à²¾à²ªà³â€Œà²—ಳ ಮೂಲಕ\nಕಾರನà³à²¨à³ ಬಾಡಿಗೆಗೆ ಪಡೆಯಿರಿ</translation>
<translation id="9084406551994160152">ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿ ಸಂಗà³à²°à²¹à²¿à²¸à²¿à²°à³à²µ ನಿಮà³à²® ವಿವರಗಳನà³à²¨à³ ಬಳಸಿಕೊಂಡೠಚಲನಚಿತà³à²° ಟಿಕೆಟà³â€Œà²—ಳನà³à²¨à³ ಖರೀದಿಸà³à²µà³à²¦à²¨à³à²¨à³ Google Assistant ಸà³à²²à²­à²—ೊಳಿಸà³à²¤à³à²¤à²¦à³†</translation>
+<translation id="9202590983572380008">ಧà³à²µà²¨à²¿ ಸೂಚನೆಗಳನà³à²¨à³ ಪà³à²°à²¾à²°à²‚ಭಿಸಿ ಅಥವಾ ನಿಲà³à²²à²¿à²¸à²¿</translation>
<translation id="945522503751344254">ಪà³à²°à²¤à²¿à²•à³à²°à²¿à²¯à³†à²¯à²¨à³à²¨à³ ಕಳà³à²¹à²¿à²¸à²¿</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ko.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ko.xtb
index 2015523a004..8c45869f0c8 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ko.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ko.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chromeì˜ Google 어시스턴트입니다.</translation>
<translation id="8500511870202433545">탭 몇 번으로\nì°¨ëŸ‰ì„ ëŒ€ì—¬í•˜ì„¸ìš”.</translation>
<translation id="9084406551994160152">Google 어시스턴트를 사용하면 안전하게 ì €ìž¥ëœ ì„¸ë¶€ì •ë³´ë¥¼ 사용하여 ì˜í™” í‹°ì¼“ì„ ë” ì‰½ê²Œ 구매하실 수 있습니다</translation>
+<translation id="9202590983572380008">ìŒì„± 안내 시작 ë˜ëŠ” 중지</translation>
<translation id="945522503751344254">ì˜ê²¬ 보내기</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ky.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ky.xtb
index bc178333202..14371b8c35d 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ky.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ky.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome'догу Google Жардамчы.</translation>
<translation id="8500511870202433545">Бир нече жолу таптап,\nижарага унаа алыңыз</translation>
<translation id="9084406551994160152">Google Жардамчыңыз коопÑуз Ñакталган маалыматыңызды колдонуп, киного билеттерди оңой Ñатып алууга жардам берет</translation>
+<translation id="9202590983572380008">Оозеки нуÑкамаларды баштоо же токтотуу</translation>
<translation id="945522503751344254">Пикириңизди билдириңиз</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lo.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lo.xtb
index 07016937006..ba6cf723588 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lo.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lo.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">ຜູ້ຊ່ວຠGoogle ໃນ Chrome.</translation>
<translation id="8500511870202433545">ເຊົ່າລົດ\nໂດàºà»àº•àº°àºªàº­àº‡àºªàº²àº¡àºšàº²àº”ເທົ່ານັ້ນ</translation>
<translation id="9084406551994160152">ຜູ້ຊ່ວຠGoogle ຂອງທ່ານຈະເຮັດໃຫ້ສາມາດຊື້ປີ້ໂດàºà»ƒàºŠà»‰àº¥àº²àºàº¥àº°àº­àº½àº”ທີ່ຈັດເàºàº±àºšà»„ວ້ຢ່າງປອດໄພຂອງທ່ານໄດ້ງ່າàºàº‚ຶ້ນ</translation>
+<translation id="9202590983572380008">ເລີ່ມ ຫຼື ຢຸດຄຳà»àº™àº°àº™àº³àºªàº½àº‡</translation>
<translation id="945522503751344254">ສົ່ງ​ຄà»àº²â€‹àº„ິດ​​ເຫັນ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lt.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lt.xtb
index 8a783cdca8f..954147681b4 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lt.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lt.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">„Google Assistant“ sistemoje „Chrome“.</translation>
<translation id="8500511870202433545">Išsinuomokite automobilį vos keliais palietimais</translation>
<translation id="9084406551994160152">„Google“ padėjėjas gali padėti lengvai įsigyti bilietų į kiną naudodamas jūsų saugiai saugomą išsamią informaciją</translation>
+<translation id="9202590983572380008">PradÄ—kite arba sustabdykite instrukcijas balsu</translation>
<translation id="945522503751344254">Siųsti atsiliepimą</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lv.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lv.xtb
index 0923e37cb35..ddbe5836ead 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lv.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_lv.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google asistents pÄrlÅ«kprogrammÄ Chrome.</translation>
<translation id="8500511870202433545">NonomÄjiet automaÅ¡Ä«nu,\nveicot tikai dažus pieskÄrienus</translation>
<translation id="9084406551994160152">Google asistents ļauj jums Ä“rtÄk iegÄdÄties biļetes uz filmÄm, izmantojot jÅ«su droÅ¡i saglabÄto informÄciju.</translation>
+<translation id="9202590983572380008">SÄkt vai apturÄ“t balss komandas</translation>
<translation id="945522503751344254">Sūtīt atsauksmes</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mk.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mk.xtb
index bb4ffc8d283..9d878b33079 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mk.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mk.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">„Помошникот на Google“ во Chrome.</translation>
<translation id="8500511870202433545">Изнајмете автомобил\nÑо Ñамо неколку допири</translation>
<translation id="9084406551994160152">Вашиот „Помошник на Google“ ви го олеÑнува купувањето билети за кино Ñо безбедно зачуваните детали</translation>
+<translation id="9202590983572380008">Започнете ги или Ñопрете ги глаÑовните упатÑтва</translation>
<translation id="945522503751344254">ИÑпратете повратни информации</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ml.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ml.xtb
index 809b9e3c85b..68363d7083c 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ml.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ml.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome-ലെ Google അസിസàµâ€Œà´±àµà´±àµ»àµà´±àµ.</translation>
<translation id="8500511870202433545">à´à´¤à´¾à´¨àµà´‚ ടാപàµà´ªàµà´•à´³à´¿à´²àµ‚ടെ\nകാർ വാടകയàµâ€Œà´•àµà´•àµ†à´Ÿàµà´•àµà´•àµ‚</translation>
<translation id="9084406551994160152">നിങàµà´™àµ¾ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯à´¿ സംഭരിചàµà´šà´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨ വിശദാംശങàµà´™à´³àµâ€ ഉപയോഗിചàµà´šàµ സിനിമാ à´Ÿà´¿à´•àµà´•à´±àµà´±àµà´•àµ¾ വാങàµà´™àµà´¨àµà´¨à´¤àµ നിങàµà´™à´³àµà´Ÿàµ† Google Assistant à´Žà´³àµà´ªàµà´ªà´®à´¾à´•àµà´•àµà´¨àµà´¨àµ</translation>
+<translation id="9202590983572380008">ശബàµâ€Œà´¦ നിർദàµà´¦àµ‡à´¶à´™àµà´™àµ¾ ആരംഭികàµà´•àµà´•à´¯àµ‹ നിർതàµà´¤àµà´•à´¯àµ‹ ചെയàµà´¯àµà´•</translation>
<translation id="945522503751344254">ഫീഡàµâ€Œà´¬à´¾à´•àµà´•àµ അയയàµâ€Œà´•àµà´•àµà´•</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mn.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mn.xtb
index 00761bdcdd0..7b09cfa53af 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mn.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mn.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome-н Google ТуÑлах.</translation>
<translation id="8500511870202433545">Ð¥ÑдхÑн товшилтоор\nмашин түрÑÑÑлÑÑÑ€Ñй</translation>
<translation id="9084406551994160152">Таны Google ТуÑлах аюулгүйгÑÑÑ€ хадгалÑан дÑлгÑÑ€Ñнгүй мÑдÑÑллÑÑ Ð°ÑˆÐ¸Ð³Ð»Ð°Ð½ киноны таÑалбар худалдан авахад Ñ…Ñлбар болгоно</translation>
+<translation id="9202590983572380008">Дуут зааварчилгааг ÑхлүүлÑÑ… ÑÑвÑл зогÑоох</translation>
<translation id="945522503751344254">Санал Ñ…Ò¯ÑÑлт илгÑÑÑ…</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mr.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mr.xtb
index 2e92cd671b4..3f2dc6fc68c 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mr.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_mr.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome मधील Google असिसà¥à¤Ÿà¤‚ट.</translation>
<translation id="8500511870202433545">फकà¥à¤¤ काही टॅपमधà¥à¤¯à¥‡\nकार भाडà¥à¤¯à¤¾à¤¨à¥‡ घà¥à¤¯à¤¾</translation>
<translation id="9084406551994160152">Google Assistant हे तà¥à¤®à¤šà¥‡ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤°à¥€à¤¤à¥à¤¯à¤¾ सà¥à¤Ÿà¥‹à¤…र केलेले तपशील वापरून चितà¥à¤°à¤ªà¤Ÿà¤¾à¤šà¥€ तिकिटे खरेदी करणे सोपे करते</translation>
+<translation id="9202590983572380008">बोलून दिलà¥à¤¯à¤¾ जाणाऱà¥à¤¯à¤¾ सूचना सà¥à¤°à¥‚ करा किंवा थांबवा</translation>
<translation id="945522503751344254">अभिपà¥à¤°à¤¾à¤¯ पाठवा</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ms.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ms.xtb
index d63ac1ee3a6..62dae70da91 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ms.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ms.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistant dalam Chrome.</translation>
<translation id="8500511870202433545">Sewa kereta\ndengan hanya beberapa ketikan</translation>
<translation id="9084406551994160152">Google Assistant memudahkan anda untuk membeli tiket wayang menggunakan butiran anda yang disimpan dengan selamat</translation>
+<translation id="9202590983572380008">Mulakan atau hentikan arahan suara</translation>
<translation id="945522503751344254">Hantar maklum balas</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_my.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_my.xtb
index f0cf2f6c7fb..2a2ad30c5fb 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_my.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_my.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome ရှိ Google Assistantá‹</translation>
<translation id="8500511870202433545">အကြိမ်အနည်းငယ် á€á€­á€¯á€·á€›á€¯á€¶á€–ြင့်\nကားငှားရမ်းနိုင်သည်</translation>
<translation id="9084406551994160152">Google Assistant သည် လုံá€á€¼á€¯á€¶á€…ွာသိမ်းထားသည့် သင်áအသေးစိá€á€ºá€¡á€á€»á€€á€ºá€¡á€œá€€á€ºá€€á€­á€¯á€¡á€žá€¯á€¶á€¸á€•á€¼á€¯á€á€¼á€„်းဖြင့် ရုပ်ရှင်လက်မှá€á€ºá€á€šá€ºá€šá€°á€›á€¬á€á€½á€„် ပိုမိုလွယ်ကူစေသည်</translation>
+<translation id="9202590983572380008">အသံဖြင့် ညွှန်ကြားá€á€»á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ စá€á€„် (သို့) ရပ်á€á€”့်နိုင်သည်</translation>
<translation id="945522503751344254">အကြံပြုá€á€»á€€á€º ပေးပို့မည်</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ne.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ne.xtb
index 450fd74117c..84756e5571b 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ne.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ne.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome को Google सहायक।</translation>
<translation id="8500511870202433545">केही पटक टà¥à¤¯à¤¾à¤ª गरेकै भरमा\nकार भाडामा लिनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="9084406551994160152">तपाईंको Google सहायकले तपाईंका सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ तरिकाले भणà¥à¤¡à¤¾à¤°à¤£ गरिà¤à¤•à¤¾ विवरण पà¥à¤°à¤¯à¥‹à¤— गरी चलचितà¥à¤°à¤•à¤¾ टिकट खरिद गरà¥à¤¨à¥‡ कारà¥à¤¯ अठसजिलो बनाउà¤à¤›</translation>
+<translation id="9202590983572380008">बोलेर निरà¥à¤¦à¥‡à¤¶à¤¨ दिन थालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ वा रोकà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="945522503751344254">पृषà¥à¤ à¤ªà¥‹à¤·à¤£ पठाउनà¥à¤¹à¥‹à¤¸à¥</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_nl.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_nl.xtb
index cf2eae9f641..9c203d2c396 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_nl.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_nl.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">De Google Assistent in Chrome.</translation>
<translation id="8500511870202433545">Met slechts een paar tikken\nhuur je een auto</translation>
<translation id="9084406551994160152">Met de Google Assistent kun je makkelijker filmtickets kopen via je beveiligd opgeslagen gegevens</translation>
+<translation id="9202590983572380008">Gesproken instructies starten of stoppen</translation>
<translation id="945522503751344254">Feedback sturen</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_no.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_no.xtb
index 914c938d2b0..d8cb3fc7bb0 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_no.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_no.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google-assistenten i Chrome.</translation>
<translation id="8500511870202433545">Lei en bil\nmed bare noen få trykk</translation>
<translation id="9084406551994160152">Google-assistenten gjør det lettere å kjøpe kinobilletter med trygt lagrede betalingsopplysninger</translation>
+<translation id="9202590983572380008">Start eller stopp taleveiledning</translation>
<translation id="945522503751344254">Send tilbakemelding</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_or.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_or.xtb
index 2535257df10..e08b58310e3 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_or.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_or.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chromeରେ Google Assistant।</translation>
<translation id="8500511870202433545">କେବଳ କିଛି ଟାପରେ\nà¬à¬• କାର ଭଡ଼ାରେ ନିଅନà­à¬¤à­</translation>
<translation id="9084406551994160152">ଆପଣଙà­à¬• Google Assistant ଆପଣଙà­à¬•à¬° ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ଭାବେ ଷà­à¬Ÿà­‹à¬° କରାଯାଇଥିବା ବିବରଣୀ ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରି ମà­à¬­à¬¿ ଟିକେଟଗà­à­œà¬¿à¬• କିଣିବା ସହଜ କରିଥାà¬</translation>
+<translation id="9202590983572380008">ଭà¬à¬¸ ନିରà­à¬¦à­à¬¦à­‡à¬¶à¬¾à¬¬à¬³à­€ ଆରମà­à¬­ କିମà­à¬¬à¬¾ ବନà­à¬¦ କରନà­à¬¤à­</translation>
<translation id="945522503751344254">ମତାମତ ପଠାନà­à¬¤à­</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pa.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pa.xtb
index 766b6d5cfa4..25b3a335ea5 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pa.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pa.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome ਵਿੱਚ Google Assistant.</translation>
<translation id="8500511870202433545">ਬਸ ਕà©à¨ ਟੈਪਾਂ ਵਿੱਚ\nਕਾਰ ਕਿਰਾਠ'ਤੇ ਲਓ</translation>
<translation id="9084406551994160152">ਤà©à¨¹à¨¾à¨¡à©€ Google Assistant ਤà©à¨¹à¨¾à¨¡à©‡ ਸਟੋਰ ਕੀਤੇ ਸà©à¨°à©±à¨–ਿਅਤ ਵੇਰਵਿਆਂ ਨੂੰ ਵਰਤ ਕੇ ਫ਼ਿਲਮ ਟਿਕਟਾਂ ਨੂੰ ਖਰੀਦਣਾ ਵਧੇਰੇ ਆਸਾਨ ਬਣਾ ਦਿੰਦੀ ਹੈ</translation>
+<translation id="9202590983572380008">ਅਵਾਜ਼ੀ ਹਿਦਾਇਤਾਂ ਸ਼à©à¨°à©‚ ਜਾਂ ਬੰਦ ਕਰੋ</translation>
<translation id="945522503751344254">ਪà©à¨°à¨¤à©€à¨•à¨°à¨® ਭੇਜੋ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pl.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pl.xtb
index adec27b6363..1d4b35b9fca 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pl.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pl.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Asystent Google w Chrome.</translation>
<translation id="8500511870202433545">Wypożyczaj samochody\nkilkoma kliknięciami</translation>
<translation id="9084406551994160152">Asystent Google ułatwia kupowanie biletów do kina przy użyciu bezpiecznie przechowywanych danych</translation>
+<translation id="9202590983572380008">Włącz lub wyłącz instrukcje głosowe</translation>
<translation id="945522503751344254">Prześlij opinię</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-BR.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-BR.xtb
index 522c51e47b4..ca73320f6f6 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-BR.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-BR.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistente no Chrome.</translation>
<translation id="8500511870202433545">Alugue um carro\ncom apenas alguns toques</translation>
<translation id="9084406551994160152">O Google Assistente usa seus dados armazenados em segurança para ajudar a comprar ingressos de cinema.</translation>
+<translation id="9202590983572380008">Começar ou parar instruções por voz</translation>
<translation id="945522503751344254">Enviar comentários</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-PT.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-PT.xtb
index aa28379ddf2..cc9f8cf849c 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-PT.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_pt-PT.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Assistente Google no Chrome.</translation>
<translation id="8500511870202433545">Alugue um automóvel\ncom apenas alguns toques.</translation>
<translation id="9084406551994160152">O Assistente Google facilita a compra de bilhetes de cinema ao usar os seus detalhes armazenados em segurança</translation>
+<translation id="9202590983572380008">Inicie ou pare as instruções por voz</translation>
<translation id="945522503751344254">Enviar feedback</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ro.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ro.xtb
index 16579f8c277..2229351209a 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ro.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ro.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Asistentul Google în Chrome.</translation>
<translation id="8500511870202433545">Închiriază o mașină\ncu doar câteva atingeri</translation>
<translation id="9084406551994160152">Asistentul Google te ajută să cumperi bilete la film folosind detaliile stocate în siguranță</translation>
+<translation id="9202590983572380008">Pornește sau oprește comenzile vocale</translation>
<translation id="945522503751344254">Trimite feedback</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ru.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ru.xtb
index 323cb94530b..0f1c3ad2e4b 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ru.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ru.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google ÐÑÑиÑтент в Chrome</translation>
<translation id="8500511870202433545">Брать автомобиль напрокат\nтеперь ещё удобнее.</translation>
<translation id="9084406551994160152">С помощью Google ÐÑÑиÑтента очень удобно покупать билеты в кино (Ð´Ð»Ñ Ñтого иÑпользуютÑÑ Ð²Ð°ÑˆÐ¸ данные, и они надежно защищены).</translation>
+<translation id="9202590983572380008">Включить или отключить голоÑовые подÑказки</translation>
<translation id="945522503751344254">Отправить отзыв</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_si.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_si.xtb
index 5c62e625196..f55abbf189d 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_si.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_si.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome තුළ Google සහකරු.</translation>
<translation id="8500511870202433545">තට්ටු කිරීම් කිහිපයකින්\n මà·à¶§à¶»à·Š රථයක් කුලියට ගන්න</translation>
<translation id="9084406551994160152">ඔබගේ Google සහà·à¶ºà¶š ඔබගේ ආරක්ෂිතව ගබඩ෠කර ඇති විස්තර භà·à·€à·’තයෙන් චිත්â€à¶»à¶´à¶§ ප්â€à¶»à·€à·šà·à¶´à¶­à·Š මිලදී ගà·à¶±à·“ම පහසු කරයි</translation>
+<translation id="9202590983572380008">හඬ උපදෙස් ආරම්භ කරන්න හ෠නවත්වන්න</translation>
<translation id="945522503751344254">අදහස් හ෠යà·à¶¢à¶±à· යවන්න</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sk.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sk.xtb
index be659bbc8d5..2a503102c32 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sk.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sk.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Asistent Google v Chrome</translation>
<translation id="8500511870202433545">PožiÄajte si auto\nniekoľkými klepnutiami</translation>
<translation id="9084406551994160152">Asistent Google vám uľahÄí kúpu lístkov do kina pomocou vaÅ¡ich bezpeÄne uložených údajov</translation>
+<translation id="9202590983572380008">Spustenie alebo zastavenie hlasových pokynov</translation>
<translation id="945522503751344254">Odoslať spätnú väzbu</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sl.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sl.xtb
index 76fe23e2b06..771653b68f9 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sl.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sl.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">PomoÄnik Google v Chromu.</translation>
<translation id="8500511870202433545">Najemite avtomobil\ns samo nekaj dotiki</translation>
<translation id="9084406551994160152">S PomoÄnikom Google je preprostejÅ¡e kupiti vstopnice za kino z varno shranjenimi podatki.</translation>
+<translation id="9202590983572380008">ZaÄetek ali ustavitev glasovnih navodil</translation>
<translation id="945522503751344254">Pošiljanje povratnih informacij</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sq.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sq.xtb
index deb15b5c5ff..ed92a1de335 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sq.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sq.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">"Asistenti i Google" në Chrome.</translation>
<translation id="8500511870202433545">Merr me qira një makinë\nme vetëm pak trokitje</translation>
<translation id="9084406551994160152">"Asistenti yt i Google" e bën më të lehtë blerjen e biletave për filma duke përdorur detajet e tua të ruajtura në mënyrë të sigurt</translation>
+<translation id="9202590983572380008">Nis ose ndalo udhëzimet zanore</translation>
<translation id="945522503751344254">Dërgo komente</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr-Latn.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr-Latn.xtb
index 7c7ea46e1d3..90a13877d84 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr-Latn.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr-Latn.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google pomocÌnik u Chrome-u.</translation>
<translation id="8500511870202433545">Iznajmite automobil\nu samo nekoliko dodira</translation>
<translation id="9084406551994160152">Google pomocÌnik vam olakÅ¡ava kupovinu karata za bioskop preko bezbedno saÄuvanih podataka</translation>
+<translation id="9202590983572380008">Pokrenite ili zaustavite glasovne komande</translation>
<translation id="945522503751344254">Pošalji povratne informacije</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr.xtb
index 0e2103945b3..cea26d62b8f 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sr.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google помоћник у Chrome-у.</translation>
<translation id="8500511870202433545">Изнајмите аутомобил\nу Ñамо неколико додира</translation>
<translation id="9084406551994160152">Google помоћник вам олакшава куповину карата за биоÑкоп преко безбедно Ñачуваних података</translation>
+<translation id="9202590983572380008">Покрените или зауÑтавите глаÑовне команде</translation>
<translation id="945522503751344254">Пошаљи повратне информације</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sv.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sv.xtb
index 58b46178ece..0d45e24761c 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sv.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sv.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google-assistenten i Chrome.</translation>
<translation id="8500511870202433545">Hyr en bil\nmed några tryck</translation>
<translation id="9084406551994160152">Med Google-assistenten blir det enklare att köpa biobiljetter med dina säkert sparade uppgifter</translation>
+<translation id="9202590983572380008">Starta eller stoppa röstanvisningar</translation>
<translation id="945522503751344254">Skicka feedback</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sw.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sw.xtb
index d1a9baf7e05..ca3046f1a66 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sw.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_sw.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Programu ya Mratibu wa Google katika Chrome.</translation>
<translation id="8500511870202433545">Kodisha gari\nkwa gusa mara chache tu</translation>
<translation id="9084406551994160152">Mratibu wako wa Google hurahisisha kununua tiketi za filamu kwa kutumia maelezo yako yaliyohifadhiwa kwa usalama</translation>
+<translation id="9202590983572380008">Anzisha au sitisha maagizo ya sauti</translation>
<translation id="945522503751344254">Tuma maoni</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ta.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ta.xtb
index d12c5ea6d5c..5e37c75cbe9 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ta.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ta.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chromeமில௠Google அசிஸà¯à®Ÿà®£à¯à®Ÿà¯</translation>
<translation id="8500511870202433545">சில தடà¯à®Ÿà®²à¯à®•à®³à®¿à®²à¯‡à®¯à¯‡ காரை\n வாடகைகà¯à®•à¯ எடà¯à®•à¯à®•à®²à®¾à®®à¯</translation>
<translation id="9084406551994160152">பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®•à®šà¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ உஙà¯à®•à®³à¯ விவரஙà¯à®•à®³à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿ திரைபà¯à®ªà®Ÿ டிகà¯à®•à¯†à®Ÿà¯à®•à®³à¯ˆ வாஙà¯à®•à¯à®µà®¤à¯ˆ Google Assistant மேலà¯à®®à¯ எளிதாகà¯à®•à¯à®•à®¿à®±à®¤à¯</translation>
+<translation id="9202590983572380008">வழிமà¯à®±à¯ˆà®•à®³à¯ˆ வாசிதà¯à®¤à¯à®•à¯ காடà¯à®Ÿà¯à®®à¯ அலà¯à®²à®¤à¯ வாசிபà¯à®ªà®¤à¯ˆ நிறà¯à®¤à¯à®¤à¯à®®à¯</translation>
<translation id="945522503751344254">கரà¯à®¤à¯à®¤à¯ˆ அனà¯à®ªà¯à®ªà¯</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_te.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_te.xtb
index 17017b3fff4..f7904625d24 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_te.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_te.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chromeలో Google Assistant.</translation>
<translation id="8500511870202433545">కేవలం కొనà±à°¨à°¿ సారà±à°²à± à°Ÿà±à°¯à°¾à°ªà± చేయడం à°¦à±à°µà°¾à°°à°¾ \nకారౠఅదà±à°¦à±†à°•à± తీసà±à°•à±‹à°‚à°¡à°¿</translation>
<translation id="9084406551994160152">మీ Google Assistant మీరౠసà±à°°à°•à±à°·à°¿à°¤à°‚à°—à°¾ à°¸à±à°Ÿà±‹à°°à± చేసిన వివరాలనౠఉపయోగించి సినిమా టికెటà±â€Œà°²à°¨à± కొనà±à°—ోలౠచేయడానà±à°¨à°¿ à°¸à±à°²à°­à°¤à°°à°‚ చేసà±à°¤à±à°‚ది</translation>
+<translation id="9202590983572380008">వాయిసౠసూచనలనౠపà±à°°à°¾à°°à°‚à°­à°¿à°‚à°šà°‚à°¡à°¿ లేదా ఆపివేయండి</translation>
<translation id="945522503751344254">ఫీడà±â€Œà°¬à±à°¯à°¾à°•à± పంపండి</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_th.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_th.xtb
index 921dc9ad6b4..a2ad368289d 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_th.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_th.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google Assistant ใน Chrome</translation>
<translation id="8500511870202433545">เช่ารถ\nโดยà¹à¸•à¸°à¹€à¸žà¸µà¸¢à¸‡à¹„ม่à¸à¸µà¹ˆà¸„รั้ง</translation>
<translation id="9084406551994160152">Google Assistant ช่วยให้คุณซื้อตั๋วภาพยนตร์ได้ง่ายขึ้นโดยใช้รายละเอียดที่จัดเà¸à¹‡à¸šà¹„ว้อย่างปลอดภัยของคุณ</translation>
+<translation id="9202590983572380008">เริ่มหรือหยุดคำสั่งเสียง</translation>
<translation id="945522503751344254">ส่งความคิดเห็น</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_tr.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_tr.xtb
index f2e30cfc707..a8c0fe5ac76 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_tr.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_tr.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome'da Google Asistan.</translation>
<translation id="8500511870202433545">Yalnızca birkaç\ndokunuşla araç kiralayın</translation>
<translation id="9084406551994160152">Google Asistanınız, güvenli bir şekilde depolanan bilgilerinizi kullanarak sinema bileti almanızı kolaylaştırır</translation>
+<translation id="9202590983572380008">Sesli talimatları başlatın veya durdurun</translation>
<translation id="945522503751344254">Geri bildirim gönder</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uk.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uk.xtb
index 8ae1721dc69..2924a844d25 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uk.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uk.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Google ÐÑиÑтент у Chrome.</translation>
<translation id="8500511870202433545">Орендуйте автомобіль\nу кілька дотиків</translation>
<translation id="9084406551994160152">Google ÐÑиÑтент полегшує купівлю квитків, викориÑтовуючи ваші надійно збережені дані</translation>
+<translation id="9202590983572380008">Увімкнути або вимкнути голоÑові вказівки</translation>
<translation id="945522503751344254">ÐадіÑлати відгук</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ur.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ur.xtb
index d92031d2106..2192aeb106e 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ur.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_ur.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">â€Chrome میں Google اسسٹنٹ۔</translation>
<translation id="8500511870202433545">â€ØµØ±Ù چند تھپتھپاÛٹوں میں\nکار کرائے پر لیں</translation>
<translation id="9084406551994160152">â€Ø¢Ù¾ Ú©ÛŒ Google اسسٹنٹ آپ Ú©ÛŒ محÙوظ طریقے سے اسٹور Ú©Ø±Ø¯Û ØªÙصیلات کا استعمال کر Ú©Û’ مووی ٹکٹس خریدنا آسان بناتی ÛÛ’</translation>
+<translation id="9202590983572380008">صوتی Ûدایات شروع کریں یا بند کریں</translation>
<translation id="945522503751344254">تاثرات بھیجیں</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uz.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uz.xtb
index 3ce691af4a8..e5bd094446f 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uz.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_uz.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chromedagi Google Assistent</translation>
<translation id="8500511870202433545">Bir nechta harakat bilan\navtomobilni ijaraga oling</translation>
<translation id="9084406551994160152">Your Google Assistent xavfsiz saqlangan maʼlumotlaringiz asosida film uchun chiptalarni osongina sotib olishda yordam beradi</translation>
+<translation id="9202590983572380008">Ovoz koʻrsatmalarini boshlash yoki toʻxtatish</translation>
<translation id="945522503751344254">Fikr-mulohaza</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_vi.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_vi.xtb
index 7404bf2a889..f3aa7b88d33 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_vi.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_vi.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Trợ lý Google trong Chrome.</translation>
<translation id="8500511870202433545">Thuê ô tô\nchỉ trong vài thao tác nhấn</translation>
<translation id="9084406551994160152">Trợ lý Google sẽ giúp bạn mua vé xem phim dễ dàng hơn bằng cách dùng những thông tin được lưu trữ an toàn của bạn</translation>
+<translation id="9202590983572380008">Bắt đầu hoặc dừng hÆ°á»›ng dẫn bằng giá»ng nói</translation>
<translation id="945522503751344254">Gửi phản hồi</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-CN.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-CN.xtb
index fbec634ee21..c7679f929f5 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-CN.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-CN.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome 中的 Google 助ç†ã€‚</translation>
<translation id="8500511870202433545">点按几下,\nå³å¯ç§Ÿè½¦</translation>
<translation id="9084406551994160152">通过 Google 助ç†ï¼Œæ‚¨å¯ä»¥ä½¿ç”¨å®‰å…¨å­˜å‚¨çš„详细信æ¯è´­ä¹°ç”µå½±ç¥¨</translation>
+<translation id="9202590983572380008">å¯ç”¨æˆ–åœç”¨è¯­éŸ³æŒ‡ä»¤</translation>
<translation id="945522503751344254">å‘é€å馈</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-HK.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-HK.xtb
index 62e0ccf7700..3a51287f513 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-HK.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-HK.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome 的「Google 助ç†ã€ã€‚</translation>
<translation id="8500511870202433545">è¦ç§Ÿè»Šï¼Ÿ\n輕按幾下å³å¯</translation>
<translation id="9084406551994160152">「Google 助ç†ã€å¯ä½¿ç”¨å®‰å…¨å„²å­˜çš„詳細資料,讓您更輕鬆地購買電影票</translation>
+<translation id="9202590983572380008">啟動或åœæ­¢èªžéŸ³æŒ‡ç¤º</translation>
<translation id="945522503751344254">æä¾›æ„見å映</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-TW.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-TW.xtb
index 2b7372966da..709a74db287 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-TW.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zh-TW.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Chrome 版 Google 助ç†ã€‚</translation>
<translation id="8500511870202433545">åªè¦è¼•è§¸å¹¾ä¸‹\nå³å¯ç§Ÿè»Š</translation>
<translation id="9084406551994160152">Google 助ç†å¯ä»¥ä½¿ç”¨å®‰å…¨å„²å­˜çš„詳細資料,讓你更輕鬆地購買電影票</translation>
+<translation id="9202590983572380008">啟動或åœæ­¢èªžéŸ³æŒ‡ä»¤</translation>
<translation id="945522503751344254">æä¾›æ„見</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zu.xtb b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zu.xtb
index 4615770fc33..dd2a32b7b1f 100644
--- a/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zu.xtb
+++ b/chromium/components/autofill_assistant/android/internal/java/strings/translations/android_chrome_autofill_assistant_strings_zu.xtb
@@ -30,5 +30,6 @@
<translation id="8253702004019660079">Umsizi we-Google ku-Chrome</translation>
<translation id="8500511870202433545">Qasha imoto\nngokuthepha okumbalwa nje</translation>
<translation id="9084406551994160152">I-Google Assistant ikwenza kube lula ukuthenga amathikithi e-movie usebenzisa imininingwane yakho egcinwe ngokuphephile</translation>
+<translation id="9202590983572380008">Qala noma misa imiyalelo yezwi</translation>
<translation id="945522503751344254">Thumela impendulo</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantAutofillCreditCard.java b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantAutofillCreditCard.java
index 7e9dff7b7bc..8fd99592596 100644
--- a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantAutofillCreditCard.java
+++ b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantAutofillCreditCard.java
@@ -64,13 +64,15 @@ public class AssistantAutofillCreditCard {
private final String mNickname;
private final GURL mCardArtUrl;
private final @VirtualCardEnrollmentState int mVirtualCardEnrollmentState;
+ private final String mProductDescription;
@CalledByNative
public AssistantAutofillCreditCard(String guid, String origin, boolean isLocal,
boolean isCached, String name, String number, String obfuscatedNumber, String month,
String year, String basicCardIssuerNetwork, int issuerIconDrawableId,
String billingAddressId, String serverId, long instrumentId, String nickname,
- GURL cardArtUrl, @VirtualCardEnrollmentState int virtualCardEnrollmentState) {
+ GURL cardArtUrl, @VirtualCardEnrollmentState int virtualCardEnrollmentState,
+ String productDescription) {
mGUID = guid;
mOrigin = origin;
mIsLocal = isLocal;
@@ -88,6 +90,7 @@ public class AssistantAutofillCreditCard {
mNickname = nickname;
mCardArtUrl = cardArtUrl;
mVirtualCardEnrollmentState = virtualCardEnrollmentState;
+ mProductDescription = productDescription;
}
@CalledByNative
@@ -178,4 +181,9 @@ public class AssistantAutofillCreditCard {
public @VirtualCardEnrollmentState int getVirtualCardEnrollmentState() {
return mVirtualCardEnrollmentState;
}
+
+ @CalledByNative
+ public String getProductDescription() {
+ return mProductDescription;
+ }
}
diff --git a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantBrowserControls.java b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantBrowserControls.java
index 2066cf44a8c..ca8cb043304 100644
--- a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantBrowserControls.java
+++ b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantBrowserControls.java
@@ -15,9 +15,8 @@ public interface AssistantBrowserControls extends Destroyable {
* Observer for different browser control events.
*/
public interface Observer {
- void onControlsOffsetChanged(int topOffset, int topControlsMinHeightOffset,
- int bottomOffset, int bottomControlsMinHeightOffset, boolean needsAnimate);
- void onBottomControlsHeightChanged(int bottomControlsHeight, int bottomControlsMinHeight);
+ void onControlsOffsetChanged();
+ void onBottomControlsHeightChanged();
}
void setObserver(Observer browserControlsObserver);
diff --git a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantContactEditorAccount.java b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantContactEditorAccount.java
deleted file mode 100644
index a80151d5b93..00000000000
--- a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantContactEditorAccount.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2022 The Chromium 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.components.autofill_assistant;
-
-import android.app.Activity;
-
-import androidx.annotation.Nullable;
-
-import org.chromium.base.Callback;
-import org.chromium.components.autofill_assistant.AssistantEditor.AssistantContactEditor;
-import org.chromium.components.autofill_assistant.AssistantOptionModel.ContactModel;
-import org.chromium.components.autofill_assistant.user_data.GmsIntegrator;
-import org.chromium.ui.base.WindowAndroid;
-
-/**
- * Editor for contact information in Chrome/WebLayer using a GMS intent.
- */
-public class AssistantContactEditorAccount implements AssistantContactEditor {
- // Enums defined in resource_id.proto of AccountSettings.
- private static final int SCREEN_ID_PERSONAL_INFO_SCREEN = 10003; // All info.
- private static final int SCREEN_ID_MISC_CONTACT_EMAIL_SCREEN = 501;
- private static final int SCREEN_ID_PRIVACY_PHONE_SCREEN = 204;
-
- private final WindowAndroid mWindowAndroid;
- private final GmsIntegrator mGmsIntegrator;
- private final boolean mRequestEmail;
- private final boolean mRequestPhone;
-
- public AssistantContactEditorAccount(Activity activity, WindowAndroid windowAndroid,
- String accountEmail, boolean requestEmail, boolean requestPhone) {
- mWindowAndroid = windowAndroid;
- mGmsIntegrator = new GmsIntegrator(accountEmail, activity);
- mRequestEmail = requestEmail;
- mRequestPhone = requestPhone;
- }
-
- /**
- * Edit the user's personal information. If the email is requested, the editor opens to the
- * contact email screen. If phone number is requested, the editor opens to the phone screen.
- * It is not allowed to request email and phone at the same time! If neither is requested -
- * e.g. we're looking for name only - the editor opens to the main view, where all information
- * is available.
- *
- * @param oldItem The item to be edited, can be null in which case a new item is created.
- * @param doneCallback Called after the editor is closed, assuming that the item has been
- * successfully edited. The callback will be called with the
- * {@code oldItem} which can be null. The list of new items needs to be
- * requested.
- * @param cancelCallback Only called if the intent failed to be launched.
- */
- @Override
- public void createOrEditItem(@Nullable ContactModel oldItem,
- Callback<ContactModel> doneCallback, Callback<ContactModel> cancelCallback) {
- Callback<Boolean> callback = success -> {
- if (success) {
- doneCallback.onResult(oldItem);
- } else {
- cancelCallback.onResult(oldItem);
- }
- };
-
- int screenId;
- if (mRequestEmail) {
- assert !mRequestPhone;
- screenId = SCREEN_ID_MISC_CONTACT_EMAIL_SCREEN;
- } else if (mRequestPhone) {
- assert !mRequestEmail;
- screenId = SCREEN_ID_PRIVACY_PHONE_SCREEN;
- } else {
- screenId = SCREEN_ID_PERSONAL_INFO_SCREEN;
- }
- mGmsIntegrator.launchAccountIntent(screenId, mWindowAndroid, callback);
- }
-}
diff --git a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantDependencies.java b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantDependencies.java
index 4800608545c..b763f6ede7a 100644
--- a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantDependencies.java
+++ b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantDependencies.java
@@ -27,11 +27,10 @@ import org.chromium.ui.base.WindowAndroid;
@JNINamespace("autofill_assistant")
public interface AssistantDependencies extends AssistantStaticDependencies {
/**
- * Updates dependencies that are tied to the activity.
+ * Updates dependencies that are tied to the activity. The activity attached to the WebContents
+ * is used (if any).
* @return Whether a new activity could be found.
*/
- boolean maybeUpdateDependencies(Activity activity);
-
boolean maybeUpdateDependencies(WebContents webContents);
Activity getActivity();
diff --git a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantStaticDependencies.java b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantStaticDependencies.java
index d969195b91c..7ce77684758 100644
--- a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantStaticDependencies.java
+++ b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantStaticDependencies.java
@@ -66,11 +66,9 @@ public interface AssistantStaticDependencies {
LargeIconBridge createIconBridge();
@Nullable
- String getSignedInAccountEmailOrNull();
-
- @Nullable
AssistantProfileImageUtil createProfileImageUtilOrNull(
Context context, @DimenRes int imageSizeRedId);
+ @Nullable
AssistantEditorFactory createEditorFactory();
}
diff --git a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandler.java b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandler.java
index ef896c46a1e..3226d520e6b 100644
--- a/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandler.java
+++ b/chromium/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandler.java
@@ -73,4 +73,10 @@ public interface AutofillAssistantActionHandler {
* Displays a generic error message to the user.
*/
void showFatalError();
+
+ /**
+ * Check whether the user is supervised.
+ * @return supervised state
+ */
+ boolean isSupervisedUser();
}
diff --git a/chromium/components/autofill_assistant/browser/BUILD.gn b/chromium/components/autofill_assistant/browser/BUILD.gn
index c88c318f6b2..3d9ed33f52f 100644
--- a/chromium/components/autofill_assistant/browser/BUILD.gn
+++ b/chromium/components/autofill_assistant/browser/BUILD.gn
@@ -20,11 +20,18 @@ proto_library("proto") {
"service.proto",
"view_layout.proto",
]
- link_deps = [ "//components/autofill_assistant/content/common/proto:proto" ]
+ link_deps = [
+ "//components/autofill_assistant/browser/public:proto",
+ "//components/autofill_assistant/content/common/proto:proto",
+ ]
}
proto_library("test_proto") {
- sources = [ "parse_jspb_test.proto" ]
+ sources = [
+ "external_action_extension_test.proto",
+ "parse_jspb_test.proto",
+ ]
+ link_deps = [ "//components/autofill_assistant/browser/public:proto" ]
}
static_library("browser") {
@@ -56,6 +63,8 @@ static_library("browser") {
"actions/execute_js_action.h",
"actions/expect_navigation_action.cc",
"actions/expect_navigation_action.h",
+ "actions/external_action.cc",
+ "actions/external_action.h",
"actions/fallback_handler/required_field.cc",
"actions/fallback_handler/required_field.h",
"actions/fallback_handler/required_fields_fallback_handler.cc",
@@ -76,6 +85,8 @@ static_library("browser") {
"actions/presave_generated_password_action.h",
"actions/prompt_action.cc",
"actions/prompt_action.h",
+ "actions/register_password_reset_request_action.cc",
+ "actions/register_password_reset_request_action.h",
"actions/release_elements_action.cc",
"actions/release_elements_action.h",
"actions/reset_pending_credentials_action.cc",
@@ -151,10 +162,14 @@ static_library("browser") {
"client_settings.h",
"client_status.cc",
"client_status.h",
+ "common_dependencies.cc",
+ "common_dependencies.h",
"controller.cc",
"controller.h",
"controller_observer.cc",
"controller_observer.h",
+ "dependencies_util.cc",
+ "dependencies_util.h",
"desktop/starter_delegate_desktop.cc",
"desktop/starter_delegate_desktop.h",
"details.cc",
@@ -191,6 +206,8 @@ static_library("browser") {
"info_box.h",
"intent_strings.cc",
"intent_strings.h",
+ "js_flow_devtools_wrapper.cc",
+ "js_flow_devtools_wrapper.h",
"js_flow_executor.h",
"js_flow_executor_impl.cc",
"js_flow_executor_impl.h",
@@ -202,6 +219,8 @@ static_library("browser") {
"overlay_state.h",
"parse_jspb.cc",
"parse_jspb.h",
+ "platform_dependencies.cc",
+ "platform_dependencies.h",
"protocol_utils.cc",
"protocol_utils.h",
"radio_button_controller.cc",
@@ -301,16 +320,23 @@ static_library("browser") {
"wait_for_dom_observer.h",
"wait_for_dom_operation.cc",
"wait_for_dom_operation.h",
+ "web/base_element_finder.cc",
+ "web/base_element_finder.h",
"web/check_on_top_worker.cc",
"web/check_on_top_worker.h",
"web/click_or_tap_worker.cc",
"web/click_or_tap_worker.h",
+ "web/css_element_finder.cc",
+ "web/css_element_finder.h",
"web/element.cc",
"web/element.h",
"web/element_action_util.cc",
"web/element_action_util.h",
"web/element_finder.cc",
"web/element_finder.h",
+ "web/element_finder_result.cc",
+ "web/element_finder_result.h",
+ "web/element_finder_result_type.h",
"web/element_position_getter.cc",
"web/element_position_getter.h",
"web/element_rect_getter.cc",
@@ -326,6 +352,8 @@ static_library("browser") {
"web/selector_observer.cc",
"web/selector_observer.h",
"web/selector_observer_script.h",
+ "web/semantic_element_finder.cc",
+ "web/semantic_element_finder.h",
"web/send_keyboard_input_worker.cc",
"web/send_keyboard_input_worker.h",
"web/web_controller.cc",
@@ -362,6 +390,7 @@ static_library("browser") {
"//components/strings:components_strings_grit",
"//components/ukm/content:content",
"//components/url_matcher",
+ "//components/variations/service",
"//components/version_info",
"//content/public/browser",
"//google_apis",
@@ -377,6 +406,8 @@ static_library("unit_test_support") {
sources = [
"actions/mock_action_delegate.cc",
"actions/mock_action_delegate.h",
+ "actions/wait_for_dom_test_base.cc",
+ "actions/wait_for_dom_test_base.h",
"fake_script_executor_delegate.cc",
"fake_script_executor_delegate.h",
"fake_script_executor_ui_delegate.cc",
@@ -389,6 +420,8 @@ static_library("unit_test_support") {
"mock_autofill_assistant_tts_controller.h",
"mock_client.cc",
"mock_client.h",
+ "mock_common_dependencies.cc",
+ "mock_common_dependencies.h",
"mock_controller_observer.cc",
"mock_controller_observer.h",
"mock_execution_delegate.cc",
@@ -439,6 +472,7 @@ source_set("unit_tests") {
"actions/edit_password_action_unittest.cc",
"actions/execute_js_action_unittest.cc",
"actions/expect_navigation_action_unittest.cc",
+ "actions/external_action_unittest.cc",
"actions/fallback_handler/required_field_unittest.cc",
"actions/fallback_handler/required_fields_fallback_handler_unittest.cc",
"actions/generate_password_for_form_field_action_unittest.cc",
@@ -449,6 +483,7 @@ source_set("unit_tests") {
"actions/popup_message_action_unittest.cc",
"actions/presave_generated_password_action_unittest.cc",
"actions/prompt_action_unittest.cc",
+ "actions/register_password_reset_request_action_unittest.cc",
"actions/release_elements_action_unittest.cc",
"actions/reset_pending_credentials_action_unittest.cc",
"actions/save_generated_password_action_unittest.cc",
@@ -548,9 +583,13 @@ source_set("unit_tests") {
"web/element_action_util_unittest.cc",
"web/element_store_unittest.cc",
"web/send_keyboard_input_worker_unittest.cc",
- "website_login_manager_impl_unittest.cc",
]
+ # TODO(crbug.com/1329148): enable these tests on desktop.
+ if (is_android) {
+ sources += [ "website_login_manager_impl_unittest.cc" ]
+ }
+
deps = [
":browser",
":proto",
@@ -571,6 +610,7 @@ source_set("unit_tests") {
"//components/ukm:test_support",
"//components/ukm/content:content",
"//components/url_matcher",
+ "//components/variations/service",
"//components/version_info",
"//content/test:test_support",
"//services/network:test_support",
@@ -593,7 +633,10 @@ if (is_android) {
"service.proto",
"view_layout.proto",
]
- deps = [ "//components/autofill_assistant/content/common/proto:proto_java" ]
+ deps = [
+ "//components/autofill_assistant/browser/public:proto_java",
+ "//components/autofill_assistant/content/common/proto:proto_java",
+ ]
}
java_cpp_enum("autofill_assistant_enums_java") {
diff --git a/chromium/components/autofill_assistant/browser/DEPS b/chromium/components/autofill_assistant/browser/DEPS
index d10f5905f86..3e9015556eb 100644
--- a/chromium/components/autofill_assistant/browser/DEPS
+++ b/chromium/components/autofill_assistant/browser/DEPS
@@ -39,5 +39,11 @@ specific_include_rules = {
"+components/prefs",
"+components/safe_browsing/core/common",
"+third_party/blink/public/common/features.h",
- ]
+ ],
+ 'dependencies_util.h': [
+ '+components/variations/service/variations_service.h',
+ ],
+ 'dependencies_util.cc': [
+ '+components/variations/service/variations_service.h',
+ ],
}
diff --git a/chromium/components/autofill_assistant/browser/action_value.proto b/chromium/components/autofill_assistant/browser/action_value.proto
index 5f4ea46fff1..1e5fcc5a38b 100644
--- a/chromium/components/autofill_assistant/browser/action_value.proto
+++ b/chromium/components/autofill_assistant/browser/action_value.proto
@@ -18,6 +18,16 @@ message AutofillProfile {
// A value expression.
message ValueExpression {
+ // Used to perform regexp-based replacements.
+ message RegexpReplacement {
+ // If the text filter matches ...
+ optional TextFilter text_filter = 1;
+ // ... replace by this value.
+ optional string replacement = 2;
+ // If true, replaces all occurrences, otherwise the first one only.
+ optional bool global = 3;
+ }
+
message Chunk {
oneof chunk {
// An integer representation to resolve a piece of Autofill information.
@@ -35,10 +45,29 @@ message ValueExpression {
string memory_key = 4;
}
- // If the chunk fully matches the key, it will be replaced. When used
- // in a regular expression context, the key needs to be quoted. Similarly,
- // the replacement will be substituted as is, without being quoted.
+ // If the chunk fully matches the given case-sensitive key, it will be
+ // replaced with the specified value.
+ // When used in a regular expression context, no escaping is applied to
+ // either the key or value. As a result, both fields must be explicitly
+ // escaped in the message.
+ // Examples:
+ // - key: GB, value: (UK|GB)
+ // - key: United\ States, value: (United States|USA|U\.S\.A\.?)
map<string, string> replacements = 3;
+ // If any of the replacements match, apply them. While not explicitly
+ // forbidden it should not be required to use this in combination with
+ // the key/value replacements. In case they are chained, the key/value
+ // replacements are applied first. Backslash-escaped digits (\1 to \9) can
+ // be used to insert text matching the corresponding parenthesized group
+ // from the pattern. \0 refers to the entire matching text.
+ // Examples:
+ // - text_filter: ^0, replacement: "", is_global: false
+ // Replaces a leading 0 in the chunk
+ // - text_filter: \s+, replacement: "", is_global: true
+ // Removes all whitespace in the chunk
+ // - text_filter: (\w+)\s(\w+), replacement: \2 \1
+ // Flips two words
+ repeated RegexpReplacement regexp_replacements = 5;
}
repeated Chunk chunk = 1;
}
diff --git a/chromium/components/autofill_assistant/browser/actions/action.cc b/chromium/components/autofill_assistant/browser/actions/action.cc
index 0189c29bbdf..8103f5a3c72 100644
--- a/chromium/components/autofill_assistant/browser/actions/action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/action.cc
@@ -273,6 +273,15 @@ std::ostream& operator<<(std::ostream& out,
case ActionProto::ActionInfoCase::kJsFlow:
out << "JsFlow";
break;
+ case ActionProto::ActionInfoCase::kExternalAction:
+ out << "ExternalAction";
+ break;
+ case ActionProto::ActionInfoCase::kRegisterPasswordResetRequest:
+ out << "RegisterPasswordResetRequest";
+ break;
+ case ActionProto::ActionInfoCase::kSetNativeValue:
+ out << "SetNativeValue";
+ break;
case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET:
out << "ACTION_INFO_NOT_SET";
break;
diff --git a/chromium/components/autofill_assistant/browser/actions/action.h b/chromium/components/autofill_assistant/browser/actions/action.h
index b0545ea45ce..452d0009db3 100644
--- a/chromium/components/autofill_assistant/browser/actions/action.h
+++ b/chromium/components/autofill_assistant/browser/actions/action.h
@@ -102,6 +102,7 @@ class Action {
base::WeakPtrFactory<Action> weak_ptr_factory_{this};
private:
+ friend class CollectUserDataActionTest;
friend class JsFlowActionTest;
};
diff --git a/chromium/components/autofill_assistant/browser/actions/action_delegate.h b/chromium/components/autofill_assistant/browser/actions/action_delegate.h
index b9e5997890b..3db5123227d 100644
--- a/chromium/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/chromium/components/autofill_assistant/browser/actions/action_delegate.h
@@ -11,6 +11,10 @@
#include "base/callback.h"
#include "base/callback_helpers.h"
+#include "components/autofill_assistant/browser/js_flow_devtools_wrapper.h"
+#include "components/autofill_assistant/browser/public/external_action_delegate.h"
+#include "components/autofill_assistant/browser/public/external_script_controller.h"
+#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/tts_button_state.h"
#include "components/autofill_assistant/browser/viewport_mode.h"
@@ -66,11 +70,13 @@ class ShowProgressBarProto_StepProgressBarConfiguration;
class ProcessedActionStatusDetailsProto;
class GetUserDataResponseProto;
class ElementAreaProto;
+class ExternalActionProto;
enum ConfigureBottomSheetProto_PeekMode : int;
enum ConfigureUiStateProto_OverlayBehavior : int;
enum DocumentReadyState : int;
enum class UserDataFieldChange;
+enum class UserDataEventField;
// Action delegate called when processing actions.
class ActionDelegate {
@@ -194,7 +200,7 @@ class ActionDelegate {
bool browse_mode_invisible = false) = 0;
// Have the UI leave the prompt state and go back to its previous state.
- virtual void CleanUpAfterPrompt() = 0;
+ virtual void CleanUpAfterPrompt(bool consume_touchable_area = true) = 0;
// Set the list of allowed domains to be used when we enter a browse state.
// This list is used to determine whether a user initiated navigation to a
@@ -312,6 +318,10 @@ class ActionDelegate {
// Get associated web contents.
virtual content::WebContents* GetWebContents() const = 0;
+ // Get the wrapper that owns the web contents and devtools client for js
+ // flows.
+ virtual JsFlowDevtoolsWrapper* GetJsFlowDevtoolsWrapper() const = 0;
+
// Get the ElementStore.
virtual ElementStore* GetElementStore() const = 0;
@@ -457,11 +467,35 @@ class ActionDelegate {
// gets attached to the action's response if non empty.
virtual ProcessedActionStatusDetailsProto& GetLogInfo() = 0;
+ // Sends a request to retrieve the required user data for this flow. Returns
+ // the result through the |callback|. Enters the |RUNNING| state while doing
+ // so.
virtual void RequestUserData(
+ UserDataEventField event_field,
const CollectUserDataOptions& options,
base::OnceCallback<void(bool, const GetUserDataResponseProto&)>
callback) = 0;
+ // Whether the current flow supports external actions.
+ virtual bool SupportsExternalActions() = 0;
+
+ // Executes the |external_action|.
+ virtual void RequestExternalAction(
+ const ExternalActionProto& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) = 0;
+
+ // Returns whether or not this instance of Autofill Assistant must use a
+ // backend endpoint to query data.
+ virtual bool MustUseBackendData() const = 0;
+
+ // Maybe sets the previously executed action. JS flow actions are excluded
+ // because they act as a script executor.
+ virtual void MaybeSetPreviousAction(
+ const ProcessedActionProto& processed_action) = 0;
+
virtual base::WeakPtr<ActionDelegate> GetWeakPtr() const = 0;
protected:
diff --git a/chromium/components/autofill_assistant/browser/actions/action_delegate_util.cc b/chromium/components/autofill_assistant/browser/actions/action_delegate_util.cc
index e81fbad233d..78bda865794 100644
--- a/chromium/components/autofill_assistant/browser/actions/action_delegate_util.cc
+++ b/chromium/components/autofill_assistant/browser/actions/action_delegate_util.cc
@@ -12,7 +12,7 @@
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/string_conversions_util.h"
#include "components/autofill_assistant/browser/user_data_util.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/element_store.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
#include "ui/events/keycodes/dom/dom_key.h"
diff --git a/chromium/components/autofill_assistant/browser/actions/action_delegate_util.h b/chromium/components/autofill_assistant/browser/actions/action_delegate_util.h
index 11cfb7e2048..4f02bafe801 100644
--- a/chromium/components/autofill_assistant/browser/actions/action_delegate_util.h
+++ b/chromium/components/autofill_assistant/browser/actions/action_delegate_util.h
@@ -11,9 +11,10 @@
#include "components/autofill_assistant/browser/selector.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/web/element_action_util.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
+class ElementFinderResult;
+
namespace action_delegate_util {
// Finds the element given by the selector. If the resolution fails, it
diff --git a/chromium/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc b/chromium/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
index 533d06988f1..d7f9b58c75c 100644
--- a/chromium/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
@@ -218,7 +218,7 @@ TEST_F(ActionDelegateUtilTest, PerformWithPasswordManagerValue) {
auto element = std::make_unique<ElementFinderResult>();
content::WebContentsTester::For(web_contents_.get())
->NavigateAndCommit(GURL("https://www.example.com"));
- element->SetRenderFrameHost(web_contents_->GetMainFrame());
+ element->SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
user_data_.selected_login_ = absl::make_optional<WebsiteLoginManager::Login>(
GURL("https://www.example.com"), "username");
@@ -241,7 +241,7 @@ TEST_F(ActionDelegateUtilTest, PerformWithPasswordManagerValue) {
TEST_F(ActionDelegateUtilTest, PerformWithFailingPasswordManagerValue) {
auto element = std::make_unique<ElementFinderResult>();
- element->SetRenderFrameHost(web_contents_->GetMainFrame());
+ element->SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
user_data_.selected_login_ = absl::make_optional<WebsiteLoginManager::Login>(
GURL("https://www.example.com"), "username");
diff --git a/chromium/components/autofill_assistant/browser/actions/action_test_utils.cc b/chromium/components/autofill_assistant/browser/actions/action_test_utils.cc
index cb6104214ac..0ee8694db9c 100644
--- a/chromium/components/autofill_assistant/browser/actions/action_test_utils.cc
+++ b/chromium/components/autofill_assistant/browser/actions/action_test_utils.cc
@@ -7,7 +7,7 @@
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/selector.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/autofill_assistant/browser/actions/action_test_utils.h b/chromium/components/autofill_assistant/browser/actions/action_test_utils.h
index 8e596decef6..8b3e991c8fd 100644
--- a/chromium/components/autofill_assistant/browser/actions/action_test_utils.h
+++ b/chromium/components/autofill_assistant/browser/actions/action_test_utils.h
@@ -9,7 +9,7 @@
#include "components/autofill_assistant/browser/action_value.pb.h"
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/selector.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/autofill_assistant/browser/actions/check_element_tag_action.h b/chromium/components/autofill_assistant/browser/actions/check_element_tag_action.h
index d2bcabcd316..4635396b154 100644
--- a/chromium/components/autofill_assistant/browser/actions/check_element_tag_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/check_element_tag_action.h
@@ -11,7 +11,7 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/dom_action.pb.h"
#include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/check_element_tag_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/check_element_tag_action_unittest.cc
index 22f8bd54593..a32a113f446 100644
--- a/chromium/components/autofill_assistant/browser/actions/check_element_tag_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/check_element_tag_action_unittest.cc
@@ -11,7 +11,7 @@
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/dom_action.pb.h"
#include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/element_store.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/autofill_assistant/browser/actions/check_option_element_action.h b/chromium/components/autofill_assistant/browser/actions/check_option_element_action.h
index 6db01ca3fb2..42ef5498b62 100644
--- a/chromium/components/autofill_assistant/browser/actions/check_option_element_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/check_option_element_action.h
@@ -11,7 +11,7 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/dom_action.pb.h"
#include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/check_option_element_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/check_option_element_action_unittest.cc
index f82b321eccb..4214ba45fcb 100644
--- a/chromium/components/autofill_assistant/browser/actions/check_option_element_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/check_option_element_action_unittest.cc
@@ -11,7 +11,7 @@
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/dom_action.pb.h"
#include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/element_store.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc
index 5edb25d5659..5177a342664 100644
--- a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc
@@ -31,7 +31,6 @@
#include "components/autofill_assistant/browser/user_data_util.h"
#include "components/autofill_assistant/browser/website_login_manager_impl.h"
#include "components/strings/grit/components_strings.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
@@ -608,38 +607,81 @@ void CollectUserDataAction::OnShowToUser(UserData* user_data,
void CollectUserDataAction::UpdateUserData(UserData* user_data) {
if (proto_.collect_user_data().has_data_source()) {
delegate_->RequestUserData(
- *collect_user_data_options_,
+ UserDataEventField::NONE, *collect_user_data_options_,
base::BindOnce(&CollectUserDataAction::OnRequestUserData,
- weak_ptr_factory_.GetWeakPtr(), user_data));
+ weak_ptr_factory_.GetWeakPtr(),
+ /* is_initial_request= */ true, user_data));
return;
}
+ UseChromeData(user_data);
+}
+
+void CollectUserDataAction::UseChromeData(UserData* user_data) {
DCHECK(delegate_->GetPersonalDataManager());
delegate_->GetPersonalDataManager()->AddObserver(this);
UpdatePersonalDataManagerProfiles(user_data);
UpdatePersonalDataManagerCards(user_data);
- UpdateMetrics(user_data);
+ UpdateMetrics(user_data, Metrics::UserDataSource::CHROME_AUTOFILL);
UpdateUi();
action_stopwatch_.StartWaitTime();
}
void CollectUserDataAction::OnRequestUserData(
+ bool is_initial_request,
UserData* user_data,
bool success,
const GetUserDataResponseProto& response) {
if (!success) {
+ if (is_initial_request && !delegate_->MustUseBackendData() &&
+ proto_.collect_user_data().data_source().allow_fallback()) {
+ FallbackToChromeData(user_data);
+ return;
+ }
+
EndAction(ClientStatus(USER_DATA_REQUEST_FAILED),
Metrics::CollectUserDataResult::FAILURE);
return;
}
UpdateUserDataFromProto(response, user_data);
- UpdateMetrics(user_data);
+ UpdateMetrics(user_data, Metrics::UserDataSource::BACKEND);
UpdateUi();
action_stopwatch_.StartWaitTime();
}
+void CollectUserDataAction::FallbackToChromeData(UserData* user_data) {
+ if (collect_user_data_options_->request_phone_number_separately) {
+ collect_user_data_options_->request_payer_phone = true;
+ collect_user_data_options_->request_phone_number_separately = false;
+ collect_user_data_options_->phone_number_section_title = std::string();
+
+ for (const auto& required_data_piece :
+ collect_user_data_options_->required_phone_number_data_pieces) {
+ collect_user_data_options_->required_contact_data_pieces.emplace_back(
+ required_data_piece);
+ }
+ collect_user_data_options_->required_phone_number_data_pieces.clear();
+
+ collect_user_data_options_->contact_summary_fields.emplace_back(
+ AutofillContactField::PHONE_HOME_WHOLE_NUMBER);
+ collect_user_data_options_->contact_summary_max_lines++;
+
+ collect_user_data_options_->contact_full_fields.emplace_back(
+ AutofillContactField::PHONE_HOME_WHOLE_NUMBER);
+ collect_user_data_options_->contact_full_max_lines++;
+ }
+
+ collect_user_data_options_->data_origin_notice.reset();
+
+ collect_user_data_options_->should_store_data_changes =
+ !delegate_->GetWebContents()->GetBrowserContext()->IsOffTheRecord();
+ collect_user_data_options_->use_alternative_edit_dialogs = false;
+
+ UseChromeData(user_data);
+}
+
void CollectUserDataAction::UpdateUi() {
const auto& collect_user_data = proto_.collect_user_data();
if (collect_user_data.has_prompt()) {
@@ -650,16 +692,16 @@ void CollectUserDataAction::UpdateUi() {
delegate_->CollectUserData(collect_user_data_options_.get());
}
-void CollectUserDataAction::UpdateMetrics(UserData* user_data) {
+void CollectUserDataAction::UpdateMetrics(
+ UserData* user_data,
+ Metrics::UserDataSource user_data_source) {
DCHECK(user_data);
if (!shown_to_user_) {
shown_to_user_ = true;
- metrics_data_.source_id =
- ukm::GetSourceIdForWebContentsDocument(delegate_->GetWebContents());
- metrics_data_.user_data_source =
- ShouldUseBackendData(proto_.collect_user_data())
- ? Metrics::UserDataSource::BACKEND
- : Metrics::UserDataSource::CHROME_AUTOFILL;
+ metrics_data_.source_id = delegate_->GetWebContents()
+ ->GetPrimaryMainFrame()
+ ->GetPageUkmSourceId();
+ metrics_data_.user_data_source = user_data_source;
FillInitialDataStateForMetrics(user_data->available_contacts_,
user_data->available_addresses_,
user_data->available_payment_instruments_);
@@ -800,7 +842,8 @@ void CollectUserDataAction::OnTermsAndConditionsLinkClicked(
Metrics::CollectUserDataResult::TERMS_AND_CONDITIONS_LINK_CLICKED);
}
-void CollectUserDataAction::ReloadUserData(UserData* user_data) {
+void CollectUserDataAction::ReloadUserData(UserDataEventField event_field,
+ UserData* user_data) {
if (HasActionEnded()) {
return;
}
@@ -810,28 +853,32 @@ void CollectUserDataAction::ReloadUserData(UserData* user_data) {
collect_user_data_options_->reload_data_callback = base::BindOnce(
&CollectUserDataAction::ReloadUserData, weak_ptr_factory_.GetWeakPtr());
delegate_->RequestUserData(
- *collect_user_data_options_,
+ event_field, *collect_user_data_options_,
base::BindOnce(&CollectUserDataAction::OnRequestUserData,
- weak_ptr_factory_.GetWeakPtr(), user_data));
+ weak_ptr_factory_.GetWeakPtr(),
+ /* is_initial_request= */ false, user_data));
}
void CollectUserDataAction::OnSelectionStateChanged(
UserDataEventField field,
UserDataEventType event_type) {
switch (field) {
- case CONTACT_EVENT:
+ case UserDataEventField::CONTACT_EVENT:
metrics_data_.contact_selection_state = user_data::GetNewSelectionState(
metrics_data_.contact_selection_state, event_type);
break;
- case CREDIT_CARD_EVENT:
+ case UserDataEventField::CREDIT_CARD_EVENT:
metrics_data_.credit_card_selection_state =
user_data::GetNewSelectionState(
metrics_data_.credit_card_selection_state, event_type);
break;
- case SHIPPING_EVENT:
+ case UserDataEventField::SHIPPING_EVENT:
metrics_data_.shipping_selection_state = user_data::GetNewSelectionState(
metrics_data_.shipping_selection_state, event_type);
break;
+ case UserDataEventField::PHONE_NUMBER_EVENT:
+ case UserDataEventField::NONE:
+ break;
}
}
@@ -988,13 +1035,16 @@ bool CollectUserDataAction::CreateOptionsFromProto() {
collect_user_data.required_shipping_address_data_piece().end());
}
+ bool should_use_backend_data = ShouldUseBackendData(collect_user_data);
+ if (delegate_->MustUseBackendData() && !should_use_backend_data) {
+ VLOG(1) << "This run must use backend data but does not.";
+ return false;
+ }
collect_user_data_options_->should_store_data_changes =
!delegate_->GetWebContents()->GetBrowserContext()->IsOffTheRecord() &&
- !ShouldUseBackendData(collect_user_data);
- collect_user_data_options_->can_edit_contacts =
- !ShouldUseBackendData(collect_user_data);
- collect_user_data_options_->use_gms_core_edit_dialogs =
- ShouldUseBackendData(collect_user_data);
+ !should_use_backend_data;
+ collect_user_data_options_->use_alternative_edit_dialogs =
+ should_use_backend_data;
collect_user_data_options_->request_login_choice =
collect_user_data.has_login_details();
@@ -1137,6 +1187,11 @@ bool CollectUserDataAction::CreateOptionsFromProto() {
delegate_->GetEmailAddressForAccessTokenAccount();
if (collect_user_data.has_data_origin_notice()) {
+ if (!should_use_backend_data) {
+ VLOG(1) << "Data origin notice should only be shown for backend provided "
+ "data.";
+ return false;
+ }
const auto& notice = collect_user_data.data_origin_notice();
if (notice.link_text().empty() || notice.dialog_title().empty() ||
notice.dialog_text().empty() || notice.dialog_button_text().empty()) {
@@ -1410,6 +1465,12 @@ void CollectUserDataAction::UpdateUserDataFromProto(
if (RequiresContact(*collect_user_data_options_)) {
user_data->available_contacts_.clear();
+ for (const auto& transient_contact : user_data->transient_contacts_) {
+ auto contact = std::make_unique<Contact>(
+ user_data::MakeUniqueFromProfile(*transient_contact->profile));
+ contact->identifier = transient_contact->identifier;
+ user_data->available_contacts_.emplace_back(std::move(contact));
+ }
for (const auto& profile_data : proto_data.available_contacts()) {
auto profile = std::make_unique<autofill::AutofillProfile>();
AddProtoDataToAutofillDataModel(profile_data.values(),
@@ -1424,13 +1485,15 @@ void CollectUserDataAction::UpdateUserDataFromProto(
if (profile_data.has_identifier()) {
contact->identifier = profile_data.identifier();
}
+ contact->can_edit = false;
user_data->available_contacts_.emplace_back(std::move(contact));
}
if (proto_data.has_selected_contact_identifier()) {
const auto& it = base::ranges::find_if(
user_data->available_contacts_, [&](const auto& contact) {
- return proto_data.selected_contact_identifier() ==
- contact->identifier.value_or(std::string());
+ return contact->identifier &&
+ proto_data.selected_contact_identifier() ==
+ contact->identifier;
});
if (it == user_data->available_contacts_.end()) {
NOTREACHED();
@@ -1450,6 +1513,13 @@ void CollectUserDataAction::UpdateUserDataFromProto(
if (RequiresPhoneNumberSeparately(*collect_user_data_options_)) {
user_data->available_phone_numbers_.clear();
+ for (const auto& transient_phone_number :
+ user_data->transient_phone_numbers_) {
+ auto phone_number = std::make_unique<PhoneNumber>(
+ user_data::MakeUniqueFromProfile(*transient_phone_number->profile));
+ phone_number->identifier = transient_phone_number->identifier;
+ user_data->available_phone_numbers_.emplace_back(std::move(phone_number));
+ }
for (const auto& phone_number_data : proto_data.available_phone_numbers()) {
auto profile = std::make_unique<autofill::AutofillProfile>();
AddAutofillEntryToDataModel(
@@ -1459,6 +1529,7 @@ void CollectUserDataAction::UpdateUserDataFromProto(
if (phone_number_data.has_identifier()) {
phone_number->identifier = phone_number_data.identifier();
}
+ phone_number->can_edit = false;
user_data->available_phone_numbers_.emplace_back(std::move(phone_number));
}
if (proto_data.has_selected_phone_number_identifier()) {
diff --git a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h
index 5e8f61c1d68..789958e9b8b 100644
--- a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h
@@ -77,7 +77,7 @@ class CollectUserDataAction : public Action,
const UserModel* user_model);
bool IsValidUserFormSection(
const autofill_assistant::UserFormSectionProto& proto);
- void ReloadUserData(UserData* user_data);
+ void ReloadUserData(UserDataEventField event_field, UserData* user_data);
// Only used for logging purposes.
void OnSelectionStateChanged(UserDataEventField field,
@@ -88,10 +88,14 @@ class CollectUserDataAction : public Action,
void ShowToUser();
void OnShowToUser(UserData* user_data, UserDataFieldChange* field_change);
void UpdateUserData(UserData* user_data);
- void OnRequestUserData(UserData* user_data,
+ void UseChromeData(UserData* user_data);
+ void OnRequestUserData(bool is_initial_request,
+ UserData* user_data,
bool success,
const GetUserDataResponseProto& response);
- void UpdateMetrics(UserData* user_data);
+ void FallbackToChromeData(UserData* user_data);
+ void UpdateMetrics(UserData* user_data,
+ Metrics::UserDataSource user_data_source);
void UpdateUi();
// Creates a new instance of |CollectUserDataOptions| from |proto_|.
diff --git a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
index 82b7cc92b95..7febc8cf02c 100644
--- a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
@@ -143,6 +143,8 @@ using ::testing::SizeIs;
using ::testing::StrEq;
using ::testing::UnorderedElementsAre;
+} // namespace
+
class CollectUserDataActionTest : public testing::Test {
public:
void SetUp() override {
@@ -151,7 +153,7 @@ class CollectUserDataActionTest : public testing::Test {
content::WebContentsTester::For(web_contents_.get())
->SetLastCommittedURL(GURL(kFakeUrl));
ukm::InitializeSourceUrlRecorderForWebContents(web_contents_.get());
- source_id_ = ukm::GetSourceIdForWebContentsDocument(web_contents_.get());
+ source_id_ = web_contents_->GetPrimaryMainFrame()->GetPageUkmSourceId();
ON_CALL(mock_action_delegate_, GetPersonalDataManager)
.WillByDefault(Return(&mock_personal_data_manager_));
@@ -184,6 +186,8 @@ class CollectUserDataActionTest : public testing::Test {
.WillByDefault(Return(web_contents_.get()));
ON_CALL(mock_action_delegate_, GetUkmRecorder())
.WillByDefault(Return(&ukm_recorder_));
+ ON_CALL(mock_action_delegate_, MustUseBackendData())
+ .WillByDefault(Return(false));
}
void ExpectSelectedProfileMatches(const std::string& profile_name,
@@ -212,6 +216,10 @@ class CollectUserDataActionTest : public testing::Test {
Pointee(MatchesCard(*card)));
}
+ void AddWaitTime(CollectUserDataAction* action, base::TimeDelta delta) {
+ action->action_stopwatch_.TransferToWaitTime(delta);
+ }
+
protected:
content::BrowserTaskEnvironment task_environment_;
content::RenderViewHostTestEnabler rvh_test_enabler_;
@@ -2567,16 +2575,86 @@ TEST_F(CollectUserDataActionTest, ConfirmButtonFallbackText) {
action.ProcessAction(callback_.Get());
}
+TEST_F(CollectUserDataActionTest, FailsForWebLayerRunsWithoutBackendData) {
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ EXPECT_CALL(mock_action_delegate_, CollectUserData).Times(0);
+ EXPECT_CALL(mock_action_delegate_, RequestUserData).Times(0);
+
+ ActionProto action_proto;
+ auto* collect_user_data = action_proto.mutable_collect_user_data();
+ collect_user_data->set_request_terms_and_conditions(false);
+ collect_user_data->set_request_payment_method(true);
+ collect_user_data->set_billing_address_name("billing");
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, FailsForDataOriginNoticeWithoutBackendData) {
+ EXPECT_CALL(mock_action_delegate_, CollectUserData).Times(0);
+ EXPECT_CALL(mock_action_delegate_, RequestUserData).Times(0);
+
+ ActionProto action_proto;
+ auto* collect_user_data = action_proto.mutable_collect_user_data();
+ collect_user_data->set_request_terms_and_conditions(false);
+ collect_user_data->set_request_payment_method(true);
+ collect_user_data->set_billing_address_name("billing");
+ collect_user_data->mutable_data_origin_notice()->set_link_text("Link");
+ collect_user_data->mutable_data_origin_notice()->set_dialog_title("Title");
+ collect_user_data->mutable_data_origin_notice()->set_dialog_text("Text");
+ collect_user_data->mutable_data_origin_notice()->set_dialog_button_text(
+ "Button");
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, SucceedsWithDataOriginNoticeAndBackendData) {
+ EXPECT_CALL(mock_action_delegate_, RequestUserData)
+ .WillOnce(RunOnceCallback<2>(true, GetUserDataResponseProto()));
+ ON_CALL(mock_action_delegate_, CollectUserData)
+ .WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
+ EXPECT_TRUE(collect_user_data_options->data_origin_notice);
+ // Do not finish the action.
+ });
+
+ ActionProto action_proto;
+ auto* collect_user_data = action_proto.mutable_collect_user_data();
+ collect_user_data->set_request_terms_and_conditions(false);
+ collect_user_data->set_request_payment_method(true);
+ collect_user_data->set_billing_address_name("billing");
+ collect_user_data->mutable_data_origin_notice()->set_link_text("Link");
+ collect_user_data->mutable_data_origin_notice()->set_dialog_title("Title");
+ collect_user_data->mutable_data_origin_notice()->set_dialog_text("Text");
+ collect_user_data->mutable_data_origin_notice()->set_dialog_button_text(
+ "Button");
+ collect_user_data->mutable_data_source();
+
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
TEST_F(CollectUserDataActionTest, ContactDataFromProto) {
- ON_CALL(mock_action_delegate_, GetPersonalDataManager())
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
.WillByDefault(Return(nullptr));
- ON_CALL(mock_action_delegate_, CollectUserData(_))
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ ON_CALL(mock_action_delegate_, CollectUserData)
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
EXPECT_FALSE(collect_user_data_options->should_store_data_changes);
- EXPECT_FALSE(collect_user_data_options->can_edit_contacts);
ASSERT_EQ(user_data_.available_contacts_.size(), 1u);
EXPECT_THAT(user_data_.available_contacts_[0]->profile->guid(),
Not(IsEmpty()));
+ EXPECT_FALSE(user_data_.available_contacts_[0]->can_edit);
auto mappings = field_formatter::CreateAutofillMappings(
*user_data_.available_contacts_[0]->profile, "en-US");
EXPECT_THAT(mappings,
@@ -2598,7 +2676,7 @@ TEST_F(CollectUserDataActionTest, ContactDataFromProto) {
auto* incomplete = user_data_response.add_available_contacts();
(*incomplete->mutable_values())[7] = MakeAutofillEntry("Jane Doe");
EXPECT_CALL(mock_action_delegate_, RequestUserData)
- .WillOnce(RunOnceCallback<1>(true, user_data_response));
+ .WillOnce(RunOnceCallback<2>(true, user_data_response));
ActionProto action_proto;
auto* collect_user_data = action_proto.mutable_collect_user_data();
@@ -2623,16 +2701,17 @@ TEST_F(CollectUserDataActionTest, ContactDataFromProto) {
}
TEST_F(CollectUserDataActionTest, PhoneNumberFromProto) {
- ON_CALL(mock_action_delegate_, GetPersonalDataManager())
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
.WillByDefault(Return(nullptr));
- ON_CALL(mock_action_delegate_, CollectUserData(_))
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ ON_CALL(mock_action_delegate_, CollectUserData)
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
EXPECT_FALSE(collect_user_data_options->should_store_data_changes);
- EXPECT_FALSE(collect_user_data_options->can_edit_contacts);
-
ASSERT_EQ(user_data_.available_contacts_.size(), 1u);
EXPECT_THAT(user_data_.available_contacts_[0]->profile->guid(),
Not(IsEmpty()));
+ EXPECT_FALSE(user_data_.available_contacts_[0]->can_edit);
auto contact_mappings = field_formatter::CreateAutofillMappings(
*user_data_.available_contacts_[0]->profile, "en-US");
// Initially the contact contains the backend data.
@@ -2668,7 +2747,7 @@ TEST_F(CollectUserDataActionTest, PhoneNumberFromProto) {
*user_data_response.add_available_phone_numbers()->mutable_value() =
MakeAutofillEntry("+1 187-654-3210");
EXPECT_CALL(mock_action_delegate_, RequestUserData)
- .WillOnce(RunOnceCallback<1>(true, user_data_response));
+ .WillOnce(RunOnceCallback<2>(true, user_data_response));
ActionProto action_proto;
auto* collect_user_data = action_proto.mutable_collect_user_data();
@@ -2680,6 +2759,9 @@ TEST_F(CollectUserDataActionTest, PhoneNumberFromProto) {
kMemoryLocation);
collect_user_data->mutable_contact_details()
->set_separate_phone_number_section(true);
+ *collect_user_data->mutable_contact_details()
+ ->add_phone_number_required_data_piece() =
+ MakeRequiredDataPiece(autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER);
collect_user_data->mutable_contact_details()->set_phone_number_section_title(
"Phone number");
collect_user_data->mutable_data_source();
@@ -2706,9 +2788,11 @@ TEST_F(CollectUserDataActionTest, PhoneNumberFromProto) {
TEST_F(CollectUserDataActionTest, PaymentDataFromProto) {
autofill::CountryNames::SetLocaleString("en-US");
- ON_CALL(mock_action_delegate_, GetPersonalDataManager())
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
.WillByDefault(Return(nullptr));
- ON_CALL(mock_action_delegate_, CollectUserData(_))
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ ON_CALL(mock_action_delegate_, CollectUserData)
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
EXPECT_FALSE(collect_user_data_options->should_store_data_changes);
EXPECT_THAT(user_data_.available_payment_instruments_[0]->card->guid(),
@@ -2771,7 +2855,7 @@ TEST_F(CollectUserDataActionTest, PaymentDataFromProto) {
AddCompleteAddressEntriesToMap("John Doe",
payment_instrument->mutable_address_values());
EXPECT_CALL(mock_action_delegate_, RequestUserData)
- .WillOnce(RunOnceCallback<1>(true, user_data_response));
+ .WillOnce(RunOnceCallback<2>(true, user_data_response));
ActionProto action_proto;
auto* collect_user_data = action_proto.mutable_collect_user_data();
@@ -2791,9 +2875,11 @@ TEST_F(CollectUserDataActionTest, PaymentDataFromProto) {
TEST_F(CollectUserDataActionTest, ShippingDataFromProto) {
autofill::CountryNames::SetLocaleString("en-US");
- ON_CALL(mock_action_delegate_, GetPersonalDataManager())
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
.WillByDefault(Return(nullptr));
- ON_CALL(mock_action_delegate_, CollectUserData(_))
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ ON_CALL(mock_action_delegate_, CollectUserData)
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
EXPECT_FALSE(collect_user_data_options->should_store_data_changes);
EXPECT_THAT(user_data_.available_addresses_[0]->profile->guid(),
@@ -2820,7 +2906,7 @@ TEST_F(CollectUserDataActionTest, ShippingDataFromProto) {
auto* address = user_data_response.add_available_addresses();
AddCompleteAddressEntriesToMap("John Doe", address->mutable_values());
EXPECT_CALL(mock_action_delegate_, RequestUserData)
- .WillOnce(RunOnceCallback<1>(true, user_data_response));
+ .WillOnce(RunOnceCallback<2>(true, user_data_response));
ActionProto action_proto;
auto* collect_user_data = action_proto.mutable_collect_user_data();
@@ -2838,9 +2924,11 @@ TEST_F(CollectUserDataActionTest, ShippingDataFromProto) {
}
TEST_F(CollectUserDataActionTest, RawDataFromProtoDoesNotGetFormatted) {
- ON_CALL(mock_action_delegate_, GetPersonalDataManager())
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
.WillByDefault(Return(nullptr));
- ON_CALL(mock_action_delegate_, CollectUserData(_))
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ ON_CALL(mock_action_delegate_, CollectUserData)
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
EXPECT_FALSE(collect_user_data_options->should_store_data_changes);
EXPECT_THAT(user_data_.available_contacts_[0]->profile->guid(),
@@ -2871,7 +2959,7 @@ TEST_F(CollectUserDataActionTest, RawDataFromProtoDoesNotGetFormatted) {
(*profile->mutable_values())[14] =
MakeAutofillEntry("+1 123-456-7890", /* raw= */ true);
EXPECT_CALL(mock_action_delegate_, RequestUserData)
- .WillOnce(RunOnceCallback<1>(true, user_data_response));
+ .WillOnce(RunOnceCallback<2>(true, user_data_response));
ActionProto action_proto;
auto* collect_user_data = action_proto.mutable_collect_user_data();
@@ -2892,9 +2980,11 @@ TEST_F(CollectUserDataActionTest, RawDataFromProtoDoesNotGetFormatted) {
TEST_F(CollectUserDataActionTest, SelectEntriesFromProtoFromIdentifiers) {
autofill::CountryNames::SetLocaleString("en-US");
- ON_CALL(mock_action_delegate_, GetPersonalDataManager())
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
.WillByDefault(Return(nullptr));
- ON_CALL(mock_action_delegate_, CollectUserData(_))
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ ON_CALL(mock_action_delegate_, CollectUserData)
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
ASSERT_TRUE(user_data_.has_selected_address("contact"));
EXPECT_EQ(user_data_.selected_address("contact")->GetRawInfo(
@@ -2956,7 +3046,7 @@ TEST_F(CollectUserDataActionTest, SelectEntriesFromProtoFromIdentifiers) {
AddCompleteAddressEntriesToMap(
"Jane Doe", payment_instrument_2->mutable_address_values());
EXPECT_CALL(mock_action_delegate_, RequestUserData)
- .WillOnce(RunOnceCallback<1>(true, user_data_response));
+ .WillOnce(RunOnceCallback<2>(true, user_data_response));
ActionProto action_proto;
auto* collect_user_data = action_proto.mutable_collect_user_data();
@@ -2980,9 +3070,11 @@ TEST_F(CollectUserDataActionTest, SelectEntriesFromProtoFromIdentifiers) {
TEST_F(CollectUserDataActionTest,
DefaultSelectEntriesFromProtoWithoutIdentifiers) {
autofill::CountryNames::SetLocaleString("en-US");
- ON_CALL(mock_action_delegate_, GetPersonalDataManager())
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
.WillByDefault(Return(nullptr));
- ON_CALL(mock_action_delegate_, CollectUserData(_))
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ ON_CALL(mock_action_delegate_, CollectUserData)
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
ASSERT_TRUE(user_data_.has_selected_address("contact"));
EXPECT_EQ(user_data_.selected_address("contact")->GetRawInfo(
@@ -3031,7 +3123,7 @@ TEST_F(CollectUserDataActionTest,
AddCompleteAddressEntriesToMap(
"Jane Doe", payment_instrument_2->mutable_address_values());
EXPECT_CALL(mock_action_delegate_, RequestUserData)
- .WillOnce(RunOnceCallback<1>(true, user_data_response));
+ .WillOnce(RunOnceCallback<2>(true, user_data_response));
ActionProto action_proto;
auto* collect_user_data = action_proto.mutable_collect_user_data();
@@ -3307,11 +3399,11 @@ TEST_F(CollectUserDataActionTest, LogsUkmSelectionStateUpdated) {
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
user_data_.terms_and_conditions_ = ACCEPTED;
collect_user_data_options->selected_user_data_changed_callback.Run(
- CONTACT_EVENT, SELECTION_CHANGED);
+ UserDataEventField::CONTACT_EVENT, SELECTION_CHANGED);
collect_user_data_options->selected_user_data_changed_callback.Run(
- CREDIT_CARD_EVENT, ENTRY_CREATED);
+ UserDataEventField::CREDIT_CARD_EVENT, ENTRY_CREATED);
collect_user_data_options->selected_user_data_changed_callback.Run(
- SHIPPING_EVENT, ENTRY_EDITED);
+ UserDataEventField::SHIPPING_EVENT, ENTRY_EDITED);
user_model_.SetSelectedCreditCard(
std::make_unique<autofill::CreditCard>(credit_card), &user_data_);
user_model_.SetSelectedAutofillProfile(
@@ -3570,8 +3662,6 @@ TEST_F(CollectUserDataActionTest, LogsUkmMoreThanFiveProfilesCount) {
}
TEST_F(CollectUserDataActionTest, LogUkmSuccess) {
- base::subtle::ScopedTimeClockOverrides overrides(
- nullptr, &TimeTicksOverride::Now, nullptr);
ActionProto action_proto;
auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
collect_user_data_proto->set_privacy_notice_text("privacy");
@@ -3594,11 +3684,10 @@ TEST_F(CollectUserDataActionTest, LogUkmSuccess) {
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
CollectUserDataAction action(&mock_action_delegate_, action_proto);
+
+ AddWaitTime(&action, base::Milliseconds(4000));
action.ProcessAction(callback_.Get());
- // We start counting the "wait time" after CollecUserData is called, so we
- // need to increase the timer and call the callback at this point.
- TimeTicksOverride::now_ticks_ += base::Seconds(4);
ASSERT_TRUE(confirm_callback);
std::move(confirm_callback).Run(&user_data_, &user_model_);
@@ -3649,8 +3738,6 @@ TEST_F(CollectUserDataActionTest, LogUkmAdditionalActionSelected) {
}
TEST_F(CollectUserDataActionTest, LogUkmFailure) {
- base::subtle::ScopedTimeClockOverrides overrides(
- nullptr, &TimeTicksOverride::Now, nullptr);
ActionProto action_proto;
auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
collect_user_data_proto->set_privacy_notice_text("privacy");
@@ -3666,10 +3753,8 @@ TEST_F(CollectUserDataActionTest, LogUkmFailure) {
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
// The continue button is never pressed.
});
+ AddWaitTime(&action, base::Milliseconds(3000));
action.ProcessAction(callback_.Get());
- // We start counting the "wait time" after CollecUserData is called, so we
- // need to increase the timer at this point.
- TimeTicksOverride::now_ticks_ += base::Seconds(3);
// The CollectUserDataAction destructor is called, this simulates the user
// closing the bottom sheet or the tab.
@@ -3696,7 +3781,7 @@ TEST_F(CollectUserDataActionTest, LogUkmDataFromBackend) {
collect_user_data_proto->mutable_data_source();
EXPECT_CALL(mock_action_delegate_, RequestUserData)
- .WillRepeatedly(RunOnceCallback<1>(true, GetUserDataResponseProto()));
+ .WillRepeatedly(RunOnceCallback<2>(true, GetUserDataResponseProto()));
ON_CALL(mock_action_delegate_, CollectUserData(_))
.WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
user_data_.terms_and_conditions_ = ACCEPTED;
@@ -3881,17 +3966,19 @@ TEST_F(CollectUserDataActionTest, ReloadsDataIfRequested) {
ON_CALL(mock_action_delegate_, GetPersonalDataManager)
.WillByDefault(Return(nullptr));
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
EXPECT_CALL(mock_action_delegate_, RequestUserData)
.Times(3)
- .WillRepeatedly(RunOnceCallback<1>(true, GetUserDataResponseProto()));
+ .WillRepeatedly(RunOnceCallback<2>(true, GetUserDataResponseProto()));
EXPECT_CALL(mock_action_delegate_, CollectUserData(_))
.WillOnce(Invoke([=](CollectUserDataOptions* collect_user_data_options) {
std::move(collect_user_data_options->reload_data_callback)
- .Run(&user_data_);
+ .Run(UserDataEventField::NONE, &user_data_);
}))
.WillOnce(Invoke([=](CollectUserDataOptions* collect_user_data_options) {
std::move(collect_user_data_options->reload_data_callback)
- .Run(&user_data_);
+ .Run(UserDataEventField::NONE, &user_data_);
}))
.WillOnce(Invoke([=](CollectUserDataOptions* collect_user_data_options) {
// We can't submit here since the user data is not complete.
@@ -3914,5 +4001,205 @@ TEST_F(CollectUserDataActionTest, ReloadsDataIfRequested) {
"Android.AutofillAssistant.PaymentRequest.AutofillChanged", 1u);
}
-} // namespace
+TEST_F(CollectUserDataActionTest, MergesTransientDataWithUserDataFromBackend) {
+ auto transient_contact = std::make_unique<autofill::AutofillProfile>();
+ transient_contact->SetRawInfo(autofill::NAME_FULL, u"Jane Doe");
+ user_data_.transient_contacts_.emplace_back(
+ std::make_unique<Contact>(std::move(transient_contact)));
+
+ auto transient_phone_number = std::make_unique<autofill::AutofillProfile>();
+ transient_phone_number->SetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER,
+ u"+16505678910");
+ user_data_.transient_phone_numbers_.emplace_back(
+ std::make_unique<PhoneNumber>(std::move(transient_phone_number)));
+
+ GetUserDataResponseProto user_data_response;
+ user_data_response.set_locale("en-US");
+ auto* profile = user_data_response.add_available_contacts();
+ (*profile->mutable_values())[7] = MakeAutofillEntry("John Doe");
+ *user_data_response.add_available_phone_numbers()->mutable_value() =
+ MakeAutofillEntry("+1 187-654-3210");
+
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
+ .WillByDefault(Return(nullptr));
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ EXPECT_CALL(mock_action_delegate_, RequestUserData)
+ .Times(2)
+ .WillRepeatedly(RunOnceCallback<2>(true, user_data_response));
+ EXPECT_CALL(mock_action_delegate_, CollectUserData(_))
+ .WillOnce(Invoke([=](CollectUserDataOptions* collect_user_data_options) {
+ ASSERT_EQ(user_data_.available_contacts_.size(), 2u);
+ EXPECT_EQ(user_data_.available_contacts_[0]->profile->GetRawInfo(
+ autofill::NAME_FULL),
+ u"Jane Doe");
+ EXPECT_EQ(user_data_.available_contacts_[1]->profile->GetRawInfo(
+ autofill::NAME_FULL),
+ u"John Doe");
+
+ ASSERT_EQ(user_data_.available_phone_numbers_.size(), 2u);
+ EXPECT_EQ(user_data_.available_phone_numbers_[0]->profile->GetRawInfo(
+ autofill::PHONE_HOME_WHOLE_NUMBER),
+ u"+16505678910");
+ EXPECT_EQ(user_data_.available_phone_numbers_[1]->profile->GetRawInfo(
+ autofill::PHONE_HOME_WHOLE_NUMBER),
+ u"+1 187-654-3210");
+
+ std::move(collect_user_data_options->reload_data_callback)
+ .Run(UserDataEventField::NONE, &user_data_);
+ }))
+ .WillOnce(Invoke([=](CollectUserDataOptions* collect_user_data_options) {
+ EXPECT_EQ(user_data_.available_contacts_.size(), 2u);
+ EXPECT_EQ(user_data_.available_phone_numbers_.size(), 2u);
+
+ // Don't end the action.
+ }));
+
+ ActionProto action_proto;
+ auto* collect_user_data = action_proto.mutable_collect_user_data();
+ collect_user_data->set_request_terms_and_conditions(false);
+ collect_user_data->mutable_contact_details()->set_request_payer_name(true);
+ collect_user_data->mutable_contact_details()
+ ->set_separate_phone_number_section(true);
+ collect_user_data->mutable_contact_details()->set_phone_number_section_title(
+ "Phone number");
+ collect_user_data->mutable_contact_details()->set_contact_details_name(
+ kMemoryLocation);
+ collect_user_data->mutable_data_source();
+
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, FallBackToChromeDataOnFailedRequest) {
+ ON_CALL(mock_personal_data_manager_, IsAutofillProfileEnabled)
+ .WillByDefault(Return(true));
+
+ autofill::AutofillProfile profile;
+ autofill::test::SetProfileInfo(
+ &profile, "Adam", "", "West", "adam.west@gmail.com", "", "Main St. 18",
+ "", "abc", "New York", "NY", "10001", "us", "+1 123-456-7890");
+
+ ON_CALL(mock_personal_data_manager_, GetProfiles)
+ .WillByDefault(
+ Return(std::vector<autofill::AutofillProfile*>({&profile})));
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(false));
+ EXPECT_CALL(mock_action_delegate_, RequestUserData)
+ .WillOnce(RunOnceCallback<2>(false, GetUserDataResponseProto()));
+
+ ON_CALL(mock_action_delegate_, CollectUserData(_))
+ .WillByDefault([&](CollectUserDataOptions* collect_user_data_options) {
+ ExpectSelectedProfileMatches(kMemoryLocation, &profile);
+
+ EXPECT_TRUE(collect_user_data_options->request_payer_phone);
+ EXPECT_FALSE(
+ collect_user_data_options->request_phone_number_separately);
+ EXPECT_THAT(collect_user_data_options->contact_summary_fields,
+ ElementsAre(AutofillContactField::NAME_FULL,
+ AutofillContactField::PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_EQ(collect_user_data_options->contact_summary_max_lines, 2);
+ EXPECT_THAT(collect_user_data_options->contact_full_fields,
+ ElementsAre(AutofillContactField::NAME_FULL,
+ AutofillContactField::PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_EQ(collect_user_data_options->contact_full_max_lines, 2);
+ EXPECT_FALSE(collect_user_data_options->data_origin_notice.has_value());
+ EXPECT_TRUE(collect_user_data_options->should_store_data_changes);
+ EXPECT_FALSE(collect_user_data_options->use_alternative_edit_dialogs);
+
+ std::move(collect_user_data_options->confirm_callback)
+ .Run(&user_data_, nullptr);
+ });
+
+ ActionProto action_proto;
+ auto* collect_user_data = action_proto.mutable_collect_user_data();
+ collect_user_data->set_request_terms_and_conditions(false);
+ collect_user_data->mutable_contact_details()->set_request_payer_name(true);
+ collect_user_data->mutable_contact_details()->add_summary_fields(
+ ContactDetailsProto::NAME_FULL);
+ collect_user_data->mutable_contact_details()->set_max_number_summary_lines(1);
+ collect_user_data->mutable_contact_details()->add_full_fields(
+ ContactDetailsProto::NAME_FULL);
+ collect_user_data->mutable_contact_details()->set_max_number_full_lines(1);
+ *collect_user_data->mutable_contact_details()->add_required_data_piece() =
+ MakeRequiredDataPiece(autofill::ServerFieldType::NAME_FULL);
+ collect_user_data->mutable_contact_details()->set_contact_details_name(
+ kMemoryLocation);
+ collect_user_data->mutable_contact_details()
+ ->set_separate_phone_number_section(true);
+ collect_user_data->mutable_contact_details()->set_phone_number_section_title(
+ "Phone number");
+ *collect_user_data->mutable_contact_details()
+ ->add_phone_number_required_data_piece() =
+ MakeRequiredDataPiece(autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER);
+ collect_user_data->mutable_data_source()->set_allow_fallback(true);
+ collect_user_data->mutable_data_origin_notice()->set_link_text("Link");
+ collect_user_data->mutable_data_origin_notice()->set_dialog_title("Title");
+ collect_user_data->mutable_data_origin_notice()->set_dialog_text("Text");
+ collect_user_data->mutable_data_origin_notice()->set_dialog_button_text(
+ "Button");
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+
+ EXPECT_THAT(
+ GetUkmUserDataSource(ukm_recorder_),
+ ElementsAreArray({ToHumanReadableEntry(
+ source_id_, kUserDataSource,
+ static_cast<int64_t>(Metrics::UserDataSource::CHROME_AUTOFILL))}));
+}
+
+TEST_F(CollectUserDataActionTest, FailActionIfFallbackIsNotPossible) {
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
+ .WillByDefault(Return(nullptr));
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(true));
+ EXPECT_CALL(mock_action_delegate_, RequestUserData)
+ .WillOnce(RunOnceCallback<2>(false, GetUserDataResponseProto()));
+
+ ActionProto action_proto;
+ auto* collect_user_data = action_proto.mutable_collect_user_data();
+ collect_user_data->set_request_terms_and_conditions(false);
+ collect_user_data->mutable_contact_details()->set_request_payer_name(true);
+ collect_user_data->mutable_contact_details()->set_contact_details_name(
+ kMemoryLocation);
+ collect_user_data->mutable_data_source()->set_allow_fallback(true);
+
+ EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
+ USER_DATA_REQUEST_FAILED))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, FailActionIfReloadFails) {
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
+ .WillByDefault(Return(nullptr));
+ ON_CALL(mock_action_delegate_, MustUseBackendData)
+ .WillByDefault(Return(false));
+ EXPECT_CALL(mock_action_delegate_, RequestUserData)
+ .WillOnce(RunOnceCallback<2>(true, GetUserDataResponseProto()))
+ .WillOnce(RunOnceCallback<2>(false, GetUserDataResponseProto()));
+ EXPECT_CALL(mock_action_delegate_, CollectUserData(_))
+ .WillOnce([&](CollectUserDataOptions* collect_user_data_options) {
+ std::move(collect_user_data_options->reload_data_callback)
+ .Run(UserDataEventField::NONE, &user_data_);
+ });
+
+ ActionProto action_proto;
+ auto* collect_user_data = action_proto.mutable_collect_user_data();
+ collect_user_data->set_request_terms_and_conditions(false);
+ collect_user_data->mutable_contact_details()->set_request_payer_name(true);
+ collect_user_data->mutable_contact_details()->set_contact_details_name(
+ kMemoryLocation);
+ collect_user_data->mutable_data_source()->set_allow_fallback(true);
+
+ EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
+ USER_DATA_REQUEST_FAILED))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/execute_js_action.h b/chromium/components/autofill_assistant/browser/actions/execute_js_action.h
index dafb2957aef..0be489f1884 100644
--- a/chromium/components/autofill_assistant/browser/actions/execute_js_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/execute_js_action.h
@@ -11,7 +11,7 @@
#include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/dom_action.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/execute_js_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/execute_js_action_unittest.cc
index ed157496535..e96375f533c 100644
--- a/chromium/components/autofill_assistant/browser/actions/execute_js_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/execute_js_action_unittest.cc
@@ -12,7 +12,7 @@
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/dom_action.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/element_store.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/autofill_assistant/browser/actions/external_action.cc b/chromium/components/autofill_assistant/browser/actions/external_action.cc
new file mode 100644
index 00000000000..ec25401d244
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/external_action.cc
@@ -0,0 +1,201 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/actions/external_action.h"
+
+#include "base/logging.h"
+#include "components/autofill_assistant/browser/actions/action_delegate.h"
+#include "components/autofill_assistant/browser/client_status.h"
+
+namespace autofill_assistant {
+
+ExternalAction::ExternalAction(ActionDelegate* delegate,
+ const ActionProto& proto)
+ : Action(delegate, proto) {
+ DCHECK(proto_.has_external_action());
+}
+
+ExternalAction::~ExternalAction() = default;
+
+void ExternalAction::InternalProcessAction(ProcessActionCallback callback) {
+ callback_ = std::move(callback);
+ if (!delegate_->SupportsExternalActions()) {
+ VLOG(1) << "External action are not supported for this run.";
+ EndAction(ClientStatus(INVALID_ACTION));
+ return;
+ }
+ if (!proto_.external_action().has_info()) {
+ VLOG(1) << "The ExternalAction's |info| is missing.";
+ EndAction(ClientStatus(INVALID_ACTION));
+ return;
+ }
+
+ delegate_->RequestExternalAction(
+ proto_.external_action(),
+ base::BindOnce(&ExternalAction::StartDomChecks,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::BindOnce(&ExternalAction::OnExternalActionFinished,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ // Do not add any code here. External delegates may choose to end the action
+ // immediately, which could result in *this being deleted and UaF errors for
+ // code after the above call.
+}
+
+void ExternalAction::StartDomChecks(
+ ExternalActionDelegate::DomUpdateCallback dom_update_callback) {
+ const auto& external_action = proto_.external_action();
+ if (!external_action.conditions().empty() ||
+ external_action.allow_interrupt()) {
+ // We keep track of the fact that we have an active WaitForDom to make sure
+ // we end it gracefully.
+ has_pending_wait_for_dom_ = true;
+ dom_update_callback_ = std::move(dom_update_callback);
+ SetupConditions();
+ // TODO(b/201964908): fix time tracking.
+ delegate_->WaitForDom(
+ /* max_wait_time= */ base::TimeDelta::Max(),
+ /* allow_observer_mode = */ false, external_action.allow_interrupt(),
+ /* observer= */ nullptr,
+ base::BindRepeating(&ExternalAction::RegisterChecks,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::BindOnce(&ExternalAction::OnWaitForElementTimed,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::BindOnce(&ExternalAction::OnDoneWaitForDom,
+ weak_ptr_factory_.GetWeakPtr())));
+ }
+}
+
+void ExternalAction::SetupConditions() {
+ for (const auto& condition_proto : proto_.external_action().conditions()) {
+ ConditionStatus condition_status;
+ condition_status.proto = condition_proto;
+ conditions_.emplace_back(condition_status);
+ }
+}
+
+void ExternalAction::RegisterChecks(
+ BatchElementChecker* checker,
+ base::OnceCallback<void(const ClientStatus&)> wait_for_dom_callback) {
+ if (!callback_) {
+ // Action is done; checks aren't necessary anymore.
+ std::move(wait_for_dom_callback).Run(OkClientStatus());
+ return;
+ }
+
+ for (size_t i = 0; i < conditions_.size(); i++) {
+ checker->AddElementConditionCheck(
+ conditions_[i].proto.element_condition(),
+ base::BindOnce(&ExternalAction::OnPreconditionResult,
+ weak_ptr_factory_.GetWeakPtr(), i));
+ }
+
+ checker->AddAllDoneCallback(base::BindOnce(
+ &ExternalAction::OnElementChecksDone, weak_ptr_factory_.GetWeakPtr(),
+ std::move(wait_for_dom_callback)));
+}
+
+void ExternalAction::OnPreconditionResult(
+ size_t condition_index,
+ const ClientStatus& status,
+ const std::vector<std::string>& ignored_payloads,
+ const std::vector<std::string>& ignored_tags,
+ const base::flat_map<std::string, DomObjectFrameStack>& ignored_elements) {
+ DCHECK_LT(condition_index, conditions_.size());
+ bool precondition_is_met = status.ok();
+
+ // If this is the first time we perform the check, we consider the
+ // precondition as 'changed' since we always want to send the notification
+ // after the first check.
+ if (first_condition_notification_sent_ &&
+ conditions_[condition_index].result == precondition_is_met) {
+ return;
+ }
+
+ conditions_[condition_index].result = precondition_is_met;
+ conditions_[condition_index].changed = true;
+}
+
+void ExternalAction::OnElementChecksDone(
+ base::OnceCallback<void(const ClientStatus&)> wait_for_dom_callback) {
+ // If it was decided to end the action in the meantime, we send an OK status
+ // to WaitForDom so that it can end gracefully. Note that it is possible for
+ // WaitForDom to decide not to call OnDoneWaitForDom, if an interrupt triggers
+ // at the same time.
+ if (external_action_end_requested_) {
+ std::move(wait_for_dom_callback).Run(OkClientStatus());
+ return;
+ }
+
+ external::ElementConditionsUpdate update_proto;
+ for (auto& condition : conditions_) {
+ if (!condition.changed)
+ continue;
+
+ condition.changed = false;
+ external::ElementConditionsUpdate::ConditionResult result;
+ result.set_id(condition.proto.id());
+ result.set_satisfied(condition.result);
+ *update_proto.add_results() = result;
+ }
+
+ // We only send the notification if there were any changes since the last
+ // check.
+ if (!update_proto.results().empty()) {
+ first_condition_notification_sent_ = true;
+ dom_update_callback_.Run(update_proto);
+ }
+
+ // Whether we had satisfied element conditions or not, we run this callback
+ // with |ELEMENT_RESOLUTION_FAILED| to let the WaitForDom know that we want to
+ // keep running checks.
+ std::move(wait_for_dom_callback).Run(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+}
+
+void ExternalAction::OnDoneWaitForDom(const ClientStatus& status) {
+ if (!callback_) {
+ return;
+ }
+
+ // If |status| is OK we send the external response. Otherwise we ignore the
+ // external response and report the error, as we might not be in a state to
+ // continue with the script.
+ if (status.ok() && external_action_end_requested_) {
+ EndWithExternalResult();
+ return;
+ }
+ EndAction(status);
+}
+
+void ExternalAction::OnExternalActionFinished(const external::Result& result) {
+ if (!callback_) {
+ return;
+ }
+
+ external_action_result_ = result;
+
+ // If there is an ongoing WaitForDom, we end the action on the next WaitForDom
+ // notification to make sure we end gracefully.
+ if (has_pending_wait_for_dom_) {
+ external_action_end_requested_ = true;
+ return;
+ }
+
+ EndWithExternalResult();
+}
+
+void ExternalAction::EndWithExternalResult() {
+ *processed_action_proto_->mutable_external_action_result()
+ ->mutable_result_info() = external_action_result_.result_info();
+ EndAction(external_action_result_.success()
+ ? ClientStatus(ACTION_APPLIED)
+ : ClientStatus(UNKNOWN_ACTION_STATUS));
+}
+
+void ExternalAction::EndAction(const ClientStatus& status) {
+ UpdateProcessedAction(status);
+ std::move(callback_).Run(std::move(processed_action_proto_));
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/external_action.h b/chromium/components/autofill_assistant/browser/actions/external_action.h
new file mode 100644
index 00000000000..aaacd8d310d
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/external_action.h
@@ -0,0 +1,80 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_EXTERNAL_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_EXTERNAL_ACTION_H_
+
+#include "components/autofill_assistant/browser/actions/action.h"
+#include "components/autofill_assistant/browser/batch_element_checker.h"
+#include "components/autofill_assistant/browser/public/external_action_delegate.h"
+#include "components/autofill_assistant/browser/public/external_script_controller.h"
+#include "components/autofill_assistant/browser/wait_for_dom_observer.h"
+
+namespace autofill_assistant {
+
+class ExternalAction : public Action {
+ public:
+ explicit ExternalAction(ActionDelegate* delegate, const ActionProto& proto);
+
+ ExternalAction(const ExternalAction&) = delete;
+ ExternalAction& operator=(const ExternalAction&) = delete;
+
+ ~ExternalAction() override;
+
+ private:
+ struct ConditionStatus {
+ ExternalActionProto::ExternalCondition proto;
+
+ // We always update the result and send the notification for each condition
+ // after the first check so the initial value of these does not matter.
+ bool result = false;
+ bool changed = false;
+ };
+
+ // Overrides Action:
+ void InternalProcessAction(ProcessActionCallback callback) override;
+
+ void StartDomChecks(
+ ExternalActionDelegate::DomUpdateCallback dom_update_callback);
+ void SetupConditions();
+ void OnPreconditionResult(
+ size_t condition_index,
+ const ClientStatus& status,
+ const std::vector<std::string>& ignored_payloads,
+ const std::vector<std::string>& ignored_tags,
+ const base::flat_map<std::string, DomObjectFrameStack>& ignored_elements);
+ void RegisterChecks(
+ BatchElementChecker* checker,
+ base::OnceCallback<void(const ClientStatus&)> wait_for_dom_callback);
+ void OnElementChecksDone(
+ base::OnceCallback<void(const ClientStatus&)> wait_for_dom_callback);
+ void OnDoneWaitForDom(const ClientStatus& status);
+ void OnExternalActionFinished(const external::Result& success);
+ void EndWithExternalResult();
+ void EndAction(const ClientStatus& status);
+
+ ProcessActionCallback callback_;
+
+ // The list of conditions to be checked.
+ std::vector<ConditionStatus> conditions_;
+ // Keeps track of whether we have already sent the first notification about
+ // the conditions.
+ bool first_condition_notification_sent_ = false;
+ // Whether there is a currently running WaitForDom.
+ bool has_pending_wait_for_dom_ = false;
+ // The callback to notify element condition updates.
+ ExternalActionDelegate::DomUpdateCallback dom_update_callback_;
+
+ // Whether we received a notification from the external caller to end the
+ // action.
+ bool external_action_end_requested_ = false;
+ // The external result reported when the external caller requested to end the
+ // action.
+ external::Result external_action_result_;
+
+ base::WeakPtrFactory<ExternalAction> weak_ptr_factory_{this};
+};
+
+} // namespace autofill_assistant
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_EXTERNAL_ACTION_H_
diff --git a/chromium/components/autofill_assistant/browser/actions/external_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/external_action_unittest.cc
new file mode 100644
index 00000000000..ac8be3fd66b
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/external_action_unittest.cc
@@ -0,0 +1,359 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/actions/external_action.h"
+
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "components/autofill_assistant/browser/actions/wait_for_dom_test_base.h"
+#include "components/autofill_assistant/browser/external_action_extension_test.pb.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/web/mock_web_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Pointee;
+using ::testing::Property;
+using ::testing::Return;
+using ::testing::UnorderedElementsAre;
+using ::testing::WithArgs;
+
+class ExternalActionTest : public WaitForDomTestBase {
+ public:
+ ExternalActionTest() = default;
+
+ protected:
+ void Run() {
+ ON_CALL(mock_action_delegate_, SupportsExternalActions)
+ .WillByDefault(Return(true));
+
+ ActionProto action_proto;
+ *action_proto.mutable_external_action() = proto_;
+ action_ =
+ std::make_unique<ExternalAction>(&mock_action_delegate_, action_proto);
+ action_->ProcessAction(callback_.Get());
+ }
+
+ base::MockCallback<Action::ProcessActionCallback> callback_;
+ ExternalActionProto proto_;
+ std::unique_ptr<ExternalAction> action_;
+};
+
+external::Result MakeResult(bool success) {
+ external::Result result;
+ result.set_success(success);
+ testing::TestResultExtension test_extension_proto;
+ test_extension_proto.set_text("test text");
+
+ *result.mutable_result_info()->MutableExtension(
+ testing::test_result_extension) = std::move(test_extension_proto);
+ return result;
+}
+
+TEST_F(ExternalActionTest, Success) {
+ proto_.mutable_info();
+
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+ .WillOnce(RunOnceCallback<2>(MakeResult(/* success= */ true)));
+
+ std::unique_ptr<ProcessedActionProto> returned_processed_action_proto;
+ EXPECT_CALL(callback_, Run)
+ .WillOnce(
+ [&returned_processed_action_proto](
+ std::unique_ptr<ProcessedActionProto> processed_action_proto) {
+ returned_processed_action_proto = std::move(processed_action_proto);
+ });
+ Run();
+ EXPECT_THAT(returned_processed_action_proto->status(), Eq(ACTION_APPLIED));
+ EXPECT_TRUE(returned_processed_action_proto->has_external_action_result());
+ EXPECT_THAT(returned_processed_action_proto->external_action_result()
+ .result_info()
+ .GetExtension(testing::test_result_extension)
+ .text(),
+ Eq("test text"));
+}
+
+TEST_F(ExternalActionTest, ExternalFailure) {
+ proto_.mutable_info();
+
+ std::unique_ptr<ProcessedActionProto> returned_processed_action_proto;
+ EXPECT_CALL(callback_, Run)
+ .WillOnce(
+ [&returned_processed_action_proto](
+ std::unique_ptr<ProcessedActionProto> processed_action_proto) {
+ returned_processed_action_proto = std::move(processed_action_proto);
+ });
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+ .WillOnce(RunOnceCallback<2>(MakeResult(/* success= */ false)));
+ Run();
+ EXPECT_THAT(returned_processed_action_proto->status(),
+ Eq(UNKNOWN_ACTION_STATUS));
+ EXPECT_TRUE(returned_processed_action_proto->has_external_action_result());
+ EXPECT_THAT(returned_processed_action_proto->external_action_result()
+ .result_info()
+ .GetExtension(testing::test_result_extension)
+ .text(),
+ Eq("test text"));
+}
+
+TEST_F(ExternalActionTest, FailsIfProtoExtensionInfoNotSet) {
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction).Times(0);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ Run();
+}
+
+TEST_F(ExternalActionTest, FailsIfDelegateDoesNotSupportExternalActions) {
+ proto_.mutable_info();
+ EXPECT_CALL(mock_action_delegate_, SupportsExternalActions())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction).Times(0);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ Run();
+}
+
+TEST_F(ExternalActionTest, ExternalActionWithInterrupts) {
+ proto_.mutable_info();
+ proto_.set_allow_interrupt(true);
+
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+ .WillOnce(
+ [](const ExternalActionProto& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ std::move(start_dom_checks_callback).Run(base::DoNothing());
+ std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+ });
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+ Run();
+ // The action should end at the next WaitForDom notification.
+ task_env_.FastForwardBy(base::Seconds(1));
+}
+
+TEST_F(ExternalActionTest, ExternalActionWithoutInterrupts) {
+ proto_.mutable_info();
+ proto_.set_allow_interrupt(false);
+
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+ .WillOnce(
+ [](const ExternalActionProto& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ std::move(start_dom_checks_callback).Run(base::DoNothing());
+ std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+ });
+ EXPECT_CALL(mock_action_delegate_, WaitForDom).Times(0);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+ Run();
+}
+
+TEST_F(ExternalActionTest, DoesNotStartWaitForDomIfDomChecksAreNotRequested) {
+ proto_.mutable_info();
+ proto_.set_allow_interrupt(true);
+
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+ .WillOnce(
+ [](const ExternalActionProto& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ // We call the |end_action_callback| without calling
+ // |start_dom_checks_callback|.
+ std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+ });
+ EXPECT_CALL(mock_action_delegate_, WaitForDom).Times(0);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+ Run();
+}
+
+TEST_F(ExternalActionTest, ExternalActionWithDomChecks) {
+ proto_.mutable_info();
+ ExternalActionProto::ExternalCondition condition;
+ condition.set_id(55);
+ *condition.mutable_element_condition()->mutable_match() =
+ ToSelectorProto("element");
+ *proto_.add_conditions() = condition;
+
+ base::MockCallback<ExternalActionDelegate::DomUpdateCallback>
+ dom_update_callback;
+
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+ .WillOnce([&dom_update_callback](
+ const ExternalActionProto& external_action,
+ base::OnceCallback<void(
+ ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ std::move(start_dom_checks_callback).Run(dom_update_callback.Get());
+ std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+ });
+
+ EXPECT_CALL(
+ dom_update_callback,
+ Run(Property(
+ &external::ElementConditionsUpdate::results,
+ ElementsAre(AllOf(
+ Property(&external::ElementConditionsUpdate::ConditionResult::id,
+ 55),
+ Property(&external::ElementConditionsUpdate::ConditionResult::
+ satisfied,
+ false))))));
+ Run();
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+ // The action should end at the next WaitForDom notification.
+ task_env_.FastForwardBy(base::Seconds(1));
+}
+
+TEST_F(ExternalActionTest, DomChecksOnlyUpdateOnChange) {
+ proto_.mutable_info();
+ ExternalActionProto::ExternalCondition changing_condition;
+ changing_condition.set_id(55);
+ *changing_condition.mutable_element_condition()->mutable_match() =
+ ToSelectorProto("changing_condition");
+ ExternalActionProto::ExternalCondition unchanging_condition;
+ unchanging_condition.set_id(9);
+ *unchanging_condition.mutable_element_condition()->mutable_match() =
+ ToSelectorProto("unchanging_condition");
+ *proto_.add_conditions() = changing_condition;
+ *proto_.add_conditions() = unchanging_condition;
+
+ base::MockCallback<ExternalActionDelegate::DomUpdateCallback>
+ dom_update_callback;
+
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+ .WillOnce([&dom_update_callback](
+ const ExternalActionProto& external_action,
+ base::OnceCallback<void(
+ ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ std::move(start_dom_checks_callback).Run(dom_update_callback.Get());
+ });
+
+ // For the first rounds of checks, all elements should be in the notification.
+ // Note that the |mock_web_controller_| reports an element as missing by
+ // default in the fixture.
+ EXPECT_CALL(
+ dom_update_callback,
+ Run(Property(
+ &external::ElementConditionsUpdate::results,
+ UnorderedElementsAre(
+ AllOf(Property(
+ &external::ElementConditionsUpdate::ConditionResult::id,
+ 55),
+ Property(&external::ElementConditionsUpdate::
+ ConditionResult::satisfied,
+ false)),
+ AllOf(Property(
+ &external::ElementConditionsUpdate::ConditionResult::id,
+ 9),
+ Property(&external::ElementConditionsUpdate::
+ ConditionResult::satisfied,
+ false))))));
+
+ Run();
+
+ // For the second rounds of checks, we simulate the |changing_condition|
+ // changing to being satisfied and |unchanging_condition| remaining
+ // unsatisfied.
+ EXPECT_CALL(mock_web_controller_,
+ FindElement(Selector({"changing_condition"}), _, _))
+ .WillOnce(WithArgs<2>([](auto&& callback) {
+ std::move(callback).Run(OkClientStatus(),
+ std::make_unique<ElementFinderResult>());
+ }));
+ EXPECT_CALL(mock_web_controller_,
+ FindElement(Selector({"unchanging_condition"}), _, _))
+ .WillOnce(WithArgs<2>([](auto&& callback) {
+ std::move(callback).Run(ClientStatus(ELEMENT_RESOLUTION_FAILED),
+ std::make_unique<ElementFinderResult>());
+ }));
+
+ // The notification should now only contain an entry for |changed_condition|.
+ EXPECT_CALL(
+ dom_update_callback,
+ Run(Property(
+ &external::ElementConditionsUpdate::results,
+ UnorderedElementsAre(AllOf(
+ Property(&external::ElementConditionsUpdate::ConditionResult::id,
+ 55),
+ Property(&external::ElementConditionsUpdate::ConditionResult::
+ satisfied,
+ true))))));
+ task_env_.FastForwardBy(base::Seconds(1));
+
+ // We keep the same state as the last roundtrip.
+ EXPECT_CALL(mock_web_controller_,
+ FindElement(Selector({"changing_condition"}), _, _))
+ .WillOnce(WithArgs<2>([](auto&& callback) {
+ std::move(callback).Run(OkClientStatus(),
+ std::make_unique<ElementFinderResult>());
+ }));
+ EXPECT_CALL(mock_web_controller_,
+ FindElement(Selector({"unchanging_condition"}), _, _))
+ .WillOnce(WithArgs<2>([](auto&& callback) {
+ std::move(callback).Run(ClientStatus(ELEMENT_RESOLUTION_FAILED),
+ std::make_unique<ElementFinderResult>());
+ }));
+ // Since there were no changes, no notification is sent.
+ EXPECT_CALL(dom_update_callback, Run(_)).Times(0);
+ task_env_.FastForwardBy(base::Seconds(1));
+}
+
+TEST_F(ExternalActionTest, WaitForDomFailure) {
+ proto_.mutable_info();
+ proto_.set_allow_interrupt(true);
+
+ EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+ .WillOnce(
+ [](const ExternalActionProto& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ std::move(start_dom_checks_callback).Run(base::DoNothing());
+ std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+ });
+
+ // Even if the external action ended in a success, if the WaitForDom ends in
+ // an error we expect the error to be reported.
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INTERRUPT_FAILED))));
+ Run();
+ wait_for_dom_status_ = ClientStatus(INTERRUPT_FAILED);
+ // The action should end at the next WaitForDom notification.
+ task_env_.FastForwardBy(base::Seconds(1));
+}
+
+} // namespace
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc b/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc
index f2d1a438e00..a489f10de90 100644
--- a/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc
+++ b/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc
@@ -17,7 +17,7 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/field_formatter.h"
#include "components/autofill_assistant/browser/web/element_action_util.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
#include "third_party/re2/src/re2/re2.h"
diff --git a/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.h b/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.h
index a109f926868..b9cd534da4e 100644
--- a/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.h
+++ b/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.h
@@ -18,10 +18,10 @@
#include "components/autofill_assistant/browser/actions/fallback_handler/required_field.h"
#include "components/autofill_assistant/browser/batch_element_checker.h"
#include "components/autofill_assistant/browser/field_formatter.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
class ClientStatus;
+class ElementFinderResult;
// A handler for required fields and fallback values, used by UseAddressAction
// and UseCreditCardAction.
diff --git a/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc b/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc
index 755ab627521..67174c2b648 100644
--- a/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc
@@ -18,7 +18,7 @@
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/autofill_assistant/browser/actions/get_element_status_action.cc b/chromium/components/autofill_assistant/browser/actions/get_element_status_action.cc
index 15d076fd1e6..408c02df978 100644
--- a/chromium/components/autofill_assistant/browser/actions/get_element_status_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/get_element_status_action.cc
@@ -13,6 +13,7 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/user_data_util.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/element_store.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
#include "third_party/re2/src/re2/re2.h"
@@ -27,7 +28,7 @@ struct MaybeRe2 {
std::string RemoveWhitespace(const std::string& value) {
std::string copy = value;
- base::EraseIf(copy, base::IsUnicodeWhitespace);
+ base::EraseIf(copy, base::IsUnicodeWhitespace<char>);
return copy;
}
diff --git a/chromium/components/autofill_assistant/browser/actions/get_element_status_action.h b/chromium/components/autofill_assistant/browser/actions/get_element_status_action.h
index c5dfa40f316..422cdc2f910 100644
--- a/chromium/components/autofill_assistant/browser/actions/get_element_status_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/get_element_status_action.h
@@ -11,9 +11,9 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/dom_action.pb.h"
#include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
+class ElementFinderResult;
// Action to get an element's status.
class GetElementStatusAction : public Action {
diff --git a/chromium/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
index 66a282680bd..4fdd67b0dce 100644
--- a/chromium/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
@@ -18,6 +18,7 @@
#include "components/autofill_assistant/browser/selector.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/user_model.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/element_store.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "content/public/test/browser_task_environment.h"
@@ -704,7 +705,7 @@ TEST_F(GetElementStatusActionTest, SucceedsWithPasswordManagerValue) {
.WillOnce(WithArgs<1>([this](auto&& callback) {
std::unique_ptr<ElementFinderResult> element =
std::make_unique<ElementFinderResult>();
- element->SetRenderFrameHost(web_contents_->GetMainFrame());
+ element->SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
std::move(callback).Run(OkClientStatus(), std::move(element));
}));
EXPECT_CALL(mock_website_login_manager_, GetPasswordForLogin(_, _))
diff --git a/chromium/components/autofill_assistant/browser/actions/js_flow_action.cc b/chromium/components/autofill_assistant/browser/actions/js_flow_action.cc
index 5eb7e1b7f61..5f0825f5c7e 100644
--- a/chromium/components/autofill_assistant/browser/actions/js_flow_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/js_flow_action.cc
@@ -26,9 +26,9 @@ const char kJsFlowActionEnabledGroup[] = "Enabled";
JsFlowAction::JsFlowAction(ActionDelegate* delegate, const ActionProto& proto)
: Action(delegate, proto),
- js_flow_executor_(
- std::make_unique<JsFlowExecutorImpl>(delegate->GetWebContents(),
- this)) {
+ js_flow_executor_(std::make_unique<JsFlowExecutorImpl>(
+ /* delegate= */ this,
+ delegate->GetJsFlowDevtoolsWrapper())) {
DCHECK(proto_.has_js_flow());
}
@@ -90,6 +90,8 @@ void JsFlowAction::OnNativeActionFinished(
current_native_action_.reset();
+ delegate_->MaybeSetPreviousAction(*processed_action);
+
std::move(finished_callback)
.Run(ClientStatus(processed_action->status(),
processed_action->status_details()),
@@ -109,6 +111,11 @@ void JsFlowAction::InternalProcessAction(ProcessActionCallback callback) {
void JsFlowAction::OnFlowFinished(ProcessActionCallback callback,
const ClientStatus& status,
std::unique_ptr<base::Value> return_value) {
+ // Since we can not know in advance how many native actions need to be run
+ // we will create a dangling promise. By destroying the flow executor we make
+ // sure that these will not be executed.
+ js_flow_executor_.reset(nullptr);
+
UpdateProcessedAction(status);
// If the flow returned a value, we extract the status and possibly a flow
diff --git a/chromium/components/autofill_assistant/browser/actions/js_flow_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/js_flow_action_unittest.cc
index a39302e577f..0811cf9e226 100644
--- a/chromium/components/autofill_assistant/browser/actions/js_flow_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/js_flow_action_unittest.cc
@@ -363,4 +363,48 @@ TEST_F(JsFlowActionTest, NativeActionReturnsActionResult) {
action->ProcessAction(callback_.Get());
}
+TEST_F(JsFlowActionTest, NativeActionReturnsAutofillErrorInfo) {
+ auto mock_js_flow_executor = std::make_unique<MockJsFlowExecutor>();
+ auto* mock_js_flow_executor_ptr = mock_js_flow_executor.get();
+ auto action = CreateAction(std::move(mock_js_flow_executor));
+
+ EXPECT_CALL(*mock_js_flow_executor_ptr, Start)
+ .WillOnce(WithArg<1>([&](auto finished_callback) {
+ ActionProto native_action;
+ native_action.mutable_wait_for_dom()->mutable_wait_condition();
+
+ action->RunNativeAction(
+ /* action_id = */ static_cast<int>(
+ native_action.action_info_case()),
+ /* action = */
+ native_action.wait_for_dom().SerializeAsString(),
+ native_action_callback_.Get());
+
+ std::move(finished_callback).Run(ClientStatus(ACTION_APPLIED), nullptr);
+ }));
+
+ AutofillErrorInfoProto autofill_error_info;
+ autofill_error_info.set_client_memory_address_key_names("key_names");
+ std::string autofill_error_info_base64;
+ base::Base64Encode(autofill_error_info.SerializeAsString(),
+ &autofill_error_info_base64);
+
+ EXPECT_CALL(mock_action_delegate_, WaitForDomWithSlowWarning)
+ .WillOnce(WithArg<4>([&](auto dom_finished_callback) {
+ *GetInnerProcessedAction(*action)
+ ->mutable_status_details()
+ ->mutable_autofill_error_info() = autofill_error_info;
+ std::move(dom_finished_callback)
+ .Run(ClientStatus(ACTION_APPLIED), base::Seconds(0));
+ }));
+
+ EXPECT_CALL(native_action_callback_, Run(_, Pointee(IsJson(R"(
+ {
+ "navigationStarted": false,
+ "autofillErrorInfo": ")" + autofill_error_info_base64 +
+ "\"}"))));
+
+ action->ProcessAction(callback_.Get());
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h b/chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h
index a411caa93e5..1089b688aa8 100644
--- a/chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -31,6 +31,7 @@ class PasswordChangeSuccessTracker;
}
namespace autofill_assistant {
+class ElementFinderResult;
class UserModel;
class MockActionDelegate : public ActionDelegate {
@@ -95,7 +96,7 @@ class MockActionDelegate : public ActionDelegate {
base::OnceCallback<void()> end_on_navigation_callback,
bool browse_mode,
bool browse_mode_invisible));
- MOCK_METHOD0(CleanUpAfterPrompt, void());
+ MOCK_METHOD1(CleanUpAfterPrompt, void(bool));
MOCK_METHOD1(SetBrowseDomainsAllowlist,
void(std::vector<std::string> domains));
MOCK_METHOD2(
@@ -134,6 +135,10 @@ class MockActionDelegate : public ActionDelegate {
MOCK_CONST_METHOD0(GetPasswordChangeSuccessTracker,
password_manager::PasswordChangeSuccessTracker*());
MOCK_CONST_METHOD0(GetWebContents, content::WebContents*());
+ MOCK_METHOD(JsFlowDevtoolsWrapper*,
+ GetJsFlowDevtoolsWrapper,
+ (),
+ (const override));
MOCK_CONST_METHOD0(GetWebController, WebController*());
MOCK_CONST_METHOD0(GetEmailAddressForAccessTokenAccount, std::string());
MOCK_CONST_METHOD0(GetUkmRecorder, ukm::UkmRecorder*());
@@ -207,11 +212,23 @@ class MockActionDelegate : public ActionDelegate {
MOCK_METHOD0(MaybeShowSlowConnectionWarning, void());
MOCK_METHOD0(GetLogInfo, ProcessedActionStatusDetailsProto&());
MOCK_CONST_METHOD0(GetElementStore, ElementStore*());
- MOCK_METHOD2(
+ MOCK_METHOD3(
RequestUserData,
- void(const CollectUserDataOptions& options,
+ void(UserDataEventField event_field,
+ const CollectUserDataOptions& options,
base::OnceCallback<void(bool, const GetUserDataResponseProto&)>
callback));
+ MOCK_METHOD0(SupportsExternalActions, bool());
+ MOCK_METHOD3(
+ RequestExternalAction,
+ void(const ExternalActionProto& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback));
+ MOCK_CONST_METHOD0(MustUseBackendData, bool());
+ MOCK_METHOD1(MaybeSetPreviousAction,
+ void(const ProcessedActionProto& processed_action));
base::WeakPtr<ActionDelegate> GetWeakPtr() const override {
return weak_ptr_factory_.GetWeakPtr();
diff --git a/chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action.h b/chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action.h
index bd96382a777..9ddc810069b 100644
--- a/chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action.h
@@ -11,7 +11,7 @@
#include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/dom_action.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action_unittest.cc
index 6d843548a0a..2b985b6b378 100644
--- a/chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/perform_on_single_element_action_unittest.cc
@@ -11,7 +11,7 @@
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/dom_action.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/element_store.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
index cb6b74c59aa..9a7869f9a5b 100644
--- a/chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
@@ -16,7 +16,7 @@
#include "base/test/test_simple_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
-#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "components/autofill_assistant/browser/actions/wait_for_dom_test_base.h"
#include "components/autofill_assistant/browser/wait_for_dom_observer.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -27,6 +27,7 @@ namespace {
using ::base::test::RunOnceCallback;
using ::testing::_;
using ::testing::ElementsAre;
+using ::testing::Eq;
using ::testing::Invoke;
using ::testing::IsEmpty;
using ::testing::IsNull;
@@ -38,20 +39,12 @@ using ::testing::StrEq;
using ::testing::UnorderedElementsAre;
using ::testing::WithArgs;
-class PromptActionTest : public testing::Test {
+class PromptActionTest : public WaitForDomTestBase {
public:
- PromptActionTest()
- : task_env_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+ PromptActionTest() = default;
void SetUp() override {
- ON_CALL(mock_web_controller_, FindElement(_, _, _))
- .WillByDefault(WithArgs<2>([](auto&& callback) {
- std::move(callback).Run(ClientStatus(ELEMENT_RESOLUTION_FAILED),
- std::make_unique<ElementFinderResult>());
- }));
- EXPECT_CALL(mock_action_delegate_, WaitForDom)
- .WillRepeatedly(Invoke(this, &PromptActionTest::FakeWaitForDom));
- ON_CALL(mock_action_delegate_, Prompt(_, _, _, _, _))
+ ON_CALL(mock_action_delegate_, Prompt)
.WillByDefault(
[this](std::unique_ptr<std::vector<UserAction>> user_actions,
bool disable_force_expand_sheet,
@@ -63,92 +56,10 @@ class PromptActionTest : public testing::Test {
}
protected:
- // Fakes ActionDelegate::WaitForDom.
- //
- // This simulates a WaitForDom that calls |check_elements_| every seconds
- // until it gets a successful callback, then calls done_waiting_callback.
- void FakeWaitForDom(
- base::TimeDelta max_wait_time,
- bool allow_observer_mode,
- bool allow_interrupt,
- WaitForDomObserver* observer,
- base::RepeatingCallback<
- void(BatchElementChecker*,
- base::OnceCallback<void(const ClientStatus&)>)> check_elements,
- base::OnceCallback<void(const ClientStatus&, base::TimeDelta)>
- done_waiting_callback) {
- fake_wait_for_dom_done_ = std::move(done_waiting_callback);
- RunFakeWaitForDom(check_elements);
- }
-
- void RunFakeWaitForDom(
- base::RepeatingCallback<
- void(BatchElementChecker*,
- base::OnceCallback<void(const ClientStatus&)>)> check_elements) {
- if (!fake_wait_for_dom_done_)
- return;
-
- checker_ = std::make_unique<BatchElementChecker>();
- has_check_elements_result_ = false;
- check_elements.Run(checker_.get(),
- base::BindOnce(&PromptActionTest::OnCheckElementsDone,
- base::Unretained(this)));
- task_env_.FastForwardBy(base::Milliseconds(fake_check_time_));
- checker_->AddAllDoneCallback(
- base::BindOnce(&PromptActionTest::OnWaitForDomDone,
- base::Unretained(this), check_elements));
- checker_->Run(&mock_web_controller_);
- }
-
- // Called from the check_elements callback passed to FakeWaitForDom.
- void OnCheckElementsDone(const ClientStatus& result) {
- ASSERT_FALSE(has_check_elements_result_); // Duplicate calls
- has_check_elements_result_ = true;
- check_elements_result_ = result;
- }
-
- // Called by |checker_| once it's done and either ends the WaitForDom or
- // schedule another run.
- void OnWaitForDomDone(
- base::RepeatingCallback<
- void(BatchElementChecker*,
- base::OnceCallback<void(const ClientStatus&)>)> check_elements) {
- ASSERT_TRUE(
- has_check_elements_result_); // OnCheckElementsDone() not called
-
- if (!fake_wait_for_dom_done_)
- return;
-
- if (check_elements_result_.ok()) {
- std::move(fake_wait_for_dom_done_)
- .Run(check_elements_result_, base::Milliseconds(fake_wait_time_));
- } else {
- wait_for_dom_timer_ = std::make_unique<base::OneShotTimer>();
- wait_for_dom_timer_->Start(
- FROM_HERE, base::Seconds(1),
- base::BindOnce(&PromptActionTest::RunFakeWaitForDom,
- base::Unretained(this), check_elements));
- }
- }
-
- // task_env_ must be first to guarantee other field
- // creation run in that environment.
- base::test::TaskEnvironment task_env_;
-
- MockActionDelegate mock_action_delegate_;
- MockWebController mock_web_controller_;
base::MockCallback<Action::ProcessActionCallback> callback_;
- base::OnceCallback<void(const ClientStatus&, base::TimeDelta)>
- fake_wait_for_dom_done_;
ActionProto proto_;
raw_ptr<PromptProto> prompt_proto_;
std::unique_ptr<std::vector<UserAction>> user_actions_;
- std::unique_ptr<BatchElementChecker> checker_;
- bool has_check_elements_result_ = false;
- ClientStatus check_elements_result_;
- std::unique_ptr<base::OneShotTimer> wait_for_dom_timer_;
- int fake_wait_time_ = 0;
- int fake_check_time_ = 0;
};
TEST_F(PromptActionTest, ChoicesMissing) {
@@ -312,7 +223,7 @@ TEST_F(PromptActionTest, AutoSelectWhenElementExists) {
std::make_unique<ElementFinderResult>());
}));
- EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt());
+ EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt(Eq(true)));
EXPECT_CALL(
callback_,
Run(Pointee(AllOf(Property(&ProcessedActionProto::status, ACTION_APPLIED),
@@ -339,7 +250,7 @@ TEST_F(PromptActionTest, TimingStatsAutoSelect) {
std::make_unique<ElementFinderResult>());
}));
- EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt());
+ EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt(Eq(true)));
ProcessedActionProto capture;
EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&capture));
task_env_.FastForwardBy(base::Seconds(1));
@@ -523,7 +434,7 @@ TEST_F(PromptActionTest, EndActionOnNavigation) {
PromptAction action(&mock_action_delegate_, proto_);
// Set new expectations for when the navigation event arrives.
- EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt());
+ EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt(Eq(true)));
EXPECT_CALL(
callback_,
Run(Pointee(AllOf(
@@ -553,7 +464,7 @@ TEST_F(PromptActionTest, TimingStatsEndActionOnNavigation) {
PromptAction action(&mock_action_delegate_, proto_);
// Set new expectations for when the navigation event arrives.
- EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt());
+ EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt(Eq(true)));
ProcessedActionProto capture;
EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&capture));
action.ProcessAction(callback_.Get());
diff --git a/chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action.cc b/chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action.cc
new file mode 100644
index 00000000000..ef4d636edc3
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action.cc
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/actions/register_password_reset_request_action.h"
+
+#include "components/autofill_assistant/browser/actions/action_delegate.h"
+#include "components/autofill_assistant/browser/client_status.h"
+#include "components/autofill_assistant/browser/user_data.h"
+#include "components/password_manager/core/browser/password_change_success_tracker.h"
+
+namespace autofill_assistant {
+
+RegisterPasswordResetRequestAction::RegisterPasswordResetRequestAction(
+ ActionDelegate* delegate,
+ const ActionProto& proto)
+ : Action(delegate, proto) {
+ DCHECK(proto_.has_register_password_reset_request());
+}
+
+RegisterPasswordResetRequestAction::~RegisterPasswordResetRequestAction() =
+ default;
+
+void RegisterPasswordResetRequestAction::InternalProcessAction(
+ ProcessActionCallback callback) {
+ callback_ = std::move(callback);
+
+ if (!delegate_->GetUserData()->selected_login_) {
+ VLOG(1) << "RegisterPasswordResetRequestAction: requested login details "
+ "not available in client memory.";
+ EndAction(ClientStatus(PRECONDITION_FAILED));
+ return;
+ }
+
+ delegate_->GetPasswordChangeSuccessTracker()->OnChangePasswordFlowModified(
+ delegate_->GetUserData()->selected_login_->origin,
+ delegate_->GetUserData()->selected_login_->username,
+ password_manager::PasswordChangeSuccessTracker::StartEvent::
+ kManualResetLinkFlow);
+
+ EndAction(ClientStatus(ACTION_APPLIED));
+}
+
+void RegisterPasswordResetRequestAction::EndAction(const ClientStatus& status) {
+ UpdateProcessedAction(status);
+ std::move(callback_).Run(std::move(processed_action_proto_));
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action.h b/chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action.h
new file mode 100644
index 00000000000..5c94b0f2138
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action.h
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_REGISTER_PASSWORD_RESET_REQUEST_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_REGISTER_PASSWORD_RESET_REQUEST_ACTION_H_
+
+#include "components/autofill_assistant/browser/actions/action.h"
+
+namespace autofill_assistant {
+
+// Action to notify the password change success tracker that a password reset
+// has been requested.
+class RegisterPasswordResetRequestAction : public Action {
+ public:
+ explicit RegisterPasswordResetRequestAction(ActionDelegate* delegate,
+ const ActionProto& proto);
+ ~RegisterPasswordResetRequestAction() override;
+
+ RegisterPasswordResetRequestAction(
+ const RegisterPasswordResetRequestAction&) = delete;
+ RegisterPasswordResetRequestAction& operator=(
+ const RegisterPasswordResetRequestAction&) = delete;
+
+ private:
+ // Overrides Action:
+ void InternalProcessAction(ProcessActionCallback callback) override;
+
+ void EndAction(const ClientStatus& status);
+
+ ProcessActionCallback callback_;
+};
+
+} // namespace autofill_assistant
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_REGISTER_PASSWORD_RESET_REQUEST_ACTION_H_
diff --git a/chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action_unittest.cc
new file mode 100644
index 00000000000..e6ec84b9ee9
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/register_password_reset_request_action_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/actions/register_password_reset_request_action.h"
+
+#include <string>
+
+#include "base/test/mock_callback.h"
+#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "components/autofill_assistant/browser/client_status.h"
+#include "components/password_manager/core/browser/mock_password_change_success_tracker.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+namespace {
+
+using password_manager::MockPasswordChangeSuccessTracker;
+using password_manager::PasswordChangeSuccessTracker;
+using ::testing::Pointee;
+using ::testing::Property;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+const char kOrigin[] = "https://example.com";
+const char kUsername[] = "username";
+
+class RegisterPasswordResetRequestActionTest : public testing::Test {
+ public:
+ void SetUp() override {
+ ON_CALL(mock_action_delegate_, GetPasswordChangeSuccessTracker)
+ .WillByDefault(Return(&mock_password_change_success_tracker_));
+
+ ON_CALL(mock_action_delegate_, GetUserData)
+ .WillByDefault(Return(&user_data_));
+ }
+
+ protected:
+ void Run() {
+ proto_.mutable_register_password_reset_request();
+ RegisterPasswordResetRequestAction action(&mock_action_delegate_, proto_);
+ action.ProcessAction(callback_.Get());
+ }
+
+ ActionProto proto_;
+ MockActionDelegate mock_action_delegate_;
+ StrictMock<MockPasswordChangeSuccessTracker>
+ mock_password_change_success_tracker_;
+ base::MockCallback<Action::ProcessActionCallback> callback_;
+ UserData user_data_;
+};
+
+TEST_F(RegisterPasswordResetRequestActionTest, RegisterResetSuccess) {
+ user_data_.selected_login_.emplace(GURL(kOrigin), kUsername);
+
+ EXPECT_CALL(
+ mock_password_change_success_tracker_,
+ OnChangePasswordFlowModified(
+ GURL(kOrigin), kUsername,
+ PasswordChangeSuccessTracker::StartEvent::kManualResetLinkFlow));
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+
+ Run();
+}
+
+TEST_F(RegisterPasswordResetRequestActionTest, UserDataSelectLoginNotSet) {
+ // Leave user_data_.select_login_ empty.
+ EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
+ PRECONDITION_FAILED))));
+
+ Run();
+}
+
+} // namespace
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/save_generated_password_action.cc b/chromium/components/autofill_assistant/browser/actions/save_generated_password_action.cc
index 772bce77b0c..021f79571e2 100644
--- a/chromium/components/autofill_assistant/browser/actions/save_generated_password_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/save_generated_password_action.cc
@@ -61,7 +61,8 @@ void SaveGeneratedPasswordAction::InternalProcessAction(
delegate_->GetPasswordChangeSuccessTracker()->OnChangePasswordFlowCompleted(
delegate_->GetUserData()->selected_login_->origin,
delegate_->GetUserData()->selected_login_->username,
- PasswordChangeSuccessTracker::EndEvent::kAutomatedGeneratedPasswordFlow);
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen);
EndAction(ClientStatus(ACTION_APPLIED));
}
diff --git a/chromium/components/autofill_assistant/browser/actions/save_generated_password_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/save_generated_password_action_unittest.cc
index ef69b05f8a7..55157ef2481 100644
--- a/chromium/components/autofill_assistant/browser/actions/save_generated_password_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/save_generated_password_action_unittest.cc
@@ -82,7 +82,7 @@ TEST_F(SaveGeneratedPasswordActionTest, SavedPassword) {
mock_password_change_success_tracker_,
OnChangePasswordFlowCompleted(GURL(kOrigin), kUsername,
PasswordChangeSuccessTracker::EndEvent::
- kAutomatedGeneratedPasswordFlow));
+ kAutomatedFlowGeneratedPasswordChosen));
action.ProcessAction(callback_.Get());
diff --git a/chromium/components/autofill_assistant/browser/actions/save_submitted_password_action.cc b/chromium/components/autofill_assistant/browser/actions/save_submitted_password_action.cc
index 6d7b0654471..81381d7ed95 100644
--- a/chromium/components/autofill_assistant/browser/actions/save_submitted_password_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/save_submitted_password_action.cc
@@ -52,7 +52,8 @@ void SaveSubmittedPasswordAction::InternalProcessAction(
delegate_->GetPasswordChangeSuccessTracker()->OnChangePasswordFlowCompleted(
delegate_->GetUserData()->selected_login_->origin,
delegate_->GetUserData()->selected_login_->username,
- PasswordChangeSuccessTracker::EndEvent::kAutomatedOwnPasswordFlow);
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen);
}
// If a timeout is specified, perform a leak check.
diff --git a/chromium/components/autofill_assistant/browser/actions/save_submitted_password_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/save_submitted_password_action_unittest.cc
index edb13113d77..a1a7a481c59 100644
--- a/chromium/components/autofill_assistant/browser/actions/save_submitted_password_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/save_submitted_password_action_unittest.cc
@@ -71,9 +71,9 @@ TEST_F(SaveSubmittedPasswordActionTest, SaveSubmittedPasswordSuccess) {
EXPECT_CALL(mock_website_login_manager_, SaveSubmittedPassword);
EXPECT_CALL(
mock_password_change_success_tracker_,
- OnChangePasswordFlowCompleted(
- GURL(kOrigin), kUsername,
- PasswordChangeSuccessTracker::EndEvent::kAutomatedOwnPasswordFlow));
+ OnChangePasswordFlowCompleted(GURL(kOrigin), kUsername,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen));
// Check for leaked credentials.
EXPECT_CALL(mock_website_login_manager_,
@@ -143,9 +143,9 @@ TEST_F(SaveSubmittedPasswordActionTest, SaveLeakedNewSubmittedPassword) {
EXPECT_CALL(mock_website_login_manager_, SaveSubmittedPassword);
EXPECT_CALL(
mock_password_change_success_tracker_,
- OnChangePasswordFlowCompleted(
- GURL(kOrigin), kUsername,
- PasswordChangeSuccessTracker::EndEvent::kAutomatedOwnPasswordFlow));
+ OnChangePasswordFlowCompleted(GURL(kOrigin), kUsername,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen));
// Check for leaked credentials.
EXPECT_CALL(mock_website_login_manager_,
@@ -180,9 +180,9 @@ TEST_F(SaveSubmittedPasswordActionTest, SaveSubmittedPasswordLeakError) {
EXPECT_CALL(mock_website_login_manager_, SaveSubmittedPassword);
EXPECT_CALL(
mock_password_change_success_tracker_,
- OnChangePasswordFlowCompleted(
- GURL(kOrigin), kUsername,
- PasswordChangeSuccessTracker::EndEvent::kAutomatedOwnPasswordFlow));
+ OnChangePasswordFlowCompleted(GURL(kOrigin), kUsername,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen));
// Check for leaked credentials.
EXPECT_CALL(mock_website_login_manager_,
@@ -221,9 +221,9 @@ TEST_F(SaveSubmittedPasswordActionTest,
EXPECT_CALL(mock_website_login_manager_, SaveSubmittedPassword);
EXPECT_CALL(
mock_password_change_success_tracker_,
- OnChangePasswordFlowCompleted(
- GURL(kOrigin), kUsername,
- PasswordChangeSuccessTracker::EndEvent::kAutomatedOwnPasswordFlow));
+ OnChangePasswordFlowCompleted(GURL(kOrigin), kUsername,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen));
// Since no timeout was submitted in the action, no leak check is performed.
EXPECT_CALL(mock_website_login_manager_,
@@ -271,9 +271,9 @@ TEST_F(SaveSubmittedPasswordActionTest,
EXPECT_CALL(mock_website_login_manager_, SaveSubmittedPassword);
EXPECT_CALL(
mock_password_change_success_tracker_,
- OnChangePasswordFlowCompleted(
- GURL(kOrigin), kUsername,
- PasswordChangeSuccessTracker::EndEvent::kAutomatedOwnPasswordFlow));
+ OnChangePasswordFlowCompleted(GURL(kOrigin), kUsername,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen));
// Since no timeout was submitted in the action, no leak check is performed.
EXPECT_CALL(mock_website_login_manager_,
diff --git a/chromium/components/autofill_assistant/browser/actions/select_option_action.cc b/chromium/components/autofill_assistant/browser/actions/select_option_action.cc
index b829a05e367..bb08268c0df 100644
--- a/chromium/components/autofill_assistant/browser/actions/select_option_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/select_option_action.cc
@@ -13,6 +13,7 @@
#include "components/autofill_assistant/browser/actions/action_delegate_util.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/user_data_util.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/select_option_action.h b/chromium/components/autofill_assistant/browser/actions/select_option_action.h
index 62b4f299681..ef0af3251b3 100644
--- a/chromium/components/autofill_assistant/browser/actions/select_option_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/select_option_action.h
@@ -11,7 +11,6 @@
#include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action.h b/chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action.h
index 728dbc6494b..b4562f1c7c0 100644
--- a/chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action.h
@@ -9,7 +9,8 @@
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action_unittest.cc
index 0a890b757d8..f41c3ff1535 100644
--- a/chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/send_keystroke_events_action_unittest.cc
@@ -13,7 +13,7 @@
#include "components/autofill_assistant/browser/dom_action.pb.h"
#include "components/autofill_assistant/browser/mock_website_login_manager.h"
#include "components/autofill_assistant/browser/value_util.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/element_store.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "content/public/test/browser_task_environment.h"
@@ -120,7 +120,7 @@ TEST_F(SendKeystrokeEventsActionTest, PasswordTextValueReturnLastTimeUsed) {
content::WebContentsTester::For(web_contents_.get())
->NavigateAndCommit(GURL(kUrl));
element.SetObjectId("id");
- element.SetRenderFrameHost(web_contents_->GetMainFrame());
+ element.SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
mock_action_delegate_.GetElementStore()->AddElement("e",
element.dom_object());
@@ -161,7 +161,7 @@ TEST_F(SendKeystrokeEventsActionTest,
content::WebContentsTester::For(web_contents_.get())
->NavigateAndCommit(GURL(kUrl));
element.SetObjectId("id");
- element.SetRenderFrameHost(web_contents_->GetMainFrame());
+ element.SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
mock_action_delegate_.GetElementStore()->AddElement("e",
element.dom_object());
diff --git a/chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc b/chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc
index 51ef3ecde9d..b2394af4e79 100644
--- a/chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc
@@ -11,7 +11,6 @@
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/actions/action_delegate_util.h"
#include "components/autofill_assistant/browser/client_status.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/set_attribute_action.h b/chromium/components/autofill_assistant/browser/actions/set_attribute_action.h
index d9bc69d6b49..98e6c0b75a1 100644
--- a/chromium/components/autofill_assistant/browser/actions/set_attribute_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/set_attribute_action.h
@@ -9,7 +9,6 @@
#include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/show_cast_action.cc b/chromium/components/autofill_assistant/browser/actions/show_cast_action.cc
index 3927bd4530c..c06ace78b71 100644
--- a/chromium/components/autofill_assistant/browser/actions/show_cast_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/show_cast_action.cc
@@ -15,6 +15,7 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/web/element_action_util.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/show_cast_action.h b/chromium/components/autofill_assistant/browser/actions/show_cast_action.h
index f72fa3b98d9..8252ab67c18 100644
--- a/chromium/components/autofill_assistant/browser/actions/show_cast_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/show_cast_action.h
@@ -11,9 +11,9 @@
#include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/top_padding.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
+class ElementFinderResult;
// An action to show cast a given element on Web. Scrolling to it first if
// required.
diff --git a/chromium/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc
index d8066870a12..95e6172729a 100644
--- a/chromium/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc
@@ -122,7 +122,7 @@ TEST_F(ShowGenericUiActionTest, GoesIntoPromptState) {
EXPECT_CALL(mock_action_delegate_, Prompt(_, _, _, _, _)).Times(1);
EXPECT_CALL(mock_action_delegate_, SetGenericUi(_, _, _)).Times(1);
EXPECT_CALL(mock_action_delegate_, ClearGenericUi()).Times(1);
- EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt()).Times(1);
+ EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt(Eq(true))).Times(1);
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
@@ -289,7 +289,7 @@ TEST_F(ShowGenericUiActionTest, EndActionOnNavigation) {
bool browse_mode, bool browse_mode_invisible) {
std::move(end_navigation_callback).Run();
});
- EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt()).Times(1);
+ EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt(Eq(true))).Times(1);
EXPECT_CALL(
callback_,
Run(Pointee(
@@ -324,7 +324,7 @@ TEST_F(ShowGenericUiActionTest, BreakingNavigationBeforeUiIsSet) {
std::move(end_action_callback)
.Run(ClientStatus(OTHER_ACTION_STATUS));
}));
- EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt()).Times(1);
+ EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt(Eq(true))).Times(1);
EXPECT_CALL(
callback_,
Run(Pointee(
diff --git a/chromium/components/autofill_assistant/browser/actions/upload_dom_action.h b/chromium/components/autofill_assistant/browser/actions/upload_dom_action.h
index 3cf6ed01ba3..2b1010bc2f5 100644
--- a/chromium/components/autofill_assistant/browser/actions/upload_dom_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/upload_dom_action.h
@@ -10,7 +10,6 @@
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/actions/action.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc
index c59e6ae49a8..66141bb686f 100644
--- a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc
@@ -6,7 +6,7 @@
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h
index 14e19d9aac9..295041374cf 100644
--- a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h
@@ -8,9 +8,9 @@
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "components/autofill_assistant/browser/actions/action.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
+class ElementFinderResult;
class WaitForDocumentAction : public Action {
public:
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_dom_test_base.cc b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_test_base.cc
new file mode 100644
index 00000000000..d83424816d6
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_test_base.cc
@@ -0,0 +1,96 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/actions/wait_for_dom_test_base.h"
+
+namespace autofill_assistant {
+
+namespace {
+
+using ::testing::WithArgs;
+
+}
+
+WaitForDomTestBase::WaitForDomTestBase()
+ : task_env_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
+ ON_CALL(mock_web_controller_, FindElement)
+ .WillByDefault(WithArgs<2>([](auto&& callback) {
+ std::move(callback).Run(ClientStatus(ELEMENT_RESOLUTION_FAILED),
+ std::make_unique<ElementFinderResult>());
+ }));
+ EXPECT_CALL(mock_action_delegate_, WaitForDom)
+ .WillRepeatedly(Invoke(this, &WaitForDomTestBase::FakeWaitForDom));
+}
+
+WaitForDomTestBase::~WaitForDomTestBase() = default;
+
+void WaitForDomTestBase::FakeWaitForDom(
+ base::TimeDelta max_wait_time,
+ bool allow_observer_mode,
+ bool allow_interrupt,
+ WaitForDomObserver* observer,
+ base::RepeatingCallback<void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)>
+ check_elements,
+ base::OnceCallback<void(const ClientStatus&, base::TimeDelta)>
+ done_waiting_callback) {
+ fake_wait_for_dom_done_ = std::move(done_waiting_callback);
+ RunFakeWaitForDom(check_elements);
+}
+
+void WaitForDomTestBase::RunFakeWaitForDom(
+ base::RepeatingCallback<void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)>
+ check_elements) {
+ if (!fake_wait_for_dom_done_)
+ return;
+
+ checker_ = std::make_unique<BatchElementChecker>();
+ has_check_elements_result_ = false;
+ check_elements.Run(checker_.get(),
+ base::BindOnce(&WaitForDomTestBase::OnCheckElementsDone,
+ base::Unretained(this)));
+ task_env_.FastForwardBy(base::Milliseconds(fake_check_time_));
+ checker_->AddAllDoneCallback(
+ base::BindOnce(&WaitForDomTestBase::OnWaitForDomDone,
+ base::Unretained(this), check_elements));
+ checker_->Run(&mock_web_controller_);
+}
+
+void WaitForDomTestBase::OnCheckElementsDone(const ClientStatus& result) {
+ ASSERT_FALSE(has_check_elements_result_); // Duplicate calls
+ has_check_elements_result_ = true;
+ check_elements_result_ = result;
+}
+
+void WaitForDomTestBase::OnWaitForDomDone(
+ base::RepeatingCallback<void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)>
+ check_elements) {
+ ASSERT_TRUE(has_check_elements_result_); // OnCheckElementsDone() not called
+
+ if (!fake_wait_for_dom_done_)
+ return;
+
+ // If we manually set |wait_for_dom_status_| to an error status we simulate
+ // the WaitForDom ending in an error.
+ if (!wait_for_dom_status_.ok()) {
+ std::move(fake_wait_for_dom_done_)
+ .Run(wait_for_dom_status_, base::Milliseconds(0));
+ return;
+ }
+
+ if (check_elements_result_.ok()) {
+ std::move(fake_wait_for_dom_done_)
+ .Run(check_elements_result_, base::Milliseconds(fake_wait_time_));
+ } else {
+ wait_for_dom_timer_ = std::make_unique<base::OneShotTimer>();
+ wait_for_dom_timer_->Start(
+ FROM_HERE, base::Seconds(1),
+ base::BindOnce(&WaitForDomTestBase::RunFakeWaitForDom,
+ base::Unretained(this), check_elements));
+ }
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_dom_test_base.h b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_test_base.h
new file mode 100644
index 00000000000..0e1d7e3d28c
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_test_base.h
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_WAIT_FOR_DOM_TEST_BASE_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_WAIT_FOR_DOM_TEST_BASE_H_
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/test/bind.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "components/autofill_assistant/browser/batch_element_checker.h"
+#include "components/autofill_assistant/browser/wait_for_dom_observer.h"
+#include "components/autofill_assistant/browser/web/mock_web_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+class WaitForDomTestBase : public testing::Test {
+ public:
+ WaitForDomTestBase();
+ ~WaitForDomTestBase() override;
+
+ protected:
+ // Fakes ActionDelegate::WaitForDom.
+ //
+ // This simulates a WaitForDom that calls |check_elements_| every seconds
+ // until it gets a successful callback, then calls done_waiting_callback.
+ void FakeWaitForDom(
+ base::TimeDelta max_wait_time,
+ bool allow_observer_mode,
+ bool allow_interrupt,
+ WaitForDomObserver* observer,
+ base::RepeatingCallback<
+ void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)> check_elements,
+ base::OnceCallback<void(const ClientStatus&, base::TimeDelta)>
+ done_waiting_callback);
+
+ void RunFakeWaitForDom(
+ base::RepeatingCallback<
+ void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)> check_elements);
+
+ // Called from the check_elements callback passed to FakeWaitForDom.
+ void OnCheckElementsDone(const ClientStatus& result);
+
+ // Called by |checker_| once it's done and either ends the WaitForDom or
+ // schedule another run.
+ void OnWaitForDomDone(
+ base::RepeatingCallback<
+ void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)> check_elements);
+
+ // task_env_ must be first to guarantee other field
+ // creation run in that environment.
+ base::test::TaskEnvironment task_env_;
+
+ MockActionDelegate mock_action_delegate_;
+ MockWebController mock_web_controller_;
+ base::OnceCallback<void(const ClientStatus&, base::TimeDelta)>
+ fake_wait_for_dom_done_;
+ std::unique_ptr<BatchElementChecker> checker_;
+ bool has_check_elements_result_ = false;
+ ClientStatus check_elements_result_;
+ std::unique_ptr<base::OneShotTimer> wait_for_dom_timer_;
+ int fake_wait_time_ = 0;
+ int fake_check_time_ = 0;
+ ClientStatus wait_for_dom_status_ = OkClientStatus();
+};
+
+} // namespace autofill_assistant
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_WAIT_FOR_DOM_TEST_BASE_H_
diff --git a/chromium/components/autofill_assistant/browser/android/BUILD.gn b/chromium/components/autofill_assistant/browser/android/BUILD.gn
index 9b87db63b02..ebe35af8f8f 100644
--- a/chromium/components/autofill_assistant/browser/android/BUILD.gn
+++ b/chromium/components/autofill_assistant/browser/android/BUILD.gn
@@ -64,7 +64,6 @@ static_library("android") {
"//components/password_manager/content/browser",
"//components/password_manager/core/browser",
"//components/strings:components_strings_grit",
- "//components/variations/service",
"//components/version_info:channel",
"//components/version_info/android:channel_getter",
"//content/public/browser",
@@ -79,8 +78,8 @@ static_library("android") {
static_library("dependencies_android") {
sources = [
- "dependencies.cc",
- "dependencies.h",
+ "dependencies_android.cc",
+ "dependencies_android.h",
]
deps = [
diff --git a/chromium/components/autofill_assistant/browser/android/DEPS b/chromium/components/autofill_assistant/browser/android/DEPS
index 67f82c5b5c9..98a0a0dc6d9 100644
--- a/chromium/components/autofill_assistant/browser/android/DEPS
+++ b/chromium/components/autofill_assistant/browser/android/DEPS
@@ -1,15 +1,3 @@
include_rules = [
"+components/keyed_service/content",
]
-
-specific_include_rules = {
- 'dependencies.h': [
- '+components/variations/service/variations_service.h',
- ],
- 'dependencies.cc': [
- '+components/variations/service/variations_service.h',
- ],
- 'client_android.cc': [
- '+components/variations/service/variations_service.h',
- ],
-}
diff --git a/chromium/components/autofill_assistant/browser/android/assistant_header_model.cc b/chromium/components/autofill_assistant/browser/android/assistant_header_model.cc
index b1c6b947405..c71ad2ccae3 100644
--- a/chromium/components/autofill_assistant/browser/android/assistant_header_model.cc
+++ b/chromium/components/autofill_assistant/browser/android/assistant_header_model.cc
@@ -6,7 +6,6 @@
#include "base/android/jni_string.h"
#include "components/autofill_assistant/android/jni_headers/AssistantHeaderModel_jni.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
#include "components/autofill_assistant/browser/android/ui_controller_android.h"
#include "components/autofill_assistant/browser/android/ui_controller_android_utils.h"
@@ -76,7 +75,7 @@ void AssistantHeaderModel::SetProgressBarErrorState(bool error) {
void AssistantHeaderModel::SetStepProgressBarConfiguration(
const ShowProgressBarProto::StepProgressBarConfiguration& configuration,
const base::android::JavaRef<jobject>& jcontext,
- const Dependencies& dependencies) {
+ const DependenciesAndroid& dependencies) {
JNIEnv* env = AttachCurrentThread();
if (!configuration.annotated_step_icons().empty()) {
auto jlist = Java_AssistantHeaderModel_createIconList(env);
diff --git a/chromium/components/autofill_assistant/browser/android/assistant_header_model.h b/chromium/components/autofill_assistant/browser/android/assistant_header_model.h
index 18c4a4873f6..964af00290b 100644
--- a/chromium/components/autofill_assistant/browser/android/assistant_header_model.h
+++ b/chromium/components/autofill_assistant/browser/android/assistant_header_model.h
@@ -10,7 +10,7 @@
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "components/autofill_assistant/browser/android/assistant_header_delegate.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/tts_button_state.h"
@@ -38,7 +38,7 @@ class AssistantHeaderModel {
void SetStepProgressBarConfiguration(
const ShowProgressBarProto::StepProgressBarConfiguration& configuration,
const base::android::JavaRef<jobject>& jcontext,
- const Dependencies& dependencies);
+ const DependenciesAndroid& dependencies);
void SetSpinPoodle(bool enabled);
void SetChips(const base::android::ScopedJavaLocalRef<jobject>& jchips);
void SetTtsButtonVisible(bool visible);
diff --git a/chromium/components/autofill_assistant/browser/android/client_android.cc b/chromium/components/autofill_assistant/browser/android/client_android.cc
index 066ea3d030e..35552dac2ef 100644
--- a/chromium/components/autofill_assistant/browser/android/client_android.cc
+++ b/chromium/components/autofill_assistant/browser/android/client_android.cc
@@ -33,7 +33,6 @@
#include "components/password_manager/content/browser/password_change_success_tracker_factory.h"
#include "components/password_manager/core/browser/password_change_success_tracker.h"
#include "components/password_manager/core/browser/password_manager_client.h"
-#include "components/variations/service/variations_service.h"
#include "components/version_info/android/channel_getter.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -42,11 +41,13 @@
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "url/gurl.h"
-using base::android::AttachCurrentThread;
-using base::android::JavaParamRef;
-using base::android::JavaRef;
-using base::android::ScopedJavaGlobalRef;
-using base::android::ScopedJavaLocalRef;
+using ::base::android::AttachCurrentThread;
+using ::base::android::ConvertJavaStringToUTF8;
+using ::base::android::ConvertUTF8ToJavaString;
+using ::base::android::JavaParamRef;
+using ::base::android::JavaRef;
+using ::base::android::ScopedJavaGlobalRef;
+using ::base::android::ScopedJavaLocalRef;
namespace autofill_assistant {
namespace {
@@ -95,7 +96,11 @@ static void JNI_AutofillAssistantClient_OnOnboardingUiChange(
ClientAndroid::ClientAndroid(content::WebContents* web_contents,
const ScopedJavaGlobalRef<jobject>& jdependencies)
: content::WebContentsUserData<ClientAndroid>(*web_contents),
- dependencies_(Dependencies::CreateFromJavaDependencies(jdependencies)),
+ dependencies_(
+ DependenciesAndroid::CreateFromJavaDependencies(jdependencies)),
+ annotate_dom_model_service_(dependencies_->GetCommonDependencies()
+ ->GetOrCreateAnnotateDomModelService(
+ web_contents->GetBrowserContext())),
jdependencies_(jdependencies),
java_object_(Java_AutofillAssistantClient_Constructor(
AttachCurrentThread(),
@@ -127,7 +132,7 @@ bool ClientAndroid::IsVisible() const {
ui_controller_android_->IsAttached();
}
-bool ClientAndroid::Start(
+void ClientAndroid::Start(
const GURL& url,
std::unique_ptr<TriggerContext> trigger_context,
std::unique_ptr<Service> test_service_to_inject,
@@ -157,9 +162,11 @@ bool ClientAndroid::Start(
// Register TTS Synthetic Field Trial.
const bool enable_tts =
trigger_context->GetScriptParameters().GetEnableTts().value_or(false);
- dependencies_->CreateFieldTrialUtil()->RegisterSyntheticFieldTrial(
- kAutofillAssistantTtsTrialName,
- enable_tts ? kEnabledGroupName : kDisabledGroupName);
+ dependencies_->GetCommonDependencies()
+ ->CreateFieldTrialUtil()
+ ->RegisterSyntheticFieldTrial(
+ kAutofillAssistantTtsTrialName,
+ enable_tts ? kEnabledGroupName : kDisabledGroupName);
DCHECK(!trigger_context->GetDirectAction());
if (VLOG_IS_ON(2)) {
@@ -172,7 +179,7 @@ bool ClientAndroid::Start(
DVLOG(2) << "\t\t" << param.name() << ": " << param.value();
}
}
- return controller_->Start(url, std::move(trigger_context));
+ controller_->Start(url, std::move(trigger_context));
}
void ClientAndroid::OnJavaDestroyUI(
@@ -181,38 +188,10 @@ void ClientAndroid::OnJavaDestroyUI(
DestroyUI();
}
-void ClientAndroid::TransferUITo(
- JNIEnv* env,
- const base::android::JavaParamRef<jobject>& jcaller,
- const base::android::JavaParamRef<jobject>& jother_web_contents) {
- if (!ui_controller_android_)
- return;
-
- auto ui_ptr = std::move(ui_controller_android_);
- // From this point on, the UIController, in ui_ptr, is either transferred or
- // deleted.
-
- if (!jother_web_contents)
- return;
-
- auto* other_web_contents =
- content::WebContents::FromJavaWebContents(jother_web_contents);
- DCHECK_NE(other_web_contents, GetWebContents());
-
- ClientAndroid* other_client =
- ClientAndroid::FromWebContents(other_web_contents);
- if (!other_client || !other_client->NeedsUI())
- return;
-
- other_client->ui_controller_android_ = std::move(ui_ptr);
- other_client->AttachUI();
-}
-
base::android::ScopedJavaLocalRef<jstring> ClientAndroid::GetPrimaryAccountName(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
- return base::android::ConvertUTF8ToJavaString(
- env, GetChromeSignedInEmailAddress());
+ return ConvertUTF8ToJavaString(env, GetSignedInEmail());
}
void ClientAndroid::OnAccessToken(JNIEnv* env,
@@ -248,7 +227,9 @@ void ClientAndroid::FetchWebsiteActions(
/* onboarding_shown = */ false,
/* is_direct_action = */ true,
/* jinitial_url = */ nullptr,
- /* is_custom_tab = */ dependencies_->IsCustomTab(*GetWebContents())),
+ /* is_custom_tab = */
+ dependencies_->GetPlatformDependencies()->IsCustomTab(
+ *GetWebContents())),
base::BindOnce(&ClientAndroid::OnFetchWebsiteActions,
weak_ptr_factory_.GetWeakPtr(), scoped_jcallback));
}
@@ -359,7 +340,8 @@ bool ClientAndroid::PerformDirectAction(
/* is_direct_action = */ true,
/* jinitial_url = */
nullptr,
- /* is_custom_tab = */ dependencies_->IsCustomTab(*GetWebContents()));
+ /* is_custom_tab = */
+ dependencies_->GetPlatformDependencies()->IsCustomTab(*GetWebContents()));
int action_index = FindDirectAction(action_name);
if (action_index == -1)
@@ -387,6 +369,13 @@ void ClientAndroid::ShowFatalError(
Metrics::DropOutReason::NO_SCRIPTS);
}
+bool ClientAndroid::IsSupervisedUser(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jcaller) {
+ return dependencies_->GetCommonDependencies()->IsSupervisedUser(
+ GetWebContents()->GetBrowserContext());
+}
+
void ClientAndroid::OnSpokenFeedbackAccessibilityServiceChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
@@ -474,7 +463,7 @@ void ClientAndroid::DestroyUI() {
}
version_info::Channel ClientAndroid::GetChannel() const {
- return version_info::android::GetChannel();
+ return dependencies_->GetCommonDependencies()->GetChannel();
}
std::string ClientAndroid::GetEmailAddressForAccessTokenAccount() const {
@@ -484,8 +473,9 @@ std::string ClientAndroid::GetEmailAddressForAccessTokenAccount() const {
env, java_object_));
}
-std::string ClientAndroid::GetChromeSignedInEmailAddress() const {
- return dependencies_->GetChromeSignedInEmailAddress(GetWebContents());
+std::string ClientAndroid::GetSignedInEmail() const {
+ return dependencies_->GetCommonDependencies()->GetSignedInEmail(
+ GetWebContents()->GetBrowserContext());
}
absl::optional<std::pair<int, int>> ClientAndroid::GetWindowSize() const {
@@ -530,13 +520,15 @@ AccessTokenFetcher* ClientAndroid::GetAccessTokenFetcher() {
}
autofill::PersonalDataManager* ClientAndroid::GetPersonalDataManager() const {
- return dependencies_->GetPersonalDataManager();
+ return dependencies_->GetCommonDependencies()->GetPersonalDataManager(
+ GetWebContents()->GetBrowserContext());
}
WebsiteLoginManager* ClientAndroid::GetWebsiteLoginManager() const {
if (!website_login_manager_) {
auto* password_manager_client =
- dependencies_->GetPasswordManagerClient(GetWebContents());
+ dependencies_->GetCommonDependencies()->GetPasswordManagerClient(
+ GetWebContents());
if (password_manager_client) {
website_login_manager_ = std::make_unique<WebsiteLoginManagerImpl>(
password_manager_client, GetWebContents());
@@ -554,16 +546,12 @@ ClientAndroid::GetPasswordChangeSuccessTracker() const {
}
std::string ClientAndroid::GetLocale() const {
+ // TODO(b/201964911): use dependencies instead.
return base::android::GetDefaultLocaleString();
}
std::string ClientAndroid::GetCountryCode() const {
- variations::VariationsService* variations_service =
- dependencies_->GetVariationsService();
- // Use fallback "ZZ" if no country is available.
- if (!variations_service || variations_service->GetLatestCountry().empty())
- return "ZZ";
- return base::ToUpperASCII(variations_service->GetLatestCountry());
+ return dependencies_->GetCommonDependencies()->GetCountryCode();
}
DeviceContext ClientAndroid::GetDeviceContext() const {
@@ -614,6 +602,39 @@ ScriptExecutorUiDelegate* ClientAndroid::GetScriptExecutorUiDelegate() {
return ui_controller_.get();
}
+bool ClientAndroid::MustUseBackendData() const {
+ // For WebLayer flows the client does not have access to Chrome's Autofill
+ // data and must use data from our backend. Similarly the client can not use
+ // e.g. Autofill's data editors and must rely on GMS Core provided
+ // replacements.
+ return dependencies_->GetCommonDependencies()->IsWebLayer();
+}
+
+void ClientAndroid::GetAnnotateDomModelVersion(
+ base::OnceCallback<void(absl::optional<int64_t>)> callback) const {
+ if (!annotate_dom_model_service_) {
+ std::move(callback).Run(absl::nullopt);
+ return;
+ }
+
+ auto model_version = annotate_dom_model_service_->GetModelVersion();
+ if (model_version.has_value()) {
+ std::move(callback).Run(model_version);
+ return;
+ }
+
+ annotate_dom_model_service_->NotifyOnModelFileAvailable(
+ base::BindOnce(&ClientAndroid::OnAnnotateDomModelFileAvailable,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void ClientAndroid::OnAnnotateDomModelFileAvailable(
+ base::OnceCallback<void(absl::optional<int64_t>)> callback,
+ bool available) {
+ DCHECK(annotate_dom_model_service_);
+ std::move(callback).Run(annotate_dom_model_service_->GetModelVersion());
+}
+
void ClientAndroid::Shutdown(Metrics::DropOutReason reason) {
if (!controller_)
return;
@@ -687,9 +708,7 @@ void ClientAndroid::CreateController(
GetWebContents(), /* client= */ this,
base::DefaultTickClock::GetInstance(),
RuntimeManager::GetForWebContents(GetWebContents())->GetWeakPtr(),
- std::move(service), ukm::UkmRecorder::Get(),
- dependencies_->GetOrCreateAnnotateDomModelService(
- GetWebContents()->GetBrowserContext()));
+ std::move(service), ukm::UkmRecorder::Get(), annotate_dom_model_service_);
ui_controller_ = std::make_unique<UiController>(
/* client= */ this, controller_.get(), std::move(tts_controller));
ui_controller_->StartListening();
diff --git a/chromium/components/autofill_assistant/browser/android/client_android.h b/chromium/components/autofill_assistant/browser/android/client_android.h
index 240182908a9..c427dcefb3e 100644
--- a/chromium/components/autofill_assistant/browser/android/client_android.h
+++ b/chromium/components/autofill_assistant/browser/android/client_android.h
@@ -11,7 +11,7 @@
#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/android/ui_controller_android.h"
#include "components/autofill_assistant/browser/client.h"
#include "components/autofill_assistant/browser/controller.h"
@@ -58,17 +58,13 @@ class ClientAndroid : public Client,
// Returns whether UI is currently being displayed to the user.
bool IsVisible() const;
- bool Start(const GURL& url,
+ void Start(const GURL& url,
std::unique_ptr<TriggerContext> trigger_context,
std::unique_ptr<Service> test_service_to_inject,
const base::android::JavaRef<jobject>& joverlay_coordinator,
const absl::optional<TriggerScriptProto>& trigger_script);
void OnJavaDestroyUI(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
- void TransferUITo(
- JNIEnv* env,
- const base::android::JavaParamRef<jobject>& jcaller,
- const base::android::JavaParamRef<jobject>& jother_web_contents);
base::android::ScopedJavaLocalRef<jstring> GetPrimaryAccountName(
JNIEnv* env,
@@ -110,6 +106,9 @@ class ClientAndroid : public Client,
void ShowFatalError(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
+ bool IsSupervisedUser(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jcaller);
+
void OnSpokenFeedbackAccessibilityServiceChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
@@ -126,7 +125,7 @@ class ClientAndroid : public Client,
void DestroyUI() override;
version_info::Channel GetChannel() const override;
std::string GetEmailAddressForAccessTokenAccount() const override;
- std::string GetChromeSignedInEmailAddress() const override;
+ std::string GetSignedInEmail() const override;
absl::optional<std::pair<int, int>> GetWindowSize() const override;
ClientContextProto::ScreenOrientation GetScreenOrientation() const override;
void FetchPaymentsClientToken(
@@ -146,6 +145,10 @@ class ClientAndroid : public Client,
void RecordDropOut(Metrics::DropOutReason reason) override;
bool HasHadUI() const override;
ScriptExecutorUiDelegate* GetScriptExecutorUiDelegate() override;
+ bool MustUseBackendData() const override;
+ void GetAnnotateDomModelVersion(
+ base::OnceCallback<void(absl::optional<int64_t>)> callback)
+ const override;
// Overrides AccessTokenFetcher
void FetchAccessToken(
@@ -179,10 +182,15 @@ class ClientAndroid : public Client,
// UiDelegate::PerformUserAction() or -1 if not found.
int FindDirectAction(const std::string& action_name);
+ void OnAnnotateDomModelFileAvailable(
+ base::OnceCallback<void(absl::optional<int64_t>)> callback,
+ bool available);
+
WEB_CONTENTS_USER_DATA_KEY_DECL();
// Contains AssistantStaticDependencies which do not change.
- const std::unique_ptr<const Dependencies> dependencies_;
+ const std::unique_ptr<const DependenciesAndroid> dependencies_;
+ const raw_ptr<AnnotateDomModelService> annotate_dom_model_service_;
// Can change based on activity attachment.
const base::android::ScopedJavaGlobalRef<jobject> jdependencies_;
diff --git a/chromium/components/autofill_assistant/browser/android/dependencies.cc b/chromium/components/autofill_assistant/browser/android/dependencies_android.cc
index a639cbdce81..c66bd3e84d9 100644
--- a/chromium/components/autofill_assistant/browser/android/dependencies.cc
+++ b/chromium/components/autofill_assistant/browser/android/dependencies_android.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
@@ -18,14 +18,16 @@ using ::base::android::ScopedJavaGlobalRef;
namespace autofill_assistant {
-std::unique_ptr<Dependencies> Dependencies::CreateFromJavaStaticDependencies(
+std::unique_ptr<DependenciesAndroid>
+DependenciesAndroid::CreateFromJavaStaticDependencies(
const JavaRef<jobject>& jstatic_dependencies) {
- return base::WrapUnique(reinterpret_cast<Dependencies*>(
+ return base::WrapUnique(reinterpret_cast<DependenciesAndroid*>(
Java_AssistantStaticDependencies_createNative(AttachCurrentThread(),
jstatic_dependencies)));
}
-std::unique_ptr<Dependencies> Dependencies::CreateFromJavaDependencies(
+std::unique_ptr<DependenciesAndroid>
+DependenciesAndroid::CreateFromJavaDependencies(
const JavaRef<jobject>& jdependencies) {
const auto jstatic_dependencies =
Java_AssistantDependencies_getStaticDependencies(AttachCurrentThread(),
@@ -33,43 +35,46 @@ std::unique_ptr<Dependencies> Dependencies::CreateFromJavaDependencies(
return CreateFromJavaStaticDependencies(jstatic_dependencies);
}
-Dependencies::Dependencies(JNIEnv* env,
- const JavaParamRef<jobject>& jstatic_dependencies)
+DependenciesAndroid::DependenciesAndroid(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& jstatic_dependencies)
: jstatic_dependencies_(jstatic_dependencies) {}
-ScopedJavaGlobalRef<jobject> Dependencies::GetJavaStaticDependencies() const {
+ScopedJavaGlobalRef<jobject> DependenciesAndroid::GetJavaStaticDependencies()
+ const {
return jstatic_dependencies_;
}
-ScopedJavaGlobalRef<jobject> Dependencies::CreateInfoPageUtil() const {
+ScopedJavaGlobalRef<jobject> DependenciesAndroid::CreateInfoPageUtil() const {
return ScopedJavaGlobalRef<jobject>(
Java_AssistantStaticDependencies_createInfoPageUtil(
AttachCurrentThread(), jstatic_dependencies_));
}
-ScopedJavaGlobalRef<jobject> Dependencies::CreateAccessTokenUtil() const {
+ScopedJavaGlobalRef<jobject> DependenciesAndroid::CreateAccessTokenUtil()
+ const {
return ScopedJavaGlobalRef<jobject>(
Java_AssistantStaticDependencies_createAccessTokenUtil(
AttachCurrentThread(), jstatic_dependencies_));
}
-ScopedJavaGlobalRef<jobject> Dependencies::CreateImageFetcher() const {
+ScopedJavaGlobalRef<jobject> DependenciesAndroid::CreateImageFetcher() const {
return ScopedJavaGlobalRef<jobject>(
Java_AssistantStaticDependencies_createImageFetcher(
AttachCurrentThread(), jstatic_dependencies_));
}
-ScopedJavaGlobalRef<jobject> Dependencies::CreateIconBridge() const {
+ScopedJavaGlobalRef<jobject> DependenciesAndroid::CreateIconBridge() const {
return ScopedJavaGlobalRef<jobject>(
Java_AssistantStaticDependencies_createIconBridge(AttachCurrentThread(),
jstatic_dependencies_));
}
-bool Dependencies::IsAccessibilityEnabled() const {
+bool DependenciesAndroid::IsAccessibilityEnabled() const {
return Java_AssistantStaticDependencies_isAccessibilityEnabled(
AttachCurrentThread(), jstatic_dependencies_);
}
-Dependencies::~Dependencies() = default;
+DependenciesAndroid::~DependenciesAndroid() = default;
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/android/dependencies.h b/chromium/components/autofill_assistant/browser/android/dependencies_android.h
index 7ae45983e07..26e630eee68 100644
--- a/chromium/components/autofill_assistant/browser/android/dependencies.h
+++ b/chromium/components/autofill_assistant/browser/android/dependencies_android.h
@@ -2,30 +2,34 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ANDROID_DEPENDENCIES_H_
-#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ANDROID_DEPENDENCIES_H_
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ANDROID_DEPENDENCIES_ANDROID_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ANDROID_DEPENDENCIES_ANDROID_H_
#include <memory>
#include "base/android/scoped_java_ref.h"
#include "base/strings/string_piece.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill_assistant/browser/assistant_field_trial_util.h"
+#include "components/autofill_assistant/browser/common_dependencies.h"
+#include "components/autofill_assistant/browser/platform_dependencies.h"
#include "components/autofill_assistant/content/browser/annotate_dom_model_service.h"
#include "components/password_manager/core/browser/password_manager_client.h"
-#include "components/variations/service/variations_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
namespace autofill_assistant {
-// Interface for platform delegates that provide platform-dependent features
-// and dependencies to the starter.
-class Dependencies {
+// Wrapper for all dependencies needed in android flows using legacy UI.
+//
+// Provides the right implementation of |CommonDependencies| and
+// |PlatformDependencies| depending on platform and whether we are in Chrome or
+// Weblayer.
+class DependenciesAndroid {
public:
- static std::unique_ptr<Dependencies> CreateFromJavaStaticDependencies(
+ static std::unique_ptr<DependenciesAndroid> CreateFromJavaStaticDependencies(
const base::android::JavaRef<jobject>& jstatic_dependencies);
- static std::unique_ptr<Dependencies> CreateFromJavaDependencies(
+ static std::unique_ptr<DependenciesAndroid> CreateFromJavaDependencies(
const base::android::JavaRef<jobject>& jdependencies);
base::android::ScopedJavaGlobalRef<jobject> GetJavaStaticDependencies() const;
@@ -36,37 +40,19 @@ class Dependencies {
bool IsAccessibilityEnabled() const;
- virtual ~Dependencies();
+ virtual const CommonDependencies* GetCommonDependencies() const = 0;
+ virtual const PlatformDependencies* GetPlatformDependencies() const = 0;
- virtual std::unique_ptr<AssistantFieldTrialUtil> CreateFieldTrialUtil()
- const = 0;
-
- virtual autofill::PersonalDataManager* GetPersonalDataManager() const = 0;
-
- virtual password_manager::PasswordManagerClient* GetPasswordManagerClient(
- content::WebContents* web_contents) const = 0;
-
- virtual variations::VariationsService* GetVariationsService() const = 0;
-
- virtual std::string GetChromeSignedInEmailAddress(
- content::WebContents* web_contents) const = 0;
-
- virtual AnnotateDomModelService* GetOrCreateAnnotateDomModelService(
- content::BrowserContext* browser_context) const = 0;
-
- virtual bool IsCustomTab(const content::WebContents& web_contents) const = 0;
-
- virtual bool IsWebLayer() const = 0;
+ virtual ~DependenciesAndroid();
protected:
- Dependencies(
+ DependenciesAndroid(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jstatic_dependencies);
- private:
const base::android::ScopedJavaGlobalRef<jobject> jstatic_dependencies_;
};
} // namespace autofill_assistant
-#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ANDROID_DEPENDENCIES_H_
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ANDROID_DEPENDENCIES_ANDROID_H_
diff --git a/chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.cc b/chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.cc
index 04648364057..f17be270bb8 100644
--- a/chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.cc
+++ b/chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.cc
@@ -7,7 +7,7 @@
#include "base/android/jni_string.h"
#include "components/autofill_assistant/android/jni_headers/AssistantViewFactory_jni.h"
#include "components/autofill_assistant/browser/android/assistant_generic_ui_delegate.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/android/generic_ui_events_android.h"
#include "components/autofill_assistant/browser/android/generic_ui_interactions_android.h"
#include "components/autofill_assistant/browser/android/interaction_handler_android.h"
@@ -30,7 +30,7 @@ base::android::ScopedJavaGlobalRef<jobject> CreateViewHierarchy(
JNIEnv* env,
const JavaRef<jobject>& jcontext,
const JavaRef<jobject>& jdelegate,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
const ViewProto& proto,
InteractionHandlerAndroid* interaction_handler,
ViewHandlerAndroid* view_handler,
@@ -102,7 +102,7 @@ base::android::ScopedJavaLocalRef<jobject> CreateJavaVerticalExpander(
const JavaRef<jobject>& jcontext,
const JavaRef<jobject>& jdelegate,
const JavaRef<jstring>& jidentifier,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
const VerticalExpanderViewProto& proto,
InteractionHandlerAndroid* interaction_handler,
ViewHandlerAndroid* view_handler,
@@ -160,7 +160,7 @@ base::android::ScopedJavaLocalRef<jobject> CreateJavaToggleButton(
const JavaRef<jobject>& jcontext,
const JavaRef<jobject>& jdelegate,
const JavaRef<jstring>& jidentifier,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
const ToggleButtonViewProto& proto,
InteractionHandlerAndroid* interaction_handler,
ViewHandlerAndroid* view_handler,
@@ -214,7 +214,7 @@ base::android::ScopedJavaGlobalRef<jobject> CreateJavaView(
JNIEnv* env,
const JavaRef<jobject>& jcontext,
const JavaRef<jobject>& jdelegate,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
const ViewProto& proto,
InteractionHandlerAndroid* interaction_handler,
ViewHandlerAndroid* view_handler,
@@ -409,7 +409,7 @@ base::android::ScopedJavaGlobalRef<jobject> CreateViewHierarchy(
JNIEnv* env,
const JavaRef<jobject>& jcontext,
const JavaRef<jobject>& jdelegate,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
const ViewProto& proto,
InteractionHandlerAndroid* interaction_handler,
ViewHandlerAndroid* view_handler,
@@ -477,7 +477,7 @@ GenericUiNestedControllerAndroid::CreateFromProto(
const GenericUserInterfaceProto& proto,
base::android::ScopedJavaGlobalRef<jobject> jcontext,
base::android::ScopedJavaGlobalRef<jobject> jinfo_page_util,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
base::android::ScopedJavaGlobalRef<jobject> jdelegate,
EventHandler* event_handler,
UserModel* user_model,
diff --git a/chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.h b/chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.h
index b127eab45a4..b21b3e9a643 100644
--- a/chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.h
+++ b/chromium/components/autofill_assistant/browser/android/generic_ui_nested_controller_android.h
@@ -11,7 +11,7 @@
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/service.pb.h"
namespace autofill_assistant {
@@ -31,7 +31,7 @@ class GenericUiNestedControllerAndroid {
const GenericUserInterfaceProto& proto,
base::android::ScopedJavaGlobalRef<jobject> jcontext,
base::android::ScopedJavaGlobalRef<jobject> jinfo_page_util,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
base::android::ScopedJavaGlobalRef<jobject> jdelegate,
EventHandler* event_handler,
UserModel* user_model,
diff --git a/chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.cc b/chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.cc
index 86ff1229eeb..b8ba3f66142 100644
--- a/chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.cc
+++ b/chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.cc
@@ -4,7 +4,7 @@
#include "components/autofill_assistant/browser/android/generic_ui_root_controller_android.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/android/generic_ui_nested_controller_android.h"
#include "components/autofill_assistant/browser/radio_button_controller.h"
@@ -29,7 +29,7 @@ GenericUiRootControllerAndroid::CreateFromProto(
const GenericUserInterfaceProto& proto,
base::android::ScopedJavaGlobalRef<jobject> jcontext,
base::android::ScopedJavaGlobalRef<jobject> jinfo_page_util,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
base::android::ScopedJavaGlobalRef<jobject> jdelegate,
EventHandler* event_handler,
UserModel* user_model,
diff --git a/chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.h b/chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.h
index 6d1f616712d..36de2af6948 100644
--- a/chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.h
+++ b/chromium/components/autofill_assistant/browser/android/generic_ui_root_controller_android.h
@@ -9,7 +9,7 @@
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/service.pb.h"
namespace autofill_assistant {
@@ -28,7 +28,7 @@ class GenericUiRootControllerAndroid {
const GenericUserInterfaceProto& proto,
base::android::ScopedJavaGlobalRef<jobject> jcontext,
base::android::ScopedJavaGlobalRef<jobject> jinfo_page_util,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
base::android::ScopedJavaGlobalRef<jobject> jdelegate,
EventHandler* event_handler,
UserModel* user_model,
diff --git a/chromium/components/autofill_assistant/browser/android/interaction_handler_android.cc b/chromium/components/autofill_assistant/browser/android/interaction_handler_android.cc
index e88e5bf6842..90bc5cb9e57 100644
--- a/chromium/components/autofill_assistant/browser/android/interaction_handler_android.cc
+++ b/chromium/components/autofill_assistant/browser/android/interaction_handler_android.cc
@@ -9,7 +9,7 @@
#include "base/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/android/generic_ui_interactions_android.h"
#include "components/autofill_assistant/browser/android/generic_ui_nested_controller_android.h"
#include "components/autofill_assistant/browser/android/view_handler_android.h"
@@ -90,7 +90,7 @@ InteractionHandlerAndroid::InteractionHandlerAndroid(
BasicInteractions* basic_interactions,
ViewHandlerAndroid* view_handler,
RadioButtonController* radio_button_controller,
- const Dependencies* dependencies,
+ const DependenciesAndroid* dependencies,
base::android::ScopedJavaGlobalRef<jobject> jcontext,
base::android::ScopedJavaGlobalRef<jobject> jinfo_page_util,
base::android::ScopedJavaGlobalRef<jobject> jdelegate)
diff --git a/chromium/components/autofill_assistant/browser/android/interaction_handler_android.h b/chromium/components/autofill_assistant/browser/android/interaction_handler_android.h
index 28999086ea1..b8f7200fcf2 100644
--- a/chromium/components/autofill_assistant/browser/android/interaction_handler_android.h
+++ b/chromium/components/autofill_assistant/browser/android/interaction_handler_android.h
@@ -14,7 +14,7 @@
#include "base/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/event_handler.h"
#include "components/autofill_assistant/browser/service.pb.h"
@@ -42,7 +42,7 @@ class InteractionHandlerAndroid : public EventHandler::Observer {
BasicInteractions* basic_interactions,
ViewHandlerAndroid* view_handler,
RadioButtonController* radio_button_controller,
- const Dependencies* dependencies,
+ const DependenciesAndroid* dependencies,
base::android::ScopedJavaGlobalRef<jobject> jcontext,
base::android::ScopedJavaGlobalRef<jobject> jinfo_page_util,
base::android::ScopedJavaGlobalRef<jobject> jdelegate);
@@ -108,7 +108,7 @@ class InteractionHandlerAndroid : public EventHandler::Observer {
raw_ptr<BasicInteractions> basic_interactions_ = nullptr;
raw_ptr<ViewHandlerAndroid> view_handler_ = nullptr;
raw_ptr<RadioButtonController> radio_button_controller_ = nullptr;
- raw_ptr<const Dependencies> dependencies_ = nullptr;
+ raw_ptr<const DependenciesAndroid> dependencies_ = nullptr;
base::android::ScopedJavaGlobalRef<jobject> jcontext_ = nullptr;
base::android::ScopedJavaGlobalRef<jobject> jinfo_page_util_ = nullptr;
base::android::ScopedJavaGlobalRef<jobject> jdelegate_ = nullptr;
diff --git a/chromium/components/autofill_assistant/browser/android/starter_delegate_android.cc b/chromium/components/autofill_assistant/browser/android/starter_delegate_android.cc
index 13fed287599..77661dba646 100644
--- a/chromium/components/autofill_assistant/browser/android/starter_delegate_android.cc
+++ b/chromium/components/autofill_assistant/browser/android/starter_delegate_android.cc
@@ -37,7 +37,7 @@ static jlong JNI_Starter_FromWebContents(
auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents);
CHECK(web_contents);
- auto dependencies = Dependencies::CreateFromJavaStaticDependencies(
+ auto dependencies = DependenciesAndroid::CreateFromJavaStaticDependencies(
ScopedJavaGlobalRef<jobject>(env, jstatic_dependencies));
StarterDelegateAndroid::CreateForWebContents(web_contents,
std::move(dependencies));
@@ -52,15 +52,15 @@ static jlong JNI_Starter_FromWebContents(
StarterDelegateAndroid::StarterDelegateAndroid(
content::WebContents* web_contents,
- std::unique_ptr<Dependencies> dependencies)
+ std::unique_ptr<DependenciesAndroid> dependencies)
: content::WebContentsUserData<StarterDelegateAndroid>(*web_contents),
dependencies_(std::move(dependencies)),
website_login_manager_(std::make_unique<WebsiteLoginManagerImpl>(
- dependencies_->GetPasswordManagerClient(web_contents),
+ GetCommonDependencies()->GetPasswordManagerClient(web_contents),
web_contents)) {
// Create the AnnotateDomModelService when the browser starts, such that it
// starts listening to model changes early enough.
- dependencies_->GetOrCreateAnnotateDomModelService(
+ GetCommonDependencies()->GetOrCreateAnnotateDomModelService(
web_contents->GetBrowserContext());
}
@@ -248,16 +248,23 @@ bool StarterDelegateAndroid::GetMakeSearchesAndBrowsingBetterEnabled() const {
}
bool StarterDelegateAndroid::GetIsLoggedIn() {
- return !dependencies_->GetChromeSignedInEmailAddress(&GetWebContents())
+ return !GetCommonDependencies()
+ ->GetSignedInEmail(GetWebContents().GetBrowserContext())
.empty();
}
+bool StarterDelegateAndroid::GetIsSupervisedUser() {
+ return GetCommonDependencies()->IsSupervisedUser(
+ GetWebContents().GetBrowserContext());
+}
+
bool StarterDelegateAndroid::GetIsCustomTab() const {
- return dependencies_->IsCustomTab(GetWebContents());
+ return dependencies_->GetPlatformDependencies()->IsCustomTab(
+ GetWebContents());
}
bool StarterDelegateAndroid::GetIsWebLayer() const {
- return dependencies_->IsWebLayer();
+ return GetCommonDependencies()->IsWebLayer();
}
bool StarterDelegateAndroid::GetIsTabCreatedByGSA() const {
@@ -346,13 +353,23 @@ bool StarterDelegateAndroid::IsRegularScriptVisible() const {
std::unique_ptr<AssistantFieldTrialUtil>
StarterDelegateAndroid::CreateFieldTrialUtil() {
- return dependencies_->CreateFieldTrialUtil();
+ return GetCommonDependencies()->CreateFieldTrialUtil();
}
bool StarterDelegateAndroid::IsAttached() {
return !!java_object_;
}
+const CommonDependencies* StarterDelegateAndroid::GetCommonDependencies()
+ const {
+ return dependencies_->GetCommonDependencies();
+}
+
+const PlatformDependencies* StarterDelegateAndroid::GetPlatformDependencies()
+ const {
+ return dependencies_->GetPlatformDependencies();
+}
+
base::WeakPtr<StarterPlatformDelegate> StarterDelegateAndroid::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
diff --git a/chromium/components/autofill_assistant/browser/android/starter_delegate_android.h b/chromium/components/autofill_assistant/browser/android/starter_delegate_android.h
index 7cbdcb06c6d..4b44ed681ab 100644
--- a/chromium/components/autofill_assistant/browser/android/starter_delegate_android.h
+++ b/chromium/components/autofill_assistant/browser/android/starter_delegate_android.h
@@ -11,7 +11,7 @@
#include "base/android/jni_weak_ref.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/assistant_field_trial_util.h"
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/onboarding_result.h"
@@ -78,11 +78,14 @@ class StarterDelegateAndroid
void SetProactiveHelpSettingEnabled(bool enabled) override;
bool GetMakeSearchesAndBrowsingBetterEnabled() const override;
bool GetIsLoggedIn() override;
+ bool GetIsSupervisedUser() override;
bool GetIsCustomTab() const override;
bool GetIsWebLayer() const override;
bool GetIsTabCreatedByGSA() const override;
std::unique_ptr<AssistantFieldTrialUtil> CreateFieldTrialUtil() override;
bool IsAttached() override;
+ const CommonDependencies* GetCommonDependencies() const override;
+ const PlatformDependencies* GetPlatformDependencies() const override;
base::WeakPtr<StarterPlatformDelegate> GetWeakPtr() override;
// Called by Java to start an autofill-assistant flow for an incoming intent.
@@ -123,14 +126,14 @@ class StarterDelegateAndroid
private:
friend class content::WebContentsUserData<StarterDelegateAndroid>;
StarterDelegateAndroid(content::WebContents* web_contents,
- std::unique_ptr<Dependencies> dependencies);
+ std::unique_ptr<DependenciesAndroid> dependencies);
void CreateJavaDependenciesIfNecessary();
WEB_CONTENTS_USER_DATA_KEY_DECL();
base::WeakPtr<Starter> starter_;
// Contains AssistantStaticDependencies which do not change.
- const std::unique_ptr<const Dependencies> dependencies_;
+ const std::unique_ptr<const DependenciesAndroid> dependencies_;
// Can change based on activity attachment.
base::android::ScopedJavaGlobalRef<jobject> java_dependencies_;
diff --git a/chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.cc b/chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.cc
index d46bdc58171..ef47ea09c4f 100644
--- a/chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.cc
+++ b/chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.cc
@@ -8,7 +8,7 @@
#include "base/android/jni_string.h"
#include "components/autofill_assistant/android/jni_headers/AssistantTriggerScriptBridge_jni.h"
#include "components/autofill_assistant/browser/android/assistant_header_model.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/android/ui_controller_android_utils.h"
using base::android::AttachCurrentThread;
@@ -23,7 +23,8 @@ TriggerScriptBridgeAndroid::TriggerScriptBridgeAndroid(
JNIEnv* env,
const base::android::JavaRef<jobject>& jweb_contents,
const base::android::JavaRef<jobject>& jassistant_deps)
- : dependencies_(Dependencies::CreateFromJavaDependencies(jassistant_deps)) {
+ : dependencies_(
+ DependenciesAndroid::CreateFromJavaDependencies(jassistant_deps)) {
java_object_ = Java_AssistantTriggerScriptBridge_Constructor(
env, jweb_contents, jassistant_deps);
Java_AssistantTriggerScriptBridge_setNativePtr(
diff --git a/chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.h b/chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.h
index 6530233073d..3fcd84a2bdf 100644
--- a/chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.h
+++ b/chromium/components/autofill_assistant/browser/android/trigger_script_bridge_android.h
@@ -7,7 +7,7 @@
#include "base/android/jni_android.h"
#include "base/memory/raw_ptr.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/trigger_context.h"
@@ -64,7 +64,7 @@ class TriggerScriptBridgeAndroid : public TriggerScriptCoordinator::UiDelegate {
// Java-side AssistantStaticDependencies object. This never changes during the
// life of the application.
- const std::unique_ptr<const Dependencies> dependencies_;
+ const std::unique_ptr<const DependenciesAndroid> dependencies_;
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/android/ui_controller_android.cc b/chromium/components/autofill_assistant/browser/android/ui_controller_android.cc
index 386e39b04c4..87009600c8c 100644
--- a/chromium/components/autofill_assistant/browser/android/ui_controller_android.cc
+++ b/chromium/components/autofill_assistant/browser/android/ui_controller_android.cc
@@ -31,7 +31,7 @@
#include "components/autofill_assistant/android/jni_headers/AssistantPlaceholdersConfiguration_jni.h"
#include "components/autofill_assistant/android/jni_headers/AutofillAssistantUiController_jni.h"
#include "components/autofill_assistant/browser/android/client_android.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/android/generic_ui_root_controller_android.h"
#include "components/autofill_assistant/browser/android/ui_controller_android_utils.h"
#include "components/autofill_assistant/browser/bottom_sheet_state.h"
@@ -246,17 +246,18 @@ std::unique_ptr<UiControllerAndroid> UiControllerAndroid::CreateFromWebContents(
const base::android::JavaRef<jobject>& jdependencies,
const base::android::JavaRef<jobject>& joverlay_coordinator) {
JNIEnv* env = AttachCurrentThread();
- if (!Java_AutofillAssistantUiController_shouldCreateNewInstance(
+ if (!Java_AutofillAssistantUiController_canAttachUi(
env, web_contents->GetJavaWebContents(), jdependencies)) {
return nullptr;
}
- return std::make_unique<UiControllerAndroid>(env, jdependencies,
+ return std::make_unique<UiControllerAndroid>(env, web_contents, jdependencies,
joverlay_coordinator);
}
UiControllerAndroid::UiControllerAndroid(
JNIEnv* env,
+ content::WebContents* web_contents,
const base::android::JavaRef<jobject>& jdependencies,
const base::android::JavaRef<jobject>& joverlay_coordinator)
: overlay_delegate_(this),
@@ -265,9 +266,11 @@ UiControllerAndroid::UiControllerAndroid(
form_delegate_(this),
generic_ui_delegate_(this),
bottom_bar_delegate_(this),
- dependencies_(Dependencies::CreateFromJavaDependencies(jdependencies)) {
+ dependencies_(
+ DependenciesAndroid::CreateFromJavaDependencies(jdependencies)) {
java_object_ = Java_AutofillAssistantUiController_Constructor(
- env, reinterpret_cast<intptr_t>(this), jdependencies,
+ env, reinterpret_cast<intptr_t>(this), web_contents->GetJavaWebContents(),
+ jdependencies,
/* allowTabSwitching= */
base::FeatureList::IsEnabled(features::kAutofillAssistantChromeEntry),
joverlay_coordinator);
@@ -281,8 +284,8 @@ UiControllerAndroid::UiControllerAndroid(
// Register header_delegate_ as delegate for clicks on header buttons.
header_model_->SetDelegate(header_delegate_);
- // Register collect_user_data_delegate_ as delegate for the collect user data
- // UI.
+ // Register collect_user_data_delegate_ as delegate for the collect user
+ // data UI.
Java_AssistantCollectUserDataModel_setDelegate(
env, GetCollectUserDataModel(),
collect_user_data_delegate_.GetJavaObject());
@@ -315,12 +318,11 @@ void UiControllerAndroid::Attach(content::WebContents* web_contents,
JNIEnv* env = AttachCurrentThread();
auto java_web_contents = web_contents->GetJavaWebContents();
- Java_AutofillAssistantUiController_setWebContents(env, java_object_,
- java_web_contents);
Java_AssistantCollectUserDataModel_setWebContents(
env, GetCollectUserDataModel(), java_web_contents);
Java_AssistantOverlayModel_setWebContents(env, GetOverlayModel(),
java_web_contents);
+
OnClientSettingsChanged(execution_delegate_->GetClientSettings());
Java_AssistantModel_setPeekModeDisabled(env, GetModel(), false);
@@ -642,6 +644,8 @@ void UiControllerAndroid::RestoreUi() {
OnDetailsChanged(ui_delegate_->GetDetails());
OnUserActionsChanged(ui_delegate_->GetUserActions());
OnCollectUserDataOptionsChanged(ui_delegate_->GetCollectUserDataOptions());
+ OnCollectUserDataUiStateChanged(/* loading= */ false,
+ UserDataEventField::NONE);
OnUserDataChanged(*execution_delegate_->GetUserData(),
UserDataFieldChange::ALL);
OnPersistentGenericUserInterfaceChanged(
@@ -803,13 +807,13 @@ void UiControllerAndroid::UpdateActions(
jcancel_chip = Java_AutofillAssistantUiController_createCloseButton(
env, java_object_, ICON_CLEAR, ConvertUTF8ToJavaString(env, ""),
/* disabled= */ false, /* sticky= */ true, /* visible=*/true,
- /* content_description= */ nullptr);
+ /* contentDescription= */ nullptr);
} else if (execution_delegate_->GetState() !=
AutofillAssistantState::INACTIVE) {
jcancel_chip = Java_AutofillAssistantUiController_createCancelButton(
env, java_object_, ICON_CLEAR, ConvertUTF8ToJavaString(env, ""), -1,
/* disabled= */ false, /* sticky= */ true, /* visible=*/true,
- /* content_description= */ nullptr);
+ /* contentDescription= */ nullptr);
}
if (jcancel_chip) {
Java_AutofillAssistantUiController_appendChipToList(env, jchips,
@@ -1177,8 +1181,8 @@ void UiControllerAndroid::OnCollectUserDataOptionsChanged(
Java_AssistantCollectUserDataModel_setShouldStoreUserDataChanges(
env, jmodel, collect_user_data_options->should_store_data_changes);
- Java_AssistantCollectUserDataModel_setUseGmsCoreEditDialogs(
- env, jmodel, collect_user_data_options->use_gms_core_edit_dialogs);
+ Java_AssistantCollectUserDataModel_setUseAlternativeEditDialogs(
+ env, jmodel, collect_user_data_options->use_alternative_edit_dialogs);
Java_AssistantCollectUserDataModel_setRequestName(
env, jmodel, collect_user_data_options->request_payer_name);
Java_AssistantCollectUserDataModel_setRequestEmail(
@@ -1238,24 +1242,12 @@ void UiControllerAndroid::OnCollectUserDataOptionsChanged(
base::android::ToJavaArrayOfStrings(
env, collect_user_data_options->supported_basic_card_networks));
if (collect_user_data_options->data_origin_notice) {
- Java_AssistantCollectUserDataModel_setDataOriginLinkText(
- env, jmodel,
- ConvertUTF8ToJavaString(
- env, collect_user_data_options->data_origin_notice->link_text()));
- Java_AssistantCollectUserDataModel_setDataOriginDialogTitle(
- env, jmodel,
- ConvertUTF8ToJavaString(
- env,
- collect_user_data_options->data_origin_notice->dialog_title()));
- Java_AssistantCollectUserDataModel_setDataOriginDialogText(
- env, jmodel,
- ConvertUTF8ToJavaString(
- env, collect_user_data_options->data_origin_notice->dialog_text()));
- Java_AssistantCollectUserDataModel_setDataOriginDialogButtonText(
- env, jmodel,
- ConvertUTF8ToJavaString(env,
- collect_user_data_options->data_origin_notice
- ->dialog_button_text()));
+ const auto& configuration = *collect_user_data_options->data_origin_notice;
+ Java_AssistantCollectUserDataModel_setDataOriginNoticeConfiguration(
+ env, jmodel, ConvertUTF8ToJavaString(env, configuration.link_text()),
+ ConvertUTF8ToJavaString(env, configuration.dialog_title()),
+ ConvertUTF8ToJavaString(env, configuration.dialog_text()),
+ ConvertUTF8ToJavaString(env, configuration.dialog_button_text()));
}
if (collect_user_data_options->request_login_choice) {
auto jlist = CreateJavaLoginChoiceList(
@@ -1323,6 +1315,27 @@ void UiControllerAndroid::OnCollectUserDataOptionsChanged(
Java_AssistantCollectUserDataModel_setVisible(env, jmodel, true);
}
+void UiControllerAndroid::OnCollectUserDataUiStateChanged(
+ bool loading,
+ UserDataEventField event_field) {
+ JNIEnv* env = AttachCurrentThread();
+ auto jmodel = GetCollectUserDataModel();
+
+ Java_AssistantCollectUserDataModel_setEnableUiInteractions(env, jmodel,
+ !loading);
+ Java_AssistantCollectUserDataModel_setMarkContactsLoading(
+ env, jmodel, loading && event_field == UserDataEventField::CONTACT_EVENT);
+ Java_AssistantCollectUserDataModel_setMarkPhoneNumbersLoading(
+ env, jmodel,
+ loading && event_field == UserDataEventField::PHONE_NUMBER_EVENT);
+ Java_AssistantCollectUserDataModel_setMarkShippingAddressesLoading(
+ env, jmodel,
+ loading && event_field == UserDataEventField::SHIPPING_EVENT);
+ Java_AssistantCollectUserDataModel_setMarkPaymentMethodsLoading(
+ env, jmodel,
+ loading && event_field == UserDataEventField::CREDIT_CARD_EVENT);
+}
+
void UiControllerAndroid::OnUserDataChanged(const UserData& user_data,
UserDataFieldChange field_change) {
DCHECK(execution_delegate_ != nullptr);
@@ -1362,7 +1375,7 @@ void UiControllerAndroid::OnUserDataChanged(const UserData& user_data,
env, *user_data.available_contacts_[index]->profile,
base::android::GetDefaultLocaleString()),
base::android::ToJavaArrayOfStrings(env, errors),
- collect_user_data_options->can_edit_contacts);
+ user_data.available_contacts_[index]->can_edit);
}
Java_AssistantCollectUserDataModel_setAvailableContacts(env, jmodel,
jcontactlist);
@@ -1409,7 +1422,7 @@ void UiControllerAndroid::OnUserDataChanged(const UserData& user_data,
const auto& errors = user_data::GetShippingAddressValidationErrors(
shipping_address->profile.get(), *collect_user_data_options);
auto jedit_token =
- collect_user_data_options->use_gms_core_edit_dialogs
+ collect_user_data_options->use_alternative_edit_dialogs
? ToJavaByteArray(
env, shipping_address->edit_token.value_or(std::string()))
: nullptr;
@@ -1443,13 +1456,26 @@ void UiControllerAndroid::OnUserDataChanged(const UserData& user_data,
const auto& selected_contact_errors = user_data::GetContactValidationErrors(
selected_contact_profile, *collect_user_data_options);
+ bool can_edit = true;
+ if (selected_contact_profile) {
+ const auto& contact_it = base::ranges::find_if(
+ user_data.available_contacts_,
+ [&](const std::unique_ptr<Contact>& contact) {
+ return contact->profile &&
+ contact->profile->guid() == selected_contact_profile->guid();
+ });
+ if (contact_it != user_data.available_contacts_.end()) {
+ can_edit = (*contact_it)->can_edit;
+ }
+ }
+
// In the UserDataFieldChange::CONTACT_PROFILE case the selection is
// already known in Java, but it has no errors. The PDM off case does not
// set updated contacts.
Java_AssistantCollectUserDataModel_setSelectedContactDetails(
env, jmodel, jselected_contact,
base::android::ToJavaArrayOfStrings(env, selected_contact_errors),
- collect_user_data_options->can_edit_contacts);
+ can_edit);
}
if (field_change == UserDataFieldChange::ALL ||
@@ -1467,13 +1493,26 @@ void UiControllerAndroid::OnUserDataChanged(const UserData& user_data,
user_data::GetPhoneNumberValidationErrors(selected_phone_number,
*collect_user_data_options);
+ bool can_edit = true;
+ if (selected_phone_number) {
+ const auto& phone_number_it = base::ranges::find_if(
+ user_data.available_phone_numbers_,
+ [&](const std::unique_ptr<PhoneNumber>& phone_number) {
+ return phone_number->profile && phone_number->profile->guid() ==
+ selected_phone_number->guid();
+ });
+ if (phone_number_it != user_data.available_phone_numbers_.end()) {
+ can_edit = (*phone_number_it)->can_edit;
+ }
+ }
+
// In the UserDataFieldChange::PHONE_NUMBER case the selection is already
// known in Java, but it has no errors. The PDM off case does not set
// updated phone numbers.
Java_AssistantCollectUserDataModel_setSelectedPhoneNumber(
env, jmodel, jselected_phone_number,
base::android::ToJavaArrayOfStrings(env, selected_phone_number_errors),
- /* canEdit = */ false);
+ can_edit);
}
if (field_change == UserDataFieldChange::ALL ||
@@ -1492,7 +1531,7 @@ void UiControllerAndroid::OnUserDataChanged(const UserData& user_data,
user_data::GetShippingAddressValidationErrors(
selected_shipping_address, *collect_user_data_options);
auto jselected_shipping_address_edit_token =
- collect_user_data_options->use_gms_core_edit_dialogs
+ collect_user_data_options->use_alternative_edit_dialogs
? ToJavaByteArray(env, std::string())
: nullptr;
if (selected_shipping_address) {
@@ -1545,7 +1584,7 @@ void UiControllerAndroid::OnUserDataChanged(const UserData& user_data,
instrument->card.get(), instrument->billing_address.get(),
*collect_user_data_options);
auto jedit_token =
- collect_user_data_options->use_gms_core_edit_dialogs
+ collect_user_data_options->use_alternative_edit_dialogs
? ToJavaByteArray(env,
instrument->edit_token.value_or(std::string()))
: nullptr;
@@ -1588,7 +1627,7 @@ void UiControllerAndroid::OnUserDataChanged(const UserData& user_data,
selected_card, selected_billing_address,
*collect_user_data_options);
auto jselected_payment_instrument_edit_token =
- collect_user_data_options->use_gms_core_edit_dialogs
+ collect_user_data_options->use_alternative_edit_dialogs
? ToJavaByteArray(env, std::string())
: nullptr;
if (selected_card) {
@@ -1991,7 +2030,6 @@ void UiControllerAndroid::OnStart(const TriggerContext& trigger_context) {}
void UiControllerAndroid::OnStop() {}
void UiControllerAndroid::OnResetState() {}
void UiControllerAndroid::OnUiShownChanged(bool shown) {}
-void UiControllerAndroid::OnShutdown(Metrics::DropOutReason reason) {}
base::android::ScopedJavaLocalRef<jobject>
UiControllerAndroid::GetGenericUiModel() {
diff --git a/chromium/components/autofill_assistant/browser/android/ui_controller_android.h b/chromium/components/autofill_assistant/browser/android/ui_controller_android.h
index 21a4b6ce263..563ca9e418a 100644
--- a/chromium/components/autofill_assistant/browser/android/ui_controller_android.h
+++ b/chromium/components/autofill_assistant/browser/android/ui_controller_android.h
@@ -19,7 +19,7 @@
#include "components/autofill_assistant/browser/android/assistant_header_delegate.h"
#include "components/autofill_assistant/browser/android/assistant_header_model.h"
#include "components/autofill_assistant/browser/android/assistant_overlay_delegate.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/chip.h"
#include "components/autofill_assistant/browser/controller_observer.h"
#include "components/autofill_assistant/browser/details.h"
@@ -54,6 +54,7 @@ class UiControllerAndroid : public ControllerObserver, UiControllerObserver {
UiControllerAndroid(
JNIEnv* env,
+ content::WebContents* web_contents,
const base::android::JavaRef<jobject>& jdependencies,
const base::android::JavaRef<jobject>& joverlay_coordinator);
@@ -118,7 +119,6 @@ class UiControllerAndroid : public ControllerObserver, UiControllerObserver {
void OnStop() override;
void OnResetState() override;
void OnUiShownChanged(bool shown) override;
- void OnShutdown(Metrics::DropOutReason reason) override;
// Overrides UiControllerObserver:
void OnStatusMessageChanged(const std::string& message) override;
@@ -126,6 +126,8 @@ class UiControllerAndroid : public ControllerObserver, UiControllerObserver {
void OnUserActionsChanged(const std::vector<UserAction>& actions) override;
void OnCollectUserDataOptionsChanged(
const CollectUserDataOptions* collect_user_data_options) override;
+ void OnCollectUserDataUiStateChanged(bool loading,
+ UserDataEventField event_field) override;
void OnDetailsChanged(const std::vector<Details>& details) override;
void OnInfoBoxChanged(const InfoBox* info_box) override;
void OnProgressActiveStepChanged(int active_step) override;
@@ -308,7 +310,7 @@ class UiControllerAndroid : public ControllerObserver, UiControllerObserver {
// Java-side AssistantStaticDependencies object. This never changes during the
// life of the application.
- const std::unique_ptr<const Dependencies> dependencies_;
+ const std::unique_ptr<const DependenciesAndroid> dependencies_;
// Native controllers for generic UI.
std::unique_ptr<GenericUiRootControllerAndroid>
diff --git a/chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.cc b/chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.cc
index 0b4f2b6d36a..2193988eea8 100644
--- a/chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.cc
+++ b/chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.cc
@@ -25,7 +25,7 @@
#include "components/autofill_assistant/android/jni_headers_public/AssistantAutofillCreditCard_jni.h"
#include "components/autofill_assistant/android/jni_headers_public/AssistantAutofillProfile_jni.h"
#include "components/autofill_assistant/browser/android/client_android.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/generic_ui_java_generated_enums.h"
#include "components/autofill_assistant/browser/service/service.h"
#include "components/autofill_assistant/browser/service/service_request_sender.h"
@@ -172,7 +172,7 @@ int GetPixelSizeOrDefault(JNIEnv* env,
base::android::ScopedJavaLocalRef<jobject> CreateJavaDrawable(
JNIEnv* env,
const JavaRef<jobject>& jcontext,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
const DrawableProto& proto,
const UserModel* user_model) {
switch (proto.drawable_case()) {
@@ -724,7 +724,8 @@ base::android::ScopedJavaLocalRef<jobject> CreateAssistantAutofillCreditCard(
credit_card.instrument_id(),
ConvertUTF16ToJavaString(env, credit_card.nickname()),
url::GURLAndroid::FromNativeGURL(env, credit_card.card_art_url()),
- static_cast<jint>(credit_card.virtual_card_enrollment_state()));
+ static_cast<jint>(credit_card.virtual_card_enrollment_state()),
+ ConvertUTF16ToJavaString(env, credit_card.product_description()));
}
void PopulateAutofillCreditCardFromJava(
@@ -792,6 +793,9 @@ void PopulateAutofillCreditCardFromJava(
static_cast<autofill::CreditCard::VirtualCardEnrollmentState>(
Java_AssistantAutofillCreditCard_getVirtualCardEnrollmentState(
env, jcredit_card)));
+ credit_card->set_product_description(ConvertJavaStringToUTF16(
+ Java_AssistantAutofillCreditCard_getProductDescription(env,
+ jcredit_card)));
}
} // namespace ui_controller_android_utils
diff --git a/chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.h b/chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.h
index 8bb0b256664..4519ae5839a 100644
--- a/chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.h
+++ b/chromium/components/autofill_assistant/browser/android/ui_controller_android_utils.h
@@ -12,7 +12,7 @@
#include "base/containers/flat_map.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/autofill_assistant/browser/android/dependencies.h"
+#include "components/autofill_assistant/browser/android/dependencies_android.h"
#include "components/autofill_assistant/browser/autofill_assistant_tts_controller.h"
#include "components/autofill_assistant/browser/bottom_sheet_state.h"
#include "components/autofill_assistant/browser/service.pb.h"
@@ -63,7 +63,7 @@ int GetPixelSizeOrDefault(JNIEnv* env,
base::android::ScopedJavaLocalRef<jobject> CreateJavaDrawable(
JNIEnv* env,
const base::android::JavaRef<jobject>& jcontext,
- const Dependencies& dependencies,
+ const DependenciesAndroid& dependencies,
const DrawableProto& proto,
const UserModel* user_model = nullptr);
diff --git a/chromium/components/autofill_assistant/browser/autofill_assistant_factory.cc b/chromium/components/autofill_assistant/browser/autofill_assistant_factory.cc
index 425214f1bdc..c3161c808ca 100644
--- a/chromium/components/autofill_assistant/browser/autofill_assistant_factory.cc
+++ b/chromium/components/autofill_assistant/browser/autofill_assistant_factory.cc
@@ -4,8 +4,10 @@
#include "components/autofill_assistant/browser/public/autofill_assistant_factory.h"
+#include <memory>
+
#include "components/autofill_assistant/browser/autofill_assistant_impl.h"
-#include "components/version_info/channel.h"
+#include "components/autofill_assistant/browser/common_dependencies.h"
namespace autofill_assistant {
@@ -13,11 +15,9 @@ namespace autofill_assistant {
std::unique_ptr<AutofillAssistant>
AutofillAssistantFactory::CreateForBrowserContext(
content::BrowserContext* browser_context,
- version_info::Channel channel,
- const std::string& country_code,
- const std::string& locale) {
- return AutofillAssistantImpl::Create(browser_context, channel, country_code,
- locale);
+ std::unique_ptr<CommonDependencies> dependencies) {
+ return AutofillAssistantImpl::Create(browser_context,
+ std::move(dependencies));
}
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/autofill_assistant_impl.cc b/chromium/components/autofill_assistant/browser/autofill_assistant_impl.cc
index aa09db0d553..f4cd369d786 100644
--- a/chromium/components/autofill_assistant/browser/autofill_assistant_impl.cc
+++ b/chromium/components/autofill_assistant/browser/autofill_assistant_impl.cc
@@ -4,8 +4,10 @@
#include "components/autofill_assistant/browser/autofill_assistant_impl.h"
+#include <memory>
#include <vector>
+#include "components/autofill_assistant/browser/common_dependencies.h"
#include "components/autofill_assistant/browser/desktop/starter_delegate_desktop.h"
#include "components/autofill_assistant/browser/headless/external_script_controller_impl.h"
#include "components/autofill_assistant/browser/protocol_utils.h"
@@ -16,7 +18,6 @@
#include "components/autofill_assistant/browser/service/service_request_sender.h"
#include "components/autofill_assistant/browser/service/service_request_sender_impl.h"
#include "components/autofill_assistant/browser/service/simple_url_loader_factory.h"
-#include "components/version_info/channel.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_context.h"
#include "net/http/http_status_code.h"
@@ -68,31 +69,30 @@ void OnCapabilitiesResponse(
// static
std::unique_ptr<AutofillAssistantImpl> AutofillAssistantImpl::Create(
content::BrowserContext* browser_context,
- version_info::Channel channel,
- const std::string& country_code,
- const std::string& locale) {
+ std::unique_ptr<CommonDependencies> dependencies) {
auto request_sender = std::make_unique<ServiceRequestSenderImpl>(
browser_context,
/* access_token_fetcher = */ nullptr,
std::make_unique<cup::CUPImplFactory>(),
std::make_unique<NativeURLLoaderFactory>(),
- ApiKeyFetcher().GetAPIKey(channel));
+ ApiKeyFetcher().GetAPIKey(dependencies->GetChannel()));
const ServerUrlFetcher& url_fetcher =
ServerUrlFetcher(ServerUrlFetcher::GetDefaultServerUrl());
+
return std::make_unique<AutofillAssistantImpl>(
- std::move(request_sender), url_fetcher.GetCapabilitiesByHashEndpoint(),
- country_code, locale);
+ browser_context, std::move(request_sender), std::move(dependencies),
+ url_fetcher.GetCapabilitiesByHashEndpoint());
}
AutofillAssistantImpl::AutofillAssistantImpl(
+ content::BrowserContext* browser_context,
std::unique_ptr<ServiceRequestSender> request_sender,
- const GURL& script_server_url,
- const std::string& country_code,
- const std::string& locale)
- : request_sender_(std::move(request_sender)),
+ std::unique_ptr<CommonDependencies> dependencies,
+ const GURL& script_server_url)
+ : browser_context_(browser_context),
+ request_sender_(std::move(request_sender)),
script_server_url_(script_server_url),
- country_code_(country_code),
- locale_(locale) {}
+ dependencies_(std::move(dependencies)) {}
AutofillAssistantImpl::~AutofillAssistantImpl() = default;
@@ -101,16 +101,30 @@ void AutofillAssistantImpl::GetCapabilitiesByHashPrefix(
const std::vector<uint64_t>& hash_prefixes,
const std::string& intent,
GetCapabilitiesResponseCallback callback) {
+ // Always return an empty response for supervised users.
+ if (dependencies_->IsSupervisedUser(browser_context_)) {
+ std::move(callback).Run(net::HTTP_OK, {});
+ return;
+ }
+
const ScriptParameters& parameters = {
base::flat_map<std::string, std::string>{
{kIntentScriptParameterKey, intent}}};
ClientContextProto client_context;
- client_context.set_country(country_code_);
- client_context.set_locale(locale_);
+ client_context.set_country(dependencies_->GetCountryCode());
+ client_context.set_locale(dependencies_->GetLocale());
client_context.mutable_chrome()->set_chrome_version(
version_info::GetProductNameAndVersionForUserAgent());
+#if BUILDFLAG(IS_ANDROID)
+ client_context.set_platform_type(ClientContextProto::PLATFORM_TYPE_ANDROID);
+#endif
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
+ BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
+ client_context.set_platform_type(ClientContextProto::PLATFORM_TYPE_DESKTOP);
+#endif
+
request_sender_->SendRequest(
script_server_url_,
ProtocolUtils::CreateCapabilitiesByHashRequest(
@@ -118,13 +132,14 @@ void AutofillAssistantImpl::GetCapabilitiesByHashPrefix(
ServiceRequestSender::AuthMode::API_KEY,
base::BindOnce(&OnCapabilitiesResponse, std::move(callback)),
RpcType::GET_CAPABILITIES_BY_HASH_PREFIX);
- return;
}
std::unique_ptr<ExternalScriptController>
AutofillAssistantImpl::CreateExternalScriptController(
- content::WebContents* web_contents) {
- return std::make_unique<ExternalScriptControllerImpl>(web_contents);
+ content::WebContents* web_contents,
+ ExternalActionDelegate* action_extension_delegate) {
+ return std::make_unique<ExternalScriptControllerImpl>(
+ web_contents, action_extension_delegate);
}
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/autofill_assistant_impl.h b/chromium/components/autofill_assistant/browser/autofill_assistant_impl.h
index c973ecf1ce9..1d12585b564 100644
--- a/chromium/components/autofill_assistant/browser/autofill_assistant_impl.h
+++ b/chromium/components/autofill_assistant/browser/autofill_assistant_impl.h
@@ -5,31 +5,37 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_AUTOFILL_ASSISTANT_IMPL_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_AUTOFILL_ASSISTANT_IMPL_H_
+#include <memory>
#include <vector>
+#include "base/memory/raw_ptr.h"
#include "components/autofill_assistant/browser/public/autofill_assistant.h"
#include "components/autofill_assistant/browser/public/external_script_controller.h"
#include "components/autofill_assistant/browser/service/service_request_sender.h"
-#include "components/version_info/version_info.h"
-#include "content/public/browser/browser_context.h"
+
+namespace content {
+class BrowserContext;
+class WebContents;
+} // namespace content
namespace autofill_assistant {
+class CommonDependencies;
+
class AutofillAssistantImpl : public autofill_assistant::AutofillAssistant {
public:
static std::unique_ptr<AutofillAssistantImpl> Create(
content::BrowserContext* browser_context,
- version_info::Channel channel,
- const std::string& country_code,
- const std::string& locale);
-
- AutofillAssistantImpl(std::unique_ptr<ServiceRequestSender> request_sender,
- const GURL& script_server_url,
- const std::string& country_code,
- const std::string& locale);
+ std::unique_ptr<CommonDependencies> dependencies);
+
+ AutofillAssistantImpl(content::BrowserContext* browser_context,
+ std::unique_ptr<ServiceRequestSender> request_sender,
+ std::unique_ptr<CommonDependencies> dependencies,
+ const GURL& script_server_url);
+ ~AutofillAssistantImpl() override;
+
AutofillAssistantImpl(const AutofillAssistantImpl&) = delete;
AutofillAssistantImpl& operator=(const AutofillAssistantImpl&) = delete;
- ~AutofillAssistantImpl() override;
void GetCapabilitiesByHashPrefix(
uint32_t hash_prefix_length,
@@ -38,17 +44,22 @@ class AutofillAssistantImpl : public autofill_assistant::AutofillAssistant {
GetCapabilitiesResponseCallback callback) override;
std::unique_ptr<ExternalScriptController> CreateExternalScriptController(
- content::WebContents* web_contents) override;
+ content::WebContents* web_contents,
+ ExternalActionDelegate* action_extension_delegate) override;
private:
+ // The `BrowserContext` for which this `AutofillAssistantImpl` was created
+ // and which must outlive it.
+ const raw_ptr<content::BrowserContext> browser_context_;
+
// The request sender responsible for communicating with a remote endpoint.
std::unique_ptr<ServiceRequestSender> request_sender_;
+
// The RPC endpoint to send requests to.
GURL script_server_url_;
- // The client's country code.
- std::string country_code_;
- // The client's locale.
- std::string locale_;
+
+ // Dependencies on client code such as country code or locale.
+ std::unique_ptr<CommonDependencies> dependencies_;
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/autofill_assistant_impl_unittest.cc b/chromium/components/autofill_assistant/browser/autofill_assistant_impl_unittest.cc
index 65d49dd0f99..9e6e5ef71e4 100644
--- a/chromium/components/autofill_assistant/browser/autofill_assistant_impl_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/autofill_assistant_impl_unittest.cc
@@ -7,6 +7,7 @@
#include "base/memory/raw_ptr.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
+#include "components/autofill_assistant/browser/mock_common_dependencies.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/service/mock_service_request_sender.h"
#include "components/version_info/version_info.h"
@@ -32,8 +33,18 @@ class AutofillAssistantImpTest : public testing::Test {
std::make_unique<NiceMock<MockServiceRequestSender>>();
mock_request_sender_ = mock_request_sender.get();
+ auto mock_common_dependencies = std::make_unique<MockCommonDependencies>();
+ mock_dependencies_ = mock_common_dependencies.get();
+ ON_CALL(*mock_dependencies_, GetCountryCode).WillByDefault(Return("US"));
+ ON_CALL(*mock_dependencies_, GetLocale).WillByDefault(Return("en-US"));
+ ON_CALL(*mock_dependencies_, IsSupervisedUser).WillByDefault(Return(false));
+
+ // As long as the `BrowserContext` is only passed as an argument during
+ // `CommonDependencies` calls, we do not need to set up a test environment
+ // for it.
service_ = std::make_unique<AutofillAssistantImpl>(
- std::move(mock_request_sender), GURL(kScriptServerUrl), "US", "en-US");
+ /* browser_context= */ nullptr, std::move(mock_request_sender),
+ std::move(mock_common_dependencies), GURL(kScriptServerUrl));
}
~AutofillAssistantImpTest() override = default;
@@ -41,6 +52,7 @@ class AutofillAssistantImpTest : public testing::Test {
base::MockCallback<AutofillAssistant::GetCapabilitiesResponseCallback>
mock_response_callback_;
raw_ptr<NiceMock<MockServiceRequestSender>> mock_request_sender_;
+ raw_ptr<MockCommonDependencies> mock_dependencies_;
std::unique_ptr<AutofillAssistantImpl> service_;
};
@@ -132,4 +144,18 @@ TEST_F(AutofillAssistantImpTest, GetCapabilitiesByHashPrefix) {
mock_response_callback_.Get());
}
+TEST_F(AutofillAssistantImpTest,
+ GetCapabilitiesByHashPrefixDoesNotExecuteForSupervisedUsers) {
+ EXPECT_CALL(*mock_dependencies_, IsSupervisedUser).WillOnce(Return(true));
+
+ EXPECT_CALL(*mock_request_sender_, OnSendRequest).Times(0);
+
+ EXPECT_CALL(
+ mock_response_callback_,
+ Run(net::HTTP_OK, std::vector<AutofillAssistant::CapabilitiesInfo>()));
+
+ service_->GetCapabilitiesByHashPrefix(16, {1339}, "DUMMY_INTENT",
+ mock_response_callback_.Get());
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/autofill_assistant_tts_controller_unittest.cc b/chromium/components/autofill_assistant/browser/autofill_assistant_tts_controller_unittest.cc
index fe18483994b..0e4f322768c 100644
--- a/chromium/components/autofill_assistant/browser/autofill_assistant_tts_controller_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/autofill_assistant_tts_controller_unittest.cc
@@ -44,6 +44,7 @@ class MockTtsController : public content::TtsController {
content::TtsEngineDelegate* GetTtsEngineDelegate() override {
return nullptr;
}
+ void RefreshVoices() override {}
void SetTtsPlatform(content::TtsPlatform* tts_platform) override {}
int QueueSize() override { return 0; }
void StripSSML(
diff --git a/chromium/components/autofill_assistant/browser/basic_interactions.cc b/chromium/components/autofill_assistant/browser/basic_interactions.cc
index 5f6d8ab32c2..56f3a5416a8 100644
--- a/chromium/components/autofill_assistant/browser/basic_interactions.cc
+++ b/chromium/components/autofill_assistant/browser/basic_interactions.cc
@@ -136,14 +136,20 @@ bool ValueToString(UserModel* user_model,
return false;
}
auto date = value->dates().values(i);
+
+ // Technically we are setting the wrong |day_of_week|, but it's ignored
+ // in practice and the formatted string will have the correct day for
+ // the date. Setting an invalid value here (e.g. -1) causes issues on
+ // Windows.
base::Time::Exploded exploded_time = {static_cast<int>(date.year()),
date.month(),
- /* day_of_week = */ -1,
+ /* day_of_week = */ 0,
date.day(),
/* hour = */ 0,
/* minute = */ 0,
/* second = */ 0,
/* millisecond = */ 0};
+
base::Time time;
if (!base::Time::FromLocalExploded(exploded_time, &time)) {
DVLOG(2) << "Error evaluating " << __func__ << ": invalid date "
diff --git a/chromium/components/autofill_assistant/browser/batch_element_checker.cc b/chromium/components/autofill_assistant/browser/batch_element_checker.cc
index d76adcc46e6..4dd8817c36d 100644
--- a/chromium/components/autofill_assistant/browser/batch_element_checker.cc
+++ b/chromium/components/autofill_assistant/browser/batch_element_checker.cc
@@ -15,7 +15,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/autofill_assistant/browser/web/element_action_util.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/selector_observer.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
@@ -76,19 +76,13 @@ void BatchElementChecker::AddAllDoneCallback(
}
void BatchElementChecker::EnableObserver(
- base::TimeDelta max_wait_time,
- base::TimeDelta periodic_check_interval,
- base::TimeDelta extra_timeout) {
- DCHECK(!use_observers_);
+ const SelectorObserver::Settings& settings) {
+ DCHECK(!observer_settings_);
DCHECK(!started_);
DCHECK(get_field_value_callbacks_.empty())
<< "Observer-based BatchElementChecker doesn't work with "
"AddFieldValueCheck";
-
- use_observers_ = true;
- observer_max_wait_time_ = max_wait_time;
- observer_periodic_check_interval_ = periodic_check_interval;
- observer_extra_timeout_ = extra_timeout;
+ observer_settings_.emplace(settings);
}
void BatchElementChecker::Run(WebController* web_controller) {
@@ -97,7 +91,7 @@ void BatchElementChecker::Run(WebController* web_controller) {
for (size_t i = 0; i < element_condition_checks_.size(); ++i) {
AddElementConditionResults(element_condition_checks_[i].proto, i);
}
- if (use_observers_) {
+ if (observer_settings_) {
RunWithObserver(web_controller);
return;
}
@@ -149,6 +143,7 @@ void BatchElementChecker::RunWithObserver(WebController* web_controller) {
DCHECK(get_field_value_callbacks_.empty())
<< "Observer-based BatchElementChecker doesn't work with "
"AddFieldValueCheck";
+ DCHECK(observer_settings_);
DCHECK(!started_);
std::vector<SelectorObserver::ObservableSelector> selectors;
@@ -164,8 +159,7 @@ void BatchElementChecker::RunWithObserver(WebController* web_controller) {
}
started_ = true;
auto result = web_controller->ObserveSelectors(
- selectors, observer_max_wait_time_, observer_periodic_check_interval_,
- observer_extra_timeout_,
+ selectors, *observer_settings_,
base::BindRepeating(&BatchElementChecker::OnResultsUpdated,
weak_ptr_factory_.GetWeakPtr())
diff --git a/chromium/components/autofill_assistant/browser/batch_element_checker.h b/chromium/components/autofill_assistant/browser/batch_element_checker.h
index 9b9067b79ed..9052da9244b 100644
--- a/chromium/components/autofill_assistant/browser/batch_element_checker.h
+++ b/chromium/components/autofill_assistant/browser/batch_element_checker.h
@@ -20,10 +20,10 @@
#include "components/autofill_assistant/browser/selector.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/web/element.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
#include "components/autofill_assistant/browser/web/selector_observer.h"
namespace autofill_assistant {
+class ElementFinderResult;
class WebController;
// Helper for checking a set of elements at the same time. It avoids duplicate
@@ -84,9 +84,7 @@ class BatchElementChecker {
// Turns on observer mode. When BatchElementChecker runs in observer mode, it
// waits until any element condition checks or element checks become true.
- void EnableObserver(base::TimeDelta max_wait_time,
- base::TimeDelta periodic_check_interval,
- base::TimeDelta extra_timeout);
+ void EnableObserver(const SelectorObserver::Settings& settings);
// Runs the checks. Once all checks are done, calls the callbacks registered
// to AddAllDoneCallback().
@@ -207,11 +205,10 @@ class BatchElementChecker {
// Run() was called. Checking elements might or might not have finished yet.
bool started_ = false;
- // Whether to wait until one of the conditions becomes true.
- bool use_observers_ = false;
- base::TimeDelta observer_max_wait_time_;
- base::TimeDelta observer_periodic_check_interval_;
- base::TimeDelta observer_extra_timeout_;
+ // Whether to wait until one of the conditions becomes true. If it's a nullopt
+ // it will check only once, if it has a value it will observe the DOM until
+ // one of the conditions becomes true or the observation times out.
+ absl::optional<const SelectorObserver::Settings> observer_settings_;
std::vector<base::OnceCallback<void()>> all_done_;
diff --git a/chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc b/chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc
index 48e52d40d3e..7b02f501e4e 100644
--- a/chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc
@@ -12,7 +12,7 @@
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "components/autofill_assistant/browser/actions/action_test_utils.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/autofill_assistant/browser/client.h b/chromium/components/autofill_assistant/browser/client.h
index aff4d5916ab..8e597c1defd 100644
--- a/chromium/components/autofill_assistant/browser/client.h
+++ b/chromium/components/autofill_assistant/browser/client.h
@@ -61,7 +61,7 @@ class Client {
// Returns the e-mail address used to sign into Chrome, or an empty string if
// the user is not signed in.
- virtual std::string GetChromeSignedInEmailAddress() const = 0;
+ virtual std::string GetSignedInEmail() const = 0;
// Returns the AccessTokenFetcher to use to get oauth credentials.
virtual AccessTokenFetcher* GetAccessTokenFetcher() = 0;
@@ -122,6 +122,14 @@ class Client {
// nullptr.
virtual ScriptExecutorUiDelegate* GetScriptExecutorUiDelegate() = 0;
+ // Returns whether or not this instance of Autofill Assistant must use a
+ // backend endpoint to query data.
+ virtual bool MustUseBackendData() const = 0;
+
+ // Return the annotate DOM model version, if available.
+ virtual void GetAnnotateDomModelVersion(
+ base::OnceCallback<void(absl::optional<int64_t>)> callback) const = 0;
+
protected:
Client() = default;
};
diff --git a/chromium/components/autofill_assistant/browser/client_context.cc b/chromium/components/autofill_assistant/browser/client_context.cc
index e3cbab6677e..35589ce16d8 100644
--- a/chromium/components/autofill_assistant/browser/client_context.cc
+++ b/chromium/components/autofill_assistant/browser/client_context.cc
@@ -14,6 +14,15 @@ ClientContextImpl::ClientContextImpl(const Client* client) : client_(client) {
version_info::GetProductNameAndVersionForUserAgent());
proto_.set_locale(client->GetLocale());
proto_.set_country(client->GetCountryCode());
+// TODO(crbug.com/1321034): Once PlatformDependencies exist and are exposed to
+// |Client|, move this check to calls of type |client->IsDesktop()|.
+#if BUILDFLAG(IS_ANDROID)
+ proto_.set_platform_type(ClientContextProto::PLATFORM_TYPE_ANDROID);
+#endif
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
+ BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
+ proto_.set_platform_type(ClientContextProto::PLATFORM_TYPE_DESKTOP);
+#endif
base::FieldTrial::ActiveGroups active_groups;
base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
@@ -30,9 +39,8 @@ ClientContextImpl::ClientContextImpl(const Client* client) : client_(client) {
void ClientContextImpl::Update(const TriggerContext& trigger_context) {
proto_.set_accessibility_enabled(client_->IsAccessibilityEnabled());
- std::string chrome_signed_in_email_address =
- client_->GetChromeSignedInEmailAddress();
- proto_.set_signed_into_chrome_status(chrome_signed_in_email_address.empty()
+ const std::string signed_in_email = client_->GetSignedInEmail();
+ proto_.set_signed_into_chrome_status(signed_in_email.empty()
? ClientContextProto::NOT_SIGNED_IN
: ClientContextProto::SIGNED_IN);
@@ -58,7 +66,7 @@ void ClientContextImpl::Update(const TriggerContext& trigger_context) {
if (!caller_email.has_value()) {
proto_.set_accounts_matching_status(ClientContextProto::UNKNOWN);
} else {
- if (chrome_signed_in_email_address == caller_email) {
+ if (signed_in_email == caller_email) {
proto_.set_accounts_matching_status(
ClientContextProto::ACCOUNTS_MATCHING);
} else {
@@ -78,6 +86,15 @@ void ClientContextImpl::Update(const TriggerContext& trigger_context) {
proto_.set_screen_orientation(client_->GetScreenOrientation());
}
+void ClientContextImpl::UpdateAnnotateDomModelContext(int64_t model_version) {
+ proto_.mutable_annotate_dom_model_context()->set_model_version(model_version);
+}
+
+void ClientContextImpl::UpdateJsFlowLibraryLoaded(
+ const bool js_flow_library_loaded) {
+ proto_.set_js_flow_library_loaded(js_flow_library_loaded);
+}
+
ClientContextProto ClientContextImpl::AsProto() const {
return proto_;
}
diff --git a/chromium/components/autofill_assistant/browser/client_context.h b/chromium/components/autofill_assistant/browser/client_context.h
index e6cede8ac45..64665a00aa1 100644
--- a/chromium/components/autofill_assistant/browser/client_context.h
+++ b/chromium/components/autofill_assistant/browser/client_context.h
@@ -18,6 +18,10 @@ class ClientContext {
virtual ~ClientContext() = default;
// Updates the client context based on the current state of the client.
virtual void Update(const TriggerContext& trigger_context) = 0;
+ // Updates the annotate DOM model context.
+ virtual void UpdateAnnotateDomModelContext(int64_t model_version) {}
+ // Updates whether the JS flow library is loaded.
+ virtual void UpdateJsFlowLibraryLoaded(bool js_flow_library_loaded){};
// Returns the proto representation of this client context.
virtual ClientContextProto AsProto() const = 0;
};
@@ -26,9 +30,11 @@ class ClientContext {
class ClientContextImpl : public ClientContext {
public:
// |client| must outlive this instance.
- ClientContextImpl(const Client* client);
+ explicit ClientContextImpl(const Client* client);
~ClientContextImpl() override = default;
void Update(const TriggerContext& trigger_context) override;
+ void UpdateAnnotateDomModelContext(int64_t model_version) override;
+ void UpdateJsFlowLibraryLoaded(bool js_flow_library_loaded) override;
ClientContextProto AsProto() const override;
private:
diff --git a/chromium/components/autofill_assistant/browser/client_context_unittest.cc b/chromium/components/autofill_assistant/browser/client_context_unittest.cc
index 279956c51ae..413f905326c 100644
--- a/chromium/components/autofill_assistant/browser/client_context_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/client_context_unittest.cc
@@ -43,7 +43,7 @@ TEST_F(ClientContextTest, Initialize) {
.WillOnce(Return(std::make_pair(1080, 1920)));
EXPECT_CALL(mock_client_, GetScreenOrientation())
.WillOnce(Return(ClientContextProto::PORTRAIT));
- EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
+ EXPECT_CALL(mock_client_, GetSignedInEmail())
.WillOnce(Return("john.doe@chromium.org"));
EXPECT_CALL(mock_client_, IsAccessibilityEnabled()).WillOnce(Return(true));
@@ -67,6 +67,16 @@ TEST_F(ClientContextTest, Initialize) {
EXPECT_THAT(actual_client_context.window_size().height_pixels(), Eq(1920));
EXPECT_THAT(actual_client_context.screen_orientation(),
ClientContextProto::PORTRAIT);
+ EXPECT_EQ(actual_client_context.js_flow_library_loaded(), false);
+#if BUILDFLAG(IS_ANDROID)
+ EXPECT_THAT(actual_client_context.platform_type(),
+ ClientContextProto::PLATFORM_TYPE_ANDROID);
+#endif
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
+ BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
+ EXPECT_THAT(actual_client_context.platform_type(),
+ ClientContextProto::PLATFORM_TYPE_DESKTOP);
+#endif
auto actual_device_context = actual_client_context.device_context();
EXPECT_THAT(actual_device_context.version().sdk_int(), Eq(123));
@@ -74,10 +84,10 @@ TEST_F(ClientContextTest, Initialize) {
EXPECT_THAT(actual_device_context.model(), Eq("model"));
}
-TEST_F(ClientContextTest, UpdateWithTriggerContext) {
+TEST_F(ClientContextTest, UpdatesToClientContext) {
// Calls expected when the constructor is called.
EXPECT_CALL(mock_client_, IsAccessibilityEnabled()).WillOnce(Return(false));
- EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
+ EXPECT_CALL(mock_client_, GetSignedInEmail())
.WillOnce(Return("john.doe@chromium.org"));
EXPECT_CALL(mock_client_, GetWindowSize())
.WillOnce(Return(std::make_pair(0, 0)));
@@ -88,22 +98,21 @@ TEST_F(ClientContextTest, UpdateWithTriggerContext) {
// Calls expected when Update is called. We expect the previous entries to
// be overwritten.
EXPECT_CALL(mock_client_, IsAccessibilityEnabled()).WillOnce(Return(true));
- EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
- .WillOnce(Return(""));
+ EXPECT_CALL(mock_client_, GetSignedInEmail()).WillOnce(Return(""));
EXPECT_CALL(mock_client_, GetWindowSize())
.WillOnce(Return(std::pair<int, int>(1080, 1920)));
EXPECT_CALL(mock_client_, GetScreenOrientation())
.WillOnce(Return(ClientContextProto::LANDSCAPE));
+
client_context.Update({std::make_unique<ScriptParameters>(
base::flat_map<std::string, std::string>{
{"USER_EMAIL", "example@chromium.org"}}),
- /* exp = */ "1,2,3",
+ /* experiment_ids = */ "1,2,3",
/* is_cct = */ true,
/* onboarding_shown = */ true,
/* is_direct_action = */ true,
/* initial_url = */ "https://www.example.com",
/* is_in_chrome_triggered = */ true});
-
auto actual_client_context = client_context.AsProto();
EXPECT_THAT(actual_client_context.experiment_ids(), Eq("1,2,3"));
EXPECT_THAT(actual_client_context.is_cct(), Eq(true));
@@ -120,6 +129,13 @@ TEST_F(ClientContextTest, UpdateWithTriggerContext) {
EXPECT_THAT(actual_client_context.window_size().height_pixels(), Eq(1920));
EXPECT_THAT(actual_client_context.screen_orientation(),
ClientContextProto::LANDSCAPE);
+ EXPECT_FALSE(actual_client_context.has_annotate_dom_model_context());
+
+ client_context.UpdateAnnotateDomModelContext(123456);
+ actual_client_context = client_context.AsProto();
+ EXPECT_THAT(
+ actual_client_context.annotate_dom_model_context().model_version(),
+ 123456);
}
TEST_F(ClientContextTest, WindowSizeIsClearedIfNoLongerAvailable) {
@@ -147,7 +163,7 @@ TEST_F(ClientContextTest, WindowSizeIsClearedIfNoLongerAvailable) {
}
TEST_F(ClientContextTest, AccountMatching) {
- EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
+ EXPECT_CALL(mock_client_, GetSignedInEmail())
.WillRepeatedly(Return("john.doe@chromium.org"));
ClientContextImpl client_context(&mock_client_);
@@ -180,19 +196,27 @@ TEST_F(ClientContextTest, AccountMatching) {
}
TEST_F(ClientContextTest, SignedInStatus) {
- EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
- .WillOnce(Return(""));
+ EXPECT_CALL(mock_client_, GetSignedInEmail()).WillOnce(Return(""));
ClientContextImpl client_context_a(&mock_client_);
EXPECT_THAT(client_context_a.AsProto().signed_into_chrome_status(),
Eq(ClientContextProto::NOT_SIGNED_IN));
- EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
+ EXPECT_CALL(mock_client_, GetSignedInEmail())
.WillOnce(Return("john.doe@chromium.org"));
ClientContextImpl client_context_b(&mock_client_);
EXPECT_THAT(client_context_b.AsProto().signed_into_chrome_status(),
Eq(ClientContextProto::SIGNED_IN));
}
+TEST_F(ClientContextTest, UpdateJsFlowLibraryLoaded) {
+ ClientContextImpl client_context(&mock_client_);
+ EXPECT_EQ(client_context.AsProto().js_flow_library_loaded(), false);
+ client_context.UpdateJsFlowLibraryLoaded(true);
+ EXPECT_EQ(client_context.AsProto().js_flow_library_loaded(), true);
+ client_context.UpdateJsFlowLibraryLoaded(false);
+ EXPECT_EQ(client_context.AsProto().js_flow_library_loaded(), false);
+}
+
} // namespace
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/client_settings.cc b/chromium/components/autofill_assistant/browser/client_settings.cc
index 436c24c4518..2d2f5e20db7 100644
--- a/chromium/components/autofill_assistant/browser/client_settings.cc
+++ b/chromium/components/autofill_assistant/browser/client_settings.cc
@@ -176,6 +176,10 @@ void ClientSettings::UpdateFromProto(const ClientSettingsProto& proto) {
selector_observer_extra_timeout =
base::Milliseconds(proto.selector_observer_extra_timeout_ms());
}
+ if (proto.has_selector_observer_debounce_interval_ms()) {
+ selector_observer_debounce_interval =
+ base::Milliseconds(proto.selector_observer_debounce_interval_ms());
+ }
}
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/client_settings.h b/chromium/components/autofill_assistant/browser/client_settings.h
index 096e20dd03f..b3a40296ade 100644
--- a/chromium/components/autofill_assistant/browser/client_settings.h
+++ b/chromium/components/autofill_assistant/browser/client_settings.h
@@ -147,6 +147,10 @@ struct ClientSettings {
// time spent waiting so a extra delay of 1 to 10 seconds for javascript
// execution and checking selectors is conceivable.
base::TimeDelta selector_observer_extra_timeout = base::Seconds(15);
+
+ // Wait until no DOM changes are received for this amount of time to check
+ // the selectors. An interval of 0 effectively disables debouncing.
+ base::TimeDelta selector_observer_debounce_interval = base::Milliseconds(100);
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/common_dependencies.cc b/chromium/components/autofill_assistant/browser/common_dependencies.cc
new file mode 100644
index 00000000000..e1b64870625
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/common_dependencies.cc
@@ -0,0 +1,11 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/common_dependencies.h"
+
+namespace autofill_assistant {
+
+CommonDependencies::~CommonDependencies() = default;
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/common_dependencies.h b/chromium/components/autofill_assistant/browser/common_dependencies.h
new file mode 100644
index 00000000000..518c4db12d7
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/common_dependencies.h
@@ -0,0 +1,77 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_COMMON_DEPENDENCIES_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_COMMON_DEPENDENCIES_H_
+
+#include <memory>
+#include <string>
+
+namespace autofill {
+class PersonalDataManager;
+} // namespace autofill
+
+namespace password_manager {
+class PasswordManagerClient;
+} // namespace password_manager
+
+namespace content {
+class WebContents;
+class BrowserContext;
+} // namespace content
+
+namespace signin {
+class IdentityManager;
+} // namespace signin
+
+namespace version_info {
+enum class Channel;
+} // namespace version_info
+
+namespace autofill_assistant {
+
+class AnnotateDomModelService;
+class AssistantFieldTrialUtil;
+
+// Interface for platform delegates that provide dependencies to the starter.
+//
+// This interface contains all methods with a common implementation across
+// platforms (desktop and Android) but a different implementation on WebLayer.
+class CommonDependencies {
+ public:
+ virtual ~CommonDependencies();
+
+ virtual std::unique_ptr<AssistantFieldTrialUtil> CreateFieldTrialUtil()
+ const = 0;
+
+ virtual std::string GetLocale() const = 0;
+
+ virtual std::string GetCountryCode() const = 0;
+
+ virtual autofill::PersonalDataManager* GetPersonalDataManager(
+ content::BrowserContext* browser_context) const = 0;
+
+ virtual password_manager::PasswordManagerClient* GetPasswordManagerClient(
+ content::WebContents* web_contents) const = 0;
+
+ virtual std::string GetSignedInEmail(
+ content::BrowserContext* browser_context) const = 0;
+
+ virtual bool IsSupervisedUser(
+ content::BrowserContext* browser_context) const = 0;
+
+ virtual AnnotateDomModelService* GetOrCreateAnnotateDomModelService(
+ content::BrowserContext* browser_context) const = 0;
+
+ virtual bool IsWebLayer() const = 0;
+
+ virtual signin::IdentityManager* GetIdentityManager(
+ content::BrowserContext* browser_context) const = 0;
+
+ virtual version_info::Channel GetChannel() const = 0;
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_COMMON_DEPENDENCIES_H_
diff --git a/chromium/components/autofill_assistant/browser/controller.cc b/chromium/components/autofill_assistant/browser/controller.cc
index c669d229234..d8425da3aa7 100644
--- a/chromium/components/autofill_assistant/browser/controller.cc
+++ b/chromium/components/autofill_assistant/browser/controller.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/ranges/algorithm.h"
@@ -21,6 +22,7 @@
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/protocol_utils.h"
#include "components/autofill_assistant/browser/service/service_impl.h"
+#include "components/autofill_assistant/browser/switches.h"
#include "components/autofill_assistant/browser/trigger_context.h"
#include "components/autofill_assistant/browser/url_utils.h"
#include "components/autofill_assistant/browser/user_data.h"
@@ -29,7 +31,6 @@
#include "components/google/core/common/google_util.h"
#include "components/password_manager/core/browser/password_change_success_tracker_impl.h"
#include "components/strings/grit/components_strings.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
@@ -59,6 +60,14 @@ bool ShouldSuppressKeyboardForState(AutofillAssistantState state) {
}
}
+bool ShouldSendModelVersionInContext(const TriggerContext& trigger_context) {
+ return trigger_context.GetScriptParameters()
+ .GetSendAnnotateDomModelVersion()
+ .value_or(false) ||
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAutofillAssistantAnnotateDom);
+}
+
} // namespace
Controller::Controller(content::WebContents* web_contents,
@@ -139,6 +148,24 @@ content::WebContents* Controller::GetWebContents() {
return web_contents();
}
+void Controller::SetJsFlowLibrary(const std::string& js_flow_library) {
+ if (js_flow_library.empty()) {
+ return;
+ }
+
+ GetJsFlowDevtoolsWrapper()->SetJsFlowLibrary(js_flow_library);
+ GetService()->UpdateJsFlowLibraryLoaded(!js_flow_library.empty());
+}
+
+JsFlowDevtoolsWrapper* Controller::GetJsFlowDevtoolsWrapper() {
+ if (!js_flow_devtools_wrapper_) {
+ js_flow_devtools_wrapper_ = std::make_unique<JsFlowDevtoolsWrapper>(
+ GetWebContents()->GetBrowserContext());
+ }
+
+ return js_flow_devtools_wrapper_.get();
+}
+
std::string Controller::GetEmailAddressForAccessTokenAccount() {
return client_->GetEmailAddressForAccessTokenAccount();
}
@@ -195,6 +222,10 @@ ProcessedActionStatusDetailsProto& Controller::GetLogInfo() {
return log_info_;
}
+bool Controller::MustUseBackendData() const {
+ return client_->MustUseBackendData();
+}
+
void Controller::AddNavigationListener(
ScriptExecutorDelegate::NavigationListener* listener) {
navigation_listeners_.AddObserver(listener);
@@ -409,16 +440,42 @@ void Controller::GetOrCheckScripts() {
#else
VLOG(2) << "GetScripts for " << script_url_.host();
#endif
-
- GetService()->GetScriptsForUrl(
- url, *trigger_context_,
- base::BindOnce(&Controller::OnGetScripts, base::Unretained(this), url));
+ MaybeUpdateClientContextAndGetScriptsForUrl(url);
} else {
script_tracker()->CheckScripts();
StartPeriodicScriptChecks();
}
}
+void Controller::MaybeUpdateClientContextAndGetScriptsForUrl(const GURL& url) {
+ DCHECK(trigger_context_);
+ if (!ShouldSendModelVersionInContext(*trigger_context_)) {
+ GetScriptsForUrl(url);
+ return;
+ }
+
+ DCHECK(client_);
+ client_->GetAnnotateDomModelVersion(
+ base::BindOnce(&Controller::OnGetAnnotateDomModelVersionForGetScripts,
+ weak_ptr_factory_.GetWeakPtr(), url));
+}
+
+void Controller::OnGetAnnotateDomModelVersionForGetScripts(
+ const GURL& url,
+ absl::optional<int64_t> model_version) {
+ if (model_version) {
+ GetService()->UpdateAnnotateDomModelContext(*model_version);
+ }
+ GetScriptsForUrl(url);
+}
+
+void Controller::GetScriptsForUrl(const GURL& url) {
+ GetService()->GetScriptsForUrl(
+ url, *trigger_context_,
+ base::BindOnce(&Controller::OnGetScripts, weak_ptr_factory_.GetWeakPtr(),
+ url));
+}
+
void Controller::StartPeriodicScriptChecks() {
periodic_script_check_count_ = settings_.periodic_script_check_count;
// If periodic checks are running, setting periodic_script_check_count_ keeps
@@ -507,6 +564,14 @@ void Controller::OnGetScripts(
Metrics::DropOutReason::GET_SCRIPTS_UNPARSABLE);
return;
}
+
+ if (response_proto.has_semantic_selector_policy()) {
+ // TODO(b/228987849): A semantic policy is set unconditionally. It may be
+ // more appropriate to only set one if there are actual eligible scripts for
+ // the given domain.
+ SetSemanticSelectorPolicy(
+ std::move(response_proto.semantic_selector_policy()));
+ }
if (response_proto.has_client_settings()) {
SetClientSettings(response_proto.client_settings());
}
@@ -737,14 +802,28 @@ void Controller::InitFromParameters() {
DCHECK(GetDeeplinkURL().is_valid()); // |deeplink_url_| must be set.
user_data_.selected_login_.emplace(
GetDeeplinkURL().DeprecatedGetOriginAsURL(), *password_change_username);
- GetPasswordChangeSuccessTracker()->OnChangePasswordFlowStarted(
- user_data_.selected_login_->origin,
- user_data_.selected_login_->username,
- password_manager::PasswordChangeSuccessTracker::StartEvent::
- kAutomatedFlow);
+
+ // We only start password change success tracking here if the run was
+ // started from the Google Password Manager. The other cases are
+ // handled directly in the UI.
+ if (trigger_context_->GetScriptParameters().GetCaller().value_or(0) ==
+ static_cast<int>(
+ Metrics::AutofillAssistantCaller::GOOGLE_PASSWORD_MANAGER)) {
+ GetPasswordChangeSuccessTracker()->OnChangePasswordFlowStarted(
+ user_data_.selected_login_->origin,
+ user_data_.selected_login_->username,
+ password_manager::PasswordChangeSuccessTracker::StartEvent::
+ kAutomatedFlow,
+ password_manager::PasswordChangeSuccessTracker::EntryPoint::
+ kLeakCheckInSettings);
+ }
}
user_model_.SetCurrentURL(GetCurrentURL());
+
+ GetService()->SetDisableRpcSigning(
+ trigger_context_->GetScriptParameters().GetDisableRpcSigning().value_or(
+ false));
}
void Controller::Track(std::unique_ptr<TriggerContext> trigger_context,
@@ -809,9 +888,6 @@ void Controller::ShowFirstMessageAndStart() {
}
void Controller::Shutdown(Metrics::DropOutReason reason) {
- for (ControllerObserver& observer : observers_) {
- observer.OnShutdown(reason);
- }
client_->Shutdown(reason);
}
@@ -987,6 +1063,13 @@ void Controller::SetDirectActionScripts(
}
}
+void Controller::SetSemanticSelectorPolicy(SemanticSelectorPolicy policy) {
+ DCHECK(annotate_dom_model_service_);
+ if (!annotate_dom_model_service_->SetOverridesPolicy(std::move(policy))) {
+ NOTREACHED() << "Setting overrides policy failed!";
+ }
+}
+
void Controller::OnRunnableScriptsChanged(
const std::vector<ScriptHandle>& runnable_scripts) {
base::ScopedClosureRunner report_first_check;
diff --git a/chromium/components/autofill_assistant/browser/controller.h b/chromium/components/autofill_assistant/browser/controller.h
index fc5e71d069b..4363a370b02 100644
--- a/chromium/components/autofill_assistant/browser/controller.h
+++ b/chromium/components/autofill_assistant/browser/controller.h
@@ -124,6 +124,8 @@ class Controller : public ScriptExecutorDelegate,
password_manager::PasswordChangeSuccessTracker*
GetPasswordChangeSuccessTracker() override;
content::WebContents* GetWebContents() override;
+ void SetJsFlowLibrary(const std::string& js_flow_library) override;
+ JsFlowDevtoolsWrapper* GetJsFlowDevtoolsWrapper() override;
std::string GetEmailAddressForAccessTokenAccount() override;
ukm::UkmRecorder* GetUkmRecorder() override;
void SetTouchableElementArea(const ElementAreaProto& area) override;
@@ -135,6 +137,7 @@ class Controller : public ScriptExecutorDelegate,
void SetBrowseModeInvisible(bool invisible) override;
bool ShouldShowWarning() override;
ProcessedActionStatusDetailsProto& GetLogInfo() override;
+ bool MustUseBackendData() const override;
// Show the UI if it's not already shown. This is only meaningful while in
// states where showing the UI is optional, such as RUNNING, in tracking mode.
@@ -295,6 +298,15 @@ class Controller : public ScriptExecutorDelegate,
void SetDirectActionScripts(
const std::vector<ScriptHandle>& direct_action_scripts);
+ // Sets the semantic selector in the DOM annotation service.
+ void SetSemanticSelectorPolicy(SemanticSelectorPolicy policy);
+
+ void MaybeUpdateClientContextAndGetScriptsForUrl(const GURL& url);
+ void OnGetAnnotateDomModelVersionForGetScripts(
+ const GURL& url,
+ absl::optional<int64_t> model_version);
+ void GetScriptsForUrl(const GURL& url);
+
ClientSettings settings_;
const raw_ptr<Client> client_;
const raw_ptr<const base::TickClock> tick_clock_;
@@ -372,6 +384,8 @@ class Controller : public ScriptExecutorDelegate,
// The next DidStartNavigation will not cause an error.
bool expect_navigation_ = false;
+ std::unique_ptr<JsFlowDevtoolsWrapper> js_flow_devtools_wrapper_;
+
// Tracks scripts and script execution. It's kept at the end, as it tend to
// depend on everything the controller support, through script and script
// actions.
diff --git a/chromium/components/autofill_assistant/browser/controller_observer.h b/chromium/components/autofill_assistant/browser/controller_observer.h
index 80ae6bdfeef..b2ead99fb3d 100644
--- a/chromium/components/autofill_assistant/browser/controller_observer.h
+++ b/chromium/components/autofill_assistant/browser/controller_observer.h
@@ -97,9 +97,6 @@ class ControllerObserver : public base::CheckedObserver {
// Called whenever the UI is shown or hidden.
virtual void OnUiShownChanged(bool shown) = 0;
-
- // Called before shutting down the Controller.
- virtual void OnShutdown(Metrics::DropOutReason reason) = 0;
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CONTROLLER_OBSERVER_H_
diff --git a/chromium/components/autofill_assistant/browser/controller_unittest.cc b/chromium/components/autofill_assistant/browser/controller_unittest.cc
index a8b5f7419fd..1cbc9d6eafe 100644
--- a/chromium/components/autofill_assistant/browser/controller_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/controller_unittest.cc
@@ -32,6 +32,7 @@
#include "components/autofill_assistant/browser/public/mock_runtime_manager.h"
#include "components/autofill_assistant/browser/service/mock_service.h"
#include "components/autofill_assistant/browser/service/service.h"
+#include "components/autofill_assistant/browser/switches.h"
#include "components/autofill_assistant/browser/test_util.h"
#include "components/autofill_assistant/browser/trigger_context.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
@@ -74,6 +75,14 @@ using ::testing::StrEq;
using ::testing::UnorderedElementsAre;
using ::testing::WithArgs;
+class MockAnnotateDomModelService : public AnnotateDomModelService {
+ public:
+ MockAnnotateDomModelService() : AnnotateDomModelService(nullptr, nullptr) {}
+ ~MockAnnotateDomModelService() override = default;
+
+ MOCK_METHOD1(SetOverridesPolicy, bool(SemanticSelectorPolicy));
+};
+
class ControllerTest : public testing::Test {
public:
ControllerTest() {
@@ -99,7 +108,7 @@ class ControllerTest : public testing::Test {
controller_ = std::make_unique<Controller>(
web_contents(), &mock_client_, task_environment()->GetMockTickClock(),
mock_runtime_manager_->GetWeakPtr(), std::move(service), &ukm_recorder_,
- /* annotate_dom_model_service= */ nullptr);
+ &mock_annotate_dom_model_service_);
controller_->SetWebControllerForTest(std::move(web_controller));
@@ -207,7 +216,7 @@ class ControllerTest : public testing::Test {
void SimulateNavigateToUrl(const GURL& url) {
SetLastCommittedUrl(url);
content::NavigationSimulator::NavigateAndCommitFromDocument(
- url, web_contents()->GetMainFrame());
+ url, web_contents()->GetPrimaryMainFrame());
content::WebContentsTester::For(web_contents())->TestSetIsLoading(false);
controller_->DidFinishLoad(nullptr, GURL(""));
}
@@ -261,6 +270,7 @@ class ControllerTest : public testing::Test {
mock_password_change_success_tracker_;
ukm::TestAutoSetUkmRecorder ukm_recorder_;
std::unique_ptr<Controller> controller_;
+ NiceMock<MockAnnotateDomModelService> mock_annotate_dom_model_service_;
};
struct NavigationState {
@@ -774,7 +784,7 @@ TEST_F(ControllerTest, SuccessfulNavigation) {
NavigationStateChangeListener listener(controller_.get());
controller_->AddNavigationListener(&listener);
content::NavigationSimulator::NavigateAndCommitFromDocument(
- GURL("http://initialurl.com"), web_contents()->GetMainFrame());
+ GURL("http://initialurl.com"), web_contents()->GetPrimaryMainFrame());
controller_->RemoveNavigationListener(&listener);
EXPECT_FALSE(controller_->IsNavigatingToNewDocument());
@@ -792,7 +802,7 @@ TEST_F(ControllerTest, FailedNavigation) {
controller_->AddNavigationListener(&listener);
content::NavigationSimulator::NavigateAndFailFromDocument(
GURL("http://initialurl.com"), net::ERR_CONNECTION_TIMED_OUT,
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
controller_->RemoveNavigationListener(&listener);
EXPECT_FALSE(controller_->IsNavigatingToNewDocument());
@@ -811,7 +821,8 @@ TEST_F(ControllerTest, NavigationWithRedirects) {
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(
- GURL("http://original.example.com/"), web_contents()->GetMainFrame());
+ GURL("http://original.example.com/"),
+ web_contents()->GetPrimaryMainFrame());
simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
simulator->Start();
EXPECT_TRUE(controller_->IsNavigatingToNewDocument());
@@ -840,9 +851,9 @@ TEST_F(ControllerTest, EventuallySuccessfulNavigation) {
controller_->AddNavigationListener(&listener);
content::NavigationSimulator::NavigateAndFailFromDocument(
GURL("http://initialurl.com"), net::ERR_CONNECTION_TIMED_OUT,
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
content::NavigationSimulator::NavigateAndCommitFromDocument(
- GURL("http://initialurl.com"), web_contents()->GetMainFrame());
+ GURL("http://initialurl.com"), web_contents()->GetPrimaryMainFrame());
controller_->RemoveNavigationListener(&listener);
EXPECT_FALSE(controller_->IsNavigatingToNewDocument());
@@ -864,15 +875,15 @@ TEST_F(ControllerTest, RemoveListener) {
NavigationStateChangeListener listener(controller_.get());
controller_->AddNavigationListener(&listener);
content::NavigationSimulator::NavigateAndCommitFromDocument(
- GURL("http://initialurl.com"), web_contents()->GetMainFrame());
+ GURL("http://initialurl.com"), web_contents()->GetPrimaryMainFrame());
listener.events.clear();
controller_->RemoveNavigationListener(&listener);
content::NavigationSimulator::NavigateAndFailFromDocument(
GURL("http://initialurl.com"), net::ERR_CONNECTION_TIMED_OUT,
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
content::NavigationSimulator::NavigateAndCommitFromDocument(
- GURL("http://initialurl.com"), web_contents()->GetMainFrame());
+ GURL("http://initialurl.com"), web_contents()->GetPrimaryMainFrame());
EXPECT_THAT(listener.events, IsEmpty());
}
@@ -966,7 +977,8 @@ TEST_F(ControllerTest, WaitForNavigationActionStartWithinTimeout) {
EXPECT_THAT(processed_actions_capture, SizeIs(0));
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(
- GURL("http://a.example.com/path"), web_contents()->GetMainFrame());
+ GURL("http://a.example.com/path"),
+ web_contents()->GetPrimaryMainFrame());
simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
simulator->Start();
task_environment()->FastForwardBy(base::Seconds(1));
@@ -1718,7 +1730,7 @@ TEST_F(ControllerTest, UnexpectedNavigationDuringPromptAction) {
EXPECT_CALL(mock_client_, Shutdown(_)).Times(0);
EXPECT_CALL(mock_client_, RecordDropOut(_)).Times(0);
content::NavigationSimulator::NavigateAndCommitFromDocument(
- GURL("http://a.example.com/page"), web_contents()->GetMainFrame());
+ GURL("http://a.example.com/page"), web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(AutofillAssistantState::PROMPT, controller_->GetState());
// Expected browser initiated navigation is allowed.
@@ -1766,7 +1778,7 @@ TEST_F(ControllerTest, UnexpectedNavigationInRunningState) {
EXPECT_CALL(mock_client_, Shutdown(_)).Times(0);
EXPECT_CALL(mock_client_, RecordDropOut(_)).Times(0);
content::NavigationSimulator::NavigateAndCommitFromDocument(
- GURL("http://a.example.com/page"), web_contents()->GetMainFrame());
+ GURL("http://a.example.com/page"), web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(AutofillAssistantState::RUNNING, controller_->GetState());
// Expected browser initiated navigation while in RUNNING state:
@@ -1994,7 +2006,7 @@ TEST_F(ControllerTest, WriteUserData) {
TermsAndConditionsState::ACCEPTED);
}
-TEST_F(ControllerTest, StartPasswordChangeFlow) {
+TEST_F(ControllerTest, StartPasswordChangeFlowFromUPM) {
const GURL initialUrl("http://example.com/password");
const std::string username = "test_username";
EXPECT_CALL(*mock_service_, GetScriptsForUrl(Eq(initialUrl), _, _))
@@ -2004,7 +2016,38 @@ TEST_F(ControllerTest, StartPasswordChangeFlow) {
OnChangePasswordFlowStarted(
initialUrl.DeprecatedGetOriginAsURL(), username,
password_manager::PasswordChangeSuccessTracker::StartEvent::
- kAutomatedFlow));
+ kAutomatedFlow,
+ password_manager::PasswordChangeSuccessTracker::EntryPoint::
+ kLeakCheckInSettings));
+
+ EXPECT_TRUE(controller_->Start(
+ initialUrl,
+ // 9 is the enum value of |GOOGLE_PASSWORD_MANAGER|, i.e. a call
+ // from the Unified Password Manager.
+ std::make_unique<TriggerContext>(
+ /* parameters=*/std::make_unique<ScriptParameters>(
+ base::flat_map<std::string, std::string>{
+ {"PASSWORD_CHANGE_USERNAME", username}, {"CALLER", "9"}}),
+ TriggerContext::Options())));
+ // Initial navigation.
+ SimulateNavigateToUrl(GURL("http://b.example.com"));
+ EXPECT_EQ(GetUserData()->selected_login_->username, username);
+ EXPECT_EQ(GetUserData()->selected_login_->origin,
+ initialUrl.DeprecatedGetOriginAsURL());
+ EXPECT_EQ(controller_->GetCurrentURL().host(), "b.example.com");
+}
+
+TEST_F(ControllerTest, StartPasswordChangeFlow) {
+ const GURL initialUrl("http://example.com/password");
+ const std::string username = "test_username";
+ EXPECT_CALL(*mock_service_, GetScriptsForUrl(Eq(initialUrl), _, _))
+ .WillOnce(RunOnceCallback<2>(net::HTTP_OK, "",
+ ServiceRequestSender::ResponseInfo{}));
+ // We do not expect a call to the tracker, since the flow is not started
+ // from UPM.
+ EXPECT_CALL(mock_password_change_success_tracker_,
+ OnChangePasswordFlowStarted)
+ .Times(0);
EXPECT_TRUE(controller_->Start(
initialUrl, std::make_unique<TriggerContext>(
@@ -2057,7 +2100,8 @@ TEST_F(ControllerTest, EndPromptWithOnEndNavigation) {
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(
- GURL("http://a.example.com/path"), web_contents()->GetMainFrame());
+ GURL("http://a.example.com/path"),
+ web_contents()->GetPrimaryMainFrame());
simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
simulator->Start();
task_environment()->FastForwardBy(base::Seconds(1));
@@ -2182,7 +2226,7 @@ TEST_F(ControllerPrerenderTest, SuccessfulNavigation) {
controller_->AddNavigationListener(&listener);
content::NavigationSimulator::NavigateAndCommitFromDocument(
- GURL("http://initialurl.com"), web_contents()->GetMainFrame());
+ GURL("http://initialurl.com"), web_contents()->GetPrimaryMainFrame());
EXPECT_THAT(
listener.events,
@@ -2208,4 +2252,160 @@ TEST_F(ControllerPrerenderTest, SuccessfulNavigation) {
EXPECT_THAT(listener.events, IsEmpty());
}
+TEST_F(ControllerTest, MustUseBackendData) {
+ EXPECT_CALL(mock_client_, MustUseBackendData).WillOnce(Return(true));
+ EXPECT_TRUE(controller_->MustUseBackendData());
+}
+
+class ControllerFencedFrameTest : public ControllerTest {
+ public:
+ ControllerFencedFrameTest() {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ blink::features::kFencedFrames, {{"implementation_type", "mparch"}});
+ }
+ ~ControllerFencedFrameTest() override = default;
+
+ content::RenderFrameHost* CreateFencedFrame(
+ content::RenderFrameHost* parent) {
+ content::RenderFrameHost* fenced_frame =
+ content::RenderFrameHostTester::For(parent)->AppendFencedFrame();
+ return fenced_frame;
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(ControllerFencedFrameTest, DoNotNavigateInFencedFrame) {
+ EXPECT_FALSE(controller_->IsNavigatingToNewDocument());
+ EXPECT_FALSE(controller_->HasNavigationError());
+
+ NavigationStateChangeListener listener(controller_.get());
+ controller_->AddNavigationListener(&listener);
+
+ content::NavigationSimulator::NavigateAndCommitFromDocument(
+ GURL("http://initialurl.com"), web_contents()->GetPrimaryMainFrame());
+
+ EXPECT_THAT(
+ listener.events,
+ ElementsAre(
+ NavigationState{/* navigating= */ true, /* has_errors= */ false},
+ NavigationState{/* navigating= */ false, /* has_errors= */ false}));
+
+ listener.events.clear();
+
+ // Create a fenced frame.
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
+ ->InitializeRenderFrameIfNeeded();
+ content::RenderFrameHost* fenced_frame_rfh =
+ CreateFencedFrame(web_contents()->GetPrimaryMainFrame());
+ GURL kFencedFrameUrl("https://fencedframe.com");
+ std::unique_ptr<content::NavigationSimulator> navigation_simulator =
+ content::NavigationSimulator::CreateRendererInitiated(kFencedFrameUrl,
+ fenced_frame_rfh);
+ navigation_simulator->Commit();
+ fenced_frame_rfh = navigation_simulator->GetFinalRenderFrameHost();
+ EXPECT_TRUE(fenced_frame_rfh->IsFencedFrameRoot());
+
+ // Autofill assistant controller doesn't handle navigations in fenced frames.
+ EXPECT_FALSE(controller_->IsNavigatingToNewDocument());
+ EXPECT_FALSE(controller_->HasNavigationError());
+
+ controller_->RemoveNavigationListener(&listener);
+
+ EXPECT_THAT(listener.events, IsEmpty());
+}
+
+TEST_F(ControllerTest, SemanticOverridesSetInService) {
+ EXPECT_CALL(mock_annotate_dom_model_service_, SetOverridesPolicy)
+ .WillOnce(Return(true));
+
+ SupportsScriptResponseProto script_response;
+ script_response.mutable_semantic_selector_policy()
+ ->mutable_bag_of_words()
+ ->add_data_point_map();
+ AddRunnableScript(&script_response, "runnable");
+ SetNextScriptResponse(script_response);
+
+ EXPECT_CALL(mock_client_, AttachUI());
+ Start("http://a.example.com/path");
+ EXPECT_EQ(AutofillAssistantState::STARTING, controller_->GetState());
+}
+
+TEST_F(ControllerTest, SkipModelVersionIfParameterNotSpecified) {
+ EXPECT_CALL(mock_client_, GetAnnotateDomModelVersion).Times(0);
+ EXPECT_CALL(*mock_service_, UpdateAnnotateDomModelContext).Times(0);
+ EXPECT_CALL(*mock_service_, GetScriptsForUrl)
+ .WillOnce(RunOnceCallback<2>(net::HTTP_OK, "",
+ ServiceRequestSender::ResponseInfo{}));
+
+ controller_->Start(GURL("https://www.example.com"),
+ std::make_unique<TriggerContext>(
+ /* parameters = */ std::make_unique<ScriptParameters>(
+ base::flat_map<std::string, std::string>{{}}),
+ TriggerContext::Options()));
+}
+
+TEST_F(ControllerTest, AttachesAvailableModelVersionOnStart) {
+ EXPECT_CALL(mock_client_, GetAnnotateDomModelVersion)
+ .WillOnce(RunOnceCallback<0>(123456));
+ EXPECT_CALL(*mock_service_, UpdateAnnotateDomModelContext(123456));
+ EXPECT_CALL(*mock_service_, GetScriptsForUrl)
+ .WillOnce(RunOnceCallback<2>(net::HTTP_OK, "",
+ ServiceRequestSender::ResponseInfo{}));
+
+ controller_->Start(GURL("https://www.example.com"),
+ std::make_unique<TriggerContext>(
+ /* parameters = */ std::make_unique<ScriptParameters>(
+ base::flat_map<std::string, std::string>{
+ {"SEND_ANNOTATE_DOM_MODEL_VERSION", "true"}}),
+ TriggerContext::Options()));
+}
+
+TEST_F(ControllerTest, DoesNotAttachUnavailableModelVersionOnStart) {
+ EXPECT_CALL(mock_client_, GetAnnotateDomModelVersion)
+ .WillOnce(RunOnceCallback<0>(absl::nullopt));
+ EXPECT_CALL(*mock_service_, UpdateAnnotateDomModelContext).Times(0);
+ EXPECT_CALL(*mock_service_, GetScriptsForUrl)
+ .WillOnce(RunOnceCallback<2>(net::HTTP_OK, "",
+ ServiceRequestSender::ResponseInfo{}));
+
+ controller_->Start(GURL("https://www.example.com"),
+ std::make_unique<TriggerContext>(
+ /* parameters = */ std::make_unique<ScriptParameters>(
+ base::flat_map<std::string, std::string>{
+ {"SEND_ANNOTATE_DOM_MODEL_VERSION", "true"}}),
+ TriggerContext::Options()));
+}
+
+TEST_F(ControllerTest, AttachesAvailableModelVersionForCommandLineSwitch) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kAutofillAssistantAnnotateDom, "true");
+
+ EXPECT_CALL(mock_client_, GetAnnotateDomModelVersion)
+ .WillOnce(RunOnceCallback<0>(123456));
+ EXPECT_CALL(*mock_service_, UpdateAnnotateDomModelContext(123456));
+ EXPECT_CALL(*mock_service_, GetScriptsForUrl)
+ .WillOnce(RunOnceCallback<2>(net::HTTP_OK, "",
+ ServiceRequestSender::ResponseInfo{}));
+
+ controller_->Start(GURL("https://www.example.com"),
+ std::make_unique<TriggerContext>(
+ /* parameters = */ std::make_unique<ScriptParameters>(
+ base::flat_map<std::string, std::string>{}),
+ TriggerContext::Options()));
+}
+
+TEST_F(ControllerTest, UpdatesJsFlowLibraryLoaded) {
+ EXPECT_CALL(*mock_service_, UpdateJsFlowLibraryLoaded(true));
+
+ controller_->SetJsFlowLibrary("const st = 2;");
+}
+
+TEST_F(ControllerTest, JsFlowLibraryNotLoadedForEmpty) {
+ EXPECT_CALL(*mock_service_, UpdateJsFlowLibraryLoaded(true)).Times(0);
+
+ controller_->SetJsFlowLibrary("");
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/dependencies_util.cc b/chromium/components/autofill_assistant/browser/dependencies_util.cc
new file mode 100644
index 00000000000..0272c889141
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/dependencies_util.cc
@@ -0,0 +1,23 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/dependencies_util.h"
+
+#include <string>
+
+#include "components/variations/service/variations_service.h"
+
+using ::variations::VariationsService;
+
+namespace autofill_assistant::dependencies_util {
+
+std::string GetCountryCode(VariationsService* variations_service) {
+ if (!variations_service || variations_service->GetLatestCountry().empty()) {
+ // Use fallback "ZZ" if no country is available.
+ return "ZZ";
+ }
+ return base::ToUpperASCII(variations_service->GetLatestCountry());
+}
+
+} // namespace autofill_assistant::dependencies_util
diff --git a/chromium/components/autofill_assistant/browser/dependencies_util.h b/chromium/components/autofill_assistant/browser/dependencies_util.h
new file mode 100644
index 00000000000..758f9b2b038
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/dependencies_util.h
@@ -0,0 +1,18 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_DEPENDENCIES_UTIL_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_DEPENDENCIES_UTIL_H_
+
+#include <string>
+
+#include "components/variations/service/variations_service.h"
+
+namespace autofill_assistant::dependencies_util {
+
+std::string GetCountryCode(variations::VariationsService* variations_service);
+
+} // namespace autofill_assistant::dependencies_util
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_DEPENDENCIES_UTIL_H_
diff --git a/chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.cc b/chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.cc
index bcd956694c2..12f126d1139 100644
--- a/chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.cc
+++ b/chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.cc
@@ -16,8 +16,12 @@
namespace autofill_assistant {
StarterDelegateDesktop::StarterDelegateDesktop(
- content::WebContents* web_contents)
- : content::WebContentsUserData<StarterDelegateDesktop>(*web_contents) {}
+ content::WebContents* web_contents,
+ std::unique_ptr<CommonDependencies> common_dependencies,
+ std::unique_ptr<PlatformDependencies> platform_dependencies)
+ : content::WebContentsUserData<StarterDelegateDesktop>(*web_contents),
+ common_dependencies_(std::move(common_dependencies)),
+ platform_dependencies_(std::move(platform_dependencies)) {}
StarterDelegateDesktop::~StarterDelegateDesktop() = default;
@@ -36,8 +40,7 @@ WebsiteLoginManager* StarterDelegateDesktop::GetWebsiteLoginManager() const {
}
version_info::Channel StarterDelegateDesktop::GetChannel() const {
- // TODO(b/201964911): Inject on instantiation.
- return version_info::Channel::DEV;
+ return common_dependencies_->GetChannel();
}
bool StarterDelegateDesktop::GetFeatureModuleInstalled() const {
@@ -52,7 +55,6 @@ void StarterDelegateDesktop::InstallFeatureModule(
}
bool StarterDelegateDesktop::GetIsFirstTimeUser() const {
- NOTREACHED();
return false;
}
@@ -96,12 +98,18 @@ bool StarterDelegateDesktop::GetMakeSearchesAndBrowsingBetterEnabled() const {
}
bool StarterDelegateDesktop::GetIsLoggedIn() {
- // Only relevant for trigger scripts, which don't exist in headless.
- return false;
+ return !common_dependencies_
+ ->GetSignedInEmail(GetWebContents().GetBrowserContext())
+ .empty();
+}
+
+bool StarterDelegateDesktop::GetIsSupervisedUser() {
+ return common_dependencies_->IsSupervisedUser(
+ GetWebContents().GetBrowserContext());
}
bool StarterDelegateDesktop::GetIsCustomTab() const {
- return false;
+ return platform_dependencies_->IsCustomTab(GetWebContents());
}
bool StarterDelegateDesktop::GetIsWebLayer() const {
@@ -114,8 +122,7 @@ bool StarterDelegateDesktop::GetIsTabCreatedByGSA() const {
std::unique_ptr<AssistantFieldTrialUtil>
StarterDelegateDesktop::CreateFieldTrialUtil() {
- // TODO(b/201964911): Create a field trial util.
- return nullptr;
+ return common_dependencies_->CreateFieldTrialUtil();
}
void StarterDelegateDesktop::StartScriptDefaultUi(
@@ -139,6 +146,16 @@ bool StarterDelegateDesktop::IsAttached() {
return true;
}
+const CommonDependencies* StarterDelegateDesktop::GetCommonDependencies()
+ const {
+ return common_dependencies_.get();
+}
+
+const PlatformDependencies* StarterDelegateDesktop::GetPlatformDependencies()
+ const {
+ return platform_dependencies_.get();
+}
+
base::WeakPtr<StarterPlatformDelegate> StarterDelegateDesktop::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
diff --git a/chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.h b/chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.h
index 86f82f326c8..f2abd050753 100644
--- a/chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.h
+++ b/chromium/components/autofill_assistant/browser/desktop/starter_delegate_desktop.h
@@ -8,8 +8,10 @@
#include <memory>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/common_dependencies.h"
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/onboarding_result.h"
+#include "components/autofill_assistant/browser/platform_dependencies.h"
#include "components/autofill_assistant/browser/starter_platform_delegate.h"
#include "components/autofill_assistant/browser/trigger_context.h"
#include "components/autofill_assistant/browser/website_login_manager.h"
@@ -60,16 +62,25 @@ class StarterDelegateDesktop
void SetProactiveHelpSettingEnabled(bool enabled) override;
bool GetMakeSearchesAndBrowsingBetterEnabled() const override;
bool GetIsLoggedIn() override;
+ bool GetIsSupervisedUser() override;
bool GetIsCustomTab() const override;
bool GetIsWebLayer() const override;
bool GetIsTabCreatedByGSA() const override;
std::unique_ptr<AssistantFieldTrialUtil> CreateFieldTrialUtil() override;
bool IsAttached() override;
+ const CommonDependencies* GetCommonDependencies() const override;
+ const PlatformDependencies* GetPlatformDependencies() const override;
base::WeakPtr<StarterPlatformDelegate> GetWeakPtr() override;
private:
friend class content::WebContentsUserData<StarterDelegateDesktop>;
- explicit StarterDelegateDesktop(content::WebContents* web_contents);
+ StarterDelegateDesktop(
+ content::WebContents* web_contents,
+ std::unique_ptr<CommonDependencies> common_dependencies,
+ std::unique_ptr<PlatformDependencies> platform_dependencies);
+
+ const std::unique_ptr<CommonDependencies> common_dependencies_;
+ const std::unique_ptr<PlatformDependencies> platform_dependencies_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
diff --git a/chromium/components/autofill_assistant/browser/details.cc b/chromium/components/autofill_assistant/browser/details.cc
index 7684d718e31..808710b7cfb 100644
--- a/chromium/components/autofill_assistant/browser/details.cc
+++ b/chromium/components/autofill_assistant/browser/details.cc
@@ -40,14 +40,18 @@ std::string FormatDateTimeProto(const DateTimeProto& date_time) {
auto date_proto = date_time.date();
auto time_proto = date_time.time();
+ // Technically we are setting the wrong |day_of_week|, but it's ignored in
+ // practice and the formatted string will have the correct day for the
+ // date. Setting an invalid value here (e.g. -1) causes issues on Windows.
base::Time::Exploded exploded_time = {static_cast<int>(date_proto.year()),
date_proto.month(),
- /* day_of_week = */ -1,
+ /* day_of_week = */ 0,
date_proto.day(),
time_proto.hour(),
time_proto.minute(),
time_proto.second(),
- 0};
+ /* millisecond = */ 0};
+
base::Time time;
if (base::Time::FromLocalExploded(exploded_time, &time)) {
diff --git a/chromium/components/autofill_assistant/browser/devtools/value_conversions.h b/chromium/components/autofill_assistant/browser/devtools/value_conversions.h
index 288c53a85aa..621d9949c4a 100644
--- a/chromium/components/autofill_assistant/browser/devtools/value_conversions.h
+++ b/chromium/components/autofill_assistant/browser/devtools/value_conversions.h
@@ -64,7 +64,7 @@ std::unique_ptr<base::Value> ToValueImpl(const std::vector<T>& vector,
const std::vector<T>*) {
std::unique_ptr<base::ListValue> result(new base::ListValue());
for (const auto& it : vector)
- result->Append(ToValue(it));
+ result->GetList().Append(base::Value::FromUniquePtrValue(ToValue(it)));
return std::move(result);
}
diff --git a/chromium/components/autofill_assistant/browser/dom_action.proto b/chromium/components/autofill_assistant/browser/dom_action.proto
index 1336d6fe4f9..c3c8805efa4 100644
--- a/chromium/components/autofill_assistant/browser/dom_action.proto
+++ b/chromium/components/autofill_assistant/browser/dom_action.proto
@@ -220,3 +220,12 @@ message ExecuteJsProto {
// not resolved in time, the action will report a |TIMED_OUT| error.
optional int32 timeout_ms = 3;
}
+
+// Set an element value through native.
+message SetNativeValueProto {
+ // The target element. Must be an instance of a |WebFormControlElement|,
+ // otherwise the action will fail.
+ optional ClientIdProto client_id = 1;
+ // The value to set.
+ optional TextValue value = 2;
+}
diff --git a/chromium/components/autofill_assistant/browser/external_action_extension_test.proto b/chromium/components/autofill_assistant/browser/external_action_extension_test.proto
new file mode 100644
index 00000000000..18efa162ca1
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/external_action_extension_test.proto
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+package autofill_assistant.testing;
+
+option optimize_for = LITE_RUNTIME;
+
+import "public/external_action.proto";
+
+extend external.ResultInfo {
+ optional TestResultExtension test_result_extension = 100;
+}
+
+message TestResultExtension {
+ optional string text = 1;
+}
diff --git a/chromium/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/chromium/components/autofill_assistant/browser/fake_script_executor_delegate.cc
index d25d60e5a9c..cc48be03dd0 100644
--- a/chromium/components/autofill_assistant/browser/fake_script_executor_delegate.cc
+++ b/chromium/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -61,6 +61,24 @@ content::WebContents* FakeScriptExecutorDelegate::GetWebContents() {
return web_contents_;
}
+void FakeScriptExecutorDelegate::SetJsFlowLibrary(
+ const std::string& js_flow_library) {
+ GetJsFlowDevtoolsWrapper()->SetJsFlowLibrary(js_flow_library);
+}
+
+JsFlowDevtoolsWrapper* FakeScriptExecutorDelegate::GetJsFlowDevtoolsWrapper() {
+ if (!js_flow_devtools_wrapper_) {
+ content::WebContents* web_contents = GetWebContents();
+ DCHECK(web_contents_)
+ << "devtools wrapper is only available in browsertests";
+
+ js_flow_devtools_wrapper_ =
+ std::make_unique<JsFlowDevtoolsWrapper>(web_contents);
+ }
+
+ return js_flow_devtools_wrapper_.get();
+}
+
std::string FakeScriptExecutorDelegate::GetEmailAddressForAccessTokenAccount() {
return std::string();
}
@@ -116,6 +134,10 @@ ProcessedActionStatusDetailsProto& FakeScriptExecutorDelegate::GetLogInfo() {
return log_info_;
}
+bool FakeScriptExecutorDelegate::MustUseBackendData() const {
+ return must_use_backend_data_;
+}
+
void FakeScriptExecutorDelegate::AddNavigationListener(
ScriptExecutorDelegate::NavigationListener* listener) {
navigation_listeners_.insert(listener);
diff --git a/chromium/components/autofill_assistant/browser/fake_script_executor_delegate.h b/chromium/components/autofill_assistant/browser/fake_script_executor_delegate.h
index ca1256c77a4..2d70a8632b9 100644
--- a/chromium/components/autofill_assistant/browser/fake_script_executor_delegate.h
+++ b/chromium/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -45,6 +45,8 @@ class FakeScriptExecutorDelegate : public ScriptExecutorDelegate {
password_manager::PasswordChangeSuccessTracker*
GetPasswordChangeSuccessTracker() override;
content::WebContents* GetWebContents() override;
+ void SetJsFlowLibrary(const std::string& js_flow_library) override;
+ JsFlowDevtoolsWrapper* GetJsFlowDevtoolsWrapper() override;
std::string GetEmailAddressForAccessTokenAccount() override;
ukm::UkmRecorder* GetUkmRecorder() override;
bool EnterState(AutofillAssistantState state) override;
@@ -69,6 +71,7 @@ class FakeScriptExecutorDelegate : public ScriptExecutorDelegate {
ConfigureUiStateProto::OverlayBehavior overlay_behavior) override;
void SetBrowseModeInvisible(bool invisible) override;
ProcessedActionStatusDetailsProto& GetLogInfo() override;
+ bool MustUseBackendData() const override;
bool ShouldShowWarning() override;
@@ -113,12 +116,18 @@ class FakeScriptExecutorDelegate : public ScriptExecutorDelegate {
std::vector<std::string>* GetCurrentBrowseDomainsList();
+ void SetMustUseBackendData(bool must_use_backend_data) {
+ must_use_backend_data_ = must_use_backend_data;
+ }
+
private:
ClientSettings client_settings_;
GURL current_url_;
raw_ptr<Service> service_ = nullptr;
raw_ptr<WebController> web_controller_ = nullptr;
raw_ptr<content::WebContents> web_contents_ = nullptr;
+ std::unique_ptr<JsFlowDevtoolsWrapper> js_flow_devtools_wrapper_;
+ std::string js_flow_library_;
std::unique_ptr<TriggerContext> trigger_context_;
std::vector<AutofillAssistantState> state_history_;
std::vector<ElementAreaProto> touchable_element_area_history_;
@@ -131,6 +140,7 @@ class FakeScriptExecutorDelegate : public ScriptExecutorDelegate {
std::vector<std::string> browse_domains_;
raw_ptr<UserModel> user_model_ = nullptr;
ProcessedActionStatusDetailsProto log_info_;
+ bool must_use_backend_data_ = false;
bool require_ui_ = false;
};
diff --git a/chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc b/chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc
index ba03a35aa75..c354be7249a 100644
--- a/chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc
+++ b/chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc
@@ -92,17 +92,23 @@ void FakeScriptExecutorUiDelegate::SetUserActions(
void FakeScriptExecutorUiDelegate::SetCollectUserDataOptions(
CollectUserDataOptions* options) {
- payment_request_options_ = options;
+ collect_user_data_options_ = options;
+}
+
+void FakeScriptExecutorUiDelegate::SetCollectUserDataUiState(
+ bool loading,
+ UserDataEventField event_field) {
+ collect_user_data_ui_loading__field_ = event_field;
}
void FakeScriptExecutorUiDelegate::SetLastSuccessfulUserDataOptions(
std::unique_ptr<CollectUserDataOptions> collect_user_data_options) {
- last_payment_request_options_ = std::move(collect_user_data_options);
+ last_collect_user_data_options_ = std::move(collect_user_data_options);
}
const CollectUserDataOptions*
FakeScriptExecutorUiDelegate::GetLastSuccessfulUserDataOptions() const {
- return last_payment_request_options_.get();
+ return last_collect_user_data_options_.get();
}
void FakeScriptExecutorUiDelegate::SetPeekMode(
@@ -158,4 +164,27 @@ void FakeScriptExecutorUiDelegate::ClearPersistentGenericUi() {
void FakeScriptExecutorUiDelegate::SetShowFeedbackChip(
bool show_feedback_chip) {}
+bool FakeScriptExecutorUiDelegate::SupportsExternalActions() {
+ return true;
+}
+
+void FakeScriptExecutorUiDelegate::ExecuteExternalAction(
+ const external::Action& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ external::Result result;
+ result.set_success(true);
+ std::move(end_action_callback).Run(result);
+}
+
+void FakeScriptExecutorUiDelegate::OnInterruptStarted() {
+ interrupt_notification_history_.emplace_back(INTERRUPT_STARTED);
+}
+
+void FakeScriptExecutorUiDelegate::OnInterruptFinished() {
+ interrupt_notification_history_.emplace_back(INTERRUPT_FINISHED);
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h b/chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h
index fc3106592a8..d6a64b5bb92 100644
--- a/chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h
+++ b/chromium/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h
@@ -20,6 +20,8 @@ namespace autofill_assistant {
// unittests.
class FakeScriptExecutorUiDelegate : public ScriptExecutorUiDelegate {
public:
+ enum InterruptNotification { INTERRUPT_STARTED = 0, INTERRUPT_FINISHED = 1 };
+
FakeScriptExecutorUiDelegate();
FakeScriptExecutorUiDelegate(const FakeScriptExecutorUiDelegate&) = delete;
@@ -52,6 +54,8 @@ class FakeScriptExecutorUiDelegate : public ScriptExecutorUiDelegate {
void SetUserActions(
std::unique_ptr<std::vector<UserAction>> user_actions) override;
void SetCollectUserDataOptions(CollectUserDataOptions* options) override;
+ void SetCollectUserDataUiState(bool loading,
+ UserDataEventField event_field) override;
void SetLastSuccessfulUserDataOptions(std::unique_ptr<CollectUserDataOptions>
collect_user_data_options) override;
const CollectUserDataOptions* GetLastSuccessfulUserDataOptions()
@@ -77,6 +81,15 @@ class FakeScriptExecutorUiDelegate : public ScriptExecutorUiDelegate {
void ClearGenericUi() override;
void ClearPersistentGenericUi() override;
void SetShowFeedbackChip(bool show_feedback_chip) override;
+ bool SupportsExternalActions() override;
+ void ExecuteExternalAction(
+ const external::Action& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) override;
+ void OnInterruptStarted() override;
+ void OnInterruptFinished() override;
const std::vector<Details>& GetDetails() { return details_; }
@@ -88,7 +101,15 @@ class FakeScriptExecutorUiDelegate : public ScriptExecutorUiDelegate {
std::vector<UserAction>* GetUserActions() { return user_actions_.get(); }
- CollectUserDataOptions* GetOptions() { return payment_request_options_; }
+ CollectUserDataOptions* GetOptions() { return collect_user_data_options_; }
+
+ UserDataEventField GetCollectUserDataUiLoadingField() {
+ return collect_user_data_ui_loading__field_;
+ }
+
+ std::vector<InterruptNotification> GetInterruptNotificationHistory() {
+ return interrupt_notification_history_;
+ }
private:
std::string status_message_;
@@ -97,8 +118,10 @@ class FakeScriptExecutorUiDelegate : public ScriptExecutorUiDelegate {
std::vector<Details> details_;
std::unique_ptr<InfoBox> info_box_;
std::unique_ptr<std::vector<UserAction>> user_actions_;
- std::unique_ptr<CollectUserDataOptions> last_payment_request_options_;
- raw_ptr<CollectUserDataOptions> payment_request_options_;
+ std::unique_ptr<CollectUserDataOptions> last_collect_user_data_options_;
+ raw_ptr<CollectUserDataOptions> collect_user_data_options_;
+ UserDataEventField collect_user_data_ui_loading__field_ =
+ UserDataEventField::NONE;
std::unique_ptr<UserData> payment_request_info_;
ConfigureBottomSheetProto::PeekMode peek_mode_ =
ConfigureBottomSheetProto::HANDLE;
@@ -106,6 +129,7 @@ class FakeScriptExecutorUiDelegate : public ScriptExecutorUiDelegate {
bool expand_or_collapse_value_ = false;
bool expand_sheet_for_prompt_ = true;
std::unique_ptr<GenericUserInterfaceProto> persistent_generic_ui_;
+ std::vector<InterruptNotification> interrupt_notification_history_;
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.cc b/chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.cc
index b1ae7191ba6..28b9938ed80 100644
--- a/chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.cc
+++ b/chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.cc
@@ -107,6 +107,10 @@ bool FakeStarterPlatformDelegate::GetIsLoggedIn() {
return is_logged_in_;
}
+bool FakeStarterPlatformDelegate::GetIsSupervisedUser() {
+ return is_supervised_user_;
+}
+
bool FakeStarterPlatformDelegate::GetIsCustomTab() const {
return is_custom_tab_;
}
@@ -131,6 +135,16 @@ bool FakeStarterPlatformDelegate::IsAttached() {
return is_attached_;
}
+const CommonDependencies* FakeStarterPlatformDelegate::GetCommonDependencies()
+ const {
+ return nullptr;
+}
+
+const PlatformDependencies*
+FakeStarterPlatformDelegate::GetPlatformDependencies() const {
+ return nullptr;
+}
+
base::WeakPtr<StarterPlatformDelegate>
FakeStarterPlatformDelegate::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
diff --git a/chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.h b/chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.h
index ecaf6d9dd5c..70884dd2623 100644
--- a/chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.h
+++ b/chromium/components/autofill_assistant/browser/fake_starter_platform_delegate.h
@@ -50,11 +50,14 @@ class FakeStarterPlatformDelegate : public StarterPlatformDelegate {
void SetProactiveHelpSettingEnabled(bool enabled) override;
bool GetMakeSearchesAndBrowsingBetterEnabled() const override;
bool GetIsLoggedIn() override;
+ bool GetIsSupervisedUser() override;
bool GetIsCustomTab() const override;
bool GetIsWebLayer() const override;
bool GetIsTabCreatedByGSA() const override;
std::unique_ptr<AssistantFieldTrialUtil> CreateFieldTrialUtil() override;
bool IsAttached() override;
+ const CommonDependencies* GetCommonDependencies() const override;
+ const PlatformDependencies* GetPlatformDependencies() const override;
base::WeakPtr<StarterPlatformDelegate> GetWeakPtr() override;
// Intentionally public to give tests direct access.
@@ -76,6 +79,7 @@ class FakeStarterPlatformDelegate : public StarterPlatformDelegate {
bool proactive_help_enabled_ = true;
bool msbb_enabled_ = true;
bool is_logged_in_ = true;
+ bool is_supervised_user_ = false;
bool is_custom_tab_ = true;
bool is_web_layer_ = true;
bool is_tab_created_by_gsa_ = true;
diff --git a/chromium/components/autofill_assistant/browser/field_formatter.cc b/chromium/components/autofill_assistant/browser/field_formatter.cc
index 0b0bd50a125..e4cd5949a56 100644
--- a/chromium/components/autofill_assistant/browser/field_formatter.cc
+++ b/chromium/components/autofill_assistant/browser/field_formatter.cc
@@ -88,14 +88,26 @@ void GetNameAndAbbreviationViaAlternativeStateNameMap(
}
}
-std::string ApplyChunkReplacement(
- const google::protobuf::Map<std::string, std::string>& replacements,
- const std::string& value) {
- const auto& it = replacements.find(value);
- if (it != replacements.end()) {
- return it->second;
+std::string ApplyChunkReplacement(const ValueExpression::Chunk& chunk,
+ const std::string& value) {
+ std::string result = value;
+ const auto& it = chunk.replacements().find(value);
+ if (it != chunk.replacements().end()) {
+ result = it->second;
}
- return value;
+ for (const auto& regexp_replacement : chunk.regexp_replacements()) {
+ re2::RE2::Options options;
+ options.set_case_sensitive(
+ regexp_replacement.text_filter().case_sensitive());
+ re2::RE2 regexp(regexp_replacement.text_filter().re2(), options);
+
+ if (regexp_replacement.global()) {
+ RE2::GlobalReplace(&result, regexp, regexp_replacement.replacement());
+ } else {
+ RE2::Replace(&result, regexp, regexp_replacement.replacement());
+ }
+ }
+ return result;
}
std::string GetMaybeQuotedChunk(const std::string& value, bool quote_meta) {
@@ -190,7 +202,7 @@ ClientStatus FormatExpression(const ValueExpression& value_expression,
case ValueExpression::Chunk::CHUNK_NOT_SET:
return ClientStatus(INVALID_ACTION);
}
- out_value->append(ApplyChunkReplacement(chunk.replacements(), chunk_value));
+ out_value->append(ApplyChunkReplacement(chunk, chunk_value));
}
return OkClientStatus();
diff --git a/chromium/components/autofill_assistant/browser/field_formatter_unittest.cc b/chromium/components/autofill_assistant/browser/field_formatter_unittest.cc
index a45180c290a..174f6d001cb 100644
--- a/chromium/components/autofill_assistant/browser/field_formatter_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/field_formatter_unittest.cc
@@ -439,6 +439,96 @@ TEST(FieldFormatterTest, FormatExpressionWithReplacements) {
EXPECT_EQ("\\+0041", result);
}
+TEST(FieldFormatterTest, FormatExpressionWithRegexpReplacement) {
+ base::flat_map<Key, std::string> mappings = {
+ {Key(1), "AA"}, {Key(2), "Bb"}, {Key(3), "Cc"}, {Key(4), "DD"}};
+ std::string result;
+
+ ValueExpression value_expression;
+ auto* chunk = value_expression.add_chunk();
+ // AA -> rA
+ chunk->set_key(1);
+ auto* case_insensitive_local = chunk->add_regexp_replacements();
+ case_insensitive_local->mutable_text_filter()->set_re2("a");
+ case_insensitive_local->mutable_text_filter()->set_case_sensitive(false);
+ case_insensitive_local->set_global(false);
+ case_insensitive_local->set_replacement("r");
+ // Bb -> Br
+ chunk = value_expression.add_chunk();
+ chunk->set_key(2);
+ auto* case_sensitive_local = chunk->add_regexp_replacements();
+ case_sensitive_local->mutable_text_filter()->set_re2("b");
+ case_sensitive_local->mutable_text_filter()->set_case_sensitive(true);
+ case_sensitive_local->set_global(false);
+ case_sensitive_local->set_replacement("r");
+ // Cc -> rr
+ chunk = value_expression.add_chunk();
+ chunk->set_key(3);
+ auto* case_insensitive_global = chunk->add_regexp_replacements();
+ case_insensitive_global->mutable_text_filter()->set_re2("c");
+ case_insensitive_global->mutable_text_filter()->set_case_sensitive(false);
+ case_insensitive_global->set_global(true);
+ case_insensitive_global->set_replacement("r");
+ // DD -> rr
+ chunk = value_expression.add_chunk();
+ chunk->set_key(4);
+ auto* case_insensitive_local_first = chunk->add_regexp_replacements();
+ case_insensitive_local_first->mutable_text_filter()->set_re2("d");
+ case_insensitive_local_first->mutable_text_filter()->set_case_sensitive(
+ false);
+ case_insensitive_local_first->set_global(false);
+ case_insensitive_local_first->set_replacement("r");
+ auto* case_insensitive_local_second = chunk->add_regexp_replacements();
+ case_insensitive_local_second->mutable_text_filter()->set_re2("d");
+ case_insensitive_local_second->mutable_text_filter()->set_case_sensitive(
+ false);
+ case_insensitive_local_second->set_global(false);
+ case_insensitive_local_second->set_replacement("r");
+
+ EXPECT_EQ(ACTION_APPLIED, FormatExpression(value_expression, mappings,
+ /* quote_meta= */ false, &result)
+ .proto_status());
+ EXPECT_EQ("rABrrrrr", result);
+}
+
+TEST(FieldFormatterTest, FormatExpressionWithRegexpReplacementCapturingGroups) {
+ std::string result;
+
+ ValueExpression value_expression;
+ auto* chunk = value_expression.add_chunk();
+ chunk->set_text("John Doe");
+ auto* group_replacement = chunk->add_regexp_replacements();
+ group_replacement->mutable_text_filter()->set_re2("(\\w+)\\s(\\w+)");
+ group_replacement->set_replacement("\\2 \\1");
+
+ EXPECT_EQ(ACTION_APPLIED,
+ FormatExpression(value_expression,
+ /* mappings= */ base::flat_map<Key, std::string>(),
+ /* quote_meta= */ false, &result)
+ .proto_status());
+ EXPECT_EQ("Doe John", result);
+}
+
+TEST(FieldFormatterTest, FormatExpressionWithInvalidRegexpReplacement) {
+ base::flat_map<Key, std::string> mappings = {{Key(1), "AA"}};
+ std::string result;
+
+ ValueExpression value_expression;
+ auto* chunk = value_expression.add_chunk();
+ // AA -> ?
+ chunk->set_key(1);
+ auto* invalid_replacement = chunk->add_regexp_replacements();
+ invalid_replacement->mutable_text_filter()->set_re2("^*");
+ invalid_replacement->mutable_text_filter()->set_case_sensitive(false);
+ invalid_replacement->set_global(false);
+ invalid_replacement->set_replacement("");
+
+ EXPECT_EQ(ACTION_APPLIED, FormatExpression(value_expression, mappings,
+ /* quote_meta= */ false, &result)
+ .proto_status());
+ EXPECT_EQ("AA", result);
+}
+
TEST(FieldFormatterTest, FormatExpressionWithMemoryKey) {
base::flat_map<Key, std::string> mappings = {{Key("_var0"), "valueA"},
{Key("_var1"), "val.ueB"}};
@@ -505,8 +595,15 @@ TEST(FieldFormatterTest, DifferentLocales) {
Contains(Pair(Key(36), "Vereinigte Staaten")));
// Invalid locales default to "en-US".
+ // Android and Desktop use a different default.
+#if BUILDFLAG(IS_ANDROID)
EXPECT_THAT(CreateAutofillMappings(profile, ""),
Contains(Pair(Key(36), "United States")));
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
+ BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
+ EXPECT_THAT(CreateAutofillMappings(profile, ""),
+ Contains(Pair(Key(36), "US")));
+#endif
EXPECT_THAT(CreateAutofillMappings(profile, "invalid"),
Contains(Pair(Key(36), "United States")));
}
diff --git a/chromium/components/autofill_assistant/browser/full_card_requester.cc b/chromium/components/autofill_assistant/browser/full_card_requester.cc
index c676a49e38d..f5359ca959a 100644
--- a/chromium/components/autofill_assistant/browser/full_card_requester.cc
+++ b/chromium/components/autofill_assistant/browser/full_card_requester.cc
@@ -38,16 +38,20 @@ void FullCardRequester::GetFullCard(
}
autofill::ContentAutofillDriver* driver =
- factory->DriverForFrame(web_contents->GetMainFrame());
+ factory->DriverForFrame(web_contents->GetPrimaryMainFrame());
if (!driver) {
OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE);
return;
}
- driver->browser_autofill_manager()->GetOrCreateFullCardRequest()->GetFullCard(
+ autofill::CreditCardCVCAuthenticator* cvc_authenticator =
+ driver->autofill_manager()
+ ->GetCreditCardAccessManager()
+ ->GetOrCreateCVCAuthenticator();
+ cvc_authenticator->GetFullCardRequest()->GetFullCard(
*card, autofill::AutofillClient::UnmaskCardReason::kAutofill,
weak_ptr_factory_.GetWeakPtr(),
- driver->browser_autofill_manager()->GetAsFullCardRequestUIDelegate());
+ cvc_authenticator->GetAsFullCardRequestUIDelegate());
}
FullCardRequester::~FullCardRequester() = default;
diff --git a/chromium/components/autofill_assistant/browser/full_card_requester_unittest.cc b/chromium/components/autofill_assistant/browser/full_card_requester_unittest.cc
index 7844e44aa12..4012f736681 100644
--- a/chromium/components/autofill_assistant/browser/full_card_requester_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/full_card_requester_unittest.cc
@@ -50,8 +50,9 @@ class FullCardRequesterTest : public testing::Test {
&browser_context_, nullptr);
autofill_client_.SetPrefs(autofill::test::PrefServiceForTesting());
autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
- web_contents_.get(), &autofill_client_, "en-US",
- autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+ web_contents_.get(), &autofill_client_,
+ base::BindRepeating(&autofill::BrowserDriverInitHook, &autofill_client_,
+ "en-US"));
autofill_client_.set_test_payments_client(
std::make_unique<autofill::payments::TestPaymentsClient>(
test_url_loader_factory_.GetSafeWeakWrapper(),
diff --git a/chromium/components/autofill_assistant/browser/headless/client_headless.cc b/chromium/components/autofill_assistant/browser/headless/client_headless.cc
index 5849a3b1a16..62e79f666aa 100644
--- a/chromium/components/autofill_assistant/browser/headless/client_headless.cc
+++ b/chromium/components/autofill_assistant/browser/headless/client_headless.cc
@@ -16,13 +16,18 @@
#include "components/autofill_assistant/browser/autofill_assistant_tts_controller.h"
#include "components/autofill_assistant/browser/controller.h"
#include "components/autofill_assistant/browser/display_strings_util.h"
+#include "components/autofill_assistant/browser/empty_website_login_manager_impl.h"
#include "components/autofill_assistant/browser/features.h"
+#include "components/autofill_assistant/browser/headless/external_script_controller_impl.h"
#include "components/autofill_assistant/browser/public/ui_state.h"
#include "components/autofill_assistant/browser/service/access_token_fetcher.h"
#include "components/autofill_assistant/browser/switches.h"
#include "components/autofill_assistant/browser/website_login_manager_impl.h"
+#include "components/password_manager/content/browser/password_change_success_tracker_factory.h"
#include "components/password_manager/core/browser/password_change_success_tracker.h"
#include "components/password_manager/core/browser/password_manager_client.h"
+#include "components/signin/public/identity_manager/account_info.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -33,22 +38,40 @@
namespace autofill_assistant {
-ClientHeadless::ClientHeadless(content::WebContents* web_contents)
- : web_contents_(web_contents) {
- headless_ui_controller_ = std::make_unique<HeadlessUiController>();
+const char kOAuth2Scope[] = "https://www.googleapis.com/auth/userinfo.profile";
+const char kConsumerName[] = "autofill_assistant";
+
+ClientHeadless::ClientHeadless(
+ content::WebContents* web_contents,
+ const CommonDependencies* common_dependencies,
+ ExternalActionDelegate* action_extension_delegate,
+ ExternalScriptControllerImpl* external_script_controller)
+ : web_contents_(web_contents),
+ common_dependencies_(common_dependencies),
+ external_script_controller_(external_script_controller) {
+ auto* password_manager_client =
+ common_dependencies_->GetPasswordManagerClient(web_contents);
+ if (password_manager_client) {
+ website_login_manager_ = std::make_unique<WebsiteLoginManagerImpl>(
+ password_manager_client, web_contents);
+ } else {
+ website_login_manager_ = std::make_unique<EmptyWebsiteLoginManagerImpl>();
+ }
+ headless_ui_controller_ =
+ std::make_unique<HeadlessUiController>(action_extension_delegate);
}
ClientHeadless::~ClientHeadless() = default;
void ClientHeadless::Start(const GURL& url,
- std::unique_ptr<TriggerContext> trigger_context,
- ControllerObserver* observer) {
+ std::unique_ptr<TriggerContext> trigger_context) {
controller_ = std::make_unique<Controller>(
web_contents_, /* client= */ this, base::DefaultTickClock::GetInstance(),
RuntimeManager::GetForWebContents(web_contents_)->GetWeakPtr(),
/* service= */ nullptr, ukm::UkmRecorder::Get(),
- /* annotate_dom_model_service= */ nullptr);
- controller_->AddObserver(observer);
+ /* annotate_dom_model_service= */
+ common_dependencies_->GetOrCreateAnnotateDomModelService(
+ GetWebContents()->GetBrowserContext()));
controller_->Start(url, std::move(trigger_context));
}
@@ -65,18 +88,16 @@ void ClientHeadless::DestroyUISoon() {}
void ClientHeadless::DestroyUI() {}
version_info::Channel ClientHeadless::GetChannel() const {
- // TODO(b/201964911): Inject on instantiation.
- return version_info::Channel::DEV;
+ return common_dependencies_->GetChannel();
}
std::string ClientHeadless::GetEmailAddressForAccessTokenAccount() const {
- // TODO(b/201964911): return the Chrome signed in user.
- return "";
+ return GetSignedInEmail();
}
-std::string ClientHeadless::GetChromeSignedInEmailAddress() const {
- // TODO(b/201964911): return the Chrome signed in user.
- return "";
+std::string ClientHeadless::GetSignedInEmail() const {
+ return common_dependencies_->GetSignedInEmail(
+ GetWebContents()->GetBrowserContext());
}
absl::optional<std::pair<int, int>> ClientHeadless::GetWindowSize() const {
@@ -95,34 +116,30 @@ void ClientHeadless::FetchPaymentsClientToken(
}
AccessTokenFetcher* ClientHeadless::GetAccessTokenFetcher() {
- // TODO(b/201964911): get access token via native.
- return nullptr;
+ return this;
}
autofill::PersonalDataManager* ClientHeadless::GetPersonalDataManager() const {
- // TODO(b/201964911): support PersonalDataManager.
- return nullptr;
+ return common_dependencies_->GetPersonalDataManager(
+ GetWebContents()->GetBrowserContext());
}
WebsiteLoginManager* ClientHeadless::GetWebsiteLoginManager() const {
- // TODO(b/201964911): return instance.
- return nullptr;
+ return website_login_manager_.get();
}
password_manager::PasswordChangeSuccessTracker*
ClientHeadless::GetPasswordChangeSuccessTracker() const {
- // TODO(b/201964911): return instance.
- return nullptr;
+ return password_manager::PasswordChangeSuccessTrackerFactory::
+ GetForBrowserContext(GetWebContents()->GetBrowserContext());
}
std::string ClientHeadless::GetLocale() const {
- // TODO(b/201964911): get locale via native.
- return "en-us";
+ return common_dependencies_->GetLocale();
}
std::string ClientHeadless::GetCountryCode() const {
- // TODO(b/201964911): get country code via native.
- return "us";
+ return common_dependencies_->GetCountryCode();
}
DeviceContext ClientHeadless::GetDeviceContext() const {
@@ -141,9 +158,7 @@ content::WebContents* ClientHeadless::GetWebContents() const {
return web_contents_;
}
-void ClientHeadless::RecordDropOut(Metrics::DropOutReason reason) {
- // TODO(b/201964911): Add metrics.
-}
+void ClientHeadless::RecordDropOut(Metrics::DropOutReason reason) {}
bool ClientHeadless::HasHadUI() const {
return false;
@@ -153,16 +168,67 @@ ScriptExecutorUiDelegate* ClientHeadless::GetScriptExecutorUiDelegate() {
return headless_ui_controller_.get();
}
-void ClientHeadless::Shutdown(Metrics::DropOutReason reason) {}
+bool ClientHeadless::MustUseBackendData() const {
+ return false;
+}
+
+void ClientHeadless::GetAnnotateDomModelVersion(
+ base::OnceCallback<void(absl::optional<int64_t>)> callback) const {
+ std::move(callback).Run(absl::nullopt);
+}
+
+void ClientHeadless::Shutdown(Metrics::DropOutReason reason) {
+ // This call can cause Controller to be destroyed. For this reason we delay it
+ // to avoid UAF errors in the controller.
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&ClientHeadless::NotifyScriptEnded,
+ weak_ptr_factory_.GetWeakPtr(), reason));
+}
+
+void ClientHeadless::NotifyScriptEnded(Metrics::DropOutReason reason) {
+ external_script_controller_->NotifyScriptEnded(reason);
+
+ // This instance can be destroyed by the above call, so nothing should be
+ // added here.
+}
void ClientHeadless::FetchAccessToken(
base::OnceCallback<void(bool, const std::string&)> callback) {
- // TODO(b/201964911): get access token via native.
- std::move(callback).Run(false, "");
+ DCHECK(!fetch_access_token_callback_);
+ fetch_access_token_callback_ = std::move(callback);
+ auto* identity_manager = common_dependencies_->GetIdentityManager(
+ GetWebContents()->GetBrowserContext());
+ access_token_fetcher_ = identity_manager->CreateAccessTokenFetcherForAccount(
+ identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync),
+ kConsumerName, {kOAuth2Scope},
+ base::BindOnce(&ClientHeadless::OnAccessTokenFetchComplete,
+ weak_ptr_factory_.GetWeakPtr()),
+ signin::AccessTokenFetcher::Mode::kImmediate);
+}
+
+void ClientHeadless::OnAccessTokenFetchComplete(
+ GoogleServiceAuthError error,
+ signin::AccessTokenInfo access_token_info) {
+ if (!fetch_access_token_callback_) {
+ return;
+ }
+
+ if (error.state() != GoogleServiceAuthError::NONE) {
+ VLOG(2) << "OAuth2 token request failed. " << error.state() << ": "
+ << error.ToString();
+
+ std::move(fetch_access_token_callback_).Run(false, "");
+ return;
+ }
+ std::move(fetch_access_token_callback_).Run(true, access_token_info.token);
}
void ClientHeadless::InvalidateAccessToken(const std::string& access_token) {
- // TODO(b/201964911): get access token via native.
+ auto* identity_manager = common_dependencies_->GetIdentityManager(
+ GetWebContents()->GetBrowserContext());
+ identity_manager->RemoveAccessTokenFromCache(
+ identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync),
+ {kOAuth2Scope}, access_token);
}
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/headless/client_headless.h b/chromium/components/autofill_assistant/browser/headless/client_headless.h
index a06f67a67b8..a29e05344ea 100644
--- a/chromium/components/autofill_assistant/browser/headless/client_headless.h
+++ b/chromium/components/autofill_assistant/browser/headless/client_headless.h
@@ -11,32 +11,39 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/client.h"
+#include "components/autofill_assistant/browser/common_dependencies.h"
#include "components/autofill_assistant/browser/controller.h"
#include "components/autofill_assistant/browser/device_context.h"
#include "components/autofill_assistant/browser/headless/headless_ui_controller.h"
+#include "components/autofill_assistant/browser/platform_dependencies.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/service/access_token_fetcher.h"
#include "components/autofill_assistant/browser/service/service.h"
#include "components/autofill_assistant/browser/website_login_manager.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
#include "content/public/browser/web_contents.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
namespace autofill_assistant {
+class ExternalScriptControllerImpl;
+
// An Autofill Assistant client for headless runs.
class ClientHeadless : public Client, public AccessTokenFetcher {
public:
- explicit ClientHeadless(content::WebContents* web_contents);
+ explicit ClientHeadless(
+ content::WebContents* web_contents,
+ const CommonDependencies* common_dependencies,
+ ExternalActionDelegate* action_extension_delegate,
+ ExternalScriptControllerImpl* external_script_controller);
ClientHeadless(const ClientHeadless&) = delete;
ClientHeadless& operator=(const ClientHeadless&) = delete;
~ClientHeadless() override;
bool IsRunning() const;
- void Start(const GURL& url,
- std::unique_ptr<TriggerContext> trigger_context,
- ControllerObserver* observer);
+ void Start(const GURL& url, std::unique_ptr<TriggerContext> trigger_context);
// Overrides Client
void AttachUI() override;
@@ -44,7 +51,7 @@ class ClientHeadless : public Client, public AccessTokenFetcher {
void DestroyUI() override;
version_info::Channel GetChannel() const override;
std::string GetEmailAddressForAccessTokenAccount() const override;
- std::string GetChromeSignedInEmailAddress() const override;
+ std::string GetSignedInEmail() const override;
absl::optional<std::pair<int, int>> GetWindowSize() const override;
ClientContextProto::ScreenOrientation GetScreenOrientation() const override;
void FetchPaymentsClientToken(
@@ -64,6 +71,10 @@ class ClientHeadless : public Client, public AccessTokenFetcher {
void RecordDropOut(Metrics::DropOutReason reason) override;
bool HasHadUI() const override;
ScriptExecutorUiDelegate* GetScriptExecutorUiDelegate() override;
+ bool MustUseBackendData() const override;
+ void GetAnnotateDomModelVersion(
+ base::OnceCallback<void(absl::optional<int64_t>)> callback)
+ const override;
// Overrides AccessTokenFetcher
void FetchAccessToken(
@@ -74,10 +85,20 @@ class ClientHeadless : public Client, public AccessTokenFetcher {
void CreateController();
void DestroyController();
void SafeDestroyController(Metrics::DropOutReason reason);
+ void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
+ signin::AccessTokenInfo access_token_info);
+ void NotifyScriptEnded(Metrics::DropOutReason reason);
- content::WebContents* web_contents_;
+ raw_ptr<content::WebContents> web_contents_;
std::unique_ptr<Controller> controller_;
+ const raw_ptr<const CommonDependencies> common_dependencies_;
+ std::unique_ptr<WebsiteLoginManager> website_login_manager_;
std::unique_ptr<HeadlessUiController> headless_ui_controller_;
+ raw_ptr<signin::IdentityManager> identity_manager_ = nullptr;
+ std::unique_ptr<signin::AccessTokenFetcher> access_token_fetcher_;
+ base::OnceCallback<void(bool, const std::string&)>
+ fetch_access_token_callback_;
+ const raw_ptr<ExternalScriptControllerImpl> external_script_controller_;
base::WeakPtrFactory<ClientHeadless> weak_ptr_factory_{this};
};
diff --git a/chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.cc b/chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.cc
index 477a5c8bf33..f1c5285d753 100644
--- a/chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.cc
+++ b/chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.cc
@@ -11,10 +11,17 @@
namespace autofill_assistant {
ExternalScriptControllerImpl::ExternalScriptControllerImpl(
- content::WebContents* web_contents)
+ content::WebContents* web_contents,
+ ExternalActionDelegate* action_extension_delegate)
: web_contents_(web_contents) {
DCHECK(web_contents_);
- client_ = std::make_unique<ClientHeadless>(web_contents);
+
+ auto* starter = Starter::FromWebContents(web_contents_);
+ if (starter) {
+ client_ = std::make_unique<ClientHeadless>(web_contents,
+ starter->GetCommonDependencies(),
+ action_extension_delegate, this);
+ }
}
ExternalScriptControllerImpl::~ExternalScriptControllerImpl() = default;
@@ -29,8 +36,9 @@ void ExternalScriptControllerImpl::StartScript(
return;
}
auto* starter = Starter::FromWebContents(web_contents_);
- // The starter has not yet been initialized.
- if (!starter) {
+ // The starter has not yet been initialized or was not initialized at the
+ // time the constructor was called.
+ if (!starter || !client_) {
std::move(script_ended_callback).Run({false});
return;
}
@@ -40,8 +48,7 @@ void ExternalScriptControllerImpl::StartScript(
auto trigger_context = std::make_unique<TriggerContext>(
std::move(parameters),
/* experiment_ids = */ "",
- // TODO(b/201964911): set the right value for Android flows
- /*is_cct = */ false,
+ starter->GetPlatformDependencies()->IsCustomTab(*web_contents_),
/*onboarding_shown = */ false,
/*is_direct_action = */ false,
/* initial_url = */ "",
@@ -62,43 +69,13 @@ void ExternalScriptControllerImpl::OnReadyToStart(
}
// TODO(b/201964911): At this point we should be sure no other Controller
// exists on this tab. Add logic to the starter to check that's the case.
- client_->Start(*url, std::move(trigger_context), this);
+ client_->Start(*url, std::move(trigger_context));
}
-void ExternalScriptControllerImpl::OnShutdown(Metrics::DropOutReason reason) {
- // TODO(b/201964911): since the Controller has not been destroyed yet, this
- // could lead to a race condition if |StartScript| is called again right away
- // after receiving this notification.
+void ExternalScriptControllerImpl::NotifyScriptEnded(
+ Metrics::DropOutReason reason) {
std::move(script_ended_callback_)
.Run({reason == Metrics::DropOutReason::SCRIPT_SHUTDOWN});
}
-void ExternalScriptControllerImpl::OnStateChanged(
- AutofillAssistantState new_state) {}
-void ExternalScriptControllerImpl::OnKeyboardSuppressionStateChanged(
- bool should_suppress_keyboard) {}
-void ExternalScriptControllerImpl::CloseCustomTab() {}
-void ExternalScriptControllerImpl::OnError(const std::string& error_message,
- Metrics::DropOutReason reason) {}
-void ExternalScriptControllerImpl::OnUserDataChanged(
- const UserData& user_data,
- UserDataFieldChange field_change) {}
-void ExternalScriptControllerImpl::OnTouchableAreaChanged(
- const RectF& visual_viewport,
- const std::vector<RectF>& touchable_areas,
- const std::vector<RectF>& restricted_areas) {}
-void ExternalScriptControllerImpl::OnViewportModeChanged(ViewportMode mode) {}
-void ExternalScriptControllerImpl::OnOverlayColorsChanged(
- const ExecutionDelegate::OverlayColors& colors) {}
-void ExternalScriptControllerImpl::OnClientSettingsChanged(
- const ClientSettings& settings) {}
-void ExternalScriptControllerImpl::OnShouldShowOverlayChanged(
- bool should_show) {}
-void ExternalScriptControllerImpl::OnExecuteScript(
- const std::string& start_message) {}
-void ExternalScriptControllerImpl::OnStart(
- const TriggerContext& trigger_context) {}
-void ExternalScriptControllerImpl::OnStop() {}
-void ExternalScriptControllerImpl::OnResetState() {}
-void ExternalScriptControllerImpl::OnUiShownChanged(bool shown) {}
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.h b/chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.h
index fb79972b327..88ae49bab94 100644
--- a/chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.h
+++ b/chromium/components/autofill_assistant/browser/headless/external_script_controller_impl.h
@@ -10,19 +10,22 @@
#include <vector>
#include "base/callback_helpers.h"
+#include "base/memory/raw_ptr.h"
#include "components/autofill_assistant/browser/autofill_assistant_impl.h"
#include "components/autofill_assistant/browser/controller.h"
#include "components/autofill_assistant/browser/controller_observer.h"
#include "components/autofill_assistant/browser/execution_delegate.h"
#include "components/autofill_assistant/browser/headless/client_headless.h"
+#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/script_executor_ui_delegate.h"
namespace autofill_assistant {
-class ExternalScriptControllerImpl : public ExternalScriptController,
- public ControllerObserver {
+class ExternalScriptControllerImpl : public ExternalScriptController {
public:
- ExternalScriptControllerImpl(content::WebContents* web_contents);
+ ExternalScriptControllerImpl(
+ content::WebContents* web_contents,
+ ExternalActionDelegate* action_extension_delegate);
ExternalScriptControllerImpl(const ExternalScriptControllerImpl&) = delete;
ExternalScriptControllerImpl& operator=(const ExternalScriptControllerImpl&) =
@@ -35,38 +38,16 @@ class ExternalScriptControllerImpl : public ExternalScriptController,
const base::flat_map<std::string, std::string>& script_parameters,
base::OnceCallback<void(ScriptResult)> script_ended_callback) override;
- // Overrides ControllerObserver.
- // TODO(b/201964911): Add empty default ControllerObserver to avoid having all
- // of these no-op implementations here.
- void OnStateChanged(AutofillAssistantState new_state) override;
- void OnKeyboardSuppressionStateChanged(
- bool should_suppress_keyboard) override;
- void CloseCustomTab() override;
- void OnError(const std::string& error_message,
- Metrics::DropOutReason reason) override;
- void OnUserDataChanged(const UserData& user_data,
- UserDataFieldChange field_change) override;
- void OnTouchableAreaChanged(
- const RectF& visual_viewport,
- const std::vector<RectF>& touchable_areas,
- const std::vector<RectF>& restricted_areas) override;
- void OnViewportModeChanged(ViewportMode mode) override;
- void OnOverlayColorsChanged(
- const ExecutionDelegate::OverlayColors& colors) override;
- void OnClientSettingsChanged(const ClientSettings& settings) override;
- void OnShouldShowOverlayChanged(bool should_show) override;
- void OnExecuteScript(const std::string& start_message) override;
- void OnStart(const TriggerContext& trigger_context) override;
- void OnStop() override;
- void OnResetState() override;
- void OnUiShownChanged(bool shown) override;
- void OnShutdown(Metrics::DropOutReason reason) override;
+ // Notifies the external caller that the script has ended. Note that the
+ // external caller can decide to destroy this instance once it has been
+ // notified so this method should not be called directly to avoid UAF issues.
+ void NotifyScriptEnded(Metrics::DropOutReason reason);
private:
void OnReadyToStart(bool can_start,
absl::optional<GURL> url,
std::unique_ptr<TriggerContext> trigger_context);
- content::WebContents* web_contents_;
+ raw_ptr<content::WebContents> web_contents_;
std::unique_ptr<ClientHeadless> client_;
base::OnceCallback<void(ScriptResult)> script_ended_callback_;
diff --git a/chromium/components/autofill_assistant/browser/headless/headless_ui_controller.cc b/chromium/components/autofill_assistant/browser/headless/headless_ui_controller.cc
index 89a8b28ddad..5def234bbd5 100644
--- a/chromium/components/autofill_assistant/browser/headless/headless_ui_controller.cc
+++ b/chromium/components/autofill_assistant/browser/headless/headless_ui_controller.cc
@@ -6,7 +6,37 @@
namespace autofill_assistant {
-HeadlessUiController::HeadlessUiController() = default;
+HeadlessUiController::HeadlessUiController(
+ ExternalActionDelegate* action_extension_delegate)
+ : action_extension_delegate_(action_extension_delegate) {}
+
+bool HeadlessUiController::SupportsExternalActions() {
+ return action_extension_delegate_ != nullptr;
+}
+
+void HeadlessUiController::ExecuteExternalAction(
+ const external::Action& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ DCHECK(action_extension_delegate_);
+
+ action_extension_delegate_->OnActionRequested(
+ external_action, std::move(start_dom_checks_callback),
+ std::move(end_action_callback));
+}
+
+void HeadlessUiController::OnInterruptStarted() {
+ if (action_extension_delegate_) {
+ action_extension_delegate_->OnInterruptStarted();
+ }
+}
+void HeadlessUiController::OnInterruptFinished() {
+ if (action_extension_delegate_) {
+ action_extension_delegate_->OnInterruptFinished();
+ }
+}
// TODO(b/201964911): fail execution instead of just logging a warning if a
// method is unexpectedly called.
@@ -122,6 +152,11 @@ void HeadlessUiController::SetCollectUserDataOptions(
CollectUserDataOptions* options) {
VLOG(2) << "Unexpected UI method called: " << __func__;
}
+void HeadlessUiController::SetCollectUserDataUiState(
+ bool loading,
+ UserDataEventField event_field) {
+ VLOG(2) << "Unexpected UI method called: " << __func__;
+}
void HeadlessUiController::SetLastSuccessfulUserDataOptions(
std::unique_ptr<CollectUserDataOptions> collect_user_data_options) {
VLOG(2) << "Unexpected UI method called: " << __func__;
diff --git a/chromium/components/autofill_assistant/browser/headless/headless_ui_controller.h b/chromium/components/autofill_assistant/browser/headless/headless_ui_controller.h
index 79b79d39f9f..eae4973e94f 100644
--- a/chromium/components/autofill_assistant/browser/headless/headless_ui_controller.h
+++ b/chromium/components/autofill_assistant/browser/headless/headless_ui_controller.h
@@ -19,7 +19,10 @@ namespace autofill_assistant {
class HeadlessUiController : public ScriptExecutorUiDelegate {
public:
- HeadlessUiController();
+ // The |action_extension_delegate| parameter can be null but if an extension
+ // action is requested it will cause the script to fail.
+ explicit HeadlessUiController(
+ ExternalActionDelegate* action_extension_delegate);
// Overrides ScriptExecutorUiDelegate
void SetStatusMessage(const std::string& message) override;
@@ -68,10 +71,24 @@ class HeadlessUiController : public ScriptExecutorUiDelegate {
void SetExpandSheetForPromptAction(bool expand) override;
void SetCollectUserDataOptions(CollectUserDataOptions* options) override;
+ void SetCollectUserDataUiState(bool loading,
+ UserDataEventField event_field) override;
void SetLastSuccessfulUserDataOptions(std::unique_ptr<CollectUserDataOptions>
collect_user_data_options) override;
const CollectUserDataOptions* GetLastSuccessfulUserDataOptions()
const override;
+ bool SupportsExternalActions() override;
+ void ExecuteExternalAction(
+ const external::Action& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) override;
+ void OnInterruptStarted() override;
+ void OnInterruptFinished() override;
+
+ private:
+ const raw_ptr<ExternalActionDelegate> action_extension_delegate_;
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/js_flow_devtools_wrapper.cc b/chromium/components/autofill_assistant/browser/js_flow_devtools_wrapper.cc
new file mode 100644
index 00000000000..de0f43b0616
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/js_flow_devtools_wrapper.cc
@@ -0,0 +1,172 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/js_flow_devtools_wrapper.h"
+
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/strings/strcat.h"
+#include "components/autofill_assistant/browser/features.h"
+#include "components/autofill_assistant/browser/js_flow_util.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/web/web_controller_util.h"
+
+namespace autofill_assistant {
+
+constexpr char kAboutBlankURL[] = "about:blank";
+
+namespace {
+std::unique_ptr<DevtoolsClient> CreateDevtoolsClient(
+ content::WebContents* web_contents) {
+ return std::make_unique<DevtoolsClient>(
+ content::DevToolsAgentHost::GetOrCreateFor(web_contents),
+ base::FeatureList::IsEnabled(
+ autofill_assistant::features::
+ kAutofillAssistantFullJsFlowStackTraces));
+}
+} // namespace
+
+JsFlowDevtoolsWrapper::JsFlowDevtoolsWrapper(
+ content::BrowserContext* browser_context)
+ : browser_context_(browser_context) {}
+
+JsFlowDevtoolsWrapper::JsFlowDevtoolsWrapper(content::WebContents* web_contents)
+ : devtools_client_(CreateDevtoolsClient(web_contents)) {}
+
+void JsFlowDevtoolsWrapper::SetJsFlowLibrary(
+ const std::string& js_flow_library) {
+ if (InitStarted() || InitDone()) {
+ LOG(ERROR) << "The js flow library can't be set after the devtools wrapper "
+ "has started initializing.";
+ return;
+ }
+
+ js_flow_library_ = js_flow_library;
+}
+
+JsFlowDevtoolsWrapper::~JsFlowDevtoolsWrapper() = default;
+
+void JsFlowDevtoolsWrapper::GetDevtoolsAndMaybeInit(
+ base::OnceCallback<void(const ClientStatus& status,
+ DevtoolsClient* devtools_client,
+ int isolated_world_context_id)> callback) {
+ if (InitDone()) {
+ std::move(callback).Run(init_status_, devtools_client_.get(),
+ isolated_world_context_id_);
+ return;
+ }
+
+ if (InitStarted()) {
+ LOG(ERROR) << "Invoked " << __func__ << " while already initializing";
+ return;
+ }
+ callback_ = std::move(callback);
+
+ MabyeCreateDevtoolsClient();
+ devtools_client_->GetPage()->GetFrameTree(
+ js_flow_util::kMainFrame,
+ base::BindOnce(&JsFlowDevtoolsWrapper::OnGetFrameTree,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void JsFlowDevtoolsWrapper::MabyeCreateDevtoolsClient() {
+ if (devtools_client_) {
+ return;
+ }
+
+ // To execute JS flows we create a new web contents that persists
+ // across navigations.
+ web_contents_ = content::WebContents::Create(
+ content::WebContents::CreateParams(browser_context_));
+ // Navigate to a blank page to connect to a frame tree.
+ web_contents_->GetController().LoadURLWithParams(
+ content::NavigationController::LoadURLParams(GURL(kAboutBlankURL)));
+
+ devtools_client_ = CreateDevtoolsClient(web_contents_.get());
+}
+
+void JsFlowDevtoolsWrapper::OnGetFrameTree(
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<page::GetFrameTreeResult> result) {
+ if (!result) {
+ LOG(ERROR) << "failed to retrieve frame tree";
+ init_status_ =
+ JavaScriptErrorStatus(reply_status, __FILE__, __LINE__, nullptr);
+ FinishInit();
+ return;
+ }
+ VLOG(2) << "frame tree retrieved";
+
+ devtools_client_->GetPage()->CreateIsolatedWorld(
+ page::CreateIsolatedWorldParams::Builder()
+ .SetFrameId(result->GetFrameTree()->GetFrame()->GetId())
+ .Build(),
+ js_flow_util::kMainFrame,
+ base::BindOnce(&JsFlowDevtoolsWrapper::OnIsolatedWorldCreated,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void JsFlowDevtoolsWrapper::OnIsolatedWorldCreated(
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<page::CreateIsolatedWorldResult> result) {
+ if (!result) {
+ LOG(ERROR) << "failed to create isolated world";
+ init_status_ =
+ JavaScriptErrorStatus(reply_status, __FILE__, __LINE__, nullptr);
+ FinishInit();
+ return;
+ }
+ VLOG(2) << "isolated world created";
+
+ isolated_world_context_id_ = result->GetExecutionContextId();
+
+ // Append the source url.
+ const auto js_flow_library = base::StrCat(
+ {js_flow_library_, js_flow_util::GetDevtoolsSourceUrlCommentToAppend(
+ UnexpectedErrorInfoProto::JS_FLOW_LIBRARY)});
+
+ devtools_client_->GetRuntime()->Evaluate(
+ runtime::EvaluateParams::Builder()
+ .SetExpression(js_flow_library)
+ .SetContextId(isolated_world_context_id_)
+ .SetAwaitPromise(true)
+ .SetReturnByValue(true)
+ .Build(),
+ js_flow_util::kMainFrame,
+ base::BindOnce(&JsFlowDevtoolsWrapper::OnJsFlowLibraryEvaluated,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void JsFlowDevtoolsWrapper::OnJsFlowLibraryEvaluated(
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::EvaluateResult> result) {
+ std::unique_ptr<base::Value> unused;
+ init_status_ = js_flow_util::ExtractFlowReturnValue(
+ reply_status, result.get(), unused, {}, 0);
+
+ if (init_status_.ok()) {
+ VLOG(2) << "JS flow library (length " << js_flow_library_.length()
+ << ") evaluated";
+ } else {
+ LOG(ERROR) << "JS flow library (length " << js_flow_library_.length()
+ << ") could not be evaluated";
+ }
+
+ FinishInit();
+}
+
+void JsFlowDevtoolsWrapper::FinishInit() {
+ std::move(callback_).Run(init_status_, devtools_client_.get(),
+ isolated_world_context_id_);
+}
+
+bool JsFlowDevtoolsWrapper::InitStarted() {
+ return !callback_.is_null();
+}
+
+bool JsFlowDevtoolsWrapper::InitDone() {
+ return isolated_world_context_id_ != -1 || !init_status_.ok();
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/js_flow_devtools_wrapper.h b/chromium/components/autofill_assistant/browser/js_flow_devtools_wrapper.h
new file mode 100644
index 00000000000..2f98ffa9d7d
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/js_flow_devtools_wrapper.h
@@ -0,0 +1,98 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_JS_FLOW_DEVTOOLS_WRAPPER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_JS_FLOW_DEVTOOLS_WRAPPER_H_
+
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/client_status.h"
+#include "components/autofill_assistant/browser/devtools/devtools_client.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace autofill_assistant {
+
+// Wraps a devtools client for js flow execution. After the first call to
+// GetDevtoolsAndMaybeInit the js flow library can not be changed anymore.
+// NOTE: This class is tested in ScriptExecutorBrowserTest and
+// JsFlowExecutorImplBrowserTest.
+class JsFlowDevtoolsWrapper {
+ public:
+ // Creates and owns the web contents. The devtools client and web contents are
+ // lazily initialized on the first call to GetDevtoolsAndMaybeInit.
+ explicit JsFlowDevtoolsWrapper(content::BrowserContext* browser_context);
+
+ // Does not own the web contents.
+ explicit JsFlowDevtoolsWrapper(content::WebContents* web_contents);
+
+ ~JsFlowDevtoolsWrapper();
+ JsFlowDevtoolsWrapper(const JsFlowDevtoolsWrapper&) = delete;
+ JsFlowDevtoolsWrapper& operator=(const JsFlowDevtoolsWrapper&) = delete;
+
+ // The first call to this function starts the initialization. Afterwards the
+ // callback is called immediately with the results from the initialization.
+ //
+ // If an error occurred during initialization status.ok() is false. The
+ // devtools client and isolated world context id are only guaranteed to be
+ // valid if status.ok() is true.
+ void GetDevtoolsAndMaybeInit(
+ base::OnceCallback<void(const ClientStatus& status,
+ DevtoolsClient* devtools_client,
+ int isolated_world_context_id)> callback);
+
+ // Sets the js flow library. Can only be called before the first call to
+ // GetDevtoolsAndMaybeInit.
+ void SetJsFlowLibrary(const std::string& js_flow_library);
+
+ private:
+ // Creates the web contents and devtools client if the browser context
+ // constructor was used.
+ void MabyeCreateDevtoolsClient();
+
+ void OnGetFrameTree(const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<page::GetFrameTreeResult> result);
+
+ void OnIsolatedWorldCreated(
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<page::CreateIsolatedWorldResult> result);
+
+ void OnJsFlowLibraryEvaluated(
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::EvaluateResult> result);
+
+ // Called when the devtools environment has finished initializing. Guaranteed
+ // to be called after the first call to GetDevtoolsAndMaybeInit (eventually).
+ void FinishInit();
+
+ // True if the wrapper is currently initializing (after
+ // GetDevtoolsAndMaybeInit was called but
+ // before FinishInit was called).
+ bool InitStarted();
+ // True after FinishInit was called.
+ bool InitDone();
+
+ content::BrowserContext* browser_context_;
+ std::string js_flow_library_;
+
+ // Only set for the browser context constructor. Lazily instantiated.
+ std::unique_ptr<content::WebContents> web_contents_;
+ std::unique_ptr<DevtoolsClient> devtools_client_;
+
+ // Set after the wrapper has finished initialization.
+ ClientStatus init_status_ = ClientStatus(ACTION_APPLIED);
+ int isolated_world_context_id_ = -1;
+
+ base::OnceCallback<void(const ClientStatus& status,
+ DevtoolsClient* devtools_client,
+ int isolated_world_context_id)>
+ callback_;
+
+ base::WeakPtrFactory<JsFlowDevtoolsWrapper> weak_ptr_factory_{this};
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_JS_FLOW_DEVTOOLS_WRAPPER_H_
diff --git a/chromium/components/autofill_assistant/browser/js_flow_executor_impl.cc b/chromium/components/autofill_assistant/browser/js_flow_executor_impl.cc
index ce9132f80c1..d6ab13e3a6c 100644
--- a/chromium/components/autofill_assistant/browser/js_flow_executor_impl.cc
+++ b/chromium/components/autofill_assistant/browser/js_flow_executor_impl.cc
@@ -14,6 +14,9 @@
#include "components/autofill_assistant/browser/js_flow_util.h"
#include "components/autofill_assistant/browser/parse_jspb.h"
#include "components/autofill_assistant/browser/web/web_controller_util.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/web_contents.h"
namespace autofill_assistant {
namespace {
@@ -88,8 +91,6 @@ constexpr char kFulfillActionPromise[] = R"(
}
)";
-constexpr char kMainFrame[] = "";
-
absl::optional<std::string> ConvertActionToBytes(const base::Value* action,
std::string* error_message) {
if (action == nullptr) {
@@ -115,14 +116,11 @@ absl::optional<std::string> ConvertActionToBytes(const base::Value* action,
} // namespace
-JsFlowExecutorImpl::JsFlowExecutorImpl(content::WebContents* web_contents,
- Delegate* delegate)
+JsFlowExecutorImpl::JsFlowExecutorImpl(
+ Delegate* delegate,
+ JsFlowDevtoolsWrapper* js_flow_devtools_wrapper)
: delegate_(delegate),
- devtools_client_(std::make_unique<DevtoolsClient>(
- content::DevToolsAgentHost::GetOrCreateFor(web_contents),
- base::FeatureList::IsEnabled(
- autofill_assistant::features::
- kAutofillAssistantFullJsFlowStackTraces))) {}
+ js_flow_devtools_wrapper_(js_flow_devtools_wrapper) {}
JsFlowExecutorImpl::~JsFlowExecutorImpl() = default;
@@ -138,53 +136,23 @@ void JsFlowExecutorImpl::Start(
js_flow_ = std::make_unique<std::string>(js_flow);
callback_ = std::move(callback);
- if (isolated_world_context_id_ == -1) {
- devtools_client_->GetPage()->GetFrameTree(
- kMainFrame, base::BindOnce(&JsFlowExecutorImpl::OnGetFrameTree,
- weak_ptr_factory_.GetWeakPtr()));
- } else {
- InternalStart();
- }
-}
-
-void JsFlowExecutorImpl::OnGetFrameTree(
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<page::GetFrameTreeResult> result) {
- if (!result) {
- LOG(ERROR) << "Failed to retrieve frame tree";
- std::move(callback_).Run(
- JavaScriptErrorStatus(reply_status, __FILE__, __LINE__, nullptr),
- nullptr);
- return;
- }
- devtools_client_->GetPage()->CreateIsolatedWorld(
- page::CreateIsolatedWorldParams::Builder()
- .SetFrameId(result->GetFrameTree()->GetFrame()->GetId())
- .Build(),
- kMainFrame,
- base::BindOnce(&JsFlowExecutorImpl::IsolatedWorldCreated,
- weak_ptr_factory_.GetWeakPtr()));
+ js_flow_devtools_wrapper_->GetDevtoolsAndMaybeInit(base::BindOnce(
+ &JsFlowExecutorImpl::InternalStart, weak_ptr_factory_.GetWeakPtr()));
}
-void JsFlowExecutorImpl::IsolatedWorldCreated(
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<page::CreateIsolatedWorldResult> result) {
- if (!result) {
- LOG(ERROR) << "Failed to create isolated world";
- std::move(callback_).Run(
- JavaScriptErrorStatus(reply_status, __FILE__, __LINE__, nullptr),
- nullptr);
+void JsFlowExecutorImpl::InternalStart(const ClientStatus& status,
+ DevtoolsClient* devtools_client,
+ const int isolated_world_context_id) {
+ DCHECK(callback_);
+
+ if (!status.ok()) {
+ RunCallback(status, nullptr);
return;
}
- isolated_world_context_id_ = result->GetExecutionContextId();
- InternalStart();
-}
-
-void JsFlowExecutorImpl::InternalStart() {
- DCHECK(isolated_world_context_id_ != -1);
- DCHECK(callback_);
+ devtools_client_ = devtools_client;
+ isolated_world_context_id_ = isolated_world_context_id;
// Before running the flow in the sandbox, we define a promise that
// the flow may fulfill to request execution of a native action.
@@ -192,9 +160,11 @@ void JsFlowExecutorImpl::InternalStart() {
// Wrap the main js_flow in an async function containing a method to
// request native actions. This is essentially providing |js_flow| with a
- // JS API to call native functionality.
+ // JS API to call native functionality. Also appends the source url.
js_flow_ = std::make_unique<std::string>(
- base::StrCat({kLeadingWrapper, *js_flow_, kTrailingWrapper}));
+ base::StrCat({kLeadingWrapper, *js_flow_, kTrailingWrapper,
+ js_flow_util::GetDevtoolsSourceUrlCommentToAppend(
+ UnexpectedErrorInfoProto::JS_FLOW)}));
// Run the wrapped js_flow in the sandbox and serve potential native action
// requests as they arrive.
@@ -205,7 +175,7 @@ void JsFlowExecutorImpl::InternalStart() {
.SetReturnByValue(true)
.SetContextId(isolated_world_context_id_)
.Build(),
- kMainFrame,
+ js_flow_util::kMainFrame,
base::BindOnce(&JsFlowExecutorImpl::OnFlowFinished,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -217,7 +187,7 @@ void JsFlowExecutorImpl::RefreshNativeActionPromise() {
.SetAwaitPromise(true)
.SetContextId(isolated_world_context_id_)
.Build(),
- kMainFrame,
+ js_flow_util::kMainFrame,
base::BindOnce(&JsFlowExecutorImpl::OnNativeActionRequested,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -241,7 +211,7 @@ void JsFlowExecutorImpl::OnNativeActionRequested(
.SetFunctionDeclaration(kArrayGetNthElement)
.SetReturnByValue(true)
.Build(),
- kMainFrame,
+ js_flow_util::kMainFrame,
base::BindOnce(&JsFlowExecutorImpl::OnNativeActionRequestActionRetrieved,
weak_ptr_factory_.GetWeakPtr(), js_array_object_id));
}
@@ -270,7 +240,7 @@ void JsFlowExecutorImpl::OnNativeActionRequestActionRetrieved(
.SetArguments(std::move(arguments))
.SetFunctionDeclaration(kArrayGetNthElement)
.Build(),
- kMainFrame,
+ js_flow_util::kMainFrame,
base::BindOnce(
&JsFlowExecutorImpl::OnNativeActionRequestFulfillPromiseRetrieved,
weak_ptr_factory_.GetWeakPtr(),
@@ -352,7 +322,7 @@ void JsFlowExecutorImpl::OnNativeActionFinished(
.SetArguments(std::move(arguments))
.SetFunctionDeclaration(kFulfillActionPromise)
.Build(),
- kMainFrame,
+ js_flow_util::kMainFrame,
base::BindOnce(&JsFlowExecutorImpl::OnFlowResumed,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -375,7 +345,10 @@ void JsFlowExecutorImpl::OnFlowFinished(
// values are allowed (see js_flow_util::ExtractFlowReturnValue for details).
std::unique_ptr<base::Value> out_result_value;
ClientStatus status = js_flow_util::ExtractFlowReturnValue(
- reply_status, result.get(), out_result_value, kJsLineOffset,
+ reply_status, result.get(), out_result_value,
+ /* js_line_offsets= */
+ {{js_flow_util::GetDevtoolsSourceUrl(UnexpectedErrorInfoProto::JS_FLOW),
+ kJsLineOffset}},
kNumStackEntriesToDrop);
RunCallback(status, std::move(out_result_value));
@@ -387,7 +360,10 @@ void JsFlowExecutorImpl::RunCallback(
if (!status.ok() && result_value) {
VLOG(1) << "Flow failed with " << status
<< " and result: " << *result_value;
+ } else if (!status.ok()) {
+ VLOG(1) << "Flow failed with " << status;
}
+
std::move(callback_).Run(status, std::move(result_value));
}
diff --git a/chromium/components/autofill_assistant/browser/js_flow_executor_impl.h b/chromium/components/autofill_assistant/browser/js_flow_executor_impl.h
index 4bbd621307c..91cbb1cf1ea 100644
--- a/chromium/components/autofill_assistant/browser/js_flow_executor_impl.h
+++ b/chromium/components/autofill_assistant/browser/js_flow_executor_impl.h
@@ -8,10 +8,12 @@
#include <memory>
#include <string>
#include "base/callback_forward.h"
+#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/devtools/devtools_client.h"
+#include "components/autofill_assistant/browser/js_flow_devtools_wrapper.h"
#include "components/autofill_assistant/browser/js_flow_executor.h"
namespace autofill_assistant {
@@ -20,8 +22,9 @@ namespace autofill_assistant {
// native actions to be performed by its delegate.
class JsFlowExecutorImpl : public JsFlowExecutor {
public:
- // |delegate| must outlive the JsFlowExecutorImpl.
- JsFlowExecutorImpl(content::WebContents* web_contents, Delegate* delegate);
+ // |delegate| and |devtools_wrapper| must outlive the JsFlowExecutorImpl.
+ JsFlowExecutorImpl(Delegate* delegate,
+ JsFlowDevtoolsWrapper* js_flow_devtools_wrapper);
~JsFlowExecutorImpl() override;
JsFlowExecutorImpl(const JsFlowExecutorImpl&) = delete;
JsFlowExecutorImpl& operator=(const JsFlowExecutorImpl&) = delete;
@@ -74,12 +77,10 @@ class JsFlowExecutorImpl : public JsFlowExecutor {
result_callback) override;
private:
- void InternalStart();
- void OnGetFrameTree(const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<page::GetFrameTreeResult> result);
- void IsolatedWorldCreated(
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<page::CreateIsolatedWorldResult> result);
+ void InternalStart(const ClientStatus& status,
+ DevtoolsClient* devtools_client,
+ const int isolated_world_context_id);
+
void RefreshNativeActionPromise();
void OnNativeActionRequested(const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::EvaluateResult> result);
@@ -118,11 +119,12 @@ class JsFlowExecutorImpl : public JsFlowExecutor {
return true;
}
- Delegate* const delegate_;
- std::unique_ptr<DevtoolsClient> devtools_client_;
- int isolated_world_context_id_ = -1;
+ const raw_ptr<Delegate> delegate_;
+ JsFlowDevtoolsWrapper* js_flow_devtools_wrapper_;
// Only set during a flow.
+ DevtoolsClient* devtools_client_;
+ int isolated_world_context_id_ = -1;
std::unique_ptr<std::string> js_flow_;
base::OnceCallback<void(const ClientStatus&, std::unique_ptr<base::Value>)>
callback_;
diff --git a/chromium/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc b/chromium/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc
index 7f97d15eff4..8bea9e9795a 100644
--- a/chromium/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc
+++ b/chromium/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc
@@ -9,7 +9,6 @@
#include <string>
#include <type_traits>
-#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_forward.h"
@@ -38,7 +37,6 @@
#include "net/http/http_status_code.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
namespace autofill_assistant {
namespace {
@@ -73,13 +71,15 @@ class MockJsFlowExecutorImplDelegate : public JsFlowExecutorImpl::Delegate {
(override));
};
-class JsFlowExecutorImplTest : public BaseBrowserTest {
+class JsFlowExecutorImplBrowserTest : public BaseBrowserTest {
public:
void SetUpOnMainThread() override {
BaseBrowserTest::SetUpOnMainThread();
+ js_flow_devtools_wrapper_ =
+ std::make_unique<JsFlowDevtoolsWrapper>(shell()->web_contents());
flow_executor_ = std::make_unique<JsFlowExecutorImpl>(
- shell()->web_contents(), &mock_delegate_);
+ &mock_delegate_, js_flow_devtools_wrapper_.get());
}
// Overload, ignore result value, just return the client status.
@@ -92,8 +92,9 @@ class JsFlowExecutorImplTest : public BaseBrowserTest {
std::unique_ptr<base::Value>& result_value) {
ClientStatus status;
base::RunLoop run_loop;
+
flow_executor_->Start(
- js_flow, base::BindOnce(&JsFlowExecutorImplTest::OnFlowFinished,
+ js_flow, base::BindOnce(&JsFlowExecutorImplBrowserTest::OnFlowFinished,
base::Unretained(this), run_loop.QuitClosure(),
&status, std::ref(result_value)));
run_loop.Run();
@@ -112,20 +113,23 @@ class JsFlowExecutorImplTest : public BaseBrowserTest {
protected:
NiceMock<MockJsFlowExecutorImplDelegate> mock_delegate_;
+
std::unique_ptr<JsFlowExecutorImpl> flow_executor_;
+ std::unique_ptr<JsFlowDevtoolsWrapper> js_flow_devtools_wrapper_;
};
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, SmokeTest) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest, SmokeTest) {
EXPECT_THAT(RunTest(std::string()),
Property(&ClientStatus::proto_status, ACTION_APPLIED));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, InvalidJs) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest, InvalidJs) {
EXPECT_THAT(RunTest("Not valid Javascript"),
Property(&ClientStatus::proto_status, UNEXPECTED_JS_ERROR));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunNativeActionWithReturnValue) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
+ RunNativeActionWithReturnValue) {
std::unique_ptr<base::Value> native_return_value =
std::make_unique<base::Value>(std::move(*base::JSONReader::Read(
R"(
@@ -185,7 +189,8 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunNativeActionWithReturnValue) {
)"));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunNativeActionAsBase64String) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
+ RunNativeActionAsBase64String) {
EXPECT_CALL(mock_delegate_, RunNativeAction)
.WillOnce([&](int action_id, const std::string& action, auto callback) {
EXPECT_EQ(12, action_id);
@@ -194,7 +199,7 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunNativeActionAsBase64String) {
});
std::unique_ptr<base::Value> result;
- EXPECT_THAT(RunTest(R"(
+ ASSERT_THAT(RunTest(R"(
let [status, value] = await runNativeAction(12, "dGVzdA==" /*test*/);
return status;
)",
@@ -204,7 +209,7 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunNativeActionAsBase64String) {
EXPECT_EQ(*result, base::Value(2));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
RunNativeActionAsSerializedProto) {
EXPECT_CALL(mock_delegate_, RunNativeAction)
.WillOnce([&](int action_id, const std::string& action, auto callback) {
@@ -216,7 +221,7 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
});
std::unique_ptr<base::Value> result;
- EXPECT_THAT(RunTest(R"(
+ ASSERT_THAT(RunTest(R"(
let [status, value] = await runNativeAction(
11, ["aa.msg", "my message"]);
return status;
@@ -226,7 +231,8 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
EXPECT_EQ(*result, base::Value(2));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunMultipleNativeActions) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
+ RunMultipleNativeActions) {
EXPECT_CALL(mock_delegate_, RunNativeAction)
.WillOnce([&](int action_id, const std::string& action, auto callback) {
EXPECT_EQ(1, action_id);
@@ -243,7 +249,7 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunMultipleNativeActions) {
// completed successfully, but the return value should hold
// OTHER_ACTION_STATUS, i.e., 3.
std::unique_ptr<base::Value> result;
- EXPECT_THAT(RunTest(R"(
+ ASSERT_THAT(RunTest(R"(
let [status, value] = await runNativeAction(
1, "dGVzdDE=" /*test1*/);
if (status == 2) { // ACTION_APPLIED
@@ -258,14 +264,14 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunMultipleNativeActions) {
EXPECT_EQ(*result, base::Value(3));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnInteger) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest, ReturnInteger) {
std::unique_ptr<base::Value> result;
ClientStatus status = RunTest("return 12345;", result);
- EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+ ASSERT_EQ(status.proto_status(), ACTION_APPLIED);
EXPECT_EQ(*result, base::Value(12345));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturningStringFails) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest, ReturningStringFails) {
// Return value checking is more comprehensively tested in
// js_flow_util::ContainsOnlyAllowedValues. This test is just to ensure that
// that util is actually used for JS flow return values.
@@ -275,7 +281,7 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturningStringFails) {
EXPECT_THAT(result, Eq(nullptr));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnDictionary) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest, ReturnDictionary) {
std::unique_ptr<base::Value> result;
ClientStatus status = RunTest(
R"(
@@ -290,7 +296,7 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnDictionary) {
};
)",
result);
- EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+ ASSERT_EQ(status.proto_status(), ACTION_APPLIED);
EXPECT_EQ(*result, *base::JSONReader::Read(R"(
{
"keyA":12345,
@@ -304,14 +310,15 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnDictionary) {
)"));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnNothing) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest, ReturnNothing) {
std::unique_ptr<base::Value> result;
ClientStatus status = RunTest("", result);
EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
EXPECT_THAT(result, Eq(nullptr));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnNonJsonObjectFails) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
+ ReturnNonJsonObjectFails) {
std::unique_ptr<base::Value> result;
ClientStatus status = RunTest(R"(
function test() {
@@ -324,14 +331,14 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnNonJsonObjectFails) {
EXPECT_THAT(result, Eq(nullptr));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnNull) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest, ReturnNull) {
std::unique_ptr<base::Value> result;
ClientStatus status = RunTest("return null;", result);
- EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+ ASSERT_EQ(status.proto_status(), ACTION_APPLIED);
EXPECT_EQ(*result, *base::JSONReader::Read("null"));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ExceptionReporting) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest, ExceptionReporting) {
std::unique_ptr<base::Value> result;
ClientStatus status = RunTest("notdefined;", result);
EXPECT_EQ(status.proto_status(), UNEXPECTED_JS_ERROR);
@@ -347,17 +354,18 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ExceptionReporting) {
ElementsAre(0));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunMultipleConsecutiveFlows) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
+ RunMultipleConsecutiveFlows) {
for (int i = 0; i < 10; ++i) {
std::unique_ptr<base::Value> result;
ClientStatus status =
RunTest(base::StrCat({"return ", base::NumberToString(i)}), result);
- EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+ ASSERT_EQ(status.proto_status(), ACTION_APPLIED);
EXPECT_EQ(*result, base::Value(i));
}
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
UnserializableRunNativeActionString) {
std::unique_ptr<base::Value> result;
EXPECT_CALL(mock_delegate_, RunNativeAction).Times(0);
@@ -372,7 +380,7 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
EXPECT_EQ(status.proto_status(), INVALID_ACTION);
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
UnserializableRunNativeActionId) {
std::unique_ptr<base::Value> result;
EXPECT_CALL(mock_delegate_, RunNativeAction).Times(0);
@@ -387,7 +395,8 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
EXPECT_EQ(status.proto_status(), INVALID_ACTION);
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, StartWhileAlreadyRunningFails) {
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
+ StartWhileAlreadyRunningFails) {
EXPECT_CALL(mock_delegate_, RunNativeAction)
.WillOnce(WithArg<2>([&](auto callback) {
// Starting a second flow while the first one is running should fail.
@@ -404,123 +413,31 @@ IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, StartWhileAlreadyRunningFails) {
return status;
)",
result);
- EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+ ASSERT_EQ(status.proto_status(), ACTION_APPLIED);
EXPECT_EQ(*result, base::Value(2));
}
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
EnvironmentIsPreservedBetweenRuns) {
EXPECT_EQ(RunTest("globalFlowState.i = 5;").proto_status(), ACTION_APPLIED);
std::unique_ptr<base::Value> result;
- EXPECT_EQ(RunTest("return globalFlowState.i;", result).proto_status(),
+ ASSERT_EQ(RunTest("return globalFlowState.i;", result).proto_status(),
ACTION_APPLIED);
EXPECT_EQ(*result, base::Value(5));
}
-class JsFlowExecutorImplScriptExecutorTest : public BaseBrowserTest {
- public:
- void SetUpOnMainThread() override {
- BaseBrowserTest::SetUpOnMainThread();
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplBrowserTest,
+ JsFlowLibraryIsAvailable) {
+ js_flow_devtools_wrapper_->SetJsFlowLibrary("const status = 2;");
- web_controller_ = WebController::CreateForWebContents(
- shell()->web_contents(), &user_data_, &log_info_, nullptr,
- /*enable_full_stack_traces= */ true);
-
- fake_script_executor_delegate_.SetService(&mock_service_);
- fake_script_executor_delegate_.SetWebController(web_controller_.get());
- fake_script_executor_delegate_.SetCurrentURL(GURL("http://example.com/"));
- fake_script_executor_delegate_.SetWebContents(shell()->web_contents());
-
- script_executor_ = std::make_unique<ScriptExecutor>(
- /* script_path= */ "",
- /* additional_context= */ std::make_unique<TriggerContext>(),
- /* global_payload= */ "",
- /* script_payload= */ "",
- /* listener= */ nullptr, &ordered_interrupts_,
- &fake_script_executor_delegate_, &fake_script_executor_ui_delegate_);
- }
-
- protected:
- void Run(const std::string& js_flow,
- const ProcessedActionStatusProto& result) {
- ActionsResponseProto actions_response;
- actions_response.add_actions()->mutable_js_flow()->set_js_flow(js_flow);
- /* actions_response.add_actions() */
- /* ->mutable_release_elements() */
- /* ->add_client_ids() */
- /* ->set_identifier("client_id"); */
-
- EXPECT_CALL(mock_service_, GetActions)
- .WillOnce(RunOnceCallback<5>(net::HTTP_OK,
- actions_response.SerializeAsString(),
- ServiceRequestSender::ResponseInfo{}));
-
- EXPECT_CALL(mock_service_,
- GetNextActions(_, _, _,
- ElementsAre(Property(
- &ProcessedActionProto::status, result)),
- _, _, _))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
- ActionsResponseProto().SerializeAsString(),
- ServiceRequestSender::ResponseInfo{}));
-
- base::RunLoop run_loop;
- script_executor_->Run(
- &user_data_,
- base::BindOnce(&JsFlowExecutorImplScriptExecutorTest::OnFlowFinished,
- base::Unretained(this), run_loop.QuitClosure()));
- run_loop.Run();
- }
-
- void OnFlowFinished(base::OnceClosure done_callback,
- const ScriptExecutor::Result& result) {
- EXPECT_TRUE(result.success);
- std::move(done_callback).Run();
- }
-
- std::vector<std::unique_ptr<Script>> ordered_interrupts_;
-
- ProcessedActionStatusDetailsProto log_info_;
- std::unique_ptr<WebController> web_controller_;
-
- FakeScriptExecutorDelegate fake_script_executor_delegate_;
- FakeScriptExecutorUiDelegate fake_script_executor_ui_delegate_;
- UserData user_data_;
-
- NiceMock<MockService> mock_service_;
- std::unique_ptr<ScriptExecutor> script_executor_;
-};
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplScriptExecutorTest,
- WaitForDomSucceeds) {
- WaitForDomProto wait_for_dom;
- wait_for_dom.mutable_wait_condition()
- ->mutable_match()
- ->add_filters()
- ->set_css_selector("#button");
- std::string wait_for_dom_base64;
- base::Base64Encode(wait_for_dom.SerializeAsString(), &wait_for_dom_base64);
-
- Run(R"(const [status, value] = await runNativeAction(19, ')" +
- wait_for_dom_base64 + R"(');
- return {status};)",
- ACTION_APPLIED);
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplScriptExecutorTest, WaitForDomFails) {
- WaitForDomProto wait_for_dom;
- wait_for_dom.mutable_wait_condition()
- ->mutable_match()
- ->add_filters()
- ->set_css_selector("#not-found");
- std::string wait_for_dom_base64;
- base::Base64Encode(wait_for_dom.SerializeAsString(), &wait_for_dom_base64);
-
- Run(R"(const [status, value] = await runNativeAction(19, ')" +
- wait_for_dom_base64 + R"(');
- return {status};)",
- ELEMENT_RESOLUTION_FAILED);
+ std::unique_ptr<base::Value> result;
+ ASSERT_THAT(RunTest(R"(
+ return status;
+ )",
+ result),
+ Property(&ClientStatus::proto_status, ACTION_APPLIED));
+ EXPECT_EQ(*result, base::Value(2));
}
} // namespace
diff --git a/chromium/components/autofill_assistant/browser/js_flow_util.cc b/chromium/components/autofill_assistant/browser/js_flow_util.cc
index ee0a5ecc6b2..c0ae4b70f4b 100644
--- a/chromium/components/autofill_assistant/browser/js_flow_util.cc
+++ b/chromium/components/autofill_assistant/browser/js_flow_util.cc
@@ -4,6 +4,7 @@
#include "components/autofill_assistant/browser/js_flow_util.h"
#include "base/base64.h"
+#include "base/logging.h"
#include "base/strings/strcat.h"
#include "components/autofill_assistant/browser/model.pb.h"
#include "components/autofill_assistant/browser/service.pb.h"
@@ -28,6 +29,13 @@ const char kActionSpecificResultKey[] = "actionSpecificResult";
// by runNativeAction().
// DO NOT CHANGE
const char kNavigationStartedKey[] = "navigationStarted";
+// The key for the autofill error info in the result object returned by
+// runNativeAction().
+// DO NOT CHANGE
+const char kAutofillErrorInfo[] = "autofillErrorInfo";
+// By appending //# sourceUrl=some_name.js to a js snippet the snippet can be
+// identified in devtools by url = some_name.js (for example in exceptions).
+constexpr char kSourceUrlCommentPrefix[] = "\n//# sourceURL=";
// Returns true for remote object types that flows are allowed to return. This
// is mostly used to filter types like FUNCTION which would otherwise slip
@@ -95,11 +103,11 @@ ClientStatus ExtractFlowReturnValue(
const DevtoolsClient::ReplyStatus& devtools_reply_status,
runtime::EvaluateResult* devtools_result,
std::unique_ptr<base::Value>& out_flow_result,
- int js_line_offset,
+ const JsLineOffsets& js_line_offsets,
int num_stack_entries_to_drop) {
ClientStatus status = CheckJavaScriptResult(
devtools_reply_status, devtools_result, __FILE__, __LINE__,
- js_line_offset, num_stack_entries_to_drop);
+ js_line_offsets, num_stack_entries_to_drop);
if (!status.ok()) {
return status;
}
@@ -146,6 +154,7 @@ ClientStatus ExtractJsFlowActionReturnValue(
}
if (!value.is_dict()) {
+ VLOG(1) << "JS flow did not return a dictionary.";
return ClientStatusWithSourceLocation(INVALID_ACTION, __FILE__, __LINE__);
}
@@ -153,6 +162,7 @@ ClientStatus ExtractJsFlowActionReturnValue(
absl::optional<int> flow_status = dict->FindInt(kStatusKey);
const base::Value* flow_return_value = dict->Find(kResultKey);
if (!flow_status || !ProcessedActionStatusProto_IsValid(*flow_status)) {
+ VLOG(1) << "JS flow did not return a valid ActionStatus in " << kStatusKey;
return ClientStatusWithSourceLocation(INVALID_ACTION, __FILE__, __LINE__);
}
@@ -163,6 +173,12 @@ ClientStatus ExtractJsFlowActionReturnValue(
return ClientStatus(static_cast<ProcessedActionStatusProto>(*flow_status));
}
+std::string SerializeToBase64(const google::protobuf::MessageLite* proto) {
+ std::string serialized_result_base64;
+ base::Base64Encode(proto->SerializeAsString(), &serialized_result_base64);
+ return serialized_result_base64;
+}
+
namespace {
absl::optional<std::string> SerializeActionResult(
@@ -205,13 +221,14 @@ absl::optional<std::string> SerializeActionResult(
case ProcessedActionProto::kSaveSubmittedPasswordResult:
proto = &processed_action.save_submitted_password_result();
break;
+ case ProcessedActionProto::kExternalActionResult:
+ proto = &processed_action.external_action_result();
+ break;
case ProcessedActionProto::RESULT_DATA_NOT_SET:
return absl::nullopt;
}
- std::string serialized_result_base64;
- base::Base64Encode(proto->SerializeAsString(), &serialized_result_base64);
- return serialized_result_base64;
+ return SerializeToBase64(proto);
}
} // namespace
@@ -228,8 +245,40 @@ std::unique_ptr<base::Value> NativeActionResultToResultValue(
result_value.Set(kActionSpecificResultKey, *serialized_result);
}
+ if (processed_action.status_details().has_autofill_error_info()) {
+ result_value.Set(
+ kAutofillErrorInfo,
+ SerializeToBase64(
+ &processed_action.status_details().autofill_error_info()));
+ }
+
return std::make_unique<base::Value>(std::move(result_value));
}
+std::string GetDevtoolsSourceUrl(
+ UnexpectedErrorInfoProto::JsExceptionLocation js_exception_location) {
+ return UnexpectedErrorInfoProto::JsExceptionLocation_Name(
+ js_exception_location);
+}
+
+UnexpectedErrorInfoProto::JsExceptionLocation GetExceptionLocation(
+ const std::string& devtools_source_url) {
+ UnexpectedErrorInfoProto::JsExceptionLocation js_exception_location;
+ return UnexpectedErrorInfoProto::JsExceptionLocation_Parse(
+ devtools_source_url, &js_exception_location)
+ ? js_exception_location
+ : UnexpectedErrorInfoProto::UNKNOWN;
+}
+
+std::string GetDevtoolsSourceUrlCommentToAppend(
+ UnexpectedErrorInfoProto::JsExceptionLocation js_exception_location) {
+ if (js_exception_location == UnexpectedErrorInfoProto::UNKNOWN) {
+ return "";
+ }
+
+ return base::StrCat(
+ {kSourceUrlCommentPrefix, GetDevtoolsSourceUrl(js_exception_location)});
+}
+
} // namespace js_flow_util
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/js_flow_util.h b/chromium/components/autofill_assistant/browser/js_flow_util.h
index 6f4ff790e60..0ee6e8e35e4 100644
--- a/chromium/components/autofill_assistant/browser/js_flow_util.h
+++ b/chromium/components/autofill_assistant/browser/js_flow_util.h
@@ -11,9 +11,11 @@
#include "base/values.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/devtools/devtools_client.h"
+#include "components/autofill_assistant/browser/web/web_controller_util.h"
-namespace autofill_assistant {
-namespace js_flow_util {
+namespace autofill_assistant::js_flow_util {
+
+constexpr char kMainFrame[] = "";
// Returns true if |value| contains only allowed value types, which are INT,
// BOOL, DOUBLE, and NONE. Dictionaries and lists are allowed, so long as they
@@ -38,7 +40,7 @@ ClientStatus ExtractFlowReturnValue(
const DevtoolsClient::ReplyStatus& devtools_reply_status,
runtime::EvaluateResult* devtools_result,
std::unique_ptr<base::Value>& out_flow_result,
- int js_line_offset,
+ const JsLineOffsets& js_line_offsets,
int num_stack_entries_to_drop);
// Extracts client status and optionally return value from |value|. Expects
@@ -67,7 +69,26 @@ ClientStatus ExtractJsFlowActionReturnValue(
std::unique_ptr<base::Value> NativeActionResultToResultValue(
const ProcessedActionProto& processed_action);
-} // namespace js_flow_util
-} // namespace autofill_assistant
+// Serializes the proto as base64.
+std::string SerializeToBase64(const google::protobuf::MessageLite* proto);
+
+// Returns the devtools source url comment to append to js code before
+// evaluating by devtools.
+//
+// For example by appending //# sourceUrl=some_name.js to a js snippet the
+// snippet can be identified in devtools by url = some_name.js (for example in
+// exceptions).
+std::string GetDevtoolsSourceUrlCommentToAppend(
+ UnexpectedErrorInfoProto::JsExceptionLocation js_exception_location);
+
+// Returns the devtools source url for the js exception location.
+std::string GetDevtoolsSourceUrl(
+ UnexpectedErrorInfoProto::JsExceptionLocation js_exception_location);
+
+// Returns the js exception location for the devtools source url.
+UnexpectedErrorInfoProto::JsExceptionLocation GetExceptionLocation(
+ const std::string& devtools_source_url);
+
+} // namespace autofill_assistant::js_flow_util
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_JS_FLOW_UTIL_H_
diff --git a/chromium/components/autofill_assistant/browser/js_flow_util_unittest.cc b/chromium/components/autofill_assistant/browser/js_flow_util_unittest.cc
index 2ab42294065..42f2787e445 100644
--- a/chromium/components/autofill_assistant/browser/js_flow_util_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/js_flow_util_unittest.cc
@@ -145,7 +145,7 @@ TEST(JsFlowUtilTest, ExtractFlowReturnValue) {
std::unique_ptr<base::Value> out_flow_value;
ClientStatus status = ExtractFlowReturnValue(
devtools_status, devtools_result.get(), out_flow_value,
- /* js_line_offset= */ 0, /* num_stack_entries_to_drop= */ 0);
+ /* js_line_offsets= */ {}, /* num_stack_entries_to_drop= */ 0);
EXPECT_TRUE(status.ok());
EXPECT_EQ(*out_flow_value, base::Value(12345));
}
@@ -250,6 +250,23 @@ TEST(JsFlowUtilTest, NativeActionResultToResultValueHasSerializedActionResult) {
"actionSpecificResult": ")" + wait_for_dom_result_base64 + "\"}")));
}
+TEST(JsFlowUtilTest, NativeActionResultToResultValueHasAutofillErrorInfo) {
+ ProcessedActionProto processed_action;
+ AutofillErrorInfoProto* autofill_error_info =
+ processed_action.mutable_status_details()->mutable_autofill_error_info();
+ autofill_error_info->set_client_memory_address_key_names("key_names");
+
+ std::string autofill_error_info_base64;
+ base::Base64Encode(autofill_error_info->SerializeAsString(),
+ &autofill_error_info_base64);
+
+ EXPECT_THAT(
+ NativeActionResultToResultValue(processed_action), Pointee(IsJson(R"(
+ {
+ "navigationStarted": false,
+ "autofillErrorInfo": ")" + autofill_error_info_base64 + "\"}")));
+}
+
TEST(JsFlowUtilTest, NativeActionResultToResultValueHasEmptyActionResult) {
ProcessedActionProto processed_action;
@@ -259,6 +276,23 @@ TEST(JsFlowUtilTest, NativeActionResultToResultValueHasEmptyActionResult) {
)")));
}
+TEST(JsFlowUtilTest, ExceptionLocationToDevtoolsUrlMapping) {
+ const std::string url =
+ GetDevtoolsSourceUrl(UnexpectedErrorInfoProto::JS_FLOW);
+ EXPECT_THAT(GetExceptionLocation(url), UnexpectedErrorInfoProto::JS_FLOW);
+}
+
+TEST(JsFlowUtilTest, UnknownUrl) {
+ EXPECT_THAT(GetExceptionLocation("SOME_STRING"),
+ UnexpectedErrorInfoProto::UNKNOWN);
+}
+
+TEST(JsFlowUtilTest, GetDevtoolsSourceUrlCommentToAppend) {
+ EXPECT_THAT(GetDevtoolsSourceUrlCommentToAppend(
+ UnexpectedErrorInfoProto::JS_FLOW_LIBRARY),
+ "\n//# sourceURL=JS_FLOW_LIBRARY");
+}
+
} // namespace
} // namespace js_flow_util
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/metrics.cc b/chromium/components/autofill_assistant/browser/metrics.cc
index e2c0b673b2a..1a5fb172703 100644
--- a/chromium/components/autofill_assistant/browser/metrics.cc
+++ b/chromium/components/autofill_assistant/browser/metrics.cc
@@ -11,7 +11,6 @@
#include "components/autofill_assistant/browser/features.h"
#include "components/autofill_assistant/browser/intent_strings.h"
#include "components/autofill_assistant/browser/startup_util.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
namespace autofill_assistant {
@@ -48,6 +47,10 @@ const char kDependenciesInvalidated[] =
"Android.AutofillAssistant.DependenciesInvalidated";
const char kOnboardingFetcherResultStatus[] =
"Android.AutofillAssistant.OnboardingFetcher.ResultStatus";
+const char kServiceRequestSuccessRetryCount[] =
+ "Android.AutofillAssistant.ServiceRequestSender.SuccessRetryCount";
+const char kServiceRequestFailureRetryCount[] =
+ "Android.AutofillAssistant.ServiceRequestSender.FailureRetryCount";
static bool DROPOUT_RECORDED = false;
std::string GetSuffixForIntent(const std::string& intent) {
@@ -110,7 +113,7 @@ Metrics::AutofillAssistantIntent ExtractIntentFromScriptParameters(
return Metrics::AutofillAssistantIntent::UNDEFINED_INTENT;
}
return enum_value_iter->second;
-} // namespace
+}
// Extracts the enum value corresponding to the caller specified in
// |script_parameters|.
@@ -562,6 +565,15 @@ void Metrics::RecordOnboardingFetcherResult(
base::UmaHistogramEnumeration(kOnboardingFetcherResultStatus, status);
}
+// static
+void Metrics::RecordServiceRequestRetryCount(int count, bool success) {
+ DCHECK_GE(count, 0);
+ base::UmaHistogramExactLinear(success ? kServiceRequestSuccessRetryCount
+ : kServiceRequestFailureRetryCount,
+ /* sample= */ count,
+ /* exclusive_max= */ 11);
+}
+
std::ostream& operator<<(std::ostream& out,
const Metrics::DropOutReason& reason) {
#ifdef NDEBUG
diff --git a/chromium/components/autofill_assistant/browser/metrics.h b/chromium/components/autofill_assistant/browser/metrics.h
index 768236c2edf..994f0931bba 100644
--- a/chromium/components/autofill_assistant/browser/metrics.h
+++ b/chromium/components/autofill_assistant/browser/metrics.h
@@ -469,10 +469,16 @@ class Metrics {
SEARCH_ADS = 4,
SHOPPING_PROPERTY = 5,
EMULATOR = 6,
+ // The run was started from within Chrome (e.g., URL heuristic match or
+ // password change launched from the settings page).
IN_CHROME = 7,
+ // The run was triggered by the Direction Action API in Chrome.
DIRECT_ACTION = 8,
+ // The run was started by Google Password Manager (passwords.google.com or
+ // credential_manager in gmscore module on Android).
+ GOOGLE_PASSWORD_MANAGER = 9,
- kMaxValue = DIRECT_ACTION
+ kMaxValue = GOOGLE_PASSWORD_MANAGER
};
// Used for logging the SOURCE script parameter.
@@ -787,6 +793,7 @@ class Metrics {
static void RecordOnboardingFetcherResult(
OnboardingFetcherResultStatus status);
static void RecordCupRpcVerificationEvent(CupRpcVerificationEvent event);
+ static void RecordServiceRequestRetryCount(int count, bool success);
// Intended for debugging: writes string representation of |reason| to
// |out|.
diff --git a/chromium/components/autofill_assistant/browser/mock_client.h b/chromium/components/autofill_assistant/browser/mock_client.h
index c0cf456f64e..04c316d0259 100644
--- a/chromium/components/autofill_assistant/browser/mock_client.h
+++ b/chromium/components/autofill_assistant/browser/mock_client.h
@@ -38,7 +38,7 @@ class MockClient : public Client {
MOCK_CONST_METHOD0(IsAccessibilityEnabled, bool());
MOCK_CONST_METHOD0(IsSpokenFeedbackAccessibilityServiceEnabled, bool());
MOCK_CONST_METHOD0(GetEmailAddressForAccessTokenAccount, std::string());
- MOCK_CONST_METHOD0(GetChromeSignedInEmailAddress, std::string());
+ MOCK_CONST_METHOD0(GetSignedInEmail, std::string());
MOCK_CONST_METHOD0(GetWebContents, content::WebContents*());
MOCK_CONST_METHOD0(GetPersonalDataManager, autofill::PersonalDataManager*());
MOCK_CONST_METHOD0(GetWebsiteLoginManager, WebsiteLoginManager*());
@@ -55,6 +55,9 @@ class MockClient : public Client {
MOCK_METHOD1(FetchPaymentsClientToken,
void(base::OnceCallback<void(const std::string&)>));
MOCK_METHOD0(GetScriptExecutorUiDelegate, ScriptExecutorUiDelegate*());
+ MOCK_CONST_METHOD0(MustUseBackendData, bool());
+ MOCK_CONST_METHOD1(GetAnnotateDomModelVersion,
+ void(base::OnceCallback<void(absl::optional<int64_t>)>));
private:
std::unique_ptr<MockPersonalDataManager> mock_personal_data_manager_;
diff --git a/chromium/components/autofill_assistant/browser/mock_client_context.h b/chromium/components/autofill_assistant/browser/mock_client_context.h
index cc9a593c55b..43debe39aa6 100644
--- a/chromium/components/autofill_assistant/browser/mock_client_context.h
+++ b/chromium/components/autofill_assistant/browser/mock_client_context.h
@@ -18,8 +18,19 @@ class MockClientContext : public ClientContext {
MockClientContext();
~MockClientContext() override;
- MOCK_METHOD1(Update, void(const TriggerContext& trigger_context));
- MOCK_CONST_METHOD0(AsProto, ClientContextProto());
+ MOCK_METHOD(void,
+ Update,
+ (const TriggerContext& trigger_context),
+ (override));
+ MOCK_METHOD(void,
+ UpdateAnnotateDomModelContext,
+ (int64_t model_version),
+ (override));
+ MOCK_METHOD(void,
+ UpdateJsFlowLibraryLoaded,
+ (bool js_flow_library_loaded),
+ (override));
+ MOCK_METHOD(ClientContextProto, AsProto, (), (const override));
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/mock_common_dependencies.cc b/chromium/components/autofill_assistant/browser/mock_common_dependencies.cc
new file mode 100644
index 00000000000..2ac0434bfa8
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/mock_common_dependencies.cc
@@ -0,0 +1,13 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/mock_common_dependencies.h"
+
+namespace autofill_assistant {
+
+MockCommonDependencies::MockCommonDependencies() = default;
+
+MockCommonDependencies::~MockCommonDependencies() = default;
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/mock_common_dependencies.h b/chromium/components/autofill_assistant/browser/mock_common_dependencies.h
new file mode 100644
index 00000000000..a500b44942b
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/mock_common_dependencies.h
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_COMMON_DEPENDENCIES_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_COMMON_DEPENDENCIES_H_
+
+#include "components/autofill_assistant/browser/assistant_field_trial_util.h"
+#include "components/autofill_assistant/browser/common_dependencies.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+class MockCommonDependencies : public CommonDependencies {
+ public:
+ MockCommonDependencies();
+ ~MockCommonDependencies() override;
+
+ MOCK_METHOD(std::unique_ptr<AssistantFieldTrialUtil>,
+ CreateFieldTrialUtil,
+ (),
+ (const override));
+ MOCK_METHOD(std::string, GetLocale, (), (const override));
+ MOCK_METHOD(std::string, GetCountryCode, (), (const override));
+ MOCK_METHOD(autofill::PersonalDataManager*,
+ GetPersonalDataManager,
+ (content::BrowserContext*),
+ (const override));
+ MOCK_METHOD(password_manager::PasswordManagerClient*,
+ GetPasswordManagerClient,
+ (content::WebContents*),
+ (const override));
+ MOCK_METHOD(std::string,
+ GetSignedInEmail,
+ (content::BrowserContext*),
+ (const override));
+ MOCK_METHOD(bool,
+ IsSupervisedUser,
+ (content::BrowserContext*),
+ (const override));
+ MOCK_METHOD(AnnotateDomModelService*,
+ GetOrCreateAnnotateDomModelService,
+ (content::BrowserContext*),
+ (const override));
+ MOCK_METHOD(bool, IsWebLayer, (), (const override));
+ MOCK_METHOD(signin::IdentityManager*,
+ GetIdentityManager,
+ (content::BrowserContext*),
+ (const override));
+ MOCK_METHOD(version_info::Channel, GetChannel, (), (const override));
+};
+
+} // namespace autofill_assistant
+
+#endif // #COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_COMMON_DEPENDENCIES_H_
diff --git a/chromium/components/autofill_assistant/browser/mock_controller_observer.h b/chromium/components/autofill_assistant/browser/mock_controller_observer.h
index f67bd170fc0..3327e695f33 100644
--- a/chromium/components/autofill_assistant/browser/mock_controller_observer.h
+++ b/chromium/components/autofill_assistant/browser/mock_controller_observer.h
@@ -46,7 +46,6 @@ class MockControllerObserver : public ControllerObserver {
MOCK_METHOD0(OnStop, void());
MOCK_METHOD0(OnResetState, void());
MOCK_METHOD1(OnUiShownChanged, void(bool shown));
- MOCK_METHOD1(OnShutdown, void(Metrics::DropOutReason reason));
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/mock_script_executor_delegate.cc b/chromium/components/autofill_assistant/browser/mock_script_executor_delegate.cc
index 2bc56bb7b01..c9450bf9cb2 100644
--- a/chromium/components/autofill_assistant/browser/mock_script_executor_delegate.cc
+++ b/chromium/components/autofill_assistant/browser/mock_script_executor_delegate.cc
@@ -11,6 +11,7 @@ using ::testing::ReturnRef;
MockScriptExecutorDelegate::MockScriptExecutorDelegate() {
ON_CALL(*this, GetSettings).WillByDefault(ReturnRef(client_settings_));
ON_CALL(*this, GetLogInfo).WillByDefault(ReturnRef(log_info_));
+ ON_CALL(*this, GetCurrentURL).WillByDefault(ReturnRef(default_url_));
}
MockScriptExecutorDelegate::~MockScriptExecutorDelegate() = default;
diff --git a/chromium/components/autofill_assistant/browser/mock_script_executor_delegate.h b/chromium/components/autofill_assistant/browser/mock_script_executor_delegate.h
index 89ac4818ccd..46c75c3ab7b 100644
--- a/chromium/components/autofill_assistant/browser/mock_script_executor_delegate.h
+++ b/chromium/components/autofill_assistant/browser/mock_script_executor_delegate.h
@@ -49,6 +49,12 @@ class MockScriptExecutorDelegate : public ScriptExecutorDelegate {
(),
(override));
MOCK_METHOD(content::WebContents*, GetWebContents, (), (override));
+ MOCK_METHOD(void,
+ SetJsFlowLibrary,
+ (const std::string& js_flow_library),
+ (override));
+ MOCK_METHOD(JsFlowDevtoolsWrapper*, GetJsFlowDevtoolsWrapper, (), (override));
+
MOCK_METHOD(std::string,
GetEmailAddressForAccessTokenAccount,
(),
@@ -95,10 +101,12 @@ class MockScriptExecutorDelegate : public ScriptExecutorDelegate {
MOCK_METHOD(void, SetBrowseModeInvisible, (bool invisible), (override));
MOCK_METHOD(ProcessedActionStatusDetailsProto&, GetLogInfo, (), (override));
MOCK_METHOD(bool, ShouldShowWarning, (), (override));
+ MOCK_METHOD(bool, MustUseBackendData, (), (const override));
private:
ClientSettings client_settings_;
ProcessedActionStatusDetailsProto log_info_;
+ const GURL default_url_ = GURL("https://example.com/");
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/mock_ui_controller_observer.h b/chromium/components/autofill_assistant/browser/mock_ui_controller_observer.h
index e1f59d5135c..cbdffdbdb71 100644
--- a/chromium/components/autofill_assistant/browser/mock_ui_controller_observer.h
+++ b/chromium/components/autofill_assistant/browser/mock_ui_controller_observer.h
@@ -30,6 +30,8 @@ class MockUiControllerObserver : public UiControllerObserver {
void(const std::vector<UserAction>& user_actions));
MOCK_METHOD1(OnCollectUserDataOptionsChanged,
void(const CollectUserDataOptions* options));
+ MOCK_METHOD2(OnCollectUserDataUiStateChanged,
+ void(bool loading, UserDataEventField event_field));
MOCK_METHOD1(OnDetailsChanged, void(const std::vector<Details>& details));
MOCK_METHOD1(OnInfoBoxChanged, void(const InfoBox* info_box));
MOCK_METHOD1(OnProgressChanged, void(int progress));
diff --git a/chromium/components/autofill_assistant/browser/parse_jspb.cc b/chromium/components/autofill_assistant/browser/parse_jspb.cc
index ac16d9a7a9b..efc2808b1b7 100644
--- a/chromium/components/autofill_assistant/browser/parse_jspb.cc
+++ b/chromium/components/autofill_assistant/browser/parse_jspb.cc
@@ -85,7 +85,7 @@ bool AppendFieldValue(const std::string& jspb_id_prefix,
// Encode these as floats (in a fixed32)
WriteTag(field_tag, WIRETYPE_FIXED32, out);
out->WriteLittleEndian32(
- bit_cast<uint32_t>(static_cast<float>(value.GetDouble())));
+ base::bit_cast<uint32_t>(static_cast<float>(value.GetDouble())));
break;
case base::Value::Type::STRING: {
diff --git a/chromium/components/autofill_assistant/browser/platform_dependencies.cc b/chromium/components/autofill_assistant/browser/platform_dependencies.cc
new file mode 100644
index 00000000000..c88724a9bdc
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/platform_dependencies.cc
@@ -0,0 +1,11 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/platform_dependencies.h"
+
+namespace autofill_assistant {
+
+PlatformDependencies::~PlatformDependencies() = default;
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/platform_dependencies.h b/chromium/components/autofill_assistant/browser/platform_dependencies.h
new file mode 100644
index 00000000000..3415cf31b6d
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/platform_dependencies.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PLATFORM_DEPENDENCIES_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PLATFORM_DEPENDENCIES_H_
+
+#include "content/public/browser/web_contents.h"
+
+namespace autofill_assistant {
+
+// Interface for platform delegates that provide dependencies to the starter.
+//
+// This interface contains all methods which require a platform-specific
+// implementation.
+class PlatformDependencies {
+ public:
+ virtual ~PlatformDependencies();
+
+ virtual bool IsCustomTab(const content::WebContents& web_contents) const = 0;
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PLATFORM_DEPENDENCIES_H_
diff --git a/chromium/components/autofill_assistant/browser/protocol_utils.cc b/chromium/components/autofill_assistant/browser/protocol_utils.cc
index e608627469a..30fc7f0d825 100644
--- a/chromium/components/autofill_assistant/browser/protocol_utils.cc
+++ b/chromium/components/autofill_assistant/browser/protocol_utils.cc
@@ -22,6 +22,7 @@
#include "components/autofill_assistant/browser/actions/edit_password_action.h"
#include "components/autofill_assistant/browser/actions/execute_js_action.h"
#include "components/autofill_assistant/browser/actions/expect_navigation_action.h"
+#include "components/autofill_assistant/browser/actions/external_action.h"
#include "components/autofill_assistant/browser/actions/generate_password_for_form_field_action.h"
#include "components/autofill_assistant/browser/actions/get_element_status_action.h"
#include "components/autofill_assistant/browser/actions/js_flow_action.h"
@@ -30,6 +31,7 @@
#include "components/autofill_assistant/browser/actions/popup_message_action.h"
#include "components/autofill_assistant/browser/actions/presave_generated_password_action.h"
#include "components/autofill_assistant/browser/actions/prompt_action.h"
+#include "components/autofill_assistant/browser/actions/register_password_reset_request_action.h"
#include "components/autofill_assistant/browser/actions/release_elements_action.h"
#include "components/autofill_assistant/browser/actions/reset_pending_credentials_action.h"
#include "components/autofill_assistant/browser/actions/save_generated_password_action.h"
@@ -206,6 +208,7 @@ std::string ProtocolUtils::CreateNextScriptActionsRequest(
ScriptActionRequestProto request_proto;
request_proto.set_global_payload(global_payload);
request_proto.set_script_payload(script_payload);
+
NextScriptActionsRequestProto* next_request =
request_proto.mutable_next_request();
for (const auto& processed_action : processed_actions) {
@@ -453,6 +456,19 @@ std::unique_ptr<Action> ProtocolUtils::CreateAction(ActionDelegate* delegate,
return std::make_unique<ExecuteJsAction>(delegate, action);
case ActionProto::ActionInfoCase::kJsFlow:
return std::make_unique<JsFlowAction>(delegate, action);
+ case ActionProto::ActionInfoCase::kExternalAction:
+ return std::make_unique<ExternalAction>(delegate, action);
+ case ActionProto::ActionInfoCase::kRegisterPasswordResetRequest:
+ return std::make_unique<RegisterPasswordResetRequestAction>(delegate,
+ action);
+ case ActionProto::ActionInfoCase::kSetNativeValue:
+ return PerformOnSingleElementAction::WithClientId(
+ delegate, action, action.set_native_value().client_id(),
+ base::BindOnce(
+ &action_delegate_util::PerformWithTextValue, delegate,
+ action.set_native_value().value(),
+ base::BindOnce(&WebController::SetNativeValue,
+ delegate->GetWebController()->GetWeakPtr())));
case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: {
VLOG(1) << "Encountered action with ACTION_INFO_NOT_SET";
return std::make_unique<UnsupportedAction>(delegate, action);
@@ -717,6 +733,19 @@ absl::optional<ActionProto> ProtocolUtils::ParseFromString(
success = ParseActionFromString(action_id, bytes, error_message,
proto.mutable_js_flow());
break;
+ case ActionProto::ActionInfoCase::kExternalAction:
+ success = ParseActionFromString(action_id, bytes, error_message,
+ proto.mutable_external_action());
+ break;
+ case ActionProto::ActionInfoCase::kSetNativeValue:
+ success = ParseActionFromString(action_id, bytes, error_message,
+ proto.mutable_set_native_value());
+ break;
+ case ActionProto::ActionInfoCase::kRegisterPasswordResetRequest:
+ success = ParseActionFromString(
+ action_id, bytes, error_message,
+ proto.mutable_register_password_reset_request());
+ break;
case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET:
// This is an "unknown action", handled as such in CreateAction.
return proto;
@@ -740,7 +769,8 @@ bool ProtocolUtils::ParseActions(ActionDelegate* delegate,
std::string* return_script_payload,
std::vector<std::unique_ptr<Action>>* actions,
std::vector<std::unique_ptr<Script>>* scripts,
- bool* should_update_scripts) {
+ bool* should_update_scripts,
+ std::string* js_flow_library) {
DCHECK(actions);
DCHECK(scripts);
@@ -759,6 +789,9 @@ bool ProtocolUtils::ParseActions(ActionDelegate* delegate,
if (return_script_payload) {
*return_script_payload = response_proto.script_payload();
}
+ if (js_flow_library) {
+ *js_flow_library = std::move(*response_proto.mutable_js_flow_library());
+ }
for (const auto& action : response_proto.actions()) {
std::unique_ptr<Action> client_action = CreateAction(delegate, action);
@@ -942,15 +975,23 @@ std::string ProtocolUtils::CreateGetUserDataRequest(
bool request_email,
bool request_phone,
bool request_shipping,
+ const std::vector<std::string>& preexisting_address_ids,
bool request_payment_methods,
const std::vector<std::string>& supported_card_networks,
+ const std::vector<std::string>& preexisting_payment_instrument_ids,
const std::string& client_token) {
GetUserDataRequestProto request_proto;
request_proto.set_run_id(run_id);
request_proto.set_request_name(request_name);
request_proto.set_request_email(request_email);
request_proto.set_request_phone(request_phone);
- request_proto.set_request_addresses(request_shipping);
+
+ if (request_shipping) {
+ auto* address_request = request_proto.mutable_request_shipping_addresses();
+ for (const std::string& id : preexisting_address_ids) {
+ address_request->add_preexisting_ids(id);
+ }
+ }
if (request_payment_methods) {
auto* payment_methods_request =
@@ -960,6 +1001,9 @@ std::string ProtocolUtils::CreateGetUserDataRequest(
payment_methods_request->add_supported_card_networks(
supported_card_network);
}
+ for (const std::string& id : preexisting_payment_instrument_ids) {
+ payment_methods_request->add_preexisting_ids(id);
+ }
}
std::string serialized_request_proto;
diff --git a/chromium/components/autofill_assistant/browser/protocol_utils.h b/chromium/components/autofill_assistant/browser/protocol_utils.h
index f3f885fc9f6..52e872ec4b9 100644
--- a/chromium/components/autofill_assistant/browser/protocol_utils.h
+++ b/chromium/components/autofill_assistant/browser/protocol_utils.h
@@ -81,8 +81,10 @@ class ProtocolUtils {
bool request_email,
bool request_phone,
bool request_shipping,
+ const std::vector<std::string>& preexisting_address_ids,
bool request_payment_methods,
const std::vector<std::string>& supported_card_networks,
+ const std::vector<std::string>& preexisting_payment_instrument_ids,
const std::string& client_token);
// Create an action from the |action|.
@@ -114,7 +116,8 @@ class ProtocolUtils {
std::string* return_script_payload,
std::vector<std::unique_ptr<Action>>* actions,
std::vector<std::unique_ptr<Script>>* scripts,
- bool* should_update_scripts);
+ bool* should_update_scripts,
+ std::string* js_flow_library);
// Parses a single serialized ActionProto. Returns nullptr in the case of
// parsing errors.
diff --git a/chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc b/chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc
index 7ce0b5da7e0..a4a30ff6143 100644
--- a/chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -230,11 +230,12 @@ TEST_F(ProtocolUtilsTest, ParseActionsParseError) {
bool unused;
std::vector<std::unique_ptr<Action>> unused_actions;
std::vector<std::unique_ptr<Script>> unused_scripts;
+ std::string unused_js_flow_library;
EXPECT_FALSE(ProtocolUtils::ParseActions(
/* delegate= */ nullptr, /* response= */ "invalid", /* run_id= */ nullptr,
- /* global_payload= */ nullptr,
- /* script_payload= */ nullptr, &unused_actions, &unused_scripts,
- /* should_update_scripts= */ &unused));
+ /* return_global_payload= */ nullptr,
+ /* return_script_payload= */ nullptr, &unused_actions, &unused_scripts,
+ /* should_update_scripts= */ &unused, &unused_js_flow_library));
}
TEST_F(ProtocolUtilsTest, ParseActionParseError) {
@@ -258,10 +259,11 @@ TEST_F(ProtocolUtilsTest, ParseActionsValid) {
bool should_update_scripts = true;
std::vector<std::unique_ptr<Action>> actions;
std::vector<std::unique_ptr<Script>> scripts;
+ std::string unused_js_flow_library;
EXPECT_TRUE(ProtocolUtils::ParseActions(
nullptr, proto_str, &run_id, &global_payload, &script_payload, &actions,
- &scripts, &should_update_scripts));
+ &scripts, &should_update_scripts, &unused_js_flow_library));
EXPECT_EQ(1u, run_id);
EXPECT_EQ("global_payload", global_payload);
EXPECT_EQ("script_payload", script_payload);
@@ -288,11 +290,12 @@ TEST_F(ProtocolUtilsTest, ParseActionsEmptyUpdateScriptList) {
bool should_update_scripts = false;
std::vector<std::unique_ptr<Script>> scripts;
std::vector<std::unique_ptr<Action>> unused_actions;
+ std::string unused_js_flow_library;
EXPECT_TRUE(ProtocolUtils::ParseActions(
nullptr, proto_str, /* run_id= */ nullptr, /* global_payload= */ nullptr,
/* script_payload */ nullptr, &unused_actions, &scripts,
- &should_update_scripts));
+ &should_update_scripts, &unused_js_flow_library));
EXPECT_TRUE(should_update_scripts);
EXPECT_TRUE(scripts.empty());
}
@@ -313,11 +316,13 @@ TEST_F(ProtocolUtilsTest, ParseActionsUpdateScriptListFullFeatured) {
bool should_update_scripts = false;
std::vector<std::unique_ptr<Script>> scripts;
std::vector<std::unique_ptr<Action>> unused_actions;
+ std::string unused_js_flow_library;
EXPECT_TRUE(ProtocolUtils::ParseActions(
- nullptr, proto_str, /* run_id= */ nullptr, /* global_payload= */ nullptr,
- /* script_payload= */ nullptr, &unused_actions, &scripts,
- &should_update_scripts));
+ nullptr, proto_str, /* run_id= */ nullptr,
+ /* return_global_payload= */ nullptr,
+ /* return_script_payload= */ nullptr, &unused_actions, &scripts,
+ &should_update_scripts, &unused_js_flow_library));
EXPECT_TRUE(should_update_scripts);
EXPECT_THAT(scripts, SizeIs(1));
EXPECT_THAT("a", Eq(scripts[0]->handle.path));
@@ -604,28 +609,39 @@ TEST_F(ProtocolUtilsTest, CreateGetUserDataRequest) {
GetUserDataRequestProto request;
EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetUserDataRequest(
/* run_id= */ 1, /* request_name= */ true, /* request_email= */ true,
- /* request_phone= */ true, /* request_shipping= */ true,
+ /* request_phone= */ true, /* request_shipping= */ false,
+ /* preexisting_address_ids= */ std::vector<std::string>(),
/* request_payment_methods= */ false,
/* supported_card_networks= */ std::vector<std::string>(),
+ /* preexisting_payment_instrument_ids= */ std::vector<std::string>(),
/* client_token= */ std::string())));
EXPECT_EQ(request.run_id(), 1u);
EXPECT_TRUE(request.request_name());
EXPECT_TRUE(request.request_email());
EXPECT_TRUE(request.request_phone());
- EXPECT_TRUE(request.request_addresses());
+ EXPECT_FALSE(request.has_request_shipping_addresses());
EXPECT_FALSE(request.has_request_payment_methods());
EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetUserDataRequest(
/* run_id= */ 1, /* request_name= */ true, /* request_email= */ true,
/* request_phone= */ true, /* request_shipping= */ true,
+ /* preexisting_address_ids= */
+ std::vector<std::string>({"address-1", "address-2"}),
/* request_payment_methods= */ true,
/* supported_card_networks= */
std::vector<std::string>({"VISA", "MASTERCARD"}),
+ /* preexisting_payment_instrument_ids= */
+ std::vector<std::string>({"instrument-1", "instrument-2"}),
/* client_token= */ "token")));
+ EXPECT_TRUE(request.has_request_shipping_addresses());
+ EXPECT_THAT(request.request_shipping_addresses().preexisting_ids(),
+ ElementsAre("address-1", "address-2"));
EXPECT_TRUE(request.has_request_payment_methods());
EXPECT_EQ(request.request_payment_methods().client_token(), "token");
EXPECT_THAT(request.request_payment_methods().supported_card_networks(),
ElementsAre("VISA", "MASTERCARD"));
+ EXPECT_THAT(request.request_payment_methods().preexisting_ids(),
+ ElementsAre("instrument-1", "instrument-2"));
}
TEST_F(ProtocolUtilsTest, ComputeNetworkStats) {
diff --git a/chromium/components/autofill_assistant/browser/public/BUILD.gn b/chromium/components/autofill_assistant/browser/public/BUILD.gn
index 7eed6462e1a..d19f754de67 100644
--- a/chromium/components/autofill_assistant/browser/public/BUILD.gn
+++ b/chromium/components/autofill_assistant/browser/public/BUILD.gn
@@ -2,11 +2,18 @@
# Use of this source code is governed by a BSD - style license that can be
# found in the LICENSE file.
+import("//third_party/protobuf/proto_library.gni")
+
+if (is_android) {
+ import("//build/config/android/rules.gni")
+}
+
static_library("public") {
sources = [
"autofill_assistant.cc",
"autofill_assistant.h",
"autofill_assistant_factory.h",
+ "external_action_delegate.h",
"external_script_controller.h",
"runtime_manager.cc",
"runtime_manager.h",
@@ -17,17 +24,33 @@ static_library("public") {
]
deps = [
+ ":proto",
"//base",
"//components/version_info:channel",
"//content/public/browser",
]
}
+proto_library("proto") {
+ proto_in_dir = "//components/autofill_assistant/"
+ sources = [ "external_action.proto" ]
+}
+
+# Java protos are only used for testing.
+if (is_android) {
+ proto_java_library("proto_java") {
+ proto_path = "//components/autofill_assistant/browser/public"
+ sources = [ "$proto_path/external_action.proto" ]
+ }
+}
+
static_library("unit_test_support") {
testonly = true
sources = [
"mock_autofill_assistant.cc",
"mock_autofill_assistant.h",
+ "mock_external_script_controller.cc",
+ "mock_external_script_controller.h",
"mock_runtime_manager.cc",
"mock_runtime_manager.h",
]
diff --git a/chromium/components/autofill_assistant/browser/public/autofill_assistant.h b/chromium/components/autofill_assistant/browser/public/autofill_assistant.h
index 7651c53fa99..2fd5cb10467 100644
--- a/chromium/components/autofill_assistant/browser/public/autofill_assistant.h
+++ b/chromium/components/autofill_assistant/browser/public/autofill_assistant.h
@@ -10,8 +10,13 @@
#include "base/callback_forward.h"
#include "base/containers/flat_map.h"
+#include "components/autofill_assistant/browser/public/external_action_delegate.h"
#include "components/autofill_assistant/browser/public/external_script_controller.h"
+namespace content {
+class WebContents;
+}
+
namespace autofill_assistant {
// Abstract interface for exported services.
@@ -53,8 +58,14 @@ class AutofillAssistant {
// |ExternalScriptController::StartScript|.
// The returned |ExternalScriptController| instance has to survive for the
// duration of the execution of the script.
+ // |action_extension_delegate| can be nullptr, but in that case the script
+ // execution will fail if it reaches an external action. If present,
+ // |action_extension_delegate| instance must outlive the
+ // |ExternalScriptController|.
virtual std::unique_ptr<ExternalScriptController>
- CreateExternalScriptController(content::WebContents* web_contents) = 0;
+ CreateExternalScriptController(
+ content::WebContents* web_contents,
+ ExternalActionDelegate* action_extension_delegate) = 0;
protected:
AutofillAssistant() = default;
diff --git a/chromium/components/autofill_assistant/browser/public/autofill_assistant_factory.h b/chromium/components/autofill_assistant/browser/public/autofill_assistant_factory.h
index b96790f9db1..1257facc9fe 100644
--- a/chromium/components/autofill_assistant/browser/public/autofill_assistant_factory.h
+++ b/chromium/components/autofill_assistant/browser/public/autofill_assistant_factory.h
@@ -5,19 +5,26 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_AUTOFILL_ASSISTANT_FACTORY_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_AUTOFILL_ASSISTANT_FACTORY_H_
+#include <memory>
+
#include "components/autofill_assistant/browser/public/autofill_assistant.h"
#include "components/version_info/channel.h"
#include "content/public/browser/browser_context.h"
namespace autofill_assistant {
-// Creates an instance of |AutofillAssistant|.
+
+class CommonDependencies;
+
+// Factory class for creating |AutofillAssistant|.
class AutofillAssistantFactory {
public:
+ // TODO(b/201964911) The |AutofillAssistant::CreateExternalScriptController|
+ // method ignores the |channel|, |country_code| and |locale| passed here and
+ // instead fetches them directly. Make the treatment between
+ // |ExternalScriptController| and |GetCapabilitiesByHashPrefix| consistent.
static std::unique_ptr<AutofillAssistant> CreateForBrowserContext(
content::BrowserContext* browser_context,
- version_info::Channel channel,
- const std::string& country_code,
- const std::string& locale);
+ std::unique_ptr<CommonDependencies> dependencies);
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/public/external_action.proto b/chromium/components/autofill_assistant/browser/public/external_action.proto
new file mode 100644
index 00000000000..595f21825fa
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/public/external_action.proto
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_package = "org.chromium.chrome.browser.autofill_assistant.external.proto";
+option java_multiple_files = true;
+
+package autofill_assistant.external;
+
+// Defines an action to be executed by the ExternalActionDelegate.
+message Action {
+ // Integrator-specific information. This was forwarded without modifications.
+ optional ActionInfo info = 1;
+}
+
+// Extended by the integrator.
+message ActionInfo {
+ extensions 100 to max;
+}
+
+// The result of the execution of |Action|
+message Result {
+ // Integrator-specific information. This will be forwarded to the backend
+ // without modification.
+ optional ResultInfo result_info = 1;
+
+ // Whether the action was successful.
+ optional bool success = 2;
+}
+
+// Extended by the integrator.
+message ResultInfo {
+ extensions 100 to max;
+}
+
+// An update on the status of the DOM conditions for the current action.
+message ElementConditionsUpdate {
+ // The list of conditions whose status changed since the last update. The
+ // first update for each action will contain all the conditions which are
+ // specified in the equivalent action in the backend.
+ message ConditionResult {
+ // The identifier of this condition, matches the one specified in the
+ // equivalent action in the backend.
+ optional int32 id = 1;
+ // Whether the condition was satisfied.
+ optional bool satisfied = 2;
+ }
+ repeated ConditionResult results = 1;
+}
diff --git a/chromium/components/autofill_assistant/browser/public/external_action_delegate.h b/chromium/components/autofill_assistant/browser/public/external_action_delegate.h
new file mode 100644
index 00000000000..427ef0fbc82
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/public/external_action_delegate.h
@@ -0,0 +1,43 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_EXTERNAL_ACTION_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_EXTERNAL_ACTION_DELEGATE_H_
+
+#include "base/callback.h"
+#include "components/autofill_assistant/browser/public/external_action.pb.h"
+
+namespace autofill_assistant {
+
+// Allows to handle external actions happening during the execution of a
+// script.
+class ExternalActionDelegate {
+ public:
+ // Called to notify a change in the DOM.
+ using DomUpdateCallback =
+ base::RepeatingCallback<void(const external::ElementConditionsUpdate&)>;
+
+ virtual ~ExternalActionDelegate() = default;
+ // Called when the script reaches an external action.
+ // The |start_dom_checks_callback| can optionally be called to start the DOM
+ // checks. This will allow interrupts to trigger (if the action itself allows
+ // them). Calling |end_action_callback| will end the external action and
+ // resume the execution of the rest of the script.
+ virtual void OnActionRequested(
+ const external::Action& action_info,
+ base::OnceCallback<void(DomUpdateCallback)> start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result&)>
+ end_action_callback) = 0;
+
+ // Called before starting the execution of an interrupt.
+ virtual void OnInterruptStarted() = 0;
+
+ // Called after finishing to execute an interrupt, before resuming the
+ // execution of the main script.
+ virtual void OnInterruptFinished() = 0;
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_EXTERNAL_ACTION_DELEGATE_H_
diff --git a/chromium/components/autofill_assistant/browser/public/external_script_controller.h b/chromium/components/autofill_assistant/browser/public/external_script_controller.h
index 540197b1d7c..44dad58a0aa 100644
--- a/chromium/components/autofill_assistant/browser/public/external_script_controller.h
+++ b/chromium/components/autofill_assistant/browser/public/external_script_controller.h
@@ -5,10 +5,10 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_EXTERNAL_SCRIPT_CONTROLLER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_EXTERNAL_SCRIPT_CONTROLLER_H_
-#include "base/memory/weak_ptr.h"
-#include "components/autofill_assistant/browser/public/runtime_observer.h"
-#include "components/autofill_assistant/browser/public/ui_state.h"
-#include "content/public/browser/web_contents.h"
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/public/mock_autofill_assistant.h b/chromium/components/autofill_assistant/browser/public/mock_autofill_assistant.h
index 2afa789ec3a..e24ad5791c0 100644
--- a/chromium/components/autofill_assistant/browser/public/mock_autofill_assistant.h
+++ b/chromium/components/autofill_assistant/browser/public/mock_autofill_assistant.h
@@ -25,7 +25,8 @@ class MockAutofillAssistant : public AutofillAssistant {
(override));
MOCK_METHOD(std::unique_ptr<ExternalScriptController>,
CreateExternalScriptController,
- (content::WebContents * web_contents),
+ (content::WebContents * web_contents,
+ ExternalActionDelegate* action_extension_delegate),
(override));
};
diff --git a/chromium/components/autofill_assistant/browser/public/mock_external_script_controller.cc b/chromium/components/autofill_assistant/browser/public/mock_external_script_controller.cc
new file mode 100644
index 00000000000..b4f88b39434
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/public/mock_external_script_controller.cc
@@ -0,0 +1,13 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/public/mock_external_script_controller.h"
+
+namespace autofill_assistant {
+
+MockExternalScriptController::MockExternalScriptController() = default;
+
+MockExternalScriptController::~MockExternalScriptController() = default;
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/public/mock_external_script_controller.h b/chromium/components/autofill_assistant/browser/public/mock_external_script_controller.h
new file mode 100644
index 00000000000..00e168088a8
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/public/mock_external_script_controller.h
@@ -0,0 +1,28 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_MOCK_EXTERNAL_SCRIPT_CONTROLLER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_MOCK_EXTERNAL_SCRIPT_CONTROLLER_H_
+
+#include "base/callback_helpers.h"
+#include "components/autofill_assistant/browser/public/external_script_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+class MockExternalScriptController : public ExternalScriptController {
+ public:
+ MockExternalScriptController();
+ ~MockExternalScriptController() override;
+
+ MOCK_METHOD(void,
+ StartScript,
+ ((const base::flat_map<std::string, std::string>&),
+ (base::OnceCallback<void(ScriptResult)>)),
+ (override));
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_MOCK_EXTERNAL_SCRIPT_CONTROLLER_H_
diff --git a/chromium/components/autofill_assistant/browser/script_executor.cc b/chromium/components/autofill_assistant/browser/script_executor.cc
index 1f368e90b6a..f35b891e2cd 100644
--- a/chromium/components/autofill_assistant/browser/script_executor.cc
+++ b/chromium/components/autofill_assistant/browser/script_executor.cc
@@ -29,6 +29,7 @@
#include "components/autofill_assistant/browser/wait_for_dom_operation.h"
#include "components/autofill_assistant/browser/web/element_action_util.h"
#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/element_store.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
#include "components/strings/grit/components_strings.h"
@@ -340,6 +341,8 @@ void ScriptExecutor::CollectUserData(
weak_ptr_factory_.GetWeakPtr(),
std::move(collect_user_data_options->terms_link_callback));
ui_delegate_->SetCollectUserDataOptions(collect_user_data_options);
+ ui_delegate_->SetCollectUserDataUiState(/* loading= */ false,
+ UserDataEventField::NONE);
delegate_->EnterState(AutofillAssistantState::PROMPT);
}
@@ -453,11 +456,13 @@ void ScriptExecutor::Prompt(
}
}
-void ScriptExecutor::CleanUpAfterPrompt() {
+void ScriptExecutor::CleanUpAfterPrompt(bool consume_touchable_area) {
ui_delegate_->SetUserActions(nullptr);
- // Mark touchable_elements_ as consumed, so that it won't affect the next
- // prompt or the end of the script.
- touchable_element_area_.reset();
+ if (consume_touchable_area) {
+ // Mark touchable_elements_ as consumed, so that it won't affect the next
+ // prompt or the end of the script.
+ touchable_element_area_.reset();
+ }
delegate_->ClearTouchableElementArea();
ui_delegate_->SetExpandSheetForPromptAction(true);
@@ -624,6 +629,10 @@ content::WebContents* ScriptExecutor::GetWebContents() const {
return delegate_->GetWebContents();
}
+JsFlowDevtoolsWrapper* ScriptExecutor::GetJsFlowDevtoolsWrapper() const {
+ return delegate_->GetJsFlowDevtoolsWrapper();
+}
+
ElementStore* ScriptExecutor::GetElementStore() const {
return element_store_.get();
}
@@ -824,6 +833,7 @@ void ScriptExecutor::OnGetActions(
roundtrip_duration.InMilliseconds());
bool success = http_status == net::HTTP_OK &&
ProcessNextActionResponse(response, response_info);
+
if (should_stop_script_) {
// The last action forced the script to stop. Sending the result of the
// action is considered best effort in this situation. Report a successful
@@ -865,14 +875,19 @@ bool ScriptExecutor::ProcessNextActionResponse(
actions_.clear();
bool should_update_scripts = false;
+ std::string js_flow_library;
std::vector<std::unique_ptr<Script>> scripts;
bool parse_result = ProtocolUtils::ParseActions(
this, response, &run_id_, &last_global_payload_, &last_script_payload_,
- &actions_, &scripts, &should_update_scripts);
+ &actions_, &scripts, &should_update_scripts, &js_flow_library);
if (!parse_result) {
return false;
}
+ if (!js_flow_library.empty()) {
+ delegate_->SetJsFlowLibrary(js_flow_library);
+ }
+
roundtrip_network_stats_ =
ProtocolUtils::ComputeNetworkStats(response, response_info, actions_);
ReportPayloadsToListener();
@@ -974,12 +989,24 @@ void ScriptExecutor::GetNextActions() {
weak_ptr_factory_.GetWeakPtr(), get_next_actions_start));
}
+void ScriptExecutor::MaybeSetPreviousAction(
+ const ProcessedActionProto& processed_action) {
+ const auto action_info_case = processed_action.action().action_info_case();
+
+ // JS flows are themselves a way of executing a script.
+ if (action_info_case == ActionProto::kJsFlow) {
+ return;
+ }
+
+ previous_action_type_ = action_info_case;
+}
+
void ScriptExecutor::OnProcessedAction(
base::TimeTicks start_time,
std::unique_ptr<ProcessedActionProto> processed_action_proto) {
DCHECK(current_action_);
base::TimeDelta run_time = base::TimeTicks::Now() - start_time;
- previous_action_type_ = processed_action_proto->action().action_info_case();
+ MaybeSetPreviousAction(*processed_action_proto);
processed_actions_.emplace_back(*processed_action_proto);
#ifdef NDEBUG
@@ -1075,13 +1102,16 @@ ProcessedActionStatusDetailsProto& ScriptExecutor::GetLogInfo() {
}
void ScriptExecutor::RequestUserData(
+ UserDataEventField event_field,
const CollectUserDataOptions& options,
base::OnceCallback<void(bool, const GetUserDataResponseProto&)> callback) {
auto* service = delegate_->GetService();
DCHECK(service);
+ delegate_->EnterState(AutofillAssistantState::RUNNING);
+ ui_delegate_->SetCollectUserDataUiState(/* loading= */ true, event_field);
service->GetUserData(
- options, run_id_,
+ options, run_id_, user_data_,
base::BindOnce(&ScriptExecutor::OnRequestUserData,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -1101,4 +1131,49 @@ void ScriptExecutor::OnRequestUserData(
std::move(callback).Run(success, response_proto);
}
+bool ScriptExecutor::SupportsExternalActions() {
+ return ui_delegate_->SupportsExternalActions();
+}
+
+void ScriptExecutor::RequestExternalAction(
+ const ExternalActionProto& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ bool prompt = external_action.allow_interrupt() ||
+ external_action.show_touchable_area();
+ if (prompt && delegate_->EnterState(AutofillAssistantState::PROMPT)) {
+ if (external_action.show_touchable_area() && touchable_element_area_) {
+ delegate_->SetTouchableElementArea(*touchable_element_area_);
+
+ // The touchable element and overlays are cleared by calling
+ // ScriptExecutor::CleanUpAfterPrompt
+ }
+ }
+ external::Action action;
+ *action.mutable_info() = external_action.info();
+ ui_delegate_->ExecuteExternalAction(
+ action, std::move(start_dom_checks_callback),
+ base::BindOnce(&ScriptExecutor::OnExternalActionFinished,
+ weak_ptr_factory_.GetWeakPtr(), external_action, prompt,
+ std::move(end_action_callback)));
+}
+
+void ScriptExecutor::OnExternalActionFinished(
+ const ExternalActionProto& external_action,
+ const bool prompt,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback,
+ const external::Result& result) {
+ if (prompt) {
+ CleanUpAfterPrompt(external_action.show_touchable_area());
+ }
+ std::move(end_action_callback).Run(result);
+}
+
+bool ScriptExecutor::MustUseBackendData() const {
+ return delegate_->MustUseBackendData();
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/script_executor.h b/chromium/components/autofill_assistant/browser/script_executor.h
index 51bd880cf11..8c09cc63c86 100644
--- a/chromium/components/autofill_assistant/browser/script_executor.h
+++ b/chromium/components/autofill_assistant/browser/script_executor.h
@@ -38,6 +38,7 @@
#include "services/metrics/public/cpp/ukm_recorder.h"
namespace autofill_assistant {
+class ElementFinderResult;
class ElementStore;
class UserModel;
class WaitForDomOperation;
@@ -173,7 +174,7 @@ class ScriptExecutor : public ActionDelegate,
base::OnceCallback<void()> end_on_navigation_callback,
bool browse_mode,
bool browse_mode_invisible) override;
- void CleanUpAfterPrompt() override;
+ void CleanUpAfterPrompt(bool consume_touchable_area = true) override;
void SetBrowseDomainsAllowlist(std::vector<std::string> domains) override;
void RetrieveElementFormAndFieldData(
const Selector& selector,
@@ -209,6 +210,7 @@ class ScriptExecutor : public ActionDelegate,
password_manager::PasswordChangeSuccessTracker*
GetPasswordChangeSuccessTracker() const override;
content::WebContents* GetWebContents() const override;
+ JsFlowDevtoolsWrapper* GetJsFlowDevtoolsWrapper() const override;
ElementStore* GetElementStore() const override;
WebController* GetWebController() const override;
std::string GetEmailAddressForAccessTokenAccount() const override;
@@ -261,9 +263,20 @@ class ScriptExecutor : public ActionDelegate,
base::WeakPtr<ActionDelegate> GetWeakPtr() const override;
ProcessedActionStatusDetailsProto& GetLogInfo() override;
void RequestUserData(
+ UserDataEventField event_field,
const CollectUserDataOptions& options,
base::OnceCallback<void(bool, const GetUserDataResponseProto&)> callback)
override;
+ bool SupportsExternalActions() override;
+ void RequestExternalAction(
+ const ExternalActionProto& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) override;
+ bool MustUseBackendData() const override;
+ void MaybeSetPreviousAction(
+ const ProcessedActionProto& processed_action) override;
private:
// TODO(b/220079189): remove this friend declaration.
@@ -335,6 +348,11 @@ class ScriptExecutor : public ActionDelegate,
int http_status,
const std::string& response,
const ServiceRequestSender::ResponseInfo& response_info);
+ void OnExternalActionFinished(
+ const ExternalActionProto& external_action,
+ const bool prompt,
+ base::OnceCallback<void(const external::Result& result)> callback,
+ const external::Result& result);
// Maybe shows the message specified in a callout, depending on the current
// state and client settings.
diff --git a/chromium/components/autofill_assistant/browser/script_executor_browsertest.cc b/chromium/components/autofill_assistant/browser/script_executor_browsertest.cc
new file mode 100644
index 00000000000..d0033b3193f
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/script_executor_browsertest.cc
@@ -0,0 +1,481 @@
+// Copyright 2022 The Chromium 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 <string>
+
+#include "base/base64.h"
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "components/autofill_assistant/browser/base_browsertest.h"
+#include "components/autofill_assistant/browser/client_status.h"
+#include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
+#include "components/autofill_assistant/browser/fake_script_executor_ui_delegate.h"
+#include "components/autofill_assistant/browser/js_flow_util.h"
+#include "components/autofill_assistant/browser/model.pb.h"
+#include "components/autofill_assistant/browser/script.h"
+#include "components/autofill_assistant/browser/script_executor.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/service/mock_service.h"
+#include "components/autofill_assistant/browser/web/web_controller.h"
+#include "content/public/test/browser_test.h"
+#include "content/shell/browser/shell.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace autofill_assistant {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::DoAll;
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Matcher;
+using ::testing::Ne;
+using ::testing::NiceMock;
+using ::testing::Pair;
+using ::testing::Pointee;
+using ::testing::Property;
+using ::testing::SizeIs;
+using ::testing::StrictMock;
+using ::testing::WithArg;
+using ::testing::WithArgs;
+
+class ScriptExecutorBrowserTest : public BaseBrowserTest {
+ public:
+ void SetUpOnMainThread() override {
+ BaseBrowserTest::SetUpOnMainThread();
+
+ web_controller_ = WebController::CreateForWebContents(
+ shell()->web_contents(), &user_data_, &log_info_, nullptr,
+ /*enable_full_stack_traces= */ true);
+
+ fake_script_executor_delegate_.SetService(&mock_service_);
+ fake_script_executor_delegate_.SetWebController(web_controller_.get());
+ fake_script_executor_delegate_.SetCurrentURL(GURL("http://example.com/"));
+ fake_script_executor_delegate_.SetWebContents(shell()->web_contents());
+ }
+
+ protected:
+ void Run(const ActionsResponseProto& actions_response) {
+ EXPECT_CALL(mock_service_, GetActions)
+ .WillOnce(RunOnceCallback<5>(net::HTTP_OK,
+ actions_response.SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{}));
+
+ ON_CALL(mock_service_, GetNextActions)
+ .WillByDefault(RunOnceCallback<6>(
+ net::HTTP_OK, ActionsResponseProto().SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{}));
+
+ base::RunLoop run_loop;
+
+ script_executor_ = std::make_unique<ScriptExecutor>(
+ /* script_path= */ "",
+ /* additional_context= */ std::make_unique<TriggerContext>(),
+ /* global_payload= */ "",
+ /* script_payload= */ "",
+ /* listener= */ nullptr, &ordered_interrupts_,
+ &fake_script_executor_delegate_, &fake_script_executor_ui_delegate_);
+
+ script_executor_->Run(
+ &user_data_, executor_callback_.Get().Then(run_loop.QuitClosure()));
+ run_loop.Run();
+ }
+
+ void RunJsFlow(const std::string& js_flow,
+ const ProcessedActionStatusProto& result) {
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+
+ EXPECT_CALL(mock_service_, GetNextActions)
+ .WillOnce(DoAll(
+ WithArgs<3>([&result](const std::vector<ProcessedActionProto>&
+ processed_actions) {
+ EXPECT_THAT(
+ processed_actions,
+ ElementsAre(Property(&ProcessedActionProto::status, result)));
+ }),
+ RunOnceCallback<6>(net::HTTP_OK,
+ ActionsResponseProto().SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{})));
+
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_js_flow()->set_js_flow(js_flow);
+ Run(actions_response);
+ }
+
+ std::unique_ptr<ScriptExecutor> script_executor_;
+
+ std::vector<std::unique_ptr<Script>> ordered_interrupts_;
+
+ ProcessedActionStatusDetailsProto log_info_;
+ std::unique_ptr<WebController> web_controller_;
+
+ FakeScriptExecutorDelegate fake_script_executor_delegate_;
+ FakeScriptExecutorUiDelegate fake_script_executor_ui_delegate_;
+ UserData user_data_;
+
+ NiceMock<MockService> mock_service_;
+
+ StrictMock<base::MockCallback<ScriptExecutor::RunScriptCallback>>
+ executor_callback_;
+};
+
+std::string CreateRunNativeActionCall(
+ const google::protobuf::MessageLite* proto,
+ const ActionProto::ActionInfoCase action_info_case) {
+ return base::StrCat({"const [status, value] = await runNativeAction(",
+ base::NumberToString(action_info_case), ", '",
+ js_flow_util::SerializeToBase64(proto), "');"});
+}
+
+std::string CreateRunNativeActionCallReturn(
+ const google::protobuf::MessageLite* proto,
+ const ActionProto::ActionInfoCase action_info_case) {
+ return base::StrCat({"{", CreateRunNativeActionCall(proto, action_info_case),
+ "return {status}}"});
+}
+
+std::string CreateRunNativeActionCallReturnIfError(
+ const google::protobuf::MessageLite* proto,
+ const ActionProto::ActionInfoCase action_info_case) {
+ return base::StrCat({"{", CreateRunNativeActionCall(proto, action_info_case),
+ "if(status != 2) return {status}}"});
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, ShutdownAfter_Stop) {
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_stop();
+
+ EXPECT_CALL(executor_callback_,
+ Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+ Field(&ScriptExecutor::Result::at_end,
+ ScriptExecutor::SHUTDOWN))));
+
+ Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest,
+ ShutdownGracefullyAfter_Tell_Stop) {
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_tell()->set_message("message");
+ actions_response.add_actions()->mutable_stop();
+
+ EXPECT_CALL(executor_callback_,
+ Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+ Field(&ScriptExecutor::Result::at_end,
+ ScriptExecutor::SHUTDOWN_GRACEFULLY))));
+
+ Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest,
+ ShutdownGracefullyAfter_Tell_EmptyJsFlow_Stop) {
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_tell()->set_message("message");
+ actions_response.add_actions()->mutable_js_flow();
+ actions_response.add_actions()->mutable_stop();
+
+ EXPECT_CALL(executor_callback_,
+ Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+ Field(&ScriptExecutor::Result::at_end,
+ ScriptExecutor::SHUTDOWN_GRACEFULLY))));
+
+ Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest,
+ ShutdownGracefullyAfter_JsFlowTell_Stop) {
+ TellProto tell;
+ tell.set_message("message");
+
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+ CreateRunNativeActionCallReturn(&tell, ActionProto::kTell));
+ actions_response.add_actions()->mutable_stop();
+
+ EXPECT_CALL(executor_callback_,
+ Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+ Field(&ScriptExecutor::Result::at_end,
+ ScriptExecutor::SHUTDOWN_GRACEFULLY))));
+
+ Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest,
+ ShutdownGracefullyAfter_JsFlowTellAndStop) {
+ TellProto tell;
+ tell.set_message("message");
+ StopProto stop;
+
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+ CreateRunNativeActionCallReturnIfError(&tell, ActionProto::kTell) +
+ CreateRunNativeActionCallReturn(&stop, ActionProto::kStop));
+
+ EXPECT_CALL(executor_callback_,
+ Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+ Field(&ScriptExecutor::Result::at_end,
+ ScriptExecutor::SHUTDOWN_GRACEFULLY))));
+
+ Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest,
+ ShutdownGracefullyAfter_JsFlowTell_JsFlowStop) {
+ TellProto tell;
+ tell.set_message("message");
+ StopProto stop;
+
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+ CreateRunNativeActionCallReturnIfError(&tell, ActionProto::kTell));
+ actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+ CreateRunNativeActionCallReturn(&stop, ActionProto::kStop));
+
+ EXPECT_CALL(executor_callback_,
+ Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+ Field(&ScriptExecutor::Result::at_end,
+ ScriptExecutor::SHUTDOWN_GRACEFULLY))));
+
+ Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest,
+ ShutdownGracefullyAfter_Tell_JsFlowStop) {
+ StopProto stop;
+
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_tell()->set_message("message");
+ actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+ CreateRunNativeActionCallReturn(&stop, ActionProto::kStop));
+
+ EXPECT_CALL(executor_callback_,
+ Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+ Field(&ScriptExecutor::Result::at_end,
+ ScriptExecutor::SHUTDOWN_GRACEFULLY))));
+
+ Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsLibraryIsUsed) {
+ ActionsResponseProto actions_response;
+ actions_response.set_js_flow_library("const status = 2;");
+ actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+ "return {status};");
+
+ EXPECT_CALL(mock_service_,
+ GetNextActions(_, _, _,
+ ElementsAre(Property(&ProcessedActionProto::status,
+ ACTION_APPLIED)),
+ _, _, _))
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
+ ActionsResponseProto().SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{}));
+
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+
+ Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsLibraryIsReused) {
+ ActionsResponseProto actions_response;
+ actions_response.set_js_flow_library("const status = 2;");
+ actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+ "return {status};");
+ actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+ "return {status};");
+
+ EXPECT_CALL(
+ mock_service_,
+ GetNextActions(
+ _, _, _,
+ ElementsAre(Property(&ProcessedActionProto::status, ACTION_APPLIED),
+ Property(&ProcessedActionProto::status, ACTION_APPLIED)),
+ _, _, _))
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
+ ActionsResponseProto().SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{}));
+
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+
+ Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsLibraryIsUsedAccrossCalls) {
+ ActionsResponseProto actions_response_1;
+ actions_response_1.set_js_flow_library("const st = 2;");
+ actions_response_1.add_actions()->mutable_js_flow()->set_js_flow(
+ "return {status: st};");
+
+ ActionsResponseProto actions_response_2;
+ actions_response_2.add_actions()->mutable_js_flow()->set_js_flow(
+ "return {status: st};");
+
+ EXPECT_CALL(mock_service_,
+ GetNextActions(_, _, _,
+ ElementsAre(Property(&ProcessedActionProto::status,
+ ACTION_APPLIED)),
+ _, _, _))
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
+ actions_response_2.SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{}))
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
+ ActionsResponseProto().SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{}));
+
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+
+ Run(actions_response_1);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsFlowErrorInJsLibraryFlow) {
+ ActionsResponseProto actions_response_1;
+ actions_response_1.set_js_flow_library("throw new Error();");
+ actions_response_1.add_actions()->mutable_js_flow()->set_js_flow(
+ "return {status: 2};");
+
+ EXPECT_CALL(mock_service_, GetNextActions)
+ .WillOnce(DoAll(
+ WithArgs<3>(
+ [](const std::vector<ProcessedActionProto>& processed_actions) {
+ ASSERT_THAT(processed_actions, SizeIs(1));
+
+ const auto& processed_action = processed_actions[0];
+ EXPECT_THAT(processed_action,
+ Property(&ProcessedActionProto::status,
+ UNEXPECTED_JS_ERROR));
+
+ const auto& unexpected_error_info =
+ processed_action.status_details().unexpected_error_info();
+
+ EXPECT_THAT(
+ unexpected_error_info.js_exception_locations(),
+ ElementsAre(UnexpectedErrorInfoProto::JS_FLOW_LIBRARY));
+ EXPECT_THAT(unexpected_error_info.js_exception_line_numbers(),
+ ElementsAre(0));
+ EXPECT_THAT(unexpected_error_info.js_exception_column_numbers(),
+ ElementsAre(6));
+ }),
+ RunOnceCallback<6>(net::HTTP_OK,
+ ActionsResponseProto().SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{})));
+
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+
+ Run(actions_response_1);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsFlowErrorInJsFlow) {
+ ActionsResponseProto actions_response_1;
+ actions_response_1.set_js_flow_library("const st = 2;\n");
+ actions_response_1.add_actions()->mutable_js_flow()->set_js_flow(
+ "throw new Error();");
+
+ EXPECT_CALL(mock_service_, GetNextActions)
+ .WillOnce(DoAll(
+ WithArgs<3>(
+ [](const std::vector<ProcessedActionProto>& processed_actions) {
+ ASSERT_THAT(processed_actions, SizeIs(1));
+
+ const auto& processed_action = processed_actions[0];
+ EXPECT_THAT(processed_action,
+ Property(&ProcessedActionProto::status,
+ UNEXPECTED_JS_ERROR));
+
+ const auto& unexpected_error_info =
+ processed_action.status_details().unexpected_error_info();
+
+ EXPECT_THAT(unexpected_error_info.js_exception_locations(),
+ ElementsAre(UnexpectedErrorInfoProto::JS_FLOW));
+ EXPECT_THAT(unexpected_error_info.js_exception_line_numbers(),
+ ElementsAre(0));
+ EXPECT_THAT(unexpected_error_info.js_exception_column_numbers(),
+ ElementsAre(6));
+ }),
+ RunOnceCallback<6>(net::HTTP_OK,
+ ActionsResponseProto().SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{})));
+
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+
+ Run(actions_response_1);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsFlowNestedError) {
+ ActionsResponseProto actions_response_1;
+ actions_response_1.set_js_flow_library(
+ "function throwError() {throw new Error();}");
+ actions_response_1.add_actions()->mutable_js_flow()->set_js_flow(
+ "throwError();");
+
+ EXPECT_CALL(mock_service_, GetNextActions)
+ .WillOnce(DoAll(
+ WithArgs<3>(
+ [](const std::vector<ProcessedActionProto>& processed_actions) {
+ ASSERT_THAT(processed_actions, SizeIs(1));
+
+ const auto& processed_action = processed_actions[0];
+ EXPECT_THAT(processed_action,
+ Property(&ProcessedActionProto::status,
+ UNEXPECTED_JS_ERROR));
+
+ const auto& unexpected_error_info =
+ processed_action.status_details().unexpected_error_info();
+
+ EXPECT_THAT(
+ unexpected_error_info.js_exception_locations(),
+ ElementsAre(UnexpectedErrorInfoProto::JS_FLOW_LIBRARY,
+ UnexpectedErrorInfoProto::JS_FLOW));
+ EXPECT_THAT(unexpected_error_info.js_exception_line_numbers(),
+ ElementsAre(0, 0));
+ EXPECT_THAT(unexpected_error_info.js_exception_column_numbers(),
+ ElementsAre(29, 0));
+ }),
+ RunOnceCallback<6>(net::HTTP_OK,
+ ActionsResponseProto().SerializeAsString(),
+ ServiceRequestSender::ResponseInfo{})));
+
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+
+ Run(actions_response_1);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, WaitForDomSucceeds) {
+ WaitForDomProto wait_for_dom;
+ wait_for_dom.mutable_wait_condition()
+ ->mutable_match()
+ ->add_filters()
+ ->set_css_selector("#button");
+
+ RunJsFlow(
+ CreateRunNativeActionCallReturn(&wait_for_dom, ActionProto::kWaitForDom),
+ ACTION_APPLIED);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, WaitForDomFails) {
+ WaitForDomProto wait_for_dom;
+ wait_for_dom.mutable_wait_condition()
+ ->mutable_match()
+ ->add_filters()
+ ->set_css_selector("#not-found");
+
+ RunJsFlow(
+ CreateRunNativeActionCallReturn(&wait_for_dom, ActionProto::kWaitForDom),
+ ELEMENT_RESOLUTION_FAILED);
+}
+} // namespace
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/script_executor_delegate.h b/chromium/components/autofill_assistant/browser/script_executor_delegate.h
index deb2c37c43a..787cb4a1479 100644
--- a/chromium/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/chromium/components/autofill_assistant/browser/script_executor_delegate.h
@@ -13,6 +13,7 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/details.h"
#include "components/autofill_assistant/browser/info_box.h"
+#include "components/autofill_assistant/browser/js_flow_devtools_wrapper.h"
#include "components/autofill_assistant/browser/state.h"
#include "components/autofill_assistant/browser/tts_button_state.h"
#include "components/autofill_assistant/browser/user_action.h"
@@ -63,6 +64,10 @@ class ScriptExecutorDelegate {
virtual password_manager::PasswordChangeSuccessTracker*
GetPasswordChangeSuccessTracker() = 0;
virtual content::WebContents* GetWebContents() = 0;
+
+ virtual void SetJsFlowLibrary(const std::string& js_flow_library) = 0;
+ virtual JsFlowDevtoolsWrapper* GetJsFlowDevtoolsWrapper() = 0;
+
virtual std::string GetEmailAddressForAccessTokenAccount() = 0;
virtual ukm::UkmRecorder* GetUkmRecorder() = 0;
@@ -147,8 +152,12 @@ class ScriptExecutorDelegate {
// gets attached to the action's response if non empty.
virtual ProcessedActionStatusDetailsProto& GetLogInfo() = 0;
+ // Returns whether or not this instance of Autofill Assistant must use a
+ // backend endpoint to query data.
+ virtual bool MustUseBackendData() const = 0;
+
protected:
- virtual ~ScriptExecutorDelegate() {}
+ virtual ~ScriptExecutorDelegate() = default;
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/script_executor_ui_delegate.h b/chromium/components/autofill_assistant/browser/script_executor_ui_delegate.h
index 8e5512b4874..cd927d802d0 100644
--- a/chromium/components/autofill_assistant/browser/script_executor_ui_delegate.h
+++ b/chromium/components/autofill_assistant/browser/script_executor_ui_delegate.h
@@ -13,17 +13,20 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/details.h"
#include "components/autofill_assistant/browser/info_box.h"
+#include "components/autofill_assistant/browser/public/external_action_delegate.h"
+#include "components/autofill_assistant/browser/public/external_script_controller.h"
#include "components/autofill_assistant/browser/state.h"
#include "components/autofill_assistant/browser/tts_button_state.h"
#include "components/autofill_assistant/browser/user_action.h"
#include "components/autofill_assistant/browser/user_data.h"
+#include "components/autofill_assistant/browser/wait_for_dom_observer.h"
#include "url/gurl.h"
namespace autofill_assistant {
// A delegate which provides the ScriptExecutor with methods to control the
// Autofill Assistant UI.
-class ScriptExecutorUiDelegate {
+class ScriptExecutorUiDelegate : public WaitForDomObserver {
public:
virtual void SetStatusMessage(const std::string& message) = 0;
virtual std::string GetStatusMessage() const = 0;
@@ -41,6 +44,8 @@ class ScriptExecutorUiDelegate {
virtual void ClearInfoBox() = 0;
virtual void SetCollectUserDataOptions(
CollectUserDataOptions* collect_user_data_options) = 0;
+ virtual void SetCollectUserDataUiState(bool loading,
+ UserDataEventField event_field) = 0;
virtual void SetLastSuccessfulUserDataOptions(
std::unique_ptr<CollectUserDataOptions> collect_user_data_options) = 0;
virtual const CollectUserDataOptions* GetLastSuccessfulUserDataOptions()
@@ -87,6 +92,17 @@ class ScriptExecutorUiDelegate {
// Clears the persistent generic UI.
virtual void ClearPersistentGenericUi() = 0;
+ // Whether this supports external actions.
+ virtual bool SupportsExternalActions() = 0;
+
+ // Executes the external action.
+ virtual void ExecuteExternalAction(
+ const external::Action& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) = 0;
+
protected:
virtual ~ScriptExecutorUiDelegate() {}
};
diff --git a/chromium/components/autofill_assistant/browser/script_executor_unittest.cc b/chromium/components/autofill_assistant/browser/script_executor_unittest.cc
index 7a0e2bd668d..469b2dcd280 100644
--- a/chromium/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -214,7 +214,7 @@ TEST_F(ScriptExecutorTest, ForwardParameters) {
{{"additional_param", "additional_param_value"},
{"param", "value"}})));
- std::move(callback).Run(net::HTTP_OK, "",
+ std::move(callback).Run(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{});
});
@@ -235,7 +235,7 @@ TEST_F(ScriptExecutorTest, RunOneActionReportAndReturn) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, true),
@@ -269,7 +269,7 @@ TEST_F(ScriptExecutorTest, RunMultipleActions) {
ServiceRequestSender::ResponseInfo{})))
.WillOnce(
DoAll(SaveArg<3>(&processed_actions2_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -307,7 +307,7 @@ TEST_F(ScriptExecutorTest, ShowsSlowConnectionWarningReplace) {
Delay(&task_environment_, 600),
RunOnceCallback<6>(net::HTTP_OK, Serialize(next_actions_response),
ServiceRequestSender::ResponseInfo{})))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -342,7 +342,7 @@ TEST_F(ScriptExecutorTest, ShowsSlowConnectionWarningConcatenate) {
Delay(&task_environment_, 600),
RunOnceCallback<6>(net::HTTP_OK, Serialize(next_actions_response),
ServiceRequestSender::ResponseInfo{})))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -379,7 +379,7 @@ TEST_F(ScriptExecutorTest, SlowConnectionWarningTriggersOnlyOnce) {
Delay(&task_environment_, 600),
RunOnceCallback<6>(net::HTTP_OK, Serialize(next_actions_response),
ServiceRequestSender::ResponseInfo{})))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -415,7 +415,7 @@ TEST_F(ScriptExecutorTest, SlowConnectionWarningTriggersMultipleTimes) {
Delay(&task_environment_, 600),
RunOnceCallback<6>(net::HTTP_OK, Serialize(next_actions_response),
ServiceRequestSender::ResponseInfo{})))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -450,7 +450,7 @@ TEST_F(ScriptExecutorTest, SlowConnectionWarningNotShowingIfNotConsecutive) {
Delay(&task_environment_, 600),
RunOnceCallback<6>(net::HTTP_OK, Serialize(initial_actions_response),
ServiceRequestSender::ResponseInfo{})))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -478,7 +478,7 @@ TEST_F(ScriptExecutorTest, SlowConnectionWarningNotShowingIfOnCompleted) {
ServiceRequestSender::ResponseInfo{}))
.WillOnce(
DoAll(Delay(&task_environment_, 600),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -532,7 +532,7 @@ TEST_F(ScriptExecutorTest, SlowConnectionWarningNotShownIfSlowWebsiteFirst) {
.WillOnce(DoAll(Delay(&task_environment_, 600),
RunOnceCallback<6>(net::HTTP_OK, Serialize(tell3),
ServiceRequestSender::ResponseInfo{})))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -684,7 +684,7 @@ TEST_F(ScriptExecutorTest, SlowWebsiteWarningNotShownIfSlowConnectionFirst) {
.WillOnce(DoAll(Delay(&task_environment_, 600),
RunOnceCallback<6>(net::HTTP_OK, Serialize(tell3),
ServiceRequestSender::ResponseInfo{})))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -745,7 +745,7 @@ TEST_F(ScriptExecutorTest, SlowWarningsBothShownIfConfigured) {
.WillOnce(DoAll(Delay(&task_environment_, 600),
RunOnceCallback<6>(net::HTTP_OK, Serialize(tell3),
ServiceRequestSender::ResponseInfo{})))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -771,7 +771,7 @@ TEST_F(ScriptExecutorTest, UnsupportedAction) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -790,7 +790,7 @@ TEST_F(ScriptExecutorTest, StopAfterEnd) {
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(mock_service_, GetNextActions)
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, true),
@@ -812,7 +812,7 @@ TEST_F(ScriptExecutorTest, StopClearsUnexecutedActions) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, true),
@@ -825,6 +825,47 @@ TEST_F(ScriptExecutorTest, StopClearsUnexecutedActions) {
EXPECT_EQ(processed_actions_capture[0].action(), actions_response.actions(0));
}
+TEST_F(ScriptExecutorTest, StopActionGetsExecutedAfterEmptyResponse) {
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_stop();
+
+ EXPECT_CALL(mock_service_, GetActions)
+ .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response),
+ ServiceRequestSender::ResponseInfo{}));
+
+ ActionsResponseProto second_actions_response;
+ second_actions_response.add_actions()->mutable_tell()->set_message(
+ "tell message");
+ std::vector<ProcessedActionProto> second_response_processed_actions_capture;
+ std::vector<ProcessedActionProto> third_response_processed_actions_capture;
+ EXPECT_CALL(mock_service_, GetNextActions)
+ // Second response.
+ .WillOnce(DoAll(
+ SaveArg<3>(&second_response_processed_actions_capture),
+ RunOnceCallback<6>(net::HTTP_OK, Serialize(second_actions_response),
+ ServiceRequestSender::ResponseInfo{})))
+ // Third response - empty. We only expect the execution to stop after this
+ // response.
+ .WillOnce(
+ DoAll(SaveArg<3>(&third_response_processed_actions_capture),
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
+ ServiceRequestSender::ResponseInfo{})));
+ EXPECT_CALL(executor_callback_,
+ Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+ Field(&ScriptExecutor::Result::at_end,
+ ScriptExecutor::SHUTDOWN))));
+ executor_->Run(&user_data_, executor_callback_.Get());
+
+ // We expect the actions from the second response to have been executed.
+ EXPECT_EQ(ui_delegate_.GetStatusMessage(), "tell message");
+ ASSERT_EQ(second_response_processed_actions_capture.size(), 1u);
+ EXPECT_EQ(second_response_processed_actions_capture[0].action(),
+ actions_response.actions(0));
+ ASSERT_EQ(third_response_processed_actions_capture.size(), 1u);
+ EXPECT_EQ(third_response_processed_actions_capture[0].action(),
+ second_actions_response.actions(0));
+}
+
TEST_F(ScriptExecutorTest, InterruptActionListOnError) {
ActionsResponseProto initial_actions_response;
initial_actions_response.add_actions()->mutable_tell()->set_message(
@@ -850,7 +891,7 @@ TEST_F(ScriptExecutorTest, InterruptActionListOnError) {
ServiceRequestSender::ResponseInfo{})))
.WillOnce(
DoAll(SaveArg<3>(&processed_actions2_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -881,7 +922,7 @@ TEST_F(ScriptExecutorTest, RunDelayedAction) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// executor_callback_.Run() not expected to be run just yet, as the action is
@@ -908,7 +949,7 @@ TEST_F(ScriptExecutorTest, ClearDetailsWhenFinished) {
.WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response),
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(mock_service_, GetNextActions)
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -931,7 +972,7 @@ TEST_F(ScriptExecutorTest, DontClearDetailsIfOtherActionsAreLeft) {
.WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response),
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(mock_service_, GetNextActions)
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
@@ -1023,7 +1064,7 @@ TEST_F(ScriptExecutorTest, WaitForDomWaitUntil) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// First check does not find the element, wait for dom waits 1s, then the
@@ -1056,11 +1097,11 @@ TEST_F(ScriptExecutorTest, RunInterrupt) {
std::vector<ProcessedActionProto> processed_actions2_capture;
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(DoAll(SaveArg<3>(&processed_actions1_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})))
.WillOnce(
DoAll(SaveArg<3>(&processed_actions2_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
@@ -1093,15 +1134,15 @@ TEST_F(ScriptExecutorTest, RunMultipleInterruptInOrder) {
testing::InSequence seq;
EXPECT_CALL(mock_service_,
GetNextActions(_, _, "payload for interrupt1", _, _, _, _))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(mock_service_,
GetNextActions(_, _, "payload for interrupt2", _, _, _, _))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(mock_service_,
GetNextActions(_, _, "main script payload", _, _, _, _))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
}
@@ -1135,7 +1176,7 @@ TEST_F(ScriptExecutorTest, RunSameInterruptMultipleTimes) {
// All scripts succeed with no more actions.
EXPECT_CALL(mock_service_, GetNextActions)
- .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
@@ -1222,7 +1263,7 @@ TEST_F(ScriptExecutorTest, DoNotRunInterruptIfPreconditionsDontMatch) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
@@ -1256,7 +1297,7 @@ TEST_F(ScriptExecutorTest, DoNotRunInterruptIfNotInterruptible) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
@@ -1294,6 +1335,8 @@ TEST_F(ScriptExecutorTest, InterruptFailsMainScript) {
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, false)));
executor_->Run(&user_data_, executor_callback_.Get());
+ EXPECT_THAT(ui_delegate_.GetInterruptNotificationHistory(),
+ ElementsAre(FakeScriptExecutorUiDelegate::INTERRUPT_STARTED));
}
TEST_F(ScriptExecutorTest, InterruptReturnsShutdown) {
@@ -1315,7 +1358,7 @@ TEST_F(ScriptExecutorTest, InterruptReturnsShutdown) {
// action.
EXPECT_CALL(mock_service_, GetNextActions)
.Times(2)
- .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
@@ -1323,6 +1366,8 @@ TEST_F(ScriptExecutorTest, InterruptReturnsShutdown) {
Field(&ScriptExecutor::Result::at_end,
ScriptExecutor::SHUTDOWN))));
executor_->Run(&user_data_, executor_callback_.Get());
+ EXPECT_THAT(ui_delegate_.GetInterruptNotificationHistory(),
+ ElementsAre(FakeScriptExecutorUiDelegate::INTERRUPT_STARTED));
}
TEST_F(ScriptExecutorTest, RunInterruptDuringPrompt) {
@@ -1379,7 +1424,7 @@ TEST_F(ScriptExecutorTest, RunInterruptDuringPrompt) {
}));
EXPECT_CALL(mock_service_, GetNextActions)
- .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
@@ -1415,6 +1460,9 @@ TEST_F(ScriptExecutorTest, RunInterruptDuringPrompt) {
ElementAreaProto::default_instance(), interruptible_area,
ElementAreaProto::default_instance()));
EXPECT_EQ("done", ui_delegate_.GetStatusMessage());
+ EXPECT_THAT(ui_delegate_.GetInterruptNotificationHistory(),
+ ElementsAre(FakeScriptExecutorUiDelegate::INTERRUPT_STARTED,
+ FakeScriptExecutorUiDelegate::INTERRUPT_FINISHED));
}
TEST_F(ScriptExecutorTest, RunPromptInBrowseMode) {
@@ -1493,7 +1541,7 @@ TEST_F(ScriptExecutorTest, RunInterruptMultipleTimesDuringPrompt) {
}));
EXPECT_CALL(mock_service_, GetNextActions)
- .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
@@ -1509,6 +1557,11 @@ TEST_F(ScriptExecutorTest, RunInterruptMultipleTimesDuringPrompt) {
AutofillAssistantState::PROMPT, AutofillAssistantState::RUNNING,
AutofillAssistantState::PROMPT, AutofillAssistantState::RUNNING,
AutofillAssistantState::PROMPT, AutofillAssistantState::RUNNING));
+ EXPECT_THAT(ui_delegate_.GetInterruptNotificationHistory(),
+ ElementsAre(FakeScriptExecutorUiDelegate::INTERRUPT_STARTED,
+ FakeScriptExecutorUiDelegate::INTERRUPT_FINISHED,
+ FakeScriptExecutorUiDelegate::INTERRUPT_STARTED,
+ FakeScriptExecutorUiDelegate::INTERRUPT_FINISHED));
}
TEST_F(ScriptExecutorTest, UpdateScriptListGetNext) {
@@ -1535,7 +1588,7 @@ TEST_F(ScriptExecutorTest, UpdateScriptListGetNext) {
.WillOnce(RunOnceCallback<6>(net::HTTP_OK,
Serialize(next_actions_response),
ServiceRequestSender::ResponseInfo{}))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
@@ -1568,7 +1621,7 @@ TEST_F(ScriptExecutorTest, UpdateScriptListShouldNotifyMultipleTimes) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(RunOnceCallback<6>(net::HTTP_OK, Serialize(actions_response),
ServiceRequestSender::ResponseInfo{}))
- .WillOnce(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillOnce(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
@@ -1608,7 +1661,7 @@ TEST_F(ScriptExecutorTest, UpdateScriptListFromInterrupt) {
.Times(3)
.WillOnce(RunOnceCallback<6>(net::HTTP_OK, Serialize(interrupt_actions),
ServiceRequestSender::ResponseInfo{}))
- .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
@@ -1643,7 +1696,7 @@ TEST_F(ScriptExecutorTest, RestorePreInterruptStatusMessage) {
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(mock_service_, GetNextActions)
- .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
@@ -1667,7 +1720,7 @@ TEST_F(ScriptExecutorTest, KeepStatusMessageWhenNotInterrupted) {
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(mock_service_, GetNextActions)
- .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, "",
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{}));
EXPECT_CALL(executor_callback_,
@@ -1678,8 +1731,8 @@ TEST_F(ScriptExecutorTest, KeepStatusMessageWhenNotInterrupted) {
EXPECT_EQ("pre-interrupt status", ui_delegate_.GetStatusMessage());
}
-#if BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)
-// This test fails on Android ASAN: https://crbug.com/1315701
+#if defined(ADDRESS_SANITIZER)
+// This test fails on ASAN: https://crbug.com/1315701
#define MAYBE_PauseWaitForDomWhileNavigating \
DISABLED_PauseWaitForDomWhileNavigating
#else
@@ -1699,7 +1752,7 @@ TEST_F(ScriptExecutorTest, MAYBE_PauseWaitForDomWhileNavigating) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// First check does not find the element, wait for dom waits 1s.
@@ -1743,7 +1796,7 @@ TEST_F(ScriptExecutorTest, StartWaitForDomWhileNavigating) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// Navigation starts before WaitForDom starts. WaitForDom does not wait and
@@ -1783,11 +1836,11 @@ TEST_F(ScriptExecutorTest, NavigateWhileRunningInterrupt) {
std::vector<ProcessedActionProto> processed_actions2_capture;
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(DoAll(SaveArg<3>(&processed_actions1_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})))
.WillOnce(
DoAll(SaveArg<3>(&processed_actions2_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_, Run(_));
@@ -1809,7 +1862,7 @@ TEST_F(ScriptExecutorTest, ReportNavigationErrors) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
delegate_.UpdateNavigationState(/* navigating= */ false, /* error= */ true);
@@ -1836,7 +1889,7 @@ TEST_F(ScriptExecutorTest, ReportNavigationEnd) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// WaitForDom does NOT wait for navigation to end, it immediately checks for
@@ -1865,8 +1918,8 @@ TEST_F(ScriptExecutorTest, ReportNavigationEnd) {
EXPECT_TRUE(processed_actions_capture[0].navigation_info().ended());
}
-#if BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)
-// This test fails on Android ASAN: https://crbug.com/1315701
+#if defined(ADDRESS_SANITIZER)
+// This test fails on ASAN: https://crbug.com/1315701
#define MAYBE_ReportUnexpectedNavigationStart \
DISABLED_ReportUnexpectedNavigationStart
#else
@@ -1885,7 +1938,7 @@ TEST_F(ScriptExecutorTest, MAYBE_ReportUnexpectedNavigationStart) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// As the element doesn't exist, WaitForDom returns and waits for 1s.
@@ -1911,8 +1964,8 @@ TEST_F(ScriptExecutorTest, MAYBE_ReportUnexpectedNavigationStart) {
EXPECT_TRUE(processed_actions_capture[0].navigation_info().unexpected());
}
-#if BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)
-// This test fails on Android ASAN: https://crbug.com/1315701
+#if defined(ADDRESS_SANITIZER)
+// This test fails on ASAN: https://crbug.com/1315701
#define MAYBE_ReportExpectedNavigationStart \
DISABLED_ReportExpectedNavigationStart
#else
@@ -1932,7 +1985,7 @@ TEST_F(ScriptExecutorTest, MAYBE_ReportExpectedNavigationStart) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// As the element doesn't exist, WaitForDom returns and waits for 1s.
@@ -1970,7 +2023,7 @@ TEST_F(ScriptExecutorTest, WaitForNavigationWithoutExpectation) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// WaitForNavigation returns immediately
@@ -1993,7 +2046,7 @@ TEST_F(ScriptExecutorTest, ExpectNavigation) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// WaitForNavigation waits for navigation to start after expect_navigation
@@ -2021,7 +2074,7 @@ TEST_F(ScriptExecutorTest, MultipleWaitForNavigation) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// The first wait_for_navigation waits for the navigation to happen. After
@@ -2050,7 +2103,7 @@ TEST_F(ScriptExecutorTest, ExpectLaterNavigationIgnoringNavigationInProgress) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
delegate_.UpdateNavigationState(/* navigating= */ true, /* error= */ false);
@@ -2087,7 +2140,7 @@ TEST_F(ScriptExecutorTest, WaitForNavigationReportsError) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<3>(&processed_actions_capture),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
// WaitForNavigation waits for navigation to start after expect_navigation
@@ -2141,7 +2194,7 @@ TEST_F(ScriptExecutorTest, RoundtripTimingStats) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<4>(&timing_stats),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
executor_->Run(&user_data_, executor_callback_.Get());
EXPECT_TRUE(task_environment_.NextTaskIsDelayed());
@@ -2171,7 +2224,7 @@ TEST_F(ScriptExecutorTest, RoundtripNetworkStats) {
EXPECT_CALL(mock_service_, GetNextActions)
.WillOnce(
DoAll(SaveArg<5>(&captured_network_stats),
- RunOnceCallback<6>(net::HTTP_OK, "",
+ RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
ServiceRequestSender::ResponseInfo{})));
EXPECT_CALL(executor_callback_,
@@ -2209,5 +2262,135 @@ TEST_F(ScriptExecutorTest, ClearPersistentUiOnError) {
ASSERT_EQ(nullptr, ui_delegate_.GetPersistentGenericUi());
}
+TEST_F(ScriptExecutorTest, RequestUserData) {
+ EXPECT_CALL(mock_service_, GetUserData)
+ .WillOnce(RunOnceCallback<3>(net::HTTP_OK, std::string(),
+ ServiceRequestSender::ResponseInfo{}));
+
+ base::MockCallback<
+ base::OnceCallback<void(bool, const GetUserDataResponseProto&)>>
+ mock_callback;
+ EXPECT_CALL(mock_callback, Run(true, _));
+
+ executor_->RequestUserData(UserDataEventField::SHIPPING_EVENT,
+ CollectUserDataOptions(), mock_callback.Get());
+ EXPECT_THAT(delegate_.GetStateHistory(),
+ ElementsAre(AutofillAssistantState::RUNNING));
+ EXPECT_EQ(ui_delegate_.GetCollectUserDataUiLoadingField(),
+ UserDataEventField::SHIPPING_EVENT);
+}
+
+TEST_F(ScriptExecutorTest, CollectUserData) {
+ // Ui has been disabled while loading.
+ ui_delegate_.SetCollectUserDataUiState(/* loading= */ true,
+ UserDataEventField::SHIPPING_EVENT);
+ EXPECT_EQ(ui_delegate_.GetCollectUserDataUiLoadingField(),
+ UserDataEventField::SHIPPING_EVENT);
+
+ CollectUserDataOptions options;
+ executor_->CollectUserData(&options);
+
+ EXPECT_TRUE(options.confirm_callback);
+ EXPECT_TRUE(options.additional_actions_callback);
+ EXPECT_TRUE(options.terms_link_callback);
+ EXPECT_EQ(ui_delegate_.GetOptions(), &options);
+ EXPECT_EQ(ui_delegate_.GetCollectUserDataUiLoadingField(),
+ UserDataEventField::NONE);
+}
+
+TEST_F(ScriptExecutorTest, MustUseBackendData) {
+ delegate_.SetMustUseBackendData(true);
+ EXPECT_TRUE(executor_->MustUseBackendData());
+
+ delegate_.SetMustUseBackendData(false);
+ EXPECT_FALSE(executor_->MustUseBackendData());
+}
+
+TEST_F(ScriptExecutorTest, ExternalActionDoesNotApplyTouchableArea) {
+ ActionsResponseProto actions_response;
+ ElementAreaProto area = MakeElementAreaProto("#area");
+ *actions_response.add_actions()
+ ->mutable_set_touchable_area()
+ ->mutable_element_area() = area;
+ actions_response.add_actions()->mutable_external_action()->mutable_info();
+
+ EXPECT_CALL(mock_service_, GetActions)
+ .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response),
+ ServiceRequestSender::ResponseInfo{}));
+ EXPECT_CALL(mock_service_, GetNextActions)
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
+ ServiceRequestSender::ResponseInfo{}));
+
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+
+ executor_->Run(&user_data_, executor_callback_.Get());
+ // The touchable area was never applied.
+ EXPECT_THAT(delegate_.GetTouchableElementAreaHistory(), IsEmpty());
+ // The delegate never entered prompt.
+ EXPECT_THAT(delegate_.GetStateHistory(), IsEmpty());
+}
+
+TEST_F(ScriptExecutorTest, ExternalActionDoesNotConsumeTouchableArea) {
+ ActionsResponseProto actions_response;
+ ElementAreaProto area = MakeElementAreaProto("#area");
+ *actions_response.add_actions()
+ ->mutable_set_touchable_area()
+ ->mutable_element_area() = area;
+ actions_response.add_actions()->mutable_external_action()->mutable_info();
+ auto* prompt_action = actions_response.add_actions()->mutable_prompt();
+ *prompt_action->add_choices()->mutable_auto_select_when()->mutable_match() =
+ ToSelectorProto("end_prompt");
+
+ EXPECT_CALL(mock_service_, GetActions)
+ .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response),
+ ServiceRequestSender::ResponseInfo{}));
+ EXPECT_CALL(mock_service_, GetNextActions)
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
+ ServiceRequestSender::ResponseInfo{}));
+
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+ EXPECT_CALL(mock_web_controller_, FindElement(Selector({"end_prompt"}), _, _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus(),
+ std::make_unique<ElementFinderResult>()));
+ executor_->Run(&user_data_, executor_callback_.Get());
+ // Since the ExternalAction did not consume the touchable area, the following
+ // prompt action was able to apply it.
+ EXPECT_THAT(delegate_.GetTouchableElementAreaHistory(),
+ ElementsAre(area, ElementAreaProto::default_instance()));
+}
+
+TEST_F(ScriptExecutorTest, ExternalActionAppliesAndRestoresTouchableArea) {
+ ActionsResponseProto actions_response;
+ ElementAreaProto area = MakeElementAreaProto("#area");
+ *actions_response.add_actions()
+ ->mutable_set_touchable_area()
+ ->mutable_element_area() = area;
+ auto* external_action =
+ actions_response.add_actions()->mutable_external_action();
+ external_action->mutable_info();
+ external_action->set_show_touchable_area(true);
+
+ EXPECT_CALL(mock_service_, GetActions)
+ .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response),
+ ServiceRequestSender::ResponseInfo{}));
+ EXPECT_CALL(mock_service_, GetNextActions)
+ .WillRepeatedly(RunOnceCallback<6>(net::HTTP_OK, /* response= */ "",
+ ServiceRequestSender::ResponseInfo{}));
+
+ EXPECT_CALL(executor_callback_,
+ Run(Field(&ScriptExecutor::Result::success, true)));
+
+ executor_->Run(&user_data_, executor_callback_.Get());
+ // The touchable area was applied at the start of the ExternalAction and
+ // restored at the end of it.
+ EXPECT_THAT(delegate_.GetTouchableElementAreaHistory(),
+ ElementsAre(area, ElementAreaProto::default_instance()));
+ EXPECT_THAT(delegate_.GetStateHistory(),
+ ElementsAre(AutofillAssistantState::PROMPT,
+ AutofillAssistantState::RUNNING));
+}
+
} // namespace
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/script_parameters.cc b/chromium/components/autofill_assistant/browser/script_parameters.cc
index 8c3d099c1da..99bab290794 100644
--- a/chromium/components/autofill_assistant/browser/script_parameters.cc
+++ b/chromium/components/autofill_assistant/browser/script_parameters.cc
@@ -107,6 +107,13 @@ const char kSourceParameterName[] = "SOURCE";
// Parameter to specify experiments.
const char kExperimentsParameterName[] = "EXPERIMENT_IDS";
+// Parameter to disable CUP RPC signing. Intended for internal use only.
+const char kDisableRpcSigningParamaterName[] = "DISABLE_RPC_SIGNING";
+
+// Parameter to send the annotate DOM model version. Should only be used if we
+// expect the model to be used.
+const char kSendAnnotateDomModelVersion[] = "SEND_ANNOTATE_DOM_MODEL_VERSION";
+
// The list of non sensitive script parameters that client requests are allowed
// to send to the backend i.e., they do not require explicit approval in the
// autofill-assistant onboarding. Even so, please always reach out to Chrome
@@ -277,6 +284,14 @@ std::vector<std::string> ScriptParameters::GetExperiments() const {
base::SplitResult::SPLIT_WANT_NONEMPTY);
}
+absl::optional<bool> ScriptParameters::GetDisableRpcSigning() const {
+ return GetTypedParameter<bool>(parameters_, kDisableRpcSigningParamaterName);
+}
+
+absl::optional<bool> ScriptParameters::GetSendAnnotateDomModelVersion() const {
+ return GetTypedParameter<bool>(parameters_, kSendAnnotateDomModelVersion);
+}
+
absl::optional<bool> ScriptParameters::GetDetailsShowInitial() const {
return GetTypedParameter<bool>(parameters_, kDetailsShowInitialParameterName);
}
diff --git a/chromium/components/autofill_assistant/browser/script_parameters.h b/chromium/components/autofill_assistant/browser/script_parameters.h
index a3f1c2d41e9..a87c2c7a829 100644
--- a/chromium/components/autofill_assistant/browser/script_parameters.h
+++ b/chromium/components/autofill_assistant/browser/script_parameters.h
@@ -65,6 +65,8 @@ class ScriptParameters {
absl::optional<int> GetCaller() const;
absl::optional<int> GetSource() const;
std::vector<std::string> GetExperiments() const;
+ absl::optional<bool> GetDisableRpcSigning() const;
+ absl::optional<bool> GetSendAnnotateDomModelVersion() const;
// Details parameters.
absl::optional<bool> GetDetailsShowInitial() const;
diff --git a/chromium/components/autofill_assistant/browser/script_parameters_unittest.cc b/chromium/components/autofill_assistant/browser/script_parameters_unittest.cc
index 570b0c66371..a335cd6e9a5 100644
--- a/chromium/components/autofill_assistant/browser/script_parameters_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/script_parameters_unittest.cc
@@ -108,6 +108,7 @@ TEST(ScriptParametersTest, SpecialScriptParameters) {
{"CALLER", "3"},
{"SOURCE", "4"},
{"EXPERIMENT_IDS", "123,456,789"},
+ {"DISABLE_RPC_SIGNING", "true"},
{"DETAILS_SHOW_INITIAL", "true"},
{"DETAILS_TITLE", "title"},
{"DETAILS_DESCRIPTION_LINE_1", "line1"},
@@ -134,6 +135,7 @@ TEST(ScriptParametersTest, SpecialScriptParameters) {
EXPECT_THAT(
parameters.GetExperiments(),
UnorderedElementsAreArray(std::vector<std::string>{"123", "456", "789"}));
+ EXPECT_THAT(parameters.GetDisableRpcSigning(), Eq(true));
EXPECT_THAT(parameters.GetDetailsShowInitial(), Eq(true));
EXPECT_THAT(parameters.GetDetailsTitle(), Eq("title"));
EXPECT_THAT(parameters.GetDetailsDescriptionLine1(), Eq("line1"));
diff --git a/chromium/components/autofill_assistant/browser/selector.cc b/chromium/components/autofill_assistant/browser/selector.cc
index d890c88829f..c02c6ddcab1 100644
--- a/chromium/components/autofill_assistant/browser/selector.cc
+++ b/chromium/components/autofill_assistant/browser/selector.cc
@@ -133,6 +133,7 @@ bool operator<(const SelectorProto::Filter& a, const SelectorProto::Filter& b) {
case SelectorProto::Filter::kEnterFrame:
case SelectorProto::Filter::kLabelled:
+ case SelectorProto::Filter::kParent:
return false;
case SelectorProto::Filter::kMatchCssSelector:
@@ -391,6 +392,10 @@ std::ostream& operator<<(std::ostream& out, const SelectorProto::Filter& f) {
out << f.property();
return out;
+ case SelectorProto::Filter::kParent:
+ out << "parent";
+ return out;
+
case SelectorProto::Filter::FILTER_NOT_SET:
// Either unset or set to an unsupported value. Let's assume the worse.
out << "INVALID";
diff --git a/chromium/components/autofill_assistant/browser/service.proto b/chromium/components/autofill_assistant/browser/service.proto
index c96b184deb8..93abc97df67 100644
--- a/chromium/components/autofill_assistant/browser/service.proto
+++ b/chromium/components/autofill_assistant/browser/service.proto
@@ -18,6 +18,7 @@ import "browser/generic_ui.proto";
import "browser/model.proto";
import "browser/view_layout.proto";
import "content/common/proto/semantic_feature_overrides.proto";
+import "browser/public/external_action.proto";
// A field trial containing the name of the trial and the name of the
// randomly selected trial group.
@@ -27,7 +28,7 @@ message FieldTrialProto {
}
// Context contains client environment details.
-// Next ID: 20
+// Next ID: 24
message ClientContextProto {
message Chrome {
optional string chrome_version = 1;
@@ -126,7 +127,26 @@ message ClientContextProto {
}
optional ScreenOrientation screen_orientation = 16;
- reserved 19;
+ // The type of platform the device is running on, e.g. Android, Desktop, or
+ // iOS.
+ enum PlatformType {
+ PLATFORM_TYPE_UNDEFINED = 0;
+ PLATFORM_TYPE_ANDROID = 1;
+ PLATFORM_TYPE_IOS = 2;
+ PLATFORM_TYPE_DESKTOP = 3;
+ }
+ optional PlatformType platform_type = 20;
+
+ // The model for semantic dom annotation.
+ message AnnotateDomModelContextProto {
+ optional int64 model_version = 1;
+ }
+ optional AnnotateDomModelContextProto annotate_dom_model_context = 22;
+
+ // Set if the client has the JS flow library needed for JS flow execution.
+ optional bool js_flow_library_loaded = 23;
+
+ reserved 19, 21;
}
// Get the list of scripts that can potentially be run on a url.
@@ -230,6 +250,12 @@ message GetUserDataRequestProto {
optional bytes client_token = 1;
// The list of supported card networks.
repeated string supported_card_networks = 2;
+ // The list of known payment instruments from a previous call.
+ repeated string preexisting_ids = 3;
+ }
+ message AddressRequest {
+ // The list of known addresses from a previous call.
+ repeated string preexisting_ids = 1;
}
// For logging, to know which run this request has originated from.
@@ -238,10 +264,10 @@ message GetUserDataRequestProto {
optional bool request_name = 2;
optional bool request_email = 3;
optional bool request_phone = 4;
- optional bool request_addresses = 5;
+ optional AddressRequest request_shipping_addresses = 8;
optional PaymentMethodRequest request_payment_methods = 7;
- reserved 6;
+ reserved 5, 6;
}
// Response with user data.
@@ -307,7 +333,7 @@ message OverlayImageProto {
optional ClientDimensionProto text_size = 7;
}
-// Next ID: 25
+// Next ID: 26
message ClientSettingsProto {
message IntegrationTestSettings {
// Disables animations for the poodle and the progress bar.
@@ -488,6 +514,10 @@ message ClientSettingsProto {
// wrong and fails with a |TIMED_OUT| error.
optional int32 selector_observer_extra_timeout_ms = 24;
+ // SelectorObserver will wait until no DOM mutation notifications are received
+ // for this amount of time to check the selectors.
+ optional int32 selector_observer_debounce_interval_ms = 25;
+
reserved 8 to 11;
}
@@ -721,6 +751,11 @@ message ActionsResponseProto {
message UpdateScriptListProto { repeated SupportedScriptProto scripts = 1; }
optional UpdateScriptListProto update_script_list = 5;
+ // Needs to be evaluated before each JS flow action. Contains function
+ // definitions that js flows might call.
+ // Only set if ClientContextProto::js_flow_library_loaded is false.
+ optional string js_flow_library = 13;
+
// Id of the current run.
optional uint64 run_id = 12;
@@ -1024,6 +1059,9 @@ message ActionProto {
UpdateClientSettingsProto update_client_settings = 89;
JsFlowProto js_flow = 92;
ExecuteJsProto execute_js = 93;
+ RegisterPasswordResetRequestProto register_password_reset_request = 94;
+ ExternalActionProto external_action = 95;
+ SetNativeValueProto set_native_value = 96;
}
// Set to true to make the client remove any contextual information if the
@@ -1125,6 +1163,8 @@ message ProcessedActionProto {
JsFlowProto.Result js_flow_result = 37;
// Should be set as a result of SaveSubmittedPassword.
SaveSubmittedPasswordProto.Result save_submitted_password_result = 38;
+ // Should be set as a result of an ExternalAction.
+ ExternalActionProto.Result external_action_result = 39;
}
// Reports information about navigation that happened while
@@ -1211,6 +1251,18 @@ message UnexpectedErrorInfoProto {
// JavaScript exception class name, if reporting a JavaScript error.
optional string js_exception_classname = 3;
+ enum JsExceptionLocation {
+ UNKNOWN = 0;
+ // Corresponds to ActionsResponseProto::js_flow_library
+ JS_FLOW_LIBRARY = 1;
+ // Corresponds to JsFlowProto::js_flow
+ JS_FLOW = 2;
+ }
+
+ // The location that caused the JavaScript exception. Guaranteed to have the
+ // same number of entries as js_exception_line_numbers and
+ // js_exception_column_numbers.
+ repeated JsExceptionLocation js_exception_locations = 8 [packed = true];
// JavaScript exception line numbers, within the js snippet that was sent to
// devtools runtime by the client, if reporting a JavaScript error. Together
// with |js_exception_column_numbers| this forms a stack trace.
@@ -1596,6 +1648,9 @@ message SelectorProto {
//
// This filter replaces |inner_text| and |value|.
PropertyFilter property = 14;
+
+ // Retrieve parent of current elements.
+ EmptyFilter parent = 15;
}
reserved 10;
@@ -2633,7 +2688,14 @@ message CollectUserDataProto {
}
// Specifies information about the data source to be used.
- message DataSource {}
+ message DataSource {
+ // If enabled and the user data request fails, fall back to the Chrome
+ // Autofill data instead where possible. E.g. in WebLayer this setting is
+ // ignored (Chrome Autofill data is not available). If this is false, the
+ // action will fail if the user data request fails, even if Chrome Autofill
+ // data would have been available.
+ optional bool allow_fallback = 1 [default = true];
+ }
optional string prompt = 1;
// NOTE: The action does not ask separately for billing address.
@@ -2924,6 +2986,11 @@ message SaveSubmittedPasswordProto {
}
}
+// Notifies the client that a password reset has been requested. This is
+// relevant for analyzing the outcome of password change flows. The action
+// fails if login details are not saved in the client's |UserData|.
+message RegisterPasswordResetRequestProto {}
+
// Configures the UI of the autofill assistant client.
message ConfigureUiStateProto {
enum OverlayBehavior {
@@ -3381,6 +3448,39 @@ message JsFlowProto {
// field will be empty and the action will return INVALID_ACTION.
optional string result_json = 1;
}
- // The JS flow to execute in a sandbox.
+
+ // The JS flow to execute in a sandbox. If present
+ // ActionsResponseProto::js_flow_library needs to be evaluated first.
optional string js_flow = 1;
}
+
+// Action which forwards a proto to the owner of the |ExternalScriptController|
+// for the current flow. Not supported for internal flows.
+message ExternalActionProto {
+ // The opaque proto to be forwarded to the owner of the
+ // |ExternalScriptController| for this flow.
+ optional external.ActionInfo info = 1;
+
+ // Whether to show the touchable area in the overlay.
+ // If true, it will consume the current touchable area.
+ optional bool show_touchable_area = 2;
+
+ // Whether interrupts should be executed while Autofill Assistant is in prompt
+ // state.
+ optional bool allow_interrupt = 3;
+
+ // A condition on the DOM. Whenever its status changes a notification is sent
+ // to the external caller.
+ message ExternalCondition {
+ // The identifier to be used in the external notification.
+ optional int32 id = 1;
+
+ // The condition to be checked internally.
+ optional ElementConditionProto element_condition = 2;
+ }
+ repeated ExternalCondition conditions = 4;
+
+ message Result {
+ optional external.ResultInfo result_info = 1;
+ }
+}
diff --git a/chromium/components/autofill_assistant/browser/service/java_service.cc b/chromium/components/autofill_assistant/browser/service/java_service.cc
index 754cd68e88f..46eb329fda3 100644
--- a/chromium/components/autofill_assistant/browser/service/java_service.cc
+++ b/chromium/components/autofill_assistant/browser/service/java_service.cc
@@ -94,6 +94,7 @@ void JavaService::GetNextActions(
void JavaService::GetUserData(const CollectUserDataOptions& options,
uint64_t run_id,
+ const UserData* user_data,
ServiceRequestSender::ResponseCallback callback) {
JNIEnv* env = base::android::AttachCurrentThread();
auto jresponse =
diff --git a/chromium/components/autofill_assistant/browser/service/java_service.h b/chromium/components/autofill_assistant/browser/service/java_service.h
index 471859f40e8..3828fba118d 100644
--- a/chromium/components/autofill_assistant/browser/service/java_service.h
+++ b/chromium/components/autofill_assistant/browser/service/java_service.h
@@ -60,6 +60,7 @@ class JavaService : public Service {
// Get user data.
void GetUserData(const CollectUserDataOptions& options,
uint64_t run_id,
+ const UserData* user_data,
ServiceRequestSender::ResponseCallback callback) override;
private:
diff --git a/chromium/components/autofill_assistant/browser/service/java_service_request_sender.cc b/chromium/components/autofill_assistant/browser/service/java_service_request_sender.cc
index 8f841ffe132..f2cc520e396 100644
--- a/chromium/components/autofill_assistant/browser/service/java_service_request_sender.cc
+++ b/chromium/components/autofill_assistant/browser/service/java_service_request_sender.cc
@@ -53,4 +53,6 @@ void JavaServiceRequestSender::OnResponse(
std::move(callback_).Run(http_status, response, ResponseInfo{});
}
+void JavaServiceRequestSender::SetDisableRpcSigning(bool disable_rpc_signing) {}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/service/java_service_request_sender.h b/chromium/components/autofill_assistant/browser/service/java_service_request_sender.h
index 95889e536a7..603ca2b8571 100644
--- a/chromium/components/autofill_assistant/browser/service/java_service_request_sender.h
+++ b/chromium/components/autofill_assistant/browser/service/java_service_request_sender.h
@@ -39,6 +39,8 @@ class JavaServiceRequestSender : public ServiceRequestSender {
jint http_status,
const base::android::JavaParamRef<jbyteArray>& jresponse);
+ void SetDisableRpcSigning(bool disable_rpc_signing) override;
+
private:
ResponseCallback callback_;
base::android::ScopedJavaGlobalRef<jobject> jservice_request_sender_;
diff --git a/chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.cc b/chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.cc
index b4020f97bc2..7d04fcaed5a 100644
--- a/chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.cc
+++ b/chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.cc
@@ -125,8 +125,9 @@ void JavaTestEndpointService::GetNextActions(
void JavaTestEndpointService::GetUserData(
const CollectUserDataOptions& options,
uint64_t run_id,
+ const UserData* user_data,
ServiceRequestSender::ResponseCallback callback) {
- service_impl_->GetUserData(options, run_id, std::move(callback));
+ service_impl_->GetUserData(options, run_id, user_data, std::move(callback));
}
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.h b/chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.h
index 3e05aa34996..7ec5da033db 100644
--- a/chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.h
+++ b/chromium/components/autofill_assistant/browser/service/java_test_endpoint_service.h
@@ -49,6 +49,7 @@ class JavaTestEndpointService : public Service {
void GetUserData(const CollectUserDataOptions& options,
uint64_t run_id,
+ const UserData* user_data,
ServiceRequestSender::ResponseCallback callback) override;
private:
diff --git a/chromium/components/autofill_assistant/browser/service/mock_service.h b/chromium/components/autofill_assistant/browser/service/mock_service.h
index cf6deedf466..02d06509b67 100644
--- a/chromium/components/autofill_assistant/browser/service/mock_service.h
+++ b/chromium/components/autofill_assistant/browser/service/mock_service.h
@@ -9,6 +9,7 @@
#include <vector>
#include "components/autofill_assistant/browser/service/service.h"
+#include "components/autofill_assistant/browser/user_data.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
@@ -51,8 +52,21 @@ class MockService : public Service {
GetUserData,
(const CollectUserDataOptions& options,
uint64_t run_id,
+ const UserData* user_data,
ServiceRequestSender::ResponseCallback callback),
(override));
+ MOCK_METHOD(void,
+ SetDisableRpcSigning,
+ (bool disable_rpc_signing),
+ (override));
+ MOCK_METHOD(void,
+ UpdateAnnotateDomModelContext,
+ (int64_t model_version),
+ (override));
+ MOCK_METHOD(void,
+ UpdateJsFlowLibraryLoaded,
+ (bool js_flow_library_loaded),
+ (override));
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/service/mock_service_request_sender.h b/chromium/components/autofill_assistant/browser/service/mock_service_request_sender.h
index 07b4eb670b8..cf800cbc65b 100644
--- a/chromium/components/autofill_assistant/browser/service/mock_service_request_sender.h
+++ b/chromium/components/autofill_assistant/browser/service/mock_service_request_sender.h
@@ -31,6 +31,11 @@ class MockServiceRequestSender : public ServiceRequestSender {
const std::string& request_body,
ResponseCallback& callback,
RpcType rpc_type));
+
+ MOCK_METHOD(void,
+ SetDisableRpcSigning,
+ (bool disable_rpc_signing),
+ (override));
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/service/service.h b/chromium/components/autofill_assistant/browser/service/service.h
index d2bf64ee2cc..c879077e095 100644
--- a/chromium/components/autofill_assistant/browser/service/service.h
+++ b/chromium/components/autofill_assistant/browser/service/service.h
@@ -55,8 +55,15 @@ class Service {
// Get user data.
virtual void GetUserData(const CollectUserDataOptions& options,
uint64_t run_id,
+ const UserData* user_data,
ServiceRequestSender::ResponseCallback callback) = 0;
+ virtual void SetDisableRpcSigning(bool disable_rpc_signing) {}
+
+ virtual void UpdateAnnotateDomModelContext(int64_t model_version) {}
+
+ virtual void UpdateJsFlowLibraryLoaded(bool js_flow_library_loaded){};
+
protected:
Service() = default;
};
diff --git a/chromium/components/autofill_assistant/browser/service/service_impl.cc b/chromium/components/autofill_assistant/browser/service/service_impl.cc
index 323405d3190..00f11d6f8b1 100644
--- a/chromium/components/autofill_assistant/browser/service/service_impl.cc
+++ b/chromium/components/autofill_assistant/browser/service/service_impl.cc
@@ -144,7 +144,24 @@ void ServiceImpl::GetNextActions(
void ServiceImpl::GetUserData(const CollectUserDataOptions& options,
uint64_t run_id,
+ const UserData* user_data,
ServiceRequestSender::ResponseCallback callback) {
+ std::vector<std::string> preexisting_address_ids;
+ std::vector<std::string> preexisting_payment_instrument_ids;
+ if (user_data) {
+ for (const auto& address : user_data->available_addresses_) {
+ if (address->identifier.has_value()) {
+ preexisting_address_ids.emplace_back(*address->identifier);
+ }
+ }
+ for (const auto& instrument : user_data->available_payment_instruments_) {
+ if (instrument->identifier.has_value()) {
+ preexisting_payment_instrument_ids.emplace_back(
+ *instrument->identifier);
+ }
+ }
+ }
+
if (options.request_payment_method) {
// We do not cache the payments client token. It could go stale (in practice
// it currently doesn't). Getting the token is little overhead.
@@ -152,17 +169,18 @@ void ServiceImpl::GetUserData(const CollectUserDataOptions& options,
&ServiceImpl::SendUserDataRequest, weak_ptr_factory_.GetWeakPtr(),
run_id, options.request_payer_name, options.request_payer_email,
options.request_payer_phone || options.request_phone_number_separately,
- options.request_shipping, options.request_payment_method,
- options.supported_basic_card_networks, std::move(callback)));
+ options.request_shipping, preexisting_address_ids,
+ options.request_payment_method, options.supported_basic_card_networks,
+ preexisting_payment_instrument_ids, std::move(callback)));
return;
}
SendUserDataRequest(
run_id, options.request_payer_name, options.request_payer_email,
options.request_payer_phone || options.request_phone_number_separately,
- options.request_shipping, options.request_payment_method,
- options.supported_basic_card_networks, std::move(callback),
- std::string());
+ options.request_shipping, preexisting_address_ids,
+ options.request_payment_method, options.supported_basic_card_networks,
+ preexisting_payment_instrument_ids, std::move(callback), std::string());
}
void ServiceImpl::SendUserDataRequest(
@@ -171,17 +189,33 @@ void ServiceImpl::SendUserDataRequest(
bool request_email,
bool request_phone,
bool request_shipping,
+ const std::vector<std::string>& preexisting_address_ids,
bool request_payment_methods,
const std::vector<std::string>& supported_card_networks,
+ const std::vector<std::string>& preexisting_payment_instrument_ids,
ServiceRequestSender::ResponseCallback callback,
const std::string& client_token) {
request_sender_->SendRequest(
user_data_url_,
ProtocolUtils::CreateGetUserDataRequest(
run_id, request_name, request_email, request_phone, request_shipping,
- request_payment_methods, supported_card_networks, client_token),
+ preexisting_address_ids, request_payment_methods,
+ supported_card_networks, preexisting_payment_instrument_ids,
+ client_token),
ServiceRequestSender::AuthMode::OAUTH_STRICT, std::move(callback),
RpcType::GET_USER_DATA);
}
+void ServiceImpl::SetDisableRpcSigning(bool disable_rpc_signing) {
+ request_sender_->SetDisableRpcSigning(disable_rpc_signing);
+}
+
+void ServiceImpl::UpdateAnnotateDomModelContext(int64_t model_version) {
+ client_context_->UpdateAnnotateDomModelContext(model_version);
+}
+
+void ServiceImpl::UpdateJsFlowLibraryLoaded(const bool js_flow_library_loaded) {
+ client_context_->UpdateJsFlowLibraryLoaded(js_flow_library_loaded);
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/service/service_impl.h b/chromium/components/autofill_assistant/browser/service/service_impl.h
index 8eab9725a26..aaadc0b67c2 100644
--- a/chromium/components/autofill_assistant/browser/service/service_impl.h
+++ b/chromium/components/autofill_assistant/browser/service/service_impl.h
@@ -10,6 +10,7 @@
#include <vector>
#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/client_context.h"
#include "components/autofill_assistant/browser/device_context.h"
@@ -18,6 +19,7 @@
#include "components/autofill_assistant/browser/service/server_url_fetcher.h"
#include "components/autofill_assistant/browser/service/service.h"
#include "components/autofill_assistant/browser/service/service_request_sender.h"
+#include "components/autofill_assistant/browser/user_data.h"
#include "components/signin/public/identity_manager/access_token_fetcher.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "services/network/public/cpp/simple_url_loader.h"
@@ -87,8 +89,15 @@ class ServiceImpl : public Service {
void GetUserData(const CollectUserDataOptions& options,
uint64_t run_id,
+ const UserData* user_data,
ServiceRequestSender::ResponseCallback callback) override;
+ void SetDisableRpcSigning(bool disable_rpc_signing) override;
+
+ void UpdateAnnotateDomModelContext(int64_t model_version) override;
+
+ void UpdateJsFlowLibraryLoaded(bool js_flow_library_loaded) override;
+
private:
void SendUserDataRequest(
uint64_t run_id,
@@ -96,12 +105,14 @@ class ServiceImpl : public Service {
bool request_email,
bool request_phone,
bool request_shipping,
+ const std::vector<std::string>& preexisting_address_ids,
bool request_payment_methods,
const std::vector<std::string>& supported_card_networks,
+ const std::vector<std::string>& preexisting_payment_instrument_ids,
ServiceRequestSender::ResponseCallback callback,
const std::string& client_token);
- Client* const client_;
+ const raw_ptr<Client> client_;
// The request sender responsible for communicating with a remote endpoint.
std::unique_ptr<ServiceRequestSender> request_sender_;
diff --git a/chromium/components/autofill_assistant/browser/service/service_impl_unittest.cc b/chromium/components/autofill_assistant/browser/service/service_impl_unittest.cc
index b253073c812..860db4aa160 100644
--- a/chromium/components/autofill_assistant/browser/service/service_impl_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/service/service_impl_unittest.cc
@@ -40,8 +40,12 @@ std::string ExpectedGetUserDataRequestBody(uint64_t run_id,
return ProtocolUtils::CreateGetUserDataRequest(
run_id, /* request_name= */ false, /* request_email= */ false,
/* request_phone= */ false,
- /* request_shipping= */ false, request_payment_methods,
- /* supported_card_networks= */ {}, client_token);
+ /* request_shipping= */ false,
+ /* preexisting_address_ids= */ std::vector<std::string>(),
+ request_payment_methods,
+ /* supported_card_networks= */ std::vector<std::string>(),
+ /* preexisting_payment_instrument_ids= */ std::vector<std::string>(),
+ client_token);
}
class ServiceImplTest : public testing::Test {
@@ -226,7 +230,8 @@ TEST_F(ServiceImplTest, GetUserDataWithPayments) {
EXPECT_CALL(mock_response_callback_,
Run(net::HTTP_OK, std::string("response"), _));
- service_->GetUserData(options, run_id, mock_response_callback_.Get());
+ service_->GetUserData(options, run_id, /* user_data= */ nullptr,
+ mock_response_callback_.Get());
}
TEST_F(ServiceImplTest, GetUserDataWithoutPayments) {
@@ -237,7 +242,7 @@ TEST_F(ServiceImplTest, GetUserDataWithoutPayments) {
EXPECT_CALL(*mock_request_sender_,
OnSendRequest(GURL(kUserDataServerUrl),
ExpectedGetUserDataRequestBody(
- run_id, /* client_token= */ "",
+ run_id, /* client_token= */ std::string(),
options.request_payment_method),
_, RpcType::GET_USER_DATA))
.WillOnce(RunOnceCallback<2>(net::HTTP_OK, std::string("response"),
@@ -245,7 +250,18 @@ TEST_F(ServiceImplTest, GetUserDataWithoutPayments) {
EXPECT_CALL(mock_response_callback_,
Run(net::HTTP_OK, std::string("response"), _));
- service_->GetUserData(options, run_id, mock_response_callback_.Get());
+ service_->GetUserData(options, run_id, /* user_data= */ nullptr,
+ mock_response_callback_.Get());
+}
+
+TEST_F(ServiceImplTest, UpdateAnnotateDomModelService) {
+ EXPECT_CALL(*mock_client_context_, UpdateAnnotateDomModelContext(123456));
+ service_->UpdateAnnotateDomModelContext(123456);
+}
+
+TEST_F(ServiceImplTest, UpdateJsFlowLibraryLoaded) {
+ EXPECT_CALL(*mock_client_context_, UpdateJsFlowLibraryLoaded(true));
+ service_->UpdateJsFlowLibraryLoaded(true);
}
} // namespace
diff --git a/chromium/components/autofill_assistant/browser/service/service_request_sender.h b/chromium/components/autofill_assistant/browser/service/service_request_sender.h
index 4df58e823be..8825abf13c7 100644
--- a/chromium/components/autofill_assistant/browser/service/service_request_sender.h
+++ b/chromium/components/autofill_assistant/browser/service/service_request_sender.h
@@ -19,7 +19,7 @@ class ServiceRequestSender {
struct ResponseInfo {
// The number of bytes transmitted over the network, before decoding. Can be
// -1 in case of interrupted downloads.
- size_t encoded_body_length = 0;
+ int64_t encoded_body_length = 0;
};
using ResponseCallback =
@@ -47,6 +47,8 @@ class ServiceRequestSender {
AuthMode auth_mode,
ResponseCallback response_callback,
RpcType rpc_type) = 0;
+
+ virtual void SetDisableRpcSigning(bool disable_rpc_signing) = 0;
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/service/service_request_sender_impl.cc b/chromium/components/autofill_assistant/browser/service/service_request_sender_impl.cc
index 44b28e98196..866b0019741 100644
--- a/chromium/components/autofill_assistant/browser/service/service_request_sender_impl.cc
+++ b/chromium/components/autofill_assistant/browser/service/service_request_sender_impl.cc
@@ -49,6 +49,7 @@ constexpr int kMaxRetriesGetUserData = 2;
void OnURLLoaderComplete(
autofill_assistant::ServiceRequestSender::ResponseCallback callback,
std::unique_ptr<::network::SimpleURLLoader> loader,
+ int max_retries,
std::unique_ptr<std::string> response_body) {
std::string response_str;
if (response_body != nullptr) {
@@ -56,16 +57,24 @@ void OnURLLoaderComplete(
}
int response_code = 0;
+ if (loader->ResponseInfo() && loader->ResponseInfo()->headers) {
+ response_code = loader->ResponseInfo()->headers->response_code();
+ }
+
autofill_assistant::ServiceRequestSender::ResponseInfo response_info;
- if (loader->ResponseInfo()) {
+ if (loader->CompletionStatus().has_value()) {
response_info.encoded_body_length =
- loader->ResponseInfo()->encoded_body_length;
- if (loader->ResponseInfo()->headers) {
- response_code = loader->ResponseInfo()->headers->response_code();
- }
+ loader->CompletionStatus()->encoded_body_length;
}
+
VLOG(3) << "Received response: status=" << response_code << ", "
- << response_str.length() << " bytes";
+ << "encoded: " << response_info.encoded_body_length << " bytes, "
+ << "decoded: " << response_str.length() << " bytes";
+
+ if (max_retries > 0) {
+ autofill_assistant::Metrics::RecordServiceRequestRetryCount(
+ loader->GetNumRetries(), response_code == net::HTTP_OK);
+ }
std::move(callback).Run(response_code, response_str, response_info);
}
@@ -90,7 +99,8 @@ void SendRequestImpl(
if (max_retries > 0) {
loader->SetRetryOptions(
max_retries, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE |
- network::SimpleURLLoader::RETRY_ON_NAME_NOT_RESOLVED);
+ network::SimpleURLLoader::RETRY_ON_NAME_NOT_RESOLVED |
+ network::SimpleURLLoader::RETRY_ON_5XX);
}
loader->AttachStringForUpload(request_body, "application/x-protobuffer");
#ifndef NDEBUG
@@ -102,7 +112,7 @@ void SendRequestImpl(
->GetURLLoaderFactoryForBrowserProcess()
.get(),
base::BindOnce(&OnURLLoaderComplete, std::move(callback),
- std::move(loader)));
+ std::move(loader), max_retries));
}
void SendRequestNoAuth(
@@ -206,7 +216,7 @@ void ServiceRequestSenderImpl::SendRequest(
max_retries = kMaxRetriesGetUserData;
}
- if (!cup::IsRpcTypeSupported(rpc_type)) {
+ if (!cup::IsRpcTypeSupported(rpc_type) || disable_rpc_signing_) {
InternalSendRequest(url, request_body, auth_mode, max_retries,
std::move(callback));
return;
@@ -340,4 +350,8 @@ bool ServiceRequestSenderImpl::OAuthEnabled(
!failed_to_fetch_oauth_token_);
}
+void ServiceRequestSenderImpl::SetDisableRpcSigning(bool disable_rpc_signing) {
+ disable_rpc_signing_ = disable_rpc_signing;
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/service/service_request_sender_impl.h b/chromium/components/autofill_assistant/browser/service/service_request_sender_impl.h
index 37827b61708..7bf23fe4698 100644
--- a/chromium/components/autofill_assistant/browser/service/service_request_sender_impl.h
+++ b/chromium/components/autofill_assistant/browser/service/service_request_sender_impl.h
@@ -56,6 +56,11 @@ class ServiceRequestSenderImpl : public ServiceRequestSender {
ResponseCallback callback,
RpcType rpc_type) override;
+ // Sets the value of the |disable_rpc_signing| field. If |true| CUP signing
+ // and verification will be bypassed even if it is enabled and the RPC type
+ // supported. Intended for internal use only.
+ void SetDisableRpcSigning(bool disable_rpc_signing) override;
+
private:
// Unlike |ServiceRequestSenderImpl::SendRequest|, assumes that any necessary
// CUP signing and validation is already done or accounted for in the
@@ -101,6 +106,9 @@ class ServiceRequestSenderImpl : public ServiceRequestSender {
// API key to add to the URL of unauthenticated requests.
std::string api_key_;
+ // Disable CUP RPC signing. Intended for internal use only.
+ bool disable_rpc_signing_ = false;
+
// Getting the OAuth token failed. For requests with auth mode allowing to
// fall back to API key, it will not be retried. For requests forcing auth,
// the OAuth token will tried to be re-fetched.
diff --git a/chromium/components/autofill_assistant/browser/service/service_request_sender_impl_unittest.cc b/chromium/components/autofill_assistant/browser/service/service_request_sender_impl_unittest.cc
index 401eede7912..c2a296f6596 100644
--- a/chromium/components/autofill_assistant/browser/service/service_request_sender_impl_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/service/service_request_sender_impl_unittest.cc
@@ -34,9 +34,12 @@
namespace autofill_assistant {
using ::base::test::RunOnceCallback;
+using ::network::URLLoaderCompletionStatus;
using ::testing::_;
+using ::testing::Field;
using ::testing::NiceMock;
using ::testing::Return;
+using ::testing::ReturnRef;
namespace {
@@ -66,6 +69,8 @@ class ServiceRequestSenderImplTest : public testing::Test {
content::TestBrowserContext context_;
NiceMock<MockAccessTokenFetcher> mock_access_token_fetcher_;
+ absl::optional<URLLoaderCompletionStatus> completion_status_ = absl::nullopt;
+
void InitCupFeatures(bool enableSigning, bool enableVerifying) {
std::vector<base::Feature> enabled_features;
std::vector<base::Feature> disabled_features;
@@ -115,6 +120,8 @@ TEST_F(ServiceRequestSenderImplTest, SendUnauthenticatedRequest) {
.WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
ServiceRequestSenderImpl request_sender{
@@ -151,6 +158,8 @@ TEST_F(ServiceRequestSenderImplTest, SendAuthenticatedRequest) {
.WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_access_token_fetcher_, OnFetchAccessToken)
.WillOnce(RunOnceCallback<0>(true, "access_token"));
EXPECT_CALL(mock_access_token_fetcher_, InvalidateAccessToken).Times(0);
@@ -187,7 +196,8 @@ TEST_F(ServiceRequestSenderImplTest, ForceAuthenticatedRequest) {
EXPECT_CALL(*loader,
SetRetryOptions(
2, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE |
- network::SimpleURLLoader::RETRY_ON_NAME_NOT_RESOLVED));
+ network::SimpleURLLoader::RETRY_ON_NAME_NOT_RESOLVED |
+ network::SimpleURLLoader::RETRY_ON_5XX));
EXPECT_CALL(*loader,
AttachStringForUpload(std::string("request"),
std::string("application/x-protobuffer")));
@@ -195,6 +205,8 @@ TEST_F(ServiceRequestSenderImplTest, ForceAuthenticatedRequest) {
.WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_access_token_fetcher_, OnFetchAccessToken)
.WillOnce(RunOnceCallback<0>(true, "access_token"));
EXPECT_CALL(mock_access_token_fetcher_, InvalidateAccessToken).Times(0);
@@ -238,6 +250,8 @@ TEST_F(ServiceRequestSenderImplTest,
auto response_info = CreateResponseInfo(net::HTTP_OK, "OK");
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
ServiceRequestSenderImpl request_sender{
@@ -278,6 +292,8 @@ TEST_F(ServiceRequestSenderImplTest,
auto response_info = CreateResponseInfo(net::HTTP_OK, "OK");
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
ServiceRequestSenderImpl request_sender{
@@ -304,6 +320,8 @@ TEST_F(ServiceRequestSenderImplTest,
EXPECT_CALL(*loader, DownloadToStringOfUnboundedSizeUntilCrashAndDie)
.Times(0);
EXPECT_CALL(*loader, ResponseInfo).Times(0);
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_access_token_fetcher_, OnFetchAccessToken)
.WillOnce(RunOnceCallback<0>(false, /*access_token = */ ""));
EXPECT_CALL(mock_access_token_fetcher_, InvalidateAccessToken).Times(0);
@@ -345,6 +363,8 @@ TEST_F(ServiceRequestSenderImplTest, SignsGetActionsRequestWhenFeatureEnabled) {
.WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
EXPECT_CALL(*cup_factory,
@@ -389,6 +409,8 @@ TEST_F(ServiceRequestSenderImplTest, ValidatesGetActionsResponsesWhenEnabled) {
RunOnceCallback<1>(std::make_unique<std::string>("packed_response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
EXPECT_CALL(*cup_factory,
@@ -429,6 +451,8 @@ TEST_F(ServiceRequestSenderImplTest, RecordsCupSigningDisabledEvent) {
.WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_access_token_fetcher_, OnFetchAccessToken)
.WillOnce(RunOnceCallback<0>(true, "access_token"));
@@ -466,6 +490,8 @@ TEST_F(ServiceRequestSenderImplTest, RecordsCupVerificationDisabledEvent) {
.WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
EXPECT_CALL(*cup_factory,
@@ -507,6 +533,8 @@ TEST_F(ServiceRequestSenderImplTest, RecordsHttpFailureEventWithCupEnabled) {
RunOnceCallback<1>(std::make_unique<std::string>("packed_response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_NOT_FOUND, "", _));
EXPECT_CALL(*cup_factory, CreateInstance).WillOnce([&]() {
return std::move(cup);
@@ -549,6 +577,8 @@ TEST_F(ServiceRequestSenderImplTest, RecordsHttpFailureEventWithCupDisabled) {
RunOnceCallback<1>(std::make_unique<std::string>("packed_response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_NOT_FOUND, "", _));
EXPECT_CALL(*cup_factory, CreateInstance).WillOnce([&]() {
return std::move(cup);
@@ -590,6 +620,8 @@ TEST_F(ServiceRequestSenderImplTest,
RunOnceCallback<1>(std::make_unique<std::string>("packed_response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
EXPECT_CALL(*cup_factory, CreateInstance).WillOnce([&]() {
return std::move(cup);
@@ -640,6 +672,8 @@ TEST_F(ServiceRequestSenderImplTest, DoesNotRecordCupEventForNonSupportedRpcs) {
.WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
EXPECT_CALL(*loader, ResponseInfo)
.WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
ServiceRequestSenderImpl request_sender{
@@ -663,6 +697,160 @@ TEST_F(ServiceRequestSenderImplTest, DoesNotRecordCupEventForNonSupportedRpcs) {
Metrics::CupRpcVerificationEvent::VERIFICATION_DISABLED, 0);
}
+TEST_F(ServiceRequestSenderImplTest,
+ DoesNotPerformCupSigningIfRpcSigningDisabled) {
+ InitCupFeatures(true, true);
+ auto loader_factory =
+ std::make_unique<NiceMock<MockSimpleURLLoaderFactory>>();
+ auto loader = std::make_unique<NiceMock<MockURLLoader>>();
+ auto response_info = CreateResponseInfo(net::HTTP_OK, "OK");
+ EXPECT_CALL(*loader_factory, OnCreateLoader)
+ .WillOnce([&](::network::ResourceRequest* resource_request,
+ const ::net::NetworkTrafficAnnotationTag& annotation_tag) {
+ return std::move(loader);
+ });
+ EXPECT_CALL(*loader, DownloadToStringOfUnboundedSizeUntilCrashAndDie)
+ .WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
+ EXPECT_CALL(*loader, ResponseInfo)
+ .WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
+ EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
+
+ auto cup_factory =
+ std::make_unique<NiceMock<autofill_assistant::cup::MockCUPFactory>>();
+ EXPECT_CALL(*cup_factory, CreateInstance).Times(0);
+
+ ServiceRequestSenderImpl request_sender{
+ &context_,
+ /* access_token_fetcher = */ nullptr, std::move(cup_factory),
+ std::move(loader_factory), std::string("fake_api_key")};
+ request_sender.SetDisableRpcSigning(true);
+ request_sender.SendRequest(
+ GURL("https://www.example.com"), std::string("request"),
+ ServiceRequestSender::AuthMode::API_KEY, mock_response_callback_.Get(),
+ autofill_assistant::RpcType::GET_ACTIONS);
+}
+
+TEST_F(ServiceRequestSenderImplTest, TestRetryLoggingForGetUserData) {
+ base::HistogramTester histogram_tester;
+ auto loader_factory =
+ std::make_unique<NiceMock<MockSimpleURLLoaderFactory>>();
+ auto loader = std::make_unique<NiceMock<MockURLLoader>>();
+ auto* loader_ptr = loader.get();
+ auto response_info = CreateResponseInfo(net::HTTP_OK, "OK");
+ EXPECT_CALL(*loader_factory, OnCreateLoader)
+ .WillOnce([&](::network::ResourceRequest* resource_request,
+ const ::net::NetworkTrafficAnnotationTag& annotation_tag) {
+ return std::move(loader);
+ });
+ EXPECT_CALL(*loader_ptr, DownloadToStringOfUnboundedSizeUntilCrashAndDie)
+ .WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
+ EXPECT_CALL(*loader_ptr, ResponseInfo)
+ .WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
+ EXPECT_CALL(*loader_ptr, GetNumRetries).WillOnce(Return(1));
+ EXPECT_CALL(mock_access_token_fetcher_, OnFetchAccessToken)
+ .WillOnce(RunOnceCallback<0>(true, "access_token"));
+ EXPECT_CALL(mock_access_token_fetcher_, InvalidateAccessToken).Times(0);
+ EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
+
+ ServiceRequestSenderImpl request_sender{
+ &context_, &mock_access_token_fetcher_, /* cup_factory= */ nullptr,
+ std::move(loader_factory),
+ /* api_key= */ std::string()};
+ request_sender.SendRequest(GURL("https://www.example.com"),
+ std::string("request"),
+ ServiceRequestSender::AuthMode::OAUTH_STRICT,
+ mock_response_callback_.Get(),
+ autofill_assistant::RpcType::GET_USER_DATA);
+
+ histogram_tester.ExpectBucketCount(
+ "Android.AutofillAssistant.ServiceRequestSender.SuccessRetryCount", 1, 1);
+}
+
+TEST_F(ServiceRequestSenderImplTest, TestNoRetryLoggingForSupportsScripts) {
+ base::HistogramTester histogram_tester;
+ auto loader_factory =
+ std::make_unique<NiceMock<MockSimpleURLLoaderFactory>>();
+ auto loader = std::make_unique<NiceMock<MockURLLoader>>();
+ auto* loader_ptr = loader.get();
+ auto response_info = CreateResponseInfo(net::HTTP_OK, "OK");
+ EXPECT_CALL(*loader_factory, OnCreateLoader)
+ .WillOnce([&](::network::ResourceRequest* resource_request,
+ const ::net::NetworkTrafficAnnotationTag& annotation_tag) {
+ return std::move(loader);
+ });
+ EXPECT_CALL(*loader_ptr, DownloadToStringOfUnboundedSizeUntilCrashAndDie)
+ .WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
+ EXPECT_CALL(*loader_ptr, ResponseInfo)
+ .WillRepeatedly(Return(response_info.get()));
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status_));
+ EXPECT_CALL(*loader_ptr, GetNumRetries).Times(0);
+ EXPECT_CALL(mock_access_token_fetcher_, OnFetchAccessToken)
+ .WillOnce(RunOnceCallback<0>(true, "access_token"));
+ EXPECT_CALL(mock_access_token_fetcher_, InvalidateAccessToken).Times(0);
+ EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response", _));
+
+ ServiceRequestSenderImpl request_sender{
+ &context_, &mock_access_token_fetcher_, /* cup_factory= */ nullptr,
+ std::move(loader_factory),
+ /* api_key= */ std::string()};
+ request_sender.SendRequest(
+ GURL("https://www.example.com"), std::string("request"),
+ ServiceRequestSender::AuthMode::OAUTH_WITH_API_KEY_FALLBACK,
+ mock_response_callback_.Get(),
+ autofill_assistant::RpcType::SUPPORTS_SCRIPT);
+
+ histogram_tester.ExpectTotalCount(
+ "Android.AutofillAssistant.ServiceRequestSender.SuccessRetryCount", 0);
+ histogram_tester.ExpectTotalCount(
+ "Android.AutofillAssistant.ServiceRequestSender.FailureRetryCount", 0);
+}
+
+TEST_F(ServiceRequestSenderImplTest, EncodedBodyLengthSet) {
+ auto cup_factory =
+ std::make_unique<NiceMock<autofill_assistant::cup::MockCUPFactory>>();
+ auto loader_factory =
+ std::make_unique<NiceMock<MockSimpleURLLoaderFactory>>();
+ auto loader = std::make_unique<NiceMock<MockURLLoader>>();
+ auto response_info = CreateResponseInfo(net::HTTP_OK, "OK");
+ EXPECT_CALL(*loader_factory, OnCreateLoader)
+ .WillOnce([&](::network::ResourceRequest* resource_request,
+ const ::net::NetworkTrafficAnnotationTag& annotation_tag) {
+ return std::move(loader);
+ });
+
+ EXPECT_CALL(*loader,
+ AttachStringForUpload(std::string("request"),
+ std::string("application/x-protobuffer")));
+ EXPECT_CALL(*loader, DownloadToStringOfUnboundedSizeUntilCrashAndDie)
+ .WillOnce(RunOnceCallback<1>(std::make_unique<std::string>("response")));
+ EXPECT_CALL(*loader, ResponseInfo)
+ .WillRepeatedly(Return(response_info.get()));
+
+ auto completion_status = absl::make_optional<URLLoaderCompletionStatus>();
+ completion_status->encoded_body_length = 1337;
+ EXPECT_CALL(*loader, CompletionStatus)
+ .WillRepeatedly(ReturnRef(completion_status));
+
+ EXPECT_CALL(
+ mock_response_callback_,
+ Run(_, _,
+ Field(&ServiceRequestSender::ResponseInfo::encoded_body_length,
+ 1337)));
+ ServiceRequestSenderImpl request_sender{
+ &context_,
+ /* access_token_fetcher = */ nullptr, std::move(cup_factory),
+ std::move(loader_factory), std::string("fake_api_key")};
+ request_sender.SendRequest(
+ GURL("https://www.example.com"), std::string("request"),
+ ServiceRequestSender::AuthMode::API_KEY, mock_response_callback_.Get(),
+ RpcType::GET_TRIGGER_SCRIPTS);
+}
+
// TODO(b/170934170): Add tests for full unit test coverage of
// service_request_sender.
diff --git a/chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.cc b/chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.cc
index ea6402c445b..b15fb855c45 100644
--- a/chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.cc
+++ b/chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.cc
@@ -25,4 +25,7 @@ void ServiceRequestSenderLocalImpl::SendRequest(
/* response_info = */ {});
}
+void ServiceRequestSenderLocalImpl::SetDisableRpcSigning(
+ bool disable_rpc_signing) {}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.h b/chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.h
index add70eaef8c..07c74fea418 100644
--- a/chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.h
+++ b/chromium/components/autofill_assistant/browser/service/service_request_sender_local_impl.h
@@ -26,6 +26,8 @@ class ServiceRequestSenderLocalImpl : public ServiceRequestSender {
ResponseCallback callback,
RpcType rpc_type) override;
+ void SetDisableRpcSigning(bool disable_rpc_signing) override;
+
private:
std::string response_;
};
diff --git a/chromium/components/autofill_assistant/browser/starter.cc b/chromium/components/autofill_assistant/browser/starter.cc
index aa0a5512473..2139db318be 100644
--- a/chromium/components/autofill_assistant/browser/starter.cc
+++ b/chromium/components/autofill_assistant/browser/starter.cc
@@ -30,7 +30,6 @@
#include "components/autofill_assistant/browser/trigger_scripts/dynamic_trigger_conditions.h"
#include "components/autofill_assistant/browser/trigger_scripts/static_trigger_conditions.h"
#include "components/autofill_assistant/browser/url_utils.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -180,7 +179,7 @@ Starter::Starter(content::WebContents* web_contents,
: content::WebContentsObserver(web_contents),
content::WebContentsUserData<Starter>(*web_contents),
current_ukm_source_id_(
- ukm::GetSourceIdForWebContentsDocument(web_contents)),
+ web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId()),
cached_failed_trigger_script_fetches_(
GetOrCreateFailedTriggerScriptFetchesCache()),
user_denylisted_domains_(kMaxUserDenylistedCacheSize),
@@ -426,7 +425,7 @@ void Starter::Init() {
fetch_trigger_scripts_on_navigation_) {
MaybeStartImplicitlyForUrl(
web_contents()->GetLastCommittedURL(),
- ukm::GetSourceIdForWebContentsDocument(web_contents()));
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
}
}
@@ -471,7 +470,13 @@ void Starter::Start(std::unique_ptr<TriggerContext> trigger_context) {
CancelPendingStartup(Metrics::TriggerScriptFinishedState::CANCELED);
pending_trigger_context_ = std::move(trigger_context);
if (!platform_delegate_->IsAttached()) {
- OnStartDone(/* start_regular_script = */ false);
+ OnStartDone(/* start_script= */ false);
+ return;
+ }
+
+ if (platform_delegate_->GetIsSupervisedUser()) {
+ OnStartDone(/* start_script= */ false);
+ return;
}
if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
@@ -508,7 +513,7 @@ void Starter::Start(std::unique_ptr<TriggerContext> trigger_context) {
if (IsTriggerScriptContext(*pending_trigger_context_) &&
!url_utils::IsSamePublicSuffixDomain(
- web_contents()->GetMainFrame()->GetLastCommittedURL(),
+ web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
startup_url.value_or(GURL()))) {
waiting_for_deeplink_navigation_ = true;
return;
@@ -847,6 +852,14 @@ void Starter::DeleteTriggerScriptCoordinator() {
trigger_script_coordinator_.reset();
}
+const CommonDependencies* Starter::GetCommonDependencies() {
+ return platform_delegate_->GetCommonDependencies();
+}
+
+const PlatformDependencies* Starter::GetPlatformDependencies() {
+ return platform_delegate_->GetPlatformDependencies();
+}
+
base::WeakPtr<Starter> Starter::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
diff --git a/chromium/components/autofill_assistant/browser/starter.h b/chromium/components/autofill_assistant/browser/starter.h
index e5f7cec6bf0..795821eca5a 100644
--- a/chromium/components/autofill_assistant/browser/starter.h
+++ b/chromium/components/autofill_assistant/browser/starter.h
@@ -14,8 +14,10 @@
#include "base/memory/weak_ptr.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
+#include "components/autofill_assistant/browser/common_dependencies.h"
#include "components/autofill_assistant/browser/controller.h"
#include "components/autofill_assistant/browser/metrics.h"
+#include "components/autofill_assistant/browser/platform_dependencies.h"
#include "components/autofill_assistant/browser/public/runtime_manager.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/starter_heuristic.h"
@@ -81,6 +83,9 @@ class Starter : public content::WebContentsObserver,
// When the activity is changed on Android.
void OnDependenciesInvalidated();
+ const CommonDependencies* GetCommonDependencies();
+ const PlatformDependencies* GetPlatformDependencies();
+
base::WeakPtr<Starter> GetWeakPtr();
private:
diff --git a/chromium/components/autofill_assistant/browser/starter_heuristic.cc b/chromium/components/autofill_assistant/browser/starter_heuristic.cc
index 9c1482e991c..872876f8a8e 100644
--- a/chromium/components/autofill_assistant/browser/starter_heuristic.cc
+++ b/chromium/components/autofill_assistant/browser/starter_heuristic.cc
@@ -74,8 +74,8 @@ void StarterHeuristic::InitFromTrialParams() {
return;
}
url_matcher::URLMatcherConditionSet::Vector condition_sets;
- base::flat_map<url_matcher::URLMatcherConditionSet::ID, std::string> mapping;
- url_matcher::URLMatcherConditionSet::ID next_condition_set_id = 0;
+ base::flat_map<base::MatcherStringPattern::ID, std::string> mapping;
+ base::MatcherStringPattern::ID next_condition_set_id = 0;
for (const auto& heuristic : heuristics->GetListDeprecated()) {
auto* intent =
heuristic.FindKeyOfType(kHeuristicIntentKey, base::Value::Type::STRING);
@@ -87,11 +87,9 @@ void StarterHeuristic::InitFromTrialParams() {
}
std::string error;
- const auto& url_conditions_dict =
- base::Value::AsDictionaryValue(*url_conditions);
condition_sets.emplace_back(
url_matcher::URLMatcherFactory::CreateFromURLFilterDictionary(
- url_matcher_.condition_factory(), &url_conditions_dict,
+ url_matcher_.condition_factory(), url_conditions->GetDict(),
next_condition_set_id, &error));
if (!error.empty()) {
VLOG(1) << "Error pasing url conditions: " << error;
@@ -132,8 +130,7 @@ base::flat_set<std::string> StarterHeuristic::IsHeuristicMatch(
return matching_intents;
}
- std::set<url_matcher::URLMatcherConditionSet::ID> matches =
- url_matcher_.MatchURL(url);
+ std::set<base::MatcherStringPattern::ID> matches = url_matcher_.MatchURL(url);
for (const auto& match : matches) {
auto intent = matcher_id_to_intent_map_.find(match);
if (intent == matcher_id_to_intent_map_.end()) {
diff --git a/chromium/components/autofill_assistant/browser/starter_heuristic.h b/chromium/components/autofill_assistant/browser/starter_heuristic.h
index 52d5e40e0c6..aa0e602664e 100644
--- a/chromium/components/autofill_assistant/browser/starter_heuristic.h
+++ b/chromium/components/autofill_assistant/browser/starter_heuristic.h
@@ -62,7 +62,7 @@ class StarterHeuristic : public base::RefCountedThreadSafe<StarterHeuristic> {
// Arbitrary mapping of matcher IDs to intent strings. This mapping is built
// dynamically to allow the heuristic to work on intents that are otherwise
// unknown to the client.
- base::flat_map<url_matcher::URLMatcherConditionSet::ID, std::string>
+ base::flat_map<base::MatcherStringPattern::ID, std::string>
matcher_id_to_intent_map_;
};
diff --git a/chromium/components/autofill_assistant/browser/starter_platform_delegate.h b/chromium/components/autofill_assistant/browser/starter_platform_delegate.h
index 78108ba2e39..8c91b2bc56c 100644
--- a/chromium/components/autofill_assistant/browser/starter_platform_delegate.h
+++ b/chromium/components/autofill_assistant/browser/starter_platform_delegate.h
@@ -7,8 +7,10 @@
#include "base/callback_forward.h"
#include "components/autofill_assistant/browser/assistant_field_trial_util.h"
+#include "components/autofill_assistant/browser/common_dependencies.h"
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/onboarding_result.h"
+#include "components/autofill_assistant/browser/platform_dependencies.h"
#include "components/autofill_assistant/browser/service/service_request_sender.h"
#include "components/autofill_assistant/browser/trigger_context.h"
#include "components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h"
@@ -87,6 +89,8 @@ class StarterPlatformDelegate {
virtual bool GetMakeSearchesAndBrowsingBetterEnabled() const = 0;
// Returns whether the user is logged in or not.
virtual bool GetIsLoggedIn() = 0;
+ // Returns whether the user is restricted to any supervision.
+ virtual bool GetIsSupervisedUser() = 0;
// Returns whether this is a custom tab or not.
virtual bool GetIsCustomTab() const = 0;
// Returns whether this is running in WebLayer or not.
@@ -99,6 +103,10 @@ class StarterPlatformDelegate {
// The starter platform delegate should only be interacted with while attached
// as it might not be able to perform its functions while detached.
virtual bool IsAttached() = 0;
+ // Returns the common dependencies.
+ virtual const CommonDependencies* GetCommonDependencies() const = 0;
+ // Returns the platform dependencies.
+ virtual const PlatformDependencies* GetPlatformDependencies() const = 0;
virtual base::WeakPtr<StarterPlatformDelegate> GetWeakPtr() = 0;
};
diff --git a/chromium/components/autofill_assistant/browser/starter_unittest.cc b/chromium/components/autofill_assistant/browser/starter_unittest.cc
index 17ec78d8329..f871e1970b3 100644
--- a/chromium/components/autofill_assistant/browser/starter_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/starter_unittest.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <string>
#include "base/base64url.h"
+#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/containers/lru_cache.h"
#include "base/memory/raw_ptr.h"
@@ -181,20 +182,20 @@ class StarterTest : public testing::Test {
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(
web_contents()->GetLastCommittedURL(),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
simulator->Start();
for (const auto& url : urls) {
simulator->Redirect(url);
}
simulator->Commit();
navigation_ids_.emplace_back(
- ukm::GetSourceIdForWebContentsDocument(web_contents()));
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
}
void SimulateNavigateToUrl(const GURL& url) {
content::WebContentsTester::For(web_contents())->NavigateAndCommit(url);
navigation_ids_.emplace_back(
- ukm::GetSourceIdForWebContentsDocument(web_contents()));
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
}
// Each request sender is only good for one trigger script. This call will
@@ -1344,13 +1345,13 @@ TEST_F(StarterTest, RedirectFailsDuringPendingTriggerScriptStart) {
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(
web_contents()->GetLastCommittedURL(),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
simulator->Start();
simulator->Redirect(GURL("https://redirect.com/to/www/example/com"));
simulator->Fail(net::ERR_BLOCKED_BY_CLIENT);
simulator->CommitErrorPage();
navigation_ids_.emplace_back(
- ukm::GetSourceIdForWebContentsDocument(web_contents()));
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
// Note that this impression is recorded for the last URL that a navigation-
// start event occurred for. We never reached the target domain, so this is
@@ -1390,7 +1391,7 @@ TEST_F(StarterTest, StartTriggerScriptDuringRedirectRecordsUkmForTargetUrl) {
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(
web_contents()->GetLastCommittedURL(),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
simulator->Start();
simulator->Redirect(GURL("https://redirect.com/to/www/example/com"));
starter_->Start(std::make_unique<TriggerContext>(
@@ -1398,7 +1399,7 @@ TEST_F(StarterTest, StartTriggerScriptDuringRedirectRecordsUkmForTargetUrl) {
simulator->Redirect(GURL(kExampleDeeplink));
simulator->Commit();
navigation_ids_.emplace_back(
- ukm::GetSourceIdForWebContentsDocument(web_contents()));
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
EXPECT_THAT(GetUkmTriggerScriptStarted(ukm_recorder_),
ElementsAreArray(ToHumanReadableMetrics(
@@ -1425,7 +1426,7 @@ TEST_F(StarterTest, RegularStartupDoesNotWaitForNavigationToFinish) {
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(
web_contents()->GetLastCommittedURL(),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
simulator->Start();
simulator->Redirect(GURL("https://redirect.com/to/www/example/com"));
@@ -2253,6 +2254,55 @@ TEST_F(StarterPrerenderTest, DoNotAffectRecordUkmDuringPrendering) {
EXPECT_THAT(GetUkmRegularScriptOnboarding(ukm_recorder_), IsEmpty());
}
+class StarterFencedFrameTest : public StarterTest {
+ public:
+ StarterFencedFrameTest() {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ blink::features::kFencedFrames, {{"implementation_type", "mparch"}});
+ }
+ ~StarterFencedFrameTest() override = default;
+
+ content::RenderFrameHost* CreateFencedFrame(
+ content::RenderFrameHost* parent) {
+ content::RenderFrameHost* fenced_frame =
+ content::RenderFrameHostTester::For(parent)->AppendFencedFrame();
+ return fenced_frame;
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(StarterFencedFrameTest, NoImplicitTriggeringForFencedFrames) {
+ // This test should not fetch trigger scripts.
+ SetupPlatformDelegateForReturningUser();
+ PrepareTriggerScriptRequestSender();
+ EXPECT_CALL(*mock_trigger_script_service_request_sender_, OnSendRequest)
+ .Times(0);
+
+ base::flat_map<std::string, std::string> script_parameters = {
+ {"ENABLED", "true"},
+ {"START_IMMEDIATELY", "false"},
+ {"REQUEST_TRIGGER_SCRIPT", "true"},
+ {"ORIGINAL_DEEPLINK", kExampleDeeplink}};
+
+ // Start on "different.com" and it shouldn't start immediately.
+ SimulateNavigateToUrl(GURL("https://www.different.com"));
+ starter_->Start(std::make_unique<TriggerContext>(
+ std::make_unique<ScriptParameters>(script_parameters),
+ TriggerContext::Options()));
+ // Create a fenced frame and navigate to the example deeplink. The navigation
+ // should not start because it is not in the primary main frame.
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
+ ->InitializeRenderFrameIfNeeded();
+ content::RenderFrameHost* fenced_frame_rfh =
+ CreateFencedFrame(web_contents()->GetPrimaryMainFrame());
+ std::unique_ptr<content::NavigationSimulator> navigation_simulator =
+ content::NavigationSimulator::CreateRendererInitiated(
+ GURL(kExampleDeeplink), fenced_frame_rfh);
+ navigation_simulator->Start();
+}
+
TEST_F(StarterTest, StartupRegistersTriggerFieldTrial) {
auto mock_field_trial_util =
std::make_unique<NiceMock<MockAssistantFieldTrialUtil>>();
@@ -2336,4 +2386,39 @@ TEST_F(StarterTest, CanStartSucceeds) {
Metrics::AutofillAssistantExperiment::NO_EXPERIMENT}}})));
}
+TEST_F(StarterTest, RegularStartupFailsForSupervisedUser) {
+ SetupPlatformDelegateForFirstTimeUser();
+ fake_platform_delegate_.is_supervised_user_ = true;
+
+ base::flat_map<std::string, std::string> script_parameters = {
+ {"ENABLED", "true"},
+ {"START_IMMEDIATELY", "true"},
+ {"ORIGINAL_DEEPLINK", kExampleDeeplink}};
+ TriggerContext::Options options;
+ options.initial_url = "https://redirect.com/to/www/example/com";
+ EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
+
+ starter_->Start(std::make_unique<TriggerContext>(
+ std::make_unique<ScriptParameters>(script_parameters), options));
+}
+
+TEST_F(StarterTest, RpcTriggerScriptStartupFailsForSupervisedUser) {
+ SetupPlatformDelegateForReturningUser();
+ fake_platform_delegate_.is_supervised_user_ = true;
+
+ base::flat_map<std::string, std::string> script_parameters = {
+ {"ENABLED", "true"},
+ {"START_IMMEDIATELY", "false"},
+ {"REQUEST_TRIGGER_SCRIPT", "true"},
+ {"ORIGINAL_DEEPLINK", kExampleDeeplink}};
+ EXPECT_CALL(*mock_trigger_script_ui_delegate_, Attach).Times(0);
+ EXPECT_CALL(*mock_trigger_script_service_request_sender_, OnSendRequest)
+ .Times(0);
+ EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
+
+ starter_->Start(std::make_unique<TriggerContext>(
+ std::make_unique<ScriptParameters>(script_parameters),
+ TriggerContext::Options()));
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/suppress_keyboard_raii.cc b/chromium/components/autofill_assistant/browser/suppress_keyboard_raii.cc
index 7873af5cd02..104bdec981f 100644
--- a/chromium/components/autofill_assistant/browser/suppress_keyboard_raii.cc
+++ b/chromium/components/autofill_assistant/browser/suppress_keyboard_raii.cc
@@ -36,7 +36,7 @@ void SuppressKeyboardRAII::RenderFrameCreated(
}
void SuppressKeyboardRAII::SuppressKeyboard(bool suppress) {
- web_contents()->GetMainFrame()->ForEachRenderFrameHost(
+ web_contents()->GetPrimaryMainFrame()->ForEachRenderFrameHost(
base::BindRepeating(&SuppressKeyboardForFrame, suppress));
}
diff --git a/chromium/components/autofill_assistant/browser/tab_helper.cc b/chromium/components/autofill_assistant/browser/tab_helper.cc
index a699db7b717..ea4b814ad05 100644
--- a/chromium/components/autofill_assistant/browser/tab_helper.cc
+++ b/chromium/components/autofill_assistant/browser/tab_helper.cc
@@ -10,8 +10,13 @@
namespace autofill_assistant {
-void CreateForWebContents(content::WebContents* web_contents) {
- StarterDelegateDesktop::CreateForWebContents(web_contents);
+void CreateForWebContents(
+ content::WebContents* web_contents,
+ std::unique_ptr<CommonDependencies> common_dependencies,
+ std::unique_ptr<PlatformDependencies> platform_dependencies) {
+ StarterDelegateDesktop::CreateForWebContents(
+ web_contents, std::move(common_dependencies),
+ std::move(platform_dependencies));
auto starter_delegate =
StarterDelegateDesktop::FromWebContents(web_contents)->GetWeakPtr();
diff --git a/chromium/components/autofill_assistant/browser/tab_helper.h b/chromium/components/autofill_assistant/browser/tab_helper.h
index 494443b0df8..21aed6dc21f 100644
--- a/chromium/components/autofill_assistant/browser/tab_helper.h
+++ b/chromium/components/autofill_assistant/browser/tab_helper.h
@@ -5,11 +5,16 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_TAB_HELPER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_TAB_HELPER_H_
+#include "components/autofill_assistant/browser/common_dependencies.h"
+#include "components/autofill_assistant/browser/platform_dependencies.h"
#include "content/public/browser/web_contents.h"
namespace autofill_assistant {
// Creates the starter instance for the |web_contents|.
-void CreateForWebContents(content::WebContents* web_contents);
+void CreateForWebContents(
+ content::WebContents* web_contents,
+ std::unique_ptr<CommonDependencies> common_dependencies,
+ std::unique_ptr<PlatformDependencies> platform_dependencies);
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc b/chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
index ec1bd624039..2cc6ee34f1a 100644
--- a/chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
+++ b/chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
@@ -12,7 +12,6 @@
#include "components/autofill_assistant/browser/protocol_utils.h"
#include "components/autofill_assistant/browser/starter_platform_delegate.h"
#include "components/autofill_assistant/browser/url_utils.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -627,7 +626,8 @@ TriggerScriptCoordinator::GetTriggerUiTypeForVisibleScript() const {
}
GURL TriggerScriptCoordinator::GetCurrentURL() const {
- GURL current_url = web_contents()->GetMainFrame()->GetLastCommittedURL();
+ GURL current_url =
+ web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL();
if (current_url.is_empty()) {
return deeplink_url_;
}
diff --git a/chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc b/chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc
index 842185e70e9..193eb909ee7 100644
--- a/chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc
@@ -105,7 +105,7 @@ class TriggerScriptCoordinatorTest : public testing::Test {
std::move(mock_web_controller), std::move(mock_request_sender),
GURL(kFakeServerUrl), std::move(mock_static_trigger_conditions),
std::move(mock_dynamic_trigger_conditions), &ukm_recorder_,
- ukm::GetSourceIdForWebContentsDocument(web_contents()));
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
}
void TearDown() override { coordinator_.reset(); }
@@ -127,10 +127,10 @@ class TriggerScriptCoordinatorTest : public testing::Test {
void SimulateNavigateToUrl(const GURL& url) {
content::WebContentsTester::For(web_contents())->SetLastCommittedURL(url);
content::NavigationSimulator::NavigateAndCommitFromDocument(
- url, web_contents()->GetMainFrame());
+ url, web_contents()->GetPrimaryMainFrame());
content::WebContentsTester::For(web_contents())->TestSetIsLoading(false);
navigation_ids_.emplace_back(
- ukm::GetSourceIdForWebContentsDocument(web_contents()));
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
}
protected:
@@ -1360,7 +1360,7 @@ TEST_F(TriggerScriptCoordinatorTest, UiTimeoutWhileShown) {
EXPECT_CALL(*mock_ui_delegate_, ShowTriggerScript).Times(1);
content::NavigationSimulator::Reload(web_contents());
navigation_ids_.emplace_back(
- ukm::GetSourceIdForWebContentsDocument(web_contents()));
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
EXPECT_CALL(*mock_ui_delegate_, HideTriggerScript).Times(0);
task_environment()->FastForwardBy(base::Seconds(1));
diff --git a/chromium/components/autofill_assistant/browser/ui_controller.cc b/chromium/components/autofill_assistant/browser/ui_controller.cc
index 6bfb1ebad7c..e17ba1f316c 100644
--- a/chromium/components/autofill_assistant/browser/ui_controller.cc
+++ b/chromium/components/autofill_assistant/browser/ui_controller.cc
@@ -76,6 +76,38 @@ bool ShouldShowFeedbackChipForReason(Metrics::DropOutReason reason) {
}
}
+bool ShouldReloadData(const CollectUserDataOptions& options,
+ UserDataEventType event_type) {
+ if (!options.use_alternative_edit_dialogs) {
+ return false;
+ }
+ switch (event_type) {
+ case UserDataEventType::ENTRY_CREATED:
+ case UserDataEventType::ENTRY_EDITED:
+ return true;
+ case UserDataEventType::UNKNOWN:
+ case UserDataEventType::NO_NOTIFICATION:
+ case UserDataEventType::SELECTION_CHANGED:
+ return false;
+ }
+}
+
+bool ShouldStoreTemporaryData(const CollectUserDataOptions& options,
+ UserDataEventType event_type) {
+ if (!options.use_alternative_edit_dialogs) {
+ return false;
+ }
+ switch (event_type) {
+ case UserDataEventType::ENTRY_CREATED:
+ case UserDataEventType::ENTRY_EDITED:
+ return true;
+ case UserDataEventType::UNKNOWN:
+ case UserDataEventType::NO_NOTIFICATION:
+ case UserDataEventType::SELECTION_CHANGED:
+ return false;
+ }
+}
+
} // namespace
UiController::UiController(
@@ -817,13 +849,15 @@ void UiController::HandleShippingAddressChange(
if (collect_user_data_options_ == nullptr) {
return;
}
- if (collect_user_data_options_->use_gms_core_edit_dialogs) {
+
+ collect_user_data_options_->selected_user_data_changed_callback.Run(
+ UserDataEventField::SHIPPING_EVENT, event_type);
+
+ if (ShouldReloadData(*collect_user_data_options_, event_type)) {
ReloadUserData(UserDataEventField::SHIPPING_EVENT, event_type);
return;
}
- collect_user_data_options_->selected_user_data_changed_callback.Run(
- SHIPPING_EVENT, event_type);
DCHECK(!collect_user_data_options_->shipping_address_name.empty());
SetProfile(collect_user_data_options_->shipping_address_name,
UserDataFieldChange::SHIPPING_ADDRESS, std::move(address));
@@ -835,13 +869,16 @@ void UiController::HandleContactInfoChange(
if (collect_user_data_options_ == nullptr) {
return;
}
- if (collect_user_data_options_->use_gms_core_edit_dialogs) {
- ReloadUserData(UserDataEventField::CONTACT_EVENT, event_type);
- return;
- }
collect_user_data_options_->selected_user_data_changed_callback.Run(
- CONTACT_EVENT, event_type);
+ UserDataEventField::CONTACT_EVENT, event_type);
+
+ if (ShouldStoreTemporaryData(*collect_user_data_options_, event_type)) {
+ UserData* user_data = GetUserData();
+ DCHECK(user_data);
+ user_data::UpsertContact(*profile, user_data->transient_contacts_);
+ }
+
DCHECK(!collect_user_data_options_->contact_details_name.empty());
SetProfile(collect_user_data_options_->contact_details_name,
UserDataFieldChange::CONTACT_PROFILE, std::move(profile));
@@ -853,13 +890,15 @@ void UiController::HandlePhoneNumberChange(
if (collect_user_data_options_ == nullptr) {
return;
}
- if (collect_user_data_options_->use_gms_core_edit_dialogs) {
- ReloadUserData(UserDataEventField::CONTACT_EVENT, event_type);
- return;
- }
- // We don't notify the UserDataEvent in this case since we currently don't log
- // metrics for the phone number.
+ collect_user_data_options_->selected_user_data_changed_callback.Run(
+ UserDataEventField::PHONE_NUMBER_EVENT, event_type);
+
+ if (ShouldStoreTemporaryData(*collect_user_data_options_, event_type)) {
+ UserData* user_data = GetUserData();
+ DCHECK(user_data);
+ user_data::UpsertPhoneNumber(*profile, user_data->transient_phone_numbers_);
+ }
GetUserData()->SetSelectedPhoneNumber(std::move(profile));
execution_delegate_->NotifyUserDataChange(UserDataFieldChange::PHONE_NUMBER);
@@ -872,13 +911,15 @@ void UiController::HandleCreditCardChange(
if (collect_user_data_options_ == nullptr) {
return;
}
- if (collect_user_data_options_->use_gms_core_edit_dialogs) {
+
+ collect_user_data_options_->selected_user_data_changed_callback.Run(
+ UserDataEventField::CREDIT_CARD_EVENT, event_type);
+
+ if (ShouldReloadData(*collect_user_data_options_, event_type)) {
ReloadUserData(UserDataEventField::CREDIT_CARD_EVENT, event_type);
return;
}
- collect_user_data_options_->selected_user_data_changed_callback.Run(
- CREDIT_CARD_EVENT, event_type);
DCHECK(!collect_user_data_options_->billing_address_name.empty());
SetProfile(collect_user_data_options_->billing_address_name,
UserDataFieldChange::BILLING_ADDRESS, std::move(billing_profile));
@@ -898,15 +939,9 @@ void UiController::SetProfile(
void UiController::ReloadUserData(UserDataEventField event_field,
UserDataEventType event_type) {
- if (collect_user_data_options_ == nullptr) {
- return;
- }
-
- collect_user_data_options_->selected_user_data_changed_callback.Run(
- event_field, event_type);
-
- auto callback = std::move(collect_user_data_options_->reload_data_callback);
- std::move(callback).Run(GetUserData());
+ DCHECK(collect_user_data_options_);
+ std::move(collect_user_data_options_->reload_data_callback)
+ .Run(event_field, GetUserData());
}
void UiController::SetTermsAndConditions(
@@ -987,6 +1022,13 @@ void UiController::SetCollectUserDataOptions(CollectUserDataOptions* options) {
execution_delegate_->NotifyUserDataChange(UserDataFieldChange::ALL);
}
+void UiController::SetCollectUserDataUiState(bool loading,
+ UserDataEventField event) {
+ for (UiControllerObserver& observer : observers_) {
+ observer.OnCollectUserDataUiStateChanged(loading, event);
+ }
+}
+
void UiController::SetLastSuccessfulUserDataOptions(
std::unique_ptr<CollectUserDataOptions> collect_user_data_options) {
last_collect_user_data_options_ = std::move(collect_user_data_options);
@@ -1042,7 +1084,6 @@ void UiController::OnOverlayColorsChanged(
const ExecutionDelegate::OverlayColors& colors) {}
void UiController::OnClientSettingsChanged(const ClientSettings& settings) {}
void UiController::OnShouldShowOverlayChanged(bool should_show) {}
-void UiController::OnShutdown(Metrics::DropOutReason reason) {}
void UiController::OnExecuteScript(const std::string& start_message) {
if (!start_message.empty())
@@ -1132,4 +1173,20 @@ void UiController::OnUiShownChanged(bool shown) {
}
}
+bool UiController::SupportsExternalActions() {
+ return false;
+}
+
+void UiController::ExecuteExternalAction(
+ const external::Action& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) {
+ NOTREACHED() << "Flows using default UI don't support external actions.";
+}
+
+void UiController::OnInterruptStarted() {}
+void UiController::OnInterruptFinished() {}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/ui_controller.h b/chromium/components/autofill_assistant/browser/ui_controller.h
index 073545fa4cb..0b212ad807b 100644
--- a/chromium/components/autofill_assistant/browser/ui_controller.h
+++ b/chromium/components/autofill_assistant/browser/ui_controller.h
@@ -10,6 +10,7 @@
#include <vector>
#include "base/callback_helpers.h"
+#include "base/memory/raw_ptr.h"
#include "components/autofill_assistant/browser/autofill_assistant_tts_controller.h"
#include "components/autofill_assistant/browser/basic_interactions.h"
#include "components/autofill_assistant/browser/bottom_sheet_state.h"
@@ -107,10 +108,14 @@ class UiController : public ScriptExecutorUiDelegate,
void SetExpandSheetForPromptAction(bool expand) override;
void SetCollectUserDataOptions(CollectUserDataOptions* options) override;
+ void SetCollectUserDataUiState(bool loading,
+ UserDataEventField event_field) override;
void SetLastSuccessfulUserDataOptions(std::unique_ptr<CollectUserDataOptions>
collect_user_data_options) override;
const CollectUserDataOptions* GetLastSuccessfulUserDataOptions()
const override;
+ void OnInterruptStarted() override;
+ void OnInterruptFinished() override;
// Overrides autofill_assistant::UiDelegate:
std::vector<Details> GetDetails() const override;
@@ -189,7 +194,13 @@ class UiController : public ScriptExecutorUiDelegate,
void OnStop() override;
void OnResetState() override;
void OnUiShownChanged(bool shown) override;
- void OnShutdown(Metrics::DropOutReason reason) override;
+ bool SupportsExternalActions() override;
+ void ExecuteExternalAction(
+ const external::Action& external_action,
+ base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+ start_dom_checks_callback,
+ base::OnceCallback<void(const external::Result& result)>
+ end_action_callback) override;
// Overrides AutofillAssistantTtsController::TtsEventDelegate
void OnTtsEvent(AutofillAssistantTtsController::TtsEventType event) override;
@@ -264,7 +275,7 @@ class UiController : public ScriptExecutorUiDelegate,
UserData* GetUserData();
UserModel* GetUserModel();
- Client* const client_;
+ const raw_ptr<Client> client_;
// Current status message, may be empty.
std::string status_message_;
@@ -311,7 +322,7 @@ class UiController : public ScriptExecutorUiDelegate,
base::ObserverList<UiControllerObserver> observers_;
- ExecutionDelegate* execution_delegate_;
+ raw_ptr<ExecutionDelegate> execution_delegate_;
EventHandler event_handler_;
BasicInteractions basic_interactions_{this, execution_delegate_};
diff --git a/chromium/components/autofill_assistant/browser/ui_controller_observer.h b/chromium/components/autofill_assistant/browser/ui_controller_observer.h
index daf33b94120..07dc7f0afe9 100644
--- a/chromium/components/autofill_assistant/browser/ui_controller_observer.h
+++ b/chromium/components/autofill_assistant/browser/ui_controller_observer.h
@@ -43,6 +43,11 @@ class UiControllerObserver : public base::CheckedObserver {
virtual void OnCollectUserDataOptionsChanged(
const CollectUserDataOptions* options) = 0;
+ // Report that the state of the User Data UI has changed.
+ virtual void OnCollectUserDataUiStateChanged(
+ bool loading,
+ UserDataEventField event_field) = 0;
+
// Called when details have changed. Details will be empty if they have been
// cleared.
virtual void OnDetailsChanged(const std::vector<Details>& details) = 0;
diff --git a/chromium/components/autofill_assistant/browser/ui_controller_unittest.cc b/chromium/components/autofill_assistant/browser/ui_controller_unittest.cc
index a691b5a6db7..e983efb5179 100644
--- a/chromium/components/autofill_assistant/browser/ui_controller_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/ui_controller_unittest.cc
@@ -494,78 +494,93 @@ TEST_F(UiControllerTest, UserDataChangesByOutOfLoopWrite) {
UserDataFieldChange::CONTACT_PROFILE);
}
-TEST_F(UiControllerTest, UserDataFormReloadFromContactChange) {
+TEST_F(UiControllerTest, UserDataFormStoreContactChange) {
auto options = std::make_unique<FakeCollectUserDataOptions>();
- base::MockCallback<base::OnceCallback<void(UserData*)>> reload_callback;
- options->reload_data_callback = reload_callback.Get();
base::MockCallback<
base::RepeatingCallback<void(UserDataEventField, UserDataEventType)>>
change_callback;
options->selected_user_data_changed_callback = change_callback.Get();
- options->use_gms_core_edit_dialogs = true;
+ options->contact_details_name = "CONTACT";
+ options->use_alternative_edit_dialogs = true;
ui_controller_->SetCollectUserDataOptions(options.get());
EXPECT_CALL(change_callback, Run(UserDataEventField::CONTACT_EVENT,
UserDataEventType::ENTRY_CREATED));
- EXPECT_CALL(reload_callback, Run);
- ui_controller_->HandleContactInfoChange(nullptr,
- UserDataEventType::ENTRY_CREATED);
+ autofill::AutofillProfile profile;
+ profile.SetRawInfo(autofill::ServerFieldType::EMAIL_ADDRESS,
+ u"johndoe@google.com");
+ ui_controller_->HandleContactInfoChange(
+ std::make_unique<autofill::AutofillProfile>(profile),
+ UserDataEventType::ENTRY_CREATED);
+
+ ASSERT_EQ(user_data_.transient_contacts_.size(), 1u);
+ EXPECT_EQ(user_data_.transient_contacts_[0]->profile->GetRawInfo(
+ autofill::ServerFieldType::EMAIL_ADDRESS),
+ u"johndoe@google.com");
}
-TEST_F(UiControllerTest, UserDataFormReloadFromPhoneNumberChange) {
+TEST_F(UiControllerTest, UserDataFormStorePhoneNumberChange) {
auto options = std::make_unique<FakeCollectUserDataOptions>();
- base::MockCallback<base::OnceCallback<void(UserData*)>> reload_callback;
- options->reload_data_callback = reload_callback.Get();
base::MockCallback<
base::RepeatingCallback<void(UserDataEventField, UserDataEventType)>>
change_callback;
options->selected_user_data_changed_callback = change_callback.Get();
- options->use_gms_core_edit_dialogs = true;
+ options->use_alternative_edit_dialogs = true;
ui_controller_->SetCollectUserDataOptions(options.get());
- EXPECT_CALL(change_callback, Run(UserDataEventField::CONTACT_EVENT,
+ EXPECT_CALL(change_callback, Run(UserDataEventField::PHONE_NUMBER_EVENT,
UserDataEventType::ENTRY_CREATED));
- EXPECT_CALL(reload_callback, Run);
- ui_controller_->HandlePhoneNumberChange(nullptr,
- UserDataEventType::ENTRY_CREATED);
+ autofill::AutofillProfile profile;
+ profile.SetRawInfo(autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER,
+ u"+41441234567");
+ ui_controller_->HandlePhoneNumberChange(
+ std::make_unique<autofill::AutofillProfile>(profile),
+ UserDataEventType::ENTRY_CREATED);
+
+ ASSERT_EQ(user_data_.transient_phone_numbers_.size(), 1u);
+ EXPECT_EQ(user_data_.transient_phone_numbers_[0]->profile->GetRawInfo(
+ autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER),
+ u"+41441234567");
}
TEST_F(UiControllerTest, UserDataFormReloadFromShippingAddressChange) {
auto options = std::make_unique<FakeCollectUserDataOptions>();
- base::MockCallback<base::OnceCallback<void(UserData*)>> reload_callback;
+ base::MockCallback<base::OnceCallback<void(UserDataEventField, UserData*)>>
+ reload_callback;
options->reload_data_callback = reload_callback.Get();
base::MockCallback<
base::RepeatingCallback<void(UserDataEventField, UserDataEventType)>>
change_callback;
options->selected_user_data_changed_callback = change_callback.Get();
- options->use_gms_core_edit_dialogs = true;
+ options->use_alternative_edit_dialogs = true;
ui_controller_->SetCollectUserDataOptions(options.get());
EXPECT_CALL(change_callback, Run(UserDataEventField::SHIPPING_EVENT,
UserDataEventType::ENTRY_CREATED));
- EXPECT_CALL(reload_callback, Run);
+ EXPECT_CALL(reload_callback, Run(UserDataEventField::SHIPPING_EVENT, _));
ui_controller_->HandleShippingAddressChange(nullptr,
UserDataEventType::ENTRY_CREATED);
}
TEST_F(UiControllerTest, UserDataFormReloadFromCreditCardChange) {
auto options = std::make_unique<FakeCollectUserDataOptions>();
- base::MockCallback<base::OnceCallback<void(UserData*)>> reload_callback;
+ base::MockCallback<base::OnceCallback<void(UserDataEventField, UserData*)>>
+ reload_callback;
options->reload_data_callback = reload_callback.Get();
base::MockCallback<
base::RepeatingCallback<void(UserDataEventField, UserDataEventType)>>
change_callback;
options->selected_user_data_changed_callback = change_callback.Get();
- options->use_gms_core_edit_dialogs = true;
+ options->use_alternative_edit_dialogs = true;
ui_controller_->SetCollectUserDataOptions(options.get());
EXPECT_CALL(change_callback, Run(UserDataEventField::CREDIT_CARD_EVENT,
UserDataEventType::ENTRY_CREATED));
- EXPECT_CALL(reload_callback, Run);
+ EXPECT_CALL(reload_callback, Run(UserDataEventField::CREDIT_CARD_EVENT, _));
ui_controller_->HandleCreditCardChange(nullptr, nullptr,
UserDataEventType::ENTRY_CREATED);
}
@@ -1101,4 +1116,12 @@ TEST_F(UiControllerTest, OnExecuteScriptSetMessageAndClearUserActions) {
EXPECT_EQ(ui_controller_->GetStatusMessage(), "script message");
}
+TEST_F(UiControllerTest, SetCollectUserDataUiState) {
+ EXPECT_CALL(mock_observer_,
+ OnCollectUserDataUiStateChanged(
+ /* loading= */ true, UserDataEventField::SHIPPING_EVENT));
+ ui_controller_->SetCollectUserDataUiState(/* loading= */ true,
+ UserDataEventField::SHIPPING_EVENT);
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/user_data.h b/chromium/components/autofill_assistant/browser/user_data.h
index 7d2aa061884..8ce76d942b1 100644
--- a/chromium/components/autofill_assistant/browser/user_data.h
+++ b/chromium/components/autofill_assistant/browser/user_data.h
@@ -61,7 +61,13 @@ enum UserDataEventType {
ENTRY_CREATED
};
-enum UserDataEventField { CONTACT_EVENT, CREDIT_CARD_EVENT, SHIPPING_EVENT };
+enum class UserDataEventField {
+ NONE,
+ CONTACT_EVENT,
+ PHONE_NUMBER_EVENT,
+ CREDIT_CARD_EVENT,
+ SHIPPING_EVENT
+};
// Represents a concrete login choice in the UI, e.g., 'Guest checkout' or
// a particular Chrome PWM login account.
@@ -123,6 +129,7 @@ struct Contact {
absl::optional<std::string> identifier;
std::unique_ptr<autofill::AutofillProfile> profile;
+ bool can_edit = true;
};
// Struct for holding a phone number. This is a wrapper around AutofillProfile
@@ -134,6 +141,7 @@ struct PhoneNumber {
absl::optional<std::string> identifier;
std::unique_ptr<autofill::AutofillProfile> profile;
+ bool can_edit = true;
};
// Struct for holding an address. This is a wrapper around AutofillProfile to
@@ -221,6 +229,9 @@ class UserData {
absl::optional<WebsiteLoginManager::Login> selected_login_;
+ std::vector<std::unique_ptr<Contact>> transient_contacts_;
+ std::vector<std::unique_ptr<PhoneNumber>> transient_phone_numbers_;
+
// Return true if address has been selected, otherwise return false.
// Note that selected_address() might return nullptr when
// has_selected_address() is true because fill manually was chosen.
@@ -313,8 +324,7 @@ struct CollectUserDataOptions {
std::vector<RequiredDataPiece> required_billing_address_data_pieces;
bool should_store_data_changes = false;
- bool can_edit_contacts = true;
- bool use_gms_core_edit_dialogs = false;
+ bool use_alternative_edit_dialogs = false;
absl::optional<std::string> add_payment_instrument_action_token;
absl::optional<std::string> add_address_token;
@@ -353,7 +363,7 @@ struct CollectUserDataOptions {
additional_actions_callback;
base::OnceCallback<void(int, UserData*, const UserModel*)>
terms_link_callback;
- base::OnceCallback<void(UserData*)> reload_data_callback;
+ base::OnceCallback<void(UserDataEventField, UserData*)> reload_data_callback;
// Called whenever there is a change to the selected user data.
base::RepeatingCallback<void(UserDataEventField, UserDataEventType)>
selected_user_data_changed_callback;
diff --git a/chromium/components/autofill_assistant/browser/user_data_util.cc b/chromium/components/autofill_assistant/browser/user_data_util.cc
index 90dcedd787b..4b1f73b0cd9 100644
--- a/chromium/components/autofill_assistant/browser/user_data_util.cc
+++ b/chromium/components/autofill_assistant/browser/user_data_util.cc
@@ -18,6 +18,7 @@
#include "components/autofill_assistant/browser/field_formatter.h"
#include "components/autofill_assistant/browser/model.pb.h"
#include "components/autofill_assistant/browser/url_utils.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/website_login_manager.h"
#include "components/strings/grit/components_strings.h"
#include "third_party/libaddressinput/chromium/addressinput_util.h"
@@ -319,6 +320,25 @@ ClientStatus MoveAutofillValueRegexpToTextFilter(
return re2_status;
}
+template <typename T>
+void UpsertAutofillProfile(const autofill::AutofillProfile& profile,
+ std::vector<std::unique_ptr<T>>& list) {
+ auto it =
+ base::ranges::find_if(list, [&profile](const std::unique_ptr<T>& ptr) {
+ return ptr->profile && ptr->profile->guid() == profile.guid();
+ });
+
+ auto new_profile = user_data::MakeUniqueFromProfile(profile);
+ if (it == list.end()) {
+ auto entry = std::make_unique<T>(std::move(new_profile));
+ entry->identifier = profile.guid();
+ list.emplace_back(std::move(entry));
+ return;
+ }
+
+ (*it)->profile = std::move(new_profile);
+}
+
} // namespace
std::vector<std::string> GetContactValidationErrors(
@@ -881,6 +901,7 @@ ClientStatus ResolveSelectorUserData(SelectorProto* selector,
case SelectorProto::Filter::kLabelled:
case SelectorProto::Filter::kMatchCssSelector:
case SelectorProto::Filter::kOnTop:
+ case SelectorProto::Filter::kParent:
case SelectorProto::Filter::FILTER_NOT_SET:
break;
// Do not add default here. In case a new filter gets added (that may
@@ -890,5 +911,15 @@ ClientStatus ResolveSelectorUserData(SelectorProto* selector,
return OkClientStatus();
}
+void UpsertContact(const autofill::AutofillProfile& profile,
+ std::vector<std::unique_ptr<Contact>>& list) {
+ UpsertAutofillProfile(profile, list);
+}
+
+void UpsertPhoneNumber(const autofill::AutofillProfile& profile,
+ std::vector<std::unique_ptr<PhoneNumber>>& list) {
+ UpsertAutofillProfile(profile, list);
+}
+
} // namespace user_data
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/user_data_util.h b/chromium/components/autofill_assistant/browser/user_data_util.h
index 1e35d1f94b5..f226c5192c0 100644
--- a/chromium/components/autofill_assistant/browser/user_data_util.h
+++ b/chromium/components/autofill_assistant/browser/user_data_util.h
@@ -16,10 +16,11 @@
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/user_data.h"
#include "components/autofill_assistant/browser/user_model.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
#include "components/autofill_assistant/browser/website_login_manager.h"
namespace autofill_assistant {
+class ElementFinderResult;
+
namespace user_data {
// Validate the completeness of a contact.
@@ -179,6 +180,13 @@ int GetFieldBitArrayForCreditCard(const autofill::CreditCard* card);
ClientStatus ResolveSelectorUserData(SelectorProto* selector,
const UserData* user_data);
+// Update or insert a contact in the list.
+void UpsertContact(const autofill::AutofillProfile& profile,
+ std::vector<std::unique_ptr<Contact>>& list);
+// Update or insert a phone number in the list.
+void UpsertPhoneNumber(const autofill::AutofillProfile& profile,
+ std::vector<std::unique_ptr<PhoneNumber>>& list);
+
} // namespace user_data
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/user_data_util_unittest.cc b/chromium/components/autofill_assistant/browser/user_data_util_unittest.cc
index 6c4e77b9a5e..eb715bd4367 100644
--- a/chromium/components/autofill_assistant/browser/user_data_util_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/user_data_util_unittest.cc
@@ -1263,7 +1263,7 @@ TEST_F(UserDataUtilTextValueTest, GetUsername) {
GURL("https://www.example.com"), "username");
ElementFinderResult element;
- element.SetRenderFrameHost(web_contents_->GetMainFrame());
+ element.SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
PasswordManagerValue password_manager_value;
password_manager_value.set_credential_type(PasswordManagerValue::USERNAME);
@@ -1281,7 +1281,7 @@ TEST_F(UserDataUtilTextValueTest, GetStoredPassword) {
GURL("https://www.example.com"), "username");
ElementFinderResult element;
- element.SetRenderFrameHost(web_contents_->GetMainFrame());
+ element.SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
PasswordManagerValue password_manager_value;
password_manager_value.set_credential_type(PasswordManagerValue::PASSWORD);
@@ -1303,7 +1303,7 @@ TEST_F(UserDataUtilTextValueTest, GetStoredPasswordFails) {
ElementFinderResult element;
content::WebContentsTester::For(web_contents_.get())
->NavigateAndCommit(GURL("https://www.example.com"));
- element.SetRenderFrameHost(web_contents_->GetMainFrame());
+ element.SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
PasswordManagerValue password_manager_value;
password_manager_value.set_credential_type(PasswordManagerValue::PASSWORD);
@@ -1419,7 +1419,7 @@ TEST_F(UserDataUtilTextValueTest, TextValuePasswordManagerValue) {
ElementFinderResult element;
content::WebContentsTester::For(web_contents_.get())
->NavigateAndCommit(GURL("https://www.example.com"));
- element.SetRenderFrameHost(web_contents_->GetMainFrame());
+ element.SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
TextValue text_value;
text_value.mutable_password_manager_value()->set_credential_type(
@@ -1611,6 +1611,106 @@ TEST_F(UserDataUtilTextValueTest, ResolveSelectorUserDataError) {
ASSERT_FALSE(status.ok());
}
+TEST(UserDataUtilTest, InsertNewContactToList) {
+ autofill::AutofillProfile new_profile;
+ autofill::test::SetProfileInfo(&new_profile, "Adam", "", "West",
+ "adam.west@gmail.com", "", "", "", "", "", "",
+ "", "");
+
+ std::unique_ptr<autofill::AutofillProfile> old_profile =
+ std::make_unique<autofill::AutofillProfile>();
+ autofill::test::SetProfileInfo(old_profile.get(), "Berta", "", "West",
+ "berta.west@gmail.com", "", "", "", "", "", "",
+ "", "");
+
+ std::vector<std::unique_ptr<Contact>> list;
+ list.emplace_back(std::make_unique<Contact>(std::move(old_profile)));
+
+ UpsertContact(new_profile, list);
+
+ ASSERT_EQ(list.size(), 2u);
+ EXPECT_EQ(list[1]->profile->guid(), new_profile.guid());
+ EXPECT_EQ(list[1]->identifier, new_profile.guid());
+ EXPECT_EQ(list[0]->profile->GetInfo(autofill::NAME_FIRST, "en-US"), u"Berta");
+ EXPECT_EQ(list[1]->profile->GetInfo(autofill::NAME_FIRST, "en-US"), u"Adam");
+}
+
+TEST(UserDataUtilTest, UpdateExistingContactInList) {
+ autofill::AutofillProfile updated_profile;
+ autofill::test::SetProfileInfo(&updated_profile, "Adam", "B.", "West",
+ "adam.west@gmail.com", "", "", "", "", "", "",
+ "", "");
+
+ std::unique_ptr<autofill::AutofillProfile> old_profile =
+ std::make_unique<autofill::AutofillProfile>();
+ old_profile->set_guid(updated_profile.guid());
+ autofill::test::SetProfileInfo(old_profile.get(), "Adam", "", "West",
+ "adam.west@gmail.com", "", "", "", "", "", "",
+ "", "");
+
+ std::vector<std::unique_ptr<Contact>> list;
+ list.emplace_back(std::make_unique<Contact>(std::move(old_profile)));
+
+ EXPECT_EQ(list[0]->profile->GetInfo(autofill::NAME_MIDDLE, "en-US"), u"");
+ UpsertContact(updated_profile, list);
+
+ ASSERT_EQ(list.size(), 1u);
+ EXPECT_EQ(list[0]->profile->guid(), updated_profile.guid());
+ EXPECT_EQ(list[0]->profile->GetInfo(autofill::NAME_MIDDLE, "en-US"), u"B.");
+}
+
+TEST(UserDataUtilTest, InsertNewPhoneNumberToList) {
+ autofill::AutofillProfile new_profile;
+ autofill::test::SetProfileInfo(&new_profile, "", "", "", "", "", "", "", "",
+ "", "", "", "+41441234567");
+
+ std::unique_ptr<autofill::AutofillProfile> old_profile =
+ std::make_unique<autofill::AutofillProfile>();
+ autofill::test::SetProfileInfo(old_profile.get(), "", "", "", "", "", "", "",
+ "", "", "", "", "+4144765432");
+
+ std::vector<std::unique_ptr<Contact>> list;
+ list.emplace_back(std::make_unique<Contact>(std::move(old_profile)));
+
+ UpsertContact(new_profile, list);
+
+ ASSERT_EQ(list.size(), 2u);
+ EXPECT_EQ(list[1]->profile->guid(), new_profile.guid());
+ EXPECT_EQ(list[1]->identifier, new_profile.guid());
+ EXPECT_EQ(
+ list[0]->profile->GetInfo(autofill::PHONE_HOME_WHOLE_NUMBER, "en-US"),
+ u"+4144765432");
+ EXPECT_EQ(
+ list[1]->profile->GetInfo(autofill::PHONE_HOME_WHOLE_NUMBER, "en-US"),
+ u"+41441234567");
+}
+
+TEST(UserDataUtilTest, UpdateExistingPhoneNumberInList) {
+ autofill::AutofillProfile updated_profile;
+ autofill::test::SetProfileInfo(&updated_profile, "", "", "", "", "", "", "",
+ "", "", "", "", "+41441234567");
+
+ std::unique_ptr<autofill::AutofillProfile> old_profile =
+ std::make_unique<autofill::AutofillProfile>();
+ old_profile->set_guid(updated_profile.guid());
+ autofill::test::SetProfileInfo(old_profile.get(), "", "", "", "", "", "", "",
+ "", "", "", "", "+4144765432");
+
+ std::vector<std::unique_ptr<Contact>> list;
+ list.emplace_back(std::make_unique<Contact>(std::move(old_profile)));
+
+ EXPECT_EQ(
+ list[0]->profile->GetInfo(autofill::PHONE_HOME_WHOLE_NUMBER, "en-US"),
+ u"+4144765432");
+ UpsertContact(updated_profile, list);
+
+ ASSERT_EQ(list.size(), 1u);
+ EXPECT_EQ(list[0]->profile->guid(), updated_profile.guid());
+ EXPECT_EQ(
+ list[0]->profile->GetInfo(autofill::PHONE_HOME_WHOLE_NUMBER, "en-US"),
+ u"+41441234567");
+}
+
} // namespace
} // namespace user_data
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/wait_for_document_operation.cc b/chromium/components/autofill_assistant/browser/wait_for_document_operation.cc
index 8a39304bf06..081736d26e4 100644
--- a/chromium/components/autofill_assistant/browser/wait_for_document_operation.cc
+++ b/chromium/components/autofill_assistant/browser/wait_for_document_operation.cc
@@ -10,7 +10,7 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/script_executor_delegate.h"
#include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/wait_for_document_operation.h b/chromium/components/autofill_assistant/browser/wait_for_document_operation.h
index 534366c3e0d..7aadca98f05 100644
--- a/chromium/components/autofill_assistant/browser/wait_for_document_operation.h
+++ b/chromium/components/autofill_assistant/browser/wait_for_document_operation.h
@@ -13,9 +13,9 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/script_executor_delegate.h"
#include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
+class ElementFinderResult;
// Waits for a minimal state of the document or times out if the state is not
// reached in time.
diff --git a/chromium/components/autofill_assistant/browser/wait_for_dom_operation.cc b/chromium/components/autofill_assistant/browser/wait_for_dom_operation.cc
index fc859a11dd7..59a10e2bfd6 100644
--- a/chromium/components/autofill_assistant/browser/wait_for_dom_operation.cc
+++ b/chromium/components/autofill_assistant/browser/wait_for_dom_operation.cc
@@ -30,11 +30,15 @@ WaitForDomOperation::WaitForDomOperation(
->GetScriptParameters()
.GetEnableObserverWaitForDom()
.value_or(false)),
- observer_(observer),
check_elements_(std::move(check_elements)),
callback_(std::move(callback)),
timeout_warning_delay_(delegate_->GetSettings().warning_delay),
- retry_timer_(delegate_->GetSettings().periodic_element_check_interval) {}
+ retry_timer_(delegate_->GetSettings().periodic_element_check_interval) {
+ if (observer) {
+ observers_.emplace_back(observer);
+ }
+ observers_.emplace_back(ui_delegate_);
+}
WaitForDomOperation::~WaitForDomOperation() {
delegate_->RemoveNavigationListener(this);
@@ -165,12 +169,14 @@ void WaitForDomOperation::RunChecks(
base::Unretained(this), std::move(report_attempt_result)));
if (use_observers_) {
batch_element_checker_->EnableObserver(
- /* max_wait_time= */ max_wait_time_ -
- wait_time_stopwatch_.TotalElapsed(),
- /* periodic_check_interval= */
- delegate_->GetSettings().periodic_element_check_interval,
- /* extra_timeout= */
- delegate_->GetSettings().selector_observer_extra_timeout);
+ {/* max_wait_time= */ max_wait_time_ -
+ wait_time_stopwatch_.TotalElapsed(),
+ /* min_check_interval= */
+ delegate_->GetSettings().periodic_element_check_interval,
+ /* extra_timeout= */
+ delegate_->GetSettings().selector_observer_extra_timeout,
+ /* debounce_interval */
+ delegate_->GetSettings().selector_observer_debounce_interval});
}
batch_element_checker_->Run(delegate_->GetWebController());
}
@@ -218,8 +224,9 @@ void WaitForDomOperation::OnAllChecksDone(
void WaitForDomOperation::RunInterrupt(const std::string& path) {
batch_element_checker_.reset();
- if (observer_)
- observer_->OnInterruptStarted();
+ for (auto* observer : observers_) {
+ observer->OnInterruptStarted();
+ }
SavePreInterruptState();
ran_interrupts_.insert(path);
@@ -246,8 +253,9 @@ void WaitForDomOperation::OnInterruptDone(
RunCallbackWithResult(ClientStatus(INTERRUPT_FAILED), &result);
return;
}
- if (observer_)
- observer_->OnInterruptFinished();
+ for (auto* observer : observers_) {
+ observer->OnInterruptFinished();
+ }
RestorePreInterruptState();
RestorePreInterruptScroll();
diff --git a/chromium/components/autofill_assistant/browser/wait_for_dom_operation.h b/chromium/components/autofill_assistant/browser/wait_for_dom_operation.h
index d2e7ff34582..c33d8446a1c 100644
--- a/chromium/components/autofill_assistant/browser/wait_for_dom_operation.h
+++ b/chromium/components/autofill_assistant/browser/wait_for_dom_operation.h
@@ -121,7 +121,7 @@ class WaitForDomOperation : public ScriptExecutor::Listener,
const base::TimeDelta max_wait_time_;
const bool allow_interrupt_;
const bool use_observers_;
- raw_ptr<WaitForDomObserver> observer_;
+ std::vector<WaitForDomObserver*> observers_;
base::RepeatingCallback<void(BatchElementChecker*,
base::OnceCallback<void(const ClientStatus&)>)>
check_elements_;
diff --git a/chromium/components/autofill_assistant/browser/web/base_element_finder.cc b/chromium/components/autofill_assistant/browser/web/base_element_finder.cc
new file mode 100644
index 00000000000..ef74f9c2cd4
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/base_element_finder.cc
@@ -0,0 +1,11 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/web/base_element_finder.h"
+
+namespace autofill_assistant {
+
+BaseElementFinder::~BaseElementFinder() = default;
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/web/base_element_finder.h b/chromium/components/autofill_assistant/browser/web/base_element_finder.h
new file mode 100644
index 00000000000..b9305f7947c
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/base_element_finder.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_BASE_ELEMENT_FINDER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_BASE_ELEMENT_FINDER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+
+namespace autofill_assistant {
+class ClientStatus;
+class ElementFinderResult;
+
+class BaseElementFinder {
+ public:
+ using Callback =
+ base::OnceCallback<void(const ClientStatus&,
+ std::unique_ptr<ElementFinderResult>)>;
+
+ virtual ~BaseElementFinder();
+
+ // Start looking for the element and return it through |callback| with
+ // a status. If |start_element| is not empty, use it as a starting point
+ // instead of starting from the main frame.
+ virtual void Start(const ElementFinderResult& start_element,
+ Callback callback) = 0;
+
+ // Get the log information for the last run. Should only be run after the
+ // run has completed (i.e. |callback_| has been called).
+ virtual ElementFinderInfoProto GetLogInfo() const = 0;
+
+ // Returns the backend node id that was previously collected.
+ virtual int GetBackendNodeId() const = 0;
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_BASE_ELEMENT_FINDER_H_
diff --git a/chromium/components/autofill_assistant/browser/web/batch_element_checker_browsertest.cc b/chromium/components/autofill_assistant/browser/web/batch_element_checker_browsertest.cc
index b304148aead..38a800b185d 100644
--- a/chromium/components/autofill_assistant/browser/web/batch_element_checker_browsertest.cc
+++ b/chromium/components/autofill_assistant/browser/web/batch_element_checker_browsertest.cc
@@ -101,6 +101,12 @@ class BatchElementCheckerBrowserTest
return proto;
}
+ static SelectorObserver::Settings SelectorObserverDefaultSettings(
+ base::TimeDelta max_wait_time) {
+ return {max_wait_time, base::Seconds(1), base::Seconds(15),
+ base::Milliseconds(100)};
+ }
+
// Run Observer BatchElementChecker on the provided conditions. The second
// value in the pairs (bool) is the match expectation.
void RunObserverBatchElementChecker(
@@ -123,8 +129,7 @@ class BatchElementCheckerBrowserTest
ObserverBatchElementCheckerAllDoneCallback,
run_loop.QuitClosure(), &expected_results, &actual_results));
- checker.EnableObserver(base::Seconds(30), base::Seconds(1),
- base::Seconds(15));
+ checker.EnableObserver(SelectorObserverDefaultSettings(base::Seconds(30)));
checker.Run(web_controller_.get());
run_loop.Run();
EXPECT_EQ(web_controller_->pending_workers_.size(), 0u);
@@ -335,7 +340,7 @@ IN_PROC_BROWSER_TEST_F(BatchElementCheckerBrowserTest, SelectorObserver) {
/* proto = */
Selector({"#iframeExternal", ".dynamic.about-2-seconds"}).proto,
/* strict = */ true}},
- base::Seconds(30), base::Seconds(1), base::Seconds(15), update_callback);
+ SelectorObserverDefaultSettings(base::Seconds(30)), update_callback);
run_loop.Run();
ASSERT_TRUE(expected_updates.empty());
@@ -375,7 +380,7 @@ IN_PROC_BROWSER_TEST_F(BatchElementCheckerBrowserTest,
{{/* selector_id = */ button_id,
/* proto = */ Selector({"#iframeRedirecting", "#button"}).proto,
/* strict = */ true}},
- base::Seconds(30), base::Seconds(1), base::Seconds(15), update_callback);
+ SelectorObserverDefaultSettings(base::Seconds(30)), update_callback);
run_loop.Run();
}
@@ -411,7 +416,7 @@ IN_PROC_BROWSER_TEST_F(BatchElementCheckerBrowserTest,
{{/* selector_id = */ SelectorObserver::SelectorId(1),
/* proto = */ Selector({"#does_not_exist"}).proto,
/* strict = */ true}},
- base::Milliseconds(300), base::Seconds(1), base::Seconds(15),
+ SelectorObserverDefaultSettings(base::Milliseconds(300)),
mock_callback.Get());
run_loop.Run();
@@ -454,7 +459,7 @@ IN_PROC_BROWSER_TEST_F(BatchElementCheckerBrowserTest,
{{/* selector_id = */ button_id,
/* proto = */ Selector({"#iframe", "#button"}).proto,
/* strict = */ true}},
- base::Milliseconds(1), base::Seconds(1), base::Seconds(15),
+ SelectorObserverDefaultSettings(base::Milliseconds(1)),
update_callback.Get());
run_loop.Run();
diff --git a/chromium/components/autofill_assistant/browser/web/check_on_top_worker.cc b/chromium/components/autofill_assistant/browser/web/check_on_top_worker.cc
index 7acdc2af7e6..eef35bb739e 100644
--- a/chromium/components/autofill_assistant/browser/web/check_on_top_worker.cc
+++ b/chromium/components/autofill_assistant/browser/web/check_on_top_worker.cc
@@ -7,6 +7,8 @@
#include <vector>
#include "base/logging.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
+#include "components/autofill_assistant/browser/web/js_snippets.h"
#include "components/autofill_assistant/browser/web/web_controller_util.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/web/check_on_top_worker.h b/chromium/components/autofill_assistant/browser/web/check_on_top_worker.h
index 0cc1661cfb6..9aa9338a8d9 100644
--- a/chromium/components/autofill_assistant/browser/web/check_on_top_worker.h
+++ b/chromium/components/autofill_assistant/browser/web/check_on_top_worker.h
@@ -13,10 +13,10 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/types_runtime.h"
#include "components/autofill_assistant/browser/devtools/devtools_client.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
#include "components/autofill_assistant/browser/web/web_controller_worker.h"
namespace autofill_assistant {
+class ElementFinderResult;
// Worker class to check whether an element is on top, in all frames.
class CheckOnTopWorker : public WebControllerWorker {
diff --git a/chromium/components/autofill_assistant/browser/web/click_or_tap_worker.cc b/chromium/components/autofill_assistant/browser/web/click_or_tap_worker.cc
index bde3b44d25f..e0c1cc5a34a 100644
--- a/chromium/components/autofill_assistant/browser/web/click_or_tap_worker.cc
+++ b/chromium/components/autofill_assistant/browser/web/click_or_tap_worker.cc
@@ -7,6 +7,7 @@
#include <vector>
#include "base/time/time.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/web_controller_util.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/web/click_or_tap_worker.h b/chromium/components/autofill_assistant/browser/web/click_or_tap_worker.h
index dbdc25bb125..7326f7ecf6b 100644
--- a/chromium/components/autofill_assistant/browser/web/click_or_tap_worker.h
+++ b/chromium/components/autofill_assistant/browser/web/click_or_tap_worker.h
@@ -15,11 +15,11 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/types_input.h"
#include "components/autofill_assistant/browser/devtools/devtools_client.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
#include "components/autofill_assistant/browser/web/element_position_getter.h"
#include "components/autofill_assistant/browser/web/web_controller_worker.h"
namespace autofill_assistant {
+class ElementFinderResult;
// Worker class for sending click or tap events.
class ClickOrTapWorker : public WebControllerWorker {
diff --git a/chromium/components/autofill_assistant/browser/web/css_element_finder.cc b/chromium/components/autofill_assistant/browser/web/css_element_finder.cc
new file mode 100644
index 00000000000..e268a10b3a2
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/css_element_finder.cc
@@ -0,0 +1,754 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/web/css_element_finder.h"
+
+#include <utility>
+
+#include "components/autofill_assistant/browser/devtools/devtools_client.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/user_data.h"
+#include "components/autofill_assistant/browser/user_data_util.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
+#include "components/autofill_assistant/browser/web/js_filter_builder.h"
+#include "components/autofill_assistant/browser/web/web_controller_util.h"
+#include "components/autofill_assistant/content/browser/content_autofill_assistant_driver.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+
+namespace autofill_assistant {
+
+namespace {
+// Javascript code to get document root element.
+const char kGetDocumentElement[] = "document.documentElement;";
+
+const char kGetArrayElement[] = "function(index) { return this[index]; }";
+
+bool ConvertPseudoType(const PseudoType pseudo_type,
+ dom::PseudoType* pseudo_type_output) {
+ switch (pseudo_type) {
+ case PseudoType::UNDEFINED:
+ break;
+ case PseudoType::FIRST_LINE:
+ *pseudo_type_output = dom::PseudoType::FIRST_LINE;
+ return true;
+ case PseudoType::FIRST_LETTER:
+ *pseudo_type_output = dom::PseudoType::FIRST_LETTER;
+ return true;
+ case PseudoType::BEFORE:
+ *pseudo_type_output = dom::PseudoType::BEFORE;
+ return true;
+ case PseudoType::AFTER:
+ *pseudo_type_output = dom::PseudoType::AFTER;
+ return true;
+ case PseudoType::BACKDROP:
+ *pseudo_type_output = dom::PseudoType::BACKDROP;
+ return true;
+ case PseudoType::SELECTION:
+ *pseudo_type_output = dom::PseudoType::SELECTION;
+ return true;
+ case PseudoType::FIRST_LINE_INHERITED:
+ *pseudo_type_output = dom::PseudoType::FIRST_LINE_INHERITED;
+ return true;
+ case PseudoType::SCROLLBAR:
+ *pseudo_type_output = dom::PseudoType::SCROLLBAR;
+ return true;
+ case PseudoType::SCROLLBAR_THUMB:
+ *pseudo_type_output = dom::PseudoType::SCROLLBAR_THUMB;
+ return true;
+ case PseudoType::SCROLLBAR_BUTTON:
+ *pseudo_type_output = dom::PseudoType::SCROLLBAR_BUTTON;
+ return true;
+ case PseudoType::SCROLLBAR_TRACK:
+ *pseudo_type_output = dom::PseudoType::SCROLLBAR_TRACK;
+ return true;
+ case PseudoType::SCROLLBAR_TRACK_PIECE:
+ *pseudo_type_output = dom::PseudoType::SCROLLBAR_TRACK_PIECE;
+ return true;
+ case PseudoType::SCROLLBAR_CORNER:
+ *pseudo_type_output = dom::PseudoType::SCROLLBAR_CORNER;
+ return true;
+ case PseudoType::RESIZER:
+ *pseudo_type_output = dom::PseudoType::RESIZER;
+ return true;
+ case PseudoType::INPUT_LIST_BUTTON:
+ *pseudo_type_output = dom::PseudoType::INPUT_LIST_BUTTON;
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+CssElementFinder::CssElementFinder(content::WebContents* web_contents,
+ DevtoolsClient* devtools_client,
+ const UserData* user_data,
+ const ElementFinderResultType result_type,
+ const Selector& selector)
+ : web_contents_(web_contents),
+ devtools_client_(devtools_client),
+ user_data_(user_data),
+ result_type_(result_type),
+ selector_(selector) {}
+
+CssElementFinder::~CssElementFinder() = default;
+
+void CssElementFinder::Start(const ElementFinderResult& start_element,
+ Callback callback) {
+ callback_ = std::move(callback);
+
+ selector_proto_ = selector_.proto;
+ ClientStatus resolve_status =
+ user_data::ResolveSelectorUserData(&selector_proto_, user_data_);
+ if (!resolve_status.ok()) {
+ SendResult(resolve_status, ElementFinderResult::EmptyResult());
+ return;
+ }
+
+ auto* frame = start_element.render_frame_host();
+ if (frame == nullptr) {
+ frame = web_contents_->GetPrimaryMainFrame();
+ }
+ current_frame_global_id_ = frame->GetGlobalId();
+ current_frame_devtools_id_ = start_element.node_frame_id();
+ frame_stack_ = start_element.frame_stack();
+
+ if (start_element.object_id().empty()) {
+ GetDocumentElement();
+ } else {
+ current_matches_.emplace_back(start_element.object_id());
+ ExecuteNextTask();
+ }
+}
+
+ElementFinderInfoProto CssElementFinder::GetLogInfo() const {
+ DCHECK(!callback_); // Run after finish.
+
+ ElementFinderInfoProto info;
+ if (!client_status_.ok()) {
+ info.set_failed_filter_index_range_start(current_filter_index_range_start_);
+ info.set_failed_filter_index_range_end(next_filter_index_);
+ info.set_get_document_failed(get_document_failed_);
+ }
+
+ return info;
+}
+
+int CssElementFinder::GetBackendNodeId() const {
+ return backend_node_id_.value_or(0);
+}
+
+void CssElementFinder::GiveUpWithError(const ClientStatus& status) {
+ DCHECK(!status.ok());
+ if (!callback_) {
+ return;
+ }
+
+ SendResult(status, ElementFinderResult::EmptyResult());
+}
+
+void CssElementFinder::ResultFound(const std::string& object_id) {
+ if (!callback_) {
+ return;
+ }
+
+ devtools_client_->GetDOM()->DescribeNode(
+ dom::DescribeNodeParams::Builder().SetObjectId(object_id).Build(),
+ current_frame_devtools_id_,
+ base::BindOnce(&CssElementFinder::OnDescribeNodeForId,
+ weak_ptr_factory_.GetWeakPtr(), object_id));
+}
+
+void CssElementFinder::OnDescribeNodeForId(
+ const std::string& object_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::DescribeNodeResult> node_result) {
+ if (node_result && node_result->GetNode()) {
+ backend_node_id_ = node_result->GetNode()->GetBackendNodeId();
+ }
+ BuildAndSendResult(object_id);
+}
+
+void CssElementFinder::BuildAndSendResult(const std::string& object_id) {
+ ElementFinderResult result;
+ result.SetRenderFrameHostGlobalId(current_frame_global_id_);
+ result.SetObjectId(object_id);
+ result.SetBackendNodeId(backend_node_id_);
+ result.SetNodeFrameId(current_frame_devtools_id_);
+ result.SetFrameStack(frame_stack_);
+
+ SendResult(OkClientStatus(), result);
+}
+
+void CssElementFinder::SendResult(const ClientStatus& status,
+ const ElementFinderResult& result) {
+ client_status_ = status;
+ DCHECK(callback_);
+ std::move(callback_).Run(status,
+ std::make_unique<ElementFinderResult>(result));
+}
+
+void CssElementFinder::ExecuteNextTask() {
+ const auto& filters = selector_proto_.filters();
+
+ if (next_filter_index_ >= filters.size()) {
+ std::string object_id;
+ switch (result_type_) {
+ case ElementFinderResultType::kExactlyOneMatch:
+ if (!ConsumeOneMatchOrFail(object_id)) {
+ return;
+ }
+ break;
+
+ case ElementFinderResultType::kAnyMatch:
+ if (!ConsumeMatchAtOrFail(0, object_id)) {
+ return;
+ }
+ break;
+
+ case ElementFinderResultType::kMatchArray:
+ if (!ConsumeMatchArrayOrFail(object_id)) {
+ return;
+ }
+ break;
+ }
+ ResultFound(object_id);
+ return;
+ }
+
+ current_filter_index_range_start_ = next_filter_index_;
+ const auto& filter = filters.Get(next_filter_index_);
+ switch (filter.filter_case()) {
+ case SelectorProto::Filter::kEnterFrame: {
+ std::string object_id;
+ if (!ConsumeOneMatchOrFail(object_id))
+ return;
+
+ // The above fails if there is more than one frame. To preserve
+ // backward-compatibility with the previous, lax behavior, callers must
+ // add pick_one before enter_frame. TODO(b/155264465): allow searching in
+ // more than one frame.
+ next_filter_index_++;
+ EnterFrame(object_id);
+ return;
+ }
+
+ case SelectorProto::Filter::kPseudoType: {
+ std::vector<std::string> matches;
+ if (!ConsumeAllMatchesOrFail(matches))
+ return;
+
+ next_filter_index_++;
+ matching_pseudo_elements_ = true;
+ ResolvePseudoElement(filter.pseudo_type(), matches);
+ return;
+ }
+
+ case SelectorProto::Filter::kNthMatch: {
+ // TODO(b/205676462): This could be done with javascript like in
+ // |SelectorObserver|.
+ std::string object_id;
+ if (!ConsumeMatchAtOrFail(filter.nth_match().index(), object_id))
+ return;
+
+ next_filter_index_++;
+ current_matches_ = {object_id};
+ ExecuteNextTask();
+ return;
+ }
+
+ case SelectorProto::Filter::kCssSelector:
+ case SelectorProto::Filter::kInnerText:
+ case SelectorProto::Filter::kValue:
+ case SelectorProto::Filter::kProperty:
+ case SelectorProto::Filter::kBoundingBox:
+ case SelectorProto::Filter::kPseudoElementContent:
+ case SelectorProto::Filter::kMatchCssSelector:
+ case SelectorProto::Filter::kCssStyle:
+ case SelectorProto::Filter::kLabelled:
+ case SelectorProto::Filter::kOnTop:
+ case SelectorProto::Filter::kParent: {
+ std::vector<std::string> matches;
+ if (!ConsumeAllMatchesOrFail(matches))
+ return;
+
+ JsFilterBuilder js_filter;
+ for (int i = next_filter_index_; i < filters.size(); i++) {
+ if (!js_filter.AddFilter(filters.Get(i))) {
+ break;
+ }
+ next_filter_index_++;
+ }
+ ApplyJsFilters(js_filter, matches);
+ return;
+ }
+
+ case SelectorProto::Filter::FILTER_NOT_SET:
+ VLOG(1) << __func__ << " Unset or unknown filter in " << filter << " in "
+ << selector_;
+ GiveUpWithError(ClientStatus(INVALID_SELECTOR));
+ return;
+ }
+}
+
+bool CssElementFinder::ConsumeOneMatchOrFail(std::string& object_id_out) {
+ if (current_matches_.size() > 1) {
+ VLOG(1) << __func__ << " Got " << current_matches_.size() << " matches for "
+ << selector_ << ", when only 1 was expected.";
+ GiveUpWithError(ClientStatus(TOO_MANY_ELEMENTS));
+ return false;
+ }
+ if (current_matches_.empty()) {
+ GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+ return false;
+ }
+
+ object_id_out = current_matches_[0];
+ current_matches_.clear();
+ return true;
+}
+
+bool CssElementFinder::ConsumeMatchAtOrFail(size_t index,
+ std::string& object_id_out) {
+ if (index < current_matches_.size()) {
+ object_id_out = current_matches_[index];
+ current_matches_.clear();
+ return true;
+ }
+
+ GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+ return false;
+}
+
+bool CssElementFinder::ConsumeAllMatchesOrFail(
+ std::vector<std::string>& matches_out) {
+ if (!current_matches_.empty()) {
+ matches_out = std::move(current_matches_);
+ current_matches_.clear();
+ return true;
+ }
+ GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+ return false;
+}
+
+bool CssElementFinder::ConsumeMatchArrayOrFail(std::string& array_object_id) {
+ if (!current_matches_js_array_.empty()) {
+ array_object_id = current_matches_js_array_;
+ current_matches_js_array_.clear();
+ return true;
+ }
+
+ if (current_matches_.empty()) {
+ GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+ return false;
+ }
+
+ MoveMatchesToJSArrayRecursive(/* index= */ 0);
+ return false;
+}
+
+void CssElementFinder::MoveMatchesToJSArrayRecursive(size_t index) {
+ if (index >= current_matches_.size()) {
+ current_matches_.clear();
+ ExecuteNextTask();
+ return;
+ }
+
+ // Push the value at |current_matches_[index]| to |current_matches_js_array_|.
+ std::string function;
+ std::vector<std::unique_ptr<runtime::CallArgument>> arguments;
+ if (index == 0) {
+ // Create an array containing a single element.
+ function = "function() { return [this]; }";
+ } else {
+ // Add an element to an existing array.
+ function = "function(dest) { dest.push(this); }";
+ AddRuntimeCallArgumentObjectId(current_matches_js_array_, &arguments);
+ }
+
+ devtools_client_->GetRuntime()->CallFunctionOn(
+ runtime::CallFunctionOnParams::Builder()
+ .SetObjectId(current_matches_[index])
+ .SetArguments(std::move(arguments))
+ .SetFunctionDeclaration(function)
+ .Build(),
+ current_frame_devtools_id_,
+ base::BindOnce(&CssElementFinder::OnMoveMatchesToJSArrayRecursive,
+ weak_ptr_factory_.GetWeakPtr(), index));
+}
+
+void CssElementFinder::OnMoveMatchesToJSArrayRecursive(
+ size_t index,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::CallFunctionOnResult> result) {
+ ClientStatus status =
+ CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
+ if (!status.ok()) {
+ VLOG(1) << __func__ << ": Failed to push value to JS array.";
+ GiveUpWithError(status);
+ return;
+ }
+
+ // We just created an array which contains the first element. We store its ID
+ // in |current_matches_js_array_|.
+ if (index == 0 &&
+ !SafeGetObjectId(result->GetResult(), &current_matches_js_array_)) {
+ VLOG(1) << __func__ << " Failed to get array ID.";
+ GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+ return;
+ }
+
+ // Continue the recursion to push the other values into the array.
+ MoveMatchesToJSArrayRecursive(index + 1);
+}
+
+void CssElementFinder::GetDocumentElement() {
+ devtools_client_->GetRuntime()->Evaluate(
+ std::string(kGetDocumentElement), current_frame_devtools_id_,
+ base::BindOnce(&CssElementFinder::OnGetDocumentElement,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CssElementFinder::OnGetDocumentElement(
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::EvaluateResult> result) {
+ ClientStatus status =
+ CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
+ if (!status.ok()) {
+ VLOG(1) << __func__ << " Failed to get document root element.";
+ get_document_failed_ = true;
+ GiveUpWithError(status);
+ return;
+ }
+ std::string object_id;
+ if (!SafeGetObjectId(result->GetResult(), &object_id)) {
+ VLOG(1) << __func__ << " Failed to get document root element.";
+ get_document_failed_ = true;
+ GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+ return;
+ }
+
+ // Use the node as root for the rest of the evaluation.
+ current_matches_.emplace_back(object_id);
+
+ ExecuteNextTask();
+}
+
+void CssElementFinder::ApplyJsFilters(
+ const JsFilterBuilder& builder,
+ const std::vector<std::string>& object_ids) {
+ DCHECK(!object_ids.empty()); // Guaranteed by ExecuteNextTask()
+ PrepareBatchTasks(object_ids.size());
+ std::string function = builder.BuildFunction();
+ for (size_t task_id = 0; task_id < object_ids.size(); task_id++) {
+ devtools_client_->GetRuntime()->CallFunctionOn(
+ runtime::CallFunctionOnParams::Builder()
+ .SetObjectId(object_ids[task_id])
+ .SetArguments(builder.BuildArgumentList())
+ .SetFunctionDeclaration(function)
+ .Build(),
+ current_frame_devtools_id_,
+ base::BindOnce(&CssElementFinder::OnApplyJsFilters,
+ weak_ptr_factory_.GetWeakPtr(), task_id));
+ }
+}
+
+void CssElementFinder::OnApplyJsFilters(
+ size_t task_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::CallFunctionOnResult> result) {
+ if (!result) {
+ // It is possible for a document element to already exist, but not be
+ // available yet to query because the document hasn't been loaded. This
+ // results in OnQuerySelectorAll getting a nullptr result. For this specific
+ // call, it is expected.
+ VLOG(1) << __func__ << ": Context doesn't exist yet to query frame "
+ << frame_stack_.size() << " of " << selector_;
+ GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+ return;
+ }
+ ClientStatus status =
+ CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
+ if (!status.ok()) {
+ VLOG(1) << __func__ << ": Failed to query selector for frame "
+ << frame_stack_.size() << " of " << selector_ << ": " << status;
+ GiveUpWithError(status);
+ return;
+ }
+
+ // The result can be empty (nothing found), an array (multiple matches
+ // found) or a single node.
+ std::string object_id;
+ if (!SafeGetObjectId(result->GetResult(), &object_id)) {
+ ReportNoMatchingElement(task_id);
+ return;
+ }
+
+ if (result->GetResult()->HasSubtype() &&
+ result->GetResult()->GetSubtype() ==
+ runtime::RemoteObjectSubtype::ARRAY) {
+ ReportMatchingElementsArray(task_id, object_id);
+ return;
+ }
+
+ ReportMatchingElement(task_id, object_id);
+}
+
+void CssElementFinder::ResolvePseudoElement(
+ PseudoType proto_pseudo_type,
+ const std::vector<std::string>& object_ids) {
+ dom::PseudoType pseudo_type;
+ if (!ConvertPseudoType(proto_pseudo_type, &pseudo_type)) {
+ VLOG(1) << __func__ << ": Unsupported pseudo-type "
+ << PseudoTypeName(proto_pseudo_type);
+ GiveUpWithError(ClientStatus(INVALID_ACTION));
+ return;
+ }
+
+ DCHECK(!object_ids.empty()); // Guaranteed by ExecuteNextTask()
+ PrepareBatchTasks(object_ids.size());
+ for (size_t task_id = 0; task_id < object_ids.size(); task_id++) {
+ devtools_client_->GetDOM()->DescribeNode(
+ dom::DescribeNodeParams::Builder()
+ .SetObjectId(object_ids[task_id])
+ .Build(),
+ current_frame_devtools_id_,
+ base::BindOnce(&CssElementFinder::OnDescribeNodeForPseudoElement,
+ weak_ptr_factory_.GetWeakPtr(), pseudo_type, task_id));
+ }
+}
+
+void CssElementFinder::OnDescribeNodeForPseudoElement(
+ dom::PseudoType pseudo_type,
+ size_t task_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::DescribeNodeResult> result) {
+ if (!result || !result->GetNode()) {
+ VLOG(1) << __func__ << " Failed to describe the node for pseudo element.";
+ GiveUpWithError(
+ UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
+ return;
+ }
+
+ auto* node = result->GetNode();
+ if (node->HasPseudoElements()) {
+ for (const auto& pseudo_element : *(node->GetPseudoElements())) {
+ if (pseudo_element->HasPseudoType() &&
+ pseudo_element->GetPseudoType() == pseudo_type) {
+ devtools_client_->GetDOM()->ResolveNode(
+ dom::ResolveNodeParams::Builder()
+ .SetBackendNodeId(pseudo_element->GetBackendNodeId())
+ .Build(),
+ current_frame_devtools_id_,
+ base::BindOnce(&CssElementFinder::OnResolveNodeForPseudoElement,
+ weak_ptr_factory_.GetWeakPtr(), task_id));
+ return;
+ }
+ }
+ }
+
+ ReportNoMatchingElement(task_id);
+}
+
+void CssElementFinder::OnResolveNodeForPseudoElement(
+ size_t task_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::ResolveNodeResult> result) {
+ if (result && result->GetObject() && result->GetObject()->HasObjectId()) {
+ ReportMatchingElement(task_id, result->GetObject()->GetObjectId());
+ return;
+ }
+
+ ReportNoMatchingElement(task_id);
+}
+
+void CssElementFinder::EnterFrame(const std::string& object_id) {
+ devtools_client_->GetDOM()->DescribeNode(
+ dom::DescribeNodeParams::Builder().SetObjectId(object_id).Build(),
+ current_frame_devtools_id_,
+ base::BindOnce(&CssElementFinder::OnDescribeNodeForFrame,
+ weak_ptr_factory_.GetWeakPtr(), object_id));
+}
+
+void CssElementFinder::OnDescribeNodeForFrame(
+ const std::string& object_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::DescribeNodeResult> result) {
+ if (!result || !result->GetNode()) {
+ VLOG(1) << __func__ << " Failed to describe the node.";
+ GiveUpWithError(
+ UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
+ return;
+ }
+
+ auto* node = result->GetNode();
+ std::vector<int> backend_ids;
+
+ if (node->GetNodeName() == "IFRAME") {
+ // See: b/206647825
+ if (!node->HasFrameId()) {
+ NOTREACHED() << "Frame without ID"; // Ensure all frames have an id.
+ GiveUpWithError(ClientStatus(FRAME_HOST_NOT_FOUND));
+ return;
+ }
+
+ frame_stack_.push_back({object_id, current_frame_devtools_id_});
+
+ auto* frame =
+ FindCorrespondingRenderFrameHost(node->GetFrameId(), web_contents_);
+ if (!frame) {
+ VLOG(1) << __func__ << " Failed to find corresponding owner frame.";
+ GiveUpWithError(ClientStatus(FRAME_HOST_NOT_FOUND));
+ return;
+ }
+ current_frame_global_id_ = frame->GetGlobalId();
+
+ if (node->HasContentDocument()) {
+ // If the frame has a ContentDocument it's considered a local frame. In
+ // this case, current frame doesn't change and can directly use the
+ // content document as root for the evaluation.
+ backend_ids.emplace_back(node->GetContentDocument()->GetBackendNodeId());
+ } else {
+ current_frame_devtools_id_ = node->GetFrameId();
+ // Kick off another find element chain to walk down the OOP iFrame.
+ GetDocumentElement();
+ return;
+ }
+ }
+
+ if (node->HasShadowRoots()) {
+ // TODO(crbug.com/806868): Support multiple shadow roots.
+ backend_ids.emplace_back(
+ node->GetShadowRoots()->front()->GetBackendNodeId());
+ }
+
+ if (!backend_ids.empty()) {
+ devtools_client_->GetDOM()->ResolveNode(
+ dom::ResolveNodeParams::Builder()
+ .SetBackendNodeId(backend_ids[0])
+ .Build(),
+ current_frame_devtools_id_,
+ base::BindOnce(&CssElementFinder::OnResolveNode,
+ weak_ptr_factory_.GetWeakPtr()));
+ return;
+ }
+
+ // Element was not a frame and didn't have shadow dom. This is unexpected, but
+ // to remain backward compatible, don't complain and just continue filtering
+ // with the current element as root.
+ current_matches_.emplace_back(object_id);
+ ExecuteNextTask();
+}
+
+void CssElementFinder::OnResolveNode(
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::ResolveNodeResult> result) {
+ if (!result || !result->GetObject() || !result->GetObject()->HasObjectId()) {
+ VLOG(1) << __func__ << " Failed to resolve object id from backend id.";
+ GiveUpWithError(
+ UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
+ return;
+ }
+
+ // Use the node as root for the rest of the evaluation.
+ current_matches_.emplace_back(result->GetObject()->GetObjectId());
+ ExecuteNextTask();
+}
+
+void CssElementFinder::PrepareBatchTasks(int n) {
+ tasks_results_.clear();
+ tasks_results_.resize(n);
+}
+
+void CssElementFinder::ReportMatchingElement(size_t task_id,
+ const std::string& object_id) {
+ tasks_results_[task_id] =
+ std::make_unique<std::vector<std::string>>(1, object_id);
+ MaybeFinalizeBatchTasks();
+}
+
+void CssElementFinder::ReportNoMatchingElement(size_t task_id) {
+ tasks_results_[task_id] = std::make_unique<std::vector<std::string>>();
+ MaybeFinalizeBatchTasks();
+}
+
+void CssElementFinder::ReportMatchingElementsArray(
+ size_t task_id,
+ const std::string& array_object_id) {
+ // Recursively add each element ID to a vector then report it as this task
+ // result.
+ ReportMatchingElementsArrayRecursive(
+ task_id, array_object_id, std::make_unique<std::vector<std::string>>(),
+ /* index= */ 0);
+}
+
+void CssElementFinder::ReportMatchingElementsArrayRecursive(
+ size_t task_id,
+ const std::string& array_object_id,
+ std::unique_ptr<std::vector<std::string>> acc,
+ int index) {
+ std::vector<std::unique_ptr<runtime::CallArgument>> arguments;
+ AddRuntimeCallArgument(index, &arguments);
+ devtools_client_->GetRuntime()->CallFunctionOn(
+ runtime::CallFunctionOnParams::Builder()
+ .SetObjectId(array_object_id)
+ .SetArguments(std::move(arguments))
+ .SetFunctionDeclaration(std::string(kGetArrayElement))
+ .Build(),
+ current_frame_devtools_id_,
+ base::BindOnce(&CssElementFinder::OnReportMatchingElementsArrayRecursive,
+ weak_ptr_factory_.GetWeakPtr(), task_id, array_object_id,
+ std::move(acc), index));
+}
+
+void CssElementFinder::OnReportMatchingElementsArrayRecursive(
+ size_t task_id,
+ const std::string& array_object_id,
+ std::unique_ptr<std::vector<std::string>> acc,
+ int index,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::CallFunctionOnResult> result) {
+ ClientStatus status =
+ CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
+ if (!status.ok()) {
+ VLOG(1) << __func__ << ": Failed to get element from array for "
+ << selector_;
+ GiveUpWithError(status);
+ return;
+ }
+
+ std::string object_id;
+ if (!SafeGetObjectId(result->GetResult(), &object_id)) {
+ // We've reached the end of the array.
+ tasks_results_[task_id] = std::move(acc);
+ MaybeFinalizeBatchTasks();
+ return;
+ }
+
+ acc->emplace_back(object_id);
+
+ // Fetch the next element.
+ ReportMatchingElementsArrayRecursive(task_id, array_object_id, std::move(acc),
+ index + 1);
+}
+
+void CssElementFinder::MaybeFinalizeBatchTasks() {
+ // Return early if one of the tasks is still pending.
+ for (const auto& result : tasks_results_) {
+ if (!result) {
+ return;
+ }
+ }
+
+ // Add all matching elements to current_matches_.
+ for (const auto& result : tasks_results_) {
+ current_matches_.insert(current_matches_.end(), result->begin(),
+ result->end());
+ }
+ tasks_results_.clear();
+
+ ExecuteNextTask();
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/web/css_element_finder.h b/chromium/components/autofill_assistant/browser/web/css_element_finder.h
new file mode 100644
index 00000000000..558f0e27ab1
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/css_element_finder.h
@@ -0,0 +1,269 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_CSS_ELEMENT_FINDER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_CSS_ELEMENT_FINDER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/action_value.pb.h"
+#include "components/autofill_assistant/browser/client_status.h"
+#include "components/autofill_assistant/browser/devtools/devtools/domains/types_dom.h"
+#include "components/autofill_assistant/browser/devtools/devtools/domains/types_runtime.h"
+#include "components/autofill_assistant/browser/devtools/devtools_client.h"
+#include "components/autofill_assistant/browser/selector.h"
+#include "components/autofill_assistant/browser/web/base_element_finder.h"
+#include "components/autofill_assistant/browser/web/element.h"
+#include "components/autofill_assistant/browser/web/element_finder_result_type.h"
+#include "components/autofill_assistant/browser/web/js_filter_builder.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace content {
+class WebContents;
+struct GlobalRenderFrameHostId;
+} // namespace content
+
+namespace autofill_assistant {
+class DevtoolsClient;
+class UserData;
+class ElementFinderResult;
+
+class CssElementFinder : public BaseElementFinder {
+ public:
+ CssElementFinder(content::WebContents* web_contents,
+ DevtoolsClient* devtools_client,
+ const UserData* user_data,
+ const ElementFinderResultType result_type,
+ const Selector& selector);
+ ~CssElementFinder() override;
+
+ CssElementFinder(const CssElementFinder&) = delete;
+ CssElementFinder& operator=(const CssElementFinder&) = delete;
+
+ void Start(const ElementFinderResult& start_element,
+ BaseElementFinder::Callback callback) override;
+
+ ElementFinderInfoProto GetLogInfo() const override;
+
+ // Returns the backend node id of the result if the proto contains
+ // |semantic_information|, or 0.
+ int GetBackendNodeId() const override;
+
+ private:
+ // Returns the given status and no element. This expects an error status.
+ void GiveUpWithError(const ClientStatus& status);
+
+ // Found a valid result.
+ void ResultFound(const std::string& object_id);
+
+ // Builds a result from the current state of the finder and returns it with
+ // an ok status.
+ void BuildAndSendResult(const std::string& object_id);
+
+ // Call |callback_| with the |status| and |result|.
+ void SendResult(const ClientStatus& status,
+ const ElementFinderResult& result);
+
+ // Figures out what to do next given the current state.
+ //
+ // Most background operations in this worker end by updating the state and
+ // calling ExecuteNextTask() again either directly or through Report*().
+ void ExecuteNextTask();
+
+ // Prepare a batch of |n| tasks that are sent at the same time to compute
+ // one or more matching elements.
+ //
+ // After calling this, Report*(i, ...) should be called *exactly once* for
+ // all 0 <= i < n to report the tasks results.
+ //
+ // Once all tasks reported their result, the object ID of all matching
+ // elements will be added to |current_matches_| and ExecuteNextTask() will
+ // be called.
+ void PrepareBatchTasks(int n);
+
+ // Report that task with ID |task_id| didn't match any element.
+ void ReportNoMatchingElement(size_t task_id);
+
+ // Report that task with ID |task_id| matched a single element with ID
+ // |object_id|.
+ void ReportMatchingElement(size_t task_id, const std::string& object_id);
+
+ // Report that task with ID |task_id| matched multiple elements that are
+ // stored in the JS array with ID |object_id|.
+ void ReportMatchingElementsArray(size_t task_id,
+ const std::string& array_object_id);
+ void ReportMatchingElementsArrayRecursive(
+ size_t task_id,
+ const std::string& array_object_id,
+ std::unique_ptr<std::vector<std::string>> acc,
+ int index);
+ void OnReportMatchingElementsArrayRecursive(
+ size_t task_id,
+ const std::string& array_object_id,
+ std::unique_ptr<std::vector<std::string>> acc,
+ int index,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::CallFunctionOnResult> result);
+
+ // If all batch tasks reported their result, add all tasks results to
+ // |current_matches_| then call ExecuteNextTask().
+ void MaybeFinalizeBatchTasks();
+
+ // Make sure there's exactly one match, set it |object_id_out| then return
+ // true.
+ //
+ // If there are too many or too few matches, this function sends an error
+ // and returns false.
+ //
+ // If this returns true, continue processing. If this returns false, return
+ // from ExecuteNextTask(). ExecuteNextTask() will be called again once the
+ // required data is available.
+ bool ConsumeOneMatchOrFail(std::string& object_id_out);
+
+ // Make sure there's at least |index + 1| matches, take the one at that
+ // index and put it in |object_id_out|, then return true.
+ //
+ // If there are not enough matches, send an error response and return false.
+ bool ConsumeMatchAtOrFail(size_t index, std::string& object_id_out);
+
+ // Make sure there's at least one match and move them all into
+ // |matches_out|.
+ //
+ // If there are no matches, send an error response and return false.
+ // If there are not enough matches yet, fetch them in the background and
+ // return false. This calls ExecuteNextTask() once matches have been
+ // fetched.
+ //
+ // If this returns true, continue processing. If this returns false, return
+ // from ExecuteNextTask(). ExecuteNextTask() will be called again once the
+ // required data is available.
+ bool ConsumeAllMatchesOrFail(std::vector<std::string>& matches_out);
+
+ // Make sure there's at least one match and move them all into a single
+ // array.
+ //
+ // If there are no matches, call SendResult() and return false.
+ //
+ // If there are matches, return false directly and move the matches into
+ // an JS array in the background. ExecuteNextTask() is called again
+ // once the background tasks have executed, and calling this will return
+ // true and write the JS array id to |array_object_id_out|.
+ bool ConsumeMatchArrayOrFail(std::string& array_object_id_out);
+
+ void OnConsumeMatchArray(
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::CallFunctionOnResult> result);
+
+ // Gets a document element from the current frame and us it as root for the
+ // rest of the tasks, then call ExecuteNextTask().
+ void GetDocumentElement();
+ void OnGetDocumentElement(const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::EvaluateResult> result);
+
+ // Handle Javascript filters
+ void ApplyJsFilters(const JsFilterBuilder& builder,
+ const std::vector<std::string>& object_ids);
+ void OnApplyJsFilters(size_t task_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::CallFunctionOnResult> result);
+
+ // Handle PSEUDO_TYPE
+ void ResolvePseudoElement(PseudoType pseudo_type,
+ const std::vector<std::string>& object_ids);
+ void OnDescribeNodeForPseudoElement(
+ dom::PseudoType pseudo_type,
+ size_t task_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::DescribeNodeResult> result);
+ void OnResolveNodeForPseudoElement(
+ size_t task_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::ResolveNodeResult> result);
+
+ // Handle ENTER_FRAME
+ void EnterFrame(const std::string& object_id);
+ void OnDescribeNodeForFrame(const std::string& object_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::DescribeNodeResult> result);
+ void OnResolveNode(const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::ResolveNodeResult> result);
+
+ // Fill |current_matches_js_array_| with the values in |current_matches_|
+ // starting from |index|, then clear |current_matches_| and call
+ // ExecuteNextTask().
+ void MoveMatchesToJSArrayRecursive(size_t index);
+
+ void OnMoveMatchesToJSArrayRecursive(
+ size_t index,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<runtime::CallFunctionOnResult> result);
+
+ void OnDescribeNodeForId(
+ const std::string& object_id,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::DescribeNodeResult> node_result);
+
+ const raw_ptr<content::WebContents> web_contents_;
+ const raw_ptr<DevtoolsClient> devtools_client_;
+ const raw_ptr<const UserData> user_data_;
+ const ElementFinderResultType result_type_;
+ const Selector selector_;
+ BaseElementFinder::Callback callback_;
+
+ // The modified selector to use going forward. This is guaranteed to have
+ // resolved any filters that need a data lookup.
+ SelectorProto selector_proto_;
+
+ // The index of the next filter to process, in selector__proto_.filters.
+ int next_filter_index_ = 0;
+
+ // Getting the document failed. Used for error reporting.
+ bool get_document_failed_ = false;
+
+ // The currently worked on filters are starting at this index..
+ int current_filter_index_range_start_ = -1;
+
+ // Pointer to the current frame
+ content::GlobalRenderFrameHostId current_frame_global_id_;
+
+ // The frame id to use to execute devtools Javascript calls within the
+ // context of the frame. Might be empty if no frame id needs to be
+ // specified.
+ std::string current_frame_devtools_id_;
+
+ // Object IDs of the current set matching elements. Cleared once it's used
+ // to query or filter.
+ std::vector<std::string> current_matches_;
+
+ // Object ID of the JavaScript array of the currently matching elements. In
+ // practice, this is used by ConsumeMatchArrayOrFail() to convert
+ // |current_matches_| to a JavaScript array.
+ std::string current_matches_js_array_;
+
+ // True if current_matches are pseudo-elements.
+ bool matching_pseudo_elements_ = false;
+
+ // The result of the background tasks. |tasks_results_[i]| contains the
+ // elements matched by task i, or nullptr if the task is still running.
+ std::vector<std::unique_ptr<std::vector<std::string>>> tasks_results_;
+
+ std::vector<JsObjectIdentifier> frame_stack_;
+
+ // The backend node id of the result. Only gets assigned if required, when
+ // this will be used for a comparison with the result of a semantic run.
+ absl::optional<int> backend_node_id_;
+
+ // The client status of the last run.
+ ClientStatus client_status_;
+
+ base::WeakPtrFactory<CssElementFinder> weak_ptr_factory_{this};
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_CSS_ELEMENT_FINDER_H_
diff --git a/chromium/components/autofill_assistant/browser/web/element.cc b/chromium/components/autofill_assistant/browser/web/element.cc
index 8fb951d48b3..526ca8a6fbb 100644
--- a/chromium/components/autofill_assistant/browser/web/element.cc
+++ b/chromium/components/autofill_assistant/browser/web/element.cc
@@ -50,19 +50,21 @@ content::RenderFrameHost* FindCorrespondingRenderFrameHost(
const std::string& frame_id,
content::WebContents* web_contents) {
if (frame_id.empty()) {
- return web_contents->GetMainFrame();
+ return web_contents->GetPrimaryMainFrame();
}
content::RenderFrameHost* result = nullptr;
- web_contents->GetMainFrame()->ForEachRenderFrameHost(base::BindRepeating(
- [](const std::string& frame_id, content::RenderFrameHost** result,
- content::RenderFrameHost* render_frame_host) {
- if (render_frame_host->GetDevToolsFrameToken().ToString() == frame_id) {
- *result = render_frame_host;
- return content::RenderFrameHost::FrameIterationAction::kStop;
- }
- return content::RenderFrameHost::FrameIterationAction::kContinue;
- },
- frame_id, &result));
+ web_contents->GetPrimaryMainFrame()->ForEachRenderFrameHost(
+ base::BindRepeating(
+ [](const std::string& frame_id, content::RenderFrameHost** result,
+ content::RenderFrameHost* render_frame_host) {
+ if (render_frame_host->GetDevToolsFrameToken().ToString() ==
+ frame_id) {
+ *result = render_frame_host;
+ return content::RenderFrameHost::FrameIterationAction::kStop;
+ }
+ return content::RenderFrameHost::FrameIterationAction::kContinue;
+ },
+ frame_id, &result));
return result;
}
diff --git a/chromium/components/autofill_assistant/browser/web/element.h b/chromium/components/autofill_assistant/browser/web/element.h
index 04aef40d18d..fa360ab696a 100644
--- a/chromium/components/autofill_assistant/browser/web/element.h
+++ b/chromium/components/autofill_assistant/browser/web/element.h
@@ -11,6 +11,7 @@
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace autofill_assistant {
@@ -25,6 +26,9 @@ struct JsObjectIdentifier {
// context of the frame. Might be empty if no frame id needs to be
// specified.
std::string node_frame_id;
+
+ // The node id of this object. This is only available for nodes.
+ absl::optional<int> backend_node_id;
};
// DomObjectFrameStack contains all data required to use an object including
diff --git a/chromium/components/autofill_assistant/browser/web/element_action_util.cc b/chromium/components/autofill_assistant/browser/web/element_action_util.cc
index 5db0c8b39e3..ee44dab5d4a 100644
--- a/chromium/components/autofill_assistant/browser/web/element_action_util.cc
+++ b/chromium/components/autofill_assistant/browser/web/element_action_util.cc
@@ -7,7 +7,7 @@
#include "base/callback.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/web/element_action_util.h b/chromium/components/autofill_assistant/browser/web/element_action_util.h
index a7cebe63686..751f5a36d5d 100644
--- a/chromium/components/autofill_assistant/browser/web/element_action_util.h
+++ b/chromium/components/autofill_assistant/browser/web/element_action_util.h
@@ -11,9 +11,10 @@
#include "base/callback.h"
#include "base/logging.h"
#include "components/autofill_assistant/browser/client_status.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace autofill_assistant {
+class ElementFinderResult;
+
namespace element_action_util {
namespace {
diff --git a/chromium/components/autofill_assistant/browser/web/element_action_util_unittest.cc b/chromium/components/autofill_assistant/browser/web/element_action_util_unittest.cc
index 6075341e6df..63ba145860a 100644
--- a/chromium/components/autofill_assistant/browser/web/element_action_util_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/web/element_action_util_unittest.cc
@@ -9,7 +9,7 @@
#include "base/test/gmock_callback_support.h"
#include "components/autofill_assistant/browser/action_value.pb.h"
#include "components/autofill_assistant/browser/actions/action_test_utils.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"
diff --git a/chromium/components/autofill_assistant/browser/web/element_finder.cc b/chromium/components/autofill_assistant/browser/web/element_finder.cc
index 0a648102d34..378a3d6dc9d 100644
--- a/chromium/components/autofill_assistant/browser/web/element_finder.cc
+++ b/chromium/components/autofill_assistant/browser/web/element_finder.cc
@@ -6,116 +6,16 @@
#include <utility>
-#include "base/barrier_callback.h"
-#include "base/time/time.h"
#include "components/autofill_assistant/browser/devtools/devtools_client.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/user_data.h"
-#include "components/autofill_assistant/browser/user_data_util.h"
-#include "components/autofill_assistant/browser/web/element.h"
-#include "components/autofill_assistant/browser/web/js_filter_builder.h"
-#include "components/autofill_assistant/browser/web/web_controller_util.h"
-#include "components/autofill_assistant/content/browser/content_autofill_assistant_driver.h"
-#include "content/public/browser/global_routing_id.h"
-#include "content/public/browser/render_frame_host.h"
+#include "components/autofill_assistant/browser/web/base_element_finder.h"
+#include "components/autofill_assistant/browser/web/css_element_finder.h"
+#include "components/autofill_assistant/browser/web/semantic_element_finder.h"
#include "content/public/browser/web_contents.h"
namespace autofill_assistant {
-namespace {
-// Javascript code to get document root element.
-const char kGetDocumentElement[] = "document.documentElement;";
-
-const char kGetArrayElement[] = "function(index) { return this[index]; }";
-
-bool ConvertPseudoType(const PseudoType pseudo_type,
- dom::PseudoType* pseudo_type_output) {
- switch (pseudo_type) {
- case PseudoType::UNDEFINED:
- break;
- case PseudoType::FIRST_LINE:
- *pseudo_type_output = dom::PseudoType::FIRST_LINE;
- return true;
- case PseudoType::FIRST_LETTER:
- *pseudo_type_output = dom::PseudoType::FIRST_LETTER;
- return true;
- case PseudoType::BEFORE:
- *pseudo_type_output = dom::PseudoType::BEFORE;
- return true;
- case PseudoType::AFTER:
- *pseudo_type_output = dom::PseudoType::AFTER;
- return true;
- case PseudoType::BACKDROP:
- *pseudo_type_output = dom::PseudoType::BACKDROP;
- return true;
- case PseudoType::SELECTION:
- *pseudo_type_output = dom::PseudoType::SELECTION;
- return true;
- case PseudoType::FIRST_LINE_INHERITED:
- *pseudo_type_output = dom::PseudoType::FIRST_LINE_INHERITED;
- return true;
- case PseudoType::SCROLLBAR:
- *pseudo_type_output = dom::PseudoType::SCROLLBAR;
- return true;
- case PseudoType::SCROLLBAR_THUMB:
- *pseudo_type_output = dom::PseudoType::SCROLLBAR_THUMB;
- return true;
- case PseudoType::SCROLLBAR_BUTTON:
- *pseudo_type_output = dom::PseudoType::SCROLLBAR_BUTTON;
- return true;
- case PseudoType::SCROLLBAR_TRACK:
- *pseudo_type_output = dom::PseudoType::SCROLLBAR_TRACK;
- return true;
- case PseudoType::SCROLLBAR_TRACK_PIECE:
- *pseudo_type_output = dom::PseudoType::SCROLLBAR_TRACK_PIECE;
- return true;
- case PseudoType::SCROLLBAR_CORNER:
- *pseudo_type_output = dom::PseudoType::SCROLLBAR_CORNER;
- return true;
- case PseudoType::RESIZER:
- *pseudo_type_output = dom::PseudoType::RESIZER;
- return true;
- case PseudoType::INPUT_LIST_BUTTON:
- *pseudo_type_output = dom::PseudoType::INPUT_LIST_BUTTON;
- return true;
- }
- return false;
-}
-
-void AddHostToList(std::vector<content::GlobalRenderFrameHostId>& host_ids,
- content::RenderFrameHost* host) {
- host_ids.push_back(host->GetGlobalId());
-}
-
-ElementFinderInfoProto::SemanticInferenceStatus
-NodeDataStatusToSemanticInferenceStatus(
- mojom::NodeDataStatus node_data_status) {
- switch (node_data_status) {
- case mojom::NodeDataStatus::kSuccess:
- return ElementFinderInfoProto::SUCCESS;
- case mojom::NodeDataStatus::kUnexpectedError:
- return ElementFinderInfoProto::UNEXPECTED_ERROR;
- case mojom::NodeDataStatus::kInitializationError:
- return ElementFinderInfoProto::INITIALIZATION_ERROR;
- case mojom::NodeDataStatus::kModelLoadError:
- return ElementFinderInfoProto::MODEL_LOAD_ERROR;
- case mojom::NodeDataStatus::kModelLoadTimeout:
- return ElementFinderInfoProto::MODEL_LOAD_TIMEOUT;
- }
-}
-
-} // namespace
-
-ElementFinderResult::ElementFinderResult() = default;
-
-ElementFinderResult::~ElementFinderResult() = default;
-
-ElementFinderResult::ElementFinderResult(const ElementFinderResult&) = default;
-
-ElementFinderResult ElementFinderResult::EmptyResult() {
- return ElementFinderResult();
-}
-
ElementFinder::ElementFinder(
content::WebContents* web_contents,
DevtoolsClient* devtools_client,
@@ -123,7 +23,7 @@ ElementFinder::ElementFinder(
ProcessedActionStatusDetailsProto* log_info,
AnnotateDomModelService* annotate_dom_model_service,
const Selector& selector,
- ResultType result_type)
+ ElementFinderResultType result_type)
: web_contents_(web_contents),
devtools_client_(devtools_client),
user_data_(user_data),
@@ -178,7 +78,7 @@ void ElementFinder::Start(const ElementFinderResult& start_element,
void ElementFinder::AddAndStartRunner(
const ElementFinderResult& start_element,
- std::unique_ptr<ElementFinderBase> runner) {
+ std::unique_ptr<BaseElementFinder> runner) {
auto* runner_ptr = runner.get();
runners_.emplace_back(std::move(runner));
results_.resize(runners_.size());
@@ -239,882 +139,4 @@ void ElementFinder::OnResult(size_t index,
SendResult(results_[0].first, std::move(results_[0].second));
}
-ElementFinder::ElementFinderBase::~ElementFinderBase() = default;
-
-ElementFinder::SemanticElementFinder::SemanticElementFinder(
- content::WebContents* web_contents,
- DevtoolsClient* devtools_client,
- AnnotateDomModelService* annotate_dom_model_service,
- const Selector& selector)
- : web_contents_(web_contents),
- devtools_client_(devtools_client),
- annotate_dom_model_service_(annotate_dom_model_service),
- selector_(selector) {
- DCHECK(annotate_dom_model_service_);
-}
-ElementFinder::SemanticElementFinder::~SemanticElementFinder() = default;
-
-void ElementFinder::SemanticElementFinder::GiveUpWithError(
- const ClientStatus& status) {
- DCHECK(!status.ok());
- if (!callback_) {
- return;
- }
-
- SendResult(status, ElementFinderResult::EmptyResult());
-}
-
-void ElementFinder::SemanticElementFinder::ResultFound(
- content::RenderFrameHost* render_frame_host,
- const std::string& object_id) {
- if (!callback_) {
- return;
- }
-
- ElementFinderResult result;
- result.SetRenderFrameHost(render_frame_host);
- result.SetObjectId(object_id);
-
- SendResult(OkClientStatus(), result);
-}
-
-void ElementFinder::SemanticElementFinder::SendResult(
- const ClientStatus& status,
- const ElementFinderResult& result) {
- DCHECK(callback_);
- std::move(callback_).Run(status,
- std::make_unique<ElementFinderResult>(result));
-}
-
-void ElementFinder::SemanticElementFinder::Start(
- const ElementFinderResult& start_element,
- Callback callback) {
- callback_ = std::move(callback);
-
- auto* start_frame = start_element.render_frame_host();
- if (!start_frame) {
- start_frame = web_contents_->GetMainFrame();
- }
- RunAnnotateDomModel(start_frame);
-}
-
-ElementFinderInfoProto ElementFinder::SemanticElementFinder::GetLogInfo()
- const {
- DCHECK(!callback_); // Run after finish.
-
- ElementFinderInfoProto info;
- DCHECK(selector_.proto.has_semantic_information());
- for (auto node_data_status : node_data_frame_status_) {
- info.mutable_semantic_inference_result()->add_status_per_frame(
- NodeDataStatusToSemanticInferenceStatus(node_data_status));
- }
- for (const auto& semantic_node_result : semantic_node_results_) {
- auto* predicted_element =
- info.mutable_semantic_inference_result()->add_predicted_elements();
- predicted_element->set_backend_node_id(
- semantic_node_result.backend_node_id());
- *predicted_element->mutable_semantic_information() =
- selector_.proto.semantic_information();
- // TODO(b/217160707): For the ignore_objective case this is not correct
- // and the inferred objective should be returned from the Agent and used
- // here.
- }
-
- return info;
-}
-
-int ElementFinder::SemanticElementFinder::GetBackendNodeId() const {
- if (semantic_node_results_.empty()) {
- return 0;
- }
- return semantic_node_results_[0].backend_node_id();
-}
-
-void ElementFinder::SemanticElementFinder::RunAnnotateDomModel(
- content::RenderFrameHost* start_frame) {
- std::vector<content::GlobalRenderFrameHostId> host_ids;
- start_frame->ForEachRenderFrameHost(
- base::BindRepeating(&AddHostToList, std::ref(host_ids)));
- const auto run_on_frame =
- base::BarrierCallback<std::vector<GlobalBackendNodeId>>(
- host_ids.size(),
- base::BindOnce(&SemanticElementFinder::OnRunAnnotateDomModel,
- weak_ptr_factory_.GetWeakPtr()));
- for (const auto& host_id : host_ids) {
- RunAnnotateDomModelOnFrame(host_id, run_on_frame);
- }
-}
-
-void ElementFinder::SemanticElementFinder::RunAnnotateDomModelOnFrame(
- const content::GlobalRenderFrameHostId& host_id,
- base::OnceCallback<void(std::vector<GlobalBackendNodeId>)> callback) {
- content::RenderFrameHost* render_frame_host =
- content::RenderFrameHost::FromID(host_id);
- if (!render_frame_host) {
- std::move(callback).Run(std::vector<GlobalBackendNodeId>());
- return;
- }
-
- auto* driver = ContentAutofillAssistantDriver::GetOrCreateForRenderFrameHost(
- render_frame_host, annotate_dom_model_service_);
- if (!driver) {
- NOTREACHED();
- std::move(callback).Run(std::vector<GlobalBackendNodeId>());
- return;
- }
-
- driver->GetAutofillAssistantAgent()->GetSemanticNodes(
- selector_.proto.semantic_information().semantic_role(),
- selector_.proto.semantic_information().objective(),
- selector_.proto.semantic_information().ignore_objective(),
- base::Milliseconds(
- selector_.proto.semantic_information().model_timeout_ms()),
- base::BindOnce(&SemanticElementFinder::OnRunAnnotateDomModelOnFrame,
- weak_ptr_factory_.GetWeakPtr(), host_id,
- std::move(callback)));
-}
-
-void ElementFinder::SemanticElementFinder::OnRunAnnotateDomModelOnFrame(
- const content::GlobalRenderFrameHostId& host_id,
- base::OnceCallback<void(std::vector<GlobalBackendNodeId>)> callback,
- mojom::NodeDataStatus status,
- const std::vector<NodeData>& node_data) {
- node_data_frame_status_.emplace_back(status);
-
- std::vector<GlobalBackendNodeId> node_ids;
- for (const auto& node : node_data) {
- node_ids.emplace_back(GlobalBackendNodeId(host_id, node.backend_node_id));
- }
- std::move(callback).Run(node_ids);
-}
-
-void ElementFinder::SemanticElementFinder::OnRunAnnotateDomModel(
- const std::vector<std::vector<GlobalBackendNodeId>>& all_nodes) {
- for (const auto& node_ids : all_nodes) {
- semantic_node_results_.insert(semantic_node_results_.end(),
- node_ids.begin(), node_ids.end());
- }
-
- // For now we only support finding a single element.
- // TODO(b/224746702): Emit multiple ResolveNode calls for the case where the
- // result type is not ResultType::kExactlyOneMatch.
- if (semantic_node_results_.size() > 1) {
- VLOG(1) << __func__ << " Got " << semantic_node_results_.size()
- << " matches for " << selector_ << ", when only 1 was expected.";
- GiveUpWithError(ClientStatus(TOO_MANY_ELEMENTS));
- return;
- }
- if (semantic_node_results_.empty()) {
- GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
- return;
- }
-
- // We need to set the empty string for the frame id. The expectation is that
- // backend node ids are global and devtools is able to resolve the node
- // without an explicit frame id.
- devtools_client_->GetDOM()->ResolveNode(
- dom::ResolveNodeParams::Builder()
- .SetBackendNodeId(semantic_node_results_[0].backend_node_id())
- .Build(),
- /* current_frame_id= */ std::string(),
- base::BindOnce(&SemanticElementFinder::OnResolveNodeForAnnotateDom,
- weak_ptr_factory_.GetWeakPtr(),
- semantic_node_results_[0].host_id()));
-}
-
-void ElementFinder::SemanticElementFinder::OnResolveNodeForAnnotateDom(
- content::GlobalRenderFrameHostId host_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::ResolveNodeResult> result) {
- if (result && result->GetObject() && result->GetObject()->HasObjectId()) {
- ResultFound(content::RenderFrameHost::FromID(host_id),
- result->GetObject()->GetObjectId());
- return;
- }
- SendResult(ClientStatus(ELEMENT_RESOLUTION_FAILED),
- ElementFinderResult::EmptyResult());
-}
-
-ElementFinder::CssElementFinder::CssElementFinder(
- content::WebContents* web_contents,
- DevtoolsClient* devtools_client,
- const UserData* user_data,
- const ResultType result_type,
- const Selector& selector)
- : web_contents_(web_contents),
- devtools_client_(devtools_client),
- user_data_(user_data),
- result_type_(result_type),
- selector_(selector) {}
-ElementFinder::CssElementFinder::~CssElementFinder() = default;
-
-void ElementFinder::CssElementFinder::Start(
- const ElementFinderResult& start_element,
- Callback callback) {
- callback_ = std::move(callback);
-
- selector_proto_ = selector_.proto;
- ClientStatus resolve_status =
- user_data::ResolveSelectorUserData(&selector_proto_, user_data_);
- if (!resolve_status.ok()) {
- SendResult(resolve_status, ElementFinderResult::EmptyResult());
- return;
- }
-
- current_frame_ = start_element.render_frame_host();
- if (current_frame_ == nullptr) {
- current_frame_ = web_contents_->GetMainFrame();
- }
- current_frame_id_ = start_element.node_frame_id();
- frame_stack_ = start_element.frame_stack();
-
- if (start_element.object_id().empty()) {
- GetDocumentElement();
- } else {
- current_matches_.emplace_back(start_element.object_id());
- ExecuteNextTask();
- }
-}
-
-ElementFinderInfoProto ElementFinder::CssElementFinder::GetLogInfo() const {
- DCHECK(!callback_); // Run after finish.
-
- ElementFinderInfoProto info;
- if (!client_status_.ok()) {
- info.set_failed_filter_index_range_start(current_filter_index_range_start_);
- info.set_failed_filter_index_range_end(next_filter_index_);
- info.set_get_document_failed(get_document_failed_);
- }
-
- return info;
-}
-
-int ElementFinder::CssElementFinder::GetBackendNodeId() const {
- return backend_node_id_.value_or(0);
-}
-
-void ElementFinder::CssElementFinder::GiveUpWithError(
- const ClientStatus& status) {
- DCHECK(!status.ok());
- if (!callback_) {
- return;
- }
-
- SendResult(status, ElementFinderResult::EmptyResult());
-}
-
-void ElementFinder::CssElementFinder::ResultFound(
- const std::string& object_id) {
- if (!callback_) {
- return;
- }
-
- if (selector_.proto.has_semantic_information()) {
- devtools_client_->GetDOM()->DescribeNode(
- dom::DescribeNodeParams::Builder().SetObjectId(object_id).Build(),
- current_frame_id_,
- base::BindOnce(&CssElementFinder::OnDescribeNodeForId,
- weak_ptr_factory_.GetWeakPtr(), object_id));
- return;
- }
-
- BuildAndSendResult(object_id);
-}
-
-void ElementFinder::CssElementFinder::OnDescribeNodeForId(
- const std::string& object_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::DescribeNodeResult> node_result) {
- if (node_result && node_result->GetNode()) {
- backend_node_id_ = node_result->GetNode()->GetBackendNodeId();
- }
- BuildAndSendResult(object_id);
-}
-
-void ElementFinder::CssElementFinder::BuildAndSendResult(
- const std::string& object_id) {
- ElementFinderResult result;
- result.SetRenderFrameHost(current_frame_);
- result.SetObjectId(object_id);
- result.SetNodeFrameId(current_frame_id_);
- result.SetFrameStack(frame_stack_);
-
- SendResult(OkClientStatus(), result);
-}
-
-void ElementFinder::CssElementFinder::SendResult(
- const ClientStatus& status,
- const ElementFinderResult& result) {
- client_status_ = status;
- DCHECK(callback_);
- std::move(callback_).Run(status,
- std::make_unique<ElementFinderResult>(result));
-}
-
-void ElementFinder::CssElementFinder::ExecuteNextTask() {
- const auto& filters = selector_proto_.filters();
-
- if (next_filter_index_ >= filters.size()) {
- std::string object_id;
- switch (result_type_) {
- case ResultType::kExactlyOneMatch:
- if (!ConsumeOneMatchOrFail(object_id)) {
- return;
- }
- break;
-
- case ResultType::kAnyMatch:
- if (!ConsumeMatchAtOrFail(0, object_id)) {
- return;
- }
- break;
-
- case ResultType::kMatchArray:
- if (!ConsumeMatchArrayOrFail(object_id)) {
- return;
- }
- break;
- }
- ResultFound(object_id);
- return;
- }
-
- current_filter_index_range_start_ = next_filter_index_;
- const auto& filter = filters.Get(next_filter_index_);
- switch (filter.filter_case()) {
- case SelectorProto::Filter::kEnterFrame: {
- std::string object_id;
- if (!ConsumeOneMatchOrFail(object_id))
- return;
-
- // The above fails if there is more than one frame. To preserve
- // backward-compatibility with the previous, lax behavior, callers must
- // add pick_one before enter_frame. TODO(b/155264465): allow searching in
- // more than one frame.
- next_filter_index_++;
- EnterFrame(object_id);
- return;
- }
-
- case SelectorProto::Filter::kPseudoType: {
- std::vector<std::string> matches;
- if (!ConsumeAllMatchesOrFail(matches))
- return;
-
- next_filter_index_++;
- matching_pseudo_elements_ = true;
- ResolvePseudoElement(filter.pseudo_type(), matches);
- return;
- }
-
- case SelectorProto::Filter::kNthMatch: {
- // TODO(b/205676462): This could be done with javascript like in
- // |SelectorObserver|.
- std::string object_id;
- if (!ConsumeMatchAtOrFail(filter.nth_match().index(), object_id))
- return;
-
- next_filter_index_++;
- current_matches_ = {object_id};
- ExecuteNextTask();
- return;
- }
-
- case SelectorProto::Filter::kCssSelector:
- case SelectorProto::Filter::kInnerText:
- case SelectorProto::Filter::kValue:
- case SelectorProto::Filter::kProperty:
- case SelectorProto::Filter::kBoundingBox:
- case SelectorProto::Filter::kPseudoElementContent:
- case SelectorProto::Filter::kMatchCssSelector:
- case SelectorProto::Filter::kCssStyle:
- case SelectorProto::Filter::kLabelled:
- case SelectorProto::Filter::kOnTop: {
- std::vector<std::string> matches;
- if (!ConsumeAllMatchesOrFail(matches))
- return;
-
- JsFilterBuilder js_filter;
- for (int i = next_filter_index_; i < filters.size(); i++) {
- if (!js_filter.AddFilter(filters.Get(i))) {
- break;
- }
- next_filter_index_++;
- }
- ApplyJsFilters(js_filter, matches);
- return;
- }
-
- case SelectorProto::Filter::FILTER_NOT_SET:
- VLOG(1) << __func__ << " Unset or unknown filter in " << filter << " in "
- << selector_;
- GiveUpWithError(ClientStatus(INVALID_SELECTOR));
- return;
- }
-}
-
-bool ElementFinder::CssElementFinder::ConsumeOneMatchOrFail(
- std::string& object_id_out) {
- if (current_matches_.size() > 1) {
- VLOG(1) << __func__ << " Got " << current_matches_.size() << " matches for "
- << selector_ << ", when only 1 was expected.";
- GiveUpWithError(ClientStatus(TOO_MANY_ELEMENTS));
- return false;
- }
- if (current_matches_.empty()) {
- GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
- return false;
- }
-
- object_id_out = current_matches_[0];
- current_matches_.clear();
- return true;
-}
-
-bool ElementFinder::CssElementFinder::ConsumeMatchAtOrFail(
- size_t index,
- std::string& object_id_out) {
- if (index < current_matches_.size()) {
- object_id_out = current_matches_[index];
- current_matches_.clear();
- return true;
- }
-
- GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
- return false;
-}
-
-bool ElementFinder::CssElementFinder::ConsumeAllMatchesOrFail(
- std::vector<std::string>& matches_out) {
- if (!current_matches_.empty()) {
- matches_out = std::move(current_matches_);
- current_matches_.clear();
- return true;
- }
- GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
- return false;
-}
-
-bool ElementFinder::CssElementFinder::ConsumeMatchArrayOrFail(
- std::string& array_object_id) {
- if (!current_matches_js_array_.empty()) {
- array_object_id = current_matches_js_array_;
- current_matches_js_array_.clear();
- return true;
- }
-
- if (current_matches_.empty()) {
- GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
- return false;
- }
-
- MoveMatchesToJSArrayRecursive(/* index= */ 0);
- return false;
-}
-
-void ElementFinder::CssElementFinder::MoveMatchesToJSArrayRecursive(
- size_t index) {
- if (index >= current_matches_.size()) {
- current_matches_.clear();
- ExecuteNextTask();
- return;
- }
-
- // Push the value at |current_matches_[index]| to |current_matches_js_array_|.
- std::string function;
- std::vector<std::unique_ptr<runtime::CallArgument>> arguments;
- if (index == 0) {
- // Create an array containing a single element.
- function = "function() { return [this]; }";
- } else {
- // Add an element to an existing array.
- function = "function(dest) { dest.push(this); }";
- AddRuntimeCallArgumentObjectId(current_matches_js_array_, &arguments);
- }
-
- devtools_client_->GetRuntime()->CallFunctionOn(
- runtime::CallFunctionOnParams::Builder()
- .SetObjectId(current_matches_[index])
- .SetArguments(std::move(arguments))
- .SetFunctionDeclaration(function)
- .Build(),
- current_frame_id_,
- base::BindOnce(&CssElementFinder::OnMoveMatchesToJSArrayRecursive,
- weak_ptr_factory_.GetWeakPtr(), index));
-}
-
-void ElementFinder::CssElementFinder::OnMoveMatchesToJSArrayRecursive(
- size_t index,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<runtime::CallFunctionOnResult> result) {
- ClientStatus status =
- CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
- if (!status.ok()) {
- VLOG(1) << __func__ << ": Failed to push value to JS array.";
- GiveUpWithError(status);
- return;
- }
-
- // We just created an array which contains the first element. We store its ID
- // in |current_matches_js_array_|.
- if (index == 0 &&
- !SafeGetObjectId(result->GetResult(), &current_matches_js_array_)) {
- VLOG(1) << __func__ << " Failed to get array ID.";
- GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
- return;
- }
-
- // Continue the recursion to push the other values into the array.
- MoveMatchesToJSArrayRecursive(index + 1);
-}
-
-void ElementFinder::CssElementFinder::GetDocumentElement() {
- devtools_client_->GetRuntime()->Evaluate(
- std::string(kGetDocumentElement), current_frame_id_,
- base::BindOnce(&CssElementFinder::OnGetDocumentElement,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ElementFinder::CssElementFinder::OnGetDocumentElement(
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<runtime::EvaluateResult> result) {
- ClientStatus status =
- CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
- if (!status.ok()) {
- VLOG(1) << __func__ << " Failed to get document root element.";
- get_document_failed_ = true;
- GiveUpWithError(status);
- return;
- }
- std::string object_id;
- if (!SafeGetObjectId(result->GetResult(), &object_id)) {
- VLOG(1) << __func__ << " Failed to get document root element.";
- get_document_failed_ = true;
- GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
- return;
- }
-
- // Use the node as root for the rest of the evaluation.
- current_matches_.emplace_back(object_id);
-
- ExecuteNextTask();
-}
-
-void ElementFinder::CssElementFinder::ApplyJsFilters(
- const JsFilterBuilder& builder,
- const std::vector<std::string>& object_ids) {
- DCHECK(!object_ids.empty()); // Guaranteed by ExecuteNextTask()
- PrepareBatchTasks(object_ids.size());
- std::string function = builder.BuildFunction();
- for (size_t task_id = 0; task_id < object_ids.size(); task_id++) {
- devtools_client_->GetRuntime()->CallFunctionOn(
- runtime::CallFunctionOnParams::Builder()
- .SetObjectId(object_ids[task_id])
- .SetArguments(builder.BuildArgumentList())
- .SetFunctionDeclaration(function)
- .Build(),
- current_frame_id_,
- base::BindOnce(&CssElementFinder::OnApplyJsFilters,
- weak_ptr_factory_.GetWeakPtr(), task_id));
- }
-}
-
-void ElementFinder::CssElementFinder::OnApplyJsFilters(
- size_t task_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<runtime::CallFunctionOnResult> result) {
- if (!result) {
- // It is possible for a document element to already exist, but not be
- // available yet to query because the document hasn't been loaded. This
- // results in OnQuerySelectorAll getting a nullptr result. For this specific
- // call, it is expected.
- VLOG(1) << __func__ << ": Context doesn't exist yet to query frame "
- << frame_stack_.size() << " of " << selector_;
- GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
- return;
- }
- ClientStatus status =
- CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
- if (!status.ok()) {
- VLOG(1) << __func__ << ": Failed to query selector for frame "
- << frame_stack_.size() << " of " << selector_ << ": " << status;
- GiveUpWithError(status);
- return;
- }
-
- // The result can be empty (nothing found), an array (multiple matches
- // found) or a single node.
- std::string object_id;
- if (!SafeGetObjectId(result->GetResult(), &object_id)) {
- ReportNoMatchingElement(task_id);
- return;
- }
-
- if (result->GetResult()->HasSubtype() &&
- result->GetResult()->GetSubtype() ==
- runtime::RemoteObjectSubtype::ARRAY) {
- ReportMatchingElementsArray(task_id, object_id);
- return;
- }
-
- ReportMatchingElement(task_id, object_id);
-}
-
-void ElementFinder::CssElementFinder::ResolvePseudoElement(
- PseudoType proto_pseudo_type,
- const std::vector<std::string>& object_ids) {
- dom::PseudoType pseudo_type;
- if (!ConvertPseudoType(proto_pseudo_type, &pseudo_type)) {
- VLOG(1) << __func__ << ": Unsupported pseudo-type "
- << PseudoTypeName(proto_pseudo_type);
- GiveUpWithError(ClientStatus(INVALID_ACTION));
- return;
- }
-
- DCHECK(!object_ids.empty()); // Guaranteed by ExecuteNextTask()
- PrepareBatchTasks(object_ids.size());
- for (size_t task_id = 0; task_id < object_ids.size(); task_id++) {
- devtools_client_->GetDOM()->DescribeNode(
- dom::DescribeNodeParams::Builder()
- .SetObjectId(object_ids[task_id])
- .Build(),
- current_frame_id_,
- base::BindOnce(&CssElementFinder::OnDescribeNodeForPseudoElement,
- weak_ptr_factory_.GetWeakPtr(), pseudo_type, task_id));
- }
-}
-
-void ElementFinder::CssElementFinder::OnDescribeNodeForPseudoElement(
- dom::PseudoType pseudo_type,
- size_t task_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::DescribeNodeResult> result) {
- if (!result || !result->GetNode()) {
- VLOG(1) << __func__ << " Failed to describe the node for pseudo element.";
- GiveUpWithError(
- UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
- return;
- }
-
- auto* node = result->GetNode();
- if (node->HasPseudoElements()) {
- for (const auto& pseudo_element : *(node->GetPseudoElements())) {
- if (pseudo_element->HasPseudoType() &&
- pseudo_element->GetPseudoType() == pseudo_type) {
- devtools_client_->GetDOM()->ResolveNode(
- dom::ResolveNodeParams::Builder()
- .SetBackendNodeId(pseudo_element->GetBackendNodeId())
- .Build(),
- current_frame_id_,
- base::BindOnce(&CssElementFinder::OnResolveNodeForPseudoElement,
- weak_ptr_factory_.GetWeakPtr(), task_id));
- return;
- }
- }
- }
-
- ReportNoMatchingElement(task_id);
-}
-
-void ElementFinder::CssElementFinder::OnResolveNodeForPseudoElement(
- size_t task_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::ResolveNodeResult> result) {
- if (result && result->GetObject() && result->GetObject()->HasObjectId()) {
- ReportMatchingElement(task_id, result->GetObject()->GetObjectId());
- return;
- }
-
- ReportNoMatchingElement(task_id);
-}
-
-void ElementFinder::CssElementFinder::EnterFrame(const std::string& object_id) {
- devtools_client_->GetDOM()->DescribeNode(
- dom::DescribeNodeParams::Builder().SetObjectId(object_id).Build(),
- current_frame_id_,
- base::BindOnce(&CssElementFinder::OnDescribeNodeForFrame,
- weak_ptr_factory_.GetWeakPtr(), object_id));
-}
-
-void ElementFinder::CssElementFinder::OnDescribeNodeForFrame(
- const std::string& object_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::DescribeNodeResult> result) {
- if (!result || !result->GetNode()) {
- VLOG(1) << __func__ << " Failed to describe the node.";
- GiveUpWithError(
- UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
- return;
- }
-
- auto* node = result->GetNode();
- std::vector<int> backend_ids;
-
- if (node->GetNodeName() == "IFRAME") {
- // See: b/206647825
- if (!node->HasFrameId()) {
- NOTREACHED() << "Frame without ID"; // Ensure all frames have an id.
- GiveUpWithError(ClientStatus(FRAME_HOST_NOT_FOUND));
- return;
- }
-
- frame_stack_.push_back({object_id, current_frame_id_});
-
- auto* frame =
- FindCorrespondingRenderFrameHost(node->GetFrameId(), web_contents_);
- if (!frame) {
- VLOG(1) << __func__ << " Failed to find corresponding owner frame.";
- GiveUpWithError(ClientStatus(FRAME_HOST_NOT_FOUND));
- return;
- }
- current_frame_ = frame;
-
- if (node->HasContentDocument()) {
- // If the frame has a ContentDocument it's considered a local frame. In
- // this case, current_frame_ doesn't change and can directly use the
- // content document as root for the evaluation.
- backend_ids.emplace_back(node->GetContentDocument()->GetBackendNodeId());
- } else {
- current_frame_id_ = node->GetFrameId();
- // Kick off another find element chain to walk down the OOP iFrame.
- GetDocumentElement();
- return;
- }
- }
-
- if (node->HasShadowRoots()) {
- // TODO(crbug.com/806868): Support multiple shadow roots.
- backend_ids.emplace_back(
- node->GetShadowRoots()->front()->GetBackendNodeId());
- }
-
- if (!backend_ids.empty()) {
- devtools_client_->GetDOM()->ResolveNode(
- dom::ResolveNodeParams::Builder()
- .SetBackendNodeId(backend_ids[0])
- .Build(),
- current_frame_id_,
- base::BindOnce(&CssElementFinder::OnResolveNode,
- weak_ptr_factory_.GetWeakPtr()));
- return;
- }
-
- // Element was not a frame and didn't have shadow dom. This is unexpected, but
- // to remain backward compatible, don't complain and just continue filtering
- // with the current element as root.
- current_matches_.emplace_back(object_id);
- ExecuteNextTask();
-}
-
-void ElementFinder::CssElementFinder::OnResolveNode(
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::ResolveNodeResult> result) {
- if (!result || !result->GetObject() || !result->GetObject()->HasObjectId()) {
- VLOG(1) << __func__ << " Failed to resolve object id from backend id.";
- GiveUpWithError(
- UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
- return;
- }
-
- // Use the node as root for the rest of the evaluation.
- current_matches_.emplace_back(result->GetObject()->GetObjectId());
- ExecuteNextTask();
-}
-
-void ElementFinder::CssElementFinder::PrepareBatchTasks(int n) {
- tasks_results_.clear();
- tasks_results_.resize(n);
-}
-
-void ElementFinder::CssElementFinder::ReportMatchingElement(
- size_t task_id,
- const std::string& object_id) {
- tasks_results_[task_id] =
- std::make_unique<std::vector<std::string>>(1, object_id);
- MaybeFinalizeBatchTasks();
-}
-
-void ElementFinder::CssElementFinder::ReportNoMatchingElement(size_t task_id) {
- tasks_results_[task_id] = std::make_unique<std::vector<std::string>>();
- MaybeFinalizeBatchTasks();
-}
-
-void ElementFinder::CssElementFinder::ReportMatchingElementsArray(
- size_t task_id,
- const std::string& array_object_id) {
- // Recursively add each element ID to a vector then report it as this task
- // result.
- ReportMatchingElementsArrayRecursive(
- task_id, array_object_id, std::make_unique<std::vector<std::string>>(),
- /* index= */ 0);
-}
-
-void ElementFinder::CssElementFinder::ReportMatchingElementsArrayRecursive(
- size_t task_id,
- const std::string& array_object_id,
- std::unique_ptr<std::vector<std::string>> acc,
- int index) {
- std::vector<std::unique_ptr<runtime::CallArgument>> arguments;
- AddRuntimeCallArgument(index, &arguments);
- devtools_client_->GetRuntime()->CallFunctionOn(
- runtime::CallFunctionOnParams::Builder()
- .SetObjectId(array_object_id)
- .SetArguments(std::move(arguments))
- .SetFunctionDeclaration(std::string(kGetArrayElement))
- .Build(),
- current_frame_id_,
- base::BindOnce(&CssElementFinder::OnReportMatchingElementsArrayRecursive,
- weak_ptr_factory_.GetWeakPtr(), task_id, array_object_id,
- std::move(acc), index));
-}
-
-void ElementFinder::CssElementFinder::OnReportMatchingElementsArrayRecursive(
- size_t task_id,
- const std::string& array_object_id,
- std::unique_ptr<std::vector<std::string>> acc,
- int index,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<runtime::CallFunctionOnResult> result) {
- ClientStatus status =
- CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
- if (!status.ok()) {
- VLOG(1) << __func__ << ": Failed to get element from array for "
- << selector_;
- GiveUpWithError(status);
- return;
- }
-
- std::string object_id;
- if (!SafeGetObjectId(result->GetResult(), &object_id)) {
- // We've reached the end of the array.
- tasks_results_[task_id] = std::move(acc);
- MaybeFinalizeBatchTasks();
- return;
- }
-
- acc->emplace_back(object_id);
-
- // Fetch the next element.
- ReportMatchingElementsArrayRecursive(task_id, array_object_id, std::move(acc),
- index + 1);
-}
-
-void ElementFinder::CssElementFinder::MaybeFinalizeBatchTasks() {
- // Return early if one of the tasks is still pending.
- for (const auto& result : tasks_results_) {
- if (!result) {
- return;
- }
- }
-
- // Add all matching elements to current_matches_.
- for (const auto& result : tasks_results_) {
- current_matches_.insert(current_matches_.end(), result->begin(),
- result->end());
- }
- tasks_results_.clear();
-
- ExecuteNextTask();
-}
-
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/web/element_finder.h b/chromium/components/autofill_assistant/browser/web/element_finder.h
index d45d285c17f..a8e8f31c5db 100644
--- a/chromium/components/autofill_assistant/browser/web/element_finder.h
+++ b/chromium/components/autofill_assistant/browser/web/element_finder.h
@@ -9,124 +9,32 @@
#include <string>
#include <vector>
-#include "base/callback.h"
+#include "base/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "base/strings/strcat.h"
-#include "components/autofill_assistant/browser/action_value.pb.h"
#include "components/autofill_assistant/browser/client_status.h"
-#include "components/autofill_assistant/browser/devtools/devtools/domains/types_dom.h"
-#include "components/autofill_assistant/browser/devtools/devtools/domains/types_runtime.h"
#include "components/autofill_assistant/browser/devtools/devtools_client.h"
#include "components/autofill_assistant/browser/selector.h"
-#include "components/autofill_assistant/browser/web/element.h"
-#include "components/autofill_assistant/browser/web/js_filter_builder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
+#include "components/autofill_assistant/browser/web/element_finder_result_type.h"
#include "components/autofill_assistant/browser/web/web_controller_worker.h"
#include "components/autofill_assistant/content/browser/annotate_dom_model_service.h"
-#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
-#include "components/autofill_assistant/content/common/node_data.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
class WebContents;
-class RenderFrameHost;
-struct GlobalRenderFrameHostId;
} // namespace content
namespace autofill_assistant {
+class BaseElementFinder;
class DevtoolsClient;
class UserData;
-// ElementFinderResult is the fully resolved element that can be used without
-// limitations. This means that |render_frame_host()| has been found and is not
-// nullptr.
-class ElementFinderResult {
- public:
- ElementFinderResult();
- ~ElementFinderResult();
- ElementFinderResult(const ElementFinderResult&);
-
- // Create an instance that is deemed to be empty. This can be used for
- // optional Elements (e.g. optional an frame).
- static ElementFinderResult EmptyResult();
-
- const DomObjectFrameStack& dom_object() const { return dom_object_; }
-
- content::RenderFrameHost* render_frame_host() const {
- if (!render_frame_id_) {
- return nullptr;
- }
- return content::RenderFrameHost::FromID(*render_frame_id_);
- }
-
- const std::string& object_id() const {
- return dom_object_.object_data.object_id;
- }
-
- const std::string& node_frame_id() const {
- return dom_object_.object_data.node_frame_id;
- }
-
- const std::vector<JsObjectIdentifier>& frame_stack() const {
- return dom_object_.frame_stack;
- }
-
- bool IsEmpty() const {
- return object_id().empty() && node_frame_id().empty();
- }
-
- void SetRenderFrameHost(content::RenderFrameHost* render_frame_host) {
- if (!render_frame_host) {
- return;
- }
- render_frame_id_ = render_frame_host->GetGlobalId();
- }
-
- void SetObjectId(const std::string& object_id) {
- dom_object_.object_data.object_id = object_id;
- }
-
- void SetNodeFrameId(const std::string& node_frame_id) {
- dom_object_.object_data.node_frame_id = node_frame_id;
- }
-
- void SetFrameStack(const std::vector<JsObjectIdentifier>& frame_stack) {
- dom_object_.frame_stack = frame_stack;
- }
-
- private:
- DomObjectFrameStack dom_object_;
-
- // The id of the render frame host that contains the element.
- absl::optional<content::GlobalRenderFrameHostId> render_frame_id_;
-};
-
// Worker class to find element(s) matching a selector. This will keep entering
// iFrames until the element is found in the last frame, then returns the
// element together with the owning frame. All subsequent operations should
// be performed on that frame.
class ElementFinder : public WebControllerWorker {
public:
- enum ResultType {
- // ElementFinderResult.object_id contains the object ID of the single node
- // that matched.
- // If there are no matches, status is ELEMENT_RESOLUTION_FAILED. If there
- // are more than one matches, status is TOO_MANY_ELEMENTS.
- kExactlyOneMatch = 0,
-
- // ElementFinderResult.object_id contains the object ID of one of the nodes
- // that matched.
- // If there are no matches, status is ELEMENT_RESOLUTION_FAILED.
- kAnyMatch,
-
- // ElementFinderResult.object_id contains the object ID of an array
- // containing all the
- // nodes
- // that matched. If there are no matches, status is
- // ELEMENT_RESOLUTION_FAILED.
- kMatchArray,
- };
-
// |web_contents|, |devtools_client| and |user_data| must be valid for the
// lifetime of the instance. If |annotate_dom_model_service| is not nullptr,
// must be valid for the lifetime of the instance.
@@ -136,7 +44,7 @@ class ElementFinder : public WebControllerWorker {
ProcessedActionStatusDetailsProto* log_info,
AnnotateDomModelService* annotate_dom_model_service,
const Selector& selector,
- ResultType result_type);
+ ElementFinderResultType result_type);
~ElementFinder() override;
using Callback =
@@ -148,328 +56,6 @@ class ElementFinder : public WebControllerWorker {
void Start(const ElementFinderResult& start_element, Callback callback);
private:
- class ElementFinderBase {
- public:
- virtual ~ElementFinderBase();
-
- // Start looking for the element and return it through |callback| with
- // a status. If |start_element| is not empty, use it as a starting point
- // instead of starting from the main frame.
- virtual void Start(const ElementFinderResult& start_element,
- Callback callback) = 0;
-
- // Get the log information for the last run. Should only be run after the
- // run has completed (i.e. |callback_| has been called).
- virtual ElementFinderInfoProto GetLogInfo() const = 0;
-
- // Returns the backend node id that was previously collected.
- virtual int GetBackendNodeId() const = 0;
- };
-
- class SemanticElementFinder : public ElementFinderBase {
- public:
- SemanticElementFinder(content::WebContents* web_contents,
- DevtoolsClient* devtools_client,
- AnnotateDomModelService* annotate_dom_model_service,
- const Selector& selector);
- ~SemanticElementFinder() override;
-
- SemanticElementFinder(const SemanticElementFinder&) = delete;
- SemanticElementFinder& operator=(const SemanticElementFinder&) = delete;
-
- void Start(const ElementFinderResult& start_element,
- Callback callback) override;
-
- ElementFinderInfoProto GetLogInfo() const override;
-
- // Returns the backend node id of the first result (if any), or 0.
- int GetBackendNodeId() const override;
-
- private:
- // Returns the given status and no element. This expects an error status.
- void GiveUpWithError(const ClientStatus& status);
-
- // Builds a result from the |render_frame_host| and the |object_id| and
- // returns it withan ok status.
- void ResultFound(content::RenderFrameHost* render_frame_host,
- const std::string& object_id);
-
- // Call |callback_| with the |status| and |result|.
- void SendResult(const ClientStatus& status,
- const ElementFinderResult& result);
-
- // Run the model annotation on all frames for the current |start_frame|.
- void RunAnnotateDomModel(content::RenderFrameHost* start_frame);
-
- // Runs the model on the frame identified by |host_id|.
- void RunAnnotateDomModelOnFrame(
- const content::GlobalRenderFrameHostId& host_id,
- base::OnceCallback<void(std::vector<GlobalBackendNodeId>)> callback);
- void OnRunAnnotateDomModelOnFrame(
- const content::GlobalRenderFrameHostId& host_id,
- base::OnceCallback<void(std::vector<GlobalBackendNodeId>)> callback,
- mojom::NodeDataStatus status,
- const std::vector<NodeData>& node_data);
-
- // Called once the model has been run on all frames.
- void OnRunAnnotateDomModel(
- const std::vector<std::vector<GlobalBackendNodeId>>& all_nodes);
-
- void OnResolveNodeForAnnotateDom(
- content::GlobalRenderFrameHostId host_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::ResolveNodeResult> result);
-
- const raw_ptr<content::WebContents> web_contents_;
- const raw_ptr<DevtoolsClient> devtools_client_;
- const raw_ptr<AnnotateDomModelService> annotate_dom_model_service_;
- const Selector selector_;
- Callback callback_;
-
- // Elements gathered through all frames. Unused if the |selector_| does not
- // contain |SemanticInformation|.
- std::vector<GlobalBackendNodeId> semantic_node_results_;
- std::vector<mojom::NodeDataStatus> node_data_frame_status_;
-
- base::WeakPtrFactory<SemanticElementFinder> weak_ptr_factory_{this};
- };
-
- class CssElementFinder : public ElementFinderBase {
- public:
- CssElementFinder(content::WebContents* web_contents,
- DevtoolsClient* devtools_client,
- const UserData* user_data,
- const ResultType result_type,
- const Selector& selector);
- ~CssElementFinder() override;
-
- CssElementFinder(const CssElementFinder&) = delete;
- CssElementFinder& operator=(const CssElementFinder&) = delete;
-
- void Start(const ElementFinderResult& start_element,
- Callback callback) override;
-
- ElementFinderInfoProto GetLogInfo() const override;
-
- // Returns the backend node id of the result if the proto contains
- // |semantic_information|, or 0.
- int GetBackendNodeId() const override;
-
- private:
- // Returns the given status and no element. This expects an error status.
- void GiveUpWithError(const ClientStatus& status);
-
- // Found a valid result.
- void ResultFound(const std::string& object_id);
-
- // Builds a result from the current state of the finder and returns it with
- // an ok status.
- void BuildAndSendResult(const std::string& object_id);
-
- // Call |callback_| with the |status| and |result|.
- void SendResult(const ClientStatus& status,
- const ElementFinderResult& result);
-
- // Figures out what to do next given the current state.
- //
- // Most background operations in this worker end by updating the state and
- // calling ExecuteNextTask() again either directly or through Report*().
- void ExecuteNextTask();
-
- // Prepare a batch of |n| tasks that are sent at the same time to compute
- // one or more matching elements.
- //
- // After calling this, Report*(i, ...) should be called *exactly once* for
- // all 0 <= i < n to report the tasks results.
- //
- // Once all tasks reported their result, the object ID of all matching
- // elements will be added to |current_matches_| and ExecuteNextTask() will
- // be called.
- void PrepareBatchTasks(int n);
-
- // Report that task with ID |task_id| didn't match any element.
- void ReportNoMatchingElement(size_t task_id);
-
- // Report that task with ID |task_id| matched a single element with ID
- // |object_id|.
- void ReportMatchingElement(size_t task_id, const std::string& object_id);
-
- // Report that task with ID |task_id| matched multiple elements that are
- // stored in the JS array with ID |object_id|.
- void ReportMatchingElementsArray(size_t task_id,
- const std::string& array_object_id);
- void ReportMatchingElementsArrayRecursive(
- size_t task_id,
- const std::string& array_object_id,
- std::unique_ptr<std::vector<std::string>> acc,
- int index);
- void OnReportMatchingElementsArrayRecursive(
- size_t task_id,
- const std::string& array_object_id,
- std::unique_ptr<std::vector<std::string>> acc,
- int index,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<runtime::CallFunctionOnResult> result);
-
- // If all batch tasks reported their result, add all tasks results to
- // |current_matches_| then call ExecuteNextTask().
- void MaybeFinalizeBatchTasks();
-
- // Make sure there's exactly one match, set it |object_id_out| then return
- // true.
- //
- // If there are too many or too few matches, this function sends an error
- // and returns false.
- //
- // If this returns true, continue processing. If this returns false, return
- // from ExecuteNextTask(). ExecuteNextTask() will be called again once the
- // required data is available.
- bool ConsumeOneMatchOrFail(std::string& object_id_out);
-
- // Make sure there's at least |index + 1| matches, take the one at that
- // index and put it in |object_id_out|, then return true.
- //
- // If there are not enough matches, send an error response and return false.
- bool ConsumeMatchAtOrFail(size_t index, std::string& object_id_out);
-
- // Make sure there's at least one match and move them all into
- // |matches_out|.
- //
- // If there are no matches, send an error response and return false.
- // If there are not enough matches yet, fetch them in the background and
- // return false. This calls ExecuteNextTask() once matches have been
- // fetched.
- //
- // If this returns true, continue processing. If this returns false, return
- // from ExecuteNextTask(). ExecuteNextTask() will be called again once the
- // required data is available.
- bool ConsumeAllMatchesOrFail(std::vector<std::string>& matches_out);
-
- // Make sure there's at least one match and move them all into a single
- // array.
- //
- // If there are no matches, call SendResult() and return false.
- //
- // If there are matches, return false directly and move the matches into
- // an JS array in the background. ExecuteNextTask() is called again
- // once the background tasks have executed, and calling this will return
- // true and write the JS array id to |array_object_id_out|.
- bool ConsumeMatchArrayOrFail(std::string& array_object_id_out);
-
- void OnConsumeMatchArray(
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<runtime::CallFunctionOnResult> result);
-
- // Gets a document element from the current frame and us it as root for the
- // rest of the tasks, then call ExecuteNextTask().
- void GetDocumentElement();
- void OnGetDocumentElement(const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<runtime::EvaluateResult> result);
-
- // Handle Javascript filters
- void ApplyJsFilters(const JsFilterBuilder& builder,
- const std::vector<std::string>& object_ids);
- void OnApplyJsFilters(
- size_t task_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<runtime::CallFunctionOnResult> result);
-
- // Handle PSEUDO_TYPE
- void ResolvePseudoElement(PseudoType pseudo_type,
- const std::vector<std::string>& object_ids);
- void OnDescribeNodeForPseudoElement(
- dom::PseudoType pseudo_type,
- size_t task_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::DescribeNodeResult> result);
- void OnResolveNodeForPseudoElement(
- size_t task_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::ResolveNodeResult> result);
-
- // Handle ENTER_FRAME
- void EnterFrame(const std::string& object_id);
- void OnDescribeNodeForFrame(
- const std::string& object_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::DescribeNodeResult> result);
- void OnResolveNode(const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::ResolveNodeResult> result);
-
- // Fill |current_matches_js_array_| with the values in |current_matches_|
- // starting from |index|, then clear |current_matches_| and call
- // ExecuteNextTask().
- void MoveMatchesToJSArrayRecursive(size_t index);
-
- void OnMoveMatchesToJSArrayRecursive(
- size_t index,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<runtime::CallFunctionOnResult> result);
-
- void OnDescribeNodeForId(
- const std::string& object_id,
- const DevtoolsClient::ReplyStatus& reply_status,
- std::unique_ptr<dom::DescribeNodeResult> node_result);
-
- const raw_ptr<content::WebContents> web_contents_;
- const raw_ptr<DevtoolsClient> devtools_client_;
- const raw_ptr<const UserData> user_data_;
- const ResultType result_type_;
- const Selector selector_;
- Callback callback_;
-
- // The modified selector to use going forward. This is guaranteed to have
- // resolved any filters that need a data lookup.
- SelectorProto selector_proto_;
-
- // The index of the next filter to process, in selector__proto_.filters.
- int next_filter_index_ = 0;
-
- // Getting the document failed. Used for error reporting.
- bool get_document_failed_ = false;
-
- // The currently worked on filters are starting at this index..
- int current_filter_index_range_start_ = -1;
-
- // Pointer to the current frame
- raw_ptr<content::RenderFrameHost> current_frame_ = nullptr;
-
- // The frame id to use to execute devtools Javascript calls within the
- // context of the frame. Might be empty if no frame id needs to be
- // specified.
- std::string current_frame_id_;
-
- // Object IDs of the current set matching elements. Cleared once it's used
- // to query or filter.
- std::vector<std::string> current_matches_;
-
- // Object ID of the JavaScript array of the currently matching elements. In
- // practice, this is used by ConsumeMatchArrayOrFail() to convert
- // |current_matches_| to a JavaScript array.
- std::string current_matches_js_array_;
-
- // True if current_matches are pseudo-elements.
- bool matching_pseudo_elements_ = false;
-
- // The result of the background tasks. |tasks_results_[i]| contains the
- // elements matched by task i, or nullptr if the task is still running.
- std::vector<std::unique_ptr<std::vector<std::string>>> tasks_results_;
-
- std::vector<JsObjectIdentifier> frame_stack_;
-
- // The backend node id of the result. Only gets assigned if required, when
- // this will be used for a comparison with the result of a semantic run.
- absl::optional<int> backend_node_id_;
-
- // The client status of the last run.
- ClientStatus client_status_;
-
- // Finder for the target of the current proximity filter.
- std::unique_ptr<ElementFinder> proximity_target_filter_;
-
- base::WeakPtrFactory<CssElementFinder> weak_ptr_factory_{this};
- };
-
// Updates |log_info_| and calls |callback_| with the |status| and |result|.
void SendResult(const ClientStatus& status,
std::unique_ptr<ElementFinderResult> result);
@@ -478,7 +64,7 @@ class ElementFinder : public WebControllerWorker {
// Adds a runner to the list and starts it from the |start_element|.
void AddAndStartRunner(const ElementFinderResult& start_element,
- std::unique_ptr<ElementFinderBase> runner);
+ std::unique_ptr<BaseElementFinder> runner);
void OnResult(size_t index,
const ClientStatus& status,
std::unique_ptr<ElementFinderResult> result);
@@ -489,10 +75,10 @@ class ElementFinder : public WebControllerWorker {
const raw_ptr<ProcessedActionStatusDetailsProto> log_info_;
const raw_ptr<AnnotateDomModelService> annotate_dom_model_service_;
const Selector selector_;
- const ResultType result_type_;
+ const ElementFinderResultType result_type_;
Callback callback_;
- std::vector<std::unique_ptr<ElementFinderBase>> runners_;
+ std::vector<std::unique_ptr<BaseElementFinder>> runners_;
std::vector<std::pair<ClientStatus, std::unique_ptr<ElementFinderResult>>>
results_;
size_t num_results_ = 0;
diff --git a/chromium/components/autofill_assistant/browser/web/element_finder_result.cc b/chromium/components/autofill_assistant/browser/web/element_finder_result.cc
new file mode 100644
index 00000000000..e97f6b68836
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/element_finder_result.cc
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/web/element_finder_result.h"
+
+namespace autofill_assistant {
+
+ElementFinderResult::ElementFinderResult() = default;
+
+ElementFinderResult::~ElementFinderResult() = default;
+
+ElementFinderResult::ElementFinderResult(const ElementFinderResult&) = default;
+
+ElementFinderResult ElementFinderResult::EmptyResult() {
+ return ElementFinderResult();
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/web/element_finder_result.h b/chromium/components/autofill_assistant/browser/web/element_finder_result.h
new file mode 100644
index 00000000000..ad44f251ac2
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/element_finder_result.h
@@ -0,0 +1,101 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_ELEMENT_FINDER_RESULT_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_ELEMENT_FINDER_RESULT_H_
+
+#include <string>
+#include <vector>
+
+#include "components/autofill_assistant/browser/web/element.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace content {
+class RenderFrameHost;
+struct GlobalRenderFrameHostId;
+} // namespace content
+
+namespace autofill_assistant {
+
+// ElementFinderResult is the fully resolved element that can be used without
+// limitations. This means that |render_frame_host()| has been found and is not
+// nullptr.
+class ElementFinderResult {
+ public:
+ ElementFinderResult();
+ ~ElementFinderResult();
+ ElementFinderResult(const ElementFinderResult&);
+
+ // Create an instance that is deemed to be empty. This can be used for
+ // optional Elements (e.g. optional an frame).
+ static ElementFinderResult EmptyResult();
+
+ const DomObjectFrameStack& dom_object() const { return dom_object_; }
+
+ content::RenderFrameHost* render_frame_host() const {
+ if (!render_frame_id_) {
+ return nullptr;
+ }
+ return content::RenderFrameHost::FromID(*render_frame_id_);
+ }
+
+ const std::string& object_id() const {
+ return dom_object_.object_data.object_id;
+ }
+
+ absl::optional<int> backend_node_id() const {
+ return dom_object_.object_data.backend_node_id;
+ }
+
+ const std::string& node_frame_id() const {
+ return dom_object_.object_data.node_frame_id;
+ }
+
+ const std::vector<JsObjectIdentifier>& frame_stack() const {
+ return dom_object_.frame_stack;
+ }
+
+ bool IsEmpty() const {
+ return object_id().empty() && node_frame_id().empty();
+ }
+
+ // Deprecated. Use SetRenderFrameHostGlobalId instead.
+ void SetRenderFrameHost(content::RenderFrameHost* render_frame_host) {
+ if (!render_frame_host) {
+ return;
+ }
+ render_frame_id_ = render_frame_host->GetGlobalId();
+ }
+
+ void SetRenderFrameHostGlobalId(
+ content::GlobalRenderFrameHostId render_frame_id) {
+ render_frame_id_ = render_frame_id;
+ }
+
+ void SetObjectId(const std::string& object_id) {
+ dom_object_.object_data.object_id = object_id;
+ }
+
+ void SetBackendNodeId(absl::optional<int> backend_node_id) {
+ dom_object_.object_data.backend_node_id = backend_node_id;
+ }
+
+ void SetNodeFrameId(const std::string& node_frame_id) {
+ dom_object_.object_data.node_frame_id = node_frame_id;
+ }
+
+ void SetFrameStack(const std::vector<JsObjectIdentifier>& frame_stack) {
+ dom_object_.frame_stack = frame_stack;
+ }
+
+ private:
+ DomObjectFrameStack dom_object_;
+
+ // The id of the render frame host that contains the element.
+ absl::optional<content::GlobalRenderFrameHostId> render_frame_id_;
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_ELEMENT_FINDER_RESULT_H_
diff --git a/chromium/components/autofill_assistant/browser/web/element_finder_result_type.h b/chromium/components/autofill_assistant/browser/web/element_finder_result_type.h
new file mode 100644
index 00000000000..a506ccff05a
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/element_finder_result_type.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_ELEMENT_FINDER_RESULT_TYPE_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_ELEMENT_FINDER_RESULT_TYPE_H_
+
+namespace autofill_assistant {
+
+enum class ElementFinderResultType {
+ // ElementFinderResult.object_id contains the object ID of the single node
+ // that matched.
+ // If there are no matches, status is ELEMENT_RESOLUTION_FAILED. If there
+ // are more than one matches, status is TOO_MANY_ELEMENTS.
+ kExactlyOneMatch = 0,
+
+ // ElementFinderResult.object_id contains the object ID of one of the nodes
+ // that matched.
+ // If there are no matches, status is ELEMENT_RESOLUTION_FAILED.
+ kAnyMatch,
+
+ // ElementFinderResult.object_id contains the object ID of an array
+ // containing all the
+ // nodes
+ // that matched. If there are no matches, status is
+ // ELEMENT_RESOLUTION_FAILED.
+ kMatchArray,
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_ELEMENT_FINDER_RESULT_H_
diff --git a/chromium/components/autofill_assistant/browser/web/element_rect_getter.cc b/chromium/components/autofill_assistant/browser/web/element_rect_getter.cc
index d3fd70f883f..84e2b6a82f6 100644
--- a/chromium/components/autofill_assistant/browser/web/element_rect_getter.cc
+++ b/chromium/components/autofill_assistant/browser/web/element_rect_getter.cc
@@ -7,10 +7,11 @@
#include "base/callback.h"
#include "base/logging.h"
#include "base/values.h"
+#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/types_runtime.h"
#include "components/autofill_assistant/browser/devtools/devtools_client.h"
#include "components/autofill_assistant/browser/rectf.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "components/autofill_assistant/browser/web/web_controller_util.h"
namespace autofill_assistant {
diff --git a/chromium/components/autofill_assistant/browser/web/element_rect_getter.h b/chromium/components/autofill_assistant/browser/web/element_rect_getter.h
index 033e0adcd52..37ff9afb0dc 100644
--- a/chromium/components/autofill_assistant/browser/web/element_rect_getter.h
+++ b/chromium/components/autofill_assistant/browser/web/element_rect_getter.h
@@ -10,10 +10,11 @@
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/devtools/devtools_client.h"
#include "components/autofill_assistant/browser/rectf.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
#include "components/autofill_assistant/browser/web/web_controller_worker.h"
namespace autofill_assistant {
+class ClientStatus;
+class ElementFinderResult;
// Worker class to get an element's bounding rectangle in viewport coordinates.
// This returns the global coordinates of the element rect, summing up (and
diff --git a/chromium/components/autofill_assistant/browser/web/element_store.cc b/chromium/components/autofill_assistant/browser/web/element_store.cc
index 5f7540ecd8d..3b81a75ca83 100644
--- a/chromium/components/autofill_assistant/browser/web/element_store.cc
+++ b/chromium/components/autofill_assistant/browser/web/element_store.cc
@@ -6,7 +6,7 @@
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/web/element.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
@@ -37,6 +37,7 @@ ClientStatus ElementStore::RestoreElement(
const DomObjectFrameStack& object,
ElementFinderResult* out_element) const {
out_element->SetObjectId(object.object_data.object_id);
+ out_element->SetBackendNodeId(object.object_data.backend_node_id);
out_element->SetNodeFrameId(object.object_data.node_frame_id);
out_element->SetFrameStack(object.frame_stack);
auto* frame = FindCorrespondingRenderFrameHost(
diff --git a/chromium/components/autofill_assistant/browser/web/element_store.h b/chromium/components/autofill_assistant/browser/web/element_store.h
index 74a90c53ffd..fe52dd0cecc 100644
--- a/chromium/components/autofill_assistant/browser/web/element_store.h
+++ b/chromium/components/autofill_assistant/browser/web/element_store.h
@@ -9,13 +9,13 @@
#include "base/memory/raw_ptr.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/web/element.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
namespace content {
class WebContents;
} // namespace content
namespace autofill_assistant {
+class ElementFinderResult;
// Temporary store for elements resolved from a |Selector| by the
// |ElementFinder|. This store only holds a shallow copy of the element,
diff --git a/chromium/components/autofill_assistant/browser/web/element_store_unittest.cc b/chromium/components/autofill_assistant/browser/web/element_store_unittest.cc
index 95309045734..bca656e9147 100644
--- a/chromium/components/autofill_assistant/browser/web/element_store_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/web/element_store_unittest.cc
@@ -8,7 +8,7 @@
#include "base/test/mock_callback.h"
#include "components/autofill_assistant/browser/actions/action_test_utils.h"
#include "components/autofill_assistant/browser/client_status.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
@@ -18,6 +18,9 @@
namespace autofill_assistant {
namespace {
+using ::testing::IsEmpty;
+using ::testing::Not;
+
class ElementStoreTest : public testing::Test {
public:
void SetUp() override {
@@ -31,8 +34,9 @@ class ElementStoreTest : public testing::Test {
const std::string& object_id) {
auto element = std::make_unique<ElementFinderResult>();
element->SetObjectId(object_id);
- element->SetNodeFrameId(
- web_contents_->GetMainFrame()->GetDevToolsFrameToken().ToString());
+ element->SetNodeFrameId(web_contents_->GetPrimaryMainFrame()
+ ->GetDevToolsFrameToken()
+ .ToString());
return element;
}
@@ -60,12 +64,15 @@ TEST_F(ElementStoreTest, AddElementToStore) {
TEST_F(ElementStoreTest, GetElementFromStore) {
auto element = CreateElement("1");
+ element->SetBackendNodeId(1);
AddElement("1", std::move(element));
ElementFinderResult result;
EXPECT_EQ(ACTION_APPLIED,
element_store_->GetElement("1", &result).proto_status());
EXPECT_EQ("1", result.object_id());
+ EXPECT_EQ(1, *result.backend_node_id());
+ EXPECT_THAT(result.node_frame_id(), Not(IsEmpty()));
}
TEST_F(ElementStoreTest, GetElementFromStoreWithBadFrameHost) {
@@ -87,7 +94,7 @@ TEST_F(ElementStoreTest, GetElementFromStoreWithNoFrameId) {
ElementFinderResult result;
EXPECT_EQ(ACTION_APPLIED,
element_store_->GetElement("1", &result).proto_status());
- EXPECT_EQ(web_contents_->GetMainFrame(), result.render_frame_host());
+ EXPECT_EQ(web_contents_->GetPrimaryMainFrame(), result.render_frame_host());
}
TEST_F(ElementStoreTest, AddElementToStoreOverwrites) {
diff --git a/chromium/components/autofill_assistant/browser/web/fake_element_store.cc b/chromium/components/autofill_assistant/browser/web/fake_element_store.cc
index 7fc1400b75c..8568a797d03 100644
--- a/chromium/components/autofill_assistant/browser/web/fake_element_store.cc
+++ b/chromium/components/autofill_assistant/browser/web/fake_element_store.cc
@@ -27,7 +27,7 @@ ClientStatus FakeElementStore::GetElement(
out_element->SetNodeFrameId(it->second.object_data.node_frame_id);
out_element->SetFrameStack(it->second.frame_stack);
if (web_contents_ != nullptr) {
- out_element->SetRenderFrameHost(web_contents_->GetMainFrame());
+ out_element->SetRenderFrameHost(web_contents_->GetPrimaryMainFrame());
}
return OkClientStatus();
}
diff --git a/chromium/components/autofill_assistant/browser/web/js_filter_builder.cc b/chromium/components/autofill_assistant/browser/web/js_filter_builder.cc
index 3be18b997e1..ce9bf589c9b 100644
--- a/chromium/components/autofill_assistant/browser/web/js_filter_builder.cc
+++ b/chromium/components/autofill_assistant/browser/web/js_filter_builder.cc
@@ -157,6 +157,12 @@ bool JsFilterBuilder::AddFilter(const SelectorProto::Filter& filter) {
return true;
}
+ case SelectorProto::Filter::kParent:
+ AddLine("elements = elements.flatMap((e) => {");
+ AddLine(" return e.parentElement ? [e.parentElement] : [];");
+ AddLine("});");
+ return true;
+
case SelectorProto::Filter::kEnterFrame:
case SelectorProto::Filter::kPseudoType:
case SelectorProto::Filter::FILTER_NOT_SET:
@@ -179,8 +185,8 @@ std::string JsFilterBuilder::AddRegexpInstance(const TextFilter& filter) {
void JsFilterBuilder::AddRegexpFilter(const TextFilter& filter,
const std::string& property) {
std::string re_var = AddRegexpInstance(filter);
- AddLine({"elements = elements.filter((e) => ", re_var, ".test(e.", property,
- "));"});
+ AddLine({"elements = elements.filter((e) => ", re_var, ".test(e[",
+ AddArgument(property), "]));"});
}
std::string JsFilterBuilder::DeclareVariable() {
diff --git a/chromium/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.cc b/chromium/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.cc
new file mode 100644
index 00000000000..93594192da2
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.cc
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/web/mock_autofill_assistant_agent.h"
+
+#include "base/test/bind.h"
+#include "content/public/browser/render_frame_host.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+
+namespace autofill_assistant {
+
+MockAutofillAssistantAgent::MockAutofillAssistantAgent() = default;
+MockAutofillAssistantAgent::~MockAutofillAssistantAgent() = default;
+
+void MockAutofillAssistantAgent::BindPendingReceiver(
+ mojo::ScopedInterfaceEndpointHandle handle) {
+ receivers_.Add(this,
+ mojo::PendingAssociatedReceiver<mojom::AutofillAssistantAgent>(
+ std::move(handle)));
+}
+
+// static
+void MockAutofillAssistantAgent::RegisterForAllFrames(
+ content::WebContents* web_contents,
+ MockAutofillAssistantAgent* agent) {
+ web_contents->GetPrimaryMainFrame()->ForEachRenderFrameHost(
+ base::BindLambdaForTesting([agent](content::RenderFrameHost* host) {
+ host->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting(
+ mojom::AutofillAssistantAgent::Name_,
+ base::BindRepeating(
+ &MockAutofillAssistantAgent::BindPendingReceiver,
+ base::Unretained(agent)));
+ }));
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h b/chromium/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h
new file mode 100644
index 00000000000..4b3fca573c4
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_MOCK_AUTOFILL_ASSISTANT_AGENT_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_MOCK_AUTOFILL_ASSISTANT_AGENT_H_
+
+#include "base/callback.h"
+#include "base/time/time.h"
+#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+class MockAutofillAssistantAgent : public mojom::AutofillAssistantAgent {
+ public:
+ MockAutofillAssistantAgent();
+ ~MockAutofillAssistantAgent() override;
+
+ void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle);
+ static void RegisterForAllFrames(content::WebContents* web_contents,
+ MockAutofillAssistantAgent* agent);
+
+ MOCK_METHOD(void,
+ GetSemanticNodes,
+ (int32_t role,
+ int32_t objective,
+ bool ignore_objective,
+ base::TimeDelta model_timeout,
+ base::OnceCallback<void(mojom::NodeDataStatus,
+ const std::vector<NodeData>&)> callback),
+ (override));
+ MOCK_METHOD(void,
+ SetElementValue,
+ (int32_t backend_node_id,
+ const std::u16string& value,
+ bool send_events,
+ base::OnceCallback<void(bool)> callback),
+ (override));
+
+ private:
+ mojo::AssociatedReceiverSet<mojom::AutofillAssistantAgent> receivers_;
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_MOCK_AUTOFILL_ASSISTANT_AGENT_H_
diff --git a/chromium/components/autofill_assistant/browser/web/mock_web_controller.h b/chromium/components/autofill_assistant/browser/web/mock_web_controller.h
index 00ecba63032..30280060b96 100644
--- a/chromium/components/autofill_assistant/browser/web/mock_web_controller.h
+++ b/chromium/components/autofill_assistant/browser/web/mock_web_controller.h
@@ -19,6 +19,7 @@
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
+class ElementFinderResult;
struct RectF;
class MockWebController : public WebController {
diff --git a/chromium/components/autofill_assistant/browser/web/selector_observer.cc b/chromium/components/autofill_assistant/browser/web/selector_observer.cc
index a6a38e69c9e..1b12eaf2f41 100644
--- a/chromium/components/autofill_assistant/browser/web/selector_observer.cc
+++ b/chromium/components/autofill_assistant/browser/web/selector_observer.cc
@@ -61,23 +61,31 @@ SelectorObserver::RequestedElement::~RequestedElement() = default;
SelectorObserver::RequestedElement::RequestedElement(const RequestedElement&) =
default;
+SelectorObserver::Settings::Settings(const base::TimeDelta& max_wait_time,
+ const base::TimeDelta& min_check_interval,
+ const base::TimeDelta& extra_timeout,
+ const base::TimeDelta& debounce_interval)
+ : max_wait_time(max_wait_time),
+ min_check_interval(min_check_interval),
+ extra_timeout(extra_timeout),
+ debounce_interval(debounce_interval) {}
+SelectorObserver::Settings::~Settings() = default;
+SelectorObserver::Settings::Settings(const Settings&) = default;
+
SelectorObserver::SelectorObserver(
const std::vector<ObservableSelector>& selectors,
- base::TimeDelta max_wait_time,
- base::TimeDelta periodic_check_interval,
- base::TimeDelta extra_timeout,
+ const Settings& settings,
content::WebContents* web_contents,
DevtoolsClient* devtools_client,
const UserData* user_data,
Callback update_callback)
- : periodic_check_interval_(periodic_check_interval),
- extra_timeout_(extra_timeout),
+ : settings_(settings),
devtools_client_(devtools_client),
web_contents_(web_contents),
user_data_(user_data),
update_callback_(update_callback) {
const DomRoot root(/* frame_id = */ "", DomRoot::kUseMainDoc);
- wait_time_remaining_ms_[root] = max_wait_time.InMilliseconds();
+ wait_time_remaining_ms_[root] = settings.max_wait_time.InMilliseconds();
for (auto& selector : selectors) {
selectors_.emplace(std::make_pair(selector.selector_id, selector));
// Every selector starts in the root frame
@@ -106,7 +114,7 @@ ClientStatus SelectorObserver::Start(base::OnceClosure finished_callback) {
ResolveObjectIdAndInjectFrame(root, 0);
timeout_timer_ = std::make_unique<base::OneShotTimer>();
- timeout_timer_->Start(FROM_HERE, MaxTimeRemaining() + extra_timeout_,
+ timeout_timer_->Start(FROM_HERE, MaxTimeRemaining() + settings_.extra_timeout,
base::BindOnce(&SelectorObserver::OnHardTimeout,
weak_ptr_factory_.GetWeakPtr()));
@@ -814,15 +822,21 @@ std::string SelectorObserver::BuildExpression(const DomRoot& dom_root) const {
snippet.AddLine("(function selectorObserver() {");
snippet.AddLine(
{"const pollInterval = ",
- base::NumberToString(periodic_check_interval_.InMilliseconds()), ";"});
+ base::NumberToString(settings_.min_check_interval.InMilliseconds()),
+ ";"});
int max_wait_time = wait_time_remaining_ms_.at(dom_root);
- snippet.AddLine({"const maxRuntime = ",
- base::NumberToString(base::saturated_cast<int>(
- (base::Milliseconds(max_wait_time) + extra_timeout_)
- .InMilliseconds())),
- ";"});
+ snippet.AddLine(
+ {"const maxRuntime = ",
+ base::NumberToString(base::saturated_cast<int>(
+ (base::Milliseconds(max_wait_time) + settings_.extra_timeout)
+ .InMilliseconds())),
+ ";"});
snippet.AddLine(
{"const maxWaitTime = ", base::NumberToString(max_wait_time), ";"});
+ snippet.AddLine(
+ {"const debounceInterval = ",
+ base::NumberToString(settings_.debounce_interval.InMilliseconds()),
+ ";"});
snippet.AddLine("const selectors = [");
size_t depth = frame_depth_.at(dom_root);
diff --git a/chromium/components/autofill_assistant/browser/web/selector_observer.h b/chromium/components/autofill_assistant/browser/web/selector_observer.h
index e89f7b9d2ce..0ffb89b63af 100644
--- a/chromium/components/autofill_assistant/browser/web/selector_observer.h
+++ b/chromium/components/autofill_assistant/browser/web/selector_observer.h
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/containers/flat_map.h"
+#include "base/memory/raw_ptr.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
@@ -60,6 +61,7 @@ class SelectorObserver : public WebControllerWorker {
// If true, match will fail if more that one element matches the selector.
bool strict;
};
+
// An update to the match status of a selector.
struct Update {
Update();
@@ -73,6 +75,7 @@ class SelectorObserver : public WebControllerWorker {
// fetch the element later.
int element_id;
};
+
struct RequestedElement {
RequestedElement(const SelectorId& selector_id, int element_id);
~RequestedElement();
@@ -85,18 +88,37 @@ class SelectorObserver : public WebControllerWorker {
// end.
int element_id;
};
+
+ // Settings to configure the selector observer.
+ struct Settings {
+ Settings(const base::TimeDelta& max_wait_time,
+ const base::TimeDelta& min_check_interval,
+ const base::TimeDelta& extra_timeout,
+ const base::TimeDelta& debounce_interval);
+ ~Settings();
+ Settings(const Settings&);
+ // Maximum amount of time it will wait for an element.
+ const base::TimeDelta max_wait_time;
+ // Selector checks will run at least this often, even if no DOM changes are
+ // detected.
+ const base::TimeDelta min_check_interval;
+ // Extra wait time before assuming something has failed and giving up.
+ const base::TimeDelta extra_timeout;
+ // Wait until no DOM changes are received for this amount of time to check
+ // the selectors. An interval of 0 effectively disables debouncing.
+ const base::TimeDelta debounce_interval;
+ };
+
using Callback = base::RepeatingCallback<
void(const ClientStatus&, const std::vector<Update>&, SelectorObserver*)>;
// |content::WebContents| and |DevtoolsClient| need to outlive this instance.
// |UserData| needs to exist until Start() is called.
explicit SelectorObserver(const std::vector<ObservableSelector>& selectors,
- base::TimeDelta max_wait_time,
- base::TimeDelta periodic_check_interval,
- base::TimeDelta extra_timeout,
- content::WebContents*,
- DevtoolsClient*,
- const UserData*,
+ const Settings& settings,
+ content::WebContents* web_contents,
+ DevtoolsClient* devtools_client,
+ const UserData* user_data,
Callback update_callback);
~SelectorObserver() override;
@@ -143,16 +165,14 @@ class SelectorObserver : public WebControllerWorker {
ERROR_STATE = 4,
};
State state_ = State::INITIALIZED;
- const base::TimeDelta periodic_check_interval_;
- const base::TimeDelta extra_timeout_;
- base::TimeDelta max_wait_time_;
+ const Settings settings_;
base::TimeTicks started_;
std::unique_ptr<base::OneShotTimer> timeout_timer_;
base::flat_map<SelectorId, ObservableSelector> selectors_;
- DevtoolsClient* devtools_client_;
- content::WebContents* web_contents_;
- const UserData* user_data_;
+ raw_ptr<DevtoolsClient> devtools_client_;
+ raw_ptr<content::WebContents> web_contents_;
+ raw_ptr<const UserData> user_data_;
Callback update_callback_;
base::OnceClosure finished_callback_;
diff --git a/chromium/components/autofill_assistant/browser/web/selector_observer_script.h b/chromium/components/autofill_assistant/browser/web/selector_observer_script.h
index f4207e521ba..05e4e97f514 100644
--- a/chromium/components/autofill_assistant/browser/web/selector_observer_script.h
+++ b/chromium/components/autofill_assistant/browser/web/selector_observer_script.h
@@ -13,7 +13,7 @@ namespace selector_observer_script {
// (1) selector_id -> element
// (2) selector_id -> element
// (3) element_id -> element
-// (4) uses setTimeout so that initialization isn't blocked checking the
+// (4) uses queueMicrotask so that initialization isn't blocked checking the
// selectors.
// (5) In case c++ doesn't call terminate()
// (7) A result is serializable by value. Unserializable elements are saved in
@@ -75,14 +75,15 @@ constexpr char kWaitForChangeScript[] = R"eof(
return runTime - count * avgCheckTime;
};
- const onChange = () => {
+ const checkSelectors = () => {
if (startTime == null) {
startTime = now();
}
checkCount += 1;
const start = now();
- if (pollingTid) clearTimeout(pollingTid);
- pollingTid = setTimeout(onChange, pollInterval);
+ clearTimeout(pollingTid);
+ clearTimeout(debounceTid);
+ pollingTid = setTimeout(checkSelectors, pollInterval);
for (const selector of selectors) {
const node = runSelector(selector);
@@ -108,6 +109,15 @@ constexpr char kWaitForChangeScript[] = R"eof(
}
};
+ let debounceTid = 0;
+ const onChange = () => {
+ clearTimeout(debounceTid);
+ debounceTid = setTimeout(checkSelectors, debounceInterval);
+ };
+
+ // (4)
+ queueMicrotask(checkSelectors);
+
const config = {
attributes: true,
childList: true,
@@ -121,8 +131,6 @@ constexpr char kWaitForChangeScript[] = R"eof(
if (pollingTid) clearTimeout(pollingTid);
clearTimeout(disconnectTid);
};
- // (4)
- setTimeout(onChange, 0);
// (5)
const disconnectTid = setTimeout(terminate, maxRuntime);
@@ -176,7 +184,7 @@ constexpr char kWaitForChangeScript[] = R"eof(
selectors.push(selector);
}
});
- onChange();
+ checkSelectors();
},
getElements(elementIds) {
const result = {};
diff --git a/chromium/components/autofill_assistant/browser/web/semantic_element_finder.cc b/chromium/components/autofill_assistant/browser/web/semantic_element_finder.cc
new file mode 100644
index 00000000000..5ed6bf88d1c
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/semantic_element_finder.cc
@@ -0,0 +1,246 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/web/semantic_element_finder.h"
+
+#include <utility>
+
+#include "base/barrier_callback.h"
+#include "base/time/time.h"
+#include "components/autofill_assistant/browser/devtools/devtools_client.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/user_data.h"
+#include "components/autofill_assistant/browser/user_data_util.h"
+#include "components/autofill_assistant/browser/web/element.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
+#include "components/autofill_assistant/browser/web/js_filter_builder.h"
+#include "components/autofill_assistant/browser/web/web_controller_util.h"
+#include "components/autofill_assistant/content/browser/content_autofill_assistant_driver.h"
+#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
+#include "content/public/browser/global_routing_id.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+
+namespace autofill_assistant {
+
+namespace {
+
+void AddHostToList(std::vector<content::GlobalRenderFrameHostId>& host_ids,
+ content::RenderFrameHost* host) {
+ host_ids.push_back(host->GetGlobalId());
+}
+
+ElementFinderInfoProto::SemanticInferenceStatus
+NodeDataStatusToSemanticInferenceStatus(
+ mojom::NodeDataStatus node_data_status) {
+ switch (node_data_status) {
+ case mojom::NodeDataStatus::kSuccess:
+ return ElementFinderInfoProto::SUCCESS;
+ case mojom::NodeDataStatus::kUnexpectedError:
+ return ElementFinderInfoProto::UNEXPECTED_ERROR;
+ case mojom::NodeDataStatus::kInitializationError:
+ return ElementFinderInfoProto::INITIALIZATION_ERROR;
+ case mojom::NodeDataStatus::kModelLoadError:
+ return ElementFinderInfoProto::MODEL_LOAD_ERROR;
+ case mojom::NodeDataStatus::kModelLoadTimeout:
+ return ElementFinderInfoProto::MODEL_LOAD_TIMEOUT;
+ }
+}
+
+} // namespace
+
+SemanticElementFinder::SemanticElementFinder(
+ content::WebContents* web_contents,
+ DevtoolsClient* devtools_client,
+ AnnotateDomModelService* annotate_dom_model_service,
+ const Selector& selector)
+ : web_contents_(web_contents),
+ devtools_client_(devtools_client),
+ annotate_dom_model_service_(annotate_dom_model_service),
+ selector_(selector) {
+ DCHECK(annotate_dom_model_service_);
+}
+
+SemanticElementFinder::~SemanticElementFinder() = default;
+
+void SemanticElementFinder::GiveUpWithError(const ClientStatus& status) {
+ DCHECK(!status.ok());
+ if (!callback_) {
+ return;
+ }
+
+ SendResult(status, ElementFinderResult::EmptyResult());
+}
+
+void SemanticElementFinder::ResultFound(
+ content::RenderFrameHost* render_frame_host,
+ const std::string& object_id,
+ int backend_node_id) {
+ if (!callback_) {
+ return;
+ }
+
+ ElementFinderResult result;
+ result.SetRenderFrameHost(render_frame_host);
+ result.SetObjectId(object_id);
+ result.SetBackendNodeId(backend_node_id);
+
+ SendResult(OkClientStatus(), result);
+}
+
+void SemanticElementFinder::SendResult(const ClientStatus& status,
+ const ElementFinderResult& result) {
+ DCHECK(callback_);
+ std::move(callback_).Run(status,
+ std::make_unique<ElementFinderResult>(result));
+}
+
+void SemanticElementFinder::Start(const ElementFinderResult& start_element,
+ BaseElementFinder::Callback callback) {
+ callback_ = std::move(callback);
+
+ auto* start_frame = start_element.render_frame_host();
+ if (!start_frame) {
+ start_frame = web_contents_->GetPrimaryMainFrame();
+ }
+ RunAnnotateDomModel(start_frame);
+}
+
+ElementFinderInfoProto SemanticElementFinder::GetLogInfo() const {
+ DCHECK(!callback_); // Run after finish.
+
+ ElementFinderInfoProto info;
+ DCHECK(selector_.proto.has_semantic_information());
+ for (auto node_data_status : node_data_frame_status_) {
+ info.mutable_semantic_inference_result()->add_status_per_frame(
+ NodeDataStatusToSemanticInferenceStatus(node_data_status));
+ }
+ for (const auto& semantic_node_result : semantic_node_results_) {
+ auto* predicted_element =
+ info.mutable_semantic_inference_result()->add_predicted_elements();
+ predicted_element->set_backend_node_id(
+ semantic_node_result.backend_node_id());
+ *predicted_element->mutable_semantic_information() =
+ selector_.proto.semantic_information();
+ // TODO(b/217160707): For the ignore_objective case this is not correct
+ // and the inferred objective should be returned from the Agent and used
+ // here.
+ }
+
+ return info;
+}
+
+int SemanticElementFinder::GetBackendNodeId() const {
+ if (semantic_node_results_.empty()) {
+ return 0;
+ }
+ return semantic_node_results_[0].backend_node_id();
+}
+
+void SemanticElementFinder::RunAnnotateDomModel(
+ content::RenderFrameHost* start_frame) {
+ std::vector<content::GlobalRenderFrameHostId> host_ids;
+ start_frame->ForEachRenderFrameHost(
+ base::BindRepeating(&AddHostToList, std::ref(host_ids)));
+ const auto run_on_frame =
+ base::BarrierCallback<std::vector<GlobalBackendNodeId>>(
+ host_ids.size(),
+ base::BindOnce(&SemanticElementFinder::OnRunAnnotateDomModel,
+ weak_ptr_factory_.GetWeakPtr()));
+ for (const auto& host_id : host_ids) {
+ RunAnnotateDomModelOnFrame(host_id, run_on_frame);
+ }
+}
+
+void SemanticElementFinder::RunAnnotateDomModelOnFrame(
+ const content::GlobalRenderFrameHostId& host_id,
+ base::OnceCallback<void(std::vector<GlobalBackendNodeId>)> callback) {
+ content::RenderFrameHost* render_frame_host =
+ content::RenderFrameHost::FromID(host_id);
+ if (!render_frame_host) {
+ std::move(callback).Run(std::vector<GlobalBackendNodeId>());
+ return;
+ }
+
+ auto* driver = ContentAutofillAssistantDriver::GetOrCreateForRenderFrameHost(
+ render_frame_host, annotate_dom_model_service_);
+ if (!driver) {
+ NOTREACHED();
+ std::move(callback).Run(std::vector<GlobalBackendNodeId>());
+ return;
+ }
+
+ driver->GetAutofillAssistantAgent()->GetSemanticNodes(
+ selector_.proto.semantic_information().semantic_role(),
+ selector_.proto.semantic_information().objective(),
+ selector_.proto.semantic_information().ignore_objective(),
+ base::Milliseconds(
+ selector_.proto.semantic_information().model_timeout_ms()),
+ base::BindOnce(&SemanticElementFinder::OnRunAnnotateDomModelOnFrame,
+ weak_ptr_factory_.GetWeakPtr(), host_id,
+ std::move(callback)));
+}
+
+void SemanticElementFinder::OnRunAnnotateDomModelOnFrame(
+ const content::GlobalRenderFrameHostId& host_id,
+ base::OnceCallback<void(std::vector<GlobalBackendNodeId>)> callback,
+ mojom::NodeDataStatus status,
+ const std::vector<NodeData>& node_data) {
+ node_data_frame_status_.emplace_back(status);
+
+ std::vector<GlobalBackendNodeId> node_ids;
+ for (const auto& node : node_data) {
+ node_ids.emplace_back(GlobalBackendNodeId(host_id, node.backend_node_id));
+ }
+ std::move(callback).Run(node_ids);
+}
+
+void SemanticElementFinder::OnRunAnnotateDomModel(
+ const std::vector<std::vector<GlobalBackendNodeId>>& all_nodes) {
+ for (const auto& node_ids : all_nodes) {
+ semantic_node_results_.insert(semantic_node_results_.end(),
+ node_ids.begin(), node_ids.end());
+ }
+
+ // For now we only support finding a single element.
+ // TODO(b/224746702): Emit multiple ResolveNode calls for the case where the
+ // result type is not ResultType::kExactlyOneMatch.
+ if (semantic_node_results_.size() > 1) {
+ VLOG(1) << __func__ << " Got " << semantic_node_results_.size()
+ << " matches for " << selector_ << ", when only 1 was expected.";
+ GiveUpWithError(ClientStatus(TOO_MANY_ELEMENTS));
+ return;
+ }
+ if (semantic_node_results_.empty()) {
+ GiveUpWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+ return;
+ }
+
+ // We need to set the empty string for the frame id. The expectation is that
+ // backend node ids are global and devtools is able to resolve the node
+ // without an explicit frame id.
+ devtools_client_->GetDOM()->ResolveNode(
+ dom::ResolveNodeParams::Builder()
+ .SetBackendNodeId(semantic_node_results_[0].backend_node_id())
+ .Build(),
+ /* current_frame_id= */ std::string(),
+ base::BindOnce(&SemanticElementFinder::OnResolveNodeForAnnotateDom,
+ weak_ptr_factory_.GetWeakPtr(),
+ semantic_node_results_[0]));
+}
+
+void SemanticElementFinder::OnResolveNodeForAnnotateDom(
+ GlobalBackendNodeId node,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::ResolveNodeResult> result) {
+ if (result && result->GetObject() && result->GetObject()->HasObjectId()) {
+ ResultFound(content::RenderFrameHost::FromID(node.host_id()),
+ result->GetObject()->GetObjectId(), node.backend_node_id());
+ return;
+ }
+ SendResult(ClientStatus(ELEMENT_RESOLUTION_FAILED),
+ ElementFinderResult::EmptyResult());
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/web/semantic_element_finder.h b/chromium/components/autofill_assistant/browser/web/semantic_element_finder.h
new file mode 100644
index 00000000000..00a6854395b
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/semantic_element_finder.h
@@ -0,0 +1,105 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_SEMANTIC_ELEMENT_FINDER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_SEMANTIC_ELEMENT_FINDER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/client_status.h"
+#include "components/autofill_assistant/browser/devtools/devtools/domains/types_dom.h"
+#include "components/autofill_assistant/browser/devtools/devtools_client.h"
+#include "components/autofill_assistant/browser/selector.h"
+#include "components/autofill_assistant/browser/web/base_element_finder.h"
+#include "components/autofill_assistant/browser/web/element.h"
+#include "components/autofill_assistant/content/browser/annotate_dom_model_service.h"
+#include "components/autofill_assistant/content/common/autofill_assistant_types.mojom.h"
+#include "components/autofill_assistant/content/common/node_data.h"
+
+namespace content {
+class WebContents;
+class RenderFrameHost;
+struct GlobalRenderFrameHostId;
+} // namespace content
+
+namespace autofill_assistant {
+class DevtoolsClient;
+class ElementFinderResult;
+
+class SemanticElementFinder : public BaseElementFinder {
+ public:
+ SemanticElementFinder(content::WebContents* web_contents,
+ DevtoolsClient* devtools_client,
+ AnnotateDomModelService* annotate_dom_model_service,
+ const Selector& selector);
+ ~SemanticElementFinder() override;
+
+ SemanticElementFinder(const SemanticElementFinder&) = delete;
+ SemanticElementFinder& operator=(const SemanticElementFinder&) = delete;
+
+ void Start(const ElementFinderResult& start_element,
+ BaseElementFinder::Callback callback) override;
+
+ ElementFinderInfoProto GetLogInfo() const override;
+
+ // Returns the backend node id of the first result (if any), or 0.
+ int GetBackendNodeId() const override;
+
+ private:
+ // Returns the given status and no element. This expects an error status.
+ void GiveUpWithError(const ClientStatus& status);
+
+ // Builds a result from the |render_frame_host|, the |object_id| and the
+ // |backend_node_id| returns it withan ok status.
+ void ResultFound(content::RenderFrameHost* render_frame_host,
+ const std::string& object_id,
+ int backend_node_id);
+
+ // Call |callback_| with the |status| and |result|.
+ void SendResult(const ClientStatus& status,
+ const ElementFinderResult& result);
+
+ // Run the model annotation on all frames for the current |start_frame|.
+ void RunAnnotateDomModel(content::RenderFrameHost* start_frame);
+
+ // Runs the model on the frame identified by |host_id|.
+ void RunAnnotateDomModelOnFrame(
+ const content::GlobalRenderFrameHostId& host_id,
+ base::OnceCallback<void(std::vector<GlobalBackendNodeId>)> callback);
+ void OnRunAnnotateDomModelOnFrame(
+ const content::GlobalRenderFrameHostId& host_id,
+ base::OnceCallback<void(std::vector<GlobalBackendNodeId>)> callback,
+ mojom::NodeDataStatus status,
+ const std::vector<NodeData>& node_data);
+
+ // Called once the model has been run on all frames.
+ void OnRunAnnotateDomModel(
+ const std::vector<std::vector<GlobalBackendNodeId>>& all_nodes);
+
+ void OnResolveNodeForAnnotateDom(
+ GlobalBackendNodeId node,
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<dom::ResolveNodeResult> result);
+
+ const raw_ptr<content::WebContents> web_contents_;
+ const raw_ptr<DevtoolsClient> devtools_client_;
+ const raw_ptr<AnnotateDomModelService> annotate_dom_model_service_;
+ const Selector selector_;
+ BaseElementFinder::Callback callback_;
+
+ // Elements gathered through all frames. Unused if the |selector_| does not
+ // contain |SemanticInformation|.
+ std::vector<GlobalBackendNodeId> semantic_node_results_;
+ std::vector<mojom::NodeDataStatus> node_data_frame_status_;
+
+ base::WeakPtrFactory<SemanticElementFinder> weak_ptr_factory_{this};
+};
+
+} // namespace autofill_assistant
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_SEMANTIC_ELEMENT_FINDER_H_
diff --git a/chromium/components/autofill_assistant/browser/web/semantic_element_finder_browsertest.cc b/chromium/components/autofill_assistant/browser/web/semantic_element_finder_browsertest.cc
new file mode 100644
index 00000000000..1b564d8072d
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/web/semantic_element_finder_browsertest.cc
@@ -0,0 +1,470 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/browser/web/web_controller.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/run_loop.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/test/bind.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/autofill_assistant/browser/actions/wait_for_dom_action.h"
+#include "components/autofill_assistant/browser/base_browsertest.h"
+#include "components/autofill_assistant/browser/client_status.h"
+#include "components/autofill_assistant/browser/fake_script_executor_ui_delegate.h"
+#include "components/autofill_assistant/browser/mock_script_executor_delegate.h"
+#include "components/autofill_assistant/browser/model.pb.h"
+#include "components/autofill_assistant/browser/script.h"
+#include "components/autofill_assistant/browser/script_executor.h"
+#include "components/autofill_assistant/browser/selector.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/service/mock_service.h"
+#include "components/autofill_assistant/browser/trigger_context.h"
+#include "components/autofill_assistant/browser/user_data.h"
+#include "components/autofill_assistant/browser/web/element.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
+#include "components/autofill_assistant/browser/web/element_finder_result_type.h"
+#include "components/autofill_assistant/browser/web/element_store.h"
+#include "components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h"
+#include "components/autofill_assistant/content/common/autofill_assistant_types.mojom.h"
+#include "components/autofill_assistant/content/common/node_data.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace autofill_assistant {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::IsEmpty;
+using ::testing::Return;
+using ::testing::WithArgs;
+
+} // namespace
+
+class SemanticElementFinderBrowserTest
+ : public autofill_assistant::BaseBrowserTest,
+ public content::WebContentsObserver {
+ public:
+ SemanticElementFinderBrowserTest() {}
+
+ SemanticElementFinderBrowserTest(const SemanticElementFinderBrowserTest&) =
+ delete;
+ SemanticElementFinderBrowserTest& operator=(
+ const SemanticElementFinderBrowserTest&) = delete;
+
+ ~SemanticElementFinderBrowserTest() override {}
+
+ void SetUpOnMainThread() override {
+ BaseBrowserTest::SetUpOnMainThread();
+
+ MockAutofillAssistantAgent::RegisterForAllFrames(
+ shell()->web_contents(), &autofill_assistant_agent_);
+
+ annotate_dom_model_service_ = std::make_unique<AnnotateDomModelService>(
+ /* opt_guide= */ nullptr, /* background_task_runner= */ nullptr);
+ web_controller_ = WebController::CreateForWebContents(
+ shell()->web_contents(), &user_data_, &log_info_,
+ annotate_dom_model_service_.get(),
+ /* enable_full_stack_traces= */ true);
+
+ Observe(shell()->web_contents());
+ }
+
+ void FindElement(const Selector& selector,
+ ClientStatus* status_out,
+ ElementFinderResult* result_out) {
+ base::RunLoop run_loop;
+ web_controller_->FindElement(
+ selector, /* strict_mode= */ true,
+ base::BindOnce(&SemanticElementFinderBrowserTest::OnFindElement,
+ base::Unretained(this), run_loop.QuitClosure(),
+ base::Unretained(status_out),
+ base::Unretained(result_out)));
+ run_loop.Run();
+ }
+
+ void OnFindElement(base::OnceClosure done_callback,
+ ClientStatus* status_out,
+ ElementFinderResult* result_out,
+ const ClientStatus& status,
+ std::unique_ptr<ElementFinderResult> result) {
+ ASSERT_TRUE(result);
+ std::move(done_callback).Run();
+ if (status_out)
+ *status_out = status;
+ if (result_out)
+ *result_out = *result;
+ }
+
+ void RunStrictElementCheck(const Selector& selector, bool expected_result) {
+ ClientStatus status;
+ ElementFinderResult ignored_element;
+ FindElement(selector, &status, &ignored_element);
+ EXPECT_EQ(expected_result, status.ok())
+ << "selector: " << selector << " status: " << expected_result;
+ }
+
+ void FindElementExpectEmptyResult(const Selector& selector) {
+ ClientStatus status;
+ ElementFinderResult element;
+ FindElement(selector, &status, &element);
+ EXPECT_EQ(ELEMENT_RESOLUTION_FAILED, status.proto_status());
+ EXPECT_THAT(element.object_id(), IsEmpty());
+ }
+
+ void OnScriptFinished(base::OnceClosure done_callback,
+ const ScriptExecutor::Result& result) {
+ std::move(done_callback).Run();
+ }
+
+ ClientStatus RunWaitForDom(
+ const ActionProto& wait_for_dom_action,
+ bool use_observers,
+ base::OnceCallback<void(ScriptExecutor*)> run_expectations) {
+ MockScriptExecutorDelegate mock_script_executor_delegate;
+ ON_CALL(mock_script_executor_delegate, GetWebController)
+ .WillByDefault(Return(web_controller_.get()));
+ TriggerContext trigger_context;
+ if (use_observers) {
+ trigger_context.SetScriptParameters(std::make_unique<ScriptParameters>(
+ base::flat_map<std::string, std::string>{
+ {"ENABLE_OBSERVER_WAIT_FOR_DOM", "true"}}));
+ }
+
+ MockService mock_service;
+ ActionsResponseProto actions_response;
+ *actions_response.add_actions() = wait_for_dom_action;
+ std::string serialized_actions_response;
+ actions_response.SerializeToString(&serialized_actions_response);
+ EXPECT_CALL(mock_service, GetActions)
+ .WillOnce(RunOnceCallback<5>(200, serialized_actions_response,
+ ServiceRequestSender::ResponseInfo{}));
+
+ std::vector<ProcessedActionProto> captured_processed_actions;
+ EXPECT_CALL(mock_service, GetNextActions)
+ .WillOnce(WithArgs<3, 6>(
+ [&captured_processed_actions](
+ const std::vector<ProcessedActionProto>& processed_actions,
+ ServiceRequestSender::ResponseCallback callback) {
+ captured_processed_actions = processed_actions;
+
+ // Send empty response to stop the script executor.
+ std::move(callback).Run(200, std::string(),
+ ServiceRequestSender::ResponseInfo{});
+ }));
+ ON_CALL(mock_script_executor_delegate, GetTriggerContext())
+ .WillByDefault(Return(&trigger_context));
+ ON_CALL(mock_script_executor_delegate, GetService())
+ .WillByDefault(Return(&mock_service));
+ GURL test_script_url("https://example.com");
+ ON_CALL(mock_script_executor_delegate, GetScriptURL())
+ .WillByDefault(testing::ReturnRef(test_script_url));
+ std::vector<std::unique_ptr<Script>> ordered_interrupts;
+ FakeScriptExecutorUiDelegate fake_script_executor_ui_delegate;
+ UserData fake_user_data;
+ ScriptExecutor script_executor(
+ /* script_path= */ std::string(),
+ /* additional_context= */ std::make_unique<TriggerContext>(),
+ /* global_payload= */ std::string(),
+ /* script_payload= */ std::string(),
+ /* listener= */ nullptr, &ordered_interrupts,
+ &mock_script_executor_delegate, &fake_script_executor_ui_delegate);
+ base::RunLoop run_loop;
+ script_executor.Run(
+ &fake_user_data,
+ base::BindOnce(&SemanticElementFinderBrowserTest::OnScriptFinished,
+ base::Unretained(this), run_loop.QuitClosure()));
+ run_loop.Run();
+ std::move(run_expectations).Run(&script_executor);
+
+ CHECK_EQ(captured_processed_actions.size(), 1u);
+ return ClientStatus(captured_processed_actions[0].status());
+ }
+
+ int GetBackendNodeId(Selector selector, ClientStatus* status_out) {
+ std::unique_ptr<ElementFinderResult> element_result;
+ int backend_node_id = -1;
+
+ base::RunLoop run_loop_1;
+ web_controller_->FindElement(
+ selector, true,
+ base::BindLambdaForTesting(
+ [&](const ClientStatus& status,
+ std::unique_ptr<ElementFinderResult> result) {
+ element_result = std::move(result);
+ *status_out = status;
+ run_loop_1.Quit();
+ }));
+ run_loop_1.Run();
+ if (!status_out->ok()) {
+ return backend_node_id;
+ }
+
+ // Second part in sequence, lookup backend node id.
+ base::RunLoop run_loop_2;
+ web_controller_->GetBackendNodeId(
+ *element_result,
+ base::BindLambdaForTesting([&](const ClientStatus& status, int id) {
+ *status_out = status;
+ backend_node_id = id;
+ run_loop_2.Quit();
+ }));
+ run_loop_2.Run();
+
+ log_info_.Clear();
+ return backend_node_id;
+ }
+
+ protected:
+ std::unique_ptr<WebController> web_controller_;
+ UserData user_data_;
+ ProcessedActionStatusDetailsProto log_info_;
+ MockAutofillAssistantAgent autofill_assistant_agent_;
+ std::unique_ptr<AnnotateDomModelService> annotate_dom_model_service_;
+};
+
+IN_PROC_BROWSER_TEST_F(SemanticElementFinderBrowserTest,
+ WaitForDomForSemanticElement) {
+ // This element is unique.
+ SelectorProto baseline_selector = ToSelectorProto("#select");
+
+ ClientStatus element_status;
+ int backend_node_id =
+ GetBackendNodeId(Selector(baseline_selector), &element_status);
+ EXPECT_TRUE(element_status.ok());
+
+ NodeData node_data;
+ node_data.backend_node_id = backend_node_id;
+ EXPECT_CALL(autofill_assistant_agent_,
+ GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+ .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
+ std::vector<NodeData>{node_data}))
+ // Capture any other frames.
+ .WillRepeatedly(RunOnceCallback<4>(
+ mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
+
+ ActionProto action_proto;
+ auto* wait_for_dom = action_proto.mutable_wait_for_dom();
+ auto* condition = wait_for_dom->mutable_wait_condition();
+ condition->mutable_client_id()->set_identifier("e");
+ condition->set_require_unique_element(true);
+ auto* semantic_information =
+ condition->mutable_match()->mutable_semantic_information();
+ semantic_information->set_semantic_role(1);
+ semantic_information->set_objective(2);
+
+ base::MockCallback<base::OnceCallback<void(ScriptExecutor*)>>
+ run_expectations;
+ EXPECT_CALL(run_expectations, Run(_))
+ .WillOnce([](ScriptExecutor* script_executor) {
+ EXPECT_TRUE(script_executor->GetElementStore()->HasElement("e"));
+ });
+ ClientStatus status = RunWaitForDom(action_proto, /* use_observers= */ false,
+ run_expectations.Get());
+ EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+}
+
+IN_PROC_BROWSER_TEST_F(SemanticElementFinderBrowserTest,
+ ElementExistenceCheckWithSemanticModel) {
+ ClientStatus status;
+ int backend_node_id = GetBackendNodeId(Selector({"#button"}), &status);
+ EXPECT_TRUE(status.ok());
+
+ NodeData node_data;
+ node_data.backend_node_id = backend_node_id;
+ EXPECT_CALL(autofill_assistant_agent_,
+ GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+ .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
+ std::vector<NodeData>{node_data}))
+ // Capture any other frames.
+ .WillRepeatedly(RunOnceCallback<4>(
+ mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
+
+ // We pretend that the button is the correct element.
+ SelectorProto proto;
+ auto* semantic_information = proto.mutable_semantic_information();
+ semantic_information->set_semantic_role(1);
+ semantic_information->set_objective(2);
+ RunStrictElementCheck(Selector(proto), true);
+
+ ASSERT_EQ(log_info_.element_finder_info().size(), 1);
+ const auto& result =
+ log_info_.element_finder_info(0).semantic_inference_result();
+ ASSERT_EQ(1, result.predicted_elements().size());
+ EXPECT_EQ(backend_node_id, result.predicted_elements(0).backend_node_id());
+ EXPECT_THAT(
+ 1, result.predicted_elements(0).semantic_information().semantic_role());
+ EXPECT_THAT(2,
+ result.predicted_elements(0).semantic_information().objective());
+}
+
+IN_PROC_BROWSER_TEST_F(SemanticElementFinderBrowserTest,
+ ElementExistenceCheckWithSemanticModelOOPIF) {
+ ClientStatus status;
+ int backend_node_id =
+ GetBackendNodeId(Selector({"#iframeExternal", "#button"}), &status);
+ EXPECT_TRUE(status.ok());
+
+ NodeData node_data;
+ node_data.backend_node_id = backend_node_id;
+ EXPECT_CALL(autofill_assistant_agent_,
+ GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+ .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
+ std::vector<NodeData>{node_data}))
+ // Capture any other frames.
+ .WillRepeatedly(RunOnceCallback<4>(
+ mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
+
+ // We pretend that the button is the correct element.
+ SelectorProto proto;
+ auto* semantic_information = proto.mutable_semantic_information();
+ semantic_information->set_semantic_role(1);
+ semantic_information->set_objective(2);
+ RunStrictElementCheck(Selector(proto), true);
+
+ ASSERT_EQ(log_info_.element_finder_info().size(), 1);
+ const auto& result =
+ log_info_.element_finder_info(0).semantic_inference_result();
+ ASSERT_EQ(1, result.predicted_elements().size());
+ EXPECT_EQ(backend_node_id, result.predicted_elements(0).backend_node_id());
+ EXPECT_THAT(
+ 1, result.predicted_elements(0).semantic_information().semantic_role());
+ EXPECT_THAT(2,
+ result.predicted_elements(0).semantic_information().objective());
+}
+
+IN_PROC_BROWSER_TEST_F(SemanticElementFinderBrowserTest,
+ ElementExistenceCheckWithSemanticModelNotFound) {
+ // All frames return an empty list as a result.
+ EXPECT_CALL(autofill_assistant_agent_,
+ GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+ .WillRepeatedly(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
+ std::vector<NodeData>{}));
+
+ SelectorProto proto;
+ auto* semantic_information = proto.mutable_semantic_information();
+ semantic_information->set_semantic_role(1);
+ semantic_information->set_objective(2);
+ FindElementExpectEmptyResult(Selector(proto));
+}
+
+IN_PROC_BROWSER_TEST_F(SemanticElementFinderBrowserTest,
+ ElementExistenceCheckWithSemanticMultipleFound) {
+ SelectorProto proto;
+ auto* semantic_information = proto.mutable_semantic_information();
+ semantic_information->set_semantic_role(1);
+ semantic_information->set_objective(2);
+
+ NodeData node_data;
+ node_data.backend_node_id = 5;
+ NodeData node_data_other;
+ node_data_other.backend_node_id = 13;
+ EXPECT_CALL(autofill_assistant_agent_,
+ GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+ .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
+ std::vector<NodeData>{node_data}))
+ .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
+ std::vector<NodeData>{node_data_other}))
+ // Capture any other frames.
+ .WillRepeatedly(RunOnceCallback<4>(
+ mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
+
+ // Two elements are found in different frames.
+ ClientStatus status;
+ FindElement(Selector(proto), &status, nullptr);
+ EXPECT_EQ(TOO_MANY_ELEMENTS, status.proto_status());
+}
+
+IN_PROC_BROWSER_TEST_F(
+ SemanticElementFinderBrowserTest,
+ ElementExistenceCheckWithSemanticModelUsesIgnoreObjective) {
+ NodeData node_data;
+ node_data.backend_node_id = 5;
+ EXPECT_CALL(autofill_assistant_agent_,
+ GetSemanticNodes(1, 2, true, base::Milliseconds(5000), _))
+ .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
+ std::vector<NodeData>{node_data}))
+ .WillRepeatedly(RunOnceCallback<4>(
+ mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
+
+ SelectorProto proto;
+ auto* semantic_information = proto.mutable_semantic_information();
+ semantic_information->set_semantic_role(1);
+ semantic_information->set_objective(2);
+ // All we want is this to be propagated to the GetSemanticNodes call as
+ // configured in the previous expectation.
+ semantic_information->set_ignore_objective(true);
+
+ ClientStatus ignore_status;
+ FindElement(Selector(proto), &ignore_status, nullptr);
+
+ // TODO(b/217160707): For now we expect the originally passed in semantic info
+ // to be logged instead of the objective inferred by the model.
+ ASSERT_EQ(log_info_.element_finder_info().size(), 1);
+ const auto& result =
+ log_info_.element_finder_info(0).semantic_inference_result();
+ ASSERT_EQ(1, result.predicted_elements().size());
+ EXPECT_EQ(5, result.predicted_elements(0).backend_node_id());
+ EXPECT_THAT(
+ 1, result.predicted_elements(0).semantic_information().semantic_role());
+ EXPECT_THAT(2,
+ result.predicted_elements(0).semantic_information().objective());
+}
+
+IN_PROC_BROWSER_TEST_F(SemanticElementFinderBrowserTest,
+ SemanticAndCssComparison) {
+ ClientStatus status;
+ int backend_node_id = GetBackendNodeId(Selector({"#button"}), &status);
+ EXPECT_TRUE(status.ok());
+
+ NodeData node_data;
+ node_data.backend_node_id = backend_node_id;
+ EXPECT_CALL(autofill_assistant_agent_,
+ GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+ .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
+ std::vector<NodeData>{node_data}))
+ // Capture any other frames.
+ .WillRepeatedly(RunOnceCallback<4>(
+ mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
+
+ // We pretend that the button is the correct element.
+ SelectorProto proto = ToSelectorProto("#button");
+ auto* semantic_information = proto.mutable_semantic_information();
+ semantic_information->set_semantic_role(1);
+ semantic_information->set_objective(2);
+ semantic_information->set_check_matches_css_element(true);
+ RunStrictElementCheck(Selector(proto), true);
+
+ ASSERT_EQ(log_info_.element_finder_info().size(), 1);
+ const auto& result =
+ log_info_.element_finder_info(0).semantic_inference_result();
+ ASSERT_EQ(1, result.predicted_elements().size());
+ EXPECT_EQ(backend_node_id, result.predicted_elements(0).backend_node_id());
+ EXPECT_THAT(
+ 1, result.predicted_elements(0).semantic_information().semantic_role());
+ EXPECT_THAT(2,
+ result.predicted_elements(0).semantic_information().objective());
+ EXPECT_TRUE(result.predicted_elements(0).matches_css_element());
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller.cc b/chromium/components/autofill_assistant/browser/web/web_controller.cc
index ffae5c272c1..3a06cd59bb7 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller.cc
+++ b/chromium/components/autofill_assistant/browser/web/web_controller.cc
@@ -31,8 +31,12 @@
#include "components/autofill_assistant/browser/string_conversions_util.h"
#include "components/autofill_assistant/browser/user_data_util.h"
#include "components/autofill_assistant/browser/web/element.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
+#include "components/autofill_assistant/browser/web/element_finder_result_type.h"
#include "components/autofill_assistant/browser/web/selector_observer.h"
#include "components/autofill_assistant/browser/web/web_controller_util.h"
+#include "components/autofill_assistant/content/browser/content_autofill_assistant_driver.h"
+#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
@@ -875,21 +879,21 @@ void WebController::FindElement(const Selector& selector,
ElementFinder::Callback callback) {
RunElementFinder(/* start_element= */ ElementFinderResult::EmptyResult(),
selector,
- strict_mode ? ElementFinder::ResultType::kExactlyOneMatch
- : ElementFinder::ResultType::kAnyMatch,
+ strict_mode ? ElementFinderResultType::kExactlyOneMatch
+ : ElementFinderResultType::kAnyMatch,
std::move(callback));
}
void WebController::FindAllElements(const Selector& selector,
ElementFinder::Callback callback) {
RunElementFinder(/* start_element= */ ElementFinderResult::EmptyResult(),
- selector, ElementFinder::ResultType::kMatchArray,
+ selector, ElementFinderResultType::kMatchArray,
std::move(callback));
}
void WebController::RunElementFinder(const ElementFinderResult& start_element,
const Selector& selector,
- ElementFinder::ResultType result_type,
+ ElementFinderResultType result_type,
ElementFinder::Callback callback) {
auto finder = std::make_unique<ElementFinder>(
web_contents_, devtools_client_.get(), user_data_, log_info_,
@@ -915,13 +919,11 @@ void WebController::OnFindElementResult(
ClientStatus WebController::ObserveSelectors(
const std::vector<SelectorObserver::ObservableSelector>& selectors,
- base::TimeDelta timeout_ms,
- base::TimeDelta periodic_check_interval,
- base::TimeDelta extra_timeout,
+ const SelectorObserver::Settings& settings,
SelectorObserver::Callback callback) {
auto observer = std::make_unique<SelectorObserver>(
- selectors, timeout_ms, periodic_check_interval, extra_timeout,
- web_contents_, devtools_client_.get(), user_data_, std::move(callback));
+ selectors, settings, web_contents_, devtools_client_.get(), user_data_,
+ std::move(callback));
auto* ptr = observer.get();
pending_workers_.emplace_back(std::move(observer));
return ptr->Start(base::BindOnce(&WebController::OnSelectorObserverFinished,
@@ -1000,11 +1002,28 @@ void WebController::GetElementFormAndFieldData(
ContentAutofillDriver* driver,
const autofill::FormData&,
const autofill::FormFieldData&)> callback) {
- GetBackendNodeId(
- element,
- base::BindOnce(&WebController::OnGetBackendNodeIdForFormAndFieldData,
- weak_ptr_factory_.GetWeakPtr(), element,
- std::move(callback)));
+ if (!element.backend_node_id()) {
+ DVLOG(1) << __func__
+ << "No backend node id on element intended for native execution.";
+ std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__), nullptr,
+ autofill::FormData(), autofill::FormFieldData());
+ return;
+ }
+
+ ContentAutofillDriver* driver =
+ ContentAutofillDriver::GetForRenderFrameHost(element.render_frame_host());
+ if (driver == nullptr) {
+ DVLOG(1) << __func__ << " Failed to get the autofill driver.";
+ std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__), nullptr,
+ autofill::FormData(), autofill::FormFieldData());
+ return;
+ }
+
+ driver->GetAutofillAgent()->GetElementFormAndFieldDataForDevToolsNodeId(
+ *element.backend_node_id(),
+ base::BindOnce(&WebController::OnGetFormAndFieldData,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+ driver));
}
void WebController::GetBackendNodeId(
@@ -1034,35 +1053,6 @@ void WebController::OnGetBackendNodeId(
result->GetNode()->GetBackendNodeId());
}
-void WebController::OnGetBackendNodeIdForFormAndFieldData(
- const ElementFinderResult& element,
- base::OnceCallback<void(const ClientStatus&,
- ContentAutofillDriver* driver,
- const autofill::FormData&,
- const autofill::FormFieldData&)> callback,
- const ClientStatus& node_status,
- const int backend_node_id) {
- if (!node_status.ok()) {
- std::move(callback).Run(node_status, nullptr, autofill::FormData(),
- autofill::FormFieldData());
- return;
- }
-
- ContentAutofillDriver* driver =
- ContentAutofillDriver::GetForRenderFrameHost(element.render_frame_host());
- if (driver == nullptr) {
- DVLOG(1) << __func__ << " Failed to get the autofill driver.";
- std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__), nullptr,
- autofill::FormData(), autofill::FormFieldData());
- return;
- }
-
- driver->GetAutofillAgent()->GetElementFormAndFieldDataForDevToolsNodeId(
- backend_node_id, base::BindOnce(&WebController::OnGetFormAndFieldData,
- weak_ptr_factory_.GetWeakPtr(),
- std::move(callback), driver));
-}
-
void WebController::OnGetFormAndFieldData(
base::OnceCallback<void(const ClientStatus&,
ContentAutofillDriver* driver,
@@ -1635,6 +1625,39 @@ void WebController::ExecuteJS(
WebControllerErrorInfoProto::EXECUTE_JS, std::move(callback));
}
+void WebController::SetNativeValue(
+ const std::string& value,
+ const ElementFinderResult& element,
+ base::OnceCallback<void(const ClientStatus&)> callback) {
+ if (!element.backend_node_id()) {
+ DVLOG(1) << __func__
+ << "No backend node id on element intended for native execution.";
+ std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__));
+ return;
+ }
+
+ auto* render_frame_host = element.render_frame_host();
+ DCHECK(render_frame_host);
+ auto* driver = ContentAutofillAssistantDriver::GetOrCreateForRenderFrameHost(
+ render_frame_host, annotate_dom_model_service_);
+ if (!driver) {
+ std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__));
+ return;
+ }
+ driver->GetAutofillAssistantAgent()->SetElementValue(
+ *element.backend_node_id(), base::UTF8ToUTF16(value),
+ /* send_events= */ true,
+ base::BindOnce(&WebController::OnSetElementValue,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void WebController::OnSetElementValue(
+ base::OnceCallback<void(const ClientStatus&)> callback,
+ bool success) const {
+ std::move(callback).Run(success ? OkClientStatus()
+ : UnexpectedErrorStatus(__FILE__, __LINE__));
+}
+
base::WeakPtr<WebController> WebController::GetWeakPtr() const {
return weak_ptr_factory_.GetWeakPtr();
}
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller.h b/chromium/components/autofill_assistant/browser/web/web_controller.h
index 88c75f4597a..7914f7e5add 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller.h
+++ b/chromium/components/autofill_assistant/browser/web/web_controller.h
@@ -52,6 +52,8 @@ class WebContents;
} // namespace content
namespace autofill_assistant {
+class ElementFinderResult;
+enum class ElementFinderResultType;
// Controller to interact with the web pages.
//
@@ -106,7 +108,7 @@ class WebController {
// |start_element|. Returns results or errors based on the |result_type|.
virtual void RunElementFinder(const ElementFinderResult& start_element,
const Selector& selector,
- ElementFinder::ResultType result_type,
+ ElementFinderResultType result_type,
ElementFinder::Callback callback);
// Find all elements matching |selector|. If there are no matches, the status
@@ -116,9 +118,7 @@ class WebController {
virtual ClientStatus ObserveSelectors(
const std::vector<SelectorObserver::ObservableSelector>& selectors,
- base::TimeDelta timeout_ms,
- base::TimeDelta periodic_check_interval,
- base::TimeDelta extra_timeout,
+ const SelectorObserver::Settings& settings,
SelectorObserver::Callback callback);
// Scroll the |element| into view. |animation| defines the transition
@@ -385,11 +385,17 @@ class WebController {
const ElementFinderResult& element,
base::OnceCallback<void(const ClientStatus&)> callback);
+ virtual void SetNativeValue(
+ const std::string& value,
+ const ElementFinderResult& element,
+ base::OnceCallback<void(const ClientStatus&)> callback);
+
virtual base::WeakPtr<WebController> GetWeakPtr() const;
private:
- friend class WebControllerBrowserTest;
friend class BatchElementCheckerBrowserTest;
+ friend class SemanticElementFinderBrowserTest;
+ friend class WebControllerBrowserTest;
void OnJavaScriptResult(
base::OnceCallback<void(const ClientStatus&)> callback,
@@ -463,14 +469,6 @@ class WebController {
base::OnceCallback<void(const ClientStatus&, int)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<dom::DescribeNodeResult> result);
- void OnGetBackendNodeIdForFormAndFieldData(
- const ElementFinderResult& element,
- base::OnceCallback<void(const ClientStatus&,
- autofill::ContentAutofillDriver* driver,
- const autofill::FormData&,
- const autofill::FormFieldData&)> callback,
- const ClientStatus& node_status,
- int backend_node_id);
void OnGetFormAndFieldData(
base::OnceCallback<void(const ClientStatus&,
autofill::ContentAutofillDriver* driver,
@@ -532,6 +530,8 @@ class WebController {
void OnDispatchJsEvent(base::OnceCallback<void(const ClientStatus&)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::EvaluateResult> result) const;
+ void OnSetElementValue(base::OnceCallback<void(const ClientStatus&)> callback,
+ bool success) const;
// Weak pointer is fine here since it must outlive this web controller, which
// is guaranteed by the owner of this object.
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index 0bce668ea90..aed1001cd56 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -13,7 +13,6 @@
#include "base/bind.h"
#include "base/callback.h"
-#include "base/callback_forward.h"
#include "base/callback_helpers.h"
#include "base/containers/checked_range.h"
#include "base/location.h"
@@ -57,11 +56,10 @@
#include "components/autofill_assistant/browser/user_model.h"
#include "components/autofill_assistant/browser/web/element.h"
#include "components/autofill_assistant/browser/web/element_action_util.h"
-#include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/browser/web/element_finder_result.h"
+#include "components/autofill_assistant/browser/web/element_finder_result_type.h"
#include "components/autofill_assistant/browser/web/element_store.h"
-#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
-#include "components/autofill_assistant/content/common/autofill_assistant_types.mojom.h"
-#include "components/autofill_assistant/content/common/node_data.h"
+#include "components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
@@ -69,12 +67,8 @@
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
-#include "mojo/public/cpp/bindings/associated_receiver_set.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
-#include "third_party/blink/public/common/switches.h"
#include "url/gurl.h"
#include "url/url_constants.h"
@@ -86,34 +80,8 @@ using ::testing::_;
using ::testing::AnyOf;
using ::testing::IsEmpty;
using ::testing::Return;
-using ::testing::SaveArg;
using ::testing::WithArgs;
-class MockAutofillAssistantAgent : public mojom::AutofillAssistantAgent {
- public:
- MockAutofillAssistantAgent() = default;
- ~MockAutofillAssistantAgent() override = default;
-
- void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle) {
- receivers_.Add(
- this, mojo::PendingAssociatedReceiver<mojom::AutofillAssistantAgent>(
- std::move(handle)));
- }
-
- MOCK_METHOD(void,
- GetSemanticNodes,
- (int32_t role,
- int32_t objective,
- bool ignore_objective,
- base::TimeDelta model_timeout,
- base::OnceCallback<void(mojom::NodeDataStatus,
- const std::vector<NodeData>&)> callback),
- (override));
-
- private:
- mojo::AssociatedReceiverSet<mojom::AutofillAssistantAgent> receivers_;
-};
-
} // namespace
class WebControllerBrowserTest : public autofill_assistant::BaseBrowserTest,
@@ -129,22 +97,13 @@ class WebControllerBrowserTest : public autofill_assistant::BaseBrowserTest,
void SetUpOnMainThread() override {
BaseBrowserTest::SetUpOnMainThread();
- // Register the same agent on all frames, such that the callback can be
- // mocked.
- shell()->web_contents()->GetMainFrame()->ForEachRenderFrameHost(
- base::BindLambdaForTesting([this](content::RenderFrameHost* host) {
- host->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting(
- mojom::AutofillAssistantAgent::Name_,
- base::BindRepeating(
- &MockAutofillAssistantAgent::BindPendingReceiver,
- base::Unretained(&autofill_assistant_agent_)));
- }));
+ MockAutofillAssistantAgent::RegisterForAllFrames(
+ shell()->web_contents(), &autofill_assistant_agent_);
- annotate_dom_model_service_ = std::make_unique<AnnotateDomModelService>(
- /* opt_guide= */ nullptr, /* background_task_runner= */ nullptr);
web_controller_ = WebController::CreateForWebContents(
shell()->web_contents(), &user_data_, &log_info_,
- annotate_dom_model_service_.get(), /*enable_full_stack_traces= */ true);
+ /* annotate_dom_model_service= */ nullptr,
+ /*enable_full_stack_traces= */ true);
Observe(shell()->web_contents());
}
@@ -601,11 +560,11 @@ class WebControllerBrowserTest : public autofill_assistant::BaseBrowserTest,
void CheckFindElementResult(const ElementFinderResult& result,
bool is_main_frame) {
if (is_main_frame) {
- EXPECT_EQ(shell()->web_contents()->GetMainFrame(),
+ EXPECT_EQ(shell()->web_contents()->GetPrimaryMainFrame(),
result.render_frame_host());
EXPECT_EQ(result.frame_stack().size(), 0u);
} else {
- EXPECT_NE(shell()->web_contents()->GetMainFrame(),
+ EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame(),
result.render_frame_host());
EXPECT_GE(result.frame_stack().size(), 1u);
}
@@ -983,38 +942,21 @@ document.getElementById("overlay_in_frame").style.visibility='hidden';
return ClientStatus(captured_processed_actions[0].status());
}
- int GetBackendNodeId(Selector selector, ClientStatus* status_out) {
- std::unique_ptr<ElementFinderResult> element_result;
- int backend_node_id = -1;
-
- base::RunLoop run_loop_1;
- web_controller_->FindElement(
- selector, true,
- base::BindLambdaForTesting(
- [&](const ClientStatus& status,
- std::unique_ptr<ElementFinderResult> result) {
- element_result = std::move(result);
- *status_out = status;
- run_loop_1.Quit();
- }));
- run_loop_1.Run();
- if (!status_out->ok()) {
- return backend_node_id;
- }
+ ClientStatus GetBackendNodeId(const ElementFinderResult& element,
+ int* backend_node_id) {
+ ClientStatus result_status;
- // Second part in sequence, lookup backend node id.
- base::RunLoop run_loop_2;
+ base::RunLoop run_loop;
web_controller_->GetBackendNodeId(
- *element_result,
+ element,
base::BindLambdaForTesting([&](const ClientStatus& status, int id) {
- *status_out = status;
- backend_node_id = id;
- run_loop_2.Quit();
+ result_status = status;
+ *backend_node_id = id;
+ run_loop.Quit();
}));
- run_loop_2.Run();
+ run_loop.Run();
- log_info_.Clear();
- return backend_node_id;
+ return result_status;
}
protected:
@@ -1023,7 +965,6 @@ document.getElementById("overlay_in_frame").style.visibility='hidden';
UserModel user_model_;
ProcessedActionStatusDetailsProto log_info_;
MockAutofillAssistantAgent autofill_assistant_agent_;
- std::unique_ptr<AnnotateDomModelService> annotate_dom_model_service_;
};
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ElementExistenceCheck) {
@@ -2761,7 +2702,7 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
// This makes the devtools action fail.
ElementFinderResult element;
element.SetNodeFrameId("doesnotexist");
- element.SetRenderFrameHost(web_contents()->GetMainFrame());
+ element.SetRenderFrameHost(web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(ELEMENT_POSITION_NOT_FOUND,
WaitUntilElementIsStable(element, 10, base::Milliseconds(100))
@@ -3151,46 +3092,6 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
}
-IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, WaitForDomForSemanticElement) {
- // This element is unique.
- SelectorProto baseline_selector = ToSelectorProto("#select");
-
- ClientStatus element_status;
- int backend_node_id =
- GetBackendNodeId(Selector(baseline_selector), &element_status);
- EXPECT_TRUE(element_status.ok());
-
- NodeData node_data;
- node_data.backend_node_id = backend_node_id;
- EXPECT_CALL(autofill_assistant_agent_,
- GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
- .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
- std::vector<NodeData>{node_data}))
- // Capture any other frames.
- .WillRepeatedly(RunOnceCallback<4>(
- mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
-
- ActionProto action_proto;
- auto* wait_for_dom = action_proto.mutable_wait_for_dom();
- auto* condition = wait_for_dom->mutable_wait_condition();
- condition->mutable_client_id()->set_identifier("e");
- condition->set_require_unique_element(true);
- auto* semantic_information =
- condition->mutable_match()->mutable_semantic_information();
- semantic_information->set_semantic_role(1);
- semantic_information->set_objective(2);
-
- base::MockCallback<base::OnceCallback<void(ScriptExecutor*)>>
- run_expectations;
- EXPECT_CALL(run_expectations, Run(_))
- .WillOnce([](ScriptExecutor* script_executor) {
- EXPECT_TRUE(script_executor->GetElementStore()->HasElement("e"));
- });
- ClientStatus status = RunWaitForDom(action_proto, /* use_observers= */ false,
- run_expectations.Get());
- EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
-}
-
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FindElementError) {
ClientStatus element_status;
ElementFinderResult element;
@@ -3224,7 +3125,7 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
base::RunLoop button_run_loop;
web_controller_->RunElementFinder(
frame_element, Selector({"#shadowsection", "#shadowbutton"}),
- ElementFinder::ResultType::kExactlyOneMatch,
+ ElementFinderResultType::kExactlyOneMatch,
base::BindOnce(&WebControllerBrowserTest::OnFindElement,
base::Unretained(this), button_run_loop.QuitClosure(),
&button_status, &button_element));
@@ -3262,7 +3163,7 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, RunElementFinderFromOOPIF) {
base::RunLoop button_run_loop;
web_controller_->RunElementFinder(
fake_frame_element, Selector({"#button"}),
- ElementFinder::ResultType::kExactlyOneMatch,
+ ElementFinderResultType::kExactlyOneMatch,
base::BindOnce(&WebControllerBrowserTest::OnFindElement,
base::Unretained(this), button_run_loop.QuitClosure(),
&button_status, &button_element));
@@ -3447,186 +3348,100 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ExecuteJSWithException) {
testing::ElementsAre(18, 12, 10));
}
-IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
- ElementExistenceCheckWithSemanticModel) {
- ClientStatus status;
- int backend_node_id = GetBackendNodeId(Selector({"#button"}), &status);
- EXPECT_TRUE(status.ok());
-
- NodeData node_data;
- node_data.backend_node_id = backend_node_id;
- EXPECT_CALL(autofill_assistant_agent_,
- GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
- .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
- std::vector<NodeData>{node_data}))
- // Capture any other frames.
- .WillRepeatedly(RunOnceCallback<4>(
- mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
-
- // We pretend that the button is the correct element.
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ParentFilter) {
SelectorProto proto;
- auto* semantic_information = proto.mutable_semantic_information();
- semantic_information->set_semantic_role(1);
- semantic_information->set_objective(2);
- RunStrictElementCheck(Selector(proto), true);
-
- ASSERT_EQ(log_info_.element_finder_info().size(), 1);
- const auto& result =
- log_info_.element_finder_info(0).semantic_inference_result();
- ASSERT_EQ(1, result.predicted_elements().size());
- EXPECT_EQ(backend_node_id, result.predicted_elements(0).backend_node_id());
- EXPECT_THAT(
- 1, result.predicted_elements(0).semantic_information().semantic_role());
- EXPECT_THAT(2,
- result.predicted_elements(0).semantic_information().objective());
-}
+ proto.add_filters()->set_css_selector("#select option:checked");
+ proto.add_filters()->mutable_parent();
-IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
- ElementExistenceCheckWithSemanticModelOOPIF) {
- ClientStatus status;
- int backend_node_id =
- GetBackendNodeId(Selector({"#iframeExternal", "#button"}), &status);
- EXPECT_TRUE(status.ok());
+ std::string element_tag;
+ EXPECT_EQ(ACTION_APPLIED,
+ GetElementTag(Selector(proto), &element_tag).proto_status());
+ EXPECT_EQ("SELECT", element_tag);
- NodeData node_data;
- node_data.backend_node_id = backend_node_id;
- EXPECT_CALL(autofill_assistant_agent_,
- GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
- .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
- std::vector<NodeData>{node_data}))
- // Capture any other frames.
- .WillRepeatedly(RunOnceCallback<4>(
- mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
-
- // We pretend that the button is the correct element.
- SelectorProto proto;
- auto* semantic_information = proto.mutable_semantic_information();
- semantic_information->set_semantic_role(1);
- semantic_information->set_objective(2);
- RunStrictElementCheck(Selector(proto), true);
+ SelectorProto failing_proto;
+ failing_proto.add_filters()->set_css_selector("body");
+ failing_proto.add_filters()->mutable_parent(); // document
+ failing_proto.add_filters()->mutable_parent(); // Nothing
- ASSERT_EQ(log_info_.element_finder_info().size(), 1);
- const auto& result =
- log_info_.element_finder_info(0).semantic_inference_result();
- ASSERT_EQ(1, result.predicted_elements().size());
- EXPECT_EQ(backend_node_id, result.predicted_elements(0).backend_node_id());
- EXPECT_THAT(
- 1, result.predicted_elements(0).semantic_information().semantic_role());
- EXPECT_THAT(2,
- result.predicted_elements(0).semantic_information().objective());
+ ClientStatus failing_status;
+ ElementFinderResult ignored_element;
+ FindElement(Selector(failing_proto), &failing_status, &ignored_element);
+ EXPECT_EQ(ELEMENT_RESOLUTION_FAILED, failing_status.proto_status());
}
-IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
- ElementExistenceCheckWithSemanticModelNotFound) {
- // All frames return an empty list as a result.
- EXPECT_CALL(autofill_assistant_agent_,
- GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
- .WillRepeatedly(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
- std::vector<NodeData>{}));
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, WebpageZoom) {
+ double initial_width =
+ content::EvalJs(
+ shell(),
+ R"(document.querySelector("#select").getBoundingClientRect().width)")
+ .ExtractDouble();
+ EXPECT_GT(initial_width, 0);
- SelectorProto proto;
- auto* semantic_information = proto.mutable_semantic_information();
- semantic_information->set_semantic_role(1);
- semantic_information->set_objective(2);
- FindElementExpectEmptyResult(Selector(proto));
-}
+ ClientStatus body_status;
+ ElementFinderResult body;
+ FindElement(Selector({"body"}), &body_status, &body);
+ EXPECT_EQ(ACTION_APPLIED, body_status.proto_status());
-IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
- ElementExistenceCheckWithSemanticMultipleFound) {
- SelectorProto proto;
- auto* semantic_information = proto.mutable_semantic_information();
- semantic_information->set_semantic_role(1);
- semantic_information->set_objective(2);
-
- NodeData node_data;
- node_data.backend_node_id = 5;
- NodeData node_data_other;
- node_data_other.backend_node_id = 13;
- EXPECT_CALL(autofill_assistant_agent_,
- GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
- .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
- std::vector<NodeData>{node_data}))
- .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
- std::vector<NodeData>{node_data_other}))
- // Capture any other frames.
- .WillRepeatedly(RunOnceCallback<4>(
- mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
-
- // Two elements are found in different frames.
- ClientStatus status;
- FindElement(Selector(proto), &status, nullptr);
- EXPECT_EQ(TOO_MANY_ELEMENTS, status.proto_status());
-}
+ ClientStatus zoom_status;
+ base::RunLoop zoom_run_loop;
+ web_controller_->ExecuteJS(
+ "this.style.webkitTransform = 'scale(2)'", body,
+ base::BindOnce(&WebControllerBrowserTest::OnClientStatus,
+ base::Unretained(this), zoom_run_loop.QuitClosure(),
+ &zoom_status));
+ zoom_run_loop.Run();
+ EXPECT_EQ(ACTION_APPLIED, zoom_status.proto_status());
-IN_PROC_BROWSER_TEST_F(
- WebControllerBrowserTest,
- ElementExistenceCheckWithSemanticModelUsesIgnoreObjective) {
- NodeData node_data;
- node_data.backend_node_id = 5;
- EXPECT_CALL(autofill_assistant_agent_,
- GetSemanticNodes(1, 2, true, base::Milliseconds(5000), _))
- .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
- std::vector<NodeData>{node_data}))
- .WillRepeatedly(RunOnceCallback<4>(
- mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
+ double after_zoom_width =
+ content::EvalJs(
+ shell(),
+ R"(document.querySelector("#select").getBoundingClientRect().width)")
+ .ExtractDouble();
+ EXPECT_NEAR(after_zoom_width, initial_width * 2, 1);
- SelectorProto proto;
- auto* semantic_information = proto.mutable_semantic_information();
- semantic_information->set_semantic_role(1);
- semantic_information->set_objective(2);
- // All we want is this to be propagated to the GetSemanticNodes call as
- // configured in the previous expectation.
- semantic_information->set_ignore_objective(true);
-
- ClientStatus ignore_status;
- FindElement(Selector(proto), &ignore_status, nullptr);
-
- // TODO(b/217160707): For now we expect the originally passed in semantic info
- // to be logged instead of the objective inferred by the model.
- ASSERT_EQ(log_info_.element_finder_info().size(), 1);
- const auto& result =
- log_info_.element_finder_info(0).semantic_inference_result();
- ASSERT_EQ(1, result.predicted_elements().size());
- EXPECT_EQ(5, result.predicted_elements(0).backend_node_id());
- EXPECT_THAT(
- 1, result.predicted_elements(0).semantic_information().semantic_role());
- EXPECT_THAT(2,
- result.predicted_elements(0).semantic_information().objective());
+ ClientStatus reset_status;
+ base::RunLoop reset_run_loop;
+ web_controller_->ExecuteJS(
+ "this.style.webkitTransform = 'scale(1)'", body,
+ base::BindOnce(&WebControllerBrowserTest::OnClientStatus,
+ base::Unretained(this), reset_run_loop.QuitClosure(),
+ &reset_status));
+ reset_run_loop.Run();
+ EXPECT_EQ(ACTION_APPLIED, reset_status.proto_status());
+
+ double after_reset_width =
+ content::EvalJs(
+ shell(),
+ R"(document.querySelector("#select").getBoundingClientRect().width)")
+ .ExtractDouble();
+ EXPECT_NEAR(after_reset_width, initial_width, 1);
}
-IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SemanticAndCssComparison) {
- ClientStatus status;
- int backend_node_id = GetBackendNodeId(Selector({"#button"}), &status);
- EXPECT_TRUE(status.ok());
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SetFieldValueThroughNative) {
+ ClientStatus element_status;
+ ElementFinderResult input;
+ FindElement(Selector({"#input1"}), &element_status, &input);
+ ASSERT_EQ(ACTION_APPLIED, element_status.proto_status());
- NodeData node_data;
- node_data.backend_node_id = backend_node_id;
+ int backend_node_id;
+ ASSERT_EQ(ACTION_APPLIED,
+ GetBackendNodeId(input, &backend_node_id).proto_status());
+ std::u16string expected_value = u"native";
EXPECT_CALL(autofill_assistant_agent_,
- GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
- .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
- std::vector<NodeData>{node_data}))
- // Capture any other frames.
- .WillRepeatedly(RunOnceCallback<4>(
- mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
-
- // We pretend that the button is the correct element.
- SelectorProto proto = ToSelectorProto("#button");
- auto* semantic_information = proto.mutable_semantic_information();
- semantic_information->set_semantic_role(1);
- semantic_information->set_objective(2);
- semantic_information->set_check_matches_css_element(true);
- RunStrictElementCheck(Selector(proto), true);
+ SetElementValue(backend_node_id, expected_value,
+ /* send_events= */ true, _))
+ .WillOnce(RunOnceCallback<3>(true));
- ASSERT_EQ(log_info_.element_finder_info().size(), 1);
- const auto& result =
- log_info_.element_finder_info(0).semantic_inference_result();
- ASSERT_EQ(1, result.predicted_elements().size());
- EXPECT_EQ(backend_node_id, result.predicted_elements(0).backend_node_id());
- EXPECT_THAT(
- 1, result.predicted_elements(0).semantic_information().semantic_role());
- EXPECT_THAT(2,
- result.predicted_elements(0).semantic_information().objective());
- EXPECT_TRUE(result.predicted_elements(0).matches_css_element());
+ ClientStatus fill_status;
+ base::RunLoop run_loop;
+ web_controller_->SetNativeValue(
+ "native", input,
+ base::BindOnce(&WebControllerBrowserTest::OnClientStatus,
+ base::Unretained(this), run_loop.QuitClosure(),
+ &fill_status));
+ run_loop.Run();
+
+ EXPECT_EQ(ACTION_APPLIED, fill_status.proto_status());
}
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller_util.cc b/chromium/components/autofill_assistant/browser/web/web_controller_util.cc
index fa7a2f41399..dfafce4d5db 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller_util.cc
+++ b/chromium/components/autofill_assistant/browser/web/web_controller_util.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/types_runtime.h"
+#include "components/autofill_assistant/browser/js_flow_util.h"
#include "components/autofill_assistant/browser/service.pb.h"
// Necessary to avoid a type collision while building for Windows.
@@ -16,27 +17,38 @@
namespace autofill_assistant {
namespace {
+
template <typename S>
void AddStackEntry(const S& s,
- const int js_line_offset,
+ const std::string& devtools_source_url,
+ const JsLineOffsets& js_line_offsets,
UnexpectedErrorInfoProto* info) {
- const int line_number = s.GetLineNumber() - js_line_offset;
- DCHECK(line_number >= 0)
- << "Line number " << s.GetLineNumber()
- << " pointing into the offset included in the stack.";
+ int line_number = s.GetLineNumber();
+ if (js_line_offsets.contains(devtools_source_url)) {
+ const int line_offset = js_line_offsets.at(devtools_source_url);
+ line_number -= line_offset;
+ DCHECK(line_number >= 0)
+ << "Line number (" << s.GetLineNumber()
+ << ") pointing into the offset (" << line_offset
+ << ") for devtools source url (" << devtools_source_url
+ << ") included in the stack.";
+ }
+
+ info->add_js_exception_locations(
+ js_flow_util::GetExceptionLocation(devtools_source_url));
info->add_js_exception_line_numbers(line_number);
info->add_js_exception_column_numbers(s.GetColumnNumber());
}
void AddStackEntries(const runtime::ExceptionDetails* exception,
- const int js_line_offset,
+ const JsLineOffsets& js_line_offsets,
const int num_stack_entries_to_drop,
UnexpectedErrorInfoProto* info) {
if (!exception->HasStackTrace()) {
- AddStackEntry(*exception, js_line_offset, info);
+ AddStackEntry(*exception, exception->HasUrl() ? exception->GetUrl() : "",
+ js_line_offsets, info);
return;
}
-
const std::vector<std::unique_ptr<runtime::CallFrame>>& frames =
*exception->GetStackTrace()->GetCallFrames();
const int num_stack_entries = static_cast<int>(frames.size());
@@ -47,7 +59,8 @@ void AddStackEntries(const runtime::ExceptionDetails* exception,
std::max(num_stack_entries - num_stack_entries_to_drop, 1);
for (int i = 0; i < num_frames_to_use; i++) {
- AddStackEntry(*frames[i], js_line_offset, info);
+ const auto& frame = *frames[i];
+ AddStackEntry(frame, frame.GetUrl(), js_line_offsets, info);
}
}
} // namespace
@@ -78,7 +91,7 @@ ClientStatus JavaScriptErrorStatus(
const std::string& file,
const int line,
const runtime::ExceptionDetails* exception,
- const int js_line_offset,
+ const JsLineOffsets& js_line_offsets,
const int num_stack_entries_to_drop) {
ClientStatus status = UnexpectedDevtoolsErrorStatus(reply_status, file, line);
status.set_proto_status(UNEXPECTED_JS_ERROR);
@@ -90,7 +103,7 @@ ClientStatus JavaScriptErrorStatus(
if (exception->HasException() && exception->GetException()->HasClassName()) {
info->set_js_exception_classname(exception->GetException()->GetClassName());
}
- AddStackEntries(exception, js_line_offset, num_stack_entries_to_drop, info);
+ AddStackEntries(exception, js_line_offsets, num_stack_entries_to_drop, info);
return status;
}
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller_util.h b/chromium/components/autofill_assistant/browser/web/web_controller_util.h
index 5e543af2bb6..7d13446bd97 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller_util.h
+++ b/chromium/components/autofill_assistant/browser/web/web_controller_util.h
@@ -30,14 +30,18 @@ ClientStatus UnexpectedDevtoolsErrorStatus(
const std::string& file,
int line);
+// Map from devtools source url to js line offset. See js_flow_util for details
+// on devtools source urls.
+using JsLineOffsets = base::flat_map<std::string, int>;
+
// Builds a ClientStatus appropriate for a JavaScript error.
ClientStatus JavaScriptErrorStatus(
const DevtoolsClient::ReplyStatus& reply_status,
const std::string& file,
int line,
const runtime::ExceptionDetails* exception,
- int js_line_offset = 0,
- int num_stack_entries_to_drop = 0);
+ const JsLineOffsets& js_line_offsets = {},
+ const int num_stack_entries_to_drop = 0);
// Makes sure that the given EvaluateResult exists, is successful and contains a
// result.
@@ -47,18 +51,18 @@ ClientStatus CheckJavaScriptResult(
T* result,
const char* file,
int line,
- int js_line_offset = 0,
+ const JsLineOffsets& js_line_offsets = {},
int num_stack_entries_to_drop = 0) {
if (!result)
return JavaScriptErrorStatus(reply_status, file, line, nullptr,
- js_line_offset, num_stack_entries_to_drop);
+ js_line_offsets, num_stack_entries_to_drop);
if (result->HasExceptionDetails())
return JavaScriptErrorStatus(reply_status, file, line,
- result->GetExceptionDetails(), js_line_offset,
+ result->GetExceptionDetails(), js_line_offsets,
num_stack_entries_to_drop);
if (!result->GetResult())
return JavaScriptErrorStatus(reply_status, file, line, nullptr,
- js_line_offset, num_stack_entries_to_drop);
+ js_line_offsets, num_stack_entries_to_drop);
return OkClientStatus();
}
diff --git a/chromium/components/autofill_assistant/browser/website_login_manager_impl.cc b/chromium/components/autofill_assistant/browser/website_login_manager_impl.cc
index f3f5b48ccab..c74f7c2fa5d 100644
--- a/chromium/components/autofill_assistant/browser/website_login_manager_impl.cc
+++ b/chromium/components/autofill_assistant/browser/website_login_manager_impl.cc
@@ -465,7 +465,8 @@ absl::optional<std::string> WebsiteLoginManagerImpl::GeneratePassword(
// TODO(crbug.com/1043132): Add support for non-main frames. If another
// frame has a different origin than the main frame, passwords-related
// features may not work.
- auto* driver = factory->GetDriverForFrame(web_contents_->GetMainFrame());
+ auto* driver =
+ factory->GetDriverForFrame(web_contents_->GetPrimaryMainFrame());
if (!driver) {
return absl::nullopt;
}
diff --git a/chromium/components/autofill_assistant/content/browser/BUILD.gn b/chromium/components/autofill_assistant/content/browser/BUILD.gn
index 592b8ebfcfc..17df1b0bc2c 100644
--- a/chromium/components/autofill_assistant/content/browser/BUILD.gn
+++ b/chromium/components/autofill_assistant/content/browser/BUILD.gn
@@ -15,6 +15,7 @@ static_library("browser") {
deps = [
"//base",
"//components/autofill_assistant/content/common:mojo_interfaces",
+ "//components/autofill_assistant/content/common/proto:proto",
"//components/keyed_service/core",
"//components/optimization_guide/core",
"//components/optimization_guide/proto:optimization_guide_proto",
diff --git a/chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.cc b/chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.cc
index cc501a400ab..37efddba4f7 100644
--- a/chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.cc
+++ b/chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.cc
@@ -59,12 +59,11 @@ void AnnotateDomModelService::Shutdown() {
// This and the optimization guide are keyed services, currently optimization
// guide is a BrowserContextKeyedService, it will be cleaned first so removing
// the observer should not be performed.
- if (annotate_dom_model_file_) {
+ if (model_file_) {
// If the model file is already loaded, it should be closed on a
// background thread.
background_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&CloseModelFile, std::move(*annotate_dom_model_file_)));
+ FROM_HERE, base::BindOnce(&CloseModelFile, std::move(*model_file_)));
}
for (auto& pending_request : pending_model_requests_) {
// Clear any pending requests, no model file is acceptable as |Shutdown| is
@@ -85,23 +84,24 @@ void AnnotateDomModelService::OnModelUpdated(
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&LoadModelFile, model_info.GetModelFilePath()),
base::BindOnce(&AnnotateDomModelService::OnModelFileLoaded,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr(), model_info.GetVersion()));
}
-void AnnotateDomModelService::OnModelFileLoaded(base::File model_file) {
+void AnnotateDomModelService::OnModelFileLoaded(int64_t model_version,
+ base::File model_file) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!model_file.IsValid()) {
return;
}
- if (annotate_dom_model_file_) {
+ if (model_file_) {
// If the model file is already loaded, it should be closed on a background
// thread.
background_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&CloseModelFile, std::move(*annotate_dom_model_file_)));
+ FROM_HERE, base::BindOnce(&CloseModelFile, std::move(*model_file_)));
}
- annotate_dom_model_file_ = std::move(model_file);
+ model_file_ = std::move(model_file);
+ model_version_ = model_version;
for (auto& pending_request : pending_model_requests_) {
if (!pending_request) {
continue;
@@ -111,18 +111,31 @@ void AnnotateDomModelService::OnModelFileLoaded(base::File model_file) {
pending_model_requests_.clear();
}
-absl::optional<base::File> AnnotateDomModelService::GetModelFile() {
- if (!annotate_dom_model_file_) {
+absl::optional<base::File> AnnotateDomModelService::GetModelFile() const {
+ if (!model_file_) {
return absl::nullopt;
}
// The model must be valid at this point.
- DCHECK(annotate_dom_model_file_->IsValid());
- return annotate_dom_model_file_->Duplicate();
+ DCHECK(model_file_->IsValid());
+ return model_file_->Duplicate();
+}
+
+absl::optional<int64_t> AnnotateDomModelService::GetModelVersion() const {
+ return model_version_;
+}
+
+std::string AnnotateDomModelService::GetOverridesPolicy() const {
+ return overrides_policy_binary_proto_;
+}
+
+bool AnnotateDomModelService::SetOverridesPolicy(
+ SemanticSelectorPolicy policy) {
+ return policy.SerializeToString(&overrides_policy_binary_proto_);
}
void AnnotateDomModelService::NotifyOnModelFileAvailable(
NotifyModelAvailableCallback callback) {
- DCHECK(!annotate_dom_model_file_);
+ DCHECK(!model_file_);
if (pending_model_requests_.size() < kMaxPendingRequestsAllowed) {
pending_model_requests_.emplace_back(std::move(callback));
return;
@@ -131,7 +144,7 @@ void AnnotateDomModelService::NotifyOnModelFileAvailable(
}
void AnnotateDomModelService::SetModelFileForTest(base::File model_file) {
- annotate_dom_model_file_ = std::move(model_file);
+ model_file_ = std::move(model_file);
for (auto& pending_request : pending_model_requests_) {
std::move(pending_request).Run(true);
}
diff --git a/chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.h b/chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.h
index ba4d0b0a902..85908084152 100644
--- a/chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.h
+++ b/chromium/components/autofill_assistant/content/browser/annotate_dom_model_service.h
@@ -13,6 +13,7 @@
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/content/common/proto/semantic_feature_overrides.pb.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/optimization_guide/core/optimization_target_model_observer.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -45,10 +46,20 @@ class AnnotateDomModelService
optimization_guide::proto::OptimizationTarget optimization_target,
const optimization_guide::ModelInfo& model_info) override;
- // Returns the annotate dom model file, should only be called when the model
- // file is already available. See the |NotifyOnModelFileAvailable| for an
- // asynchronous notification of the model being available.
- absl::optional<base::File> GetModelFile();
+ // Returns the annotate dom model file. If this returns a nullopt, see the
+ // |NotifyOnModelFileAvailable| for an asynchronous notification of the model
+ // being available.
+ absl::optional<base::File> GetModelFile() const;
+
+ // Returns the model of the version. If this returns a nullopt, see the
+ // |NotifyOnModelFileAvailable| for an asynchronous notification of the model
+ // being available.
+ absl::optional<int64_t> GetModelVersion() const;
+
+ // Returns the overrides policy as a serialized binary proto representation
+ // that will be passed to renderer processes.
+ std::string GetOverridesPolicy() const;
+ virtual bool SetOverridesPolicy(SemanticSelectorPolicy policy);
// If the model file is not available, requestors can ask to be notified, via
// |callback|. This enables a two-step approach to relabily get the model file
@@ -61,7 +72,7 @@ class AnnotateDomModelService
void SetModelFileForTest(base::File model_file);
private:
- void OnModelFileLoaded(base::File model_file);
+ void OnModelFileLoaded(int64_t model_version, base::File model_file);
// Optimization Guide Service that provides model files for this service.
raw_ptr<optimization_guide::OptimizationGuideModelProvider> opt_guide_ =
@@ -70,7 +81,12 @@ class AnnotateDomModelService
// The file that contains the annotate DOM model. Available when the
// file path has been provided by the Optimization Guide and has been
// successfully loaded.
- absl::optional<base::File> annotate_dom_model_file_;
+ absl::optional<base::File> model_file_;
+ // The version of the current model.
+ absl::optional<int64_t> model_version_;
+
+ // A serialized binary representation of a SemanticSelectorPolicy proto.
+ std::string overrides_policy_binary_proto_;
// The set of callbacks associated with requests for the language detection
// model.
diff --git a/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc b/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc
index 79e42a96194..99bd750b1e2 100644
--- a/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc
+++ b/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc
@@ -7,6 +7,7 @@
#include "base/files/file.h"
#include "base/guid.h"
#include "base/location.h"
+#include "components/autofill_assistant/content/common/proto/semantic_feature_overrides.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
@@ -49,7 +50,7 @@ ContentAutofillAssistantDriver::GetOrCreateForRenderFrameHost(
ContentAutofillAssistantDriver* driver =
ContentAutofillAssistantDriver::GetOrCreateForCurrentDocument(
render_frame_host);
- if (driver) {
+ if (driver && annotate_dom_model_service) {
driver->SetAnnotateDomModelService(annotate_dom_model_service);
}
return driver;
@@ -77,13 +78,15 @@ void ContentAutofillAssistantDriver::GetAnnotateDomModel(
GetAnnotateDomModelCallback callback) {
if (!annotate_dom_model_service_) {
NOTREACHED() << "No model service";
- std::move(callback).Run(mojom::ModelStatus::kUnexpectedError, base::File());
+ std::move(callback).Run(mojom::ModelStatus::kUnexpectedError, base::File(),
+ GetOverridesPolicy());
return;
}
absl::optional<base::File> file = annotate_dom_model_service_->GetModelFile();
if (file) {
- std::move(callback).Run(mojom::ModelStatus::kSuccess, *std::move(file));
+ std::move(callback).Run(mojom::ModelStatus::kSuccess, *std::move(file),
+ GetOverridesPolicy());
return;
}
@@ -129,7 +132,8 @@ void ContentAutofillAssistantDriver::RunCallback(
}
DCHECK(it->second->callback_);
- std::move(it->second->callback_).Run(model_status, std::move(model_file));
+ std::move(it->second->callback_)
+ .Run(model_status, std::move(model_file), GetOverridesPolicy());
pending_calls_.erase(it);
}
@@ -139,4 +143,9 @@ void ContentAutofillAssistantDriver::SetAnnotateDomModelService(
annotate_dom_model_service_ = annotate_dom_model_service;
}
+std::string ContentAutofillAssistantDriver::GetOverridesPolicy() const {
+ DCHECK(annotate_dom_model_service_);
+ return annotate_dom_model_service_->GetOverridesPolicy();
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.h b/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.h
index 69b81025489..c9bc30a327d 100644
--- a/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.h
+++ b/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver.h
@@ -68,6 +68,7 @@ class ContentAutofillAssistantDriver
void RunCallback(const std::string& guid,
mojom::ModelStatus model_status,
base::File model_file);
+ std::string GetOverridesPolicy() const;
raw_ptr<AnnotateDomModelService> annotate_dom_model_service_ = nullptr;
diff --git a/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver_unittest.cc b/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver_unittest.cc
index d83ac8b134b..743e19041e8 100644
--- a/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver_unittest.cc
+++ b/chromium/components/autofill_assistant/content/browser/content_autofill_assistant_driver_unittest.cc
@@ -48,8 +48,8 @@ class ContentAutofillAssistantDriverTest : public testing::Test {
&browser_context_, nullptr);
// Constructor of ContentAutofillAssistantDriver is private, cannot use
// std::make_unique.
- driver_ = base::WrapUnique(
- new ContentAutofillAssistantDriver(web_contents_->GetMainFrame()));
+ driver_ = base::WrapUnique(new ContentAutofillAssistantDriver(
+ web_contents_->GetPrimaryMainFrame()));
driver_->SetAnnotateDomModelService(annotate_dom_model_service_.get());
}
@@ -67,29 +67,29 @@ class ContentAutofillAssistantDriverTest : public testing::Test {
std::unique_ptr<ContentAutofillAssistantDriver> driver_;
std::unique_ptr<AnnotateDomModelService> annotate_dom_model_service_;
base::File model_file_;
+
+ base::MockCallback<base::OnceCallback<
+ void(mojom::ModelStatus, base::File, const std::string&)>>
+ callback_;
};
TEST_F(ContentAutofillAssistantDriverTest, GetLoadedModelFromService) {
// Model has been loaded before.
annotate_dom_model_service_->SetModelFileForTest(model_file_.Duplicate());
- base::MockCallback<base::OnceCallback<void(mojom::ModelStatus, base::File)>>
- callback;
- EXPECT_CALL(callback, Run(mojom::ModelStatus::kSuccess, _));
+ EXPECT_CALL(callback_, Run(mojom::ModelStatus::kSuccess, _, _));
driver_->GetAnnotateDomModel(/* timeout= */ base::Milliseconds(1000),
- callback.Get());
+ callback_.Get());
EXPECT_FALSE(HasPendingCallbacks());
}
TEST_F(ContentAutofillAssistantDriverTest, GetModelFromServiceAfterLoading) {
- base::MockCallback<base::OnceCallback<void(mojom::ModelStatus, base::File)>>
- callback;
- EXPECT_CALL(callback, Run(mojom::ModelStatus::kSuccess, _));
+ EXPECT_CALL(callback_, Run(mojom::ModelStatus::kSuccess, _, _));
driver_->GetAnnotateDomModel(/* timeout= */ base::Milliseconds(1000),
- callback.Get());
+ callback_.Get());
// Model loaded after being requested.
annotate_dom_model_service_->SetModelFileForTest(model_file_.Duplicate());
@@ -98,12 +98,10 @@ TEST_F(ContentAutofillAssistantDriverTest, GetModelFromServiceAfterLoading) {
}
TEST_F(ContentAutofillAssistantDriverTest, GetModelTimesOut) {
- base::MockCallback<base::OnceCallback<void(mojom::ModelStatus, base::File)>>
- callback;
- EXPECT_CALL(callback, Run(mojom::ModelStatus::kTimeout, _));
+ EXPECT_CALL(callback_, Run(mojom::ModelStatus::kTimeout, _, _));
driver_->GetAnnotateDomModel(/* timeout= */ base::Milliseconds(1000),
- callback.Get());
+ callback_.Get());
// Model does not get loaded.
task_environment_.FastForwardBy(base::Seconds(2));
@@ -112,16 +110,14 @@ TEST_F(ContentAutofillAssistantDriverTest, GetModelTimesOut) {
}
TEST_F(ContentAutofillAssistantDriverTest, MultipleParallelCalls) {
- base::MockCallback<base::OnceCallback<void(mojom::ModelStatus, base::File)>>
- callback;
- EXPECT_CALL(callback, Run(mojom::ModelStatus::kTimeout, _)).Times(3);
+ EXPECT_CALL(callback_, Run(mojom::ModelStatus::kTimeout, _, _)).Times(3);
driver_->GetAnnotateDomModel(/* timeout= */ base::Milliseconds(1000),
- callback.Get());
+ callback_.Get());
driver_->GetAnnotateDomModel(/* timeout= */ base::Milliseconds(1000),
- callback.Get());
+ callback_.Get());
driver_->GetAnnotateDomModel(/* timeout= */ base::Milliseconds(1000),
- callback.Get());
+ callback_.Get());
// Model does not get loaded.
task_environment_.FastForwardBy(base::Seconds(2));
@@ -129,4 +125,16 @@ TEST_F(ContentAutofillAssistantDriverTest, MultipleParallelCalls) {
EXPECT_FALSE(HasPendingCallbacks());
}
+TEST_F(ContentAutofillAssistantDriverTest, EmptyOverrides) {
+ EXPECT_CALL(callback_, Run(mojom::ModelStatus::kSuccess, _, std::string()));
+
+ driver_->GetAnnotateDomModel(/* timeout= */ base::Milliseconds(1000),
+ callback_.Get());
+
+ // Model loaded after being requested.
+ annotate_dom_model_service_->SetModelFileForTest(model_file_.Duplicate());
+
+ EXPECT_FALSE(HasPendingCallbacks());
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/content/common/BUILD.gn b/chromium/components/autofill_assistant/content/common/BUILD.gn
index 4f4f1bbb842..63cf9526a79 100644
--- a/chromium/components/autofill_assistant/content/common/BUILD.gn
+++ b/chromium/components/autofill_assistant/content/common/BUILD.gn
@@ -8,6 +8,8 @@ static_library("common") {
sources = [
"node_data.cc",
"node_data.h",
+ "switches.cc",
+ "switches.h",
]
}
diff --git a/chromium/components/autofill_assistant/content/common/autofill_assistant_agent.mojom b/chromium/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
index ff86b3937c9..9c1c9160afc 100644
--- a/chromium/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
+++ b/chromium/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
@@ -5,6 +5,7 @@
module autofill_assistant.mojom;
import "components/autofill_assistant/content/common/autofill_assistant_types.mojom";
+import "mojo/public/mojom/base/string16.mojom";
import "mojo/public/mojom/base/time.mojom";
// There is one instance of this interface per render frame in the renderer
@@ -16,4 +17,12 @@ interface AutofillAssistantAgent {
mojo_base.mojom.TimeDelta timeout)
=> (NodeDataStatus status,
array<autofill_assistant.mojom.NodeData> nodes);
+
+ // Set the value of a web element. The target needs to be a form control
+ // element, otherwise the call fails. This works for input, textarea and
+ // select. For a select element it finds the option with a value matching
+ // the given parameter (exactly) and makes that option the current selection.
+ SetElementValue(int32 backend_node_id, mojo_base.mojom.String16 value,
+ bool send_events)
+ => (bool success);
};
diff --git a/chromium/components/autofill_assistant/content/common/autofill_assistant_driver.mojom b/chromium/components/autofill_assistant/content/common/autofill_assistant_driver.mojom
index 703883b0cf3..b69d97aa4ab 100644
--- a/chromium/components/autofill_assistant/content/common/autofill_assistant_driver.mojom
+++ b/chromium/components/autofill_assistant/content/common/autofill_assistant_driver.mojom
@@ -5,6 +5,7 @@
module autofill_assistant.mojom;
import "components/autofill_assistant/content/common/autofill_assistant_types.mojom";
+import "mojo/public/mojom/base/byte_string.mojom";
import "mojo/public/mojom/base/read_only_file.mojom";
import "mojo/public/mojom/base/time.mojom";
@@ -14,5 +15,6 @@ interface AutofillAssistantDriver {
// Request that the annotate DOM model is being loaded and returned for use
// by the AutofillAssistantAgent.
GetAnnotateDomModel(mojo_base.mojom.TimeDelta timeout)
- => (ModelStatus status, mojo_base.mojom.ReadOnlyFile? model_file);
+ => (ModelStatus status, mojo_base.mojom.ReadOnlyFile? model_file,
+ mojo_base.mojom.ByteString overrides_policy);
};
diff --git a/chromium/components/autofill_assistant/content/common/switches.cc b/chromium/components/autofill_assistant/content/common/switches.cc
new file mode 100644
index 00000000000..35256c49dcf
--- /dev/null
+++ b/chromium/components/autofill_assistant/content/common/switches.cc
@@ -0,0 +1,13 @@
+// Copyright 2022 The Chromium 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/autofill_assistant/content/common/switches.h"
+
+namespace autofill_assistant::switches {
+
+// Enables annotating DOM debugging when set.
+const char kAutofillAssistantDebugAnnotateDom[] =
+ "autofill-assistant-debug-annotate-dom";
+
+} // namespace autofill_assistant::switches
diff --git a/chromium/components/autofill_assistant/content/common/switches.h b/chromium/components/autofill_assistant/content/common/switches.h
new file mode 100644
index 00000000000..80e8ce6d425
--- /dev/null
+++ b/chromium/components/autofill_assistant/content/common/switches.h
@@ -0,0 +1,15 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_AUTOFILL_ASSISTANT_CONTENT_COMMON_SWITCHES_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_CONTENT_COMMON_SWITCHES_H_
+
+namespace autofill_assistant::switches {
+
+// All switches in alphabetical order.
+extern const char kAutofillAssistantDebugAnnotateDom[];
+
+} // namespace autofill_assistant::switches
+
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_CONTENT_COMMON_SWITCHES_H_
diff --git a/chromium/components/autofill_assistant/content/renderer/BUILD.gn b/chromium/components/autofill_assistant/content/renderer/BUILD.gn
index 3138bb95fb8..b2173f11e4e 100644
--- a/chromium/components/autofill_assistant/content/renderer/BUILD.gn
+++ b/chromium/components/autofill_assistant/content/renderer/BUILD.gn
@@ -20,6 +20,7 @@ static_library("renderer") {
"//base",
"//components/autofill_assistant/content/common:common",
"//components/autofill_assistant/content/common:mojo_interfaces",
+ "//components/autofill_assistant/content/common/proto:proto",
"//components/optimization_guide:machine_learning_tflite_buildflags",
"//content/public/common:common",
"//content/public/renderer:renderer",
@@ -73,6 +74,7 @@ source_set("browser_tests") {
"//base",
"//base/test:test_support",
"//components/autofill_assistant/content/common:mojo_interfaces",
+ "//components/autofill_assistant/content/common/proto:proto",
"//content/public/browser",
"//content/public/renderer",
"//content/test:test_support",
diff --git a/chromium/components/autofill_assistant/content/renderer/DEPS b/chromium/components/autofill_assistant/content/renderer/DEPS
index a28ced1664c..9218c077f71 100644
--- a/chromium/components/autofill_assistant/content/renderer/DEPS
+++ b/chromium/components/autofill_assistant/content/renderer/DEPS
@@ -10,6 +10,7 @@ include_rules = [
"+third_party/blink/public/common",
"+third_party/blink/public/platform",
"+third_party/blink/public/web",
+ "+third_party/protobuf/src/google/protobuf/repeated_field.h",
"+third_party/tflite",
"+third_party/tflite_support",
]
diff --git a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
index f7b2cb06fe5..cdf09d94e7d 100644
--- a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
+++ b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
@@ -4,18 +4,101 @@
#include "components/autofill_assistant/content/renderer/autofill_assistant_agent.h"
+#include <ostream>
+
+#include "base/command_line.h"
+#include "components/autofill_assistant/content/common/switches.h"
#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
#include "content/public/renderer/render_frame.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/modules/autofill_assistant/node_signals.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_form_control_element.h"
#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+#include "components/autofill_assistant/content/common/proto/semantic_feature_overrides.pb.h"
#include "components/autofill_assistant/content/renderer/autofill_assistant_model_executor.h"
#endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
namespace autofill_assistant {
+namespace {
+
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+
+using OverridesMap = AutofillAssistantModelExecutor::OverridesMap;
+using SparseVector = AutofillAssistantModelExecutor::SparseVector;
+
+std::string NodeSignalsToDebugString(
+ const blink::AutofillAssistantNodeSignals& node_signals) {
+ std::ostringstream out;
+
+ out << "AutofillAssistantNodeSignals {\n"
+ << "\tbackend_node_id: " << node_signals.backend_node_id
+ << "\n\tnode_features {";
+ for (const auto& text : node_signals.node_features.text) {
+ out << "\n\t\ttext: " << text.Utf16();
+ }
+ out << "\n\t\taria: " << node_signals.node_features.aria.Utf16()
+ << "\n\t\thtml_tag: " << node_signals.node_features.html_tag.Utf16()
+ << "\n\t\ttype: " << node_signals.node_features.type.Utf16()
+ << "\n\t\tinvisible_attributes: "
+ << node_signals.node_features.invisible_attributes.Utf16()
+ << "\n\t}\n\tlabel_features {";
+ for (const auto& text : node_signals.label_features.text) {
+ out << "\n\t\ttext: " << text.Utf16();
+ }
+ out << "\n\t}\n\tcontext_features {";
+ for (const auto& header_text : node_signals.context_features.header_text) {
+ out << "\n\t\theader_text: " << header_text.Utf16();
+ }
+ out << "\n\t\tform_type: " << node_signals.context_features.form_type.Utf16()
+ << "\n\t}\n}";
+
+ return out.str();
+}
+
+SparseVector KeyCoordinatesToSparseVector(
+ const ::google::protobuf::RepeatedPtrField<SparseEncoding>&
+ key_coordinates) {
+ SparseVector sparse_vector;
+ for (const auto& coordinate : key_coordinates) {
+ sparse_vector.emplace_back(
+ std::make_pair(std::make_pair(coordinate.feature_concatenation_index(),
+ coordinate.vocabulary_index()),
+ coordinate.number_of_occurrences()));
+ }
+ return sparse_vector;
+}
+
+absl::optional<OverridesMap> ParseOverridesPolicyToMap(
+ std::string overrides_policy) {
+ SemanticSelectorPolicy policy;
+ if (!policy.ParseFromString(
+ std::string(overrides_policy.begin(), overrides_policy.end()))) {
+ return absl::nullopt;
+ }
+ if (policy.bag_of_words().data_point_map().empty()) {
+ return absl::nullopt;
+ }
+ OverridesMap overrides_map;
+ for (const auto& data_point : policy.bag_of_words().data_point_map()) {
+ if (data_point.key_coordinate().empty()) {
+ continue;
+ }
+ const auto& value = data_point.value();
+ overrides_map[KeyCoordinatesToSparseVector(data_point.key_coordinate())] =
+ std::make_pair(value.semantic_role(), value.objective());
+ }
+ return overrides_map;
+}
+
+#endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+
+} // namespace
AutofillAssistantAgent::AutofillAssistantAgent(
content::RenderFrame* render_frame,
@@ -52,6 +135,7 @@ void AutofillAssistantAgent::GetSemanticNodes(
GetSemanticNodesCallback callback) {
blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
if (!frame) {
+ VLOG(1) << "Failed to get semantic nodes, no frame.";
std::move(callback).Run(mojom::NodeDataStatus::kUnexpectedError,
std::vector<NodeData>());
return;
@@ -66,7 +150,8 @@ void AutofillAssistantAgent::GetSemanticNodes(
void AutofillAssistantAgent::GetAnnotateDomModel(
base::TimeDelta model_timeout,
- base::OnceCallback<void(mojom::ModelStatus, base::File)> callback) {
+ base::OnceCallback<void(mojom::ModelStatus, base::File, const std::string&)>
+ callback) {
GetDriver().GetAnnotateDomModel(model_timeout, std::move(callback));
}
@@ -77,14 +162,16 @@ mojom::AutofillAssistantDriver& AutofillAssistantAgent::GetDriver() {
return *driver_;
}
-void AutofillAssistantAgent::OnGetModelFile(base::Time start_time,
- blink::WebLocalFrame* frame,
- int32_t role,
- int32_t objective,
- bool ignore_objective,
- GetSemanticNodesCallback callback,
- mojom::ModelStatus model_status,
- base::File model) {
+void AutofillAssistantAgent::OnGetModelFile(
+ base::Time start_time,
+ blink::WebLocalFrame* frame,
+ int32_t role,
+ int32_t objective,
+ bool ignore_objective,
+ GetSemanticNodesCallback callback,
+ mojom::ModelStatus model_status,
+ base::File model,
+ const std::string& overrides_policy) {
std::vector<NodeData> nodes;
switch (model_status) {
case mojom::ModelStatus::kSuccess:
@@ -109,7 +196,8 @@ void AutofillAssistantAgent::OnGetModelFile(base::Time start_time,
<< (on_node_signals - on_get_model_file).InMilliseconds() << "ms";
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
- AutofillAssistantModelExecutor model_executor;
+ AutofillAssistantModelExecutor model_executor(
+ ParseOverridesPolicyToMap(std::move(overrides_policy)));
if (!model_executor.InitializeModelFromFile(std::move(model))) {
std::move(callback).Run(mojom::NodeDataStatus::kInitializationError, nodes);
return;
@@ -123,9 +211,16 @@ void AutofillAssistantAgent::OnGetModelFile(base::Time start_time,
for (const auto& node_signal : node_signals) {
auto result = model_executor.ExecuteModelWithInput(node_signal);
- DVLOG(3) << "Annotated node with result: role: " << result->first
- << " and objective: " << result->second
- << " (or ignore: " << ignore_objective << ")";
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAutofillAssistantDebugAnnotateDom)) {
+ VLOG(3) << NodeSignalsToDebugString(node_signal);
+ if (result) {
+ VLOG(3) << "Result { role: " << result->first
+ << ", objective: " << result->second
+ << (ignore_objective ? " (ignored)" : "") << " }";
+ }
+ }
+
if (result && result->first == role &&
(result->second == objective || ignore_objective)) {
NodeData node_data;
@@ -145,4 +240,30 @@ void AutofillAssistantAgent::OnGetModelFile(base::Time start_time,
std::move(callback).Run(mojom::NodeDataStatus::kSuccess, nodes);
}
+void AutofillAssistantAgent::SetElementValue(const int32_t backend_node_id,
+ const std::u16string& value,
+ bool send_events,
+ SetElementValueCallback callback) {
+ blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+ if (!frame) {
+ VLOG(1) << "Failed to set Element value, no frame.";
+ std::move(callback).Run(false);
+ return;
+ }
+
+ blink::WebElement target_element =
+ frame->GetDocument().GetElementByDevToolsNodeId(backend_node_id);
+ if (target_element.IsNull() || !target_element.IsFormControlElement()) {
+ VLOG(3) << "Failed to set Element value, invalid target.";
+ std::move(callback).Run(false);
+ return;
+ }
+
+ blink::WebFormControlElement target_form_control_element =
+ target_element.To<blink::WebFormControlElement>();
+ target_form_control_element.SetValue(blink::WebString::FromUTF16(value),
+ send_events);
+ std::move(callback).Run(true);
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.h b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
index b546af66e3f..41773f1c171 100644
--- a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
+++ b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
@@ -47,6 +47,10 @@ class AutofillAssistantAgent : public content::RenderFrameObserver,
bool ignore_objective,
base::TimeDelta model_timeout,
GetSemanticNodesCallback callback) override;
+ void SetElementValue(int32_t backend_node_id,
+ const std::u16string& value,
+ bool send_events,
+ SetElementValueCallback callback) override;
private:
// content::RenderFrameObserver:
@@ -54,7 +58,8 @@ class AutofillAssistantAgent : public content::RenderFrameObserver,
void GetAnnotateDomModel(
base::TimeDelta model_timeout,
- base::OnceCallback<void(mojom::ModelStatus, base::File)> callback);
+ base::OnceCallback<
+ void(mojom::ModelStatus, base::File, const std::string&)> callback);
mojom::AutofillAssistantDriver& GetDriver();
@@ -65,7 +70,8 @@ class AutofillAssistantAgent : public content::RenderFrameObserver,
bool ignore_objective,
GetSemanticNodesCallback callback,
mojom::ModelStatus model_status,
- base::File model);
+ base::File model,
+ const std::string& overrides_policy);
mojo::AssociatedRemote<mojom::AutofillAssistantDriver> driver_;
diff --git a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
index 36900340e42..690a33abd36 100644
--- a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
+++ b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
@@ -13,12 +13,17 @@
#include "base/test/mock_callback.h"
#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
#include "components/autofill_assistant/content/common/autofill_assistant_driver.mojom.h"
+#include "components/autofill_assistant/content/common/proto/semantic_feature_overrides.pb.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/test/render_view_test.h"
#include "mojo/public/cpp/bindings/associated_receiver_set.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_form_control_element.h"
+#include "third_party/blink/public/web/web_local_frame.h"
namespace autofill_assistant {
namespace {
@@ -27,6 +32,9 @@ using ::base::test::RunOnceCallback;
using ::testing::_;
using ::testing::SizeIs;
+constexpr int kDummySemanticRole = 9999;
+constexpr int kDummyObjective = 1111;
+
class MockAutofillAssistantDriver : public mojom::AutofillAssistantDriver {
public:
void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle) {
@@ -39,7 +47,8 @@ class MockAutofillAssistantDriver : public mojom::AutofillAssistantDriver {
void,
GetAnnotateDomModel,
(base::TimeDelta timeout,
- base::OnceCallback<void(mojom::ModelStatus, base::File)> callback),
+ base::OnceCallback<
+ void(mojom::ModelStatus, base::File, const std::string&)> callback),
(override));
private:
@@ -92,7 +101,7 @@ class AutofillAssistantAgentBrowserTest : public content::RenderViewTest {
TEST_F(AutofillAssistantAgentBrowserTest, GetSemanticNodes) {
EXPECT_CALL(autofill_assistant_driver_, GetAnnotateDomModel)
.WillOnce(RunOnceCallback<1>(mojom::ModelStatus::kSuccess,
- model_file_.Duplicate()));
+ model_file_.Duplicate(), std::string()));
base::MockCallback<base::OnceCallback<void(mojom::NodeDataStatus,
const std::vector<NodeData>&)>>
@@ -117,7 +126,8 @@ TEST_F(AutofillAssistantAgentBrowserTest, GetSemanticNodes) {
TEST_F(AutofillAssistantAgentBrowserTest, GetSemanticNodesModelTimeout) {
// Do not reply to the model call.
EXPECT_CALL(autofill_assistant_driver_, GetAnnotateDomModel)
- .WillOnce(RunOnceCallback<1>(mojom::ModelStatus::kTimeout, base::File()));
+ .WillOnce(RunOnceCallback<1>(mojom::ModelStatus::kTimeout, base::File(),
+ std::string()));
base::MockCallback<base::OnceCallback<void(mojom::NodeDataStatus,
const std::vector<NodeData>&)>>
@@ -143,7 +153,7 @@ TEST_F(AutofillAssistantAgentBrowserTest, GetSemanticNodesModelError) {
// Do not reply to the model call.
EXPECT_CALL(autofill_assistant_driver_, GetAnnotateDomModel)
.WillOnce(RunOnceCallback<1>(mojom::ModelStatus::kUnexpectedError,
- base::File()));
+ base::File(), std::string()));
base::MockCallback<base::OnceCallback<void(mojom::NodeDataStatus,
const std::vector<NodeData>&)>>
@@ -168,7 +178,7 @@ TEST_F(AutofillAssistantAgentBrowserTest, GetSemanticNodesModelError) {
TEST_F(AutofillAssistantAgentBrowserTest, GetSemanticNodesIgnoreObjective) {
EXPECT_CALL(autofill_assistant_driver_, GetAnnotateDomModel)
.WillOnce(RunOnceCallback<1>(mojom::ModelStatus::kSuccess,
- model_file_.Duplicate()));
+ model_file_.Duplicate(), std::string()));
LoadHTML(R"(
<div>
@@ -189,5 +199,114 @@ TEST_F(AutofillAssistantAgentBrowserTest, GetSemanticNodesIgnoreObjective) {
base::RunLoop().RunUntilIdle();
}
+TEST_F(AutofillAssistantAgentBrowserTest, Overrides) {
+ SemanticSelectorPolicy policy_proto;
+ auto* single_override =
+ policy_proto.mutable_bag_of_words()->add_data_point_map();
+
+ auto* coordinate = single_override->add_key_coordinate();
+ coordinate->set_feature_concatenation_index(0);
+ // Vocabulary entry "input"
+ coordinate->set_vocabulary_index(1);
+ coordinate->set_number_of_occurrences(1);
+
+ auto* coordinate2 = single_override->add_key_coordinate();
+ coordinate2->set_feature_concatenation_index(3);
+ // Vocabulary entry "street"
+ coordinate2->set_vocabulary_index(862);
+ coordinate2->set_number_of_occurrences(1);
+
+ auto* value = single_override->mutable_value();
+ value->set_objective(kDummyObjective);
+ value->set_semantic_role(kDummySemanticRole);
+
+ std::string policy;
+ ASSERT_TRUE(policy_proto.SerializeToString(&policy));
+
+ EXPECT_CALL(autofill_assistant_driver_, GetAnnotateDomModel)
+ .WillOnce(RunOnceCallback<1>(mojom::ModelStatus::kSuccess,
+ model_file_.Duplicate(), policy));
+
+ LoadHTML(R"(
+ <div>
+ <label for="street">street</label><input id="street">
+ </div>)");
+
+ base::MockCallback<base::OnceCallback<void(mojom::NodeDataStatus,
+ const std::vector<NodeData>&)>>
+ callback;
+ EXPECT_CALL(callback, Run(mojom::NodeDataStatus::kSuccess, SizeIs(1)));
+
+ autofill_assistant_agent_->GetSemanticNodes(
+ kDummySemanticRole, kDummyObjective,
+ /* ignore_objective= */ false,
+ /* model_timeout= */ base::Milliseconds(1000), callback.Get());
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(AutofillAssistantAgentBrowserTest, SetElementValueForInput) {
+ LoadHTML(R"(<input id="id">)");
+
+ base::MockCallback<base::OnceCallback<void(bool)>> callback;
+ EXPECT_CALL(callback, Run(true));
+
+ const auto web_element = GetMainRenderFrame()
+ ->GetWebFrame()
+ ->GetDocument()
+ .GetElementById(blink::WebString::FromUTF8("id"))
+ .To<blink::WebFormControlElement>();
+
+ autofill_assistant_agent_->SetElementValue(
+ web_element.GetDevToolsNodeIdForTest(), u"value",
+ /* send_events= */ true, callback.Get());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(web_element.Value(), "value");
+}
+
+TEST_F(AutofillAssistantAgentBrowserTest, SetElementValueForSelect) {
+ LoadHTML(R"(
+ <select id="id">
+ <option value="dog">Dog</option>
+ <option value="cat">Cat</option>
+ </select>)");
+
+ base::MockCallback<base::OnceCallback<void(bool)>> callback;
+ EXPECT_CALL(callback, Run(true));
+
+ const auto web_element = GetMainRenderFrame()
+ ->GetWebFrame()
+ ->GetDocument()
+ .GetElementById(blink::WebString::FromUTF8("id"))
+ .To<blink::WebFormControlElement>();
+
+ EXPECT_EQ(web_element.Value(), "dog");
+
+ autofill_assistant_agent_->SetElementValue(
+ web_element.GetDevToolsNodeIdForTest(), u"cat",
+ /* send_events= */ true, callback.Get());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(web_element.Value(), "cat");
+}
+
+TEST_F(AutofillAssistantAgentBrowserTest,
+ SetElementValueFailsForNonFormControl) {
+ LoadHTML(R"(
+ <div id="id"></div>)");
+
+ base::MockCallback<base::OnceCallback<void(bool)>> callback;
+ EXPECT_CALL(callback, Run(false));
+
+ const auto web_element =
+ GetMainRenderFrame()->GetWebFrame()->GetDocument().GetElementById(
+ blink::WebString::FromUTF8("id"));
+
+ autofill_assistant_agent_->SetElementValue(
+ web_element.GetDevToolsNodeIdForTest(), u"value",
+ /* send_events= */ true, callback.Get());
+ base::RunLoop().RunUntilIdle();
+}
+
} // namespace
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.cc b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.cc
index 4e499ae64e8..df2c4a8b992 100644
--- a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.cc
+++ b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.cc
@@ -4,9 +4,13 @@
#include "components/autofill_assistant/content/renderer/autofill_assistant_model_executor.h"
+#include <ostream>
+
+#include "base/command_line.h"
#include "base/i18n/case_conversion.h"
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
+#include "components/autofill_assistant/content/common/switches.h"
#include "components/optimization_guide/core/execution_status.h"
#include "components/optimization_guide/core/tflite_op_resolver.h"
#include "third_party/abseil-cpp/absl/status/status.h"
@@ -17,8 +21,43 @@
#include "third_party/tflite_support/src/tensorflow_lite_support/metadata/cc/metadata_extractor.h"
namespace autofill_assistant {
+namespace {
+
+std::string SparseVectorToDebugString(
+ AutofillAssistantModelExecutor::SparseVector sparse_vector) {
+ std::ostringstream out;
+ out << "Sparse vector representation:\n";
+ for (const auto& entry : sparse_vector) {
+ out << " [idx: [" << entry.first.first << ", " << entry.first.second
+ << "], count: " << entry.second << "]";
+ }
+ return out.str();
+}
+
+void DenseEncode(
+ const AutofillAssistantModelExecutor::SparseVector& sparse_vector,
+ std::vector<std::vector<float>>& inputs) {
+ for (const auto& entry : sparse_vector) {
+ const auto& coordinates = entry.first;
+ if (static_cast<size_t>(coordinates.first) >= inputs.size()) {
+ NOTREACHED();
+ continue;
+ }
+ if (static_cast<size_t>(coordinates.second) >=
+ inputs[coordinates.first].size()) {
+ NOTREACHED();
+ continue;
+ }
+ inputs[coordinates.first][coordinates.second] = entry.second;
+ }
+}
+
+} // namespace
+
+AutofillAssistantModelExecutor::AutofillAssistantModelExecutor(
+ absl::optional<OverridesMap> overrides)
+ : overrides_(std::move(overrides)) {}
-AutofillAssistantModelExecutor::AutofillAssistantModelExecutor() = default;
AutofillAssistantModelExecutor::~AutofillAssistantModelExecutor() = default;
bool AutofillAssistantModelExecutor::InitializeModelFromFile(
@@ -121,6 +160,18 @@ bool AutofillAssistantModelExecutor::Preprocess(
NOTREACHED() << "Input tensors mismatch.";
return false;
}
+
+ SparseVector sparse_vector = TokenizeSignalsToSparseVector(node_signals);
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAutofillAssistantDebugAnnotateDom)) {
+ VLOG(3) << SparseVectorToDebugString(sparse_vector);
+ }
+
+ if (overrides_ && overrides_->contains(sparse_vector)) {
+ overrides_result_ = (*overrides_)[sparse_vector];
+ return true;
+ }
+
std::vector<std::vector<float>> inputs;
for (const auto* input_tensor : input_tensors) {
tflite::RuntimeShape shape = tflite::GetTensorShape(input_tensor);
@@ -129,27 +180,7 @@ bool AutofillAssistantModelExecutor::Preprocess(
}
inputs.emplace_back(std::vector(shape.Dims(1), 0.0f));
}
-
- DCHECK(tags_tokenizer_);
- Tokenize(node_signals.node_features.html_tag.Utf16(), tags_tokenizer_.get(),
- &inputs[0]);
- DCHECK(types_tokenizer_);
- Tokenize(node_signals.node_features.type.Utf16(), types_tokenizer_.get(),
- &inputs[1]);
- DCHECK(text_tokenizer_);
- Tokenize(node_signals.node_features.invisible_attributes.Utf16(),
- text_tokenizer_.get(), &inputs[2]);
- for (const auto& text : node_signals.node_features.text) {
- Tokenize(text.Utf16(), text_tokenizer_.get(), &inputs[2]);
- }
- for (const auto& text : node_signals.label_features.text) {
- Tokenize(text.Utf16(), text_tokenizer_.get(), &inputs[3]);
- }
- for (const auto& text : node_signals.context_features.header_text) {
- Tokenize(text.Utf16(), text_tokenizer_.get(), &inputs[4]);
- }
- Tokenize(node_signals.context_features.form_type.Utf16(),
- text_tokenizer_.get(), &inputs[4]);
+ DenseEncode(sparse_vector, inputs);
for (size_t i = 0; i < inputs.size(); ++i) {
absl::Status tensor_status =
@@ -163,7 +194,7 @@ bool AutofillAssistantModelExecutor::Preprocess(
bool AutofillAssistantModelExecutor::GetIndexOfBestRole(
const std::vector<float>& output_role,
- size_t* index_of_best_role) {
+ size_t* index_of_best_role) const {
if (output_role.size() <
static_cast<size_t>(
model_metadata_.output().semantic_role().classes_size())) {
@@ -182,7 +213,7 @@ bool AutofillAssistantModelExecutor::GetIndexOfBestRole(
bool AutofillAssistantModelExecutor::GetBlockIndex(
const std::vector<float>& output_role,
size_t index_of_best_role,
- int* block_index) {
+ int* block_index) const {
if (index_of_best_role >=
static_cast<size_t>(model_metadata_.output()
.semantic_role()
@@ -198,7 +229,7 @@ bool AutofillAssistantModelExecutor::GetBlockIndex(
bool AutofillAssistantModelExecutor::GetObjective(
const std::vector<float>& output_objective,
int block_index,
- int* objective) {
+ int* objective) const {
if (block_index + 1 >= model_metadata_.output().objective().blocks_size()) {
NOTREACHED();
return false;
@@ -207,8 +238,8 @@ bool AutofillAssistantModelExecutor::GetObjective(
model_metadata_.output().objective().blocks(block_index);
auto block_end = output_objective.begin() +
model_metadata_.output().objective().blocks(block_index + 1);
- size_t index_of_best_objective =
- std::distance(block_start, std::max_element(block_start, block_end));
+ size_t index_of_best_objective = std::distance(
+ output_objective.begin(), std::max_element(block_start, block_end));
if (index_of_best_objective >=
static_cast<size_t>(
model_metadata_.output().objective().classes_size())) {
@@ -223,6 +254,18 @@ bool AutofillAssistantModelExecutor::GetObjective(
absl::optional<std::pair<int, int>> AutofillAssistantModelExecutor::Postprocess(
const std::vector<const TfLiteTensor*>& output_tensors) {
+ // Check if we have an override for this execution and return that instead.
+ if (overrides_result_) {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAutofillAssistantDebugAnnotateDom)) {
+ VLOG(3) << "Found override, using (role: " << overrides_result_->first
+ << ", objective: " << overrides_result_->second << ")";
+ }
+ // Cleanup the result in case this executor is reused.
+ std::pair<int, int> result = *overrides_result_;
+ overrides_result_.reset();
+ return result;
+ }
if (output_tensors.size() < 2u) {
NOTREACHED() << "Output Tensors mismatch.";
return absl::nullopt;
@@ -244,6 +287,12 @@ absl::optional<std::pair<int, int>> AutofillAssistantModelExecutor::Postprocess(
if (!GetIndexOfBestRole(output_role, &index_of_best_role)) {
return absl::nullopt;
}
+ if (index_of_best_role >=
+ static_cast<size_t>(
+ model_metadata_.output().semantic_role().classes_size())) {
+ NOTREACHED();
+ return absl::nullopt;
+ }
int semantic_role =
model_metadata_.output().semantic_role().classes(index_of_best_role);
if (semantic_role == 0) {
@@ -265,19 +314,52 @@ absl::optional<std::pair<int, int>> AutofillAssistantModelExecutor::Postprocess(
void AutofillAssistantModelExecutor::Tokenize(
const std::u16string& input,
tflite::support::text::tokenizer::RegexTokenizer* tokenizer,
- std::vector<float>* output) {
+ const int feature_index,
+ SparseMap& output_map) {
auto result =
tokenizer->Tokenize(base::UTF16ToUTF8(base::i18n::ToUpper(input)));
for (const auto& token : result.subwords) {
int index;
if (tokenizer->LookupId(token, &index)) {
- if (static_cast<size_t>(index) >= output->size()) {
- NOTREACHED();
- continue;
- }
- ++output->at(index);
+ output_map[std::make_pair(feature_index, index)]++;
}
}
}
+AutofillAssistantModelExecutor::SparseVector
+AutofillAssistantModelExecutor::TokenizeSignalsToSparseVector(
+ const blink::AutofillAssistantNodeSignals& node_signals) {
+ SparseMap sparse_map;
+
+ DCHECK(tags_tokenizer_);
+ Tokenize(node_signals.node_features.html_tag.Utf16(), tags_tokenizer_.get(),
+ /* feature_index= */ 0, sparse_map);
+ DCHECK(types_tokenizer_);
+ Tokenize(node_signals.node_features.type.Utf16(), types_tokenizer_.get(),
+ /* feature_index= */ 1, sparse_map);
+ DCHECK(text_tokenizer_);
+ Tokenize(node_signals.node_features.invisible_attributes.Utf16(),
+ text_tokenizer_.get(), /* feature_index= */ 2, sparse_map);
+ for (const auto& text : node_signals.node_features.text) {
+ Tokenize(text.Utf16(), text_tokenizer_.get(), /* feature_index= */ 2,
+ sparse_map);
+ }
+ for (const auto& text : node_signals.label_features.text) {
+ Tokenize(text.Utf16(), text_tokenizer_.get(), /* feature_index= */ 3,
+ sparse_map);
+ }
+ for (const auto& text : node_signals.context_features.header_text) {
+ Tokenize(text.Utf16(), text_tokenizer_.get(), /* feature_index= */ 4,
+ sparse_map);
+ }
+ Tokenize(node_signals.context_features.form_type.Utf16(),
+ text_tokenizer_.get(), /* feature_index= */ 4, sparse_map);
+
+ SparseVector sparse_vector;
+ for (const auto& entry : sparse_map) {
+ sparse_vector.emplace_back(entry);
+ }
+ return sparse_vector;
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.h b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.h
index f067934a534..d234c6f3c48 100644
--- a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.h
+++ b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor.h
@@ -33,8 +33,12 @@ class AutofillAssistantModelExecutor
using ExecutionTask = optimization_guide::GenericModelExecutionTask<
std::pair<int, int>,
const blink::AutofillAssistantNodeSignals&>;
+ using SparseVector = std::vector<std::pair<std::pair<int, int>, int>>;
+ using SparseMap = base::flat_map<std::pair<int, int>, int>;
+ using OverridesMap = base::flat_map<SparseVector, std::pair<int, int>>;
- AutofillAssistantModelExecutor();
+ explicit AutofillAssistantModelExecutor(
+ absl::optional<OverridesMap> policy = absl::nullopt);
~AutofillAssistantModelExecutor() override;
AutofillAssistantModelExecutor(const AutofillAssistantModelExecutor&) =
@@ -70,21 +74,25 @@ class AutofillAssistantModelExecutor
void BuildExecutionTask(
std::unique_ptr<tflite::task::core::TfLiteEngine> tflite_engine);
- // Tokenize the |input| and count words into the |output| vector. The |output|
- // can be reused for all relevant inputs for a signal.
+ // Tokenize the |input| and count words into the |output_map|. The same
+ // |output_map| should be reused for all relevant inputs for a signal.
void Tokenize(const std::u16string& input,
tflite::support::text::tokenizer::RegexTokenizer* tokenizer,
- std::vector<float>* output);
+ const int feature_index,
+ SparseMap& output_map);
+
+ SparseVector TokenizeSignalsToSparseVector(
+ const blink::AutofillAssistantNodeSignals& node_signals);
// Helper functions for post processing based on |model_metadata_|.
bool GetIndexOfBestRole(const std::vector<float>& output_role,
- size_t* index_of_best_role);
+ size_t* index_of_best_role) const;
bool GetBlockIndex(const std::vector<float>& output_role,
size_t index_of_best_role,
- int* block_index);
+ int* block_index) const;
bool GetObjective(const std::vector<float>& output_objective,
int block_index,
- int* objective);
+ int* objective) const;
// Tokenizer for HTML tag.
std::unique_ptr<tflite::support::text::tokenizer::RegexTokenizer>
@@ -102,6 +110,12 @@ class AutofillAssistantModelExecutor
base::MemoryMappedFile model_file_;
// Model Metadata for handling input/output.
ModelMetadata model_metadata_;
+ // Data regarding business logic for model execution.
+ // Set if there is an override for this model execution.
+ // Sparse encoding of a feature vector table.
+ // The format is: overrides_[vector] = (semantic_role, objective)
+ absl::optional<OverridesMap> overrides_;
+ absl::optional<std::pair<int, int>> overrides_result_;
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor_unittest.cc b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor_unittest.cc
index 2f5df620f25..71a5c269564 100644
--- a/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor_unittest.cc
+++ b/chromium/components/autofill_assistant/content/renderer/autofill_assistant_model_executor_unittest.cc
@@ -24,6 +24,12 @@
namespace autofill_assistant {
namespace {
+using OverridesMap = AutofillAssistantModelExecutor::OverridesMap;
+using SparseVector = AutofillAssistantModelExecutor::SparseVector;
+
+constexpr int kDummyObjective = 9999;
+constexpr int kDummySemanticRole = 1111;
+
class AutofillAssistantModelExecutorTest : public testing::Test {
public:
AutofillAssistantModelExecutorTest() {
@@ -36,6 +42,20 @@ class AutofillAssistantModelExecutorTest : public testing::Test {
~AutofillAssistantModelExecutorTest() override = default;
protected:
+ OverridesMap CreateOverrides() {
+ OverridesMap map;
+ SparseVector vector;
+ // First, create a feature vector of the feature "street" that is found
+ // twice on the website for the "second" feature index.
+ vector.push_back(std::make_pair(
+ std::make_pair(/* feature_index = */ 2, /* feature= */ 862),
+ /* count= */ 2));
+ // Add an override with a dummy objetive and semantic role for that feature
+ // vector.
+ map[vector] = std::make_pair(kDummyObjective, kDummySemanticRole);
+ return map;
+ }
+
base::File model_file_;
AutofillAssistantModelExecutor model_executor_;
@@ -86,5 +106,71 @@ TEST_F(AutofillAssistantModelExecutorTest, ExecuteWithLoadedModel) {
EXPECT_EQ(result->second, 7 /* FILL_DELIVERY_ADDRESS */);
}
+TEST_F(AutofillAssistantModelExecutorTest, OverridesMatch) {
+ AutofillAssistantModelExecutor model_executor =
+ AutofillAssistantModelExecutor(CreateOverrides());
+
+ ASSERT_TRUE(model_executor.InitializeModelFromFile(model_file_.Duplicate()));
+
+ blink::AutofillAssistantNodeSignals node_signals;
+ node_signals.node_features.invisible_attributes =
+ blink::WebString::FromUTF8("street");
+ node_signals.node_features.text.push_back(
+ blink::WebString::FromUTF8("street"));
+
+ auto result = model_executor.ExecuteModelWithInput(node_signals);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(result->first, 9999);
+ EXPECT_EQ(result->second, 1111);
+}
+
+TEST_F(AutofillAssistantModelExecutorTest, OverridesNoMatch) {
+ AutofillAssistantModelExecutor model_executor =
+ AutofillAssistantModelExecutor(CreateOverrides());
+
+ ASSERT_TRUE(model_executor.InitializeModelFromFile(model_file_.Duplicate()));
+
+ blink::AutofillAssistantNodeSignals node_signals;
+ node_signals.node_features.text.push_back(
+ blink::WebString::FromUTF8("street"));
+
+ auto result = model_executor.ExecuteModelWithInput(node_signals);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_NE(result->first, 9999);
+ EXPECT_NE(result->second, 1111);
+}
+
+TEST_F(AutofillAssistantModelExecutorTest, OverridesResultNotReused) {
+ AutofillAssistantModelExecutor model_executor =
+ AutofillAssistantModelExecutor(CreateOverrides());
+
+ ASSERT_TRUE(model_executor.InitializeModelFromFile(model_file_.Duplicate()));
+ {
+ blink::AutofillAssistantNodeSignals node_signals;
+ node_signals.node_features.invisible_attributes =
+ blink::WebString::FromUTF8("street");
+ node_signals.node_features.text.push_back(
+ blink::WebString::FromUTF8("street"));
+
+ auto result = model_executor.ExecuteModelWithInput(node_signals);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(result->first, 9999);
+ EXPECT_EQ(result->second, 1111);
+ }
+
+ // We expect the internal overrides result from the previous execution to have
+ // been cleared.
+ {
+ blink::AutofillAssistantNodeSignals node_signals;
+ node_signals.node_features.text.push_back(
+ blink::WebString::FromUTF8("unknown"));
+
+ auto result = model_executor.ExecuteModelWithInput(node_signals);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_NE(result->first, 9999);
+ EXPECT_NE(result->second, 1111);
+ }
+}
+
} // namespace
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/guided_browsing/OWNERS b/chromium/components/autofill_assistant/guided_browsing/OWNERS
new file mode 100644
index 00000000000..ed892cb7288
--- /dev/null
+++ b/chromium/components/autofill_assistant/guided_browsing/OWNERS
@@ -0,0 +1,2 @@
+# Please keep these in alphabetical order
+jainshashank@google.com
diff --git a/chromium/components/autofill_assistant/guided_browsing/android/BUILD.gn b/chromium/components/autofill_assistant/guided_browsing/android/BUILD.gn
new file mode 100644
index 00000000000..8e3d580f887
--- /dev/null
+++ b/chromium/components/autofill_assistant/guided_browsing/android/BUILD.gn
@@ -0,0 +1,50 @@
+# Copyright 2022 The Chromium 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/rules.gni")
+
+android_library("java") {
+ resources_package =
+ "org.chromium.components.autofill_assistant.guided_browsing"
+
+ deps = [
+ ":java_resources",
+ "$google_play_services_package:google_play_services_vision_common_java",
+ "$google_play_services_package:google_play_services_vision_java",
+ "//base:base_java",
+ "//content/public/android:content_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
+ "//ui/android:ui_java",
+ ]
+
+ sources = [
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/LayoutUtils.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/AssistantQrCodeController.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/AssistantQrCodeDelegate.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/camera_scan/AssistantQrCodeCameraCallbacks.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/camera_scan/AssistantQrCodeCameraPreview.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/camera_scan/AssistantQrCodeCameraPreviewOverlay.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/camera_scan/AssistantQrCodeCameraScanBinder.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/camera_scan/AssistantQrCodeCameraScanCoordinator.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/camera_scan/AssistantQrCodeCameraScanDialog.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/camera_scan/AssistantQrCodeCameraScanModel.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/camera_scan/AssistantQrCodeCameraScanView.java",
+ "java/src/org/chromium/components/autofill_assistant/guided_browsing/qr_code/utils/AssistantQrCodePermissionUtils.java",
+ ]
+
+ annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+android_resources("java_resources") {
+ sources = [
+ "java/res/layout/autofill_assistant_qr_code_camera_scan_dialog.xml",
+ "java/res/layout/autofill_assistant_qr_code_camera_scan_permission_layout.xml",
+ "java/res/values-v17/dimens.xml",
+ ]
+ deps = [
+ "//components/browser_ui/strings/android:browser_ui_strings_grd",
+ "//components/browser_ui/styles/android:java_resources",
+ "//components/browser_ui/widget/android:java_resources",
+ ]
+}
diff --git a/chromium/components/autofill_assistant/guided_browsing/android/DEPS b/chromium/components/autofill_assistant/guided_browsing/android/DEPS
new file mode 100644
index 00000000000..83e44bc3998
--- /dev/null
+++ b/chromium/components/autofill_assistant/guided_browsing/android/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+ "+components/browser_ui/styles/android",
+ "+components/browser_ui/widget/android",
+ "+components/browser_ui/strings/android",
+ "+content/public/android",
+ "+ui/android",
+]
diff --git a/chromium/components/autofill_assistant_strings.grdp b/chromium/components/autofill_assistant_strings.grdp
index e742fdbf931..b3855438aa9 100644
--- a/chromium/components/autofill_assistant_strings.grdp
+++ b/chromium/components/autofill_assistant_strings.grdp
@@ -7,7 +7,7 @@
Information missing
</message>
<message name="IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR" desc="Text label that is shown when autofill assistant cannot help anymore, because something went wrong.">
- Sorry, something went wrong.
+ Something went wrong
</message>
<message name="IDS_AUTOFILL_ASSISTANT_LOADING" desc="Text label that is shown during the loading of the first page, right after being triggered.">
Opening <ph name="SITE_NAME">$1<ex>google.com</ex></ph>…
diff --git a/chromium/components/autofill_assistant_strings_grdp/IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR.png.sha1 b/chromium/components/autofill_assistant_strings_grdp/IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR.png.sha1
new file mode 100644
index 00000000000..a43b4afe8b3
--- /dev/null
+++ b/chromium/components/autofill_assistant_strings_grdp/IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR.png.sha1
@@ -0,0 +1 @@
+2e9bf853508c8a97139f865bf83484effdaa441f \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings.grdp b/chromium/components/autofill_payments_strings.grdp
index f356edc2a1f..492566ffac5 100644
--- a/chromium/components/autofill_payments_strings.grdp
+++ b/chromium/components/autofill_payments_strings.grdp
@@ -105,6 +105,9 @@
<message name="IDS_AUTOFILL_MOBILE_SAVE_CARD_TO_CLOUD_CONFIRMATION_DIALOG_EXPLANATION" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments." formatter_data="android_java">
Save your card and billing info to your Google Account for secure and faster checkouts
</message>
+ <message name="IDS_AUTOFILL_MOBILE_SAVE_CARD_TO_CLOUD_CONFIRMATION_DIALOG_EXPLANATION_WITH_ACCOUNT_NAME" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. Includes Google account reference." formatter_data="android_java">
+ Save your card and billing info to your Google Account <ph name="USER_EMAIL">^1<ex>user@gmail.com</ex></ph> for secure and faster checkouts
+ </message>
</if>
<if expr="is_ios">
<then>
@@ -124,7 +127,7 @@
<message name="IDS_AUTOFILL_FIX_FLOW_PROMPT_SAVE_CARD_LABEL" desc="Text to show on the button to save the card to Google when the fix flow dialog is shown after the Autofill save card prompt." formatter_data="android_java">
Save card
</message>
- <if expr="is_linux and not chromeos_ash">
+ <if expr="is_linux">
<then>
<message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_TO_CLOUD" desc="Title text for the Autofill save card prompt when the card is to be saved by uploading it to Google Payments and also saved locally. The prompt can be either a bubble or an infobar.">
Do you want to save this card to your Google Account?
@@ -136,6 +139,12 @@
</message>
</else>
</if>
+ <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE" desc="Title text for the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. The prompt can be either a bubble or an infobar. This is an experiment text and may change in the future.">
+ Save card securely
+ </message>
+ <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED" desc="Title text for the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. The prompt can be either a bubble or an infobar. This is an experiment text and may change in the future.">
+ Save card?
+ </message>
<message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_TO_CLOUD_V3" desc="Title text for the Autofill save card prompt when the card is to be saved by uploading it to Google Payments, according to January 2018 UI guidelines. The prompt can be either a bubble or an infobar.">
Save card?
</message>
@@ -163,6 +172,12 @@
<message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_V3" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments, according to April 2018 UI guidelines. The prompt will be shown in a bubble below the omnibox.">
To pay faster next time, save your card and billing address to your Google Account.
</message>
+ <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. The prompt will be shown in a bubble below the omnibox. This is an experiment text and may change in the future.">
+ It’ll be encrypted, saved securely and the CVC is never stored.
+ </message>
+ <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. The prompt will be shown in a bubble below the omnibox. This is an experiment text and may change in the future.">
+ Pay faster next time and protect your card with Google’s industry-leading security.
+ </message>
<message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_V3_WITH_NAME" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments, according to April 2018 UI guidelines. The prompt will be shown in a bubble below the omnibox.">
To pay faster next time, save your card, name, and billing address to your Google Account.
</message>
@@ -545,7 +560,7 @@
Make it more secure with a virtual card?
</message>
<message name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_DIALOG_CONTENT_LABEL" desc="Text explaining the benefit of enrolling a credit card as a virtual card. Also contains a link to learn more about virtual cards from IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL.">
- A virtual card disguises your actual card to help protect you from potential fraud. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL">$1<ex>Learn about virtual cards</ex></ph>
+ A virtual card hides your actual card to help protect you from potential fraud. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL">$1<ex>Learn about virtual cards</ex></ph>
</message>
<message name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_DECLINE_BUTTON_LABEL_SKIP" desc="Text displayed in the decline button in the virtual card enroll bubble. It is displayed only if the bubble/infobar will still be shown again at a later date after this one is declined.">
Skip
@@ -647,6 +662,9 @@
<message name="IDS_AUTOFILL_OFFERS_REMINDER_POSITIVE_BUTTON_LABEL" desc="Label for the positive button for the bubble/infobar shown on the merchant website when an offer is available to use.">
Got it
</message>
+ <message name="IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT" desc="Label for the footer text of the autofill suggestions popup when a user clicks on a promo code field. It redirects the user to the offer webview page for more details on the promo code offers suggested. An example of details the user can see is the terms and conditions of the promo code offers that are shown in the autofill suggestions popup.">
+ See promo code details
+ </message>
<if expr="not is_ios and not is_android">
<message name="IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT" desc="Secondary explanatory text for the Desktop bubble shown on the merchant website when an offer is available to use.">
Pay with <ph name="CARD_DETAIL">$1<ex>Visa - 1234</ex></ph> at checkout
@@ -656,23 +674,38 @@
</message>
</if>
<if expr="is_android">
- <message name="IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT" desc="Secondary explanatory text for the Clank infobar shown on the merchant website when an offer is available to use." formatter_data="android_java">
+ <message name="IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT" desc="Secondary explanatory text for the infobar shown on the merchant website when an offer is available to use." formatter_data="android_java">
Pay with <ph name="CARD_DETAIL">%1$s<ex>Visa - 1234</ex></ph> at checkout.
</message>
<message name="IDS_AUTOFILL_OFFERS_REMINDER_DEEP_LINK_TEXT" desc="Text to be linked to take the user to the offer in the Google Pay app." formatter_data="android_java">
See details
</message>
+ <message name="IDS_AUTOFILL_OFFERS_MESSAGE_TITLE" desc="Title of the message shown on the merchant website when a GPay-activated card linked offer is available to use.">
+ Google Pay offer available
+ </message>
+ <message name="IDS_AUTOFILL_OFFERS_MESSAGE_DESCRIPTION_TEXT" desc="Secondary explanatory text for the message shown on the merchant website when an offer is available to use.">
+ Check out with <ph name="CARD_DETAIL">$1<ex>Visa - 1234</ex></ph> to use offer
+ </message>
+ <message name="IDS_AUTOFILL_OFFERS_MESSAGE_PRIMARY_BUTTON_TEXT" desc="Text for the primary button of the message shown on the merchant website when a GPay-activated card linked offer is available to use.">
+ Details
+ </message>
</if>
<if expr="toolkit_views">
<message name="IDS_AUTOFILL_PROMO_CODE_OFFERS_REMINDER_TITLE" desc="Title of the bubble shown on the merchant website when a merchant promo code offer is available to use.">
Use this code at checkout
</message>
+ <message name="IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_TITLE" desc="Title of the bubble shown on the merchant website when a GPay merchant promo code offer is available to use, and the user can click on the promo code field to autofill it.">
+ Reminder: Saved promo code available
+ </message>
<message name="IDS_AUTOFILL_PROMO_CODE_OFFER_BUTTON_TOOLTIP_NORMAL" desc="The tooltip message for a button containing a copyable promo code in the merchant promo code offer notification bubble on Desktop. This message is shown when the button is hovered over and if the button has not been clicked.">
Click to copy
</message>
<message name="IDS_AUTOFILL_PROMO_CODE_OFFER_BUTTON_TOOLTIP_CLICKED" desc="The tooltip message for a button containing a copyable promo code in the merchant promo code offer notification bubble on Desktop. This message is shown when the button is hovered over and if the button has been clicked.">
Copied to clipboard
</message>
+ <message name="IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_VALUE_PROP_TEXT" desc="The first line of the GPay promo ocde notification bubble, consisting of the value prop text and a see details link">
+ <ph name="VALUE_PROP">$1<ex>5% off on shoes. Up to $50.</ex></ph> <ph name="DETAILS">$2<ex>See Details</ex></ph>
+ </message>
</if>
<!-- Autofill Payments OTP verification dialog -->
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_TITLE.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_TITLE.png.sha1
new file mode 100644
index 00000000000..67750d032df
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_TITLE.png.sha1
@@ -0,0 +1 @@
+ea2b1ecffc0df1c3604c711b29ed085417abeaa3 \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_VALUE_PROP_TEXT.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_VALUE_PROP_TEXT.png.sha1
new file mode 100644
index 00000000000..67750d032df
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_GPAY_PROMO_CODE_OFFERS_REMINDER_VALUE_PROP_TEXT.png.sha1
@@ -0,0 +1 @@
+ea2b1ecffc0df1c3604c711b29ed085417abeaa3 \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MOBILE_SAVE_CARD_TO_CLOUD_CONFIRMATION_DIALOG_EXPLANATION_WITH_ACCOUNT_NAME.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MOBILE_SAVE_CARD_TO_CLOUD_CONFIRMATION_DIALOG_EXPLANATION_WITH_ACCOUNT_NAME.png.sha1
new file mode 100644
index 00000000000..9e040b50722
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MOBILE_SAVE_CARD_TO_CLOUD_CONFIRMATION_DIALOG_EXPLANATION_WITH_ACCOUNT_NAME.png.sha1
@@ -0,0 +1 @@
+44c6e8c0f0fee08842017c5b62b321c78de74b7d \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_DESCRIPTION_TEXT.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_DESCRIPTION_TEXT.png.sha1
new file mode 100644
index 00000000000..bbbd9c4502a
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_DESCRIPTION_TEXT.png.sha1
@@ -0,0 +1 @@
+587b54e3577dbb3f64e126b4d7c01c4480bac10e \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_PRIMARY_BUTTON_TEXT.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_PRIMARY_BUTTON_TEXT.png.sha1
new file mode 100644
index 00000000000..3072d2f3e4e
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_PRIMARY_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+33628376178104d1b7a0adddac1e4bd96995e208 \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_TITLE.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_TITLE.png.sha1
new file mode 100644
index 00000000000..4a9273aae59
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_MESSAGE_TITLE.png.sha1
@@ -0,0 +1 @@
+1f41b89f1889a8e89497daf7d63ac16bf0113bd2 \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT.png.sha1
new file mode 100644
index 00000000000..842d100c3bd
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT.png.sha1
@@ -0,0 +1 @@
+0d7e4b5e0f4c610dc0127919664e5c85e1758211 \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1
new file mode 100644
index 00000000000..bc126ebe9fe
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1
@@ -0,0 +1 @@
+3954965caed137db4c9a5c60753e68ba889416f6 \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1
new file mode 100644
index 00000000000..bdfbe2c7fce
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1
@@ -0,0 +1 @@
+71a20cbaa4eefc2b2b38164e42a2dde78995b5b1 \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1
new file mode 100644
index 00000000000..4cbafd2160c
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_ENCRYPTED_AND_SECURE.png.sha1
@@ -0,0 +1 @@
+a399eb6bfc146b537ed09de6d8d9022736f6930d \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1
new file mode 100644
index 00000000000..03d3e6584aa
--- /dev/null
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_EXPERIMENT_FASTER_AND_PROTECTED.png.sha1
@@ -0,0 +1 @@
+75415291bc382b21eae385d42272a837bc519485 \ No newline at end of file
diff --git a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_DIALOG_CONTENT_LABEL.png.sha1 b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_DIALOG_CONTENT_LABEL.png.sha1
index c82bfa3d1e8..24c1690d3a1 100644
--- a/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_DIALOG_CONTENT_LABEL.png.sha1
+++ b/chromium/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_DIALOG_CONTENT_LABEL.png.sha1
@@ -1 +1 @@
-6d220d5a0e8b42035028e8e06b251e6bfae75e74 \ No newline at end of file
+eeb8b83879e8ba5065b2acee01141f951d95ce29 \ No newline at end of file
diff --git a/chromium/components/autofill_strings.grdp b/chromium/components/autofill_strings.grdp
index a5551ea2377..50b8455c197 100644
--- a/chromium/components/autofill_strings.grdp
+++ b/chromium/components/autofill_strings.grdp
@@ -92,46 +92,6 @@
, '''
</message>
- <!-- These are all variants of the same logical field: The major subdivision below the "country" level. -->
- <message name="IDS_AUTOFILL_FIELD_LABEL_STATE" desc="The label of the State entry.">
- State
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_AREA" desc="The label of the Area entry.">
- Area
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_COUNTY" desc="The label of the County entry.">
- County
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_DEPARTMENT" desc="The label of the Department entry.">
- Department
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_DISTRICT" desc="The label of the District entry.">
- District
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_EMIRATE" desc="The label of the Emirate entry.">
- Emirate
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_ISLAND" desc="The label of the Island entry.">
- Island
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_PARISH" desc="The label of the Parish entry.">
- Parish
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_PREFECTURE" desc="The label of the Prefecture entry.">
- Prefecture
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_PROVINCE" desc="The label of the Province entry.">
- Province
- </message>
- <!-- End state variants. -->
-
- <message name="IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE" desc="The label of the ZIP code entry.">
- ZIP code
- </message>
- <message name="IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE" desc="The label of the Postal code entry.">
- Postal code
- </message>
-
<message name="IDS_AUTOFILL_HIDE_SUGGESTIONS" desc="The text displayed at the Autofill popup to allow the user to hide the suggestions menu." meaning="Hides the suggestion list.">
Hide suggestions
</message>
@@ -140,17 +100,32 @@
Manage...
</message>
- <message name="IDS_AUTOFILL_MANAGE_ADDRESSES" desc="The text displayed at the bottom of the Autofill popup to allow the user to manage their Autofill preferences for addresses. Imperative. When the user clicks on it, opens the Autofill settings page." meaning="Manage addresses in the user profile that can be used by Chrome Autofill.">
- Manage addresses...
- </message>
+ <if expr="not use_titlecase">
+ <message name="IDS_AUTOFILL_MANAGE_ADDRESSES" desc="The text displayed at the bottom of the Autofill popup to allow the user to manage their Autofill preferences for addresses. Imperative. When the user clicks on it, opens the Autofill settings page." meaning="Manage addresses in the user profile that can be used by Chrome Autofill.">
+ Manage addresses...
+ </message>
- <message name="IDS_AUTOFILL_MANAGE_PAYMENT_METHODS" desc="The text displayed at the bottom of the Autofill popup to allow the user to manage their Autofill preferences for payment methods like credit cards/debit cards. Imperative. When the user clicks on it, opens the Autofill settings page." meaning="Manage the payment methods in the user profile that can be used by Chrome Autofill.">
- Manage payment methods...
- </message>
+ <message name="IDS_AUTOFILL_MANAGE_PAYMENT_METHODS" desc="The text displayed at the bottom of the Autofill popup to allow the user to manage their Autofill preferences for payment methods like credit cards/debit cards. Imperative. When the user clicks on it, opens the Autofill settings page." meaning="Manage the payment methods in the user profile that can be used by Chrome Autofill.">
+ Manage payment methods...
+ </message>
- <message name="IDS_AUTOFILL_MANAGE_PASSWORDS" desc="The text displayed at the bottom of the Autofill popup to allow the user to manage passwords that can be used by Chrome Autofill. Imperative. When the user clicks on it, opens the Passwords section of the Settings page." meaning="Manage passwords in the user profile that can be used by Chrome Autofill.">
- Manage passwords...
- </message>
+ <message name="IDS_AUTOFILL_MANAGE_PASSWORDS" desc="The text displayed at the bottom of the Autofill popup to allow the user to manage passwords that can be used by Chrome Autofill. Imperative. When the user clicks on it, opens the Passwords section of the Settings page." meaning="Manage passwords in the user profile that can be used by Chrome Autofill.">
+ Manage passwords...
+ </message>
+ </if>
+ <if expr="use_titlecase">
+ <message name="IDS_AUTOFILL_MANAGE_ADDRESSES" desc="In Title Case: The text displayed at the bottom of the Autofill popup to allow the user to manage their Autofill preferences for addresses. Imperative. When the user clicks on it, opens the Autofill settings page." meaning="Manage addresses in the user profile that can be used by Chrome Autofill.">
+ Manage Addresses...
+ </message>
+
+ <message name="IDS_AUTOFILL_MANAGE_PAYMENT_METHODS" desc="In Title Case: The text displayed at the bottom of the Autofill popup to allow the user to manage their Autofill preferences for payment methods like credit cards/debit cards. Imperative. When the user clicks on it, opens the Autofill settings page." meaning="Manage the payment methods in the user profile that can be used by Chrome Autofill.">
+ Manage Payment Methods...
+ </message>
+
+ <message name="IDS_AUTOFILL_MANAGE_PASSWORDS" desc="In Title Case: The text displayed at the bottom of the Autofill popup to allow the user to manage passwords that can be used by Chrome Autofill. Imperative. When the user clicks on it, opens the Passwords section of the Settings page." meaning="Manage passwords in the user profile that can be used by Chrome Autofill.">
+ Manage Passwords...
+ </message>
+ </if>
<message name="IDS_AUTOFILL_SCAN_CREDIT_CARD" desc="An item in the autofill popup that triggers a new credit card to be scanned using the camera on the device." formatter_data="android_java">
Scan new card
diff --git a/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_ADDRESSES.png.sha1 b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_ADDRESSES.png.sha1
new file mode 100644
index 00000000000..103ebeefbcc
--- /dev/null
+++ b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_ADDRESSES.png.sha1
@@ -0,0 +1 @@
+34422c7d0da142162559ebe94ef835385d5cc5e6 \ No newline at end of file
diff --git a/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_PASSWORDS.png.sha1 b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_PASSWORDS.png.sha1
new file mode 100644
index 00000000000..c96b128806b
--- /dev/null
+++ b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_PASSWORDS.png.sha1
@@ -0,0 +1 @@
+7cf174c28e0e7c92b916543fb27cb48e36c14b82 \ No newline at end of file
diff --git a/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_PAYMENT_METHODS.png.sha1 b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_PAYMENT_METHODS.png.sha1
new file mode 100644
index 00000000000..0e3ce8a7a89
--- /dev/null
+++ b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_MANAGE_PAYMENT_METHODS.png.sha1
@@ -0,0 +1 @@
+9fe038242e20ee472a1ce86773da95acbb51fdca \ No newline at end of file
diff --git a/chromium/components/background_fetch/background_fetch_delegate_base.cc b/chromium/components/background_fetch/background_fetch_delegate_base.cc
index 26f7716ae83..e3994b98074 100644
--- a/chromium/components/background_fetch/background_fetch_delegate_base.cc
+++ b/chromium/components/background_fetch/background_fetch_delegate_base.cc
@@ -179,18 +179,19 @@ void BackgroundFetchDelegateBase::CancelDownload(std::string job_id) {
}
}
-void BackgroundFetchDelegateBase::OnUiFinished(const std::string& job_id,
- bool activated) {
+void BackgroundFetchDelegateBase::OnUiFinished(const std::string& job_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- if (activated) {
- if (auto client = GetClient(job_id))
- client->OnUIActivated(job_id);
- }
job_details_map_.erase(job_id);
DoCleanUpUi(job_id);
}
+void BackgroundFetchDelegateBase::OnUiActivated(const std::string& job_id) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (auto client = GetClient(job_id))
+ client->OnUIActivated(job_id);
+}
+
JobDetails* BackgroundFetchDelegateBase::GetJobDetails(
const std::string& job_id,
bool allow_null) {
@@ -235,7 +236,7 @@ void BackgroundFetchDelegateBase::MarkJobComplete(const std::string& job_id) {
JobDetails* job_details = GetJobDetails(job_id);
if (job_details->job_state == JobDetails::State::kCancelled) {
- OnUiFinished(job_id, /*activated=*/false);
+ OnUiFinished(job_id);
return;
}
diff --git a/chromium/components/background_fetch/background_fetch_delegate_base.h b/chromium/components/background_fetch/background_fetch_delegate_base.h
index b9d509d2fad..55300d5af60 100644
--- a/chromium/components/background_fetch/background_fetch_delegate_base.h
+++ b/chromium/components/background_fetch/background_fetch_delegate_base.h
@@ -105,7 +105,10 @@ class BackgroundFetchDelegateBase : public content::BackgroundFetchDelegate {
// Called when the UI has finished showing. If `activated` is true, it was
// tapped, otherwise it was dismissed.
- void OnUiFinished(const std::string& job_id, bool activated);
+ void OnUiFinished(const std::string& job_id);
+
+ // Called when the UI has been tapped.
+ void OnUiActivated(const std::string& job);
protected:
// Return the download service for `context_`.
diff --git a/chromium/components/background_fetch/job_details.cc b/chromium/components/background_fetch/job_details.cc
index 6873a56f06f..e48575ce7a8 100644
--- a/chromium/components/background_fetch/job_details.cc
+++ b/chromium/components/background_fetch/job_details.cc
@@ -68,6 +68,10 @@ uint64_t JobDetails::GetInProgressBytes() const {
return bytes;
}
+bool JobDetails::IsComplete() const {
+ return job_state == State::kJobComplete;
+}
+
void JobDetails::UpdateInProgressBytes(const std::string& download_guid,
uint64_t bytes_uploaded,
uint64_t bytes_downloaded) {
diff --git a/chromium/components/background_fetch/job_details.h b/chromium/components/background_fetch/job_details.h
index b7e91927e3d..2c04dc3fad3 100644
--- a/chromium/components/background_fetch/job_details.h
+++ b/chromium/components/background_fetch/job_details.h
@@ -49,6 +49,9 @@ struct JobDetails {
// requests.
uint64_t GetDownloadedBytes() const;
+ // Whether the job has finished successfully (not aborted).
+ bool IsComplete() const;
+
void UpdateInProgressBytes(const std::string& download_guid,
uint64_t bytes_uploaded,
uint64_t bytes_downloaded);
diff --git a/chromium/components/background_sync/background_sync_controller_impl.cc b/chromium/components/background_sync/background_sync_controller_impl.cc
index 2d5041cd8b7..f6901609830 100644
--- a/chromium/components/background_sync/background_sync_controller_impl.cc
+++ b/chromium/components/background_sync/background_sync_controller_impl.cc
@@ -114,17 +114,17 @@ void BackgroundSyncControllerImpl::GetParameterOverrides(
if (!variations::GetVariationParams(kFieldTrialName, &field_params))
return;
- if (base::LowerCaseEqualsASCII(field_params[kDisabledParameterName],
- "true")) {
+ if (base::EqualsCaseInsensitiveASCII(field_params[kDisabledParameterName],
+ "true")) {
parameters->disable = true;
}
- if (base::LowerCaseEqualsASCII(field_params[kKeepBrowserAwakeParameterName],
- "true")) {
+ if (base::EqualsCaseInsensitiveASCII(
+ field_params[kKeepBrowserAwakeParameterName], "true")) {
parameters->keep_browser_awake_till_events_complete = true;
}
- if (base::LowerCaseEqualsASCII(
+ if (base::EqualsCaseInsensitiveASCII(
field_params[kSkipPermissionsCheckParameterName], "true")) {
parameters->skip_permissions_check_for_testing = true;
}
@@ -195,8 +195,8 @@ void BackgroundSyncControllerImpl::GetParameterOverrides(
if (delegate_->ShouldDisableAndroidNetworkDetection()) {
parameters->rely_on_android_network_detection = false;
} else if (base::Contains(field_params, kRelyOnAndroidNetworkDetection)) {
- if (base::LowerCaseEqualsASCII(field_params[kRelyOnAndroidNetworkDetection],
- "true")) {
+ if (base::EqualsCaseInsensitiveASCII(
+ field_params[kRelyOnAndroidNetworkDetection], "true")) {
parameters->rely_on_android_network_detection = true;
}
}
diff --git a/chromium/components/background_sync/background_sync_permission_context.cc b/chromium/components/background_sync/background_sync_permission_context.cc
index db276949852..ce22734d970 100644
--- a/chromium/components/background_sync/background_sync_permission_context.cc
+++ b/chromium/components/background_sync/background_sync_permission_context.cc
@@ -16,7 +16,6 @@ BackgroundSyncPermissionContext::BackgroundSyncPermissionContext(
}
void BackgroundSyncPermissionContext::DecidePermission(
- content::WebContents* web_contents,
const permissions::PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
diff --git a/chromium/components/background_sync/background_sync_permission_context.h b/chromium/components/background_sync/background_sync_permission_context.h
index c1c60fd2d42..350118c8ace 100644
--- a/chromium/components/background_sync/background_sync_permission_context.h
+++ b/chromium/components/background_sync/background_sync_permission_context.h
@@ -29,7 +29,6 @@ class BackgroundSyncPermissionContext
private:
// PermissionContextBase:
void DecidePermission(
- content::WebContents* web_contents,
const permissions::PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
diff --git a/chromium/components/background_sync/background_sync_permission_context_unittest.cc b/chromium/components/background_sync/background_sync_permission_context_unittest.cc
index 5f192791f98..341a4fb9798 100644
--- a/chromium/components/background_sync/background_sync_permission_context_unittest.cc
+++ b/chromium/components/background_sync/background_sync_permission_context_unittest.cc
@@ -43,11 +43,11 @@ class BackgroundSyncPermissionContextTest
base::RunLoop run_loop;
const permissions::PermissionRequestID id(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
permissions::PermissionRequestID::RequestLocalId());
permission_context->RequestPermission(
- web_contents(), id, url, /* user_gesture= */ false,
+ id, url, /* user_gesture= */ false,
base::BindOnce(
&BackgroundSyncPermissionContextTest::TrackPermissionDecision,
base::Unretained(this), run_loop.QuitClosure()));
diff --git a/chromium/components/background_task_scheduler/internal/BUILD.gn b/chromium/components/background_task_scheduler/internal/BUILD.gn
index 05cf1f1a2c7..1a63bcb3b52 100644
--- a/chromium/components/background_task_scheduler/internal/BUILD.gn
+++ b/chromium/components/background_task_scheduler/internal/BUILD.gn
@@ -32,6 +32,7 @@ if (is_android) {
"$google_play_services_package:google_play_services_gcm_java",
"$google_play_services_package:google_play_services_tasks_java",
"//base:base_java",
+ "//build/android:build_java",
"//components/background_task_scheduler:background_task_scheduler_task_ids_java",
"//components/background_task_scheduler:public_java",
"//content/public/android:content_main_dex_java",
@@ -71,9 +72,9 @@ if (is_android) {
deps = [
"//base:base_java",
+ "//base:jni_java",
"//components/background_task_scheduler:factory_java",
"//components/background_task_scheduler:public_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
]
}
@@ -102,7 +103,6 @@ if (is_android) {
"$google_play_services_package:google_play_services_basement_java",
"$google_play_services_package:google_play_services_gcm_java",
"$google_play_services_package:google_play_services_tasks_java",
- "//base:base_java",
"//base:base_java_test_support",
"//components/background_task_scheduler:background_task_scheduler_task_ids_java",
"//components/background_task_scheduler:public_java",
diff --git a/chromium/components/blocked_content/popup_blocker_tab_helper.cc b/chromium/components/blocked_content/popup_blocker_tab_helper.cc
index 03529793510..111264a5b54 100644
--- a/chromium/components/blocked_content/popup_blocker_tab_helper.cc
+++ b/chromium/components/blocked_content/popup_blocker_tab_helper.cc
@@ -76,7 +76,7 @@ void PopupBlockerTabHelper::DidFinishNavigation(
void PopupBlockerTabHelper::HidePopupNotification() {
auto* pscs = content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
if (pscs)
pscs->ClearPopupsBlocked();
}
@@ -96,7 +96,7 @@ void PopupBlockerTabHelper::AddBlockedPopup(
auto* content_settings =
content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
if (content_settings) {
content_settings->OnContentBlocked(ContentSettingsType::POPUPS);
}
diff --git a/chromium/components/blocked_content/popup_blocker_tab_helper_unittest.cc b/chromium/components/blocked_content/popup_blocker_tab_helper_unittest.cc
index a8cf87f8506..547f2feccef 100644
--- a/chromium/components/blocked_content/popup_blocker_tab_helper_unittest.cc
+++ b/chromium/components/blocked_content/popup_blocker_tab_helper_unittest.cc
@@ -159,7 +159,7 @@ TEST_F(PopupBlockerTabHelperTest, DoesNotShowPopupWithInvalidID) {
TEST_F(PopupBlockerTabHelperTest, SetsContentSettingsPopupState) {
auto* content_settings =
content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::POPUPS));
TestPopupNavigationDelegate::ResultHolder result;
@@ -186,12 +186,12 @@ TEST_F(PopupBlockerTabHelperTest, ClearsContentSettingsPopupStateOnNavigation) {
std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result),
blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
EXPECT_TRUE(content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents()->GetMainFrame())
+ web_contents()->GetPrimaryMainFrame())
->IsContentBlocked(ContentSettingsType::POPUPS));
NavigateAndCommit(GURL(kUrl2));
EXPECT_FALSE(content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents()->GetMainFrame())
+ web_contents()->GetPrimaryMainFrame())
->IsContentBlocked(ContentSettingsType::POPUPS));
}
@@ -202,24 +202,24 @@ TEST_F(PopupBlockerTabHelperTest,
std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result),
blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
EXPECT_TRUE(content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents()->GetMainFrame())
+ web_contents()->GetPrimaryMainFrame())
->IsContentBlocked(ContentSettingsType::POPUPS));
// Navigating a non-primary main frame shoudn't clear the popups.
content::MockNavigationHandle handle(GURL(kUrl2),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
handle.set_has_committed(true);
handle.set_is_in_primary_main_frame(false);
helper()->DidFinishNavigation(&handle);
EXPECT_TRUE(content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents()->GetMainFrame())
+ web_contents()->GetPrimaryMainFrame())
->IsContentBlocked(ContentSettingsType::POPUPS));
// Navigating the primary main frame should clear the popups.
handle.set_is_in_primary_main_frame(true);
helper()->DidFinishNavigation(&handle);
EXPECT_FALSE(content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents()->GetMainFrame())
+ web_contents()->GetPrimaryMainFrame())
->IsContentBlocked(ContentSettingsType::POPUPS));
}
diff --git a/chromium/components/blocked_content/popup_opener_tab_helper.cc b/chromium/components/blocked_content/popup_opener_tab_helper.cc
index c09d054d577..fbe1c904014 100644
--- a/chromium/components/blocked_content/popup_opener_tab_helper.cc
+++ b/chromium/components/blocked_content/popup_opener_tab_helper.cc
@@ -13,7 +13,6 @@
#include "components/blocked_content/popup_tracker.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "services/metrics/public/cpp/metrics_utils.h"
@@ -103,7 +102,7 @@ void PopupOpenerTabHelper::MaybeLogPagePopupContentSettings() {
return;
const ukm::SourceId source_id =
- ukm::GetSourceIdForWebContentsDocument(web_contents());
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId();
// Do not record duplicate Popup.Page events for popups opened in succession
// from the same opener.
diff --git a/chromium/components/blocked_content/popup_tracker.cc b/chromium/components/blocked_content/popup_tracker.cc
index 6ebb1ac30d2..a3577dce8ae 100644
--- a/chromium/components/blocked_content/popup_tracker.cc
+++ b/chromium/components/blocked_content/popup_tracker.cc
@@ -11,7 +11,6 @@
#include "base/metrics/histogram_macros.h"
#include "base/time/default_tick_clock.h"
#include "components/blocked_content/popup_opener_tab_helper.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "services/metrics/public/cpp/metrics_utils.h"
@@ -51,7 +50,7 @@ PopupTracker::PopupTracker(content::WebContents* contents,
visibility_tracker_(
base::DefaultTickClock::GetInstance(),
contents->GetVisibility() != content::Visibility::HIDDEN),
- opener_source_id_(ukm::GetSourceIdForWebContentsDocument(opener)),
+ opener_source_id_(opener->GetPrimaryMainFrame()->GetPageUkmSourceId()),
window_open_disposition_(disposition) {
if (auto* popup_opener = PopupOpenerTabHelper::FromWebContents(opener))
popup_opener->OnOpenedPopup(this);
diff --git a/chromium/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/chromium/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index 213248b961a..28bacee8046 100644
--- a/chromium/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/chromium/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -192,7 +192,7 @@ TEST_F(SafeBrowsingTriggeredPopupBlockerTest,
for (const auto& test_case : kTestCases) {
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(
- test_case.initial_url, web_contents()->GetMainFrame());
+ test_case.initial_url, web_contents()->GetPrimaryMainFrame());
simulator->Start();
simulator->Redirect(test_case.redirect_url);
simulator->Commit();
@@ -587,9 +587,10 @@ TEST_F(SafeBrowsingTriggeredPopupBlockerFencedFrameTest,
const GURL fenced_frame_url("https://fencedframe.test");
MarkUrlAsAbusiveEnforce(fenced_frame_url);
std::unique_ptr<content::NavigationSimulator> navigation_simulator =
- content::NavigationSimulator::CreateForFencedFrame(fenced_frame_url,
- fenced_frame_root);
+ content::NavigationSimulator::CreateRendererInitiated(fenced_frame_url,
+ fenced_frame_root);
navigation_simulator->Commit();
+ fenced_frame_root = navigation_simulator->GetFinalRenderFrameHost();
// The popup blocker is not triggered for a fenced frame.
EXPECT_FALSE(popup_blocker()->ShouldApplyAbusivePopupBlocker(
diff --git a/chromium/components/blocklist/OWNERS b/chromium/components/blocklist/OWNERS
index 2783dea1c6e..c7682a1bf6b 100644
--- a/chromium/components/blocklist/OWNERS
+++ b/chromium/components/blocklist/OWNERS
@@ -1 +1 @@
-file://components/data_reduction_proxy/OWNERS
+file://chrome/browser/data_saver/OWNERS
diff --git a/chromium/components/bookmarks/browser/bookmark_load_details.cc b/chromium/components/bookmarks/browser/bookmark_load_details.cc
index f1b9c528deb..d1bed0cbd91 100644
--- a/chromium/components/bookmarks/browser/bookmark_load_details.cc
+++ b/chromium/components/bookmarks/browser/bookmark_load_details.cc
@@ -13,7 +13,8 @@ namespace bookmarks {
BookmarkLoadDetails::BookmarkLoadDetails(BookmarkClient* client)
: load_managed_node_callback_(client->GetLoadManagedNodeCallback()),
- index_(std::make_unique<TitledUrlIndex>()) {
+ index_(std::make_unique<TitledUrlIndex>()),
+ load_start_(base::TimeTicks::Now()) {
// WARNING: do NOT add |client| as a member. Much of this code runs on another
// thread, and |client_| is not thread safe, and/or may be destroyed before
// this.
diff --git a/chromium/components/bookmarks/browser/bookmark_load_details.h b/chromium/components/bookmarks/browser/bookmark_load_details.h
index 0c1ff5e5db9..830826eaa7c 100644
--- a/chromium/components/bookmarks/browser/bookmark_load_details.h
+++ b/chromium/components/bookmarks/browser/bookmark_load_details.h
@@ -13,6 +13,10 @@
#include "components/bookmarks/browser/bookmark_client.h"
#include "components/bookmarks/browser/bookmark_node.h"
+namespace base {
+class TimeTicks;
+}
+
namespace bookmarks {
class BookmarkPermanentNode;
@@ -91,6 +95,8 @@ class BookmarkLoadDetails {
void CreateUrlIndex();
UrlIndex* url_index() { return url_index_.get(); }
+ base::TimeTicks load_start() { return load_start_; }
+
private:
std::unique_ptr<BookmarkNode> root_node_;
raw_ptr<BookmarkNode> root_node_ptr_;
@@ -108,6 +114,7 @@ class BookmarkLoadDetails {
scoped_refptr<UrlIndex> url_index_;
// A string blob represetning the sync metadata stored in the json file.
std::string sync_metadata_str_;
+ base::TimeTicks load_start_;
};
} // namespace bookmarks
diff --git a/chromium/components/bookmarks/browser/bookmark_model.cc b/chromium/components/bookmarks/browser/bookmark_model.cc
index 8dc727bab13..66b8d1b80f6 100644
--- a/chromium/components/bookmarks/browser/bookmark_model.cc
+++ b/chromium/components/bookmarks/browser/bookmark_model.cc
@@ -34,6 +34,7 @@
#include "components/bookmarks/browser/url_and_title.h"
#include "components/bookmarks/browser/url_index.h"
#include "components/bookmarks/common/bookmark_constants.h"
+#include "components/bookmarks/common/bookmark_metrics.h"
#include "components/favicon_base/favicon_types.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
@@ -823,6 +824,10 @@ void BookmarkModel::DoneLoading(std::unique_ptr<BookmarkLoadDetails> details) {
base::Unretained(store_.get()))
: base::DoNothing());
+ const base::TimeDelta load_duration =
+ base::TimeTicks::Now() - details->load_start();
+ metrics::RecordTimeToLoadAtStartup(load_duration);
+
// Notify our direct observers.
for (BookmarkModelObserver& observer : observers_)
observer.BookmarkModelLoaded(this, details->ids_reassigned());
diff --git a/chromium/components/bookmarks/browser/bookmark_storage.cc b/chromium/components/bookmarks/browser/bookmark_storage.cc
index 0ff4aeeb2fa..915b9678460 100644
--- a/chromium/components/bookmarks/browser/bookmark_storage.cc
+++ b/chromium/components/bookmarks/browser/bookmark_storage.cc
@@ -23,6 +23,7 @@
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/bookmarks/common/bookmark_constants.h"
+#include "components/bookmarks/common/bookmark_metrics.h"
namespace bookmarks {
@@ -50,7 +51,8 @@ BookmarkStorage::BookmarkStorage(BookmarkModel* model,
writer_(profile_path.Append(kBookmarksFileName),
backend_task_runner_,
kSaveDelay,
- "BookmarkStorage") {}
+ "BookmarkStorage"),
+ last_scheduled_save_(base::TimeTicks::Now()) {}
BookmarkStorage::~BookmarkStorage() {
if (writer_.HasPendingWrite())
@@ -67,6 +69,11 @@ void BookmarkStorage::ScheduleSave() {
}
writer_.ScheduleWriteWithBackgroundDataSerializer(this);
+
+ const base::TimeDelta schedule_delta =
+ base::TimeTicks::Now() - last_scheduled_save_;
+ metrics::RecordTimeSinceLastScheduledSave(schedule_delta);
+ last_scheduled_save_ = base::TimeTicks::Now();
}
void BookmarkStorage::BookmarkModelDeleted() {
diff --git a/chromium/components/bookmarks/browser/bookmark_storage.h b/chromium/components/bookmarks/browser/bookmark_storage.h
index 6bd25c8eae1..e85825859cd 100644
--- a/chromium/components/bookmarks/browser/bookmark_storage.h
+++ b/chromium/components/bookmarks/browser/bookmark_storage.h
@@ -89,6 +89,9 @@ class BookmarkStorage
// The state of the backup file creation which is created lazily just before
// the first scheduled save.
bool backup_triggered_ = false;
+
+ // Used to track the frequency of saves starting from the first save.
+ base::TimeTicks last_scheduled_save_;
};
} // namespace bookmarks
diff --git a/chromium/components/bookmarks/browser/bookmark_storage_unittest.cc b/chromium/components/bookmarks/browser/bookmark_storage_unittest.cc
index e03c2e9016b..c20ec55aa08 100644
--- a/chromium/components/bookmarks/browser/bookmark_storage_unittest.cc
+++ b/chromium/components/bookmarks/browser/bookmark_storage_unittest.cc
@@ -9,6 +9,7 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "components/bookmarks/browser/bookmark_model.h"
@@ -31,6 +32,7 @@ std::unique_ptr<BookmarkModel> CreateModelWithOneBookmark() {
} // namespace
TEST(BookmarkStorageTest, ShouldSaveFileToDiskAfterDelay) {
+ base::HistogramTester histogram_tester;
std::unique_ptr<BookmarkModel> model = CreateModelWithOneBookmark();
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
@@ -56,6 +58,8 @@ TEST(BookmarkStorageTest, ShouldSaveFileToDiskAfterDelay) {
task_environment.FastForwardBy(base::Milliseconds(20));
EXPECT_FALSE(storage.HasScheduledSaveForTesting());
EXPECT_TRUE(base::PathExists(temp_dir.GetPath().Append(kBookmarksFileName)));
+ histogram_tester.ExpectTotalCount(
+ "Bookmarks.Storage.TimeSinceLastScheduledSave", 1);
}
TEST(BookmarkStorageTest, ShouldSaveFileDespiteShutdownWhileScheduled) {
@@ -115,4 +119,36 @@ TEST(BookmarkStorageTest, ShouldGenerateBackupFileUponFirstSave) {
EXPECT_FALSE(base::PathExists(backup_file_path));
}
+TEST(BookmarkStorageTest, RecordTimeSinceLastScheduledSave) {
+ base::HistogramTester histogram_tester;
+ std::unique_ptr<BookmarkModel> model = CreateModelWithOneBookmark();
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ base::test::TaskEnvironment task_environment{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ BookmarkStorage storage(model.get(), temp_dir.GetPath());
+
+ ASSERT_FALSE(storage.HasScheduledSaveForTesting());
+ ASSERT_FALSE(base::PathExists(temp_dir.GetPath().Append(kBookmarksFileName)));
+
+ storage.ScheduleSave();
+
+ base::TimeDelta delay_ms = base::Milliseconds(10);
+ // Advance clock until immediately before saving takes place.
+ task_environment.FastForwardBy(delay_ms);
+ storage.ScheduleSave();
+ EXPECT_TRUE(storage.HasScheduledSaveForTesting());
+ EXPECT_FALSE(base::PathExists(temp_dir.GetPath().Append(kBookmarksFileName)));
+
+ // Advance clock past the saving moment.
+ task_environment.FastForwardBy(BookmarkStorage::kSaveDelay + delay_ms);
+ EXPECT_FALSE(storage.HasScheduledSaveForTesting());
+ EXPECT_TRUE(base::PathExists(temp_dir.GetPath().Append(kBookmarksFileName)));
+ histogram_tester.ExpectTotalCount(
+ "Bookmarks.Storage.TimeSinceLastScheduledSave", 2);
+ histogram_tester.ExpectTimeBucketCount(
+ "Bookmarks.Storage.TimeSinceLastScheduledSave", delay_ms, 1);
+}
+
} // namespace bookmarks
diff --git a/chromium/components/bookmarks/browser/bookmark_utils.cc b/chromium/components/bookmarks/browser/bookmark_utils.cc
index 651dc39ca8e..3e9c55300d1 100644
--- a/chromium/components/bookmarks/browser/bookmark_utils.cc
+++ b/chromium/components/bookmarks/browser/bookmark_utils.cc
@@ -429,7 +429,7 @@ bool DoesBookmarkContainWords(const std::u16string& title,
DoesBookmarkTextContainWords(base::UTF8ToUTF16(url.spec()), words) ||
DoesBookmarkTextContainWords(
url_formatter::FormatUrl(url, url_formatter::kFormatUrlOmitNothing,
- net::UnescapeRule::NORMAL, nullptr,
+ base::UnescapeRule::NORMAL, nullptr,
nullptr, nullptr),
words);
}
@@ -522,8 +522,8 @@ std::u16string CleanUpUrlForMatching(
return base::i18n::ToLower(url_formatter::FormatUrlWithAdjustments(
GURL(TruncateUrl(gurl.spec())),
url_formatter::kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::SPACES | net::UnescapeRule::PATH_SEPARATORS |
- net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+ base::UnescapeRule::SPACES | base::UnescapeRule::PATH_SEPARATORS |
+ base::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
nullptr, nullptr, adjustments ? adjustments : &tmp_adjustments));
}
diff --git a/chromium/components/bookmarks/browser/model_loader.cc b/chromium/components/bookmarks/browser/model_loader.cc
index ade292fcb20..eb8a7492f4e 100644
--- a/chromium/components/bookmarks/browser/model_loader.cc
+++ b/chromium/components/bookmarks/browser/model_loader.cc
@@ -10,6 +10,7 @@
#include "base/json/json_file_value_serializer.h"
#include "base/json/json_reader.h"
#include "base/metrics/histogram_functions.h"
+#include "base/numerics/clamped_math.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
@@ -17,6 +18,8 @@
#include "components/bookmarks/browser/bookmark_load_details.h"
#include "components/bookmarks/browser/titled_url_index.h"
#include "components/bookmarks/browser/url_index.h"
+#include "components/bookmarks/common/bookmark_metrics.h"
+#include "components/bookmarks/common/url_load_stats.h"
namespace bookmarks {
@@ -79,52 +82,13 @@ void LoadBookmarks(const base::FilePath& path,
details->CreateUrlIndex();
- UrlIndex::Stats stats = details->url_index()->ComputeStats();
+ UrlLoadStats stats = details->url_index()->ComputeStats();
+ metrics::RecordUrlLoadStatsOnProfileLoad(stats);
- DCHECK_LE(stats.duplicate_url_bookmark_count, stats.total_url_bookmark_count);
- DCHECK_LE(stats.duplicate_url_and_title_bookmark_count,
- stats.duplicate_url_bookmark_count);
- DCHECK_LE(stats.duplicate_url_and_title_and_parent_bookmark_count,
- stats.duplicate_url_and_title_bookmark_count);
-
- base::UmaHistogramCounts100000(
- "Bookmarks.Count.OnProfileLoad",
- base::saturated_cast<int>(stats.total_url_bookmark_count));
-
- if (stats.duplicate_url_bookmark_count != 0) {
- base::UmaHistogramCounts100000(
- "Bookmarks.Count.OnProfileLoad.DuplicateUrl2",
- base::saturated_cast<int>(stats.duplicate_url_bookmark_count));
- }
-
- if (stats.duplicate_url_and_title_bookmark_count != 0) {
- base::UmaHistogramCounts100000(
- "Bookmarks.Count.OnProfileLoad.DuplicateUrlAndTitle",
- base::saturated_cast<int>(
- stats.duplicate_url_and_title_bookmark_count));
+ int64_t file_size_bytes;
+ if (bookmark_file_exists && base::GetFileSize(path, &file_size_bytes)) {
+ metrics::RecordFileSizeAtStartup(file_size_bytes);
}
-
- if (stats.duplicate_url_and_title_and_parent_bookmark_count != 0) {
- base::UmaHistogramCounts100000(
- "Bookmarks.Count.OnProfileLoad.DuplicateUrlAndTitleAndParent",
- base::saturated_cast<int>(
- stats.duplicate_url_and_title_and_parent_bookmark_count));
- }
-
- // Log derived metrics for convenience.
- base::UmaHistogramCounts100000(
- "Bookmarks.Count.OnProfileLoad.UniqueUrl",
- base::saturated_cast<int>(stats.total_url_bookmark_count -
- stats.duplicate_url_bookmark_count));
- base::UmaHistogramCounts100000(
- "Bookmarks.Count.OnProfileLoad.UniqueUrlAndTitle",
- base::saturated_cast<int>(stats.total_url_bookmark_count -
- stats.duplicate_url_and_title_bookmark_count));
- base::UmaHistogramCounts100000(
- "Bookmarks.Count.OnProfileLoad.UniqueUrlAndTitleAndParent",
- base::saturated_cast<int>(
- stats.total_url_bookmark_count -
- stats.duplicate_url_and_title_and_parent_bookmark_count));
}
} // namespace
diff --git a/chromium/components/bookmarks/browser/url_index.cc b/chromium/components/bookmarks/browser/url_index.cc
index 10b11c7dcfa..033f5a5e317 100644
--- a/chromium/components/bookmarks/browser/url_index.cc
+++ b/chromium/components/bookmarks/browser/url_index.cc
@@ -9,6 +9,7 @@
#include "base/containers/adapters.h"
#include "base/guid.h"
#include "components/bookmarks/browser/url_and_title.h"
+#include "components/bookmarks/common/url_load_stats.h"
namespace bookmarks {
@@ -23,7 +24,7 @@ namespace {
// calling site.
void AddStatsForBookmarksWithSameUrl(
std::vector<const BookmarkNode*>* bookmarks_with_same_url,
- UrlIndex::Stats* stats) {
+ UrlLoadStats* stats) {
if (bookmarks_with_same_url->size() <= 1)
return;
@@ -126,9 +127,9 @@ bool UrlIndex::HasBookmarks() const {
return !nodes_ordered_by_url_set_.empty();
}
-UrlIndex::Stats UrlIndex::ComputeStats() const {
+UrlLoadStats UrlIndex::ComputeStats() const {
base::AutoLock url_lock(url_lock_);
- UrlIndex::Stats stats;
+ UrlLoadStats stats;
stats.total_url_bookmark_count = nodes_ordered_by_url_set_.size();
if (stats.total_url_bookmark_count <= 1)
diff --git a/chromium/components/bookmarks/browser/url_index.h b/chromium/components/bookmarks/browser/url_index.h
index 5f1c0dca6ef..e31877053c2 100644
--- a/chromium/components/bookmarks/browser/url_index.h
+++ b/chromium/components/bookmarks/browser/url_index.h
@@ -19,6 +19,7 @@ namespace bookmarks {
class BookmarkNode;
+struct UrlLoadStats;
struct UrlAndTitle;
// UrlIndex maintains the bookmark nodes of type url. The nodes are ordered by
@@ -67,24 +68,8 @@ class UrlIndex : public HistoryBookmarkModel {
// Returns true if there is at least one bookmark.
bool HasBookmarks() const;
- // Returns some stats about number of URL bookmarks stored, for UMA purposes.
- struct Stats {
- // Number of bookmark in the index excluding folders.
- size_t total_url_bookmark_count = 0;
- // Number of bookmarks (excluding folders) with a URL that is used by at
- // least one other bookmark, excluding one bookmark per unique URL (i.e. all
- // except one are considered duplicates).
- size_t duplicate_url_bookmark_count = 0;
- // Number of bookmarks (excluding folders) with the pair <URL, title> that
- // is used by at least one other bookmark, excluding one bookmark per unique
- // URL (i.e. all except one are considered duplicates).
- size_t duplicate_url_and_title_bookmark_count = 0;
- // Number of bookmarks (excluding folders) with the triple <URL, title,
- // parent> that is used by at least one other bookmark, excluding one
- // bookmark per unique URL (i.e. all except one are considered duplicates).
- size_t duplicate_url_and_title_and_parent_bookmark_count = 0;
- };
- Stats ComputeStats() const;
+ // Compute stats from the load.
+ UrlLoadStats ComputeStats() const;
// HistoryBookmarkModel:
bool IsBookmarked(const GURL& url) override;
diff --git a/chromium/components/bookmarks/common/BUILD.gn b/chromium/components/bookmarks/common/BUILD.gn
index 5985827a7e4..2f414f9516f 100644
--- a/chromium/components/bookmarks/common/BUILD.gn
+++ b/chromium/components/bookmarks/common/BUILD.gn
@@ -6,8 +6,11 @@ static_library("common") {
sources = [
"bookmark_constants.cc",
"bookmark_constants.h",
+ "bookmark_metrics.cc",
+ "bookmark_metrics.h",
"bookmark_pref_names.cc",
"bookmark_pref_names.h",
+ "url_load_stats.h",
]
deps = [ "//base" ]
diff --git a/chromium/components/bookmarks/common/android/BUILD.gn b/chromium/components/bookmarks/common/android/BUILD.gn
index 8b1a38c835e..7423d635e14 100644
--- a/chromium/components/bookmarks/common/android/BUILD.gn
+++ b/chromium/components/bookmarks/common/android/BUILD.gn
@@ -18,7 +18,7 @@ source_set("android") {
android_library("bookmarks_java") {
deps = [
- "//base:base_java",
+ "//base:jni_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
srcjar_deps = [ ":bookmark_type_javagen" ]
diff --git a/chromium/components/bookmarks/common/bookmark_metrics.cc b/chromium/components/bookmarks/common/bookmark_metrics.cc
new file mode 100644
index 00000000000..40f6ef0f55c
--- /dev/null
+++ b/chromium/components/bookmarks/common/bookmark_metrics.cc
@@ -0,0 +1,77 @@
+// Copyright 2022 The Chromium 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/bookmarks/common/bookmark_metrics.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "components/bookmarks/common/url_load_stats.h"
+
+namespace {
+const int kBytesPerKB = 1024;
+}
+
+namespace bookmarks::metrics {
+
+void RecordTimeSinceLastScheduledSave(base::TimeDelta delta) {
+ UmaHistogramLongTimes("Bookmarks.Storage.TimeSinceLastScheduledSave", delta);
+}
+
+void RecordTimeToLoadAtStartup(base::TimeDelta delta) {
+ UmaHistogramTimes("Bookmarks.Storage.TimeToLoadAtStartup", delta);
+}
+
+void RecordFileSizeAtStartup(int64_t total_bytes) {
+ int total_size_kb = base::saturated_cast<int>(total_bytes / kBytesPerKB);
+ base::UmaHistogramCounts1M("Bookmarks.Storage.FileSizeAtStartup",
+ total_size_kb);
+}
+
+void RecordUrlLoadStatsOnProfileLoad(const UrlLoadStats& stats) {
+ DCHECK_LE(stats.duplicate_url_bookmark_count, stats.total_url_bookmark_count);
+ DCHECK_LE(stats.duplicate_url_and_title_bookmark_count,
+ stats.duplicate_url_bookmark_count);
+ DCHECK_LE(stats.duplicate_url_and_title_and_parent_bookmark_count,
+ stats.duplicate_url_and_title_bookmark_count);
+
+ base::UmaHistogramCounts100000(
+ "Bookmarks.Count.OnProfileLoad",
+ base::saturated_cast<int>(stats.total_url_bookmark_count));
+
+ if (stats.duplicate_url_bookmark_count != 0) {
+ base::UmaHistogramCounts100000(
+ "Bookmarks.Count.OnProfileLoad.DuplicateUrl2",
+ base::saturated_cast<int>(stats.duplicate_url_bookmark_count));
+ }
+
+ if (stats.duplicate_url_and_title_bookmark_count != 0) {
+ base::UmaHistogramCounts100000(
+ "Bookmarks.Count.OnProfileLoad.DuplicateUrlAndTitle",
+ base::saturated_cast<int>(
+ stats.duplicate_url_and_title_bookmark_count));
+ }
+
+ if (stats.duplicate_url_and_title_and_parent_bookmark_count != 0) {
+ base::UmaHistogramCounts100000(
+ "Bookmarks.Count.OnProfileLoad.DuplicateUrlAndTitleAndParent",
+ base::saturated_cast<int>(
+ stats.duplicate_url_and_title_and_parent_bookmark_count));
+ }
+
+ // Log derived metrics for convenience.
+ base::UmaHistogramCounts100000(
+ "Bookmarks.Count.OnProfileLoad.UniqueUrl",
+ base::saturated_cast<int>(stats.total_url_bookmark_count -
+ stats.duplicate_url_bookmark_count));
+ base::UmaHistogramCounts100000(
+ "Bookmarks.Count.OnProfileLoad.UniqueUrlAndTitle",
+ base::saturated_cast<int>(stats.total_url_bookmark_count -
+ stats.duplicate_url_and_title_bookmark_count));
+ base::UmaHistogramCounts100000(
+ "Bookmarks.Count.OnProfileLoad.UniqueUrlAndTitleAndParent",
+ base::saturated_cast<int>(
+ stats.total_url_bookmark_count -
+ stats.duplicate_url_and_title_and_parent_bookmark_count));
+}
+
+} // namespace bookmarks::metrics \ No newline at end of file
diff --git a/chromium/components/bookmarks/common/bookmark_metrics.h b/chromium/components/bookmarks/common/bookmark_metrics.h
new file mode 100644
index 00000000000..bd2f05b5ef3
--- /dev/null
+++ b/chromium/components/bookmarks/common/bookmark_metrics.h
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_BOOKMARKS_COMMON_BOOKMARK_METRICS_H_
+#define COMPONENTS_BOOKMARKS_COMMON_BOOKMARK_METRICS_H_
+
+#include "base/time/time.h"
+
+namespace bookmarks {
+
+struct UrlLoadStats;
+
+namespace metrics {
+
+// Records the time since the last save with a 1 hour max. The first save will
+// record the time since startup.
+void RecordTimeSinceLastScheduledSave(base::TimeDelta delta);
+
+// Records the time it takes to load the bookmark model on startup with a 10
+// second max, the time starts when BookmarkModel.Load is called.
+void RecordTimeToLoadAtStartup(base::TimeDelta delta);
+
+// Records size of the bookmark file at startup.
+void RecordFileSizeAtStartup(int64_t total_bytes);
+
+// Records the metrics derived from `stats`. Recording happens on profile load.
+void RecordUrlLoadStatsOnProfileLoad(const UrlLoadStats& stats);
+
+} // namespace metrics
+
+} // namespace bookmarks
+
+#endif // COMPONENTS_BOOKMARKS_COMMON_BOOKMARK_METRICS_H_ \ No newline at end of file
diff --git a/chromium/components/bookmarks/common/url_load_stats.h b/chromium/components/bookmarks/common/url_load_stats.h
new file mode 100644
index 00000000000..a3964cd5248
--- /dev/null
+++ b/chromium/components/bookmarks/common/url_load_stats.h
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_BOOKMARKS_COMMON_URL_LOAD_STATS_H_
+#define COMPONENTS_BOOKMARKS_COMMON_URL_LOAD_STATS_H_
+
+namespace bookmarks {
+
+// Returns some stats about number of URL bookmarks stored, for UMA purposes.
+struct UrlLoadStats {
+ // Number of bookmark in the index excluding folders.
+ size_t total_url_bookmark_count = 0;
+ // Number of bookmarks (excluding folders) with a URL that is used by at
+ // least one other bookmark, excluding one bookmark per unique URL (i.e. all
+ // except one are considered duplicates).
+ size_t duplicate_url_bookmark_count = 0;
+ // Number of bookmarks (excluding folders) with the pair <URL, title> that
+ // is used by at least one other bookmark, excluding one bookmark per unique
+ // URL (i.e. all except one are considered duplicates).
+ size_t duplicate_url_and_title_bookmark_count = 0;
+ // Number of bookmarks (excluding folders) with the triple <URL, title,
+ // parent> that is used by at least one other bookmark, excluding one
+ // bookmark per unique URL (i.e. all except one are considered duplicates).
+ size_t duplicate_url_and_title_and_parent_bookmark_count = 0;
+};
+
+} // namespace bookmarks
+
+#endif // COMPONENTS_BOOKMARKS_COMMON_URL_LOAD_STATS_H_ \ No newline at end of file
diff --git a/chromium/components/breadcrumbs/core/application_breadcrumbs_logger.cc b/chromium/components/breadcrumbs/core/application_breadcrumbs_logger.cc
index 6f31c59bb93..577fe8fe0d5 100644
--- a/chromium/components/breadcrumbs/core/application_breadcrumbs_logger.cc
+++ b/chromium/components/breadcrumbs/core/application_breadcrumbs_logger.cc
@@ -81,7 +81,7 @@ void ApplicationBreadcrumbsLogger::OnUserAction(const std::string& action,
void ApplicationBreadcrumbsLogger::OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
- std::string pressure_string = "";
+ const char* pressure_string = "";
switch (memory_pressure_level) {
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
pressure_string = "None";
@@ -94,7 +94,7 @@ void ApplicationBreadcrumbsLogger::OnMemoryPressure(
break;
}
- AddEvent(base::StringPrintf("Memory Pressure: %s", pressure_string.c_str()));
+ AddEvent(base::StringPrintf("Memory Pressure: %s", pressure_string));
}
bool ApplicationBreadcrumbsLogger::IsUserTriggeredAction(
diff --git a/chromium/components/browser_sync/signin_confirmation_helper.cc b/chromium/components/browser_sync/signin_confirmation_helper.cc
index 0d8619007e0..62cf4a4498b 100644
--- a/chromium/components/browser_sync/signin_confirmation_helper.cc
+++ b/chromium/components/browser_sync/signin_confirmation_helper.cc
@@ -26,8 +26,8 @@ namespace {
class HasTypedURLsTask : public history::HistoryDBTask {
public:
explicit HasTypedURLsTask(base::OnceCallback<void(bool)> cb)
- : has_typed_urls_(false), cb_(std::move(cb)) {}
- ~HasTypedURLsTask() override {}
+ : cb_(std::move(cb)) {}
+ ~HasTypedURLsTask() override = default;
bool RunOnDBThread(history::HistoryBackend* backend,
history::HistoryDatabase* db) override {
@@ -44,7 +44,7 @@ class HasTypedURLsTask : public history::HistoryDBTask {
void DoneRunOnMainThread() override { std::move(cb_).Run(has_typed_urls_); }
private:
- bool has_typed_urls_;
+ bool has_typed_urls_ = false;
base::OnceCallback<void(bool)> cb_;
};
diff --git a/chromium/components/browser_sync/sync_api_component_factory_impl.cc b/chromium/components/browser_sync/sync_api_component_factory_impl.cc
index acbe7954c43..bd70909546d 100644
--- a/chromium/components/browser_sync/sync_api_component_factory_impl.cc
+++ b/chromium/components/browser_sync/sync_api_component_factory_impl.cc
@@ -15,7 +15,6 @@
#include "build/chromeos_buildflags.h"
#include "components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h"
#include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
-#include "components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h"
#include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
#include "components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h"
#include "components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.h"
@@ -33,6 +32,7 @@
#include "components/send_tab_to_self/features.h"
#include "components/send_tab_to_self/send_tab_to_self_model_type_controller.h"
#include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
+#include "components/sync/base/features.h"
#include "components/sync/base/legacy_directory_deletion.h"
#include "components/sync/base/report_unrecoverable_error.h"
#include "components/sync/base/sync_prefs.h"
@@ -193,14 +193,12 @@ SyncApiComponentFactoryImpl::CreateCommonDataTypeControllers(
// Autofill sync is enabled by default. Register unless explicitly
// disabled.
if (!disabled_types.Has(syncer::AUTOFILL_PROFILE)) {
- controllers.push_back(
- std::make_unique<AutofillProfileModelTypeController>(
- std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
- db_thread_,
- base::BindRepeating(
- &AutofillProfileDelegateFromDataService,
- base::RetainedRef(web_data_service_on_disk_))),
- sync_client_->GetPrefService(), sync_service));
+ controllers.push_back(std::make_unique<syncer::ModelTypeController>(
+ syncer::AUTOFILL_PROFILE,
+ std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
+ db_thread_, base::BindRepeating(
+ &AutofillProfileDelegateFromDataService,
+ base::RetainedRef(web_data_service_on_disk_)))));
}
// Wallet data sync is enabled by default. Register unless explicitly
@@ -224,8 +222,6 @@ SyncApiComponentFactoryImpl::CreateCommonDataTypeControllers(
// Wallet offer data is enabled by default. Register unless explicitly
// disabled.
- // TODO(crbug.com/1112095): Currently the offer data depends on Wallet data
- // sync, but revisit after other offer types are implemented.
if (!disabled_types.Has(syncer::AUTOFILL_WALLET_DATA) &&
!disabled_types.Has(syncer::AUTOFILL_WALLET_OFFER)) {
controllers.push_back(CreateWalletModelTypeController(
@@ -251,46 +247,48 @@ SyncApiComponentFactoryImpl::CreateCommonDataTypeControllers(
}
}
- // These features are enabled only if history is not disabled.
- if (!sync_client_->GetPrefService()->GetBoolean(
- prefs::kSavingBrowserHistoryDisabled)) {
- // TypedUrl sync is enabled by default. Register unless explicitly
- // disabled.
- if (!disabled_types.Has(syncer::TYPED_URLS)) {
- // TypedURLModelTypeController uses a proxy delegate internally, as
- // provided by HistoryService.
- controllers.push_back(
- std::make_unique<history::TypedURLModelTypeController>(
- sync_client_->GetHistoryService(),
- sync_client_->GetPrefService()));
- }
+ // TypedUrl sync is enabled by default. Register unless explicitly disabled.
+ if (!disabled_types.Has(syncer::TYPED_URLS)) {
+ // TypedURLModelTypeController uses a proxy delegate internally, as
+ // provided by HistoryService.
+ controllers.push_back(
+ std::make_unique<history::TypedURLModelTypeController>(
+ syncer::TYPED_URLS, sync_service, sync_client_->GetHistoryService(),
+ sync_client_->GetPrefService()));
+ }
- // Delete directive sync is enabled by default.
- if (!disabled_types.Has(syncer::HISTORY_DELETE_DIRECTIVES)) {
- controllers.push_back(
- std::make_unique<history::HistoryDeleteDirectivesModelTypeController>(
- dump_stack, sync_service,
- sync_client_->GetModelTypeStoreService(),
- sync_client_->GetHistoryService()));
- }
+ if (!disabled_types.Has(syncer::HISTORY) &&
+ base::FeatureList::IsEnabled(syncer::kSyncEnableHistoryDataType)) {
+ controllers.push_back(
+ std::make_unique<history::TypedURLModelTypeController>(
+ syncer::HISTORY, sync_service, sync_client_->GetHistoryService(),
+ sync_client_->GetPrefService()));
+ }
- // Session sync is enabled by default. This is disabled if history is
- // disabled because the tab sync data is added to the web history on the
- // server.
- if (!disabled_types.Has(syncer::PROXY_TABS)) {
- controllers.push_back(
- std::make_unique<sync_sessions::ProxyTabsDataTypeController>(
- base::BindRepeating(
- &sync_sessions::SessionSyncService::ProxyTabsStateChanged,
- base::Unretained(sync_client_->GetSessionSyncService()))));
- controllers.push_back(
- std::make_unique<sync_sessions::SessionModelTypeController>(
- sync_service, sync_client_->GetPrefService(),
- std::make_unique<syncer::ForwardingModelTypeControllerDelegate>(
- sync_client_->GetSessionSyncService()
- ->GetControllerDelegate()
- .get())));
- }
+ // Delete directive sync is enabled by default.
+ if (!disabled_types.Has(syncer::HISTORY_DELETE_DIRECTIVES)) {
+ controllers.push_back(
+ std::make_unique<history::HistoryDeleteDirectivesModelTypeController>(
+ dump_stack, sync_service, sync_client_->GetModelTypeStoreService(),
+ sync_client_->GetHistoryService(), sync_client_->GetPrefService()));
+ }
+
+ if (!disabled_types.Has(syncer::PROXY_TABS)) {
+ controllers.push_back(
+ std::make_unique<sync_sessions::ProxyTabsDataTypeController>(
+ sync_service, sync_client_->GetPrefService(),
+ base::BindRepeating(
+ &sync_sessions::SessionSyncService::ProxyTabsStateChanged,
+ base::Unretained(sync_client_->GetSessionSyncService()))));
+ }
+ if (!disabled_types.Has(syncer::SESSIONS)) {
+ controllers.push_back(
+ std::make_unique<sync_sessions::SessionModelTypeController>(
+ sync_service, sync_client_->GetPrefService(),
+ std::make_unique<syncer::ForwardingModelTypeControllerDelegate>(
+ sync_client_->GetSessionSyncService()
+ ->GetControllerDelegate()
+ .get())));
}
// Password sync is enabled by default. Register unless explicitly
@@ -376,15 +374,12 @@ SyncApiComponentFactoryImpl::CreateCommonDataTypeControllers(
std::unique_ptr<DataTypeManager>
SyncApiComponentFactoryImpl::CreateDataTypeManager(
- const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
- debug_info_listener,
const DataTypeController::TypeMap* controllers,
const syncer::DataTypeEncryptionHandler* encryption_handler,
syncer::ModelTypeConfigurer* configurer,
DataTypeManagerObserver* observer) {
- return std::make_unique<DataTypeManagerImpl>(debug_info_listener, controllers,
- encryption_handler, configurer,
- observer);
+ return std::make_unique<DataTypeManagerImpl>(controllers, encryption_handler,
+ configurer, observer);
}
std::unique_ptr<syncer::SyncEngine>
diff --git a/chromium/components/browser_sync/sync_api_component_factory_impl.h b/chromium/components/browser_sync/sync_api_component_factory_impl.h
index 435c7032191..bfa4ad7b608 100644
--- a/chromium/components/browser_sync/sync_api_component_factory_impl.h
+++ b/chromium/components/browser_sync/sync_api_component_factory_impl.h
@@ -69,8 +69,6 @@ class SyncApiComponentFactoryImpl : public syncer::SyncApiComponentFactory {
// SyncApiComponentFactory implementation:
std::unique_ptr<syncer::DataTypeManager> CreateDataTypeManager(
- const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
- debug_info_listener,
const syncer::DataTypeController::TypeMap* controllers,
const syncer::DataTypeEncryptionHandler* encryption_handler,
syncer::ModelTypeConfigurer* configurer,
diff --git a/chromium/components/browser_ui/accessibility/DEPS b/chromium/components/browser_ui/accessibility/DEPS
index 08335e87944..ac8e25130df 100644
--- a/chromium/components/browser_ui/accessibility/DEPS
+++ b/chromium/components/browser_ui/accessibility/DEPS
@@ -3,4 +3,5 @@ include_rules = [
"+components/user_prefs",
"+content/public/android/java",
"+content/public/browser",
+ "+ui/android",
]
diff --git a/chromium/components/browser_ui/accessibility/android/BUILD.gn b/chromium/components/browser_ui/accessibility/android/BUILD.gn
index 23b6a6358b0..855fc968913 100644
--- a/chromium/components/browser_ui/accessibility/android/BUILD.gn
+++ b/chromium/components/browser_ui/accessibility/android/BUILD.gn
@@ -32,6 +32,12 @@ android_library("java") {
"java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java",
"java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java",
"java/src/org/chromium/components/browser_ui/accessibility/FontSizePrefs.java",
+ "java/src/org/chromium/components/browser_ui/accessibility/PageZoomCoordinator.java",
+ "java/src/org/chromium/components/browser_ui/accessibility/PageZoomMediator.java",
+ "java/src/org/chromium/components/browser_ui/accessibility/PageZoomPreference.java",
+ "java/src/org/chromium/components/browser_ui/accessibility/PageZoomProperties.java",
+ "java/src/org/chromium/components/browser_ui/accessibility/PageZoomUtils.java",
+ "java/src/org/chromium/components/browser_ui/accessibility/PageZoomViewBinder.java",
"java/src/org/chromium/components/browser_ui/accessibility/TextScalePreference.java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
@@ -39,24 +45,52 @@ android_library("java") {
":constants_java",
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/settings/android:java",
"//content/public/android:content_full_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_preference_preference_java",
+ "//ui/android:ui_no_recycler_view_java",
]
resources_package = "org.chromium.components.browser_ui.accessibility"
}
android_resources("java_resources") {
- deps = [
- "//components/browser_ui/strings/android:browser_ui_strings_grd",
- "//components/browser_ui/styles/android:java_resources",
- "//third_party/androidx:androidx_preference_preference_java",
- ]
sources = [
+ "java/res/drawable/ic_zoom.xml",
+ "java/res/drawable/page_zoom_background.xml",
"java/res/layout/custom_preference.xml",
+ "java/res/layout/page_zoom_preference.xml",
+ "java/res/layout/page_zoom_view.xml",
"java/res/layout/preference_text_scale.xml",
+ "java/res/values/dimens.xml",
"java/res/values/styles.xml",
"java/res/xml/accessibility_preferences.xml",
]
+
+ deps = [
+ "//components/browser_ui/strings/android:browser_ui_strings_grd",
+ "//components/browser_ui/styles/android:java_resources",
+ "//third_party/androidx:androidx_preference_preference_java",
+ ]
+}
+
+java_library("junit") {
+ bypass_platform_checks = true
+ testonly = true
+ sources = [ "java/src/org/chromium/components/browser_ui/accessibility/PageZoomMediatorUnitTest.java" ]
+
+ deps = [
+ ":java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//base:base_junit_test_support",
+ "//content/public/android:content_full_java",
+ "//third_party/android_deps:robolectric_all_java",
+ "//third_party/androidx:androidx_test_runner_java",
+ "//third_party/junit",
+ "//third_party/mockito:mockito_java",
+ "//ui/android:ui_no_recycler_view_java",
+ ]
}
diff --git a/chromium/components/browser_ui/bottomsheet/android/BUILD.gn b/chromium/components/browser_ui/bottomsheet/android/BUILD.gn
index e797df69839..6bb2e2dbc1a 100644
--- a/chromium/components/browser_ui/bottomsheet/android/BUILD.gn
+++ b/chromium/components/browser_ui/bottomsheet/android/BUILD.gn
@@ -39,6 +39,7 @@ android_library("manager_java") {
deps = [
":java",
+ "//components/browser_ui/widget/android:java",
"//ui/android:ui_full_java",
]
}
@@ -49,7 +50,6 @@ android_library_factory("factory_java") {
deps = [
":java",
":manager_java",
- "//base:base_java",
"//components/browser_ui/widget/android:java",
"//ui/android:ui_full_java",
"//ui/android:ui_utils_java",
diff --git a/chromium/components/browser_ui/bottomsheet/android/internal/BUILD.gn b/chromium/components/browser_ui/bottomsheet/android/internal/BUILD.gn
index 108bde46964..0998eb09c85 100644
--- a/chromium/components/browser_ui/bottomsheet/android/internal/BUILD.gn
+++ b/chromium/components/browser_ui/bottomsheet/android/internal/BUILD.gn
@@ -59,6 +59,7 @@ android_library("javatests") {
"//base:base_java",
"//base:base_java_test_support",
"//components/browser_ui/widget/android:java",
+ "//content/public/test/android:content_java_test_support",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit",
"//ui/android:ui_java_test_support",
diff --git a/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java b/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
index d3830ee01f0..8b99f79b648 100644
--- a/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
+++ b/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
@@ -25,6 +25,7 @@ import org.chromium.base.Callback;
import org.chromium.base.Log;
import org.chromium.base.MathUtils;
import org.chromium.base.ObserverList;
+import org.chromium.base.supplier.Supplier;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent.HeightMode;
@@ -164,6 +165,18 @@ class BottomSheet extends FrameLayout
/** A means of checking whether accessibility is currently enabled. */
private AccessibilityUtil mAccessibilityUtil;
+ private Window mWindow;
+
+ /**
+ * Provides the height of the base app area on which bottom sheet client is drawn. This is
+ * not necessary for most embedders of BottomSheet, unless they have non-zero vertical Window
+ * offset that would push down a part of app area out of the screen. BottomSheet then uses
+ * this height to resize the sheet content so all of it is visible.
+ *
+ * Note: The only embedder for which BottomSheet needs this is partial-height custom tabs.
+ */
+ private Supplier<Integer> mBaseHeightProvider;
+
/**
* A view used to render a shadow behind the sheet and extends outside the bounds of its parent
* view.
@@ -303,8 +316,10 @@ class BottomSheet extends FrameLayout
* calculations in this class.
* @param window Android window for getting insets.
* @param keyboardDelegate Delegate for hiding the keyboard.
+ * @param baseHeightProvider Provides the height of base app area the sheet content is drawn on.
*/
- public void init(Window window, KeyboardVisibilityDelegate keyboardDelegate) {
+ public void init(Window window, KeyboardVisibilityDelegate keyboardDelegate,
+ Supplier<Integer> baseHeightProvider) {
mSheetContainer = (ViewGroup) getParent();
mToolbarHolder =
@@ -317,6 +332,8 @@ class BottomSheet extends FrameLayout
mContainerWidth = mSheetContainer.getWidth();
mContainerHeight = mSheetContainer.getHeight();
mContentWidth = mContainerWidth;
+ mWindow = window;
+ mBaseHeightProvider = baseHeightProvider;
sizeAndPositionSheetInParent();
@@ -335,8 +352,14 @@ class BottomSheet extends FrameLayout
mContainerHeight = bottom - top;
if (previousWidth != mContainerWidth || previousHeight != mContainerHeight) {
- if (mCurrentState == SheetState.HALF && !isHalfStateEnabled()) {
- setSheetState(SheetState.FULL, false);
+ if (!isHalfStateEnabled()) {
+ if (mCurrentState == SheetState.HALF) {
+ setSheetState(SheetState.FULL, false);
+ } else if (mCurrentState == SheetState.SCROLLING
+ && mTargetState == SheetState.HALF) {
+ // Let the animation resume to the full height.
+ mTargetState = SheetState.FULL;
+ }
}
invalidateContentDesiredHeight();
sizeAndPositionSheetInParent();
@@ -1016,6 +1039,8 @@ class BottomSheet extends FrameLayout
if (getFocusedChild() == null) requestFocus();
}
+ sizeAndPositionSheetInParent();
+
for (BottomSheetObserver o : mObservers) {
o.onSheetStateChanged(mCurrentState, reason);
}
@@ -1063,12 +1088,25 @@ class BottomSheet extends FrameLayout
return mContainerWidth;
}
- /** Center and size the sheet in its container. */
private void sizeAndPositionSheetInParent() {
+ // Center and size the sheet in its container.
int maxSheetWidth = getMaxSheetWidth();
getLayoutParams().width = maxSheetWidth;
setTranslationX((LocalizationUtils.isLayoutRtl() ? -1 : 1)
* (mContainerWidth - maxSheetWidth) / 2f);
+
+ // Resizing is necessary if we have non-zero translation on Window Y, which can change
+ // throughout the lifecycle. Ensure sheet content's bottom is aligned with the base layout.
+ if (mWindow.getAttributes().y != 0
+ && (mCurrentState == SheetState.PEEK || mCurrentState == SheetState.HALF
+ || mCurrentState == SheetState.FULL)) {
+ BottomSheetContent content = mSheetContent;
+ assert content != null
+ && content.getContentView() != null : "Current content should exist";
+
+ int bottomY = mBaseHeightProvider.get();
+ content.getContentView().getLayoutParams().height = bottomY - (int) getTranslationY();
+ }
requestLayout();
}
diff --git a/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java b/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java
index 94d0dcc90df..d697812d7c8 100644
--- a/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java
+++ b/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java
@@ -22,13 +22,15 @@ public class BottomSheetControllerFactory {
* @param window The activity's window.
* @param keyboardDelegate A means of hiding the keyboard.
* @param root The view that should contain the sheet.
+ * @param baseHeightProvider Provides the height of base app area the sheet content is drawn on.
* @return A new instance of the {@link BottomSheetController}.
*/
public static ManagedBottomSheetController createBottomSheetController(
final Supplier<ScrimCoordinator> scrim, Callback<View> initializedCallback,
- Window window, KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root) {
+ Window window, KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root,
+ Supplier<Integer> baseHeightProvider) {
return new BottomSheetControllerImpl(
- scrim, initializedCallback, window, keyboardDelegate, root);
+ scrim, initializedCallback, window, keyboardDelegate, root, baseHeightProvider);
}
// Redirect methods to provider to make them only accessible to classes that have access to the
diff --git a/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java b/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
index a824dc79b24..8005cfc7174 100644
--- a/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
+++ b/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
@@ -12,7 +12,10 @@ import android.view.Window;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.Callback;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.base.supplier.Supplier;
+import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
import org.chromium.components.browser_ui.widget.scrim.ScrimProperties;
import org.chromium.ui.KeyboardVisibilityDelegate;
@@ -81,6 +84,16 @@ class BottomSheetControllerImpl implements ManagedBottomSheetController {
/** A means of checking whether accessibility is currently enabled. */
private AccessibilityUtil mAccessibilityUtil;
+ /** A supplier indicating whether back press should be handled by the bottom sheet. */
+ private final ObservableSupplierImpl<Boolean> mBackPressStateChangedSupplier =
+ new ObservableSupplierImpl<>();
+
+ /**
+ * A {@link BackPressHandler} to handle back press when the bottom sheet is open and/or has
+ * sheet content.
+ */
+ private final BackPressHandler mBackPressHandler;
+
/**
* Build a new controller of the bottom sheet.
* @param scrim A supplier of the scrim that shows when the bottom sheet is opened.
@@ -89,28 +102,51 @@ class BottomSheetControllerImpl implements ManagedBottomSheetController {
* @param window A means of accessing the screen size.
* @param keyboardDelegate A means of hiding the keyboard.
* @param root The view that should contain the sheet.
+ * @param baseHeightProvider Provides the height of base app area the sheet content is drawn on.
*/
public BottomSheetControllerImpl(final Supplier<ScrimCoordinator> scrim,
Callback<View> initializedCallback, Window window,
- KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root) {
+ KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root,
+ Supplier<Integer> baseHeightProvider) {
mScrimCoordinatorSupplier = scrim;
mPendingSheetObservers = new ArrayList<>();
mSuppressionTokens = new TokenHolder(() -> onSuppressionTokensChanged());
mSheetInitializer = () -> {
- initializeSheet(initializedCallback, window, keyboardDelegate, root);
+ initializeSheet(
+ initializedCallback, window, keyboardDelegate, root, baseHeightProvider);
+ };
+
+ mBackPressHandler = new BackPressHandler() {
+ @Override
+ public void handleBackPress() {
+ boolean ret = BottomSheetControllerImpl.this.handleBackPress();
+ assert ret;
+ }
+
+ @Override
+ public ObservableSupplier<Boolean> getHandleBackPressChangedSupplier() {
+ return mBackPressStateChangedSupplier;
+ }
};
}
+ @Override
+ public BackPressHandler getBottomSheetBackPressHandler() {
+ return mBackPressHandler;
+ }
+
/**
* Do the actual initialization of the bottom sheet.
* @param initializedCallback A callback for the creation of the sheet.
* @param window A means of accessing the screen size.
* @param keyboardDelegate A means of hiding the keyboard.
* @param root The view that should contain the sheet.
+ * @param baseHeightProvider Provides the height of base app area the sheet content is drawn on.
*/
private void initializeSheet(Callback<View> initializedCallback, Window window,
- KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root) {
+ KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root,
+ Supplier<Integer> baseHeightProvider) {
mBottomSheetContainer = root.get();
mBottomSheetContainer.setVisibility(View.VISIBLE);
@@ -119,7 +155,7 @@ class BottomSheetControllerImpl implements ManagedBottomSheetController {
mBottomSheet = (BottomSheet) root.get().findViewById(R.id.bottom_sheet);
initializedCallback.onResult(mBottomSheet);
- mBottomSheet.init(window, keyboardDelegate);
+ mBottomSheet.init(window, keyboardDelegate, baseHeightProvider);
mBottomSheet.setAccessibilityUtil(mAccessibilityUtil);
// Initialize the queue with a comparator that checks content priority.
@@ -200,10 +236,13 @@ class BottomSheetControllerImpl implements ManagedBottomSheetController {
mIsSuppressingCurrentContent = false;
mIsProcessingHideRequest = false;
showNextContent(true);
+ updateBackPressStateChangedSupplier();
}
@Override
public void onSheetContentChanged(BottomSheetContent newContent) {
+ updateBackPressStateChangedSupplier();
+
if (newContent != null) return;
// If there are no more things to be shown, the container can avoid layouts.
@@ -328,6 +367,7 @@ class BottomSheetControllerImpl implements ManagedBottomSheetController {
/** Handle a change in the state of the token holder responsible for the suppression tokens. */
private void onSuppressionTokensChanged() {
if (!mSuppressionTokens.hasTokens()) doUnsuppression();
+ updateBackPressStateChangedSupplier();
}
@Override
@@ -553,4 +593,15 @@ class BottomSheetControllerImpl implements ManagedBottomSheetController {
boolean hasSuppressionTokensForTesting() {
return mSuppressionTokens.hasTokens();
}
+
+ /**
+ * Update the supplier to hold true when the sheet is in a valid state and holds sheet content,
+ * and when there are no suppression tokens, false otherwise.
+ * TODO (crbug.com/1279941): Account for sheet content back press handling and #isSheetOpen
+ * after sheet content refactor.
+ */
+ private void updateBackPressStateChangedSupplier() {
+ mBackPressStateChangedSupplier.set(mBottomSheet != null && !mSuppressionTokens.hasTokens()
+ && mBottomSheet.getCurrentSheetContent() != null);
+ }
}
diff --git a/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java b/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
index 789a93c3717..f0e6a252137 100644
--- a/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
+++ b/chromium/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
@@ -5,6 +5,7 @@
package org.chromium.components.browser_ui.bottomsheet;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
@@ -21,17 +22,18 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.MathUtils;
-import org.chromium.base.ThreadUtils;
import org.chromium.base.test.BaseActivityTestRule;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.Restriction;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.test.util.BlankUiTestActivity;
import org.chromium.ui.test.util.UiRestriction;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
/** This class tests the functionality of the {@link BottomSheetObserver}. */
@@ -125,12 +127,17 @@ public class BottomSheetObserverTest {
}
}, rootView, Color.WHITE);
- mBottomSheetController = new BottomSheetControllerImpl(() -> scrim, (v) -> {},
- mTestRule.getActivity().getWindow(), KeyboardVisibilityDelegate.getInstance(),
- () -> rootView);
+ mBottomSheetController = TestThreadUtils.runOnUiThreadBlocking(
+ ()
+ -> new BottomSheetControllerImpl(()
+ -> scrim,
+ (v)
+ -> {},
+ mTestRule.getActivity().getWindow(),
+ KeyboardVisibilityDelegate.getInstance(), () -> rootView, () -> 0));
mTestSupport = new BottomSheetTestSupport(mBottomSheetController);
- ThreadUtils.runOnUiThreadBlocking(() -> {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
mSheetContent = new TestBottomSheetContent(
mTestRule.getActivity(), BottomSheetContent.ContentPriority.HIGH, false);
mBottomSheetController.requestShowContent(mSheetContent, false);
@@ -143,7 +150,7 @@ public class BottomSheetObserverTest {
/** Test that the onSheetClosed event is triggered if the sheet is closed without animation. */
@Test
@MediumTest
- public void testCloseEventCalled_noAnimation() throws TimeoutException {
+ public void testCloseEventCalled_noAnimation() throws TimeoutException, ExecutionException {
runCloseEventTest(false, true);
}
@@ -153,14 +160,15 @@ public class BottomSheetObserverTest {
*/
@Test
@MediumTest
- public void testCloseEventCalled_noAnimationNoPeekState() throws TimeoutException {
+ public void testCloseEventCalled_noAnimationNoPeekState()
+ throws TimeoutException, ExecutionException {
runCloseEventTest(false, false);
}
/** Test that the onSheetClosed event is triggered if the sheet is closed with animation. */
@Test
@MediumTest
- public void testCloseEventCalled_withAnimation() throws TimeoutException {
+ public void testCloseEventCalled_withAnimation() throws TimeoutException, ExecutionException {
runCloseEventTest(true, true);
}
@@ -170,7 +178,8 @@ public class BottomSheetObserverTest {
*/
@Test
@MediumTest
- public void testCloseEventCalled_withAnimationNoPeekState() throws TimeoutException {
+ public void testCloseEventCalled_withAnimationNoPeekState()
+ throws TimeoutException, ExecutionException {
runCloseEventTest(true, false);
}
@@ -180,11 +189,11 @@ public class BottomSheetObserverTest {
* @param peekStateEnabled Whether the sheet's content has a peek state.
*/
private void runCloseEventTest(boolean animationEnabled, boolean peekStateEnabled)
- throws TimeoutException {
+ throws TimeoutException, ExecutionException {
CallbackHelper hiddenHelper = mObserver.mHiddenCallbackHelper;
int initialHideEvents = hiddenHelper.getCallCount();
- ThreadUtils.runOnUiThreadBlocking(
+ TestThreadUtils.runOnUiThreadBlocking(
() -> mTestSupport.setSheetState(BottomSheetController.SheetState.FULL, false));
mSheetContent.setPeekHeight(peekStateEnabled ? BottomSheetContent.HeightMode.DEFAULT
@@ -198,13 +207,18 @@ public class BottomSheetObserverTest {
int targetState = peekStateEnabled ? BottomSheetController.SheetState.PEEK
: BottomSheetController.SheetState.HIDDEN;
- ThreadUtils.runOnUiThreadBlocking(
+ TestThreadUtils.runOnUiThreadBlocking(
() -> mTestSupport.setSheetState(targetState, animationEnabled));
closedCallbackHelper.waitForCallback(closedCallbackCount, 1);
if (targetState == BottomSheetController.SheetState.HIDDEN) {
hiddenHelper.waitForCallback(initialHideEvents, 1);
+ assertFalse(TestThreadUtils.runOnUiThreadBlocking(
+ ()
+ -> mBottomSheetController.getBottomSheetBackPressHandler()
+ .getHandleBackPressChangedSupplier()
+ .get()));
}
assertEquals(initialOpenedCount, mObserver.mOpenedCallbackHelper.getCallCount());
@@ -215,7 +229,7 @@ public class BottomSheetObserverTest {
/** Test that the onSheetOpened event is triggered if the sheet is opened without animation. */
@Test
@MediumTest
- public void testOpenedEventCalled_noAnimation() throws TimeoutException {
+ public void testOpenedEventCalled_noAnimation() throws TimeoutException, ExecutionException {
runOpenEventTest(false, true);
}
@@ -225,14 +239,15 @@ public class BottomSheetObserverTest {
*/
@Test
@MediumTest
- public void testOpenedEventCalled_noAnimationNoPeekState() throws TimeoutException {
+ public void testOpenedEventCalled_noAnimationNoPeekState()
+ throws TimeoutException, ExecutionException {
runOpenEventTest(false, false);
}
/** Test that the onSheetOpened event is triggered if the sheet is opened with animation. */
@Test
@MediumTest
- public void testOpenedEventCalled_withAnimation() throws TimeoutException {
+ public void testOpenedEventCalled_withAnimation() throws TimeoutException, ExecutionException {
runOpenEventTest(true, true);
}
@@ -242,7 +257,8 @@ public class BottomSheetObserverTest {
*/
@Test
@MediumTest
- public void testOpenedEventCalled_withAnimationNoPeekState() throws TimeoutException {
+ public void testOpenedEventCalled_withAnimationNoPeekState()
+ throws TimeoutException, ExecutionException {
runOpenEventTest(true, false);
}
@@ -252,7 +268,7 @@ public class BottomSheetObserverTest {
* @param peekStateEnabled Whether the sheet's content has a peek state.
*/
private void runOpenEventTest(boolean animationEnabled, boolean peekStateEnabled)
- throws TimeoutException {
+ throws TimeoutException, ExecutionException {
mSheetContent.setPeekHeight(peekStateEnabled ? BottomSheetContent.HeightMode.DEFAULT
: BottomSheetContent.HeightMode.DISABLED);
@@ -263,7 +279,7 @@ public class BottomSheetObserverTest {
CallbackHelper closedCallbackHelper = mObserver.mClosedCallbackHelper;
int initialClosedCount = closedCallbackHelper.getCallCount();
- ThreadUtils.runOnUiThreadBlocking(
+ TestThreadUtils.runOnUiThreadBlocking(
() -> mTestSupport.setSheetState(mTestSupport.getOpeningState(), false));
assertNotEquals("Sheet should not be hidden.", mBottomSheetController.getSheetState(),
@@ -273,7 +289,7 @@ public class BottomSheetObserverTest {
mBottomSheetController.getSheetState(), BottomSheetController.SheetState.PEEK);
}
- ThreadUtils.runOnUiThreadBlocking(
+ TestThreadUtils.runOnUiThreadBlocking(
()
-> mTestSupport.setSheetState(
BottomSheetController.SheetState.FULL, animationEnabled));
@@ -285,6 +301,12 @@ public class BottomSheetObserverTest {
openedCallbackHelper.getCallCount());
assertEquals(initialClosedCount, closedCallbackHelper.getCallCount());
+
+ assertTrue(TestThreadUtils.runOnUiThreadBlocking(
+ ()
+ -> mBottomSheetController.getBottomSheetBackPressHandler()
+ .getHandleBackPressChangedSupplier()
+ .get()));
}
/**
@@ -293,7 +315,7 @@ public class BottomSheetObserverTest {
@Test
@MediumTest
public void testOffsetChangedEvent() throws TimeoutException {
- ThreadUtils.runOnUiThreadBlocking(
+ TestThreadUtils.runOnUiThreadBlocking(
() -> mTestSupport.setSheetState(BottomSheetController.SheetState.FULL, false));
CallbackHelper callbackHelper = mObserver.mOffsetChangedCallbackHelper;
@@ -307,21 +329,21 @@ public class BottomSheetObserverTest {
// When in the hidden state, the transition value should be 0.
int callbackCount = callbackHelper.getCallCount();
- ThreadUtils.runOnUiThreadBlocking(
+ TestThreadUtils.runOnUiThreadBlocking(
() -> mTestSupport.setSheetOffsetFromBottom(hiddenHeight, StateChangeReason.NONE));
callbackHelper.waitForCallback(callbackCount, 1);
assertEquals(0f, mObserver.getLastOffsetChangedValue(), MathUtils.EPSILON);
// When in the full state, the transition value should be 1.
callbackCount = callbackHelper.getCallCount();
- ThreadUtils.runOnUiThreadBlocking(
+ TestThreadUtils.runOnUiThreadBlocking(
() -> mTestSupport.setSheetOffsetFromBottom(fullHeight, StateChangeReason.NONE));
callbackHelper.waitForCallback(callbackCount, 1);
assertEquals(1f, mObserver.getLastOffsetChangedValue(), MathUtils.EPSILON);
// Halfway between peek and full should send 0.5.
callbackCount = callbackHelper.getCallCount();
- ThreadUtils.runOnUiThreadBlocking(
+ TestThreadUtils.runOnUiThreadBlocking(
() -> mTestSupport.setSheetOffsetFromBottom(midPeekFull, StateChangeReason.NONE));
callbackHelper.waitForCallback(callbackCount, 1);
assertEquals(0.5f, mObserver.getLastOffsetChangedValue(), MathUtils.EPSILON);
@@ -338,7 +360,7 @@ public class BottomSheetObserverTest {
CallbackHelper callbackHelper = mObserver.mContentChangedCallbackHelper;
int callCount = callbackHelper.getCallCount();
- ThreadUtils.runOnUiThreadBlocking(() -> {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
// We wrap the View in a FrameLayout as we need something to read the
// hard coded height in the layout params. There is no way to create a
// View with a specific height on its own as View::onMeasure will by
@@ -366,7 +388,7 @@ public class BottomSheetObserverTest {
callbackHelper.waitForCallback(callCount);
// HALF state is forbidden when wrapping the content.
- ThreadUtils.runOnUiThreadBlocking(
+ TestThreadUtils.runOnUiThreadBlocking(
() -> mTestSupport.setSheetState(BottomSheetController.SheetState.HALF, false));
assertEquals(BottomSheetController.SheetState.FULL, mBottomSheetController.getSheetState());
diff --git a/chromium/components/browser_ui/client_certificate/android/BUILD.gn b/chromium/components/browser_ui/client_certificate/android/BUILD.gn
index 7890738b09f..d5f6bfedef2 100644
--- a/chromium/components/browser_ui/client_certificate/android/BUILD.gn
+++ b/chromium/components/browser_ui/client_certificate/android/BUILD.gn
@@ -20,6 +20,8 @@ android_library("java") {
deps = [
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/widget/android:java_resources",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/components/browser_ui/contacts_picker/android/BUILD.gn b/chromium/components/browser_ui/contacts_picker/android/BUILD.gn
index 8149a6a5e34..b4f905b7f28 100644
--- a/chromium/components/browser_ui/contacts_picker/android/BUILD.gn
+++ b/chromium/components/browser_ui/contacts_picker/android/BUILD.gn
@@ -36,6 +36,8 @@ android_library("java") {
deps = [
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/util/android:java",
"//components/browser_ui/widget/android:java",
"//components/payments/mojom:mojom_java",
@@ -100,7 +102,6 @@ android_library("javatests") {
"//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/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_core_core_java",
diff --git a/chromium/components/browser_ui/http_auth/android/BUILD.gn b/chromium/components/browser_ui/http_auth/android/BUILD.gn
index 6136efedc20..c55b60910e4 100644
--- a/chromium/components/browser_ui/http_auth/android/BUILD.gn
+++ b/chromium/components/browser_ui/http_auth/android/BUILD.gn
@@ -11,7 +11,6 @@ android_library("java") {
deps = [
":java_resources",
- "//base:base_java",
"//components/browser_ui/widget/android:java",
"//components/strings:components_strings_grd",
"//third_party/android_deps:material_design_java",
diff --git a/chromium/components/browser_ui/media/android/BUILD.gn b/chromium/components/browser_ui/media/android/BUILD.gn
index 679af824432..1092df314f5 100644
--- a/chromium/components/browser_ui/media/android/BUILD.gn
+++ b/chromium/components/browser_ui/media/android/BUILD.gn
@@ -85,7 +85,6 @@ java_library("junit") {
]
deps = [
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//base/test:test_support_java",
diff --git a/chromium/components/browser_ui/modaldialog/android/BUILD.gn b/chromium/components/browser_ui/modaldialog/android/BUILD.gn
index 3d6dd2e87b8..7ec923bc3a1 100644
--- a/chromium/components/browser_ui/modaldialog/android/BUILD.gn
+++ b/chromium/components/browser_ui/modaldialog/android/BUILD.gn
@@ -18,8 +18,8 @@ android_library("java") {
"//components/browser_ui/styles/android:java",
"//components/browser_ui/widget/android:java",
"//content/public/android:content_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_core_core_java",
"//ui/android:ui_java",
]
resources_package = "org.chromium.components.browser_ui.modaldialog"
@@ -63,7 +63,6 @@ android_library("javatests") {
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/androidx:androidx_annotation_annotation_java",
- "//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/hamcrest:hamcrest_java",
"//third_party/junit:junit",
diff --git a/chromium/components/browser_ui/photo_picker/android/BUILD.gn b/chromium/components/browser_ui/photo_picker/android/BUILD.gn
index fe4984130aa..ab722e11594 100644
--- a/chromium/components/browser_ui/photo_picker/android/BUILD.gn
+++ b/chromium/components/browser_ui/photo_picker/android/BUILD.gn
@@ -42,6 +42,8 @@ android_library("java") {
deps = [
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/util/android:java",
"//components/browser_ui/widget/android:java",
"//content/public/android:content_java",
@@ -142,6 +144,7 @@ android_library("javatests") {
":java_resources",
"//base:base_java",
"//base:base_java_test_support",
+ "//build/android:build_java",
"//chrome/test/android:chrome_java_test_support",
"//components/browser_ui/test/android:test_support_java",
"//components/browser_ui/widget/android:java",
@@ -149,7 +152,6 @@ android_library("javatests") {
"//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/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_core_core_java",
diff --git a/chromium/components/browser_ui/settings/android/BUILD.gn b/chromium/components/browser_ui/settings/android/BUILD.gn
index f105274ca27..967a91bc39b 100644
--- a/chromium/components/browser_ui/settings/android/BUILD.gn
+++ b/chromium/components/browser_ui/settings/android/BUILD.gn
@@ -29,9 +29,11 @@ android_library("java") {
":java_resources",
"//base:base_java",
"//components/browser_ui/util/android:java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+ "//third_party/androidx:androidx_core_core_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
"//ui/android:ui_java",
]
@@ -56,6 +58,7 @@ android_resources("java_resources") {
"java/res/drawable-xxhdpi/ic_business_small.png",
"java/res/drawable-xxxhdpi/ic_account_child_grey600_36dp.png",
"java/res/drawable/list_divider.xml",
+ "java/res/layout-sw360dp/preference_spinner_single_line.xml",
"java/res/layout/button_preference_button.xml",
"java/res/layout/button_preference_layout.xml",
"java/res/layout/checkable_image_view_widget.xml",
@@ -66,7 +69,9 @@ android_resources("java_resources") {
"java/res/layout/preference_spinner_single_line.xml",
"java/res/layout/preference_spinner_single_line_item.xml",
"java/res/layout/settings_action_bar_shadow.xml",
+ "java/res/values-sw600dp/dimens.xml",
"java/res/values/attrs.xml",
+ "java/res/values/dimens.xml",
"java/res/values/ids.xml",
"java/res/values/styles.xml",
]
diff --git a/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java b/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java
index bde1bfafbd3..9aa7788ce6c 100644
--- a/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java
+++ b/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java
@@ -8,6 +8,7 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.graphics.drawable.DrawableCompat;
@@ -71,6 +72,9 @@ public class ExpandablePreferenceGroup extends PreferenceGroup {
? R.string.accessibility_expanded_group
: R.string.accessibility_collapsed_group);
view.setContentDescription(description);
+ if (view.isAccessibilityFocused()) {
+ view.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
+ }
}
private static Drawable createDrawable(Context context) {
diff --git a/chromium/components/browser_ui/share/android/BUILD.gn b/chromium/components/browser_ui/share/android/BUILD.gn
index 7ee31479c68..ed1569bc7cc 100644
--- a/chromium/components/browser_ui/share/android/BUILD.gn
+++ b/chromium/components/browser_ui/share/android/BUILD.gn
@@ -40,6 +40,7 @@ android_library("java") {
":constants_java",
":java_resources",
"//base:base_java",
+ "//base:jni_java",
"//components/browser_ui/util/android:java",
"//components/browser_ui/widget/android:java_resources",
"//components/dom_distiller/core/android:dom_distiller_core_java",
diff --git a/chromium/components/browser_ui/site_settings/android/BUILD.gn b/chromium/components/browser_ui/site_settings/android/BUILD.gn
index 285c9b0a5eb..f1ab039a4f4 100644
--- a/chromium/components/browser_ui/site_settings/android/BUILD.gn
+++ b/chromium/components/browser_ui/site_settings/android/BUILD.gn
@@ -72,7 +72,8 @@ android_library("java") {
deps = [
":java_resources",
"//base:base_java",
- "//build/android:build_config_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/settings/android:java",
"//components/browser_ui/styles/android:java",
"//components/browser_ui/widget/android:java",
@@ -87,9 +88,14 @@ android_library("java") {
"//components/user_prefs/android:java",
"//content/public/android:content_java",
"//services/device/public/java:device_feature_list_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+ "//third_party/androidx:androidx_core_core_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
+ "//third_party/androidx:androidx_recyclerview_recyclerview_java",
+ "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
"//ui/android:ui_full_java",
"//ui/android:ui_utils_java",
"//url:gurl_java",
@@ -169,6 +175,5 @@ android_resources("java_resources") {
"//components/browser_ui/strings/android:browser_ui_strings_grd",
"//components/browser_ui/styles/android:java_resources",
"//components/permissions/android:java_resources",
- "//third_party/android_deps:android_support_v7_appcompat_java",
]
}
diff --git a/chromium/components/browser_ui/site_settings/android/website_preference_bridge.cc b/chromium/components/browser_ui/site_settings/android/website_preference_bridge.cc
index def2775f382..9a1f6792dc4 100644
--- a/chromium/components/browser_ui/site_settings/android/website_preference_bridge.cc
+++ b/chromium/components/browser_ui/site_settings/android/website_preference_bridge.cc
@@ -171,8 +171,7 @@ void GetOrigins(JNIEnv* env,
continue;
}
- if (auto_blocker->GetEmbargoResult(GURL(origin), content_type)
- .content_setting == CONTENT_SETTING_BLOCK) {
+ if (auto_blocker->IsEmbargoed(GURL(origin), content_type)) {
seen_origins.push_back(origin);
insertionFunc(env, static_cast<int>(content_type), list,
ConvertOriginToJavaString(env, origin), jembedder,
@@ -199,7 +198,7 @@ ContentSetting GetPermissionSettingForOrigin(
embedder_url = GURL(embedder_str);
return permissions::PermissionsClient::Get()
->GetPermissionManager(unwrap(jbrowser_context_handle))
- ->GetPermissionStatus(content_type, url, embedder_url)
+ ->GetPermissionStatusDeprecated(content_type, url, embedder_url)
.content_setting;
}
@@ -302,16 +301,13 @@ static jboolean JNI_WebsitePreferenceBridge_IsNotificationEmbargoedForOrigin(
const JavaParamRef<jobject>& jbrowser_context_handle,
const JavaParamRef<jstring>& origin) {
GURL origin_url(ConvertJavaStringToUTF8(env, origin));
- permissions::PermissionResult status =
- permissions::PermissionsClient::Get()
- ->GetPermissionManager(unwrap(jbrowser_context_handle))
- ->GetPermissionStatus(ContentSettingsType::NOTIFICATIONS, origin_url,
- origin_url);
- return status.content_setting == ContentSetting::CONTENT_SETTING_BLOCK &&
- (status.source ==
- permissions::PermissionStatusSource::MULTIPLE_IGNORES ||
- status.source ==
- permissions::PermissionStatusSource::MULTIPLE_DISMISSALS);
+ BrowserContext* browser_context = unwrap(jbrowser_context_handle);
+ permissions::PermissionDecisionAutoBlocker* auto_blocker =
+ permissions::PermissionsClient::Get()->GetPermissionDecisionAutoBlocker(
+ browser_context);
+
+ return auto_blocker->IsEmbargoed(origin_url,
+ ContentSettingsType::NOTIFICATIONS);
}
static void SetNotificationSettingForOrigin(
diff --git a/chromium/components/browser_ui/sms/android/BUILD.gn b/chromium/components/browser_ui/sms/android/BUILD.gn
index b46244bbc81..1ab1041a110 100644
--- a/chromium/components/browser_ui/sms/android/BUILD.gn
+++ b/chromium/components/browser_ui/sms/android/BUILD.gn
@@ -55,6 +55,7 @@ android_library("java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
"//components/browser_ui/styles/android:java_resources",
"//components/infobars/android:java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings.grd b/chromium/components/browser_ui/strings/android/browser_ui_strings.grd
index 244a386fc07..54aaef5bc04 100644
--- a/chromium/components/browser_ui/strings/android/browser_ui_strings.grd
+++ b/chromium/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -460,6 +460,9 @@
<message name="IDS_PAGE_INFO_ABOUT_THIS_SITE_TITLE" desc="The title label of the 'About this site' subpage in Page Info bubble.">
From the web
</message>
+ <message name="IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE" desc="The title of the 'About this page' row in the Page Info bubble.">
+ About this page
+ </message>
<message name="IDS_PAGE_INFO_ABOUT_THIS_SITE_SUBPAGE_FROM_LABEL" desc="The label containing the source of the description in the 'About this site' subpage in Page Info bubble.">
From <ph name="SOURCE_NAME">%1$s<ex>Wikipedia</ex></ph>
</message>
@@ -609,21 +612,6 @@
<message name="IDS_ACCESSIBILITY_TOOLBAR_VIEW_SITE_INFO" desc="Spoken by screen readers to explain that clicking on the status icon will show more site information when clicked. The string is read in the context of the 'Double tap to...' Android screen reader accessibility prompt e.g. 'Double tap to view site information'.">
View site information
</message>
- <message name="IDS_PAGE_ZOOM_TITLE" desc="Title of the preference that allows the user to update the accessibility page zoom feature that applies to the web contents." translateable="false">
- Zoom
- </message>
- <message name="IDS_PAGE_ZOOM_SUMMARY" desc="Summary of the preference that allows the user to update the accessibility page zoom feature that applies to the web contents." translateable="false">
- Use this setting to increase/decrease the zoom for the page.
- </message>
- <message name="IDS_PAGE_ZOOM_DECREASE_ZOOM_BUTTON_TEXT" desc="Accessibility label for button to allow user to decrease page zoom" translateable="false">
- Decrease zoom
- </message>
- <message name="IDS_PAGE_ZOOM_INCREASE_ZOOM_BUTTON_TEXT" desc="Accessibility label for button to allow user to increase page zoom" translateable="false">
- Increase zoom
- </message>
- <message name="IDS_PAGE_ZOOM_FACTOR" desc="Text description of the current page zoom factor set by the user" translateable="false">
- <ph name="ZOOM_FACTOR">%1$d<ex>100</ex></ph> %%
- </message>
<!-- Media Capture and Streams -->
<message name="IDS_MEDIA_CAPTURE_NOTIFICATION_APP_NAME_SEPARATOR" desc="A separator between the app name and notification message.">
@@ -666,6 +654,31 @@
Sharing your screen
</message>
+ <!-- Bluetooth -->
+ <message name="IDS_CONNECTED_TO_BLUETOOTH_DEVICE_NOTIFICATION_TITLE" desc="Text to be shown as a notification when a website is connected to a Bluetooth device.">
+ Connected to a Bluetooth device
+ </message>
+ <message name="IDS_SCANNING_FOR_BLUETOOTH_DEVICES_NOTIFICATION_TITLE" desc="Text to be shown as a notification when a website is scanning for nearby Bluetooth devices.">
+ Scanning for Bluetooth devices
+ </message>
+ <message name="IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT" desc="Url of the current tab. The notification will display this text for the user to identify the tab to return to.">
+ Tap to return to <ph name="URL_OF_THE_CURRENT_TAB">%1$s<ex>https://googlechrome.github.io/samples/web-bluetooth/device-info.html</ex></ph>
+ </message>
+ <message name="IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT_INCOGNITO" desc="The notification will display this text for the user to return to the Incognito tab which has created the notification.">
+ Tap to return to the site
+ </message>
+
+ <!-- USB -->
+ <message name="IDS_CONNECTED_TO_USB_DEVICE_NOTIFICATION_TITLE" desc="Text to be shown as a notification when a website is connected to a USB device.">
+ Connected to a USB device
+ </message>
+ <message name="IDS_USB_NOTIFICATION_CONTENT_TEXT" desc="Url of the current tab. The notification will display this text for the user to identify the tab to return to.">
+ Tap to return to <ph name="URL_OF_THE_CURRENT_TAB">%1$s<ex>https://webusb.github.io/arduino/demos/console/</ex></ph>
+ </message>
+ <message name="IDS_USB_NOTIFICATION_CONTENT_TEXT_INCOGNITO" desc="The notification will display this text for the user to return to the Incognito tab which has created the notification.">
+ Tap to return to the site
+ </message>
+
<message name="IDS_SHARE_LINK_CHOOSER_TITLE" desc="title for the share dialog when sharing the current address [CHAR_LIMIT=27]">
Share via
</message>
@@ -886,6 +899,41 @@
<message name="IDS_ACCESSIBILITY_CAPTIONS_TITLE" desc="Title of the preference that allows the user to update caption settings.">
Captions
</message>
+
+ <!-- Page Zoom -->
+ <message name="IDS_PAGE_ZOOM_TITLE" desc="Title of the preference that allows the user to update the accessibility page zoom feature that applies to the web contents." translateable="false">
+ Zoom
+ </message>
+ <message name="IDS_PAGE_ZOOM_SUMMARY" desc="Summary of the preference that allows the user to update the accessibility page zoom feature that applies to the web contents." translateable="false">
+ Make the default text and images on sites smaller or larger
+ </message>
+ <message name="IDS_PAGE_ZOOM_DECREASE_ZOOM_BUTTON_TEXT" desc="Accessibility label for button to allow user to decrease page zoom" translateable="false">
+ Decrease zoom
+ </message>
+ <message name="IDS_PAGE_ZOOM_INCREASE_ZOOM_BUTTON_TEXT" desc="Accessibility label for button to allow user to increase page zoom" translateable="false">
+ Increase zoom
+ </message>
+ <message name="IDS_PAGE_ZOOM_FACTOR" desc="Text description of the current page zoom factor set by the user" translateable="false">
+ <ph name="ZOOM_FACTOR">%1$d<ex>100</ex></ph> %%
+ </message>
+ <message name="IDS_PAGE_ZOOM_ALWAYS_SHOW_PREFERENCE_TITLE" desc="Title of the preference that allows the user to always show the menu item for zoom" translateable="false">
+ Show zoom
+ </message>
+ <message name="IDS_PAGE_ZOOM_ALWAYS_SHOW_PREFERENCE_SUMMARY" desc="Summary of the preference that allows the user to always show the menu item for zoom." translateable="false">
+ Zoom in or out on a page from the main menu
+ </message>
+ <message name="IDS_PAGE_ZOOM_PREVIEW_TITLE" desc="Label of the section of sample text shown to the user to preview zoom level." translateable="false">
+ Preview
+ </message>
+ <message name="IDS_PAGE_ZOOM_PREVIEW_TEXT_TITLE" desc="Sample text displayed to user in settings to see/test a desired zoom level." translateable="false">
+ The Wonderful Wizard of Oz
+ </message>
+ <message name="IDS_PAGE_ZOOM_PREVIEW_TEXT_SUMMARY" desc="Sample text displayed to user in settings to see/test a desired zoom level." translateable="false">
+ Chapter 11: The Wonderful Emerald City of Oz
+ </message>
+ <message name="IDS_PAGE_ZOOM_PREVIEW_TEXT_ITEM" desc="Sample text displayed to user in settings to see/test a desired zoom level." translateable="false">
+ Even with eyes protected by the green spectacles Dorothy and her friends were at first dazzled by the
+ </message>
</messages>
</release>
</grit>
diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT.png.sha1
new file mode 100644
index 00000000000..def6aea07c7
--- /dev/null
+++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT.png.sha1
@@ -0,0 +1 @@
+23a25c72cf84a34cf49eb7ceecbfeee7d83c12b3 \ No newline at end of file
diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT_INCOGNITO.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT_INCOGNITO.png.sha1
new file mode 100644
index 00000000000..36fe77095c6
--- /dev/null
+++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_BLUETOOTH_NOTIFICATION_CONTENT_TEXT_INCOGNITO.png.sha1
@@ -0,0 +1 @@
+f69ba48545bf71cc2baf0d4c2208d7eaab74101f \ No newline at end of file
diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONNECTED_TO_BLUETOOTH_DEVICE_NOTIFICATION_TITLE.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONNECTED_TO_BLUETOOTH_DEVICE_NOTIFICATION_TITLE.png.sha1
new file mode 100644
index 00000000000..7d041f97bfa
--- /dev/null
+++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONNECTED_TO_BLUETOOTH_DEVICE_NOTIFICATION_TITLE.png.sha1
@@ -0,0 +1 @@
+079bd9f95cf6d844599500a69bdbe97358a94c3c \ No newline at end of file
diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONNECTED_TO_USB_DEVICE_NOTIFICATION_TITLE.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONNECTED_TO_USB_DEVICE_NOTIFICATION_TITLE.png.sha1
new file mode 100644
index 00000000000..30e0690dc98
--- /dev/null
+++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_CONNECTED_TO_USB_DEVICE_NOTIFICATION_TITLE.png.sha1
@@ -0,0 +1 @@
+3026bb76b3c5eac4eedea30479b49d1f6451978b \ No newline at end of file
diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha1
new file mode 100644
index 00000000000..cf0d5cdd3bd
--- /dev/null
+++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha1
@@ -0,0 +1 @@
+615c239640a2d400f684595cfdfe5716b11e8417 \ No newline at end of file
diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_SCANNING_FOR_BLUETOOTH_DEVICES_NOTIFICATION_TITLE.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_SCANNING_FOR_BLUETOOTH_DEVICES_NOTIFICATION_TITLE.png.sha1
new file mode 100644
index 00000000000..def6aea07c7
--- /dev/null
+++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_SCANNING_FOR_BLUETOOTH_DEVICES_NOTIFICATION_TITLE.png.sha1
@@ -0,0 +1 @@
+23a25c72cf84a34cf49eb7ceecbfeee7d83c12b3 \ No newline at end of file
diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_USB_NOTIFICATION_CONTENT_TEXT.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_USB_NOTIFICATION_CONTENT_TEXT.png.sha1
new file mode 100644
index 00000000000..30e0690dc98
--- /dev/null
+++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_USB_NOTIFICATION_CONTENT_TEXT.png.sha1
@@ -0,0 +1 @@
+3026bb76b3c5eac4eedea30479b49d1f6451978b \ No newline at end of file
diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_USB_NOTIFICATION_CONTENT_TEXT_INCOGNITO.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_USB_NOTIFICATION_CONTENT_TEXT_INCOGNITO.png.sha1
new file mode 100644
index 00000000000..5a81ea4407b
--- /dev/null
+++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_USB_NOTIFICATION_CONTENT_TEXT_INCOGNITO.png.sha1
@@ -0,0 +1 @@
+3dbeb783a2f85d8383e118391cc260ba115fc2ff \ No newline at end of file
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_af.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_af.xtb
index 3e79c7eaa8e..b149b48777b 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_af.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_af.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Versteek inligting</translation>
<translation id="3328801116991980348">Werfinligting</translation>
<translation id="3333961966071413176">Alle kontakte</translation>
+<translation id="3362437373201486687">Soek tans na Bluetooth-toestelle</translation>
<translation id="3386292677130313581">Vra voordat werwe toegelaat word om jou ligging te ken (aanbeveel)</translation>
<translation id="3538390592868664640">Verhoed werwe om 'n 3D-kaart van jou omgewing te skep of kameraposisie na te spoor</translation>
<translation id="3551268116566418498">Verlaat Incognitomodus?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Aflaai is voltooi</translation>
<translation id="3987993985790029246">Kopieer skakel</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> van ?</translation>
+<translation id="3992684624889376114">Meer oor hierdie bladsy</translation>
<translation id="4002066346123236978">Titel</translation>
<translation id="4008040567710660924">Laat webkoekies vir 'n spesifieke werf toe.</translation>
<translation id="4046123991198612571">Volgende snit</translation>
<translation id="4149994727733219643">Vereenvoudigde aansig vir webbladsye</translation>
<translation id="4165986682804962316">Werfinstellings</translation>
+<translation id="4169549551965910670">Aan ’n USB-toestel gekoppel</translation>
<translation id="4194328954146351878">Vra voor werwe toegelaat word om inligting op NFC-toestelle te sien en te verander (aanbeveel)</translation>
<translation id="4200726100658658164">Maak ligginginstellings oop</translation>
<translation id="4226663524361240545">Kennisgewings kan die toestel laat vibreer</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Altyd</translation>
<translation id="757524316907819857">Blokkeer werwe om beskermde inhoud te speel</translation>
<translation id="7577900504646297215">Bestuur belangstellings</translation>
+<translation id="7594634374516752650">Aan ’n Bluetooth-toestel gekoppel</translation>
<translation id="7649070708921625228">Hulp</translation>
<translation id="7658239707568436148">Kanselleer</translation>
<translation id="7781829728241885113">Gister</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_am.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_am.xtb
index ce0585811ee..71a440abcac 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_am.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_am.xtb
@@ -53,7 +53,7 @@
<translation id="2253414712144136228"><ph name="NAME_OF_LIST_ITEM" />ን አስወáŒá‹µ</translation>
<translation id="2289270750774289114">አንድ ጣቢያ በአቅራቢያ ያሉ ብሉቱዠመሣሪያዎችን áˆáˆáŒŽ ለማáŒáŠ˜á‰µ ሲáˆáˆáŒ ጠይቅ (የሚመከር)</translation>
<translation id="2315043854645842844">የደንበኛ ወገን á‹•á‹á‰…ና ማረጋገጫ áˆáˆ­áŒ« በስርዓተ-ክወናዠአይደገááˆá¢</translation>
-<translation id="2321958826496381788">ይህን በሚመች áˆáŠ”ታ ማንበብ እስኪችሉ ድረስ ተንሸራታቹን ይጎትቱትᢠበአንድ አንቀጽ ላይ áˆáˆˆá‰´ መታ ካደረጉ በኋላ ጽሑá ቢያንስ የዚህ ያህሠትáˆá‰€á‰µ ሊኖረዠይገባáˆá¢</translation>
+<translation id="2321958826496381788">ይህን በሚመች áˆáŠ”ታ ማንበብ እስኪችሉ ድረስ ተንሸራታቹን ይጎትቱትᢠበአንድ አንቀጽ ላይ áˆáˆˆá‰´ መታ ካደረጉ በኋላ ጽáˆá ቢያንስ የዚህ ያህሠትáˆá‰€á‰µ ሊኖረዠይገባáˆá¢</translation>
<translation id="2359808026110333948">ቀጥáˆ</translation>
<translation id="2379925928934107488">በሚቻáˆá‰ á‰µ ጊዜ Chrome ጠቆር ያለ ገጽታ ሲጠቀሠጠቆር ያለ ገጽታን ጣቢያዎች ላይ ተáŒá‰¥áˆ­</translation>
<translation id="2387895666653383613">የጽሑá áˆáŠ¬á‰µ</translation>
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">መረጃ ደብቅ</translation>
<translation id="3328801116991980348">የጣቢያ መረጃ</translation>
<translation id="3333961966071413176">áˆáˆ‰áˆ እá‹á‰‚ያዎች</translation>
+<translation id="3362437373201486687">የብሉቱዠመሣሪያዎችን በመቃኘት ላይ</translation>
<translation id="3386292677130313581">ጣቢያዎች አካባቢዎን እንዲያá‹á‰ ከመáቀድዎ በáŠá‰µ ይጠይቅ (የሚመከር)</translation>
<translation id="3538390592868664640">ጣቢያዎች የዙሪያዎ የ3ሠካርታ እንዳይáˆáŒ¥áˆ© ወይሠየካሜራ ቦታን እንዳይከታተሉ á‹«áŒá‹·á‰¸á‹</translation>
<translation id="3551268116566418498">ከማንáŠá‰µ የማያሳá‹á‰… áˆáŠá‰³ á‹­á‹áŒ£?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ማá‹áˆ¨á‹µ ተጠናቅቋáˆ</translation>
<translation id="3987993985790029246">አገናአቅዳ</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">ስለዚህ ገጽ</translation>
<translation id="4002066346123236978">ርዕስ</translation>
<translation id="4008040567710660924">የተወሰአጣቢያ ኩኪዎችን áቀድá¢</translation>
<translation id="4046123991198612571">ቀጣይ ትራክ</translation>
<translation id="4149994727733219643">ለድረ-ገጾች የተቃለለ እይታ</translation>
<translation id="4165986682804962316">የጣቢያ ቅንብሮች</translation>
+<translation id="4169549551965910670">ከዩኤስቢ መሣሪያ ጋር ተገናáŠá‰·áˆ</translation>
<translation id="4194328954146351878">ጣቢያዎች በኤንኤáሲ መሣሪያዎች ላይ መረጃ እንዲያዩ እና እንዲቀይሩ ከመáˆá‰€á‹± በáŠá‰µ ይጠይቅ (የሚመከር)</translation>
<translation id="4200726100658658164">የአካባቢ ቅንብሮችን ክáˆá‰µ</translation>
<translation id="4226663524361240545">ማሳወቂያዎች መሣሪያá‹áŠ• እንዲáŠá‹áˆ­ ሊያደርጉት ይችላሉ</translation>
@@ -185,7 +188,7 @@
<translation id="5502860503640766021"><ph name="PERMISSION_1" /> ተáˆá‰…ዶላቸዋáˆá£ <ph name="PERMISSION_2" /> ታáŒá‹°á‹‹áˆ</translation>
<translation id="5505264765875738116">ጣቢያዎች ማሳወቂያዎችን ለመላክ መጠየቅ አይችሉáˆ</translation>
<translation id="5516455585884385570">የማሳወቂያ ቅንብሮችን ክáˆá‰µ</translation>
-<translation id="5527111080432883924">ጣቢያዎች ከቅንጥብ ሰሌዳ ጽሑá እና áˆáˆµáˆŽá‰½áŠ• እንዲያáŠá‰¥á‰¡ ከመáቀድ በáŠá‰µ ጠይቅ (የሚመከር)</translation>
+<translation id="5527111080432883924">ጣቢያዎች ከቅንጥብ ሰሌዳ ጽáˆá እና áˆáˆµáˆŽá‰½áŠ• እንዲያáŠá‰¥á‰¡ ከመáቀድ በáŠá‰µ ጠይቅ (የሚመከር)</translation>
<translation id="5553374991681107062">የቅርብ ጊዜá‹</translation>
<translation id="5556459405103347317">ዳáŒáˆ ጫን</translation>
<translation id="5596627076506792578">ተጨማሪ አማራጮች</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">áˆáˆáŒŠá‹œ</translation>
<translation id="757524316907819857">ጣቢያዎች ጥበቃ የሚደረáŒáˆˆá‰µáŠ• ይዘት እንዳያጫá‹á‰± á‹«áŒá‹±</translation>
<translation id="7577900504646297215">áላጎቶችን አቀናብር</translation>
+<translation id="7594634374516752650">ከብሉቱዠመሣሪያ ጋር ተገናáŠá‰·áˆ</translation>
<translation id="7649070708921625228">እገዛ</translation>
<translation id="7658239707568436148">ይቅር</translation>
<translation id="7781829728241885113">ትናንት</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb
index a6139820fc4..2680d5ed770 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">إخÙاء المعلومات</translation>
<translation id="3328801116991980348">معلومات الموقع الإلكتروني</translation>
<translation id="3333961966071413176">جميع جهات الاتصال</translation>
+<translation id="3362437373201486687">جار٠البحث عن أجهزة تتضمّن بلوتوث.</translation>
<translation id="3386292677130313581">السؤال قبل السماح للمواقع الإلكترونية بمعرÙØ© الموقع الجغراÙÙŠ (موصى به)</translation>
<translation id="3538390592868664640">منع المواقع الإلكترونية من إنشاء خريطة ثلاثية الأبعاد للبيئة المحيطة بك أو تتبّÙع موضع الكاميرا</translation>
<translation id="3551268116566418498">أتريد مغادرة وضع التصÙØ­ المتخÙي؟</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">اكتمل التنزيل</translation>
<translation id="3987993985790029246">نسخ الرابط</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">لمحة حول هذه الصÙحة</translation>
<translation id="4002066346123236978">العنوان</translation>
<translation id="4008040567710660924">السماح لموقع إلكتروني معيّن بتشغيل ملÙات تعري٠الارتباط</translation>
<translation id="4046123991198612571">المقطع الصوتي التالي</translation>
<translation id="4149994727733219643">عرض مبسَّط لصÙحات الويب</translation>
<translation id="4165986682804962316">إعدادات المواقع الإلكترونية</translation>
+<translation id="4169549551965910670">â€ØªÙ… ربط الموقع الإلكتروني بجهاز USB.</translation>
<translation id="4194328954146351878">â€ÙŠØªÙ… طلب الإذن قبل السماح للمواقع الإلكترونية بالاطّلاع على المعلومات وتعديلها على الأجهزة المزوَّدة بتقنية NFC (إعداد Ù…Ùقترَح).</translation>
<translation id="4200726100658658164">Ùتح إعدادات الموقع الجغراÙÙŠ</translation>
<translation id="4226663524361240545">يمكن أن تؤدي الإشعارات إلى اهتزاز الجهاز</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">دومًا</translation>
<translation id="757524316907819857">منع المواقع الإلكترونية من تشغيل المحتوى المحمي</translation>
<translation id="7577900504646297215">إدارة الاهتمامات</translation>
+<translation id="7594634374516752650">تم الاتصال بجهاز يتضمّن بلوتوث.</translation>
<translation id="7649070708921625228">مساعدة</translation>
<translation id="7658239707568436148">إلغاء</translation>
<translation id="7781829728241885113">أمس</translation>
@@ -330,7 +334,7 @@
<translation id="8702612070107455751">سيتم محو أي بيانات متوÙّرة بلا اتصال بالإنترنت.</translation>
<translation id="8712637175834984815">تم</translation>
<translation id="8719283222052720129">â€ÙعّÙÙ„ الإذن لتطبيق <ph name="APP_NAME" /> ÙÙŠ <ph name="BEGIN_LINK" />إعدادات Android<ph name="END_LINK" />.</translation>
-<translation id="8725066075913043281">أعد المحاولة</translation>
+<translation id="8725066075913043281">إعادة المحاولة</translation>
<translation id="8730621377337864115">تم</translation>
<translation id="8737217482364735741">سيؤدي هذا الإجراء إلى محو جميع البيانات وملÙات تعري٠الارتباط المÙخزّنة من خلال <ph name="ORIGIN" />.</translation>
<translation id="8751914237388039244">اختيار صورة</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb
index c13f87898ae..10588244726 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">তথà§à¦¯ লà§à¦•à§à§±à¦¾à¦“ক</translation>
<translation id="3328801116991980348">ছাইটৰ তথà§à¦¯</translation>
<translation id="3333961966071413176">সকলো সমà§à¦ªà§°à§à¦•</translation>
+<translation id="3362437373201486687">বà§à¦²à§à¦Ÿà§à¦¥ ডিভাইচ সà§à¦•à§‡à¦¨ কৰি থকা হৈছে</translation>
<translation id="3386292677130313581">ছাইটসমূহে আপোনাৰ অৱসà§à¦¥à¦¾à¦¨ জনাৰ অনà§à¦®à¦¤à¦¿ দিয়াৰ পূৰà§à¦¬à§‡ সোধক (চà§à¦ªà¦¾à§°à¦¿à¦› কৰা হয়)</translation>
<translation id="3538390592868664640">ছাইটসমূহক আপোনাৰ চৌপাশৰ à¦à¦–ন 3D মেপ সৃষà§à¦Ÿà¦¿ কৰাৰ পৰা অথবা কেমেৰাৰ সà§à¦¥à¦¾à¦¨ টà§à§°à§‡à¦• কৰাৰ পৰা অৱৰোধ কৰক</translation>
<translation id="3551268116566418498">ইনক’গনিট’ ম’ডৰ পৰা বাহিৰ হ’বনে?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ডাউনল’ড সমà§à¦ªà§‚ৰà§à¦£ হ’ল</translation>
<translation id="3987993985790029246">লিংক পà§à§°à¦¤à¦¿à¦²à¦¿à¦ªà¦¿ কৰক</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">à¦à¦‡ পৃষà§à¦ à¦¾à¦–নৰ বিষয়ে</translation>
<translation id="4002066346123236978">শিৰোনাম</translation>
<translation id="4008040567710660924">কোনো নিৰà§à¦¦à¦¿à¦·à§à¦Ÿ ছাইটৰ বাবে কà§à¦•à¦¿à¦¸à¦®à§‚হক অনà§à¦®à¦¤à¦¿ দিয়ক।</translation>
<translation id="4046123991198612571">পৰৱৰà§à¦¤à§€ টà§à§°à§‡à¦•</translation>
<translation id="4149994727733219643">ৱেবপৃষà§à¦ à¦¾à§° বাবে সৰলকৰণ কৰা ভিউ</translation>
<translation id="4165986682804962316">ছাইটৰ ছেটিংসমূহ</translation>
+<translation id="4169549551965910670">à¦à¦Ÿà¦¾ ইউà¦à¦›à¦¬à¦¿ ডিভাইচৰ সৈতে সংযোগ কৰা হৈছে</translation>
<translation id="4194328954146351878">ছাইটসমূহক NFC ডিভাইচত তথà§à¦¯ চোৱা আৰৠসলনি কৰাৰ অনà§à¦®à¦¤à¦¿ দিয়াৰ পূৰà§à¦¬à§‡ সোধক (চà§à¦ªà¦¾à§°à¦¿à¦› কৰা হয়)</translation>
<translation id="4200726100658658164">অৱসà§à¦¥à¦¾à¦¨à§° ছেটিংসমূহ খোলক</translation>
<translation id="4226663524361240545">জাননীয়ে ডিভাইচটো কমà§à¦ªà¦¨ কৰিব পাৰে</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">সদায়</translation>
<translation id="757524316907819857">সà§à§°à¦•à§à¦·à¦¿à¦¤ সমল পà§à¦²à§‡â€™ কৰাৰ পৰা ছাইটসমূহক অৱৰোধ কৰক</translation>
<translation id="7577900504646297215">আগà§à§°à¦¹à¦¸à¦®à§‚হ পৰিচালনা কৰক</translation>
+<translation id="7594634374516752650">à¦à¦Ÿà¦¾ বà§à¦²à§à¦Ÿà§à¦¥ ডিভাইচৰ সৈতে সংযà§à¦•à§à¦¤ হৈছে</translation>
<translation id="7649070708921625228">সহায়</translation>
<translation id="7658239707568436148">বাতিল কৰক</translation>
<translation id="7781829728241885113">কালি</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_az.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_az.xtb
index 16da0b6a4e5..bb79565588f 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_az.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_az.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Məlumatı gizlədin</translation>
<translation id="3328801116991980348">Sayt haqqında</translation>
<translation id="3333961966071413176">Bütün kontaktar</translation>
+<translation id="3362437373201486687">Bluetooth cihazları skan edilir</translation>
<translation id="3386292677130313581">Saytlara məkanınızı bilmək icazəsi verməmişdən əvvəl soruşun (tövsiyə olunur)</translation>
<translation id="3538390592868664640">Saytların ətrafınızdakı sahələrin 3D xəritəsini yaratmasına və ya kamera mövqeyini izləməsinə qarşı blok qoyun</translation>
<translation id="3551268116566418498">Anonim rejimdən çıxılsın?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Endirmə tamamdır</translation>
<translation id="3987993985790029246">Linki kopyalayın</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Bu səhifə haqqında</translation>
<translation id="4002066346123236978">Başlıq</translation>
<translation id="4008040567710660924">Xüsusi sayt üçün kukilərə icazə verin.</translation>
<translation id="4046123991198612571">Növbəti trek</translation>
<translation id="4149994727733219643">İnternet səhifələr üçün sadələşdirilmiş görünüş</translation>
<translation id="4165986682804962316">Sayt ayarları</translation>
+<translation id="4169549551965910670">USB cihazına qoşulub</translation>
<translation id="4194328954146351878">Saytların NFC cihazlarındakı məlumatları görməsi və dəyişməsinə icazə verməzdən əvvəl soruşun (tövsiyə edilir)</translation>
<translation id="4200726100658658164">Məkan Ayarlarını açın</translation>
<translation id="4226663524361240545">Bildirişlər cihazı titrədə bilər</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Həmişə</translation>
<translation id="757524316907819857">Saytların qorunan kontenti oxutmasını blok edin</translation>
<translation id="7577900504646297215">Maraqları idarə edin</translation>
+<translation id="7594634374516752650">Bluetooth cihazına qoşulub</translation>
<translation id="7649070708921625228">Yardım</translation>
<translation id="7658239707568436148">Ləğv edin</translation>
<translation id="7781829728241885113">Dünən</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_be.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_be.xtb
index 3c18a636859..42e50463c23 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_be.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_be.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Схаваць інфармацыю</translation>
<translation id="3328801116991980348">ЗвеÑткі пра Ñайт</translation>
<translation id="3333961966071413176">УÑе кантакты</translation>
+<translation id="3362437373201486687">Ідзе пошук прылад з Bluetooth</translation>
<translation id="3386292677130313581">Пытацца, перш чым дазволіць Ñайтам атрымліваць інфармацыю пра ваша меÑцазнаходжанне (Ñ€Ñкамендуецца)</translation>
<translation id="3538390592868664640">Забараніць Ñайтам Ñтвараць 3D-карту вашага аÑÑÑ€Ð¾Ð´Ð´Ð·Ñ Ñ– адÑочваць Ñтановішча камеры</translation>
<translation id="3551268116566418498">ВыйÑці з Ñ€Ñжыму інкогніта?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Спампоўка завершана</translation>
<translation id="3987993985790029246">Скапіраваць ÑпаÑылку</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Пра гÑту Ñтаронку</translation>
<translation id="4002066346123236978">Ðазва</translation>
<translation id="4008040567710660924">Дазвол выкарыÑÑ‚Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð°Ñž cookie ад канкрÑтнага Ñайта.</translation>
<translation id="4046123991198612571">ÐаÑтупны Ñ‚Ñ€Ñк</translation>
<translation id="4149994727733219643">Спрошчаны выглÑд Ð´Ð»Ñ Ð²Ñб-Ñтаронак</translation>
<translation id="4165986682804962316">Ðалады Ñайта</translation>
+<translation id="4169549551965910670">Выканана падключÑнне да прылады USB</translation>
<translation id="4194328954146351878">Пытацца, перш чым дазвалÑць Ñайтам праглÑдаць Ñ– змÑнÑць інфармацыю на прыладах NFC (Ñ€Ñкамендуецца)</translation>
<translation id="4200726100658658164">Ðдкрыць налады меÑцазнаходжаннÑ</translation>
<translation id="4226663524361240545">ÐпавÑшчÑнні могуць уключаць вібрацыю прылады</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ЗаўÑёды</translation>
<translation id="757524316907819857">Блакіраваць прайграванне абароненага змеÑціва на Ñайтах</translation>
<translation id="7577900504646297215">Кіраваць інтарÑÑамі</translation>
+<translation id="7594634374516752650">Ð’Ñб-Ñайт падключаны да прылады з Bluetooth</translation>
<translation id="7649070708921625228">Даведка</translation>
<translation id="7658239707568436148">СкаÑаваць</translation>
<translation id="7781829728241885113">Учора</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bg.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bg.xtb
index d192bc225b7..372358c97c7 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bg.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bg.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Скриване на информациÑта</translation>
<translation id="3328801116991980348">Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° Ñайта</translation>
<translation id="3333961966071413176">Ð’Ñички контакти</translation>
+<translation id="3362437373201486687">Сканира Ñе за уÑтройÑтва Ñ Bluetooth</translation>
<translation id="3386292677130313581">Извеждане на запитване, преди на Ñайтовете да Ñе разреши доÑтъп до меÑтоположението ви (препоръчително)</translation>
<translation id="3538390592868664640">Блокиране на Ñайтовете, така че да не могат да Ñъздават триизмерна карта на заобикалÑщата ви Ñреда или да ÑледÑÑ‚ позициÑта на камерата</translation>
<translation id="3551268116566418498">Излизане от режим „инкогнито“?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ИзтеглÑнето завърши</translation>
<translation id="3987993985790029246">Връзка: Коп.</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> от ?</translation>
+<translation id="3992684624889376114">Ð’Ñичко за тази Ñтраница</translation>
<translation id="4002066346123236978">Заглавие</translation>
<translation id="4008040567710660924">Разрешаване на „биÑквитките“ за конкретен Ñайт.</translation>
<translation id="4046123991198612571">Следващ запиÑ</translation>
<translation id="4149994727733219643">ОпроÑтен изглед на уеб Ñтраниците</translation>
<translation id="4165986682804962316">ÐаÑтройки за Ñайта</translation>
+<translation id="4169549551965910670">УÑтановена е връзка Ñ USB уÑтройÑтво</translation>
<translation id="4194328954146351878">Извеждане на запитване, преди да Ñе разреши на Ñайтовете да четат и променÑÑ‚ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð½Ð° уÑтройÑтва Ñ NFC (препоръчително)</translation>
<translation id="4200726100658658164">ОтварÑне на наÑтройките за меÑтоположението</translation>
<translation id="4226663524361240545">Възможно е уÑтройÑтвото да вибрира при извеÑтиÑ</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Винаги</translation>
<translation id="757524316907819857">Блокиране на възможноÑтта на Ñайтовете да възпроизвеждат защитено Ñъдържание</translation>
<translation id="7577900504646297215">Управление на интереÑите</translation>
+<translation id="7594634374516752650">УÑтановена е връзка Ñ ÑƒÑтройÑтво Ñ Bluetooth</translation>
<translation id="7649070708921625228">Помощ</translation>
<translation id="7658239707568436148">Отказ</translation>
<translation id="7781829728241885113">Вчера</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bn.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bn.xtb
index 79cd2247bc3..cac423d8412 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bn.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bn.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">তথà§à¦¯ লà§à¦•à¦¾à¦¨</translation>
<translation id="3328801116991980348">সাইট তথà§à¦¯</translation>
<translation id="3333961966071413176">সব পরিচিতি</translation>
+<translation id="3362437373201486687">বà§à¦²à§à¦Ÿà§à¦¥ ডিভাইসের জনà§à¦¯ সà§à¦•à§à¦¯à¦¾à¦¨ করা হচà§à¦›à§‡</translation>
<translation id="3386292677130313581">সাইটগà§à¦²à¦¿à¦•à§‡ আপনার লোকেশন জানতে দিতে মঞà§à¦œà§à¦°à¦¿ দেওয়ার আগে জিজà§à¦žà¦¾à¦¸à¦¾ করà§à¦¨ (পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¿à¦¤)</translation>
<translation id="3538390592868664640">আপনার আশেপাশের à¦à¦²à¦¾à¦•à¦¾à¦° à¦à¦•à¦Ÿà¦¿ 3D মà§à¦¯à¦¾à¦ª তৈরি করা বা কà§à¦¯à¦¾à¦®à§‡à¦°à¦¾à¦° অবসà§à¦¥à¦¾à¦¨ টà§à¦°à§à¦¯à¦¾à¦• করার কাজে নিযà§à¦•à§à¦¤ সাইটগà§à¦²à¦¿à¦•à§‡ বà§à¦²à¦• করে দিন</translation>
<translation id="3551268116566418498">ছদà§à¦®à¦¬à§‡à¦¶à§€ মোড ছেড়ে বেরিয়ে আসবেন?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ডাউনলোড সমà§à¦ªà§‚রà§à¦£</translation>
<translation id="3987993985790029246">লিঙà§à¦• কপি করà§à¦¨</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">à¦à¦‡ পৃষà§à¦ à¦¾ সমà§à¦ªà¦°à§à¦•à§‡</translation>
<translation id="4002066346123236978">শিরোনাম</translation>
<translation id="4008040567710660924">কোনও নিরà§à¦¦à¦¿à¦·à§à¦Ÿ সাইটের জনà§à¦¯ কà§à¦•à¦¿à¦•à§‡ অনà§à¦®à¦¤à¦¿ দিন।</translation>
<translation id="4046123991198612571">পরবরà§à¦¤à§€ টà§à¦°à§à¦¯à¦¾à¦•</translation>
<translation id="4149994727733219643">ওয়েব পৃষà§à¦ à¦¾à¦° জনà§à¦¯ সরলীকৃত ভিউ</translation>
<translation id="4165986682804962316">সাইটের সেটিংস</translation>
+<translation id="4169549551965910670">à¦à¦•à¦Ÿà¦¿ ইউà¦à¦¸à¦¬à¦¿ ডিভাইসে কানেকà§à¦Ÿ করা হয়েছে</translation>
<translation id="4194328954146351878">সাইটগà§à¦²à¦¿à¦•à§‡ NFC ডিভাইসে তথà§à¦¯ দেখতে à¦à¦¬à¦‚ পরিবরà§à¦¤à¦¨ করার অনà§à¦®à¦¤à¦¿ দেওয়ার আগে জিজà§à¦žà¦¾à¦¸à¦¾ করে নিন (পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¿à¦¤)</translation>
<translation id="4200726100658658164">লোকেশন সেটিংস খà§à¦²à§à¦¨</translation>
<translation id="4226663524361240545">বিজà§à¦žà¦ªà§à¦¤à¦¿ আসলে ডিভাইস ভাইবà§à¦°à§‡à¦Ÿ হতে পারে</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">সবসময়</translation>
<translation id="757524316907819857">সà§à¦°à¦•à§à¦·à¦¿à¦¤ কনà§à¦Ÿà§‡à¦¨à§à¦Ÿ চালানো থেকে সাইটকে বà§à¦²à¦• করà§à¦¨</translation>
<translation id="7577900504646297215">আপনার পছনà§à¦¦ মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨</translation>
+<translation id="7594634374516752650">বà§à¦²à§à¦Ÿà§à¦¥ ডিভাইসের সাথে কানেকà§à¦Ÿ করা হয়েছে</translation>
<translation id="7649070708921625228">সহায়তা</translation>
<translation id="7658239707568436148">বাতিল</translation>
<translation id="7781829728241885113">গতকাল</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bs.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bs.xtb
index 908ad16eb30..34b9e481e5d 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bs.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bs.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Sakrij informacije</translation>
<translation id="3328801116991980348">Informacije o web lokaciji</translation>
<translation id="3333961966071413176">Svi kontakti</translation>
+<translation id="3362437373201486687">Skeniranje Bluetooth uređaja</translation>
<translation id="3386292677130313581">Web lokacije moraju tražiti dozvolu za pristup lokaciji (preporuÄeno)</translation>
<translation id="3538390592868664640">Web lokacijama je blokirano kreiranje 3D mape vašeg okruženja ili praćenje položaja kamere</translation>
<translation id="3551268116566418498">Napustiti anonimni naÄina rada?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Preuzimanje je završeno</translation>
<translation id="3987993985790029246">Kopiraj link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">O ovoj stranici</translation>
<translation id="4002066346123236978">Naslov</translation>
<translation id="4008040567710660924">Omogućavanje kolaÄića za odreÄ‘enu web lokaciju.</translation>
<translation id="4046123991198612571">Sljedeća pjesma</translation>
<translation id="4149994727733219643">Pojednostavljeni prikaz za web lokacije</translation>
<translation id="4165986682804962316">Postavke web-lokacije</translation>
+<translation id="4169549551965910670">Povezano s USB uređajem</translation>
<translation id="4194328954146351878">Web lokacije moraju tražiti odobrenje za pregled i izmjenu informacija na NFC ureÄ‘ajima (preporuÄeno)</translation>
<translation id="4200726100658658164">Otvaranje postavki lokacije</translation>
<translation id="4226663524361240545">Obavještenja mogu aktivirati vibraciju uređaja</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Uvijek</translation>
<translation id="757524316907819857">Blokirajte reproduciranje zaštićenog sadržaja na web lokacijama</translation>
<translation id="7577900504646297215">Upravljajte interesovanjima</translation>
+<translation id="7594634374516752650">Povezano s Bluetooth uređajem</translation>
<translation id="7649070708921625228">Pomoć</translation>
<translation id="7658239707568436148">Otkaži</translation>
<translation id="7781829728241885113">JuÄer</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ca.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ca.xtb
index c26602e53dc..a2c32eeee6c 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ca.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ca.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Amaga la informació</translation>
<translation id="3328801116991980348">Informació del lloc web</translation>
<translation id="3333961966071413176">Tots els contactes</translation>
+<translation id="3362437373201486687">S'estan cercant dispositius Bluetooth</translation>
<translation id="3386292677130313581">Pregunta abans de permetre que els llocs web sàpiguen la teva ubicació (opció recomanada)</translation>
<translation id="3538390592868664640">Impedeix que els llocs web creïn un mapa en 3D del teu entorn o que facin un seguiment de la posició de la càmera</translation>
<translation id="3551268116566418498">Vols sortir del mode d'incògnit?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">S'ha completat la baixada</translation>
<translation id="3987993985790029246">Copia l'enllaç</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Sobre aquesta pàgina</translation>
<translation id="4002066346123236978">Títol</translation>
<translation id="4008040567710660924">Permet les galetes d'un lloc web concret.</translation>
<translation id="4046123991198612571">Pista següent</translation>
<translation id="4149994727733219643">Visualització simplificada de pàgines web</translation>
<translation id="4165986682804962316">Configuració del lloc web</translation>
+<translation id="4169549551965910670">Connectat a un dispositiu USB</translation>
<translation id="4194328954146351878">Pregunta abans de permetre que els llocs web vegin i canviïn la informació que hi ha als dispositius amb NFC (opció recomanada)</translation>
<translation id="4200726100658658164">Obre la configuració d'ubicació</translation>
<translation id="4226663524361240545">És possible que les notificacions facin vibrar el dispositiu</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Sempre</translation>
<translation id="757524316907819857">Impedeix que els llocs web reprodueixin contingut protegit</translation>
<translation id="7577900504646297215">Gestiona els interessos</translation>
+<translation id="7594634374516752650">Connectat a un dispositiu Bluetooth</translation>
<translation id="7649070708921625228">Ajuda</translation>
<translation id="7658239707568436148">Cancel·la</translation>
<translation id="7781829728241885113">Ahir</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cs.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cs.xtb
index e22718b0da2..f4aa434c2cb 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cs.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cs.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Skrýt informace</translation>
<translation id="3328801116991980348">Informace o stránkách</translation>
<translation id="3333961966071413176">VÅ¡echny kontakty</translation>
+<translation id="3362437373201486687">Vyhledávání zařízení Bluetooth</translation>
<translation id="3386292677130313581">Pokud web bude chtít znát vaÅ¡i polohu, zobrazit dotaz (doporuÄeno)</translation>
<translation id="3538390592868664640">Bránit webům ve vytváření 3D mapy okolí a ve sledování polohy kamery</translation>
<translation id="3551268116566418498">UkonÄit anonymní režim?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Stahování bylo dokonÄeno</translation>
<translation id="3987993985790029246">Kopírovat odkaz</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">O této stránce</translation>
<translation id="4002066346123236978">Název</translation>
<translation id="4008040567710660924">Povolit soubory cookie pro konkrétní web.</translation>
<translation id="4046123991198612571">Další skladba</translation>
<translation id="4149994727733219643">Zjednodušené zobrazení webových stránek</translation>
<translation id="4165986682804962316">Nastavení webu</translation>
+<translation id="4169549551965910670">Připojeno k zařízení USB</translation>
<translation id="4194328954146351878">Pokud web bude chtít Äíst a mÄ›nit informace na zařízeních NFC, zobrazit dotaz (doporuÄeno)</translation>
<translation id="4200726100658658164">Otevřít nastavení polohy</translation>
<translation id="4226663524361240545">Oznámení mohou aktivovat vibraci</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Vždy</translation>
<translation id="757524316907819857">Bránit webům v přehrávání chráněného obsahu</translation>
<translation id="7577900504646297215">Spravovat zájmy</translation>
+<translation id="7594634374516752650">Telefon je připojen k zařízení Bluetooth</translation>
<translation id="7649070708921625228">Nápověda</translation>
<translation id="7658239707568436148">Zrušit</translation>
<translation id="7781829728241885113">VÄera</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cy.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cy.xtb
index c63716098d7..6f3ba218511 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cy.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cy.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Cuddio Gwybodaeth</translation>
<translation id="3328801116991980348">Gwybodaeth am y wefan</translation>
<translation id="3333961966071413176">Pob cyswllt</translation>
+<translation id="3362437373201486687">Wrthi'n sganio am ddyfeisiau Bluetooth</translation>
<translation id="3386292677130313581">Gofyn cyn caniatáu i wefannau wybod eich lleoliad (argymhellir)</translation>
<translation id="3538390592868664640">Rhwystro gwefannau rhag creu map 3D o'ch amgylchoedd neu olrhain safle'r camera</translation>
<translation id="3551268116566418498">Gadael y modd Anhysbys?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Lawrlwytho wedi'i gwblhau</translation>
<translation id="3987993985790029246">Copïo'r ddolen</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Ynghylch y dudalen hon</translation>
<translation id="4002066346123236978">Teitl</translation>
<translation id="4008040567710660924">Caniatáu cwcis ar gyfer gwefan benodol.</translation>
<translation id="4046123991198612571">Trac nesaf</translation>
<translation id="4149994727733219643">Gwedd syml ar gyfer tudalennau gwe</translation>
<translation id="4165986682804962316">Gosodiadau gwefan</translation>
+<translation id="4169549551965910670">Wedi'i gysylltu â dyfais USB</translation>
<translation id="4194328954146351878">Gofyn cyn caniatáu i wefannau weld a newid gwybodaeth ar ddyfeisiau NFC (argymhellir)</translation>
<translation id="4200726100658658164">Agor Gosodiadau Lleoliad</translation>
<translation id="4226663524361240545">Mae'n bosib y bydd hysbysiadau ddirgrynu’r ddyfais</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Bob tro</translation>
<translation id="757524316907819857">Rhwystro gwefannau rhag chwarae cynnwys gwarchodedig</translation>
<translation id="7577900504646297215">Rheoli diddordebau</translation>
+<translation id="7594634374516752650">Wedi cysylltu â dyfais Bluetooth</translation>
<translation id="7649070708921625228">Cymorth</translation>
<translation id="7658239707568436148">Canslo</translation>
<translation id="7781829728241885113">Ddoe</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_da.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_da.xtb
index 6f9baa109a2..250e0a4be56 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_da.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_da.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Skjul oplysninger</translation>
<translation id="3328801116991980348">Webstedoplysninger</translation>
<translation id="3333961966071413176">Alle kontakter</translation>
+<translation id="3362437373201486687">Søger efter Bluetooth-enheder</translation>
<translation id="3386292677130313581">Spørg, om websites må få adgang til din lokation (anbefales)</translation>
<translation id="3538390592868664640">Bloker oprettelsen af 3D-kort over dine omgivelser eller registrering af kamerapositionen for websites</translation>
<translation id="3551268116566418498">Forlad inkognitotilstand?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Download fuldført</translation>
<translation id="3987993985790029246">Kopiér linket</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Om denne side</translation>
<translation id="4002066346123236978">Titel</translation>
<translation id="4008040567710660924">Tillad cookies for et bestemt website.</translation>
<translation id="4046123991198612571">Næste nummer</translation>
<translation id="4149994727733219643">Enkel visning af websider</translation>
<translation id="4165986682804962316">Websiteindstillinger</translation>
+<translation id="4169549551965910670">Forbundet til en USB-enhed</translation>
<translation id="4194328954146351878">Spørg, om websites må få adgang til og ændre oplysninger på NFC-enheder (anbefales)</translation>
<translation id="4200726100658658164">Ã…bn lokationsindstillinger</translation>
<translation id="4226663524361240545">Notifikationer kan få enheden til at vibrere</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Altid</translation>
<translation id="757524316907819857">Bloker afspilning af beskyttet indhold på websites</translation>
<translation id="7577900504646297215">Administrer interesser</translation>
+<translation id="7594634374516752650">Forbundet til en Bluetooth-enhed</translation>
<translation id="7649070708921625228">Hjælp</translation>
<translation id="7658239707568436148">Annuller</translation>
<translation id="7781829728241885113">I går</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_de.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_de.xtb
index 6659793fed0..09cda35c78d 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_de.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_de.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Informationen ausblenden</translation>
<translation id="3328801116991980348">Websiteinformationen</translation>
<translation id="3333961966071413176">Alle Kontakte</translation>
+<translation id="3362437373201486687">Nach Bluetooth-Geräten wird gesucht</translation>
<translation id="3386292677130313581">Nachfragen, bevor Websites mein Standort angezeigt wird (empfohlen)</translation>
<translation id="3538390592868664640">Websites daran hindern, eine 3D-Karte meiner Umgebung zu erstellen oder die Kameraposition zu verfolgen</translation>
<translation id="3551268116566418498">Inkognitomodus deaktivieren?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Download abgeschlossen</translation>
<translation id="3987993985790029246">Link kopieren</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> von ?</translation>
+<translation id="3992684624889376114">Ãœber diese Seite</translation>
<translation id="4002066346123236978">Titel</translation>
<translation id="4008040567710660924">Cookies für eine bestimmte Website werden zugelassen.</translation>
<translation id="4046123991198612571">Nächster Titel</translation>
<translation id="4149994727733219643">Vereinfachte Ansicht für Webseiten</translation>
<translation id="4165986682804962316">Website-Einstellungen</translation>
+<translation id="4169549551965910670">Mit einem USB-Gerät verbunden</translation>
<translation id="4194328954146351878">Nachfragen, bevor Websites erlaubt wird, Informationen auf NFC-Geräten abzurufen und zu ändern (empfohlen)</translation>
<translation id="4200726100658658164">Standorteinstellungen öffnen</translation>
<translation id="4226663524361240545">Bei Benachrichtigungen kann das Gerät vibrieren</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Immer</translation>
<translation id="757524316907819857">Wiedergabe von geschützten Inhalten für Websites blockieren</translation>
<translation id="7577900504646297215">Interessen verwalten</translation>
+<translation id="7594634374516752650">Mit einem Bluetooth-Gerät verbunden</translation>
<translation id="7649070708921625228">Hilfe</translation>
<translation id="7658239707568436148">Abbrechen</translation>
<translation id="7781829728241885113">Gestern</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_el.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_el.xtb
index 5be6b48533e..defad2d523f 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_el.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_el.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">ΑπόκÏυψη πληÏοφοÏιών</translation>
<translation id="3328801116991980348">ΠληÏοφοÏίες ιστοτόπου</translation>
<translation id="3333961966071413176">Όλες οι επαφές</translation>
+<translation id="3362437373201486687">ΣάÏωση για συσκευές Bluetooth</translation>
<translation id="3386292677130313581">Îα γίνεται εÏώτηση Ï€ÏÎ¿Ï„Î¿Ï ÎµÏ€Î¹Ï„Ïαπεί η κοινοποίηση της τοποθεσίας σας σε ιστότοπους (συνιστάται)</translation>
<translation id="3538390592868664640">Αποκλείστε ιστοτόπους από τη δημιουÏγία ενός Ï„Ïισδιάστατου χάÏτη του πεÏιβάλλοντα χώÏου σας ή την παÏακολοÏθηση της θέσης της κάμεÏας.</translation>
<translation id="3551268116566418498">Έξοδος από ανώνυμη πεÏιήγηση;</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ΟλοκλήÏωση λήψης</translation>
<translation id="3987993985790029246">Αντ. συνδ.</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Σχετικά με αυτήν τη σελίδα</translation>
<translation id="4002066346123236978">Τίτλος</translation>
<translation id="4008040567710660924">ΕπιτÏέπει τα cookie για έναν συγκεκÏιμένο ιστότοπο.</translation>
<translation id="4046123991198612571">Επόμενο κομμάτι</translation>
<translation id="4149994727733219643">Απλοποιημένη Ï€Ïοβολή για ιστοσελίδες</translation>
<translation id="4165986682804962316">Ρυθμίσεις ιστότοπου</translation>
+<translation id="4169549551965910670">Συνδέθηκε σε συσκευή USB</translation>
<translation id="4194328954146351878">Îα γίνεται εÏώτηση Ï€Ïιν επιτÏαπεί στους ιστοτόπους να βλέπουν και να αλλάζουν πληÏοφοÏίες σε συσκευές NFC (συνιστάται)</translation>
<translation id="4200726100658658164">Άνοιγμα Ïυθμίσεων τοποθεσίας</translation>
<translation id="4226663524361240545">Κατά τη λήψη ειδοποιήσεων ενδέχεται να δονείται η συσκευή</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Πάντα</translation>
<translation id="757524316907819857">Αποκλεισμός αναπαÏαγωγής Ï€Ïοστατευόμενου πεÏιεχομένου από ιστοτόπους</translation>
<translation id="7577900504646297215">ΔιαχείÏιση ενδιαφεÏόντων</translation>
+<translation id="7594634374516752650">Συνδέθηκε σε μια συσκευή Bluetooth</translation>
<translation id="7649070708921625228">Βοήθεια</translation>
<translation id="7658239707568436148">ΑκÏÏωση</translation>
<translation id="7781829728241885113">Χθες</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_en-GB.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_en-GB.xtb
index e47dafdbbe2..0958c7310af 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_en-GB.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_en-GB.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Hide info</translation>
<translation id="3328801116991980348">Site information</translation>
<translation id="3333961966071413176">All contacts</translation>
+<translation id="3362437373201486687">Scanning for Bluetooth devices</translation>
<translation id="3386292677130313581">Ask before allowing sites to know your location (recommended)</translation>
<translation id="3538390592868664640">Block sites from creating a 3D map of your surroundings or tracking camera position</translation>
<translation id="3551268116566418498">Leave Incognito mode?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Download complete</translation>
<translation id="3987993985790029246">Copy link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">About this page</translation>
<translation id="4002066346123236978">Title</translation>
<translation id="4008040567710660924">Allow cookies for a specific site.</translation>
<translation id="4046123991198612571">Next track</translation>
<translation id="4149994727733219643">Simplified view for web pages</translation>
<translation id="4165986682804962316">Site settings</translation>
+<translation id="4169549551965910670">Connected to a USB device</translation>
<translation id="4194328954146351878">Ask before allowing sites to see and change information on NFC devices (recommended)</translation>
<translation id="4200726100658658164">Open location settings</translation>
<translation id="4226663524361240545">Notifications may vibrate the device</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Always</translation>
<translation id="757524316907819857">Block sites from playing protected content</translation>
<translation id="7577900504646297215">Manage interests</translation>
+<translation id="7594634374516752650">Connected to a Bluetooth device</translation>
<translation id="7649070708921625228">Help</translation>
<translation id="7658239707568436148">Cancel</translation>
<translation id="7781829728241885113">Yesterday</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es-419.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es-419.xtb
index 1f4a0b67ede..53d5e1a01e4 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es-419.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es-419.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ocultar información</translation>
<translation id="3328801116991980348">Información del sitio</translation>
<translation id="3333961966071413176">Todos los contactos</translation>
+<translation id="3362437373201486687">Buscando dispositivos Bluetooth</translation>
<translation id="3386292677130313581">Preguntar antes de permitir que los sitios conozcan tu ubicación (recomendado)</translation>
<translation id="3538390592868664640">No permitir que los sitios creen un mapa 3D de tu entorno ni hagan un seguimiento de la posición de la cámara</translation>
<translation id="3551268116566418498">¿Salir del modo Incógnito?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Descarga completa</translation>
<translation id="3987993985790029246">Copiar vínculo</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Acerca de esta página</translation>
<translation id="4002066346123236978">Título</translation>
<translation id="4008040567710660924">Permite las cookies para un sitio específico.</translation>
<translation id="4046123991198612571">Siguiente pista</translation>
<translation id="4149994727733219643">Vista simplificada para páginas web</translation>
<translation id="4165986682804962316">Configuración de sitios</translation>
+<translation id="4169549551965910670">Se conectó a un dispositivo USB</translation>
<translation id="4194328954146351878">Preguntar antes de permitir que los sitios vean y cambien la información de los dispositivos NFC (recomendado)</translation>
<translation id="4200726100658658164">Abrir la configuración de la ubicación</translation>
<translation id="4226663524361240545">Es posible que las notificaciones hagan vibrar el dispositivo</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Siempre</translation>
<translation id="757524316907819857">Impedir que los sitios reproduzcan contenido protegido</translation>
<translation id="7577900504646297215">Administrar intereses</translation>
+<translation id="7594634374516752650">Conectado a un dispositivo Bluetooth</translation>
<translation id="7649070708921625228">Ayuda</translation>
<translation id="7658239707568436148">Cancelar</translation>
<translation id="7781829728241885113">Ayer</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es.xtb
index 22a44b9ff65..462327ed897 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ocultar información</translation>
<translation id="3328801116991980348">Información del sitio</translation>
<translation id="3333961966071413176">Todos los contactos</translation>
+<translation id="3362437373201486687">Buscando dispositivos Bluetooth</translation>
<translation id="3386292677130313581">Preguntar antes de permitir que los sitios detecten tu ubicación (recomendado)</translation>
<translation id="3538390592868664640">No permitir que los sitios creen un mapa 3D de tu entorno o hagan un seguimiento de la posición de la cámara</translation>
<translation id="3551268116566418498">¿Salir del modo Incógnito?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Descarga completa</translation>
<translation id="3987993985790029246">Copiar enlace</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Acerca de esta página</translation>
<translation id="4002066346123236978">Título</translation>
<translation id="4008040567710660924">Permitir cookies en un sitio específico.</translation>
<translation id="4046123991198612571">Pista siguiente</translation>
<translation id="4149994727733219643">Vista simplificada de páginas web</translation>
<translation id="4165986682804962316">Configuración del sitio</translation>
+<translation id="4169549551965910670">Conectado a un dispositivo USB</translation>
<translation id="4194328954146351878">Pregunta antes de permitir que los sitios vean y cambien la información de los dispositivos NFC (recomendado)</translation>
<translation id="4200726100658658164">Abrir Ajustes de ubicación</translation>
<translation id="4226663524361240545">Es posible que las notificaciones hagan que el dispositivo vibre</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Siempre</translation>
<translation id="757524316907819857">No permitir que los sitios reproduzcan contenido protegido</translation>
<translation id="7577900504646297215">Gestionar intereses</translation>
+<translation id="7594634374516752650">Conectado a un dispositivo Bluetooth</translation>
<translation id="7649070708921625228">Ayuda</translation>
<translation id="7658239707568436148">Cancelar</translation>
<translation id="7781829728241885113">Ayer</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_et.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_et.xtb
index 1f86d729e49..c319ea86310 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_et.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_et.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Peida teave</translation>
<translation id="3328801116991980348">Saiditeave</translation>
<translation id="3333961966071413176">Kõik kontaktid</translation>
+<translation id="3362437373201486687">Bluetooth-seadmete otsimine</translation>
<translation id="3386292677130313581">Küsi enne saitidele minu asukoha avaldamist (soovitatav)</translation>
<translation id="3538390592868664640">Saitide jaoks blokeeritakse teid ümbritsevast 3D-kaardi loomine või kaamera asendi jälgimine</translation>
<translation id="3551268116566418498">Kas lahkuda inkognito režiimist?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Allalaadimine on lõpule viidud</translation>
<translation id="3987993985790029246">Kop. link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Lisateave selle lehe kohta</translation>
<translation id="4002066346123236978">Pealkiri</translation>
<translation id="4008040567710660924">Lubage konkreetse saidi küpsisefailid.</translation>
<translation id="4046123991198612571">Järgmine lugu</translation>
<translation id="4149994727733219643">Veebilehtede lihtsustatud vaade</translation>
<translation id="4165986682804962316">Saidi seaded</translation>
+<translation id="4169549551965910670">Ãœhendatud USB-seadmega</translation>
<translation id="4194328954146351878">Teilt küsitakse luba, kui saidid üritavad NFC-seadmetes teavet vaadata ja muuta (soovitatav)</translation>
<translation id="4200726100658658164">Ava asukohaseaded</translation>
<translation id="4226663524361240545">Seade võib märguannete korral vibreerida</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Alati</translation>
<translation id="757524316907819857">Saitidel kaitstud sisu esitamise blokeerimine</translation>
<translation id="7577900504646297215">Huvide haldamine</translation>
+<translation id="7594634374516752650">Ãœhendatud Bluetooth-seadmega</translation>
<translation id="7649070708921625228">Abi</translation>
<translation id="7658239707568436148">Tühista</translation>
<translation id="7781829728241885113">Eile</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_eu.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_eu.xtb
index 572e32ddc58..01aa50276e1 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_eu.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_eu.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ezkutatu informazioa</translation>
<translation id="3328801116991980348">Webgunearen informazioa</translation>
<translation id="3333961966071413176">Kontaktu guztiak</translation>
+<translation id="3362437373201486687">Bluetooth bidezko gailuak bilatzen</translation>
<translation id="3386292677130313581">Webguneei zure kokapena erakusteko baimena eman aurretik, eskatu onespena (gomendatua)</translation>
<translation id="3538390592868664640">Ez utzi inongo webguneri inguruaren 3D-ko mapa bat sortzen edo kameraren posizioaren jarraipena egiten</translation>
<translation id="3551268116566418498">Ezkutuko modutik irten nahi duzu?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Deskargatzen amaitu da</translation>
<translation id="3987993985790029246">Kopiatu esteka</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Orri honi buruz</translation>
<translation id="4002066346123236978">Izena</translation>
<translation id="4008040567710660924">Onartu webgune zehatz baten cookieak.</translation>
<translation id="4046123991198612571">Hurrengo pista</translation>
<translation id="4149994727733219643">Web-orrien ikuspegi sinplifikatua</translation>
<translation id="4165986682804962316">Webgunearen ezarpenak</translation>
+<translation id="4169549551965910670">USB bidezko gailu batera konektatuta</translation>
<translation id="4194328954146351878">Webguneei NFC darabilten gailuetako informazioa ikusi eta aldatzeko baimena eman aurretik, eskatu onespena (gomendatua)</translation>
<translation id="4200726100658658164">Ireki kokapen-ezarpenak</translation>
<translation id="4226663524361240545">Gailua dardararaz dezakete jakinarazpenek</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Beti</translation>
<translation id="757524316907819857">Ez utzi webguneei eduki babestua erreproduzitzen</translation>
<translation id="7577900504646297215">Kudeatu interesak</translation>
+<translation id="7594634374516752650">Bluetooth bidezko gailu batera konektatuta zaude</translation>
<translation id="7649070708921625228">Laguntza</translation>
<translation id="7658239707568436148">Utzi</translation>
<translation id="7781829728241885113">Atzo</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb
index 2322ea1a6f9..38b1c1ba2a2 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb
@@ -23,7 +23,7 @@
<translation id="1644574205037202324">سابقه</translation>
<translation id="1647582022260550163">مطمئنید می‌خواهید اجازه‌ها را بازنشانی کنید و کوکی‌ها و داده‌های سایت را پاک کنید؟</translation>
<translation id="1660204651932907780">به سایت‌ها اجازه داده شود صدا پخش کنند (توصیه می‌شود)</translation>
-<translation id="1677097821151855053">از کوکی‌ها Ùˆ دیگر داده‌های سایت برای به‌خاطر سپردن شما استÙاده می‌شود؛ برای مثال ورود به سیستم یا نمایش آگهی‌های شخصی‌شده. برای مدیریت کردن کوکی‌ها برای همه سایت‌ها، به <ph name="BEGIN_LINK" />تنظیمات<ph name="END_LINK" /> بروید.</translation>
+<translation id="1677097821151855053">از کوکی‌ها Ùˆ دیگر داده‌های سایت برای به‌خاطر سپردن شما استÙاده می‌شود؛ برای مثال ورود به سیستم یا نمایش آگهی‌های شخصی‌â€Ø³Ø§Ø²ÛŒâ€Œâ€Ø´Ø¯Ù‡. برای مدیریت کردن کوکی‌ها برای همه سایت‌ها، به <ph name="BEGIN_LINK" />تنظیمات<ph name="END_LINK" /> بروید.</translation>
<translation id="1688867105868176567">داده‌های سایت پاک شود؟</translation>
<translation id="169515064810179024">سایت‌ها نمی‌توانند به حسگرهای حرکتی دسترسی داشته باشند</translation>
<translation id="1717218214683051432">حسگرهای حرکتی</translation>
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">پنهان کردن اطلاعات</translation>
<translation id="3328801116991980348">اطلاعات سایت</translation>
<translation id="3333961966071413176">همه مخاطبین</translation>
+<translation id="3362437373201486687">درحال جستجوی دستگاه‌های بلوتوث</translation>
<translation id="3386292677130313581">قبل از اجازه به سایت‌ها برای اطلاع از مکانتان، ابتدا سؤال شود (توصیه می‌شود)</translation>
<translation id="3538390592868664640">قابلیت سایت‌ها برای ایجاد نقشه سه‌بعدی از محیط یا ردیابی موقعیت دوربین مسدود می‌شود</translation>
<translation id="3551268116566418498">از «حالت ناشناس» خارج می‌شوید؟</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">بارگیری کامل شد</translation>
<translation id="3987993985790029246">کپی پیوند</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> از ؟</translation>
+<translation id="3992684624889376114">درباره این صÙحه</translation>
<translation id="4002066346123236978">عنوان</translation>
<translation id="4008040567710660924">کوکی‌ها را برای سایت خاصی مجاز کنید.</translation>
<translation id="4046123991198612571">آهنگ بعدی</translation>
<translation id="4149994727733219643">نمای ساده‌شده برای صÙحه‌های وب</translation>
<translation id="4165986682804962316">تنظیمات سایت</translation>
+<translation id="4169549551965910670">â€Ø¨Ù‡ دستگاه USB متصل است</translation>
<translation id="4194328954146351878">â€Ù‚بل‌از اینکه به سایت‌ها اجازه داده شود اطلاعات دستگاه‌های NFC را ببینند Ùˆ تغییر دهند سؤال شود (توصیه می‌شود)</translation>
<translation id="4200726100658658164">باز کردن «تنظیمات مکان»</translation>
<translation id="4226663524361240545">اعلان‌ها ممکن است دستگاه را بلرزانند</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">همیشه</translation>
<translation id="757524316907819857">مسدود کردن سایت‌ها برای پخش محتوای محاÙظت‌شده</translation>
<translation id="7577900504646297215">مدیریت علایق</translation>
+<translation id="7594634374516752650">به دستگاه بلوتوث متصل است</translation>
<translation id="7649070708921625228">راهنما</translation>
<translation id="7658239707568436148">لغو</translation>
<translation id="7781829728241885113">دیروز</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fi.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fi.xtb
index bc2bdff6d32..a9b307f4d94 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fi.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fi.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Piilota tiedot</translation>
<translation id="3328801116991980348">Tietoja sivustosta</translation>
<translation id="3333961966071413176">Kaikki kontaktit</translation>
+<translation id="3362437373201486687">Haetaan Bluetooth-laitteita</translation>
<translation id="3386292677130313581">Pyydä lupaa, kun sivustot yrittävät käyttää sijaintiasi (suositus).</translation>
<translation id="3538390592868664640">Estä sivustoja luomasta 3D-karttaa ympäristöstäsi tai seuraamasta kameran asentoa</translation>
<translation id="3551268116566418498">Poistutaanko incognito-tilasta?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Lataus on valmis</translation>
<translation id="3987993985790029246">Kopioi linkki</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Tietoja sivusta</translation>
<translation id="4002066346123236978">Nimi</translation>
<translation id="4008040567710660924">Salli evästeet tietyllä sivustolla.</translation>
<translation id="4046123991198612571">Seuraava kappale</translation>
<translation id="4149994727733219643">Yksinkertaisempi sivunäkymä</translation>
<translation id="4165986682804962316">Sivustoasetukset</translation>
+<translation id="4169549551965910670">Yhdistetty USB-laitteeseen</translation>
<translation id="4194328954146351878">Kysy, saavatko sivustot nähdä ja muuttaa NFC-laitteiden tietoja (suositeltu)</translation>
<translation id="4200726100658658164">Avaa sijaintiasetukset</translation>
<translation id="4226663524361240545">Laite voi väristä ilmoitusten yhteydessä.</translation>
@@ -247,7 +250,7 @@
<translation id="6912998170423641340">Estä sivustoja lukemasta tekstiä ja kuvia leikepöydältä</translation>
<translation id="6945221475159498467">Valitse</translation>
<translation id="6965382102122355670">OK</translation>
-<translation id="6981982820502123353">Esteettömyys</translation>
+<translation id="6981982820502123353">Saavutettavuus</translation>
<translation id="6992289844737586249">Pyydä lupaa, kun sivustot yrittävät käyttää mikrofonia (suositus).</translation>
<translation id="7000754031042624318">Poistettu käytöstä Android-asetuksissa</translation>
<translation id="7016516562562142042">Sallittu nykyisellä hakukoneella</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Aina</translation>
<translation id="757524316907819857">Estä sivustoja toistamasta suojattua sisältöä</translation>
<translation id="7577900504646297215">Ylläpidä kiinnostuksen kohteita</translation>
+<translation id="7594634374516752650">Yhdistetty Bluetooth-laitteeseen</translation>
<translation id="7649070708921625228">Ohje</translation>
<translation id="7658239707568436148">Peru</translation>
<translation id="7781829728241885113">Eilen</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fil.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fil.xtb
index 0c0d07667cc..b79b551dc8e 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fil.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fil.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Itago ang Impormasyon</translation>
<translation id="3328801116991980348">Impormasyon ng site</translation>
<translation id="3333961966071413176">Lahat ng contact</translation>
+<translation id="3362437373201486687">Nagsa-scan ng mga Bluetooth device</translation>
<translation id="3386292677130313581">Magtanong bago payagan ang mga site na malaman ang iyong lokasyon (inirerekomenda)</translation>
<translation id="3538390592868664640">I-block ang mga site sa paggawa ng 3D na mapa ng iyong kapaligiran o pagsubaybay sa posisyon ng camera</translation>
<translation id="3551268116566418498">Umalis sa Incognito mode?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Tapos na ang pag-download</translation>
<translation id="3987993985790029246">Kopyahin ang link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Tungkol sa page na ito</translation>
<translation id="4002066346123236978">Pamagat</translation>
<translation id="4008040567710660924">Payagan ang cookies para sa isang partikular na site.</translation>
<translation id="4046123991198612571">Susunod na track</translation>
<translation id="4149994727733219643">Pinasimpleng view para sa mga web page</translation>
<translation id="4165986682804962316">Mga setting ng site</translation>
+<translation id="4169549551965910670">Nakakonekta sa USB device</translation>
<translation id="4194328954146351878">Magtanong bago payagan ang mga site na tingnan at baguhin ang impormasyon sa mga NFC device (inirerekomenda)</translation>
<translation id="4200726100658658164">Buksan ang Mga Setting ng Lokasyon</translation>
<translation id="4226663524361240545">Maaaring mag-vibrate ang device dahil sa mga notification</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Palagi</translation>
<translation id="757524316907819857">I-block ang mga site sa pag-play ng pinoprotektahang content</translation>
<translation id="7577900504646297215">Pamahalaan ang mga interes</translation>
+<translation id="7594634374516752650">Nakakonekta sa isang Bluetooth device</translation>
<translation id="7649070708921625228">Tulong</translation>
<translation id="7658239707568436148">Kanselahin</translation>
<translation id="7781829728241885113">Kahapon</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr-CA.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr-CA.xtb
index 8bdfe645467..9f5cee374d1 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr-CA.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr-CA.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Masquer les renseignements</translation>
<translation id="3328801116991980348">Information sur le site</translation>
<translation id="3333961966071413176">Tous les contacts</translation>
+<translation id="3362437373201486687">Recherche d'appareils Bluetooth en cours…</translation>
<translation id="3386292677130313581">Demander avant d'autoriser des sites à connaître votre emplacement (recommandé)</translation>
<translation id="3538390592868664640">Empêcher les sites de créer une carte 3D de votre environnement et de faire le suivi de la position de l'appareil photo</translation>
<translation id="3551268116566418498">Désactiver mode navig. privée?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Téléchargement terminé</translation>
<translation id="3987993985790029246">Copier lien</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">À propos de cette page</translation>
<translation id="4002066346123236978">Titre</translation>
<translation id="4008040567710660924">Autoriser les témoins pour un site en particulier.</translation>
<translation id="4046123991198612571">Chanson suivante</translation>
<translation id="4149994727733219643">Affichage simplifié pour les pages Web</translation>
<translation id="4165986682804962316">Paramètres du site</translation>
+<translation id="4169549551965910670">Connecté à un appareil USB</translation>
<translation id="4194328954146351878">Demander l'autorisation avant de permettre aux sites de consulter et de modifier les données stockées sur les appareils CCP (recommandé)</translation>
<translation id="4200726100658658164">Ouvrir les paramètres de localisation</translation>
<translation id="4226663524361240545">Les notifications peuvent faire vibrer l'appareil</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Toujours</translation>
<translation id="757524316907819857">Empêcher les sites de lire du contenu protégé</translation>
<translation id="7577900504646297215">Gérer les centres d'intérêt</translation>
+<translation id="7594634374516752650">Connecté à un appareil Bluetooth</translation>
<translation id="7649070708921625228">Aide</translation>
<translation id="7658239707568436148">Annuler</translation>
<translation id="7781829728241885113">Hier</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr.xtb
index ea7d9cefc77..d2e5e091c2b 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Masquer les informations</translation>
<translation id="3328801116991980348">Informations sur le site</translation>
<translation id="3333961966071413176">Tous les contacts</translation>
+<translation id="3362437373201486687">Recherche d'appareils Bluetooth…</translation>
<translation id="3386292677130313581">Demander avant d'autoriser des sites à connaître votre position (recommandé)</translation>
<translation id="3538390592868664640">Empêcher les sites de créer un plan 3D de votre environnement ou de suivre la position de la caméra</translation>
<translation id="3551268116566418498">Quitter la navigation privée ?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Téléchargement terminé</translation>
<translation id="3987993985790029246">Copier lien</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/ ?</translation>
+<translation id="3992684624889376114">À propos de cette page</translation>
<translation id="4002066346123236978">Titre</translation>
<translation id="4008040567710660924">Autorisez les cookies pour un site spécifique.</translation>
<translation id="4046123991198612571">Piste suivante</translation>
<translation id="4149994727733219643">Vue simplifiée pour les pages Web</translation>
<translation id="4165986682804962316">Paramètres de site</translation>
+<translation id="4169549551965910670">Connecté à un appareil USB</translation>
<translation id="4194328954146351878">Vous demander avant d'autoriser les sites à consulter et modifier des informations sur des appareils NFC (recommandé)</translation>
<translation id="4200726100658658164">Accéder aux paramètres de localisation</translation>
<translation id="4226663524361240545">L'appareil vibrera en cas de notifications.</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Toujours</translation>
<translation id="757524316907819857">Empêcher les sites de lire les contenus protégés</translation>
<translation id="7577900504646297215">Gérer les centres d'intérêt</translation>
+<translation id="7594634374516752650">Connecté à un appareil Bluetooth</translation>
<translation id="7649070708921625228">Aide</translation>
<translation id="7658239707568436148">Annuler</translation>
<translation id="7781829728241885113">Hier</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gl.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gl.xtb
index 5f9c9f5797e..d7edc7894b4 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gl.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gl.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ocultar información</translation>
<translation id="3328801116991980348">Información do sitio</translation>
<translation id="3333961966071413176">Todos os contactos</translation>
+<translation id="3362437373201486687">Buscando dispositivos Bluetooth</translation>
<translation id="3386292677130313581">Pregunta antes de permitir que os sitios coñezan a túa localización (recomendado)</translation>
<translation id="3538390592868664640">Impide que os sitios creen un mapa 3D do que te rodea e fagan un seguimento da posición da cámara</translation>
<translation id="3551268116566418498">Saír do modo de incógnito?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Descarga completa</translation>
<translation id="3987993985790029246">Copiar ligazón</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Acerca desta páxina</translation>
<translation id="4002066346123236978">Título</translation>
<translation id="4008040567710660924">Permite cookies para un sitio específico.</translation>
<translation id="4046123991198612571">Pista seguinte</translation>
<translation id="4149994727733219643">Vista simplificada de páxinas web</translation>
<translation id="4165986682804962316">Configuración do sitio</translation>
+<translation id="4169549551965910670">Conectouse un dispositivo USB</translation>
<translation id="4194328954146351878">Pregunta antes de permitir que os sitios consulten e cambien información en dispositivos con NFC (recomendado)</translation>
<translation id="4200726100658658164">Abrir Configuración de localización</translation>
<translation id="4226663524361240545">O dispositivo pode vibrar ao recibir notificacións</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Sempre</translation>
<translation id="757524316907819857">Non permite que os sitios reproduzan o contido protexido</translation>
<translation id="7577900504646297215">Xestionar intereses</translation>
+<translation id="7594634374516752650">Estableceuse conexión cun dispositivo Bluetooth</translation>
<translation id="7649070708921625228">Axuda</translation>
<translation id="7658239707568436148">Cancelar</translation>
<translation id="7781829728241885113">Onte</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gu.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gu.xtb
index b154709c9b3..4dc6063d507 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gu.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gu.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">માહિતી છà«àªªàª¾àªµà«‹</translation>
<translation id="3328801116991980348">સાઇટ માહિતી</translation>
<translation id="3333961966071413176">બધા સંપરà«àª•à«‹</translation>
+<translation id="3362437373201486687">બà«àª²à«‚ટૂથ ડિવાઇસ માટે સà«àª•à«…ન કરી રહà«àª¯àª¾àª‚ છીàª</translation>
<translation id="3386292677130313581">સાઇટને તમારા સà«àª¥àª¾àª¨àª¨à«‡ જાણવાની મંજૂરી આપતાં પહેલાં પૂછો (સà«àªàª¾àªµ આપેલો છે)</translation>
<translation id="3538390592868664640">કોઈ સાઇટને તમારી આજà«àª¬àª¾àªœà«àª¨à«‹ 3D નકશો બનાવવા અથવા કૅમેરાના સà«àªŸà«‡àªŸàª¸àª¨à«‡ ટà«àª°à«…ક કરી શકવા માટે બà«àª²à«‰àª• કરો</translation>
<translation id="3551268116566418498">છૂપો મોડ છોડી�</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ડાઉનલોડ પૂરà«àª£</translation>
<translation id="3987993985790029246">લિંક કૉપિ કરો</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">આ પેજ વિશે</translation>
<translation id="4002066346123236978">શીરà«àª·àª•</translation>
<translation id="4008040567710660924">કોઈ ચોકà«àª•àª¸ સાઇટ માટે કà«àª•à«€àª¨à«‡ મંજૂરી આપો.</translation>
<translation id="4046123991198612571">આગલો ટà«àª°à«…ક</translation>
<translation id="4149994727733219643">વેબપેજ માટે સરળ દૃશà«àª¯</translation>
<translation id="4165986682804962316">સાઇટ સેટિંગ</translation>
+<translation id="4169549551965910670">કોઈ USB ડિવાઇસથી કનેકà«àªŸà«‡àª¡</translation>
<translation id="4194328954146351878">NFC ડિવાઇસ પર સાઇટને માહિતી જોવાની અને બદલવાની મંજૂરી આપતા પહેલાં પૂછો (ભલામણ કરવામાં આવે છે)</translation>
<translation id="4200726100658658164">સà«àª¥àª¾àª¨ સેટિંગ ખોલો</translation>
<translation id="4226663524361240545">સૂચનાઓ ઉપકરણને વાઇબà«àª°à«‡àªŸ કરી શકે છે</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">હંમેશાં</translation>
<translation id="757524316907819857">સાઇટને સંરકà«àª·àª¿àª¤ કનà«àªŸà«‡àª¨à«àªŸ ચલાવવાથી બà«àª²à«‰àª• કરો</translation>
<translation id="7577900504646297215">રà«àªšàª¿àª“ મેનેજ કરો</translation>
+<translation id="7594634374516752650">બà«àª²à«‚ટૂથ ડિવાઇસ સાથે કનેકà«àªŸ કરી</translation>
<translation id="7649070708921625228">સહાય</translation>
<translation id="7658239707568436148">રદ કરો</translation>
<translation id="7781829728241885113">ગઈ કાલે</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hi.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hi.xtb
index 804bcfcf38d..5b64e279bc9 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hi.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hi.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">जानकारी छिपाà¤à¤‚</translation>
<translation id="3328801116991980348">साइट जानकारी</translation>
<translation id="3333961966071413176">सभी संपरà¥à¤•</translation>
+<translation id="3362437373201486687">बà¥à¤²à¥‚टूथ डिवाइस के लिठसà¥à¤•à¥ˆà¤¨ किया जा रहा है</translation>
<translation id="3386292677130313581">साइटों को अपनी जगह की जानकारी देने से पहले अनà¥à¤®à¤¤à¤¿ लेना ज़रूरी बनाà¤à¤‚ (सà¥à¤à¤¾à¤¯à¤¾ गया)</translation>
<translation id="3538390592868664640">साइटों को अपने आस-पास की जगह का 3D मैप बनाने या कैमरे की सà¥à¤¥à¤¿à¤¤à¤¿ टà¥à¤°à¥ˆà¤• करने से रोकें</translation>
<translation id="3551268116566418498">गà¥à¤ªà¥à¤¤ मोड से बाहर निकलना है?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">डाउनलोड पूरा हà¥à¤†</translation>
<translation id="3987993985790029246">लिंक की पà¥à¤°à¤¤à¤¿ बनाà¤à¤‚</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">इस पेज के बारे में</translation>
<translation id="4002066346123236978">शीरà¥à¤·à¤•</translation>
<translation id="4008040567710660924">किसी खास साइट के लिठकà¥à¤•à¥€ की मंज़ूरी दें.</translation>
<translation id="4046123991198612571">अगला टà¥à¤°à¥ˆà¤•</translation>
<translation id="4149994727733219643">वेब पेजों के लिठसरल बनाया गया वà¥à¤¯à¥‚</translation>
<translation id="4165986682804962316">साइट सेटिंग</translation>
+<translation id="4169549551965910670">यूà¤à¤¸à¤¬à¥€ डिवाइस से कनेकà¥à¤Ÿ है</translation>
<translation id="4194328954146351878">साइटों को à¤à¤¨à¤à¤«à¤¼à¤¸à¥€ की सà¥à¤µà¤¿à¤§à¤¾ वाले डिवाइसों पर, जानकारी देखने और उसमें बदलाव करने की अनà¥à¤®à¤¤à¤¿ देने से पहले पूछा जाठ(सà¥à¤à¤¾à¤ˆ गई सेटिंग)</translation>
<translation id="4200726100658658164">जगह की सेटिंग खोलें</translation>
<translation id="4226663524361240545">नोटिफ़िकेशन से डिवाइस में कंपन हो सकता है</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">हमेशा</translation>
<translation id="757524316907819857">साइटों को सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ सामगà¥à¤°à¥€ चलाने से रोकें</translation>
<translation id="7577900504646297215">पसंद पà¥à¤°à¤¬à¤‚धित करें</translation>
+<translation id="7594634374516752650">बà¥à¤²à¥‚टूथ डिवाइस से जà¥à¤¡à¤¼à¤¾ हà¥à¤† है</translation>
<translation id="7649070708921625228">सहायता</translation>
<translation id="7658239707568436148">अभी नहीं</translation>
<translation id="7781829728241885113">बीता कल</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hr.xtb
index 3183d1d64cc..dc840aff5b3 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hr.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hr.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Sakrij informacije</translation>
<translation id="3328801116991980348">Informacije o web-lokaciji</translation>
<translation id="3333961966071413176">Svi kontakti</translation>
+<translation id="3362437373201486687">Traženje Bluetooth uređaja</translation>
<translation id="3386292677130313581">Web-lokacije moraju tražiti dopuÅ¡tenje za pristup lokaciji (preporuÄeno)</translation>
<translation id="3538390592868664640">Blokirajte web-lokacije da izrađuju 3D kartu vašeg okruženja ili prate položaj kamere</translation>
<translation id="3551268116566418498">Napustiti anonimni naÄin?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Preuzimanje dovršeno</translation>
<translation id="3987993985790029246">Kopiraj vezu</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Informacije o ovoj stranici</translation>
<translation id="4002066346123236978">Naslov</translation>
<translation id="4008040567710660924">Dopusti kolaÄiće za odreÄ‘enu web-lokaciju.</translation>
<translation id="4046123991198612571">Sljedeća pjesma</translation>
<translation id="4149994727733219643">Pojednostavljeni prikaz za web-stranice</translation>
<translation id="4165986682804962316">Postavke web-lokacije</translation>
+<translation id="4169549551965910670">Povezano s USB uređajem</translation>
<translation id="4194328954146351878">Pitaj prije omogućavanja web-lokacijama da vide i mijenjaju podatke na NFC ureÄ‘ajima (preporuÄeno)</translation>
<translation id="4200726100658658164">Otvori postavke lokacije</translation>
<translation id="4226663524361240545">Obavijesti mogu ukljuÄiti vibriranje ureÄ‘aja</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Uvijek</translation>
<translation id="757524316907819857">Web-lokacijama nije dopušteno reproduciranje zaštićenog sadržaja</translation>
<translation id="7577900504646297215">Upravljaj interesima</translation>
+<translation id="7594634374516752650">Povezano s Bluetooth uređajem</translation>
<translation id="7649070708921625228">Pomoć</translation>
<translation id="7658239707568436148">Odustani</translation>
<translation id="7781829728241885113">Danas</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb
index bde6ccd0143..8bdc58eaa8f 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Információk elrejtése…</translation>
<translation id="3328801116991980348">Webhelyadatok</translation>
<translation id="3333961966071413176">Összes névjegy</translation>
+<translation id="3362437373201486687">Bluetooth-eszközök keresése…</translation>
<translation id="3386292677130313581">Kérdezzen rá, mielőtt engedélyezné a webhelyek számára a tartózkodási helyhez való hozzáférést (ajánlott)</translation>
<translation id="3538390592868664640">Az Ön környezetéről készített 3D-s térkép létrehozásának, valamint a kamerapozíció követésének letiltása a webhelyek számára</translation>
<translation id="3551268116566418498">Kilép az inkognitó módból?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">A letöltés sikeres</translation>
<translation id="3987993985790029246">Link másolása</translation>
<translation id="3991845972263764475">? / <ph name="BYTES_DOWNLOADED_WITH_UNITS" /></translation>
+<translation id="3992684624889376114">Információ az oldalról</translation>
<translation id="4002066346123236978">Cím</translation>
<translation id="4008040567710660924">Adott webhely cookie-jainak engedélyezése.</translation>
<translation id="4046123991198612571">Következő szám</translation>
<translation id="4149994727733219643">Weboldalak egyszerűsített nézete</translation>
<translation id="4165986682804962316">Webhelybeállítások</translation>
+<translation id="4169549551965910670">Csatlakoztatva USB-eszközhöz</translation>
<translation id="4194328954146351878">Kérdezzen rá, mielőtt engedélyezi a webhelyek számára az NFC-eszközökön található információk megtekintését és az ilyen eszközökkel való információcserét (ajánlott)</translation>
<translation id="4200726100658658164">Helybeállítások megnyitása</translation>
<translation id="4226663524361240545">Az értesítések miatt rezeghet az eszköz</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Mindig</translation>
<translation id="757524316907819857">Védett tartalom lejátszásának letiltása a webhelyeken</translation>
<translation id="7577900504646297215">Érdeklődési körök kezelése</translation>
+<translation id="7594634374516752650">Bluetooth-eszközhöz csatlakoztatva</translation>
<translation id="7649070708921625228">Súgó</translation>
<translation id="7658239707568436148">Mégse</translation>
<translation id="7781829728241885113">Tegnap</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hy.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hy.xtb
index edc8696d32a..3db7ca18184 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hy.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hy.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ô¹Õ¡Ö„ÖÕ¶Õ¥Õ¬ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨</translation>
<translation id="3328801116991980348">ÕÕ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ Õ¯Õ¡ÕµÖ„Õ« Õ´Õ¡Õ½Õ«Õ¶</translation>
<translation id="3333961966071413176">Ô²Õ¸Õ¬Õ¸Ö€ Õ¯Õ¸Õ¶Õ¿Õ¡Õ¯Õ¿Õ¶Õ¥Ö€Õ¨</translation>
+<translation id="3362437373201486687">Bluetooth սարքերի որոնում…</translation>
<translation id="3386292677130313581">Õ€Õ¡Ö€ÖÕ¶Õ¥Õ¬ Õ¶Õ¡Õ­Ö„Õ¡Õ¶ Õ±Õ¥Ö€ Õ¿Õ¥Õ²Õ¡Õ¤Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¯Õ¡ÕµÖ„Õ¥Ö€Õ«Õ¶ Õ°Õ¡Õ½Õ¡Õ¶Õ¥Õ¬Õ« Õ¤Õ¡Ö€Õ±Õ¶Õ¥Õ¬Õ¨ (Õ­Õ¸Ö€Õ°Õ¸Ö‚Ö€Õ¤ Õ§ Õ¿Ö€Õ¾Õ¸Ö‚Õ´)</translation>
<translation id="3538390592868664640">Ô±Ö€Õ£Õ¥Õ¬Õ¥Õ¬ Õ¯Õ¡ÕµÖ„Õ¥Ö€Õ«Õ¶ Õ½Õ¿Õ¥Õ²Õ®Õ¥Õ¬ Õ·Ö€Õ»Õ¡Õ¯Õ¡ÕµÖ„Õ« Õ¥Õ¼Õ¡Õ¹Õ¡Öƒ Ö„Õ¡Ö€Õ¿Õ¥Õ¦Õ¨ Ö‡ Õ°Õ¥Õ¿Õ¡Õ£Õ®Õ¥Õ¬ Õ¿Õ¥Õ½Õ¡Õ­ÖÕ«Õ¯Õ« Õ¤Õ«Ö€Ö„Õ¨</translation>
<translation id="3551268116566418498">Ô´Õ¸Ö‚Ö€Õ½ Õ£Õ¡ÕžÕ¬ Õ«Õ¶Õ¯Õ¸Õ£Õ¶Õ«Õ¿Õ¸ Õ¼Õ¥ÕªÕ«Õ´Õ«Ö</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Õ†Õ¥Ö€Õ¢Õ¥Õ¼Õ¶Õ¸Ö‚Õ´Õ¶ Õ¡Õ¾Õ¡Ö€Õ¿Õ¾Õ¥Ö</translation>
<translation id="3987993985790029246">ÕŠÕ¡Õ¿Õ³Õ¥Õ¶Õ¥Õ¬ Õ°Õ²Õ¸Ö‚Õ´Õ¨</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Ô±ÕµÕ½ Õ§Õ»Õ« Õ´Õ¡Õ½Õ«Õ¶</translation>
<translation id="4002066346123236978">ÕŽÕ¥Ö€Õ¶Õ¡Õ£Õ«Ö€</translation>
<translation id="4008040567710660924">Ô¹Õ¸Ö‚ÕµÕ¬Õ¡Õ¿Ö€Õ¥Õ¬ Ö„Õ¸Ö‚Ö„Õ«Õ¶Õ¥Ö€Õ¨ Õ¯Õ¸Õ¶Õ¯Ö€Õ¥Õ¿ Õ¯Õ¡ÕµÖ„Õ« Õ°Õ¡Õ´Õ¡Ö€:</translation>
<translation id="4046123991198612571">Õ€Õ¡Õ»Õ¸Ö€Õ¤Õ¨</translation>
<translation id="4149994727733219643">Ô¿Õ¡ÕµÖ„Õ¥Ö€Õ« ÕºÕ¡Ö€Õ¦Õ¥ÖÕ¾Õ¡Õ® Õ¤Õ«Õ¿Õ¡Õ¯Õ¥Ö€Õº</translation>
<translation id="4165986682804962316">Ô¿Õ¡ÕµÖ„Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€</translation>
+<translation id="4169549551965910670">Ô¿Õ¡ÕµÖ„Õ¨ Õ´Õ«Õ¡ÖÕ¡Õ® Õ§ USB Õ½Õ¡Ö€Ö„Õ«</translation>
<translation id="4194328954146351878">Ô¿Õ¡ÕµÖ„Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€ Õ©Õ¸Ö‚ÕµÕ¬Õ¿Õ¾Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Õ­Õ¶Õ¤Ö€Õ¥Õ¬Õ Õ¿Õ¥Õ½Õ¶Õ¥Õ¬Õ¸Ö‚ Ö‡ ÖƒÕ¸ÖƒÕ¸Õ­Õ¥Õ¬Õ¸Ö‚ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨ NFC Õ½Õ¡Ö€Ö„Õ¥Ö€Õ¸Ö‚Õ´ (Õ­Õ¸Ö€Õ°Õ¸Ö‚Ö€Õ¤ Õ§ Õ¿Ö€Õ¾Õ¸Ö‚Õ´)</translation>
<translation id="4200726100658658164">Ô²Õ¡ÖÕ¥Õ¬ Õ¿Õ¥Õ²Õ¡Õ¤Ö€Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨</translation>
<translation id="4226663524361240545">Ô¾Õ¡Õ¶Õ¸Ö‚ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€Õ« ÕªÕ¡Õ´Õ¡Õ¶Õ¡Õ¯ Õ½Õ¡Ö€Ö„Õ¨ Õ¯Õ©Ö€Õ©Õ¼Õ¡</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Õ„Õ«Õ·Õ¿</translation>
<translation id="757524316907819857">Ô±Ö€Õ£Õ¥Õ¬Õ¥Õ¬ Õ¯Õ¡ÕµÖ„Õ¥Ö€Õ«Õ¶ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¾Õ¡Õ® Õ¢Õ¸Õ¾Õ¡Õ¶Õ¤Õ¡Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Õ¶Õ¾Õ¡Õ£Õ¡Ö€Õ¯Õ¥Õ¬</translation>
<translation id="7577900504646297215">Ô¿Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬ Õ°Õ¥Õ¿Õ¡Ö„Ö€Ö„Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨</translation>
+<translation id="7594634374516752650">Õ„Õ«Õ¡ÖÕ¡Õ® Õ§ Bluetooth Õ½Õ¡Ö€Ö„Õ«</translation>
<translation id="7649070708921625228">Õ•Õ£Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
<translation id="7658239707568436148">Õ‰Õ¥Õ²Õ¡Ö€Õ¯Õ¥Õ¬</translation>
<translation id="7781829728241885113">ÔµÖ€Õ¥Õ¯</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_id.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_id.xtb
index cbe9cd98a80..0a45174e5a7 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_id.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_id.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Sembunyikan Info</translation>
<translation id="3328801116991980348">Informasi situs</translation>
<translation id="3333961966071413176">Semua kontak</translation>
+<translation id="3362437373201486687">Memindai perangkat Bluetooth</translation>
<translation id="3386292677130313581">Minta izin sebelum mengizinkan situs mengetahui lokasi Anda (disarankan)</translation>
<translation id="3538390592868664640">Blokir situs agar tidak membuat peta 3D untuk area di sekeliling Anda atau melacak posisi kamera</translation>
<translation id="3551268116566418498">Keluar dari mode Samaran?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Download selesai</translation>
<translation id="3987993985790029246">Salin link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Tentang halaman ini</translation>
<translation id="4002066346123236978">Judul</translation>
<translation id="4008040567710660924">Izinkan cookie untuk situs tertentu.</translation>
<translation id="4046123991198612571">Lagu berikutnya</translation>
<translation id="4149994727733219643">Tampilan sederhana untuk halaman web</translation>
<translation id="4165986682804962316">Setelan situs</translation>
+<translation id="4169549551965910670">Terhubung ke perangkat USB</translation>
<translation id="4194328954146351878">Tanyakan sebelum mengizinkan situs melihat dan mengubah informasi di perangkat NFC (direkomendasikan)</translation>
<translation id="4200726100658658164">Buka Setelan Lokasi</translation>
<translation id="4226663524361240545">Notifikasi dapat membuat perangkat bergetar</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Selalu</translation>
<translation id="757524316907819857">Blokir situs agar tidak memutar konten yang dilindungi</translation>
<translation id="7577900504646297215">Kelola minat</translation>
+<translation id="7594634374516752650">Terhubung ke perangkat Bluetooth</translation>
<translation id="7649070708921625228">Bantuan</translation>
<translation id="7658239707568436148">Batal</translation>
<translation id="7781829728241885113">Kemarin</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_is.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_is.xtb
index 12abe5ca754..1af54c251e9 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_is.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_is.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Fela upplýsingar</translation>
<translation id="3328801116991980348">Upplýsingar um vefsvæði</translation>
<translation id="3333961966071413176">Allir tengiliðir</translation>
+<translation id="3362437373201486687">Leitar að Bluetooth-tækjum</translation>
<translation id="3386292677130313581">Spyrja áður en vefsvæðum er veitt heimild til að sjá staðsetningu þína (ráðlagt)</translation>
<translation id="3538390592868664640">Komdu í veg fyrir að vefsvæði búi til þrívíddarkort af umhverfinu eða reki staðsetningu myndavélarinnar</translation>
<translation id="3551268116566418498">Slökkva á huliðsstillingu?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Niðurhali lokið</translation>
<translation id="3987993985790029246">Afrita tengil</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Um þessa síðu</translation>
<translation id="4002066346123236978">Heiti</translation>
<translation id="4008040567710660924">Leyfa fótspor fyrir tiltekið vefsvæði.</translation>
<translation id="4046123991198612571">Næsta lag</translation>
<translation id="4149994727733219643">Einfaldað yfirlit fyrir vefsíður</translation>
<translation id="4165986682804962316">Vefsvæðastillingar</translation>
+<translation id="4169549551965910670">Tengt við USB-tæki</translation>
<translation id="4194328954146351878">Spyrja áður en vefsvæði fá að sjá og breyta upplýsingum í NFC-tækjum (ráðlagt)</translation>
<translation id="4200726100658658164">Opna staðsetningarstillingar</translation>
<translation id="4226663524361240545">Tilkynningar gætu látið tækið titra</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Alltaf</translation>
<translation id="757524316907819857">Banna síðum að spila varið efni</translation>
<translation id="7577900504646297215">Stjórna áhugamálum</translation>
+<translation id="7594634374516752650">Tengt við Bluetooth-tæki</translation>
<translation id="7649070708921625228">Hjálp</translation>
<translation id="7658239707568436148">Hætta við</translation>
<translation id="7781829728241885113">à gær</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_it.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_it.xtb
index 3a2395b8add..71f01c43b2a 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_it.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_it.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Nascondi informazioni</translation>
<translation id="3328801116991980348">Informazioni sito</translation>
<translation id="3333961966071413176">Tutti i contatti</translation>
+<translation id="3362437373201486687">Ricerca di dispositivi Bluetooth in corso…</translation>
<translation id="3386292677130313581">Chiedi conferma prima di consentire ai siti di conoscere la tua posizione (opzione consigliata)</translation>
<translation id="3538390592868664640">Impedisci ai siti di creare una mappa 3D dell'ambiente circostante o di monitorare la posizione della fotocamera</translation>
<translation id="3551268116566418498">Uscire da modalità in incognito?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Download completato</translation>
<translation id="3987993985790029246">Copia link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> di ?</translation>
+<translation id="3992684624889376114">Informazioni su questa pagina</translation>
<translation id="4002066346123236978">Titolo</translation>
<translation id="4008040567710660924">Consenti i cookie per un sito specifico.</translation>
<translation id="4046123991198612571">Traccia successiva</translation>
<translation id="4149994727733219643">Visualizzazione semplificata delle pagine web</translation>
<translation id="4165986682804962316">Impostazioni sito</translation>
+<translation id="4169549551965910670">Connesso a un dispositivo USB</translation>
<translation id="4194328954146351878">Chiedi conferma prima di consentire ai siti di vedere e modificare informazioni su dispositivi NFC (opzione consigliata)</translation>
<translation id="4200726100658658164">Apri Impostazioni di geolocalizzazione</translation>
<translation id="4226663524361240545">Le notifiche possono far vibrare il dispositivo</translation>
@@ -209,7 +212,7 @@
<translation id="6040143037577758943">Chiudi</translation>
<translation id="6042308850641462728">Altro</translation>
<translation id="6064125863973209585">Download completati</translation>
-<translation id="6165508094623778733">Ulteriori informazioni</translation>
+<translation id="6165508094623778733">Scopri di più</translation>
<translation id="6177111841848151710">Bloccata per il motore di ricerca corrente</translation>
<translation id="6177128806592000436">La tua connessione a questo sito non è sicura</translation>
<translation id="6181444274883918285">Aggiungi eccezione per un sito</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Sempre</translation>
<translation id="757524316907819857">Impedisci ai siti di riprodurre contenuti protetti</translation>
<translation id="7577900504646297215">Gestisci interessi</translation>
+<translation id="7594634374516752650">Connesso a un dispositivo Bluetooth</translation>
<translation id="7649070708921625228">Guida</translation>
<translation id="7658239707568436148">Annulla</translation>
<translation id="7781829728241885113">Ieri</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb
index a02b78a3aa0..8c6d5ab3799 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">הסתרת פרטי×</translation>
<translation id="3328801116991980348">פרטי ×תר</translation>
<translation id="3333961966071413176">כל ×נשי הקשר</translation>
+<translation id="3362437373201486687">â€×ž×ª×‘צע חיפוש של מכשירי Bluetooth</translation>
<translation id="3386292677130313581">יש לש×ול לפני שמ××¤×©×¨×™× ×œ××ª×¨×™× ×œ×“×¢×ª מה ×”×ž×™×§×•× ×©×œ×š (מומלץ)</translation>
<translation id="3538390592868664640">חסימה של יצירת מפה בתלת ממד של הסביבה שלך ×ו של מעקב ×חר ×ž×™×§×•× ×”×ž×¦×œ×ž×” על ידי ×תרי×.</translation>
<translation id="3551268116566418498">לצ×ת מהמצב ×”×נונימי?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ההורדה הושלמה</translation>
<translation id="3987993985790029246">העתקת קישור</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">מידע על הדף הזה</translation>
<translation id="4002066346123236978">כותרת</translation>
<translation id="4008040567710660924">â€×ישור קובצי Cookie של ×תר מסוי×.</translation>
<translation id="4046123991198612571">הרצועה הב××”</translation>
<translation id="4149994727733219643">תצוגה פשוטה של דפי ×ינטרנט</translation>
<translation id="4165986682804962316">הגדרות ל×תרי×</translation>
+<translation id="4169549551965910670">â€×ž×—ובר להתקן USB</translation>
<translation id="4194328954146351878">â€×”צגת ש×לה לפני מתן הרש××” ל××ª×¨×™× ×œ×¨×ות ולשנות מידע במכשירי NFC (מומלץ).</translation>
<translation id="4200726100658658164">פתיחת הגדרות המיקו×</translation>
<translation id="4226663524361240545">רטט של המכשיר ×פשרי כשמתקבלת הודעה</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">תמיד</translation>
<translation id="757524316907819857">חסימה של הפעלת תוכן מוגן על-ידי ×תרי×</translation>
<translation id="7577900504646297215">ניהול תחומי עניין</translation>
+<translation id="7594634374516752650">â€×”×תר מחובר למכשיר Bluetooth</translation>
<translation id="7649070708921625228">עזרה</translation>
<translation id="7658239707568436148">ביטול</translation>
<translation id="7781829728241885113">×תמול</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ja.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ja.xtb
index a5629fa8bb6..48b1492b252 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ja.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ja.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">情報を表示ã—ãªã„</translation>
<translation id="3328801116991980348">サイト情報</translation>
<translation id="3333961966071413176">ã™ã¹ã¦ã®é€£çµ¡å…ˆ</translation>
+<translation id="3362437373201486687">Bluetooth デãƒã‚¤ã‚¹ã‚’スキャンã—ã¦ã„ã¾ã™</translation>
<translation id="3386292677130313581">サイトã«ç¾åœ¨åœ°ã®èªè­˜ã‚’許å¯ã™ã‚‹å‰ã«ç¢ºèªã™ã‚‹ï¼ˆæŽ¨å¥¨ï¼‰</translation>
<translation id="3538390592868664640">サイトã«ã‚ˆã‚‹å‘¨å›²ã® 3D マップã®ä½œæˆã¾ãŸã¯ã‚«ãƒ¡ãƒ©ä½ç½®ã®è¿½è·¡ã‚’ブロックã—ã¾ã™</translation>
<translation id="3551268116566418498">シークレット モードを終了ã—ã¾ã™ã‹ï¼Ÿ</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ダウンロード完了</translation>
<translation id="3987993985790029246">リンクã®ã‚³ãƒ”ー</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">ã“ã®ãƒšãƒ¼ã‚¸ã«ã¤ã„ã¦</translation>
<translation id="4002066346123236978">タイトル</translation>
<translation id="4008040567710660924">特定ã®ã‚µã‚¤ãƒˆã® Cookie を許å¯ã—ã¾ã™ã€‚</translation>
<translation id="4046123991198612571">次ã®ãƒˆãƒ©ãƒƒã‚¯</translation>
<translation id="4149994727733219643">ウェブページã®ç°¡æ˜“表示</translation>
<translation id="4165986682804962316">サイトã®è¨­å®š</translation>
+<translation id="4169549551965910670">USB デãƒã‚¤ã‚¹ã«æŽ¥ç¶šã•ã‚Œã¦ã„ã¾ã™</translation>
<translation id="4194328954146351878">サイト㌠NFC デãƒã‚¤ã‚¹ã®æƒ…報をå‚ç…§ãŠã‚ˆã³å¤‰æ›´ã™ã‚‹ã“ã¨ã‚’許å¯ã™ã‚‹å‰ã«ç¢ºèªã™ã‚‹ï¼ˆæŽ¨å¥¨ï¼‰</translation>
<translation id="4200726100658658164">ä½ç½®æƒ…å ±ã®è¨­å®šã‚’é–‹ã</translation>
<translation id="4226663524361240545">通知をå—ã‘å–ã‚‹ã¨ãƒ‡ãƒã‚¤ã‚¹ãŒæŒ¯å‹•ã—ã¾ã™</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">常ã«ä½¿ç”¨</translation>
<translation id="757524316907819857">サイトã§ã®ä¿è­·ã•ã‚ŒãŸã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®å†ç”Ÿã‚’ブロックã™ã‚‹</translation>
<translation id="7577900504646297215">興味ã®ã‚るトピックを管ç†</translation>
+<translation id="7594634374516752650">Bluetooth デãƒã‚¤ã‚¹ã«æŽ¥ç¶šã•ã‚Œã¦ã„ã¾ã™</translation>
<translation id="7649070708921625228">ヘルプ</translation>
<translation id="7658239707568436148">キャンセル</translation>
<translation id="7781829728241885113">昨日</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb
index 4ca61664b5b..8b07e1dccb4 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ დáƒáƒ›áƒáƒšáƒ•áƒ</translation>
<translation id="3328801116991980348">სáƒáƒ˜áƒ¢áƒ˜áƒ¡ ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ</translation>
<translation id="3333961966071413176">ყველრკáƒáƒœáƒ¢áƒáƒ¥áƒ¢áƒ˜</translation>
+<translation id="3362437373201486687">მიმდინáƒáƒ áƒ”áƒáƒ‘ს Bluetooth მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ების სკáƒáƒœáƒ˜áƒ áƒ”ბáƒ</translation>
<translation id="3386292677130313581">შეკითხვრსáƒáƒ˜áƒ¢áƒ”ბისთვის თქვენი მდებáƒáƒ áƒ”áƒáƒ‘ის დáƒáƒ“გენის დáƒáƒ¨áƒ•áƒ”ბáƒáƒ›áƒ“ე (რეკáƒáƒ›áƒ”ნდებული)</translation>
<translation id="3538390592868664640">სáƒáƒ˜áƒ¢áƒ”ბისთვის თქვენი გáƒáƒ áƒ”მáƒáƒ¡ 3-გáƒáƒœáƒ–áƒáƒ›áƒ˜áƒšáƒ”ბიáƒáƒœáƒ˜ რუკის შექმნის áƒáƒœ კáƒáƒ›áƒ”რის პáƒáƒ–იციისთვის თვáƒáƒšáƒ˜áƒ¡ მიდევნების დáƒáƒ‘ლáƒáƒ™áƒ•áƒ</translation>
<translation id="3551268116566418498">გáƒáƒ•áƒ˜áƒ“ეთ ინკáƒáƒ’ნიტრრეჟიმიდáƒáƒœ?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ჩáƒáƒ›áƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვრდáƒáƒ¡áƒ áƒ£áƒšáƒ“áƒ</translation>
<translation id="3987993985790029246">ბმულის კáƒáƒžáƒ˜áƒ áƒ”ბáƒ</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">áƒáƒ› გვერდის შესáƒáƒ®áƒ”ბ</translation>
<translation id="4002066346123236978">სáƒáƒ—áƒáƒ£áƒ áƒ˜</translation>
<translation id="4008040567710660924">ქუქი-ჩáƒáƒœáƒáƒ¬áƒ”რების დáƒáƒ¨áƒ•áƒ”ბრკáƒáƒœáƒ™áƒ áƒ”ტული სáƒáƒ˜áƒ¢áƒ˜áƒ¡áƒ—ვის.</translation>
<translation id="4046123991198612571">შემდეგი ჩáƒáƒœáƒáƒ¬áƒ”რი</translation>
<translation id="4149994727733219643">გáƒáƒ›áƒáƒ áƒ¢áƒ˜áƒ•áƒ”ბული ხედი ვებგვერდებისთვის</translation>
<translation id="4165986682804962316">სáƒáƒ˜áƒ¢áƒ˜áƒ¡ პáƒáƒ áƒáƒ›áƒ”ტრები</translation>
+<translation id="4169549551965910670">დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბულირUSB მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ¡áƒ—áƒáƒœ</translation>
<translation id="4194328954146351878">შეკითხვრსáƒáƒ˜áƒ¢áƒ”ბისთვის NFC მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ებზე ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒáƒ–ე წვდáƒáƒ›áƒ˜áƒ¡áƒ დრმისი შეცვლის დáƒáƒ¨áƒ•áƒ”ბáƒáƒ›áƒ“ე (რეკáƒáƒ›áƒ”ნდებული)</translation>
<translation id="4200726100658658164">მდებáƒáƒ áƒ”áƒáƒ‘ის პáƒáƒ áƒáƒ›áƒ”ტრების გáƒáƒ®áƒ¡áƒœáƒ</translation>
<translation id="4226663524361240545">შეტყáƒáƒ‘ინებებმრშეიძლებრმáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ის ვიბრáƒáƒªáƒ˜áƒ გáƒáƒ›áƒáƒ˜áƒ¬áƒ•áƒ˜áƒáƒ¡</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ყáƒáƒ•áƒ”ლთვის</translation>
<translation id="757524316907819857">სáƒáƒ˜áƒ¢áƒ”ბისთვის დáƒáƒªáƒ£áƒšáƒ˜ კáƒáƒœáƒ¢áƒ”ნტის დáƒáƒ™áƒ•áƒ áƒ˜áƒ¡ დáƒáƒ‘ლáƒáƒ™áƒ•áƒ</translation>
<translation id="7577900504646297215">ინტერესების მáƒáƒ áƒ—ვáƒ</translation>
+<translation id="7594634374516752650">დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბულირBluetooth მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ¡áƒ—áƒáƒœ</translation>
<translation id="7649070708921625228">დáƒáƒ®áƒ›áƒáƒ áƒ”ბáƒ</translation>
<translation id="7658239707568436148">გáƒáƒ£áƒ¥áƒ›áƒ”ბáƒ</translation>
<translation id="7781829728241885113">გუშინ</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kk.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kk.xtb
index 329503647dd..323e9ce51b3 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kk.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kk.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ðқпаратты жаÑыру</translation>
<translation id="3328801116991980348">Сайт ақпараты</translation>
<translation id="3333961966071413176">Барлық контакт</translation>
+<translation id="3362437373201486687">Bluetooth құрылғылары Ñканерленуде</translation>
<translation id="3386292677130313581">Сайттар орналаÑқан орныңызды анықтау үшін Ñ€Ò±Ò›Ñат Ñұрайды (Ò±Ñынылады)</translation>
<translation id="3538390592868664640">Сайттарға айналаңыздың 3D картаÑын жаÑауға немеÑе камера орнын бақылауға тыйым Ñалу</translation>
<translation id="3551268116566418498">Инкогнито режимінен шығаÑыз ба?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Жүктеп алынды</translation>
<translation id="3987993985790029246">Сілтемені көшіру</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">ОÑÑ‹ бет туралы ақпарат</translation>
<translation id="4002066346123236978">Тақырып</translation>
<translation id="4008040567710660924">Белгілі бір Ñайт үшін cookie файлдарына Ñ€Ò±Ò›Ñат беріңіз.</translation>
<translation id="4046123991198612571">КелеÑÑ– аудиотрек</translation>
<translation id="4149994727733219643">Веб-беттердің қарапайым көрініÑÑ–</translation>
<translation id="4165986682804962316">Сайт параметрлері</translation>
+<translation id="4169549551965910670">USB құрылғыÑына жалғанды.</translation>
<translation id="4194328954146351878">Сайттардың NFC құрылғылары туралы ақпаратты көруіне және өзгертуіне Ñ€Ò±Ò›Ñат Ð±ÐµÑ€Ð¼ÐµÑ Ð±Ò±Ñ€Ñ‹Ð½ Ñізден Ñұрау (Ò±Ñынылады)</translation>
<translation id="4200726100658658164">ОрналаÑу параметрлерін ашу</translation>
<translation id="4226663524361240545">Хабарландыру келіп Ñ‚Ò¯Ñкенде, құрылғы дірілдейді</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Әрқашан</translation>
<translation id="757524316907819857">Сайттардың қорғалған мазмұнды ойнатуына тыйым Ñалу</translation>
<translation id="7577900504646297215">Қызығушылықтарды баÑқару</translation>
+<translation id="7594634374516752650">Bluetooth құрылғыÑына жалғанды</translation>
<translation id="7649070708921625228">Ðнықтама</translation>
<translation id="7658239707568436148">Ð‘Ð°Ñ Ñ‚Ð°Ñ€Ñ‚Ñƒ</translation>
<translation id="7781829728241885113">Кеше</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_km.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_km.xtb
index 8e2a18c3abf..88bf7147376 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_km.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_km.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">លាក់ពáŸážáŸŒáž˜áž¶áž“</translation>
<translation id="3328801116991980348">áž–áŸážáŸŒáž˜áž¶áž“áž‚áŸáž áž‘ំពáŸážš</translation>
<translation id="3333961966071413176">ទំនាក់​ទំនង​ទាំងអស់</translation>
+<translation id="3362437373201486687">កំពុងស្កáŸáž“រកឧបករណáŸáž”៊្លូធូស</translation>
<translation id="3386292677130313581">សួរមុនពáŸáž›áž¢áž“ុញ្ញាážáž²áŸ’យគáŸáž áž‘ំពáŸážšážŸáŸ’គាល់ទីážáž¶áŸ†áž„របស់អ្នក (បានណែនាំ)</translation>
<translation id="3538390592868664640">ទប់ស្កាážáŸ‹áž‚áŸáž áž‘ំពáŸážšáž˜áž·áž“ឱ្យបង្កើážáž•áŸ‚នទី 3D នៃមជ្ឈដ្ឋានជុំវិញរបស់អ្នក ឬážáž¶áž˜ážŠáž¶áž“ទីážáž¶áŸ†áž„កាមáŸážšáŸ‰áž¶</translation>
<translation id="3551268116566418498">ចាកចáŸáž‰áž–ី​មុážáž„ារ​ឯកជន​ដែរទáŸ?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ការទាញយកបានបញ្ចប់</translation>
<translation id="3987993985790029246">ចម្លងážáŸ†ážŽ</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">អំពី​ទំពáŸážšâ€‹áž“áŸáŸ‡</translation>
<translation id="4002066346123236978">ចំណងជើង</translation>
<translation id="4008040567710660924">អនុញ្ញាážážáž¼áž‚ី​សម្រាប់គáŸáž áž‘ំពáŸážšâ€‹áž‡áž¶áž€áŸ‹áž›áž¶áž€áŸ‹áŸ”</translation>
<translation id="4046123991198612571">បទ​បន្ទាប់</translation>
<translation id="4149994727733219643">ទិដ្ឋភាព​សាមញ្ញ​សម្រាប់​ទំពáŸážšáž”ណ្ដាញ</translation>
<translation id="4165986682804962316">ការកំណážáŸ‹áž‚áŸáž áž‘ំពáŸážš</translation>
+<translation id="4169549551965910670">បានភ្ជាប់​ទៅ​ឧបករណ០USB</translation>
<translation id="4194328954146351878">សួរ​មុនពáŸáž›â€‹áž¢áž“ុញ្ញាážáž±áŸ’យ​គáŸáž áž‘ំពáŸážšáž˜áž¾áž›ážƒáž¾áž‰ និងផ្លាស់ប្ដូរពáŸážáŸŒáž˜áž¶áž“នៅលើឧបករណ០NFC (បានណែនាំ)</translation>
<translation id="4200726100658658164">បើកការកំណážáŸ‹áž‘ីážáž¶áŸ†áž„</translation>
<translation id="4226663524361240545">ការជូនដំណឹងអាចនឹងធ្វើឲ្យឧបករណáŸáž‰áŸážš</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ជានិច្ច</translation>
<translation id="757524316907819857">ទប់ស្កាážáŸ‹â€‹áž‚áŸáž áž‘ំពáŸážšâ€‹áž˜áž·áž“ឱ្យ​លáŸáž„ážáŸ’លឹមសារ​ដែលមានការការពារ</translation>
<translation id="7577900504646297215">គ្រប់គ្រង​ចំណាប់អារម្មណáŸ</translation>
+<translation id="7594634374516752650">បានភ្ជាប់ឧបករណáŸáž”៊្លូធូស</translation>
<translation id="7649070708921625228">ជំនួយ</translation>
<translation id="7658239707568436148">បដិសáŸáž’</translation>
<translation id="7781829728241885113">ម្សិលមិញ</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kn.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kn.xtb
index 7d42ff90f63..abc3da2c4bf 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kn.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kn.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">ಮಾಹಿತಿಯನà³à²¨à³ ಮರೆಮಾಡಿ</translation>
<translation id="3328801116991980348">ಸೈಟೠಮಾಹಿತಿ</translation>
<translation id="3333961966071413176">ಎಲà³à²²à²¾ ಸಂಪರà³à²•à²—ಳà³</translation>
+<translation id="3362437373201486687">ಬà³à²²à³‚ಟೂತೠಸಾಧನಳನà³à²¨à³ ಹà³à²¡à³à²•à³à²µà³à²¦à²•à³à²•à²¾à²—ಿ ಸà³à²•à³à²¯à²¾à²¨à³ ಮಾಡಲಾಗà³à²¤à³à²¤à²¿à²¦à³†</translation>
<translation id="3386292677130313581">ನಿಮà³à²® ಸà³à²¥à²³à²µà²¨à³à²¨à³ ತಿಳಿಯಲೠಸೈಟà³â€Œà²—ಳಿಗೆ ಅನà³à²®à²¤à²¿à²¸à³à²µ ಮೊದಲೠಕೇಳಿ (ಶಿಫಾರಸೠಮಾಡಲಾಗಿದೆ)</translation>
<translation id="3538390592868664640">ನಿಮà³à²® ಸà³à²¤à³à²¤à²®à³à²¤à³à²¤à²²à²¿à²¨ 3D ನಕà³à²·à³†à²—ಳನà³à²¨à³ ರಚಿಸà³à²µà³à²¦à²°à²¿à²‚ದ ಅಥವಾ ಕà³à²¯à²¾à²®à²°à²¾ ಸà³à²¥à²¿à²¤à²¿à²¯à²¨à³à²¨à³ ಟà³à²°à³à²¯à²¾à²•à³ ಮಾಡà³à²µà³à²¦à²°à²¿à²‚ದ ಸೈಟà³â€Œà²—ಳನà³à²¨à³ ನಿರà³à²¬à²‚ಧಿಸಿ</translation>
<translation id="3551268116566418498">ಅಜà³à²žà²¾à²¤ ಮೋಡೠತೊರೆಯà³à²µà³à²¦à³‡?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ಡೌನà³â€Œà²²à³‹à²¡à³â€Œâ€Œ ಪೂರà³à²£à²—ೊಂಡಿದೆ</translation>
<translation id="3987993985790029246">ಲಿಂಕೠನಕಲಿಸಿ</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">ಈ ಪà³à²Ÿà²¦ ಕà³à²°à²¿à²¤à³</translation>
<translation id="4002066346123236978">ಶೀರà³à²·à²¿à²•à³†</translation>
<translation id="4008040567710660924">ನಿರà³à²¦à²¿à²·à³à²Ÿ ಸೈಟà³â€Œ ಒಂದಕà³à²•à³† ಕà³à²•à³€à²—ಳನà³à²¨à³ ಅನà³à²®à²¤à²¿à²¸à²¿.</translation>
<translation id="4046123991198612571">ಮà³à²‚ದಿನ ಟà³à²°à³à²¯à²¾à²•à³</translation>
<translation id="4149994727733219643">ವೆಬೠಪà³à²Ÿà²—ಳಿಗಾಗಿ ಸರಳೀಕೃತ ವೀಕà³à²·à²£à³†</translation>
<translation id="4165986682804962316">ಸೈಟೠಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳà³</translation>
+<translation id="4169549551965910670">USB ಸಾಧನವೊಂದಕà³à²•à³† ಕನೆಕà³à²Ÿà³ ಮಾಡಲಾಗಿದೆ</translation>
<translation id="4194328954146351878">NFC ಸಾಧನಗಳಲà³à²²à²¿ ಮಾಹಿತಿಯನà³à²¨à³ ನೋಡಲೠಮತà³à²¤à³ ಬದಲಾಯಿಸಲೠಸೈಟà³â€Œà²—ಳಿಗೆ ಅನà³à²®à²¤à²¿à²¸à³à²µ ಮೊದಲೠಕೇಳಿ (ಶಿಫಾರಸೠಮಾಡಲಾಗಿದೆ)</translation>
<translation id="4200726100658658164">ಸà³à²¥à²³ ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳನà³à²¨à³ ತೆರೆಯಿರಿ</translation>
<translation id="4226663524361240545">ಪà³à²°à²•à²Ÿà²£à³†à²—ಳೠಸಾಧನವನà³à²¨à³ ವೈಬà³à²°à³‡à²Ÿà³ ಮಾಡಬಹà³à²¦à³</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ಯಾವಾಗಲೂ</translation>
<translation id="757524316907819857">ಸಂರಕà³à²·à²¿à²¤ ವಿಷಯವನà³à²¨à³ ಪà³à²²à³‡ ಮಾಡದಂತೆ ಸೈಟà³â€Œà²—ಳನà³à²¨à³ ನಿರà³à²¬à²‚ಧಿಸಿ</translation>
<translation id="7577900504646297215">ಆಸಕà³à²¤à²¿à²—ಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿</translation>
+<translation id="7594634374516752650">ಬà³à²²à³‚ಟೂತೠಸಾಧನಕà³à²•à³† ಕನೆಕà³à²Ÿà³ ಮಾಡಲಾಗಿದೆ</translation>
<translation id="7649070708921625228">ಸಹಾಯ</translation>
<translation id="7658239707568436148">ರದà³à²¦à³à²®à²¾à²¡à²¿</translation>
<translation id="7781829728241885113">ನಿನà³à²¨à³†</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ko.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ko.xtb
index 383dd7f4b71..40b47d0cea8 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ko.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ko.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">정보 숨기기</translation>
<translation id="3328801116991980348">사ì´íŠ¸ ì •ë³´</translation>
<translation id="3333961966071413176">모든 ì—°ë½ì²˜</translation>
+<translation id="3362437373201486687">블루투스 기기 검색 중</translation>
<translation id="3386292677130313581">사ì´íŠ¸ì—ì„œ ë‚´ 위치를 파악하ë„ë¡ í—ˆìš©í•˜ê¸° ì „ì— í™•ì¸(권장)</translation>
<translation id="3538390592868664640">사ì´íŠ¸ì—ì„œ 주변 í™˜ê²½ì˜ 3D 지ë„를 ìƒì„±í•˜ê±°ë‚˜ ì¹´ë©”ë¼ ìœ„ì¹˜ë¥¼ 추ì í•˜ì§€ 못하ë„ë¡ ì°¨ë‹¨</translation>
<translation id="3551268116566418498">ì‹œí¬ë¦¿ 모드를 종료하시겠습니까?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">다운로드 완료</translation>
<translation id="3987993985790029246">ë§í¬ 복사</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">ì´ íŽ˜ì´ì§€ì— 관한 ì •ë³´</translation>
<translation id="4002066346123236978">제목</translation>
<translation id="4008040567710660924">특정 사ì´íŠ¸ì˜ 쿠키를 허용합니다.</translation>
<translation id="4046123991198612571">ë‹¤ìŒ íŠ¸ëž™</translation>
<translation id="4149994727733219643">웹페ì´ì§€ 간단히 보기</translation>
<translation id="4165986682804962316">사ì´íŠ¸ 설정</translation>
+<translation id="4169549551965910670">USB ê¸°ê¸°ì— ì—°ê²°ë˜ì—ˆìŠµë‹ˆë‹¤.</translation>
<translation id="4194328954146351878">사ì´íŠ¸ì—ì„œ NFC ê¸°ê¸°ì˜ ì •ë³´ë¥¼ ë³´ê³  변경하ë„ë¡ í—ˆìš©í•˜ê¸° ì „ì— í™•ì¸(권장)</translation>
<translation id="4200726100658658164">위치 설정 열기</translation>
<translation id="4226663524361240545">ì•Œë¦¼ì´ ìžˆìœ¼ë©´ 진ë™ì´ 울릴 ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤.</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">í•­ìƒ</translation>
<translation id="757524316907819857">사ì´íŠ¸ì—ì„œ ë³´í˜¸ëœ ì½˜í…츠를 재ìƒí•˜ì§€ 못하ë„ë¡ ì°¨ë‹¨</translation>
<translation id="7577900504646297215">관심분야 관리</translation>
+<translation id="7594634374516752650">블루투스 ê¸°ê¸°ì— ì—°ê²°ë¨</translation>
<translation id="7649070708921625228">ë„움ë§</translation>
<translation id="7658239707568436148">취소</translation>
<translation id="7781829728241885113">어제</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ky.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ky.xtb
index 870ef16979b..9a626251a67 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ky.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ky.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Маалыматты жашыруу</translation>
<translation id="3328801116991980348">Сайт жөнүндө маалымат</translation>
<translation id="3333961966071413176">Бардык байланыштар</translation>
+<translation id="3362437373201486687">Bluetooth түзмөктөрү изделип жатат</translation>
<translation id="3386292677130313581">Сайттарга жайгашкан жериңиз тууралуу маалымат берүүдөн мурун урукÑат ÑуралÑын (Ñунушталат)</translation>
<translation id="3538390592868664640">Сайттарга айланаңыздын 3D картаÑын түзүүгө же камераңыздын абалына көз Ñалууга бөгөт коюуңуз</translation>
<translation id="3551268116566418498">Жашыруун режимден чыгаÑызбы?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Жүктөп алуу аÑктады</translation>
<translation id="3987993985790029246">Шилтм көчр</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Бул бет жөнүндө</translation>
<translation id="4002066346123236978">Ðталышы</translation>
<translation id="4008040567710660924">Белгилүү бир Ñайттын cookie файлдарына урукÑат берүү.</translation>
<translation id="4046123991198612571">Кийинки трек</translation>
<translation id="4149994727733219643">Веб барактар үчүн жөнөкөйлөштүрүлгөн көрүнүш</translation>
<translation id="4165986682804962316">Сайт жөндөөлөрү</translation>
+<translation id="4169549551965910670">USB түзмөгүнө туташты</translation>
<translation id="4194328954146351878">Колдонмолор NFC түзмөктөрүндөгү маалыматты көрүп жана өзгөрткөнү жатканда урукÑат ÑуралÑын (Ñунушталат)</translation>
<translation id="4200726100658658164">Жайгашкан жерди аныктоо жөндөөлөрүн ачуу</translation>
<translation id="4226663524361240545">Билдирмелер кегенде түзмөк дирилдейт</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ÐÑ€ дайым</translation>
<translation id="757524316907819857">Сайттарга корголгон мазмунду ойнотууга тыюу ÑалынÑын</translation>
<translation id="7577900504646297215">Кызыккан нерÑелерди башкаруу</translation>
+<translation id="7594634374516752650">Bluetooth түзмөгүнө туташтырылды</translation>
<translation id="7649070708921625228">Жардам</translation>
<translation id="7658239707568436148">Жокко чыгаруу</translation>
<translation id="7781829728241885113">КечÑÑ</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lo.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lo.xtb
index a25a14cba28..cf1df5dcedb 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lo.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lo.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">ເຊື່ອງຂà»à»‰àº¡àº¹àº™</translation>
<translation id="3328801116991980348">ຂà»à»‰â€‹àº¡àº¹àº™â€‹à»€àº§àº±àºšâ€‹à»„ຊທ໌</translation>
<translation id="3333961966071413176">ລາàºàºŠàº·à»ˆàºœàº¹à»‰àº•àº´àº”ຕà»à»ˆàº—ັງà»àº»àº”</translation>
+<translation id="3362437373201486687">àºàº³àº¥àº±àº‡àºªàº°à»àºàº™àº«àº²àº­àº¸àº›àº°àºàº­àº™ Bluetooth</translation>
<translation id="3386292677130313581">ຖາມàºà»ˆàº­àº™àº—ີ່ຈະອະນຸàºàº²àº”ໃຫ້ເວັບໄຊຮູ້ສະຖານທີ່ຂອງທ່ານ (à»àº™àº°àº™àº³)</translation>
<translation id="3538390592868664640">ບລັອàºà»€àº§àº±àºšà»„ຊບà»à»ˆà»ƒàº«à»‰àºªà»‰àº²àº‡à»àºœàº™àº—ີ່ 3 ມິຕິຂອງສິ່ງທີ່ຢູ່ອ້ອມຂ້າງຕົວທ່ານ ຫຼື ຕິດຕາມຕຳà»à»œà»ˆàº‡àºà»‰àº­àº‡</translation>
<translation id="3551268116566418498">ອອàºàºˆàº²àºà»‚à»àº”ບà»à»ˆà»€àº›àºµàº”ເຜີàºàº•àº»àº§àº•àº»àº™àºšà»?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ດາວ​ໂຫຼດ​ສຳ​ເລັດ</translation>
<translation id="3987993985790029246">ອັດ​ສຳ​ເນົາລິ້ງເຊື່ອມໂàºàº‡</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">àºà»ˆàº½àº§àºàº±àºšà»œà»‰àº²àº™àºµà»‰</translation>
<translation id="4002066346123236978">ຫົວຂà»à»‰</translation>
<translation id="4008040567710660924">ອະນຸàºàº²àº”ຄຸàºàºàºµà»‰àºªàº³àº¥àº±àºšà»€àº§àº±àºšà»„ຊສະເພາະ.</translation>
<translation id="4046123991198612571">ເພງຕà»à»ˆà»„ປ</translation>
<translation id="4149994727733219643">ມຸມມອງງ່າàºàº”າàºàºªàº³àº¥àº±àºšà»œà»‰àº²à»€àº§àº±àºš</translation>
<translation id="4165986682804962316">àºàº²àº™àº•àº±à»‰àº‡àº„່າເວັບໄຊທ໌</translation>
+<translation id="4169549551965910670">ເຊື່ອມຕà»à»ˆàº«àº²àº­àº¸àº›àº°àºàº­àº™ USB à»àº¥à»‰àº§</translation>
<translation id="4194328954146351878">ຖາມàºà»ˆàº­àº™àºàº²àº™àº­àº°àº™àº¸àºàº²àº”ໃຫ້ເວັບໄຊເບິ່ງເຫັນ à»àº¥àº° ປ່ຽນຂà»à»‰àº¡àº¹àº™àº¢àº¹à»ˆàº­àº¸àº›àº°àºàº­àº™ NFC (à»àº™àº°àº™àº³)</translation>
<translation id="4200726100658658164">ເປີດàºàº²àº™àº•àº±à»‰àº‡àº„່າສະຖານທີ່</translation>
<translation id="4226663524361240545">àºàº²àº™à»àºˆà»‰àº‡à»€àº•àº·àº­àº™àº­àº²àº”ຈະເຮັດໃຫ້ອຸປະàºàº­àº™àºªàº±à»ˆàº™</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ຢູ່ສະເà»àºµ</translation>
<translation id="757524316907819857">ບລັອàºà»€àº§àº±àºšà»„ຊບà»à»ˆà»ƒàº«à»‰àº«àº¼àº´à»‰àº™à»€àº™àº·à»‰àº­àº«àº²àº—ີ່ມີàºàº²àº™àº›à»‰àº­àº‡àºàº±àº™à»„ວ້</translation>
<translation id="7577900504646297215">ຈັດàºàº²àº™àº„ວາມສົນໃຈ</translation>
+<translation id="7594634374516752650">ເຊື່ອມຕà»à»ˆàº«àº²àº­àº¸àº›àº°àºàº­àº™ Bluetooth à»àº¥à»‰àº§</translation>
<translation id="7649070708921625228">ຊ່ວàºâ€‹à»€àº«àº¼àº·àº­</translation>
<translation id="7658239707568436148">àºàº»àºâ€‹à»€àº¥àºµàºâ€‹</translation>
<translation id="7781829728241885113">ມື້​ວານ​ນີ້</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lt.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lt.xtb
index b9f33e931f3..5cebe75d186 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lt.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lt.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">SlÄ—pti informacijÄ…</translation>
<translation id="3328801116991980348">SvetainÄ—s informacija</translation>
<translation id="3333961966071413176">Visi kontaktai</translation>
+<translation id="3362437373201486687">Nuskaitoma ieškant „Bluetooth“ įrenginių</translation>
<translation id="3386292677130313581">Klausti prieš leidžiant svetainėms žinoti vietą (rekomenduojama)</translation>
<translation id="3538390592868664640">Blokuoti, kad svetainės nekurtų jūsų aplinkos 3D žemėlapio ir nestebėtų kameros padėties</translation>
<translation id="3551268116566418498">Išjungti inkognito režimą?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Atsisiuntimas baigtas</translation>
<translation id="3987993985790029246">Kop. nuor.</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> iš ?</translation>
+<translation id="3992684624889376114">Apie šį puslapį</translation>
<translation id="4002066346123236978">Pavadinimas</translation>
<translation id="4008040567710660924">Leisti konkreÄios svetainÄ—s slapukus.</translation>
<translation id="4046123991198612571">Kitas takelis</translation>
<translation id="4149994727733219643">Supaprastinta tinklalapių peržiūra</translation>
<translation id="4165986682804962316">SvetainÄ—s nustatymai</translation>
+<translation id="4169549551965910670">Prisijungta prie USB įrenginio</translation>
<translation id="4194328954146351878">Klausti prieš leidžiant svetainėms peržiūrėti ir keisti informaciją NFC įrenginiuose (rekomenduojama)</translation>
<translation id="4200726100658658164">Atidaryti vietovÄ—s nustatymus</translation>
<translation id="4226663524361240545">Gavus pranešimą įrenginys gali vibruoti</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Visada</translation>
<translation id="757524316907819857">Užblokuoti svetaines, kad neleistų apsaugoto turinio</translation>
<translation id="7577900504646297215">Tvarkyti pomÄ—gius</translation>
+<translation id="7594634374516752650">Prijungta prie „Bluetooth“ įrenginio</translation>
<translation id="7649070708921625228">Pagalba</translation>
<translation id="7658239707568436148">Atšaukti</translation>
<translation id="7781829728241885113">Vakar</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lv.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lv.xtb
index 0d145be493d..17d7664a516 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lv.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lv.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">SlÄ“pt informÄciju</translation>
<translation id="3328801116991980348">Vietnes informÄcija</translation>
<translation id="3333961966071413176">Visas kontaktpersonas</translation>
+<translation id="3362437373201486687">Notiek Bluetooth ierÄ«Äu meklÄ“Å¡ana…</translation>
<translation id="3386292677130313581">JautÄt, pirms atļaut vietnÄ“m uzzinÄt jÅ«su atraÅ¡anÄs vietu (ieteicams)</translation>
<translation id="3538390592868664640">Neļaut vietnÄ“m izveidot jÅ«su apkÄrtnes 3D karti vai izsekot kameras pozÄ«ciju</translation>
<translation id="3551268116566418498">Vai iziet no inkognito režīma?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">LejupielÄde pabeigta</translation>
<translation id="3987993985790029246">Saites kopēšana</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> no ?</translation>
+<translation id="3992684624889376114">Par Å¡o lapu</translation>
<translation id="4002066346123236978">Nosaukums</translation>
<translation id="4008040567710660924">Atļaut sīkfailus konkrētai vietnei.</translation>
<translation id="4046123991198612571">NÄkamais ieraksts</translation>
<translation id="4149994727733219643">VienkÄrÅ¡ots tÄ«mekļa lapu skatÄ«jums</translation>
<translation id="4165986682804962316">Vietnes iestatījumi</translation>
+<translation id="4169549551965910670">Izveidots savienojums ar USB ierīci.</translation>
<translation id="4194328954146351878">VaicÄt, pirms ļaut vietnÄ“m NFC ierÄ«cÄ“s piekļūt informÄcijai un to mainÄ«t (ieteicams)</translation>
<translation id="4200726100658658164">AtvÄ“rt atraÅ¡anÄs vietu iestatÄ«jumus</translation>
<translation id="4226663524361240545">Saņemot paziņojumu, ierīce var vibrēt.</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Vienmēr</translation>
<translation id="757524316907819857">Neļaut vietnÄ“m atskaņot aizsargÄtu saturu</translation>
<translation id="7577900504646297215">PÄrvaldÄ«t intereses</translation>
+<translation id="7594634374516752650">Izveidots savienojums ar Bluetooth ierīci</translation>
<translation id="7649070708921625228">Palīdzība</translation>
<translation id="7658239707568436148">Atcelt</translation>
<translation id="7781829728241885113">Vakar</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mk.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mk.xtb
index 8b8e425ed21..b021611dd49 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mk.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mk.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Сокриј ги информациите</translation>
<translation id="3328801116991980348">Информации за веб-локација</translation>
<translation id="3333961966071413176">Сите контакти</translation>
+<translation id="3362437373201486687">Скенира за уреди Ñо Bluetooth</translation>
<translation id="3386292677130313581">Прашај пред да дозволиш Ñајтовите да ја дознаат локацијата (Ñе препорачува)</translation>
<translation id="3538390592868664640">Ðе дозволувај им на Ñајтовите да Ñоздаваат 3D-карта на опкружувањето или да ја Ñледат позицијата на камерата</translation>
<translation id="3551268116566418498">Да Ñе излезе од „Инкогнито“?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Преземањето е завршено</translation>
<translation id="3987993985790029246">Копирај линк</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">За оваа Ñтраница</translation>
<translation id="4002066346123236978">ÐаÑлов</translation>
<translation id="4008040567710660924">Дозволете колачиња за конкретен Ñајт.</translation>
<translation id="4046123991198612571">Следна пеÑна</translation>
<translation id="4149994727733219643">ПоедноÑтавен приказ за веб-Ñтраници</translation>
<translation id="4165986682804962316">ПоÑтавки на локација</translation>
+<translation id="4169549551965910670">Поврзано Ñо USB-уред</translation>
<translation id="4194328954146351878">Прашај пред да дозволиш Ñајтовите да гледаат и менуваат податоци на NFC-уреди (Ñе препорачува)</translation>
<translation id="4200726100658658164">Отворете ги поÑтавките за локација</translation>
<translation id="4226663524361240545">ИзвеÑтувањата може да предизвикаат вибрации на уредот</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Секогаш</translation>
<translation id="757524316907819857">Блокирај го пуштањето заштитени Ñодржини од Ñтрана на Ñајтовите</translation>
<translation id="7577900504646297215">Управувајте Ñо она што ве интереÑира</translation>
+<translation id="7594634374516752650">Поврзан Ñо уред Ñо Bluetooth</translation>
<translation id="7649070708921625228">Помош</translation>
<translation id="7658239707568436148">Откажи</translation>
<translation id="7781829728241885113">Вчера</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ml.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ml.xtb
index 9ed8e9242cd..4cec675fa09 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ml.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ml.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">വിവരങàµà´™àµ¾ മറയàµâ€Œà´•àµà´•àµà´•</translation>
<translation id="3328801116991980348">സൈറàµà´±àµ വിവരങàµà´™à´³àµâ€</translation>
<translation id="3333961966071413176">à´Žà´²àµà´²à´¾ കോൺടാകàµà´±àµà´±àµà´•à´³àµà´‚</translation>
+<translation id="3362437373201486687">Bluetooth ഉപകരണങàµà´™àµ¾à´•àµà´•à´¾à´¯à´¿ à´¸àµâ€Œà´•à´¾àµ» ചെയàµà´¯àµà´¨àµà´¨àµ</translation>
<translation id="3386292677130313581">നിങàµà´™à´³àµà´Ÿàµ† ലൊകàµà´•àµ‡à´·àµ» അറിയാൻ സൈറàµà´±àµà´•à´³àµ† à´…à´¨àµà´µà´¦à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ à´®àµà´®àµà´ªàµ ചോദികàµà´•àµà´• (à´¶àµà´ªà´¾àµ¼à´¶à´šàµ†à´¯àµâ€Œà´¤à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ)</translation>
<translation id="3538390592868664640">നിങàµà´™à´³àµà´Ÿàµ† à´šàµà´±àµà´±àµà´ªà´¾à´Ÿàµà´•à´³àµà´Ÿàµ† 3D മാപàµà´ªàµ സൃഷàµà´Ÿà´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿àµ½ നിനàµà´¨àµ‹ à´•àµà´¯à´¾à´®à´±à´¯àµà´Ÿàµ† à´¸àµà´¥à´¾à´¨à´‚ à´Ÿàµà´°à´¾à´•àµà´•àµ ചെയàµà´¯àµà´¨àµà´¨à´¤à´¿àµ½ നിനàµà´¨àµ‹ സൈറàµà´±àµà´•à´³àµ† à´¬àµà´²àµ‹à´•àµà´•àµ ചെയàµà´¯àµà´•</translation>
<translation id="3551268116566418498">അദൃശàµà´¯ മോഡൠവിടണോ?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ഡൗൺലോഡൠപൂർതàµà´¤à´¿à´¯à´¾à´¯à´¿</translation>
<translation id="3987993985790029246">ലിങàµà´•àµ പകർതàµà´¤àµà´•</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">à´ˆ പേജിനെകàµà´•àµà´±à´¿à´šàµà´šàµ</translation>
<translation id="4002066346123236978">ശീർഷകം</translation>
<translation id="4008040567710660924">ഒരൠപàµà´°à´¤àµà´¯àµ‡à´• സൈറàµà´±à´¿à´¨à´¾à´¯à´¿ à´•àµà´•àµà´•à´¿à´•àµ¾ à´…à´¨àµà´µà´¦à´¿à´•àµà´•àµà´•.</translation>
<translation id="4046123991198612571">à´…à´Ÿàµà´¤àµà´¤ à´Ÿàµà´°à´¾à´•àµà´•àµ</translation>
<translation id="4149994727733219643">വെബൠപേജàµà´•àµ¾à´•àµà´•à´¾à´¯à´¿ ലളിതവൽകàµà´•à´°à´¿à´šàµà´š കാഴàµà´š</translation>
<translation id="4165986682804962316">സൈറàµà´±àµ à´•àµà´°à´®àµ€à´•à´°à´£à´™àµà´™àµ¾</translation>
+<translation id="4169549551965910670">USB ഉപകരണതàµà´¤à´¿à´²àµ‡à´•àµà´•àµ കണകàµà´±àµà´±àµ ചെയàµà´¤àµ</translation>
<translation id="4194328954146351878">NFC ഉപകരണങàµà´™à´³à´¿à´²àµ† വിവരങàµà´™àµ¾ കാണാനàµà´‚ അവയിൽ മാറàµà´±à´‚ വരàµà´¤àµà´¤à´¾à´¨àµà´‚ സൈറàµà´±àµà´•à´³àµ† à´…à´¨àµà´µà´¦à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ à´®àµà´®àµà´ªàµ ചോദികàµà´•àµà´• (നിർദàµà´¦àµ‡à´¶à´¿à´•àµà´•àµà´¨àµà´¨à´¤àµ)</translation>
<translation id="4200726100658658164">ലൊകàµà´•àµ‡à´·àµ» à´•àµà´°à´®àµ€à´•à´°à´£à´‚ à´¤àµà´±à´•àµà´•àµà´•</translation>
<translation id="4226663524361240545">അറിയിപàµà´ªàµà´•àµ¾ ലഭികàµà´•àµà´®àµà´ªàµ‹àµ¾ ഉപകരണം വൈബàµà´°àµ‡à´±àµà´±àµ ചെയàµâ€Œà´¤àµ‡à´•àµà´•à´¾à´‚</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">à´Žà´²àµà´²à´¾à´¯àµà´ªàµà´ªàµ‹à´´àµà´‚</translation>
<translation id="757524316907819857">പരിരകàµà´·à´¿à´¤ ഉളàµà´³à´Ÿà´•àµà´•à´‚ à´ªàµà´²àµ‡ ചെയàµà´¯àµà´¨àµà´¨à´¤à´¿àµ½ നിനàµà´¨àµ സൈറàµà´±àµà´•à´³àµ† à´¬àµà´²àµ‹à´•àµà´•àµ ചെയàµà´¯àµà´•</translation>
<translation id="7577900504646297215">താൽപàµà´ªà´°àµà´¯à´™àµà´™àµ¾ മാനേജൠചെയàµà´¯àµà´•</translation>
+<translation id="7594634374516752650">ഒരൠBluetooth ഉപകരണതàµà´¤à´¿à´²àµ‡à´•àµà´•àµ കണകàµà´±àµà´±àµ ചെയàµà´¤àµ</translation>
<translation id="7649070708921625228">സഹായം</translation>
<translation id="7658239707568436148">റദàµà´¦à´¾à´•àµà´•àµ‚</translation>
<translation id="7781829728241885113">ഇനàµà´¨à´²àµ†</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb
index bf0f280395f..7a3afe81612 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">ÐœÑдÑÑллийг нуух</translation>
<translation id="3328801116991980348">Сайтын мÑдÑÑлÑл</translation>
<translation id="3333961966071413176">Бүх харилцагч</translation>
+<translation id="3362437373201486687">Bluetooth төхөөрөмжүүдийг Ñкан хийж байна</translation>
<translation id="3386292677130313581">Сайтууд байршил мÑдÑÑ… зөвшөөрөл авах (Ñанал болгоÑон)</translation>
<translation id="3538390592868664640">Сайтуудад таны ÑргÑн тойрны 3D газрын зургийг Ò¯Ò¯ÑгÑÑ… ÑÑвÑл камерын хөдөлгөөнийг Ñ…Ñнахыг хориглох</translation>
<translation id="3551268116566418498">Ðууцлалтай Ð³Ð¾Ñ€Ð¸Ð¼Ð¾Ð¾Ñ Ð³Ð°Ñ€Ð°Ñ… уу?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Татаж авч дууÑлаа</translation>
<translation id="3987993985790029246">ХолбооÑыг хуулах</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Ð­Ð½Ñ Ñ…ÑƒÑƒÐ´Ð°Ñны тухай</translation>
<translation id="4002066346123236978">Гарчиг</translation>
<translation id="4008040567710660924">Күүкиг тодорхой Ñайтад зөвшөөрнө Ò¯Ò¯.</translation>
<translation id="4046123991198612571">Дараагийн бичлÑг</translation>
<translation id="4149994727733219643">Веб хуудаÑны Ñ…ÑлбаршуулÑан харагдац</translation>
<translation id="4165986682804962316">Сайтын тохиргоо</translation>
+<translation id="4169549551965910670">USB төхөөрөмжтÑй холбогдлоо</translation>
<translation id="4194328954146351878">Сайтуудыг NFC төхөөрөмжүүд дÑÑрх мÑдÑÑллийг харах болон өөрчлөхийг Ð·Ó©Ð²ÑˆÓ©Ó©Ñ€Ó©Ñ…Ó©Ó©Ñ Ó©Ð¼Ð½Ó© аÑуух (зөвлөÑөн)</translation>
<translation id="4200726100658658164">Байршлын тохиргоог нÑÑÑ…</translation>
<translation id="4226663524361240545">ÐœÑдÑгдÑл ирÑÑ…Ñд төхөөрөмж чичрÑнÑ</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ҮргÑлж</translation>
<translation id="757524316907819857">Хамгаалалттай контентыг тоглуулж буй Ñайтуудыг блок хийх</translation>
<translation id="7577900504646297215">Сонирхлыг удирдах</translation>
+<translation id="7594634374516752650">Bluetooth төхөөрөмжид холбогдлоо</translation>
<translation id="7649070708921625228">ТуÑламж</translation>
<translation id="7658239707568436148">Болих</translation>
<translation id="7781829728241885113">Өчигдөр</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mr.xtb
index 9085935b7b0..9fce8b6d627 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mr.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mr.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">माहिती लपवा</translation>
<translation id="3328801116991980348">साइट माहिती</translation>
<translation id="3333961966071413176">सरà¥à¤µ संपरà¥à¤•</translation>
+<translation id="3362437373201486687">बà¥à¤²à¥‚टूथ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤¸à¤¾à¤ à¥€ सà¥à¤•à¥…न करत आहे</translation>
<translation id="3386292677130313581">साइटना तà¥à¤®à¤šà¥‡ सà¥à¤¥à¤¾à¤¨ जाणून घेणà¥à¤¯à¤¾à¤šà¥€ अनà¥à¤®à¤¤à¥€ देणà¥à¤¯à¤¾à¤ªà¥‚रà¥à¤µà¥€ विचारा (शिफारस केलेले)</translation>
<translation id="3538390592868664640">तà¥à¤®à¤šà¥à¤¯à¤¾ आसपासचà¥à¤¯à¤¾ परिसराचा 3D नकाशा तयार करणà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न किंवा कॅमेरà¥â€à¤¯à¤¾à¤šà¥‡ सà¥à¤¥à¤¾à¤¨ टà¥à¤°à¥…क करणà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न साइट बà¥à¤²à¥‰à¤• करा</translation>
<translation id="3551268116566418498">गà¥à¤ªà¥à¤¤ मोडमधून बाहेर पडायचे का?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">पूरà¥à¤£ डाउनलोड करा</translation>
<translation id="3987993985790029246">लिंक कॉपी करा</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">या पेजबदà¥à¤¦à¤²</translation>
<translation id="4002066346123236978">शीरà¥à¤·à¤•</translation>
<translation id="4008040567710660924">विशिषà¥à¤ŸÂ à¤¸à¤¾à¤‡à¤Ÿà¤¸à¤¾à¤ à¥€Â à¤•à¥à¤•à¥€à¤¨à¤¾Â à¤…नà¥à¤®à¤¤à¥€Â à¤¦à¥à¤¯à¤¾.</translation>
<translation id="4046123991198612571">पà¥à¤¢à¥€à¤² टà¥à¤°à¥…क</translation>
<translation id="4149994727733219643">वेब पेजसाठी सोपा केलेला वà¥à¤¹à¥à¤¯à¥‚</translation>
<translation id="4165986682804962316">साइट सेटिंगà¥à¤œ</translation>
+<translation id="4169549551965910670">USB डिवà¥à¤¹à¤¾à¤‡à¤¸à¤¶à¥€ कनेकà¥à¤Ÿ केले आहे</translation>
<translation id="4194328954146351878">साइटना NFC डिवà¥à¤¹à¤¾à¤‡à¤¸à¤µà¤°à¥€à¤² माहिती पाहू देणà¥à¤¯à¤¾à¤šà¥€ किंवा बदलणà¥à¤¯à¤¾à¤šà¥€ अनà¥à¤®à¤¤à¥€ देणà¥à¤¯à¤¾à¤ªà¥‚रà¥à¤µà¥€ विचारा (शिफारस केलेले)</translation>
<translation id="4200726100658658164">सà¥à¤¥à¤¾à¤¨ सेटिंगà¥à¤œ उघडा</translation>
<translation id="4226663524361240545">सूचनांमà¥à¤³à¥‡ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤šà¥‡ कंपन होऊ शकते</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">नेहमी</translation>
<translation id="757524316907819857">संरकà¥à¤·à¤¿à¤¤ आशय पà¥à¤²à¥‡ करणà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न साइटवर बà¥à¤²à¥‰à¤• करा</translation>
<translation id="7577900504646297215">सà¥à¤µà¤¾à¤°à¤¸à¥à¤¯à¥‡ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा</translation>
+<translation id="7594634374516752650">बà¥à¤²à¥‚टूथ डिवà¥â€à¤¹à¤¾à¤‡à¤¸à¤¶à¥€ कनेकà¥à¤Ÿ केली आहे</translation>
<translation id="7649070708921625228">मदत</translation>
<translation id="7658239707568436148">रदà¥à¤¦ करा</translation>
<translation id="7781829728241885113">काल</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ms.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ms.xtb
index 12eced5b243..010ce0793c1 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ms.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ms.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Sembunyikan Maklumat</translation>
<translation id="3328801116991980348">Maklumat tapak</translation>
<translation id="3333961966071413176">Semua kenalan</translation>
+<translation id="3362437373201486687">Mengimbas peranti Bluetooth</translation>
<translation id="3386292677130313581">Tanya sebelum membenarkan tapak mengetahui lokasi anda (disyorkan)</translation>
<translation id="3538390592868664640">Sekat tapak daripada membuat peta 3D bagi persekitaran anda atau menjejaki kedudukan kamera</translation>
<translation id="3551268116566418498">Tinggalkan mod Inkognito?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Muat turun selesai</translation>
<translation id="3987993985790029246">Salin pautan</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Perihal halaman ini</translation>
<translation id="4002066346123236978">Tajuk</translation>
<translation id="4008040567710660924">Benarkan kuki untuk tapak tertentu.</translation>
<translation id="4046123991198612571">Lagu seterusnya</translation>
<translation id="4149994727733219643">Paparan ringkas bagi halaman web</translation>
<translation id="4165986682804962316">Tetapan tapak</translation>
+<translation id="4169549551965910670">Disambungkan kepada peranti USB</translation>
<translation id="4194328954146351878">Tanya sebelum membenarkan laman melihat dan menukar maklumat pada peranti NFC (disyorkan)</translation>
<translation id="4200726100658658164">Buka Tetapan Lokasi</translation>
<translation id="4226663524361240545">Pemberitahuan boleh menggetarkan peranti</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Sentiasa</translation>
<translation id="757524316907819857">Sekat tapak daripada memainkan kandungan yang dilindungi</translation>
<translation id="7577900504646297215">Urus minat</translation>
+<translation id="7594634374516752650">Disambungkan ke peranti Bluetooth</translation>
<translation id="7649070708921625228">Bantuan</translation>
<translation id="7658239707568436148">Batal</translation>
<translation id="7781829728241885113">Semalam</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_my.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_my.xtb
index 00b7b61fa61..bf7f3716118 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_my.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_my.xtb
@@ -55,7 +55,7 @@
<translation id="2315043854645842844">ကလိုင်းယင့် ဘက်မှ လက်မှá€á€ºá€›á€½á€±á€¸á€™á€¾á€¯á€€á€­á€¯ လည်ပá€á€ºá€žá€Šá€ºá€·á€…နစ်မှ မပံ့ပိုးပါá‹</translation>
<translation id="2321958826496381788">သင်က ယင်းကို ဖá€á€ºá€›á€¡á€†á€„်ပြေသည့် အထိ ဆွဲá€á€”်းကို ဆွဲယူပါዠစာသားá€á€½á€„် စာပိုဒ် á€á€…်á€á€¯á€€á€­á€¯ နှစ်ကြိမ် ပုá€á€ºá€œá€­á€¯á€€á€ºá€žá€Šá€ºá€· နောက်မှာ ဤမျှလောက် ကြီးလျက် မြင်ရသင့်သည်á‹</translation>
<translation id="2359808026110333948">ဆက်လုပ်ရန်</translation>
-<translation id="2379925928934107488">Chrome က á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ မှောင်သည့် အပြင်အဆင်သုံးလျှင် ဖြစ်နိုင်ပါက áŽá€„်းအပြင်အဆင်ကို ပြောင်းပေးသည်</translation>
+<translation id="2379925928934107488">Chrome က á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ အမှောင်နောက်á€á€¶á€žá€¯á€¶á€¸á€œá€»á€¾á€„် ဖြစ်နိုင်ပါက áŽá€„်းအပြင်အဆင်ကို ပြောင်းပေးသည်</translation>
<translation id="2387895666653383613">စာသား စကေး</translation>
<translation id="2402980924095424747"><ph name="MEGABYTES" /> မီဂါဘိုက်</translation>
<translation id="2404630663942400771">{PERMISSIONS_SUMMARY_ALLOWED,plural, =1{<ph name="PERMISSION_1" />አ<ph name="PERMISSION_2" /> နှင့် နောက်ထပ် <ph name="NUM_MORE" /> á€á€¯ á€á€½á€„့်ပြုထားသည်}other{<ph name="PERMISSION_1" />አ<ph name="PERMISSION_2" /> နှင့် နောက်ထပ် <ph name="NUM_MORE" /> á€á€¯ á€á€½á€„့်ပြုထားသည်}}</translation>
@@ -70,7 +70,7 @@
<translation id="2570922361219980984">ဤစက်ပစ္စည်းအá€á€½á€€á€ºá€œá€Šá€ºá€¸ á€á€Šá€ºá€”ေရာ အသုံးပြုá€á€½á€„့်ကို ပိá€á€ºá€‘ားသည်ዠ<ph name="BEGIN_LINK" />Android ဆက်á€á€„်များ<ph name="END_LINK" /> á€á€½á€„် áŽá€„်းကို ဖွင့်ပါá‹</translation>
<translation id="257931822824936280">á€á€»á€²á€·á€‘ားá - á€á€±á€«á€€á€ºá€žá€­á€™á€ºá€¸á€›á€”် နှိပ်ပါ</translation>
<translation id="2586657967955657006">ကလစ်ဘုá€á€º</translation>
-<translation id="2597457036804169544">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ မှောင်သည့် အပြင်အဆင်မပြောင်းပါ</translation>
+<translation id="2597457036804169544">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ အမှောင်နောက်á€á€¶á€žá€­á€¯á€· မပြောင်းပါနှင့်</translation>
<translation id="2621115761605608342">á€á€­á€€á€»á€žá€Šá€·á€ºá€†á€­á€¯á€€á€ºá€¡á€á€½á€€á€º JavaScript ကိုá€á€½á€„့်ပြုပါá‹</translation>
<translation id="2653659639078652383">ပေးပို့ရန်</translation>
<translation id="2677748264148917807">ထွက်á€á€½á€¬á€›á€”်</translation>
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ ဖျောက်ထားရန်</translation>
<translation id="3328801116991980348">ဆိုက် အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸</translation>
<translation id="3333961966071413176">အဆက်အသွယ်အားလုံး</translation>
+<translation id="3362437373201486687">ဘလူးá€á€¯á€žá€ºá€…က်များ ရှာနေသည်</translation>
<translation id="3386292677130313581">သင့်á€á€Šá€ºá€”ေရာကို ဆိုက်များအား အသိမပေးမီ မေးပါ (အကြံပြုထားသည်)</translation>
<translation id="3538390592868664640">သင့်ပá€á€ºá€á€”်းကျင်á 3D မြေပုံဆွဲá€á€¼á€„်း သို့မဟုá€á€º ကင်မရာအနေအထား á€á€¼á€±á€›á€¬á€á€¶á€á€¼á€„်းá€á€­á€¯á€· မပြုလုပ်ရန် á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ ပိá€á€ºá€‘ားသည်</translation>
<translation id="3551268116566418498">ရုပ်ဖျက်မုဒ်မှ ထွက်လိုသလားá‹</translation>
@@ -111,7 +112,7 @@
<translation id="3600792891314830896">အသံဖွင့်နိုင်သော á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ အသံá€á€­á€á€ºá€‘ားသည်</translation>
<translation id="3744111561329211289">နောက်á€á€¶á€á€½á€„် စင့်á€á€ºá€œá€¯á€•á€ºá€á€¼á€„်း</translation>
<translation id="3763247130972274048">áဠစက္ကန့်ကျော်ရန် ဗီဒီယိုဘယ်ဘက် သို့မဟုá€á€º ညာဘက်á€á€½á€„် နှစ်á€á€»á€€á€ºá€á€­á€¯á€·á€•á€«</translation>
-<translation id="3797520601150691162">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€á€…်á€á€¯á€¡á€á€½á€€á€º မှောင်သည့် အပြင်အဆင်ကိုမပြောင်းပါ</translation>
+<translation id="3797520601150691162">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€á€…်á€á€¯á€¡á€á€½á€€á€º အမှောင်နောက်á€á€¶á€žá€­á€¯á€· မပြောင်းပါ</translation>
<translation id="381841723434055211">ဖုန်းနံပါá€á€ºá€™á€»á€¬á€¸</translation>
<translation id="3826050100957962900">ပြင်ပအဖွဲ့အစည်းသို့ လက်မှá€á€ºá€‘ိုးá€á€„်á€á€¼á€„်း</translation>
<translation id="3835233591525155343">သင်á ကိရိယာအသုံးပြုမှု</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ဒေါင်းလုဒ်လုပ်á€á€¼á€„်း ပြည့်စုံပါပြီ</translation>
<translation id="3987993985790029246">လင့်á€á€ºá€€á€°á€¸á€šá€°á€™á€Šá€º</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">ဤစာမျက်နှာ အကြောင်း</translation>
<translation id="4002066346123236978">á€á€±á€«á€„်းစဉ်</translation>
<translation id="4008040567710660924">အá€á€»á€­á€¯á€·á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€¡á€á€½á€€á€º ကွá€á€ºá€€á€®á€¸á€™á€»á€¬á€¸ á€á€½á€„့်ပြုသည်á‹</translation>
<translation id="4046123991198612571">နောက်á€á€…်ပုဒ်</translation>
<translation id="4149994727733219643">á€á€˜á€ºá€…ာမျက်နှာများအá€á€½á€€á€º ရိုးရှင်းသည့်မြင်ကွင်း</translation>
<translation id="4165986682804962316">á€á€€á€ºá€†á€­á€¯á€€á€º ဆက်á€á€„်များ</translation>
+<translation id="4169549551965910670">USB ကိရိယာသို့ á€á€»á€­á€á€ºá€†á€€á€ºá€•á€¼á€®á€¸</translation>
<translation id="4194328954146351878">NFC စက်ပစ္စည်းများရှိ အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€¡á€¬á€¸ ကြည့်á€á€½á€„့်နှင့် ပြောင်းလဲá€á€½á€„့်မပေးမီ ဦးစွာမေးမြန်းပါ (အကြံပြုထားသည်)</translation>
<translation id="4200726100658658164">'á€á€Šá€ºá€”ေရာပြဆက်á€á€„်များ' ကို ဖွင့်ပါ</translation>
<translation id="4226663524361240545">အကြောင်းကြားá€á€»á€€á€ºá€žá€Šá€º စက်ပစ္စည်းကို á€á€¯á€”်á€á€«á€…ေပါမည်</translation>
@@ -256,7 +259,7 @@
<translation id="7066151586745993502">{NUM_SELECTED,plural, =1{1 á€á€¯ ရွေးထားသည်}other{# á€á€¯ ရွေးထားသည်}}</translation>
<translation id="7087918508125750058"><ph name="ITEM_COUNT" /> á€á€¯ ရွေးထားသည်ዠမျက်နှာပြင်á ထိပ်နားá€á€½á€„် ရွေးá€á€»á€šá€ºá€…ရာများ ရှိသည်</translation>
<translation id="7141896414559753902">ပေါ့ပ်အပ်နှင့် á€á€…်ဆင့်ပြန်ညွှန်á€á€»á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ မဖော်ပြနိုင်စေရန် á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€¡á€¬á€¸ ပိá€á€ºá€•á€« (အကြံပြုထားသည်)</translation>
-<translation id="7176368934862295254"><ph name="KILOBYTES" /> ကီလိုဘိုက်</translation>
+<translation id="7176368934862295254"><ph name="KILOBYTES" /> KB</translation>
<translation id="7180611975245234373">ပြန်လည်စá€á€„်ရန်</translation>
<translation id="723171743924126238">ပုံများကို ရွေးရန်</translation>
<translation id="7243308994586599757">ရွေးစရာများမှာ မျက်နှာပြင်á အောက်á€á€¼á€±á€•á€­á€¯á€„်းနားမှာ ရှိကြသည်</translation>
@@ -265,12 +268,13 @@
<translation id="7302486331832100261">ပုံမှန်အားဖြင့် သင်သည် အကြောင်းကြားá€á€»á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ ပိá€á€ºá€‘ားသည်ዠá€á€½á€„့်ပြုရန် 'အသေးစိá€á€ºá€™á€»á€¬á€¸' ကို á€á€­á€¯á€·á€•á€«á‹</translation>
<translation id="7423098979219808738">အရင်မေးပါ</translation>
<translation id="7423538860840206698">ကလစ်ဘုá€á€ºá€€á€­á€¯ ကြည့်ရှုá€á€½á€„့် ပိá€á€ºá€‘ားသည်</translation>
-<translation id="7425915948813553151">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€¡á€á€½á€€á€º မှောင်သည့် အပြင်အဆင်</translation>
+<translation id="7425915948813553151">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€¡á€á€½á€€á€º အမှောင်နောက်á€á€¶</translation>
<translation id="7521387064766892559">JavaScript</translation>
<translation id="7554752735887601236">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€á€…်á€á€¯á€€ သင့်မိုက်ကရိုဖုန်းကို အသုံးပြုနေသည်</translation>
<translation id="7561196759112975576">အမြဲá€á€™á€ºá€¸</translation>
<translation id="757524316907819857">ကာကွယ်ထားသည့် အကြောင်းအရာများကို မပြသရန် á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ ပိá€á€ºá€•á€«</translation>
<translation id="7577900504646297215">စိá€á€ºá€á€„်စားမှုများ စီမံရန်</translation>
+<translation id="7594634374516752650">ဘလူးá€á€¯á€žá€ºá€žá€¯á€¶á€¸á€…က်သို့ á€á€»á€­á€á€ºá€†á€€á€ºá€‘ားသည်</translation>
<translation id="7649070708921625228">အကူအညီ</translation>
<translation id="7658239707568436148">မလုပ်á€á€±á€¬á€·</translation>
<translation id="7781829728241885113">မနေ့က</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ne.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ne.xtb
index e0fd0f6839d..b526160a9d0 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ne.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ne.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">जानकारी लà¥à¤•à¤¾à¤‰à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3328801116991980348">साइट जानकारी</translation>
<translation id="3333961966071413176">सबै समà¥à¤ªà¤°à¥à¤•à¤¹à¤°à¥‚</translation>
+<translation id="3362437373201486687">बà¥à¤²à¥à¤Ÿà¥à¤¥ डिभाइसहरू छनॠकि छैननॠभनी जाà¤à¤š गरिà¤à¤¦à¥ˆ छ</translation>
<translation id="3386292677130313581">साइटहरूलाई तपाईà¤à¤•à¥‹ सà¥à¤¥à¤¾à¤¨ थाहा पाउने अनà¥à¤®à¤¤à¤¿ दिनॠभनà¥à¤¦à¤¾ पहिले तपाईà¤à¤²à¤¾à¤ˆ सोधà¥à¤¨à¥‡ (सिफारिस गरिà¤à¤•à¥‹)</translation>
<translation id="3538390592868664640">साइटहरूलाई आफू वरपरको ठाउà¤à¤•à¥‹ 3D नकà¥à¤¸à¤¾ बनाउन वा कà¥à¤¯à¤¾à¤®à¥‡à¤°à¤¾à¤•à¥‹ अवसà¥à¤¥à¤¾ पतà¥à¤¤à¤¾ लगाउन नदिनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3551268116566418498">इनà¥à¤•à¥‹à¤—à¥à¤¨à¤¿à¤Ÿà¥‹ मोडबाट बाहिरिने हो?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">डाउनलोड पूरà¥à¤£ भयो</translation>
<translation id="3987993985790029246">लिंक पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">यो पेजका बारेमा</translation>
<translation id="4002066346123236978">शीरà¥à¤·à¤•</translation>
<translation id="4008040567710660924">कà¥à¤¨à¥ˆ खास साइटमा कà¥à¤•à¥€à¤¹à¤°à¥‚लाई अनà¥à¤®à¤¤à¤¿ दिनà¥à¤¹à¥‹à¤¸à¥à¥¤</translation>
<translation id="4046123991198612571">अरà¥à¤•à¥‹ टà¥à¤°à¥à¤¯à¤¾à¤•</translation>
<translation id="4149994727733219643">वेब पृषà¥à¤ à¤¹à¤°à¥‚का लागि सरलीकृत दृशà¥à¤¯</translation>
<translation id="4165986682804962316">साइट सेटिङहरू</translation>
+<translation id="4169549551965910670">USB डिभाइसमा कनेकà¥à¤Ÿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="4194328954146351878">साइटहरूलाई NFC डिभाइसमा भà¤à¤•à¤¾ जानकारी हेरà¥à¤¨à¥‡ र ती जानकारी परिवरà¥à¤¤à¤¨ गरà¥à¤¨à¥‡ अनà¥à¤®à¤¤à¤¿ दिनà¥à¤…घि मलाई सोधियोसॠ(सिफारिस गरिà¤à¤•à¥‹)</translation>
<translation id="4200726100658658164">लोकेसन सेटिङ खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="4226663524361240545">सूचनाहरूले गरà¥à¤¦à¤¾ यनà¥à¤¤à¥à¤° कमà¥à¤ªà¤¨ गरà¥à¤¨ सकà¥à¤›</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">सधैं</translation>
<translation id="757524316907819857">साइटहरूलाई संरकà¥à¤·à¤¿à¤¤ सामगà¥à¤°à¥€ पà¥à¤²à¥‡ गरà¥à¤¨ रोक लगाउनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="7577900504646297215">रà¥à¤šà¤¿à¤¹à¤°à¥‚ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="7594634374516752650">कà¥à¤¨à¥ˆ बà¥à¤²à¥à¤Ÿà¥à¤¥ डिभाइसमा कनेकà¥à¤Ÿ गरिà¤à¤•à¥‹ छ</translation>
<translation id="7649070708921625228">मदà¥à¤¦à¤¤</translation>
<translation id="7658239707568436148">रदà¥à¤¦ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="7781829728241885113">हिजो</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_nl.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_nl.xtb
index 4307a299f86..411aad3a6c1 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_nl.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_nl.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Informatie verbergen</translation>
<translation id="3328801116991980348">Site-informatie</translation>
<translation id="3333961966071413176">Alle contacten</translation>
+<translation id="3362437373201486687">Scannen naar bluetooth-apparaten</translation>
<translation id="3386292677130313581">Eerst vragen voordat sites toegang krijgen tot je locatie (aanbevolen)</translation>
<translation id="3538390592868664640">Voorkomen dat sites een 3D-kaart van je omgeving maken of de camerapositie volgen</translation>
<translation id="3551268116566418498">Incognitomodus verlaten?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Downloaden voltooid</translation>
<translation id="3987993985790029246">Link kop.</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> van ?</translation>
+<translation id="3992684624889376114">Over deze pagina</translation>
<translation id="4002066346123236978">Titel</translation>
<translation id="4008040567710660924">Cookies voor een specifieke site toestaan.</translation>
<translation id="4046123991198612571">Volgend nummer</translation>
<translation id="4149994727733219643">Vereenvoudigde weergave voor webpagina's</translation>
<translation id="4165986682804962316">Site-instellingen</translation>
+<translation id="4169549551965910670">Verbonden met een USB-apparaat</translation>
<translation id="4194328954146351878">Vragen voordat sites informatie op NFC-apparaten mogen zien en wijzigen (aanbevolen)</translation>
<translation id="4200726100658658164">Locatie-instellingen openen</translation>
<translation id="4226663524361240545">Het apparaat kan trillen bij meldingen</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Altijd</translation>
<translation id="757524316907819857">Blokkeren dat sites beschermde content kunnen afspelen</translation>
<translation id="7577900504646297215">Interesses beheren</translation>
+<translation id="7594634374516752650">Verbonden met een bluetooth-apparaat</translation>
<translation id="7649070708921625228">Hulp</translation>
<translation id="7658239707568436148">Annuleren</translation>
<translation id="7781829728241885113">Gisteren</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_no.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_no.xtb
index 2e5b54f2712..1552e3f7ea0 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_no.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_no.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Skjul informasjonen</translation>
<translation id="3328801116991980348">Informasjon om nettstedet</translation>
<translation id="3333961966071413176">Alle kontakter</translation>
+<translation id="3362437373201486687">Skanner etter Bluetooth-enheter</translation>
<translation id="3386292677130313581">Spør før nettsteder får vite posisjonen min (anbefales)</translation>
<translation id="3538390592868664640">Blokkér nettsteder fra å lage 3D-kart av omgivelsene dine eller spore kameraposisjonen</translation>
<translation id="3551268116566418498">Vil du avslutte Inkognitomodus?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Nedlasting fullført</translation>
<translation id="3987993985790029246">Kopiér link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Om denne siden</translation>
<translation id="4002066346123236978">Tittel</translation>
<translation id="4008040567710660924">Tillat informasjonskapsler for et spesifikt nettsted.</translation>
<translation id="4046123991198612571">Neste spor</translation>
<translation id="4149994727733219643">Forenklet visning av nettsider</translation>
<translation id="4165986682804962316">Nettstedsinnstillinger</translation>
+<translation id="4169549551965910670">Koblet til en USB-enhet</translation>
<translation id="4194328954146351878">Spør før nettsteder får lov til å se og endre informasjon på NFC-enheter (anbefales)</translation>
<translation id="4200726100658658164">Ã…pne posisjonsinnstillingene</translation>
<translation id="4226663524361240545">Varsler kan gjøre at enheten vibrerer</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Alltid</translation>
<translation id="757524316907819857">Blokkér nettsteder fra å spille av beskyttet innhold</translation>
<translation id="7577900504646297215">Administrer interesser</translation>
+<translation id="7594634374516752650">Koblet til en Bluetooth-enhet</translation>
<translation id="7649070708921625228">Hjelp</translation>
<translation id="7658239707568436148">Avbryt</translation>
<translation id="7781829728241885113">I går</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_or.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_or.xtb
index 5b98056c628..bfcdd89031c 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_or.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_or.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">ସୂଚନା ଲà­à¬šà¬¾à¬¨à­à¬¤à­</translation>
<translation id="3328801116991980348">ସାଇଟà­â€Œ ସୂଚନା</translation>
<translation id="3333961966071413176">ସମସà­à¬¤ ଯୋଗାଯୋଗଗà­à¬¡à¬¼à¬¿à¬•</translation>
+<translation id="3362437373201486687">ବà­à¬²à­à¬Ÿà­à¬¥ ଡିଭାଇସଗà­à­œà¬¿à¬• ପାଇଠସà­à¬•à¬¾à¬¨ କରାଯାଉଛି</translation>
<translation id="3386292677130313581">ସାଇଟà­â€à¬—à­à­œà¬¿à¬• ଆପଣଙà­à¬•à¬° ଲୋକେସନà­â€Œ ଜାଣିବା ପୂରà­à¬¬à¬°à­ ପଚାରନà­à¬¤à­ (ସà­à¬ªà¬¾à¬°à¬¿à¬¶à­â€Œ କରାଯାଇଛି)</translation>
<translation id="3538390592868664640">ଆପଣଙà­à¬• ପରିପାରà­à¬¶à­à­±à¬° à¬à¬• 3D ମà­à­Ÿà¬¾à¬ªà­ ତିଆରି କରିବା à¬à¬¬à¬‚ କà­à­Ÿà¬¾à¬®à­‡à¬°à¬¾ ସà­à¬¥à¬¿à¬¤à¬¿ ଟà­à¬°à¬¾à¬•à­ କରିବାକୠସାଇଟଗà­à¬¡à¬¼à¬¿à¬•à­ ବà­à¬²à¬•à­ କରନà­à¬¤à­</translation>
<translation id="3551268116566418498">ଇନକଗà­à¬¨à¬¿à¬Ÿà­‹ ମୋଡରୠବାହାରି ଯିବେ କି?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ଡାଉନà­â€â€â€à¬²à­‹à¬¡à­ ଶେଷ ହୋଇଛି</translation>
<translation id="3987993985790029246">ଲିଙà­à¬•à­ କପି କରନà­à¬¤à­</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">à¬à¬¹à¬¿ ପୃଷà­à¬ à¬¾ ବିଷୟରେ</translation>
<translation id="4002066346123236978">ଆଖà­à­Ÿà¬¾</translation>
<translation id="4008040567710660924">à¬à¬• ନିରà­à¬¦à­à¬¦à¬¿à¬·à­à¬Ÿ ସାଇଟୠପାଇଠକà­à¬•à­€à¬—à­à­œà¬¿à¬•à­ ଅନà­à¬®à¬¤à¬¿ ଦିà¬à¥¤</translation>
<translation id="4046123991198612571">ପରବରà­à¬¤à­à¬¤à­€ ଟà­à¬°à¬¾à¬•à­</translation>
<translation id="4149994727733219643">ୱେବୠପୃଷà­à¬ à¬¾à¬—à­à­œà¬¿à¬• ପାଇଠସରଳୀକୃତ ଭà­à­Ÿà­</translation>
<translation id="4165986682804962316">ସାଇଟà­â€ ସେଟିଂସà­â€Œ</translation>
+<translation id="4169549551965910670">à¬à¬• USB ଡିଭାଇସ ସହ କନେକà­à¬Ÿ କରାଯାଇଛି</translation>
<translation id="4194328954146351878">NFC ଡିଭାଇସଗà­à¬¡à¬¼à¬¿à¬•à¬°à­‡ ସୂଚନା ଦେଖିବା à¬à¬¬à¬‚ ପରିବରà­à¬¤à­à¬¤à¬¨ କରିବା ପାଇଠସାଇଟଗà­à¬¡à¬¼à¬¿à¬•à­ ଅନà­à¬®à¬¤à¬¿ ଦେବା ପୂରà­à¬¬à¬°à­ ପଚାରନà­à¬¤à­ (ସà­à¬ªà¬¾à¬°à¬¿à¬¶ କରାଯାଇଛି)</translation>
<translation id="4200726100658658164">ଲୋକେସନୠସେଟିଂସୠଖୋଲନà­à¬¤à­</translation>
<translation id="4226663524361240545">ବିଜà­à¬žà¬ªà­à¬¤à¬¿ ଡିଭାଇସà­â€à¬•à­ ଭାଇବà­à¬°à­‡à¬Ÿà­ କରିପାରେ</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ସରà­à¬¬à¬¦à¬¾</translation>
<translation id="757524316907819857">ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ଥିବା ବିଷୟବସà­à¬¤à­ ଚଲାଇବାରୠସାଇଟà­â€Œà¬—à­à¬¡à¬¼à¬¿à¬•à­ ବà­à¬²à¬•à­ କରନà­à¬¤à­</translation>
<translation id="7577900504646297215">ରà­à¬šà¬¿à¬—à­à¬¡à¬¼à¬¿à¬•à­ ପରିଚାଳନା କରନà­à¬¤à­</translation>
+<translation id="7594634374516752650">à¬à¬• ବà­à¬²à­à¬Ÿà­à¬¥ ଡିଭାଇସ ସହ କନେକà­à¬Ÿ କରାଯାଇଛି</translation>
<translation id="7649070708921625228">ସହାୟତା</translation>
<translation id="7658239707568436148">ବାତିଲà­</translation>
<translation id="7781829728241885113">ଗତକଲି</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pa.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pa.xtb
index b0577736073..ea9c09bd4f1 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pa.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pa.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">ਜਾਣਕਾਰੀ ਲà©à¨•à¨¾à¨“</translation>
<translation id="3328801116991980348">ਸਾਈਟ ਜਾਣਕਾਰੀ</translation>
<translation id="3333961966071413176">ਸਭ ਸੰਪਰਕ</translation>
+<translation id="3362437373201486687">ਬਲੂਟà©à©±à¨¥ ਡੀਵਾਈਸਾਂ ਲਈ ਸਕੈਨ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ</translation>
<translation id="3386292677130313581">ਸਾਈਟਾਂ ਨੂੰ ਤà©à¨¹à¨¾à¨¡à¨¾ ਟਿਕਾਣਾ ਜਾਣਨ ਤੋਂ ਪਹਿਲਾਂ ਤà©à¨¹à¨¾à¨¡à©€ ਇਜਾਜ਼ਤ ਲੈਣ ਨੂੰ ਜ਼ਰੂਰੀ ਬਣਾਓ (ਸਿਫ਼ਾਰਸ਼ੀ)</translation>
<translation id="3538390592868664640">ਸਾਈਟਾਂ ਨੂੰ ਤà©à¨¹à¨¾à¨¡à©‡ ਆਲੇ-ਦà©à¨†à¨²à©‡ ਦਾ 3D ਨਕਸ਼ਾ ਬਣਾਉਣ ਜਾਂ ਕੈਮਰਾ ਸਥਿਤੀ ਨੂੰ ਟਰੈਕ ਕਰਨ ਤੋਂ ਬਲਾਕ ਕਰੋ</translation>
<translation id="3551268116566418498">ਕੀ ਇਨਕੋਗਨਿਟੋ ਮੋਡ ਨੂੰ ਛੱਡਣਾ ਹੈ?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ਡਾਊਨਲੋਡ ਪੂਰਾ ਹੋਇਆ</translation>
<translation id="3987993985790029246">ਲਿੰਕ ਕਾਪੀ ਕਰੋ</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">ਇਸ ਪੰਨੇ ਬਾਰੇ</translation>
<translation id="4002066346123236978">ਸਿਰਲੇਖ</translation>
<translation id="4008040567710660924">ਕਿਸੇ ਖਾਸ ਸਾਈਟ ਨੂੰ ਕà©à¨•à©€à©› ਨੂੰ ਵਰਤਣ ਦਿਓ।</translation>
<translation id="4046123991198612571">ਅਗਲਾ ਟਰੈਕ</translation>
<translation id="4149994727733219643">ਵੈੱਬ ਪੰਨਿਆਂ ਲਈ ਸਰਲੀਕਿਰਤ ਦà©à¨°à¨¿à¨¶</translation>
<translation id="4165986682804962316">ਸਾਈਟ ਸੈਟਿੰਗਾਂ</translation>
+<translation id="4169549551965910670">USB ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਹੈ</translation>
<translation id="4194328954146351878">ਸਾਈਟਾਂ ਨੂੰ NFC ਡੀਵਾਈਸਾਂ 'ਤੇ ਜਾਣਕਾਰੀ ਦੇਖਣ ਅਤੇ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦੇਣ ਤੋਂ ਪਹਿਲਾਂ ਪà©à©±à¨›à©‹ (ਸਿਫ਼ਾਰਸ਼ੀ)</translation>
<translation id="4200726100658658164">ਟਿਕਾਣਾ ਸੈਟਿੰਗਾਂ ਖੋਲà©à¨¹à©‹</translation>
<translation id="4226663524361240545">ਸੂਚਨਾਵਾਂ ਡੀਵਾਈਸ ਨੂੰ ਥਰਥਰਾਹਟ ਕਰ ਸਕਦੀਆਂ ਹਨ</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ਹਮੇਸ਼ਾਂ</translation>
<translation id="757524316907819857">ਸਾਈਟਾਂ ਨੂੰ ਸà©à¨°à©±à¨–ਿਅਤ ਸਮੱਗਰੀ ਚਲਾਉਣ ਤੋਂ ਬਲਾਕ ਕਰੋ</translation>
<translation id="7577900504646297215">ਦਿਲਚਸਪੀਆਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ</translation>
+<translation id="7594634374516752650">ਬਲੂਟà©à©±à¨¥ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕੀਤੀ ਗਈ</translation>
<translation id="7649070708921625228">ਸਹਾਇਤਾ</translation>
<translation id="7658239707568436148">ਰੱਦ ਕਰੋ</translation>
<translation id="7781829728241885113">ਕੱਲà©à¨¹</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pl.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pl.xtb
index 5c44ac69cfd..3681ce92022 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pl.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pl.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ukryj informacje</translation>
<translation id="3328801116991980348">Informacje o witrynie</translation>
<translation id="3333961966071413176">Wszystkie kontakty</translation>
+<translation id="3362437373201486687">Skanuję w poszukiwaniu urządzeń Bluetooth</translation>
<translation id="3386292677130313581">Pytaj, zanim udostępnisz stronom swoją lokalizację (zalecane)</translation>
<translation id="3538390592868664640">Nie zezwalaj stronom na tworzenie mapy 3D Twojego otoczenia ani na śledzenie pozycji kamery</translation>
<translation id="3551268116566418498">Wyłączyć tryb incognito?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Pobieranie zakończone</translation>
<translation id="3987993985790029246">Skopiuj link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Informacje o tej stronie</translation>
<translation id="4002066346123236978">Tytuł</translation>
<translation id="4008040567710660924">Zezwalaj na pliki cookie z określonej strony internetowej.</translation>
<translation id="4046123991198612571">Następny utwór</translation>
<translation id="4149994727733219643">Uproszczony widok stron internetowych</translation>
<translation id="4165986682804962316">Ustawienia witryn</translation>
+<translation id="4169549551965910670">Połączona z urządzeniem USB</translation>
<translation id="4194328954146351878">Pytaj, zanim zezwolisz stronom na odczytywanie i zmienianie informacji na urządzeniach z funkcją NFC (zalecane)</translation>
<translation id="4200726100658658164">Otwórz ustawienia lokalizacji</translation>
<translation id="4226663524361240545">Powiadomienia będą sygnalizowane wibracjami</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Zawsze</translation>
<translation id="757524316907819857">Blokuj odtwarzanie treści chronionej na stronach</translation>
<translation id="7577900504646297215">ZarzÄ…dzaj zainteresowaniami</translation>
+<translation id="7594634374516752650">Połączono z urządzeniem Bluetooth</translation>
<translation id="7649070708921625228">Pomoc</translation>
<translation id="7658239707568436148">Anuluj</translation>
<translation id="7781829728241885113">Wczoraj</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-BR.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-BR.xtb
index 234c73e4990..df3ea3cd08f 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-BR.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-BR.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ocultar informações</translation>
<translation id="3328801116991980348">Informações do site</translation>
<translation id="3333961966071413176">Todos os contatos</translation>
+<translation id="3362437373201486687">Procurando dispositivos Bluetooth</translation>
<translation id="3386292677130313581">Perguntar antes de permitir que sites saibam seu local (recomendado)</translation>
<translation id="3538390592868664640">Impedir sites de criar um mapa 3D do ambiente a sua volta ou acompanhar a posição da câmera</translation>
<translation id="3551268116566418498">Sair do modo de navegação anônima?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Download concluído</translation>
<translation id="3987993985790029246">Copiar link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Sobre esta página</translation>
<translation id="4002066346123236978">Título</translation>
<translation id="4008040567710660924">Permita cookies de um site específico.</translation>
<translation id="4046123991198612571">Próxima faixa</translation>
<translation id="4149994727733219643">Versão simplificada das páginas da Web</translation>
<translation id="4165986682804962316">Configurações do site</translation>
+<translation id="4169549551965910670">Conectado a um dispositivo USB</translation>
<translation id="4194328954146351878">Perguntar antes de permitir que sites vejam e mudem informações em dispositivos NFC (recomendado)</translation>
<translation id="4200726100658658164">Abrir as configurações de localização</translation>
<translation id="4226663524361240545">É possível que as notificações façam o dispositivo vibrar</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Sempre</translation>
<translation id="757524316907819857">Impedir que sites mostrem conteúdo protegido</translation>
<translation id="7577900504646297215">Gerenciar interesses</translation>
+<translation id="7594634374516752650">Conectado a um dispositivo Bluetooth</translation>
<translation id="7649070708921625228">Ajuda</translation>
<translation id="7658239707568436148">Cancelar</translation>
<translation id="7781829728241885113">Ontem</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-PT.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-PT.xtb
index 3474ae1dc84..cfaef4f2d62 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-PT.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-PT.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ocultar informações</translation>
<translation id="3328801116991980348">Informações do site</translation>
<translation id="3333961966071413176">Todos os contactos</translation>
+<translation id="3362437373201486687">A procurar dispositivos Bluetooth</translation>
<translation id="3386292677130313581">Perguntar antes de permitir que os sites conheçam a sua localização (recomendado)</translation>
<translation id="3538390592868664640">Impeça que os sites criem um mapa 3D do ambiente à sua volta ou monitorizem a posição da câmara</translation>
<translation id="3551268116566418498">Quer sair da Navegação anónima?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Transferência concluída</translation>
<translation id="3987993985790029246">Cop. link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Acerca desta página</translation>
<translation id="4002066346123236978">Título</translation>
<translation id="4008040567710660924">Permita cookies para um site específico.</translation>
<translation id="4046123991198612571">Faixa seguinte</translation>
<translation id="4149994727733219643">Vista simplificada de páginas Web</translation>
<translation id="4165986682804962316">Definições de sites</translation>
+<translation id="4169549551965910670">Ligado a um dispositivo USB</translation>
<translation id="4194328954146351878">Perguntar antes de permitir que os sites vejam e alterem informações em dispositivos NFC (recomendado)</translation>
<translation id="4200726100658658164">Abrir definições de localização</translation>
<translation id="4226663524361240545">As notificações podem fazer com que o dispositivo vibre</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Sempre</translation>
<translation id="757524316907819857">Impedir que os sites reproduzam conteúdos protegidos</translation>
<translation id="7577900504646297215">Gerir interesses</translation>
+<translation id="7594634374516752650">Ligado a um dispositivo Bluetooth</translation>
<translation id="7649070708921625228">Ajuda</translation>
<translation id="7658239707568436148">Cancelar</translation>
<translation id="7781829728241885113">Ontem</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ro.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ro.xtb
index 903fe3dd8b4..d5dd8bd93ab 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ro.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ro.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ascunde informațiile</translation>
<translation id="3328801116991980348">Informații despre site</translation>
<translation id="3333961966071413176">Toată agenda</translation>
+<translation id="3362437373201486687">Se caută dispozitive Bluetooth</translation>
<translation id="3386292677130313581">Întreabă înainte de a permite site-urilor să afle locația (recomandat)</translation>
<translation id="3538390592868664640">Împiedică site-urile să creeze o hartă 3D a lucrurilor din jur sau să urmărească poziția camerei video</translation>
<translation id="3551268116566418498">Ieși din modul incognito?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Descărcare finalizată</translation>
<translation id="3987993985790029246">Copiază linkul</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Despre această pagină</translation>
<translation id="4002066346123236978">Titlu</translation>
<translation id="4008040567710660924">Permite cookie-uri pentru un anumit site.</translation>
<translation id="4046123991198612571">Melodia următoare</translation>
<translation id="4149994727733219643">Afișare simplificată pentru paginile web</translation>
<translation id="4165986682804962316">Setări pentru site-uri</translation>
+<translation id="4169549551965910670">Conectat la un dispozitiv USB</translation>
<translation id="4194328954146351878">Întreabă înainte de a permite site-urilor să vadă și să modifice informații de pe dispozitive NFC (recomandat)</translation>
<translation id="4200726100658658164">Deschide setările privind locația</translation>
<translation id="4226663524361240545">Notificările pot face dispozitivul să vibreze</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ÃŽntotdeauna</translation>
<translation id="757524316907819857">Împiedică site-urile să redea conținutul protejat</translation>
<translation id="7577900504646297215">Gestionează interesele</translation>
+<translation id="7594634374516752650">Conectat la un dispozitiv Bluetooth</translation>
<translation id="7649070708921625228">Ajutor</translation>
<translation id="7658239707568436148">Anulează</translation>
<translation id="7781829728241885113">Ieri</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ru.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ru.xtb
index 15dd2efdb68..9ba16b2960b 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ru.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ru.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Скрыть информацию</translation>
<translation id="3328801116991980348">Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ Ñайте</translation>
<translation id="3333961966071413176">Ð’Ñе контакты</translation>
+<translation id="3362437373201486687">ПоиÑк уÑтройÑтв Bluetooth</translation>
<translation id="3386292677130313581">Запрашивать разрешение на доÑтуп к данным о меÑтоположении (рекомендуетÑÑ)</translation>
<translation id="3538390592868664640">Запретить Ñайтам Ñоздавать 3D-карту меÑта, в котором вы находитеÑÑŒ, и отÑлеживать положение камеры</translation>
<translation id="3551268116566418498">Выйти из режима инкогнито?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Скачивание завершено.</translation>
<translation id="3987993985790029246">Копировать ÑÑылку</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> из ?</translation>
+<translation id="3992684624889376114">Об Ñтой Ñтранице</translation>
<translation id="4002066346123236978">Ðазвание</translation>
<translation id="4008040567710660924">Разрешить определенному Ñайту ÑохранÑÑ‚ÑŒ файлы cookie</translation>
<translation id="4046123991198612571">Следующий трек</translation>
<translation id="4149994727733219643">Упрощенный проÑмотр веб-Ñтраниц</translation>
<translation id="4165986682804962316">ÐаÑтройки Ñайтов</translation>
+<translation id="4169549551965910670">Выполнено подключение к USB-уÑтройÑтву</translation>
<translation id="4194328954146351878">Запрашивать Ð´Ð»Ñ Ñайтов разрешение на доÑтуп к информации и ее изменение через NFC (рекомендуетÑÑ)</translation>
<translation id="4200726100658658164">Открыть наÑтройки геолокации</translation>
<translation id="4226663524361240545">Ð’Ð¸Ð±Ñ€Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¸ получении уведомлений</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Ð’Ñегда</translation>
<translation id="757524316907819857">Запретить Ñайтам воÑпроизводить защищенный контент</translation>
<translation id="7577900504646297215">Управление интереÑами</translation>
+<translation id="7594634374516752650">Подключено уÑтройÑтво Bluetooth</translation>
<translation id="7649070708921625228">Справка</translation>
<translation id="7658239707568436148">Отмена</translation>
<translation id="7781829728241885113">Вчера</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_si.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_si.xtb
index 23ada100a54..97a184797e7 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_si.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_si.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">තතු සඟවන්න</translation>
<translation id="3328801116991980348">අඩවි තොරතුරු</translation>
<translation id="3333961966071413176">සියලු සම්බන්ධතà·</translation>
+<translation id="3362437373201486687">බ්ලූටූත් උපà·à¶‚ග සඳහ෠ස්කෑන් කරමින්</translation>
<translation id="3386292677130313581">අඩවි වලට ඔබගේ ස්ථà·à¶±à¶º දà·à¶± ගà·à¶±à·“මට ඉඩ දීමට පෙර විමසන්න (නිර්දේà·à·’තයි)</translation>
<translation id="3538390592868664640">වෙබ් අඩවිය ඔබේ වටපිටà·à·€à·š ත්â€à¶»à·’මà·à¶± සිතියමක් සෑදීමෙන් හ෠කà·à¶¸à¶»à· ස්ථà·à¶±à¶º හඹ෠යෑමෙන් අවහිර කරන්න</translation>
<translation id="3551268116566418498">අප්â€à¶»à·ƒà·’ද්ධ ප්â€à¶»à¶šà·à¶». ඉවත් වන්නද?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">බà·à¶œà·à¶±à·“ම සම්පූර්ණයි</translation>
<translation id="3987993985790029246">සබà·à¶³à·’ය පිටපත් කරන්න</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">මෙම පිටුව පිළිබඳ</translation>
<translation id="4002066346123236978">සිරස්තලය</translation>
<translation id="4008040567710660924">නිà·à·Šà¶ à·’ත අඩවියක් සඳහ෠කුකීවලට ඉඩ දෙන්න.</translation>
<translation id="4046123991198612571">ඊළඟ ඛණ්ඩය</translation>
<translation id="4149994727733219643">වෙබ් පිටු සඳහ෠සරල දසුනක්</translation>
<translation id="4165986682804962316">අඩවි à·ƒà·à¶šà·ƒà·”ම්</translation>
+<translation id="4169549551965910670">USB උපà·à¶‚ගයකට සම්බන්ධ කර ඇත</translation>
<translation id="4194328954146351878">NFC උපà·à¶‚ගවල තොරතුරු බà·à¶½à·“මට සහ වෙනස් කිරීමට අඩවිවලට ඉඩ දීමට පෙර අසන්න (නිර්දේà·à·’තයි)</translation>
<translation id="4200726100658658164">ස්ථà·à¶± à·ƒà·à¶šà·ƒà·“ම් විවෘත කරන්න</translation>
<translation id="4226663524361240545">දà·à¶±à·”ම්දීම් උපà·à¶‚ගය කම්පනය කළ à·„à·à¶š</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">à·ƒà·à¶¸à·€à·’ටම</translation>
<translation id="757524316907819857">ආරක්ෂිත අන්තර්ගත à·€à·à¶¯à¶±à¶º කරමින් තිබෙන අඩවි තහනම් කිරීම</translation>
<translation id="7577900504646297215">ලà·à¶¯à·’කම් කළමන෠කරන්න</translation>
+<translation id="7594634374516752650">බ්ලූටූත් උපà·à¶‚ගයකට සම්බන්ධ කර ඇත</translation>
<translation id="7649070708921625228">උදවු</translation>
<translation id="7658239707568436148">අවලංගු කරන්න</translation>
<translation id="7781829728241885113">ඊයේ</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sk.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sk.xtb
index 53d6a433b80..4051dd34cb0 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sk.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sk.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Skryť informácie</translation>
<translation id="3328801116991980348">Informácie o stránkach</translation>
<translation id="3333961966071413176">VÅ¡etky kontakty</translation>
+<translation id="3362437373201486687">Hľadajú sa zariadenia Bluetooth…</translation>
<translation id="3386292677130313581">PýtaÅ¥ sa, Äi chcete povoliÅ¥ webu zisÅ¥ovaÅ¥ vaÅ¡u polohu (odporúÄané)</translation>
<translation id="3538390592868664640">Brániť webom vytvárať priestorovú mapu okolia a sledovať pozíciu kamery</translation>
<translation id="3551268116566418498">Chcete ukonÄiÅ¥ režim inkognito?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">SÅ¥ahovanie dokonÄené</translation>
<translation id="3987993985790029246">Kopírovať odkaz</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Táto stránka</translation>
<translation id="4002066346123236978">Názov</translation>
<translation id="4008040567710660924">Povoliť súbory cookie na konkrétnom webe.</translation>
<translation id="4046123991198612571">Ďalšia skladba</translation>
<translation id="4149994727733219643">Jednoduché zobrazenie webových stránok</translation>
<translation id="4165986682804962316">Nastavenia webu</translation>
+<translation id="4169549551965910670">Pripojené k zariadeniu USB</translation>
<translation id="4194328954146351878">PýtaÅ¥ sa, Äi chcete povoliÅ¥ webom zobrazovaÅ¥ a meniÅ¥ informácie v zariadeniach s rozhraním NFC (odprúÄané)</translation>
<translation id="4200726100658658164">Otvoriť nastavenia polohy</translation>
<translation id="4226663524361240545">Upozornenia môžu pri prijatí na zariadení spustiť vibrovanie</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Vždy</translation>
<translation id="757524316907819857">Brániť webom prehrávať chránený obsah</translation>
<translation id="7577900504646297215">Spravovať záujmy</translation>
+<translation id="7594634374516752650">Pripojené k zariadeniu Bluetooth</translation>
<translation id="7649070708921625228">Pomocník</translation>
<translation id="7658239707568436148">Zrušiť</translation>
<translation id="7781829728241885113">VÄera</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sl.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sl.xtb
index 57e4d3f8c85..ae026958225 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sl.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sl.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Skrij informacije</translation>
<translation id="3328801116991980348">Podatki o mestu</translation>
<translation id="3333961966071413176">Vsi stiki</translation>
+<translation id="3362437373201486687">Iskanje naprav Bluetooth</translation>
<translation id="3386292677130313581">Prikaži poziv, preden se spletnim mestom razkrije vaÅ¡a lokacija (priporoÄeno)</translation>
<translation id="3538390592868664640">PrepreÄevanje, da bi spletna mesta ustvarila 3D-zemljevid vaÅ¡e okolice ali spremljala položaj kamere</translation>
<translation id="3551268116566418498">Izklop anonimnega naÄina?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Prenos konÄan</translation>
<translation id="3987993985790029246">Kopiranje povezave</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">O tej strani</translation>
<translation id="4002066346123236978">Naslov</translation>
<translation id="4008040567710660924">OmogoÄanje piÅ¡kotkov za doloÄeno spletno mesto.</translation>
<translation id="4046123991198612571">Naslednja skladba</translation>
<translation id="4149994727733219643">Poenostavljen pogled za spletne strani</translation>
<translation id="4165986682804962316">Nastavitve spletnega mesta</translation>
+<translation id="4169549551965910670">Povezano z napravo USB</translation>
<translation id="4194328954146351878">VpraÅ¡aj, preden se spletnim mestom dovoli ogled in spreminjanje podatkov v napravah NFC (priporoÄeno)</translation>
<translation id="4200726100658658164">Odpri nastavitve lokacije</translation>
<translation id="4226663524361240545">Ob prejemanju obvestil naprava morda vibrira</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Vedno</translation>
<translation id="757524316907819857">PrepreÄi spletnim mestom predvajanje zaÅ¡Äitene vsebine</translation>
<translation id="7577900504646297215">Upravljanje zanimanj</translation>
+<translation id="7594634374516752650">Povezano z napravo Bluetooth</translation>
<translation id="7649070708921625228">PomoÄ</translation>
<translation id="7658239707568436148">PrekliÄi</translation>
<translation id="7781829728241885113">VÄeraj</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sq.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sq.xtb
index cf6a1bb146b..566fba2149f 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sq.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sq.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Fshih informacionin</translation>
<translation id="3328801116991980348">Informacionet rreth sajtit</translation>
<translation id="3333961966071413176">Të gjitha kontaktet</translation>
+<translation id="3362437373201486687">Po skanon për pajisje me Bluetooth</translation>
<translation id="3386292677130313581">Pyet përpara se sajtet të lejohen të dinë vendndodhjen tënde (rekomandohet)</translation>
<translation id="3538390592868664640">Blloko krijimin nga sajtet të një harte 3D të ambientit tënd rrethues ose gjurmimin prej tyre të pozicionit të kamerës</translation>
<translation id="3551268116566418498">Të dilet nga modaliteti "i fshehtë"?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Shkarkimi përfundoi</translation>
<translation id="3987993985790029246">Kopjo lidhjen</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Rreth kësaj faqeje</translation>
<translation id="4002066346123236978">Titulli</translation>
<translation id="4008040567710660924">Lejo kukit për një sajt specifik.</translation>
<translation id="4046123991198612571">Kënga tjetër</translation>
<translation id="4149994727733219643">Pamje e thjeshtuar për faqet e uebit</translation>
<translation id="4165986682804962316">Cilësimet e sajtit</translation>
+<translation id="4169549551965910670">U lidh me një pajisje USB</translation>
<translation id="4194328954146351878">Pyet përpara se të lejosh sajtet të shikojnë dhe ndryshojnë informacionin në pajisjet NFC (rekomandohet)</translation>
<translation id="4200726100658658164">Hap "Cilësimet e vendndodhjes"</translation>
<translation id="4226663524361240545">Njoftimet mund të bëjnë që pajisja të dridhet</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Gjithmonë</translation>
<translation id="757524316907819857">Blloko sajtet që të mos luajnë përmbajtje të mbrojtura</translation>
<translation id="7577900504646297215">Menaxho interesat</translation>
+<translation id="7594634374516752650">U lidh me një pajisje me Bluetooth</translation>
<translation id="7649070708921625228">Ndihma</translation>
<translation id="7658239707568436148">Anulo</translation>
<translation id="7781829728241885113">Dje</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr-Latn.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr-Latn.xtb
index e77ddc6dcd8..9817f4d2888 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr-Latn.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr-Latn.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Sakrij informacije</translation>
<translation id="3328801116991980348">Informacije o sajtu</translation>
<translation id="3333961966071413176">Svi kontakti</translation>
+<translation id="3362437373201486687">Traže se Bluetooth uređaji</translation>
<translation id="3386292677130313581">Pitaj pre nego Å¡to dozvoliÅ¡ sajtovima da znaju lokaciju (preporuÄeno)</translation>
<translation id="3538390592868664640">SpreÄite sajtove da prave 3D mapu okruženja ili da prate položaj kamere</translation>
<translation id="3551268116566418498">Izlazite iz rež. bez arhiviranja?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Preuzimanje je dovršeno</translation>
<translation id="3987993985790029246">Kopiraj link</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">O ovoj stranici</translation>
<translation id="4002066346123236978">Naslov</translation>
<translation id="4008040567710660924">OmogucÌava kolaÄicÌe za odreÄ‘eni sajt.</translation>
<translation id="4046123991198612571">SledecÌa pesma</translation>
<translation id="4149994727733219643">Pojednostavljen prikaz veb-stranica</translation>
<translation id="4165986682804962316">Podešavanja sajta</translation>
+<translation id="4169549551965910670">Povezano sa USB uređajem</translation>
<translation id="4194328954146351878">Pre nego Å¡to dozvolite sajtovima da vide i menjaju informacije na NFC ureÄ‘ajima prikazuje se upit (preporuÄeno)</translation>
<translation id="4200726100658658164">Otvorite podešavanja lokacije</translation>
<translation id="4226663524361240545">UreÄ‘aj cÌe vibrirati kada primate obaveÅ¡tenja</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Uvek</translation>
<translation id="757524316907819857">Blokiraj da sajtovi puÅ¡taju zaÅ¡ticÌeni sadržaj</translation>
<translation id="7577900504646297215">Upravljajte interesovanjima</translation>
+<translation id="7594634374516752650">Povezan je sa Bluetooth uređajem</translation>
<translation id="7649070708921625228">PomocÌ</translation>
<translation id="7658239707568436148">Otkaži</translation>
<translation id="7781829728241885113">JuÄe</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr.xtb
index fecc28d4489..e37d9412064 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Сакриј информације</translation>
<translation id="3328801116991980348">Информације о Ñајту</translation>
<translation id="3333961966071413176">Сви контакти</translation>
+<translation id="3362437373201486687">Траже Ñе Bluetooth уређаји</translation>
<translation id="3386292677130313581">Питај пре него што дозволиш Ñајтовима да знају локацију (препоручено)</translation>
<translation id="3538390592868664640">Спречите Ñајтове да праве 3D мапу окружења или да прате положај камере</translation>
<translation id="3551268116566418498">Излазите из реж. без архивирања?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Преузимање је довршено</translation>
<translation id="3987993985790029246">Копирај линк</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">О овој Ñтраници</translation>
<translation id="4002066346123236978">ÐаÑлов</translation>
<translation id="4008040567710660924">Омогућава колачиће за одређени Ñајт.</translation>
<translation id="4046123991198612571">Следећа пеÑма</translation>
<translation id="4149994727733219643">ПоједноÑтављен приказ веб-Ñтраница</translation>
<translation id="4165986682804962316">Подешавања Ñајта</translation>
+<translation id="4169549551965910670">Повезано Ñа USB уређајем</translation>
<translation id="4194328954146351878">Пре него што дозволите Ñајтовима да виде и мењају информације на NFC уређајима приказује Ñе упит (препоручено)</translation>
<translation id="4200726100658658164">Отворите подешавања локације</translation>
<translation id="4226663524361240545">Уређај ће вибрирати када примате обавештења</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Увек</translation>
<translation id="757524316907819857">Блокирај да Ñајтови пуштају заштићени Ñадржај</translation>
<translation id="7577900504646297215">Управљајте интереÑовањима</translation>
+<translation id="7594634374516752650">Повезан је Ñа Bluetooth уређајем</translation>
<translation id="7649070708921625228">Помоћ</translation>
<translation id="7658239707568436148">Откажи</translation>
<translation id="7781829728241885113">Јуче</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sv.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sv.xtb
index 076d5b7ab8b..95b77af1379 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sv.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sv.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Dölj info</translation>
<translation id="3328801116991980348">Platsinformation</translation>
<translation id="3333961966071413176">Alla kontakter</translation>
+<translation id="3362437373201486687">Söker efter Bluetooth-enheter</translation>
<translation id="3386292677130313581">Fråga innan webbplatser tillåts att veta var du befinner dig (rekommenderas)</translation>
<translation id="3538390592868664640">Blockera webbplatser från att skapa en 3D-karta över dina omgivningar eller registrera kamerans position</translation>
<translation id="3551268116566418498">Vill du avsluta inkognitoläget?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Nedladdning slutförd</translation>
<translation id="3987993985790029246">Kopiera länk</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Om den här sidan</translation>
<translation id="4002066346123236978">Titel</translation>
<translation id="4008040567710660924">Tillåt cookies för en enskild webbplats.</translation>
<translation id="4046123991198612571">Nästa spår</translation>
<translation id="4149994727733219643">Förenklad vy för webbsidor</translation>
<translation id="4165986682804962316">Webbplatsinställningar</translation>
+<translation id="4169549551965910670">Ansluten till en USB-enhet</translation>
<translation id="4194328954146351878">Fråga innan webbplatser tillåts att visa och ändra information på NFC-enheter (rekommenderas)</translation>
<translation id="4200726100658658164">Öppna platsinställningarna</translation>
<translation id="4226663524361240545">Aviseringar kan göra att enheten vibrerar</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Alltid</translation>
<translation id="757524316907819857">Blockera webbplatser från att spela upp skyddat innehåll</translation>
<translation id="7577900504646297215">Hantera intressen</translation>
+<translation id="7594634374516752650">Ansluten till en Bluetooth-enhet</translation>
<translation id="7649070708921625228">Hjälp</translation>
<translation id="7658239707568436148">Avbryt</translation>
<translation id="7781829728241885113">Igår</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sw.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sw.xtb
index 3b0f8803b5d..013899a725b 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sw.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sw.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ficha Maelezo</translation>
<translation id="3328801116991980348">Maelezo ya tovuti</translation>
<translation id="3333961966071413176">Anwani zote</translation>
+<translation id="3362437373201486687">Inatafuta vifaa vyenye Bluetooth</translation>
<translation id="3386292677130313581">Uliza kabla ya kuruhusu tovuti zijue mahali ulipo (inapendekezwa)</translation>
<translation id="3538390592868664640">Zuia tovuti zisibuni ramani ya 3D ya mazingira yako wala kufuatilia mkao wa kamera</translation>
<translation id="3551268116566418498">Ungependa kufunga hali fiche?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Faili imekamilika kupakuliwa</translation>
<translation id="3987993985790029246">Nakili kiungo</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Kuhusu ukurasa huu</translation>
<translation id="4002066346123236978">Kichwa</translation>
<translation id="4008040567710660924">Ruhusu vidakuzi katika tovuti maalum.</translation>
<translation id="4046123991198612571">Wimbo unaofuata</translation>
<translation id="4149994727733219643">Mwonekano uliorahisishwa kwa ajili ya kurasa za wavuti</translation>
<translation id="4165986682804962316">Mipangilio ya tovuti</translation>
+<translation id="4169549551965910670">Imeunganishwa kwenye kifaa cha USB</translation>
<translation id="4194328954146351878">Uliza kabla ya kuruhusu tovuti zione na zibadilishe maelezo kwenye vifaa vya NFC (inapendekezwa)</translation>
<translation id="4200726100658658164">Fungua Mipangilio ya Mahali</translation>
<translation id="4226663524361240545">Arifa huenda zitatetemesha kifaa</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Kila wakati</translation>
<translation id="757524316907819857">Zuia tovuti zisicheze maudhui yanayolindwa</translation>
<translation id="7577900504646297215">Dhibiti yanayokuvutia</translation>
+<translation id="7594634374516752650">Imeunganishwa kwenye Kifaa chenye Bluetooth</translation>
<translation id="7649070708921625228">Usaidizi</translation>
<translation id="7658239707568436148">Ghairi</translation>
<translation id="7781829728241885113">Jana</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ta.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ta.xtb
index 634ef52276a..eb6ec5d4237 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ta.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ta.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">தகவலை மறை</translation>
<translation id="3328801116991980348">தளம௠கà¯à®±à®¿à®¤à¯à®¤ தகவலà¯</translation>
<translation id="3333961966071413176">எலà¯à®²à®¾à®¤à¯ தொடரà¯à®ªà¯à®•à®³à¯à®®à¯</translation>
+<translation id="3362437373201486687">பà¯à®³à¯‚டூத௠சாதனஙà¯à®•à®³à¯ உளà¯à®³à®©à®µà®¾ என ஸà¯à®•à¯‡à®©à¯ செயà¯à®•à®¿à®±à®¤à¯</translation>
<translation id="3386292677130313581">எனத௠இரà¯à®ªà¯à®ªà®¿à®Ÿà®¤à¯à®¤à¯ˆ அறிய தளஙà¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à¯à®®à¯ à®®à¯à®©à¯ கேள௠(பரிநà¯à®¤à¯à®°à¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®•à®¿à®±à®¤à¯)</translation>
<translation id="3538390592868664640">தளஙà¯à®•à®³à¯, எனà¯à®©à¯ˆà®šà¯ சà¯à®±à¯à®±à®¿à®¯à¯à®³à¯à®³ இடஙà¯à®•à®³à®¿à®©à¯ 3D மேபà¯à®ªà¯ˆ உரà¯à®µà®¾à®•à¯à®•à¯à®µà®¤à¯ˆà®¯à¯à®®à¯ கேமரா நிலையை டிராக௠செயà¯à®µà®¤à¯ˆà®¯à¯à®®à¯ தடà¯</translation>
<translation id="3551268116566418498">மறைநிலையில௠இரà¯à®¨à¯à®¤à¯ வெளியேறவா?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">பதிவிறகà¯à®•à®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯</translation>
<translation id="3987993985790029246">இணைபà¯à®ªà¯ˆ நகலெடà¯</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">இநà¯à®¤à®ªà¯ பகà¯à®•à®®à¯ - ஓர௠அறிமà¯à®•à®®à¯</translation>
<translation id="4002066346123236978">தலைபà¯à®ªà¯</translation>
<translation id="4008040567710660924">கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®Ÿ தளதà¯à®¤à®¿à®±à¯à®•à¯, கà¯à®•à¯à®•à¯€à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®•à¯à®•à¯à®®à¯.</translation>
<translation id="4046123991198612571">அடà¯à®¤à¯à®¤ டிராகà¯</translation>
<translation id="4149994727733219643">இணையப௠பகà¯à®•à®™à¯à®•à®³à¯à®•à¯à®•à®¾à®© எளிதாகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ காடà¯à®šà®¿</translation>
<translation id="4165986682804962316">தள அமைபà¯à®ªà¯à®•à®³à¯</translation>
+<translation id="4169549551965910670">USB சாதனதà¯à®¤à¯à®Ÿà®©à¯ இணைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯</translation>
<translation id="4194328954146351878">NFC சாதனஙà¯à®•à®³à®¿à®©à¯ தகவலà¯à®•à®³à¯ˆà®¤à¯ தளஙà¯à®•à®³à¯ பாரà¯à®ªà¯à®ªà®¤à®±à¯à®•à¯à®®à¯ மாறà¯à®±à¯à®µà®¤à®±à¯à®•à¯à®®à¯ à®®à¯à®©à¯à®ªà¯ அனà¯à®®à®¤à®¿ கேடà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯ (பரிநà¯à®¤à¯à®°à¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®•à®¿à®±à®¤à¯)</translation>
<translation id="4200726100658658164">இரà¯à®ªà¯à®ªà®¿à®Ÿ அமைபà¯à®ªà¯à®•à®³à¯ˆà®¤à¯ திறகà¯à®•à¯à®®à¯</translation>
<translation id="4226663524361240545">அறிவிபà¯à®ªà¯à®•à®³à¯ வரà¯à®®à¯ போத௠சாதனம௠அதிரà¯à®µà¯à®±à®•à¯à®•à¯‚டà¯à®®à¯</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">எபà¯à®ªà¯‹à®¤à¯à®®à¯</translation>
<translation id="757524316907819857">பாதà¯à®•à®¾à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ உளà¯à®³à®Ÿà®•à¯à®•à®¤à¯à®¤à¯ˆà®¤à¯ தளஙà¯à®•à®³à¯ இயகà¯à®•à¯à®µà®¤à¯ˆà®¤à¯ தடà¯à®•à¯à®•à¯à®®à¯</translation>
<translation id="7577900504646297215">ஆரà¯à®µà®™à¯à®•à®³à¯ˆ நிரà¯à®µà®•à®¿à®•à¯à®•à¯à®®à¯</translation>
+<translation id="7594634374516752650">பà¯à®³à¯‚டூத௠சாதனதà¯à®¤à¯‹à®Ÿà¯ இணைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯</translation>
<translation id="7649070708921625228">உதவி</translation>
<translation id="7658239707568436148">ரதà¯à®¤à¯ செயà¯</translation>
<translation id="7781829728241885113">நேறà¯à®±à¯</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb
index 571ee18c29c..77669d69a7c 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">సమాచారానà±à°¨à°¿ దాచà±</translation>
<translation id="3328801116991980348">సైటౠసమాచారం</translation>
<translation id="3333961966071413176">మొతà±à°¤à°‚ కాంటాకà±à°Ÿà±â€Œà°²à±</translation>
+<translation id="3362437373201486687">à°¬à±à°²à±‚టూతౠపరికరాల కోసం à°¸à±à°•à°¾à°¨à± చేసà±à°¤à±‹à°‚ది</translation>
<translation id="3386292677130313581">మీ à°¸à±à°¥à°¾à°¨à°¾à°¨à±à°¨à°¿ సైటà±â€Œà°²à± తెలà±à°¸à±à°•à±à°¨à±‡à°²à°¾ వాటిని à°…à°¨à±à°®à°¤à°¿à°‚చే à°®à±à°‚à°¦à±, మిమà±à°®à°²à±à°¨à°¿ à°…à°¡à±à°—à±à°¤à±à°‚ది (సిఫారà±à°¸à± చేయబడింది)</translation>
<translation id="3538390592868664640">మీ పరిసరాల 3D à°®à±à°¯à°¾à°ªà±â€Œà°¨à± సృషà±à°Ÿà°¿à°‚à°šà°•à±à°‚à°¡à°¾ లేదా కెమెరా పొజిషనà±â€Œà°¨à± à°Ÿà±à°°à°¾à°•à± చేయకà±à°‚à°¡à°¾ సైటà±â€Œà°²à°¨à± à°¬à±à°²à°¾à°•à± చేయండి</translation>
<translation id="3551268116566418498">à°…à°œà±à°žà°¾à°¤ మోడà±â€Œà°¨à± నిషà±à°•à±à°°à°®à°¿à°‚చాలా?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">డౌనà±â€Œà°²à±‹à°¡à± పూరà±à°¤à°¯à°¿à°‚ది</translation>
<translation id="3987993985790029246">లింకà±â€Œà°¨à± కాపీ చేయి</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">à°ˆ పేజీ à°—à±à°°à°¿à°‚à°šà°¿</translation>
<translation id="4002066346123236978">శీరà±à°·à°¿à°•</translation>
<translation id="4008040567710660924">నిరà±à°¦à°¿à°·à±à°Ÿ సైటౠకోసం à°•à±à°•à±à°•à±€à°²à°¨à± à°…à°¨à±à°®à°¤à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="4046123991198612571">తరà±à°µà°¾à°¤ à°Ÿà±à°°à°¾à°•à±</translation>
<translation id="4149994727733219643">వెబౠపేజీల కోసం సరళమైన వీకà±à°·à°£</translation>
<translation id="4165986682804962316">సైటౠసెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à±</translation>
+<translation id="4169549551965910670">USB పరికరానికి కనెకà±à°Ÿà± చేయబడింది</translation>
<translation id="4194328954146351878">NFC పరికరాలలో సమాచారానà±à°¨à°¿ చూడటానికి, మారà±à°šà°¡à°¾à°¨à°¿à°•à°¿ సైటà±â€Œà°²à°¨à± à°…à°¨à±à°®à°¤à°¿à°‚చే à°®à±à°‚దౠఅడగాలి (సిఫారà±à°¸à± చేయడమైనది)</translation>
<translation id="4200726100658658164">లొకేషనౠసెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± తెరవండి</translation>
<translation id="4226663524361240545">నోటిఫికేషనà±â€Œà°²à± పరికరానà±à°¨à°¿ వైబà±à°°à±‡à°Ÿà± చేయవచà±à°šà±</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">à°Žà°²à±à°²à°ªà±à°ªà±à°¡à±‚</translation>
<translation id="757524316907819857">à°°à°•à±à°·à°¿à°¤ కంటెంటà±â€Œà°¨à± à°ªà±à°²à±‡ చేయకà±à°‚à°¡à°¾ సైటà±â€Œà°²à°¨à± à°¬à±à°²à°¾à°•à± చేసà±à°¤à±à°‚ది</translation>
<translation id="7577900504646297215">ఆసకà±à°¤à±à°²à°¨à± మేనేజౠచేయండి</translation>
+<translation id="7594634374516752650">à°¬à±à°²à±‚టూతౠపరికరానికి కనెకà±à°Ÿà± అయింది</translation>
<translation id="7649070708921625228">సహాయం</translation>
<translation id="7658239707568436148">à°°à°¦à±à°¦à± చేయండి</translation>
<translation id="7781829728241885113">నినà±à°¨</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_th.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_th.xtb
index d867271e130..9abd4fd33cc 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_th.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_th.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">ซ่อนข้อมูล</translation>
<translation id="3328801116991980348">ข้อมูลไซต์</translation>
<translation id="3333961966071413176">รายชื่อติดต่อทั้งหมด</translation>
+<translation id="3362437373201486687">à¸à¸³à¸¥à¸±à¸‡à¸ªà¹à¸à¸™à¸«à¸²à¸­à¸¸à¸›à¸à¸£à¸“์บลูทูธ</translation>
<translation id="3386292677130313581">ถามà¸à¹ˆà¸­à¸™à¸­à¸™à¸¸à¸à¸²à¸•à¹ƒà¸«à¹‰à¹€à¸§à¹‡à¸šà¹„ซต์ทราบตำà¹à¸«à¸™à¹ˆà¸‡à¸‚องคุณ (à¹à¸™à¸°à¸™à¸³)</translation>
<translation id="3538390592868664640">บล็อà¸à¹„ม่ให้เว็บไซต์สร้างà¹à¸œà¸™à¸—ี่ 3 มิติของสิ่งที่อยู่รอบตัวคุณหรือติดตามตำà¹à¸«à¸™à¹ˆà¸‡à¸‚องà¸à¸¥à¹‰à¸­à¸‡</translation>
<translation id="3551268116566418498">ออà¸à¸ˆà¸²à¸à¹‚หมดไม่ระบุตัวตนไหม</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ดาวน์โหลดเสร็จสมบูรณ์</translation>
<translation id="3987993985790029246">คัดลอà¸à¸¥à¸´à¸‡à¸à¹Œ</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸«à¸™à¹‰à¸²à¸™à¸µà¹‰</translation>
<translation id="4002066346123236978">ชื่อ</translation>
<translation id="4008040567710660924">อนุà¸à¸²à¸•à¸„ุà¸à¸à¸µà¹‰à¸‚องเว็บไซต์ที่เจาะจง</translation>
<translation id="4046123991198612571">à¹à¸—ร็à¸à¸–ัดไป</translation>
<translation id="4149994727733219643">มุมมองอย่างง่ายสำหรับหน้าเว็บ</translation>
<translation id="4165986682804962316">à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าเว็บไซต์</translation>
+<translation id="4169549551965910670">เชื่อมต่ออยู่à¸à¸±à¸šà¸­à¸¸à¸›à¸à¸£à¸“์ USB</translation>
<translation id="4194328954146351878">ถามà¸à¹ˆà¸­à¸™à¸—ี่จะอนุà¸à¸²à¸•à¹ƒà¸«à¹‰à¹€à¸§à¹‡à¸šà¹„ซต์ดูà¹à¸¥à¸°à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸‚้อมูลในอุปà¸à¸£à¸“์ NFC (à¹à¸™à¸°à¸™à¸³)</translation>
<translation id="4200726100658658164">เปิดà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าตำà¹à¸«à¸™à¹ˆà¸‡</translation>
<translation id="4226663524361240545">à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸­à¸™à¸­à¸²à¸ˆà¸—ำให้อุปà¸à¸£à¸“์สั่น</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ทุà¸à¸„รั้ง</translation>
<translation id="757524316907819857">บล็อà¸à¹„ม่ให้เว็บไซต์เล่นเนื้อหาที่ได้รับความคุ้มครอง</translation>
<translation id="7577900504646297215">จัดà¸à¸²à¸£à¸„วามสนใจ</translation>
+<translation id="7594634374516752650">เชื่อมต่อà¸à¸±à¸šà¸­à¸¸à¸›à¸à¸£à¸“์บลูทูธà¹à¸¥à¹‰à¸§</translation>
<translation id="7649070708921625228">ความช่วยเหลือ</translation>
<translation id="7658239707568436148">ยà¸à¹€à¸¥à¸´à¸</translation>
<translation id="7781829728241885113">เมื่อวานนี้</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_tr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_tr.xtb
index a4b6bff4c45..305c8b41dc4 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_tr.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_tr.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Bilgileri Gizle</translation>
<translation id="3328801116991980348">Site bilgileri</translation>
<translation id="3333961966071413176">Tüm kişiler</translation>
+<translation id="3362437373201486687">Bluetooth cihazları taranıyor</translation>
<translation id="3386292677130313581">Sitelerin, konumunuzu öğrenmesine izin verilmeden önce size sorulsun (önerilir)</translation>
<translation id="3538390592868664640">Sitelerin çevremin 3D haritasını oluşturmasını veya kamera konumunu takip etmesini engelle</translation>
<translation id="3551268116566418498">Gizli moddan çıkılsın mı?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">İndirme tamamlandı</translation>
<translation id="3987993985790029246">Bağlantıyı kopyala</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Bu sayfa hakkında</translation>
<translation id="4002066346123236978">Başlık</translation>
<translation id="4008040567710660924">Belirli bir site için çerezlere izin verin.</translation>
<translation id="4046123991198612571">Sonraki parça</translation>
<translation id="4149994727733219643">Web sayfalarının basitleştirilmiş görünümü</translation>
<translation id="4165986682804962316">Site ayarları</translation>
+<translation id="4169549551965910670">USB cihaza bağlandı</translation>
<translation id="4194328954146351878">NFC cihazlarda sitelere bilgileri görme ve değiştirme izni vermeden önce sor (önerilir)</translation>
<translation id="4200726100658658164">Konum Ayarlarını açın</translation>
<translation id="4226663524361240545">Bildirimler cihazı titretebilir</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Her zaman</translation>
<translation id="757524316907819857">Sitelerin korumalı içeriği oynatmasını engellenir</translation>
<translation id="7577900504646297215">İlgi alanlarını yönet</translation>
+<translation id="7594634374516752650">Bir Bluetooth aygıtına bağlı</translation>
<translation id="7649070708921625228">Yardım</translation>
<translation id="7658239707568436148">Ä°ptal</translation>
<translation id="7781829728241885113">Dün</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uk.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uk.xtb
index 602bf901bd4..02243442074 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uk.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uk.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Сховати інформацію</translation>
<translation id="3328801116991980348">Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ñайт</translation>
<translation id="3333961966071413176">УÑÑ– контакти</translation>
+<translation id="3362437373201486687">Пошук приÑтроїв із Bluetooth…</translation>
<translation id="3386292677130313581">Запитувати, перш ніж дозволити Ñайтам визначати ваше міÑÑ†ÐµÐ·Ð½Ð°Ñ…Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ (рекомендуєтьÑÑ)</translation>
<translation id="3538390592868664640">Заборонити Ñайтам Ñтворювати 3D-карту вашого Ð¾Ñ‚Ð¾Ñ‡ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ відÑтежувати Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ ÐºÐ°Ð¼ÐµÑ€Ð¸</translation>
<translation id="3551268116566418498">Вимкнути анонімний режим?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Завантажено</translation>
<translation id="3987993985790029246">Копіювати</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Про цю Ñторінку</translation>
<translation id="4002066346123236978">Ðазва</translation>
<translation id="4008040567710660924">Дозволити файли cookie Ð´Ð»Ñ ÐºÐ¾Ð½ÐºÑ€ÐµÑ‚Ð½Ð¾Ð³Ð¾ Ñайту.</translation>
<translation id="4046123991198612571">ÐаÑтупна композиціÑ</translation>
<translation id="4149994727733219643">Спрощений переглÑд веб-Ñторінок</translation>
<translation id="4165986682804962316">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñайтів</translation>
+<translation id="4169549551965910670">Підключено до приÑтрою USB</translation>
<translation id="4194328954146351878">Запитувати, перш ніж дозволÑти Ñайтам переглÑдати й змінювати інформацію на приÑтроÑÑ… NFC (рекомендовано)</translation>
<translation id="4200726100658658164">Відкрити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу до моїх геоданих</translation>
<translation id="4226663524361240545">Коли надходитимуть ÑповіщеннÑ, приÑтрій може вібрувати</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Завжди</translation>
<translation id="757524316907819857">Заборонити Ñайтам відтворювати захищений вміÑÑ‚</translation>
<translation id="7577900504646297215">Керувати інтереÑами</translation>
+<translation id="7594634374516752650">Підключено до приÑтрою з Bluetooth</translation>
<translation id="7649070708921625228">Довідка</translation>
<translation id="7658239707568436148">СкаÑувати</translation>
<translation id="7781829728241885113">Учора</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ur.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ur.xtb
index ae08270b335..96b6037a5cb 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ur.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ur.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">معلومات چھپائیں</translation>
<translation id="3328801116991980348">سائٹ کی معلومات</translation>
<translation id="3333961966071413176">تمام رابطے</translation>
+<translation id="3362437373201486687">بلوٹوتھ آلات Ú©Û’ لیے اسکین Ú©ÛŒ جا رÛÛŒ Ûیں</translation>
<translation id="3386292677130313581">سائٹس Ú©Ùˆ آپ کا مقام جاننے Ú©ÛŒ اجازت دینے سے Ù¾ÛÙ„Û’ پوچھیں (تجویز کردÛ)</translation>
<translation id="3538390592868664640">â€Ø³Ø§Ø¦Ù¹Ø³ Ú©Ùˆ اپنے اطرا٠کا 3D Ù†Ù‚Ø´Û ØªØ®Ù„ÛŒÙ‚ یا کیمرے Ú©ÛŒ پوزیشن ٹریک کرنے سے مسدود کریں</translation>
<translation id="3551268116566418498">پوشیدگی وضع ترک کریں؟</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">ڈاؤن لوڈ مکمل Ûوگیا ÛÛ’</translation>
<translation id="3987993985790029246">لنک کاپی کریں</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">اس صÙØ­Û Ú©Û’ بارے میں</translation>
<translation id="4002066346123236978">عنوان</translation>
<translation id="4008040567710660924">مخصوص سائٹ کے لیے کوکیز کی اجازت دیں۔</translation>
<translation id="4046123991198612571">اگلا ٹریک</translation>
<translation id="4149994727733219643">ویب صÙحات کیلئے Ø³Ø§Ø¯Û Ù…Ù†Ø¸Ø±</translation>
<translation id="4165986682804962316">سائٹ کی ترتیبات</translation>
+<translation id="4169549551965910670">â€USB Ø¢Ù„Û Ø³Û’ منسلک ÛÛ’</translation>
<translation id="4194328954146351878">â€Ø³Ø§Ø¦Ù¹Ø³ Ú©Ùˆ NFC آلات پر معلومات دیکھنے اور تبدیل کرنے Ú©ÛŒ اجازت دینے سے Ù¾ÛÙ„Û’ پوچھیں (تجویز کردÛ)</translation>
<translation id="4200726100658658164">مقام کی ترتیبات کھولیں</translation>
<translation id="4226663524361240545">اطلاعات Ø¢Ù„Û Ú©Ùˆ وائبریٹ کر سکتی Ûیں</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">ÛمیشÛ</translation>
<translation id="757524316907819857">محÙوظ مواد چلانے سے سائٹس Ú©Ùˆ مسدود کریں</translation>
<translation id="7577900504646297215">دلچسپیوں کا نظم کریں</translation>
+<translation id="7594634374516752650">بلوٹوتھ آلے سے منسلک ÛÛ’</translation>
<translation id="7649070708921625228">مدد</translation>
<translation id="7658239707568436148">منسوخ کریں</translation>
<translation id="7781829728241885113">Ú¯Ø²Ø´ØªÛ Ú©Ù„</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uz.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uz.xtb
index fb263b1eb33..e0f846b54af 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uz.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uz.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ma’lumotlarni yashirish</translation>
<translation id="3328801116991980348">Sayt haqida ma’lumot</translation>
<translation id="3333961966071413176">Barcha kontaktlar</translation>
+<translation id="3362437373201486687">Bluetooth qurilmalar qidirilmoqda</translation>
<translation id="3386292677130313581">Joylashuv ma’lumotini ko‘rishiga ruxsat so‘ralsin (tavsiya etiladi)</translation>
<translation id="3538390592868664640">Saytlarni atrofingizning 3D xaritasini yaratish yoki kamera joylashuvini aniqlashdan bloklash</translation>
<translation id="3551268116566418498">Inkognito rejimi tark etilsinmi?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Yuklab olindi</translation>
<translation id="3987993985790029246">Nusxalash</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Bu sahifa haqida</translation>
<translation id="4002066346123236978">Nomi</translation>
<translation id="4008040567710660924">Muayyan saytlar uchun cookie-fayllarga ruxsat berish</translation>
<translation id="4046123991198612571">Keyingi musiqa</translation>
<translation id="4149994727733219643">Veb sahifalarni ko‘rish uchun oddiy rejim</translation>
<translation id="4165986682804962316">Sayt sozlamalari</translation>
+<translation id="4169549551965910670">USB qurilmaga ulandi</translation>
<translation id="4194328954146351878">Saytlar NFC qurilmalarni koʻrishi va ulardagi axborotni oʻzgartirishidan oldin ruxsat olsin (tavsiya etiladi)</translation>
<translation id="4200726100658658164">Joylashuv sozlamalarini ochish</translation>
<translation id="4226663524361240545">Bildirishnoma kelganida qurilma tebranishi mumkin</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Har doim</translation>
<translation id="757524316907819857">Saytlarga himoyalangan kontent ijrosini taqiqlash</translation>
<translation id="7577900504646297215">Qiziqishlarni sozlash</translation>
+<translation id="7594634374516752650">Bluetooth qurilmaga ulandi</translation>
<translation id="7649070708921625228">Yordam</translation>
<translation id="7658239707568436148">Bekor qilish</translation>
<translation id="7781829728241885113">Kecha</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb
index ef149c888c7..91ac3770f0d 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Ẩn thông tin</translation>
<translation id="3328801116991980348">Thông tin vỠtrang web</translation>
<translation id="3333961966071413176">Tất cả ngÆ°á»i liên hệ</translation>
+<translation id="3362437373201486687">Äang quét tìm thiết bị Bluetooth</translation>
<translation id="3386292677130313581">Há»i trÆ°á»›c khi cho phép các trang web biết vị trí của bạn (được Ä‘á» xuất)</translation>
<translation id="3538390592868664640">Chặn không cho trang web tạo bản đồ 3D vỠcác khu vực xung quanh bạn hoặc theo dõi thông tin vị trí của máy ảnh</translation>
<translation id="3551268116566418498">Thoát khá»i chế Ä‘á»™ Ẩn danh?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Äã tải xuống xong</translation>
<translation id="3987993985790029246">Sao chép Ä‘Æ°á»ng liên kết</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" />/?</translation>
+<translation id="3992684624889376114">Giới thiệu vỠtrang này</translation>
<translation id="4002066346123236978">Tiêu Ä‘á»</translation>
<translation id="4008040567710660924">Cho phép cookie của một trang web cụ thể.</translation>
<translation id="4046123991198612571">Bản nhạc tiếp theo</translation>
<translation id="4149994727733219643">Chế độ xem đơn giản cho trang web</translation>
<translation id="4165986682804962316">Cài đặt trang web</translation>
+<translation id="4169549551965910670">Äã kết nối vá»›i má»™t thiết bị USB</translation>
<translation id="4194328954146351878">Há»i trÆ°á»›c khi cho phép các trang web xem và thay đổi thông tin trên thiết bị NFC (nên chá»n)</translation>
<translation id="4200726100658658164">Mở phần Cài đặt vị trí</translation>
<translation id="4226663524361240545">Thông báo có thể làm rung thiết bị</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Luôn luôn</translation>
<translation id="757524316907819857">Chặn không cho trang web phát nội dung được bảo vệ</translation>
<translation id="7577900504646297215">Quản lý mối quan tâm</translation>
+<translation id="7594634374516752650">Äã kết nối vá»›i má»™t thiết bị Bluetooth</translation>
<translation id="7649070708921625228">Trợ giúp</translation>
<translation id="7658239707568436148">Hủy</translation>
<translation id="7781829728241885113">Hôm qua</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-CN.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-CN.xtb
index a7d3acc78ce..6031a372583 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-CN.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-CN.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">éšè—ä¿¡æ¯</translation>
<translation id="3328801116991980348">网站信æ¯</translation>
<translation id="3333961966071413176">所有è”系人</translation>
+<translation id="3362437373201486687">正在æœå¯»è“牙设备</translation>
<translation id="3386292677130313581">需先询问,得到许å¯åŽæ‰å…许网站获å–您的ä½ç½®ä¿¡æ¯ï¼ˆæŽ¨è)</translation>
<translation id="3538390592868664640">ç¦æ­¢ç½‘站为您的周边环境创建 3D 地图或跟踪摄åƒå¤´ä½ç½®</translation>
<translation id="3551268116566418498">退出无痕模å¼ï¼Ÿ</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">下载完毕</translation>
<translation id="3987993985790029246">å¤åˆ¶é“¾æŽ¥</translation>
<translation id="3991845972263764475">已下载 <ph name="BYTES_DOWNLOADED_WITH_UNITS" />,总大å°ä¸æ˜Ž</translation>
+<translation id="3992684624889376114">关于此页é¢</translation>
<translation id="4002066346123236978">标题</translation>
<translation id="4008040567710660924">å…许特定网站使用 Cookie。</translation>
<translation id="4046123991198612571">下一曲</translation>
<translation id="4149994727733219643">使用简化版视图查看网页</translation>
<translation id="4165986682804962316">网站设置</translation>
+<translation id="4169549551965910670">已连接 USB 设备</translation>
<translation id="4194328954146351878">需先询问,得到许å¯åŽæ‰å…许网站查看和更改 NFC 设备上的信æ¯ï¼ˆæŽ¨è)</translation>
<translation id="4200726100658658164">打开ä½ç½®ä¿¡æ¯è®¾ç½®</translation>
<translation id="4226663524361240545">收到通知时设备会振动</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">永远</translation>
<translation id="757524316907819857">ç¦æ­¢ç½‘站播放å—ä¿æŠ¤çš„内容</translation>
<translation id="7577900504646297215">管ç†æˆ‘的兴趣</translation>
+<translation id="7594634374516752650">已连接到è“牙设备</translation>
<translation id="7649070708921625228">帮助</translation>
<translation id="7658239707568436148">å–消</translation>
<translation id="7781829728241885113">昨天</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb
index aca7bcc0cc7..b4ba20ae84e 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">éš±è—資料</translation>
<translation id="3328801116991980348">網站資料</translation>
<translation id="3333961966071413176">所有è¯çµ¡äºº</translation>
+<translation id="3362437373201486687">正在掃瞄è—牙è£ç½®</translation>
<translation id="3386292677130313581">å…許網站存å–您的ä½ç½®å‰å…ˆè©¢å•æ‚¨ (建議)</translation>
<translation id="3538390592868664640">ç¦æ­¢ç¶²ç«™å»ºç«‹æ‚¨èº«è™•ç’°å¢ƒçš„ 3D 地圖或追蹤æ”錄機ä½ç½®</translation>
<translation id="3551268116566418498">è¦é€€å‡ºç„¡ç—•æ¨¡å¼å—Žï¼Ÿ</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">下載完æˆ</translation>
<translation id="3987993985790029246">複製連çµ</translation>
<translation id="3991845972263764475">已下載:<ph name="BYTES_DOWNLOADED_WITH_UNITS" />,總大å°ï¼šä¸æ˜Ž</translation>
+<translation id="3992684624889376114">關於本é </translation>
<translation id="4002066346123236978">標題</translation>
<translation id="4008040567710660924">å…è¨±ç‰¹å®šç¶²ç«™å­˜å– Cookie。</translation>
<translation id="4046123991198612571">下一首曲目</translation>
<translation id="4149994727733219643">使用簡化檢視模å¼æŸ¥çœ‹ç¶²é </translation>
<translation id="4165986682804962316">網站設定</translation>
+<translation id="4169549551965910670">已連線至 USB è£ç½®</translation>
<translation id="4194328954146351878">在å…許網站查看和變更 NFC è£ç½®ä¸Šçš„資料å‰å…ˆè©¢å•æ‚¨ (建議)</translation>
<translation id="4200726100658658164">é–‹å•Ÿä½ç½®è¨­å®š</translation>
<translation id="4226663524361240545">è£ç½®æœƒåœ¨æ”¶åˆ°é€šçŸ¥æ™‚震動</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">æ°¸é ä½¿ç”¨</translation>
<translation id="757524316907819857">ç¦æ­¢ç¶²ç«™æ’­æ”¾å—ä¿è­·å…§å®¹</translation>
<translation id="7577900504646297215">管ç†èˆˆè¶£</translation>
+<translation id="7594634374516752650">已連線至è—牙è£ç½®</translation>
<translation id="7649070708921625228">說明</translation>
<translation id="7658239707568436148">å–消</translation>
<translation id="7781829728241885113">昨天</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-TW.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-TW.xtb
index bc65d38424c..2b03114d363 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-TW.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-TW.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">éš±è—資訊</translation>
<translation id="3328801116991980348">網站資訊</translation>
<translation id="3333961966071413176">所有è¯çµ¡äºº</translation>
+<translation id="3362437373201486687">正在掃æè—牙è£ç½®</translation>
<translation id="3386292677130313581">å…許網站存å–ä½ çš„ä½ç½®è³‡è¨Šå‰ï¼Œå¿…須先詢å•ä½  (建議)</translation>
<translation id="3538390592868664640">ç¦æ­¢ç¶²ç«™æ ¹æ“šä½ çš„周é­ç’°å¢ƒå»ºç«‹ 3D 地圖或追蹤æ”影機ä½ç½®</translation>
<translation id="3551268116566418498">è¦é€€å‡ºç„¡ç—•æ¨¡å¼å—Žï¼Ÿ</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">下載完æˆ</translation>
<translation id="3987993985790029246">複製連çµ</translation>
<translation id="3991845972263764475">已下載:<ph name="BYTES_DOWNLOADED_WITH_UNITS" />,總大å°ï¼šä¸æ˜Ž</translation>
+<translation id="3992684624889376114">為何顯示此é </translation>
<translation id="4002066346123236978">標題</translation>
<translation id="4008040567710660924">å…許特定網站的 Cookie。</translation>
<translation id="4046123991198612571">下一首曲目</translation>
<translation id="4149994727733219643">使用簡易檢視模å¼æŸ¥çœ‹ç¶²é </translation>
<translation id="4165986682804962316">網站設定</translation>
+<translation id="4169549551965910670">已連接至 USB è£ç½®</translation>
<translation id="4194328954146351878">系統必須先詢å•ä½ ï¼Œæ‰èƒ½å…許網站在 NFC è£ç½®ä¸ŠæŸ¥çœ‹å’Œè®Šæ›´è³‡è¨Š (建議設定)</translation>
<translation id="4200726100658658164">é–‹å•Ÿä½ç½®è³‡è¨Šè¨­å®š</translation>
<translation id="4226663524361240545">收到通知時è£ç½®æœƒéœ‡å‹•</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">一律使用</translation>
<translation id="757524316907819857">ç¦æ­¢ç¶²ç«™æ’­æ”¾å—ä¿è­·çš„內容</translation>
<translation id="7577900504646297215">管ç†èˆˆè¶£å–œå¥½</translation>
+<translation id="7594634374516752650">已連線至è—牙è£ç½®</translation>
<translation id="7649070708921625228">說明</translation>
<translation id="7658239707568436148">å–消</translation>
<translation id="7781829728241885113">昨天</translation>
diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zu.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zu.xtb
index 31b4ca29977..667ad964559 100644
--- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zu.xtb
+++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zu.xtb
@@ -101,6 +101,7 @@
<translation id="3295602654194328831">Fihla ulwazi</translation>
<translation id="3328801116991980348">Ulwazi lwesayithi</translation>
<translation id="3333961966071413176">Bonke oxhumana nabo</translation>
+<translation id="3362437373201486687">Iskena amadivayisi e-Bluetooth</translation>
<translation id="3386292677130313581">Buza ngaphambi kokuvumela amasayithi ukuthi azi indawo yakho (kunconyiwe)</translation>
<translation id="3538390592868664640">Vimbela amasayithi ekudaleni imephu ye-3D yendawo ekuzungezile noma ukulandelela indawo yekhamera</translation>
<translation id="3551268116566418498">Shiya imodi ye-incognito?</translation>
@@ -122,11 +123,13 @@
<translation id="3967822245660637423">Ukulanda kuqedile</translation>
<translation id="3987993985790029246">Kopisha isixhumanisi</translation>
<translation id="3991845972263764475"><ph name="BYTES_DOWNLOADED_WITH_UNITS" /> / ?</translation>
+<translation id="3992684624889376114">Mayelana naleli khasi</translation>
<translation id="4002066346123236978">Isihloko</translation>
<translation id="4008040567710660924">Vumela amakhukhi esayithi elithile.</translation>
<translation id="4046123991198612571">Ithrekhi elandelayo</translation>
<translation id="4149994727733219643">Ukubuka okwenziwe lula kwamakhasi ewebhu</translation>
<translation id="4165986682804962316">Izilungiselelo zesayithi</translation>
+<translation id="4169549551965910670">Ixhumeke kudivayisi ye-USB</translation>
<translation id="4194328954146351878">Buza ngaphambi kokuvumela amasayithi ukuthi abone futhi aguqule ulwazi kumadivayisi e-NFC (kuyanconywa)</translation>
<translation id="4200726100658658164">Vula Izilungiselelo zendawo</translation>
<translation id="4226663524361240545">Izaziso zingadlidliza idivayisi</translation>
@@ -271,6 +274,7 @@
<translation id="7561196759112975576">Njalo</translation>
<translation id="757524316907819857">Vimbela amasayithi ekudlaleni okuqukethwe okuvikelwe</translation>
<translation id="7577900504646297215">Lawula izintshisekelo</translation>
+<translation id="7594634374516752650">Ixhume kudivayisi ye-Bluetooth</translation>
<translation id="7649070708921625228">Usizo</translation>
<translation id="7658239707568436148">Khansela</translation>
<translation id="7781829728241885113">Izolo</translation>
diff --git a/chromium/components/browser_ui/styles/android/BUILD.gn b/chromium/components/browser_ui/styles/android/BUILD.gn
index 4793c034ccb..8fb0928d30a 100644
--- a/chromium/components/browser_ui/styles/android/BUILD.gn
+++ b/chromium/components/browser_ui/styles/android/BUILD.gn
@@ -12,10 +12,9 @@ android_library("java") {
]
deps = [
":java_resources",
- "//base:base_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
]
resources_package = "org.chromium.components.browser_ui.styles"
@@ -23,11 +22,11 @@ android_library("java") {
android_resources("java_resources") {
sources = [
- "java/res/color-night/new_tab_button_pressed_tint.xml",
- "java/res/color-night/new_tab_button_tint.xml",
- "java/res/color-night/switch_thumb_tint.xml",
+ "java/res/color-night/new_tab_button_pressed_tint_list.xml",
+ "java/res/color-night/new_tab_button_tint_list.xml",
+ "java/res/color-night/switch_thumb_tint_list.xml",
"java/res/color-night/switch_track_tint.xml",
- "java/res/color/chip_text_color_secondary.xml",
+ "java/res/color/chip_text_color_secondary_list.xml",
"java/res/color/default_icon_color_accent1_tint_list.xml",
"java/res/color/default_icon_color_dark_tint_list.xml",
"java/res/color/default_icon_color_disabled.xml",
@@ -43,16 +42,16 @@ android_resources("java_resources") {
"java/res/color/default_text_color_list.xml",
"java/res/color/default_text_color_on_accent1_list.xml",
"java/res/color/default_text_color_secondary_list.xml",
- "java/res/color/filled_button_bg_dynamic.xml",
- "java/res/color/icon_animated_faded_color.xml",
- "java/res/color/new_tab_button_pressed_tint.xml",
- "java/res/color/new_tab_button_tint.xml",
- "java/res/color/progress_bar_bg_color.xml",
- "java/res/color/selection_control_button_tint.xml",
- "java/res/color/switch_thumb_tint.xml",
- "java/res/color/switch_thumb_tint_incognito.xml",
+ "java/res/color/filled_button_bg_dynamic_list.xml",
+ "java/res/color/icon_animated_faded_color_list.xml",
+ "java/res/color/new_tab_button_pressed_tint_list.xml",
+ "java/res/color/new_tab_button_tint_list.xml",
+ "java/res/color/progress_bar_bg_color_list.xml",
+ "java/res/color/selection_control_button_tint_list.xml",
+ "java/res/color/switch_thumb_tint_incognito_baseline_list.xml",
+ "java/res/color/switch_thumb_tint_list.xml",
"java/res/color/switch_track_tint.xml",
- "java/res/color/switch_track_tint_incognito.xml",
+ "java/res/color/switch_track_tint_incognito_baseline_list.xml",
"java/res/color/text_highlight_color.xml",
"java/res/drawable-hdpi/btn_star_filled.png",
"java/res/drawable-hdpi/ic_chrome.png",
@@ -213,7 +212,6 @@ android_resources("java_resources") {
"java/res/drawable/ic_warning_red_16dp.xml",
"java/res/drawable/ic_warning_red_24dp.xml",
"java/res/drawable/ic_web_asset_24dp.xml",
- "java/res/drawable/ic_zoom_in.xml",
"java/res/drawable/smartphone_black_24dp.xml",
"java/res/drawable/toolbar_hairline.xml",
"java/res/values-night/colors.xml",
diff --git a/chromium/components/browser_ui/theme/android/BUILD.gn b/chromium/components/browser_ui/theme/android/BUILD.gn
index bbb1f9a4fb4..172a210430c 100644
--- a/chromium/components/browser_ui/theme/android/BUILD.gn
+++ b/chromium/components/browser_ui/theme/android/BUILD.gn
@@ -6,7 +6,9 @@ import("//build/config/android/rules.gni")
android_resources("java_resources") {
sources = [
+ "java/res/values-night/colors.xml",
"java/res/values-night/themes.xml",
+ "java/res/values/colors.xml",
"java/res/values/themes.xml",
]
deps = [
diff --git a/chromium/components/browser_ui/util/android/BUILD.gn b/chromium/components/browser_ui/util/android/BUILD.gn
index 64e3603d396..971e6c9e838 100644
--- a/chromium/components/browser_ui/util/android/BUILD.gn
+++ b/chromium/components/browser_ui/util/android/BUILD.gn
@@ -22,6 +22,7 @@ android_library("java") {
deps = [
":java_resources",
"//base:base_java",
+ "//build/android:build_java",
"//cc:cc_java",
"//components/embedder_support/android:util_java",
"//components/url_formatter/android:url_formatter_java",
diff --git a/chromium/components/browser_ui/webshare/android/BUILD.gn b/chromium/components/browser_ui/webshare/android/BUILD.gn
index 24424f041e6..018c72ad873 100644
--- a/chromium/components/browser_ui/webshare/android/BUILD.gn
+++ b/chromium/components/browser_ui/webshare/android/BUILD.gn
@@ -34,7 +34,6 @@ java_library("junit") {
]
deps = [
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//base/test:test_support_java",
diff --git a/chromium/components/browser_ui/widget/android/BUILD.gn b/chromium/components/browser_ui/widget/android/BUILD.gn
index 8add5764fba..0c28f6f860c 100644
--- a/chromium/components/browser_ui/widget/android/BUILD.gn
+++ b/chromium/components/browser_ui/widget/android/BUILD.gn
@@ -59,6 +59,7 @@ android_library("java") {
"java/src/org/chromium/components/browser_ui/widget/displaystyle/ViewResizer.java",
"java/src/org/chromium/components/browser_ui/widget/dragreorder/DragReorderableListAdapter.java",
"java/src/org/chromium/components/browser_ui/widget/dragreorder/DragStateDelegate.java",
+ "java/src/org/chromium/components/browser_ui/widget/gesture/BackPressHandler.java",
"java/src/org/chromium/components/browser_ui/widget/gesture/SwipeGestureListener.java",
"java/src/org/chromium/components/browser_ui/widget/highlight/PulseDrawable.java",
"java/src/org/chromium/components/browser_ui/widget/highlight/PulseInterpolator.java",
@@ -108,6 +109,7 @@ android_library("java") {
"java/src/org/chromium/components/browser_ui/widget/text/VerticallyFixedEditText.java",
"java/src/org/chromium/components/browser_ui/widget/textbubble/ArrowBubbleDrawable.java",
"java/src/org/chromium/components/browser_ui/widget/textbubble/TextBubble.java",
+ "java/src/org/chromium/components/browser_ui/widget/textbubble/TextBubbleBackPressHandler.java",
"java/src/org/chromium/components/browser_ui/widget/tile/TileView.java",
"java/src/org/chromium/components/browser_ui/widget/tile/TileViewBinder.java",
"java/src/org/chromium/components/browser_ui/widget/tile/TileViewCoordinator.java",
@@ -121,11 +123,16 @@ android_library("java") {
"//components/browser_ui/styles/android:java",
"//components/browser_ui/util/android:java",
"//components/embedder_support/android:util_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java",
+ "//third_party/androidx:androidx_core_core_java",
+ "//third_party/androidx:androidx_interpolator_interpolator_java",
+ "//third_party/androidx:androidx_recyclerview_recyclerview_java",
+ "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
+ "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
"//ui/android:ui_java",
"//url:gurl_java",
]
@@ -257,6 +264,7 @@ android_resources("java_resources") {
"java/res/layout/tile_no_text_view_condensed.xml",
"java/res/layout/tile_view_modern.xml",
"java/res/layout/tile_view_modern_condensed.xml",
+ "java/res/layout/title_and_description_layout.xml",
"java/res/values-ldrtl/values.xml",
"java/res/values-night/colors.xml",
"java/res/values-night/dimens.xml",
@@ -332,7 +340,6 @@ android_library("javatests") {
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/androidx:androidx_annotation_annotation_java",
- "//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_test_runner_java",
@@ -386,7 +393,6 @@ java_library("junit") {
]
deps = [
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//base/test:test_support_java",
diff --git a/chromium/components/browsing_data/content/BUILD.gn b/chromium/components/browsing_data/content/BUILD.gn
index b142b97c4e6..d63232c8a2c 100644
--- a/chromium/components/browsing_data/content/BUILD.gn
+++ b/chromium/components/browsing_data/content/BUILD.gn
@@ -37,6 +37,7 @@ static_library("content") {
"//components/content_settings/core/common",
"//components/no_state_prefetch/browser",
"//components/prefs",
+ "//components/services/storage/privileged/mojom",
"//components/services/storage/public/cpp",
"//components/services/storage/public/mojom",
"//components/site_isolation",
diff --git a/chromium/components/browsing_data/content/DEPS b/chromium/components/browsing_data/content/DEPS
index fd71be87f4a..5b2eae4beac 100644
--- a/chromium/components/browsing_data/content/DEPS
+++ b/chromium/components/browsing_data/content/DEPS
@@ -3,6 +3,7 @@ include_rules = [
"+components/content_settings/core/common",
"+components/prefs",
"+components/no_state_prefetch/browser",
+ "+components/services/storage/privileged",
"+components/services/storage/public",
"+components/site_isolation",
"+content/public/browser",
diff --git a/chromium/components/browsing_data/content/browsing_data_helper.cc b/chromium/components/browsing_data/content/browsing_data_helper.cc
index 8d7c317e8ff..ab1b3e88316 100644
--- a/chromium/components/browsing_data/content/browsing_data_helper.cc
+++ b/chromium/components/browsing_data/content/browsing_data_helper.cc
@@ -178,10 +178,6 @@ void RemoveFederatedSiteSettingsData(
HostContentSettingsMap::PatternSourcePredicate());
host_content_settings_map->ClearSettingsForOneTypeWithPredicate(
- ContentSettingsType::FEDERATED_IDENTITY_REQUEST, delete_begin, delete_end,
- HostContentSettingsMap::PatternSourcePredicate());
-
- host_content_settings_map->ClearSettingsForOneTypeWithPredicate(
ContentSettingsType::FEDERATED_IDENTITY_SHARING, delete_begin, delete_end,
HostContentSettingsMap::PatternSourcePredicate());
}
diff --git a/chromium/components/browsing_data/content/file_system_helper_unittest.cc b/chromium/components/browsing_data/content/file_system_helper_unittest.cc
index 0e950e3794e..1c77e3d9cd5 100644
--- a/chromium/components/browsing_data/content/file_system_helper_unittest.cc
+++ b/chromium/components/browsing_data/content/file_system_helper_unittest.cc
@@ -79,7 +79,7 @@ class FileSystemHelperTest : public testing::Test {
// Callback that should be executed in response to
// storage::FileSystemContext::OpenFileSystem.
void OpenFileSystemCallback(base::RunLoop* run_loop,
- const GURL& root,
+ const storage::FileSystemURL& root,
const std::string& name,
base::File::Error error) {
open_file_system_result_ = error;
@@ -93,7 +93,8 @@ class FileSystemHelperTest : public testing::Test {
browser_context_.GetDefaultStoragePartition()
->GetFileSystemContext()
->OpenFileSystem(
- blink::StorageKey(origin), type, open_mode,
+ blink::StorageKey(origin), /*bucket=*/absl::nullopt, type,
+ open_mode,
base::BindOnce(&FileSystemHelperTest::OpenFileSystemCallback,
base::Unretained(this), &run_loop));
BlockUntilQuit(&run_loop);
diff --git a/chromium/components/browsing_data/content/indexed_db_helper.cc b/chromium/components/browsing_data/content/indexed_db_helper.cc
index c3eaa2766b7..0ad770f44ea 100644
--- a/chromium/components/browsing_data/content/indexed_db_helper.cc
+++ b/chromium/components/browsing_data/content/indexed_db_helper.cc
@@ -13,7 +13,7 @@
#include "base/memory/scoped_refptr.h"
#include "base/time/time.h"
#include "components/browsing_data/content/browsing_data_helper.h"
-#include "components/services/storage/public/mojom/indexed_db_control.mojom.h"
+#include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
@@ -45,7 +45,7 @@ void IndexedDBHelper::StartFetching(FetchCallback callback) {
void IndexedDBHelper::DeleteIndexedDB(const blink::StorageKey& storage_key,
base::OnceCallback<void(bool)> callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- storage_partition_->GetIndexedDBControl().DeleteForStorageKey(
+ storage_partition_->GetIndexedDBControl().DeleteForBucket(
storage_key, std::move(callback));
}
diff --git a/chromium/components/browsing_data/content/indexed_db_helper.h b/chromium/components/browsing_data/content/indexed_db_helper.h
index 18751f04aa0..a98f003e667 100644
--- a/chromium/components/browsing_data/content/indexed_db_helper.h
+++ b/chromium/components/browsing_data/content/indexed_db_helper.h
@@ -13,7 +13,7 @@
#include "base/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
-#include "components/services/storage/public/mojom/indexed_db_control.mojom.h"
+#include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h"
#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
namespace blink {
diff --git a/chromium/components/browsing_topics/BUILD.gn b/chromium/components/browsing_topics/BUILD.gn
index 8ad3b45e078..98860dd5f52 100644
--- a/chromium/components/browsing_topics/BUILD.gn
+++ b/chromium/components/browsing_topics/BUILD.gn
@@ -24,6 +24,7 @@ source_set("browsing_topics") {
deps = [
"//base",
"//components/browsing_topics/common:common",
+ "//components/browsing_topics/mojom:mojo_bindings",
"//components/history/content/browser",
"//components/history/core/browser",
"//components/keyed_service/core",
@@ -51,6 +52,7 @@ source_set("test_support") {
deps = [
":browsing_topics",
"//base/test:test_support",
+ "//components/browsing_topics/mojom:mojo_bindings",
"//components/history/core/browser:browser",
"//third_party/blink/public/common",
]
diff --git a/chromium/components/browsing_topics/browsing_topics_calculator.cc b/chromium/components/browsing_topics/browsing_topics_calculator.cc
index 0add266b3d5..5159de5a670 100644
--- a/chromium/components/browsing_topics/browsing_topics_calculator.cc
+++ b/chromium/components/browsing_topics/browsing_topics_calculator.cc
@@ -64,25 +64,22 @@ void RecordCalculatorResultMetrics(
// Precondition: the annotation didn't fail in general (e.g. `ModelInfo` is
// valid).
void DeriveHostTopicsMapAndTopicHostsMap(
- const std::vector<std::string>& raw_hosts,
const std::vector<optimization_guide::BatchAnnotationResult>& results,
std::map<HashedHost, std::set<Topic>>& host_topics_map,
std::map<Topic, std::set<HashedHost>>& topic_hosts_map) {
DCHECK(host_topics_map.empty());
DCHECK(topic_hosts_map.empty());
- DCHECK_EQ(raw_hosts.size(), results.size());
-
for (size_t i = 0; i < results.size(); ++i) {
const optimization_guide::BatchAnnotationResult& result = results[i];
- const std::string raw_host = raw_hosts[i];
+ const std::string& original_host = result.input();
const absl::optional<std::vector<optimization_guide::WeightedIdentifier>>&
annotation_result_topics = result.topics();
if (!annotation_result_topics)
continue;
- HashedHost host = HashMainFrameHostForStorage(raw_host);
+ HashedHost host = HashMainFrameHostForStorage(original_host);
for (const optimization_guide::WeightedIdentifier& annotation_result_topic :
*annotation_result_topics) {
@@ -339,18 +336,17 @@ void BrowsingTopicsCalculator::OnRequestModelCompleted(
// Ignore `successful`. In `OnGetTopicsForHostsCompleted()`, it will need to
// check the model again anyway in case there's a race.
if (raw_hosts.empty()) {
- OnGetTopicsForHostsCompleted(/*raw_hosts=*/{}, /*results=*/{});
+ OnGetTopicsForHostsCompleted(/*results=*/{});
return;
}
- annotations_service_->BatchAnnotatePageTopics(
+ annotations_service_->BatchAnnotate(
base::BindOnce(&BrowsingTopicsCalculator::OnGetTopicsForHostsCompleted,
- weak_ptr_factory_.GetWeakPtr(), raw_hosts),
- raw_hosts);
+ weak_ptr_factory_.GetWeakPtr()),
+ raw_hosts, optimization_guide::AnnotationType::kPageTopics);
}
void BrowsingTopicsCalculator::OnGetTopicsForHostsCompleted(
- std::vector<std::string> raw_hosts,
const std::vector<optimization_guide::BatchAnnotationResult>& results) {
absl::optional<optimization_guide::ModelInfo> model_info =
annotations_service_->GetModelInfoForType(
@@ -374,7 +370,7 @@ void BrowsingTopicsCalculator::OnGetTopicsForHostsCompleted(
std::map<HashedHost, std::set<Topic>> host_topics_map;
std::map<Topic, std::set<HashedHost>> topic_hosts_map;
- DeriveHostTopicsMapAndTopicHostsMap(raw_hosts, results, host_topics_map,
+ DeriveHostTopicsMapAndTopicHostsMap(results, host_topics_map,
topic_hosts_map);
std::vector<Topic> top_topics;
diff --git a/chromium/components/browsing_topics/browsing_topics_calculator.h b/chromium/components/browsing_topics/browsing_topics_calculator.h
index 5a3b85f0d0f..98b62668a62 100644
--- a/chromium/components/browsing_topics/browsing_topics_calculator.h
+++ b/chromium/components/browsing_topics/browsing_topics_calculator.h
@@ -9,6 +9,7 @@
#include <set>
#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/time/time.h"
#include "components/browsing_topics/common/common_types.h"
@@ -102,7 +103,6 @@ class BrowsingTopicsCalculator {
bool successful);
void OnGetTopicsForHostsCompleted(
- std::vector<std::string> raw_hosts,
const std::vector<optimization_guide::BatchAnnotationResult>& results);
void OnCalculateCompleted(CalculatorResultStatus status,
@@ -110,10 +110,11 @@ class BrowsingTopicsCalculator {
// Those pointers are safe to hold and use throughout the lifetime of
// `BrowsingTopicsService`, which owns this object.
- privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings_;
- history::HistoryService* history_service_;
- content::BrowsingTopicsSiteDataManager* site_data_manager_;
- optimization_guide::PageContentAnnotationsService* annotations_service_;
+ raw_ptr<privacy_sandbox::PrivacySandboxSettings> privacy_sandbox_settings_;
+ raw_ptr<history::HistoryService> history_service_;
+ raw_ptr<content::BrowsingTopicsSiteDataManager> site_data_manager_;
+ raw_ptr<optimization_guide::PageContentAnnotationsService>
+ annotations_service_;
CalculateCompletedCallback calculate_completed_callback_;
diff --git a/chromium/components/browsing_topics/browsing_topics_calculator_unittest.cc b/chromium/components/browsing_topics/browsing_topics_calculator_unittest.cc
index 7d9e38560e1..8b9b4981b4e 100644
--- a/chromium/components/browsing_topics/browsing_topics_calculator_unittest.cc
+++ b/chromium/components/browsing_topics/browsing_topics_calculator_unittest.cc
@@ -45,13 +45,6 @@ constexpr char kHost4[] = "www.foo4.com";
constexpr char kHost5[] = "www.foo5.com";
constexpr char kHost6[] = "www.foo6.com";
-constexpr char kTokenizedHost1[] = "foo1 com";
-constexpr char kTokenizedHost2[] = "foo2 com";
-constexpr char kTokenizedHost3[] = "foo3 com";
-constexpr char kTokenizedHost4[] = "foo4 com";
-constexpr char kTokenizedHost5[] = "foo5 com";
-constexpr char kTokenizedHost6[] = "foo6 com";
-
} // namespace
class BrowsingTopicsCalculatorTest : public testing::Test {
@@ -323,12 +316,12 @@ TEST_F(BrowsingTopicsCalculatorTest, TopTopicsRankedByFrequency) {
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -351,12 +344,12 @@ TEST_F(BrowsingTopicsCalculatorTest, ModelHasNoTopicsForHost) {
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, {}},
- {kTokenizedHost2, {}},
- {kTokenizedHost3, {}},
- {kTokenizedHost4, {}},
- {kTokenizedHost5, {}},
- {kTokenizedHost6, {}}});
+ {{kHost1, {}},
+ {kHost2, {}},
+ {kHost3, {}},
+ {kHost4, {}},
+ {kHost5, {}},
+ {kHost6, {}}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -381,12 +374,12 @@ TEST_F(BrowsingTopicsCalculatorTest,
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -412,12 +405,12 @@ TEST_F(BrowsingTopicsCalculatorTest,
// affect the top topics ordering.
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2}, 0.9)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2}, 0.9)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -435,12 +428,12 @@ TEST_F(BrowsingTopicsCalculatorTest,
TEST_F(BrowsingTopicsCalculatorTest, AllTopTopicsRandomlyPadded) {
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
EpochTopics result = CalculateTopics();
ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
@@ -462,12 +455,12 @@ TEST_F(BrowsingTopicsCalculatorTest, TopTopicsPartiallyPadded) {
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -508,12 +501,12 @@ TEST_F(BrowsingTopicsCalculatorTest, CalculationResultUkm) {
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -580,12 +573,12 @@ TEST_F(BrowsingTopicsCalculatorTest, TopTopicsAndObservingDomains) {
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -618,12 +611,12 @@ TEST_F(
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -655,12 +648,12 @@ TEST_F(
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -696,12 +689,12 @@ TEST_F(BrowsingTopicsCalculatorTest,
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -737,12 +730,12 @@ TEST_F(BrowsingTopicsCalculatorTest,
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -775,12 +768,12 @@ TEST_F(BrowsingTopicsCalculatorTest, TopicBlocked) {
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -817,12 +810,12 @@ TEST_F(BrowsingTopicsCalculatorTest, PaddedTopicsDoNotDuplicate) {
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 102}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 102}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 102}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 102}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 102}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({102}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 102}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 102}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 102}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 102}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 102}, 0.1)},
+ {kHost6, TopicsAndWeight({102}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
@@ -852,12 +845,12 @@ TEST_F(BrowsingTopicsCalculatorTest, Metrics) {
test_page_content_annotator_.UsePageTopics(
*optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
- {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
- {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
- {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
- {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
- {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+ {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+ {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+ {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+ {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+ {kHost5, TopicsAndWeight({5, 6}, 0.1)},
+ {kHost6, TopicsAndWeight({6}, 0.1)}});
task_environment_.AdvanceClock(base::Seconds(1));
diff --git a/chromium/components/browsing_topics/browsing_topics_page_load_data_tracker_unittest.cc b/chromium/components/browsing_topics/browsing_topics_page_load_data_tracker_unittest.cc
index 397752c15e3..664bad4154d 100644
--- a/chromium/components/browsing_topics/browsing_topics_page_load_data_tracker_unittest.cc
+++ b/chromium/components/browsing_topics/browsing_topics_page_load_data_tracker_unittest.cc
@@ -111,12 +111,12 @@ class BrowsingTopicsPageLoadDataTrackerTest
BrowsingTopicsPageLoadDataTracker* GetBrowsingTopicsPageLoadDataTracker() {
return BrowsingTopicsPageLoadDataTracker::GetOrCreateForPage(
- web_contents()->GetMainFrame()->GetPage());
+ web_contents()->GetPrimaryMainFrame()->GetPage());
}
content::BrowsingTopicsSiteDataManager* topics_site_data_manager() {
return web_contents()
- ->GetMainFrame()
+ ->GetPrimaryMainFrame()
->GetProcess()
->GetStoragePartition()
->GetBrowsingTopicsSiteDataManager();
diff --git a/chromium/components/browsing_topics/browsing_topics_service.h b/chromium/components/browsing_topics/browsing_topics_service.h
index d9a4f596578..7808d59258f 100644
--- a/chromium/components/browsing_topics/browsing_topics_service.h
+++ b/chromium/components/browsing_topics/browsing_topics_service.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_BROWSING_TOPICS_BROWSING_TOPICS_SERVICE_H_
#define COMPONENTS_BROWSING_TOPICS_BROWSING_TOPICS_SERVICE_H_
+#include "components/browsing_topics/mojom/browsing_topics_internals.mojom-forward.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/privacy_sandbox/canonical_topic.h"
#include "content/public/browser/render_frame_host.h"
@@ -25,6 +26,10 @@ class BrowsingTopicsService : public KeyedService {
const url::Origin& context_origin,
content::RenderFrameHost* main_frame) = 0;
+ // Get the topics state to show in the chrome://topics-internals page.
+ virtual mojom::WebUIGetBrowsingTopicsStateResultPtr
+ GetBrowsingTopicsStateForWebUi() const = 0;
+
// Return the topics (i.e. one topic from each epoch) that can be potentially
// exposed to a given site. Up to `kBrowsingTopicsNumberOfEpochsToExpose`
// epochs' topics can be returned. Padded top topics or random topics won't be
diff --git a/chromium/components/browsing_topics/browsing_topics_service_impl.cc b/chromium/components/browsing_topics/browsing_topics_service_impl.cc
index 6172c58ef9b..19abd50f9b9 100644
--- a/chromium/components/browsing_topics/browsing_topics_service_impl.cc
+++ b/chromium/components/browsing_topics/browsing_topics_service_impl.cc
@@ -10,6 +10,7 @@
#include "base/time/time.h"
#include "components/browsing_topics/browsing_topics_calculator.h"
#include "components/browsing_topics/browsing_topics_page_load_data_tracker.h"
+#include "components/browsing_topics/mojom/browsing_topics_internals.mojom.h"
#include "components/browsing_topics/util.h"
#include "components/optimization_guide/content/browser/page_content_annotations_service.h"
#include "content/public/browser/browsing_topics_site_data_manager.h"
@@ -289,7 +290,7 @@ BrowsingTopicsServiceImpl::GetBrowsingTopicsForJsApi(
continue;
}
- blink::mojom::EpochTopicPtr result_topic = blink::mojom::EpochTopic::New();
+ auto result_topic = blink::mojom::EpochTopic::New();
result_topic->topic = topic.value().value();
result_topic->config_version = base::StrCat(
{"chrome.", base::NumberToString(
@@ -354,6 +355,77 @@ BrowsingTopicsServiceImpl::GetBrowsingTopicsForJsApi(
return result_topics;
}
+mojom::WebUIGetBrowsingTopicsStateResultPtr
+BrowsingTopicsServiceImpl::GetBrowsingTopicsStateForWebUi() const {
+ if (!browsing_topics_state_loaded_) {
+ return mojom::WebUIGetBrowsingTopicsStateResult::NewOverrideStatusMessage(
+ "State loading hasn't finished. Please retry shortly.");
+ }
+
+ if (browsing_topics_state_.next_scheduled_calculation_time().is_null()) {
+ DCHECK(topics_calculator_ && browsing_topics_state_.epochs().empty());
+
+ return mojom::WebUIGetBrowsingTopicsStateResult::NewOverrideStatusMessage(
+ "Initial calculation hasn't finished. Please retry shortly.");
+ }
+
+ auto webui_state = mojom::WebUIBrowsingTopicsState::New();
+
+ webui_state->next_scheduled_calculation_time =
+ browsing_topics_state_.next_scheduled_calculation_time();
+
+ for (const EpochTopics& epoch : browsing_topics_state_.epochs()) {
+ DCHECK_LE(epoch.padded_top_topics_start_index(),
+ epoch.top_topics_and_observing_domains().size());
+
+ // Note: for a failed epoch calculation, the default zero-initialized values
+ // will be displayed in the Web UI.
+ auto webui_epoch = mojom::WebUIEpoch::New();
+ webui_epoch->calculation_time = epoch.calculation_time();
+ webui_epoch->model_version = base::NumberToString(epoch.model_version());
+ webui_epoch->taxonomy_version =
+ base::NumberToString(epoch.taxonomy_version());
+
+ for (size_t i = 0; i < epoch.top_topics_and_observing_domains().size();
+ ++i) {
+ const TopicAndDomains& topic_and_domains =
+ epoch.top_topics_and_observing_domains()[i];
+
+ privacy_sandbox::CanonicalTopic canonical_topic =
+ privacy_sandbox::CanonicalTopic(topic_and_domains.topic(),
+ epoch.taxonomy_version());
+
+ std::vector<std::string> webui_observed_by_domains;
+ webui_observed_by_domains.reserve(
+ topic_and_domains.hashed_domains().size());
+ for (const auto& domain : topic_and_domains.hashed_domains()) {
+ webui_observed_by_domains.push_back(
+ base::NumberToString(domain.value()));
+ }
+
+ // Note: if the topic is invalid (i.e. cleared), the output `topic_id`
+ // will be 0; if the topic is invalid, or if the taxonomy version isn't
+ // recognized by this Chrome binary, the output `topic_name` will be
+ // "Unknown".
+ auto webui_topic = mojom::WebUITopic::New();
+ webui_topic->topic_id = topic_and_domains.topic().value();
+ webui_topic->topic_name = canonical_topic.GetLocalizedRepresentation();
+ webui_topic->is_real_topic = (i < epoch.padded_top_topics_start_index());
+ webui_topic->observed_by_domains = std::move(webui_observed_by_domains);
+
+ webui_epoch->topics.push_back(std::move(webui_topic));
+ }
+
+ webui_state->epochs.push_back(std::move(webui_epoch));
+ }
+
+ // Reorder the epochs from latest to oldest.
+ std::reverse(webui_state->epochs.begin(), webui_state->epochs.end());
+
+ return mojom::WebUIGetBrowsingTopicsStateResult::NewBrowsingTopicsState(
+ std::move(webui_state));
+}
+
std::vector<privacy_sandbox::CanonicalTopic>
BrowsingTopicsServiceImpl::GetTopicsForSiteForDisplay(
const url::Origin& top_origin) const {
diff --git a/chromium/components/browsing_topics/browsing_topics_service_impl.h b/chromium/components/browsing_topics/browsing_topics_service_impl.h
index 5ec9d7423de..380e721539d 100644
--- a/chromium/components/browsing_topics/browsing_topics_service_impl.h
+++ b/chromium/components/browsing_topics/browsing_topics_service_impl.h
@@ -48,6 +48,9 @@ class BrowsingTopicsServiceImpl
const url::Origin& context_origin,
content::RenderFrameHost* main_frame) override;
+ mojom::WebUIGetBrowsingTopicsStateResultPtr GetBrowsingTopicsStateForWebUi()
+ const override;
+
std::vector<privacy_sandbox::CanonicalTopic> GetTopicsForSiteForDisplay(
const url::Origin& top_origin) const override;
diff --git a/chromium/components/browsing_topics/browsing_topics_service_impl_unittest.cc b/chromium/components/browsing_topics/browsing_topics_service_impl_unittest.cc
index 964f0fbe653..b79fd58c42d 100644
--- a/chromium/components/browsing_topics/browsing_topics_service_impl_unittest.cc
+++ b/chromium/components/browsing_topics/browsing_topics_service_impl_unittest.cc
@@ -225,7 +225,7 @@ class BrowsingTopicsServiceImplTest
content::BrowsingTopicsSiteDataManager* topics_site_data_manager() {
return web_contents()
- ->GetMainFrame()
+ ->GetPrimaryMainFrame()
->GetProcess()
->GetStoragePartition()
->GetBrowsingTopicsSiteDataManager();
@@ -561,7 +561,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(
GURL("https://www.bar.com")),
- web_contents()->GetMainFrame())
+ web_contents()->GetPrimaryMainFrame())
.empty());
auto entries = ukm_recorder.GetEntriesByName(
ukm::builders::BrowsingTopics_DocumentBrowsingTopicsApiResult::
@@ -578,6 +578,9 @@ TEST_F(BrowsingTopicsServiceImplTest,
url::Origin::Create(GURL("https://www.bar.com")))
.empty());
EXPECT_TRUE(browsing_topics_service_->GetTopTopicsForDisplay().empty());
+ EXPECT_EQ(browsing_topics_service_->GetBrowsingTopicsStateForWebUi()
+ ->get_override_status_message(),
+ "State loading hasn't finished. Please retry shortly.");
// Finish file loading.
task_environment()->RunUntilIdle();
@@ -586,13 +589,15 @@ TEST_F(BrowsingTopicsServiceImplTest,
->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(
GURL("https://www.bar.com")),
- web_contents()->GetMainFrame())
+ web_contents()->GetPrimaryMainFrame())
.empty());
EXPECT_FALSE(browsing_topics_service_
->GetTopicsForSiteForDisplay(
url::Origin::Create(GURL("https://www.bar.com")))
.empty());
EXPECT_FALSE(browsing_topics_service_->GetTopTopicsForDisplay().empty());
+ EXPECT_FALSE(browsing_topics_service_->GetBrowsingTopicsStateForWebUi()
+ ->is_override_status_message());
}
TEST_F(
@@ -797,7 +802,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(
GURL("https://www.bar.com")),
- web_contents()->GetMainFrame())
+ web_contents()->GetPrimaryMainFrame())
.empty());
auto entries = ukm_recorder.GetEntriesByName(
@@ -833,7 +838,7 @@ TEST_F(BrowsingTopicsServiceImplTest, GetBrowsingTopicsForJsApi_OneEpoch) {
std::vector<blink::mojom::EpochTopicPtr> result =
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_TRUE(result.empty());
@@ -852,7 +857,7 @@ TEST_F(BrowsingTopicsServiceImplTest, GetBrowsingTopicsForJsApi_OneEpoch) {
result = browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(result.size(), 1u);
EXPECT_EQ(result[0]->topic, 2);
@@ -886,7 +891,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
std::vector<blink::mojom::EpochTopicPtr> result =
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_TRUE(result.empty());
@@ -922,7 +927,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
std::vector<blink::mojom::EpochTopicPtr> result =
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_TRUE(result.empty());
@@ -934,7 +939,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
result = browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_TRUE(result.empty());
}
@@ -983,7 +988,7 @@ TEST_F(BrowsingTopicsServiceImplTest, GetBrowsingTopicsForJsApi_FourEpochs) {
std::vector<blink::mojom::EpochTopicPtr> result =
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(result.size(), 3u);
std::set<int> result_set;
@@ -997,7 +1002,7 @@ TEST_F(BrowsingTopicsServiceImplTest, GetBrowsingTopicsForJsApi_FourEpochs) {
result = browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(result.size(), 3u);
result_set.clear();
@@ -1052,7 +1057,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
std::vector<blink::mojom::EpochTopicPtr> result =
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(result.size(), 2u);
std::set<int> result_set;
@@ -1065,7 +1070,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
result = browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(result.size(), 2u);
result_set.clear();
@@ -1128,7 +1133,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
std::vector<blink::mojom::EpochTopicPtr> result =
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(result.size(), 2u);
std::set<int> result_set;
@@ -1165,7 +1170,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
NavigateToPage(GURL("https://www.foo.com"));
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
std::vector<ApiUsageContext> api_usage_contexts =
content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
@@ -1205,7 +1210,7 @@ TEST_F(BrowsingTopicsServiceImplTest, ApiResultUkm_ZeroAndOneTopic) {
// any metrics.
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
entries = ukm_recorder.GetEntriesByName(
ukm::builders::BrowsingTopics_DocumentBrowsingTopicsApiResult::
@@ -1227,7 +1232,7 @@ TEST_F(BrowsingTopicsServiceImplTest, ApiResultUkm_ZeroAndOneTopic) {
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
entries = ukm_recorder.GetEntriesByName(
ukm::builders::BrowsingTopics_DocumentBrowsingTopicsApiResult::
@@ -1310,7 +1315,7 @@ TEST_F(BrowsingTopicsServiceImplTest,
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://www.bar.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
auto entries = ukm_recorder.GetEntriesByName(
ukm::builders::BrowsingTopics_DocumentBrowsingTopicsApiResult::
@@ -1394,7 +1399,7 @@ TEST_F(BrowsingTopicsServiceImplTest, GetTopicsForSiteForDisplay) {
// Current time is before the epoch switch time.
std::vector<privacy_sandbox::CanonicalTopic> result =
browsing_topics_service_->GetTopicsForSiteForDisplay(
- web_contents()->GetMainFrame()->GetLastCommittedOrigin());
+ web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
EXPECT_EQ(result.size(), 2u);
EXPECT_EQ(result[0].topic_id(), Topic(2));
@@ -1465,6 +1470,127 @@ TEST_F(BrowsingTopicsServiceImplTest, GetTopTopicsForDisplay) {
EXPECT_EQ(result[12].topic_id(), Topic(10));
}
+TEST_F(BrowsingTopicsServiceImplTest,
+ GetBrowsingTopicsStateForWebUi_FirstCalculationNotFinished) {
+ base::Time start_time = base::Time::Now();
+
+ base::queue<EpochTopics> mock_calculator_results;
+ mock_calculator_results.push(CreateTestEpochTopics({{Topic(1), {}},
+ {Topic(2), {}},
+ {Topic(3), {}},
+ {Topic(4), {}},
+ {Topic(5), {}}},
+ start_time));
+
+ InitializeBrowsingTopicsService(std::move(mock_calculator_results));
+
+ task_environment()->RunUntilIdle();
+
+ EXPECT_EQ(browsing_topics_service_->GetBrowsingTopicsStateForWebUi()
+ ->get_override_status_message(),
+ "Initial calculation hasn't finished. Please retry shortly.");
+}
+
+TEST_F(BrowsingTopicsServiceImplTest, GetBrowsingTopicsStateForWebUi) {
+ base::Time start_time = base::Time::Now();
+
+ base::queue<EpochTopics> mock_calculator_results;
+ mock_calculator_results.push(
+ CreateTestEpochTopics({{Topic(1), {HashedDomain(123), HashedDomain(456)}},
+ {Topic(2), {}},
+ {Topic(0), {}}, // blocked
+ {Topic(4), {}},
+ {Topic(5), {}}},
+ start_time));
+
+ // Failed calculation.
+ mock_calculator_results.push(EpochTopics());
+
+ mock_calculator_results.push(
+ CreateTestEpochTopics({{Topic(6), {}},
+ {Topic(7), {}},
+ {Topic(8), {}},
+ {Topic(9), {}},
+ {Topic(10), {}}},
+ start_time + base::Days(14),
+ /*padded_top_topics_start_index=*/2));
+
+ InitializeBrowsingTopicsService(std::move(mock_calculator_results));
+
+ // Finish file loading and three calculations.
+ task_environment()->FastForwardBy(3 * kCalculatorDelay + 2 * base::Days(7));
+
+ auto result = browsing_topics_service_->GetBrowsingTopicsStateForWebUi();
+
+ mojom::WebUIBrowsingTopicsStatePtr& webui_state =
+ result->get_browsing_topics_state();
+
+ EXPECT_EQ(webui_state->epochs.size(), 3u);
+ EXPECT_EQ(webui_state->next_scheduled_calculation_time,
+ start_time + 3 * kCalculatorDelay + 3 * base::Days(7));
+
+ const mojom::WebUIEpochPtr& epoch0 = webui_state->epochs[0];
+ const mojom::WebUIEpochPtr& epoch1 = webui_state->epochs[1];
+ const mojom::WebUIEpochPtr& epoch2 = webui_state->epochs[2];
+
+ EXPECT_EQ(epoch0->calculation_time, start_time + base::Days(14));
+ EXPECT_EQ(epoch0->model_version, "5000000000");
+ EXPECT_EQ(epoch0->taxonomy_version, "1");
+ EXPECT_EQ(epoch0->topics.size(), 5u);
+ EXPECT_EQ(epoch0->topics[0]->topic_id, 6);
+ EXPECT_EQ(epoch0->topics[0]->topic_name, u"Entertainment industry");
+ EXPECT_TRUE(epoch0->topics[0]->is_real_topic);
+ EXPECT_TRUE(epoch0->topics[0]->observed_by_domains.empty());
+ EXPECT_EQ(epoch0->topics[1]->topic_id, 7);
+ EXPECT_EQ(epoch0->topics[1]->topic_name, u"Humor");
+ EXPECT_TRUE(epoch0->topics[1]->is_real_topic);
+ EXPECT_TRUE(epoch0->topics[1]->observed_by_domains.empty());
+ EXPECT_EQ(epoch0->topics[2]->topic_id, 8);
+ EXPECT_EQ(epoch0->topics[2]->topic_name, u"Live comedy");
+ EXPECT_FALSE(epoch0->topics[2]->is_real_topic);
+ EXPECT_TRUE(epoch0->topics[2]->observed_by_domains.empty());
+ EXPECT_EQ(epoch0->topics[3]->topic_id, 9);
+ EXPECT_EQ(epoch0->topics[3]->topic_name, u"Live sporting events");
+ EXPECT_FALSE(epoch0->topics[3]->is_real_topic);
+ EXPECT_TRUE(epoch0->topics[3]->observed_by_domains.empty());
+ EXPECT_EQ(epoch0->topics[4]->topic_id, 10);
+ EXPECT_EQ(epoch0->topics[4]->topic_name, u"Magic");
+ EXPECT_FALSE(epoch0->topics[4]->is_real_topic);
+ EXPECT_TRUE(epoch0->topics[4]->observed_by_domains.empty());
+
+ EXPECT_TRUE(epoch1->calculation_time.is_null());
+ EXPECT_EQ(epoch1->model_version, "0");
+ EXPECT_EQ(epoch1->taxonomy_version, "0");
+ EXPECT_EQ(epoch1->topics.size(), 0u);
+
+ EXPECT_EQ(epoch2->calculation_time, start_time);
+ EXPECT_EQ(epoch2->model_version, "5000000000");
+ EXPECT_EQ(epoch2->taxonomy_version, "1");
+ EXPECT_EQ(epoch2->topics.size(), 5u);
+ EXPECT_EQ(epoch2->topics[0]->topic_id, 1);
+ EXPECT_EQ(epoch2->topics[0]->topic_name, u"Arts & entertainment");
+ EXPECT_TRUE(epoch2->topics[0]->is_real_topic);
+ EXPECT_EQ(epoch2->topics[0]->observed_by_domains.size(), 2u);
+ EXPECT_EQ(epoch2->topics[0]->observed_by_domains[0], "123");
+ EXPECT_EQ(epoch2->topics[0]->observed_by_domains[1], "456");
+ EXPECT_EQ(epoch2->topics[1]->topic_id, 2);
+ EXPECT_EQ(epoch2->topics[1]->topic_name, u"Acting & theater");
+ EXPECT_TRUE(epoch2->topics[1]->is_real_topic);
+ EXPECT_TRUE(epoch2->topics[1]->observed_by_domains.empty());
+ EXPECT_EQ(epoch2->topics[2]->topic_id, 0);
+ EXPECT_EQ(epoch2->topics[2]->topic_name, u"Unknown");
+ EXPECT_TRUE(epoch2->topics[2]->is_real_topic);
+ EXPECT_TRUE(epoch2->topics[2]->observed_by_domains.empty());
+ EXPECT_EQ(epoch2->topics[3]->topic_id, 4);
+ EXPECT_EQ(epoch2->topics[3]->topic_name, u"Concerts & music festivals");
+ EXPECT_TRUE(epoch2->topics[3]->is_real_topic);
+ EXPECT_TRUE(epoch2->topics[3]->observed_by_domains.empty());
+ EXPECT_EQ(epoch2->topics[4]->topic_id, 5);
+ EXPECT_EQ(epoch2->topics[4]->topic_name, u"Dance");
+ EXPECT_TRUE(epoch2->topics[4]->is_real_topic);
+ EXPECT_TRUE(epoch2->topics[4]->observed_by_domains.empty());
+}
+
TEST_F(BrowsingTopicsServiceImplTest, ClearTopic) {
base::Time start_time = base::Time::Now();
@@ -1618,7 +1744,7 @@ TEST_F(BrowsingTopicsServiceImplTest, ClearTopicsDataForOrigin) {
std::vector<blink::mojom::EpochTopicPtr> api_call_result =
browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://b.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(api_call_result.size(), 1u);
EXPECT_EQ(api_call_result[0]->topic, 3);
@@ -1646,7 +1772,7 @@ TEST_F(BrowsingTopicsServiceImplTest, ClearTopicsDataForOrigin) {
// the same context domain was seen in the page before.
api_call_result = browsing_topics_service_->GetBrowsingTopicsForJsApi(
/*context_origin=*/url::Origin::Create(GURL("https://b.com")),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
// Since the domain "b.com" is removed. The candidate topic won't be returned.
EXPECT_TRUE(api_call_result.empty());
diff --git a/chromium/components/browsing_topics/epoch_topics.cc b/chromium/components/browsing_topics/epoch_topics.cc
index 5c71aefbee3..2f9e5a42419 100644
--- a/chromium/components/browsing_topics/epoch_topics.cc
+++ b/chromium/components/browsing_topics/epoch_topics.cc
@@ -7,6 +7,7 @@
#include "base/hash/legacy_hash.h"
#include "base/json/values_util.h"
#include "base/logging.h"
+#include "base/numerics/checked_math.h"
#include "components/browsing_topics/util.h"
#include "third_party/blink/public/common/features.h"
#include "url/gurl.h"
diff --git a/chromium/components/browsing_topics/mojom/BUILD.gn b/chromium/components/browsing_topics/mojom/BUILD.gn
new file mode 100644
index 00000000000..dd75d329674
--- /dev/null
+++ b/chromium/components/browsing_topics/mojom/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2022 The Chromium 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/channel.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojo_bindings") {
+ sources = [ "browsing_topics_internals.mojom" ]
+ public_deps = [ "//mojo/public/mojom/base" ]
+ webui_module_path = "/"
+}
diff --git a/chromium/components/contextual_search/content/common/mojom/OWNERS b/chromium/components/browsing_topics/mojom/OWNERS
index 08850f42120..08850f42120 100644
--- a/chromium/components/contextual_search/content/common/mojom/OWNERS
+++ b/chromium/components/browsing_topics/mojom/OWNERS
diff --git a/chromium/components/browsing_topics/mojom/browsing_topics_internals.mojom b/chromium/components/browsing_topics/mojom/browsing_topics_internals.mojom
new file mode 100644
index 00000000000..bc4092bf9d9
--- /dev/null
+++ b/chromium/components/browsing_topics/mojom/browsing_topics_internals.mojom
@@ -0,0 +1,165 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module browsing_topics.mojom;
+
+import "mojo/public/mojom/base/string16.mojom";
+import "mojo/public/mojom/base/time.mojom";
+
+// Struct representing the features and configuration parameters related to the
+// topics API.
+struct WebUIBrowsingTopicsConfiguration {
+ // Whether the `BrowsingTopics` feature is enabled.
+ bool browsing_topics_enabled;
+
+ // Whether the `PrivacySandboxAdsAPIsOverride` feature is enabled.
+ bool privacy_sandbox_ads_apis_override_enabled;
+
+ // Whether the `PrivacySandboxSettings3` feature is enabled.
+ bool privacy_sandbox_settings3_enabled;
+
+ // Whether the `OverridePrivacySandboxSettingsLocalTesting` feature is
+ // enabled.
+ bool override_privacy_sandbox_settings_local_testing_enabled;
+
+ // Whether the `BrowsingTopicsBypassIPIsPubliclyRoutableCheck` feature is
+ // enabled.
+ bool browsing_topics_bypass_ip_is_publicly_routable_check_enabled;
+
+ // The number of epochs from where to calculate the topics to give to a
+ // requesting contexts.
+ int32 number_of_epochs_to_expose;
+
+ // The periodic topics calculation interval.
+ mojo_base.mojom.TimeDelta time_period_per_epoch;
+
+ // The number of top topics to derive and to keep for each epoch.
+ int32 number_of_top_topics_per_epoch;
+
+ // The probability (in percent number) to return the random topic to a site.
+ // The "random topic" is per-site, and is selected from the full taxonomy
+ // uniformly at random, and each site has a
+ // `use_random_topic_probability_percent`% chance to see the random topic
+ // instead of one of the top topics.
+ int32 use_random_topic_probability_percent;
+
+ // How many epochs of API usage data (i.e. topics observations) will be based
+ // off for the filtering of topics for a calling context.
+ int32 number_of_epochs_of_observation_data_to_use_for_filtering;
+
+ // The max number of observed-by context domains to keep for each top topic.
+ // The intent is to cap the in-use memory.
+ int32 max_number_of_api_usage_context_domains_to_keep_per_topic;
+
+ // The max number of entries allowed to be retrieved from the
+ // `BrowsingTopicsSiteDataStorage` database for each query for the API usage
+ // contexts. The query will occur once per epoch at topics calculation time.
+ // The intent is to cap the peak memory usage.
+ int32 max_number_of_api_usage_context_entries_to_load_per_epoch;
+
+ // The max number of API usage context domains allowed to be stored per page
+ // load.
+ int32 max_number_of_api_usage_context_domains_to_store_per_page_load;
+
+ // The configuration version applicable to all epochs.
+ int32 config_version;
+
+ // The taxonomy version. This only affects the topics classification that
+ // occurs during this browser session, and doesn't affect the pre-existing
+ // epochs.
+ int32 taxonomy_version;
+};
+
+// Struct representing the state of one topic that will be displayed by WebUI.
+struct WebUITopic {
+ // An integer id for this topic.
+ int32 topic_id;
+
+ // The readable name for this topic.
+ mojo_base.mojom.String16 topic_name;
+
+ // Whether the topic is a real topic derived from browsing history or a
+ // randomly padded one.
+ bool is_real_topic;
+
+ // The context domains that have observed the topic (i.e. called the
+ // document.browsingTopics() API in a page that is classified to the topic).
+ array<string> observed_by_domains;
+};
+
+// Struct representing the state of one epoch that will be displayed by WebUI.
+struct WebUIEpoch {
+ // The top topics for this epoch derived from the browsing history.
+ array<WebUITopic> topics;
+
+ // When the topics were calculated.
+ mojo_base.mojom.Time calculation_time;
+
+ // The version of the model used to calculate this epoch's topics.
+ string model_version;
+
+ // The version of the taxonomy applicable to this epoch's topics.
+ string taxonomy_version;
+};
+
+// Struct representing the topics state that will be displayed by WebUI.
+struct WebUIBrowsingTopicsState {
+ // Epochs ordered from latest to oldest.
+ array<WebUIEpoch> epochs;
+
+ // The next time when topics will be calculated.
+ mojo_base.mojom.Time next_scheduled_calculation_time;
+};
+
+// Contains either the topics state, or an error/override message.
+union WebUIGetBrowsingTopicsStateResult {
+ // To be displayed in the UI for showing errors, or giving more clarifications
+ // for an empty state.
+ string override_status_message;
+
+ // The topics state.
+ WebUIBrowsingTopicsState browsing_topics_state;
+};
+
+// Struct representing the model metadata that will be displayed by WebUI.
+struct WebUIModelInfo {
+ string model_version;
+ string model_file_path;
+};
+
+// Contains either the model metadata, or an error/override message.
+union WebUIGetModelInfoResult {
+ string override_status_message;
+ WebUIModelInfo model_info;
+};
+
+// Browser interface for the page. Consists of calls for data and hooks for
+// interactivity. This is implemented in the browser and handling requests from
+// the WebUI renderer.
+interface PageHandler {
+ // Get the features and configuration parameters related to the topics API.
+ GetBrowsingTopicsConfiguration() => (WebUIBrowsingTopicsConfiguration config);
+
+ // Get the topics state.
+ GetBrowsingTopicsState() => (WebUIGetBrowsingTopicsStateResult result);
+
+ // Get the model metadata.
+ GetModelInfo() => (WebUIGetModelInfoResult result);
+
+ // Classify `hosts` into topics using the same classification service that the
+ // Topics API (https://github.com/patcg-individual-drafts/topics) relies on,
+ // e.g. as if the Topics calculation happens now where `hosts` are the
+ // eligible hosts in the browsing history. Precondition: `hosts` is not empty.
+ // If the classification succeeds, each nested array in `topics_for_hosts`
+ // will contain the topics for the corresponding entry in `hosts`; if the
+ // classification fails, (e.g. model unavailable), `topics_for_hosts` will be
+ // an empty array.
+ //
+ // Note that the model or its availability status may have changed since the
+ // last call to `GetModelInfo()` (i.e. the WebUI implementation always happens
+ // to do in this order), but this is a minor issue so we won't expose the
+ // model info via this method again.
+ ClassifyHosts(array<string> hosts)
+ => (array<array<WebUITopic>> topics_for_hosts);
+};
diff --git a/chromium/components/browsing_topics/test_util.h b/chromium/components/browsing_topics/test_util.h
index 6bc6922483b..c8093344c27 100644
--- a/chromium/components/browsing_topics/test_util.h
+++ b/chromium/components/browsing_topics/test_util.h
@@ -11,6 +11,7 @@
#include "base/time/time.h"
#include "components/browsing_topics/browsing_topics_calculator.h"
#include "components/browsing_topics/browsing_topics_service.h"
+#include "components/browsing_topics/mojom/browsing_topics_internals.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/mojom/browsing_topics/browsing_topics.mojom.h"
@@ -82,6 +83,10 @@ class MockBrowsingTopicsService : public BrowsingTopicsService {
GetBrowsingTopicsForJsApi,
(const url::Origin&, content::RenderFrameHost*),
(override));
+ MOCK_METHOD(mojom::WebUIGetBrowsingTopicsStateResultPtr,
+ GetBrowsingTopicsStateForWebUi,
+ (),
+ (const override));
MOCK_METHOD(std::vector<privacy_sandbox::CanonicalTopic>,
GetTopicsForSiteForDisplay,
(const url::Origin&),
diff --git a/chromium/components/captive_portal/core/features.gni b/chromium/components/captive_portal/core/features.gni
index 9b277f56f4a..fac41ad82a6 100644
--- a/chromium/components/captive_portal/core/features.gni
+++ b/chromium/components/captive_portal/core/features.gni
@@ -6,5 +6,5 @@ import("//build/config/compiler/compiler.gni")
# Please keep features in alphabetical order.
declare_args() {
- enable_captive_portal_detection = !is_android && !is_chromecast && !is_ios
+ enable_captive_portal_detection = !is_android && !is_ios && !is_castos
}
diff --git a/chromium/components/cast/named_message_port_connector/named_message_port_connector.js b/chromium/components/cast/named_message_port_connector/named_message_port_connector.js
index 53913adc42d..8f453cdaa52 100644
--- a/chromium/components/cast/named_message_port_connector/named_message_port_connector.js
+++ b/chromium/components/cast/named_message_port_connector/named_message_port_connector.js
@@ -9,7 +9,7 @@ if (!window['cast']) {
* @const
*/
// eslint-disable-next-line no-var
- var cast = new Object;
+ var cast = new Object();
}
if (!cast['__platform__']) {
diff --git a/chromium/components/cast_certificate/cast_crl.cc b/chromium/components/cast_certificate/cast_crl.cc
index a6a20714569..aa83bcbcb96 100644
--- a/chromium/components/cast_certificate/cast_crl.cc
+++ b/chromium/components/cast_certificate/cast_crl.cc
@@ -12,6 +12,7 @@
#include "base/base64.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
+#include "base/numerics/clamped_math.h"
#include "crypto/sha2.h"
#include "net/cert/internal/cert_errors.h"
#include "net/cert/internal/parse_certificate.h"
diff --git a/chromium/components/cast_streaming/DEPS b/chromium/components/cast_streaming/DEPS
index 8d7da2946c8..2480d91aae9 100644
--- a/chromium/components/cast_streaming/DEPS
+++ b/chromium/components/cast_streaming/DEPS
@@ -1,7 +1,5 @@
include_rules = [
- "+content/public/common",
"+net",
- "+services/network/public",
# The browser and renderer code may not depend on eachother, and can only
# depend on shared "public" code.
diff --git a/chromium/components/cast_streaming/browser/BUILD.gn b/chromium/components/cast_streaming/browser/BUILD.gn
index 4cce25d0d16..a69837fa21b 100644
--- a/chromium/components/cast_streaming/browser/BUILD.gn
+++ b/chromium/components/cast_streaming/browser/BUILD.gn
@@ -50,6 +50,7 @@ if (!is_win) {
":demuxer_stream_data_provider",
":network_context",
":receiver_session_public",
+ ":streaming_initialization_info",
":streaming_session",
"//base",
"//media",
@@ -71,8 +72,10 @@ if (!is_win) {
source_set("streaming_session") {
deps = [
":core",
+ ":demuxer_stream_data_provider",
":receiver_session_public",
":renderer_controls",
+ ":streaming_initialization_info",
"//base",
"//components/cast_streaming/public",
"//components/cast_streaming/public:config_conversions",
@@ -103,6 +106,7 @@ if (!is_win) {
testonly = true
deps = [
":core",
+ ":streaming_initialization_info",
"//components/cast_streaming/public",
"//components/cast_streaming/public:config_conversions",
"//media/mojo/common",
@@ -173,6 +177,7 @@ if (!is_win) {
source_set("demuxer_stream_data_provider") {
deps = [
+ ":demuxer_stream_client",
"//base",
"//components/cast_streaming/public/mojom",
"//media",
@@ -194,8 +199,29 @@ source_set("network_context") {
sources = [ "network_context_getter.cc" ]
}
+source_set("streaming_initialization_info") {
+ public_deps = [
+ ":demuxer_stream_client",
+ "//media",
+ "//third_party/openscreen/src/cast/streaming:receiver",
+ ]
+ visibility = [ ":*" ]
+ sources = [
+ "streaming_initialization_info.cc",
+ "streaming_initialization_info.h",
+ ]
+}
+
+source_set("demuxer_stream_client") {
+ visibility = [ ":*" ]
+ sources = [ "demuxer_stream_client.h" ]
+ public_deps = [ "//base" ]
+}
+
source_set("renderer_controls") {
public_deps = [
+ ":demuxer_stream_client",
+ ":streaming_initialization_info",
"//base",
"//components/cast_streaming/public:remoting_utils",
"//components/cast_streaming/public/mojom",
@@ -214,6 +240,8 @@ source_set("renderer_controls") {
"renderer_control_multiplexer.h",
"renderer_rpc_call_translator.cc",
"renderer_rpc_call_translator.h",
+ "rpc_demuxer_stream_handler.cc",
+ "rpc_demuxer_stream_handler.h",
"rpc_initialization_call_handler_base.cc",
"rpc_initialization_call_handler_base.h",
]
@@ -235,13 +263,19 @@ source_set("unit_tests") {
testonly = true
deps = [
":browser",
+ ":demuxer_stream_client",
":demuxer_stream_data_provider",
":renderer_controls",
"//base",
"//base/test:test_support",
+ "//components/cast_streaming/public:remoting_utils",
+ "//media",
"//testing/gtest",
]
- sources = [ "demuxer_stream_data_provider_unittest.cc" ]
+ sources = [
+ "demuxer_stream_data_provider_unittest.cc",
+ "rpc_demuxer_stream_handler_unittests.cc",
+ ]
if (!is_win) {
deps += [
diff --git a/chromium/components/cast_streaming/browser/cast_streaming_session.cc b/chromium/components/cast_streaming/browser/cast_streaming_session.cc
index a8ebd8ee24e..d611b9252c7 100644
--- a/chromium/components/cast_streaming/browser/cast_streaming_session.cc
+++ b/chromium/components/cast_streaming/browser/cast_streaming_session.cc
@@ -5,13 +5,16 @@
#include "components/cast_streaming/browser/cast_streaming_session.h"
#include "base/bind.h"
+#include "base/callback.h"
#include "base/time/time.h"
#include "components/cast_streaming/browser/stream_consumer.h"
#include "components/cast_streaming/public/config_conversions.h"
+#include "media/base/demuxer_stream.h"
#include "media/base/timestamp_constants.h"
#include "media/mojo/common/mojo_decoder_buffer_converter.h"
#include "mojo/public/cpp/system/data_pipe.h"
+namespace cast_streaming {
namespace {
// Timeout to stop the Session when no data is received.
@@ -32,9 +35,27 @@ bool CreateDataPipeForStreamType(media::DemuxerStream::Type type,
// Timeout to end the Session when no offer message is sent.
constexpr base::TimeDelta kInitTimeout = base::Seconds(5);
-} // namespace
+StreamingInitializationInfo CreateMirroringInitializationInfo(
+ const openscreen::cast::ReceiverSession* session,
+ openscreen::cast::ReceiverSession::ConfiguredReceivers receivers) {
+ absl::optional<StreamingInitializationInfo::AudioStreamInfo>
+ audio_stream_info;
+ if (receivers.audio_receiver) {
+ audio_stream_info.emplace(ToAudioDecoderConfig(receivers.audio_config),
+ receivers.audio_receiver);
+ }
-namespace cast_streaming {
+ absl::optional<StreamingInitializationInfo::VideoStreamInfo>
+ video_stream_info;
+ if (receivers.video_receiver) {
+ video_stream_info.emplace(ToVideoDecoderConfig(receivers.video_config),
+ receivers.video_receiver);
+ }
+
+ return {session, std::move(audio_stream_info), std::move(video_stream_info)};
+}
+
+} // namespace
CastStreamingSession::ReceiverSessionClient::ReceiverSessionClient(
CastStreamingSession::Client* client,
@@ -49,7 +70,8 @@ CastStreamingSession::ReceiverSessionClient::ReceiverSessionClient(
base::BindOnce(
&CastStreamingSession::ReceiverSessionClient::OnCastChannelClosed,
base::Unretained(this))),
- client_(client) {
+ client_(client),
+ weak_factory_(this) {
DCHECK(task_runner);
DCHECK(client_);
@@ -72,18 +94,16 @@ CastStreamingSession::ReceiverSessionClient::ReceiverSessionClient(
base::Unretained(this)));
}
-base::RepeatingClosure
-CastStreamingSession::ReceiverSessionClient::GetAudioBufferRequester() {
+void CastStreamingSession::ReceiverSessionClient::GetAudioBuffer(
+ base::OnceClosure no_frames_available_cb) {
DCHECK(audio_consumer_);
- return base::BindRepeating(&StreamConsumer::ReadFrame,
- audio_consumer_->GetWeakPtr());
+ audio_consumer_->ReadFrame(std::move(no_frames_available_cb));
}
-base::RepeatingClosure
-CastStreamingSession::ReceiverSessionClient::GetVideoBufferRequester() {
+void CastStreamingSession::ReceiverSessionClient::GetVideoBuffer(
+ base::OnceClosure no_frames_available_cb) {
DCHECK(video_consumer_);
- return base::BindRepeating(&StreamConsumer::ReadFrame,
- video_consumer_->GetWeakPtr());
+ video_consumer_->ReadFrame(std::move(no_frames_available_cb));
}
CastStreamingSession::ReceiverSessionClient::~ReceiverSessionClient() = default;
@@ -95,11 +115,12 @@ void CastStreamingSession::ReceiverSessionClient::OnInitializationTimeout() {
is_initialized_ = true;
}
-absl::optional<CastStreamingSession::AudioStreamInfo>
+absl::optional<mojo::ScopedDataPipeConsumerHandle>
CastStreamingSession::ReceiverSessionClient::InitializeAudioConsumer(
- openscreen::cast::Receiver* audio_receiver,
- const openscreen::cast::AudioCaptureConfig& audio_capture_config) {
- DCHECK(audio_receiver);
+ const StreamingInitializationInfo& initialization_info) {
+ if (!initialization_info.audio_stream_info) {
+ return absl::nullopt;
+ }
// Create the audio data pipe.
mojo::ScopedDataPipeProducerHandle data_pipe_producer;
@@ -109,26 +130,35 @@ CastStreamingSession::ReceiverSessionClient::InitializeAudioConsumer(
return absl::nullopt;
}
- // We can use unretained pointers here because StreamConsumer is owned by
- // this object and |client_| is guaranteed to outlive this object. Here,
- // the duration is set to kNoTimestamp so the audio renderer does not block.
- // Audio frames duration is not known ahead of time in mirroring.
- audio_consumer_ = std::make_unique<StreamConsumer>(
- audio_receiver, media::kNoTimestamp, std::move(data_pipe_producer),
- base::BindRepeating(&CastStreamingSession::Client::OnAudioBufferReceived,
- base::Unretained(client_)),
- base::BindRepeating(&base::OneShotTimer::Reset,
- base::Unretained(&data_timeout_timer_)));
-
- return AudioStreamInfo{ToAudioDecoderConfig(audio_capture_config),
- std::move(data_pipe_consumer)};
+ if (!audio_consumer_) {
+ // We can use unretained pointers here because StreamConsumer is owned by
+ // this object and |client_| is guaranteed to outlive this object. Here,
+ // the duration is set to kNoTimestamp so the audio renderer does not block.
+ // Audio frames duration is not known ahead of time in mirroring.
+ audio_consumer_ = std::make_unique<StreamConsumer>(
+ initialization_info.audio_stream_info->receiver, media::kNoTimestamp,
+ std::move(data_pipe_producer),
+ base::BindRepeating(
+ &CastStreamingSession::Client::OnAudioBufferReceived,
+ base::Unretained(client_)),
+ base::BindRepeating(&base::OneShotTimer::Reset,
+ base::Unretained(&data_timeout_timer_)));
+ } else {
+ audio_consumer_ = std::make_unique<StreamConsumer>(
+ std::move(*audio_consumer_),
+ initialization_info.audio_stream_info->receiver,
+ std::move(data_pipe_producer));
+ }
+
+ return data_pipe_consumer;
}
-absl::optional<CastStreamingSession::VideoStreamInfo>
+absl::optional<mojo::ScopedDataPipeConsumerHandle>
CastStreamingSession::ReceiverSessionClient::InitializeVideoConsumer(
- openscreen::cast::Receiver* video_receiver,
- const openscreen::cast::VideoCaptureConfig& video_capture_config) {
- DCHECK(video_receiver);
+ const StreamingInitializationInfo& initialization_info) {
+ if (!initialization_info.video_stream_info) {
+ return absl::nullopt;
+ }
// Create the video data pipe.
mojo::ScopedDataPipeProducerHandle data_pipe_producer;
@@ -138,47 +168,64 @@ CastStreamingSession::ReceiverSessionClient::InitializeVideoConsumer(
return absl::nullopt;
}
- // We can use unretained pointers here because StreamConsumer is owned by
- // this object and |client_| is guaranteed to outlive this object.
- // |data_timeout_timer_| is also owned by this object and will outlive both
- // StreamConsumers.
- // The frame duration is set to 10 minutes to work around cases where
- // senders do not send data for a long period of time. We end up with
- // overlapping video frames but this is fine since the media pipeline mostly
- // considers the playout time when deciding which frame to present or play
- video_consumer_ = std::make_unique<StreamConsumer>(
- video_receiver, base::Minutes(10), std::move(data_pipe_producer),
- base::BindRepeating(&CastStreamingSession::Client::OnVideoBufferReceived,
- base::Unretained(client_)),
- base::BindRepeating(&base::OneShotTimer::Reset,
- base::Unretained(&data_timeout_timer_)));
-
- return VideoStreamInfo{ToVideoDecoderConfig(video_capture_config),
- std::move(data_pipe_consumer)};
+ if (!video_consumer_) {
+ // We can use unretained pointers here because StreamConsumer is owned by
+ // this object and |client_| is guaranteed to outlive this object.
+ // |data_timeout_timer_| is also owned by this object and will outlive both
+ // StreamConsumers.
+ // The frame duration is set to 10 minutes to work around cases where
+ // senders do not send data for a long period of time. We end up with
+ // overlapping video frames but this is fine since the media pipeline mostly
+ // considers the playout time when deciding which frame to present or play
+ video_consumer_ = std::make_unique<StreamConsumer>(
+ initialization_info.video_stream_info->receiver, base::Minutes(10),
+ std::move(data_pipe_producer),
+ base::BindRepeating(
+ &CastStreamingSession::Client::OnVideoBufferReceived,
+ base::Unretained(client_)),
+ base::BindRepeating(&base::OneShotTimer::Reset,
+ base::Unretained(&data_timeout_timer_)));
+ } else {
+ video_consumer_ = std::make_unique<StreamConsumer>(
+ std::move(*video_consumer_),
+ initialization_info.video_stream_info->receiver,
+ std::move(data_pipe_producer));
+ }
+
+ return data_pipe_consumer;
}
-void CastStreamingSession::ReceiverSessionClient::OnNegotiated(
- const openscreen::cast::ReceiverSession* session,
- openscreen::cast::ReceiverSession::ConfiguredReceivers receivers) {
+void CastStreamingSession::ReceiverSessionClient::StartStreamingSession(
+ StreamingInitializationInfo initialization_info) {
DVLOG(1) << __func__;
- DCHECK_EQ(session, receiver_session_.get());
+ DCHECK_EQ(initialization_info.session, receiver_session_.get());
+
+ // This is necessary in case the offer message had no audio and no video
+ // stream.
+ if (!initialization_info.audio_stream_info &&
+ !initialization_info.video_stream_info) {
+ client_->OnSessionEnded();
+ return;
+ }
+
init_timeout_timer_.Stop();
bool is_new_offer = is_initialized_;
if (is_new_offer) {
// This is a second offer message, reinitialize the streams.
- bool existing_session_has_audio = audio_consumer_ != nullptr;
- bool existing_session_has_video = video_consumer_ != nullptr;
- audio_consumer_.reset();
- video_consumer_.reset();
-
- bool new_offer_has_audio = receivers.audio_receiver != nullptr;
- bool new_offer_has_video = receivers.video_receiver != nullptr;
+ const bool existing_session_has_audio = !!audio_consumer_;
+ const bool existing_session_has_video = !!video_consumer_;
+ const bool new_offer_has_audio = !!initialization_info.audio_stream_info;
+ const bool new_offer_has_video = !!initialization_info.video_stream_info;
if (new_offer_has_audio != existing_session_has_audio ||
new_offer_has_video != existing_session_has_video) {
- // Different audio/video configuration than in the first offer message.
- // Return early here.
+ // This call to StartStreamingSession() has support for audio and/or video
+ // streaming which does not match the ones provided during a prior call to
+ // this method. Return early here.
+ DLOG(ERROR) << "New streaming session has support for audio or video "
+ "which does not match the ones provided during a prior "
+ "streaming initialization.";
client_->OnSessionEnded();
return;
}
@@ -187,47 +234,41 @@ void CastStreamingSession::ReceiverSessionClient::OnNegotiated(
// Set |is_initialized_| now so we can return early on failure.
is_initialized_ = true;
- absl::optional<AudioStreamInfo> audio_stream_info;
- if (receivers.audio_receiver) {
- audio_stream_info = InitializeAudioConsumer(receivers.audio_receiver,
- receivers.audio_config);
- if (audio_stream_info) {
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> audio_pipe_consumer_handle;
+ if (initialization_info.audio_stream_info) {
+ audio_pipe_consumer_handle = InitializeAudioConsumer(initialization_info);
+ if (audio_pipe_consumer_handle) {
DVLOG(1) << "Initialized audio stream. "
- << audio_stream_info->decoder_config.AsHumanReadableString();
+ << initialization_info.audio_stream_info->config
+ .AsHumanReadableString();
} else {
client_->OnSessionEnded();
return;
}
}
- absl::optional<VideoStreamInfo> video_stream_info;
- if (receivers.video_receiver) {
- video_stream_info = InitializeVideoConsumer(receivers.video_receiver,
- receivers.video_config);
- if (video_stream_info) {
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> video_pipe_consumer_handle;
+ if (initialization_info.video_stream_info) {
+ video_pipe_consumer_handle = InitializeVideoConsumer(initialization_info);
+ if (video_pipe_consumer_handle) {
DVLOG(1) << "Initialized video stream. "
- << video_stream_info->decoder_config.AsHumanReadableString();
+ << initialization_info.video_stream_info->config
+ .AsHumanReadableString();
} else {
audio_consumer_.reset();
- audio_stream_info.reset();
client_->OnSessionEnded();
return;
}
}
- // This is necessary in case the offer message had no audio and no video
- // stream.
- if (!audio_stream_info && !video_stream_info) {
- client_->OnSessionEnded();
- return;
- }
-
if (is_new_offer) {
- client_->OnSessionReinitialization(std::move(audio_stream_info),
- std::move(video_stream_info));
+ client_->OnSessionReinitialization(std::move(initialization_info),
+ std::move(audio_pipe_consumer_handle),
+ std::move(video_pipe_consumer_handle));
} else {
- client_->OnSessionInitialization(std::move(audio_stream_info),
- std::move(video_stream_info));
+ client_->OnSessionInitialization(std::move(initialization_info),
+ std::move(audio_pipe_consumer_handle),
+ std::move(video_pipe_consumer_handle));
data_timeout_timer_.Start(
FROM_HERE, kNoDataTimeout,
base::BindOnce(
@@ -236,13 +277,21 @@ void CastStreamingSession::ReceiverSessionClient::OnNegotiated(
}
}
+void CastStreamingSession::ReceiverSessionClient::OnNegotiated(
+ const openscreen::cast::ReceiverSession* session,
+ openscreen::cast::ReceiverSession::ConfiguredReceivers receivers) {
+ StartStreamingSession(
+ CreateMirroringInitializationInfo(session, std::move(receivers)));
+}
+
void CastStreamingSession::ReceiverSessionClient::OnRemotingNegotiated(
const openscreen::cast::ReceiverSession* session,
openscreen::cast::ReceiverSession::RemotingNegotiation negotiation) {
DCHECK(playback_command_dispatcher_);
- OnNegotiated(session, std::move(negotiation.receivers));
playback_command_dispatcher_->OnRemotingSessionNegotiated(
negotiation.messenger);
+ playback_command_dispatcher_->ConfigureRemotingAsync(
+ this, session, std::move(negotiation.receivers));
}
void CastStreamingSession::ReceiverSessionClient::OnReceiversDestroying(
@@ -287,6 +336,11 @@ void CastStreamingSession::ReceiverSessionClient::OnCastChannelClosed() {
receiver_session_.reset();
}
+base::WeakPtr<CastStreamingSession::ReceiverSessionClient>
+CastStreamingSession::ReceiverSessionClient::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
CastStreamingSession::Client::~Client() = default;
CastStreamingSession::CastStreamingSession() = default;
CastStreamingSession::~CastStreamingSession() = default;
@@ -311,14 +365,20 @@ void CastStreamingSession::Stop() {
receiver_session_.reset();
}
-base::RepeatingClosure CastStreamingSession::GetAudioBufferRequester() {
+AudioDemuxerStreamDataProvider::RequestBufferCB
+CastStreamingSession::GetAudioBufferRequester() {
DCHECK(receiver_session_);
- return receiver_session_->GetAudioBufferRequester();
+ return base::BindRepeating(
+ &CastStreamingSession::ReceiverSessionClient::GetAudioBuffer,
+ receiver_session_->GetWeakPtr());
}
-base::RepeatingClosure CastStreamingSession::GetVideoBufferRequester() {
+VideoDemuxerStreamDataProvider::RequestBufferCB
+CastStreamingSession::GetVideoBufferRequester() {
DCHECK(receiver_session_);
- return receiver_session_->GetVideoBufferRequester();
+ return base::BindRepeating(
+ &CastStreamingSession::ReceiverSessionClient::GetVideoBuffer,
+ receiver_session_->GetWeakPtr());
}
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/browser/cast_streaming_session.h b/chromium/components/cast_streaming/browser/cast_streaming_session.h
index db8ed7f6c35..f06ea030423 100644
--- a/chromium/components/cast_streaming/browser/cast_streaming_session.h
+++ b/chromium/components/cast_streaming/browser/cast_streaming_session.h
@@ -14,8 +14,10 @@
#include "base/timer/timer.h"
#include "components/cast/message_port/message_port.h"
#include "components/cast_streaming/browser/cast_message_port_impl.h"
+#include "components/cast_streaming/browser/demuxer_stream_data_provider.h"
#include "components/cast_streaming/browser/playback_command_dispatcher.h"
#include "components/cast_streaming/browser/public/receiver_session.h"
+#include "components/cast_streaming/browser/remoting_session_client.h"
#include "components/cast_streaming/browser/renderer_controller_config.h"
#include "components/openscreen_platform/network_util.h"
#include "components/openscreen_platform/task_runner.h"
@@ -35,22 +37,16 @@ class StreamConsumer;
// Cast Streaming Session for a provided MessagePort server.
class CastStreamingSession {
public:
- template <class T>
- struct StreamInfo {
- T decoder_config;
- mojo::ScopedDataPipeConsumerHandle data_pipe;
- };
- using AudioStreamInfo = StreamInfo<media::AudioDecoderConfig>;
- using VideoStreamInfo = StreamInfo<media::VideoDecoderConfig>;
-
class Client {
public:
// Called when the Cast Streaming Session has been successfully initialized.
// It is guaranteed that at least one of |audio_stream_info| or
// |video_stream_info| will be set.
virtual void OnSessionInitialization(
- absl::optional<AudioStreamInfo> audio_stream_info,
- absl::optional<VideoStreamInfo> video_stream_info) = 0;
+ StreamingInitializationInfo initialization_info,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> audio_pipe_consumer,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle>
+ video_pipe_consumer) = 0;
// Called on every new audio buffer after OnSessionInitialization(). The
// frame data must be accessed via the |data_pipe| property in StreamInfo.
@@ -65,8 +61,10 @@ class CastStreamingSession {
// Called on receiver session reinitialization. It is guaranteed that at
// least one of |audio_stream_info| or |video_stream_info| will be set.
virtual void OnSessionReinitialization(
- absl::optional<AudioStreamInfo> audio_stream_info,
- absl::optional<VideoStreamInfo> video_stream_info) = 0;
+ StreamingInitializationInfo initialization_info,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> audio_pipe_consumer,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle>
+ video_pipe_consumer) = 0;
// Called when the Cast Streaming Session has ended.
virtual void OnSessionEnded() = 0;
@@ -103,17 +101,20 @@ class CastStreamingSession {
// lifespan of this object and only after a call to Start().
void Stop();
+ bool is_running() const { return !!receiver_session_; }
+
// Return a callback that may be used to request a buffer of the specified
// type, to be returned asynchronously through the client API. May only be
// called following a call to Start() and prior to a call to Stop().
- base::RepeatingClosure GetAudioBufferRequester();
- base::RepeatingClosure GetVideoBufferRequester();
+ AudioDemuxerStreamDataProvider::RequestBufferCB GetAudioBufferRequester();
+ VideoDemuxerStreamDataProvider::RequestBufferCB GetVideoBufferRequester();
private:
// Owns the Open Screen ReceiverSession. The Streaming Session is tied to the
// lifespan of this object.
class ReceiverSessionClient final
- : public openscreen::cast::ReceiverSession::Client {
+ : public openscreen::cast::ReceiverSession::Client,
+ public remoting::RemotingSessionClient::Dispatcher {
public:
ReceiverSessionClient(
CastStreamingSession::Client* client,
@@ -129,23 +130,25 @@ class CastStreamingSession {
// Requests a new buffer of the specified type, which will be provided
// Return a callback that may be used to request a buffer of the specified
// type, to be returned asynchronously through the |client_|.
- base::RepeatingClosure GetAudioBufferRequester();
- base::RepeatingClosure GetVideoBufferRequester();
+ void GetAudioBuffer(base::OnceClosure no_frames_available_cb);
+ void GetVideoBuffer(base::OnceClosure no_frames_available_cb);
+
+ // Returns a WeakPtr associated with this instance;
+ base::WeakPtr<ReceiverSessionClient> GetWeakPtr();
private:
void OnInitializationTimeout();
- // Initializes the audio consumer with |audio_capture_config|. Returns an
- // empty Optional on failure.
- absl::optional<AudioStreamInfo> InitializeAudioConsumer(
- openscreen::cast::Receiver* audio_receiver,
- const openscreen::cast::AudioCaptureConfig& audio_capture_config);
+ // Initializes the audio or video consumer, returning the data pipe to
+ // be used to pass DecoderBuffer data to the Renderer process on success.
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> InitializeAudioConsumer(
+ const StreamingInitializationInfo& initialization_info);
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> InitializeVideoConsumer(
+ const StreamingInitializationInfo& initialization_info);
- // Initializes the video consumer with |video_capture_config|. Returns an
- // empty Optional on failure.
- absl::optional<VideoStreamInfo> InitializeVideoConsumer(
- openscreen::cast::Receiver* video_receiver,
- const openscreen::cast::VideoCaptureConfig& video_capture_config);
+ // remoting::PlaybackCommandDispatcher::Client implementation.
+ void StartStreamingSession(
+ StreamingInitializationInfo initialization_info) override;
// openscreen::cast::ReceiverSession::Client implementation.
void OnNegotiated(const openscreen::cast::ReceiverSession* session,
@@ -180,6 +183,8 @@ class CastStreamingSession {
const raw_ptr<CastStreamingSession::Client> client_;
std::unique_ptr<StreamConsumer> audio_consumer_;
std::unique_ptr<StreamConsumer> video_consumer_;
+
+ base::WeakPtrFactory<ReceiverSessionClient> weak_factory_;
};
std::unique_ptr<ReceiverSessionClient> receiver_session_;
diff --git a/chromium/components/cast_streaming/browser/demuxer_stream_client.h b/chromium/components/cast_streaming/browser/demuxer_stream_client.h
new file mode 100644
index 00000000000..c320bff7114
--- /dev/null
+++ b/chromium/components/cast_streaming/browser/demuxer_stream_client.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_CAST_STREAMING_BROWSER_DEMUXER_STREAM_CLIENT_H_
+#define COMPONENTS_CAST_STREAMING_BROWSER_DEMUXER_STREAM_CLIENT_H_
+
+#include "base/callback.h"
+
+namespace cast_streaming {
+
+// This class acts as a client to act upon state changes of the DemuxerStream
+// and DemuxerStreamDataProvider with which it is associated.
+class DemuxerStreamClient {
+ public:
+ virtual ~DemuxerStreamClient() = default;
+
+ // Enables the bitstream converter for the data provider associated with this
+ // demuxer stream.
+ using BitstreamConverterEnabledCB = base::OnceCallback<void(bool)>;
+ virtual void EnableBitstreamConverter(BitstreamConverterEnabledCB cb) = 0;
+
+ // Called when no buffers are available for reading.
+ virtual void OnNoBuffersAvailable() = 0;
+
+ // Called when a fatal error occurs. Only called once.
+ virtual void OnError() = 0;
+};
+
+} // namespace cast_streaming
+
+#endif // COMPONENTS_CAST_STREAMING_BROWSER_DEMUXER_STREAM_CLIENT_H_
diff --git a/chromium/components/cast_streaming/browser/demuxer_stream_data_provider.h b/chromium/components/cast_streaming/browser/demuxer_stream_data_provider.h
index d5dffe1c536..6cc294cd8e2 100644
--- a/chromium/components/cast_streaming/browser/demuxer_stream_data_provider.h
+++ b/chromium/components/cast_streaming/browser/demuxer_stream_data_provider.h
@@ -6,8 +6,10 @@
#define COMPONENTS_CAST_STREAMING_BROWSER_DEMUXER_STREAM_DATA_PROVIDER_H_
#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
-#include "components/cast_streaming/public/mojom/cast_streaming_session.mojom.h"
+#include "components/cast_streaming/browser/demuxer_stream_client.h"
+#include "components/cast_streaming/public/mojom/demuxer_connector.mojom.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
@@ -40,18 +42,27 @@ class DemuxerStreamDataProvider : public TMojoReceiverType {
// media::AudioDecoderConfig or media::VideoDecoderConfig).
typedef decltype(TStreamInfoType::element_type::decoder_config) ConfigType;
+ // The callback type which will be used to request a new buffer be read. The
+ // callback is expected to call ProvideBuffer() once a buffer is available.
+ // The callback parameter provided when calling a RequestBufferCB is to be
+ // called if there no buffers available for reading at time of calling.
+ typedef base::RepeatingCallback<void(base::OnceClosure)> RequestBufferCB;
+
// |request_buffer| is the callback which will be used to request a new
// buffer be read. The callback is expected to call ProvideBuffer() once a
// buffer is available.
DemuxerStreamDataProvider(
mojo::PendingReceiver<TMojoReceiverType> pending_receiver,
- base::RepeatingClosure request_buffer,
+ RequestBufferCB request_buffer,
base::OnceClosure on_mojo_disconnect,
ConfigType config)
: config_(std::move(config)),
request_buffer_(std::move(request_buffer)),
- receiver_(this, std::move(pending_receiver)) {
- receiver_.set_disconnect_handler(std::move(on_mojo_disconnect));
+ on_mojo_disconnect_(std::move(on_mojo_disconnect)),
+ receiver_(this, std::move(pending_receiver)),
+ weak_factory_(this) {
+ receiver_.set_disconnect_handler(base::BindOnce(
+ &DemuxerStreamDataProvider::OnFatalError, weak_factory_.GetWeakPtr()));
}
// Sets the new config to be passed to the renderer process as part of the
@@ -76,10 +87,28 @@ class DemuxerStreamDataProvider : public TMojoReceiverType {
.Run(std::move(next_stream_info_), std::move(buffer));
}
- const ConfigType& config() const { return config_; }
+ const ConfigType& config() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return config_;
+ }
+
+ void SetClient(base::WeakPtr<DemuxerStreamClient> client) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ client_ = std::move(client);
+ }
private:
using GetBufferCallback = typename TMojoReceiverType::GetBufferCallback;
+ using EnableBitstreamConverterCallback =
+ typename TMojoReceiverType::EnableBitstreamConverterCallback;
+
+ void OnFatalError() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (client_) {
+ client_->OnError();
+ }
+ std::move(on_mojo_disconnect_).Run();
+ }
// TMojoReceiverType implementation.
void GetBuffer(GetBufferCallback callback) override {
@@ -94,7 +123,19 @@ class DemuxerStreamDataProvider : public TMojoReceiverType {
}
current_callback_ = std::move(callback);
- request_buffer_.Run();
+ request_buffer_.Run(
+ base::BindOnce(&DemuxerStreamClient::OnNoBuffersAvailable, client_));
+ }
+
+ void EnableBitstreamConverter(
+ EnableBitstreamConverterCallback callback) override {
+ if (client_) {
+ client_->EnableBitstreamConverter(std::move(callback));
+ } else {
+ std::move(callback).Run(false);
+ LOG(WARNING)
+ << "EnableBitstreamConverter() called when no client was available";
+ }
}
// The most recently set config.
@@ -108,11 +149,20 @@ class DemuxerStreamDataProvider : public TMojoReceiverType {
GetBufferCallback current_callback_;
// Callback to request a new buffer be read from the receiver.
- base::RepeatingClosure request_buffer_;
+ RequestBufferCB request_buffer_;
+
+ // Client to use when the associated DemuxerStream requires an action be
+ // performed.
+ base::WeakPtr<DemuxerStreamClient> client_;
+
+ // Callback called upon a mojo disconnection.
+ base::OnceClosure on_mojo_disconnect_;
SEQUENCE_CHECKER(sequence_checker_);
mojo::Receiver<TMojoReceiverType> receiver_;
+
+ base::WeakPtrFactory<DemuxerStreamDataProvider> weak_factory_;
};
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/browser/demuxer_stream_data_provider_unittest.cc b/chromium/components/cast_streaming/browser/demuxer_stream_data_provider_unittest.cc
index c5201896850..992a1c7b3be 100644
--- a/chromium/components/cast_streaming/browser/demuxer_stream_data_provider_unittest.cc
+++ b/chromium/components/cast_streaming/browser/demuxer_stream_data_provider_unittest.cc
@@ -6,8 +6,10 @@
#include <utility>
+#include "base/memory/weak_ptr.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
+#include "components/cast_streaming/browser/demuxer_stream_client.h"
#include "media/base/audio_codecs.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/channel_layout.h"
@@ -47,6 +49,8 @@ class DemuxerStreamDataProviderTest : public testing::Test {
base::Unretained(&callbacks_)),
second_config_);
+ data_provider_->SetClient(client_.weak_factory_.GetWeakPtr());
+
std::vector<uint8_t> data = {1, 2, 3};
first_buffer_ = media::DecoderBuffer::CopyFrom(data.data(), 3);
first_buffer_->set_duration(base::Seconds(1));
@@ -70,7 +74,7 @@ class DemuxerStreamDataProviderTest : public testing::Test {
protected:
class Callbacks {
public:
- MOCK_METHOD0(RequestBuffer, void());
+ MOCK_METHOD1(RequestBuffer, void(base::OnceClosure));
MOCK_METHOD0(OnMojoDisconnect, void());
MOCK_METHOD0(OnGetBufferDoneCalled, void());
@@ -92,6 +96,17 @@ class DemuxerStreamDataProviderTest : public testing::Test {
}
};
+ class MockDemuxerStreamClient : public DemuxerStreamClient {
+ public:
+ ~MockDemuxerStreamClient() override = default;
+
+ MOCK_METHOD1(EnableBitstreamConverter, void(BitstreamConverterEnabledCB));
+ MOCK_METHOD0(OnNoBuffersAvailable, void());
+ MOCK_METHOD0(OnError, void());
+
+ base::WeakPtrFactory<MockDemuxerStreamClient> weak_factory_{this};
+ };
+
using MojoPipePair = std::pair<mojo::ScopedDataPipeProducerHandle,
mojo::ScopedDataPipeConsumerHandle>;
MojoPipePair GetMojoPipePair() {
@@ -103,6 +118,7 @@ class DemuxerStreamDataProviderTest : public testing::Test {
}
testing::StrictMock<Callbacks> callbacks_;
+ testing::StrictMock<MockDemuxerStreamClient> client_;
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -121,7 +137,7 @@ class DemuxerStreamDataProviderTest : public testing::Test {
TEST_F(DemuxerStreamDataProviderTest, DataSentInOrderExpected) {
// Test first call, providing a config and a buffer.
- EXPECT_CALL(callbacks_, RequestBuffer());
+ EXPECT_CALL(callbacks_, RequestBuffer(testing::_));
remote_->GetBuffer(base::BindOnce(
&DemuxerStreamDataProviderTest::Callbacks::OnGetBufferDone,
base::Unretained(&callbacks_), first_config_, first_buffer_));
@@ -138,7 +154,7 @@ TEST_F(DemuxerStreamDataProviderTest, DataSentInOrderExpected) {
EXPECT_TRUE(first_config_.Matches(data_provider_->config()));
// Test second call, providing NO config but do provide a buffer.
- EXPECT_CALL(callbacks_, RequestBuffer());
+ EXPECT_CALL(callbacks_, RequestBuffer(testing::_));
remote_->GetBuffer(base::BindOnce(
&DemuxerStreamDataProviderTest::Callbacks::OnGetBufferDone,
base::Unretained(&callbacks_), absl::nullopt, second_buffer_));
@@ -152,7 +168,7 @@ TEST_F(DemuxerStreamDataProviderTest, DataSentInOrderExpected) {
task_environment_.RunUntilIdle();
// Test third call, providing a different config and a buffer.
- EXPECT_CALL(callbacks_, RequestBuffer());
+ EXPECT_CALL(callbacks_, RequestBuffer(testing::_));
remote_->GetBuffer(base::BindOnce(
&DemuxerStreamDataProviderTest::Callbacks::OnGetBufferDone,
base::Unretained(&callbacks_), second_config_, third_buffer_));
@@ -169,4 +185,25 @@ TEST_F(DemuxerStreamDataProviderTest, DataSentInOrderExpected) {
task_environment_.RunUntilIdle();
}
+TEST_F(DemuxerStreamDataProviderTest, NoBuffersCallback) {
+ EXPECT_CALL(callbacks_, RequestBuffer(testing::_))
+ .WillOnce([](base::OnceClosure no_buffers_cb) {
+ std::move(no_buffers_cb).Run();
+ });
+ EXPECT_CALL(client_, OnNoBuffersAvailable());
+ remote_->GetBuffer(base::BindOnce(
+ &DemuxerStreamDataProviderTest::Callbacks::OnGetBufferDone,
+ base::Unretained(&callbacks_), first_config_, first_buffer_));
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(DemuxerStreamDataProviderTest, EnableBitstreamConverter) {
+ EXPECT_CALL(client_, EnableBitstreamConverter(testing::_))
+ .WillOnce(
+ [](base::OnceCallback<void(bool)> cb) { std::move(cb).Run(true); });
+ ;
+ remote_->EnableBitstreamConverter(base::OnceCallback<void(bool)>());
+ task_environment_.RunUntilIdle();
+}
+
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/browser/playback_command_dispatcher.cc b/chromium/components/cast_streaming/browser/playback_command_dispatcher.cc
index 2f6b9258c06..062a98536dc 100644
--- a/chromium/components/cast_streaming/browser/playback_command_dispatcher.cc
+++ b/chromium/components/cast_streaming/browser/playback_command_dispatcher.cc
@@ -24,19 +24,20 @@ PlaybackCommandDispatcher::PlaybackCommandDispatcher(
// Create a muxer using the "real" media::mojom::Renderer instance that
// connects to the remote media::Renderer.
mojo::Remote<media::mojom::Renderer> renderer;
- control_configuration->SetPlaybackController(
+ control_configuration_ = std::move(control_configuration);
+ control_configuration_->SetPlaybackController(
renderer.BindNewPipeAndPassReceiver(),
base::BindOnce(&PlaybackCommandDispatcher::OnSetPlaybackControllerDone,
weak_factory_.GetWeakPtr()));
muxer_ = std::make_unique<RendererControlMultiplexer>(std::move(renderer),
task_runner_);
- // Create a "fake" media::mojom::Renderer so that the RpcCallTranslator can
- // pass commands to the |muxer_|.
- mojo::Remote<media::mojom::Renderer> translators_renderer;
- RegisterCommandSource(translators_renderer.BindNewPipeAndPassReceiver());
- call_translator_ = std::make_unique<remoting::RendererRpcCallTranslator>(
- std::move(translators_renderer));
+ auto message_processor_callback = base::BindRepeating(
+ &PlaybackCommandDispatcher::SendRemotingRpcMessageToRemote,
+ weak_factory_.GetWeakPtr());
+ renderer_call_translator_ =
+ std::make_unique<remoting::RendererRpcCallTranslator>(
+ std::move(message_processor_callback), muxer_.get());
}
PlaybackCommandDispatcher::~PlaybackCommandDispatcher() {
@@ -53,41 +54,61 @@ void PlaybackCommandDispatcher::OnRemotingSessionNegotiated(
DCHECK(messenger);
messenger_ = messenger;
- handle_ = messenger_->GetUniqueHandle();
+ RegisterHandleForCallbacks(
+ openscreen::cast::RpcMessenger::kAcquireRendererHandle);
+ RegisterHandleForCallbacks(
+ openscreen::cast::RpcMessenger::kAcquireDemuxerHandle);
- // Include the |handle_| in the callback so that it will persist even upon
- // re-negotiation.
- auto message_processor_callback = base::BindPostTask(
- task_runner_,
+ renderer_call_translator_->set_handle(AcquireHandle());
+ demuxer_stream_handler_ = std::make_unique<remoting::RpcDemuxerStreamHandler>(
+ this,
+ base::BindRepeating(&PlaybackCommandDispatcher::AcquireHandle,
+ base::Unretained(this)),
base::BindRepeating(
&PlaybackCommandDispatcher::SendRemotingRpcMessageToRemote,
- weak_factory_.GetWeakPtr(), handle_),
- FROM_HERE);
- call_translator_->SetMessageProcessor(std::move(message_processor_callback));
+ base::Unretained(this)));
+}
- auto message_receiver_callback = base::BindPostTask(
- task_runner_,
- base::BindRepeating(
- &PlaybackCommandDispatcher::ProcessRemotingRpcMessageFromRemote,
- weak_factory_.GetWeakPtr()),
- FROM_HERE);
- messenger_->RegisterMessageReceiverCallback(
- handle_, [cb = std::move(message_receiver_callback)](
- std::unique_ptr<openscreen::cast::RpcMessage> message) {
- cb.Run(std::move(message));
- });
+void PlaybackCommandDispatcher::ConfigureRemotingAsync(
+ Dispatcher* dispatcher,
+ const openscreen::cast::ReceiverSession* session,
+ openscreen::cast::ReceiverSession::ConfiguredReceivers receivers) {
+ DCHECK(dispatcher);
+ DCHECK(session);
+ DCHECK(demuxer_stream_handler_);
+ DCHECK(!streaming_init_info_);
+
+ streaming_dispatcher_ = dispatcher;
+ receiver_session_ = session;
+
+ absl::optional<StreamingInitializationInfo::AudioStreamInfo>
+ audio_stream_info;
+ if (receivers.audio_receiver) {
+ audio_stream_info.emplace(media::AudioDecoderConfig(),
+ receivers.audio_receiver);
+ }
+
+ absl::optional<StreamingInitializationInfo::VideoStreamInfo>
+ video_stream_info;
+ if (receivers.video_receiver) {
+ video_stream_info.emplace(media::VideoDecoderConfig(),
+ receivers.video_receiver);
+ }
+
+ streaming_init_info_.emplace(receiver_session_, std::move(audio_stream_info),
+ std::move(video_stream_info));
}
void PlaybackCommandDispatcher::OnRemotingSessionEnded() {
- if (messenger_) {
- messenger_->UnregisterMessageReceiverCallback(handle_);
- messenger_ = nullptr;
- }
+ demuxer_stream_handler_.reset();
+ messenger_ = nullptr;
+ streaming_init_info_ = absl::nullopt;
}
void PlaybackCommandDispatcher::SendRemotingRpcMessageToRemote(
openscreen::cast::RpcMessenger::Handle handle,
std::unique_ptr<openscreen::cast::RpcMessage> message) {
+ DCHECK_NE(handle, openscreen::cast::RpcMessenger::kInvalidHandle);
DCHECK(message);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
@@ -104,12 +125,52 @@ void PlaybackCommandDispatcher::ProcessRemotingRpcMessageFromRemote(
DCHECK(message);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
- // TODO(rwkeane): Handle DemuxerStream messages too.
- if (!remoting::DispatchInitializationRpcCall(message.get(), this) &&
- !remoting::DispatchRendererRpcCall(message.get(),
- call_translator_.get())) {
- LOG(ERROR) << "Unhandled RPC Message for command " << message->proc();
+ const bool did_dispatch_as_initialization_call =
+ remoting::DispatchInitializationRpcCall(message.get(), this);
+ if (did_dispatch_as_initialization_call) {
+ return;
}
+
+ const bool did_dispatch_as_renderer_call =
+ renderer_call_translator_ &&
+ remoting::DispatchRendererRpcCall(message.get(),
+ renderer_call_translator_.get());
+ if (did_dispatch_as_renderer_call) {
+ return;
+ }
+
+ const bool did_dispatch_as_demuxer_stream_callback =
+ demuxer_stream_handler_ &&
+ remoting::DispatchDemuxerStreamCBRpcCall(message.get(),
+ demuxer_stream_handler_.get());
+ if (did_dispatch_as_demuxer_stream_callback) {
+ return;
+ }
+
+ LOG(ERROR) << "Unhandled RPC Message for command " << message->proc();
+}
+
+openscreen::cast::RpcMessenger::Handle
+PlaybackCommandDispatcher::AcquireHandle() {
+ DCHECK(messenger_);
+ auto handle = messenger_->GetUniqueHandle();
+ RegisterHandleForCallbacks(handle);
+ return handle;
+}
+
+void PlaybackCommandDispatcher::RegisterHandleForCallbacks(
+ openscreen::cast::RpcMessenger::Handle handle) {
+ DCHECK(messenger_);
+ messenger_->RegisterMessageReceiverCallback(
+ handle, [ptr = weak_factory_.GetWeakPtr()](
+ std::unique_ptr<openscreen::cast::RpcMessage> message) {
+ if (!ptr) {
+ DVLOG(1)
+ << "Message receiver has been invalidated. Dropping message.";
+ return;
+ }
+ ptr->ProcessRemotingRpcMessageFromRemote(std::move(message));
+ });
}
void PlaybackCommandDispatcher::OnSetPlaybackControllerDone() {
@@ -121,17 +182,87 @@ void PlaybackCommandDispatcher::OnSetPlaybackControllerDone() {
}
void PlaybackCommandDispatcher::RpcAcquireRendererAsync(AcquireRendererCB cb) {
- acquire_renderer_cb_ = base::BindOnce(std::move(cb), handle_);
+ DCHECK(renderer_call_translator_);
+ const auto handle = renderer_call_translator_->handle();
+
+ DCHECK_NE(handle, openscreen::cast::RpcMessenger::kInvalidHandle);
+ acquire_renderer_cb_ = base::BindOnce(std::move(cb), handle);
if (has_set_playback_controller_call_returned_) {
std::move(acquire_renderer_cb_).Run();
}
}
-void PlaybackCommandDispatcher::OnRpcAcquireDemuxer(int audio_stream_handle,
- int video_stream_handle) {
- // TODO(rwkeane): Handle DemuxerStreams.
- NOTIMPLEMENTED();
+void PlaybackCommandDispatcher::OnRpcAcquireDemuxer(
+ openscreen::cast::RpcMessenger::Handle audio_stream_handle,
+ openscreen::cast::RpcMessenger::Handle video_stream_handle) {
+ if (demuxer_stream_handler_) {
+ demuxer_stream_handler_->OnRpcAcquireDemuxer(audio_stream_handle,
+ video_stream_handle);
+ }
+}
+
+void PlaybackCommandDispatcher::OnNewAudioConfig(
+ media::AudioDecoderConfig config) {
+ DCHECK(streaming_init_info_);
+ DCHECK(streaming_dispatcher_);
+ if (!streaming_init_info_->audio_stream_info) {
+ LOG(ERROR) << "Received Audio config for a remoting session where audio is "
+ "not supported";
+ return;
+ }
+
+ streaming_init_info_->audio_stream_info->config = std::move(config);
+ MaybeStartStreamingSession();
+}
+
+void PlaybackCommandDispatcher::OnNewVideoConfig(
+ media::VideoDecoderConfig config) {
+ DCHECK(streaming_init_info_);
+ DCHECK(streaming_dispatcher_);
+ if (!streaming_init_info_->video_stream_info) {
+ LOG(ERROR) << "Received Video config for a remoting session where video is "
+ "not supported";
+ return;
+ }
+
+ streaming_init_info_->video_stream_info->config = std::move(config);
+ MaybeStartStreamingSession();
+}
+
+void PlaybackCommandDispatcher::MaybeStartStreamingSession() {
+ DCHECK(streaming_init_info_);
+ const bool is_audio_config_ready =
+ !streaming_init_info_->audio_stream_info ||
+ !streaming_init_info_->audio_stream_info->config.Matches(
+ media::AudioDecoderConfig());
+ const bool is_video_config_ready =
+ !streaming_init_info_->video_stream_info ||
+ !streaming_init_info_->video_stream_info->config.Matches(
+ media::VideoDecoderConfig());
+ if (!is_audio_config_ready || !is_video_config_ready) {
+ return;
+ }
+
+ DCHECK(demuxer_stream_handler_);
+ if (streaming_init_info_->audio_stream_info) {
+ auto client = demuxer_stream_handler_->GetAudioClient();
+ DCHECK(client);
+ client->OnNoBuffersAvailable();
+ streaming_init_info_->audio_stream_info->demuxer_stream_client =
+ std::move(client);
+ }
+ if (streaming_init_info_->video_stream_info) {
+ auto client = demuxer_stream_handler_->GetVideoClient();
+ DCHECK(client);
+ client->OnNoBuffersAvailable();
+ streaming_init_info_->video_stream_info->demuxer_stream_client =
+ std::move(client);
+ }
+
+ // |streaming_init_info_| is intentionally copied here.
+ DCHECK(streaming_dispatcher_);
+ streaming_dispatcher_->StartStreamingSession(streaming_init_info_.value());
}
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/browser/playback_command_dispatcher.h b/chromium/components/cast_streaming/browser/playback_command_dispatcher.h
index 4a4786d83ce..849eab17d91 100644
--- a/chromium/components/cast_streaming/browser/playback_command_dispatcher.h
+++ b/chromium/components/cast_streaming/browser/playback_command_dispatcher.h
@@ -7,15 +7,21 @@
#include <memory>
+#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "components/cast_streaming/browser/remoting_session_client.h"
#include "components/cast_streaming/browser/renderer_control_multiplexer.h"
+#include "components/cast_streaming/browser/rpc_demuxer_stream_handler.h"
#include "components/cast_streaming/browser/rpc_initialization_call_handler_base.h"
+#include "components/cast_streaming/browser/streaming_initialization_info.h"
#include "components/cast_streaming/public/mojom/renderer_controller.mojom.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/video_decoder_config.h"
#include "media/mojo/mojom/renderer.mojom.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "third_party/openscreen/src/cast/streaming/receiver_session.h"
#include "third_party/openscreen/src/cast/streaming/rpc_messenger.h"
namespace openscreen {
@@ -39,7 +45,8 @@ class RendererRpcCallTranslator;
// also used for starting playback of a Cast Mirroring session.
class PlaybackCommandDispatcher
: public remoting::RpcInitializationCallHandlerBase,
- public remoting::RemotingSessionClient {
+ public remoting::RemotingSessionClient,
+ public remoting::RpcDemuxerStreamHandler::Client {
public:
PlaybackCommandDispatcher(
scoped_refptr<base::SequencedTaskRunner> task_runner,
@@ -52,6 +59,11 @@ class PlaybackCommandDispatcher
// remoting::RemotingSessionClient overrides.
void OnRemotingSessionNegotiated(
openscreen::cast::RpcMessenger* messenger) override;
+ void ConfigureRemotingAsync(
+ Dispatcher* dispatcher,
+ const openscreen::cast::ReceiverSession* session,
+ openscreen::cast::ReceiverSession::ConfiguredReceivers receivers)
+ override;
void OnRemotingSessionEnded() override;
private:
@@ -61,27 +73,58 @@ class PlaybackCommandDispatcher
void ProcessRemotingRpcMessageFromRemote(
std::unique_ptr<openscreen::cast::RpcMessage> message);
+ // Acquires a new handle from |messenger_|.
+ openscreen::cast::RpcMessenger::Handle AcquireHandle();
+
+ // Registers a |handle| with |messenger_| to receive callbacks to
+ // ProcessRemotingRpcMessageFromRemote().
+ void RegisterHandleForCallbacks(
+ openscreen::cast::RpcMessenger::Handle handle);
+
+ // Starts streaming if each expected audio or video config has been received.
+ void MaybeStartStreamingSession();
+
// Callback for mojom::RendererController::SetPlaybackController() call.
void OnSetPlaybackControllerDone();
// RpcInitializationCallHandlerBase overrides.
void RpcAcquireRendererAsync(AcquireRendererCB cb) override;
- void OnRpcAcquireDemuxer(int audio_stream_handle,
- int video_stream_handle) override;
+ void OnRpcAcquireDemuxer(
+ openscreen::cast::RpcMessenger::Handle audio_stream_handle,
+ openscreen::cast::RpcMessenger::Handle video_stream_handle) override;
+
+ // RpcDemuxerStreamHandler::Client overrides.
+ void OnNewAudioConfig(media::AudioDecoderConfig new_config) override;
+ void OnNewVideoConfig(media::VideoDecoderConfig new_config) override;
// Synchronization for calling |acquire_renderer_cb_| at the correct time.
bool has_set_playback_controller_call_returned_ = false;
base::OnceCallback<void()> acquire_renderer_cb_;
- openscreen::cast::RpcMessenger* messenger_;
- openscreen::cast::RpcMessenger::Handle handle_;
+ raw_ptr<openscreen::cast::RpcMessenger> messenger_;
// Multiplexes Renderer commands from a number of senders.
std::unique_ptr<RendererControlMultiplexer> muxer_;
// Handles translating between Remoting commands (in proto form) and mojo
// commands.
- std::unique_ptr<remoting::RendererRpcCallTranslator> call_translator_;
+ std::unique_ptr<remoting::RendererRpcCallTranslator>
+ renderer_call_translator_;
+
+ // Handles DemuxerStream interactions.
+ std::unique_ptr<remoting::RpcDemuxerStreamHandler> demuxer_stream_handler_;
+
+ // Handles for the demuxer stream data providers, to be used for dispatching
+ // demuxer stream RPC commands.
+ absl::optional<StreamingInitializationInfo> streaming_init_info_;
+ raw_ptr<Dispatcher> streaming_dispatcher_ = nullptr;
+ raw_ptr<const openscreen::cast::ReceiverSession> receiver_session_ = nullptr;
+
+ // The mojo API used to configure the renderer controls in the renderer
+ // process. Although this instance is only needed once, it is stored as an
+ // instance variable so that the destruction of this instance is visible to
+ // the Renderer process via the mojo disconnection handler.
+ mojo::AssociatedRemote<mojom::RendererController> control_configuration_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
base::WeakPtrFactory<PlaybackCommandDispatcher> weak_factory_{this};
diff --git a/chromium/components/cast_streaming/browser/public/receiver_session.h b/chromium/components/cast_streaming/browser/public/receiver_session.h
index 4b03a3bd7e0..77b0b93af1e 100644
--- a/chromium/components/cast_streaming/browser/public/receiver_session.h
+++ b/chromium/components/cast_streaming/browser/public/receiver_session.h
@@ -9,7 +9,7 @@
#include "base/callback.h"
#include "base/time/time.h"
-#include "components/cast_streaming/public/mojom/cast_streaming_session.mojom.h"
+#include "components/cast_streaming/public/mojom/demuxer_connector.mojom.h"
#include "components/cast_streaming/public/mojom/renderer_controller.mojom.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "third_party/openscreen/src/cast/streaming/receiver_session.h"
@@ -26,7 +26,7 @@ class VideoDecoderConfig;
namespace cast_streaming {
// This interface handles a single Cast Streaming Receiver Session over a given
-// |message_port| and with a given |cast_streaming_receiver|. On destruction,
+// |message_port| and with a given |demuxer_connector|. On destruction,
// the Cast Streaming Receiver Session will be terminated if it was ever
// started.
// TODO(1220176): Forward declare ReceiverSession::Preferences instead of
@@ -92,15 +92,13 @@ class ReceiverSession {
// |PlaybackCommandForwardingRenderer| is being used, the below overload is
// recommended instead.
virtual void StartStreamingAsync(
- mojo::AssociatedRemote<mojom::CastStreamingReceiver>
- cast_streaming_receiver) = 0;
+ mojo::AssociatedRemote<mojom::DemuxerConnector> demuxer_connector) = 0;
// As above, but also sets the |renderer_controller| to be used to control a
// renderer-process |PlaybackCommandForwardingRenderer|. This control may then
// be done through the RenderControls returned by GetRendererControls() below.
virtual void StartStreamingAsync(
- mojo::AssociatedRemote<mojom::CastStreamingReceiver>
- cast_streaming_receiver,
+ mojo::AssociatedRemote<mojom::DemuxerConnector> demuxer_connector,
mojo::AssociatedRemote<mojom::RendererController>
renderer_controller) = 0;
diff --git a/chromium/components/cast_streaming/browser/receiver_session_impl.cc b/chromium/components/cast_streaming/browser/receiver_session_impl.cc
index 63074a0f932..a09ddfc08c2 100644
--- a/chromium/components/cast_streaming/browser/receiver_session_impl.cc
+++ b/chromium/components/cast_streaming/browser/receiver_session_impl.cc
@@ -36,22 +36,20 @@ ReceiverSessionImpl::ReceiverSessionImpl(
ReceiverSessionImpl::~ReceiverSessionImpl() = default;
void ReceiverSessionImpl::StartStreamingAsync(
- mojo::AssociatedRemote<mojom::CastStreamingReceiver>
- cast_streaming_receiver) {
+ mojo::AssociatedRemote<mojom::DemuxerConnector> demuxer_connector) {
DCHECK(HasNetworkContextGetter());
DVLOG(1) << __func__;
- cast_streaming_receiver_ = std::move(cast_streaming_receiver);
+ demuxer_connector_ = std::move(demuxer_connector);
- cast_streaming_receiver_->EnableReceiver(base::BindOnce(
+ demuxer_connector_->EnableReceiver(base::BindOnce(
&ReceiverSessionImpl::OnReceiverEnabled, weak_factory_.GetWeakPtr()));
- cast_streaming_receiver_.set_disconnect_handler(base::BindOnce(
+ demuxer_connector_.set_disconnect_handler(base::BindOnce(
&ReceiverSessionImpl::OnMojoDisconnect, weak_factory_.GetWeakPtr()));
}
void ReceiverSessionImpl::StartStreamingAsync(
- mojo::AssociatedRemote<mojom::CastStreamingReceiver>
- cast_streaming_receiver,
+ mojo::AssociatedRemote<mojom::DemuxerConnector> demuxer_connector,
mojo::AssociatedRemote<mojom::RendererController> renderer_controller) {
DCHECK(!renderer_control_config_);
external_renderer_controls_ =
@@ -60,7 +58,7 @@ void ReceiverSessionImpl::StartStreamingAsync(
renderer_control_config_.emplace(std::move(renderer_controller),
external_renderer_controls_->Bind());
- StartStreamingAsync(std::move(cast_streaming_receiver));
+ StartStreamingAsync(std::move(demuxer_connector));
}
ReceiverSession::RendererController*
@@ -79,15 +77,16 @@ void ReceiverSessionImpl::OnReceiverEnabled() {
}
void ReceiverSessionImpl::OnSessionInitialization(
- absl::optional<cast_streaming::CastStreamingSession::AudioStreamInfo>
- audio_stream_info,
- absl::optional<cast_streaming::CastStreamingSession::VideoStreamInfo>
- video_stream_info) {
+ StreamingInitializationInfo initialization_info,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> audio_pipe_consumer,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> video_pipe_consumer) {
DVLOG(1) << __func__;
- DCHECK(audio_stream_info || video_stream_info);
+ DCHECK_EQ(!!initialization_info.audio_stream_info, !!audio_pipe_consumer);
+ DCHECK_EQ(!!initialization_info.video_stream_info, !!video_pipe_consumer);
+ DCHECK(audio_pipe_consumer || video_pipe_consumer);
mojom::AudioStreamInitializationInfoPtr audio_info;
- if (audio_stream_info) {
+ if (audio_pipe_consumer) {
mojo::PendingRemote<mojom::AudioBufferRequester> audio_receiver;
audio_demuxer_stream_data_provider_ =
std::make_unique<AudioDemuxerStreamDataProvider>(
@@ -95,16 +94,18 @@ void ReceiverSessionImpl::OnSessionInitialization(
cast_streaming_session_.GetAudioBufferRequester(),
base::BindOnce(&ReceiverSessionImpl::OnMojoDisconnect,
weak_factory_.GetWeakPtr()),
- std::move(audio_stream_info->decoder_config));
+ std::move(initialization_info.audio_stream_info->config));
+ audio_demuxer_stream_data_provider_->SetClient(std::move(
+ initialization_info.audio_stream_info->demuxer_stream_client));
audio_info = mojom::AudioStreamInitializationInfo::New(
std::move(audio_receiver),
mojom::AudioStreamInfo::New(
audio_demuxer_stream_data_provider_->config(),
- std::move(std::move(audio_stream_info->data_pipe))));
+ std::move(std::move(audio_pipe_consumer.value()))));
}
mojom::VideoStreamInitializationInfoPtr video_info;
- if (video_stream_info) {
+ if (video_pipe_consumer) {
mojo::PendingRemote<mojom::VideoBufferRequester> video_receiver;
video_demuxer_stream_data_provider_ =
std::make_unique<VideoDemuxerStreamDataProvider>(
@@ -112,16 +113,18 @@ void ReceiverSessionImpl::OnSessionInitialization(
cast_streaming_session_.GetVideoBufferRequester(),
base::BindOnce(&ReceiverSessionImpl::OnMojoDisconnect,
weak_factory_.GetWeakPtr()),
- std::move(video_stream_info->decoder_config));
+ std::move(initialization_info.video_stream_info->config));
+ video_demuxer_stream_data_provider_->SetClient(std::move(
+ initialization_info.video_stream_info->demuxer_stream_client));
video_info = mojom::VideoStreamInitializationInfo::New(
std::move(video_receiver),
mojom::VideoStreamInfo::New(
video_demuxer_stream_data_provider_->config(),
- std::move(std::move(video_stream_info->data_pipe))));
+ std::move(std::move(video_pipe_consumer.value()))));
}
- cast_streaming_receiver_->OnStreamsInitialized(std::move(audio_info),
- std::move(video_info));
+ demuxer_connector_->OnStreamsInitialized(std::move(audio_info),
+ std::move(video_info));
InformClientOfConfigChange();
}
@@ -141,25 +144,42 @@ void ReceiverSessionImpl::OnVideoBufferReceived(
}
void ReceiverSessionImpl::OnSessionReinitialization(
- absl::optional<cast_streaming::CastStreamingSession::AudioStreamInfo>
- audio_stream_info,
- absl::optional<cast_streaming::CastStreamingSession::VideoStreamInfo>
- video_stream_info) {
+ StreamingInitializationInfo initialization_info,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> audio_pipe_consumer,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> video_pipe_consumer) {
DVLOG(1) << __func__;
- DCHECK(audio_stream_info || video_stream_info);
- DCHECK_EQ(!!audio_stream_info, !!audio_demuxer_stream_data_provider_);
- DCHECK_EQ(!!video_stream_info, !!video_demuxer_stream_data_provider_);
-
- if (audio_stream_info) {
- audio_demuxer_stream_data_provider_->OnNewStreamInfo(
- std::move(audio_stream_info->decoder_config),
- std::move(audio_stream_info->data_pipe));
+ DCHECK(audio_pipe_consumer || video_pipe_consumer);
+ DCHECK_EQ(!!audio_pipe_consumer, !!initialization_info.audio_stream_info);
+ DCHECK_EQ(!!video_pipe_consumer, !!initialization_info.video_stream_info);
+ DCHECK_EQ(!!audio_pipe_consumer, !!audio_demuxer_stream_data_provider_);
+ DCHECK_EQ(!!video_pipe_consumer, !!video_demuxer_stream_data_provider_);
+
+ if (audio_pipe_consumer) {
+ if (!audio_demuxer_stream_data_provider_->config().Matches(
+ initialization_info.audio_stream_info->config)) {
+ audio_demuxer_stream_data_provider_->SetClient(std::move(
+ initialization_info.audio_stream_info->demuxer_stream_client));
+ audio_demuxer_stream_data_provider_->OnNewStreamInfo(
+ std::move(initialization_info.audio_stream_info->config),
+ std::move(*audio_pipe_consumer));
+ } else {
+ DVLOG(1) << "Skipping application of new AudioDecoderConfig as no "
+ "config parameters have changed";
+ }
}
- if (video_stream_info) {
- video_demuxer_stream_data_provider_->OnNewStreamInfo(
- std::move(video_stream_info->decoder_config),
- std::move(video_stream_info->data_pipe));
+ if (video_pipe_consumer) {
+ if (!video_demuxer_stream_data_provider_->config().Matches(
+ initialization_info.video_stream_info->config)) {
+ video_demuxer_stream_data_provider_->SetClient(std::move(
+ initialization_info.video_stream_info->demuxer_stream_client));
+ video_demuxer_stream_data_provider_->OnNewStreamInfo(
+ std::move(initialization_info.video_stream_info->config),
+ std::move(*video_pipe_consumer));
+ } else {
+ DVLOG(1) << "Skipping application of new VideoDecoderConfig as no "
+ "config parameters have changed";
+ }
}
InformClientOfConfigChange();
@@ -182,7 +202,7 @@ void ReceiverSessionImpl::OnSessionEnded() {
DVLOG(1) << __func__;
// Tear down the Mojo connection.
- cast_streaming_receiver_.reset();
+ demuxer_connector_.reset();
// Tear down all remaining Mojo objects if needed. This is necessary if the
// Cast Streaming Session ending was initiated by the receiver component.
@@ -201,7 +221,9 @@ void ReceiverSessionImpl::OnMojoDisconnect() {
// Close the Cast Streaming Session. OnSessionEnded() will be called as part
// of the Session shutdown, which will tear down the Mojo connection.
- cast_streaming_session_.Stop();
+ if (cast_streaming_session_.is_running()) {
+ cast_streaming_session_.Stop();
+ }
// Tear down all remaining Mojo objects.
audio_demuxer_stream_data_provider_.reset();
diff --git a/chromium/components/cast_streaming/browser/receiver_session_impl.h b/chromium/components/cast_streaming/browser/receiver_session_impl.h
index e7a824b6339..b982e486ff3 100644
--- a/chromium/components/cast_streaming/browser/receiver_session_impl.h
+++ b/chromium/components/cast_streaming/browser/receiver_session_impl.h
@@ -10,7 +10,7 @@
#include "components/cast_streaming/browser/cast_streaming_session.h"
#include "components/cast_streaming/browser/demuxer_stream_data_provider.h"
#include "components/cast_streaming/browser/public/receiver_session.h"
-#include "components/cast_streaming/public/mojom/cast_streaming_session.mojom.h"
+#include "components/cast_streaming/public/mojom/demuxer_connector.mojom.h"
#include "components/cast_streaming/public/mojom/renderer_controller.mojom.h"
#include "media/mojo/mojom/media_types.mojom.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
@@ -37,12 +37,12 @@ class ReceiverSessionImpl final
ReceiverSessionImpl& operator=(const ReceiverSessionImpl&) = delete;
// ReceiverSession implementation.
- void StartStreamingAsync(mojo::AssociatedRemote<mojom::CastStreamingReceiver>
- cast_streaming_receiver) override;
- void StartStreamingAsync(mojo::AssociatedRemote<mojom::CastStreamingReceiver>
- cast_streaming_receiver,
- mojo::AssociatedRemote<mojom::RendererController>
- renderer_controller) override;
+ void StartStreamingAsync(mojo::AssociatedRemote<mojom::DemuxerConnector>
+ demuxer_connector) override;
+ void StartStreamingAsync(
+ mojo::AssociatedRemote<mojom::DemuxerConnector> demuxer_connector,
+ mojo::AssociatedRemote<mojom::RendererController> renderer_controller)
+ override;
RendererController* GetRendererControls() override;
private:
@@ -66,10 +66,10 @@ class ReceiverSessionImpl final
mojo::Remote<media::mojom::Renderer> renderer_controls_;
};
- // Handler for |cast_streaming_receiver_| disconnect.
+ // Handler for |demuxer_connector_| disconnect.
void OnMojoDisconnect();
- // Callback for mojom::CastStreamingReceiver::EnableReceiver()
+ // Callback for mojom::DemuxerConnector::EnableReceiver()
void OnReceiverEnabled();
// Informs the client of updated configs.
@@ -77,17 +77,17 @@ class ReceiverSessionImpl final
// cast_streaming::CastStreamingSession::Client implementation.
void OnSessionInitialization(
- absl::optional<cast_streaming::CastStreamingSession::AudioStreamInfo>
- audio_stream_info,
- absl::optional<cast_streaming::CastStreamingSession::VideoStreamInfo>
- video_stream_info) override;
+ StreamingInitializationInfo initialization_info,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> audio_pipe_consumer,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> video_pipe_consumer)
+ override;
void OnAudioBufferReceived(media::mojom::DecoderBufferPtr buffer) override;
void OnVideoBufferReceived(media::mojom::DecoderBufferPtr buffer) override;
void OnSessionReinitialization(
- absl::optional<cast_streaming::CastStreamingSession::AudioStreamInfo>
- audio_stream_info,
- absl::optional<cast_streaming::CastStreamingSession::VideoStreamInfo>
- video_stream_info) override;
+ StreamingInitializationInfo initialization_info,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> audio_pipe_consumer,
+ absl::optional<mojo::ScopedDataPipeConsumerHandle> video_pipe_consumer)
+ override;
void OnSessionEnded() override;
// Populated in the ctor, and empty following a call to either
@@ -95,7 +95,7 @@ class ReceiverSessionImpl final
MessagePortProvider message_port_provider_;
std::unique_ptr<ReceiverSession::AVConstraints> av_constraints_;
- mojo::AssociatedRemote<mojom::CastStreamingReceiver> cast_streaming_receiver_;
+ mojo::AssociatedRemote<mojom::DemuxerConnector> demuxer_connector_;
cast_streaming::CastStreamingSession cast_streaming_session_;
std::unique_ptr<AudioDemuxerStreamDataProvider>
diff --git a/chromium/components/cast_streaming/browser/remoting_session_client.h b/chromium/components/cast_streaming/browser/remoting_session_client.h
index 2de17c9a4c0..7599ee18d52 100644
--- a/chromium/components/cast_streaming/browser/remoting_session_client.h
+++ b/chromium/components/cast_streaming/browser/remoting_session_client.h
@@ -5,32 +5,51 @@
#ifndef COMPONENTS_CAST_STREAMING_BROWSER_REMOTING_SESSION_CLIENT_H_
#define COMPONENTS_CAST_STREAMING_BROWSER_REMOTING_SESSION_CLIENT_H_
-namespace openscreen {
-namespace cast {
+#include "components/cast_streaming/browser/streaming_initialization_info.h"
+#include "third_party/openscreen/src/cast/streaming/receiver_session.h"
+
+namespace openscreen::cast {
class RpcMessenger;
-} // namespace cast
-} // namespace openscreen
+} // namespace openscreen::cast
-namespace cast_streaming {
-namespace remoting {
+namespace cast_streaming::remoting {
// This class provides an interface for management of a remoting session's
// lifetime events.
class RemotingSessionClient {
public:
+ // This class provides a way for a RemotingSessionClient to start a new
+ // streaming session.
+ class Dispatcher {
+ public:
+ virtual ~Dispatcher() = default;
+
+ // Starts a new streaming session with configuration as dictated by
+ // |initialization_info|.
+ virtual void StartStreamingSession(
+ StreamingInitializationInfo initialization_info) = 0;
+ };
+
virtual ~RemotingSessionClient() = default;
// Called when a new remoting session is negotiated. |messenger| is the
- // RpcMessenger assocaited with this session, and is expected to remain valid
+ // RpcMessenger associated with this session, and is expected to remain valid
// until either OnRemotingSessionEnded() or this method are called.
virtual void OnRemotingSessionNegotiated(
openscreen::cast::RpcMessenger* messenger) = 0;
+ // Configures the remoting session using these parameters and upcoming RPC
+ // calls received from Openscreen. Will eventually call |dispatcher|'s
+ // StartStreamingSession().
+ virtual void ConfigureRemotingAsync(
+ Dispatcher* dispatcher,
+ const openscreen::cast::ReceiverSession* session,
+ openscreen::cast::ReceiverSession::ConfiguredReceivers receivers) = 0;
+
// Called when a remoting session ends.
virtual void OnRemotingSessionEnded() = 0;
};
-} // namespace remoting
-} // namespace cast_streaming
+} // namespace cast_streaming::remoting
#endif // COMPONENTS_CAST_STREAMING_BROWSER_REMOTING_SESSION_CLIENT_H_
diff --git a/chromium/components/cast_streaming/browser/renderer_rpc_call_translator.cc b/chromium/components/cast_streaming/browser/renderer_rpc_call_translator.cc
index 976ce4db6ee..d31a81b207c 100644
--- a/chromium/components/cast_streaming/browser/renderer_rpc_call_translator.cc
+++ b/chromium/components/cast_streaming/browser/renderer_rpc_call_translator.cc
@@ -10,100 +10,103 @@
namespace cast_streaming::remoting {
RendererRpcCallTranslator::RendererRpcCallTranslator(
- mojo::Remote<media::mojom::Renderer> remote_renderer)
- : renderer_client_receiver_(this),
- renderer_remote_(std::move(remote_renderer)),
+ RpcMessageProcessor processor,
+ media::mojom::Renderer* renderer)
+ : message_processor_(std::move(processor)),
+ renderer_client_receiver_(this),
+ renderer_(std::move(renderer)),
weak_factory_(this) {}
RendererRpcCallTranslator::~RendererRpcCallTranslator() = default;
-void RendererRpcCallTranslator::SetMessageProcessor(
- RpcMessageProcessor processor) {
- message_processor_ = std::move(processor);
-}
-
void RendererRpcCallTranslator::OnRpcInitialize() {
- renderer_remote_->Initialize(
- renderer_client_receiver_.BindNewEndpointAndPassRemote(),
- /* streams */ {}, /* media_url_params */ nullptr,
- base::BindOnce(&RendererRpcCallTranslator::OnInitializeCompleted,
- weak_factory_.GetWeakPtr(), message_processor_));
+ if (!has_been_initialized_) {
+ has_been_initialized_ = true;
+ renderer_->Initialize(
+ renderer_client_receiver_.BindNewEndpointAndPassRemote(),
+ /* streams */ {}, /* media_url_params */ nullptr,
+ base::BindOnce(&RendererRpcCallTranslator::OnInitializeCompleted,
+ weak_factory_.GetWeakPtr(), handle_));
+ } else {
+ OnInitializeCompleted(handle_, true);
+ }
}
void RendererRpcCallTranslator::OnRpcFlush(uint32_t audio_count,
uint32_t video_count) {
- renderer_remote_->Flush(
- base::BindOnce(&RendererRpcCallTranslator::OnFlushCompleted,
- weak_factory_.GetWeakPtr(), message_processor_));
+ renderer_->Flush(base::BindOnce(&RendererRpcCallTranslator::OnFlushCompleted,
+ weak_factory_.GetWeakPtr(), handle_));
}
void RendererRpcCallTranslator::OnRpcStartPlayingFrom(base::TimeDelta time) {
- renderer_remote_->StartPlayingFrom(time);
+ renderer_->StartPlayingFrom(time);
}
void RendererRpcCallTranslator::OnRpcSetPlaybackRate(double playback_rate) {
- renderer_remote_->SetPlaybackRate(playback_rate);
+ renderer_->SetPlaybackRate(playback_rate);
}
void RendererRpcCallTranslator::OnRpcSetVolume(double volume) {
- renderer_remote_->SetVolume(volume);
+ renderer_->SetVolume(volume);
}
void RendererRpcCallTranslator::OnTimeUpdate(base::TimeDelta media_time,
base::TimeDelta max_time,
base::TimeTicks capture_time) {
- message_processor_.Run(CreateMessageForMediaTimeUpdate(media_time));
+ message_processor_.Run(handle_, CreateMessageForMediaTimeUpdate(media_time));
}
void RendererRpcCallTranslator::OnBufferingStateChange(
media::BufferingState state,
media::BufferingStateChangeReason reason) {
- message_processor_.Run(CreateMessageForBufferingStateChange(state));
+ message_processor_.Run(handle_, CreateMessageForBufferingStateChange(state));
}
void RendererRpcCallTranslator::OnError(const media::PipelineStatus& status) {
- message_processor_.Run(CreateMessageForError());
+ message_processor_.Run(handle_, CreateMessageForError());
}
void RendererRpcCallTranslator::OnEnded() {
- message_processor_.Run(CreateMessageForMediaEnded());
+ message_processor_.Run(handle_, CreateMessageForMediaEnded());
}
void RendererRpcCallTranslator::OnAudioConfigChange(
const media::AudioDecoderConfig& config) {
- message_processor_.Run(CreateMessageForAudioConfigChange(config));
+ message_processor_.Run(handle_, CreateMessageForAudioConfigChange(config));
}
void RendererRpcCallTranslator::OnVideoConfigChange(
const media::VideoDecoderConfig& config) {
- message_processor_.Run(CreateMessageForVideoConfigChange(config));
+ message_processor_.Run(handle_, CreateMessageForVideoConfigChange(config));
}
void RendererRpcCallTranslator::OnVideoNaturalSizeChange(
const gfx::Size& size) {
- message_processor_.Run(CreateMessageForVideoNaturalSizeChange(size));
+ message_processor_.Run(handle_, CreateMessageForVideoNaturalSizeChange(size));
}
void RendererRpcCallTranslator::OnVideoOpacityChange(bool opaque) {
- message_processor_.Run(CreateMessageForVideoOpacityChange(opaque));
+ message_processor_.Run(handle_, CreateMessageForVideoOpacityChange(opaque));
}
void RendererRpcCallTranslator::OnStatisticsUpdate(
const media::PipelineStatistics& stats) {
- message_processor_.Run(CreateMessageForStatisticsUpdate(stats));
+ message_processor_.Run(handle_, CreateMessageForStatisticsUpdate(stats));
}
void RendererRpcCallTranslator::OnWaiting(media::WaitingReason reason) {}
void RendererRpcCallTranslator::OnInitializeCompleted(
- RpcMessageProcessor processor,
+ openscreen::cast::RpcMessenger::Handle handle_at_time_of_sending,
bool success) {
- message_processor_.Run(CreateMessageForInitializationComplete(success));
+ message_processor_.Run(handle_at_time_of_sending,
+ CreateMessageForInitializationComplete(success));
}
void RendererRpcCallTranslator::OnFlushCompleted(
- RpcMessageProcessor processor) {
- message_processor_.Run(CreateMessageForFlushComplete());
+ openscreen::cast::RpcMessenger::Handle handle_at_time_of_sending) {
+ message_processor_.Run(handle_at_time_of_sending,
+ CreateMessageForFlushComplete());
}
} // namespace cast_streaming::remoting
diff --git a/chromium/components/cast_streaming/browser/renderer_rpc_call_translator.h b/chromium/components/cast_streaming/browser/renderer_rpc_call_translator.h
index fc26a806781..e0725bd3e36 100644
--- a/chromium/components/cast_streaming/browser/renderer_rpc_call_translator.h
+++ b/chromium/components/cast_streaming/browser/renderer_rpc_call_translator.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/cast_streaming/public/rpc_call_message_handler.h"
#include "media/base/renderer.h"
@@ -28,18 +29,22 @@ class RendererRpcCallTranslator : public media::mojom::RendererClient,
public RpcRendererCallMessageHandler {
public:
using RpcMessageProcessor = base::RepeatingCallback<void(
+ openscreen::cast::RpcMessenger::Handle handle,
std::unique_ptr<openscreen::cast::RpcMessage>)>;
- // |remote_renderer| is the remote media::mojom::Renderer to which commands
+ // |renderer| is the remote media::mojom::Renderer to which commands
// translated from proto messages should be sent.
- explicit RendererRpcCallTranslator(
- mojo::Remote<media::mojom::Renderer> remote_renderer);
+ // |processor| is responsible for handling any proto messages ready to be sent
+ // out.
+ explicit RendererRpcCallTranslator(RpcMessageProcessor processor,
+ media::mojom::Renderer* renderer);
~RendererRpcCallTranslator() override;
- // |processor| is responsible for handling any proto messages ready to be sent
- // out. This callback is expected to set the handle in each incoming message.
- // This callback must be callable from any thread.
- void SetMessageProcessor(RpcMessageProcessor processor);
+ // Sets the |handle| to be used for future outgoing RPC calls.
+ void set_handle(openscreen::cast::RpcMessenger::Handle handle) {
+ handle_ = handle;
+ }
+ openscreen::cast::RpcMessenger::Handle handle() const { return handle_; }
private:
// media::mojom::RendererClient overrides.
@@ -65,17 +70,27 @@ class RendererRpcCallTranslator : public media::mojom::RendererClient,
void OnRpcSetPlaybackRate(double playback_rate) override;
void OnRpcSetVolume(double volume) override;
- // Callbacks for mojo calls. |processor| is included as an input so that if
- // the callback changes before the response to this message is returned, it
- // will send with the old |message_processor_| value.
- void OnInitializeCompleted(RpcMessageProcessor processor, bool succeeded);
- void OnFlushCompleted(RpcMessageProcessor processor);
+ // Callbacks for mojo calls. |handle_at_time_of_sending| is included as an
+ // input so that if |handle_| changes before the response to this message is
+ // returned, it will send with the old |handle_| value.
+ void OnInitializeCompleted(
+ openscreen::cast::RpcMessenger::Handle handle_at_time_of_sending,
+ bool succeeded);
+ void OnFlushCompleted(
+ openscreen::cast::RpcMessenger::Handle handle_at_time_of_sending);
+
+ // Signifies whether the Initialize() command has been sent to the Renderer,
+ // which will only be done once over the duration of this instance's lifetime.
+ bool has_been_initialized_ = false;
RpcMessageProcessor message_processor_;
mojo::AssociatedReceiver<media::mojom::RendererClient>
renderer_client_receiver_;
- mojo::Remote<media::mojom::Renderer> renderer_remote_;
+ raw_ptr<media::mojom::Renderer> renderer_;
+
+ openscreen::cast::RpcMessenger::Handle handle_ =
+ openscreen::cast::RpcMessenger::kInvalidHandle;
base::WeakPtrFactory<RendererRpcCallTranslator> weak_factory_;
};
diff --git a/chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler.cc b/chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler.cc
new file mode 100644
index 00000000000..4aba9444e57
--- /dev/null
+++ b/chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler.cc
@@ -0,0 +1,224 @@
+// Copyright 2022 The Chromium 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/cast_streaming/browser/rpc_demuxer_stream_handler.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "components/cast_streaming/public/remoting_message_factories.h"
+#include "third_party/openscreen/src/cast/streaming/remoting.pb.h"
+
+namespace cast_streaming::remoting {
+namespace {
+
+// The number of frames requested in each ReadUntil RPC message.
+constexpr int kNumFramesInEachReadUntil = 16;
+
+} // namespace
+
+RpcDemuxerStreamHandler::RpcDemuxerStreamHandler(
+ Client* client,
+ HandleFactory handle_factory,
+ RpcProcessMessageCB process_message_cb)
+ : client_(client),
+ handle_factory_(std::move(handle_factory)),
+ process_message_cb_(std::move(process_message_cb)) {
+ DCHECK(handle_factory_);
+ DCHECK(process_message_cb_);
+}
+
+RpcDemuxerStreamHandler::~RpcDemuxerStreamHandler() = default;
+
+void RpcDemuxerStreamHandler::OnRpcAcquireDemuxer(
+ openscreen::cast::RpcMessenger::Handle audio_stream_handle,
+ openscreen::cast::RpcMessenger::Handle video_stream_handle) {
+ // Initialization of the Demuxer happens automatically, so immediately
+ // initialize the DemuxerStreams.
+ if (audio_stream_handle != openscreen::cast::RpcMessenger::kInvalidHandle) {
+ audio_message_processor_ = std::make_unique<MessageProcessor>(
+ client_, process_message_cb_, handle_factory_.Run(),
+ audio_stream_handle, MessageProcessor::Type::kAudio);
+ std::unique_ptr<openscreen::cast::RpcMessage> message =
+ remoting::CreateMessageForDemuxerStreamInitialize(
+ audio_message_processor_->local_handle());
+ process_message_cb_.Run(audio_message_processor_->remote_handle(),
+ std::move(message));
+ }
+
+ if (video_stream_handle != openscreen::cast::RpcMessenger::kInvalidHandle) {
+ video_message_processor_ = std::make_unique<MessageProcessor>(
+ client_, process_message_cb_, handle_factory_.Run(),
+ video_stream_handle, MessageProcessor::Type::kVideo);
+ std::unique_ptr<openscreen::cast::RpcMessage> message =
+ remoting::CreateMessageForDemuxerStreamInitialize(
+ video_message_processor_->local_handle());
+ process_message_cb_.Run(video_message_processor_->remote_handle(),
+ std::move(message));
+ }
+}
+
+void RpcDemuxerStreamHandler::OnRpcInitializeCallback(
+ openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config) {
+ if (audio_message_processor_ &&
+ handle == audio_message_processor_->local_handle()) {
+ audio_message_processor_->OnRpcInitializeCallback(std::move(audio_config),
+ std::move(video_config));
+ } else if (video_message_processor_ &&
+ handle == video_message_processor_->local_handle()) {
+ video_message_processor_->OnRpcInitializeCallback(std::move(audio_config),
+ std::move(video_config));
+ } else {
+ LOG(WARNING) << "OnRpcInitializeCallback received for invalid handle";
+ }
+}
+
+void RpcDemuxerStreamHandler::OnRpcReadUntilCallback(
+ openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config,
+ uint32_t total_frames_received) {
+ if (audio_message_processor_ &&
+ handle == audio_message_processor_->local_handle()) {
+ audio_message_processor_->OnRpcReadUntilCallback(std::move(audio_config),
+ std::move(video_config),
+ total_frames_received);
+ } else if (video_message_processor_ &&
+ handle == video_message_processor_->local_handle()) {
+ video_message_processor_->OnRpcReadUntilCallback(std::move(audio_config),
+ std::move(video_config),
+ total_frames_received);
+ } else {
+ LOG(WARNING) << "OnRpcReadUntilCallback received for invalid handle";
+ }
+}
+
+void RpcDemuxerStreamHandler::OnRpcBitstreamConverterEnabled(
+ openscreen::cast::RpcMessenger::Handle handle,
+ bool success) {
+ if (audio_message_processor_ &&
+ handle == audio_message_processor_->local_handle()) {
+ audio_message_processor_->OnBitstreamConverterEnabled(success);
+ } else if (video_message_processor_ &&
+ handle == video_message_processor_->local_handle()) {
+ video_message_processor_->OnBitstreamConverterEnabled(success);
+ } else {
+ LOG(WARNING)
+ << "OnRpcBitstreamConverterEnabled received for invalid handle";
+ }
+}
+
+base::WeakPtr<DemuxerStreamClient> RpcDemuxerStreamHandler::GetAudioClient() {
+ if (!audio_message_processor_) {
+ return nullptr;
+ }
+
+ return audio_message_processor_->GetWeakPtr();
+}
+
+base::WeakPtr<DemuxerStreamClient> RpcDemuxerStreamHandler::GetVideoClient() {
+ if (!video_message_processor_) {
+ return nullptr;
+ }
+
+ return video_message_processor_->GetWeakPtr();
+}
+
+RpcDemuxerStreamHandler::Client::~Client() = default;
+
+RpcDemuxerStreamHandler::MessageProcessor::MessageProcessor(
+ Client* client,
+ RpcProcessMessageCB process_message_cb,
+ openscreen::cast::RpcMessenger::Handle local_handle,
+ openscreen::cast::RpcMessenger::Handle remote_handle,
+ Type type)
+ : client_(client),
+ process_message_cb_(std::move(process_message_cb)),
+ local_handle_(local_handle),
+ remote_handle_(remote_handle),
+ type_(type),
+ weak_factory_(this) {
+ DCHECK(client_);
+ DCHECK_NE(local_handle_, openscreen::cast::RpcMessenger::kInvalidHandle);
+ DCHECK_NE(remote_handle_, openscreen::cast::RpcMessenger::kInvalidHandle);
+}
+
+RpcDemuxerStreamHandler::MessageProcessor::~MessageProcessor() = default;
+
+bool RpcDemuxerStreamHandler::MessageProcessor::OnRpcInitializeCallback(
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config) {
+ if (audio_config && type_ != Type::kAudio) {
+ LOG(WARNING) << "Received an audio config for a video DemuxerStream";
+ return false;
+ } else if (video_config && type_ != Type::kVideo) {
+ LOG(WARNING) << "Received a video config for an audio DemuxerStream";
+ return false;
+ }
+
+ if (audio_config) {
+ client_->OnNewAudioConfig(std::move(audio_config.value()));
+ } else if (video_config) {
+ client_->OnNewVideoConfig(std::move(video_config.value()));
+ }
+
+ return true;
+}
+
+bool RpcDemuxerStreamHandler::MessageProcessor::OnRpcReadUntilCallback(
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config,
+ uint32_t total_frames_received) {
+ if (!OnRpcInitializeCallback(std::move(audio_config),
+ std::move(video_config))) {
+ LOG(WARNING) << "Failed to process OnRpcReadUntilCallback.";
+ return false;
+ }
+
+ total_frames_received_ = total_frames_received;
+ is_read_until_call_pending_ = false;
+ return true;
+}
+
+void RpcDemuxerStreamHandler::MessageProcessor::OnBitstreamConverterEnabled(
+ bool success) {
+ if (!bitstream_converter_enabled_cb_) {
+ return;
+ }
+
+ std::move(bitstream_converter_enabled_cb_).Run(success);
+}
+
+base::WeakPtr<RpcDemuxerStreamHandler::MessageProcessor>
+RpcDemuxerStreamHandler::MessageProcessor::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+void RpcDemuxerStreamHandler::MessageProcessor::EnableBitstreamConverter(
+ BitstreamConverterEnabledCB cb) {
+ DCHECK(!bitstream_converter_enabled_cb_);
+ bitstream_converter_enabled_cb_ = std::move(cb);
+
+ auto message = CreateMessageForDemuxerStreamEnableBitstreamConverter();
+ process_message_cb_.Run(remote_handle(), std::move(message));
+}
+
+void RpcDemuxerStreamHandler::MessageProcessor::OnNoBuffersAvailable() {
+ if (is_read_until_call_pending()) {
+ return;
+ }
+
+ set_read_until_call_pending();
+ auto message = CreateMessageForDemuxerStreamReadUntil(
+ local_handle(), total_frames_received() + kNumFramesInEachReadUntil);
+ process_message_cb_.Run(remote_handle(), std::move(message));
+}
+
+void RpcDemuxerStreamHandler::MessageProcessor::OnError() {
+ auto message = CreateMessageForDemuxerStreamError();
+ process_message_cb_.Run(remote_handle(), std::move(message));
+}
+
+} // namespace cast_streaming::remoting
diff --git a/chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler.h b/chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler.h
new file mode 100644
index 00000000000..e27f35e6914
--- /dev/null
+++ b/chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler.h
@@ -0,0 +1,159 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_CAST_STREAMING_BROWSER_RPC_DEMUXER_STREAM_HANDLER_H_
+#define COMPONENTS_CAST_STREAMING_BROWSER_RPC_DEMUXER_STREAM_HANDLER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/cast_streaming/browser/demuxer_stream_client.h"
+#include "components/cast_streaming/public/rpc_call_message_handler.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/video_decoder_config.h"
+#include "third_party/openscreen/src/cast/streaming/rpc_messenger.h"
+
+namespace openscreen::cast {
+class RpcMessage;
+} // namespace openscreen::cast
+
+namespace cast_streaming::remoting {
+
+// Wrapper around all RPC operations associated with a DemuxerStream. This one
+// instance handles interactions with both audio and video DemuxerStream
+// instances.
+class RpcDemuxerStreamHandler : public RpcDemuxerStreamCBMessageHandler {
+ public:
+ // Class responsible for handling callbacks from this class upon a change in
+ // config.
+ class Client {
+ public:
+ virtual ~Client();
+
+ virtual void OnNewAudioConfig(media::AudioDecoderConfig new_config) = 0;
+ virtual void OnNewVideoConfig(media::VideoDecoderConfig new_config) = 0;
+ };
+
+ // Creates a new instance of this class. |client| is expected to outlive this
+ // class.
+ using HandleFactory =
+ base::RepeatingCallback<openscreen::cast::RpcMessenger::Handle()>;
+ using RpcProcessMessageCB = base::RepeatingCallback<void(
+ openscreen::cast::RpcMessenger::Handle,
+ std::unique_ptr<openscreen::cast::RpcMessage>)>;
+ RpcDemuxerStreamHandler(Client* client,
+ HandleFactory handle_factory,
+ RpcProcessMessageCB process_message_cb);
+
+ ~RpcDemuxerStreamHandler() override;
+
+ // To be called when the RPC_ACQUIRE_DEMUXER message is received. Acts to
+ // immediately create and send an RPC message for initialization of each
+ // valid demuxer stream.
+ void OnRpcAcquireDemuxer(
+ openscreen::cast::RpcMessenger::Handle audio_stream_handle,
+ openscreen::cast::RpcMessenger::Handle video_stream_handle);
+
+ // To be called when the RPC_DS_ENABLEBITSTREAMCONVERTER_CALLBACK message is
+ // received.
+ void OnRpcBitstreamConverterEnabled(
+ openscreen::cast::RpcMessenger::Handle handle,
+ bool success);
+
+ base::WeakPtr<DemuxerStreamClient> GetAudioClient();
+ base::WeakPtr<DemuxerStreamClient> GetVideoClient();
+
+ private:
+ class MessageProcessor : public DemuxerStreamClient {
+ public:
+ enum class Type { kUnknown = 0, kAudio, kVideo };
+
+ // Creates a new instance.
+ //
+ // |local_handle| is a new handle created for this instance, and with which
+ // remote messages arriving here will have their handle set.
+ // |remote_handle| is the handle received from the remote sender in the
+ // OnRpcAcquiredDemuxer() call and used as the handle for sending messages
+ // back to the sender.
+ MessageProcessor(Client* client,
+ RpcProcessMessageCB process_message_cb,
+ openscreen::cast::RpcMessenger::Handle local_handle,
+ openscreen::cast::RpcMessenger::Handle remote_handle,
+ Type type);
+ ~MessageProcessor() override;
+
+ bool OnRpcInitializeCallback(
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config);
+ bool OnRpcReadUntilCallback(
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config,
+ uint32_t total_frames_received);
+ void OnBitstreamConverterEnabled(bool success);
+
+ base::WeakPtr<MessageProcessor> GetWeakPtr();
+
+ uint32_t total_frames_received() const { return total_frames_received_; }
+
+ openscreen::cast::RpcMessenger::Handle local_handle() const {
+ return local_handle_;
+ }
+ openscreen::cast::RpcMessenger::Handle remote_handle() const {
+ return remote_handle_;
+ }
+
+ bool is_read_until_call_pending() const {
+ return is_read_until_call_pending_;
+ }
+ void set_read_until_call_pending() { is_read_until_call_pending_ = true; }
+
+ private:
+ void EnableBitstreamConverter(BitstreamConverterEnabledCB cb) override;
+ void OnNoBuffersAvailable() override;
+ void OnError() override;
+
+ raw_ptr<Client> client_;
+ RpcProcessMessageCB process_message_cb_;
+ openscreen::cast::RpcMessenger::Handle local_handle_;
+ openscreen::cast::RpcMessenger::Handle remote_handle_;
+ Type type_ = Type::kUnknown;
+
+ uint32_t total_frames_received_ = 0;
+
+ bool is_read_until_call_pending_ = false;
+
+ // Most recent callback for EnableBitstreamConverter().
+ BitstreamConverterEnabledCB bitstream_converter_enabled_cb_;
+
+ base::WeakPtrFactory<MessageProcessor> weak_factory_;
+ };
+
+ // Helpers for the above methods of the same name.
+ void RequestMoreBuffers(MessageProcessor* message_processor);
+ void OnError(MessageProcessor* message_processor);
+
+ // RpcDemuxerStreamCBMessageHandler overrides.
+ void OnRpcInitializeCallback(
+ openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config) override;
+ void OnRpcReadUntilCallback(
+ openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config,
+ uint32_t total_frames_received) override;
+
+ const raw_ptr<Client> client_;
+ HandleFactory handle_factory_;
+ RpcProcessMessageCB process_message_cb_;
+
+ std::unique_ptr<MessageProcessor> audio_message_processor_;
+ std::unique_ptr<MessageProcessor> video_message_processor_;
+};
+
+} // namespace cast_streaming::remoting
+
+#endif // COMPONENTS_CAST_STREAMING_BROWSER_RPC_DEMUXER_STREAM_HANDLER_H_
diff --git a/chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler_unittests.cc b/chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler_unittests.cc
new file mode 100644
index 00000000000..3e0e57c6ba5
--- /dev/null
+++ b/chromium/components/cast_streaming/browser/rpc_demuxer_stream_handler_unittests.cc
@@ -0,0 +1,342 @@
+// Copyright 2022 The Chromium 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/cast_streaming/browser/rpc_demuxer_stream_handler.h"
+
+#include <memory>
+#include <utility>
+
+#include "media/base/audio_codecs.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/buffering_state.h"
+#include "media/base/channel_layout.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/media_util.h"
+#include "media/base/sample_format.h"
+#include "media/base/video_codecs.h"
+#include "media/base/video_decoder_config.h"
+#include "media/base/video_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/openscreen/src/cast/streaming/rpc_messenger.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace cast_streaming::remoting {
+namespace {
+
+ACTION_P(CheckInitializeDemuxerStream, remote_handle, local_handle) {
+ const openscreen::cast::RpcMessenger::Handle handle = arg0;
+ const std::unique_ptr<openscreen::cast::RpcMessage>& rpc = arg1;
+
+ ASSERT_TRUE(rpc);
+ EXPECT_EQ(rpc->proc(), openscreen::cast::RpcMessage::RPC_DS_INITIALIZE);
+ EXPECT_EQ(handle, remote_handle);
+ ASSERT_TRUE(rpc->has_integer_value());
+ EXPECT_EQ(local_handle, rpc->integer_value());
+}
+
+ACTION_P(CheckReadUntilCall, remote_handle, local_handle, last_total) {
+ const openscreen::cast::RpcMessenger::Handle handle = arg0;
+ const std::unique_ptr<openscreen::cast::RpcMessage>& rpc = arg1;
+
+ ASSERT_TRUE(rpc);
+ EXPECT_EQ(rpc->proc(), openscreen::cast::RpcMessage::RPC_DS_READUNTIL);
+ EXPECT_EQ(handle, remote_handle);
+ ASSERT_TRUE(rpc->has_demuxerstream_readuntil_rpc());
+ const auto& read_until = rpc->demuxerstream_readuntil_rpc();
+ EXPECT_GT(read_until.count(), static_cast<uint32_t>(last_total));
+ EXPECT_EQ(read_until.callback_handle(), local_handle);
+}
+
+ACTION_P(CheckOnErrorCall, remote_handle) {
+ const openscreen::cast::RpcMessenger::Handle handle = arg0;
+ const std::unique_ptr<openscreen::cast::RpcMessage>& rpc = arg1;
+
+ ASSERT_TRUE(rpc);
+ EXPECT_EQ(rpc->proc(), openscreen::cast::RpcMessage::RPC_DS_ONERROR);
+ EXPECT_EQ(handle, remote_handle);
+}
+
+ACTION_P(CheckEnableBistreamConverterCall, remote_handle) {
+ const openscreen::cast::RpcMessenger::Handle handle = arg0;
+ const std::unique_ptr<openscreen::cast::RpcMessage>& rpc = arg1;
+
+ ASSERT_TRUE(rpc);
+ EXPECT_EQ(rpc->proc(),
+ openscreen::cast::RpcMessage::RPC_DS_ENABLEBITSTREAMCONVERTER);
+ EXPECT_EQ(handle, remote_handle);
+}
+
+} // namespace
+
+class RpcDemuxerStreamHandlerTest : public testing::Test {
+ public:
+ RpcDemuxerStreamHandlerTest()
+ : test_audio_config_(media::AudioCodec::kAAC,
+ media::SampleFormat::kSampleFormatF32,
+ media::CHANNEL_LAYOUT_MONO,
+ 10000,
+ media::EmptyExtraData(),
+ media::EncryptionScheme::kUnencrypted),
+ test_video_config_(media::VideoCodec::kH264,
+ media::VideoCodecProfile::H264PROFILE_MAIN,
+ media::VideoDecoderConfig::AlphaMode::kIsOpaque,
+ media::VideoColorSpace::JPEG(),
+ media::VideoTransformation(),
+ {1920, 1080},
+ {1920, 1080},
+ {1920, 1080},
+ media::EmptyExtraData(),
+ media::EncryptionScheme::kUnencrypted),
+ stream_handler_(
+ &client_,
+ base::BindRepeating(&RpcDemuxerStreamHandlerTest::GetHandle,
+ base::Unretained(this)),
+ base::BindRepeating(&RpcDemuxerStreamHandlerTest::SendMessage,
+ base::Unretained(this))) {
+ EXPECT_CALL(*this, GetHandle())
+ .WillOnce(Return(audio_local_handle_))
+ .WillOnce(Return(video_local_handle_));
+ EXPECT_CALL(*this, SendMessage(audio_remote_handle_, _))
+ .WillOnce(CheckInitializeDemuxerStream(audio_remote_handle_,
+ audio_local_handle_));
+ EXPECT_CALL(*this, SendMessage(video_remote_handle_, _))
+ .WillOnce(CheckInitializeDemuxerStream(video_remote_handle_,
+ video_local_handle_));
+ stream_handler_.OnRpcAcquireDemuxer(audio_remote_handle_,
+ video_remote_handle_);
+ }
+
+ ~RpcDemuxerStreamHandlerTest() override = default;
+
+ protected:
+ class MockClient : public RpcDemuxerStreamHandler::Client {
+ public:
+ ~MockClient() override = default;
+
+ MOCK_METHOD1(OnNewAudioConfig, void(media::AudioDecoderConfig));
+ MOCK_METHOD1(OnNewVideoConfig, void(media::VideoDecoderConfig));
+ };
+
+ MOCK_METHOD2(SendMessage,
+ void(openscreen::cast::RpcMessenger::Handle,
+ std::unique_ptr<openscreen::cast::RpcMessage>));
+
+ MOCK_METHOD0(GetHandle, openscreen::cast::RpcMessenger::Handle());
+
+ MOCK_METHOD1(OnBitstreamConverterEnabled, void(bool));
+
+ void OnRpcInitializeCallback(
+ openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config) {
+ static_cast<RpcDemuxerStreamCBMessageHandler*>(&stream_handler_)
+ ->OnRpcInitializeCallback(handle, std::move(audio_config),
+ std::move(video_config));
+ }
+
+ void OnRpcReadUntilCallback(
+ openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config,
+ uint32_t total_frames_received) {
+ static_cast<RpcDemuxerStreamCBMessageHandler*>(&stream_handler_)
+ ->OnRpcReadUntilCallback(handle, std::move(audio_config),
+ std::move(video_config),
+ total_frames_received);
+ }
+
+ void RequestMoreAudioBuffers() {
+ auto client = stream_handler_.GetAudioClient();
+ ASSERT_TRUE(!!client);
+ client->OnNoBuffersAvailable();
+ }
+
+ void RequestMoreVideoBuffers() {
+ auto client = stream_handler_.GetVideoClient();
+ ASSERT_TRUE(!!client);
+ client->OnNoBuffersAvailable();
+ }
+
+ void OnAudioError() {
+ auto client = stream_handler_.GetAudioClient();
+ ASSERT_TRUE(!!client);
+ client->OnError();
+ }
+
+ void OnVideoError() {
+ auto client = stream_handler_.GetVideoClient();
+ ASSERT_TRUE(!!client);
+ client->OnError();
+ }
+
+ void EnableAudioBitstreamConverter() {
+ auto client = stream_handler_.GetAudioClient();
+ ASSERT_TRUE(!!client);
+ client->EnableBitstreamConverter(base::BindOnce(
+ &RpcDemuxerStreamHandlerTest::OnBitstreamConverterEnabled,
+ base::Unretained(this)));
+ }
+
+ void EnableVideoBitstreamConverter() {
+ auto client = stream_handler_.GetVideoClient();
+ ASSERT_TRUE(!!client);
+ client->EnableBitstreamConverter(base::BindOnce(
+ &RpcDemuxerStreamHandlerTest::OnBitstreamConverterEnabled,
+ base::Unretained(this)));
+ }
+
+ openscreen::cast::RpcMessenger::Handle audio_remote_handle_ = 123;
+ openscreen::cast::RpcMessenger::Handle video_remote_handle_ = 456;
+
+ openscreen::cast::RpcMessenger::Handle audio_local_handle_ = 135;
+ openscreen::cast::RpcMessenger::Handle video_local_handle_ = 246;
+
+ media::AudioDecoderConfig test_audio_config_;
+ media::VideoDecoderConfig test_video_config_;
+
+ StrictMock<MockClient> client_;
+
+ RpcDemuxerStreamHandler stream_handler_;
+};
+
+TEST_F(RpcDemuxerStreamHandlerTest, InitKnownAudioHandleWithAudioConfig) {
+ EXPECT_CALL(client_, OnNewAudioConfig(_))
+ .WillOnce([this](media::AudioDecoderConfig config) {
+ EXPECT_TRUE(test_audio_config_.Matches(config));
+ });
+ OnRpcInitializeCallback(audio_local_handle_, test_audio_config_,
+ absl::nullopt);
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, InitKnownAudioHandleWithVideoConfig) {
+ OnRpcInitializeCallback(audio_local_handle_, absl::nullopt,
+ test_video_config_);
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, InitKnownVideoHandleWithAudioConfig) {
+ EXPECT_CALL(client_, OnNewVideoConfig(_))
+ .WillOnce([this](media::VideoDecoderConfig config) {
+ EXPECT_TRUE(test_video_config_.Matches(config));
+ });
+ OnRpcInitializeCallback(video_local_handle_, absl::nullopt,
+ test_video_config_);
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, InitKnownVideoHandleWithVideoConfig) {
+ OnRpcInitializeCallback(video_local_handle_, test_audio_config_,
+ absl::nullopt);
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, ReadKnownAudioHandleWithAudioConfig) {
+ EXPECT_CALL(client_, OnNewAudioConfig(_))
+ .WillOnce([this](media::AudioDecoderConfig config) {
+ EXPECT_TRUE(test_audio_config_.Matches(config));
+ });
+ OnRpcReadUntilCallback(audio_local_handle_, test_audio_config_, absl::nullopt,
+ uint32_t{1});
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, ReadKnownAudioHandleWithVideoConfig) {
+ OnRpcReadUntilCallback(audio_local_handle_, absl::nullopt, test_video_config_,
+ uint32_t{1});
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, ReadKnownVideoHandleWithAudioConfig) {
+ EXPECT_CALL(client_, OnNewVideoConfig(_))
+ .WillOnce([this](media::VideoDecoderConfig config) {
+ EXPECT_TRUE(test_video_config_.Matches(config));
+ });
+ OnRpcReadUntilCallback(video_local_handle_, absl::nullopt, test_video_config_,
+ uint32_t{1});
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, ReadKnownVideoHandleWithVideoConfig) {
+ OnRpcReadUntilCallback(video_local_handle_, test_audio_config_, absl::nullopt,
+ uint32_t{1});
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, RequestMoreAudioBuffers) {
+ EXPECT_CALL(client_, OnNewAudioConfig(_))
+ .WillOnce([this](media::AudioDecoderConfig config) {
+ EXPECT_TRUE(test_audio_config_.Matches(config));
+ });
+ OnRpcReadUntilCallback(audio_local_handle_, test_audio_config_, absl::nullopt,
+ uint32_t{1});
+ EXPECT_CALL(*this, SendMessage(_, _))
+ .WillOnce(
+ CheckReadUntilCall(audio_remote_handle_, audio_local_handle_, 1));
+ RequestMoreAudioBuffers();
+
+ EXPECT_CALL(client_, OnNewAudioConfig(_))
+ .WillOnce([this](media::AudioDecoderConfig config) {
+ EXPECT_TRUE(test_audio_config_.Matches(config));
+ });
+ OnRpcReadUntilCallback(audio_local_handle_, test_audio_config_, absl::nullopt,
+ uint32_t{17});
+ EXPECT_CALL(*this, SendMessage(_, _))
+ .WillOnce(
+ CheckReadUntilCall(audio_remote_handle_, audio_local_handle_, 17));
+ RequestMoreAudioBuffers();
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, RequestMoreVideoBuffers) {
+ EXPECT_CALL(client_, OnNewVideoConfig(_))
+ .WillOnce([this](media::VideoDecoderConfig config) {
+ EXPECT_TRUE(test_video_config_.Matches(config));
+ });
+ OnRpcReadUntilCallback(video_local_handle_, absl::nullopt, test_video_config_,
+ uint32_t{12});
+ EXPECT_CALL(*this, SendMessage(_, _))
+ .WillOnce(
+ CheckReadUntilCall(video_remote_handle_, video_local_handle_, 12));
+ RequestMoreVideoBuffers();
+
+ EXPECT_CALL(client_, OnNewVideoConfig(_))
+ .WillOnce([this](media::VideoDecoderConfig config) {
+ EXPECT_TRUE(test_video_config_.Matches(config));
+ });
+ OnRpcReadUntilCallback(video_local_handle_, absl::nullopt, test_video_config_,
+ uint32_t{42});
+ EXPECT_CALL(*this, SendMessage(_, _))
+ .WillOnce(
+ CheckReadUntilCall(video_remote_handle_, video_local_handle_, 42));
+ RequestMoreVideoBuffers();
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, OnAudioError) {
+ EXPECT_CALL(*this, SendMessage(_, _))
+ .WillOnce(CheckOnErrorCall(audio_remote_handle_));
+ OnAudioError();
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, OnVideoError) {
+ EXPECT_CALL(*this, SendMessage(_, _))
+ .WillOnce(CheckOnErrorCall(video_remote_handle_));
+ OnVideoError();
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, OnEnableAudioBitstreamConverter) {
+ EXPECT_CALL(*this, SendMessage(_, _))
+ .WillOnce(CheckEnableBistreamConverterCall(audio_remote_handle_));
+ EnableAudioBitstreamConverter();
+
+ EXPECT_CALL(*this, OnBitstreamConverterEnabled(true));
+ stream_handler_.OnRpcBitstreamConverterEnabled(audio_local_handle_, true);
+}
+
+TEST_F(RpcDemuxerStreamHandlerTest, OnEnableVideoBitstreamConverter) {
+ EXPECT_CALL(*this, SendMessage(_, _))
+ .WillOnce(CheckEnableBistreamConverterCall(video_remote_handle_));
+ EnableVideoBitstreamConverter();
+
+ EXPECT_CALL(*this, OnBitstreamConverterEnabled(false));
+ stream_handler_.OnRpcBitstreamConverterEnabled(video_local_handle_, false);
+}
+
+} // namespace cast_streaming::remoting
diff --git a/chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.cc b/chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.cc
index c8d2e2ce8d2..5fe4624a711 100644
--- a/chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.cc
+++ b/chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.cc
@@ -18,15 +18,16 @@ RpcInitializationCallHandlerBase::RpcInitializationCallHandlerBase(
RpcInitializationCallHandlerBase::~RpcInitializationCallHandlerBase() = default;
-void RpcInitializationCallHandlerBase::OnRpcAcquireRenderer(int handle) {
+void RpcInitializationCallHandlerBase::OnRpcAcquireRenderer(
+ openscreen::cast::RpcMessenger::Handle handle) {
RpcAcquireRendererAsync(
base::BindOnce(&RpcInitializationCallHandlerBase::OnAcquireRendererDone,
weak_factory_.GetWeakPtr(), handle));
}
void RpcInitializationCallHandlerBase::OnAcquireRendererDone(
- int sender_handle,
- int receiver_handle) {
+ openscreen::cast::RpcMessenger::Handle sender_handle,
+ openscreen::cast::RpcMessenger::Handle receiver_handle) {
auto message = CreateMessageForAcquireRendererDone(receiver_handle);
message_processor_.Run(sender_handle, std::move(message));
}
diff --git a/chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.h b/chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.h
index 9a1d20ac298..0099561b9ca 100644
--- a/chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.h
+++ b/chromium/components/cast_streaming/browser/rpc_initialization_call_handler_base.h
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "components/cast_streaming/public/rpc_call_message_handler.h"
+#include "third_party/openscreen/src/cast/streaming/rpc_messenger.h"
namespace openscreen::cast {
class RpcMessage;
@@ -27,21 +28,25 @@ class RpcInitializationCallHandlerBase
// Called following an RPC call to acquire the Renderer. Callback parameter is
// the handle to be used by future calls associated with the Renderer.
- using AcquireRendererCB = base::OnceCallback<void(int)>;
+ using AcquireRendererCB =
+ base::OnceCallback<void(openscreen::cast::RpcMessenger::Handle)>;
virtual void RpcAcquireRendererAsync(AcquireRendererCB cb) = 0;
protected:
- using RpcProcessMessageCB = base::RepeatingCallback<
- void(int handle, std::unique_ptr<openscreen::cast::RpcMessage>)>;
-
+ using RpcProcessMessageCB = base::RepeatingCallback<void(
+ openscreen::cast::RpcMessenger::Handle handle,
+ std::unique_ptr<openscreen::cast::RpcMessage>)>;
explicit RpcInitializationCallHandlerBase(
RpcProcessMessageCB message_processor);
private:
- void OnAcquireRendererDone(int sender_handle, int receiver_handle);
+ void OnAcquireRendererDone(
+ openscreen::cast::RpcMessenger::Handle sender_handle,
+ openscreen::cast::RpcMessenger::Handle receiver_handle);
// RpcInitializationCallMessageHandler partial implementation.
- void OnRpcAcquireRenderer(int sender_handle) override;
+ void OnRpcAcquireRenderer(
+ openscreen::cast::RpcMessenger::Handle sender_handle) override;
RpcProcessMessageCB message_processor_;
diff --git a/chromium/components/cast_streaming/browser/stream_consumer.cc b/chromium/components/cast_streaming/browser/stream_consumer.cc
index 8966dea2d8f..84504770c8f 100644
--- a/chromium/components/cast_streaming/browser/stream_consumer.cc
+++ b/chromium/components/cast_streaming/browser/stream_consumer.cc
@@ -21,8 +21,7 @@ StreamConsumer::StreamConsumer(openscreen::cast::Receiver* receiver,
mojo::SimpleWatcher::ArmingPolicy::MANUAL,
base::SequencedTaskRunnerHandle::Get()),
frame_duration_(frame_duration),
- on_new_frame_(std::move(on_new_frame)),
- weak_factory_{this} {
+ on_new_frame_(std::move(on_new_frame)) {
DCHECK(receiver_);
receiver_->SetConsumer(this);
MojoResult result =
@@ -35,20 +34,31 @@ StreamConsumer::StreamConsumer(openscreen::cast::Receiver* receiver,
}
}
+StreamConsumer::StreamConsumer(StreamConsumer&& other,
+ openscreen::cast::Receiver* receiver,
+ mojo::ScopedDataPipeProducerHandle data_pipe)
+ : StreamConsumer(receiver,
+ other.frame_duration_,
+ std::move(data_pipe),
+ std::move(other.frame_received_cb_),
+ std::move(other.on_new_frame_)) {
+ if (other.is_read_pending_) {
+ ReadFrame(std::move(other.no_frames_available_cb_));
+ }
+}
+
StreamConsumer::~StreamConsumer() {
receiver_->SetConsumer(nullptr);
}
-void StreamConsumer::ReadFrame() {
+void StreamConsumer::ReadFrame(base::OnceClosure no_frames_available_cb) {
DCHECK(!is_read_pending_);
+ DCHECK(!no_frames_available_cb_);
is_read_pending_ = true;
+ no_frames_available_cb_ = std::move(no_frames_available_cb);
MaybeSendNextFrame();
}
-base::WeakPtr<StreamConsumer> StreamConsumer::GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
-}
-
void StreamConsumer::CloseDataPipeOnError() {
DLOG(WARNING) << "[ssrc:" << receiver_->ssrc() << "] Data pipe closed.";
receiver_->SetConsumer(nullptr);
@@ -93,9 +103,13 @@ void StreamConsumer::MaybeSendNextFrame() {
const int current_frame_buffer_size = receiver_->AdvanceToNextFrame();
if (current_frame_buffer_size == openscreen::cast::Receiver::kNoFramesReady) {
+ if (no_frames_available_cb_) {
+ std::move(no_frames_available_cb_).Run();
+ }
return;
}
+ no_frames_available_cb_.Reset();
on_new_frame_.Run();
void* buffer = nullptr;
diff --git a/chromium/components/cast_streaming/browser/stream_consumer.h b/chromium/components/cast_streaming/browser/stream_consumer.h
index 775c46ee149..28823408247 100644
--- a/chromium/components/cast_streaming/browser/stream_consumer.h
+++ b/chromium/components/cast_streaming/browser/stream_consumer.h
@@ -7,7 +7,6 @@
#include "base/callback.h"
#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "media/mojo/mojom/media_types.mojom.h"
#include "mojo/public/cpp/system/data_pipe.h"
@@ -40,6 +39,9 @@ class StreamConsumer final : public openscreen::cast::Receiver::Consumer {
mojo::ScopedDataPipeProducerHandle data_pipe,
FrameReceivedCB frame_received_cb,
base::RepeatingClosure on_new_frame);
+ StreamConsumer(StreamConsumer&& old_consumer,
+ openscreen::cast::Receiver* receiver,
+ mojo::ScopedDataPipeProducerHandle data_pipe);
~StreamConsumer() override;
StreamConsumer(const StreamConsumer&) = delete;
@@ -47,11 +49,9 @@ class StreamConsumer final : public openscreen::cast::Receiver::Consumer {
// Informs the StreamConsumer that a new frame should be read asynchronously.
// Eventually, the |frame_received_cb_| will be called with the data for this
- // frame.
- void ReadFrame();
-
- // Returns a WeakPtr associated with this instance;
- base::WeakPtr<StreamConsumer> GetWeakPtr();
+ // frame. |no_frames_available_cb| will be called if no frames are immediately
+ // available when this callback first tries to read them.
+ void ReadFrame(base::OnceClosure no_frames_available_cb);
private:
// Maximum frame size that OnFramesReady() can accept.
@@ -94,10 +94,10 @@ class StreamConsumer final : public openscreen::cast::Receiver::Consumer {
bool is_read_pending_ = false;
+ base::OnceClosure no_frames_available_cb_;
+
// Closure called on every new frame.
base::RepeatingClosure on_new_frame_;
-
- base::WeakPtrFactory<StreamConsumer> weak_factory_;
};
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/browser/streaming_initialization_info.cc b/chromium/components/cast_streaming/browser/streaming_initialization_info.cc
new file mode 100644
index 00000000000..711473b6559
--- /dev/null
+++ b/chromium/components/cast_streaming/browser/streaming_initialization_info.cc
@@ -0,0 +1,66 @@
+// Copyright 2022 The Chromium 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/cast_streaming/browser/streaming_initialization_info.h"
+
+#include "base/callback_helpers.h"
+
+namespace cast_streaming {
+
+StreamingInitializationInfo::StreamingInitializationInfo() = default;
+
+StreamingInitializationInfo::StreamingInitializationInfo(
+ const openscreen::cast::ReceiverSession* receiver_session,
+ absl::optional<AudioStreamInfo> audio_info,
+ absl::optional<VideoStreamInfo> video_info)
+ : session(receiver_session),
+ audio_stream_info(std::move(audio_info)),
+ video_stream_info(std::move(video_info)) {}
+
+StreamingInitializationInfo::StreamingInitializationInfo(
+ const StreamingInitializationInfo& other) = default;
+
+StreamingInitializationInfo::~StreamingInitializationInfo() = default;
+
+StreamingInitializationInfo::AudioStreamInfo::AudioStreamInfo() = default;
+
+StreamingInitializationInfo::AudioStreamInfo::AudioStreamInfo(
+ media::AudioDecoderConfig audio_config,
+ openscreen::cast::Receiver* cast_receiver)
+ : AudioStreamInfo(std::move(audio_config), cast_receiver, nullptr) {}
+
+StreamingInitializationInfo::AudioStreamInfo::AudioStreamInfo(
+ media::AudioDecoderConfig audio_config,
+ openscreen::cast::Receiver* cast_receiver,
+ base::WeakPtr<DemuxerStreamClient> ds_client)
+ : config(std::move(audio_config)),
+ receiver(cast_receiver),
+ demuxer_stream_client(std::move(ds_client)) {}
+
+StreamingInitializationInfo::AudioStreamInfo::AudioStreamInfo(
+ const StreamingInitializationInfo::AudioStreamInfo& other) = default;
+
+StreamingInitializationInfo::AudioStreamInfo::~AudioStreamInfo() = default;
+
+StreamingInitializationInfo::VideoStreamInfo::VideoStreamInfo() = default;
+
+StreamingInitializationInfo::VideoStreamInfo::VideoStreamInfo(
+ media::VideoDecoderConfig video_config,
+ openscreen::cast::Receiver* cast_receiver)
+ : VideoStreamInfo(std::move(video_config), cast_receiver, nullptr) {}
+
+StreamingInitializationInfo::VideoStreamInfo::VideoStreamInfo(
+ media::VideoDecoderConfig video_config,
+ openscreen::cast::Receiver* cast_receiver,
+ base::WeakPtr<DemuxerStreamClient> ds_client)
+ : config(std::move(video_config)),
+ receiver(cast_receiver),
+ demuxer_stream_client(std::move(ds_client)) {}
+
+StreamingInitializationInfo::VideoStreamInfo::VideoStreamInfo(
+ const StreamingInitializationInfo::VideoStreamInfo& other) = default;
+
+StreamingInitializationInfo::VideoStreamInfo::~VideoStreamInfo() = default;
+
+} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/browser/streaming_initialization_info.h b/chromium/components/cast_streaming/browser/streaming_initialization_info.h
new file mode 100644
index 00000000000..2fd89a7e0c6
--- /dev/null
+++ b/chromium/components/cast_streaming/browser/streaming_initialization_info.h
@@ -0,0 +1,92 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_CAST_STREAMING_BROWSER_STREAMING_INITIALIZATION_INFO_H_
+#define COMPONENTS_CAST_STREAMING_BROWSER_STREAMING_INITIALIZATION_INFO_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/cast_streaming/browser/demuxer_stream_client.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/video_decoder_config.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace openscreen::cast {
+class Receiver;
+class ReceiverSession;
+} // namespace openscreen::cast
+
+namespace cast_streaming {
+
+// This struct provides information pertaining to the initialization or
+// re-initialization of a cast streaming session.
+// NOTE: This struct IS copyable.
+struct StreamingInitializationInfo {
+ struct AudioStreamInfo {
+ AudioStreamInfo(media::AudioDecoderConfig audio_config,
+ openscreen::cast::Receiver* cast_receiver);
+ AudioStreamInfo(media::AudioDecoderConfig audio_config,
+ openscreen::cast::Receiver* cast_receiver,
+ base::WeakPtr<DemuxerStreamClient> ds_client);
+ AudioStreamInfo();
+ AudioStreamInfo(const AudioStreamInfo& other);
+ ~AudioStreamInfo();
+
+ // The decoder config associated with this audio stream.
+ media::AudioDecoderConfig config;
+
+ // The Receiver for the audio stream. This pointer will remain valid for the
+ // duration of the streaming session.
+ openscreen::cast::Receiver* receiver;
+
+ // Client with methods to be called when the DemuxerStream requires an
+ // action be executed.
+ base::WeakPtr<DemuxerStreamClient> demuxer_stream_client;
+ };
+
+ struct VideoStreamInfo {
+ VideoStreamInfo(media::VideoDecoderConfig video_config,
+ openscreen::cast::Receiver* cast_receiver);
+ VideoStreamInfo(media::VideoDecoderConfig video_config,
+ openscreen::cast::Receiver* cast_receiver,
+ base::WeakPtr<DemuxerStreamClient> ds_client);
+ VideoStreamInfo();
+ VideoStreamInfo(const VideoStreamInfo& other);
+ ~VideoStreamInfo();
+
+ // The decoder config associated with this video stream.
+ media::VideoDecoderConfig config;
+
+ // The Receiver for the video stream. This pointer will remain valid for the
+ // duration of the streaming session.
+ openscreen::cast::Receiver* receiver;
+
+ // Client with methods to be called when the DemuxerStream requires an
+ // action be executed.
+ base::WeakPtr<DemuxerStreamClient> demuxer_stream_client;
+ };
+
+ StreamingInitializationInfo(
+ const openscreen::cast::ReceiverSession* receiver_session,
+ absl::optional<AudioStreamInfo> audio_info,
+ absl::optional<VideoStreamInfo> video_info);
+ StreamingInitializationInfo();
+ StreamingInitializationInfo(const StreamingInitializationInfo& other);
+ ~StreamingInitializationInfo();
+
+ // The receiver session for which the remainder of this config is valid. This
+ // pointer will remain valid for the duration of the streaming session.
+ const openscreen::cast::ReceiverSession* session;
+
+ // Information detailing the audio stream. Will be populated iff the streaming
+ // session has audio.
+ absl::optional<AudioStreamInfo> audio_stream_info;
+
+ // Information detailing the video stream. Will be populated iff the streaming
+ // session has video.
+ absl::optional<VideoStreamInfo> video_stream_info;
+};
+
+} // namespace cast_streaming
+
+#endif // COMPONENTS_CAST_STREAMING_BROWSER_STREAMING_INITIALIZATION_INFO_H_
diff --git a/chromium/components/cast_streaming/public/mojom/BUILD.gn b/chromium/components/cast_streaming/public/mojom/BUILD.gn
index 3aa2b09d450..44c4ad379b4 100644
--- a/chromium/components/cast_streaming/public/mojom/BUILD.gn
+++ b/chromium/components/cast_streaming/public/mojom/BUILD.gn
@@ -6,7 +6,7 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [
- "cast_streaming_session.mojom",
+ "demuxer_connector.mojom",
"renderer_controller.mojom",
]
public_deps = [
diff --git a/chromium/components/cast_streaming/public/mojom/cast_streaming_session.mojom b/chromium/components/cast_streaming/public/mojom/demuxer_connector.mojom
index 1de3d693e65..00ef8109b2b 100644
--- a/chromium/components/cast_streaming/public/mojom/cast_streaming_session.mojom
+++ b/chromium/components/cast_streaming/public/mojom/demuxer_connector.mojom
@@ -47,6 +47,10 @@ interface AudioBufferRequester {
// returns.
GetBuffer() => (AudioStreamInfo? stream_info,
media.mojom.DecoderBuffer buffer);
+
+ // Requests that the data source providing audio buffers enable its bitstream
+ // converter. Returns whether the operation was successful.
+ EnableBitstreamConverter() => (bool success);
};
// Provides a "pull" mechanism to request DecoderBuffer frames of video data.
@@ -56,6 +60,9 @@ interface VideoBufferRequester {
// As AudioBufferRequester::GetBuffer() above.
GetBuffer() => (VideoStreamInfo? stream_info,
media.mojom.DecoderBuffer buffer);
+
+ // As AudioBufferRequester::EnableBitstreamConverter() above.
+ EnableBitstreamConverter() => (bool success);
};
// Initialization information for an audio DemuxerStream.
@@ -81,7 +88,7 @@ struct VideoStreamInitializationInfo {
// Implemented by the renderer, used to start the Cast Streaming Session.
// Closure of the Mojo channel will trigger the end of the Cast Streaming
// Session.
-interface CastStreamingReceiver {
+interface DemuxerConnector {
// Used for synchronization between the browser and the renderer. The browser
// should invoke this after binding the interface, and wait for the reply
// callback to know when the renderer is ready to receive and render frames.
@@ -89,7 +96,7 @@ interface CastStreamingReceiver {
// Called when the streams have been successfully initialized. At least one of
// |audio_buffer_requester| or |video_buffer_requester| must be set. This will
- // only be called once per the lifetime of CastStreamingReceiver.
+ // only be called once per the lifetime of DemuxerConnector.
OnStreamsInitialized(
AudioStreamInitializationInfo? audio_buffer_requester,
VideoStreamInitializationInfo? video_buffer_requester);
diff --git a/chromium/components/cast_streaming/public/remoting_message_factories.cc b/chromium/components/cast_streaming/public/remoting_message_factories.cc
index 0847e40e4be..2dcb3a0c4e8 100644
--- a/chromium/components/cast_streaming/public/remoting_message_factories.cc
+++ b/chromium/components/cast_streaming/public/remoting_message_factories.cc
@@ -118,12 +118,50 @@ std::unique_ptr<openscreen::cast::RpcMessage> CreateMessageForFlushComplete() {
}
std::unique_ptr<openscreen::cast::RpcMessage>
-CreateMessageForAcquireRendererDone(int receiver_renderer_handle) {
+CreateMessageForAcquireRendererDone(
+ openscreen::cast::RpcMessenger::Handle receiver_renderer_handle) {
auto rpc =
CreateMessage(openscreen::cast::RpcMessage::RPC_ACQUIRE_RENDERER_DONE);
rpc->set_integer_value(receiver_renderer_handle);
return rpc;
}
+std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamInitialize(
+ openscreen::cast::RpcMessenger::Handle local_handle) {
+ DCHECK_NE(local_handle, openscreen::cast::RpcMessenger::kInvalidHandle);
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_INITIALIZE);
+ rpc->set_integer_value(local_handle);
+ return rpc;
+}
+
+std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamReadUntil(
+ openscreen::cast::RpcMessenger::Handle local_handle,
+ uint32_t buffers_requested) {
+ DCHECK_NE(local_handle, openscreen::cast::RpcMessenger::kInvalidHandle);
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_READUNTIL);
+ auto* message = rpc->mutable_demuxerstream_readuntil_rpc();
+ message->set_count(buffers_requested);
+ message->set_callback_handle(local_handle);
+ return rpc;
+}
+
+std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamEnableBitstreamConverter() {
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_ENABLEBITSTREAMCONVERTER);
+ return rpc;
+}
+
+std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamError() {
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_ONERROR);
+ return rpc;
+}
+
} // namespace remoting
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/public/remoting_message_factories.h b/chromium/components/cast_streaming/public/remoting_message_factories.h
index 0bd5f579e3f..9d00b066d8f 100644
--- a/chromium/components/cast_streaming/public/remoting_message_factories.h
+++ b/chromium/components/cast_streaming/public/remoting_message_factories.h
@@ -10,6 +10,7 @@
#include "base/time/time.h"
#include "media/base/buffering_state.h"
#include "media/base/pipeline_status.h"
+#include "third_party/openscreen/src/cast/streaming/rpc_messenger.h"
namespace gfx {
class Size;
@@ -66,7 +67,23 @@ CreateMessageForInitializationComplete(bool has_succeeded);
std::unique_ptr<openscreen::cast::RpcMessage> CreateMessageForFlushComplete();
std::unique_ptr<openscreen::cast::RpcMessage>
-CreateMessageForAcquireRendererDone(int receiver_renderer_handle);
+CreateMessageForAcquireRendererDone(
+ openscreen::cast::RpcMessenger::Handle receiver_renderer_handle);
+
+std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamInitialize(
+ openscreen::cast::RpcMessenger::Handle local_handle);
+
+std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamReadUntil(
+ openscreen::cast::RpcMessenger::Handle local_handle,
+ uint32_t buffers_requested);
+
+std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamEnableBitstreamConverter();
+
+std::unique_ptr<openscreen::cast::RpcMessage>
+CreateMessageForDemuxerStreamError();
} // namespace remoting
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/public/remoting_message_factories_unittest.cc b/chromium/components/cast_streaming/public/remoting_message_factories_unittest.cc
index 23e55233212..17c825a9a96 100644
--- a/chromium/components/cast_streaming/public/remoting_message_factories_unittest.cc
+++ b/chromium/components/cast_streaming/public/remoting_message_factories_unittest.cc
@@ -183,11 +183,42 @@ TEST_F(RemotingMessageFactoriesTest, CreateMessageForFlushComplete) {
TEST_F(RemotingMessageFactoriesTest, CreateMessageForAcquireRendererDone) {
int kTestHandle = 42;
- auto rpc = CreateMessageForAcquireRendererDone(kTestHandle);
+ const auto rpc = CreateMessageForAcquireRendererDone(kTestHandle);
EXPECT_EQ(rpc->proc(),
openscreen::cast::RpcMessage::RPC_ACQUIRE_RENDERER_DONE);
EXPECT_EQ(rpc->integer_value(), kTestHandle);
}
+TEST_F(RemotingMessageFactoriesTest, CreateMessageForDemuxerStreamInitialize) {
+ constexpr int kTestHandle = 42;
+ const auto rpc = CreateMessageForDemuxerStreamInitialize(kTestHandle);
+ EXPECT_EQ(rpc->proc(), openscreen::cast::RpcMessage::RPC_DS_INITIALIZE);
+ EXPECT_EQ(rpc->integer_value(), kTestHandle);
+}
+
+TEST_F(RemotingMessageFactoriesTest, CreateMessageForDemuxerStreamReadUntil) {
+ constexpr int kTestHandle = 42;
+ constexpr uint32_t kTotalCount = 63;
+ const auto rpc =
+ CreateMessageForDemuxerStreamReadUntil(kTestHandle, kTotalCount);
+ EXPECT_EQ(rpc->proc(), openscreen::cast::RpcMessage::RPC_DS_READUNTIL);
+ ASSERT_TRUE(rpc->has_demuxerstream_readuntil_rpc());
+ auto& message = rpc->demuxerstream_readuntil_rpc();
+ EXPECT_EQ(message.count(), kTotalCount);
+ EXPECT_EQ(message.callback_handle(), kTestHandle);
+}
+
+TEST_F(RemotingMessageFactoriesTest,
+ CreateMessageForDemuxerStreamEnableBitstreamConverter) {
+ const auto rpc = CreateMessageForDemuxerStreamEnableBitstreamConverter();
+ EXPECT_EQ(rpc->proc(),
+ openscreen::cast::RpcMessage::RPC_DS_ENABLEBITSTREAMCONVERTER);
+}
+
+TEST_F(RemotingMessageFactoriesTest, CreateMessageForDemuxerStreamError) {
+ const auto rpc = CreateMessageForDemuxerStreamError();
+ EXPECT_EQ(rpc->proc(), openscreen::cast::RpcMessage::RPC_DS_ONERROR);
+}
+
} // namespace remoting
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/public/remoting_proto_enum_utils.cc b/chromium/components/cast_streaming/public/remoting_proto_enum_utils.cc
index 69a19e9a8cf..c0813afce8e 100644
--- a/chromium/components/cast_streaming/public/remoting_proto_enum_utils.cc
+++ b/chromium/components/cast_streaming/public/remoting_proto_enum_utils.cc
@@ -270,6 +270,14 @@ absl::optional<media::VideoCodecProfile> ToMediaVideoCodecProfile(
CASE_RETURN_OTHER(HEVCPROFILE_MAIN);
CASE_RETURN_OTHER(HEVCPROFILE_MAIN10);
CASE_RETURN_OTHER(HEVCPROFILE_MAIN_STILL_PICTURE);
+ CASE_RETURN_OTHER(HEVCPROFILE_REXT);
+ CASE_RETURN_OTHER(HEVCPROFILE_HIGH_THROUGHPUT);
+ CASE_RETURN_OTHER(HEVCPROFILE_MULTIVIEW_MAIN);
+ CASE_RETURN_OTHER(HEVCPROFILE_SCALABLE_MAIN);
+ CASE_RETURN_OTHER(HEVCPROFILE_3D_MAIN);
+ CASE_RETURN_OTHER(HEVCPROFILE_SCREEN_EXTENDED);
+ CASE_RETURN_OTHER(HEVCPROFILE_SCALABLE_REXT);
+ CASE_RETURN_OTHER(HEVCPROFILE_HIGH_THROUGHPUT_SCREEN_EXTENDED);
CASE_RETURN_OTHER(DOLBYVISION_PROFILE0);
CASE_RETURN_OTHER(DOLBYVISION_PROFILE4);
CASE_RETURN_OTHER(DOLBYVISION_PROFILE5);
@@ -310,6 +318,14 @@ ToProtoVideoDecoderConfigProfile(media::VideoCodecProfile value) {
CASE_RETURN_OTHER(HEVCPROFILE_MAIN);
CASE_RETURN_OTHER(HEVCPROFILE_MAIN10);
CASE_RETURN_OTHER(HEVCPROFILE_MAIN_STILL_PICTURE);
+ CASE_RETURN_OTHER(HEVCPROFILE_REXT);
+ CASE_RETURN_OTHER(HEVCPROFILE_HIGH_THROUGHPUT);
+ CASE_RETURN_OTHER(HEVCPROFILE_MULTIVIEW_MAIN);
+ CASE_RETURN_OTHER(HEVCPROFILE_SCALABLE_MAIN);
+ CASE_RETURN_OTHER(HEVCPROFILE_3D_MAIN);
+ CASE_RETURN_OTHER(HEVCPROFILE_SCREEN_EXTENDED);
+ CASE_RETURN_OTHER(HEVCPROFILE_SCALABLE_REXT);
+ CASE_RETURN_OTHER(HEVCPROFILE_HIGH_THROUGHPUT_SCREEN_EXTENDED);
CASE_RETURN_OTHER(DOLBYVISION_PROFILE0);
CASE_RETURN_OTHER(DOLBYVISION_PROFILE4);
CASE_RETURN_OTHER(DOLBYVISION_PROFILE5);
diff --git a/chromium/components/cast_streaming/public/rpc_call_message_handler.cc b/chromium/components/cast_streaming/public/rpc_call_message_handler.cc
index a92d1b5636b..b9560f3e1bf 100644
--- a/chromium/components/cast_streaming/public/rpc_call_message_handler.cc
+++ b/chromium/components/cast_streaming/public/rpc_call_message_handler.cc
@@ -4,17 +4,60 @@
#include "components/cast_streaming/public/rpc_call_message_handler.h"
+#include "base/check.h"
#include "base/logging.h"
+#include "components/cast_streaming/public/remoting_proto_enum_utils.h"
+#include "components/cast_streaming/public/remoting_proto_utils.h"
+#include "media/base/demuxer_stream.h"
#include "third_party/openscreen/src/cast/streaming/remoting.pb.h"
namespace cast_streaming {
namespace remoting {
+namespace {
+
+template <typename T>
+absl::optional<media::AudioDecoderConfig> ExtractAudioConfig(
+ const T& config_container) {
+ if (!config_container.has_audio_decoder_config()) {
+ return absl::nullopt;
+ }
+
+ const auto& audio_message = config_container.audio_decoder_config();
+ media::AudioDecoderConfig config;
+ ConvertProtoToAudioDecoderConfig(audio_message, &config);
+ if (!config.IsValidConfig()) {
+ return absl::nullopt;
+ }
+
+ return config;
+}
+
+template <typename T>
+absl::optional<media::VideoDecoderConfig> ExtractVideoConfig(
+ const T& config_container) {
+ if (!config_container.has_video_decoder_config()) {
+ return absl::nullopt;
+ }
+
+ const auto& video_message = config_container.video_decoder_config();
+ media::VideoDecoderConfig config;
+ ConvertProtoToVideoDecoderConfig(video_message, &config);
+ if (!config.IsValidConfig()) {
+ return absl::nullopt;
+ }
+
+ return config;
+}
+
+} // namespace
RpcInitializationCallMessageHandler::~RpcInitializationCallMessageHandler() =
default;
RpcRendererCallMessageHandler::~RpcRendererCallMessageHandler() = default;
+RpcDemuxerStreamCBMessageHandler::~RpcDemuxerStreamCBMessageHandler() = default;
+
bool DispatchInitializationRpcCall(
openscreen::cast::RpcMessage* message,
RpcInitializationCallMessageHandler* client) {
@@ -96,5 +139,56 @@ bool DispatchRendererRpcCall(openscreen::cast::RpcMessage* message,
}
}
+bool DispatchDemuxerStreamCBRpcCall(openscreen::cast::RpcMessage* message,
+ RpcDemuxerStreamCBMessageHandler* client) {
+ DCHECK(message);
+ DCHECK(client);
+
+ switch (message->proc()) {
+ case openscreen::cast::RpcMessage::RPC_DS_INITIALIZE_CALLBACK: {
+ if (!message->has_demuxerstream_initializecb_rpc()) {
+ LOG(ERROR) << "RPC_DS_INITIALIZE_CALLBACK with no "
+ "demuxerstream_initializecb_rpc() property received";
+ return false;
+ }
+ const auto& callback_message = message->demuxerstream_initializecb_rpc();
+ client->OnRpcInitializeCallback(message->handle(),
+ ExtractAudioConfig(callback_message),
+ ExtractVideoConfig(callback_message));
+ return true;
+ }
+ case openscreen::cast::RpcMessage::RPC_DS_READUNTIL_CALLBACK: {
+ if (!message->has_demuxerstream_readuntilcb_rpc()) {
+ LOG(ERROR) << "RPC_DS_READUNTIL with no "
+ "demuxerstream_readuntilcb_rpc() property received";
+ return false;
+ }
+ const auto& rpc_message = message->demuxerstream_readuntilcb_rpc();
+ auto audio_config = ExtractAudioConfig(rpc_message);
+ auto video_config = ExtractVideoConfig(rpc_message);
+
+ const auto status = ToDemuxerStreamStatus(rpc_message.status());
+ if ((audio_config || video_config) &&
+ status != media::DemuxerStream::kConfigChanged) {
+ LOG(ERROR) << "RPC_DS_READUNTIL with status != kConfigChanged contains "
+ "a new config";
+ return false;
+ } else if (!audio_config && !video_config &&
+ status == media::DemuxerStream::kConfigChanged) {
+ LOG(ERROR) << "RPC_DS_READUNTIL with status = kConfigChanged contains "
+ "no config";
+ return false;
+ }
+ const uint32_t total_frames_received = rpc_message.count();
+ client->OnRpcReadUntilCallback(message->handle(), std::move(audio_config),
+ std::move(video_config),
+ total_frames_received);
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
} // namespace remoting
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/public/rpc_call_message_handler.h b/chromium/components/cast_streaming/public/rpc_call_message_handler.h
index 56dfb60ebf1..7623983f163 100644
--- a/chromium/components/cast_streaming/public/rpc_call_message_handler.h
+++ b/chromium/components/cast_streaming/public/rpc_call_message_handler.h
@@ -6,6 +6,10 @@
#define COMPONENTS_CAST_STREAMING_PUBLIC_RPC_CALL_MESSAGE_HANDLER_H_
#include "base/time/time.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/video_decoder_config.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/openscreen/src/cast/streaming/rpc_messenger.h"
namespace openscreen {
namespace cast {
@@ -23,9 +27,11 @@ class RpcInitializationCallMessageHandler {
public:
virtual ~RpcInitializationCallMessageHandler();
- virtual void OnRpcAcquireRenderer(int handle) = 0;
- virtual void OnRpcAcquireDemuxer(int audio_stream_handle,
- int video_stream_handle) = 0;
+ virtual void OnRpcAcquireRenderer(
+ openscreen::cast::RpcMessenger::Handle handle) = 0;
+ virtual void OnRpcAcquireDemuxer(
+ openscreen::cast::RpcMessenger::Handle audio_stream_handle,
+ openscreen::cast::RpcMessenger::Handle video_stream_handle) = 0;
};
class RpcRendererCallMessageHandler {
@@ -39,12 +45,29 @@ class RpcRendererCallMessageHandler {
virtual void OnRpcSetVolume(double volume) = 0;
};
+class RpcDemuxerStreamCBMessageHandler {
+ public:
+ virtual ~RpcDemuxerStreamCBMessageHandler();
+
+ virtual void OnRpcInitializeCallback(
+ openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config) = 0;
+ virtual void OnRpcReadUntilCallback(
+ openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config,
+ uint32_t total_frames_received) = 0;
+};
+
// Processes the incoming |message| and forwards it to the appropriate |client|
// method.
bool DispatchInitializationRpcCall(openscreen::cast::RpcMessage* message,
RpcInitializationCallMessageHandler* client);
bool DispatchRendererRpcCall(openscreen::cast::RpcMessage* message,
RpcRendererCallMessageHandler* client);
+bool DispatchDemuxerStreamCBRpcCall(openscreen::cast::RpcMessage* message,
+ RpcDemuxerStreamCBMessageHandler* client);
} // namespace remoting
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/public/rpc_call_message_handler_unittest.cc b/chromium/components/cast_streaming/public/rpc_call_message_handler_unittest.cc
index 37e495f6240..d2949fd9dce 100644
--- a/chromium/components/cast_streaming/public/rpc_call_message_handler_unittest.cc
+++ b/chromium/components/cast_streaming/public/rpc_call_message_handler_unittest.cc
@@ -6,6 +6,9 @@
#include <memory>
+#include "components/cast_streaming/public/remoting_proto_enum_utils.h"
+#include "components/cast_streaming/public/remoting_proto_utils.h"
+#include "media/base/media_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/openscreen/src/cast/streaming/remoting.pb.h"
@@ -13,6 +16,7 @@
using testing::_;
using testing::Invoke;
using testing::Return;
+using testing::StrictMock;
namespace cast_streaming {
namespace remoting {
@@ -21,7 +25,6 @@ class RpcCallMessageHandlerTest : public testing::Test {
public:
class MockRpcRendererCallMessageHandler
: public RpcRendererCallMessageHandler {
- protected:
public:
~MockRpcRendererCallMessageHandler() override = default;
@@ -34,7 +37,6 @@ class RpcCallMessageHandlerTest : public testing::Test {
class MockRpcInitializationCallMessageHandler
: public RpcInitializationCallMessageHandler {
- protected:
public:
~MockRpcInitializationCallMessageHandler() override = default;
@@ -42,8 +44,46 @@ class RpcCallMessageHandlerTest : public testing::Test {
MOCK_METHOD2(OnRpcAcquireDemuxer, void(int, int));
};
- MockRpcRendererCallMessageHandler renderer_client_;
- MockRpcInitializationCallMessageHandler initialization_client_;
+ class MockRpcDemuxerStreamCBMessageHandler
+ : public RpcDemuxerStreamCBMessageHandler {
+ public:
+ ~MockRpcDemuxerStreamCBMessageHandler() override = default;
+
+ MOCK_METHOD3(OnRpcInitializeCallback,
+ void(int,
+ absl::optional<media::AudioDecoderConfig>,
+ absl::optional<media::VideoDecoderConfig>));
+ MOCK_METHOD4(OnRpcReadUntilCallback,
+ void(int,
+ absl::optional<media::AudioDecoderConfig>,
+ absl::optional<media::VideoDecoderConfig>,
+ uint32_t));
+ };
+
+ RpcCallMessageHandlerTest() = default;
+
+ media::AudioDecoderConfig test_audio_config_ =
+ media::AudioDecoderConfig(media::AudioCodec::kAAC,
+ media::SampleFormat::kSampleFormatF32,
+ media::CHANNEL_LAYOUT_MONO,
+ 10000,
+ media::EmptyExtraData(),
+ media::EncryptionScheme::kUnencrypted);
+ media::VideoDecoderConfig test_video_config_ =
+ media::VideoDecoderConfig(media::VideoCodec::kH264,
+ media::VideoCodecProfile::H264PROFILE_MAIN,
+ media::VideoDecoderConfig::AlphaMode::kIsOpaque,
+ media::VideoColorSpace::JPEG(),
+ media::VideoTransformation(),
+ {1920, 1080},
+ {1920, 1080},
+ {1920, 1080},
+ media::EmptyExtraData(),
+ media::EncryptionScheme::kUnencrypted);
+
+ StrictMock<MockRpcRendererCallMessageHandler> renderer_client_;
+ StrictMock<MockRpcInitializationCallMessageHandler> initialization_client_;
+ StrictMock<MockRpcDemuxerStreamCBMessageHandler> demuxer_stream_client_;
};
TEST_F(RpcCallMessageHandlerTest, OnRpcInitialize) {
@@ -120,5 +160,156 @@ TEST_F(RpcCallMessageHandlerTest, OnInvalidInitializationMessage) {
DispatchInitializationRpcCall(rpc.get(), &initialization_client_));
}
+TEST_F(RpcCallMessageHandlerTest, OnDemuxerStreamInitializeCallbackValid) {
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ constexpr int kHandle = 123;
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_INITIALIZE_CALLBACK);
+ rpc->set_handle(kHandle);
+ auto* initialize_cb = rpc->mutable_demuxerstream_initializecb_rpc();
+ auto* audio_config = initialize_cb->mutable_audio_decoder_config();
+ auto* video_config = initialize_cb->mutable_video_decoder_config();
+
+ ConvertAudioDecoderConfigToProto(test_audio_config_, audio_config);
+ ConvertVideoDecoderConfigToProto(test_video_config_, video_config);
+ EXPECT_CALL(demuxer_stream_client_, OnRpcInitializeCallback(kHandle, _, _))
+ .WillOnce([this](openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config) {
+ EXPECT_TRUE(audio_config.has_value());
+ EXPECT_TRUE(test_audio_config_.Matches(audio_config.value()));
+ EXPECT_TRUE(video_config.has_value());
+ EXPECT_TRUE(test_video_config_.Matches(video_config.value()));
+ });
+ EXPECT_TRUE(
+ DispatchDemuxerStreamCBRpcCall(rpc.get(), &demuxer_stream_client_));
+}
+
+TEST_F(RpcCallMessageHandlerTest, OnDemuxerStreamInitializeCallbackOneConfig) {
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_INITIALIZE_CALLBACK);
+ constexpr int kHandle = 123;
+ rpc->set_handle(kHandle);
+ auto* initialize_cb = rpc->mutable_demuxerstream_initializecb_rpc();
+ auto* video_config = initialize_cb->mutable_video_decoder_config();
+ ConvertVideoDecoderConfigToProto(test_video_config_, video_config);
+ EXPECT_CALL(demuxer_stream_client_, OnRpcInitializeCallback(kHandle, _, _))
+ .WillOnce([this](openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config) {
+ EXPECT_FALSE(audio_config.has_value());
+ EXPECT_TRUE(video_config.has_value());
+ EXPECT_TRUE(test_video_config_.Matches(video_config.value()));
+ });
+ EXPECT_TRUE(
+ DispatchDemuxerStreamCBRpcCall(rpc.get(), &demuxer_stream_client_));
+}
+
+TEST_F(RpcCallMessageHandlerTest, OnDemuxerStreamInitializeCallbackNoConfig) {
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_INITIALIZE_CALLBACK);
+ rpc->set_handle(123);
+ EXPECT_FALSE(
+ DispatchDemuxerStreamCBRpcCall(rpc.get(), &demuxer_stream_client_));
+}
+
+TEST_F(RpcCallMessageHandlerTest, OnDemuxerStreamReadUntilCallbackValid) {
+ constexpr int kHandle = 123;
+ constexpr int kCount = 456;
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_READUNTIL_CALLBACK);
+ rpc->set_handle(kHandle);
+ auto* readuntil_cb = rpc->mutable_demuxerstream_readuntilcb_rpc();
+ auto* audio_config = readuntil_cb->mutable_audio_decoder_config();
+ auto* video_config = readuntil_cb->mutable_video_decoder_config();
+
+ ConvertAudioDecoderConfigToProto(test_audio_config_, audio_config);
+ ConvertVideoDecoderConfigToProto(test_video_config_, video_config);
+ readuntil_cb->set_count(kCount);
+ readuntil_cb->set_status(
+ ToProtoDemuxerStreamStatus(media::DemuxerStream::kConfigChanged).value());
+ EXPECT_CALL(demuxer_stream_client_,
+ OnRpcReadUntilCallback(kHandle, _, _, kCount))
+ .WillOnce([this](openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config,
+ uint32_t count) {
+ EXPECT_TRUE(audio_config.has_value());
+ EXPECT_TRUE(test_audio_config_.Matches(audio_config.value()));
+ EXPECT_TRUE(video_config.has_value());
+ EXPECT_TRUE(test_video_config_.Matches(video_config.value()));
+ });
+ EXPECT_TRUE(
+ DispatchDemuxerStreamCBRpcCall(rpc.get(), &demuxer_stream_client_));
+}
+
+TEST_F(RpcCallMessageHandlerTest, OnDemuxerStreamReadUntilCallbackOneConfig) {
+ constexpr int kHandle = 123;
+ constexpr int kCount = 456;
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_READUNTIL_CALLBACK);
+ rpc->set_handle(kHandle);
+ auto* readuntil_cb = rpc->mutable_demuxerstream_readuntilcb_rpc();
+ auto* video_config = readuntil_cb->mutable_video_decoder_config();
+ ConvertVideoDecoderConfigToProto(test_video_config_, video_config);
+ readuntil_cb->set_count(kCount);
+ readuntil_cb->set_status(
+ ToProtoDemuxerStreamStatus(media::DemuxerStream::kConfigChanged).value());
+ EXPECT_CALL(demuxer_stream_client_,
+ OnRpcReadUntilCallback(kHandle, _, _, kCount))
+ .WillOnce([this](openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config,
+ uint32_t count) {
+ EXPECT_FALSE(audio_config.has_value());
+ EXPECT_TRUE(video_config.has_value());
+ EXPECT_TRUE(test_video_config_.Matches(video_config.value()));
+ });
+ EXPECT_TRUE(
+ DispatchDemuxerStreamCBRpcCall(rpc.get(), &demuxer_stream_client_));
+}
+
+TEST_F(RpcCallMessageHandlerTest, OnDemuxerStreamReadUntilCallbackNoConfig) {
+ constexpr int kHandle = 123;
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_READUNTIL_CALLBACK);
+ rpc->set_handle(kHandle);
+ auto* readuntil_cb = rpc->mutable_demuxerstream_readuntilcb_rpc();
+ readuntil_cb->set_status(
+ ToProtoDemuxerStreamStatus(media::DemuxerStream::kConfigChanged).value());
+ EXPECT_FALSE(
+ DispatchDemuxerStreamCBRpcCall(rpc.get(), &demuxer_stream_client_));
+}
+
+TEST_F(RpcCallMessageHandlerTest, OnDemuxerStreamReadUntilCallbackNonConfig) {
+ constexpr int kHandle = 123;
+ constexpr int kCount = 456;
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_DS_READUNTIL_CALLBACK);
+ rpc->set_handle(kHandle);
+ auto* readuntil_cb = rpc->mutable_demuxerstream_readuntilcb_rpc();
+ readuntil_cb->set_count(kCount);
+ readuntil_cb->set_status(
+ ToProtoDemuxerStreamStatus(media::DemuxerStream::kOk).value());
+ EXPECT_CALL(demuxer_stream_client_,
+ OnRpcReadUntilCallback(kHandle, _, _, kCount))
+ .WillOnce([](openscreen::cast::RpcMessenger::Handle handle,
+ absl::optional<media::AudioDecoderConfig> audio_config,
+ absl::optional<media::VideoDecoderConfig> video_config,
+ uint32_t count) {
+ EXPECT_FALSE(audio_config.has_value());
+ EXPECT_FALSE(video_config.has_value());
+ });
+ EXPECT_TRUE(
+ DispatchDemuxerStreamCBRpcCall(rpc.get(), &demuxer_stream_client_));
+}
+
+TEST_F(RpcCallMessageHandlerTest, OnInvalidDemuxerStreamMessage) {
+ auto rpc = std::make_unique<openscreen::cast::RpcMessage>();
+ rpc->set_proc(openscreen::cast::RpcMessage::RPC_ACQUIRE_RENDERER);
+ rpc->set_integer_value(42);
+ EXPECT_FALSE(
+ DispatchDemuxerStreamCBRpcCall(rpc.get(), &demuxer_stream_client_));
+}
+
} // namespace remoting
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/renderer/BUILD.gn b/chromium/components/cast_streaming/renderer/BUILD.gn
index 2785e302cf6..23fa08e037d 100644
--- a/chromium/components/cast_streaming/renderer/BUILD.gn
+++ b/chromium/components/cast_streaming/renderer/BUILD.gn
@@ -4,62 +4,56 @@
import("//testing/test.gni")
+# Public target on which all external code requiring a resource_provider
+# implementation should depend.
source_set("renderer") {
- deps = [
- ":demuxer",
- "//base",
- "//components/cast_streaming/public",
- "//content/public/renderer",
- "//third_party/blink/public:blink_headers",
- "//third_party/blink/public/common",
- ]
- public = [ "public/demuxer_provider.h " ]
- sources = [
- "cast_streaming_render_frame_observer.cc",
- "cast_streaming_render_frame_observer.h",
- "demuxer_provider.cc",
- ]
-}
-
-source_set("renderer_controller_proxy") {
- public = [ "public/renderer_controller_proxy.h" ]
- sources = [ "renderer_controller_proxy.cc" ]
public_deps = [
- "//base",
- "//components/cast_streaming/public/mojom",
- "//mojo/public/cpp/bindings",
+ ":resource_provider",
+ ":resource_provider_factory",
]
}
+# Public target used by the //content code to inject the Renderer used for
+# cast_streaming operations. This target is included in ALL chromium builds
+# using the Chromium media pipeline, so be careful when adding new dependencies
+# here.
source_set("wrapping_renderer_factory_selector") {
public = [ "public/wrapping_renderer_factory_selector.h" ]
sources = [ "wrapping_renderer_factory_selector.cc" ]
public_deps = [ "//media" ]
deps = [
":playback_command_forwarding_renderer",
- ":renderer_controller_proxy",
+ ":resource_provider",
"//base",
]
}
-source_set("renderer_controller_proxy_factory") {
- public = [ "public/renderer_controller_proxy_factory.h" ]
- sources = [
- "renderer_controller_proxy_impl.cc",
- "renderer_controller_proxy_impl.h",
- ]
- public_deps = [
- ":renderer_controller_proxy",
+# This target is a dependency of :wrapping_renderer_factory_selector, which is
+# included in ALL chromium builds using the Chromium media pipeline. Be careful
+# when adding new dependencies here.
+source_set("resource_provider") {
+ deps = [
"//base",
"//components/cast_streaming/public/mojom",
+ "//media",
+ "//media/mojo/common",
"//mojo/public/cpp/bindings",
]
+ public = [ "public/resource_provider.h" ]
+ sources = [ "resource_provider.cc" ]
}
-source_set("demuxer") {
+source_set("resource_provider_factory") {
+ public = [ "public/resource_provider_factory.h" ]
+ deps = [ ":resource_provider_impl" ]
+}
+
+source_set("resource_provider_impl") {
+ public_deps = [ ":resource_provider" ]
deps = [
":decoder_buffer_reader",
"//base",
+ "//components/cast_streaming/public",
"//components/cast_streaming/public/mojom",
"//media",
"//media/mojo/common",
@@ -68,8 +62,12 @@ source_set("demuxer") {
sources = [
"cast_streaming_demuxer.cc",
"cast_streaming_demuxer.h",
- "cast_streaming_receiver.cc",
- "cast_streaming_receiver.h",
+ "demuxer_connector.cc",
+ "demuxer_connector.h",
+ "renderer_controller_proxy.cc",
+ "renderer_controller_proxy.h",
+ "resource_provider_impl.cc",
+ "resource_provider_impl.h",
]
visibility = [ ":*" ]
}
@@ -88,6 +86,9 @@ source_set("decoder_buffer_reader") {
visibility = [ ":*" ]
}
+# This target is a dependency of :wrapping_renderer_factory_selector, which is
+# included in ALL chromium builds using the Chromium media pipeline. Be careful
+# when adding new dependencies here.
source_set("playback_command_forwarding_renderer") {
sources = [
"playback_command_forwarding_renderer.cc",
@@ -117,8 +118,7 @@ source_set("unit_tests") {
":decoder_buffer_reader",
":playback_command_forwarding_renderer",
":renderer",
- ":renderer_controller_proxy",
- ":renderer_controller_proxy_factory",
+ ":resource_provider_impl",
":wrapping_renderer_factory_selector",
"//base/test:test_support",
"//components/cast_streaming/public/mojom",
diff --git a/chromium/components/cast_streaming/renderer/DEPS b/chromium/components/cast_streaming/renderer/DEPS
index 13c8e138963..d2dc1e6f1cb 100644
--- a/chromium/components/cast_streaming/renderer/DEPS
+++ b/chromium/components/cast_streaming/renderer/DEPS
@@ -1,9 +1,7 @@
include_rules = [
- "+content/public/renderer",
"+media/base",
"+media/mojo",
"+mojo/public",
- "+third_party/blink/public",
"+ui/gfx",
"+url",
]
diff --git a/chromium/components/cast_streaming/renderer/cast_streaming_demuxer.cc b/chromium/components/cast_streaming/renderer/cast_streaming_demuxer.cc
index b44bb78aaa7..20164313759 100644
--- a/chromium/components/cast_streaming/renderer/cast_streaming_demuxer.cc
+++ b/chromium/components/cast_streaming/renderer/cast_streaming_demuxer.cc
@@ -10,8 +10,8 @@
#include "base/bind.h"
#include "base/sequence_checker.h"
#include "base/task/single_thread_task_runner.h"
-#include "components/cast_streaming/renderer/cast_streaming_receiver.h"
#include "components/cast_streaming/renderer/decoder_buffer_reader.h"
+#include "components/cast_streaming/renderer/demuxer_connector.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/decoder_buffer.h"
#include "media/base/timestamp_constants.h"
@@ -160,10 +160,15 @@ class CastStreamingDemuxerStream : public media::DemuxerStream {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
decoder_config_ = std::move(data_stream_info->decoder_config);
- buffer_reader_ = std::make_unique<DecoderBufferReader>(
- base::BindRepeating(&CastStreamingDemuxerStream::OnBufferReady,
- weak_factory_.GetWeakPtr()),
- std::move(data_stream_info->data_pipe));
+ if (!buffer_reader_) {
+ buffer_reader_ = std::make_unique<DecoderBufferReader>(
+ base::BindRepeating(&CastStreamingDemuxerStream::OnBufferReady,
+ weak_factory_.GetWeakPtr()),
+ std::move(data_stream_info->data_pipe));
+ } else {
+ buffer_reader_ = std::make_unique<DecoderBufferReader>(
+ std::move(*buffer_reader_), std::move(data_stream_info->data_pipe));
+ }
if (pending_read_cb_) {
// Return early if there is already an ongoing Read() call. The prior
@@ -177,6 +182,8 @@ class CastStreamingDemuxerStream : public media::DemuxerStream {
}
}
+ void OnBitstreamConverterEnabled(bool success) { NOTIMPLEMENTED_LOG_ONCE(); }
+
// DemuxerStream partial implementation.
void Read(ReadCB read_cb) final {
DVLOG(3) << __func__;
@@ -207,6 +214,12 @@ class CastStreamingDemuxerStream : public media::DemuxerStream {
buffer_reader_->ReadBufferAsync();
}
+ void EnableBitstreamConverter() final {
+ remote_->EnableBitstreamConverter(
+ base::BindOnce(&CastStreamingDemuxerStream::OnBitstreamConverterEnabled,
+ weak_factory_.GetWeakPtr()));
+ }
+
media::StreamLiveness liveness() const final {
return media::StreamLiveness::kLive;
}
@@ -279,14 +292,14 @@ class CastStreamingVideoDemuxerStream final
};
CastStreamingDemuxer::CastStreamingDemuxer(
- CastStreamingReceiver* receiver,
- const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner)
- : media_task_runner_(media_task_runner),
+ DemuxerConnector* demuxer_connector,
+ scoped_refptr<base::SingleThreadTaskRunner> media_task_runner)
+ : media_task_runner_(std::move(media_task_runner)),
original_task_runner_(base::SequencedTaskRunnerHandle::Get()),
- receiver_(receiver),
+ demuxer_connector_(demuxer_connector),
weak_factory_(this) {
DVLOG(1) << __func__;
- DCHECK(receiver_);
+ DCHECK(demuxer_connector_);
}
CastStreamingDemuxer::~CastStreamingDemuxer() {
@@ -294,8 +307,8 @@ CastStreamingDemuxer::~CastStreamingDemuxer() {
if (was_initialization_successful_) {
original_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&CastStreamingReceiver::OnDemuxerDestroyed,
- base::Unretained(receiver_)));
+ FROM_HERE, base::BindOnce(&DemuxerConnector::OnDemuxerDestroyed,
+ base::Unretained(demuxer_connector_)));
}
}
@@ -366,9 +379,9 @@ void CastStreamingDemuxer::Initialize(media::DemuxerHost* host,
initialized_cb_ = std::move(status_cb);
original_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&CastStreamingReceiver::SetDemuxer,
- base::Unretained(receiver_), base::Unretained(this)));
+ FROM_HERE, base::BindOnce(&DemuxerConnector::SetDemuxer,
+ base::Unretained(demuxer_connector_),
+ base::Unretained(this)));
}
void CastStreamingDemuxer::AbortPendingReads() {
diff --git a/chromium/components/cast_streaming/renderer/cast_streaming_demuxer.h b/chromium/components/cast_streaming/renderer/cast_streaming_demuxer.h
index 1f5dba592bb..8a7585ba114 100644
--- a/chromium/components/cast_streaming/renderer/cast_streaming_demuxer.h
+++ b/chromium/components/cast_streaming/renderer/cast_streaming_demuxer.h
@@ -5,7 +5,7 @@
#ifndef COMPONENTS_CAST_STREAMING_RENDERER_CAST_STREAMING_DEMUXER_H_
#define COMPONENTS_CAST_STREAMING_RENDERER_CAST_STREAMING_DEMUXER_H_
-#include "components/cast_streaming/public/mojom/cast_streaming_session.mojom.h"
+#include "components/cast_streaming/public/mojom/demuxer_connector.mojom.h"
#include "media/base/demuxer.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@@ -17,9 +17,9 @@ class SingleThreadTaskRunner;
namespace cast_streaming {
-class CastStreamingReceiver;
class CastStreamingAudioDemuxerStream;
class CastStreamingVideoDemuxerStream;
+class DemuxerConnector;
// media::Demuxer implementation for a Cast Streaming Receiver.
// This object is instantiated on the main thread, whose task runner is stored
@@ -27,12 +27,12 @@ class CastStreamingVideoDemuxerStream;
// on the main thread. Every other method is called on the media thread, whose
// task runner is |media_task_runner_|.
// TODO(crbug.com/1082821): Simplify the CastStreamingDemuxer initialization
-// sequence when the CastStreamingReceiver Component has been implemented.
+// sequence when the DemuxerConnector Component has been implemented.
class CastStreamingDemuxer final : public media::Demuxer {
public:
CastStreamingDemuxer(
- CastStreamingReceiver* receiver,
- const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner);
+ DemuxerConnector* demuxer_connector,
+ scoped_refptr<base::SingleThreadTaskRunner> media_task_runner);
~CastStreamingDemuxer() override;
CastStreamingDemuxer(const CastStreamingDemuxer&) = delete;
@@ -81,7 +81,7 @@ class CastStreamingDemuxer final : public media::Demuxer {
// Set to true if the Demuxer was successfully initialized.
bool was_initialization_successful_ = false;
media::PipelineStatusCallback initialized_cb_;
- CastStreamingReceiver* const receiver_;
+ DemuxerConnector* const demuxer_connector_;
base::WeakPtrFactory<CastStreamingDemuxer> weak_factory_;
};
diff --git a/chromium/components/cast_streaming/renderer/cast_streaming_receiver.h b/chromium/components/cast_streaming/renderer/cast_streaming_receiver.h
deleted file mode 100644
index 476580b334d..00000000000
--- a/chromium/components/cast_streaming/renderer/cast_streaming_receiver.h
+++ /dev/null
@@ -1,71 +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 COMPONENTS_CAST_STREAMING_RENDERER_CAST_STREAMING_RECEIVER_H_
-#define COMPONENTS_CAST_STREAMING_RENDERER_CAST_STREAMING_RECEIVER_H_
-
-#include "base/callback.h"
-#include "base/sequence_checker.h"
-#include "components/cast_streaming/public/mojom/cast_streaming_session.mojom.h"
-#include "mojo/public/cpp/bindings/associated_receiver.h"
-#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
-
-namespace cast_streaming {
-
-class CastStreamingDemuxer;
-
-// Handles the Cast Streaming Session lifetime in the renderer process.
-// Owned by WebEngineRenderFrameObserver, this object will be destroyed on
-// RenderFrame destruction. This is guaranteed to outlive the
-// CastStreamingDemuxer that uses it as the RenderFrame destruction will have
-// triggered the destruction of the media pipeline and the CastStreamingDemuxer
-// before the call to content::RenderFrameObserver::OnDestruct(), which triggers
-// this object destruction.
-class CastStreamingReceiver final : public mojom::CastStreamingReceiver {
- public:
- using PendingCastStreamingReceiver =
- mojo::PendingAssociatedReceiver<mojom::CastStreamingReceiver>;
- using InterfaceRegistryBinderCallback = base::RepeatingCallback<void(
- CastStreamingReceiver::PendingCastStreamingReceiver)>;
-
- explicit CastStreamingReceiver(
- base::OnceCallback<void(InterfaceRegistryBinderCallback)>
- interface_binder_factory);
- ~CastStreamingReceiver() override;
-
- CastStreamingReceiver(const CastStreamingReceiver&) = delete;
- CastStreamingReceiver& operator=(const CastStreamingReceiver&) = delete;
-
- void SetDemuxer(CastStreamingDemuxer* demuxer);
- void OnDemuxerDestroyed();
-
- // Returns true if a Mojo connection is active.
- bool IsBound() const;
-
- private:
- void BindToReceiver(PendingCastStreamingReceiver receiver);
-
- void MaybeCallEnableReceiverCallback();
-
- void OnReceiverDisconnected();
-
- // mojom::CastStreamingReceiver implementation.
- void EnableReceiver(EnableReceiverCallback callback) override;
- void OnStreamsInitialized(
- mojom::AudioStreamInitializationInfoPtr audio_stream_info,
- mojom::VideoStreamInitializationInfoPtr video_stream_info) override;
-
- mojo::AssociatedReceiver<mojom::CastStreamingReceiver>
- cast_streaming_receiver_receiver_{this};
-
- EnableReceiverCallback enable_receiver_callback_;
- CastStreamingDemuxer* demuxer_ = nullptr;
- bool is_demuxer_initialized_ = false;
-
- SEQUENCE_CHECKER(sequence_checker_);
-};
-
-} // namespace cast_streaming
-
-#endif // COMPONENTS_CAST_STREAMING_RENDERER_CAST_STREAMING_RECEIVER_H_
diff --git a/chromium/components/cast_streaming/renderer/cast_streaming_render_frame_observer.cc b/chromium/components/cast_streaming/renderer/cast_streaming_render_frame_observer.cc
deleted file mode 100644
index 7da8603dcc1..00000000000
--- a/chromium/components/cast_streaming/renderer/cast_streaming_render_frame_observer.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2021 The Chromium 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/cast_streaming/renderer/cast_streaming_render_frame_observer.h"
-
-#include "base/bind.h"
-#include "components/cast_streaming/renderer/cast_streaming_receiver.h"
-#include "content/public/renderer/render_frame.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
-
-namespace cast_streaming {
-namespace {
-
-void BindInterface(
- content::RenderFrame* render_frame,
- cast_streaming::CastStreamingReceiver::InterfaceRegistryBinderCallback cb) {
- DCHECK(render_frame);
- render_frame->GetAssociatedInterfaceRegistry()->AddInterface(std::move(cb));
-}
-
-} // namespace
-
-CastStreamingRenderFrameObserver::CastStreamingRenderFrameObserver(
- content::RenderFrame* render_frame,
- base::OnceCallback<void(int)> on_render_frame_deleted_callback)
- : content::RenderFrameObserver(render_frame),
- cast_streaming_receiver_(
- base::BindOnce(&BindInterface, base::Unretained(render_frame))),
- on_render_frame_deleted_callback_(
- std::move(on_render_frame_deleted_callback)) {
- DCHECK(render_frame);
- DCHECK(on_render_frame_deleted_callback_);
-}
-
-CastStreamingRenderFrameObserver::~CastStreamingRenderFrameObserver() = default;
-
-void CastStreamingRenderFrameObserver::OnDestruct() {
- std::move(on_render_frame_deleted_callback_).Run(routing_id());
-}
-
-} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/renderer/cast_streaming_render_frame_observer.h b/chromium/components/cast_streaming/renderer/cast_streaming_render_frame_observer.h
deleted file mode 100644
index f6ff76527ac..00000000000
--- a/chromium/components/cast_streaming/renderer/cast_streaming_render_frame_observer.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2021 The Chromium 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 COMPONENTS_CAST_STREAMING_RENDERER_CAST_STREAMING_RENDER_FRAME_OBSERVER_H_
-#define COMPONENTS_CAST_STREAMING_RENDERER_CAST_STREAMING_RENDER_FRAME_OBSERVER_H_
-
-#include "base/callback.h"
-#include "components/cast_streaming/renderer/cast_streaming_receiver.h"
-#include "content/public/renderer/render_frame_observer.h"
-
-namespace content {
-class RenderFrame;
-} // namespace content
-
-namespace cast_streaming {
-
-// This class owns the CastStreamingReceiver and ties its lifespan to a
-// RenderFrame. Owned by CastStreamingContentRendererClient, this object will be
-// destroyed on RenderFrame destruction, triggering the destruction of of the
-// CastStreamingReceiver.
-class CastStreamingRenderFrameObserver : public content::RenderFrameObserver {
- public:
- // |on_render_frame_deleted_callback| must delete |this|.
- CastStreamingRenderFrameObserver(
- content::RenderFrame* render_frame,
- base::OnceCallback<void(int)> on_render_frame_deleted_callback);
- ~CastStreamingRenderFrameObserver() override;
-
- CastStreamingRenderFrameObserver(const CastStreamingRenderFrameObserver&) =
- delete;
- CastStreamingRenderFrameObserver& operator=(
- const CastStreamingRenderFrameObserver&) = delete;
-
- CastStreamingReceiver* cast_streaming_receiver() {
- return &cast_streaming_receiver_;
- }
-
- private:
- // content::RenderFrameObserver implementation.
- void OnDestruct() override;
-
- CastStreamingReceiver cast_streaming_receiver_;
-
- base::OnceCallback<void(int)> on_render_frame_deleted_callback_;
-};
-
-} // namespace cast_streaming
-
-#endif // COMPONENTS_CAST_STREAMING_RENDERER_CAST_STREAMING_RENDER_FRAME_OBSERVER_H_
diff --git a/chromium/components/cast_streaming/renderer/decoder_buffer_reader.cc b/chromium/components/cast_streaming/renderer/decoder_buffer_reader.cc
index 16670a49826..ea2cbbb0d69 100644
--- a/chromium/components/cast_streaming/renderer/decoder_buffer_reader.cc
+++ b/chromium/components/cast_streaming/renderer/decoder_buffer_reader.cc
@@ -17,6 +17,14 @@ DecoderBufferReader::DecoderBufferReader(
DCHECK(new_buffer_cb_);
}
+DecoderBufferReader::DecoderBufferReader(
+ DecoderBufferReader&& other,
+ mojo::ScopedDataPipeConsumerHandle data_pipe)
+ : DecoderBufferReader(std::move(other.new_buffer_cb_),
+ std::move(data_pipe)) {
+ is_read_pending_ = other.is_read_pending_;
+}
+
DecoderBufferReader::~DecoderBufferReader() = default;
void DecoderBufferReader::ProvideBuffer(media::mojom::DecoderBufferPtr buffer) {
diff --git a/chromium/components/cast_streaming/renderer/decoder_buffer_reader.h b/chromium/components/cast_streaming/renderer/decoder_buffer_reader.h
index a4cf24815a8..64db866a2c0 100644
--- a/chromium/components/cast_streaming/renderer/decoder_buffer_reader.h
+++ b/chromium/components/cast_streaming/renderer/decoder_buffer_reader.h
@@ -25,6 +25,8 @@ class DecoderBufferReader {
DecoderBufferReader(NewBufferCb new_buffer_cb,
mojo::ScopedDataPipeConsumerHandle data_pipe);
+ DecoderBufferReader(DecoderBufferReader&& other,
+ mojo::ScopedDataPipeConsumerHandle data_pipe);
~DecoderBufferReader();
// Queues up a DecodeBuffer to be read. The contents of this buffer will be
diff --git a/chromium/components/cast_streaming/renderer/cast_streaming_receiver.cc b/chromium/components/cast_streaming/renderer/demuxer_connector.cc
index d31e8609a57..02831d31778 100644
--- a/chromium/components/cast_streaming/renderer/cast_streaming_receiver.cc
+++ b/chromium/components/cast_streaming/renderer/demuxer_connector.cc
@@ -2,32 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/cast_streaming/renderer/cast_streaming_receiver.h"
+#include "components/cast_streaming/renderer/demuxer_connector.h"
#include "components/cast_streaming/renderer/cast_streaming_demuxer.h"
namespace cast_streaming {
-CastStreamingReceiver::CastStreamingReceiver(
- base::OnceCallback<
- void(CastStreamingReceiver::InterfaceRegistryBinderCallback)>
- interface_binder_factory) {
- DVLOG(1) << __func__;
- DCHECK(interface_binder_factory);
-
- // It is fine to use an unretained pointer to |this| here as the
- // AssociatedInterfaceRegistry, owned by |render_frame| will be torn-down at
- // the same time as |this|.
- std::move(interface_binder_factory)
- .Run(base::BindRepeating(&CastStreamingReceiver::BindToReceiver,
- base::Unretained(this)));
-}
+DemuxerConnector::DemuxerConnector() = default;
-CastStreamingReceiver::~CastStreamingReceiver() {
+DemuxerConnector::~DemuxerConnector() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
-void CastStreamingReceiver::SetDemuxer(CastStreamingDemuxer* demuxer) {
+void DemuxerConnector::SetDemuxer(CastStreamingDemuxer* demuxer) {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(demuxer);
@@ -53,38 +40,38 @@ void CastStreamingReceiver::SetDemuxer(CastStreamingDemuxer* demuxer) {
}
}
-void CastStreamingReceiver::OnDemuxerDestroyed() {
+void DemuxerConnector::OnDemuxerDestroyed() {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(demuxer_);
demuxer_ = nullptr;
is_demuxer_initialized_ = false;
- cast_streaming_receiver_receiver_.reset();
+ demuxer_connector_receiver_.reset();
}
-void CastStreamingReceiver::BindToReceiver(
- mojo::PendingAssociatedReceiver<mojom::CastStreamingReceiver> receiver) {
+void DemuxerConnector::BindReceiver(
+ mojo::PendingAssociatedReceiver<mojom::DemuxerConnector> receiver) {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(!cast_streaming_receiver_receiver_.is_bound());
+ DCHECK(!demuxer_connector_receiver_.is_bound());
- cast_streaming_receiver_receiver_.Bind(std::move(receiver));
+ demuxer_connector_receiver_.Bind(std::move(receiver));
// Mojo service disconnection means the Cast Streaming Session ended or the
// Cast Streaming Sender disconnected.
- cast_streaming_receiver_receiver_.set_disconnect_handler(base::BindOnce(
- &CastStreamingReceiver::OnReceiverDisconnected, base::Unretained(this)));
+ demuxer_connector_receiver_.set_disconnect_handler(base::BindOnce(
+ &DemuxerConnector::OnReceiverDisconnected, base::Unretained(this)));
}
-bool CastStreamingReceiver::IsBound() const {
+bool DemuxerConnector::IsBound() const {
DVLOG(2) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return cast_streaming_receiver_receiver_.is_bound();
+ return demuxer_connector_receiver_.is_bound();
}
-void CastStreamingReceiver::MaybeCallEnableReceiverCallback() {
+void DemuxerConnector::MaybeCallEnableReceiverCallback() {
DVLOG(2) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -92,11 +79,11 @@ void CastStreamingReceiver::MaybeCallEnableReceiverCallback() {
std::move(enable_receiver_callback_).Run();
}
-void CastStreamingReceiver::OnReceiverDisconnected() {
+void DemuxerConnector::OnReceiverDisconnected() {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- cast_streaming_receiver_receiver_.reset();
+ demuxer_connector_receiver_.reset();
enable_receiver_callback_.Reset();
if (demuxer_ && !is_demuxer_initialized_) {
@@ -105,7 +92,7 @@ void CastStreamingReceiver::OnReceiverDisconnected() {
}
}
-void CastStreamingReceiver::EnableReceiver(EnableReceiverCallback callback) {
+void DemuxerConnector::EnableReceiver(EnableReceiverCallback callback) {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!enable_receiver_callback_);
@@ -115,7 +102,7 @@ void CastStreamingReceiver::EnableReceiver(EnableReceiverCallback callback) {
MaybeCallEnableReceiverCallback();
}
-void CastStreamingReceiver::OnStreamsInitialized(
+void DemuxerConnector::OnStreamsInitialized(
mojom::AudioStreamInitializationInfoPtr audio_stream_info,
mojom::VideoStreamInitializationInfoPtr video_stream_info) {
DVLOG(1) << __func__;
diff --git a/chromium/components/cast_streaming/renderer/demuxer_connector.h b/chromium/components/cast_streaming/renderer/demuxer_connector.h
new file mode 100644
index 00000000000..75e54794ec6
--- /dev/null
+++ b/chromium/components/cast_streaming/renderer/demuxer_connector.h
@@ -0,0 +1,64 @@
+// 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 COMPONENTS_CAST_STREAMING_RENDERER_DEMUXER_CONNECTOR_H_
+#define COMPONENTS_CAST_STREAMING_RENDERER_DEMUXER_CONNECTOR_H_
+
+#include "base/callback.h"
+#include "base/sequence_checker.h"
+#include "components/cast_streaming/public/mojom/demuxer_connector.mojom.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+
+namespace cast_streaming {
+
+class CastStreamingDemuxer;
+
+// Handles initiating the streaming session between the browser-process sender
+// and renderer-process receiver of the Cast Streaming Session. Specifically,
+// this class manages the DemuxerConnector's lifetime in the renderer
+// process. The lifetime of this object should match that of |render_frame| with
+// which it is associated, and is guaranteed to outlive the CastStreamingDemuxer
+// that uses it, as the RenderFrame destruction will have triggered its
+// destruction first.
+class DemuxerConnector final : public mojom::DemuxerConnector {
+ public:
+ DemuxerConnector();
+ ~DemuxerConnector() override;
+ DemuxerConnector(const DemuxerConnector&) = delete;
+ DemuxerConnector& operator=(const DemuxerConnector&) = delete;
+
+ void BindReceiver(
+ mojo::PendingAssociatedReceiver<mojom::DemuxerConnector> connector);
+
+ void SetDemuxer(CastStreamingDemuxer* demuxer);
+ void OnDemuxerDestroyed();
+
+ // Returns true if a Mojo connection is active.
+ bool IsBound() const;
+
+ private:
+ void MaybeCallEnableReceiverCallback();
+
+ void OnReceiverDisconnected();
+
+ // mojom::DemuxerConnector implementation.
+ void EnableReceiver(EnableReceiverCallback callback) override;
+ void OnStreamsInitialized(
+ mojom::AudioStreamInitializationInfoPtr audio_stream_info,
+ mojom::VideoStreamInitializationInfoPtr video_stream_info) override;
+
+ mojo::AssociatedReceiver<mojom::DemuxerConnector> demuxer_connector_receiver_{
+ this};
+
+ EnableReceiverCallback enable_receiver_callback_;
+ CastStreamingDemuxer* demuxer_ = nullptr;
+ bool is_demuxer_initialized_ = false;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+};
+
+} // namespace cast_streaming
+
+#endif // COMPONENTS_CAST_STREAMING_RENDERER_DEMUXER_CONNECTOR_H_
diff --git a/chromium/components/cast_streaming/renderer/demuxer_provider.cc b/chromium/components/cast_streaming/renderer/demuxer_provider.cc
deleted file mode 100644
index 6bad5b85573..00000000000
--- a/chromium/components/cast_streaming/renderer/demuxer_provider.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2021 The Chromium 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/cast_streaming/renderer/public/demuxer_provider.h"
-
-#include "base/task/single_thread_task_runner.h"
-#include "components/cast_streaming/public/cast_streaming_url.h"
-#include "components/cast_streaming/renderer/cast_streaming_demuxer.h"
-#include "components/cast_streaming/renderer/cast_streaming_render_frame_observer.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_view.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
-#include "third_party/blink/public/web/web_view.h"
-
-namespace cast_streaming {
-
-DemuxerProvider::DemuxerProvider() = default;
-
-DemuxerProvider::~DemuxerProvider() = default;
-
-void DemuxerProvider::OnRenderFrameDeleted(int render_frame_id) {
- size_t count = render_frame_id_to_observer_map_.erase(render_frame_id);
- DCHECK_EQ(count, 1u);
-}
-
-void DemuxerProvider::RenderFrameCreated(content::RenderFrame* render_frame) {
- int render_frame_id = render_frame->GetRoutingID();
- auto render_frame_observer =
- std::make_unique<CastStreamingRenderFrameObserver>(
- render_frame, base::BindOnce(&DemuxerProvider::OnRenderFrameDeleted,
- base::Unretained(this)));
- auto render_frame_observer_iter = render_frame_id_to_observer_map_.emplace(
- render_frame_id, std::move(render_frame_observer));
- DCHECK(render_frame_observer_iter.second);
-}
-
-std::unique_ptr<media::Demuxer> DemuxerProvider::OverrideDemuxerForUrl(
- content::RenderFrame* render_frame,
- const GURL& url,
- scoped_refptr<base::SingleThreadTaskRunner> media_task_runner) {
- if (IsCastStreamingMediaSourceUrl(url)) {
- auto iter =
- render_frame_id_to_observer_map_.find(render_frame->GetRoutingID());
- DCHECK(iter != render_frame_id_to_observer_map_.end());
-
- // Do not create a CastStreamingDemuxer if the Cast Streaming MessagePort
- // was not set in the browser process. This will manifest as an unbound
- // CastStreamingReceiver object in the renderer process.
- // TODO(crbug.com/1082821): Simplify the instantiation conditions for the
- // CastStreamingDemuxer.
- if (iter->second->cast_streaming_receiver()->IsBound()) {
- return std::make_unique<cast_streaming::CastStreamingDemuxer>(
- iter->second->cast_streaming_receiver(), media_task_runner);
- }
- }
-
- return nullptr;
-}
-
-} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc b/chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc
index 58fee2c8bf9..0755de12d01 100644
--- a/chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc
+++ b/chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc
@@ -255,6 +255,11 @@ void PlaybackCommandForwardingRenderer::OnError(media::PipelineStatus status) {
upstream_renderer_client_->OnError(status);
}
+void PlaybackCommandForwardingRenderer::OnFallback(
+ media::PipelineStatus status) {
+ NOTREACHED();
+}
+
void PlaybackCommandForwardingRenderer::OnEnded() {
DCHECK(task_runner_->BelongsToCurrentThread());
diff --git a/chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.h b/chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.h
index f1614b1f6c1..da5eb6d55e6 100644
--- a/chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.h
+++ b/chromium/components/cast_streaming/renderer/playback_command_forwarding_renderer.h
@@ -90,6 +90,7 @@ class PlaybackCommandForwardingRenderer : public media::Renderer,
// Each of these simply forwards the call to both |remote_renderer_client_|
// and |upstream_renderer_client_|.
void OnError(media::PipelineStatus status) override;
+ void OnFallback(media::PipelineStatus status) override;
void OnEnded() override;
void OnStatisticsUpdate(const media::PipelineStatistics& stats) override;
void OnBufferingStateChange(
diff --git a/chromium/components/cast_streaming/renderer/public/demuxer_provider.h b/chromium/components/cast_streaming/renderer/public/demuxer_provider.h
deleted file mode 100644
index 68473a77b44..00000000000
--- a/chromium/components/cast_streaming/renderer/public/demuxer_provider.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2021 The Chromium 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 COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_DEMUXER_PROVIDER_H_
-#define COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_DEMUXER_PROVIDER_H_
-
-#include <map>
-#include <memory>
-
-#include "base/memory/scoped_refptr.h"
-#include "url/gurl.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace content {
-class RenderFrame;
-} // namespace content
-
-namespace media {
-class Demuxer;
-} // namespace media
-
-namespace cast_streaming {
-
-class CastStreamingRenderFrameObserver;
-
-// Provides a media::Demuxer for a Cast Streaming receiver.
-// Both public methods of this class should be called from the method of
-// matching name in the embedder's ContentRendererClient implementation.
-class DemuxerProvider {
- public:
- DemuxerProvider();
- ~DemuxerProvider();
-
- DemuxerProvider(const DemuxerProvider&) = delete;
- DemuxerProvider& operator=(const DemuxerProvider&) = delete;
-
- // Associates |render_frame| with a CastStreamingReceiver.
- void RenderFrameCreated(content::RenderFrame* render_frame);
-
- // Checks the |url| against a predefined constant, providing a
- // CastStreamingDemuxer instance in the case of a match.
- std::unique_ptr<media::Demuxer> OverrideDemuxerForUrl(
- content::RenderFrame* render_frame,
- const GURL& url,
- scoped_refptr<base::SingleThreadTaskRunner> media_task_runner);
-
- private:
- // Called by CastStreamingRenderFrameObserver when its corresponding
- // RenderFrame is in the process of being deleted.
- void OnRenderFrameDeleted(int render_frame_id);
-
- // Map of RenderFrame ID to CastStreamingRenderFrameObserver.
- std::map<int, std::unique_ptr<CastStreamingRenderFrameObserver>>
- render_frame_id_to_observer_map_;
-};
-
-} // namespace cast_streaming
-
-#endif // COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_DEMUXER_PROVIDER_H_
diff --git a/chromium/components/cast_streaming/renderer/public/renderer_controller_proxy.h b/chromium/components/cast_streaming/renderer/public/renderer_controller_proxy.h
deleted file mode 100644
index 2312254fc0b..00000000000
--- a/chromium/components/cast_streaming/renderer/public/renderer_controller_proxy.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2021 The Chromium 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 COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RENDERER_CONTROLLER_PROXY_H_
-#define COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RENDERER_CONTROLLER_PROXY_H_
-
-#include "base/callback.h"
-#include "components/cast_streaming/public/mojom/renderer_controller.mojom.h"
-#include "media/mojo/mojom/renderer.mojom.h"
-#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-
-namespace content {
-class RenderFrame;
-} // namespace content
-
-namespace cast_streaming {
-
-// This class acts as a hub through which mojom::RendererController calls may be
-// received from the browser process and then be dispatched to elsewhere in the
-// renderer process. This eliminates any timing concerns associated with
-// initialization of the intended receiver for this mojo pipe and instantiation
-// of the source in the browser thread. Without this class, there is no
-// deterministic way to bind the PendingAssociatedReceiver received from the
-// Browser thread to the caller of the GetReceiver() method, if this call were
-// to happen after the browser interface is bound.
-//
-// This class is intended to be a singleton, owned by the ContentRendererClient.
-// For this reason, this class is assumed to outlive all RenderFrames with which
-// it interacts.
-class RendererControllerProxy {
- public:
- static RendererControllerProxy* GetInstance();
-
- virtual ~RendererControllerProxy();
-
- // Captures the receiving end of a mojom::RendererController sent to a
- // specific |frame|.
- virtual base::RepeatingCallback<
- void(mojo::PendingAssociatedReceiver<mojom::RendererController>)>
- GetBinder(content::RenderFrame* frame) = 0;
-
- // Gets the receiver associated with a given |frame|.
- virtual mojo::PendingReceiver<media::mojom::Renderer> GetReceiver(
- content::RenderFrame* frame) = 0;
-
- protected:
- RendererControllerProxy();
-
- private:
- static RendererControllerProxy* singleton_instance_;
-};
-
-} // namespace cast_streaming
-
-#endif // COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RENDERER_CONTROLLER_PROXY_H_
diff --git a/chromium/components/cast_streaming/renderer/public/renderer_controller_proxy_factory.h b/chromium/components/cast_streaming/renderer/public/renderer_controller_proxy_factory.h
deleted file mode 100644
index fffa2107a8a..00000000000
--- a/chromium/components/cast_streaming/renderer/public/renderer_controller_proxy_factory.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2021 The Chromium 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 COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RENDERER_CONTROLLER_PROXY_FACTORY_H_
-#define COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RENDERER_CONTROLLER_PROXY_FACTORY_H_
-
-#include <memory>
-
-namespace cast_streaming {
-
-class RendererControllerProxy;
-
-// Creates a new RendererControllerProxy. May only be called once.
-std::unique_ptr<RendererControllerProxy> CreateRendererControllerProxy();
-
-} // namespace cast_streaming
-
-#endif // COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RENDERER_CONTROLLER_PROXY_FACTORY_H_
diff --git a/chromium/components/cast_streaming/renderer/public/resource_provider.h b/chromium/components/cast_streaming/renderer/public/resource_provider.h
new file mode 100644
index 00000000000..0c3eadfde53
--- /dev/null
+++ b/chromium/components/cast_streaming/renderer/public/resource_provider.h
@@ -0,0 +1,75 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RESOURCE_PROVIDER_H_
+#define COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RESOURCE_PROVIDER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "components/cast_streaming/public/mojom/demuxer_connector.mojom.h"
+#include "components/cast_streaming/public/mojom/renderer_controller.mojom.h"
+#include "media/mojo/mojom/renderer.mojom.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "url/gurl.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace media {
+class Demuxer;
+} // namespace media
+
+namespace cast_streaming {
+
+// This class is responsible for initiating all per-RenderFrame mojo connections
+// with the browser process, as required to render cast streaming media. This
+// includes:
+// - The DemuxerConnector API, feeding frames to the media::Demuxer instantiated
+// through this instance's MaybeGetDemuxerOverride() method.
+// - The RendererController API, for sending media::Renderer commands from the
+// browser process, both for use when remoting or for the embedder to inject
+// playback commands.
+// Additionally, this class provides the hooks required for using these mojo
+// APIs.
+class ResourceProvider {
+ public:
+ template <typename TMojoInterfaceType>
+ using ReceiverBinder = base::RepeatingCallback<void(
+ mojo::PendingAssociatedReceiver<TMojoInterfaceType>)>;
+
+ virtual ~ResourceProvider();
+
+ ResourceProvider(const ResourceProvider&) = delete;
+ ResourceProvider& operator=(const ResourceProvider&) = delete;
+
+ // Gets the binder to be used by the AssociatedInterfaceRegistry to pass
+ // browser-process mojo API endpoints to this class.
+ virtual ReceiverBinder<mojom::RendererController>
+ GetRendererControllerBinder() = 0;
+ virtual ReceiverBinder<mojom::DemuxerConnector>
+ GetDemuxerConnectorBinder() = 0;
+
+ // Checks the |url| against a predefined constant, providing a
+ // CastStreamingDemuxer instance in the case of a match.
+ virtual std::unique_ptr<media::Demuxer> MaybeGetDemuxerOverride(
+ const GURL& url,
+ scoped_refptr<base::SingleThreadTaskRunner> media_task_runner) = 0;
+
+ // Gets the receiver for this instance. To be used by the renderer-process
+ // PlaybackCommandForwardingRenderer to receive playback commands from the
+ // browser.
+ virtual mojo::PendingReceiver<media::mojom::Renderer>
+ GetRendererCommandReceiver() = 0;
+
+ protected:
+ ResourceProvider();
+};
+
+} // namespace cast_streaming
+
+#endif // COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RESOURCE_PROVIDER_H_
diff --git a/chromium/components/cast_streaming/renderer/public/resource_provider_factory.h b/chromium/components/cast_streaming/renderer/public/resource_provider_factory.h
new file mode 100644
index 00000000000..410320511aa
--- /dev/null
+++ b/chromium/components/cast_streaming/renderer/public/resource_provider_factory.h
@@ -0,0 +1,18 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RESOURCE_PROVIDER_FACTORY_H_
+#define COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RESOURCE_PROVIDER_FACTORY_H_
+
+#include <memory>
+
+namespace cast_streaming {
+
+class ResourceProvider;
+
+std::unique_ptr<ResourceProvider> CreateResourceProvider();
+
+} // namespace cast_streaming
+
+#endif // COMPONENTS_CAST_STREAMING_RENDERER_PUBLIC_RESOURCE_PROVIDER_FACTORY_H_
diff --git a/chromium/components/cast_streaming/renderer/public/wrapping_renderer_factory_selector.h b/chromium/components/cast_streaming/renderer/public/wrapping_renderer_factory_selector.h
index 617562885d9..a2939a207db 100644
--- a/chromium/components/cast_streaming/renderer/public/wrapping_renderer_factory_selector.h
+++ b/chromium/components/cast_streaming/renderer/public/wrapping_renderer_factory_selector.h
@@ -9,12 +9,9 @@
#include "media/base/renderer_factory_selector.h"
-namespace content {
-class RenderFrame;
-}
-
namespace cast_streaming {
+class ResourceProvider;
class PlaybackCommandForwardingRendererFactory;
// This class provides an implementation of RendererFactorySelector to be used
@@ -25,11 +22,10 @@ class PlaybackCommandForwardingRendererFactory;
// GetCurrentRendererType() always returns kCastStreaming.
class WrappingRendererFactorySelector : public media::RendererFactorySelector {
public:
- explicit WrappingRendererFactorySelector(content::RenderFrame* render_frame);
+ explicit WrappingRendererFactorySelector(ResourceProvider* resource_provider);
~WrappingRendererFactorySelector() override;
// media::RendererFactorySelector overrides.
- media::RendererType GetCurrentRendererType() override;
media::RendererFactory* GetCurrentFactory() override;
private:
diff --git a/chromium/components/cast_streaming/renderer/renderer_controller_proxy.cc b/chromium/components/cast_streaming/renderer/renderer_controller_proxy.cc
index 577d7cf752c..cb9b87a9fc4 100644
--- a/chromium/components/cast_streaming/renderer/renderer_controller_proxy.cc
+++ b/chromium/components/cast_streaming/renderer/renderer_controller_proxy.cc
@@ -2,26 +2,58 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/cast_streaming/renderer/public/renderer_controller_proxy.h"
+#include "components/cast_streaming/renderer/renderer_controller_proxy.h"
+
+#include <utility>
+
+#include "base/logging.h"
namespace cast_streaming {
-// static
-RendererControllerProxy* RendererControllerProxy::singleton_instance_ = nullptr;
+RendererControllerProxy::RendererControllerProxy(
+ MojoDisconnectCB disconnection_handler)
+ : renderer_process_pending_receiver_(
+ renderer_process_remote_.InitWithNewPipeAndPassReceiver()),
+ browser_process_receiver_(this),
+ on_mojo_disconnection_(std::move(disconnection_handler)) {}
+
+RendererControllerProxy::~RendererControllerProxy() = default;
-// static
-RendererControllerProxy* RendererControllerProxy::GetInstance() {
- return singleton_instance_;
+void RendererControllerProxy::BindRendererController(
+ mojo::PendingAssociatedReceiver<mojom::RendererController>
+ pending_receiver) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(on_mojo_disconnection_);
+ browser_process_receiver_.Bind(std::move(pending_receiver));
+ browser_process_receiver_.set_disconnect_handler(
+ std::move(on_mojo_disconnection_));
}
-RendererControllerProxy::RendererControllerProxy() {
- DCHECK(!singleton_instance_);
- singleton_instance_ = this;
+mojo::PendingReceiver<media::mojom::Renderer>
+RendererControllerProxy::GetReceiver() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(renderer_process_pending_receiver_);
+
+ if (set_playback_controller_cb_) {
+ std::move(set_playback_controller_cb_).Run();
+ }
+
+ return std::move(renderer_process_pending_receiver_);
}
-RendererControllerProxy::~RendererControllerProxy() {
- DCHECK(singleton_instance_);
- singleton_instance_ = nullptr;
+void RendererControllerProxy::SetPlaybackController(
+ mojo::PendingReceiver<media::mojom::Renderer>
+ browser_process_renderer_controls,
+ SetPlaybackControllerCallback callback) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!set_playback_controller_cb_);
+ set_playback_controller_cb_ = std::move(callback);
+
+ CHECK(mojo::FusePipes(std::move(browser_process_renderer_controls),
+ std::move(renderer_process_remote_)));
+ if (!renderer_process_pending_receiver_) {
+ std::move(set_playback_controller_cb_).Run();
+ }
}
} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/renderer/renderer_controller_proxy.h b/chromium/components/cast_streaming/renderer/renderer_controller_proxy.h
new file mode 100644
index 00000000000..ea469fdfa7a
--- /dev/null
+++ b/chromium/components/cast_streaming/renderer/renderer_controller_proxy.h
@@ -0,0 +1,73 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_CAST_STREAMING_RENDERER_RENDERER_CONTROLLER_PROXY_H_
+#define COMPONENTS_CAST_STREAMING_RENDERER_RENDERER_CONTROLLER_PROXY_H_
+
+#include "base/callback.h"
+#include "base/threading/thread_checker.h"
+#include "components/cast_streaming/public/mojom/renderer_controller.mojom.h"
+#include "media/mojo/mojom/renderer.mojom.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace cast_streaming {
+
+// This class serves the purpose of allowing both the browser and renderer
+// processes to connect media::mojom::Renderer receiver and remote endpoints
+// without concerns of creation order or race conditions becoming concerns.
+class RendererControllerProxy : public mojom::RendererController {
+ public:
+ using MojoDisconnectCB = base::OnceCallback<void()>;
+
+ explicit RendererControllerProxy(MojoDisconnectCB disconnection_handler);
+ ~RendererControllerProxy() override;
+
+ // Binds the frame-specific receiver from the browser process.
+ void BindRendererController(
+ mojo::PendingAssociatedReceiver<mojom::RendererController>
+ pending_receiver);
+
+ // Gets the receiver associated with the RenderFrame associated with this
+ // instance.
+ mojo::PendingReceiver<media::mojom::Renderer> GetReceiver();
+
+ private:
+ // mojom::RendererController overrides.
+ //
+ // Fuses |browser_process_renderer_controls| received from the browser
+ // process with |renderer_process_remote_|, so that commands sent by the
+ // browser process are passed immediately to
+ // |renderer_process_pending_receiver_|.
+ void SetPlaybackController(mojo::PendingReceiver<media::mojom::Renderer>
+ browser_process_renderer_controls,
+ SetPlaybackControllerCallback callback) override;
+
+ // Callback from the SetPlaybackController() method. Returned when the
+ // receiver is taken by the Renderer which uses it.
+ SetPlaybackControllerCallback set_playback_controller_cb_;
+
+ // This handle is bound to the |renderer_process_pending_receiver_| upon
+ // class construction. When SetPlaybackController() is called, ownership of
+ // this instance will be transferred away from this class.
+ mojo::PendingRemote<media::mojom::Renderer> renderer_process_remote_;
+
+ // Owned by this class from time of creation until the first call to
+ // GetReceiver(), at which time its ownership is passed to the caller.
+ mojo::PendingReceiver<media::mojom::Renderer>
+ renderer_process_pending_receiver_;
+
+ // Receiver passed from the browser process to the renderer process.
+ mojo::AssociatedReceiver<mojom::RendererController> browser_process_receiver_;
+
+ MojoDisconnectCB on_mojo_disconnection_;
+
+ THREAD_CHECKER(thread_checker_);
+};
+
+} // namespace cast_streaming
+
+#endif // COMPONENTS_CAST_STREAMING_RENDERER_RENDERER_CONTROLLER_PROXY_H_
diff --git a/chromium/components/cast_streaming/renderer/renderer_controller_proxy_impl.cc b/chromium/components/cast_streaming/renderer/renderer_controller_proxy_impl.cc
deleted file mode 100644
index 46ff4d1dc25..00000000000
--- a/chromium/components/cast_streaming/renderer/renderer_controller_proxy_impl.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2021 The Chromium 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/cast_streaming/renderer/renderer_controller_proxy_impl.h"
-
-#include "base/bind.h"
-#include "components/cast_streaming/renderer/public/renderer_controller_proxy_factory.h"
-
-namespace cast_streaming {
-
-// static
-std::unique_ptr<RendererControllerProxy> CreateRendererControllerProxy() {
- return std::make_unique<RendererControllerProxyImpl>();
-}
-
-RendererControllerProxyImpl::RendererControllerProxyImpl() = default;
-
-RendererControllerProxyImpl::~RendererControllerProxyImpl() = default;
-
-base::RepeatingCallback<
- void(mojo::PendingAssociatedReceiver<mojom::RendererController>)>
-RendererControllerProxyImpl::GetBinder(content::RenderFrame* frame) {
- // base::Unretained is safe here because this binder is used only for
- // RenderFrame / RenderFrameHost communication, and as this class is owned by
- // the ContentRendererClient which outlives any such instances.
- return base::BindRepeating(&RendererControllerProxyImpl::BindInterface,
- base::Unretained(this), frame);
-}
-
-void RendererControllerProxyImpl::OnRenderFrameDestroyed(
- content::RenderFrame* frame) {
- DCHECK(frame);
-
- per_frame_proxies_.erase(frame);
-}
-
-mojo::PendingReceiver<media::mojom::Renderer>
-RendererControllerProxyImpl::GetReceiver(content::RenderFrame* frame) {
- DCHECK(frame);
-
- auto it = GetFrameProxy(frame);
- return it->second->GetReceiver();
-}
-
-void RendererControllerProxyImpl::BindInterface(
- content::RenderFrame* frame,
- mojo::PendingAssociatedReceiver<mojom::RendererController>
- pending_receiver) {
- DCHECK(frame);
- DCHECK(pending_receiver);
-
- auto it = GetFrameProxy(frame);
- it->second->BindReceiver(std::move(pending_receiver));
-}
-
-RendererControllerProxyImpl::RenderFrameMap::iterator
-RendererControllerProxyImpl::GetFrameProxy(content::RenderFrame* frame) {
- auto it = per_frame_proxies_.find(frame);
- if (it != per_frame_proxies_.end()) {
- return it;
- }
- auto new_proxy = std::make_unique<FrameProxy>(
- base::BindRepeating(&RendererControllerProxyImpl::OnRenderFrameDestroyed,
- base::Unretained(this), frame));
- return per_frame_proxies_.emplace_hint(it, frame, std::move(new_proxy));
-}
-
-RendererControllerProxyImpl::FrameProxy::FrameProxy(
- base::RepeatingCallback<void()> disconnection_handler)
- : renderer_process_pending_receiver_(
- renderer_process_remote_.InitWithNewPipeAndPassReceiver()),
- browser_process_receiver_(this),
- on_mojo_disconnection_(std::move(disconnection_handler)) {}
-
-RendererControllerProxyImpl::FrameProxy::~FrameProxy() = default;
-
-void RendererControllerProxyImpl::FrameProxy::BindReceiver(
- mojo::PendingAssociatedReceiver<mojom::RendererController>
- pending_receiver) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- browser_process_receiver_.Bind(std::move(pending_receiver));
- browser_process_receiver_.set_disconnect_handler(on_mojo_disconnection_);
-}
-
-mojo::PendingReceiver<media::mojom::Renderer>
-RendererControllerProxyImpl::FrameProxy::GetReceiver() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(renderer_process_pending_receiver_);
-
- if (set_playback_controller_cb_) {
- std::move(set_playback_controller_cb_).Run();
- }
-
- return std::move(renderer_process_pending_receiver_);
-}
-
-void RendererControllerProxyImpl::FrameProxy::SetPlaybackController(
- mojo::PendingReceiver<media::mojom::Renderer>
- browser_process_renderer_controls,
- SetPlaybackControllerCallback callback) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!set_playback_controller_cb_);
- set_playback_controller_cb_ = std::move(callback);
-
- CHECK(mojo::FusePipes(std::move(browser_process_renderer_controls),
- std::move(renderer_process_remote_)));
- if (!renderer_process_pending_receiver_) {
- std::move(set_playback_controller_cb_).Run();
- }
-}
-
-} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/renderer/renderer_controller_proxy_impl.h b/chromium/components/cast_streaming/renderer/renderer_controller_proxy_impl.h
deleted file mode 100644
index b54d0123d30..00000000000
--- a/chromium/components/cast_streaming/renderer/renderer_controller_proxy_impl.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2021 The Chromium 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 COMPONENTS_CAST_STREAMING_RENDERER_RENDERER_CONTROLLER_PROXY_IMPL_H_
-#define COMPONENTS_CAST_STREAMING_RENDERER_RENDERER_CONTROLLER_PROXY_IMPL_H_
-
-#include <map>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "components/cast_streaming/public/mojom/renderer_controller.mojom.h"
-#include "components/cast_streaming/renderer/public/renderer_controller_proxy.h"
-#include "media/mojo/mojom/renderer.mojom.h"
-#include "mojo/public/cpp/bindings/associated_receiver.h"
-#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace content {
-class RenderFrame;
-} // namespace content
-
-namespace cast_streaming {
-
-// This class provides an implementation of the singleton class
-// RendererControllerProxy.
-class RendererControllerProxyImpl : public RendererControllerProxy {
- public:
- RendererControllerProxyImpl();
- ~RendererControllerProxyImpl() override;
-
- // RendererControllerProxy overrides.
- base::RepeatingCallback<
- void(mojo::PendingAssociatedReceiver<mojom::RendererController>)>
- GetBinder(content::RenderFrame* frame) override;
- mojo::PendingReceiver<media::mojom::Renderer> GetReceiver(
- content::RenderFrame* frame) override;
-
- private:
- // This class serves the purpose of allowing both the browser and renderer
- // processes to connect media::mojom::Renderer receiver and remote endpoints
- // without concerns of creation order or race conditions becoming concerns.
- class FrameProxy : public mojom::RendererController {
- public:
- explicit FrameProxy(base::RepeatingCallback<void()> disconnection_handler);
- ~FrameProxy() override;
-
- // Binds the frame-specific receiver from the browser process.
- void BindReceiver(mojo::PendingAssociatedReceiver<mojom::RendererController>
- pending_receiver);
-
- // Analogous to RendererControllerProxy::GetReceiver().
- mojo::PendingReceiver<media::mojom::Renderer> GetReceiver();
-
- private:
- // mojom::RendererController overrides.
- //
- // Fuses |browser_process_renderer_controls| received from the browser
- // process with |renderer_process_remote_|, so that commands sent by the
- // browser process are passed immediately to
- // |renderer_process_pending_receiver_|.
- void SetPlaybackController(mojo::PendingReceiver<media::mojom::Renderer>
- browser_process_renderer_controls,
- SetPlaybackControllerCallback callback) override;
-
- // Callback from the SetPlaybackController() method. Returned when the
- // receiver is taken by the Renderer which uses it.
- SetPlaybackControllerCallback set_playback_controller_cb_;
-
- // This handle is bound to the |renderer_process_pending_receiver_| upon
- // class construction. When SetPlaybackController() is called, ownership of
- // this instance will be transferred away from this class.
- mojo::PendingRemote<media::mojom::Renderer> renderer_process_remote_;
-
- // Owned by this class from time of creation until the first call to
- // GetReceiver(), at which time its ownership is passed to the caller.
- mojo::PendingReceiver<media::mojom::Renderer>
- renderer_process_pending_receiver_;
-
- // Receiver passed from the browser process to the renderer process.
- mojo::AssociatedReceiver<mojom::RendererController>
- browser_process_receiver_;
-
- base::RepeatingCallback<void()> on_mojo_disconnection_;
-
- THREAD_CHECKER(thread_checker_);
- };
-
- using RenderFrameMap =
- std::map<content::RenderFrame*, std::unique_ptr<FrameProxy>>;
-
- // Returned by RendererControllerProxyImpl::GetBinder() to allow
- // for receiving browser-process sent instances of this mojo receiver
- void BindInterface(content::RenderFrame* frame,
- mojo::PendingAssociatedReceiver<mojom::RendererController>
- pending_receiver);
-
- // Helper to create a new entry in |per_frame_proxies_|.
- RenderFrameMap::iterator GetFrameProxy(content::RenderFrame* frame);
-
- void OnRenderFrameDestroyed(content::RenderFrame* frame);
-
- RenderFrameMap per_frame_proxies_;
-};
-
-} // namespace cast_streaming
-
-#endif // COMPONENTS_CAST_STREAMING_RENDERER_RENDERER_CONTROLLER_PROXY_IMPL_H_
diff --git a/chromium/components/cast_streaming/renderer/resource_provider.cc b/chromium/components/cast_streaming/renderer/resource_provider.cc
new file mode 100644
index 00000000000..83a854f4101
--- /dev/null
+++ b/chromium/components/cast_streaming/renderer/resource_provider.cc
@@ -0,0 +1,13 @@
+// Copyright 2022 The Chromium 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/cast_streaming/renderer/public/resource_provider.h"
+
+namespace cast_streaming {
+
+ResourceProvider::ResourceProvider() = default;
+
+ResourceProvider::~ResourceProvider() = default;
+
+} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/renderer/resource_provider_impl.cc b/chromium/components/cast_streaming/renderer/resource_provider_impl.cc
new file mode 100644
index 00000000000..96676a0582d
--- /dev/null
+++ b/chromium/components/cast_streaming/renderer/resource_provider_impl.cc
@@ -0,0 +1,92 @@
+// Copyright 2021 The Chromium 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/cast_streaming/renderer/resource_provider_impl.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/task/single_thread_task_runner.h"
+#include "components/cast_streaming/public/cast_streaming_url.h"
+#include "components/cast_streaming/public/features.h"
+#include "components/cast_streaming/renderer/cast_streaming_demuxer.h"
+#include "media/base/demuxer.h"
+
+namespace cast_streaming {
+
+// static
+std::unique_ptr<ResourceProvider> CreateResourceProvider() {
+ return std::make_unique<ResourceProviderImpl>();
+}
+
+ResourceProviderImpl::ResourceProviderImpl() : weak_factory_(this) {
+ per_frame_resources_ =
+ std::make_unique<PerRenderFrameResources>(base::BindOnce(
+ &ResourceProviderImpl::OnError, weak_factory_.GetWeakPtr()));
+}
+
+ResourceProviderImpl::~ResourceProviderImpl() = default;
+
+void ResourceProviderImpl::BindRendererController(
+ mojo::PendingAssociatedReceiver<mojom::RendererController> receiver) {
+ if (per_frame_resources_) {
+ per_frame_resources_->renderer_controller_proxy().BindRendererController(
+ std::move(receiver));
+ }
+}
+
+void ResourceProviderImpl::BindDemuxerConnector(
+ mojo::PendingAssociatedReceiver<mojom::DemuxerConnector> receiver) {
+ if (per_frame_resources_) {
+ per_frame_resources_->demuxer_connector().BindReceiver(std::move(receiver));
+ }
+}
+
+void ResourceProviderImpl::OnError() {
+ per_frame_resources_.reset();
+}
+
+ResourceProvider::ReceiverBinder<mojom::RendererController>
+ResourceProviderImpl::GetRendererControllerBinder() {
+ return base::BindRepeating(&ResourceProviderImpl::BindRendererController,
+ weak_factory_.GetWeakPtr());
+}
+
+ResourceProvider::ReceiverBinder<mojom::DemuxerConnector>
+ResourceProviderImpl::GetDemuxerConnectorBinder() {
+ return base::BindRepeating(&ResourceProviderImpl::BindDemuxerConnector,
+ weak_factory_.GetWeakPtr());
+}
+
+std::unique_ptr<media::Demuxer> ResourceProviderImpl::MaybeGetDemuxerOverride(
+ const GURL& url,
+ scoped_refptr<base::SingleThreadTaskRunner> media_task_runner) {
+ // Do not create a CastStreamingDemuxer if the Cast Streaming MessagePort
+ // was not set in the browser process. This will manifest as an unbound
+ // DemuxerConnector object in the renderer process.
+ // TODO(crbug.com/1082821): Simplify the instantiation conditions for the
+ // CastStreamingDemuxer.
+ if (per_frame_resources_ && IsCastStreamingMediaSourceUrl(url) &&
+ per_frame_resources_->demuxer_connector().IsBound()) {
+ return std::make_unique<CastStreamingDemuxer>(
+ &per_frame_resources_->demuxer_connector(),
+ std::move(media_task_runner));
+ }
+
+ return nullptr;
+}
+
+mojo::PendingReceiver<media::mojom::Renderer>
+ResourceProviderImpl::GetRendererCommandReceiver() {
+ DCHECK(per_frame_resources_);
+ return per_frame_resources_->renderer_controller_proxy().GetReceiver();
+}
+
+ResourceProviderImpl::PerRenderFrameResources::PerRenderFrameResources(
+ base::OnceClosure on_error)
+ : renderer_controller_proxy_(std::move(on_error)) {}
+
+ResourceProviderImpl::PerRenderFrameResources::~PerRenderFrameResources() =
+ default;
+
+} // namespace cast_streaming
diff --git a/chromium/components/cast_streaming/renderer/resource_provider_impl.h b/chromium/components/cast_streaming/renderer/resource_provider_impl.h
new file mode 100644
index 00000000000..cc2106175de
--- /dev/null
+++ b/chromium/components/cast_streaming/renderer/resource_provider_impl.h
@@ -0,0 +1,99 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_CAST_STREAMING_RENDERER_RESOURCE_PROVIDER_IMPL_H_
+#define COMPONENTS_CAST_STREAMING_RENDERER_RESOURCE_PROVIDER_IMPL_H_
+
+#include <map>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/callback_forward.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/cast_streaming/public/mojom/demuxer_connector.mojom.h"
+#include "components/cast_streaming/public/mojom/renderer_controller.mojom.h"
+#include "components/cast_streaming/renderer/demuxer_connector.h"
+#include "components/cast_streaming/renderer/public/resource_provider.h"
+#include "components/cast_streaming/renderer/renderer_controller_proxy.h"
+#include "media/mojo/mojom/renderer.mojom.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "url/gurl.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+class Demuxer;
+} // namespace media
+
+namespace cast_streaming {
+
+// Provides the per-render-frame singleton instances used to pass data and
+// playback commands received from the remote sender in the browser process to
+// the renderer process.
+class ResourceProviderImpl : public ResourceProvider {
+ public:
+ ResourceProviderImpl();
+ ~ResourceProviderImpl() override;
+
+ ResourceProviderImpl(const ResourceProviderImpl&) = delete;
+ ResourceProviderImpl& operator=(const ResourceProviderImpl&) = delete;
+
+ private:
+ // Resources to be associated with each RenderFrame instance. This class
+ // serves to tie their lifetimes to that of the |render_frame| with which
+ // they are associated, and ensures that their destruction occurs when any
+ // such resource becomes invalid.
+ class PerRenderFrameResources {
+ public:
+ // |on_error| is the callback to be provided to |renderer_controller_proxy_|
+ // as the MojoDisconnectCB. It is expected to delete this instance.
+ explicit PerRenderFrameResources(base::OnceClosure on_error);
+ ~PerRenderFrameResources();
+
+ DemuxerConnector& demuxer_connector() { return demuxer_connector_; }
+
+ RendererControllerProxy& renderer_controller_proxy() {
+ return renderer_controller_proxy_;
+ }
+
+ private:
+ // The singleton associated with forming the mojo connection used to pass
+ // DecoderBuffers from the browser process into the renderer process's
+ // DemuxerStream used by the media pipeline.
+ DemuxerConnector demuxer_connector_;
+
+ // The singleton associated with sending playback commands from the browser
+ // to the renderer process.
+ RendererControllerProxy renderer_controller_proxy_;
+ };
+
+ void BindRendererController(
+ mojo::PendingAssociatedReceiver<mojom::RendererController> receiver);
+ void BindDemuxerConnector(
+ mojo::PendingAssociatedReceiver<mojom::DemuxerConnector> receiver);
+
+ void OnError();
+
+ // ResourceProvider overrides.
+ ReceiverBinder<mojom::RendererController> GetRendererControllerBinder()
+ override;
+ ReceiverBinder<mojom::DemuxerConnector> GetDemuxerConnectorBinder() override;
+ std::unique_ptr<media::Demuxer> MaybeGetDemuxerOverride(
+ const GURL& url,
+ scoped_refptr<base::SingleThreadTaskRunner> media_task_runner) override;
+ mojo::PendingReceiver<media::mojom::Renderer> GetRendererCommandReceiver()
+ override;
+
+ std::unique_ptr<PerRenderFrameResources> per_frame_resources_;
+
+ base::WeakPtrFactory<ResourceProviderImpl> weak_factory_;
+};
+
+} // namespace cast_streaming
+
+#endif // COMPONENTS_CAST_STREAMING_RENDERER_RESOURCE_PROVIDER_IMPL_H_
diff --git a/chromium/components/cast_streaming/renderer/wrapping_renderer_factory_selector.cc b/chromium/components/cast_streaming/renderer/wrapping_renderer_factory_selector.cc
index 1d36418ae25..30ee6fc58a4 100644
--- a/chromium/components/cast_streaming/renderer/wrapping_renderer_factory_selector.cc
+++ b/chromium/components/cast_streaming/renderer/wrapping_renderer_factory_selector.cc
@@ -7,26 +7,21 @@
#include <memory>
#include "components/cast_streaming/renderer/playback_command_forwarding_renderer_factory.h"
-#include "components/cast_streaming/renderer/public/renderer_controller_proxy.h"
+#include "components/cast_streaming/renderer/public/resource_provider.h"
namespace cast_streaming {
WrappingRendererFactorySelector::WrappingRendererFactorySelector(
- content::RenderFrame* render_frame) {
- DCHECK(render_frame);
- auto* renderer_controller_proxy = RendererControllerProxy::GetInstance();
- DCHECK(renderer_controller_proxy);
+ ResourceProvider* resource_provider) {
+ DCHECK(resource_provider);
+
wrapping_factory_ =
std::make_unique<PlaybackCommandForwardingRendererFactory>(
- renderer_controller_proxy->GetReceiver(render_frame));
+ resource_provider->GetRendererCommandReceiver());
}
WrappingRendererFactorySelector::~WrappingRendererFactorySelector() = default;
-media::RendererType WrappingRendererFactorySelector::GetCurrentRendererType() {
- return media::RendererType::kCastStreaming;
-}
-
media::RendererFactory* WrappingRendererFactorySelector::GetCurrentFactory() {
DCHECK(wrapping_factory_);
media::RendererFactory* wrapped_factory =
diff --git a/chromium/components/cbor/OWNERS b/chromium/components/cbor/OWNERS
index c2ed450520b..125c883b484 100644
--- a/chromium/components/cbor/OWNERS
+++ b/chromium/components/cbor/OWNERS
@@ -1,3 +1,2 @@
-jochen@chromium.org
engedy@chromium.org
file://base/SECURITY_OWNERS
diff --git a/chromium/components/cdm/DEPS b/chromium/components/cdm/DEPS
index be799d2b560..86694de5897 100644
--- a/chromium/components/cdm/DEPS
+++ b/chromium/components/cdm/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+ipc",
+ "+media/audio/android/audio_manager_android.h",
"+media/base",
"+media/cdm",
"+media/media_buildflags.h",
diff --git a/chromium/components/cdm/browser/cdm_message_filter_android.cc b/chromium/components/cdm/browser/cdm_message_filter_android.cc
index a02a4c6a75a..e2ba92e0db5 100644
--- a/chromium/components/cdm/browser/cdm_message_filter_android.cc
+++ b/chromium/components/cdm/browser/cdm_message_filter_android.cc
@@ -16,11 +16,13 @@
#include "content/public/browser/android/android_overlay_provider.h"
#include "ipc/ipc_message_macros.h"
#include "ipc/ipc_message_start.h"
+#include "media/audio/android/audio_manager_android.h"
#include "media/base/android/media_codec_util.h"
#include "media/base/android/media_drm_bridge.h"
#include "media/base/audio_codecs.h"
#include "media/base/eme_constants.h"
#include "media/base/media_switches.h"
+#include "media/base/media_util.h"
#include "media/base/video_codecs.h"
#include "media/media_buildflags.h"
@@ -80,6 +82,12 @@ const CodecInfo<media::AudioCodec> kMP4AudioCodecsToQuery[] = {
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
};
+// Is an audio sink connected which supports the given codec?
+static bool CanPassthrough(media::AudioCodec codec) {
+ return (media::AudioManagerAndroid::GetSinkAudioEncodingFormats() &
+ media::ConvertAudioCodecToBitstreamFormat(codec)) != 0;
+}
+
static SupportedCodecs GetSupportedCodecs(
const SupportedKeySystemRequest& request,
bool is_secure) {
@@ -110,9 +118,19 @@ static SupportedCodecs GetSupportedCodecs(
}
}
+ // It is possible that a device that is not able to decode the audio stream
+ // is connected to an audiosink device that can. In this case, CanDecode()
+ // returns false but CanPassthrough() will return true. CanPassthrough()
+ // calls AudioManagerAndroid::GetSinkAudioEncodingFormats() to retrieve a
+ // bitmask representing audio bitstream formats that are supported by the
+ // connected audiosink device. This bitmask is then matched against current
+ // audio stream's codec type. A match indicates that the connected
+ // audiosink device is able to decode the current audio stream and Chromium
+ // should passthrough the audio bitstream instead of trying to decode it.
for (const auto& info : kMP4AudioCodecsToQuery) {
if ((request.codecs & info.eme_codec) &&
- media::MediaCodecUtil::CanDecode(info.codec)) {
+ (media::MediaCodecUtil::CanDecode(info.codec) ||
+ CanPassthrough(info.codec))) {
supported_codecs |= info.eme_codec;
}
}
diff --git a/chromium/components/cdm/browser/media_drm_storage_impl_unittest.cc b/chromium/components/cdm/browser/media_drm_storage_impl_unittest.cc
index fb61cad1fbd..edacff45701 100644
--- a/chromium/components/cdm/browser/media_drm_storage_impl_unittest.cc
+++ b/chromium/components/cdm/browser/media_drm_storage_impl_unittest.cc
@@ -131,7 +131,7 @@ class MediaDrmStorageImplTest : public content::RenderViewHostTestHarness {
}
content::RenderFrameHost* SimulateNavigation(const GURL& url) {
- content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
+ content::RenderFrameHost* rfh = web_contents()->GetPrimaryMainFrame();
content::RenderFrameHostTester::For(rfh)->InitializeRenderFrameIfNeeded();
auto navigation_simulator =
diff --git a/chromium/components/cdm/renderer/android_key_systems.cc b/chromium/components/cdm/renderer/android_key_systems.cc
index 00bceb6fcd1..d028edbcf29 100644
--- a/chromium/components/cdm/renderer/android_key_systems.cc
+++ b/chromium/components/cdm/renderer/android_key_systems.cc
@@ -10,6 +10,7 @@
#include "base/command_line.h"
#include "base/logging.h"
#include "content/public/renderer/render_thread.h"
+#include "media/base/content_decryption_module.h"
#include "media/base/eme_constants.h"
#include "media/base/media_switches.h"
#include "media/media_buildflags.h"
@@ -18,10 +19,11 @@
#include "third_party/widevine/cdm/widevine_cdm_common.h" // nogncheck
#endif // BUILDFLAG(ENABLE_WIDEVINE)
+using media::CdmSessionType;
using media::EmeConfigRule;
using media::EmeFeatureSupport;
using media::EmeInitDataType;
-using media::EmeSessionTypeSupport;
+using media::EncryptionScheme;
using media::KeySystemProperties;
using media::SupportedCodecs;
#if BUILDFLAG(ENABLE_WIDEVINE)
@@ -61,8 +63,8 @@ class AndroidPlatformKeySystemProperties : public KeySystemProperties {
}
EmeConfigRule GetEncryptionSchemeConfigRule(
- media::EncryptionScheme encryption_scheme) const override {
- return encryption_scheme == media::EncryptionScheme::kCenc
+ EncryptionScheme encryption_scheme) const override {
+ return encryption_scheme == EncryptionScheme::kCenc
? EmeConfigRule::SUPPORTED
: EmeConfigRule::NOT_SUPPORTED;
}
@@ -85,8 +87,8 @@ class AndroidPlatformKeySystemProperties : public KeySystemProperties {
: EmeConfigRule::NOT_SUPPORTED;
}
- EmeSessionTypeSupport GetPersistentLicenseSessionSupport() const override {
- return EmeSessionTypeSupport::NOT_SUPPORTED;
+ EmeConfigRule GetPersistentLicenseSessionSupport() const override {
+ return EmeConfigRule::NOT_SUPPORTED;
}
EmeFeatureSupport GetPersistentStateSupport() const override {
return EmeFeatureSupport::ALWAYS_ENABLED;
@@ -131,39 +133,40 @@ void AddAndroidWidevine(
// On Android, ".secure" codecs are all hardware secure codecs.
auto hw_secure_codecs = response.secure_codecs;
- // Since we do not control the implementation of the MediaDrm API on Android,
- // we assume that it can and will make use of persistence no matter whether
- // persistence-based features are supported or not.
-
- const EmeSessionTypeSupport persistent_license_support =
- response.is_persistent_license_supported
- ? EmeSessionTypeSupport::SUPPORTED_WITH_IDENTIFIER
- : EmeSessionTypeSupport::NOT_SUPPORTED;
+ if (codecs == media::EME_CODEC_NONE) {
+ // It doesn't make sense to support hw secure codecs but not regular codecs.
+ DCHECK(hw_secure_codecs == media::EME_CODEC_NONE);
+ DVLOG(3) << __func__ << " Widevine NOT supported.";
+ return;
+ }
- if (codecs != media::EME_CODEC_NONE) {
- DVLOG(3) << __func__ << " Widevine supported.";
+ DVLOG(3) << __func__ << " Widevine supported.";
- base::flat_set<media::EncryptionScheme> encryption_schemes = {
- media::EncryptionScheme::kCenc};
- if (response.is_cbcs_encryption_supported) {
- encryption_schemes.insert(media::EncryptionScheme::kCbcs);
- }
+ base::flat_set<EncryptionScheme> encryption_schemes = {
+ EncryptionScheme::kCenc};
+ if (response.is_cbcs_encryption_supported) {
+ encryption_schemes.insert(EncryptionScheme::kCbcs);
+ }
- key_systems->emplace_back(new WidevineKeySystemProperties(
- codecs, // Regular codecs.
- encryption_schemes, // Encryption schemes.
- hw_secure_codecs, // Hardware secure codecs.
- encryption_schemes, // Hardware secure encryption schemes.
- Robustness::HW_SECURE_CRYPTO, // Max audio robustness.
- Robustness::HW_SECURE_ALL, // Max video robustness.
- persistent_license_support, // persistent-license.
- EmeFeatureSupport::ALWAYS_ENABLED, // Persistent state.
- EmeFeatureSupport::ALWAYS_ENABLED)); // Distinctive identifier.
- } else {
- // It doesn't make sense to support hw secure codecs but not regular codecs.
- DVLOG(3) << __func__ << " Widevine NOT supported.";
- DCHECK(hw_secure_codecs == media::EME_CODEC_NONE);
+ base::flat_set<CdmSessionType> session_types = {CdmSessionType::kTemporary};
+ if (response.is_persistent_license_supported) {
+ session_types.insert(CdmSessionType::kPersistentLicense);
}
+
+ // Since we do not control the implementation of the MediaDrm API on Android,
+ // we assume that it can and will make use of persistence no matter whether
+ // persistence-based features are supported or not.
+ key_systems->emplace_back(new WidevineKeySystemProperties(
+ codecs, // Regular codecs.
+ encryption_schemes, // Encryption schemes.
+ session_types, // Session types.
+ hw_secure_codecs, // Hardware secure codecs.
+ encryption_schemes, // Hardware secure encryption schemes.
+ session_types, // Hardware secure Session types.
+ Robustness::HW_SECURE_CRYPTO, // Max audio robustness.
+ Robustness::HW_SECURE_ALL, // Max video robustness.
+ EmeFeatureSupport::ALWAYS_ENABLED, // Persistent state.
+ EmeFeatureSupport::ALWAYS_ENABLED)); // Distinctive identifier.
}
#endif // BUILDFLAG(ENABLE_WIDEVINE)
diff --git a/chromium/components/cdm/renderer/external_clear_key_key_system_properties.cc b/chromium/components/cdm/renderer/external_clear_key_key_system_properties.cc
index 4bb6f8f5670..8b182631756 100644
--- a/chromium/components/cdm/renderer/external_clear_key_key_system_properties.cc
+++ b/chromium/components/cdm/renderer/external_clear_key_key_system_properties.cc
@@ -74,9 +74,9 @@ media::EmeConfigRule ExternalClearKeyProperties::GetRobustnessConfigRule(
}
// Persistent license sessions are faked.
-media::EmeSessionTypeSupport
+media::EmeConfigRule
ExternalClearKeyProperties::GetPersistentLicenseSessionSupport() const {
- return media::EmeSessionTypeSupport::SUPPORTED;
+ return media::EmeConfigRule::SUPPORTED;
}
media::EmeFeatureSupport ExternalClearKeyProperties::GetPersistentStateSupport()
diff --git a/chromium/components/cdm/renderer/external_clear_key_key_system_properties.h b/chromium/components/cdm/renderer/external_clear_key_key_system_properties.h
index f087aced87e..80c95833eae 100644
--- a/chromium/components/cdm/renderer/external_clear_key_key_system_properties.h
+++ b/chromium/components/cdm/renderer/external_clear_key_key_system_properties.h
@@ -31,8 +31,7 @@ class ExternalClearKeyProperties : public media::KeySystemProperties {
media::EmeMediaType media_type,
const std::string& requested_robustness,
const bool* hw_secure_requirement) const override;
- media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport()
- const override;
+ media::EmeConfigRule GetPersistentLicenseSessionSupport() const override;
media::EmeFeatureSupport GetPersistentStateSupport() const override;
media::EmeFeatureSupport GetDistinctiveIdentifierSupport() const override;
};
diff --git a/chromium/components/cdm/renderer/widevine_key_system_properties.cc b/chromium/components/cdm/renderer/widevine_key_system_properties.cc
index 4c522b0092e..a1a5164bc15 100644
--- a/chromium/components/cdm/renderer/widevine_key_system_properties.cc
+++ b/chromium/components/cdm/renderer/widevine_key_system_properties.cc
@@ -17,11 +17,12 @@
#error This file should only be built when Widevine is enabled.
#endif
+using media::CdmSessionType;
using media::EmeConfigRule;
using media::EmeFeatureSupport;
using media::EmeInitDataType;
using media::EmeMediaType;
-using media::EmeSessionTypeSupport;
+using media::EncryptionScheme;
using media::SupportedCodecs;
using Robustness = cdm::WidevineKeySystemProperties::Robustness;
@@ -57,22 +58,24 @@ bool IsHardwareSecurityEnabledForKeySystem(const std::string& key_system) {
} // namespace
WidevineKeySystemProperties::WidevineKeySystemProperties(
- media::SupportedCodecs codecs,
- base::flat_set<media::EncryptionScheme> encryption_schemes,
- media::SupportedCodecs hw_secure_codecs,
- base::flat_set<media::EncryptionScheme> hw_secure_encryption_schemes,
+ SupportedCodecs codecs,
+ base::flat_set<EncryptionScheme> encryption_schemes,
+ base::flat_set<CdmSessionType> session_types,
+ SupportedCodecs hw_secure_codecs,
+ base::flat_set<EncryptionScheme> hw_secure_encryption_schemes,
+ base::flat_set<CdmSessionType> hw_secure_session_types,
Robustness max_audio_robustness,
Robustness max_video_robustness,
- media::EmeSessionTypeSupport persistent_license_support,
- media::EmeFeatureSupport persistent_state_support,
- media::EmeFeatureSupport distinctive_identifier_support)
+ EmeFeatureSupport persistent_state_support,
+ EmeFeatureSupport distinctive_identifier_support)
: codecs_(codecs),
encryption_schemes_(std::move(encryption_schemes)),
+ session_types_(std::move(session_types)),
hw_secure_codecs_(hw_secure_codecs),
hw_secure_encryption_schemes_(std::move(hw_secure_encryption_schemes)),
+ hw_secure_session_types_(std::move(hw_secure_session_types)),
max_audio_robustness_(max_audio_robustness),
max_video_robustness_(max_video_robustness),
- persistent_license_support_(persistent_license_support),
persistent_state_support_(persistent_state_support),
distinctive_identifier_support_(distinctive_identifier_support) {}
@@ -114,10 +117,10 @@ bool WidevineKeySystemProperties::IsSupportedInitDataType(
}
EmeConfigRule WidevineKeySystemProperties::GetEncryptionSchemeConfigRule(
- media::EncryptionScheme encryption_scheme) const {
- bool is_supported = encryption_schemes_.count(encryption_scheme);
+ EncryptionScheme encryption_scheme) const {
+ bool is_supported = encryption_schemes_.contains(encryption_scheme);
bool is_hw_secure_supported =
- hw_secure_encryption_schemes_.count(encryption_scheme);
+ hw_secure_encryption_schemes_.contains(encryption_scheme);
if (is_supported && is_hw_secure_supported)
return EmeConfigRule::SUPPORTED;
@@ -202,13 +205,18 @@ EmeConfigRule WidevineKeySystemProperties::GetRobustnessConfigRule(
if (robustness >= Robustness::SW_SECURE_DECODE || hw_secure_codecs_required)
return EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
#elif BUILDFLAG(IS_WIN)
- // On Windows, hardware security uses MediaFoundation-based CDM which requires
- // identifier and persistent state.
- if (robustness >= Robustness::HW_SECURE_CRYPTO || hw_secure_codecs_required) {
+ if (robustness >= Robustness::HW_SECURE_CRYPTO) {
+ // On Windows, hardware security uses MediaFoundation-based CDM which
+ // requires identifier and persistent state.
return IsHardwareSecurityEnabledForKeySystem(key_system)
? EmeConfigRule::
IDENTIFIER_PERSISTENCE_AND_HW_SECURE_CODECS_REQUIRED
: EmeConfigRule::NOT_SUPPORTED;
+ } else if (robustness < Robustness::HW_SECURE_CRYPTO) {
+ // On Windows, when software security is queried, explicitly not allow
+ // hardware secure codecs to prevent robustness level upgrade, for stability
+ // and compatibility reasons. See https://crbug.com/1327043.
+ return EmeConfigRule::HW_SECURE_CODECS_NOT_ALLOWED;
}
#else
// On other platforms, require hardware secure codecs for HW_SECURE_CRYPTO and
@@ -220,9 +228,37 @@ EmeConfigRule WidevineKeySystemProperties::GetRobustnessConfigRule(
return EmeConfigRule::SUPPORTED;
}
-EmeSessionTypeSupport
-WidevineKeySystemProperties::GetPersistentLicenseSessionSupport() const {
- return persistent_license_support_;
+EmeConfigRule WidevineKeySystemProperties::GetPersistentLicenseSessionSupport()
+ const {
+ bool is_supported =
+ session_types_.contains(CdmSessionType::kPersistentLicense);
+
+#if BUILDFLAG(IS_CHROMEOS)
+ // The logic around hardware/software security support is complicated on
+ // ChromeOS. This code is to preserve the original logic, by deciding the
+ // support only based on `is_supported` and ignore `is_hw_secure_supported`.
+ // Note: On ChromeOS, platform verification (similar to CDM host verification)
+ // is required for persistent license support, which requires identifier.
+ // TODO(crbug.com/1324262): Fix the logic after refactoring EmeConfigRule.
+ return is_supported ? EmeConfigRule::IDENTIFIER_AND_PERSISTENCE_REQUIRED
+ : EmeConfigRule::NOT_SUPPORTED;
+#else // BUILDFLAG(IS_CHROMEOS)
+ bool is_hw_secure_supported =
+ hw_secure_session_types_.contains(CdmSessionType::kPersistentLicense);
+
+ // Per GetPersistentLicenseSessionSupport() API, there's no need to specify
+ // the PERSISTENCE requirement here, which is implicitly assumed and enforced
+ // by `KeySystemConfigSelector`.
+ if (is_supported && is_hw_secure_supported) {
+ return EmeConfigRule::SUPPORTED;
+ } else if (is_supported && !is_hw_secure_supported) {
+ return EmeConfigRule::HW_SECURE_CODECS_NOT_ALLOWED;
+ } else if (!is_supported && is_hw_secure_supported) {
+ return EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
+ } else {
+ return EmeConfigRule::NOT_SUPPORTED;
+ }
+#endif // BUILDFLAG(IS_CHROMEOS)
}
EmeFeatureSupport WidevineKeySystemProperties::GetPersistentStateSupport()
diff --git a/chromium/components/cdm/renderer/widevine_key_system_properties.h b/chromium/components/cdm/renderer/widevine_key_system_properties.h
index a2782964007..fec5708c916 100644
--- a/chromium/components/cdm/renderer/widevine_key_system_properties.h
+++ b/chromium/components/cdm/renderer/widevine_key_system_properties.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/containers/flat_set.h"
+#include "media/base/content_decryption_module.h"
#include "media/base/key_system_properties.h"
namespace cdm {
@@ -30,11 +31,12 @@ class WidevineKeySystemProperties : public media::KeySystemProperties {
WidevineKeySystemProperties(
media::SupportedCodecs codecs,
base::flat_set<media::EncryptionScheme> encryption_schemes,
+ base::flat_set<media::CdmSessionType> session_types,
media::SupportedCodecs hw_secure_codecs,
base::flat_set<media::EncryptionScheme> hw_secure_encryption_schemes,
+ base::flat_set<media::CdmSessionType> hw_secure_session_types,
Robustness max_audio_robustness,
Robustness max_video_robustness,
- media::EmeSessionTypeSupport persistent_license_support,
media::EmeFeatureSupport persistent_state_support,
media::EmeFeatureSupport distinctive_identifier_support);
~WidevineKeySystemProperties() override;
@@ -53,19 +55,19 @@ class WidevineKeySystemProperties : public media::KeySystemProperties {
media::EmeMediaType media_type,
const std::string& requested_robustness,
const bool* hw_secure_requirement) const override;
- media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport()
- const override;
+ media::EmeConfigRule GetPersistentLicenseSessionSupport() const override;
media::EmeFeatureSupport GetPersistentStateSupport() const override;
media::EmeFeatureSupport GetDistinctiveIdentifierSupport() const override;
private:
const media::SupportedCodecs codecs_;
const base::flat_set<media::EncryptionScheme> encryption_schemes_;
+ const base::flat_set<media::CdmSessionType> session_types_;
const media::SupportedCodecs hw_secure_codecs_;
const base::flat_set<media::EncryptionScheme> hw_secure_encryption_schemes_;
+ const base::flat_set<media::CdmSessionType> hw_secure_session_types_;
const Robustness max_audio_robustness_;
const Robustness max_video_robustness_;
- const media::EmeSessionTypeSupport persistent_license_support_;
const media::EmeFeatureSupport persistent_state_support_;
const media::EmeFeatureSupport distinctive_identifier_support_;
};
diff --git a/chromium/components/certificate_matching/OWNERS b/chromium/components/certificate_matching/OWNERS
index 545a98382b6..36ecb74b4e6 100644
--- a/chromium/components/certificate_matching/OWNERS
+++ b/chromium/components/certificate_matching/OWNERS
@@ -1,2 +1,2 @@
file://ash/components/policy/OWNERS
-file://chromeos/network/OWNERS
+file://chromeos/ash/components/network/OWNERS
diff --git a/chromium/components/certificate_matching/certificate_principal_pattern.cc b/chromium/components/certificate_matching/certificate_principal_pattern.cc
index 9552b45d8c0..f6f0af4afc8 100644
--- a/chromium/components/certificate_matching/certificate_principal_pattern.cc
+++ b/chromium/components/certificate_matching/certificate_principal_pattern.cc
@@ -14,9 +14,9 @@
namespace certificate_matching {
namespace {
-std::string GetOptionalStringKey(const base::Value& dictionary,
+std::string GetOptionalStringKey(const base::Value::Dict& dictionary,
base::StringPiece key) {
- auto* value = dictionary.FindStringKey(key);
+ auto* value = dictionary.FindString(key);
return value ? *value : std::string();
}
@@ -84,12 +84,12 @@ bool CertificatePrincipalPattern::Matches(
// static
CertificatePrincipalPattern CertificatePrincipalPattern::ParseFromOptionalDict(
- const base::Value* dict,
+ const base::Value::Dict* dict,
base::StringPiece key_common_name,
base::StringPiece key_locality,
base::StringPiece key_organization,
base::StringPiece key_organization_unit) {
- if (!dict || !dict->is_dict())
+ if (!dict)
return CertificatePrincipalPattern();
return CertificatePrincipalPattern(
GetOptionalStringKey(*dict, key_common_name),
diff --git a/chromium/components/certificate_matching/certificate_principal_pattern.h b/chromium/components/certificate_matching/certificate_principal_pattern.h
index 617571174d9..cca19b53350 100644
--- a/chromium/components/certificate_matching/certificate_principal_pattern.h
+++ b/chromium/components/certificate_matching/certificate_principal_pattern.h
@@ -9,10 +9,7 @@
#include "base/component_export.h"
#include "base/strings/string_piece_forward.h"
-
-namespace base {
-class Value;
-}
+#include "base/values.h"
namespace net {
struct CertPrincipal;
@@ -61,7 +58,7 @@ class COMPONENT_EXPORT(CERTIFICATE_MATCHING) CertificatePrincipalPattern {
// empty (putting no constraint on the principal field). If |value| is nullptr
// or not a dictionary, returns an empty pattern.
static CertificatePrincipalPattern ParseFromOptionalDict(
- const base::Value* dict,
+ const base::Value::Dict* dict,
base::StringPiece key_common_name,
base::StringPiece key_locality,
base::StringPiece key_organization,
diff --git a/chromium/components/certificate_matching/certificate_principal_pattern_unittest.cc b/chromium/components/certificate_matching/certificate_principal_pattern_unittest.cc
index dab4b497ac3..36446bf53c3 100644
--- a/chromium/components/certificate_matching/certificate_principal_pattern_unittest.cc
+++ b/chromium/components/certificate_matching/certificate_principal_pattern_unittest.cc
@@ -89,16 +89,8 @@ TEST(CertificatePrincipalPatternTest, ParseFromNullptr) {
EXPECT_TRUE(pattern.Empty());
}
-TEST(CertificatePrincipalPatternTest, ParseFromNonDict) {
- base::Value string_value("test");
- CertificatePrincipalPattern pattern =
- CertificatePrincipalPattern::ParseFromOptionalDict(&string_value, kKeyCN,
- kKeyL, kKeyO, kKeyOU);
- EXPECT_TRUE(pattern.Empty());
-}
-
TEST(CertificatePrincipalPatternTest, ParseFromEmptyDict) {
- base::Value dict_value(base::Value::Type::DICTIONARY);
+ base::Value::Dict dict_value;
CertificatePrincipalPattern pattern =
CertificatePrincipalPattern::ParseFromOptionalDict(&dict_value, kKeyCN,
kKeyL, kKeyO, kKeyOU);
@@ -106,11 +98,11 @@ TEST(CertificatePrincipalPatternTest, ParseFromEmptyDict) {
}
TEST(CertificatePrincipalPatternTest, Parse) {
- base::Value dict_value(base::Value::Type::DICTIONARY);
- dict_value.SetKey(kKeyCN, base::Value("ValueCN"));
- dict_value.SetKey(kKeyL, base::Value("ValueL"));
- dict_value.SetKey(kKeyO, base::Value("ValueO"));
- dict_value.SetKey(kKeyOU, base::Value("ValueOU"));
+ base::Value::Dict dict_value;
+ dict_value.Set(kKeyCN, "ValueCN");
+ dict_value.Set(kKeyL, "ValueL");
+ dict_value.Set(kKeyO, "ValueO");
+ dict_value.Set(kKeyOU, "ValueOU");
CertificatePrincipalPattern pattern =
CertificatePrincipalPattern::ParseFromOptionalDict(&dict_value, kKeyCN,
kKeyL, kKeyO, kKeyOU);
diff --git a/chromium/components/certificate_transparency/chrome_ct_policy_enforcer.cc b/chromium/components/certificate_transparency/chrome_ct_policy_enforcer.cc
index 98b0f0fc7cb..25b260f8213 100644
--- a/chromium/components/certificate_transparency/chrome_ct_policy_enforcer.cc
+++ b/chromium/components/certificate_transparency/chrome_ct_policy_enforcer.cc
@@ -21,7 +21,6 @@
#include "base/time/time.h"
#include "base/values.h"
#include "base/version.h"
-#include "components/certificate_transparency/ct_features.h"
#include "components/certificate_transparency/ct_known_logs.h"
#include "crypto/sha2.h"
#include "net/cert/ct_policy_status.h"
@@ -203,8 +202,6 @@ bool ChromeCTPolicyEnforcer::IsLogOperatedByGoogle(
}
bool ChromeCTPolicyEnforcer::IsLogDataTimely() const {
- if (ct_log_list_always_timely_for_testing_)
- return true;
// We consider built-in information to be timely for 10 weeks.
return (clock_->Now() - log_list_date_).InDays() < 70 /* 10 weeks */;
}
@@ -248,14 +245,12 @@ CTPolicyCompliance ChromeCTPolicyEnforcer::CheckCTPolicyCompliance(
// months.
// Increases the SCT requirements for certificates with a lifetime between
// 180 days and 15 months, from 2 to 3.
+ // This conditional, and the pre-2022 policy logic can be removed after June
+ // 1, 2023, since all publicly trusted certificates issued prior to the
+ // policy change date will have expired by then.
const base::Time kPolicyUpdateDate =
base::Time::UnixEpoch() + base::Seconds(1649980800);
- bool use_2022_policy =
- base::FeatureList::IsEnabled(
- features::kCertificateTransparency2022PolicyAllCerts) ||
- (base::FeatureList::IsEnabled(
- features::kCertificateTransparency2022Policy) &&
- issuance_date >= kPolicyUpdateDate);
+ bool use_2022_policy = issuance_date >= kPolicyUpdateDate;
bool has_valid_google_sct = false;
bool has_valid_nongoogle_sct = false;
@@ -376,18 +371,7 @@ CTPolicyCompliance ChromeCTPolicyEnforcer::CheckCTPolicyCompliance(
// AND there is at least one embedded SCT from a non-Google Log once or
// currently qualified;
// ...
- //
- // Note: This policy language is only enforced after the below issuance
- // date, as that's when the diversity policy first came into effect for
- // SCTs embedded in certificates.
- // The date when diverse SCTs requirement is effective from.
- // 2015-07-01 00:00:00 UTC.
- // TODO(carlosil): There are no more valid certificates from before this
- // date, so this date and the associated logic should be cleaned up.
- const base::Time kDiverseSCTRequirementStartDate =
- base::Time::UnixEpoch() + base::Seconds(1435708800);
- if (issuance_date >= kDiverseSCTRequirementStartDate &&
- !(has_embedded_google_sct && has_embedded_nongoogle_sct)) {
+ if (!(has_embedded_google_sct && has_embedded_nongoogle_sct)) {
// Note: This also covers the case for non-embedded SCTs, as it's only
// possible to reach here if both sets are not diverse enough.
return CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS;
diff --git a/chromium/components/certificate_transparency/chrome_ct_policy_enforcer.h b/chromium/components/certificate_transparency/chrome_ct_policy_enforcer.h
index e013db0dd71..c167e4e281c 100644
--- a/chromium/components/certificate_transparency/chrome_ct_policy_enforcer.h
+++ b/chromium/components/certificate_transparency/chrome_ct_policy_enforcer.h
@@ -91,10 +91,6 @@ class COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) ChromeCTPolicyEnforcer
return log_operator_history_;
}
- void SetCTLogListAlwaysTimelyForTesting(bool always_timely) {
- ct_log_list_always_timely_for_testing_ = always_timely;
- }
-
void SetOperatorHistoryForTesting(
std::map<std::string, OperatorHistoryEntry> log_operator_history) {
log_operator_history_ = std::move(log_operator_history);
@@ -149,10 +145,6 @@ class COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) ChromeCTPolicyEnforcer
// generated.
base::Time log_list_date_;
- // If set, the CT log list will be considered timely regardless of its last
- // update time.
- bool ct_log_list_always_timely_for_testing_ = false;
-
// If set, this log ID will be considered a valid, Google operated log.
// Calling UpdateCTLogList clears this value if set.
absl::optional<std::string> valid_google_log_for_testing_;
diff --git a/chromium/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc b/chromium/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
index eded367578b..056ceeed142 100644
--- a/chromium/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
+++ b/chromium/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
@@ -14,7 +14,6 @@
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "base/version.h"
-#include "components/certificate_transparency/ct_features.h"
#include "components/certificate_transparency/ct_known_logs.h"
#include "crypto/rsa_private_key.h"
#include "crypto/sha2.h"
@@ -70,7 +69,7 @@ class ChromeCTPolicyEnforcerTest : public ::testing::Test {
SignedCertificateTimestamp::Origin desired_origin,
size_t num_scts,
const std::vector<std::string>& desired_log_keys,
- bool timestamp_past_enforcement_date,
+ bool timestamp_past_2022_policy_date,
SCTList* verified_scts) {
for (size_t i = 0; i < num_scts; ++i) {
scoped_refptr<SignedCertificateTimestamp> sct(
@@ -81,11 +80,11 @@ class ChromeCTPolicyEnforcerTest : public ::testing::Test {
else
sct->log_id = std::string(crypto::kSHA256Length, static_cast<char>(i));
- if (timestamp_past_enforcement_date) {
- EXPECT_TRUE(base::Time::FromUTCExploded({2015, 8, 0, 15, 0, 0, 0, 0},
+ if (timestamp_past_2022_policy_date) {
+ EXPECT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 15, 0, 0, 0, 0},
&sct->timestamp));
} else {
- EXPECT_TRUE(base::Time::FromUTCExploded({2015, 6, 0, 15, 0, 0, 0, 0},
+ EXPECT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 14, 0, 0, 0, 0},
&sct->timestamp));
}
@@ -95,6 +94,7 @@ class ChromeCTPolicyEnforcerTest : public ::testing::Test {
void AddDisqualifiedLogSCT(SignedCertificateTimestamp::Origin desired_origin,
bool timestamp_after_disqualification_date,
+ bool timestamp_past_2022_policy_date,
SCTList* verified_scts) {
static const char kTestRetiredLogID[] =
"\xcd\xb5\x17\x9b\x7f\xc1\xc0\x46\xfe\xea\x31\x13\x6a\x3f\x8f\x00\x2e"
@@ -102,8 +102,14 @@ class ChromeCTPolicyEnforcerTest : public ::testing::Test {
static_assert(std::size(kTestRetiredLogID) - 1 == crypto::kSHA256Length,
"Incorrect log ID length.");
base::Time retirement_time;
- ASSERT_TRUE(base::Time::FromUTCExploded({2016, 4, 0, 15, 0, 0, 0, 0},
- &retirement_time));
+ if (timestamp_past_2022_policy_date) {
+ ASSERT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 16, 0, 0, 0, 0},
+ &retirement_time));
+ } else {
+ ASSERT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 14, 12, 0, 0, 0},
+ &retirement_time));
+ }
+
policy_enforcer_->SetDisqualifiedLogForTesting(
std::make_pair(std::string(kTestRetiredLogID, 32), retirement_time));
@@ -112,11 +118,9 @@ class ChromeCTPolicyEnforcerTest : public ::testing::Test {
sct->origin = desired_origin;
sct->log_id = std::string(kTestRetiredLogID, crypto::kSHA256Length);
if (timestamp_after_disqualification_date) {
- EXPECT_TRUE(base::Time::FromUTCExploded({2016, 4, 0, 16, 0, 0, 0, 0},
- &sct->timestamp));
+ sct->timestamp = retirement_time + base::Hours(1);
} else {
- EXPECT_TRUE(base::Time::FromUTCExploded({2016, 4, 0, 1, 0, 0, 0, 0},
- &sct->timestamp));
+ sct->timestamp = retirement_time - base::Hours(1);
}
verified_scts->push_back(sct);
@@ -125,11 +129,12 @@ class ChromeCTPolicyEnforcerTest : public ::testing::Test {
void FillListWithSCTsOfOrigin(
SignedCertificateTimestamp::Origin desired_origin,
size_t num_scts,
+ bool timestamp_past_2022_policy_date,
SCTList* verified_scts) {
std::vector<std::string> desired_log_ids;
desired_log_ids.push_back(google_log_id_);
- FillListWithSCTsOfOrigin(desired_origin, num_scts, desired_log_ids, true,
- verified_scts);
+ FillListWithSCTsOfOrigin(desired_origin, num_scts, desired_log_ids,
+ timestamp_past_2022_policy_date, verified_scts);
}
base::Time CreateTime(const base::Time::Exploded& exploded) {
@@ -159,28 +164,13 @@ class ChromeCTPolicyEnforcerTest : public ::testing::Test {
base::test::ScopedFeatureList scoped_feature_list_;
};
-// Subclass of ChromeCTPolicyEnforcerTest that tests using only the pre-2022
-// policy.
-class ChromeCTPolicyEnforcerTestPre2022Policy
- : public ChromeCTPolicyEnforcerTest {
- public:
- void SetUp() override {
- scoped_feature_list_.InitWithFeatures(
- /*enabled_features*/ {},
- /*disabled_features*/ {
- features::kCertificateTransparency2022Policy,
- features::kCertificateTransparency2022PolicyAllCerts});
- ChromeCTPolicyEnforcerTest::SetUp();
- }
-};
-
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+TEST_F(ChromeCTPolicyEnforcerTest,
DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle) {
SCTList scts;
std::vector<std::string> desired_log_ids(2, google_log_id_);
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- desired_log_ids.size(), desired_log_ids, true,
+ desired_log_ids.size(), desired_log_ids, false,
&scts);
EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
@@ -188,13 +178,13 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
NetLogWithSource()));
}
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+TEST_F(ChromeCTPolicyEnforcerTest,
DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllNonGoogle) {
SCTList scts;
std::vector<std::string> desired_log_ids(2, non_google_log_id_);
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- desired_log_ids.size(), desired_log_ids, true,
+ desired_log_ids.size(), desired_log_ids, false,
&scts);
EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
@@ -202,20 +192,7 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
NetLogWithSource()));
}
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
- ConformsToCTPolicyIfSCTBeforeEnforcementDate) {
- SCTList scts;
- // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
- // All 5 SCTs will be from non-Google logs.
- FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
- std::vector<std::string>(), false, &scts);
-
- EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
- policy_enforcer_->CheckCompliance(chain_.get(), scts,
- NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+TEST_F(ChromeCTPolicyEnforcerTest,
ConformsToPolicyExactNumberOfSCTsForValidityPeriod) {
std::unique_ptr<crypto::RSAPrivateKey> private_key(
crypto::RSAPrivateKey::Create(1024));
@@ -284,18 +261,31 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
for (size_t j = 0; j < required_scts - 1; ++j) {
SCTList scts;
- FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, j,
- std::vector<std::string>(), false, &scts);
- EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
- policy_enforcer_->CheckCompliance(cert.get(), scts,
- NetLogWithSource()))
+ std::vector<std::string> desired_logs;
+ desired_logs.push_back(google_log_id_);
+ if (j > 0) {
+ FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+ desired_logs, false, &scts);
+ FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+ j - 1, std::vector<std::string>(), false,
+ &scts);
+ }
+ CTPolicyCompliance expected_failure =
+ j == 1 ? CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS
+ : CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
+ EXPECT_EQ(expected_failure, policy_enforcer_->CheckCompliance(
+ cert.get(), scts, NetLogWithSource()))
<< " for: " << (end - start).InDays() << " and " << required_scts
<< " scts=" << scts.size() << " j=" << j;
}
SCTList scts;
+ std::vector<std::string> desired_logs;
+ desired_logs.push_back(google_log_id_);
+ FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+ desired_logs, false, &scts);
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
- required_scts, std::vector<std::string>(), false,
- &scts);
+ required_scts - 1, std::vector<std::string>(),
+ false, &scts);
EXPECT_EQ(
CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
policy_enforcer_->CheckCompliance(cert.get(), scts, NetLogWithSource()))
@@ -304,7 +294,7 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
}
}
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+TEST_F(ChromeCTPolicyEnforcerTest,
DoesNotConformToCTPolicyNotEnoughUniqueEmbeddedLogs) {
SCTList scts;
std::vector<std::string> desired_logs;
@@ -313,21 +303,21 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
desired_logs.clear();
desired_logs.push_back(google_log_id_);
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
- desired_logs.size(), desired_logs, true, &scts);
+ desired_logs.size(), desired_logs, false, &scts);
// Two distinct non-Google logs.
desired_logs.clear();
desired_logs.emplace_back(crypto::kSHA256Length, 'A');
desired_logs.emplace_back(crypto::kSHA256Length, 'B');
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
- desired_logs.size(), desired_logs, true, &scts);
+ desired_logs.size(), desired_logs, false, &scts);
// Two unique SCTs from the same non-Google log.
desired_logs.clear();
desired_logs.emplace_back(crypto::kSHA256Length, 'C');
desired_logs.emplace_back(crypto::kSHA256Length, 'C');
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
- desired_logs.size(), desired_logs, true, &scts);
+ desired_logs.size(), desired_logs, false, &scts);
// |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
// However, there are only 4 SCTs are from distinct logs.
@@ -336,8 +326,7 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
NetLogWithSource()));
}
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
- DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
+TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
SCTList scts;
// The results should be the same before and after disqualification,
@@ -346,9 +335,9 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
// SCT from before disqualification.
scts.clear();
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- 1, &scts);
+ 1, false, &scts);
AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- false, &scts);
+ false, false, &scts);
EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
policy_enforcer_->CheckCompliance(chain_.get(), scts,
NetLogWithSource()));
@@ -356,9 +345,9 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
// SCT from after disqualification.
scts.clear();
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- 1, &scts);
+ 1, false, &scts);
AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- true, &scts);
+ true, false, &scts);
EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
policy_enforcer_->CheckCompliance(chain_.get(), scts,
NetLogWithSource()));
@@ -366,8 +355,9 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
// Embedded SCT from before disqualification.
scts.clear();
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- 1, &scts);
- AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
+ 1, false, &scts);
+ AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, false,
+ &scts);
EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
policy_enforcer_->CheckCompliance(chain_.get(), scts,
NetLogWithSource()));
@@ -375,8 +365,9 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
// Embedded SCT from after disqualification.
scts.clear();
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- 1, &scts);
- AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+ 1, false, &scts);
+ AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, false,
+ &scts);
EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
policy_enforcer_->CheckCompliance(chain_.get(), scts,
NetLogWithSource()));
@@ -386,28 +377,13 @@ TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
// policy and the 2022 policy.
class ChromeCTPolicyEnforcerTestBothPolicies
: public ChromeCTPolicyEnforcerTest,
- public ::testing::WithParamInterface<bool> {
- public:
- void SetUp() override {
- if (GetParam()) {
- scoped_feature_list_.InitAndEnableFeature(
- features::kCertificateTransparency2022PolicyAllCerts);
- } else {
- scoped_feature_list_.InitWithFeatures(
- /*enabled_features*/ {},
- /*disabled_features*/ {
- features::kCertificateTransparency2022Policy,
- features::kCertificateTransparency2022PolicyAllCerts});
- }
- ChromeCTPolicyEnforcerTest::SetUp();
- }
-};
+ public ::testing::WithParamInterface<bool> {};
TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
ConformsToCTPolicyWithNonEmbeddedSCTs) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- 2, &scts);
+ 2, GetParam(), &scts);
if (GetParam()) {
std::map<std::string, OperatorHistoryEntry> operator_history;
FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -422,7 +398,7 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, EnforcementDisabledByBinaryAge) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- 2, &scts);
+ 2, GetParam(), &scts);
if (GetParam()) {
std::map<std::string, OperatorHistoryEntry> operator_history;
FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -444,7 +420,8 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
ConformsToCTPolicyWithEmbeddedSCTs) {
// |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
SCTList scts;
- FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
+ FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+ GetParam(), &scts);
if (GetParam()) {
std::map<std::string, OperatorHistoryEntry> operator_history;
FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -465,13 +442,15 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
desired_logs.clear();
desired_logs.push_back(google_log_id_);
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
- desired_logs.size(), desired_logs, true, &scts);
+ desired_logs.size(), desired_logs, GetParam(),
+ &scts);
// One non-Google log, delivered via TLS.
desired_logs.clear();
desired_logs.push_back(non_google_log_id_);
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- desired_logs.size(), desired_logs, true, &scts);
+ desired_logs.size(), desired_logs, GetParam(),
+ &scts);
if (GetParam()) {
std::map<std::string, OperatorHistoryEntry> operator_history;
FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -492,13 +471,15 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
desired_logs.clear();
desired_logs.push_back(google_log_id_);
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
- desired_logs.size(), desired_logs, true, &scts);
+ desired_logs.size(), desired_logs, GetParam(),
+ &scts);
// One non-Google log, delivered via OCSP.
desired_logs.clear();
desired_logs.push_back(non_google_log_id_);
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
- desired_logs.size(), desired_logs, true, &scts);
+ desired_logs.size(), desired_logs, GetParam(),
+ &scts);
if (GetParam()) {
std::map<std::string, OperatorHistoryEntry> operator_history;
@@ -515,7 +496,8 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
DoesNotConformToCTPolicyNotEnoughSCTs) {
// |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
SCTList scts;
- FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 2, &scts);
+ FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 2,
+ GetParam(), &scts);
if (GetParam()) {
std::map<std::string, OperatorHistoryEntry> operator_history;
FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -530,8 +512,15 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
ConformsWithDisqualifiedLogBeforeDisqualificationDate) {
SCTList scts;
- FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
- AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
+ std::vector<std::string> desired_log_ids;
+ desired_log_ids.push_back(google_log_id_);
+ FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+ desired_log_ids, GetParam(), &scts);
+ FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+ GetParam() ? 1 : 3, std::vector<std::string>(),
+ GetParam(), &scts);
+ AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false,
+ GetParam(), &scts);
if (GetParam()) {
std::map<std::string, OperatorHistoryEntry> operator_history;
FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -550,8 +539,9 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
// Add required - 1 valid SCTs (with the old policy 5 are required, with the
// new policy 3).
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
- GetParam() ? 2 : 4, &scts);
- AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+ GetParam() ? 2 : 4, GetParam(), &scts);
+ AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true,
+ GetParam(), &scts);
if (GetParam()) {
std::map<std::string, OperatorHistoryEntry> operator_history;
FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -567,11 +557,12 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
DoesNotConformWithIssuanceDateAfterDisqualificationDate) {
SCTList scts;
- AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+ AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true,
+ GetParam(), &scts);
// Add required - 1 valid SCTs (with the old policy 5 are required, with the
// new policy 3).
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
- GetParam() ? 2 : 4, &scts);
+ GetParam() ? 2 : 4, GetParam(), &scts);
// Make sure all SCTs are after the disqualification date.
for (size_t i = 1; i < scts.size(); ++i)
scts[i]->timestamp = scts[0]->timestamp;
@@ -591,7 +582,7 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, UpdateCTLogList) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- 2, &scts);
+ 2, GetParam(), &scts);
std::vector<std::pair<std::string, base::Time>> disqualified_logs;
std::vector<std::string> operated_by_google_logs;
@@ -631,7 +622,7 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, UpdateCTLogList) {
TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, TimestampUpdates) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
- 1, &scts);
+ 1, GetParam(), &scts);
// Clear the log list and set the last updated time to more than 10 weeks ago.
std::vector<std::pair<std::string, base::Time>> disqualified_logs;
@@ -715,7 +706,8 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
ConformsWithCTPolicyFutureRetirementDateLogs) {
SCTList scts;
- FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
+ FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+ GetParam(), &scts);
std::vector<std::pair<std::string, base::Time>> disqualified_logs;
std::vector<std::string> operated_by_google_logs = {google_log_id_};
@@ -747,7 +739,8 @@ TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
DoesNotConformWithCTPolicyPastRetirementDateLogs) {
SCTList scts;
- FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
+ FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+ GetParam(), &scts);
std::vector<std::pair<std::string, base::Time>> disqualified_logs;
std::vector<std::string> operated_by_google_logs = {google_log_id_};
@@ -781,17 +774,7 @@ INSTANTIATE_TEST_SUITE_P(All,
ChromeCTPolicyEnforcerTestBothPolicies,
testing::Bool());
-class ChromeCTPolicyEnforcerTest2022Policy : public ChromeCTPolicyEnforcerTest {
- public:
- void SetUp() override {
- scoped_feature_list_.InitAndEnableFeature(
- features::kCertificateTransparency2022Policy);
- ChromeCTPolicyEnforcerTest::SetUp();
- }
-};
-
-TEST_F(ChromeCTPolicyEnforcerTest2022Policy,
- 2022PolicyNotInEffectBeforeTargetDate) {
+TEST_F(ChromeCTPolicyEnforcerTest, 2022PolicyNotInEffectBeforeTargetDate) {
// Old policy should enforce one Google log requirement.
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
@@ -810,8 +793,7 @@ TEST_F(ChromeCTPolicyEnforcerTest2022Policy,
NetLogWithSource()));
}
-TEST_F(ChromeCTPolicyEnforcerTest2022Policy,
- 2022PolicyInEffectAfterTargetDate) {
+TEST_F(ChromeCTPolicyEnforcerTest, 2022PolicyInEffectAfterTargetDate) {
// New policy should allow SCTs from all non-Google operators to comply as
// long as diversity requirement is fulfilled.
SCTList scts;
@@ -831,17 +813,7 @@ TEST_F(ChromeCTPolicyEnforcerTest2022Policy,
NetLogWithSource()));
}
-class ChromeCTPolicyEnforcerTest2022PolicyAllCerts
- : public ChromeCTPolicyEnforcerTest {
- public:
- void SetUp() override {
- scoped_feature_list_.InitAndEnableFeature(
- features::kCertificateTransparency2022PolicyAllCerts);
- ChromeCTPolicyEnforcerTest::SetUp();
- }
-};
-
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, UpdatedSCTRequirements) {
+TEST_F(ChromeCTPolicyEnforcerTest, UpdatedSCTRequirements) {
std::unique_ptr<crypto::RSAPrivateKey> private_key(
crypto::RSAPrivateKey::Create(1024));
ASSERT_TRUE(private_key);
@@ -896,7 +868,7 @@ TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, UpdatedSCTRequirements) {
for (size_t j = 0; j <= scts_required; ++j) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, j,
- std::vector<std::string>(), false, &scts);
+ std::vector<std::string>(), true, &scts);
// Add different operators to the logs so the SCTs comply with operator
// diversity.
FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -921,7 +893,7 @@ TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, UpdatedSCTRequirements) {
}
}
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
+TEST_F(ChromeCTPolicyEnforcerTest,
DoesNotConformToCTPolicyAllLogsSameOperator) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
@@ -939,8 +911,7 @@ TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
NetLogWithSource()));
}
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
- ConformsToCTPolicyDifferentOperators) {
+TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyDifferentOperators) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
2, std::vector<std::string>(), true, &scts);
@@ -953,8 +924,7 @@ TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
NetLogWithSource()));
}
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
- ConformsToPolicyDueToOperatorSwitch) {
+TEST_F(ChromeCTPolicyEnforcerTest, ConformsToPolicyDueToOperatorSwitch) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
2, std::vector<std::string>(), true, &scts);
@@ -976,8 +946,7 @@ TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
NetLogWithSource()));
}
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
- DoesNotConformToPolicyDueToOperatorSwitch) {
+TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToPolicyDueToOperatorSwitch) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
2, std::vector<std::string>(), true, &scts);
@@ -996,7 +965,7 @@ TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
NetLogWithSource()));
}
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, MultipleOperatorSwitches) {
+TEST_F(ChromeCTPolicyEnforcerTest, MultipleOperatorSwitches) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
2, std::vector<std::string>(), true, &scts);
@@ -1016,8 +985,7 @@ TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, MultipleOperatorSwitches) {
NetLogWithSource()));
}
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
- MultipleOperatorSwitchesBeforeSCTTimestamp) {
+TEST_F(ChromeCTPolicyEnforcerTest, MultipleOperatorSwitchesBeforeSCTTimestamp) {
SCTList scts;
FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
2, std::vector<std::string>(), true, &scts);
diff --git a/chromium/components/certificate_transparency/chrome_require_ct_delegate.cc b/chromium/components/certificate_transparency/chrome_require_ct_delegate.cc
index 28b2ef4aba7..e806fbd2f5c 100644
--- a/chromium/components/certificate_transparency/chrome_require_ct_delegate.cc
+++ b/chromium/components/certificate_transparency/chrome_require_ct_delegate.cc
@@ -229,7 +229,7 @@ bool ChromeRequireCTDelegate::MatchHostname(const std::string& hostname,
// Scheme and port are ignored by the policy, so it's OK to construct a
// new GURL here. However, |hostname| is in network form, not URL form,
// so it's necessary to wrap IPv6 addresses in brackets.
- std::set<url_matcher::URLMatcherConditionSet::ID> matching_ids =
+ std::set<base::MatcherStringPattern::ID> matching_ids =
url_matcher_->MatchURL(
GURL("https://" + net::HostPortPair(hostname, 443).HostForURL()));
if (matching_ids.empty())
diff --git a/chromium/components/certificate_transparency/chrome_require_ct_delegate.h b/chromium/components/certificate_transparency/chrome_require_ct_delegate.h
index 8b3fed9bdd6..e3865b7cf10 100644
--- a/chromium/components/certificate_transparency/chrome_require_ct_delegate.h
+++ b/chromium/components/certificate_transparency/chrome_require_ct_delegate.h
@@ -96,8 +96,8 @@ class COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY) ChromeRequireCTDelegate
bool FilterTakesPrecedence(const Filter& lhs, const Filter& rhs) const;
std::unique_ptr<url_matcher::URLMatcher> url_matcher_;
- url_matcher::URLMatcherConditionSet::ID next_id_;
- std::map<url_matcher::URLMatcherConditionSet::ID, Filter> filters_;
+ base::MatcherStringPattern::ID next_id_;
+ std::map<base::MatcherStringPattern::ID, Filter> filters_;
// Both SPKI lists are sorted.
net::HashValueVector spkis_;
diff --git a/chromium/components/certificate_transparency/ct_features.cc b/chromium/components/certificate_transparency/ct_features.cc
index fea5eea435a..bb5133d7a92 100644
--- a/chromium/components/certificate_transparency/ct_features.cc
+++ b/chromium/components/certificate_transparency/ct_features.cc
@@ -13,12 +13,5 @@ const base::Feature kCertificateTransparencyComponentUpdater{
"CertificateTransparencyComponentUpdater",
base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kCertificateTransparency2022Policy{
- "CertificateTransparency2022Policy", base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kCertificateTransparency2022PolicyAllCerts{
- "CertificateTransparency2022PolicyAllCerts",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
} // namespace features
} // namespace certificate_transparency
diff --git a/chromium/components/certificate_transparency/ct_features.h b/chromium/components/certificate_transparency/ct_features.h
index 1738fcd2e1e..5b20965f974 100644
--- a/chromium/components/certificate_transparency/ct_features.h
+++ b/chromium/components/certificate_transparency/ct_features.h
@@ -14,21 +14,6 @@ namespace features {
COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
extern const base::Feature kCertificateTransparencyComponentUpdater;
-// If enabled, the 2022 CT policy which removes the one Google log
-// requirement, introduces log operator diversity requirements, and increases
-// the number of embedded SCTs required for certificates with a lifetime over
-// 180 days (from 2 to 3) will be used for any certificate issued after February
-// 1, 2022.
-COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
-extern const base::Feature kCertificateTransparency2022Policy;
-
-// If enabled, the 2022 CT policy which removes the one Google log
-// requirement, introduces log operator diversity requirements, and increases
-// the number of embedded SCTs required for certificates with a lifetime over
-// 180 days (from 2 to 3) will be used for all certificates.
-COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
-extern const base::Feature kCertificateTransparency2022PolicyAllCerts;
-
} // namespace features
} // namespace certificate_transparency
diff --git a/chromium/components/certificate_transparency/data/log_list.json b/chromium/components/certificate_transparency/data/log_list.json
index e7448754206..cdad75188ba 100644
--- a/chromium/components/certificate_transparency/data/log_list.json
+++ b/chromium/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
{
- "version": "7.51",
- "log_list_timestamp": "2022-04-29T12:56:40Z",
+ "version": "11.8",
+ "log_list_timestamp": "2022-08-31T12:54:56Z",
"operators": [
{
"name": "Google",
@@ -41,6 +41,22 @@
}
},
{
+ "description": "Google 'Argon2024' log",
+ "log_id": "7s3QZNXbGs7FXLedtM0TojKHRny87N7DUUhZRnEftZs=",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHblsqctplMVc5ramA7vSuNxUQxcomQwGAVAdnWTAWUYr3MgDHQW0LagJ95lB7QT75Ve6JgT2EVLOFGU7L3YrwA==",
+ "url": "https://ct.googleapis.com/logs/us1/argon2024/",
+ "mmd": 86400,
+ "state": {
+ "qualified": {
+ "timestamp": "2022-08-18T23:45:14Z"
+ }
+ },
+ "temporal_interval": {
+ "start_inclusive": "2024-01-01T00:00:00Z",
+ "end_exclusive": "2025-01-01T00:00:00Z"
+ }
+ },
+ {
"description": "Google 'Xenon2022' log",
"log_id": "RqVV63X6kSAwtaKJafTzfREsQXS+/Um4havy/HD+bUc=",
"key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+WS9FSxAYlCVEzg8xyGwOrmPonoV14nWjjETAIdZvLvukPzIWBMKv6tDNlQjpIHNrUcUt1igRPpqoKDXw2MeKw==",
@@ -73,19 +89,19 @@
}
},
{
- "description": "Google 'Aviator' log",
- "log_id": "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q=",
- "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q==",
- "url": "https://ct.googleapis.com/aviator/",
+ "description": "Google 'Xenon2024' log",
+ "log_id": "dv+IPwq2+5VRwmHM9Ye6NLSkzbsp3GhCCp/mZ0xaOnQ=",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuWDgNB415GUAk0+QCb1a7ETdjA/O7RE+KllGmjG2x5n33O89zY+GwjWlPtwpurvyVOKoDIMIUQbeIW02UI44TQ==",
+ "url": "https://ct.googleapis.com/logs/eu1/xenon2024/",
"mmd": 86400,
"state": {
- "readonly": {
- "timestamp": "2016-11-30T13:24:18Z",
- "final_tree_head": {
- "sha256_root_hash": "LcGcZRsm+LGYmrlyC5LXhV1T6OD8iH5dNlb0sEJl9bA=",
- "tree_size": 46466472
- }
+ "qualified": {
+ "timestamp": "2022-08-18T23:45:14Z"
}
+ },
+ "temporal_interval": {
+ "start_inclusive": "2024-01-01T00:00:00Z",
+ "end_exclusive": "2025-01-01T00:00:00Z"
}
},
{
@@ -95,8 +111,12 @@
"url": "https://ct.googleapis.com/icarus/",
"mmd": 86400,
"state": {
- "usable": {
- "timestamp": "2017-03-06T19:35:01Z"
+ "readonly": {
+ "timestamp": "2022-05-03T18:52:50Z",
+ "final_tree_head": {
+ "sha256_root_hash": "D1H4wAJmq0MRCeLfeOtrsZ9Am015anO5MkeasNhnQWI=",
+ "tree_size": 762430819
+ }
}
}
},
@@ -107,8 +127,12 @@
"url": "https://ct.googleapis.com/pilot/",
"mmd": 86400,
"state": {
- "usable": {
- "timestamp": "2014-09-02T20:41:44Z"
+ "readonly": {
+ "timestamp": "2022-05-03T18:52:50Z",
+ "final_tree_head": {
+ "sha256_root_hash": "57gZIfRLpXho89LUBCQeZUDIHx8gA9PL1M4hc71W90E=",
+ "tree_size": 1077336645
+ }
}
}
},
@@ -119,8 +143,12 @@
"url": "https://ct.googleapis.com/rocketeer/",
"mmd": 86400,
"state": {
- "usable": {
- "timestamp": "2015-08-04T19:00:05Z"
+ "readonly": {
+ "timestamp": "2022-05-03T18:52:50Z",
+ "final_tree_head": {
+ "sha256_root_hash": "Fj49ZSzvzvjNlEOCVhdkwpTlP7hyp82SFsmfWPScpv0=",
+ "tree_size": 1117582774
+ }
}
}
},
@@ -131,8 +159,12 @@
"url": "https://ct.googleapis.com/skydiver/",
"mmd": 86400,
"state": {
- "usable": {
- "timestamp": "2017-03-06T19:35:01Z"
+ "readonly": {
+ "timestamp": "2022-05-03T18:52:50Z",
+ "final_tree_head": {
+ "sha256_root_hash": "m8mPaxXwy+EdNwnfdsSWCKdhEk1bm/5Mp84ilMB5xvc=",
+ "tree_size": 312892829
+ }
}
}
}
@@ -246,6 +278,38 @@
}
},
{
+ "description": "DigiCert Yeti2024 Log",
+ "log_id": "SLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHM=",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV7jBbzCkfy7k8NDZYGITleN6405Tw7O4c4XBGA0jDliE0njvm7MeLBrewY+BGxlEWLcAd2AgGnLYgt6unrHGSw==",
+ "url": "https://yeti2024.ct.digicert.com/log/",
+ "mmd": 86400,
+ "state": {
+ "qualified": {
+ "timestamp": "2022-08-18T23:45:14Z"
+ }
+ },
+ "temporal_interval": {
+ "start_inclusive": "2024-01-01T00:00:00Z",
+ "end_exclusive": "2025-01-01T00:00:00Z"
+ }
+ },
+ {
+ "description": "DigiCert Yeti2025 Log",
+ "log_id": "fVkeEuF4KnscYWd8Xv340IdcFKBOlZ65Ay/ZDowuebg=",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE35UAXhDBAfc34xB00f+yypDtMplfDDn+odETEazRs3OTIMITPEy1elKGhj3jlSR82JGYSDvw8N8h8bCBWlklQw==",
+ "url": "https://yeti2025.ct.digicert.com/log/",
+ "mmd": 86400,
+ "state": {
+ "qualified": {
+ "timestamp": "2022-08-18T23:45:14Z"
+ }
+ },
+ "temporal_interval": {
+ "start_inclusive": "2025-01-01T00:00:00Z",
+ "end_exclusive": "2026-01-01T00:00:00Z"
+ }
+ },
+ {
"description": "DigiCert Nessie2022 Log",
"log_id": "UaOw9f0BeZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeU=",
"key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJyTdaAMoy/5jvg4RR019F2ihEV1McclBKMe2okuX7MCv/C87v+nxsfz1Af+p+0lADGMkmNd5LqZVqxbGvlHYcQ==",
@@ -278,39 +342,35 @@
}
},
{
- "description": "Symantec log",
- "log_id": "3esdK3oNT6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvsw=",
- "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEluqsHEYMG1XcDfy1lCdGV0JwOmkY4r87xNuroPS2bMBTP01CEDPwWJePa75y9CrsHEKqAy8afig1dpkIPSEUhg==",
- "url": "https://ct.ws.symantec.com/",
- "mmd": 86400,
- "state": {
- "retired": {
- "timestamp": "2019-02-16T00:00:00Z"
- }
- }
- },
- {
- "description": "Symantec 'Vega' log",
- "log_id": "vHjh38X2PGhGSTNNoQ+hXwl5aSAJwIG08/aRfz7ZuKU=",
- "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6pWeAv/u8TNtS4e8zf0ZF2L/lNPQWQc/Ai0ckP7IRzA78d0NuBEMXR2G3avTK0Zm+25ltzv9WWis36b4ztIYTQ==",
- "url": "https://vega.ws.symantec.com/",
+ "description": "DigiCert Nessie2024 Log",
+ "log_id": "c9meiRtMlnigIH1HneayxhzQUV5xGSqMa4AQesF3crU=",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELfyieza/VpHp/j/oPfzDp+BhUuos6QWjnycXgQVwa4FhRIr4OxCAQu0DLwBQIfxBVISjVNUusnoWSyofK2YEKw==",
+ "url": "https://nessie2024.ct.digicert.com/log/",
"mmd": 86400,
"state": {
- "retired": {
- "timestamp": "2019-02-16T00:00:00Z"
+ "qualified": {
+ "timestamp": "2022-08-18T23:45:14Z"
}
+ },
+ "temporal_interval": {
+ "start_inclusive": "2024-01-01T00:00:00Z",
+ "end_exclusive": "2025-01-01T00:00:00Z"
}
},
{
- "description": "Symantec 'Sirius' log",
- "log_id": "FZcEiNe5l6Bb61JRKt7o0ui0oxZSZBIan6v71fha2T8=",
- "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEowJkhCK7JewN47zCyYl93UXQ7uYVhY/Z5xcbE4Dq7bKFN61qxdglnfr0tPNuFiglN+qjN2Syxwv9UeXBBfQOtQ==",
- "url": "https://sirius.ws.symantec.com/",
+ "description": "DigiCert Nessie2025 Log",
+ "log_id": "5tIxY0B3jMEQQQbXcbnOwdJA9paEhvu6hzId/R43jlA=",
+ "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8vDwp4uBLgk5O59C2jhEX7TM7Ta72EN/FklXhwR/pQE09+hoP7d4H2BmLWeadYC3U6eF1byrRwZV27XfiKFvOA==",
+ "url": "https://nessie2025.ct.digicert.com/log/",
"mmd": 86400,
"state": {
- "retired": {
- "timestamp": "2019-02-16T00:00:00Z"
+ "qualified": {
+ "timestamp": "2022-08-18T23:45:14Z"
}
+ },
+ "temporal_interval": {
+ "start_inclusive": "2025-01-01T00:00:00Z",
+ "end_exclusive": "2026-01-01T00:00:00Z"
}
},
{
@@ -320,8 +380,8 @@
"url": "https://yeti2022-2.ct.digicert.com/log/",
"mmd": 86400,
"state": {
- "qualified": {
- "timestamp": "2022-03-11T00:00:00Z"
+ "usable": {
+ "timestamp": "2022-05-20T00:00:00Z"
}
},
"temporal_interval": {
@@ -332,126 +392,6 @@
]
},
{
- "name": "Certly",
- "email": [
- "ian@certly.io"
- ],
- "logs": [
- {
- "description": "Certly.IO log",
- "log_id": "zbUXm3/BwEb+6jETaj+PAC5hgvr4iW/syLL1tatgSQA=",
- "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2MNvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA==",
- "url": "https://log.certly.io/",
- "mmd": 86400,
- "state": {
- "retired": {
- "timestamp": "2016-04-15T00:00:00Z"
- }
- }
- }
- ]
- },
- {
- "name": "Izenpe",
- "email": [
- "atecnica@izenpe.net"
- ],
- "logs": [
- {
- "description": "Izenpe log",
- "log_id": "dGG0oJz7PUHXUVlXWy52SaRFqNJ3CbDMVkpkgrfrQaM=",
- "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ2Q5DC3cUBj4IQCiDu0s6j51up+TZAkAEcQRF6tczw90rLWXkJMAW7jr9yc92bIKgV8vDXU4lDeZHvYHduDuvg==",
- "url": "https://ct.izenpe.com/",
- "mmd": 86400,
- "state": {
- "retired": {
- "timestamp": "2016-05-30T00:00:00Z"
- }
- }
- }
- ]
- },
- {
- "name": "WoSign",
- "email": [
- "ctlog@wosign.com"
- ],
- "logs": [
- {
- "description": "WoSign log",
- "log_id": "QbLcLonmPOSvG6e7Kb9oxt7m+fHMBH4w3/rjs7olkmM=",
- "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzBGIey1my66PTTBmJxklIpMhRrQvAdPG+SvVyLpzmwai8IoCnNBrRhgwhbrpJIsO0VtwKAx+8TpFf1rzgkJgMQ==",
- "url": "https://ctlog.wosign.com/",
- "mmd": 86400,
- "state": {
- "retired": {
- "timestamp": "2018-02-12T23:59:59Z"
- }
- }
- }
- ]
- },
- {
- "name": "Venafi",
- "email": [
- "ctlog-admin@venafi.com"
- ],
- "logs": [
- {
- "description": "Venafi log",
- "log_id": "rDua7X+pZ0dXFZ5tfVdWcvnZgQCUHpve/+yhMTt1eC0=",
- "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAolpIHxdSlTXLo1s6H1OCdpSj/4DyHDc8wLG9wVmLqy1lk9fz4ATVmm+/1iN2Nk8jmctUKK2MFUtlWXZBSpym97M7frGlSaQXUWyA3CqQUEuIJOmlEjKTBEiQAvpfDjCHjlV2Be4qTM6jamkJbiWtgnYPhJL6ONaGTiSPm7Byy57iaz/hbckldSOIoRhYBiMzeNoA0DiRZ9KmfSeXZ1rB8y8X5urSW+iBzf2SaOfzBvDpcoTuAaWx2DPazoOl28fP1hZ+kHUYvxbcMjttjauCFx+JII0dmuZNIwjfeG/GBb9frpSX219k1O4Wi6OEbHEr8at/XQ0y7gTikOxBn/s5wQIDAQAB",
- "url": "https://ctlog.api.venafi.com/",
- "mmd": 86400,
- "state": {
- "retired": {
- "timestamp": "2017-02-28T18:42:26Z"
- }
- }
- }
- ]
- },
- {
- "name": "CNNIC",
- "email": [
- "ctlog-admin@cnnic.cn"
- ],
- "logs": [
- {
- "description": "CNNIC CT log",
- "log_id": "pXesnO11SN2PAltnokEInfhuD0duwgPC7L7bGF8oJjg=",
- "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7UIYZopMgTTJWPp2IXhhuAf1l6a9zM7gBvntj5fLaFm9pVKhKYhVnno94XuXeN8EsDgiSIJIj66FpUGvai5samyetZhLocRuXhAiXXbDNyQ4KR51tVebtEq2zT0mT9liTtGwiksFQccyUsaVPhsHq9gJ2IKZdWauVA2Fm5x9h8B9xKn/L/2IaMpkIYtd967TNTP/dLPgixN1PLCLaypvurDGSVDsuWabA3FHKWL9z8wr7kBkbdpEhLlg2H+NAC+9nGKx+tQkuhZ/hWR65aX+CNUPy2OB9/u2rNPyDydb988LENXoUcMkQT0dU3aiYGkFAY0uZjD2vH97TM20xYtNQIDAQAB",
- "url": "https://ctserver.cnnic.cn/",
- "mmd": 86400,
- "state": {
- "retired": {
- "timestamp": "2018-09-18T00:00:00Z"
- }
- }
- }
- ]
- },
- {
- "name": "StartCom",
- "email": [
- "ct@startssl.com"
- ],
- "logs": [
- {
- "description": "StartCom log",
- "log_id": "NLtq1sPfnAPuqKSZ/3iRSGydXlysktAfe/0bzhnbSO8=",
- "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESPNZ8/YFGNPbsu1Gfs/IEbVXsajWTOaft0oaFIZDqUiwy1o/PErK38SCFFWa+PeOQFXc9NKv6nV0+05/YIYuUQ==",
- "url": "https://ct.startssl.com/",
- "mmd": 86400,
- "state": {
- "retired": {
- "timestamp": "2018-02-12T23:59:59Z"
- }
- }
- }
- ]
- },
- {
"name": "Sectigo",
"email": [
"ctops@sectigo.com"
diff --git a/chromium/components/certificate_transparency/tools/PRESUBMIT.py b/chromium/components/certificate_transparency/tools/PRESUBMIT.py
index df9579e43f2..0da917400b2 100644
--- a/chromium/components/certificate_transparency/tools/PRESUBMIT.py
+++ b/chromium/components/certificate_transparency/tools/PRESUBMIT.py
@@ -19,12 +19,14 @@ def _RunMakeCTLogListTests(input_api, output_api):
test_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
'make_ct_known_logs_list_unittest.py')
cmd_name = 'make_ct_known_logs_list_unittest'
- cmd = [input_api.python_executable, test_path]
+ cmd = [test_path]
test_cmd = input_api.Command(
name=cmd_name,
cmd=cmd,
kwargs={},
- message=output_api.PresubmitPromptWarning)
+ message=output_api.PresubmitPromptWarning,
+ python3=True
+ )
return input_api.RunTests([test_cmd])
diff --git a/chromium/components/chrome_cleaner/public/constants/BUILD.gn b/chromium/components/chrome_cleaner/public/constants/BUILD.gn
index 38c17138375..7e16990007a 100644
--- a/chromium/components/chrome_cleaner/public/constants/BUILD.gn
+++ b/chromium/components/chrome_cleaner/public/constants/BUILD.gn
@@ -2,10 +2,26 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/buildflag_header.gni")
+import("//build/config/chrome_build.gni")
+
+declare_args() {
+ # Whether software reporter is enabled for this build. This defaults to true
+ # for Chrome branded builds, but can be disabled manually if a Chrome branded
+ # build is required without necessarily needing the software reporter.
+ enable_software_reporter = is_chrome_branded
+}
+
source_set("constants") {
sources = [
"constants.cc",
"constants.h",
"result_codes.h",
]
+ public_deps = [ ":buildflags" ]
+}
+
+buildflag_header("buildflags") {
+ header = "buildflags.h"
+ flags = [ "ENABLE_SOFTWARE_REPORTER=$enable_software_reporter" ]
}
diff --git a/chromium/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.cc b/chromium/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.cc
index 393200a2e54..e68b05a1615 100644
--- a/chromium/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.cc
+++ b/chromium/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.cc
@@ -5,7 +5,10 @@
#include "components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.h"
#include "base/check.h"
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/unsafe_shared_memory_region.h"
#include "base/notreached.h"
+#include "base/numerics/checked_math.h"
#include "base/time/time.h"
#include "media/base/ipc/media_param_traits_macros.h"
#include "mojo/public/cpp/base/time_mojom_traits.h"
@@ -69,7 +72,9 @@ bool EnumTraits<chromeos_camera::mojom::DecodeError,
mojo::ScopedSharedBufferHandle StructTraits<
chromeos_camera::mojom::BitstreamBufferDataView,
media::BitstreamBuffer>::memory_handle(media::BitstreamBuffer& input) {
- base::subtle::PlatformSharedMemoryRegion input_region = input.TakeRegion();
+ base::subtle::PlatformSharedMemoryRegion input_region =
+ base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+ input.TakeRegion());
DCHECK(input_region.IsValid()) << "Bad BitstreamBuffer handle";
// TODO(https://crbug.com/793446): Split BitstreamBuffers into ReadOnly and
@@ -105,14 +110,18 @@ bool StructTraits<chromeos_camera::mojom::BitstreamBufferDataView,
if (!handle.is_valid())
return false;
- auto memory_region =
- mojo::UnwrapPlatformSharedMemoryRegion(std::move(handle));
- if (!memory_region.IsValid())
+ auto region = base::UnsafeSharedMemoryRegion::Deserialize(
+ mojo::UnwrapPlatformSharedMemoryRegion(std::move(handle)));
+ if (!region.IsValid())
return false;
- media::BitstreamBuffer bitstream_buffer(
- input.id(), std::move(memory_region), input.size(),
- base::checked_cast<off_t>(input.offset()), timestamp);
+ auto offset = base::MakeCheckedNum(input.offset()).Cast<uint64_t>();
+ if (!offset.IsValid())
+ return false;
+
+ media::BitstreamBuffer bitstream_buffer(input.id(), std::move(region),
+ input.size(), offset.ValueOrDie(),
+ timestamp);
if (key_id.size()) {
// Note that BitstreamBuffer currently ignores how each buffer is
// encrypted and uses the settings from the Audio/VideoDecoderConfig.
diff --git a/chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.cc b/chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.cc
index 4f8c6dce8a1..1a0f9f12287 100644
--- a/chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.cc
+++ b/chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.cc
@@ -8,10 +8,11 @@
#include "base/bind.h"
#include "base/logging.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "media/base/bind_to_current_loop.h"
-#include "media/base/unaligned_shared_memory.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
@@ -60,10 +61,10 @@ void FakeMjpegDecodeAccelerator::Decode(
scoped_refptr<media::VideoFrame> video_frame) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
- auto src_shm = std::make_unique<media::UnalignedSharedMemory>(
- bitstream_buffer.TakeRegion(), bitstream_buffer.size(),
- false /* read_only */);
- if (!src_shm->MapAt(bitstream_buffer.offset(), bitstream_buffer.size())) {
+ base::UnsafeSharedMemoryRegion src_shm_region = bitstream_buffer.TakeRegion();
+ base::WritableSharedMemoryMapping src_shm_mapping =
+ src_shm_region.MapAt(bitstream_buffer.offset(), bitstream_buffer.size());
+ if (!src_shm_mapping.IsValid()) {
DLOG(ERROR) << "Unable to map shared memory in FakeMjpegDecodeAccelerator";
NotifyError(bitstream_buffer.id(),
MjpegDecodeAccelerator::UNREADABLE_INPUT);
@@ -75,7 +76,7 @@ void FakeMjpegDecodeAccelerator::Decode(
FROM_HERE,
base::BindOnce(&FakeMjpegDecodeAccelerator::DecodeOnDecoderThread,
base::Unretained(this), bitstream_buffer.id(),
- std::move(video_frame), std::move(src_shm)));
+ std::move(video_frame), std::move(src_shm_mapping)));
}
void FakeMjpegDecodeAccelerator::Decode(
@@ -90,7 +91,7 @@ void FakeMjpegDecodeAccelerator::Decode(
void FakeMjpegDecodeAccelerator::DecodeOnDecoderThread(
int32_t task_id,
scoped_refptr<media::VideoFrame> video_frame,
- std::unique_ptr<media::UnalignedSharedMemory> src_shm) {
+ base::WritableSharedMemoryMapping src_shm_mapping) {
DCHECK(decoder_task_runner_->BelongsToCurrentThread());
// Do not actually decode the Jpeg data.
diff --git a/chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.h b/chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.h
index 25f155f1154..b18f9552938 100644
--- a/chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.h
+++ b/chromium/components/chromeos_camera/fake_mjpeg_decode_accelerator.h
@@ -7,8 +7,7 @@
#include <stdint.h>
-#include <memory>
-
+#include "base/memory/shared_memory_mapping.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread.h"
#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
@@ -47,10 +46,9 @@ class FakeMjpegDecodeAccelerator : public MjpegDecodeAccelerator {
bool IsSupported() override;
private:
- void DecodeOnDecoderThread(
- int32_t task_id,
- scoped_refptr<media::VideoFrame> video_frame,
- std::unique_ptr<media::UnalignedSharedMemory> src_shm);
+ void DecodeOnDecoderThread(int32_t task_id,
+ scoped_refptr<media::VideoFrame> video_frame,
+ base::WritableSharedMemoryMapping src_shm_mapping);
void NotifyError(int32_t task_id, Error error);
void NotifyErrorOnClientThread(int32_t task_id, Error error);
void OnDecodeDoneOnClientThread(int32_t task_id);
diff --git a/chromium/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc b/chromium/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
index a65d033109b..1082d19c9a6 100644
--- a/chromium/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
+++ b/chromium/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
@@ -23,7 +23,6 @@
#include "base/gtest_prod_util.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
-#include "base/memory/platform_shared_memory_region.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/numerics/safe_conversions.h"
@@ -851,11 +850,8 @@ void JpegClient::StartDecode(int32_t task_id, bool do_prepare_memory) {
task.image->data_str.size(), 0 /* src_offset */,
hw_out_dmabuf_frame_);
} else {
- base::subtle::PlatformSharedMemoryRegion dup_region =
- base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
- in_shm_.Duplicate());
- ASSERT_EQ(dup_region.GetSize(), task.image->data_str.size());
- media::BitstreamBuffer bitstream_buffer(task_id, std::move(dup_region),
+ ASSERT_EQ(in_shm_.GetSize(), task.image->data_str.size());
+ media::BitstreamBuffer bitstream_buffer(task_id, in_shm_.Duplicate(),
task.image->data_str.size());
decoder_->Decode(std::move(bitstream_buffer), hw_out_frame_);
}
diff --git a/chromium/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc b/chromium/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
index e978755a18e..aa303a7749e 100644
--- a/chromium/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
+++ b/chromium/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
@@ -213,21 +213,23 @@ void MojoJpegEncodeAcceleratorService::EncodeWithFD(
base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
input_buffer_size, base::UnguessableToken::Create()));
- base::subtle::PlatformSharedMemoryRegion output_shm_region =
- base::subtle::PlatformSharedMemoryRegion::Take(
- std::move(output_fd),
- base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
- output_buffer_size, base::UnguessableToken::Create());
+ base::UnsafeSharedMemoryRegion output_shm_region =
+ base::UnsafeSharedMemoryRegion::Deserialize(
+ base::subtle::PlatformSharedMemoryRegion::Take(
+ std::move(output_fd),
+ base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
+ output_buffer_size, base::UnguessableToken::Create()));
media::BitstreamBuffer output_buffer(task_id, std::move(output_shm_region),
output_buffer_size);
std::unique_ptr<media::BitstreamBuffer> exif_buffer;
if (exif_buffer_size > 0) {
- base::subtle::PlatformSharedMemoryRegion exif_shm_region =
- base::subtle::PlatformSharedMemoryRegion::Take(
- base::subtle::ScopedFDPair(std::move(exif_fd), base::ScopedFD()),
- base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
- exif_buffer_size, base::UnguessableToken::Create());
+ base::UnsafeSharedMemoryRegion exif_shm_region =
+ base::UnsafeSharedMemoryRegion::Deserialize(
+ base::subtle::PlatformSharedMemoryRegion::Take(
+ std::move(exif_fd),
+ base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
+ exif_buffer_size, base::UnguessableToken::Create()));
exif_buffer = std::make_unique<media::BitstreamBuffer>(
task_id, std::move(exif_shm_region), exif_buffer_size);
}
@@ -336,11 +338,12 @@ void MojoJpegEncodeAcceleratorService::EncodeWithDmaBuf(
if (exif_buffer_size > 0) {
// Currently we use our zero-based |task_id| as id of |exif_buffer| to track
// the encode task process from both Chrome OS and Chrome side.
- base::subtle::PlatformSharedMemoryRegion exif_shm_region =
- base::subtle::PlatformSharedMemoryRegion::Take(
- base::subtle::ScopedFDPair(std::move(exif_fd), base::ScopedFD()),
- base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
- exif_buffer_size, base::UnguessableToken::Create());
+ base::UnsafeSharedMemoryRegion exif_shm_region =
+ base::UnsafeSharedMemoryRegion::Deserialize(
+ base::subtle::PlatformSharedMemoryRegion::Take(
+ std::move(exif_fd),
+ base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
+ exif_buffer_size, base::UnguessableToken::Create()));
exif_buffer = std::make_unique<media::BitstreamBuffer>(
task_id, std::move(exif_shm_region), exif_buffer_size);
}
diff --git a/chromium/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc b/chromium/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc
index ed5c282b9e9..4a15f4f9845 100644
--- a/chromium/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc
+++ b/chromium/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc
@@ -6,7 +6,7 @@
#include "base/bind.h"
#include "base/command_line.h"
-#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/unsafe_shared_memory_region.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
@@ -71,16 +71,14 @@ TEST_F(MojoMjpegDecodeAcceleratorServiceTest, InitializeAndDecode) {
subsamples.push_back(media::SubsampleEntry(15, 7));
base::RunLoop run_loop2;
- base::subtle::PlatformSharedMemoryRegion shm_region =
- base::subtle::PlatformSharedMemoryRegion::CreateUnsafe(
- kInputBufferSizeInBytes);
+ base::UnsafeSharedMemoryRegion shm_region =
+ base::UnsafeSharedMemoryRegion::Create(kInputBufferSizeInBytes);
// mojo::SharedBufferHandle::Create will make a writable region, but an unsafe
// one is needed.
mojo::ScopedSharedBufferHandle output_frame_handle =
- mojo::WrapPlatformSharedMemoryRegion(
- base::subtle::PlatformSharedMemoryRegion::CreateUnsafe(
- kOutputFrameSizeInBytes));
+ mojo::WrapUnsafeSharedMemoryRegion(
+ base::UnsafeSharedMemoryRegion::Create(kOutputFrameSizeInBytes));
media::BitstreamBuffer bitstream_buffer(kArbitraryBitstreamBufferId,
std::move(shm_region),
diff --git a/chromium/components/client_hints/OWNERS b/chromium/components/client_hints/OWNERS
index 6c2aca6d9ae..1268c375931 100644
--- a/chromium/components/client_hints/OWNERS
+++ b/chromium/components/client_hints/OWNERS
@@ -1,5 +1,4 @@
-aarontag@chromium.org
-abeyad@chromium.org
arichiv@chromium.org
ryansturm@chromium.org
+victortan@chromium.org
yoavweiss@chromium.org
diff --git a/chromium/components/client_hints/README.md b/chromium/components/client_hints/README.md
index 1a6624cc3e1..e9bf94a5fa9 100644
--- a/chromium/components/client_hints/README.md
+++ b/chromium/components/client_hints/README.md
@@ -62,7 +62,7 @@ The full explanation is outside of the scope of this document and can be found i
Client Hint preferences are stored in the preferences service as a content setting (`ContentSettingsType::CLIENT_HINTS`), keyed to the origin. This storage is accessed through the [content::ClientHintsControllerDelegate] interface, with the principle implementation being [client_hints::ClientHints] in //components (to share across multiple platforms). The delegate is accessible in the browser process as a property of the [content::BrowserContext] (in //chrome land, this is implemented as the Profile and “Off The Record†Profile. An important note is that there is an “incognito profile†that gets its own client hints storage).
-This storage is marked as `content_settings::SessionModel::UserSession`. This means that when settings are read in from disk (on browser start up) there’s also a check for a flag that’s set on graceful shutdown. (This is to exclude crashes and browser updates). If that flag is set, the settings are cleared. Practically, this means that the settings are cleared after closing the browser.
+This storage is marked as `content_settings::SessionModel::Durable`. This means that the client hint settings are read in from disk on browser start up and loaded into memory. Practically, this means that the client hint settings persist until the user clears site data or cookies for the origin.
The code for reading from and writing to the client hint preferences in content is in [/content/browser/client_hints/client_hints.cc]
@@ -138,9 +138,21 @@ TODO(crbug.com/1176808): There should be UseCounters measuring usage, but there
Client Hints are populated in [BaseFetchContext::AddClientHintsIfNecessary](/third_party/blink/renderer/core/loader/base_fetch_context.cc). If you need frame-based information, this should be added to [ClientHintsImageInfo](/third_party/blink/renderer/core/loader/base_fetch_context.cc), which is populated in [FrameFetchContext::AddClientHintsIfNecessary](/third_party/blink/renderer/core/loader/frame_fetch_context.cc)
+### Web platform tests
+* Add the new client hint to [/third_party/blink/web_tests/external/wpt/client-hints/resources/export.js], [/third_party/blink/web_tests/external/wpt/client-hints/resources/clienthintslist.py], [/third_party/blink/web_tests/external/wpt/client-hints/accept-ch/feature-policy-navigation/\_\_dir\_\_.headers], [/third_party/blink/web_tests/external/wpt/client-hints/sandbox/\_\_dir\_\_.headers], and [/third_party/blink/web_tests/external/wpt/client-hints/accept-ch/\_\_dir\_\_.headers]
+
### Devtools Backend
-Any addition to [blink::UserAgentMetadata](/third_party/blink/public/common/user_agent/user_agent_metadata.h) also needs to extend the related Chrome Devtools Protocol API calls, namely `setUserAgentOverride`. The backend implementation can be found in [third_party/blink/renderer/core/inspector/inspector_emulation_agent.h](/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h), and the UserAgentMetadata type in [third_party/blink/public/devtools_protocol/browser_protocol.pdl](/third_party/blink/public/devtools_protocol/browser_protocol.pdl) will also need to be extended.
+* Any addition to [blink::UserAgentMetadata](/third_party/blink/public/common/user_agent/user_agent_metadata.h) also needs to extend the related Chrome Devtools Protocol API calls, namely `setUserAgentOverride`. The backend implementation can be found in [/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h], and the UserAgentMetadata type in [/third_party/blink/public/devtools_protocol/browser_protocol.pdl] will also need to be extended.
+* Update overridden function `SetUserAgentOverride` in [/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc], and [/content/browser/devtools/protocol/emulation_handler.cc].
+* Add the new client hint to [/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/set-accept-ch.php] and update tests in [/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override.js].
+
+### Devtools Frontend
+
+Devtools frontend source code is in a different branch [devtools/devtools-frontend](https://chromium.googlesource.com/devtools/devtools-frontend).
+
+* Any addition to [blink::UserAgentMetadata] also needs to extend the related type `UserAgentMetadata` in [/third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.pdl], [/third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.json], and [/third_party/devtools-frontend/src/front_end/generated/protocol.d.ts].
+* Add the permission policy token to the `PermissionsPolicyFeature` enum in [/third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.pdl], and [/third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.json].
<!-- links -->
[/components/client_hints/]: /components/client_hints/
@@ -165,7 +177,18 @@ Any addition to [blink::UserAgentMetadata](/third_party/blink/public/common/user
[/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5]: /third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
[/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom]: /third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom
[/third_party/blink/public/devtools_protocol/browser_protocol.pdl]: /third_party/blink/public/devtools_protocol/browser_protocol.pdl
-[/third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.pdl]: /third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.pdl
-[/third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.json]: /third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.json
+[/third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.pdl]: https://chromium.googlesource.com/devtools/devtools-frontend/+/main/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+[/third_party/devtools-frontend/src/third_party/blink/public/devtools_protocol/browser_protocol.json]: https://chromium.googlesource.com/devtools/devtools-frontend/+/main/third_party/blink/public/devtools_protocol/browser_protocol.json
+[/third_party/devtools-frontend/src/front_end/generated/InspectorBackendCommands.js]: https://chromium.googlesource.com/devtools/devtools-frontend/+/main/front_end/generated/InspectorBackendCommands.js
[/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt]: /third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
[/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt]: /third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
+[/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h]: /third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
+[/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc]: /third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+[/content/browser/devtools/protocol/emulation_handler.cc]: /content/browser/devtools/protocol/emulation_handler.cc
+[/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/set-accept-ch.php]: /third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/set-accept-ch.php
+[/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override.js]: /third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override.js
+[/third_party/blink/web_tests/external/wpt/client-hints/resources/export.js]: /third_party/blink/web_tests/external/wpt/client-hints/resources/export.js
+[/third_party/blink/web_tests/external/wpt/client-hints/resources/clienthintslist.py]: /third_party/blink/web_tests/external/wpt/client-hints/resources/clienthintslist.py
+[/third_party/blink/web_tests/external/wpt/client-hints/accept-ch/feature-policy-navigation/\_\_dir\_\_.headers]: /third_party/blink/web_tests/external/wpt/client-hints/accept-ch/feature-policy-navigation/__dir__.headers
+[/third_party/blink/web_tests/external/wpt/client-hints/sandbox/\_\_dir\_\_.headers]: /third_party/blink/web_tests/external/wpt/client-hints/sandbox/__dir__.headers
+[/third_party/blink/web_tests/external/wpt/client-hints/accept-ch/\_\_dir\_\_.headers]: /third_party/blink/web_tests/external/wpt/client-hints/accept-ch/__dir__.headers \ No newline at end of file
diff --git a/chromium/components/client_hints/browser/client_hints.cc b/chromium/components/client_hints/browser/client_hints.cc
index 49376231f2e..95d4fc6bb5c 100644
--- a/chromium/components/client_hints/browser/client_hints.cc
+++ b/chromium/components/client_hints/browser/client_hints.cc
@@ -20,6 +20,7 @@
#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/embedder_support/user_agent_utils.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "services/network/public/cpp/client_hints.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
@@ -105,7 +106,8 @@ ClientHints::ClientHints(
auto command_line_hints = ParseInitializeClientHintsStroage();
for (const auto& origin_hints_pair : command_line_hints) {
- PersistClientHints(origin_hints_pair.first, origin_hints_pair.second);
+ PersistClientHints(origin_hints_pair.first, nullptr,
+ origin_hints_pair.second);
}
}
}
@@ -128,10 +130,14 @@ void ClientHints::GetAllowedClientHintsFromSource(
client_hints->SetIsEnabled(hint, true);
}
-bool ClientHints::IsJavaScriptAllowed(const GURL& url) {
- return settings_map_->GetContentSetting(url, url,
- ContentSettingsType::JAVASCRIPT) !=
- CONTENT_SETTING_BLOCK;
+bool ClientHints::IsJavaScriptAllowed(const GURL& url,
+ content::RenderFrameHost* parent_rfh) {
+ return settings_map_->GetContentSetting(
+ parent_rfh ? parent_rfh->GetOutermostMainFrame()
+ ->GetLastCommittedOrigin()
+ .GetURL()
+ : url,
+ url, ContentSettingsType::JAVASCRIPT) != CONTENT_SETTING_BLOCK;
}
bool ClientHints::AreThirdPartyCookiesBlocked(const GURL& url) {
@@ -146,6 +152,7 @@ blink::UserAgentMetadata ClientHints::GetUserAgentMetadata() {
void ClientHints::PersistClientHints(
const url::Origin& primary_origin,
+ content::RenderFrameHost* parent_rfh,
const std::vector<network::mojom::WebClientHintsType>& client_hints) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -157,7 +164,7 @@ void ClientHints::PersistClientHints(
!network::IsUrlPotentiallyTrustworthy(primary_url))
return;
- if (!IsJavaScriptAllowed(primary_url))
+ if (!IsJavaScriptAllowed(primary_url, parent_rfh))
return;
DCHECK_LE(
diff --git a/chromium/components/client_hints/browser/client_hints.h b/chromium/components/client_hints/browser/client_hints.h
index 207f99648df..50306ff11da 100644
--- a/chromium/components/client_hints/browser/client_hints.h
+++ b/chromium/components/client_hints/browser/client_hints.h
@@ -45,13 +45,15 @@ class ClientHints : public KeyedService,
const url::Origin& origin,
blink::EnabledClientHints* client_hints) override;
- bool IsJavaScriptAllowed(const GURL& url) override;
+ bool IsJavaScriptAllowed(const GURL& url,
+ content::RenderFrameHost* parent_rfh) override;
bool AreThirdPartyCookiesBlocked(const GURL& url) override;
blink::UserAgentMetadata GetUserAgentMetadata() override;
void PersistClientHints(const url::Origin& primary_origin,
+ content::RenderFrameHost* parent_rfh,
const std::vector<network::mojom::WebClientHintsType>&
client_hints) override;
diff --git a/chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.cc b/chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.cc
index 9a70359091c..3bac2260e82 100644
--- a/chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.cc
+++ b/chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.cc
@@ -32,6 +32,7 @@ InMemoryClientHintsControllerDelegate::
// implementation.
void InMemoryClientHintsControllerDelegate::PersistClientHints(
const url::Origin& primary_origin,
+ content::RenderFrameHost* parent_rfh,
const std::vector<network::mojom::WebClientHintsType>& client_hints) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const GURL primary_url = primary_origin.GetURL();
@@ -39,7 +40,7 @@ void InMemoryClientHintsControllerDelegate::PersistClientHints(
DCHECK(network::IsUrlPotentiallyTrustworthy(primary_url));
// Client hints should only be enabled when JavaScript is enabled.
- if (!IsJavaScriptAllowed(primary_url))
+ if (!IsJavaScriptAllowed(primary_url, parent_rfh))
return;
blink::EnabledClientHints enabled_hints;
@@ -84,7 +85,8 @@ InMemoryClientHintsControllerDelegate::GetNetworkQualityTracker() {
}
bool InMemoryClientHintsControllerDelegate::IsJavaScriptAllowed(
- const GURL& url) {
+ const GURL& url,
+ content::RenderFrameHost* parent_rfh) {
return is_javascript_allowed_callback_.Run(url);
}
diff --git a/chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.h b/chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.h
index 004204dfea8..8a13c5d73a2 100644
--- a/chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.h
+++ b/chromium/components/client_hints/browser/in_memory_client_hints_controller_delegate.h
@@ -51,6 +51,7 @@ class InMemoryClientHintsControllerDelegate final
// content::ClientHintsControllerDelegate implementation:
void PersistClientHints(const url::Origin& primary_origin,
+ content::RenderFrameHost* parent_rfh,
const std::vector<network::mojom::WebClientHintsType>&
client_hints) override;
void GetAllowedClientHintsFromSource(
@@ -60,7 +61,8 @@ class InMemoryClientHintsControllerDelegate final
const std::vector<network::mojom::WebClientHintsType>&) override;
void ClearAdditionalClientHints() override;
network::NetworkQualityTracker* GetNetworkQualityTracker() override;
- bool IsJavaScriptAllowed(const GURL& url) override;
+ bool IsJavaScriptAllowed(const GURL& url,
+ content::RenderFrameHost* parent_rfh) override;
bool AreThirdPartyCookiesBlocked(const GURL& url) override;
blink::UserAgentMetadata GetUserAgentMetadata() override;
diff --git a/chromium/components/cloud_devices/common/cloud_device_description.cc b/chromium/components/cloud_devices/common/cloud_device_description.cc
index 468ea54eeb9..0c3cc3e8fe3 100644
--- a/chromium/components/cloud_devices/common/cloud_device_description.cc
+++ b/chromium/components/cloud_devices/common/cloud_device_description.cc
@@ -15,7 +15,7 @@ namespace cloud_devices {
namespace {
-bool IsValidTicketImpl(const base::Value::Dict& value) {
+bool IsValidTicket(const base::Value::Dict& value) {
const std::string* version = value.FindString(json::kVersion);
return version && *version == json::kVersion10;
}
@@ -40,18 +40,10 @@ bool CloudDeviceDescription::InitFromValue(base::Value ticket) {
if (!ticket.is_dict())
return false;
root_ = std::move(ticket.GetDict());
- return IsValidTicketImpl(root_);
+ return IsValidTicket(root_);
}
-// static
-bool CloudDeviceDescription::IsValidTicket(const base::Value& ticket) {
- if (!ticket.is_dict())
- return false;
-
- return IsValidTicketImpl(ticket.GetDict());
-}
-
-std::string CloudDeviceDescription::ToString() const {
+std::string CloudDeviceDescription::ToStringForTesting() const {
std::string json;
base::JSONWriter::WriteWithOptions(
root_, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
diff --git a/chromium/components/cloud_devices/common/cloud_device_description.h b/chromium/components/cloud_devices/common/cloud_device_description.h
index 0fa35b5d634..0082adc16c2 100644
--- a/chromium/components/cloud_devices/common/cloud_device_description.h
+++ b/chromium/components/cloud_devices/common/cloud_device_description.h
@@ -28,9 +28,7 @@ class CloudDeviceDescription {
bool InitFromString(const std::string& json);
bool InitFromValue(base::Value value);
- static bool IsValidTicket(const base::Value& value);
-
- std::string ToString() const;
+ std::string ToStringForTesting() const;
base::Value ToValue() &&;
diff --git a/chromium/components/cloud_devices/common/printer_description.cc b/chromium/components/cloud_devices/common/printer_description.cc
index 3ce69e0278c..7c9c71ae3e1 100644
--- a/chromium/components/cloud_devices/common/printer_description.cc
+++ b/chromium/components/cloud_devices/common/printer_description.cc
@@ -1038,8 +1038,7 @@ class PwgRasterConfigTraits : public NoValueValidation,
if (document_types_supported) {
if (!document_types_supported->is_list())
return false;
- for (const auto& type_value :
- document_types_supported->GetListDeprecated()) {
+ for (const auto& type_value : document_types_supported->GetList()) {
if (!type_value.is_string())
return false;
diff --git a/chromium/components/cloud_devices/common/printer_description_unittest.cc b/chromium/components/cloud_devices/common/printer_description_unittest.cc
index e52ab75c6b5..2eea2876f3e 100644
--- a/chromium/components/cloud_devices/common/printer_description_unittest.cc
+++ b/chromium/components/cloud_devices/common/printer_description_unittest.cc
@@ -622,7 +622,8 @@ const struct TestTypedValueCapabilities {
TEST(PrinterDescriptionTest, CddInit) {
CloudDeviceDescription description;
- EXPECT_EQ(NormalizeJson(kDefaultCdd), NormalizeJson(description.ToString()));
+ EXPECT_EQ(NormalizeJson(kDefaultCdd),
+ NormalizeJson(description.ToStringForTesting()));
ContentTypesCapability content_types;
PwgRasterConfigCapability pwg_raster;
@@ -741,7 +742,8 @@ TEST(PrinterDescriptionTest, CddSetAll) {
reverse.SaveTo(&description);
pwg_raster_config.SaveTo(&description);
- EXPECT_EQ(NormalizeJson(kCdd), NormalizeJson(description.ToString()));
+ EXPECT_EQ(NormalizeJson(kCdd),
+ NormalizeJson(description.ToStringForTesting()));
}
TEST(PrinterDescriptionTest, CddGetDocumentTypeSupported) {
@@ -841,7 +843,7 @@ TEST(PrinterDescriptionTest, CddSetDocumentTypeSupported) {
pwg_raster.SaveTo(&description);
EXPECT_EQ(NormalizeJson(kDocumentTypeColorOnlyCdd),
- NormalizeJson(description.ToString()));
+ NormalizeJson(description.ToStringForTesting()));
}
{
CloudDeviceDescription description;
@@ -856,7 +858,7 @@ TEST(PrinterDescriptionTest, CddSetDocumentTypeSupported) {
pwg_raster.SaveTo(&description);
EXPECT_EQ(NormalizeJson(kDocumentTypeGrayOnlyCdd),
- NormalizeJson(description.ToString()));
+ NormalizeJson(description.ToStringForTesting()));
}
{
CloudDeviceDescription description;
@@ -873,7 +875,7 @@ TEST(PrinterDescriptionTest, CddSetDocumentTypeSupported) {
pwg_raster.SaveTo(&description);
EXPECT_EQ(NormalizeJson(kDocumentTypeColorAndGrayCdd),
- NormalizeJson(description.ToString()));
+ NormalizeJson(description.ToStringForTesting()));
}
{
CloudDeviceDescription description;
@@ -886,7 +888,7 @@ TEST(PrinterDescriptionTest, CddSetDocumentTypeSupported) {
pwg_raster.SaveTo(&description);
EXPECT_EQ(NormalizeJson(kDocumentTypeNoneCdd),
- NormalizeJson(description.ToString()));
+ NormalizeJson(description.ToStringForTesting()));
}
}
@@ -1090,7 +1092,7 @@ TEST(PrinterDescriptionTest, CddSetVendorCapability) {
vendor_capabilities.SaveTo(&description);
EXPECT_EQ(NormalizeJson(kVendorCapabilityOnlyCdd),
- NormalizeJson(description.ToString()));
+ NormalizeJson(description.ToStringForTesting()));
}
#if BUILDFLAG(IS_CHROMEOS)
@@ -1117,7 +1119,8 @@ TEST(PrinterDescriptionTest, CddSetPin) {
PinCapability pin_capability;
pin_capability.set_value(true);
pin_capability.SaveTo(&description);
- EXPECT_EQ(NormalizeJson(kPinOnlyCdd), NormalizeJson(description.ToString()));
+ EXPECT_EQ(NormalizeJson(kPinOnlyCdd),
+ NormalizeJson(description.ToStringForTesting()));
}
#endif // BUILDFLAG(IS_CHROMEOS)
@@ -1211,12 +1214,14 @@ TEST(PrinterDescriptionTest, CddGetAll) {
EXPECT_FALSE(collate.default_value());
EXPECT_TRUE(reverse.default_value());
- EXPECT_EQ(NormalizeJson(kCdd), NormalizeJson(description.ToString()));
+ EXPECT_EQ(NormalizeJson(kCdd),
+ NormalizeJson(description.ToStringForTesting()));
}
TEST(PrinterDescriptionTest, CjtInit) {
CloudDeviceDescription description;
- EXPECT_EQ(NormalizeJson(kDefaultCjt), NormalizeJson(description.ToString()));
+ EXPECT_EQ(NormalizeJson(kDefaultCjt),
+ NormalizeJson(description.ToStringForTesting()));
PwgRasterConfigTicketItem pwg_raster_config;
ColorTicketItem color;
@@ -1300,7 +1305,8 @@ TEST(PrinterDescriptionTest, CjtSetAll) {
collate.SaveTo(&description);
reverse.SaveTo(&description);
- EXPECT_EQ(NormalizeJson(kCjt), NormalizeJson(description.ToString()));
+ EXPECT_EQ(NormalizeJson(kCjt),
+ NormalizeJson(description.ToStringForTesting()));
}
TEST(PrinterDescriptionTest, CjtGetAll) {
@@ -1353,7 +1359,8 @@ TEST(PrinterDescriptionTest, CjtGetAll) {
EXPECT_FALSE(collate.value());
EXPECT_TRUE(reverse.value());
- EXPECT_EQ(NormalizeJson(kCjt), NormalizeJson(description.ToString()));
+ EXPECT_EQ(NormalizeJson(kCjt),
+ NormalizeJson(description.ToStringForTesting()));
}
} // namespace printer
diff --git a/chromium/components/commerce/content/BUILD.gn b/chromium/components/commerce/content/browser/BUILD.gn
index 751af54828e..5318123d0aa 100644
--- a/chromium/components/commerce/content/BUILD.gn
+++ b/chromium/components/commerce/content/browser/BUILD.gn
@@ -2,18 +2,17 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-static_library("metrics") {
+static_library("browser") {
sources = [
- "metrics/commerce_metrics_tab_helper.cc",
- "metrics/commerce_metrics_tab_helper.h",
+ "commerce_tab_helper.cc",
+ "commerce_tab_helper.h",
+ "web_contents_wrapper.cc",
+ "web_contents_wrapper.h",
]
deps = [
"//base",
- "//components/commerce/core:metrics",
- "//components/optimization_guide/content/browser",
- "//components/optimization_guide/proto:optimization_guide_proto",
- "//components/prefs",
+ "//components/commerce/core:shopping_service",
"//content/public/browser",
]
}
diff --git a/chromium/components/commerce/content/browser/commerce_tab_helper.cc b/chromium/components/commerce/content/browser/commerce_tab_helper.cc
new file mode 100644
index 00000000000..7339afa712d
--- /dev/null
+++ b/chromium/components/commerce/content/browser/commerce_tab_helper.cc
@@ -0,0 +1,61 @@
+// Copyright 2022 The Chromium 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/commerce/content/browser/commerce_tab_helper.h"
+
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+
+namespace commerce {
+
+CommerceTabHelper::CommerceTabHelper(content::WebContents* content,
+ bool is_off_the_record,
+ ShoppingService* shopping_service,
+ int32_t js_world_id)
+ : content::WebContentsObserver(content),
+ content::WebContentsUserData<CommerceTabHelper>(*content),
+ is_off_the_record_(is_off_the_record),
+ web_wrapper_(std::make_unique<WebContentsWrapper>(content, js_world_id)),
+ shopping_service_(shopping_service) {
+ if (shopping_service_)
+ shopping_service_->WebWrapperCreated(web_wrapper_.get());
+}
+
+CommerceTabHelper::~CommerceTabHelper() = default;
+
+void CommerceTabHelper::DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (!shopping_service_ || navigation_handle->IsSameDocument() ||
+ !navigation_handle->IsInPrimaryMainFrame()) {
+ return;
+ }
+
+ // Notify the service that we're no longer interested in a particular URL.
+ shopping_service_->DidNavigateAway(web_wrapper_.get(),
+ previous_main_frame_url_);
+ previous_main_frame_url_ = web_wrapper_->GetLastCommittedURL();
+
+ shopping_service_->DidNavigatePrimaryMainFrame(web_wrapper_.get());
+}
+
+void CommerceTabHelper::DidFinishLoad(
+ content::RenderFrameHost* render_frame_host,
+ const GURL& validated_url) {
+ if (!shopping_service_)
+ return;
+
+ shopping_service_->DidFinishLoad(web_wrapper_.get());
+}
+
+void CommerceTabHelper::WebContentsDestroyed() {
+ if (shopping_service_)
+ shopping_service_->WebWrapperDestroyed(web_wrapper_.get());
+
+ web_wrapper_->ClearWebContentsPointer();
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(CommerceTabHelper);
+
+} // namespace commerce
diff --git a/chromium/components/commerce/content/browser/commerce_tab_helper.h b/chromium/components/commerce/content/browser/commerce_tab_helper.h
new file mode 100644
index 00000000000..35f2c0dd9ad
--- /dev/null
+++ b/chromium/components/commerce/content/browser/commerce_tab_helper.h
@@ -0,0 +1,71 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_COMMERCE_CONTENT_BROWSER_COMMERCE_TAB_HELPER_H_
+#define COMPONENTS_COMMERCE_CONTENT_BROWSER_COMMERCE_TAB_HELPER_H_
+
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/commerce/content/browser/web_contents_wrapper.h"
+#include "components/commerce/core/shopping_service.h"
+#include "components/commerce/core/web_wrapper.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class NavigationHandle;
+class RenderFrameHost;
+class WebContents;
+} // namespace content
+
+namespace commerce {
+
+// This tab helper creates and maintains a WebWrapper that is backed by
+// WebContents. Events that occur on the wrapper are reported back to the
+// shopping service where they are used by various commerce features.
+class CommerceTabHelper
+ : public content::WebContentsObserver,
+ public content::WebContentsUserData<CommerceTabHelper> {
+ public:
+ ~CommerceTabHelper() override;
+ CommerceTabHelper(const CommerceTabHelper& other) = delete;
+ CommerceTabHelper& operator=(const CommerceTabHelper& other) = delete;
+
+ // content::WebContentsObserver implementation
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override;
+
+ void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+ const GURL& validated_url) override;
+
+ void WebContentsDestroyed() override;
+
+ private:
+ friend class content::WebContentsUserData<CommerceTabHelper>;
+
+ CommerceTabHelper(content::WebContents* contents,
+ bool is_off_the_record,
+ ShoppingService* shopping_service,
+ int32_t js_world_id);
+
+ const bool is_off_the_record_;
+
+ std::unique_ptr<WebContentsWrapper> web_wrapper_;
+
+ raw_ptr<ShoppingService> shopping_service_;
+
+ // The url from the previous successful main frame navigation. This will be
+ // empty if this is the first navigation for this tab or post-restart. We keep
+ // track of this because the URL kepkt by the backing WebContents will have
+ // changed before we get the signal for it.
+ GURL previous_main_frame_url_;
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+} // namespace commerce
+
+#endif // COMPONENTS_COMMERCE_CONTENT_BROWSER_COMMERCE_TAB_HELPER_H_
diff --git a/chromium/components/commerce/content/hint/commerce_hint_tab_helper.cc b/chromium/components/commerce/content/browser/hint/commerce_hint_tab_helper.cc
index a9c049acc16..4aff0bbe7f0 100644
--- a/chromium/components/commerce/content/hint/commerce_hint_tab_helper.cc
+++ b/chromium/components/commerce/content/browser/hint/commerce_hint_tab_helper.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/commerce/content/hint/commerce_hint_tab_helper.h"
+#include "components/commerce/content/browser/hint/commerce_hint_tab_helper.h"
namespace commerce_hint {
diff --git a/chromium/components/commerce/content/hint/commerce_hint_tab_helper.h b/chromium/components/commerce/content/browser/hint/commerce_hint_tab_helper.h
index 68c417202b2..5457ef7d8e7 100644
--- a/chromium/components/commerce/content/hint/commerce_hint_tab_helper.h
+++ b/chromium/components/commerce/content/browser/hint/commerce_hint_tab_helper.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_COMMERCE_CONTENT_HINT_COMMERCE_HINT_TAB_HELPER_H_
-#define COMPONENTS_COMMERCE_CONTENT_HINT_COMMERCE_HINT_TAB_HELPER_H_
+#ifndef COMPONENTS_COMMERCE_CONTENT_BROWSER_HINT_COMMERCE_HINT_TAB_HELPER_H_
+#define COMPONENTS_COMMERCE_CONTENT_BROWSER_HINT_COMMERCE_HINT_TAB_HELPER_H_
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
@@ -34,4 +34,4 @@ class CommerceHintTabHelper
} // namespace commerce_hint
-#endif // COMPONENTS_COMMERCE_CONTENT_HINT_COMMERCE_HINT_TAB_HELPER_H_
+#endif // COMPONENTS_COMMERCE_CONTENT_BROWSER_HINT_COMMERCE_HINT_TAB_HELPER_H_
diff --git a/chromium/components/commerce/content/browser/web_contents_wrapper.cc b/chromium/components/commerce/content/browser/web_contents_wrapper.cc
new file mode 100644
index 00000000000..e363f90d869
--- /dev/null
+++ b/chromium/components/commerce/content/browser/web_contents_wrapper.cc
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium 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/commerce/content/browser/web_contents_wrapper.h"
+
+#include "base/values.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_frame_host.h"
+
+namespace commerce {
+
+WebContentsWrapper::WebContentsWrapper(content::WebContents* web_contents,
+ int32_t js_world_id)
+ : web_contents_(web_contents), js_world_id_(js_world_id) {}
+
+const GURL& WebContentsWrapper::GetLastCommittedURL() {
+ if (!web_contents_)
+ return std::move(GURL());
+
+ return web_contents_->GetLastCommittedURL();
+}
+
+bool WebContentsWrapper::IsOffTheRecord() {
+ if (!web_contents_ || !web_contents_->GetBrowserContext())
+ return false;
+
+ return web_contents_->GetBrowserContext()->IsOffTheRecord();
+}
+
+void WebContentsWrapper::RunJavascript(
+ const std::u16string& script,
+ base::OnceCallback<void(const base::Value)> callback) {
+ if (!web_contents_ && web_contents_->GetPrimaryMainFrame()) {
+ std::move(callback).Run(base::Value());
+ return;
+ }
+
+ web_contents_->GetPrimaryMainFrame()->ExecuteJavaScriptInIsolatedWorld(
+ script, std::move(callback), js_world_id_);
+}
+
+void WebContentsWrapper::ClearWebContentsPointer() {
+ web_contents_ = nullptr;
+}
+
+} // namespace commerce
diff --git a/chromium/components/commerce/content/browser/web_contents_wrapper.h b/chromium/components/commerce/content/browser/web_contents_wrapper.h
new file mode 100644
index 00000000000..61199be7a1e
--- /dev/null
+++ b/chromium/components/commerce/content/browser/web_contents_wrapper.h
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_COMMERCE_CONTENT_BROWSER_WEB_CONTENTS_WRAPPER_H_
+#define COMPONENTS_COMMERCE_CONTENT_BROWSER_WEB_CONTENTS_WRAPPER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "components/commerce/core/web_wrapper.h"
+#include "content/public/browser/web_contents.h"
+
+class GURL;
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace commerce {
+
+// A WebWrapper backed by content::WebContents.
+class WebContentsWrapper : public WebWrapper {
+ public:
+ explicit WebContentsWrapper(content::WebContents* web_contents,
+ int32_t js_world_id);
+ WebContentsWrapper(const WebContentsWrapper&) = delete;
+ WebContentsWrapper operator=(const WebContentsWrapper&) = delete;
+ ~WebContentsWrapper() override = default;
+
+ const GURL& GetLastCommittedURL() override;
+
+ bool IsOffTheRecord() override;
+
+ void RunJavascript(
+ const std::u16string& script,
+ base::OnceCallback<void(const base::Value)> callback) override;
+
+ void ClearWebContentsPointer();
+
+ private:
+ base::raw_ptr<content::WebContents> web_contents_;
+
+ // The ID of the isolated world to run javascript in.
+ int32_t js_world_id_;
+};
+
+} // namespace commerce
+
+#endif // COMPONENTS_COMMERCE_CONTENT_BROWSER_WEB_CONTENTS_WRAPPER_H_
diff --git a/chromium/components/commerce/content/metrics/commerce_metrics_tab_helper.cc b/chromium/components/commerce/content/metrics/commerce_metrics_tab_helper.cc
deleted file mode 100644
index 9a9807cbaff..00000000000
--- a/chromium/components/commerce/content/metrics/commerce_metrics_tab_helper.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2022 The Chromium 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/commerce/content/metrics/commerce_metrics_tab_helper.h"
-
-#include "base/bind.h"
-#include "components/commerce/core/metrics/metrics_utils.h"
-#include "components/optimization_guide/content/browser/optimization_guide_decider.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-
-namespace commerce::metrics {
-
-CommerceMetricsTabHelper::CommerceMetricsTabHelper(
- content::WebContents* content,
- optimization_guide::OptimizationGuideDecider* optimization_guide,
- PrefService* pref_service,
- bool is_off_the_record)
- : content::WebContentsObserver(content),
- content::WebContentsUserData<CommerceMetricsTabHelper>(*content),
- optimization_guide_(optimization_guide),
- pref_service_(pref_service),
- is_off_the_record_(is_off_the_record),
- weak_ptr_factory_(this) {
- // In tests |optimization_guide_| can be null.
- if (optimization_guide_) {
- std::vector<optimization_guide::proto::OptimizationType> types;
- types.push_back(
- optimization_guide::proto::OptimizationType::PRICE_TRACKING);
- optimization_guide_->RegisterOptimizationTypes(types);
- }
-}
-
-CommerceMetricsTabHelper::~CommerceMetricsTabHelper() = default;
-
-void CommerceMetricsTabHelper::DidFinishNavigation(
- content::NavigationHandle* navigation_handle) {
- if (!navigation_handle->HasCommitted() ||
- navigation_handle->IsSameDocument() ||
- !navigation_handle->IsInPrimaryMainFrame() || !optimization_guide_) {
- return;
- }
-
- optimization_guide_->CanApplyOptimizationAsync(
- navigation_handle,
- optimization_guide::proto::OptimizationType::PRICE_TRACKING,
- base::BindOnce(&CommerceMetricsTabHelper::OnOptimizationGuideResult,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void CommerceMetricsTabHelper::OnOptimizationGuideResult(
- optimization_guide::OptimizationGuideDecision decision,
- const optimization_guide::OptimizationMetadata& metadata) {
- RecordPDPStateForNavigation(decision, metadata, pref_service_,
- is_off_the_record_);
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(CommerceMetricsTabHelper);
-
-} // namespace commerce::metrics
diff --git a/chromium/components/commerce/content/metrics/commerce_metrics_tab_helper.h b/chromium/components/commerce/content/metrics/commerce_metrics_tab_helper.h
deleted file mode 100644
index 1b41eaf7190..00000000000
--- a/chromium/components/commerce/content/metrics/commerce_metrics_tab_helper.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2022 The Chromium 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 COMPONENTS_COMMERCE_CONTENT_METRICS_COMMERCE_METRICS_TAB_HELPER_H_
-#define COMPONENTS_COMMERCE_CONTENT_METRICS_COMMERCE_METRICS_TAB_HELPER_H_
-
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/core/optimization_guide_decision.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-class PrefService;
-
-namespace content {
-class NavigationHandle;
-class WebContents;
-} // namespace content
-
-namespace optimization_guide {
-class OptimizationGuideDecider;
-} // namespace optimization_guide
-
-namespace commerce::metrics {
-
-class CommerceMetricsTabHelper
- : public content::WebContentsObserver,
- public content::WebContentsUserData<CommerceMetricsTabHelper> {
- public:
- ~CommerceMetricsTabHelper() override;
- CommerceMetricsTabHelper(const CommerceMetricsTabHelper& other) = delete;
- CommerceMetricsTabHelper& operator=(const CommerceMetricsTabHelper& other) =
- delete;
-
- void DidFinishNavigation(
- content::NavigationHandle* navigation_handle) override;
-
- private:
- friend class content::WebContentsUserData<CommerceMetricsTabHelper>;
-
- CommerceMetricsTabHelper(
- content::WebContents* contents,
- optimization_guide::OptimizationGuideDecider* optimization_guide,
- PrefService* pref_service,
- bool is_off_the_record);
-
- void OnOptimizationGuideResult(
- optimization_guide::OptimizationGuideDecision decision,
- const optimization_guide::OptimizationMetadata& metadata);
-
- raw_ptr<optimization_guide::OptimizationGuideDecider> optimization_guide_;
-
- raw_ptr<PrefService> pref_service_;
-
- bool is_off_the_record_;
-
- base::WeakPtrFactory<CommerceMetricsTabHelper> weak_ptr_factory_;
-
- WEB_CONTENTS_USER_DATA_KEY_DECL();
-};
-
-} // namespace commerce::metrics
-
-#endif // COMPONENTS_COMMERCE_CONTENT_METRICS_COMMERCE_METRICS_TAB_HELPER_H_
diff --git a/chromium/components/commerce/core/BUILD.gn b/chromium/components/commerce/core/BUILD.gn
index 81799d09902..9318dd57656 100644
--- a/chromium/components/commerce/core/BUILD.gn
+++ b/chromium/components/commerce/core/BUILD.gn
@@ -24,32 +24,26 @@ source_set("feature_list") {
]
deps = [
+ ":commerce_heuristics_data",
"//base",
"//components/flags_ui",
"//components/search",
"//third_party/re2:re2",
"//url:url",
]
-
- if (!is_android) {
- deps += [ ":commerce_heuristics_data" ]
- }
}
source_set("feature_list_unittests") {
testonly = true
sources = [ "commerce_feature_list_unittest.cc" ]
deps = [
+ ":commerce_heuristics_data",
":feature_list",
"//base",
"//base/test:test_support",
"//testing/gtest",
"//third_party/re2:re2",
]
-
- if (!is_android) {
- deps += [ ":commerce_heuristics_data" ]
- }
}
static_library("metrics") {
@@ -69,7 +63,10 @@ static_library("metrics") {
proto_library("proto") {
proto_in_dir = "//"
- sources = [ "proto/price_tracking.proto" ]
+ sources = [
+ "proto/merchant_trust.proto",
+ "proto/price_tracking.proto",
+ ]
}
if (is_android) {
@@ -79,62 +76,72 @@ if (is_android) {
}
}
-if (!is_android) {
- source_set("commerce_heuristics_data") {
- sources = [
- "commerce_heuristics_data.cc",
- "commerce_heuristics_data.h",
- ]
+source_set("commerce_heuristics_data") {
+ sources = [
+ "commerce_heuristics_data.cc",
+ "commerce_heuristics_data.h",
+ "commerce_heuristics_data_metrics_helper.cc",
+ "commerce_heuristics_data_metrics_helper.h",
+ ]
- deps = [
- "//base",
- "//third_party/re2:re2",
- ]
- }
+ deps = [
+ "//base",
+ "//third_party/re2:re2",
+ ]
+}
- source_set("commerce_heuristics_data_unittests") {
- testonly = true
- sources = [ "commerce_heuristics_data_unittest.cc" ]
- deps = [
- ":commerce_heuristics_data",
- "//base",
- "//base/test:test_support",
- "//testing/gtest",
- "//third_party/re2:re2",
- ]
- }
+source_set("commerce_heuristics_data_unittests") {
+ testonly = true
+ sources = [ "commerce_heuristics_data_unittest.cc" ]
+ deps = [
+ ":commerce_heuristics_data",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gtest",
+ "//third_party/re2:re2",
+ ]
+}
- source_set("heuristics_provider") {
- sources = [
- "heuristics/commerce_heuristics_provider.cc",
- "heuristics/commerce_heuristics_provider.h",
- ]
+source_set("heuristics_provider") {
+ sources = [
+ "heuristics/commerce_heuristics_provider.cc",
+ "heuristics/commerce_heuristics_provider.h",
+ ]
- deps = [
- ":commerce_heuristics_data",
- ":feature_list",
- "//base",
- "//components/resources:components_resources_grit",
- "//net",
- "//third_party/re2:re2",
- "//ui/base",
- "//url:url",
- ]
- }
+ deps = [
+ ":commerce_heuristics_data",
+ ":feature_list",
+ "//base",
+ "//components/resources:components_resources_grit",
+ "//net",
+ "//third_party/re2:re2",
+ "//ui/base",
+ "//url:url",
+ ]
}
static_library("shopping_service") {
sources = [
"pref_names.cc",
"pref_names.h",
+ "shopping_bookmark_model_observer.cc",
+ "shopping_bookmark_model_observer.h",
"shopping_service.cc",
"shopping_service.h",
+ "web_wrapper.h",
]
deps = [
+ ":feature_list",
+ ":metrics",
+ ":proto",
"//base",
+ "//components/bookmarks/browser",
"//components/keyed_service/core",
+ "//components/optimization_guide/core",
+ "//components/optimization_guide/proto:optimization_guide_proto",
"//components/prefs:prefs",
+ "//url:url",
]
if (is_android) {
@@ -143,7 +150,10 @@ static_library("shopping_service") {
"android/shopping_service_android.h",
]
- deps += [ ":shopping_service_jni_headers" ]
+ deps += [
+ ":shopping_service_jni_headers",
+ "//url:gurl_android",
+ ]
}
}
@@ -152,3 +162,29 @@ if (is_android) {
sources = [ "android/java/src/org/chromium/components/commerce/core/ShoppingService.java" ]
}
}
+
+source_set("shopping_service_unit_tests") {
+ testonly = true
+
+ sources = [
+ "pdp_metrics_unittest.cc",
+ "shopping_service_test_base.cc",
+ "shopping_service_test_base.h",
+ "shopping_service_unittest.cc",
+ ]
+
+ deps = [
+ ":feature_list",
+ ":metrics",
+ ":shopping_service",
+ "//base/test:test_support",
+ "//components/bookmarks/browser",
+ "//components/bookmarks/test",
+ "//components/commerce/core:proto",
+ "//components/optimization_guide/core",
+ "//components/optimization_guide/proto:optimization_guide_proto",
+ "//components/prefs:test_support",
+ "//testing/gtest",
+ "//url:url",
+ ]
+}
diff --git a/chromium/components/commerce/core/DEPS b/chromium/components/commerce/core/DEPS
index d9a56098de4..68fdbf00468 100644
--- a/chromium/components/commerce/core/DEPS
+++ b/chromium/components/commerce/core/DEPS
@@ -1,4 +1,6 @@
include_rules = [
+ "+components/bookmarks/browser",
+ "+components/bookmarks/test",
"+components/flags_ui",
"+components/keyed_service",
"+components/optimization_guide",
diff --git a/chromium/components/commerce/core/android/BUILD.gn b/chromium/components/commerce/core/android/BUILD.gn
index cb4da26d596..d571a89446e 100644
--- a/chromium/components/commerce/core/android/BUILD.gn
+++ b/chromium/components/commerce/core/android/BUILD.gn
@@ -10,7 +10,8 @@ android_library("core_java") {
[ "java/src/org/chromium/components/commerce/core/ShoppingService.java" ]
deps = [
- "//base:base_java",
"//base:jni_java",
+ "//build/android:build_java",
+ "//url:gurl_java",
]
}
diff --git a/chromium/components/commerce/core/android/shopping_service_android.cc b/chromium/components/commerce/core/android/shopping_service_android.cc
index 5ee0ec898a8..33b218f0b69 100644
--- a/chromium/components/commerce/core/android/shopping_service_android.cc
+++ b/chromium/components/commerce/core/android/shopping_service_android.cc
@@ -4,13 +4,19 @@
#include "components/commerce/core/android/shopping_service_android.h"
-#include "components/commerce/core/shopping_service.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
#include "components/commerce/core/shopping_service_jni_headers/ShoppingService_jni.h"
+#include "url/android/gurl_android.h"
+#include "url/gurl.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ScopedJavaLocalRef;
namespace commerce {
ShoppingServiceAndroid::ShoppingServiceAndroid(ShoppingService* service)
- : shopping_service_(service) {
+ : shopping_service_(service), weak_ptr_factory_(this) {
java_ref_.Reset(Java_ShoppingService_create(
base::android::AttachCurrentThread(), reinterpret_cast<jlong>(this)));
}
@@ -19,4 +25,39 @@ ShoppingServiceAndroid::~ShoppingServiceAndroid() {
Java_ShoppingService_destroy(base::android::AttachCurrentThread(), java_ref_);
}
+void ShoppingServiceAndroid::GetProductInfoForUrl(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ const JavaParamRef<jobject>& j_gurl,
+ const JavaParamRef<jobject>& j_callback) {
+ CHECK(shopping_service_);
+
+ GURL url = *url::GURLAndroid::ToNativeGURL(env, j_gurl);
+
+ shopping_service_->GetProductInfoForUrl(
+ url, base::BindOnce(&ShoppingServiceAndroid::HandleProductInfoCallback,
+ weak_ptr_factory_.GetWeakPtr(), env,
+ ScopedJavaGlobalRef<jobject>(j_callback)));
+}
+
+void ShoppingServiceAndroid::HandleProductInfoCallback(
+ JNIEnv* env,
+ const ScopedJavaGlobalRef<jobject>& callback,
+ const GURL& url,
+ const absl::optional<ProductInfo>& info) {
+ ScopedJavaLocalRef<jobject> info_java_object(nullptr);
+ if (info.has_value()) {
+ info_java_object = Java_ShoppingService_createProductInfo(
+ env, ConvertUTF8ToJavaString(env, info->title),
+ url::GURLAndroid::FromNativeGURL(env, GURL(info->image_url)),
+ info->product_cluster_id, info->offer_id,
+ ConvertUTF8ToJavaString(env, info->currency_code), info->amount_micros,
+ ConvertUTF8ToJavaString(env, info->country_code));
+ }
+
+ Java_ShoppingService_runProductInfoCallback(
+ env, callback, url::GURLAndroid::FromNativeGURL(env, url),
+ info_java_object);
+}
+
} // namespace commerce
diff --git a/chromium/components/commerce/core/android/shopping_service_android.h b/chromium/components/commerce/core/android/shopping_service_android.h
index 91b6e209c89..9fe4dce2fbb 100644
--- a/chromium/components/commerce/core/android/shopping_service_android.h
+++ b/chromium/components/commerce/core/android/shopping_service_android.h
@@ -8,10 +8,15 @@
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/supports_user_data.h"
+#include "components/commerce/core/shopping_service.h"
+using base::android::JavaParamRef;
using base::android::ScopedJavaGlobalRef;
+class GURL;
+
namespace commerce {
class ShoppingService;
@@ -24,9 +29,19 @@ class ShoppingServiceAndroid : public base::SupportsUserData::Data {
ShoppingServiceAndroid(ShoppingService* service);
~ShoppingServiceAndroid() override;
+ void GetProductInfoForUrl(JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ const JavaParamRef<jobject>& j_gurl,
+ const JavaParamRef<jobject>& j_callback);
+
ScopedJavaGlobalRef<jobject> java_ref() { return java_ref_; }
private:
+ void HandleProductInfoCallback(JNIEnv* env,
+ const ScopedJavaGlobalRef<jobject>& callback,
+ const GURL& url,
+ const absl::optional<ProductInfo>& info);
+
// A handle to the backing shopping service. This is held as a raw pointer
// since this object's lifecycle is tied to the service itself. This object
// will always be destroyed before the service is.
@@ -34,6 +49,8 @@ class ShoppingServiceAndroid : public base::SupportsUserData::Data {
// A handle to the java side of this object.
ScopedJavaGlobalRef<jobject> java_ref_;
+
+ base::WeakPtrFactory<ShoppingServiceAndroid> weak_ptr_factory_;
};
} // namespace commerce
diff --git a/chromium/components/commerce/core/commerce_feature_list.cc b/chromium/components/commerce/core/commerce_feature_list.cc
index 40aa6a816c3..9e3b0ebaf5e 100644
--- a/chromium/components/commerce/core/commerce_feature_list.cc
+++ b/chromium/components/commerce/core/commerce_feature_list.cc
@@ -64,6 +64,15 @@ const re2::RE2& GetCouponPartnerMerchantPattern() {
} // namespace
+const base::Feature kCommerceAllowLocalImages{
+ "CommerceAllowLocalImages", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kCommerceAllowServerImages{
+ "CommerceAllowServerImages", base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kCommerceCoupons{"CommerceCoupons",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
const base::Feature kCommerceMerchantViewer{"CommerceMerchantViewer",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -91,6 +100,9 @@ const char kRetailCouponsWithCodeParam[] = "RetailCouponsWithCodeParam";
const base::Feature kDiscountConsentV2{"DiscountConsentV2",
base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kCommerceHintAndroid{"CommerceHintAndroid",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Params for Discount Consent V2 in the NTP Cart module.
const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[] =
"discount-consent-ntp-variation";
@@ -192,6 +204,9 @@ const char kContextualConsentShowOnSRPParam[] = "show-on-srp";
const base::FeatureParam<bool> kContextualConsentShowOnSRP{
&commerce::kDiscountConsentV2, kContextualConsentShowOnSRPParam, false};
+const char kCommerceHintAndroidHeuristicsImprovementParam[] =
+ "CommerceHintAndroidHeuristicsImprovementParam";
+
bool IsPartnerMerchant(const GURL& url) {
return commerce::IsCouponDiscountPartnerMerchant(url) ||
IsRuleDiscountPartnerMerchant(url);
diff --git a/chromium/components/commerce/core/commerce_feature_list.h b/chromium/components/commerce/core/commerce_feature_list.h
index 525676530b6..6032e5d2521 100644
--- a/chromium/components/commerce/core/commerce_feature_list.h
+++ b/chromium/components/commerce/core/commerce_feature_list.h
@@ -65,6 +65,9 @@ constexpr flags_ui::FeatureEntry::FeatureVariation
std::size(kCommercePriceTrackingWithOptimizationGuideAndOptOut),
nullptr}};
+extern const base::Feature kCommerceAllowLocalImages;
+extern const base::Feature kCommerceAllowServerImages;
+extern const base::Feature kCommerceCoupons;
extern const base::Feature kCommerceMerchantViewer;
extern const base::FeatureParam<bool> kDeleteAllMerchantsOnClearBrowsingHistory;
extern const base::Feature kShoppingList;
@@ -77,11 +80,18 @@ extern const char kRetailCouponsWithCodeParam[];
// Feature flag for Discount user consent v2.
extern const base::Feature kDiscountConsentV2;
+// Feature flag for exposing commerce hint on Android.
+extern const base::Feature kCommerceHintAndroid;
+
// Feature parameters for ChromeCart on Desktop.
// Whether to use OptimizationGuide to optimize renderer signal collection.
constexpr base::FeatureParam<bool> kOptimizeRendererSignal(
+#if !BUILDFLAG(IS_ANDROID)
&ntp_features::kNtpChromeCartModule,
+#else
+ &kCommerceHintAndroid,
+#endif
"optimize-renderer-signal",
true);
@@ -97,8 +107,13 @@ constexpr base::FeatureParam<base::TimeDelta> kCouponDisplayInterval{
// The heuristics of cart pages are from top 100 US shopping domains.
// https://colab.corp.google.com/drive/1fTGE_SQw_8OG4ubzQvWcBuyHEhlQ-pwQ?usp=sharing
constexpr base::FeatureParam<std::string> kCartPattern{
- &ntp_features::kNtpChromeCartModule, "cart-pattern",
- // clang-format off
+#if !BUILDFLAG(IS_ANDROID)
+ &ntp_features::kNtpChromeCartModule,
+#else
+ &kCommerceHintAndroid,
+#endif
+ "cart-pattern",
+ // clang-format off
"(^https?://cart\\.)"
"|"
"(/("
@@ -114,17 +129,28 @@ constexpr base::FeatureParam<std::string> kCartPattern{
"|"
"(cart-show)"
")(/|\\.|$))"
- // clang-format on
+ // clang-format on
};
constexpr base::FeatureParam<std::string> kCartPatternMapping{
- &ntp_features::kNtpChromeCartModule, "cart-pattern-mapping",
- // Empty JSON string.
- ""};
+#if !BUILDFLAG(IS_ANDROID)
+ &ntp_features::kNtpChromeCartModule,
+#else
+ &kCommerceHintAndroid,
+#endif
+ "cart-pattern-mapping",
+ // Empty JSON string.
+ ""
+};
constexpr base::FeatureParam<std::string> kCheckoutPattern{
- &ntp_features::kNtpChromeCartModule, "checkout-pattern",
- // clang-format off
+#if !BUILDFLAG(IS_ANDROID)
+ &ntp_features::kNtpChromeCartModule,
+#else
+ &kCommerceHintAndroid,
+#endif
+ "checkout-pattern",
+ // clang-format off
"/("
"("
"("
@@ -137,13 +163,19 @@ constexpr base::FeatureParam<std::string> kCheckoutPattern{
"|"
"(\\w+(checkout|chkout)(s)?)"
")(/|\\.|$|\\?)"
- // clang-format on
+ // clang-format on
};
constexpr base::FeatureParam<std::string> kCheckoutPatternMapping{
- &ntp_features::kNtpChromeCartModule, "checkout-pattern-mapping",
- // Empty JSON string.
- ""};
+#if !BUILDFLAG(IS_ANDROID)
+ &ntp_features::kNtpChromeCartModule,
+#else
+ &kCommerceHintAndroid,
+#endif
+ "checkout-pattern-mapping",
+ // Empty JSON string.
+ ""
+};
// The following are Feature params for Discount user consent v2.
// This indicates the Discount Consent v2 variation on the NTP Cart module.
@@ -242,6 +274,9 @@ extern const base::FeatureParam<bool>
extern const char kContextualConsentShowOnSRPParam[];
extern const base::FeatureParam<bool> kContextualConsentShowOnSRP;
+// Feature params for enabling the cart heuristics improvement on Android.
+extern const char kCommerceHintAndroidHeuristicsImprovementParam[];
+
// Check if a URL belongs to a partner merchant of any type of discount.
bool IsPartnerMerchant(const GURL& url);
// Check if a URL belongs to a partner merchant of rule discount.
diff --git a/chromium/components/commerce/core/commerce_heuristics_data_metrics_helper.cc b/chromium/components/commerce/core/commerce_heuristics_data_metrics_helper.cc
new file mode 100644
index 00000000000..6ecf844e578
--- /dev/null
+++ b/chromium/components/commerce/core/commerce_heuristics_data_metrics_helper.cc
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium 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/commerce/core/commerce_heuristics_data_metrics_helper.h"
+
+#include "base/metrics/histogram_functions.h"
+
+void CommerceHeuristicsDataMetricsHelper::RecordMerchantNameSource(
+ HeuristicsSource source) {
+ base::UmaHistogramEnumeration("Commerce.Heuristics.MerchantNameSource",
+ source);
+}
+
+void CommerceHeuristicsDataMetricsHelper::RecordCheckoutURLGeneralPatternSource(
+ HeuristicsSource source) {
+ base::UmaHistogramEnumeration(
+ "Commerce.Heuristics.CheckoutURLGeneralPatternSource", source);
+}
+
+void CommerceHeuristicsDataMetricsHelper::RecordCartExtractionScriptSource(
+ HeuristicsSource source) {
+ base::UmaHistogramEnumeration(
+ "Commerce.Heuristics.CartExtractionScriptSource", source);
+}
diff --git a/chromium/components/commerce/core/commerce_heuristics_data_metrics_helper.h b/chromium/components/commerce/core/commerce_heuristics_data_metrics_helper.h
new file mode 100644
index 00000000000..1fcf05ce678
--- /dev/null
+++ b/chromium/components/commerce/core/commerce_heuristics_data_metrics_helper.h
@@ -0,0 +1,38 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_COMMERCE_CORE_COMMERCE_HEURISTICS_DATA_METRICS_HELPER_H_
+#define COMPONENTS_COMMERCE_CORE_COMMERCE_HEURISTICS_DATA_METRICS_HELPER_H_
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+class CommerceHeuristicsDataMetricsHelper {
+ public:
+ // Represent the source of commerce heuristics.
+ enum class HeuristicsSource {
+ // Heuristics are from component updater.
+ FROM_COMPONENT = 0,
+ // Heuristics are from resources.
+ FROM_RESOURCE = 1,
+ // Heuristics are from feature parameter.
+ FROM_FEATURE_PARAMETER = 2,
+ // Heuristics are missing.
+ MISSING = 3,
+ kMaxValue = MISSING,
+ };
+
+ // Gets called when we try to get merchant name for a domain to record the
+ // source of this name data.
+ static void RecordMerchantNameSource(HeuristicsSource source);
+
+ // Gets called when we try to get general checkout URL detection pattern to
+ // record the source of this pattern data.
+ static void RecordCheckoutURLGeneralPatternSource(HeuristicsSource source);
+
+ // Gets called when we try to get the cart extraction script to record the
+ // source of this script data.
+ static void RecordCartExtractionScriptSource(HeuristicsSource source);
+};
+
+#endif // COMPONENTS_COMMERCE_CORE_COMMERCE_HEURISTICS_DATA_METRICS_HELPER_H_
diff --git a/chromium/components/commerce/core/heuristics/commerce_heuristics_provider.cc b/chromium/components/commerce/core/heuristics/commerce_heuristics_provider.cc
index 2376820e18b..d25449d1280 100644
--- a/chromium/components/commerce/core/heuristics/commerce_heuristics_provider.cc
+++ b/chromium/components/commerce/core/heuristics/commerce_heuristics_provider.cc
@@ -12,6 +12,7 @@
#include "build/buildflag.h"
#include "components/commerce/core/commerce_feature_list.h"
#include "components/commerce/core/commerce_heuristics_data.h"
+#include "components/commerce/core/commerce_heuristics_data_metrics_helper.h"
#include "components/grit/components_resources.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "third_party/re2/src/re2/re2.h"
@@ -32,8 +33,9 @@ const std::map<std::string, std::string>& GetCartPatternMapping() {
const base::Value json(
base::JSONReader::Read(
commerce::kCartPatternMapping.Get().empty()
- ? ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_CART_DOMAIN_CART_URL_REGEX_JSON)
+ ? ui::ResourceBundle::GetSharedInstance()
+ .LoadDataResourceString(
+ IDR_CART_DOMAIN_CART_URL_REGEX_JSON)
: commerce::kCartPatternMapping.Get())
.value());
DCHECK(json.is_dict());
@@ -51,8 +53,9 @@ const std::map<std::string, std::string>& GetCheckoutPatternMapping() {
const base::Value json(
base::JSONReader::Read(
commerce::kCheckoutPatternMapping.Get().empty()
- ? ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_CHECKOUT_URL_REGEX_DOMAIN_MAPPING_JSON)
+ ? ui::ResourceBundle::GetSharedInstance()
+ .LoadDataResourceString(
+ IDR_CHECKOUT_URL_REGEX_DOMAIN_MAPPING_JSON)
: commerce::kCheckoutPatternMapping.Get())
.value());
DCHECK(json.is_dict());
@@ -124,10 +127,17 @@ const re2::RE2* GetVisitCheckoutPattern(const GURL& url) {
if (global_pattern_from_component &&
commerce::kCheckoutPattern.Get() ==
commerce::kCheckoutPattern.default_value) {
+ CommerceHeuristicsDataMetricsHelper::
+ RecordCheckoutURLGeneralPatternSource(
+ CommerceHeuristicsDataMetricsHelper::HeuristicsSource::
+ FROM_COMPONENT);
return global_pattern_from_component;
}
static base::NoDestructor<re2::RE2> instance(
commerce::kCheckoutPattern.Get(), options);
+ CommerceHeuristicsDataMetricsHelper::RecordCheckoutURLGeneralPatternSource(
+ CommerceHeuristicsDataMetricsHelper::HeuristicsSource::
+ FROM_FEATURE_PARAMETER);
return instance.get();
}
if (checkout_regex_map->find(domain) == checkout_regex_map->end()) {
diff --git a/chromium/components/commerce/core/metrics/metrics_utils.cc b/chromium/components/commerce/core/metrics/metrics_utils.cc
index a2ccf06b83a..052eb6347b4 100644
--- a/chromium/components/commerce/core/metrics/metrics_utils.cc
+++ b/chromium/components/commerce/core/metrics/metrics_utils.cc
@@ -11,8 +11,10 @@
namespace commerce::metrics {
+const char kPDPStateHistogramName[] = "Commerce.PDPStateOnNavigation";
+
void RecordPDPStateToUma(ShoppingPDPState state) {
- base::UmaHistogramEnumeration("Commerce.PDPStateOnNavigation", state);
+ base::UmaHistogramEnumeration(kPDPStateHistogramName, state);
}
ShoppingPDPState ComputeStateForOptGuideResult(
diff --git a/chromium/components/commerce/core/metrics/metrics_utils.h b/chromium/components/commerce/core/metrics/metrics_utils.h
index 15e0817d0b3..bbdaa620e30 100644
--- a/chromium/components/commerce/core/metrics/metrics_utils.h
+++ b/chromium/components/commerce/core/metrics/metrics_utils.h
@@ -12,6 +12,8 @@
namespace commerce::metrics {
+extern const char kPDPStateHistogramName[];
+
// Possible options for the stat of a product details page (PDP). These must be
// kept in sync with the values in enums.xml.
enum class ShoppingPDPState {
diff --git a/chromium/components/commerce/core/pdp_metrics_unittest.cc b/chromium/components/commerce/core/pdp_metrics_unittest.cc
new file mode 100644
index 00000000000..a14bb80994f
--- /dev/null
+++ b/chromium/components/commerce/core/pdp_metrics_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2022 The Chromium 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/bind.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/commerce/core/commerce_feature_list.h"
+#include "components/commerce/core/metrics/metrics_utils.h"
+#include "components/commerce/core/shopping_service.h"
+#include "components/commerce/core/shopping_service_test_base.h"
+#include "components/optimization_guide/core/new_optimization_guide_decider.h"
+#include "components/optimization_guide/core/optimization_guide_decision.h"
+#include "components/optimization_guide/core/optimization_guide_switches.h"
+#include "components/optimization_guide/core/optimization_metadata.h"
+#include "components/optimization_guide/proto/hints.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using optimization_guide::OptimizationGuideDecision;
+using optimization_guide::OptimizationGuideDecisionCallback;
+using optimization_guide::OptimizationMetadata;
+using optimization_guide::proto::Any;
+using optimization_guide::proto::OptimizationType;
+
+namespace commerce {
+
+class PDPMetricsTest : public ShoppingServiceTestBase {
+ public:
+ PDPMetricsTest() {
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ optimization_guide::switches::
+ kDisableCheckingUserPermissionsForTesting);
+ test_features_.InitAndEnableFeature(commerce::kShoppingPDPMetrics);
+ }
+ PDPMetricsTest(const PDPMetricsTest&) = delete;
+ PDPMetricsTest operator=(const PDPMetricsTest&) = delete;
+ ~PDPMetricsTest() override = default;
+
+ base::test::ScopedFeatureList test_features_;
+};
+
+// Test that PDP metrics for the page are recorded.
+TEST_F(PDPMetricsTest, TestPDPIsRecorded) {
+ std::string url = "http://example.com";
+
+ OptimizationMetadata meta =
+ opt_guide_->BuildPriceTrackingResponse("title", url, 123, 456, "US");
+
+ base::HistogramTester histogram_tester;
+
+ opt_guide_->SetResponse(GURL(url), OptimizationType::PRICE_TRACKING,
+ OptimizationGuideDecision::kTrue, meta);
+
+ MockWebWrapper web(GURL(url), false);
+
+ DidNavigatePrimaryMainFrame(&web);
+
+ histogram_tester.ExpectBucketCount(
+ metrics::kPDPStateHistogramName,
+ metrics::ShoppingPDPState::kIsPDPWithClusterId, 1);
+ histogram_tester.ExpectBucketCount(metrics::kPDPStateHistogramName,
+ metrics::ShoppingPDPState::kNotPDP, 0);
+}
+
+// Test that PDP metrics for an incognito page are not recorded.
+TEST_F(PDPMetricsTest, TestIncognitoPDPIsNotRecorded) {
+ std::string url = "http://example.com";
+
+ OptimizationMetadata meta =
+ opt_guide_->BuildPriceTrackingResponse("title", url, 123, 456, "US");
+
+ base::HistogramTester histogram_tester;
+
+ opt_guide_->SetResponse(GURL(url), OptimizationType::PRICE_TRACKING,
+ OptimizationGuideDecision::kTrue, meta);
+
+ MockWebWrapper web(GURL(url), true);
+
+ DidNavigatePrimaryMainFrame(&web);
+
+ histogram_tester.ExpectBucketCount(
+ metrics::kPDPStateHistogramName,
+ metrics::ShoppingPDPState::kIsPDPWithClusterId, 0);
+ histogram_tester.ExpectBucketCount(metrics::kPDPStateHistogramName,
+ metrics::ShoppingPDPState::kNotPDP, 0);
+}
+
+// Test that a page that isn't considered a PDP is recorded.
+TEST_F(PDPMetricsTest, TestFalseOptGuideResponseIsRecorded) {
+ std::string url = "http://example.com";
+
+ OptimizationMetadata meta =
+ opt_guide_->BuildPriceTrackingResponse("title", url, 123, 456, "US");
+
+ base::HistogramTester histogram_tester;
+
+ opt_guide_->SetResponse(GURL(url), OptimizationType::PRICE_TRACKING,
+ OptimizationGuideDecision::kFalse, meta);
+
+ MockWebWrapper web(GURL(url), false);
+
+ DidNavigatePrimaryMainFrame(&web);
+
+ histogram_tester.ExpectBucketCount(
+ metrics::kPDPStateHistogramName,
+ metrics::ShoppingPDPState::kIsPDPWithClusterId, 0);
+ histogram_tester.ExpectBucketCount(metrics::kPDPStateHistogramName,
+ metrics::ShoppingPDPState::kNotPDP, 1);
+}
+
+} // namespace commerce
diff --git a/chromium/components/commerce/core/proto/merchant_trust.proto b/chromium/components/commerce/core/proto/merchant_trust.proto
new file mode 100644
index 00000000000..e14e298c392
--- /dev/null
+++ b/chromium/components/commerce/core/proto/merchant_trust.proto
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+package commerce;
+
+option java_package = "org.chromium.components.commerce";
+
+option optimize_for = LITE_RUNTIME;
+
+message MerchantTrustSignalsV2 {
+ // Overall rating out of 5.
+ optional float merchant_star_rating = 1;
+
+ // Total number of reviews.
+ optional int32 merchant_count_rating = 2;
+
+ // Link to the merchant details page.
+ optional string merchant_details_page_url = 3;
+
+ // Whether we have return policy for the merchant.
+ optional bool has_return_policy = 4;
+
+ // Non-personalized familiarity score of the merchant, ranged from 0 to 1.
+ optional float non_personalized_familiarity_score = 5;
+
+ // Whether the merchant contains sensitive content.
+ optional bool contains_sensitive_content = 6;
+
+ // Whether proactively showing message is disabled for the merchant.
+ optional bool proactive_message_disabled = 7;
+}
diff --git a/chromium/components/commerce/core/resources/query_shopping_meta.js b/chromium/components/commerce/core/resources/query_shopping_meta.js
new file mode 100644
index 00000000000..f0682364f42
--- /dev/null
+++ b/chromium/components/commerce/core/resources/query_shopping_meta.js
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+const metaTags = document.getElementsByTagName('meta');
+const output = {};
+for (let i = 0; i < metaTags.length; i++) {
+ const curMeta = metaTags[i];
+ const name = curMeta.getAttribute('property');
+ const value = curMeta.getAttribute('content');
+ if (!name || !value) {
+ continue;
+ }
+ // "og" in this context refers to "open graph" rather than "optimization
+ // guide".
+ if (name.startsWith('og:')) {
+ output[name.substring(3)] = value;
+ }
+}
+return JSON.stringify(output);
+})();
diff --git a/chromium/components/commerce/core/shopping_bookmark_model_observer.cc b/chromium/components/commerce/core/shopping_bookmark_model_observer.cc
new file mode 100644
index 00000000000..fd51460a4c8
--- /dev/null
+++ b/chromium/components/commerce/core/shopping_bookmark_model_observer.cc
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium 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/commerce/core/shopping_bookmark_model_observer.h"
+
+#include "components/bookmarks/browser/bookmark_node.h"
+
+namespace {
+// TODO(1314355): This constant should be removed in favor of power bookmark
+// support in components.
+const char kPowerBookmarkMetaKey[] = "power_bookmark_meta";
+} // namespace
+
+namespace commerce {
+
+ShoppingBookmarkModelObserver::ShoppingBookmarkModelObserver(
+ bookmarks::BookmarkModel* model) {
+ scoped_observation_.Observe(model);
+}
+
+ShoppingBookmarkModelObserver::~ShoppingBookmarkModelObserver() = default;
+
+void ShoppingBookmarkModelObserver::BookmarkModelChanged() {}
+
+void ShoppingBookmarkModelObserver::OnWillChangeBookmarkNode(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) {
+ // Since the node is about to change, map its current known URL.
+ node_to_url_map_[node->id()] = node->url();
+}
+
+void ShoppingBookmarkModelObserver::BookmarkNodeChanged(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) {
+ if (node_to_url_map_[node->id()] != node->url()) {
+ // If the URL did change, clear the power bookmark shopping meta.
+ // TODO(1314355): This blindly deletes the power bookmark meta without
+ // checking the type. This works while shopping is the
+ // only power bookmark type but will need to be updated.
+ model->DeleteNodeMetaInfo(node, kPowerBookmarkMetaKey);
+ }
+
+ node_to_url_map_.erase(node->id());
+}
+
+} // namespace commerce
diff --git a/chromium/components/commerce/core/shopping_bookmark_model_observer.h b/chromium/components/commerce/core/shopping_bookmark_model_observer.h
new file mode 100644
index 00000000000..412d6c51e78
--- /dev/null
+++ b/chromium/components/commerce/core/shopping_bookmark_model_observer.h
@@ -0,0 +1,58 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_COMMERCE_CORE_SHOPPING_BOOKMARK_MODEL_OBSERVER_H_
+#define COMPONENTS_COMMERCE_CORE_SHOPPING_BOOKMARK_MODEL_OBSERVER_H_
+
+#include <map>
+
+#include "base/scoped_observation.h"
+#include "components/bookmarks/browser/base_bookmark_model_observer.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+
+namespace bookmarks {
+class BookmarkNode;
+} // namespace bookmarks
+
+namespace commerce {
+
+// A utility class that watches for changes in bookmark URLs. In the case that
+// the bookmark was a shopping item, the meta should be removed since we can't
+// guarantee the URL still points to the product.
+//
+// TODO(1317783): We can probably update the data rather than delete it once
+// privacy-preserving fetch from optimization guide becomes
+// available.
+class ShoppingBookmarkModelObserver
+ : public bookmarks::BaseBookmarkModelObserver {
+ public:
+ explicit ShoppingBookmarkModelObserver(bookmarks::BookmarkModel* model);
+ ShoppingBookmarkModelObserver(const ShoppingBookmarkModelObserver&) = delete;
+ ShoppingBookmarkModelObserver& operator=(
+ const ShoppingBookmarkModelObserver&) = delete;
+
+ ~ShoppingBookmarkModelObserver() override;
+
+ void BookmarkModelChanged() override;
+
+ void OnWillChangeBookmarkNode(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override;
+
+ void BookmarkNodeChanged(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override;
+
+ private:
+ // A map of bookmark ID to its current URL. This is used to detect incoming
+ // changes to the URL since there isn't an explicit event for it.
+ std::map<int64_t, GURL> node_to_url_map_;
+
+ // Automatically remove this observer from its host when destroyed.
+ base::ScopedObservation<bookmarks::BookmarkModel,
+ bookmarks::BookmarkModelObserver>
+ scoped_observation_{this};
+};
+
+} // namespace commerce
+
+#endif // COMPONENTS_COMMERCE_CORE_SHOPPING_BOOKMARK_MODEL_OBSERVER_H_
diff --git a/chromium/components/commerce/core/shopping_service.cc b/chromium/components/commerce/core/shopping_service.cc
index 2c308cf5a57..7ed1db674bf 100644
--- a/chromium/components/commerce/core/shopping_service.cc
+++ b/chromium/components/commerce/core/shopping_service.cc
@@ -6,10 +6,59 @@
#include "components/commerce/core/pref_names.h"
#include "components/prefs/pref_registry_simple.h"
+#include <vector>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/commerce/core/commerce_feature_list.h"
+#include "components/commerce/core/metrics/metrics_utils.h"
+#include "components/commerce/core/proto/merchant_trust.pb.h"
+#include "components/commerce/core/proto/price_tracking.pb.h"
+#include "components/commerce/core/shopping_bookmark_model_observer.h"
+#include "components/commerce/core/web_wrapper.h"
+#include "components/optimization_guide/core/new_optimization_guide_decider.h"
+#include "components/optimization_guide/core/optimization_guide_util.h"
+#include "components/optimization_guide/proto/hints.pb.h"
+
namespace commerce {
-void ShoppingService::Shutdown() {
- // intentional noop
+ProductInfo::ProductInfo() = default;
+ProductInfo::ProductInfo(const ProductInfo&) = default;
+ProductInfo& ProductInfo::operator=(const ProductInfo&) = default;
+ProductInfo::~ProductInfo() = default;
+MerchantInfo::MerchantInfo() = default;
+MerchantInfo::MerchantInfo(MerchantInfo&&) = default;
+MerchantInfo::~MerchantInfo() = default;
+
+ShoppingService::ShoppingService(
+ bookmarks::BookmarkModel* bookmark_model,
+ optimization_guide::NewOptimizationGuideDecider* opt_guide,
+ PrefService* pref_service)
+ : opt_guide_(opt_guide),
+ pref_service_(pref_service),
+ weak_ptr_factory_(this) {
+ // Register for the types of information we're allowed to receive from
+ // optimization guide.
+ if (opt_guide_) {
+ std::vector<optimization_guide::proto::OptimizationType> types;
+
+ // Don't register for info unless we're allowed to by an experiment.
+ if (IsProductInfoApiEnabled() || IsPDPMetricsRecordingEnabled()) {
+ types.push_back(
+ optimization_guide::proto::OptimizationType::PRICE_TRACKING);
+ }
+ if (IsMerchantInfoApiEnabled()) {
+ types.push_back(optimization_guide::proto::MERCHANT_TRUST_SIGNALS_V2);
+ }
+
+ opt_guide_->RegisterOptimizationTypes(types);
+ }
+
+ if (bookmark_model) {
+ shopping_bookmark_observer_ =
+ std::make_unique<ShoppingBookmarkModelObserver>(bookmark_model);
+ }
}
void ShoppingService::RegisterPrefs(PrefRegistrySimple* registry) {
@@ -19,4 +68,280 @@ void ShoppingService::RegisterPrefs(PrefRegistrySimple* registry) {
true);
}
+void ShoppingService::WebWrapperCreated(WebWrapper* web) {}
+
+void ShoppingService::DidNavigatePrimaryMainFrame(WebWrapper* web) {
+ if (opt_guide_ &&
+ (IsProductInfoApiEnabled() || IsPDPMetricsRecordingEnabled())) {
+ UpdateProductInfoCacheForInsertion(web->GetLastCommittedURL());
+
+ opt_guide_->CanApplyOptimization(
+ web->GetLastCommittedURL(),
+ optimization_guide::proto::OptimizationType::PRICE_TRACKING,
+ base::BindOnce(
+ [](base::WeakPtr<ShoppingService> service, const GURL& url,
+ bool is_off_the_record,
+ optimization_guide::OptimizationGuideDecision decision,
+ const optimization_guide::OptimizationMetadata& metadata) {
+ service->HandleOptGuideProductInfoResponse(
+ url,
+ base::BindOnce(
+ [](const GURL&, const absl::optional<ProductInfo>&) {}),
+ decision, metadata);
+
+ service->PDPMetricsCallback(is_off_the_record, decision,
+ metadata);
+ },
+ weak_ptr_factory_.GetWeakPtr(), web->GetLastCommittedURL(),
+ web->IsOffTheRecord()));
+ }
+}
+
+void ShoppingService::DidNavigateAway(WebWrapper* web, const GURL& from_url) {
+ UpdateProductInfoCacheForRemoval(from_url);
+}
+
+void ShoppingService::DidFinishLoad(WebWrapper* web) {}
+
+void ShoppingService::WebWrapperDestroyed(WebWrapper* web) {
+ UpdateProductInfoCacheForRemoval(web->GetLastCommittedURL());
+}
+
+void ShoppingService::UpdateProductInfoCacheForInsertion(const GURL& url) {
+ if (!IsProductInfoApiEnabled())
+ return;
+
+ auto it = product_info_cache_.find(url.spec());
+ if (it != product_info_cache_.end()) {
+ std::get<0>(it->second) = std::get<0>(it->second) + 1;
+ } else {
+ product_info_cache_[url.spec()] = std::make_tuple(1, false, nullptr);
+ std::get<2>(product_info_cache_[url.spec()]).reset();
+ }
+}
+
+void ShoppingService::UpdateProductInfoCache(
+ const GURL& url,
+ bool needs_js,
+ std::unique_ptr<ProductInfo> info) {
+ auto it = product_info_cache_.find(url.spec());
+ if (it == product_info_cache_.end())
+ return;
+
+ int count = std::get<0>(it->second);
+ product_info_cache_[url.spec()] =
+ std::make_tuple(count, needs_js, std::move(info));
+}
+
+const ProductInfo* ShoppingService::GetFromProductInfoCache(const GURL& url) {
+ auto it = product_info_cache_.find(url.spec());
+ if (it == product_info_cache_.end())
+ return nullptr;
+
+ return std::get<2>(it->second).get();
+}
+
+void ShoppingService::UpdateProductInfoCacheForRemoval(const GURL& url) {
+ if (!IsProductInfoApiEnabled())
+ return;
+
+ // Check if the previously navigated URL cache needs to be cleared. If more
+ // than one tab was open with the same URL, keep it in the cache but decrement
+ // the internal count.
+ auto it = product_info_cache_.find(url.spec());
+ if (it != product_info_cache_.end()) {
+ if (std::get<0>(it->second) <= 1) {
+ product_info_cache_.erase(it);
+ } else {
+ std::get<0>(it->second) = std::get<0>(it->second) - 1;
+ }
+ }
+}
+
+void ShoppingService::PDPMetricsCallback(
+ bool is_off_the_record,
+ optimization_guide::OptimizationGuideDecision decision,
+ const optimization_guide::OptimizationMetadata& metadata) {
+ if (!IsPDPMetricsRecordingEnabled())
+ return;
+
+ metrics::RecordPDPStateForNavigation(decision, metadata, pref_service_,
+ is_off_the_record);
+}
+
+void ShoppingService::GetProductInfoForUrl(const GURL& url,
+ ProductInfoCallback callback) {
+ if (!opt_guide_)
+ return;
+
+ // Crash if this API is used without a valid experiment.
+ CHECK(IsProductInfoApiEnabled());
+
+ const ProductInfo* cachedInfo = GetFromProductInfoCache(url);
+ if (cachedInfo) {
+ absl::optional<ProductInfo> info;
+ // Make a copy based on the cached value.
+ info.emplace(*cachedInfo);
+ std::move(callback).Run(url, info);
+ return;
+ }
+
+ opt_guide_->CanApplyOptimization(
+ url, optimization_guide::proto::OptimizationType::PRICE_TRACKING,
+ base::BindOnce(&ShoppingService::HandleOptGuideProductInfoResponse,
+ weak_ptr_factory_.GetWeakPtr(), url, std::move(callback)));
+}
+
+void ShoppingService::GetMerchantInfoForUrl(const GURL& url,
+ MerchantInfoCallback callback) {
+ if (!opt_guide_)
+ return;
+
+ // Crash if this API is used without a valid experiment.
+ CHECK(IsMerchantInfoApiEnabled());
+
+ opt_guide_->CanApplyOptimization(
+ url,
+ optimization_guide::proto::OptimizationType::MERCHANT_TRUST_SIGNALS_V2,
+ base::BindOnce(&ShoppingService::HandleOptGuideMerchantInfoResponse,
+ weak_ptr_factory_.GetWeakPtr(), url, std::move(callback)));
+}
+
+bool ShoppingService::IsProductInfoApiEnabled() {
+ return base::FeatureList::IsEnabled(kShoppingList);
+}
+
+bool ShoppingService::IsPDPMetricsRecordingEnabled() {
+ return base::FeatureList::IsEnabled(commerce::kShoppingPDPMetrics);
+}
+
+bool ShoppingService::IsMerchantInfoApiEnabled() {
+ return base::FeatureList::IsEnabled(kCommerceMerchantViewer);
+}
+
+void ShoppingService::HandleOptGuideProductInfoResponse(
+ const GURL& url,
+ ProductInfoCallback callback,
+ optimization_guide::OptimizationGuideDecision decision,
+ const optimization_guide::OptimizationMetadata& metadata) {
+ if (!IsProductInfoApiEnabled())
+ return;
+
+ // If optimization guide returns negative, return a negative signal with an
+ // empty data object.
+ if (decision != optimization_guide::OptimizationGuideDecision::kTrue) {
+ std::move(callback).Run(url, absl::nullopt);
+ return;
+ }
+
+ std::unique_ptr<ProductInfo> info = nullptr;
+
+ if (metadata.any_metadata().has_value()) {
+ absl::optional<commerce::PriceTrackingData> parsed_any =
+ optimization_guide::ParsedAnyMetadata<commerce::PriceTrackingData>(
+ metadata.any_metadata().value());
+ commerce::PriceTrackingData price_data = parsed_any.value();
+ if (parsed_any.has_value() && price_data.IsInitialized()) {
+ commerce::BuyableProduct buyable_product = price_data.buyable_product();
+
+ info = std::make_unique<ProductInfo>();
+
+ if (buyable_product.has_title())
+ info->title = buyable_product.title();
+
+ if (buyable_product.has_image_url() &&
+ base::FeatureList::IsEnabled(kCommerceAllowServerImages)) {
+ info->image_url = GURL(buyable_product.image_url());
+ }
+
+ if (buyable_product.has_offer_id())
+ info->offer_id = buyable_product.offer_id();
+
+ if (buyable_product.has_product_cluster_id())
+ info->product_cluster_id = buyable_product.product_cluster_id();
+
+ if (buyable_product.has_current_price()) {
+ info->currency_code = buyable_product.current_price().currency_code();
+ info->amount_micros = buyable_product.current_price().amount_micros();
+ }
+
+ if (buyable_product.has_country_code())
+ info->country_code = buyable_product.country_code();
+ }
+ }
+
+ absl::optional<ProductInfo> optional_info;
+ if (info) {
+ optional_info.emplace(*info);
+ UpdateProductInfoCache(url, true, std::move(info));
+ }
+
+ // TODO(mdjones): Longer-term it probably makes sense to wait until the
+ // javascript has run to execute this.
+ std::move(callback).Run(url, optional_info);
+}
+
+void ShoppingService::HandleOptGuideMerchantInfoResponse(
+ const GURL& url,
+ MerchantInfoCallback callback,
+ optimization_guide::OptimizationGuideDecision decision,
+ const optimization_guide::OptimizationMetadata& metadata) {
+ // If optimization guide returns negative, return a negative signal with an
+ // empty data object.
+ if (decision != optimization_guide::OptimizationGuideDecision::kTrue) {
+ std::move(callback).Run(url, absl::nullopt);
+ return;
+ }
+
+ absl::optional<MerchantInfo> info;
+
+ if (metadata.any_metadata().has_value()) {
+ absl::optional<commerce::MerchantTrustSignalsV2> parsed_any =
+ optimization_guide::ParsedAnyMetadata<commerce::MerchantTrustSignalsV2>(
+ metadata.any_metadata().value());
+ commerce::MerchantTrustSignalsV2 merchant_data = parsed_any.value();
+ if (parsed_any.has_value() && merchant_data.IsInitialized()) {
+ info.emplace();
+
+ if (merchant_data.has_merchant_star_rating()) {
+ info->star_rating = merchant_data.merchant_star_rating();
+ }
+
+ if (merchant_data.has_merchant_count_rating()) {
+ info->count_rating = merchant_data.merchant_count_rating();
+ }
+
+ if (merchant_data.has_merchant_details_page_url()) {
+ info->details_page_url =
+ GURL(merchant_data.merchant_details_page_url());
+ }
+
+ if (merchant_data.has_has_return_policy()) {
+ info->has_return_policy = merchant_data.has_return_policy();
+ }
+
+ if (merchant_data.has_non_personalized_familiarity_score()) {
+ info->non_personalized_familiarity_score =
+ merchant_data.non_personalized_familiarity_score();
+ }
+
+ if (merchant_data.has_contains_sensitive_content()) {
+ info->contains_sensitive_content =
+ merchant_data.contains_sensitive_content();
+ }
+
+ if (merchant_data.has_proactive_message_disabled()) {
+ info->proactive_message_disabled =
+ merchant_data.proactive_message_disabled();
+ }
+ }
+ }
+
+ std::move(callback).Run(url, std::move(info));
+}
+
+void ShoppingService::Shutdown() {}
+
+ShoppingService::~ShoppingService() = default;
+
} // namespace commerce
diff --git a/chromium/components/commerce/core/shopping_service.h b/chromium/components/commerce/core/shopping_service.h
index 172b4c2c2c0..6e911bbcd12 100644
--- a/chromium/components/commerce/core/shopping_service.h
+++ b/chromium/components/commerce/core/shopping_service.h
@@ -5,24 +5,192 @@
#ifndef COMPONENTS_COMMERCE_CORE_SHOPPING_SERVICE_H_
#define COMPONENTS_COMMERCE_CORE_SHOPPING_SERVICE_H_
+#include <map>
+#include <memory>
+#include <string>
+#include <tuple>
+
+#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
#include "base/supports_user_data.h"
#include "components/keyed_service/core/keyed_service.h"
+#include "components/optimization_guide/core/optimization_guide_decision.h"
+
+class GURL;
+class PrefService;
class PrefRegistrySimple;
+namespace bookmarks {
+class BookmarkModel;
+} // namespace bookmarks
+
+namespace optimization_guide {
+class NewOptimizationGuideDecider;
+class OptimizationMetadata;
+} // namespace optimization_guide
+
namespace commerce {
+class ShoppingBookmarkModelObserver;
+class WebWrapper;
+
+// Information returned by the product info APIs.
+struct ProductInfo {
+ ProductInfo();
+ ProductInfo(const ProductInfo&);
+ ProductInfo& operator=(const ProductInfo&);
+ ~ProductInfo();
+
+ std::string title;
+ GURL image_url;
+ uint64_t product_cluster_id;
+ uint64_t offer_id;
+ std::string currency_code;
+ long amount_micros;
+ std::string country_code;
+};
+
+// Information returned by the merchant info APIs.
+struct MerchantInfo {
+ MerchantInfo();
+ MerchantInfo(const MerchantInfo&) = delete;
+ MerchantInfo& operator=(const MerchantInfo&) = delete;
+ MerchantInfo(MerchantInfo&&);
+ MerchantInfo& operator=(MerchantInfo&&) = default;
+ ~MerchantInfo();
+
+ float star_rating;
+ uint32_t count_rating;
+ GURL details_page_url;
+ bool has_return_policy;
+ float non_personalized_familiarity_score;
+ bool contains_sensitive_content;
+ bool proactive_message_disabled;
+};
+
+// Callbacks for querying a single URL or observing information from all
+// navigated urls.
+using ProductInfoCallback =
+ base::OnceCallback<void(const GURL&, const absl::optional<ProductInfo>&)>;
+using MerchantInfoCallback =
+ base::OnceCallback<void(const GURL&, absl::optional<MerchantInfo>)>;
+
class ShoppingService : public KeyedService, public base::SupportsUserData {
public:
- ShoppingService() = default;
- ~ShoppingService() override = default;
+ ShoppingService(bookmarks::BookmarkModel* bookmark_model,
+ optimization_guide::NewOptimizationGuideDecider* opt_guide,
+ PrefService* pref_service);
+ ~ShoppingService() override;
ShoppingService(const ShoppingService&) = delete;
ShoppingService& operator=(const ShoppingService&) = delete;
static void RegisterPrefs(PrefRegistrySimple* registry);
+ void GetProductInfoForUrl(const GURL& url, ProductInfoCallback callback);
+
+ void GetMerchantInfoForUrl(const GURL& url, MerchantInfoCallback callback);
+
void Shutdown() override;
+
+ private:
+ // "CommerceTabHelper" encompases both the content/ and ios/ versions.
+ friend class CommerceTabHelper;
+ // Test classes are also friends.
+ friend class ShoppingServiceTestBase;
+
+ // A notification that a WebWrapper has been created. This typically
+ // corresponds to a user creating a tab.
+ void WebWrapperCreated(WebWrapper* web);
+
+ // A notification that a WebWrapper has been destroyed. This signals that the
+ // web page backing the provided WebWrapper is about to be destroyed.
+ // Typically corresponds to a user closing a tab.
+ void WebWrapperDestroyed(WebWrapper* web);
+
+ private:
+ // Allow tests to access private methods.
+ friend class ShoppingServiceTestBase;
+
+ // A notification that a web wrapper finished a navigation in the primary
+ // main frame.
+ void DidNavigatePrimaryMainFrame(WebWrapper* web);
+
+ // A notification that the user navigated away from the |from_url|.
+ void DidNavigateAway(WebWrapper* web, const GURL& from_url);
+
+ // A notification that the provided web wrapper has finished loading its main
+ // frame.
+ void DidFinishLoad(WebWrapper* web);
+
+ // Whether APIs like |GetProductInfoForURL| are enabled and allowed to be
+ // used.
+ bool IsProductInfoApiEnabled();
+
+ // Whether the PDP (product details page) state of a page is allowed to be
+ // recorded.
+ bool IsPDPMetricsRecordingEnabled();
+
+ // A callback for recording metrics after page navigation and having
+ // determined the page is shopping related.
+ void PDPMetricsCallback(
+ bool is_off_the_record,
+ optimization_guide::OptimizationGuideDecision decision,
+ const optimization_guide::OptimizationMetadata& metadata);
+
+ // Whether APIs like |GetMerchantInfoForURL| are enabled and allowed to be
+ // used.
+ bool IsMerchantInfoApiEnabled();
+
+ void HandleOptGuideProductInfoResponse(
+ const GURL& url,
+ ProductInfoCallback callback,
+ optimization_guide::OptimizationGuideDecision decision,
+ const optimization_guide::OptimizationMetadata& metadata);
+
+ void HandleOptGuideMerchantInfoResponse(
+ const GURL& url,
+ MerchantInfoCallback callback,
+ optimization_guide::OptimizationGuideDecision decision,
+ const optimization_guide::OptimizationMetadata& metadata);
+
+ // Update the cache notifying that a tab is on the specified URL.
+ void UpdateProductInfoCacheForInsertion(const GURL& url);
+
+ // Update the data stored in the cache.
+ void UpdateProductInfoCache(const GURL& url,
+ bool needs_js,
+ std::unique_ptr<ProductInfo> info);
+
+ // Get the data stored in the cache or nullptr if none exists.
+ const ProductInfo* GetFromProductInfoCache(const GURL& url);
+
+ // Update the cache storing product info for a navigation away from the
+ // provided URL or closing of a tab.
+ void UpdateProductInfoCacheForRemoval(const GURL& url);
+
+ // A handle to optimization guide for information about URLs that have
+ // recently been navigated to.
+ raw_ptr<optimization_guide::NewOptimizationGuideDecider> opt_guide_;
+
+ raw_ptr<PrefService> pref_service_;
+
+ // The service's means of observing the bookmark model which is automatically
+ // removed from the model when destroyed. This will be null if no
+ // BookmarkModel is provided to the service.
+ std::unique_ptr<ShoppingBookmarkModelObserver> shopping_bookmark_observer_;
+
+ // This is a cache that maps URL to a tuple of number of web wrappers the URL
+ // is open in, whether the javascript fallback needs to run, and the product
+ // info associated with the URL, so: <count, run_js, info>.
+ std::unordered_map<std::string,
+ std::tuple<uint32_t, bool, std::unique_ptr<ProductInfo>>>
+ product_info_cache_;
+
+ base::WeakPtrFactory<ShoppingService> weak_ptr_factory_;
};
} // namespace commerce
diff --git a/chromium/components/commerce/core/shopping_service_test_base.cc b/chromium/components/commerce/core/shopping_service_test_base.cc
new file mode 100644
index 00000000000..dbddc3a4591
--- /dev/null
+++ b/chromium/components/commerce/core/shopping_service_test_base.cc
@@ -0,0 +1,186 @@
+// Copyright 2022 The Chromium 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/commerce/core/shopping_service_test_base.h"
+
+#include "base/notreached.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/test/test_bookmark_client.h"
+#include "components/commerce/core/commerce_feature_list.h"
+#include "components/commerce/core/proto/merchant_trust.pb.h"
+#include "components/commerce/core/proto/price_tracking.pb.h"
+#include "components/optimization_guide/proto/common_types.pb.h"
+#include "components/optimization_guide/proto/hints.pb.h"
+#include "components/prefs/testing_pref_service.h"
+
+using optimization_guide::OptimizationGuideDecision;
+using optimization_guide::OptimizationGuideDecisionCallback;
+using optimization_guide::OptimizationMetadata;
+using optimization_guide::proto::Any;
+using optimization_guide::proto::OptimizationType;
+
+namespace commerce {
+
+MockOptGuideDecider::MockOptGuideDecider() = default;
+MockOptGuideDecider::~MockOptGuideDecider() = default;
+
+void MockOptGuideDecider::RegisterOptimizationTypes(
+ const std::vector<OptimizationType>& optimization_types) {}
+
+void MockOptGuideDecider::CanApplyOptimization(
+ const GURL& url,
+ OptimizationType optimization_type,
+ OptimizationGuideDecisionCallback callback) {
+ bool type_matches = optimization_type_.has_value() &&
+ optimization_type_.value() == optimization_type;
+ bool url_matches = response_url_.has_value() && url == response_url_.value();
+
+ if (!type_matches || !url_matches) {
+ std::move(callback).Run(OptimizationGuideDecision::kUnknown,
+ OptimizationMetadata());
+ return;
+ }
+
+ std::move(callback).Run(optimization_decision_.value(),
+ optimization_data_.value());
+}
+
+OptimizationGuideDecision MockOptGuideDecider::CanApplyOptimization(
+ const GURL& url,
+ OptimizationType optimization_type,
+ OptimizationMetadata* optimization_metadata) {
+ // We don't use the synchronous API in the shopping service.
+ NOTREACHED();
+
+ return OptimizationGuideDecision::kUnknown;
+}
+
+void MockOptGuideDecider::SetResponse(const GURL& url,
+ const OptimizationType type,
+ const OptimizationGuideDecision decision,
+ const OptimizationMetadata& data) {
+ response_url_ = url;
+ optimization_type_ = type;
+ optimization_decision_ = decision;
+ optimization_data_ = data;
+}
+
+OptimizationMetadata MockOptGuideDecider::BuildPriceTrackingResponse(
+ const std::string& title,
+ const std::string& image_url,
+ const uint64_t offer_id,
+ const uint64_t product_cluster_id,
+ const std::string& country_code) {
+ OptimizationMetadata meta;
+
+ PriceTrackingData price_tracking_data;
+ BuyableProduct* buyable_product =
+ price_tracking_data.mutable_buyable_product();
+ buyable_product->set_title(title);
+ buyable_product->set_image_url(image_url);
+ buyable_product->set_offer_id(offer_id);
+ buyable_product->set_product_cluster_id(product_cluster_id);
+ buyable_product->set_country_code(country_code);
+
+ Any any;
+ any.set_type_url(price_tracking_data.GetTypeName());
+ price_tracking_data.SerializeToString(any.mutable_value());
+ meta.set_any_metadata(any);
+
+ return meta;
+}
+
+OptimizationMetadata MockOptGuideDecider::BuildMerchantTrustResponse(
+ const float star_rating,
+ const uint32_t count_rating,
+ const std::string& details_page_url,
+ const bool has_return_policy,
+ const bool contains_sensitive_content) {
+ OptimizationMetadata meta;
+
+ MerchantTrustSignalsV2 merchant_trust_data;
+ merchant_trust_data.set_merchant_star_rating(star_rating);
+ merchant_trust_data.set_merchant_count_rating(count_rating);
+ merchant_trust_data.set_merchant_details_page_url(details_page_url);
+ merchant_trust_data.set_has_return_policy(has_return_policy);
+ merchant_trust_data.set_contains_sensitive_content(
+ contains_sensitive_content);
+
+ Any any;
+ any.set_type_url(merchant_trust_data.GetTypeName());
+ merchant_trust_data.SerializeToString(any.mutable_value());
+ meta.set_any_metadata(any);
+
+ return meta;
+}
+
+MockWebWrapper::MockWebWrapper(const GURL& last_committed_url,
+ bool is_off_the_record)
+ : last_committed_url_(last_committed_url),
+ is_off_the_record_(is_off_the_record) {}
+
+MockWebWrapper::~MockWebWrapper() = default;
+
+const GURL& MockWebWrapper::GetLastCommittedURL() {
+ return last_committed_url_;
+}
+
+bool MockWebWrapper::IsOffTheRecord() {
+ return is_off_the_record_;
+}
+
+void MockWebWrapper::RunJavascript(
+ const std::u16string& script,
+ base::OnceCallback<void(const base::Value)> callback) {
+ if (!mock_js_result_) {
+ std::move(callback).Run(base::Value());
+ return;
+ }
+
+ std::move(callback).Run(mock_js_result_->Clone());
+}
+
+void MockWebWrapper::SetMockJavaScriptResult(base::Value* result) {
+ mock_js_result_ = result;
+}
+
+ShoppingServiceTestBase::ShoppingServiceTestBase()
+ : bookmark_model_(bookmarks::TestBookmarkClient::CreateModel()),
+ opt_guide_(std::make_unique<MockOptGuideDecider>()),
+ pref_service_(std::make_unique<TestingPrefServiceSimple>()) {
+ shopping_service_ = std::make_unique<ShoppingService>(
+ bookmark_model_.get(), opt_guide_.get(), pref_service_.get());
+}
+
+ShoppingServiceTestBase::~ShoppingServiceTestBase() = default;
+
+void ShoppingServiceTestBase::TestBody() {}
+
+void ShoppingServiceTestBase::DidNavigatePrimaryMainFrame(WebWrapper* web) {
+ shopping_service_->DidNavigatePrimaryMainFrame(web);
+}
+
+void ShoppingServiceTestBase::DidNavigateAway(WebWrapper* web,
+ const GURL& url) {
+ shopping_service_->DidNavigateAway(web, url);
+}
+
+void ShoppingServiceTestBase::WebWrapperDestroyed(WebWrapper* web) {
+ shopping_service_->WebWrapperDestroyed(web);
+}
+
+int ShoppingServiceTestBase::GetProductInfoCacheOpenURLCount(const GURL& url) {
+ auto it = shopping_service_->product_info_cache_.find(url.spec());
+ if (it == shopping_service_->product_info_cache_.end())
+ return 0;
+
+ return std::get<0>(it->second);
+}
+
+const ProductInfo* ShoppingServiceTestBase::GetFromProductInfoCache(
+ const GURL& url) {
+ return shopping_service_->GetFromProductInfoCache(url);
+}
+
+} // namespace commerce
diff --git a/chromium/components/commerce/core/shopping_service_test_base.h b/chromium/components/commerce/core/shopping_service_test_base.h
new file mode 100644
index 00000000000..80fdfe2669c
--- /dev/null
+++ b/chromium/components/commerce/core/shopping_service_test_base.h
@@ -0,0 +1,142 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_COMMERCE_CORE_SHOPPING_SERVICE_TEST_BASE_H_
+#define COMPONENTS_COMMERCE_CORE_SHOPPING_SERVICE_TEST_BASE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/values.h"
+#include "components/commerce/core/shopping_service.h"
+#include "components/commerce/core/web_wrapper.h"
+#include "components/optimization_guide/core/new_optimization_guide_decider.h"
+#include "components/optimization_guide/core/optimization_guide_decision.h"
+#include "components/optimization_guide/core/optimization_metadata.h"
+#include "components/optimization_guide/proto/hints.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using optimization_guide::OptimizationGuideDecision;
+using optimization_guide::OptimizationGuideDecisionCallback;
+using optimization_guide::OptimizationMetadata;
+using optimization_guide::proto::OptimizationType;
+
+class TestingPrefServiceSimple;
+
+namespace bookmarks {
+class BookmarkModel;
+}
+
+namespace commerce {
+
+// A mock Optimization Guide decider that allows us to specify the response for
+// a particular URL.
+class MockOptGuideDecider
+ : public optimization_guide::NewOptimizationGuideDecider {
+ public:
+ MockOptGuideDecider();
+ MockOptGuideDecider(const MockOptGuideDecider&) = delete;
+ MockOptGuideDecider operator=(const MockOptGuideDecider&) = delete;
+ ~MockOptGuideDecider() override;
+
+ void RegisterOptimizationTypes(
+ const std::vector<OptimizationType>& optimization_types) override;
+
+ void CanApplyOptimization(
+ const GURL& url,
+ OptimizationType optimization_type,
+ OptimizationGuideDecisionCallback callback) override;
+
+ OptimizationGuideDecision CanApplyOptimization(
+ const GURL& url,
+ OptimizationType optimization_type,
+ OptimizationMetadata* optimization_metadata) override;
+
+ void SetResponse(const GURL& url,
+ const OptimizationType type,
+ const OptimizationGuideDecision decision,
+ const OptimizationMetadata& data);
+
+ OptimizationMetadata BuildPriceTrackingResponse(
+ const std::string& title,
+ const std::string& image_url,
+ const uint64_t offer_id,
+ const uint64_t product_cluster_id,
+ const std::string& country_code);
+
+ OptimizationMetadata BuildMerchantTrustResponse(
+ const float star_rating,
+ const uint32_t count_rating,
+ const std::string& details_page_url,
+ const bool has_return_policy,
+ const bool contains_sensitive_content);
+
+ private:
+ absl::optional<GURL> response_url_;
+ absl::optional<OptimizationType> optimization_type_;
+ absl::optional<OptimizationGuideDecision> optimization_decision_;
+ absl::optional<OptimizationMetadata> optimization_data_;
+};
+
+// A mock WebWrapper where returned values can be manually set.
+class MockWebWrapper : public WebWrapper {
+ public:
+ MockWebWrapper(const GURL& last_committed_url, bool is_off_the_record);
+ MockWebWrapper(const MockWebWrapper&) = delete;
+ MockWebWrapper operator=(const MockWebWrapper&) = delete;
+ ~MockWebWrapper() override;
+
+ const GURL& GetLastCommittedURL() override;
+
+ bool IsOffTheRecord() override;
+
+ void RunJavascript(
+ const std::u16string& script,
+ base::OnceCallback<void(const base::Value)> callback) override;
+
+ // Set the result of some javascript execution. This object does not take
+ // ownership of the provided pointer.
+ void SetMockJavaScriptResult(base::Value* result);
+
+ private:
+ GURL last_committed_url_;
+ bool is_off_the_record_;
+
+ raw_ptr<base::Value> mock_js_result_;
+};
+
+class ShoppingServiceTestBase : public testing::Test {
+ public:
+ ShoppingServiceTestBase();
+ ShoppingServiceTestBase(const ShoppingServiceTestBase&) = delete;
+ ShoppingServiceTestBase operator=(const ShoppingServiceTestBase&) = delete;
+ ~ShoppingServiceTestBase() override;
+
+ void TestBody() override;
+
+ // A direct proxies to the same methods in the ShoppingService class.
+ void DidNavigatePrimaryMainFrame(WebWrapper* web);
+ void DidNavigateAway(WebWrapper* web, const GURL& url);
+ void WebWrapperDestroyed(WebWrapper* web);
+
+ // Get the count of the number of tabs a particular URL is open in from the
+ // product info cache.
+ int GetProductInfoCacheOpenURLCount(const GURL& url);
+
+ // Get the item in the product info cache if it exists.
+ const ProductInfo* GetFromProductInfoCache(const GURL& url);
+
+ protected:
+ std::unique_ptr<bookmarks::BookmarkModel> bookmark_model_;
+
+ std::unique_ptr<MockOptGuideDecider> opt_guide_;
+
+ std::unique_ptr<ShoppingService> shopping_service_;
+
+ std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+};
+
+} // namespace commerce
+
+#endif // COMPONENTS_COMMERCE_CORE_SHOPPING_SERVICE_TEST_BASE_H_
diff --git a/chromium/components/commerce/core/shopping_service_unittest.cc b/chromium/components/commerce/core/shopping_service_unittest.cc
new file mode 100644
index 00000000000..c887c709a30
--- /dev/null
+++ b/chromium/components/commerce/core/shopping_service_unittest.cc
@@ -0,0 +1,246 @@
+// Copyright 2022 The Chromium 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/commerce/core/shopping_service.h"
+#include "base/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/commerce/core/commerce_feature_list.h"
+#include "components/commerce/core/shopping_service_test_base.h"
+#include "components/optimization_guide/core/new_optimization_guide_decider.h"
+#include "components/optimization_guide/core/optimization_guide_decision.h"
+#include "components/optimization_guide/core/optimization_metadata.h"
+#include "components/optimization_guide/proto/hints.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using optimization_guide::OptimizationGuideDecision;
+using optimization_guide::OptimizationGuideDecisionCallback;
+using optimization_guide::OptimizationMetadata;
+using optimization_guide::proto::Any;
+using optimization_guide::proto::OptimizationType;
+
+namespace {
+const char kProductUrl[] = "http://example.com/";
+const char kTitle[] = "product title";
+const char kImageUrl[] = "http://example.com/image.png";
+const uint64_t kOfferId = 123;
+const uint64_t kClusterId = 456;
+const char kCountryCode[] = "US";
+
+const char kMerchantUrl[] = "http://example.com/merchant";
+const float kStarRating = 4.5;
+const uint32_t kCountRating = 1000;
+const char kDetailsPageUrl[] = "http://example.com/merchant_details_page";
+const bool kHasReturnPolicy = true;
+const bool kContainsSensitiveContent = false;
+} // namespace
+
+namespace commerce {
+
+class ShoppingServiceTest : public ShoppingServiceTestBase {
+ public:
+ ShoppingServiceTest() = default;
+ ShoppingServiceTest(const ShoppingServiceTest&) = delete;
+ ShoppingServiceTest operator=(const ShoppingServiceTest&) = delete;
+ ~ShoppingServiceTest() override = default;
+};
+
+// Test that product info is processed correctly.
+TEST_F(ShoppingServiceTest, TestProductInfoResponse) {
+ // Ensure a feature that uses product info is enabled. This doesn't
+ // necessarily need to be the shopping list.
+ base::test::ScopedFeatureList test_features;
+ test_features.InitAndEnableFeature(commerce::kShoppingList);
+
+ OptimizationMetadata meta = opt_guide_->BuildPriceTrackingResponse(
+ kTitle, kImageUrl, kOfferId, kClusterId, kCountryCode);
+
+ opt_guide_->SetResponse(GURL(kProductUrl), OptimizationType::PRICE_TRACKING,
+ OptimizationGuideDecision::kTrue, meta);
+
+ bool callback_executed = false;
+ shopping_service_->GetProductInfoForUrl(
+ GURL(kProductUrl), base::BindOnce(
+ [](bool* callback_executed, const GURL& url,
+ const absl::optional<ProductInfo>& info) {
+ ASSERT_EQ(kProductUrl, url.spec());
+ ASSERT_TRUE(info.has_value());
+
+ ASSERT_EQ(kTitle, info->title);
+ ASSERT_EQ(kImageUrl, info->image_url);
+ ASSERT_EQ(kOfferId, info->offer_id);
+ ASSERT_EQ(kClusterId, info->product_cluster_id);
+ ASSERT_EQ(kCountryCode, info->country_code);
+ *callback_executed = true;
+ },
+ &callback_executed));
+
+ // Make sure the callback was actually run. In testing the callback is run
+ // immediately, this check ensures that is actually the case.
+ ASSERT_TRUE(callback_executed);
+}
+
+// Test that no object is provided for a negative optimization guide response.
+TEST_F(ShoppingServiceTest, TestProductInfoResponse_OptGuideFalse) {
+ base::test::ScopedFeatureList test_features;
+ test_features.InitAndEnableFeature(commerce::kShoppingList);
+
+ opt_guide_->SetResponse(GURL(kProductUrl), OptimizationType::PRICE_TRACKING,
+ OptimizationGuideDecision::kFalse,
+ OptimizationMetadata());
+
+ bool callback_executed = false;
+ shopping_service_->GetProductInfoForUrl(
+ GURL(kProductUrl), base::BindOnce(
+ [](bool* callback_executed, const GURL& url,
+ const absl::optional<ProductInfo>& info) {
+ ASSERT_EQ(kProductUrl, url.spec());
+ ASSERT_FALSE(info.has_value());
+ *callback_executed = true;
+ },
+ &callback_executed));
+
+ ASSERT_TRUE(callback_executed);
+}
+
+// Test that the product info cache only keeps track of live tabs.
+TEST_F(ShoppingServiceTest, TestProductInfoCacheURLCount) {
+ base::test::ScopedFeatureList test_features;
+ test_features.InitAndEnableFeature(kShoppingList);
+
+ std::string url = "http://example.com/foo";
+ MockWebWrapper web1(GURL(url), false);
+ MockWebWrapper web2(GURL(url), false);
+
+ std::string url2 = "http://example.com/bar";
+ MockWebWrapper web3(GURL(url2), false);
+
+ // Ensure navigating to then navigating away clears the cache item.
+ ASSERT_EQ(0, GetProductInfoCacheOpenURLCount(GURL(url)));
+ DidNavigatePrimaryMainFrame(&web1);
+ ASSERT_EQ(1, GetProductInfoCacheOpenURLCount(GURL(url)));
+ DidNavigateAway(&web1, GURL(url));
+ ASSERT_EQ(0, GetProductInfoCacheOpenURLCount(GURL(url)));
+
+ // Ensure navigating to a URL in multiple "tabs" retains the cached item until
+ // both are navigated away.
+ ASSERT_EQ(0, GetProductInfoCacheOpenURLCount(GURL(url)));
+ DidNavigatePrimaryMainFrame(&web1);
+ DidNavigatePrimaryMainFrame(&web2);
+ ASSERT_EQ(2, GetProductInfoCacheOpenURLCount(GURL(url)));
+ DidNavigateAway(&web1, GURL(url));
+ ASSERT_EQ(1, GetProductInfoCacheOpenURLCount(GURL(url)));
+ DidNavigateAway(&web2, GURL(url));
+ ASSERT_EQ(0, GetProductInfoCacheOpenURLCount(GURL(url)));
+
+ // Make sure more than one entry can be in the cache.
+ ASSERT_EQ(0, GetProductInfoCacheOpenURLCount(GURL(url)));
+ ASSERT_EQ(0, GetProductInfoCacheOpenURLCount(GURL(url2)));
+ DidNavigatePrimaryMainFrame(&web1);
+ DidNavigatePrimaryMainFrame(&web3);
+ ASSERT_EQ(1, GetProductInfoCacheOpenURLCount(GURL(url)));
+ ASSERT_EQ(1, GetProductInfoCacheOpenURLCount(GURL(url2)));
+
+ // Simulate closing the browser to ensure the cache is emptied.
+ WebWrapperDestroyed(&web1);
+ WebWrapperDestroyed(&web2);
+ WebWrapperDestroyed(&web3);
+
+ ASSERT_EQ(0, GetProductInfoCacheOpenURLCount(GURL(url)));
+ ASSERT_EQ(0, GetProductInfoCacheOpenURLCount(GURL(url2)));
+}
+
+// Test that product info is inserted into the cache without a client
+// necessarily querying for it.
+TEST_F(ShoppingServiceTest, TestProductInfoCacheFullLifecycle) {
+ base::test::ScopedFeatureList test_features;
+ test_features.InitWithFeatures({kShoppingList, kCommerceAllowServerImages},
+ {});
+
+ MockWebWrapper web(GURL(kProductUrl), false);
+
+ OptimizationMetadata meta = opt_guide_->BuildPriceTrackingResponse(
+ kTitle, kImageUrl, kOfferId, kClusterId, kCountryCode);
+
+ opt_guide_->SetResponse(GURL(kProductUrl), OptimizationType::PRICE_TRACKING,
+ OptimizationGuideDecision::kTrue, meta);
+
+ DidNavigatePrimaryMainFrame(&web);
+
+ // By this point there should be something in the cache.
+ ASSERT_EQ(1, GetProductInfoCacheOpenURLCount(GURL(kProductUrl)));
+
+ // We should be able to access the cached data.
+ const ProductInfo* cached_info = GetFromProductInfoCache(GURL(kProductUrl));
+ ASSERT_EQ(kTitle, cached_info->title);
+ ASSERT_EQ(kImageUrl, cached_info->image_url);
+ ASSERT_EQ(kOfferId, cached_info->offer_id);
+ ASSERT_EQ(kClusterId, cached_info->product_cluster_id);
+ ASSERT_EQ(kCountryCode, cached_info->country_code);
+
+ // The main API should still work.
+ bool callback_executed = false;
+ shopping_service_->GetProductInfoForUrl(
+ GURL(kProductUrl), base::BindOnce(
+ [](bool* callback_executed, const GURL& url,
+ const absl::optional<ProductInfo>& info) {
+ ASSERT_EQ(kProductUrl, url.spec());
+ ASSERT_TRUE(info.has_value());
+
+ ASSERT_EQ(kTitle, info->title);
+ ASSERT_EQ(kImageUrl, info->image_url);
+ ASSERT_EQ(kOfferId, info->offer_id);
+ ASSERT_EQ(kClusterId, info->product_cluster_id);
+ ASSERT_EQ(kCountryCode, info->country_code);
+ *callback_executed = true;
+ },
+ &callback_executed));
+
+ // Make sure the callback was actually run. In testing the callback is run
+ // immediately, this check ensures that is actually the case.
+ ASSERT_TRUE(callback_executed);
+
+ // Close the "tab" and make sure the cache is empty.
+ WebWrapperDestroyed(&web);
+ ASSERT_EQ(0, GetProductInfoCacheOpenURLCount(GURL(kProductUrl)));
+}
+
+// Test that merchant info is processed correctly.
+TEST_F(ShoppingServiceTest, TestMerchantInfoResponse) {
+ // Ensure a feature that uses merchant info is enabled.
+ base::test::ScopedFeatureList test_features;
+ test_features.InitAndEnableFeature(commerce::kCommerceMerchantViewer);
+
+ OptimizationMetadata meta = opt_guide_->BuildMerchantTrustResponse(
+ kStarRating, kCountRating, kDetailsPageUrl, kHasReturnPolicy,
+ kContainsSensitiveContent);
+
+ opt_guide_->SetResponse(GURL(kMerchantUrl),
+ OptimizationType::MERCHANT_TRUST_SIGNALS_V2,
+ OptimizationGuideDecision::kTrue, meta);
+
+ bool callback_executed = false;
+ shopping_service_->GetMerchantInfoForUrl(
+ GURL(kMerchantUrl),
+ base::BindOnce(
+ [](bool* callback_executed, const GURL& url,
+ absl::optional<MerchantInfo> info) {
+ ASSERT_EQ(kMerchantUrl, url.spec());
+ ASSERT_TRUE(info.has_value());
+
+ ASSERT_EQ(kStarRating, info->star_rating);
+ ASSERT_EQ(kCountRating, info->count_rating);
+ ASSERT_EQ(kDetailsPageUrl, info->details_page_url.spec());
+ ASSERT_EQ(kHasReturnPolicy, info->has_return_policy);
+ ASSERT_EQ(kContainsSensitiveContent,
+ info->contains_sensitive_content);
+ *callback_executed = true;
+ },
+ &callback_executed));
+
+ // Make sure the callback was actually run. In testing the callback is run
+ // immediately, this check ensures that is actually the case.
+ ASSERT_TRUE(callback_executed);
+}
+
+} // namespace commerce
diff --git a/chromium/components/commerce/core/web_wrapper.h b/chromium/components/commerce/core/web_wrapper.h
new file mode 100644
index 00000000000..f342c3162c7
--- /dev/null
+++ b/chromium/components/commerce/core/web_wrapper.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_COMMERCE_CORE_WEB_WRAPPER_H_
+#define COMPONENTS_COMMERCE_CORE_WEB_WRAPPER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "url/gurl.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace commerce {
+
+// A wrapper class for WebContent on desktop and android or WebState on iOS.
+class WebWrapper {
+ public:
+ WebWrapper() = default;
+ WebWrapper(const WebWrapper&) = delete;
+ virtual ~WebWrapper() = default;
+
+ // Get the URL that is currently being displayed for the page.
+ virtual const GURL& GetLastCommittedURL() = 0;
+
+ // Whether content is off the record or in incognito mode.
+ virtual bool IsOffTheRecord() = 0;
+
+ // Execute the provided |script| and pass the result through |callback|. This
+ // will run in an isolated world if possible.
+ virtual void RunJavascript(
+ const std::u16string& script,
+ base::OnceCallback<void(const base::Value)> callback) = 0;
+};
+
+} // namespace commerce
+
+#endif // COMPONENTS_COMMERCE_CORE_WEB_WRAPPER_H_
diff --git a/chromium/components/commerce/ios/DEPS b/chromium/components/commerce/ios/DEPS
new file mode 100644
index 00000000000..0fc0ddddacd
--- /dev/null
+++ b/chromium/components/commerce/ios/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ios/web/public",
+]
diff --git a/chromium/components/commerce/ios/browser/BUILD.gn b/chromium/components/commerce/ios/browser/BUILD.gn
new file mode 100644
index 00000000000..7a3eea0b734
--- /dev/null
+++ b/chromium/components/commerce/ios/browser/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("browser") {
+ sources = [
+ "commerce_tab_helper.h",
+ "commerce_tab_helper.mm",
+ "web_state_wrapper.h",
+ "web_state_wrapper.mm",
+ ]
+
+ deps = [
+ "//base",
+ "//components/commerce/core:shopping_service",
+ "//ios/web",
+ "//ios/web/public/js_messaging",
+ ]
+
+ configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/chromium/components/commerce/ios/browser/commerce_tab_helper.h b/chromium/components/commerce/ios/browser/commerce_tab_helper.h
new file mode 100644
index 00000000000..ecc6e667b12
--- /dev/null
+++ b/chromium/components/commerce/ios/browser/commerce_tab_helper.h
@@ -0,0 +1,78 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_COMMERCE_IOS_BROWSER_COMMERCE_TAB_HELPER_H_
+#define COMPONENTS_COMMERCE_IOS_BROWSER_COMMERCE_TAB_HELPER_H_
+
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "components/commerce/core/shopping_service.h"
+#include "components/commerce/core/web_wrapper.h"
+#include "components/commerce/ios/browser/web_state_wrapper.h"
+#include "ios/web/public/web_state_observer.h"
+#include "ios/web/public/web_state_user_data.h"
+
+namespace web {
+class NavigationContext;
+class WebState;
+} // namespace web
+
+namespace commerce {
+
+// This tab helper creates and maintains a WebWrapper that is backed by
+// WebState. Events that occur on the wrapper are reported back to the
+// shopping service where they are used by various commerce features.
+class CommerceTabHelper : public web::WebStateObserver,
+ public web::WebStateUserData<CommerceTabHelper> {
+ public:
+ ~CommerceTabHelper() override;
+ CommerceTabHelper(const CommerceTabHelper& other) = delete;
+ CommerceTabHelper& operator=(const CommerceTabHelper& other) = delete;
+
+ static void CreateForWebState(web::WebState* web_state,
+ bool is_off_the_record,
+ ShoppingService* shopping_service);
+
+ // web::WebStateObserver implementation
+ void DidFinishNavigation(web::WebState* web_state,
+ web::NavigationContext* navigation_context) override;
+
+ void PageLoaded(
+ web::WebState* web_state,
+ web::PageLoadCompletionStatus load_completion_status) override;
+
+ void WebStateDestroyed(web::WebState* web_state) override;
+
+ private:
+ friend class web::WebStateUserData<CommerceTabHelper>;
+
+ CommerceTabHelper(web::WebState* contents,
+ bool is_off_the_record,
+ ShoppingService* shopping_service);
+
+ const bool is_off_the_record_;
+
+ std::unique_ptr<WebStateWrapper> web_wrapper_;
+
+ raw_ptr<ShoppingService> shopping_service_;
+
+ // Automatically remove this observer from its host when destroyed.
+ base::ScopedObservation<web::WebState, web::WebStateObserver>
+ scoped_observation_{this};
+
+ // The url from the previous successful main frame navigation. This will be
+ // empty if this is the first navigation for this tab or post-restart. We keep
+ // track of this because the URL kepkt by the backing WebContents will have
+ // changed before we get the signal for it.
+ GURL previous_main_frame_url_;
+
+ WEB_STATE_USER_DATA_KEY_DECL();
+};
+
+} // namespace commerce
+
+#endif // COMPONENTS_COMMERCE_IOS_BROWSER_COMMERCE_TAB_HELPER_H_
diff --git a/chromium/components/commerce/ios/browser/commerce_tab_helper.mm b/chromium/components/commerce/ios/browser/commerce_tab_helper.mm
new file mode 100644
index 00000000000..6fcd60316c3
--- /dev/null
+++ b/chromium/components/commerce/ios/browser/commerce_tab_helper.mm
@@ -0,0 +1,79 @@
+// Copyright 2022 The Chromium 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/commerce/ios/browser/commerce_tab_helper.h"
+
+#include "base/memory/ptr_util.h"
+#include "ios/web/public/navigation/navigation_context.h"
+#include "ios/web/public/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace commerce {
+
+void CommerceTabHelper::CreateForWebState(web::WebState* web_state,
+ bool is_off_the_record,
+ ShoppingService* shopping_service) {
+ if (FromWebState(web_state))
+ return;
+
+ web_state->SetUserData(UserDataKey(),
+ base::WrapUnique(new CommerceTabHelper(
+ web_state, is_off_the_record, shopping_service)));
+}
+
+CommerceTabHelper::CommerceTabHelper(web::WebState* state,
+ bool is_off_the_record,
+ ShoppingService* shopping_service)
+ : is_off_the_record_(is_off_the_record),
+ web_wrapper_(std::make_unique<WebStateWrapper>(state)),
+ shopping_service_(shopping_service) {
+ scoped_observation_.Observe(state);
+
+ if (shopping_service_)
+ shopping_service_->WebWrapperCreated(web_wrapper_.get());
+}
+
+CommerceTabHelper::~CommerceTabHelper() = default;
+
+void CommerceTabHelper::DidFinishNavigation(
+ web::WebState* web_state,
+ web::NavigationContext* navigation_context) {
+ if (!shopping_service_ || navigation_context->IsSameDocument()) {
+ return;
+ }
+
+ // Notify the service that we're no longer interested in a particular URL.
+ shopping_service_->DidNavigateAway(web_wrapper_.get(),
+ previous_main_frame_url_);
+ previous_main_frame_url_ = web_wrapper_->GetLastCommittedURL();
+
+ shopping_service_->DidNavigatePrimaryMainFrame(web_wrapper_.get());
+}
+
+void CommerceTabHelper::PageLoaded(
+ web::WebState* web_state,
+ web::PageLoadCompletionStatus load_completion_status) {
+ if (!shopping_service_)
+ return;
+
+ shopping_service_->DidFinishLoad(web_wrapper_.get());
+}
+
+void CommerceTabHelper::WebStateDestroyed(web::WebState* web_state) {
+ if (shopping_service_)
+ shopping_service_->WebWrapperDestroyed(web_wrapper_.get());
+
+ web_wrapper_->ClearWebStatePointer();
+
+ // This needs to be reset prior to the destruction of the web state in order
+ // to prevent a CHECK failure for observer count.
+ scoped_observation_.Reset();
+}
+
+WEB_STATE_USER_DATA_KEY_IMPL(CommerceTabHelper);
+
+} // namespace commerce
diff --git a/chromium/components/commerce/ios/browser/web_state_wrapper.h b/chromium/components/commerce/ios/browser/web_state_wrapper.h
new file mode 100644
index 00000000000..8bea9f6d04f
--- /dev/null
+++ b/chromium/components/commerce/ios/browser/web_state_wrapper.h
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_COMMERCE_IOS_BROWSER_WEB_STATE_WRAPPER_H_
+#define COMPONENTS_COMMERCE_IOS_BROWSER_WEB_STATE_WRAPPER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "components/commerce/core/web_wrapper.h"
+#include "ios/web/public/web_state.h"
+
+class GURL;
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace commerce {
+
+// A WebWrapper backed by web::WebState.
+class WebStateWrapper : public WebWrapper {
+ public:
+ explicit WebStateWrapper(web::WebState* web_state);
+ WebStateWrapper(const WebStateWrapper&) = delete;
+ WebStateWrapper operator=(const WebStateWrapper&) = delete;
+ ~WebStateWrapper() override = default;
+
+ const GURL& GetLastCommittedURL() override;
+
+ bool IsOffTheRecord() override;
+
+ void RunJavascript(
+ const std::u16string& script,
+ base::OnceCallback<void(const base::Value)> callback) override;
+
+ void ClearWebStatePointer();
+
+ private:
+ base::raw_ptr<web::WebState> web_state_;
+};
+
+} // namespace commerce
+
+#endif // COMPONENTS_COMMERCE_IOS_BROWSER_WEB_STATE_WRAPPER_H_
diff --git a/chromium/components/commerce/ios/browser/web_state_wrapper.mm b/chromium/components/commerce/ios/browser/web_state_wrapper.mm
new file mode 100644
index 00000000000..408ec73db95
--- /dev/null
+++ b/chromium/components/commerce/ios/browser/web_state_wrapper.mm
@@ -0,0 +1,61 @@
+// Copyright 2022 The Chromium 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/commerce/ios/browser/web_state_wrapper.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "ios/web/public/browser_state.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frames_manager.h"
+#include "ios/web/public/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace commerce {
+
+WebStateWrapper::WebStateWrapper(web::WebState* web_state)
+ : web_state_(web_state) {}
+
+const GURL& WebStateWrapper::GetLastCommittedURL() {
+ if (!web_state_)
+ return std::move(GURL());
+
+ return web_state_->GetLastCommittedURL();
+}
+
+bool WebStateWrapper::IsOffTheRecord() {
+ if (!web_state_ || !web_state_->GetBrowserState())
+ return false;
+
+ return web_state_->GetBrowserState()->IsOffTheRecord();
+}
+
+void WebStateWrapper::RunJavascript(
+ const std::u16string& script,
+ base::OnceCallback<void(const base::Value)> callback) {
+ // GetWebFramesManager() never returns null, but the main frame mght be.
+ if (!web_state_ || !web_state_->GetWebFramesManager()->GetMainWebFrame()) {
+ std::move(callback).Run(base::Value());
+ return;
+ }
+
+ web_state_->GetWebFramesManager()->GetMainWebFrame()->ExecuteJavaScript(
+ script,
+ base::BindOnce(
+ [](base::OnceCallback<void(const base::Value)> callback,
+ const base::Value* response) {
+ std::move(callback).Run(response ? response->Clone()
+ : base::Value());
+ },
+ std::move(callback)));
+}
+
+void WebStateWrapper::ClearWebStatePointer() {
+ web_state_ = nullptr;
+}
+
+} // namespace commerce
diff --git a/chromium/components/commerce_strings.grdp b/chromium/components/commerce_strings.grdp
index cea8b17ba38..257c88d7a92 100644
--- a/chromium/components/commerce_strings.grdp
+++ b/chromium/components/commerce_strings.grdp
@@ -23,5 +23,14 @@
<message name="IDS_DISCOUNT_CONTEXTUAL_CONSENT_ACCEPTED_CONFIRMATION_DONE" desc="The text shown on the consent confirmation bubble bubble. User can click this button to close the bubble.">
Done
</message>
+ <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_ACCEPT_BUTTON" translateable="false" desc="Text shown on the accept button of the consent dialog. By clicking this button, users has chosen to accept the consent.">
+ Yes, I'm in
+ </message>
+ <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_TITLE" translateable="false" desc="The title shown on the native consent dialog for getting discount. Note: This is intentionally left blank. No translation is required.">
+ Get discounts on your carts&#13; &amp; when you shop online
+ </message>
+ <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_BODY" translateable="false" desc="The actual consent descrition that shows in the native consent dialog. Note: This is intentionally left blank. No translation is required.">
+ Let Google find personalized discounts on your carts and on online stores you visit. When available, discounts will automatically show up on your carts and on sites.
+ </message>
</if>
</grit-part> \ No newline at end of file
diff --git a/chromium/components/component_updater/android/BUILD.gn b/chromium/components/component_updater/android/BUILD.gn
index ad474edf395..a793e3b77e4 100644
--- a/chromium/components/component_updater/android/BUILD.gn
+++ b/chromium/components/component_updater/android/BUILD.gn
@@ -13,6 +13,8 @@ android_library("background_task_update_scheduler_java") {
deps = [
":background_task_update_scheduler_jni_headers",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/background_task_scheduler:background_task_scheduler_java",
"//components/background_task_scheduler:background_task_scheduler_task_ids_java",
"//third_party/android_deps:chromium_play_services_availability_java",
@@ -56,6 +58,8 @@ android_library("embedded_component_loader_java") {
":component_provider_service_aidl_java",
":embedded_component_loader_jni_headers",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_main_dex_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
diff --git a/chromium/components/component_updater/component_updater_service.cc b/chromium/components/component_updater/component_updater_service.cc
index 33d6bee1660..6e0ca5dbba4 100644
--- a/chromium/components/component_updater/component_updater_service.cc
+++ b/chromium/components/component_updater/component_updater_service.cc
@@ -367,52 +367,26 @@ bool CrxUpdateService::CheckForUpdates(
UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_AUTOMATIC,
UPDATE_TYPE_COUNT);
- std::vector<std::string> secure_ids; // Requires HTTPS for update checks.
- std::vector<std::string> unsecure_ids; // Can fallback to HTTP.
- for (const auto& id : components_order_) {
- DCHECK(components_.find(id) != components_.end());
-
- const auto component = GetComponent(id);
- if (!component || component->requires_network_encryption)
- secure_ids.push_back(id);
- else
- unsecure_ids.push_back(id);
- }
-
- if (unsecure_ids.empty() && secure_ids.empty()) {
+ if (components_order_.empty()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(on_finished));
return true;
}
- Callback on_finished_callback = base::BindOnce(
- [](UpdateScheduler::OnFinishedCallback on_finished,
- update_client::Error error) { std::move(on_finished).Run(); },
- std::move(on_finished));
-
- if (!unsecure_ids.empty()) {
- update_client_->Update(
- unsecure_ids,
- base::BindOnce(&CrxUpdateService::GetCrxComponents,
- base::Unretained(this)),
- {}, false,
- base::BindOnce(
- &CrxUpdateService::OnUpdateComplete, base::Unretained(this),
- secure_ids.empty() ? std::move(on_finished_callback) : Callback(),
- base::TimeTicks::Now()));
- }
-
- if (!secure_ids.empty()) {
- update_client_->Update(
- secure_ids,
- base::BindOnce(&CrxUpdateService::GetCrxComponents,
- base::Unretained(this)),
- {}, false,
- base::BindOnce(&CrxUpdateService::OnUpdateComplete,
- base::Unretained(this), std::move(on_finished_callback),
- base::TimeTicks::Now()));
- }
-
+ update_client_->Update(
+ components_order_,
+ base::BindOnce(&CrxUpdateService::GetCrxComponents,
+ base::Unretained(this)),
+ {}, false,
+ base::BindOnce(&CrxUpdateService::OnUpdateComplete,
+ base::Unretained(this),
+ base::BindOnce(
+ [](UpdateScheduler::OnFinishedCallback on_finished,
+ update_client::Error /*error*/) {
+ std::move(on_finished).Run();
+ },
+ std::move(on_finished)),
+ base::TimeTicks::Now()));
return true;
}
diff --git a/chromium/components/component_updater/configurator_impl.cc b/chromium/components/component_updater/configurator_impl.cc
index c6d75f0ecb1..b9dd594e7a7 100644
--- a/chromium/components/component_updater/configurator_impl.cc
+++ b/chromium/components/component_updater/configurator_impl.cc
@@ -149,7 +149,10 @@ update_client::UpdaterStateProvider ConfiguratorImpl::GetUpdaterStateProvider()
absl::optional<bool> ConfiguratorImpl::IsMachineExternallyManaged() const {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
- return base::IsMachineExternallyManaged();
+ // TODO (crbug.com/1320776): For legacy compatibility, this uses
+ // IsEnterpriseDevice() which effectively equates to a domain join check.
+ // Consider whether this should use IsManagedDevice() instead.
+ return base::IsEnterpriseDevice();
#else
return absl::nullopt;
#endif
diff --git a/chromium/components/component_updater/crl_set_remover.cc b/chromium/components/component_updater/crl_set_remover.cc
index cc913e33073..8e5a9f5d4fb 100644
--- a/chromium/components/component_updater/crl_set_remover.cc
+++ b/chromium/components/component_updater/crl_set_remover.cc
@@ -13,9 +13,8 @@ namespace component_updater {
void DeleteLegacyCRLSet(const base::FilePath& user_data_dir) {
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
- base::BindOnce(base::GetDeleteFileCallback(),
- user_data_dir.Append(
- FILE_PATH_LITERAL("Certificate Revocation Lists"))));
+ base::GetDeleteFileCallback(user_data_dir.Append(
+ FILE_PATH_LITERAL("Certificate Revocation Lists"))));
}
} // namespace component_updater
diff --git a/chromium/components/component_updater/installer_policies/BUILD.gn b/chromium/components/component_updater/installer_policies/BUILD.gn
index aff69665bff..7b5328ceeec 100644
--- a/chromium/components/component_updater/installer_policies/BUILD.gn
+++ b/chromium/components/component_updater/installer_policies/BUILD.gn
@@ -24,6 +24,8 @@ static_library("installer_policies_no_content_deps") {
"safety_tips_component_installer.h",
"trust_token_key_commitments_component_installer_policy.cc",
"trust_token_key_commitments_component_installer_policy.h",
+ "url_param_classification_component_installer.cc",
+ "url_param_classification_component_installer.h",
]
deps = [
@@ -33,6 +35,8 @@ static_library("installer_policies_no_content_deps") {
"//components/prefs:prefs",
"//components/reputation/core",
"//components/reputation/core:proto",
+ "//components/url_param_filter/core:core",
+ "//components/url_param_filter/core:url_param_filter_classification_proto",
]
# Disallow depending on content.
@@ -62,6 +66,7 @@ source_set("unit_tests") {
"autofill_states_component_installer_unittest.cc",
"optimization_hints_component_installer_unittest.cc",
"trust_token_key_commitments_component_installer_policy_unittest.cc",
+ "url_param_classification_component_installer_unittest.cc",
]
deps = [
@@ -71,6 +76,9 @@ source_set("unit_tests") {
"//components/autofill/core/browser:test_support",
"//components/component_updater:test_support",
"//components/optimization_guide/core",
+ "//components/url_param_filter/core:core",
+ "//components/url_param_filter/core:test_support",
+ "//components/url_param_filter/core:url_param_filter_classification_proto",
"//services/network/public/cpp",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/components/component_updater/installer_policies/DEPS b/chromium/components/component_updater/installer_policies/DEPS
index 8c299adc7ce..a327ea6f235 100644
--- a/chromium/components/component_updater/installer_policies/DEPS
+++ b/chromium/components/component_updater/installer_policies/DEPS
@@ -6,5 +6,6 @@ include_rules = [
"+components/optimization_guide/core",
"+components/prefs",
"+components/reputation/core",
+ "+components/url_param_filter/core",
"+services/network/public",
]
diff --git a/chromium/components/component_updater/installer_policies/url_param_classification_component_installer.cc b/chromium/components/component_updater/installer_policies/url_param_classification_component_installer.cc
new file mode 100644
index 00000000000..adf0bffd382
--- /dev/null
+++ b/chromium/components/component_updater/installer_policies/url_param_classification_component_installer.cc
@@ -0,0 +1,244 @@
+// Copyright 2022 The Chromium 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/component_updater/installer_policies/url_param_classification_component_installer.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/path_service.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/version.h"
+#include "components/component_updater/component_updater_paths.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace {
+
+const base::FilePath::CharType kUrlParamClassificationsFileName[] =
+ FILE_PATH_LITERAL("list.pb");
+
+// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
+// The extension id is: dnhnnofocefcglhjeigmkhcgfoaipbaa
+const uint8_t kUrlParamClassificationsPublicKeySHA256[32] = {
+ 0x3d, 0x7d, 0xde, 0x5e, 0x24, 0x52, 0x6b, 0x79, 0x48, 0x6c, 0xa7,
+ 0x26, 0x5e, 0x08, 0xf1, 0x00, 0x82, 0x56, 0x69, 0xb1, 0xca, 0xfc,
+ 0x8a, 0x25, 0x50, 0x06, 0x6b, 0x5f, 0xa1, 0xd5, 0xeb, 0x80};
+
+const char kUrlParamClassificationManifestName[] = "Url Param Classifications";
+
+// Runs on a thread pool.
+absl::optional<std::string> LoadFileFromDisk(const base::FilePath& pb_path) {
+ if (!base::FeatureList::IsEnabled(
+ url_param_filter::features::kIncognitoParamFilterEnabled))
+ return absl::nullopt;
+ VLOG(1) << "Reading Url Param Classifications from file: " << pb_path.value();
+ std::string file_contents;
+ if (!base::ReadFileToString(pb_path, &file_contents)) {
+ // The file won't exist on new installations, so this is not always an
+ // error.
+ VLOG(1) << "Failed reading from " << pb_path.value();
+ return absl::nullopt;
+ }
+ return file_contents;
+}
+
+// Writes a metric denoting the |result| of validating a classification list.
+//
+// This method is called in VerifyInstallation which returns false (on an error)
+// or true (if the whole list is valid), so the metrics will be populated at
+// most once per version installed.
+void WriteMetrics(
+ component_updater::UrlParamClassificationComponentInstallerPolicy::
+ ClassificationListValidationResult result) {
+ base::UmaHistogramEnumeration(
+ "Navigation.UrlParamFilter.ClassificationListValidationResult", result);
+}
+
+absl::optional<base::FilePath>& GetConfigPathInstance() {
+ // Contains nullopt until registration is complete. Afterward, contains the
+ // FilePath for the component file, or an empty FilePath if no component was
+ // installed at startup.
+ static base::NoDestructor<absl::optional<base::FilePath>> instance;
+ return *instance;
+}
+
+} // namespace
+
+namespace component_updater {
+
+UrlParamClassificationComponentInstallerPolicy::
+ UrlParamClassificationComponentInstallerPolicy(
+ component_updater::OnUrlParamClassificationComponentReady
+ on_component_ready)
+ : on_component_ready_(on_component_ready) {}
+
+UrlParamClassificationComponentInstallerPolicy::
+ ~UrlParamClassificationComponentInstallerPolicy() = default;
+
+bool UrlParamClassificationComponentInstallerPolicy::
+ SupportsGroupPolicyEnabledComponentUpdates() const {
+ return true;
+}
+
+bool UrlParamClassificationComponentInstallerPolicy::RequiresNetworkEncryption()
+ const {
+ return false;
+}
+
+update_client::CrxInstaller::Result
+UrlParamClassificationComponentInstallerPolicy::OnCustomInstall(
+ const base::Value& manifest,
+ const base::FilePath& install_dir) {
+ return update_client::CrxInstaller::Result(0); // Nothing custom here.
+}
+
+void UrlParamClassificationComponentInstallerPolicy::OnCustomUninstall() {}
+
+base::FilePath UrlParamClassificationComponentInstallerPolicy::GetInstalledPath(
+ const base::FilePath& base) {
+ return base.Append(kUrlParamClassificationsFileName);
+}
+
+void UrlParamClassificationComponentInstallerPolicy::ComponentReady(
+ const base::Version& version,
+ const base::FilePath& install_dir,
+ base::Value manifest) {
+ VLOG(1) << "Component ready, version " << version.GetString() << " in "
+ << install_dir.value();
+ // Given BEST_EFFORT since we don't need to be USER_BLOCKING.
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+ base::BindOnce(&LoadFileFromDisk, GetInstalledPath(install_dir)),
+ base::BindOnce(
+ [](OnUrlParamClassificationComponentReady callback,
+ const absl::optional<std::string>& maybe_file) {
+ if (maybe_file.has_value())
+ callback.Run(maybe_file.value());
+ },
+ on_component_ready_));
+}
+
+// Called during startup and installation before ComponentReady().
+bool UrlParamClassificationComponentInstallerPolicy::VerifyInstallation(
+ const base::Value& manifest,
+ const base::FilePath& install_dir) const {
+ if (!base::PathExists(GetInstalledPath(install_dir))) {
+ WriteMetrics(
+ ClassificationListValidationResult::kMissingClassificationsFile);
+ return false;
+ }
+
+ std::string file_contents;
+ if (!base::ReadFileToString(GetInstalledPath(install_dir), &file_contents)) {
+ WriteMetrics(
+ ClassificationListValidationResult::kReadingClassificationsFileFailed);
+ return false;
+ }
+
+ url_param_filter::FilterClassifications classification_list;
+ if (!classification_list.ParseFromString(file_contents)) {
+ WriteMetrics(ClassificationListValidationResult::kParsingToProtoFailed);
+ return false;
+ }
+
+ std::vector<url_param_filter::FilterClassification> source_classifications,
+ destination_classifications;
+ for (const url_param_filter::FilterClassification& fc :
+ classification_list.classifications()) {
+ if (!fc.has_site()) {
+ WriteMetrics(
+ ClassificationListValidationResult::kClassificationMissingSite);
+ return false;
+ }
+
+ if (!fc.has_site_role()) {
+ WriteMetrics(
+ ClassificationListValidationResult::kClassificationMissingSiteRole);
+ return false;
+ }
+
+ if (fc.site_role() ==
+ url_param_filter::FilterClassification_SiteRole_SOURCE) {
+ source_classifications.push_back(fc);
+ }
+
+ if (fc.site_role() ==
+ url_param_filter::FilterClassification_SiteRole_DESTINATION) {
+ destination_classifications.push_back(fc);
+ }
+ }
+
+ WriteMetrics(ClassificationListValidationResult::kSuccessful);
+ return true;
+}
+
+base::FilePath
+UrlParamClassificationComponentInstallerPolicy::GetRelativeInstallDir() const {
+ return base::FilePath(FILE_PATH_LITERAL("UrlParamClassifications"));
+}
+
+void UrlParamClassificationComponentInstallerPolicy::GetHash(
+ std::vector<uint8_t>* hash) const {
+ hash->assign(std::begin(kUrlParamClassificationsPublicKeySHA256),
+ std::end(kUrlParamClassificationsPublicKeySHA256));
+}
+
+std::string UrlParamClassificationComponentInstallerPolicy::GetName() const {
+ return kUrlParamClassificationManifestName;
+}
+
+update_client::InstallerAttributes
+UrlParamClassificationComponentInstallerPolicy::GetInstallerAttributes() const {
+ return update_client::InstallerAttributes();
+}
+
+// static
+void UrlParamClassificationComponentInstallerPolicy::WriteComponentForTesting(
+ const base::FilePath& install_dir,
+ base::StringPiece contents) {
+ CHECK(base::WriteFile(GetInstalledPath(install_dir), contents));
+ GetConfigPathInstance() = GetInstalledPath(install_dir);
+}
+
+// static
+void UrlParamClassificationComponentInstallerPolicy::ResetForTesting() {
+ GetConfigPathInstance().reset();
+}
+
+void RegisterUrlParamClassificationComponent(ComponentUpdateService* cus) {
+ // Register the component even if feature isn't enabled so that when it is
+ // enabled in the future, the component is already installed.
+ VLOG(1) << "Registering Url Param Classifications component.";
+ auto installer = base::MakeRefCounted<ComponentInstaller>(
+ std::make_unique<UrlParamClassificationComponentInstallerPolicy>(
+ base::BindRepeating([](std::string raw_classifications) {
+ url_param_filter::ClassificationsLoader::GetInstance()
+ ->ReadClassifications(raw_classifications);
+ })));
+
+ installer->Register(
+ cus, base::BindOnce([]() {
+ if (GetConfigPathInstance().has_value()) {
+ absl::optional<std::string> file_contents =
+ LoadFileFromDisk(GetConfigPathInstance().value());
+ if (file_contents.has_value()) {
+ // Only read classifications after registration if
+ // needed for testing.
+ url_param_filter::ClassificationsLoader::GetInstance()
+ ->ReadClassifications(file_contents.value());
+ }
+ }
+ }));
+}
+
+} // namespace component_updater
diff --git a/chromium/components/component_updater/installer_policies/url_param_classification_component_installer.h b/chromium/components/component_updater/installer_policies/url_param_classification_component_installer.h
new file mode 100644
index 00000000000..871ea3645af
--- /dev/null
+++ b/chromium/components/component_updater/installer_policies/url_param_classification_component_installer.h
@@ -0,0 +1,91 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_COMPONENT_UPDATER_INSTALLER_POLICIES_URL_PARAM_CLASSIFICATION_COMPONENT_INSTALLER_H_
+#define COMPONENTS_COMPONENT_UPDATER_INSTALLER_POLICIES_URL_PARAM_CLASSIFICATION_COMPONENT_INSTALLER_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/values.h"
+#include "components/component_updater/component_installer.h"
+
+namespace component_updater {
+
+class ComponentUpdateService;
+
+using OnUrlParamClassificationComponentReady =
+ base::RepeatingCallback<void(std::string)>;
+
+class UrlParamClassificationComponentInstallerPolicy
+ : public ComponentInstallerPolicy {
+ public:
+ // The result of reading and validating the UrlParamFilterClassification list
+ // from Component Updater.
+ //
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
+ enum class ClassificationListValidationResult {
+ // No invalid classifications were found in the list.
+ kSuccessful = 0,
+ // The file wasn't present.
+ kMissingClassificationsFile = 1,
+ // Reading from the classifications file failed.
+ kReadingClassificationsFileFailed = 2,
+ // The raw classifications string was unabled to be parsed into the proto.
+ kParsingToProtoFailed = 3,
+ // Classification was ignored due to missing required site name.
+ kClassificationMissingSite = 4,
+ // Classification was ignored due to missing required site role.
+ kClassificationMissingSiteRole = 5,
+ kMaxValue = kClassificationMissingSiteRole,
+ };
+
+ explicit UrlParamClassificationComponentInstallerPolicy(
+ OnUrlParamClassificationComponentReady on_component_ready);
+ ~UrlParamClassificationComponentInstallerPolicy() override;
+
+ UrlParamClassificationComponentInstallerPolicy(
+ const UrlParamClassificationComponentInstallerPolicy&) = delete;
+ UrlParamClassificationComponentInstallerPolicy& operator=(
+ const UrlParamClassificationComponentInstallerPolicy&) = delete;
+
+ static void WriteComponentForTesting(const base::FilePath& install_dir,
+ base::StringPiece contents);
+ static void ResetForTesting();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(UrlParamClassificationComponentInstallerTest,
+ VerifyAttributes);
+
+ // The following methods override ComponentInstallerPolicy.
+ bool SupportsGroupPolicyEnabledComponentUpdates() const override;
+ bool RequiresNetworkEncryption() const override;
+ update_client::CrxInstaller::Result OnCustomInstall(
+ const base::Value& manifest,
+ const base::FilePath& install_dir) override;
+ void OnCustomUninstall() override;
+ bool VerifyInstallation(const base::Value& manifest,
+ const base::FilePath& install_dir) const override;
+ void ComponentReady(const base::Version& version,
+ const base::FilePath& install_dir,
+ base::Value manifest) override;
+ base::FilePath GetRelativeInstallDir() const override;
+ void GetHash(std::vector<uint8_t>* hash) const override;
+ std::string GetName() const override;
+ update_client::InstallerAttributes GetInstallerAttributes() const override;
+
+ static base::FilePath GetInstalledPath(const base::FilePath& base);
+ void MaybeFireCallback(
+ const absl::optional<std::string>& maybe_classifications);
+
+ OnUrlParamClassificationComponentReady on_component_ready_;
+};
+
+// Call once during startup to make the component update service aware of
+// the Url Param Classification component.
+void RegisterUrlParamClassificationComponent(ComponentUpdateService* cus);
+
+} // namespace component_updater
+
+#endif // COMPONENTS_COMPONENT_UPDATER_INSTALLER_POLICIES_URL_PARAM_CLASSIFICATION_COMPONENT_INSTALLER_H_
diff --git a/chromium/components/component_updater/installer_policies/url_param_classification_component_installer_unittest.cc b/chromium/components/component_updater/installer_policies/url_param_classification_component_installer_unittest.cc
new file mode 100644
index 00000000000..883c35fb08c
--- /dev/null
+++ b/chromium/components/component_updater/installer_policies/url_param_classification_component_installer_unittest.cc
@@ -0,0 +1,304 @@
+// Copyright 2022 The Chromium 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/component_updater/installer_policies/url_param_classification_component_installer.h"
+
+#include <memory>
+
+#include "base/check.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/version.h"
+#include "components/component_updater/mock_component_updater_service.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
+
+namespace component_updater {
+namespace {
+
+MATCHER_P(EqualsProto,
+ want,
+ "Matches an argument against an expected a proto Message.") {
+ return arg.SerializeAsString() == want.SerializeAsString();
+}
+
+class UrlParamClassificationComponentInstallerTest : public ::testing::Test {
+ public:
+ UrlParamClassificationComponentInstallerTest() {
+ CHECK(component_install_dir_.CreateUniqueTempDir());
+ CHECK(component_install_dir_.IsValid());
+ }
+
+ ~UrlParamClassificationComponentInstallerTest() override = default;
+
+ const std::string kClassificationListValidationResultHistogram =
+ "Navigation.UrlParamFilter.ClassificationListValidationResult";
+ const std::string kSourceSite = "source.xyz";
+ const std::string kDestinationSite = "destination.xyz";
+ const url_param_filter::FilterClassification_SiteRole kSourceSiteRole =
+ url_param_filter::FilterClassification_SiteRole_SOURCE;
+ const url_param_filter::FilterClassification_SiteRole kDestinationSiteRole =
+ url_param_filter::FilterClassification_SiteRole_DESTINATION;
+
+ protected:
+ using ClassificationListValidationResult =
+ UrlParamClassificationComponentInstallerPolicy::
+ ClassificationListValidationResult;
+
+ void SetComponentFileContents(base::StringPiece content) {
+ base::FilePath path =
+ component_install_dir_.GetPath().Append(FILE_PATH_LITERAL("list.pb"));
+ CHECK(base::WriteFile(path, content));
+ CHECK(base::PathExists(path));
+ }
+
+ const base::FilePath& path() { return component_install_dir_.GetPath(); }
+
+ private:
+ base::ScopedTempDir component_install_dir_;
+};
+
+class UrlParamClassificationComponentInstallerFeatureAgnosticTest
+ : public UrlParamClassificationComponentInstallerTest,
+ public ::testing::WithParamInterface<bool> {
+ public:
+ UrlParamClassificationComponentInstallerFeatureAgnosticTest() {
+ if (GetParam()) {
+ scoped_list_.InitAndEnableFeature(
+ url_param_filter::features::kIncognitoParamFilterEnabled);
+ } else {
+ scoped_list_.InitAndDisableFeature(
+ url_param_filter::features::kIncognitoParamFilterEnabled);
+ }
+ }
+
+ std::unique_ptr<component_updater::ComponentInstallerPolicy> policy =
+ std::make_unique<UrlParamClassificationComponentInstallerPolicy>(
+ base::DoNothing());
+
+ private:
+ base::test::ScopedFeatureList scoped_list_;
+};
+
+TEST_P(UrlParamClassificationComponentInstallerFeatureAgnosticTest,
+ ComponentRegistered) {
+ base::test::TaskEnvironment task_env;
+ auto service =
+ std::make_unique<component_updater::MockComponentUpdateService>();
+
+ EXPECT_CALL(*service, RegisterComponent(_)).Times(1);
+ RegisterUrlParamClassificationComponent(service.get());
+
+ task_env.RunUntilIdle();
+}
+
+TEST_P(UrlParamClassificationComponentInstallerFeatureAgnosticTest,
+ VerifyInstallation_RejectsMissingFile) {
+ base::HistogramTester histogram_tester;
+ histogram_tester.ExpectTotalCount(
+ kClassificationListValidationResultHistogram, 0);
+
+ // Don't write the classifications file
+ EXPECT_FALSE(policy->VerifyInstallation(base::Value(), path()));
+
+ histogram_tester.ExpectBucketCount(
+ kClassificationListValidationResultHistogram,
+ ClassificationListValidationResult::kMissingClassificationsFile, 1);
+}
+
+TEST_P(UrlParamClassificationComponentInstallerFeatureAgnosticTest,
+ VerifyInstallation_RejectsNotProtoFile) {
+ base::HistogramTester histogram_tester;
+ histogram_tester.ExpectTotalCount(
+ kClassificationListValidationResultHistogram, 0);
+
+ SetComponentFileContents("clearly not expected proto");
+ EXPECT_FALSE(policy->VerifyInstallation(base::Value(), path()));
+
+ histogram_tester.ExpectBucketCount(
+ kClassificationListValidationResultHistogram,
+ ClassificationListValidationResult::kParsingToProtoFailed, 1);
+}
+
+TEST_P(UrlParamClassificationComponentInstallerFeatureAgnosticTest,
+ VerifyInstallation_RejectsMissingSiteNameClassification) {
+ base::HistogramTester histogram_tester;
+ histogram_tester.ExpectTotalCount(
+ kClassificationListValidationResultHistogram, 0);
+
+ url_param_filter::FilterClassifications classifications =
+ url_param_filter::MakeClassificationsProtoFromMap(
+ {{"source1.xyz", {"plzblock1"}}},
+ {{"destination2.xyz", {"plzblock2"}}});
+ SetComponentFileContents(classifications.SerializeAsString());
+
+ // Accepts valid classifications.
+ EXPECT_TRUE(policy->VerifyInstallation(base::Value(), path()));
+ histogram_tester.ExpectBucketCount(
+ kClassificationListValidationResultHistogram,
+ ClassificationListValidationResult::kSuccessful, 1);
+
+ // Add classification with a missing required site name.
+ classifications.add_classifications()->set_site_role(kSourceSiteRole);
+ SetComponentFileContents(classifications.SerializeAsString());
+
+ // VerifyInstallation fails now that an invalid classification was added.
+ EXPECT_FALSE(policy->VerifyInstallation(base::Value(), path()));
+ histogram_tester.ExpectBucketCount(
+ kClassificationListValidationResultHistogram,
+ ClassificationListValidationResult::kClassificationMissingSite, 1);
+}
+
+TEST_P(UrlParamClassificationComponentInstallerFeatureAgnosticTest,
+ VerifyInstallation_RejectsMissingSiteRoleClassification) {
+ base::HistogramTester histogram_tester;
+ histogram_tester.ExpectTotalCount(
+ kClassificationListValidationResultHistogram, 0);
+
+ url_param_filter::FilterClassifications classifications =
+ url_param_filter::MakeClassificationsProtoFromMap(
+ {{"source1.xyz", {"plzblock1"}}},
+ {{"destination2.xyz", {"plzblock2"}}});
+ SetComponentFileContents(classifications.SerializeAsString());
+
+ // Accepts valid classifications.
+ EXPECT_TRUE(policy->VerifyInstallation(base::Value(), path()));
+ histogram_tester.ExpectBucketCount(
+ kClassificationListValidationResultHistogram,
+ ClassificationListValidationResult::kSuccessful, 1);
+
+ // Add classification with a missing required site role.
+ classifications.add_classifications()->set_site(kSourceSite);
+ SetComponentFileContents(classifications.SerializeAsString());
+
+ // VerifyInstallation fails now that an invalid classification was added.
+ EXPECT_FALSE(policy->VerifyInstallation(base::Value(), path()));
+ histogram_tester.ExpectBucketCount(
+ kClassificationListValidationResultHistogram,
+ ClassificationListValidationResult::kClassificationMissingSiteRole, 1);
+}
+
+TEST_P(UrlParamClassificationComponentInstallerFeatureAgnosticTest,
+ VerifyInstallation_AcceptsEmptyList) {
+ base::HistogramTester histogram_tester;
+ histogram_tester.ExpectTotalCount(
+ kClassificationListValidationResultHistogram, 0);
+
+ url_param_filter::FilterClassifications classifications =
+ url_param_filter::MakeClassificationsProtoFromMap({}, {});
+ SetComponentFileContents(classifications.SerializeAsString());
+
+ // Accepts valid classifications.
+ EXPECT_TRUE(policy->VerifyInstallation(base::Value(), path()));
+ histogram_tester.ExpectBucketCount(
+ kClassificationListValidationResultHistogram,
+ ClassificationListValidationResult::kSuccessful, 1);
+}
+
+TEST_P(UrlParamClassificationComponentInstallerFeatureAgnosticTest,
+ VerifyInstallation_AcceptsValidList) {
+ base::HistogramTester histogram_tester;
+ histogram_tester.ExpectTotalCount(
+ kClassificationListValidationResultHistogram, 0);
+
+ url_param_filter::FilterClassifications classifications =
+ url_param_filter::MakeClassificationsProtoFromMap(
+ {{"source1.xyz", {"plzblock1"}}},
+ {{"destination2.xyz", {"plzblock2"}}});
+ SetComponentFileContents(classifications.SerializeAsString());
+
+ // Accepts valid classifications.
+ EXPECT_TRUE(policy->VerifyInstallation(base::Value(), path()));
+ histogram_tester.ExpectBucketCount(
+ kClassificationListValidationResultHistogram,
+ ClassificationListValidationResult::kSuccessful, 1);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ /* no label */,
+ UrlParamClassificationComponentInstallerFeatureAgnosticTest,
+ ::testing::Bool());
+
+TEST_F(UrlParamClassificationComponentInstallerTest,
+ FeatureEnabled_ComponentReady_FiresCallback) {
+ base::test::TaskEnvironment task_env;
+ base::test::ScopedFeatureList scoped_list;
+
+ scoped_list.InitAndEnableFeatureWithParameters(
+ url_param_filter::features::kIncognitoParamFilterEnabled,
+ {{"classifications",
+ url_param_filter::
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{"f_source.xyz", {"f_block_1"}}},
+ {{"f_destination.xyz", {"f_block_2"}}})}});
+
+ // Provide a valid classification list to the file.
+ url_param_filter::FilterClassifications classifications =
+ url_param_filter::MakeClassificationsProtoFromMap(
+ {{"cu_source.xyz", {"plzblock1"}}},
+ {{"cu_destination.xyz", {"plzblock2"}}});
+ SetComponentFileContents(classifications.SerializeAsString());
+
+ base::RunLoop run_loop;
+ std::unique_ptr<component_updater::ComponentInstallerPolicy> policy =
+ std::make_unique<UrlParamClassificationComponentInstallerPolicy>(
+ base::BindLambdaForTesting([&](std::string raw_classifications) {
+ EXPECT_EQ(raw_classifications, classifications.SerializeAsString());
+ run_loop.Quit();
+ }));
+
+ ASSERT_TRUE(policy->VerifyInstallation(base::Value(), path()));
+ // The callback to set the classifications on the ClassificationsLoader should
+ // be called.
+ policy->ComponentReady(base::Version(), path(), base::Value());
+ run_loop.Run();
+}
+
+TEST_F(UrlParamClassificationComponentInstallerTest,
+ FeatureDisabled_ComponentReady_DoesntFireCallback) {
+ base::test::TaskEnvironment task_env;
+ base::test::ScopedFeatureList scoped_list;
+ scoped_list.InitAndDisableFeature(
+ url_param_filter::features::kIncognitoParamFilterEnabled);
+
+ base::RunLoop run_loop;
+ std::unique_ptr<component_updater::ComponentInstallerPolicy> policy =
+ std::make_unique<UrlParamClassificationComponentInstallerPolicy>(
+ base::BindLambdaForTesting([&](std::string raw_classifications) {
+ ASSERT_TRUE(false);
+ run_loop.Quit();
+ }));
+
+ // Provide a valid classification list.
+ url_param_filter::FilterClassifications classifications =
+ url_param_filter::MakeClassificationsProtoFromMap(
+ {{"cu_source.xyz", {"plzblock1"}}},
+ {{"cu_destination.xyz", {"plzblock2"}}});
+ SetComponentFileContents(classifications.SerializeAsString());
+ ASSERT_TRUE(policy->VerifyInstallation(base::Value(), path()));
+
+ // The callback to set the classifications on the ClassificationsLoader should
+ // never be called.
+ policy->ComponentReady(base::Version(), path(), base::Value());
+ run_loop.RunUntilIdle();
+}
+
+} // namespace
+} // namespace component_updater
diff --git a/chromium/components/components_chromium_strings.grd b/chromium/components/components_chromium_strings.grd
index 1dddc8586f3..e9cd11a9bde 100644
--- a/chromium/components/components_chromium_strings.grd
+++ b/chromium/components/components_chromium_strings.grd
@@ -223,8 +223,7 @@
</message>
</if>
<!-- The ChromeOS version of this string is defined in //components/error_page_strings.grdp. -->
- <!-- TODO(crbug.com/1307455): Remove ` and not chromeos_ash and not chromeos_lacros` once fixed. -->
- <if expr="is_linux and not chromeos_ash and not chromeos_lacros">
+ <if expr="is_linux">
<message name="IDS_ERRORPAGES_SUGGESTION_PROXY_DISABLE_PLATFORM" desc="Linux instructions for disabling use of a proxy server.">
Go to
the Chromium menu &gt;
diff --git a/chromium/components/components_google_chrome_strings.grd b/chromium/components/components_google_chrome_strings.grd
index 7e287e10094..768314e9577 100644
--- a/chromium/components/components_google_chrome_strings.grd
+++ b/chromium/components/components_google_chrome_strings.grd
@@ -230,8 +230,7 @@
</message>
</if>
<!-- The ChromeOS version of this string is defined in //components/error_page_strings.grdp. -->
- <!-- TODO(crbug.com/1307455): Remove ` and not chromeos_ash and not chromeos_lacros` once fixed. -->
- <if expr="is_linux and not chromeos_ash and not chromeos_lacros">
+ <if expr="is_linux">
<message name="IDS_ERRORPAGES_SUGGESTION_PROXY_DISABLE_PLATFORM" desc="Linux instructions for disabling use of a proxy server.">
Go to
the Chrome menu &gt;
diff --git a/chromium/components/components_strings.grd b/chromium/components/components_strings.grd
index 9665c685374..1a7e1748a3f 100644
--- a/chromium/components/components_strings.grd
+++ b/chromium/components/components_strings.grd
@@ -333,6 +333,7 @@
<part file="translate_strings.grdp" />
<part file="tab_groups_strings.grdp" />
<part file="undo_strings.grdp" />
+ <part file="user_education_strings.grdp" />
<part file="version_ui_strings.grdp" />
<part file="webapps_strings.grdp" />
diff --git a/chromium/components/constrained_window/BUILD.gn b/chromium/components/constrained_window/BUILD.gn
index bca689e3981..392c8e7ad50 100644
--- a/chromium/components/constrained_window/BUILD.gn
+++ b/chromium/components/constrained_window/BUILD.gn
@@ -17,6 +17,7 @@ static_library("constrained_window") {
]
deps = [
+ "//components/guest_view/browser",
"//components/web_modal",
"//content/public/browser",
"//ui/display",
diff --git a/chromium/components/constrained_window/DEPS b/chromium/components/constrained_window/DEPS
index 8a179bd47d9..a9c61b7996b 100644
--- a/chromium/components/constrained_window/DEPS
+++ b/chromium/components/constrained_window/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+components/guest_view/browser",
"+components/web_modal",
"+content/public/browser",
"+ui/aura",
diff --git a/chromium/components/constrained_window/constrained_window_views.cc b/chromium/components/constrained_window/constrained_window_views.cc
index d662221bcc6..df70e533d77 100644
--- a/chromium/components/constrained_window/constrained_window_views.cc
+++ b/chromium/components/constrained_window/constrained_window_views.cc
@@ -12,6 +12,7 @@
#include "base/no_destructor.h"
#include "build/build_config.h"
#include "components/constrained_window/constrained_window_views_client.h"
+#include "components/guest_view/browser/guest_view_base.h"
#include "components/web_modal/web_contents_modal_dialog_host.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
@@ -174,7 +175,14 @@ void UpdateWidgetModalDialogPosition(views::Widget* widget,
content::WebContents* GetTopLevelWebContents(
content::WebContents* initiator_web_contents) {
- return initiator_web_contents->GetResponsibleWebContents();
+ // TODO(mcnee): While calling both `GetResponsibleWebContents` and
+ // `GetTopLevelWebContents` appears redundant, there appears to still be cases
+ // where users of guest view are not initializing the guest WebContents
+ // properly, causing GetResponsibleWebContents to break. See
+ // https://crbug.com/1325850
+ // The order of composing these methods is arbitrary.
+ return guest_view::GuestViewBase::GetTopLevelWebContents(
+ initiator_web_contents->GetResponsibleWebContents());
}
views::Widget* ShowWebModalDialogViews(
@@ -259,4 +267,13 @@ void ShowBrowserModal(std::unique_ptr<ui::DialogModel> dialog_model,
->Show();
}
+void ShowWebModal(std::unique_ptr<ui::DialogModel> dialog_model,
+ content::WebContents* web_contents) {
+ constrained_window::ShowWebModalDialogViews(
+ views::BubbleDialogModelHost::CreateModal(std::move(dialog_model),
+ ui::MODAL_TYPE_CHILD)
+ .release(),
+ web_contents);
+}
+
} // namespace constrained_window
diff --git a/chromium/components/constrained_window/constrained_window_views.h b/chromium/components/constrained_window/constrained_window_views.h
index b3f1732ebc2..c24827bfca4 100644
--- a/chromium/components/constrained_window/constrained_window_views.h
+++ b/chromium/components/constrained_window/constrained_window_views.h
@@ -92,6 +92,10 @@ views::Widget* CreateBrowserModalDialogViews(views::DialogDelegate* dialog,
void ShowBrowserModal(std::unique_ptr<ui::DialogModel> dialog_model,
gfx::NativeWindow parent);
+// Shows a web/tab-modal dialog based on `dialog_model`.
+void ShowWebModal(std::unique_ptr<ui::DialogModel> dialog_model,
+ content::WebContents* web_contents);
+
} // namespace constrained_window
#endif // COMPONENTS_CONSTRAINED_WINDOW_CONSTRAINED_WINDOW_VIEWS_H_
diff --git a/chromium/components/content_capture/android/BUILD.gn b/chromium/components/content_capture/android/BUILD.gn
index a2f9cd2eba2..f9e24f74371 100644
--- a/chromium/components/content_capture/android/BUILD.gn
+++ b/chromium/components/content_capture/android/BUILD.gn
@@ -20,6 +20,8 @@ source_set("android") {
android_library("java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
diff --git a/chromium/components/content_capture/android/junit/BUILD.gn b/chromium/components/content_capture/android/junit/BUILD.gn
index 3c567a74edc..17337b0ea09 100644
--- a/chromium/components/content_capture/android/junit/BUILD.gn
+++ b/chromium/components/content_capture/android/junit/BUILD.gn
@@ -15,7 +15,6 @@ java_library("components_content_capture_junit_tests") {
"src/org/chromium/components/content_capture/UrlAllowlistTest.java",
]
deps = [
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//components/content_capture/android:java",
diff --git a/chromium/components/content_capture/android/test_support/BUILD.gn b/chromium/components/content_capture/android/test_support/BUILD.gn
index 1e6eec4fd04..94b0cca29a6 100644
--- a/chromium/components/content_capture/android/test_support/BUILD.gn
+++ b/chromium/components/content_capture/android/test_support/BUILD.gn
@@ -17,9 +17,9 @@ source_set("test_support") {
android_library("java") {
deps = [
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
sources = [ "java/src/org/chromium/components/content_capture/ContentCaptureTestSupport.java" ]
diff --git a/chromium/components/content_capture/android/test_support/content_capture_test_support_android.cc b/chromium/components/content_capture/android/test_support/content_capture_test_support_android.cc
index ab7601e5cd7..33c00009d0f 100644
--- a/chromium/components/content_capture/android/test_support/content_capture_test_support_android.cc
+++ b/chromium/components/content_capture/android/test_support/content_capture_test_support_android.cc
@@ -69,8 +69,8 @@ static void JNI_ContentCaptureTestSupport_SimulateDidUpdateFaviconURL(
ToType(*icon.FindKey("type")->GetIfString()), sizes));
}
CHECK(!favicon_urls.empty());
- provider->NotifyFaviconURLUpdatedForTesting(web_contents->GetMainFrame(),
- favicon_urls);
+ provider->NotifyFaviconURLUpdatedForTesting(
+ web_contents->GetPrimaryMainFrame(), favicon_urls);
}
} // namespace content_capture
diff --git a/chromium/components/content_capture/browser/content_capture_receiver.cc b/chromium/components/content_capture/browser/content_capture_receiver.cc
index 7c82d61f793..3a31638424c 100644
--- a/chromium/components/content_capture/browser/content_capture_receiver.cc
+++ b/chromium/components/content_capture/browser/content_capture_receiver.cc
@@ -222,12 +222,11 @@ void ContentCaptureReceiver::UpdateFaviconURL(
}
void ContentCaptureReceiver::RetrieveFaviconURL() {
- if (!rfh()->IsActive() || rfh()->GetMainFrame() != rfh() ||
+ if (!rfh()->IsActive() || !rfh()->IsInPrimaryMainFrame() ||
disable_get_favicon_from_web_contents_for_testing()) {
frame_content_capture_data_.favicon = std::string();
} else {
- frame_content_capture_data_.favicon = ToJSON(
- content::WebContents::FromRenderFrameHost(rfh())->GetFaviconURLs());
+ frame_content_capture_data_.favicon = ToJSON(rfh()->FaviconURLs());
}
}
diff --git a/chromium/components/content_capture/browser/content_capture_receiver_browsertest.cc b/chromium/components/content_capture/browser/content_capture_receiver_browsertest.cc
index c640738dc0d..fa99f1e3253 100644
--- a/chromium/components/content_capture/browser/content_capture_receiver_browsertest.cc
+++ b/chromium/components/content_capture/browser/content_capture_receiver_browsertest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/json/json_reader.h"
#include "components/content_capture/browser/content_capture_test_helper.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
@@ -22,7 +23,7 @@ class ContentCaptureBrowserTest : public content::ContentBrowserTest {
// Navigate to the initial page.
const GURL main_frame_url = embedded_test_server()->GetURL(kMainFrameUrl);
ASSERT_TRUE(NavigateToURL(web_contents(), main_frame_url));
- main_frame_ = web_contents()->GetMainFrame();
+ main_frame_ = web_contents()->GetPrimaryMainFrame();
EXPECT_NE(nullptr, main_frame_);
// Create a provider and add a consumer.
@@ -109,4 +110,56 @@ IN_PROC_BROWSER_TEST_F(ContentCaptureBrowserTest,
consumer()->captured_data());
}
+IN_PROC_BROWSER_TEST_F(ContentCaptureBrowserTest,
+ DoNotUpdateFaviconURLsInFencedFrame) {
+ // Simulate to capture the content from main frame.
+ main_frame_sender()->DidCaptureContent(helper()->test_data(),
+ true /* first_data */);
+
+ // Simulate to capture the content from fenced frame.
+ fenced_frame_sender()->DidCaptureContent(helper()->test_data2(),
+ true /* first_data */);
+
+ // Insert the favicon dynamically to the primary main frame.
+ ASSERT_TRUE(ExecJs(web_contents(),
+ "let l = document.createElement('link');"
+ "l.rel='icon'; l.type='image/png'; "
+ "l.href='https://example.com/favicon.ico';"
+ "document.head.appendChild(l)"));
+
+ std::string expected_json =
+ R"JSON([{
+ "type":"favicon",
+ "url":"https://example.com/favicon.ico"
+ }])JSON";
+ absl::optional<base::Value> expected = base::JSONReader::Read(expected_json);
+
+ // Verify that the captured data's favicon url from the primary main frame is
+ // valid.
+ auto* main_frame_receiver =
+ provider()->ContentCaptureReceiverForFrameForTesting(main_frame_);
+ absl::optional<base::Value> main_frame_actual = base::JSONReader::Read(
+ main_frame_receiver->GetContentCaptureFrame().favicon);
+ EXPECT_TRUE(main_frame_actual);
+ EXPECT_EQ(expected, main_frame_actual);
+
+ // Verify that the captured data's favicon url from the fenced frame is empty.
+ auto* fenced_frame_receiver =
+ provider()->ContentCaptureReceiverForFrameForTesting(fenced_frame_);
+ EXPECT_FALSE(base::JSONReader::Read(
+ fenced_frame_receiver->GetContentCaptureFrame().favicon));
+
+ // Insert the favicon dynamically to the fenced frame.
+ ASSERT_TRUE(ExecJs(fenced_frame_.get(),
+ "let l = document.createElement('link');"
+ "l.rel='icon'; l.type='image/png'; "
+ "l.href='https://example.com/favicon.ico';"
+ "document.head.appendChild(l)"));
+
+ // Verify that the captured data's favicon url from the fenced frame is still
+ // empty.
+ EXPECT_FALSE(base::JSONReader::Read(
+ fenced_frame_receiver->GetContentCaptureFrame().favicon));
+}
+
} // namespace content_capture
diff --git a/chromium/components/content_capture/browser/content_capture_receiver_test.cc b/chromium/components/content_capture/browser/content_capture_receiver_test.cc
index 9f934df6dde..62518f96c2e 100644
--- a/chromium/components/content_capture/browser/content_capture_receiver_test.cc
+++ b/chromium/components/content_capture/browser/content_capture_receiver_test.cc
@@ -45,7 +45,7 @@ class ContentCaptureReceiverTest : public content::RenderViewHostTestHarness,
// This needed to keep the WebContentsObserverConsistencyChecker checks
// happy for when AppendChild is called.
NavigateAndCommit(GURL(kMainFrameUrl));
- main_frame_ = web_contents()->GetMainFrame();
+ main_frame_ = web_contents()->GetPrimaryMainFrame();
EXPECT_TRUE(main_frame_);
main_frame_sender_ = std::make_unique<FakeContentCaptureSender>();
@@ -57,7 +57,7 @@ class ContentCaptureReceiverTest : public content::RenderViewHostTestHarness,
void NavigateMainFrame(const GURL& url) {
consumer()->Reset();
NavigateAndCommit(url);
- main_frame_ = web_contents()->GetMainFrame();
+ main_frame_ = web_contents()->GetPrimaryMainFrame();
}
void NavigateMainFrameSameDocument() {
@@ -302,7 +302,7 @@ TEST_P(ContentCaptureReceiverTest, ChildFrameDidCaptureContent) {
// This test is for issue crbug.com/995121 .
TEST_P(ContentCaptureReceiverTest, RenderFrameHostGone) {
auto* receiver = provider()->ContentCaptureReceiverForFrameForTesting(
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
// No good way to simulate crbug.com/995121, just set rfh_ to nullptr in
// ContentCaptureReceiver, so content::WebContents::FromRenderFrameHost()
// won't return WebContents.
@@ -316,7 +316,7 @@ TEST_P(ContentCaptureReceiverTest, RenderFrameHostGone) {
TEST_P(ContentCaptureReceiverTest, TitleUpdateTaskDelay) {
auto* receiver = provider()->ContentCaptureReceiverForFrameForTesting(
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
// Uses TestMockTimeTaskRunner to check the task state.
receiver->title_update_task_runner_ = task_runner;
@@ -550,7 +550,7 @@ class ContentCaptureReceiverMultipleFrameTest
// This needed to keep the WebContentsObserverConsistencyChecker checks
// happy for when AppendChild is called.
NavigateAndCommit(GURL("about:blank"));
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("child");
helper_.CreateProviderAndConsumer(web_contents());
diff --git a/chromium/components/content_capture/browser/onscreen_content_provider.cc b/chromium/components/content_capture/browser/onscreen_content_provider.cc
index 27d64fb9944..b2da4264af7 100644
--- a/chromium/components/content_capture/browser/onscreen_content_provider.cc
+++ b/chromium/components/content_capture/browser/onscreen_content_provider.cc
@@ -144,8 +144,8 @@ void OnscreenContentProvider::ReadyToCommitNavigation(
void OnscreenContentProvider::TitleWasSet(content::NavigationEntry* entry) {
// Set the title to the mainframe.
- if (auto* receiver =
- ContentCaptureReceiverForFrame(web_contents()->GetMainFrame())) {
+ if (auto* receiver = ContentCaptureReceiverForFrame(
+ web_contents()->GetPrimaryMainFrame())) {
// To match what the user sees, intentionally get the title from WebContents
// instead of NavigationEntry, though they might be same.
receiver->SetTitle(web_contents()->GetTitle());
@@ -290,8 +290,8 @@ bool OnscreenContentProvider::BuildContentCaptureSessionLastSeen(
bool OnscreenContentProvider::BuildContentCaptureSessionForMainFrame(
ContentCaptureSession* session) {
- if (auto* receiver =
- ContentCaptureReceiverForFrame(web_contents()->GetMainFrame())) {
+ if (auto* receiver = ContentCaptureReceiverForFrame(
+ web_contents()->GetPrimaryMainFrame())) {
session->push_back(receiver->GetContentCaptureFrame());
return true;
}
diff --git a/chromium/components/content_creation/notes/android/BUILD.gn b/chromium/components/content_creation/notes/android/BUILD.gn
index 1d02a2aa69e..e2cac783db0 100644
--- a/chromium/components/content_creation/notes/android/BUILD.gn
+++ b/chromium/components/content_creation/notes/android/BUILD.gn
@@ -25,6 +25,8 @@ android_library("java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_core_core_java",
]
diff --git a/chromium/components/content_creation/reactions/android/BUILD.gn b/chromium/components/content_creation/reactions/android/BUILD.gn
index 97ae80c1c8c..11959062d55 100644
--- a/chromium/components/content_creation/reactions/android/BUILD.gn
+++ b/chromium/components/content_creation/reactions/android/BUILD.gn
@@ -16,6 +16,8 @@ android_library("java") {
deps = [
":reaction_types_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_core_core_java",
]
diff --git a/chromium/components/content_settings/android/BUILD.gn b/chromium/components/content_settings/android/BUILD.gn
index ae44ce48f05..933839606bb 100644
--- a/chromium/components/content_settings/android/BUILD.gn
+++ b/chromium/components/content_settings/android/BUILD.gn
@@ -19,7 +19,8 @@ android_library("java") {
]
deps = [
":content_settings_enums_java",
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
diff --git a/chromium/components/content_settings/browser/page_specific_content_settings.h b/chromium/components/content_settings/browser/page_specific_content_settings.h
index c8f0dc4e8e9..186bd3461e1 100644
--- a/chromium/components/content_settings/browser/page_specific_content_settings.h
+++ b/chromium/components/content_settings/browser/page_specific_content_settings.h
@@ -109,10 +109,6 @@ class PageSpecificContentSettings
content::RenderFrameHost* rfh,
RendererContentSettingRules* rules) = 0;
- virtual ContentSetting GetEmbargoSetting(
- const GURL& request_origin,
- ContentSettingsType permission) = 0;
-
// Gets any additional file system types which should be used when
// constructing a browsing_data::FileSystemHelper.
virtual std::vector<storage::FileSystemType>
diff --git a/chromium/components/content_settings/browser/page_specific_content_settings_unittest.cc b/chromium/components/content_settings/browser/page_specific_content_settings_unittest.cc
index 4e94ffd2a1c..30760b83c11 100644
--- a/chromium/components/content_settings/browser/page_specific_content_settings_unittest.cc
+++ b/chromium/components/content_settings/browser/page_specific_content_settings_unittest.cc
@@ -116,7 +116,8 @@ class PageSpecificContentSettingsTest
TEST_F(PageSpecificContentSettingsTest, BlockedContent) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
// Check that after initializing, nothing is blocked.
#if !BUILDFLAG(IS_ANDROID)
@@ -140,14 +141,14 @@ TEST_F(PageSpecificContentSettingsTest, BlockedContent) {
origin, "A=B", base::Time::Now(), absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */));
ASSERT_TRUE(cookie1);
- GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(),
+ GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
{*cookie1},
false});
- content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ content_settings = PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
#if !BUILDFLAG(IS_ANDROID)
content_settings->OnContentBlocked(ContentSettingsType::IMAGES);
#endif
@@ -176,7 +177,7 @@ TEST_F(PageSpecificContentSettingsTest, BlockedContent) {
content_settings->IsContentBlocked(ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_TRUE(content_settings->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
- GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(),
+ GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
@@ -188,7 +189,7 @@ TEST_F(PageSpecificContentSettingsTest, BlockedContent) {
origin, "C=D", base::Time::Now(), absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */));
ASSERT_TRUE(cookie2);
- GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(),
+ GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
@@ -206,25 +207,25 @@ TEST_F(PageSpecificContentSettingsTest, BlockedContent) {
GetHandle()->OnServiceWorkerAccessed(
simulator->GetNavigationHandle(), GURL("http://google.com"),
content::AllowServiceWorkerResult::FromPolicy(true, false));
- content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ content_settings = PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
simulator->Commit();
- content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ content_settings = PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
// Block a javascript when page starts to start ServiceWorker.
GetHandle()->OnServiceWorkerAccessed(
- web_contents()->GetMainFrame(), GURL("http://google.com"),
+ web_contents()->GetPrimaryMainFrame(), GURL("http://google.com"),
content::AllowServiceWorkerResult::FromPolicy(true, false));
EXPECT_TRUE(
content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
// Reset blocked content settings.
NavigateAndCommit(GURL("http://google.com"));
- content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ content_settings = PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
#if !BUILDFLAG(IS_ANDROID)
EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::IMAGES));
#endif
@@ -242,7 +243,8 @@ TEST_F(PageSpecificContentSettingsTest, BlockedContent) {
TEST_F(PageSpecificContentSettingsTest, BlockedFileSystems) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
// Access a file system.
content_settings->OnStorageAccessed(StorageType::FILE_SYSTEM,
@@ -259,7 +261,8 @@ TEST_F(PageSpecificContentSettingsTest, BlockedFileSystems) {
TEST_F(PageSpecificContentSettingsTest, AllowedContent) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
// Test default settings.
ASSERT_FALSE(content_settings->IsContentAllowed(ContentSettingsType::IMAGES));
@@ -278,7 +281,7 @@ TEST_F(PageSpecificContentSettingsTest, AllowedContent) {
origin, "A=B", base::Time::Now(), absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */));
ASSERT_TRUE(cookie1);
- GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(),
+ GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
@@ -293,7 +296,7 @@ TEST_F(PageSpecificContentSettingsTest, AllowedContent) {
origin, "C=D", base::Time::Now(), absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */));
ASSERT_TRUE(cookie2);
- GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(),
+ GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
@@ -306,14 +309,15 @@ TEST_F(PageSpecificContentSettingsTest, AllowedContent) {
TEST_F(PageSpecificContentSettingsTest, EmptyCookieList) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
GetHandle()->OnCookiesAccessed(
- web_contents()->GetMainFrame(),
+ web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead, GURL("http://google.com"),
GURL("http://google.com"), net::CookieList(), true});
ASSERT_FALSE(
@@ -325,7 +329,8 @@ TEST_F(PageSpecificContentSettingsTest, EmptyCookieList) {
TEST_F(PageSpecificContentSettingsTest, SiteDataObserver) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
MockSiteDataObserver mock_observer(web_contents());
EXPECT_CALL(mock_observer, OnSiteDataAccessed()).Times(6);
@@ -335,7 +340,7 @@ TEST_F(PageSpecificContentSettingsTest, SiteDataObserver) {
origin, "A=B", base::Time::Now(), absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */));
ASSERT_TRUE(cookie);
- GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(),
+ GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
@@ -352,7 +357,7 @@ TEST_F(PageSpecificContentSettingsTest, SiteDataObserver) {
cookie_list.push_back(*other_cookie);
GetHandle()->OnCookiesAccessed(
- web_contents()->GetMainFrame(),
+ web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead, GURL("http://google.com"),
GURL("http://google.com"), cookie_list, blocked_by_policy});
content_settings->OnStorageAccessed(
@@ -368,13 +373,14 @@ TEST_F(PageSpecificContentSettingsTest, SiteDataObserver) {
TEST_F(PageSpecificContentSettingsTest, LocalSharedObjectsContainer) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
bool blocked_by_policy = false;
auto cookie = net::CanonicalCookie::Create(
GURL("http://google.com"), "k=v", base::Time::Now(),
absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */);
- GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(),
+ GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead,
GURL("http://google.com"),
GURL("http://google.com"),
@@ -423,7 +429,8 @@ TEST_F(PageSpecificContentSettingsTest, LocalSharedObjectsContainer) {
TEST_F(PageSpecificContentSettingsTest, LocalSharedObjectsContainerCookie) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
bool blocked_by_policy = false;
auto cookie1 = net::CanonicalCookie::Create(
GURL("http://google.com"), "k1=v", base::Time::Now(),
@@ -441,7 +448,7 @@ TEST_F(PageSpecificContentSettingsTest, LocalSharedObjectsContainerCookie) {
GURL("http://www.google.com"), "k4=v; Domain=.www.google.com",
base::Time::Now(), absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */);
- GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(),
+ GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead,
GURL("http://www.google.com"),
GURL("http://www.google.com"),
@@ -452,7 +459,7 @@ TEST_F(PageSpecificContentSettingsTest, LocalSharedObjectsContainerCookie) {
GURL("https://www.google.com"), "k5=v", base::Time::Now(),
absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */);
- GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(),
+ GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead,
GURL("https://www.google.com"),
GURL("https://www.google.com"),
@@ -470,7 +477,8 @@ TEST_F(PageSpecificContentSettingsTest,
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
// First trigger OnContentBlocked.
EXPECT_FALSE(content_settings->IsContentBlocked(
@@ -567,11 +575,11 @@ TEST_F(PageSpecificContentSettingsWithPrerenderTest, SiteDataAccessed) {
EXPECT_CALL(mock_observer, OnSiteDataAccessed()).Times(1);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateRendererInitiated(
- prerender_url, web_contents()->GetMainFrame());
+ prerender_url, web_contents()->GetPrimaryMainFrame());
// TODO(https://crbug.com/1181763): Investigate how default referrer value
// is set and update here accordingly.
navigation->SetReferrer(blink::mojom::Referrer::New(
- web_contents()->GetMainFrame()->GetLastCommittedURL(),
+ web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
navigation->Commit();
}
@@ -624,9 +632,9 @@ TEST_F(PageSpecificContentSettingsWithPrerenderTest,
.Times(1);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateRendererInitiated(
- prerender_url, web_contents()->GetMainFrame());
+ prerender_url, web_contents()->GetPrimaryMainFrame());
navigation->SetReferrer(blink::mojom::Referrer::New(
- web_contents()->GetMainFrame()->GetLastCommittedURL(),
+ web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
navigation->Commit();
}
@@ -648,7 +656,7 @@ TEST_F(PageSpecificContentSettingsWithPrerenderTest,
EXPECT_CALL(*mock_delegate, UpdateLocationBar()).Times(1);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateRendererInitiated(
- prerender_url, web_contents()->GetMainFrame());
+ prerender_url, web_contents()->GetPrimaryMainFrame());
navigation->Commit();
}
@@ -673,19 +681,19 @@ TEST_F(PageSpecificContentSettingsWithPrerenderTest, ContentAllowedAndBlocked) {
.Times(1);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateRendererInitiated(
- prerender_url, web_contents()->GetMainFrame());
+ prerender_url, web_contents()->GetPrimaryMainFrame());
// TODO(https://crbug.com/1181763): Investigate how default referrer value is
// set and update here accordingly.
navigation->SetReferrer(blink::mojom::Referrer::New(
- web_contents()->GetMainFrame()->GetLastCommittedURL(),
+ web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
navigation->Commit();
}
TEST_F(PageSpecificContentSettingsTest, Topics) {
NavigateAndCommit(GURL("http://google.com"));
- PageSpecificContentSettings* pscs =
- PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+ PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
+ web_contents()->GetPrimaryMainFrame());
EXPECT_FALSE(pscs->HasAccessedTopics());
EXPECT_THAT(pscs->GetAccessedTopics(), testing::IsEmpty());
@@ -709,11 +717,12 @@ class PageSpecificContentSettingsWithFencedFrameTest
content::RenderFrameHost* CreateFencedFrame(const GURL& url) {
content::RenderFrameHost* fenced_frame_root =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(
+ web_contents()->GetPrimaryMainFrame())
->AppendFencedFrame();
std::unique_ptr<content::NavigationSimulator> navigation_simulator =
- content::NavigationSimulator::CreateForFencedFrame(url,
- fenced_frame_root);
+ content::NavigationSimulator::CreateRendererInitiated(
+ url, fenced_frame_root);
navigation_simulator->Commit();
return navigation_simulator->GetFinalRenderFrameHost();
}
diff --git a/chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.cc b/chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.cc
index 83fca553707..cd740e9f04d 100644
--- a/chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.cc
+++ b/chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.cc
@@ -31,12 +31,6 @@ void TestPageSpecificContentSettingsDelegate::
SetDefaultRendererContentSettingRules(content::RenderFrameHost* rfh,
RendererContentSettingRules* rules) {}
-ContentSetting TestPageSpecificContentSettingsDelegate::GetEmbargoSetting(
- const GURL& request_origin,
- ContentSettingsType permission) {
- return ContentSetting::CONTENT_SETTING_ASK;
-}
-
std::vector<storage::FileSystemType>
TestPageSpecificContentSettingsDelegate::GetAdditionalFileSystemTypes() {
return {};
diff --git a/chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.h b/chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.h
index f83888eefd5..d4d7282a71b 100644
--- a/chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.h
+++ b/chromium/components/content_settings/browser/test_page_specific_content_settings_delegate.h
@@ -25,8 +25,6 @@ class TestPageSpecificContentSettingsDelegate
void SetDefaultRendererContentSettingRules(
content::RenderFrameHost* rfh,
RendererContentSettingRules* rules) 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/components/content_settings/core/browser/content_settings_default_provider.cc b/chromium/components/content_settings/core/browser/content_settings_default_provider.cc
index 1f6642baaf5..3a0c37be577 100644
--- a/chromium/components/content_settings/core/browser/content_settings_default_provider.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_default_provider.cc
@@ -55,10 +55,10 @@ const char kObsoleteFontAccessDefaultPref[] =
#endif // !BUILDFLAG(IS_ANDROID)
#endif // !BUILDFLAG(IS_IOS)
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
// This setting was moved and should be migrated on profile startup.
const char kDeprecatedEnableDRM[] = "settings.privacy.drm_enabled";
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
+#endif // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
ContentSetting GetDefaultValue(const WebsiteSettingsInfo* info) {
const base::Value& initial_default = info->initial_default_value();
@@ -138,9 +138,9 @@ void DefaultProvider::RegisterProfilePrefs(
#endif // !BUILDFLAG(IS_ANDROID)
#endif // !BUILDFLAG(IS_IOS)
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
registry->RegisterBooleanPref(kDeprecatedEnableDRM, true);
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
+#endif // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
}
DefaultProvider::DefaultProvider(PrefService* prefs, bool off_the_record)
@@ -423,7 +423,7 @@ void DefaultProvider::DiscardOrMigrateObsoletePreferences() {
#endif // !BUILDFLAG(IS_ANDROID)
#endif // !BUILDFLAG(IS_IOS)
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
// TODO(crbug.com/1191642): Remove this migration logic in M100.
WebsiteSettingsRegistry* website_settings =
WebsiteSettingsRegistry::GetInstance();
@@ -437,7 +437,7 @@ void DefaultProvider::DiscardOrMigrateObsoletePreferences() {
: CONTENT_SETTING_BLOCK);
}
prefs_->ClearPref(kDeprecatedEnableDRM);
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
+#endif // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
}
} // namespace content_settings
diff --git a/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc b/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc
index cb5c81c7b28..1d9ecd0f6ba 100644
--- a/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc
@@ -55,6 +55,10 @@ constexpr PrefsForManagedContentSettingsMapEntry
ContentSettingsType::JAVASCRIPT, CONTENT_SETTING_ALLOW},
{prefs::kManagedJavaScriptBlockedForUrls,
ContentSettingsType::JAVASCRIPT, CONTENT_SETTING_BLOCK},
+ {prefs::kManagedClipboardAllowedForUrls,
+ ContentSettingsType::CLIPBOARD_READ_WRITE, CONTENT_SETTING_ALLOW},
+ {prefs::kManagedClipboardBlockedForUrls,
+ ContentSettingsType::CLIPBOARD_READ_WRITE, CONTENT_SETTING_BLOCK},
{prefs::kManagedNotificationsAllowedForUrls,
ContentSettingsType::NOTIFICATIONS, CONTENT_SETTING_ALLOW},
{prefs::kManagedNotificationsBlockedForUrls,
@@ -107,6 +111,8 @@ constexpr PrefsForManagedContentSettingsMapEntry
constexpr const char* kManagedPrefs[] = {
prefs::kManagedAutoSelectCertificateForUrls,
+ prefs::kManagedClipboardAllowedForUrls,
+ prefs::kManagedClipboardBlockedForUrls,
prefs::kManagedCookiesAllowedForUrls,
prefs::kManagedCookiesBlockedForUrls,
prefs::kManagedCookiesSessionOnlyForUrls,
@@ -152,6 +158,7 @@ constexpr const char* kManagedPrefs[] = {
// is managed any user defined exceptions (patterns) for this type are ignored.
constexpr const char* kManagedDefaultPrefs[] = {
prefs::kManagedDefaultAdsSetting,
+ prefs::kManagedDefaultClipboardSetting,
prefs::kManagedDefaultCookiesSetting,
prefs::kManagedDefaultFileSystemReadGuardSetting,
prefs::kManagedDefaultFileSystemWriteGuardSetting,
@@ -188,6 +195,8 @@ struct PolicyProvider::PrefsForManagedDefaultMapEntry {
const PolicyProvider::PrefsForManagedDefaultMapEntry
PolicyProvider::kPrefsForManagedDefault[] = {
{ContentSettingsType::ADS, prefs::kManagedDefaultAdsSetting},
+ {ContentSettingsType::CLIPBOARD_READ_WRITE,
+ prefs::kManagedDefaultClipboardSetting},
{ContentSettingsType::COOKIES, prefs::kManagedDefaultCookiesSetting},
{ContentSettingsType::IMAGES, prefs::kManagedDefaultImagesSetting},
{ContentSettingsType::GEOLOCATION,
diff --git a/chromium/components/content_settings/core/browser/content_settings_pref_provider.cc b/chromium/components/content_settings/core/browser/content_settings_pref_provider.cc
index a4e1f3507ee..a82d701f457 100644
--- a/chromium/components/content_settings/core/browser/content_settings_pref_provider.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_pref_provider.cc
@@ -44,6 +44,12 @@ namespace {
// These settings are no longer used, and should be deleted on profile startup.
const char kObsoleteDomainToOriginMigrationStatus[] =
"profile.content_settings.domain_to_origin_migration_status";
+const char kObsoleteWebIdActiveSessionPref[] =
+ "profile.content_settings.exceptions.webid_active_session";
+const char kObsoleteWebIdRequestPref[] =
+ "profile.content_settings.exceptions.webid_request";
+const char kObsoleteWebIdSharePref[] =
+ "profile.content_settings.exceptions.webid_share";
#if !BUILDFLAG(IS_IOS)
const char kObsoleteFullscreenExceptionsPref[] =
@@ -91,6 +97,9 @@ void PrefProvider::RegisterProfilePrefs(
// These prefs have been removed, but need to be registered so they can
// be deleted on startup.
registry->RegisterIntegerPref(kObsoleteDomainToOriginMigrationStatus, 0);
+ registry->RegisterDictionaryPref(kObsoleteWebIdActiveSessionPref);
+ registry->RegisterDictionaryPref(kObsoleteWebIdRequestPref);
+ registry->RegisterDictionaryPref(kObsoleteWebIdSharePref);
#if !BUILDFLAG(IS_IOS)
registry->RegisterDictionaryPref(
kObsoleteFullscreenExceptionsPref,
@@ -279,6 +288,9 @@ void PrefProvider::DiscardOrMigrateObsoletePreferences() {
return;
prefs_->ClearPref(kObsoleteDomainToOriginMigrationStatus);
+ prefs_->ClearPref(kObsoleteWebIdActiveSessionPref);
+ prefs_->ClearPref(kObsoleteWebIdRequestPref);
+ prefs_->ClearPref(kObsoleteWebIdSharePref);
// These prefs were never stored on iOS/Android so they don't need to be
// deleted.
diff --git a/chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc b/chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc
index a816caac349..b7cd0f5a323 100644
--- a/chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc
@@ -166,7 +166,7 @@ TEST_F(ContentSettingsRegistryTest, IsDefaultSettingValid) {
EXPECT_FALSE(info->IsDefaultSettingValid(CONTENT_SETTING_ALLOW));
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
info = registry()->Get(ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER);
EXPECT_TRUE(info->IsDefaultSettingValid(CONTENT_SETTING_ALLOW));
#endif
diff --git a/chromium/components/content_settings/core/browser/website_settings_registry.cc b/chromium/components/content_settings/core/browser/website_settings_registry.cc
index 08daef2024a..c2b56b324e5 100644
--- a/chromium/components/content_settings/core/browser/website_settings_registry.cc
+++ b/chromium/components/content_settings/core/browser/website_settings_registry.cc
@@ -230,12 +230,7 @@ void WebsiteSettingsRegistry::Init() {
WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
WebsiteSettingsInfo::SINGLE_ORIGIN_ONLY_SCOPE, DESKTOP,
WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO);
- Register(ContentSettingsType::FEDERATED_IDENTITY_SHARING, "webid-share",
- base::Value(), WebsiteSettingsInfo::UNSYNCABLE,
- WebsiteSettingsInfo::NOT_LOSSY,
- WebsiteSettingsInfo::SINGLE_ORIGIN_ONLY_SCOPE, ALL_PLATFORMS,
- WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO);
- Register(ContentSettingsType::FEDERATED_IDENTITY_REQUEST, "webid-request",
+ Register(ContentSettingsType::FEDERATED_IDENTITY_SHARING, "fedcm-share",
base::Value(), WebsiteSettingsInfo::UNSYNCABLE,
WebsiteSettingsInfo::NOT_LOSSY,
WebsiteSettingsInfo::SINGLE_ORIGIN_ONLY_SCOPE, ALL_PLATFORMS,
@@ -243,18 +238,23 @@ void WebsiteSettingsRegistry::Init() {
Register(ContentSettingsType::HTTP_ALLOWED, "http-allowed", base::Value(),
WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
WebsiteSettingsInfo::SINGLE_ORIGIN_WITH_EMBEDDED_EXCEPTIONS_SCOPE,
- DESKTOP | PLATFORM_ANDROID,
- WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
+ ALL_PLATFORMS, WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
Register(ContentSettingsType::FORMFILL_METADATA, "formfill-metadata",
base::Value(), WebsiteSettingsInfo::UNSYNCABLE,
WebsiteSettingsInfo::LOSSY,
WebsiteSettingsInfo::SINGLE_ORIGIN_ONLY_SCOPE, ALL_PLATFORMS,
WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
Register(ContentSettingsType::FEDERATED_IDENTITY_ACTIVE_SESSION,
- "webid-active-session", base::Value(),
+ "fedcm-active-session", base::Value(),
WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
WebsiteSettingsInfo::SINGLE_ORIGIN_ONLY_SCOPE, ALL_PLATFORMS,
WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO);
+ Register(ContentSettingsType::NOTIFICATION_INTERACTIONS,
+ "notification-interactions", base::Value(),
+ WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::LOSSY,
+ WebsiteSettingsInfo::SINGLE_ORIGIN_ONLY_SCOPE,
+ DESKTOP | PLATFORM_ANDROID,
+ WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO);
}
} // namespace content_settings
diff --git a/chromium/components/content_settings/core/common/content_settings.cc b/chromium/components/content_settings/core/common/content_settings.cc
index 3512b57674b..b861950751c 100644
--- a/chromium/components/content_settings/core/common/content_settings.cc
+++ b/chromium/components/content_settings/core/common/content_settings.cc
@@ -95,7 +95,7 @@ constexpr HistogramValue kHistogramValue[] = {
// Removed FILE_HANDLING in M98.
{ContentSettingsType::FILE_SYSTEM_ACCESS_CHOOSER_DATA, 76},
{ContentSettingsType::FEDERATED_IDENTITY_SHARING, 77},
- {ContentSettingsType::FEDERATED_IDENTITY_REQUEST, 78},
+ // Removed FEDERATED_IDENTITY_REQUEST in M103.
{ContentSettingsType::JAVASCRIPT_JIT, 79},
{ContentSettingsType::HTTP_ALLOWED, 80},
{ContentSettingsType::FORMFILL_METADATA, 81},
@@ -103,6 +103,7 @@ constexpr HistogramValue kHistogramValue[] = {
{ContentSettingsType::AUTO_DARK_WEB_CONTENT, 83},
{ContentSettingsType::REQUEST_DESKTOP_SITE, 84},
{ContentSettingsType::FEDERATED_IDENTITY_API, 85},
+ {ContentSettingsType::NOTIFICATION_INTERACTIONS, 86},
};
void FilterRulesForType(ContentSettingsForOneType& settings,
diff --git a/chromium/components/content_settings/core/common/content_settings_pattern_parser.cc b/chromium/components/content_settings/core/common/content_settings_pattern_parser.cc
index fa27361009d..0160a91ae6f 100644
--- a/chromium/components/content_settings/core/common/content_settings_pattern_parser.cc
+++ b/chromium/components/content_settings/core/common/content_settings_pattern_parser.cc
@@ -6,6 +6,7 @@
#include <stddef.h>
+#include "base/notreached.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "url/url_constants.h"
@@ -18,8 +19,9 @@ const char kHostWildcard[] = "*";
const char kPathWildcard[] = "*";
const char kPortWildcard[] = "*";
const char kSchemeWildcard[] = "*";
-const char kUrlPathSeparator[] = "/";
-const char kUrlPortSeparator[] = ":";
+const char kUrlPathSeparator = '/';
+const char kUrlPortSeparator = ':';
+const char kUrlPortAndPathSeparator[] = ":/";
// A domain wildcard pattern involves exactly one separating dot,
// inside the square brackets. This is a common misunderstanding of that
// pattern that we want to check for. See: https://crbug.com/823706.
@@ -45,16 +47,17 @@ void PatternParser::Parse(base::StringPiece pattern_spec,
base::StringPiece port_piece;
base::StringPiece path_piece;
- size_t start = 0;
- size_t current_pos = 0;
+ base::StringPiece::size_type start = 0;
+ base::StringPiece::size_type current_pos = 0;
if (pattern_spec.empty())
return;
// Test if a scheme pattern is in the spec.
- const std::string standard_scheme_separator(url::kStandardSchemeSeparator);
+ const base::StringPiece standard_scheme_separator(
+ url::kStandardSchemeSeparator);
current_pos = pattern_spec.find(standard_scheme_separator, start);
- if (current_pos != std::string::npos) {
+ if (current_pos != base::StringPiece::npos) {
scheme_piece = pattern_spec.substr(start, current_pos - start);
start = current_pos + standard_scheme_separator.size();
current_pos = start;
@@ -71,33 +74,34 @@ void PatternParser::Parse(base::StringPiece pattern_spec,
if (pattern_spec[current_pos] == '[')
current_pos = pattern_spec.find("]", start);
- if (current_pos == std::string::npos)
+ if (current_pos == base::StringPiece::npos)
return; // Bad pattern spec.
- current_pos = pattern_spec.find(kUrlPortSeparator, current_pos);
- if (current_pos == std::string::npos) {
- // No port spec found
- current_pos = pattern_spec.find(kUrlPathSeparator, start);
- if (current_pos == std::string::npos) {
- current_pos = pattern_spec.size();
- host_piece = pattern_spec.substr(start, current_pos - start);
- } else {
- // Pattern has a path spec.
- host_piece = pattern_spec.substr(start, current_pos - start);
- }
+ current_pos =
+ pattern_spec.find_first_of(kUrlPortAndPathSeparator, current_pos);
+ if (current_pos == base::StringPiece::npos) {
+ // No port spec found AND no path found.
+ current_pos = pattern_spec.size();
+ host_piece = pattern_spec.substr(start, current_pos - start);
start = current_pos;
- } else {
+ } else if (pattern_spec[current_pos] == kUrlPathSeparator) {
+ // Pattern has a path spec.
+ host_piece = pattern_spec.substr(start, current_pos - start);
+ start = current_pos;
+ } else if (pattern_spec[current_pos] == kUrlPortSeparator) {
// Port spec found.
host_piece = pattern_spec.substr(start, current_pos - start);
start = current_pos + 1;
if (start < pattern_spec.size()) {
current_pos = pattern_spec.find(kUrlPathSeparator, start);
- if (current_pos == std::string::npos) {
+ if (current_pos == base::StringPiece::npos) {
current_pos = pattern_spec.size();
}
port_piece = pattern_spec.substr(start, current_pos - start);
start = current_pos;
}
+ } else {
+ NOTREACHED();
}
current_pos = pattern_spec.size();
@@ -145,7 +149,7 @@ void PatternParser::Parse(base::StringPiece pattern_spec,
builder->WithHost(std::string(host_piece));
} else {
// If the host contains a wildcard symbol then it is invalid.
- if (host_piece.find(kHostWildcard) != std::string::npos) {
+ if (host_piece.find(kHostWildcard) != base::StringPiece::npos) {
builder->Invalid();
return;
}
@@ -164,8 +168,8 @@ void PatternParser::Parse(base::StringPiece pattern_spec,
builder->WithPortWildcard();
} else {
// Check if the port string represents a valid port.
- for (size_t i = 0; i < port_piece.size(); ++i) {
- if (!base::IsAsciiDigit(port_piece[i])) {
+ for (const auto port_char : port_piece) {
+ if (!base::IsAsciiDigit(port_char)) {
builder->Invalid();
return;
}
@@ -173,11 +177,11 @@ void PatternParser::Parse(base::StringPiece pattern_spec,
// TODO(markusheintz): Check port range.
builder->WithPort(std::string(port_piece));
}
- } else {
- if (!ContentSettingsPattern::IsNonWildcardDomainNonPortScheme(
- scheme_piece) &&
- scheme_piece != url::kFileScheme)
- builder->WithPortWildcard();
+ } else if (!ContentSettingsPattern::IsNonWildcardDomainNonPortScheme(
+ scheme_piece) &&
+ !base::EqualsCaseInsensitiveASCII(scheme_piece,
+ url::kFileScheme)) {
+ builder->WithPortWildcard();
}
if (!path_piece.empty()) {
@@ -225,7 +229,7 @@ std::string PatternParser::ToString(
if (ContentSettingsPattern::IsNonWildcardDomainNonPortScheme(parts.scheme)) {
if (parts.path.empty())
- str += std::string(kUrlPathSeparator);
+ str += kUrlPathSeparator;
else
str += parts.path;
return str;
diff --git a/chromium/components/content_settings/core/common/content_settings_pattern_parser_fuzzer.cc b/chromium/components/content_settings/core/common/content_settings_pattern_parser_fuzzer.cc
index cfefa00aaaa..52733619d29 100644
--- a/chromium/components/content_settings/core/common/content_settings_pattern_parser_fuzzer.cc
+++ b/chromium/components/content_settings/core/common/content_settings_pattern_parser_fuzzer.cc
@@ -43,7 +43,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
CHECK_EQ(recanonicalized_pattern.ToString(), canonical_pattern_spec)
<< "\n (originally '" << pattern_spec << "')";
CHECK_EQ(recanonicalized_pattern.Compare(canonical_pattern),
- ContentSettingsPattern::Relation::IDENTITY);
+ ContentSettingsPattern::Relation::IDENTITY)
+ << "Canonical pattern\n"
+ << canonical_pattern.ToString() << "\nand recanonicalized pattern\n"
+ << recanonicalized_pattern.ToString() << "\nwere not identical";
return 0;
}
diff --git a/chromium/components/content_settings/core/common/content_settings_pattern_parser_unittest.cc b/chromium/components/content_settings/core/common/content_settings_pattern_parser_unittest.cc
index 244db0108a7..747bc52bffc 100644
--- a/chromium/components/content_settings/core/common/content_settings_pattern_parser_unittest.cc
+++ b/chromium/components/content_settings/core/common/content_settings_pattern_parser_unittest.cc
@@ -359,3 +359,36 @@ TEST(ContentSettingsPatternParserTest, SerializePatterns) {
EXPECT_EQ("chrome-extension://peoadpeiejnhkmpaakpnompolbglelel/",
content_settings::PatternParser::ToString(parts));
}
+
+TEST(ContentSettingsPatternParserTest, IdempotencyOfCanonicalization) {
+ const std::string pattern_specs[] = {
+ "abc",
+ "https://chromium.org",
+ "file:///foo/",
+ "file:///foo/:/bar/:/baz",
+ "https://foo/:/bar/:/baz",
+ "file://:/path",
+ "file://:/:", // crbug.com/1196591
+ "file:///C:/Users/a.txt",
+ "filE:///foo/", // crbug.com/1323130
+ };
+
+ for (const std::string& spec : pattern_specs) {
+ SCOPED_TRACE("spec: " + spec);
+ auto builder = ContentSettingsPattern::CreateBuilder();
+ content_settings::PatternParser::Parse(spec, builder.get());
+ ContentSettingsPattern pattern = builder->Build();
+ EXPECT_TRUE(pattern.IsValid());
+ std::string canonical = pattern.ToString();
+
+ auto builder2 = ContentSettingsPattern::CreateBuilder();
+ content_settings::PatternParser::Parse(canonical, builder2.get());
+ ContentSettingsPattern pattern2 = builder2->Build();
+ EXPECT_TRUE(pattern2.IsValid());
+ std::string canonical2 = pattern2.ToString();
+
+ EXPECT_EQ(canonical, canonical2);
+ EXPECT_EQ(pattern.Compare(pattern2),
+ ContentSettingsPattern::Relation::IDENTITY);
+ }
+}
diff --git a/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc b/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc
index 547d9022aff..719af758ac3 100644
--- a/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc
+++ b/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc
@@ -545,8 +545,6 @@ TEST(ContentSettingsPatternTest, InvalidPatterns) {
// Invalid file pattern strings.
EXPECT_FALSE(Pattern("file://").IsValid());
EXPECT_STREQ("", Pattern("file://").ToString().c_str());
- EXPECT_FALSE(Pattern("file:///foo/bar.html:8080").IsValid());
- EXPECT_STREQ("", Pattern("file:///foo/bar.html:8080").ToString().c_str());
// Host having multiple ending dots.
EXPECT_FALSE(Pattern("www.example.com..").IsValid());
diff --git a/chromium/components/content_settings/core/common/content_settings_types.h b/chromium/components/content_settings/core/common/content_settings_types.h
index 03eeba359b3..9c6728a428b 100644
--- a/chromium/components/content_settings/core/common/content_settings_types.h
+++ b/chromium/components/content_settings/core/common/content_settings_types.h
@@ -242,18 +242,11 @@ enum class ContentSettingsType : int32_t {
// the corresponding "guard" setting.
FILE_SYSTEM_ACCESS_CHOOSER_DATA,
- // Stores a grant for the browser to intermediate or allow without
- // restriction sharing of identity information by an identity provider to
- // specified relying parties. The setting is associated with the identity
- // provider's origin.
- // This is managed by WebID.
- FEDERATED_IDENTITY_SHARING,
-
// Stores a grant that allows a relying party to send a request for identity
// information to specified identity providers, potentially through any
// anti-tracking measures that would otherwise prevent it. This setting is
// associated with the relying party's origin.
- FEDERATED_IDENTITY_REQUEST,
+ FEDERATED_IDENTITY_SHARING,
// Whether to use the v8 optimized JIT for running JavaScript on the page.
JAVASCRIPT_JIT,
@@ -288,6 +281,12 @@ enum class ContentSettingsType : int32_t {
// the browser FedCM API.
FEDERATED_IDENTITY_API,
+ // Stores notification interactions per origin for the past 90 days.
+ // Interactions per origin are pre-aggregated over seven-day windows: A
+ // notification interaction or display is assigned to the last Monday midnight
+ // in local time.
+ NOTIFICATION_INTERACTIONS,
+
NUM_TYPES,
};
diff --git a/chromium/components/content_settings/core/common/pref_names.cc b/chromium/components/content_settings/core/common/pref_names.cc
index e6a333fe0bf..11a46740112 100644
--- a/chromium/components/content_settings/core/common/pref_names.cc
+++ b/chromium/components/content_settings/core/common/pref_names.cc
@@ -25,6 +25,8 @@ const char kContentSettingsWindowLastTabIndex[] =
// content settings.
const char kManagedDefaultAdsSetting[] =
"profile.managed_default_content_settings.ads";
+const char kManagedDefaultClipboardSetting[] =
+ "profile.managed_default_content_settings.clipboard";
const char kManagedDefaultCookiesSetting[] =
"profile.managed_default_content_settings.cookies";
const char kManagedDefaultGeolocationSetting[] =
@@ -66,6 +68,10 @@ const char kManagedDefaultLocalFontsSetting[] =
// Preferences that are exclusively used to store managed
// content settings patterns.
+const char kManagedClipboardAllowedForUrls[] =
+ "profile.managed_clipboard_allowed_for_urls";
+const char kManagedClipboardBlockedForUrls[] =
+ "profile.managed_clipboard_blocked_for_urls";
const char kManagedAutoSelectCertificateForUrls[] =
"profile.managed_auto_select_certificate_for_urls";
const char kManagedCookiesAllowedForUrls[] =
diff --git a/chromium/components/content_settings/core/common/pref_names.h b/chromium/components/content_settings/core/common/pref_names.h
index 8d01f228586..ac1023bec27 100644
--- a/chromium/components/content_settings/core/common/pref_names.h
+++ b/chromium/components/content_settings/core/common/pref_names.h
@@ -19,6 +19,7 @@ extern const char kContentSettingsVersion[];
extern const char kContentSettingsWindowLastTabIndex[];
extern const char kManagedDefaultAdsSetting[];
+extern const char kManagedDefaultClipboardSetting[];
extern const char kManagedDefaultCookiesSetting[];
extern const char kManagedDefaultImagesSetting[];
extern const char kManagedDefaultInsecureContentSetting[];
@@ -39,6 +40,8 @@ extern const char kManagedDefaultWebHidGuardSetting[];
extern const char kManagedDefaultWindowPlacementSetting[];
extern const char kManagedDefaultLocalFontsSetting[];
+extern const char kManagedClipboardAllowedForUrls[];
+extern const char kManagedClipboardBlockedForUrls[];
extern const char kManagedCookiesAllowedForUrls[];
extern const char kManagedCookiesBlockedForUrls[];
extern const char kManagedCookiesSessionOnlyForUrls[];
diff --git a/chromium/components/content_settings/renderer/content_settings_agent_impl_browsertest.cc b/chromium/components/content_settings/renderer/content_settings_agent_impl_browsertest.cc
index 64b52e04e4e..7a43a9ba49e 100644
--- a/chromium/components/content_settings/renderer/content_settings_agent_impl_browsertest.cc
+++ b/chromium/components/content_settings/renderer/content_settings_agent_impl_browsertest.cc
@@ -19,6 +19,7 @@
#include "net/cookies/site_for_cookies.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/test/test_web_frame_content_dumper.h"
#include "third_party/blink/public/web/web_view.h"
#include "url/gurl.h"
diff --git a/chromium/components/contextual_search/README.md b/chromium/components/contextual_search/README.md
index 5302ac8837f..b63e88bc18f 100644
--- a/chromium/components/contextual_search/README.md
+++ b/chromium/components/contextual_search/README.md
@@ -1,42 +1,6 @@
# Contextual Search Component
-The Contextual Search component implements some platform agnostic services that
-mediate between the main CS implementation in the Browser and some other parts
-of the system. These include the JS API service to allow communication from
-the Overlay to CS, and user-interaction aggregation.
+The Contextual Search component is no longer very useful, now that Contextual Search has been put into maintenance mode.
+This still declares buildflags for the renderer and public definitions needed by other parts of the system.
-## JS API Service
-
-The JS API Service allows JavaScript in the Overlay Panel to call into
-Contextual Search. This is done by providing an entry point at
-chrome.contextualSearch when the Contextual Search Overlay Panel is active.
-
-Enabling this API is somewhat complicated in order to make sure that the API
-only exists for a Renderer created by CS for the Overlay:
-
-1. Whenever a Renderer is created, an OverlayJsRenderFrameObserver is created.
- This class is at the heart of the connection between the Renderer, CS, JS
- and the JavaScript API.
-2. An OverlayJsRenderFrameObserver is created for every renderer to check if
- it's a CS Overlay Panel. When the renderer starts up it asks Contextual
- Search if the current URL belongs to its overlay panel, and if it does then
- the JS API is enabled for that renderer to allow it to accept JS messages
- from the page.
-3. When the OverlayJsRenderFrameObserver gets the response in #2 it creates a
- ContextualSearchWrapper object that allows injecting JavaScript into the
- WebFrame.
-4. When some JS in a page wants to call into Chrome it simply accesses methods
- in the chrome.contextualSearch API. If the page is being presented in the
- Overlay Panel for CS then that object will exist with native method
- implementations. The renderer forwards the API request to the browser for
- processing.
-
-The above interaction is used by the Translate onebox to allow showing a
-translation in the caption of the Contextual Search Bar, and to provide control
-of the position of the Overlay.
-
-## User Interaction Aggregation
-
-The CtrAggregator and WeeklyActivityStorage classes are used aggregate user
-actions on a weekly basis for use in machine learning for improved triggering.
diff --git a/chromium/components/contextual_search/content/BUILD.gn b/chromium/components/contextual_search/content/BUILD.gn
deleted file mode 100644
index 60e4c6ec1fc..00000000000
--- a/chromium/components/contextual_search/content/BUILD.gn
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-static_library("browser") {
- sources = [
- "browser/contextual_search_js_api_service_impl.cc",
- "browser/contextual_search_js_api_service_impl.h",
- ]
- deps = [
- "//base",
- "//components/contextual_search/content/common/mojom",
- ]
-}
-
-static_library("renderer") {
- sources = [
- "renderer/contextual_search_wrapper.cc",
- "renderer/contextual_search_wrapper.h",
- "renderer/overlay_js_render_frame_observer.cc",
- "renderer/overlay_js_render_frame_observer.h",
- ]
- deps = [
- "//base",
- "//components/contextual_search/content/common/mojom",
- "//content/public/common",
- "//content/public/renderer",
- "//gin",
- "//mojo/public/cpp/bindings",
- "//services/service_manager/public/cpp",
- "//third_party/blink/public:blink_headers",
- "//v8",
- ]
-}
diff --git a/chromium/components/contextual_search/content/DEPS b/chromium/components/contextual_search/content/DEPS
deleted file mode 100644
index 57157178c73..00000000000
--- a/chromium/components/contextual_search/content/DEPS
+++ /dev/null
@@ -1,4 +0,0 @@
-include_rules = [
- "+mojo/public",
- "+services/service_manager/public/cpp",
-]
diff --git a/chromium/components/contextual_search/content/browser/contextual_search_js_api_handler.h b/chromium/components/contextual_search/content/browser/contextual_search_js_api_handler.h
deleted file mode 100644
index 53516c12203..00000000000
--- a/chromium/components/contextual_search/content/browser/contextual_search_js_api_handler.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_BROWSER_CONTEXTUAL_SEARCH_JS_API_HANDLER_H_
-#define COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_BROWSER_CONTEXTUAL_SEARCH_JS_API_HANDLER_H_
-
-#include <string>
-
-#include "components/contextual_search/content/common/mojom/contextual_search_js_api_service.mojom.h"
-
-namespace contextual_search {
-
-// Interface that the Contextual Search Renderer uses to call back to
-// the browser to handle its JavaScript API.
-class ContextualSearchJsApiHandler {
- public:
- ContextualSearchJsApiHandler() {}
-
- ContextualSearchJsApiHandler(const ContextualSearchJsApiHandler&) = delete;
- ContextualSearchJsApiHandler& operator=(const ContextualSearchJsApiHandler&) =
- delete;
-
- virtual ~ContextualSearchJsApiHandler() {}
-
- // Enabling API, determines if the JS API should be enabled for the given URL.
- virtual void ShouldEnableJsApi(
- const GURL& gurl,
- mojom::ContextualSearchJsApiService::ShouldEnableJsApiCallback
- callback) = 0;
-
- //=======
- // JS API
- //=======
-
- // Set the caption in the Contextual Search Bar, and indicate whether
- // the caption provides an answer (such as an actual definition), rather than
- // just general notification of what kind of answer may be available.
- virtual void SetCaption(const std::string& caption, bool does_answer) = 0;
-
- // Changes the Overlay position to the desired position.
- // The panel cannot be set to any opened position if it's not already opened.
- virtual void ChangeOverlayPosition(
- mojom::OverlayPosition desired_position) = 0;
-};
-
-} // namespace contextual_search
-
-#endif // COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_BROWSER_CONTEXTUAL_SEARCH_JS_API_HANDLER_H_
diff --git a/chromium/components/contextual_search/content/browser/contextual_search_js_api_service_impl.cc b/chromium/components/contextual_search/content/browser/contextual_search_js_api_service_impl.cc
deleted file mode 100644
index 400c5964e30..00000000000
--- a/chromium/components/contextual_search/content/browser/contextual_search_js_api_service_impl.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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.
-
-#include "components/contextual_search/content/browser/contextual_search_js_api_service_impl.h"
-
-#include <memory>
-#include <utility>
-
-#include "components/contextual_search/content/browser/contextual_search_js_api_handler.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
-
-namespace contextual_search {
-
-ContextualSearchJsApiServiceImpl::ContextualSearchJsApiServiceImpl(
- ContextualSearchJsApiHandler* contextual_search_js_api_handler)
- : contextual_search_js_api_handler_(contextual_search_js_api_handler) {}
-
-ContextualSearchJsApiServiceImpl::~ContextualSearchJsApiServiceImpl() {}
-
-void ContextualSearchJsApiServiceImpl::ShouldEnableJsApi(
- const GURL& gurl,
- mojom::ContextualSearchJsApiService::ShouldEnableJsApiCallback callback) {
- contextual_search_js_api_handler_->ShouldEnableJsApi(gurl,
- std::move(callback));
-}
-
-void ContextualSearchJsApiServiceImpl::HandleSetCaption(
- const std::string& caption,
- bool does_answer) {
- contextual_search_js_api_handler_->SetCaption(caption, does_answer);
-}
-
-void ContextualSearchJsApiServiceImpl::HandleChangeOverlayPosition(
- mojom::OverlayPosition desired_position) {
- contextual_search_js_api_handler_->ChangeOverlayPosition(desired_position);
-}
-
-// static
-void CreateContextualSearchJsApiService(
- ContextualSearchJsApiHandler* contextual_search_js_api_handler,
- mojo::PendingReceiver<mojom::ContextualSearchJsApiService> receiver) {
- mojo::MakeSelfOwnedReceiver(
- std::make_unique<ContextualSearchJsApiServiceImpl>(
- contextual_search_js_api_handler),
- std::move(receiver));
-}
-
-} // namespace contextual_search
diff --git a/chromium/components/contextual_search/content/browser/contextual_search_js_api_service_impl.h b/chromium/components/contextual_search/content/browser/contextual_search_js_api_service_impl.h
deleted file mode 100644
index 2f215a648c7..00000000000
--- a/chromium/components/contextual_search/content/browser/contextual_search_js_api_service_impl.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_BROWSER_CONTEXTUAL_SEARCH_JS_API_SERVICE_IMPL_H_
-#define COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_BROWSER_CONTEXTUAL_SEARCH_JS_API_SERVICE_IMPL_H_
-
-#include "base/memory/raw_ptr.h"
-#include "components/contextual_search/content/browser/contextual_search_js_api_handler.h"
-#include "components/contextual_search/content/common/mojom/contextual_search_js_api_service.mojom.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-
-namespace contextual_search {
-
-// This is the receiving end of Contextual Search JavaScript API calls.
-// TODO(donnd): Move this to java. See https://crbug.com/866972.
-class ContextualSearchJsApiServiceImpl
- : public mojom::ContextualSearchJsApiService {
- public:
- explicit ContextualSearchJsApiServiceImpl(
- ContextualSearchJsApiHandler* contextual_search_js_api_handler);
-
- ContextualSearchJsApiServiceImpl(const ContextualSearchJsApiServiceImpl&) =
- delete;
- ContextualSearchJsApiServiceImpl& operator=(
- const ContextualSearchJsApiServiceImpl&) = delete;
-
- ~ContextualSearchJsApiServiceImpl() override;
-
- // Mojo ContextualSearchApiService implementation.
- // Determines if the JavaScript API should be enabled for the given |gurl|.
- // The given |callback| will be notified with the answer.
- void ShouldEnableJsApi(
- const GURL& gurl,
- mojom::ContextualSearchJsApiService::ShouldEnableJsApiCallback callback)
- override;
-
- // Handles a JavaScript call to set the caption in the Bar to
- // the given |message|.
- void HandleSetCaption(const std::string& message, bool does_answer) override;
-
- // Handles a JavaScript call to change the Overlay position.
- // The panel cannot be changed to any opened position if it's not already
- // opened.
- void HandleChangeOverlayPosition(
- mojom::OverlayPosition desired_position) override;
-
- private:
- // The UI handler for calls through the JavaScript API.
- raw_ptr<ContextualSearchJsApiHandler> contextual_search_js_api_handler_;
-};
-
-// static
-void CreateContextualSearchJsApiService(
- ContextualSearchJsApiHandler* contextual_search_js_api_handler,
- mojo::PendingReceiver<mojom::ContextualSearchJsApiService> receiver);
-
-} // namespace contextual_search
-
-#endif // COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_BROWSER_CONTEXTUAL_SEARCH_JS_API_SERVICE_IMPL_H_
diff --git a/chromium/components/contextual_search/content/common/mojom/BUILD.gn b/chromium/components/contextual_search/content/common/mojom/BUILD.gn
deleted file mode 100644
index 70afab490f2..00000000000
--- a/chromium/components/contextual_search/content/common/mojom/BUILD.gn
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("mojom") {
- generate_java = true
- sources = [ "contextual_search_js_api_service.mojom" ]
- public_deps = [ "//url/mojom:url_mojom_gurl" ]
-}
diff --git a/chromium/components/contextual_search/content/common/mojom/contextual_search_js_api_service.mojom b/chromium/components/contextual_search/content/common/mojom/contextual_search_js_api_service.mojom
deleted file mode 100644
index 5a25217345c..00000000000
--- a/chromium/components/contextual_search/content/common/mojom/contextual_search_js_api_service.mojom
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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.
-
-module contextual_search.mojom;
-
-import "url/mojom/url.mojom";
-
- // Possible positions for the Overlay Panel.
-enum OverlayPosition {
- // Not visible.
- kClose,
- // Visible only as a Bar peeking up from the bottom.
- kPeek,
- // Content is partially visible and partially offscreen.
- kExpand,
- // Content is fully visible.
- kMaximize,
-};
-
-// This service is implemented by the browser process and is used by the
-// renderer when a Contextual Search JavaScript API function is called.
-// When a renderer wants to know if the CS JS API should be enabled it calls the
-// first method. All the other methods are available to JavaScript at
-// chrome.contextualSearch only when the first method returns true, indicating
-// that the panel belongs to Contextual Search. More details are in the
-// README.md file in this component.
-interface ContextualSearchJsApiService {
-
- // Determines if this JavaScript API should be enabled for the given URL.
- // The asynchronous callback will be notified with the answer.
- // TODO(donnd): Consider changing the messaging direction to be from browser
- // to renderer. See https://crbug.com/866976.
- ShouldEnableJsApi(url.mojom.Url url) => (bool should_enable);
-
- // Handle a call from the JS API to set the caption, and indicate whether
- // the caption provides an answer (such as an actual definition), rather than
- // just general notification of what kind of answer may be available.
- HandleSetCaption(string message, bool does_answer);
-
- // Called by JavaScript to change the Overlay position. The panel cannot be
- // changed to any opened position if it's not already opened.
- HandleChangeOverlayPosition(OverlayPosition desired_position);
-};
diff --git a/chromium/components/contextual_search/content/renderer/DEPS b/chromium/components/contextual_search/content/renderer/DEPS
deleted file mode 100644
index a91e349bbd9..00000000000
--- a/chromium/components/contextual_search/content/renderer/DEPS
+++ /dev/null
@@ -1,8 +0,0 @@
-include_rules = [
- "+content/public/common",
- "+content/public/renderer",
- "+content/renderer",
- "+gin",
- "+third_party/blink",
- "+v8",
-]
diff --git a/chromium/components/contextual_search/content/renderer/contextual_search_wrapper.cc b/chromium/components/contextual_search/content/renderer/contextual_search_wrapper.cc
deleted file mode 100644
index 9f44841dfa8..00000000000
--- a/chromium/components/contextual_search/content/renderer/contextual_search_wrapper.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// 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.
-
-#include "components/contextual_search/content/renderer/contextual_search_wrapper.h"
-
-#include "base/strings/string_util.h"
-#include "content/public/renderer/chrome_object_extensions_utils.h"
-#include "content/public/renderer/render_frame.h"
-#include "gin/arguments.h"
-#include "gin/object_template_builder.h"
-#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
-#include "third_party/blink/public/web/blink.h"
-#include "third_party/blink/public/web/web_frame.h"
-#include "third_party/blink/public/web/web_local_frame.h"
-#include "v8/include/v8.h"
-
-namespace {
-
-static const char kContextualSearchObjectName[] = "contextualSearch";
-static const char kSetCaptionMethodName[] = "setCaption";
-static const char kChangeOverlayPositionMethodName[] = "changeOverlayPosition";
-
-} // namespace
-
-namespace contextual_search {
-
-gin::WrapperInfo ContextualSearchWrapper::kWrapperInfo = {
- gin::kEmbedderNativeGin};
-
-// static
-void ContextualSearchWrapper::Install(content::RenderFrame* render_frame) {
- // NOTE: Installing new v8 functions that can access Chrome native code
- // requires a security review! We did an exhaustive search for a better
- // way to implement a communication channel between the page and Chrome,
- // but found nothing better.
- // TODO(donnd): use a better communication channel once that becomes
- // available, e.g. navigator.connect API. See https://crbug.com/541683.
- // TODO(donnd): refactor some of this boilerplate into a reusable
- // method. This was cribbed from MemoryBenchmarkingExtension.
- v8::Isolate* isolate = blink::MainThreadIsolate();
- v8::HandleScope handle_scope(isolate);
- v8::Local<v8::Context> context =
- render_frame->GetWebFrame()->MainWorldScriptContext();
- if (context.IsEmpty())
- return;
-
- v8::Context::Scope context_scope(context);
- gin::Handle<ContextualSearchWrapper> wrapper =
- gin::CreateHandle(isolate, new ContextualSearchWrapper(render_frame));
- if (wrapper.IsEmpty())
- return;
-
- // Setting a property can trigger microtask execution.
- v8::MicrotasksScope microtasks_scope(isolate,
- v8::MicrotasksScope::kRunMicrotasks);
-
- // Connect the Contextual Search Chrome object into V8 so the SERP can call
- // us.
- v8::Local<v8::Object> chrome =
- content::GetOrCreateChromeObject(isolate, context);
- chrome
- ->Set(context, gin::StringToV8(isolate, kContextualSearchObjectName),
- wrapper.ToV8())
- .Check();
-}
-
-ContextualSearchWrapper::ContextualSearchWrapper(
- content::RenderFrame* render_frame)
- : RenderFrameObserver(render_frame) {}
-
-ContextualSearchWrapper::~ContextualSearchWrapper() {}
-
-gin::ObjectTemplateBuilder ContextualSearchWrapper::GetObjectTemplateBuilder(
- v8::Isolate* isolate) {
- return gin::Wrappable<ContextualSearchWrapper>::GetObjectTemplateBuilder(
- isolate)
- .SetMethod(kSetCaptionMethodName, &ContextualSearchWrapper::SetCaption)
- .SetMethod(kChangeOverlayPositionMethodName,
- &ContextualSearchWrapper::ChangeOverlayPosition);
-}
-
-bool ContextualSearchWrapper::EnsureServiceConnected() {
- if (render_frame() && !contextual_search_js_api_service_) {
- render_frame()->GetBrowserInterfaceBroker()->GetInterface(
- contextual_search_js_api_service_.BindNewPipeAndPassReceiver());
- return true;
- }
- return false;
-}
-
-void ContextualSearchWrapper::OnDestruct() {}
-
-void ContextualSearchWrapper::SetCaption(const std::string& caption,
- bool does_answer) {
- if (EnsureServiceConnected()) {
- contextual_search_js_api_service_->HandleSetCaption(caption, does_answer);
- }
-}
-
-void ContextualSearchWrapper::ChangeOverlayPosition(
- unsigned int desired_position) {
- if (EnsureServiceConnected()) {
- contextual_search_js_api_service_->HandleChangeOverlayPosition(
- static_cast<mojom::OverlayPosition>(desired_position));
- }
-}
-
-} // namespace contextual_search
diff --git a/chromium/components/contextual_search/content/renderer/contextual_search_wrapper.h b/chromium/components/contextual_search/content/renderer/contextual_search_wrapper.h
deleted file mode 100644
index b1379f9a6d8..00000000000
--- a/chromium/components/contextual_search/content/renderer/contextual_search_wrapper.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_RENDERER_CONTEXTUAL_SEARCH_WRAPPER_H_
-#define COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_RENDERER_CONTEXTUAL_SEARCH_WRAPPER_H_
-
-#include "components/contextual_search/content/common/mojom/contextual_search_js_api_service.mojom.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_frame_observer.h"
-#include "gin/handle.h"
-#include "gin/wrappable.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace blink {
-class WebFrame;
-}
-
-namespace contextual_search {
-
-// Wrapper for injecting Contextual Search JavaScript
-// into a WebFrame.
-class ContextualSearchWrapper : public gin::Wrappable<ContextualSearchWrapper>,
- public content::RenderFrameObserver {
- public:
- // Installs Contextual Search JavaScript.
- static void Install(content::RenderFrame* render_frame);
-
- ContextualSearchWrapper(const ContextualSearchWrapper&) = delete;
- ContextualSearchWrapper& operator=(const ContextualSearchWrapper&) = delete;
-
- // RenderFrameObserver implementation.
- void OnDestruct() override;
-
- // Required by gin::Wrappable.
- static gin::WrapperInfo kWrapperInfo;
-
- private:
- // Instantiate by calling Install.
- explicit ContextualSearchWrapper(content::RenderFrame* render_frame);
- ~ContextualSearchWrapper() override;
-
- // gin::Wrappable.
- gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
- v8::Isolate* isolate) final;
-
- // Called by JavaScript to set the caption , and indicate whether
- // the caption provides an answer (e.g. an actual definition), rather than
- // just general notification of what kind of answer may be available.
- void SetCaption(const std::string& caption, bool does_answer);
-
- // Called by JavaScript to change the Overlay position.
- // The panel cannot be changed to any opened position if it's not already
- // opened.
- void ChangeOverlayPosition(unsigned int desired_position);
-
- // Helper function to ensure that this class has connected to the API service.
- // Returns false if cannot connect.
- bool EnsureServiceConnected();
-
- // The service to notify when API calls are made.
- mojo::Remote<mojom::ContextualSearchJsApiService>
- contextual_search_js_api_service_;
-};
-
-} // namespace contextual_search
-
-#endif // COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_RENDERER_CONTEXTUAL_SEARCH_WRAPPER_H_
diff --git a/chromium/components/contextual_search/content/renderer/overlay_js_render_frame_observer.cc b/chromium/components/contextual_search/content/renderer/overlay_js_render_frame_observer.cc
deleted file mode 100644
index 06f1906262f..00000000000
--- a/chromium/components/contextual_search/content/renderer/overlay_js_render_frame_observer.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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.
-
-#include "components/contextual_search/content/renderer/overlay_js_render_frame_observer.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "components/contextual_search/content/renderer/contextual_search_wrapper.h"
-#include "content/public/renderer/render_frame.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/web/web_document.h"
-#include "v8/include/v8.h"
-
-namespace contextual_search {
-
-OverlayJsRenderFrameObserver::OverlayJsRenderFrameObserver(
- content::RenderFrame* render_frame,
- service_manager::BinderRegistry* registry)
- : RenderFrameObserver(render_frame) {}
-
-OverlayJsRenderFrameObserver::~OverlayJsRenderFrameObserver() {}
-
-void OverlayJsRenderFrameObserver::DidClearWindowObject() {
- if (!did_start_enabling_js_api_) {
- blink::WebURL url = render_frame()->GetWebFrame()->GetDocument().Url();
- GURL gurl(url);
- if (!url.IsEmpty() && EnsureServiceConnected()) {
- did_start_enabling_js_api_ = true;
- contextual_search_js_api_service_->ShouldEnableJsApi(
- gurl, base::BindOnce(&OverlayJsRenderFrameObserver::EnableJsApi,
- weak_factory_.GetWeakPtr()));
- }
- }
-}
-
-void OverlayJsRenderFrameObserver::EnableJsApi(bool should_enable) {
- if (!should_enable)
- return;
- contextual_search::ContextualSearchWrapper::Install(render_frame());
-}
-
-bool OverlayJsRenderFrameObserver::EnsureServiceConnected() {
- if (render_frame() && !contextual_search_js_api_service_) {
- render_frame()->GetBrowserInterfaceBroker()->GetInterface(
- contextual_search_js_api_service_.BindNewPipeAndPassReceiver());
- return true;
- }
- return false;
-}
-
-void OverlayJsRenderFrameObserver::OnDestruct() {
- delete this;
-}
-
-} // namespace contextual_search
diff --git a/chromium/components/contextual_search/content/renderer/overlay_js_render_frame_observer.h b/chromium/components/contextual_search/content/renderer/overlay_js_render_frame_observer.h
deleted file mode 100644
index a0018a69aca..00000000000
--- a/chromium/components/contextual_search/content/renderer/overlay_js_render_frame_observer.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_RENDERER_OVERLAY_JS_RENDER_FRAME_OBSERVER_H_
-#define COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_RENDERER_OVERLAY_JS_RENDER_FRAME_OBSERVER_H_
-
-#include "base/memory/weak_ptr.h"
-#include "components/contextual_search/content/common/mojom/contextual_search_js_api_service.mojom.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_frame_observer.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "third_party/blink/public/web/web_local_frame.h"
-#include "v8/include/v8.h"
-
-namespace contextual_search {
-
-// OverlayJsRenderFrame observer waits for a page to be loaded and then
-// tries to connect to a mojo service hosted in the browser process. The
-// service will tell this render process if the current page is presented
-// in an overlay panel.
-class OverlayJsRenderFrameObserver : public content::RenderFrameObserver {
- public:
- OverlayJsRenderFrameObserver(content::RenderFrame* render_frame,
- service_manager::BinderRegistry* registry);
-
- OverlayJsRenderFrameObserver(const OverlayJsRenderFrameObserver&) = delete;
- OverlayJsRenderFrameObserver& operator=(const OverlayJsRenderFrameObserver&) =
- delete;
-
- ~OverlayJsRenderFrameObserver() override;
-
- // RenderFrameObserver implementation.
- void DidClearWindowObject() override;
-
- private:
- // RenderFrameObserver implementation.
- void OnDestruct() override;
-
- // Helper function to ensure that this class has connected to the CS service.
- // Returns false if cannot connect.
- bool EnsureServiceConnected();
-
- // Enables or disables the JS API.
- void EnableJsApi(bool should_enable);
-
- // The CS service to notify when deciding to enable the API or when API calls
- // are made.
- mojo::Remote<mojom::ContextualSearchJsApiService>
- contextual_search_js_api_service_;
-
- // Remembers whether we did start enabling the JS API by making a request
- // to the Contextual Search service to ask if we should enable for this
- // URL or not.
- bool did_start_enabling_js_api_ = false;
-
- base::WeakPtrFactory<OverlayJsRenderFrameObserver> weak_factory_{this};
-};
-
-} // namespace contextual_search
-
-#endif // COMPONENTS_CONTEXTUAL_SEARCH_CONTENT_RENDERER_OVERLAY_JS_RENDER_FRAME_OBSERVER_H_
diff --git a/chromium/components/contextual_search/core/BUILD.gn b/chromium/components/contextual_search/core/BUILD.gn
index 96ef8db115e..6a97b0130b9 100644
--- a/chromium/components/contextual_search/core/BUILD.gn
+++ b/chromium/components/contextual_search/core/BUILD.gn
@@ -4,12 +4,8 @@
static_library("browser") {
sources = [
- "browser/ctr_aggregator.cc",
- "browser/ctr_aggregator.h",
"browser/public.cc",
"browser/public.h",
- "browser/weekly_activity_storage.cc",
- "browser/weekly_activity_storage.h",
]
deps = [
"//base",
@@ -17,14 +13,3 @@ static_library("browser") {
"//components/prefs:prefs",
]
}
-
-source_set("unit_tests") {
- testonly = true
- sources = [ "browser/ctr_aggregator_unittest.cc" ]
-
- deps = [
- ":browser",
- "//base",
- "//testing/gtest",
- ]
-}
diff --git a/chromium/components/contextual_search/core/browser/ctr_aggregator.cc b/chromium/components/contextual_search/core/browser/ctr_aggregator.cc
deleted file mode 100644
index f591cc958ec..00000000000
--- a/chromium/components/contextual_search/core/browser/ctr_aggregator.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-// 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.
-
-#include "components/contextual_search/core/browser/ctr_aggregator.h"
-
-#include "base/numerics/safe_conversions.h"
-#include "base/time/time.h"
-
-namespace {
-
-const double kSecondsPerWeek =
- base::Time::kMicrosecondsPerWeek / base::Time::kMicrosecondsPerSecond;
-// Used for validation in debug build. Week numbers are > 2300 as of year 2016.
-// TODO(donnd): reenable this const. https://crbug.com/1094008. See below.
-// const int kReasonableMinWeek = 2000;
-
-} // namespace
-
-namespace contextual_search {
-
-CtrAggregator::CtrAggregator(WeeklyActivityStorage& storage)
- : storage_(storage) {
- base::Time now = base::Time::NowFromSystemTime();
- double now_in_seconds = now.ToDoubleT();
- week_number_ = now_in_seconds / kSecondsPerWeek;
- // TODO(donnd): reenable this DCHECK. Some bots have bad clocks or time
- // settings, causing flaky test failures. https://crbug.com/1094008.
- // DCHECK(week_number_ >= kReasonableMinWeek);
- // NOTE: This initialization may callback into the storage implementation so
- // that needs to be fully initialized when constructing this aggregator.
- storage_.AdvanceToWeek(week_number_);
-}
-
-// Testing only
-CtrAggregator::CtrAggregator(WeeklyActivityStorage& storage, int week_number)
- : storage_(storage), week_number_(week_number) {
- storage_.AdvanceToWeek(week_number_);
-}
-
-CtrAggregator::~CtrAggregator() {}
-
-void CtrAggregator::RecordImpression(bool did_click) {
- storage_.WriteImpressions(week_number_,
- 1 + storage_.ReadImpressions(week_number_));
- if (did_click)
- storage_.WriteClicks(week_number_, 1 + storage_.ReadClicks(week_number_));
-}
-
-int CtrAggregator::GetCurrentWeekNumber() {
- return week_number_;
-}
-
-bool CtrAggregator::HasPreviousWeekData() {
- return storage_.HasData(week_number_ - 1);
-}
-
-int CtrAggregator::GetPreviousWeekImpressions() {
- return storage_.ReadImpressions(week_number_ - 1);
-}
-
-float CtrAggregator::GetPreviousWeekCtr() {
- if (!HasPreviousWeekData())
- return NAN;
-
- int clicks = GetPreviousWeekClicks();
- int impressions = GetPreviousWeekImpressions();
- if (impressions == 0)
- return 0.0;
- return base::saturated_cast<float>(clicks) / impressions;
-}
-
-bool CtrAggregator::HasPrevious28DayData() {
- for (int previous = 1; previous <= kNumWeeksNeededFor28DayData; previous++) {
- if (!storage_.HasData(week_number_ - previous))
- return false;
- }
- return true;
-}
-
-float CtrAggregator::GetPrevious28DayCtr() {
- if (!HasPrevious28DayData())
- return NAN;
-
- int clicks = GetPrevious28DayClicks();
- int impressions = GetPrevious28DayImpressions();
- if (impressions == 0)
- return 0.0;
- return base::saturated_cast<float>(clicks) / impressions;
-}
-
-int CtrAggregator::GetPrevious28DayImpressions() {
- int impressions = 0;
- for (int previous = 1; previous <= kNumWeeksNeededFor28DayData; previous++) {
- impressions += storage_.ReadImpressions(week_number_ - previous);
- }
- return impressions;
-}
-
-// private
-
-int CtrAggregator::GetPreviousWeekClicks() {
- return storage_.ReadClicks(week_number_ - 1);
-}
-
-int CtrAggregator::GetPrevious28DayClicks() {
- int clicks = 0;
- for (int previous = 1; previous <= kNumWeeksNeededFor28DayData; previous++) {
- clicks += storage_.ReadClicks(week_number_ - previous);
- }
- return clicks;
-}
-
-// Testing only
-
-void CtrAggregator::IncrementWeek(int weeks) {
- week_number_ += weeks;
- storage_.AdvanceToWeek(week_number_);
-}
-
-} // namespace contextual_search
diff --git a/chromium/components/contextual_search/core/browser/ctr_aggregator.h b/chromium/components/contextual_search/core/browser/ctr_aggregator.h
deleted file mode 100644
index 4f50cc93abb..00000000000
--- a/chromium/components/contextual_search/core/browser/ctr_aggregator.h
+++ /dev/null
@@ -1,117 +0,0 @@
-// 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.
-
-// Provides aggregation of feature usage by tracking impressions and clicks
-// over 1-week and 28-day intervals. Impressions are views of some UX and
-// clicks are any measured interaction with that UX, yielding CTR -- Click
-// Through Rate.
-// Used by Contextual Search to record impressions of the Bar and CTR of
-// panel opens to use as signals for Tap triggering.
-
-#ifndef COMPONENTS_CONTEXTUAL_SEARCH_CORE_BROWSER_CTR_AGGREGATOR_H_
-#define COMPONENTS_CONTEXTUAL_SEARCH_CORE_BROWSER_CTR_AGGREGATOR_H_
-
-#include "base/gtest_prod_util.h"
-#include "components/contextual_search/core/browser/weekly_activity_storage.h"
-
-namespace contextual_search {
-
-// Number of weeks of data needed for 28 days.
-const int kNumWeeksNeededFor28DayData = 4;
-
-// Usage: Create a CtrAggregator and start recording impressions or reading
-// aggregated data. Get data from the previous week or previous 4-week period
-// that ended with the previous week.
-// A new week starts at an arbitrary time based on seconds since the Epoch.
-// The data from the previous week and previous 28-day period are guaranteed to
-// be complete only if the HasPrevious method returns true. If one of the data
-// accessors is called when the data is not complete invalid data may be
-// returned.
-class CtrAggregator {
- public:
- // Constructs a CtrAggregator using the given |storage| mechanism.
- // Data is stored by |storage| typically on persistent device-local storage.
- // A callback through the storage interface may occur at construction time,
- // so the |storage| must be fully initialized when this constructor is
- // called.
- CtrAggregator(WeeklyActivityStorage& storage);
-
- CtrAggregator(const CtrAggregator&) = delete;
- CtrAggregator& operator=(const CtrAggregator&) = delete;
-
- ~CtrAggregator();
-
- // Records an impression. Records a click if |did_click| is true.
- void RecordImpression(bool did_click);
-
- // Returns the number for the current week. Useful for checking when the
- // current week changes.
- int GetCurrentWeekNumber();
-
- // Returns whether we have the previous week's data for this user.
- bool HasPreviousWeekData();
-
- // Gets the number of impressions from the previous week.
- // Callers must check if there is previous week's data for this user, or
- // invalid data may be returned.
- int GetPreviousWeekImpressions();
-
- // Gets the CTR from the previous week.
- // Callers must check if there is previous week's data for this user, or
- // invalid data may be returned.
- float GetPreviousWeekCtr();
-
- // Returns whether we have data from a 28 day period ending in the previous
- // week.
- bool HasPrevious28DayData();
-
- // Gets the number of impressions from a 28 day period ending in the previous
- // week.
- // Callers must check if there is previous 28 day data for this user, or
- // invalid data may be returned.
- int GetPrevious28DayImpressions();
-
- // Gets the CTR from a 28 day period ending in the previous week.
- // Callers must check if there is previous 28 day data for this user, or
- // invalid data may be returned.
- float GetPrevious28DayCtr();
-
- private:
- // This implementation uses a fixed number of bins to store integer impression
- // and click data for the most recent N weeks, where N = 5 (in order to keep 4
- // complete weeks). Another bin keeps track of the current week being
- // written. Yet another bin records when data was first stored or accessed so
- // we can know when a time period has complete data.
- friend class CtrAggregatorTest;
- FRIEND_TEST_ALL_PREFIXES(CtrAggregatorTest, SimpleOperationTest);
- FRIEND_TEST_ALL_PREFIXES(CtrAggregatorTest, MultiWeekTest);
- FRIEND_TEST_ALL_PREFIXES(CtrAggregatorTest, SkipOneWeekTest);
- FRIEND_TEST_ALL_PREFIXES(CtrAggregatorTest, SkipThreeWeeksTest);
- FRIEND_TEST_ALL_PREFIXES(CtrAggregatorTest, SkipFourWeeksTest);
-
- // Constructs an instance for testing; sets the week.
- CtrAggregator(WeeklyActivityStorage& storage, int week_number);
- // For testing, increments the current week number by |weeks|.
- void IncrementWeek(int weeks);
-
- // Gets the number of clicks from the previous week.
- // Callers must check if there is previous week's data for this user, or
- // invalid data may be returned.
- int GetPreviousWeekClicks();
- // Gets the number of clicks from a 28 day period ending in the previous
- // week.
- // Callers must check if there is previous 28 day data for this user, or
- // invalid data may be returned.
- int GetPrevious28DayClicks();
-
- // Stores the weekly activity data.
- WeeklyActivityStorage& storage_;
-
- // The current week number, expressed as the number of weeks since Epoch.
- int week_number_;
-};
-
-} // namespace contextual_search
-
-#endif // COMPONENTS_CONTEXTUAL_SEARCH_CORE_BROWSER_CTR_AGGREGATOR_H_
diff --git a/chromium/components/contextual_search/core/browser/ctr_aggregator_unittest.cc b/chromium/components/contextual_search/core/browser/ctr_aggregator_unittest.cc
deleted file mode 100644
index 3a3f3d54e64..00000000000
--- a/chromium/components/contextual_search/core/browser/ctr_aggregator_unittest.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-// 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.
-
-#include "components/contextual_search/core/browser/ctr_aggregator.h"
-
-#include <memory>
-#include <unordered_map>
-
-#include "base/gtest_prod_util.h"
-#include "base/values.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using base::Value;
-
-namespace {
-const int kTestWeek = 2500;
-}
-
-namespace contextual_search {
-
-class CtrAggregatorTest : public testing::Test {
- public:
- CtrAggregatorTest() {}
-
- CtrAggregatorTest(const CtrAggregatorTest&) = delete;
- CtrAggregatorTest& operator=(const CtrAggregatorTest&) = delete;
-
- ~CtrAggregatorTest() override {}
-
- class WeeklyActivityStorageStub : public WeeklyActivityStorage {
- public:
- WeeklyActivityStorageStub();
-
- private:
- int ReadClicksForWeekRemainder(int week_remainder) override;
- int ReadImpressionsForWeekRemainder(int week_remainder) override;
- int ReadOldestWeekWritten() override;
- int ReadNewestWeekWritten() override;
- void WriteClicksForWeekRemainder(int week_remainder, int value) override;
- void WriteImpressionsForWeekRemainder(int week_remainder,
- int value) override;
- void WriteOldestWeekWritten(int value) override;
- void WriteNewestWeekWritten(int value) override;
-
- std::unordered_map<int, int> clicks_;
- std::unordered_map<int, int> impressions_;
- int oldest_week_;
- int newest_week_;
- };
-
- // Test helpers
- void Fill4Weeks(); // Fill 4 weeks with 2 impressions, 1 click.
-
- // The class under test.
- std::unique_ptr<CtrAggregator> aggregator_;
-
- protected:
- // The storage stub.
- std::unique_ptr<WeeklyActivityStorage> storage_;
-
- void SetUp() override {
- storage_ = std::make_unique<WeeklyActivityStorageStub>();
- aggregator_.reset(new CtrAggregator(*storage_, kTestWeek));
- }
-
- void TearDown() override {}
-};
-
-CtrAggregatorTest::WeeklyActivityStorageStub::WeeklyActivityStorageStub()
- : WeeklyActivityStorage(4), oldest_week_(0), newest_week_(0) {}
-
-int CtrAggregatorTest::WeeklyActivityStorageStub::ReadClicksForWeekRemainder(
- int week_remainder) {
- return clicks_[week_remainder];
-}
-
-int CtrAggregatorTest::WeeklyActivityStorageStub::
- ReadImpressionsForWeekRemainder(int week_remainder) {
- return impressions_[week_remainder];
-}
-
-int CtrAggregatorTest::WeeklyActivityStorageStub::ReadOldestWeekWritten() {
- return oldest_week_;
-}
-
-int CtrAggregatorTest::WeeklyActivityStorageStub::ReadNewestWeekWritten() {
- return newest_week_;
-}
-
-void CtrAggregatorTest::WeeklyActivityStorageStub::WriteClicksForWeekRemainder(
- int week_remainder,
- int value) {
- clicks_[week_remainder] = value;
-}
-
-void CtrAggregatorTest::WeeklyActivityStorageStub::
- WriteImpressionsForWeekRemainder(int week_remainder, int value) {
- impressions_[week_remainder] = value;
-}
-
-void CtrAggregatorTest::WeeklyActivityStorageStub::WriteOldestWeekWritten(
- int value) {
- oldest_week_ = value;
-}
-
-void CtrAggregatorTest::WeeklyActivityStorageStub::WriteNewestWeekWritten(
- int value) {
- newest_week_ = value;
-}
-
-void CtrAggregatorTest::Fill4Weeks() {
- int weeks_to_record = 4;
- for (int i = 0; i < weeks_to_record; i++) {
- aggregator_->RecordImpression(true);
- aggregator_->RecordImpression(false);
- EXPECT_FALSE(aggregator_->HasPrevious28DayData());
- aggregator_->IncrementWeek(1);
- }
- EXPECT_TRUE(aggregator_->HasPrevious28DayData());
-}
-
-// NaN has the property that it is not equal to itself.
-#define EXPECT_NAN(x) EXPECT_NE(x, x)
-
-TEST_F(CtrAggregatorTest, SimpleOperationTest) {
- aggregator_->RecordImpression(true);
- aggregator_->RecordImpression(false);
- EXPECT_FALSE(aggregator_->HasPreviousWeekData());
- EXPECT_EQ(0, aggregator_->GetPreviousWeekImpressions());
- EXPECT_NAN(aggregator_->GetPreviousWeekCtr());
-
- aggregator_->IncrementWeek(1);
- EXPECT_TRUE(aggregator_->HasPreviousWeekData());
- EXPECT_EQ(2, aggregator_->GetPreviousWeekImpressions());
- EXPECT_FLOAT_EQ(0.5f, aggregator_->GetPreviousWeekCtr());
-}
-
-TEST_F(CtrAggregatorTest, MultiWeekTest) {
- Fill4Weeks();
- aggregator_->RecordImpression(false);
- aggregator_->IncrementWeek(1);
- EXPECT_TRUE(aggregator_->HasPrevious28DayData());
- EXPECT_FLOAT_EQ(static_cast<float>(3.0 / 7),
- aggregator_->GetPrevious28DayCtr());
- aggregator_->RecordImpression(false);
- aggregator_->IncrementWeek(1);
- EXPECT_TRUE(aggregator_->HasPrevious28DayData());
- EXPECT_FLOAT_EQ(static_cast<float>(2.0 / 6),
- aggregator_->GetPrevious28DayCtr());
-}
-
-TEST_F(CtrAggregatorTest, SkipOneWeekTest) {
- Fill4Weeks();
- aggregator_->IncrementWeek(1);
- EXPECT_EQ(0, aggregator_->GetPreviousWeekCtr());
- EXPECT_FLOAT_EQ(static_cast<float>(3.0 / 6),
- aggregator_->GetPrevious28DayCtr());
-}
-
-TEST_F(CtrAggregatorTest, SkipThreeWeeksTest) {
- Fill4Weeks();
- aggregator_->IncrementWeek(3);
- EXPECT_EQ(0, aggregator_->GetPreviousWeekCtr());
- EXPECT_FLOAT_EQ(static_cast<float>(1.0 / 2),
- aggregator_->GetPrevious28DayCtr());
-}
-
-TEST_F(CtrAggregatorTest, SkipFourWeeksTest) {
- Fill4Weeks();
- aggregator_->IncrementWeek(4);
- EXPECT_EQ(0, aggregator_->GetPreviousWeekCtr());
- EXPECT_EQ(0, aggregator_->GetPrevious28DayCtr());
-}
-
-} // namespace contextual_search
diff --git a/chromium/components/contextual_search/core/browser/weekly_activity_storage.cc b/chromium/components/contextual_search/core/browser/weekly_activity_storage.cc
deleted file mode 100644
index b60e9a6bcd5..00000000000
--- a/chromium/components/contextual_search/core/browser/weekly_activity_storage.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// 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.
-
-#include "components/contextual_search/core/browser/weekly_activity_storage.h"
-
-#include <algorithm> // std::min
-
-#include "base/check.h"
-
-namespace {
-
-// Used for validation in debug build. Week numbers are > 2300 as of year 2016.
-// TODO(donnd): reenable this const. https://crbug.com/1094008. See below.
-// const int kReasonableMinWeek = 2000;
-
-} // namespace
-
-namespace contextual_search {
-
-WeeklyActivityStorage::WeeklyActivityStorage(int weeks_needed) {
- weeks_needed_ = weeks_needed;
-}
-
-WeeklyActivityStorage::~WeeklyActivityStorage() {}
-
-int WeeklyActivityStorage::ReadClicks(int week_number) {
- return ReadClicksForWeekRemainder(GetWeekRemainder(week_number));
-}
-
-void WeeklyActivityStorage::WriteClicks(int week_number, int value) {
- WriteClicksForWeekRemainder(GetWeekRemainder(week_number), value);
-}
-
-int WeeklyActivityStorage::ReadImpressions(int week_number) {
- return ReadImpressionsForWeekRemainder(GetWeekRemainder(week_number));
-}
-
-void WeeklyActivityStorage::WriteImpressions(int week_number, int value) {
- WriteImpressionsForWeekRemainder(GetWeekRemainder(week_number), value);
-}
-
-bool WeeklyActivityStorage::HasData(int week_number) {
- return ReadOldestWeekWritten() <= week_number &&
- ReadNewestWeekWritten() >= week_number;
-}
-
-void WeeklyActivityStorage::AdvanceToWeek(int week_number) {
- EnsureHasActivity(week_number);
-}
-
-// private
-
-// Round-robin implementation:
-// GetWeekRemainder and EnsureHasActivity are implemented with a round-robin
-// implementation that simply recycles usage of the last N weeks, where N is
-// less than weeks_needed_.
-
-int WeeklyActivityStorage::GetWeekRemainder(int which_week) {
- return which_week % (weeks_needed_ + 1);
-}
-
-void WeeklyActivityStorage::EnsureHasActivity(int which_week) {
- // TODO(donnd): reenable this DCHECK. Some bots have bad clocks or time
- // settings, causing flaky test failures. https://crbug.com/1094008.
- // DCHECK(which_week > kReasonableMinWeek);
-
- // If still on the newest week we're done!
- int newest_week = ReadNewestWeekWritten();
- if (newest_week == which_week)
- return;
-
- // Update the newest and oldest week written.
- if (which_week > newest_week) {
- WriteNewestWeekWritten(which_week);
- }
- int oldest_week = ReadOldestWeekWritten();
- if (oldest_week == 0 || oldest_week > which_week)
- WriteOldestWeekWritten(which_week);
-
- // Any stale weeks to update?
- if (newest_week == 0)
- return;
-
- // Moved to some new week beyond the newest previously recorded.
- // Since we recycle storage we must clear the new week and all that we
- // may have skipped since our last access.
- int weeks_to_clear = std::min(which_week - newest_week, weeks_needed_);
- int week = which_week;
- while (weeks_to_clear > 0) {
- WriteImpressionsForWeekRemainder(GetWeekRemainder(week), 0);
- WriteClicksForWeekRemainder(GetWeekRemainder(week), 0);
- week--;
- weeks_to_clear--;
- }
-}
-
-} // namespace contextual_search
diff --git a/chromium/components/contextual_search/core/browser/weekly_activity_storage.h b/chromium/components/contextual_search/core/browser/weekly_activity_storage.h
deleted file mode 100644
index 4a1f599b724..00000000000
--- a/chromium/components/contextual_search/core/browser/weekly_activity_storage.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_CONTEXTUAL_SEARCH_CORE_BROWSER_WEEKLY_ACTIVITY_STORAGE_H_
-#define COMPONENTS_CONTEXTUAL_SEARCH_CORE_BROWSER_WEEKLY_ACTIVITY_STORAGE_H_
-
-namespace contextual_search {
-
-// An abstract class that stores weekly user interaction data in device-specific
-// integer storage. Only a limited storage window is supported, set through the
-// constructor. Allows callers to read and write user actions to persistent
-// storage on the device by overriding the ReadStorage and WriteStorage calls.
-// A user view of some UX is an "Impression", and user interaction is considered
-// a "Click" even if the triggering gesture was something else. Together they
-// produce the Click-Through-Rate, or CTR.
-class WeeklyActivityStorage {
- public:
- // Constructs an instance that will manage at least |weeks_needed| weeks of
- // data.
- WeeklyActivityStorage(int weeks_needed);
-
- WeeklyActivityStorage(const WeeklyActivityStorage&) = delete;
- WeeklyActivityStorage& operator=(const WeeklyActivityStorage&) = delete;
-
- virtual ~WeeklyActivityStorage();
-
- // Advances the accessible storage range to end at the given |week_number|.
- // Since only a limited number of storage weeks are supported, advancing to
- // a different week makes data from weeks than the range size inaccessible.
- // This must be called for each week before reading or writing any data
- // for that week.
- // HasData will return true for all the weeks that still have accessible data.
- void AdvanceToWeek(int week_number);
-
- // Returns the number of clicks for the given week.
- int ReadClicks(int week_number);
- // Writes |value| into the number of clicks for the given |week_number|.
- void WriteClicks(int week_number, int value);
-
- // Returns the number of impressions for the given week.
- int ReadImpressions(int week_number);
- // Writes |value| into the number of impressions for the given |week_number|.
- void WriteImpressions(int week_number, int value);
-
- // Returns whether the given |week_number| has data, based on whether
- // InitData has ever been called for that week.
- bool HasData(int week_number);
-
- // Reads and returns values from persistent storage.
- // If there is no stored value then 0 is returned.
- virtual int ReadClicksForWeekRemainder(int week_remainder) = 0;
- virtual int ReadImpressionsForWeekRemainder(int week_remainder) = 0;
- virtual int ReadOldestWeekWritten() = 0;
- virtual int ReadNewestWeekWritten() = 0;
- // Writes values to persistent storage.
- virtual void WriteClicksForWeekRemainder(int week_remainder, int value) = 0;
- virtual void WriteImpressionsForWeekRemainder(int week_remainder,
- int value) = 0;
- virtual void WriteOldestWeekWritten(int value) = 0;
- virtual void WriteNewestWeekWritten(int value) = 0;
-
- private:
- // Returns the key to bin information about the given week |which_week|.
- int GetWeekRemainder(int which_week);
-
- // Ensures that activity data is initialized for the given week |which_week|.
- void EnsureHasActivity(int which_week);
-
- // The number of weeks of data that this instance needs to support.
- int weeks_needed_;
-};
-
-} // namespace contextual_search
-
-#endif // COMPONENTS_CONTEXTUAL_SEARCH_CORE_BROWSER_WEEKLY_ACTIVITY_STORAGE_H_
diff --git a/chromium/components/continuous_search/browser/search_result_extractor_client.cc b/chromium/components/continuous_search/browser/search_result_extractor_client.cc
index 0bfa4518f92..3faa3f065b8 100644
--- a/chromium/components/continuous_search/browser/search_result_extractor_client.cc
+++ b/chromium/components/continuous_search/browser/search_result_extractor_client.cc
@@ -34,8 +34,8 @@ void SearchResultExtractorClient::RequestData(
content::WebContents* web_contents,
const std::vector<mojom::ResultType>& result_types,
RequestDataCallback callback) {
- if (!web_contents || !web_contents->GetMainFrame() ||
- !web_contents->GetMainFrame()->GetRemoteAssociatedInterfaces()) {
+ if (!web_contents || !web_contents->GetPrimaryMainFrame() ||
+ !web_contents->GetPrimaryMainFrame()->GetRemoteAssociatedInterfaces()) {
std::move(callback).Run(SearchResultExtractorClientStatus::kWebContentsGone,
mojom::CategoryResults::New());
return;
@@ -50,8 +50,9 @@ void SearchResultExtractorClient::RequestData(
}
mojo::AssociatedRemote<mojom::SearchResultExtractor> extractor;
- web_contents->GetMainFrame()->GetRemoteAssociatedInterfaces()->GetInterface(
- extractor.BindNewEndpointAndPassReceiver());
+ web_contents->GetPrimaryMainFrame()
+ ->GetRemoteAssociatedInterfaces()
+ ->GetInterface(extractor.BindNewEndpointAndPassReceiver());
mojom::SearchResultExtractor* extractor_ptr = extractor.get();
extractor_ptr->ExtractCurrentSearchResults(
diff --git a/chromium/components/continuous_search/browser/search_result_extractor_client_unittest.cc b/chromium/components/continuous_search/browser/search_result_extractor_client_unittest.cc
index cb36b90e552..2b9ebd6a98d 100644
--- a/chromium/components/continuous_search/browser/search_result_extractor_client_unittest.cc
+++ b/chromium/components/continuous_search/browser/search_result_extractor_client_unittest.cc
@@ -74,7 +74,7 @@ class SearchResultExtractorClientRenderViewHostTest
// interface.
void OverrideInterface(FakeSearchResultExtractor* extractor) {
web_contents()
- ->GetMainFrame()
+ ->GetPrimaryMainFrame()
->GetRemoteAssociatedInterfaces()
->OverrideBinderForTesting(
mojom::SearchResultExtractor::Name_,
diff --git a/chromium/components/crash/OWNERS b/chromium/components/crash/OWNERS
index 5991c80307a..7341deaf1b6 100644
--- a/chromium/components/crash/OWNERS
+++ b/chromium/components/crash/OWNERS
@@ -1,4 +1,3 @@
-jochen@chromium.org
mark@chromium.org
rsesek@chromium.org
thestig@chromium.org
diff --git a/chromium/components/crash/android/BUILD.gn b/chromium/components/crash/android/BUILD.gn
index 3a3788beca5..63a0d916db9 100644
--- a/chromium/components/crash/android/BUILD.gn
+++ b/chromium/components/crash/android/BUILD.gn
@@ -23,6 +23,8 @@ java_cpp_enum("java_enums_srcjar") {
android_library("java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/minidump_uploader:minidump_uploader_java",
"//components/version_info/android:version_constants_java",
"//third_party/androidx:androidx_annotation_annotation_java",
@@ -62,7 +64,6 @@ java_library("junit") {
":anr_collector_java",
":anr_data_proto_java",
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//third_party/android_deps:robolectric_all_java",
@@ -102,7 +103,10 @@ generate_jni("java_handler_jni_headers") {
}
android_library("handler_java") {
- deps = [ "//base:base_java" ]
+ deps = [
+ "//base:jni_java",
+ "//build/android:build_java",
+ ]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
sources = _java_handler_jni_sources
}
diff --git a/chromium/components/crash/core/app/breakpad_linux.cc b/chromium/components/crash/core/app/breakpad_linux.cc
index 823e49a234e..3c28019ae3d 100644
--- a/chromium/components/crash/core/app/breakpad_linux.cc
+++ b/chromium/components/crash/core/app/breakpad_linux.cc
@@ -26,6 +26,7 @@
#include <string>
#include <tuple>
+#include "base/allocator/partition_allocator/oom.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/debug/dump_without_crashing.h"
@@ -36,7 +37,6 @@
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/global_descriptors.h"
-#include "base/process/memory.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -104,7 +104,8 @@ namespace {
// except pass it along) and we don't have functions to deal with time_t's well,
// while we do have functions to deal with uint64_t's.
uint64_t g_crash_loop_before_time = 0;
-#else
+#endif
+#if !BUILDFLAG(IS_CHROMEOS)
char* g_upload_url = nullptr;
void SetUploadURL(const std::string& url) {
DCHECK(!g_upload_url);
@@ -246,7 +247,7 @@ void my_uint64tos(char* output, uint64_t i, unsigned i_len) {
output[index - 1] = '0' + (i % 10);
}
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
bool my_isxdigit(char c) {
return base::IsAsciiDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f');
}
@@ -293,7 +294,7 @@ void SetClientIdFromCommandLine(const base::CommandLine& command_line) {
}
// MIME substrings.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
const char g_sep[] = ":";
#endif
const char g_rn[] = "\r\n";
@@ -474,7 +475,7 @@ void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) {
size));
}
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
// This subclass is used on Chromium OS to report crashes in a format easy for
// the central crash reporting facility to understand.
// Format is <name>:<data length in decimal>:<data>
@@ -587,7 +588,7 @@ void CrashReporterWriter::AddFileContents(const char* filename_msg,
AddItem(file_data, file_size);
Flush();
}
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_ANDROID)
// Writes the "package" field, which is in the format:
@@ -723,7 +724,7 @@ bool CrashDone(const MinidumpDescriptor& minidump,
info.distro_length = my_strlen(base::g_linux_distro);
info.upload = upload;
info.process_start_time = g_process_start_time;
- info.oom_size = base::g_oom_size;
+ info.oom_size = partition_alloc::g_oom_size;
info.pid = g_pid;
info.crash_keys = crash_reporter::internal::GetCrashKeyStorage();
HandleCrashDump(info);
@@ -1064,8 +1065,8 @@ class NonBrowserCrashHandler : public google_breakpad::CrashGenerationClient {
iov[2].iov_len = sizeof(fds[0]);
iov[3].iov_base = &g_process_start_time;
iov[3].iov_len = sizeof(g_process_start_time);
- iov[4].iov_base = &base::g_oom_size;
- iov[4].iov_len = sizeof(base::g_oom_size);
+ iov[4].iov_base = &partition_alloc::g_oom_size;
+ iov[4].iov_len = sizeof(partition_alloc::g_oom_size);
google_breakpad::SerializedNonAllocatingMap* serialized_map;
iov[5].iov_len = crash_reporter::internal::GetCrashKeyStorage()->Serialize(
const_cast<const google_breakpad::SerializedNonAllocatingMap**>(
@@ -1265,7 +1266,7 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
const char* exe_buf,
int upload_status_fd,
google_breakpad::PageAllocator* allocator) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
// CrOS uses crash_reporter instead of wget to report crashes,
// it needs to know where the crash dump lives and the pid and uid of the
// crashing process.
@@ -1297,11 +1298,12 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
my_strlcat(exe_flag, exe_buf, buf_len);
char* crash_loop_before_flag = nullptr;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
if (g_crash_loop_before_time != 0) {
crash_loop_before_flag = StringFromPrefixAndUint(
"--crash_loop_before=", g_crash_loop_before_time, allocator);
}
-
+#endif // if BUILDFLAG(IS_CHROMEOS_ASH)
const char* args[] = {
kCrashReporterBinary,
chrome_flag,
@@ -1314,7 +1316,7 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
static const char msg[] = "Cannot upload crash dump: cannot exec "
"/sbin/crash_reporter\n";
-#else // BUILDFLAG(IS_CHROMEOS_ASH)
+#else // BUILDFLAG(IS_CHROMEOS)
// Compress |dumpfile| with gzip.
const pid_t gzip_child = sys_fork();
@@ -1404,7 +1406,7 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
};
static const char msg[] = "Cannot upload crash dump: cannot exec "
"/usr/bin/wget\n";
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif // BUILDFLAG(IS_CHROMEOS)
execve(args[0], const_cast<char**>(args), environ);
WriteLog(msg, sizeof(msg) - 1);
@@ -1455,7 +1457,7 @@ bool IsValidCrashReportId(const char* buf, size_t bytes_read,
WriteLog(msg, sizeof(msg) - 1);
return false;
}
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
// See kSuccessMagic in platform2/crash-reporter/chrome_collector.cc.
return my_strcmp(buf, "_sys_cr_finished") == 0;
#else
@@ -1472,7 +1474,7 @@ void HandleCrashReportId(const char* buf, size_t bytes_read,
size_t expected_len) {
WriteNewline();
if (!IsValidCrashReportId(buf, bytes_read, expected_len)) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
static const char msg[] =
"System crash_reporter failed to process crash report.";
#else
@@ -1488,7 +1490,7 @@ void HandleCrashReportId(const char* buf, size_t bytes_read,
return;
}
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
static const char msg[] = "Crash dump received by crash_reporter\n";
WriteLog(msg, sizeof(msg) - 1);
#else
@@ -1519,7 +1521,7 @@ void HandleCrashReportId(const char* buf, size_t bytes_read,
#endif
}
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
const char* GetCrashingProcessName(const BreakpadInfo& info,
google_breakpad::PageAllocator* allocator) {
// Symlink to process binary is at /proc/###/exe.
@@ -1547,7 +1549,7 @@ const char* GetCrashingProcessName(const BreakpadInfo& info,
// Either way too long, or a read error.
return "chrome-crash-unknown-process";
}
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif // BUILDFLAG(IS_CHROMEOS)
// Attempts to close all open file descriptors other than stdin, stdout and
// stderr (0, 1, and 2).
@@ -1582,7 +1584,7 @@ void HandleCrashDump(const BreakpadInfo& info) {
return;
}
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
// Grab the crashing process' name now, when it should still be available.
// If we try to do this later in our grandchild the crashing process has
// already terminated.
@@ -1723,7 +1725,7 @@ void HandleCrashDump(const BreakpadInfo& info) {
// <dump contents>
// \r\n BOUNDARY -- \r\n
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
CrashReporterWriter writer(temp_file_fd);
#else
MimeWriter writer(temp_file_fd, mime_boundary);
@@ -2026,7 +2028,7 @@ void InitCrashReporter(const std::string& process_type) {
#endif
process_type.empty();
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
SetUploadURL(GetCrashReporterClient()->GetUploadUrl());
#endif
diff --git a/chromium/components/crash/core/app/crashpad.cc b/chromium/components/crash/core/app/crashpad.cc
index 6da6be46cee..9058d56e8bd 100644
--- a/chromium/components/crash/core/app/crashpad.cc
+++ b/chromium/components/crash/core/app/crashpad.cc
@@ -209,7 +209,7 @@ bool InitializeCrashpadImpl(bool initial_client,
g_database =
crashpad::CrashReportDatabase::Initialize(database_path).release();
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_IOS)
CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
SetUploadConsent(crash_reporter_client->GetCollectStatsConsent());
#endif
@@ -222,26 +222,28 @@ bool InitializeCrashpadImpl(bool initial_client,
bool InitializeCrashpad(bool initial_client, const std::string& process_type) {
return InitializeCrashpadImpl(initial_client, process_type, std::string(),
base::FilePath(), std::vector<std::string>(),
- false);
+ /*embedded_handler=*/false);
}
#if BUILDFLAG(IS_WIN)
-void InitializeCrashpadWithEmbeddedHandler(bool initial_client,
+bool InitializeCrashpadWithEmbeddedHandler(bool initial_client,
const std::string& process_type,
const std::string& user_data_dir,
const base::FilePath& exe_path) {
- InitializeCrashpadImpl(initial_client, process_type, user_data_dir, exe_path,
- std::vector<std::string>(), true);
+ return InitializeCrashpadImpl(initial_client, process_type, user_data_dir,
+ exe_path, std::vector<std::string>(),
+ /*embedded_handler=*/true);
}
-void InitializeCrashpadWithDllEmbeddedHandler(
+bool InitializeCrashpadWithDllEmbeddedHandler(
bool initial_client,
const std::string& process_type,
const std::string& user_data_dir,
const base::FilePath& exe_path,
const std::vector<std::string>& initial_arguments) {
- InitializeCrashpadImpl(initial_client, process_type, user_data_dir, exe_path,
- initial_arguments, true);
+ return InitializeCrashpadImpl(initial_client, process_type, user_data_dir,
+ exe_path, initial_arguments,
+ /*embedded_handler=*/true);
}
#endif // BUILDFLAG(IS_WIN)
diff --git a/chromium/components/crash/core/app/crashpad.h b/chromium/components/crash/core/app/crashpad.h
index 55322a1d41b..35668b44277 100644
--- a/chromium/components/crash/core/app/crashpad.h
+++ b/chromium/components/crash/core/app/crashpad.h
@@ -85,7 +85,7 @@ bool InitializeCrashpad(bool initial_client, const std::string& process_type);
// current executable if |exe_path| is empty with a command line argument of
// --type=crashpad-handler. If |user_data_dir| is non-empty, it is added to the
// handler's command line for use by Chrome Crashpad extensions.
-void InitializeCrashpadWithEmbeddedHandler(bool initial_client,
+bool InitializeCrashpadWithEmbeddedHandler(bool initial_client,
const std::string& process_type,
const std::string& user_data_dir,
const base::FilePath& exe_path);
@@ -97,7 +97,7 @@ void InitializeCrashpadWithEmbeddedHandler(bool initial_client,
// In this situation the exe_path is not sufficient to allow spawning a crash
// handler through the DLL so |initial_arguments| needs to be passed to
// specify the DLL entry point.
-void InitializeCrashpadWithDllEmbeddedHandler(
+bool InitializeCrashpadWithDllEmbeddedHandler(
bool initial_client,
const std::string& process_type,
const std::string& user_data_dir,
diff --git a/chromium/components/crash/core/app/crashpad_ios.mm b/chromium/components/crash/core/app/crashpad_ios.mm
index d3db34c8fb3..83d13ef4b96 100644
--- a/chromium/components/crash/core/app/crashpad_ios.mm
+++ b/chromium/components/crash/core/app/crashpad_ios.mm
@@ -166,7 +166,10 @@ bool PlatformCrashpadInitialization(
@autoreleasepool {
CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
crash_reporter_client->GetCrashDumpLocation(database_path);
- std::string url = crash_reporter_client->GetUploadUrl();
+ // Don't pass `url` to extensions since they never upload minidumps.
+ std::string url = [NSBundle.mainBundle.bundlePath hasSuffix:@"appex"]
+ ? ""
+ : crash_reporter_client->GetUploadUrl();
return GetCrashpadClient().StartCrashpadInProcessHandler(
*database_path, url, GetProcessSimpleAnnotations());
} // @autoreleasepool
diff --git a/chromium/components/crash/core/app/crashpad_linux.cc b/chromium/components/crash/core/app/crashpad_linux.cc
index 32e2038e15a..39fb479eba5 100644
--- a/chromium/components/crash/core/app/crashpad_linux.cc
+++ b/chromium/components/crash/core/app/crashpad_linux.cc
@@ -117,7 +117,7 @@ bool PlatformCrashpadInitialization(
// to ChromeOS's /sbin/crash_reporter which in turn passes the dump to
// crash_sender which handles the upload.
std::string url;
-#if !(BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#if !BUILDFLAG(IS_CHROMEOS)
url = crash_reporter_client->GetUploadUrl();
#else
url = std::string();
@@ -156,7 +156,7 @@ bool PlatformCrashpadInitialization(
annotations["build_time_millis"] = base::NumberToString(build_time);
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
// Chromium OS: save board and builder path for 'tast symbolize'.
annotations["chromeos-board"] = base::SysInfo::GetLsbReleaseBoard();
std::string builder_path;
@@ -181,7 +181,7 @@ bool PlatformCrashpadInitialization(
// contain these annotations.
arguments.push_back("--monitor-self-annotation=ptype=crashpad-handler");
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
arguments.push_back("--use-cros-crash-reporter");
if (crash_reporter_client->IsRunningUnattended()) {
diff --git a/chromium/components/crash/core/app/crashpad_win.cc b/chromium/components/crash/core/app/crashpad_win.cc
index d2354b84f3a..80f33dc5e2f 100644
--- a/chromium/components/crash/core/app/crashpad_win.cc
+++ b/chromium/components/crash/core/app/crashpad_win.cc
@@ -75,6 +75,8 @@ bool PlatformCrashpadInitialization(
CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
+ bool initialized = false;
+
if (initial_client) {
std::wstring database_path_str;
if (crash_reporter_client->GetCrashDumpLocation(&database_path_str))
@@ -140,22 +142,28 @@ bool PlatformCrashpadInitialization(
arguments.push_back(std::string("--monitor-self-annotation=ptype=") +
switches::kCrashpadHandler);
- GetCrashpadClient().StartHandler(exe_file, *database_path, metrics_path,
- url, process_annotations, arguments, false,
- false);
+ initialized = GetCrashpadClient().StartHandler(
+ exe_file, *database_path, metrics_path, url, process_annotations,
+ arguments, /*restartable=*/false, /*asynchronous_start=*/false);
- // If we're the browser, push the pipe name into the environment so child
- // processes can connect to it. If we inherited another crashpad_handler's
- // pipe name, we'll overwrite it here.
- env->SetVar(kPipeNameVar,
- base::WideToUTF8(GetCrashpadClient().GetHandlerIPCPipe()));
+ if (initialized) {
+ // If we're the browser, push the pipe name into the environment so child
+ // processes can connect to it. If we inherited another crashpad_handler's
+ // pipe name, we'll overwrite it here.
+ env->SetVar(kPipeNameVar,
+ base::WideToUTF8(GetCrashpadClient().GetHandlerIPCPipe()));
+ }
} else {
std::string pipe_name_utf8;
if (env->GetVar(kPipeNameVar, &pipe_name_utf8)) {
- GetCrashpadClient().SetHandlerIPCPipe(base::UTF8ToWide(pipe_name_utf8));
+ initialized = GetCrashpadClient().SetHandlerIPCPipe(
+ base::UTF8ToWide(pipe_name_utf8));
}
}
+ if (!initialized)
+ return false;
+
if (crash_reporter_client->GetShouldDumpLargerDumps()) {
const uint32_t kIndirectMemoryLimit = 4 * 1024 * 1024;
crashpad::CrashpadInfo::GetCrashpadInfo()
diff --git a/chromium/components/crash/core/browser/crashes_ui_util.cc b/chromium/components/crash/core/browser/crashes_ui_util.cc
index 454bfe3754e..9484ed6ddbc 100644
--- a/chromium/components/crash/core/browser/crashes_ui_util.cc
+++ b/chromium/components/crash/core/browser/crashes_ui_util.cc
@@ -69,26 +69,26 @@ std::string UploadInfoStateAsString(UploadList::UploadInfo::State state) {
return "";
}
-void UploadListToValue(UploadList* upload_list, base::ListValue* out_value) {
+void UploadListToValue(UploadList* upload_list, base::Value::List* out_value) {
std::vector<UploadList::UploadInfo> crashes;
upload_list->GetUploads(50, &crashes);
for (const auto& info : crashes) {
- std::unique_ptr<base::DictionaryValue> crash(new base::DictionaryValue());
- crash->SetStringKey("id", info.upload_id);
+ base::Value::Dict crash;
+ crash.Set("id", info.upload_id);
if (info.state == UploadList::UploadInfo::State::Uploaded) {
- crash->SetStringKey("upload_time",
- base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
- info.upload_time)));
+ crash.Set("upload_time",
+ base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(info.upload_time)));
}
if (!info.capture_time.is_null()) {
- crash->SetStringKey("capture_time",
- base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
- info.capture_time)));
+ crash.Set("capture_time",
+ base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(info.capture_time)));
}
- crash->SetStringKey("local_id", info.local_id);
- crash->SetStringKey("state", UploadInfoStateAsString(info.state));
- crash->SetStringKey("file_size", base::UTF16ToUTF8(info.file_size));
+ crash.Set("local_id", info.local_id);
+ crash.Set("state", UploadInfoStateAsString(info.state));
+ crash.Set("file_size", base::UTF16ToUTF8(info.file_size));
out_value->Append(std::move(crash));
}
}
diff --git a/chromium/components/crash/core/browser/crashes_ui_util.h b/chromium/components/crash/core/browser/crashes_ui_util.h
index c49ee025d86..c7f0bf75d39 100644
--- a/chromium/components/crash/core/browser/crashes_ui_util.h
+++ b/chromium/components/crash/core/browser/crashes_ui_util.h
@@ -7,9 +7,7 @@
#include <stddef.h>
-namespace base {
-class ListValue;
-}
+#include "base/values.h"
class UploadList;
@@ -38,7 +36,7 @@ extern const char kCrashesUIUpdateCrashList[];
extern const char kCrashesUIRequestSingleCrashUpload[];
// Converts and appends the most recent uploads to |out_value|.
-void UploadListToValue(UploadList* upload_list, base::ListValue* out_value);
+void UploadListToValue(UploadList* upload_list, base::Value::List* out_value);
} // namespace crash_reporter
diff --git a/chromium/components/crash/core/common/crash_key_base_support.cc b/chromium/components/crash/core/common/crash_key_base_support.cc
index 8aa5c66e4a3..cd563b9fdda 100644
--- a/chromium/components/crash/core/common/crash_key_base_support.cc
+++ b/chromium/components/crash/core/common/crash_key_base_support.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <ostream>
+#include "base/check_op.h"
#include "base/debug/crash_logging.h"
#include "components/crash/core/common/crash_key.h"
diff --git a/chromium/components/cronet/BUILD.gn b/chromium/components/cronet/BUILD.gn
index 5be0ce232c1..8c42444bd41 100644
--- a/chromium/components/cronet/BUILD.gn
+++ b/chromium/components/cronet/BUILD.gn
@@ -161,9 +161,9 @@ if (is_android) {
}
if (is_fuchsia) {
- use_cfv2 = false
+ use_cfv1 = false
additional_manifest_fragments =
- [ "//build/config/fuchsia/test/network_capabilities.test-cmx" ]
+ [ "//build/config/fuchsia/test/network.shard.test-cml" ]
}
}
diff --git a/chromium/components/cronet/android/BUILD.gn b/chromium/components/cronet/android/BUILD.gn
index 39346101999..31de1a5155e 100644
--- a/chromium/components/cronet/android/BUILD.gn
+++ b/chromium/components/cronet/android/BUILD.gn
@@ -274,7 +274,10 @@ android_library("cronet_impl_common_base_java") {
"java/src/org/chromium/net/impl/CronetEngineBase.java",
"java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java",
"java/src/org/chromium/net/impl/CronetExceptionImpl.java",
+ "java/src/org/chromium/net/impl/CronetLogger.java",
+ "java/src/org/chromium/net/impl/CronetLoggerFactory.java",
"java/src/org/chromium/net/impl/NetworkExceptionImpl.java",
+ "java/src/org/chromium/net/impl/NoOpLogger.java",
"java/src/org/chromium/net/impl/Preconditions.java",
"java/src/org/chromium/net/impl/QuicExceptionImpl.java",
"java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java",
@@ -362,7 +365,7 @@ cronet_impl_native_java_deps_to_package = [
":cronet_urlconnection_impl_java",
"//base:base_java",
"//base:jni_java",
- "//build/android:build_config_java",
+ "//build/android:build_java",
"//net/android:net_java",
"//url:url_java",
]
@@ -407,7 +410,7 @@ android_library("cronet_impl_native_base_java") {
":cronet_api_java",
":cronet_impl_common_base_java",
"//base:jni_java",
- "//build/android:build_config_java",
+ "//build/android:build_java",
"//third_party/android_deps:com_google_code_findbugs_jsr305_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
@@ -434,7 +437,16 @@ android_resources("cronet_sample_apk_resources") {
"sample/res/values/strings.xml",
]
android_manifest = "sample/AndroidManifest.xml"
- deps = [ "//third_party/android_deps:android_support_v7_appcompat_java" ]
+ deps = [
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+ "//third_party/androidx:androidx_drawerlayout_drawerlayout_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
+ "//third_party/androidx:androidx_interpolator_interpolator_java",
+ "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
+ "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
+ "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
+ ]
}
android_library("cronet_sample_apk_java") {
@@ -448,8 +460,9 @@ android_library("cronet_sample_apk_java") {
":cronet_sample_apk_resources",
":package_api_java",
":package_impl_native_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
+ "//third_party/androidx:androidx_lifecycle_lifecycle_viewmodel_java",
]
}
@@ -786,7 +799,6 @@ if (!is_component_build) {
"//third_party/junit",
]
- enable_multidex = false
if (!is_java_debug) {
proguard_enabled = true
proguard_configs = [ "sample/javatests/proguard.cfg" ]
@@ -888,6 +900,8 @@ if (!is_component_build) {
":cronet_impl_all_java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
+ "//build/android:build_java",
"//net/android:net_java_test_support",
"//third_party/android_sdk:android_test_base_java",
"//third_party/junit",
@@ -920,6 +934,8 @@ if (!is_component_build) {
":cronet_api_java",
":cronet_impl_all_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/android_sdk:android_test_base_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/junit",
@@ -958,6 +974,7 @@ if (!is_component_build) {
":cronet_fake_javatests",
":cronet_test_apk_java",
"//base:base_java",
+ "//base:jni_java",
"//base:base_java_test_support",
"//net/android:embedded_test_server_aidl_java",
@@ -997,6 +1014,7 @@ if (!is_component_build) {
"test/javatests/src/org/chromium/net/TestNetworkQualityThroughputListener.java",
"test/javatests/src/org/chromium/net/UploadDataProvidersTest.java",
"test/javatests/src/org/chromium/net/UrlResponseInfoTest.java",
+ "test/javatests/src/org/chromium/net/impl/CronetLoggerTest.java",
"test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java",
"test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java",
"test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java",
@@ -1032,7 +1050,6 @@ if (!is_component_build) {
# it depends on basically all source files.
enable_lint = true
lint_suppressions_file = "lint-suppressions.xml"
- lint_baseline_file = "lint-baseline.xml"
# Still needs to support KitKat. See crbug.com/1042122.
lint_min_sdk_version = 19
@@ -1060,6 +1077,8 @@ if (!is_component_build) {
":cronet_test_apk_resources",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
+ "//build/android:build_java",
"//net/android:net_java",
"//net/android:net_java_test_support",
"//third_party/android_sdk:android_test_base_java",
@@ -1146,6 +1165,8 @@ if (!is_component_build) {
":cronet_test_apk_resources",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/android_sdk:android_test_base_java",
"//third_party/android_sdk:android_test_mock_java",
"//third_party/android_support_test_runner:runner_java",
@@ -1186,6 +1207,8 @@ if (!is_component_build) {
":cronet_javatests",
":cronet_test_apk_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/android_sdk:android_test_mock_java",
"//third_party/junit",
]
@@ -1289,6 +1312,7 @@ if (!is_component_build) {
jar_src("jar_cronet_impl_native_java_source") {
src_search_dirs = [
"//base/android/java/src",
+ "//build/android/java/src",
"//components/cronet/android/java/src",
"//net/android/java/src",
"//url/android/java/src",
@@ -1296,6 +1320,8 @@ if (!is_component_build) {
source_deps = [
":cronet_impl_native_base_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//net/android:net_java",
"//url:url_java",
]
diff --git a/chromium/components/crx_file/crx_creator.cc b/chromium/components/crx_file/crx_creator.cc
index e46697dcef7..e56183cbc11 100644
--- a/chromium/components/crx_file/crx_creator.cc
+++ b/chromium/components/crx_file/crx_creator.cc
@@ -81,7 +81,8 @@ CreatorResult SignArchiveAndCreateHeader(const base::FilePath& output_path,
// through, run ZIP through.
auto signer = crypto::SignatureCreator::Create(
signing_key, crypto::SignatureCreator::HashAlgorithm::SHA256);
- signer->Update(kSignatureContext, std::size(kSignatureContext));
+ signer->Update(reinterpret_cast<const uint8_t*>(kSignatureContext),
+ std::size(kSignatureContext));
signer->Update(signed_header_size_octets,
std::size(signed_header_size_octets));
signer->Update(
diff --git a/chromium/components/crx_file/crx_file.h b/chromium/components/crx_file/crx_file.h
index 4e258e125b7..22c46bedb2f 100644
--- a/chromium/components/crx_file/crx_file.h
+++ b/chromium/components/crx_file/crx_file.h
@@ -11,7 +11,7 @@ namespace crx_file {
constexpr char kCrxFileHeaderMagic[] = "Cr24";
constexpr char kCrxDiffFileHeaderMagic[] = "CrOD";
constexpr int kCrxFileHeaderMagicSize = 4;
-constexpr unsigned char kSignatureContext[] = u8"CRX3 SignedData";
+constexpr char kSignatureContext[] = "CRX3 SignedData";
} // namespace crx_file
diff --git a/chromium/components/crx_file/crx_verifier.cc b/chromium/components/crx_file/crx_verifier.cc
index c7b80d71373..75a2c01c022 100644
--- a/chromium/components/crx_file/crx_verifier.cc
+++ b/chromium/components/crx_file/crx_verifier.cc
@@ -188,7 +188,7 @@ VerifierResult VerifyCrx3(
base::as_bytes(base::make_span(sig)),
base::as_bytes(base::make_span(key))))
return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED;
- v->VerifyUpdate(kSignatureContext);
+ v->VerifyUpdate(base::as_bytes(base::make_span(kSignatureContext)));
v->VerifyUpdate(header_size_octets);
v->VerifyUpdate(base::as_bytes(base::make_span(signed_header_data_str)));
verifiers.push_back(std::move(v));
diff --git a/chromium/components/custom_handlers/protocol_handler.cc b/chromium/components/custom_handlers/protocol_handler.cc
index aedebf65620..33181158cfa 100644
--- a/chromium/components/custom_handlers/protocol_handler.cc
+++ b/chromium/components/custom_handlers/protocol_handler.cc
@@ -5,12 +5,12 @@
#include "components/custom_handlers/protocol_handler.h"
#include "base/json/values_util.h"
+#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_client.h"
#include "content/public/common/origin_util.h"
-#include "net/base/escape.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h"
#include "third_party/blink/public/common/scheme_registry.h"
@@ -133,7 +133,7 @@ GURL ProtocolHandler::TranslateUrl(const GURL& url) const {
std::string translatedUrlSpec(url_.spec());
base::ReplaceFirstSubstringAfterOffset(
&translatedUrlSpec, 0, "%s",
- net::EscapeQueryParamValue(url.spec(), false));
+ base::EscapeQueryParamValue(url.spec(), false));
return GURL(translatedUrlSpec);
}
diff --git a/chromium/components/custom_handlers/protocol_handler_registry.cc b/chromium/components/custom_handlers/protocol_handler_registry.cc
index 394408003db..708058d446b 100644
--- a/chromium/components/custom_handlers/protocol_handler_registry.cc
+++ b/chromium/components/custom_handlers/protocol_handler_registry.cc
@@ -62,6 +62,23 @@ GURL TranslateUrl(
// ProtocolHandlerRegistry -----------------------------------------------------
+std::unique_ptr<ProtocolHandlerRegistry> ProtocolHandlerRegistry::Create(
+ PrefService* prefs,
+ std::unique_ptr<Delegate> delegate) {
+ auto registry =
+ std::make_unique<ProtocolHandlerRegistry>(prefs, std::move(delegate));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // If installing defaults, they must be installed prior calling
+ // InitProtocolSettings.
+ registry->InstallDefaultsForChromeOS();
+#endif
+
+ registry->InitProtocolSettings();
+
+ return registry;
+}
+
ProtocolHandlerRegistry::ProtocolHandlerRegistry(
PrefService* prefs,
std::unique_ptr<Delegate> delegate)
diff --git a/chromium/components/custom_handlers/protocol_handler_registry.h b/chromium/components/custom_handlers/protocol_handler_registry.h
index 887c0c316d4..428cfd96923 100644
--- a/chromium/components/custom_handlers/protocol_handler_registry.h
+++ b/chromium/components/custom_handlers/protocol_handler_registry.h
@@ -75,10 +75,15 @@ class ProtocolHandlerRegistry : public KeyedService {
virtual void OnProtocolHandlerRegistryChanged() = 0;
};
- // Creates a new instance.
+ // Intended for testing use only.
ProtocolHandlerRegistry(PrefService* prefs,
std::unique_ptr<Delegate> delegate);
+ // Creates a new instance and performs initialization.
+ static std::unique_ptr<ProtocolHandlerRegistry> Create(
+ PrefService* prefs,
+ std::unique_ptr<Delegate> delegate);
+
ProtocolHandlerRegistry(const ProtocolHandlerRegistry&) = delete;
ProtocolHandlerRegistry& operator=(const ProtocolHandlerRegistry&) = delete;
@@ -211,16 +216,6 @@ class ProtocolHandlerRegistry : public KeyedService {
// load command was issued, otherwise the command will be ignored.
void AddPredefinedHandler(const ProtocolHandler& handler);
- // Install default protocol handlers for chromeos which must be done
- // prior to calling InitProtocolSettings.
- // TODO(jfernandez): This method is declared as public because it's invoked by
- // the ProtocolHandlerRegistryFactory. Instead declaring it private and make
- // the factory a friend class, we could add a Create() method on
- // ProtocolHandlerRegistry that constructs the object, calls the necessary
- // methods on it, then returns it as a unique_ptr (this is the usual way to
- // have these Init() type methods called correctly).
- void InstallDefaultsForChromeOS();
-
void SetIsLoading(bool is_loading);
private:
@@ -230,6 +225,10 @@ class ProtocolHandlerRegistry : public KeyedService {
friend class ProtocolHandlerRegistryTest;
+ // Install default protocol handlers for chromeos which must be done
+ // prior to calling InitProtocolSettings.
+ void InstallDefaultsForChromeOS();
+
// Puts the given handler at the top of the list of handlers for its
// protocol.
void PromoteHandler(const ProtocolHandler& handler);
diff --git a/chromium/components/custom_handlers/protocol_handler_registry_browsertest.cc b/chromium/components/custom_handlers/protocol_handler_registry_browsertest.cc
index 5d3a0025edd..1a92438b2d1 100644
--- a/chromium/components/custom_handlers/protocol_handler_registry_browsertest.cc
+++ b/chromium/components/custom_handlers/protocol_handler_registry_browsertest.cc
@@ -157,7 +157,7 @@ IN_PROC_BROWSER_TEST_F(RegisterProtocolHandlerBrowserTest, FencedFrame) {
// Create a FencedFrame.
content::RenderFrameHost* fenced_frame_host =
fenced_frame_test_helper().CreateFencedFrame(
- web_contents()->GetMainFrame(),
+ web_contents()->GetPrimaryMainFrame(),
embedded_test_server()->GetURL("/fenced_frames/title1.html"));
ASSERT_TRUE(fenced_frame_host);
diff --git a/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.cc b/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.cc
index de310882ebb..a841a095dbb 100644
--- a/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.cc
+++ b/chromium/components/custom_handlers/simple_protocol_handler_registry_factory.cc
@@ -54,13 +54,9 @@ KeyedService* SimpleProtocolHandlerRegistryFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
// We can't ensure the UserPref has been set, so we pass a nullptr
// PrefService.
- ProtocolHandlerRegistry* registry = new ProtocolHandlerRegistry(
- nullptr, std::make_unique<TestProtocolHandlerRegistryDelegate>());
-
- // Must be called as a part of the creation process.
- registry->InitProtocolSettings();
-
- return registry;
+ return custom_handlers::ProtocolHandlerRegistry::Create(
+ nullptr, std::make_unique<TestProtocolHandlerRegistryDelegate>())
+ .release();
}
} // namespace custom_handlers
diff --git a/chromium/components/data_reduction_proxy/COMMON_METADATA b/chromium/components/data_reduction_proxy/COMMON_METADATA
deleted file mode 100644
index e548f9709c1..00000000000
--- a/chromium/components/data_reduction_proxy/COMMON_METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-monorail {
- component: "Internals>Network>DataProxy"
-}
diff --git a/chromium/components/data_reduction_proxy/DEPS b/chromium/components/data_reduction_proxy/DEPS
deleted file mode 100644
index e09f4739a34..00000000000
--- a/chromium/components/data_reduction_proxy/DEPS
+++ /dev/null
@@ -1,16 +0,0 @@
-include_rules = [
- "+components/pref_registry",
- "+components/prefs",
- "+components/previews/core",
- "+crypto",
- "+google_apis",
- "+net",
- "+mojo/public/cpp",
- "+services/network/public/mojom",
-
- "-components"
-
- # Data Reduction Proxy is a layered component; subdirectories must explicitly
- # introduce the ability to use the content layer as appropriate.
- "-components/data_reduction_proxy/content",
-]
diff --git a/chromium/components/data_reduction_proxy/DIR_METADATA b/chromium/components/data_reduction_proxy/DIR_METADATA
deleted file mode 100644
index eb96fa3f02c..00000000000
--- a/chromium/components/data_reduction_proxy/DIR_METADATA
+++ /dev/null
@@ -1 +0,0 @@
-mixins: "//components/data_reduction_proxy/COMMON_METADATA"
diff --git a/chromium/components/data_reduction_proxy/OWNERS b/chromium/components/data_reduction_proxy/OWNERS
deleted file mode 100644
index c5f65edcaa0..00000000000
--- a/chromium/components/data_reduction_proxy/OWNERS
+++ /dev/null
@@ -1,54 +0,0 @@
-curranmax@chromium.org # Primary
-ryansturm@chromium.org # Primary
-spelchat@chromium.org # Primary
-
-tbansal@chromium.org # Secondary
-
-per-file *_messages.cc=set noparent
-per-file *_messages.cc=file://ipc/SECURITY_OWNERS
-
-per-file *_messages*.h=set noparent
-per-file *_messages*.h=file://ipc/SECURITY_OWNERS
-
-
-# Leave ASCII Art in comments
-################################################################################
-# #
-# :=?I7777777777I777I+:, #
-# .,~?7777777777777777777777777I+: #
-# .~?777777777777777777777777777777777I=, #
-# ,=I777777777777I?+==~~~~=++?7777777777?: #
-# ,?7777777777I~, ,+I7I~ #
-# ,?77777777I+, .:, #
-# .=I7777777?:. :?I, #
-# ?7777777I: .,=I7? , #
-# ,I777777I: ,+I777= ~7+ #
-# :I777777?, +I7777I: =777+ #
-# :I777777+ :I777777I: =I7777= #
-# ,?777777~ .:?77777777? ,?777777~ #
-# =777777= .,=7777777777+ ,I77777I. #
-# ,?77777?, ,=I7777777777I~ ~777777+ #
-# ~777777~ =I777777777777?: .I777777, #
-# +77777?, .,I77777777777777? ~777777= #
-# ?77777? :777777777777777+ ,777777? #
-# I77777? I7777777777777I~ .777777? #
-# I77777? .I777777777777?~. .777777? #
-# I77777? .:77777777777? ,777777? #
-# +77777?, .:I77777777+ ~777777= #
-# ~I77777~ :+++=,. .I777777, #
-# ,I77777?, ~777777+ #
-# =777777= :I777777, #
-# ,?777777~ ,?777777~ #
-# :I777777= ,?777777= #
-# :I777777? ,I777777+, #
-# ,I777777I: .=7777777+ #
-# .+7777777?: =I7777777= #
-# =I7777777?~ .=I7777777I: #
-# ,?777777777+, ~II7777777I~ #
-# ,?7777777777I~, ,+I777777777I+ #
-# ,=I777777777777I?+=~~~~~~=+I7777777777777?~ #
-# ~?I77777777777777777777777777777777I+, #
-# ,~?7777777777777777777777777I+:, #
-# :=?II777777777I7III+~, #
-# #
-################################################################################
diff --git a/chromium/components/data_reduction_proxy/README b/chromium/components/data_reduction_proxy/README
deleted file mode 100644
index 4ca8da5a6e0..00000000000
--- a/chromium/components/data_reduction_proxy/README
+++ /dev/null
@@ -1,14 +0,0 @@
-The Data Reduction Proxy component contains code for a configuring and using
-the data reduction proxy.
-
-The Data Reduction Proxy is a layered component. See
-http://www.chromium.org/developers/design-documents/layered-components-design
-for more information.
-
-Folder structure:
-
- core/: Shared code that does not depend on src/content/ or src/ios/
- browser/: Browser process code.
- common/: Code shared by the browser and the renderer.
-
- content/: Driver for the shared code based on the content layer. \ No newline at end of file
diff --git a/chromium/components/data_reduction_proxy/core/browser/BUILD.gn b/chromium/components/data_reduction_proxy/core/browser/BUILD.gn
deleted file mode 100644
index 8b2208f6440..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/BUILD.gn
+++ /dev/null
@@ -1,52 +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.
-
-if (is_android) {
- import("//build/config/android/rules.gni")
-}
-
-browser_sources = [
- "data_reduction_proxy_metrics.h",
- "data_reduction_proxy_settings.cc",
- "data_reduction_proxy_settings.h",
-]
-
-if (is_android) {
- static_library("browser_small") {
- sources = browser_sources
-
- deps = [
- "//base",
- "//components/data_reduction_proxy/core/common",
- "//components/pref_registry",
- "//components/prefs",
- "//crypto",
- "//google_apis",
- "//net:net",
- "//url:url",
- ]
- }
-}
-
-static_library("browser") {
- sources = browser_sources
-
- public_deps = [ "//components/data_reduction_proxy/core/common" ]
- deps = [
- "//base",
- "//components/pref_registry",
- "//components/prefs",
- "//crypto",
- "//google_apis",
- "//net",
- "//services/network:network_service",
- "//third_party/leveldatabase",
- "//url",
- ]
-
- defines = [ "USE_GOOGLE_API_KEYS" ]
- if (!is_ios) {
- defines += [ "USE_GOOGLE_API_KEYS_FOR_AUTH_KEY" ]
- }
-}
diff --git a/chromium/components/data_reduction_proxy/core/browser/DEPS b/chromium/components/data_reduction_proxy/core/browser/DEPS
deleted file mode 100644
index cc968059058..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/DEPS
+++ /dev/null
@@ -1,8 +0,0 @@
-include_rules = [
- "+components/data_use_measurement/core",
- "+components/variations",
- "+mojo/public/cpp",
- "+services/network/public",
- "+services/network/test",
- "+third_party/leveldatabase",
-]
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h
deleted file mode 100644
index 252b5fad4f5..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h
+++ /dev/null
@@ -1,26 +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.
-
-#ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_METRICS_H_
-#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_METRICS_H_
-
-#include <vector>
-
-namespace data_reduction_proxy {
-
-typedef std::vector<long long> ContentLengthList;
-
-// The number of days of bandwidth usage statistics that are tracked.
-const unsigned int kNumDaysInHistory = 60;
-
-// The number of days of bandwidth usage statistics that are presented.
-const unsigned int kNumDaysInHistorySummary = 30;
-
-static_assert(kNumDaysInHistorySummary <= kNumDaysInHistory,
- "kNumDaysInHistorySummary should be no larger than "
- "kNumDaysInHistory");
-
-} // namespace data_reduction_proxy
-
-#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_METRICS_H_
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
deleted file mode 100644
index 767dd61ea53..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
+++ /dev/null
@@ -1,64 +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.
-
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/feature_list.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/observer_list.h"
-#include "base/time/clock.h"
-#include "base/time/default_clock.h"
-#include "build/build_config.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
-
-namespace {
-
-// Returns true if the Data Reduction Proxy is forced to be enabled from the
-// command line.
-bool ShouldForceEnableDataReductionProxy() {
- return base::CommandLine::ForCurrentProcess()->HasSwitch(
- data_reduction_proxy::switches::kEnableDataReductionProxy);
-}
-
-} // namespace
-
-namespace data_reduction_proxy {
-
-DataReductionProxySettings::DataReductionProxySettings(
- bool is_off_the_record_profile)
- : is_off_the_record_profile_(is_off_the_record_profile) {
- DCHECK(!is_off_the_record_profile_);
-}
-
-DataReductionProxySettings::~DataReductionProxySettings() = default;
-
-void DataReductionProxySettings::InitDataReductionProxySettings() {}
-
-// static
-bool DataReductionProxySettings::IsDataSaverEnabledByUser(
- bool is_off_the_record_profile) {
- if (is_off_the_record_profile)
- return false;
- if (ShouldForceEnableDataReductionProxy())
- return true;
- return false;
-}
-
-void DataReductionProxySettings::AddDataReductionProxySettingsObserver(
- DataReductionProxySettingsObserver* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
- observers_.AddObserver(observer);
-}
-
-void DataReductionProxySettings::RemoveDataReductionProxySettingsObserver(
- DataReductionProxySettingsObserver* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
- observers_.RemoveObserver(observer);
-}
-
-} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
deleted file mode 100644
index c1729d5fd6f..00000000000
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
+++ /dev/null
@@ -1,78 +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.
-
-#ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_SETTINGS_H_
-#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_SETTINGS_H_
-
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/raw_ptr.h"
-#include "base/observer_list.h"
-#include "base/threading/thread_checker.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
-#include "url/gurl.h"
-
-namespace data_reduction_proxy {
-
-// Classes may derive from |DataReductionProxySettingsObserver| and register as
-// an observer of |DataReductionProxySettings| to get notified when the proxy
-// request headers change or when the DRPSettings class is initialized.
-class DataReductionProxySettingsObserver {
- public:
- // Notifies when Data Saver is enabled or disabled.
- virtual void OnDataSaverEnabledChanged(bool enabled) {}
-};
-
-// Central point for configuring the data reduction proxy.
-// This object lives on the UI thread and all of its methods are expected to
-// be called from there.
-class DataReductionProxySettings {
- public:
- explicit DataReductionProxySettings(bool is_off_the_record_profile);
-
- DataReductionProxySettings(const DataReductionProxySettings&) = delete;
- DataReductionProxySettings& operator=(const DataReductionProxySettings&) =
- delete;
-
- virtual ~DataReductionProxySettings();
-
- // Initializes the Data Reduction Proxy with the profile prefs. The caller
- // must ensure that all parameters remain alive for the lifetime of the
- // |DataReductionProxySettings| instance.
- void InitDataReductionProxySettings();
-
- // Returns true if the Data Saver feature is enabled by the user on Android.
- // This checks only the Data Saver prefs on Android or forcing flag on any
- // platform. Does not check any holdback experiments. Note that this may be
- // different from the value of |IsDataReductionProxyEnabled|.
- static bool IsDataSaverEnabledByUser(bool is_off_the_record_profile);
-
- // Enables or disables Data Saver, regardless of platform.
- static void SetDataSaverEnabledForTesting(bool enabled);
-
- // Adds an observer that is notified every time the proxy request headers
- // change.
- void AddDataReductionProxySettingsObserver(
- DataReductionProxySettingsObserver* observer);
-
- // Removes an observer that is notified every time the proxy request headers
- // change.
- void RemoveDataReductionProxySettingsObserver(
- DataReductionProxySettingsObserver* observer);
-
- private:
- // Observers to notify when the proxy request headers change or |this| is
- // initialized.
- base::ObserverList<DataReductionProxySettingsObserver>::Unchecked observers_;
-
- // True if |this| was constructed for an off-the-record profile.
- const bool is_off_the_record_profile_;
-
- base::ThreadChecker thread_checker_;
-};
-
-} // namespace data_reduction_proxy
-
-#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_SETTINGS_H_
diff --git a/chromium/components/data_reduction_proxy/core/common/BUILD.gn b/chromium/components/data_reduction_proxy/core/common/BUILD.gn
deleted file mode 100644
index 45d0a6906d8..00000000000
--- a/chromium/components/data_reduction_proxy/core/common/BUILD.gn
+++ /dev/null
@@ -1,33 +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.
-
-# Variables:
-# deps: Extra dependencies.
-template("common_tmpl") {
- static_library(target_name) {
- sources = [
- "data_reduction_proxy_switches.cc",
- "data_reduction_proxy_switches.h",
- ]
-
- deps = [
- "//base",
- "//google_apis",
- "//services/network/public/cpp",
- ]
-
- defines = [ "USE_GOOGLE_API_KEYS" ]
-
- if (defined(invoker.deps)) {
- deps += invoker.deps
- }
- }
-}
-
-common_tmpl("common") {
- deps = [
- "//net",
- "//url",
- ]
-}
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc
deleted file mode 100644
index e8532097059..00000000000
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc
+++ /dev/null
@@ -1,14 +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.
-
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
-
-namespace data_reduction_proxy {
-namespace switches {
-
-// Enable the data reduction proxy.
-const char kEnableDataReductionProxy[] = "enable-spdy-proxy-auth";
-
-} // namespace switches
-} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h
deleted file mode 100644
index 51e1ce9b08a..00000000000
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h
+++ /dev/null
@@ -1,19 +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.
-
-#ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_SWITCHES_H_
-#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_SWITCHES_H_
-
-namespace data_reduction_proxy {
-namespace switches {
-
-// All switches in alphabetical order. The switches should be documented
-// alongside the definition of their values in the .cc file.
-
-extern const char kEnableDataReductionProxy[];
-
-} // namespace switches
-} // namespace data_reduction_proxy
-
-#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_SWITCHES_H_
diff --git a/chromium/components/dbus/menu/BUILD.gn b/chromium/components/dbus/menu/BUILD.gn
index 672687efb25..be112fa92b9 100644
--- a/chromium/components/dbus/menu/BUILD.gn
+++ b/chromium/components/dbus/menu/BUILD.gn
@@ -34,6 +34,7 @@ source_set("unit_tests") {
deps = [
":menu",
"//base",
+ "//build:chromecast_buildflags",
"//build:chromeos_buildflags",
"//testing/gtest",
]
diff --git a/chromium/components/dbus/menu/menu_property_list_unittest.cc b/chromium/components/dbus/menu/menu_property_list_unittest.cc
index 5f83d8e3358..0261bc8d3fb 100644
--- a/chromium/components/dbus/menu/menu_property_list_unittest.cc
+++ b/chromium/components/dbus/menu/menu_property_list_unittest.cc
@@ -8,6 +8,7 @@
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
+#include "build/chromecast_buildflags.h"
#include "build/chromeos_buildflags.h"
#include "components/dbus/properties/types.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -325,7 +326,7 @@ TEST(MenuPropertyListTest, ComputePropertiesIcon) {
EXPECT_EQ(menu->ComputeProperties(), props);
}
-#if BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
TEST(MenuPropertyListTest, ComputePropertiesAccelerator) {
// The Wayland implementation requires the keyboard layout to be set.
// The ScopedKeyboardLayout does not unset the already existing layout engine,
@@ -365,7 +366,7 @@ TEST(MenuPropertyListTest, ComputePropertiesAccelerator) {
if (old_layout)
ui::KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(old_layout);
}
-#endif
+#endif // BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
TEST(MenuPropertyListTest, ComputePropertyChanges) {
MenuItemProperties old_props;
diff --git a/chromium/components/desks_storage/BUILD.gn b/chromium/components/desks_storage/BUILD.gn
index 7e4f64c9d4e..d606925724f 100644
--- a/chromium/components/desks_storage/BUILD.gn
+++ b/chromium/components/desks_storage/BUILD.gn
@@ -9,6 +9,8 @@ static_library("desks_storage") {
"core/desk_model.cc",
"core/desk_model.h",
"core/desk_model_observer.h",
+ "core/desk_model_wrapper.cc",
+ "core/desk_model_wrapper.h",
"core/desk_sync_bridge.cc",
"core/desk_sync_bridge.h",
"core/desk_sync_service.cc",
@@ -39,16 +41,37 @@ static_library("desks_storage") {
public_deps = []
}
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "core/desk_test_util.cc",
+ "core/desk_test_util.h",
+ ]
+
+ deps = [
+ "//ash/public/cpp",
+ "//base",
+ "//components/app_constants",
+ ]
+}
+
source_set("unit_tests") {
testonly = true
sources = [
+ "core/desk_model_wrapper_unittests.cc",
"core/desk_sync_bridge_unittest.cc",
"core/desk_template_conversion_unittests.cc",
"core/desk_template_util_unittests.cc",
+ "core/desk_test_util_unittests.cc",
"core/local_desks_data_manager_unittests.cc",
+ "core/saved_desk_builder.cc",
+ "core/saved_desk_builder.h",
+ "core/saved_desk_test_util.cc",
+ "core/saved_desk_test_util.h",
]
deps = [
":desks_storage",
+ ":test_support",
"//ash/public/cpp",
"//base",
"//base/test:test_support",
diff --git a/chromium/components/desks_storage/DEPS b/chromium/components/desks_storage/DEPS
index 937bd54b01f..e27267f6c2c 100644
--- a/chromium/components/desks_storage/DEPS
+++ b/chromium/components/desks_storage/DEPS
@@ -8,6 +8,7 @@ include_rules = [
"+components/keyed_service/core",
"+components/services/app_service/public/cpp",
"+components/sync",
+ "+components/tab_groups",
"+components/version_info",
"+third_party/re2",
"+ui/base/ui_base_types.h",
diff --git a/chromium/components/desks_storage/OWNERS b/chromium/components/desks_storage/OWNERS
index f6c3f39d54f..0e79d6a659c 100644
--- a/chromium/components/desks_storage/OWNERS
+++ b/chromium/components/desks_storage/OWNERS
@@ -1,4 +1,3 @@
afakhry@chromium.org
avynn@google.com
-sammiequon@chromium.org
yzd@google.com
diff --git a/chromium/components/desks_storage/core/desk_model.cc b/chromium/components/desks_storage/core/desk_model.cc
index 3855c99b930..fdedbf0d3b5 100644
--- a/chromium/components/desks_storage/core/desk_model.cc
+++ b/chromium/components/desks_storage/core/desk_model.cc
@@ -73,7 +73,8 @@ void DeskModel::SetPolicyDeskTemplates(const std::string& policy_json) {
for (auto& desk_template : parsed_list.value->GetListDeprecated()) {
std::unique_ptr<ash::DeskTemplate> dt =
- desk_template_conversion::ParseDeskTemplateFromPolicy(desk_template);
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ desk_template, ash::DeskTemplateSource::kPolicy);
if (dt) {
policy_entries_.push_back(std::move(dt));
} else {
diff --git a/chromium/components/desks_storage/core/desk_model.h b/chromium/components/desks_storage/core/desk_model.h
index 8221dc130d1..2bb60d81aaf 100644
--- a/chromium/components/desks_storage/core/desk_model.h
+++ b/chromium/components/desks_storage/core/desk_model.h
@@ -16,6 +16,7 @@
namespace ash {
class DeskTemplate;
+enum class DeskTemplateType;
}
namespace apps {
@@ -79,9 +80,11 @@ class DeskModel {
DeskModel& operator=(const DeskModel&) = delete;
virtual ~DeskModel();
- using GetAllEntriesCallback =
- base::OnceCallback<void(GetAllEntriesStatus status,
- const std::vector<ash::DeskTemplate*>& entries)>;
+ // TODO(crbug.com/1320805): Once DeskSyncBridge is set to support saved desk,
+ // add methods to support operations on both types of templates.
+ using GetAllEntriesCallback = base::OnceCallback<void(
+ GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries)>;
// Returns a vector of entries in the model.
virtual void GetAllEntries(GetAllEntriesCallback callback) = 0;
@@ -137,10 +140,24 @@ class DeskModel {
virtual std::size_t GetEntryCount() const = 0;
// Gets the maximum number of templates this storage backend could hold.
- // Adding more templates beyond this limit will result in |kHitMaximumLimit|
+ // Adding more templates beyond this limit will result in `kHitMaximumLimit`
// error.
virtual std::size_t GetMaxEntryCount() const = 0;
+ // Gets the number of save and recall desks currently saved.
+ virtual std::size_t GetSaveAndRecallDeskEntryCount() const = 0;
+
+ // Gets the number of desk templates currently saved.
+ virtual std::size_t GetDeskTemplateEntryCount() const = 0;
+
+ // Gets the maximum number of save and recall desks entry this storage backend
+ // could hold.
+ virtual std::size_t GetMaxSaveAndRecallDeskEntryCount() const = 0;
+
+ // Gets the maximum number of desk template entry this storage backend
+ // could hold.
+ virtual std::size_t GetMaxDeskTemplateEntryCount() const = 0;
+
// Returns a vector of desk template UUIDs.
// This method assumes each implementation has a cache and can return the
// UUIDs synchronously.
@@ -152,6 +169,14 @@ class DeskModel {
// Whether this model is syncing desk templates to server.
virtual bool IsSyncing() const = 0;
+ // Returns another template that shares the same `name` as the template with
+ // the uuid `uuid`. The `uuid` is used to make sure we are not returning the
+ // current entry itself.
+ virtual ash::DeskTemplate* FindOtherEntryWithName(
+ const std::u16string& name,
+ ash::DeskTemplateType type,
+ const base::GUID& uuid) const = 0;
+
// Observer registration methods. The model will remove all observers upon
// destruction automatically.
void AddObserver(DeskModelObserver* observer);
@@ -164,8 +189,8 @@ class DeskModel {
void RemovePolicyDeskTemplates();
protected:
- // Finds the admin desk template with the given `uuid`. Returns `nullptr` if
- // none is found.
+ // Finds the admin desk template with the given `uuid`. Returns `nullptr`
+ // if none is found.
std::unique_ptr<ash::DeskTemplate> GetAdminDeskTemplateByUUID(
const std::string& uuid) const;
diff --git a/chromium/components/desks_storage/core/desk_model_wrapper.cc b/chromium/components/desks_storage/core/desk_model_wrapper.cc
new file mode 100644
index 00000000000..c4bf2b4bd60
--- /dev/null
+++ b/chromium/components/desks_storage/core/desk_model_wrapper.cc
@@ -0,0 +1,192 @@
+// Copyright 2022 The Chromium 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/desks_storage/core/desk_model_wrapper.h"
+
+#include "ash/public/cpp/desk_template.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "components/account_id/account_id.h"
+#include "components/desks_storage/core/desk_model.h"
+#include "desk_sync_bridge.h"
+#include "local_desk_data_manager.h"
+
+namespace desks_storage {
+
+DeskModelWrapper::DeskModelWrapper(
+ desks_storage::DeskModel* save_and_recall_desks_model)
+ : save_and_recall_desks_model_(save_and_recall_desks_model) {}
+
+DeskModelWrapper::~DeskModelWrapper() = default;
+
+void DeskModelWrapper::GetAllEntries(
+ DeskModel::GetAllEntriesCallback callback) {
+ auto template_entries = std::vector<const ash::DeskTemplate*>();
+ auto desk_template_status =
+ GetDeskTemplateModel()->GetAllEntries(template_entries);
+ for (const auto& it : policy_entries_)
+ template_entries.push_back(it.get());
+
+ if (desk_template_status != DeskModel::GetAllEntriesStatus::kOk) {
+ std::move(callback).Run(DeskModel::GetAllEntriesStatus::kFailure,
+ template_entries);
+ return;
+ }
+
+ save_and_recall_desks_model_->GetAllEntries(base::BindOnce(
+ &DeskModelWrapper::OnGetAllEntries, weak_ptr_factory_.GetWeakPtr(),
+ template_entries, std::move(callback)));
+}
+
+void DeskModelWrapper::GetEntryByUUID(
+ const std::string& uuid,
+ DeskModel::GetEntryByUuidCallback callback) {
+ // Check if this is an admin template uuid first.
+ std::unique_ptr<ash::DeskTemplate> policy_entry =
+ GetAdminDeskTemplateByUUID(uuid);
+
+ if (policy_entry) {
+ std::move(callback).Run(GetEntryByUuidStatus::kOk, std::move(policy_entry));
+ return;
+ }
+
+ if (GetDeskTemplateModel()->HasUuid(uuid)) {
+ GetDeskTemplateModel()->GetEntryByUUID(uuid, std::move(callback));
+ } else {
+ save_and_recall_desks_model_->GetEntryByUUID(uuid, std::move(callback));
+ }
+}
+
+void DeskModelWrapper::AddOrUpdateEntry(
+ std::unique_ptr<ash::DeskTemplate> new_entry,
+ DeskModel::AddOrUpdateEntryCallback callback) {
+ if (new_entry->type() == ash::DeskTemplateType::kTemplate) {
+ GetDeskTemplateModel()->AddOrUpdateEntry(std::move(new_entry),
+ std::move(callback));
+ } else {
+ save_and_recall_desks_model_->AddOrUpdateEntry(std::move(new_entry),
+ std::move(callback));
+ }
+}
+
+void DeskModelWrapper::DeleteEntry(const std::string& uuid_str,
+ DeskModel::DeleteEntryCallback callback) {
+ auto status = std::make_unique<DeskModel::DeleteEntryStatus>();
+ if (GetDeskTemplateModel()->HasUuid(uuid_str)) {
+ GetDeskTemplateModel()->DeleteEntry(uuid_str, std::move(callback));
+ } else {
+ save_and_recall_desks_model_->DeleteEntry(uuid_str, std::move(callback));
+ }
+}
+
+void DeskModelWrapper::DeleteAllEntries(
+ DeskModel::DeleteEntryCallback callback) {
+ DeskModel::DeleteEntryStatus desk_template_delete_status =
+ GetDeskTemplateModel()->DeleteAllEntries();
+ if (desk_template_delete_status != DeskModel::DeleteEntryStatus::kOk) {
+ std::move(callback).Run(desk_template_delete_status);
+ return;
+ }
+ save_and_recall_desks_model_->DeleteAllEntries(
+ base::BindOnce(&DeskModelWrapper::OnDeleteAllEntries,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+// TODO(crbug.com/1320805): Remove this function once both desk models support
+// desk type counts.
+size_t DeskModelWrapper::GetEntryCount() const {
+ return GetSaveAndRecallDeskEntryCount() + GetDeskTemplateEntryCount();
+}
+
+size_t DeskModelWrapper::GetSaveAndRecallDeskEntryCount() const {
+ return save_and_recall_desks_model_->GetSaveAndRecallDeskEntryCount();
+}
+
+size_t DeskModelWrapper::GetDeskTemplateEntryCount() const {
+ return GetDeskTemplateModel()->GetDeskTemplateEntryCount() +
+ policy_entries_.size();
+}
+
+size_t DeskModelWrapper::GetMaxEntryCount() const {
+ return GetMaxSaveAndRecallDeskEntryCount() + GetMaxDeskTemplateEntryCount();
+}
+
+size_t DeskModelWrapper::GetMaxSaveAndRecallDeskEntryCount() const {
+ return save_and_recall_desks_model_->GetMaxSaveAndRecallDeskEntryCount();
+}
+
+size_t DeskModelWrapper::GetMaxDeskTemplateEntryCount() const {
+ return GetDeskTemplateModel()->GetMaxDeskTemplateEntryCount() +
+ policy_entries_.size();
+}
+
+std::vector<base::GUID> DeskModelWrapper::GetAllEntryUuids() const {
+ std::vector<base::GUID> keys;
+
+ for (const auto& it : policy_entries_)
+ keys.push_back(it.get()->uuid());
+
+ for (const auto& save_and_recall_uuid :
+ save_and_recall_desks_model_->GetAllEntryUuids()) {
+ keys.emplace_back(save_and_recall_uuid);
+ }
+
+ for (const auto& desk_template_uuid :
+ GetDeskTemplateModel()->GetAllEntryUuids()) {
+ keys.emplace_back(desk_template_uuid);
+ }
+ return keys;
+}
+
+bool DeskModelWrapper::IsReady() const {
+ return save_and_recall_desks_model_->IsReady() &&
+ GetDeskTemplateModel()->IsReady();
+}
+
+bool DeskModelWrapper::IsSyncing() const {
+ return GetDeskTemplateModel()->IsSyncing();
+}
+
+ash::DeskTemplate* DeskModelWrapper::FindOtherEntryWithName(
+ const std::u16string& name,
+ ash::DeskTemplateType type,
+ const base::GUID& uuid) const {
+ if (type == ash::DeskTemplateType::kTemplate) {
+ return GetDeskTemplateModel()->FindOtherEntryWithName(name, type, uuid);
+ } else {
+ return save_and_recall_desks_model_->FindOtherEntryWithName(name, type,
+ uuid);
+ }
+}
+
+desks_storage::DeskSyncBridge* DeskModelWrapper::GetDeskTemplateModel() const {
+ DCHECK(desk_template_model_);
+ return desk_template_model_;
+}
+
+void DeskModelWrapper::OnGetAllEntries(
+ const std::vector<const ash::DeskTemplate*>& template_entries,
+ DeskModel::GetAllEntriesCallback callback,
+ desks_storage::DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ if (status != DeskModel::GetAllEntriesStatus::kOk) {
+ std::move(callback).Run(status, template_entries);
+ return;
+ }
+
+ auto all_entries = template_entries;
+
+ for (auto* const entry : entries)
+ all_entries.push_back(entry);
+
+ std::move(callback).Run(status, all_entries);
+}
+
+void DeskModelWrapper::OnDeleteAllEntries(
+ DeskModel::DeleteEntryCallback callback,
+ desks_storage::DeskModel::DeleteEntryStatus status) {
+ std::move(callback).Run(status);
+}
+
+} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/desk_model_wrapper.h b/chromium/components/desks_storage/core/desk_model_wrapper.h
new file mode 100644
index 00000000000..abc6c26c41b
--- /dev/null
+++ b/chromium/components/desks_storage/core/desk_model_wrapper.h
@@ -0,0 +1,89 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DESKS_STORAGE_CORE_DESK_MODEL_WRAPPER_H_
+#define COMPONENTS_DESKS_STORAGE_CORE_DESK_MODEL_WRAPPER_H_
+
+#include <map>
+#include <memory>
+
+#include "ash/public/cpp/desk_template.h"
+#include "base/guid.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "components/account_id/account_id.h"
+#include "components/desks_storage/core/desk_model.h"
+#include "components/desks_storage/core/desk_sync_bridge.h"
+
+namespace ash {
+class DeskTemplate;
+}
+
+namespace desks_storage {
+// The DeskModelWrapper handles storage operations for chrome sync
+// desk templates and local storage save and recall desks backends.
+//
+// TODO(crbug.com/1323946): Add unittests for this class.
+class DeskModelWrapper : public DeskModel {
+ public:
+ DeskModelWrapper(desks_storage::DeskModel* save_and_recall_desks_model);
+
+ DeskModelWrapper(const DeskModelWrapper&) = delete;
+ DeskModelWrapper& operator=(const DeskModelWrapper&) = delete;
+ ~DeskModelWrapper() override;
+
+ // DeskModel:
+ void GetAllEntries(GetAllEntriesCallback callback) override;
+ void GetEntryByUUID(const std::string& uuid,
+ GetEntryByUuidCallback callback) override;
+ void AddOrUpdateEntry(std::unique_ptr<ash::DeskTemplate> new_entry,
+ AddOrUpdateEntryCallback callback) override;
+ void DeleteEntry(const std::string& uuid,
+ DeleteEntryCallback callback) override;
+ void DeleteAllEntries(DeleteEntryCallback callback) override;
+ std::size_t GetEntryCount() const override;
+ std::size_t GetMaxEntryCount() const override;
+ std::size_t GetSaveAndRecallDeskEntryCount() const override;
+ std::size_t GetDeskTemplateEntryCount() const override;
+ std::size_t GetMaxSaveAndRecallDeskEntryCount() const override;
+ std::size_t GetMaxDeskTemplateEntryCount() const override;
+ std::vector<base::GUID> GetAllEntryUuids() const override;
+ bool IsReady() const override;
+ bool IsSyncing() const override;
+ ash::DeskTemplate* FindOtherEntryWithName(
+ const std::u16string& name,
+ ash::DeskTemplateType type,
+ const base::GUID& uuid) const override;
+
+ // Setter method to set `desk_template_model_` to the correct `bridge`.
+ void SetDeskSyncBridge(desks_storage::DeskSyncBridge* bridge) {
+ desk_template_model_ = bridge;
+ }
+
+ private:
+ desks_storage::DeskSyncBridge* GetDeskTemplateModel() const;
+
+ // Wrapper for GetAllEntriesCallback to consolidate entries from both storage
+ // models into one.
+ void OnGetAllEntries(
+ const std::vector<const ash::DeskTemplate*>& template_entries,
+ DeskModel::GetAllEntriesCallback callback,
+ desks_storage::DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries);
+
+ // Wrapper for DeleteEntryCallback to consolidate deleting all entries from
+ // both storage models.
+ void OnDeleteAllEntries(DeskModel::DeleteEntryCallback callback,
+ desks_storage::DeskModel::DeleteEntryStatus status);
+
+ desks_storage::DeskModel* save_and_recall_desks_model_;
+
+ desks_storage::DeskSyncBridge* desk_template_model_;
+
+ base::WeakPtrFactory<DeskModelWrapper> weak_ptr_factory_{this};
+};
+
+} // namespace desks_storage
+
+#endif // COMPONENTS_DESKS_STORAGE_CORE_LOCAL_DESK_DATA_MANAGER_H_
diff --git a/chromium/components/desks_storage/core/desk_model_wrapper_unittests.cc b/chromium/components/desks_storage/core/desk_model_wrapper_unittests.cc
new file mode 100644
index 00000000000..dec3c2a82b9
--- /dev/null
+++ b/chromium/components/desks_storage/core/desk_model_wrapper_unittests.cc
@@ -0,0 +1,854 @@
+// Copyright 2022 The Chromium 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 <memory>
+
+#include "components/desks_storage/core/desk_model_wrapper.h"
+
+#include "ash/public/cpp/desk_template.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/guid.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind.h"
+#include "base/test/simple_test_clock.h"
+#include "base/test/task_environment.h"
+#include "components/account_id/account_id.h"
+#include "components/app_constants/constants.h"
+#include "components/app_restore/app_launch_info.h"
+#include "components/desks_storage/core/desk_model_observer.h"
+#include "components/desks_storage/core/desk_sync_bridge.h"
+#include "components/desks_storage/core/desk_template_conversion.h"
+#include "components/desks_storage/core/desk_template_util.h"
+#include "components/desks_storage/core/desk_test_util.h"
+#include "components/desks_storage/core/local_desk_data_manager.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/features.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/in_memory_metadata_change_list.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/protocol/entity_data.h"
+#include "components/sync/protocol/model_type_state.pb.h"
+#include "components/sync/test/model/mock_model_type_change_processor.h"
+#include "components/sync/test/model/model_type_store_test_util.h"
+#include "components/sync/test/model/test_matchers.h"
+#include "desk_model_wrapper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace desks_storage {
+
+namespace {
+
+constexpr char kTemplateFileNameFormat[] = "%s.saveddesk";
+constexpr char kUuidFormat[] = "1c186d5a-502e-49ce-9ee1-00000000000%d";
+constexpr char kTemplateNameFormat[] = "desk_%d";
+const std::string kTestUuid1 = base::StringPrintf(kUuidFormat, 1);
+const std::string kTestUuid2 = base::StringPrintf(kUuidFormat, 2);
+const std::string kTestUuid3 = base::StringPrintf(kUuidFormat, 3);
+const std::string kTestUuid4 = base::StringPrintf(kUuidFormat, 4);
+const std::string kTestUuid5 = base::StringPrintf(kUuidFormat, 5);
+
+const std::string kTestFileName1 =
+ base::StringPrintf(kTemplateFileNameFormat, kTestUuid1.c_str());
+const std::string kPolicyWithOneTemplate =
+ "[{\"version\":1,\"uuid\":\"" + kTestUuid5 +
+ "\",\"name\":\""
+ "Admin Template 1"
+ "\",\"created_time_usec\":\"1633535632\",\"updated_time_usec\": "
+ "\"1633535632\",\"desk\":{\"apps\":[{\"window_"
+ "bound\":{\"left\":0,\"top\":1,\"height\":121,\"width\":120},\"window_"
+ "state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
+ "\"url\":\"https://example.com\",\"title\":\"Example\"},{\"url\":\"https://"
+ "example.com/"
+ "2\",\"title\":\"Example2\"}],\"active_tab_index\":1,\"window_id\":0,"
+ "\"display_id\":\"100\",\"pre_minimized_window_state\":\"NORMAL\"}]}}]";
+
+// Search `entry_list` for `uuid_query` as a uuid and returns true if
+// found, false if not.
+bool FindUuidInUuidList(
+ const std::string& uuid_query,
+ const std::vector<const ash::DeskTemplate*>& entry_list) {
+ base::GUID guid = base::GUID::ParseCaseInsensitive(uuid_query);
+ DCHECK(guid.is_valid());
+
+ for (auto* entry : entry_list) {
+ if (entry->uuid() == guid)
+ return true;
+ }
+
+ return false;
+}
+
+// Verifies that the status passed into it is kOk
+void VerifyEntryAddedCorrectly(DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
+}
+
+void VerifyEntryAddedErrorHitMaximumLimit(
+ DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kHitMaximumLimit);
+}
+
+// Make test template with ID containing the index. Defaults to desk template
+// type if a type is not specified.
+
+std::unique_ptr<ash::DeskTemplate> MakeTestDeskTemplate(
+ int index,
+ ash::DeskTemplateType type) {
+ const std::string template_uuid = base::StringPrintf(kUuidFormat, index);
+ const std::string template_name =
+ base::StringPrintf(kTemplateNameFormat, index);
+ std::unique_ptr<ash::DeskTemplate> desk_template =
+ std::make_unique<ash::DeskTemplate>(
+ template_uuid, ash::DeskTemplateSource::kUser, template_name,
+ base::Time::Now(), type);
+ desk_template->set_desk_restore_data(
+ std::make_unique<app_restore::RestoreData>());
+ return desk_template;
+}
+
+// Make test template with default restore data.
+std::unique_ptr<ash::DeskTemplate> MakeTestDeskTemplate(
+ const std::string& uuid,
+ ash::DeskTemplateSource source,
+ const std::string& name,
+ const base::Time created_time) {
+ auto entry = std::make_unique<ash::DeskTemplate>(
+ uuid, source, name, created_time, ash::DeskTemplateType::kTemplate);
+ entry->set_desk_restore_data(std::make_unique<app_restore::RestoreData>());
+ return entry;
+}
+
+// Make test save and recall desk with default restore data.
+std::unique_ptr<ash::DeskTemplate> MakeTestSaveAndRecallDesk(
+ const std::string& uuid,
+ const std::string& name,
+ const base::Time created_time) {
+ auto entry = std::make_unique<ash::DeskTemplate>(
+ uuid, ash::DeskTemplateSource::kUser, name, created_time,
+ ash::DeskTemplateType::kSaveAndRecall);
+ entry->set_desk_restore_data(std::make_unique<app_restore::RestoreData>());
+ return entry;
+}
+
+} // namespace
+
+class MockDeskModelObserver : public DeskModelObserver {
+ public:
+ MOCK_METHOD0(DeskModelLoaded, void());
+ MOCK_METHOD1(EntriesAddedOrUpdatedRemotely,
+ void(const std::vector<const ash::DeskTemplate*>&));
+ MOCK_METHOD1(EntriesRemovedRemotely, void(const std::vector<std::string>&));
+ MOCK_METHOD1(EntriesAddedOrUpdatedLocally,
+ void(const std::vector<const ash::DeskTemplate*>&));
+ MOCK_METHOD1(EntriesRemovedLocally, void(const std::vector<std::string>&));
+};
+
+// This test class only tests the overall wrapper desk model class. The
+// correctness of the underlying desk model storages that
+// `DeskModelWrapper` uses are tested in their own unittests.
+class DeskModelWrapperTest : public testing::Test {
+ public:
+ DeskModelWrapperTest()
+ : sample_desk_template_one_(
+ MakeTestDeskTemplate(kTestUuid1,
+ ash::DeskTemplateSource::kUser,
+ "desk_01",
+ base::Time::Now())),
+ sample_desk_template_two_(
+ MakeTestDeskTemplate(kTestUuid2,
+ ash::DeskTemplateSource::kUser,
+ "desk_02",
+ base::Time::Now())),
+ sample_save_and_recall_desk_one_(
+ MakeTestSaveAndRecallDesk(kTestUuid3,
+ "save_and_recall_desk_01",
+ base::Time::Now())),
+ sample_save_and_recall_desk_two_(
+ MakeTestSaveAndRecallDesk(kTestUuid4,
+ "save_and_recall_desk_02",
+ base::Time::Now())),
+ task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
+ cache_(std::make_unique<apps::AppRegistryCache>()),
+ account_id_(AccountId::FromUserEmail("test@gmail.com")),
+ data_manager_(std::unique_ptr<LocalDeskDataManager>()),
+ store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {}
+
+ DeskModelWrapperTest(const DeskModelWrapperTest&) = delete;
+ DeskModelWrapperTest& operator=(const DeskModelWrapperTest&) = delete;
+
+ ~DeskModelWrapperTest() override = default;
+
+ void SetUp() override {
+ EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+ data_manager_ = std::make_unique<LocalDeskDataManager>(temp_dir_.GetPath(),
+ account_id_);
+ data_manager_->SetExcludeSaveAndRecallDeskInMaxEntryCountForTesting(false);
+ desk_test_util::PopulateAppRegistryCache(account_id_, cache_.get());
+ model_wrapper_ = std::make_unique<DeskModelWrapper>(data_manager_.get());
+ task_environment_.RunUntilIdle();
+ testing::Test::SetUp();
+ }
+
+ void CreateBridge() {
+ ON_CALL(mock_processor_, IsTrackingMetadata())
+ .WillByDefault(testing::Return(true));
+ bridge_ = std::make_unique<DeskSyncBridge>(
+ mock_processor_.CreateForwardingProcessor(),
+ syncer::ModelTypeStoreTestUtil::FactoryForForwardingStore(store_.get()),
+ account_id_);
+ bridge_->AddObserver(&mock_observer_);
+ }
+
+ void FinishInitialization() { base::RunLoop().RunUntilIdle(); }
+
+ void InitializeBridge() {
+ CreateBridge();
+ FinishInitialization();
+ model_wrapper_->SetDeskSyncBridge(bridge_.get());
+ }
+
+ void ShutdownBridge() {
+ base::RunLoop().RunUntilIdle();
+ bridge_->RemoveObserver(&mock_observer_);
+ }
+
+ void RestartBridge() {
+ ShutdownBridge();
+ InitializeBridge();
+ }
+
+ void AddTwoTemplates() {
+ base::RunLoop loop1;
+ model_wrapper_->AddOrUpdateEntry(
+ std::move(sample_desk_template_one_),
+ base::BindLambdaForTesting(
+ [&](DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
+ loop1.Quit();
+ }));
+ loop1.Run();
+
+ base::RunLoop loop2;
+ model_wrapper_->AddOrUpdateEntry(
+ std::move(sample_desk_template_two_),
+ base::BindLambdaForTesting(
+ [&](DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
+ loop2.Quit();
+ }));
+ loop2.Run();
+ }
+
+ void AddTwoSaveAndRecallDeskTemplates() {
+ base::RunLoop loop1;
+ model_wrapper_->AddOrUpdateEntry(
+ std::move(sample_save_and_recall_desk_one_),
+ base::BindLambdaForTesting(
+ [&](DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
+ loop1.Quit();
+ }));
+ loop1.Run();
+
+ base::RunLoop loop2;
+ model_wrapper_->AddOrUpdateEntry(
+ std::move(sample_save_and_recall_desk_two_),
+ base::BindLambdaForTesting(
+ [&](DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
+ loop2.Quit();
+ }));
+ loop2.Run();
+ }
+
+ void AddSavedDeskToDeskModel(std::unique_ptr<ash::DeskTemplate> entry) {
+ base::RunLoop loop;
+ model_wrapper_->AddOrUpdateEntry(
+ std::move(entry),
+ base::BindLambdaForTesting(
+ [&](DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
+ loop.Quit();
+ }));
+ loop.Run();
+ }
+
+ void VerifyAllEntries(size_t expected_size, const std::string& trace_string) {
+ SCOPED_TRACE(trace_string);
+ base::RunLoop loop;
+
+ model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), expected_size);
+ loop.Quit();
+ }));
+
+ loop.Run();
+ }
+
+ base::ScopedTempDir temp_dir_;
+ std::unique_ptr<ash::DeskTemplate> sample_desk_template_one_;
+ std::unique_ptr<ash::DeskTemplate> sample_desk_template_two_;
+ std::unique_ptr<ash::DeskTemplate> sample_save_and_recall_desk_one_;
+ std::unique_ptr<ash::DeskTemplate> sample_save_and_recall_desk_two_;
+ base::test::TaskEnvironment task_environment_;
+ std::unique_ptr<apps::AppRegistryCache> cache_;
+ AccountId account_id_;
+ std::unique_ptr<LocalDeskDataManager> data_manager_;
+ std::unique_ptr<syncer::ModelTypeStore> store_;
+ testing::NiceMock<syncer::MockModelTypeChangeProcessor> mock_processor_;
+ std::unique_ptr<DeskSyncBridge> bridge_;
+ testing::NiceMock<MockDeskModelObserver> mock_observer_;
+ std::unique_ptr<DeskModelWrapper> model_wrapper_;
+};
+
+TEST_F(DeskModelWrapperTest, CanAddDeskTemplateEntry) {
+ InitializeBridge();
+
+ model_wrapper_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(1ul, "Added one desk template");
+ base::RunLoop loop;
+ // Verify that it's not desk template entry in the save and recall desk
+ // storage.
+ model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), 1ul);
+ EXPECT_EQ(entries[0]->type(), ash::DeskTemplateType::kTemplate);
+ loop.Quit();
+ }));
+ EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 1ul);
+ EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 0ul);
+ loop.Run();
+}
+
+TEST_F(DeskModelWrapperTest, CanAddSaveAndRecallDeskEntry) {
+ InitializeBridge();
+
+ model_wrapper_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(1u, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(1ul, "Added one save and recall desk");
+ base::RunLoop loop;
+ // Verify that it's not SaveAndRecall entry in the desk template storage.
+ model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), 1ul);
+ EXPECT_EQ(entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
+ loop.Quit();
+ }));
+ EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 0ul);
+ EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 1ul);
+ loop.Run();
+}
+
+TEST_F(DeskModelWrapperTest,
+ ReturnsErrorWhenAddingTooManySaveAndRecallDeskEntry) {
+ InitializeBridge();
+ for (std::size_t index = 0;
+ index < model_wrapper_->GetMaxSaveAndRecallDeskEntryCount(); ++index) {
+ AddSavedDeskToDeskModel(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kSaveAndRecall));
+ }
+
+ model_wrapper_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(
+ model_wrapper_->GetMaxSaveAndRecallDeskEntryCount() + 1,
+ ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedErrorHitMaximumLimit));
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(DeskModelWrapperTest, CanGetAllEntries) {
+ InitializeBridge();
+
+ AddTwoTemplates();
+ base::RunLoop loop;
+ model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), 2ul);
+ EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, entries));
+ EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, entries));
+
+ // Sanity check for the search function.
+ EXPECT_FALSE(FindUuidInUuidList(kTestUuid3, entries));
+ loop.Quit();
+ }));
+ loop.Run();
+}
+
+TEST_F(DeskModelWrapperTest, GetAllEntriesIncludesPolicyValues) {
+ InitializeBridge();
+
+ AddTwoTemplates();
+ AddTwoSaveAndRecallDeskTemplates();
+ model_wrapper_->SetPolicyDeskTemplates(kPolicyWithOneTemplate);
+
+ base::RunLoop loop;
+ model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), 5ul);
+ EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, entries));
+ EXPECT_TRUE(FindUuidInUuidList(kTestUuid2, entries));
+ EXPECT_TRUE(FindUuidInUuidList(kTestUuid3, entries));
+ EXPECT_TRUE(FindUuidInUuidList(kTestUuid4, entries));
+ EXPECT_TRUE(FindUuidInUuidList(kTestUuid5, entries));
+ // One of these templates should be from policy.
+ EXPECT_EQ(
+ base::ranges::count_if(entries,
+ [](const ash::DeskTemplate* entry) {
+ return entry->source() ==
+ ash::DeskTemplateSource::kPolicy;
+ }),
+ 1l);
+
+ loop.Quit();
+ }));
+ loop.Run();
+
+ model_wrapper_->SetPolicyDeskTemplates("");
+}
+
+TEST_F(DeskModelWrapperTest, CanDetectDuplicateEntryNames) {
+ InitializeBridge();
+
+ // Add desk template entry to desk model.
+ AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
+ // Add desk template entry with the duplicated name to desk model.
+ auto dupe_template_uuid = base::StringPrintf(kUuidFormat, 6);
+ auto dupe_desk_template =
+ MakeTestDeskTemplate(dupe_template_uuid, ash::DeskTemplateSource::kUser,
+ "desk_01", base::Time::Now());
+ AddSavedDeskToDeskModel(std::move(dupe_desk_template));
+ // Add save and recall desk to desk model.
+ AddSavedDeskToDeskModel(std::move(sample_save_and_recall_desk_one_));
+ // Add save and recall entry with the duplicated name to desk model.
+ auto dupe_save_and_recall_uuid = base::StringPrintf(kUuidFormat, 7);
+ auto dupe_save_and_recall_desk = MakeTestSaveAndRecallDesk(
+ dupe_save_and_recall_uuid, "save_and_recall_desk_01", base::Time::Now());
+ AddSavedDeskToDeskModel(std::move(dupe_save_and_recall_desk));
+
+ // Add save and recall entry with the duplicated name as a desk template to
+ // desk model. This is to test that the two desk types don't share the same
+ // namespace for the sake of duplication checks.
+ auto dupe_second_save_and_recall_uuid = base::StringPrintf(kUuidFormat, 8);
+ auto dupe_second_save_and_recall_desk = MakeTestSaveAndRecallDesk(
+ dupe_second_save_and_recall_uuid, "desk_01", base::Time::Now());
+ AddSavedDeskToDeskModel(std::move(dupe_second_save_and_recall_desk));
+ base::RunLoop loop;
+ model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), 5ul);
+
+ const ash::DeskTemplate* duplicate_desk_template =
+ model_wrapper_->FindOtherEntryWithName(
+ u"desk_01", ash::DeskTemplateType::kTemplate,
+ base::GUID::ParseCaseInsensitive(dupe_template_uuid));
+ EXPECT_TRUE(duplicate_desk_template);
+
+ const ash::DeskTemplate* duplicate_save_and_recall =
+ model_wrapper_->FindOtherEntryWithName(
+ u"save_and_recall_desk_01",
+ ash::DeskTemplateType::kSaveAndRecall,
+ base::GUID::ParseCaseInsensitive(dupe_template_uuid));
+ EXPECT_TRUE(duplicate_save_and_recall);
+
+ const ash::DeskTemplate* duplicate_save_and_recall_not_found =
+ model_wrapper_->FindOtherEntryWithName(
+ u"desk_01", ash::DeskTemplateType::kSaveAndRecall,
+ base::GUID::ParseCaseInsensitive(
+ dupe_second_save_and_recall_uuid));
+ EXPECT_FALSE(duplicate_save_and_recall_not_found);
+
+ loop.Quit();
+ }));
+ loop.Run();
+}
+
+TEST_F(DeskModelWrapperTest, CanDetectNoDuplicateEntryNames) {
+ InitializeBridge();
+
+ // Add desk template entry to desk model.
+ AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
+
+ // Add a second desk template entry to the desk model with a unique name.
+ auto second_template_uuid = base::StringPrintf(kUuidFormat, 7);
+ auto second_desk_template =
+ MakeTestDeskTemplate(second_template_uuid, ash::DeskTemplateSource::kUser,
+ "desk_02", base::Time::Now());
+ AddSavedDeskToDeskModel(std::move(second_desk_template));
+
+ // Add save and recall desk to desk model.
+ AddSavedDeskToDeskModel(std::move(sample_save_and_recall_desk_one_));
+ // Add save and recall entry with the duplicated name to desk model.
+ auto second_save_and_recall_uuid = base::StringPrintf(kUuidFormat, 7);
+ auto second_save_and_recall_desk =
+ MakeTestSaveAndRecallDesk(second_save_and_recall_uuid,
+ "save_and_recall_desk_02", base::Time::Now());
+ AddSavedDeskToDeskModel(std::move(second_save_and_recall_desk));
+ base::RunLoop loop;
+ model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), 4ul);
+
+ const ash::DeskTemplate* duplicate_desk_template =
+ model_wrapper_->FindOtherEntryWithName(
+ u"desk_02", ash::DeskTemplateType::kTemplate,
+ base::GUID::ParseCaseInsensitive(second_template_uuid));
+ EXPECT_FALSE(duplicate_desk_template);
+
+ const ash::DeskTemplate* duplicate_save_and_recall =
+ model_wrapper_->FindOtherEntryWithName(
+ u"save_and_recall_desk_02",
+ ash::DeskTemplateType::kSaveAndRecall,
+ base::GUID::ParseCaseInsensitive(second_save_and_recall_uuid));
+ EXPECT_FALSE(duplicate_save_and_recall);
+ loop.Quit();
+ }));
+ loop.Run();
+}
+
+TEST_F(DeskModelWrapperTest, CanGetEntryByUuid) {
+ InitializeBridge();
+
+ // Add desk template entry to desk model.
+ AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
+
+ // Add save and recall desk to desk model.
+ AddSavedDeskToDeskModel(std::move(sample_save_and_recall_desk_one_));
+
+ // Find the desk template by its uuid.
+ model_wrapper_->GetEntryByUUID(
+ kTestUuid1,
+ base::BindLambdaForTesting([&](DeskModel::GetEntryByUuidStatus status,
+ std::unique_ptr<ash::DeskTemplate> entry) {
+ EXPECT_EQ(status, DeskModel::GetEntryByUuidStatus::kOk);
+
+ EXPECT_EQ(entry->uuid(), base::GUID::ParseCaseInsensitive(kTestUuid1));
+ EXPECT_EQ(base::UTF16ToUTF8(entry->template_name()), "desk_01");
+ }));
+
+ // Find the save and recall desk by its uuid.
+ model_wrapper_->GetEntryByUUID(
+ kTestUuid3,
+ base::BindLambdaForTesting([&](DeskModel::GetEntryByUuidStatus status,
+ std::unique_ptr<ash::DeskTemplate> entry) {
+ EXPECT_EQ(status, DeskModel::GetEntryByUuidStatus::kOk);
+
+ EXPECT_EQ(entry->uuid(), base::GUID::ParseCaseInsensitive(kTestUuid3));
+ EXPECT_EQ(base::UTF16ToUTF8(entry->template_name()),
+ "save_and_recall_desk_01");
+ }));
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(DeskModelWrapperTest, GetEntryByUuidShouldReturnAdminTemplate) {
+ InitializeBridge();
+
+ AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
+
+ // Set admin template with UUID: kTestUuid5.
+ model_wrapper_->SetPolicyDeskTemplates(kPolicyWithOneTemplate);
+
+ // Check that the admin template is included as an entry.
+ EXPECT_EQ(model_wrapper_->GetAllEntryUuids().size(), 2ul);
+
+ model_wrapper_->GetEntryByUUID(
+ kTestUuid5,
+ base::BindLambdaForTesting([&](DeskModel::GetEntryByUuidStatus status,
+ std::unique_ptr<ash::DeskTemplate> entry) {
+ EXPECT_EQ(status, DeskModel::GetEntryByUuidStatus::kOk);
+ EXPECT_EQ(entry->uuid(), base::GUID::ParseCaseInsensitive(kTestUuid5));
+ EXPECT_EQ(entry->source(), ash::DeskTemplateSource::kPolicy);
+ EXPECT_EQ(base::UTF16ToUTF8(entry->template_name()),
+ "Admin Template 1");
+ }));
+}
+
+TEST_F(DeskModelWrapperTest, GetEntryByUuidReturnsNotFoundIfEntryDoesNotExist) {
+ InitializeBridge();
+
+ base::RunLoop loop;
+
+ model_wrapper_->GetEntryByUUID(
+ kTestUuid1,
+ base::BindLambdaForTesting([&](DeskModel::GetEntryByUuidStatus status,
+ std::unique_ptr<ash::DeskTemplate> entry) {
+ EXPECT_EQ(status, DeskModel::GetEntryByUuidStatus::kNotFound);
+ loop.Quit();
+ }));
+ loop.Run();
+}
+
+TEST_F(DeskModelWrapperTest, CanUpdateEntry) {
+ InitializeBridge();
+
+ // Make a clone of a desk template and modify its name.
+ auto modified_desk_template = sample_desk_template_one_->Clone();
+ modified_desk_template->set_template_name(u"desk_01_mod");
+
+ AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
+
+ AddSavedDeskToDeskModel(std::move(modified_desk_template));
+
+ // Make a clone of a save and recall desk and modify its name.
+ auto modified_save_and_recall_desk =
+ sample_save_and_recall_desk_one_->Clone();
+ modified_save_and_recall_desk->set_template_name(
+ u"save_and_recall_desk_01_mod");
+
+ AddSavedDeskToDeskModel(std::move(sample_save_and_recall_desk_one_));
+
+ AddSavedDeskToDeskModel(std::move(modified_save_and_recall_desk));
+
+ // Check that the entries are updated.
+ model_wrapper_->GetEntryByUUID(
+ kTestUuid1,
+ base::BindLambdaForTesting([&](DeskModel::GetEntryByUuidStatus status,
+ std::unique_ptr<ash::DeskTemplate> entry) {
+ EXPECT_EQ(status, DeskModel::GetEntryByUuidStatus::kOk);
+
+ EXPECT_EQ(entry->uuid(), base::GUID::ParseCaseInsensitive(kTestUuid1));
+ EXPECT_EQ(entry->template_name(),
+ base::UTF8ToUTF16(std::string("desk_01_mod")));
+ }));
+
+ model_wrapper_->GetEntryByUUID(
+ kTestUuid3,
+ base::BindLambdaForTesting([&](DeskModel::GetEntryByUuidStatus status,
+ std::unique_ptr<ash::DeskTemplate> entry) {
+ EXPECT_EQ(status, DeskModel::GetEntryByUuidStatus::kOk);
+
+ EXPECT_EQ(entry->uuid(), base::GUID::ParseCaseInsensitive(kTestUuid3));
+ EXPECT_EQ(entry->template_name(), u"save_and_recall_desk_01_mod");
+ }));
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(DeskModelWrapperTest, CanDeleteDeskTemplateEntry) {
+ InitializeBridge();
+
+ model_wrapper_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ model_wrapper_->DeleteEntry(
+ kTestUuid1,
+ base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
+ }));
+
+ VerifyAllEntries(0ul, "Delete desk template");
+}
+
+TEST_F(DeskModelWrapperTest, CanDeleteSaveAndRecallDeskEntry) {
+ InitializeBridge();
+
+ model_wrapper_->AddOrUpdateEntry(std::move(sample_save_and_recall_desk_one_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ model_wrapper_->DeleteEntry(
+ kTestUuid3,
+ base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
+ }));
+
+ VerifyAllEntries(0ul, "Delete save and recall desk");
+}
+
+TEST_F(DeskModelWrapperTest, CanDeleteAllEntries) {
+ InitializeBridge();
+
+ AddTwoTemplates();
+ AddTwoSaveAndRecallDeskTemplates();
+
+ model_wrapper_->DeleteAllEntries(
+ base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
+ }));
+
+ VerifyAllEntries(0ul,
+ "Delete all entries after adding two desk templates and two "
+ "save and recall desks");
+}
+
+TEST_F(DeskModelWrapperTest,
+ GetEntryCountShouldIncludeBothUserAndAdminTemplates) {
+ InitializeBridge();
+
+ // Add two user templates.
+ AddTwoTemplates();
+ // Add two save and recall desks templates.
+ AddTwoSaveAndRecallDeskTemplates();
+
+ // Set one admin template.
+ model_wrapper_->SetPolicyDeskTemplates(kPolicyWithOneTemplate);
+
+ // There should be 5 templates: 2 user templates + 1 admin template + 2 save
+ // and recall desks.
+ EXPECT_EQ(model_wrapper_->GetEntryCount(), 5ul);
+ // MaxEntryCount should be 6 max save and recall desks + 6 max user templates
+ // + 1 admin template.
+ EXPECT_EQ(model_wrapper_->GetMaxEntryCount(), 13ul);
+}
+
+TEST_F(DeskModelWrapperTest, GetMaxEntryCountShouldIncreaseWithAdminTemplates) {
+ InitializeBridge();
+
+ // Add two user templates.
+ AddTwoTemplates();
+
+ std::size_t max_entry_count = model_wrapper_->GetMaxDeskTemplateEntryCount();
+
+ // Set one admin template.
+ model_wrapper_->SetPolicyDeskTemplates(kPolicyWithOneTemplate);
+
+ // The max entry count should increase by 1 since we have set an admin
+ // template.
+ EXPECT_EQ(model_wrapper_->GetMaxDeskTemplateEntryCount(),
+ max_entry_count + 1ul);
+ // Sanity check to make sure that save and recall desk max count isn't
+ // affected by the admin template.
+ EXPECT_EQ(model_wrapper_->GetMaxSaveAndRecallDeskEntryCount(), 6ul);
+}
+
+TEST_F(DeskModelWrapperTest, AddDeskTemplatesAndSaveAndRecallDeskEntries) {
+ InitializeBridge();
+
+ // Add two user templates.
+ AddTwoTemplates();
+
+ // Add two SaveAndRecall desks.
+ AddTwoSaveAndRecallDeskTemplates();
+
+ EXPECT_EQ(model_wrapper_->GetEntryCount(), 4ul);
+ EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 2ul);
+ EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 2ul);
+
+ base::RunLoop loop;
+ model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ loop.Quit();
+ }));
+
+ loop.Run();
+ VerifyAllEntries(4ul,
+ "Add two desks templates and two saved and recall desks");
+}
+
+TEST_F(DeskModelWrapperTest, AddSaveAndRecallDeskEntry) {
+ InitializeBridge();
+
+ model_wrapper_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(1u, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(1ul, "Added one save and recall desk");
+ base::RunLoop loop;
+ // Verify that it's not SaveAndRecall entry in the desk template cache.
+ model_wrapper_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), 1ul);
+ EXPECT_EQ(entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
+ loop.Quit();
+ }));
+ EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 0ul);
+ EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 1ul);
+ loop.Run();
+}
+
+TEST_F(DeskModelWrapperTest, CanAddMaxEntriesForBothTypes) {
+ InitializeBridge();
+
+ for (std::size_t index = 0u;
+ index < model_wrapper_->GetMaxSaveAndRecallDeskEntryCount(); ++index) {
+ model_wrapper_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+ for (std::size_t index = 0u;
+ index < model_wrapper_->GetMaxDeskTemplateEntryCount(); ++index) {
+ model_wrapper_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+
+ VerifyAllEntries(
+ 12ul, "Added max number of save and recall desks and desk templates");
+ EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 6ul);
+ EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 6ul);
+}
+
+TEST_F(DeskModelWrapperTest,
+ CanAddMaxEntriesDeskTemplatesAndStillAddEntryForSaveAndRecallDesks) {
+ InitializeBridge();
+
+ for (std::size_t index = 0u;
+ index < model_wrapper_->GetMaxDeskTemplateEntryCount(); ++index) {
+ model_wrapper_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+ model_wrapper_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(1ul, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(7ul,
+ "Added one save and recall desk after capping "
+ "desk template entries");
+
+ EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 6ul);
+ EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 1ul);
+}
+
+TEST_F(DeskModelWrapperTest,
+ CanAddMaxEntriesForSaveAndRecallDeskAndStillAddEntryForDeskTemplate) {
+ InitializeBridge();
+
+ for (std::size_t index = 0u;
+ index < model_wrapper_->GetMaxSaveAndRecallDeskEntryCount(); ++index) {
+ model_wrapper_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+
+ model_wrapper_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(1ul, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(7ul,
+ "Added one desk template after capping "
+ "save and recall desk entries");
+
+ EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 1ul);
+ EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 6ul);
+}
+
+} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/desk_sync_bridge.cc b/chromium/components/desks_storage/core/desk_sync_bridge.cc
index 57c8404b4c1..24da0bfdf17 100644
--- a/chromium/components/desks_storage/core/desk_sync_bridge.cc
+++ b/chromium/components/desks_storage/core/desk_sync_bridge.cc
@@ -17,6 +17,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
+#include "build/chromeos_buildflags.h"
#include "chromeos/ui/base/window_state_type.h"
#include "components/account_id/account_id.h"
#include "components/app_constants/constants.h"
@@ -64,6 +65,9 @@ using SyncWindowOpenDisposition =
using ProgressiveWebApp = sync_pb::WorkspaceDeskSpecifics_ProgressiveWebApp;
using ChromeApp = sync_pb::WorkspaceDeskSpecifics_ChromeApp;
using WorkspaceDeskSpecifics_App = sync_pb::WorkspaceDeskSpecifics_App;
+using SyncTabGroup = sync_pb::WorkspaceDeskSpecifics_BrowserAppWindow_TabGroup;
+using SyncTabGroupColor = sync_pb::WorkspaceDeskSpecifics_TabGroupColor;
+using TabGroupColor = tab_groups::TabGroupColorId;
namespace {
@@ -79,9 +83,7 @@ constexpr std::size_t kMaxTemplateCount = 6u;
// chrome.storage.sync.QUOTA_BYTES_PER_ITEM.
constexpr std::size_t kMaxTemplateSize = 8192u;
-// Allocate a EntityData and copies |specifics| into it.
-//
-// TODO(crbug/1304465): Switch symbol identifiers to new standard.
+// Allocate a EntityData and copies `specifics` into it.
std::unique_ptr<syncer::EntityData> CopyToEntityData(
const sync_pb::WorkspaceDeskSpecifics& specifics) {
auto entity_data = std::make_unique<syncer::EntityData>();
@@ -92,7 +94,7 @@ std::unique_ptr<syncer::EntityData> CopyToEntityData(
return entity_data;
}
-// Parses the content of |record_list| into |*desk_templates|.
+// Parses the content of `record_list` into `*desk_templates`.
absl::optional<syncer::ModelError> ParseDeskTemplatesOnBackendSequence(
std::map<base::GUID, std::unique_ptr<DeskTemplate>>* desk_templates,
std::unique_ptr<ModelTypeStore::RecordList> record_list) {
@@ -128,15 +130,80 @@ absl::optional<syncer::ModelError> ParseDeskTemplatesOnBackendSequence(
return absl::nullopt;
}
-// Fill |out_gurls| using tabs' URL in |browser_app_window|.
-void FillUrlList(std::vector<GURL>* out_gurls,
- const BrowserAppWindow& browser_app_window) {
+// Fill `out_gurls` using tabs' URL in `browser_app_window`.
+void FillUrlList(const BrowserAppWindow& browser_app_window,
+ std::vector<GURL>* out_gurls) {
for (auto tab : browser_app_window.tabs()) {
if (tab.has_url())
out_gurls->emplace_back(tab.url());
}
}
+// Since tab groups must have completely valid fields therefore this function
+// exists to validate that sync tab groups are entirely valid.
+bool ValidSyncTabGroup(const SyncTabGroup& sync_tab_group) {
+ return sync_tab_group.has_first_index() && sync_tab_group.has_last_index() &&
+ sync_tab_group.has_title() && sync_tab_group.has_color();
+}
+
+// Converts a sync tab group color to its tab_groups::TabGroupColorId
+// equivalent.
+TabGroupColor TabGroupColorIdFromSyncTabColor(
+ const SyncTabGroupColor& sync_color) {
+ switch (sync_color) {
+ // Default to grey if unknown.
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_UNKNOWN_COLOR:
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREY:
+ return TabGroupColor::kGrey;
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_BLUE:
+ return TabGroupColor::kBlue;
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_RED:
+ return TabGroupColor::kRed;
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_YELLOW:
+ return TabGroupColor::kYellow;
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN:
+ return TabGroupColor::kGreen;
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PINK:
+ return TabGroupColor::kPink;
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PURPLE:
+ return TabGroupColor::kPurple;
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_CYAN:
+ return TabGroupColor::kCyan;
+ case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_ORANGE:
+ return TabGroupColor::kOrange;
+ };
+}
+
+// Instantiates a TabGroup from its sync equivalent.
+app_restore::TabGroupInfo FillTabGroupInfoFromProto(
+ const SyncTabGroup& sync_tab_group) {
+ // This function should never be called with a partially instantiated
+ // tab group.
+ DCHECK(ValidSyncTabGroup(sync_tab_group));
+
+ return app_restore::TabGroupInfo(
+ {static_cast<uint32_t>(sync_tab_group.first_index()),
+ static_cast<uint32_t>(sync_tab_group.last_index())},
+ tab_groups::TabGroupVisualData(
+ base::UTF8ToUTF16(sync_tab_group.title()),
+ TabGroupColorIdFromSyncTabColor(sync_tab_group.color()),
+ sync_tab_group.is_collapsed()));
+}
+
+// Fill `out_group_infos` using information found in the proto's
+// tab group structure.
+void FillTabGroupInfosFromProto(
+ const BrowserAppWindow& browser_app_window,
+ std::vector<app_restore::TabGroupInfo>* out_group_infos) {
+ for (const auto& group : browser_app_window.tab_groups()) {
+ if (!ValidSyncTabGroup(group)) {
+ continue;
+ }
+
+ out_group_infos->push_back(FillTabGroupInfoFromProto(group));
+ }
+}
+
// Get App ID from App proto.
std::string GetAppId(const sync_pb::WorkspaceDeskSpecifics_App& app) {
switch (app.app().app_case()) {
@@ -167,7 +234,7 @@ std::string GetAppId(const sync_pb::WorkspaceDeskSpecifics_App& app) {
}
}
-// Convert App proto to |app_restore::AppLaunchInfo|.
+// Convert App proto to `app_restore::AppLaunchInfo`.
std::unique_ptr<app_restore::AppLaunchInfo> ConvertToAppLaunchInfo(
const sync_pb::WorkspaceDeskSpecifics_App& app) {
const int32_t window_id = app.window_id();
@@ -196,7 +263,7 @@ std::unique_ptr<app_restore::AppLaunchInfo> ConvertToAppLaunchInfo(
if (app.has_app_name())
app_launch_info->app_name = app.app_name();
- // This is a short-term fix as |event_flag| is required to launch ArcApp.
+ // This is a short-term fix as `event_flag` is required to launch ArcApp.
// Currently we don't support persisting user action in template
// so always default to 0 which is no action.
// https://source.chromium.org/chromium/chromium/src/
@@ -207,8 +274,8 @@ std::unique_ptr<app_restore::AppLaunchInfo> ConvertToAppLaunchInfo(
switch (app.app().app_case()) {
case sync_pb::WorkspaceDeskSpecifics_AppOneOf::AppCase::APP_NOT_SET:
- // This should never happen. |APP_NOT_SET| corresponds to empty |app_id|.
- // This method will early return when |app_id| is empty.
+ // This should never happen. `APP_NOT_SET` corresponds to empty `app_id`.
+ // This method will early return when `app_id` is empty.
NOTREACHED();
break;
case sync_pb::WorkspaceDeskSpecifics_AppOneOf::AppCase::kBrowserAppWindow:
@@ -218,8 +285,14 @@ std::unique_ptr<app_restore::AppLaunchInfo> ConvertToAppLaunchInfo(
}
app_launch_info->urls.emplace();
- FillUrlList(&app_launch_info->urls.value(),
- app.app().browser_app_window());
+ FillUrlList(app.app().browser_app_window(),
+ &app_launch_info->urls.value());
+
+ if (app.app().browser_app_window().tab_groups_size() > 0) {
+ app_launch_info->tab_group_infos.emplace();
+ FillTabGroupInfosFromProto(app.app().browser_app_window(),
+ &app_launch_info->tab_group_infos.value());
+ }
if (app.app().browser_app_window().has_show_as_app())
app_launch_info->app_type_browser =
@@ -227,20 +300,20 @@ std::unique_ptr<app_restore::AppLaunchInfo> ConvertToAppLaunchInfo(
break;
case sync_pb::WorkspaceDeskSpecifics_AppOneOf::AppCase::kChromeApp:
- // |app_id| is enough to identify a Chrome app.
+ // `app_id` is enough to identify a Chrome app.
break;
case sync_pb::WorkspaceDeskSpecifics_AppOneOf::AppCase::kProgressWebApp:
- // |app_id| is enough to identify a Progressive Web app.
+ // `app_id` is enough to identify a Progressive Web app.
break;
case sync_pb::WorkspaceDeskSpecifics_AppOneOf::AppCase::kArcApp:
- // |app_id| is enough to identify an Arc app.
+ // `app_id` is enough to identify an Arc app.
break;
}
return app_launch_info;
}
-// Convert Sync proto WindowState |state| to ui::WindowShowState used by
+// Convert Sync proto WindowState `state` to ui::WindowShowState used by
// the app_restore::WindowInfo struct.
ui::WindowShowState ToUiWindowState(WindowState state) {
switch (state) {
@@ -261,8 +334,8 @@ ui::WindowShowState ToUiWindowState(WindowState state) {
}
}
-// Convert Sync proto WindowState |state| to chromeos::WindowStateType used by
-// the app_restore::WindowInfo struct.
+// Convert Sync proto WindowState `state` to chromeos::WindowStateType used
+// by the app_restore::WindowInfo struct.
chromeos::WindowStateType ToChromeOsWindowState(WindowState state) {
switch (state) {
case WindowState::WorkspaceDeskSpecifics_WindowState_UNKNOWN_WINDOW_STATE:
@@ -292,6 +365,8 @@ WindowState FromChromeOsWindowState(chromeos::WindowStateType state) {
case chromeos::WindowStateType::kPinned:
case chromeos::WindowStateType::kTrustedPinned:
case chromeos::WindowStateType::kPip:
+ // TODO(crbug.com/1331825): Float state support for desk template.
+ case chromeos::WindowStateType::kFloated:
return WindowState::WorkspaceDeskSpecifics_WindowState_NORMAL;
case chromeos::WindowStateType::kMinimized:
return WindowState::WorkspaceDeskSpecifics_WindowState_MINIMIZED;
@@ -323,9 +398,60 @@ WindowState FromUiWindowState(ui::WindowShowState state) {
}
}
-// Fill |out_browser_app_window| with the given GURLs as BrowserAppTabs.
-void FillBrowserAppTabs(BrowserAppWindow* out_browser_app_window,
- const std::vector<GURL>& gurls) {
+// Converts a sync tab group color to its tab_groups::TabGroupColorId
+// equivalent.
+SyncTabGroupColor SyncTabColorFromTabGroupColorId(
+ const TabGroupColor& sync_color) {
+ switch (sync_color) {
+ case TabGroupColor::kGrey:
+ return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREY;
+ case TabGroupColor::kBlue:
+ return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_BLUE;
+ case TabGroupColor::kRed:
+ return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_RED;
+ case TabGroupColor::kYellow:
+ return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_YELLOW;
+ case TabGroupColor::kGreen:
+ return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN;
+ case TabGroupColor::kPink:
+ return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PINK;
+ case TabGroupColor::kPurple:
+ return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PURPLE;
+ case TabGroupColor::kCyan:
+ return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_CYAN;
+ case TabGroupColor::kOrange:
+ return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_ORANGE;
+ };
+}
+
+void FillSyncTabGroupInfo(const app_restore::TabGroupInfo& tab_group_info,
+ SyncTabGroup* out_sync_tab_group) {
+ out_sync_tab_group->set_first_index(tab_group_info.tab_range.start());
+ out_sync_tab_group->set_last_index(tab_group_info.tab_range.end());
+ out_sync_tab_group->set_title(
+ base::UTF16ToUTF8(tab_group_info.visual_data.title()));
+ // Save some storage space by leaving is_collapsed to default value if the
+ // tab group isn't collapsed.
+ if (tab_group_info.visual_data.is_collapsed()) {
+ out_sync_tab_group->set_is_collapsed(
+ tab_group_info.visual_data.is_collapsed());
+ }
+ out_sync_tab_group->set_color(
+ SyncTabColorFromTabGroupColorId(tab_group_info.visual_data.color()));
+}
+
+void FillBrowserAppTabGroupInfos(
+ const std::vector<app_restore::TabGroupInfo>& tab_group_infos,
+ BrowserAppWindow* out_browser_app_window) {
+ for (const auto& tab_group : tab_group_infos) {
+ SyncTabGroup* sync_tab_group = out_browser_app_window->add_tab_groups();
+ FillSyncTabGroupInfo(tab_group, sync_tab_group);
+ }
+}
+
+// Fill `out_browser_app_window` with the given GURLs as BrowserAppTabs.
+void FillBrowserAppTabs(const std::vector<GURL>& gurls,
+ BrowserAppWindow* out_browser_app_window) {
for (const auto& gurl : gurls) {
const std::string& url = gurl.spec();
if (url.empty()) {
@@ -337,12 +463,12 @@ void FillBrowserAppTabs(BrowserAppWindow* out_browser_app_window,
}
}
-// Fill |out_browser_app_window| with urls and tab information from
-// |app_restore_data|.
-void FillBrowserAppWindow(BrowserAppWindow* out_browser_app_window,
- const app_restore::AppRestoreData* app_restore_data) {
+// Fill `out_browser_app_window` with urls and tab information from
+// `app_restore_data`.
+void FillBrowserAppWindow(const app_restore::AppRestoreData* app_restore_data,
+ BrowserAppWindow* out_browser_app_window) {
if (app_restore_data->urls.has_value())
- FillBrowserAppTabs(out_browser_app_window, app_restore_data->urls.value());
+ FillBrowserAppTabs(app_restore_data->urls.value(), out_browser_app_window);
if (app_restore_data->active_tab_index.has_value()) {
out_browser_app_window->set_active_tab_index(
@@ -353,28 +479,30 @@ void FillBrowserAppWindow(BrowserAppWindow* out_browser_app_window,
out_browser_app_window->set_show_as_app(
app_restore_data->app_type_browser.value());
}
+
+ if (app_restore_data->tab_group_infos.has_value()) {
+ FillBrowserAppTabGroupInfos(app_restore_data->tab_group_infos.value(),
+ out_browser_app_window);
+ }
}
-// Fill |out_window_bound| with information from |bound|.
-//
-// TOOD(crbug/1295051): here and elsewhere move out_* args to end of parameter
-// lists.
-void FillWindowBound(WindowBound* out_window_bound, const gfx::Rect& bound) {
- out_window_bound->set_left(bound.x());
- out_window_bound->set_top(bound.y());
- out_window_bound->set_width(bound.width());
- out_window_bound->set_height(bound.height());
+// Fill `out_window_bounds` with information from `bounds`.
+void FillWindowBound(const gfx::Rect& bounds, WindowBound* out_window_bounds) {
+ out_window_bounds->set_left(bounds.x());
+ out_window_bounds->set_top(bounds.y());
+ out_window_bounds->set_width(bounds.width());
+ out_window_bounds->set_height(bounds.height());
}
-// Fill |out_app| with information from |window_info|.
-void FillAppWithWindowInfo(WorkspaceDeskSpecifics_App* out_app,
- const app_restore::WindowInfo* window_info) {
+// Fill `out_app` with information from `window_info`.
+void FillAppWithWindowInfo(const app_restore::WindowInfo* window_info,
+ WorkspaceDeskSpecifics_App* out_app) {
if (window_info->activation_index.has_value())
out_app->set_z_index(window_info->activation_index.value());
if (window_info->current_bounds.has_value()) {
- FillWindowBound(out_app->mutable_window_bound(),
- window_info->current_bounds.value());
+ FillWindowBound(window_info->current_bounds.value(),
+ out_app->mutable_window_bound());
}
if (window_info->window_state_type.has_value()) {
@@ -393,13 +521,13 @@ void FillAppWithWindowInfo(WorkspaceDeskSpecifics_App* out_app,
if (window_info->app_title.has_value())
out_app->set_title(base::UTF16ToUTF8(window_info->app_title.value()));
- // AppRestoreData.GetWindowInfo does not include |display_id| in the returned
- // WindowInfo. Therefore, we are not filling |display_id| here.
+ // AppRestoreData.GetWindowInfo does not include `display_id` in the returned
+ // WindowInfo. Therefore, we are not filling `display_id` here.
}
-// Fill |out_app| with |display_id| from |app_restore_data|.
-void FillAppWithDisplayId(WorkspaceDeskSpecifics_App* out_app,
- const app_restore::AppRestoreData* app_restore_data) {
+// Fill `out_app` with the `display_id` from `app_restore_data`.
+void FillAppWithDisplayId(const app_restore::AppRestoreData* app_restore_data,
+ WorkspaceDeskSpecifics_App* out_app) {
if (app_restore_data->display_id.has_value())
out_app->set_display_id(app_restore_data->display_id.value());
}
@@ -442,31 +570,31 @@ void FillAppWithAppNameAndTitle(
}
}
-void FillArcAppSize(ArcAppWindowSize* out_window_size, const gfx::Size& size) {
+void FillArcAppSize(const gfx::Size& size, ArcAppWindowSize* out_window_size) {
out_window_size->set_width(size.width());
out_window_size->set_height(size.height());
}
-void FillArcBoundsInRoot(WindowBound* out_rect, const gfx::Rect& data_rect) {
+void FillArcBoundsInRoot(const gfx::Rect& data_rect, WindowBound* out_rect) {
out_rect->set_left(data_rect.x());
out_rect->set_top(data_rect.y());
out_rect->set_width(data_rect.width());
out_rect->set_height(data_rect.height());
}
-void FillArcApp(ArcApp* out_app,
- const app_restore::AppRestoreData* app_restore_data) {
+void FillArcApp(const app_restore::AppRestoreData* app_restore_data,
+ ArcApp* out_app) {
if (app_restore_data->minimum_size.has_value()) {
- FillArcAppSize(out_app->mutable_minimum_size(),
- app_restore_data->minimum_size.value());
+ FillArcAppSize(app_restore_data->minimum_size.value(),
+ out_app->mutable_minimum_size());
}
if (app_restore_data->maximum_size.has_value()) {
- FillArcAppSize(out_app->mutable_maximum_size(),
- app_restore_data->maximum_size.value());
+ FillArcAppSize(app_restore_data->maximum_size.value(),
+ out_app->mutable_maximum_size());
}
if (app_restore_data->bounds_in_root.has_value()) {
- FillArcBoundsInRoot(out_app->mutable_bounds_in_root(),
- app_restore_data->bounds_in_root.value());
+ FillArcBoundsInRoot(app_restore_data->bounds_in_root.value(),
+ out_app->mutable_bounds_in_root());
}
}
@@ -484,64 +612,111 @@ void FillAppWithLaunchContainerAndOpenDisposition(
FillAppWithWindowOpenDisposition(app_restore_data, out_app);
}
-// Fill |out_app| with |app_restore_data|.
-void FillApp(WorkspaceDeskSpecifics_App* out_app,
- const std::string& app_id,
+// Fill `out_app` with `app_restore_data`.
+// Return `false` if app type is unsupported.
+bool FillApp(const std::string& app_id,
const apps::AppType app_type,
- const app_restore::AppRestoreData* app_restore_data) {
- FillAppWithWindowInfo(out_app, app_restore_data->GetWindowInfo().get());
-
- // AppRestoreData.GetWindowInfo does not include |display_id| in the returned
- // WindowInfo. We need to fill the |display_id| from AppRestoreData.
- FillAppWithDisplayId(out_app, app_restore_data);
-
- // If present, fills the proto's `app_name` and `title` fields with the
- // information stored in the `app_restore_data`'s `app_name` and `title`
- // fields.
- FillAppWithAppNameAndTitle(app_restore_data, out_app);
-
- // See definition components/services/app_service/public/mojom/types.mojom
+ const app_restore::AppRestoreData* app_restore_data,
+ WorkspaceDeskSpecifics_App* out_app) {
+ // See definition in components/services/app_service/public/cpp/app_types.h
switch (app_type) {
case apps::AppType::kWeb:
- case apps::AppType::kStandaloneBrowser: {
- if (app_constants::kChromeAppId == app_id ||
- app_constants::kLacrosAppId == app_id) {
- // Chrome or Lacros Browser Window.
+ case apps::AppType::kSystemWeb: {
+ // System Web Apps.
+ // kSystemWeb is returned for System Web Apps in Lacros-primary
+ // configuration. These can be persisted and launched the same way as
+ // Chrome Apps.
+ ChromeApp* chrome_app_window =
+ out_app->mutable_app()->mutable_chrome_app();
+ chrome_app_window->set_app_id(app_id);
+ FillAppWithLaunchContainerAndOpenDisposition(app_restore_data, out_app);
+ break;
+ }
+
+ case apps::AppType::kChromeApp: {
+ // Ash Chrome browser OR PWA OR Chrome App hosted in Ash Chrome.
+ if (app_constants::kChromeAppId == app_id) {
+ // This window is either a browser window or a PWA window.
+ // Both cases are persisted as "browser app" since they are launched the
+ // same way. PWA window will have field `app_name` and
+ // `app_type_browser` fields set. FillAppWithAppNameAndTitle has
+ // persisted `app_name` field. FillBrowserAppWindow will persist
+ // `app_type_browser` field.
BrowserAppWindow* browser_app_window =
out_app->mutable_app()->mutable_browser_app_window();
- FillBrowserAppWindow(browser_app_window, app_restore_data);
+ FillBrowserAppWindow(app_restore_data, browser_app_window);
} else {
- // PWA app.
- ProgressiveWebApp* pwa_window =
- out_app->mutable_app()->mutable_progress_web_app();
- pwa_window->set_app_id(app_id);
+ // Chrome App
+ ChromeApp* chrome_app_window =
+ out_app->mutable_app()->mutable_chrome_app();
+ chrome_app_window->set_app_id(app_id);
FillAppWithLaunchContainerAndOpenDisposition(app_restore_data, out_app);
}
break;
}
- case apps::AppType::kChromeApp: {
- // Chrome extension backed app, Chrome Apps
+
+ case apps::AppType::kStandaloneBrowser: {
+ if (app_constants::kLacrosAppId == app_id) {
+ // Lacros Chrome browser window or PWA hosted in Lacros Chrome.
+ BrowserAppWindow* browser_app_window =
+ out_app->mutable_app()->mutable_browser_app_window();
+ FillBrowserAppWindow(app_restore_data, browser_app_window);
+ } else {
+ // Chrome app running in Lacros should have
+ // AppType::kStandaloneBrowserChromeApp and never reach here.
+ NOTREACHED();
+ // Ignore this app type.
+ return false;
+ }
+
+ break;
+ }
+
+ case apps::AppType::kStandaloneBrowserChromeApp: {
+ // Chrome App hosted in Lacros.
ChromeApp* chrome_app_window =
out_app->mutable_app()->mutable_chrome_app();
chrome_app_window->set_app_id(app_id);
FillAppWithLaunchContainerAndOpenDisposition(app_restore_data, out_app);
break;
}
+
case apps::AppType::kArc: {
ArcApp* arc_app = out_app->mutable_app()->mutable_arc_app();
arc_app->set_app_id(app_id);
- FillArcApp(arc_app, app_restore_data);
- break;
- }
- default: {
- // Unhandled app type.
+ FillArcApp(app_restore_data, arc_app);
break;
}
+
+ case apps::AppType::kBuiltIn:
+ case apps::AppType::kCrostini:
+ case apps::AppType::kPluginVm:
+ case apps::AppType::kUnknown:
+ case apps::AppType::kMacOs:
+ case apps::AppType::kRemote:
+ case apps::AppType::kBorealis:
+ case apps::AppType::kExtension:
+ case apps::AppType::kStandaloneBrowserExtension:
+ // Unsupported app types will be ignored.
+ return false;
}
+
+ FillAppWithWindowInfo(app_restore_data->GetWindowInfo().get(), out_app);
+
+ // AppRestoreData.GetWindowInfo does not include `display_id` in the returned
+ // WindowInfo. We need to fill the `display_id` from AppRestoreData.
+ FillAppWithDisplayId(app_restore_data, out_app);
+
+ // If present, fills the proto's `app_name` and `title` fields with the
+ // information stored in the `app_restore_data`'s `app_name` and `title`
+ // fields.
+ FillAppWithAppNameAndTitle(app_restore_data, out_app);
+
+ return true;
}
-void FillArcExtraInfoFromProto(app_restore::WindowInfo* out_window_info,
- const ArcApp& app) {
+void FillArcExtraInfoFromProto(const ArcApp& app,
+ app_restore::WindowInfo* out_window_info) {
out_window_info->arc_extra_info.emplace();
app_restore::WindowInfo::ArcExtraInfo& arc_info =
out_window_info->arc_extra_info.value();
@@ -561,9 +736,9 @@ void FillArcExtraInfoFromProto(app_restore::WindowInfo* out_window_info,
}
}
-// Fill |out_window_info| with information from Sync proto |app|.
-void FillWindowInfoFromProto(app_restore::WindowInfo* out_window_info,
- sync_pb::WorkspaceDeskSpecifics_App& app) {
+// Fill `out_window_info` with information from Sync proto `app`.
+void FillWindowInfoFromProto(sync_pb::WorkspaceDeskSpecifics_App& app,
+ app_restore::WindowInfo* out_window_info) {
if (app.has_window_state() &&
sync_pb::WorkspaceDeskSpecifics_WindowState_IsValid(app.window_state())) {
out_window_info->window_state_type.emplace(
@@ -602,11 +777,11 @@ void FillWindowInfoFromProto(app_restore::WindowInfo* out_window_info,
if (app.app().app_case() ==
sync_pb::WorkspaceDeskSpecifics_AppOneOf::AppCase::kArcApp) {
- FillArcExtraInfoFromProto(out_window_info, app.app().arc_app());
+ FillArcExtraInfoFromProto(app.app().arc_app(), out_window_info);
}
}
-// Convert a desk template to |app_restore::RestoreData|.
+// Convert a desk template to `app_restore::RestoreData`.
std::unique_ptr<app_restore::RestoreData> ConvertToRestoreData(
const sync_pb::WorkspaceDeskSpecifics& entry_proto) {
auto restore_data = std::make_unique<app_restore::RestoreData>();
@@ -623,7 +798,7 @@ std::unique_ptr<app_restore::RestoreData> ConvertToRestoreData(
restore_data->AddAppLaunchInfo(std::move(app_launch_info));
app_restore::WindowInfo app_window_info;
- FillWindowInfoFromProto(&app_window_info, app_proto);
+ FillWindowInfoFromProto(app_proto, &app_window_info);
restore_data->ModifyWindowInfo(app_id, app_proto.window_id(),
app_window_info);
@@ -632,12 +807,12 @@ std::unique_ptr<app_restore::RestoreData> ConvertToRestoreData(
return restore_data;
}
-// Fill a desk template |out_entry_proto| with information from
-// |restore_data|.
+// Fill a desk template `out_entry_proto` with information from
+// `restore_data`.
void FillWorkspaceDeskSpecifics(
- sync_pb::WorkspaceDeskSpecifics* out_entry_proto,
apps::AppRegistryCache* apps_cache,
- const app_restore::RestoreData* restore_data) {
+ const app_restore::RestoreData* restore_data,
+ sync_pb::WorkspaceDeskSpecifics* out_entry_proto) {
DCHECK(apps_cache);
for (auto const& app_id_to_launch_list :
@@ -648,24 +823,24 @@ void FillWorkspaceDeskSpecifics(
const int window_id = window_id_to_launch_info.first;
const app_restore::AppRestoreData* app_restore_data =
window_id_to_launch_info.second.get();
- // The apps cache returns kChromeApp for browser windows, therefore we
- // short circuit the cache retrieval if we get the browser ID.
- const auto app_type = app_id == app_constants::kChromeAppId
- ? apps::AppType::kWeb
- : apps_cache->GetAppType(app_id);
+
+ const auto app_type = apps_cache->GetAppType(app_id);
WorkspaceDeskSpecifics_App* app =
out_entry_proto->mutable_desk()->add_apps();
app->set_window_id(window_id);
- FillApp(app, app_id, app_type, app_restore_data);
+ if (!FillApp(app_id, app_type, app_restore_data, app)) {
+ // Unsupported app type, remove this app entry.
+ out_entry_proto->mutable_desk()->mutable_apps()->RemoveLast();
+ }
}
}
}
// Fill a desk template `out_entry_proto` with the type of desk based on the
// desk's type field.
-void FillDeskType(sync_pb::WorkspaceDeskSpecifics* out_entry_proto,
- const DeskTemplate* desk_template) {
+void FillDeskType(const DeskTemplate* desk_template,
+ sync_pb::WorkspaceDeskSpecifics* out_entry_proto) {
switch (desk_template->type()) {
case DeskTemplateType::kTemplate:
out_entry_proto->set_desk_type(
@@ -720,20 +895,16 @@ std::unique_ptr<DeskTemplate> DeskSyncBridge::FromSyncProto(
// Protobuf parsing enforces UTF-8 encoding for all strings.
auto desk_template = std::make_unique<DeskTemplate>(
- uuid, ash::DeskTemplateSource::kUser, pb_entry.name(), created_time);
+ uuid, ash::DeskTemplateSource::kUser, pb_entry.name(), created_time,
+ pb_entry.has_desk_type()
+ ? GetDeskTemplateTypeFromProtoType(pb_entry.desk_type())
+ : ash::DeskTemplateType::kTemplate);
if (pb_entry.has_updated_time_windows_epoch_micros()) {
desk_template->set_updated_time(desk_template_conversion::ProtoTimeToTime(
pb_entry.updated_time_windows_epoch_micros()));
}
- if (pb_entry.has_desk_type()) {
- desk_template->set_type(
- GetDeskTemplateTypeFromProtoType(pb_entry.desk_type()));
- } else {
- desk_template->set_type(DeskTemplateType::kTemplate);
- }
-
desk_template->set_desk_restore_data(ConvertToRestoreData(pb_entry));
return desk_template;
}
@@ -781,8 +952,8 @@ absl::optional<syncer::ModelError> DeskSyncBridge::ApplySyncChanges(
switch (change->type()) {
case syncer::EntityChange::ACTION_DELETE: {
- if (entries_.find(uuid) != entries_.end()) {
- entries_.erase(uuid);
+ if (desk_template_entries_.find(uuid) != desk_template_entries_.end()) {
+ desk_template_entries_.erase(uuid);
batch->DeleteData(uuid.AsLowercaseString());
removed.push_back(uuid.AsLowercaseString());
}
@@ -804,7 +975,7 @@ absl::optional<syncer::ModelError> DeskSyncBridge::ApplySyncChanges(
std::string serialized_remote_entry = specifics.SerializeAsString();
// Add/update the remote_entry to the model.
- entries_[uuid] = std::move(remote_entry);
+ desk_template_entries_[uuid] = std::move(remote_entry);
added_or_updated.push_back(GetUserEntryByUUID(uuid));
// Write to the store.
@@ -841,7 +1012,7 @@ void DeskSyncBridge::GetData(StorageKeyList storage_keys,
void DeskSyncBridge::GetAllDataForDebugging(DataCallback callback) {
auto batch = std::make_unique<syncer::MutableDataBatch>();
- for (const auto& it : entries_) {
+ for (const auto& it : desk_template_entries_) {
batch->Put(it.first.AsLowercaseString(),
CopyToEntityData(ToSyncProto(it.second.get())));
}
@@ -859,22 +1030,28 @@ std::string DeskSyncBridge::GetStorageKey(
}
void DeskSyncBridge::GetAllEntries(GetAllEntriesCallback callback) {
- std::vector<DeskTemplate*> entries;
+ std::vector<const DeskTemplate*> entries;
+
+ GetAllEntriesStatus status = GetAllEntries(entries);
+
+ std::move(callback).Run(status, std::move(entries));
+}
+DeskModel::GetAllEntriesStatus DeskSyncBridge::GetAllEntries(
+ std::vector<const DeskTemplate*>& entries) {
if (!IsReady()) {
- std::move(callback).Run(GetAllEntriesStatus::kFailure, std::move(entries));
- return;
+ return GetAllEntriesStatus::kFailure;
}
for (const auto& it : policy_entries_)
entries.push_back(it.get());
- for (const auto& it : entries_) {
+ for (const auto& it : desk_template_entries_) {
DCHECK_EQ(it.first, it.second->uuid());
entries.push_back(it.second.get());
}
- std::move(callback).Run(GetAllEntriesStatus::kOk, std::move(entries));
+ return GetAllEntriesStatus::kOk;
}
void DeskSyncBridge::GetEntryByUUID(const std::string& uuid_str,
@@ -890,8 +1067,8 @@ void DeskSyncBridge::GetEntryByUUID(const std::string& uuid_str,
return;
}
- auto it = entries_.find(uuid);
- if (it == entries_.end()) {
+ auto it = desk_template_entries_.find(uuid);
+ if (it == desk_template_entries_.end()) {
std::unique_ptr<DeskTemplate> policy_entry =
GetAdminDeskTemplateByUUID(uuid_str);
@@ -922,7 +1099,7 @@ void DeskSyncBridge::AddOrUpdateEntry(std::unique_ptr<DeskTemplate> new_entry,
return;
}
- // When a user creates a desk template locally, the desk template has |kUser|
+ // When a user creates a desk template locally, the desk template has `kUser`
// as its source. Only user desk templates should be saved to Sync.
DCHECK_EQ(DeskTemplateSource::kUser, new_entry->source());
@@ -930,15 +1107,6 @@ void DeskSyncBridge::AddOrUpdateEntry(std::unique_ptr<DeskTemplate> new_entry,
entry->set_template_name(
base::CollapseWhitespace(new_entry->template_name(), true));
- // While we still find duplicate names iterate the duplicate number. i.e.
- // if there are 4 duplicates of some template name then this iterates until
- // the current template will be named 5.
- while (HasUserTemplateWithName(entry->template_name())) {
- entry->set_template_name(
- desk_template_util::AppendDuplicateNumberToDuplicateName(
- entry->template_name()));
- }
-
std::unique_ptr<ModelTypeStore::WriteBatch> batch =
store_->CreateWriteBatch();
@@ -955,7 +1123,7 @@ void DeskSyncBridge::AddOrUpdateEntry(std::unique_ptr<DeskTemplate> new_entry,
batch->GetMetadataChangeList());
std::unique_ptr<DeskTemplate> persisted_entry = FromSyncProto(sync_proto);
- entries_[uuid] = std::move(persisted_entry);
+ desk_template_entries_[uuid] = std::move(persisted_entry);
const DeskTemplate* result = GetUserEntryByUUID(uuid);
batch->WriteData(uuid.AsLowercaseString(),
@@ -989,7 +1157,7 @@ void DeskSyncBridge::DeleteEntry(const std::string& uuid_str,
change_processor()->Delete(uuid.AsLowercaseString(),
batch->GetMetadataChangeList());
- entries_.erase(uuid);
+ desk_template_entries_.erase(uuid);
batch->DeleteData(uuid.AsLowercaseString());
@@ -999,11 +1167,15 @@ void DeskSyncBridge::DeleteEntry(const std::string& uuid_str,
}
void DeskSyncBridge::DeleteAllEntries(DeleteEntryCallback callback) {
+ DeleteEntryStatus status = DeleteAllEntries();
+ std::move(callback).Run(status);
+}
+
+DeskModel::DeleteEntryStatus DeskSyncBridge::DeleteAllEntries() {
if (!IsReady()) {
// This sync bridge has not finished initializing.
// Cannot delete anything.
- std::move(callback).Run(DeleteEntryStatus::kFailure);
- return;
+ return DeleteEntryStatus::kFailure;
}
std::unique_ptr<ModelTypeStore::WriteBatch> batch =
@@ -1016,16 +1188,34 @@ void DeskSyncBridge::DeleteAllEntries(DeleteEntryCallback callback) {
batch->GetMetadataChangeList());
batch->DeleteData(uuid.AsLowercaseString());
}
- entries_.clear();
+ desk_template_entries_.clear();
+ return DeleteEntryStatus::kOk;
+}
- std::move(callback).Run(DeleteEntryStatus::kOk);
+size_t DeskSyncBridge::GetEntryCount() const {
+ return GetSaveAndRecallDeskEntryCount() + GetDeskTemplateEntryCount();
}
-std::size_t DeskSyncBridge::GetEntryCount() const {
- return entries_.size() + policy_entries_.size();
+size_t DeskSyncBridge::GetMaxEntryCount() const {
+ return GetMaxSaveAndRecallDeskEntryCount() + GetMaxDeskTemplateEntryCount();
}
-std::size_t DeskSyncBridge::GetMaxEntryCount() const {
+// Return 0 for now since chrome sync does not support save and recall desks.
+size_t DeskSyncBridge::GetSaveAndRecallDeskEntryCount() const {
+ return 0u;
+}
+
+size_t DeskSyncBridge::GetDeskTemplateEntryCount() const {
+ return desk_template_entries_.size() + policy_entries_.size();
+}
+
+// Chrome sync does not support save and recall desks yet. Return 0 for max
+// count.
+size_t DeskSyncBridge::GetMaxSaveAndRecallDeskEntryCount() const {
+ return 0u;
+}
+
+size_t DeskSyncBridge::GetMaxDeskTemplateEntryCount() const {
return kMaxTemplateCount + policy_entries_.size();
}
@@ -1035,7 +1225,7 @@ std::vector<base::GUID> DeskSyncBridge::GetAllEntryUuids() const {
for (const auto& it : policy_entries_)
keys.push_back(it.get()->uuid());
- for (const auto& it : entries_) {
+ for (const auto& it : desk_template_entries_) {
DCHECK_EQ(it.first, it.second->uuid());
keys.emplace_back(it.first);
}
@@ -1053,6 +1243,16 @@ bool DeskSyncBridge::IsSyncing() const {
return change_processor()->IsTrackingMetadata();
}
+// TODO(zhumatthew): Once desk sync bridge supports save and recall desk type,
+// update this method to search the correct cache for the entry.
+ash::DeskTemplate* DeskSyncBridge::FindOtherEntryWithName(
+ const std::u16string& name,
+ ash::DeskTemplateType type,
+ const base::GUID& uuid) const {
+ return desk_template_util::FindOtherEntryWithName(name, uuid,
+ desk_template_entries_);
+}
+
sync_pb::WorkspaceDeskSpecifics DeskSyncBridge::ToSyncProto(
const DeskTemplate* desk_template) {
apps::AppRegistryCache* cache =
@@ -1060,8 +1260,7 @@ sync_pb::WorkspaceDeskSpecifics DeskSyncBridge::ToSyncProto(
DCHECK(cache);
sync_pb::WorkspaceDeskSpecifics pb_entry;
-
- FillDeskType(&pb_entry, desk_template);
+ FillDeskType(desk_template, &pb_entry);
pb_entry.set_uuid(desk_template->uuid().AsLowercaseString());
pb_entry.set_name(base::UTF16ToUTF8(desk_template->template_name()));
@@ -1074,16 +1273,16 @@ sync_pb::WorkspaceDeskSpecifics DeskSyncBridge::ToSyncProto(
}
if (desk_template->desk_restore_data()) {
- FillWorkspaceDeskSpecifics(&pb_entry, cache,
- desk_template->desk_restore_data());
+ FillWorkspaceDeskSpecifics(cache, desk_template->desk_restore_data(),
+ &pb_entry);
}
return pb_entry;
}
const DeskTemplate* DeskSyncBridge::GetUserEntryByUUID(
const base::GUID& uuid) const {
- auto it = entries_.find(uuid);
- if (it == entries_.end())
+ auto it = desk_template_entries_.find(uuid);
+ if (it == desk_template_entries_.end())
return nullptr;
return it->second.get();
}
@@ -1146,7 +1345,7 @@ void DeskSyncBridge::OnReadAllData(
return;
}
- entries_ = std::move(*stored_desk_templates);
+ desk_template_entries_ = std::move(*stored_desk_templates);
store_->ReadAllMetadata(base::BindOnce(&DeskSyncBridge::OnReadAllMetadata,
weak_ptr_factory_.GetWeakPtr()));
@@ -1181,12 +1380,12 @@ void DeskSyncBridge::UploadLocalOnlyData(
syncer::MetadataChangeList* metadata_change_list,
const syncer::EntityChangeList& entity_data) {
std::set<base::GUID> local_keys_to_upload;
- for (const auto& it : entries_) {
+ for (const auto& it : desk_template_entries_) {
DCHECK_EQ(DeskTemplateSource::kUser, it.second->source());
local_keys_to_upload.insert(it.first);
}
- // Strip |local_keys_to_upload| of any key (UUID) that is already known to the
+ // Strip `local_keys_to_upload` of any key (UUID) that is already known to the
// server.
for (const std::unique_ptr<syncer::EntityChange>& change : entity_data) {
local_keys_to_upload.erase(
@@ -1195,19 +1394,33 @@ void DeskSyncBridge::UploadLocalOnlyData(
// Upload the local-only templates.
for (const base::GUID& uuid : local_keys_to_upload) {
- change_processor()->Put(uuid.AsLowercaseString(),
- CopyToEntityData(ToSyncProto(entries_[uuid].get())),
- metadata_change_list);
+ change_processor()->Put(
+ uuid.AsLowercaseString(),
+ CopyToEntityData(ToSyncProto(desk_template_entries_[uuid].get())),
+ metadata_change_list);
}
}
bool DeskSyncBridge::HasUserTemplateWithName(const std::u16string& name) {
return std::find_if(
- entries_.begin(), entries_.end(),
+ desk_template_entries_.begin(), desk_template_entries_.end(),
[&name](std::pair<const base::GUID,
std::unique_ptr<ash::DeskTemplate>>& entry) {
return entry.second->template_name() == name;
- }) != entries_.end();
+ }) != desk_template_entries_.end();
+}
+
+bool DeskSyncBridge::HasUuid(const std::string& uuid_str) const {
+ const base::GUID uuid = base::GUID::ParseCaseInsensitive(uuid_str);
+ if (!uuid.is_valid())
+ return false;
+ return std::find_if(
+ desk_template_entries_.begin(), desk_template_entries_.end(),
+ [&uuid](
+ const std::pair<const base::GUID,
+ std::unique_ptr<ash::DeskTemplate>>& entry) {
+ return entry.first == uuid;
+ }) != desk_template_entries_.end();
}
} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/desk_sync_bridge.h b/chromium/components/desks_storage/core/desk_sync_bridge.h
index 00ae0b39c80..fba8b306d68 100644
--- a/chromium/components/desks_storage/core/desk_sync_bridge.h
+++ b/chromium/components/desks_storage/core/desk_sync_bridge.h
@@ -29,6 +29,7 @@ class WorkspaceDeskSpecifics;
namespace ash {
class DeskTemplate;
+enum class DeskTemplateType;
} // namespace ash
namespace desks_storage {
@@ -73,6 +74,10 @@ class DeskSyncBridge : public syncer::ModelTypeSyncBridge, public DeskModel {
void DeleteAllEntries(DeleteEntryCallback callback) override;
std::size_t GetEntryCount() const override;
std::size_t GetMaxEntryCount() const override;
+ std::size_t GetSaveAndRecallDeskEntryCount() const override;
+ std::size_t GetDeskTemplateEntryCount() const override;
+ std::size_t GetMaxSaveAndRecallDeskEntryCount() const override;
+ std::size_t GetMaxDeskTemplateEntryCount() const override;
std::vector<base::GUID> GetAllEntryUuids() const override;
bool IsReady() const override;
// Whether this sync bridge is syncing local data to sync. This sync bridge
@@ -80,26 +85,38 @@ class DeskSyncBridge : public syncer::ModelTypeSyncBridge, public DeskModel {
// for Workspace Desk model type.
bool IsSyncing() const override;
+ ash::DeskTemplate* FindOtherEntryWithName(
+ const std::u16string& name,
+ ash::DeskTemplateType type,
+ const base::GUID& uuid) const override;
+
// Other helper methods.
// Converts an ash::DeskTemplate to its corresponding WorkspaceDesk proto.
sync_pb::WorkspaceDeskSpecifics ToSyncProto(
const ash::DeskTemplate* desk_template);
+ bool HasUuid(const std::string& uuid_str) const;
+
const ash::DeskTemplate* GetUserEntryByUUID(const base::GUID& uuid) const;
+ DeskModel::GetAllEntriesStatus GetAllEntries(
+ std::vector<const ash::DeskTemplate*>& entries);
+
+ DeskModel::DeleteEntryStatus DeleteAllEntries();
+
private:
using DeskEntries = std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>;
// Notify all observers that the model is loaded;
void NotifyDeskModelLoaded();
- // Notify all observers of any |new_entries| when they are added/updated via
+ // Notify all observers of any `new_entries` when they are added/updated via
// sync.
void NotifyRemoteDeskTemplateAddedOrUpdated(
const std::vector<const ash::DeskTemplate*>& new_entries);
- // Notify all observers when the entries with |uuids| have been removed via
+ // Notify all observers when the entries with `uuids` have been removed via
// sync or disabling sync locally.
void NotifyRemoteDeskTemplateDeleted(const std::vector<std::string>& uuids);
@@ -122,8 +139,8 @@ class DeskSyncBridge : public syncer::ModelTypeSyncBridge, public DeskModel {
// Returns true if `templates_` contains a desk template with `name`.
bool HasUserTemplateWithName(const std::u16string& name);
- // |entries_| is keyed by UUIDs.
- DeskEntries entries_;
+ // `desk_template_entries_` is keyed by UUIDs.
+ DeskEntries desk_template_entries_;
// Whether local data and metadata have finished loading and this sync bridge
// is ready to be accessed.
@@ -140,4 +157,4 @@ class DeskSyncBridge : public syncer::ModelTypeSyncBridge, public DeskModel {
} // namespace desks_storage
-#endif // COMPONENTS_DESKS_STORAGE_CORE_DESK_SYNC_BRIDGE_H_ \ No newline at end of file
+#endif // COMPONENTS_DESKS_STORAGE_CORE_DESK_SYNC_BRIDGE_H_
diff --git a/chromium/components/desks_storage/core/desk_sync_bridge_unittest.cc b/chromium/components/desks_storage/core/desk_sync_bridge_unittest.cc
index d1eef1789e1..6bab2805e66 100644
--- a/chromium/components/desks_storage/core/desk_sync_bridge_unittest.cc
+++ b/chromium/components/desks_storage/core/desk_sync_bridge_unittest.cc
@@ -24,9 +24,9 @@
#include "components/app_restore/app_launch_info.h"
#include "components/desks_storage/core/desk_model_observer.h"
#include "components/desks_storage/core/desk_template_conversion.h"
+#include "components/desks_storage/core/desk_test_util.h"
+#include "components/desks_storage/core/saved_desk_builder.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
-#include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
-#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/features.h"
#include "components/sync/model/entity_change.h"
#include "components/sync/model/in_memory_metadata_change_list.h"
@@ -54,11 +54,14 @@ using DeskType = ash::DeskTemplateType;
using WindowBound = sync_pb::WorkspaceDeskSpecifics_WindowBound;
using WindowState = sync_pb::WorkspaceDeskSpecifics_WindowState;
using WorkspaceDeskSpecifics_App = sync_pb::WorkspaceDeskSpecifics_App;
+using SyncTabGroup = sync_pb::WorkspaceDeskSpecifics_BrowserAppWindow_TabGroup;
+using SyncTabGroupColor = sync_pb::WorkspaceDeskSpecifics_TabGroupColor;
namespace {
using ash::DeskTemplate;
using ash::DeskTemplateSource;
+using ash::DeskTemplateType;
using sync_pb::ModelTypeState;
using sync_pb::WorkspaceDeskSpecifics;
using syncer::EntityChange;
@@ -77,9 +80,6 @@ using testing::Return;
using testing::SizeIs;
using testing::StrEq;
-constexpr char kTestPwaAppId[] = "test_pwa_app_id";
-constexpr char kTestChromeAppId[] = "test_chrome_app_id";
-constexpr char kTestArcAppId[] = "test_arc_app_id";
constexpr char kTestArcAppTitle[] = "test_arc_app_title";
constexpr char kUuidFormat[] = "9e186d5a-502e-49ce-9ee1-00000000000%d";
constexpr char kAdminTemplateUuidFormat[] =
@@ -89,12 +89,18 @@ constexpr char kTestUrlFormat[] = "https://www.testdomain%d.com/";
constexpr char kTestAppNameFormat[] = "_some_prefix_%s";
constexpr int kDefaultTemplateIndex = 1;
constexpr int kBrowserWindowId = 1555;
+constexpr int kPwaWindowId = 1666;
+constexpr int kChromeAppWindowId = 1777;
+constexpr int kUnsupportedAppWindowId = 1888;
+constexpr char kTestTabGroupName[] = "test_tab_group";
+constexpr char kTestTabGroupNameFormat[] = "test_tab_group_%zu";
// Example app index as set in `ExampleWorkspaceDeskSpecifics`.
constexpr int kExampleDeskBrowserAppIndex = 0;
constexpr int kExampleDeskArcAppIndex = 1;
constexpr int kExampleDeskChromeAppIndex = 2;
constexpr int kExampleDeskProgressiveWebAppIndex = 3;
+constexpr int kExampleDeskSystemWebAppIndex = 4;
const base::GUID kTestUuid1 =
base::GUID::ParseCaseInsensitive(base::StringPrintf(kUuidFormat, 1));
@@ -117,7 +123,10 @@ const std::string kPolicyWithTwoTemplates =
"state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
"\"url\":\"https://example.com\",\"title\":\"Example\"},{\"url\":\"https://"
"example.com/"
- "2\",\"title\":\"Example2\"}],\"active_tab_index\":1,\"window_id\":0,"
+ "2\",\"title\":\"Example2\"}],\"tab_groups\":[{\"range_"
+ "start\":1,\"range_end\":2,\"title\":\"sample_tab_"
+ "group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_index\":"
+ "1,\"window_id\":0,"
"\"display_id\":\"100\",\"pre_minimized_window_state\":\"NORMAL\"}]}},"
"{\"version\":1,\"uuid\":\"" +
base::StringPrintf(kUuidFormat, 9) +
@@ -133,11 +142,9 @@ const std::string kPolicyWithTwoTemplates =
"2\",\"title\":\"Example2\"}],\"active_tab_index\":1,\"window_id\":0,"
"\"display_id\":\"100\",\"pre_minimized_window_state\":\"NORMAL\"}]}}]";
-void FillExampleBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
- int number_of_tabs = 2) {
- BrowserAppWindow* app_window =
- app->mutable_app()->mutable_browser_app_window();
-
+void FillDefaultBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
+ BrowserAppWindow* app_window,
+ int number_of_tabs) {
for (int i = 0; i < number_of_tabs; ++i) {
BrowserAppTab* tab = app_window->add_tabs();
tab->set_url(base::StringPrintf(kTestUrlFormat, i));
@@ -158,10 +165,44 @@ void FillExampleBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
app->set_snap_percentage(75);
}
+void FillExampleBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
+ int number_of_tabs = 2) {
+ BrowserAppWindow* app_window =
+ app->mutable_app()->mutable_browser_app_window();
+
+ FillDefaultBrowserAppWindow(app, app_window, number_of_tabs);
+
+ SyncTabGroup* tab_group = app_window->add_tab_groups();
+ tab_group->set_first_index(1);
+ tab_group->set_last_index(2);
+ tab_group->set_title(kTestTabGroupName);
+ tab_group->set_color(
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN);
+}
+
+void FillExampleBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
+ int tab_group_first_index,
+ int tab_group_last_index,
+ const std::string& tab_group_title,
+ bool tab_group_is_collapsed,
+ SyncTabGroupColor tab_group_color) {
+ BrowserAppWindow* app_window =
+ app->mutable_app()->mutable_browser_app_window();
+
+ FillDefaultBrowserAppWindow(app, app_window, tab_group_last_index);
+
+ SyncTabGroup* tab_group = app_window->add_tab_groups();
+ tab_group->set_first_index(tab_group_first_index);
+ tab_group->set_last_index(tab_group_last_index);
+ tab_group->set_title(tab_group_title);
+ if (tab_group_is_collapsed)
+ tab_group->set_is_collapsed(tab_group_is_collapsed);
+ tab_group->set_color(tab_group_color);
+}
+
void FillExampleProgressiveWebAppWindow(WorkspaceDeskSpecifics_App* app) {
- ProgressiveWebApp* app_window =
- app->mutable_app()->mutable_progress_web_app();
- app_window->set_app_id(kTestPwaAppId);
+ ChromeApp* app_window = app->mutable_app()->mutable_chrome_app();
+ app_window->set_app_id(desk_test_util::kTestPwaAppId);
WindowBound* window_bound = app->mutable_window_bound();
window_bound->set_left(210);
@@ -176,15 +217,40 @@ void FillExampleProgressiveWebAppWindow(WorkspaceDeskSpecifics_App* app) {
sync_pb::WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_WINDOW);
app->set_disposition(
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_WINDOW);
- app->set_app_name(base::StringPrintf(kTestAppNameFormat, kTestPwaAppId));
+ app->set_app_name(
+ base::StringPrintf(kTestAppNameFormat, desk_test_util::kTestPwaAppId));
app->set_display_id(99887766l);
app->set_z_index(233);
app->set_window_id(2555);
}
+void FillExampleSystemWebAppWindow(WorkspaceDeskSpecifics_App* app) {
+ ChromeApp* app_window = app->mutable_app()->mutable_chrome_app();
+ app_window->set_app_id(desk_test_util::kTestSwaAppId);
+
+ WindowBound* window_bound = app->mutable_window_bound();
+ window_bound->set_left(220);
+ window_bound->set_top(230);
+ window_bound->set_width(2340);
+ window_bound->set_height(2450);
+ app->set_window_state(
+ WindowState::WorkspaceDeskSpecifics_WindowState_MINIMIZED);
+ app->set_pre_minimized_window_state(
+ WindowState::WorkspaceDeskSpecifics_WindowState_FULLSCREEN);
+ app->set_container(
+ sync_pb::WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_WINDOW);
+ app->set_disposition(
+ sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_WINDOW);
+ app->set_app_name(
+ base::StringPrintf(kTestAppNameFormat, desk_test_util::kTestSwaAppId));
+ app->set_display_id(99887766l);
+ app->set_z_index(234);
+ app->set_window_id(2556);
+}
+
void FillExampleChromeAppWindow(WorkspaceDeskSpecifics_App* app) {
ChromeApp* app_window = app->mutable_app()->mutable_chrome_app();
- app_window->set_app_id(kTestChromeAppId);
+ app_window->set_app_id(desk_test_util::kTestChromeAppId);
WindowBound* window_bound = app->mutable_window_bound();
window_bound->set_left(210);
@@ -198,7 +264,8 @@ void FillExampleChromeAppWindow(WorkspaceDeskSpecifics_App* app) {
WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_PANEL_DEPRECATED);
app->set_disposition(
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_WINDOW);
- app->set_app_name(base::StringPrintf(kTestAppNameFormat, kTestChromeAppId));
+ app->set_app_name(
+ base::StringPrintf(kTestAppNameFormat, desk_test_util::kTestChromeAppId));
app->set_display_id(99887766l);
app->set_z_index(233);
app->set_window_id(2555);
@@ -206,7 +273,7 @@ void FillExampleChromeAppWindow(WorkspaceDeskSpecifics_App* app) {
void FillExampleArcAppWindow(WorkspaceDeskSpecifics_App* app) {
ArcApp* app_window = app->mutable_app()->mutable_arc_app();
- app_window->set_app_id(kTestArcAppId);
+ app_window->set_app_id(desk_test_util::kTestArcAppId);
ArcSize* minimum_size = app_window->mutable_minimum_size();
minimum_size->set_width(1);
@@ -229,7 +296,8 @@ void FillExampleArcAppWindow(WorkspaceDeskSpecifics_App* app) {
window_bound->set_height(2440);
app->set_window_state(
WindowState::WorkspaceDeskSpecifics_WindowState_MAXIMIZED);
- app->set_app_name(base::StringPrintf(kTestAppNameFormat, kTestArcAppId));
+ app->set_app_name(
+ base::StringPrintf(kTestAppNameFormat, desk_test_util::kTestArcAppId));
app->set_display_id(99887766l);
app->set_z_index(233);
app->set_window_id(2555);
@@ -258,6 +326,39 @@ WorkspaceDeskSpecifics ExampleWorkspaceDeskSpecifics(
FillExampleArcAppWindow(desk->add_apps());
FillExampleChromeAppWindow(desk->add_apps());
FillExampleProgressiveWebAppWindow(desk->add_apps());
+ FillExampleSystemWebAppWindow(desk->add_apps());
+ return specifics;
+}
+
+WorkspaceDeskSpecifics ExampleWorkspaceDeskSpecifics(
+ const std::string& uuid,
+ const std::string& template_name,
+ int tab_group_first_index,
+ int tab_group_last_index,
+ const std::string& tab_group_title,
+ bool tab_group_is_collapsed,
+ SyncTabGroupColor tab_group_color) {
+ base::Time created_time = base::Time::Now();
+
+ WorkspaceDeskSpecifics specifics;
+ specifics.set_uuid(uuid);
+ specifics.set_name(template_name);
+ specifics.set_created_time_windows_epoch_micros(
+ created_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ specifics.set_updated_time_windows_epoch_micros(
+ (created_time + base::Minutes(5))
+ .ToDeltaSinceWindowsEpoch()
+ .InMicroseconds());
+ specifics.set_desk_type(
+ SyncDeskType::WorkspaceDeskSpecifics_DeskType_SAVE_AND_RECALL);
+ Desk* desk = specifics.mutable_desk();
+ FillExampleBrowserAppWindow(desk->add_apps(), tab_group_first_index,
+ tab_group_last_index, tab_group_title,
+ tab_group_is_collapsed, tab_group_color);
+ FillExampleArcAppWindow(desk->add_apps());
+ FillExampleChromeAppWindow(desk->add_apps());
+ FillExampleProgressiveWebAppWindow(desk->add_apps());
+ FillExampleSystemWebAppWindow(desk->add_apps());
return specifics;
}
@@ -277,28 +378,6 @@ WorkspaceDeskSpecifics CreateUnkownDeskType() {
SyncDeskType::WorkspaceDeskSpecifics_DeskType_UNKNOWN_TYPE);
}
-std::unique_ptr<ash::DeskTemplate> CreateTemplateWithBrowserFromScratch(
- int template_index,
- const base::Time& created_time) {
- const std::string template_uuid =
- base::StringPrintf(kUuidFormat, template_index);
- const std::string template_name =
- base::StringPrintf(kNameFormat, template_index);
- auto desk_template = std::make_unique<ash::DeskTemplate>(
- template_uuid, DeskTemplateSource::kUser, template_name, created_time);
-
- auto restore_data = std::make_unique<app_restore::RestoreData>();
- auto browser_info = std::make_unique<app_restore::AppLaunchInfo>(
- app_constants::kChromeAppId, kBrowserWindowId);
- browser_info->urls = {GURL(base::StringPrintf(kTestUrlFormat, 1)),
- GURL(base::StringPrintf(kTestUrlFormat, 2))};
-
- restore_data->AddAppLaunchInfo(std::move(browser_info));
- desk_template->set_desk_restore_data(std::move(restore_data));
-
- return desk_template;
-}
-
WorkspaceDeskSpecifics CreateBrowserTemplateExpectedValue(
int template_index,
const base::Time& created_time) {
@@ -325,21 +404,62 @@ WorkspaceDeskSpecifics CreateBrowserTemplateExpectedValue(
return expected_desk_specifics;
}
+WorkspaceDeskSpecifics CreatePwaTemplateExpectedValue(
+ int template_index,
+ const base::Time& created_time) {
+ WorkspaceDeskSpecifics expected_desk_specifics;
+ expected_desk_specifics.set_uuid(
+ base::StringPrintf(kUuidFormat, template_index));
+ expected_desk_specifics.set_name(
+ base::StringPrintf(kNameFormat, template_index));
+ expected_desk_specifics.set_created_time_windows_epoch_micros(
+ created_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ expected_desk_specifics.set_desk_type(
+ SyncDeskType::WorkspaceDeskSpecifics_DeskType_TEMPLATE);
+ Desk* expected_desk = expected_desk_specifics.mutable_desk();
+ WorkspaceDeskSpecifics_App* app = expected_desk->add_apps();
+ app->set_window_id(kPwaWindowId);
+ BrowserAppWindow* browser_window =
+ app->mutable_app()->mutable_browser_app_window();
+
+ BrowserAppTab* first_tab = browser_window->add_tabs();
+ first_tab->set_url(GURL(base::StringPrintf(kTestUrlFormat, 1)).spec());
+
+ browser_window->set_show_as_app(true);
+
+ return expected_desk_specifics;
+}
+
+WorkspaceDeskSpecifics CreateChromeAppTemplateExpectedValue(
+ int template_index,
+ const base::Time& created_time,
+ int window_id,
+ const std::string& app_id) {
+ WorkspaceDeskSpecifics expected_desk_specifics;
+ expected_desk_specifics.set_uuid(
+ base::StringPrintf(kUuidFormat, template_index));
+ expected_desk_specifics.set_name(
+ base::StringPrintf(kNameFormat, template_index));
+ expected_desk_specifics.set_created_time_windows_epoch_micros(
+ created_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ expected_desk_specifics.set_desk_type(
+ SyncDeskType::WorkspaceDeskSpecifics_DeskType_TEMPLATE);
+ Desk* expected_desk = expected_desk_specifics.mutable_desk();
+ WorkspaceDeskSpecifics_App* app = expected_desk->add_apps();
+ app->set_window_id(window_id);
+
+ ChromeApp* app_window = app->mutable_app()->mutable_chrome_app();
+ app_window->set_app_id(app_id);
+
+ return expected_desk_specifics;
+}
+
ModelTypeState StateWithEncryption(const std::string& encryption_key_name) {
ModelTypeState state;
state.set_encryption_key_name(encryption_key_name);
return state;
}
-apps::AppPtr MakeApp(const char* app_id,
- const char* name,
- apps::AppType app_type) {
- apps::AppPtr app = std::make_unique<apps::App>(app_type, app_id);
- app->readiness = apps::Readiness::kReady;
- app->name = name;
- return app;
-}
-
class MockDeskModelObserver : public DeskModelObserver {
public:
MOCK_METHOD0(DeskModelLoaded, void());
@@ -406,39 +526,6 @@ class DeskSyncBridgeTest : public testing::Test {
InitializeBridge();
}
- void PopulateAppRegistryCache() {
- std::vector<apps::AppPtr> deltas;
-
- deltas.push_back(
- MakeApp(kTestPwaAppId, "Test PWA App", apps::AppType::kWeb));
- // chromeAppId returns kExtension in the real Apps cache.
- deltas.push_back(MakeApp(app_constants::kChromeAppId, "Chrome Browser",
- apps::AppType::kChromeApp));
- deltas.push_back(MakeApp(kTestChromeAppId, "Test Chrome App",
- apps::AppType::kChromeApp));
- deltas.push_back(MakeApp(kTestArcAppId, "Arc app", apps::AppType::kArc));
-
- if (base::FeatureList::IsEnabled(
- apps::kAppServiceOnAppUpdateWithoutMojom)) {
- cache_->OnApps(std::move(deltas), apps::AppType::kUnknown,
- /*should_notify_initialized=*/false);
- } else {
- std::vector<apps::mojom::AppPtr> mojom_deltas;
- for (const auto& delta : deltas) {
- mojom_deltas.push_back(apps::ConvertAppToMojomApp(delta));
- }
- cache_->OnApps(std::move(mojom_deltas), apps::mojom::AppType::kUnknown,
- /*should_notify_initialized=*/false);
- }
-
- cache_->SetAccountId(account_id_);
-
- apps::AppRegistryCacheWrapper::Get().AddAppRegistryCache(account_id_,
- cache_.get());
- }
-
- void SetUp() override { PopulateAppRegistryCache(); }
-
void WriteToStoreWithMetadata(
const std::vector<WorkspaceDeskSpecifics>& specifics_list,
ModelTypeState state) {
@@ -574,6 +661,11 @@ class DeskSyncBridgeTest : public testing::Test {
bridge()->SetPolicyDeskTemplates(policy_json);
}
+ // testing::test.
+ void SetUp() override {
+ desk_test_util::PopulateAppRegistryCache(account_id_, cache_.get());
+ }
+
MockModelTypeChangeProcessor* processor() { return &mock_processor_; }
DeskSyncBridge* bridge() { return bridge_.get(); }
@@ -614,6 +706,9 @@ TEST_F(DeskSyncBridgeTest, DeskTemplateConversionShouldBeLossless) {
WorkspaceDeskSpecifics converted_desk_proto =
bridge()->ToSyncProto(desk_template.get());
+ std::unique_ptr<DeskTemplate> converted_desk_template =
+ DeskSyncBridge::FromSyncProto(converted_desk_proto);
+
EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
}
@@ -631,7 +726,8 @@ TEST_F(DeskSyncBridgeTest, DeskTemplateJsonConversionShouldBeLossless) {
desk_template.get(), app_cache());
std::unique_ptr<ash::DeskTemplate> converted_desk_template =
- desk_template_conversion::ParseDeskTemplateFromPolicy(template_value);
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ template_value, ash::DeskTemplateSource::kPolicy);
EXPECT_EQ(desk_template->desk_restore_data()->ConvertToValue(),
converted_desk_template->desk_restore_data()->ConvertToValue());
@@ -660,6 +756,9 @@ TEST_F(DeskSyncBridgeTest, AppNameConversionShouldBeLossless) {
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskProgressiveWebAppIndex)
->set_app_name("app name 4");
+ desk_proto.mutable_desk()
+ ->mutable_apps(kExampleDeskSystemWebAppIndex)
+ ->set_app_name("app name 5");
std::unique_ptr<DeskTemplate> desk_template =
DeskSyncBridge::FromSyncProto(desk_proto);
@@ -741,21 +840,199 @@ TEST_F(DeskSyncBridgeTest, LaunchContainerConversionShouldBeLossless) {
}
}
+TEST_F(DeskSyncBridgeTest, TabGroupInfoConversionShouldBeLossless) {
+ CreateBridge();
+
+ std::vector<SyncTabGroupColor> values = {
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREY,
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_BLUE,
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_RED,
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_YELLOW,
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN,
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PINK,
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PURPLE,
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_CYAN,
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_ORANGE};
+
+ // Iterate over colors, changing the values contained within the
+ // tab group for each iteration.
+ size_t curr_start = 0;
+ for (const auto& test_color_value : values) {
+ // We test with the color GREEN by default.
+ if (test_color_value ==
+ SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN) {
+ continue;
+ }
+
+ WorkspaceDeskSpecifics desk_proto = ExampleWorkspaceDeskSpecifics(
+ kTestUuid1.AsLowercaseString(), "template 1",
+ /*Set a different range for each iteration.*/
+ curr_start, curr_start + 1,
+ /*Change name for each iteration.*/
+ base::StringPrintf(kTestTabGroupNameFormat, curr_start),
+ /*Modulate between true and false for is_collapsed value on each
+ iteration.*/
+ static_cast<bool>(curr_start % 2),
+ /*Set the color to the current iteration*/
+ test_color_value);
+
+ std::unique_ptr<DeskTemplate> desk_template =
+ DeskSyncBridge::FromSyncProto(desk_proto);
+
+ WorkspaceDeskSpecifics converted_desk_proto =
+ bridge()->ToSyncProto(desk_template.get());
+
+ EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
+
+ // Shift range up by one.
+ ++curr_start;
+ }
+}
+
// Tests that URLs are saved properly when converting a DeskTemplate
-// to its protobuf form.
-TEST_F(DeskSyncBridgeTest, EnsureBrowserWindowsSavedProperly) {
+// to its Protobuf form.
+TEST_F(DeskSyncBridgeTest, EnsureAshBrowserWindowsSavedProperly) {
CreateBridge();
- base::Time created_time = base::Time::Now();
// Uses a different method to instantiate the template that doesn't rely
// on the assumption that the template is instantiated from a proto, but
// rather is captured and saved for the first time.
std::unique_ptr<DeskTemplate> desk_template =
- CreateTemplateWithBrowserFromScratch(kDefaultTemplateIndex, created_time);
+ SavedDeskBuilder()
+ .SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
+ .SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
+ .AddAshBrowserAppWindow(kBrowserWindowId,
+ {GURL(base::StringPrintf(kTestUrlFormat, 1)),
+ GURL(base::StringPrintf(kTestUrlFormat, 2))})
+ .Build();
+
WorkspaceDeskSpecifics converted_desk_proto =
bridge()->ToSyncProto(desk_template.get());
WorkspaceDeskSpecifics expected_desk_proto =
- CreateBrowserTemplateExpectedValue(kDefaultTemplateIndex, created_time);
+ CreateBrowserTemplateExpectedValue(kDefaultTemplateIndex,
+ desk_template->created_time());
+
+ EXPECT_THAT(converted_desk_proto, EqualsSpecifics(expected_desk_proto));
+}
+
+TEST_F(DeskSyncBridgeTest, EnsureLacrosBrowserWindowsCanBeSavedProperly) {
+ CreateBridge();
+
+ // Uses a different method to instantiate the template that doesn't rely
+ // on the assumption that the template is instantiated from a proto, but
+ // rather is captured and saved for the first time.
+ std::unique_ptr<DeskTemplate> desk_template =
+ SavedDeskBuilder()
+ .SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
+ .SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
+ .AddLacrosBrowserAppWindow(
+ kBrowserWindowId, {GURL(base::StringPrintf(kTestUrlFormat, 1)),
+ GURL(base::StringPrintf(kTestUrlFormat, 2))})
+ .Build();
+
+ WorkspaceDeskSpecifics converted_desk_proto =
+ bridge()->ToSyncProto(desk_template.get());
+ WorkspaceDeskSpecifics expected_desk_proto =
+ CreateBrowserTemplateExpectedValue(kDefaultTemplateIndex,
+ desk_template->created_time());
+
+ EXPECT_THAT(converted_desk_proto, EqualsSpecifics(expected_desk_proto));
+}
+
+TEST_F(DeskSyncBridgeTest, EnsurePwaInAshChromeCanBeSavedProperly) {
+ CreateBridge();
+
+ std::unique_ptr<DeskTemplate> desk_template =
+ SavedDeskBuilder()
+ .SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
+ .SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
+ .AddAshPwaAppWindow(kPwaWindowId,
+ base::StringPrintf(kTestUrlFormat, 1))
+ .Build();
+ WorkspaceDeskSpecifics converted_desk_proto =
+ bridge()->ToSyncProto(desk_template.get());
+ WorkspaceDeskSpecifics expected_desk_proto = CreatePwaTemplateExpectedValue(
+ kDefaultTemplateIndex, desk_template->created_time());
+
+ EXPECT_THAT(converted_desk_proto, EqualsSpecifics(expected_desk_proto));
+}
+
+TEST_F(DeskSyncBridgeTest, EnsurePwaInLacrosChromeCanBeSavedProperly) {
+ CreateBridge();
+
+ std::unique_ptr<DeskTemplate> desk_template =
+ SavedDeskBuilder()
+ .SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
+ .SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
+ .AddLacrosPwaAppWindow(kPwaWindowId,
+ base::StringPrintf(kTestUrlFormat, 1))
+ .Build();
+ WorkspaceDeskSpecifics converted_desk_proto =
+ bridge()->ToSyncProto(desk_template.get());
+ WorkspaceDeskSpecifics expected_desk_proto = CreatePwaTemplateExpectedValue(
+ kDefaultTemplateIndex, desk_template->created_time());
+
+ EXPECT_THAT(converted_desk_proto, EqualsSpecifics(expected_desk_proto));
+}
+
+TEST_F(DeskSyncBridgeTest, EnsureChromeAppCanBeSavedProperly) {
+ CreateBridge();
+
+ std::unique_ptr<DeskTemplate> desk_template =
+ SavedDeskBuilder()
+ .SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
+ .SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
+ .AddChromeAppWindow(kChromeAppWindowId,
+ desk_test_util::kTestChromeAppId)
+ .Build();
+ WorkspaceDeskSpecifics converted_desk_proto =
+ bridge()->ToSyncProto(desk_template.get());
+ WorkspaceDeskSpecifics expected_desk_proto =
+ CreateChromeAppTemplateExpectedValue(
+ kDefaultTemplateIndex, desk_template->created_time(),
+ kChromeAppWindowId, desk_test_util::kTestChromeAppId);
+
+ EXPECT_THAT(converted_desk_proto, EqualsSpecifics(expected_desk_proto));
+}
+
+TEST_F(DeskSyncBridgeTest, EnsureLacrosChromeAppCanBeSavedProperly) {
+ CreateBridge();
+
+ std::unique_ptr<DeskTemplate> desk_template =
+ SavedDeskBuilder()
+ .SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
+ .SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
+ .AddChromeAppWindow(kChromeAppWindowId,
+ desk_test_util::kTestLacrosChromeAppId)
+ .Build();
+ WorkspaceDeskSpecifics converted_desk_proto =
+ bridge()->ToSyncProto(desk_template.get());
+ WorkspaceDeskSpecifics expected_desk_proto =
+ CreateChromeAppTemplateExpectedValue(
+ kDefaultTemplateIndex, desk_template->created_time(),
+ kChromeAppWindowId, desk_test_util::kTestLacrosChromeAppId);
+
+ EXPECT_THAT(converted_desk_proto, EqualsSpecifics(expected_desk_proto));
+}
+
+TEST_F(DeskSyncBridgeTest, EnsureUnsupportedAppCanBeIgnored) {
+ CreateBridge();
+
+ std::unique_ptr<DeskTemplate> desk_template =
+ SavedDeskBuilder()
+ .SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
+ .SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
+ .AddChromeAppWindow(kChromeAppWindowId,
+ desk_test_util::kTestChromeAppId)
+ .AddGenericAppWindow(kUnsupportedAppWindowId,
+ desk_test_util::kTestUnsupportedAppId)
+ .Build();
+ WorkspaceDeskSpecifics converted_desk_proto =
+ bridge()->ToSyncProto(desk_template.get());
+ WorkspaceDeskSpecifics expected_desk_proto =
+ CreateChromeAppTemplateExpectedValue(
+ kDefaultTemplateIndex, desk_template->created_time(),
+ kChromeAppWindowId, desk_test_util::kTestChromeAppId);
EXPECT_THAT(converted_desk_proto, EqualsSpecifics(expected_desk_proto));
}
@@ -833,7 +1110,7 @@ TEST_F(DeskSyncBridgeTest, GetAllEntriesIncludesPolicyEntries) {
base::RunLoop loop;
bridge()->GetAllEntries(base::BindLambdaForTesting(
[&](DeskModel::GetAllEntriesStatus status,
- const std::vector<ash::DeskTemplate*>& entries) {
+ const std::vector<const ash::DeskTemplate*>& entries) {
EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(entries.size(), 4ul);
@@ -935,9 +1212,9 @@ TEST_F(DeskSyncBridgeTest, AddEntryShouldSucceedWheSyncIsDisabled) {
base::RunLoop loop;
bridge()->AddOrUpdateEntry(
- std::make_unique<DeskTemplate>(kTestUuid1.AsLowercaseString(),
- DeskTemplateSource::kUser, "template 1",
- AdvanceAndGetTime()),
+ std::make_unique<DeskTemplate>(
+ kTestUuid1.AsLowercaseString(), DeskTemplateSource::kUser,
+ "template 1", AdvanceAndGetTime(), DeskTemplateType::kTemplate),
base::BindLambdaForTesting([&](DeskModel::AddOrUpdateEntryStatus status) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop.Quit();
@@ -957,9 +1234,9 @@ TEST_F(DeskSyncBridgeTest, AddEntryShouldFailWhenBridgeIsNotReady) {
base::RunLoop loop;
bridge()->AddOrUpdateEntry(
- std::make_unique<DeskTemplate>(kTestUuid1.AsLowercaseString(),
- DeskTemplateSource::kUser, "template 1",
- AdvanceAndGetTime()),
+ std::make_unique<DeskTemplate>(
+ kTestUuid1.AsLowercaseString(), DeskTemplateSource::kUser,
+ "template 1", AdvanceAndGetTime(), DeskTemplateType::kTemplate),
base::BindLambdaForTesting([&](DeskModel::AddOrUpdateEntryStatus status) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kFailure);
loop.Quit();
@@ -967,7 +1244,7 @@ TEST_F(DeskSyncBridgeTest, AddEntryShouldFailWhenBridgeIsNotReady) {
loop.Run();
}
-TEST_F(DeskSyncBridgeTest, AppendsDuplicateMarkingsCorrectly) {
+TEST_F(DeskSyncBridgeTest, CanDetectDuplicateName) {
InitializeBridge();
AddTwoTemplates();
@@ -978,16 +1255,23 @@ TEST_F(DeskSyncBridgeTest, AppendsDuplicateMarkingsCorrectly) {
// The two duplicated templates should be added.
EXPECT_EQ(4ul, bridge()->GetAllEntryUuids().size());
+ EXPECT_TRUE(bridge()->FindOtherEntryWithName(
+ bridge()->GetUserEntryByUUID(kTestUuid9)->template_name(),
+ bridge()->GetUserEntryByUUID(kTestUuid9)->type(),
+ bridge()->GetUserEntryByUUID(kTestUuid9)->uuid()));
+}
- // Template 8 should be renamed to avoid name collision.
- EXPECT_EQ("template 1 (1)",
- base::UTF16ToUTF8(
- bridge()->GetUserEntryByUUID(kTestUuid8)->template_name()));
+TEST_F(DeskSyncBridgeTest, CanDetectNoDuplicateName) {
+ InitializeBridge();
- // Template 9 should be renamed twice to avoid name collision.
- EXPECT_EQ("template 1 (2)",
- base::UTF16ToUTF8(
- bridge()->GetUserEntryByUUID(kTestUuid9)->template_name()));
+ AddTwoTemplates();
+
+ EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
+
+ EXPECT_FALSE(bridge()->FindOtherEntryWithName(
+ bridge()->GetUserEntryByUUID(kTestUuid1)->template_name(),
+ bridge()->GetUserEntryByUUID(kTestUuid1)->type(),
+ bridge()->GetUserEntryByUUID(kTestUuid1)->uuid()));
}
TEST_F(DeskSyncBridgeTest, GetEntryByUUIDShouldSucceed) {
@@ -1113,7 +1397,8 @@ TEST_F(DeskSyncBridgeTest, UpdateEntryLocally) {
bridge()->AddOrUpdateEntry(
std::make_unique<DeskTemplate>(kTestUuid1.AsLowercaseString(),
DeskTemplateSource::kUser,
- "updated template 1", AdvanceAndGetTime()),
+ "updated template 1", AdvanceAndGetTime(),
+ DeskTemplateType::kTemplate),
base::BindLambdaForTesting([&](DeskModel::AddOrUpdateEntryStatus status) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop.Quit();
diff --git a/chromium/components/desks_storage/core/desk_sync_service.cc b/chromium/components/desks_storage/core/desk_sync_service.cc
index 4fa203e06ec..a1df32386fc 100644
--- a/chromium/components/desks_storage/core/desk_sync_service.cc
+++ b/chromium/components/desks_storage/core/desk_sync_service.cc
@@ -37,4 +37,4 @@ DeskSyncService::GetControllerDelegate() {
return bridge_->change_processor()->GetControllerDelegate();
}
-} // namespace desks_storage \ No newline at end of file
+} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/desk_sync_service.h b/chromium/components/desks_storage/core/desk_sync_service.h
index 44dfd7df5e0..a968b18c34c 100644
--- a/chromium/components/desks_storage/core/desk_sync_service.h
+++ b/chromium/components/desks_storage/core/desk_sync_service.h
@@ -42,4 +42,4 @@ class DeskSyncService : public KeyedService {
} // namespace desks_storage
-#endif // COMPONENTS_DESKS_STORAGE_CORE_DESK_SYNC_SERVICE_H_ \ No newline at end of file
+#endif // COMPONENTS_DESKS_STORAGE_CORE_DESK_SYNC_SERVICE_H_
diff --git a/chromium/components/desks_storage/core/desk_template_conversion.cc b/chromium/components/desks_storage/core/desk_template_conversion.cc
index 814cd51546a..7802ca44b04 100644
--- a/chromium/components/desks_storage/core/desk_template_conversion.cc
+++ b/chromium/components/desks_storage/core/desk_template_conversion.cc
@@ -4,28 +4,36 @@
#include "components/desks_storage/core/desk_template_conversion.h"
-#include "ash/public/cpp/desk_template.h"
#include "base/guid.h"
#include "base/json/json_reader.h"
#include "base/json/values_util.h"
+#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/app_constants/constants.h"
#include "components/app_restore/app_launch_info.h"
#include "components/app_restore/restore_data.h"
+#include "components/app_restore/tab_group_info.h"
#include "components/app_restore/window_info.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/sync/protocol/proto_enum_conversions.h"
+#include "components/tab_groups/tab_group_color.h"
+#include "components/tab_groups/tab_group_visual_data.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/rect.h"
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/crosapi/cpp/lacros_startup_state.h" // nogncheck
+#endif // !BUILDFLAG(IS_CHROMEOS_LACROS)
+
namespace {
using SyncWindowOpenDisposition =
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition;
using SyncLaunchContainer = sync_pb::WorkspaceDeskSpecifics_LaunchContainer;
+using GroupColor = tab_groups::TabGroupColorId;
// JSON value keys.
constexpr char kActiveTabIndex[] = "active_tab_index";
@@ -33,10 +41,11 @@ constexpr char kAppId[] = "app_id";
constexpr char kApps[] = "apps";
constexpr char kAppName[] = "app_name";
constexpr char kAppType[] = "app_type";
+constexpr char kAppTypeArc[] = "ARC";
constexpr char kAppTypeBrowser[] = "BROWSER";
constexpr char kAppTypeChrome[] = "CHROME_APP";
constexpr char kAppTypeProgressiveWeb[] = "PWA";
-constexpr char kAppTypeArc[] = "ARC";
+constexpr char kAppTypeUnsupported[] = "UNSUPPORTED";
constexpr char kBoundsInRoot[] = "bounds_in_root";
constexpr char kCreatedTime[] = "created_time_usec";
constexpr char kDesk[] = "desk";
@@ -45,6 +54,7 @@ constexpr char kDeskTypeTemplate[] = "TEMPLATE";
constexpr char kDeskTypeSaveAndRecall[] = "SAVE_AND_RECALL";
constexpr char kDisplayId[] = "display_id";
constexpr char kEventFlag[] = "event_flag";
+constexpr char kIsAppTypeBrowser[] = "is_app";
constexpr char kLaunchContainer[] = "launch_container";
constexpr char kLaunchContainerWindow[] = "LAUNCH_CONTAINER_WINDOW";
constexpr char kLaunchContainerUnspecified[] = "LAUNCH_CONTAINER_UNSPECIFIED";
@@ -55,15 +65,21 @@ constexpr char kMaximumSize[] = "maximum_size";
constexpr char kMinimumSize[] = "minimum_size";
constexpr char kName[] = "name";
constexpr char kPreMinimizedWindowState[] = "pre_minimized_window_state";
+constexpr char kTabRangeFirstIndex[] = "first_index";
+constexpr char kTabRangeLastIndex[] = "last_index";
constexpr char kSizeHeight[] = "height";
constexpr char kSizeWidth[] = "width";
constexpr char kSnapPercentage[] = "snap_percent";
constexpr char kTabs[] = "tabs";
+constexpr char kTabGroups[] = "tab_groups";
constexpr char kTabUrl[] = "url";
constexpr char kTitle[] = "title";
constexpr char kUpdatedTime[] = "updated_time_usec";
constexpr char kUuid[] = "uuid";
constexpr char kVersion[] = "version";
+constexpr char kTabGroupTitleKey[] = "title";
+constexpr char kTabGroupColorKey[] = "color";
+constexpr char kTabGroupIsCollapsed[] = "is_collapsed";
constexpr char kWindowId[] = "window_id";
constexpr char kWindowBound[] = "window_bound";
constexpr char kWindowBoundHeight[] = "height";
@@ -96,6 +112,35 @@ constexpr char kZIndex[] = "z_index";
// Valid value sets.
const std::set<std::string> kValidDeskTypes = {kDeskTypeTemplate,
kDeskTypeSaveAndRecall};
+const std::set<std::string> kValidLaunchContainers = {
+ kLaunchContainerWindow, kLaunchContainerPanelDeprecated,
+ kLaunchContainerTab, kLaunchContainerNone, kLaunchContainerUnspecified};
+const std::set<std::string> kValidWindowOpenDispositions = {
+ kWindowOpenDispositionUnknown,
+ kWindowOpenDispositionCurrentTab,
+ kWindowOpenDispositionSingletonTab,
+ kWindowOpenDispositionNewForegroundTab,
+ kWindowOpenDispositionNewBackgroundTab,
+ kWindowOpenDispositionNewPopup,
+ kWindowOpenDispositionNewWindow,
+ kWindowOpenDispositionSaveToDisk,
+ kWindowOpenDispositionOffTheRecord,
+ kWindowOpenDispositionIgnoreAction,
+ kWindowOpenDispositionSwitchToTab,
+ kWindowOpenDispositionNewPictureInPicture};
+const std::set<std::string> kValidWindowStates = {kWindowStateNormal,
+ kWindowStateMinimized,
+ kWindowStateMaximized,
+ kWindowStateFullscreen,
+ kWindowStatePrimarySnapped,
+ kWindowStateSecondarySnapped,
+ kZIndex};
+const std::set<std::string> kValidTabGroupColors = {
+ app_restore::kTabGroupColorUnknown, app_restore::kTabGroupColorGrey,
+ app_restore::kTabGroupColorBlue, app_restore::kTabGroupColorRed,
+ app_restore::kTabGroupColorYellow, app_restore::kTabGroupColorGreen,
+ app_restore::kTabGroupColorPink, app_restore::kTabGroupColorPurple,
+ app_restore::kTabGroupColorCyan, app_restore::kTabGroupColorOrange};
// Version number.
constexpr int kVersionNum = 1;
@@ -130,6 +175,20 @@ bool GetInt(const base::Value& dict, const char* key, int* out) {
return GetInt(&dict, key, out);
}
+bool GetBool(const base::Value* dict, const char* key, bool* out) {
+ const base::Value* value =
+ dict->FindKeyOfType(key, base::Value::Type::BOOLEAN);
+ if (!value)
+ return false;
+
+ *out = value->GetBool();
+ return true;
+}
+
+bool GetBool(const base::Value& dict, const char* key, bool* out) {
+ return GetBool(&dict, key, out);
+}
+
// Get App ID from App proto.
std::string GetJsonAppId(const base::Value& app) {
std::string app_type;
@@ -137,8 +196,20 @@ std::string GetJsonAppId(const base::Value& app) {
return std::string(); // App Type must be specified.
if (app_type == kAppTypeBrowser) {
+ // Return the primary browser's known app ID.
+ const bool is_lacros =
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ true;
+#else
+ // Note that this will launch the browser as lacros if it is enabled,
+ // even if it was saved as a non-lacros window (and vice-versa).
+ crosapi::lacros_startup_state::IsLacrosEnabled() &&
+ crosapi::lacros_startup_state::IsLacrosPrimaryEnabled();
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+
// Browser app has a known app ID.
- return std::string(app_constants::kChromeAppId);
+ return std::string(is_lacros ? app_constants::kLacrosAppId
+ : app_constants::kChromeAppId);
} else if (app_type == kAppTypeChrome || app_type == kAppTypeProgressiveWeb ||
app_type == kAppTypeArc) {
// Read the provided app ID
@@ -152,13 +223,114 @@ std::string GetJsonAppId(const base::Value& app) {
return std::string();
}
+// Convert a TabGroupInfo object to a base::Value dictionary.
+base::Value ConvertTabGroupInfoToValue(
+ const app_restore::TabGroupInfo& group_info) {
+ base::Value tab_group_dict(base::Value::Type::DICTIONARY);
+
+ tab_group_dict.SetIntKey(kTabRangeFirstIndex, group_info.tab_range.start());
+ tab_group_dict.SetIntKey(kTabRangeLastIndex, group_info.tab_range.end());
+ tab_group_dict.SetStringKey(
+ kTabGroupTitleKey, base::UTF16ToUTF8(group_info.visual_data.title()));
+ tab_group_dict.SetStringKey(
+ kTabGroupColorKey,
+ app_restore::TabGroupColorToString(group_info.visual_data.color()));
+ tab_group_dict.SetBoolKey(kTabGroupIsCollapsed,
+ group_info.visual_data.is_collapsed());
+
+ return tab_group_dict;
+}
+
+bool IsValidGroupColor(const std::string& group_color) {
+ return base::Contains(kValidTabGroupColors, group_color);
+}
+
+GroupColor ConvertGroupColorStringToGroupColor(const std::string& group_color) {
+ if (group_color == app_restore::kTabGroupColorGrey) {
+ return GroupColor::kGrey;
+ } else if (group_color == app_restore::kTabGroupColorBlue) {
+ return GroupColor::kBlue;
+ } else if (group_color == app_restore::kTabGroupColorRed) {
+ return GroupColor::kRed;
+ } else if (group_color == app_restore::kTabGroupColorYellow) {
+ return GroupColor::kYellow;
+ } else if (group_color == app_restore::kTabGroupColorGreen) {
+ return GroupColor::kGreen;
+ } else if (group_color == app_restore::kTabGroupColorPink) {
+ return GroupColor::kPink;
+ } else if (group_color == app_restore::kTabGroupColorPurple) {
+ return GroupColor::kPurple;
+ } else if (group_color == app_restore::kTabGroupColorCyan) {
+ return GroupColor::kCyan;
+ } else if (group_color == app_restore::kTabGroupColorOrange) {
+ return GroupColor::kOrange;
+ // There is no UNKNOWN equivalent in GroupColor, simply default
+ // to grey.
+ } else if (group_color == app_restore::kTabGroupColorUnknown) {
+ return GroupColor::kGrey;
+ } else {
+ NOTREACHED();
+ return GroupColor::kGrey;
+ }
+}
+
+// Constructs a GroupVisualData from value `group_visual_data` IFF all fields
+// are present and valid in the value parameter. Returns true on success, false
+// on failure.
+bool MakeTabGroupVisualDataFromValue(
+ const base::Value& tab_group,
+ tab_groups::TabGroupVisualData* out_visual_data) {
+ std::string tab_group_title;
+ std::string group_color_string;
+ bool is_collapsed;
+ if (GetString(tab_group, kTabGroupTitleKey, &tab_group_title) &&
+ GetBool(tab_group, kTabGroupIsCollapsed, &is_collapsed) &&
+ GetString(tab_group, kTabGroupColorKey, &group_color_string) &&
+ IsValidGroupColor(group_color_string)) {
+ *out_visual_data = tab_groups::TabGroupVisualData(
+ base::UTF8ToUTF16(tab_group_title),
+ ConvertGroupColorStringToGroupColor(group_color_string), is_collapsed);
+ return true;
+ }
+
+ return false;
+}
+
+// Constructs a gfx::Range from value `group_range` IFF all fields are
+// present and valid in the value parameter. Returns true on success, false on
+// failure.
+bool MakeTabGroupRangeFromValue(const base::Value& tab_group,
+ gfx::Range* out_range) {
+ int32_t range_start;
+ int32_t range_end;
+ if (GetInt(tab_group, kTabRangeFirstIndex, &range_start) &&
+ GetInt(tab_group, kTabRangeLastIndex, &range_end)) {
+ *out_range = gfx::Range(range_start, range_end);
+ return true;
+ }
+
+ return false;
+}
+
+// Constructs a TabGroupInfo from `tab_group` IFF all fields are present
+// and valid in the value parameter. Returns true on success, false on failure.
+absl::optional<app_restore::TabGroupInfo> MakeTabGroupInfoFromDict(
+ const base::Value& tab_group) {
+ absl::optional<app_restore::TabGroupInfo> tab_group_info = absl::nullopt;
+
+ tab_groups::TabGroupVisualData visual_data;
+ gfx::Range range;
+ if (MakeTabGroupRangeFromValue(tab_group, &range) &&
+ MakeTabGroupVisualDataFromValue(tab_group, &visual_data)) {
+ tab_group_info.emplace(range, visual_data);
+ }
+
+ return tab_group_info;
+}
+
// Returns true if launch container string value is valid.
bool IsValidLaunchContainer(const std::string& launch_container) {
- return launch_container == kLaunchContainerWindow ||
- launch_container == kLaunchContainerPanelDeprecated ||
- launch_container == kLaunchContainerTab ||
- launch_container == kLaunchContainerNone ||
- launch_container == kLaunchContainerUnspecified;
+ return base::Contains(kValidLaunchContainers, launch_container);
}
// Returns a casted apps::mojom::LaunchContainer to be set as an app restore
@@ -189,18 +361,7 @@ int32_t StringToLaunchContainer(const std::string& launch_container) {
// Returns true if the disposition is a valid value.
bool IsValidWindowOpenDisposition(const std::string& disposition) {
- return disposition == kWindowOpenDispositionUnknown ||
- disposition == kWindowOpenDispositionCurrentTab ||
- disposition == kWindowOpenDispositionSingletonTab ||
- disposition == kWindowOpenDispositionNewForegroundTab ||
- disposition == kWindowOpenDispositionNewBackgroundTab ||
- disposition == kWindowOpenDispositionNewPopup ||
- disposition == kWindowOpenDispositionNewWindow ||
- disposition == kWindowOpenDispositionSaveToDisk ||
- disposition == kWindowOpenDispositionOffTheRecord ||
- disposition == kWindowOpenDispositionIgnoreAction ||
- disposition == kWindowOpenDispositionSwitchToTab ||
- disposition == kWindowOpenDispositionNewPictureInPicture;
+ return base::Contains(kValidWindowOpenDispositions, disposition);
}
// Returns a casted WindowOpenDisposition to be set in the app restore data.
@@ -285,6 +446,10 @@ std::unique_ptr<app_restore::AppLaunchInfo> ConvertJsonToAppLaunchInfo(
// TODO(crbug.com/1311801): Add support for actual event_flag values.
app_launch_info->event_flag = 0;
+ bool app_type_browser;
+ if (GetBool(app, kIsAppTypeBrowser, &app_type_browser))
+ app_launch_info->app_type_browser = app_type_browser;
+
if (app_type == kAppTypeBrowser) {
int active_tab_index;
if (GetInt(app, kActiveTabIndex, &active_tab_index))
@@ -301,6 +466,21 @@ std::unique_ptr<app_restore::AppLaunchInfo> ConvertJsonToAppLaunchInfo(
}
}
}
+
+ // Fill the tab groups
+ app_launch_info->tab_group_infos.emplace();
+ const base::Value* tab_groups =
+ app.FindKeyOfType(kTabGroups, base::Value::Type::LIST);
+ if (tab_groups) {
+ for (auto& tab : tab_groups->GetList()) {
+ absl::optional<app_restore::TabGroupInfo> tab_group =
+ MakeTabGroupInfoFromDict(tab);
+ if (tab_group.has_value()) {
+ app_launch_info->tab_group_infos->push_back(
+ std::move(tab_group.value()));
+ }
+ }
+ }
}
// For Chrome apps and PWAs, the |app_id| is sufficient for identification.
@@ -308,12 +488,7 @@ std::unique_ptr<app_restore::AppLaunchInfo> ConvertJsonToAppLaunchInfo(
}
bool IsValidWindowState(const std::string& window_state) {
- return window_state == kWindowStateNormal ||
- window_state == kWindowStateMinimized ||
- window_state == kWindowStateMaximized ||
- window_state == kWindowStateFullscreen ||
- window_state == kWindowStatePrimarySnapped ||
- window_state == kWindowStateSecondarySnapped;
+ return base::Contains(kValidWindowStates, window_state);
}
// Convert JSON string WindowState |state| to ui::WindowShowState used by
@@ -604,22 +779,45 @@ base::Value ConvertURLsToBrowserAppTabValues(const std::vector<GURL>& urls) {
std::string GetAppTypeForJson(apps::AppRegistryCache* apps_cache,
const std::string& app_id) {
- const auto app_type = app_id == app_constants::kChromeAppId
- ? apps::AppType::kWeb
- : apps_cache->GetAppType(app_id);
+ const auto app_type = apps_cache->GetAppType(app_id);
+ // This switch should follow the same structure as DeskSyncBridge#FillApp.
switch (app_type) {
case apps::AppType::kWeb:
- return app_id == app_constants::kChromeAppId ? kAppTypeBrowser
- : kAppTypeProgressiveWeb;
- case apps::AppType::kChromeApp:
+ case apps::AppType::kSystemWeb:
return kAppTypeChrome;
+
+ case apps::AppType::kChromeApp:
+ if (app_id == app_constants::kChromeAppId) {
+ return kAppTypeBrowser;
+ } else {
+ return kAppTypeChrome;
+ }
+
+ case apps::AppType::kStandaloneBrowser:
+ if (app_id == app_constants::kLacrosAppId) {
+ return kAppTypeBrowser;
+ } else {
+ return kAppTypeUnsupported;
+ }
+
case apps::AppType::kArc:
return kAppTypeArc;
- default:
- // Default to browser if unsupported, this shouldn't be captured and
- // there is no error type in the proto definition.
- return kAppTypeBrowser;
+
+ case apps::AppType::kStandaloneBrowserChromeApp:
+ return kAppTypeChrome;
+
+ case apps::AppType::kBuiltIn:
+ case apps::AppType::kCrostini:
+ case apps::AppType::kPluginVm:
+ case apps::AppType::kUnknown:
+ case apps::AppType::kMacOs:
+ case apps::AppType::kRemote:
+ case apps::AppType::kBorealis:
+ case apps::AppType::kExtension:
+ case apps::AppType::kStandaloneBrowserExtension:
+ // Default to unsupported. This app should not be captured.
+ return kAppTypeUnsupported;
}
}
@@ -627,6 +825,12 @@ base::Value ConvertWindowToDeskApp(const std::string& app_id,
const int window_id,
const app_restore::AppRestoreData* app,
apps::AppRegistryCache* apps_cache) {
+ std::string app_type = GetAppTypeForJson(apps_cache, app_id);
+
+ if (kAppTypeUnsupported == app_type) {
+ return base::Value(base::Value::Type::NONE);
+ }
+
base::Value app_data = base::Value(base::Value::Type::DICTIONARY);
if (app->current_bounds.has_value()) {
@@ -664,18 +868,31 @@ base::Value ConvertWindowToDeskApp(const std::string& app_id,
if (app->activation_index.has_value())
app_data.SetKey(kZIndex, base::Value(app->activation_index.value()));
- std::string app_type = GetAppTypeForJson(apps_cache, app_id);
-
app_data.SetKey(kAppType, base::Value(app_type));
if (app->urls.has_value())
app_data.SetKey(kTabs, ConvertURLsToBrowserAppTabValues(app->urls.value()));
+ if (app->tab_group_infos.has_value()) {
+ base::Value tab_groups_value(base::Value::Type::LIST);
+
+ for (const auto& tab_group : app->tab_group_infos.value()) {
+ tab_groups_value.Append(ConvertTabGroupInfoToValue(tab_group));
+ }
+
+ app_data.SetKey(kTabGroups, std::move(tab_groups_value));
+ }
+
if (app->active_tab_index.has_value()) {
app_data.SetKey(kActiveTabIndex,
base::Value(app->active_tab_index.value()));
}
+ if (app->app_type_browser.has_value()) {
+ app_data.SetKey(kIsAppTypeBrowser,
+ base::Value(app->app_type_browser.value()));
+ }
+
if (app_type != kAppTypeBrowser)
app_data.SetKey(kAppId, base::Value(app_id));
@@ -725,8 +942,12 @@ base::Value ConvertRestoreDataToValue(
for (const auto& app : restore_data->app_id_to_launch_list()) {
for (const auto& window : app.second) {
- desk_data.Append(ConvertWindowToDeskApp(app.first, window.first,
- window.second.get(), apps_cache));
+ auto app_data = ConvertWindowToDeskApp(app.first, window.first,
+ window.second.get(), apps_cache);
+ if (app_data.is_none())
+ continue;
+
+ desk_data.Append(std::move(app_data));
}
}
@@ -760,6 +981,31 @@ namespace desks_storage {
namespace desk_template_conversion {
+// Converts the TabGroupColorId passed into its string equivalent
+// as defined in the k constants above.
+std::string ConvertTabGroupColorIdToString(GroupColor color) {
+ switch (color) {
+ case GroupColor::kGrey:
+ return app_restore::kTabGroupColorGrey;
+ case GroupColor::kBlue:
+ return app_restore::kTabGroupColorBlue;
+ case GroupColor::kRed:
+ return app_restore::kTabGroupColorRed;
+ case GroupColor::kYellow:
+ return app_restore::kTabGroupColorYellow;
+ case GroupColor::kGreen:
+ return app_restore::kTabGroupColorGreen;
+ case GroupColor::kPink:
+ return app_restore::kTabGroupColorPink;
+ case GroupColor::kPurple:
+ return app_restore::kTabGroupColorPurple;
+ case GroupColor::kCyan:
+ return app_restore::kTabGroupColorCyan;
+ case GroupColor::kOrange:
+ return app_restore::kTabGroupColorOrange;
+ }
+}
+
// Converts a time field from sync protobufs to a time object.
base::Time ProtoTimeToTime(int64_t proto_time) {
return base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(proto_time));
@@ -771,8 +1017,9 @@ int64_t TimeToProtoTime(const base::Time& t) {
return t.ToDeltaSinceWindowsEpoch().InMicroseconds();
}
-std::unique_ptr<ash::DeskTemplate> ParseDeskTemplateFromPolicy(
- const base::Value& policy_json) {
+std::unique_ptr<ash::DeskTemplate> ParseDeskTemplateFromSource(
+ const base::Value& policy_json,
+ ash::DeskTemplateSource source) {
if (!policy_json.is_dict())
return nullptr;
@@ -809,11 +1056,11 @@ std::unique_ptr<ash::DeskTemplate> ParseDeskTemplateFromPolicy(
std::unique_ptr<ash::DeskTemplate> desk_template =
std::make_unique<ash::DeskTemplate>(
- uuid, ash::DeskTemplateSource::kPolicy, name, created_time);
+ uuid, source, name, created_time,
+ GetDeskTypeFromString(desk_type_string));
desk_template->set_updated_time(updated_time);
desk_template->set_desk_restore_data(ConvertJsonToRestoreData(desk));
- desk_template->set_type(GetDeskTypeFromString(desk_type_string));
return desk_template;
}
diff --git a/chromium/components/desks_storage/core/desk_template_conversion.h b/chromium/components/desks_storage/core/desk_template_conversion.h
index 57d9676be39..ff9ad4f45fa 100644
--- a/chromium/components/desks_storage/core/desk_template_conversion.h
+++ b/chromium/components/desks_storage/core/desk_template_conversion.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_DESKS_STORAGE_CORE_DESK_TEMPLATE_CONVERSION_H_
#define COMPONENTS_DESKS_STORAGE_CORE_DESK_TEMPLATE_CONVERSION_H_
+#include "ash/public/cpp/desk_template.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/services/app_service/public/cpp/app_types.h"
@@ -31,6 +32,10 @@ using SyncWindowOpenDisposition =
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition;
using SyncLaunchContainer = sync_pb::WorkspaceDeskSpecifics_LaunchContainer;
+// Converts the TabGroupColorId passed into its string equivalent
+// as defined in the k constants above.
+std::string ConvertTabGroupColorIdToString(tab_groups::TabGroupColorId color);
+
// Converts a time field from sync protobufs to a time object.
base::Time ProtoTimeToTime(int64_t proto_time);
@@ -38,11 +43,11 @@ base::Time ProtoTimeToTime(int64_t proto_time);
// (Microseconds since the Windows epoch).
int64_t TimeToProtoTime(const base::Time& t);
-// Converts a JSON desk template to an ash desk template.
-// The returned desk template will have source set to
-// |ash::DeskTemplateSource::kPolicy|.
-std::unique_ptr<ash::DeskTemplate> ParseDeskTemplateFromPolicy(
- const base::Value& policy_json);
+// Converts a JSON desk template to an ash desk template. The returned desk
+// template will have source set to `source`.
+std::unique_ptr<ash::DeskTemplate> ParseDeskTemplateFromSource(
+ const base::Value& policy_json,
+ ash::DeskTemplateSource source);
base::Value SerializeDeskTemplateAsPolicy(
const ash::DeskTemplate* desk_template,
@@ -76,4 +81,4 @@ apps::mojom::LaunchContainer ToMojomLaunchContainer(
} // namespace desks_storage
-#endif // COMPONENTS_DESKS_STORAGE_CORE_DESK_TEMPLATE_CONVERSION_H_ \ No newline at end of file
+#endif // COMPONENTS_DESKS_STORAGE_CORE_DESK_TEMPLATE_CONVERSION_H_
diff --git a/chromium/components/desks_storage/core/desk_template_conversion_unittests.cc b/chromium/components/desks_storage/core/desk_template_conversion_unittests.cc
index 56939ee5bd7..1e0f941d1a1 100644
--- a/chromium/components/desks_storage/core/desk_template_conversion_unittests.cc
+++ b/chromium/components/desks_storage/core/desk_template_conversion_unittests.cc
@@ -9,6 +9,7 @@
#include "ash/public/cpp/desk_template.h"
#include "base/guid.h"
#include "base/json/json_reader.h"
+#include "base/json/values_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
@@ -18,6 +19,9 @@
#include "components/app_restore/app_launch_info.h"
#include "components/app_restore/app_restore_data.h"
#include "components/app_restore/window_info.h"
+#include "components/desks_storage/core/desk_test_util.h"
+#include "components/desks_storage/core/saved_desk_builder.h"
+#include "components/desks_storage/core/saved_desk_test_util.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
#include "components/services/app_service/public/cpp/app_types.h"
@@ -28,14 +32,16 @@ namespace desks_storage {
namespace {
+const int32_t kTestWindowId = 1234567;
const std::string kEmptyJson = "{}";
const std::string kTestUuidBrowser = "040b6112-67f2-4d3c-8ba8-53a117272eba";
+constexpr int kBrowserWindowId = 1555;
+const std::string kBrowserUrl1 = "https://example.com/";
+const std::string kBrowserUrl2 = "https://example.com/2";
const std::string kTestUuidChromeAndProgressive =
"7f4b7ff0-970a-41bb-aa91-f6c3e2724207";
const std::string kBrowserTemplateName = "BrowserTest";
const std::string kChromePwaTemplateName = "ChromeAppTest";
-const std::string kChromeAppId = "chrome_app_1";
-const std::string kProgressiveAppid = "progressive_app_1";
const std::string kValidTemplateBrowser =
"{\"version\":1,\"uuid\":\"" + kTestUuidBrowser + "\",\"name\":\"" +
kBrowserTemplateName +
@@ -43,9 +49,14 @@ const std::string kValidTemplateBrowser =
"\"1633535632\",\"desk_type\":\"TEMPLATE\",\"desk\":{\"apps\":[{\"window_"
"bound\":{\"left\":0,\"top\":1,\"height\":121,\"width\":120},\"window_"
"state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
- "\"url\":\"https://example.com/\"},{\"url\":\"https://"
+ "\"url\":\"" +
+ kBrowserUrl1 +
+ "\"},{\"url\":\"https://"
"example.com/"
- "2\"}],\"active_tab_index\":1,\"window_id\":0,"
+ "2\"}],\"tab_groups\":[{\"first_"
+ "index\":1,\"last_index\":2,\"title\":\"sample_tab_"
+ "group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_index\":"
+ "1,\"window_id\":0,"
"\"display_id\":\"100\",\"event_flag\":0,\"pre_minimized_window_state\":"
"\"NORMAL\"}]}}";
const std::string kValidTemplateChromeAndProgressive =
@@ -58,12 +69,13 @@ const std::string kValidTemplateChromeAndProgressive =
"\"left\":200,\"top\":200,\"height\":1000,\"width\":1000},\"window_state\":"
"\"PRIMARY_SNAPPED\",\"z_index\":2,\"app_type\":\"CHROME_APP\",\"app_id\":"
"\"" +
- kChromeAppId +
+ desk_test_util::kTestChromeAppId1 +
"\",\"window_id\":0,\"display_id\":\"100\",\"event_flag\":0,\"pre_"
"minimized_window_state\":\"NORMAL\", \"snap_percent\":75},{\"window_"
"bound\":{\"left\":0,\"top\":0,\"height\":120,\"width\":120},\"window_"
- "state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"PWA\",\"app_id\":\"" +
- kProgressiveAppid +
+ "state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"CHROME_APP\",\"app_id\":"
+ "\"" +
+ desk_test_util::kTestPwaAppId1 +
"\",\"window_id\":1,\"display_id\":"
"\"100\",\"event_flag\":0,\"pre_minimized_window_state\":\"NORMAL\"}]}}";
const std::string kTemplateWithoutType =
@@ -73,19 +85,20 @@ const std::string kTemplateWithoutType =
"\"1633535632\",\"desk\":{\"apps\":[{\"window_"
"bound\":{\"left\":0,\"top\":1,\"height\":121,\"width\":120},\"window_"
"state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
- "\"url\":\"https://example.com/\"},{\"url\":\"https://"
- "example.com/"
- "2\"}],\"active_tab_index\":1,\"window_id\":0,"
+ "\"url\":\"" +
+ kBrowserUrl1 + "\"},{\"url\":\"" + kBrowserUrl1 +
+ "\"}],\"tab_groups\":[{\"first_"
+ "index\":1,\"last_index\":2,\"title\":\"sample_tab_"
+ "group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_index\":"
+ "1,\"window_id\":0,"
"\"display_id\":\"100\",\"event_flag\":0,\"pre_minimized_window_state\":"
"\"NORMAL\"}]}}";
+const constexpr char16_t kSampleTabGroupTitle[] = u"sample_tab_group";
-apps::AppPtr MakeApp(const char* app_id,
- const char* name,
- apps::AppType app_type) {
- apps::AppPtr app = std::make_unique<apps::App>(app_type, app_id);
- app->readiness = apps::Readiness::kReady;
- app->name = name;
- return app;
+app_restore::TabGroupInfo MakeSampleTabGroup() {
+ return app_restore::TabGroupInfo(
+ {1, 2}, tab_groups::TabGroupVisualData(
+ kSampleTabGroupTitle, tab_groups::TabGroupColorId::kGrey));
}
} // namespace
@@ -101,38 +114,10 @@ class DeskTemplateConversionTest : public testing::Test {
: account_id_(AccountId::FromUserEmail("test@gmail.com")),
cache_(std::make_unique<apps::AppRegistryCache>()) {}
- void PopulateAppRegistryCache() {
- std::vector<apps::AppPtr> deltas;
-
- deltas.push_back(MakeApp(kProgressiveAppid.c_str(), "Test PWA App",
- apps::AppType::kWeb));
- // chromeAppId returns kExtension in the real Apps cache.
- deltas.push_back(MakeApp(app_constants::kChromeAppId, "Chrome Browser",
- apps::AppType::kChromeApp));
- deltas.push_back(MakeApp(kChromeAppId.c_str(), "Test Chrome App",
- apps::AppType::kChromeApp));
-
- if (base::FeatureList::IsEnabled(
- apps::kAppServiceOnAppUpdateWithoutMojom)) {
- cache_->OnApps(std::move(deltas), apps::AppType::kUnknown,
- /*should_notify_initialized=*/false);
- } else {
- std::vector<apps::mojom::AppPtr> mojom_deltas;
- for (const auto& delta : deltas) {
- mojom_deltas.push_back(apps::ConvertAppToMojomApp(delta));
- }
- cache_->OnApps(std::move(mojom_deltas), apps::mojom::AppType::kUnknown,
- /*should_notify_initialized=*/false);
- }
-
- cache_->SetAccountId(account_id_);
-
- apps::AppRegistryCacheWrapper::Get().AddAppRegistryCache(account_id_,
- cache_.get());
+ void SetUp() override {
+ desk_test_util::PopulateAppRegistryCache(account_id_, cache_.get());
}
- void SetUp() override { PopulateAppRegistryCache(); }
-
AccountId account_id_;
private:
@@ -148,8 +133,8 @@ TEST_F(DeskTemplateConversionTest, ParseBrowserTemplate) {
EXPECT_TRUE(parsed_json.value->is_dict());
std::unique_ptr<ash::DeskTemplate> dt =
- desk_template_conversion::ParseDeskTemplateFromPolicy(
- parsed_json.value.value());
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ parsed_json.value.value(), ash::DeskTemplateSource::kPolicy);
EXPECT_TRUE(dt != nullptr);
EXPECT_EQ(dt->uuid(), base::GUID::ParseCaseInsensitive(kTestUuidBrowser));
@@ -182,8 +167,10 @@ TEST_F(DeskTemplateConversionTest, ParseBrowserTemplate) {
// it
// https://osscs.corp.google.com/chromium/chromium/src/+/main:components/app_restore/app_restore_data.cc
EXPECT_TRUE(ali->urls.has_value());
- EXPECT_EQ(ali->urls.value()[0].spec(), "https://example.com/");
- EXPECT_EQ(ali->urls.value()[1].spec(), "https://example.com/2");
+ EXPECT_EQ(ali->urls.value()[0].spec(), kBrowserUrl1);
+ EXPECT_EQ(ali->urls.value()[1].spec(), kBrowserUrl2);
+ EXPECT_TRUE(ali->tab_group_infos.has_value());
+ EXPECT_EQ(ali->tab_group_infos.value()[0], MakeSampleTabGroup());
EXPECT_TRUE(wi->window_state_type.has_value());
EXPECT_EQ(wi->window_state_type.value(), chromeos::WindowStateType::kNormal);
EXPECT_TRUE(wi->pre_minimized_show_state_type.has_value());
@@ -206,8 +193,8 @@ TEST_F(DeskTemplateConversionTest, ParseChromePwaTemplate) {
EXPECT_TRUE(parsed_json.value->is_dict());
std::unique_ptr<ash::DeskTemplate> dt =
- desk_template_conversion::ParseDeskTemplateFromPolicy(
- parsed_json.value.value());
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ parsed_json.value.value(), ash::DeskTemplateSource::kPolicy);
EXPECT_TRUE(dt != nullptr);
EXPECT_EQ(dt->uuid(),
@@ -220,21 +207,21 @@ TEST_F(DeskTemplateConversionTest, ParseChromePwaTemplate) {
EXPECT_TRUE(rd != nullptr);
EXPECT_EQ(rd->app_id_to_launch_list().size(), 2UL);
- EXPECT_NE(rd->app_id_to_launch_list().find(kChromeAppId),
+ EXPECT_NE(rd->app_id_to_launch_list().find(desk_test_util::kTestChromeAppId1),
rd->app_id_to_launch_list().end());
- EXPECT_NE(rd->app_id_to_launch_list().find(kProgressiveAppid),
+ EXPECT_NE(rd->app_id_to_launch_list().find(desk_test_util::kTestPwaAppId1),
rd->app_id_to_launch_list().end());
const app_restore::AppRestoreData* ard_chrome =
- rd->GetAppRestoreData(kChromeAppId, 0);
+ rd->GetAppRestoreData(desk_test_util::kTestChromeAppId1, 0);
const app_restore::AppRestoreData* ard_pwa =
- rd->GetAppRestoreData(kProgressiveAppid, 1);
+ rd->GetAppRestoreData(desk_test_util::kTestPwaAppId1, 1);
EXPECT_TRUE(ard_chrome != nullptr);
EXPECT_TRUE(ard_pwa != nullptr);
std::unique_ptr<app_restore::AppLaunchInfo> ali_chrome =
- ard_chrome->GetAppLaunchInfo(kChromeAppId, 0);
+ ard_chrome->GetAppLaunchInfo(desk_test_util::kTestChromeAppId1, 0);
std::unique_ptr<app_restore::AppLaunchInfo> ali_pwa =
- ard_pwa->GetAppLaunchInfo(kProgressiveAppid, 1);
+ ard_pwa->GetAppLaunchInfo(desk_test_util::kTestPwaAppId1, 1);
std::unique_ptr<app_restore::WindowInfo> wi_chrome =
ard_chrome->GetWindowInfo();
std::unique_ptr<app_restore::WindowInfo> wi_pwa = ard_pwa->GetWindowInfo();
@@ -293,8 +280,8 @@ TEST_F(DeskTemplateConversionTest, EmptyJsonTest) {
EXPECT_TRUE(parsed_json.value->is_dict());
std::unique_ptr<ash::DeskTemplate> dt =
- desk_template_conversion::ParseDeskTemplateFromPolicy(
- parsed_json.value.value());
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ parsed_json.value.value(), ash::DeskTemplateSource::kPolicy);
EXPECT_TRUE(dt == nullptr);
}
@@ -307,8 +294,8 @@ TEST_F(DeskTemplateConversionTest, ParsesWithDefaultValueSetToTemplates) {
EXPECT_TRUE(parsed_json.value->is_dict());
std::unique_ptr<ash::DeskTemplate> dt =
- desk_template_conversion::ParseDeskTemplateFromPolicy(
- parsed_json.value.value());
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ parsed_json.value.value(), ash::DeskTemplateSource::kPolicy);
EXPECT_TRUE(dt);
EXPECT_EQ(ash::DeskTemplateType::kTemplate, dt->type());
}
@@ -322,8 +309,35 @@ TEST_F(DeskTemplateConversionTest, DeskTemplateFromJsonBrowserTest) {
EXPECT_TRUE(parsed_json.value->is_dict());
std::unique_ptr<ash::DeskTemplate> desk_template =
- desk_template_conversion::ParseDeskTemplateFromPolicy(
- parsed_json.value.value());
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ parsed_json.value.value(), ash::DeskTemplateSource::kPolicy);
+
+ apps::AppRegistryCache* app_cache =
+ apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(account_id_);
+
+ base::Value desk_template_value =
+ desk_template_conversion::SerializeDeskTemplateAsPolicy(
+ desk_template.get(), app_cache);
+
+ EXPECT_EQ(parsed_json.value, desk_template_value);
+}
+
+TEST_F(DeskTemplateConversionTest, ToJsonIgnoreUnsupportedApp) {
+ base::StringPiece raw_json = base::StringPiece(kValidTemplateBrowser);
+ base::JSONReader::ValueWithError parsed_json =
+ base::JSONReader::ReadAndReturnValueWithError(raw_json);
+
+ EXPECT_TRUE(parsed_json.value.has_value());
+ EXPECT_TRUE(parsed_json.value->is_dict());
+
+ std::unique_ptr<ash::DeskTemplate> desk_template =
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ parsed_json.value.value(), ash::DeskTemplateSource::kUser);
+
+ // Adding this unsupported app should not change the serialized JSON content.
+ saved_desk_test_util::AddGenericAppWindow(
+ kTestWindowId, desk_test_util::kTestUnsupportedAppId,
+ desk_template->mutable_desk_restore_data());
apps::AppRegistryCache* app_cache =
apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(account_id_);
@@ -345,8 +359,8 @@ TEST_F(DeskTemplateConversionTest, DeskTemplateFromJsonAppTest) {
EXPECT_TRUE(parsed_json.value->is_dict());
std::unique_ptr<ash::DeskTemplate> desk_template =
- desk_template_conversion::ParseDeskTemplateFromPolicy(
- parsed_json.value.value());
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ parsed_json.value.value(), ash::DeskTemplateSource::kPolicy);
apps::AppRegistryCache* app_cache =
apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(account_id_);
@@ -358,4 +372,56 @@ TEST_F(DeskTemplateConversionTest, DeskTemplateFromJsonAppTest) {
EXPECT_EQ(parsed_json.value, desk_template_value);
}
+TEST_F(DeskTemplateConversionTest, EnsureLacrosBrowserWindowsSavedProperly) {
+ base::Time created_time = base::Time::Now();
+ std::unique_ptr<ash::DeskTemplate> desk_template =
+ SavedDeskBuilder()
+ .SetUuid(kTestUuidBrowser)
+ .SetName(kBrowserTemplateName)
+ .SetType(ash::DeskTemplateType::kSaveAndRecall)
+ .SetCreatedTime(created_time)
+ .AddLacrosBrowserAppWindow(kBrowserWindowId,
+ {GURL(kBrowserUrl1), GURL(kBrowserUrl2)})
+ .Build();
+
+ apps::AppRegistryCache* app_cache =
+ apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(account_id_);
+
+ base::Value desk_template_value =
+ desk_template_conversion::SerializeDeskTemplateAsPolicy(
+ desk_template.get(), app_cache);
+
+ base::Value::Dict expected_browser_tab1;
+ expected_browser_tab1.Set("url", base::Value(kBrowserUrl1));
+ base::Value::Dict expected_browser_tab2;
+ expected_browser_tab2.Set("url", base::Value(kBrowserUrl2));
+ base::Value::List expected_tab_list;
+ expected_tab_list.Append(std::move(expected_browser_tab1));
+ expected_tab_list.Append(std::move(expected_browser_tab2));
+
+ base::Value::Dict expected_browser_app_value;
+ expected_browser_app_value.Set("app_type", base::Value("BROWSER"));
+ expected_browser_app_value.Set("event_flag", base::Value(0));
+ expected_browser_app_value.Set("window_id", base::Value(kBrowserWindowId));
+ expected_browser_app_value.Set("tabs", std::move(expected_tab_list));
+
+ base::Value::List expected_app_list;
+ expected_app_list.Append(std::move(expected_browser_app_value));
+
+ base::Value::Dict expected_desk_value;
+ expected_desk_value.Set("apps", std::move(expected_app_list));
+
+ base::Value::Dict expected_value;
+ expected_value.Set("version", base::Value(1));
+ expected_value.Set("uuid", base::Value(kTestUuidBrowser));
+ expected_value.Set("name", base::Value(kBrowserTemplateName));
+ expected_value.Set("created_time_usec", base::TimeToValue(created_time));
+ expected_value.Set("updated_time_usec",
+ base::TimeToValue(desk_template->GetLastUpdatedTime()));
+ expected_value.Set("desk_type", base::Value("SAVE_AND_RECALL"));
+ expected_value.Set("desk", std::move(expected_desk_value));
+
+ EXPECT_EQ(expected_value, desk_template_value);
+}
+
} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/desk_template_util.cc b/chromium/components/desks_storage/core/desk_template_util.cc
index ec2a1052642..f64dbf77755 100644
--- a/chromium/components/desks_storage/core/desk_template_util.cc
+++ b/chromium/components/desks_storage/core/desk_template_util.cc
@@ -4,40 +4,28 @@
#include "components/desks_storage/core/desk_template_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "third_party/re2/src/re2/re2.h"
-
-namespace {
-
-// Duplicate value format.
-constexpr char kDuplicateNumberFormat[] = "(%d)";
-// Initial duplicate number value.
-constexpr char kInitialDuplicateNumberValue[] = " (1)";
-// Regex used in determining if duplicate name should be incremented.
-constexpr char kDuplicateNumberRegex[] = "\\(([0-9]+)\\)$";
-
-} // namespace
-
namespace desks_storage {
namespace desk_template_util {
-std::u16string AppendDuplicateNumberToDuplicateName(
- const std::u16string& duplicate_name_u16) {
- std::string duplicate_name = base::UTF16ToUTF8(duplicate_name_u16);
- int found_duplicate_number;
-
- if (RE2::PartialMatch(duplicate_name, kDuplicateNumberRegex,
- &found_duplicate_number)) {
- RE2::Replace(
- &duplicate_name, kDuplicateNumberRegex,
- base::StringPrintf(kDuplicateNumberFormat, found_duplicate_number + 1));
- } else {
- duplicate_name.append(kInitialDuplicateNumberValue);
+ash::DeskTemplate* FindOtherEntryWithName(
+ const std::u16string& name,
+ const base::GUID& uuid,
+ const std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>& entries) {
+ auto iter = std::find_if(
+ entries.begin(), entries.end(),
+ [name, uuid](const std::pair<const base::GUID,
+ std::unique_ptr<ash::DeskTemplate>>& entry) {
+ // Name duplication is allowed if one of the templates is an admin
+ // template.
+ return (entry.second->uuid() != uuid &&
+ entry.second->template_name() == name &&
+ entry.second->source() != ash::DeskTemplateSource::kPolicy);
+ });
+ if (iter == entries.end()) {
+ return nullptr;
}
-
- return base::UTF8ToUTF16(duplicate_name);
+ return iter->second.get();
}
} // namespace desk_template_util
diff --git a/chromium/components/desks_storage/core/desk_template_util.h b/chromium/components/desks_storage/core/desk_template_util.h
index 77caedb2dee..f666500f65d 100644
--- a/chromium/components/desks_storage/core/desk_template_util.h
+++ b/chromium/components/desks_storage/core/desk_template_util.h
@@ -7,16 +7,16 @@
#include <string>
+#include "ash/public/cpp/desk_template.h"
+
namespace desks_storage {
namespace desk_template_util {
-// Returns a copy of a duplicated name to be stored. This function works by
-// taking the name to be duplicated and adding a "(1)" to it. If the name
-// already has "(1)" then the number inside of the parenthesis will be
-// incremented.
-std::u16string AppendDuplicateNumberToDuplicateName(
- const std::u16string& duplicate_name_u16);
+ash::DeskTemplate* FindOtherEntryWithName(
+ const std::u16string& name,
+ const base::GUID& uuid,
+ const std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>& entries);
} // namespace desk_template_util
diff --git a/chromium/components/desks_storage/core/desk_template_util_unittests.cc b/chromium/components/desks_storage/core/desk_template_util_unittests.cc
index 0cedf515423..5de83874d07 100644
--- a/chromium/components/desks_storage/core/desk_template_util_unittests.cc
+++ b/chromium/components/desks_storage/core/desk_template_util_unittests.cc
@@ -5,40 +5,59 @@
#include "components/desks_storage/core/desk_template_util.h"
#include <string>
+#include "base/time/time.h"
+#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace desks_storage {
using DeskTemplateUtilTest = testing::Test;
-TEST_F(DeskTemplateUtilTest, AddSequenceNumberForFirstDuplicate) {
- EXPECT_EQ(u"test (1)",
- desk_template_util::AppendDuplicateNumberToDuplicateName(u"test"));
+TEST_F(DeskTemplateUtilTest, FindDuplicateEntry) {
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>> entries;
+ const base::GUID& uuid = base::GUID::GenerateRandomV4();
+ auto desk_template = std::make_unique<ash::DeskTemplate>(
+ uuid.AsLowercaseString(), ash::DeskTemplateSource::kUser, "Template 1",
+ base::Time::Now(), ash::DeskTemplateType::kTemplate);
+ entries[uuid] = std::move(desk_template);
+
+ const base::GUID& new_uuid = base::GUID::GenerateRandomV4();
+ auto new_desk_template = std::make_unique<ash::DeskTemplate>(
+ new_uuid.AsLowercaseString(), ash::DeskTemplateSource::kUser,
+ "Template 1", base::Time::Now(), ash::DeskTemplateType::kTemplate);
+ entries[new_uuid] = std::move(new_desk_template);
+ EXPECT_TRUE(
+ desk_template_util::FindOtherEntryWithName(u"Template 1", uuid, entries));
}
-TEST_F(DeskTemplateUtilTest, IncrementSequenceNumberOnSubsequentDuplicate) {
- EXPECT_EQ(
- u"test (2)",
- desk_template_util::AppendDuplicateNumberToDuplicateName(u"test (1)"));
- EXPECT_EQ(
- u"test (10)",
- desk_template_util::AppendDuplicateNumberToDuplicateName(u"test (9)"));
- EXPECT_EQ(
- u"test (11)",
- desk_template_util::AppendDuplicateNumberToDuplicateName(u"test (10)"));
- EXPECT_EQ(
- u"test (101)",
- desk_template_util::AppendDuplicateNumberToDuplicateName(u"test (100)"));
+TEST_F(DeskTemplateUtilTest, FindNoDuplicateEntryInFilledMap) {
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>> entries;
+ const base::GUID& uuid = base::GUID::GenerateRandomV4();
+ auto desk_template = std::make_unique<ash::DeskTemplate>(
+ uuid.AsLowercaseString(), ash::DeskTemplateSource::kUser, "Template 1",
+ base::Time::Now(), ash::DeskTemplateType::kTemplate);
+ entries[uuid] = std::move(desk_template);
+
+ const base::GUID& new_uuid = base::GUID::GenerateRandomV4();
+ auto new_desk_template = std::make_unique<ash::DeskTemplate>(
+ new_uuid.AsLowercaseString(), ash::DeskTemplateSource::kUser,
+ "Template 2", base::Time::Now(), ash::DeskTemplateType::kTemplate);
+ entries[new_uuid] = std::move(new_desk_template);
+ EXPECT_FALSE(
+ desk_template_util::FindOtherEntryWithName(u"Template 1", uuid, entries));
}
-TEST_F(DeskTemplateUtilTest, OnlyIncrementTheLastSequenceNumber) {
- EXPECT_EQ(u"test (1) (2)",
- desk_template_util::AppendDuplicateNumberToDuplicateName(
- u"test (1) (1)"));
- EXPECT_EQ(u"test (1) (10)",
- desk_template_util::AppendDuplicateNumberToDuplicateName(
- u"test (1) (9)"));
+TEST_F(DeskTemplateUtilTest, FindNoDuplicateEntryInAOneElementMap) {
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>> entries;
+ const base::GUID& uuid = base::GUID::GenerateRandomV4();
+ auto desk_template = std::make_unique<ash::DeskTemplate>(
+ uuid.AsLowercaseString(), ash::DeskTemplateSource::kUser, "Template 1",
+ base::Time::Now(), ash::DeskTemplateType::kTemplate);
+
+ entries[uuid] = std::move(desk_template);
+ EXPECT_FALSE(
+ desk_template_util::FindOtherEntryWithName(u"Template 1", uuid, entries));
}
} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/desk_test_util.cc b/chromium/components/desks_storage/core/desk_test_util.cc
new file mode 100644
index 00000000000..d4aa25f77f6
--- /dev/null
+++ b/chromium/components/desks_storage/core/desk_test_util.cc
@@ -0,0 +1,95 @@
+// Copyright 2022 The Chromium 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/desks_storage/core/desk_test_util.h"
+
+#include "components/app_constants/constants.h"
+#include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+
+namespace desks_storage {
+
+namespace desk_test_util {
+
+apps::AppPtr MakeApp(const char* app_id,
+ const char* name,
+ apps::AppType app_type) {
+ apps::AppPtr app = std::make_unique<apps::App>(app_type, app_id);
+ app->readiness = apps::Readiness::kReady;
+ app->name = name;
+ return app;
+}
+
+void PopulateAppRegistryCache(AccountId account_id,
+ apps::AppRegistryCache* cache) {
+ std::vector<apps::AppPtr> deltas;
+
+ deltas.push_back(
+ MakeApp(kTestPwaAppId, "Test PWA App", apps::AppType::kChromeApp));
+ // chromeAppId returns kExtension in the real Apps cache.
+ deltas.push_back(MakeApp(app_constants::kChromeAppId, "Ash Chrome Browser",
+ apps::AppType::kChromeApp));
+ deltas.push_back(MakeApp(app_constants::kLacrosAppId, "Lacros Chrome Browser",
+ apps::AppType::kStandaloneBrowser));
+ deltas.push_back(
+ MakeApp(kTestChromeAppId, "Test Chrome App", apps::AppType::kChromeApp));
+ deltas.push_back(MakeApp(kTestArcAppId, "Arc app", apps::AppType::kArc));
+ deltas.push_back(
+ MakeApp(kTestPwaAppId1, "Test PWA App 2", apps::AppType::kChromeApp));
+ deltas.push_back(MakeApp(kTestChromeAppId1, "Test Chrome App 2",
+ apps::AppType::kChromeApp));
+ deltas.push_back(
+ MakeApp(kTestSwaAppId, "Test System Web App 1", apps::AppType::kWeb));
+ deltas.push_back(MakeApp(kTestUnsupportedAppId, "Test Supported App 1",
+ apps::AppType::kPluginVm));
+ deltas.push_back(MakeApp(kTestLacrosChromeAppId, "Test Chrome App",
+ apps::AppType::kStandaloneBrowserChromeApp));
+
+ if (base::FeatureList::IsEnabled(apps::kAppServiceOnAppUpdateWithoutMojom)) {
+ cache->OnApps(std::move(deltas), apps::AppType::kUnknown,
+ /*should_notify_initialized=*/false);
+ } else {
+ std::vector<apps::mojom::AppPtr> mojom_deltas;
+ for (const auto& delta : deltas) {
+ mojom_deltas.push_back(apps::ConvertAppToMojomApp(delta));
+ }
+ cache->OnApps(std::move(mojom_deltas), apps::mojom::AppType::kUnknown,
+ /*should_notify_initialized=*/false);
+ }
+
+ cache->SetAccountId(account_id);
+
+ apps::AppRegistryCacheWrapper::Get().AddAppRegistryCache(account_id, cache);
+}
+
+void AddAppIdToAppRegistryCache(AccountId account_id,
+ apps::AppRegistryCache* cache,
+ const char* app_id) {
+ std::vector<apps::AppPtr> deltas;
+
+ // We need to add the app as any type that's not a `apps::AppType::kChromeApp`
+ // since there's a default hard coded string for that type, which will merge
+ // all app_id to it.
+ deltas.push_back(MakeApp(app_id, "Arc app", apps::AppType::kArc));
+
+ if (base::FeatureList::IsEnabled(apps::kAppServiceOnAppUpdateWithoutMojom)) {
+ cache->OnApps(std::move(deltas), apps::AppType::kUnknown,
+ /*should_notify_initialized=*/false);
+ } else {
+ std::vector<apps::mojom::AppPtr> mojom_deltas;
+ for (const auto& delta : deltas) {
+ mojom_deltas.push_back(apps::ConvertAppToMojomApp(delta));
+ }
+ cache->OnApps(std::move(mojom_deltas), apps::mojom::AppType::kUnknown,
+ /*should_notify_initialized=*/false);
+ }
+
+ cache->SetAccountId(account_id);
+
+ apps::AppRegistryCacheWrapper::Get().AddAppRegistryCache(account_id, cache);
+}
+
+} // namespace desk_test_util
+
+} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/desk_test_util.h b/chromium/components/desks_storage/core/desk_test_util.h
new file mode 100644
index 00000000000..34be7467b9e
--- /dev/null
+++ b/chromium/components/desks_storage/core/desk_test_util.h
@@ -0,0 +1,36 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DESKS_STORAGE_CORE_DESK_TEST_UTIL_H_
+#define COMPONENTS_DESKS_STORAGE_CORE_DESK_TEST_UTIL_H_
+
+#include "components/account_id/account_id.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
+
+namespace desks_storage {
+
+namespace desk_test_util {
+
+constexpr char kTestChromeAppId[] = "test_chrome_app_id";
+constexpr char kTestPwaAppId[] = "test_pwa_app_id";
+constexpr char kTestSwaAppId[] = "test_swa_app_id";
+constexpr char kTestArcAppId[] = "test_arc_app_id";
+constexpr char kTestLacrosChromeAppId[] = "test_lacros_chrome_app_id";
+constexpr char kTestUnsupportedAppId[] = "test_unsupported_app_id";
+constexpr char kTestChromeAppId1[] = "test_chrome_app_1";
+constexpr char kTestPwaAppId1[] = "test_pwa_app_1";
+
+// Populates the given cache with test app information.
+void PopulateAppRegistryCache(AccountId account_id,
+ apps::AppRegistryCache* cache);
+
+void AddAppIdToAppRegistryCache(AccountId account_id,
+ apps::AppRegistryCache* cache,
+ const char* app_id);
+
+} // namespace desk_test_util
+
+} // namespace desks_storage
+
+#endif // COMPONENTS_DESKS_STORAGE_CORE_DESK_TEST_UTIL_H_
diff --git a/chromium/components/desks_storage/core/desk_test_util_unittests.cc b/chromium/components/desks_storage/core/desk_test_util_unittests.cc
new file mode 100644
index 00000000000..b2cf77654a0
--- /dev/null
+++ b/chromium/components/desks_storage/core/desk_test_util_unittests.cc
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium 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/desks_storage/core/desk_test_util.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace desks_storage {
+
+using DeskTestUtilTest = testing::Test;
+
+TEST_F(DeskTestUtilTest, PopulateRegistryCacheHasAppInfo) {
+ AccountId account_id = AccountId::FromUserEmail("test@gmail.com");
+ auto cache = std::make_unique<apps::AppRegistryCache>();
+ desk_test_util::PopulateAppRegistryCache(account_id, cache.get());
+ EXPECT_EQ(10ul, cache->GetAllApps().size());
+}
+
+TEST_F(DeskTestUtilTest, AddOneAppIdToRegistryCacheHasAppInfo) {
+ AccountId account_id = AccountId::FromUserEmail("test@gmail.com");
+ auto cache = std::make_unique<apps::AppRegistryCache>();
+ desk_test_util::PopulateAppRegistryCache(account_id, cache.get());
+ desk_test_util::AddAppIdToAppRegistryCache(account_id, cache.get(), "test");
+ EXPECT_EQ(11ul, cache->GetAllApps().size());
+}
+
+} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/local_desk_data_manager.cc b/chromium/components/desks_storage/core/local_desk_data_manager.cc
index 028baf00c6b..b527c6d8b08 100644
--- a/chromium/components/desks_storage/core/local_desk_data_manager.cc
+++ b/chromium/components/desks_storage/core/local_desk_data_manager.cc
@@ -15,10 +15,13 @@
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/values.h"
+#include "components/account_id/account_id.h"
#include "components/app_restore/restore_data.h"
#include "components/desks_storage/core/desk_model.h"
#include "components/desks_storage/core/desk_template_conversion.h"
#include "components/desks_storage/core/desk_template_util.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
#include "components/sync/protocol/workspace_desk_specifics.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/re2/src/re2/stringpiece.h"
@@ -29,74 +32,31 @@ namespace desks_storage {
namespace {
// Setting this to true allows us to add more than the maximum number of
-// templates. Used only for testing.
+// desk templates. Used only for testing.
bool g_disable_max_template_limit = false;
-// File extension for templates.
-constexpr char kFileExtension[] = ".template";
-// Key used in base::Value generation for the template name field.
-constexpr char kDeskTemplateNameKey[] = "template_name";
-// Key used in base::Value generation for the uuid field.
-constexpr char kDeskTemplateUuidKey[] = "uuid";
-// Key used in base::Value generation for the time created field.
-constexpr char kDeskTemplateTimeCreatedKey[] = "time_created";
-// Key used in base::Value generation for the restore data field.
-constexpr char kDeskTemplateRestoreDataKey[] = "restore_data";
-constexpr std::size_t kMaxTemplateCount = 6u;
-
-// Converts ash::DeskTemplates to base::Value for serialization.
-base::Value ConvertDeskTemplateToValue(ash::DeskTemplate* desk_template) {
- base::Value dict(base::Value::Type::DICTIONARY);
- dict.SetKey(kDeskTemplateUuidKey,
- base::Value(desk_template->uuid().AsLowercaseString()));
- dict.SetKey(kDeskTemplateNameKey,
- base::Value(desk_template->template_name()));
- dict.SetKey(kDeskTemplateTimeCreatedKey,
- base::TimeToValue(desk_template->created_time()));
- DCHECK(desk_template->desk_restore_data() != nullptr);
- dict.SetKey(kDeskTemplateRestoreDataKey,
- desk_template->desk_restore_data()->ConvertToValue());
- return dict;
-}
-
-// Converts base::Values deserialized from template files to
-// |ash::DeskTemplate|.
-std::unique_ptr<ash::DeskTemplate> ConvertValueToDeskTemplate(
- base::Value& desk_template_value) {
- absl::optional<base::Time> created_time(base::ValueToTime(
- desk_template_value.FindKey(kDeskTemplateTimeCreatedKey)));
- if (!created_time)
- return nullptr;
-
- std::string* uuid = desk_template_value.FindStringKey(kDeskTemplateUuidKey);
- if (!uuid)
- return nullptr;
-
- std::string* name = desk_template_value.FindStringKey(kDeskTemplateNameKey);
- if (!name)
- return nullptr;
-
- std::unique_ptr<ash::DeskTemplate> desk_template =
- std::make_unique<ash::DeskTemplate>(*uuid, ash::DeskTemplateSource::kUser,
- *name, created_time.value());
-
- // Full Restore will only take in std::unique_ptr as it's constructor
- // parameter from base::Value. We're not allowed to use the explicit
- // std::unique_ptr constructor so this is how we wrap the base::Value in a
- // std::unique_ptr
- std::unique_ptr<base::Value> restore_data_value_ptr =
- base::Value::ToUniquePtrValue(
- std::move(*desk_template_value.FindKey(kDeskTemplateRestoreDataKey)));
- DCHECK(restore_data_value_ptr);
-
- desk_template->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>(
- std::move(restore_data_value_ptr)));
- return desk_template;
-}
-
-// Reads a template file at fully_qualified_path| into a
-// std::unique_ptr<ash::DeskTemplate> This function returns a |nullptr| if the
+// Setting this to true allows us to exclude the max count of save and recall
+// desk entries as part of `GetMaxEntryCount` since there are some tests
+// treating save and recall desks behavior as regular desk templates (such as
+// button enablement). Also, since save and recall desks and desk templates are
+// currently being treated as desk templates, exclude save and recall desks
+// limit until save and recall desks are enabled.
+bool g_exclude_save_and_recall_desk_in_max_entry_count = true;
+
+// File extension for saving template entries.
+constexpr char kFileExtension[] = ".saveddesk";
+constexpr char kSavedDeskDirectoryName[] = "saveddesk";
+constexpr size_t kMaxDeskTemplateCount = 6u;
+// Currently, the save for later button is dependent on the the max number of
+// entries total.
+constexpr size_t kMaxSaveAndRecallDeskCount = 6u;
+
+// Set of valid desk types.
+const std::set<ash::DeskTemplateType> kDeskTypes = {
+ ash::DeskTemplateType::kTemplate, ash::DeskTemplateType::kSaveAndRecall};
+
+// Reads a file at `fully_qualified_path` into a
+// std::unique_ptr<ash::DeskTemplate> This function returns a `nullptr` if the
// file does not exist or deserialization fails.
std::unique_ptr<ash::DeskTemplate> ReadFileToTemplate(
const base::FilePath& fully_qualified_path) {
@@ -121,7 +81,8 @@ std::unique_ptr<ash::DeskTemplate> ReadFileToTemplate(
return nullptr;
}
- return ConvertValueToDeskTemplate(*desk_template_value);
+ return desk_template_conversion::ParseDeskTemplateFromSource(
+ *desk_template_value, ash::DeskTemplateSource::kUser);
}
bool EndsWith(const char* input, const char* suffix) {
@@ -132,22 +93,24 @@ bool EndsWith(const char* input, const char* suffix) {
}
return false;
}
-
+// TODO(crbug.com/1320836): Make template creation for
+// local_desk_data_manager_unittests cleaner.
bool IsValidTemplateFileName(const char* name) {
if (name == nullptr)
return false;
return EndsWith(name, kFileExtension);
}
-// Writes a DeskTemplate |entry| to a file at |path_to_template|.
-// This function utilizes blocking calls and assumes that it is being called
-// from a thread which can accept such calls, please don't call this function
-// from the UI thread.
+// Writes a DeskTemplate or SaveAndRecallDesk base::Value `json_value` to a file
+// at `path_to_template`. This function utilizes blocking calls and assumes that
+// it is being called from a thread which can accept such calls, please don't
+// call this function from the UI thread.
bool WriteTemplateFile(const base::FilePath& path_to_template,
- ash::DeskTemplate* entry) {
+ base::Value json_value) {
std::string json_string;
JSONStringValueSerializer serializer(&json_string);
- serializer.Serialize(ConvertDeskTemplateToValue(entry));
+
+ serializer.Serialize(json_value);
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
@@ -155,113 +118,281 @@ bool WriteTemplateFile(const base::FilePath& path_to_template,
return base::WriteFile(path_to_template, json_string);
}
-// Generates the fully qualified path to a template file given the |file_path|
-// to the desk template directory and the template's |uuid|.
+// Generates the fully qualified path to a desk template or save and recall desk
+// file given the `file_path` to the desk template or save and recall desk
+// directory and the entry's `uuid`.
base::FilePath GetFullyQualifiedPath(base::FilePath file_path,
const std::string& uuid) {
std::string filename(uuid);
filename.append(kFileExtension);
+
return base::FilePath(file_path.Append(base::FilePath(filename)));
}
} // namespace
-LocalDeskDataManager::LocalDeskDataManager(const base::FilePath& path)
+LocalDeskDataManager::LocalDeskDataManager(
+ const base::FilePath& user_data_dir_path,
+ const AccountId& account_id)
: task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
- local_path_(path),
- cache_status_(CacheStatus::kNotInitialized) {}
+ user_data_dir_path_(user_data_dir_path),
+ local_saved_desk_path_(
+ user_data_dir_path.AppendASCII(kSavedDeskDirectoryName)),
+ account_id_(account_id),
+ cache_status_(CacheStatus::kNotInitialized) {
+ // Populate `saved_desks_list_` with all the desk types.
+ for (const auto& desk_type : kDeskTypes) {
+ saved_desks_list_[desk_type];
+ }
+ auto entries = std::make_unique<
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>>();
+ // Load the cache.
+ task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::BindOnce(&LocalDeskDataManager::EnsureCacheIsLoaded,
+ base::Unretained(this), entries.get()),
+ base::BindOnce(&LocalDeskDataManager::MoveEntriesIntoCache,
+ weak_ptr_factory_.GetWeakPtr(), std::move(entries)));
+}
LocalDeskDataManager::~LocalDeskDataManager() = default;
void LocalDeskDataManager::GetAllEntries(
DeskModel::GetAllEntriesCallback callback) {
auto status = std::make_unique<DeskModel::GetAllEntriesStatus>();
- auto entries = std::make_unique<std::vector<ash::DeskTemplate*>>();
+ auto entries = std::make_unique<std::vector<const ash::DeskTemplate*>>();
+ if (cache_status_ != CacheStatus::kOk) {
+ *status = DeskModel::GetAllEntriesStatus::kFailure;
+ std::move(callback).Run(*status, *entries);
+ return;
+ }
+ for (const auto& it : policy_entries_)
+ entries->push_back(it.get());
+ for (auto& saved_desk : saved_desks_list_) {
+ for (auto& [uuid, template_entry] : saved_desk.second) {
+ DCHECK_EQ(uuid, template_entry->uuid());
+ entries->push_back(template_entry.get());
+ }
+ }
// It's safe to pass base::Unretained(this) since the LocalDeskDataManager is
// a long-lived object that should persist during user session.
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&LocalDeskDataManager::GetAllEntriesTask,
- base::Unretained(this), status.get(), entries.get()),
+ base::Unretained(this), status.get()),
base::BindOnce(&LocalDeskDataManager::OnGetAllEntries,
weak_ptr_factory_.GetWeakPtr(), std::move(status),
std::move(entries), std::move(callback)));
}
void LocalDeskDataManager::GetEntryByUUID(
- const std::string& uuid,
+ const std::string& uuid_str,
DeskModel::GetEntryByUuidCallback callback) {
auto status = std::make_unique<DeskModel::GetEntryByUuidStatus>();
auto entry_ptr = std::make_unique<ash::DeskTemplate*>();
+ if (cache_status_ != LocalDeskDataManager::CacheStatus::kOk) {
+ *status = DeskModel::GetEntryByUuidStatus::kFailure;
+ std::move(callback).Run(*status, nullptr);
+ return;
+ }
+
+ const base::GUID uuid = base::GUID::ParseCaseInsensitive(uuid_str);
+ if (!uuid.is_valid()) {
+ *status = DeskModel::GetEntryByUuidStatus::kInvalidUuid;
+ }
+ const ash::DeskTemplateType desk_type = GetDeskTypeOfUuid(uuid);
+
+ const auto cache_entry = saved_desks_list_[desk_type].find(uuid);
+
+ if (cache_entry != saved_desks_list_[desk_type].end()) {
+ *status = DeskModel::GetEntryByUuidStatus::kOk;
+ *entry_ptr = cache_entry->second.get();
+ } else {
+ *status = DeskModel::GetEntryByUuidStatus::kNotFound;
+ }
+
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&LocalDeskDataManager::GetEntryByUuidTask,
- base::Unretained(this), uuid, status.get(),
- entry_ptr.get()),
+ base::Unretained(this), uuid_str, status.get()),
base::BindOnce(&LocalDeskDataManager::OnGetEntryByUuid,
- weak_ptr_factory_.GetWeakPtr(), uuid, std::move(status),
- std::move(entry_ptr), std::move(callback)));
+ weak_ptr_factory_.GetWeakPtr(), uuid_str,
+ std::move(status), std::move(entry_ptr),
+ std::move(callback)));
}
void LocalDeskDataManager::AddOrUpdateEntry(
std::unique_ptr<ash::DeskTemplate> new_entry,
DeskModel::AddOrUpdateEntryCallback callback) {
auto status = std::make_unique<DeskModel::AddOrUpdateEntryStatus>();
+ if (cache_status_ != CacheStatus::kOk) {
+ *status = DeskModel::AddOrUpdateEntryStatus::kFailure;
+ std::move(callback).Run(*status);
+ return;
+ }
+
+ const ash::DeskTemplateType desk_type = new_entry->type();
+ size_t template_type_max_size = desk_type == ash::DeskTemplateType::kTemplate
+ ? kMaxDeskTemplateCount
+ : kMaxSaveAndRecallDeskCount;
+ if (!g_disable_max_template_limit &&
+ saved_desks_list_[desk_type].size() >= template_type_max_size) {
+ *status = DeskModel::AddOrUpdateEntryStatus::kHitMaximumLimit;
+ std::move(callback).Run(*status);
+ return;
+ }
+
+ base::GUID uuid = new_entry->uuid();
+ if (!uuid.is_valid()) {
+ *status = DeskModel::AddOrUpdateEntryStatus::kInvalidArgument;
+ std::move(callback).Run(*status);
+ return;
+ }
+
+ apps::AppRegistryCache* cache =
+ apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(account_id_);
+ DCHECK(cache);
+ base::Value template_base_value =
+ desk_template_conversion::SerializeDeskTemplateAsPolicy(new_entry.get(),
+ cache);
+ // Deserialize the `template_base_value` to a desk template to make sure that
+ // we can properly get the correct information now instead of during a future
+ // user operation.
+ std::unique_ptr<ash::DeskTemplate> deserialize_entry =
+ desk_template_conversion::ParseDeskTemplateFromSource(
+ template_base_value, new_entry->source());
+ bool is_update =
+ std::find_if(
+ saved_desks_list_[desk_type].begin(),
+ saved_desks_list_[desk_type].end(),
+ [&uuid](const std::pair<const base::GUID,
+ std::unique_ptr<ash::DeskTemplate>>& entry) {
+ return entry.first == uuid;
+ }) != saved_desks_list_[desk_type].end();
+ std::unique_ptr<ash::DeskTemplate> old_entry = nullptr;
+ if (is_update)
+ old_entry = saved_desks_list_[desk_type][uuid]->Clone();
+
+ saved_desks_list_[desk_type][uuid] = std::move(deserialize_entry);
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&LocalDeskDataManager::AddOrUpdateEntryTask,
- base::Unretained(this), std::move(new_entry),
- status.get()),
+ base::Unretained(this), uuid, status.get(),
+ std::move(template_base_value)),
base::BindOnce(&LocalDeskDataManager::OnAddOrUpdateEntry,
weak_ptr_factory_.GetWeakPtr(), std::move(status),
- std::move(callback)));
+ std::move(callback), is_update, desk_type, uuid,
+ std::move(old_entry)));
}
void LocalDeskDataManager::DeleteEntry(
const std::string& uuid_str,
DeskModel::DeleteEntryCallback callback) {
auto status = std::make_unique<DeskModel::DeleteEntryStatus>();
+ if (cache_status_ != CacheStatus::kOk) {
+ *status = DeskModel::DeleteEntryStatus::kFailure;
+ std::move(callback).Run(*status);
+ return;
+ }
+ const base::GUID uuid = base::GUID::ParseCaseInsensitive(uuid_str);
+ if (!uuid.is_valid()) {
+ // There does not exist an entry with invalid UUID.
+ // Therefore the deletion request is vicariously successful.
+ *status = DeskModel::DeleteEntryStatus::kOk;
+ std::move(callback).Run(*status);
+ return;
+ }
+ const ash::DeskTemplateType desk_type = GetDeskTypeOfUuid(uuid);
+ // `entry` is used to keep track of the deleted entry in case we need to
+ // rollback the deletion if the file operation fails to delete it.
+ auto entry = std::make_unique<
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>>();
+ (*entry)[uuid] = std::move(saved_desks_list_[desk_type][uuid]);
+ saved_desks_list_[desk_type].erase(uuid);
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&LocalDeskDataManager::DeleteEntryTask,
base::Unretained(this), uuid_str, status.get()),
base::BindOnce(&LocalDeskDataManager::OnDeleteEntry,
weak_ptr_factory_.GetWeakPtr(), std::move(status),
- std::move(callback)));
+ std::move(entry), std::move(callback)));
}
void LocalDeskDataManager::DeleteAllEntries(
DeskModel::DeleteEntryCallback callback) {
auto status = std::make_unique<DeskModel::DeleteEntryStatus>();
+ if (cache_status_ != CacheStatus::kOk) {
+ *status = DeskModel::DeleteEntryStatus::kFailure;
+ std::move(callback).Run(*status);
+ return;
+ }
+ // `entries` is used to keep track of any desk template entry that failed to
+ // be deleted by the file system. This is used to rollback the deletion of
+ // those fail to delete files.
+ auto entries = std::make_unique<
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>>();
+
+ // Deletes all desk templates and save and recall desks.
+ for (auto& saved_desk : saved_desks_list_) {
+ for (auto& [uuid, template_entry] : saved_desk.second) {
+ (*entries)[uuid] = std::move(template_entry);
+ }
+ saved_desk.second.clear();
+ }
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&LocalDeskDataManager::DeleteAllEntriesTask,
- base::Unretained(this), status.get()),
+ base::Unretained(this), status.get(), entries.get()),
base::BindOnce(&LocalDeskDataManager::OnDeleteEntry,
weak_ptr_factory_.GetWeakPtr(), std::move(status),
- std::move(callback)));
+ std::move(entries), std::move(callback)));
}
-std::size_t LocalDeskDataManager::GetEntryCount() const {
- return templates_.size() + policy_entries_.size();
+// TODO(crbug.com/1320805): Remove this function once both desk models support
+// desk type counts.
+size_t LocalDeskDataManager::GetEntryCount() const {
+ return GetSaveAndRecallDeskEntryCount() + GetDeskTemplateEntryCount();
}
-std::size_t LocalDeskDataManager::GetMaxEntryCount() const {
- return kMaxTemplateCount + policy_entries_.size();
+size_t LocalDeskDataManager::GetSaveAndRecallDeskEntryCount() const {
+ return saved_desks_list_.at(ash::DeskTemplateType::kSaveAndRecall).size();
+}
+
+size_t LocalDeskDataManager::GetDeskTemplateEntryCount() const {
+ return saved_desks_list_.at(ash::DeskTemplateType::kTemplate).size() +
+ policy_entries_.size();
+}
+
+size_t LocalDeskDataManager::GetMaxEntryCount() const {
+ return kMaxDeskTemplateCount +
+ (!g_exclude_save_and_recall_desk_in_max_entry_count
+ ? kMaxSaveAndRecallDeskCount
+ : 0u) +
+ policy_entries_.size();
+}
+
+size_t LocalDeskDataManager::GetMaxSaveAndRecallDeskEntryCount() const {
+ return kMaxSaveAndRecallDeskCount;
+}
+
+size_t LocalDeskDataManager::GetMaxDeskTemplateEntryCount() const {
+ return kMaxDeskTemplateCount + policy_entries_.size();
}
std::vector<base::GUID> LocalDeskDataManager::GetAllEntryUuids() const {
std::vector<base::GUID> keys;
- for (const auto& it : templates_) {
- DCHECK_EQ(it.first, it.second->uuid());
- keys.emplace_back(it.first);
+ for (const auto& save_desks : saved_desks_list_) {
+ for (const auto& [uuid, template_entry] : save_desks.second) {
+ DCHECK_EQ(uuid, template_entry->uuid());
+ keys.emplace_back(uuid);
+ }
}
return keys;
}
@@ -275,37 +406,64 @@ bool LocalDeskDataManager::IsSyncing() const {
return false;
}
+ash::DeskTemplate* LocalDeskDataManager::FindOtherEntryWithName(
+ const std::u16string& name,
+ ash::DeskTemplateType type,
+ const base::GUID& uuid) const {
+ return desk_template_util::FindOtherEntryWithName(name, uuid,
+ saved_desks_list_.at(type));
+}
+
// static
void LocalDeskDataManager::SetDisableMaxTemplateLimitForTesting(bool disabled) {
g_disable_max_template_limit = disabled;
}
-void LocalDeskDataManager::EnsureCacheIsLoaded() {
- if (cache_status_ == CacheStatus::kOk) {
- // Cache is already loaded.
- return;
- }
+// static
+void LocalDeskDataManager::SetExcludeSaveAndRecallDeskInMaxEntryCountForTesting(
+ bool exclude) {
+ g_exclude_save_and_recall_desk_in_max_entry_count = exclude;
+}
+void LocalDeskDataManager::EnsureCacheIsLoaded(
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>* entries_ptr) {
+ // Cache is already loaded. Do nothing.
+ if (cache_status_ == CacheStatus::kOk)
+ return;
+ base::DirReaderPosix user_data_dir_reader(
+ user_data_dir_path_.AsUTF8Unsafe().c_str());
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
- base::DirReaderPosix dir_reader(local_path_.AsUTF8Unsafe().c_str());
- if (!dir_reader.IsValid()) {
- // Template directory path is invalid. This local storage cannot load any
+ if (!user_data_dir_reader.IsValid()) {
+ // User data directory path is invalid. This local storage cannot load any
// templates from disk.
cache_status_ = CacheStatus::kInvalidPath;
return;
}
+ // Set dir_reader to read from the `local_saved_desk_path_` directory.
+ // check to make sure there is a `local_saved_desk_path_` directory. If not
+ // create it.
+ bool dir_create_success = base::CreateDirectory(local_saved_desk_path_);
+ base::DirReaderPosix dir_reader(
+ local_saved_desk_path_.AsUTF8Unsafe().c_str());
+
+ if (!dir_create_success || !dir_reader.IsValid()) {
+ // Failed to find or create the `local_saved_desk_path_` directory path.
+ // This local storage cannot load any entry of `type` from disk.
+ cache_status_ = CacheStatus::kInvalidPath;
+ return;
+ }
+
while (dir_reader.Next()) {
if (!IsValidTemplateFileName(dir_reader.name())) {
continue;
}
- std::unique_ptr<ash::DeskTemplate> desk_template =
- ReadFileToTemplate(local_path_.Append(dir_reader.name()));
- if (desk_template) {
- const base::GUID uuid = desk_template->uuid();
- templates_[uuid] = std::move(desk_template);
+ std::unique_ptr<ash::DeskTemplate> entry =
+ ReadFileToTemplate(local_saved_desk_path_.Append(dir_reader.name()));
+ if (entry) {
+ (*entries_ptr)[entry->uuid()] = std::move(entry);
}
}
@@ -313,61 +471,37 @@ void LocalDeskDataManager::EnsureCacheIsLoaded() {
}
void LocalDeskDataManager::GetAllEntriesTask(
- DeskModel::GetAllEntriesStatus* status_ptr,
- std::vector<ash::DeskTemplate*>* entries_ptr) {
- EnsureCacheIsLoaded();
+ DeskModel::GetAllEntriesStatus* out_status_ptr) {
if (cache_status_ == CacheStatus::kInvalidPath) {
- *status_ptr = DeskModel::GetAllEntriesStatus::kFailure;
- return;
+ *out_status_ptr = DeskModel::GetAllEntriesStatus::kFailure;
}
-
- for (const auto& it : policy_entries_)
- entries_ptr->push_back(it.get());
-
- for (auto& it : templates_) {
- DCHECK_EQ(it.first, it.second->uuid());
- entries_ptr->push_back(it.second.get());
- }
-
if (cache_status_ == CacheStatus::kOk) {
- *status_ptr = DeskModel::GetAllEntriesStatus::kOk;
+ *out_status_ptr = DeskModel::GetAllEntriesStatus::kOk;
} else {
- *status_ptr = DeskModel::GetAllEntriesStatus::kPartialFailure;
+ *out_status_ptr = DeskModel::GetAllEntriesStatus::kPartialFailure;
}
}
void LocalDeskDataManager::OnGetAllEntries(
std::unique_ptr<DeskModel::GetAllEntriesStatus> status_ptr,
- std::unique_ptr<std::vector<ash::DeskTemplate*>> entries_ptr,
+ std::unique_ptr<std::vector<const ash::DeskTemplate*>> entries_ptr,
DeskModel::GetAllEntriesCallback callback) {
std::move(callback).Run(*status_ptr, *entries_ptr);
}
void LocalDeskDataManager::GetEntryByUuidTask(
const std::string& uuid_str,
- DeskModel::GetEntryByUuidStatus* status_ptr,
- ash::DeskTemplate** entry_ptr_ptr) {
- EnsureCacheIsLoaded();
-
- if (cache_status_ == CacheStatus::kInvalidPath) {
- *status_ptr = DeskModel::GetEntryByUuidStatus::kFailure;
+ DeskModel::GetEntryByUuidStatus* out_status_ptr) {
+ if (cache_status_ == LocalDeskDataManager::CacheStatus::kInvalidPath) {
+ *out_status_ptr = DeskModel::GetEntryByUuidStatus::kFailure;
return;
}
const base::GUID uuid = base::GUID::ParseCaseInsensitive(uuid_str);
if (!uuid.is_valid()) {
- *status_ptr = DeskModel::GetEntryByUuidStatus::kInvalidUuid;
+ *out_status_ptr = DeskModel::GetEntryByUuidStatus::kInvalidUuid;
return;
}
-
- const auto cache_entry = templates_.find(uuid);
-
- if (cache_entry != templates_.end()) {
- *status_ptr = DeskModel::GetEntryByUuidStatus::kOk;
- *entry_ptr_ptr = cache_entry->second.get();
- } else {
- *status_ptr = DeskModel::GetEntryByUuidStatus::kNotFound;
- }
}
void LocalDeskDataManager::OnGetEntryByUuid(
@@ -391,132 +525,125 @@ void LocalDeskDataManager::OnGetEntryByUuid(
}
void LocalDeskDataManager::AddOrUpdateEntryTask(
- std::unique_ptr<ash::DeskTemplate> new_entry,
- DeskModel::AddOrUpdateEntryStatus* status_ptr) {
- EnsureCacheIsLoaded();
- if (cache_status_ == CacheStatus::kInvalidPath) {
- *status_ptr = DeskModel::AddOrUpdateEntryStatus::kFailure;
- return;
- }
-
- if (!g_disable_max_template_limit && templates_.size() >= kMaxTemplateCount) {
- *status_ptr = DeskModel::AddOrUpdateEntryStatus::kHitMaximumLimit;
- return;
- }
-
- if (!new_entry->uuid().is_valid()) {
- *status_ptr = DeskModel::AddOrUpdateEntryStatus::kInvalidArgument;
- return;
- }
-
- base::GUID uuid = new_entry->uuid();
-
- // While we still find duplicate names iterate the duplicate number. i.e.
- // if there are 4 duplicates of some template name then this iterates until
- // the current template will be named 5.
- while (HasTemplateWithName(new_entry->template_name())) {
- new_entry->set_template_name(
- desk_template_util::AppendDuplicateNumberToDuplicateName(
- new_entry->template_name()));
- }
-
- templates_[uuid] = std::move(new_entry);
-
+ const base::GUID uuid,
+ DeskModel::AddOrUpdateEntryStatus* out_status_ptr,
+ base::Value entry_base_value) {
const base::FilePath fully_qualified_path =
- GetFullyQualifiedPath(local_path_, uuid.AsLowercaseString());
-
- if (WriteTemplateFile(fully_qualified_path, templates_[uuid].get())) {
- *status_ptr = DeskModel::AddOrUpdateEntryStatus::kOk;
+ GetFullyQualifiedPath(local_saved_desk_path_, uuid.AsLowercaseString());
+ if (WriteTemplateFile(fully_qualified_path, std::move(entry_base_value))) {
+ *out_status_ptr = DeskModel::AddOrUpdateEntryStatus::kOk;
} else {
- *status_ptr = DeskModel::AddOrUpdateEntryStatus::kFailure;
+ *out_status_ptr = DeskModel::AddOrUpdateEntryStatus::kFailure;
}
}
void LocalDeskDataManager::OnAddOrUpdateEntry(
std::unique_ptr<DeskModel::AddOrUpdateEntryStatus> status_ptr,
- DeskModel::AddOrUpdateEntryCallback callback) {
+ DeskModel::AddOrUpdateEntryCallback callback,
+ bool is_update,
+ ash::DeskTemplateType desk_type,
+ const base::GUID uuid,
+ std::unique_ptr<ash::DeskTemplate> entry) {
+ // Rollback the template addition to the cache if there's a failure.
+ if (*status_ptr == DeskModel::AddOrUpdateEntryStatus::kFailure) {
+ if (is_update) {
+ saved_desks_list_[desk_type][uuid] = std::move(entry);
+ } else {
+ saved_desks_list_[desk_type].erase(uuid);
+ }
+ }
std::move(callback).Run(*status_ptr);
}
void LocalDeskDataManager::DeleteEntryTask(
const std::string& uuid_str,
- DeskModel::DeleteEntryStatus* status_ptr) {
- EnsureCacheIsLoaded();
- if (cache_status_ == LocalDeskDataManager::CacheStatus::kInvalidPath) {
- *status_ptr = DeskModel::DeleteEntryStatus::kFailure;
- return;
- }
-
- const base::GUID uuid = base::GUID::ParseCaseInsensitive(uuid_str);
- if (!uuid.is_valid()) {
- // There does not exist a desk template with invalid UUID.
- // Therefore the deletion request is vicariously successful.
- *status_ptr = DeskModel::DeleteEntryStatus::kOk;
- return;
- }
-
- templates_.erase(uuid);
-
+ DeskModel::DeleteEntryStatus* out_status_ptr) {
const base::FilePath fully_qualified_path =
- GetFullyQualifiedPath(local_path_, uuid_str);
+ GetFullyQualifiedPath(local_saved_desk_path_, uuid_str);
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
if (base::DeleteFile(fully_qualified_path)) {
- *status_ptr = DeskModel::DeleteEntryStatus::kOk;
+ *out_status_ptr = DeskModel::DeleteEntryStatus::kOk;
} else {
- *status_ptr = DeskModel::DeleteEntryStatus::kFailure;
+ *out_status_ptr = DeskModel::DeleteEntryStatus::kFailure;
}
}
void LocalDeskDataManager::DeleteAllEntriesTask(
- DeskModel::DeleteEntryStatus* status_ptr) {
- EnsureCacheIsLoaded();
- if (cache_status_ == CacheStatus::kInvalidPath) {
- *status_ptr = DeskModel::DeleteEntryStatus::kFailure;
- return;
- }
-
- templates_.clear();
-
- base::DirReaderPosix dir_reader(local_path_.AsUTF8Unsafe().c_str());
-
+ DeskModel::DeleteEntryStatus* out_status_ptr,
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>* out_entries_ptr) {
+ base::DirReaderPosix dir_reader(
+ local_saved_desk_path_.AsUTF8Unsafe().c_str());
if (!dir_reader.IsValid()) {
- *status_ptr = DeskModel::DeleteEntryStatus::kFailure;
+ *out_status_ptr = DeskModel::DeleteEntryStatus::kFailure;
return;
}
-
DeskModel::DeleteEntryStatus overall_delete_successes =
DeskModel::DeleteEntryStatus::kOk;
+
while (dir_reader.Next()) {
- if (!IsValidTemplateFileName(dir_reader.name())) {
+ if (!IsValidTemplateFileName(dir_reader.name()))
continue;
- }
- base::FilePath fully_qualified_path(local_path_.Append(dir_reader.name()));
+ base::FilePath fully_qualified_path(
+ local_saved_desk_path_.Append(dir_reader.name()));
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
+ // Keep a copy of the entry in memory. If the delete fails, get the uuid of
+ // the failed deleted entry so it can be rolled back later.
+ std::unique_ptr<ash::DeskTemplate> entry =
+ ReadFileToTemplate(fully_qualified_path);
bool delete_success = base::DeleteFile(fully_qualified_path);
- if ((overall_delete_successes == DeskModel::DeleteEntryStatus::kOk) &&
- !delete_success)
+ if (!delete_success) {
overall_delete_successes = DeskModel::DeleteEntryStatus::kFailure;
+ } else {
+ // File deleted successfully, remove the backup of the deleted file.
+ if (entry) {
+ (*out_entries_ptr).erase(entry->uuid());
+ }
+ }
}
-
- *status_ptr = overall_delete_successes;
+ *out_status_ptr = overall_delete_successes;
}
void LocalDeskDataManager::OnDeleteEntry(
std::unique_ptr<DeskModel::DeleteEntryStatus> status_ptr,
+ std::unique_ptr<std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>>
+ entries_ptr,
DeskModel::DeleteEntryCallback callback) {
+ // Rollback deletes from the cache for the failed file deletes.
+ if (*status_ptr == DeskModel::DeleteEntryStatus::kFailure) {
+ MoveEntriesIntoCache(std::move(entries_ptr));
+ }
std::move(callback).Run(*status_ptr);
}
-bool LocalDeskDataManager::HasTemplateWithName(const std::u16string& name) {
- return std::find_if(
- templates_.begin(), templates_.end(),
- [&name](std::pair<const base::GUID,
- std::unique_ptr<ash::DeskTemplate>>& entry) {
- return entry.second->template_name() == name;
- }) != templates_.end();
+ash::DeskTemplateType LocalDeskDataManager::GetDeskTypeOfUuid(
+ const base::GUID uuid) const {
+ for (const auto& [desk_type, saved_desk] : saved_desks_list_) {
+ bool found_uuid =
+ std::find_if(
+ saved_desk.begin(), saved_desk.end(),
+ [&uuid](
+ const std::pair<const base::GUID,
+ std::unique_ptr<ash::DeskTemplate>>& entry) {
+ return entry.first == uuid;
+ }) != saved_desk.end();
+ if (found_uuid)
+ return desk_type;
+ }
+ return ash::DeskTemplateType::kTemplate;
+}
+
+void LocalDeskDataManager::MoveEntriesIntoCache(
+ std::unique_ptr<std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>>
+ entries_ptr) {
+ for (auto& [uuid, template_entry] : *entries_ptr) {
+ if (template_entry) {
+ saved_desks_list_[template_entry->type()][uuid] =
+ std::move(template_entry);
+ }
+ }
}
} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/local_desk_data_manager.h b/chromium/components/desks_storage/core/local_desk_data_manager.h
index 041957dbcf5..d9ab73f0457 100644
--- a/chromium/components/desks_storage/core/local_desk_data_manager.h
+++ b/chromium/components/desks_storage/core/local_desk_data_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,22 +8,23 @@
#include <map>
#include <memory>
+#include "ash/public/cpp/desk_template.h"
#include "base/files/file_path.h"
#include "base/guid.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner_helpers.h"
+#include "components/account_id/account_id.h"
#include "components/desks_storage/core/desk_model.h"
namespace ash {
-class DeskTemplate;
class OverviewTestBase;
} // namespace ash
namespace desks_storage {
// The LocalDeskDataManager is the local storage implementation of
// the DeskModel interface and handles storage operations for local
-// desk templates.
+// desk templates and save and recall desks.
//
// TODO(crbug.com/1227215): add calls to DeskModelObserver
class LocalDeskDataManager : public DeskModel {
@@ -44,7 +45,8 @@ class LocalDeskDataManager : public DeskModel {
kInvalidPath,
};
- explicit LocalDeskDataManager(const base::FilePath& path);
+ LocalDeskDataManager(const base::FilePath& user_data_dir_path,
+ const AccountId& account_id);
LocalDeskDataManager(const LocalDeskDataManager&) = delete;
LocalDeskDataManager& operator=(const LocalDeskDataManager&) = delete;
@@ -52,7 +54,7 @@ class LocalDeskDataManager : public DeskModel {
// DeskModel:
void GetAllEntries(GetAllEntriesCallback callback) override;
- void GetEntryByUUID(const std::string& uuid,
+ void GetEntryByUUID(const std::string& uuid_str,
GetEntryByUuidCallback callback) override;
void AddOrUpdateEntry(std::unique_ptr<ash::DeskTemplate> new_entry,
AddOrUpdateEntryCallback callback) override;
@@ -61,33 +63,37 @@ class LocalDeskDataManager : public DeskModel {
void DeleteAllEntries(DeleteEntryCallback callback) override;
std::size_t GetEntryCount() const override;
std::size_t GetMaxEntryCount() const override;
+ std::size_t GetSaveAndRecallDeskEntryCount() const override;
+ std::size_t GetDeskTemplateEntryCount() const override;
+ std::size_t GetMaxSaveAndRecallDeskEntryCount() const override;
+ std::size_t GetMaxDeskTemplateEntryCount() const override;
std::vector<base::GUID> GetAllEntryUuids() const override;
bool IsReady() const override;
bool IsSyncing() const override;
+ ash::DeskTemplate* FindOtherEntryWithName(
+ const std::u16string& name,
+ ash::DeskTemplateType type,
+ const base::GUID& uuid) const override;
static void SetDisableMaxTemplateLimitForTesting(bool disabled);
+ static void SetExcludeSaveAndRecallDeskInMaxEntryCountForTesting(
+ bool disabled);
private:
friend class ash::OverviewTestBase;
- // Loads desk templates from |local_path_| into cache if the cache is not
+ // Loads templates from `local_saved_desk_path_` into the
+ // `saved_desks_list_`, based on the template's desk type, if the cache is not
// loaded yet.
- void EnsureCacheIsLoaded();
+ void EnsureCacheIsLoaded(
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>* entries_ptr);
- // Gets all desk templates from user's local template directory.
- void GetAllEntriesTask(DeskModel::GetAllEntriesStatus* status_ptr,
- std::vector<ash::DeskTemplate*>* entries_ptr);
+ // Gets all entries from user's `local_saved_desk_path_`.
+ void GetAllEntriesTask(DeskModel::GetAllEntriesStatus* status_ptr);
- // Wrapper method to call GetAllEntriesCallback.
- void OnGetAllEntries(
- std::unique_ptr<DeskModel::GetAllEntriesStatus> status_ptr,
- std::unique_ptr<std::vector<ash::DeskTemplate*>> entries_ptr,
- DeskModel::GetAllEntriesCallback callback);
-
- // Get a specific desk template by |uuid_str|.
+ // Get a specific entry by `uuid_str`.
void GetEntryByUuidTask(const std::string& uuid_str,
- DeskModel::GetEntryByUuidStatus* status_ptr,
- ash::DeskTemplate** entry_ptr_ptr);
+ DeskModel::GetEntryByUuidStatus* status_ptr);
// Wrapper method to call GetEntryByUuidCallback.
void OnGetEntryByUuid(
@@ -96,45 +102,73 @@ class LocalDeskDataManager : public DeskModel {
std::unique_ptr<ash::DeskTemplate*> entry_ptr_ptr,
DeskModel::GetEntryByUuidCallback callback);
- // Add or update a desk template by |new_entry|'s UUID.
- void AddOrUpdateEntryTask(std::unique_ptr<ash::DeskTemplate> new_entry,
- DeskModel::AddOrUpdateEntryStatus* status_ptr);
+ // Wrapper method to call GetAllEntriesCallback.
+ void OnGetAllEntries(
+ std::unique_ptr<DeskModel::GetAllEntriesStatus> status_ptr,
+ std::unique_ptr<std::vector<const ash::DeskTemplate*>> entries_ptr,
+ DeskModel::GetAllEntriesCallback callback);
+
+ // Add or update an entry by `new_entry`'s UUID.
+ void AddOrUpdateEntryTask(const base::GUID uuid,
+ DeskModel::AddOrUpdateEntryStatus* status_ptr,
+ base::Value entry_base_value);
// Wrapper method to call AddOrUpdateEntryCallback.
void OnAddOrUpdateEntry(
std::unique_ptr<DeskModel::AddOrUpdateEntryStatus> status_ptr,
- DeskModel::AddOrUpdateEntryCallback callback);
+ DeskModel::AddOrUpdateEntryCallback callback,
+ bool is_update,
+ ash::DeskTemplateType desk_type,
+ const base::GUID uuid,
+ std::unique_ptr<ash::DeskTemplate> entry);
- // Remove entry with |uuid_str|. If the entry with |uuid_str| does not
+ // Remove entry with `uuid_str`. If the entry with `uuid_str` does not
// exist, then the deletion is considered a success.
void DeleteEntryTask(const std::string& uuid_str,
DeskModel::DeleteEntryStatus* status_ptr);
// Delete all entries.
- void DeleteAllEntriesTask(DeskModel::DeleteEntryStatus* status_ptr);
+ void DeleteAllEntriesTask(
+ DeskModel::DeleteEntryStatus* status_ptr,
+ std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>* entries_ptr);
// Wrapper method to call DeleteEntryCallback.
- void OnDeleteEntry(std::unique_ptr<DeskModel::DeleteEntryStatus> status_ptr,
- DeskModel::DeleteEntryCallback callback);
+ void OnDeleteEntry(
+ std::unique_ptr<DeskModel::DeleteEntryStatus> status_ptr,
+ std::unique_ptr<std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>>
+ entries_ptr,
+ DeskModel::DeleteEntryCallback callback);
+
+ // Returns the desk type of the `uuid`.
+ ash::DeskTemplateType GetDeskTypeOfUuid(const base::GUID uuid) const;
- // Returns true if |templates_| contains a desk template with |name|.
- bool HasTemplateWithName(const std::u16string& name);
+ // Wrapper method to load the read files into the `saved_desks_list_` cache.
+ void MoveEntriesIntoCache(
+ std::unique_ptr<std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>>
+ entries_ptr);
// Task runner used to schedule tasks on the IO thread.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
- // File path to the template subdirection in user data directory's:
- // e.g. "/path/to/user/data/dir/templates".
- const base::FilePath local_path_;
+ // File path to the user data directory's: e.g.
+ // "/path/to/user/data/dir/".
+ const base::FilePath user_data_dir_path_;
- // In-memory desk template cache that owns desk_templates so that these desk
- // templates can be retrieved by GetAllEntries.
- // |templates_| is keyed by UUIDs.
- std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>> templates_;
+ // File path to the saveddesks template subdirectory in user data directory's:
+ // e.g. "/path/to/user/data/dir/saveddesk".
+ const base::FilePath local_saved_desk_path_;
- // Flag indicating the status of this in memory cache.
+ // Account ID of the user this class will cache app data for.
+ const AccountId account_id_;
+
+ // Cache status of the templates cache for both desk types.
CacheStatus cache_status_;
+ using SavedDesks = std::map<base::GUID, std::unique_ptr<ash::DeskTemplate>>;
+
+ // In memory cache of saved desks based on their type.
+ std::map<ash::DeskTemplateType, SavedDesks> saved_desks_list_;
+
// Weak pointer factory for posting tasks to task runner.
base::WeakPtrFactory<LocalDeskDataManager> weak_ptr_factory_{this};
};
diff --git a/chromium/components/desks_storage/core/local_desks_data_manager_unittests.cc b/chromium/components/desks_storage/core/local_desks_data_manager_unittests.cc
index 8a60ba00ed5..69d23b227cf 100644
--- a/chromium/components/desks_storage/core/local_desks_data_manager_unittests.cc
+++ b/chromium/components/desks_storage/core/local_desks_data_manager_unittests.cc
@@ -19,6 +19,13 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
+#include "components/account_id/account_id.h"
+#include "components/app_constants/constants.h"
+#include "components/desks_storage/core/desk_template_util.h"
+#include "components/desks_storage/core/desk_test_util.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
+#include "components/services/app_service/public/cpp/app_types.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace desks_storage {
@@ -28,13 +35,10 @@ namespace {
constexpr char kJunkData[] = "This is not valid template data.";
constexpr char kTemplateFileNameFormat[] = "%s.template";
constexpr char kUuidFormat[] = "1c186d5a-502e-49ce-9ee1-00000000000%d";
+constexpr char kSaveAndRecallDeskUuidFormat[] =
+ "1c186d5a-502e-49ce-9ee1-0000000000%d";
constexpr char kTemplateNameFormat[] = "desk_%d";
-constexpr char kDeskOneTemplateDuplicateExpectedName[] = "desk_01 (1)";
-constexpr char kDeskOneTemplateDuplicateTwoExpectedName[] = "desk_01 (2)";
-constexpr char kDuplicatePatternMatchingNamedDeskExpectedNameOne[] =
- "(1) desk_template (1)";
-constexpr char kDuplicatePatternMatchingNamedDeskExpectedNameTwo[] =
- "(1) desk_template (2)";
+constexpr uint32_t kThreadSafeIterations = 1000;
const base::FilePath kInvalidFilePath = base::FilePath("?");
const std::string kTestUuid1 = base::StringPrintf(kUuidFormat, 1);
@@ -46,6 +50,13 @@ const std::string kTestUuid6 = base::StringPrintf(kUuidFormat, 6);
const std::string kTestUuid7 = base::StringPrintf(kUuidFormat, 7);
const std::string kTestUuid8 = base::StringPrintf(kUuidFormat, 8);
const std::string kTestUuid9 = base::StringPrintf(kUuidFormat, 9);
+const std::string kTestSaveAndRecallDeskUuid1 =
+ base::StringPrintf(kSaveAndRecallDeskUuidFormat, 10);
+const std::string kTestSaveAndRecallDeskUuid2 =
+ base::StringPrintf(kSaveAndRecallDeskUuidFormat, 11);
+const std::string kTestSaveAndRecallDeskUuid3 =
+ base::StringPrintf(kSaveAndRecallDeskUuidFormat, 12);
+
const base::Time kTestTime1 = base::Time();
const std::string kTestFileName1 =
base::StringPrintf(kTemplateFileNameFormat, kTestUuid1.c_str());
@@ -64,8 +75,9 @@ const std::string kPolicyWithOneTemplate =
// Search |entry_list| for |entry_query| as a uuid and returns true if
// found, false if not.
-bool FindUuidInUuidList(const std::string& uuid_query,
- const std::vector<ash::DeskTemplate*>& entry_list) {
+bool FindUuidInUuidList(
+ const std::string& uuid_query,
+ const std::vector<const ash::DeskTemplate*>& entry_list) {
base::GUID guid = base::GUID::ParseCaseInsensitive(uuid_query);
DCHECK(guid.is_valid());
@@ -77,24 +89,21 @@ bool FindUuidInUuidList(const std::string& uuid_query,
return false;
}
-// Takes in a vector of DeskTemplate pointers and a uuid, returns a pointer to
-// the DeskTemplate with matching uuid if found in vector, nullptr if not.
-ash::DeskTemplate* FindEntryInEntryList(
- const std::string& uuid_string,
- const std::vector<ash::DeskTemplate*>& entries) {
- base::GUID uuid = base::GUID::ParseLowercase(uuid_string);
- auto found_entry = std::find_if(
- entries.begin(), entries.end(),
- [&uuid](ash::DeskTemplate* entry) { return uuid == entry->uuid(); });
-
- return found_entry != entries.end() ? *found_entry : nullptr;
-}
-
// Verifies that the status passed into it is kOk
void VerifyEntryAddedCorrectly(DeskModel::AddOrUpdateEntryStatus status) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
}
+// Verifies that the status passed into it is kOk
+void VerifyEntryDeletedCorrectly(DeskModel::DeleteEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
+}
+
+// Verifies that the status passed into it is kFailure
+void VerifyEntryAddedFailure(DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kFailure);
+}
+
void VerifyEntryAddedErrorHitMaximumLimit(
DeskModel::AddOrUpdateEntryStatus status) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kHitMaximumLimit);
@@ -108,94 +117,113 @@ void WriteJunkData(const base::FilePath& temp_dir) {
EXPECT_TRUE(base::WriteFile(full_path, kJunkData));
}
-// Make test desk template with ID containing the index.
-std::unique_ptr<ash::DeskTemplate> MakeTestDeskTemplate(int index) {
+// Make test template with ID containing the index. Defaults to desk template
+// type if a type is not specified.
+std::unique_ptr<ash::DeskTemplate> MakeTestDeskTemplate(
+ int index,
+ ash::DeskTemplateType type) {
const std::string template_uuid = base::StringPrintf(kUuidFormat, index);
const std::string template_name =
base::StringPrintf(kTemplateNameFormat, index);
std::unique_ptr<ash::DeskTemplate> desk_template =
- std::make_unique<ash::DeskTemplate>(template_uuid,
- ash::DeskTemplateSource::kUser,
- template_name, base::Time::Now());
+ std::make_unique<ash::DeskTemplate>(
+ template_uuid, ash::DeskTemplateSource::kUser, template_name,
+ base::Time::Now(), type);
desk_template->set_desk_restore_data(
std::make_unique<app_restore::RestoreData>());
return desk_template;
}
+// Make test template with default restore data.
+std::unique_ptr<ash::DeskTemplate> MakeTestDeskTemplate(
+ const std::string& uuid,
+ ash::DeskTemplateSource source,
+ const std::string& name,
+ const base::Time created_time) {
+ auto entry = std::make_unique<ash::DeskTemplate>(
+ uuid, source, name, created_time, ash::DeskTemplateType::kTemplate);
+ entry->set_desk_restore_data(std::make_unique<app_restore::RestoreData>());
+ return entry;
+}
+
+// Make test save and recall desk with default restore data.
+std::unique_ptr<ash::DeskTemplate> MakeTestSaveAndRecallDesk(
+ const std::string& uuid,
+ const std::string& name,
+ const base::Time created_time) {
+ auto entry = std::make_unique<ash::DeskTemplate>(
+ uuid, ash::DeskTemplateSource::kUser, name, created_time,
+ ash::DeskTemplateType::kSaveAndRecall);
+ entry->set_desk_restore_data(std::make_unique<app_restore::RestoreData>());
+ return entry;
+}
} // namespace
+// TODO(crbug:1320940): Clean up tests to move on from std::string.
class LocalDeskDataManagerTest : public testing::Test {
public:
LocalDeskDataManagerTest()
: sample_desk_template_one_(
- std::make_unique<ash::DeskTemplate>(kTestUuid1,
- ash::DeskTemplateSource::kUser,
- std::string("desk_01"),
- kTestTime1)),
+ MakeTestDeskTemplate(kTestUuid1,
+ ash::DeskTemplateSource::kUser,
+ std::string("desk_01"),
+ kTestTime1)),
sample_desk_template_one_duplicate_(
- std::make_unique<ash::DeskTemplate>(kTestUuid5,
- ash::DeskTemplateSource::kUser,
- std::string("desk_01"),
- base::Time::Now())),
+ MakeTestDeskTemplate(kTestUuid5,
+ ash::DeskTemplateSource::kUser,
+ std::string("desk_01"),
+ base::Time::Now())),
sample_desk_template_one_duplicate_two_(
- std::make_unique<ash::DeskTemplate>(kTestUuid6,
- ash::DeskTemplateSource::kUser,
- std::string("desk_01"),
- base::Time::Now())),
+ MakeTestDeskTemplate(kTestUuid6,
+ ash::DeskTemplateSource::kUser,
+ std::string("desk_01"),
+ base::Time::Now())),
duplicate_pattern_matching_named_desk_(
- std::make_unique<ash::DeskTemplate>(
- kTestUuid7,
- ash::DeskTemplateSource::kUser,
- std::string("(1) desk_template"),
- base::Time::Now())),
+ MakeTestDeskTemplate(kTestUuid7,
+ ash::DeskTemplateSource::kUser,
+ std::string("(1) desk_template"),
+ base::Time::Now())),
duplicate_pattern_matching_named_desk_two_(
- std::make_unique<ash::DeskTemplate>(
- kTestUuid8,
- ash::DeskTemplateSource::kUser,
- std::string("(1) desk_template"),
- base::Time::Now())),
+ MakeTestDeskTemplate(kTestUuid8,
+ ash::DeskTemplateSource::kUser,
+ std::string("(1) desk_template"),
+ base::Time::Now())),
duplicate_pattern_matching_named_desk_three_(
- std::make_unique<ash::DeskTemplate>(
- kTestUuid9,
- ash::DeskTemplateSource::kUser,
- std::string("(1) desk_template"),
- base::Time::Now())),
+ MakeTestDeskTemplate(kTestUuid9,
+ ash::DeskTemplateSource::kUser,
+ std::string("(1) desk_template"),
+ base::Time::Now())),
sample_desk_template_two_(
- std::make_unique<ash::DeskTemplate>(kTestUuid2,
- ash::DeskTemplateSource::kUser,
- std::string("desk_02"),
- base::Time::Now())),
+ MakeTestDeskTemplate(kTestUuid2,
+ ash::DeskTemplateSource::kUser,
+ std::string("desk_02"),
+ base::Time::Now())),
sample_desk_template_three_(
- std::make_unique<ash::DeskTemplate>(kTestUuid3,
- ash::DeskTemplateSource::kUser,
- std::string("desk_03"),
- base::Time::Now())),
+ MakeTestDeskTemplate(kTestUuid3,
+ ash::DeskTemplateSource::kUser,
+ std::string("desk_03"),
+ base::Time::Now())),
+ sample_save_and_recall_desk_one_(
+ MakeTestSaveAndRecallDesk(kTestSaveAndRecallDeskUuid1,
+ "save_and_recall_desk_01",
+ kTestTime1)),
+ sample_save_and_recall_desk_two_(
+ MakeTestSaveAndRecallDesk(kTestSaveAndRecallDeskUuid2,
+ "save_and_recall_desk_02",
+ base::Time::Now())),
+ sample_save_and_recall_desk_three_(
+ MakeTestSaveAndRecallDesk(kTestSaveAndRecallDeskUuid3,
+ "save_and_recall_desk_03",
+ base::Time::Now())),
modified_sample_desk_template_one_(
- std::make_unique<ash::DeskTemplate>(kTestUuid1,
- ash::DeskTemplateSource::kUser,
- std::string("desk_01_mod"),
- kTestTime1)),
+ MakeTestDeskTemplate(kTestUuid1,
+ ash::DeskTemplateSource::kUser,
+ std::string("desk_01_mod"),
+ kTestTime1)),
task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
- data_manager_(std::unique_ptr<LocalDeskDataManager>()) {
- sample_desk_template_one_->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>());
- sample_desk_template_one_duplicate_->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>());
- sample_desk_template_one_duplicate_two_->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>());
- duplicate_pattern_matching_named_desk_->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>());
- duplicate_pattern_matching_named_desk_two_->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>());
- duplicate_pattern_matching_named_desk_three_->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>());
- sample_desk_template_two_->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>());
- sample_desk_template_three_->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>());
- modified_sample_desk_template_one_->set_desk_restore_data(
- std::make_unique<app_restore::RestoreData>());
- }
+ cache_(std::make_unique<apps::AppRegistryCache>()),
+ account_id_(AccountId::FromUserEmail("test@gmail.com")),
+ data_manager_(std::unique_ptr<LocalDeskDataManager>()) {}
LocalDeskDataManagerTest(const LocalDeskDataManagerTest&) = delete;
LocalDeskDataManagerTest& operator=(const LocalDeskDataManagerTest&) = delete;
@@ -204,7 +232,11 @@ class LocalDeskDataManagerTest : public testing::Test {
void SetUp() override {
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
- data_manager_ = std::make_unique<LocalDeskDataManager>(temp_dir_.GetPath());
+ data_manager_ = std::make_unique<LocalDeskDataManager>(temp_dir_.GetPath(),
+ account_id_);
+ data_manager_->SetExcludeSaveAndRecallDeskInMaxEntryCountForTesting(false);
+ desk_test_util::PopulateAppRegistryCache(account_id_, cache_.get());
+ task_environment_.RunUntilIdle();
testing::Test::SetUp();
}
@@ -230,6 +262,43 @@ class LocalDeskDataManagerTest : public testing::Test {
loop2.Run();
}
+ void AddTwoSaveAndRecallDeskTemplates() {
+ base::RunLoop loop1;
+ data_manager_->AddOrUpdateEntry(
+ std::move(sample_save_and_recall_desk_one_),
+ base::BindLambdaForTesting(
+ [&](DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(DeskModel::AddOrUpdateEntryStatus::kOk, status);
+ loop1.Quit();
+ }));
+ loop1.Run();
+
+ base::RunLoop loop2;
+ data_manager_->AddOrUpdateEntry(
+ std::move(sample_save_and_recall_desk_two_),
+ base::BindLambdaForTesting(
+ [&](DeskModel::AddOrUpdateEntryStatus status) {
+ EXPECT_EQ(DeskModel::AddOrUpdateEntryStatus::kOk, status);
+ loop2.Quit();
+ }));
+ loop2.Run();
+ }
+
+ void VerifyAllEntries(size_t expected_size, const std::string& trace_string) {
+ SCOPED_TRACE(trace_string);
+ base::RunLoop loop;
+
+ data_manager_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), expected_size);
+ loop.Quit();
+ }));
+
+ loop.Run();
+ }
+
base::ScopedTempDir temp_dir_;
std::unique_ptr<ash::DeskTemplate> sample_desk_template_one_;
std::unique_ptr<ash::DeskTemplate> sample_desk_template_one_duplicate_;
@@ -240,11 +309,17 @@ class LocalDeskDataManagerTest : public testing::Test {
duplicate_pattern_matching_named_desk_three_;
std::unique_ptr<ash::DeskTemplate> sample_desk_template_two_;
std::unique_ptr<ash::DeskTemplate> sample_desk_template_three_;
+ std::unique_ptr<ash::DeskTemplate> sample_save_and_recall_desk_one_;
+ std::unique_ptr<ash::DeskTemplate> sample_save_and_recall_desk_two_;
+ std::unique_ptr<ash::DeskTemplate> sample_save_and_recall_desk_three_;
std::unique_ptr<ash::DeskTemplate> modified_sample_desk_template_one_;
base::test::TaskEnvironment task_environment_;
+ std::unique_ptr<apps::AppRegistryCache> cache_;
+ AccountId account_id_;
std::unique_ptr<LocalDeskDataManager> data_manager_;
};
+// TODO(crbug/1320949): Pass a callback to VerifyEntryAdded.
TEST_F(LocalDeskDataManagerTest, CanAddEntry) {
data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
base::BindOnce(&VerifyEntryAddedCorrectly));
@@ -253,14 +328,34 @@ TEST_F(LocalDeskDataManagerTest, CanAddEntry) {
}
TEST_F(LocalDeskDataManagerTest, ReturnsErrorWhenAddingTooManyEntry) {
- for (std::size_t index = 0u; index < data_manager_->GetMaxEntryCount();
- ++index) {
- data_manager_->AddOrUpdateEntry(MakeTestDeskTemplate(index),
- base::BindOnce(&VerifyEntryAddedCorrectly));
+ for (std::size_t index = 0u;
+ index < data_manager_->GetMaxDeskTemplateEntryCount(); ++index) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
}
data_manager_->AddOrUpdateEntry(
- MakeTestDeskTemplate(data_manager_->GetMaxEntryCount() + 1),
+ MakeTestDeskTemplate(data_manager_->GetMaxDeskTemplateEntryCount() + 1,
+ ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedErrorHitMaximumLimit));
+
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(LocalDeskDataManagerTest,
+ ReturnsErrorWhenAddingTooManySaveAndRecallDeskEntry) {
+ for (std::size_t index = 0u;
+ index < data_manager_->GetMaxSaveAndRecallDeskEntryCount(); ++index) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(
+ data_manager_->GetMaxSaveAndRecallDeskEntryCount() + 1,
+ ash::DeskTemplateType::kSaveAndRecall),
base::BindOnce(&VerifyEntryAddedErrorHitMaximumLimit));
task_environment_.RunUntilIdle();
@@ -279,7 +374,7 @@ TEST_F(LocalDeskDataManagerTest, CanGetAllEntries) {
base::RunLoop loop;
data_manager_->GetAllEntries(base::BindLambdaForTesting(
[&](DeskModel::GetAllEntriesStatus status,
- const std::vector<ash::DeskTemplate*>& entries) {
+ const std::vector<const ash::DeskTemplate*>& entries) {
EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(entries.size(), 3ul);
EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, entries));
@@ -308,7 +403,7 @@ TEST_F(LocalDeskDataManagerTest, GetAllEntriesIncludesPolicyValues) {
base::RunLoop loop;
data_manager_->GetAllEntries(base::BindLambdaForTesting(
[&](DeskModel::GetAllEntriesStatus status,
- const std::vector<ash::DeskTemplate*>& entries) {
+ const std::vector<const ash::DeskTemplate*>& entries) {
EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(entries.size(), 4ul);
EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, entries));
@@ -334,7 +429,7 @@ TEST_F(LocalDeskDataManagerTest, GetAllEntriesIncludesPolicyValues) {
data_manager_->SetPolicyDeskTemplates("");
}
-TEST_F(LocalDeskDataManagerTest, CanMarkDuplicateEntryNames) {
+TEST_F(LocalDeskDataManagerTest, CanDetectDuplicateEntryNames) {
data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
base::BindOnce(&VerifyEntryAddedCorrectly));
data_manager_->AddOrUpdateEntry(
@@ -345,70 +440,24 @@ TEST_F(LocalDeskDataManagerTest, CanMarkDuplicateEntryNames) {
std::move(sample_desk_template_one_duplicate_two_),
base::BindOnce(&VerifyEntryAddedCorrectly));
- base::RunLoop loop;
- data_manager_->GetAllEntries(base::BindLambdaForTesting(
- [&](DeskModel::GetAllEntriesStatus status,
- const std::vector<ash::DeskTemplate*>& entries) {
- EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
- EXPECT_EQ(entries.size(), 3ul);
- EXPECT_TRUE(FindUuidInUuidList(kTestUuid1, entries));
- EXPECT_TRUE(FindUuidInUuidList(kTestUuid5, entries));
- EXPECT_TRUE(FindUuidInUuidList(kTestUuid6, entries));
-
- ash::DeskTemplate* duplicate_one =
- FindEntryInEntryList(kTestUuid5, entries);
- EXPECT_NE(duplicate_one, nullptr);
- EXPECT_EQ(base::UTF16ToUTF8(duplicate_one->template_name()),
- std::string(kDeskOneTemplateDuplicateExpectedName));
-
- ash::DeskTemplate* duplicate_two =
- FindEntryInEntryList(kTestUuid6, entries);
- EXPECT_NE(duplicate_two, nullptr);
- EXPECT_EQ(base::UTF16ToUTF8(duplicate_two->template_name()),
- std::string(kDeskOneTemplateDuplicateTwoExpectedName));
- loop.Quit();
- }));
- loop.Run();
+ EXPECT_TRUE(data_manager_->FindOtherEntryWithName(
+ base::UTF8ToUTF16(std::string("desk_01")),
+ ash::DeskTemplateType::kTemplate,
+ base::GUID::ParseCaseInsensitive(kTestUuid1)));
+ task_environment_.RunUntilIdle();
}
-TEST_F(LocalDeskDataManagerTest, AppendsDuplicateMarkingsCorrectly) {
- data_manager_->AddOrUpdateEntry(
- std::move(duplicate_pattern_matching_named_desk_),
- base::BindOnce(&VerifyEntryAddedCorrectly));
- data_manager_->AddOrUpdateEntry(
- std::move(duplicate_pattern_matching_named_desk_two_),
- base::BindOnce(&VerifyEntryAddedCorrectly));
-
- data_manager_->AddOrUpdateEntry(
- std::move(duplicate_pattern_matching_named_desk_three_),
- base::BindOnce(&VerifyEntryAddedCorrectly));
-
- base::RunLoop loop;
- data_manager_->GetAllEntries(base::BindLambdaForTesting(
- [&](DeskModel::GetAllEntriesStatus status,
- const std::vector<ash::DeskTemplate*>& entries) {
- EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
- EXPECT_EQ(entries.size(), 3ul);
- EXPECT_TRUE(FindUuidInUuidList(kTestUuid7, entries));
- EXPECT_TRUE(FindUuidInUuidList(kTestUuid8, entries));
- EXPECT_TRUE(FindUuidInUuidList(kTestUuid9, entries));
-
- ash::DeskTemplate* duplicate_one =
- FindEntryInEntryList(kTestUuid8, entries);
- EXPECT_NE(duplicate_one, nullptr);
- EXPECT_EQ(
- base::UTF16ToUTF8(duplicate_one->template_name()),
- std::string(kDuplicatePatternMatchingNamedDeskExpectedNameOne));
+TEST_F(LocalDeskDataManagerTest, CanDetectNoDuplicateEntryNames) {
+ data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_two_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
- ash::DeskTemplate* duplicate_two =
- FindEntryInEntryList(kTestUuid9, entries);
- EXPECT_NE(duplicate_two, nullptr);
- EXPECT_EQ(
- base::UTF16ToUTF8(duplicate_two->template_name()),
- std::string(kDuplicatePatternMatchingNamedDeskExpectedNameTwo));
- loop.Quit();
- }));
- loop.Run();
+ EXPECT_FALSE(data_manager_->FindOtherEntryWithName(
+ base::UTF8ToUTF16(std::string("desk_01")),
+ ash::DeskTemplateType::kTemplate,
+ base::GUID::ParseCaseInsensitive(kTestUuid1)));
+ task_environment_.RunUntilIdle();
}
TEST_F(LocalDeskDataManagerTest, CanGetEntryByUuid) {
@@ -482,7 +531,9 @@ TEST_F(LocalDeskDataManagerTest, DeskTemplateIsIgnoredIfItHasBadData) {
TEST_F(LocalDeskDataManagerTest,
GetEntryByUuidReturnsFailureIfDeskManagerHasInvalidPath) {
- data_manager_ = std::make_unique<LocalDeskDataManager>(kInvalidFilePath);
+ data_manager_ =
+ std::make_unique<LocalDeskDataManager>(kInvalidFilePath, account_id_);
+ task_environment_.RunUntilIdle();
base::RunLoop loop;
data_manager_->GetEntryByUUID(
@@ -522,22 +573,19 @@ TEST_F(LocalDeskDataManagerTest, CanDeleteEntry) {
data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
base::BindOnce(&VerifyEntryAddedCorrectly));
- base::RunLoop loop;
-
data_manager_->DeleteEntry(
kTestUuid1,
base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
}));
-
+ base::RunLoop loop;
data_manager_->GetAllEntries(base::BindLambdaForTesting(
[&](DeskModel::GetAllEntriesStatus status,
- const std::vector<ash::DeskTemplate*>& entries) {
+ const std::vector<const ash::DeskTemplate*>& entries) {
EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(entries.size(), 0ul);
loop.Quit();
}));
-
loop.Run();
}
@@ -557,10 +605,11 @@ TEST_F(LocalDeskDataManagerTest, CanDeleteAllEntries) {
base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
}));
+ task_environment_.RunUntilIdle();
data_manager_->GetAllEntries(base::BindLambdaForTesting(
[&](DeskModel::GetAllEntriesStatus status,
- const std::vector<ash::DeskTemplate*>& entries) {
+ const std::vector<const ash::DeskTemplate*>& entries) {
EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(entries.size(), 0ul);
loop.Quit();
@@ -595,4 +644,294 @@ TEST_F(LocalDeskDataManagerTest,
EXPECT_EQ(max_entry_count + 1ul, data_manager_->GetMaxEntryCount());
}
+TEST_F(LocalDeskDataManagerTest, AddDeskTemplatesAndSaveAndRecallDeskEntries) {
+ // Add two user templates.
+ AddTwoTemplates();
+
+ // Add two SaveAndRecall desks.
+ AddTwoSaveAndRecallDeskTemplates();
+
+ EXPECT_EQ(data_manager_->GetEntryCount(), 4ul);
+ EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 2ul);
+ EXPECT_EQ(data_manager_->GetSaveAndRecallDeskEntryCount(), 2ul);
+
+ base::RunLoop loop;
+ data_manager_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ loop.Quit();
+ }));
+
+ loop.Run();
+ VerifyAllEntries(4ul,
+ "Add two desks templates and two saved and recall desks");
+}
+
+TEST_F(LocalDeskDataManagerTest, AddSaveAndRecallDeskEntry) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(1u, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(1ul, "Added one save and recall desk");
+ base::RunLoop loop;
+ // Verify that it's not SaveAndRecall entry in the desk template cache.
+ data_manager_->GetAllEntries(base::BindLambdaForTesting(
+ [&](DeskModel::GetAllEntriesStatus status,
+ const std::vector<const ash::DeskTemplate*>& entries) {
+ EXPECT_EQ(status, DeskModel::GetAllEntriesStatus::kOk);
+ EXPECT_EQ(entries.size(), 1ul);
+ EXPECT_EQ(entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
+ loop.Quit();
+ }));
+ EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 0ul);
+ EXPECT_EQ(data_manager_->GetSaveAndRecallDeskEntryCount(), 1ul);
+ loop.Run();
+}
+
+TEST_F(LocalDeskDataManagerTest, CanGetSaveAndRecallDeskEntryByUuid) {
+ data_manager_->AddOrUpdateEntry(std::move(sample_save_and_recall_desk_one_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ data_manager_->GetEntryByUUID(
+ kTestSaveAndRecallDeskUuid1,
+ base::BindLambdaForTesting([&](DeskModel::GetEntryByUuidStatus status,
+ std::unique_ptr<ash::DeskTemplate> entry) {
+ EXPECT_EQ(DeskModel::GetEntryByUuidStatus::kOk, status);
+
+ EXPECT_EQ(base::GUID::ParseCaseInsensitive(kTestSaveAndRecallDeskUuid1),
+ entry->uuid());
+ EXPECT_EQ(u"save_and_recall_desk_01", entry->template_name());
+ EXPECT_EQ(kTestTime1, entry->created_time());
+ }));
+
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(LocalDeskDataManagerTest, CanDeleteSaveAndRecallDeskEntry) {
+ data_manager_->AddOrUpdateEntry(std::move(sample_save_and_recall_desk_one_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(1ul, "Added one save and recall desk");
+ EXPECT_EQ(data_manager_->GetSaveAndRecallDeskEntryCount(), 1ul);
+ data_manager_->DeleteEntry(
+ kTestSaveAndRecallDeskUuid1,
+ base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
+ }));
+
+ VerifyAllEntries(0ul, "Deleted one save and recall desk");
+}
+
+TEST_F(LocalDeskDataManagerTest, CanAddMaxEntriesForBothTypes) {
+ for (std::size_t index = 0u;
+ index < data_manager_->GetMaxSaveAndRecallDeskEntryCount(); ++index) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+ for (std::size_t index = 0u;
+ index < data_manager_->GetMaxDeskTemplateEntryCount(); ++index) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+
+ VerifyAllEntries(
+ 12ul, "Added max number of save and recall desks and desk templates");
+ EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 6ul);
+ EXPECT_EQ(data_manager_->GetSaveAndRecallDeskEntryCount(), 6ul);
+}
+
+TEST_F(LocalDeskDataManagerTest, CanDeleteAllEntriesOfBothTypes) {
+ data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_two_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_three_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ data_manager_->AddOrUpdateEntry(std::move(sample_save_and_recall_desk_one_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ data_manager_->AddOrUpdateEntry(std::move(sample_save_and_recall_desk_two_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ data_manager_->AddOrUpdateEntry(std::move(sample_save_and_recall_desk_three_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(6ul,
+ "Added a mix of save and recall desks and desk templates");
+
+ data_manager_->DeleteAllEntries(
+ base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
+ }));
+
+ VerifyAllEntries(0ul, "Deleted all entries");
+ EXPECT_EQ(0ul, data_manager_->GetEntryCount());
+}
+
+TEST_F(LocalDeskDataManagerTest,
+ CanAddMaxEntriesDeskTemplatesAndStillAddEntryForSaveAndRecallDesks) {
+ for (std::size_t index = 0u;
+ index < data_manager_->GetMaxDeskTemplateEntryCount(); ++index) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(1ul, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(7ul,
+ "Added one save and recall desk after capping "
+ "desk template entries");
+
+ EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 6ul);
+ EXPECT_EQ(data_manager_->GetSaveAndRecallDeskEntryCount(), 1ul);
+}
+
+TEST_F(LocalDeskDataManagerTest,
+ CanAddMaxEntriesForSaveAndRecallDeskAndStillAddEntryForDeskTemplate) {
+ for (std::size_t index = 0u;
+ index < data_manager_->GetMaxSaveAndRecallDeskEntryCount(); ++index) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kSaveAndRecall),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(1ul, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ VerifyAllEntries(7ul,
+ "Added one desk template after capping "
+ "save and recall desk entries");
+
+ EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 1ul);
+ EXPECT_EQ(data_manager_->GetSaveAndRecallDeskEntryCount(), 6ul);
+}
+
+TEST_F(LocalDeskDataManagerTest, RollbackUpdateTemplatesOnFileWriteFailure) {
+ // Add two user templates.
+ for (std::size_t index = 0u; index < 2u; ++index) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+
+ EXPECT_EQ(data_manager_->GetEntryCount(), 2ul);
+ EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 2ul);
+ task_environment_.RunUntilIdle();
+
+ base::SetPosixFilePermissions(temp_dir_.GetPath(),
+ base::FILE_PERMISSION_READ_BY_USER);
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(1ul, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedFailure));
+
+ VerifyAllEntries(2ul,
+ "Updated one desk template failed to write to file system");
+
+ base::SetPosixFilePermissions(temp_dir_.GetPath(),
+ base::FILE_PERMISSION_READ_BY_USER |
+ base::FILE_PERMISSION_WRITE_BY_USER |
+ base::FILE_PERMISSION_EXECUTE_BY_USER);
+}
+
+TEST_F(LocalDeskDataManagerTest, RollbackAddTemplatesOnFileWriteFailure) {
+ // Add two user templates.
+ for (std::size_t index = 0u; index < 2u; ++index) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+
+ EXPECT_EQ(data_manager_->GetEntryCount(), 2ul);
+ EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 2ul);
+ task_environment_.RunUntilIdle();
+
+ base::SetPosixFilePermissions(temp_dir_.GetPath(),
+ base::FILE_PERMISSION_READ_BY_USER);
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(3ul, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedFailure));
+ task_environment_.RunUntilIdle();
+
+ VerifyAllEntries(2ul, "Add one desk template failed to write to file system");
+
+ base::SetPosixFilePermissions(temp_dir_.GetPath(),
+ base::FILE_PERMISSION_READ_BY_USER |
+ base::FILE_PERMISSION_WRITE_BY_USER |
+ base::FILE_PERMISSION_EXECUTE_BY_USER);
+}
+
+TEST_F(LocalDeskDataManagerTest, RollbackDeleteTemplatesOnFileDeleteFailure) {
+ data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ EXPECT_EQ(data_manager_->GetEntryCount(), 1ul);
+ EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 1ul);
+ task_environment_.RunUntilIdle();
+ base::SetPosixFilePermissions(temp_dir_.GetPath(),
+ base::FILE_PERMISSION_READ_BY_USER);
+ data_manager_->DeleteEntry(
+ kTestUuid1,
+ base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kFailure);
+ }));
+ task_environment_.RunUntilIdle();
+
+ VerifyAllEntries(1ul, "Delete desk template failed to delete on file system");
+
+ base::SetPosixFilePermissions(temp_dir_.GetPath(),
+ base::FILE_PERMISSION_READ_BY_USER |
+ base::FILE_PERMISSION_WRITE_BY_USER |
+ base::FILE_PERMISSION_EXECUTE_BY_USER);
+}
+
+TEST_F(LocalDeskDataManagerTest,
+ RollbackDeleteAllTemplatesOnFileDeleteFailure) {
+ // Add four user templates.
+ for (std::size_t index = 0u; index < 4u; ++index) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+ }
+ EXPECT_EQ(data_manager_->GetEntryCount(), 4ul);
+ EXPECT_EQ(data_manager_->GetDeskTemplateEntryCount(), 4ul);
+ task_environment_.RunUntilIdle();
+ base::SetPosixFilePermissions(temp_dir_.GetPath(),
+ base::FILE_PERMISSION_READ_BY_USER);
+ data_manager_->DeleteAllEntries(
+ base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
+ EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kFailure);
+ }));
+ task_environment_.RunUntilIdle();
+
+ VerifyAllEntries(4ul,
+ "Delete all desk template failed to delete on file system");
+
+ base::SetPosixFilePermissions(temp_dir_.GetPath(),
+ base::FILE_PERMISSION_READ_BY_USER |
+ base::FILE_PERMISSION_WRITE_BY_USER |
+ base::FILE_PERMISSION_EXECUTE_BY_USER);
+}
+
+// Note: To fully utilize this test build and run it in a tsan build.
+// Instructions to do so can be found at:
+// //docs/website/site/developers/testing/threadsanitizer-tsan-v2/index.md
+// Otherwise the tsan tryjob should catch this test failing in CQ.
+TEST_F(LocalDeskDataManagerTest, StressTestModifyingEntriesForThreadSafety) {
+ for (uint32_t iteration = 0; iteration < kThreadSafeIterations; ++iteration) {
+ data_manager_->AddOrUpdateEntry(
+ MakeTestDeskTemplate(iteration % 10, ash::DeskTemplateType::kTemplate),
+ base::BindOnce(&VerifyEntryAddedCorrectly));
+
+ if (iteration % data_manager_->GetDeskTemplateEntryCount() == 0) {
+ data_manager_->DeleteAllEntries(
+ base::BindOnce(&VerifyEntryDeletedCorrectly));
+ }
+ }
+
+ task_environment_.RunUntilIdle();
+}
+
} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/saved_desk_builder.cc b/chromium/components/desks_storage/core/saved_desk_builder.cc
new file mode 100644
index 00000000000..947af91df62
--- /dev/null
+++ b/chromium/components/desks_storage/core/saved_desk_builder.cc
@@ -0,0 +1,108 @@
+// Copyright 2022 The Chromium 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/desks_storage/core/saved_desk_builder.h"
+
+#include "ash/public/cpp/desk_template.h"
+#include "base/guid.h"
+#include "base/time/time.h"
+#include "components/app_restore/app_launch_info.h"
+#include "components/desks_storage/core/saved_desk_test_util.h"
+
+namespace desks_storage {
+
+SavedDeskBuilder::SavedDeskBuilder()
+ : desk_name_("unnamed desk"),
+ desk_source_(ash::DeskTemplateSource::kUser),
+ desk_type_(ash::DeskTemplateType::kTemplate),
+ restore_data_(std::make_unique<app_restore::RestoreData>()) {
+ desk_uuid_ = base::GUID::GenerateRandomV4().AsLowercaseString();
+ created_time_ = base::Time::Now();
+};
+
+SavedDeskBuilder::~SavedDeskBuilder(){};
+
+std::unique_ptr<ash::DeskTemplate> SavedDeskBuilder::Build() {
+ auto desk_template = std::make_unique<ash::DeskTemplate>(
+ desk_uuid_, desk_source_, desk_name_, created_time_, desk_type_);
+
+ desk_template->set_desk_restore_data(std::move(restore_data_));
+
+ return desk_template;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::SetUuid(const std::string& uuid) {
+ desk_uuid_ = uuid;
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::SetName(const std::string& name) {
+ desk_name_ = name;
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::SetType(ash::DeskTemplateType desk_type) {
+ desk_type_ = desk_type;
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::SetSource(
+ ash::DeskTemplateSource desk_source) {
+ desk_source_ = desk_source;
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::SetCreatedTime(base::Time& created_time) {
+ created_time_ = created_time;
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::AddAshBrowserAppWindow(
+ int window_id,
+ std::vector<GURL> urls) {
+ saved_desk_test_util::AddBrowserWindow(/*is_lacros=*/false, window_id, urls,
+ restore_data_.get());
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::AddLacrosBrowserAppWindow(
+ int window_id,
+ std::vector<GURL> urls) {
+ saved_desk_test_util::AddBrowserWindow(/*is_lacros=*/true, window_id, urls,
+ restore_data_.get());
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::AddAshPwaAppWindow(int window_id,
+ const std::string url) {
+ saved_desk_test_util::AddPwaWindow(/*is_lacros=*/false, window_id, url,
+ restore_data_.get());
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::AddLacrosPwaAppWindow(
+ int window_id,
+ const std::string url) {
+ saved_desk_test_util::AddPwaWindow(/*is_lacros=*/true, window_id, url,
+ restore_data_.get());
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::AddChromeAppWindow(
+ int window_id,
+ const std::string app_id) {
+ saved_desk_test_util::AddGenericAppWindow(window_id, app_id,
+ restore_data_.get());
+ return *this;
+}
+
+SavedDeskBuilder& SavedDeskBuilder::AddGenericAppWindow(
+ int window_id,
+ const std::string app_id) {
+ saved_desk_test_util::AddGenericAppWindow(window_id, app_id,
+ restore_data_.get());
+ return *this;
+}
+
+} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/saved_desk_builder.h b/chromium/components/desks_storage/core/saved_desk_builder.h
new file mode 100644
index 00000000000..7bef0623295
--- /dev/null
+++ b/chromium/components/desks_storage/core/saved_desk_builder.h
@@ -0,0 +1,84 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DESKS_STORAGE_CORE_SAVED_DESK_BUILDER_H_
+#define COMPONENTS_DESKS_STORAGE_CORE_SAVED_DESK_BUILDER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "components/app_restore/restore_data.h"
+#include "url/gurl.h"
+
+namespace ash {
+class DeskTemplate;
+enum class DeskTemplateSource;
+enum class DeskTemplateType;
+} // namespace ash
+
+namespace desks_storage {
+
+// Helper class for building a saved desk for test.
+class SavedDeskBuilder {
+ public:
+ SavedDeskBuilder();
+ SavedDeskBuilder(const SavedDeskBuilder&) = delete;
+ SavedDeskBuilder& operator=(const SavedDeskBuilder&) = delete;
+ ~SavedDeskBuilder();
+
+ // Builds a saved desk. This should only be called once per builder
+ // instance.
+ std::unique_ptr<ash::DeskTemplate> Build();
+
+ // Sets saved desk UUID. If not set, the built desk will have a random UUID.
+ SavedDeskBuilder& SetUuid(const std::string& uuid);
+
+ // Sets saved desk name. If not set, the built desk will have a fixed name.
+ SavedDeskBuilder& SetName(const std::string& name);
+
+ // Sets saved desk type. If not set, the built desk will default to
+ // DeskTemplate.
+ SavedDeskBuilder& SetType(ash::DeskTemplateType desk_type);
+
+ // Sets saved desk source. If not set, the built desk will default to kUser.
+ SavedDeskBuilder& SetSource(ash::DeskTemplateSource desk_source);
+
+ // Sets saved desk creation timestamp. If not set, the built desk will have
+ // its creation timestamp set at the creation time of the SavedDeskBuilder.
+ SavedDeskBuilder& SetCreatedTime(base::Time& created_time);
+
+ // Adds a Ash Chrome Browser App window.
+ SavedDeskBuilder& AddAshBrowserAppWindow(int window_id,
+ std::vector<GURL> urls);
+
+ // Adds a Lacros Chrome Browser App window.
+ SavedDeskBuilder& AddLacrosBrowserAppWindow(int window_id,
+ std::vector<GURL> urls);
+
+ // Adds a PWA window hosted in Ash Chrome.
+ SavedDeskBuilder& AddAshPwaAppWindow(int window_id, const std::string url);
+
+ // Adds a PWA window hosted in Lacros Chrome.
+ SavedDeskBuilder& AddLacrosPwaAppWindow(int window_id, const std::string url);
+
+ // Adds a Chrome app window.
+ SavedDeskBuilder& AddChromeAppWindow(int window_id, const std::string app_id);
+
+ // Adds a generic app window.
+ SavedDeskBuilder& AddGenericAppWindow(int window_id,
+ const std::string app_id);
+
+ private:
+ std::string desk_uuid_;
+ std::string desk_name_;
+ ash::DeskTemplateSource desk_source_;
+ ash::DeskTemplateType desk_type_;
+ base::Time created_time_;
+ std::unique_ptr<app_restore::RestoreData> restore_data_;
+};
+
+} // namespace desks_storage
+
+#endif // COMPONENTS_DESKS_STORAGE_CORE_SAVED_DESK_BUILDER_H_
diff --git a/chromium/components/desks_storage/core/saved_desk_test_util.cc b/chromium/components/desks_storage/core/saved_desk_test_util.cc
new file mode 100644
index 00000000000..ac6697f5a14
--- /dev/null
+++ b/chromium/components/desks_storage/core/saved_desk_test_util.cc
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium 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/desks_storage/core/saved_desk_test_util.h"
+
+#include "components/app_constants/constants.h"
+#include "components/app_restore/app_launch_info.h"
+#include "components/app_restore/restore_data.h"
+
+namespace desks_storage {
+
+namespace saved_desk_test_util {
+
+void AddBrowserWindow(bool is_lacros,
+ int window_id,
+ std::vector<GURL> urls,
+ app_restore::RestoreData* out_restore_data) {
+ auto browser_info = std::make_unique<app_restore::AppLaunchInfo>(
+ is_lacros ? app_constants::kLacrosAppId : app_constants::kChromeAppId,
+ window_id);
+ browser_info->urls = urls;
+
+ out_restore_data->AddAppLaunchInfo(std::move(browser_info));
+}
+
+void AddPwaWindow(bool is_lacros,
+ int window_id,
+ std::string url,
+ app_restore::RestoreData* out_restore_data) {
+ auto app_launch_info = std::make_unique<app_restore::AppLaunchInfo>(
+ is_lacros ? app_constants::kLacrosAppId : app_constants::kChromeAppId,
+ window_id);
+
+ app_launch_info->urls = {GURL(url)};
+ app_launch_info->app_type_browser = true;
+
+ out_restore_data->AddAppLaunchInfo(std::move(app_launch_info));
+}
+
+void AddGenericAppWindow(int window_id,
+ std::string app_id,
+ app_restore::RestoreData* out_restore_data) {
+ auto app_launch_info =
+ std::make_unique<app_restore::AppLaunchInfo>(app_id, window_id);
+
+ out_restore_data->AddAppLaunchInfo(std::move(app_launch_info));
+}
+
+} // namespace saved_desk_test_util
+
+} // namespace desks_storage
diff --git a/chromium/components/desks_storage/core/saved_desk_test_util.h b/chromium/components/desks_storage/core/saved_desk_test_util.h
new file mode 100644
index 00000000000..edea31d74e1
--- /dev/null
+++ b/chromium/components/desks_storage/core/saved_desk_test_util.h
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DESKS_STORAGE_CORE_SAVED_DESK_TEST_UTIL_H_
+#define COMPONENTS_DESKS_STORAGE_CORE_SAVED_DESK_TEST_UTIL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "components/app_restore/restore_data.h"
+#include "url/gurl.h"
+
+namespace desks_storage {
+
+namespace saved_desk_test_util {
+
+// Adds a Chrome browser window to `out_restore_data`.
+void AddBrowserWindow(bool is_lacros,
+ int window_id,
+ std::vector<GURL> urls,
+ app_restore::RestoreData* out_restore_data);
+
+// Adds a PWA window to `out_restore_data`.
+void AddPwaWindow(bool is_lacros,
+ int window_id,
+ std::string url,
+ app_restore::RestoreData* out_restore_data);
+
+// Adds a Generic app window to `out_restore_data`.
+void AddGenericAppWindow(int window_id,
+ std::string app_id,
+ app_restore::RestoreData* out_restore_data);
+
+} // namespace saved_desk_test_util
+
+} // namespace desks_storage
+
+#endif // COMPONENTS_DESKS_STORAGE_CORE_SAVED_DESK_TEST_UTIL_H_
diff --git a/chromium/components/device_signals/OWNERS b/chromium/components/device_signals/OWNERS
new file mode 100644
index 00000000000..eb5ae5e3694
--- /dev/null
+++ b/chromium/components/device_signals/OWNERS
@@ -0,0 +1,2 @@
+seblalancette@chromium.org
+rogerta@chromium.org
diff --git a/chromium/components/device_signals/README.md b/chromium/components/device_signals/README.md
new file mode 100644
index 00000000000..ae7da43d81a
--- /dev/null
+++ b/chromium/components/device_signals/README.md
@@ -0,0 +1,12 @@
+# Device Signals
+
+This component contains the implementation of device signals (aka context-aware
+signals) collection. It contains definition for services used to aggregate such
+signals, along with utility functions that can be reused in various contexts.
+
+A set of device signals can be used by Zero Trust access providers to assess a
+device's security posture. That posture can then be used to calculate the risk
+factor around granting access to restricted resources to the device (and its
+user). This component facilitates the collection of those signals in Enterprise
+use cases while also providing symbols for other use cases requiring some of
+these signals (e.g. metrics). \ No newline at end of file
diff --git a/chromium/components/device_signals/core/BUILD.gn b/chromium/components/device_signals/core/BUILD.gn
new file mode 100644
index 00000000000..3c3c530e82f
--- /dev/null
+++ b/chromium/components/device_signals/core/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("unit_tests") {
+ testonly = true
+ deps = [
+ "//components/device_signals/core/browser:unit_tests",
+ "//components/device_signals/core/common:unit_tests",
+ ]
+}
diff --git a/chromium/components/device_signals/core/browser/BUILD.gn b/chromium/components/device_signals/core/browser/BUILD.gn
new file mode 100644
index 00000000000..a12e34d6503
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/BUILD.gn
@@ -0,0 +1,80 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("browser") {
+ public = [
+ "signals_aggregator.h",
+ "signals_aggregator_impl.h",
+ "signals_collector.h",
+ "system_signals_service_host.h",
+ "user_context.h",
+ "user_delegate.h",
+ "user_permission_service.h",
+ "user_permission_service_impl.h",
+ ]
+
+ sources = [
+ "signals_aggregator_impl.cc",
+ "user_context.cc",
+ "user_permission_service_impl.cc",
+ ]
+
+ public_deps = [
+ "//base",
+ "//components/device_signals/core/common/mojom",
+ "//components/keyed_service/core",
+ ]
+
+ deps = [
+ "//components/device_signals/core/common",
+ "//components/policy/core/common",
+ "//components/signin/public/identity_manager",
+ ]
+}
+
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "mock_signals_collector.cc",
+ "mock_signals_collector.h",
+ "mock_system_signals_service_host.cc",
+ "mock_system_signals_service_host.h",
+ "mock_user_delegate.cc",
+ "mock_user_delegate.h",
+ "mock_user_permission_service.cc",
+ "mock_user_permission_service.h",
+ ]
+
+ deps = [
+ ":browser",
+ "//base",
+ "//components/device_signals/core/common/mojom",
+ "//components/signin/public/identity_manager",
+ "//testing/gmock",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "signals_aggregator_impl_unittest.cc",
+ "user_permission_service_impl_unittest.cc",
+ ]
+
+ deps = [
+ ":browser",
+ ":test_support",
+ "//base/test:test_support",
+ "//components/device_signals/core/common",
+ "//components/policy/core/common",
+ "//components/policy/core/common:test_support",
+ "//components/signin/public/identity_manager",
+ "//components/signin/public/identity_manager:test_support",
+ "//testing/gtest",
+ ]
+
+ if (is_win) {
+ deps += [ "//components/device_signals/core/browser/win:unit_tests" ]
+ }
+}
diff --git a/chromium/components/device_signals/core/browser/DEPS b/chromium/components/device_signals/core/browser/DEPS
new file mode 100644
index 00000000000..97d436ae7d8
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+components/keyed_service/core",
+ "+components/signin/public",
+ "+components/policy/core",
+]
diff --git a/chromium/components/device_signals/core/browser/mock_signals_collector.cc b/chromium/components/device_signals/core/browser/mock_signals_collector.cc
new file mode 100644
index 00000000000..dd569e50e9f
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_signals_collector.cc
@@ -0,0 +1,12 @@
+// Copyright 2022 The Chromium 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/device_signals/core/browser/mock_signals_collector.h"
+
+namespace device_signals {
+
+MockSignalsCollector::MockSignalsCollector() = default;
+MockSignalsCollector::~MockSignalsCollector() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/mock_signals_collector.h b/chromium/components/device_signals/core/browser/mock_signals_collector.h
new file mode 100644
index 00000000000..cb3e1172573
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_signals_collector.h
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_SIGNALS_COLLECTOR_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_SIGNALS_COLLECTOR_H_
+
+#include "base/callback.h"
+#include "base/values.h"
+#include "components/device_signals/core/browser/signals_collector.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockSignalsCollector : public SignalsCollector {
+ public:
+ MockSignalsCollector();
+ ~MockSignalsCollector() override;
+
+ MOCK_METHOD(const std::unordered_set<std::string>,
+ GetSupportedSignalNames,
+ (),
+ (override));
+ MOCK_METHOD(void,
+ GetSignal,
+ (const std::string&,
+ const base::Value&,
+ SignalsCollector::GetSignalCallback),
+ (override));
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_SIGNALS_COLLECTOR_H_
diff --git a/chromium/components/device_signals/core/browser/mock_system_signals_service_host.cc b/chromium/components/device_signals/core/browser/mock_system_signals_service_host.cc
new file mode 100644
index 00000000000..d66b1ba2eaa
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_system_signals_service_host.cc
@@ -0,0 +1,15 @@
+// Copyright 2022 The Chromium 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/device_signals/core/browser/mock_system_signals_service_host.h"
+
+namespace device_signals {
+
+MockSystemSignalsServiceHost::MockSystemSignalsServiceHost() = default;
+MockSystemSignalsServiceHost::~MockSystemSignalsServiceHost() = default;
+
+MockSystemSignalsService::MockSystemSignalsService() = default;
+MockSystemSignalsService::~MockSystemSignalsService() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/mock_system_signals_service_host.h b/chromium/components/device_signals/core/browser/mock_system_signals_service_host.h
new file mode 100644
index 00000000000..b5278eefcda
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_system_signals_service_host.h
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_SYSTEM_SIGNALS_SERVICE_HOST_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_SYSTEM_SIGNALS_SERVICE_HOST_H_
+
+#include "build/build_config.h"
+#include "components/device_signals/core/browser/system_signals_service_host.h"
+#include "components/device_signals/core/common/mojom/system_signals.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockSystemSignalsServiceHost : public SystemSignalsServiceHost {
+ public:
+ MockSystemSignalsServiceHost();
+ ~MockSystemSignalsServiceHost() override;
+
+ MOCK_METHOD(mojom::SystemSignalsService*, GetService, (), (override));
+};
+
+class MockSystemSignalsService : public mojom::SystemSignalsService {
+ public:
+ MockSystemSignalsService();
+ ~MockSystemSignalsService() override;
+
+ MOCK_METHOD(void,
+ GetBinarySignals,
+ (std::vector<device_signals::mojom::BinarySignalsRequestPtr>,
+ GetBinarySignalsCallback),
+ (override));
+
+#if BUILDFLAG(IS_WIN)
+ MOCK_METHOD(void,
+ GetAntiVirusSignals,
+ (GetAntiVirusSignalsCallback),
+ (override));
+ MOCK_METHOD(void, GetHotfixSignals, (GetHotfixSignalsCallback), (override));
+#endif // BUILDFLAG(IS_WIN)
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOCK_SYSTEM_SIGNALS_SERVICE_HOST_H_
diff --git a/chromium/components/device_signals/core/browser/mock_user_delegate.cc b/chromium/components/device_signals/core/browser/mock_user_delegate.cc
new file mode 100644
index 00000000000..243ca6c32da
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_user_delegate.cc
@@ -0,0 +1,12 @@
+// Copyright 2022 The Chromium 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/device_signals/core/browser/mock_user_delegate.h"
+
+namespace device_signals {
+
+MockUserDelegate::MockUserDelegate() = default;
+MockUserDelegate::~MockUserDelegate() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/mock_user_delegate.h b/chromium/components/device_signals/core/browser/mock_user_delegate.h
new file mode 100644
index 00000000000..c620f3aa8c2
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_user_delegate.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_USER_DELEGATE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_USER_DELEGATE_H_
+
+#include "components/device_signals/core/browser/user_delegate.h"
+#include "components/signin/public/identity_manager/account_info.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockUserDelegate : public UserDelegate {
+ public:
+ MockUserDelegate();
+ ~MockUserDelegate() override;
+
+ MOCK_METHOD(bool, IsAffiliated, (), (const, override));
+ MOCK_METHOD(bool, IsSameManagedUser, (const AccountInfo&), (const, override));
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_USER_DELEGATE_H_
diff --git a/chromium/components/device_signals/core/browser/mock_user_permission_service.cc b/chromium/components/device_signals/core/browser/mock_user_permission_service.cc
new file mode 100644
index 00000000000..7e299d084f3
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_user_permission_service.cc
@@ -0,0 +1,12 @@
+// Copyright 2022 The Chromium 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/device_signals/core/browser/mock_user_permission_service.h"
+
+namespace device_signals {
+
+MockUserPermissionService::MockUserPermissionService() = default;
+MockUserPermissionService::~MockUserPermissionService() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/mock_user_permission_service.h b/chromium/components/device_signals/core/browser/mock_user_permission_service.h
new file mode 100644
index 00000000000..51f96215f33
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_user_permission_service.h
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_USER_PERMISSION_SERVICE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_USER_PERMISSION_SERVICE_H_
+
+#include "base/callback.h"
+#include "components/device_signals/core/browser/user_permission_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockUserPermissionService : public UserPermissionService {
+ public:
+ MockUserPermissionService();
+ ~MockUserPermissionService() override;
+
+ MOCK_METHOD(void,
+ CanCollectSignals,
+ (const UserContext&, CanCollectCallback),
+ (override));
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_USER_PERMISSION_SERVICE_H_
diff --git a/chromium/components/device_signals/core/browser/signals_aggregator.h b/chromium/components/device_signals/core/browser/signals_aggregator.h
new file mode 100644
index 00000000000..6ff18d6ebad
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/signals_aggregator.h
@@ -0,0 +1,39 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_AGGREGATOR_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_AGGREGATOR_H_
+
+#include "base/callback_forward.h"
+#include "base/values.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace device_signals {
+
+struct UserContext;
+
+class SignalsAggregator : public KeyedService {
+ public:
+ using GetSignalsCallback = base::OnceCallback<void(base::Value)>;
+
+ ~SignalsAggregator() override = default;
+
+ // Will asynchronously collect signals as defined in the `parameters`
+ // dictionary, where keys represent the names of the signals to be collected
+ // and values represent their collection parameters. Invokes `callback` with
+ // the collected signals stored in a dictionary, where the keys are the
+ // signal names and values are the collected values. If signal collection
+ // failed, the value returned in the callback may be solely an error string.
+ // Will use `user_context` to validate that the user has permissions to the
+ // device's signals.
+ // Currently only supports the collection of one signal (only one entry in
+ // `parameter`).
+ virtual void GetSignals(const UserContext& user_context,
+ base::Value::Dict parameters,
+ GetSignalsCallback callback) = 0;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_AGGREGATOR_H_
diff --git a/chromium/components/device_signals/core/browser/signals_aggregator_impl.cc b/chromium/components/device_signals/core/browser/signals_aggregator_impl.cc
new file mode 100644
index 00000000000..f2cb8044188
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/signals_aggregator_impl.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/browser/signals_aggregator_impl.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/check.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "components/device_signals/core/browser/signals_collector.h"
+#include "components/device_signals/core/browser/user_context.h"
+#include "components/device_signals/core/browser/user_permission_service.h"
+#include "components/device_signals/core/common/signals_constants.h"
+
+namespace device_signals {
+
+namespace {
+
+std::string PermissionToError(const UserPermission permission) {
+ switch (permission) {
+ case UserPermission::kUnaffiliated:
+ return errors::kUnaffiliatedUser;
+ case UserPermission::kMissingConsent:
+ return errors::kConsentRequired;
+ case UserPermission::kConsumerUser:
+ case UserPermission::kUnknownUser:
+ return errors::kUnsupported;
+ case UserPermission::kGranted:
+ NOTREACHED();
+ return "";
+ }
+}
+
+} // namespace
+
+SignalsAggregatorImpl::SignalsAggregatorImpl(
+ UserPermissionService* permission_service,
+ std::vector<std::unique_ptr<SignalsCollector>> collectors)
+ : permission_service_(permission_service),
+ collectors_(std::move(collectors)) {
+ DCHECK(permission_service_);
+}
+
+SignalsAggregatorImpl::~SignalsAggregatorImpl() = default;
+
+void SignalsAggregatorImpl::GetSignals(const UserContext& user_context,
+ base::Value::Dict parameters,
+ GetSignalsCallback callback) {
+ if (parameters.empty()) {
+ std::move(callback).Run(base::Value(errors::kUnsupported));
+ return;
+ }
+
+ // Request for collection of multiple signals is not yet supported. Only the
+ // first signal will be returned.
+ DCHECK(parameters.size() == 1);
+
+ permission_service_->CanCollectSignals(
+ user_context,
+ base::BindOnce(&SignalsAggregatorImpl::OnUserPermissionChecked,
+ weak_factory_.GetWeakPtr(), std::move(parameters),
+ std::move(callback)));
+}
+
+void SignalsAggregatorImpl::OnUserPermissionChecked(
+ base::Value::Dict parameters,
+ GetSignalsCallback callback,
+ const UserPermission user_permission) {
+ if (user_permission != UserPermission::kGranted) {
+ std::move(callback).Run(base::Value(PermissionToError(user_permission)));
+ return;
+ }
+
+ std::pair<const std::string&, const base::Value&> signal_request =
+ *parameters.begin();
+ for (const auto& collector : collectors_) {
+ const auto signals_set = collector->GetSupportedSignalNames();
+ if (signals_set.find(signal_request.first) == signals_set.end()) {
+ // Signal is not supported by current collector.
+ continue;
+ }
+
+ // Signal is supported by current collector.
+ auto return_callback = base::BindOnce(
+ &SignalsAggregatorImpl::OnSignalCollected, weak_factory_.GetWeakPtr(),
+ signal_request.first, std::move(callback));
+ collector->GetSignal(signal_request.first, signal_request.second,
+ std::move(return_callback));
+ return;
+ }
+
+ // Not a supported signal.
+ std::move(callback).Run(base::Value(errors::kUnsupported));
+}
+
+void SignalsAggregatorImpl::OnSignalCollected(const std::string signal_name,
+ GetSignalsCallback callback,
+ base::Value value) {
+ base::Value::Dict return_value;
+ return_value.Set(signal_name, std::move(value));
+ std::move(callback).Run(base::Value(std::move(return_value)));
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/signals_aggregator_impl.h b/chromium/components/device_signals/core/browser/signals_aggregator_impl.h
new file mode 100644
index 00000000000..949b7cb920b
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/signals_aggregator_impl.h
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_AGGREGATOR_IMPL_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_AGGREGATOR_IMPL_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/device_signals/core/browser/signals_aggregator.h"
+
+namespace device_signals {
+
+class SignalsCollector;
+class UserPermissionService;
+enum class UserPermission;
+
+class SignalsAggregatorImpl : public SignalsAggregator {
+ public:
+ explicit SignalsAggregatorImpl(
+ UserPermissionService* permission_service,
+ std::vector<std::unique_ptr<SignalsCollector>> collectors);
+
+ SignalsAggregatorImpl(const SignalsAggregatorImpl&) = delete;
+ SignalsAggregatorImpl& operator=(const SignalsAggregatorImpl&) = delete;
+
+ ~SignalsAggregatorImpl() override;
+
+ // SignalsAggregator:
+ void GetSignals(const UserContext& user_context,
+ base::Value::Dict parameters,
+ GetSignalsCallback callback) override;
+
+ private:
+ void OnUserPermissionChecked(base::Value::Dict parameters,
+ GetSignalsCallback callback,
+ const UserPermission user_permission);
+
+ void OnSignalCollected(const std::string signal_name,
+ GetSignalsCallback callback,
+ base::Value value);
+
+ base::raw_ptr<UserPermissionService> permission_service_;
+ std::vector<std::unique_ptr<SignalsCollector>> collectors_;
+
+ base::WeakPtrFactory<SignalsAggregatorImpl> weak_factory_{this};
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_AGGREGATOR_IMPL_H_
diff --git a/chromium/components/device_signals/core/browser/signals_aggregator_impl_unittest.cc b/chromium/components/device_signals/core/browser/signals_aggregator_impl_unittest.cc
new file mode 100644
index 00000000000..42d9c3c5d97
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/signals_aggregator_impl_unittest.cc
@@ -0,0 +1,229 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/browser/signals_aggregator_impl.h"
+
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "components/device_signals/core/browser/mock_signals_collector.h"
+#include "components/device_signals/core/browser/mock_user_permission_service.h"
+#include "components/device_signals/core/browser/user_context.h"
+#include "components/device_signals/core/browser/user_permission_service.h"
+#include "components/device_signals/core/common/signals_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::Pointee;
+using testing::Ref;
+using testing::Return;
+
+namespace device_signals {
+
+namespace {
+
+constexpr char kFakeSignalName[] = "signal_name";
+constexpr char kOtherFakeSignalName[] = "other_signal_name";
+
+constexpr char kGaiaId[] = "gaia-id";
+
+base::Value GetFakeSignalParameter() {
+ return base::Value("some parameter needed by the signal collector");
+}
+
+base::Value GetFakeSignalValue() {
+ base::Value::Dict fake_signal_value;
+ fake_signal_value.Set("some_key", "some_value");
+ return base::Value(std::move(fake_signal_value));
+}
+
+base::Value GetOtherFakeSignalParameter() {
+ base::Value::Dict fake_signal_value;
+ fake_signal_value.Set("some_file_path", "some/path");
+ return base::Value(std::move(fake_signal_value));
+}
+
+base::Value GetOtherFakeSignalValue() {
+ return base::Value("just a string");
+}
+
+std::unique_ptr<MockSignalsCollector> GetCollectorForFakeSignal() {
+ auto mock_collector = std::make_unique<MockSignalsCollector>();
+ ON_CALL(*mock_collector.get(), GetSupportedSignalNames())
+ .WillByDefault(
+ Return(std::unordered_set<std::string>({kFakeSignalName})));
+
+ ON_CALL(*mock_collector.get(), GetSignal(kFakeSignalName, _, _))
+ .WillByDefault(
+ Invoke([](const std::string& signal_name, const base::Value& params,
+ SignalsCollector::GetSignalCallback callback) {
+ EXPECT_EQ(params, GetFakeSignalParameter());
+ std::move(callback).Run(GetFakeSignalValue());
+ }));
+
+ return mock_collector;
+}
+
+std::unique_ptr<MockSignalsCollector> GetCollectorForOtherFakeSignal() {
+ auto mock_collector = std::make_unique<MockSignalsCollector>();
+ ON_CALL(*mock_collector.get(), GetSupportedSignalNames())
+ .WillByDefault(
+ Return(std::unordered_set<std::string>({kOtherFakeSignalName})));
+
+ ON_CALL(*mock_collector.get(), GetSignal(kOtherFakeSignalName, _, _))
+ .WillByDefault(
+ Invoke([](const std::string& signal_name, const base::Value& params,
+ SignalsCollector::GetSignalCallback callback) {
+ EXPECT_EQ(params, GetOtherFakeSignalParameter());
+ std::move(callback).Run(GetOtherFakeSignalValue());
+ }));
+
+ return mock_collector;
+}
+
+} // namespace
+
+class SignalsAggregatorImplTest : public testing::Test {
+ protected:
+ SignalsAggregatorImplTest() {
+ auto fake_signal_collector = GetCollectorForFakeSignal();
+ fake_signal_collector_ = fake_signal_collector.get();
+
+ auto other_fake_signal_collector = GetCollectorForOtherFakeSignal();
+ other_fake_signal_collector_ = other_fake_signal_collector.get();
+
+ std::vector<std::unique_ptr<SignalsCollector>> collectors;
+ collectors.push_back(std::move(fake_signal_collector));
+ collectors.push_back(std::move(other_fake_signal_collector));
+ aggregator_ = std::make_unique<SignalsAggregatorImpl>(
+ &mock_permission_service_, std::move(collectors));
+ }
+
+ void GrantUserPermission() {
+ EXPECT_CALL(mock_permission_service_, CanCollectSignals(user_context_, _))
+ .WillOnce([](const UserContext&,
+ UserPermissionService::CanCollectCallback callback) {
+ std::move(callback).Run(UserPermission::kGranted);
+ });
+ }
+
+ base::test::TaskEnvironment task_environment_;
+ raw_ptr<MockSignalsCollector> fake_signal_collector_;
+ raw_ptr<MockSignalsCollector> other_fake_signal_collector_;
+ testing::StrictMock<MockUserPermissionService> mock_permission_service_;
+ UserContext user_context_{kGaiaId};
+ std::unique_ptr<SignalsAggregatorImpl> aggregator_;
+};
+
+// Tests that the aggregator will return an empty value when given an empty
+// parameter dictionary.
+TEST_F(SignalsAggregatorImplTest, GetSignals_NoSignal) {
+ base::test::TestFuture<base::Value> future;
+ base::Value::Dict empty_value;
+ aggregator_->GetSignals(user_context_, std::move(empty_value),
+ future.GetCallback());
+ EXPECT_EQ(future.Get(), base::Value(errors::kUnsupported));
+}
+
+// Tests how the aggregator behaves when given a parameter with a single signal
+// which is supported by one of the collectors.
+TEST_F(SignalsAggregatorImplTest, GetSignals_SingleSignal_Supported) {
+ GrantUserPermission();
+
+ base::Value::Dict parameters;
+ parameters.Set(kFakeSignalName, GetFakeSignalParameter());
+
+ EXPECT_CALL(*fake_signal_collector_, GetSupportedSignalNames()).Times(1);
+ EXPECT_CALL(*fake_signal_collector_, GetSignal(kFakeSignalName, _, _))
+ .Times(1);
+
+ EXPECT_CALL(*other_fake_signal_collector_, GetSignal(_, _, _)).Times(0);
+
+ base::test::TestFuture<base::Value> future;
+ aggregator_->GetSignals(user_context_, std::move(parameters),
+ future.GetCallback());
+
+ base::Value::Dict expected_value;
+ expected_value.Set(kFakeSignalName, GetFakeSignalValue());
+
+ EXPECT_EQ(future.Get(), base::Value(std::move(expected_value)));
+}
+
+// Tests how the aggregator behaves when given a parameter with a single signal
+// which is supported by another collector.
+TEST_F(SignalsAggregatorImplTest, GetSignals_SingleSignal_SupportedOther) {
+ GrantUserPermission();
+
+ base::Value::Dict parameters;
+ parameters.Set(kOtherFakeSignalName, GetOtherFakeSignalParameter());
+
+ EXPECT_CALL(*other_fake_signal_collector_, GetSupportedSignalNames())
+ .Times(1);
+ EXPECT_CALL(*other_fake_signal_collector_,
+ GetSignal(kOtherFakeSignalName, _, _))
+ .Times(1);
+
+ EXPECT_CALL(*fake_signal_collector_, GetSignal(_, _, _)).Times(0);
+
+ base::test::TestFuture<base::Value> future;
+ aggregator_->GetSignals(user_context_, std::move(parameters),
+ future.GetCallback());
+
+ base::Value::Dict expected_value;
+ expected_value.Set(kOtherFakeSignalName, GetOtherFakeSignalValue());
+
+ EXPECT_EQ(future.Get(), base::Value(std::move(expected_value)));
+}
+
+// Tests how the aggregator behaves when given a parameter with a single signal
+// that no collector supports.
+TEST_F(SignalsAggregatorImplTest, GetSignals_SingleSignal_Unsupported) {
+ GrantUserPermission();
+
+ base::Value::Dict parameters;
+ parameters.Set("something unsupported", base::Value());
+
+ base::test::TestFuture<base::Value> future;
+ aggregator_->GetSignals(user_context_, std::move(parameters),
+ future.GetCallback());
+ EXPECT_EQ(future.Get(), base::Value(errors::kUnsupported));
+}
+
+// Tests how the aggregator behaves when encountering user permission errors.
+TEST_F(SignalsAggregatorImplTest, GetSignals_InvalidUserPermissions) {
+ std::map<UserPermission, std::string> permission_to_error_map;
+ permission_to_error_map[UserPermission::kUnaffiliated] =
+ errors::kUnaffiliatedUser;
+ permission_to_error_map[UserPermission::kMissingConsent] =
+ errors::kConsentRequired;
+ permission_to_error_map[UserPermission::kConsumerUser] = errors::kUnsupported;
+ permission_to_error_map[UserPermission::kUnknownUser] = errors::kUnsupported;
+
+ for (const auto& test_case : permission_to_error_map) {
+ EXPECT_CALL(mock_permission_service_, CanCollectSignals(user_context_, _))
+ .WillOnce(
+ [&test_case](const UserContext&,
+ UserPermissionService::CanCollectCallback callback) {
+ std::move(callback).Run(test_case.first);
+ });
+
+ // This value is not important for these test cases.
+ base::Value::Dict parameters;
+ parameters.Set("something unsupported", base::Value());
+
+ base::test::TestFuture<base::Value> future;
+ aggregator_->GetSignals(user_context_, std::move(parameters),
+ future.GetCallback());
+
+ EXPECT_EQ(future.Get(), base::Value(test_case.second));
+ }
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/signals_collector.h b/chromium/components/device_signals/core/browser/signals_collector.h
new file mode 100644
index 00000000000..9c63ae5a151
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/signals_collector.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_COLLECTOR_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_COLLECTOR_H_
+
+#include <string>
+#include <unordered_set>
+
+#include "base/callback_forward.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace device_signals {
+
+class SignalsCollector {
+ public:
+ using GetSignalCallback = base::OnceCallback<void(base::Value)>;
+
+ virtual ~SignalsCollector() = default;
+
+ // Returns the set of signal names that this collector can collect.
+ virtual const std::unordered_set<std::string> GetSupportedSignalNames() = 0;
+
+ // Collects the signal named `signal_name` using `params` (if needed), and
+ // invokes `callback` with the signal value.
+ virtual void GetSignal(const std::string& signal_name,
+ const base::Value& params,
+ GetSignalCallback callback) = 0;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_COLLECTOR_H_
diff --git a/chromium/components/device_signals/core/browser/system_signals_service_host.h b/chromium/components/device_signals/core/browser/system_signals_service_host.h
new file mode 100644
index 00000000000..d20ff25e27b
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/system_signals_service_host.h
@@ -0,0 +1,26 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SYSTEM_SIGNALS_SERVICE_HOST_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SYSTEM_SIGNALS_SERVICE_HOST_H_
+
+#include "components/device_signals/core/common/mojom/system_signals.mojom-forward.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace device_signals {
+
+// Class in charge of creating and handling the service's lifecycle. Clients of
+// SystemSignalsService should always go through a common instance of this class
+// to retrieve a service instance.
+class SystemSignalsServiceHost : public KeyedService {
+ public:
+ ~SystemSignalsServiceHost() override = default;
+
+ // Returns a pointer to the currently available SystemSignalsService instance.
+ virtual device_signals::mojom::SystemSignalsService* GetService() = 0;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_SYSTEM_SIGNALS_SERVICE_HOST_H_
diff --git a/chromium/components/device_signals/core/browser/user_context.cc b/chromium/components/device_signals/core/browser/user_context.cc
new file mode 100644
index 00000000000..ed3d091b247
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/user_context.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/browser/user_context.h"
+
+namespace device_signals {
+
+bool operator==(const UserContext& l, const UserContext& r) {
+ return l.user_id == r.user_id;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/user_context.h b/chromium/components/device_signals/core/browser/user_context.h
new file mode 100644
index 00000000000..7bbeda21d5b
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/user_context.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_CONTEXT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_CONTEXT_H_
+
+#include <string>
+
+namespace device_signals {
+
+struct UserContext {
+ // GAIA ID of the user.
+ std::string user_id;
+};
+
+bool operator==(const UserContext& l, const UserContext& r);
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_CONTEXT_H_
diff --git a/chromium/components/device_signals/core/browser/user_delegate.h b/chromium/components/device_signals/core/browser/user_delegate.h
new file mode 100644
index 00000000000..a98f430be15
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/user_delegate.h
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_DELEGATE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_DELEGATE_H_
+
+struct AccountInfo;
+
+namespace device_signals {
+
+// Delegate representing the user that is currently logged into the browser
+// itself.
+class UserDelegate {
+ public:
+ virtual ~UserDelegate() = default;
+
+ // Returns true if the current user is managed by an organization that is
+ // affiliated with the organization managing the device.
+ virtual bool IsAffiliated() const = 0;
+
+ // Returns true if the user currently logged into the browser is managed and
+ // is the same user as `user`.
+ virtual bool IsSameManagedUser(const AccountInfo& user) const = 0;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_DELEGATE_H_
diff --git a/chromium/components/device_signals/core/browser/user_permission_service.h b/chromium/components/device_signals/core/browser/user_permission_service.h
new file mode 100644
index 00000000000..e82c95cf34a
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/user_permission_service.h
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_PERMISSION_SERVICE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_PERMISSION_SERVICE_H_
+
+#include "base/callback_forward.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace device_signals {
+
+struct UserContext;
+
+enum class UserPermission {
+ // Returned when the user is part of an organization that is not affiliated
+ // with the organization currently managing the browser.
+ kUnaffiliated = 0,
+
+ // Returned when the browser is not managed, but the user is - but the user
+ // has not given their consent for device signals to be collected.
+ kMissingConsent = 1,
+
+ // Returned when the user is not part of any organization.
+ kConsumerUser = 2,
+
+ // Returned when the given user context does not represent any currently
+ // logged-in user.
+ kUnknownUser = 3,
+
+ // Returned when the user is granted permission to the device's signals.
+ kGranted = 4,
+};
+
+// Service that can be used to conduct permission checks on given users. The
+// users may represent a different user than the profile user, and so the
+// permission check is more exhaustive than simple consent check and involves
+// validating the affiliation of the user's organization.
+class UserPermissionService : public KeyedService {
+ public:
+ using CanCollectCallback = base::OnceCallback<void(UserPermission)>;
+
+ ~UserPermissionService() override = default;
+
+ // Will asynchronously verify whether context-aware signals can be collected
+ // on behalf of the user represented by `user_context`. The determined user
+ // permission is returned via `callback`.
+ virtual void CanCollectSignals(const UserContext& user_context,
+ CanCollectCallback callback) = 0;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_PERMISSION_SERVICE_H_
diff --git a/chromium/components/device_signals/core/browser/user_permission_service_impl.cc b/chromium/components/device_signals/core/browser/user_permission_service_impl.cc
new file mode 100644
index 00000000000..c2ac146f9ff
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/user_permission_service_impl.cc
@@ -0,0 +1,80 @@
+// Copyright 2022 The Chromium 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/device_signals/core/browser/user_permission_service_impl.h"
+
+#include "base/check.h"
+#include "components/device_signals/core/browser/user_context.h"
+#include "components/device_signals/core/browser/user_delegate.h"
+#include "components/policy/core/common/management/management_service.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+
+namespace device_signals {
+
+UserPermissionServiceImpl::UserPermissionServiceImpl(
+ signin::IdentityManager* identity_manager,
+ policy::ManagementService* management_service,
+ std::unique_ptr<UserDelegate> user_delegate)
+ : identity_manager_(identity_manager),
+ management_service_(management_service),
+ user_delegate_(std::move(user_delegate)) {
+ DCHECK(identity_manager_);
+ DCHECK(management_service_);
+ DCHECK(user_delegate_);
+}
+
+UserPermissionServiceImpl::~UserPermissionServiceImpl() = default;
+
+void UserPermissionServiceImpl::CanCollectSignals(
+ const UserContext& user_context,
+ UserPermissionService::CanCollectCallback callback) {
+ // Return "unknown user" if the user ID is invalid, or does not represent a
+ // logged-in user.
+ if (user_context.user_id.empty()) {
+ std::move(callback).Run(UserPermission::kUnknownUser);
+ return;
+ }
+
+ // TODO(b:233250828): Verify this function covers the required use cases.
+ auto account_info =
+ identity_manager_->FindExtendedAccountInfoByGaiaId(user_context.user_id);
+
+ if (account_info.IsEmpty()) {
+ std::move(callback).Run(UserPermission::kUnknownUser);
+ return;
+ }
+
+ // Return "consumer user" if the user is not managed by an organization.
+ if (!account_info.IsManaged()) {
+ std::move(callback).Run(UserPermission::kConsumerUser);
+ return;
+ }
+
+ // Automatically return "missing consent" if the browser is not managed. This
+ // specific check is temporary until there is a flow in Chrome to gather user
+ // consent, at which point lack of browser management would lead into a user
+ // consent check.
+ if (!management_service_->HasManagementAuthority(
+ policy::EnterpriseManagementAuthority::CLOUD_DOMAIN)) {
+ std::move(callback).Run(UserPermission::kMissingConsent);
+ return;
+ }
+
+ // If the account info represents the current browser user (e.g. Profile
+ // user), verify if that user is part of an affiliated organization.
+ if (user_delegate_->IsSameManagedUser(account_info)) {
+ UserPermission permission = user_delegate_->IsAffiliated()
+ ? UserPermission::kGranted
+ : UserPermission::kUnaffiliated;
+ std::move(callback).Run(permission);
+ return;
+ }
+
+ // TODO(b:232269863): Fetch customer IDs for that user by calling the DM
+ // server, then cache results here (and clear cache when the account is
+ // logged-out).
+ std::move(callback).Run(UserPermission::kUnaffiliated);
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/user_permission_service_impl.h b/chromium/components/device_signals/core/browser/user_permission_service_impl.h
new file mode 100644
index 00000000000..3e77b195987
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/user_permission_service_impl.h
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_PERMISSION_SERVICE_IMPL_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_PERMISSION_SERVICE_IMPL_H_
+
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/device_signals/core/browser/user_permission_service.h"
+
+namespace policy {
+class ManagementService;
+} // namespace policy
+
+namespace signin {
+class IdentityManager;
+} // namespace signin
+
+namespace device_signals {
+
+class UserDelegate;
+
+class UserPermissionServiceImpl : public UserPermissionService {
+ public:
+ UserPermissionServiceImpl(signin::IdentityManager* identity_manager,
+ policy::ManagementService* management_service,
+ std::unique_ptr<UserDelegate> user_delegate);
+
+ UserPermissionServiceImpl(const UserPermissionServiceImpl&) = delete;
+ UserPermissionServiceImpl& operator=(const UserPermissionServiceImpl&) =
+ delete;
+
+ ~UserPermissionServiceImpl() override;
+
+ // UserPermissionService:
+ void CanCollectSignals(const UserContext& user_context,
+ CanCollectCallback callback) override;
+
+ private:
+ base::raw_ptr<signin::IdentityManager> identity_manager_;
+ base::raw_ptr<policy::ManagementService> management_service_;
+ std::unique_ptr<UserDelegate> user_delegate_;
+
+ base::WeakPtrFactory<UserPermissionServiceImpl> weak_factory_{this};
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_PERMISSION_SERVICE_IMPL_H_
diff --git a/chromium/components/device_signals/core/browser/user_permission_service_impl_unittest.cc b/chromium/components/device_signals/core/browser/user_permission_service_impl_unittest.cc
new file mode 100644
index 00000000000..d6579051c74
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/user_permission_service_impl_unittest.cc
@@ -0,0 +1,201 @@
+// Copyright 2022 The Chromium 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/device_signals/core/browser/user_permission_service_impl.h"
+
+#include "base/memory/raw_ptr.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "components/device_signals/core/browser/mock_user_delegate.h"
+#include "components/device_signals/core/browser/user_context.h"
+#include "components/device_signals/core/browser/user_delegate.h"
+#include "components/policy/core/common/management/management_service.h"
+#include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
+#include "components/signin/public/identity_manager/account_info.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using policy::EnterpriseManagementAuthority;
+using policy::ScopedManagementServiceOverrideForTesting;
+using testing::_;
+using testing::Return;
+
+namespace device_signals {
+
+namespace {
+
+constexpr char kUserGaiaId[] = "some-gaia-id";
+constexpr char kUserEmail[] = "user@abc.com";
+constexpr char kHostedDomain[] = "example.com";
+
+class TestManagementService : public policy::ManagementService {
+ public:
+ TestManagementService() : ManagementService({}) {}
+ explicit TestManagementService(
+ std::vector<std::unique_ptr<policy::ManagementStatusProvider>> providers)
+ : ManagementService(std::move(providers)) {}
+ void SetManagementStatusProviderForTesting(
+ std::vector<std::unique_ptr<policy::ManagementStatusProvider>>
+ providers) {
+ SetManagementStatusProvider(std::move(providers));
+ }
+};
+
+} // namespace
+
+class UserPermissionServiceImplTest : public testing::Test {
+ protected:
+ UserPermissionServiceImplTest()
+ : scoped_override_(&management_service_,
+ EnterpriseManagementAuthority::CLOUD_DOMAIN) {
+ auto mock_user_delegate =
+ std::make_unique<testing::StrictMock<MockUserDelegate>>();
+ mock_user_delegate_ = mock_user_delegate.get();
+
+ permission_service_ = std::make_unique<UserPermissionServiceImpl>(
+ identity_test_env_.identity_manager(), &management_service_,
+ std::move(mock_user_delegate));
+ }
+
+ base::test::TaskEnvironment task_environment_;
+
+ signin::IdentityTestEnvironment identity_test_env_;
+ TestManagementService management_service_;
+ ScopedManagementServiceOverrideForTesting scoped_override_;
+ raw_ptr<testing::StrictMock<MockUserDelegate>> mock_user_delegate_;
+
+ std::unique_ptr<UserPermissionServiceImpl> permission_service_;
+};
+
+// Tests CanCollectSignals with a missing user ID.
+TEST_F(UserPermissionServiceImplTest, CanCollectSignals_EmptyUserId) {
+ base::test::TestFuture<UserPermission> future;
+ UserContext user_context;
+ permission_service_->CanCollectSignals(user_context, future.GetCallback());
+ EXPECT_EQ(future.Get(), UserPermission::kUnknownUser);
+}
+
+// Tests CanCollectSignals with a user ID that does not represent a valid user.
+TEST_F(UserPermissionServiceImplTest, CanCollectSignals_UserId_NoUser) {
+ base::test::TestFuture<UserPermission> future;
+ UserContext user_context;
+ user_context.user_id = kUserGaiaId;
+ permission_service_->CanCollectSignals(user_context, future.GetCallback());
+ EXPECT_EQ(future.Get(), UserPermission::kUnknownUser);
+}
+
+// Tests CanCollectSignals with a user ID that does not represent a managed
+// user.
+TEST_F(UserPermissionServiceImplTest, CanCollectSignals_User_NotManaged) {
+ // Create known account.
+ AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
+ kUserEmail, kUserGaiaId);
+
+ // Make sure there is no hosted domain.
+ account.hosted_domain = "";
+ identity_test_env_.UpdateAccountInfoForAccount(account);
+
+ base::test::TestFuture<UserPermission> future;
+ UserContext user_context;
+ user_context.user_id = account.gaia;
+ permission_service_->CanCollectSignals(user_context, future.GetCallback());
+ EXPECT_EQ(future.Get(), UserPermission::kConsumerUser);
+}
+
+// Tests CanCollectSignals with a managed user ID but the browser is not
+// managed.
+TEST_F(UserPermissionServiceImplTest, CanCollectSignals_BrowserNotManaged) {
+ // Set management to something other than CLOUD_DOMAIN.
+ ScopedManagementServiceOverrideForTesting another_scope(
+ &management_service_, EnterpriseManagementAuthority::CLOUD);
+
+ // Create known account.
+ AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
+ kUserEmail, kUserGaiaId);
+
+ // Make sure there is a hosted domain.
+ account.hosted_domain = kHostedDomain;
+ identity_test_env_.UpdateAccountInfoForAccount(account);
+
+ base::test::TestFuture<UserPermission> future;
+ UserContext user_context;
+ user_context.user_id = account.gaia;
+ permission_service_->CanCollectSignals(user_context, future.GetCallback());
+ EXPECT_EQ(future.Get(), UserPermission::kMissingConsent);
+}
+
+// Tests CanCollectSignals with a managed user ID and the browser is managed,
+// where the user is the same as the profile user but it is not affiliated with
+// the browser's org.
+TEST_F(UserPermissionServiceImplTest,
+ CanCollectSignals_BrowserManaged_ProfileUser_Unaffiliated) {
+ // Create known account.
+ AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
+ kUserEmail, kUserGaiaId);
+
+ // Make sure there is a hosted domain.
+ account.hosted_domain = kHostedDomain;
+ identity_test_env_.UpdateAccountInfoForAccount(account);
+
+ EXPECT_CALL(*mock_user_delegate_, IsSameManagedUser(account))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_user_delegate_, IsAffiliated()).WillOnce(Return(false));
+
+ base::test::TestFuture<UserPermission> future;
+ UserContext user_context;
+ user_context.user_id = account.gaia;
+ permission_service_->CanCollectSignals(user_context, future.GetCallback());
+ EXPECT_EQ(future.Get(), UserPermission::kUnaffiliated);
+}
+
+// Tests CanCollectSignals with a managed user ID and the browser is managed,
+// where the user is the same as the profile user and it is affiliated with the
+// browser's org.
+TEST_F(UserPermissionServiceImplTest,
+ CanCollectSignals_BrowserManaged_ProfileUser_Affiliated) {
+ // Create known account.
+ AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
+ kUserEmail, kUserGaiaId);
+
+ // Make sure there is a hosted domain.
+ account.hosted_domain = kHostedDomain;
+ identity_test_env_.UpdateAccountInfoForAccount(account);
+
+ EXPECT_CALL(*mock_user_delegate_, IsSameManagedUser(account))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_user_delegate_, IsAffiliated()).WillOnce(Return(true));
+
+ base::test::TestFuture<UserPermission> future;
+ UserContext user_context;
+ user_context.user_id = account.gaia;
+ permission_service_->CanCollectSignals(user_context, future.GetCallback());
+ EXPECT_EQ(future.Get(), UserPermission::kGranted);
+}
+
+// Tests CanCollectSignals with a managed user ID and the browser is managed,
+// but the user is not the Profile user.
+// This is missing the remote affiliation check at the moment an defaults to
+// "unaffiliated".
+TEST_F(UserPermissionServiceImplTest,
+ CanCollectSignals_BrowserManaged_NotProfile) {
+ // Create known account.
+ AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
+ kUserEmail, kUserGaiaId);
+
+ // Make sure there is a hosted domain.
+ account.hosted_domain = kHostedDomain;
+ identity_test_env_.UpdateAccountInfoForAccount(account);
+
+ EXPECT_CALL(*mock_user_delegate_, IsSameManagedUser(account))
+ .WillOnce(Return(false));
+
+ base::test::TestFuture<UserPermission> future;
+ UserContext user_context;
+ user_context.user_id = account.gaia;
+ permission_service_->CanCollectSignals(user_context, future.GetCallback());
+ EXPECT_EQ(future.Get(), UserPermission::kUnaffiliated);
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/win/BUILD.gn b/chromium/components/device_signals/core/browser/win/BUILD.gn
new file mode 100644
index 00000000000..61a4e9910ea
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/win/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("win") {
+ public = [ "win_signals_collector.h" ]
+
+ sources = [ "win_signals_collector.cc" ]
+
+ public_deps = [
+ "//base",
+ "//components/device_signals/core/browser",
+ ]
+
+ deps = [ "//components/device_signals/core/common" ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "win_signals_collector_unittest.cc" ]
+
+ deps = [
+ ":win",
+ "//base/test:test_support",
+ "//components/device_signals/core/browser:test_support",
+ "//components/device_signals/core/common",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/device_signals/core/browser/win/win_signals_collector.cc b/chromium/components/device_signals/core/browser/win/win_signals_collector.cc
new file mode 100644
index 00000000000..67ce37a9476
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/win/win_signals_collector.cc
@@ -0,0 +1,102 @@
+// Copyright 2022 The Chromium 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/device_signals/core/browser/win/win_signals_collector.h"
+
+#include "base/bind.h"
+#include "base/check.h"
+#include "base/values.h"
+#include "components/device_signals/core/browser/system_signals_service_host.h"
+#include "components/device_signals/core/common/mojom/system_signals.mojom.h"
+#include "components/device_signals/core/common/signals_constants.h"
+
+namespace device_signals {
+
+WinSignalsCollector::WinSignalsCollector(
+ SystemSignalsServiceHost* system_service_host)
+ : system_service_host_(system_service_host),
+ signals_collection_map_({
+ {names::kAntiVirusInfo,
+ base::BindRepeating(&WinSignalsCollector::GetAntiVirusSignal,
+ base::Unretained(this))},
+ {names::kInstalledHotfixes,
+ base::BindRepeating(&WinSignalsCollector::GetHotfixSignal,
+ base::Unretained(this))},
+ }) {
+ DCHECK(system_service_host_);
+}
+
+WinSignalsCollector::~WinSignalsCollector() = default;
+
+const std::unordered_set<std::string>
+WinSignalsCollector::GetSupportedSignalNames() {
+ std::unordered_set<std::string> supported_signals;
+ for (const auto& collection_pair : signals_collection_map_) {
+ supported_signals.insert(collection_pair.first);
+ }
+
+ return supported_signals;
+}
+
+void WinSignalsCollector::GetSignal(const std::string& signal_name,
+ const base::Value& params,
+ GetSignalCallback callback) {
+ const auto it = signals_collection_map_.find(signal_name);
+ if (it == signals_collection_map_.end()) {
+ std::move(callback).Run(base::Value(errors::kUnsupported));
+ return;
+ }
+
+ it->second.Run(params, std::move(callback));
+}
+
+void WinSignalsCollector::GetAntiVirusSignal(const base::Value& params,
+ GetSignalCallback callback) {
+ auto* system_signals_service = system_service_host_->GetService();
+ if (!system_signals_service) {
+ std::move(callback).Run(base::Value(errors::kMissingSystemService));
+ return;
+ }
+
+ system_signals_service->GetAntiVirusSignals(
+ base::BindOnce(&WinSignalsCollector::OnAntiVirusSignalCollected,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void WinSignalsCollector::OnAntiVirusSignalCollected(
+ GetSignalCallback callback,
+ const std::vector<AvProduct>& av_products) {
+ base::Value::List av_values;
+ for (const auto& av_product : av_products) {
+ av_values.Append(av_product.ToValue());
+ }
+
+ std::move(callback).Run(base::Value(std::move(av_values)));
+}
+
+void WinSignalsCollector::GetHotfixSignal(const base::Value& params,
+ GetSignalCallback callback) {
+ auto* system_signals_service = system_service_host_->GetService();
+ if (!system_signals_service) {
+ std::move(callback).Run(base::Value(errors::kMissingSystemService));
+ return;
+ }
+
+ system_signals_service->GetHotfixSignals(
+ base::BindOnce(&WinSignalsCollector::OnHotfixSignalCollected,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void WinSignalsCollector::OnHotfixSignalCollected(
+ GetSignalCallback callback,
+ const std::vector<InstalledHotfix>& hotfixes) {
+ base::Value::List hotfix_values;
+ for (const auto& hotfix : hotfixes) {
+ hotfix_values.Append(hotfix.ToValue());
+ }
+
+ std::move(callback).Run(base::Value(std::move(hotfix_values)));
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/win/win_signals_collector.h b/chromium/components/device_signals/core/browser/win/win_signals_collector.h
new file mode 100644
index 00000000000..c80f6e8be94
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/win/win_signals_collector.h
@@ -0,0 +1,78 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_WIN_WIN_SIGNALS_COLLECTOR_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_WIN_WIN_SIGNALS_COLLECTOR_H_
+
+#include <map>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/device_signals/core/browser/signals_collector.h"
+#include "components/device_signals/core/common/win/win_types.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace device_signals {
+
+class SystemSignalsServiceHost;
+
+// Collector in charge of collecting device signals that are either specific to
+// Windows, or require a specific Windows-only implementation.
+class WinSignalsCollector : public SignalsCollector {
+ public:
+ explicit WinSignalsCollector(SystemSignalsServiceHost* system_service_host);
+
+ ~WinSignalsCollector() override;
+
+ WinSignalsCollector(const WinSignalsCollector&) = delete;
+ WinSignalsCollector& operator=(const WinSignalsCollector&) = delete;
+
+ // SignalsCollector:
+ const std::unordered_set<std::string> GetSupportedSignalNames() override;
+ void GetSignal(const std::string& signal_name,
+ const base::Value& params,
+ GetSignalCallback callback) override;
+
+ private:
+ // Collection function for the AV signal. `params` is ignored since AV signal
+ // does not require parameters. `callback` will be invoked when the signal
+ // values are available, or when something went wrong.
+ void GetAntiVirusSignal(const base::Value& params,
+ GetSignalCallback callback);
+
+ // Invoked when the SystemSignalsService returns the collected AV signals as
+ // `av_products`. Will convert the structs to base::Value and invoke
+ // `callback`.
+ void OnAntiVirusSignalCollected(GetSignalCallback callback,
+ const std::vector<AvProduct>& av_products);
+
+ // Collection function for the Hotfix signal. `params` is ignored since Hotfix
+ // signal does not require parameters. `callback` will be invoked when the
+ // signal values are available, or when something went wrong.
+ void GetHotfixSignal(const base::Value& params, GetSignalCallback callback);
+
+ // Invoked when the SystemSignalsService returns the collected Hotfix signals
+ // as `hotfixes`. Will convert the structs to base::Value and invoke
+ // `callback`.
+ void OnHotfixSignalCollected(GetSignalCallback callback,
+ const std::vector<InstalledHotfix>& hotfixes);
+
+ // Instance used to retrieve a pointer to a SystemSignalsService instance.
+ base::raw_ptr<SystemSignalsServiceHost> system_service_host_;
+
+ // Map used to forward signal collection requests to the right function keyed
+ // from a given signal name.
+ std::map<const std::string,
+ base::RepeatingCallback<void(const base::Value&, GetSignalCallback)>>
+ signals_collection_map_;
+
+ base::WeakPtrFactory<WinSignalsCollector> weak_factory_{this};
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_WIN_WIN_SIGNALS_COLLECTOR_H_
diff --git a/chromium/components/device_signals/core/browser/win/win_signals_collector_unittest.cc b/chromium/components/device_signals/core/browser/win/win_signals_collector_unittest.cc
new file mode 100644
index 00000000000..cd95070e9f9
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/win/win_signals_collector_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2022 The Chromium 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/device_signals/core/browser/win/win_signals_collector.h"
+
+#include <array>
+
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "base/values.h"
+#include "components/device_signals/core/browser/mock_system_signals_service_host.h"
+#include "components/device_signals/core/common/signals_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace device_signals {
+
+using GetAntiVirusSignalsCallback =
+ MockSystemSignalsService::GetAntiVirusSignalsCallback;
+using GetHotfixSignalsCallback =
+ MockSystemSignalsService::GetHotfixSignalsCallback;
+
+class WinSignalsCollectorTest : public testing::Test {
+ protected:
+ WinSignalsCollectorTest() : win_collector_(&service_host_) {
+ ON_CALL(service_host_, GetService()).WillByDefault(Return(&service_));
+ }
+
+ base::test::TaskEnvironment task_environment_;
+
+ StrictMock<MockSystemSignalsServiceHost> service_host_;
+ StrictMock<MockSystemSignalsService> service_;
+ WinSignalsCollector win_collector_;
+};
+
+// Test that runs a sanity check on the set of signals supported by this
+// collector. Will need to be updated if new signals become supported.
+TEST_F(WinSignalsCollectorTest, SupportedSignalNames) {
+ const std::array<std::string, 2> supported_signals{
+ {names::kAntiVirusInfo, names::kInstalledHotfixes}};
+
+ const auto names_set = win_collector_.GetSupportedSignalNames();
+
+ EXPECT_EQ(names_set.size(), supported_signals.size());
+ for (const auto& signal_name : supported_signals) {
+ EXPECT_TRUE(names_set.find(signal_name) != names_set.end());
+ }
+}
+
+// Tests that an unsupported signal is marked as unsupported.
+TEST_F(WinSignalsCollectorTest, GetSignal_Unsupported) {
+ base::test::TestFuture<base::Value> future;
+ win_collector_.GetSignal(names::kDeviceId, base::Value(),
+ future.GetCallback());
+ EXPECT_EQ(future.Get(), base::Value(errors::kUnsupported));
+}
+
+// Tests that not being able to retrieve a pointer to the SystemSignalsService
+// returns an error.
+TEST_F(WinSignalsCollectorTest, GetSignal_MissingSystemSignalsService) {
+ for (const auto& signal_name : win_collector_.GetSupportedSignalNames()) {
+ EXPECT_CALL(service_host_, GetService()).WillOnce(Return(nullptr));
+
+ base::test::TestFuture<base::Value> future;
+ win_collector_.GetSignal(signal_name, base::Value(), future.GetCallback());
+ EXPECT_EQ(future.Get(), base::Value(errors::kMissingSystemService));
+ }
+}
+
+// Tests a successful AV signal retrieval.
+TEST_F(WinSignalsCollectorTest, GetSignal_AV) {
+ std::vector<AvProduct> av_products;
+ av_products.push_back(
+ {"AV Product Name", AvProductState::kOn, "some product id"});
+
+ EXPECT_CALL(service_host_, GetService()).Times(1);
+ EXPECT_CALL(service_, GetAntiVirusSignals(_))
+ .WillOnce(
+ Invoke([&av_products](GetAntiVirusSignalsCallback signal_callback) {
+ std::move(signal_callback).Run(av_products);
+ }));
+
+ base::test::TestFuture<base::Value> future;
+ win_collector_.GetSignal(names::kAntiVirusInfo, base::Value(),
+ future.GetCallback());
+
+ const base::Value& response = future.Get();
+ ASSERT_TRUE(response.is_list());
+ const base::Value::List& list_value = response.GetList();
+ ASSERT_EQ(list_value.size(), av_products.size());
+ EXPECT_EQ(list_value[0], av_products[0].ToValue());
+}
+
+// Tests a successful hotfix signal retrieval.
+TEST_F(WinSignalsCollectorTest, GetSignal_Hotfix) {
+ std::vector<InstalledHotfix> hotfixes;
+ hotfixes.push_back({"hotfix id"});
+
+ EXPECT_CALL(service_host_, GetService()).Times(1);
+ EXPECT_CALL(service_, GetHotfixSignals(_))
+ .WillOnce(Invoke([&hotfixes](GetHotfixSignalsCallback signal_callback) {
+ std::move(signal_callback).Run(hotfixes);
+ }));
+
+ base::test::TestFuture<base::Value> future;
+ win_collector_.GetSignal(names::kInstalledHotfixes, base::Value(),
+ future.GetCallback());
+
+ const base::Value& response = future.Get();
+ ASSERT_TRUE(response.is_list());
+ const base::Value::List& list_value = response.GetList();
+ ASSERT_EQ(list_value.size(), hotfixes.size());
+ EXPECT_EQ(list_value[0], hotfixes[0].ToValue());
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/BUILD.gn b/chromium/components/device_signals/core/common/BUILD.gn
new file mode 100644
index 00000000000..3d707b775e4
--- /dev/null
+++ b/chromium/components/device_signals/core/common/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("common") {
+ public = [ "signals_constants.h" ]
+
+ sources = [ "signals_constants.cc" ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ if (is_win) {
+ deps = [ "//components/device_signals/core/common/win:unit_tests" ]
+ }
+}
diff --git a/chromium/components/device_signals/core/common/mojom/BUILD.gn b/chromium/components/device_signals/core/common/mojom/BUILD.gn
new file mode 100644
index 00000000000..2d9e2cda1f0
--- /dev/null
+++ b/chromium/components/device_signals/core/common/mojom/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2022 The Chromium 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("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+ sources = [ "system_signals.mojom" ]
+
+ public_deps = [
+ "//mojo/public/mojom/base",
+ "//sandbox/policy/mojom",
+ ]
+
+ if (is_win) {
+ cpp_typemaps = [
+ {
+ types = [
+ {
+ mojom = "device_signals.mojom.AntiVirusProductState"
+ cpp = "::device_signals::AvProductState"
+ },
+ {
+ mojom = "device_signals.mojom.AntiVirusSignal"
+ cpp = "::device_signals::AvProduct"
+ },
+ {
+ mojom = "device_signals.mojom.HotfixSignal"
+ cpp = "::device_signals::InstalledHotfix"
+ },
+ ]
+ traits_headers = [ "system_signals_mojom_traits.h" ]
+ traits_sources = [ "system_signals_mojom_traits.cc" ]
+ traits_public_deps = [
+ "//base",
+ "//components/device_signals/core/common/win",
+ ]
+ },
+ ]
+ }
+}
diff --git a/chromium/components/device_signals/core/common/mojom/OWNERS b/chromium/components/device_signals/core/common/mojom/OWNERS
new file mode 100644
index 00000000000..f486cf5abe0
--- /dev/null
+++ b/chromium/components/device_signals/core/common/mojom/OWNERS
@@ -0,0 +1,5 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/device_signals/core/common/mojom/system_signals.mojom b/chromium/components/device_signals/core/common/mojom/system_signals.mojom
new file mode 100644
index 00000000000..f335e665684
--- /dev/null
+++ b/chromium/components/device_signals/core/common/mojom/system_signals.mojom
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module device_signals.mojom;
+
+import "mojo/public/mojom/base/file_path.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
+
+enum TrileanValue {
+ kFalse,
+ kTrue,
+ kUnknown
+};
+
+struct BinarySignalsRequest {
+ mojo_base.mojom.FilePath file_path;
+};
+
+struct BinarySignalsResponse {
+ mojo_base.mojom.FilePath file_path;
+
+ bool is_running;
+
+ [EnableIfNot=is_linux]
+ string product_name;
+
+ [EnableIfNot=is_linux]
+ string public_key_sha256;
+
+ [EnableIfNot=is_linux]
+ string version;
+};
+
+[EnableIf=is_win]
+enum AntiVirusProductState {
+ // The security product software is turned on and protecting the user.
+ kOn,
+ // The security product software is turned off and protection is disabled.
+ kOff,
+ // The security product software is in the snoozed state, temporarily off,
+ // and not actively protecting the computer.
+ kSnoozed,
+ // The security product software is expired and does not have the latest
+ // updates.
+ kExpired
+};
+
+[EnableIf=is_win]
+struct AntiVirusSignal {
+ string display_name;
+ string product_id;
+ AntiVirusProductState state;
+};
+
+[EnableIf=is_win]
+struct HotfixSignal {
+ string hotfix_id;
+};
+
+// Service in charge of collecting a specific set of device signals. The source
+// of these signals is platform-specific and, in some cases (i.e. Windows), may
+// need to be run a separate process.
+// This service can be accessed from the browser process.
+[ServiceSandbox=sandbox.mojom.Sandbox.kNoSandbox]
+interface SystemSignalsService {
+ // Collects signals about a set of binary files specified by `requests`.
+ // Returns the collected information in `response`.
+ GetBinarySignals(array<BinarySignalsRequest> requests)
+ => (array<BinarySignalsResponse> response);
+
+ // Collects information about AntiVirus software installed on the current
+ // device. Returns that information via `av_signals`.
+ [EnableIf=is_win]
+ GetAntiVirusSignals() => (array<AntiVirusSignal> av_signals);
+
+ // Collects information about hotfixes that were installed on the device.
+ // Returns that information via `hotfix_signals`.
+ [EnableIf=is_win]
+ GetHotfixSignals() => (array<HotfixSignal> hotfix_signals);
+};
diff --git a/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc
new file mode 100644
index 00000000000..22daec5f4ce
--- /dev/null
+++ b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc
@@ -0,0 +1,95 @@
+// Copyright 2022 The Chromium 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/device_signals/core/common/mojom/system_signals_mojom_traits.h"
+
+#include "base/notreached.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace mojo {
+
+// static
+device_signals::mojom::AntiVirusProductState EnumTraits<
+ device_signals::mojom::AntiVirusProductState,
+ device_signals::AvProductState>::ToMojom(device_signals::AvProductState
+ input) {
+ switch (input) {
+ case device_signals::AvProductState::kOn:
+ return device_signals::mojom::AntiVirusProductState::kOn;
+ case device_signals::AvProductState::kOff:
+ return device_signals::mojom::AntiVirusProductState::kOff;
+ case device_signals::AvProductState::kSnoozed:
+ return device_signals::mojom::AntiVirusProductState::kSnoozed;
+ case device_signals::AvProductState::kExpired:
+ return device_signals::mojom::AntiVirusProductState::kExpired;
+ }
+}
+
+// static
+bool EnumTraits<device_signals::mojom::AntiVirusProductState,
+ device_signals::AvProductState>::
+ FromMojom(device_signals::mojom::AntiVirusProductState input,
+ device_signals::AvProductState* output) {
+ absl::optional<device_signals::AvProductState> parsed_state;
+ switch (input) {
+ case device_signals::mojom::AntiVirusProductState::kOn:
+ parsed_state = device_signals::AvProductState::kOn;
+ break;
+ case device_signals::mojom::AntiVirusProductState::kOff:
+ parsed_state = device_signals::AvProductState::kOff;
+ break;
+ case device_signals::mojom::AntiVirusProductState::kSnoozed:
+ parsed_state = device_signals::AvProductState::kSnoozed;
+ break;
+ case device_signals::mojom::AntiVirusProductState::kExpired:
+ parsed_state = device_signals::AvProductState::kExpired;
+ break;
+ }
+
+ if (parsed_state.has_value()) {
+ *output = parsed_state.value();
+ return true;
+ }
+ return false;
+}
+
+// static
+bool StructTraits<device_signals::mojom::AntiVirusSignalDataView,
+ device_signals::AvProduct>::
+ Read(device_signals::mojom::AntiVirusSignalDataView data,
+ device_signals::AvProduct* output) {
+ std::string display_name;
+ if (!data.ReadDisplayName(&display_name)) {
+ return false;
+ }
+ output->display_name = display_name;
+
+ std::string product_id;
+ if (!data.ReadProductId(&product_id)) {
+ return false;
+ }
+ output->product_id = product_id;
+
+ device_signals::AvProductState state;
+ if (!data.ReadState(&state)) {
+ return false;
+ }
+ output->state = state;
+ return true;
+}
+
+// static
+bool StructTraits<device_signals::mojom::HotfixSignalDataView,
+ device_signals::InstalledHotfix>::
+ Read(device_signals::mojom::HotfixSignalDataView data,
+ device_signals::InstalledHotfix* output) {
+ std::string hotfix_id;
+ if (!data.ReadHotfixId(&hotfix_id)) {
+ return false;
+ }
+ output->hotfix_id = hotfix_id;
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.h b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.h
new file mode 100644
index 00000000000..7b1f5469a03
--- /dev/null
+++ b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.h
@@ -0,0 +1,59 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_H_
+
+#include <string>
+
+#include "components/device_signals/core/common/mojom/system_signals.mojom-shared.h"
+#include "components/device_signals/core/common/win/win_types.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<device_signals::mojom::AntiVirusProductState,
+ device_signals::AvProductState> {
+ static device_signals::mojom::AntiVirusProductState ToMojom(
+ device_signals::AvProductState input);
+ static bool FromMojom(device_signals::mojom::AntiVirusProductState input,
+ device_signals::AvProductState* output);
+};
+
+template <>
+struct StructTraits<device_signals::mojom::AntiVirusSignalDataView,
+ device_signals::AvProduct> {
+ static const std::string& display_name(
+ const device_signals::AvProduct& input) {
+ return input.display_name;
+ }
+
+ static const std::string& product_id(const device_signals::AvProduct& input) {
+ return input.product_id;
+ }
+
+ static device_signals::AvProductState state(
+ const device_signals::AvProduct& input) {
+ return input.state;
+ }
+
+ static bool Read(device_signals::mojom::AntiVirusSignalDataView data,
+ device_signals::AvProduct* output);
+};
+
+template <>
+struct StructTraits<device_signals::mojom::HotfixSignalDataView,
+ device_signals::InstalledHotfix> {
+ static const std::string& hotfix_id(
+ const device_signals::InstalledHotfix& input) {
+ return input.hotfix_id;
+ }
+
+ static bool Read(device_signals::mojom::HotfixSignalDataView input,
+ device_signals::InstalledHotfix* output);
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_H_ \ No newline at end of file
diff --git a/chromium/components/device_signals/core/common/signals_constants.cc b/chromium/components/device_signals/core/common/signals_constants.cc
new file mode 100644
index 00000000000..021c7b759fb
--- /dev/null
+++ b/chromium/components/device_signals/core/common/signals_constants.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/signals_constants.h"
+
+namespace device_signals {
+
+namespace names {
+
+// Name of the signal for getting information of the AllowScreenLock
+// policy https://chromeenterprise.google/policies/?policy=AllowScreenLock.
+const char kAllowScreenLock[] = "allowSreenLock";
+
+// Name of the signal for getting information about AV software installed on
+// the device.
+const char kAntiVirusInfo[] = "antiVirusInfo";
+
+// Name of the signal for getting information about the browser version.
+const char kBrowserVersion[] = "browserVersion";
+
+// Name of the signal for getting information about whether a built in
+// dns is enabled on the device.
+const char kBuiltInDnsClientEnabled[] = "builtInDnsClientEnabled";
+
+// Name of the signal for getting information about whether chrome cleanup
+// is enabled on the device.
+const char kChromeCleanupEnabled[] = "chromeCleanupEnabled";
+
+// Name of the signal for getting information about the device id.
+const char kDeviceId[] = "deviceId";
+
+// Name of the signal for getting information about the device
+// manufacturer (e.g. Dell).
+const char kDeviceManufacturer[] = "deviceManufacturer";
+
+// Name of the signal for getting information about the device model
+// (e.g. iPhone 12 Max).
+const char kDeviceModel[] = "deviceModel";
+
+// Name of the signal for getting information about the human readable
+// name for the device.
+const char kDisplayName[] = "displayName";
+
+// Name of the signal for getting information about the dns address of
+// the device.
+const char kDnsAddress[] = "dnsAddress";
+
+// Name of the signal for getting information about the CBCM enrollment
+// domain of the browser.
+const char kEnrollmentDomain[] = "enrollmentDomain";
+
+// Name of the parameterized signal for getting information from resources
+// stored on the file system. This includes the presence/absence of
+// files/folders, and also additional signals' extraction from executables.
+const char kFileSystemInfo[] = "fileSystemInfo";
+
+// Name of the signal for getting information about whether firewall is
+// enabled on the device.
+const char kFirewallOn[] = "firewallOn";
+
+// Name of the signal for getting information about the IMEI.
+const char kImei[] = "imei";
+
+// Name of the signal for getting information about installed hotfixes on
+// the device.
+const char kInstalledHotfixes[] = "hotfixes";
+
+// Name of the signal for getting information about whether the disk
+// on the device is encrypted.
+const char kIsDiskEncrypted[] = "isDiskEncrypted";
+
+// Name of the signal for getting information about whether the device is
+// jailbroken or modified.
+const char kIsJailbroken[] = "isJailBroken";
+
+// Name of the signal for getting information about whether access to
+// the OS user is protected by a password.
+const char kIsPasswordProtected[] = "isProtectedByPassword";
+
+// Name of the signal for getting information about the MEID.
+const char kMeid[] = "meid";
+
+// Name of the signal for getting information about the obfuscated CBCM
+// enrolled customer Id.
+const char kObfuscatedCustomerId[] = "obfuscatedCustomerId";
+
+// Name of the signal for getting information about the OS running
+// on the device (e.g. Chrome OS).
+const char kOs[] = "os";
+
+// Name of the signal for getting information about the OS version
+// of the device (e.g. macOS 10.15.7).
+const char kOsVersion[] = "osVersion";
+
+// Name of the signal for getting information about whether the device
+// has a password reuse protection warning trigger.
+const char kPasswordProtectionWarningTrigger[] =
+ "passwordPotectionWarningTrigger";
+
+// Name of the signal for getting information about whether users can
+// access other computers from Chrome using Chrome Remote Desktop.
+const char kRemoteDesktopAvailable[] = "remoteDesktopAvailable";
+
+// Name of the signal for getting information of the value of the
+// SafeBrowsingProtectionLevel policy.
+// https://chromeenterprise.google/policies/#SafeBrowsingProtectionLevel
+const char kSafeBrowsingProtectionLevel[] = "safeBrowsingProtectionLevel";
+
+// Name of the signal for getting information about the device serial
+// number.
+const char kSerialNumber[] = "serialNumber";
+
+// Name of the parameterized signal for getting information from settings
+// storage (e.g. Registry, Plist) on the device.
+const char kSettings[] = "settings";
+
+// Name of the signal for getting information about the signed in profile
+// name.
+const char kSignedInProfileName[] = "signedInProfileName";
+
+// Name of the signal for getting information of the value of the
+// SitePerProcess policy.
+// https://chromeenterprise.google/policies/#SitePerProcess
+const char kSiteIsolationEnabled[] = "siteIsolationEnabled";
+
+// Name of the signal for getting information about whether third party
+// blocking is enabled on the device.
+const char kThirdPartyBlockingEnabled[] = "thirdPartyBlockingEnabled";
+
+// Name of the signal for getting information about the hash
+// of the EKPub certificate of the TPM on the device, if available.
+const char kTpmHash[] = "tpmHash";
+
+// Name of the signal for getting information about the windows domain
+// the device has joined.
+const char kWindowsDomain[] = "windowsDomain";
+
+} // namespace names
+
+namespace errors {
+
+// Returned when the user has not given explicit consent for a specific signal
+// to be collected.
+const char kConsentRequired[] = "CONSENT_REQUIRED";
+
+// Returned when the user is not affiliated with the organization managing the
+// browser.
+const char kUnaffiliatedUser[] = "UNAFFILIATED_USER";
+
+// Returned when the specified signal is not supported.
+const char kUnsupported[] = "UNSUPPORTED";
+
+// Returned when the signals collection code in unable to get a reference to
+// the SystemSignalsService.
+const char kMissingSystemService[] = "MISSING_SYSTEM_SERVICE";
+
+} // namespace errors
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/signals_constants.h b/chromium/components/device_signals/core/common/signals_constants.h
new file mode 100644
index 00000000000..b78cdba4562
--- /dev/null
+++ b/chromium/components/device_signals/core/common/signals_constants.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_SIGNALS_CONSTANTS_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_SIGNALS_CONSTANTS_H_
+
+namespace device_signals {
+
+// Signal names can be used as keys to store/retrieve signal values from
+// dictionaries.
+namespace names {
+
+extern const char kAllowScreenLock[];
+extern const char kAntiVirusInfo[];
+extern const char kBrowserVersion[];
+extern const char kBuiltInDnsClientEnabled[];
+extern const char kChromeCleanupEnabled[];
+extern const char kDeviceId[];
+extern const char kDeviceManufacturer[];
+extern const char kDeviceModel[];
+extern const char kDisplayName[];
+extern const char kDnsAddress[];
+extern const char kEnrollmentDomain[];
+extern const char kFileSystemInfo[];
+extern const char kFirewallOn[];
+extern const char kImei[];
+extern const char kInstalledHotfixes[];
+extern const char kIsDiskEncrypted[];
+extern const char kIsJailbroken[];
+extern const char kIsPasswordProtected[];
+extern const char kMeid[];
+extern const char kObfuscatedCustomerId[];
+extern const char kOs[];
+extern const char kOsVersion[];
+extern const char kPasswordProtectionWarningTrigger[];
+extern const char kRemoteDesktopAvailable[];
+extern const char kSafeBrowsingProtectionLevel[];
+extern const char kSerialNumber[];
+extern const char kSettings[];
+extern const char kSignedInProfileName[];
+extern const char kSiteIsolationEnabled[];
+extern const char kThirdPartyBlockingEnabled[];
+extern const char kTpmHash[];
+extern const char kWindowsDomain[];
+
+} // namespace names
+
+// Error strings that can be returned to indicated why signal values were not
+// returned.
+namespace errors {
+
+extern const char kConsentRequired[];
+extern const char kUnaffiliatedUser[];
+extern const char kUnsupported[];
+extern const char kMissingSystemService[];
+
+} // namespace errors
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_SIGNALS_CONSTANTS_H_
diff --git a/chromium/components/device_signals/core/common/win/BUILD.gn b/chromium/components/device_signals/core/common/win/BUILD.gn
new file mode 100644
index 00000000000..048ed98ffa6
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/BUILD.gn
@@ -0,0 +1,65 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("win") {
+ public = [
+ "win_types.h",
+ "wmi_client.h",
+ "wmi_client_impl.h",
+ "wsc_client.h",
+ "wsc_client_impl.h",
+ ]
+
+ sources = [
+ "win_types.cc",
+ "wmi_client.cc",
+ "wmi_client_impl.cc",
+ "wsc_client.cc",
+ "wsc_client_impl.cc",
+ ]
+
+ public_deps = [
+ "//base",
+ "//third_party/abseil-cpp:absl",
+ ]
+}
+
+source_set("test_support") {
+ testonly = true
+ sources = [
+ "com_fakes.cc",
+ "com_fakes.h",
+ "mock_wmi_client.cc",
+ "mock_wmi_client.h",
+ "mock_wsc_client.cc",
+ "mock_wsc_client.h",
+ ]
+
+ deps = [
+ ":win",
+ "//base",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/abseil-cpp:absl",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "win_types_unittest.cc",
+ "wmi_client_impl_unittest.cc",
+ "wsc_client_impl_unittest.cc",
+ ]
+
+ deps = [
+ ":test_support",
+ ":win",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/abseil-cpp:absl",
+ ]
+}
diff --git a/chromium/components/device_signals/core/common/win/com_fakes.cc b/chromium/components/device_signals/core/common/win/com_fakes.cc
new file mode 100644
index 00000000000..b9c74e9ad30
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/com_fakes.cc
@@ -0,0 +1,369 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/win/com_fakes.h"
+
+#include "base/check.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device_signals {
+
+#define IMPL_IUNKOWN_NOQI_WITH_REF(cls) \
+ IFACEMETHODIMP cls::QueryInterface(REFIID riid, void** ppv) { \
+ return E_NOTIMPL; \
+ } \
+ ULONG cls::AddRef() { return ::InterlockedIncrement(&ref_count_); } \
+ ULONG cls::Release(void) { \
+ DCHECK(ref_count_ > 0); \
+ return ::InterlockedDecrement(&ref_count_); \
+ }
+
+#define IMPL_IDISPATCH(cls) \
+ IMPL_IUNKOWN_NOQI_WITH_REF(cls) \
+ IFACEMETHODIMP cls::GetTypeInfoCount(UINT* pctinfo) { return E_NOTIMPL; } \
+ IFACEMETHODIMP cls::GetTypeInfo(UINT iTInfo, LCID lcid, \
+ ITypeInfo** ppTInfo) { \
+ return E_NOTIMPL; \
+ } \
+ IFACEMETHODIMP cls::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, \
+ UINT cNames, LCID lcid, \
+ DISPID* rgDispId) { \
+ return E_NOTIMPL; \
+ } \
+ IFACEMETHODIMP cls::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, \
+ WORD wFlags, DISPPARAMS* pDispParams, \
+ VARIANT* pVarResult, EXCEPINFO* pExcepInfo, \
+ UINT* puArgErr) { \
+ return E_NOTIMPL; \
+ }
+
+// FakeEnumWbemClassObject
+
+FakeEnumWbemClassObject::FakeEnumWbemClassObject() = default;
+
+FakeEnumWbemClassObject::~FakeEnumWbemClassObject() = default;
+
+HRESULT FakeEnumWbemClassObject::Next(long lTimeout,
+ ULONG uCount,
+ IWbemClassObject** apObjects,
+ ULONG* puReturned) {
+ // This fake implementation only supports moving through items one by one.
+ DCHECK(uCount == 1);
+
+ if (!iterator_.has_value()) {
+ iterator_ = items_.begin();
+ }
+
+ if (iterator_.value() == items_.end()) {
+ // Reached the end of the vector.
+ *puReturned = 0;
+ return WBEM_S_FALSE;
+ }
+
+ *puReturned = iterator_.value()->second;
+
+ // If the next item is nullptr, then return an error code.
+ HRESULT result = WBEM_S_FALSE;
+ if (iterator_.value()->first) {
+ result = WBEM_S_NO_ERROR;
+ *apObjects = iterator_.value()->first;
+ }
+
+ ++iterator_.value();
+ return result;
+}
+
+HRESULT FakeEnumWbemClassObject::Clone(IEnumWbemClassObject** ppEnum) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeEnumWbemClassObject::NextAsync(ULONG uCount,
+ IWbemObjectSink* pSink) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeEnumWbemClassObject::Reset() {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeEnumWbemClassObject::Skip(long lTimeout, ULONG nCount) {
+ return E_NOTIMPL;
+}
+
+IMPL_IUNKOWN_NOQI_WITH_REF(FakeEnumWbemClassObject)
+
+// FakeWbemClassObject
+
+FakeWbemClassObject::FakeWbemClassObject() = default;
+FakeWbemClassObject::FakeWbemClassObject(FakeWbemClassObject&&) = default;
+FakeWbemClassObject& FakeWbemClassObject::operator=(FakeWbemClassObject&&) =
+ default;
+
+FakeWbemClassObject::~FakeWbemClassObject() = default;
+
+HRESULT FakeWbemClassObject::Get(LPCWSTR wszName,
+ long lFlags,
+ VARIANT* pVal,
+ CIMTYPE* pType,
+ long* plFlavor) {
+ EXPECT_EQ(0, lFlags);
+
+ std::wstring key(wszName);
+ auto iterator = map_.find(key);
+ if (iterator == map_.end()) {
+ // Not found, return any HRESULT that is not 0.
+ return WBEM_S_FALSE;
+ }
+
+ *pVal = iterator->second.Copy();
+
+ return WBEM_S_NO_ERROR;
+}
+
+HRESULT FakeWbemClassObject::Delete(LPCWSTR wszName) {
+ map_.erase(wszName);
+ return WBEM_S_NO_ERROR;
+}
+
+HRESULT FakeWbemClassObject::BeginEnumeration(long lEnumFlags) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::BeginMethodEnumeration(long lEnumFlags) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::Clone(IWbemClassObject** ppCopy) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::CompareTo(long lFlags,
+ IWbemClassObject* pCompareTo) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::DeleteMethod(LPCWSTR wszName) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::EndEnumeration() {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::EndMethodEnumeration() {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::GetMethod(LPCWSTR wszName,
+ long lFlags,
+ IWbemClassObject** ppInSignature,
+ IWbemClassObject** ppOutSignature) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::GetMethodOrigin(LPCWSTR wszMethodName,
+ BSTR* pstrClassName) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::GetMethodQualifierSet(
+ LPCWSTR wszMethod,
+ IWbemQualifierSet** ppQualSet) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::GetNames(LPCWSTR wszQualifierName,
+ long lFlags,
+ VARIANT* pQualifierVal,
+ SAFEARRAY** pNames) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::GetObjectText(long lFlags, BSTR* pstrObjectText) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::GetPropertyOrigin(LPCWSTR wszName,
+ BSTR* pstrClassName) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::GetPropertyQualifierSet(
+ LPCWSTR wszProperty,
+ IWbemQualifierSet** ppQualSet) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::GetQualifierSet(IWbemQualifierSet** ppQualSet) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::InheritsFrom(LPCWSTR strAncestor) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::Next(long lFlags,
+ BSTR* strName,
+ VARIANT* pVal,
+ CIMTYPE* pType,
+ long* plFlavor) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::NextMethod(long lFlags,
+ BSTR* pstrName,
+ IWbemClassObject** ppInSignature,
+ IWbemClassObject** ppOutSignature) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::Put(LPCWSTR wszName,
+ long lFlags,
+ VARIANT* pVal,
+ CIMTYPE Type) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::PutMethod(LPCWSTR wszName,
+ long lFlags,
+ IWbemClassObject* pInSignature,
+ IWbemClassObject* pOutSignature) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::SpawnDerivedClass(long lFlags,
+ IWbemClassObject** ppNewClass) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWbemClassObject::SpawnInstance(long lFlags,
+ IWbemClassObject** ppNewInstance) {
+ return E_NOTIMPL;
+}
+
+IMPL_IUNKOWN_NOQI_WITH_REF(FakeWbemClassObject)
+
+// FakeWscProduct
+
+FakeWscProduct::FakeWscProduct() = default;
+
+FakeWscProduct::FakeWscProduct(const wchar_t* name,
+ const wchar_t* id,
+ WSC_SECURITY_PRODUCT_STATE state)
+ : name_(name), id_(id), state_(state) {}
+
+FakeWscProduct::FakeWscProduct(FakeWscProduct&& other)
+ : failed_step_(other.failed_step_), state_(other.state_) {
+ name_.Reset(other.name_.Release());
+ id_.Reset(other.id_.Release());
+}
+
+FakeWscProduct& FakeWscProduct::operator=(FakeWscProduct&& other) {
+ failed_step_ = other.failed_step_;
+ state_ = other.state_;
+ name_.Reset(other.name_.Release());
+ id_.Reset(other.id_.Release());
+ return *this;
+}
+
+FakeWscProduct::~FakeWscProduct() = default;
+
+HRESULT FakeWscProduct::get_ProductName(BSTR* pVal) {
+ if (ShouldFail(FailureStep::kProductName)) {
+ return E_FAIL;
+ }
+
+ *pVal = name_.Get();
+ return S_OK;
+}
+
+HRESULT FakeWscProduct::get_ProductGuid(BSTR* pVal) {
+ if (ShouldFail(FailureStep::kProductId)) {
+ return E_FAIL;
+ }
+
+ *pVal = id_.Get();
+ return S_OK;
+}
+
+HRESULT FakeWscProduct::get_ProductState(WSC_SECURITY_PRODUCT_STATE* pVal) {
+ if (ShouldFail(FailureStep::kProductState)) {
+ return E_FAIL;
+ }
+
+ *pVal = state_;
+ return S_OK;
+}
+
+HRESULT FakeWscProduct::get_ProductStateTimestamp(BSTR* pVal) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWscProduct::get_RemediationPath(BSTR* pVal) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWscProduct::get_SignatureStatus(
+ WSC_SECURITY_SIGNATURE_STATUS* pVal) {
+ return E_NOTIMPL;
+}
+
+HRESULT FakeWscProduct::get_ProductIsDefault(BOOL* pVal) {
+ return E_NOTIMPL;
+}
+
+bool FakeWscProduct::ShouldFail(FakeWscProduct::FailureStep step) {
+ return failed_step_.has_value() && failed_step_.value() == step;
+}
+
+IMPL_IDISPATCH(FakeWscProduct)
+
+// FakeWSCProductList
+
+FakeWSCProductList::FakeWSCProductList() = default;
+
+FakeWSCProductList::~FakeWSCProductList() = default;
+
+HRESULT FakeWSCProductList::get_Count(LONG* pVal) {
+ if (ShouldFail(FailureStep::kGetCount)) {
+ return E_FAIL;
+ }
+
+ *pVal = products_.size();
+ return S_OK;
+}
+
+HRESULT FakeWSCProductList::get_Item(ULONG index, IWscProduct** pVal) {
+ if (ShouldFail(FailureStep::kGetItem)) {
+ return E_FAIL;
+ }
+
+ if (index < 0 || index >= products_.size()) {
+ return E_FAIL;
+ }
+
+ *pVal = products_[index];
+
+ return S_OK;
+}
+
+HRESULT FakeWSCProductList::Initialize(ULONG provider) {
+ if (ShouldFail(FailureStep::kInitialize)) {
+ return E_FAIL;
+ }
+
+ // Initialize can only be called once.
+ DCHECK(!provider_.has_value());
+
+ provider_ = provider;
+ return S_OK;
+}
+
+bool FakeWSCProductList::ShouldFail(FakeWSCProductList::FailureStep step) {
+ return failed_step_.has_value() && failed_step_.value() == step;
+}
+
+IMPL_IDISPATCH(FakeWSCProductList)
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/com_fakes.h b/chromium/components/device_signals/core/common/win/com_fakes.h
new file mode 100644
index 00000000000..06fc7314b75
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/com_fakes.h
@@ -0,0 +1,234 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_COM_FAKES_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_COM_FAKES_H_
+
+#include <atlcomcli.h>
+#include <iwscapi.h>
+#include <wbemidl.h>
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_variant.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace device_signals {
+
+#define DECLARE_IUNKOWN_NOQI_WITH_REF() \
+ IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) override; \
+ ULONG STDMETHODCALLTYPE AddRef() override; \
+ ULONG STDMETHODCALLTYPE Release(void) override; \
+ ULONG ref_count_ = 1;
+
+#define DECLARE_IDISPATCH() \
+ DECLARE_IUNKOWN_NOQI_WITH_REF() \
+ IFACEMETHODIMP GetTypeInfoCount(UINT* pctinfo) override; \
+ IFACEMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) \
+ override; \
+ IFACEMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, \
+ LCID lcid, DISPID* rgDispId) override; \
+ IFACEMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, \
+ WORD wFlags, DISPPARAMS* pDispParams, \
+ VARIANT* pVarResult, EXCEPINFO* pExcepInfo, \
+ UINT* puArgErr) override;
+
+class FakeEnumWbemClassObject : public IEnumWbemClassObject {
+ public:
+ FakeEnumWbemClassObject();
+
+ virtual ~FakeEnumWbemClassObject();
+
+ void Add(IWbemClassObject* class_object, ULONG items_returned = 1) {
+ items_.emplace_back(class_object, items_returned);
+ }
+
+ private:
+ // IEnumWbemClassObject:
+ DECLARE_IUNKOWN_NOQI_WITH_REF()
+ IFACEMETHODIMP Clone(IEnumWbemClassObject** ppEnum) override;
+ IFACEMETHODIMP Next(long lTimeout,
+ ULONG uCount,
+ IWbemClassObject** apObjects,
+ ULONG* puReturned) override;
+ IFACEMETHODIMP NextAsync(ULONG uCount, IWbemObjectSink* pSink) override;
+ IFACEMETHODIMP Reset() override;
+ IFACEMETHODIMP Skip(long lTimeout, ULONG nCount) override;
+
+ std::vector<std::pair<IWbemClassObject*, ULONG>> items_;
+ absl::optional<std::vector<std::pair<IWbemClassObject*, ULONG>>::iterator>
+ iterator_;
+};
+
+class FakeWbemClassObject : public IWbemClassObject {
+ public:
+ FakeWbemClassObject();
+
+ FakeWbemClassObject(const FakeWbemClassObject& copy) = delete;
+ FakeWbemClassObject& operator=(const FakeWbemClassObject&) = delete;
+ FakeWbemClassObject(FakeWbemClassObject&&);
+ FakeWbemClassObject& operator=(FakeWbemClassObject&&);
+
+ virtual ~FakeWbemClassObject();
+
+ void Set(const wchar_t* key, const std::wstring& bstr_value) {
+ map_[key] = base::win::ScopedVariant(bstr_value.data(), bstr_value.size());
+ }
+
+ void Set(const wchar_t* key, const LONG long_value) {
+ map_[key] = base::win::ScopedVariant(long_value);
+ }
+
+ // Implemented IWbemClassObject:
+ IFACEMETHODIMP Get(LPCWSTR wszName,
+ long lFlags,
+ VARIANT* pVal,
+ CIMTYPE* pType = nullptr,
+ long* plFlavor = nullptr) override;
+ IFACEMETHODIMP Delete(LPCWSTR wszName) override;
+
+ private:
+ // IWbemClassObject:
+ DECLARE_IUNKOWN_NOQI_WITH_REF()
+ IFACEMETHODIMP BeginEnumeration(long lEnumFlags) override;
+ IFACEMETHODIMP BeginMethodEnumeration(long lEnumFlags) override;
+ IFACEMETHODIMP Clone(IWbemClassObject** ppCopy) override;
+ IFACEMETHODIMP CompareTo(long lFlags, IWbemClassObject* pCompareTo) override;
+ IFACEMETHODIMP DeleteMethod(LPCWSTR wszName) override;
+ IFACEMETHODIMP EndEnumeration() override;
+ IFACEMETHODIMP EndMethodEnumeration() override;
+ IFACEMETHODIMP GetMethod(LPCWSTR wszName,
+ long lFlags,
+ IWbemClassObject** ppInSignature,
+ IWbemClassObject** ppOutSignature) override;
+ IFACEMETHODIMP GetMethodOrigin(LPCWSTR wszMethodName,
+ BSTR* pstrClassName) override;
+ IFACEMETHODIMP GetMethodQualifierSet(LPCWSTR wszMethod,
+ IWbemQualifierSet** ppQualSet) override;
+ IFACEMETHODIMP GetNames(LPCWSTR wszQualifierName,
+ long lFlags,
+ VARIANT* pQualifierVal,
+ SAFEARRAY** pNames) override;
+ IFACEMETHODIMP GetObjectText(long lFlags, BSTR* pstrObjectText) override;
+ IFACEMETHODIMP GetPropertyOrigin(LPCWSTR wszName,
+ BSTR* pstrClassName) override;
+ IFACEMETHODIMP GetPropertyQualifierSet(
+ LPCWSTR wszProperty,
+ IWbemQualifierSet** ppQualSet) override;
+ IFACEMETHODIMP GetQualifierSet(IWbemQualifierSet** ppQualSet) override;
+ IFACEMETHODIMP InheritsFrom(LPCWSTR strAncestor) override;
+ IFACEMETHODIMP Next(long lFlags,
+ BSTR* strName,
+ VARIANT* pVal,
+ CIMTYPE* pType = nullptr,
+ long* plFlavor = nullptr) override;
+ IFACEMETHODIMP NextMethod(long lFlags,
+ BSTR* pstrName,
+ IWbemClassObject** ppInSignature,
+ IWbemClassObject** ppOutSignature) override;
+ IFACEMETHODIMP Put(LPCWSTR wszName,
+ long lFlags,
+ VARIANT* pVal,
+ CIMTYPE Type) override;
+ IFACEMETHODIMP PutMethod(LPCWSTR wszName,
+ long lFlags,
+ IWbemClassObject* pInSignature,
+ IWbemClassObject* pOutSignature) override;
+ IFACEMETHODIMP SpawnDerivedClass(long lFlags,
+ IWbemClassObject** ppNewClass) override;
+ IFACEMETHODIMP SpawnInstance(long lFlags,
+ IWbemClassObject** ppNewInstance) override;
+
+ std::map<std::wstring, base::win::ScopedVariant> map_;
+};
+
+class FakeWscProduct : public IWscProduct {
+ public:
+ FakeWscProduct();
+ FakeWscProduct(const wchar_t* name,
+ const wchar_t* id,
+ WSC_SECURITY_PRODUCT_STATE state);
+
+ FakeWscProduct(const FakeWscProduct& copy) = delete;
+ FakeWscProduct& operator=(const FakeWscProduct&) = delete;
+ FakeWscProduct(FakeWscProduct&&);
+ FakeWscProduct& operator=(FakeWscProduct&&);
+
+ virtual ~FakeWscProduct();
+
+ enum class FailureStep {
+ kProductName = 0,
+ kProductId = 1,
+ kProductState = 2,
+ };
+
+ // IWscProduct:
+ IFACEMETHODIMP get_ProductName(BSTR* pVal) override;
+ IFACEMETHODIMP get_ProductGuid(BSTR* pVal) override;
+ IFACEMETHODIMP get_ProductState(WSC_SECURITY_PRODUCT_STATE* pVal) override;
+
+ // Can be used to force a failure to happen in one of the functions
+ // represented by `step`.
+ void set_failed_step(FailureStep step) { failed_step_ = step; }
+
+ private:
+ // IWscProduct:
+ DECLARE_IDISPATCH()
+ IFACEMETHODIMP get_ProductStateTimestamp(BSTR* pVal) override;
+ IFACEMETHODIMP get_RemediationPath(BSTR* pVal) override;
+ IFACEMETHODIMP get_SignatureStatus(
+ WSC_SECURITY_SIGNATURE_STATUS* pVal) override;
+ IFACEMETHODIMP get_ProductIsDefault(BOOL* pVal) override;
+
+ bool ShouldFail(FailureStep step);
+
+ absl::optional<FailureStep> failed_step_;
+
+ base::win::ScopedBstr name_;
+ base::win::ScopedBstr id_;
+ WSC_SECURITY_PRODUCT_STATE state_;
+};
+
+class FakeWSCProductList : public IWSCProductList {
+ public:
+ FakeWSCProductList();
+
+ virtual ~FakeWSCProductList();
+
+ enum class FailureStep {
+ kInitialize = 0,
+ kGetCount = 1,
+ kGetItem = 2,
+ };
+
+ void Add(IWscProduct* product) { products_.push_back(product); }
+ // IWSCProductList:
+ IFACEMETHODIMP get_Count(LONG* pVal) override;
+ IFACEMETHODIMP get_Item(ULONG index, IWscProduct** pVal) override;
+ IFACEMETHODIMP Initialize(ULONG provider) override;
+
+ // Can be used to force a failure to happen in one of the functions
+ // represented by `step`.
+ void set_failed_step(FailureStep step) { failed_step_ = step; }
+
+ const absl::optional<ULONG>& provider() { return provider_; }
+
+ private:
+ // IWSCProductList:
+ DECLARE_IDISPATCH()
+
+ bool ShouldFail(FailureStep step);
+
+ absl::optional<FailureStep> failed_step_;
+
+ absl::optional<ULONG> provider_;
+ std::vector<IWscProduct*> products_;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_COM_FAKES_H_
diff --git a/chromium/components/device_signals/core/common/win/mock_wmi_client.cc b/chromium/components/device_signals/core/common/win/mock_wmi_client.cc
new file mode 100644
index 00000000000..f12f35b0c60
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/mock_wmi_client.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/win/mock_wmi_client.h"
+
+namespace device_signals {
+
+MockWmiClient::MockWmiClient() = default;
+MockWmiClient::~MockWmiClient() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/mock_wmi_client.h b/chromium/components/device_signals/core/common/win/mock_wmi_client.h
new file mode 100644
index 00000000000..ac934b7b9a4
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/mock_wmi_client.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WMI_CLIENT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WMI_CLIENT_H_
+
+#include "components/device_signals/core/common/win/wmi_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockWmiClient : public WmiClient {
+ public:
+ MockWmiClient();
+ ~MockWmiClient() override;
+
+ MOCK_METHOD(WmiHotfixesResponse, GetInstalledHotfixes, (), (override));
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WMI_CLIENT_H_
diff --git a/chromium/components/device_signals/core/common/win/mock_wsc_client.cc b/chromium/components/device_signals/core/common/win/mock_wsc_client.cc
new file mode 100644
index 00000000000..18bb1698827
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/mock_wsc_client.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/win/mock_wsc_client.h"
+
+namespace device_signals {
+
+MockWscClient::MockWscClient() = default;
+MockWscClient::~MockWscClient() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/mock_wsc_client.h b/chromium/components/device_signals/core/common/win/mock_wsc_client.h
new file mode 100644
index 00000000000..080dfa50fbe
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/mock_wsc_client.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WSC_CLIENT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WSC_CLIENT_H_
+
+#include "components/device_signals/core/common/win/wsc_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockWscClient : public WscClient {
+ public:
+ MockWscClient();
+ ~MockWscClient() override;
+
+ MOCK_METHOD(WscAvProductsResponse, GetAntiVirusProducts, (), (override));
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WMI_CLIENT_H_
diff --git a/chromium/components/device_signals/core/common/win/win_types.cc b/chromium/components/device_signals/core/common/win/win_types.cc
new file mode 100644
index 00000000000..8add6652e48
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/win_types.cc
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium 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/device_signals/core/common/win/win_types.h"
+
+#include "base/values.h"
+
+namespace device_signals {
+
+base::Value AvProduct::ToValue() const {
+ base::Value::Dict values;
+ values.Set("displayName", display_name);
+ values.Set("state", static_cast<int>(state));
+ values.Set("productId", product_id);
+ return base::Value(std::move(values));
+}
+
+base::Value InstalledHotfix::ToValue() const {
+ base::Value::Dict values;
+ values.Set("hotfixId", hotfix_id);
+ return base::Value(std::move(values));
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/win_types.h b/chromium/components/device_signals/core/common/win/win_types.h
new file mode 100644
index 00000000000..d551d48d2b5
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/win_types.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WIN_TYPES_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WIN_TYPES_H_
+
+#include <string>
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace device_signals {
+
+// Various states in which an AntiVirus software can be.
+enum class AvProductState { kOn, kOff, kSnoozed, kExpired };
+
+// Metadata about an installed AntiVirus software product.
+// Can be retrieve via WSC on Windows 8 and above, and below properties are
+// collected via this interface:
+// https://docs.microsoft.com/en-us/windows/win32/api/iwscapi/nn-iwscapi-iwscproduct
+struct AvProduct {
+ std::string display_name;
+ AvProductState state;
+
+ // Although not present on the documentation, IWscProduct exposes a
+ // `get_ProductGuid` function to retrieve an GUID representing an Antivirus
+ // software.
+ std::string product_id;
+
+ base::Value ToValue() const;
+};
+
+// Metadata about an installed Hotfix update.
+struct InstalledHotfix {
+ // In WMI, this value represents the `HotFixID` property from entries in
+ // "Win32_QuickFixEngineering". They have a format looking like `KB123123`.
+ // https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-quickfixengineering
+ std::string hotfix_id;
+
+ base::Value ToValue() const;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WIN_TYPES_H_
diff --git a/chromium/components/device_signals/core/common/win/win_types_unittest.cc b/chromium/components/device_signals/core/common/win/win_types_unittest.cc
new file mode 100644
index 00000000000..4a6b5bb781a
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/win_types_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium 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/device_signals/core/common/win/win_types.h"
+
+#include <array>
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device_signals {
+
+TEST(WinTypes, AvProduct_ToValue) {
+ std::array<AvProductState, 4> states{
+ {AvProductState::kOn, AvProductState::kOff, AvProductState::kSnoozed,
+ AvProductState::kExpired}};
+
+ for (const auto state : states) {
+ AvProduct av_product{"some display name", state, "some product id"};
+
+ base::Value::Dict expected_value;
+ expected_value.Set("displayName", av_product.display_name);
+ expected_value.Set("state", static_cast<int>(av_product.state));
+ expected_value.Set("productId", av_product.product_id);
+
+ EXPECT_EQ(av_product.ToValue(), base::Value(std::move(expected_value)));
+ }
+}
+
+TEST(WinTypes, InstalledHotfix_ToValue) {
+ InstalledHotfix hotfix{"some hotfix id"};
+
+ base::Value::Dict expected_value;
+ expected_value.Set("hotfixId", hotfix.hotfix_id);
+
+ EXPECT_EQ(hotfix.ToValue(), base::Value(std::move(expected_value)));
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/wmi_client.cc b/chromium/components/device_signals/core/common/win/wmi_client.cc
new file mode 100644
index 00000000000..7aa439e8792
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wmi_client.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/win/wmi_client.h"
+
+namespace device_signals {
+
+WmiHotfixesResponse::WmiHotfixesResponse() = default;
+
+WmiHotfixesResponse::WmiHotfixesResponse(const WmiHotfixesResponse& other) =
+ default;
+
+WmiHotfixesResponse::~WmiHotfixesResponse() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/wmi_client.h b/chromium/components/device_signals/core/common/win/wmi_client.h
new file mode 100644
index 00000000000..6215633cf7a
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wmi_client.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/win/wmi.h"
+#include "components/device_signals/core/common/win/win_types.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+// WMI interfaces are available on Windows Vista and above, and are officially
+// undocumented.
+namespace device_signals {
+
+// Errors that can occur when calling WMI, or parsing response values.
+enum class WmiParsingError {
+ kFailedToIterateResults = 0,
+ kFailedToGetName = 1,
+ kMaxValue = kFailedToGetName
+};
+
+// Response object for calls to retrieve information about installed hotfix
+// updates.
+struct WmiHotfixesResponse {
+ WmiHotfixesResponse();
+ WmiHotfixesResponse(const WmiHotfixesResponse& other);
+
+ ~WmiHotfixesResponse();
+
+ std::vector<InstalledHotfix> hotfixes;
+ absl::optional<base::win::WmiError> query_error;
+ std::vector<WmiParsingError> parsing_errors;
+};
+
+// Interface for a client instance used to get information from WMI.
+class WmiClient {
+ public:
+ virtual ~WmiClient() = default;
+
+ // Will retrieve information about installed hotfix updates.
+ virtual WmiHotfixesResponse GetInstalledHotfixes() = 0;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_H_
diff --git a/chromium/components/device_signals/core/common/win/wmi_client_impl.cc b/chromium/components/device_signals/core/common/win/wmi_client_impl.cc
new file mode 100644
index 00000000000..40b68264e2e
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wmi_client_impl.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/win/wmi_client_impl.h"
+
+#include <wbemidl.h>
+#include <windows.h>
+#include <wrl/client.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/check.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/win/com_init_util.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_variant.h"
+#include "base/win/windows_version.h"
+#include "base/win/wmi.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace device_signals {
+
+namespace {
+
+// Parses a string value from `class_object` named `property_name`.
+absl::optional<std::string> ParseString(
+ const std::wstring& property_name,
+ const ComPtr<IWbemClassObject>& class_object) {
+ base::win::ScopedVariant string_variant;
+ HRESULT hr = class_object->Get(property_name.c_str(), 0,
+ string_variant.Receive(), 0, 0);
+
+ if (FAILED(hr) || string_variant.type() != VT_BSTR) {
+ return absl::nullopt;
+ }
+
+ // Owned by ScopedVariant.
+ BSTR temp_bstr = V_BSTR(string_variant.ptr());
+ return base::SysWideToUTF8(
+ std::wstring(temp_bstr, ::SysStringLen(temp_bstr)));
+}
+
+} // namespace
+
+WmiClientImpl::WmiClientImpl()
+ : run_query_callback_(base::BindRepeating(base::win::RunWmiQuery)) {}
+
+WmiClientImpl::WmiClientImpl(RunWmiQueryCallback run_query_callback)
+ : run_query_callback_(std::move(run_query_callback)) {}
+
+WmiClientImpl::~WmiClientImpl() = default;
+
+WmiHotfixesResponse WmiClientImpl::GetInstalledHotfixes() {
+ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+ base::BlockingType::MAY_BLOCK);
+ ComPtr<IEnumWbemClassObject> enumerator;
+ auto result_code = run_query_callback_.Run(
+ base::win::kCimV2ServerName, L"SELECT * FROM Win32_QuickFixEngineering",
+ &enumerator);
+
+ WmiHotfixesResponse response;
+ if (result_code.has_value()) {
+ response.query_error = result_code.value();
+ return response;
+ }
+
+ HRESULT hr;
+ while (true) {
+ ComPtr<IWbemClassObject> class_object;
+ ULONG items_returned = 0U;
+ hr = enumerator->Next(WBEM_INFINITE, 1, &class_object, &items_returned);
+
+ if (hr == WBEM_S_FALSE || items_returned == 0U) {
+ // Reached the end of the enumerator.
+ break;
+ }
+
+ // Something went wrong, and it wasn't the end of the enumerator.
+ if (FAILED(hr)) {
+ response.parsing_errors.push_back(
+ WmiParsingError::kFailedToIterateResults);
+ continue;
+ }
+
+ absl::optional<std::string> hotfix_id =
+ ParseString(L"HotFixId", class_object);
+ if (!hotfix_id.has_value()) {
+ response.parsing_errors.push_back(WmiParsingError::kFailedToGetName);
+ continue;
+ }
+
+ InstalledHotfix hotfix;
+ hotfix.hotfix_id = hotfix_id.value();
+
+ // If all values were parsed properly, add `hotfix` into the response
+ // vector. If any value could not be parsed properly, the item was
+ // discarded.
+ response.hotfixes.push_back(hotfix);
+ }
+
+ return response;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/wmi_client_impl.h b/chromium/components/device_signals/core/common/win/wmi_client_impl.h
new file mode 100644
index 00000000000..fa931472c97
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wmi_client_impl.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_IMPL_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_IMPL_H_
+
+#include "base/callback.h"
+#include "components/device_signals/core/common/win/wmi_client.h"
+
+// WMI interfaces are available on Windows Vista and above, and are officially
+// undocumented.
+namespace device_signals {
+
+class WmiClientImpl : public WmiClient {
+ public:
+ using RunWmiQueryCallback =
+ base::RepeatingCallback<absl::optional<base::win::WmiError>(
+ const std::wstring&,
+ const std::wstring&,
+ Microsoft::WRL::ComPtr<IEnumWbemClassObject>*)>;
+
+ WmiClientImpl();
+
+ ~WmiClientImpl() override;
+
+ // WmiClient:
+ WmiHotfixesResponse GetInstalledHotfixes() override;
+
+ private:
+ friend class WmiClientImplTest;
+
+ // Constructor taking in a `run_query_callback` which can be used to mock
+ // running the WMI query.
+ explicit WmiClientImpl(RunWmiQueryCallback run_query_callback);
+
+ RunWmiQueryCallback run_query_callback_;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_IMPL_H_
diff --git a/chromium/components/device_signals/core/common/win/wmi_client_impl_unittest.cc b/chromium/components/device_signals/core/common/win/wmi_client_impl_unittest.cc
new file mode 100644
index 00000000000..cd83f1f4f7b
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wmi_client_impl_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/win/wmi_client_impl.h"
+
+#include <wbemidl.h>
+#include <windows.h>
+#include <wrl/client.h>
+#include <wrl/implements.h>
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/test/task_environment.h"
+#include "base/win/wmi.h"
+#include "components/device_signals/core/common/win/com_fakes.h"
+#include "components/device_signals/core/common/win/win_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace device_signals {
+
+namespace {
+
+FakeWbemClassObject CreateHotfixObject(const std::wstring& hotfix_id) {
+ FakeWbemClassObject hotfix_obj;
+ hotfix_obj.Set(L"HotFixId", hotfix_id);
+ return hotfix_obj;
+}
+
+} // namespace
+
+class WmiClientImplTest : public testing::Test {
+ public:
+ WmiClientImplTest()
+ : wmi_client_(base::BindRepeating(&WmiClientImplTest::RunQuery,
+ base::Unretained(this))) {}
+
+ protected:
+ absl::optional<base::win::WmiError> RunQuery(
+ const std::wstring& server_name,
+ const std::wstring& query,
+ ComPtr<IEnumWbemClassObject>* enumerator) {
+ ++nb_calls_;
+ captured_server_name_ = server_name;
+ captured_query_ = query;
+
+ if (query_error_.has_value()) {
+ return query_error_.value();
+ }
+ *enumerator = &fake_enumerator_;
+ return absl::nullopt;
+ }
+
+ void ExpectHotfixQueryRan() {
+ EXPECT_EQ(captured_server_name_, base::win::kCimV2ServerName);
+ EXPECT_EQ(captured_query_, L"SELECT * FROM Win32_QuickFixEngineering");
+ EXPECT_EQ(nb_calls_, 1U);
+ }
+
+ FakeEnumWbemClassObject fake_enumerator_;
+ absl::optional<base::win::WmiError> query_error_;
+
+ std::wstring captured_server_name_;
+ std::wstring captured_query_;
+ uint32_t nb_calls_ = 0;
+
+ WmiClientImpl wmi_client_;
+};
+
+// Tests how the client behaves when the WMI query fails when querying for
+// installed hotfixes.
+TEST_F(WmiClientImplTest, GetInstalledHotfixes_FailedRunQuery) {
+ query_error_ = base::win::WmiError::kFailedToConnectToWMI;
+
+ auto hotfix_response = wmi_client_.GetInstalledHotfixes();
+
+ ExpectHotfixQueryRan();
+ EXPECT_EQ(hotfix_response.hotfixes.size(), 0U);
+ EXPECT_EQ(hotfix_response.parsing_errors.size(), 0U);
+ EXPECT_EQ(hotfix_response.query_error,
+ base::win::WmiError::kFailedToConnectToWMI);
+}
+
+// Tests how the client behaves when parsing hotfix objects, one of which is
+// valid, and another which is missing an ID.
+TEST_F(WmiClientImplTest, GetInstalledHotfixes_ParsingItems) {
+ std::wstring hotfix_id1 = L"some_hotfix_id";
+ FakeWbemClassObject hotfix_obj1 = CreateHotfixObject(hotfix_id1);
+
+ FakeWbemClassObject hotfix_obj2 = CreateHotfixObject(L"some_other_id");
+
+ hotfix_obj2.Delete(L"HotFixId");
+
+ fake_enumerator_.Add(&hotfix_obj1);
+ fake_enumerator_.Add(&hotfix_obj2);
+
+ auto hotfix_response = wmi_client_.GetInstalledHotfixes();
+
+ ExpectHotfixQueryRan();
+ EXPECT_EQ(hotfix_response.query_error, absl::nullopt);
+
+ // Success item.
+ ASSERT_EQ(hotfix_response.hotfixes.size(), 1U);
+ EXPECT_EQ(hotfix_response.hotfixes[0].hotfix_id,
+ base::SysWideToUTF8(hotfix_id1));
+
+ // Failed item.
+ ASSERT_EQ(hotfix_response.parsing_errors.size(), 1U);
+ EXPECT_EQ(hotfix_response.parsing_errors[0],
+ WmiParsingError::kFailedToGetName);
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/wsc_client.cc b/chromium/components/device_signals/core/common/win/wsc_client.cc
new file mode 100644
index 00000000000..4b44c206ef8
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wsc_client.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/win/wsc_client.h"
+
+namespace device_signals {
+
+WscAvProductsResponse::WscAvProductsResponse() = default;
+WscAvProductsResponse::~WscAvProductsResponse() = default;
+
+WscAvProductsResponse::WscAvProductsResponse(
+ const WscAvProductsResponse& other) = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/wsc_client.h b/chromium/components/device_signals/core/common/win/wsc_client.h
new file mode 100644
index 00000000000..089808eee8d
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wsc_client.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+#include "components/device_signals/core/common/win/win_types.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+// WSC interfaces are available on Windows 8 and above. More information at:
+// https://docs.microsoft.com/en-us/windows/win32/api/iwscapi/
+namespace device_signals {
+
+enum class WscQueryError {
+ kFailedToCreateInstance = 0,
+ kFailedToInitializeProductList = 1,
+ kFailedToGetProductCount = 2,
+};
+
+// Errors that can occur when calling WSC, or parsing response values.
+enum class WscParsingError {
+ kFailedToGetItem = 0,
+ kFailedToGetState = 1,
+ kStateInvalid = 2,
+ kFailedToGetName = 3,
+ kFailedToGetId = 4,
+ kMaxValue = kFailedToGetId
+};
+
+// Response object for calls to retrieve information about installed AntiVirus
+// software.
+struct WscAvProductsResponse {
+ WscAvProductsResponse();
+ ~WscAvProductsResponse();
+
+ WscAvProductsResponse(const WscAvProductsResponse& other);
+
+ std::vector<AvProduct> av_products;
+ absl::optional<WscQueryError> query_error;
+ std::vector<WscParsingError> parsing_errors;
+};
+
+// Interface for a client instance used to get information from Windows Security
+// Center.
+class WscClient {
+ public:
+ virtual ~WscClient() = default;
+
+ // Will retrieve information about installed AntiVirus software.
+ virtual WscAvProductsResponse GetAntiVirusProducts() = 0;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_H_
diff --git a/chromium/components/device_signals/core/common/win/wsc_client_impl.cc b/chromium/components/device_signals/core/common/win/wsc_client_impl.cc
new file mode 100644
index 00000000000..fae2aed1514
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wsc_client_impl.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/win/wsc_client_impl.h"
+
+#include <iwscapi.h>
+#include <windows.h>
+#include <wrl/client.h>
+#include <wscapi.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/check.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/win/com_init_util.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_variant.h"
+#include "base/win/windows_version.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace device_signals {
+
+namespace {
+
+AvProductState ConvertState(WSC_SECURITY_PRODUCT_STATE state) {
+ switch (state) {
+ case WSC_SECURITY_PRODUCT_STATE_ON:
+ return AvProductState::kOn;
+ case WSC_SECURITY_PRODUCT_STATE_OFF:
+ return AvProductState::kOff;
+ case WSC_SECURITY_PRODUCT_STATE_SNOOZED:
+ return AvProductState::kSnoozed;
+ case WSC_SECURITY_PRODUCT_STATE_EXPIRED:
+ return AvProductState::kExpired;
+ }
+}
+
+HRESULT CreateProductList(IWSCProductList** product_list) {
+ return ::CoCreateInstance(__uuidof(WSCProductList), nullptr,
+ CLSCTX_INPROC_SERVER, IID_PPV_ARGS(product_list));
+}
+
+} // namespace
+
+WscClientImpl::WscClientImpl()
+ : create_callback_(base::BindRepeating(CreateProductList)) {}
+
+WscClientImpl::WscClientImpl(CreateProductListCallback create_callback)
+ : create_callback_(std::move(create_callback)) {}
+
+WscClientImpl::~WscClientImpl() = default;
+
+WscAvProductsResponse WscClientImpl::GetAntiVirusProducts() {
+ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+ base::BlockingType::MAY_BLOCK);
+ WscAvProductsResponse response;
+
+ ComPtr<IWSCProductList> product_list;
+ HRESULT hr = create_callback_.Run(&product_list);
+ if (FAILED(hr)) {
+ response.query_error = WscQueryError::kFailedToCreateInstance;
+ return response;
+ }
+
+ hr = product_list->Initialize(WSC_SECURITY_PROVIDER_ANTIVIRUS);
+ if (FAILED(hr)) {
+ response.query_error = WscQueryError::kFailedToInitializeProductList;
+ return response;
+ }
+
+ LONG product_count;
+ hr = product_list->get_Count(&product_count);
+ if (FAILED(hr)) {
+ response.query_error = WscQueryError::kFailedToGetProductCount;
+ return response;
+ }
+
+ for (LONG i = 0; i < product_count; i++) {
+ ComPtr<IWscProduct> product;
+ hr = product_list->get_Item(i, &product);
+ if (FAILED(hr)) {
+ response.parsing_errors.push_back(WscParsingError::kFailedToGetItem);
+ continue;
+ }
+
+ AvProduct av_product;
+ WSC_SECURITY_PRODUCT_STATE product_state;
+ hr = product->get_ProductState(&product_state);
+ if (FAILED(hr)) {
+ response.parsing_errors.push_back(WscParsingError::kFailedToGetState);
+ continue;
+ }
+
+ av_product.state = ConvertState(product_state);
+
+ base::win::ScopedBstr product_name;
+ hr = product->get_ProductName(product_name.Receive());
+ if (FAILED(hr)) {
+ response.parsing_errors.push_back(WscParsingError::kFailedToGetName);
+ continue;
+ }
+ av_product.display_name = base::SysWideToUTF8(
+ std::wstring(product_name.Get(), product_name.Length()));
+
+ base::win::ScopedBstr product_id;
+ hr = product->get_ProductGuid(product_id.Receive());
+ if (FAILED(hr)) {
+ response.parsing_errors.push_back(WscParsingError::kFailedToGetId);
+ continue;
+ }
+ av_product.product_id = base::SysWideToUTF8(
+ std::wstring(product_id.Get(), product_id.Length()));
+
+ response.av_products.push_back(std::move(av_product));
+ }
+
+ return response;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/wsc_client_impl.h b/chromium/components/device_signals/core/common/win/wsc_client_impl.h
new file mode 100644
index 00000000000..e17df9d36ef
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wsc_client_impl.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_IMPL_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_IMPL_H_
+
+#include <iwscapi.h>
+
+#include "base/callback.h"
+#include "components/device_signals/core/common/win/wsc_client.h"
+
+namespace device_signals {
+
+class WscClientImpl : public WscClient {
+ public:
+ using CreateProductListCallback =
+ base::RepeatingCallback<HRESULT(IWSCProductList**)>;
+
+ WscClientImpl();
+
+ ~WscClientImpl() override;
+
+ // WscClient:
+ WscAvProductsResponse GetAntiVirusProducts() override;
+
+ private:
+ friend class WscClientImplTest;
+
+ // Constructor taking in a `create_callback` which can be used to mock
+ // creating the product list COM object.
+ explicit WscClientImpl(CreateProductListCallback create_callback);
+
+ CreateProductListCallback create_callback_;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_IMPL_H_
diff --git a/chromium/components/device_signals/core/common/win/wsc_client_impl_unittest.cc b/chromium/components/device_signals/core/common/win/wsc_client_impl_unittest.cc
new file mode 100644
index 00000000000..e922328ad36
--- /dev/null
+++ b/chromium/components/device_signals/core/common/win/wsc_client_impl_unittest.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2022 The Chromium 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/device_signals/core/common/win/wsc_client_impl.h"
+
+#include <iwscapi.h>
+#include <windows.h>
+#include <wrl/client.h>
+#include <wscapi.h>
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/test/task_environment.h"
+#include "base/win/windows_version.h"
+#include "components/device_signals/core/common/win/com_fakes.h"
+#include "components/device_signals/core/common/win/win_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace device_signals {
+
+struct AvTestData {
+ const wchar_t* name;
+ const wchar_t* id;
+ WSC_SECURITY_PRODUCT_STATE state;
+ AvProductState expected_state;
+};
+
+class WscClientImplTest : public testing::Test {
+ protected:
+ WscClientImplTest()
+ : wsc_client_(base::BindRepeating(&WscClientImplTest::CreateProductList,
+ base::Unretained(this))) {}
+
+ HRESULT CreateProductList(IWSCProductList** product_list) {
+ if (fail_list_creation_) {
+ return E_FAIL;
+ }
+
+ *product_list = &product_list_;
+ return S_OK;
+ }
+
+ void ExpectAvInitialized() {
+ EXPECT_TRUE(product_list_.provider().has_value());
+ EXPECT_EQ(product_list_.provider().value(),
+ WSC_SECURITY_PROVIDER_ANTIVIRUS);
+ }
+
+ bool fail_list_creation_ = false;
+
+ FakeWSCProductList product_list_;
+ WscClientImpl wsc_client_;
+};
+
+// Tests how the client handles all different product states when parsing
+// products received from the list.
+TEST_F(WscClientImplTest, GetAntiVirusProducts_AllStates) {
+ std::vector<AvTestData> test_data;
+ test_data.push_back({L"first name", L"first product id",
+ WSC_SECURITY_PRODUCT_STATE_ON, AvProductState::kOn});
+ test_data.push_back({L"second name", L"second product id",
+ WSC_SECURITY_PRODUCT_STATE_OFF, AvProductState::kOff});
+ test_data.push_back({L"third name", L"third product id",
+ WSC_SECURITY_PRODUCT_STATE_SNOOZED,
+ AvProductState::kSnoozed});
+ test_data.push_back({L"fourth name", L"fourth product id",
+ WSC_SECURITY_PRODUCT_STATE_EXPIRED,
+ AvProductState::kExpired});
+
+ // Used to keep products from going out of scope.
+ std::vector<FakeWscProduct> products;
+
+ for (const auto& data : test_data) {
+ products.emplace_back(data.name, data.id, data.state);
+ }
+
+ for (auto& product : products) {
+ product_list_.Add(&product);
+ }
+
+ auto response = wsc_client_.GetAntiVirusProducts();
+
+ ExpectAvInitialized();
+ EXPECT_EQ(response.query_error, absl::nullopt);
+
+ EXPECT_EQ(response.parsing_errors.size(), 0U);
+
+ ASSERT_EQ(response.av_products.size(), test_data.size());
+ ASSERT_GT(response.av_products.size(), 0U);
+
+ for (size_t i = 0; i < test_data.size(); i++) {
+ EXPECT_EQ(response.av_products[i].display_name,
+ base::SysWideToUTF8(test_data[i].name));
+ EXPECT_EQ(response.av_products[i].product_id,
+ base::SysWideToUTF8(test_data[i].id));
+ EXPECT_EQ(response.av_products[i].state, test_data[i].expected_state);
+ }
+}
+
+// Tests how the client reacts to a failure occurring when trying to create a
+// product list instance.
+TEST_F(WscClientImplTest, GetAntiVirusProducts_FailedCreate) {
+ fail_list_creation_ = true;
+
+ auto response = wsc_client_.GetAntiVirusProducts();
+
+ EXPECT_EQ(response.av_products.size(), 0U);
+ EXPECT_EQ(response.parsing_errors.size(), 0U);
+
+ ASSERT_TRUE(response.query_error.has_value());
+ EXPECT_EQ(response.query_error.value(),
+ WscQueryError::kFailedToCreateInstance);
+}
+
+// Tests how the client reacts to a failure occurring when trying to initialize
+// a product list instance.
+TEST_F(WscClientImplTest, GetAntiVirusProducts_FailedInitialize) {
+ product_list_.set_failed_step(FakeWSCProductList::FailureStep::kInitialize);
+
+ auto response = wsc_client_.GetAntiVirusProducts();
+
+ EXPECT_EQ(response.av_products.size(), 0U);
+ EXPECT_EQ(response.parsing_errors.size(), 0U);
+
+ ASSERT_TRUE(response.query_error.has_value());
+ EXPECT_EQ(response.query_error.value(),
+ WscQueryError::kFailedToInitializeProductList);
+}
+
+// Tests how the client reacts to a failure occurring when trying to get a
+// product count from a product list.
+TEST_F(WscClientImplTest, GetAntiVirusProducts_FailedGetProductCount) {
+ product_list_.set_failed_step(FakeWSCProductList::FailureStep::kGetCount);
+
+ auto response = wsc_client_.GetAntiVirusProducts();
+
+ ExpectAvInitialized();
+ EXPECT_EQ(response.av_products.size(), 0U);
+ EXPECT_EQ(response.parsing_errors.size(), 0U);
+
+ ASSERT_TRUE(response.query_error.has_value());
+ EXPECT_EQ(response.query_error.value(),
+ WscQueryError::kFailedToGetProductCount);
+}
+
+// Tests how the client reacts to a failure occurring when trying to get an item
+// from a product list.
+TEST_F(WscClientImplTest, GetAntiVirusProducts_FailedGetItem) {
+ product_list_.set_failed_step(FakeWSCProductList::FailureStep::kGetItem);
+
+ auto* name1 = L"first name";
+ auto* id1 = L"first product id";
+ FakeWscProduct on_product(name1, id1, WSC_SECURITY_PRODUCT_STATE_ON);
+ product_list_.Add(&on_product);
+
+ auto response = wsc_client_.GetAntiVirusProducts();
+
+ ExpectAvInitialized();
+ EXPECT_EQ(response.av_products.size(), 0U);
+ EXPECT_FALSE(response.query_error.has_value());
+
+ ASSERT_EQ(response.parsing_errors.size(), 1U);
+ EXPECT_EQ(response.parsing_errors[0], WscParsingError::kFailedToGetItem);
+}
+
+// Tests how the client reacts to a failure occurring when facing product
+// parsing errors.
+TEST_F(WscClientImplTest, GetAntiVirusProducts_ProductErrors) {
+ // Valid product.
+ auto* name1 = L"first name";
+ auto* id1 = L"first product id";
+ FakeWscProduct on_product(name1, id1, WSC_SECURITY_PRODUCT_STATE_ON);
+
+ // Product missing a state.
+ auto* name2 = L"second name";
+ auto* id2 = L"second product id";
+ FakeWscProduct stateless_product(name2, id2, WSC_SECURITY_PRODUCT_STATE_OFF);
+ stateless_product.set_failed_step(FakeWscProduct::FailureStep::kProductState);
+
+ // Product missing a name.
+ auto* name3 = L"third name";
+ auto* id3 = L"third product id";
+ FakeWscProduct nameless_product(name3, id3,
+ WSC_SECURITY_PRODUCT_STATE_SNOOZED);
+ nameless_product.set_failed_step(FakeWscProduct::FailureStep::kProductName);
+
+ // Product missing a name.
+ auto* name4 = L"fourth name";
+ auto* id4 = L"fourth product id";
+ FakeWscProduct id_less_product(name4, id4,
+ WSC_SECURITY_PRODUCT_STATE_SNOOZED);
+ id_less_product.set_failed_step(FakeWscProduct::FailureStep::kProductId);
+
+ product_list_.Add(&on_product);
+ product_list_.Add(&stateless_product);
+ product_list_.Add(&nameless_product);
+ product_list_.Add(&id_less_product);
+
+ auto response = wsc_client_.GetAntiVirusProducts();
+
+ ExpectAvInitialized();
+ EXPECT_FALSE(response.query_error.has_value());
+
+ ASSERT_EQ(response.av_products.size(), 1U);
+ EXPECT_EQ(response.av_products[0].display_name, base::SysWideToUTF8(name1));
+ EXPECT_EQ(response.av_products[0].product_id, base::SysWideToUTF8(id1));
+ EXPECT_EQ(response.av_products[0].state, AvProductState::kOn);
+
+ ASSERT_EQ(response.parsing_errors.size(), 3U);
+ EXPECT_EQ(response.parsing_errors[0], WscParsingError::kFailedToGetState);
+ EXPECT_EQ(response.parsing_errors[1], WscParsingError::kFailedToGetName);
+ EXPECT_EQ(response.parsing_errors[2], WscParsingError::kFailedToGetId);
+}
+
+// Smoke/sanity test to verify that Defender's instance GUID does not change
+// over time. This test actually calls WSC.
+TEST(RealWscClientImplTest, SmokeWsc_GetAntiVirusProducts) {
+ // That part of the display name is not translated when getting it from WSC,
+ // so it can be used quite simply.
+ constexpr char kPartialDefenderName[] = "Microsoft Defender";
+
+ constexpr char kDefenderProductGuid[] =
+ "{D68DDC3A-831F-4fae-9E44-DA132C1ACF46}";
+
+ // WSC is only supported on Win8+ (and not server).
+ base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ if (os_info->version_type() == base::win::SUITE_SERVER ||
+ os_info->version() < base::win::Version::WIN8) {
+ return;
+ }
+
+ WscClientImpl wsc_client;
+ auto response = wsc_client.GetAntiVirusProducts();
+
+ for (const auto& av_product : response.av_products) {
+ if (av_product.display_name.find(kPartialDefenderName) !=
+ std::string::npos) {
+ EXPECT_EQ(av_product.product_id, kDefenderProductGuid);
+ }
+ }
+}
+
+} // namespace device_signals
diff --git a/chromium/components/digital_asset_links/digital_asset_links_handler.cc b/chromium/components/digital_asset_links/digital_asset_links_handler.cc
index e18ada8b60f..34ba3a9aa03 100644
--- a/chromium/components/digital_asset_links/digital_asset_links_handler.cc
+++ b/chromium/components/digital_asset_links/digital_asset_links_handler.cc
@@ -107,7 +107,7 @@ bool StatementHasMatchingFingerprint(const base::Value& statement,
void AddMessageToConsole(content::WebContents* web_contents,
const std::string& message) {
if (web_contents) {
- web_contents->GetMainFrame()->AddMessageToConsole(
+ web_contents->GetPrimaryMainFrame()->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kWarning, message);
return;
}
diff --git a/chromium/components/digital_goods/DEPS b/chromium/components/digital_goods/DEPS
new file mode 100644
index 00000000000..5287f1da9a7
--- /dev/null
+++ b/chromium/components/digital_goods/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+components/payments",
+ "+url"
+]
diff --git a/chromium/components/digital_goods/OWNERS b/chromium/components/digital_goods/OWNERS
new file mode 100644
index 00000000000..18731516101
--- /dev/null
+++ b/chromium/components/digital_goods/OWNERS
@@ -0,0 +1,3 @@
+glenrob@chromium.org
+peconn@chromium.org
+rouslan@chromium.org
diff --git a/chromium/components/digital_goods/README.md b/chromium/components/digital_goods/README.md
new file mode 100644
index 00000000000..e50abda9d65
--- /dev/null
+++ b/chromium/components/digital_goods/README.md
@@ -0,0 +1 @@
+This directory contains Digital Goods mojo types shared between Blink, ARC, and crosapi.
diff --git a/chromium/components/digital_goods/mojom/BUILD.gn b/chromium/components/digital_goods/mojom/BUILD.gn
new file mode 100644
index 00000000000..4789f6be885
--- /dev/null
+++ b/chromium/components/digital_goods/mojom/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2022 The Chromium 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("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+ generate_java = true
+
+ sources = [ "digital_goods.mojom" ]
+
+ deps = []
+
+ public_deps = [
+ "//components/payments/mojom",
+ "//url/mojom:url_mojom_gurl",
+ ]
+
+ export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
+ export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
+ export_header_blink = "third_party/blink/public/platform/web_common.h"
+}
diff --git a/chromium/components/data_reduction_proxy/core/common/OWNERS b/chromium/components/digital_goods/mojom/OWNERS
index 08850f42120..08850f42120 100644
--- a/chromium/components/data_reduction_proxy/core/common/OWNERS
+++ b/chromium/components/digital_goods/mojom/OWNERS
diff --git a/chromium/components/digital_goods/mojom/digital_goods.mojom b/chromium/components/digital_goods/mojom/digital_goods.mojom
new file mode 100644
index 00000000000..8bfdfa7db07
--- /dev/null
+++ b/chromium/components/digital_goods/mojom/digital_goods.mojom
@@ -0,0 +1,87 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// These types are shared between Blink, ARC, and crosapi, so they must conform
+// to the requirements of the strictest API and be stable and versioned.
+
+// Next MinVersion: 2
+
+[JavaPackage="org.chromium.payments.mojom"]
+module payments.mojom;
+
+import "url/mojom/url.mojom";
+import "components/payments/mojom/payment_request_data.mojom";
+
+// Result of Digital Goods operations. The response code is forwarded to the
+// renderer, so individual errors don't need to be handled. Any value other than
+// |kOK| will result in a JavaScript exception with a description message to aid
+// in debugging.
+[Extensible, Stable]
+enum BillingResponseCode {
+ // Success.
+ kOk,
+
+ // Unknown error calling a Digital Goods action (a more specific error code
+ // below is preferred).
+ kError,
+
+ // Item purchased is already owned.
+ kItemAlreadyOwned,
+
+ // Failure to consume an item since it is not owned.
+ kItemNotOwned,
+
+ // Requested item is not available for purchase.
+ kItemUnavailable,
+
+ // The requested client app is not installed.
+ kClientAppUnavailable,
+
+ // Client app failed to handle Digital Goods message format.
+ kClientAppError,
+};
+
+[Extensible, Stable]
+enum ItemType {
+ kUnknown,
+ kProduct,
+ kSubscription,
+};
+
+// Describes an item from a digital goods service provider.
+// See https://wicg.github.io/digital-goods/#itemDetails-dictionary
+[Stable]
+struct ItemDetails {
+ string item_id;
+ string title;
+ string description;
+ PaymentCurrencyAmount price;
+ // Periods are specified as ISO 8601 durations.
+ // https://en.wikipedia.org/wiki/ISO_8601#Durations
+ string? subscription_period;
+ string? free_trial_period;
+ PaymentCurrencyAmount? introductory_price;
+ string? introductory_price_period;
+ // Set to 0 for no introductory cycles.
+ [MinVersion=1] uint32 introductory_price_cycles;
+ [MinVersion=1] ItemType type;
+ [MinVersion=1] array<url.mojom.Url>? icon_urls;
+};
+
+// Result of creating a Digital Goods service.
+[Extensible, Stable]
+enum CreateDigitalGoodsResponseCode {
+ kOk,
+ kError,
+ kUnsupportedPaymentMethod,
+ kUnsupportedContext,
+};
+
+// Describes a specific purchase of an item.
+// See https://wicg.github.io/digital-goods/#purchaseDetails-dictionary
+[Stable]
+struct PurchaseReference {
+ string item_id;
+ string purchase_token;
+};
diff --git a/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc b/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
index 571ff78fcfb..87299eac0fb 100644
--- a/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
+++ b/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
@@ -29,6 +29,9 @@
namespace discardable_memory {
namespace {
+const base::Feature kShorterPeriodicPurge{"ShorterPeriodicPurge",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Global atomic to generate unique discardable shared memory IDs.
base::AtomicSequenceNumber g_next_discardable_shared_memory_id;
@@ -420,8 +423,13 @@ void ClientDiscardableSharedMemoryManager::ScheduledPurge() {
// recover the memory without adverse latency effects.
// TODO(crbug.com/1123679): Determine if |kMinAgeForScheduledPurge| and the
// constant from |ScheduledPurge| need to be tuned.
- PurgeUnlockedMemory(
- ClientDiscardableSharedMemoryManager::kMinAgeForScheduledPurge);
+ if (base::FeatureList::IsEnabled(kShorterPeriodicPurge)) {
+ PurgeUnlockedMemory(
+ ClientDiscardableSharedMemoryManager::kMinAgeForScheduledPurge / 2);
+ } else {
+ PurgeUnlockedMemory(
+ ClientDiscardableSharedMemoryManager::kMinAgeForScheduledPurge);
+ }
bool should_schedule = false;
{
diff --git a/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h b/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h
index 055c8f66f20..b22f65aee27 100644
--- a/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h
+++ b/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h
@@ -78,6 +78,8 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager
bytes_allocated_limit_for_testing_ = limit;
}
+ // Anything younger than |kMinAgeForScheduledPurge| is not discarded when we
+ // do our periodic purge.
static constexpr base::TimeDelta kMinAgeForScheduledPurge = base::Minutes(5);
// The expected cost of purging should be very small (< 1ms), so it can be
diff --git a/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc b/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc
index bf06b8c1757..ad058b888a8 100644
--- a/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc
+++ b/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc
@@ -161,7 +161,7 @@ class DiscardableMemoryImpl : public base::DiscardableMemory {
int64_t GetDefaultMemoryLimit() {
const int kMegabyte = 1024 * 1024;
-#if BUILDFLAG(IS_CHROMECAST)
+#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
// Bypass IsLowEndDevice() check and fix max_default_memory_limit to 64MB on
// Chromecast devices. Set value here as IsLowEndDevice() is used on some, but
// not all Chromecast devices.
diff --git a/chromium/components/dom_distiller/content/browser/android/BUILD.gn b/chromium/components/dom_distiller/content/browser/android/BUILD.gn
index 2b2ef464ea0..741eb0ecfc5 100644
--- a/chromium/components/dom_distiller/content/browser/android/BUILD.gn
+++ b/chromium/components/dom_distiller/content/browser/android/BUILD.gn
@@ -6,7 +6,8 @@ import("//build/config/android/rules.gni")
android_library("dom_distiller_content_java") {
deps = [
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/dom_distiller/core/android:dom_distiller_core_java",
"//content/public/android:content_java",
]
diff --git a/chromium/components/dom_distiller/content/browser/distillable_page_utils.cc b/chromium/components/dom_distiller/content/browser/distillable_page_utils.cc
index f1de05775f3..9910e29ed58 100644
--- a/chromium/components/dom_distiller/content/browser/distillable_page_utils.cc
+++ b/chromium/components/dom_distiller/content/browser/distillable_page_utils.cc
@@ -38,7 +38,7 @@ void OnExtractFeaturesJsResult(const DistillablePageDetector* detector,
void IsDistillablePageForDetector(content::WebContents* web_contents,
const DistillablePageDetector* detector,
base::OnceCallback<void(bool)> callback) {
- content::RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ content::RenderFrameHost* main_frame = web_contents->GetPrimaryMainFrame();
if (!main_frame) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), false));
diff --git a/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc b/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
index adb03f9078f..4effbe94af1 100644
--- a/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
+++ b/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
@@ -110,7 +110,7 @@ void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript(
} else {
DCHECK(buffer_.empty());
if (web_contents()) {
- RunIsolatedJavaScript(web_contents()->GetMainFrame(), buffer);
+ RunIsolatedJavaScript(web_contents()->GetPrimaryMainFrame(), buffer);
}
}
}
@@ -186,7 +186,7 @@ void DomDistillerViewerSource::RequestViewerHandle::DOMContentLoaded(
// No SendJavaScript() calls allowed before |buffer_| is run and cleared.
waiting_for_page_ready_ = false;
if (!buffer_.empty()) {
- RunIsolatedJavaScript(web_contents()->GetMainFrame(), buffer_);
+ RunIsolatedJavaScript(web_contents()->GetPrimaryMainFrame(), buffer_);
buffer_.clear();
}
// No need to Cancel() here.
diff --git a/chromium/components/dom_distiller/content/renderer/distillability_agent.cc b/chromium/components/dom_distiller/content/renderer/distillability_agent.cc
index 11546b3ce13..7a3265a74b4 100644
--- a/chromium/components/dom_distiller/content/renderer/distillability_agent.cc
+++ b/chromium/components/dom_distiller/content/renderer/distillability_agent.cc
@@ -68,7 +68,7 @@ bool IsLast(bool is_loaded) {
bool IsFiltered(const GURL& url) {
for (auto* filter : kFilterlist) {
- if (base::LowerCaseEqualsASCII(url.host(), filter)) {
+ if (base::EqualsCaseInsensitiveASCII(url.host(), filter)) {
return true;
}
}
@@ -241,9 +241,9 @@ void DistillabilityAgent::DidMeaningfulLayout(
}
DCHECK(render_frame());
- if (!render_frame()->IsMainFrame())
- return;
DCHECK(render_frame()->GetWebFrame());
+ if (!render_frame()->GetWebFrame()->IsOutermostMainFrame())
+ return;
blink::WebDocument doc = render_frame()->GetWebFrame()->GetDocument();
if (doc.IsNull() || doc.Body().IsNull())
return;
diff --git a/chromium/components/dom_distiller/core/android/BUILD.gn b/chromium/components/dom_distiller/core/android/BUILD.gn
index 95532b8827a..12dc9b406ef 100644
--- a/chromium/components/dom_distiller/core/android/BUILD.gn
+++ b/chromium/components/dom_distiller/core/android/BUILD.gn
@@ -7,6 +7,8 @@ import("//build/config/android/rules.gni")
android_library("dom_distiller_core_java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/dom_distiller/core/mojom:mojom_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//url:gurl_java",
diff --git a/chromium/components/dom_distiller/core/javascript/dom_distiller_viewer.js b/chromium/components/dom_distiller/core/javascript/dom_distiller_viewer.js
index 8fb849196ff..a4b32bcd724 100644
--- a/chromium/components/dom_distiller/core/javascript/dom_distiller_viewer.js
+++ b/chromium/components/dom_distiller/core/javascript/dom_distiller_viewer.js
@@ -433,9 +433,9 @@ class Pincher {
// eslint-disable-next-line no-var
var pincher, fontSizeSlider;
if (navigator.userAgent.toLowerCase().indexOf('android') > -1) {
- pincher = new Pincher;
+ pincher = new Pincher();
} else {
- fontSizeSlider = new FontSizeSlider;
+ fontSizeSlider = new FontSizeSlider();
}
function useFontScaling(scale) {
diff --git a/chromium/components/dom_distiller/core/viewer.cc b/chromium/components/dom_distiller/core/viewer.cc
index dda3f4ba8ad..dd4162fcb5b 100644
--- a/chromium/components/dom_distiller/core/viewer.cc
+++ b/chromium/components/dom_distiller/core/viewer.cc
@@ -10,6 +10,7 @@
#include "base/json/json_writer.h"
#include "base/metrics/histogram_macros.h"
+#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -24,7 +25,6 @@
#include "components/dom_distiller/core/url_utils.h"
#include "components/grit/components_resources.h"
#include "components/strings/grit/components_strings.h"
-#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/template_expressions.h"
diff --git a/chromium/components/dom_distiller/ios/BUILD.gn b/chromium/components/dom_distiller/ios/BUILD.gn
index 29c300ef966..3cff5240c58 100644
--- a/chromium/components/dom_distiller/ios/BUILD.gn
+++ b/chromium/components/dom_distiller/ios/BUILD.gn
@@ -17,6 +17,7 @@ source_set("ios") {
"//components/dom_distiller/core/proto",
"//components/favicon/ios",
"//ios/web/public",
+ "//ios/web/public/js_messaging",
"//url",
]
}
diff --git a/chromium/components/dom_distiller/ios/distiller_page_ios.mm b/chromium/components/dom_distiller/ios/distiller_page_ios.mm
index cb6701b9da7..e51048b2069 100644
--- a/chromium/components/dom_distiller/ios/distiller_page_ios.mm
+++ b/chromium/components/dom_distiller/ios/distiller_page_ios.mm
@@ -16,6 +16,8 @@
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "ios/web/public/browser_state.h"
+#import "ios/web/public/js_messaging/web_frame.h"
+#import "ios/web/public/js_messaging/web_frame_util.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/navigation/web_state_policy_decider.h"
#import "ios/web/public/web_state.h"
@@ -210,9 +212,16 @@ void DistillerPageIOS::OnLoadURLDone(
HandleJavaScriptResult(nil);
return;
}
+
+ web::WebFrame* main_frame = web::GetMainFrame(web_state_.get());
+ if (!main_frame) {
+ HandleJavaScriptResult(nil);
+ return;
+ }
+
// Inject the script.
base::WeakPtr<DistillerPageIOS> weak_this = weak_ptr_factory_.GetWeakPtr();
- web_state_->ExecuteJavaScript(
+ main_frame->ExecuteJavaScript(
base::UTF8ToUTF16(script_),
base::BindOnce(&DistillerPageIOS::HandleJavaScriptResult, weak_this));
}
diff --git a/chromium/components/download/internal/background_service/BUILD.gn b/chromium/components/download/internal/background_service/BUILD.gn
index 53e2ba80273..e1bc7082c83 100644
--- a/chromium/components/download/internal/background_service/BUILD.gn
+++ b/chromium/components/download/internal/background_service/BUILD.gn
@@ -77,6 +77,7 @@ if (is_android) {
deps = [
"//base:base_java",
+ "//base:jni_java",
"//net/android:net_java",
]
}
diff --git a/chromium/components/download/internal/background_service/config.cc b/chromium/components/download/internal/background_service/config.cc
index 2ffd9a691ed..830edae373b 100644
--- a/chromium/components/download/internal/background_service/config.cc
+++ b/chromium/components/download/internal/background_service/config.cc
@@ -7,6 +7,7 @@
#include <string>
#include "base/metrics/field_trial_params.h"
+#include "base/numerics/clamped_math.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "components/download/public/background_service/features.h"
diff --git a/chromium/components/download/internal/background_service/logger_impl.cc b/chromium/components/download/internal/background_service/logger_impl.cc
index 175e8170dd2..32c1489cf74 100644
--- a/chromium/components/download/internal/background_service/logger_impl.cc
+++ b/chromium/components/download/internal/background_service/logger_impl.cc
@@ -148,8 +148,12 @@ base::Value EntryToValue(
if (driver.has_value()) {
serialized_entry.SetDoubleKey("bytes_downloaded", driver->bytes_downloaded);
serialized_entry.SetKey("driver", DriverEntryToValue(driver.value()));
+ serialized_entry.SetStringKey(
+ "time_downloaded", base::TimeFormatHTTP(driver->completion_time));
} else {
serialized_entry.SetDoubleKey("bytes_downloaded", entry.bytes_downloaded);
+ serialized_entry.SetStringKey("time_downloaded",
+ base::TimeFormatHTTP(entry.completion_time));
}
if (completion_type.has_value()) {
diff --git a/chromium/components/download/internal/common/BUILD.gn b/chromium/components/download/internal/common/BUILD.gn
index 1ddc494ecc7..8566c5796ba 100644
--- a/chromium/components/download/internal/common/BUILD.gn
+++ b/chromium/components/download/internal/common/BUILD.gn
@@ -117,6 +117,8 @@ if (is_android) {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/android_provider:android_provider_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
diff --git a/chromium/components/download/internal/common/download_item_impl.cc b/chromium/components/download/internal/common/download_item_impl.cc
index 3b3b5907abd..22b5c89ec8c 100644
--- a/chromium/components/download/internal/common/download_item_impl.cc
+++ b/chromium/components/download/internal/common/download_item_impl.cc
@@ -454,7 +454,6 @@ DownloadItemImpl::DownloadItemImpl(
const base::FilePath& path,
const GURL& url,
const std::string& mime_type,
-
DownloadJob::CancelRequestCallback cancel_request_callback)
: request_info_(url),
guid_(base::GenerateGUID()),
@@ -555,23 +554,6 @@ void DownloadItemImpl::ValidateMixedContentDownload() {
MaybeCompleteDownload();
}
-void DownloadItemImpl::AcceptIncognitoWarning() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!incognito_warning_accepted_);
-
- DVLOG(20) << __func__ << "() download=" << DebugString(true);
-
- incognito_warning_accepted_ = true;
-
- UpdateObservers(); // TODO(asanka): This is potentially unsafe. The download
- // may not be in a consistent state or around at all after
- // invoking observers, but we keep it here because it is
- // used in ValidateDangerousDownload(), too.
- // http://crbug.com/586610
-
- MaybeCompleteDownload();
-}
-
void DownloadItemImpl::StealDangerousDownload(bool delete_file_afterward,
AcquireFileCallback callback) {
DVLOG(20) << __func__ << "() download = " << DebugString(true);
@@ -1063,11 +1045,6 @@ bool DownloadItemImpl::IsMixedContent() const {
mixed_content_status_ == MixedContentStatus::SILENT_BLOCK;
}
-bool DownloadItemImpl::ShouldShowIncognitoWarning() const {
- return base::FeatureList::IsEnabled(features::kIncognitoDownloadsWarning) &&
- delegate_->IsOffTheRecord() && !incognito_warning_accepted_;
-}
-
DownloadDangerType DownloadItemImpl::GetDangerType() const {
return danger_type_;
}
@@ -1759,6 +1736,7 @@ void DownloadItemImpl::OnDownloadTargetDetermined(
MixedContentStatus mixed_content_status,
const base::FilePath& intermediate_path,
const base::FilePath& display_name,
+ const std::string& mime_type,
absl::optional<DownloadSchedule> download_schedule,
DownloadInterruptReason interrupt_reason) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -1805,6 +1783,8 @@ void DownloadItemImpl::OnDownloadTargetDetermined(
mixed_content_status_ = mixed_content_status;
if (!display_name.empty())
SetDisplayName(display_name);
+ if (!mime_type.empty())
+ mime_type_ = mime_type;
// This was an interrupted download that was looking for a filename. Resolve
// early without performing the intermediate rename. If there is a
@@ -2403,10 +2383,6 @@ bool DownloadItemImpl::IsDownloadReadyForCompletion(
if (IsMixedContent())
return false;
- if (ShouldShowIncognitoWarning()) {
- return false;
- }
-
// Check for consistency before invoking delegate. Since there are no pending
// target determination calls and the download is in progress, both the target
// and current paths should be non-empty and they should point to the same
diff --git a/chromium/components/download/internal/common/download_item_impl_delegate.cc b/chromium/components/download/internal/common/download_item_impl_delegate.cc
index 3a2f6e4c2a4..9fbe16db00b 100644
--- a/chromium/components/download/internal/common/download_item_impl_delegate.cc
+++ b/chromium/components/download/internal/common/download_item_impl_delegate.cc
@@ -38,7 +38,8 @@ void DownloadItemImplDelegate::DetermineDownloadTarget(
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, target_path, base::FilePath(),
- absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+ std::string(), absl::nullopt /*download_schedule*/,
+ DOWNLOAD_INTERRUPT_REASON_NONE);
}
bool DownloadItemImplDelegate::ShouldCompleteDownload(
diff --git a/chromium/components/download/internal/common/download_item_impl_unittest.cc b/chromium/components/download/internal/common/download_item_impl_unittest.cc
index 8b0fa6e8b0b..bb347804021 100644
--- a/chromium/components/download/internal/common/download_item_impl_unittest.cc
+++ b/chromium/components/download/internal/common/download_item_impl_unittest.cc
@@ -373,7 +373,8 @@ class DownloadItemTest : public testing::Test {
std::move(callback).Run(
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, danger_type,
DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
- base::FilePath(), download_schedule, DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/, download_schedule,
+ DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
}
@@ -634,8 +635,8 @@ TEST_F(DownloadItemTest, NotificationAfterOnDownloadTargetDetermined) {
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
EXPECT_FALSE(observer.CheckAndResetDownloadUpdated());
task_environment_.RunUntilIdle();
EXPECT_TRUE(observer.CheckAndResetDownloadUpdated());
@@ -924,8 +925,8 @@ TEST_F(DownloadItemTest, AutomaticResumption_AttemptLimit) {
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
// Use a continuable interrupt.
@@ -1019,7 +1020,8 @@ TEST_F(DownloadItemTest, FailedResumptionDoesntUpdateOriginState) {
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN,
base::FilePath(kDummyIntermediatePath), base::FilePath(),
- absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+ std::string() /*mime_type*/, absl::nullopt /*download_schedule*/,
+ DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
ASSERT_TRUE(item->GetResponseHeaders());
@@ -1221,8 +1223,8 @@ TEST_F(DownloadItemTest, DisplayName) {
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
EXPECT_EQ(FILE_PATH_LITERAL("foo.bar"),
item->GetFileNameToReportUser().value());
@@ -1276,7 +1278,8 @@ TEST_F(DownloadItemTest, InitDownloadFileFails) {
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN,
base::FilePath(kDummyIntermediatePath), base::FilePath(),
- absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+ std::string() /*mime_type*/, absl::nullopt /*download_schedule*/,
+ DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
@@ -1319,8 +1322,8 @@ TEST_F(DownloadItemTest, StartFailedDownload) {
.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, target_path,
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
// Interrupt reason carried in create info should be recorded.
@@ -1351,8 +1354,8 @@ TEST_F(DownloadItemTest, CallbackAfterRename) {
final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1402,8 +1405,8 @@ TEST_F(DownloadItemTest, CallbackAfterInterruptedRename) {
final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1468,8 +1471,8 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Restart) {
final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1515,8 +1518,8 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Continue) {
final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1557,8 +1560,8 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Failed) {
final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1601,7 +1604,8 @@ TEST_F(DownloadItemTest, DownloadTargetDetermined_Cancel) {
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN,
base::FilePath(FILE_PATH_LITERAL("bar")),
- base::FilePath(), absl::nullopt /*download_schedule*/,
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/,
DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
EXPECT_EQ(DownloadItem::CANCELLED, item->GetState());
}
@@ -1616,8 +1620,8 @@ TEST_F(DownloadItemTest, DownloadTargetDetermined_CancelWithEmptyName) {
base::FilePath(), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, base::FilePath(),
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
EXPECT_EQ(DownloadItem::CANCELLED, item->GetState());
}
@@ -1632,13 +1636,34 @@ TEST_F(DownloadItemTest, DownloadTargetDetermined_Conflict) {
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, target_path, base::FilePath(),
- absl::nullopt /*download_schedule*/,
+ std::string() /*mime_type*/, absl::nullopt /*download_schedule*/,
DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE);
EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE,
item->GetLastReason());
}
+TEST_F(DownloadItemTest, DownloadTargetDetermined_NewMimeType) {
+ DownloadItemImpl* item = CreateDownloadItem();
+ DownloadItemImplDelegate::DownloadTargetCallback callback;
+ MockDownloadFile* file = CallDownloadItemStart(item, &callback);
+ base::FilePath target_path(FILE_PATH_LITERAL("/foo/bar"));
+ base::FilePath intermediate_path(target_path.InsertBeforeExtensionASCII("x"));
+ auto task_runner = base::ThreadTaskRunnerHandle::Get();
+ SetRenameExpectation(file, task_runner, intermediate_path,
+ DOWNLOAD_INTERRUPT_REASON_NONE);
+
+ std::string mime_type = "application/pdf";
+ std::move(callback).Run(
+ target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+ DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+ DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
+ base::FilePath(), mime_type, absl::nullopt /*download_schedule*/,
+ DOWNLOAD_INTERRUPT_REASON_NONE);
+ EXPECT_EQ(mime_type, item->GetMimeType());
+ CleanupItem(item, file, DownloadItem::IN_PROGRESS);
+}
+
TEST_F(DownloadItemTest, FileRemoved) {
DownloadItemImpl* item = CreateDownloadItem();
@@ -2500,8 +2525,8 @@ TEST_P(DownloadItemDestinationUpdateRaceTest, DownloadCancelledByUser) {
.Run(base::FilePath(), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN, base::FilePath(),
- base::FilePath(), absl::nullopt /*download_schedule*/,
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ base::FilePath(), std::string() /*mime_type*/,
+ absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
EXPECT_EQ(DownloadItem::CANCELLED, item_->GetState());
EXPECT_TRUE(canceled());
task_environment_.RunUntilIdle();
@@ -2551,7 +2576,8 @@ TEST_P(DownloadItemDestinationUpdateRaceTest, IntermediateRenameFails) {
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN,
base::FilePath(kDummyIntermediatePath), base::FilePath(),
- absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+ std::string() /*mime_type*/, absl::nullopt /*download_schedule*/,
+ DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
ASSERT_FALSE(intermediate_rename_callback.is_null());
@@ -2616,7 +2642,8 @@ TEST_P(DownloadItemDestinationUpdateRaceTest, IntermediateRenameSucceeds) {
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadItem::MixedContentStatus::UNKNOWN,
base::FilePath(kDummyIntermediatePath), base::FilePath(),
- absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+ std::string() /*mime_type*/, absl::nullopt /*download_schedule*/,
+ DOWNLOAD_INTERRUPT_REASON_NONE);
task_environment_.RunUntilIdle();
ASSERT_FALSE(intermediate_rename_callback.is_null());
diff --git a/chromium/components/download/internal/common/download_response_handler.cc b/chromium/components/download/internal/common/download_response_handler.cc
index ed1e7f021fb..ee44dabebe1 100644
--- a/chromium/components/download/internal/common/download_response_handler.cc
+++ b/chromium/components/download/internal/common/download_response_handler.cc
@@ -128,8 +128,14 @@ void DownloadResponseHandler::OnReceiveResponse(
if (create_info_->result != DOWNLOAD_INTERRUPT_REASON_NONE)
OnResponseStarted(mojom::DownloadStreamHandlePtr());
- if (body)
- OnStartLoadingResponseBody(std::move(body));
+ if (started_)
+ return;
+
+ mojom::DownloadStreamHandlePtr stream_handle =
+ mojom::DownloadStreamHandle::New();
+ stream_handle->stream = std::move(body);
+ stream_handle->client_receiver = client_remote_.BindNewPipeAndPassReceiver();
+ OnResponseStarted(std::move(stream_handle));
}
std::unique_ptr<DownloadCreateInfo>
@@ -231,18 +237,6 @@ void DownloadResponseHandler::OnReceiveCachedMetadata(
void DownloadResponseHandler::OnTransferSizeUpdated(
int32_t transfer_size_diff) {}
-void DownloadResponseHandler::OnStartLoadingResponseBody(
- mojo::ScopedDataPipeConsumerHandle body) {
- if (started_)
- return;
-
- mojom::DownloadStreamHandlePtr stream_handle =
- mojom::DownloadStreamHandle::New();
- stream_handle->stream = std::move(body);
- stream_handle->client_receiver = client_remote_.BindNewPipeAndPassReceiver();
- OnResponseStarted(std::move(stream_handle));
-}
-
void DownloadResponseHandler::OnComplete(
const network::URLLoaderCompletionStatus& status) {
if (completed_)
diff --git a/chromium/components/download/internal/common/in_progress_download_manager.cc b/chromium/components/download/internal/common/in_progress_download_manager.cc
index bab233a4a25..f89e5056e4c 100644
--- a/chromium/components/download/internal/common/in_progress_download_manager.cc
+++ b/chromium/components/download/internal/common/in_progress_download_manager.cc
@@ -184,7 +184,7 @@ void OnPathReserved(DownloadItemImplDelegate::DownloadTargetCallback callback,
std::move(callback).Run(
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, danger_type,
mixed_content_status, intermediate_path, base::FilePath(),
- std::move(download_schedule),
+ std::string() /*mime_type*/, std::move(download_schedule),
intermediate_path.empty() ? DOWNLOAD_INTERRUPT_REASON_FILE_FAILED
: DOWNLOAD_INTERRUPT_REASON_NONE);
}
@@ -404,8 +404,8 @@ void InProgressDownloadManager::DetermineDownloadTarget(
std::move(callback).Run(
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
download->GetDangerType(), download->GetMixedContentStatus(),
- target_path, base::FilePath(), download->GetDownloadSchedule(),
- DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
+ target_path, base::FilePath(), std::string() /*mime_type*/,
+ download->GetDownloadSchedule(), DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
RecordBackgroundTargetDeterminationResult(
BackgroudTargetDeterminationResultTypes::kTargetPathMissing);
return;
@@ -417,8 +417,8 @@ void InProgressDownloadManager::DetermineDownloadTarget(
std::move(callback).Run(
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
download->GetDangerType(), download->GetMixedContentStatus(),
- target_path, base::FilePath(), download->GetDownloadSchedule(),
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ target_path, base::FilePath(), std::string() /*mime_type*/,
+ download->GetDownloadSchedule(), DOWNLOAD_INTERRUPT_REASON_NONE);
RecordBackgroundTargetDeterminationResult(
BackgroudTargetDeterminationResultTypes::kSuccess);
return;
@@ -441,8 +441,8 @@ void InProgressDownloadManager::DetermineDownloadTarget(
std::move(callback).Run(
target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
download->GetDangerType(), download->GetMixedContentStatus(),
- intermediate_path, base::FilePath(), download->GetDownloadSchedule(),
- DOWNLOAD_INTERRUPT_REASON_NONE);
+ intermediate_path, base::FilePath(), std::string() /*mime_type*/,
+ download->GetDownloadSchedule(), DOWNLOAD_INTERRUPT_REASON_NONE);
#endif // BUILDFLAG(IS_ANDROID)
}
diff --git a/chromium/components/download/internal/common/resource_downloader.cc b/chromium/components/download/internal/common/resource_downloader.cc
index 2934f2bacde..565ea4e51ee 100644
--- a/chromium/components/download/internal/common/resource_downloader.cc
+++ b/chromium/components/download/internal/common/resource_downloader.cc
@@ -40,8 +40,6 @@ class URLLoaderStatusMonitor : public network::mojom::URLLoaderClient {
OnUploadProgressCallback callback) override {}
void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override {}
void OnTransferSizeUpdated(int32_t transfer_size_diff) override {}
- void OnStartLoadingResponseBody(
- mojo::ScopedDataPipeConsumerHandle body) override {}
void OnComplete(const network::URLLoaderCompletionStatus& status) override;
private:
@@ -219,8 +217,7 @@ void ResourceDownloader::InterceptResponse(
// Simulate on the new URLLoaderClient calls that happened on the old client.
response_head->cert_status = cert_status;
url_loader_client_->OnReceiveResponse(std::move(response_head),
- mojo::ScopedDataPipeConsumerHandle());
- url_loader_client_->OnStartLoadingResponseBody(std::move(response_body));
+ std::move(response_body));
// Bind the new client.
url_loader_client_receiver_ =
diff --git a/chromium/components/download/network/BUILD.gn b/chromium/components/download/network/BUILD.gn
index a57934e5a2f..59904ad6daf 100644
--- a/chromium/components/download/network/BUILD.gn
+++ b/chromium/components/download/network/BUILD.gn
@@ -53,6 +53,8 @@ if (is_android) {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//net/android:net_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
@@ -75,7 +77,6 @@ if (is_android) {
"//base:base_java",
"//base:base_java_test_support",
"//net/android:net_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
diff --git a/chromium/components/download/public/common/BUILD.gn b/chromium/components/download/public/common/BUILD.gn
index 9d74c12d417..cb5c98cf8fa 100644
--- a/chromium/components/download/public/common/BUILD.gn
+++ b/chromium/components/download/public/common/BUILD.gn
@@ -120,7 +120,6 @@ if (is_android) {
srcjar_deps = [ ":jni_enums" ]
deps = [
- "//base:base_java",
"//components/download/internal/common:internal_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
diff --git a/chromium/components/download/public/common/download_features.cc b/chromium/components/download/public/common/download_features.cc
index 602b950e06b..d48485bffd1 100644
--- a/chromium/components/download/public/common/download_features.cc
+++ b/chromium/components/download/public/common/download_features.cc
@@ -69,9 +69,6 @@ const base::Feature kDeleteOverwrittenDownloads{
const base::Feature kAllowFileBufferSizeControl{
"AllowFileBufferSizeControl", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kIncognitoDownloadsWarning{
- "IncognitoDownloadsWarning", base::FEATURE_DISABLED_BY_DEFAULT};
-
const base::Feature kDownloadRange{"DownloadRange",
base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chromium/components/download/public/common/download_features.h b/chromium/components/download/public/common/download_features.h
index 42b8f5b515e..b050cf54fd1 100644
--- a/chromium/components/download/public/common/download_features.h
+++ b/chromium/components/download/public/common/download_features.h
@@ -71,10 +71,6 @@ COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature
COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature
kAllowFileBufferSizeControl;
-// Whether to show warning when downloading in incognito.
-COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature
- kIncognitoDownloadsWarning;
-
// Arbitrary range request support for download system.
COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature kDownloadRange;
} // namespace features
diff --git a/chromium/components/download/public/common/download_item.h b/chromium/components/download/public/common/download_item.h
index f7b65b6f442..7635af502de 100644
--- a/chromium/components/download/public/common/download_item.h
+++ b/chromium/components/download/public/common/download_item.h
@@ -180,9 +180,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItem : public base::SupportsUserData {
// Called when the user has validated the download of a mixed content file.
virtual void ValidateMixedContentDownload() = 0;
- // Called when user accepts Incognito download warning.
- virtual void AcceptIncognitoWarning() = 0;
-
// Called to acquire a dangerous download. If |delete_file_afterward| is true,
// invokes |callback| on the UI thread with the path to the downloaded file,
// and removes the DownloadItem from views and history if appropriate.
@@ -433,11 +430,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItem : public base::SupportsUserData {
// False if not mixed content or that function has been called.
virtual bool IsMixedContent() const = 0;
- // True if file is downloaded in Incognito and user has not accepted it yet.
- // False if file is downloaded in regular mode or has accepted the incognito
- // warning.
- virtual bool ShouldShowIncognitoWarning() const = 0;
-
// Why |safety_state_| is not SAFE.
virtual DownloadDangerType GetDangerType() const = 0;
diff --git a/chromium/components/download/public/common/download_item_impl.h b/chromium/components/download/public/common/download_item_impl.h
index d82d11f4344..185b14c6b5a 100644
--- a/chromium/components/download/public/common/download_item_impl.h
+++ b/chromium/components/download/public/common/download_item_impl.h
@@ -249,7 +249,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl
void UpdateObservers() override;
void ValidateDangerousDownload() override;
void ValidateMixedContentDownload() override;
- void AcceptIncognitoWarning() override;
void StealDangerousDownload(bool need_removal,
AcquireFileCallback callback) override;
void Pause() override;
@@ -306,7 +305,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl
const DownloadItemRerouteInfo& GetRerouteInfo() const override;
bool IsDangerous() const override;
bool IsMixedContent() const override;
- bool ShouldShowIncognitoWarning() const override;
DownloadDangerType GetDangerType() const override;
MixedContentStatus GetMixedContentStatus() const override;
bool TimeRemaining(base::TimeDelta* remaining) const override;
@@ -605,6 +603,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl
MixedContentStatus mixed_content_status,
const base::FilePath& intermediate_path,
const base::FilePath& display_name,
+ const std::string& mime_type,
absl::optional<DownloadSchedule> download_schedule,
DownloadInterruptReason interrupt_reason);
@@ -793,9 +792,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl
// The current state of this download.
DownloadInternalState state_ = INITIAL_INTERNAL;
- // A flag for indicating whether user has accepted incognito warning or not
- bool incognito_warning_accepted_ = false;
-
// Current danger type for the download.
DownloadDangerType danger_type_ = DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
diff --git a/chromium/components/download/public/common/download_item_impl_delegate.h b/chromium/components/download/public/common/download_item_impl_delegate.h
index 37df534ec0e..99b631cd40e 100644
--- a/chromium/components/download/public/common/download_item_impl_delegate.h
+++ b/chromium/components/download/public/common/download_item_impl_delegate.h
@@ -53,6 +53,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImplDelegate {
DownloadItem::MixedContentStatus mixed_content_status,
const base::FilePath& intermediate_path,
const base::FilePath& display_name,
+ const std::string& mime_type,
absl::optional<DownloadSchedule> download_schedule,
DownloadInterruptReason interrupt_reason)>;
// Request determination of the download target from the delegate.
diff --git a/chromium/components/download/public/common/download_response_handler.h b/chromium/components/download/public/common/download_response_handler.h
index d56dad69b1a..47e816d6139 100644
--- a/chromium/components/download/public/common/download_response_handler.h
+++ b/chromium/components/download/public/common/download_response_handler.h
@@ -75,8 +75,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadResponseHandler
OnUploadProgressCallback callback) override;
void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override;
void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
- void OnStartLoadingResponseBody(
- mojo::ScopedDataPipeConsumerHandle body) override;
void OnComplete(const network::URLLoaderCompletionStatus& status) override;
private:
diff --git a/chromium/components/download/public/common/mock_download_item.h b/chromium/components/download/public/common/mock_download_item.h
index 4e45bf86ab8..5bb25645a1b 100644
--- a/chromium/components/download/public/common/mock_download_item.h
+++ b/chromium/components/download/public/common/mock_download_item.h
@@ -46,7 +46,6 @@ class MockDownloadItem : public DownloadItem {
MOCK_METHOD0(UpdateObservers, void());
MOCK_METHOD0(ValidateDangerousDownload, void());
MOCK_METHOD0(ValidateMixedContentDownload, void());
- MOCK_METHOD0(AcceptIncognitoWarning, void());
MOCK_METHOD2(StealDangerousDownload, void(bool, AcquireFileCallback));
MOCK_METHOD0(Pause, void());
MOCK_METHOD1(Resume, void(bool));
@@ -108,7 +107,6 @@ class MockDownloadItem : public DownloadItem {
(const override));
MOCK_CONST_METHOD0(IsDangerous, bool());
MOCK_CONST_METHOD0(IsMixedContent, bool());
- MOCK_CONST_METHOD0(ShouldShowIncognitoWarning, bool());
MOCK_CONST_METHOD0(GetDangerType, DownloadDangerType());
MOCK_CONST_METHOD0(GetMixedContentStatus, MixedContentStatus());
MOCK_CONST_METHOD1(TimeRemaining, bool(base::TimeDelta*));
diff --git a/chromium/components/download/public/common/mock_download_item_impl.h b/chromium/components/download/public/common/mock_download_item_impl.h
index f7f38a3cbb5..ace02f3d434 100644
--- a/chromium/components/download/public/common/mock_download_item_impl.h
+++ b/chromium/components/download/public/common/mock_download_item_impl.h
@@ -27,13 +27,14 @@ class MockDownloadItemImpl : public DownloadItemImpl {
explicit MockDownloadItemImpl(DownloadItemImplDelegate* delegate);
~MockDownloadItemImpl() override;
- MOCK_METHOD8(OnDownloadTargetDetermined,
+ MOCK_METHOD9(OnDownloadTargetDetermined,
void(const base::FilePath&,
TargetDisposition,
DownloadDangerType,
MixedContentStatus,
const base::FilePath&,
const base::FilePath&,
+ const std::string&,
absl::optional<DownloadSchedule>,
DownloadInterruptReason));
MOCK_METHOD1(AddObserver, void(DownloadItem::Observer*));
@@ -46,7 +47,6 @@ class MockDownloadItemImpl : public DownloadItemImpl {
MOCK_METHOD0(OpenDownload, void());
MOCK_METHOD0(ShowDownloadInShell, void());
MOCK_METHOD0(ValidateDangerousDownload, void());
- MOCK_METHOD0(AcceptIncognitoWarning, void());
MOCK_METHOD2(StealDangerousDownload, void(bool, AcquireFileCallback));
MOCK_METHOD3(UpdateProgress, void(int64_t, int64_t, const std::string&));
MOCK_METHOD1(Cancel, void(bool));
@@ -110,7 +110,6 @@ class MockDownloadItemImpl : public DownloadItemImpl {
MOCK_CONST_METHOD0(GetMixedContentStatus, MixedContentStatus());
MOCK_CONST_METHOD0(IsDangerous, bool());
MOCK_CONST_METHOD0(IsMixedContent, bool());
- MOCK_CONST_METHOD0(ShouldShowIncognitoWarning, bool());
MOCK_METHOD0(GetAutoOpened, bool());
MOCK_CONST_METHOD0(GetForcedFilePath, const base::FilePath&());
MOCK_CONST_METHOD0(HasUserGesture, bool());
diff --git a/chromium/components/download/public/task/BUILD.gn b/chromium/components/download/public/task/BUILD.gn
index 699fcd2dea8..124cbbac5c3 100644
--- a/chromium/components/download/public/task/BUILD.gn
+++ b/chromium/components/download/public/task/BUILD.gn
@@ -32,10 +32,7 @@ if (is_android) {
android_library("public_java") {
srcjar_deps = [ ":jni_enums" ]
- deps = [
- "//base:base_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
- ]
+ deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
}
java_cpp_enum("jni_enums") {
diff --git a/chromium/components/download/resources/download_internals/download_internals.html b/chromium/components/download/resources/download_internals/download_internals.html
index 80cfdacf97b..b656efa8d64 100644
--- a/chromium/components/download/resources/download_internals/download_internals.html
+++ b/chromium/components/download/resources/download_internals/download_internals.html
@@ -59,6 +59,7 @@
<th>ID</th>
<th>URL</th>
<th>Size</th>
+ <th>Time</th>
</tr>
</thead>
<tbody>
@@ -72,6 +73,7 @@
<div jscontent="file_path"></div>
</td>
<td jscontent="bytes_downloaded"></td>
+ <td jscontent="time_downloaded"></td>
</tr>
</tbody>
</table>
diff --git a/chromium/components/download/resources/download_internals/download_internals_browser_proxy.js b/chromium/components/download/resources/download_internals/download_internals_browser_proxy.js
index 27f7534e0f0..c35a58b31a9 100644
--- a/chromium/components/download/resources/download_internals/download_internals_browser_proxy.js
+++ b/chromium/components/download/resources/download_internals/download_internals_browser_proxy.js
@@ -72,6 +72,7 @@ export let ServiceStatus;
* state: !ServiceEntryState,
* url: string,
* bytes_downloaded: number,
+ * time_downloaded: string,
* result: (!ServiceEntryResult|undefined),
* driver: {
* state: !DriverEntryState,
diff --git a/chromium/components/embedder_support/OWNERS b/chromium/components/embedder_support/OWNERS
index d04ad56e631..839d6b72600 100644
--- a/chromium/components/embedder_support/OWNERS
+++ b/chromium/components/embedder_support/OWNERS
@@ -1,3 +1,2 @@
-aarontag@chromium.org
-abeyad@chromium.org
miketaylr@chromium.org
+victortan@chromium.org
diff --git a/chromium/components/embedder_support/android/BUILD.gn b/chromium/components/embedder_support/android/BUILD.gn
index 5ac1ca67ac6..7aa380ab68e 100644
--- a/chromium/components/embedder_support/android/BUILD.gn
+++ b/chromium/components/embedder_support/android/BUILD.gn
@@ -21,7 +21,7 @@ android_library("browser_context_java") {
android_library("simple_factory_key_java") {
sources = [ "java/src/org/chromium/components/embedder_support/simple_factory_key/SimpleFactoryKeyHandle.java" ]
- deps = [ "//base:base_java" ]
+ deps = [ "//base:jni_java" ]
}
generate_jni("simple_factory_key_jni_headers") {
@@ -41,7 +41,10 @@ static_library("simple_factory_key") {
}
android_library("application_java") {
- deps = [ "//base:base_java" ]
+ deps = [
+ "//base:base_java",
+ "//build/android:build_java",
+ ]
sources = [
"java/src/org/chromium/components/embedder_support/application/ClassLoaderContextWrapperFactory.java",
"java/src/org/chromium/components/embedder_support/application/FirebaseConfig.java",
@@ -60,6 +63,8 @@ android_library("util_java") {
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/url_formatter/android:url_formatter_java",
"//content/public/android:content_main_dex_java",
"//net/android:net_java",
@@ -141,7 +146,8 @@ android_library("content_view_java") {
android_library("view_java") {
deps = [
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_java",
"//ui/android:ui_no_recycler_view_java",
]
@@ -203,6 +209,8 @@ android_library("web_contents_delegate_java") {
deps = [
":web_contents_delegate_java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_core_core_java",
@@ -252,7 +260,7 @@ generate_jni("context_menu_jni_headers") {
android_library("context_menu_java") {
deps = [
- "//base:base_java",
+ "//base:jni_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/blink/public:blink_headers_java",
@@ -267,7 +275,7 @@ android_library("native_java_unittests_java") {
testonly = true
deps = [
":util_java",
- "//base:base_java",
+ "//base:jni_java",
"//third_party/junit",
]
sources = [ "native_java_unittests/src/org/chromium/components/embedder_support/util/InputStreamUnittest.java" ]
@@ -320,7 +328,6 @@ android_library("embedder_support_javatests") {
":util_java",
":web_contents_delegate_java",
":web_contents_delegate_java_resources",
- "//base:base_java",
"//base:base_java_test_support",
"//content/public/test/android:content_java_test_support",
"//third_party/android_support_test_runner:rules_java",
diff --git a/chromium/components/embedder_support/android/metrics/BUILD.gn b/chromium/components/embedder_support/android/metrics/BUILD.gn
index f503ee56b38..649f14e4b8e 100644
--- a/chromium/components/embedder_support/android/metrics/BUILD.gn
+++ b/chromium/components/embedder_support/android/metrics/BUILD.gn
@@ -50,6 +50,7 @@ android_library("java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
srcjar_deps = [ ":java_enum_srcjar" ]
diff --git a/chromium/components/embedder_support/android/metrics/android_metrics_service_client.cc b/chromium/components/embedder_support/android/metrics/android_metrics_service_client.cc
index 85ed657e149..0fef2d04d5d 100644
--- a/chromium/components/embedder_support/android/metrics/android_metrics_service_client.cc
+++ b/chromium/components/embedder_support/android/metrics/android_metrics_service_client.cc
@@ -132,7 +132,7 @@ void RegisterOrRemovePreviousRunMetricsFile(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
- base::BindOnce(base::GetDeleteFileCallback(), metrics_file));
+ base::GetDeleteFileCallback(metrics_file));
}
}
@@ -193,8 +193,8 @@ std::unique_ptr<metrics::FileMetricsProvider> CreateFileMetricsProvider(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
- base::BindOnce(base::GetDeletePathRecursivelyCallback(),
- std::move(browser_metrics_upload_dir)));
+ base::GetDeletePathRecursivelyCallback(
+ std::move(browser_metrics_upload_dir)));
}
return file_metrics_provider;
@@ -324,6 +324,7 @@ void AndroidMetricsServiceClient::MaybeStartMetrics() {
pref_service_, /* metrics_reporting_enabled */ false));
OnMetricsNotStarted();
pref_service_->ClearPref(prefs::kMetricsClientID);
+ pref_service_->ClearPref(prefs::kMetricsProvisionalClientID);
}
}
diff --git a/chromium/components/embedder_support/android/metrics/android_metrics_service_client.h b/chromium/components/embedder_support/android/metrics/android_metrics_service_client.h
index 5cf3838f4b3..12f41d27cb8 100644
--- a/chromium/components/embedder_support/android/metrics/android_metrics_service_client.h
+++ b/chromium/components/embedder_support/android/metrics/android_metrics_service_client.h
@@ -220,7 +220,7 @@ class AndroidMetricsServiceClient : public MetricsServiceClient,
// we log metrics. If this returns false, MetricsServiceClient should
// indicate reporting is disabled. Sampling is due to storage/bandwidth
// considerations.
- bool IsInSample() const;
+ virtual bool IsInSample() const;
// Returns the installer type of the app.
virtual InstallerPackageType GetInstallerPackageType();
diff --git a/chromium/components/embedder_support/android/util/android_stream_reader_url_loader.cc b/chromium/components/embedder_support/android/util/android_stream_reader_url_loader.cc
index 0ed93d613ee..fe0bbb8ba58 100644
--- a/chromium/components/embedder_support/android/util/android_stream_reader_url_loader.cc
+++ b/chromium/components/embedder_support/android/util/android_stream_reader_url_loader.cc
@@ -20,6 +20,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "components/embedder_support/android/util/input_stream.h"
#include "components/embedder_support/android/util/input_stream_reader.h"
+#include "net/base/features.h"
#include "net/base/io_buffer.h"
#include "net/base/mime_sniffer.h"
#include "net/http/http_status_code.h"
@@ -86,6 +87,26 @@ class InputStreamReaderWrapper
}
int ReadRawData(net::IOBuffer* buffer, int buffer_size) {
+ if (base::FeatureList::IsEnabled(net::features::kOptimizeNetworkBuffers)) {
+ int available = 0;
+ // Only use `available` if the app has an estimate, otherwise it'll return
+ // 0. In that case we still want to do a blocking read until there's data
+ // or EOF.
+ if (input_stream_->BytesAvailable(&available) && available > 0) {
+ // Make sure a bad app doesn't lead to reading past the buffer.
+ buffer_size = std::min(available, buffer_size);
+ } else {
+ // `buffer_size' could be large since it comes from the size of the data
+ // pipe, but we don't want to synchronously wait for too many bytes in
+ // case they're coming from the network.
+ buffer_size = std::min(
+ net::features::
+ kOptimizeNetworkBuffersMaxInputStreamBytesToReadWhenAvailableUnknown
+ .Get(),
+ buffer_size);
+ }
+ }
+
return input_stream_reader_->ReadRawData(buffer, buffer_size);
}
@@ -290,8 +311,7 @@ void AndroidStreamReaderURLLoader::SendBody() {
MojoCreateDataPipeOptions* options_ptr = nullptr;
MojoCreateDataPipeOptions options;
- if (base::FeatureList::IsEnabled(
- network::features::kOptimizeNetworkBuffers)) {
+ if (base::FeatureList::IsEnabled(net::features::kOptimizeNetworkBuffers)) {
options_ptr = &options;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
@@ -330,8 +350,7 @@ void AndroidStreamReaderURLLoader::SendResponseToClient() {
cache_response_ =
response_delegate_->ShouldCacheResponse(response_head_.get());
client_->OnReceiveResponse(std::move(response_head_),
- mojo::ScopedDataPipeConsumerHandle());
- client_->OnStartLoadingResponseBody(std::move(consumer_handle_));
+ std::move(consumer_handle_));
}
void AndroidStreamReaderURLLoader::ReadMore() {
diff --git a/chromium/components/embedder_support/android/util/url_utilities.cc b/chromium/components/embedder_support/android/util/url_utilities.cc
index 3c83ccd9a79..dcb369d72ba 100644
--- a/chromium/components/embedder_support/android/util/url_utilities.cc
+++ b/chromium/components/embedder_support/android/util/url_utilities.cc
@@ -6,10 +6,10 @@
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
+#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "components/embedder_support/android/util_jni_headers/UrlUtilities_jni.h"
#include "components/google/core/common/google_util.h"
-#include "net/base/escape.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/url_util.h"
#include "url/android/gurl_android.h"
@@ -174,7 +174,7 @@ static ScopedJavaLocalRef<jstring> JNI_UrlUtilities_EscapeQueryParamValue(
const JavaParamRef<jstring>& url,
jboolean use_plus) {
return ConvertUTF8ToJavaString(
- env, net::EscapeQueryParamValue(
+ env, base::EscapeQueryParamValue(
base::android::ConvertJavaStringToUTF8(url), use_plus));
}
diff --git a/chromium/components/embedder_support/pref_names.cc b/chromium/components/embedder_support/pref_names.cc
index e0b70f2e73b..c2a04d03f67 100644
--- a/chromium/components/embedder_support/pref_names.cc
+++ b/chromium/components/embedder_support/pref_names.cc
@@ -15,4 +15,8 @@ const char kAlternateErrorPagesEnabled[] = "alternate_error_pages.enabled";
const char kForceMajorVersionToMinorPosition[] =
"force_major_version_to_minor_position_in_user_agent";
+// Enum indicating if the user agent reduction feature should be forced enabled
+// or disabled. Defaults to blink::features::kReduceUserAgentMinorVersion trial.
+const char kReduceUserAgentMinorVersion[] = "user_agent_reduction";
+
} // namespace embedder_support
diff --git a/chromium/components/embedder_support/pref_names.h b/chromium/components/embedder_support/pref_names.h
index 1cfd57dc42a..806ed3cab6e 100644
--- a/chromium/components/embedder_support/pref_names.h
+++ b/chromium/components/embedder_support/pref_names.h
@@ -12,6 +12,7 @@ namespace embedder_support {
extern const char kAlternateErrorPagesEnabled[];
extern const char kForceMajorVersionToMinorPosition[];
+extern const char kReduceUserAgentMinorVersion[];
} // namespace embedder_support
diff --git a/chromium/components/embedder_support/user_agent_utils.cc b/chromium/components/embedder_support/user_agent_utils.cc
index b82c1c48630..2b7545b767a 100644
--- a/chromium/components/embedder_support/user_agent_utils.cc
+++ b/chromium/components/embedder_support/user_agent_utils.cc
@@ -164,12 +164,25 @@ const std::string& GetWindowsPlatformVersion() {
// the minor position.
// TODO(crbug.com/1290820): Remove this method along with policy.
bool ShouldForceMajorVersionToMinorPosition(
- ForceMajorVersionToMinorPosition force_major_to_minor = kDefault) {
+ ForceMajorVersionToMinorPosition force_major_to_minor) {
return (
- (force_major_to_minor != kForceDisabled &&
+ (force_major_to_minor !=
+ ForceMajorVersionToMinorPosition::kForceDisabled &&
base::FeatureList::IsEnabled(
blink::features::kForceMajorVersionInMinorPositionInUserAgent)) ||
- force_major_to_minor == kForceEnabled);
+ force_major_to_minor == ForceMajorVersionToMinorPosition::kForceEnabled);
+}
+
+// Returns true if the user agent reduction should be forced (or prevented).
+// TODO(crbug.com/1330890): Remove this method along with policy.
+bool ShouldReduceUserAgentMinorVersion(
+ UserAgentReductionEnterprisePolicyState user_agent_reduction) {
+ return ((user_agent_reduction !=
+ UserAgentReductionEnterprisePolicyState::kForceDisabled &&
+ base::FeatureList::IsEnabled(
+ blink::features::kReduceUserAgentMinorVersion)) ||
+ user_agent_reduction ==
+ UserAgentReductionEnterprisePolicyState::kForceEnabled);
}
const std::string& GetMajorInMinorVersionNumber() {
@@ -329,18 +342,17 @@ std::string GetMajorVersionForUserAgentString(
} // namespace
std::string GetProductAndVersion(
- ForceMajorVersionToMinorPosition force_major_to_minor) {
+ ForceMajorVersionToMinorPosition force_major_to_minor,
+ UserAgentReductionEnterprisePolicyState user_agent_reduction) {
if (ShouldForceMajorVersionToMinorPosition(force_major_to_minor)) {
// Force major version to 99 and major version to minor version position.
- if (base::FeatureList::IsEnabled(
- blink::features::kReduceUserAgentMinorVersion)) {
+ if (ShouldReduceUserAgentMinorVersion(user_agent_reduction)) {
return "Chrome/" + GetReducedMajorInMinorVersionNumber();
} else {
return "Chrome/" + GetMajorInMinorVersionNumber();
}
} else {
- if (base::FeatureList::IsEnabled(
- blink::features::kReduceUserAgentMinorVersion)) {
+ if (ShouldReduceUserAgentMinorVersion(user_agent_reduction)) {
return version_info::GetProductNameAndVersionForReducedUserAgent(
blink::features::kUserAgentFrozenBuildVersion.Get().data());
} else {
@@ -349,8 +361,24 @@ std::string GetProductAndVersion(
}
}
+// Internal function to handle return the full or "reduced" user agent string,
+// depending on the UserAgentReduction enterprise policy.
+std::string GetUserAgentInternal(
+ ForceMajorVersionToMinorPosition force_major_to_minor,
+ UserAgentReductionEnterprisePolicyState user_agent_reduction) {
+ std::string product =
+ GetProductAndVersion(force_major_to_minor, user_agent_reduction);
+#if BUILDFLAG(IS_ANDROID)
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseMobileUserAgent))
+ product += " Mobile";
+#endif
+ return content::BuildUserAgentFromProduct(product);
+}
+
std::string GetUserAgent(
- ForceMajorVersionToMinorPosition force_major_to_minor) {
+ ForceMajorVersionToMinorPosition force_major_to_minor,
+ UserAgentReductionEnterprisePolicyState user_agent_reduction) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(kUserAgent)) {
std::string ua = command_line->GetSwitchValueASCII(kUserAgent);
@@ -360,12 +388,12 @@ std::string GetUserAgent(
}
if (base::FeatureList::IsEnabled(blink::features::kFullUserAgent))
- return GetFullUserAgent();
+ return GetFullUserAgent(force_major_to_minor);
if (base::FeatureList::IsEnabled(blink::features::kReduceUserAgent))
return GetReducedUserAgent(force_major_to_minor);
- return GetFullUserAgent(force_major_to_minor);
+ return GetUserAgentInternal(force_major_to_minor, user_agent_reduction);
}
std::string GetReducedUserAgent(
@@ -378,13 +406,9 @@ std::string GetReducedUserAgent(
std::string GetFullUserAgent(
ForceMajorVersionToMinorPosition force_major_to_minor) {
- std::string product = GetProductAndVersion(force_major_to_minor);
-#if BUILDFLAG(IS_ANDROID)
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseMobileUserAgent))
- product += " Mobile";
-#endif
- return content::BuildUserAgentFromProduct(product);
+ return GetUserAgentInternal(
+ force_major_to_minor,
+ UserAgentReductionEnterprisePolicyState::kForceDisabled);
}
// Generate a pseudo-random permutation of the following brand/version pairs:
@@ -534,7 +558,7 @@ blink::UserAgentMetadata GetUserAgentMetadata() {
return GetUserAgentMetadata(nullptr);
}
-blink::UserAgentMetadata GetUserAgentMetadata(PrefService* pref_service) {
+blink::UserAgentMetadata GetUserAgentMetadata(const PrefService* pref_service) {
blink::UserAgentMetadata metadata;
bool enable_updated_grease_by_policy = true;
UserAgentOptions ua_options;
@@ -551,7 +575,7 @@ blink::UserAgentMetadata GetUserAgentMetadata(PrefService* pref_service) {
enable_updated_grease_by_policy, ua_options.force_major_to_minor);
metadata.full_version = GetVersionNumber(ua_options);
metadata.platform = GetPlatformForUAMetadata();
- metadata.architecture = content::GetLowEntropyCpuArchitecture();
+ metadata.architecture = content::GetCpuArchitecture();
metadata.model = content::BuildModelInfo();
metadata.mobile = false;
#if BUILDFLAG(IS_ANDROID)
@@ -567,12 +591,8 @@ blink::UserAgentMetadata GetUserAgentMetadata(PrefService* pref_service) {
metadata.platform_version =
base::StringPrintf("%d.%d.%d", major, minor, bugfix);
#endif
-
- // These methods use the same information as the User-Agent string, but are
- // "low entropy" in that they reduce the number of options for output to a
- // set number. For more information, see the respective headers.
- metadata.architecture = content::GetLowEntropyCpuArchitecture();
- metadata.bitness = content::GetLowEntropyCpuBitness();
+ metadata.architecture = content::GetCpuArchitecture();
+ metadata.bitness = content::GetCpuBitness();
metadata.wow64 = content::IsWoW64();
return metadata;
@@ -611,17 +631,32 @@ int GetHighestKnownUniversalApiContractVersionForTesting() {
// TODO(crbug.com/1290820): Remove this function with policy.
embedder_support::ForceMajorVersionToMinorPosition GetMajorToMinorFromPrefs(
- PrefService* pref_service) {
+ const PrefService* pref_service) {
if (!pref_service->HasPrefPath(kForceMajorVersionToMinorPosition))
- return kDefault;
+ return ForceMajorVersionToMinorPosition::kDefault;
switch (pref_service->GetInteger(kForceMajorVersionToMinorPosition)) {
case 1:
- return kForceDisabled;
+ return ForceMajorVersionToMinorPosition::kForceDisabled;
+ case 2:
+ return ForceMajorVersionToMinorPosition::kForceEnabled;
+ case 0:
+ default:
+ return ForceMajorVersionToMinorPosition::kDefault;
+ }
+}
+
+embedder_support::UserAgentReductionEnterprisePolicyState
+GetUserAgentReductionFromPrefs(const PrefService* pref_service) {
+ if (!pref_service->HasPrefPath(kReduceUserAgentMinorVersion))
+ return UserAgentReductionEnterprisePolicyState::kDefault;
+ switch (pref_service->GetInteger(kReduceUserAgentMinorVersion)) {
+ case 1:
+ return UserAgentReductionEnterprisePolicyState::kForceDisabled;
case 2:
- return kForceEnabled;
+ return UserAgentReductionEnterprisePolicyState::kForceEnabled;
case 0:
default:
- return kDefault;
+ return UserAgentReductionEnterprisePolicyState::kDefault;
}
}
diff --git a/chromium/components/embedder_support/user_agent_utils.h b/chromium/components/embedder_support/user_agent_utils.h
index c6ac7435ca2..791fdd7229f 100644
--- a/chromium/components/embedder_support/user_agent_utils.h
+++ b/chromium/components/embedder_support/user_agent_utils.h
@@ -22,10 +22,15 @@ class WebContents;
namespace embedder_support {
-// TODO(crbug.com/1291612): Move this enum definition to
-// chrome/browser/chrome_content_browser_client.h
// TODO(crbug.com/1290820): Remove this enum along with policy.
-enum ForceMajorVersionToMinorPosition {
+enum class ForceMajorVersionToMinorPosition {
+ kDefault = 0,
+ kForceDisabled = 1,
+ kForceEnabled = 2,
+};
+
+// TODO(crbug.com/1330890): Remove this enum along with policy.
+enum class UserAgentReductionEnterprisePolicyState {
kDefault = 0,
kForceDisabled = 1,
kForceEnabled = 2,
@@ -33,30 +38,44 @@ enum ForceMajorVersionToMinorPosition {
struct UserAgentOptions {
bool force_major_version_100 = false;
- ForceMajorVersionToMinorPosition force_major_to_minor = kDefault;
+ ForceMajorVersionToMinorPosition force_major_to_minor =
+ ForceMajorVersionToMinorPosition::kDefault;
};
// Returns the product & version string. Examples:
-// "Chrome/101.0.0.0" - if UA reduction is enabled
-// "Chrome/101.0.4698.0" - if UA reduction is not enabled
+// "Chrome/101.0.0.0" - if UA reduction is enabled w/o major to minor
+// "Chrome/101.0.4698.0" - if UA reduction isn't enabled w/o major to minor
+// "Chrome/99.101.0.0" - if UA reduction is enabled w/ major to minor
+// "Chrome/99.101.0.4698.0" - if UA reduction isn'n enabled w/ major to minor
+// TODO(crbug.com/1291612): modify to accept an optional PrefService*.
std::string GetProductAndVersion(
- ForceMajorVersionToMinorPosition force_major_to_minor = kDefault);
+ ForceMajorVersionToMinorPosition force_major_to_minor =
+ ForceMajorVersionToMinorPosition::kDefault,
+ UserAgentReductionEnterprisePolicyState user_agent_reduction =
+ UserAgentReductionEnterprisePolicyState::kDefault);
-// Returns the user agent string for Chrome.
+// Returns the full user agent string for Chrome.
// TODO(crbug.com/1291612): modify to accept an optional PrefService*.
std::string GetFullUserAgent(
- ForceMajorVersionToMinorPosition force_major_to_minor = kDefault);
+ ForceMajorVersionToMinorPosition force_major_to_minor =
+ ForceMajorVersionToMinorPosition::kDefault);
// Returns the reduced user agent string for Chrome.
// TODO(crbug.com/1291612): modify to accept an optional PrefService*.
std::string GetReducedUserAgent(
- ForceMajorVersionToMinorPosition force_major_to_minor = kDefault);
+ ForceMajorVersionToMinorPosition force_major_to_minor =
+ ForceMajorVersionToMinorPosition::kDefault);
-// Returns the full or "reduced" user agent string, depending on the
-// UserAgentReduction enterprise policy and blink::features::kReduceUserAgent
+// Returns the full or "reduced" user agent string, depending on the following:
+// 1) UserAgentReduction enterprise policy.
+// 2) blink::features::kReduceUserAgent: reduced-user-agent about flag.
+// 3) blink::features::kFullUserAgent: full-user-agent about flag.
// TODO(crbug.com/1291612): modify to accept an optional PrefService*.
std::string GetUserAgent(
- ForceMajorVersionToMinorPosition force_major_to_minor = kDefault);
+ ForceMajorVersionToMinorPosition force_major_to_minor =
+ ForceMajorVersionToMinorPosition::kDefault,
+ UserAgentReductionEnterprisePolicyState user_agent_reduction =
+ UserAgentReductionEnterprisePolicyState::kDefault);
// Returns UserAgentMetadata per the default policy.
// This override is currently used in fuchsia, where the enterprise policy
@@ -66,7 +85,7 @@ blink::UserAgentMetadata GetUserAgentMetadata();
// Return UserAgentMetadata, potentially overridden by policy.
// Note that this override is likely to be removed once an enterprise
// escape hatch is no longer needed. See https://crbug.com/1261908.
-blink::UserAgentMetadata GetUserAgentMetadata(PrefService* local_state);
+blink::UserAgentMetadata GetUserAgentMetadata(const PrefService* local_state);
// Return UserAgentBrandList based on the expected output version type.
blink::UserAgentBrandList GenerateBrandVersionList(
@@ -107,7 +126,13 @@ int GetHighestKnownUniversalApiContractVersionForTesting();
// the provided integer policy value for ForceMajorVersionToMinorPosition.
// TODO(crbug.com/1290820): Remove this function with policy.
embedder_support::ForceMajorVersionToMinorPosition GetMajorToMinorFromPrefs(
- PrefService* pref_service);
+ const PrefService* pref_service);
+
+// Returns the UserAgentReductionEnterprisePolicyState enum value corresponding
+// to the provided integer policy value for UserAgentReduction.
+// TODO(crbug.com/1330890): Remove this function with policy.
+embedder_support::UserAgentReductionEnterprisePolicyState
+GetUserAgentReductionFromPrefs(const PrefService* pref_service);
} // namespace embedder_support
diff --git a/chromium/components/embedder_support/user_agent_utils_unittest.cc b/chromium/components/embedder_support/user_agent_utils_unittest.cc
index adc9d623e66..0178f5a06cc 100644
--- a/chromium/components/embedder_support/user_agent_utils_unittest.cc
+++ b/chromium/components/embedder_support/user_agent_utils_unittest.cc
@@ -325,6 +325,15 @@ bool ContainsBrandVersion(const blink::UserAgentBrandList& brand_list,
class UserAgentUtilsTest : public testing::Test,
public testing::WithParamInterface<bool> {
public:
+ // The minor version in the reduced UA string is always "0.0.0".
+ static constexpr char kReducedMinorVersion[] = "0.0.0";
+ // The minor version in the ReduceUserAgentMinorVersion experiment is always
+ // "0.X.0", where X is the frozen build version.
+ const std::string kReduceUserAgentMinorVersion =
+ "0." +
+ std::string(blink::features::kUserAgentFrozenBuildVersion.Get().data()) +
+ ".0";
+
std::string MajorToMinorVersionNumber() {
const base::Version version = version_info::GetVersion();
std::string version_str;
@@ -342,6 +351,46 @@ class UserAgentUtilsTest : public testing::Test,
return version_str;
}
+ std::string GetUserAgentMinorVersion(const std::string& user_agent_value) {
+ // A regular expression that matches Chrome/{major_version}.{minor_version}
+ // in the User-Agent string, where the {minor_version} is captured.
+ static constexpr char kChromeVersionRegex[] =
+ "Chrome/[0-9]+\\.([0-9]+\\.[0-9]+\\.[0-9]+)";
+ std::string minor_version;
+ EXPECT_TRUE(re2::RE2::PartialMatch(user_agent_value, kChromeVersionRegex,
+ &minor_version));
+ return minor_version;
+ }
+
+ void VerifyFullUserAgent() {
+ EXPECT_EQ(
+ GetUserAgent(ForceMajorVersionToMinorPosition::kForceEnabled),
+ GetFullUserAgent(ForceMajorVersionToMinorPosition::kForceEnabled));
+ EXPECT_EQ(
+ GetUserAgent(ForceMajorVersionToMinorPosition::kForceDisabled),
+ GetFullUserAgent(ForceMajorVersionToMinorPosition::kForceDisabled));
+ EXPECT_EQ(GetUserAgent(), GetFullUserAgent());
+ EXPECT_NE(GetUserAgentMinorVersion(GetFullUserAgent()),
+ kReducedMinorVersion);
+ }
+
+ void VerifyGetUserAgentFunctions() {
+ // 1) GetUserAgent should return user agent depends on
+ // kReduceUserAgentMinorVersion feature.
+ // 2) GetReducedUserAgent should return reduced user agent.
+ // 3) GetFullUserAgent should return full user agent.
+ if (base::FeatureList::IsEnabled(
+ blink::features::kReduceUserAgentMinorVersion)) {
+ EXPECT_EQ(GetUserAgentMinorVersion(GetUserAgent()), kReducedMinorVersion);
+ } else {
+ EXPECT_NE(GetUserAgentMinorVersion(GetUserAgent()), kReducedMinorVersion);
+ }
+ EXPECT_EQ(GetUserAgentMinorVersion(GetReducedUserAgent()),
+ kReducedMinorVersion);
+ EXPECT_NE(GetUserAgentMinorVersion(GetFullUserAgent()),
+ kReducedMinorVersion);
+ }
+
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
@@ -429,7 +478,8 @@ TEST_F(UserAgentUtilsTest, UserAgentStringReduced) {
blink::features::kForceMajorVersionInMinorPositionInUserAgent},
{});
{
- std::string buffer = GetReducedUserAgent(kForceDisabled);
+ std::string buffer =
+ GetReducedUserAgent(ForceMajorVersionToMinorPosition::kForceDisabled);
std::string device_compat = "Mobile ";
EXPECT_EQ(buffer,
base::StringPrintf(content::frozen_user_agent_strings::kAndroid,
@@ -466,7 +516,8 @@ TEST_F(UserAgentUtilsTest, UserAgentStringReduced) {
blink::features::kForceMajorVersionInMinorPositionInUserAgent},
{});
{
- std::string buffer = GetReducedUserAgent(kForceDisabled);
+ std::string buffer =
+ GetReducedUserAgent(ForceMajorVersionToMinorPosition::kForceDisabled);
EXPECT_EQ(buffer, base::StringPrintf(
content::frozen_user_agent_strings::kDesktop,
content::GetUnifiedPlatform().c_str(),
@@ -487,7 +538,7 @@ TEST_F(UserAgentUtilsTest, UserAgentStringFull) {
{blink::features::kFullUserAgent,
blink::features::kForceMajorVersionInMinorPositionInUserAgent},
{});
- { EXPECT_EQ(GetUserAgent(), GetFullUserAgent()); }
+ { VerifyFullUserAgent(); }
// Verify that the full user agent string when both reduced and full UA
// feature enabled respects
@@ -497,7 +548,36 @@ TEST_F(UserAgentUtilsTest, UserAgentStringFull) {
{blink::features::kFullUserAgent, blink::features::kReduceUserAgent,
blink::features::kForceMajorVersionInMinorPositionInUserAgent},
{});
- { EXPECT_EQ(GetUserAgent(), GetFullUserAgent()); }
+ { VerifyFullUserAgent(); }
+
+ // Verify that the full user agent string still returns the full user agent
+ // even turns on kReduceUserAgentMinorVersion
+ scoped_feature_list.Reset();
+ scoped_feature_list.InitWithFeatures(
+ {blink::features::kFullUserAgent,
+ blink::features::kReduceUserAgentMinorVersion},
+ {});
+ { VerifyFullUserAgent(); }
+
+ // Verify that three user agent functions return the correct user agent string
+ // when kReduceUserAgentMinorVersion turns on.
+ scoped_feature_list.Reset();
+ scoped_feature_list.InitWithFeatures(
+ {blink::features::kReduceUserAgentMinorVersion}, {});
+ { VerifyGetUserAgentFunctions(); }
+
+ // Verify that three user agent functions return the correct user agent
+ // when kReduceUserAgentMinorVersion turns off.
+ scoped_feature_list.Reset();
+ scoped_feature_list.InitWithFeatures(
+ {}, {blink::features::kReduceUserAgentMinorVersion});
+ { VerifyGetUserAgentFunctions(); }
+
+ // Verify that three user agent functions return the correct user agent
+ // without explicit features turn on.
+ scoped_feature_list.Reset();
+ scoped_feature_list.InitWithFeatures({}, {});
+ { VerifyGetUserAgentFunctions(); }
}
TEST_F(UserAgentUtilsTest, UserAgentMetadata) {
@@ -548,7 +628,8 @@ TEST_F(UserAgentUtilsTest, UserAgentMetadata) {
// ForceMajorVersionToMinorPosition: kForceDisabled
pref_service.registry()->RegisterIntegerPref(
- kForceMajorVersionToMinorPosition, kForceDisabled);
+ kForceMajorVersionToMinorPosition,
+ static_cast<int>(ForceMajorVersionToMinorPosition::kForceDisabled));
metadata = GetUserAgentMetadata(&pref_service);
EXPECT_EQ(metadata.full_version, full_version);
EXPECT_TRUE(ContainsBrandVersion(metadata.brand_version_list,
@@ -570,7 +651,9 @@ TEST_F(UserAgentUtilsTest, UserAgentMetadata) {
major_to_minor_product_brand_full_version));
// ForceMajorVersionToMinorPosition: kForceEnabled
- pref_service.SetInteger(kForceMajorVersionToMinorPosition, kForceEnabled);
+ pref_service.SetInteger(
+ kForceMajorVersionToMinorPosition,
+ static_cast<int>(ForceMajorVersionToMinorPosition::kForceEnabled));
metadata = GetUserAgentMetadata(&pref_service);
EXPECT_EQ(metadata.full_version, major_to_minor_full_version);
EXPECT_TRUE(ContainsBrandVersion(metadata.brand_version_list,
@@ -634,15 +717,14 @@ TEST_F(UserAgentUtilsTest, UserAgentMetadata) {
#else
EXPECT_EQ(metadata.platform, "Unknown");
#endif
- EXPECT_EQ(metadata.architecture, content::GetLowEntropyCpuArchitecture());
+ EXPECT_EQ(metadata.architecture, content::GetCpuArchitecture());
EXPECT_EQ(metadata.model, content::BuildModelInfo());
- EXPECT_EQ(metadata.bitness, content::GetLowEntropyCpuBitness());
+ EXPECT_EQ(metadata.bitness, content::GetCpuBitness());
EXPECT_EQ(metadata.wow64, content::IsWoW64());
}
-TEST_F(UserAgentUtilsTest, GenerateBrandVersionList) {
+TEST_F(UserAgentUtilsTest, GenerateBrandVersionListUnbranded) {
blink::UserAgentMetadata metadata;
-
metadata.brand_version_list = GenerateBrandVersionList(
84, absl::nullopt, "84", absl::nullopt, absl::nullopt, true,
blink::UserAgentBrandVersionType::kMajorVersion);
@@ -656,6 +738,17 @@ TEST_F(UserAgentUtilsTest, GenerateBrandVersionList) {
std::string brand_list_w_fv = metadata.SerializeBrandFullVersionList();
EXPECT_EQ(R"(" Not A;Brand";v="99.0.0.0", "Chromium";v="84.0.0.0")",
brand_list_w_fv);
+}
+
+TEST_F(UserAgentUtilsTest, GenerateBrandVersionListUnbrandedVerifySeedChanges) {
+ blink::UserAgentMetadata metadata;
+
+ metadata.brand_version_list = GenerateBrandVersionList(
+ 84, absl::nullopt, "84", absl::nullopt, absl::nullopt, true,
+ blink::UserAgentBrandVersionType::kMajorVersion);
+ // Capture the serialized brand lists with version 84 as the seed.
+ std::string brand_list = metadata.SerializeBrandMajorVersionList();
+ std::string brand_list_w_fv = metadata.SerializeBrandFullVersionList();
metadata.brand_version_list = GenerateBrandVersionList(
85, absl::nullopt, "85", absl::nullopt, absl::nullopt, true,
@@ -663,9 +756,10 @@ TEST_F(UserAgentUtilsTest, GenerateBrandVersionList) {
metadata.brand_full_version_list = GenerateBrandVersionList(
85, absl::nullopt, "85.0.0.0", absl::nullopt, absl::nullopt, true,
blink::UserAgentBrandVersionType::kFullVersion);
- std::string brand_list_diff = metadata.SerializeBrandMajorVersionList();
- // Make sure the lists are different for different seeds
+
+ // Make sure the lists are different for different seeds (84 vs 85).
// 1. verify major version
+ std::string brand_list_diff = metadata.SerializeBrandMajorVersionList();
EXPECT_EQ(R"("Chromium";v="85", " Not;A Brand";v="99")", brand_list_diff);
EXPECT_NE(brand_list, brand_list_diff);
// 2.verify full version
@@ -673,25 +767,10 @@ TEST_F(UserAgentUtilsTest, GenerateBrandVersionList) {
EXPECT_EQ(R"("Chromium";v="85.0.0.0", " Not;A Brand";v="99.0.0.0")",
brand_list_diff_w_fv);
EXPECT_NE(brand_list_w_fv, brand_list_diff_w_fv);
+}
- metadata.brand_version_list = GenerateBrandVersionList(
- 84, "Totally A Brand", "84", absl::nullopt, absl::nullopt, true,
- blink::UserAgentBrandVersionType::kMajorVersion);
- metadata.brand_full_version_list = GenerateBrandVersionList(
- 84, "Totally A Brand", "84.0.0.0", absl::nullopt, absl::nullopt, true,
- blink::UserAgentBrandVersionType::kFullVersion);
- // 1. verify major version
- std::string brand_list_w_brand = metadata.SerializeBrandMajorVersionList();
- EXPECT_EQ(
- R"(" Not A;Brand";v="99", "Chromium";v="84", "Totally A Brand";v="84")",
- brand_list_w_brand);
- // 2. verify full version
- std::string brand_list_w_brand_fv = metadata.SerializeBrandFullVersionList();
- EXPECT_EQ(base::StrCat({"\" Not A;Brand\";v=\"99.0.0.0\", ",
- "\"Chromium\";v=\"84.0.0.0\", ",
- "\"Totally A Brand\";v=\"84.0.0.0\""}),
- brand_list_w_brand_fv);
-
+TEST_F(UserAgentUtilsTest, GenerateBrandVersionListWithGreaseBrandOverride) {
+ blink::UserAgentMetadata metadata;
// The old GREASE generation algorithm should not respond to experiment
// overrides.
metadata.brand_version_list = GenerateBrandVersionList(
@@ -710,6 +789,11 @@ TEST_F(UserAgentUtilsTest, GenerateBrandVersionList) {
metadata.SerializeBrandFullVersionList();
EXPECT_EQ(R"(" Not A;Brand";v="99.0.0.0", "Chromium";v="84.0.0.0")",
brand_list_grease_override_fv);
+}
+
+TEST_F(UserAgentUtilsTest,
+ GenerateBrandVersionListWithGreaseBrandAndVersionOverride) {
+ blink::UserAgentMetadata metadata;
metadata.brand_version_list = GenerateBrandVersionList(
84, absl::nullopt, "84", "Clean GREASE", "1024", true,
@@ -727,6 +811,10 @@ TEST_F(UserAgentUtilsTest, GenerateBrandVersionList) {
metadata.SerializeBrandFullVersionList();
EXPECT_EQ(R"(" Not A;Brand";v="99.0.0.0", "Chromium";v="84.0.0.0")",
brand_list_and_version_grease_override_fv);
+}
+
+TEST_F(UserAgentUtilsTest, GenerateBrandVersionListWithGreaseVersionOverride) {
+ blink::UserAgentMetadata metadata;
metadata.brand_version_list = GenerateBrandVersionList(
84, absl::nullopt, "84", absl::nullopt, "1024", true,
@@ -744,7 +832,30 @@ TEST_F(UserAgentUtilsTest, GenerateBrandVersionList) {
metadata.SerializeBrandFullVersionList();
EXPECT_EQ(R"(" Not A;Brand";v="99.0.0.0", "Chromium";v="84.0.0.0")",
brand_version_grease_override_fv);
+}
+TEST_F(UserAgentUtilsTest, GenerateBrandVersionListWithBrand) {
+ blink::UserAgentMetadata metadata;
+ metadata.brand_version_list = GenerateBrandVersionList(
+ 84, "Totally A Brand", "84", absl::nullopt, absl::nullopt, true,
+ blink::UserAgentBrandVersionType::kMajorVersion);
+ metadata.brand_full_version_list = GenerateBrandVersionList(
+ 84, "Totally A Brand", "84.0.0.0", absl::nullopt, absl::nullopt, true,
+ blink::UserAgentBrandVersionType::kFullVersion);
+ // 1. verify major version
+ std::string brand_list_w_brand = metadata.SerializeBrandMajorVersionList();
+ EXPECT_EQ(
+ R"(" Not A;Brand";v="99", "Chromium";v="84", "Totally A Brand";v="84")",
+ brand_list_w_brand);
+ // 2. verify full version
+ std::string brand_list_w_brand_fv = metadata.SerializeBrandFullVersionList();
+ EXPECT_EQ(base::StrCat({"\" Not A;Brand\";v=\"99.0.0.0\", ",
+ "\"Chromium\";v=\"84.0.0.0\", ",
+ "\"Totally A Brand\";v=\"84.0.0.0\""}),
+ brand_list_w_brand_fv);
+}
+
+TEST_F(UserAgentUtilsTest, GenerateBrandVersionListInvalidSeed) {
// Should DCHECK on negative numbers
EXPECT_DCHECK_DEATH(GenerateBrandVersionList(
-1, absl::nullopt, "99", absl::nullopt, absl::nullopt, true,
@@ -754,12 +865,7 @@ TEST_F(UserAgentUtilsTest, GenerateBrandVersionList) {
blink::UserAgentBrandVersionType::kFullVersion));
}
-TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
- base::test::ScopedFeatureList scoped_feature_list;
- // Test to ensure the old algorithm is respected when the flag is not set.
- scoped_feature_list.InitAndEnableFeatureWithParameters(
- features::kGreaseUACH, {{"updated_algorithm", "false"}});
-
+TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersionOldAlgorithm) {
std::vector<int> permuted_order{0, 1, 2};
blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, absl::nullopt, absl::nullopt, true,
@@ -772,10 +878,14 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
blink::UserAgentBrandVersionType::kFullVersion);
EXPECT_EQ(greased_bv.brand, " Not A;Brand");
EXPECT_EQ(greased_bv.version, "99.0.0.0");
+}
+TEST_F(UserAgentUtilsTest,
+ GetGreasedUserAgentBrandVersionOldAlgorithmIgnoresBrandOverrides) {
// With the new algorithm disabled, we want to avoid experiment params
// ("WhatIsGrease", 1024) from taking an effect.
- greased_bv = GetGreasedUserAgentBrandVersion(
+ std::vector<int> permuted_order{0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, "WhatIsGrease", absl::nullopt, true,
blink::UserAgentBrandVersionType::kMajorVersion);
EXPECT_EQ(greased_bv.brand, " Not A;Brand");
@@ -786,8 +896,14 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
blink::UserAgentBrandVersionType::kFullVersion);
EXPECT_EQ(greased_bv.brand, " Not A;Brand");
EXPECT_EQ(greased_bv.version, "99.0.0.0");
+}
- greased_bv = GetGreasedUserAgentBrandVersion(
+TEST_F(UserAgentUtilsTest,
+ GetGreasedUserAgentBrandVersionOldAlgorithmIgnoresVersionOverrides) {
+ // With the new algorithm disabled, we want to avoid experiment params
+ // ("WhatIsGrease", 1024) from taking an effect.
+ std::vector<int> permuted_order{0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, absl::nullopt, "1024", true,
blink::UserAgentBrandVersionType::kMajorVersion);
EXPECT_EQ(greased_bv.brand, " Not A;Brand");
@@ -798,8 +914,15 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
blink::UserAgentBrandVersionType::kFullVersion);
EXPECT_EQ(greased_bv.brand, " Not A;Brand");
EXPECT_EQ(greased_bv.version, "99.0.0.0");
+}
- greased_bv = GetGreasedUserAgentBrandVersion(
+TEST_F(
+ UserAgentUtilsTest,
+ GetGreasedUserAgentBrandVersionOldAlgorithmIgnoresBrandAndVersionOverrides) {
+ // With the new algorithm disabled, we want to avoid experiment params
+ // ("WhatIsGrease", 1024) from taking an effect.
+ std::vector<int> permuted_order{0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, "WhatIsGrease", "1024", true,
blink::UserAgentBrandVersionType::kMajorVersion);
EXPECT_EQ(greased_bv.brand, " Not A;Brand");
@@ -810,13 +933,15 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
blink::UserAgentBrandVersionType::kFullVersion);
EXPECT_EQ(greased_bv.brand, " Not A;Brand");
EXPECT_EQ(greased_bv.version, "99.0.0.0");
+}
- // Test to ensure the new algorithm works and is still overridable.
- scoped_feature_list.Reset();
+// Tests to ensure the new algorithm works and is still overridable.
+TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersionNewAlgorithm) {
+ base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
features::kGreaseUACH, {{"updated_algorithm", "true"}});
-
- greased_bv = GetGreasedUserAgentBrandVersion(
+ std::vector<int> permuted_order{0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, absl::nullopt, absl::nullopt, true,
blink::UserAgentBrandVersionType::kMajorVersion);
EXPECT_EQ(greased_bv.brand, "/Not=A?Brand");
@@ -827,8 +952,15 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
blink::UserAgentBrandVersionType::kFullVersion);
EXPECT_EQ(greased_bv.brand, "/Not=A?Brand");
EXPECT_EQ(greased_bv.version, "8.0.0.0");
+}
- greased_bv = GetGreasedUserAgentBrandVersion(
+TEST_F(UserAgentUtilsTest,
+ GetGreasedUserAgentBrandVersionNewAlgorithmBrandOverride) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kGreaseUACH, {{"updated_algorithm", "true"}});
+ std::vector<int> permuted_order{0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, "WhatIsGrease", absl::nullopt, true,
blink::UserAgentBrandVersionType::kMajorVersion);
EXPECT_EQ(greased_bv.brand, "WhatIsGrease");
@@ -839,8 +971,15 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
blink::UserAgentBrandVersionType::kFullVersion);
EXPECT_EQ(greased_bv.brand, "WhatIsGrease");
EXPECT_EQ(greased_bv.version, "8.0.0.0");
+}
- greased_bv = GetGreasedUserAgentBrandVersion(
+TEST_F(UserAgentUtilsTest,
+ GetGreasedUserAgentBrandVersionNewAlgorithmVersionOverride) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kGreaseUACH, {{"updated_algorithm", "true"}});
+ std::vector<int> permuted_order{0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, absl::nullopt, "1024", true,
blink::UserAgentBrandVersionType::kMajorVersion);
EXPECT_EQ(greased_bv.brand, "/Not=A?Brand");
@@ -851,8 +990,15 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
blink::UserAgentBrandVersionType::kFullVersion);
EXPECT_EQ(greased_bv.brand, "/Not=A?Brand");
EXPECT_EQ(greased_bv.version, "1024.0.0.0");
+}
- greased_bv = GetGreasedUserAgentBrandVersion(
+TEST_F(UserAgentUtilsTest,
+ GetGreasedUserAgentBrandVersionNewAlgorithmBrandAndVersionOverride) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kGreaseUACH, {{"updated_algorithm", "true"}});
+ std::vector<int> permuted_order{0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, "WhatIsGrease", "1024", true,
blink::UserAgentBrandVersionType::kMajorVersion);
EXPECT_EQ(greased_bv.brand, "WhatIsGrease");
@@ -863,37 +1009,41 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
blink::UserAgentBrandVersionType::kFullVersion);
EXPECT_EQ(greased_bv.brand, "WhatIsGrease");
EXPECT_EQ(greased_bv.version, "1024.0.0.0");
+}
- permuted_order = {2, 1, 0};
- greased_bv = GetGreasedUserAgentBrandVersion(
+TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersionFullVersions) {
+ std::vector<int> permuted_order = {2, 1, 0};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 86, absl::nullopt, absl::nullopt, true,
blink::UserAgentBrandVersionType::kMajorVersion);
- EXPECT_EQ(greased_bv.brand, ";Not_A Brand");
- EXPECT_EQ(greased_bv.version, "24");
+ EXPECT_EQ(greased_bv.brand, ";Not A Brand");
+ EXPECT_EQ(greased_bv.version, "99");
greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 86, absl::nullopt, absl::nullopt, true,
blink::UserAgentBrandVersionType::kFullVersion);
- EXPECT_EQ(greased_bv.brand, ";Not_A Brand");
- EXPECT_EQ(greased_bv.version, "24.0.0.0");
+ EXPECT_EQ(greased_bv.brand, ";Not A Brand");
+ EXPECT_EQ(greased_bv.version, "99.0.0.0");
// Test the greasy input with full version
greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, absl::nullopt, "1024.0.0.0", true,
blink::UserAgentBrandVersionType::kMajorVersion);
- EXPECT_EQ(greased_bv.brand, "/Not=A?Brand");
- EXPECT_EQ(greased_bv.version, "1024");
+ EXPECT_EQ(greased_bv.brand, ";Not A Brand");
+ EXPECT_EQ(greased_bv.version, "99");
greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, absl::nullopt, "1024.0.0.0", true,
blink::UserAgentBrandVersionType::kFullVersion);
- EXPECT_EQ(greased_bv.brand, "/Not=A?Brand");
- EXPECT_EQ(greased_bv.version, "1024.0.0.0");
+ EXPECT_EQ(greased_bv.brand, ";Not A Brand");
+ EXPECT_EQ(greased_bv.version, "99.0.0.0");
+}
- // Ensure the enterprise override bool takes precedence over the command line
- // flag
- permuted_order = {0, 1, 2};
- greased_bv = GetGreasedUserAgentBrandVersion(
+TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersionEnterpriseOverride) {
+ // Ensure the enterprise override bool can force the old GREASE algorithm to
+ // be used.
+ std::vector<int> permuted_order = {0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
permuted_order, 84, absl::nullopt, absl::nullopt, false,
blink::UserAgentBrandVersionType::kMajorVersion);
EXPECT_EQ(greased_bv.brand, " Not A;Brand");
@@ -904,7 +1054,33 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
blink::UserAgentBrandVersionType::kFullVersion);
EXPECT_EQ(greased_bv.brand, " Not A;Brand");
EXPECT_EQ(greased_bv.version, "99.0.0.0");
+}
+TEST_F(
+ UserAgentUtilsTest,
+ GetGreasedUserAgentBrandVersionEnterpriseOverrideSupersedesOtherOverrides) {
+ // Ensure the enterprise override bool can force the old GREASE algorithm to
+ // be used and supersedes passed-in brand/version overrides.
+ std::vector<int> permuted_order = {0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv = GetGreasedUserAgentBrandVersion(
+ permuted_order, 84, "helloWorld", "100000", false,
+ blink::UserAgentBrandVersionType::kMajorVersion);
+ EXPECT_EQ(greased_bv.brand, " Not A;Brand");
+ EXPECT_EQ(greased_bv.version, "99");
+
+ greased_bv = GetGreasedUserAgentBrandVersion(
+ permuted_order, 84, "helloWorld", "100000", false,
+ blink::UserAgentBrandVersionType::kFullVersion);
+ EXPECT_EQ(greased_bv.brand, " Not A;Brand");
+ EXPECT_EQ(greased_bv.version, "99.0.0.0");
+}
+
+TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersionNoLeadingWhitespace) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kGreaseUACH, {{"updated_algorithm", "true"}});
+ std::vector<int> permuted_order = {0, 1, 2};
+ blink::UserAgentBrandVersion greased_bv;
// Go up to 110 based on the 11 total chars * 10 possible first chars.
for (int i = 0; i < 110; i++) {
// Regardless of the major version seed, the spec calls for no leading
@@ -922,37 +1098,68 @@ TEST_F(UserAgentUtilsTest, GetGreasedUserAgentBrandVersion) {
}
TEST_F(UserAgentUtilsTest, GetProductAndVersion) {
+ std::string product;
+ std::string major_version;
+ std::string minor_version;
+ std::string build_version;
+ std::string patch_version;
+
+ // (1) Features: UserAgentReduction and MajorVersionInMinor disabled.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
/*enabled_features=*/{}, /*disabled_features=*/{
blink::features::kForceMajorVersionInMinorPositionInUserAgent,
blink::features::kReduceUserAgentMinorVersion});
- std::string product = GetProductAndVersion();
- std::string major_version;
- std::string minor_version;
+ // (1a) Policies: UserAgentReduction and MajorVersionInMinor default.
+ product =
+ GetProductAndVersion(ForceMajorVersionToMinorPosition::kDefault,
+ UserAgentReductionEnterprisePolicyState::kDefault);
EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
- &major_version, &minor_version));
+ &major_version, &minor_version,
+ &build_version));
EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
EXPECT_EQ(minor_version, "0");
+ EXPECT_NE(build_version, "0");
+ // Patch version cannot be tested as it would be set in a release branch.
- // Ensure policy is respected if ForceMajorToMinor is force enabled
- product = GetProductAndVersion(/*force_major_to_minor=*/kForceEnabled);
+ // (1b) Policies: UserAgentReduction and MajorVersionInMinor force enabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceEnabled,
+ UserAgentReductionEnterprisePolicyState::kForceEnabled);
EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
- &major_version, &minor_version));
+ &major_version, &minor_version,
+ &build_version, &patch_version));
EXPECT_EQ(major_version, "99");
EXPECT_EQ(minor_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(build_version, "0");
+ EXPECT_EQ(patch_version, "0");
- // Ensure the build version FeatureParam is used when set.
+ // (1c) Policies:: UserAgentReduction and MajorVersionInMinor force disabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceDisabled,
+ UserAgentReductionEnterprisePolicyState::kForceDisabled);
+ EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
+ &major_version, &minor_version,
+ &build_version));
+ EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(minor_version, "0");
+ EXPECT_NE(build_version, "0");
+ // Patch version cannot be tested as it would be set in a release branch.
+
+ // (2) Features: UserAgentReduction enabled with version and
+ // MajorVersionInMinor disabled.
scoped_feature_list.Reset();
scoped_feature_list.InitWithFeaturesAndParameters(
/*enabled_features=*/{{blink::features::kReduceUserAgentMinorVersion,
{{{"build_version", "5555"}}}}},
/*disabled_features=*/{
blink::features::kForceMajorVersionInMinorPositionInUserAgent});
- product = GetProductAndVersion();
- std::string build_version;
- std::string patch_version;
+
+ // (2a) Policies: UserAgentReduction and MajorVersionInMinor default.
+ product =
+ GetProductAndVersion(ForceMajorVersionToMinorPosition::kDefault,
+ UserAgentReductionEnterprisePolicyState::kDefault);
EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
&major_version, &minor_version,
&build_version, &patch_version));
@@ -961,35 +1168,84 @@ TEST_F(UserAgentUtilsTest, GetProductAndVersion) {
EXPECT_EQ(build_version, "5555");
EXPECT_EQ(patch_version, "0");
- // Ensure policy is respected if ForcemajorToMinor is force disabled, even if
- // the respective Blink feature is enabled.
+ // (2b) Policies: UserAgentReduction and MajorVersionInMinor force enabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceEnabled,
+ UserAgentReductionEnterprisePolicyState::kForceEnabled);
+ EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
+ &major_version, &minor_version,
+ &build_version, &patch_version));
+ EXPECT_EQ(major_version, "99");
+ EXPECT_EQ(minor_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(build_version, "0");
+ EXPECT_EQ(patch_version, "0");
+
+ // (2c) Policies:: UserAgentReduction and MajorVersionInMinor force disabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceDisabled,
+ UserAgentReductionEnterprisePolicyState::kForceDisabled);
+ EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
+ &major_version, &minor_version,
+ &build_version));
+ EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(minor_version, "0");
+ EXPECT_NE(build_version, "5555");
+ // Patch version cannot be tested as it would be set in a release branch.
+
+ // (3) Features: UserAgentReduction disabled and MajorVersionInMinor enabled.
scoped_feature_list.Reset();
scoped_feature_list.InitWithFeatures(
/*enabled_features=*/{blink::features::
kForceMajorVersionInMinorPositionInUserAgent},
/*disabled_features=*/{blink::features::kReduceUserAgentMinorVersion});
- product = GetProductAndVersion(/*force_major_to_minor=*/kForceDisabled);
+
+ // (3a) Policies: UserAgentReduction and MajorVersionInMinor default.
+ product =
+ GetProductAndVersion(ForceMajorVersionToMinorPosition::kDefault,
+ UserAgentReductionEnterprisePolicyState::kDefault);
EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
&major_version, &minor_version,
&build_version));
- EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
- EXPECT_EQ(minor_version, "0");
- EXPECT_NE(build_version, "9999");
+ EXPECT_EQ(major_version, "99");
+ EXPECT_EQ(minor_version, version_info::GetMajorVersionNumber());
+ EXPECT_NE(build_version, "0");
+ // Patch version cannot be tested as it would be set in a release branch.
- product = GetProductAndVersion();
+ // (3b) Policies: UserAgentReduction and MajorVersionInMinor force enabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceEnabled,
+ UserAgentReductionEnterprisePolicyState::kForceEnabled);
EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
&major_version, &minor_version,
- &build_version));
+ &build_version, &patch_version));
EXPECT_EQ(major_version, "99");
EXPECT_EQ(minor_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(build_version, "0");
+ EXPECT_EQ(patch_version, "0");
+
+ // (3c) Policies:: UserAgentReduction and MajorVersionInMinor force disabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceDisabled,
+ UserAgentReductionEnterprisePolicyState::kForceDisabled);
+ EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
+ &major_version, &minor_version,
+ &build_version));
+ EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(minor_version, "0");
EXPECT_NE(build_version, "0");
+ // Patch version cannot be tested as it would be set in a release branch.
+ // (4) Features: UserAgentReduction enabled and MajorVersionInMinor disabled.
scoped_feature_list.Reset();
scoped_feature_list.InitWithFeatures(
/*enabled_features=*/{blink::features::kReduceUserAgentMinorVersion},
/*disabled_features=*/{
blink::features::kForceMajorVersionInMinorPositionInUserAgent});
- product = GetProductAndVersion();
+
+ // (4a) Policies: UserAgentReduction and MajorVersionInMinor default.
+ product =
+ GetProductAndVersion(ForceMajorVersionToMinorPosition::kDefault,
+ UserAgentReductionEnterprisePolicyState::kDefault);
EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
&major_version, &minor_version,
&build_version, &patch_version));
@@ -998,13 +1254,42 @@ TEST_F(UserAgentUtilsTest, GetProductAndVersion) {
EXPECT_EQ(build_version, "0");
EXPECT_EQ(patch_version, "0");
+ // (4b) Policies: UserAgentReduction and MajorVersionInMinor force enabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceEnabled,
+ UserAgentReductionEnterprisePolicyState::kForceEnabled);
+ EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
+ &major_version, &minor_version,
+ &build_version, &patch_version));
+ EXPECT_EQ(major_version, "99");
+ EXPECT_EQ(minor_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(build_version, "0");
+ EXPECT_EQ(patch_version, "0");
+
+ // (4c) Policies:: UserAgentReduction and MajorVersionInMinor force disabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceDisabled,
+ UserAgentReductionEnterprisePolicyState::kForceDisabled);
+ EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
+ &major_version, &minor_version,
+ &build_version));
+ EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(minor_version, "0");
+ EXPECT_NE(build_version, "0");
+ // Patch version cannot be tested as it would be set in a release branch.
+
+ // (5) Features: UserAgentReduction and MajorVersionInMinor enabled.
scoped_feature_list.Reset();
scoped_feature_list.InitWithFeatures(
/*enabled_features=*/{blink::features::kReduceUserAgentMinorVersion,
blink::features::
kForceMajorVersionInMinorPositionInUserAgent},
/*disabled_features=*/{});
- product = GetProductAndVersion();
+
+ // (5a) Policies: UserAgentReduction and MajorVersionInMinor default.
+ product =
+ GetProductAndVersion(ForceMajorVersionToMinorPosition::kDefault,
+ UserAgentReductionEnterprisePolicyState::kDefault);
EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
&major_version, &minor_version,
&build_version, &patch_version));
@@ -1012,6 +1297,30 @@ TEST_F(UserAgentUtilsTest, GetProductAndVersion) {
EXPECT_EQ(minor_version, version_info::GetMajorVersionNumber());
EXPECT_EQ(build_version, "0");
EXPECT_EQ(patch_version, "0");
+
+ // (5b) Policies: UserAgentReduction and MajorVersionInMinor force enabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceEnabled,
+ UserAgentReductionEnterprisePolicyState::kForceEnabled);
+ EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
+ &major_version, &minor_version,
+ &build_version, &patch_version));
+ EXPECT_EQ(major_version, "99");
+ EXPECT_EQ(minor_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(build_version, "0");
+ EXPECT_EQ(patch_version, "0");
+
+ // (5c) Policies:: UserAgentReduction and MajorVersionInMinor force disabled.
+ product = GetProductAndVersion(
+ ForceMajorVersionToMinorPosition::kForceDisabled,
+ UserAgentReductionEnterprisePolicyState::kForceDisabled);
+ EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
+ &major_version, &minor_version,
+ &build_version));
+ EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
+ EXPECT_EQ(minor_version, "0");
+ EXPECT_NE(build_version, "0");
+ // Patch version cannot be tested as it would be set in a release branch.
}
TEST_F(UserAgentUtilsTest, GetUserAgent) {
diff --git a/chromium/components/endpoint_fetcher/BUILD.gn b/chromium/components/endpoint_fetcher/BUILD.gn
new file mode 100644
index 00000000000..f26fc85d599
--- /dev/null
+++ b/chromium/components/endpoint_fetcher/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2022 The Chromium 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("//testing/test.gni")
+
+source_set("endpoint_fetcher") {
+ sources = [
+ "endpoint_fetcher.cc",
+ "endpoint_fetcher.h",
+ ]
+
+ deps = [
+ "//base:base",
+ "//components/signin/public/identity_manager",
+ "//components/version_info:channel",
+ "//google_apis",
+ "//net/traffic_annotation",
+ "//services/data_decoder/public/cpp",
+ "//services/network/public/cpp",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "endpoint_fetcher_unittest.cc" ]
+
+ deps = [
+ ":endpoint_fetcher",
+ "//base:base",
+ "//base/test:test_support",
+ "//components/signin/public/identity_manager:test_support",
+ "//net:net",
+ "//net/traffic_annotation:test_support",
+ "//services/data_decoder/public/cpp:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp:cpp",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/endpoint_fetcher/DEPS b/chromium/components/endpoint_fetcher/DEPS
new file mode 100644
index 00000000000..90a6064202d
--- /dev/null
+++ b/chromium/components/endpoint_fetcher/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+components/signin/public/identity_manager",
+ "+components/version_info",
+ "+google_apis",
+ "+net",
+ "+services/data_decoder",
+ "+services/network",
+]
diff --git a/chromium/components/endpoint_fetcher/OWNERS b/chromium/components/endpoint_fetcher/OWNERS
new file mode 100644
index 00000000000..3104d54dc56
--- /dev/null
+++ b/chromium/components/endpoint_fetcher/OWNERS
@@ -0,0 +1,4 @@
+ayman@chromium.org
+davidjm@chromium.org
+mdjones@chromium.org
+wychen@chromium.org
diff --git a/chromium/components/endpoint_fetcher/README.md b/chromium/components/endpoint_fetcher/README.md
new file mode 100644
index 00000000000..04fee676366
--- /dev/null
+++ b/chromium/components/endpoint_fetcher/README.md
@@ -0,0 +1,3 @@
+The endpoint fetcher component contains code for calling a backend endpoint
+and returning a response. It supports two types of authentication: OAuth
+and a Chrome API key.
diff --git a/chromium/components/endpoint_fetcher/endpoint_fetcher.cc b/chromium/components/endpoint_fetcher/endpoint_fetcher.cc
new file mode 100644
index 00000000000..5dd418e8ccf
--- /dev/null
+++ b/chromium/components/endpoint_fetcher/endpoint_fetcher.cc
@@ -0,0 +1,288 @@
+// 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 "components/endpoint_fetcher/endpoint_fetcher.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/signin/public/identity_manager/access_token_info.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/version_info/channel.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/google_api_keys.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace {
+const char kContentTypeKey[] = "Content-Type";
+const char kDeveloperKey[] = "X-Developer-Key";
+const int kNumRetries = 3;
+const int64_t kDefaultTimeOutMs = 30000;
+} // namespace
+
+EndpointFetcher::EndpointFetcher(
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ const std::string& oauth_consumer_name,
+ const GURL& url,
+ const std::string& http_method,
+ const std::string& content_type,
+ const std::vector<std::string>& scopes,
+ int64_t timeout_ms,
+ const std::string& post_data,
+ const net::NetworkTrafficAnnotationTag& annotation_tag,
+ signin::IdentityManager* const identity_manager)
+ : EndpointFetcher(oauth_consumer_name,
+ url,
+ http_method,
+ content_type,
+ scopes,
+ timeout_ms,
+ post_data,
+ annotation_tag,
+ url_loader_factory,
+ identity_manager) {}
+
+EndpointFetcher::EndpointFetcher(
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ const GURL& url,
+ const std::string& http_method,
+ const std::string& content_type,
+ int64_t timeout_ms,
+ const std::string& post_data,
+ const std::vector<std::string>& headers,
+ const net::NetworkTrafficAnnotationTag& annotation_tag,
+ bool is_stable_channel)
+ : auth_type_(CHROME_API_KEY),
+ url_(url),
+ http_method_(http_method),
+ content_type_(content_type),
+ timeout_ms_(timeout_ms),
+ post_data_(post_data),
+ headers_(headers),
+ annotation_tag_(annotation_tag),
+ url_loader_factory_(url_loader_factory),
+ identity_manager_(nullptr),
+ sanitize_response_(true),
+ is_stable_channel_(is_stable_channel) {}
+
+EndpointFetcher::EndpointFetcher(
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ const GURL& url,
+ const net::NetworkTrafficAnnotationTag& annotation_tag)
+ : auth_type_(NO_AUTH),
+ url_(url),
+ http_method_("GET"),
+ content_type_(std::string()),
+ timeout_ms_(0),
+ post_data_(std::string()),
+ annotation_tag_(annotation_tag),
+ url_loader_factory_(url_loader_factory),
+ identity_manager_(nullptr),
+ sanitize_response_(false) {}
+
+EndpointFetcher::EndpointFetcher(
+ const std::string& oauth_consumer_name,
+ const GURL& url,
+ const std::string& http_method,
+ const std::string& content_type,
+ const std::vector<std::string>& scopes,
+ int64_t timeout_ms,
+ const std::string& post_data,
+ const net::NetworkTrafficAnnotationTag& annotation_tag,
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ signin::IdentityManager* const identity_manager)
+ : auth_type_(OAUTH),
+ oauth_consumer_name_(oauth_consumer_name),
+ url_(url),
+ http_method_(http_method),
+ content_type_(content_type),
+ timeout_ms_(timeout_ms),
+ post_data_(post_data),
+ annotation_tag_(annotation_tag),
+ url_loader_factory_(url_loader_factory),
+ identity_manager_(identity_manager),
+ sanitize_response_(true) {
+ for (auto scope : scopes) {
+ oauth_scopes_.insert(scope);
+ }
+}
+
+EndpointFetcher::EndpointFetcher(
+ const GURL& url,
+ const std::string& http_method,
+ const std::string& content_type,
+ int64_t timeout_ms,
+ const std::string& post_data,
+ const std::vector<std::string>& headers,
+ const std::vector<std::string>& cors_exempt_headers,
+ const net::NetworkTrafficAnnotationTag& annotation_tag,
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ const bool is_oauth_fetch)
+ : auth_type_(is_oauth_fetch ? OAUTH : CHROME_API_KEY),
+ url_(url),
+ http_method_(http_method),
+ content_type_(content_type),
+ timeout_ms_(timeout_ms),
+ post_data_(post_data),
+ headers_(headers),
+ cors_exempt_headers_(cors_exempt_headers),
+ annotation_tag_(annotation_tag),
+ url_loader_factory_(url_loader_factory),
+ identity_manager_(nullptr),
+ sanitize_response_(true) {}
+
+EndpointFetcher::EndpointFetcher(
+ const net::NetworkTrafficAnnotationTag& annotation_tag)
+ : timeout_ms_(kDefaultTimeOutMs),
+ annotation_tag_(annotation_tag),
+ identity_manager_(nullptr),
+ sanitize_response_(true) {}
+
+EndpointFetcher::~EndpointFetcher() = default;
+
+void EndpointFetcher::Fetch(EndpointFetcherCallback endpoint_fetcher_callback) {
+ DCHECK(!access_token_fetcher_);
+ DCHECK(!simple_url_loader_);
+ DCHECK(identity_manager_);
+ // Check if we have a primary account with the default consent level "sync"
+ // before attempting to fetch a token.
+ if (!identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
+ auto response = std::make_unique<EndpointResponse>();
+ VLOG(1) << __func__ << " No primary accounts found";
+ response->response = "No primary accounts found";
+ // TODO(crbug.com/993393) Add more detailed error messaging
+ std::move(endpoint_fetcher_callback).Run(std::move(response));
+ return;
+ }
+
+ signin::AccessTokenFetcher::TokenCallback token_callback = base::BindOnce(
+ &EndpointFetcher::OnAuthTokenFetched, weak_ptr_factory_.GetWeakPtr(),
+ std::move(endpoint_fetcher_callback));
+ // TODO(crbug.com/997018) Make access_token_fetcher_ local variable passed
+ // to callback
+ access_token_fetcher_ =
+ std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
+ oauth_consumer_name_, identity_manager_, oauth_scopes_,
+ std::move(token_callback),
+ signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
+}
+
+void EndpointFetcher::OnAuthTokenFetched(
+ EndpointFetcherCallback endpoint_fetcher_callback,
+ GoogleServiceAuthError error,
+ signin::AccessTokenInfo access_token_info) {
+ access_token_fetcher_.reset();
+ if (error.state() != GoogleServiceAuthError::NONE) {
+ auto response = std::make_unique<EndpointResponse>();
+ response->response = "There was an authentication error";
+ // TODO(crbug.com/993393) Add more detailed error messaging
+ std::move(endpoint_fetcher_callback).Run(std::move(response));
+ return;
+ }
+ PerformRequest(std::move(endpoint_fetcher_callback),
+ access_token_info.token.c_str());
+}
+
+void EndpointFetcher::PerformRequest(
+ EndpointFetcherCallback endpoint_fetcher_callback,
+ const char* key) {
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->method = http_method_;
+ resource_request->url = url_;
+ resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+ if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) {
+ resource_request->headers.SetHeader(kContentTypeKey, content_type_);
+ }
+ DCHECK(headers_.size() % 2 == 0);
+ for (size_t i = 0; i + 1 < headers_.size(); i += 2) {
+ resource_request->headers.SetHeader(headers_[i], headers_[i + 1]);
+ }
+ DCHECK(cors_exempt_headers_.size() % 2 == 0);
+ for (size_t i = 0; i + 1 < cors_exempt_headers_.size(); i += 2) {
+ resource_request->cors_exempt_headers.SetHeaderIfMissing(
+ cors_exempt_headers_[i], cors_exempt_headers_[i + 1]);
+ }
+ switch (auth_type_) {
+ case OAUTH:
+ resource_request->headers.SetHeader(
+ kDeveloperKey, GaiaUrls::GetInstance()->oauth2_chrome_client_id());
+ resource_request->headers.SetHeader(
+ net::HttpRequestHeaders::kAuthorization,
+ base::StringPrintf("Bearer %s", key));
+ break;
+ case CHROME_API_KEY: {
+ std::string api_key = is_stable_channel_
+ ? google_apis::GetAPIKey()
+ : google_apis::GetNonStableAPIKey();
+ resource_request->headers.SetHeader("x-goog-api-key", api_key);
+ break;
+ }
+ default:
+ break;
+ }
+ // TODO(crbug.com/997018) Make simple_url_loader_ local variable passed to
+ // callback
+ simple_url_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), annotation_tag_);
+
+ if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) {
+ simple_url_loader_->AttachStringForUpload(post_data_, content_type_);
+ }
+ simple_url_loader_->SetRetryOptions(kNumRetries,
+ network::SimpleURLLoader::RETRY_ON_5XX);
+ simple_url_loader_->SetTimeoutDuration(base::Milliseconds(timeout_ms_));
+ simple_url_loader_->SetAllowHttpErrorResults(true);
+ network::SimpleURLLoader::BodyAsStringCallback body_as_string_callback =
+ base::BindOnce(&EndpointFetcher::OnResponseFetched,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(endpoint_fetcher_callback));
+ simple_url_loader_->DownloadToString(
+ url_loader_factory_.get(), std::move(body_as_string_callback),
+ network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
+}
+
+void EndpointFetcher::OnResponseFetched(
+ EndpointFetcherCallback endpoint_fetcher_callback,
+ std::unique_ptr<std::string> response_body) {
+ if (response_body) {
+ if (sanitize_response_) {
+ data_decoder::JsonSanitizer::Sanitize(
+ std::move(*response_body),
+ base::BindOnce(&EndpointFetcher::OnSanitizationResult,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(endpoint_fetcher_callback)));
+ } else {
+ auto response = std::make_unique<EndpointResponse>();
+ response->response = *response_body;
+ std::move(endpoint_fetcher_callback).Run(std::move(response));
+ }
+ } else {
+ auto response = std::make_unique<EndpointResponse>();
+ // TODO(crbug.com/993393) Add more detailed error messaging
+ std::string net_error = net::ErrorToString(simple_url_loader_->NetError());
+ VLOG(1) << __func__ << " with response error: " << net_error;
+ response->response = "There was a response error";
+ std::move(endpoint_fetcher_callback).Run(std::move(response));
+ }
+ simple_url_loader_.reset();
+}
+
+void EndpointFetcher::OnSanitizationResult(
+ EndpointFetcherCallback endpoint_fetcher_callback,
+ data_decoder::JsonSanitizer::Result result) {
+ auto response = std::make_unique<EndpointResponse>();
+ if (result.value.has_value())
+ response->response = result.value.value();
+ else if (result.error.has_value())
+ response->response =
+ "There was a sanitization error: " + result.error.value();
+ else
+ response->response = "There was an unknown sanitization error";
+ std::move(endpoint_fetcher_callback).Run(std::move(response));
+}
+
+std::string EndpointFetcher::GetUrlForTesting() {
+ return url_.spec();
+}
diff --git a/chromium/components/endpoint_fetcher/endpoint_fetcher.h b/chromium/components/endpoint_fetcher/endpoint_fetcher.h
new file mode 100644
index 00000000000..3843c8e4cf0
--- /dev/null
+++ b/chromium/components/endpoint_fetcher/endpoint_fetcher.h
@@ -0,0 +1,169 @@
+// 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.
+
+#ifndef COMPONENTS_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
+#define COMPONENTS_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
+#include "components/signin/public/identity_manager/scope_set.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/data_decoder/public/cpp/json_sanitizer.h"
+
+namespace network {
+struct ResourceRequest;
+class SimpleURLLoader;
+} // namespace network
+
+namespace signin {
+struct AccessTokenInfo;
+class IdentityManager;
+} // namespace signin
+
+class GoogleServiceAuthError;
+class GURL;
+
+struct EndpointResponse {
+ std::string response;
+ // TODO(crbug.com/993393) Add more detailed error messaging
+};
+
+using EndpointFetcherCallback =
+ base::OnceCallback<void(std::unique_ptr<EndpointResponse>)>;
+
+// EndpointFetcher calls an endpoint and returns the response.
+// EndpointFetcher is not thread safe and it is up to the caller
+// to wait until the callback function passed to Fetch() completes
+// before invoking Fetch() again.
+// Destroying an EndpointFetcher will result in the in-flight request being
+// cancelled.
+// EndpointFetcher performs authentication via the signed in user to
+// Chrome.
+// If the request times out an empty response will be returned. There will also
+// be an error code indicating timeout once more detailed error messaging is
+// added TODO(crbug.com/993393).
+class EndpointFetcher {
+ public:
+ // Preferred constructor - forms identity_manager and url_loader_factory.
+ // OAUTH authentication is used for this constructor.
+ EndpointFetcher(
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ const std::string& oauth_consumer_name,
+ const GURL& url,
+ const std::string& http_method,
+ const std::string& content_type,
+ const std::vector<std::string>& scopes,
+ int64_t timeout_ms,
+ const std::string& post_data,
+ const net::NetworkTrafficAnnotationTag& annotation_tag,
+ signin::IdentityManager* const identity_manager);
+
+ // Constructor if Chrome API Key is used for authentication
+ EndpointFetcher(
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ const GURL& url,
+ const std::string& http_method,
+ const std::string& content_type,
+ int64_t timeout_ms,
+ const std::string& post_data,
+ const std::vector<std::string>& headers,
+ const net::NetworkTrafficAnnotationTag& annotation_tag,
+ bool is_stable_channel);
+
+ // Constructor if no authentication is needed.
+ EndpointFetcher(
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ const GURL& url,
+ const net::NetworkTrafficAnnotationTag& annotation_tag);
+
+ // Used for tests. Can be used if caller constructs their own
+ // url_loader_factory and identity_manager.
+ EndpointFetcher(
+ const std::string& oauth_consumer_name,
+ const GURL& url,
+ const std::string& http_method,
+ const std::string& content_type,
+ const std::vector<std::string>& scopes,
+ int64_t timeout_ms,
+ const std::string& post_data,
+ const net::NetworkTrafficAnnotationTag& annotation_tag,
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ signin::IdentityManager* const identity_manager);
+
+ // This Constructor can be used in a background thread.
+ EndpointFetcher(
+ const GURL& url,
+ const std::string& http_method,
+ const std::string& content_type,
+ int64_t timeout_ms,
+ const std::string& post_data,
+ const std::vector<std::string>& headers,
+ const std::vector<std::string>& cors_exempt_headers,
+ const net::NetworkTrafficAnnotationTag& annotation_tag,
+ const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
+ const bool is_oauth_fetch);
+
+ EndpointFetcher(const EndpointFetcher& endpoint_fetcher) = delete;
+
+ EndpointFetcher& operator=(const EndpointFetcher& endpoint_fetcher) = delete;
+
+ virtual ~EndpointFetcher();
+
+ // TODO(crbug.com/999256) enable cancellation support
+ void Fetch(EndpointFetcherCallback callback);
+ virtual void PerformRequest(EndpointFetcherCallback endpoint_fetcher_callback,
+ const char* key);
+
+ std::string GetUrlForTesting();
+
+ protected:
+ // Used for Mock only. see MockEndpointFetcher class.
+ explicit EndpointFetcher(
+ const net::NetworkTrafficAnnotationTag& annotation_tag);
+
+ private:
+ void OnAuthTokenFetched(EndpointFetcherCallback callback,
+ GoogleServiceAuthError error,
+ signin::AccessTokenInfo access_token_info);
+ void OnResponseFetched(EndpointFetcherCallback callback,
+ std::unique_ptr<std::string> response_body);
+ void OnSanitizationResult(EndpointFetcherCallback endpoint_fetcher_callback,
+ data_decoder::JsonSanitizer::Result result);
+
+ enum AuthType { CHROME_API_KEY, OAUTH, NO_AUTH };
+ AuthType auth_type_;
+
+ // Members set in constructor to be passed to network::ResourceRequest or
+ // network::SimpleURLLoader.
+ const std::string oauth_consumer_name_;
+ const GURL url_;
+ const std::string http_method_;
+ const std::string content_type_;
+ int64_t timeout_ms_;
+ const std::string post_data_;
+ const std::vector<std::string> headers_;
+ const std::vector<std::string> cors_exempt_headers_;
+ const net::NetworkTrafficAnnotationTag annotation_tag_;
+ signin::ScopeSet oauth_scopes_;
+
+ // Members set in constructor
+ const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+ const raw_ptr<signin::IdentityManager> identity_manager_;
+ bool sanitize_response_;
+ bool is_stable_channel_;
+
+ // Members set in Fetch
+ std::unique_ptr<const signin::PrimaryAccountAccessTokenFetcher>
+ access_token_fetcher_;
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
+
+ base::WeakPtrFactory<EndpointFetcher> weak_ptr_factory_{this};
+};
+
+#endif // COMPONENTS_ENDPOINT_FETCHER_ENDPOINT_FETCHER_H_
diff --git a/chromium/components/endpoint_fetcher/endpoint_fetcher_unittest.cc b/chromium/components/endpoint_fetcher/endpoint_fetcher_unittest.cc
new file mode 100644
index 00000000000..45823261d45
--- /dev/null
+++ b/chromium/components/endpoint_fetcher/endpoint_fetcher_unittest.cc
@@ -0,0 +1,167 @@
+// 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 "components/endpoint_fetcher/endpoint_fetcher.h"
+
+#include <string>
+
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "net/http/http_util.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using MockEndpointFetcherCallback = base::MockCallback<EndpointFetcherCallback>;
+
+namespace {
+const char kContentType[] = "mock_content_type";
+const char kEmail[] = "mock_email@gmail.com";
+const char kEndpoint[] = "https://my-endpoint.com";
+const char kExpectedResponse[] = "{}";
+const char kExpectedAuthError[] = "There was an authentication error";
+const char kExpectedResponseError[] = "There was a response error";
+const char kExpectedPrimaryAccountError[] = "No primary accounts found";
+const char kExpectedSanitizationError[] = "There was a sanitization error";
+const char kHttpMethod[] = "POST";
+const char kMalformedResponse[] = "asdf";
+const char kMockPostData[] = "mock_post_data";
+int64_t kMockTimeoutMs = 1000000;
+const char kOAuthConsumerName[] = "mock_oauth_consumer_name";
+const char kScope[] = "mock_scope";
+} // namespace
+
+using ::testing::Field;
+using ::testing::Pointee;
+
+class EndpointFetcherTest : public testing::Test {
+ protected:
+ EndpointFetcherTest() {}
+
+ EndpointFetcherTest(const EndpointFetcherTest& endpoint_fetcher_test) =
+ delete;
+ EndpointFetcherTest& operator=(
+ const EndpointFetcherTest& endpoint_fetcher_test) = delete;
+
+ ~EndpointFetcherTest() override {}
+
+ void SetUp() override {
+ scoped_refptr<network::SharedURLLoaderFactory> test_url_loader_factory =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
+ endpoint_fetcher_ = std::make_unique<EndpointFetcher>(
+ kOAuthConsumerName, GURL(kEndpoint), kHttpMethod, kContentType,
+ std::vector<std::string>{kScope}, kMockTimeoutMs, kMockPostData,
+ TRAFFIC_ANNOTATION_FOR_TESTS, test_url_loader_factory,
+ identity_test_env_.identity_manager());
+ in_process_data_decoder_ =
+ std::make_unique<data_decoder::test::InProcessDataDecoder>();
+ }
+
+ void SignIn() {
+ identity_test_env_.MakePrimaryAccountAvailable(kEmail,
+ signin::ConsentLevel::kSync);
+ identity_test_env_.SetAutomaticIssueOfAccessTokens(true);
+ }
+
+ MockEndpointFetcherCallback& endpoint_fetcher_callback() {
+ return mock_callback_;
+ }
+
+ EndpointFetcher* endpoint_fetcher() { return endpoint_fetcher_.get(); }
+
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return &test_url_loader_factory_;
+ }
+
+ signin::IdentityTestEnvironment& identity_test_env() {
+ return identity_test_env_;
+ }
+
+ void SetMockResponse(const GURL& request_url,
+ const std::string& response_data,
+ net::HttpStatusCode response_code,
+ net::Error error) {
+ auto head = network::mojom::URLResponseHead::New();
+ std::string headers(base::StringPrintf(
+ "HTTP/1.1 %d %s\nContent-type: application/json\n\n",
+ static_cast<int>(response_code), GetHttpReasonPhrase(response_code)));
+ head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+ net::HttpUtil::AssembleRawHeaders(headers));
+ head->mime_type = "application/json";
+ network::URLLoaderCompletionStatus status(error);
+ status.decoded_body_length = response_data.size();
+ test_url_loader_factory_.AddResponse(request_url, std::move(head),
+ response_data, status);
+ }
+
+ private:
+ base::test::TaskEnvironment task_environment_;
+ signin::IdentityTestEnvironment identity_test_env_;
+ MockEndpointFetcherCallback mock_callback_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ std::unique_ptr<EndpointFetcher> endpoint_fetcher_;
+ std::unique_ptr<data_decoder::test::InProcessDataDecoder>
+ in_process_data_decoder_;
+};
+
+TEST_F(EndpointFetcherTest, FetchResponse) {
+ SignIn();
+ SetMockResponse(GURL(kEndpoint), kExpectedResponse, net::HTTP_OK, net::OK);
+ EXPECT_CALL(
+ endpoint_fetcher_callback(),
+ Run(Pointee(Field(&EndpointResponse::response, kExpectedResponse))));
+ endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get());
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(EndpointFetcherTest, FetchMalformedResponse) {
+ SignIn();
+ SetMockResponse(GURL(kEndpoint), kMalformedResponse, net::HTTP_OK, net::OK);
+ EXPECT_CALL(
+ endpoint_fetcher_callback(),
+ Run(Pointee(Field(&EndpointResponse::response,
+ testing::StartsWith(kExpectedSanitizationError)))));
+ endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get());
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(EndpointFetcherTest, FetchEndpointResponseError) {
+ SignIn();
+ SetMockResponse(GURL(kEndpoint), kExpectedResponse, net::HTTP_BAD_REQUEST,
+ net::ERR_FAILED);
+ EXPECT_CALL(
+ endpoint_fetcher_callback(),
+ Run(Pointee(Field(&EndpointResponse::response, kExpectedResponseError))));
+ endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get());
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(EndpointFetcherTest, FetchOAuthError) {
+ SignIn();
+ identity_test_env().SetAutomaticIssueOfAccessTokens(false);
+ EXPECT_CALL(
+ endpoint_fetcher_callback(),
+ Run(Pointee(Field(&EndpointResponse::response, kExpectedAuthError))));
+ endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get());
+ identity_test_env().WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+ GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(EndpointFetcherTest, FetchOAuthNoPrimaryAccount) {
+ EXPECT_CALL(endpoint_fetcher_callback(),
+ Run(Pointee(Field(&EndpointResponse::response,
+ kExpectedPrimaryAccountError))));
+ endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get());
+ base::RunLoop().RunUntilIdle();
+}
diff --git a/chromium/components/enterprise/BUILD.gn b/chromium/components/enterprise/BUILD.gn
index 7ccdab80861..eda7994fa89 100644
--- a/chromium/components/enterprise/BUILD.gn
+++ b/chromium/components/enterprise/BUILD.gn
@@ -70,6 +70,13 @@ static_library("enterprise") {
"//services/network/public/cpp",
]
+ # Include certain intermediate targets as public deps so consumers
+ # can start using them as needed.
+ public_deps = [
+ "//components/reporting/client:report_queue_configuration",
+ "//components/reporting/util:status",
+ ]
+
if (!is_chromeos_ash) {
sources += [
"browser/controller/chrome_browser_cloud_management_controller.cc",
@@ -96,22 +103,43 @@ static_library("test_support") {
}
}
+static_library("reporting_test_support") {
+ testonly = true
+
+ sources = [
+ "browser/reporting/fake_browser_report_generator_delegate.cc",
+ "browser/reporting/fake_browser_report_generator_delegate.h",
+ ]
+
+ deps = [
+ ":enterprise",
+ "//base",
+ "//components/policy/core/common:test_support",
+ "//components/policy/proto",
+ "//components/version_info",
+ ]
+}
+
source_set("unit_tests") {
testonly = true
sources = [
"browser/reporting/chrome_profile_request_generator_unittest.cc",
"browser/reporting/report_uploader_unittest.cc",
+ "common/proto/connectors_unittest.cc",
]
deps = [
":enterprise",
+ ":reporting_test_support",
+ "common/proto:connectors_proto",
"//base/test:test_support",
"//build:chromeos_buildflags",
"//components/policy/core/common:test_support",
"//components/version_info",
"//testing/gmock",
"//testing/gtest",
+ "//third_party/content_analysis_sdk:liblcasdk",
]
if (!is_chromeos_ash) {
diff --git a/chromium/components/enterprise/DEPS b/chromium/components/enterprise/DEPS
index 5e16a50061c..5cdbeb3e371 100644
--- a/chromium/components/enterprise/DEPS
+++ b/chromium/components/enterprise/DEPS
@@ -9,5 +9,6 @@ include_rules = [
"+crypto/signature_verifier.h",
"+net",
"+services/network/public",
- "+third_party/protobuf"
+ "+third_party/protobuf",
+ "+third_party/content_analysis_sdk"
]
diff --git a/chromium/components/enterprise/browser/controller/browser_dm_token_storage.cc b/chromium/components/enterprise/browser/controller/browser_dm_token_storage.cc
index 58f289873ac..db7ead3bafc 100644
--- a/chromium/components/enterprise/browser/controller/browser_dm_token_storage.cc
+++ b/chromium/components/enterprise/browser/controller/browser_dm_token_storage.cc
@@ -104,6 +104,8 @@ void BrowserDMTokenStorage::StoreDMToken(const std::string& dm_token,
store_callback_ = std::move(callback);
if (dm_token.empty()) {
+ // TODO(crbug.com/1318153): Implement DMToken deletion logic once all
+ // delegates have the required handling.
dm_token_ = CreateEmptyToken();
SaveDMToken("");
} else if (dm_token == kInvalidTokenValue) {
diff --git a/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.cc b/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.cc
index e76df99e587..63e90c85bee 100644
--- a/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.cc
+++ b/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.cc
@@ -272,11 +272,21 @@ bool ChromeBrowserCloudManagementController::
return delegate_->IsEnterpriseStartupDialogShowing();
}
-void ChromeBrowserCloudManagementController::UnenrollBrowser() {
+void ChromeBrowserCloudManagementController::UnenrollBrowser(
+ bool delete_dm_token) {
+ if (delete_dm_token) {
+ DVLOG(1) << "Browser unenrollment: Attempting DMToken deletion";
+ BrowserDMTokenStorage::Get()->ClearDMToken(base::BindOnce(
+ &ChromeBrowserCloudManagementController::UnenrollCallback,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+
// Invalidate DM token in storage.
- BrowserDMTokenStorage::Get()->InvalidateDMToken(base::BindOnce(
- &ChromeBrowserCloudManagementController::InvalidateDMTokenCallback,
- weak_factory_.GetWeakPtr()));
+ DVLOG(1) << "Browser unenrollment: Attempting DMToken invalidation";
+ BrowserDMTokenStorage::Get()->InvalidateDMToken(
+ base::BindOnce(&ChromeBrowserCloudManagementController::UnenrollCallback,
+ weak_factory_.GetWeakPtr()));
}
void ChromeBrowserCloudManagementController::InvalidatePolicies() {
@@ -292,17 +302,15 @@ void ChromeBrowserCloudManagementController::InvalidatePolicies() {
report_scheduler_->OnDMTokenUpdated();
}
-void ChromeBrowserCloudManagementController::InvalidateDMTokenCallback(
- bool success) {
+void ChromeBrowserCloudManagementController::UnenrollCallback(bool success) {
UMA_HISTOGRAM_BOOLEAN(
"Enterprise.MachineLevelUserCloudPolicyEnrollment.UnenrollSuccess",
success);
- if (success) {
- DVLOG(1) << "Successfully invalidated the DM token";
+ DVLOG(1) << "Browser unenrollment: " << (success ? "Successful" : "Failed");
+
+ if (success)
InvalidatePolicies();
- } else {
- DVLOG(1) << "Failed to invalidate the DM token";
- }
+
NotifyBrowserUnenrolled(success);
}
@@ -319,9 +327,15 @@ void ChromeBrowserCloudManagementController::OnRegistrationStateChanged(
void ChromeBrowserCloudManagementController::OnClientError(
CloudPolicyClient* client) {
// DM_STATUS_SERVICE_DEVICE_NOT_FOUND being the last status implies the
- // browser has been unenrolled.
- if (client->status() == DM_STATUS_SERVICE_DEVICE_NOT_FOUND)
- UnenrollBrowser();
+ // browser has been unenrolled via DMToken invalidation, so it is not expected
+ // to re-enroll automatically. DM_STATUS_SERVICE_DEVICE_NEEDS_RESET signals
+ // that the browser has been unenrolled via DMToken deletion, and that it will
+ // automatically re-enroll if a valid enrollment token has been set.
+ if (client->status() == DM_STATUS_SERVICE_DEVICE_NOT_FOUND ||
+ client->status() == DM_STATUS_SERVICE_DEVICE_NEEDS_RESET) {
+ UnenrollBrowser(/*delete_dm_token=*/client->status() ==
+ DM_STATUS_SERVICE_DEVICE_NEEDS_RESET);
+ }
}
void ChromeBrowserCloudManagementController::OnServiceAccountSet(
diff --git a/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h b/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h
index 46475346e5a..38d474aa129 100644
--- a/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h
+++ b/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h
@@ -232,7 +232,9 @@ class ChromeBrowserCloudManagementController
// Returns whether the enterprise startup dialog is being diaplayed.
bool IsEnterpriseStartupDialogShowing();
- void UnenrollBrowser();
+ // Unenrolls the browser from cloud management by either invalidating or
+ // deleting the stored DMToken.
+ void UnenrollBrowser(bool delete_dm_token);
// CloudPolicyClient::Observer implementation:
void OnPolicyFetched(CloudPolicyClient* client) override;
@@ -266,7 +268,7 @@ class ChromeBrowserCloudManagementController
const std::string& client_id);
void InvalidatePolicies();
- void InvalidateDMTokenCallback(bool success);
+ void UnenrollCallback(bool success);
void CreateReportScheduler();
diff --git a/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_helper.cc b/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_helper.cc
index aa564e9a106..74e6884d1d0 100644
--- a/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_helper.cc
+++ b/chromium/components/enterprise/browser/controller/chrome_browser_cloud_management_helper.cc
@@ -70,8 +70,14 @@ void ChromeBrowserCloudManagementRegistrar::
registration_helper_ = std::make_unique<CloudPolicyClientRegistrationHelper>(
policy_client.get(),
enterprise_management::DeviceRegisterRequest::BROWSER);
+
+ // Check if token enrollment is mandatory
+ bool is_enrollment_mandatory =
+ BrowserDMTokenStorage::Get()->ShouldDisplayErrorMessageOnFailure();
+
registration_helper_->StartRegistrationWithEnrollmentToken(
enrollment_token, client_id, client_data_delegate,
+ is_enrollment_mandatory,
base::BindOnce(&ChromeBrowserCloudManagementRegistrar::
CallCloudManagementRegistrationCallback,
base::Unretained(this), std::move(policy_client),
diff --git a/chromium/components/enterprise/browser/reporting/chrome_profile_request_generator_unittest.cc b/chromium/components/enterprise/browser/reporting/chrome_profile_request_generator_unittest.cc
index 04ab69ff30d..76a6788bebe 100644
--- a/chromium/components/enterprise/browser/reporting/chrome_profile_request_generator_unittest.cc
+++ b/chromium/components/enterprise/browser/reporting/chrome_profile_request_generator_unittest.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "components/enterprise/browser/reporting/fake_browser_report_generator_delegate.h"
#include "components/enterprise/browser/reporting/report_util.h"
#include "components/enterprise/browser/reporting/reporting_delegate_factory.h"
#include "components/policy/core/common/cloud/cloud_policy_util.h"
@@ -26,83 +27,6 @@ const base::FilePath::CharType kProfilePath[] =
constexpr char kProfileName[] = "profile-name";
constexpr char kBrowserExePath[] = "browser-path";
-class FakeBrowserReportGeneratorDelegate
- : public BrowserReportGenerator::Delegate {
- public:
- ~FakeBrowserReportGeneratorDelegate() override = default;
-
- std::string GetExecutablePath() override { return kBrowserExePath; }
- version_info::Channel GetChannel() override {
- return version_info::Channel::STABLE;
- }
- std::vector<BrowserReportGenerator::ReportedProfileData> GetReportedProfiles()
- override {
- return std::vector<BrowserReportGenerator::ReportedProfileData>();
- }
- bool IsExtendedStableChannel() override { return true; }
- void GenerateBuildStateInfo(
- enterprise_management::BrowserReport* report) override {
- return;
- }
- void GeneratePluginsIfNeeded(
- BrowserReportGenerator::ReportCallback callback,
- std::unique_ptr<enterprise_management::BrowserReport> report) override {
- std::move(callback).Run(std::move(report));
- }
-};
-
-class FakeProfileReportGeneratorDelegate
- : public ProfileReportGenerator::Delegate {
- public:
- ~FakeProfileReportGeneratorDelegate() override = default;
-
- bool Init(const base::FilePath& path) override { return true; }
- void GetSigninUserInfo(
- enterprise_management::ChromeUserProfileInfo* report) override {}
- void GetExtensionInfo(
- enterprise_management::ChromeUserProfileInfo* report) override {}
- void GetExtensionRequest(
- enterprise_management::ChromeUserProfileInfo* report) override {}
-
- std::unique_ptr<policy::PolicyConversionsClient> MakePolicyConversionsClient()
- override {
- return nullptr;
- }
- policy::MachineLevelUserCloudPolicyManager* GetCloudPolicyManager() override {
- return nullptr;
- }
-};
-
-class FakeReportingDelegateFactory : public ReportingDelegateFactory {
- public:
- ~FakeReportingDelegateFactory() override = default;
-
- std::unique_ptr<BrowserReportGenerator::Delegate>
- GetBrowserReportGeneratorDelegate() override {
- return std::make_unique<FakeBrowserReportGeneratorDelegate>();
- }
-
- std::unique_ptr<ProfileReportGenerator::Delegate>
- GetProfileReportGeneratorDelegate() override {
- return std::make_unique<FakeProfileReportGeneratorDelegate>();
- }
-
- std::unique_ptr<ReportGenerator::Delegate> GetReportGeneratorDelegate()
- override {
- return nullptr;
- }
-
- std::unique_ptr<ReportScheduler::Delegate> GetReportSchedulerDelegate()
- override {
- return nullptr;
- }
-
- std::unique_ptr<RealTimeReportGenerator::Delegate>
- GetRealTimeReportGeneratorDelegate() override {
- return nullptr;
- }
-};
-
} // namespace
class ChromeProfileRequestGeneratorTest : public ::testing::Test {
@@ -141,7 +65,7 @@ class ChromeProfileRequestGeneratorTest : public ::testing::Test {
}
private:
- FakeReportingDelegateFactory delegate_factory_;
+ test::FakeReportingDelegateFactory delegate_factory_{kBrowserExePath};
ChromeProfileRequestGenerator generator_;
};
diff --git a/chromium/components/enterprise/browser/reporting/fake_browser_report_generator_delegate.cc b/chromium/components/enterprise/browser/reporting/fake_browser_report_generator_delegate.cc
new file mode 100644
index 00000000000..b3c31a31b3d
--- /dev/null
+++ b/chromium/components/enterprise/browser/reporting/fake_browser_report_generator_delegate.cc
@@ -0,0 +1,122 @@
+// Copyright 2022 The Chromium 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/enterprise/browser/reporting/fake_browser_report_generator_delegate.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
+#include "components/enterprise/browser/reporting/browser_report_generator.h"
+#include "components/enterprise/browser/reporting/report_util.h"
+#include "components/enterprise/browser/reporting/reporting_delegate_factory.h"
+#include "components/version_info/channel.h"
+
+namespace enterprise_management {
+class ChromeUserProfileInfo;
+class BrowserReport;
+} // namespace enterprise_management
+
+namespace policy {
+class PolicyConversionsClient;
+class MachineLevelUserCloudPolicyManager;
+} // namespace policy
+
+namespace enterprise_reporting::test {
+
+FakeProfileReportGeneratorDelegate::~FakeProfileReportGeneratorDelegate() =
+ default;
+
+bool FakeProfileReportGeneratorDelegate::Init(const base::FilePath& path) {
+ return true;
+}
+void FakeProfileReportGeneratorDelegate::GetSigninUserInfo(
+ enterprise_management::ChromeUserProfileInfo* report) {}
+
+void FakeProfileReportGeneratorDelegate::GetExtensionInfo(
+ enterprise_management::ChromeUserProfileInfo* report) {}
+
+void FakeProfileReportGeneratorDelegate::GetExtensionRequest(
+ enterprise_management::ChromeUserProfileInfo* report) {}
+
+std::unique_ptr<policy::PolicyConversionsClient>
+FakeProfileReportGeneratorDelegate::MakePolicyConversionsClient() {
+ return nullptr;
+}
+
+policy::MachineLevelUserCloudPolicyManager*
+FakeProfileReportGeneratorDelegate::GetCloudPolicyManager() {
+ return nullptr;
+}
+
+FakeBrowserReportGeneratorDelegate::FakeBrowserReportGeneratorDelegate(
+ base::StringPiece executable_path)
+ : executable_path_(executable_path) {}
+
+FakeBrowserReportGeneratorDelegate::~FakeBrowserReportGeneratorDelegate() =
+ default;
+
+std::string FakeBrowserReportGeneratorDelegate::GetExecutablePath() {
+ return executable_path_;
+}
+
+version_info::Channel FakeBrowserReportGeneratorDelegate::GetChannel() {
+ return version_info::Channel::STABLE;
+}
+
+std::vector<BrowserReportGenerator::ReportedProfileData>
+FakeBrowserReportGeneratorDelegate::GetReportedProfiles() {
+ return std::vector<BrowserReportGenerator::ReportedProfileData>();
+}
+
+bool FakeBrowserReportGeneratorDelegate::IsExtendedStableChannel() {
+ return true;
+}
+
+void FakeBrowserReportGeneratorDelegate::GenerateBuildStateInfo(
+ enterprise_management::BrowserReport* report) {
+ return;
+}
+
+void FakeBrowserReportGeneratorDelegate::GeneratePluginsIfNeeded(
+ BrowserReportGenerator::ReportCallback callback,
+ std::unique_ptr<enterprise_management::BrowserReport> report) {
+ std::move(callback).Run(std::move(report));
+}
+
+FakeReportingDelegateFactory::FakeReportingDelegateFactory(
+ base::StringPiece executable_path)
+ : executable_path_(executable_path) {}
+
+FakeReportingDelegateFactory::~FakeReportingDelegateFactory() = default;
+
+std::unique_ptr<BrowserReportGenerator::Delegate>
+FakeReportingDelegateFactory::GetBrowserReportGeneratorDelegate() {
+ return std::make_unique<test::FakeBrowserReportGeneratorDelegate>(
+ executable_path_);
+}
+
+std::unique_ptr<ProfileReportGenerator::Delegate>
+FakeReportingDelegateFactory::GetProfileReportGeneratorDelegate() {
+ return std::make_unique<FakeProfileReportGeneratorDelegate>();
+}
+
+std::unique_ptr<ReportGenerator::Delegate>
+FakeReportingDelegateFactory::GetReportGeneratorDelegate() {
+ return nullptr;
+}
+
+std::unique_ptr<ReportScheduler::Delegate>
+FakeReportingDelegateFactory::GetReportSchedulerDelegate() {
+ return nullptr;
+}
+
+std::unique_ptr<RealTimeReportGenerator::Delegate>
+FakeReportingDelegateFactory::GetRealTimeReportGeneratorDelegate() {
+ return nullptr;
+}
+
+} // namespace enterprise_reporting::test \ No newline at end of file
diff --git a/chromium/components/enterprise/browser/reporting/fake_browser_report_generator_delegate.h b/chromium/components/enterprise/browser/reporting/fake_browser_report_generator_delegate.h
new file mode 100644
index 00000000000..c0bd20f6b91
--- /dev/null
+++ b/chromium/components/enterprise/browser/reporting/fake_browser_report_generator_delegate.h
@@ -0,0 +1,106 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_ENTERPRISE_BROWSER_REPORTING_FAKE_BROWSER_REPORT_GENERATOR_DELEGATE_H_
+#define COMPONENTS_ENTERPRISE_BROWSER_REPORTING_FAKE_BROWSER_REPORT_GENERATOR_DELEGATE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
+#include "components/enterprise/browser/reporting/browser_report_generator.h"
+#include "components/enterprise/browser/reporting/report_util.h"
+#include "components/enterprise/browser/reporting/reporting_delegate_factory.h"
+#include "components/version_info/channel.h"
+
+namespace enterprise_management {
+class ChromeUserProfileInfo;
+class BrowserReport;
+} // namespace enterprise_management
+
+namespace policy {
+class PolicyConversionsClient;
+class MachineLevelUserCloudPolicyManager;
+} // namespace policy
+
+namespace enterprise_reporting::test {
+
+class FakeProfileReportGeneratorDelegate
+ : public ProfileReportGenerator::Delegate {
+ public:
+ ~FakeProfileReportGeneratorDelegate() override;
+
+ bool Init(const base::FilePath& path) override;
+
+ void GetSigninUserInfo(
+ enterprise_management::ChromeUserProfileInfo* report) override;
+
+ void GetExtensionInfo(
+ enterprise_management::ChromeUserProfileInfo* report) override;
+
+ void GetExtensionRequest(
+ enterprise_management::ChromeUserProfileInfo* report) override;
+
+ std::unique_ptr<policy::PolicyConversionsClient> MakePolicyConversionsClient()
+ override;
+
+ policy::MachineLevelUserCloudPolicyManager* GetCloudPolicyManager() override;
+};
+
+class FakeBrowserReportGeneratorDelegate
+ : public BrowserReportGenerator::Delegate {
+ public:
+ explicit FakeBrowserReportGeneratorDelegate(
+ base::StringPiece executable_path);
+ ~FakeBrowserReportGeneratorDelegate() override;
+
+ std::string GetExecutablePath() override;
+
+ version_info::Channel GetChannel() override;
+
+ std::vector<BrowserReportGenerator::ReportedProfileData> GetReportedProfiles()
+ override;
+
+ bool IsExtendedStableChannel() override;
+
+ void GenerateBuildStateInfo(
+ enterprise_management::BrowserReport* report) override;
+
+ void GeneratePluginsIfNeeded(
+ BrowserReportGenerator::ReportCallback callback,
+ std::unique_ptr<enterprise_management::BrowserReport> report) override;
+
+ private:
+ const std::string executable_path_;
+};
+
+class FakeReportingDelegateFactory : public ReportingDelegateFactory {
+ public:
+ explicit FakeReportingDelegateFactory(base::StringPiece executable_path);
+
+ ~FakeReportingDelegateFactory() override;
+
+ std::unique_ptr<BrowserReportGenerator::Delegate>
+ GetBrowserReportGeneratorDelegate() override;
+
+ std::unique_ptr<ProfileReportGenerator::Delegate>
+ GetProfileReportGeneratorDelegate() override;
+
+ std::unique_ptr<ReportGenerator::Delegate> GetReportGeneratorDelegate()
+ override;
+
+ std::unique_ptr<ReportScheduler::Delegate> GetReportSchedulerDelegate()
+ override;
+
+ std::unique_ptr<RealTimeReportGenerator::Delegate>
+ GetRealTimeReportGeneratorDelegate() override;
+
+ private:
+ const std::string executable_path_;
+};
+
+} // namespace enterprise_reporting::test
+
+#endif // COMPONENTS_ENTERPRISE_BROWSER_REPORTING_FAKE_BROWSER_REPORT_GENERATOR_DELEGATE_H_ \ No newline at end of file
diff --git a/chromium/components/enterprise/browser/reporting/policy_info.cc b/chromium/components/enterprise/browser/reporting/policy_info.cc
index cc7ed94ee90..d9156637294 100644
--- a/chromium/components/enterprise/browser/reporting/policy_info.cc
+++ b/chromium/components/enterprise/browser/reporting/policy_info.cc
@@ -108,24 +108,23 @@ void UpdatePolicyInfo(em::Policy* policy_info,
} // namespace
void AppendChromePolicyInfoIntoProfileReport(
- const base::Value& policies,
+ const base::Value::Dict& policies,
em::ChromeUserProfileInfo* profile_info) {
- for (auto policy_iter : policies.FindKey("chromePolicies")->DictItems()) {
+ for (auto policy_iter : *policies.FindDict("chromePolicies")) {
UpdatePolicyInfo(profile_info->add_chrome_policies(), policy_iter.first,
policy_iter.second);
}
}
void AppendExtensionPolicyInfoIntoProfileReport(
- const base::Value& policies,
+ const base::Value::Dict& policies,
em::ChromeUserProfileInfo* profile_info) {
- if (!policies.FindKey("extensionPolicies")) {
+ if (!policies.Find("extensionPolicies")) {
// Android and iOS don't support extensions and their policies.
return;
}
- for (auto extension_iter :
- policies.FindKey("extensionPolicies")->DictItems()) {
+ for (auto extension_iter : *policies.FindDict("extensionPolicies")) {
const base::Value& policies_value = extension_iter.second;
if (policies_value.DictSize() == 0)
continue;
diff --git a/chromium/components/enterprise/browser/reporting/policy_info.h b/chromium/components/enterprise/browser/reporting/policy_info.h
index 82a5fa9ea7a..820ef51340b 100644
--- a/chromium/components/enterprise/browser/reporting/policy_info.h
+++ b/chromium/components/enterprise/browser/reporting/policy_info.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_ENTERPRISE_BROWSER_REPORTING_POLICY_INFO_H_
#define COMPONENTS_ENTERPRISE_BROWSER_REPORTING_POLICY_INFO_H_
+#include "base/values.h"
#include "components/policy/proto/device_management_backend.pb.h"
namespace base {
@@ -20,11 +21,11 @@ class MachineLevelUserCloudPolicyManager;
namespace enterprise_reporting {
void AppendChromePolicyInfoIntoProfileReport(
- const base::Value& policies,
+ const base::Value::Dict& policies,
enterprise_management::ChromeUserProfileInfo* profile_info);
void AppendExtensionPolicyInfoIntoProfileReport(
- const base::Value& policies,
+ const base::Value::Dict& policies,
enterprise_management::ChromeUserProfileInfo* profile_info);
void AppendMachineLevelUserCloudPolicyFetchTimestamp(
diff --git a/chromium/components/enterprise/browser/reporting/profile_report_generator.cc b/chromium/components/enterprise/browser/reporting/profile_report_generator.cc
index 3a88bc352ec..b28fd89da9e 100644
--- a/chromium/components/enterprise/browser/reporting/profile_report_generator.cc
+++ b/chromium/components/enterprise/browser/reporting/profile_report_generator.cc
@@ -72,7 +72,7 @@ ProfileReportGenerator::MaybeGenerate(const base::FilePath& path,
policies_ = policy::DictionaryPolicyConversions(std::move(client))
.EnableConvertTypes(false)
.EnablePrettyPrint(false)
- .ToValue();
+ .ToValueDict();
GetChromePolicyInfo();
GetExtensionPolicyInfo();
GetPolicyFetchTimestampInfo();
diff --git a/chromium/components/enterprise/browser/reporting/profile_report_generator.h b/chromium/components/enterprise/browser/reporting/profile_report_generator.h
index d5faeaada66..c96480c0324 100644
--- a/chromium/components/enterprise/browser/reporting/profile_report_generator.h
+++ b/chromium/components/enterprise/browser/reporting/profile_report_generator.h
@@ -83,7 +83,7 @@ class ProfileReportGenerator {
private:
std::unique_ptr<Delegate> delegate_;
- base::Value policies_;
+ base::Value::Dict policies_;
bool extensions_enabled_ = true;
bool policies_enabled_ = true;
diff --git a/chromium/components/enterprise/browser/reporting/real_time_uploader.cc b/chromium/components/enterprise/browser/reporting/real_time_uploader.cc
index faf959e49b7..3e48b6ea558 100644
--- a/chromium/components/enterprise/browser/reporting/real_time_uploader.cc
+++ b/chromium/components/enterprise/browser/reporting/real_time_uploader.cc
@@ -114,12 +114,12 @@ void RealTimeUploader::OnReportQueueCreated(
}
void RealTimeUploader::UploadClosure(
- std::unique_ptr<google::protobuf::MessageLite> report,
+ std::unique_ptr<const google::protobuf::MessageLite> report,
EnqueueCallback callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(report_queue_);
report_queue_->Enqueue(
- report.get(), report_priority_,
+ std::move(report), report_priority_,
base::BindPostTask(
base::ThreadTaskRunnerHandle::Get(),
base::BindOnce(&RealTimeUploader::OnReportEnqueued,
diff --git a/chromium/components/enterprise/browser/reporting/real_time_uploader.h b/chromium/components/enterprise/browser/reporting/real_time_uploader.h
index 2ee58302d1b..0de77453c54 100644
--- a/chromium/components/enterprise/browser/reporting/real_time_uploader.h
+++ b/chromium/components/enterprise/browser/reporting/real_time_uploader.h
@@ -57,8 +57,9 @@ class RealTimeUploader {
reporting::ReportQueueProvider::CreateReportQueueResponse
create_report_queue_response);
- void UploadClosure(std::unique_ptr<google::protobuf::MessageLite> report,
- EnqueueCallback callback);
+ void UploadClosure(
+ std::unique_ptr<const google::protobuf::MessageLite> report,
+ EnqueueCallback callback);
void OnReportEnqueued(EnqueueCallback callback, reporting::Status status);
std::unique_ptr<reporting::ReportQueue> report_queue_;
diff --git a/chromium/components/enterprise/browser/reporting/real_time_uploader_unittest.cc b/chromium/components/enterprise/browser/reporting/real_time_uploader_unittest.cc
index 063ad6d0a54..a9adceb68cc 100644
--- a/chromium/components/enterprise/browser/reporting/real_time_uploader_unittest.cc
+++ b/chromium/components/enterprise/browser/reporting/real_time_uploader_unittest.cc
@@ -19,6 +19,7 @@
using ::testing::_;
using ::testing::InSequence;
using ::testing::Invoke;
+using ::testing::StrEq;
using ::testing::WithArg;
namespace enterprise_reporting {
@@ -159,7 +160,7 @@ TEST_F(RealTimeUploaderTest, UploadReport) {
InSequence sequence;
EXPECT_CALL(*uploader_->mock_report_queue(),
- AddRecord(base::StringPiece(expected_report_1), kPriority, _))
+ AddRecord(StrEq(expected_report_1), kPriority, _))
.Times(1)
.WillOnce(WithArg<2>(
Invoke([](reporting::ReportQueue::EnqueueCallback callback) {
@@ -169,7 +170,7 @@ TEST_F(RealTimeUploaderTest, UploadReport) {
})));
EXPECT_CALL(*uploader_->mock_report_queue(),
- AddRecord(base::StringPiece(expected_report_2), kPriority, _))
+ AddRecord(StrEq(expected_report_2), kPriority, _))
.Times(1)
.WillOnce(WithArg<2>(
Invoke([](reporting::ReportQueue::EnqueueCallback callback) {
@@ -207,7 +208,7 @@ TEST_F(RealTimeUploaderTest, UploadReportBeforeQueueIsReady) {
EXPECT_CALL(*uploader_->mock_report_queue(),
- AddRecord(base::StringPiece(expected_report), kPriority, _))
+ AddRecord(StrEq(expected_report), kPriority, _))
.Times(1)
.WillOnce(WithArg<2>(
Invoke([](reporting::ReportQueue::EnqueueCallback callback) {
diff --git a/chromium/components/enterprise/browser/reporting/report_uploader_unittest.cc b/chromium/components/enterprise/browser/reporting/report_uploader_unittest.cc
index f526eff546d..9d359bda4f2 100644
--- a/chromium/components/enterprise/browser/reporting/report_uploader_unittest.cc
+++ b/chromium/components/enterprise/browser/reporting/report_uploader_unittest.cc
@@ -12,6 +12,7 @@
#include "build/chromeos_buildflags.h"
#include "components/enterprise/browser/reporting/report_request.h"
#include "components/enterprise/browser/reporting/report_type.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "device_management_backend.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -148,7 +149,7 @@ TEST_F(ReportUploaderTest, PersistentError) {
}
TEST_F(ReportUploaderTest, RequestTooBigError) {
- CreateUploader(/* *retyr_count = */ 2);
+ CreateUploader(/* *retry_count = */ 2);
EXPECT_CALL(client_, UploadReportProxy(_, _))
.Times(2)
.WillOnce(WithArgs<1>(policy::ScheduleStatusCallback(false)))
diff --git a/chromium/components/enterprise/common/proto/connectors.proto b/chromium/components/enterprise/common/proto/connectors.proto
index cb22a2eb6d3..9a39910ae20 100644
--- a/chromium/components/enterprise/common/proto/connectors.proto
+++ b/chromium/components/enterprise/common/proto/connectors.proto
@@ -82,6 +82,8 @@ message ClientMetadata {
};
// Analysis request sent from chrome to backend.
+// The proto here is the source of truth; the copy in the LCAC Github repo
+// should always be in sync with it (https://github.com/chromium/content_analysis_sdk/blob/main/proto/content_analysis/sdk/analysis.proto)
message ContentAnalysisRequest {
// The TokenID for Enterprise-enrolled devices. This identifies a specific
// chrome instance.
@@ -91,16 +93,16 @@ message ContentAnalysisRequest {
// This identifies a specific chrome instance.
optional string fcm_notification_token = 2;
+ // Token used to correlate requests and responses. This is different than the
+ // FCM token in that it is unique for each request.
+ optional string request_token = 5;
+
// Which enterprise connector fired this request.
optional AnalysisConnector analysis_connector = 9;
// Information about the data that triggered the content analysis request.
optional ContentMetaData request_data = 10;
- // Token used to correlate requests and responses. This is different than the
- // FCM token in that it is unique for each request.
- optional string request_token = 5;
-
// The tags configured for the URL that triggered the content analysis.
repeated string tags = 11;
@@ -108,6 +110,21 @@ message ContentAnalysisRequest {
// be reported with device/user identifiable information.
optional ClientMetadata client_metadata = 12;
+ oneof content_data {
+ // The text content to analyze in local content analysis request. This field
+ // is mutually exclusive with file_path.
+ string text_content = 13;
+
+ // The full path to the file to analyze in local content analysis request.
+ // The path is expressed in a platform dependent way. This field is mutually
+ // exclusive with text_content.
+ string file_path = 14;
+ }
+
+ // The absolute deadline (timestamp in UTC Epoch time) that Chrome will wait
+ // until a response from the agent is received.
+ optional int64 expires_at = 15;
+
// Reserved to make sure there is no overlap with DeepScanningClientRequest.
reserved 3, 4, 6 to 8;
}
diff --git a/chromium/components/enterprise/common/proto/connectors_unittest.cc b/chromium/components/enterprise/common/proto/connectors_unittest.cc
new file mode 100644
index 00000000000..05c7f95625c
--- /dev/null
+++ b/chromium/components/enterprise/common/proto/connectors_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright 2022 The Chromium 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 "third_party/content_analysis_sdk/src/proto/content_analysis/sdk/
+
+#include "components/enterprise/common/proto/connectors.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/content_analysis_sdk/src/proto/content_analysis/sdk/analysis.pb.h"
+
+namespace enterprise_connectors {
+
+TEST(EnterpriseConnectorsProtoTest, AnalysisConnectorEnum) {
+ EXPECT_EQ(enterprise_connectors::AnalysisConnector_ARRAYSIZE, 5);
+ EXPECT_EQ(enterprise_connectors::AnalysisConnector_ARRAYSIZE,
+ content_analysis::sdk::AnalysisConnector_ARRAYSIZE);
+
+ EXPECT_EQ((int)enterprise_connectors::ANALYSIS_CONNECTOR_UNSPECIFIED,
+ (int)content_analysis::sdk::ANALYSIS_CONNECTOR_UNSPECIFIED);
+ EXPECT_EQ((int)enterprise_connectors::FILE_DOWNLOADED,
+ (int)content_analysis::sdk::FILE_DOWNLOADED);
+ EXPECT_EQ((int)enterprise_connectors::FILE_ATTACHED,
+ (int)content_analysis::sdk::FILE_ATTACHED);
+ EXPECT_EQ((int)enterprise_connectors::BULK_DATA_ENTRY,
+ (int)content_analysis::sdk::BULK_DATA_ENTRY);
+ EXPECT_EQ((int)enterprise_connectors::PRINT,
+ (int)content_analysis::sdk::PRINT);
+}
+
+using ChromiumResult = enterprise_connectors::ContentAnalysisResponse::Result;
+using SdkResult = content_analysis::sdk::ContentAnalysisResponse::Result;
+
+TEST(EnterpriseConnectorsProtoTest, StatusEnum) {
+ EXPECT_EQ(ChromiumResult::Status_ARRAYSIZE, 3);
+ EXPECT_EQ(ChromiumResult::Status_ARRAYSIZE, SdkResult::Status_ARRAYSIZE);
+
+ EXPECT_EQ((int)ChromiumResult::STATUS_UNKNOWN,
+ (int)SdkResult::STATUS_UNKNOWN);
+ EXPECT_EQ((int)ChromiumResult::SUCCESS, (int)SdkResult::SUCCESS);
+ EXPECT_EQ((int)ChromiumResult::FAILURE, (int)SdkResult::FAILURE);
+}
+
+using ChromiumRule = ChromiumResult::TriggeredRule;
+using SdkRule = SdkResult::TriggeredRule;
+
+TEST(EnterpriseConnectorsProtoTest, TriggeredRuleActionEnum) {
+ EXPECT_EQ(ChromiumRule::Action_ARRAYSIZE, 4);
+ EXPECT_EQ(ChromiumRule::Action_ARRAYSIZE, SdkRule::Action_ARRAYSIZE);
+
+ EXPECT_EQ((int)ChromiumRule::ACTION_UNSPECIFIED,
+ (int)SdkRule::ACTION_UNSPECIFIED);
+ EXPECT_EQ((int)ChromiumRule::REPORT_ONLY, (int)SdkRule::REPORT_ONLY);
+ EXPECT_EQ((int)ChromiumRule::WARN, (int)SdkRule::WARN);
+ EXPECT_EQ((int)ChromiumRule::BLOCK, (int)SdkRule::BLOCK);
+}
+
+} // namespace enterprise_connectors
diff --git a/chromium/components/enterprise/content/clipboard_restriction_service.h b/chromium/components/enterprise/content/clipboard_restriction_service.h
index 113cda93260..8df1cadd248 100644
--- a/chromium/components/enterprise/content/clipboard_restriction_service.h
+++ b/chromium/components/enterprise/content/clipboard_restriction_service.h
@@ -8,6 +8,7 @@
#include <map>
#include <memory>
+#include "base/memory/raw_ptr.h"
#include "base/memory/singleton.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "components/keyed_service/core/keyed_service.h"
@@ -49,9 +50,9 @@ class ClipboardRestrictionService : KeyedService {
void UpdateSettings();
PrefChangeRegistrar pref_change_registrar_;
- PrefService* pref_service_;
+ raw_ptr<PrefService> pref_service_;
- url_matcher::URLMatcherConditionSet::ID next_id_;
+ base::MatcherStringPattern::ID next_id_;
std::unique_ptr<url_matcher::URLMatcher> enable_url_matcher_;
std::unique_ptr<url_matcher::URLMatcher> disable_url_matcher_;
diff --git a/chromium/components/enterprise/content/clipboard_restriction_service_unittest.cc b/chromium/components/enterprise/content/clipboard_restriction_service_unittest.cc
index fd354c1cf0f..e4852ea89aa 100644
--- a/chromium/components/enterprise/content/clipboard_restriction_service_unittest.cc
+++ b/chromium/components/enterprise/content/clipboard_restriction_service_unittest.cc
@@ -25,28 +25,28 @@ class ClipboardRestrictionServiceTest : public testing::Test {
void SetPolicy(absl::optional<std::vector<std::string>> enable_patterns,
absl::optional<std::vector<std::string>> disable_patterns,
int min_data_size = 100) {
- base::Value::DictStorage pref_storage;
+ base::Value::Dict pref_dict;
if (enable_patterns) {
- base::Value::ListStorage enable_storage;
- for (const auto& p : *enable_patterns) {
- enable_storage.push_back(base::Value(p));
+ base::Value::List enable_list;
+ for (auto& p : *enable_patterns) {
+ enable_list.Append(std::move(p));
}
- pref_storage["enable"] = base::Value(enable_storage);
+ pref_dict.Set("enable", std::move(enable_list));
}
if (disable_patterns) {
- base::Value::ListStorage disable_storage;
- for (const auto& p : *disable_patterns) {
- disable_storage.push_back(base::Value(p));
+ base::Value::List disable_list;
+ for (auto& p : *disable_patterns) {
+ disable_list.Append(std::move(p));
}
- pref_storage["disable"] = base::Value(disable_storage);
+ pref_dict.Set("disable", std::move(disable_list));
}
- pref_storage["minimum_data_size"] = base::Value(min_data_size);
+ pref_dict.Set("minimum_data_size", min_data_size);
pref_service_.SetManagedPref(enterprise::content::kCopyPreventionSettings,
- std::make_unique<base::Value>(pref_storage));
+ base::Value(std::move(pref_dict)));
}
void CreateService() {
diff --git a/chromium/components/enterprise/content/copy_prevention_settings_policy_handler.cc b/chromium/components/enterprise/content/copy_prevention_settings_policy_handler.cc
index ff184f03b43..138bb56c9de 100644
--- a/chromium/components/enterprise/content/copy_prevention_settings_policy_handler.cc
+++ b/chromium/components/enterprise/content/copy_prevention_settings_policy_handler.cc
@@ -44,11 +44,11 @@ bool CopyPreventionSettingsPolicyHandler::CheckPolicySettings(
return false;
}
- const base::Value* value =
- policies.GetValue(policy_name(), base::Value::Type::DICT);
- const base::Value* enable = value->FindListKey(
+ const base::Value::Dict& dict =
+ policies.GetValue(policy_name(), base::Value::Type::DICT)->GetDict();
+ const base::Value::List* enable = dict.FindList(
enterprise::content::kCopyPreventionSettingsEnableFieldName);
- const base::Value* disable = value->FindListKey(
+ const base::Value::List* disable = dict.FindList(
enterprise::content::kCopyPreventionSettingsDisableFieldName);
if (!enable || !disable) {
errors->AddError(policy_name(),
@@ -56,7 +56,7 @@ bool CopyPreventionSettingsPolicyHandler::CheckPolicySettings(
return false;
}
- for (auto& pattern : disable->GetListDeprecated()) {
+ for (auto& pattern : *disable) {
if (pattern.GetString() == "*") {
errors->AddError(
policy_name(),
diff --git a/chromium/components/error_page/common/localized_error.cc b/chromium/components/error_page/common/localized_error.cc
index e54f585bd7c..68760547192 100644
--- a/chromium/components/error_page/common/localized_error.cc
+++ b/chromium/components/error_page/common/localized_error.cc
@@ -16,6 +16,7 @@
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/notreached.h"
+#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -30,7 +31,6 @@
#include "components/strings/grit/components_chromium_strings.h"
#include "components/strings/grit/components_strings.h"
#include "components/url_formatter/url_formatter.h"
-#include "net/base/escape.h"
#include "net/base/net_errors.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
@@ -968,7 +968,7 @@ LocalizedError::PageState LocalizedError::GetPageState(
std::u16string failed_url_string(url_formatter::FormatUrl(
failed_url, url_formatter::kFormatUrlOmitNothing,
- net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr));
+ base::UnescapeRule::NORMAL, nullptr, nullptr, nullptr));
// URLs are always LTR.
if (base::i18n::IsRTL())
base::i18n::WrapStringWithLTRFormatting(&failed_url_string);
@@ -979,8 +979,9 @@ LocalizedError::PageState LocalizedError::GetPageState(
} else {
result.strings.SetStringPath("title", failed_url_string);
- // If we do not have a meaningful hostname to display, show the scheme.
- if (host_name.empty() && !failed_url.IsStandard()) {
+ // If the page is blocked by policy, and no hostname is available to show,
+ // instead show the scheme.
+ if (error_code == net::ERR_BLOCKED_BY_ADMINISTRATOR && host_name.empty()) {
options.heading_resource_id = IDS_ERRORPAGES_HEADING_BLOCKED_SCHEME;
host_name = base::UTF8ToUTF16(failed_url.scheme());
}
@@ -1155,7 +1156,7 @@ LocalizedError::PageState LocalizedError::GetPageStateForOverriddenErrorPage(
} else {
std::u16string failed_url_string(url_formatter::FormatUrl(
failed_url, url_formatter::kFormatUrlOmitNothing,
- net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr));
+ base::UnescapeRule::NORMAL, nullptr, nullptr, nullptr));
// URLs are always LTR.
if (base::i18n::IsRTL())
base::i18n::WrapStringWithLTRFormatting(&failed_url_string);
diff --git a/chromium/components/error_page/content/browser/net_error_auto_reloader.cc b/chromium/components/error_page/content/browser/net_error_auto_reloader.cc
index 872daad5373..51892722150 100644
--- a/chromium/components/error_page/content/browser/net_error_auto_reloader.cc
+++ b/chromium/components/error_page/content/browser/net_error_auto_reloader.cc
@@ -291,7 +291,7 @@ void NetErrorAutoReloader::ReloadMainFrame() {
++num_reloads_for_current_error_;
is_auto_reload_in_progress_ = true;
- web_contents()->GetMainFrame()->Reload();
+ web_contents()->GetPrimaryMainFrame()->Reload();
}
std::unique_ptr<content::NavigationThrottle>
diff --git a/chromium/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc b/chromium/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
index 5951a9cc920..081edb6d607 100644
--- a/chromium/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
+++ b/chromium/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
@@ -562,7 +562,7 @@ IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderFencedFrameBrowserTest,
const GURL fenced_frame_url = embedded_test_server()->GetURL("/title2.html");
content::RenderFrameHost* fenced_frame_host =
fenced_frame_test_helper().CreateFencedFrame(
- shell()->web_contents()->GetMainFrame(), fenced_frame_url,
+ shell()->web_contents()->GetPrimaryMainFrame(), fenced_frame_url,
net::ERR_BLOCKED_BY_RESPONSE);
// The fenced frame navigation failed since it doesn't have the
diff --git a/chromium/components/exo/BUILD.gn b/chromium/components/exo/BUILD.gn
index aa024724f90..e7bf750edbf 100644
--- a/chromium/components/exo/BUILD.gn
+++ b/chromium/components/exo/BUILD.gn
@@ -30,6 +30,8 @@ static_library("exo") {
"buffer.h",
"capabilities.cc",
"capabilities.h",
+ "common_utils.cc",
+ "common_utils.h",
"data_device.cc",
"data_device.h",
"data_device_delegate.h",
@@ -147,7 +149,9 @@ static_library("exo") {
"//ash/keyboard/ui",
"//ash/public/cpp",
"//ash/resources/vector_icons",
- "//chromeos/crosapi/cpp",
+ "//chromeos/crosapi/cpp:crosapi_constants",
+ "//chromeos/dbus/power",
+ "//chromeos/dbus/power:power_manager_proto",
"//chromeos/ui/base",
"//chromeos/ui/frame",
"//components/app_restore",
@@ -209,14 +213,6 @@ static_library("exo") {
configs += [ ":xkbcommon" ]
}
}
-
- if (is_chromecast) {
- deps += [ "//ui/accessibility:accessibility" ]
- sources += [
- "fullscreen_shell_surface.cc",
- "fullscreen_shell_surface.h",
- ]
- }
}
source_set("test_support") {
@@ -349,6 +345,8 @@ source_set("unit_tests") {
"//ash/public/cpp",
"//ash/public/cpp/external_arc",
"//build:chromeos_buildflags",
+ "//chromeos/dbus/power",
+ "//chromeos/dbus/power:power_manager_proto",
"//chromeos/ui/base",
"//chromeos/ui/frame",
"//components/fullscreen_control",
@@ -360,11 +358,6 @@ source_set("unit_tests") {
"//ui/gfx/geometry",
]
}
-
- if (is_chromecast) {
- deps += [ "//ui/accessibility:accessibility" ]
- sources += [ "fullscreen_shell_surface_unittest.cc" ]
- }
}
test("exo_unittests") {
diff --git a/chromium/components/exo/DEPS b/chromium/components/exo/DEPS
index a52a362fcc8..4360d1ab768 100644
--- a/chromium/components/exo/DEPS
+++ b/chromium/components/exo/DEPS
@@ -32,4 +32,12 @@ specific_include_rules = {
"+components/viz/service/frame_sinks",
"+components/viz/service/surfaces",
],
+ "ui_lock_controller_unittest\.cc": [
+ "+chromeos/dbus/power",
+ "+chromeos/dbus/power_manager",
+ ],
+ "wm_helper_chromeos\..*": [
+ "+chromeos/dbus/power",
+ "+chromeos/dbus/power_manager",
+ ]
}
diff --git a/chromium/components/exo/buffer.cc b/chromium/components/exo/buffer.cc
index cc99addf55d..3a52741ba3d 100644
--- a/chromium/components/exo/buffer.cc
+++ b/chromium/components/exo/buffer.cc
@@ -138,13 +138,16 @@ Buffer::Texture::Texture(
texture_target_(GL_TEXTURE_2D),
query_type_(GL_COMMANDS_COMPLETED_CHROMIUM) {
gpu::SharedImageInterface* sii = context_provider_->SharedImageInterface();
- const uint32_t usage =
- gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY;
- mailbox_ = sii->CreateSharedImage(viz::ResourceFormat::RGBA_8888, size,
- gfx::ColorSpace(), kTopLeft_GrSurfaceOrigin,
- kPremul_SkAlphaType, usage,
- gpu::kNullSurfaceHandle);
+ // Add GLES2 usage as it is used by RasterImplementationGLES.
+ const uint32_t usage = gpu::SHARED_IMAGE_USAGE_RASTER |
+ gpu::SHARED_IMAGE_USAGE_DISPLAY |
+ gpu::SHARED_IMAGE_USAGE_GLES2;
+
+ mailbox_ = sii->CreateSharedImage(
+ viz::ResourceFormat::RGBA_8888, size, gfx::ColorSpace::CreateSRGB(),
+ kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
+ gpu::kNullSurfaceHandle);
DCHECK(!mailbox_.IsZero());
gpu::raster::RasterInterface* ri = context_provider_->RasterInterface();
ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
@@ -168,14 +171,18 @@ Buffer::Texture::Texture(
query_type_(query_type),
wait_for_release_delay_(wait_for_release_delay) {
gpu::SharedImageInterface* sii = context_provider_->SharedImageInterface();
- uint32_t usage =
- gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY;
+
+ // Add GLES2 usage as it is used by RasterImplementationGLES.
+ uint32_t usage = gpu::SHARED_IMAGE_USAGE_RASTER |
+ gpu::SHARED_IMAGE_USAGE_DISPLAY |
+ gpu::SHARED_IMAGE_USAGE_GLES2;
if (is_overlay_candidate) {
usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
}
mailbox_ = sii->CreateSharedImage(
- gpu_memory_buffer_, gpu_memory_buffer_manager, gfx::ColorSpace(),
- kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage);
+ gpu_memory_buffer_, gpu_memory_buffer_manager,
+ gfx::ColorSpace::CreateSRGB(), kTopLeft_GrSurfaceOrigin,
+ kPremul_SkAlphaType, usage);
DCHECK(!mailbox_.IsZero());
gpu::raster::RasterInterface* ri = context_provider_->RasterInterface();
ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
@@ -645,6 +652,19 @@ void Buffer::MaybeRunPerCommitRelease(
std::move(buffer_release_callback).Run();
} else {
#if BUILDFLAG(IS_POSIX)
+ // Watching the release fence's fd results in a context switch to the I/O
+ // thread. That may steal thread time from other applications, which can
+ // do something useful during that time. Moreover, most of the time the
+ // fence can have already been signalled. Thus, only watch the fence is
+ // readable iff it hasn't been signalled yet.
+ base::TimeTicks ticks;
+ auto status = gfx::GpuFence::GetStatusChangeTime(
+ release_fence.owned_fd.get(), &ticks);
+ if (status == gfx::GpuFence::kSignaled) {
+ std::move(buffer_release_callback).Run();
+ return;
+ }
+
auto controller = base::FileDescriptorWatcher::WatchReadable(
release_fence.owned_fd.get(),
base::BindRepeating(&Buffer::FenceSignalled, AsWeakPtr(), commit_id));
diff --git a/chromium/components/exo/client_controlled_shell_surface.cc b/chromium/components/exo/client_controlled_shell_surface.cc
index d9643be6ec9..97d7e9d52ef 100644
--- a/chromium/components/exo/client_controlled_shell_surface.cc
+++ b/chromium/components/exo/client_controlled_shell_surface.cc
@@ -166,17 +166,13 @@ class ClientControlledWindowStateDelegate : public ash::WindowStateDelegate {
switch (window_state->GetStateType()) {
case chromeos::WindowStateType::kDefault:
case chromeos::WindowStateType::kNormal:
- window->SetProperty(aura::client::kPreFullscreenShowStateKey,
- ui::SHOW_STATE_NORMAL);
next_state = chromeos::WindowStateType::kFullscreen;
break;
case chromeos::WindowStateType::kMaximized:
- window->SetProperty(aura::client::kPreFullscreenShowStateKey,
- ui::SHOW_STATE_MAXIMIZED);
next_state = chromeos::WindowStateType::kFullscreen;
break;
case chromeos::WindowStateType::kFullscreen:
- switch (window->GetProperty(aura::client::kPreFullscreenShowStateKey)) {
+ switch (window->GetProperty(aura::client::kRestoreShowStateKey)) {
case ui::SHOW_STATE_DEFAULT:
case ui::SHOW_STATE_NORMAL:
next_state = chromeos::WindowStateType::kNormal;
@@ -192,17 +188,11 @@ class ClientControlledWindowStateDelegate : public ash::WindowStateDelegate {
case ui::SHOW_STATE_END:
NOTREACHED() << " unknown state :"
<< window->GetProperty(
- aura::client::kPreFullscreenShowStateKey);
+ aura::client::kRestoreShowStateKey);
return false;
}
break;
case chromeos::WindowStateType::kMinimized: {
- ui::WindowShowState pre_full_state =
- window->GetProperty(aura::client::kPreMinimizedShowStateKey);
- if (pre_full_state != ui::SHOW_STATE_FULLSCREEN) {
- window->SetProperty(aura::client::kPreFullscreenShowStateKey,
- pre_full_state);
- }
next_state = chromeos::WindowStateType::kFullscreen;
break;
}
@@ -378,8 +368,21 @@ void ClientControlledShellSurface::SetBounds(int64_t display_id,
return;
}
+ // When use_default_scale_cancellation_ is false, the client is scale-aware
+ // and we expect that the |bounds| has been calculated by the client based
+ // on the device_scale_factor of the display with |display_id|.
+ // If the display has been changed before |SetBounds()| is called, for some
+ // cases(eg. move ARC window between displays with shortcut), |pending_scale_|
+ // may be stale and tied the old pending_display. Therefore, we re-initialize
+ // it to 0.0 here to force an update on the value in |EnsurePendingScale()|.
+ // Also need to note that we only want to commit |pending_scale_| in the cases
+ // where it hasn't been initialized before this method call.
+ bool const commit_immediately = pending_scale_ == 0.0;
+ if (!use_default_scale_cancellation_ && display_id != pending_display_id_)
+ pending_scale_ = 0.0;
+
SetDisplay(display_id);
- EnsurePendingScale();
+ EnsurePendingScale(commit_immediately);
const gfx::Rect bounds_dp =
gfx::ScaleToRoundedRect(bounds, GetClientToDpPendingScale());
@@ -390,7 +393,7 @@ void ClientControlledShellSurface::SetBoundsOrigin(const gfx::Point& origin) {
TRACE_EVENT1("exo", "ClientControlledShellSurface::SetBoundsOrigin", "origin",
origin.ToString());
- EnsurePendingScale();
+ EnsurePendingScale(/*commit_immediately=*/true);
const gfx::Point origin_dp =
gfx::ScaleToRoundedPoint(origin, GetClientToDpPendingScale());
pending_geometry_.set_origin(origin_dp);
@@ -405,7 +408,7 @@ void ClientControlledShellSurface::SetBoundsSize(const gfx::Size& size) {
return;
}
- EnsurePendingScale();
+ EnsurePendingScale(/*commit_immediately=*/true);
const gfx::Size size_dp =
gfx::ScaleToRoundedSize(size, GetClientToDpPendingScale());
pending_geometry_.set_size(size_dp);
@@ -839,6 +842,10 @@ ClientControlledShellSurface::CreateNonClientFrameView(views::Widget* widget) {
return frame_view;
}
+bool ClientControlledShellSurface::ShouldSaveWindowPlacement() const {
+ return false;
+}
+
void ClientControlledShellSurface::SaveWindowPlacement(
const gfx::Rect& bounds,
ui::WindowShowState show_state) {}
@@ -945,6 +952,22 @@ void ClientControlledShellSurface::CompositorLockTimedOut() {
////////////////////////////////////////////////////////////////////////////////
// ShellSurfaceBase overrides:
+void ClientControlledShellSurface::SetSystemModal(bool system_modal) {
+ // System modal container is used by clients to implement client side
+ // managed system modal dialogs using a single ShellSurface instance.
+ // Hit-test region will be non-empty when at least one dialog exists on
+ // the client side. Here we detect the transition between no client side
+ // dialog and at least one dialog so activatable state is properly
+ // updated.
+ if (container_ != ash::kShellWindowId_SystemModalContainer) {
+ LOG(ERROR)
+ << "Only a window in SystemModalContainer can change the modality";
+ return;
+ }
+
+ ShellSurfaceBase::SetSystemModal(system_modal);
+}
+
void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds) {
const auto* screen = display::Screen::GetScreen();
aura::Window* window = widget_->GetNativeWindow();
@@ -1432,16 +1455,17 @@ const ash::NonClientFrameViewAsh* ClientControlledShellSurface::GetFrameView()
widget_->non_client_view()->frame_view());
}
-void ClientControlledShellSurface::EnsurePendingScale() {
+void ClientControlledShellSurface::EnsurePendingScale(bool commit_immediately) {
// Handle the case where we receive bounds from the client before the initial
- // scale has been set.
+ // scale has been set or |pending_scale_| is stale due to change of displays.
if (pending_scale_ == 0.0) {
DCHECK(!use_default_scale_cancellation_);
display::Display display;
if (display::Screen::GetScreen()->GetDisplayWithDisplayId(
pending_display_id_, &display)) {
SetScale(display.device_scale_factor());
- CommitPendingScale();
+ if (commit_immediately)
+ CommitPendingScale();
}
}
}
diff --git a/chromium/components/exo/client_controlled_shell_surface.h b/chromium/components/exo/client_controlled_shell_surface.h
index 508df8078e2..b3c407868d6 100644
--- a/chromium/components/exo/client_controlled_shell_surface.h
+++ b/chromium/components/exo/client_controlled_shell_surface.h
@@ -195,6 +195,7 @@ class ClientControlledShellSurface : public ShellSurfaceBase,
bool CanMaximize() const override;
std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
views::Widget* widget) override;
+ bool ShouldSaveWindowPlacement() const override;
void SaveWindowPlacement(const gfx::Rect& bounds,
ui::WindowShowState show_state) override;
bool GetSavedWindowPlacement(const views::Widget* widget,
@@ -238,6 +239,9 @@ class ClientControlledShellSurface : public ShellSurfaceBase,
// Update the resizability based on the resize lock type.
void UpdateResizability() override;
+ // Overridden from exo::ShellSurfaceBase
+ void SetSystemModal(bool system_modal) override;
+
protected:
// Overridden from ShellSurfaceBase:
float GetScale() const override;
@@ -286,7 +290,7 @@ class ClientControlledShellSurface : public ShellSurfaceBase,
ash::NonClientFrameViewAsh* GetFrameView();
const ash::NonClientFrameViewAsh* GetFrameView() const;
- void EnsurePendingScale();
+ void EnsurePendingScale(bool commit_immediately);
float GetClientToDpPendingScale() const;
gfx::Rect GetClientBoundsForWindowBoundsAndWindowState(
diff --git a/chromium/components/exo/client_controlled_shell_surface_unittest.cc b/chromium/components/exo/client_controlled_shell_surface_unittest.cc
index ce998d12a5f..644a1c1ca1e 100644
--- a/chromium/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/chromium/components/exo/client_controlled_shell_surface_unittest.cc
@@ -29,6 +29,7 @@
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
+#include "ash/wm/work_area_insets.h"
#include "ash/wm/workspace_controller_test_api.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
@@ -66,9 +67,11 @@
#include "ui/compositor_extra/shadow.h"
#include "ui/display/display.h"
#include "ui/display/test/display_manager_test_api.h"
+#include "ui/display/util/display_util.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_targeter.h"
#include "ui/events/test/event_generator.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/views/paint_info.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/shadow_controller.h"
@@ -301,6 +304,25 @@ TEST_F(ClientControlledShellSurfaceTest,
EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
}
+TEST_F(ClientControlledShellSurfaceTest,
+ NonSystemModalContainerCantChangeModality) {
+ std::unique_ptr<Surface> surface(new Surface);
+ auto shell_surface = exo_test_helper()->CreateClientControlledShellSurface(
+ surface.get(), /*is_modal=*/false);
+ gfx::Size desktop_size(640, 480);
+ std::unique_ptr<Buffer> desktop_buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(desktop_size)));
+ surface->Attach(desktop_buffer.get());
+ surface->SetInputRegion(cc::Region());
+
+ shell_surface->SetSystemModal(true);
+ surface->Commit();
+
+ // It is expected that a non system modal container is unable to set a system
+ // modal.
+ EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
+}
+
TEST_F(ClientControlledShellSurfaceTest, SurfaceShadow) {
gfx::Size buffer_size(128, 128);
std::unique_ptr<Buffer> buffer(
@@ -944,7 +966,7 @@ TEST_F(ClientControlledShellSurfaceTest,
display::Display::SetForceDeviceScaleFactor(scale);
int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
- display::Display::SetInternalDisplayId(display_id);
+ display::SetInternalDisplayIds({display_id});
gfx::Size buffer_size(64, 64);
std::unique_ptr<Buffer> buffer(
@@ -966,7 +988,7 @@ TEST_F(ClientControlledShellSurfaceTest,
TEST_F(ClientControlledShellSurfaceTest,
DefaultDeviceScaleFactorFromDisplayManager) {
int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
- display::Display::SetInternalDisplayId(display_id);
+ display::SetInternalDisplayIds({display_id});
gfx::Size size(1920, 1080);
display::DisplayManager* display_manager =
@@ -1995,21 +2017,26 @@ TEST_F(ClientControlledShellSurfaceTest, SetBoundsReparentsToDisplay) {
// is enabled or disabled.
TEST_F(ClientControlledShellSurfaceTest,
SetBoundsWithAndWithoutDefaultScaleCancellation) {
- UpdateDisplay("800x600*2");
+ UpdateDisplay("800x600*2,800x600*2");
const auto primary_display_id =
display::Screen::GetScreen()->GetPrimaryDisplay().id();
+ const auto secondary_display_id =
+ display::Screen::GetScreen()->GetAllDisplays().back().id();
const gfx::Size buffer_size(64, 64);
std::unique_ptr<Buffer> buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
- const gfx::Rect bounds(64, 64, 128, 128);
- const gfx::Rect bounds_dp = gfx::ScaleToRoundedRect(bounds, 1.f / 2.f);
+ constexpr double kOriginalScale = 4.f;
+ const gfx::Rect bounds_dp(64, 64, 128, 128);
+ const gfx::Rect bounds_px_for_2x = gfx::ScaleToRoundedRect(bounds_dp, 2.f);
+ const gfx::Rect bounds_px_for_4x =
+ gfx::ScaleToRoundedRect(bounds_dp, kOriginalScale);
for (const auto default_scale_cancellation : {true, false}) {
- const auto bounds_to_set = default_scale_cancellation ? bounds_dp : bounds;
-
+ SCOPED_TRACE(::testing::Message() << "default_scale_cancellation: "
+ << default_scale_cancellation);
{
// Set display id, bounds origin, bounds size at the same time via
// SetBounds method.
@@ -2017,20 +2044,40 @@ TEST_F(ClientControlledShellSurfaceTest,
auto shell_surface(exo_test_helper()->CreateClientControlledShellSurface(
surface.get(), /*is_modal=*/false, default_scale_cancellation));
- shell_surface->SetBounds(primary_display_id, bounds_to_set);
+ // When display doesn't change, scale stays the same
+ shell_surface->SetScale(kOriginalScale);
+ shell_surface->SetDisplay(primary_display_id);
+ shell_surface->SetBounds(primary_display_id, default_scale_cancellation
+ ? bounds_dp
+ : bounds_px_for_4x);
surface->Attach(buffer.get());
surface->Commit();
EXPECT_EQ(bounds_dp,
shell_surface->GetWidget()->GetWindowBoundsInScreen());
- }
+ // When display changes, scale gets updated by the display dsf
+ shell_surface->SetScale(kOriginalScale);
+ shell_surface->SetBounds(secondary_display_id, default_scale_cancellation
+ ? bounds_dp
+ : bounds_px_for_2x);
+ surface->Attach(buffer.get());
+ surface->Commit();
+
+ EXPECT_EQ(bounds_dp.width(),
+ shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
+ EXPECT_EQ(bounds_dp.height(),
+ shell_surface->GetWidget()->GetWindowBoundsInScreen().height());
+ }
{
// Set display id, bounds origin, bounds size separately.
+ const auto bounds_to_set =
+ default_scale_cancellation ? bounds_dp : bounds_px_for_4x;
std::unique_ptr<Surface> surface(new Surface);
auto shell_surface(exo_test_helper()->CreateClientControlledShellSurface(
surface.get(), /*is_modal=*/false, default_scale_cancellation));
+ shell_surface->SetScale(kOriginalScale);
shell_surface->SetDisplay(primary_display_id);
shell_surface->SetBoundsOrigin(bounds_to_set.origin());
shell_surface->SetBoundsSize(bounds_to_set.size());
@@ -2750,9 +2797,9 @@ TEST_F(ClientControlledShellSurfaceTest, SnappedClientBounds) {
// Clear insets so that it won't affects the bounds.
shell_surface->SetSystemUiVisibility(true);
- int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
- ash::Shell::Get()->display_manager()->UpdateWorkAreaOfDisplay(display_id,
- gfx::Insets());
+ aura::Window* root = ash::Shell::GetPrimaryRootWindow();
+ ash::WorkAreaInsets::ForWindow(root)->UpdateWorkAreaInsetsForTest(
+ root, gfx::Rect(), gfx::Insets(), gfx::Insets());
auto* delegate =
TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
diff --git a/chromium/components/exo/common_utils.cc b/chromium/components/exo/common_utils.cc
new file mode 100644
index 00000000000..48dcfc7b574
--- /dev/null
+++ b/chromium/components/exo/common_utils.cc
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium 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/exo/common_utils.h"
+
+#include "base/logging.h"
+
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
+namespace exo {
+
+bool IsDrmAtomicAvailable() {
+#if defined(USE_OZONE)
+ auto& host_properties =
+ ui::OzonePlatform::GetInstance()->GetPlatformRuntimeProperties();
+ return host_properties.supports_overlays;
+#else
+ LOG(WARNING) << "Ozone disabled, cannot determine whether DrmAtomic is "
+ "present. Assuming it is not";
+ return false;
+#endif
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/common_utils.h b/chromium/components/exo/common_utils.h
new file mode 100644
index 00000000000..175b69b7dc1
--- /dev/null
+++ b/chromium/components/exo/common_utils.h
@@ -0,0 +1,14 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_EXO_COMMON_UTILS_H_
+#define COMPONENTS_EXO_COMMON_UTILS_H_
+
+namespace exo {
+
+bool IsDrmAtomicAvailable();
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_COMMON_UTILS_H_
diff --git a/chromium/components/exo/extended_drag_source.cc b/chromium/components/exo/extended_drag_source.cc
index c0d9ce4a9a7..72c8bc2bd62 100644
--- a/chromium/components/exo/extended_drag_source.cc
+++ b/chromium/components/exo/extended_drag_source.cc
@@ -20,6 +20,7 @@
#include "components/exo/wm_helper.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
@@ -166,6 +167,9 @@ ExtendedDragSource::~ExtendedDragSource() {
if (source_)
source_->RemoveObserver(this);
+ if (drag_source_window_)
+ drag_source_window_->RemoveObserver(this);
+
DCHECK_EQ(instance_, this);
instance_ = nullptr;
}
@@ -216,10 +220,12 @@ void ExtendedDragSource::OnToplevelWindowDragStarted(
drag_event_source_ = source;
drag_source_window_ =
drag_source_window ? drag_source_window->GetToplevelWindow() : nullptr;
+ if (drag_source_window_)
+ drag_source_window_->AddObserver(this);
MaybeLockCursor();
if (dragged_window_holder_ && dragged_window_holder_->toplevel_window())
- StartDrag(dragged_window_holder_->toplevel_window(), start_location);
+ StartDrag(dragged_window_holder_->toplevel_window());
}
DragOperation ExtendedDragSource::OnToplevelWindowDragDropped() {
@@ -239,7 +245,9 @@ void ExtendedDragSource::OnToplevelWindowDragCancelled() {
void ExtendedDragSource::OnToplevelWindowDragEvent(ui::LocatedEvent* event) {
DCHECK(event);
+ aura::Window* target = static_cast<aura::Window*>(event->target());
pointer_location_ = event->root_location_f();
+ wm::ConvertPointToScreen(target->GetRootWindow(), &pointer_location_);
if (!dragged_window_holder_)
return;
@@ -264,6 +272,11 @@ void ExtendedDragSource::OnDataSourceDestroying(DataSource* source) {
source_ = nullptr;
}
+void ExtendedDragSource::OnWindowDestroyed(aura::Window* window) {
+ if (drag_source_window_ == window)
+ drag_source_window_ = nullptr;
+}
+
void ExtendedDragSource::MaybeLockCursor() {
if (delegate_->ShouldLockCursor()) {
ash::Shell::Get()->cursor_manager()->LockCursor();
@@ -278,13 +291,12 @@ void ExtendedDragSource::UnlockCursor() {
}
}
-void ExtendedDragSource::StartDrag(aura::Window* toplevel,
- const gfx::PointF& pointer_location) {
+void ExtendedDragSource::StartDrag(aura::Window* toplevel) {
// Ensure |toplevel| window does skip events while it's being dragged.
event_blocker_ =
std::make_unique<aura::ScopedWindowEventTargetingBlocker>(toplevel);
- DVLOG(1) << "Starting drag. pointer_loc=" << pointer_location.ToString();
+ DVLOG(1) << "Starting drag. pointer_loc=" << pointer_location_.ToString();
auto* toplevel_handler = ash::Shell::Get()->toplevel_window_event_handler();
auto move_source = drag_event_source_ == ui::mojom::DragEventSource::kTouch
? ::wm::WINDOW_MOVE_SOURCE_TOUCH
@@ -298,15 +310,21 @@ void ExtendedDragSource::StartDrag(aura::Window* toplevel,
toplevel->ClearProperty(ash::kIsDraggingTabsKey);
toplevel->ClearProperty(ash::kTabDraggingSourceWindowKey);
}
- if (extended_drag_source)
+ if (extended_drag_source) {
extended_drag_source->dragged_window_holder_.reset();
+ extended_drag_source->event_blocker_.reset();
+ }
},
base::Unretained(toplevel), weak_factory_.GetWeakPtr());
// TODO(crbug.com/1167581): Experiment setting |update_gesture_target| back
// to true when capture is removed from drag and drop.
+
+ gfx::PointF pointer_location_in_parent(pointer_location_);
+ wm::ConvertPointFromScreen(toplevel->parent(), &pointer_location_in_parent);
+
toplevel_handler->AttemptToStartDrag(
- toplevel, pointer_location, HTCAPTION, move_source,
+ toplevel, pointer_location_in_parent, HTCAPTION, move_source,
std::move(end_closure),
/*update_gesture_target=*/false,
/*grab_capture =*/
@@ -324,10 +342,8 @@ void ExtendedDragSource::OnDraggedWindowVisibilityChanging(bool visible) {
aura::Window* toplevel = dragged_window_holder_->toplevel_window();
DCHECK(toplevel);
-
- DCHECK(drag_source_window_);
toplevel->SetProperty(ash::kIsDraggingTabsKey, true);
- if (drag_source_window_ != toplevel) {
+ if (drag_source_window_ && drag_source_window_ != toplevel) {
toplevel->SetProperty(ash::kTabDraggingSourceWindowKey,
drag_source_window_);
}
@@ -344,16 +360,18 @@ void ExtendedDragSource::OnDraggedWindowVisibilityChanged(bool visible) {
aura::Window* toplevel = dragged_window_holder_->toplevel_window();
DCHECK(toplevel);
- DCHECK(drag_source_window_);
// The |toplevel| window for the dragged surface has just been created and
// it's about to be mapped. Calculate and set its position based on
// |drag_offset_| and |pointer_location_| before starting the actual drag.
- auto screen_location = CalculateOrigin(toplevel);
+ auto screen_location =
+ gfx::ToFlooredPoint(pointer_location_ - dragged_window_holder_->offset());
auto toplevel_bounds =
gfx::Rect({screen_location, toplevel->bounds().size()});
- toplevel->SetBounds(toplevel_bounds);
+ auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
+ drag_source_window_ ? drag_source_window_ : toplevel);
+ toplevel->SetBoundsInScreen(toplevel_bounds, display);
if (WMHelper::GetInstance()->InTabletMode()) {
// The bounds that is stored in ash::kRestoreBoundsOverrideKey will be used
@@ -366,16 +384,7 @@ void ExtendedDragSource::OnDraggedWindowVisibilityChanged(bool visible) {
DVLOG(1) << "Dragged window mapped. toplevel=" << toplevel
<< " origin=" << screen_location.ToString();
- gfx::PointF pointer_location(screen_location +
- dragged_window_holder_->offset());
- StartDrag(toplevel, pointer_location);
-}
-
-gfx::Point ExtendedDragSource::CalculateOrigin(aura::Window* target) const {
- DCHECK(dragged_window_holder_);
- gfx::Point screen_location = gfx::ToRoundedPoint(pointer_location_);
- wm::ConvertPointToScreen(target->GetRootWindow(), &screen_location);
- return screen_location - dragged_window_holder_->offset();
+ StartDrag(toplevel);
}
void ExtendedDragSource::Cleanup() {
@@ -384,6 +393,8 @@ void ExtendedDragSource::Cleanup() {
aura::client::kAnimationsDisabledKey);
}
event_blocker_.reset();
+ if (drag_source_window_)
+ drag_source_window_->RemoveObserver(this);
drag_source_window_ = nullptr;
UnlockCursor();
}
@@ -400,4 +411,8 @@ absl::optional<gfx::Vector2d> ExtendedDragSource::GetDragOffsetForTesting()
: absl::nullopt;
}
+aura::Window* ExtendedDragSource::GetDragSourceWindowForTesting() {
+ return drag_source_window_;
+}
+
} // namespace exo
diff --git a/chromium/components/exo/extended_drag_source.h b/chromium/components/exo/extended_drag_source.h
index e3a19002047..3ce4f3aa13b 100644
--- a/chromium/components/exo/extended_drag_source.h
+++ b/chromium/components/exo/extended_drag_source.h
@@ -36,6 +36,7 @@ class DataSource;
class Surface;
class ExtendedDragSource : public DataSourceObserver,
+ public aura::WindowObserver,
public ash::ToplevelWindowDragDelegate {
public:
class Delegate {
@@ -84,19 +85,21 @@ class ExtendedDragSource : public DataSourceObserver,
// DataSourceObserver:
void OnDataSourceDestroying(DataSource* source) override;
+ // aura::WindowObserver:
+ void OnWindowDestroyed(aura::Window* window) override;
+
aura::Window* GetDraggedWindowForTesting();
absl::optional<gfx::Vector2d> GetDragOffsetForTesting() const;
+ aura::Window* GetDragSourceWindowForTesting();
private:
class DraggedWindowHolder;
void MaybeLockCursor();
void UnlockCursor();
- void StartDrag(aura::Window* toplevel,
- const gfx::PointF& pointer_location_in_screen);
+ void StartDrag(aura::Window* toplevel);
void OnDraggedWindowVisibilityChanging(bool visible);
void OnDraggedWindowVisibilityChanged(bool visible);
- gfx::Point CalculateOrigin(aura::Window* target) const;
void Cleanup();
static ExtendedDragSource* instance_;
@@ -107,6 +110,7 @@ class ExtendedDragSource : public DataSourceObserver,
// tied to the zcr_extended_drag_source_v1 object it's attached to.
Delegate* const delegate_;
+ // The pointer location in screen coordinates.
gfx::PointF pointer_location_;
ui::mojom::DragEventSource drag_event_source_;
bool cursor_locked_ = false;
diff --git a/chromium/components/exo/extended_drag_source_unittest.cc b/chromium/components/exo/extended_drag_source_unittest.cc
index c0f2c11dd49..bf27395aa2c 100644
--- a/chromium/components/exo/extended_drag_source_unittest.cc
+++ b/chromium/components/exo/extended_drag_source_unittest.cc
@@ -14,6 +14,7 @@
#include "ash/public/cpp/window_properties.h"
#include "ash/shell.h"
#include "ash/wm/toplevel_window_event_handler.h"
+#include "ash/wm/window_state.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
@@ -532,6 +533,33 @@ TEST_F(ExtendedDragSourceTest, DestroyDraggedSurfaceWhileDragging) {
EXPECT_TRUE(surface->window()->GetBoundsInScreen().origin().IsOrigin());
}
+// Regression test for crbug.com/1330125.
+// In exo, the drag source window could be destroyed while the dragged window
+// is updating its visibility. The example in this bug was while merging a
+// single-tab browser window in and out of another browser window.
+TEST_F(ExtendedDragSourceTest, DestroyDragSourceWindowWhileDragging) {
+ auto shell_surface =
+ exo::test::ShellSurfaceBuilder({32, 32}).BuildShellSurface();
+ auto* surface = shell_surface->root_surface();
+ // Start hidden so when it's shown later it triggers the memory violation.
+ shell_surface->GetWidget()->Hide();
+
+ extended_drag_source_->Drag(surface, gfx::Vector2d());
+ aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
+ EXPECT_EQ(window, extended_drag_source_->GetDraggedWindowForTesting());
+
+ auto drag_source_window = CreateToplevelTestWindow({20, 30, 200, 100});
+ extended_drag_source_->OnToplevelWindowDragStarted(
+ {70.0, 70.0}, ui::mojom::DragEventSource::kMouse,
+ drag_source_window.get());
+ EXPECT_EQ(extended_drag_source_->GetDragSourceWindowForTesting(),
+ drag_source_window.get());
+ drag_source_window.reset();
+ EXPECT_EQ(extended_drag_source_->GetDragSourceWindowForTesting(), nullptr);
+ // Without the fix, in asan build, this should cause the use-after-free.
+ shell_surface->GetWidget()->Show();
+}
+
TEST_F(ExtendedDragSourceTest, DragRequestsInRow_NoCrash) {
// Create and map a toplevel shell surface, but hidden.
auto shell_surface =
@@ -596,9 +624,9 @@ TEST_F(ExtendedDragSourceTest, DragWithScreenCoordinates) {
auto operation = DragDropOperation::Create(
&data_exchange_delegate, data_source.get(), shell_surface->root_surface(),
nullptr, location, ui::mojom::DragEventSource::kMouse);
-
auto* drag_drop_controller = static_cast<ash::DragDropController*>(
aura::client::GetDragDropClient(ash::Shell::GetPrimaryRootWindow()));
+
EXPECT_FALSE(shell_surface->IsDragged());
base::RunLoop loop;
drag_drop_controller->SetLoopClosureForTesting(
@@ -615,4 +643,68 @@ TEST_F(ExtendedDragSourceTest, DragWithScreenCoordinates) {
EXPECT_FALSE(shell_surface->IsDragged());
}
+TEST_F(ExtendedDragSourceTest, DragToAnotherDisplay) {
+ UpdateDisplay("800x600,800x600");
+
+ // Create and map a toplevel shell surface.
+ auto shell_surface =
+ exo::test::ShellSurfaceBuilder({32, 32}).BuildShellSurface();
+ auto* surface = shell_surface->root_surface();
+
+ shell_surface->GetWidget()->SetBounds({810, 10, 32, 32});
+
+ auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
+ shell_surface->GetWidget()->GetNativeWindow());
+ EXPECT_EQ(gfx::Rect(800, 0, 800, 600), display.bounds());
+
+ gfx::Rect expected_bounds =
+ shell_surface->GetWidget()->GetWindowBoundsInScreen();
+
+ constexpr int kDragOffset = 10;
+ extended_drag_source_->Drag(surface, gfx::Vector2d(kDragOffset, 0));
+
+ // Start the DND + extended-drag session.
+ // Creates a mouse-pressed event before starting the drag session.
+ ui::test::EventGenerator* generator = GetEventGenerator();
+ generator->MoveMouseTo({810 + kDragOffset, 10});
+ generator->PressLeftButton();
+
+ // Start a DragDropOperation.
+ drag_drop_controller_->set_should_block_during_drag_drop(true);
+ seat_->StartDrag(data_source_.get(), surface, /*icon=*/nullptr,
+ ui::mojom::DragEventSource::kMouse);
+ // Just move to the middle to avoid snapping.
+ int x_movement = 500;
+ expected_bounds.set_x(310);
+
+ constexpr int kXDragDelta = 20;
+ auto* toplevel_handler = ash::Shell::Get()->toplevel_window_event_handler();
+
+ base::RunLoop loop;
+ drag_drop_controller_->SetLoopClosureForTesting(
+ base::BindLambdaForTesting([&]() {
+ if (x_movement == 500) {
+ auto* window_state = ash::WindowState::Get(
+ shell_surface->GetWidget()->GetNativeWindow());
+ EXPECT_EQ(gfx::PointF(20, 10),
+ window_state->drag_details()->initial_location_in_parent);
+ }
+ if (x_movement > 0) {
+ x_movement -= kXDragDelta;
+ generator->MoveMouseBy(-kXDragDelta, 0);
+ EXPECT_TRUE(toplevel_handler->is_drag_in_progress());
+ } else {
+ generator->ReleaseLeftButton();
+ }
+ }),
+ loop.QuitClosure());
+ loop.Run();
+ EXPECT_FALSE(toplevel_handler->is_drag_in_progress());
+ EXPECT_EQ(expected_bounds,
+ shell_surface->GetWidget()->GetWindowBoundsInScreen());
+ display = display::Screen::GetScreen()->GetDisplayNearestWindow(
+ shell_surface->GetWidget()->GetNativeWindow());
+ EXPECT_EQ(gfx::Rect(0, 0, 800, 600), display.bounds());
+}
+
} // namespace exo
diff --git a/chromium/components/exo/fullscreen_shell_surface.cc b/chromium/components/exo/fullscreen_shell_surface.cc
deleted file mode 100644
index c6b3a74b047..00000000000
--- a/chromium/components/exo/fullscreen_shell_surface.cc
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright 2018 The Chromium 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/exo/fullscreen_shell_surface.h"
-
-#include "components/exo/shell_surface_util.h"
-#include "components/exo/surface.h"
-#include "components/exo/wm_helper.h"
-#include "third_party/skia/include/core/SkPath.h"
-#include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_observer.h"
-#include "ui/aura/window_occlusion_tracker.h"
-#include "ui/aura/window_targeter.h"
-#include "ui/base/metadata/metadata_header_macros.h"
-#include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/compositor/compositor.h"
-#include "ui/views/accessibility/view_accessibility.h"
-#include "ui/views/view.h"
-#include "ui/views/widget/widget.h"
-#include "ui/wm/core/window_util.h"
-
-namespace exo {
-
-class FullscreenShellSurface::FullscreenShellView : public views::View {
- public:
- METADATA_HEADER(FullscreenShellView);
- FullscreenShellView() = default;
- FullscreenShellView(const FullscreenShellView&) = delete;
- FullscreenShellView& operator=(const FullscreenShellView&) = delete;
- ~FullscreenShellView() override = default;
-
- // views::View:
- void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
- node_data->role = ax::mojom::Role::kClient;
- }
-
- void SetChildAxTreeId(ui::AXTreeID child_ax_tree_id) {
- GetViewAccessibility().OverrideChildTreeID(child_ax_tree_id);
- }
-};
-
-BEGIN_METADATA(FullscreenShellSurface, FullscreenShellView, views::View)
-END_METADATA
-
-FullscreenShellSurface::FullscreenShellSurface()
- : SurfaceTreeHost("FullscreenShellSurfaceHost") {
- CreateFullscreenShellSurfaceWidget(ui::SHOW_STATE_FULLSCREEN);
- SetCanResize(false);
- widget_->SetFullscreen(true);
-}
-
-FullscreenShellSurface::~FullscreenShellSurface() {
- if (widget_) {
- widget_->GetNativeWindow()->RemoveObserver(this);
- // Remove transient children so they are not automatically destroyed.
- for (auto* child : wm::GetTransientChildren(widget_->GetNativeWindow()))
- wm::RemoveTransientChild(widget_->GetNativeWindow(), child);
- if (widget_->IsVisible())
- widget_->Hide();
- widget_->CloseNow();
- }
- if (parent_)
- parent_->RemoveObserver(this);
- if (root_surface())
- root_surface()->RemoveSurfaceObserver(this);
-}
-
-void FullscreenShellSurface::SetApplicationId(const char* application_id) {
- // Store the value in |application_id_| in case the window does not exist yet.
- if (application_id)
- application_id_ = std::string(application_id);
- else
- application_id_.reset();
-
- if (widget_ && widget_->GetNativeWindow())
- SetShellApplicationId(widget_->GetNativeWindow(), application_id_);
-}
-
-void FullscreenShellSurface::SetStartupId(const char* startup_id) {
- // Store the value in |startup_id_| in case the window does not exist yet.
- if (startup_id)
- startup_id_ = std::string(startup_id);
- else
- startup_id_.reset();
-
- if (widget_ && widget_->GetNativeWindow())
- SetShellStartupId(widget_->GetNativeWindow(), startup_id_);
-}
-
-void FullscreenShellSurface::SetSurface(Surface* surface) {
- if (root_surface())
- root_surface()->RemoveSurfaceObserver(this);
- SetRootSurface(surface);
- SetShellRootSurface(widget_->GetNativeWindow(), root_surface());
- if (surface) {
- surface->AddSurfaceObserver(this);
- host_window()->Show();
- widget_->Show();
- } else {
- host_window()->Hide();
- widget_->Hide();
- }
-}
-
-void FullscreenShellSurface::Maximize() {
- if (!widget_)
- return;
-
- widget_->Maximize();
-}
-
-void FullscreenShellSurface::Minimize() {
- if (!widget_)
- return;
-
- widget_->Minimize();
-}
-
-void FullscreenShellSurface::Close() {
- if (!close_callback_.is_null())
- close_callback_.Run();
-}
-
-void FullscreenShellSurface::OnSurfaceCommit() {
- SurfaceTreeHost::OnSurfaceCommit();
- if (!OnPreWidgetCommit())
- return;
-
- CommitWidget();
- SubmitCompositorFrame();
-}
-
-bool FullscreenShellSurface::IsInputEnabled(Surface*) const {
- return true;
-}
-
-void FullscreenShellSurface::OnSetFrame(SurfaceFrameType frame_type) {}
-
-void FullscreenShellSurface::OnSetFrameColors(SkColor active_color,
- SkColor inactive_color) {}
-
-void FullscreenShellSurface::OnSetStartupId(const char* startup_id) {
- SetStartupId(startup_id);
-}
-
-void FullscreenShellSurface::OnSetApplicationId(const char* application_id) {
- SetApplicationId(application_id);
-}
-
-void FullscreenShellSurface::OnSurfaceDestroying(Surface* surface) {
- DCHECK_EQ(root_surface(), surface);
- surface->RemoveSurfaceObserver(this);
- SetRootSurface(nullptr);
-
- if (widget_)
- SetShellRootSurface(widget_->GetNativeWindow(), nullptr);
-
- // Hide widget before surface is destroyed. This allows hide animations to
- // run using the current surface contents.
- if (widget_) {
- // Remove transient children so they are not automatically hidden.
- for (auto* child : wm::GetTransientChildren(widget_->GetNativeWindow()))
- wm::RemoveTransientChild(widget_->GetNativeWindow(), child);
-
- widget_->Hide();
- }
-
- // Note: In its use in the Wayland server implementation, the surface
- // destroyed callback may destroy the ShellSurface instance. This call needs
- // to be last so that the instance can be destroyed.
- std::move(surface_destroyed_callback_).Run();
-}
-
-bool FullscreenShellSurface::CanMaximize() const {
- return true;
-}
-
-bool FullscreenShellSurface::CanMinimize() const {
- return true;
-}
-
-bool FullscreenShellSurface::ShouldShowWindowTitle() const {
- return false;
-}
-
-void FullscreenShellSurface::WindowClosing() {
- contents_view_->SetEnabled(false);
- contents_view_ = nullptr;
- widget_ = nullptr;
-}
-
-views::Widget* FullscreenShellSurface::GetWidget() {
- return widget_;
-}
-
-const views::Widget* FullscreenShellSurface::GetWidget() const {
- return widget_;
-}
-
-views::View* FullscreenShellSurface::GetContentsView() {
- if (!contents_view_)
- contents_view_ = new FullscreenShellView();
- return contents_view_;
-}
-
-bool FullscreenShellSurface::WidgetHasHitTestMask() const {
- return true;
-}
-
-void FullscreenShellSurface::GetWidgetHitTestMask(SkPath* mask) const {
- GetHitTestMask(mask);
- gfx::Point origin = host_window()->bounds().origin();
- SkMatrix matrix;
- // TODO (sagallea) acquire scale from display
- float scale = 1.f;
- matrix.setScaleTranslate(
- SkFloatToScalar(1.0f / scale), SkFloatToScalar(1.0f / scale),
- SkIntToScalar(origin.x()), SkIntToScalar(origin.y()));
- mask->transform(matrix);
-}
-
-void FullscreenShellSurface::OnWindowDestroying(aura::Window* window) {
- if (window == parent_) {
- parent_ = nullptr;
- // |parent_| being set to null effects the ability to maximize the window.
- if (widget_)
- widget_->OnSizeConstraintsChanged();
- }
-
- window->RemoveObserver(this);
-}
-
-void FullscreenShellSurface::SetChildAxTreeId(ui::AXTreeID child_ax_tree_id) {
- DCHECK(contents_view_);
- contents_view_->SetChildAxTreeId(child_ax_tree_id);
-}
-
-void FullscreenShellSurface::SetEnabled(bool enabled) {
- DCHECK(contents_view_);
- contents_view_->SetEnabled(enabled);
-}
-
-void FullscreenShellSurface::UpdateHostWindowBounds() {
- // This method applies multiple changes to the window tree. Use ScopedPause
- // to ensure that occlusion isn't recomputed before all changes have been
- // applied.
- aura::WindowOcclusionTracker::ScopedPause pause_occlusion;
-
- host_window()->SetBounds(
- gfx::Rect(root_surface()->window()->bounds().size()));
- host_window()->SetTransparent(!root_surface()->FillsBoundsOpaquely());
-}
-
-void FullscreenShellSurface::CreateFullscreenShellSurfaceWidget(
- ui::WindowShowState show_state) {
- DCHECK(!widget_);
-
- views::Widget::InitParams params;
- params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
- params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
- params.delegate = this;
- params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
- params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
- params.show_state = show_state;
- params.activatable = views::Widget::InitParams::Activatable::kYes;
- params.parent = WMHelper::GetInstance()->GetRootWindowForNewWindows();
- params.bounds = gfx::Rect(params.parent->bounds().size());
-
- widget_ = new views::Widget();
- widget_->Init(std::move(params));
-
- aura::Window* window = widget_->GetNativeWindow();
- window->SetName("FullscreenShellSurface");
- window->AddChild(host_window());
-
- SetShellApplicationId(window, application_id_);
- SetShellStartupId(window, startup_id_);
- SetShellRootSurface(window, root_surface());
-
- window->AddObserver(this);
-}
-
-void FullscreenShellSurface::CommitWidget() {
- if (!widget_)
- return;
-
- // Show widget if needed.
- if (!widget_->IsVisible()) {
- DCHECK(!widget_->IsClosed());
- widget_->Show();
- }
-}
-
-bool FullscreenShellSurface::OnPreWidgetCommit() {
- // If we have a |widget_|, then we must have a |contents_view_| as both are
- // created together.
- if (!widget_ && contents_view_->GetEnabled() &&
- host_window()->bounds().IsEmpty()) {
- return false;
- }
-
- return true;
-}
-
-} // namespace exo
diff --git a/chromium/components/exo/fullscreen_shell_surface.h b/chromium/components/exo/fullscreen_shell_surface.h
deleted file mode 100644
index cb58b0c08fb..00000000000
--- a/chromium/components/exo/fullscreen_shell_surface.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2018 The Chromium 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 COMPONENTS_EXO_FULLSCREEN_SHELL_SURFACE_H_
-#define COMPONENTS_EXO_FULLSCREEN_SHELL_SURFACE_H_
-
-#include "components/exo/surface_observer.h"
-#include "components/exo/surface_tree_host.h"
-#include "ui/accessibility/ax_node_data.h"
-#include "ui/accessibility/ax_tree_id.h"
-#include "ui/aura/window_observer.h"
-#include "ui/views/widget/widget_delegate.h"
-
-namespace exo {
-class Surface;
-
-// This class implements a toplevel fullscreen surface for which position
-// and state are managed by the shell.
-class FullscreenShellSurface : public SurfaceTreeHost,
- public SurfaceObserver,
- public aura::WindowObserver,
- public views::WidgetDelegate {
- public:
- FullscreenShellSurface();
-
- FullscreenShellSurface(const FullscreenShellSurface&) = delete;
- FullscreenShellSurface& operator=(const FullscreenShellSurface&) = delete;
-
- ~FullscreenShellSurface() override;
-
- // Set the callback to run when the user wants the shell surface to be closed.
- // The receiver can chose to not close the window on this signal.
- void set_close_callback(const base::RepeatingClosure& close_callback) {
- close_callback_ = close_callback;
- }
-
- // Set the callback to run when the surface is destroyed.
- void set_surface_destroyed_callback(
- base::OnceClosure surface_destroyed_callback) {
- surface_destroyed_callback_ = std::move(surface_destroyed_callback);
- }
-
- // Set the application ID for the surface
- void SetApplicationId(const char* startup_id);
-
- // Set the startup ID for the surface.
- void SetStartupId(const char* startup_id);
-
- // Set the Surface in use. Will replace root_surface_ if a surface is
- // currently set. Will remove root_surface_ if |surface| is nullptr.
- void SetSurface(Surface* surface);
-
- void Maximize();
- void Minimize();
- void Close();
-
- // SurfaceDelegate:
- void OnSurfaceCommit() override;
- bool IsInputEnabled(Surface* surface) const override;
- void OnSetFrame(SurfaceFrameType type) override;
- void OnSetFrameColors(SkColor active_color, SkColor inactive_color) override;
- void OnSetStartupId(const char* startup_id) override;
- void OnSetApplicationId(const char* application_id) override;
-
- // SurfaceObserver:
- void OnSurfaceDestroying(Surface* surface) override;
-
- // views::WidgetDelegate:
- bool CanMaximize() const override;
- bool CanMinimize() const override;
- bool ShouldShowWindowTitle() const override;
- void WindowClosing() override;
- views::Widget* GetWidget() override;
- const views::Widget* GetWidget() const override;
- views::View* GetContentsView() override;
- bool WidgetHasHitTestMask() const override;
- void GetWidgetHitTestMask(SkPath* mask) const override;
-
- // aura::WindowObserver:
- void OnWindowDestroying(aura::Window* window) override;
-
- void SetChildAxTreeId(ui::AXTreeID child_ax_tree_id);
- void SetEnabled(bool enabled);
-
- private:
- class FullscreenShellView;
- // Keep the bounds in sync with the root surface bounds.
- void UpdateHostWindowBounds() override;
-
- void CreateFullscreenShellSurfaceWidget(ui::WindowShowState show_state);
- void CommitWidget();
- bool OnPreWidgetCommit();
-
- views::Widget* widget_ = nullptr;
- aura::Window* parent_ = nullptr;
- absl::optional<std::string> application_id_;
- absl::optional<std::string> startup_id_;
- base::RepeatingClosure close_callback_;
- base::OnceClosure surface_destroyed_callback_;
- FullscreenShellView* contents_view_ = nullptr;
-};
-
-} // namespace exo
-
-#endif // COMPONENTS_EXO_FULLSCREEN_SHELL_SURFACE_H_
diff --git a/chromium/components/exo/fullscreen_shell_surface_unittest.cc b/chromium/components/exo/fullscreen_shell_surface_unittest.cc
deleted file mode 100644
index 4ab98e036b7..00000000000
--- a/chromium/components/exo/fullscreen_shell_surface_unittest.cc
+++ /dev/null
@@ -1,293 +0,0 @@
-// Copyright 2018 The Chromium 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/exo/fullscreen_shell_surface.h"
-
-#include "base/bind.h"
-#include "components/exo/buffer.h"
-#include "components/exo/shell_surface_util.h"
-#include "components/exo/sub_surface.h"
-#include "components/exo/surface.h"
-#include "components/exo/test/exo_test_base_views.h"
-#include "components/exo/wm_helper.h"
-#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/ax_node_data.h"
-#include "ui/accessibility/ax_tree_id.h"
-#include "ui/aura/env.h"
-#include "ui/aura/window.h"
-#include "ui/compositor/compositor.h"
-#include "ui/gfx/buffer_types.h"
-#include "ui/views/accessibility/view_accessibility.h"
-#include "ui/views/widget/widget.h"
-
-namespace exo {
-namespace {
-
-using FullscreenShellSurfaceTest = test::ExoTestBaseViews;
-
-std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
- const gfx::Size& size,
- gfx::BufferFormat format) {
- return aura::Env::GetInstance()
- ->context_factory()
- ->GetGpuMemoryBufferManager()
- ->CreateGpuMemoryBuffer(size, format, gfx::BufferUsage::GPU_READ,
- gpu::kNullSurfaceHandle, nullptr);
-}
-
-void DestroyFullscreenShellSurface(
- std::unique_ptr<FullscreenShellSurface>* fullscreen_surface) {
- fullscreen_surface->reset();
-}
-
-void Close(int* close_call_count) {
- (*close_call_count)++;
-}
-
-TEST_F(FullscreenShellSurfaceTest, SurfaceDestroyedCallback) {
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
-
- fullscreen_surface->set_surface_destroyed_callback(base::BindOnce(
- &DestroyFullscreenShellSurface, base::Unretained(&fullscreen_surface)));
-
- // Change the surface so the commit has an actual change otherwise it triggers
- // a DCHECK during frame submission.
- surface->SetViewport(gfx::SizeF(64, 64));
- surface->Commit();
-
- EXPECT_TRUE(fullscreen_surface.get());
- surface.reset();
- EXPECT_FALSE(fullscreen_surface.get());
-}
-
-TEST_F(FullscreenShellSurfaceTest, CloseCallback) {
- gfx::Size buffer_size(64, 64);
- std::unique_ptr<Buffer> buffer(new Buffer(
- CreateGpuMemoryBuffer(buffer_size, gfx::BufferFormat::RGBA_8888)));
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
-
- int close_call_count = 0;
- fullscreen_surface->set_close_callback(
- base::BindRepeating(&Close, base::Unretained(&close_call_count)));
-
- surface->Attach(buffer.get());
- surface->Commit();
-
- EXPECT_EQ(0, close_call_count);
- fullscreen_surface->Close();
- EXPECT_EQ(1, close_call_count);
-}
-
-TEST_F(FullscreenShellSurfaceTest, ShouldShowWindowTitle) {
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
-
- EXPECT_FALSE(fullscreen_surface->ShouldShowWindowTitle());
-}
-
-TEST_F(FullscreenShellSurfaceTest, SetApplicationId) {
- gfx::Size buffer_size(64, 64);
- std::unique_ptr<Buffer> buffer(new Buffer(
- CreateGpuMemoryBuffer(buffer_size, gfx::BufferFormat::RGBA_8888)));
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
-
- EXPECT_TRUE(fullscreen_surface->GetWidget());
- fullscreen_surface->SetApplicationId("test-id");
-
- surface->Attach(buffer.get());
- surface->Commit();
- aura::Window* window = fullscreen_surface->GetWidget()->GetNativeWindow();
- EXPECT_EQ("test-id", *GetShellApplicationId(window));
- fullscreen_surface->SetApplicationId("test");
- EXPECT_EQ("test", *GetShellApplicationId(window));
-
- fullscreen_surface->SetApplicationId(nullptr);
- EXPECT_EQ(nullptr, GetShellApplicationId(window));
-}
-
-TEST_F(FullscreenShellSurfaceTest, SetStartupId) {
- gfx::Size buffer_size(64, 64);
- std::unique_ptr<Buffer> buffer(new Buffer(
- CreateGpuMemoryBuffer(buffer_size, gfx::BufferFormat::RGBA_8888)));
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
-
- EXPECT_TRUE(fullscreen_surface->GetWidget());
- fullscreen_surface->SetStartupId("test-id");
-
- surface->Attach(buffer.get());
- surface->Commit();
- aura::Window* window = fullscreen_surface->GetWidget()->GetNativeWindow();
- EXPECT_EQ("test-id", *GetShellStartupId(window));
- fullscreen_surface->SetStartupId("test");
- EXPECT_EQ("test", *GetShellStartupId(window));
-
- fullscreen_surface->SetStartupId(nullptr);
- EXPECT_EQ(nullptr, GetShellStartupId(window));
-}
-
-TEST_F(FullscreenShellSurfaceTest, Maximize) {
- gfx::Size buffer_size(64, 64);
- std::unique_ptr<Buffer> buffer(new Buffer(
- CreateGpuMemoryBuffer(buffer_size, gfx::BufferFormat::RGBA_8888)));
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
-
- surface->Attach(buffer.get());
- surface->Commit();
- fullscreen_surface->Maximize();
- EXPECT_TRUE(fullscreen_surface->GetWidget()->IsMaximized());
-}
-
-TEST_F(FullscreenShellSurfaceTest, Minimize) {
- gfx::Size buffer_size(64, 64);
- std::unique_ptr<Buffer> buffer(new Buffer(
- CreateGpuMemoryBuffer(buffer_size, gfx::BufferFormat::RGBA_8888)));
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
-
- surface->Attach(buffer.get());
- surface->Commit();
- fullscreen_surface->Minimize();
- EXPECT_TRUE(fullscreen_surface->GetWidget()->IsMinimized());
-}
-
-TEST_F(FullscreenShellSurfaceTest, Bounds) {
- aura::Window* root_window =
- WMHelper::GetInstance()->GetRootWindowForNewWindows();
- gfx::Rect new_root_bounds(10, 10, 100, 100);
- root_window->SetBounds(new_root_bounds);
-
- gfx::Size buffer_size(64, 64);
- std::unique_ptr<Buffer> buffer(new Buffer(
- CreateGpuMemoryBuffer(buffer_size, gfx::BufferFormat::RGBA_8888)));
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
-
- surface->Attach(buffer.get());
- surface->Commit();
- gfx::Rect fullscreen_bounds =
- fullscreen_surface->GetWidget()->GetWindowBoundsInScreen();
- gfx::Rect expected_bounds(new_root_bounds.size());
- EXPECT_EQ(fullscreen_bounds, expected_bounds);
-}
-
-TEST_F(FullscreenShellSurfaceTest, BoundsWithPartiallyOffscreenSubSurface) {
- aura::Window* root_window =
- WMHelper::GetInstance()->GetRootWindowForNewWindows();
- gfx::Rect new_root_bounds(10, 10, 100, 100);
- gfx::Rect expected_bounds(new_root_bounds.size());
- root_window->SetBounds(new_root_bounds);
-
- gfx::Size buffer_size(100, 100);
- auto buffer = std::make_unique<Buffer>(
- CreateGpuMemoryBuffer(buffer_size, gfx::BufferFormat::RGBA_8888));
- auto parent = std::make_unique<Surface>();
- auto fullscreen_surface = std::make_unique<FullscreenShellSurface>();
- fullscreen_surface->SetSurface(parent.get());
-
- parent->Attach(buffer.get());
- parent->Commit();
- EXPECT_EQ(fullscreen_surface->GetWidget()->GetWindowBoundsInScreen(),
- expected_bounds);
- EXPECT_EQ(parent->window()->bounds(), expected_bounds);
-
- auto surface = std::make_unique<Surface>();
- auto sub_surface = std::make_unique<SubSurface>(surface.get(), parent.get());
- surface->Attach(buffer.get());
- sub_surface->SetPosition(gfx::PointF(-50, -50));
-
- parent->Commit();
- // Make sure the sub-surface doesn't affect the Fullscreen Shell's Window
- // size/position.
- EXPECT_EQ(fullscreen_surface->GetWidget()->GetWindowBoundsInScreen(),
- expected_bounds);
- // The root surface should also have the same position/size as before.
- EXPECT_EQ(parent->window()->bounds(), expected_bounds);
-}
-
-TEST_F(FullscreenShellSurfaceTest, SetAXChildTree) {
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
- const views::ViewAccessibility& view_accessibility =
- fullscreen_surface->GetContentsView()->GetViewAccessibility();
- ui::AXNodeData node_data;
- view_accessibility.GetAccessibleNodeData(&node_data);
- EXPECT_FALSE(
- node_data.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId));
-
- ui::AXTreeID tree_id = ui::AXTreeID::CreateNewAXTreeID();
- fullscreen_surface->SetChildAxTreeId(tree_id);
- view_accessibility.GetAccessibleNodeData(&node_data);
- EXPECT_TRUE(
- node_data.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId));
-}
-
-TEST_F(FullscreenShellSurfaceTest, SwapSurface) {
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
- fullscreen_surface->SetEnabled(true);
- fullscreen_surface->set_surface_destroyed_callback(base::BindOnce(
- &DestroyFullscreenShellSurface, base::Unretained(&fullscreen_surface)));
- EXPECT_EQ(surface.get(), fullscreen_surface->root_surface());
- std::unique_ptr<Surface> surface2(new Surface);
- fullscreen_surface->SetSurface(surface2.get());
- EXPECT_EQ(surface2.get(), fullscreen_surface->root_surface());
- EXPECT_NE(surface.get(), fullscreen_surface->root_surface());
- // RootWindow->FullscreenShellSurface->FullscreenShellSurfaceHost->ExoSurface
- EXPECT_EQ(1ul, WMHelper::GetInstance()
- ->GetRootWindowForNewWindows()
- ->children()[0]
- ->children()[0]
- ->children()
- .size());
-}
-
-TEST_F(FullscreenShellSurfaceTest, RemoveSurface) {
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<FullscreenShellSurface> fullscreen_surface(
- new FullscreenShellSurface());
- fullscreen_surface->SetSurface(surface.get());
- fullscreen_surface->SetEnabled(true);
- fullscreen_surface->set_surface_destroyed_callback(base::BindOnce(
- &DestroyFullscreenShellSurface, base::Unretained(&fullscreen_surface)));
- EXPECT_EQ(surface.get(), fullscreen_surface->root_surface());
- fullscreen_surface->SetSurface(nullptr);
- EXPECT_FALSE(fullscreen_surface->root_surface());
- // RootWindow->FullscreenShellSurface->FullscreenShellSurfaceHost->null
- EXPECT_EQ(0ul, WMHelper::GetInstance()
- ->GetRootWindowForNewWindows()
- ->children()[0]
- ->children()[0]
- ->children()
- .size());
-}
-
-} // namespace
-} // namespace exo
diff --git a/chromium/components/exo/keyboard.cc b/chromium/components/exo/keyboard.cc
index 94220ddeca7..524379420f2 100644
--- a/chromium/components/exo/keyboard.cc
+++ b/chromium/components/exo/keyboard.cc
@@ -4,6 +4,7 @@
#include "components/exo/keyboard.h"
+#include "ash/accelerators/accelerator_table.h"
#include "ash/constants/app_types.h"
#include "ash/constants/ash_features.h"
#include "ash/keyboard/ui/keyboard_ui_controller.h"
@@ -13,6 +14,8 @@
#include "ash/shell.h"
#include "base/bind.h"
#include "base/containers/contains.h"
+#include "base/containers/span.h"
+#include "base/no_destructor.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "components/exo/input_trace.h"
@@ -126,14 +129,10 @@ bool CanConsumeAshAccelerators(Surface* surface) {
for (; window; window = window->parent()) {
const auto app_type =
static_cast<ash::AppType>(window->GetProperty(aura::client::kAppType));
- // TODO(fukino): Always returning false for Lacros window is a short-term
- // solution. In reality, Lacros can consume ash accelerator's key
- // combination when it is a deprecated ash accelerator or the window is
- // running PWA. We need to let the wayland client dynamically decrlare
- // whether it want to consume ash accelerators' key combinations.
- // crbug.com/1174025.
+ // TOOD(hidehiko): get rid of this if check, after introducing capability,
+ // followed by ARC/Crostini migration.
if (app_type == ash::AppType::LACROS)
- return false;
+ return surface->is_keyboard_shortcuts_inhibited();
}
return true;
}
@@ -146,25 +145,27 @@ bool ProcessAshAcceleratorIfPossible(Surface* surface, ui::KeyEvent* event) {
if (CanConsumeAshAccelerators(surface))
return false;
- // TODO(crbug.com/1301977): Remove this workaround on fixing acceleartor
- // handling for lacros.
- const ui::Accelerator kAppHandlingAccelerators[] = {
- // Ctrl-N (new window), Shift-Ctrl-N (new incognite window), Ctrl-T (new
- // tab), and Shit-Ctrl-T (restore tab) need to be sent to the active
- // client even when the active window is lacros-chrome, since the
- // ash-chrome does not handle these new-window requests properly at this
- // moment.
- {ui::VKEY_N, ui::EF_CONTROL_DOWN},
- {ui::VKEY_N, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN},
- {ui::VKEY_T, ui::EF_CONTROL_DOWN},
- {ui::VKEY_T, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN},
- // Also forward Ctrl-/ and Shift-Ctrl-/ so Lacros processes the help app
- // opening while it can be intercepted.
- {ui::VKEY_OEM_2, ui::EF_CONTROL_DOWN},
- {ui::VKEY_OEM_2, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN},
- };
+ // If accelerators can be processed by browser, send it to the app.
+ static const base::NoDestructor<std::vector<ui::Accelerator>>
+ kAppHandlingAccelerators([] {
+ std::vector<ui::Accelerator> result;
+ for (size_t i = 0; i < ash::kAcceleratorDataLength; ++i) {
+ const auto& ash_entry = ash::kAcceleratorData[i];
+ if (base::Contains(base::span<const ash::AcceleratorAction>(
+ ash::kActionsInterceptableByBrowser,
+ ash::kActionsInterceptableByBrowserLength),
+ ash_entry.action) ||
+ base::Contains(base::span<const ash::AcceleratorAction>(
+ ash::kActionsDuplicatedWithBrowser,
+ ash::kActionsDuplicatedWithBrowserLength),
+ ash_entry.action)) {
+ result.emplace_back(ash_entry.keycode, ash_entry.modifiers);
+ }
+ }
+ return result;
+ }());
ui::Accelerator accelerator(*event);
- if (base::Contains(kAppHandlingAccelerators, accelerator))
+ if (base::Contains(*kAppHandlingAccelerators, accelerator))
return false;
return ash::AcceleratorController::Get()->Process(accelerator);
diff --git a/chromium/components/exo/keyboard_unittest.cc b/chromium/components/exo/keyboard_unittest.cc
index 435ff19131e..510d34147b9 100644
--- a/chromium/components/exo/keyboard_unittest.cc
+++ b/chromium/components/exo/keyboard_unittest.cc
@@ -4,6 +4,7 @@
#include "components/exo/keyboard.h"
+#include "ash/accelerators/accelerator_controller_impl.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/constants/app_types.h"
#include "ash/constants/ash_pref_names.h"
@@ -30,6 +31,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/focus_client.h"
+#include "ui/base/accelerators/test_accelerator_target.h"
#include "ui/base/ime/dummy_text_input_client.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/event_constants.h"
@@ -523,6 +525,84 @@ TEST_F(KeyboardTest, OnKeyboardKey_NotSendKeyIfConsumedByIme) {
input_method->SetFocusedTextInputClient(nullptr);
}
+TEST_F(KeyboardTest, OnKeyboardKey_KeyboardInhibit) {
+ std::unique_ptr<Surface> surface(new Surface());
+ std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
+ gfx::Size buffer_size(10, 10);
+ std::unique_ptr<Buffer> buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+ surface->Attach(buffer.get());
+ surface->Commit();
+ // Set lacros attribute now for testing. This can be removed, when
+ // all clients are migrated into this model.
+ surface->window()->SetProperty(aura::client::kAppType,
+ static_cast<int>(ash::AppType::LACROS));
+
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->FocusWindow(nullptr);
+
+ // Register accelerator to be triggered.
+ ui::TestAcceleratorTarget accelerator_target;
+ {
+ ui::Accelerator accelerator(ui::VKEY_P,
+ ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN);
+ ash::AcceleratorControllerImpl* controller =
+ ash::Shell::Get()->accelerator_controller();
+ controller->Register({accelerator}, &accelerator_target);
+ }
+
+ auto delegate = std::make_unique<NiceMockKeyboardDelegate>();
+ auto* delegate_ptr = delegate.get();
+ NiceMockKeyboardObserver observer;
+ Seat seat;
+ Keyboard keyboard(std::move(delegate), &seat);
+ keyboard.AddObserver(&observer);
+ keyboard.SetNeedKeyboardKeyAcks(true);
+
+ EXPECT_CALL(*delegate_ptr, CanAcceptKeyboardEventsForSurface(surface.get()))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(*delegate_ptr,
+ OnKeyboardModifiers(KeyboardModifiers{kNumLockMask, 0, 0, 0}));
+ EXPECT_CALL(
+ *delegate_ptr,
+ OnKeyboardEnter(surface.get(), base::flat_map<ui::DomCode, KeyState>()));
+ focus_client->FocusWindow(surface->window());
+ testing::Mock::VerifyAndClearExpectations(delegate_ptr);
+
+ ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+ // This should only generate a press event for KEY_P.
+ accelerator_target.ResetCounts();
+ EXPECT_CALL(observer,
+ OnKeyboardKey(testing::_, ui::DomCode::US_P, testing::_))
+ .Times(0);
+ EXPECT_CALL(*delegate_ptr,
+ OnKeyboardKey(testing::_, ui::DomCode::US_P, testing::_))
+ .Times(0);
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_P);
+ generator.PressKey(ui::VKEY_P, ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN);
+ EXPECT_EQ(1, accelerator_target.accelerator_count());
+ testing::Mock::VerifyAndClearExpectations(&observer);
+ testing::Mock::VerifyAndClearExpectations(delegate_ptr);
+
+ // Set keyboard-shortcut-inhibited, so the key event should be sent to app.
+ surface->SetKeyboardShortcutsInhibited(true);
+ accelerator_target.ResetCounts();
+ {
+ testing::InSequence s;
+ EXPECT_CALL(observer, OnKeyboardKey(testing::_, ui::DomCode::US_P, true));
+ EXPECT_CALL(*delegate_ptr,
+ OnKeyboardKey(testing::_, ui::DomCode::US_P, true));
+ }
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_P);
+ generator.PressKey(ui::VKEY_P, ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN);
+ EXPECT_EQ(0, accelerator_target.accelerator_count());
+ testing::Mock::VerifyAndClearExpectations(&observer);
+ testing::Mock::VerifyAndClearExpectations(delegate_ptr);
+}
+
TEST_F(KeyboardTest, FocusWithArcOverlay) {
auto delegate = std::make_unique<NiceMockKeyboardDelegate>();
// Just allow any surface to receive focus.
diff --git a/chromium/components/exo/layer_tree_frame_sink_holder.cc b/chromium/components/exo/layer_tree_frame_sink_holder.cc
index 11f20135520..4d0e60418ce 100644
--- a/chromium/components/exo/layer_tree_frame_sink_holder.cc
+++ b/chromium/components/exo/layer_tree_frame_sink_holder.cc
@@ -4,6 +4,7 @@
#include "components/exo/layer_tree_frame_sink_holder.h"
+#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/threading/thread_task_runner_handle.h"
#include "cc/trees/layer_tree_frame_sink.h"
@@ -11,6 +12,9 @@
#include "components/viz/common/frame_timing_details.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
#include "components/viz/common/resources/returned_resource.h"
+#include "gpu/command_buffer/client/context_support.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "ui/gfx/gpu_fence.h"
namespace exo {
@@ -19,9 +23,13 @@ namespace exo {
LayerTreeFrameSinkHolder::LayerTreeFrameSinkHolder(
SurfaceTreeHost* surface_tree_host,
- std::unique_ptr<cc::LayerTreeFrameSink> frame_sink)
+ std::unique_ptr<cc::LayerTreeFrameSink> frame_sink,
+ scoped_refptr<viz::ContextProvider> context_provider)
: surface_tree_host_(surface_tree_host),
- frame_sink_(std::move(frame_sink)) {
+ frame_sink_(std::move(frame_sink)),
+ context_provider_(context_provider),
+ use_gpu_fence_(
+ context_provider_->ContextCapabilities().chromium_gpu_fence) {
frame_sink_->BindToClient(this);
}
@@ -110,17 +118,19 @@ LayerTreeFrameSinkHolder::BuildHitTestData() {
void LayerTreeFrameSinkHolder::ReclaimResources(
std::vector<viz::ReturnedResource> resources) {
- for (auto& resource : resources) {
- // Skip resources that are also in last frame. This can happen if
- // the frame sink id changed.
- if (base::Contains(last_frame_resources_, resource.id)) {
- continue;
- }
- resource_manager_.ReclaimResource(std::move(resource));
+ if (!use_gpu_fence_) {
+ ReclaimResourcesInternal(std::move(resources), nullptr);
+ return;
}
- if (lifetime_manager_ && resource_manager_.HasNoCallbacks())
- ScheduleDelete();
+ // See the comment at ReclaimResourcesInternal's declaration.
+ gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
+ auto gpu_fence_id = gles2->CreateGpuFenceCHROMIUM();
+ context_provider_->ContextSupport()->GetGpuFence(
+ gpu_fence_id,
+ base::BindOnce(&LayerTreeFrameSinkHolder::ReclaimResourcesInternal,
+ weak_ptr_factory_.GetWeakPtr(), std::move(resources)));
+ gles2->DestroyGpuFenceCHROMIUM(gpu_fence_id);
}
void LayerTreeFrameSinkHolder::DidReceiveCompositorFrameAck() {
@@ -166,4 +176,24 @@ void LayerTreeFrameSinkHolder::OnDestroyed() {
ScheduleDelete();
}
+void LayerTreeFrameSinkHolder::ReclaimResourcesInternal(
+ std::vector<viz::ReturnedResource> resources,
+ std::unique_ptr<gfx::GpuFence> release_fence) {
+ for (auto& resource : resources) {
+ // Skip resources that are also in last frame. This can happen if
+ // the frame sink id changed.
+ if (base::Contains(last_frame_resources_, resource.id)) {
+ continue;
+ }
+
+ if (resource.release_fence.is_null() && release_fence)
+ resource.release_fence = release_fence->GetGpuFenceHandle().Clone();
+
+ resource_manager_.ReclaimResource(std::move(resource));
+ }
+
+ if (lifetime_manager_ && resource_manager_.HasNoCallbacks())
+ ScheduleDelete();
+}
+
} // namespace exo
diff --git a/chromium/components/exo/layer_tree_frame_sink_holder.h b/chromium/components/exo/layer_tree_frame_sink_holder.h
index 7f260f56d5b..0c16a62f745 100644
--- a/chromium/components/exo/layer_tree_frame_sink_holder.h
+++ b/chromium/components/exo/layer_tree_frame_sink_holder.h
@@ -7,6 +7,7 @@
#include <memory>
+#include "base/memory/weak_ptr.h"
#include "cc/trees/layer_tree_frame_sink_client.h"
#include "components/exo/frame_sink_resource_manager.h"
#include "components/exo/wm_helper.h"
@@ -14,9 +15,14 @@
#include "components/viz/common/resources/release_callback.h"
namespace viz {
+class ContextProvider;
struct FrameTimingDetails;
}
+namespace gfx {
+class GpuFence;
+}
+
namespace cc {
class LayerTreeFrameSink;
}
@@ -30,8 +36,10 @@ class SurfaceTreeHost;
class LayerTreeFrameSinkHolder : public cc::LayerTreeFrameSinkClient,
public WMHelper::LifetimeManager::Observer {
public:
- LayerTreeFrameSinkHolder(SurfaceTreeHost* surface_tree_host,
- std::unique_ptr<cc::LayerTreeFrameSink> frame_sink);
+ LayerTreeFrameSinkHolder(
+ SurfaceTreeHost* surface_tree_host,
+ std::unique_ptr<cc::LayerTreeFrameSink> frame_sink,
+ scoped_refptr<viz::ContextProvider> context_provider);
LayerTreeFrameSinkHolder(const LayerTreeFrameSinkHolder&) = delete;
LayerTreeFrameSinkHolder& operator=(const LayerTreeFrameSinkHolder&) = delete;
@@ -78,8 +86,17 @@ class LayerTreeFrameSinkHolder : public cc::LayerTreeFrameSinkClient,
// WMHelper::LifetimeManager::Observer:
void OnDestroyed() override;
+ // |release_fence| is a fence that is created when ReclaimResources call comes
+ // and that is used for those resources that do not have fences. That
+ // happens when resources are not overlaid, but rather composited.
+ // TODO(crbug.com/1310136): this should be managed by SkiaRenderer instead.
+ void ReclaimResourcesInternal(std::vector<viz::ReturnedResource> resources,
+ std::unique_ptr<gfx::GpuFence> release_fence);
+
SurfaceTreeHost* surface_tree_host_;
std::unique_ptr<cc::LayerTreeFrameSink> frame_sink_;
+ scoped_refptr<viz::ContextProvider> context_provider_;
+ const bool use_gpu_fence_;
FrameSinkResourceManager resource_manager_;
@@ -92,6 +109,8 @@ class LayerTreeFrameSinkHolder : public cc::LayerTreeFrameSinkClient,
bool delete_pending_ = false;
WMHelper::LifetimeManager* lifetime_manager_ = nullptr;
+
+ base::WeakPtrFactory<LayerTreeFrameSinkHolder> weak_ptr_factory_{this};
};
} // namespace exo
diff --git a/chromium/components/exo/pointer.cc b/chromium/components/exo/pointer.cc
index 45b533241ce..008a4ef7996 100644
--- a/chromium/components/exo/pointer.cc
+++ b/chromium/components/exo/pointer.cc
@@ -478,7 +478,7 @@ void Pointer::OnSurfaceDestroying(Surface* surface) {
// ui::EventHandler overrides:
void Pointer::OnMouseEvent(ui::MouseEvent* event) {
- if (seat_->was_shutdown())
+ if (seat_->was_shutdown() || event->handled())
return;
// Nothing to report to a client nor have to update the pointer when capture
@@ -486,8 +486,6 @@ void Pointer::OnMouseEvent(ui::MouseEvent* event) {
if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED)
return;
- seat_->SetLastPointerLocation(event->root_location_f());
-
Surface* target = GetEffectiveTargetForEvent(event);
gfx::PointF location_in_target = event->location_f();
if (target) {
@@ -737,7 +735,7 @@ void Pointer::OnDragCompleted(const ui::DropTargetEvent& event) {
// being destroyed. Verify whether this is the case, and adapt the event.
//
// TODO(https://crbug.com/1160925): Avoid nested RunLoop in exo
- // DataDevice::OnPerformDrop() - remove the block below when it is fixed.
+ // DataDevice::GetDropCallback() - remove the block below when it is fixed.
auto* event_target = static_cast<aura::Window*>(event.target());
if (!event_target) {
LOG(WARNING) << "EventTarget has been destroyed during the drop operation.";
diff --git a/chromium/components/exo/pointer_unittest.cc b/chromium/components/exo/pointer_unittest.cc
index ef0c28379fa..9cd3d42291c 100644
--- a/chromium/components/exo/pointer_unittest.cc
+++ b/chromium/components/exo/pointer_unittest.cc
@@ -1176,6 +1176,45 @@ TEST_F(PointerTest, DragDropAndPointerEnterLeaveEvents_NoOpOnTouchDrag) {
pointer.reset();
}
+TEST_F(PointerTest, IgnoresHandledEvents) {
+ // A very dumb handler that simply marks all events as handled. This is needed
+ // allows us to mark a mouse event as handled as it gets processed by the
+ // event processor.
+ class SetHandledHandler : public ui::EventHandler {
+ void OnMouseEvent(ui::MouseEvent* event) override { event->SetHandled(); }
+ };
+ SetHandledHandler handler;
+ ash::Shell::Get()->AddPreTargetHandler(&handler);
+
+ Seat seat(std::make_unique<TestDataExchangeDelegate>());
+ testing::NiceMock<MockPointerDelegate> pointer_delegate;
+ std::unique_ptr<Pointer> pointer(new Pointer(&pointer_delegate, &seat));
+
+ // Make origin into a real window so the touch can click it
+ std::unique_ptr<ShellSurface> shell_surface =
+ test::ShellSurfaceBuilder({10, 10}).BuildShellSurface();
+
+ EXPECT_CALL(pointer_delegate, CanAcceptPointerEventsForSurface(testing::_))
+ .WillRepeatedly(testing::Return(true));
+ ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+
+ // The SetHandlerHandler should have marked the event as processed. Therefore
+ // the event should simply be ignored.
+ EXPECT_CALL(pointer_delegate,
+ OnPointerButton(testing::_, testing::_, testing::_))
+ .Times(0);
+
+ // This event should be ignored because it has already been handled.
+ auto window_point = shell_surface->surface_for_testing()
+ ->window()
+ ->GetBoundsInScreen()
+ .CenterPoint();
+ generator.MoveMouseTo(window_point);
+ generator.ClickLeftButton();
+
+ ash::Shell::Get()->RemovePreTargetHandler(&handler);
+}
+
namespace {
class PointerDragDropObserver : public WMHelper::DragDropObserver {
diff --git a/chromium/components/exo/seat.cc b/chromium/components/exo/seat.cc
index 08354790e7c..156952b8d2f 100644
--- a/chromium/components/exo/seat.cc
+++ b/chromium/components/exo/seat.cc
@@ -31,6 +31,7 @@
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint_serializer.h"
+#include "ui/display/screen.h"
#include "ui/events/event_utils.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/gfx/geometry/point_f.h"
@@ -130,13 +131,11 @@ void Seat::StartDrag(DataSource* source,
Surface* icon,
ui::mojom::DragEventSource event_source) {
// DragDropOperation manages its own lifetime.
+ auto cursor_location =
+ gfx::PointF(display::Screen::GetScreen()->GetCursorScreenPoint());
drag_drop_operation_ =
DragDropOperation::Create(data_exchange_delegate_.get(), source, origin,
- icon, last_pointer_location_, event_source);
-}
-
-void Seat::SetLastPointerLocation(const gfx::PointF& last_pointer_location) {
- last_pointer_location_ = last_pointer_location;
+ icon, cursor_location, event_source);
}
void Seat::AbortPendingDragOperation() {
diff --git a/chromium/components/exo/seat.h b/chromium/components/exo/seat.h
index 619eda37fff..82129c46c2a 100644
--- a/chromium/components/exo/seat.h
+++ b/chromium/components/exo/seat.h
@@ -118,10 +118,6 @@ class Seat : public aura::client::FocusChangeObserver,
Surface* icon,
ui::mojom::DragEventSource event_source);
- // Sets the last location in screen coordinates, irrespective of mouse or
- // touch.
- void SetLastPointerLocation(const gfx::PointF& last_pointer_location);
-
// Abort any drag operations that haven't been started yet.
void AbortPendingDragOperation();
@@ -237,8 +233,6 @@ class Seat : public aura::client::FocusChangeObserver,
// True while Seat is updating clipboard data to selection source.
bool changing_clipboard_data_to_selection_source_;
- gfx::PointF last_pointer_location_;
-
bool was_shutdown_ = false;
#if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chromium/components/exo/shell_surface.cc b/chromium/components/exo/shell_surface.cc
index 4d424fef9c0..6d14076bcdd 100644
--- a/chromium/components/exo/shell_surface.cc
+++ b/chromium/components/exo/shell_surface.cc
@@ -402,11 +402,6 @@ void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
return;
}
- if (needs_layout_on_show_) {
- needs_layout_on_show_ = false;
- return;
- }
-
// If size changed then give the client a chance to produce new contents
// before origin on screen is changed. Retain the old origin by reverting
// the origin delta until the next configure is acknowledged.
@@ -425,6 +420,14 @@ void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
}
}
+void ShellSurface::OnWindowAddedToRootWindow(aura::Window* window) {
+ ShellSurfaceBase::OnWindowAddedToRootWindow(window);
+ if (window != widget_->GetNativeWindow())
+ return;
+ if (!origin_change_callback_.is_null())
+ origin_change_callback_.Run(GetClientBoundsInScreen(widget_).origin());
+}
+
////////////////////////////////////////////////////////////////////////////////
// ash::WindowStateObserver overrides:
@@ -607,9 +610,12 @@ void ShellSurface::Configure(bool ends_drag) {
GetClientBoundsInScreen(widget_), window_state->GetStateType(),
IsResizing(), widget_->IsActive(), origin_offset);
} else {
- serial = configure_callback_.Run(gfx::Rect(),
- chromeos::WindowStateType::kNormal,
- false, false, origin_offset);
+ gfx::Rect bounds;
+ if (initial_bounds_)
+ bounds.set_origin(initial_bounds_->origin());
+ serial =
+ configure_callback_.Run(bounds, chromeos::WindowStateType::kNormal,
+ false, false, origin_offset);
}
}
diff --git a/chromium/components/exo/shell_surface.h b/chromium/components/exo/shell_surface.h
index a4533dd4051..9c95c0b2ff2 100644
--- a/chromium/components/exo/shell_surface.h
+++ b/chromium/components/exo/shell_surface.h
@@ -119,6 +119,7 @@ class ShellSurface : public ShellSurfaceBase, public ash::WindowStateObserver {
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) override;
+ void OnWindowAddedToRootWindow(aura::Window* window) override;
// Overridden from ash::WindowStateObserver:
void OnPreWindowStateTypeChange(ash::WindowState* window_state,
diff --git a/chromium/components/exo/shell_surface_base.cc b/chromium/components/exo/shell_surface_base.cc
index 3b37132052c..e19378fd491 100644
--- a/chromium/components/exo/shell_surface_base.cc
+++ b/chromium/components/exo/shell_surface_base.cc
@@ -26,6 +26,7 @@
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/check.h"
+#include "base/debug/stack_trace.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
@@ -386,18 +387,6 @@ void ShellSurfaceBase::SetIcon(const gfx::ImageSkia& icon) {
}
void ShellSurfaceBase::SetSystemModal(bool system_modal) {
- // System modal container is used by clients to implement client side
- // managed system modal dialogs using a single ShellSurface instance.
- // Hit-test region will be non-empty when at least one dialog exists on
- // the client side. Here we detect the transition between no client side
- // dialog and at least one dialog so activatable state is properly
- // updated.
- if (container_ != ash::kShellWindowId_SystemModalContainer) {
- LOG(ERROR)
- << "Only a window in SystemModalContainer can change the modality";
- return;
- }
-
if (system_modal == system_modal_)
return;
@@ -529,6 +518,7 @@ void ShellSurfaceBase::SetPip() {
pending_pip_ = true;
return;
}
+ pending_pip_ = false;
// Set all the necessary window properties and window state.
auto* window = widget_->GetNativeWindow();
@@ -536,15 +526,15 @@ void ShellSurfaceBase::SetPip() {
window->SetProperty(aura::client::kZOrderingKey,
ui::ZOrderLevel::kFloatingWindow);
- // Pip windows should start in the bottom right corner of the screen so move
- // |window| to the bottom right of the work area and let the pip positioner
- // move it within the work area.
+ if (initial_bounds_)
+ return;
+ // If no initial bounds is specified, pip windows should start in the bottom
+ // right corner of the screen so move |window| to the bottom right of the
+ // work area and let the pip positioner move it within the work area.
auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(window);
gfx::Size window_size = window->bounds().size();
window->SetBoundsInScreen(
gfx::Rect(display.work_area().bottom_right(), window_size), display);
-
- pending_pip_ = false;
}
void ShellSurfaceBase::UnsetPip() {
@@ -644,6 +634,23 @@ void ShellSurfaceBase::SetWindowBounds(const gfx::Rect& bounds) {
widget_->SetBounds(bounds);
}
+void ShellSurfaceBase::SetRestoreInfo(int32_t restore_session_id,
+ int32_t restore_window_id) {
+ // TODO(crbug.com/1327490): Rename restore info variables.
+ // Restore information must be set before widget is created.
+ DCHECK(!widget_);
+ restore_session_id_.emplace(restore_session_id);
+ restore_window_id_.emplace(restore_window_id);
+}
+
+void ShellSurfaceBase::SetRestoreInfoWithWindowIdSource(
+ int32_t restore_session_id,
+ const std::string& restore_window_id_source) {
+ restore_session_id_.emplace(restore_session_id);
+ if (!restore_window_id_source.empty())
+ restore_window_id_source_.emplace(restore_window_id_source);
+}
+
void ShellSurfaceBase::SetDisplay(int64_t display_id) {
TRACE_EVENT1("exo", "ShellSurfaceBase::SetDisplay", "display_id", display_id);
@@ -1010,6 +1017,10 @@ ShellSurfaceBase::CreateNonClientFrameView(views::Widget* widget) {
return CreateNonClientFrameViewInternal(widget);
}
+bool ShellSurfaceBase::ShouldSaveWindowPlacement() const {
+ return !is_popup_ && !movement_disabled_;
+}
+
bool ShellSurfaceBase::WidgetHasHitTestMask() const {
return true;
}
@@ -1217,6 +1228,9 @@ void ShellSurfaceBase::CreateShellSurfaceWidget(
DisableMovement();
}
+ if (system_modal_)
+ SetModalType(ui::MODAL_TYPE_SYSTEM);
+
views::Widget::InitParams params;
params.type = emulate_x11_override_redirect
? views::Widget::InitParams::TYPE_MENU
@@ -1280,13 +1294,34 @@ void ShellSurfaceBase::CreateShellSurfaceWidget(
params.activatable = activatable
? views::Widget::InitParams::Activatable::kYes
: views::Widget::InitParams::Activatable::kNo;
+ if (restore_session_id_) {
+ params.init_properties_container.SetProperty(app_restore::kWindowIdKey,
+ *restore_session_id_);
+ }
+ if (restore_window_id_) {
+ params.init_properties_container.SetProperty(
+ app_restore::kRestoreWindowIdKey, *restore_window_id_);
+ }
+ if (restore_window_id_source_) {
+ params.init_properties_container.SetProperty(
+ app_restore::kRestoreWindowIdKey,
+ app_restore::FetchRestoreWindowId(*restore_window_id_source_));
+ params.init_properties_container.SetProperty(
+ app_restore::kAppIdKey, restore_window_id_source_.value());
+ }
#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // Restore `params` to those of the saved `restore_window_id_`.
app_restore::ModifyWidgetParams(params.init_properties_container.GetProperty(
app_restore::kRestoreWindowIdKey),
&params);
#endif
+ // If app restore specifies the initial bounds, set `initial_bounds_` to it so
+ // that shell surface knows the initial bounds is set.
+ if (!params.bounds.IsEmpty() && !initial_bounds_)
+ initial_bounds_.emplace(params.bounds);
+
OverrideInitParams(&params);
// Note: NativeWidget owns this widget.
@@ -1334,10 +1369,8 @@ void ShellSurfaceBase::CreateShellSurfaceWidget(
if (frame_type_ != SurfaceFrameType::NONE)
OnSetFrame(frame_type_);
- if (pending_pip_) {
+ if (pending_pip_)
SetPip();
- pending_pip_ = false;
- }
root_surface()->OnDeskChanged(GetWindowDeskStateChanged(window));
@@ -1708,8 +1741,10 @@ void ShellSurfaceBase::CommitWidget() {
// TODO(crbug.com/1291592): Hook this up with the WM's window positioning
// logic.
- if (needs_layout_on_show_)
+ if (needs_layout_on_show_) {
widget_->CenterWindow(GetWidgetBoundsFromVisibleBounds().size());
+ needs_layout_on_show_ = false;
+ }
widget_->Show();
if (has_grab_)
diff --git a/chromium/components/exo/shell_surface_base.h b/chromium/components/exo/shell_surface_base.h
index aeb38988a52..192eefe3b2a 100644
--- a/chromium/components/exo/shell_surface_base.h
+++ b/chromium/components/exo/shell_surface_base.h
@@ -104,9 +104,6 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// Set icon for the surface.
void SetIcon(const gfx::ImageSkia& icon);
- // Sets the system modality.
- void SetSystemModal(bool system_modal);
-
// Set the application ID for the surface.
void SetApplicationId(const char* application_id);
@@ -161,6 +158,16 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// shell surface.
void SetWindowBounds(const gfx::Rect& bounds_in_screen);
+ // Set `restore_session_id_` and `restore_window_id_` to be the browser
+ // session id and restore id, respectively.
+ void SetRestoreInfo(int32_t restore_id, int32_t restore_window_id);
+
+ // Set `restore_window_id_source` to be the app id for Restore to fetch window
+ // id for.
+ void SetRestoreInfoWithWindowIdSource(
+ int32_t restore_id,
+ const std::string& restore_window_id_source);
+
// Returns a trace value representing the state of the surface.
std::unique_ptr<base::trace_event::TracedValue> AsTracedValue() const;
@@ -221,6 +228,7 @@ class ShellSurfaceBase : public SurfaceTreeHost,
void SetInitialWorkspace(const char* initial_workspace) override;
void Pin(bool trusted) override;
void Unpin() override;
+ void SetSystemModal(bool system_modal) override;
// SurfaceObserver:
void OnSurfaceDestroying(Surface* surface) override;
@@ -240,6 +248,7 @@ class ShellSurfaceBase : public SurfaceTreeHost,
views::View* GetContentsView() override;
std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
views::Widget* widget) override;
+ bool ShouldSaveWindowPlacement() const override;
bool WidgetHasHitTestMask() const override;
void GetWidgetHitTestMask(SkPath* mask) const override;
@@ -445,6 +454,13 @@ class ShellSurfaceBase : public SurfaceTreeHost,
bool in_extended_drag_ = false;
absl::optional<std::string> initial_workspace_;
+ // Restore members. These pass window restore related ids from exo clients,
+ // e.g. Lacros, so that the window can be created with the correct restore
+ // info looked up using the ids.
+ absl::optional<int32_t> restore_session_id_;
+ absl::optional<int32_t> restore_window_id_;
+ absl::optional<std::string> restore_window_id_source_;
+
// Overlay members.
std::unique_ptr<views::Widget> overlay_widget_;
bool skip_ime_processing_ = false;
diff --git a/chromium/components/exo/shell_surface_unittest.cc b/chromium/components/exo/shell_surface_unittest.cc
index 30ffcef9680..2cd2bcfa0e2 100644
--- a/chromium/components/exo/shell_surface_unittest.cc
+++ b/chromium/components/exo/shell_surface_unittest.cc
@@ -23,6 +23,7 @@
#include "base/callback.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
+#include "components/app_restore/window_properties.h"
#include "components/exo/buffer.h"
#include "components/exo/permission.h"
#include "components/exo/shell_surface_util.h"
@@ -1731,6 +1732,28 @@ TEST_F(ShellSurfaceTest, ServerStartResize) {
size.width() + kDragAmount);
}
+// Make sure that dragging to another display will update the origin to
+// correct value.
+TEST_F(ShellSurfaceTest, UpdateBoundsWhenDraggedToAnotherDisplay) {
+ UpdateDisplay("800x600, 800x600");
+ std::unique_ptr<ShellSurface> shell_surface =
+ test::ShellSurfaceBuilder({64, 64}).BuildShellSurface();
+ ui::test::EventGenerator* event_generator = GetEventGenerator();
+ shell_surface->SetWindowBounds({0, 0, 64, 64});
+
+ gfx::Point last_origin;
+ auto origin_change = [&](const gfx::Point& p) { last_origin = p; };
+
+ shell_surface->set_origin_change_callback(
+ base::BindLambdaForTesting(origin_change));
+ event_generator->MoveMouseTo(1, 1);
+ event_generator->PressLeftButton();
+ shell_surface->StartMove();
+ event_generator->MoveMouseTo(801, 1);
+ event_generator->ReleaseLeftButton();
+ EXPECT_EQ(last_origin, gfx::Point(800, 0));
+}
+
// Make sure that resize shadow does not update until commit when the window
// property |aura::client::kUseWindowBoundsForShadow| is false.
TEST_F(ShellSurfaceTest, ResizeShadowIndependentBounds) {
@@ -2263,6 +2286,51 @@ TEST_F(ShellSurfaceTest, InitialCenteredBoundsWithConfigure) {
EXPECT_EQ(expected, shell_surface->GetWidget()->GetWindowBoundsInScreen());
}
+// Test that restore info is set correctly.
+TEST_F(ShellSurfaceTest, SetRestoreInfo) {
+ int32_t restore_session_id = 200;
+ int32_t restore_window_id = 100;
+
+ gfx::Size size(20, 30);
+ auto shell_surface =
+ test::ShellSurfaceBuilder(size).SetNoCommit().BuildShellSurface();
+
+ shell_surface->SetRestoreInfo(restore_session_id, restore_window_id);
+ shell_surface->Restore();
+ shell_surface->root_surface()->Commit();
+
+ EXPECT_TRUE(shell_surface->GetWidget()->IsVisible());
+ EXPECT_EQ(restore_session_id,
+ shell_surface->GetWidget()->GetNativeWindow()->GetProperty(
+ app_restore::kWindowIdKey));
+ EXPECT_EQ(restore_window_id,
+ shell_surface->GetWidget()->GetNativeWindow()->GetProperty(
+ app_restore::kRestoreWindowIdKey));
+}
+
+// Test that restore id is set correctly.
+TEST_F(ShellSurfaceTest, SetRestoreInfoWithWindowIdSource) {
+ int32_t restore_session_id = 200;
+ const std::string app_id = "app_id";
+
+ gfx::Size size(20, 30);
+ auto shell_surface =
+ test::ShellSurfaceBuilder(size).SetNoCommit().BuildShellSurface();
+
+ shell_surface->SetRestoreInfoWithWindowIdSource(restore_session_id, app_id);
+ shell_surface->Restore();
+ shell_surface->root_surface()->Commit();
+
+ EXPECT_TRUE(shell_surface->GetWidget()->IsVisible());
+
+ // FetchRestoreWindowId will return 0, because no app with id "app_id" is
+ // installed.
+ EXPECT_EQ(0, shell_surface->GetWidget()->GetNativeWindow()->GetProperty(
+ app_restore::kRestoreWindowIdKey));
+ EXPECT_EQ(app_id, *shell_surface->GetWidget()->GetNativeWindow()->GetProperty(
+ app_restore::kAppIdKey));
+}
+
// Surfaces without non-client view should not crash.
TEST_F(ShellSurfaceTest, NoNonClientViewWithConfigure) {
// Popup windows don't have a non-client view.
@@ -2287,4 +2355,35 @@ TEST_F(ShellSurfaceTest, WindowIsResizableWithEmptySizeConstraints) {
EXPECT_TRUE(shell_surface->GetWidget()->widget_delegate()->CanResize());
}
+TEST_F(ShellSurfaceTest, SetSystemModal) {
+ std::unique_ptr<ShellSurface> shell_surface =
+ test::ShellSurfaceBuilder({256, 256})
+ .SetMaximumSize(gfx::Size(10, 10))
+ .SetUseSystemModalContainer()
+ .SetNoCommit()
+ .BuildShellSurface();
+
+ shell_surface->SetSystemModal(true);
+ shell_surface->root_surface()->Commit();
+
+ EXPECT_EQ(ui::MODAL_TYPE_SYSTEM, shell_surface->GetModalType());
+ EXPECT_FALSE(shell_surface->frame_enabled());
+}
+
+TEST_F(ShellSurfaceTest, PipInitialPosition) {
+ std::unique_ptr<ShellSurface> shell_surface =
+ test::ShellSurfaceBuilder({256, 256})
+ .SetMaximumSize(gfx::Size(10, 10))
+ .SetUseSystemModalContainer()
+ .SetNoCommit()
+ .BuildShellSurface();
+ shell_surface->SetWindowBounds(gfx::Rect(20, 20, 256, 256));
+ shell_surface->SetPip();
+ shell_surface->root_surface()->Commit();
+ // PIP positioner place the PIP window to the edge that is closer to the given
+ // position
+ EXPECT_EQ(gfx::Rect(8, 20, 256, 256),
+ shell_surface->GetWidget()->GetWindowBoundsInScreen());
+}
+
} // namespace exo
diff --git a/chromium/components/exo/sub_surface.cc b/chromium/components/exo/sub_surface.cc
index 510e9896af3..175093636b0 100644
--- a/chromium/components/exo/sub_surface.cc
+++ b/chromium/components/exo/sub_surface.cc
@@ -124,6 +124,12 @@ void SubSurface::OnSetParent(Surface* parent, const gfx::Point&) {
surface_->window()->SetProperty(aura::client::kSkipImeProcessing, true);
}
+Capabilities* SubSurface::GetCapabilities() {
+ if (parent_)
+ return parent_->GetCapabilities();
+ return nullptr;
+}
+
////////////////////////////////////////////////////////////////////////////////
// SurfaceObserver overrides:
diff --git a/chromium/components/exo/sub_surface.h b/chromium/components/exo/sub_surface.h
index 7476b13c161..0903d19ce65 100644
--- a/chromium/components/exo/sub_surface.h
+++ b/chromium/components/exo/sub_surface.h
@@ -88,6 +88,8 @@ class SubSurface : public SurfaceDelegate,
void SetInitialWorkspace(const char* initial_workspace) override {}
void Pin(bool trusted) override {}
void Unpin() override {}
+ void SetSystemModal(bool system_modal) override {}
+ Capabilities* GetCapabilities() override;
// Overridden from SurfaceObserver:
void OnSurfaceDestroying(Surface* surface) override;
diff --git a/chromium/components/exo/sub_surface_unittest.cc b/chromium/components/exo/sub_surface_unittest.cc
index e8b7e838e04..c4900c5f773 100644
--- a/chromium/components/exo/sub_surface_unittest.cc
+++ b/chromium/components/exo/sub_surface_unittest.cc
@@ -116,12 +116,18 @@ TEST_F(SubSurfaceTest, PlaceBelow) {
}
TEST_F(SubSurfaceTest, ParentDamageOnReorder) {
+ gfx::Size buffer_size(800, 600);
+ auto buffer = std::make_unique<Buffer>(
+ exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
auto surface_tree_host = std::make_unique<SurfaceTreeHost>("SubSurfaceTest");
LayerTreeFrameSinkHolder* frame_sink_holder =
surface_tree_host->layer_tree_frame_sink_holder();
auto parent = std::make_unique<Surface>();
- parent->SetViewport({800.f, 600.f});
+ parent->Attach(buffer.get());
+ // Set the overlay priority hint to low to prevent a texture draw quad from
+ // being used.
+ parent->SetOverlayPriorityHint(OverlayPriority::LOW);
auto surface1 = std::make_unique<Surface>();
auto surface2 = std::make_unique<Surface>();
auto non_sibling_surface = std::make_unique<Surface>();
diff --git a/chromium/components/exo/surface.cc b/chromium/components/exo/surface.cc
index dd8af5b8a01..cd7cd4b3996 100644
--- a/chromium/components/exo/surface.cc
+++ b/chromium/components/exo/surface.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
#include "base/callback_helpers.h"
#include "base/containers/adapters.h"
#include "base/logging.h"
@@ -30,6 +31,7 @@
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/surface_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/quads/tile_draw_quad.h"
#include "components/viz/common/resources/resource_id.h"
#include "media/media_buildflags.h"
#include "third_party/khronos/GLES2/gl2.h"
@@ -542,6 +544,11 @@ void Surface::SetOverlayPriorityHint(OverlayPriority hint) {
pending_state_.overlay_priority_hint = hint;
}
+void Surface::SetBackgroundColor(absl::optional<SkColor> background_color) {
+ TRACE_EVENT0("exo", "Surface::SetBackgroundColor");
+ pending_state_.basic_state.background_color = background_color;
+}
+
void Surface::SetViewport(const gfx::SizeF& viewport) {
TRACE_EVENT1("exo", "Surface::SetViewport", "viewport", viewport.ToString());
@@ -1220,10 +1227,7 @@ void Surface::UpdateResource(FrameSinkResourceManager* resource_manager) {
std::move(state_.per_commit_explicit_release_callback_))) {
current_resource_has_alpha_ =
FormatHasAlpha(state_.buffer->buffer()->GetFormat());
- // Setting colors for YUV buffers has been problematic in the past. See
- // crrev.com/c/2331769
- if (state_.buffer->buffer()->GetFormat() != gfx::BufferFormat::YVU_420)
- current_resource_.color_space = state_.basic_state.color_space;
+ current_resource_.color_space = state_.basic_state.color_space;
} else {
current_resource_.id = viz::kInvalidResourceId;
// Use the buffer's size, so the AppendContentsToFrame() will append
@@ -1418,7 +1422,9 @@ void Surface::AppendContentsToFrame(const gfx::PointF& origin,
}
SkColor background_color = SK_ColorTRANSPARENT;
- if (current_resource_has_alpha_ && are_contents_opaque)
+ if (state_.basic_state.background_color.has_value())
+ background_color = state_.basic_state.background_color.value();
+ else if (current_resource_has_alpha_ && are_contents_opaque)
background_color = SK_ColorBLACK; // Avoid writing alpha < 1
// If this surface is being replaced by a SurfaceId emit a SurfaceDrawQuad.
@@ -1449,61 +1455,80 @@ void Surface::AppendContentsToFrame(const gfx::PointF& origin,
// A resource was still produced for this so we still need to release it
// later.
frame->resource_list.push_back(current_resource_);
- } else if (state_.basic_state.alpha) {
- // Texture quad is only needed if buffer is not fully transparent.
- viz::TextureDrawQuad* texture_quad =
- render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
- float vertex_opacity[4] = {1.0, 1.0, 1.0, 1.0};
- texture_quad->SetNew(
- quad_state, quad_rect, quad_rect,
- /* needs_blending=*/!are_contents_opaque, current_resource_.id,
- /* premultiplied_alpha=*/true, uv_crop.origin(),
- uv_crop.bottom_right(), background_color, vertex_opacity,
- /* y_flipped=*/false, /* nearest_neighbor=*/false,
- state_.basic_state.only_visible_on_secure_output,
- gfx::ProtectedVideoType::kClear);
- if (current_resource_.is_overlay_candidate)
- texture_quad->set_resource_size_in_pixels(current_resource_.size);
-
- switch (state_.overlay_priority_hint) {
- case OverlayPriority::LOW:
- texture_quad->overlay_priority_hint = viz::OverlayPriority::kLow;
- break;
- case OverlayPriority::REQUIRED:
- texture_quad->overlay_priority_hint = viz::OverlayPriority::kRequired;
- break;
- case OverlayPriority::REGULAR:
- texture_quad->overlay_priority_hint = viz::OverlayPriority::kRegular;
- break;
- }
+ } else if (state_.basic_state.alpha != 0.0f) {
+ // Draw quad is only needed if buffer is not fully transparent.
+
+ const bool requires_texture_draw_quad =
+ state_.basic_state.only_visible_on_secure_output ||
+ state_.overlay_priority_hint != OverlayPriority::LOW;
+
+ if (requires_texture_draw_quad) {
+ viz::TextureDrawQuad* texture_quad =
+ render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
+ float vertex_opacity[4] = {1.0, 1.0, 1.0, 1.0};
+ texture_quad->SetNew(
+ quad_state, quad_rect, quad_rect,
+ /* needs_blending=*/!are_contents_opaque, current_resource_.id,
+ /* premultiplied*/ true, uv_crop.origin(), uv_crop.bottom_right(),
+ background_color, vertex_opacity,
+ /* flipped=*/false, /* nearest*/ false,
+ state_.basic_state.only_visible_on_secure_output,
+ gfx::ProtectedVideoType::kClear);
+ if (current_resource_.is_overlay_candidate)
+ texture_quad->set_resource_size_in_pixels(current_resource_.size);
+
+ switch (state_.overlay_priority_hint) {
+ case OverlayPriority::LOW:
+ texture_quad->overlay_priority_hint = viz::OverlayPriority::kLow;
+ break;
+ case OverlayPriority::REQUIRED:
+ texture_quad->overlay_priority_hint =
+ viz::OverlayPriority::kRequired;
+ break;
+ case OverlayPriority::REGULAR:
+ texture_quad->overlay_priority_hint =
+ viz::OverlayPriority::kRegular;
+ break;
+ }
#if BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
- if (state_.basic_state.only_visible_on_secure_output &&
- state_.buffer.has_value() && state_.buffer->buffer() &&
- state_.buffer->buffer()->NeedsHardwareProtection()) {
- texture_quad->protected_video_type =
- gfx::ProtectedVideoType::kHardwareProtected;
- }
+ if (state_.basic_state.only_visible_on_secure_output &&
+ state_.buffer.has_value() && state_.buffer->buffer() &&
+ state_.buffer->buffer()->NeedsHardwareProtection()) {
+ texture_quad->protected_video_type =
+ gfx::ProtectedVideoType::kHardwareProtected;
+ }
#endif // BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
- frame->resource_list.push_back(current_resource_);
- if (!damage_rect.IsEmpty()) {
- texture_quad->damage_rect = gfx::ToEnclosedRect(damage_rect);
- render_pass->has_per_quad_damage = true;
- // Clear handled damage so it will not be added to the |render_pass|.
- damage_rect = gfx::RectF();
+ if (!damage_rect.IsEmpty()) {
+ texture_quad->damage_rect = gfx::ToEnclosedRect(damage_rect);
+ render_pass->has_per_quad_damage = true;
+ // Clear handled damage so it will not be added to the |render_pass|.
+ damage_rect = gfx::RectF();
+ }
+ } else {
+ viz::TileDrawQuad* tile_quad =
+ render_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
+ // TODO(crbug.com/1339335): Support AA quads coming from exo.
+ constexpr bool kForceAntiAliasingOff = true;
+ tile_quad->SetNew(
+ quad_state, quad_rect, quad_rect,
+ /* needs_blending=*/!are_contents_opaque, current_resource_.id,
+ gfx::ScaleRect(uv_crop, current_resource_.size.width(),
+ current_resource_.size.height()),
+ current_resource_.size,
+ /* is_premultiplied=*/true,
+ /* nearest_neighbor */ false, kForceAntiAliasingOff);
}
+ frame->resource_list.push_back(current_resource_);
}
- } else if (state_.buffer.has_value() && state_.buffer->buffer()) {
- SkColor color = state_.buffer->buffer()->GetColor().toSkColor();
- viz::SolidColorDrawQuad* solid_quad =
- render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
- solid_quad->SetNew(quad_state, quad_rect, quad_rect, color,
- false /* force_anti_aliasing_off */);
} else {
+ SkColor color = state_.buffer.has_value() && state_.buffer->buffer()
+ ? state_.buffer->buffer()->GetColor().toSkColor()
+ : SK_ColorBLACK;
viz::SolidColorDrawQuad* solid_quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
- solid_quad->SetNew(quad_state, quad_rect, quad_rect, SK_ColorBLACK,
+ solid_quad->SetNew(quad_state, quad_rect, quad_rect, color,
false /* force_anti_aliasing_off */);
}
@@ -1512,29 +1537,31 @@ void Surface::AppendContentsToFrame(const gfx::PointF& origin,
void Surface::UpdateContentSize() {
gfx::SizeF content_size;
- if (!state_.basic_state.viewport.IsEmpty()) {
- content_size = state_.basic_state.viewport;
- } else if (!state_.basic_state.crop.IsEmpty()) {
- DLOG_IF(WARNING, !base::IsValueInRangeForNumericType<int>(
- state_.basic_state.crop.width()) ||
- !base::IsValueInRangeForNumericType<int>(
- state_.basic_state.crop.height()))
- << "Crop rectangle size (" << state_.basic_state.crop.size().ToString()
- << ") most be expressible using integers when viewport is not set";
- content_size = state_.basic_state.crop.size();
- } else {
- content_size = gfx::ScaleSize(
- gfx::SizeF(ToTransformedSize(
- state_.buffer.has_value() ? state_.buffer->size() : gfx::Size(),
- state_.basic_state.buffer_transform)),
- 1.0f / state_.basic_state.buffer_scale);
- }
-
// Enable/disable sub-surface based on if it has contents.
- if (has_contents())
+ if (has_contents()) {
+ if (!state_.basic_state.viewport.IsEmpty()) {
+ content_size = state_.basic_state.viewport;
+ } else if (!state_.basic_state.crop.IsEmpty()) {
+ DLOG_IF(WARNING, !base::IsValueInRangeForNumericType<int>(
+ state_.basic_state.crop.width()) ||
+ !base::IsValueInRangeForNumericType<int>(
+ state_.basic_state.crop.height()))
+ << "Crop rectangle size ("
+ << state_.basic_state.crop.size().ToString()
+ << ") most be expressible using integers when viewport is not set";
+ content_size = state_.basic_state.crop.size();
+ } else {
+ content_size = gfx::ScaleSize(
+ gfx::SizeF(ToTransformedSize(
+ state_.buffer.has_value() ? state_.buffer->size() : gfx::Size(),
+ state_.basic_state.buffer_transform)),
+ 1.0f / state_.basic_state.buffer_scale);
+ }
+
window_->Show();
- else
+ } else {
window_->Hide();
+ }
if (content_size_ != content_size) {
content_size_ = content_size;
@@ -1601,4 +1628,23 @@ void Surface::ThrottleFrameRate(bool on) {
observer.ThrottleFrameRate(on);
}
+void Surface::SetKeyboardShortcutsInhibited(bool inhibited) {
+ if (keyboard_shortcuts_inhibited_ == inhibited)
+ return;
+
+ keyboard_shortcuts_inhibited_ = inhibited;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // Also set kCanConsumeSystemKeysKey property, so that the key event
+ // is also forwarded to exo::Keyboard.
+ // TODO(hidehiko): Support capability on migrating ARC/Crostini.
+ window_->SetProperty(ash::kCanConsumeSystemKeysKey, inhibited);
+#endif
+}
+
+Capabilities* Surface::GetCapabilities() {
+ if (delegate_)
+ return delegate_->GetCapabilities();
+ return nullptr;
+}
+
} // namespace exo
diff --git a/chromium/components/exo/surface.h b/chromium/components/exo/surface.h
index fc7e2b69765..90ace2573ba 100644
--- a/chromium/components/exo/surface.h
+++ b/chromium/components/exo/surface.h
@@ -47,7 +47,7 @@ namespace gfx {
class ColorSpace;
class GpuFence;
struct PresentationFeedback;
-}
+} // namespace gfx
namespace viz {
class CompositorFrame;
@@ -55,6 +55,7 @@ class CompositorFrame;
namespace exo {
class Buffer;
+class Capabilities;
class FrameSinkResourceManager;
class SurfaceObserver;
@@ -180,6 +181,10 @@ class Surface final : public ui::PropertyHandler {
void SetRoundedCorners(const gfx::RRectF& rounded_corners_bounds);
void SetOverlayPriorityHint(OverlayPriority hint);
+ // Sets the background color that shall be associated with the next buffer
+ // commit.
+ void SetBackgroundColor(absl::optional<SkColor> background_color);
+
// This sets the surface viewport for scaling.
void SetViewport(const gfx::SizeF& viewport);
@@ -407,6 +412,20 @@ class Surface final : public ui::PropertyHandler {
// Starts or ends throttling on the surface.
void ThrottleFrameRate(bool on);
+ // If true is set, if this window has a focus, key events should be sent to
+ // the app, even if it is an ash shortcut (with some exceptions).
+ // See exo::Keyboard for more details.
+ void SetKeyboardShortcutsInhibited(bool inhibited);
+
+ // Returns whether keyboard shortcuts are inhibited.
+ bool is_keyboard_shortcuts_inhibited() const {
+ return keyboard_shortcuts_inhibited_;
+ }
+
+ // Returns the capability set associated with this surface, or nullptr if one
+ // can not be determined. See go/secure-exo-ids for more details.
+ Capabilities* GetCapabilities();
+
private:
struct State {
State();
@@ -426,8 +445,11 @@ class Surface final : public ui::PropertyHandler {
SkBlendMode blend_mode = SkBlendMode::kSrcOver;
float alpha = 1.0f;
gfx::Vector2d offset;
- gfx::ColorSpace color_space;
+ gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
bool is_tracking_occlusion = false;
+ // Represents optional background color that must be associated with the
+ // next buffer commit.
+ absl::optional<SkColor> background_color;
};
class BufferAttachment {
public:
@@ -609,6 +631,8 @@ class Surface final : public ui::PropertyHandler {
gfx::Size embedded_surface_size_;
LeaveEnterCallback leave_enter_callback_;
+
+ bool keyboard_shortcuts_inhibited_ = false;
};
class ScopedSurface {
diff --git a/chromium/components/exo/surface_delegate.h b/chromium/components/exo/surface_delegate.h
index 51a90109c10..73baed6217c 100644
--- a/chromium/components/exo/surface_delegate.h
+++ b/chromium/components/exo/surface_delegate.h
@@ -10,6 +10,7 @@
#include "ui/gfx/geometry/size_f.h"
namespace exo {
+class Capabilities;
class Surface;
// Frame types that can be used to decorate a surface.
@@ -101,6 +102,13 @@ class SurfaceDelegate {
// Releases the pinned mode and allows the user to do other things again.
virtual void Unpin() = 0;
+ // Sets the system modality.
+ virtual void SetSystemModal(bool modal) = 0;
+
+ // Returns the capability relevant to this surface. See go/secure-exo-ids for
+ // more information.
+ virtual Capabilities* GetCapabilities() = 0;
+
protected:
virtual ~SurfaceDelegate() {}
};
diff --git a/chromium/components/exo/surface_tree_host.cc b/chromium/components/exo/surface_tree_host.cc
index 197a9a296b0..1693ba97fb2 100644
--- a/chromium/components/exo/surface_tree_host.cc
+++ b/chromium/components/exo/surface_tree_host.cc
@@ -108,13 +108,14 @@ SurfaceTreeHost::SurfaceTreeHost(const std::string& window_name)
host_window_->SetEventTargetingPolicy(
aura::EventTargetingPolicy::kDescendantsOnly);
host_window_->SetEventTargeter(std::make_unique<CustomWindowTargeter>(this));
- layer_tree_frame_sink_holder_ = std::make_unique<LayerTreeFrameSinkHolder>(
- this, host_window_->CreateLayerTreeFrameSink());
+
context_provider_ = aura::Env::GetInstance()
->context_factory()
->SharedMainThreadContextProvider();
DCHECK(context_provider_);
context_provider_->AddObserver(this);
+ layer_tree_frame_sink_holder_ = std::make_unique<LayerTreeFrameSinkHolder>(
+ this, host_window_->CreateLayerTreeFrameSink(), context_provider_);
}
SurfaceTreeHost::~SurfaceTreeHost() {
@@ -197,6 +198,11 @@ void SurfaceTreeHost::DidPresentCompositorFrame(
active_presentation_callbacks_.erase(it);
}
+void SurfaceTreeHost::SetCapabilities(Capabilities* capabilities) {
+ DCHECK(capabilities_ == nullptr);
+ capabilities_ = capabilities;
+}
+
////////////////////////////////////////////////////////////////////////////////
// SurfaceDelegate overrides:
@@ -220,6 +226,11 @@ void SurfaceTreeHost::OnNewOutputAdded() {
UpdateDisplayOnTree();
}
+Capabilities* SurfaceTreeHost::GetCapabilities() {
+ DCHECK(capabilities_);
+ return capabilities_;
+}
+
////////////////////////////////////////////////////////////////////////////////
// display::DisplayObserver:
void SurfaceTreeHost::OnDisplayMetricsChanged(const display::Display& display,
@@ -368,7 +379,7 @@ viz::CompositorFrame SurfaceTreeHost::PrepareToSubmitCompositorFrame() {
// We can immediately delete the old LayerTreeFrameSinkHolder because all of
// it's resources are lost anyways.
layer_tree_frame_sink_holder_ = std::make_unique<LayerTreeFrameSinkHolder>(
- this, host_window_->CreateLayerTreeFrameSink());
+ this, host_window_->CreateLayerTreeFrameSink(), context_provider_);
}
viz::CompositorFrame frame;
diff --git a/chromium/components/exo/surface_tree_host.h b/chromium/components/exo/surface_tree_host.h
index 43137691244..241c54dd569 100644
--- a/chromium/components/exo/surface_tree_host.h
+++ b/chromium/components/exo/surface_tree_host.h
@@ -114,6 +114,8 @@ class SurfaceTreeHost : public SurfaceDelegate,
void SetInitialWorkspace(const char* initial_workspace) override {}
void Pin(bool trusted) override {}
void Unpin() override {}
+ void SetSystemModal(bool system_modal) override {}
+ Capabilities* GetCapabilities() override;
// display::DisplayObserver:
void OnDisplayMetricsChanged(const display::Display& display,
@@ -126,6 +128,8 @@ class SurfaceTreeHost : public SurfaceDelegate,
client_submits_surfaces_in_pixel_coordinates_ = enabled;
}
+ void SetCapabilities(Capabilities* capabilities);
+
protected:
void UpdateDisplayOnTree();
@@ -181,6 +185,8 @@ class SurfaceTreeHost : public SurfaceDelegate,
bool client_submits_surfaces_in_pixel_coordinates_ = false;
+ Capabilities* capabilities_ = nullptr;
+
base::WeakPtrFactory<SurfaceTreeHost> weak_ptr_factory_{this};
};
diff --git a/chromium/components/exo/surface_unittest.cc b/chromium/components/exo/surface_unittest.cc
index 3ac9f223646..e69f8592213 100644
--- a/chromium/components/exo/surface_unittest.cc
+++ b/chromium/components/exo/surface_unittest.cc
@@ -186,6 +186,7 @@ TEST_P(SurfaceTest, Attach) {
surface->Attach(buffer.get());
EXPECT_TRUE(surface->HasPendingAttachedBuffer());
surface->Commit();
+ EXPECT_EQ(gfx::SizeF(buffer_size), surface->content_size());
// Commit without calling Attach() should have no effect.
surface->Commit();
@@ -196,6 +197,7 @@ TEST_P(SurfaceTest, Attach) {
surface->Attach(nullptr);
EXPECT_FALSE(surface->HasPendingAttachedBuffer());
surface->Commit();
+ EXPECT_TRUE(surface->content_size().IsEmpty());
// LayerTreeFrameSinkHolder::ReclaimResources() gets called via
// CompositorFrameSinkClient interface. We need to wait here for the mojo
// call to finish so that the release callback finishes running before
@@ -489,7 +491,7 @@ TEST_P(SurfaceTest, MAYBE_SetOpaqueRegion) {
frame.render_pass_list.back()->quad_list.back());
EXPECT_FALSE(texture_draw_quad->ShouldDrawWithBlending());
- EXPECT_EQ(SK_ColorBLACK, texture_draw_quad->background_color);
+ EXPECT_EQ(SkColors::kBlack, texture_draw_quad->background_color);
EXPECT_EQ(gfx::Rect(buffer_size), ToTargetSpaceDamage(frame));
}
@@ -506,7 +508,7 @@ TEST_P(SurfaceTest, MAYBE_SetOpaqueRegion) {
auto* texture_draw_quad = viz::TextureDrawQuad::MaterialCast(
frame.render_pass_list.back()->quad_list.back());
EXPECT_TRUE(texture_draw_quad->ShouldDrawWithBlending());
- EXPECT_EQ(SK_ColorTRANSPARENT, texture_draw_quad->background_color);
+ EXPECT_EQ(SkColors::kTransparent, texture_draw_quad->background_color);
EXPECT_EQ(gfx::Rect(buffer_size), ToTargetSpaceDamage(frame));
}
@@ -805,6 +807,11 @@ TEST_P(SurfaceTest, SetViewport) {
const viz::CompositorFrame& frame = GetFrameFromSurface(shell_surface.get());
ASSERT_EQ(1u, frame.render_pass_list.size());
EXPECT_EQ(ToPixel(gfx::Rect(0, 0, 512, 512)), GetCompleteDamage(frame));
+
+ // This will make the surface have no content regardless of the viewport.
+ surface->Attach(nullptr);
+ surface->Commit();
+ EXPECT_TRUE(surface->content_size().IsEmpty());
}
TEST_P(SurfaceTest, SubpixelCoordinate) {
@@ -915,6 +922,11 @@ TEST_P(SurfaceTest, SetCrop) {
const viz::CompositorFrame& frame = GetFrameFromSurface(shell_surface.get());
ASSERT_EQ(1u, frame.render_pass_list.size());
EXPECT_EQ(ToPixel(gfx::Rect(0, 0, 12, 12)), GetCompleteDamage(frame));
+
+ // This will make the surface have no content regardless of the crop.
+ surface->Attach(nullptr);
+ surface->Commit();
+ EXPECT_TRUE(surface->content_size().IsEmpty());
}
void SurfaceTest::SetCropAndBufferTransformHelperTransformAndTest(
@@ -1262,8 +1274,8 @@ TEST_P(SurfaceTest, ScaledSurfaceQuad) {
TEST_P(SurfaceTest, ColorBufferAlpha) {
gfx::Size buffer_size(1, 1);
- constexpr SkColor4f kBuffColorExpected[] = {{1.f, 0.5f, 0.f, 1.f},
- {0.f, 0.5f, 1.f, 0.f}};
+ constexpr SkColor4f kBuffColorExpected[] = {{1.f, 128.0f / 255.0f, 0.f, 1.f},
+ {0.f, 128.0f / 255.0f, 1.f, 0.f}};
constexpr bool kExpectedOpaque[] = {true, false};
for (size_t i = 0; i < std::size(kBuffColorExpected); i++) {
auto buffer =
@@ -1294,8 +1306,7 @@ TEST_P(SurfaceTest, ColorBufferAlpha) {
EXPECT_EQ(kExpectedOpaque[i],
draw_quad->shared_quad_state->are_contents_opaque);
auto* solid_color_quad = viz::SolidColorDrawQuad::MaterialCast(draw_quad);
- EXPECT_EQ(kBuffColorExpected[i].SkColor4f::toSkColor(),
- solid_color_quad->color);
+ EXPECT_EQ(kBuffColorExpected[i], solid_color_quad->color);
}
}
}
diff --git a/chromium/components/exo/text_input.cc b/chromium/components/exo/text_input.cc
index 54acd309255..db5c0883f4b 100644
--- a/chromium/components/exo/text_input.cc
+++ b/chromium/components/exo/text_input.cc
@@ -360,8 +360,8 @@ bool TextInput::SetAutocorrectRange(const gfx::Range& range) {
return false;
}
-absl::optional<ui::GrammarFragment> TextInput::GetGrammarFragment(
- const gfx::Range& range) {
+absl::optional<ui::GrammarFragment> TextInput::GetGrammarFragmentAtCursor()
+ const {
// TODO(https://crbug.com/1201454): Implement this method.
NOTIMPLEMENTED_LOG_ONCE();
return absl::nullopt;
diff --git a/chromium/components/exo/text_input.h b/chromium/components/exo/text_input.h
index 2f7b1806872..f31d7b87fcd 100644
--- a/chromium/components/exo/text_input.h
+++ b/chromium/components/exo/text_input.h
@@ -167,8 +167,8 @@ class TextInput : public ui::TextInputClient,
gfx::Range GetAutocorrectRange() const override;
gfx::Rect GetAutocorrectCharacterBounds() const override;
bool SetAutocorrectRange(const gfx::Range& range) override;
- absl::optional<ui::GrammarFragment> GetGrammarFragment(
- const gfx::Range& range) override;
+ absl::optional<ui::GrammarFragment> GetGrammarFragmentAtCursor()
+ const override;
bool ClearGrammarFragments(const gfx::Range& range) override;
bool AddGrammarFragments(
const std::vector<ui::GrammarFragment>& fragments) override;
diff --git a/chromium/components/exo/touch.cc b/chromium/components/exo/touch.cc
index 6ad15885bf5..298bcad58f9 100644
--- a/chromium/components/exo/touch.cc
+++ b/chromium/components/exo/touch.cc
@@ -62,11 +62,9 @@ bool Touch::HasStylusDelegate() const {
// ui::EventHandler overrides:
void Touch::OnTouchEvent(ui::TouchEvent* event) {
- if (seat_->was_shutdown())
+ if (seat_->was_shutdown() || event->handled())
return;
- seat_->SetLastPointerLocation(event->root_location_f());
-
bool send_details = false;
const int touch_pointer_id = event->pointer_details().id;
diff --git a/chromium/components/exo/touch_unittest.cc b/chromium/components/exo/touch_unittest.cc
index a5692111acd..b9cd717d532 100644
--- a/chromium/components/exo/touch_unittest.cc
+++ b/chromium/components/exo/touch_unittest.cc
@@ -628,5 +628,38 @@ TEST_F(TouchTest, TouchMultiple2Surfaces) {
touch.reset();
}
+TEST_F(TouchTest, IgnoresHandledEvents) {
+ // A very dumb handler that simply marks all events as handled. This is needed
+ // allows us to mark a mouse event as handled as it gets processed by the
+ // event processor.
+ class SetHandledHandler : public ui::EventHandler {
+ void OnTouchEvent(ui::TouchEvent* event) override { event->SetHandled(); }
+ };
+ SetHandledHandler handler;
+ ash::Shell::Get()->AddPreTargetHandler(&handler);
+
+ Seat seat(std::make_unique<TestDataExchangeDelegate>());
+
+ testing::NiceMock<MockTouchDelegate> touch_delegate;
+ std::unique_ptr<Touch> touch(new Touch(&touch_delegate, &seat));
+
+ // Make origin into a real window so the touch can click it
+ std::unique_ptr<ShellSurface> shell_surface =
+ test::ShellSurfaceBuilder({10, 10}).BuildShellSurface();
+
+ ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+
+ // The SetHandlerHandler should have marked the event as processed. Therefore
+ // the event should simply be ignored.
+ EXPECT_CALL(touch_delegate, OnTouchFrame()).Times(0);
+
+ generator.GestureTapAt(shell_surface->surface_for_testing()
+ ->window()
+ ->GetBoundsInScreen()
+ .CenterPoint());
+
+ ash::Shell::Get()->RemovePreTargetHandler(&handler);
+}
+
} // namespace
} // namespace exo
diff --git a/chromium/components/exo/ui_lock_controller.cc b/chromium/components/exo/ui_lock_controller.cc
index f701c06ec47..5b24a0c5db4 100644
--- a/chromium/components/exo/ui_lock_controller.cc
+++ b/chromium/components/exo/ui_lock_controller.cc
@@ -10,6 +10,7 @@
#include "ash/constants/app_types.h"
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/keyboard/keyboard_controller.h"
+#include "ash/public/cpp/session/session_controller.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_state_observer.h"
@@ -24,6 +25,7 @@
#include "components/exo/shell_surface_util.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
+#include "components/exo/wm_helper_chromeos.h"
#include "components/fullscreen_control/fullscreen_control_popup.h"
#include "components/fullscreen_control/subtle_notification_view.h"
#include "components/strings/grit/components_strings.h"
@@ -31,6 +33,8 @@
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/user_activity/user_activity_detector.h"
+#include "ui/base/user_activity/user_activity_observer.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/dom/dom_code.h"
@@ -67,6 +71,8 @@ constexpr float kExitPopupHideHeight = 150.f;
// being interrupted, don't show it again until this long has passed.
constexpr auto kPointerCaptureNotificationCooldown = base::Minutes(5);
+constexpr auto kReshowNotificationsWhenIdleFor = base::Minutes(5);
+
constexpr int kUILockControllerSeatObserverPriority = 1;
static_assert(
exo::Seat::IsValidObserverPriority(kUILockControllerSeatObserverPriority),
@@ -163,9 +169,13 @@ void ExitFullscreen(aura::Window* window) {
// Owns the widgets for messages prompting to exit fullscreen/mouselock, and
// the exit popup. Owned as a window property.
-class ExitNotifier : public ui::EventHandler, public ash::WindowStateObserver {
+class ExitNotifier : public ui::EventHandler,
+ public exo::UILockController::Notifier,
+ public ash::WindowStateObserver {
public:
- explicit ExitNotifier(aura::Window* window) : window_(window) {
+ explicit ExitNotifier(exo::UILockController* controller, aura::Window* window)
+ : window_(window) {
+ controller_observation_.Observe(controller);
ash::WindowState* window_state = ash::WindowState::Get(window);
window_state_observation_.Observe(window_state);
if (window_state->IsFullscreen())
@@ -180,6 +190,32 @@ class ExitNotifier : public ui::EventHandler, public ash::WindowStateObserver {
ClosePointerCaptureNotification();
}
+ void OnPointerCaptureEnabled() {
+ pointer_is_captured_ = true;
+ MaybeShowPointerCaptureNotification();
+ }
+
+ void OnPointerCaptureDisabled() { pointer_is_captured_ = false; }
+
+ // If this window is currently in a state that would have triggered a
+ // notification when entered, re-show that notification as a reminder.
+ void NotifyAgain() override {
+ // Always reset the notification cooldown, to ensure notifications show in
+ // the case where pointer lock is not currently active but will be soon.
+ next_pointer_notify_time_ = base::TimeTicks::Now();
+
+ ash::WindowState* window_state = ash::WindowState::Get(window_);
+ if (window_state->IsFullscreen()) {
+ OnFullscreen();
+ } else if (pointer_is_captured_) {
+ MaybeShowPointerCaptureNotification();
+ }
+ }
+
+ void OnUILockControllerDestroying() override {
+ controller_observation_.Reset();
+ }
+
views::Widget* fullscreen_esc_notification() {
return fullscreen_esc_notification_;
}
@@ -190,6 +226,7 @@ class ExitNotifier : public ui::EventHandler, public ash::WindowStateObserver {
FullscreenControlPopup* exit_popup() { return exit_popup_.get(); }
+ private:
void MaybeShowPointerCaptureNotification() {
// Respect cooldown.
if (base::TimeTicks::Now() < next_pointer_notify_time_)
@@ -246,7 +283,6 @@ class ExitNotifier : public ui::EventHandler, public ash::WindowStateObserver {
}
}
- private:
void OnPointerCaptureNotifyTimerFinished() {
// Start the cooldown when the timer successfully elapses, to ensure the
// notification was shown for a sufficiently long time.
@@ -398,6 +434,7 @@ class ExitNotifier : public ui::EventHandler, public ash::WindowStateObserver {
views::Widget* fullscreen_esc_notification_ = nullptr;
views::Widget* pointer_capture_notification_ = nullptr;
bool want_pointer_capture_notification_ = false;
+ bool pointer_is_captured_ = false;
std::unique_ptr<FullscreenControlPopup> exit_popup_;
bool is_handling_events_ = false;
bool exit_popup_cooldown_ = false;
@@ -407,6 +444,9 @@ class ExitNotifier : public ui::EventHandler, public ash::WindowStateObserver {
base::OneShotTimer exit_popup_timer_;
base::ScopedObservation<ash::WindowState, ash::WindowStateObserver>
window_state_observation_{this};
+ base::ScopedObservation<exo::UILockController,
+ exo::UILockController::Notifier>
+ controller_observation_{this};
};
} // namespace
@@ -417,7 +457,9 @@ namespace exo {
namespace {
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(ExitNotifier, kExitNotifierKey, nullptr)
-ExitNotifier* GetExitNotifier(aura::Window* window, bool create) {
+ExitNotifier* GetExitNotifier(UILockController* controller,
+ aura::Window* window,
+ bool create) {
if (!base::FeatureList::IsEnabled(chromeos::features::kExoLockNotification))
return nullptr;
@@ -431,8 +473,8 @@ ExitNotifier* GetExitNotifier(aura::Window* window, bool create) {
ExitNotifier* notifier = toplevel->GetProperty(kExitNotifierKey);
if (!notifier && create) {
// Object is owned as a window property.
- notifier = toplevel->SetProperty(kExitNotifierKey,
- std::make_unique<ExitNotifier>(toplevel));
+ notifier = toplevel->SetProperty(
+ kExitNotifierKey, std::make_unique<ExitNotifier>(controller, toplevel));
}
return notifier;
@@ -446,13 +488,33 @@ constexpr auto kExcludedFlags = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
ui::EF_ALTGR_DOWN | ui::EF_IS_REPEAT;
UILockController::UILockController(Seat* seat) : seat_(seat) {
+ last_activity_time_ = base::TimeTicks::Now();
WMHelper::GetInstance()->AddPreTargetHandler(this);
seat_->AddObserver(this, kUILockControllerSeatObserverPriority);
+
+ WMHelper::GetInstance()->AddPowerObserver(this);
+
+ auto* session_controller = ash::SessionController::Get();
+ if (session_controller)
+ session_controller->AddObserver(this);
+
+ ui::UserActivityDetector::Get()->AddObserver(this);
}
UILockController::~UILockController() {
+ ui::UserActivityDetector::Get()->RemoveObserver(this);
+
+ auto* session_controller = ash::SessionController::Get();
+ if (session_controller)
+ session_controller->RemoveObserver(this);
+
+ WMHelper::GetInstance()->RemovePowerObserver(this);
+
seat_->RemoveObserver(this);
WMHelper::GetInstance()->RemovePreTargetHandler(this);
+
+ for (Notifier& notifier : notifiers_)
+ notifier.OnUILockControllerDestroying();
}
void UILockController::OnKeyEvent(ui::KeyEvent* event) {
@@ -470,27 +532,46 @@ void UILockController::OnKeyEvent(ui::KeyEvent* event) {
}
}
+void UILockController::SuspendDone() {
+ ReshowAllNotifications();
+}
+
+void UILockController::ScreenBrightnessChanged(double percent) {
+ // Show alert when the device returns from low (epsilon) brightness which
+ // covers three cases.
+ // 1. The device returns from sleep.
+ // 2. The device lid is opened (with sleep on).
+ // 3. The device returns from low display brightness.
+ double epsilon = std::numeric_limits<double>::epsilon();
+ if (percent <= epsilon) {
+ device_in_dark_ = true;
+ } else {
+ if (device_in_dark_)
+ ReshowAllNotifications();
+ device_in_dark_ = false;
+ }
+}
+
+void UILockController::LidEventReceived(bool opened) {
+ // Show alert when the lid is opened. This also covers the case when the user
+ // turns off "Sleep when cover is closed".
+ if (opened)
+ ReshowAllNotifications();
+}
+
+void UILockController::OnLockStateChanged(bool locked) {
+ if (!locked)
+ ReshowAllNotifications();
+}
+
void UILockController::OnSurfaceFocused(Surface* gained_focus,
Surface* lost_focus,
bool has_focused_surface) {
if (gained_focus != focused_surface_to_unlock_)
StopTimer();
- if (!base::FeatureList::IsEnabled(chromeos::features::kExoLockNotification))
- return;
-
- if (!gained_focus || !gained_focus->window())
- return;
-
- aura::Window* window = gained_focus->window()->GetToplevelWindow();
- if (!IsUILockControllerEnabled(window))
- return;
-
- // Object is owned as a window property.
- if (!window->GetProperty(kExitNotifierKey)) {
- window->SetProperty(kExitNotifierKey,
- std::make_unique<ExitNotifier>(window));
- }
+ if (gained_focus)
+ GetExitNotifier(this, gained_focus->window(), true);
}
void UILockController::OnPointerCaptureEnabled(Pointer* pointer,
@@ -501,9 +582,9 @@ void UILockController::OnPointerCaptureEnabled(Pointer* pointer,
return;
captured_pointers_.insert(pointer);
- ExitNotifier* notifier = GetExitNotifier(window, false);
+ ExitNotifier* notifier = GetExitNotifier(this, window, false);
if (notifier)
- notifier->MaybeShowPointerCaptureNotification();
+ notifier->OnPointerCaptureEnabled();
}
void UILockController::OnPointerCaptureDisabled(Pointer* pointer,
@@ -513,10 +594,18 @@ void UILockController::OnPointerCaptureDisabled(Pointer* pointer,
captured_pointers_.erase(pointer);
if (captured_pointers_.empty()) {
- ExitNotifier* notifier = GetExitNotifier(window, false);
+ ExitNotifier* notifier = GetExitNotifier(this, window, false);
if (notifier)
- notifier->ClosePointerCaptureNotification();
+ notifier->OnPointerCaptureDisabled();
+ }
+}
+
+void UILockController::OnUserActivity(const ui::Event* event) {
+ base::TimeTicks now = base::TimeTicks::Now();
+ if (now - last_activity_time_ >= kReshowNotificationsWhenIdleFor) {
+ ReshowAllNotifications();
}
+ last_activity_time_ = now;
}
views::Widget* UILockController::GetPointerCaptureNotificationForTesting(
@@ -534,6 +623,20 @@ FullscreenControlPopup* UILockController::GetExitPopupForTesting(
return window->GetProperty(kExitNotifierKey)->exit_popup();
}
+void UILockController::AddObserver(UILockController::Notifier* notifier) {
+ notifiers_.AddObserver(notifier);
+}
+
+void UILockController::RemoveObserver(UILockController::Notifier* notifier) {
+ notifiers_.RemoveObserver(notifier);
+}
+
+void UILockController::ReshowAllNotifications() {
+ VLOG(1) << "ReshowAllNotifications";
+ for (Notifier& notifier : notifiers_)
+ notifier.NotifyAgain();
+}
+
namespace {
bool EscapeHoldShouldExitFullscreen(Seat* seat) {
auto* surface = seat->GetFocusedSurface();
diff --git a/chromium/components/exo/ui_lock_controller.h b/chromium/components/exo/ui_lock_controller.h
index 5601c20b031..91e7049479e 100644
--- a/chromium/components/exo/ui_lock_controller.h
+++ b/chromium/components/exo/ui_lock_controller.h
@@ -10,6 +10,8 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/exo/seat_observer.h"
+#include "components/exo/wm_helper_chromeos.h"
+#include "ui/base/user_activity/user_activity_observer.h"
#include "ui/events/event_handler.h"
class FullscreenControlPopup;
@@ -28,8 +30,25 @@ extern const base::TimeDelta kLongPressEscapeDuration;
// Chromium's Keyboard Lock feature
// (see https://chromestatus.com/feature/5642959835889664). In other cases we
// nudge the user to use Overview.
-class UILockController : public ui::EventHandler, public SeatObserver {
+class UILockController : public ui::EventHandler,
+ public SeatObserver,
+ public ash::SessionObserver,
+ public WMHelperChromeOS::PowerObserver,
+ public ui::UserActivityObserver {
public:
+ // Interface for classes that display notifications based on UI lock states.
+ class Notifier : public base::CheckedObserver {
+ public:
+ // Called when any UI-lock-related notifications must be shown again.
+ //
+ // If a state that normally shows a notification on entry is currently
+ // active, show that notification again. Otherwise, reset any cooldowns
+ // so that the notification will show next time.
+ virtual void NotifyAgain() = 0;
+
+ virtual void OnUILockControllerDestroying() = 0;
+ };
+
explicit UILockController(Seat* seat);
UILockController(const UILockController&) = delete;
UILockController& operator=(const UILockController&) = delete;
@@ -38,6 +57,14 @@ class UILockController : public ui::EventHandler, public SeatObserver {
// Overridden from ui::EventHandler:
void OnKeyEvent(ui::KeyEvent* event) override;
+ // Overridden from WMHelper::PowerObserver:
+ void SuspendDone() override;
+ void ScreenBrightnessChanged(double percent) override;
+ void LidEventReceived(bool opened) override;
+
+ // Overridden from ash::SessionObserver:
+ void OnLockStateChanged(bool locked) override;
+
// Overridden from SeatObserver:
void OnSurfaceFocused(Surface* gained_focus,
Surface* lost_focus,
@@ -47,11 +74,19 @@ class UILockController : public ui::EventHandler, public SeatObserver {
void OnPointerCaptureDisabled(Pointer* pointer,
aura::Window* capture_window) override;
+ // Overridden from ui::UserActivityObserver:
+ void OnUserActivity(const ui::Event* event) override;
+
views::Widget* GetEscNotificationForTesting(aura::Window* window);
views::Widget* GetPointerCaptureNotificationForTesting(aura::Window* window);
FullscreenControlPopup* GetExitPopupForTesting(aura::Window* window);
+ void AddObserver(Notifier* notifier);
+ void RemoveObserver(Notifier* notifier);
+
private:
+ void ReshowAllNotifications();
+
void OnEscapeKey(bool pressed);
void OnEscapeHeld();
void StopTimer();
@@ -59,6 +94,9 @@ class UILockController : public ui::EventHandler, public SeatObserver {
Seat* seat_;
base::OneShotTimer exit_fullscreen_timer_;
+ // Whether the screen brightness is low enough to make the display dark.
+ bool device_in_dark_ = false;
+
// The surface which was focused when |exit_fullscreen_timer_| started
// running, or nullptr if the timer isn't running. Do not dereference; may
// dangle if the Surface is destroyed while the timer is running. Valid only
@@ -67,6 +105,15 @@ class UILockController : public ui::EventHandler, public SeatObserver {
// Pointers currently being captured.
base::flat_set<base::raw_ptr<Pointer>> captured_pointers_;
+
+ base::ObserverList<Notifier> notifiers_;
+
+ // Time of last user-generated input event. Used to display notifications
+ // again if the user goes idle and then becomes active again.
+ //
+ // Note TimeTicks may stand still if the device is suspended, but that's OK
+ // because device suspend/resume events also retrigger notifications.
+ base::TimeTicks last_activity_time_;
};
} // namespace exo
diff --git a/chromium/components/exo/ui_lock_controller_unittest.cc b/chromium/components/exo/ui_lock_controller_unittest.cc
index 6f8b1d96ea0..627fd8db395 100644
--- a/chromium/components/exo/ui_lock_controller_unittest.cc
+++ b/chromium/components/exo/ui_lock_controller_unittest.cc
@@ -8,7 +8,11 @@
#include "ash/shell.h"
#include "ash/wm/window_state.h"
#include "base/feature_list.h"
+#include "base/test/power_monitor_test.h"
#include "base/test/scoped_feature_list.h"
+#include "chromeos/dbus/power/fake_power_manager_client.h"
+#include "chromeos/dbus/power/power_manager_client.h"
+#include "chromeos/dbus/power_manager/backlight.pb.h"
#include "chromeos/ui/base/window_properties.h"
#include "components/exo/buffer.h"
#include "components/exo/display.h"
@@ -19,6 +23,7 @@
#include "components/exo/surface.h"
#include "components/exo/test/exo_test_base.h"
#include "components/exo/test/exo_test_helper.h"
+#include "components/exo/test/shell_surface_builder.h"
#include "components/exo/wm_helper.h"
#include "components/fullscreen_control/fullscreen_control_popup.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -34,40 +39,23 @@ namespace {
constexpr char kNoEscHoldAppId[] = "no-esc-hold";
constexpr char kOverviewToExitAppId[] = "overview-to-exit";
-struct SurfaceTriplet {
- std::unique_ptr<Surface> surface;
- std::unique_ptr<ShellSurface> shell_surface;
- std::unique_ptr<Buffer> buffer;
-
- aura::Window* GetAlwaysOnTopContainer() {
- aura::Window* native_window = GetTopLevelWidget()->GetNativeWindow();
- return ash::Shell::GetContainer(native_window->GetRootWindow(),
- ash::kShellWindowId_AlwaysOnTopContainer);
- }
-
- views::Widget* GetTopLevelWidget() {
- views::Widget* top_level_widget =
- views::Widget::GetTopLevelWidgetForNativeView(surface->window());
- assert(top_level_widget);
- return top_level_widget;
- }
-
- aura::Window* GetTopLevelWindow() {
- auto* top_level_widget = views::Widget::GetTopLevelWidgetForNativeView(
- shell_surface->host_window());
- assert(top_level_widget);
- return top_level_widget->GetNativeWindow();
- }
+aura::Window* GetTopLevelWindow(
+ const std::unique_ptr<ShellSurface>& shell_surface) {
+ auto* top_level_widget = views::Widget::GetTopLevelWidgetForNativeView(
+ shell_surface->host_window());
+ assert(top_level_widget);
+ return top_level_widget->GetNativeWindow();
+}
- ash::WindowState* GetTopLevelWindowState() {
- return ash::WindowState::Get(GetTopLevelWindow());
- }
-};
+ash::WindowState* GetTopLevelWindowState(
+ const std::unique_ptr<ShellSurface>& shell_surface) {
+ return ash::WindowState::Get(GetTopLevelWindow(shell_surface));
+}
class MockPointerDelegate : public PointerDelegate {
public:
- MockPointerDelegate(Surface* surface) {
- EXPECT_CALL(*this, CanAcceptPointerEventsForSurface(surface))
+ MockPointerDelegate() {
+ EXPECT_CALL(*this, CanAcceptPointerEventsForSurface(testing::_))
.WillRepeatedly(testing::Return(true));
}
@@ -163,30 +151,26 @@ class UILockControllerTest : public test::ExoTestBase {
test::ExoTestBase::TearDown();
}
- SurfaceTriplet BuildSurface(gfx::Point origin, int w, int h) {
- auto surface = std::make_unique<Surface>();
- auto shell_surface = std::make_unique<ShellSurface>(
- surface.get(), origin,
- /*can_minimize=*/true, ash::desks_util::GetActiveDeskContainerId());
- auto buffer = std::make_unique<Buffer>(
- exo_test_helper()->CreateGpuMemoryBuffer({w, h}));
- surface->Attach(buffer.get());
-
- return {std::move(surface), std::move(shell_surface), std::move(buffer)};
+ std::unique_ptr<ShellSurface> BuildSurface(gfx::Point origin, int w, int h) {
+ test::ShellSurfaceBuilder builder({w, h});
+ builder.SetOrigin(origin);
+ return builder.BuildShellSurface();
}
- SurfaceTriplet BuildSurface(int w, int h) {
+ std::unique_ptr<ShellSurface> BuildSurface(int w, int h) {
return BuildSurface(gfx::Point(0, 0), w, h);
}
- views::Widget* GetEscNotification(SurfaceTriplet* surface) {
+ views::Widget* GetEscNotification(
+ const std::unique_ptr<ShellSurface>& surface) {
return seat_->GetUILockControllerForTesting()->GetEscNotificationForTesting(
- surface->GetTopLevelWindow());
+ GetTopLevelWindow(surface));
}
- views::Widget* GetPointerCaptureNotification(SurfaceTriplet* surface) {
+ views::Widget* GetPointerCaptureNotification(
+ const std::unique_ptr<ShellSurface>& surface) {
return seat_->GetUILockControllerForTesting()
- ->GetPointerCaptureNotificationForTesting(surface->GetTopLevelWindow());
+ ->GetPointerCaptureNotificationForTesting(GetTopLevelWindow(surface));
}
bool IsExitPopupVisible(aura::Window* window) {
@@ -206,11 +190,11 @@ class UILockControllerTest : public test::ExoTestBase {
};
TEST_F(UILockControllerTest, HoldingEscapeExitsFullscreen) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
- auto* window_state = test_surface.GetTopLevelWindowState();
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ auto* window_state = GetTopLevelWindowState(test_surface);
EXPECT_TRUE(window_state->IsFullscreen());
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
@@ -223,11 +207,11 @@ TEST_F(UILockControllerTest, HoldingEscapeExitsFullscreen) {
}
TEST_F(UILockControllerTest, HoldingCtrlEscapeDoesNotExitFullscreen) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
- auto* window_state = test_surface.GetTopLevelWindowState();
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ auto* window_state = GetTopLevelWindowState(test_surface);
EXPECT_TRUE(window_state->IsFullscreen());
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_CONTROL_DOWN);
@@ -237,13 +221,13 @@ TEST_F(UILockControllerTest, HoldingCtrlEscapeDoesNotExitFullscreen) {
TEST_F(UILockControllerTest,
HoldingEscapeOnlyExitsFullscreenIfWindowPropertySet) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
// Do not set chromeos::kEscHoldToExitFullscreen on TopLevelWindow.
- test_surface.shell_surface->SetApplicationId(kNoEscHoldAppId);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
- auto* window_state = test_surface.GetTopLevelWindowState();
+ test_surface->SetApplicationId(kNoEscHoldAppId);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ auto* window_state = GetTopLevelWindowState(test_surface);
EXPECT_TRUE(window_state->IsFullscreen());
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
@@ -252,30 +236,29 @@ TEST_F(UILockControllerTest,
}
TEST_F(UILockControllerTest, HoldingEscapeOnlyExitsFocusedFullscreen) {
- SurfaceTriplet test_surface1 = BuildSurface(1024, 768);
- test_surface1.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface1.shell_surface->SetFullscreen(true);
- test_surface1.surface->Commit();
+ std::unique_ptr<ShellSurface> test_surface1 = BuildSurface(1024, 768);
+ test_surface1->SetUseImmersiveForFullscreen(false);
+ test_surface1->SetFullscreen(true);
+ test_surface1->surface_for_testing()->Commit();
- SurfaceTriplet test_surface2 = BuildSurface(1024, 768);
- test_surface2.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface2.shell_surface->SetFullscreen(true);
- test_surface2.surface->Commit();
+ std::unique_ptr<ShellSurface> test_surface2 = BuildSurface(1024, 768);
+ test_surface2->SetUseImmersiveForFullscreen(false);
+ test_surface2->SetFullscreen(true);
+ test_surface2->surface_for_testing()->Commit();
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
task_environment()->FastForwardBy(base::Seconds(2));
- EXPECT_TRUE(test_surface1.GetTopLevelWindowState()->IsFullscreen());
- EXPECT_FALSE(test_surface2.GetTopLevelWindowState()->IsFullscreen());
+ EXPECT_TRUE(GetTopLevelWindowState(test_surface1)->IsFullscreen());
+ EXPECT_FALSE(GetTopLevelWindowState(test_surface2)->IsFullscreen());
}
TEST_F(UILockControllerTest, DestroyingWindowCancels) {
- std::unique_ptr<SurfaceTriplet> test_surface =
- std::make_unique<SurfaceTriplet>(BuildSurface(1024, 768));
- test_surface->shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface->shell_surface->SetFullscreen(true);
- test_surface->surface->Commit();
- auto* window_state = test_surface->GetTopLevelWindowState();
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ auto* window_state = GetTopLevelWindowState(test_surface);
EXPECT_TRUE(window_state->IsFullscreen());
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
@@ -290,37 +273,39 @@ TEST_F(UILockControllerTest, DestroyingWindowCancels) {
TEST_F(UILockControllerTest, FocusChangeCancels) {
// Arrange: two windows, one is fullscreen and focused
- SurfaceTriplet other_surface = BuildSurface(1024, 768);
- other_surface.surface->Commit();
+ std::unique_ptr<ShellSurface> other_surface = BuildSurface(1024, 768);
+ other_surface->surface_for_testing()->Commit();
- SurfaceTriplet fullscreen_surface = BuildSurface(1024, 768);
- fullscreen_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- fullscreen_surface.shell_surface->SetFullscreen(true);
- fullscreen_surface.surface->Commit();
+ std::unique_ptr<ShellSurface> fullscreen_surface = BuildSurface(1024, 768);
+ fullscreen_surface->SetUseImmersiveForFullscreen(false);
+ fullscreen_surface->SetFullscreen(true);
+ fullscreen_surface->surface_for_testing()->Commit();
- EXPECT_EQ(fullscreen_surface.surface.get(), seat_->GetFocusedSurface());
- EXPECT_FALSE(fullscreen_surface.GetTopLevelWindowState()->IsMinimized());
+ EXPECT_EQ(fullscreen_surface->surface_for_testing(),
+ seat_->GetFocusedSurface());
+ EXPECT_FALSE(GetTopLevelWindowState(fullscreen_surface)->IsMinimized());
// Act: Press escape, then toggle focus back and forth
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
task_environment()->FastForwardBy(base::Seconds(1));
- wm::ActivateWindow(other_surface.surface->window());
- wm::ActivateWindow(fullscreen_surface.surface->window());
+ wm::ActivateWindow(other_surface->surface_for_testing()->window());
+ wm::ActivateWindow(fullscreen_surface->surface_for_testing()->window());
task_environment()->FastForwardBy(base::Seconds(2));
// Assert: Fullscreen window was not minimized, despite regaining focus.
- EXPECT_FALSE(fullscreen_surface.GetTopLevelWindowState()->IsMinimized());
- EXPECT_EQ(fullscreen_surface.surface.get(), seat_->GetFocusedSurface());
+ EXPECT_FALSE(GetTopLevelWindowState(fullscreen_surface)->IsMinimized());
+ EXPECT_EQ(fullscreen_surface->surface_for_testing(),
+ seat_->GetFocusedSurface());
}
TEST_F(UILockControllerTest, ShortHoldEscapeDoesNotExitFullscreen) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
- auto* window_state = test_surface.GetTopLevelWindowState();
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ auto* window_state = GetTopLevelWindowState(test_surface);
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
task_environment()->FastForwardBy(base::Seconds(1));
@@ -331,144 +316,209 @@ TEST_F(UILockControllerTest, ShortHoldEscapeDoesNotExitFullscreen) {
}
TEST_F(UILockControllerTest, FullScreenShowsEscNotification) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
- EXPECT_TRUE(test_surface.GetTopLevelWindowState()->IsFullscreen());
- EXPECT_TRUE(GetEscNotification(&test_surface));
+ EXPECT_TRUE(GetTopLevelWindowState(test_surface)->IsFullscreen());
+ EXPECT_TRUE(GetEscNotification(test_surface));
}
TEST_F(UILockControllerTest, EscNotificationClosesAfterDuration) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
- EXPECT_TRUE(GetEscNotification(&test_surface));
+ EXPECT_TRUE(GetEscNotification(test_surface));
task_environment()->FastForwardBy(base::Seconds(5));
- EXPECT_FALSE(GetEscNotification(&test_surface));
+ EXPECT_FALSE(GetEscNotification(test_surface));
}
TEST_F(UILockControllerTest, HoldingEscapeHidesNotification) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
- EXPECT_TRUE(test_surface.GetTopLevelWindowState()->IsFullscreen());
- EXPECT_TRUE(GetEscNotification(&test_surface));
+ EXPECT_TRUE(GetTopLevelWindowState(test_surface)->IsFullscreen());
+ EXPECT_TRUE(GetEscNotification(test_surface));
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
task_environment()->FastForwardBy(base::Seconds(3));
- EXPECT_FALSE(test_surface.GetTopLevelWindowState()->IsFullscreen());
- EXPECT_FALSE(GetEscNotification(&test_surface));
+ EXPECT_FALSE(GetTopLevelWindowState(test_surface)->IsFullscreen());
+ EXPECT_FALSE(GetEscNotification(test_surface));
}
TEST_F(UILockControllerTest, LosingFullscreenHidesNotification) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
- EXPECT_TRUE(test_surface.GetTopLevelWindowState()->IsFullscreen());
- EXPECT_TRUE(GetEscNotification(&test_surface));
+ EXPECT_TRUE(GetTopLevelWindowState(test_surface)->IsFullscreen());
+ EXPECT_TRUE(GetEscNotification(test_surface));
// Have surface loose fullscreen, notification should now be hidden.
- test_surface.shell_surface->Minimize();
- test_surface.shell_surface->SetFullscreen(false);
- test_surface.surface->Commit();
+ test_surface->Minimize();
+ test_surface->SetFullscreen(false);
+ test_surface->surface_for_testing()->Commit();
- EXPECT_FALSE(test_surface.GetTopLevelWindowState()->IsFullscreen());
+ EXPECT_FALSE(GetTopLevelWindowState(test_surface)->IsFullscreen());
EXPECT_FALSE(
seat_->GetUILockControllerForTesting()->GetEscNotificationForTesting(
- test_surface.GetTopLevelWindow()));
+ GetTopLevelWindow(test_surface)));
}
-TEST_F(UILockControllerTest, EscNotificationIsReshown) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
+TEST_F(UILockControllerTest, EscNotificationIsReshownIfInterrupted) {
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
- EXPECT_TRUE(GetEscNotification(&test_surface));
+ EXPECT_TRUE(GetEscNotification(test_surface));
// Stop fullscreen.
- test_surface.shell_surface->SetFullscreen(false);
+ test_surface->SetFullscreen(false);
EXPECT_FALSE(
seat_->GetUILockControllerForTesting()->GetEscNotificationForTesting(
- test_surface.GetTopLevelWindow()));
+ GetTopLevelWindow(test_surface)));
// Fullscreen should show notification since it did not stay visible for
// duration.
- test_surface.shell_surface->SetFullscreen(true);
- EXPECT_TRUE(GetEscNotification(&test_surface));
+ test_surface->SetFullscreen(true);
+ EXPECT_TRUE(GetEscNotification(test_surface));
// After duration, notification should be removed.
task_environment()->FastForwardBy(base::Seconds(5));
- EXPECT_FALSE(GetEscNotification(&test_surface));
+ EXPECT_FALSE(GetEscNotification(test_surface));
// Notification is shown after fullscreen toggle.
- test_surface.shell_surface->SetFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- EXPECT_TRUE(GetEscNotification(&test_surface));
+ test_surface->SetFullscreen(false);
+ test_surface->SetFullscreen(true);
+ EXPECT_TRUE(GetEscNotification(test_surface));
+}
+
+TEST_F(UILockControllerTest, EscNotificationIsReshownAfterUnlock) {
+ // Arrange: Go fullscreen and time out the notification.
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ task_environment()->FastForwardBy(base::Seconds(10));
+ // Ensure the notification did time out; if not, we can't trust the test
+ // result.
+ EXPECT_FALSE(GetEscNotification(test_surface));
+
+ // Act: Simulate locking and unlocking.
+ GetSessionControllerClient()->LockScreen();
+ GetSessionControllerClient()->UnlockScreen();
+
+ // Assert: Notification shown again.
+ EXPECT_TRUE(GetEscNotification(test_surface));
+}
+
+TEST_F(UILockControllerTest, EscNotificationReshownWhenScreenTurnedOn) {
+ // Arrange: Set up a pointer capture notification, then let it expire.
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ task_environment()->FastForwardBy(base::Seconds(10));
+ // Ensure the notification did time out; if not, we can't trust the test
+ // result.
+ EXPECT_FALSE(GetEscNotification(test_surface));
+
+ // Act: Simulate turning the backlight off and on again.
+ power_manager::SetBacklightBrightnessRequest request;
+ request.set_percent(0);
+ chromeos::FakePowerManagerClient::Get()->SetScreenBrightness(request);
+ base::RunLoop().RunUntilIdle();
+ request.set_percent(100);
+ chromeos::FakePowerManagerClient::Get()->SetScreenBrightness(request);
+ base::RunLoop().RunUntilIdle();
+
+ // Assert: Notification shown again.
+ EXPECT_TRUE(GetEscNotification(test_surface));
+}
+
+TEST_F(UILockControllerTest, EscNotificationReshownWhenLidReopened) {
+ // Arrange: Set up a pointer capture notification, then let it expire.
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ task_environment()->FastForwardBy(base::Seconds(10));
+ // Ensure the notification did time out; if not, we can't trust the test
+ // result.
+ EXPECT_FALSE(GetEscNotification(test_surface));
+
+ // Act: Simulate closing and reopening the lid.
+ chromeos::FakePowerManagerClient::Get()->SetLidState(
+ chromeos::PowerManagerClient::LidState::CLOSED, base::TimeTicks::Now());
+ chromeos::FakePowerManagerClient::Get()->SetLidState(
+ chromeos::PowerManagerClient::LidState::OPEN, base::TimeTicks::Now());
+
+ // Assert: Notification shown again.
+ EXPECT_TRUE(GetEscNotification(test_surface));
}
TEST_F(UILockControllerTest, EscNotificationShowsOnSecondaryDisplay) {
// Create surface on secondary display.
UpdateDisplay("900x800,70x600");
- SurfaceTriplet test_surface = BuildSurface(gfx::Point(900, 100), 200, 200);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
+ std::unique_ptr<ShellSurface> test_surface =
+ BuildSurface(gfx::Point(900, 100), 200, 200);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
// Esc notification should be in secondary display.
- views::Widget* esc_notification = GetEscNotification(&test_surface);
+ views::Widget* esc_notification = GetEscNotification(test_surface);
EXPECT_TRUE(GetSecondaryDisplay().bounds().Contains(
esc_notification->GetWindowBoundsInScreen()));
}
TEST_F(UILockControllerTest, PointerLockShowsNotification) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetApplicationId(kOverviewToExitAppId);
- test_surface.surface->Commit();
- testing::NiceMock<MockPointerDelegate> delegate(test_surface.surface.get());
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->surface_for_testing()->Commit();
+ testing::NiceMock<MockPointerDelegate> delegate;
Pointer pointer(&delegate, seat_.get());
testing::NiceMock<MockPointerConstraintDelegate> constraint(
- &pointer, test_surface.surface.get());
- EXPECT_FALSE(GetPointerCaptureNotification(&test_surface));
+ &pointer, test_surface->surface_for_testing());
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
- EXPECT_TRUE(GetPointerCaptureNotification(&test_surface));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
}
TEST_F(UILockControllerTest, PointerLockNotificationObeysCooldown) {
// Arrange: Set up a pointer capture notification.
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetApplicationId(kOverviewToExitAppId);
- test_surface.surface->Commit();
- testing::NiceMock<MockPointerDelegate> delegate(test_surface.surface.get());
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->surface_for_testing()->Commit();
+ testing::NiceMock<MockPointerDelegate> delegate;
Pointer pointer(&delegate, seat_.get());
testing::NiceMock<MockPointerConstraintDelegate> constraint(
- &pointer, test_surface.surface.get());
+ &pointer, test_surface->surface_for_testing());
EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
- EXPECT_TRUE(GetPointerCaptureNotification(&test_surface));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
// Act: Wait for the notification to timeout.
task_environment()->FastForwardBy(base::Seconds(5));
// Assert: Notification has disappeared.
- EXPECT_FALSE(GetPointerCaptureNotification(&test_surface));
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
// Act: Remove and re-apply the constraint.
pointer.OnPointerConstraintDelegateDestroying(&constraint);
EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
// Assert: Notification not shown due to the cooldown.
- EXPECT_FALSE(GetPointerCaptureNotification(&test_surface));
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
// Act: Wait for the cooldown, then re-apply again
pointer.OnPointerConstraintDelegateDestroying(&constraint);
@@ -476,49 +526,199 @@ TEST_F(UILockControllerTest, PointerLockNotificationObeysCooldown) {
EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
// Assert: Cooldown has expired so notification is shown.
- EXPECT_TRUE(GetPointerCaptureNotification(&test_surface));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+}
+
+TEST_F(UILockControllerTest, PointerLockNotificationReshownOnLidOpen) {
+ // Arrange: Set up a pointer capture notification, then let it expire.
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->surface_for_testing()->Commit();
+ testing::NiceMock<MockPointerDelegate> delegate;
+ Pointer pointer(&delegate, seat_.get());
+ testing::NiceMock<MockPointerConstraintDelegate> constraint(
+ &pointer, test_surface->surface_for_testing());
+ EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+ task_environment()->FastForwardBy(base::Seconds(5));
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
+
+ // Act: Simulate closing and reopening the lid.
+ chromeos::FakePowerManagerClient::Get()->SetLidState(
+ chromeos::PowerManagerClient::LidState::CLOSED, base::TimeTicks::Now());
+ chromeos::FakePowerManagerClient::Get()->SetLidState(
+ chromeos::PowerManagerClient::LidState::OPEN, base::TimeTicks::Now());
+
+ // Assert: Notification shown again.
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+}
+
+TEST_F(UILockControllerTest, PointerLockNotificationReshownWhenScreenTurnedOn) {
+ // Arrange: Set up a pointer capture notification, then let it expire.
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->surface_for_testing()->Commit();
+ testing::NiceMock<MockPointerDelegate> delegate;
+ Pointer pointer(&delegate, seat_.get());
+ testing::NiceMock<MockPointerConstraintDelegate> constraint(
+ &pointer, test_surface->surface_for_testing());
+ EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+ task_environment()->FastForwardBy(base::Seconds(5));
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
+
+ // Act: Simulate turning the backlight off and on again.
+ power_manager::SetBacklightBrightnessRequest request;
+ request.set_percent(0);
+ chromeos::FakePowerManagerClient::Get()->SetScreenBrightness(request);
+ base::RunLoop().RunUntilIdle();
+ request.set_percent(100);
+ chromeos::FakePowerManagerClient::Get()->SetScreenBrightness(request);
+ base::RunLoop().RunUntilIdle();
+
+ // Assert: Notification shown again.
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+}
+
+TEST_F(UILockControllerTest, PointerLockNotificationReshownOnUnlock) {
+ // Arrange: Set up a pointer capture notification, then let it expire.
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->surface_for_testing()->Commit();
+ testing::NiceMock<MockPointerDelegate> delegate;
+ Pointer pointer(&delegate, seat_.get());
+ testing::NiceMock<MockPointerConstraintDelegate> constraint(
+ &pointer, test_surface->surface_for_testing());
+ EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
+ task_environment()->FastForwardBy(base::Seconds(5));
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
+
+ // Act: Simulate locking and unlocking.
+ GetSessionControllerClient()->LockScreen();
+ GetSessionControllerClient()->UnlockScreen();
+
+ // Assert: Notification shown again.
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+}
+
+TEST_F(UILockControllerTest, PointerLockNotificationReshownAfterSuspend) {
+ // Arrange: Set up a pointer capture notification, then let it expire.
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->surface_for_testing()->Commit();
+ testing::NiceMock<MockPointerDelegate> delegate;
+ Pointer pointer(&delegate, seat_.get());
+ testing::NiceMock<MockPointerConstraintDelegate> constraint(
+ &pointer, test_surface->surface_for_testing());
+ EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+ task_environment()->FastForwardBy(base::Seconds(5));
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
+
+ // Act: Simulate suspend and resume
+ chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
+ power_manager::SuspendImminent_Reason_IDLE);
+ task_environment()->FastForwardBy(base::Minutes(1));
+ chromeos::FakePowerManagerClient::Get()->SendSuspendDone(base::Minutes(1));
+
+ // Assert: Notification shown again.
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+}
+
+TEST_F(UILockControllerTest, PointerLockNotificationReshownAfterIdle) {
+ // Arrange: Set up a pointer capture notification, then let it expire.
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->surface_for_testing()->Commit();
+ testing::NiceMock<MockPointerDelegate> delegate;
+ Pointer pointer(&delegate, seat_.get());
+ testing::NiceMock<MockPointerConstraintDelegate> constraint(
+ &pointer, test_surface->surface_for_testing());
+ EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+ task_environment()->FastForwardBy(base::Seconds(5));
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
+
+ // Act: Simulate activity, then go idle.
+ seat_->GetUILockControllerForTesting()->OnUserActivity(/*event=*/nullptr);
+ task_environment()->FastForwardBy(base::Minutes(10));
+
+ // Assert: Notification not yet shown again.
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
+
+ // Act: Simulate activity after being idle.
+ seat_->GetUILockControllerForTesting()->OnUserActivity(/*event=*/nullptr);
+
+ // Assert: Notification shown again.
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+}
+
+TEST_F(UILockControllerTest, PointerLockCooldownResetForAllWindows) {
+ // Arrange: Create two surfaces, one with a pointer lock notification.
+ std::unique_ptr<ShellSurface> other_surface = BuildSurface(1024, 768);
+ other_surface->SetApplicationId(kOverviewToExitAppId);
+ other_surface->surface_for_testing()->Commit();
+
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->surface_for_testing()->Commit();
+
+ testing::NiceMock<MockPointerDelegate> delegate;
+ Pointer pointer(&delegate, seat_.get());
+ testing::NiceMock<MockPointerConstraintDelegate> constraint(
+ &pointer, test_surface->surface_for_testing());
+ EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+
+ // Act: Focus the other window, then lock and unlock.
+ wm::ActivateWindow(other_surface->surface_for_testing()->window());
+ GetSessionControllerClient()->LockScreen();
+ GetSessionControllerClient()->UnlockScreen();
+
+ // Assert: Notification shown again.
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
}
TEST_F(UILockControllerTest, FullscreenNotificationHasPriority) {
// Arrange: Set up a pointer capture notification.
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetApplicationId(kOverviewToExitAppId);
- test_surface.surface->Commit();
- testing::NiceMock<MockPointerDelegate> delegate(test_surface.surface.get());
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->surface_for_testing()->Commit();
+ testing::NiceMock<MockPointerDelegate> delegate;
Pointer pointer(&delegate, seat_.get());
testing::NiceMock<MockPointerConstraintDelegate> constraint(
- &pointer, test_surface.surface.get());
+ &pointer, test_surface->surface_for_testing());
EXPECT_TRUE(pointer.ConstrainPointer(&constraint));
- EXPECT_TRUE(GetPointerCaptureNotification(&test_surface));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
// Act: Go fullscreen.
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
// Assert: Fullscreen notification overrides pointer notification.
- EXPECT_FALSE(GetPointerCaptureNotification(&test_surface));
- EXPECT_TRUE(GetEscNotification(&test_surface));
+ EXPECT_FALSE(GetPointerCaptureNotification(test_surface));
+ EXPECT_TRUE(GetEscNotification(test_surface));
// Act: Exit fullscreen.
- test_surface.shell_surface->SetFullscreen(false);
- test_surface.surface->Commit();
+ test_surface->SetFullscreen(false);
+ test_surface->surface_for_testing()->Commit();
// Assert: Pointer notification returns, since it was interrupted.
- EXPECT_TRUE(GetPointerCaptureNotification(&test_surface));
- EXPECT_FALSE(GetEscNotification(&test_surface));
+ EXPECT_TRUE(GetPointerCaptureNotification(test_surface));
+ EXPECT_FALSE(GetEscNotification(test_surface));
}
TEST_F(UILockControllerTest, ExitPopup) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
- auto* window_state = test_surface.GetTopLevelWindowState();
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ auto* window_state = GetTopLevelWindowState(test_surface);
EXPECT_TRUE(window_state->IsFullscreen());
- aura::Window* window = test_surface.GetTopLevelWindow();
+ aura::Window* window = GetTopLevelWindow(test_surface);
EXPECT_FALSE(IsExitPopupVisible(window));
- EXPECT_TRUE(GetEscNotification(&test_surface));
+ EXPECT_TRUE(GetEscNotification(test_surface));
// Move mouse above y=3 should not show exit popup while notification is
// visible.
@@ -527,7 +727,7 @@ TEST_F(UILockControllerTest, ExitPopup) {
// Wait for notification to close, now exit popup should show.
task_environment()->FastForwardBy(base::Seconds(5));
- EXPECT_FALSE(GetEscNotification(&test_surface));
+ EXPECT_FALSE(GetEscNotification(test_surface));
GetEventGenerator()->MoveMouseTo(1, 2);
EXPECT_TRUE(IsExitPopupVisible(window));
@@ -564,31 +764,32 @@ TEST_F(UILockControllerTest, ExitPopup) {
}
TEST_F(UILockControllerTest, ExitPopupNotShownForOverviewCase) {
- SurfaceTriplet test_surface = BuildSurface(1024, 768);
+ std::unique_ptr<ShellSurface> test_surface = BuildSurface(1024, 768);
// Set chromeos::kUseOverviewToExitFullscreen on TopLevelWindow.
- test_surface.shell_surface->SetApplicationId(kOverviewToExitAppId);
- test_surface.shell_surface->SetUseImmersiveForFullscreen(false);
- test_surface.shell_surface->SetFullscreen(true);
- test_surface.surface->Commit();
- EXPECT_FALSE(IsExitPopupVisible(test_surface.GetTopLevelWindow()));
+ test_surface->SetApplicationId(kOverviewToExitAppId);
+ test_surface->SetUseImmersiveForFullscreen(false);
+ test_surface->SetFullscreen(true);
+ test_surface->surface_for_testing()->Commit();
+ EXPECT_FALSE(IsExitPopupVisible(GetTopLevelWindow(test_surface)));
// Move mouse above y=3 should not show exit popup.
GetEventGenerator()->MoveMouseTo(0, 2);
- EXPECT_FALSE(IsExitPopupVisible(test_surface.GetTopLevelWindow()));
+ EXPECT_FALSE(IsExitPopupVisible(GetTopLevelWindow(test_surface)));
}
TEST_F(UILockControllerTest, OnlyShowWhenActive) {
- SurfaceTriplet test_surface1 = BuildSurface(1024, 768);
- test_surface1.surface->Commit();
- SurfaceTriplet test_surface2 = BuildSurface(gfx::Point(100, 100), 200, 200);
- test_surface2.surface->Commit();
+ std::unique_ptr<ShellSurface> test_surface1 = BuildSurface(1024, 768);
+ test_surface1->surface_for_testing()->Commit();
+ std::unique_ptr<ShellSurface> test_surface2 =
+ BuildSurface(gfx::Point(100, 100), 200, 200);
+ test_surface2->surface_for_testing()->Commit();
// Surface2 is active when we make Surface1 fullscreen.
// Esc notification, and exit popup should not be shown.
- test_surface1.shell_surface->SetFullscreen(true);
- EXPECT_FALSE(GetEscNotification(&test_surface1));
+ test_surface1->SetFullscreen(true);
+ EXPECT_FALSE(GetEscNotification(test_surface1));
GetEventGenerator()->MoveMouseTo(0, 2);
- EXPECT_FALSE(IsExitPopupVisible(test_surface1.GetTopLevelWindow()));
+ EXPECT_FALSE(IsExitPopupVisible(GetTopLevelWindow(test_surface1)));
}
} // namespace
diff --git a/chromium/components/exo/wayland/BUILD.gn b/chromium/components/exo/wayland/BUILD.gn
index 98b6af75573..cc84d4523ad 100644
--- a/chromium/components/exo/wayland/BUILD.gn
+++ b/chromium/components/exo/wayland/BUILD.gn
@@ -28,6 +28,8 @@ source_set("wayland") {
"wayland_display_observer.h",
"wayland_display_output.cc",
"wayland_display_output.h",
+ "wayland_display_util.cc",
+ "wayland_display_util.h",
"wayland_input_delegate.cc",
"wayland_input_delegate.h",
"wayland_pointer_delegate.cc",
@@ -73,6 +75,7 @@ source_set("wayland") {
"//components/exo",
"//components/exo:buildflags",
"//components/exo/wayland/protocol:aura_shell_protocol",
+ "//components/exo/wayland/protocol:chrome_color_management_protocol",
"//components/exo/wayland/protocol:overlay_prioritizer_protocol",
"//components/exo/wayland/protocol:surface_augmenter_protocol",
"//device/gamepad",
@@ -88,6 +91,7 @@ source_set("wayland") {
"//third_party/wayland-protocols:input_timestamps_protocol",
"//third_party/wayland-protocols:keyboard_configuration_protocol",
"//third_party/wayland-protocols:keyboard_extension_protocol",
+ "//third_party/wayland-protocols:keyboard_shortcuts_inhibit_protocol",
"//third_party/wayland-protocols:linux_explicit_synchronization_protocol",
"//third_party/wayland-protocols:notification_shell_protocol",
"//third_party/wayland-protocols:pointer_constraints_protocol",
@@ -143,15 +147,6 @@ source_set("wayland") {
deps += [ "//ui/events/keycodes:xkb" ]
}
- if (is_chromecast) {
- defines += [ "USE_FULLSCREEN_SHELL" ]
- sources += [
- "zwp_fullscreen_shell.cc",
- "zwp_fullscreen_shell.h",
- ]
- deps += [ "//third_party/wayland-protocols:fullscreen_shell_protocol" ]
- }
-
if (is_chromeos_ash) {
sources += [
"wayland_keyboard_delegate.cc",
@@ -190,6 +185,8 @@ source_set("wayland") {
"zwp_idle_inhibit_manager.h",
"zwp_input_timestamps_manager.cc",
"zwp_input_timestamps_manager.h",
+ "zwp_keyboard_shortcuts_inhibit_manager.cc",
+ "zwp_keyboard_shortcuts_inhibit_manager.h",
"zwp_pointer_constraints.cc",
"zwp_pointer_constraints.h",
"zwp_pointer_gestures.cc",
@@ -208,6 +205,7 @@ source_set("wayland") {
if (enable_color_manager) {
deps += [
"//components/exo/wayland/protocol:chrome_color_management_protocol",
+ "//ui/base/wayland:color_manager_util",
]
sources += [
"zcr_color_manager.cc",
@@ -220,6 +218,7 @@ source_set("wayland") {
"//services/device/wake_lock/power_save_blocker",
"//third_party/wayland-protocols:idle_inhibit_protocol",
"//ui/base/cursor/mojom:cursor_type",
+ "//ui/base/wayland:wayland_server_input_types",
"//ui/events/ozone/layout",
]
}
@@ -235,6 +234,7 @@ static_library("weston_test") {
"//third_party/wayland:wayland_server",
"//third_party/wayland-protocols:weston_test",
"//ui/base:test_support",
+ "//ui/compositor",
"//ui/wm",
]
sources = [ "weston_test.cc" ]
@@ -278,6 +278,8 @@ source_set("unit_tests") {
if (is_chromeos_ash) {
sources += [
+ "wayland_display_observer_unittest.cc",
+ "wayland_display_util_unittest.cc",
"wayland_keyboard_delegate_unittest.cc",
"wayland_positioner_unittest.cc",
"zaura_shell_unittest.cc",
@@ -289,6 +291,7 @@ source_set("unit_tests") {
"//ash:test_support",
"//ash/public/cpp",
"//third_party/wayland-protocols:remote_shell_protocol",
+ "//third_party/wayland-protocols:xdg_output_protocol",
"//third_party/wayland-protocols:xdg_shell_protocol",
"//ui/compositor",
"//ui/compositor:test_support",
@@ -340,6 +343,7 @@ source_set("client_support") {
"//third_party/wayland-protocols:input_timestamps_protocol",
"//third_party/wayland-protocols:keyboard_configuration_protocol",
"//third_party/wayland-protocols:keyboard_extension_protocol",
+ "//third_party/wayland-protocols:keyboard_shortcuts_inhibit_protocol",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
"//third_party/wayland-protocols:linux_explicit_synchronization_protocol",
@@ -600,6 +604,7 @@ test("wayland_client_tests") {
use_xvfb = use_xvfb_in_this_config
sources = [
+ "clients/capability_binding_test.cc",
"clients/interface_binding_test.cc",
"clients/test/run_all_client_tests.cc",
]
@@ -608,6 +613,8 @@ test("wayland_client_tests") {
":client_support",
":client_version_test",
":wayland_client_test_helper",
+ "//components/exo",
+ "//components/exo/wayland",
"//ui/base",
"//ui/gl:test_support",
]
@@ -712,7 +719,10 @@ test("wayland_client_compatibility_tests") {
if (ozone_platform_drm) {
test("wayland_client_integration_tests") {
- sources = [ "clients/wayland_client_integration_tests.cc" ]
+ sources = [
+ "test/integration/buffer_checker_test.cc",
+ "test/integration/wayland_client_integration_tests_main.cc",
+ ]
deps = [
":client_support",
"//base",
@@ -721,6 +731,7 @@ if (ozone_platform_drm) {
"//ui/gfx:gfx",
"//ui/gfx/linux:drm",
"//ui/gfx/linux:gbm",
+ "//ui/gl",
]
}
diff --git a/chromium/components/exo/wayland/clients/capability_binding_test.cc b/chromium/components/exo/wayland/clients/capability_binding_test.cc
new file mode 100644
index 00000000000..973886b1a16
--- /dev/null
+++ b/chromium/components/exo/wayland/clients/capability_binding_test.cc
@@ -0,0 +1,190 @@
+// Copyright 2021 The Chromium 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 <remote-shell-unstable-v1-client-protocol.h>
+#include <remote-shell-unstable-v2-client-protocol.h>
+#include <wayland-client-core.h>
+#include <wayland-client-protocol.h>
+#include <wayland-server-core.h>
+#include <wayland-server-protocol-core.h>
+#include <wayland-util.h>
+#include <xdg-shell-client-protocol.h>
+#include <xdg-shell-unstable-v6-client-protocol.h>
+
+#include "components/exo/client_controlled_shell_surface.h"
+#include "components/exo/shell_surface.h"
+#include "components/exo/wayland/clients/test/wayland_client_test.h"
+#include "components/exo/wayland/server.h"
+#include "components/exo/wayland/server_util.h"
+#include "components/exo/wayland/xdg_shell.h"
+#include "components/exo/xdg_shell_surface.h"
+
+namespace exo::wayland {
+
+namespace {
+
+using CapabilityBindingTest = WaylandClientTest;
+
+class GlobalBindings {
+ public:
+ explicit GlobalBindings(struct wl_display* display) {
+ registry_ = wl_display_get_registry(display);
+ struct wl_registry_listener compositor_binding_listener = {
+ [](void* data, wl_registry* registry, uint32_t id,
+ const char* interface, uint32_t version) {
+ static_cast<GlobalBindings*>(data)->BindGlobal(registry, id,
+ interface, version);
+ },
+ nullptr};
+ wl_registry_add_listener(registry_, &compositor_binding_listener, this);
+ wl_display_roundtrip(display);
+ }
+
+ struct wl_registry* registry() { return registry_; }
+ struct wl_compositor* compositor() { return compositor_; }
+ struct wl_shell* shell() { return shell_; }
+ struct xdg_wm_base* xdg_wm_base() { return xdg_wm_base_; }
+ struct zxdg_shell_v6* zxdg_shell() { return zxdg_shell_; }
+ struct zcr_remote_shell_v1* zcr_remote_shell_v1() {
+ return zcr_remote_shell_v1_;
+ }
+ struct zcr_remote_shell_v2* zcr_remote_shell_v2() {
+ return zcr_remote_shell_v2_;
+ }
+
+ private:
+ void BindGlobal(wl_registry* registry,
+ uint32_t id,
+ const char* interface,
+ uint32_t version) {
+ if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ compositor_ = static_cast<struct wl_compositor*>(
+ wl_registry_bind(registry, id, &wl_compositor_interface, version));
+ } else if (strcmp(interface, wl_shell_interface.name) == 0) {
+ shell_ = static_cast<struct wl_shell*>(
+ wl_registry_bind(registry, id, &wl_shell_interface, version));
+ } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
+ xdg_wm_base_ = static_cast<struct xdg_wm_base*>(
+ wl_registry_bind(registry, id, &xdg_wm_base_interface, version));
+ } else if (strcmp(interface, zxdg_shell_v6_interface.name) == 0) {
+ zxdg_shell_ = static_cast<struct zxdg_shell_v6*>(
+ wl_registry_bind(registry, id, &zxdg_shell_v6_interface, version));
+ } else if (strcmp(interface, zcr_remote_shell_v1_interface.name) == 0) {
+ zcr_remote_shell_v1_ =
+ static_cast<struct zcr_remote_shell_v1*>(wl_registry_bind(
+ registry, id, &zcr_remote_shell_v1_interface, version));
+ } else if (strcmp(interface, zcr_remote_shell_v2_interface.name) == 0) {
+ zcr_remote_shell_v2_ =
+ static_cast<struct zcr_remote_shell_v2*>(wl_registry_bind(
+ registry, id, &zcr_remote_shell_v2_interface, version));
+ }
+ }
+
+ struct wl_registry* registry_ = nullptr;
+ struct wl_compositor* compositor_ = nullptr;
+ struct wl_shell* shell_ = nullptr;
+ struct xdg_wm_base* xdg_wm_base_ = nullptr;
+ struct zxdg_shell_v6* zxdg_shell_ = nullptr;
+ struct zcr_remote_shell_v1* zcr_remote_shell_v1_ = nullptr;
+ struct zcr_remote_shell_v2* zcr_remote_shell_v2_ = nullptr;
+};
+
+// Iterates over all the |server|'s clients for all of their wl_resources,
+// returning the ones with the correct |resource_class|.
+static std::vector<wl_resource*> GetResourcesByClass(
+ Server* server,
+ const char* resource_class) {
+ struct Data {
+ const char* resource_class;
+ std::vector<wl_resource*> ret;
+ };
+ Data return_holder{.resource_class = resource_class};
+ struct wl_client* client;
+ struct wl_list* all_clients =
+ wl_display_get_client_list(server->GetWaylandDisplayForTesting());
+ auto save_resource = [](struct wl_resource* resource, void* data) {
+ Data* return_hold = static_cast<Data*>(data);
+ if (strcmp(wl_resource_get_class(resource), return_hold->resource_class) ==
+ 0) {
+ return_hold->ret.push_back(resource);
+ }
+ return WL_ITERATOR_CONTINUE;
+ };
+ wl_client_for_each(client, all_clients) {
+ wl_client_for_each_resource(client, save_resource, &return_holder);
+ }
+ return std::move(return_holder).ret;
+}
+
+template <typename UserData>
+UserData* GetUserDataForInterface(Server* server,
+ const struct wl_interface& interface) {
+ std::vector<wl_resource*> surface_resources =
+ GetResourcesByClass(server, interface.name);
+ if (surface_resources.empty())
+ return nullptr;
+ return GetUserDataAs<UserData>(surface_resources[0]);
+}
+
+} // namespace
+
+TEST_F(CapabilityBindingTest, ShellSurfacesHaveCapabilities) {
+ // Due to a limitation in the viz::TestGpuServiceHolder, we are only allowed
+ // one instance of the WaylandTestHelper. For this reason, all checks must be
+ // done in a single test.
+ struct wl_display* display = wl_display_connect(nullptr);
+ GlobalBindings gb(display);
+ Capabilities* server_capabilities =
+ GetCapabilities(GetServer()->GetWaylandDisplayForTesting());
+ ASSERT_NE(server_capabilities, nullptr);
+
+ // wl_shell_surface
+ wl_shell_get_shell_surface(gb.shell(),
+ wl_compositor_create_surface(gb.compositor()));
+ wl_display_roundtrip(display);
+ EXPECT_EQ(GetUserDataForInterface<ShellSurface>(GetServer(),
+ wl_shell_surface_interface)
+ ->GetCapabilities(),
+ server_capabilities);
+
+ // xdg_surface
+ xdg_wm_base_get_xdg_surface(gb.xdg_wm_base(),
+ wl_compositor_create_surface(gb.compositor()));
+ wl_display_roundtrip(display);
+ EXPECT_EQ(GetUserDataForInterface<WaylandXdgSurface>(GetServer(),
+ xdg_surface_interface)
+ ->shell_surface->GetCapabilities(),
+ server_capabilities);
+
+ // zxdg_surface
+ zxdg_shell_v6_get_xdg_surface(gb.zxdg_shell(),
+ wl_compositor_create_surface(gb.compositor()));
+ wl_display_roundtrip(display);
+ EXPECT_EQ(GetUserDataForInterface<WaylandXdgSurface>(
+ GetServer(), zxdg_surface_v6_interface)
+ ->shell_surface->GetCapabilities(),
+ server_capabilities);
+
+ // zcr_remote_surface_v1
+ zcr_remote_shell_v1_get_remote_surface(
+ gb.zcr_remote_shell_v1(), wl_compositor_create_surface(gb.compositor()),
+ ZCR_REMOTE_SHELL_V1_CONTAINER_DEFAULT);
+ wl_display_roundtrip(display);
+ EXPECT_EQ(GetUserDataForInterface<ClientControlledShellSurface>(
+ GetServer(), zcr_remote_surface_v1_interface)
+ ->GetCapabilities(),
+ server_capabilities);
+
+ // zcr_remote_surface_v2
+ zcr_remote_shell_v2_get_remote_surface(
+ gb.zcr_remote_shell_v2(), wl_compositor_create_surface(gb.compositor()),
+ ZCR_REMOTE_SHELL_V2_CONTAINER_DEFAULT);
+ wl_display_roundtrip(display);
+ EXPECT_EQ(GetUserDataForInterface<ClientControlledShellSurface>(
+ GetServer(), zcr_remote_surface_v2_interface)
+ ->GetCapabilities(),
+ server_capabilities);
+}
+
+} // namespace exo::wayland
diff --git a/chromium/components/exo/wayland/clients/client_base.cc b/chromium/components/exo/wayland/clients/client_base.cc
index 5977a017648..50f1a59f126 100644
--- a/chromium/components/exo/wayland/clients/client_base.cc
+++ b/chromium/components/exo/wayland/clients/client_base.cc
@@ -28,6 +28,7 @@
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/shared_memory_mapper.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_number_conversions.h"
@@ -122,11 +123,12 @@ ClientBase* CastToClientBase(void* data) {
class MemfdMemoryMapping : public base::SharedMemoryMapping {
public:
- MemfdMemoryMapping(void* memory, size_t size)
- : base::SharedMemoryMapping(memory,
- size,
- size /* mapped_size */,
- base::UnguessableToken::Create()) {}
+ MemfdMemoryMapping(base::span<uint8_t> mapped_span)
+ : base::SharedMemoryMapping(
+ mapped_span,
+ mapped_span.size(),
+ base::UnguessableToken::Create(),
+ base::SharedMemoryMapper::GetDefaultInstance()) {}
};
void RegistryHandler(void* data,
@@ -153,7 +155,7 @@ void RegistryHandler(void* data,
wl_registry_bind(registry, id, &wp_presentation_interface, 1)));
} else if (strcmp(interface, "zaura_shell") == 0) {
globals->aura_shell.reset(static_cast<zaura_shell*>(
- wl_registry_bind(registry, id, &zaura_shell_interface, 14)));
+ wl_registry_bind(registry, id, &zaura_shell_interface, 34)));
} else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
globals->linux_dmabuf.reset(static_cast<zwp_linux_dmabuf_v1*>(
wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface, 2)));
@@ -531,7 +533,7 @@ bool ClientBase::Init(const InitParams& params) {
return false;
}
// We can't actually use the virtual GEM, so discard it like we do in CrOS
- if (base::LowerCaseEqualsASCII("vgem", drm_version->name))
+ if (base::EqualsCaseInsensitiveASCII("vgem", drm_version->name))
continue;
if (strstr(drm_version->name, params.use_drm_value.c_str())) {
drm_fd_ = std::move(drm_fd);
@@ -564,11 +566,14 @@ bool ClientBase::Init(const InitParams& params) {
make_current_ = std::make_unique<ui::ScopedMakeCurrent>(gl_context_.get(),
gl_surface_.get());
- if (gl::GLSurfaceEGL::HasEGLExtension("EGL_EXT_image_flush_external") ||
- gl::GLSurfaceEGL::HasEGLExtension("EGL_ARM_implicit_external_sync")) {
+ if (gl::GLSurfaceEGL::GetGLDisplayEGL()->HasEGLExtension(
+ "EGL_EXT_image_flush_external") ||
+ gl::GLSurfaceEGL::GetGLDisplayEGL()->HasEGLExtension(
+ "EGL_ARM_implicit_external_sync")) {
egl_sync_type_ = EGL_SYNC_FENCE_KHR;
}
- if (gl::GLSurfaceEGL::HasEGLExtension("EGL_ANDROID_native_fence_sync")) {
+ if (gl::GLSurfaceEGL::GetGLDisplayEGL()->HasEGLExtension(
+ "EGL_ANDROID_native_fence_sync")) {
egl_sync_type_ = EGL_SYNC_NATIVE_FENCE_ANDROID;
}
@@ -918,6 +923,16 @@ void ClientBase::HandleDmabufModifier(
uint32_t modifier_hi,
uint32_t modifier_lo) {}
+////////////////////////////////////////////////////////////////////////////////
+// zaura_output_listener
+
+void ClientBase::HandleInsets(const gfx::Insets& insets) {}
+
+void ClientBase::HandleLogicalTransform(int32_t transform) {}
+
+////////////////////////////////////////////////////////////////////////////////
+// helper functions
+
std::unique_ptr<ClientBase::Buffer> ClientBase::CreateBuffer(
const gfx::Size& size,
int32_t drm_format,
@@ -969,7 +984,8 @@ std::unique_ptr<ClientBase::Buffer> ClientBase::CreateBuffer(
return nullptr;
}
- buffer->shared_memory_mapping = MemfdMemoryMapping(mapped_data, length);
+ base::span<uint8_t> mapped_span = base::make_span(mapped_data, length);
+ buffer->shared_memory_mapping = MemfdMemoryMapping(mapped_span);
buffer->shm_pool.reset(
wl_shm_create_pool(globals_.shm.get(), memfd, length));
@@ -1248,7 +1264,13 @@ void ClientBase::SetupAuraShellIfAvailable() {
[](void* data, struct zaura_shell* zaura_shell, uint32_t layout_mode) {},
[](void* data, struct zaura_shell* zaura_shell, uint32_t id) {
CastToClientBase(data)->bug_fix_ids_.insert(id);
- }};
+ },
+ [](void* data, struct zaura_shell* zaura_shell,
+ struct wl_array* desk_names) {},
+ [](void* data, struct zaura_shell* zaura_shell,
+ int32_t active_desk_index) {},
+ [](void* data, struct zaura_shell* zaura_shell,
+ struct wl_surface* gained_active, struct wl_surface* lost_active) {}};
zaura_shell_add_listener(globals_.aura_shell.get(), &kAuraShellListener,
this);
@@ -1260,6 +1282,26 @@ void ClientBase::SetupAuraShellIfAvailable() {
}
zaura_surface_set_frame(aura_surface.get(), ZAURA_SURFACE_FRAME_TYPE_NORMAL);
+
+ static zaura_output_listener kAuraOutputListener = {
+ [](void* data, struct zaura_output* zaura_output, uint32_t flags,
+ uint32_t scale) {},
+ [](void* data, struct zaura_output* zaura_output, uint32_t connection) {},
+ [](void* data, struct zaura_output* zaura_output, uint32_t scale) {},
+ [](void* data, struct zaura_output* zaura_output, int32_t top,
+ int32_t left, int32_t bottom, int32_t right) {
+ CastToClientBase(data)->HandleInsets(
+ gfx::Insets::TLBR(top, left, bottom, right));
+ },
+ [](void* data, struct zaura_output* zaura_output, int32_t transform) {
+ CastToClientBase(data)->HandleLogicalTransform(transform);
+ },
+ };
+
+ std::unique_ptr<zaura_output> aura_output(zaura_shell_get_aura_output(
+ globals_.aura_shell.get(), globals_.output.get()));
+ zaura_output_add_listener(aura_output.get(), &kAuraOutputListener, this);
+ globals_.aura_output = std::move(aura_output);
}
void ClientBase::SetupPointerStylus() {
diff --git a/chromium/components/exo/wayland/clients/client_base.h b/chromium/components/exo/wayland/clients/client_base.h
index b4e47bd13f9..8b5fb7d68d1 100644
--- a/chromium/components/exo/wayland/clients/client_base.h
+++ b/chromium/components/exo/wayland/clients/client_base.h
@@ -15,6 +15,7 @@
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
@@ -85,6 +86,7 @@ class ClientBase {
std::unique_ptr<wl_subcompositor> subcompositor;
std::unique_ptr<wl_touch> touch;
std::unique_ptr<zaura_shell> aura_shell;
+ std::unique_ptr<zaura_output> aura_output;
std::unique_ptr<zxdg_shell_v6> xdg_shell_v6;
std::unique_ptr<xdg_wm_base> xdg_wm_base;
std::unique_ptr<zwp_fullscreen_shell_v1> fullscreen_shell;
@@ -204,6 +206,10 @@ class ClientBase {
uint32_t modifier_hi,
uint32_t modifier_lo);
+ // zaura_output_listener
+ virtual void HandleInsets(const gfx::Insets& insets);
+ virtual void HandleLogicalTransform(int32_t transform);
+
gfx::Size size_ = gfx::Size(256, 256);
int scale_ = 1;
int transform_ = WL_OUTPUT_TRANSFORM_NORMAL;
diff --git a/chromium/components/exo/wayland/clients/client_helper.cc b/chromium/components/exo/wayland/clients/client_helper.cc
index 343a9c08c1b..dd99e1e966a 100644
--- a/chromium/components/exo/wayland/clients/client_helper.cc
+++ b/chromium/components/exo/wayland/clients/client_helper.cc
@@ -91,6 +91,8 @@ DEFAULT_DELETER(zcr_gaming_input_v2, zcr_gaming_input_v2_destroy)
DEFAULT_DELETER(zcr_keyboard_configuration_v1,
zcr_keyboard_configuration_v1_destroy)
DEFAULT_DELETER(zcr_keyboard_extension_v1, zcr_keyboard_extension_v1_destroy)
+DEFAULT_DELETER(zwp_keyboard_shortcuts_inhibit_manager_v1,
+ zwp_keyboard_shortcuts_inhibit_manager_v1_destroy)
DEFAULT_DELETER(zcr_notification_shell_v1, zcr_notification_shell_v1_destroy)
DEFAULT_DELETER(zcr_remote_shell_v1, zcr_remote_shell_v1_destroy)
DEFAULT_DELETER(zcr_remote_shell_v2, zcr_remote_shell_v2_destroy)
diff --git a/chromium/components/exo/wayland/clients/client_helper.h b/chromium/components/exo/wayland/clients/client_helper.h
index 96012d40963..8fef924e1a8 100644
--- a/chromium/components/exo/wayland/clients/client_helper.h
+++ b/chromium/components/exo/wayland/clients/client_helper.h
@@ -16,6 +16,7 @@
#include <input-timestamps-unstable-v1-client-protocol.h>
#include <keyboard-configuration-unstable-v1-client-protocol.h>
#include <keyboard-extension-unstable-v1-client-protocol.h>
+#include <keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h>
#include <linux-dmabuf-unstable-v1-client-protocol.h>
#include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
#include <notification-shell-unstable-v1-client-protocol.h>
@@ -112,6 +113,7 @@ DEFAULT_DELETER_FDECL(zcr_cursor_shapes_v1)
DEFAULT_DELETER_FDECL(zcr_gaming_input_v2)
DEFAULT_DELETER_FDECL(zcr_keyboard_configuration_v1)
DEFAULT_DELETER_FDECL(zcr_keyboard_extension_v1)
+DEFAULT_DELETER_FDECL(zwp_keyboard_shortcuts_inhibit_manager_v1)
DEFAULT_DELETER_FDECL(zcr_notification_shell_v1)
DEFAULT_DELETER_FDECL(zcr_remote_shell_v1)
DEFAULT_DELETER_FDECL(zcr_remote_shell_v2)
diff --git a/chromium/components/exo/wayland/clients/info.cc b/chromium/components/exo/wayland/clients/info.cc
index 1e97fac31bc..6e216148979 100644
--- a/chromium/components/exo/wayland/clients/info.cc
+++ b/chromium/components/exo/wayland/clients/info.cc
@@ -43,6 +43,10 @@ struct Info {
};
// |next_scales| are swapped with |scales| after receiving output done event.
std::vector<Scale> scales, next_scales;
+ struct {
+ int32_t top, left, bottom, right;
+ } insets;
+ int32_t logical_transform;
std::unique_ptr<wl_output> output;
std::unique_ptr<zaura_output> aura_output;
};
@@ -155,6 +159,25 @@ void AuraOutputDeviceScaleFactor(void* data,
info->device_scale_factor = device_scale_factor;
}
+void AuraOutputInsets(void* data,
+ zaura_output* output,
+ int32_t top,
+ int32_t left,
+ int32_t bottom,
+ int32_t right) {
+ Info* info = static_cast<Info*>(data);
+
+ info->insets = {top, left, bottom, right};
+}
+
+void AuraOutputLogicalTransform(void* data,
+ zaura_output* output,
+ int32_t transform) {
+ Info* info = static_cast<Info*>(data);
+
+ info->logical_transform = transform;
+}
+
std::string OutputSubpixelToString(int32_t subpixel) {
switch (subpixel) {
case WL_OUTPUT_SUBPIXEL_UNKNOWN:
@@ -293,7 +316,8 @@ int main(int argc, char* argv[]) {
OutputScale};
zaura_output_listener aura_output_listener = {
- AuraOutputScale, AuraOutputConnection, AuraOutputDeviceScaleFactor};
+ AuraOutputScale, AuraOutputConnection, AuraOutputDeviceScaleFactor,
+ AuraOutputInsets, AuraOutputLogicalTransform};
for (auto& info : globals.outputs) {
wl_output_add_listener(info.output.get(), &output_listener, &info);
if (globals.aura_shell) {
@@ -349,6 +373,14 @@ int main(int argc, char* argv[]) {
<< AuraOutputScaleFlagsToString(scale.flags) << std::endl;
}
}
+ std::cout << " insets:" << std::endl
+ << " top: " << info.insets.top << std::endl
+ << " left: " << info.insets.left << std::endl
+ << " bottom: " << info.insets.bottom << std::endl
+ << " right: " << info.insets.right << std::endl
+ << std::endl;
+ std::cout << " logical_transform: "
+ << OutputTransformToString(info.logical_transform) << std::endl;
}
return 0;
diff --git a/chromium/components/exo/wayland/clients/wayland_client_integration_tests.cc b/chromium/components/exo/wayland/clients/wayland_client_integration_tests.cc
deleted file mode 100644
index c6f2522b9be..00000000000
--- a/chromium/components/exo/wayland/clients/wayland_client_integration_tests.cc
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 2021 The Chromium 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 <gbm.h>
-#include <cstdint>
-#include <iterator>
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/containers/flat_map.h"
-#include "base/containers/queue.h"
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/task_environment.h"
-#include "base/test/test_suite.h"
-#include "components/exo/wayland/clients/client_base.h"
-#include "components/exo/wayland/clients/client_helper.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/buffer_format_util.h"
-#include "ui/gfx/buffer_types.h"
-#include "ui/gfx/buffer_usage_util.h"
-#include "ui/gfx/linux/drm_util_linux.h"
-#include "ui/gfx/linux/gbm_util.h"
-
-namespace {
-
-std::string DrmCodeToString(uint64_t drm_format) {
- return std::string{static_cast<char>(drm_format),
- static_cast<char>(drm_format >> 8),
- static_cast<char>(drm_format >> 16),
- static_cast<char>(drm_format >> 24), 0};
-}
-
-std::string DrmCodeToBufferFormatString(uint64_t drm_format) {
- return gfx::BufferFormatToString(
- ui::GetBufferFormatFromFourCCFormat(drm_format));
-}
-
-} // namespace
-
-namespace exo {
-namespace wayland {
-namespace clients {
-
-namespace {
-
-class BufferCheckerTestClient : public ClientBase {
- public:
- explicit BufferCheckerTestClient() = default;
- ~BufferCheckerTestClient() override = default;
-
- bool HasAnySupportedUsages(uint32_t format) {
- std::vector<gfx::BufferUsage> supported_usages;
- bool callback_pending = false;
- std::unique_ptr<wl_callback> frame_callback;
- wl_callback_listener frame_listener = {
- [](void* data, struct wl_callback*, uint32_t) {
- *(static_cast<bool*>(data)) = false;
- }};
-
- base::queue<gfx::BufferUsage> usages_to_test;
- for (int i = 0; i < static_cast<int>(gfx::BufferUsage::LAST); i++)
- usages_to_test.push(static_cast<gfx::BufferUsage>(i));
-
- gfx::BufferUsage current_usage;
- std::unique_ptr<Buffer> current_buffer;
- do {
- if (callback_pending)
- continue;
-
- if (current_buffer) {
- supported_usages.push_back(current_usage);
- }
-
- if (wl_display_get_error(display_.get())) {
- LOG(ERROR) << "Wayland error encountered";
- return false;
- }
-
- // Buffers may fail to be created, so loop until we get one or return
- // early if we run out. We can't loop in the outer loop because, without
- // doing the rest of the code, we won't be dispatched to again.
- do {
- if (usages_to_test.size() == 0) {
- std::vector<std::string> supported_usage_strings;
- std::transform(supported_usages.begin(), supported_usages.end(),
- std::back_inserter(supported_usage_strings),
- gfx::BufferUsageToString);
- LOG(INFO) << "Successfully used buffer with format drm: "
- << DrmCodeToString(format) << " gfx::BufferFormat: "
- << DrmCodeToBufferFormatString(format)
- << " gfx::BufferUsages: ["
- << base::JoinString(supported_usage_strings, ", ") << "]";
- return supported_usages.size() > 0;
- }
-
- current_usage = usages_to_test.front();
- usages_to_test.pop();
-
- current_buffer = CreateDrmBuffer(
- gfx::Size(surface_size_.width(), surface_size_.height()), format,
- ui::BufferUsageToGbmFlags(current_usage), /*y_invert=*/false);
- if (!current_buffer) {
- LOG(ERROR) << "Unable to create buffer for drm: "
- << DrmCodeToString(format) << " gfx::BufferFormat: "
- << DrmCodeToBufferFormatString(format)
- << " gfx::BufferUsage "
- << gfx::BufferUsageToString(current_usage);
- }
- } while (current_buffer == nullptr);
-
- LOG(INFO) << "Attempting to use buffer with format drm: "
- << DrmCodeToString(format)
- << " gfx::BufferFormat: " << DrmCodeToBufferFormatString(format)
- << " gfx::BufferUsage "
- << gfx::BufferUsageToString(current_usage);
- ;
-
- wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
- surface_size_.height());
- wl_surface_attach(surface_.get(), current_buffer->buffer.get(), 0, 0);
-
- frame_callback.reset(wl_surface_frame(surface_.get()));
- wl_callback_add_listener(frame_callback.get(), &frame_listener,
- &callback_pending);
- callback_pending = true;
- wl_surface_commit(surface_.get());
-
- wl_display_flush(display_.get());
- } while (wl_display_dispatch(display_.get()) != -1);
-
- LOG(ERROR)
- << "Expected to return from inside the loop. Wayland disconnected?";
- return false;
- }
-
- std::vector<uint32_t> reported_formats;
- base::flat_map<uint32_t, std::vector<uint64_t>> reported_format_modifer_map;
-
- protected:
- void HandleDmabufFormat(void* data,
- struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1,
- uint32_t format) override {
- reported_formats.push_back(format);
- }
-
- void HandleDmabufModifier(void* data,
- struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1,
- uint32_t format,
- uint32_t modifier_hi,
- uint32_t modifier_lo) override {
- if (!reported_format_modifer_map.contains(format)) {
- reported_format_modifer_map[format] = std::vector<uint64_t>();
- }
-
- uint64_t modifier = static_cast<uint64_t>(modifier_hi) << 32 | modifier_lo;
- reported_format_modifer_map[format].push_back(modifier);
- }
-};
-
-} // namespace
-} // namespace clients
-} // namespace wayland
-} // namespace exo
-
-class IntegrationTestClientTest : public testing::Test {
- protected:
- void SetUp() override {
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- CHECK(base_params_.FromCommandLine(*command_line));
- CHECK(base_params_.use_drm) << "Missing --use-drm parameter which is "
- "required for gbm buffer allocation";
- }
-
- base::test::SingleThreadTaskEnvironment task_environment_{
- base::test::TaskEnvironment::MainThreadType::UI};
-
- exo::wayland::clients::ClientBase::InitParams base_params_;
-};
-
-void PrintReportedFormats(std::vector<uint32_t>& formats) {
- std::vector<std::string> drm_names;
- std::vector<std::string> buffer_names;
- for (auto format : formats) {
- drm_names.push_back(DrmCodeToString(format));
- buffer_names.push_back(DrmCodeToBufferFormatString(format));
- }
- LOG(ERROR) << "zwp_linux_dmabuf_v1 reported supported DRM formats: "
- << base::JoinString(drm_names, ", ");
- LOG(ERROR) << "zwp_linux_dmabuf_v1 reported supported gfx::BufferFormats: "
- << base::JoinString(buffer_names, ", ");
-}
-
-TEST_F(IntegrationTestClientTest, CanUseAllReportedBuffers) {
- exo::wayland::clients::BufferCheckerTestClient client;
- auto params = base_params_;
- // Initialize no buffers when we start, wait until we've gotten the list
- params.num_buffers = 0;
- ASSERT_TRUE(client.Init(params));
- PrintReportedFormats(client.reported_formats);
- for (auto format : client.reported_formats)
- EXPECT_TRUE(client.HasAnySupportedUsages(format));
-}
-
-int main(int argc, char* argv[]) {
- base::CommandLine::Init(argc, argv);
- base::TestSuite test_suite(argc, argv);
-
- // Tests may not run to completion if we do not run them serially.
- return base::LaunchUnitTestsSerially(
- argc, argv,
- base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
-}
diff --git a/chromium/components/exo/wayland/protocol/aura-shell.xml b/chromium/components/exo/wayland/protocol/aura-shell.xml
index 4156e70a3fa..cdfad469ac8 100644
--- a/chromium/components/exo/wayland/protocol/aura-shell.xml
+++ b/chromium/components/exo/wayland/protocol/aura-shell.xml
@@ -24,7 +24,7 @@
DEALINGS IN THE SOFTWARE.
</copyright>
- <interface name="zaura_shell" version="29">
+ <interface name="zaura_shell" version="34">
<description summary="aura_shell">
The global interface exposing aura shell capabilities is used to
instantiate an interface extension for a wl_surface object.
@@ -512,7 +512,7 @@
</event>
</interface>
- <interface name="zaura_output" version="6">
+ <interface name="zaura_output" version="34">
<description summary="aura shell interface to a wl_output">
An additional interface to a wl_output object, which allows the
client to access aura shell specific functionality for output.
@@ -614,9 +614,40 @@
</description>
<arg name="scale" type="uint" enum="scale_factor" summary="output device scale factor"/>
</event>
- </interface>
- <interface name="zaura_toplevel" version="29">
+ <!-- Version 33 additions -->
+
+ <event name="insets" since="33">
+ <description summary="advertise the insets for the output">
+ This event describes the insets for the output in logical screen
+ coordinates, from which the work area can be calculated.
+
+ This event is sent before wl_output.done, after which the client would
+ apply the change.
+ </description>
+ <arg name="top" type="int"/>
+ <arg name="left" type="int"/>
+ <arg name="bottom" type="int"/>
+ <arg name="right" type="int"/>
+ </event>
+
+ <!-- Version 34 additions -->
+
+ <event name="logical_transform" since="34">
+ <description summary="advertise the output's logical transform">
+ This event describes the logical transform for the output. Whereas
+ wl_output.geometry's transform corresponds to the display's panel
+ rotation, the logical transform corresponds to the display's logical
+ rotation.
+
+ This event is sent before wl_output.done, after which the client would
+ apply the change.
+ </description>
+ <arg name="transform" type="int" enum="wl_output.transform"/>
+ </event>
+ </interface>
+
+ <interface name="zaura_toplevel" version="32">
<description summary="aura shell interface to the toplevel shell">
An interface to the toplevel shell, which allows the
client to access shell specific functionality.
@@ -701,6 +732,41 @@
<arg name="x" type="int" />
<arg name="y" type="int" />
</event>
+
+ <request name="set_restore_info" since="30">
+ <description summary="set session id and restore id">
+ Request session id and restore id of a newly created browser window.
+ Set the information used by compositor to restore the toplevel
+ surface state, such as window position, window state, upon creation.
+ This is not double buffered and must be set before sending first commit.
+ </description>
+ <arg name="restore_session_id" type="int" summary="unique browser session id"/>
+ <arg name="restore_window_id" type="int" summary="restore browser window id"/>
+ </request>
+
+ <request name="set_system_modal" since="31">
+ <description summary="make window a system modal">
+ Requests that the toplevel surface should become a system modal. The
+ compositor will prevent other windows from receiving events. If there
+ are multiple system modal surfaces, the compositor will decide which
+ one to receive events.
+ </description>
+ </request>
+
+ <request name="unset_system_modal" since="31">
+ <description summary="unset window system modal state">
+ Requests that the system modal state of the toplevel surface will be
+ unset. The compositor will then allow other windows to recieve events.
+ </description>
+ </request>
+
+ <request name="set_restore_info_with_window_id_source" since="32">
+ <description summary="set session id and restore window id source">
+ Request session id and restore id of the window. Set the information used by compositor to restore the toplevel surface state, such as window position, window state, upon creation. This is not double buffered and must be set before sending first commit. This is different from set_restore_info, used for clients that create multiple windows associated with restore_id_source.
+ </description>
+ <arg name="restore_session_id" type="int" summary="unique browser session id"/>
+ <arg name="restore_window_id_source" type="string" summary="restore window id source"/>
+ </request>
</interface>
<interface name="zaura_popup" version="28">
diff --git a/chromium/components/exo/wayland/protocol/surface-augmenter.xml b/chromium/components/exo/wayland/protocol/surface-augmenter.xml
index 66ec0556cf7..e541109db8e 100644
--- a/chromium/components/exo/wayland/protocol/surface-augmenter.xml
+++ b/chromium/components/exo/wayland/protocol/surface-augmenter.xml
@@ -24,7 +24,7 @@
DEALINGS IN THE SOFTWARE.
</copyright>
- <interface name="surface_augmenter" version="2">
+ <interface name="surface_augmenter" version="3">
<description summary="surface composition delegation">
The global interface exposing surface delegated composition
capabilities is used to instantiate an interface extension for a
@@ -89,7 +89,7 @@
</request>
</interface>
- <interface name="augmented_surface" version="2">
+ <interface name="augmented_surface" version="3">
<description summary="delegate composition of a wl_surface">
An additional interface to a wl_surface object, which allows the
client to specify the delegated composition of the surface
@@ -155,6 +155,21 @@
<arg name="bottom_right" type="fixed" summary="bottom right corner"/>
<arg name="bottom_left" type="fixed" summary="bottom left corner"/>
</request>
+
+ <!-- Version 3 additions -->
+
+ <request name="set_background_color" since="3">
+ <description summary="sets a background color of this surface">
+ Sets a background color of a this surface. This information will be
+ associated with the next buffer commit. Please note this is different
+ from solid color buffers, which creates a new buffer instance, and
+ rather provides additional information how the buffer should be
+ composited. Passing empty array means the background color is reset.
+ The default value is determined by the Wayland compositor then.
+ </description>
+ <arg name="color" type="array"
+ summary="overlay color represented by a SkColor4f"/>
+ </request>
</interface>
<interface name="augmented_sub_surface" version="1">
diff --git a/chromium/components/exo/wayland/server.cc b/chromium/components/exo/wayland/server.cc
index 6b00d123a1a..2e6270a8a07 100644
--- a/chromium/components/exo/wayland/server.cc
+++ b/chromium/components/exo/wayland/server.cc
@@ -13,6 +13,7 @@
#include <input-timestamps-unstable-v1-server-protocol.h>
#include <keyboard-configuration-unstable-v1-server-protocol.h>
#include <keyboard-extension-unstable-v1-server-protocol.h>
+#include <keyboard-shortcuts-inhibit-unstable-v1-server-protocol.h>
#include <linux-explicit-synchronization-unstable-v1-server-protocol.h>
#include <notification-shell-unstable-v1-server-protocol.h>
#include <overlay-prioritizer-server-protocol.h>
@@ -52,6 +53,7 @@
#include "build/chromeos_buildflags.h"
#include "components/exo/buildflags.h"
#include "components/exo/capabilities.h"
+#include "components/exo/common_utils.h"
#include "components/exo/display.h"
#include "components/exo/wayland/overlay_prioritizer.h"
#include "components/exo/wayland/serial_tracker.h"
@@ -97,6 +99,7 @@
#include "components/exo/wayland/zcr_touchpad_haptics.h"
#include "components/exo/wayland/zwp_idle_inhibit_manager.h"
#include "components/exo/wayland/zwp_input_timestamps_manager.h"
+#include "components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.h"
#include "components/exo/wayland/zwp_pointer_constraints.h"
#include "components/exo/wayland/zwp_pointer_gestures.h"
#include "components/exo/wayland/zwp_relative_pointer_manager.h"
@@ -140,18 +143,6 @@ const char kWaylandSocketGroup[] = "wayland";
constexpr base::FilePath::CharType kCustomServerDir[] =
FILE_PATH_LITERAL("wayland");
-bool IsDrmAtomicAvailable() {
-#if defined(USE_OZONE)
- auto& host_properties =
- ui::OzonePlatform::GetInstance()->GetPlatformRuntimeProperties();
- return host_properties.supports_overlays;
-#else
- LOG(WARNING) << "Ozone disabled, cannot determine whether DrmAtomic is "
- "present. Assuming it is not";
- return false;
-#endif
-}
-
void wayland_log(const char* fmt, va_list argp) {
LOG(WARNING) << "libwayland: " << base::StringPrintV(fmt, argp);
}
@@ -334,7 +325,7 @@ void Server::Initialize() {
bind_shell);
wl_global_create(wl_display_.get(), &zcr_cursor_shapes_v1_interface, 1,
display_, bind_cursor_shapes);
- wl_global_create(wl_display_.get(), &zcr_gaming_input_v2_interface, 2,
+ wl_global_create(wl_display_.get(), &zcr_gaming_input_v2_interface, 3,
display_, bind_gaming_input);
wl_global_create(wl_display_.get(), &zcr_keyboard_configuration_v1_interface,
zcr_keyboard_configuration_v1_interface.version, display_,
@@ -375,10 +366,8 @@ void Server::Initialize() {
display_, bind_extended_drag);
wl_global_create(wl_display_.get(), &zxdg_output_manager_v1_interface, 3,
display_, bind_zxdg_output_manager);
- if (ash::features::IsIdleInhibitEnabled()) {
- wl_global_create(wl_display_.get(), &zwp_idle_inhibit_manager_v1_interface,
- 1, display_, bind_zwp_idle_inhibit_manager);
- }
+ wl_global_create(wl_display_.get(), &zwp_idle_inhibit_manager_v1_interface, 1,
+ display_, bind_zwp_idle_inhibit_manager);
weston_test_holder_ = std::make_unique<WestonTest>(this);
@@ -387,6 +376,10 @@ void Server::Initialize() {
wl_global_create(wl_display_.get(), &zcr_keyboard_extension_v1_interface, 2,
zcr_keyboard_extension_data_.get(), bind_keyboard_extension);
+ wl_global_create(wl_display_.get(),
+ &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1,
+ display_, bind_keyboard_shortcuts_inhibit_manager);
+
zwp_text_manager_data_ = std::make_unique<WaylandTextInputManager>(
display_->seat()->xkb_tracker(), serial_tracker_.get());
wl_global_create(wl_display_.get(), &zwp_text_input_manager_v1_interface, 1,
@@ -394,7 +387,7 @@ void Server::Initialize() {
zcr_text_input_extension_data_ =
std::make_unique<WaylandTextInputExtension>();
- wl_global_create(wl_display_.get(), &zcr_text_input_extension_v1_interface, 1,
+ wl_global_create(wl_display_.get(), &zcr_text_input_extension_v1_interface, 2,
zcr_text_input_extension_data_.get(),
bind_text_input_extension);
diff --git a/chromium/components/exo/wayland/surface_augmenter.cc b/chromium/components/exo/wayland/surface_augmenter.cc
index 1135e3b8e4e..5d9081b329b 100644
--- a/chromium/components/exo/wayland/surface_augmenter.cc
+++ b/chromium/components/exo/wayland/surface_augmenter.cc
@@ -66,6 +66,10 @@ class AugmentedSurface : public SurfaceObserver {
surface_->SetViewport(gfx::SizeF(width, height));
}
+ void SetBackgroundColor(absl::optional<SkColor> background_color) {
+ surface_->SetBackgroundColor(background_color);
+ }
+
// SurfaceObserver:
void OnSurfaceDestroying(Surface* surface) override {
surface->RemoveSurfaceObserver(this);
@@ -130,10 +134,25 @@ void augmented_surface_set_rounded_corners_bounds(wl_client* client,
wl_fixed_to_double(bottom_left));
}
+void augmented_surface_set_background_color(wl_client* client,
+ wl_resource* resource,
+ wl_array* color_data) {
+ absl::optional<SkColor> sk_color;
+ // Empty data means no color.
+ if (color_data->size) {
+ float* data = reinterpret_cast<float*>(color_data->data);
+ SkColor4f color = {data[0], data[1], data[2], data[3]};
+ sk_color = color.toSkColor();
+ }
+
+ GetUserDataAs<AugmentedSurface>(resource)->SetBackgroundColor(sk_color);
+}
+
const struct augmented_surface_interface augmented_implementation = {
augmented_surface_destroy, augmented_surface_set_corners_DEPRECATED,
augmented_surface_set_destination_size,
- augmented_surface_set_rounded_corners_bounds};
+ augmented_surface_set_rounded_corners_bounds,
+ augmented_surface_set_background_color};
////////////////////////////////////////////////////////////////////////////////
// augmented_sub_surface_interface:
diff --git a/chromium/components/exo/wayland/surface_augmenter.h b/chromium/components/exo/wayland/surface_augmenter.h
index 986f405cd54..40d06050c52 100644
--- a/chromium/components/exo/wayland/surface_augmenter.h
+++ b/chromium/components/exo/wayland/surface_augmenter.h
@@ -12,7 +12,7 @@ struct wl_client;
namespace exo {
namespace wayland {
-constexpr uint32_t kSurfaceAugmenterVersion = 2;
+constexpr uint32_t kSurfaceAugmenterVersion = 3;
void bind_surface_augmenter(wl_client* client,
void* data,
diff --git a/chromium/components/exo/wayland/wayland_display_observer.cc b/chromium/components/exo/wayland/wayland_display_observer.cc
index 440fb390b31..af675be97cc 100644
--- a/chromium/components/exo/wayland/wayland_display_observer.cc
+++ b/chromium/components/exo/wayland/wayland_display_observer.cc
@@ -9,10 +9,16 @@
#include <string>
+#include "chrome-color-management-server-protocol.h"
+#include "components/exo/wayland/server_util.h"
#include "components/exo/wayland/wayland_display_output.h"
+#include "components/exo/wayland/wayland_display_util.h"
+#include "components/exo/wayland/zcr_color_manager.h"
#include "components/exo/wm_helper.h"
+#include "ui/display/display_observer.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/screen.h"
+#include "wayland-server-protocol-core.h"
namespace exo {
namespace wayland {
@@ -23,6 +29,10 @@ WaylandDisplayHandler::WaylandDisplayHandler(WaylandDisplayOutput* output,
}
WaylandDisplayHandler::~WaylandDisplayHandler() {
+ if (color_management_output_resource_)
+ wl_resource_set_user_data(color_management_output_resource_, nullptr);
+ if (xdg_output_resource_)
+ wl_resource_set_user_data(xdg_output_resource_, nullptr);
output_->UnregisterOutput(output_resource_);
}
@@ -73,6 +83,20 @@ void WaylandDisplayHandler::OnDisplayMetricsChanged(
for (auto& observer : observers_)
needs_done |= observer.SendDisplayMetrics(display, changed_metrics);
+ // if the color space changed, send a
+ // zcr_color_management_output.color_space_changed() event as well as
+ // wl_output_send_done() event.
+ if ((changed_metrics & DISPLAY_METRIC_COLOR_SPACE) ==
+ DISPLAY_METRIC_COLOR_SPACE) {
+ if (color_management_output_resource_) {
+ // TODO(b/217795369): will need to track and send events for each
+ // potential client's zcr color management output resource.
+ zcr_color_management_output_v1_send_color_space_changed(
+ color_management_output_resource_);
+ needs_done = true;
+ }
+ }
+
if (needs_done) {
if (wl_resource_get_version(output_resource_) >=
WL_OUTPUT_DONE_SINCE_VERSION) {
@@ -100,6 +124,37 @@ void WaylandDisplayHandler::UnsetXdgOutputResource() {
xdg_output_resource_ = nullptr;
}
+void WaylandDisplayHandler::OnGetColorManagementOutput(
+ wl_resource* color_management_output_resource) {
+ DCHECK(!color_management_output_resource_);
+ color_management_output_resource_ = color_management_output_resource;
+
+ // TODO(b/215778539): send an appropriate EDR value. Currently using dummy
+ // value of 3000. Also find out when dynamic range changes and if it happens
+ // in OnDisplayMetricsChanged().
+ zcr_color_management_output_v1_send_extended_dynamic_range(
+ color_management_output_resource_, 3000);
+
+ wl_output_send_done(output_resource_);
+ wl_client_flush(wl_resource_get_client(output_resource_));
+}
+
+void WaylandDisplayHandler::UnsetColorManagementOutputResource() {
+ DCHECK(color_management_output_resource_);
+ color_management_output_resource_ = nullptr;
+}
+
+void WaylandDisplayHandler::XdgOutputSendLogicalPosition(
+ const gfx::Point& position) {
+ zxdg_output_v1_send_logical_position(xdg_output_resource_, position.x(),
+ position.y());
+}
+
+void WaylandDisplayHandler::XdgOutputSendLogicalSize(const gfx::Size& size) {
+ zxdg_output_v1_send_logical_size(xdg_output_resource_, size.width(),
+ size.height());
+}
+
bool WaylandDisplayHandler::SendDisplayMetrics(const display::Display& display,
uint32_t changed_metrics) {
if (!output_resource_)
@@ -166,10 +221,8 @@ bool WaylandDisplayHandler::SendDisplayMetrics(const display::Display& display,
static_cast<int>(60000));
if (xdg_output_resource_) {
- const gfx::Size logical_size = ScaleToRoundedSize(
- physical_size_px, 1.0f / display.device_scale_factor());
- zxdg_output_v1_send_logical_size(xdg_output_resource_, logical_size.width(),
- logical_size.height());
+ XdgOutputSendLogicalPosition(origin);
+ XdgOutputSendLogicalSize(display.bounds().size());
} else {
if (wl_resource_get_version(output_resource_) >=
WL_OUTPUT_SCALE_SINCE_VERSION) {
@@ -183,24 +236,5 @@ bool WaylandDisplayHandler::SendDisplayMetrics(const display::Display& display,
return true;
}
-wl_output_transform WaylandDisplayHandler::OutputTransform(
- display::Display::Rotation rotation) {
- // Note: |rotation| describes the counter clockwise rotation that a
- // display's output is currently adjusted for, which is the inverse
- // of what we need to return.
- switch (rotation) {
- case display::Display::ROTATE_0:
- return WL_OUTPUT_TRANSFORM_NORMAL;
- case display::Display::ROTATE_90:
- return WL_OUTPUT_TRANSFORM_270;
- case display::Display::ROTATE_180:
- return WL_OUTPUT_TRANSFORM_180;
- case display::Display::ROTATE_270:
- return WL_OUTPUT_TRANSFORM_90;
- }
- NOTREACHED();
- return WL_OUTPUT_TRANSFORM_NORMAL;
-}
-
} // namespace wayland
} // namespace exo
diff --git a/chromium/components/exo/wayland/wayland_display_observer.h b/chromium/components/exo/wayland/wayland_display_observer.h
index a5f688360d1..5796e4e0366 100644
--- a/chromium/components/exo/wayland/wayland_display_observer.h
+++ b/chromium/components/exo/wayland/wayland_display_observer.h
@@ -58,18 +58,25 @@ class WaylandDisplayHandler : public display::DisplayObserver,
// Unset the xdg output object.
void UnsetXdgOutputResource();
+ // TODO(b/223538419): Move this functionality into an observer
+ // Called when a color_management_output object is created through
+ // get_color_management_output() request by the wayland client.
+ void OnGetColorManagementOutput(wl_resource* color_manager_output_resource);
+ // Unset the color management object
+ void UnsetColorManagementOutputResource();
+
protected:
wl_resource* output_resource() const { return output_resource_; }
+ // Overridable for testing.
+ virtual void XdgOutputSendLogicalPosition(const gfx::Point& position);
+ virtual void XdgOutputSendLogicalSize(const gfx::Size& size);
+
private:
// Overridden from WaylandDisplayObserver:
bool SendDisplayMetrics(const display::Display& display,
uint32_t changed_metrics) override;
- // Returns the transform that a compositor will apply to a surface to
- // compensate for the rotation of an output device.
- wl_output_transform OutputTransform(display::Display::Rotation rotation);
-
// Output.
WaylandDisplayOutput* output_;
@@ -79,6 +86,9 @@ class WaylandDisplayHandler : public display::DisplayObserver,
// Resource associated with a zxdg_output_v1 object.
wl_resource* xdg_output_resource_ = nullptr;
+ // Resource associated with a zcr_color_management_output_v1 object.
+ wl_resource* color_management_output_resource_ = nullptr;
+
base::ObserverList<WaylandDisplayObserver> observers_;
display::ScopedDisplayObserver display_observer_{this};
diff --git a/chromium/components/exo/wayland/wayland_display_observer_unittest.cc b/chromium/components/exo/wayland/wayland_display_observer_unittest.cc
new file mode 100644
index 00000000000..4199915ed44
--- /dev/null
+++ b/chromium/components/exo/wayland/wayland_display_observer_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright 2022 The Chromium 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/exo/wayland/wayland_display_observer.h"
+
+#include <sys/socket.h>
+#include <wayland-server-protocol-core.h>
+#include <xdg-output-unstable-v1-server-protocol.h>
+
+#include "components/exo/test/exo_test_base.h"
+#include "components/exo/wayland/wayland_display_output.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace exo {
+namespace wayland {
+namespace {
+
+class MockWaylandDisplayHandler : public WaylandDisplayHandler {
+ public:
+ using WaylandDisplayHandler::WaylandDisplayHandler;
+ MockWaylandDisplayHandler(const MockWaylandDisplayHandler&) = delete;
+ MockWaylandDisplayHandler& operator=(const MockWaylandDisplayHandler&) =
+ delete;
+ ~MockWaylandDisplayHandler() override = default;
+
+ MOCK_METHOD(void,
+ XdgOutputSendLogicalPosition,
+ (const gfx::Point&),
+ (override));
+ MOCK_METHOD(void, XdgOutputSendLogicalSize, (const gfx::Size&), (override));
+};
+
+class WaylandDisplayObserverTest : public test::ExoTestBase {
+ protected:
+ static constexpr uint32_t kAllChanges = 0xFFFFFFFF;
+
+ void SetUp() override {
+ test::ExoTestBase::SetUp();
+
+ ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds_), 0);
+ wayland_display_ = wl_display_create();
+ client_ = wl_client_create(wayland_display_, fds_[0]);
+ wl_output_resource_ =
+ wl_resource_create(client_, &wl_output_interface, 2, 0);
+ xdg_output_resource_ =
+ wl_resource_create(client_, &zxdg_output_v1_interface, 2, 0);
+ output_ = std::make_unique<WaylandDisplayOutput>(GetPrimaryDisplay().id());
+ handler_ = std::make_unique<::testing::NiceMock<MockWaylandDisplayHandler>>(
+ output_.get(), wl_output_resource_);
+ handler_->OnXdgOutputCreated(xdg_output_resource_);
+ handler_->Initialize();
+ }
+
+ void TearDown() override {
+ handler_->UnsetXdgOutputResource();
+ wl_resource_destroy(xdg_output_resource_);
+ wl_resource_destroy(wl_output_resource_);
+ wl_client_destroy(client_);
+ wl_display_destroy(wayland_display_);
+ close(fds_[1]);
+
+ test::ExoTestBase::TearDown();
+ }
+
+ int fds_[2] = {0, 0};
+ wl_display* wayland_display_ = nullptr;
+ wl_client* client_ = nullptr;
+ wl_resource* wl_output_resource_ = nullptr;
+ wl_resource* xdg_output_resource_ = nullptr;
+ std::unique_ptr<WaylandDisplayOutput> output_;
+ std::unique_ptr<MockWaylandDisplayHandler> handler_;
+};
+
+TEST_F(WaylandDisplayObserverTest, SendLogicalPositionAndSize) {
+ constexpr gfx::Point kExpectedOrigin(10, 20);
+ constexpr gfx::Size kExpectedSize(800, 600);
+ constexpr gfx::Rect kExpectedBounds(kExpectedOrigin, kExpectedSize);
+ display::Display display(GetPrimaryDisplay().id(), kExpectedBounds);
+ display.set_device_scale_factor(2);
+ display.set_rotation(display::Display::ROTATE_180);
+ display.set_panel_rotation(display::Display::ROTATE_270);
+
+ EXPECT_CALL(*handler_, XdgOutputSendLogicalPosition(kExpectedOrigin))
+ .Times(1);
+ EXPECT_CALL(*handler_, XdgOutputSendLogicalSize(kExpectedSize)).Times(1);
+ handler_->OnDisplayMetricsChanged(display, kAllChanges);
+}
+
+} // namespace
+} // namespace wayland
+} // namespace exo
diff --git a/chromium/components/exo/wayland/wayland_display_util.cc b/chromium/components/exo/wayland/wayland_display_util.cc
new file mode 100644
index 00000000000..a0195d98f9a
--- /dev/null
+++ b/chromium/components/exo/wayland/wayland_display_util.cc
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium 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/exo/wayland/wayland_display_util.h"
+
+#include "base/notreached.h"
+
+namespace exo {
+namespace wayland {
+
+wl_output_transform OutputTransform(display::Display::Rotation rotation) {
+ // Note: |rotation| describes the counter clockwise rotation that a
+ // display's output is currently adjusted for, which is the inverse
+ // of what we need to return.
+ switch (rotation) {
+ case display::Display::ROTATE_0:
+ return WL_OUTPUT_TRANSFORM_NORMAL;
+ case display::Display::ROTATE_90:
+ return WL_OUTPUT_TRANSFORM_270;
+ case display::Display::ROTATE_180:
+ return WL_OUTPUT_TRANSFORM_180;
+ case display::Display::ROTATE_270:
+ return WL_OUTPUT_TRANSFORM_90;
+ }
+ NOTREACHED();
+ return WL_OUTPUT_TRANSFORM_NORMAL;
+}
+
+} // namespace wayland
+} // namespace exo
diff --git a/chromium/components/exo/wayland/wayland_display_util.h b/chromium/components/exo/wayland/wayland_display_util.h
new file mode 100644
index 00000000000..c86d0213384
--- /dev/null
+++ b/chromium/components/exo/wayland/wayland_display_util.h
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_EXO_WAYLAND_WAYLAND_DISPLAY_UTIL_H_
+#define COMPONENTS_EXO_WAYLAND_WAYLAND_DISPLAY_UTIL_H_
+
+#include <wayland-server-protocol-core.h>
+
+#include "ui/display/display.h"
+
+namespace exo {
+namespace wayland {
+
+// Returns the transform that a compositor will apply to a surface to
+// compensate for the rotation of an output device.
+wl_output_transform OutputTransform(display::Display::Rotation rotation);
+
+} // namespace wayland
+} // namespace exo
+
+#endif // COMPONENTS_EXO_WAYLAND_WAYLAND_DISPLAY_UTIL_H_
diff --git a/chromium/components/exo/wayland/wayland_display_util_unittest.cc b/chromium/components/exo/wayland/wayland_display_util_unittest.cc
new file mode 100644
index 00000000000..d2032ec0274
--- /dev/null
+++ b/chromium/components/exo/wayland/wayland_display_util_unittest.cc
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium 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/exo/wayland/wayland_display_util.h"
+
+#include <wayland-server-protocol-core.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/display.h"
+
+namespace exo {
+namespace wayland {
+namespace {
+
+TEST(WaylandDisplayUtilTest, OutputTransformSimple) {
+ EXPECT_EQ(OutputTransform(display::Display::ROTATE_0),
+ WL_OUTPUT_TRANSFORM_NORMAL);
+ EXPECT_EQ(OutputTransform(display::Display::ROTATE_90),
+ WL_OUTPUT_TRANSFORM_270);
+ EXPECT_EQ(OutputTransform(display::Display::ROTATE_180),
+ WL_OUTPUT_TRANSFORM_180);
+ EXPECT_EQ(OutputTransform(display::Display::ROTATE_270),
+ WL_OUTPUT_TRANSFORM_90);
+}
+
+} // namespace
+} // namespace wayland
+} // namespace exo
diff --git a/chromium/components/exo/wayland/wayland_positioner.cc b/chromium/components/exo/wayland/wayland_positioner.cc
index a761ffae4fe..8c28ce446d3 100644
--- a/chromium/components/exo/wayland/wayland_positioner.cc
+++ b/chromium/components/exo/wayland/wayland_positioner.cc
@@ -6,8 +6,7 @@
#include <xdg-shell-unstable-v6-server-protocol.h>
-namespace exo {
-namespace wayland {
+namespace exo::wayland {
namespace {
@@ -56,12 +55,12 @@ DecomposeStableAnchor(uint32_t anchor) {
case XDG_POSITIONER_ANCHOR_TOP_LEFT:
return std::make_pair(WaylandPositioner::Direction::kNegative,
WaylandPositioner::Direction::kNegative);
- case XDG_POSITIONER_ANCHOR_TOP_RIGHT:
- return std::make_pair(WaylandPositioner::Direction::kPositive,
- WaylandPositioner::Direction::kNegative);
case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT:
return std::make_pair(WaylandPositioner::Direction::kNegative,
WaylandPositioner::Direction::kPositive);
+ case XDG_POSITIONER_ANCHOR_TOP_RIGHT:
+ return std::make_pair(WaylandPositioner::Direction::kPositive,
+ WaylandPositioner::Direction::kNegative);
case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT:
return std::make_pair(WaylandPositioner::Direction::kPositive,
WaylandPositioner::Direction::kPositive);
@@ -113,12 +112,12 @@ DecomposeStableGravity(uint32_t gravity) {
case XDG_POSITIONER_GRAVITY_TOP_LEFT:
return std::make_pair(WaylandPositioner::Direction::kNegative,
WaylandPositioner::Direction::kNegative);
- case XDG_POSITIONER_GRAVITY_TOP_RIGHT:
- return std::make_pair(WaylandPositioner::Direction::kPositive,
- WaylandPositioner::Direction::kNegative);
case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT:
return std::make_pair(WaylandPositioner::Direction::kNegative,
WaylandPositioner::Direction::kPositive);
+ case XDG_POSITIONER_GRAVITY_TOP_RIGHT:
+ return std::make_pair(WaylandPositioner::Direction::kPositive,
+ WaylandPositioner::Direction::kNegative);
case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT:
return std::make_pair(WaylandPositioner::Direction::kPositive,
WaylandPositioner::Direction::kPositive);
@@ -217,6 +216,21 @@ Range1D Calculate(const ConstraintAdjustment& adjustments,
return {start, static_cast<int32_t>(start + size)};
}
+// The intermediate adjustment results when computing the best positioning for
+// the popup.
+struct IntermediateAdjustmentResult {
+ // Result statistics for comparing two different placements.
+ struct Stats {
+ // If this is set to false, this result will be chosen iff it is the only
+ // non-constrained option.
+ bool preferred;
+ bool constrained;
+ int32_t visibility;
+ } stats;
+ Range1D position;
+ ConstraintAdjustment adjustment;
+};
+
// Determines which adjustments (subject to them being a subset of the allowed
// adjustments) result in the best range position.
//
@@ -228,24 +242,27 @@ std::pair<Range1D, ConstraintAdjustment> DetermineBestConstraintAdjustment(
int32_t offset,
WaylandPositioner::Direction anchor,
WaylandPositioner::Direction gravity,
- const ConstraintAdjustment& valid_adjustments) {
+ const ConstraintAdjustment& valid_adjustments,
+ bool avoid_occlusion) {
if (work_area.start != 0) {
int32_t shift = -work_area.start;
std::pair<Range1D, ConstraintAdjustment> shifted_result =
DetermineBestConstraintAdjustment(
work_area.GetTranspose(shift), anchor_range.GetTranspose(shift),
- size, offset, anchor, gravity, valid_adjustments);
+ size, offset, anchor, gravity, valid_adjustments, avoid_occlusion);
return {shifted_result.first.GetTranspose(-shift), shifted_result.second};
}
// To determine the position, cycle through the available combinations of
// adjustments and choose the first one that maximizes the amount of the
- // window that is visible on screen.
- Range1D best_position{0, 0};
- ConstraintAdjustment best_adjustments;
- bool best_constrained = true;
- int32_t best_visibility = 0;
-
+ // window that is visible on screen. Preferences are given in accordance to
+ // order when all the stats are equivalent. Therefore, the preference for
+ // adjustment will be flip > slide > resize.
+ IntermediateAdjustmentResult best{{/*preferred=*/false,
+ /*constrained=*/true,
+ /*visibility=*/0},
+ /*position=*/{0, 0},
+ /*adjustment=*/ConstraintAdjustment{}};
for (uint32_t adjustment_bit_field = 0; adjustment_bit_field < 8;
++adjustment_bit_field) {
// When several options tie for visibility, we preference based on the
@@ -259,19 +276,37 @@ std::pair<Range1D, ConstraintAdjustment> DetermineBestConstraintAdjustment(
(adjustment.resize && !valid_adjustments.resize))
continue;
+ // When sliding, it can be possible to occlude the parent menu. Therefore,
+ // this option should not be used if there are better options which have
+ // acceptable placement.
+ bool possible_occlusion = false;
+ if (avoid_occlusion && adjustment.slide)
+ possible_occlusion = true;
+
Range1D position = Calculate(adjustment, work_area.end, anchor_range, size,
offset, anchor, gravity);
bool constrained = position.start < 0 || position.end > work_area.end;
int32_t visibility = std::abs(std::min(position.end, work_area.end) -
std::max(position.start, 0));
- if (visibility > best_visibility || ((!constrained) && best_constrained)) {
- best_position = position;
- best_constrained = constrained;
- best_visibility = visibility;
- best_adjustments = adjustment;
+
+ bool preferred = !possible_occlusion && !constrained;
+ bool is_better = false;
+ if (preferred) {
+ // Always choose a preferred adjustment if the best we have is not
+ // preferred.
+ if (!best.stats.preferred || visibility > best.stats.visibility)
+ is_better = true;
+ } else {
+ if (!constrained && best.stats.constrained)
+ is_better = true;
+ }
+
+ if (is_better) {
+ best = IntermediateAdjustmentResult{
+ {preferred, constrained, visibility}, position, adjustment};
}
}
- return {best_position, best_adjustments};
+ return {best.position, best.adjustment};
}
} // namespace
@@ -319,7 +354,7 @@ WaylandPositioner::Result WaylandPositioner::CalculateBounds(
int32_t offset_x = offset_.x();
int32_t offset_y = offset_.y();
- // Exo overrides the ability to slide in cases when the orthogonal
+ // Exo may prefer some adjustments over others in cases when the orthogonal
// anchor+gravity would mean the slide can occlude |anchor_rect_|, unless it
// already is occluded.
//
@@ -327,28 +362,28 @@ WaylandPositioner::Result WaylandPositioner::CalculateBounds(
// dropdown menus to occlude the menu header. Whilst this may cause some
// popups to avoid sliding where they could, for UX reasons we'd rather that
// than allowing menus to be occluded.
+ //
+ // This is best effort. If it is not possible to position the popup within the
+ // work area, exo might choose to occlude the parent.
bool x_occluded = !(anchor_x == gravity_x && anchor_x != kNeutral);
bool y_occluded = !(anchor_y == gravity_y && anchor_y != kNeutral);
- if (x_occluded && !y_occluded)
- adjustments_y.slide = false;
- if (y_occluded && !x_occluded)
- adjustments_x.slide = false;
+ bool avoid_y_occlusion = x_occluded && !y_occluded;
+ bool avoid_x_occlusion = y_occluded && !x_occluded;
std::pair<Range1D, ConstraintAdjustment> x =
DetermineBestConstraintAdjustment(
{work_area.x(), work_area.right()},
{anchor_rect_.x(), anchor_rect_.right()}, size_.width(), offset_x,
- anchor_x, gravity_x, adjustments_x);
+ anchor_x, gravity_x, adjustments_x, avoid_x_occlusion);
std::pair<Range1D, ConstraintAdjustment> y =
DetermineBestConstraintAdjustment(
{work_area.y(), work_area.bottom()},
{anchor_rect_.y(), anchor_rect_.bottom()}, size_.height(), offset_y,
- anchor_y, gravity_y, adjustments_y);
+ anchor_y, gravity_y, adjustments_y, avoid_y_occlusion);
gfx::Point origin(x.first.start, y.first.start);
gfx::Size size(std::max(1, x.first.end - x.first.start),
std::max(1, y.first.end - y.first.start));
return {origin, size};
}
-} // namespace wayland
-} // namespace exo
+} // namespace exo::wayland
diff --git a/chromium/components/exo/wayland/wayland_positioner_unittest.cc b/chromium/components/exo/wayland/wayland_positioner_unittest.cc
index c7c46c023b1..0787c9626f0 100644
--- a/chromium/components/exo/wayland/wayland_positioner_unittest.cc
+++ b/chromium/components/exo/wayland/wayland_positioner_unittest.cc
@@ -216,6 +216,22 @@ TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRectUnstable) {
gfx::Rect(1, 1, 4, 4));
}
+// Allowing sliding which will occlude the anchor if there are no other
+// positioning options which do not result in a constrained view available.
+TEST_F(WaylandPositionerTest,
+ AllowsSlidingThatOccludesWhenThereAreNoOtherOptionsUnstable) {
+ EXPECT_EQ(
+ TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+ .SetSize(4, 4)
+ .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
+ .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT)
+ // Disable resizing in both axes which will force sliding.
+ .SetAdjustment(~(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X |
+ ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y))
+ .SolveToRect(),
+ gfx::Rect(1, 1, 4, 4));
+}
+
// Tests for the stable protocol.
TEST_F(WaylandPositionerTest, UnconstrainedCases) {
@@ -349,6 +365,21 @@ TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) {
gfx::Rect(1, 1, 4, 4));
}
+// Allowing sliding which will occlude the anchor if there are no other
+// positioning options which do not result in a constrained view available.
+TEST_F(WaylandPositionerTest,
+ AllowsSlidingThatOccludesWhenThereAreNoOtherOptions) {
+ EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
+ .SetSize(4, 4)
+ .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
+ .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT)
+ // Disable resizing in both axes which will force sliding.
+ .SetAdjustment(~(XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y))
+ .SolveToRect(),
+ gfx::Rect(1, 1, 4, 4));
+}
+
// Make sure that the size should never be an empty even if the constraints
// resulted in empty size.
TEST_F(WaylandPositionerTest, ResizableShouldNotBeEmpty) {
diff --git a/chromium/components/exo/wayland/weston_test.cc b/chromium/components/exo/wayland/weston_test.cc
index 464754511ee..d147205d82f 100644
--- a/chromium/components/exo/wayland/weston_test.cc
+++ b/chromium/components/exo/wayland/weston_test.cc
@@ -19,6 +19,7 @@
#include "components/exo/wm_helper.h"
#include "components/exo/xkb_tracker.h"
#include "ui/base/test/ui_controls.h"
+#include "ui/compositor/compositor_animation_observer.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
@@ -322,6 +323,8 @@ void bind_weston_test(wl_client* client,
WestonTest::WestonTest(Server* server)
: data_(std::make_unique<WestonTestState>(server)) {
+ ui::CompositorAnimationObserver::DisableCheckActiveDuration();
+
wl_global_create(server->GetWaylandDisplay(), &weston_test_interface,
kWestonTestVersion, data_.get(), bind_weston_test);
}
diff --git a/chromium/components/exo/wayland/wl_shell.cc b/chromium/components/exo/wayland/wl_shell.cc
index 41883a613be..6010bc4d41d 100644
--- a/chromium/components/exo/wayland/wl_shell.cc
+++ b/chromium/components/exo/wayland/wl_shell.cc
@@ -155,6 +155,8 @@ void shell_get_shell_surface(wl_client* client,
// before they are enabled and can become visible.
shell_surface->SetEnabled(false);
+ shell_surface->SetCapabilities(GetCapabilities(client));
+
shell_surface->set_configure_callback(
base::BindRepeating(&HandleShellSurfaceConfigureCallback,
base::Unretained(shell_surface_resource)));
diff --git a/chromium/components/exo/wayland/xdg_shell.cc b/chromium/components/exo/wayland/xdg_shell.cc
index 39ec305f0ed..278b943d5e8 100644
--- a/chromium/components/exo/wayland/xdg_shell.cc
+++ b/chromium/components/exo/wayland/xdg_shell.cc
@@ -146,21 +146,6 @@ uint32_t HandleXdgSurfaceConfigureCallback(
return serial;
}
-struct WaylandXdgSurface {
- WaylandXdgSurface(std::unique_ptr<XdgShellSurface> shell_surface,
- SerialTracker* const serial_tracker)
- : shell_surface(std::move(shell_surface)),
- serial_tracker(serial_tracker) {}
-
- WaylandXdgSurface(const WaylandXdgSurface&) = delete;
- WaylandXdgSurface& operator=(const WaylandXdgSurface&) = delete;
-
- std::unique_ptr<XdgShellSurface> shell_surface;
-
- // Owned by Server, which always outlives this surface.
- SerialTracker* const serial_tracker;
-};
-
// Wrapper around shell surface that allows us to handle the case where the
// xdg surface resource is destroyed before the toplevel resource.
class WaylandToplevel : public aura::WindowObserver {
@@ -649,9 +634,6 @@ void xdg_surface_get_popup(wl_client* client,
xdg_popup_send_configure(xdg_popup_resource, position.origin.x(),
position.origin.y(), position.size.width(),
position.size.height());
- uint32_t serial = shell_surface_data->serial_tracker->GetNextSerial(
- SerialTracker::EventType::OTHER_EVENT);
- xdg_surface_send_configure(resource, serial);
}
void xdg_surface_set_window_geometry(wl_client* client,
@@ -710,6 +692,8 @@ void xdg_wm_base_get_xdg_surface(wl_client* client,
// before they are enabled and can become visible.
shell_surface->SetEnabled(false);
+ shell_surface->SetCapabilities(GetCapabilities(client));
+
std::unique_ptr<WaylandXdgSurface> wayland_shell_surface =
std::make_unique<WaylandXdgSurface>(std::move(shell_surface),
data->serial_tracker);
@@ -791,6 +775,13 @@ static const struct zxdg_decoration_manager_v1_interface
} // namespace
+WaylandXdgSurface ::WaylandXdgSurface(
+ std::unique_ptr<XdgShellSurface> shell_surface,
+ SerialTracker* const serial_tracker)
+ : shell_surface(std::move(shell_surface)), serial_tracker(serial_tracker) {}
+
+WaylandXdgSurface::~WaylandXdgSurface() = default;
+
void bind_zxdg_decoration_manager(wl_client* client,
void* data,
uint32_t version,
diff --git a/chromium/components/exo/wayland/xdg_shell.h b/chromium/components/exo/wayland/xdg_shell.h
index fb29e261602..bc928cdaaf9 100644
--- a/chromium/components/exo/wayland/xdg_shell.h
+++ b/chromium/components/exo/wayland/xdg_shell.h
@@ -6,6 +6,7 @@
#define COMPONENTS_EXO_WAYLAND_XDG_SHELL_H_
#include <stdint.h>
+#include <memory>
struct wl_client;
struct wl_resource;
@@ -14,6 +15,7 @@ namespace exo {
class Display;
class ShellSurfaceBase;
class ShellSurface;
+class XdgShellSurface;
namespace wayland {
class SerialTracker;
@@ -32,6 +34,21 @@ struct WaylandXdgShell {
SerialTracker* const serial_tracker;
};
+struct WaylandXdgSurface {
+ WaylandXdgSurface(std::unique_ptr<XdgShellSurface> shell_surface,
+ SerialTracker* const serial_tracker);
+
+ ~WaylandXdgSurface();
+
+ WaylandXdgSurface(const WaylandXdgSurface&) = delete;
+ WaylandXdgSurface& operator=(const WaylandXdgSurface&) = delete;
+
+ std::unique_ptr<XdgShellSurface> shell_surface;
+
+ // Owned by Server, which always outlives this surface.
+ SerialTracker* const serial_tracker;
+};
+
void bind_xdg_shell(wl_client* client,
void* data,
uint32_t version,
diff --git a/chromium/components/exo/wayland/zaura_shell.cc b/chromium/components/exo/wayland/zaura_shell.cc
index b3e05d799d5..1ae22aafec6 100644
--- a/chromium/components/exo/wayland/zaura_shell.cc
+++ b/chromium/components/exo/wayland/zaura_shell.cc
@@ -29,6 +29,7 @@
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/server_util.h"
#include "components/exo/wayland/wayland_display_observer.h"
+#include "components/exo/wayland/wayland_display_util.h"
#include "components/exo/wayland/wl_output.h"
#include "components/exo/wm_helper.h"
#include "ui/aura/env.h"
@@ -36,7 +37,7 @@
#include "ui/compositor/layer.h"
#include "ui/display/display_observer.h"
#include "ui/display/manager/display_manager.h"
-#include "ui/display/manager/display_util.h"
+#include "ui/display/manager/display_manager_util.h"
#include "ui/display/screen.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/coordinate_conversion.h"
@@ -704,6 +705,18 @@ void AuraToplevel::SetWindowBounds(int32_t x,
shell_surface_->SetWindowBounds(gfx::Rect(x, y, width, height));
}
+void AuraToplevel::SetRestoreInfo(int32_t restore_session_id,
+ int32_t restore_window_id) {
+ shell_surface_->SetRestoreInfo(restore_session_id, restore_window_id);
+}
+
+void AuraToplevel::SetRestoreInfoWithWindowIdSource(
+ int32_t restore_session_id,
+ const std::string& restore_window_id_source) {
+ shell_surface_->SetRestoreInfoWithWindowIdSource(restore_session_id,
+ restore_window_id_source);
+}
+
void AuraToplevel::OnOriginChange(const gfx::Point& origin) {
zaura_toplevel_send_origin_change(aura_toplevel_resource_, origin.x(),
origin.y());
@@ -722,6 +735,10 @@ void AuraToplevel::SetClientUsesScreenCoordinates() {
&AuraToplevel::OnOriginChange, weak_ptr_factory_.GetWeakPtr()));
}
+void AuraToplevel::SetSystemModal(bool modal) {
+ shell_surface_->SetSystemModal(modal);
+}
+
void AddState(wl_array* states, xdg_toplevel_state state) {
xdg_toplevel_state* value = static_cast<xdg_toplevel_state*>(
wl_array_add(states, sizeof(xdg_toplevel_state)));
@@ -765,90 +782,97 @@ void AuraPopup::SetClientSubmitsSurfacesInPixelCoordinates(bool enable) {
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
-namespace {
-
////////////////////////////////////////////////////////////////////////////////
// aura_output_interface:
-class AuraOutput : public WaylandDisplayObserver {
- public:
- explicit AuraOutput(wl_resource* resource) : resource_(resource) {}
-
- AuraOutput(const AuraOutput&) = delete;
- AuraOutput& operator=(const AuraOutput&) = delete;
-
- // Overridden from WaylandDisplayObserver:
- bool SendDisplayMetrics(const display::Display& display,
- uint32_t changed_metrics) override {
- if (!(changed_metrics &
- (display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
- display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR |
- display::DisplayObserver::DISPLAY_METRIC_ROTATION))) {
- return false;
- }
+AuraOutput::AuraOutput(wl_resource* resource) : resource_(resource) {}
- const WMHelper* wm_helper = WMHelper::GetInstance();
- const display::ManagedDisplayInfo& display_info =
- wm_helper->GetDisplayInfo(display.id());
-
- if (wl_resource_get_version(resource_) >=
- ZAURA_OUTPUT_SCALE_SINCE_VERSION) {
- display::ManagedDisplayMode active_mode;
- bool rv =
- wm_helper->GetActiveModeForDisplayId(display.id(), &active_mode);
- DCHECK(rv);
- const int32_t current_output_scale =
- std::round(display_info.zoom_factor() * 1000.f);
- std::vector<float> zoom_factors =
- display::GetDisplayZoomFactors(active_mode);
-
- // Ensure that the current zoom factor is a part of the list.
- auto it = std::find_if(
- zoom_factors.begin(), zoom_factors.end(),
- [&display_info](float zoom_factor) -> bool {
- return std::abs(display_info.zoom_factor() - zoom_factor) <=
- std::numeric_limits<float>::epsilon();
- });
- if (it == zoom_factors.end())
- zoom_factors.push_back(display_info.zoom_factor());
-
- for (float zoom_factor : zoom_factors) {
- int32_t output_scale = std::round(zoom_factor * 1000.f);
- uint32_t flags = 0;
- if (output_scale == 1000)
- flags |= ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED;
- if (current_output_scale == output_scale)
- flags |= ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT;
-
- // TODO(malaykeshav): This can be removed in the future when client
- // has been updated.
- if (wl_resource_get_version(resource_) < 6)
- output_scale = std::round(1000.f / zoom_factor);
-
- zaura_output_send_scale(resource_, flags, output_scale);
- }
- }
+AuraOutput::~AuraOutput() = default;
- if (wl_resource_get_version(resource_) >=
- ZAURA_OUTPUT_CONNECTION_SINCE_VERSION) {
- zaura_output_send_connection(resource_,
- display.IsInternal()
- ? ZAURA_OUTPUT_CONNECTION_TYPE_INTERNAL
- : ZAURA_OUTPUT_CONNECTION_TYPE_UNKNOWN);
- }
+bool AuraOutput::SendDisplayMetrics(const display::Display& display,
+ uint32_t changed_metrics) {
+ if (!(changed_metrics &
+ (display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
+ display::DisplayObserver::DISPLAY_METRIC_WORK_AREA |
+ display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR |
+ display::DisplayObserver::DISPLAY_METRIC_ROTATION))) {
+ return false;
+ }
- if (wl_resource_get_version(resource_) >=
- ZAURA_OUTPUT_DEVICE_SCALE_FACTOR_SINCE_VERSION) {
- zaura_output_send_device_scale_factor(
- resource_, display_info.device_scale_factor() * 1000);
+ const WMHelper* wm_helper = WMHelper::GetInstance();
+ const display::ManagedDisplayInfo& display_info =
+ wm_helper->GetDisplayInfo(display.id());
+
+ if (wl_resource_get_version(resource_) >= ZAURA_OUTPUT_SCALE_SINCE_VERSION) {
+ display::ManagedDisplayMode active_mode;
+ bool rv = wm_helper->GetActiveModeForDisplayId(display.id(), &active_mode);
+ DCHECK(rv);
+ const int32_t current_output_scale =
+ std::round(display_info.zoom_factor() * 1000.f);
+ std::vector<float> zoom_factors =
+ display::GetDisplayZoomFactors(active_mode);
+
+ // Ensure that the current zoom factor is a part of the list.
+ auto it = std::find_if(
+ zoom_factors.begin(), zoom_factors.end(),
+ [&display_info](float zoom_factor) -> bool {
+ return std::abs(display_info.zoom_factor() - zoom_factor) <=
+ std::numeric_limits<float>::epsilon();
+ });
+ if (it == zoom_factors.end())
+ zoom_factors.push_back(display_info.zoom_factor());
+
+ for (float zoom_factor : zoom_factors) {
+ int32_t output_scale = std::round(zoom_factor * 1000.f);
+ uint32_t flags = 0;
+ if (output_scale == 1000)
+ flags |= ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED;
+ if (current_output_scale == output_scale)
+ flags |= ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT;
+
+ // TODO(malaykeshav): This can be removed in the future when client
+ // has been updated.
+ if (wl_resource_get_version(resource_) < 6)
+ output_scale = std::round(1000.f / zoom_factor);
+
+ zaura_output_send_scale(resource_, flags, output_scale);
}
+ }
- return true;
+ if (wl_resource_get_version(resource_) >=
+ ZAURA_OUTPUT_CONNECTION_SINCE_VERSION) {
+ zaura_output_send_connection(
+ resource_, display.IsInternal() ? ZAURA_OUTPUT_CONNECTION_TYPE_INTERNAL
+ : ZAURA_OUTPUT_CONNECTION_TYPE_UNKNOWN);
}
- private:
- wl_resource* const resource_;
-};
+ if (wl_resource_get_version(resource_) >=
+ ZAURA_OUTPUT_DEVICE_SCALE_FACTOR_SINCE_VERSION) {
+ zaura_output_send_device_scale_factor(
+ resource_, display_info.device_scale_factor() * 1000);
+ }
+
+ if (wl_resource_get_version(resource_) >= ZAURA_OUTPUT_INSETS_SINCE_VERSION)
+ SendInsets(display.bounds().InsetsFrom(display.work_area()));
+
+ if (wl_resource_get_version(resource_) >=
+ ZAURA_OUTPUT_LOGICAL_TRANSFORM_SINCE_VERSION) {
+ SendLogicalTransform(OutputTransform(display.rotation()));
+ }
+
+ return true;
+}
+
+void AuraOutput::SendInsets(const gfx::Insets& insets) {
+ zaura_output_send_insets(resource_, insets.top(), insets.left(),
+ insets.bottom(), insets.right());
+}
+
+void AuraOutput::SendLogicalTransform(int32_t transform) {
+ zaura_output_send_logical_transform(resource_, transform);
+}
+
+namespace {
////////////////////////////////////////////////////////////////////////////////
// aura_shell_interface:
@@ -1054,11 +1078,41 @@ void aura_toplevel_set_window_bounds(wl_client* client,
GetUserDataAs<AuraToplevel>(resource)->SetWindowBounds(x, y, width, height);
}
+void aura_toplevel_set_restore_info(wl_client* client,
+ wl_resource* resource,
+ int32_t restore_session_id,
+ int32_t restore_window_id) {
+ GetUserDataAs<AuraToplevel>(resource)->SetRestoreInfo(restore_session_id,
+ restore_window_id);
+}
+
+void aura_toplevel_set_system_modal(wl_client* client, wl_resource* resource) {
+ GetUserDataAs<AuraToplevel>(resource)->SetSystemModal(true);
+}
+
+void aura_toplevel_unset_system_modal(wl_client* client,
+ wl_resource* resource) {
+ GetUserDataAs<AuraToplevel>(resource)->SetSystemModal(false);
+}
+
+void aura_toplevel_set_restore_info_with_window_id_source(
+ wl_client* client,
+ wl_resource* resource,
+ int32_t restore_session_id,
+ const char* restore_window_id_source) {
+ GetUserDataAs<AuraToplevel>(resource)->SetRestoreInfoWithWindowIdSource(
+ restore_session_id, restore_window_id_source);
+}
+
const struct zaura_toplevel_interface aura_toplevel_implementation = {
aura_toplevel_set_orientation_lock,
aura_toplevel_surface_submission_in_pixel_coordinates,
aura_toplevel_set_client_supports_window_bounds,
aura_toplevel_set_window_bounds,
+ aura_toplevel_set_restore_info,
+ aura_toplevel_set_system_modal,
+ aura_toplevel_unset_system_modal,
+ aura_toplevel_set_restore_info_with_window_id_source,
};
void aura_popup_surface_submission_in_pixel_coordinates(wl_client* client,
diff --git a/chromium/components/exo/wayland/zaura_shell.h b/chromium/components/exo/wayland/zaura_shell.h
index 88c681ab5ed..dd49ec25617 100644
--- a/chromium/components/exo/wayland/zaura_shell.h
+++ b/chromium/components/exo/wayland/zaura_shell.h
@@ -10,6 +10,8 @@
#include "chromeos/ui/base/window_state_type.h"
#include "components/exo/surface.h"
#include "components/exo/surface_observer.h"
+#include "components/exo/wayland/wayland_display_observer.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/wm/public/activation_change_observer.h"
@@ -24,7 +26,7 @@ class ShellSurfaceBase;
namespace wayland {
class SerialTracker;
-constexpr uint32_t kZAuraShellVersion = 29;
+constexpr uint32_t kZAuraShellVersion = 34;
// Adds bindings to the Aura Shell. Normally this implies Ash on ChromeOS
// builds. On non-ChromeOS builds the protocol provides access to Aura windowing
@@ -117,6 +119,11 @@ class AuraToplevel {
void SetClientSubmitsSurfacesInPixelCoordinates(bool enable);
void SetClientUsesScreenCoordinates();
void SetWindowBounds(int32_t x, int32_t y, int32_t width, int32_t height);
+ void SetRestoreInfo(int32_t restore_session_id, int32_t restore_window_id);
+ void SetRestoreInfoWithWindowIdSource(
+ int32_t restore_session_id,
+ const std::string& restore_window_id_source);
+ void SetSystemModal(bool modal);
void OnConfigure(const gfx::Rect& bounds,
chromeos::WindowStateType state_type,
@@ -146,6 +153,27 @@ class AuraPopup {
ShellSurfaceBase* shell_surface_;
};
+class AuraOutput : public WaylandDisplayObserver {
+ public:
+ explicit AuraOutput(wl_resource* resource);
+
+ AuraOutput(const AuraOutput&) = delete;
+ AuraOutput& operator=(const AuraOutput&) = delete;
+
+ ~AuraOutput() override;
+
+ // Overridden from WaylandDisplayObserver:
+ bool SendDisplayMetrics(const display::Display& display,
+ uint32_t changed_metrics) override;
+
+ protected:
+ virtual void SendInsets(const gfx::Insets& insets);
+ virtual void SendLogicalTransform(int32_t transform);
+
+ private:
+ wl_resource* const resource_;
+};
+
} // namespace wayland
} // namespace exo
diff --git a/chromium/components/exo/wayland/zaura_shell_unittest.cc b/chromium/components/exo/wayland/zaura_shell_unittest.cc
index 24e16cac028..b055ae536fe 100644
--- a/chromium/components/exo/wayland/zaura_shell_unittest.cc
+++ b/chromium/components/exo/wayland/zaura_shell_unittest.cc
@@ -6,6 +6,7 @@
#include <aura-shell-server-protocol.h>
+#include <sys/socket.h>
#include <memory>
#include "ash/session/session_controller_impl.h"
@@ -15,6 +16,8 @@
#include "base/time/time.h"
#include "components/exo/buffer.h"
#include "components/exo/test/exo_test_base.h"
+#include "components/exo/wayland/scoped_wl.h"
+#include "components/exo/wayland/wayland_display_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window_occlusion_tracker.h"
@@ -117,6 +120,8 @@ class MockSurfaceDelegate : public SurfaceDelegate {
(override));
MOCK_METHOD(void, Pin, (bool trusted), (override));
MOCK_METHOD(void, Unpin, (), (override));
+ MOCK_METHOD(void, SetSystemModal, (bool modal), (override));
+ MOCK_METHOD(Capabilities*, GetCapabilities, (), (override));
};
} // namespace
@@ -401,5 +406,101 @@ TEST_F(ZAuraSurfaceTest, CanSetFullscreenModeToImmersive) {
aura_surface().SetFullscreenMode(ZAURA_SURFACE_FULLSCREEN_MODE_IMMERSIVE);
}
+class MockAuraOutput : public AuraOutput {
+ public:
+ using AuraOutput::AuraOutput;
+
+ MOCK_METHOD(void, SendInsets, (const gfx::Insets&), (override));
+ MOCK_METHOD(void, SendLogicalTransform, (int32_t), (override));
+};
+
+class ZAuraOutputTest : public test::ExoTestBase {
+ protected:
+ ZAuraOutputTest() = default;
+ ZAuraOutputTest(const ZAuraOutputTest&) = delete;
+ ZAuraOutputTest& operator=(const ZAuraOutputTest&) = delete;
+ // test::ExxoTestBase:
+ ~ZAuraOutputTest() override = default;
+
+ void SetUp() override {
+ test::ExoTestBase::SetUp();
+
+ int fds[2];
+ ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds), 0);
+ wayland_display_.reset(wl_display_create());
+ client_ = wl_client_create(wayland_display_.get(), fds[0]);
+ }
+
+ std::unique_ptr<MockAuraOutput> CreateAuraOutput(int version) {
+ return std::make_unique<::testing::NiceMock<MockAuraOutput>>(
+ wl_resource_create(client_, &zaura_output_interface, version, 0));
+ }
+
+ std::unique_ptr<wl_display, WlDisplayDeleter> wayland_display_;
+ wl_client* client_ = nullptr;
+};
+
+TEST_F(ZAuraOutputTest, SendInsets) {
+ auto mock_aura_output = CreateAuraOutput(ZAURA_OUTPUT_INSETS_SINCE_VERSION);
+
+ UpdateDisplay("800x600");
+ display::Display display =
+ display_manager()->GetDisplayForId(display_manager()->first_display_id());
+ const gfx::Rect initial_bounds{800, 600};
+ EXPECT_EQ(display.bounds(), initial_bounds);
+ const gfx::Rect new_work_area{10, 20, 500, 400};
+ EXPECT_NE(display.work_area(), new_work_area);
+ display.set_work_area(new_work_area);
+
+ const gfx::Insets expected_insets = initial_bounds.InsetsFrom(new_work_area);
+ EXPECT_CALL(*mock_aura_output, SendInsets(expected_insets)).Times(1);
+ mock_aura_output->SendDisplayMetrics(
+ display, display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
+}
+
+TEST_F(ZAuraOutputTest, SendLogicalTransform) {
+ auto mock_aura_output =
+ CreateAuraOutput(ZAURA_OUTPUT_LOGICAL_TRANSFORM_SINCE_VERSION);
+
+ UpdateDisplay("800x600");
+ display::Display display =
+ display_manager()->GetDisplayForId(display_manager()->first_display_id());
+
+ // Make sure the expected calls happen in order.
+ ::testing::InSequence seq;
+
+ EXPECT_EQ(display.rotation(), display::Display::ROTATE_0);
+ EXPECT_EQ(display.panel_rotation(), display::Display::ROTATE_0);
+ EXPECT_CALL(*mock_aura_output,
+ SendLogicalTransform(OutputTransform(display.rotation())))
+ .Times(1);
+ mock_aura_output->SendDisplayMetrics(
+ display, display::DisplayObserver::DISPLAY_METRIC_ROTATION);
+
+ display.set_rotation(display::Display::ROTATE_270);
+ display.set_panel_rotation(display::Display::ROTATE_180);
+ EXPECT_CALL(*mock_aura_output,
+ SendLogicalTransform(OutputTransform(display.rotation())))
+ .Times(1);
+ mock_aura_output->SendDisplayMetrics(
+ display, display::DisplayObserver::DISPLAY_METRIC_ROTATION);
+
+ display.set_rotation(display::Display::ROTATE_90);
+ display.set_panel_rotation(display::Display::ROTATE_180);
+ EXPECT_CALL(*mock_aura_output,
+ SendLogicalTransform(OutputTransform(display.rotation())))
+ .Times(1);
+ mock_aura_output->SendDisplayMetrics(
+ display, display::DisplayObserver::DISPLAY_METRIC_ROTATION);
+
+ display.set_rotation(display::Display::ROTATE_270);
+ display.set_panel_rotation(display::Display::ROTATE_270);
+ EXPECT_CALL(*mock_aura_output,
+ SendLogicalTransform(OutputTransform(display.rotation())))
+ .Times(1);
+ mock_aura_output->SendDisplayMetrics(
+ display, display::DisplayObserver::DISPLAY_METRIC_ROTATION);
+}
+
} // namespace wayland
} // namespace exo
diff --git a/chromium/components/exo/wayland/zcr_color_manager.cc b/chromium/components/exo/wayland/zcr_color_manager.cc
index cc3d7a908e8..b9732ea8f38 100644
--- a/chromium/components/exo/wayland/zcr_color_manager.cc
+++ b/chromium/components/exo/wayland/zcr_color_manager.cc
@@ -16,13 +16,20 @@
#include "components/exo/surface_observer.h"
#include "components/exo/wayland/server.h"
#include "components/exo/wayland/server_util.h"
+#include "components/exo/wayland/wayland_display_observer.h"
+#include "components/exo/wayland/wayland_display_output.h"
#include "components/exo/wm_helper_chromeos.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/third_party/skcms/skcms.h"
+#include "ui/base/wayland/color_manager_util.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/screen.h"
+#include "ui/display/types/display_constants.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/display_color_spaces.h"
#include "ui/gfx/geometry/triangle_f.h"
+#include "wayland-server-protocol.h"
namespace exo {
namespace wayland {
@@ -30,60 +37,46 @@ namespace wayland {
namespace {
#define PARAM_TO_FLOAT(x) (x / 10000.f)
+#define FLOAT_TO_PARAM(x) (x * 10000)
constexpr auto kDefaultColorSpace = gfx::ColorSpace::CreateSRGB();
-constexpr auto kChromaticityMap =
- base::MakeFixedFlatMap<zcr_color_manager_v1_chromaticity_names,
- gfx::ColorSpace::PrimaryID>(
- {{ZCR_COLOR_MANAGER_V1_CHROMATICITY_NAMES_BT601_525_LINE,
- gfx::ColorSpace::PrimaryID::SMPTE170M},
- {ZCR_COLOR_MANAGER_V1_CHROMATICITY_NAMES_BT601_625_LINE,
- gfx::ColorSpace::PrimaryID::BT470BG},
- {ZCR_COLOR_MANAGER_V1_CHROMATICITY_NAMES_SMPTE170M,
- gfx::ColorSpace::PrimaryID::SMPTE170M},
- {ZCR_COLOR_MANAGER_V1_CHROMATICITY_NAMES_BT709,
- gfx::ColorSpace::PrimaryID::BT709},
- {ZCR_COLOR_MANAGER_V1_CHROMATICITY_NAMES_BT2020,
- gfx::ColorSpace::PrimaryID::BT2020},
- {ZCR_COLOR_MANAGER_V1_CHROMATICITY_NAMES_SRGB,
- gfx::ColorSpace::PrimaryID::BT709},
- {ZCR_COLOR_MANAGER_V1_CHROMATICITY_NAMES_DISPLAYP3,
- gfx::ColorSpace::PrimaryID::P3},
- {ZCR_COLOR_MANAGER_V1_CHROMATICITY_NAMES_ADOBERGB,
- gfx::ColorSpace::PrimaryID::ADOBE_RGB}});
-
-constexpr auto kEotfMap =
- base::MakeFixedFlatMap<zcr_color_manager_v1_eotf_names,
- gfx::ColorSpace::TransferID>({
- {ZCR_COLOR_MANAGER_V1_EOTF_NAMES_LINEAR,
- gfx::ColorSpace::TransferID::LINEAR},
- {ZCR_COLOR_MANAGER_V1_EOTF_NAMES_SRGB,
- gfx::ColorSpace::TransferID::BT709},
- {ZCR_COLOR_MANAGER_V1_EOTF_NAMES_BT2087,
- gfx::ColorSpace::TransferID::GAMMA24},
- {ZCR_COLOR_MANAGER_V1_EOTF_NAMES_ADOBERGB,
- // This is ever so slightly inaccurate. The number ought to be
- // 2.19921875f, not 2.2
- gfx::ColorSpace::TransferID::GAMMA22},
- {ZCR_COLOR_MANAGER_V1_EOTF_NAMES_PQ, gfx::ColorSpace::TransferID::PQ},
- });
-
// Wrapper around a |gfx::ColorSpace| that tracks additional data useful to the
// protocol. These live as wayland resource data.
class ColorManagerColorSpace {
public:
explicit ColorManagerColorSpace(gfx::ColorSpace color_space)
- : color_space(color_space) {}
+ : color_space(color_space),
+ eotf(ui::wayland::ToColorManagerEOTF(color_space.GetTransferID())),
+ primaries(color_space.GetColorSpacePrimaries()) {}
+
+ ColorManagerColorSpace(gfx::ColorSpace color_space,
+ zcr_color_manager_v1_eotf_names eotf,
+ SkColorSpacePrimaries primaries)
+ : color_space(color_space), eotf(eotf), primaries(primaries) {}
+
virtual ~ColorManagerColorSpace() = default;
const gfx::ColorSpace color_space;
-
- virtual void SendColorSpaceInfo(wl_resource* color_space_resource) {
- wl_resource_post_error(color_space_resource,
- ZCR_COLOR_SPACE_V1_ERROR_NO_INFORMATION,
- "No information available for color space");
+ const zcr_color_manager_v1_eotf_names eotf;
+ const SkColorSpacePrimaries primaries;
+
+ void SendColorSpaceInfo(wl_resource* color_space_resource) {
+ SendCustomColorSpaceInfo(color_space_resource);
+ zcr_color_space_v1_send_params(
+ color_space_resource, eotf,
+ static_cast<int>(FLOAT_TO_PARAM(primaries.fRX)),
+ static_cast<int>(FLOAT_TO_PARAM(primaries.fRY)),
+ static_cast<int>(FLOAT_TO_PARAM(primaries.fGX)),
+ static_cast<int>(FLOAT_TO_PARAM(primaries.fGY)),
+ static_cast<int>(FLOAT_TO_PARAM(primaries.fBX)),
+ static_cast<int>(FLOAT_TO_PARAM(primaries.fBY)),
+ static_cast<int>(FLOAT_TO_PARAM(primaries.fWX)),
+ static_cast<int>(FLOAT_TO_PARAM(primaries.fWY)));
+ zcr_color_space_v1_send_done(color_space_resource);
}
+
+ virtual void SendCustomColorSpaceInfo(wl_resource* color_space_resource) {}
};
class NameBasedColorSpace final : public ColorManagerColorSpace {
@@ -93,16 +86,17 @@ class NameBasedColorSpace final : public ColorManagerColorSpace {
zcr_color_manager_v1_chromaticity_names chromaticity,
zcr_color_manager_v1_eotf_names eotf,
zcr_color_manager_v1_whitepoint_names whitepoint)
- : ColorManagerColorSpace(color_space),
- chromaticity(chromaticity),
- eotf(eotf),
+ : ColorManagerColorSpace(color_space,
+ eotf,
+ color_space.GetColorSpacePrimaries()),
+ chromaticity(ui::wayland::ToColorManagerChromaticity(
+ color_space.GetPrimaryID())),
whitepoint(whitepoint) {}
const zcr_color_manager_v1_chromaticity_names chromaticity;
- const zcr_color_manager_v1_eotf_names eotf;
const zcr_color_manager_v1_whitepoint_names whitepoint;
- void SendColorSpaceInfo(wl_resource* color_space_resource) override {
+ void SendCustomColorSpaceInfo(wl_resource* color_space_resource) override {
zcr_color_space_v1_send_names(color_space_resource, eotf, chromaticity,
whitepoint);
}
@@ -192,17 +186,41 @@ void color_management_output_get_color_space(
struct wl_client* client,
struct wl_resource* color_management_output_resource,
uint32_t id) {
+ auto* display_handler =
+ GetUserDataAs<WaylandDisplayHandler>(color_management_output_resource);
+ if (!display_handler)
+ return;
+
+ display::Display display;
+ bool exists = display::Screen::GetScreen()->GetDisplayWithDisplayId(
+ display_handler->id(), &display);
+ if (!exists) {
+ // WaylandDisplayHandler is created asynchronously, and the
+ // display can be deleted before created. This usually won't happen
+ // in real environment, but can happen in test environment.
+ return;
+ }
+ // TODO(mrfemi): replace with actual snapshot color space.
+ auto snapshot_color_space = gfx::ColorSpace::CreateSRGB();
+
+ // create new zcr color space for the current color space of the output
+ auto color_space =
+ std::make_unique<ColorManagerColorSpace>(snapshot_color_space);
+
wl_resource* color_space_resource =
wl_resource_create(client, &zcr_color_space_v1_interface, 1, id);
- wl_resource_set_implementation(color_space_resource,
- &color_space_v1_implementation,
- /*data=*/nullptr, /*destroy=*/nullptr);
+ SetImplementation(color_space_resource, &color_space_v1_implementation,
+ std::move(color_space));
}
void color_management_output_destroy(
struct wl_client* client,
struct wl_resource* color_management_output_resource) {
+ auto* display_handler =
+ GetUserDataAs<WaylandDisplayHandler>(color_management_output_resource);
+ if (display_handler)
+ display_handler->UnsetColorManagementOutputResource();
wl_resource_destroy(color_management_output_resource);
}
@@ -315,8 +333,8 @@ void color_manager_create_color_space_from_names(
uint32_t error_flags = 0;
auto chromaticity_id = gfx::ColorSpace::PrimaryID::INVALID;
- const auto* maybe_primary = kChromaticityMap.find(chromaticity);
- if (maybe_primary != std::end(kChromaticityMap)) {
+ const auto* maybe_primary = ui::wayland::kChromaticityMap.find(chromaticity);
+ if (maybe_primary != std::end(ui::wayland::kChromaticityMap)) {
chromaticity_id = maybe_primary->second;
} else {
DLOG(ERROR) << "Unable to find named chromaticity for id=" << chromaticity;
@@ -324,8 +342,8 @@ void color_manager_create_color_space_from_names(
}
auto eotf_id = gfx::ColorSpace::TransferID::INVALID;
- const auto* maybe_eotf = kEotfMap.find(eotf);
- if (maybe_eotf != std::end(kEotfMap)) {
+ const auto* maybe_eotf = ui::wayland::kEotfMap.find(eotf);
+ if (maybe_eotf != std::end(ui::wayland::kEotfMap)) {
eotf_id = maybe_eotf->second;
} else {
DLOG(ERROR) << "Unable to find named eotf for id=" << eotf;
@@ -382,8 +400,8 @@ void color_manager_create_color_space_from_params(
}
auto eotf_id = gfx::ColorSpace::TransferID::INVALID;
- const auto* maybe_eotf = kEotfMap.find(eotf);
- if (maybe_eotf != std::end(kEotfMap)) {
+ const auto* maybe_eotf = ui::wayland::kEotfMap.find(eotf);
+ if (maybe_eotf != std::end(ui::wayland::kEotfMap)) {
eotf_id = maybe_eotf->second;
} else {
DLOG(ERROR) << "Unable to find named transfer function for id=" << eotf;
@@ -419,9 +437,14 @@ void color_manager_get_color_management_output(
wl_resource* color_management_output_resource = wl_resource_create(
client, &zcr_color_management_output_v1_interface, 1, id);
+ auto* display_handler = GetUserDataAs<WaylandDisplayHandler>(output);
+ if (!display_handler)
+ return;
+
wl_resource_set_implementation(color_management_output_resource,
&color_management_output_v1_implementation,
- /*data=*/nullptr, /*destroy=*/nullptr);
+ display_handler, nullptr);
+ display_handler->OnGetColorManagementOutput(color_management_output_resource);
}
void color_manager_get_color_management_surface(
diff --git a/chromium/components/exo/wayland/zcr_gaming_input.cc b/chromium/components/exo/wayland/zcr_gaming_input.cc
index 00996ece0d6..57e002c3b73 100644
--- a/chromium/components/exo/wayland/zcr_gaming_input.cc
+++ b/chromium/components/exo/wayland/zcr_gaming_input.cc
@@ -195,6 +195,25 @@ class WaylandGamepadDelegate : public GamepadDelegate {
gamepad_vibrator_resource);
}
+ if (wl_resource_get_version(gamepad_resource_) >=
+ ZCR_GAMEPAD_V2_SUPPORTED_KEY_BITS_SINCE_VERSION) {
+ // Sending key_bits.
+ wl_array wl_key_bits;
+ wl_array_init(&wl_key_bits);
+ std::vector<uint64_t> key_bits =
+ ui::OzonePlatform::GetInstance()
+ ->GetInputController()
+ ->GetGamepadKeyBits(gamepad->device.id);
+ size_t key_bits_len = key_bits.size() * sizeof(uint64_t);
+ uint64_t* wl_key_bits_ptr =
+ static_cast<uint64_t*>(wl_array_add(&wl_key_bits, key_bits_len));
+ if (wl_key_bits_ptr) {
+ memcpy(wl_key_bits_ptr, key_bits.data(), key_bits_len);
+ zcr_gamepad_v2_send_supported_key_bits(gamepad_resource_, &wl_key_bits);
+ }
+ wl_array_release(&wl_key_bits);
+ }
+
zcr_gamepad_v2_send_activated(gamepad_resource_);
}
diff --git a/chromium/components/exo/wayland/zcr_keyboard_configuration.cc b/chromium/components/exo/wayland/zcr_keyboard_configuration.cc
index 9fb56f99b2b..bdb76d60296 100644
--- a/chromium/components/exo/wayland/zcr_keyboard_configuration.cc
+++ b/chromium/components/exo/wayland/zcr_keyboard_configuration.cc
@@ -5,6 +5,7 @@
#include "components/exo/wayland/zcr_keyboard_configuration.h"
#include <keyboard-configuration-unstable-v1-server-protocol.h>
+#include <linux/input.h>
#include <wayland-server-core.h>
#include <wayland-server-protocol-core.h>
@@ -15,6 +16,11 @@
#include "components/exo/keyboard_device_configuration_delegate.h"
#include "components/exo/keyboard_observer.h"
#include "components/exo/wayland/server_util.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/input_device_event_observer.h"
+#include "ui/events/ozone/evdev/event_device_util.h"
+#include "ui/ozone/public/input_controller.h"
+#include "ui/ozone/public/ozone_platform.h"
namespace exo {
namespace wayland {
@@ -27,7 +33,8 @@ namespace {
class WaylandKeyboardDeviceConfigurationDelegate
: public KeyboardDeviceConfigurationDelegate,
public KeyboardObserver,
- public ash::ImeControllerImpl::Observer {
+ public ash::ImeControllerImpl::Observer,
+ public ui::InputDeviceEventObserver {
public:
WaylandKeyboardDeviceConfigurationDelegate(wl_resource* resource,
Keyboard* keyboard)
@@ -37,6 +44,8 @@ class WaylandKeyboardDeviceConfigurationDelegate
ash::ImeControllerImpl* ime_controller =
ash::Shell::Get()->ime_controller();
ime_controller->AddObserver(this);
+ ui::DeviceDataManager::GetInstance()->AddObserver(this);
+ ProcessKeyBitsUpdate();
OnKeyboardLayoutNameChanged(ime_controller->keyboard_layout_name());
}
WaylandKeyboardDeviceConfigurationDelegate(
@@ -45,6 +54,7 @@ class WaylandKeyboardDeviceConfigurationDelegate
const WaylandKeyboardDeviceConfigurationDelegate&) = delete;
~WaylandKeyboardDeviceConfigurationDelegate() override {
+ ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
ash::Shell::Get()->ime_controller()->RemoveObserver(this);
if (keyboard_) {
keyboard_->SetDeviceConfigurationDelegate(nullptr);
@@ -74,7 +84,51 @@ class WaylandKeyboardDeviceConfigurationDelegate
resource_, layout_name.c_str());
}
+ // Overridden from ui::InputDeviceEventObserver:
+ void OnInputDeviceConfigurationChanged(uint8_t input_device_types) override {
+ if (!(input_device_types & ui::InputDeviceEventObserver::kKeyboard)) {
+ return;
+ }
+ ProcessKeyBitsUpdate();
+ }
+
private:
+ // Notify key bits update.
+ void ProcessKeyBitsUpdate() {
+ if (wl_resource_get_version(resource_) <
+ ZCR_KEYBOARD_DEVICE_CONFIGURATION_V1_SUPPORTED_KEY_BITS_SINCE_VERSION) {
+ return;
+ }
+
+ // Preparing wayland keybits.
+ wl_array wl_key_bits;
+ wl_array_init(&wl_key_bits);
+ size_t key_bits_len = EVDEV_BITS_TO_INT64(KEY_CNT) * sizeof(uint64_t);
+ uint64_t* wl_key_bits_ptr =
+ static_cast<uint64_t*>(wl_array_add(&wl_key_bits, key_bits_len));
+ if (!wl_key_bits_ptr) {
+ wl_array_release(&wl_key_bits);
+ return;
+ }
+ memset(wl_key_bits_ptr, 0, key_bits_len);
+
+ ui::InputController* input_controller =
+ ui::OzonePlatform::GetInstance()->GetInputController();
+ // Combine supported key bits from all keyboard into single key bits.
+ for (const ui::InputDevice& device :
+ ui::DeviceDataManager::GetInstance()->GetKeyboardDevices()) {
+ const std::vector<uint64_t>& key_bits =
+ input_controller->GetKeyboardKeyBits(device.id);
+ for (size_t i = 0; i < key_bits.size(); i++) {
+ wl_key_bits_ptr[i] |= key_bits[i];
+ }
+ }
+
+ zcr_keyboard_device_configuration_v1_send_supported_key_bits(resource_,
+ &wl_key_bits);
+ wl_array_release(&wl_key_bits);
+ }
+
wl_resource* resource_;
Keyboard* keyboard_;
};
diff --git a/chromium/components/exo/wayland/zcr_remote_shell.cc b/chromium/components/exo/wayland/zcr_remote_shell.cc
index fa978297984..c9ed8f3b466 100644
--- a/chromium/components/exo/wayland/zcr_remote_shell.cc
+++ b/chromium/components/exo/wayland/zcr_remote_shell.cc
@@ -182,6 +182,8 @@ void remote_shell_get_remote_surface(wl_client* client,
if (wl_resource_get_version(remote_surface_resource) < 18)
shell_surface->set_server_reparent_window(true);
+ shell_surface->SetCapabilities(GetCapabilities(client));
+
shell_surface->set_delegate(
shell->CreateShellSurfaceDelegate(remote_surface_resource));
shell_surface->set_close_callback(
diff --git a/chromium/components/exo/wayland/zcr_remote_shell_v2.cc b/chromium/components/exo/wayland/zcr_remote_shell_v2.cc
index 132e9d3ca12..c481157bf8b 100644
--- a/chromium/components/exo/wayland/zcr_remote_shell_v2.cc
+++ b/chromium/components/exo/wayland/zcr_remote_shell_v2.cc
@@ -163,6 +163,8 @@ void remote_shell_get_remote_surface_v2(wl_client* client,
wl_resource_create(client, &zcr_remote_surface_v2_interface,
wl_resource_get_version(resource), id);
+ shell_surface->SetCapabilities(GetCapabilities(client));
+
shell_surface->set_delegate(
shell->CreateShellSurfaceDelegate(remote_surface_resource));
shell_surface->set_close_callback(
diff --git a/chromium/components/exo/wayland/zwp_fullscreen_shell.cc b/chromium/components/exo/wayland/zwp_fullscreen_shell.cc
deleted file mode 100644
index 2e73a9731c5..00000000000
--- a/chromium/components/exo/wayland/zwp_fullscreen_shell.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2018 The Chromium 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/exo/wayland/zwp_fullscreen_shell.h"
-
-#include <fullscreen-shell-unstable-v1-server-protocol.h>
-#include <wayland-server-core.h>
-#include <wayland-server-protocol-core.h>
-
-#include "base/bind.h"
-#include "components/exo/fullscreen_shell_surface.h"
-#include "components/exo/surface.h"
-#include "components/exo/wayland/server_util.h"
-
-namespace exo {
-namespace wayland {
-
-namespace {
-
-void fullscreen_shell_release(wl_client* client, wl_resource* resource) {
- wl_resource_destroy(resource);
-}
-
-void fullscreen_shell_present_surface(wl_client* client,
- wl_resource* resource,
- wl_resource* surface,
- uint32_t method,
- wl_resource* output) {
- FullscreenShellSurface* fullscreen_shell_surface =
- GetUserDataAs<FullscreenShellSurface>(resource);
- fullscreen_shell_surface->SetSurface(surface ? GetUserDataAs<Surface>(surface)
- : nullptr);
-}
-
-void fullscreen_shell_present_surface_for_mode(wl_client* client,
- wl_resource* resource,
- wl_resource* surface,
- wl_resource* output,
- int32_t framerate,
- uint32_t feedback) {
- NOTIMPLEMENTED();
-}
-
-const struct zwp_fullscreen_shell_v1_interface fullscreen_shell_implementation =
- {fullscreen_shell_release, fullscreen_shell_present_surface,
- fullscreen_shell_present_surface_for_mode};
-
-} // namespace
-
-void bind_fullscreen_shell(wl_client* client,
- void* data,
- uint32_t version,
- uint32_t id) {
- std::unique_ptr<FullscreenShellSurface> fullscreen_shell_surface =
- std::make_unique<FullscreenShellSurface>();
- fullscreen_shell_surface->SetEnabled(true);
- wl_resource* resource =
- wl_resource_create(client, &zwp_fullscreen_shell_v1_interface, 1, id);
- fullscreen_shell_surface->set_surface_destroyed_callback(
- base::BindOnce(&wl_resource_destroy, base::Unretained(resource)));
- SetImplementation(resource, &fullscreen_shell_implementation,
- std::move(fullscreen_shell_surface));
-}
-
-} // namespace wayland
-} // namespace exo
diff --git a/chromium/components/exo/wayland/zwp_fullscreen_shell.h b/chromium/components/exo/wayland/zwp_fullscreen_shell.h
deleted file mode 100644
index 48c18c1ea01..00000000000
--- a/chromium/components/exo/wayland/zwp_fullscreen_shell.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2018 The Chromium 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 COMPONENTS_EXO_WAYLAND_ZWP_FULLSCREEN_SHELL_H_
-#define COMPONENTS_EXO_WAYLAND_ZWP_FULLSCREEN_SHELL_H_
-
-#include <stdint.h>
-
-struct wl_client;
-
-namespace exo {
-namespace wayland {
-
-void bind_fullscreen_shell(wl_client* client,
- void* data,
- uint32_t version,
- uint32_t id);
-
-} // namespace wayland
-} // namespace exo
-
-#endif // COMPONENTS_EXO_WAYLAND_ZWP_FULLSCREEN_SHELL_H_
diff --git a/chromium/components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.cc b/chromium/components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.cc
new file mode 100644
index 00000000000..ed198557e4f
--- /dev/null
+++ b/chromium/components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.cc
@@ -0,0 +1,101 @@
+// Copyright 2022 The Chromium 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/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.h"
+
+#include <keyboard-shortcuts-inhibit-unstable-v1-server-protocol.h>
+
+#include "components/exo/surface.h"
+#include "components/exo/surface_observer.h"
+#include "components/exo/wayland/server_util.h"
+
+namespace exo::wayland {
+namespace {
+
+// Tracks the keyboard-shortcut-inhibitor setting, and notifies corresponding
+// Surface on unset.
+class KeyboardShortcutsInhibitor : public SurfaceObserver {
+ public:
+ explicit KeyboardShortcutsInhibitor(Surface* surface) : surface_(surface) {
+ surface->SetKeyboardShortcutsInhibited(true);
+ surface_->AddSurfaceObserver(this);
+ }
+
+ ~KeyboardShortcutsInhibitor() override {
+ if (surface_) {
+ surface_->RemoveSurfaceObserver(this);
+ surface_->SetKeyboardShortcutsInhibited(false);
+ }
+ }
+
+ void OnSurfaceDestroying(Surface* surface) override {
+ DCHECK_EQ(surface, surface_);
+ surface->RemoveSurfaceObserver(this);
+ surface_ = nullptr;
+ }
+
+ private:
+ Surface* surface_;
+};
+
+void keyboard_shortcuts_inhibitor_destroy(wl_client* client,
+ wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+constexpr struct zwp_keyboard_shortcuts_inhibitor_v1_interface
+ keyboard_shortcuts_inhibitor_implementation = {
+ keyboard_shortcuts_inhibitor_destroy,
+};
+
+void keyboard_shortcuts_inhibit_manager_destroy(wl_client* client,
+ wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+void keyboard_shortcuts_inhibit_manager_inhibit_shortcuts(
+ wl_client* client,
+ wl_resource* resource,
+ uint32_t id,
+ wl_resource* surface_resource,
+ wl_resource* seat) {
+ Surface* surface = GetUserDataAs<Surface>(surface_resource);
+ if (surface->is_keyboard_shortcuts_inhibited()) {
+ wl_resource_post_error(
+ resource,
+ ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_ERROR_ALREADY_INHIBITED,
+ "the associated surface has already been set to inhibit keyboard "
+ "shortcuts");
+ return;
+ }
+
+ uint32_t version = wl_resource_get_version(resource);
+ wl_resource* keyboard_shortcuts_inhibitor_resource = wl_resource_create(
+ client, &zwp_keyboard_shortcuts_inhibitor_v1_interface, version, id);
+ SetImplementation(keyboard_shortcuts_inhibitor_resource,
+ &keyboard_shortcuts_inhibitor_implementation,
+ std::make_unique<KeyboardShortcutsInhibitor>(surface));
+}
+
+constexpr struct zwp_keyboard_shortcuts_inhibit_manager_v1_interface
+ keyboard_shortcuts_inhibit_manager_implementation = {
+ keyboard_shortcuts_inhibit_manager_destroy,
+ keyboard_shortcuts_inhibit_manager_inhibit_shortcuts,
+};
+
+} // namespace
+
+void bind_keyboard_shortcuts_inhibit_manager(wl_client* client,
+ void* data,
+ uint32_t version,
+ uint32_t id) {
+ wl_resource* resource = wl_resource_create(
+ client, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, version,
+ id);
+ wl_resource_set_implementation(
+ resource, &keyboard_shortcuts_inhibit_manager_implementation, data,
+ nullptr);
+}
+
+} // namespace exo::wayland
diff --git a/chromium/components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.h b/chromium/components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.h
new file mode 100644
index 00000000000..59fcebf02c6
--- /dev/null
+++ b/chromium/components/exo/wayland/zwp_keyboard_shortcuts_inhibit_manager.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_EXO_WAYLAND_ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_H_
+#define COMPONENTS_EXO_WAYLAND_ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_H_
+
+#include <stdint.h>
+
+struct wl_client;
+
+namespace exo::wayland {
+
+void bind_keyboard_shortcuts_inhibit_manager(wl_client* client,
+ void* data,
+ uint32_t version,
+ uint32_t id);
+
+} // namespace exo::wayland
+
+#endif // COMPONENTS_EXO_WAYLAND_ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_H_
diff --git a/chromium/components/exo/wayland/zwp_text_input_manager.cc b/chromium/components/exo/wayland/zwp_text_input_manager.cc
index afb1c709c06..acd824da85c 100644
--- a/chromium/components/exo/wayland/zwp_text_input_manager.cc
+++ b/chromium/components/exo/wayland/zwp_text_input_manager.cc
@@ -20,6 +20,7 @@
#include "components/exo/wayland/server_util.h"
#include "components/exo/xkb_tracker.h"
#include "ui/base/ime/utf_offset.h"
+#include "ui/base/wayland/wayland_server_input_types.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/ozone/layout/xkb/xkb_modifier_converter.h"
@@ -63,6 +64,8 @@ class WaylandTextInputDelegate : public TextInput::Delegate {
return weak_factory_.GetWeakPtr();
}
+ wl_resource* resource() { return text_input_; }
+
private:
wl_client* client() { return wl_resource_get_client(text_input_); }
@@ -271,6 +274,8 @@ class WaylandExtendedTextInput {
delegate_->set_extended_text_input(nullptr);
}
+ WaylandTextInputDelegate* delegate() { return delegate_.get(); }
+
private:
base::WeakPtr<WaylandTextInputDelegate> delegate_;
};
@@ -478,8 +483,40 @@ void extended_text_input_destroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
+void extended_text_input_set_input_type(wl_client* client,
+ wl_resource* resource,
+ uint32_t input_type,
+ uint32_t input_mode,
+ uint32_t input_flags,
+ uint32_t learning_mode) {
+ auto* delegate =
+ GetUserDataAs<WaylandExtendedTextInput>(resource)->delegate();
+ if (!delegate)
+ return;
+
+ // If unknown value is passed, fall back to the default one.
+ auto ui_type =
+ ui::wayland::ConvertToTextInputType(
+ static_cast<zcr_extended_text_input_v1_input_type>(input_type))
+ .value_or(ui::TEXT_INPUT_TYPE_TEXT);
+ auto ui_mode =
+ ui::wayland::ConvertToTextInputMode(
+ static_cast<zcr_extended_text_input_v1_input_mode>(input_mode))
+ .value_or(ui::TEXT_INPUT_MODE_DEFAULT);
+ // Ignore unknown flags.
+ auto ui_flags = ui::wayland::ConvertToTextInputFlags(input_flags).first;
+ bool should_do_learning =
+ learning_mode == ZCR_EXTENDED_TEXT_INPUT_V1_LEARNING_MODE_ENABLED;
+
+ auto* text_input = GetUserDataAs<TextInput>(delegate->resource());
+ text_input->SetTypeModeFlags(ui_type, ui_mode, ui_flags, should_do_learning);
+}
+
constexpr struct zcr_extended_text_input_v1_interface
- extended_text_input_implementation = {extended_text_input_destroy};
+ extended_text_input_implementation = {
+ extended_text_input_destroy,
+ extended_text_input_set_input_type,
+};
////////////////////////////////////////////////////////////////////////////////
// text_input_extension_v1 interface:
diff --git a/chromium/components/exo/wayland/zxdg_output_manager.cc b/chromium/components/exo/wayland/zxdg_output_manager.cc
index 9fdd209e822..6d58b63ebe0 100644
--- a/chromium/components/exo/wayland/zxdg_output_manager.cc
+++ b/chromium/components/exo/wayland/zxdg_output_manager.cc
@@ -18,7 +18,8 @@ namespace wayland {
void xdg_output_destroy(wl_client* client, wl_resource* resource) {
WaylandDisplayHandler* handler =
GetUserDataAs<WaylandDisplayHandler>(resource);
- handler->UnsetXdgOutputResource();
+ if (handler)
+ handler->UnsetXdgOutputResource();
wl_resource_destroy(resource);
}
@@ -46,9 +47,11 @@ void xdg_output_manager_get_xdg_output(wl_client* client,
WaylandDisplayHandler* handler =
GetUserDataAs<WaylandDisplayHandler>(output_resource);
- wl_resource_set_implementation(resource, &xdg_output_implementation, handler,
- nullptr);
- handler->OnXdgOutputCreated(resource);
+ if (handler) {
+ wl_resource_set_implementation(resource, &xdg_output_implementation,
+ handler, nullptr);
+ handler->OnXdgOutputCreated(resource);
+ }
}
const struct zxdg_output_manager_v1_interface
diff --git a/chromium/components/exo/wayland/zxdg_shell.cc b/chromium/components/exo/wayland/zxdg_shell.cc
index 64a17e56d2d..2d736fbd8c4 100644
--- a/chromium/components/exo/wayland/zxdg_shell.cc
+++ b/chromium/components/exo/wayland/zxdg_shell.cc
@@ -18,6 +18,7 @@
#include "components/exo/wayland/serial_tracker.h"
#include "components/exo/wayland/server_util.h"
#include "components/exo/wayland/wayland_positioner.h"
+#include "components/exo/wayland/xdg_shell.h"
#include "components/exo/xdg_shell_surface.h"
#include "ui/aura/window_observer.h"
#include "ui/base/hit_test.h"
@@ -166,21 +167,6 @@ uint32_t HandleXdgSurfaceV6ConfigureCallback(
return serial;
}
-struct WaylandXdgSurface {
- WaylandXdgSurface(std::unique_ptr<XdgShellSurface> shell_surface,
- SerialTracker* const serial_tracker)
- : shell_surface(std::move(shell_surface)),
- serial_tracker(serial_tracker) {}
-
- WaylandXdgSurface(const WaylandXdgSurface&) = delete;
- WaylandXdgSurface& operator=(const WaylandXdgSurface&) = delete;
-
- std::unique_ptr<XdgShellSurface> shell_surface;
-
- // Owned by Server, which always outlives this surface.
- SerialTracker* const serial_tracker;
-};
-
// Wrapper around shell surface that allows us to handle the case where the
// xdg surface resource is destroyed before the toplevel resource.
class WaylandToplevel : public aura::WindowObserver {
@@ -663,6 +649,8 @@ void xdg_shell_v6_get_xdg_surface(wl_client* client,
// mapped before they are enabled and can become visible.
shell_surface->SetEnabled(false);
+ shell_surface->SetCapabilities(GetCapabilities(client));
+
std::unique_ptr<WaylandXdgSurface> wayland_shell_surface =
std::make_unique<WaylandXdgSurface>(std::move(shell_surface),
data->serial_tracker);
diff --git a/chromium/components/exo/wm_helper.cc b/chromium/components/exo/wm_helper.cc
index 99979048acb..1cf41e1583b 100644
--- a/chromium/components/exo/wm_helper.cc
+++ b/chromium/components/exo/wm_helper.cc
@@ -45,6 +45,14 @@ void WMHelper::RemoveExoWindowObserver(ExoWindowObserver* observer) {
exo_window_observers_.RemoveObserver(observer);
}
+void WMHelper::AddPowerObserver(PowerObserver* observer) {
+ NOTREACHED();
+}
+
+void WMHelper::RemovePowerObserver(PowerObserver* observer) {
+ NOTREACHED();
+}
+
void WMHelper::NotifyExoWindowCreated(aura::Window* window) {
for (auto& obs : exo_window_observers_)
obs.OnExoWindowCreated(window);
diff --git a/chromium/components/exo/wm_helper.h b/chromium/components/exo/wm_helper.h
index 50129ad6349..d481ef6e2ca 100644
--- a/chromium/components/exo/wm_helper.h
+++ b/chromium/components/exo/wm_helper.h
@@ -113,6 +113,16 @@ class WMHelper : public aura::client::DragDropDelegate {
virtual void OnExoWindowCreated(aura::Window* window) {}
};
+ // Interface for Exo classes needing to listen to PowerManagerClient events.
+ //
+ // Only implemented for ChromeOS, otherwise a no-op.
+ class PowerObserver : public base::CheckedObserver {
+ public:
+ virtual void SuspendDone() {}
+ virtual void ScreenBrightnessChanged(double percent) {}
+ virtual void LidEventReceived(bool opened) {}
+ };
+
WMHelper();
WMHelper(const WMHelper&) = delete;
@@ -134,6 +144,9 @@ class WMHelper : public aura::client::DragDropDelegate {
void AddExoWindowObserver(ExoWindowObserver* observer);
void RemoveExoWindowObserver(ExoWindowObserver* observer);
+ virtual void AddPowerObserver(PowerObserver* observer);
+ virtual void RemovePowerObserver(PowerObserver* observer);
+
virtual void AddDragDropObserver(DragDropObserver* observer) = 0;
virtual void RemoveDragDropObserver(DragDropObserver* observer) = 0;
virtual void SetDragDropDelegate(aura::Window*) = 0;
diff --git a/chromium/components/exo/wm_helper_chromeos.cc b/chromium/components/exo/wm_helper_chromeos.cc
index b9f0ed84d26..0fce4fd6275 100644
--- a/chromium/components/exo/wm_helper_chromeos.cc
+++ b/chromium/components/exo/wm_helper_chromeos.cc
@@ -11,6 +11,9 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/memory/singleton.h"
+#include "base/time/time.h"
+#include "chromeos/dbus/power/power_manager_client.h"
+#include "chromeos/dbus/power_manager/backlight.pb.h"
#include "components/exo/wm_helper.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/client/drag_drop_delegate.h"
@@ -23,6 +26,7 @@
#include "ui/display/manager/display_configurator.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/types/display_snapshot.h"
+#include "ui/display/util/display_util.h"
#include "ui/wm/core/capture_controller.h"
#include "ui/wm/public/activation_client.h"
@@ -38,9 +42,18 @@ aura::Window* GetPrimaryRoot() {
////////////////////////////////////////////////////////////////////////////////
// WMHelperChromeOS, public:
-WMHelperChromeOS::WMHelperChromeOS() : vsync_timing_manager_(this) {}
+WMHelperChromeOS::WMHelperChromeOS() : vsync_timing_manager_(this) {
+ auto* power_manager = chromeos::PowerManagerClient::Get();
+ // May be null in tests
+ if (power_manager)
+ power_manager->AddObserver(this);
+}
-WMHelperChromeOS::~WMHelperChromeOS() {}
+WMHelperChromeOS::~WMHelperChromeOS() {
+ auto* power_manager = chromeos::PowerManagerClient::Get();
+ if (power_manager)
+ power_manager->RemoveObserver(this);
+}
WMHelperChromeOS* WMHelperChromeOS::GetInstance() {
return static_cast<WMHelperChromeOS*>(WMHelper::GetInstance());
@@ -80,6 +93,14 @@ void WMHelperChromeOS::RemoveFrameThrottlingObserver() {
&vsync_timing_manager_);
}
+void WMHelperChromeOS::AddPowerObserver(WMHelper::PowerObserver* observer) {
+ power_observers_.AddObserver(observer);
+}
+
+void WMHelperChromeOS::RemovePowerObserver(WMHelper::PowerObserver* observer) {
+ power_observers_.RemoveObserver(observer);
+}
+
void WMHelperChromeOS::AddActivationObserver(
wm::ActivationChangeObserver* observer) {
ash::Shell::Get()->activation_client()->AddObserver(observer);
@@ -162,6 +183,25 @@ aura::client::DragDropDelegate::DropCallback WMHelperChromeOS::GetDropCallback(
std::move(drop_callbacks));
}
+void WMHelperChromeOS::SuspendDone(base::TimeDelta sleep_duration) {
+ for (PowerObserver& observer : power_observers_)
+ observer.SuspendDone();
+}
+
+void WMHelperChromeOS::ScreenBrightnessChanged(
+ const power_manager::BacklightBrightnessChange& change) {
+ for (PowerObserver& observer : power_observers_)
+ observer.ScreenBrightnessChanged(change.percent());
+}
+
+void WMHelperChromeOS::LidEventReceived(
+ chromeos::PowerManagerClient::LidState state,
+ base::TimeTicks timestamp) {
+ for (PowerObserver& observer : power_observers_)
+ observer.LidEventReceived(state ==
+ chromeos::PowerManagerClient::LidState::OPEN);
+}
+
void WMHelperChromeOS::AddVSyncParameterObserver(
mojo::PendingRemote<viz::mojom::VSyncParameterObserver> observer) {
GetPrimaryRoot()->layer()->GetCompositor()->AddVSyncParameterObserver(
@@ -272,7 +312,7 @@ aura::client::CaptureClient* WMHelperChromeOS::GetCaptureClient() {
}
float GetDefaultDeviceScaleFactor() {
- if (!display::Display::HasInternalDisplay())
+ if (!display::HasInternalDisplay())
return 1.0;
if (display::Display::HasForceDeviceScaleFactor())
diff --git a/chromium/components/exo/wm_helper_chromeos.h b/chromium/components/exo/wm_helper_chromeos.h
index e42de661235..18ac572f655 100644
--- a/chromium/components/exo/wm_helper_chromeos.h
+++ b/chromium/components/exo/wm_helper_chromeos.h
@@ -10,6 +10,7 @@
#include "ash/display/window_tree_host_manager.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "chromeos/dbus/power/power_manager_client.h"
#include "components/exo/vsync_timing_manager.h"
#include "components/exo/wm_helper.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@@ -49,7 +50,9 @@ namespace exo {
// A ChromeOS-specific helper class for accessing WindowManager related
// features.
-class WMHelperChromeOS : public WMHelper, public VSyncTimingManager::Delegate {
+class WMHelperChromeOS : public WMHelper,
+ public chromeos::PowerManagerClient::Observer,
+ public VSyncTimingManager::Delegate {
public:
WMHelperChromeOS();
@@ -76,6 +79,8 @@ class WMHelperChromeOS : public WMHelper, public VSyncTimingManager::Delegate {
aura::client::FocusChangeObserver* observer) override;
void AddDragDropObserver(DragDropObserver* observer) override;
void RemoveDragDropObserver(DragDropObserver* observer) override;
+ void AddPowerObserver(WMHelper::PowerObserver* observer) override;
+ void RemovePowerObserver(WMHelper::PowerObserver* observer) override;
void SetDragDropDelegate(aura::Window*) override;
void ResetDragDropDelegate(aura::Window*) override;
VSyncTimingManager& GetVSyncTimingManager() override;
@@ -115,6 +120,13 @@ class WMHelperChromeOS : public WMHelper, public VSyncTimingManager::Delegate {
aura::client::DragDropDelegate::DropCallback GetDropCallback(
const ui::DropTargetEvent& event) override;
+ // Overridden from chromeos::PowerManagerClient::Observer:
+ void SuspendDone(base::TimeDelta sleep_duration) override;
+ void ScreenBrightnessChanged(
+ const power_manager::BacklightBrightnessChange& change) override;
+ void LidEventReceived(chromeos::PowerManagerClient::LidState state,
+ base::TimeTicks timestamp) override;
+
// Overridden from VSyncTimingManager::Delegate:
void AddVSyncParameterObserver(
mojo::PendingRemote<viz::mojom::VSyncParameterObserver> observer)
@@ -127,6 +139,7 @@ class WMHelperChromeOS : public WMHelper, public VSyncTimingManager::Delegate {
ui::mojom::DragOperation& output_drag_op);
base::ObserverList<DragDropObserver>::Unchecked drag_drop_observers_;
+ base::ObserverList<PowerObserver> power_observers_;
LifetimeManager lifetime_manager_;
VSyncTimingManager vsync_timing_manager_;
bool default_scale_cancellation_ = true;
diff --git a/chromium/components/external_intents/android/BUILD.gn b/chromium/components/external_intents/android/BUILD.gn
index 44c58770bcd..054e7ab4584 100644
--- a/chromium/components/external_intents/android/BUILD.gn
+++ b/chromium/components/external_intents/android/BUILD.gn
@@ -7,7 +7,6 @@ import("//build/config/android/rules.gni")
android_library("java") {
sources = [
"java/src/org/chromium/components/external_intents/AuthenticatorNavigationInterceptor.java",
- "java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java",
"java/src/org/chromium/components/external_intents/ExternalIntentsSwitches.java",
"java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java",
"java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java",
@@ -21,6 +20,8 @@ android_library("java") {
deps = [
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/embedder_support/android:util_java",
"//components/navigation_interception/android:navigation_interception_java",
"//components/url_formatter/android:url_formatter_java",
@@ -45,18 +46,11 @@ android_resources("java_resources") {
}
generate_jni("jni_headers") {
- sources = [
- "java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java",
- "java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java",
- ]
+ sources = [ "java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java" ]
}
static_library("android") {
- sources = [
- "external_intents_features.cc",
- "external_intents_features.h",
- "intercept_navigation_delegate_impl.cc",
- ]
+ sources = [ "intercept_navigation_delegate_impl.cc" ]
deps = [
":jni_headers",
diff --git a/chromium/components/external_intents/android/external_intents_features.cc b/chromium/components/external_intents/android/external_intents_features.cc
deleted file mode 100644
index dca311ee082..00000000000
--- a/chromium/components/external_intents/android/external_intents_features.cc
+++ /dev/null
@@ -1,35 +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 "components/external_intents/android/external_intents_features.h"
-
-#include <jni.h>
-#include <stddef.h>
-#include <string>
-
-#include "base/android/jni_string.h"
-#include "base/notreached.h"
-#include "components/external_intents/android/jni_headers/ExternalIntentsFeatures_jni.h"
-
-namespace external_intents {
-
-namespace {
-
-// Array of features exposed through the Java ExternalIntentsFeatures API.
-const base::Feature* kFeaturesExposedToJava[] = {
- &kIntentBlockExternalFormRedirectsNoGesture,
-};
-
-} // namespace
-
-// Alphabetical:
-const base::Feature kIntentBlockExternalFormRedirectsNoGesture{
- "IntentBlockExternalFormRedirectsNoGesture",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
-static jlong JNI_ExternalIntentsFeatures_GetFeature(JNIEnv* env, jint ordinal) {
- return reinterpret_cast<jlong>(kFeaturesExposedToJava[ordinal]);
-}
-
-} // namespace external_intents
diff --git a/chromium/components/external_intents/android/external_intents_features.h b/chromium/components/external_intents/android/external_intents_features.h
deleted file mode 100644
index e6c0d54b303..00000000000
--- a/chromium/components/external_intents/android/external_intents_features.h
+++ /dev/null
@@ -1,17 +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 COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURES_H_
-#define COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURES_H_
-
-#include "base/feature_list.h"
-
-namespace external_intents {
-
-// Alphabetical:
-extern const base::Feature kIntentBlockExternalFormRedirectsNoGesture;
-
-} // namespace external_intents
-
-#endif // COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURES_H_
diff --git a/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java b/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
index 394c48c8c24..6943d5449f8 100644
--- a/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
+++ b/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
@@ -48,6 +48,7 @@ import org.chromium.base.ThreadUtils;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.MaxAndroidSdkLevel;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.components.external_intents.ExternalNavigationDelegate.IntentToAutofillAllowingAppResult;
import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingAsyncActionType;
import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
@@ -86,7 +87,6 @@ public class ExternalNavigationHandlerTest {
private static final boolean IS_CUSTOM_TAB_INTENT = true;
private static final boolean SEND_TO_EXTERNAL_APPS = true;
- private static final boolean IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED = true;
private static final boolean HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY = true;
private static final boolean INTENT_STARTED_TASK = true;
@@ -95,6 +95,7 @@ public class ExternalNavigationHandlerTest {
private static final String YOUTUBE_URL = "http://youtube.com/";
private static final String YOUTUBE_MOBILE_URL = "http://m.youtube.com";
private static final String YOUTUBE_PACKAGE_NAME = "youtube";
+ private static final String OTHER_BROWSER_PACKAGE = "com.other.browser";
private static final String SEARCH_RESULT_URL_FOR_TOM_HANKS =
"https://www.google.com/search?q=tom+hanks";
@@ -328,12 +329,14 @@ public class ExternalNavigationHandlerTest {
.withPageTransition(PageTransition.FORM_SUBMIT)
.withIsRedirect(true)
.withHasUserGesture(false)
- .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+ .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY);
checkUrl("http://youtube.com://")
.withPageTransition(PageTransition.FORM_SUBMIT)
.withIsRedirect(true)
.withHasUserGesture(false)
- .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+ .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY);
}
@Test
@@ -345,8 +348,7 @@ public class ExternalNavigationHandlerTest {
redirectHandler.updateIntent(
Intent.parseUri("http://example.test", Intent.URI_INTENT_SCHEME),
- !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(
PageTransition.LINK | PageTransition.FROM_API, false, false, 0, 0, true);
redirectHandler.updateNewUrlLoading(PageTransition.FORM_SUBMIT, false, false, 0, 0, false);
@@ -427,12 +429,6 @@ public class ExternalNavigationHandlerTest {
@Test
@SmallTest
public void testWtai() {
- // Start the telephone application with the given number.
- checkUrl("wtai://wp/mc;0123456789")
- .withIsIncognito(true)
- .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
- START_OTHER_ACTIVITY | INTENT_SANITIZATION_EXCEPTION);
-
// These two cases are currently unimplemented.
checkUrl("wtai://wp/sd;0123456789")
.expecting(OverrideUrlLoadingResultType.NO_OVERRIDE,
@@ -638,8 +634,8 @@ public class ExternalNavigationHandlerTest {
// Ignore if url is redirected, transition type is IncomingIntent and a new intent doesn't
// have any new resolver.
- redirectHandler.updateIntent(ytIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ ytIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
checkUrl(YOUTUBE_MOBILE_URL)
@@ -650,8 +646,8 @@ public class ExternalNavigationHandlerTest {
.expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
// Do not ignore if a new intent has any new resolver.
- redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
checkUrl(YOUTUBE_MOBILE_URL)
@@ -663,8 +659,8 @@ public class ExternalNavigationHandlerTest {
START_OTHER_ACTIVITY);
// Do not ignore if a new intent cannot be handled by Chrome.
- redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
checkUrl("intent://myownurl")
@@ -687,8 +683,8 @@ public class ExternalNavigationHandlerTest {
int transTypeLinkFromIntent = PageTransition.LINK | PageTransition.FROM_API;
// Ignore if an initial Intent was heading to Chrome.
- redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
checkUrl(YOUTUBE_MOBILE_URL)
@@ -699,8 +695,8 @@ public class ExternalNavigationHandlerTest {
.expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
// Do not ignore if the URI has an external protocol.
- redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
checkUrl("market://1234")
@@ -723,8 +719,8 @@ public class ExternalNavigationHandlerTest {
// In Custom Tabs, if the first url is not a redirect, stay in chrome.
Intent barIntent = Intent.parseUri(YOUTUBE_URL, Intent.URI_INTENT_SCHEME);
barIntent.setPackage(mContext.getPackageName());
- redirectHandler.updateIntent(barIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ barIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
checkUrl(YOUTUBE_URL)
.withPageTransition(transTypeLinkFromIntent)
@@ -735,8 +731,8 @@ public class ExternalNavigationHandlerTest {
// In Custom Tabs, if the first url is a redirect, don't allow it to intent out.
Intent fooIntent = Intent.parseUri("http://foo.com/", Intent.URI_INTENT_SCHEME);
fooIntent.setPackage(mContext.getPackageName());
- redirectHandler.updateIntent(fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
checkUrl(YOUTUBE_URL)
@@ -750,8 +746,8 @@ public class ExternalNavigationHandlerTest {
// url is a redirect.
Intent extraIntent2 = Intent.parseUri(YOUTUBE_URL, Intent.URI_INTENT_SCHEME);
extraIntent2.setPackage(mContext.getPackageName());
- redirectHandler.updateIntent(extraIntent2, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ extraIntent2, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
checkUrl(YOUTUBE_URL)
@@ -764,8 +760,8 @@ public class ExternalNavigationHandlerTest {
Intent extraIntent3 = Intent.parseUri(YOUTUBE_URL, Intent.URI_INTENT_SCHEME);
extraIntent3.setPackage(mContext.getPackageName());
- redirectHandler.updateIntent(extraIntent3, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ extraIntent3, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
checkUrl(YOUTUBE_URL)
@@ -777,8 +773,8 @@ public class ExternalNavigationHandlerTest {
START_OTHER_ACTIVITY);
// External intent for a user-initiated navigation should always be allowed.
- redirectHandler.updateIntent(fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
// Simulate a real user navigation.
redirectHandler.updateNewUrlLoading(
@@ -801,8 +797,8 @@ public class ExternalNavigationHandlerTest {
Intent fooIntent = Intent.parseUri("http://foo.com/", Intent.URI_INTENT_SCHEME);
fooIntent.putExtra(CustomTabsIntent.EXTRA_ENABLE_INSTANT_APPS, true);
fooIntent.setPackage(mContext.getPackageName());
- redirectHandler.updateIntent(fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ fooIntent, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
@@ -863,8 +859,8 @@ public class ExternalNavigationHandlerTest {
RedirectHandler redirectHandler = RedirectHandler.create();
Intent fooIntent =
Intent.parseUri("http://instantappenabled.com", Intent.URI_INTENT_SCHEME);
- redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS,
- IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED, !INTENT_STARTED_TASK);
+ redirectHandler.updateIntent(
+ fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
@@ -912,7 +908,7 @@ public class ExternalNavigationHandlerTest {
+ "https%3A%2F%2Fwww.google.com;end";
mUrlHandler.mIsSerpReferrer = true;
- mDelegate.setIsIntentToInstantApp(true);
+ mDelegate.setHandlesInstantAppLaunchingInternally(true);
checkUrl(intentUrl).expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
START_OTHER_ACTIVITY | PROXY_FOR_INSTANT_APPS);
Assert.assertTrue(
@@ -922,14 +918,34 @@ public class ExternalNavigationHandlerTest {
mUrlHandler.mIsSerpReferrer = false;
checkUrl(intentUrl).expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
- // Check that that just having the SERP referrer alone doesn't cause intents to be treated
- // as intents to instant apps if the delegate indicates that they shouldn't be.
+ // Check that that just having the SERP referrer alone doesn't cause instant app intents to
+ // launched through the proxy.
mUrlHandler.mIsSerpReferrer = true;
- mDelegate.setIsIntentToInstantApp(false);
- checkUrl(intentUrl).expecting(
- OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT, START_OTHER_ACTIVITY);
- Assert.assertFalse(
- mUrlHandler.mStartActivityIntent.getBooleanExtra(IS_INSTANT_APP_EXTRA, true));
+ mDelegate.setHandlesInstantAppLaunchingInternally(false);
+ checkUrl(intentUrl).expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+ }
+
+ @Test
+ @SmallTest
+ public void testIsIntentToInstantApp() {
+ // Check that the delegate correctly distinguishes instant app intents from others.
+ String instantAppIntentUrlPrefix = "intent://buzzfeed.com/tasty#Intent;scheme=http;";
+
+ mUrlHandler.mIsSerpReferrer = true;
+ mDelegate.setHandlesInstantAppLaunchingInternally(true);
+
+ // Check that Supervisor is detected by action even without package.
+ for (String action : ExternalNavigationHandler.INSTANT_APP_START_ACTIONS) {
+ String intentUrl = instantAppIntentUrlPrefix + "action=" + action + ";end";
+ checkUrl(intentUrl).expecting(
+ OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY | PROXY_FOR_INSTANT_APPS);
+ }
+
+ String intentUrl = instantAppIntentUrlPrefix
+ + "package=" + ExternalNavigationHandler.INSTANT_APP_SUPERVISOR_PKG + ";end";
+ checkUrl(intentUrl).expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY | PROXY_FOR_INSTANT_APPS);
}
@Test
@@ -1837,9 +1853,50 @@ public class ExternalNavigationHandlerTest {
@SuppressLint("SdCardPath")
@SmallTest
@MaxAndroidSdkLevel(Build.VERSION_CODES.S)
- public void testFileAccess() {
+ public void testFileAccessHtml() {
+ String fileUrl = "file:///sdcard/Downloads/test.html";
+
+ mUrlHandler.mShouldRequestFileAccess = false;
+ // Verify no overrides if file access is allowed (under different load conditions).
+ checkUrl(fileUrl).expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+ checkUrl(fileUrl)
+ .withPageTransition(PageTransition.RELOAD)
+ .withIsRendererInitiated(false)
+ .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+ checkUrl(fileUrl)
+ .withPageTransition(PageTransition.AUTO_TOPLEVEL)
+ .withIsRendererInitiated(false)
+ .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+
+ mUrlHandler.mShouldRequestFileAccess = true;
+ // Verify that the file intent action is triggered if file access is not allowed.
+ checkUrl(fileUrl)
+ .withPageTransition(PageTransition.AUTO_TOPLEVEL)
+ .withIsRendererInitiated(false)
+ .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION,
+ OverrideUrlLoadingAsyncActionType.UI_GATING_BROWSER_NAVIGATION, START_FILE);
+ }
+
+ @Test
+ @SmallTest
+ @MinAndroidSdkLevel(33) // TODO(twellington): Replace with version code when available.
+ public void testFileAccessHtml_AndroidT() {
String fileUrl = "file:///sdcard/Downloads/test.html";
+ mUrlHandler.mShouldRequestFileAccess = true;
+ // Verify that the file intent is not triggered if the mime type can't be handled.
+ checkUrl(fileUrl)
+ .withPageTransition(PageTransition.AUTO_TOPLEVEL)
+ .withIsRendererInitiated(false)
+ .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+ }
+
+ @Test
+ @SuppressLint("SdCardPath")
+ @SmallTest
+ public void testFileAccessImage() {
+ String fileUrl = "file://file.png";
+
mUrlHandler.mShouldRequestFileAccess = false;
// Verify no overrides if file access is allowed (under different load conditions).
checkUrl(fileUrl).expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
@@ -2382,19 +2439,36 @@ public class ExternalNavigationHandlerTest {
@Test
@SmallTest
- public void testIntentToOtherBrowser() {
- // This will create a non-specialized ResolveInfo for the target package.
- mDelegate.setCanResolveActivityForExternalSchemes(true);
+ public void testUrlIntentToOtherBrowser() {
+ mDelegate.setResolvesToOtherBrowser(true);
+
+ String unsafeUrls[] = new String[] {
+ "intent:#Intent;S.EXTRA_HIDDEN_URL=encodedUrl;action=CUSTOM.ACTION;end",
+ "intent:#Intent;S.EXTRA_HIDDEN_URL=encodedUrl;end",
+ "intent://example.com#Intent;scheme=https;action=CUSTOM.ACTION;end",
+ "intent://example.com#Intent;scheme=https;end", "intent:example.com#Intent;end"};
+ for (String url : unsafeUrls) {
+ checkUrl(url)
+ .withPageTransition(PageTransition.LINK)
+ .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY);
+ Assert.assertTrue(mUrlHandler.mRequiresIntentChooser);
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testSafeIntentToOtherBrowser() throws Exception {
+ mDelegate.setResolvesToOtherBrowser(true);
- String intent = "intent://example.com#Intent;scheme=https;package=com.other.browser;end";
+ String intent =
+ "intent:#Intent;action=ACTION.PROMO;package=" + OTHER_BROWSER_PACKAGE + ";end";
- // This is a limitation of this testing harness, which doesn't get past
- // startActivityIfNeeded as that requires an Activity context. This functionality is
- // tested in ExternalNavigationDelegateImplTest.
checkUrl(intent)
.withPageTransition(PageTransition.LINK)
.expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
START_OTHER_ACTIVITY);
+ Assert.assertFalse(mUrlHandler.mRequiresIntentChooser);
}
@Test
@@ -2527,7 +2601,7 @@ public class ExternalNavigationHandlerTest {
public boolean mShouldRequestFileAccess;
public String mNewUrlAfterClobbering;
public String mReferrerUrlForClobbering;
- public boolean mStartFileIntentCalled;
+ public boolean mRequestFilePermissionsCalled;
public Intent mStartActivityInIncognitoIntent;
public boolean mStartIncognitoIntentCalled;
public boolean mCanShowIncognitoDialog;
@@ -2535,6 +2609,7 @@ public class ExternalNavigationHandlerTest {
public boolean mResolveInfoContainsSelf;
public Intent mStartActivityIntent;
public boolean mCalledWithProxy;
+ public boolean mRequiresIntentChooser;
private boolean mSendIntentsForReal;
public ExternalNavigationHandlerForTesting(ExternalNavigationDelegate delegate) {
@@ -2548,11 +2623,6 @@ public class ExternalNavigationHandlerTest {
}
@Override
- public boolean blockExternalFormRedirectsWithoutGesture() {
- return true;
- }
-
- @Override
protected boolean canLaunchIncognitoIntent(Intent intent, Context context) {
mStartActivityInIncognitoIntent = intent;
mStartIncognitoIntentCalled = true;
@@ -2589,13 +2659,14 @@ public class ExternalNavigationHandlerTest {
}
@Override
- protected boolean shouldRequestFileAccess(GURL url) {
+ protected boolean shouldRequestFileAccess(GURL url, String permissionNeeded) {
return mShouldRequestFileAccess;
}
@Override
- protected void startFileIntent(ExternalNavigationParams params) {
- mStartFileIntentCalled = true;
+ protected void requestFilePermissions(
+ ExternalNavigationParams params, String permissionNeeded) {
+ mRequestFilePermissionsCalled = true;
}
@Override
@@ -2622,6 +2693,7 @@ public class ExternalNavigationHandlerTest {
GURL intentDataUrl, GURL referrerUrl) {
mStartActivityIntent = intent;
mCalledWithProxy = proxy;
+ mRequiresIntentChooser = requiresIntentChooser;
if (mSendIntentsForReal) {
return super.startActivity(intent, proxy, requiresIntentChooser, resolvingInfos,
resolveActivity, browserFallbackUrl, intentDataUrl, referrerUrl);
@@ -2653,7 +2725,6 @@ public class ExternalNavigationHandlerTest {
intentActivity.packageName(), intentActivity));
}
}
- if (!list.isEmpty()) return list;
String schemeString = intent.getScheme();
boolean isMarketScheme = schemeString != null && schemeString.startsWith("market");
@@ -2668,6 +2739,9 @@ public class ExternalNavigationHandlerTest {
// Scheme-less intents (eg. Action-based intents like opening Settings).
list.add(newResolveInfo("package"));
}
+ if (mResolvesToOtherBrowser) {
+ list.add(newResolveInfo(OTHER_BROWSER_PACKAGE));
+ }
return list;
}
@@ -2675,6 +2749,9 @@ public class ExternalNavigationHandlerTest {
if (mWillResolveToDisambiguationDialog) {
return newResolveInfo("android.disambiguation.dialog");
}
+ if (mResolvesToOtherBrowser) {
+ return newResolveInfo(OTHER_BROWSER_PACKAGE);
+ }
List<ResolveInfo> list = queryIntentActivities(intent);
return list.size() > 0 ? list.get(0) : null;
@@ -2706,7 +2783,7 @@ public class ExternalNavigationHandlerTest {
@Override
public boolean handlesInstantAppLaunchingInternally() {
- return false;
+ return mHandlesInstantAppLaunchingInternally;
}
@Override
@@ -2803,11 +2880,6 @@ public class ExternalNavigationHandlerTest {
}
@Override
- public boolean isIntentToInstantApp(Intent intent) {
- return mIsIntentToInstantApp;
- }
-
- @Override
public boolean isIntentToAutofillAssistant(Intent intent) {
return mIsIntentToAutofillAssistant;
}
@@ -2898,10 +2970,6 @@ public class ExternalNavigationHandlerTest {
mShouldDisableExternalIntentRequests = disable;
}
- public void setIsIntentToInstantApp(boolean value) {
- mIsIntentToInstantApp = value;
- }
-
public void setIsIntentToAutofillAssistant(boolean value) {
mIsIntentToAutofillAssistant = value;
}
@@ -2939,6 +3007,14 @@ public class ExternalNavigationHandlerTest {
mShouldEmbedderInitiatedNavigationsStayInBrowser = value;
}
+ public void setHandlesInstantAppLaunchingInternally(boolean value) {
+ mHandlesInstantAppLaunchingInternally = value;
+ }
+
+ public void setResolvesToOtherBrowser(boolean value) {
+ mResolvesToOtherBrowser = value;
+ }
+
public boolean startIncognitoIntentCalled;
public boolean maybeSetRequestMetadataCalled;
public Callback<Boolean> incognitoDialogUserDecisionCallback;
@@ -2953,7 +3029,6 @@ public class ExternalNavigationHandlerTest {
public boolean mIsChromeAppInForeground = true;
private boolean mIsCallingAppTrusted;
private boolean mShouldDisableExternalIntentRequests;
- private boolean mIsIntentToInstantApp;
private boolean mIsIntentToAutofillAssistant;
private @IntentToAutofillAllowingAppResult int mAutofillAssistantAllowAppOverrideResult;
private boolean mCanLoadUrlInTab;
@@ -2964,6 +3039,8 @@ public class ExternalNavigationHandlerTest {
private boolean mWillResolveToDisambiguationDialog;
private Context mContext;
private boolean mShouldEmbedderInitiatedNavigationsStayInBrowser = true;
+ private boolean mHandlesInstantAppLaunchingInternally = true;
+ private boolean mResolvesToOtherBrowser;
}
private void checkIntentSanity(Intent intent, String name) {
@@ -3109,7 +3186,7 @@ public class ExternalNavigationHandlerTest {
Assert.assertEquals(expectStartIncognito, mUrlHandler.mStartIncognitoIntentCalled);
Assert.assertEquals(expectStartActivity, startActivityCalled);
Assert.assertEquals(expectStartWebApk, startWebApkCalled);
- Assert.assertEquals(expectStartFile, mUrlHandler.mStartFileIntentCalled);
+ Assert.assertEquals(expectStartFile, mUrlHandler.mRequestFilePermissionsCalled);
Assert.assertEquals(expectProxyForIA, mUrlHandler.mCalledWithProxy);
if (startActivityCalled && expectSaneIntent) {
diff --git a/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java b/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java
index 70f1d6f943d..fe8d7414d26 100644
--- a/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java
+++ b/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java
@@ -82,7 +82,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testRealIntentRedirect() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
@@ -105,7 +105,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testEffectiveIntentRedirect_linkNavigation() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
@@ -128,7 +128,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testEffectiveIntentRedirect_formSubmit() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
@@ -151,7 +151,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testNoIntent() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(null, false, false, false, false);
+ handler.updateIntent(null, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
@@ -174,7 +174,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testClear() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
@@ -202,7 +202,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testNonLinkFromIntent() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false);
@@ -225,7 +225,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testUserInteraction() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
@@ -260,7 +260,7 @@ public class RedirectHandlerTest {
RedirectHandler handler = RedirectHandler.create();
Intent fooIntent = new Intent(sFooIntent);
fooIntent.putExtra(Browser.EXTRA_APPLICATION_ID, TEST_PACKAGE_NAME);
- handler.updateIntent(fooIntent, false, false, false, false);
+ handler.updateIntent(fooIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
@@ -288,7 +288,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testNavigationFromUserTyping() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false);
@@ -315,7 +315,7 @@ public class RedirectHandlerTest {
RedirectHandler handler = RedirectHandler.create();
Intent fooIntent = new Intent(sFooIntent);
fooIntent.setPackage(TEST_PACKAGE_NAME);
- handler.updateIntent(fooIntent, false, false, false, false);
+ handler.updateIntent(fooIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
@@ -346,7 +346,7 @@ public class RedirectHandlerTest {
// 1. 3XX redirection should not override URL loading.
/////////////////////////////////////////////////////
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
handler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
Assert.assertFalse(handler.shouldNotOverrideUrlLoading());
@@ -360,7 +360,7 @@ public class RedirectHandlerTest {
// 2. Effective redirection should not override URL loading.
/////////////////////////////////////////////////////
handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
handler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
Assert.assertFalse(handler.shouldNotOverrideUrlLoading());
@@ -386,7 +386,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testNavigationFromLinkWithoutUserGesture() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
long lastUserInteractionTime = SystemClock.elapsedRealtime();
@@ -417,7 +417,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testNavigationFromReload() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
long lastUserInteractionTime = SystemClock.elapsedRealtime();
@@ -448,7 +448,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testNavigationWithForwardBack() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sYtIntent, false, false, false, false);
+ handler.updateIntent(sYtIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
long lastUserInteractionTime = SystemClock.elapsedRealtime();
@@ -506,7 +506,7 @@ public class RedirectHandlerTest {
@Feature({"IntentHandling"})
public void testClientRedirectWithoutUserGesture() {
RedirectHandler handler = RedirectHandler.create();
- handler.updateIntent(sFooIntent, false, false, false, false);
+ handler.updateIntent(sFooIntent, false, false, false);
Assert.assertFalse(handler.isOnNavigation());
Assert.assertFalse(handler.hasUserStartedNonInitialNavigation());
diff --git a/chromium/components/favicon/android/BUILD.gn b/chromium/components/favicon/android/BUILD.gn
index a0d2d845e75..076d0cd0644 100644
--- a/chromium/components/favicon/android/BUILD.gn
+++ b/chromium/components/favicon/android/BUILD.gn
@@ -8,7 +8,8 @@ android_library("java") {
sources = [ "java/src/org/chromium/components/favicon/LargeIconBridge.java" ]
deps = [
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/util/android:java",
"//content/public/android:content_full_java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/components/favicon/content/content_favicon_driver_unittest.cc b/chromium/components/favicon/content/content_favicon_driver_unittest.cc
index 656a70204a3..046a858fd85 100644
--- a/chromium/components/favicon/content/content_favicon_driver_unittest.cc
+++ b/chromium/components/favicon/content/content_favicon_driver_unittest.cc
@@ -39,7 +39,7 @@ void TestFetchFaviconForPage(
ContentFaviconDriver::FromWebContents(web_contents);
content::WebContentsTester::For(web_contents)->NavigateAndCommit(page_url);
static_cast<content::WebContentsObserver*>(favicon_driver)
- ->DidUpdateFaviconURL(web_contents->GetMainFrame(), candidates);
+ ->DidUpdateFaviconURL(web_contents->GetPrimaryMainFrame(), candidates);
base::RunLoop().RunUntilIdle();
}
@@ -116,13 +116,14 @@ TEST_F(ContentFaviconDriverTest, IgnoreManifestURLBeforeOnLoad) {
navigation->SetKeepLoading(true);
navigation->Commit();
GURL manifest_url = kFakeManifestURL;
- auto* rfh_tester =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame());
+ auto* rfh_tester = content::RenderFrameHostTester::For(
+ web_contents()->GetPrimaryMainFrame());
rfh_tester->SimulateManifestURLUpdate(manifest_url);
static_cast<content::WebContentsObserver*>(favicon_driver)
- ->DidUpdateWebManifestURL(web_contents()->GetMainFrame(), manifest_url);
- EXPECT_EQ(GURL(),
- favicon_driver->GetManifestURL(web_contents()->GetMainFrame()));
+ ->DidUpdateWebManifestURL(web_contents()->GetPrimaryMainFrame(),
+ manifest_url);
+ EXPECT_EQ(GURL(), favicon_driver->GetManifestURL(
+ web_contents()->GetPrimaryMainFrame()));
}
// Ensures that we use a manifest URL if it arrives after the onload handler
@@ -136,13 +137,14 @@ TEST_F(ContentFaviconDriverTest, UseManifestURLAFterOnLoad) {
navigation->Commit();
navigation->StopLoading();
GURL manifest_url = kFakeManifestURL;
- auto* rfh_tester =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame());
+ auto* rfh_tester = content::RenderFrameHostTester::For(
+ web_contents()->GetPrimaryMainFrame());
rfh_tester->SimulateManifestURLUpdate(manifest_url);
static_cast<content::WebContentsObserver*>(favicon_driver)
- ->DidUpdateWebManifestURL(web_contents()->GetMainFrame(), manifest_url);
- EXPECT_EQ(kFakeManifestURL,
- favicon_driver->GetManifestURL(web_contents()->GetMainFrame()));
+ ->DidUpdateWebManifestURL(web_contents()->GetPrimaryMainFrame(),
+ manifest_url);
+ EXPECT_EQ(kFakeManifestURL, favicon_driver->GetManifestURL(
+ web_contents()->GetPrimaryMainFrame()));
}
// Test that no download is initiated when
@@ -159,7 +161,8 @@ TEST_F(ContentFaviconDriverTest, ShouldNotCauseImageDownload) {
favicon_urls.push_back(blink::mojom::FaviconURL::New(
kIconURL, blink::mojom::FaviconIconType::kFavicon, kEmptyIconSizes));
static_cast<content::WebContentsObserver*>(favicon_driver)
- ->DidUpdateFaviconURL(web_contents()->GetMainFrame(), favicon_urls);
+ ->DidUpdateFaviconURL(web_contents()->GetPrimaryMainFrame(),
+ favicon_urls);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(web_contents_tester()->HasPendingDownloadImage(kIconURL));
@@ -223,7 +226,7 @@ TEST_F(ContentFaviconDriverTestNoFaviconService,
// Trigger downloading a manifest.
static_cast<content::WebContentsObserver*>(driver)->DidUpdateWebManifestURL(
- web_contents()->GetMainFrame(), GURL("http://bad.manifest.com"));
+ web_contents()->GetPrimaryMainFrame(), GURL("http://bad.manifest.com"));
// The request for the manifest is still pending, delete the WebContents,
// which should trigger notifying the callback for the manifest and *not*
diff --git a/chromium/components/favicon_base/favicon_url_parser.cc b/chromium/components/favicon_base/favicon_url_parser.cc
index a8c23a8a8d6..2df39ba1f8b 100644
--- a/chromium/components/favicon_base/favicon_url_parser.cc
+++ b/chromium/components/favicon_base/favicon_url_parser.cc
@@ -97,18 +97,20 @@ bool ParseFaviconPathWithFavicon2Format(const std::string& path,
const base::StringPiece key = it.GetKey();
// Note: each of these keys can be used in chrome://favicon2 path. See file
// "favicon_url_parser.h" for a description of what each one does.
- if (key == "allow_google_server_fallback") {
+ if (key == "allow_google_server_fallback" ||
+ key == "allowGoogleServerFallback") {
const std::string val = it.GetUnescapedValue();
if (!(val == "0" || val == "1"))
return false;
parsed->allow_favicon_server_fallback = val == "1";
- } else if (key == "show_fallback_monogram") {
+ } else if (key == "show_fallback_monogram" ||
+ key == "showFallbackMonogram") {
parsed->show_fallback_monogram = true;
- } else if (key == "icon_url") {
+ } else if (key == "icon_url" || key == "iconUrl") {
parsed->icon_url = it.GetUnescapedValue();
- } else if (key == "page_url") {
+ } else if (key == "page_url" || key == "pageUrl") {
parsed->page_url = it.GetUnescapedValue();
- } else if (key == "scale_factor" &&
+ } else if ((key == "scale_factor" || key == "scaleFactor") &&
!webui::ParseScaleFactor(it.GetUnescapedValue(),
&parsed->device_scale_factor)) {
return false;
diff --git a/chromium/components/feature_engagement/README.md b/chromium/components/feature_engagement/README.md
index 1420c3eb5e4..7fed8c5e720 100644
--- a/chromium/components/feature_engagement/README.md
+++ b/chromium/components/feature_engagement/README.md
@@ -229,7 +229,7 @@ if (trigger_help_ui) {
}
```
-If `feature_engagement::Tracker::ShouldTriggerHelpUI` return `true` you must
+If `feature_engagement::Tracker::ShouldTriggerHelpUI` returns `true`, you must
display the In-Product Help, as it will be tracked as if you showed it. In
addition you are required to inform when the feature has been dismissed:
@@ -292,8 +292,8 @@ shown.
To ensure that your in-product help triggers at the right time, you need to
configure what the constraints are for showing. There are two ways of doing
this: (1) Using a [client side configuration](#client-side-configuration), or
-(2) [field trial configuration](#field-trial-configuration). It is also possible
-to use a mix of both (1) and (2).
+(2) using a [field trial configuration](#field-trial-configuration). It is also
+possible to use a mix of both (1) and (2).
Please read both sections below to figure out what fits your use-case best.
@@ -340,8 +340,8 @@ As an example for when leaving an IPH disabled by default could be helpful,
imagine that your feature uses one main feature flag in addition to multiple IPH
feature flags. You can still check in all the configuration locally, but leave
the main feature flag and the IPHs off by default. This enables you to use a
-field trial to turn the IPHs on at the same time as your your main feature flag.
-This could potentially help some features in two ways:
+field trial to turn the IPHs on at the same time as your main feature flag. This
+could potentially help some features in two ways:
1. They do not need to guard invocations of `ShouldTriggerHelpUI(...)` for each
IPH with their main feature flag, possibly leading to simpler code if the
diff --git a/chromium/components/feature_engagement/internal/BUILD.gn b/chromium/components/feature_engagement/internal/BUILD.gn
index 1aedb7269e3..8c1cdc1048b 100644
--- a/chromium/components/feature_engagement/internal/BUILD.gn
+++ b/chromium/components/feature_engagement/internal/BUILD.gn
@@ -133,6 +133,8 @@ if (is_android) {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/feature_engagement/public:public_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//ui/android:ui_java",
diff --git a/chromium/components/feature_engagement/internal/chrome_variations_configuration.cc b/chromium/components/feature_engagement/internal/chrome_variations_configuration.cc
index a931216e969..0b2d8bd27d9 100644
--- a/chromium/components/feature_engagement/internal/chrome_variations_configuration.cc
+++ b/chromium/components/feature_engagement/internal/chrome_variations_configuration.cc
@@ -81,7 +81,7 @@ bool ParseComparatorSubstring(const base::StringPiece& definition,
bool ParseComparator(const base::StringPiece& definition,
Comparator* comparator) {
- if (base::LowerCaseEqualsASCII(definition, kComparatorTypeAny)) {
+ if (base::EqualsCaseInsensitiveASCII(definition, kComparatorTypeAny)) {
comparator->type = ANY;
comparator->value = 0;
return true;
@@ -149,7 +149,7 @@ bool ParseEventConfig(const base::StringPiece& definition,
const base::StringPiece& value = pair[1];
// TODO(nyquist): Ensure that key matches regex /^[a-zA-Z0-9-_]+$/.
- if (base::LowerCaseEqualsASCII(key, kEventConfigDataNameKey)) {
+ if (base::EqualsCaseInsensitiveASCII(key, kEventConfigDataNameKey)) {
if (has_name) {
*event_config = EventConfig();
return false;
@@ -157,7 +157,8 @@ bool ParseEventConfig(const base::StringPiece& definition,
has_name = true;
event_config->name = std::string(value);
- } else if (base::LowerCaseEqualsASCII(key, kEventConfigDataComparatorKey)) {
+ } else if (base::EqualsCaseInsensitiveASCII(
+ key, kEventConfigDataComparatorKey)) {
if (has_comparator) {
*event_config = EventConfig();
return false;
@@ -171,7 +172,8 @@ bool ParseEventConfig(const base::StringPiece& definition,
}
event_config->comparator = comparator;
- } else if (base::LowerCaseEqualsASCII(key, kEventConfigDataWindowKey)) {
+ } else if (base::EqualsCaseInsensitiveASCII(key,
+ kEventConfigDataWindowKey)) {
if (has_window) {
*event_config = EventConfig();
return false;
@@ -185,7 +187,8 @@ bool ParseEventConfig(const base::StringPiece& definition,
}
event_config->window = parsed_value;
- } else if (base::LowerCaseEqualsASCII(key, kEventConfigDataStorageKey)) {
+ } else if (base::EqualsCaseInsensitiveASCII(key,
+ kEventConfigDataStorageKey)) {
if (has_storage) {
*event_config = EventConfig();
return false;
@@ -224,12 +227,13 @@ bool ParseSessionRateImpact(const base::StringPiece& definition,
if (trimmed_def.length() == 0)
return false;
- if (base::LowerCaseEqualsASCII(trimmed_def, kImpactedFeaturesTypeAll)) {
+ if (base::EqualsCaseInsensitiveASCII(trimmed_def, kImpactedFeaturesTypeAll)) {
session_rate_impact->type = SessionRateImpact::Type::ALL;
return true;
}
- if (base::LowerCaseEqualsASCII(trimmed_def, kImpactedFeaturesTypeNone)) {
+ if (base::EqualsCaseInsensitiveASCII(trimmed_def,
+ kImpactedFeaturesTypeNone)) {
session_rate_impact->type = SessionRateImpact::Type::NONE;
return true;
}
@@ -246,8 +250,10 @@ bool ParseSessionRateImpact(const base::StringPiece& definition,
<< "for feature " << this_feature->name;
continue;
}
- if (base::LowerCaseEqualsASCII(feature_name, kImpactedFeaturesTypeAll) ||
- base::LowerCaseEqualsASCII(feature_name, kImpactedFeaturesTypeNone)) {
+ if (base::EqualsCaseInsensitiveASCII(feature_name,
+ kImpactedFeaturesTypeAll) ||
+ base::EqualsCaseInsensitiveASCII(feature_name,
+ kImpactedFeaturesTypeNone)) {
DVLOG(1) << "Illegal feature name when parsing session_rate_impact "
<< "for feature " << this_feature->name << ": " << feature_name;
return false;
@@ -281,12 +287,13 @@ bool ParseBlockedBy(const base::StringPiece& definition,
if (trimmed_def.length() == 0)
return false;
- if (base::LowerCaseEqualsASCII(trimmed_def, kImpactedFeaturesTypeAll)) {
+ if (base::EqualsCaseInsensitiveASCII(trimmed_def, kImpactedFeaturesTypeAll)) {
blocked_by->type = BlockedBy::Type::ALL;
return true;
}
- if (base::LowerCaseEqualsASCII(trimmed_def, kImpactedFeaturesTypeNone)) {
+ if (base::EqualsCaseInsensitiveASCII(trimmed_def,
+ kImpactedFeaturesTypeNone)) {
blocked_by->type = BlockedBy::Type::NONE;
return true;
}
@@ -303,8 +310,10 @@ bool ParseBlockedBy(const base::StringPiece& definition,
<< "for feature " << this_feature->name;
continue;
}
- if (base::LowerCaseEqualsASCII(feature_name, kImpactedFeaturesTypeAll) ||
- base::LowerCaseEqualsASCII(feature_name, kImpactedFeaturesTypeNone)) {
+ if (base::EqualsCaseInsensitiveASCII(feature_name,
+ kImpactedFeaturesTypeAll) ||
+ base::EqualsCaseInsensitiveASCII(feature_name,
+ kImpactedFeaturesTypeNone)) {
DVLOG(1) << "Illegal feature name when parsing blocked_by "
<< "for feature " << this_feature->name << ": " << feature_name;
return false;
@@ -334,12 +343,13 @@ bool ParseBlocking(const base::StringPiece& definition, Blocking* blocking) {
if (trimmed_def.length() == 0)
return false;
- if (base::LowerCaseEqualsASCII(trimmed_def, kImpactedFeaturesTypeAll)) {
+ if (base::EqualsCaseInsensitiveASCII(trimmed_def, kImpactedFeaturesTypeAll)) {
blocking->type = Blocking::Type::ALL;
return true;
}
- if (base::LowerCaseEqualsASCII(trimmed_def, kImpactedFeaturesTypeNone)) {
+ if (base::EqualsCaseInsensitiveASCII(trimmed_def,
+ kImpactedFeaturesTypeNone)) {
blocking->type = Blocking::Type::NONE;
return true;
}
@@ -365,7 +375,7 @@ bool ParseSnoozeParams(const base::StringPiece& definition,
const base::StringPiece& key = pair[0];
const base::StringPiece& value = pair[1];
- if (base::LowerCaseEqualsASCII(key, kSnoozeParamsMaxLimit)) {
+ if (base::EqualsCaseInsensitiveASCII(key, kSnoozeParamsMaxLimit)) {
uint32_t parsed_value;
if (!base::StringToUint(value, &parsed_value)) {
snooze_params->snooze_interval = 0u;
@@ -373,7 +383,7 @@ bool ParseSnoozeParams(const base::StringPiece& definition,
}
snooze_params->max_limit = parsed_value;
has_max_limit = true;
- } else if (base::LowerCaseEqualsASCII(key, kSnoozeParamsInterval)) {
+ } else if (base::EqualsCaseInsensitiveASCII(key, kSnoozeParamsInterval)) {
uint32_t parsed_value;
if (!base::StringToUint(value, &parsed_value)) {
snooze_params->max_limit = 0u;
@@ -395,12 +405,12 @@ bool ParseTrackingOnly(const base::StringPiece& definition,
base::StringPiece trimmed_def =
base::TrimWhitespaceASCII(definition, base::TRIM_ALL);
- if (base::LowerCaseEqualsASCII(trimmed_def, kTrackingOnlyTrue)) {
+ if (base::EqualsCaseInsensitiveASCII(trimmed_def, kTrackingOnlyTrue)) {
*tracking_only = true;
return true;
}
- return base::LowerCaseEqualsASCII(trimmed_def, kTrackingOnlyFalse);
+ return base::EqualsCaseInsensitiveASCII(trimmed_def, kTrackingOnlyFalse);
}
} // namespace
diff --git a/chromium/components/feature_engagement/internal/system_time_provider.cc b/chromium/components/feature_engagement/internal/system_time_provider.cc
index 8e6d83d88ed..da5654f6b84 100644
--- a/chromium/components/feature_engagement/internal/system_time_provider.cc
+++ b/chromium/components/feature_engagement/internal/system_time_provider.cc
@@ -4,6 +4,7 @@
#include "components/feature_engagement/internal/system_time_provider.h"
+#include "base/numerics/clamped_math.h"
#include "base/time/time.h"
namespace feature_engagement {
diff --git a/chromium/components/feature_engagement/public/BUILD.gn b/chromium/components/feature_engagement/public/BUILD.gn
index 8b523191bd7..0814a05bb70 100644
--- a/chromium/components/feature_engagement/public/BUILD.gn
+++ b/chromium/components/feature_engagement/public/BUILD.gn
@@ -68,6 +68,7 @@ if (is_android) {
deps = [
":jni_headers",
"//base:base_java",
+ "//base:jni_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/chromium/components/feature_engagement/public/event_constants.cc b/chromium/components/feature_engagement/public/event_constants.cc
index 9598c4f0e4b..94bc287358e 100644
--- a/chromium/components/feature_engagement/public/event_constants.cc
+++ b/chromium/components/feature_engagement/public/event_constants.cc
@@ -38,9 +38,6 @@ const char kWebUITabStripOpened[] = "webui_tab_strip_opened";
const char kDesktopPwaInstalled[] = "desktop_pwa_installed";
-const char kUpdatedConnectionSecurityIndicatorDisplayed[] =
- "updated_connection_security_indicator_displayed";
-
const char kFocusHelpBubbleAcceleratorPressed[] =
"focus_help_bubble_accelerator_pressed";
diff --git a/chromium/components/feature_engagement/public/event_constants.h b/chromium/components/feature_engagement/public/event_constants.h
index 546e945d217..c7a23e83729 100644
--- a/chromium/components/feature_engagement/public/event_constants.h
+++ b/chromium/components/feature_engagement/public/event_constants.h
@@ -65,9 +65,6 @@ extern const char kWebUITabStripOpened[];
// The PWA was installed by the user.
extern const char kDesktopPwaInstalled[];
-// Omnibox displayed the updated connection security indicator.
-extern const char kUpdatedConnectionSecurityIndicatorDisplayed[];
-
// The user entered the special "focus help bubble" accelerator.
extern const char kFocusHelpBubbleAcceleratorPressed[];
@@ -94,7 +91,7 @@ extern const char kViewedReadingList[];
// The user has triggered the translate infobar manually.
extern const char kTriggeredTranslateInfobar[];
-// The user has viewed the the BottomToolbar tip.
+// The user has viewed the BottomToolbar tip.
extern const char kBottomToolbarOpened[];
// The Discover feed has loaded content in the NTP.
diff --git a/chromium/components/feature_engagement/public/feature_configurations.cc b/chromium/components/feature_engagement/public/feature_configurations.cc
index f9bfc199230..5af929179d5 100644
--- a/chromium/components/feature_engagement/public/feature_configurations.cc
+++ b/chromium/components/feature_engagement/public/feature_configurations.cc
@@ -214,6 +214,23 @@ absl::optional<FeatureConfig> GetClientSideFeatureConfig(
Comparator(LESS_THAN, 1), 1, 360));
return config;
}
+ if (kIPHContextualPageActionsPriceTrackingFeature.name == feature->name) {
+ // A config that allows the Price Tracking IPH to be shown:
+ // * Once per day. 3 times max in 90 days
+ absl::optional<FeatureConfig> config = FeatureConfig();
+ config->valid = true;
+ config->availability = Comparator(ANY, 0);
+ config->session_rate = Comparator(EQUAL, 0);
+ config->trigger =
+ EventConfig("contextual_page_actions_price_tracking_iph_trigger",
+ Comparator(LESS_THAN, 1), 1, 360);
+ config->used = EventConfig("contextual_page_actions_price_tracking_used",
+ Comparator(EQUAL, 0), 90, 360);
+ config->event_configs.insert(
+ EventConfig("contextual_page_actions_price_tracking_iph_trigger",
+ Comparator(LESS_THAN, 3), 90, 360));
+ return config;
+ }
if (kIPHAddToHomescreenMessageFeature.name == feature->name) {
// A config that allows the Add to homescreen message IPH to be shown:
// * Once per 15 days
@@ -460,6 +477,29 @@ absl::optional<FeatureConfig> GetClientSideFeatureConfig(
k10YearsInDays, k10YearsInDays);
return config;
}
+
+ if (kIPHWebFeedAwarenessFeature.name == feature->name) {
+ // A config that allows the web feed IPH to be shown up to three times
+ // total, no more than once per session.
+ absl::optional<FeatureConfig> config = FeatureConfig();
+ config->valid = true;
+ config->availability = Comparator(ANY, 0);
+
+ config->session_rate = Comparator(LESS_THAN, 1);
+ SessionRateImpact session_rate_impact;
+ session_rate_impact.type = SessionRateImpact::Type::ALL;
+ config->session_rate_impact = session_rate_impact;
+
+ // Keep the IPH trigger event for 10 years, which is a relatively long time
+ // period that we could consider as being "forever".
+ config->trigger =
+ EventConfig("iph_web_feed_awareness_triggered",
+ Comparator(LESS_THAN, 3), k10YearsInDays, k10YearsInDays);
+ config->used = EventConfig("web_feed_awareness_used", Comparator(ANY, 0),
+ k10YearsInDays, k10YearsInDays);
+ return config;
+ }
+
if (kIPHFeedSwipeRefresh.name == feature->name) {
// A config that allows the feed swipe refresh message IPH to be shown:
// * Once per 15 days
diff --git a/chromium/components/feature_engagement/public/feature_constants.cc b/chromium/components/feature_engagement/public/feature_constants.cc
index 034172e2363..d0a13bec40e 100644
--- a/chromium/components/feature_engagement/public/feature_constants.cc
+++ b/chromium/components/feature_engagement/public/feature_constants.cc
@@ -14,6 +14,7 @@ const base::Feature kEnableAutomaticSnooze{"EnableAutomaticSnooze",
const base::Feature kIPHDemoMode{"IPH_DemoMode",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHSnooze{"IPH_Snooze", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kEnableIPH{"EnableIPH", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kUseClientConfigIPH{"UseClientConfigIPH",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -83,6 +84,11 @@ const base::Feature kIPHAutoDarkUserEducationMessageFeature{
"IPH_AutoDarkUserEducationMessage", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kIPHAutoDarkUserEducationMessageOptInFeature{
"IPH_AutoDarkUserEducationMessageOptIn", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kIPHContextualPageActionsPriceTrackingFeature{
+ "IPH_ContextualPageActions_PriceTracking",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kIPHCrowFeature{"IPH_Crow",
+ base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHDataSaverDetailFeature{
"IPH_DataSaverDetail", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kIPHDataSaverMilestonePromoFeature{
@@ -208,7 +214,7 @@ const base::Feature kIPHPageInfoStoreInfoFeature{
const base::Feature kIPHPreviewsOmniboxUIFeature{
"IPH_PreviewsOmniboxUI", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kIPHShoppingListMenuItemFeature{
- "IPH_ShoppingListMenuItem", base::FEATURE_DISABLED_BY_DEFAULT};
+ "IPH_ShoppingListMenuItem", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kIPHTabGroupsQuicklyComparePagesFeature{
"IPH_TabGroupsQuicklyComparePages", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHTabGroupsTapToSeeAnotherTabFeature{
@@ -237,6 +243,8 @@ const base::Feature kIPHExploreSitesTileFeature{
"IPH_ExploreSitesTile", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHFeedHeaderMenuFeature{"IPH_FeedHeaderMenu",
base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kIPHWebFeedAwarenessFeature{
+ "IPH_WebFeedAwareness", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kIPHFeedSwipeRefresh{"IPH_FeedSwipeRefresh",
base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kIPHChromeReengagementNotification1Feature{
@@ -286,15 +294,14 @@ const base::Feature kIPHDefaultSiteViewFeature{
"IPH_DefaultSiteView", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHPasswordSuggestionsFeature{
"IPH_PasswordSuggestions", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHFollowWhileBrowsingFeature{
+ "IPH_FollowWhileBrowsing", base::FEATURE_DISABLED_BY_DEFAULT};
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
const base::Feature kIPHAutofillVirtualCardSuggestionFeature{
"IPH_AutofillVirtualCardSuggestion", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kIPHUpdatedConnectionSecurityIndicatorsFeature{
- "IPH_UpdatedConnectionSecurityIndicators",
- base::FEATURE_DISABLED_BY_DEFAULT};
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
// BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) ||
// BUILDFLAG(IS_FUCHSIA)
diff --git a/chromium/components/feature_engagement/public/feature_constants.h b/chromium/components/feature_engagement/public/feature_constants.h
index fa255f73dff..6dc3e390fe9 100644
--- a/chromium/components/feature_engagement/public/feature_constants.h
+++ b/chromium/components/feature_engagement/public/feature_constants.h
@@ -29,6 +29,8 @@ extern const base::Feature kUseClientConfigIPH;
// A feature to ensure all arrays can contain at least one feature.
extern const base::Feature kIPHDummyFeature;
+extern const base::Feature kEnableIPH;
+
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
extern const base::Feature kIPHDesktopSharedHighlightingFeature;
@@ -49,7 +51,6 @@ extern const base::Feature kIPHWebUITabStripFeature;
extern const base::Feature kIPHDesktopSnoozeFeature;
extern const base::Feature kIPHDesktopPwaInstallFeature;
extern const base::Feature kIPHProfileSwitchFeature;
-extern const base::Feature kIPHUpdatedConnectionSecurityIndicatorsFeature;
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
// BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
@@ -68,6 +69,8 @@ extern const base::Feature kIPHAddToHomescreenTextBubbleFeature;
extern const base::Feature kIPHAutoDarkOptOutFeature;
extern const base::Feature kIPHAutoDarkUserEducationMessageFeature;
extern const base::Feature kIPHAutoDarkUserEducationMessageOptInFeature;
+extern const base::Feature kIPHContextualPageActionsPriceTrackingFeature;
+extern const base::Feature kIPHCrowFeature;
extern const base::Feature kIPHDataSaverDetailFeature;
extern const base::Feature kIPHDataSaverMilestonePromoFeature;
extern const base::Feature kIPHDataSaverPreviewFeature;
@@ -147,6 +150,7 @@ extern const base::Feature kIPHVideoTutorialNTPSummaryFeature;
extern const base::Feature kIPHVideoTutorialTryNowFeature;
extern const base::Feature kIPHExploreSitesTileFeature;
extern const base::Feature kIPHFeedHeaderMenuFeature;
+extern const base::Feature kIPHWebFeedAwarenessFeature;
extern const base::Feature kIPHFeedSwipeRefresh;
extern const base::Feature kIPHChromeReengagementNotification1Feature;
extern const base::Feature kIPHChromeReengagementNotification2Feature;
@@ -173,12 +177,12 @@ extern const base::Feature kIPHBadgedTranslateManualTriggerFeature;
extern const base::Feature kIPHDiscoverFeedHeaderFeature;
extern const base::Feature kIPHDefaultSiteViewFeature;
extern const base::Feature kIPHPasswordSuggestionsFeature;
+extern const base::Feature kIPHFollowWhileBrowsingFeature;
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
extern const base::Feature kIPHAutofillVirtualCardSuggestionFeature;
-extern const base::Feature kIPHUpdatedConnectionSecurityIndicatorsFeature;
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
// BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) ||
// BUILDFLAG(IS_FUCHSIA)
diff --git a/chromium/components/feature_engagement/public/feature_list.cc b/chromium/components/feature_engagement/public/feature_list.cc
index e49b73c0d79..ab905b12f67 100644
--- a/chromium/components/feature_engagement/public/feature_list.cc
+++ b/chromium/components/feature_engagement/public/feature_list.cc
@@ -36,6 +36,7 @@ const base::Feature* const kAllFeatures[] = {
&kIPHChromeReengagementNotification1Feature,
&kIPHChromeReengagementNotification2Feature,
&kIPHChromeReengagementNotification3Feature,
+ &kIPHContextualPageActionsPriceTrackingFeature,
&kIPHContextualSearchTranslationEnableFeature,
&kIPHContextualSearchWebSearchFeature,
&kIPHContextualSearchPromoteTapFeature,
@@ -43,6 +44,7 @@ const base::Feature* const kAllFeatures[] = {
&kIPHContextualSearchOptInFeature,
&kIPHContextualSearchTappedButShouldLongpressFeature,
&kIPHContextualSearchInPanelHelpFeature,
+ &kIPHCrowFeature,
&kIPHDownloadSettingsFeature,
&kIPHDownloadInfoBarDownloadContinuingFeature,
&kIPHDownloadInfoBarDownloadsAreFasterFeature,
@@ -96,6 +98,7 @@ const base::Feature* const kAllFeatures[] = {
&kIPHVideoTutorialTryNowFeature,
&kIPHExploreSitesTileFeature,
&kIPHFeedHeaderMenuFeature,
+ &kIPHWebFeedAwarenessFeature,
&kIPHFeedSwipeRefresh,
&kIPHShareScreenshotFeature,
&kIPHSharingHubLinkToggleFeature,
@@ -104,7 +107,6 @@ const base::Feature* const kAllFeatures[] = {
&kIPHSharedHighlightingBuilder,
&kIPHSharedHighlightingReceiverFeature,
&kIPHStartSurfaceTabSwitcherHomeButton,
- &kIPHUpdatedConnectionSecurityIndicatorsFeature,
&kIPHSharingHubWebnotesStylizeFeature,
#endif // BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_IOS)
@@ -118,6 +120,7 @@ const base::Feature* const kAllFeatures[] = {
&kIPHDiscoverFeedHeaderFeature,
&kIPHDefaultSiteViewFeature,
&kIPHPasswordSuggestionsFeature,
+ &kIPHFollowWhileBrowsingFeature,
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
@@ -136,7 +139,6 @@ const base::Feature* const kAllFeatures[] = {
&kIPHWebUITabStripFeature,
&kIPHDesktopPwaInstallFeature,
&kIPHProfileSwitchFeature,
- &kIPHUpdatedConnectionSecurityIndicatorsFeature,
&kIPHDesktopSharedHighlightingFeature,
&kIPHIntentChipFeature,
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ||
diff --git a/chromium/components/feature_engagement/public/feature_list.h b/chromium/components/feature_engagement/public/feature_list.h
index 4cf31930470..83105819f3c 100644
--- a/chromium/components/feature_engagement/public/feature_list.h
+++ b/chromium/components/feature_engagement/public/feature_list.h
@@ -61,6 +61,9 @@ DEFINE_VARIATION_PARAM(kIPHAutoDarkUserEducationMessageFeature,
"IPH_AutoDarkUserEducationMessage");
DEFINE_VARIATION_PARAM(kIPHAutoDarkUserEducationMessageOptInFeature,
"IPH_AutoDarkUserEducationMessageOptIn");
+DEFINE_VARIATION_PARAM(kIPHContextualPageActionsPriceTrackingFeature,
+ "IPH_ContextualPageActions_PriceTracking");
+DEFINE_VARIATION_PARAM(kIPHCrowFeature, "IPH_Crow");
DEFINE_VARIATION_PARAM(kIPHDataSaverDetailFeature, "IPH_DataSaverDetail");
DEFINE_VARIATION_PARAM(kIPHDataSaverMilestonePromoFeature,
"IPH_DataSaverMilestonePromo");
@@ -187,6 +190,7 @@ DEFINE_VARIATION_PARAM(kIPHVideoTutorialTryNowFeature,
"IPH_VideoTutorial_TryNow");
DEFINE_VARIATION_PARAM(kIPHExploreSitesTileFeature, "IPH_ExploreSitesTile");
DEFINE_VARIATION_PARAM(kIPHFeedHeaderMenuFeature, "IPH_FeedHeaderMenu");
+DEFINE_VARIATION_PARAM(kIPHWebFeedAwarenessFeature, "IPH_WebFeedAwareness");
DEFINE_VARIATION_PARAM(kIPHFeedSwipeRefresh, "IPH_FeedSwipeRefresh");
DEFINE_VARIATION_PARAM(kIPHShareScreenshotFeature, "IPH_ShareScreenshot");
DEFINE_VARIATION_PARAM(kIPHSharingHubLinkToggleFeature,
@@ -200,8 +204,6 @@ DEFINE_VARIATION_PARAM(kIPHSharedHighlightingReceiverFeature,
"IPH_SharedHighlightingReceiver");
DEFINE_VARIATION_PARAM(kIPHStartSurfaceTabSwitcherHomeButton,
"IPH_StartSurfaceTabSwitcherHomeButton");
-DEFINE_VARIATION_PARAM(kIPHUpdatedConnectionSecurityIndicatorsFeature,
- "IPH_UpdatedConnectionSecurityIndicators");
DEFINE_VARIATION_PARAM(kIPHSharingHubWebnotesStylizeFeature,
"IPH_SharingHubWebnotesStylize");
#endif // BUILDFLAG(IS_ANDROID)
@@ -221,6 +223,8 @@ DEFINE_VARIATION_PARAM(kIPHDiscoverFeedHeaderFeature,
DEFINE_VARIATION_PARAM(kIPHDefaultSiteViewFeature, "IPH_DefaultSiteView");
DEFINE_VARIATION_PARAM(kIPHPasswordSuggestionsFeature,
"IPH_PasswordSuggestions");
+DEFINE_VARIATION_PARAM(kIPHFollowWhileBrowsingFeature,
+ "IPH_FollowWhileBrowsing");
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
@@ -246,8 +250,6 @@ DEFINE_VARIATION_PARAM(kIPHTabSearchFeature, "IPH_TabSearch");
DEFINE_VARIATION_PARAM(kIPHWebUITabStripFeature, "IPH_WebUITabStrip");
DEFINE_VARIATION_PARAM(kIPHDesktopPwaInstallFeature, "IPH_DesktopPwaInstall");
DEFINE_VARIATION_PARAM(kIPHProfileSwitchFeature, "IPH_ProfileSwitch");
-DEFINE_VARIATION_PARAM(kIPHUpdatedConnectionSecurityIndicatorsFeature,
- "IPH_UpdatedConnectionSecurityIndicators");
DEFINE_VARIATION_PARAM(kIPHDesktopSharedHighlightingFeature,
"IPH_DesktopSharedHighlighting");
DEFINE_VARIATION_PARAM(kIPHIntentChipFeature, "IPH_IntentChip");
@@ -281,6 +283,8 @@ constexpr flags_ui::FeatureEntry::FeatureVariation
VARIATION_ENTRY(kIPHAutoDarkOptOutFeature),
VARIATION_ENTRY(kIPHAutoDarkUserEducationMessageFeature),
VARIATION_ENTRY(kIPHAutoDarkUserEducationMessageOptInFeature),
+ VARIATION_ENTRY(kIPHContextualPageActionsPriceTrackingFeature),
+ VARIATION_ENTRY(kIPHCrowFeature),
VARIATION_ENTRY(kIPHDataSaverDetailFeature),
VARIATION_ENTRY(kIPHDataSaverMilestonePromoFeature),
VARIATION_ENTRY(kIPHDataSaverPreviewFeature),
@@ -346,7 +350,6 @@ constexpr flags_ui::FeatureEntry::FeatureVariation
VARIATION_ENTRY(kIPHWebFeedPostFollowDialogFeature),
VARIATION_ENTRY(kIPHSharedHighlightingBuilder),
VARIATION_ENTRY(kIPHSharedHighlightingReceiverFeature),
- VARIATION_ENTRY(kIPHUpdatedConnectionSecurityIndicatorsFeature),
VARIATION_ENTRY(kIPHSharingHubWebnotesStylizeFeature),
#elif BUILDFLAG(IS_IOS)
VARIATION_ENTRY(kIPHBottomToolbarTipFeature),
@@ -359,6 +362,7 @@ constexpr flags_ui::FeatureEntry::FeatureVariation
VARIATION_ENTRY(kIPHDiscoverFeedHeaderFeature),
VARIATION_ENTRY(kIPHDefaultSiteViewFeature),
VARIATION_ENTRY(kIPHPasswordSuggestionsFeature),
+ VARIATION_ENTRY(kIPHFollowWhileBrowsingFeature),
#elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
VARIATION_ENTRY(kIPHDesktopTabGroupsNewGroupFeature),
@@ -377,7 +381,6 @@ constexpr flags_ui::FeatureEntry::FeatureVariation
VARIATION_ENTRY(kIPHWebUITabStripFeature),
VARIATION_ENTRY(kIPHDesktopPwaInstallFeature),
VARIATION_ENTRY(kIPHProfileSwitchFeature),
- VARIATION_ENTRY(kIPHUpdatedConnectionSecurityIndicatorsFeature),
VARIATION_ENTRY(kIPHDesktopSharedHighlightingFeature),
VARIATION_ENTRY(kIPHIntentChipFeature),
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
diff --git a/chromium/components/feed/content/renderer/rss_link_reader.cc b/chromium/components/feed/content/renderer/rss_link_reader.cc
index 42c18fbdf85..338e73d5981 100644
--- a/chromium/components/feed/content/renderer/rss_link_reader.cc
+++ b/chromium/components/feed/content/renderer/rss_link_reader.cc
@@ -37,14 +37,14 @@ GURL GetRssUrlFromLinkElement(const blink::WebElement& link_element) {
if (!web_type.ContainsOnlyASCII() || !web_rel.ContainsOnlyASCII())
return GURL();
std::string type = web_type.Ascii();
- if (!(base::LowerCaseEqualsASCII(type, "application/rss+xml") ||
- base::LowerCaseEqualsASCII(type, "application/rss+atom") ||
- base::LowerCaseEqualsASCII(type, "application/atom+xml"))) {
+ if (!(base::EqualsCaseInsensitiveASCII(type, "application/rss+xml") ||
+ base::EqualsCaseInsensitiveASCII(type, "application/rss+atom") ||
+ base::EqualsCaseInsensitiveASCII(type, "application/atom+xml"))) {
return GURL();
}
std::string rel = web_rel.Ascii();
- if (!(base::LowerCaseEqualsASCII(rel, "alternate") ||
- base::LowerCaseEqualsASCII(rel, "service.feed"))) {
+ if (!(base::EqualsCaseInsensitiveASCII(rel, "alternate") ||
+ base::EqualsCaseInsensitiveASCII(rel, "service.feed"))) {
return GURL();
}
blink::WebURL url =
diff --git a/chromium/components/feed/core/common/pref_names.cc b/chromium/components/feed/core/common/pref_names.cc
index 95b0f47893f..396c35a5304 100644
--- a/chromium/components/feed/core/common/pref_names.cc
+++ b/chromium/components/feed/core/common/pref_names.cc
@@ -45,8 +45,8 @@ const char kReliabilityLoggingIdSalt[] = "feedv2.reliability_logging_id_salt";
const char kHasStoredData[] = "feedv2.has_stored_data";
const char kWebFeedContentOrder[] = "webfeed.content_order";
const char kLastSeenFeedType[] = "feedv2.last_seen_feed_type";
-const char kNoticeStates[] = "feed.notice_states";
const char kFeedOnDeviceUserActionsCollector[] = "feed.user_actions_collection";
+const char kInfoCardStates[] = "feed.info_card_states";
} // namespace prefs
@@ -89,9 +89,9 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(feed::prefs::kHasStoredData, false);
registry->RegisterIntegerPref(feed::prefs::kWebFeedContentOrder, 0);
registry->RegisterIntegerPref(feed::prefs::kLastSeenFeedType, 0);
- registry->RegisterDictionaryPref(feed::prefs::kNoticeStates, 0);
registry->RegisterListPref(feed::prefs::kFeedOnDeviceUserActionsCollector,
PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(feed::prefs::kInfoCardStates, 0);
#if BUILDFLAG(IS_IOS)
registry->RegisterBooleanPref(feed::prefs::kLastFetchHadLoggingEnabled,
diff --git a/chromium/components/feed/core/common/pref_names.h b/chromium/components/feed/core/common/pref_names.h
index e82eb59025a..68b2e22ee81 100644
--- a/chromium/components/feed/core/common/pref_names.h
+++ b/chromium/components/feed/core/common/pref_names.h
@@ -79,11 +79,11 @@ extern const char kHasStoredData[];
extern const char kWebFeedContentOrder[];
// The last feed type that the user was viewing.
extern const char kLastSeenFeedType[];
-// The pref name for the keys of the notices.
-extern const char kNoticeStates[];
// The pref name for storing user actions. Used for personalizing feed for
// unsigned users. The list is sorted by ascenting time stamp.
extern const char kFeedOnDeviceUserActionsCollector[];
+// The pref name for the keys of the info cards.
+extern const char kInfoCardStates[];
} // namespace prefs
diff --git a/chromium/components/feed/core/proto/BUILD.gn b/chromium/components/feed/core/proto/BUILD.gn
index 2b1d9bce0c8..59e042d283d 100644
--- a/chromium/components/feed/core/proto/BUILD.gn
+++ b/chromium/components/feed/core/proto/BUILD.gn
@@ -42,6 +42,7 @@ proto_library("proto_v2") {
"v2/wire/feed_query.proto",
"v2/wire/feed_request.proto",
"v2/wire/feed_response.proto",
+ "v2/wire/info_card.proto",
"v2/wire/next_page_token.proto",
"v2/wire/payload_metadata.proto",
"v2/wire/reliability_logging_enums.proto",
diff --git a/chromium/components/feed/core/proto/v2/store.proto b/chromium/components/feed/core/proto/v2/store.proto
index 67ad56da581..3582cf80fe5 100644
--- a/chromium/components/feed/core/proto/v2/store.proto
+++ b/chromium/components/feed/core/proto/v2/store.proto
@@ -70,6 +70,9 @@ message StreamData {
repeated int64 content_ids = 11;
// Root EventID provided by the server.
bytes root_event_id = 12;
+ // The unix timestamp in milliseconds that the feed response is produced on
+ // the server. This is returned from the server and based on server clock.
+ int64 last_server_response_time_millis = 15;
reserved 3, 5;
}
diff --git a/chromium/components/feed/core/proto/v2/wire/capability.proto b/chromium/components/feed/core/proto/v2/wire/capability.proto
index b94df7421ac..15200d9ea37 100644
--- a/chromium/components/feed/core/proto/v2/wire/capability.proto
+++ b/chromium/components/feed/core/proto/v2/wire/capability.proto
@@ -37,4 +37,7 @@ enum Capability {
OPEN_IN_INCOGNITO = 67;
ON_DEVICE_USER_PROFILE = 70;
INVALIDATE_CACHE_COMMAND = 73;
+ INFO_CARD_ACKNOWLEDGEMENT_TRACKING = 77;
+ THANK_CREATOR = 79;
+ OPEN_IN_NEW_TAB_IN_GROUP = 81;
}
diff --git a/chromium/components/feed/core/proto/v2/wire/chrome_fulfillment_info.proto b/chromium/components/feed/core/proto/v2/wire/chrome_fulfillment_info.proto
index 31fc3756e32..321b0a16b2b 100644
--- a/chromium/components/feed/core/proto/v2/wire/chrome_fulfillment_info.proto
+++ b/chromium/components/feed/core/proto/v2/wire/chrome_fulfillment_info.proto
@@ -6,9 +6,12 @@ syntax = "proto2";
package feedwire;
+import "components/feed/core/proto/v2/wire/info_card.proto";
+
option optimize_for = LITE_RUNTIME;
message ChromeFulfillmentInfo {
optional bool notice_card_acknowledged = 1;
repeated string acknowledged_notice_key = 2;
+ repeated InfoCardTrackingState info_card_tracking_state = 3;
}
diff --git a/chromium/components/feed/core/proto/v2/wire/client_info.proto b/chromium/components/feed/core/proto/v2/wire/client_info.proto
index a2036390a01..fb774c082cd 100644
--- a/chromium/components/feed/core/proto/v2/wire/client_info.proto
+++ b/chromium/components/feed/core/proto/v2/wire/client_info.proto
@@ -19,7 +19,9 @@ message ClientInfo {
ANDROID_ID = 1;
IOS = 2;
}
- enum AppType { CHROME_ANDROID = 3; }
+ enum AppType {
+ CHROME_ANDROID = 3;
+ }
optional PlatformType platform_type = 1;
optional Version platform_version = 2;
optional AppType app_type = 3;
diff --git a/chromium/components/feed/core/proto/v2/wire/client_user_profiles.proto b/chromium/components/feed/core/proto/v2/wire/client_user_profiles.proto
index 674dc6c81ff..30f33572af2 100644
--- a/chromium/components/feed/core/proto/v2/wire/client_user_profiles.proto
+++ b/chromium/components/feed/core/proto/v2/wire/client_user_profiles.proto
@@ -10,6 +10,7 @@ option optimize_for = LITE_RUNTIME;
message ClientUserProfiles {
optional DiscoverUserActionsProfile discover_user_actions_profile = 1;
+ optional ViewDemotionProfile view_demotion_profile = 2;
}
message ActionCounts {
message Counts {
@@ -26,6 +27,7 @@ message ActionCounts {
}
message DiscoverUserActionsProfile {
message ContentMediaXEntityActionCounts {
+ optional uint64 content_category_media_type = 1;
optional uint64 mid = 2;
repeated ActionCounts counts = 3;
}
@@ -37,3 +39,4 @@ message DiscoverUserActionsProfile {
repeated ContentMediaXEntityActionCounts content_media_x_entity = 2;
repeated CardCategoryXEntityActionCounts card_category_x_entity = 3;
}
+message ViewDemotionProfile {}
diff --git a/chromium/components/feed/core/proto/v2/wire/feed_query.proto b/chromium/components/feed/core/proto/v2/wire/feed_query.proto
index 4f0f021f24b..103e1b45724 100644
--- a/chromium/components/feed/core/proto/v2/wire/feed_query.proto
+++ b/chromium/components/feed/core/proto/v2/wire/feed_query.proto
@@ -13,11 +13,14 @@ import "components/feed/core/proto/v2/wire/token.proto";
option optimize_for = LITE_RUNTIME;
message FeedQuery {
- message Tokens { repeated Token tokens = 1; }
+ message Tokens {
+ repeated Token tokens = 1;
+ }
enum RequestReason {
UNKNOWN_REQUEST_REASON = 0;
MANUAL_REFRESH = 1;
SCHEDULED_REFRESH = 2;
+ APP_CLOSE_REFRESH = 13;
NEXT_PAGE_SCROLL = 3;
PREFETCHED_WEB_FEED = 8;
INTERACTIVE_WEB_FEED = 9;
diff --git a/chromium/components/feed/core/proto/v2/wire/feed_response.proto b/chromium/components/feed/core/proto/v2/wire/feed_response.proto
index c3324d43a30..40b90b332f1 100644
--- a/chromium/components/feed/core/proto/v2/wire/feed_response.proto
+++ b/chromium/components/feed/core/proto/v2/wire/feed_response.proto
@@ -10,6 +10,7 @@ import "components/feed/core/proto/v2/wire/chrome_feed_response_metadata.proto";
import "components/feed/core/proto/v2/wire/content_lifetime.proto";
import "components/feed/core/proto/v2/wire/data_operation.proto";
import "components/feed/core/proto/v2/wire/eventid.proto";
+import "components/feed/core/proto/v2/wire/info_card.proto";
import "components/feed/core/proto/v2/wire/server_experiment_data.proto";
option optimize_for = LITE_RUNTIME;
@@ -24,5 +25,6 @@ message FeedResponseMetadata {
optional bool pinned_content_fulfilled = 4;
optional ServerExperimentData server_experiment_data = 5;
optional ContentLifetime content_lifetime = 6;
+ optional InfoCardServingInfo info_card_serving_info = 7;
optional ChromeFeedResponseMetadata chrome_feed_response_metadata = 326233599;
}
diff --git a/chromium/components/feed/core/proto/v2/wire/info_card.proto b/chromium/components/feed/core/proto/v2/wire/info_card.proto
new file mode 100644
index 00000000000..81d8fb255bb
--- /dev/null
+++ b/chromium/components/feed/core/proto/v2/wire/info_card.proto
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+package feedwire;
+
+option optimize_for = LITE_RUNTIME;
+
+enum InfoCardType {
+ INFO_CARD_NONE = 0;
+ INFO_CARD_MAIN_PRIVACY_NOTICE = 1;
+ INFO_CARD_YOUTUBE_PRIVACY_NOTICE = 2;
+ INFO_CARD_FOLLOWING_FEED_ONBOARDING_INTRO = 3;
+ INFO_CARD_FOLLOWING_FEED_FOLLOW_FROM_PAGE_EDUCATION = 4;
+}
+message InfoCardTrackingState {
+ optional int32 type = 1;
+ optional int32 explicitly_dismissed_count = 2;
+ optional int32 view_count = 3;
+ optional int32 click_count = 4;
+ optional int64 first_view_timestamp = 5;
+ optional int64 last_view_timestamp = 6;
+}
+message InfoCardServingInfo {
+ repeated int32 fulfilled_info_card_types = 1;
+ repeated int64 known_info_card_types = 2;
+}
diff --git a/chromium/components/feed/core/proto/v2/wire/request_schedule.proto b/chromium/components/feed/core/proto/v2/wire/request_schedule.proto
index 2832031734f..9c80e76a85e 100644
--- a/chromium/components/feed/core/proto/v2/wire/request_schedule.proto
+++ b/chromium/components/feed/core/proto/v2/wire/request_schedule.proto
@@ -14,5 +14,7 @@ message RequestSchedule {
message TimeBasedSchedule {
repeated Duration refresh_time_from_response_time = 1;
}
- oneof schedule { TimeBasedSchedule time_based_schedule = 1; }
+ oneof schedule {
+ TimeBasedSchedule time_based_schedule = 1;
+ }
}
diff --git a/chromium/components/feed/core/proto/v2/wire/web_feeds.proto b/chromium/components/feed/core/proto/v2/wire/web_feeds.proto
index b404d74b246..0f108807c24 100644
--- a/chromium/components/feed/core/proto/v2/wire/web_feeds.proto
+++ b/chromium/components/feed/core/proto/v2/wire/web_feeds.proto
@@ -11,6 +11,14 @@ import "components/feed/core/proto/v2/wire/web_feed_matcher.proto";
option optimize_for = LITE_RUNTIME;
+enum WebFeedChangeReason {
+ WEB_FEED_CHANGE_REASON_UNSPECIFIED = 0;
+ WEB_PAGE_MENU = 1;
+ WEB_PAGE_ACCELERATOR = 2;
+ MANAGEMENT = 3;
+ IN_FEED_RECOMMENDATION = 4;
+ BACK_OF_CARD_UNFOLLOW = 5;
+}
message WebFeed {
enum State {
STATE_UNSPECIFIED = 0;
@@ -46,6 +54,7 @@ message FollowWebFeedRequest {
string canonical_uri = 5;
repeated string page_rss_uris = 3;
ConsistencyToken consistency_token = 4;
+ WebFeedChangeReason change_reason = 6;
}
message FollowWebFeedResponse {
WebFeed web_feed = 1;
@@ -54,6 +63,7 @@ message FollowWebFeedResponse {
message UnfollowWebFeedRequest {
string name = 1;
ConsistencyToken consistency_token = 2;
+ WebFeedChangeReason change_reason = 3;
}
message UnfollowWebFeedResponse {
ConsistencyToken consistency_token = 1;
diff --git a/chromium/components/feed/core/v2/BUILD.gn b/chromium/components/feed/core/v2/BUILD.gn
index ba68b7e6db4..35b95f1e361 100644
--- a/chromium/components/feed/core/v2/BUILD.gn
+++ b/chromium/components/feed/core/v2/BUILD.gn
@@ -72,8 +72,8 @@ source_set("feed_core_v2") {
"request_throttler.h",
"scheduling.cc",
"scheduling.h",
- "stream/notice_card_tracker.cc",
- "stream/notice_card_tracker.h",
+ "stream/info_card_tracker.cc",
+ "stream/info_card_tracker.h",
"stream/privacy_notice_card_tracker.cc",
"stream/privacy_notice_card_tracker.h",
"stream/unread_content_notifier.cc",
@@ -216,7 +216,7 @@ source_set("core_unit_tests") {
"public/public_types_unittest.cc",
"request_throttler_unittest.cc",
"scheduling_unittest.cc",
- "stream/notice_card_tracker_unittest.cc",
+ "stream/info_card_tracker_unittest.cc",
"stream/privacy_notice_card_tracker_unittest.cc",
"stream_model_unittest.cc",
"test/callback_receiver_unittest.cc",
diff --git a/chromium/components/feed/core/v2/algorithm_unittest.cc b/chromium/components/feed/core/v2/algorithm_unittest.cc
index 0fedc702012..ffaf09279af 100644
--- a/chromium/components/feed/core/v2/algorithm_unittest.cc
+++ b/chromium/components/feed/core/v2/algorithm_unittest.cc
@@ -7,6 +7,7 @@
#include <utility>
#include <vector>
+#include "base/memory/raw_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace feed {
class ResultAggregator {
@@ -19,7 +20,7 @@ class ResultAggregator {
}
private:
- std::vector<std::pair<int, int>>* results_;
+ raw_ptr<std::vector<std::pair<int, int>>> results_;
};
TEST(DiffSortedRange, LeftEmpty) {
diff --git a/chromium/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc b/chromium/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
index 5f1f9e7589f..706f572578e 100644
--- a/chromium/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
+++ b/chromium/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
@@ -314,7 +314,8 @@ TEST_F(FeedApiReliabilityLoggingTest, CacheRead_Stale) {
MakeTypicalInitialModelState(
/*first_cluster_id=*/0,
kTestTimeEpoch -
- GetFeedConfig().GetStalenessThreshold(kForYouStream) -
+ GetFeedConfig().GetStalenessThreshold(
+ kForYouStream, /*is_web_feed_subscriber=*/true) -
base::Minutes(1)),
base::DoNothing());
@@ -347,7 +348,8 @@ TEST_F(FeedApiReliabilityLoggingTest, CacheRead_StaleWithNetworkError) {
MakeTypicalInitialModelState(
/*first_cluster_id=*/0,
kTestTimeEpoch -
- GetFeedConfig().GetStalenessThreshold(kForYouStream) -
+ GetFeedConfig().GetStalenessThreshold(
+ kForYouStream, /*is_web_feed_subscriber=*/true) -
base::Minutes(1)),
base::DoNothing());
diff --git a/chromium/components/feed/core/v2/api_test/feed_api_stream_unittest.cc b/chromium/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
index 94d754dbde2..6c81dc319bf 100644
--- a/chromium/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
+++ b/chromium/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
@@ -11,6 +11,8 @@
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "components/feed/core/common/pref_names.h"
+#include "components/feed/core/proto/v2/wire/feed_query.pb.h"
+#include "components/feed/core/proto/v2/wire/info_card.pb.h"
#include "components/feed/core/shared_prefs/pref_names.h"
#include "components/feed/core/v2/api_test/feed_api_test.h"
#include "components/feed/core/v2/config.h"
@@ -24,7 +26,6 @@
#include "components/feed/core/v2/public/stream_type.h"
#include "components/feed/core/v2/public/types.h"
#include "components/feed/core/v2/scheduling.h"
-#include "components/feed/core/v2/stream/notice_card_tracker.h"
#include "components/feed/core/v2/test/callback_receiver.h"
#include "components/feed/core/v2/test/stream_builder.h"
#include "components/feed/feed_feature_list.h"
@@ -38,7 +39,9 @@ namespace feed {
namespace test {
namespace {
-const char kTestKey[] = "Youtube";
+const int kTestInfoCardType1 = 101;
+const int kTestInfoCardType2 = 8888;
+const int kMinimumViewIntervalSeconds = 5 * 60;
TEST_F(FeedApiTest, IsArticlesListVisibleByDefault) {
EXPECT_TRUE(stream_->IsArticlesListVisible());
@@ -65,6 +68,9 @@ TEST_F(FeedApiTest, BackgroundRefreshForYouSuccess) {
RefreshTaskId::kRefreshForYouFeed));
EXPECT_EQ(LoadStreamStatus::kLoadedFromNetwork,
metrics_reporter_->background_refresh_status);
+ EXPECT_TRUE(network_.query_request_sent);
+ EXPECT_EQ(feedwire::FeedQuery::SCHEDULED_REFRESH,
+ network_.query_request_sent->feed_request().feed_query().reason());
EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
EXPECT_FALSE(stream_->GetModel(kForYouStream));
TestForYouSurface surface(stream_.get());
@@ -320,6 +326,22 @@ TEST_P(FeedStreamTestForAllStreamTypes, LoadFromNetwork) {
ModelStateFor(GetStreamType(), store_.get()));
}
+TEST_F(FeedApiTest, OnboardingFetchAfterStartup) {
+ // Enable WebFeed and WebFeedOnboarding flags.
+ base::test::ScopedFeatureList features;
+ std::vector<base::Feature> enabled_features = {kWebFeed, kWebFeedOnboarding},
+ disabled_features = {};
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ features.InitWithFeatures(enabled_features, disabled_features);
+
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+
+ // There should have been a fetch, even though there are no subscriptions.
+ ASSERT_TRUE(network_.query_request_sent);
+ ASSERT_EQ(1, network_.GetWebFeedListContentsCount());
+}
+
TEST_F(FeedApiTest, WebFeedLoadWithNoSubscriptions) {
TestWebFeedSurface surface(stream_.get());
WaitForIdleTaskQueue();
@@ -327,6 +349,79 @@ TEST_F(FeedApiTest, WebFeedLoadWithNoSubscriptions) {
EXPECT_EQ("loading -> no-subscriptions", surface.DescribeUpdates());
}
+TEST_F(FeedApiTest, WebFeedLoadWithNoSubscriptionsAndOnboarding) {
+ // Turn on the onboarding feature.
+ base::test::ScopedFeatureList features;
+ std::vector<base::Feature> enabled_features = {kWebFeedOnboarding},
+ disabled_features = {};
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ features.InitWithFeatures(enabled_features, disabled_features);
+
+ // Scopes are to control the lifetime of the surface object.
+ {
+ TestWebFeedSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ }
+ // The initial fetch should work fine.
+ ASSERT_EQ(1, network_.GetWebFeedListContentsCount());
+
+ // Prepare the next fetch response.
+ response_translator_.InjectResponse(MakeTypicalNextPageState());
+
+ // Make the content a bit less than the stale threshold, and make sure we
+ // don't fetch.
+ task_environment_.FastForwardBy(base::Days(6));
+ {
+ TestWebFeedSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ }
+ ASSERT_EQ(1, network_.GetWebFeedListContentsCount());
+
+ // Make the content as stale as the threshold, and make sure we do fetch.
+ task_environment_.FastForwardBy(base::Days(2));
+ {
+ TestWebFeedSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ }
+ ASSERT_EQ(2, network_.GetWebFeedListContentsCount());
+}
+
+TEST_F(FeedApiTest, WebFeedContentExprirationWithNoSubscriptionsAndOnboarding) {
+ // Turn on the onboarding feature.
+ base::test::ScopedFeatureList features;
+ std::vector<base::Feature> enabled_features = {kWebFeedOnboarding},
+ disabled_features = {};
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ features.InitWithFeatures(enabled_features, disabled_features);
+
+ // Scopes are to control the lifetime of the surface object.
+ {
+ TestWebFeedSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ // The initial fetch should work fine.
+ ASSERT_EQ("loading -> [user@foo] 2 slices", surface.DescribeUpdates());
+ }
+
+ // Don't prepare a fetch response to simulate fetch failure.
+
+ // Make the content a bit less than the expired threshold, and make sure we
+ // load the stale, but expired content.
+ task_environment_.FastForwardBy(base::Days(13));
+ {
+ TestWebFeedSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ ASSERT_EQ("loading -> [user@foo] 2 slices", surface.DescribeUpdates());
+ }
+
+ // Make the content expired, and make sure we don't load it.
+ task_environment_.FastForwardBy(base::Days(2));
+ {
+ TestWebFeedSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ ASSERT_EQ("loading -> cant-refresh", surface.DescribeUpdates());
+ }
+}
+
// Test that we use QueryInteractiveFeedDiscoverApi and QueryNextPageDiscoverApi
// when kDiscoFeedEndpoint is enabled.
TEST_F(FeedApiTest, LoadFromNetworkDiscoFeedEnabled) {
@@ -517,7 +612,8 @@ TEST_F(FeedApiTest, ForceRefreshIfMissedScheduledRefresh) {
TEST_F(FeedApiTest, LoadFromNetworkBecauseStoreIsStale_NetworkStaleAge) {
base::TimeDelta default_staleness_threshold =
- GetFeedConfig().GetStalenessThreshold(kForYouStream);
+ GetFeedConfig().GetStalenessThreshold(kForYouStream,
+ /*is_web_feed_subscriber=*/true);
base::TimeDelta server_staleness_threshold = default_staleness_threshold / 2;
{
@@ -612,7 +708,8 @@ TEST_P(FeedStreamTestForAllStreamTypes, LoadFromNetworkBecauseStoreIsStale) {
MakeTypicalInitialModelState(
/*first_cluster_id=*/0,
kTestTimeEpoch -
- GetFeedConfig().GetStalenessThreshold(GetStreamType()) -
+ GetFeedConfig().GetStalenessThreshold(
+ GetStreamType(), /*is_web_feed_subscriber=*/true) -
base::Minutes(1)),
base::DoNothing());
@@ -1164,7 +1261,7 @@ TEST_F(FeedApiTest, FollowForcesRefresh) {
callback.RunAndGetResult().request_status);
// Wait for model to unload and reattach surface. New content is loaded.
- task_environment_.FastForwardBy(base::Seconds(5));
+ WaitForModelToAutoUnload();
surface.Attach(stream_.get());
WaitForIdleTaskQueue();
ASSERT_EQ("loading -> 3 slices", surface.DescribeUpdates());
@@ -1769,7 +1866,8 @@ TEST_F(FeedApiTest, LoadMoreDoesNotUpdateLoggingEnabled) {
for (bool waa_on : {true, false}) {
for (bool privacy_notice_fulfilled : {true, false}) {
response_translator_.InjectResponse(MakeTypicalNextPageState(
- page++, kTestTimeEpoch, signed_in, waa_on, privacy_notice_fulfilled));
+ page++, kTestTimeEpoch, kTestTimeEpoch, signed_in, waa_on,
+ privacy_notice_fulfilled));
stream_->LoadMore(surface, base::DoNothing());
WaitForIdleTaskQueue();
EXPECT_TRUE(surface.update->logging_parameters().logging_enabled());
@@ -2341,7 +2439,7 @@ TEST_F(FeedApiTest, UnloadOnlyOneOfMultipleModels) {
for_you_surface.Detach();
- task_environment_.FastForwardBy(base::Seconds(2));
+ WaitForModelToAutoUnload();
WaitForIdleTaskQueue();
EXPECT_TRUE(stream_->GetModel(kWebFeedStream));
@@ -2566,6 +2664,19 @@ TEST_F(FeedApiTest, ClearAllOnStartupIfFeedIsDisabled) {
1);
}
+TEST_F(FeedApiTest, FeedIsAblated) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ base::FieldTrialParams params;
+ scoped_feature_list.InitAndEnableFeature(kIsAblated);
+
+ base::HistogramTester histograms;
+ CreateStream();
+ histograms.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
+ UserSettingsOnStart::kFeedNotEnabled, 1);
+
+ ASSERT_FALSE(network_.query_request_sent.has_value());
+}
+
TEST_F(FeedApiTest, ReportUserSettingsFromMetadataWaaOnDpOff) {
// Fetch a feed, so that there's stored data.
{
@@ -2933,76 +3044,506 @@ TEST_F(FeedApiTest, SignOutWhileSurfaceIsOpen) {
surface.DescribeUpdates());
}
-// TODO(crbug.com/1266030): Fix flakes.
-TEST_F(FeedApiTest, DISABLED_NoticeViewed) {
+TEST_F(FeedApiTest, FollowedFromWebPageMenuCount) {
+ // Arrange.
+ TestWebFeedSurface surface(stream_.get());
+ // Act.
+ stream_->IncrementFollowedFromWebPageMenuCount();
+ // Assert.
+ EXPECT_EQ(1, stream_->GetMetadata().followed_from_web_page_menu_count());
+ EXPECT_EQ(1, stream_->GetRequestMetadata(kWebFeedStream, false)
+ .followed_from_web_page_menu_count);
+}
+
+TEST_F(FeedApiTest, InfoCardTrackingActions) {
+ // Set up the server and client timestamps that affect the computation of
+ // the view timestamps in the info card tracking state.
+ base::Time server_timestamp = base::Time::Now();
+ task_environment_.AdvanceClock(base::Seconds(100));
+ base::Time client_timestamp = base::Time::Now();
+ base::TimeDelta timestamp_adjustment = server_timestamp - client_timestamp;
+ task_environment_.AdvanceClock(base::Seconds(200));
+
+ // Load the initial page.
+ response_translator_.InjectResponse(
+ MakeTypicalInitialModelState(0, client_timestamp, server_timestamp));
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+
base::HistogramTester histograms;
- for (int i = 1; i <= NoticeCardTracker::kViewCountThreshold; ++i) {
- if (i > 1)
- task_environment_.AdvanceClock(
- GetFeedConfig().minimum_notice_view_interval);
- stream_->ReportNoticeViewed(kForYouStream, kTestKey);
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeViewed.Youtube", true, i);
- bool should_acknowledge = (i == NoticeCardTracker::kViewCountThreshold);
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeAcknowledged.Youtube", true,
- should_acknowledge ? 1 : 0);
- if (should_acknowledge) {
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeAcknowledgementPath.Youtube",
- NoticeAcknowledgementPath::kViaViewing, 1);
- }
+ // Perform actions on one info card and verify the histograms.
+ base::Time first_view_timestamp2 = base::Time::Now() + timestamp_adjustment;
+ base::Time last_view_timestamp2 = first_view_timestamp2;
+ stream_->ReportInfoCardTrackViewStarted(kForYouStream, kTestInfoCardType2);
+ stream_->ReportInfoCardViewed(kForYouStream, kTestInfoCardType2,
+ kMinimumViewIntervalSeconds);
+ stream_->ReportInfoCardClicked(kForYouStream, kTestInfoCardType2);
+ stream_->ReportInfoCardClicked(kForYouStream, kTestInfoCardType2);
+ histograms.ExpectUniqueSample("ContentSuggestions.Feed.InfoCard.Started",
+ kTestInfoCardType2, 1);
+ histograms.ExpectBucketCount("ContentSuggestions.Feed.InfoCard.Viewed",
+ kTestInfoCardType2, 1);
+ histograms.ExpectBucketCount("ContentSuggestions.Feed.InfoCard.Clicked",
+ kTestInfoCardType2, 2);
+ histograms.ExpectBucketCount("ContentSuggestions.Feed.InfoCard.Dismissed",
+ kTestInfoCardType2, 0);
+
+ // Perform actions on another info card and verify the histograms.
+ base::Time first_view_timestamp1 = base::Time::Now() + timestamp_adjustment;
+ stream_->ReportInfoCardViewed(kForYouStream, kTestInfoCardType1,
+ kMinimumViewIntervalSeconds);
+ task_environment_.AdvanceClock(base::Seconds(kMinimumViewIntervalSeconds));
+ stream_->ReportInfoCardViewed(kForYouStream, kTestInfoCardType1,
+ kMinimumViewIntervalSeconds);
+ task_environment_.AdvanceClock(base::Seconds(kMinimumViewIntervalSeconds));
+ base::Time last_view_timestamp1 = base::Time::Now() + timestamp_adjustment;
+ stream_->ReportInfoCardViewed(kForYouStream, kTestInfoCardType1,
+ kMinimumViewIntervalSeconds);
+ stream_->ReportInfoCardClicked(kForYouStream, kTestInfoCardType1);
+ stream_->ReportInfoCardDismissedExplicitly(kForYouStream, kTestInfoCardType1);
+ histograms.ExpectBucketCount("ContentSuggestions.Feed.InfoCard.Started",
+ kTestInfoCardType1, 0);
+ histograms.ExpectBucketCount("ContentSuggestions.Feed.InfoCard.Viewed",
+ kTestInfoCardType1, 3);
+ histograms.ExpectBucketCount("ContentSuggestions.Feed.InfoCard.Clicked",
+ kTestInfoCardType1, 1);
+ histograms.ExpectBucketCount("ContentSuggestions.Feed.InfoCard.Dismissed",
+ kTestInfoCardType1, 1);
+
+ // Refresh the page so that a feed query including the info card tracking
+ // states is sent.
+ response_translator_.InjectResponse(MakeTypicalRefreshModelState());
+ stream_->ManualRefresh(kForYouStream, base::DoNothing());
+ WaitForIdleTaskQueue();
+
+ // Verify the info card tracking states. There should be 2 states with
+ // expected counts and view timestamps populated.
+ ASSERT_EQ(2, network_.query_request_sent->feed_request()
+ .feed_query()
+ .chrome_fulfillment_info()
+ .info_card_tracking_state_size());
+ feedwire::InfoCardTrackingState state1;
+ state1.set_type(kTestInfoCardType1);
+ state1.set_view_count(3);
+ state1.set_click_count(1);
+ state1.set_explicitly_dismissed_count(1);
+ state1.set_first_view_timestamp(
+ feedstore::ToTimestampMillis(first_view_timestamp1));
+ state1.set_last_view_timestamp(
+ feedstore::ToTimestampMillis(last_view_timestamp1));
+ EXPECT_THAT(state1, EqualsProto(network_.query_request_sent->feed_request()
+ .feed_query()
+ .chrome_fulfillment_info()
+ .info_card_tracking_state(0)));
+ feedwire::InfoCardTrackingState state2;
+ state2.set_type(kTestInfoCardType2);
+ state2.set_view_count(1);
+ state2.set_click_count(2);
+ state2.set_first_view_timestamp(
+ feedstore::ToTimestampMillis(first_view_timestamp2));
+ state2.set_last_view_timestamp(
+ feedstore::ToTimestampMillis(last_view_timestamp2));
+ EXPECT_THAT(state2, EqualsProto(network_.query_request_sent->feed_request()
+ .feed_query()
+ .chrome_fulfillment_info()
+ .info_card_tracking_state(1)));
+}
+
+TEST_F(FeedApiTest, InvalidateFeedCache_CauseRefreshOnNextLoad) {
+ // Load the ForYou feed with initial content from the network.
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
+ ASSERT_EQ("loading -> [user@foo] 2 slices", surface.DescribeUpdates());
+
+ // Invalidate cached content and reattach the surface, waiting for the model
+ // to unload. It should refresh from the network with refreshed content.
+ response_translator_.InjectResponse(MakeTypicalRefreshModelState());
+ stream_->InvalidateContentCacheFor(StreamKind::kForYou);
+ surface.Detach();
+ WaitForModelToAutoUnload();
+ surface.Attach(stream_.get());
+ WaitForIdleTaskQueue();
+ EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
+ ASSERT_EQ("loading -> 3 slices", surface.DescribeUpdates());
+}
+
+TEST_F(FeedApiTest, InvalidateFeedCache_DoesNotForceRefreshFeedWhileInUse) {
+ // Load the ForYou feed with initial content from the network.
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
+ ASSERT_EQ("loading -> [user@foo] 2 slices", surface.DescribeUpdates());
+
+ // Invalidate the ForYou feed and verify nothing changes right away.
+ response_translator_.InjectResponse(MakeTypicalRefreshModelState());
+ stream_->InvalidateContentCacheFor(StreamKind::kForYou);
+ WaitForIdleTaskQueue();
+ ASSERT_EQ("", surface.DescribeUpdates());
+ EXPECT_FALSE(response_translator_.InjectedResponseConsumed());
+}
+
+TEST_F(FeedApiTest, InvalidateFeedCache_UnknownDoesNotForceRefreshAnyFeeds) {
+ // Enable WebFeed and WebFeedOnboarding flags to force WebFeed to fetch
+ // without subscriptions.
+ base::test::ScopedFeatureList features;
+ std::vector<base::Feature> enabled_features = {kWebFeed, kWebFeedOnboarding},
+ disabled_features = {};
+ features.InitWithFeatures(enabled_features, disabled_features);
+ {
+ // Load both feeds and allow them to fetch network contents.
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ TestForYouSurface for_you_surface(stream_.get());
+ TestWebFeedSurface web_feed_surface(stream_.get());
+ WaitForIdleTaskQueue();
+ ASSERT_EQ("loading -> [user@foo] 2 slices",
+ for_you_surface.DescribeUpdates());
+ ASSERT_EQ("loading -> [user@foo] 2 slices",
+ web_feed_surface.DescribeUpdates());
+ EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
}
- // One more viewed report should only increase the NoticeViewed UMA count.
- stream_->ReportNoticeViewed(kForYouStream, kTestKey);
- histograms.ExpectUniqueSample("ContentSuggestions.Feed.NoticeViewed.Youtube",
- true,
- NoticeCardTracker::kViewCountThreshold + 1);
- // NoticeAcknowledged UMA is only reported once.
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeAcknowledged.Youtube", true, 1);
+ // Both surfaces have been destroyed, so wait for the model to unload.
+ WaitForModelToAutoUnload();
+
+ // Invalidate with an unknown feed and recreate both surfaces. None should
+ // refresh from network.
+ response_translator_.InjectResponse(MakeTypicalRefreshModelState());
+ stream_->InvalidateContentCacheFor(StreamKind::kUnknown);
+ {
+ TestForYouSurface for_you_surface(stream_.get());
+ TestWebFeedSurface web_feed_surface(stream_.get());
+ WaitForIdleTaskQueue();
+ ASSERT_EQ("loading -> [user@foo] 2 slices",
+ for_you_surface.DescribeUpdates());
+ ASSERT_EQ("loading -> [user@foo] 2 slices",
+ web_feed_surface.DescribeUpdates());
+ }
+ EXPECT_FALSE(response_translator_.InjectedResponseConsumed());
}
-TEST_F(FeedApiTest, NoticeOpenAndDismissActions) {
- base::HistogramTester histograms;
+TEST_F(FeedApiTest, InvalidateFeedCache_DoesNotRefreshOtherFeed) {
+ // Enable WebFeed and WebFeedOnboarding flags to force WebFeed to fetch
+ // without subscriptions.
+ base::test::ScopedFeatureList features;
+ std::vector<base::Feature> enabled_features = {kWebFeed, kWebFeedOnboarding},
+ disabled_features = {};
+ features.InitWithFeatures(enabled_features, disabled_features);
+ {
+ // Load both feeds and allow them to fetch network contents.
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ TestForYouSurface for_you_surface(stream_.get());
+ TestWebFeedSurface web_feed_surface(stream_.get());
+ WaitForIdleTaskQueue();
+ ASSERT_EQ("loading -> [user@foo] 2 slices",
+ for_you_surface.DescribeUpdates());
+ ASSERT_EQ("loading -> [user@foo] 2 slices",
+ web_feed_surface.DescribeUpdates());
+ EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
+ }
- for (int i = 1; i <= NoticeCardTracker::kClickCountThreshold; ++i) {
- stream_->ReportNoticeOpenAction(kForYouStream, kTestKey);
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeOpenAction.Youtube", true, i);
- bool should_acknowledge = (i == NoticeCardTracker::kClickCountThreshold);
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeAcknowledged.Youtube", true,
- should_acknowledge ? 1 : 0);
- if (should_acknowledge) {
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeAcknowledgementPath.Youtube",
- NoticeAcknowledgementPath::kViaOpenAction, 1);
- }
+ // Both surfaces have been destroyed, so wait for the model to unload.
+ WaitForModelToAutoUnload();
+
+ // Invalidate only the WebFeed feed and recreate both surfaces. Only the
+ // WebFeed should refresh from network.
+ response_translator_.InjectResponse(MakeTypicalRefreshModelState());
+ stream_->InvalidateContentCacheFor(StreamKind::kFollowing);
+ {
+ TestForYouSurface for_you_surface(stream_.get());
+ TestWebFeedSurface web_feed_surface(stream_.get());
+ WaitForIdleTaskQueue();
+ ASSERT_EQ("loading -> [user@foo] 2 slices",
+ for_you_surface.DescribeUpdates());
+ ASSERT_EQ("loading -> [user@foo] 3 slices",
+ web_feed_surface.DescribeUpdates());
}
+ EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
+}
- stream_->ReportNoticeDismissed(kForYouStream, kTestKey);
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeOpenAction.Youtube", true,
- NoticeCardTracker::kClickCountThreshold);
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeDismissed.Youtube", true, 1);
- // NoticeAcknowledged UMA is only reported once.
- histograms.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeAcknowledged.Youtube", true, 1);
+TEST_F(FeedApiTest, FeedCloseRefresh_Scroll) {
+ // Sometimes the clock starts near zero; move it forward just in case.
+ task_environment_.AdvanceClock(base::Minutes(10));
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ kFeedCloseRefresh, {{"require_interaction", "true"}});
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+
+ // Simulate content being viewed. This shouldn't schedule a refresh itself,
+ // but it's required in order for scrolling to schedule a refresh.
+ stream_->ReportFeedViewed(kForYouStream, surface.GetSurfaceId());
+ EXPECT_EQ(base::Seconds(0),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ // Scrolling should cause a refresh to be scheduled.
+ stream_->ReportStreamScrolled(kForYouStream, 1);
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ refresh_scheduler_.Clear();
+
+ // Scrolling shouldn't schedule a refresh for the next few minutes.
+ stream_->ReportStreamScrolled(kForYouStream, 1);
+ // Scheduler shouldn't have been called yet.
+ EXPECT_EQ(base::Seconds(0),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ refresh_scheduler_.Clear();
+ task_environment_.FastForwardBy(base::Minutes(5) + base::Seconds(1));
+ stream_->ReportStreamScrolled(kForYouStream, 1);
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
}
-TEST_F(FeedApiTest, FollowedFromWebPageMenuCount) {
- // Arrange.
- TestWebFeedSurface surface(stream_.get());
- // Act.
- stream_->IncrementFollowedFromWebPageMenuCount();
- // Assert.
- EXPECT_EQ(1, stream_->GetMetadata().followed_from_web_page_menu_count());
- EXPECT_EQ(1, stream_->GetRequestMetadata(kWebFeedStream, false)
- .followed_from_web_page_menu_count);
+TEST_F(FeedApiTest, FeedCloseRefresh_Open) {
+ // Sometimes the clock starts near zero; move it forward just in case.
+ task_environment_.AdvanceClock(base::Minutes(10));
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ kFeedCloseRefresh, {{"require_interaction", "true"}});
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+
+ // Opening should cause a refresh to be scheduled.
+ stream_->ReportOpenAction(GURL("http://example.com"), kForYouStream, "");
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+}
+
+TEST_F(FeedApiTest, FeedCloseRefresh_OpenInNewTab) {
+ // Sometimes the clock starts near zero; move it forward just in case.
+ task_environment_.AdvanceClock(base::Minutes(10));
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ kFeedCloseRefresh, {{"require_interaction", "true"}});
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+
+ // Should cause a refresh to be scheduled.
+ stream_->ReportOpenInNewTabAction(GURL("http://example.com"), kForYouStream,
+ "");
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+}
+
+TEST_F(FeedApiTest, FeedCloseRefresh_ManualRefreshResetsCoalesceTimestamp) {
+ // Sometimes the clock starts near zero; move it forward just in case.
+ task_environment_.AdvanceClock(base::Minutes(10));
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ kFeedCloseRefresh, {{"require_interaction", "true"}});
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+
+ // Simulate content being viewed. This shouldn't schedule a refresh itself,
+ // but it's required in order for later interaction to schedule a refresh.
+ stream_->ReportFeedViewed(kForYouStream, surface.GetSurfaceId());
+ EXPECT_EQ(base::Seconds(0),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ // Should cause a refresh to be scheduled.
+ stream_->ReportStreamScrolled(kForYouStream, 1);
+ refresh_scheduler_.Clear();
+ // Manual refresh resets the anti-jank timestamp, so the next
+ // ReportStreamScrolled() should update the schedule.
+ stream_->ManualRefresh(kForYouStream, base::DoNothing());
+ stream_->ReportStreamScrolled(kForYouStream, 1);
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+}
+
+TEST_F(FeedApiTest, FeedCloseRefresh_FeedViewed) {
+ // Sometimes the clock starts near zero; move it forward just in case.
+ task_environment_.AdvanceClock(base::Minutes(10));
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ kFeedCloseRefresh, {{"require_interaction", "false"}});
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ stream_->ReportFeedViewed(kForYouStream, surface.GetSurfaceId());
+ // The schedule should have been updated.
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ // Only a surface's first view should cause the schedule to be set.
+ refresh_scheduler_.Clear();
+ stream_->ReportFeedViewed(kForYouStream, surface.GetSurfaceId());
+ // Zero means the scheudle wasn't updated.
+ EXPECT_EQ(base::Seconds(0),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ task_environment_.AdvanceClock(base::Minutes(6));
+
+ // Opening another surface should cause a refresh to be scheduled.
+ refresh_scheduler_.Clear();
+ TestForYouSurface surface2(stream_.get());
+ WaitForIdleTaskQueue();
+ stream_->ReportFeedViewed(kForYouStream, surface2.GetSurfaceId());
+ // The schedule should have been updated.
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ task_environment_.AdvanceClock(base::Minutes(6));
+
+ // Leaving the surface and returning should schedule a refresh.
+ refresh_scheduler_.Clear();
+ surface.Detach();
+ WaitForIdleTaskQueue();
+ surface.Attach(stream_.get());
+ WaitForIdleTaskQueue();
+ stream_->ReportFeedViewed(kForYouStream, surface.GetSurfaceId());
+ // The schedule should have been updated.
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+}
+
+TEST_F(FeedApiTest, FeedCloseRefresh_ExistingScheduleGetsReplaced) {
+ // Sometimes the clock starts near zero; move it forward just in case.
+ task_environment_.AdvanceClock(base::Minutes(10));
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ kFeedCloseRefresh, {{"require_interaction", "true"}});
+
+ // Inject a typical network response, with a server-defined request schedule.
+ {
+ RequestSchedule schedule;
+ schedule.anchor_time = kTestTimeEpoch;
+ schedule.refresh_offsets = {base::Minutes(12), base::Minutes(24)};
+ RefreshResponseData response_data;
+ response_data.model_update_request = MakeTypicalInitialModelState();
+ response_data.request_schedule = schedule;
+ response_translator_.InjectResponse(std::move(response_data));
+ }
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ // Verify the first refresh was scheduled (epoch + 12 - 10)
+ EXPECT_EQ(base::Minutes(2),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ // Simulate content being viewed. This shouldn't schedule a refresh itself,
+ // but it's required in order for later interaction to schedule a refresh.
+ stream_->ReportFeedViewed(kForYouStream, surface.GetSurfaceId());
+
+ // Should cause a refresh to be scheduled.
+ stream_->ReportStreamScrolled(kForYouStream, 1);
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+}
+
+TEST_F(FeedApiTest, FeedCloseRefresh_Retry) {
+ // Sometimes the clock starts near zero; move it forward just in case.
+ task_environment_.AdvanceClock(base::Minutes(10));
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ kFeedCloseRefresh, {{"require_interaction", "false"}});
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+ // Update the schedule.
+ stream_->ReportFeedViewed(kForYouStream, surface.GetSurfaceId());
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ // Simulate an allowed refresh that failed.
+ surface.Detach();
+ task_environment_.FastForwardBy(base::Minutes(35));
+ WaitForIdleTaskQueue();
+ refresh_scheduler_.Clear();
+ FeedNetwork::RawResponse raw_response;
+ raw_response.response_info.status_code = 400;
+ network_.InjectApiRawResponse<QueryBackgroundFeedDiscoverApi>(raw_response);
+ stream_->ExecuteRefreshTask(RefreshTaskId::kRefreshForYouFeed);
+ WaitForIdleTaskQueue();
+
+ // The next one should have been scheduled for 60 minutes after the anchor
+ // time, and the clock advanced 35 minutes, so the next one should be
+ // scheduled 25 minutes from now.
+ EXPECT_EQ(std::set<RefreshTaskId>({RefreshTaskId::kRefreshForYouFeed}),
+ refresh_scheduler_.completed_tasks);
+ EXPECT_EQ(base::Minutes(25),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ // Same thing again. There should be one more scheduled retry.
+ task_environment_.FastForwardBy(base::Minutes(35));
+ refresh_scheduler_.Clear();
+ network_.InjectApiRawResponse<QueryBackgroundFeedDiscoverApi>(raw_response);
+ stream_->ExecuteRefreshTask(RefreshTaskId::kRefreshForYouFeed);
+ WaitForIdleTaskQueue();
+
+ // The next one should have been scheduled for 90 minutes after the anchor
+ // time, and the clock advanced 70 minutes total, so the next one should be
+ // scheduled 20 minutes from now.
+ EXPECT_EQ(std::set<RefreshTaskId>({RefreshTaskId::kRefreshForYouFeed}),
+ refresh_scheduler_.completed_tasks);
+ EXPECT_EQ(base::Minutes(20),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+}
+
+TEST_F(FeedApiTest, FeedCloseRefresh_RequestType) {
+ // Sometimes the clock starts near zero; move it forward just in case.
+ task_environment_.AdvanceClock(base::Minutes(10));
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ kFeedCloseRefresh, {{"require_interaction", "true"}});
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ TestForYouSurface surface(stream_.get());
+ WaitForIdleTaskQueue();
+
+ // Opening should cause a refresh to be scheduled.
+ stream_->ReportOpenAction(GURL("http://example.com"), kForYouStream, "");
+ EXPECT_EQ(base::Minutes(30),
+ refresh_scheduler_
+ .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
+
+ // Close the surface and unload the model.
+ surface.Detach();
+ task_environment_.FastForwardBy(base::Seconds(2));
+ WaitForIdleTaskQueue();
+
+ // Do the refresh.
+ response_translator_.InjectResponse(MakeTypicalInitialModelState());
+ stream_->ExecuteRefreshTask(RefreshTaskId::kRefreshForYouFeed);
+ WaitForIdleTaskQueue();
+
+ ASSERT_TRUE(refresh_scheduler_.completed_tasks.count(
+ RefreshTaskId::kRefreshForYouFeed));
+ EXPECT_EQ(LoadStreamStatus::kLoadedFromNetwork,
+ metrics_reporter_->background_refresh_status);
+ EXPECT_TRUE(network_.query_request_sent);
+ // Request reason should be set.
+ EXPECT_EQ(feedwire::FeedQuery::APP_CLOSE_REFRESH,
+ network_.query_request_sent->feed_request().feed_query().reason());
+ EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
}
// Keep instantiations at the bottom.
diff --git a/chromium/components/feed/core/v2/api_test/feed_api_test.cc b/chromium/components/feed/core/v2/api_test/feed_api_test.cc
index d3dcb902131..5a2261f905b 100644
--- a/chromium/components/feed/core/v2/api_test/feed_api_test.cc
+++ b/chromium/components/feed/core/v2/api_test/feed_api_test.cc
@@ -70,7 +70,8 @@ std::unique_ptr<StreamModelUpdateRequest> StoredModelData(
};
LoadStreamFromStoreTask load_task(
LoadStreamFromStoreTask::LoadType::kFullLoad, nullptr, stream_type, store,
- /*missed_last_refresh=*/false, base::BindLambdaForTesting(complete));
+ /*missed_last_refresh=*/false, /*is_web_feed_subscriber=*/true,
+ base::BindLambdaForTesting(complete));
// We want to load the data no matter how stale, or which account.
load_task.IgnoreStalenessForTesting();
load_task.IngoreAccountForTesting();
@@ -959,6 +960,11 @@ void FeedApiTest::WaitForIdleTaskQueue() {
}));
}
+void FeedApiTest::WaitForModelToAutoUnload() {
+ task_environment_.FastForwardBy(GetFeedConfig().model_unload_timeout +
+ base::Seconds(1));
+}
+
void FeedApiTest::UnloadModel(const StreamType& stream_type) {
WaitForIdleTaskQueue();
stream_->UnloadModel(stream_type);
diff --git a/chromium/components/feed/core/v2/api_test/feed_api_test.h b/chromium/components/feed/core/v2/api_test/feed_api_test.h
index eec26b64cb6..0ef006e6eff 100644
--- a/chromium/components/feed/core/v2/api_test/feed_api_test.h
+++ b/chromium/components/feed/core/v2/api_test/feed_api_test.h
@@ -317,6 +317,9 @@ class TestFeedNetwork : public FeedNetwork {
int GetListFollowedWebFeedsRequestCount() const {
return GetApiRequestCount<ListWebFeedsDiscoverApi>();
}
+ int GetWebFeedListContentsCount() const {
+ return GetApiRequestCount<WebFeedListContentsDiscoverApi>();
+ }
std::vector<NetworkRequestType> sent_request_types() const {
return sent_request_types_;
@@ -473,6 +476,9 @@ class FeedApiTest : public testing::Test, public FeedStream::Delegate {
std::unique_ptr<StreamModel> CreateStreamModel();
bool IsTaskQueueIdle() const;
void WaitForIdleTaskQueue();
+ // Fast forwards the task environment enough for the in-memory model to
+ // auto-unload, which will only take place if there are no attached surfaces.
+ void WaitForModelToAutoUnload();
void UnloadModel(const StreamType& stream_type);
void FollowWebFeed(const WebFeedPageInformation page_info);
diff --git a/chromium/components/feed/core/v2/config.cc b/chromium/components/feed/core/v2/config.cc
index 6c7ab94e782..462813ca957 100644
--- a/chromium/components/feed/core/v2/config.cc
+++ b/chromium/components/feed/core/v2/config.cc
@@ -66,6 +66,14 @@ void OverrideWithFinch(Config& config) {
kInterestFeedV2, "content_expiration_threshold_seconds",
config.content_expiration_threshold.InSecondsF()));
+ if (base::FeatureList::IsEnabled(kWebFeedOnboarding)) {
+ config.subscriptionless_content_expiration_threshold =
+ base::Seconds(base::GetFieldTrialParamByFeatureAsDouble(
+ kWebFeedOnboarding,
+ "subscriptionless_content_expiration_threshold_seconds",
+ config.subscriptionless_content_expiration_threshold.InSecondsF()));
+ }
+
config.background_refresh_window_length =
base::Seconds(base::GetFieldTrialParamByFeatureAsDouble(
kInterestFeedV2, "background_refresh_window_length_seconds",
@@ -141,6 +149,15 @@ void OverrideWithFinch(Config& config) {
kWebFeed, "web_feed_stale_content_threshold_seconds",
config.web_feed_stale_content_threshold.InSecondsF()));
+ if (base::FeatureList::IsEnabled(kWebFeedOnboarding)) {
+ config.subscriptionless_web_feed_stale_content_threshold =
+ base::Seconds(base::GetFieldTrialParamByFeatureAsDouble(
+ kWebFeedOnboarding,
+ "subscriptionless_web_feed_stale_content_threshold_seconds",
+ config.subscriptionless_web_feed_stale_content_threshold
+ .InSecondsF()));
+ }
+
// Erase any capabilities with "enable_CAPABILITY = false" set.
base::EraseIf(config.experimental_capabilities, CapabilityDisabled);
@@ -190,10 +207,15 @@ Config::Config() = default;
Config::Config(const Config& other) = default;
Config::~Config() = default;
-base::TimeDelta Config::GetStalenessThreshold(
- const StreamType& stream_type) const {
- return stream_type.IsForYou() ? stale_content_threshold
- : web_feed_stale_content_threshold;
+base::TimeDelta Config::GetStalenessThreshold(const StreamType& stream_type,
+ bool has_subscriptions) const {
+ if (stream_type.IsForYou()) {
+ return stale_content_threshold;
+ }
+ if (has_subscriptions) {
+ return web_feed_stale_content_threshold;
+ }
+ return subscriptionless_web_feed_stale_content_threshold;
}
} // namespace feed
diff --git a/chromium/components/feed/core/v2/config.h b/chromium/components/feed/core/v2/config.h
index d0aa3bec8eb..456b081b17a 100644
--- a/chromium/components/feed/core/v2/config.h
+++ b/chromium/components/feed/core/v2/config.h
@@ -26,6 +26,9 @@ struct Config {
base::TimeDelta stale_content_threshold = base::Hours(24);
// Content older than this threshold will not be shown to the user.
base::TimeDelta content_expiration_threshold = base::Hours(48);
+ // For users with no follows, content older than this will not be shown.
+ base::TimeDelta subscriptionless_content_expiration_threshold =
+ base::Days(14);
// How long the window is for background refresh tasks. If the task cannot be
// scheduled in the window, the background refresh is aborted.
base::TimeDelta background_refresh_window_length = base::Hours(24);
@@ -57,14 +60,15 @@ struct Config {
base::TimeDelta session_id_max_age = base::Days(30);
// Maximum number of images prefetched per refresh.
int max_prefetch_image_requests_per_refresh = 50;
- // The minimum interval from the last time the notice is viewed in order for
- // it to be considered viewed again.
- base::TimeDelta minimum_notice_view_interval = base::Minutes(5);
// Configuration for Web Feeds.
// How long before Web Feed content is considered stale.
base::TimeDelta web_feed_stale_content_threshold = base::Hours(1);
+ // How long before Web Feed content is considered stale if there are no
+ // subscriptions.
+ base::TimeDelta subscriptionless_web_feed_stale_content_threshold =
+ base::Days(7);
// TimeDelta after startup to fetch recommended and subscribed Web Feeds if
// they are stale. If zero, no fetching is done.
// This delay is also used to trigger retrying stored follow/unfollow requests
@@ -111,7 +115,8 @@ struct Config {
Config(const Config& other);
~Config();
- base::TimeDelta GetStalenessThreshold(const StreamType& stream_type) const;
+ base::TimeDelta GetStalenessThreshold(const StreamType& stream_type,
+ bool is_web_feed_subscriber) const;
};
// Gets the current configuration.
diff --git a/chromium/components/feed/core/v2/enums.cc b/chromium/components/feed/core/v2/enums.cc
index 6c17d4606d2..f068ac66ec4 100644
--- a/chromium/components/feed/core/v2/enums.cc
+++ b/chromium/components/feed/core/v2/enums.cc
@@ -106,6 +106,8 @@ std::ostream& operator<<(std::ostream& out, LoadStreamStatus value) {
return out << "kAccountTokenFetchTimedOut";
case LoadStreamStatus::kNetworkFetchTimedOut:
return out << "kNetworkFetchTimedOut";
+ case LoadStreamStatus::kLoadNotAllowedDisabled:
+ return out << "kLoadNotAllowedDisabled";
}
#else
return out << (static_cast<int>(value));
@@ -147,6 +149,7 @@ bool IsLoadingSuccessfulAndFresh(LoadStreamStatus status) {
case LoadStreamStatus::kAccountTokenFetchFailedWrongAccount:
case LoadStreamStatus::kAccountTokenFetchTimedOut:
case LoadStreamStatus::kNetworkFetchTimedOut:
+ case LoadStreamStatus::kLoadNotAllowedDisabled:
return false;
}
}
@@ -238,6 +241,8 @@ base::StringPiece ToString(UserSettingsOnStart v) {
return "SignedInWaaOffDpOff";
case UserSettingsOnStart::kSignedInNoRecentData:
return "SignedInNoRecentData";
+ case UserSettingsOnStart::kFeedNotEnabled:
+ return "FeedNotEnabled";
}
return "Unknown";
}
diff --git a/chromium/components/feed/core/v2/enums.h b/chromium/components/feed/core/v2/enums.h
index 2468d7e409e..a1daeae1abd 100644
--- a/chromium/components/feed/core/v2/enums.h
+++ b/chromium/components/feed/core/v2/enums.h
@@ -40,6 +40,9 @@ enum class LoadType {
// The stored stream data and the loaded model will not be affected if the
// network request fails.
kManualRefresh = 3,
+ // Same as kBackgroundRefresh but specifically scheduled based on user
+ // interaction with the feed.
+ kFeedCloseBackgroundRefresh = 4,
};
// This must be kept in sync with FeedLoadStreamStatus in enums.xml.
@@ -86,7 +89,8 @@ enum class LoadStreamStatus {
kAccountTokenFetchFailedWrongAccount = 27,
kAccountTokenFetchTimedOut = 28,
kNetworkFetchTimedOut = 29,
- kMaxValue = kNetworkFetchTimedOut,
+ kLoadNotAllowedDisabled = 30,
+ kMaxValue = kLoadNotAllowedDisabled,
};
// Were we able to load fresh Feed data. This should be 'true' unless some kind
@@ -141,22 +145,6 @@ enum class WebFeedRefreshStatus {
};
std::ostream& operator<<(std::ostream& out, WebFeedRefreshStatus value);
-// Tells how the notice acknowledgement is reached. These values are also used
-// in histograms. Entries should not be renumbered and numeric values should
-// never be reused.
-enum class NoticeAcknowledgementPath {
- // The acknowledgment is reached after the user views the notice for a
- // required number of times (currently it is 3).
- kViaViewing = 0,
- // The acknowledgment is reached after the user taps the notice to perform
- // an open action for a required number of times (currently it is 1).
- kViaOpenAction = 1,
- // The acknowledgement is reached after the user taps X button to close
- // the notice.
- kViaDismissal = 2,
- kMaxValue = kViaDismissal,
-};
-
// This must be kept in sync with FeedUserSettingsOnStart in enums.xml.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
@@ -181,7 +169,9 @@ enum class UserSettingsOnStart {
// The Feed is enabled, but there is no recent Feed data, so user settings
// state is unknown.
kSignedInNoRecentData = 8,
- kMaxValue = kSignedInNoRecentData,
+ // The Feed is disabled.
+ kFeedNotEnabled = 9,
+ kMaxValue = kFeedNotEnabled,
};
base::StringPiece ToString(UserSettingsOnStart v);
std::ostream& operator<<(std::ostream& out, UserSettingsOnStart value);
diff --git a/chromium/components/feed/core/v2/feed_network_impl.cc b/chromium/components/feed/core/v2/feed_network_impl.cc
index d1dcea3df96..d9410cdaa17 100644
--- a/chromium/components/feed/core/v2/feed_network_impl.cc
+++ b/chromium/components/feed/core/v2/feed_network_impl.cc
@@ -606,6 +606,9 @@ void FeedNetworkImpl::SendDiscoverApiRequest(
request_metadata ? CreateApiRequestHeaders(*request_metadata)
: net::HttpRequestHeaders();
+ // Set the x-response-encoding header to enable compression for DiscoFeed.
+ headers.SetHeader("x-response-encoding", "gzip");
+
Send(url, method, std::move(request_body),
/*allow_bless_auth=*/false, account_info, std::move(headers),
std::move(callback));
diff --git a/chromium/components/feed/core/v2/feed_network_impl_unittest.cc b/chromium/components/feed/core/v2/feed_network_impl_unittest.cc
index 7849e243609..ad5231116fc 100644
--- a/chromium/components/feed/core/v2/feed_network_impl_unittest.cc
+++ b/chromium/components/feed/core/v2/feed_network_impl_unittest.cc
@@ -794,6 +794,18 @@ TEST_F(FeedNetworkTest, SendApiRequest_ListWebFeedsSendsCorrectContentType) {
EXPECT_EQ("application/x-protobuf", requested_content_type);
}
+TEST_F(FeedNetworkTest,
+ SendApiRequest_DiscoFeedRequestsSendResponseEncodingHeader) {
+ feed_network()->SendApiRequest<QueryBackgroundFeedDiscoverApi>(
+ {}, account_info(), request_metadata(), base::DoNothing());
+
+ std::string requested_response_encoding;
+ RespondToDiscoverRequest("", net::HTTP_OK)
+ .headers.GetHeader("x-response-encoding", &requested_response_encoding);
+
+ EXPECT_EQ("gzip", requested_response_encoding);
+}
+
TEST_F(FeedNetworkTest, TestOverrideHostDoesNotAffectDiscoverApis) {
profile_prefs().SetString(feed::prefs::kHostOverrideHost,
"http://www.newhost.com/");
diff --git a/chromium/components/feed/core/v2/feed_stream.cc b/chromium/components/feed/core/v2/feed_stream.cc
index 4dac44406ab..dd0a0268d12 100644
--- a/chromium/components/feed/core/v2/feed_stream.cc
+++ b/chromium/components/feed/core/v2/feed_stream.cc
@@ -34,7 +34,9 @@
#include "components/feed/core/v2/metrics_reporter.h"
#include "components/feed/core/v2/prefs.h"
#include "components/feed/core/v2/protocol_translator.h"
+#include "components/feed/core/v2/public/common_enums.h"
#include "components/feed/core/v2/public/feed_api.h"
+#include "components/feed/core/v2/public/feed_service.h"
#include "components/feed/core/v2/public/feed_stream_surface.h"
#include "components/feed/core/v2/public/logging_parameters.h"
#include "components/feed/core/v2/public/refresh_task_scheduler.h"
@@ -43,7 +45,6 @@
#include "components/feed/core/v2/public/types.h"
#include "components/feed/core/v2/public/unread_content_observer.h"
#include "components/feed/core/v2/scheduling.h"
-#include "components/feed/core/v2/stream/notice_card_tracker.h"
#include "components/feed/core/v2/stream/unread_content_notifier.h"
#include "components/feed/core/v2/stream_model.h"
#include "components/feed/core/v2/surface_updater.h"
@@ -108,6 +109,16 @@ ContentOrder GetValidWebFeedContentOrder(const PrefService& pref_service) {
return ContentOrder::kGrouped;
}
+LoadType RequestScheduleTypeToLoadType(RequestSchedule::Type type) {
+ switch (type) {
+ case RequestSchedule::Type::kFeedCloseRefresh:
+ return LoadType::kFeedCloseBackgroundRefresh;
+ case RequestSchedule::Type::kScheduledRefresh:
+ default:
+ return LoadType::kBackgroundRefresh;
+ }
+}
+
} // namespace
FeedStream::Stream::Stream(const StreamType& stream_type)
@@ -135,6 +146,7 @@ FeedStream::FeedStream(RefreshTaskScheduler* refresh_task_scheduler,
task_queue_(this),
request_throttler_(profile_prefs),
privacy_notice_card_tracker_(profile_prefs),
+ info_card_tracker_(profile_prefs),
user_actions_collector_(profile_prefs) {
DCHECK(persistent_key_value_store_);
DCHECK(feed_network_);
@@ -148,8 +160,8 @@ FeedStream::FeedStream(RefreshTaskScheduler* refresh_task_scheduler,
base::RepeatingClosure preference_change_callback =
base::BindRepeating(&FeedStream::EnabledPreferencesChanged, GetWeakPtr());
- enable_snippets_.Init(prefs::kEnableSnippets, profile_prefs,
- preference_change_callback);
+ snippets_enabled_by_policy_.Init(prefs::kEnableSnippets, profile_prefs,
+ preference_change_callback);
articles_list_visible_.Init(prefs::kArticlesListVisible, profile_prefs,
preference_change_callback);
has_stored_data_.Init(feed::prefs::kHasStoredData, profile_prefs);
@@ -242,9 +254,9 @@ void FeedStream::InitializeComplete(WaitForStoreInitializeTask::Result result) {
}
}
metadata_populated_ = true;
- metrics_reporter_->OnMetadataInitialized(IsFeedEnabledByEnterprisePolicy(),
- IsArticlesListVisible(),
- IsSignedIn(), metadata_);
+ metrics_reporter_->OnMetadataInitialized(
+ IsFeedEnabledByEnterprisePolicy(), IsArticlesListVisible(), IsSignedIn(),
+ IsFeedEnabled(), metadata_);
web_feed_subscription_coordinator_->Populate(result.web_feed_startup_data);
@@ -312,6 +324,9 @@ void FeedStream::StreamLoadComplete(LoadStreamTask::Result result) {
if (base::FeatureList::IsEnabled(kWebFeed) &&
result.load_type != LoadType::kManualRefresh) {
if (result.stream_type.IsForYou()) {
+ // Checking for users without follows.
+ // TODO(b/229143375) - We should rate limit fetches if the server side is
+ // turned off for this locale, and continually fails.
if (!HasUnreadContent(kWebFeedStream)) {
LoadStreamTask::Options options;
options.load_type = LoadType::kBackgroundRefresh;
@@ -478,11 +493,15 @@ bool FeedStream::IsArticlesListVisible() {
}
bool FeedStream::IsFeedEnabledByEnterprisePolicy() {
- return enable_snippets_.GetValue();
+ return snippets_enabled_by_policy_.GetValue();
+}
+
+bool FeedStream::IsFeedEnabled() {
+ return FeedService::IsEnabled(*profile_prefs());
}
bool FeedStream::IsEnabledAndVisible() {
- return IsArticlesListVisible() && IsFeedEnabledByEnterprisePolicy();
+ return IsArticlesListVisible() && IsFeedEnabled();
}
void FeedStream::EnabledPreferencesChanged() {
@@ -566,6 +585,8 @@ void FeedStream::ManualRefresh(const StreamType& stream_type,
base::BindOnce(&FeedStream::StreamLoadComplete,
base::Unretained(this))));
}
+
+ last_refresh_scheduled_on_interaction_time_ = base::TimeTicks();
}
void FeedStream::ExecuteOperations(
@@ -661,6 +682,15 @@ bool FeedStream::WasUrlRecentlyNavigatedFromFeed(const GURL& url) {
url) != recent_feed_navigations_.end();
}
+void FeedStream::InvalidateContentCacheFor(StreamKind stream_kind) {
+ if (StreamKind::kForYou == stream_kind) {
+ SetStreamStale(kForYouStream, true);
+ }
+ if (StreamKind::kFollowing == stream_kind) {
+ SetStreamStale(kWebFeedStream, true);
+ }
+}
+
DebugStreamData FeedStream::GetDebugStreamData() {
return ::feed::prefs::GetDebugStreamData(*profile_prefs_);
}
@@ -813,6 +843,11 @@ LaunchResult FeedStream::ShouldAttemptLoad(const StreamType& stream_type,
INELIGIBLE_DISCOVER_DISABLED_BY_ENTERPRISE_POLICY};
}
+ if (!IsFeedEnabled()) {
+ return {LoadStreamStatus::kLoadNotAllowedDisabled,
+ feedwire::DiscoverLaunchResult::INELIGIBLE_DISCOVER_DISABLED};
+ }
+
if (!delegate_->IsEulaAccepted()) {
return {LoadStreamStatus::kLoadNotAllowedEulaNotAccepted,
feedwire::DiscoverLaunchResult::INELIGIBLE_EULA_NOT_ACCEPTED};
@@ -899,8 +934,6 @@ RequestMetadata FeedStream::GetCommonRequestMetadata(
result.notice_card_acknowledged =
privacy_notice_card_tracker_.HasAcknowledgedNoticeCard();
result.autoplay_enabled = delegate_->IsAutoplayEnabled();
- result.acknowledged_notice_keys =
- NoticeCardTracker::GetAllAckowledgedKeys(profile_prefs_);
if (signed_in_request) {
result.client_instance_id = prefs::GetClientInstanceId(*profile_prefs_);
@@ -948,6 +981,13 @@ RequestMetadata FeedStream::GetRequestMetadata(const StreamType& stream_type,
if (stream_type.IsWebFeed()) {
result.content_order = GetValidWebFeedContentOrder(*profile_prefs_);
}
+
+ if (stream->model) {
+ result.info_card_tracking_states = info_card_tracker_.GetAllStates(
+ stream->model->last_server_response_time_millis(),
+ stream->model->last_added_time_millis());
+ }
+
return result;
}
@@ -1003,14 +1043,17 @@ void FeedStream::ExecuteRefreshTask(RefreshTaskId task_id) {
ShouldAttemptLoad(stream_type, LoadType::kBackgroundRefresh)
.load_stream_status;
+ RequestSchedule request_schedule =
+ feed::prefs::GetRequestSchedule(task_id, *profile_prefs_);
+ LoadType load_type = RequestScheduleTypeToLoadType(request_schedule.type);
+
// If `do_not_attempt_reason` indicates the stream shouldn't be loaded, it's
// unlikely that criteria will change, so we skip rescheduling.
if (do_not_attempt_reason == LoadStreamStatus::kNoStatus ||
do_not_attempt_reason == LoadStreamStatus::kModelAlreadyLoaded) {
// Schedule the next refresh attempt. If a new refresh schedule is returned
// through this refresh, it will be overwritten.
- SetRequestSchedule(
- task_id, feed::prefs::GetRequestSchedule(task_id, *profile_prefs_));
+ SetRequestSchedule(task_id, std::move(request_schedule));
}
if (do_not_attempt_reason != LoadStreamStatus::kNoStatus) {
@@ -1021,7 +1064,7 @@ void FeedStream::ExecuteRefreshTask(RefreshTaskId task_id) {
LoadStreamTask::Options options;
options.stream_type = stream_type;
- options.load_type = LoadType::kBackgroundRefresh;
+ options.load_type = load_type;
options.refresh_even_when_not_stale = true;
task_queue_.AddTask(FROM_HERE,
std::make_unique<LoadStreamTask>(
@@ -1229,6 +1272,7 @@ void FeedStream::ReportOpenAction(const GURL& url,
privacy_notice_card_tracker_.OnOpenAction(
stream.model->FindContentId(ToContentRevision(slice_id)));
}
+ ScheduleFeedCloseRefreshOnInteraction(stream_type);
}
void FeedStream::ReportOpenVisitComplete(base::TimeDelta visit_time) {
metrics_reporter_->OpenVisitComplete(visit_time);
@@ -1250,6 +1294,7 @@ void FeedStream::ReportOpenInNewTabAction(const GURL& url,
privacy_notice_card_tracker_.OnOpenAction(
stream.model->FindContentId(ToContentRevision(slice_id)));
}
+ ScheduleFeedCloseRefreshOnInteraction(stream_type);
}
void FeedStream::ReportSliceViewed(SurfaceId surface_id,
@@ -1292,8 +1337,14 @@ void FeedStream::ReportFeedViewed(const StreamType& stream_type,
metrics_reporter_->FeedViewed(surface_id);
Stream& stream = GetStream(stream_type);
- stream.surfaces.FeedViewed(surface_id);
+ // Skip feed-close refresh scheduling if this surface was already viewed.
+ // entry should never be null, but if it is, we will skip rescheduling.
+ StreamSurfaceSet::Entry* entry = stream.surfaces.FindSurface(surface_id);
+ if (entry && !entry->feed_viewed)
+ ScheduleFeedCloseRefreshOnFirstView(stream_type);
+
+ stream.surfaces.FeedViewed(surface_id);
MaybeNotifyHasUnreadContent(stream_type);
}
@@ -1303,6 +1354,8 @@ void FeedStream::ReportPageLoaded() {
void FeedStream::ReportStreamScrolled(const StreamType& stream_type,
int distance_dp) {
metrics_reporter_->StreamScrolled(stream_type, distance_dp);
+ if (GetStream(stream_type).surfaces.HasSurfaceShowingContent())
+ ScheduleFeedCloseRefreshOnInteraction(stream_type);
}
void FeedStream::ReportStreamScrollStart() {
metrics_reporter_->StreamScrollStart();
@@ -1312,54 +1365,35 @@ void FeedStream::ReportOtherUserAction(const StreamType& stream_type,
metrics_reporter_->OtherUserAction(stream_type, action_type);
}
-void FeedStream::ReportNoticeCreated(const StreamType& stream_type,
- const std::string& key) {
- metrics_reporter_->OnNoticeCreated(stream_type, key);
+void FeedStream::ReportInfoCardTrackViewStarted(const StreamType& stream_type,
+ int info_card_type) {
+ metrics_reporter_->OnInfoCardTrackViewStarted(stream_type, info_card_type);
}
-void FeedStream::ReportNoticeViewed(const StreamType& stream_type,
- const std::string& key) {
- metrics_reporter_->OnNoticeViewed(stream_type, key);
- NoticeCardTracker& tracker = GetNoticeCardTracker(key);
- bool was_acknowledged = tracker.HasAcknowledged();
- tracker.OnViewed();
- if (!was_acknowledged && tracker.HasAcknowledged()) {
- metrics_reporter_->OnNoticeAcknowledged(
- stream_type, key, NoticeAcknowledgementPath::kViaViewing);
- }
+void FeedStream::ReportInfoCardViewed(const StreamType& stream_type,
+ int info_card_type,
+ int minimum_view_interval_seconds) {
+ metrics_reporter_->OnInfoCardViewed(stream_type, info_card_type);
+ info_card_tracker_.OnViewed(info_card_type, minimum_view_interval_seconds);
}
-void FeedStream::ReportNoticeOpenAction(const StreamType& stream_type,
- const std::string& key) {
- metrics_reporter_->OnNoticeOpenAction(stream_type, key);
- NoticeCardTracker& tracker = GetNoticeCardTracker(key);
- bool was_acknowledged = tracker.HasAcknowledged();
- tracker.OnOpenAction();
- if (!was_acknowledged && tracker.HasAcknowledged())
- metrics_reporter_->OnNoticeAcknowledged(
- stream_type, key, NoticeAcknowledgementPath::kViaOpenAction);
+void FeedStream::ReportInfoCardClicked(const StreamType& stream_type,
+ int info_card_type) {
+ metrics_reporter_->OnInfoCardClicked(stream_type, info_card_type);
+ info_card_tracker_.OnClicked(info_card_type);
}
-void FeedStream::ReportNoticeDismissed(const StreamType& stream_type,
- const std::string& key) {
- metrics_reporter_->OnNoticeDismissed(stream_type, key);
- NoticeCardTracker& tracker = GetNoticeCardTracker(key);
- bool was_acknowledged = tracker.HasAcknowledged();
- tracker.OnDismissed();
- if (!was_acknowledged && tracker.HasAcknowledged())
- metrics_reporter_->OnNoticeAcknowledged(
- stream_type, key, NoticeAcknowledgementPath::kViaDismissal);
+void FeedStream::ReportInfoCardDismissedExplicitly(
+ const StreamType& stream_type,
+ int info_card_type) {
+ metrics_reporter_->OnInfoCardDismissedExplicitly(stream_type, info_card_type);
+ info_card_tracker_.OnDismissed(info_card_type);
}
-NoticeCardTracker& FeedStream::GetNoticeCardTracker(const std::string& key) {
- const auto iter = notice_card_trackers_.find(key);
- if (iter != notice_card_trackers_.end())
- return iter->second;
-
- return notice_card_trackers_
- .emplace(std::piecewise_construct, std::forward_as_tuple(key),
- std::forward_as_tuple(profile_prefs_, key))
- .first->second;
+void FeedStream::ResetInfoCardStates(const StreamType& stream_type,
+ int info_card_type) {
+ metrics_reporter_->OnInfoCardStateReset(stream_type, info_card_type);
+ info_card_tracker_.ResetState(info_card_type);
}
void FeedStream::SetContentOrder(const StreamType& stream_type,
@@ -1412,4 +1446,35 @@ ContentOrder FeedStream::GetContentOrderFromPrefs(
return prefs::GetWebFeedContentOrder(*profile_prefs_);
}
+void FeedStream::ScheduleFeedCloseRefreshOnInteraction(const StreamType& type) {
+ if (!base::FeatureList::IsEnabled(kFeedCloseRefresh))
+ return;
+ ScheduleFeedCloseRefresh(type);
+}
+
+void FeedStream::ScheduleFeedCloseRefreshOnFirstView(const StreamType& type) {
+ if (!base::FeatureList::IsEnabled(kFeedCloseRefresh) ||
+ kFeedCloseRefreshRequireInteraction.Get()) {
+ return;
+ }
+ ScheduleFeedCloseRefresh(type);
+}
+
+void FeedStream::ScheduleFeedCloseRefresh(const StreamType& type) {
+ // To avoid causing jank, only schedule the refresh once every several
+ // minutes.
+ base::TimeTicks now = base::TimeTicks::Now();
+ if (now - last_refresh_scheduled_on_interaction_time_ < base::Minutes(5))
+ return;
+
+ last_refresh_scheduled_on_interaction_time_ = now;
+
+ base::TimeDelta delay = base::Minutes(kFeedCloseRefreshDelayMinutes.Get());
+ RequestSchedule schedule;
+ schedule.anchor_time = base::Time::Now();
+ schedule.refresh_offsets = {delay, delay * 2, delay * 3};
+ schedule.type = RequestSchedule::Type::kFeedCloseRefresh;
+ SetRequestSchedule(type, std::move(schedule));
+}
+
} // namespace feed
diff --git a/chromium/components/feed/core/v2/feed_stream.h b/chromium/components/feed/core/v2/feed_stream.h
index 22d8599b961..92f0bd2cb6c 100644
--- a/chromium/components/feed/core/v2/feed_stream.h
+++ b/chromium/components/feed/core/v2/feed_stream.h
@@ -31,6 +31,7 @@
#include "components/feed/core/v2/public/stream_type.h"
#include "components/feed/core/v2/request_throttler.h"
#include "components/feed/core/v2/scheduling.h"
+#include "components/feed/core/v2/stream/info_card_tracker.h"
#include "components/feed/core/v2/stream/privacy_notice_card_tracker.h"
#include "components/feed/core/v2/stream_model.h"
#include "components/feed/core/v2/stream_surface_set.h"
@@ -51,7 +52,6 @@ namespace feed {
namespace feed_stream {
class UnreadContentNotifier;
}
-class NoticeCardTracker;
class FeedNetwork;
class FeedStore;
class WebFeedSubscriptionCoordinator;
@@ -144,6 +144,7 @@ class FeedStream : public FeedApi,
void ProcessViewAction(base::StringPiece data,
const LoggingParameters& logging_parameters) override;
bool WasUrlRecentlyNavigatedFromFeed(const GURL& url) override;
+ void InvalidateContentCacheFor(StreamKind stream_kind) override;
DebugStreamData GetDebugStreamData() override;
void ForceRefreshForDebugging(const StreamType& stream_type) override;
std::string DumpStateForDebugging() override;
@@ -168,14 +169,17 @@ class FeedStream : public FeedApi,
void ReportStreamScrollStart() override;
void ReportOtherUserAction(const StreamType& stream_type,
FeedUserActionType action_type) override;
- void ReportNoticeCreated(const StreamType& stream_type,
- const std::string& key) override;
- void ReportNoticeViewed(const StreamType& stream_type,
- const std::string& key) override;
- void ReportNoticeOpenAction(const StreamType& stream_type,
- const std::string& key) override;
- void ReportNoticeDismissed(const StreamType& stream_type,
- const std::string& key) override;
+ void ReportInfoCardTrackViewStarted(const StreamType& stream_type,
+ int info_card_type) override;
+ void ReportInfoCardViewed(const StreamType& stream_type,
+ int info_card_type,
+ int minimum_view_interval_seconds) override;
+ void ReportInfoCardClicked(const StreamType& stream_type,
+ int info_card_type) override;
+ void ReportInfoCardDismissedExplicitly(const StreamType& stream_type,
+ int info_card_type) override;
+ void ResetInfoCardStates(const StreamType& stream_type,
+ int info_card_type) override;
base::Time GetLastFetchTime(const StreamType& stream_type) override;
void SetContentOrder(const StreamType& stream_type,
ContentOrder content_order) override;
@@ -382,6 +386,7 @@ class FeedStream : public FeedApi,
void ClearAll();
bool IsFeedEnabledByEnterprisePolicy();
+ bool IsFeedEnabled();
bool HasReachedConditionsToUploadActionsWithNoticeCard();
@@ -393,11 +398,18 @@ class FeedStream : public FeedApi,
const Stream* FindStream(const StreamType& type) const;
void UpdateExperiments(Experiments experiments);
- NoticeCardTracker& GetNoticeCardTracker(const std::string& key);
-
RequestMetadata GetCommonRequestMetadata(bool signed_in_request,
bool allow_expired_session_id) const;
+ // Schedule a feed-close refresh when the user has taken some kind of action
+ // on the feed.
+ void ScheduleFeedCloseRefreshOnInteraction(const StreamType& type);
+ // Schedule a feed-close refresh when the user has viewed content for the
+ // first time.
+ void ScheduleFeedCloseRefreshOnFirstView(const StreamType& type);
+ // Internal method for scheduling the feed-close refresh.
+ void ScheduleFeedCloseRefresh(const StreamType& type);
+
// Unowned.
raw_ptr<RefreshTaskScheduler> refresh_task_scheduler_;
@@ -428,7 +440,7 @@ class FeedStream : public FeedApi,
base::TimeTicks signed_out_for_you_refreshes_until_;
BooleanPrefMember has_stored_data_;
- BooleanPrefMember enable_snippets_;
+ BooleanPrefMember snippets_enabled_by_policy_;
BooleanPrefMember articles_list_visible_;
// State loaded at startup:
@@ -446,13 +458,15 @@ class FeedStream : public FeedApi,
PrivacyNoticeCardTracker privacy_notice_card_tracker_;
- std::map<std::string, NoticeCardTracker> notice_card_trackers_;
+ InfoCardTracker info_card_tracker_;
bool clear_all_in_progress_ = false;
std::vector<GURL> recent_feed_navigations_;
UserActionsCollector user_actions_collector_;
+ base::TimeTicks last_refresh_scheduled_on_interaction_time_{};
+
base::WeakPtrFactory<FeedStream> weak_ptr_factory_{this};
};
diff --git a/chromium/components/feed/core/v2/feedstore_util.cc b/chromium/components/feed/core/v2/feedstore_util.cc
index 5142fa17205..d567b50de86 100644
--- a/chromium/components/feed/core/v2/feedstore_util.cc
+++ b/chromium/components/feed/core/v2/feedstore_util.cc
@@ -186,4 +186,12 @@ feed::ContentIdSet GetViewContentIds(const Metadata& metadata,
return {};
}
+void SetLastServerResponseTime(base::Time t, feedstore::StreamData& data) {
+ data.set_last_server_response_time_millis(ToTimestampMillis(t));
+}
+
+base::Time GetLastServerResponseTime(const feedstore::StreamData& data) {
+ return FromTimestampMillis(data.last_server_response_time_millis());
+}
+
} // namespace feedstore
diff --git a/chromium/components/feed/core/v2/feedstore_util.h b/chromium/components/feed/core/v2/feedstore_util.h
index 57f3159e4cc..def58f5bf72 100644
--- a/chromium/components/feed/core/v2/feedstore_util.h
+++ b/chromium/components/feed/core/v2/feedstore_util.h
@@ -32,11 +32,14 @@ feed::StreamType StreamTypeFromId(base::StringPiece id);
int64_t ToTimestampMillis(base::Time t);
base::Time FromTimestampMillis(int64_t millis);
void SetLastAddedTime(base::Time t, feedstore::StreamData& data);
+void SetLastServerResponseTime(base::Time t, feedstore::StreamData& data);
base::Time GetLastAddedTime(const feedstore::StreamData& data);
base::Time GetSessionIdExpiryTime(const feedstore::Metadata& metadata);
base::Time GetStreamViewTime(const Metadata& metadata,
const feed::StreamType& stream_type);
+base::Time GetLastServerResponseTime(const feedstore::StreamData& data);
+
bool IsKnownStale(const Metadata& metadata,
const feed::StreamType& stream_type);
base::Time GetLastFetchTime(const Metadata& metadata,
diff --git a/chromium/components/feed/core/v2/metrics_reporter.cc b/chromium/components/feed/core/v2/metrics_reporter.cc
index bc55b949019..a413c4cbf08 100644
--- a/chromium/components/feed/core/v2/metrics_reporter.cc
+++ b/chromium/components/feed/core/v2/metrics_reporter.cc
@@ -155,30 +155,22 @@ base::StringPiece HistogramReplacement(const StreamType& stream_type) {
return stream_type.IsWebFeed() ? "Feed.WebFeed." : "Feed.";
}
-std::string NoticeUmaName(const StreamType& stream_type,
- const std::string& key,
- base::StringPiece uma_base_name) {
- std::string normalized_key = base::ToLowerASCII(key);
- normalized_key[0] = base::ToUpperASCII(normalized_key[0]);
- // Don't report UMA if the key is not supported.
- if (normalized_key != "Youtube") {
- base::UmaHistogramBoolean(
- base::StrCat({"ContentSuggestions.", HistogramReplacement(stream_type),
- "InvalidNoticeKey"}),
- true);
- return std::string();
- }
+std::string InfoCardActionUmaName(const StreamType& stream_type,
+ base::StringPiece action_name) {
return base::StrCat({"ContentSuggestions.", HistogramReplacement(stream_type),
- uma_base_name, ".", normalized_key});
+ "InfoCard.", action_name});
}
UserSettingsOnStart GetUserSettingsOnStart(
bool isEnabledByEnterprisePolicy,
bool isFeedVisible,
bool isSignedIn,
+ bool isEnabled,
const feedstore::Metadata& metadata) {
if (!isEnabledByEnterprisePolicy)
return UserSettingsOnStart::kFeedNotEnabledByPolicy;
+ if (!isEnabled)
+ return UserSettingsOnStart::kFeedNotEnabled;
if (!isFeedVisible) {
if (isSignedIn)
return UserSettingsOnStart::kFeedNotVisibleSignedIn;
@@ -246,9 +238,11 @@ void MetricsReporter::OnMetadataInitialized(
bool isEnabledByEnterprisePolicy,
bool isFeedVisible,
bool isSignedIn,
+ bool isEnabled,
const feedstore::Metadata& metadata) {
- UserSettingsOnStart settings = GetUserSettingsOnStart(
- isEnabledByEnterprisePolicy, isFeedVisible, isSignedIn, metadata);
+ UserSettingsOnStart settings =
+ GetUserSettingsOnStart(isEnabledByEnterprisePolicy, isFeedVisible,
+ isSignedIn, isEnabled, metadata);
delegate_->RegisterFeedUserSettingsFieldTrial(ToString(settings));
base::UmaHistogramEnumeration("ContentSuggestions.Feed.UserSettingsOnStart",
settings);
@@ -584,6 +578,19 @@ void MetricsReporter::OtherUserAction(const StreamType& stream_type,
case FeedUserActionType::kTappedDiscoverFeedPreview:
case FeedUserActionType::kOpenedAutoplaySettings:
case FeedUserActionType::kTappedFollowButton:
+ case FeedUserActionType::kDiscoverFeedSelected:
+ case FeedUserActionType::kFollowingFeedSelected:
+ case FeedUserActionType::kTappedUnfollowButton:
+ case FeedUserActionType::kShowFollowSucceedSnackbar:
+ case FeedUserActionType::kShowFollowFailedSnackbar:
+ case FeedUserActionType::kShowUnfollowSucceedSnackbar:
+ case FeedUserActionType::kShowUnfollowFailedSnackbar:
+ case FeedUserActionType::kTappedGoToFeedOnSnackbar:
+ case FeedUserActionType::kTappedCrowButton:
+ case FeedUserActionType::kFirstFollowSheetShown:
+ case FeedUserActionType::kFirstFollowSheetTappedGoToFeed:
+ case FeedUserActionType::kFirstFollowSheetTappedGotIt:
+ case FeedUserActionType::kFollowRecommendationIPHShown:
// Nothing additional for these actions. Note that some of these are iOS
// only.
@@ -984,49 +991,35 @@ void MetricsReporter::ReportFollowCountOnLoad(bool content_shown,
subscription_count);
}
-void MetricsReporter::OnNoticeCreated(const StreamType& stream_type,
- const std::string& key) {
- std::string uma_name = NoticeUmaName(stream_type, key, "NoticeCreated");
- if (uma_name.empty())
- return;
- base::UmaHistogramBoolean(uma_name, true);
+void MetricsReporter::OnInfoCardTrackViewStarted(const StreamType& stream_type,
+ int info_card_type) {
+ base::UmaHistogramSparse(InfoCardActionUmaName(stream_type, "Started"),
+ info_card_type);
}
-void MetricsReporter::OnNoticeViewed(const StreamType& stream_type,
- const std::string& key) {
- std::string uma_name = NoticeUmaName(stream_type, key, "NoticeViewed");
- if (uma_name.empty())
- return;
- base::UmaHistogramBoolean(uma_name, true);
+void MetricsReporter::OnInfoCardViewed(const StreamType& stream_type,
+ int info_card_type) {
+ base::UmaHistogramSparse(InfoCardActionUmaName(stream_type, "Viewed"),
+ info_card_type);
}
-void MetricsReporter::OnNoticeOpenAction(const StreamType& stream_type,
- const std::string& key) {
- std::string uma_name = NoticeUmaName(stream_type, key, "NoticeOpenAction");
- if (uma_name.empty())
- return;
- base::UmaHistogramBoolean(uma_name, true);
+void MetricsReporter::OnInfoCardClicked(const StreamType& stream_type,
+ int info_card_type) {
+ base::UmaHistogramSparse(InfoCardActionUmaName(stream_type, "Clicked"),
+ info_card_type);
}
-void MetricsReporter::OnNoticeDismissed(const StreamType& stream_type,
- const std::string& key) {
- std::string uma_name = NoticeUmaName(stream_type, key, "NoticeDismissed");
- if (uma_name.empty())
- return;
- base::UmaHistogramBoolean(uma_name, true);
+void MetricsReporter::OnInfoCardDismissedExplicitly(
+ const StreamType& stream_type,
+ int info_card_type) {
+ base::UmaHistogramSparse(InfoCardActionUmaName(stream_type, "Dismissed"),
+ info_card_type);
}
-void MetricsReporter::OnNoticeAcknowledged(
- const StreamType& stream_type,
- const std::string& key,
- NoticeAcknowledgementPath acknowledgement_path) {
- std::string uma_name = NoticeUmaName(stream_type, key, "NoticeAcknowledged");
- if (uma_name.empty())
- return;
- base::UmaHistogramBoolean(uma_name, true);
- base::UmaHistogramEnumeration(
- NoticeUmaName(stream_type, key, "NoticeAcknowledgementPath"),
- acknowledgement_path);
+void MetricsReporter::OnInfoCardStateReset(const StreamType& stream_type,
+ int info_card_type) {
+ base::UmaHistogramSparse(InfoCardActionUmaName(stream_type, "Reset"),
+ info_card_type);
}
} // namespace feed
diff --git a/chromium/components/feed/core/v2/metrics_reporter.h b/chromium/components/feed/core/v2/metrics_reporter.h
index 6c6cec16eed..74541f0668a 100644
--- a/chromium/components/feed/core/v2/metrics_reporter.h
+++ b/chromium/components/feed/core/v2/metrics_reporter.h
@@ -57,6 +57,7 @@ class MetricsReporter {
void OnMetadataInitialized(bool isEnabledByEnterprisePolicy,
bool isFeedVisible,
bool isSignedIn,
+ bool isEnabled,
const feedstore::Metadata& metadata);
// User interactions. See |FeedApi| for definitions.
@@ -133,15 +134,14 @@ class MetricsReporter {
WebFeedRefreshStatus status,
int subscribed_web_feed_count);
- // Notice events.
- void OnNoticeCreated(const StreamType& stream_type, const std::string& key);
- void OnNoticeViewed(const StreamType& stream_type, const std::string& key);
- void OnNoticeOpenAction(const StreamType& stream_type,
- const std::string& key);
- void OnNoticeDismissed(const StreamType& stream_type, const std::string& key);
- void OnNoticeAcknowledged(const StreamType& stream_type,
- const std::string& key,
- NoticeAcknowledgementPath acknowledgement_path);
+ // Info card events.
+ void OnInfoCardTrackViewStarted(const StreamType& stream_type,
+ int info_card_type);
+ void OnInfoCardViewed(const StreamType& stream_type, int info_card_type);
+ void OnInfoCardClicked(const StreamType& stream_type, int info_card_type);
+ void OnInfoCardDismissedExplicitly(const StreamType& stream_type,
+ int info_card_type);
+ void OnInfoCardStateReset(const StreamType& stream_type, int info_card_type);
private:
// State replicated for reporting per-stream-type metrics.
diff --git a/chromium/components/feed/core/v2/metrics_reporter_unittest.cc b/chromium/components/feed/core/v2/metrics_reporter_unittest.cc
index 9543da1ae38..664c92cda13 100644
--- a/chromium/components/feed/core/v2/metrics_reporter_unittest.cc
+++ b/chromium/components/feed/core/v2/metrics_reporter_unittest.cc
@@ -307,6 +307,23 @@ TEST_F(MetricsReporterTest, ReportsLoadStreamStatus) {
kContentStats.shared_state_size / 1024, 1);
}
+TEST_F(MetricsReporterTest, ReportsLoadStreamStatusWhenDisabled) {
+ reporter_->OnLoadStream(kForYouStream, LoadStreamStatus::kDataInStoreIsStale,
+ LoadStreamStatus::kLoadNotAllowedDisabled,
+ /*is_initial_load=*/true,
+ /*loaded_new_content_from_network=*/false,
+ /*stored_content_age=*/base::Days(5), kContentStats,
+ DefaultRequestMetadata(),
+ std::make_unique<LoadLatencyTimes>());
+
+ histogram_.ExpectUniqueSample(
+ "ContentSuggestions.Feed.LoadStreamStatus.Initial",
+ LoadStreamStatus::kLoadNotAllowedDisabled, 1);
+ histogram_.ExpectUniqueSample(
+ "ContentSuggestions.Feed.LoadStreamStatus.InitialFromStore",
+ LoadStreamStatus::kDataInStoreIsStale, 1);
+}
+
TEST_F(MetricsReporterTest, WebFeed_ReportsLoadStreamStatus) {
reporter_->OnLoadStream(kWebFeedStream, LoadStreamStatus::kDataInStoreIsStale,
LoadStreamStatus::kLoadedFromNetwork,
@@ -986,50 +1003,22 @@ TEST_F(MetricsReporterTest, NetworkRequestCompleteReportsUma) {
123, 1);
}
-TEST_F(MetricsReporterTest, ReportNotice) {
- reporter_->OnNoticeCreated(kForYouStream, "youtube");
- histogram_.ExpectUniqueSample("ContentSuggestions.Feed.NoticeCreated.Youtube",
- true, 1);
-
- reporter_->OnNoticeViewed(kWebFeedStream, "YOUTUBE");
- histogram_.ExpectUniqueSample(
- "ContentSuggestions.Feed.WebFeed.NoticeViewed.Youtube", true, 1);
-
- reporter_->OnNoticeOpenAction(kForYouStream, "youTube");
- histogram_.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeOpenAction.Youtube", true, 1);
-
- reporter_->OnNoticeDismissed(kWebFeedStream, "Youtube");
- histogram_.ExpectUniqueSample(
- "ContentSuggestions.Feed.WebFeed.NoticeDismissed.Youtube", true, 1);
-
- reporter_->OnNoticeAcknowledged(kForYouStream, "Youtube",
- NoticeAcknowledgementPath::kViaOpenAction);
- histogram_.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeAcknowledged.Youtube", true, 1);
- histogram_.ExpectUniqueSample(
- "ContentSuggestions.Feed.NoticeAcknowledgementPath.Youtube",
- NoticeAcknowledgementPath::kViaOpenAction, 1);
-
- // Invalid key.
- reporter_->OnNoticeAcknowledged(kForYouStream, "Test",
- NoticeAcknowledgementPath::kViaOpenAction);
- histogram_.ExpectUniqueSample("ContentSuggestions.Feed.InvalidNoticeKey",
- true, 1);
- EXPECT_TRUE(
- histogram_
- .GetAllSamples("ContentSuggestions.Feed.NoticeAcknowledged.Test")
- .empty());
- EXPECT_TRUE(histogram_
- .GetAllSamples(
- "ContentSuggestions.Feed.NoticeAcknowledgementPath.Test")
- .empty());
+TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedNotEnabled) {
+ reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
+ /*isFeedVisible=*/false,
+ /*isSignedIn=*/false,
+ /*isEnabled=*/false, feedstore::Metadata());
+ histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
+ UserSettingsOnStart::kFeedNotEnabled, 1);
+ EXPECT_EQ(std::vector<std::string>({"FeedNotEnabled"}),
+ register_feed_user_settings_field_trial_calls_);
}
-TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedNotEnabled) {
+TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedNotEnabledByPolicy) {
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/false,
/*isFeedVisible=*/false,
- /*isSignedIn=*/false, feedstore::Metadata());
+ /*isSignedIn=*/false,
+ /*isEnabled=*/true, feedstore::Metadata());
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kFeedNotEnabledByPolicy,
1);
@@ -1040,7 +1029,8 @@ TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedNotEnabled) {
TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedNotVisible_SignedOut) {
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
/*isFeedVisible=*/false,
- /*isSignedIn=*/false, feedstore::Metadata());
+ /*isSignedIn=*/false,
+ /*isEnabled=*/true, feedstore::Metadata());
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kFeedNotVisibleSignedOut,
1);
@@ -1051,7 +1041,8 @@ TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedNotVisible_SignedOut) {
TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedNotVisible_SignedIn) {
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
/*isFeedVisible=*/false,
- /*isSignedIn=*/true, feedstore::Metadata());
+ /*isSignedIn=*/true,
+ /*isEnabled=*/true, feedstore::Metadata());
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kFeedNotVisibleSignedIn,
1);
@@ -1062,7 +1053,8 @@ TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedNotVisible_SignedIn) {
TEST_F(MetricsReporterTest, UserSettingsOnStart_EnabledSignedOut) {
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
/*isFeedVisible=*/true,
- /*isSignedIn=*/false, feedstore::Metadata());
+ /*isSignedIn=*/false,
+ /*isEnabled=*/true, feedstore::Metadata());
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kSignedOut, 1);
EXPECT_EQ(std::vector<std::string>({"SignedOut"}),
@@ -1076,7 +1068,8 @@ TEST_F(MetricsReporterTest, UserSettingsOnStart_WaaOffDpOff) {
feedstore::ToTimestampMillis(base::Time::Now() - kUserSettingsMaxAge));
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
/*isFeedVisible=*/true,
- /*isSignedIn=*/true, metadata);
+ /*isSignedIn=*/true,
+ /*isEnabled=*/true, metadata);
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kSignedInWaaOffDpOff, 1);
EXPECT_EQ(std::vector<std::string>({"SignedInWaaOffDpOff"}),
@@ -1091,7 +1084,8 @@ TEST_F(MetricsReporterTest, UserSettingsOnStart_WaaOnDpOff) {
metadata.set_web_and_app_activity_enabled(true);
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
/*isFeedVisible=*/true,
- /*isSignedIn=*/true, metadata);
+ /*isSignedIn=*/true,
+ /*isEnabled=*/true, metadata);
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kSignedInWaaOnDpOff, 1);
EXPECT_EQ(std::vector<std::string>({"SignedInWaaOnDpOff"}),
@@ -1109,7 +1103,8 @@ TEST_F(MetricsReporterTest, UserSettingsOnStart_WaaOffDpOn) {
metadata.set_discover_personalization_enabled(true);
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
/*isFeedVisible=*/true,
- /*isSignedIn=*/true, metadata);
+ /*isSignedIn=*/true,
+ /*isEnabled=*/true, metadata);
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kSignedInWaaOffDpOn, 1);
EXPECT_EQ(std::vector<std::string>({"SignedInWaaOffDpOn"}),
@@ -1128,7 +1123,8 @@ TEST_F(MetricsReporterTest, UserSettingsOnStart_WaaOnDpOn) {
metadata.set_web_and_app_activity_enabled(true);
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
/*isFeedVisible=*/true,
- /*isSignedIn=*/true, metadata);
+ /*isSignedIn=*/true,
+ /*isEnabled=*/true, metadata);
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kSignedInWaaOnDpOn, 1);
EXPECT_EQ(std::vector<std::string>({"SignedInWaaOnDpOn"}),
@@ -1142,7 +1138,8 @@ TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedDataTooOld) {
base::Seconds(1)));
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
/*isFeedVisible=*/true,
- /*isSignedIn=*/true, metadata);
+ /*isSignedIn=*/true,
+ /*isEnabled=*/true, metadata);
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kSignedInNoRecentData, 1);
EXPECT_EQ(std::vector<std::string>({"SignedInNoRecentData"}),
@@ -1155,11 +1152,34 @@ TEST_F(MetricsReporterTest, UserSettingsOnStart_FeedDataFromFuture) {
feedstore::ToTimestampMillis(base::Time::Now() + base::Seconds(1)));
reporter_->OnMetadataInitialized(/*isEnabledByEnterprisePolicy=*/true,
/*isFeedVisible=*/true,
- /*isSignedIn=*/true, metadata);
+ /*isSignedIn=*/true,
+ /*isEnabled=*/true, metadata);
histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserSettingsOnStart",
UserSettingsOnStart::kSignedInNoRecentData, 1);
EXPECT_EQ(std::vector<std::string>({"SignedInNoRecentData"}),
register_feed_user_settings_field_trial_calls_);
}
+TEST_F(MetricsReporterTest, ReportInfoCard) {
+ reporter_->OnInfoCardTrackViewStarted(kForYouStream, 0);
+ histogram_.ExpectUniqueSample("ContentSuggestions.Feed.InfoCard.Started", 0,
+ 1);
+
+ reporter_->OnInfoCardViewed(kForYouStream, 0);
+ histogram_.ExpectUniqueSample("ContentSuggestions.Feed.InfoCard.Viewed", 0,
+ 1);
+
+ reporter_->OnInfoCardClicked(kWebFeedStream, 1);
+ histogram_.ExpectUniqueSample(
+ "ContentSuggestions.Feed.WebFeed.InfoCard.Clicked", 1, 1);
+
+ reporter_->OnInfoCardDismissedExplicitly(kForYouStream, 0);
+ histogram_.ExpectUniqueSample("ContentSuggestions.Feed.InfoCard.Dismissed", 0,
+ 1);
+
+ reporter_->OnInfoCardStateReset(kWebFeedStream, 1);
+ histogram_.ExpectUniqueSample(
+ "ContentSuggestions.Feed.WebFeed.InfoCard.Reset", 1, 1);
+}
+
} // namespace feed
diff --git a/chromium/components/feed/core/v2/proto_util.cc b/chromium/components/feed/core/v2/proto_util.cc
index c565ee15d5f..079acb37b79 100644
--- a/chromium/components/feed/core/v2/proto_util.cc
+++ b/chromium/components/feed/core/v2/proto_util.cc
@@ -162,10 +162,22 @@ feedwire::Request CreateFeedQueryRequest(
feed_request.add_client_capability(Capability::DOWNLOAD_LINK);
}
+#if BUILDFLAG(IS_ANDROID)
+ // Note that the Crow feature is referenced as THANK_CREATOR within the feed.
+ if (base::FeatureList::IsEnabled(kShareCrowButton)) {
+ feed_request.add_client_capability(Capability::THANK_CREATOR);
+ }
+#endif
+
if (base::FeatureList::IsEnabled(kPersonalizeFeedUnsignedUsers)) {
feed_request.add_client_capability(Capability::ON_DEVICE_USER_PROFILE);
}
+ if (base::FeatureList::IsEnabled(kInfoCardAcknowledgementTracking)) {
+ feed_request.add_client_capability(
+ Capability::INFO_CARD_ACKNOWLEDGEMENT_TRACKING);
+ }
+
*feed_request.mutable_client_info() = CreateClientInfo(request_metadata);
feedwire::FeedQuery& query = *feed_request.mutable_feed_query();
query.set_reason(request_reason);
@@ -220,14 +232,14 @@ void SetNoticeCardAcknowledged(feedwire::Request* request,
}
}
-void SetCardSpecificNoticeAcknowledged(
- feedwire::Request* request,
- const RequestMetadata& request_metadata) {
- for (const auto& key : request_metadata.acknowledged_notice_keys) {
+void SetInfoCardTrackingStates(feedwire::Request* request,
+ const RequestMetadata& request_metadata) {
+ for (const auto& state : request_metadata.info_card_tracking_states) {
request->mutable_feed_request()
->mutable_feed_query()
->mutable_chrome_fulfillment_info()
- ->add_acknowledged_notice_key(key);
+ ->add_info_card_tracking_state()
+ ->CopyFrom(state);
}
}
@@ -326,7 +338,7 @@ feedwire::Request CreateFeedQueryRefreshRequest(
->set_web_feed_token(kChromeFollowToken);
}
SetNoticeCardAcknowledged(&request, request_metadata);
- SetCardSpecificNoticeAcknowledged(&request, request_metadata);
+ SetInfoCardTrackingStates(&request, request_metadata);
return request;
}
diff --git a/chromium/components/feed/core/v2/proto_util_unittest.cc b/chromium/components/feed/core/v2/proto_util_unittest.cc
index 2c9a56d8147..984a9be574a 100644
--- a/chromium/components/feed/core/v2/proto_util_unittest.cc
+++ b/chromium/components/feed/core/v2/proto_util_unittest.cc
@@ -8,10 +8,12 @@
#include "components/feed/core/proto/v2/wire/capability.pb.h"
#include "components/feed/core/proto/v2/wire/client_info.pb.h"
#include "components/feed/core/proto/v2/wire/feed_request.pb.h"
+#include "components/feed/core/proto/v2/wire/info_card.pb.h"
#include "components/feed/core/proto/v2/wire/request.pb.h"
#include "components/feed/core/v2/config.h"
#include "components/feed/core/v2/public/feed_api.h"
#include "components/feed/core/v2/test/proto_printer.h"
+#include "components/feed/core/v2/test/test_util.h"
#include "components/feed/core/v2/types.h"
#include "components/feed/feed_feature_list.h"
#include "components/reading_list/features/reading_list_switches.h"
@@ -22,6 +24,7 @@
namespace feed {
namespace {
+using feedwire::InfoCardTrackingState;
using ::testing::Contains;
using ::testing::IsSupersetOf;
using ::testing::Not;
@@ -150,9 +153,17 @@ TEST(ProtoUtilTest, PrivacyNoticeCardNotAcknowledged) {
.notice_card_acknowledged());
}
-TEST(ProtoUtilTest, NoticeAcknowledged) {
+TEST(ProtoUtilTest, InfoCardTrackingStates) {
RequestMetadata request_metadata;
- request_metadata.acknowledged_notice_keys = {"key1", "key2"};
+ InfoCardTrackingState state1;
+ state1.set_type(101);
+ state1.set_view_count(2);
+ InfoCardTrackingState state2;
+ state1.set_type(2000);
+ state1.set_view_count(5);
+ state1.set_click_count(2);
+ state1.set_explicitly_dismissed_count(1);
+ request_metadata.info_card_tracking_states = {state1, state2};
feedwire::Request request = CreateFeedQueryRefreshRequest(
kForYouStream, feedwire::FeedQuery::MANUAL_REFRESH, request_metadata,
/*consistency_token=*/std::string());
@@ -160,27 +171,15 @@ TEST(ProtoUtilTest, NoticeAcknowledged) {
ASSERT_EQ(2, request.feed_request()
.feed_query()
.chrome_fulfillment_info()
- .acknowledged_notice_key_size());
- EXPECT_EQ("key1", request.feed_request()
- .feed_query()
- .chrome_fulfillment_info()
- .acknowledged_notice_key(0));
- EXPECT_EQ("key2", request.feed_request()
- .feed_query()
- .chrome_fulfillment_info()
- .acknowledged_notice_key(1));
-}
-
-TEST(ProtoUtilTest, NoticeNotAcknowledged) {
- RequestMetadata request_metadata;
- feedwire::Request request = CreateFeedQueryRefreshRequest(
- kForYouStream, feedwire::FeedQuery::MANUAL_REFRESH, request_metadata,
- /*consistency_token=*/std::string());
-
- EXPECT_EQ(0, request.feed_request()
- .feed_query()
- .chrome_fulfillment_info()
- .acknowledged_notice_key_size());
+ .info_card_tracking_state_size());
+ EXPECT_THAT(state1, EqualsProto(request.feed_request()
+ .feed_query()
+ .chrome_fulfillment_info()
+ .info_card_tracking_state(0)));
+ EXPECT_THAT(state2, EqualsProto(request.feed_request()
+ .feed_query()
+ .chrome_fulfillment_info()
+ .info_card_tracking_state(1)));
}
TEST(ProtoUtilTest, AutoplayEnabled) {
@@ -251,5 +250,36 @@ TEST(ProtoUtilTest, ReadLaterDisabled) {
Not(Contains((feedwire::Capability::READ_LATER))));
}
+#if BUILDFLAG(IS_ANDROID)
+TEST(ProtoUtilTest, CrowButtonEnabled) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures({kShareCrowButton}, {});
+ feedwire::FeedRequest request =
+ CreateFeedQueryRefreshRequest(kForYouStream,
+ feedwire::FeedQuery::MANUAL_REFRESH,
+ /*request_metadata=*/{},
+ /*consistency_token=*/std::string())
+ .feed_request();
+
+ ASSERT_THAT(request.client_capability(),
+ Contains(feedwire::Capability::THANK_CREATOR));
+}
+#endif // BUILDFLAG(IS_ANDROID)
+
+TEST(ProtoUtilTest, InfoCardAcknowledgementTrackingEnabled) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures({kInfoCardAcknowledgementTracking}, {});
+ feedwire::FeedRequest request =
+ CreateFeedQueryRefreshRequest(kForYouStream,
+ feedwire::FeedQuery::MANUAL_REFRESH,
+ /*request_metadata=*/{},
+ /*consistency_token=*/std::string())
+ .feed_request();
+
+ ASSERT_THAT(
+ request.client_capability(),
+ Contains(feedwire::Capability::INFO_CARD_ACKNOWLEDGEMENT_TRACKING));
+}
+
} // namespace
} // namespace feed
diff --git a/chromium/components/feed/core/v2/protocol_translator.cc b/chromium/components/feed/core/v2/protocol_translator.cc
index a795d3c2716..0c5823c4e53 100644
--- a/chromium/components/feed/core/v2/protocol_translator.cc
+++ b/chromium/components/feed/core/v2/protocol_translator.cc
@@ -320,6 +320,12 @@ RefreshResponseData TranslateWireResponse(
TranslateContentLifetime(response_metadata.content_lifetime());
}
+ if (response_metadata.has_response_time_ms()) {
+ feedstore::SetLastServerResponseTime(
+ feedstore::FromTimestampMillis(response_metadata.response_time_ms()),
+ result->stream_data);
+ }
+
const auto& chrome_response_metadata =
response_metadata.chrome_feed_response_metadata();
// Note that we're storing the raw proto bytes for the root event ID because
diff --git a/chromium/components/feed/core/v2/public/common_enums.cc b/chromium/components/feed/core/v2/public/common_enums.cc
index 1041c32f377..5eef4015043 100644
--- a/chromium/components/feed/core/v2/public/common_enums.cc
+++ b/chromium/components/feed/core/v2/public/common_enums.cc
@@ -100,6 +100,32 @@ std::ostream& operator<<(std::ostream& out, FeedUserActionType value) {
return out << "kTappedManageHidden";
case FeedUserActionType::kTappedFollowButton:
return out << "kTappedFollow";
+ case FeedUserActionType::kDiscoverFeedSelected:
+ return out << "kDiscoverFeedSelected";
+ case FeedUserActionType::kFollowingFeedSelected:
+ return out << "kFollowingFeedSelected";
+ case FeedUserActionType::kTappedUnfollowButton:
+ return out << "kTappedUnfollow";
+ case FeedUserActionType::kShowFollowSucceedSnackbar:
+ return out << "kShowFollowSucceedSnackbar";
+ case FeedUserActionType::kShowFollowFailedSnackbar:
+ return out << "kShowFollowFailedSnackbar";
+ case FeedUserActionType::kShowUnfollowSucceedSnackbar:
+ return out << "kShowUnfollowSucceedSnackbar";
+ case FeedUserActionType::kShowUnfollowFailedSnackbar:
+ return out << "kShowUnfollowFailedSnackbar";
+ case FeedUserActionType::kTappedGoToFeedOnSnackbar:
+ return out << "kTappedGoToFeedOnSnackbar";
+ case FeedUserActionType::kTappedCrowButton:
+ return out << "kTappedCrow";
+ case FeedUserActionType::kFirstFollowSheetShown:
+ return out << "kFirstFollowSheetShown";
+ case FeedUserActionType::kFirstFollowSheetTappedGoToFeed:
+ return out << "kFirstFollowSheetTappedGoToFeed";
+ case FeedUserActionType::kFirstFollowSheetTappedGotIt:
+ return out << "kFirstFollowSheetTappedGotIt";
+ case FeedUserActionType::kFollowRecommendationIPHShown:
+ return out << "kFollowRecommendationIPHShown";
}
}
diff --git a/chromium/components/feed/core/v2/public/common_enums.h b/chromium/components/feed/core/v2/public/common_enums.h
index 5e6a36d79c3..3d74f372b8a 100644
--- a/chromium/components/feed/core/v2/public/common_enums.h
+++ b/chromium/components/feed/core/v2/public/common_enums.h
@@ -123,10 +123,43 @@ enum class FeedUserActionType {
kTappedManage = 42,
// User tapped "Hidden" in the manage intestitial.
kTappedManageHidden = 43,
- // User tapped the "Follow" button on the main menu.
+ // User tapped the "Follow" button on the main menu. (Android)
+ // User tapped the "Follow" option on the context menu. (IOS)
kTappedFollowButton = 44,
+ // User tapped on the Discover feed from the feed header.
+ kDiscoverFeedSelected = 45,
+ // User tapped on the Following feed from the feed header.
+ kFollowingFeedSelected = 46,
+ // User tapped the "Unfollow" option on the context menu.
+ kTappedUnfollowButton = 47,
+ // User action caused a follow succeed snackbar to be shown. User action not
+ // reported here. iOS only.
+ kShowFollowSucceedSnackbar = 48,
+ // User action caused a follow failed snackbar to be shown. User action not
+ // reported here. iOS only.
+ kShowFollowFailedSnackbar = 49,
+ // User action caused a unfollow succeed snackbar to be shown. User action not
+ // reported here. iOS only.
+ kShowUnfollowSucceedSnackbar = 50,
+ // User action caused a unfollow failed snackbar to be shown. User action not
+ // reported here. iOS only.
+ kShowUnfollowFailedSnackbar = 51,
+ // User tapped to go to feed using the snackbar 'go to feed' option.
+ kTappedGoToFeedOnSnackbar = 52,
+ // User tapped the Crow button in the context menu.
+ kTappedCrowButton = 53,
+ // User action caused a first follow sheet to be shown. User action not
+ // reported here. iOS only.
+ kFirstFollowSheetShown = 54,
+ // User tapped the "Go To Feed" button on the first follow sheet. (IOS)
+ kFirstFollowSheetTappedGoToFeed = 55,
+ // User tapped the "Got It" button on the first follow sheet. (IOS)
+ kFirstFollowSheetTappedGotIt = 56,
+ // Page load caused a Follow Recommendation IPH to be shown. User action not
+ // reported here. iOS only.
+ kFollowRecommendationIPHShown = 57,
- kMaxValue = kTappedFollowButton,
+ kMaxValue = kFollowRecommendationIPHShown,
};
// For testing and debugging only.
diff --git a/chromium/components/feed/core/v2/public/feed_api.h b/chromium/components/feed/core/v2/public/feed_api.h
index 13b8f39c9b9..d5d27e73462 100644
--- a/chromium/components/feed/core/v2/public/feed_api.h
+++ b/chromium/components/feed/core/v2/public/feed_api.h
@@ -149,6 +149,11 @@ class FeedApi {
// navigated to by the user.
virtual bool WasUrlRecentlyNavigatedFromFeed(const GURL& url) = 0;
+ // Requests that the cache of the feed identified by |stream_kind| be
+ // invalidated so that its contents are re-fetched the next time that feed is
+ // shown/loaded.
+ virtual void InvalidateContentCacheFor(StreamKind stream_kind) = 0;
+
// User interaction reporting. Unless otherwise documented, these have no
// side-effects other than reporting metrics.
@@ -187,19 +192,23 @@ class FeedApi {
// reporting function above..
virtual void ReportOtherUserAction(const StreamType& stream_type,
FeedUserActionType action_type) = 0;
- // The notice identified by |key| is created.
- virtual void ReportNoticeCreated(const StreamType& stream_type,
- const std::string& key) = 0;
- // The notice identified by |key| is viewed (fully visible in the viewport).
- virtual void ReportNoticeViewed(const StreamType& stream_type,
- const std::string& key) = 0;
- // The notice identified by |key| has been clicked/tapped to perform an open
- // action.
- virtual void ReportNoticeOpenAction(const StreamType& stream_type,
- const std::string& key) = 0;
- // The notice identified by |key| is dismissed.
- virtual void ReportNoticeDismissed(const StreamType& stream_type,
- const std::string& key) = 0;
+ // Reports that the info card is being tracked for its full visibility.
+ virtual void ReportInfoCardTrackViewStarted(const StreamType& stream_type,
+ int info_card_type) = 0;
+ // Reports that the info card is visible in the viewport within the threshold.
+ virtual void ReportInfoCardViewed(const StreamType& stream_type,
+ int info_card_type,
+ int minimum_view_interval_seconds) = 0;
+ // Reports that the user taps the info card.
+ virtual void ReportInfoCardClicked(const StreamType& stream_type,
+ int info_card_type) = 0;
+ // Reports that the user dismisses the info card explicitly by tapping the
+ // close button.
+ virtual void ReportInfoCardDismissedExplicitly(const StreamType& stream_type,
+ int info_card_type) = 0;
+ // Resets all the states of the info card.
+ virtual void ResetInfoCardStates(const StreamType& stream_type,
+ int info_card_type) = 0;
// The following methods are used for the internals page.
diff --git a/chromium/components/feed/core/v2/public/feed_service.cc b/chromium/components/feed/core/v2/public/feed_service.cc
index 36a89723c72..089197ef348 100644
--- a/chromium/components/feed/core/v2/public/feed_service.cc
+++ b/chromium/components/feed/core/v2/public/feed_service.cc
@@ -285,7 +285,8 @@ void FeedService::ClearCachedData() {
// static
bool FeedService::IsEnabled(const PrefService& pref_service) {
- return pref_service.GetBoolean(feed::prefs::kEnableSnippets);
+ return !base::FeatureList::IsEnabled(kIsAblated) &&
+ pref_service.GetBoolean(feed::prefs::kEnableSnippets);
}
// static
diff --git a/chromium/components/feed/core/v2/public/types.h b/chromium/components/feed/core/v2/public/types.h
index 72f6ee1ecda..a5dc2f6e741 100644
--- a/chromium/components/feed/core/v2/public/types.h
+++ b/chromium/components/feed/core/v2/public/types.h
@@ -106,10 +106,6 @@ struct NetworkResponse {
std::string response_bytes;
// HTTP status code if available, or net::Error otherwise.
int status_code;
-
- NetworkResponse() = default;
- NetworkResponse(NetworkResponse&& other) = default;
- NetworkResponse& operator=(NetworkResponse&& other) = default;
};
// For the snippets-internals page.
diff --git a/chromium/components/feed/core/v2/scheduling.cc b/chromium/components/feed/core/v2/scheduling.cc
index 9122c3c0cae..a6781655746 100644
--- a/chromium/components/feed/core/v2/scheduling.cc
+++ b/chromium/components/feed/core/v2/scheduling.cc
@@ -5,10 +5,12 @@
#include "components/feed/core/v2/scheduling.h"
#include "base/json/values_util.h"
+#include "base/stl_util.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/feed/core/v2/config.h"
#include "components/feed/core/v2/feedstore_util.h"
+#include "components/feed/feed_feature_list.h"
namespace feed {
namespace {
@@ -43,6 +45,17 @@ base::TimeDelta GetThresholdTime(base::TimeDelta default_threshold,
return server_threshold;
}
+RequestSchedule::Type GetScheduleType(const base::Value* value) {
+ if (value && value->is_int()) {
+ int int_value = value->GetInt();
+ if (int_value >= 0 &&
+ int_value <= base::to_underlying(RequestSchedule::Type::kMaxValue)) {
+ return static_cast<RequestSchedule::Type>(int_value);
+ }
+ }
+ return RequestSchedule::Type::kScheduledRefresh;
+}
+
} // namespace
RequestSchedule::RequestSchedule() = default;
@@ -56,6 +69,7 @@ base::Value RequestScheduleToValue(const RequestSchedule& schedule) {
base::Value result(base::Value::Type::DICTIONARY);
result.SetKey("anchor", base::TimeToValue(schedule.anchor_time));
result.SetKey("offsets", VectorToValue(schedule.refresh_offsets));
+ result.SetKey("type", base::Value(base::to_underlying(schedule.type)));
return result;
}
@@ -67,6 +81,8 @@ RequestSchedule RequestScheduleFromValue(const base::Value& value) {
base::ValueToTime(value.FindKey("anchor"));
const base::Value* offsets =
value.FindKeyOfType("offsets", base::Value::Type::LIST);
+ result.type = GetScheduleType(value.FindKey("type"));
+
if (!anchor || !offsets || !ValueToVector(*offsets, &result.refresh_offsets))
return {};
result.anchor_time = *anchor;
@@ -99,14 +115,15 @@ base::Time NextScheduledRequestTime(base::Time now, RequestSchedule* schedule) {
bool ShouldWaitForNewContent(const feedstore::Metadata& metadata,
const StreamType& stream_type,
- base::TimeDelta content_age) {
+ base::TimeDelta content_age,
+ bool is_web_feed_subscriber) {
const feedstore::Metadata::StreamMetadata* stream_metadata =
feedstore::FindMetadataForStream(metadata, stream_type);
if (stream_metadata && stream_metadata->is_known_stale())
return true;
- base::TimeDelta staleness_threshold =
- GetFeedConfig().GetStalenessThreshold(stream_type);
+ base::TimeDelta staleness_threshold = GetFeedConfig().GetStalenessThreshold(
+ stream_type, is_web_feed_subscriber);
if (stream_metadata && stream_metadata->has_content_lifetime()) {
staleness_threshold = GetThresholdTime(
staleness_threshold,
@@ -118,12 +135,18 @@ bool ShouldWaitForNewContent(const feedstore::Metadata& metadata,
bool ContentInvalidFromAge(const feedstore::Metadata& metadata,
const StreamType& stream_type,
- base::TimeDelta content_age) {
+ base::TimeDelta content_age,
+ bool is_web_feed_subscriber) {
const feedstore::Metadata::StreamMetadata* stream_metadata =
feedstore::FindMetadataForStream(metadata, stream_type);
base::TimeDelta content_expiration_threshold =
GetFeedConfig().content_expiration_threshold;
+ if (base::FeatureList::IsEnabled(kWebFeedOnboarding) &&
+ !is_web_feed_subscriber && stream_type == kWebFeedStream) {
+ content_expiration_threshold =
+ GetFeedConfig().subscriptionless_content_expiration_threshold;
+ }
if (stream_metadata && stream_metadata->has_content_lifetime()) {
content_expiration_threshold = GetThresholdTime(
content_expiration_threshold,
diff --git a/chromium/components/feed/core/v2/scheduling.h b/chromium/components/feed/core/v2/scheduling.h
index bdc4a901bfe..99fed1c9530 100644
--- a/chromium/components/feed/core/v2/scheduling.h
+++ b/chromium/components/feed/core/v2/scheduling.h
@@ -30,8 +30,15 @@ struct RequestSchedule {
RequestSchedule(RequestSchedule&&);
RequestSchedule& operator=(RequestSchedule&&);
+ enum class Type : int {
+ kScheduledRefresh = 0,
+ kFeedCloseRefresh = 1,
+ kMaxValue = kFeedCloseRefresh,
+ };
+
base::Time anchor_time;
std::vector<base::TimeDelta> refresh_offsets;
+ Type type = Type::kScheduledRefresh;
};
base::Value RequestScheduleToValue(const RequestSchedule&);
@@ -44,11 +51,13 @@ base::Time NextScheduledRequestTime(base::Time now, RequestSchedule* schedule);
// Returns whether we should wait for new content before showing stream content.
bool ShouldWaitForNewContent(const feedstore::Metadata& metadata,
const StreamType& stream_type,
- base::TimeDelta content_age);
+ base::TimeDelta content_age,
+ bool is_web_feed_subscriber);
bool ContentInvalidFromAge(const feedstore::Metadata& metadata,
const StreamType& stream_type,
- base::TimeDelta content_age);
+ base::TimeDelta content_age,
+ bool is_web_feed_subscriber);
} // namespace feed
#endif // COMPONENTS_FEED_CORE_V2_SCHEDULING_H_
diff --git a/chromium/components/feed/core/v2/scheduling_unittest.cc b/chromium/components/feed/core/v2/scheduling_unittest.cc
index a83ed4776c2..3eba99cdc7a 100644
--- a/chromium/components/feed/core/v2/scheduling_unittest.cc
+++ b/chromium/components/feed/core/v2/scheduling_unittest.cc
@@ -6,10 +6,12 @@
#include "base/check.h"
#include "base/json/json_writer.h"
#include "base/strings/string_util.h"
+#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "components/feed/core/v2/config.h"
#include "components/feed/core/v2/feedstore_util.h"
#include "components/feed/core/v2/public/stream_type.h"
+#include "components/feed/feed_feature_list.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace feed {
@@ -31,11 +33,13 @@ TEST(RequestSchedule, CanSerialize) {
RequestSchedule schedule;
schedule.anchor_time = kAnchorTime;
schedule.refresh_offsets = {base::Hours(1), base::Hours(6)};
+ schedule.type = RequestSchedule::Type::kScheduledRefresh;
const base::Value schedule_value = RequestScheduleToValue(schedule);
ASSERT_EQ(R"({
"anchor": "11644495200000000",
- "offsets": [ "3600000000", "21600000000" ]
+ "offsets": [ "3600000000", "21600000000" ],
+ "type": 0
}
)",
ToJSON(schedule_value));
@@ -44,6 +48,24 @@ TEST(RequestSchedule, CanSerialize) {
RequestScheduleFromValue(schedule_value);
EXPECT_EQ(schedule.anchor_time, deserialized_schedule.anchor_time);
EXPECT_EQ(schedule.refresh_offsets, deserialized_schedule.refresh_offsets);
+ EXPECT_EQ(schedule.type, deserialized_schedule.type);
+}
+
+TEST(RequestSchedule, GetScheduleType) {
+ RequestSchedule schedule;
+ schedule.anchor_time = kAnchorTime;
+ schedule.refresh_offsets = {base::Hours(1), base::Hours(6)};
+ schedule.type = RequestSchedule::Type::kScheduledRefresh;
+ EXPECT_EQ(RequestSchedule::Type::kScheduledRefresh,
+ RequestScheduleFromValue(RequestScheduleToValue(schedule)).type);
+ schedule.type = RequestSchedule::Type::kFeedCloseRefresh;
+ base::Value schedule_value = RequestScheduleToValue(schedule);
+ EXPECT_EQ(RequestSchedule::Type::kFeedCloseRefresh,
+ RequestScheduleFromValue(schedule_value).type);
+ // Default to kScheduledRefresh if the type isn't valid.
+ schedule_value.GetDict().Set("type", -1);
+ EXPECT_EQ(RequestSchedule::Type::kScheduledRefresh,
+ RequestScheduleFromValue(schedule_value).type);
}
class NextScheduledRequestTimeTest : public testing::Test {
@@ -121,6 +143,8 @@ class ContentLifetimeTest : public testing::Test {
public:
const base::TimeDelta kDefaultContentExpiration = base::Hours(24);
const base::TimeDelta kDefaultStaleContentThreshold = base::Hours(4);
+ const base::TimeDelta kDefaultSubscriptionlessContentExpiration =
+ base::Days(14);
void SetUp() override {
Config config = GetFeedConfig();
@@ -158,86 +182,145 @@ class ContentLifetimeTest : public testing::Test {
TEST_F(ContentLifetimeTest, ShouldWaitForNewContent_DefaultThreshold) {
EXPECT_FALSE(ShouldWaitForNewContent(metadata_, kForYouStream,
- kDefaultStaleContentThreshold));
+ kDefaultStaleContentThreshold,
+ /*is_web_feed_subscriber=*/true));
EXPECT_TRUE(ShouldWaitForNewContent(
- metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold)));
- EXPECT_TRUE(
- ShouldWaitForNewContent(metadata_, kForYouStream, base::Hours(5)));
- EXPECT_FALSE(
- ShouldWaitForNewContent(metadata_, kForYouStream, base::Hours(3)));
+ metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold),
+ true));
+ EXPECT_TRUE(ShouldWaitForNewContent(metadata_, kForYouStream, base::Hours(5),
+ /*is_web_feed_subscriber=*/true));
+ EXPECT_FALSE(ShouldWaitForNewContent(metadata_, kForYouStream, base::Hours(3),
+ /*is_web_feed_subscriber=*/true));
+ // If the web feed onboarding feature is turned off, then we should return
+ // true even if the user is not subscribed.
+ EXPECT_TRUE(ShouldWaitForNewContent(metadata_, kForYouStream, base::Days(8),
+ /*is_web_feed_subscriber=*/false));
+ EXPECT_TRUE(ShouldWaitForNewContent(metadata_, kForYouStream, base::Days(6),
+ /*is_web_feed_subscriber=*/false));
}
TEST_F(ContentLifetimeTest, ShouldWaitForNewContent_ServerThreshold_Valid) {
set_stale_age(base::Minutes(60));
- EXPECT_TRUE(
- ShouldWaitForNewContent(metadata_, kForYouStream, base::Minutes(61)));
- EXPECT_FALSE(
- ShouldWaitForNewContent(metadata_, kForYouStream, base::Minutes(59)));
+ EXPECT_TRUE(ShouldWaitForNewContent(metadata_, kForYouStream,
+ base::Minutes(61),
+ /*is_web_feed_subscriber=*/true));
+ EXPECT_FALSE(ShouldWaitForNewContent(metadata_, kForYouStream,
+ base::Minutes(59),
+ /*is_web_feed_subscriber=*/true));
+}
+
+TEST_F(ContentLifetimeTest, ShouldWaitForNewContent_WithNoSubscriptions) {
+ // Enable WebFeed and WebFeedOnboarding flags.
+ base::test::ScopedFeatureList features;
+ std::vector<base::Feature> enabled_features = {kWebFeedOnboarding},
+ disabled_features = {};
+ features.InitWithFeatures(enabled_features, disabled_features);
+ EXPECT_FALSE(ShouldWaitForNewContent(metadata_, kWebFeedStream, base::Days(6),
+ /*is_web_feed_subscriber=*/false));
+ EXPECT_TRUE(ShouldWaitForNewContent(metadata_, kWebFeedStream, base::Days(8),
+ /*is_web_feed_subscriber=*/false));
}
TEST_F(ContentLifetimeTest, ShouldWaitForNewContent_ServerThreshold_Invalid) {
// We ignore stale ages greater than the default.
EXPECT_TRUE(ShouldWaitForNewContent(
- metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold)));
+ metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold),
+ true));
set_stale_age(kDefaultStaleContentThreshold + base::Minutes(1));
EXPECT_TRUE(ShouldWaitForNewContent(
- metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold)));
+ metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold),
+ /*is_web_feed_subscriber=*/true));
// We ignore zero durations.
set_stale_age(base::Days(0));
EXPECT_FALSE(ShouldWaitForNewContent(metadata_, kForYouStream,
- kDefaultStaleContentThreshold));
+ kDefaultStaleContentThreshold,
+ /*is_web_feed_subscriber=*/true));
EXPECT_TRUE(ShouldWaitForNewContent(
- metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold)));
+ metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold),
+ true));
// We ignore negative durations.
set_stale_age(base::Days(-1));
EXPECT_FALSE(ShouldWaitForNewContent(metadata_, kForYouStream,
- kDefaultStaleContentThreshold));
+ kDefaultStaleContentThreshold,
+ /*is_web_feed_subscriber=*/true));
EXPECT_TRUE(ShouldWaitForNewContent(
- metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold)));
+ metadata_, kForYouStream, WithEpsilon(kDefaultStaleContentThreshold),
+ /*is_web_feed_subscriber=*/true));
}
TEST_F(ContentLifetimeTest, ContentInvalidFromAge_DefaultThreshold) {
EXPECT_FALSE(ContentInvalidFromAge(metadata_, kForYouStream,
- kDefaultContentExpiration));
+ kDefaultContentExpiration,
+ /*is_web_feed_subscriber=*/true));
EXPECT_TRUE(
ContentInvalidFromAge(metadata_, kForYouStream,
- kDefaultContentExpiration + base::Milliseconds(1)));
- EXPECT_TRUE(ContentInvalidFromAge(metadata_, kForYouStream, base::Hours(25)));
- EXPECT_FALSE(
- ContentInvalidFromAge(metadata_, kForYouStream, base::Hours(23)));
+ kDefaultContentExpiration + base::Milliseconds(1),
+ /*is_web_feed_subscriber=*/true));
+ EXPECT_TRUE(ContentInvalidFromAge(metadata_, kForYouStream, base::Hours(25),
+ /*is_web_feed_subscriber=*/true));
+ EXPECT_FALSE(ContentInvalidFromAge(metadata_, kForYouStream, base::Hours(23),
+ /*is_web_feed_subscriber=*/true));
}
TEST_F(ContentLifetimeTest, ContentInvalidFromAge_ServerThreshold_Valid) {
set_invalid_age(base::Minutes(60));
- EXPECT_TRUE(
- ContentInvalidFromAge(metadata_, kForYouStream, base::Minutes(61)));
- EXPECT_FALSE(
- ContentInvalidFromAge(metadata_, kForYouStream, base::Minutes(59)));
+ EXPECT_TRUE(ContentInvalidFromAge(metadata_, kForYouStream, base::Minutes(61),
+ /*is_web_feed_subscriber=*/true));
+ EXPECT_FALSE(ContentInvalidFromAge(metadata_, kForYouStream,
+ base::Minutes(59),
+ /*is_web_feed_subscriber=*/true));
}
TEST_F(ContentLifetimeTest, ContentInvalidFromAge_ServerThreshold_Invalid) {
// We ignore stale ages greater than the default.
EXPECT_TRUE(ContentInvalidFromAge(metadata_, kForYouStream,
- WithEpsilon(kDefaultContentExpiration)));
+ WithEpsilon(kDefaultContentExpiration),
+ /*is_web_feed_subscriber=*/true));
set_invalid_age(kDefaultContentExpiration + base::Minutes(1));
EXPECT_TRUE(ContentInvalidFromAge(metadata_, kForYouStream,
- WithEpsilon(kDefaultContentExpiration)));
+ WithEpsilon(kDefaultContentExpiration),
+ /*is_web_feed_subscriber=*/true));
// We ignore zero durations.
set_invalid_age(base::Days(0));
EXPECT_FALSE(ContentInvalidFromAge(metadata_, kForYouStream,
- kDefaultContentExpiration));
- EXPECT_TRUE(ContentInvalidFromAge(metadata_, kForYouStream,
- WithEpsilon(kDefaultContentExpiration)));
+ kDefaultContentExpiration,
+ /*is_web_feed_subscriber=*/true));
+ EXPECT_TRUE(ContentInvalidFromAge(
+ metadata_, kForYouStream,
+ WithEpsilon(kDefaultSubscriptionlessContentExpiration),
+ /*is_web_feed_subscriber=*/true));
// We ignore negative durations.
set_invalid_age(base::Days(-1));
EXPECT_FALSE(ContentInvalidFromAge(metadata_, kForYouStream,
- kDefaultContentExpiration));
+ kDefaultContentExpiration,
+ /*is_web_feed_subscriber=*/true));
EXPECT_TRUE(ContentInvalidFromAge(metadata_, kForYouStream,
- WithEpsilon(kDefaultContentExpiration)));
+ WithEpsilon(kDefaultContentExpiration),
+ /*is_web_feed_subscriber=*/true));
+}
+
+TEST_F(ContentLifetimeTest, ContentInvalidFromAge_SubscriptionlessThreshold) {
+ // Enable WebFeed and WebFeedOnboarding flags.
+ base::test::ScopedFeatureList features;
+ std::vector<base::Feature> enabled_features = {kWebFeedOnboarding},
+ disabled_features = {};
+ features.InitWithFeatures(enabled_features, disabled_features);
+ EXPECT_FALSE(ContentInvalidFromAge(metadata_, kWebFeedStream,
+ kDefaultSubscriptionlessContentExpiration,
+ /*is_web_feed_subscriber=*/false));
+ EXPECT_TRUE(ContentInvalidFromAge(
+ metadata_, kWebFeedStream,
+ kDefaultSubscriptionlessContentExpiration + base::Milliseconds(1),
+ /*is_web_feed_subscriber=*/false));
+
+ EXPECT_FALSE(ContentInvalidFromAge(metadata_, kWebFeedStream, base::Days(13),
+ /*is_web_feed_subscriber=*/false));
+ EXPECT_TRUE(ContentInvalidFromAge(metadata_, kWebFeedStream, base::Days(15),
+ /*is_web_feed_subscriber=*/false));
}
} // namespace
diff --git a/chromium/components/feed/core/v2/stream/info_card_tracker.cc b/chromium/components/feed/core/v2/stream/info_card_tracker.cc
new file mode 100644
index 00000000000..4ec06a2f05d
--- /dev/null
+++ b/chromium/components/feed/core/v2/stream/info_card_tracker.cc
@@ -0,0 +1,175 @@
+// Copyright 2022 The Chromium 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/feed/core/v2/stream/info_card_tracker.h"
+
+#include <algorithm>
+
+#include "base/base64.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/feed/core/common/pref_names.h"
+#include "components/feed/core/v2/config.h"
+#include "components/feed/core/v2/feedstore_util.h"
+#include "components/feed/core/v2/proto_util.h"
+#include "components/feed/feed_feature_list.h"
+#include "components/prefs/pref_service.h"
+
+namespace feed {
+
+using feedwire::InfoCardTrackingState;
+
+namespace {
+
+std::string InfoCardTypeToString(int info_card_type) {
+ return base::NumberToString(info_card_type);
+}
+
+bool compareInfoCardTrackingState(const InfoCardTrackingState& i1,
+ const InfoCardTrackingState& i2) {
+ return (i1.type() < i2.type());
+}
+
+InfoCardTrackingState DecodeFromBase64SerializedString(
+ const std::string& base64_serialized_state) {
+ InfoCardTrackingState state;
+
+ std::string serialized_state;
+ if (!base::Base64Decode(base64_serialized_state, &serialized_state)) {
+ DLOG(ERROR) << "Error decoding persisted state from base64";
+ return state;
+ }
+
+ if (!state.ParseFromString(serialized_state))
+ DLOG(ERROR) << "Error parsing InfoCardTrackingState message";
+
+ return state;
+}
+
+int64_t GetAdjustedViewTimestamp(int64_t view_timestamp,
+ int64_t server_timestamp,
+ int64_t timestamp_adjustment) {
+ view_timestamp += timestamp_adjustment;
+ // Ensure that the view timestamp does not get earlier than the server.
+ if (view_timestamp < server_timestamp)
+ view_timestamp = server_timestamp;
+ // Ensure that the view timestamp does not exceed the lifetime of the content.
+ int64_t max_timestamp =
+ server_timestamp +
+ GetFeedConfig().content_expiration_threshold.InMilliseconds();
+ if (view_timestamp > max_timestamp)
+ view_timestamp = max_timestamp;
+ return view_timestamp;
+}
+
+} // namespace
+
+InfoCardTracker::InfoCardTracker(PrefService* profile_prefs)
+ : profile_prefs_(profile_prefs) {
+ DCHECK(profile_prefs_);
+}
+
+InfoCardTracker::~InfoCardTracker() = default;
+
+std::vector<InfoCardTrackingState> InfoCardTracker::GetAllStates(
+ int64_t server_timestamp,
+ int64_t client_timestamp) const {
+ std::vector<InfoCardTrackingState> states;
+ const base::Value* dict = profile_prefs_->Get(prefs::kInfoCardStates);
+ if (dict && dict->is_dict()) {
+ int64_t timestamp_adjustment = server_timestamp - client_timestamp;
+ for (const auto pair : dict->DictItems()) {
+ int info_card_type = 0;
+ if (!base::StringToInt(pair.first, &info_card_type))
+ continue;
+ if (!pair.second.is_string())
+ continue;
+ InfoCardTrackingState state =
+ DecodeFromBase64SerializedString(pair.second.GetString());
+ state.set_type(info_card_type);
+ if (state.has_first_view_timestamp()) {
+ state.set_first_view_timestamp(
+ GetAdjustedViewTimestamp(state.first_view_timestamp(),
+ server_timestamp, timestamp_adjustment));
+ }
+ if (state.has_last_view_timestamp()) {
+ state.set_last_view_timestamp(
+ GetAdjustedViewTimestamp(state.last_view_timestamp(),
+ server_timestamp, timestamp_adjustment));
+ }
+ states.push_back(state);
+ }
+ }
+ std::sort(states.begin(), states.end(), compareInfoCardTrackingState);
+ return states;
+}
+
+void InfoCardTracker::OnViewed(int info_card_type,
+ int minimum_view_interval_seconds) {
+ auto now = base::Time::Now();
+ InfoCardTrackingState state = GetState(info_card_type);
+ if (state.has_last_view_timestamp() &&
+ now - feedstore::FromTimestampMillis(state.last_view_timestamp()) <
+ base::Seconds(minimum_view_interval_seconds)) {
+ return;
+ }
+
+ state.set_view_count(state.view_count() + 1);
+ if (!state.has_first_view_timestamp())
+ state.set_first_view_timestamp(feedstore::ToTimestampMillis(now));
+ state.set_last_view_timestamp(feedstore::ToTimestampMillis(now));
+ SetState(info_card_type, state);
+}
+
+void InfoCardTracker::OnClicked(int info_card_type) {
+ InfoCardTrackingState state = GetState(info_card_type);
+ state.set_click_count(state.click_count() + 1);
+ SetState(info_card_type, state);
+}
+
+void InfoCardTracker::OnDismissed(int info_card_type) {
+ InfoCardTrackingState state = GetState(info_card_type);
+ state.set_explicitly_dismissed_count(state.explicitly_dismissed_count() + 1);
+ SetState(info_card_type, state);
+}
+
+void InfoCardTracker::ResetState(int info_card_type) {
+ InfoCardTrackingState state;
+ SetState(info_card_type, state);
+}
+
+InfoCardTrackingState InfoCardTracker::GetState(int info_card_type) const {
+ const base::Value* all_states =
+ profile_prefs_->GetDictionary(prefs::kInfoCardStates);
+ if (!all_states)
+ return InfoCardTrackingState();
+ const std::string* base64_serialized_state =
+ all_states->FindStringKey(InfoCardTypeToString(info_card_type));
+ if (!base64_serialized_state)
+ return InfoCardTrackingState();
+ return DecodeFromBase64SerializedString(*base64_serialized_state);
+}
+
+void InfoCardTracker::SetState(int info_card_type,
+ const InfoCardTrackingState& state) {
+ // SerializeToString encodes the proto into a series of bytes that is not
+ // going to be compatible with UTF-8 encoding. We need to convert them to
+ // base64 before writing to the prefs store.
+ std::string serialized_state;
+ state.SerializeToString(&serialized_state);
+ std::string base64_state;
+ base::Base64Encode(serialized_state, &base64_state);
+
+ base::Value updated_states(base::Value::Type::DICTIONARY);
+ const base::Value* states = profile_prefs_->Get(prefs::kInfoCardStates);
+ if (states && states->is_dict()) {
+ updated_states = states->Clone();
+ }
+ updated_states.SetStringKey(InfoCardTypeToString(info_card_type),
+ base64_state);
+ profile_prefs_->Set(prefs::kInfoCardStates, updated_states);
+}
+
+} // namespace feed
diff --git a/chromium/components/feed/core/v2/stream/info_card_tracker.h b/chromium/components/feed/core/v2/stream/info_card_tracker.h
new file mode 100644
index 00000000000..e959e715416
--- /dev/null
+++ b/chromium/components/feed/core/v2/stream/info_card_tracker.h
@@ -0,0 +1,64 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_FEED_CORE_V2_STREAM_INFO_CARD_TRACKER_H_
+#define COMPONENTS_FEED_CORE_V2_STREAM_INFO_CARD_TRACKER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "components/feed/core/proto/v2/wire/info_card.pb.h"
+#include "components/feed/core/v2/public/common_enums.h"
+
+class PrefService;
+
+namespace feed {
+
+// Tracks the raw counts of the info cards in order to determine their
+// acknowledgement states.
+class InfoCardTracker {
+ public:
+ explicit InfoCardTracker(PrefService* profile_prefs);
+ ~InfoCardTracker();
+
+ InfoCardTracker(const InfoCardTracker&) = delete;
+ InfoCardTracker& operator=(const InfoCardTracker&) = delete;
+
+ // Returns the list of states of all tracked info cards. The returned view
+ // timestamps will be adjusted to be based on server's clock. The adjustment
+ // is computed based on `server_timestamp` and `client_timestamp`.
+ // `server_timestamp` is the server timestamp, in milliseconds from Epoch,
+ // when the response is produced.
+ // `client_timestamp` is the client timestamp, in milliseconds from Epoch,
+ // when the response is received.
+ std::vector<feedwire::InfoCardTrackingState> GetAllStates(
+ int64_t server_timestamp,
+ int64_t client_timestamp) const;
+
+ // Called when the info card is fully visible.
+ void OnViewed(int info_card_type, int minimum_view_interval_seconds);
+
+ // Called when the info card is tapped.
+ void OnClicked(int info_card_type);
+
+ // Called when the info card is dismissed explicitly.
+ void OnDismissed(int info_card_type);
+
+ // Reset the state of the info card.
+ void ResetState(int info_card_type);
+
+ private:
+ feedwire::InfoCardTrackingState GetState(int info_card_type) const;
+ void SetState(int info_card_type,
+ const feedwire::InfoCardTrackingState& state);
+
+ raw_ptr<PrefService> profile_prefs_;
+};
+
+} // namespace feed
+
+#endif // COMPONENTS_FEED_CORE_V2_STREAM_INFO_CARD_TRACKER_H_
diff --git a/chromium/components/feed/core/v2/stream/info_card_tracker_unittest.cc b/chromium/components/feed/core/v2/stream/info_card_tracker_unittest.cc
new file mode 100644
index 00000000000..130623d2f89
--- /dev/null
+++ b/chromium/components/feed/core/v2/stream/info_card_tracker_unittest.cc
@@ -0,0 +1,257 @@
+// Copyright 2022 The Chromium 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/feed/core/v2/stream/info_card_tracker.h"
+
+#include <string>
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "components/feed/core/common/pref_names.h"
+#include "components/feed/core/v2/config.h"
+#include "components/feed/core/v2/feedstore_util.h"
+#include "components/feed/core/v2/test/test_util.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace feed {
+
+namespace {
+
+const int kMinimumViewIntervalSeconds = 5 * 60;
+const base::TimeDelta kEnoughTime = base::Seconds(kMinimumViewIntervalSeconds);
+const base::TimeDelta kNotEnoughTime = kEnoughTime - base::Minutes(1);
+const feedwire::InfoCardType kTestInfoCardType1 =
+ feedwire::INFO_CARD_MAIN_PRIVACY_NOTICE;
+const feedwire::InfoCardType kTestInfoCardType2 =
+ feedwire::INFO_CARD_YOUTUBE_PRIVACY_NOTICE;
+
+} // namespace
+
+class InfoCardTrackerTest : public testing::Test {
+ public:
+ void SetUp() override {
+ feed::RegisterProfilePrefs(profile_prefs_.registry());
+ // Make sure current time isn't zero.
+ task_environment_.AdvanceClock(base::Days(1));
+ }
+
+ protected:
+ base::test::TaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ TestingPrefServiceSimple profile_prefs_;
+ InfoCardTracker tracker_{&profile_prefs_};
+};
+
+TEST_F(InfoCardTrackerTest, InitialEmptyState) {
+ EXPECT_TRUE(tracker_.GetAllStates(0, 0).empty());
+}
+
+TEST_F(InfoCardTrackerTest, ViewCount) {
+ int64_t initial_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ int64_t view_timestamp = initial_timestamp;
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+ feedwire::InfoCardTrackingState expected_state;
+ expected_state.set_type(kTestInfoCardType1);
+ expected_state.set_view_count(1);
+ expected_state.set_first_view_timestamp(view_timestamp);
+ expected_state.set_last_view_timestamp(view_timestamp);
+ EXPECT_THAT(tracker_.GetAllStates(initial_timestamp, initial_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state)));
+
+ // No enough time. The view count does not increase.
+ task_environment_.AdvanceClock(kNotEnoughTime);
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+ EXPECT_THAT(tracker_.GetAllStates(initial_timestamp, initial_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state)));
+
+ // Enough time. The view count increases.
+ task_environment_.AdvanceClock(kEnoughTime - kNotEnoughTime);
+ view_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+ expected_state.set_view_count(2);
+ expected_state.set_last_view_timestamp(view_timestamp);
+ EXPECT_THAT(tracker_.GetAllStates(initial_timestamp, initial_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state)));
+}
+
+TEST_F(InfoCardTrackerTest, ResetState) {
+ int64_t initial_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ int64_t view_timestamp = initial_timestamp;
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+ tracker_.OnClicked(kTestInfoCardType1);
+ tracker_.OnDismissed(kTestInfoCardType1);
+ feedwire::InfoCardTrackingState expected_state;
+ expected_state.set_type(kTestInfoCardType1);
+ expected_state.set_view_count(1);
+ expected_state.set_click_count(1);
+ expected_state.set_explicitly_dismissed_count(1);
+ expected_state.set_first_view_timestamp(view_timestamp);
+ expected_state.set_last_view_timestamp(view_timestamp);
+ EXPECT_THAT(tracker_.GetAllStates(initial_timestamp, initial_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state)));
+
+ tracker_.ResetState(kTestInfoCardType1);
+ expected_state.Clear();
+ expected_state.set_type(kTestInfoCardType1);
+ EXPECT_THAT(tracker_.GetAllStates(initial_timestamp, initial_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state)));
+}
+
+TEST_F(InfoCardTrackerTest, ComboActions) {
+ int64_t initial_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ int64_t type1_fisrt_view_time = initial_timestamp;
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+ tracker_.OnClicked(kTestInfoCardType1);
+ tracker_.OnDismissed(kTestInfoCardType1);
+
+ task_environment_.AdvanceClock(kEnoughTime);
+ tracker_.OnClicked(kTestInfoCardType2);
+ tracker_.OnClicked(kTestInfoCardType2);
+ int64_t type2_fisrt_view_time =
+ feedstore::ToTimestampMillis(base::Time::Now());
+ tracker_.OnViewed(kTestInfoCardType2, kMinimumViewIntervalSeconds);
+ task_environment_.AdvanceClock(kNotEnoughTime);
+ tracker_.OnViewed(kTestInfoCardType2, kMinimumViewIntervalSeconds);
+
+ task_environment_.AdvanceClock(kEnoughTime);
+ int64_t type1_last_view_time =
+ feedstore::ToTimestampMillis(base::Time::Now());
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+ tracker_.OnClicked(kTestInfoCardType1);
+
+ feedwire::InfoCardTrackingState expected_state1;
+ expected_state1.set_type(kTestInfoCardType1);
+ expected_state1.set_view_count(2);
+ expected_state1.set_click_count(2);
+ expected_state1.set_explicitly_dismissed_count(1);
+ expected_state1.set_first_view_timestamp(type1_fisrt_view_time);
+ expected_state1.set_last_view_timestamp(type1_last_view_time);
+ feedwire::InfoCardTrackingState expected_state2;
+ expected_state2.set_type(kTestInfoCardType2);
+ expected_state2.set_view_count(1);
+ expected_state2.set_click_count(2);
+ expected_state2.set_first_view_timestamp(type2_fisrt_view_time);
+ expected_state2.set_last_view_timestamp(type2_fisrt_view_time);
+ EXPECT_THAT(tracker_.GetAllStates(initial_timestamp, initial_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state1),
+ EqualsProto(expected_state2)));
+
+ tracker_.ResetState(kTestInfoCardType1);
+ expected_state1.Clear();
+ expected_state1.set_type(kTestInfoCardType1);
+ EXPECT_THAT(tracker_.GetAllStates(initial_timestamp, initial_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state1),
+ EqualsProto(expected_state2)));
+}
+
+TEST_F(InfoCardTrackerTest, AdjustViewTimestamp_ServerTimestampLessThanClient) {
+ // Make server timestamp earlier than client timestamp.
+ int64_t server_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ task_environment_.AdvanceClock(base::Minutes(10));
+ int64_t client_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ task_environment_.AdvanceClock(base::Minutes(2));
+
+ int64_t first_view_timestamp =
+ feedstore::ToTimestampMillis(base::Time::Now());
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+
+ task_environment_.AdvanceClock(kEnoughTime);
+ int64_t last_view_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+
+ feedwire::InfoCardTrackingState expected_state;
+ expected_state.set_type(kTestInfoCardType1);
+ expected_state.set_view_count(2);
+ // Standard adjustment should be made to view timestamps.
+ expected_state.set_first_view_timestamp(first_view_timestamp +
+ server_timestamp - client_timestamp);
+ expected_state.set_last_view_timestamp(last_view_timestamp +
+ server_timestamp - client_timestamp);
+ EXPECT_THAT(tracker_.GetAllStates(server_timestamp, client_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state)));
+}
+
+TEST_F(InfoCardTrackerTest, AdjustViewTimestamp_ClientTimestampLessThanServer) {
+ // Make client timestamp earlier than server timestamp.
+ int64_t client_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ task_environment_.AdvanceClock(base::Minutes(10));
+ int64_t server_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ task_environment_.AdvanceClock(base::Minutes(2));
+
+ int64_t first_view_timestamp =
+ feedstore::ToTimestampMillis(base::Time::Now());
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+
+ task_environment_.AdvanceClock(kEnoughTime);
+ int64_t last_view_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+
+ feedwire::InfoCardTrackingState expected_state;
+ expected_state.set_type(kTestInfoCardType1);
+ expected_state.set_view_count(2);
+ // Standard adjustment should be made to view timestamps.
+ expected_state.set_first_view_timestamp(first_view_timestamp +
+ server_timestamp - client_timestamp);
+ expected_state.set_last_view_timestamp(last_view_timestamp +
+ server_timestamp - client_timestamp);
+ EXPECT_THAT(tracker_.GetAllStates(server_timestamp, client_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state)));
+}
+
+TEST_F(InfoCardTrackerTest,
+ AdjustViewTimestamp_DoNotGoEarlierThanServerTimestamp) {
+ int64_t server_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ task_environment_.AdvanceClock(base::Minutes(10));
+ // Move client timestamp to some time later to simulate the client clock
+ // going backward.
+ int64_t client_timestamp =
+ feedstore::ToTimestampMillis(base::Time::Now() + base::Hours(1));
+
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+
+ task_environment_.AdvanceClock(kEnoughTime);
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+
+ feedwire::InfoCardTrackingState expected_state;
+ expected_state.set_type(kTestInfoCardType1);
+ expected_state.set_view_count(2);
+ // The view timestamps should not go earlier than server timestamp.
+ expected_state.set_first_view_timestamp(server_timestamp);
+ expected_state.set_last_view_timestamp(server_timestamp);
+ EXPECT_THAT(tracker_.GetAllStates(server_timestamp, client_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state)));
+}
+
+TEST_F(InfoCardTrackerTest, AdjustViewTimestamp_DoNotGoOverContentLifetime) {
+ // Make server timestamp earlier than client timestamp.
+ int64_t server_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ task_environment_.AdvanceClock(base::Minutes(10));
+ int64_t client_timestamp = feedstore::ToTimestampMillis(base::Time::Now());
+ task_environment_.AdvanceClock(base::Minutes(2));
+
+ int64_t first_view_timestamp =
+ feedstore::ToTimestampMillis(base::Time::Now());
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+
+ task_environment_.AdvanceClock(GetFeedConfig().content_expiration_threshold +
+ base::Minutes(20));
+ tracker_.OnViewed(kTestInfoCardType1, kMinimumViewIntervalSeconds);
+
+ feedwire::InfoCardTrackingState expected_state;
+ expected_state.set_type(kTestInfoCardType1);
+ expected_state.set_view_count(2);
+ // Standard adjustment should be made to view timestamps.
+ expected_state.set_first_view_timestamp(first_view_timestamp +
+ server_timestamp - client_timestamp);
+ // View timestamp cannot go over content's lifetime.
+ expected_state.set_last_view_timestamp(
+ server_timestamp +
+ GetFeedConfig().content_expiration_threshold.InMilliseconds());
+ EXPECT_THAT(tracker_.GetAllStates(server_timestamp, client_timestamp),
+ testing::ElementsAre(EqualsProto(expected_state)));
+}
+
+} // namespace feed
diff --git a/chromium/components/feed/core/v2/stream/notice_card_tracker.cc b/chromium/components/feed/core/v2/stream/notice_card_tracker.cc
deleted file mode 100644
index 96e36abcdd7..00000000000
--- a/chromium/components/feed/core/v2/stream/notice_card_tracker.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2021 The Chromium 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/feed/core/v2/stream/notice_card_tracker.h"
-
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/feed/core/common/pref_names.h"
-#include "components/feed/core/v2/config.h"
-#include "components/feed/core/v2/proto_util.h"
-#include "components/feed/feed_feature_list.h"
-#include "components/prefs/pref_service.h"
-
-namespace feed {
-
-namespace {
-const char kViewsKey[] = "views";
-const char kClicksKey[] = "clicks";
-const char kDismissedKey[] = "dismissed";
-} // namespace
-
-const int NoticeCardTracker::kViewCountThreshold = 3;
-const int NoticeCardTracker::kClickCountThreshold = 1;
-
-NoticeCardTracker::NoticeCardTracker(PrefService* profile_prefs,
- const std::string& key)
- : profile_prefs_(profile_prefs), key_(key) {
- DCHECK(profile_prefs_);
-}
-
-NoticeCardTracker::~NoticeCardTracker() = default;
-
-// static
-std::vector<std::string> NoticeCardTracker::GetAllAckowledgedKeys(
- PrefService* profile_prefs) {
- std::vector<std::string> keys;
- const base::Value* dict = profile_prefs->Get(prefs::kNoticeStates);
- if (dict && dict->is_dict()) {
- for (const auto pair : dict->DictItems()) {
- const auto& value = pair.second;
- if (value.is_dict()) {
- int views_count = 0;
- int clicks_count = 0;
- int dismissed = 0;
- for (const auto state_pair : value.DictItems()) {
- if (!state_pair.second.is_int())
- continue;
- if (state_pair.first == kViewsKey)
- views_count = state_pair.second.GetInt();
- else if (state_pair.first == kClicksKey)
- clicks_count = state_pair.second.GetInt();
- else if (state_pair.first == kDismissedKey)
- dismissed = state_pair.second.GetInt();
- }
- if (CanBeTreatedAsAcknowledged(views_count, clicks_count, dismissed)) {
- keys.push_back(pair.first);
- }
- }
- }
- }
- return keys;
-}
-
-// static
-bool NoticeCardTracker::CanBeTreatedAsAcknowledged(int views_count,
- int clicks_count,
- int dismissed) {
- return (views_count >= kViewCountThreshold) ||
- (clicks_count >= kClickCountThreshold) || (dismissed > 0);
-}
-
-void NoticeCardTracker::OnViewed() {
- auto now = base::TimeTicks::Now();
- if (now - last_view_time_ < GetFeedConfig().minimum_notice_view_interval)
- return;
-
- last_view_time_ = now;
-
- IncrementViewCount();
-}
-
-void NoticeCardTracker::OnOpenAction() {
- IncrementClickCount();
-}
-
-void NoticeCardTracker::OnDismissed() {
- SetCount(kDismissedKey, 1);
-}
-
-bool NoticeCardTracker::HasAcknowledged() const {
- return CanBeTreatedAsAcknowledged(GetViewCount(), GetClickCount(),
- GetDismissState());
-}
-
-int NoticeCardTracker::GetViewCount() const {
- return GetCount(kViewsKey);
-}
-
-int NoticeCardTracker::GetClickCount() const {
- return GetCount(kClicksKey);
-}
-
-int NoticeCardTracker::GetDismissState() const {
- return GetCount(kDismissedKey);
-}
-
-void NoticeCardTracker::IncrementViewCount() {
- SetCount(kViewsKey, GetViewCount() + 1);
-}
-
-void NoticeCardTracker::IncrementClickCount() {
- SetCount(kClicksKey, GetClickCount() + 1);
-}
-
-const base::Value* NoticeCardTracker::GetStates() const {
- DCHECK(!key_.empty());
-
- const base::Value* dict = profile_prefs_->Get(prefs::kNoticeStates);
- if (!dict || !dict->is_dict())
- return nullptr;
-
- return dict->FindDictKey(key_);
-}
-
-int NoticeCardTracker::GetCount(base::StringPiece dict_key) const {
- const base::Value* value = GetStates();
- if (!value)
- return 0;
- return value->FindIntKey(dict_key).value_or(0);
-}
-
-void NoticeCardTracker::SetCount(base::StringPiece dict_key, int new_count) {
- base::Value updated_notices(base::Value::Type::DICTIONARY);
- base::Value updated_states(base::Value::Type::DICTIONARY);
-
- const base::Value* notices = profile_prefs_->Get(prefs::kNoticeStates);
- if (notices && notices->is_dict()) {
- updated_notices = notices->Clone();
- const base::Value* states = notices->FindDictKey(key_);
- if (states)
- updated_states = states->Clone();
- }
-
- updated_states.SetIntKey(dict_key, new_count);
- updated_notices.SetKey(key_, std::move(updated_states));
-
- profile_prefs_->Set(prefs::kNoticeStates, updated_notices);
-}
-
-} // namespace feed
diff --git a/chromium/components/feed/core/v2/stream/notice_card_tracker.h b/chromium/components/feed/core/v2/stream/notice_card_tracker.h
deleted file mode 100644
index 64be2e0f27c..00000000000
--- a/chromium/components/feed/core/v2/stream/notice_card_tracker.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2021 The Chromium 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 COMPONENTS_FEED_CORE_V2_STREAM_NOTICE_CARD_TRACKER_H_
-#define COMPONENTS_FEED_CORE_V2_STREAM_NOTICE_CARD_TRACKER_H_
-
-#include <string>
-#include <vector>
-
-#include "base/memory/raw_ptr.h"
-#include "base/strings/string_piece.h"
-#include "base/time/time.h"
-
-class PrefService;
-
-namespace base {
-class Value;
-} // namespace base
-
-namespace feed {
-
-// Tracker for the notice's acknowledgement state.
-class NoticeCardTracker {
- public:
- // The number of views of the notice to be considered as acknowledged by the
- // user.
- static const int kViewCountThreshold;
- // The number of clicks of the notice to be considered as acknowledged by the
- // user.
- static const int kClickCountThreshold;
-
- // Returns all keys of notices that have been acknowledged.
- static std::vector<std::string> GetAllAckowledgedKeys(
- PrefService* profile_prefs);
-
- NoticeCardTracker(PrefService* profile_prefs, const std::string& key);
- ~NoticeCardTracker();
-
- NoticeCardTracker(const NoticeCardTracker&) = delete;
- NoticeCardTracker& operator=(const NoticeCardTracker&) = delete;
-
- // Called when this notice has been viewed.
- void OnViewed();
-
- // Called when the user has clicked/tapped this notice to perform an open
- // action.
- void OnOpenAction();
-
- // Called when this notice is dismissed.
- void OnDismissed();
-
- // Indicates whether the notice is acknowledged by the user. The notice is
- // acknowledged when one of the following conditions is met:
- // 1) There were enough views.
- // 2) Acknowdleged by the user directly when the user clicks or dismisses it.
- bool HasAcknowledged() const;
-
- private:
- static bool CanBeTreatedAsAcknowledged(int views_count,
- int clicks_count,
- int dismissed);
- const base::Value* GetStates() const;
- int GetViewCount() const;
- int GetClickCount() const;
- int GetDismissState() const;
- void IncrementViewCount();
- void IncrementClickCount();
- int GetCount(base::StringPiece dict_key) const;
- void SetCount(base::StringPiece, int new_count);
-
- raw_ptr<PrefService> profile_prefs_;
- std::string key_;
- base::TimeTicks last_view_time_;
-};
-
-} // namespace feed
-
-#endif // COMPONENTS_FEED_CORE_V2_STREAM_NOTICE_CARD_TRACKER_H_
diff --git a/chromium/components/feed/core/v2/stream/notice_card_tracker_unittest.cc b/chromium/components/feed/core/v2/stream/notice_card_tracker_unittest.cc
deleted file mode 100644
index 38653951633..00000000000
--- a/chromium/components/feed/core/v2/stream/notice_card_tracker_unittest.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2021 The Chromium 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/feed/core/v2/stream/notice_card_tracker.h"
-
-#include <string>
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
-#include "components/feed/core/common/pref_names.h"
-#include "components/feed/core/v2/config.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace feed {
-namespace {
-
-const std::string kTestKey = "test";
-const std::string kTestKey2 = "foo";
-const std::string kTestKey3 = "hello";
-
-class NoticeCardTrackerTest : public testing::Test {
- public:
- void SetUp() override {
- feed::RegisterProfilePrefs(profile_prefs_.registry());
- // Make sure current time isn't zero.
- task_environment_.AdvanceClock(base::Days(1));
- }
-
- protected:
- base::test::TaskEnvironment task_environment_{
- base::test::TaskEnvironment::TimeSource::MOCK_TIME};
- // Note that kEnoughTime should not be declared as global vairiable since
- // otherwise it will affect other tests that depend on the overridden
- // GetFeedConfig().
- base::TimeDelta kEnoughTime = GetFeedConfig().minimum_notice_view_interval;
- const base::TimeDelta kNotEnoughTime = kEnoughTime - base::Minutes(1);
- TestingPrefServiceSimple profile_prefs_;
- NoticeCardTracker tracker1_{&profile_prefs_, kTestKey};
- NoticeCardTracker tracker2_{&profile_prefs_, kTestKey2};
- NoticeCardTracker tracker3_{&profile_prefs_, kTestKey3};
-};
-
-TEST_F(NoticeCardTrackerTest, AcknowledgedNoticeCardWhenEnoughViews) {
- for (int i = 0; i < NoticeCardTracker::kViewCountThreshold; ++i) {
- if (i > 0)
- task_environment_.AdvanceClock(kEnoughTime);
- tracker1_.OnViewed();
- }
-
- EXPECT_TRUE(tracker1_.HasAcknowledged());
-}
-
-TEST_F(NoticeCardTrackerTest, AcknowledgedNoticeCardWhenEnoughClicks) {
- for (int i = 0; i < NoticeCardTracker::kClickCountThreshold; ++i) {
- tracker1_.OnOpenAction();
- }
-
- EXPECT_TRUE(tracker1_.HasAcknowledged());
-}
-
-TEST_F(NoticeCardTrackerTest, ViewsAreIgnoredIfNotEnoughTimeElapsed) {
- for (int i = 0; i < NoticeCardTracker::kViewCountThreshold; ++i) {
- if (i > 0)
- task_environment_.AdvanceClock(kNotEnoughTime);
- tracker1_.OnViewed();
- }
-
- EXPECT_FALSE(tracker1_.HasAcknowledged());
-}
-
-TEST_F(NoticeCardTrackerTest,
- DontAcknowledgedNoticeCardWhenNotEnoughViewsNorClicks) {
- for (int i = 0; i < NoticeCardTracker::kViewCountThreshold - 1; ++i) {
- if (i > 0)
- task_environment_.AdvanceClock(kEnoughTime);
- tracker1_.OnViewed();
- }
-
- EXPECT_FALSE(tracker1_.HasAcknowledged());
-}
-
-TEST_F(NoticeCardTrackerTest, Dismiss) {
- tracker2_.OnDismissed();
- EXPECT_FALSE(tracker1_.HasAcknowledged());
- EXPECT_TRUE(tracker2_.HasAcknowledged());
- EXPECT_FALSE(tracker3_.HasAcknowledged());
-
- std::vector<std::string> acknowledged_keys =
- NoticeCardTracker::GetAllAckowledgedKeys(&profile_prefs_);
- EXPECT_THAT(acknowledged_keys, testing::UnorderedElementsAre(kTestKey2));
-}
-
-TEST_F(NoticeCardTrackerTest, MultipleKeysForViews) {
- for (int i = 0; i < NoticeCardTracker::kViewCountThreshold - 1; ++i) {
- if (i > 0)
- task_environment_.AdvanceClock(kEnoughTime);
- tracker1_.OnViewed();
- tracker2_.OnViewed();
- }
-
- // Only make |tracker1_| get enough views.
- task_environment_.AdvanceClock(kEnoughTime);
- tracker1_.OnViewed();
-
- EXPECT_TRUE(tracker1_.HasAcknowledged());
- EXPECT_FALSE(tracker2_.HasAcknowledged());
- EXPECT_FALSE(tracker3_.HasAcknowledged());
-
- std::vector<std::string> acknowledged_keys =
- NoticeCardTracker::GetAllAckowledgedKeys(&profile_prefs_);
- EXPECT_THAT(acknowledged_keys, testing::UnorderedElementsAre(kTestKey));
-}
-
-TEST_F(NoticeCardTrackerTest, MultipleKeysForClicks) {
- for (int i = 0; i < NoticeCardTracker::kClickCountThreshold; ++i) {
- tracker1_.OnOpenAction();
- tracker2_.OnOpenAction();
- }
-
- EXPECT_TRUE(tracker1_.HasAcknowledged());
- EXPECT_TRUE(tracker2_.HasAcknowledged());
- EXPECT_FALSE(tracker3_.HasAcknowledged());
-
- std::vector<std::string> acknowledged_keys =
- NoticeCardTracker::GetAllAckowledgedKeys(&profile_prefs_);
- EXPECT_THAT(acknowledged_keys,
- testing::UnorderedElementsAre(kTestKey, kTestKey2));
-}
-
-TEST_F(NoticeCardTrackerTest, Combo) {
- std::vector<std::string> acknowledged_keys;
-
- // Make one card have enough views.
- for (int i = 0; i < NoticeCardTracker::kViewCountThreshold; ++i) {
- if (i > 0)
- task_environment_.AdvanceClock(kEnoughTime);
- tracker2_.OnViewed();
- }
-
- EXPECT_FALSE(tracker1_.HasAcknowledged());
- EXPECT_TRUE(tracker2_.HasAcknowledged());
- EXPECT_FALSE(tracker3_.HasAcknowledged());
-
- acknowledged_keys = NoticeCardTracker::GetAllAckowledgedKeys(&profile_prefs_);
- EXPECT_THAT(acknowledged_keys, testing::UnorderedElementsAre(kTestKey2));
-
- // Make another card have enough clicks;
- for (int i = 0; i < NoticeCardTracker::kClickCountThreshold; ++i) {
- tracker3_.OnOpenAction();
- }
-
- EXPECT_FALSE(tracker1_.HasAcknowledged());
- EXPECT_TRUE(tracker2_.HasAcknowledged());
- EXPECT_TRUE(tracker3_.HasAcknowledged());
-
- acknowledged_keys = NoticeCardTracker::GetAllAckowledgedKeys(&profile_prefs_);
- EXPECT_THAT(acknowledged_keys,
- testing::UnorderedElementsAre(kTestKey2, kTestKey3));
-
- // Dismiss some other card.
- tracker1_.OnDismissed();
-
- EXPECT_TRUE(tracker1_.HasAcknowledged());
- EXPECT_TRUE(tracker2_.HasAcknowledged());
- EXPECT_TRUE(tracker3_.HasAcknowledged());
-
- acknowledged_keys = NoticeCardTracker::GetAllAckowledgedKeys(&profile_prefs_);
- EXPECT_THAT(acknowledged_keys,
- testing::UnorderedElementsAre(kTestKey, kTestKey2, kTestKey3));
-}
-
-} // namespace
-} // namespace feed
diff --git a/chromium/components/feed/core/v2/stream_model.h b/chromium/components/feed/core/v2/stream_model.h
index d53af5d4fea..8db94bdf0e3 100644
--- a/chromium/components/feed/core/v2/stream_model.h
+++ b/chromium/components/feed/core/v2/stream_model.h
@@ -119,6 +119,18 @@ class StreamModel {
return stream_data_.privacy_notice_fulfilled();
}
+ // The client timestamp, in milliseconds from the Epoch, when the content
+ // from the response is retrieved.
+ int64_t last_added_time_millis() const {
+ return stream_data_.last_added_time_millis();
+ }
+
+ // The server timestamp, in milliseconds from the Epoch, when the response is
+ // produced.
+ int64_t last_server_response_time_millis() const {
+ return stream_data_.last_server_response_time_millis();
+ }
+
// Returns the full list of content in the order it should be presented.
const std::vector<ContentRevision>& GetContentList() const {
return content_list_;
diff --git a/chromium/components/feed/core/v2/stream_model_unittest.cc b/chromium/components/feed/core/v2/stream_model_unittest.cc
index 2e562f8ac9a..02fd2366f7b 100644
--- a/chromium/components/feed/core/v2/stream_model_unittest.cc
+++ b/chromium/components/feed/core/v2/stream_model_unittest.cc
@@ -455,7 +455,7 @@ TEST(StreamModelTest, SharedStateUpdatesKeepOriginal) {
observer.Clear();
store_observer.Clear();
model.Update(MakeTypicalNextPageState(
- 2, kTestTimeEpoch, true, true, true,
+ 2, kTestTimeEpoch, kTestTimeEpoch, true, true, true,
StreamModelUpdateRequest::Source::kNetworkLoadMore));
EXPECT_EQ(2UL, observer.GetUiUpdate()->shared_states.size());
diff --git a/chromium/components/feed/core/v2/surface_updater.cc b/chromium/components/feed/core/v2/surface_updater.cc
index 5b4ac104ea6..a664a3491bd 100644
--- a/chromium/components/feed/core/v2/surface_updater.cc
+++ b/chromium/components/feed/core/v2/surface_updater.cc
@@ -199,6 +199,7 @@ feedui::ZeroStateSlice::Type GetZeroStateType(LoadStreamStatus status) {
case LoadStreamStatus::kDataInStoreIsForAnotherUser:
case LoadStreamStatus::kAbortWithPendingClearAll:
case LoadStreamStatus::kAlreadyHaveUnreadContent:
+ case LoadStreamStatus::kLoadNotAllowedDisabled:
break;
}
return feedui::ZeroStateSlice::NO_CARDS_AVAILABLE;
diff --git a/chromium/components/feed/core/v2/tasks/load_stream_from_store_task.cc b/chromium/components/feed/core/v2/tasks/load_stream_from_store_task.cc
index bda6f54bf09..d6ee6304d36 100644
--- a/chromium/components/feed/core/v2/tasks/load_stream_from_store_task.cc
+++ b/chromium/components/feed/core/v2/tasks/load_stream_from_store_task.cc
@@ -35,12 +35,14 @@ LoadStreamFromStoreTask::LoadStreamFromStoreTask(
const StreamType& stream_type,
FeedStore* store,
bool missed_last_refresh,
+ bool is_web_feed_subscriber,
base::OnceCallback<void(Result)> callback)
: load_type_(load_type),
feed_stream_(*feed_stream),
stream_type_(stream_type),
store_(store),
missed_last_refresh_(missed_last_refresh),
+ is_web_feed_subscriber_(is_web_feed_subscriber),
result_callback_(std::move(callback)),
update_request_(std::make_unique<StreamModelUpdateRequest>()) {}
@@ -67,16 +69,8 @@ void LoadStreamFromStoreTask::LoadStreamDone(
return;
}
if (!ignore_account_) {
- const AccountInfo& account_info = feed_stream_.GetAccountInfo();
- if (result.stream_data.signed_in() && result.stream_data.gaia().empty()) {
- // TODO(crbug.com/1268575): For backward compatibility, set the gaia in
- // stream_data if it is unset. Remove this code after it's been in at
- // least one Chrome release.
- result.stream_data.set_gaia(account_info.gaia);
- result.stream_data.set_email(account_info.email);
- }
-
if (result.stream_data.signed_in()) {
+ const AccountInfo& account_info = feed_stream_.GetAccountInfo();
if (result.stream_data.gaia() != account_info.gaia ||
result.stream_data.email() != account_info.email) {
Complete(LoadStreamStatus::kDataInStoreIsForAnotherUser,
@@ -93,7 +87,8 @@ void LoadStreamFromStoreTask::LoadStreamDone(
const feedstore::Metadata& metadata = feed_stream_.GetMetadata();
- if (ContentInvalidFromAge(metadata, result.stream_type, content_age_)) {
+ if (ContentInvalidFromAge(metadata, result.stream_type, content_age_,
+ is_web_feed_subscriber_)) {
Complete(LoadStreamStatus::kDataInStoreIsExpired,
feedwire::DiscoverCardReadCacheResult::STALE);
return;
@@ -101,7 +96,7 @@ void LoadStreamFromStoreTask::LoadStreamDone(
if (content_age_.is_negative()) {
stale_reason_ = LoadStreamStatus::kDataInStoreIsStaleTimestampInFuture;
} else if (ShouldWaitForNewContent(metadata, result.stream_type,
- content_age_)) {
+ content_age_, is_web_feed_subscriber_)) {
stale_reason_ = LoadStreamStatus::kDataInStoreIsStale;
} else if (missed_last_refresh_) {
stale_reason_ = LoadStreamStatus::kDataInStoreStaleMissedLastRefresh;
diff --git a/chromium/components/feed/core/v2/tasks/load_stream_from_store_task.h b/chromium/components/feed/core/v2/tasks/load_stream_from_store_task.h
index 9c80be3b922..255272f4ef1 100644
--- a/chromium/components/feed/core/v2/tasks/load_stream_from_store_task.h
+++ b/chromium/components/feed/core/v2/tasks/load_stream_from_store_task.h
@@ -62,6 +62,7 @@ class LoadStreamFromStoreTask : public offline_pages::Task {
const StreamType& stream_type,
FeedStore* store,
bool missed_last_refresh,
+ bool is_web_feed_subscriber,
base::OnceCallback<void(Result)> callback);
~LoadStreamFromStoreTask() override;
LoadStreamFromStoreTask(const LoadStreamFromStoreTask&) = delete;
@@ -72,7 +73,6 @@ class LoadStreamFromStoreTask : public offline_pages::Task {
private:
void Run() override;
-
void LoadStreamDone(FeedStore::LoadStreamResult);
void LoadContentDone(std::vector<feedstore::Content> content,
std::vector<feedstore::StreamSharedState> shared_states);
@@ -91,6 +91,7 @@ class LoadStreamFromStoreTask : public offline_pages::Task {
bool ignore_staleness_ = false;
bool missed_last_refresh_ = false;
bool ignore_account_ = false;
+ bool is_web_feed_subscriber_ = false;
base::OnceCallback<void(Result)> result_callback_;
// Data to be stuffed into the Result when the task is complete.
diff --git a/chromium/components/feed/core/v2/tasks/load_stream_task.cc b/chromium/components/feed/core/v2/tasks/load_stream_task.cc
index 42570910982..4256cce580b 100644
--- a/chromium/components/feed/core/v2/tasks/load_stream_task.cc
+++ b/chromium/components/feed/core/v2/tasks/load_stream_task.cc
@@ -52,6 +52,8 @@ feedwire::FeedQuery::RequestReason GetRequestReason(
// TODO(b/185848601): Switch back to PREFETCHED_WEB_FEED when
// the server supports it.
: feedwire::FeedQuery::INTERACTIVE_WEB_FEED;
+ case LoadType::kFeedCloseBackgroundRefresh:
+ return feedwire::FeedQuery::APP_CLOSE_REFRESH;
case LoadType::kLoadMore:
NOTREACHED();
return feedwire::FeedQuery::MANUAL_REFRESH;
@@ -172,7 +174,9 @@ bool LoadStreamTask::CheckPreconditions() {
}
void LoadStreamTask::CheckIfSubscriberComplete(bool is_web_feed_subscriber) {
- if (!is_web_feed_subscriber) {
+ is_web_feed_subscriber_ = is_web_feed_subscriber;
+ if (!is_web_feed_subscriber &&
+ !base::FeatureList::IsEnabled(kWebFeedOnboarding)) {
Done({LoadStreamStatus::kNotAWebFeedSubscriber,
feedwire::DiscoverLaunchResult::NOT_A_WEB_FEED_SUBSCRIBER});
return;
@@ -208,7 +212,7 @@ void LoadStreamTask::PassedPreconditions() {
: LoadStreamFromStoreTask::LoadType::kLoadNoContent;
load_from_store_task_ = std::make_unique<LoadStreamFromStoreTask>(
load_from_store_type, &stream_, options_.stream_type, &stream_.GetStore(),
- stream_.MissedLastRefresh(options_.stream_type),
+ stream_.MissedLastRefresh(options_.stream_type), is_web_feed_subscriber_,
base::BindOnce(&LoadStreamTask::LoadFromStoreComplete, GetWeakPtr()));
load_from_store_task_->Execute(base::DoNothing());
}
@@ -322,6 +326,7 @@ void LoadStreamTask::UploadActionsComplete(UploadActionsTask::Result result) {
base::BindOnce(&LoadStreamTask::QueryApiRequestComplete,
GetWeakPtr()));
break;
+ case LoadType::kFeedCloseBackgroundRefresh:
case LoadType::kBackgroundRefresh:
network.SendApiRequest<QueryBackgroundFeedDiscoverApi>(
request, account_info, std::move(request_metadata),
diff --git a/chromium/components/feed/core/v2/tasks/load_stream_task.h b/chromium/components/feed/core/v2/tasks/load_stream_task.h
index a2fc09bfb59..983757ec3f4 100644
--- a/chromium/components/feed/core/v2/tasks/load_stream_task.h
+++ b/chromium/components/feed/core/v2/tasks/load_stream_task.h
@@ -146,6 +146,7 @@ class LoadStreamTask : public offline_pages::Task {
LaunchReliabilityLogger& launch_reliability_logger_;
int64_t server_receive_timestamp_ns_ = 0l;
int64_t server_send_timestamp_ns_ = 0l;
+ bool is_web_feed_subscriber_ = false;
base::WeakPtrFactory<LoadStreamTask> weak_ptr_factory_{this};
};
diff --git a/chromium/components/feed/core/v2/tasks/prefetch_images_task.cc b/chromium/components/feed/core/v2/tasks/prefetch_images_task.cc
index 7ee34ac2cba..d2ff7bb28f2 100644
--- a/chromium/components/feed/core/v2/tasks/prefetch_images_task.cc
+++ b/chromium/components/feed/core/v2/tasks/prefetch_images_task.cc
@@ -50,10 +50,13 @@ void PrefetchImagesTask::Run() {
return;
}
+ // Web feed subscriber is set to true so we don't use the less restrictive
+ // staleness number for when there are no subscriptions.
load_from_store_task_ = std::make_unique<LoadStreamFromStoreTask>(
LoadStreamFromStoreTask::LoadType::kFullLoad, &stream_, kForYouStream,
&stream_.GetStore(),
/*missed_last_refresh=*/false,
+ /*is_web_feed_subscriber=*/true,
base::BindOnce(&PrefetchImagesTask::LoadStreamComplete,
base::Unretained(this)));
diff --git a/chromium/components/feed/core/v2/types.h b/chromium/components/feed/core/v2/types.h
index 884ff3d1be8..ac9791d9e69 100644
--- a/chromium/components/feed/core/v2/types.h
+++ b/chromium/components/feed/core/v2/types.h
@@ -15,6 +15,7 @@
#include "base/types/id_type.h"
#include "base/values.h"
#include "components/feed/core/proto/v2/wire/client_info.pb.h"
+#include "components/feed/core/proto/v2/wire/info_card.pb.h"
#include "components/feed/core/proto/v2/wire/reliability_logging_enums.pb.h"
#include "components/feed/core/v2/enums.h"
#include "components/feed/core/v2/public/common_enums.h"
@@ -58,7 +59,7 @@ struct RequestMetadata {
bool notice_card_acknowledged = false;
bool autoplay_enabled = false;
int followed_from_web_page_menu_count = 0;
- std::vector<std::string> acknowledged_notice_keys;
+ std::vector<feedwire::InfoCardTrackingState> info_card_tracking_states;
};
// Data internal to MetricsReporter which is persisted to Prefs.
diff --git a/chromium/components/feed/core/v2/user_actions_collector.h b/chromium/components/feed/core/v2/user_actions_collector.h
index e51f3b3384b..df76d648303 100644
--- a/chromium/components/feed/core/v2/user_actions_collector.h
+++ b/chromium/components/feed/core/v2/user_actions_collector.h
@@ -45,7 +45,7 @@ class UserActionsCollector {
// Current prefs on the disk. The list is sorted by increasing timestamp.
base::Value visit_metadata_string_list_pref_;
- PrefService* profile_prefs_ = nullptr;
+ raw_ptr<PrefService> profile_prefs_ = nullptr;
};
} // namespace feed
diff --git a/chromium/components/feed/core/v2/web_feed_subscription_coordinator.h b/chromium/components/feed/core/v2/web_feed_subscription_coordinator.h
index 7dcec81ab2a..ade91949e7d 100644
--- a/chromium/components/feed/core/v2/web_feed_subscription_coordinator.h
+++ b/chromium/components/feed/core/v2/web_feed_subscription_coordinator.h
@@ -244,7 +244,7 @@ class WebFeedSubscriptionCoordinator : public WebFeedSubscriptions {
bool fetching_subscribed_web_feeds_ = false;
bool fetching_subscribed_web_feeds_because_stale_ = false;
- HooksForTesting* hooks_for_testing_ = nullptr;
+ raw_ptr<HooksForTesting> hooks_for_testing_ = nullptr;
base::WeakPtrFactory<WebFeedSubscriptionCoordinator> weak_ptr_factory_{this};
};
diff --git a/chromium/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc b/chromium/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
index c5b319fa37c..16ad5559ee6 100644
--- a/chromium/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
+++ b/chromium/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
@@ -11,10 +11,10 @@
#include "base/containers/flat_map.h"
#include "base/strings/strcat.h"
#include "base/strings/string_piece.h"
+#include "base/substring_set_matcher/matcher_string_pattern.h"
#include "components/feed/core/proto/v2/store.pb.h"
#include "components/feed/core/proto/v2/wire/web_feed_matcher.pb.h"
#include "components/feed/core/v2/feedstore_util.h"
-#include "components/url_matcher/string_pattern.h"
#include "components/url_matcher/url_matcher.h"
#include "url/gurl.h"
namespace feed {
@@ -34,7 +34,7 @@ class HostSuffixMatcher {
return host_suffix < other_host_suffix;
}
std::string host_suffix;
- int condition_id;
+ base::MatcherStringPattern::ID condition_id;
};
explicit HostSuffixMatcher(std::vector<Entry> entries) {
@@ -44,7 +44,8 @@ class HostSuffixMatcher {
// Find all host suffixes that match `host_string`, and add the match IDs to
// `match_set`.
- void FindMatches(const std::string& host_string, std::set<int>& match_set) {
+ void FindMatches(const std::string& host_string,
+ std::set<base::MatcherStringPattern::ID>& match_set) {
base::StringPiece host(host_string);
if (host.empty())
return;
@@ -62,7 +63,8 @@ class HostSuffixMatcher {
}
private:
- void FindExactMatches(base::StringPiece prefix, std::set<int>& match_set) {
+ void FindExactMatches(base::StringPiece prefix,
+ std::set<base::MatcherStringPattern::ID>& match_set) {
auto iter = std::lower_bound(entries_.begin(), entries_.end(), prefix);
while (iter != entries_.end() && iter->host_suffix == prefix) {
match_set.insert(iter->condition_id);
@@ -73,7 +75,7 @@ class HostSuffixMatcher {
std::vector<Entry> entries_;
};
-constexpr int kFirstConditionId = 1;
+constexpr base::MatcherStringPattern::ID kFirstConditionId = 1;
// References a set of conditions that all must be true to identify a Web Feed.
// Conditions in this context are represented by unique integer IDs. Each
@@ -111,7 +113,8 @@ class EntrySet {
// Search each 'vertical slice' independently: page URL, page host suffix,
// page suffix, and RSS URL. Collect set of matching IDs.
- std::set<int> matching_ids = page_url_matcher_.MatchURL(page_url);
+ std::set<base::MatcherStringPattern::ID> matching_ids =
+ page_url_matcher_.MatchURL(page_url);
for (const GURL& rss_url : rss_urls) {
auto ids = rss_url_matcher_.MatchURL(rss_url);
matching_ids.insert(ids.begin(), ids.end());
@@ -128,7 +131,7 @@ class EntrySet {
// scan is easier and likely pretty fast since condition_sets_ is dense and
// flat.
- int mcs_first_condition_id = kFirstConditionId;
+ base::MatcherStringPattern::ID mcs_first_condition_id = kFirstConditionId;
int matches_for_mcs = 0;
auto match_iter = matching_ids.begin();
for (size_t i = 0;
@@ -168,8 +171,8 @@ class EntrySet {
// Members that just hold on to memory used in matchers.
url_matcher::URLMatcherConditionFactory condition_factory_;
- std::vector<url_matcher::StringPattern> host_match_patterns_;
- std::vector<url_matcher::StringPattern> path_match_patterns_;
+ std::vector<base::MatcherStringPattern> host_match_patterns_;
+ std::vector<base::MatcherStringPattern> path_match_patterns_;
// List of `MultiConditionSet`. Each one knows how to aggregate match IDs
// reported from matchers below into a WebFeed match.
@@ -363,9 +366,9 @@ class EntrySetBuilder {
return true;
}
- static std::vector<const url_matcher::StringPattern*> MakeVectorOfPointers(
- const std::vector<url_matcher::StringPattern>& patterns) {
- std::vector<const url_matcher::StringPattern*> result(patterns.size());
+ static std::vector<const base::MatcherStringPattern*> MakeVectorOfPointers(
+ const std::vector<base::MatcherStringPattern>& patterns) {
+ std::vector<const base::MatcherStringPattern*> result(patterns.size());
for (size_t i = 0; i < patterns.size(); ++i) {
result[i] = &patterns[i];
}
@@ -376,7 +379,7 @@ class EntrySetBuilder {
std::unique_ptr<EntrySet> entry_set_ = std::make_unique<EntrySet>();
// Temporary state for building `entry_set_`.
- int next_condition_set_id = kFirstConditionId;
+ base::MatcherStringPattern::ID next_condition_set_id = kFirstConditionId;
std::vector<scoped_refptr<url_matcher::URLMatcherConditionSet>>
page_conditions_;
std::vector<scoped_refptr<url_matcher::URLMatcherConditionSet>>
diff --git a/chromium/components/feed/feed_feature_list.cc b/chromium/components/feed/feed_feature_list.cc
index c9a53c5df34..fe43388deb5 100644
--- a/chromium/components/feed/feed_feature_list.cc
+++ b/chromium/components/feed/feed_feature_list.cc
@@ -110,4 +110,20 @@ std::string GetFeedReferrerUrl() {
const base::Feature kPersonalizeFeedUnsignedUsers{
"PersonalizeFeedUnsignedUsers", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kInfoCardAcknowledgementTracking{
+ "InfoCardAcknowledgementTracking", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kShareCrowButton{"ShareCrowButton",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kIsAblated{"FeedAblation",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kFeedCloseRefresh{"FeedCloseRefresh",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+const base::FeatureParam<int> kFeedCloseRefreshDelayMinutes{
+ &kFeedCloseRefresh, "delay_minutes", 30};
+const base::FeatureParam<bool> kFeedCloseRefreshRequireInteraction{
+ &kFeedCloseRefresh, "require_interaction", false};
+
} // namespace feed
diff --git a/chromium/components/feed/feed_feature_list.h b/chromium/components/feed/feed_feature_list.h
index 8f119e4b372..bfd89f1dc79 100644
--- a/chromium/components/feed/feed_feature_list.h
+++ b/chromium/components/feed/feed_feature_list.h
@@ -108,6 +108,27 @@ std::string GetFeedReferrerUrl();
// Personalize feed for unsigned users.
extern const base::Feature kPersonalizeFeedUnsignedUsers;
+// Feature that enables tracking the acknowledgement state for the info cards.
+extern const base::Feature kInfoCardAcknowledgementTracking;
+
+// Feature that enables the Crow feature.
+// Owned by the CwF team but located here until it makes sense to create a crow
+// component, since it is being used in the feed component.
+extern const base::Feature kShareCrowButton;
+
+// Feature that when enabled completely removes all Feeds from chrome.
+extern const base::Feature kIsAblated;
+
+// When enabled, schedule a background refresh for a feed sometime after the
+// last user engagement with that feed.
+extern const base::Feature kFeedCloseRefresh;
+// On each qualifying user engagement, schedule a background refresh this many
+// minutes out.
+extern const base::FeatureParam<int> kFeedCloseRefreshDelayMinutes;
+// If true, schedule the refresh only when the user scrolls or interacts. If
+// false, schedule only when the feed surface is opened to content.
+extern const base::FeatureParam<bool> kFeedCloseRefreshRequireInteraction;
+
} // namespace feed
#endif // COMPONENTS_FEED_FEED_FEATURE_LIST_H_
diff --git a/chromium/components/feed/mojom/OWNERS b/chromium/components/feed/mojom/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/components/feed/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/feedback/OWNERS b/chromium/components/feedback/OWNERS
index 3de1c82a1b9..b01a6b0c049 100644
--- a/chromium/components/feedback/OWNERS
+++ b/chromium/components/feedback/OWNERS
@@ -1,4 +1,5 @@
# Primary OWNERS
+xiangdongkong@google.com
jimmyxgong@chromium.org
# Backup OWNERS
@@ -6,6 +7,3 @@ zentaro@chromium.org
afakhry@chromium.org
iby@chromium.org
mutexlox@chromium.org
-
-per-file redaction_tool*=iby@chromium.org
-per-file redaction_tool*=mutexlox@chromium.org
diff --git a/chromium/components/feedback/feedback_report.cc b/chromium/components/feedback/feedback_report.cc
index 561279e13a5..b1179835a6d 100644
--- a/chromium/components/feedback/feedback_report.cc
+++ b/chromium/components/feedback/feedback_report.cc
@@ -108,8 +108,7 @@ void FeedbackReport::LoadReportsAndQueue(const base::FilePath& user_dir,
}
void FeedbackReport::DeleteReportOnDisk() {
- reports_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(base::GetDeleteFileCallback(), file_));
+ reports_task_runner_->PostTask(FROM_HERE, base::GetDeleteFileCallback(file_));
}
FeedbackReport::~FeedbackReport() {}
diff --git a/chromium/components/feedback/feedback_uploader.cc b/chromium/components/feedback/feedback_uploader.cc
index 1268231ae86..ad917116463 100644
--- a/chromium/components/feedback/feedback_uploader.cc
+++ b/chromium/components/feedback/feedback_uploader.cc
@@ -14,7 +14,6 @@
#include "components/feedback/feedback_switches.h"
#include "components/variations/net/variations_http_headers.h"
#include "net/base/load_flags.h"
-#include "net/url_request/url_fetcher.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
diff --git a/chromium/components/feedback/pii_types.h b/chromium/components/feedback/pii_types.h
index 8a6425ad23b..0e8e159571f 100644
--- a/chromium/components/feedback/pii_types.h
+++ b/chromium/components/feedback/pii_types.h
@@ -18,26 +18,23 @@ enum class PIIType {
// components following <package_name>/ are app specific and will be
// considered as PII sensitive data.
kAndroidAppStoragePath,
- // BSSID (Basic Service Set Identifier) of a wifi service.
- kBSSID,
- // Unique identifier of the cell of the Cell tower object that's used by
- // ModemManager.
- kCellID,
// Email addresses.
kEmail,
// GAIA (Google Accounts and ID Administration) ID. Gaia ID is a 64-bit
// integer.
kGaiaID,
- // Hexadecimal strings of length 32, 40 and 64 are considered to be hashes.
- kHash,
// IPP (Internet Printing Protocol) Addresses.
kIPPAddress,
// IP (Internet Protocol) address. Stores data in two versions: IPv4 (e.g.
// 127.0.0.1) or IPv6 (e.g. 2001:0db8:85a3:0000:0000:8a2e:0370:7334).
kIPAddress,
- // The Location Area Code (LAC) for GSM and WCDMA networks of the Cell tower
- // object that's used by ModemManager.
- kLocationAreaCode,
+ // Location information related to Cell tower object that's used by
+ // ModemManager. Contains two type of data:
+ // 1- The Location Area Code (LAC) for GSM and WCDMA networks of the Cell
+ // tower object that's used by ModemManager.
+ // 2- Cell ID as unique identifier of the cell of the Cell tower object that's
+ // used by ModemManager.
+ kLocationInfo,
// MAC address is a unique identifier assigned to a network interface
// controller (NIC) for use as a network address in communications within a
// network segment (e.g 00:00:5e:00:53:af). MAC addresses with general meaning
@@ -48,14 +45,20 @@ enum class PIIType {
kUIHierarchyWindowTitles,
// URLs that can appear in logs.
kURL,
- // Universal Unique Identifiers (UUIDs). UUID can also be given by 'blkid',
- // 'lvs' and 'pvs' tools.
- kUUID,
// Serial numbers.
kSerial,
// SSID (Service Set Identifier) of wifi networks can be detected in the logs
- // provided by wpa_supplicant and shill.
+ // provided by wpa_supplicant and shill. BSSID (Basic Service Set Identifier)
+ // of a wifi service, is also categorized under this.
kSSID,
+ // Stable identifiers. Contains information in these main categories:
+ // 1- Universal Unique Identifiers (UUIDs): UUID can also be given by 'blkid',
+ // 'lvs' and 'pvs' tools.
+ // 2- Hashes: Hexadecimal strings of length 32, 40 and 64 are considered to be
+ // hashes.
+ // 3- Eche specific UID which is a base46 conversion of a 32 byte binary value
+ // generated by `ash/webui/eche_app_ui/eche_uid_provider.cc`
+ kStableIdentifier,
// Volume labels presented in the 'blkid' tool, and as part of removable
// media paths shown in various logs such as cros-disks (in syslog).
kVolumeLabel,
@@ -63,4 +66,4 @@ enum class PIIType {
} // namespace feedback
-#endif // COMPONENTS_FEEDBACK_PII_TYPES_H_ \ No newline at end of file
+#endif // COMPONENTS_FEEDBACK_PII_TYPES_H_
diff --git a/chromium/components/feedback/redaction_tool.cc b/chromium/components/feedback/redaction_tool.cc
index c26504b8ef4..4e96026357c 100644
--- a/chromium/components/feedback/redaction_tool.cc
+++ b/chromium/components/feedback/redaction_tool.cc
@@ -49,9 +49,9 @@ namespace {
// (?:regex) denotes non-capturing parentheses group.
CustomPatternWithAlias kCustomPatternsWithContext[] = {
// ModemManager
- {"CellID", "(\\bCell ID: ')([0-9a-fA-F]+)(')", PIIType::kCellID},
+ {"CellID", "(\\bCell ID: ')([0-9a-fA-F]+)(')", PIIType::kLocationInfo},
{"LocAC", "(\\bLocation area code: ')([0-9a-fA-F]+)(')",
- PIIType::kLocationAreaCode},
+ PIIType::kLocationInfo},
// wpa_supplicant
{"SSID", "(?i-s)(\\bssid[= ]')(.+)(')", PIIType::kSSID},
@@ -87,10 +87,11 @@ CustomPatternWithAlias kCustomPatternsWithContext[] = {
// UUIDs given by the 'blkid' tool. These don't necessarily look like
// standard UUIDs, so treat them specially.
- {"UUID", R"xxx((UUID=")([0-9a-zA-Z-]+)("))xxx", PIIType::kUUID},
+ {"UUID", R"xxx((UUID=")([0-9a-zA-Z-]+)("))xxx", PIIType::kStableIdentifier},
// Also cover UUIDs given by the 'lvs' and 'pvs' tools, which similarly
// don't necessarily look like standard UUIDs.
- {"UUID", R"xxx(("[lp]v_uuid":")([0-9a-zA-Z-]+)("))xxx", PIIType::kUUID},
+ {"UUID", R"xxx(("[lp]v_uuid":")([0-9a-zA-Z-]+)("))xxx",
+ PIIType::kStableIdentifier},
// Volume labels presented in the 'blkid' tool, and as part of removable
// media paths shown in various logs such as cros-disks (in syslog).
@@ -387,7 +388,11 @@ CustomPatternWithAlias kCustomPatternsWithoutContext[] = {
{"UUID",
"(?i)([0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-"
"[0-9a-zA-Z]{12})",
- PIIType::kUUID},
+ PIIType::kStableIdentifier},
+ // Eche UID which is a base64 conversion of a 32 bytes public key.
+ {"UID",
+ "((?:[A-Za-z0-9+/]{4}){10}(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=))",
+ PIIType::kStableIdentifier},
};
// Like RE2's FindAndConsume, searches for the first occurrence of |pattern| in
@@ -503,10 +508,11 @@ std::string RedactionTool::RedactAndKeepSelected(
// Do hashes last since they may appear in URLs and they also prevent us
// from properly recognizing the Android storage paths.
- if (pii_types_to_keep.find(PIIType::kHash) == pii_types_to_keep.end()) {
+ if (pii_types_to_keep.find(PIIType::kStableIdentifier) ==
+ pii_types_to_keep.end()) {
// URLs and Android storage paths will be partially redacted (only hashes)
// if |pii_types_to_keep| contains PIIType::kURL or
- // PIIType::kAndroidAppStoragePath and not PIIType::kHash.
+ // PIIType::kAndroidAppStoragePath and not PIIType::kStableIdentifier.
redacted = RedactHashes(std::move(redacted), nullptr);
}
return redacted;
@@ -624,7 +630,7 @@ std::string RedactionTool::RedactHashes(
hashes_[hash] = replacement_hash;
}
if (detected != nullptr) {
- (*detected)[PIIType::kHash].insert(hash);
+ (*detected)[PIIType::kStableIdentifier].insert(hash);
}
result += replacement_hash;
diff --git a/chromium/components/feedback/redaction_tool_unittest.cc b/chromium/components/feedback/redaction_tool_unittest.cc
index ede23654aad..d26d6e3536e 100644
--- a/chromium/components/feedback/redaction_tool_unittest.cc
+++ b/chromium/components/feedback/redaction_tool_unittest.cc
@@ -36,7 +36,8 @@ const StringWithRedaction kStringsWithRedactions[] = {
"aaaaaaaa [SSID=<SSID: 1>]aaaaa", PIIType::kSSID},
{"aaaaaaaahttp://tets.comaaaaaaa", // URL.
"aaaaaaaa<URL: 1>", PIIType::kURL},
- {"u:object_r:system_data_file:s0:c512,c768", // No PII, it is an SELinux context.
+ {"u:object_r:system_data_file:s0:c512,c768", // No PII, it is an SELinux
+ // context.
"u:object_r:system_data_file:s0:c512,c768", PIIType::kNone},
{"aaaaaemail@example.comaaa", // Email address.
"<email: 1>", PIIType::kEmail},
@@ -181,7 +182,9 @@ const StringWithRedaction kStringsWithRedactions[] = {
{"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js?bar=x",
"<URL: 3>", PIIType::kURL}, // Potentially PII in parameter.
{"/root/27540283740a0897ab7c8de0f809add2bacde78f/foo",
- "/root/<HASH:2754 1>/foo", PIIType::kHash}, // Hash string.
+ "/root/<HASH:2754 1>/foo", PIIType::kStableIdentifier}, // Hash string.
+ {"B3mcFTkQAHofv94DDTUuVJGGEI/BbzsyDncplMCR2P4=", "<UID: 1>",
+ PIIType::kStableIdentifier},
#if BUILDFLAG(IS_CHROMEOS_ASH) // We only redact Android paths on Chrome OS.
// Allowed android storage path.
{"112K\t/home/root/deadbeef1234/android-data/data/system_de",
@@ -468,11 +471,11 @@ TEST_F(RedactionToolTest, RedactCustomPatternWithContext) {
// The PIIType for the CustomPatternWithAlias is not relevant, only for
// testing.
const CustomPatternWithAlias kPattern1 = {"ID", "(\\b(?i)id:? ')(\\d+)(')",
- PIIType::kUUID};
+ PIIType::kStableIdentifier};
const CustomPatternWithAlias kPattern2 = {"ID", "(\\b(?i)id=')(\\d+)(')",
- PIIType::kUUID};
+ PIIType::kStableIdentifier};
const CustomPatternWithAlias kPattern3 = {"IDG", "(\\b(?i)idg=')(\\d+)(')",
- PIIType::kCellID};
+ PIIType::kLocationInfo};
EXPECT_EQ("", RedactCustomPatternWithContext("", kPattern1));
EXPECT_EQ("foo\nbar\n",
RedactCustomPatternWithContext("foo\nbar\n", kPattern1));
@@ -543,15 +546,23 @@ TEST_F(RedactionToolTest, RedactAndKeepSelected) {
// will be redacted with the URL or Android storage path that they're part of.
std::string redaction_output_mac_and_hashes;
for (const auto& s : kStringsWithRedactions) {
- if (s.pii_type == PIIType::kMACAddress || s.pii_type == PIIType::kHash) {
+ if (s.pii_type == PIIType::kMACAddress ||
+ s.pii_type == PIIType::kStableIdentifier) {
redaction_output_mac_and_hashes.append(s.pre_redaction).append("\n");
} else {
redaction_output_mac_and_hashes.append(s.post_redaction).append("\n");
}
}
- EXPECT_EQ(redaction_output_mac_and_hashes,
+ EXPECT_EQ(
+ redaction_output_mac_and_hashes,
+ redactor_.RedactAndKeepSelected(
+ redaction_input, {PIIType::kMACAddress, PIIType::kStableIdentifier}));
+}
+
+TEST_F(RedactionToolTest, RedactUid) {
+ EXPECT_EQ("<UID: 1>",
redactor_.RedactAndKeepSelected(
- redaction_input, {PIIType::kMACAddress, PIIType::kHash}));
+ "B3mcFTkQAHofv94DDTUuVJGGEI/BbzsyDncplMCR2P4=", {}));
}
TEST_F(RedactionToolTest, RedactAndKeepSelectedHashes) {
@@ -652,7 +663,10 @@ TEST_F(RedactionToolTest, DetectPII) {
"::0101:ffff:c0a8:640a",
}},
{PIIType::kMACAddress, {"aa:aa:aa:aa:aa:aa"}}, {
- PIIType::kHash, { "27540283740a0897ab7c8de0f809add2bacde78f" }
+ PIIType::kStableIdentifier, {
+ "27540283740a0897ab7c8de0f809add2bacde78f",
+ "B3mcFTkQAHofv94DDTUuVJGGEI/BbzsyDncplMCR2P4=",
+ }
}
};
diff --git a/chromium/components/find_in_page/android/BUILD.gn b/chromium/components/find_in_page/android/BUILD.gn
index 1f83721a240..4a0c910105d 100644
--- a/chromium/components/find_in_page/android/BUILD.gn
+++ b/chromium/components/find_in_page/android/BUILD.gn
@@ -18,6 +18,8 @@ android_library("java") {
":java_resources",
":jni_headers",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_java",
"//ui/android:ui_java",
]
diff --git a/chromium/components/find_in_page/find_tab_helper.cc b/chromium/components/find_in_page/find_tab_helper.cc
index 4aa33ba4f8f..a1d8477ccff 100644
--- a/chromium/components/find_in_page/find_tab_helper.cc
+++ b/chromium/components/find_in_page/find_tab_helper.cc
@@ -127,8 +127,9 @@ void FindTabHelper::StopFinding(SelectionAction selection_action) {
}
void FindTabHelper::ActivateFindInPageResultForAccessibility() {
- GetWebContents().GetMainFrame()->ActivateFindInPageResultForAccessibility(
- current_find_request_id_);
+ GetWebContents()
+ .GetPrimaryMainFrame()
+ ->ActivateFindInPageResultForAccessibility(current_find_request_id_);
}
std::u16string FindTabHelper::GetInitialSearchText() {
diff --git a/chromium/components/flags_ui/PRESUBMIT.py b/chromium/components/flags_ui/PRESUBMIT.py
deleted file mode 100644
index 864735f0efc..00000000000
--- a/chromium/components/flags_ui/PRESUBMIT.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-USE_PYTHON3 = True
-
-
-def _CommonChecks(input_api, output_api):
- results = []
- try:
- import sys
- old_sys_path = sys.path[:]
- cwd = input_api.PresubmitLocalPath()
- sys.path += [input_api.os_path.join(cwd, '..', '..', 'tools')]
- import web_dev_style.presubmit_support
- results += web_dev_style.presubmit_support.CheckStyle(input_api, output_api)
- finally:
- sys.path = old_sys_path
- return results
-
-
-def CheckChangeOnUpload(input_api, output_api):
- return _CommonChecks(input_api, output_api)
-
-
-def CheckChangeOnCommit(input_api, output_api):
- return _CommonChecks(input_api, output_api)
diff --git a/chromium/components/flags_ui/feature_entry.cc b/chromium/components/flags_ui/feature_entry.cc
index 882dc41b533..e48ea7cbb9a 100644
--- a/chromium/components/flags_ui/feature_entry.cc
+++ b/chromium/components/flags_ui/feature_entry.cc
@@ -42,6 +42,10 @@ bool FeatureEntry::InternalNameMatches(const std::string& name) const {
case FeatureEntry::ENABLE_DISABLE_VALUE:
case FeatureEntry::FEATURE_VALUE:
case FeatureEntry::FEATURE_WITH_PARAMS_VALUE:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ case FeatureEntry::PLATFORM_FEATURE_NAME_VALUE:
+ case FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE:
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
// Check that the pattern matches what's produced by NameForOption().
int index = -1;
return name.size() > internal_name_length + 1 &&
@@ -55,11 +59,17 @@ int FeatureEntry::NumOptions() const {
switch (type) {
case ENABLE_DISABLE_VALUE:
case FEATURE_VALUE:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ case PLATFORM_FEATURE_NAME_VALUE:
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
return 3;
case MULTI_VALUE:
return choices.size();
case FEATURE_WITH_PARAMS_VALUE:
- return 3 + feature.feature_variations.size();
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ case PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE:
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ return 3 + GetVariations().size();
default:
return 0;
}
@@ -69,7 +79,12 @@ std::string FeatureEntry::NameForOption(int index) const {
DCHECK(type == FeatureEntry::MULTI_VALUE ||
type == FeatureEntry::ENABLE_DISABLE_VALUE ||
type == FeatureEntry::FEATURE_VALUE ||
- type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE);
+ type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
+ type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ );
DCHECK_LT(index, NumOptions());
return std::string(internal_name) + testing::kMultiSeparator +
base::NumberToString(index);
@@ -83,18 +98,31 @@ std::u16string FeatureEntry::DescriptionForOption(int index) const {
DCHECK(type == FeatureEntry::MULTI_VALUE ||
type == FeatureEntry::ENABLE_DISABLE_VALUE ||
type == FeatureEntry::FEATURE_VALUE ||
- type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE);
+ type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
+ type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ );
DCHECK_LT(index, NumOptions());
const char* description = nullptr;
if (type == FeatureEntry::ENABLE_DISABLE_VALUE ||
- type == FeatureEntry::FEATURE_VALUE) {
+ type == FeatureEntry::FEATURE_VALUE
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE
+#endif
+ ) {
const char* const kEnableDisableDescriptions[] = {
kGenericExperimentChoiceDefault,
kGenericExperimentChoiceEnabled,
kGenericExperimentChoiceDisabled,
};
description = kEnableDisableDescriptions[index];
- } else if (type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE) {
+ } else if (type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ || type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
+#endif
+ ) {
if (index == 0) {
description = kGenericExperimentChoiceDefault;
} else if (index == 1) {
@@ -102,9 +130,9 @@ std::u16string FeatureEntry::DescriptionForOption(int index) const {
} else if (index < NumOptions() - 1) {
// First two options do not have variations params.
int variation_index = index - 2;
- return base::ASCIIToUTF16(base::StrCat(
- {kGenericExperimentChoiceEnabled, " ",
- feature.feature_variations[variation_index].description_text}));
+ return base::ASCIIToUTF16(
+ base::StrCat({kGenericExperimentChoiceEnabled, " ",
+ GetVariations()[variation_index].description_text}));
} else {
DCHECK_EQ(NumOptions() - 1, index);
description = kGenericExperimentChoiceDisabled;
@@ -124,7 +152,12 @@ const FeatureEntry::Choice& FeatureEntry::ChoiceForOption(int index) const {
FeatureEntry::FeatureState FeatureEntry::StateForOption(int index) const {
DCHECK(type == FeatureEntry::FEATURE_VALUE ||
- type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE);
+ type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
+ type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
+#endif
+ );
DCHECK_LT(index, NumOptions());
if (index == 0)
@@ -137,16 +170,27 @@ FeatureEntry::FeatureState FeatureEntry::StateForOption(int index) const {
const FeatureEntry::FeatureVariation* FeatureEntry::VariationForOption(
int index) const {
DCHECK(type == FeatureEntry::FEATURE_VALUE ||
- type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE);
+ type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ || type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
+ type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
+#endif
+ );
DCHECK_LT(index, NumOptions());
- if (type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE && index > 1 &&
- index < NumOptions() - 1) {
- // We have no variations for FEATURE_VALUE type. Option at |index|
- // corresponds to variation at |index| - 2 as the list starts with "Default"
- // and "Enabled" (with default parameters).
- return &feature.feature_variations[index - 2];
+ if ((type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ || type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
+#endif
+ ) &&
+ index > 1 && index < NumOptions() - 1) {
+ // We have no variations for FEATURE_VALUE type or
+ // PLATFORM_FEATURE_NAME_VALUE type. Option at |index| corresponds to
+ // variation at |index| - 2 as the list starts with "Default" and "Enabled"
+ // (with default parameters).
+ return &GetVariations()[index - 2];
}
+
return nullptr;
}
@@ -208,11 +252,47 @@ bool FeatureEntry::IsValid() const {
return false;
}
return true;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ case FeatureEntry::PLATFORM_FEATURE_NAME_VALUE:
+ if (!platform_feature_name.name) {
+ LOG(ERROR) << "no feature name is set";
+ return false;
+ }
+ return true;
+ case FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE:
+ if (!platform_feature_name.name) {
+ LOG(ERROR) << "no feature name is set";
+ return false;
+ }
+ if (platform_feature_name.feature_variations.size() == 0) {
+ LOG(ERROR) << "feature_variations is empty";
+ return false;
+ }
+ if (!platform_feature_name.feature_trial_name) {
+ LOG(ERROR) << "feature_trial_name is null";
+ return false;
+ }
+ return true;
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
NOTREACHED();
return false;
}
+const base::span<const FeatureEntry::FeatureVariation>
+FeatureEntry::GetVariations() const {
+ if (type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE) {
+ return feature.feature_variations;
+ }
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ if (type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE) {
+ return platform_feature_name.feature_variations;
+ }
+#endif
+ NOTREACHED();
+ return base::span<const FeatureEntry::FeatureVariation>();
+}
+
namespace testing {
const char kMultiSeparator[] = {kMultiSeparatorChar, '\0'};
diff --git a/chromium/components/flags_ui/feature_entry.h b/chromium/components/flags_ui/feature_entry.h
index c7d9a177db7..855e9ec22d3 100644
--- a/chromium/components/flags_ui/feature_entry.h
+++ b/chromium/components/flags_ui/feature_entry.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/containers/span.h"
+#include "build/chromeos_buildflags.h"
namespace base {
struct Feature;
@@ -74,15 +75,38 @@ struct FeatureEntry {
// passed from the server in a trial config). When set to Enabled, the
// feature is overriden to be enabled and empty set of parameters is used
// boiling down to the default behavior in the code.
- // TODO(crbug.com/805766): The resulting chrome://flags entries will not
- // work on Chrome OS devices (but will work in the CrOS-emulated build on
- // Linux).
FEATURE_WITH_PARAMS_VALUE,
// Corresponds to a command line switch where the value is treated as a list
// of url::Origins. (Lists will not be reordered.) Default state is
// disabled like SINGLE_VALUE.
ORIGIN_LIST_VALUE,
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // The below two types are for *platform* features -- that is, those defined
+ // and queried via platform2/featured/feature_library.h. Such features
+ // should be defined outside of the browser (e.g., in platform2 or
+ // platform) using a compile-time-constant default value and name.
+ // See feature_library.h for more documentation.
+
+ // Corresponds to a feature *name*, starting with "CrOSLateBoot", for a
+ // platform feature.
+ //
+ // Broadly similar to FEATURE_VALUE, but we cannot define |base::Feature|s
+ // starting with CrOSLateBoot in the browser directly -- they must instead
+ // be defined and queried outside of the browser, using
+ // platform2/featured/feature_library.h.
+ PLATFORM_FEATURE_NAME_VALUE,
+
+ // Corresponds to a feature *name*, starting with "CrOSLateBoot", for a
+ // platform feature, along with its parameters.
+ //
+ // Broadly similar to FEATURE_WITH_PARAMS_VALUE, but we cannot define
+ // |base::Feature|s starting with CrOSLateBoot in the browser directly --
+ // they must instead be defined and queried outside of the browser, using
+ // platform2/featured/feature_library.h.
+ PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE,
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
};
// Describes state of a feature.
@@ -184,6 +208,25 @@ struct FeatureEntry {
const char* feature_trial_name;
} feature;
+ struct {
+ // For PLATFORM_FEATURE_NAME_TYPE or
+ // PLATFORM_FEATURE_WITH_PARAMS_VALUE_TYPE, the name of the feature this
+ // entry corresponds to. The same feature must not be used in multiple
+ // FeatureEntries.
+ const char* name;
+
+ // This describes the options if type is
+ // PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE.
+ // The first variation is the default "Enabled" variation, its
+ // description_id is disregarded.
+ base::span<const FeatureVariation> feature_variations;
+
+ // The name of the FieldTrial in which the selected variation parameters
+ // should be registered. This is used if type is
+ // PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE.
+ const char* feature_trial_name;
+ } platform_feature_name;
+
// This describes the options if type is MULTI_VALUE.
base::span<const Choice> choices;
};
@@ -221,6 +264,10 @@ struct FeatureEntry {
// Returns true if the entry is considered as valid.
// See the implenetation for the details of what is valid.
bool IsValid() const;
+
+ // Returns the variation list. Type must be either
+ // FEATURE_WITH_PARAMS_VALUE or PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE.
+ const base::span<const FeatureVariation> GetVariations() const;
};
namespace testing {
diff --git a/chromium/components/flags_ui/feature_entry_macros.h b/chromium/components/flags_ui/feature_entry_macros.h
index accb85c70dd..6a0e37e83f7 100644
--- a/chromium/components/flags_ui/feature_entry_macros.h
+++ b/chromium/components/flags_ui/feature_entry_macros.h
@@ -4,7 +4,7 @@
#ifndef COMPONENTS_FLAGS_UI_FEATURE_ENTRY_MACROS_H_
#define COMPONENTS_FLAGS_UI_FEATURE_ENTRY_MACROS_H_
-
+#include "build/chromeos_buildflags.h"
// Macros to simplify specifying the type of FeatureEntry. Please refer to
// the comments on FeatureEntry::Type in feature_entry.h, which explain the
@@ -44,4 +44,16 @@
.feature = { &feature_entry, feature_variations, feature_trial } \
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#define PLATFORM_FEATURE_NAME_TYPE(name) \
+ flags_ui::FeatureEntry::PLATFORM_FEATURE_NAME_VALUE, { \
+ .platform_feature_name = { name, {}, nullptr } \
+ }
+#define PLATFORM_FEATURE_WITH_PARAMS_VALUE_TYPE(name, feature_variations, \
+ feature_trial) \
+ flags_ui::FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE, { \
+ .platform_feature_name = { name, feature_variations, feature_trial } \
+ }
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
#endif // COMPONENTS_FLAGS_UI_FEATURE_ENTRY_MACROS_H_
diff --git a/chromium/components/flags_ui/flags_state.cc b/chromium/components/flags_ui/flags_state.cc
index 8a8a528080d..f8b4990f684 100644
--- a/chromium/components/flags_ui/flags_state.cc
+++ b/chromium/components/flags_ui/flags_state.cc
@@ -12,6 +12,7 @@
#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
+#include "base/feature_list_buildflags.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/stl_util.h"
@@ -21,12 +22,12 @@
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
#include "components/flags_ui/feature_entry.h"
#include "components/flags_ui/flags_storage.h"
#include "components/flags_ui/flags_ui_switches.h"
#include "components/variations/field_trial_config/field_trial_util.h"
#include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_switches.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#include "url/origin.h"
@@ -78,6 +79,10 @@ bool IsDefaultValue(const FeatureEntry& entry,
case FeatureEntry::ENABLE_DISABLE_VALUE:
case FeatureEntry::FEATURE_VALUE:
case FeatureEntry::FEATURE_WITH_PARAMS_VALUE:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ case FeatureEntry::PLATFORM_FEATURE_NAME_VALUE:
+ case FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE:
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
for (int i = 0; i < entry.NumOptions(); ++i) {
if (enabled_entries.count(entry.NameForOption(i)) > 0)
return false;
@@ -94,7 +99,12 @@ base::Value CreateOptionsData(const FeatureEntry& entry,
DCHECK(entry.type == FeatureEntry::MULTI_VALUE ||
entry.type == FeatureEntry::ENABLE_DISABLE_VALUE ||
entry.type == FeatureEntry::FEATURE_VALUE ||
- entry.type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE);
+ entry.type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ || entry.type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
+ entry.type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ );
base::Value result(base::Value::Type::LIST);
for (int i = 0; i < entry.NumOptions(); ++i) {
base::Value value(base::Value::Type::DICTIONARY);
@@ -259,6 +269,10 @@ struct FlagsState::SwitchEntry {
// If |switch_name| is not empty, the value of the switch to set.
std::string switch_value;
+ // If |variation_id| is not empty, variation id value to set.
+ // In the format of VariationsIdsProvider::ForceVariationIds().
+ std::string variation_id;
+
SwitchEntry() : feature_state(false) {}
};
@@ -296,7 +310,8 @@ void FlagsState::ConvertFlagsToSwitches(
void FlagsState::GetSwitchesAndFeaturesFromFlags(
FlagsStorage* flags_storage,
std::set<std::string>* switches,
- std::set<std::string>* features) const {
+ std::set<std::string>* features,
+ std::set<std::string>* variation_ids) const {
std::set<std::string> enabled_entries;
std::map<std::string, SwitchEntry> name_to_switch_map;
GenerateFlagsToSwitchesMapping(flags_storage,
@@ -316,6 +331,9 @@ void FlagsState::GetSwitchesAndFeaturesFromFlags(
features->insert(entry.feature_name + ":enabled");
else
features->insert(entry.feature_name + ":disabled");
+ if (!entry.variation_id.empty()) {
+ variation_ids->insert(entry.variation_id);
+ }
}
}
}
@@ -490,14 +508,29 @@ std::vector<std::string> FlagsState::RegisterEnabledFeatureVariationParameters(
// First collect all the data for each trial.
for (const FeatureEntry& entry : feature_entries) {
- if (entry.type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE) {
+ if (entry.type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ || entry.type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ ) {
for (int j = 0; j < entry.NumOptions(); ++j) {
if (entry.StateForOption(j) == FeatureEntry::FeatureState::ENABLED &&
enabled_entries.count(entry.NameForOption(j))) {
- std::string trial_name = entry.feature.feature_trial_name;
- // The user has chosen to enable the feature by this option.
- enabled_features_by_trial_name[trial_name].insert(
- entry.feature.feature->name);
+ std::string trial_name;
+ if (entry.type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE) {
+ trial_name = entry.feature.feature_trial_name;
+ // The user has chosen to enable the feature by this option.
+ enabled_features_by_trial_name[trial_name].insert(
+ entry.feature.feature->name);
+ }
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ else {
+ trial_name = entry.platform_feature_name.feature_trial_name;
+ // The user has chosen to enable the feature by this option.
+ enabled_features_by_trial_name[trial_name].insert(
+ entry.platform_feature_name.name);
+ }
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
const FeatureEntry::FeatureVariation* variation =
entry.VariationForOption(j);
@@ -593,6 +626,10 @@ void FlagsState::GetFlagFeatureEntries(
case FeatureEntry::ENABLE_DISABLE_VALUE:
case FeatureEntry::FEATURE_VALUE:
case FeatureEntry::FEATURE_WITH_PARAMS_VALUE:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ case FeatureEntry::PLATFORM_FEATURE_NAME_VALUE:
+ case FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE:
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
data.SetKey("options", CreateOptionsData(entry, enabled_entries));
break;
}
@@ -603,7 +640,18 @@ void FlagsState::GetFlagFeatureEntries(
(entry.supported_platforms & kOsCrOSOwnerOnly) != 0) {
supported = true;
}
-#endif
+
+#if BUILDFLAG(ENABLE_BANNED_BASE_FEATURE_PREFIX)
+ if ((entry.type == FeatureEntry::PLATFORM_FEATURE_NAME_VALUE ||
+ entry.type == FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE) &&
+ !base::StartsWith(entry.platform_feature_name.name,
+ BUILDFLAG(BANNED_BASE_FEATURE_PREFIX))) {
+ LOG(ERROR) << "mising required prefix for "
+ << entry.platform_feature_name.name;
+ supported = false;
+ }
+#endif // BUILDFLAG(ENABLED_BANNED_BASE_FEATURE_PREFIX)
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
if (supported)
supported_entries.push_back(std::move(data));
@@ -622,8 +670,9 @@ unsigned short FlagsState::GetCurrentPlatform() {
return kOsWin;
#elif BUILDFLAG(IS_CHROMEOS_ASH)
return kOsCrOS;
-#elif (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) || \
- BUILDFLAG(IS_OPENBSD)
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+ return kOsLacros;
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_OPENBSD)
return kOsLinux;
#elif BUILDFLAG(IS_ANDROID)
return kOsAndroid;
@@ -650,12 +699,14 @@ void FlagsState::AddFeatureMapping(
const std::string& key,
const std::string& feature_name,
bool feature_state,
+ const std::string& variation_id,
std::map<std::string, SwitchEntry>* name_to_switch_map) const {
DCHECK(!base::Contains(*name_to_switch_map, key));
SwitchEntry* entry = &(*name_to_switch_map)[key];
entry->feature_name = feature_name;
entry->feature_state = feature_state;
+ entry->variation_id = variation_id;
}
void FlagsState::AddSwitchesToCommandLine(
@@ -671,6 +722,8 @@ void FlagsState::AddSwitchesToCommandLine(
flags_switches_[switches::kFlagSwitchesBegin] = std::string();
}
+ std::vector<std::string> variation_ids;
+
for (const std::string& entry_name : enabled_entries) {
const auto& entry_it = name_to_switch_map.find(entry_name);
if (entry_it == name_to_switch_map.end()) {
@@ -681,6 +734,9 @@ void FlagsState::AddSwitchesToCommandLine(
const SwitchEntry& entry = entry_it->second;
if (!entry.feature_name.empty()) {
feature_switches[entry.feature_name] = entry.feature_state;
+ if (!entry.variation_id.empty()) {
+ variation_ids.push_back(entry.variation_id);
+ }
} else if (!entry.switch_name.empty()) {
command_line->AppendSwitchASCII(entry.switch_name, entry.switch_value);
flags_switches_[entry.switch_name] = entry.switch_value;
@@ -695,6 +751,10 @@ void FlagsState::AddSwitchesToCommandLine(
MergeFeatureCommandLineSwitch(feature_switches, disable_features_flag_name,
false, command_line);
}
+ if (!variation_ids.empty()) {
+ command_line->AppendSwitchASCII(variations::switches::kForceVariationIds,
+ base::JoinString(variation_ids, ","));
+ }
if (sentinels == kAddSentinels) {
command_line->AppendSwitch(switches::kFlagSwitchesEnd);
@@ -829,17 +889,31 @@ void FlagsState::GenerateFlagsToSwitchesMapping(
case FeatureEntry::FEATURE_VALUE:
case FeatureEntry::FEATURE_WITH_PARAMS_VALUE:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ case FeatureEntry::PLATFORM_FEATURE_NAME_VALUE:
+ case FeatureEntry::PLATFORM_FEATURE_NAME_WITH_PARAMS_VALUE:
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
for (int j = 0; j < entry.NumOptions(); ++j) {
FeatureEntry::FeatureState state = entry.StateForOption(j);
if (state == FeatureEntry::FeatureState::DEFAULT) {
AddFeatureMapping(entry.NameForOption(j), std::string(), false,
- name_to_switch_map);
+ std::string(), name_to_switch_map);
} else {
const FeatureEntry::FeatureVariation* variation =
entry.VariationForOption(j);
- std::string feature_name(entry.feature.feature->name);
+ std::string feature_name;
+ if (entry.type == FeatureEntry::FEATURE_VALUE ||
+ entry.type == FeatureEntry::FEATURE_WITH_PARAMS_VALUE) {
+ feature_name = entry.feature.feature->name;
+ }
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ else {
+ feature_name = entry.platform_feature_name.name;
+ }
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
std::vector<std::string> params_value;
+ std::string variation_id;
if (variation) {
feature_name.append(":");
for (int i = 0; i < variation->num_params; ++i) {
@@ -850,11 +924,14 @@ void FlagsState::GenerateFlagsToSwitchesMapping(
params_value.push_back(
param_name.append("/").append(param_value));
}
+ if (variation->variation_id) {
+ variation_id = variation->variation_id;
+ }
}
AddFeatureMapping(
entry.NameForOption(j),
feature_name.append(base::JoinString(params_value, "/")),
- state == FeatureEntry::FeatureState::ENABLED,
+ state == FeatureEntry::FeatureState::ENABLED, variation_id,
name_to_switch_map);
}
}
diff --git a/chromium/components/flags_ui/flags_state.h b/chromium/components/flags_ui/flags_state.h
index b7b6b3b22c2..cbf4358ed11 100644
--- a/chromium/components/flags_ui/flags_state.h
+++ b/chromium/components/flags_ui/flags_state.h
@@ -115,9 +115,13 @@ class FlagsState {
// switches corresponding to enabled entries and |features| with the set of
// strings corresponding to enabled/disabled base::Feature states. Feature
// names are suffixed with ":enabled" or ":disabled" depending on their state.
- void GetSwitchesAndFeaturesFromFlags(FlagsStorage* flags_storage,
- std::set<std::string>* switches,
- std::set<std::string>* features) const;
+ // Also fills |variation_ids| with variation IDs to force based on
+ // flags_storage, in the format of VariationsIdsProvider::ForceVariationIds().
+ void GetSwitchesAndFeaturesFromFlags(
+ FlagsStorage* flags_storage,
+ std::set<std::string>* switches,
+ std::set<std::string>* features,
+ std::set<std::string>* variation_ids) const;
bool IsRestartNeededToCommitChanges();
void SetFeatureEntryEnabled(FlagsStorage* flags_storage,
@@ -190,11 +194,13 @@ class FlagsState {
std::map<std::string, SwitchEntry>* name_to_switch_map) const;
// Adds mapping to |name_to_switch_map| to toggle base::Feature |feature_name|
- // to state |feature_state|.
+ // to state |feature_state|, along with the given |variation_id|, in the
+ // format of VariationsIdsProvider::ForceVariationIds().
void AddFeatureMapping(
const std::string& key,
const std::string& feature_name,
bool feature_state,
+ const std::string& variation_id,
std::map<std::string, SwitchEntry>* name_to_switch_map) const;
// Updates the switches in |command_line| by applying the modifications
diff --git a/chromium/components/flags_ui/flags_state_unittest.cc b/chromium/components/flags_ui/flags_state_unittest.cc
index 0dd72c150df..ae1ec7c31b4 100644
--- a/chromium/components/flags_ui/flags_state_unittest.cc
+++ b/chromium/components/flags_ui/flags_state_unittest.cc
@@ -30,6 +30,7 @@
#include "components/prefs/testing_pref_service.h"
#include "components/strings/grit/components_strings.h"
#include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace flags_ui {
@@ -97,7 +98,7 @@ const FeatureEntry::FeatureVariation kTestVariations2[] = {
const FeatureEntry::FeatureVariation kTestVariations3[] = {
{"dummy description 1", kTestVariationOther1, 1, nullptr},
{"dummy description 2", kTestVariationOther2, 1, nullptr},
- {"dummy description 3", kTestVariationOther3, 2, nullptr}};
+ {"dummy description 3", kTestVariationOther3, 2, "t123456"}};
const char kTestVariation3Cmdline[] =
"FeatureName3:param1/value/param%3A%2F3/value";
@@ -343,6 +344,11 @@ TEST_F(FlagsStateTest, ConvertFlagsToSwitches) {
EXPECT_TRUE(command_line3.HasSwitch(kEnableFeatures));
EXPECT_EQ(command_line3.GetSwitchValueASCII(kEnableFeatures),
kTestVariation3Cmdline);
+ EXPECT_TRUE(
+ command_line3.HasSwitch(variations::switches::kForceVariationIds));
+ EXPECT_EQ(command_line3.GetSwitchValueASCII(
+ variations::switches::kForceVariationIds),
+ "t123456");
}
TEST_F(FlagsStateTest, RegisterAllFeatureVariationParameters) {
diff --git a/chromium/components/flags_ui/resources/flags.css b/chromium/components/flags_ui/resources/flags.css
index 5a3ad4d38c8..f9cfc01cddb 100644
--- a/chromium/components/flags_ui/resources/flags.css
+++ b/chromium/components/flags_ui/resources/flags.css
@@ -35,7 +35,7 @@ html {
--google-red-300: rgb(242, 139, 130);
--google-red-700: rgb(197, 34, 31);
- --interactive-color: var(--google-blue-500);
+ --interactive-color: var(--google-blue-600);
--primary-color: var(--google-grey-900);
--secondary-color: var(--google-grey-700);
--warning-color: var(--google-red-700);
diff --git a/chromium/components/flags_ui/resources/flags.js b/chromium/components/flags_ui/resources/flags.js
index f01e11fda4c..23ac99ae141 100644
--- a/chromium/components/flags_ui/resources/flags.js
+++ b/chromium/components/flags_ui/resources/flags.js
@@ -234,7 +234,7 @@ function announceStatus(text) {
function resetAllFlags() {
chrome.send('resetAllFlags');
FlagSearch.getInstance().clearSearch();
- announceStatus(loadTimeData.getString("reset-acknowledged"));
+ announceStatus(loadTimeData.getString('reset-acknowledged'));
showRestartToast(true);
requestExperimentalFeaturesData();
}
diff --git a/chromium/components/fuchsia_component_support/BUILD.gn b/chromium/components/fuchsia_component_support/BUILD.gn
new file mode 100644
index 00000000000..cac10950046
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/BUILD.gn
@@ -0,0 +1,59 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+
+visibility = []
+
+# Integration helpers for fuchsia.* FIDL APIs used by Fuchsia Components.
+source_set("fuchsia_component_support") {
+ # Only for use by Fuchsia Components.
+ visibility += [
+ ":unit_tests",
+ "//chromecast/internal/*",
+ "//fuchsia_web/runners/*",
+ "//fuchsia_web/webengine/*",
+ "//fuchsia_web/webinstance_host/*",
+ ]
+ public = [
+ "config_reader.h",
+ "feedback_registration.h",
+ "inspect.h",
+ ]
+ sources = [
+ "config_reader.cc",
+ "feedback_registration.cc",
+ "inspect.cc",
+ ]
+ public_deps = [
+ "//base",
+ "//third_party/abseil-cpp:absl",
+ ]
+ deps = [
+ "//build:branding_buildflags",
+ "//components/version_info",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.feedback",
+ "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+ "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ visibility += [ "//components:components_unittests__exec" ]
+ sources = [
+ "config_reader_unittest.cc",
+ "inspect_unittest.cc",
+ ]
+ deps = [
+ ":fuchsia_component_support",
+ "//base",
+ "//base/test:test_support",
+ "//components/version_info",
+ "//testing/gtest",
+ "//third_party/fuchsia-sdk/sdk/pkg/fdio",
+ "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+ "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp",
+ ]
+}
diff --git a/chromium/components/fuchsia_component_support/DEPS b/chromium/components/fuchsia_component_support/DEPS
new file mode 100644
index 00000000000..857ff62bb28
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/version_info",
+]
diff --git a/chromium/components/fuchsia_component_support/DIR_METADATA b/chromium/components/fuchsia_component_support/DIR_METADATA
new file mode 100644
index 00000000000..210aa6a954b
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//build/fuchsia/COMMON_METADATA"
diff --git a/chromium/components/fuchsia_component_support/OWNERS b/chromium/components/fuchsia_component_support/OWNERS
new file mode 100644
index 00000000000..e7034eabb1e
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/OWNERS
@@ -0,0 +1 @@
+file://build/fuchsia/OWNERS
diff --git a/chromium/components/fuchsia_component_support/README.md b/chromium/components/fuchsia_component_support/README.md
new file mode 100644
index 00000000000..9bf96bc3af0
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/README.md
@@ -0,0 +1,5 @@
+# //components/fuchsia_component_support
+
+This component provides common functionality for
+[Fuchsia Components](https://fuchsia.dev/fuchsia-src/glossary?hl=en#component),
+such as Chrome, WebEngine, and WebRunner. \ No newline at end of file
diff --git a/chromium/components/fuchsia_component_support/config_reader.cc b/chromium/components/fuchsia_component_support/config_reader.cc
new file mode 100644
index 00000000000..46055674356
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/config_reader.cc
@@ -0,0 +1,83 @@
+// 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/fuchsia_component_support/config_reader.h"
+
+#include <string>
+#include <utility>
+
+#include "base/check.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+
+namespace fuchsia_component_support {
+
+namespace {
+
+bool HaveConflicts(const base::Value& dict1, const base::Value& dict2) {
+ for (auto item : dict1.DictItems()) {
+ const base::Value* value = dict2.FindKey(item.first);
+ if (!value)
+ continue;
+ if (!value->is_dict())
+ return true;
+ if (HaveConflicts(item.second, *value))
+ return true;
+ }
+
+ return false;
+}
+
+base::Value ReadConfigFile(const base::FilePath& path) {
+ std::string file_content;
+ bool loaded = base::ReadFileToString(path, &file_content);
+ CHECK(loaded) << "Couldn't read config file: " << path;
+
+ base::JSONReader::ValueWithError parsed =
+ base::JSONReader::ReadAndReturnValueWithError(file_content);
+ CHECK(parsed.value) << "Failed to parse " << path << ": "
+ << parsed.error_message;
+ CHECK(parsed.value->is_dict()) << "Config is not a JSON dictionary: " << path;
+
+ return std::move(*parsed.value);
+}
+
+absl::optional<base::Value> ReadConfigsFromDir(const base::FilePath& dir) {
+ base::FileEnumerator configs(dir, false, base::FileEnumerator::FILES,
+ "*.json");
+ absl::optional<base::Value> config;
+ for (base::FilePath path; !(path = configs.Next()).empty();) {
+ base::Value path_config = ReadConfigFile(path);
+ if (config) {
+ CHECK(!HaveConflicts(*config, path_config));
+ config->MergeDictionary(&path_config);
+ } else {
+ config = std::move(path_config);
+ }
+ }
+
+ return config;
+}
+
+} // namespace
+
+const absl::optional<base::Value>& LoadPackageConfig() {
+ // Package configurations do not change at run-time, so read the configuration
+ // on the first call and cache the result.
+ static base::NoDestructor<absl::optional<base::Value>> config(
+ ReadConfigsFromDir(base::FilePath("/config/data")));
+
+ return *config;
+}
+
+absl::optional<base::Value> LoadConfigFromDirForTest( // IN-TEST
+ const base::FilePath& dir) {
+ return ReadConfigsFromDir(dir);
+}
+
+} // namespace fuchsia_component_support
diff --git a/chromium/components/fuchsia_component_support/config_reader.h b/chromium/components/fuchsia_component_support/config_reader.h
new file mode 100644
index 00000000000..7c69e58c5f6
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/config_reader.h
@@ -0,0 +1,30 @@
+// 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 COMPONENTS_FUCHSIA_COMPONENT_SUPPORT_CONFIG_READER_H_
+#define COMPONENTS_FUCHSIA_COMPONENT_SUPPORT_CONFIG_READER_H_
+
+#include "base/values.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace fuchsia_component_support {
+
+// Return a JSON dictionary read from the calling Component's config-data.
+// All *.json files in the config-data directory are read, parsed, and merged
+// into a single JSON dictionary value.
+// Null is returned if no config-data exists for the Component.
+// CHECK()s if one or more config files are malformed, or there are duplicate
+// non-dictionary fields in different config files.
+const absl::optional<base::Value>& LoadPackageConfig();
+
+// Used to test the implementation of LoadPackageConfig().
+absl::optional<base::Value> LoadConfigFromDirForTest(const base::FilePath& dir);
+
+} // namespace fuchsia_component_support
+
+#endif // COMPONENTS_FUCHSIA_COMPONENT_SUPPORT_CONFIG_READER_H_
diff --git a/chromium/components/fuchsia_component_support/config_reader_unittest.cc b/chromium/components/fuchsia_component_support/config_reader_unittest.cc
new file mode 100644
index 00000000000..641677b1c9a
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/config_reader_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright 2021 The Chromium 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/fuchsia_component_support/config_reader.h"
+
+#include <string>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fuchsia_component_support {
+
+TEST(ConfigReaderTest, NoConfigData) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ auto config = LoadConfigFromDirForTest(temp_dir.GetPath());
+ EXPECT_FALSE(config.has_value());
+}
+
+TEST(ConfigReaderTest, SingleConfigJson) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("config.json"),
+ "{ \"name\": \"value\" }"));
+
+ auto config = LoadConfigFromDirForTest(temp_dir.GetPath());
+ ASSERT_TRUE(config.has_value());
+ ASSERT_TRUE(config->is_dict());
+
+ EXPECT_EQ(config->DictSize(), 1u);
+ const std::string* value = config->FindStringKey("name");
+ ASSERT_TRUE(value);
+ EXPECT_EQ(*value, "value");
+}
+
+TEST(ConfigReaderTest, MultipleConfigJson) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("foo.json"),
+ "{ \"name1\": \"value?\" }"));
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("bar.json"),
+ "{ \"name2\": \"value!\" }"));
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("wibble.json"),
+ "{ \"name3\": \"value...\" }"));
+
+ auto config = LoadConfigFromDirForTest(temp_dir.GetPath());
+ ASSERT_TRUE(config.has_value());
+ ASSERT_TRUE(config->is_dict());
+
+ EXPECT_EQ(config->DictSize(), 3u);
+
+ std::string* value = config->FindStringKey("name1");
+ ASSERT_TRUE(value);
+ EXPECT_EQ(*value, "value?");
+
+ value = config->FindStringKey("name2");
+ ASSERT_TRUE(value);
+ EXPECT_EQ(*value, "value!");
+
+ value = config->FindStringKey("name3");
+ ASSERT_TRUE(value);
+ EXPECT_EQ(*value, "value...");
+}
+
+TEST(ConfigReaderTest, OneOfTheseConfigsIsNotValidLikeTheOthers) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ // Provide some valid config JSONs.
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("foo.json"),
+ "{ \"name1\": \"value?\" }"));
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("wibble.json"),
+ "{ \"name2\": \"value...\" }"));
+
+ // Provide an invalid one, which should cause a CHECK failure.
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("not_valid.json"),
+ "{ \"name3\"= }"));
+
+ EXPECT_DEATH({ LoadConfigFromDirForTest(temp_dir.GetPath()); }, "");
+}
+
+TEST(ConfigReaderTest, MultipleClashingConfigJson) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("foo.json"),
+ "{ \"name\": \"value?\" }"));
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("bar.json"),
+ "{ \"name\": \"value!\" }"));
+ ASSERT_TRUE(base::WriteFile(temp_dir.GetPath().Append("wibble.json"),
+ "{ \"name\": \"value...\" }"));
+
+ EXPECT_DEATH({ LoadConfigFromDirForTest(temp_dir.GetPath()); }, "");
+}
+
+} // namespace fuchsia_component_support
diff --git a/chromium/components/fuchsia_component_support/feedback_registration.cc b/chromium/components/fuchsia_component_support/feedback_registration.cc
new file mode 100644
index 00000000000..0405b62ee68
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/feedback_registration.cc
@@ -0,0 +1,57 @@
+// 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/fuchsia_component_support/feedback_registration.h"
+
+#include <fuchsia/feedback/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+
+#include "base/fuchsia/process_context.h"
+#include "base/strings/string_piece.h"
+#include "build/branding_buildflags.h"
+#include "components/version_info/version_info.h"
+
+namespace fuchsia_component_support {
+
+void RegisterProductDataForCrashReporting(
+ base::StringPiece component_url,
+ base::StringPiece crash_product_name) {
+ fuchsia::feedback::CrashReportingProduct product_data;
+ product_data.set_name(std::string(crash_product_name));
+ product_data.set_version(version_info::GetVersionNumber());
+ // TODO(https://crbug.com/1077428): Use the actual channel when appropriate.
+ // For now, always set it to the empty string to avoid reporting "missing".
+ product_data.set_channel("");
+
+ auto crash_reporting_service =
+ base::ComponentContextForProcess()
+ ->svc()
+ ->Connect<fuchsia::feedback::CrashReportingProductRegister>();
+
+ // Only register the |crash_product_name| for official Chrome-branded builds.
+ // Otherwise, the crashes will be handled as non-component-specific crashes.
+ // Since Fuchsia handles crashes, it is possible that Fuchsia will upload a
+ // crash for an unofficial and/or unbranded build of a Chromium-based
+ // component if it is running on an official Fuchsia build. To avoid adding
+ // noise from such crash reports, which are not received on other platforms,
+ // do not set a product name for such builds.
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && defined(OFFICIAL_BUILD)
+ crash_reporting_service->Upsert(std::string(component_url),
+ std::move(product_data));
+#endif
+}
+
+void RegisterProductDataForFeedback(base::StringPiece component_namespace) {
+ fuchsia::feedback::ComponentData component_data;
+ component_data.set_namespace_(std::string(component_namespace));
+ // TODO(https://crbug.com/1077428): Add release channel to the annotations.
+ component_data.mutable_annotations()->push_back(
+ {"version", version_info::GetVersionNumber()});
+ base::ComponentContextForProcess()
+ ->svc()
+ ->Connect<fuchsia::feedback::ComponentDataRegister>()
+ ->Upsert(std::move(component_data), []() {});
+}
+
+} // namespace fuchsia_component_support
diff --git a/chromium/components/fuchsia_component_support/feedback_registration.h b/chromium/components/fuchsia_component_support/feedback_registration.h
new file mode 100644
index 00000000000..53d7b143fd9
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/feedback_registration.h
@@ -0,0 +1,30 @@
+// 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 COMPONENTS_FUCHSIA_COMPONENT_SUPPORT_FEEDBACK_REGISTRATION_H_
+#define COMPONENTS_FUCHSIA_COMPONENT_SUPPORT_FEEDBACK_REGISTRATION_H_
+
+#include "base/strings/string_piece_forward.h"
+
+namespace fuchsia_component_support {
+
+// Overrides the default Fuchsia product info in crash reports.
+// Crashes for the component |component_url| will contain |crash_product_name|,
+// the version from version_info, and an appropriate value for the release
+// channel. |component_url| must match the current component. The calling
+// process must have access to "fuchsia.feedback.CrashReportingProductRegister".
+// Registration is skipped for unofficial and unbranded builds.
+void RegisterProductDataForCrashReporting(base::StringPiece component_url,
+ base::StringPiece crash_product_name);
+
+// Registers basic annotations for the component in |component_namespace|.
+// Feedback reports will contain a namespace |component_namespace| that contains
+// the version from version_info, and an appropriate value for the release
+// channel. The calling process must have access to
+// "fuchsia.feedback.ComponentDataRegister".
+void RegisterProductDataForFeedback(base::StringPiece component_namespace);
+
+} // namespace fuchsia_component_support
+
+#endif // COMPONENTS_FUCHSIA_COMPONENT_SUPPORT_FEEDBACK_REGISTRATION_H_ \ No newline at end of file
diff --git a/chromium/components/fuchsia_component_support/inspect.cc b/chromium/components/fuchsia_component_support/inspect.cc
new file mode 100644
index 00000000000..1bfbc2369b0
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/inspect.cc
@@ -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.
+
+#include "components/fuchsia_component_support/inspect.h"
+
+#include <lib/sys/inspect/cpp/component.h>
+
+#include "components/version_info/version_info.h"
+
+namespace fuchsia_component_support {
+
+namespace {
+const char kVersion[] = "version";
+const char kLastChange[] = "last_change_revision";
+} // namespace
+
+void PublishVersionInfoToInspect(sys::ComponentInspector* inspector) {
+ // These values are managed by the inspector, since they won't be updated over
+ // the lifetime of the component.
+ // TODO(https://crbug.com/1077428): Add release channel.
+ inspector->root().CreateString(kVersion, version_info::GetVersionNumber(),
+ inspector);
+ inspector->root().CreateString(kLastChange, version_info::GetLastChange(),
+ inspector);
+}
+
+} // namespace fuchsia_component_support
diff --git a/chromium/components/fuchsia_component_support/inspect.h b/chromium/components/fuchsia_component_support/inspect.h
new file mode 100644
index 00000000000..359cf44b530
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/inspect.h
@@ -0,0 +1,20 @@
+// 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 COMPONENTS_FUCHSIA_COMPONENT_SUPPORT_INSPECT_H_
+#define COMPONENTS_FUCHSIA_COMPONENT_SUPPORT_INSPECT_H_
+
+namespace sys {
+class ComponentInspector;
+} // namespace sys
+
+namespace fuchsia_component_support {
+
+// Publish the Chromium version via the Inspect API. The lifetime of
+// |inspector| has to be the same as the component it belongs to.
+void PublishVersionInfoToInspect(sys::ComponentInspector* inspector);
+
+} // namespace fuchsia_component_support
+
+#endif // COMPONENTS_FUCHSIA_COMPONENT_SUPPORT_INSPECT_H_ \ No newline at end of file
diff --git a/chromium/components/fuchsia_component_support/inspect_unittest.cc b/chromium/components/fuchsia_component_support/inspect_unittest.cc
new file mode 100644
index 00000000000..49332451165
--- /dev/null
+++ b/chromium/components/fuchsia_component_support/inspect_unittest.cc
@@ -0,0 +1,98 @@
+// 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/fuchsia_component_support/inspect.h"
+
+#include <lib/fdio/directory.h>
+#include <lib/inspect/cpp/hierarchy.h>
+#include <lib/inspect/cpp/reader.h>
+#include <lib/inspect/service/cpp/reader.h>
+#include <lib/sys/cpp/component_context.h>
+#include <lib/sys/inspect/cpp/component.h>
+
+#include <cstdint>
+#include <memory>
+
+#include "base/fuchsia/mem_buffer_util.h"
+#include "base/task/single_thread_task_executor.h"
+#include "base/test/task_environment.h"
+#include "components/version_info/version_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fuchsia_component_support {
+
+namespace {
+
+const char kVersion[] = "version";
+const char kLastChange[] = "last_change_revision";
+
+class InspectTest : public ::testing::Test {
+ public:
+ InspectTest() {
+ fidl::InterfaceHandle<fuchsia::io::Directory> incoming_directory;
+ auto incoming_services =
+ std::make_shared<sys::ServiceDirectory>(std::move(incoming_directory));
+ context_ = std::make_unique<sys::ComponentContext>(
+ std::move(incoming_services),
+ published_root_directory_.NewRequest().TakeChannel());
+ inspector_ = std::make_unique<sys::ComponentInspector>(context_.get());
+ base::RunLoop().RunUntilIdle();
+ }
+
+ InspectTest(const InspectTest&) = delete;
+ InspectTest& operator=(const InspectTest&) = delete;
+
+ protected:
+ base::test::SingleThreadTaskEnvironment task_environment_{
+ base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
+ std::unique_ptr<sys::ComponentContext> context_;
+ fidl::InterfaceHandle<fuchsia::io::Directory> published_root_directory_;
+ std::unique_ptr<sys::ComponentInspector> inspector_;
+};
+
+} // namespace
+
+TEST_F(InspectTest, PublishVersionInfoToInspect) {
+ fuchsia_component_support::PublishVersionInfoToInspect(inspector_.get());
+ fidl::InterfaceHandle<fuchsia::io::Directory> directory;
+ zx_status_t status = fdio_service_connect_at(
+ published_root_directory_.channel().get(), "diagnostics",
+ directory.NewRequest().TakeChannel().release());
+ ASSERT_EQ(ZX_OK, status);
+ std::unique_ptr<sys::ServiceDirectory> diagnostics =
+ std::make_unique<sys::ServiceDirectory>(std::move(directory));
+
+ // Access the inspect::Tree where the data is served. |tree| is in the
+ // directory created for the test, not the diagnostics directory for the test
+ // component.
+ fuchsia::inspect::TreePtr tree;
+ diagnostics->Connect(tree.NewRequest());
+ fuchsia::inspect::TreeContent content;
+ base::RunLoop run_loop;
+ tree->GetContent([&content, &run_loop](fuchsia::inspect::TreeContent c) {
+ content = std::move(c);
+ run_loop.Quit();
+ });
+ run_loop.Run();
+
+ // Parse the data as an inspect::Hierarchy.
+ ASSERT_TRUE(content.has_buffer());
+ std::string buffer_data =
+ base::StringFromMemBuffer(content.buffer()).value_or(std::string());
+ const uint8_t* raw_data =
+ reinterpret_cast<const uint8_t*>(buffer_data.data());
+ inspect::Hierarchy hierarchy =
+ inspect::ReadFromBuffer(
+ std::vector<uint8_t>(raw_data, raw_data + buffer_data.length()))
+ .take_value();
+
+ auto* property =
+ hierarchy.node().get_property<inspect::StringPropertyValue>(kVersion);
+ EXPECT_EQ(property->value(), version_info::GetVersionNumber());
+ property =
+ hierarchy.node().get_property<inspect::StringPropertyValue>(kLastChange);
+ EXPECT_EQ(property->value(), version_info::GetLastChange());
+}
+
+} // namespace fuchsia_component_support
diff --git a/chromium/components/fuchsia_legacymetrics/BUILD.gn b/chromium/components/fuchsia_legacymetrics/BUILD.gn
new file mode 100644
index 00000000000..62b3e7cd02f
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+
+visibility = []
+
+source_set("fuchsia_legacymetrics") {
+ visibility += [
+ ":unit_tests",
+ "//chromecast/internal/*",
+ "//fuchsia_web/webengine/*",
+ ]
+ sources = [
+ "legacymetrics_client.cc",
+ "legacymetrics_histogram_flattener.cc",
+ "legacymetrics_histogram_flattener.h",
+ "legacymetrics_user_event_recorder.cc",
+ "legacymetrics_user_event_recorder.h",
+ ]
+ public = [ "legacymetrics_client.h" ]
+ deps = [ "//base" ]
+ public_deps = [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.legacymetrics" ]
+ friend = [ ":unit_tests" ] # For access to private headers.
+}
+
+source_set("unit_tests") {
+ testonly = true
+ visibility += [ "//components:components_unittests__exec" ]
+ sources = [
+ "legacymetrics_client_unittest.cc",
+ "legacymetrics_histogram_flattener_unittest.cc",
+ "legacymetrics_user_event_recorder_unittest.cc",
+ ]
+ public_deps = [
+ ":fuchsia_legacymetrics",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/fuchsia_legacymetrics/DIR_METADATA b/chromium/components/fuchsia_legacymetrics/DIR_METADATA
new file mode 100644
index 00000000000..210aa6a954b
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//build/fuchsia/COMMON_METADATA"
diff --git a/chromium/components/fuchsia_legacymetrics/OWNERS b/chromium/components/fuchsia_legacymetrics/OWNERS
new file mode 100644
index 00000000000..fd58a1c2527
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/OWNERS
@@ -0,0 +1,2 @@
+kmarshall@chromium.org
+file://build/fuchsia/OWNERS
diff --git a/chromium/components/fuchsia_legacymetrics/legacymetrics_client.cc b/chromium/components/fuchsia_legacymetrics/legacymetrics_client.cc
new file mode 100644
index 00000000000..f72dc9c9a53
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/legacymetrics_client.cc
@@ -0,0 +1,299 @@
+// 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/fuchsia_legacymetrics/legacymetrics_client.h"
+
+#include <lib/fit/function.h>
+#include <lib/sys/cpp/component_context.h>
+#include <zircon/errors.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/callback_helpers.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/process_context.h"
+#include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.h"
+
+namespace fuchsia_legacymetrics {
+
+constexpr size_t LegacyMetricsClient::kMaxBatchSize;
+
+constexpr base::TimeDelta LegacyMetricsClient::kInitialReconnectDelay;
+constexpr base::TimeDelta LegacyMetricsClient::kMaxReconnectDelay;
+constexpr size_t LegacyMetricsClient::kReconnectBackoffFactor;
+
+LegacyMetricsClient::LegacyMetricsClient() = default;
+
+LegacyMetricsClient::~LegacyMetricsClient() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void LegacyMetricsClient::DisableAutoConnect() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(auto_connect_);
+ DCHECK_EQ(report_interval_, base::TimeDelta())
+ << "DisableAutoConnect() must be called before Start().";
+
+ auto_connect_ = false;
+}
+
+void LegacyMetricsClient::SetMetricsRecorder(
+ fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder>
+ metrics_recorder) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!auto_connect_);
+
+ auto weak_this = weak_factory_.GetWeakPtr();
+ ResetMetricsRecorderState();
+
+ // ResetMetricsRecorderState() may call |on_flush_complete_closures_|, which
+ // may destroy LegacyMetricsClient.
+ if (!weak_this)
+ return;
+
+ SetMetricsRecorderInternal(std::move(metrics_recorder));
+
+ if (report_interval_.is_positive())
+ ScheduleNextReport();
+}
+
+void LegacyMetricsClient::Start(base::TimeDelta report_interval) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_GT(report_interval, base::Seconds(0));
+
+ // Start recording user events.
+ user_events_recorder_ = std::make_unique<LegacyMetricsUserActionRecorder>();
+
+ report_interval_ = report_interval;
+
+ if (auto_connect_)
+ ConnectFromComponentContext();
+
+ if (metrics_recorder_)
+ ScheduleNextReport();
+}
+
+void LegacyMetricsClient::SetReportAdditionalMetricsCallback(
+ ReportAdditionalMetricsCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!metrics_recorder_)
+ << "SetReportAdditionalMetricsCallback() must be called before Start().";
+ DCHECK(!report_additional_callback_);
+ DCHECK(callback);
+
+ report_additional_callback_ = std::move(callback);
+}
+
+void LegacyMetricsClient::SetNotifyFlushCallback(NotifyFlushCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(callback);
+ DCHECK(!metrics_recorder_)
+ << "SetNotifyFlushCallback() must be called before Start().";
+
+ notify_flush_callback_ = std::move(callback);
+}
+
+void LegacyMetricsClient::ConnectFromComponentContext() {
+ DCHECK(!metrics_recorder_) << "Trying to connect when already connected.";
+ DVLOG(1) << "Trying to connect to MetricsRecorder service.";
+ DCHECK(auto_connect_);
+
+ fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder>
+ metrics_recorder;
+ base::ComponentContextForProcess()->svc()->Connect(
+ metrics_recorder.NewRequest());
+ SetMetricsRecorderInternal(std::move(metrics_recorder));
+
+ ScheduleNextReport();
+}
+
+void LegacyMetricsClient::SetMetricsRecorderInternal(
+ fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder>
+ metrics_recorder) {
+ metrics_recorder_.Bind(std::move(metrics_recorder));
+ metrics_recorder_.set_error_handler(fit::bind_member(
+ this, &LegacyMetricsClient::OnMetricsRecorderDisconnected));
+ metrics_recorder_.events().OnCloseSoon =
+ fit::bind_member(this, &LegacyMetricsClient::OnCloseSoon);
+}
+
+void LegacyMetricsClient::ScheduleNextReport() {
+ DCHECK(!is_flushing_);
+
+ if (report_timer_.IsRunning())
+ return;
+
+ DVLOG(1) << "Scheduling next report in " << report_interval_.InSeconds()
+ << "seconds.";
+ report_timer_.Start(FROM_HERE, report_interval_, this,
+ &LegacyMetricsClient::StartReport);
+}
+
+void LegacyMetricsClient::StartReport() {
+ if (!report_additional_callback_) {
+ Report({});
+ return;
+ }
+ report_additional_callback_.Run(
+ base::BindOnce(&LegacyMetricsClient::Report, weak_factory_.GetWeakPtr()));
+}
+
+void LegacyMetricsClient::Report(
+ std::vector<fuchsia::legacymetrics::Event> events) {
+ DVLOG(1) << __func__ << " called.";
+
+ // The connection might have dropped while additional metrics were being
+ // collected. Continue recording events and cache them locally in memory until
+ // connection is reestablished.
+ if (!metrics_recorder_)
+ return;
+
+ // Include histograms.
+ for (auto& histogram : GetLegacyMetricsDeltas()) {
+ fuchsia::legacymetrics::Event histogram_event;
+ histogram_event.set_histogram(std::move(histogram));
+ events.push_back(std::move(histogram_event));
+ }
+
+ // Include user events.
+ if (user_events_recorder_->HasEvents()) {
+ for (auto& event : user_events_recorder_->TakeEvents()) {
+ fuchsia::legacymetrics::Event user_event;
+ user_event.set_user_action_event(std::move(event));
+ events.push_back(std::move(user_event));
+ }
+ }
+
+ std::move(events.begin(), events.end(), std::back_inserter(to_send_));
+
+ DrainBuffer();
+}
+
+void LegacyMetricsClient::DrainBuffer() {
+ DVLOG(1) << __func__ << " called.";
+
+ if (record_ack_pending_) {
+ // There is a Record() call already inflight. When it is acknowledged,
+ // buffer draining will continue.
+ return;
+ }
+
+ if (to_send_.empty()) {
+ DVLOG(1) << "Buffer drained.";
+
+ if (is_flushing_) {
+ metrics_recorder_.Unbind();
+ CompleteFlush();
+ return;
+ }
+
+ ScheduleNextReport();
+ return;
+ }
+
+ // Since ordering doesn't matter, we can efficiently drain |to_send_| by
+ // repeatedly sending and truncating its tail.
+ const size_t batch_size = std::min(to_send_.size(), kMaxBatchSize);
+ const size_t batch_start_idx = to_send_.size() - batch_size;
+ std::vector<fuchsia::legacymetrics::Event> batch;
+ batch.resize(batch_size);
+ std::move(to_send_.begin() + batch_start_idx, to_send_.end(), batch.begin());
+ to_send_.resize(to_send_.size() - batch_size);
+
+ record_ack_pending_ = true;
+ metrics_recorder_->Record(std::move(batch), [this]() {
+ record_ack_pending_ = false;
+
+ // Reset the reconnect delay after a successful Record() call.
+ reconnect_delay_ = kInitialReconnectDelay;
+
+ DrainBuffer();
+ });
+}
+
+void LegacyMetricsClient::OnMetricsRecorderDisconnected(zx_status_t status) {
+ ZX_LOG(ERROR, status) << "MetricsRecorder connection lost.";
+
+ if (auto_connect_ && status == ZX_ERR_PEER_CLOSED) {
+ DVLOG(1) << "Scheduling reconnect after " << reconnect_delay_;
+
+ // Try to reconnect with exponential backoff.
+ reconnect_timer_.Start(FROM_HERE, reconnect_delay_, this,
+ &LegacyMetricsClient::ReconnectMetricsRecorder);
+
+ // Increase delay exponentially. No random jittering since we don't expect
+ // many clients overloading the service with simultaneous reconnections.
+ reconnect_delay_ = std::min(reconnect_delay_ * kReconnectBackoffFactor,
+ kMaxReconnectDelay);
+ }
+
+ ResetMetricsRecorderState();
+}
+
+void LegacyMetricsClient::ReconnectMetricsRecorder() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DVLOG(1) << __func__ << " called.";
+
+ ConnectFromComponentContext();
+ ScheduleNextReport();
+}
+
+void LegacyMetricsClient::FlushAndDisconnect(
+ base::OnceClosure on_flush_complete) {
+ DVLOG(1) << __func__ << " called.";
+
+ if (on_flush_complete)
+ on_flush_complete_closures_.push_back(std::move(on_flush_complete));
+
+ if (is_flushing_)
+ return;
+
+ report_timer_.AbandonAndStop();
+
+ is_flushing_ = true;
+ if (notify_flush_callback_) {
+ // Defer reporting until the flush operation has finished.
+ std::move(notify_flush_callback_)
+ .Run(base::BindOnce(&LegacyMetricsClient::StartReport,
+ weak_factory_.GetWeakPtr()));
+ } else {
+ StartReport();
+ }
+}
+
+void LegacyMetricsClient::OnCloseSoon() {
+ FlushAndDisconnect(base::OnceClosure());
+}
+
+void LegacyMetricsClient::CompleteFlush() {
+ DCHECK(is_flushing_);
+
+ is_flushing_ = false;
+
+ // One of the callbacks may destroy |this|, so move them all to the stack
+ // first.
+ std::vector<base::OnceClosure> on_flush_complete_closures;
+ on_flush_complete_closures.swap(on_flush_complete_closures_);
+ for (auto& closure : on_flush_complete_closures) {
+ std::move(closure).Run();
+ }
+}
+
+void LegacyMetricsClient::ResetMetricsRecorderState() {
+ // Stop reporting metric events.
+ report_timer_.AbandonAndStop();
+
+ record_ack_pending_ = false;
+
+ if (is_flushing_)
+ CompleteFlush();
+}
+
+} // namespace fuchsia_legacymetrics
diff --git a/chromium/components/fuchsia_legacymetrics/legacymetrics_client.h b/chromium/components/fuchsia_legacymetrics/legacymetrics_client.h
new file mode 100644
index 00000000000..0f193adad17
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/legacymetrics_client.h
@@ -0,0 +1,124 @@
+// 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 COMPONENTS_FUCHSIA_LEGACYMETRICS_LEGACYMETRICS_CLIENT_H_
+#define COMPONENTS_FUCHSIA_LEGACYMETRICS_LEGACYMETRICS_CLIENT_H_
+
+#include <fuchsia/legacymetrics/cpp/fidl.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.h"
+
+namespace fuchsia_legacymetrics {
+
+// Used to report events & histogram data to the
+// fuchsia.legacymetrics.MetricsRecorder service.
+// LegacyMetricsClient must be Start()ed on an IO-capable sequence.
+// Cannot be used in conjunction with other metrics reporting services.
+// Must be constructed, used, and destroyed on the same sequence.
+class LegacyMetricsClient {
+ public:
+ // Maximum number of Events to send to Record() at a time, so as to not exceed
+ // the 64KB FIDL maximum message size.
+ static constexpr size_t kMaxBatchSize = 50;
+
+ // Constants for FIDL reconnection with exponential backoff.
+ static constexpr base::TimeDelta kInitialReconnectDelay = base::Seconds(1);
+ static constexpr base::TimeDelta kMaxReconnectDelay = base::Hours(1);
+ static constexpr size_t kReconnectBackoffFactor = 2;
+
+ using ReportAdditionalMetricsCallback = base::RepeatingCallback<void(
+ base::OnceCallback<void(std::vector<fuchsia::legacymetrics::Event>)>)>;
+ using NotifyFlushCallback =
+ base::OnceCallback<void(base::OnceClosure completion_cb)>;
+
+ LegacyMetricsClient();
+ ~LegacyMetricsClient();
+
+ explicit LegacyMetricsClient(const LegacyMetricsClient&) = delete;
+ LegacyMetricsClient& operator=(const LegacyMetricsClient&) = delete;
+
+ // Disables automatic MetricsRecorder connection. Caller will have to supply
+ // MetricsRecorder by calling SetMetricsRecorder(). Must be called before
+ // Start().
+ void DisableAutoConnect();
+
+ // Sets |metrics_recorder| to use. Should be called only after
+ // DisableAutoConnect().
+ void SetMetricsRecorder(
+ fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder>
+ metrics_recorder);
+
+ // Starts buffering data and schedules metric reporting after every
+ // |report_interval|.
+ void Start(base::TimeDelta report_interval);
+
+ // Sets an asynchronous |callback| to be invoked just prior to reporting,
+ // allowing users to asynchronously gather and provide additional custom
+ // metrics. |callback| will receive the list of metrics when they are ready.
+ // Reporting is paused until |callback| is fulfilled.
+ // If used, then this method must be called before calling Start().
+ void SetReportAdditionalMetricsCallback(
+ ReportAdditionalMetricsCallback callback);
+
+ // Sets a |callback| which is invoked to warn that the connection to the
+ // remote MetricsRecorder will be terminated. The completion closure passed to
+ // |callback| should be invoked to signal flush completion.
+ void SetNotifyFlushCallback(NotifyFlushCallback callback);
+
+ // Use when caller needs an explicit flush and then disconnect, such as before
+ // termination. Caller will be notified when all events in the buffer are
+ // sent.
+ void FlushAndDisconnect(base::OnceClosure on_flush_complete);
+
+ private:
+ void ConnectFromComponentContext();
+ void SetMetricsRecorderInternal(
+ fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder>
+ metrics_recorder);
+ void ScheduleNextReport();
+ void StartReport();
+ void Report(std::vector<fuchsia::legacymetrics::Event> additional_metrics);
+ void OnMetricsRecorderDisconnected(zx_status_t status);
+ void ReconnectMetricsRecorder();
+ void OnCloseSoon();
+ void CompleteFlush();
+ void ResetMetricsRecorderState();
+
+ // Incrementally sends the contents of |to_send_| to |metrics_recorder_|.
+ void DrainBuffer();
+
+ base::TimeDelta reconnect_delay_ = kInitialReconnectDelay;
+ base::TimeDelta report_interval_;
+ ReportAdditionalMetricsCallback report_additional_callback_;
+ NotifyFlushCallback notify_flush_callback_;
+ bool is_flushing_ = false;
+ bool record_ack_pending_ = false;
+ std::vector<fuchsia::legacymetrics::Event> to_send_;
+ std::unique_ptr<LegacyMetricsUserActionRecorder> user_events_recorder_;
+
+ bool auto_connect_ = true;
+ base::RetainingOneShotTimer reconnect_timer_;
+
+ fuchsia::legacymetrics::MetricsRecorderPtr metrics_recorder_;
+ base::RetainingOneShotTimer report_timer_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ std::vector<base::OnceClosure> on_flush_complete_closures_;
+
+ // Prevents use-after-free if |report_additional_callback_| is invoked after
+ // |this| is destroyed.
+ base::WeakPtrFactory<LegacyMetricsClient> weak_factory_{this};
+};
+
+} // namespace fuchsia_legacymetrics
+
+#endif // COMPONENTS_FUCHSIA_LEGACYMETRICS_LEGACYMETRICS_CLIENT_H_
diff --git a/chromium/components/fuchsia_legacymetrics/legacymetrics_client_unittest.cc b/chromium/components/fuchsia_legacymetrics/legacymetrics_client_unittest.cc
new file mode 100644
index 00000000000..5ac72f490ba
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/legacymetrics_client_unittest.cc
@@ -0,0 +1,707 @@
+// 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 <fuchsia/legacymetrics/cpp/fidl.h>
+#include <fuchsia/legacymetrics/cpp/fidl_test_base.h>
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/fuchsia/scoped_service_binding.h"
+#include "base/fuchsia/test_component_context_for_process.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/fuchsia_legacymetrics/legacymetrics_client.h"
+#include "components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fuchsia_legacymetrics {
+namespace {
+
+using ::testing::Property;
+using ::testing::UnorderedElementsAreArray;
+
+constexpr base::TimeDelta kReportInterval = base::Minutes(1);
+constexpr base::TimeDelta kShortDuration = base::Seconds(1);
+
+class TestMetricsRecorder
+ : public fuchsia::legacymetrics::testing::MetricsRecorder_TestBase {
+ public:
+ TestMetricsRecorder() = default;
+ ~TestMetricsRecorder() override = default;
+
+ bool IsRecordInFlight() const { return ack_callback_.has_value(); }
+
+ bool IsEmpty() const { return recorded_events_.empty(); }
+
+ std::vector<fuchsia::legacymetrics::Event> WaitForEvents() {
+ if (recorded_events_.empty()) {
+ base::RunLoop run_loop;
+ on_record_cb_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+ return std::move(recorded_events_);
+ }
+
+ void DropAck() { ack_callback_ = absl::nullopt; }
+
+ void SendAck() {
+ (*ack_callback_)();
+ ack_callback_ = absl::nullopt;
+ }
+
+ void set_expect_ack_dropped(bool expect_dropped) {
+ expect_ack_dropped_ = expect_dropped;
+ }
+
+ // fuchsia::legacymetrics::MetricsRecorder implementation.
+ void Record(std::vector<fuchsia::legacymetrics::Event> events,
+ RecordCallback callback) override {
+ std::move(events.begin(), events.end(),
+ std::back_inserter(recorded_events_));
+
+ // Received a call to Record() before the previous one was acknowledged,
+ // which can happen in some cases (e.g. flushing).
+ if (ack_callback_)
+ EXPECT_TRUE(expect_ack_dropped_);
+
+ ack_callback_ = std::move(callback);
+
+ if (on_record_cb_)
+ std::move(on_record_cb_).Run();
+ }
+
+ void NotImplemented_(const std::string& name) override { FAIL() << name; }
+
+ private:
+ std::vector<fuchsia::legacymetrics::Event> recorded_events_;
+ base::OnceClosure on_record_cb_;
+ absl::optional<RecordCallback> ack_callback_;
+ bool expect_ack_dropped_ = false;
+};
+
+class LegacyMetricsClientTest : public testing::Test {
+ public:
+ LegacyMetricsClientTest()
+ : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME,
+ base::test::TaskEnvironment::MainThreadType::IO) {}
+ ~LegacyMetricsClientTest() override = default;
+
+ void SetUp() override {
+ service_binding_ = MakeServiceBinding();
+ base::SetRecordActionTaskRunner(base::ThreadTaskRunnerHandle::Get());
+
+ // Flush any dirty histograms from previous test runs in this process.
+ GetLegacyMetricsDeltas();
+ }
+
+ std::unique_ptr<base::ScopedSingleClientServiceBinding<
+ fuchsia::legacymetrics::MetricsRecorder>>
+ MakeServiceBinding() {
+ return std::make_unique<base::ScopedSingleClientServiceBinding<
+ fuchsia::legacymetrics::MetricsRecorder>>(
+ test_context_.additional_services(), &test_recorder_);
+ }
+
+ void StartClientAndExpectConnection() {
+ client_.Start(kReportInterval);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(service_binding_->has_clients());
+ }
+
+ // Disconnects the service side of the metrics FIDL channel and replaces the
+ // binding with a new instance.
+ void DisconnectAndRestartMetricsService() {
+ service_binding_.reset();
+ service_binding_ = MakeServiceBinding();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void ExpectReconnectAfterDelay(const base::TimeDelta& delay) {
+ // Just before the expected delay, the client shouldn't reconnect yet.
+ task_environment_.FastForwardBy(delay - kShortDuration);
+ EXPECT_FALSE(service_binding_->has_clients())
+ << "Expected delay: " << delay;
+
+ // Complete the full expected reconnect delay. Client should reconnect.
+ task_environment_.FastForwardBy(kShortDuration);
+ EXPECT_TRUE(service_binding_->has_clients()) << "Expected delay: " << delay;
+ }
+
+ void SetMetricsRecorder() {
+ fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder>
+ metrics_recorder;
+ direct_binding_.Bind(metrics_recorder.NewRequest());
+ client_.SetMetricsRecorder(std::move(metrics_recorder));
+ }
+
+ protected:
+ base::test::TaskEnvironment task_environment_;
+ base::TestComponentContextForProcess test_context_;
+ TestMetricsRecorder test_recorder_;
+ std::unique_ptr<base::ScopedSingleClientServiceBinding<
+ fuchsia::legacymetrics::MetricsRecorder>>
+ service_binding_;
+ fidl::Binding<fuchsia::legacymetrics::MetricsRecorder> direct_binding_{
+ &test_recorder_};
+
+ LegacyMetricsClient client_;
+};
+
+TEST_F(LegacyMetricsClientTest, ReportIntervalBoundary) {
+ client_.Start(kReportInterval);
+
+ task_environment_.FastForwardBy(kReportInterval - base::Seconds(1));
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+ task_environment_.FastForwardBy(base::Seconds(1));
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+}
+
+void PopulateAdditionalEvents(
+ base::OnceCallback<void(std::vector<fuchsia::legacymetrics::Event>)>
+ callback) {
+ fuchsia::legacymetrics::ImplementationDefinedEvent impl_event;
+ impl_event.set_name("baz");
+
+ fuchsia::legacymetrics::Event event;
+ event.set_impl_defined_event(std::move(impl_event));
+
+ std::vector<fuchsia::legacymetrics::Event> events;
+ events.push_back(std::move(event));
+ std::move(callback).Run(std::move(events));
+}
+
+TEST_F(LegacyMetricsClientTest, AllTypes) {
+ client_.SetReportAdditionalMetricsCallback(
+ base::BindRepeating(&PopulateAdditionalEvents));
+ client_.Start(kReportInterval);
+
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+ base::RecordComputedAction("bar");
+
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ auto events = test_recorder_.WaitForEvents();
+ EXPECT_EQ(3u, events.size());
+ EXPECT_EQ("baz", events[0].impl_defined_event().name());
+ EXPECT_EQ("foo", events[1].histogram().name());
+ EXPECT_EQ("bar", events[2].user_action_event().name());
+}
+
+TEST_F(LegacyMetricsClientTest, DisconnectWhileCollectingAdditionalEvents) {
+ // Hold the completion callback for later execution.
+ base::OnceCallback<void(std::vector<fuchsia::legacymetrics::Event>)>
+ on_report_done;
+ client_.SetReportAdditionalMetricsCallback(base::BindRepeating(
+ [](base::OnceCallback<void(std::vector<fuchsia::legacymetrics::Event>)>*
+ stored_on_report_done,
+ base::OnceCallback<void(std::vector<fuchsia::legacymetrics::Event>)>
+ on_report_done) {
+ *stored_on_report_done = std::move(on_report_done);
+ },
+ base::Unretained(&on_report_done)));
+
+ client_.Start(kReportInterval);
+
+ task_environment_.FastForwardBy(kReportInterval);
+
+ // Disconnect the service.
+ service_binding_.reset();
+ base::RunLoop().RunUntilIdle();
+
+ // Fulfill the report additional metrics callback.
+ std::move(on_report_done).Run({});
+}
+
+TEST_F(LegacyMetricsClientTest, ReportSkippedNoEvents) {
+ client_.Start(kReportInterval);
+
+ // Verify that Record() is not invoked if there is no data to report.
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+
+ // Add some events and allow the interval to lapse. Verify that the data is
+ // reported.
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ test_recorder_.SendAck();
+
+ // Verify that Record() is skipped again for no-data.
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+}
+
+TEST_F(LegacyMetricsClientTest, MultipleReports) {
+ client_.Start(kReportInterval);
+
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ test_recorder_.SendAck();
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ test_recorder_.SendAck();
+}
+
+TEST_F(LegacyMetricsClientTest, NoReportIfNeverAcked) {
+ client_.Start(kReportInterval);
+
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ test_recorder_.DropAck();
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+}
+
+TEST_F(LegacyMetricsClientTest, ReconnectAfterServiceDisconnect) {
+ StartClientAndExpectConnection();
+ DisconnectAndRestartMetricsService();
+ EXPECT_FALSE(service_binding_->has_clients());
+ task_environment_.FastForwardBy(LegacyMetricsClient::kInitialReconnectDelay);
+ EXPECT_TRUE(service_binding_->has_clients());
+
+ base::RecordComputedAction("foo");
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ test_recorder_.SendAck();
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+}
+
+TEST_F(LegacyMetricsClientTest, ServiceDisconnectWhileRecordPending) {
+ StartClientAndExpectConnection();
+
+ base::RecordComputedAction("foo");
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ DisconnectAndRestartMetricsService();
+ EXPECT_FALSE(service_binding_->has_clients());
+ test_recorder_.DropAck();
+
+ task_environment_.FastForwardBy(LegacyMetricsClient::kInitialReconnectDelay);
+ EXPECT_TRUE(service_binding_->has_clients());
+
+ base::RecordComputedAction("foo");
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+}
+
+TEST_F(LegacyMetricsClientTest, ServiceDisconnectWhileFlushing) {
+ StartClientAndExpectConnection();
+
+ base::RecordComputedAction("foo");
+ client_.FlushAndDisconnect(base::OnceClosure());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ DisconnectAndRestartMetricsService();
+ test_recorder_.DropAck();
+ EXPECT_FALSE(service_binding_->has_clients());
+
+ task_environment_.FastForwardBy(LegacyMetricsClient::kInitialReconnectDelay);
+ EXPECT_TRUE(service_binding_->has_clients());
+
+ base::RecordComputedAction("foo");
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+}
+
+TEST_F(LegacyMetricsClientTest,
+ ReconnectConsecutivelyWithoutRecordBacksOffExponentially) {
+ StartClientAndExpectConnection();
+
+ for (base::TimeDelta expected_delay =
+ LegacyMetricsClient::kInitialReconnectDelay;
+ expected_delay <= LegacyMetricsClient::kMaxReconnectDelay;
+ expected_delay *= LegacyMetricsClient::kReconnectBackoffFactor) {
+ DisconnectAndRestartMetricsService();
+ ExpectReconnectAfterDelay(expected_delay);
+ }
+}
+
+TEST_F(LegacyMetricsClientTest, ReconnectDelayNeverExceedsMax) {
+ StartClientAndExpectConnection();
+
+ // Find the theoretical maximum number of consecutive failed connections. Also
+ // add a few extra iterations to ensure that we never exceed the max delay.
+ const size_t num_iterations =
+ 3 + log(LegacyMetricsClient::kMaxReconnectDelay /
+ LegacyMetricsClient::kInitialReconnectDelay) /
+ log(LegacyMetricsClient::kReconnectBackoffFactor);
+
+ // As a heuristic, starting with 1 second and a factor of 2 reaches 24 hours
+ // in about 17 iterations. So the expected number of iterations needed to
+ // reach the maximum delay should be less than about 20.
+ EXPECT_LE(num_iterations, 20u);
+
+ for (size_t i = 0; i < num_iterations; i++) {
+ DisconnectAndRestartMetricsService();
+ EXPECT_FALSE(service_binding_->has_clients()) << "Iteration " << i;
+ task_environment_.FastForwardBy(LegacyMetricsClient::kMaxReconnectDelay);
+ EXPECT_TRUE(service_binding_->has_clients()) << "Iteration " << i;
+ }
+}
+
+TEST_F(LegacyMetricsClientTest, RecordCompletionResetsReconnectDelay) {
+ StartClientAndExpectConnection();
+
+ // First reconnect has initial delay.
+ DisconnectAndRestartMetricsService();
+ ExpectReconnectAfterDelay(LegacyMetricsClient::kInitialReconnectDelay);
+
+ // Another reconnect without a successful Record() call increases the delay.
+ DisconnectAndRestartMetricsService();
+ ExpectReconnectAfterDelay(LegacyMetricsClient::kInitialReconnectDelay *
+ LegacyMetricsClient::kReconnectBackoffFactor);
+
+ // Record and report an event, invoking a FIDL Record().
+ base::RecordComputedAction("ArbitraryEvent");
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ test_recorder_.SendAck();
+ base::RunLoop().RunUntilIdle();
+
+ // Reconnect after a successful Record() uses the initial delay again.
+ DisconnectAndRestartMetricsService();
+ ExpectReconnectAfterDelay(LegacyMetricsClient::kInitialReconnectDelay);
+}
+
+TEST_F(LegacyMetricsClientTest, ContinueRecordingUserActionsAfterDisconnect) {
+ StartClientAndExpectConnection();
+
+ base::RecordComputedAction("BeforeDisconnect");
+ DisconnectAndRestartMetricsService();
+ base::RecordComputedAction("DuringDisconnect");
+ ExpectReconnectAfterDelay(LegacyMetricsClient::kInitialReconnectDelay);
+ base::RecordComputedAction("AfterReconnect");
+
+ // Fast forward to report metrics.
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ auto events = test_recorder_.WaitForEvents();
+ EXPECT_THAT(
+ events,
+ UnorderedElementsAreArray({
+ Property(&fuchsia::legacymetrics::Event::user_action_event,
+ Property(&fuchsia::legacymetrics::UserActionEvent::name,
+ "BeforeDisconnect")),
+ Property(&fuchsia::legacymetrics::Event::user_action_event,
+ Property(&fuchsia::legacymetrics::UserActionEvent::name,
+ "DuringDisconnect")),
+ Property(&fuchsia::legacymetrics::Event::user_action_event,
+ Property(&fuchsia::legacymetrics::UserActionEvent::name,
+ "AfterReconnect")),
+ }));
+}
+
+TEST_F(LegacyMetricsClientTest, Batching) {
+ client_.Start(kReportInterval);
+
+ // Log enough actions that the list will be split across multiple batches.
+ // Batches are read out in reverse order, so even though it is being logged
+ // first, it will be emitted in the final batch.
+ base::RecordComputedAction("batch2");
+
+ for (size_t i = 0; i < LegacyMetricsClient::kMaxBatchSize; ++i)
+ base::RecordComputedAction("batch1");
+
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ // First batch.
+ auto events = test_recorder_.WaitForEvents();
+ EXPECT_EQ(LegacyMetricsClient::kMaxBatchSize, events.size());
+ for (const auto& event : events)
+ EXPECT_EQ(event.user_action_event().name(), "batch1");
+ test_recorder_.SendAck();
+
+ // Second batch (remainder).
+ events = test_recorder_.WaitForEvents();
+ EXPECT_EQ(1u, events.size());
+ for (const auto& event : events)
+ EXPECT_EQ(event.user_action_event().name(), "batch2");
+ test_recorder_.SendAck();
+}
+
+TEST_F(LegacyMetricsClientTest, FlushWithPending) {
+ client_.Start(kReportInterval);
+ base::RunLoop().RunUntilIdle();
+
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+ service_binding_->events().OnCloseSoon();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ // The service should be unbound once all data is drained.
+ EXPECT_TRUE(service_binding_->has_clients());
+ auto events = test_recorder_.WaitForEvents();
+ test_recorder_.SendAck();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1u, events.size());
+ EXPECT_EQ("foo", events[0].histogram().name());
+ EXPECT_FALSE(service_binding_->has_clients());
+}
+
+TEST_F(LegacyMetricsClientTest, FlushNoData) {
+ client_.Start(kReportInterval);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(service_binding_->has_clients());
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+ service_binding_->events().OnCloseSoon();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(service_binding_->has_clients());
+}
+
+TEST_F(LegacyMetricsClientTest, FlushWithOutstandingAck) {
+ client_.Start(kReportInterval);
+ base::RunLoop().RunUntilIdle();
+
+ // Send "foo", but don't ack.
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ // Allow the flush operation to call Record() without waiting for a prior ack.
+ test_recorder_.set_expect_ack_dropped(true);
+
+ // Buffer another event and trigger a flush.
+ UMA_HISTOGRAM_COUNTS_1M("bar", 20);
+ EXPECT_TRUE(service_binding_->has_clients());
+ service_binding_->events().OnCloseSoon();
+
+ // Simulate an asynchronous ack from the recorder, which be delivered around
+ // the same time as the flush's Record() call. The ack should be gracefully
+ // ignored by the client.
+ test_recorder_.SendAck();
+
+ base::RunLoop().RunUntilIdle();
+
+ auto events = test_recorder_.WaitForEvents();
+ test_recorder_.SendAck();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2u, events.size());
+ EXPECT_EQ("foo", events[0].histogram().name());
+ EXPECT_EQ("bar", events[1].histogram().name());
+ EXPECT_FALSE(service_binding_->has_clients());
+}
+
+TEST_F(LegacyMetricsClientTest, ExternalFlushSignal) {
+ base::test::TestFuture<base::OnceClosure> flush_receiver;
+ client_.SetNotifyFlushCallback(flush_receiver.GetCallback());
+ client_.Start(kReportInterval);
+ base::RunLoop().RunUntilIdle();
+
+ UMA_HISTOGRAM_COUNTS_1M("foo", 20);
+
+ // Verify that reporting does not start until the flush completion callback is
+ // run.
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+ service_binding_->events().OnCloseSoon();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+
+ // Verify that invoking the completion callback unblocks reporting.
+ EXPECT_TRUE(flush_receiver.IsReady());
+ flush_receiver.Take().Run();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+}
+
+TEST_F(LegacyMetricsClientTest, ExplicitFlush) {
+ client_.Start(kReportInterval);
+
+ base::RecordComputedAction("bar");
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+
+ bool called = false;
+ client_.FlushAndDisconnect(
+ base::BindLambdaForTesting([&called] { called = true; }));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ EXPECT_FALSE(called);
+
+ auto events = test_recorder_.WaitForEvents();
+ EXPECT_EQ(1u, events.size());
+ EXPECT_EQ("bar", events[0].user_action_event().name());
+
+ test_recorder_.SendAck();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(LegacyMetricsClientTest, DoubleFlush) {
+ client_.Start(kReportInterval);
+
+ base::RecordComputedAction("bar");
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(test_recorder_.IsRecordInFlight());
+
+ bool called = false;
+ client_.FlushAndDisconnect(
+ base::BindLambdaForTesting([&called] { called = true; }));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ EXPECT_FALSE(called);
+
+ bool called2 = false;
+ client_.FlushAndDisconnect(
+ base::BindLambdaForTesting([&called2] { called2 = true; }));
+
+ test_recorder_.WaitForEvents();
+ test_recorder_.SendAck();
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that both FlushAndDisconnect() callbacks were called.
+ EXPECT_TRUE(called);
+ EXPECT_TRUE(called2);
+}
+
+TEST_F(LegacyMetricsClientTest, ExplicitFlushMultipleBatches) {
+ const size_t kSizeForMultipleBatches = LegacyMetricsClient::kMaxBatchSize * 2;
+ client_.Start(kReportInterval);
+
+ for (size_t i = 0; i < kSizeForMultipleBatches; ++i)
+ base::RecordComputedAction("bar");
+
+ client_.FlushAndDisconnect(base::DoNothing());
+ base::RunLoop().RunUntilIdle();
+ test_recorder_.SendAck();
+ base::RunLoop().RunUntilIdle();
+
+ auto events = test_recorder_.WaitForEvents();
+ EXPECT_EQ(kSizeForMultipleBatches, events.size());
+ for (size_t i = 0; i < kSizeForMultipleBatches; ++i)
+ EXPECT_EQ("bar", events[i].user_action_event().name());
+}
+
+TEST_F(LegacyMetricsClientTest, UseInjectedMetricsRecorder) {
+ client_.DisableAutoConnect();
+ SetMetricsRecorder();
+
+ client_.Start(kReportInterval);
+
+ base::RecordComputedAction("bar");
+
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ auto events = test_recorder_.WaitForEvents();
+ EXPECT_EQ(1u, events.size());
+ EXPECT_EQ("bar", events[0].user_action_event().name());
+
+ // Verify that /svc wasn't used.
+ EXPECT_FALSE(service_binding_->has_clients());
+
+ // Verify that LegacyMetricsClient doesn't try to reconnect after
+ // MetricsRecorder has been disconnected.
+ direct_binding_.Unbind();
+ task_environment_.FastForwardBy(LegacyMetricsClient::kInitialReconnectDelay *
+ 2);
+ EXPECT_FALSE(service_binding_->has_clients());
+}
+
+TEST_F(LegacyMetricsClientTest, UseInjectedMetricsRecorderReconnect) {
+ client_.DisableAutoConnect();
+ SetMetricsRecorder();
+
+ client_.Start(kReportInterval);
+
+ bool flush_complete = false;
+ client_.FlushAndDisconnect(
+ base::BindLambdaForTesting([&flush_complete] { flush_complete = true; }));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(flush_complete);
+
+ EXPECT_TRUE(test_recorder_.IsEmpty());
+
+ // Set recorder again and verify that it receives metrics now.
+ SetMetricsRecorder();
+
+ base::RecordComputedAction("bar");
+
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ auto events = test_recorder_.WaitForEvents();
+ EXPECT_EQ(1u, events.size());
+}
+
+TEST_F(LegacyMetricsClientTest, SetMetricsRecorderDuringRecord) {
+ client_.DisableAutoConnect();
+ SetMetricsRecorder();
+
+ client_.Start(kReportInterval);
+
+ base::RecordComputedAction("bar");
+
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ test_recorder_.DropAck();
+
+ // Set recorder again and verify that it can receive metrics.
+ SetMetricsRecorder();
+
+ base::RecordComputedAction("bar");
+
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ auto events = test_recorder_.WaitForEvents();
+ EXPECT_EQ(2u, events.size());
+}
+
+TEST_F(LegacyMetricsClientTest, SetMetricsRecorderDuringFlush) {
+ client_.DisableAutoConnect();
+ SetMetricsRecorder();
+
+ client_.Start(kReportInterval);
+
+ base::RecordComputedAction("bar");
+
+ bool flush_complete = false;
+ client_.FlushAndDisconnect(
+ base::BindLambdaForTesting([&flush_complete] { flush_complete = true; }));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+ test_recorder_.DropAck();
+ EXPECT_FALSE(flush_complete);
+
+ // Set recorder again. It's expected to complete the Flush().
+ SetMetricsRecorder();
+ EXPECT_TRUE(flush_complete);
+
+ // Verify that metrics are sent to the new MetricsRecorder instance.
+ base::RecordComputedAction("bar");
+
+ task_environment_.FastForwardBy(kReportInterval);
+ EXPECT_TRUE(test_recorder_.IsRecordInFlight());
+
+ auto events = test_recorder_.WaitForEvents();
+ EXPECT_EQ(2u, events.size());
+}
+
+} // namespace
+} // namespace fuchsia_legacymetrics
diff --git a/chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.cc b/chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.cc
new file mode 100644
index 00000000000..65a5a025063
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.cc
@@ -0,0 +1,92 @@
+// 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/fuchsia_legacymetrics/legacymetrics_histogram_flattener.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/metrics/statistics_recorder.h"
+
+namespace fuchsia_legacymetrics {
+namespace {
+
+// Serializes changes to histogram metrics as FIDL structs.
+// Cannot be used in conjunction with other metrics collection systems (e.g.
+// UMA).
+class LegacyMetricsHistogramFlattener : public base::HistogramFlattener {
+ public:
+ LegacyMetricsHistogramFlattener() : histogram_snapshot_manager_(this) {}
+ ~LegacyMetricsHistogramFlattener() override = default;
+
+ LegacyMetricsHistogramFlattener(const LegacyMetricsHistogramFlattener&) =
+ delete;
+ LegacyMetricsHistogramFlattener& operator=(
+ const LegacyMetricsHistogramFlattener&) = delete;
+
+ // Returns a vector of changes to histogram data made since the last call to
+ // this method. Returns all histogram data when invoked for the first time.
+ std::vector<fuchsia::legacymetrics::Histogram> GetDeltas() {
+ DCHECK(histogram_deltas_.empty());
+
+ // Gather all histogram deltas, which will be sent to RecordDelta() and
+ // buffered in |histogram_deltas_|.
+ base::StatisticsRecorder::PrepareDeltas(
+ // Only return in-memory/non-persisted histograms.
+ false,
+ // Do not set flags on histograms.
+ base::Histogram::kNoFlags,
+ // Only upload metrics marked for UMA upload.
+ base::Histogram::kUmaTargetedHistogramFlag,
+ &histogram_snapshot_manager_);
+
+ return std::move(histogram_deltas_);
+ }
+
+ private:
+ // base::HistogramFlattener implementation.
+ // Appends the contents of |snapshot| for the specified |histogram| within
+ // |histogram_deltas_|.
+ void RecordDelta(const base::HistogramBase& histogram,
+ const base::HistogramSamples& snapshot) override {
+ DCHECK_NE(0, snapshot.TotalCount());
+
+ DVLOG(3) << "RecordDelta " << histogram.histogram_name();
+
+ fuchsia::legacymetrics::Histogram converted;
+ converted.set_name(histogram.histogram_name());
+ converted.set_sum(snapshot.sum());
+
+ for (std::unique_ptr<base::SampleCountIterator> it = snapshot.Iterator();
+ !it->Done(); it->Next()) {
+ base::Histogram::Sample min;
+ int64_t max = 0;
+ base::Histogram::Count count;
+ it->Get(&min, &max, &count);
+
+ fuchsia::legacymetrics::HistogramBucket bucket;
+ bucket.min = min;
+ bucket.max = max;
+ bucket.count = count;
+
+ DVLOG(4) << " Bucket: [" << min << "," << max << ") = " << count;
+ converted.mutable_buckets()->emplace_back(std::move(bucket));
+ }
+
+ histogram_deltas_.emplace_back(std::move(converted));
+ }
+
+ base::HistogramSnapshotManager histogram_snapshot_manager_;
+ std::vector<fuchsia::legacymetrics::Histogram> histogram_deltas_;
+};
+
+} // namespace
+
+std::vector<fuchsia::legacymetrics::Histogram> GetLegacyMetricsDeltas() {
+ return LegacyMetricsHistogramFlattener().GetDeltas();
+}
+
+} // namespace fuchsia_legacymetrics
diff --git a/chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.h b/chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.h
new file mode 100644
index 00000000000..773087f0501
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.h
@@ -0,0 +1,20 @@
+// 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 COMPONENTS_FUCHSIA_LEGACYMETRICS_LEGACYMETRICS_HISTOGRAM_FLATTENER_H_
+#define COMPONENTS_FUCHSIA_LEGACYMETRICS_LEGACYMETRICS_HISTOGRAM_FLATTENER_H_
+
+#include <fuchsia/legacymetrics/cpp/fidl.h>
+#include <vector>
+
+#include "base/metrics/histogram_flattener.h"
+#include "base/metrics/histogram_snapshot_manager.h"
+
+namespace fuchsia_legacymetrics {
+
+std::vector<fuchsia::legacymetrics::Histogram> GetLegacyMetricsDeltas();
+
+} // namespace fuchsia_legacymetrics
+
+#endif // COMPONENTS_FUCHSIA_LEGACYMETRICS_LEGACYMETRICS_HISTOGRAM_FLATTENER_H_
diff --git a/chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener_unittest.cc b/chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener_unittest.cc
new file mode 100644
index 00000000000..0a92d559303
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/legacymetrics_histogram_flattener_unittest.cc
@@ -0,0 +1,167 @@
+// 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/fuchsia_legacymetrics/legacymetrics_histogram_flattener.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using fuchsia::legacymetrics::Histogram;
+using fuchsia::legacymetrics::HistogramBucket;
+
+namespace fuchsia_legacymetrics {
+namespace {
+
+constexpr char kHistogramCount1M[] = "Foo.Bar";
+
+int64_t GetCount(int64_t value, const std::vector<HistogramBucket>& buckets) {
+ for (const HistogramBucket& bucket : buckets) {
+ if (value >= bucket.min && value < bucket.max)
+ return bucket.count;
+ }
+
+ return 0;
+}
+
+const fuchsia::legacymetrics::Histogram* LookupHistogram(
+ base::StringPiece name,
+ const std::vector<Histogram>& histograms) {
+ for (const auto& histogram : histograms) {
+ if (histogram.name() == name)
+ return &histogram;
+ }
+ return nullptr;
+}
+
+class LegacyMetricsHistogramFlattenerTest : public testing::Test {
+ public:
+ LegacyMetricsHistogramFlattenerTest() = default;
+ ~LegacyMetricsHistogramFlattenerTest() override = default;
+
+ void SetUp() override {
+ // Flush all histogram deltas from prior tests executed in this process.
+ GetLegacyMetricsDeltas();
+ }
+};
+
+TEST_F(LegacyMetricsHistogramFlattenerTest, NoHistogramData) {
+ EXPECT_TRUE(GetLegacyMetricsDeltas().empty());
+}
+
+TEST_F(LegacyMetricsHistogramFlattenerTest, Boolean) {
+ constexpr char kBooleanHistogram[] = "Foo.Bar.Boolean";
+ UMA_HISTOGRAM_BOOLEAN(kBooleanHistogram, true);
+ UMA_HISTOGRAM_BOOLEAN(kBooleanHistogram, true);
+ UMA_HISTOGRAM_BOOLEAN(kBooleanHistogram, false);
+
+ auto deltas = GetLegacyMetricsDeltas();
+ EXPECT_EQ(1,
+ GetCount(0, LookupHistogram(kBooleanHistogram, deltas)->buckets()));
+ EXPECT_EQ(2,
+ GetCount(1, LookupHistogram(kBooleanHistogram, deltas)->buckets()));
+}
+
+TEST_F(LegacyMetricsHistogramFlattenerTest, Linear) {
+ constexpr char kLinearHistogram[] = "Foo.Bar.Linear";
+
+ for (int i = 0; i < 200; ++i) {
+ UMA_HISTOGRAM_EXACT_LINEAR(kLinearHistogram, i, 200);
+ }
+
+ auto deltas = GetLegacyMetricsDeltas();
+
+ for (int i = 0; i < 200; ++i) {
+ EXPECT_EQ(
+ 1, GetCount(i, LookupHistogram(kLinearHistogram, deltas)->buckets()));
+ }
+}
+
+TEST_F(LegacyMetricsHistogramFlattenerTest, Percentage) {
+ constexpr char kPercentageHistogram[] = "Foo.Bar.Percentage";
+
+ for (int i = 0; i <= 100; ++i) {
+ for (int j = 0; j < i; ++j)
+ UMA_HISTOGRAM_PERCENTAGE(kPercentageHistogram, i);
+ }
+
+ auto deltas = GetLegacyMetricsDeltas();
+
+ for (int i = 0; i <= 100; ++i) {
+ EXPECT_EQ(
+ i,
+ GetCount(i, LookupHistogram(kPercentageHistogram, deltas)->buckets()));
+ }
+}
+
+enum Fruit {
+ APPLE,
+ BANANA,
+ PEAR,
+ FRUIT_MAX = PEAR,
+};
+
+TEST_F(LegacyMetricsHistogramFlattenerTest, Enumeration) {
+ constexpr char kEnumHistogram[] = "Foo.Bar.Enumeration";
+
+ UMA_HISTOGRAM_ENUMERATION(kEnumHistogram, APPLE, FRUIT_MAX);
+ UMA_HISTOGRAM_ENUMERATION(kEnumHistogram, BANANA, FRUIT_MAX);
+ UMA_HISTOGRAM_ENUMERATION(kEnumHistogram, BANANA, FRUIT_MAX);
+ UMA_HISTOGRAM_ENUMERATION(kEnumHistogram, BANANA, FRUIT_MAX);
+ UMA_HISTOGRAM_ENUMERATION(kEnumHistogram, PEAR, FRUIT_MAX);
+
+ auto deltas = GetLegacyMetricsDeltas();
+
+ EXPECT_EQ(
+ 1, GetCount(APPLE, LookupHistogram(kEnumHistogram, deltas)->buckets()));
+ EXPECT_EQ(
+ 3, GetCount(BANANA, LookupHistogram(kEnumHistogram, deltas)->buckets()));
+ EXPECT_EQ(1,
+ GetCount(PEAR, LookupHistogram(kEnumHistogram, deltas)->buckets()));
+}
+
+TEST_F(LegacyMetricsHistogramFlattenerTest, NoNewData) {
+ UMA_HISTOGRAM_COUNTS_1M(kHistogramCount1M, 20);
+
+ auto deltas = GetLegacyMetricsDeltas();
+ EXPECT_EQ(
+ 1, GetCount(20, LookupHistogram(kHistogramCount1M, deltas)->buckets()));
+
+ // No changes to a histogram means we should not be seeing it in the deltas.
+ deltas = GetLegacyMetricsDeltas();
+ EXPECT_TRUE(deltas.empty());
+}
+
+TEST_F(LegacyMetricsHistogramFlattenerTest, MultipleHistograms) {
+ constexpr char kAnotherHistogram[] = "Foo.Bar2";
+
+ UMA_HISTOGRAM_COUNTS_1M(kHistogramCount1M, 20);
+ UMA_HISTOGRAM_COUNTS_1M(kAnotherHistogram, 1000);
+ UMA_HISTOGRAM_COUNTS_1M(kAnotherHistogram, 1000);
+
+ auto deltas = GetLegacyMetricsDeltas();
+ EXPECT_EQ(
+ 1, GetCount(20, LookupHistogram(kHistogramCount1M, deltas)->buckets()));
+ EXPECT_EQ(
+ 2, GetCount(1000, LookupHistogram(kAnotherHistogram, deltas)->buckets()));
+}
+
+TEST_F(LegacyMetricsHistogramFlattenerTest, MultipleBuckets) {
+ UMA_HISTOGRAM_COUNTS_1M(kHistogramCount1M, 20);
+ UMA_HISTOGRAM_COUNTS_1M(kHistogramCount1M, 1000);
+
+ auto deltas = GetLegacyMetricsDeltas();
+ EXPECT_EQ(
+ 1, GetCount(20, LookupHistogram(kHistogramCount1M, deltas)->buckets()));
+ EXPECT_EQ(
+ 1, GetCount(1000, LookupHistogram(kHistogramCount1M, deltas)->buckets()));
+
+ UMA_HISTOGRAM_COUNTS_1M(kHistogramCount1M, 1000);
+ UMA_HISTOGRAM_COUNTS_1M(kHistogramCount1M, 1000);
+ deltas = GetLegacyMetricsDeltas();
+ EXPECT_EQ(
+ 2, GetCount(1000, LookupHistogram(kHistogramCount1M, deltas)->buckets()));
+}
+
+} // namespace
+} // namespace fuchsia_legacymetrics
diff --git a/chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.cc b/chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.cc
new file mode 100644
index 00000000000..d33af51c600
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.cc
@@ -0,0 +1,48 @@
+// 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 "components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/metrics/user_metrics.h"
+#include "base/time/time.h"
+
+namespace fuchsia_legacymetrics {
+
+constexpr size_t LegacyMetricsUserActionRecorder::kMaxEventCount;
+
+LegacyMetricsUserActionRecorder::LegacyMetricsUserActionRecorder()
+ : on_event_callback_(
+ base::BindRepeating(&LegacyMetricsUserActionRecorder::OnUserAction,
+ base::Unretained(this))) {
+ base::AddActionCallback(on_event_callback_);
+}
+
+LegacyMetricsUserActionRecorder::~LegacyMetricsUserActionRecorder() {
+ base::RemoveActionCallback(on_event_callback_);
+}
+
+bool LegacyMetricsUserActionRecorder::HasEvents() const {
+ return !events_.empty();
+}
+
+std::vector<fuchsia::legacymetrics::UserActionEvent>
+LegacyMetricsUserActionRecorder::TakeEvents() {
+ return std::move(events_);
+}
+
+void LegacyMetricsUserActionRecorder::OnUserAction(const std::string& action,
+ base::TimeTicks time) {
+ if (events_.size() >= kMaxEventCount)
+ return;
+
+ fuchsia::legacymetrics::UserActionEvent fidl_event;
+ fidl_event.set_name(action);
+ fidl_event.set_time(time.ToZxTime());
+ events_.push_back(std::move(fidl_event));
+}
+
+} // namespace fuchsia_legacymetrics
diff --git a/chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.h b/chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.h
new file mode 100644
index 00000000000..0ab7d4023a0
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef COMPONENTS_FUCHSIA_LEGACYMETRICS_LEGACYMETRICS_USER_EVENT_RECORDER_H_
+#define COMPONENTS_FUCHSIA_LEGACYMETRICS_LEGACYMETRICS_USER_EVENT_RECORDER_H_
+
+#include <fuchsia/legacymetrics/cpp/fidl.h>
+#include <string>
+#include <vector>
+
+#include "base/metrics/user_metrics.h"
+
+namespace fuchsia_legacymetrics {
+
+// Captures and stores user action events, and converts them to
+// fuchsia.legacymetrics equivalent.
+class LegacyMetricsUserActionRecorder {
+ public:
+ // Maximum number of Events to store locally before dropping new ones.
+ static constexpr size_t kMaxEventCount = 5000;
+
+ LegacyMetricsUserActionRecorder();
+ ~LegacyMetricsUserActionRecorder();
+
+ LegacyMetricsUserActionRecorder(const LegacyMetricsUserActionRecorder&) =
+ delete;
+ LegacyMetricsUserActionRecorder& operator=(
+ const LegacyMetricsUserActionRecorder&) = delete;
+
+ bool HasEvents() const;
+ std::vector<fuchsia::legacymetrics::UserActionEvent> TakeEvents();
+
+ private:
+ // base::ActionCallback implementation.
+ void OnUserAction(const std::string& action, base::TimeTicks time);
+
+ std::vector<fuchsia::legacymetrics::UserActionEvent> events_;
+ const base::ActionCallback on_event_callback_;
+};
+
+} // namespace fuchsia_legacymetrics
+
+#endif // COMPONENTS_FUCHSIA_LEGACYMETRICS_LEGACYMETRICS_USER_EVENT_RECORDER_H_
diff --git a/chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder_unittest.cc b/chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder_unittest.cc
new file mode 100644
index 00000000000..8fa8174f6bd
--- /dev/null
+++ b/chromium/components/fuchsia_legacymetrics/legacymetrics_user_event_recorder_unittest.cc
@@ -0,0 +1,90 @@
+// 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/fuchsia_legacymetrics/legacymetrics_user_event_recorder.h"
+
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fuchsia_legacymetrics {
+namespace {
+
+class LegacyMetricsUserActionRecorderTest : public testing::Test {
+ public:
+ LegacyMetricsUserActionRecorderTest() = default;
+ ~LegacyMetricsUserActionRecorderTest() override = default;
+
+ void SetUp() override {
+ base::SetRecordActionTaskRunner(base::ThreadTaskRunnerHandle::Get());
+ }
+
+ private:
+ base::test::SingleThreadTaskEnvironment task_environment;
+};
+
+TEST_F(LegacyMetricsUserActionRecorderTest, ProduceAndConsume) {
+ constexpr char kExpectedUserAction1[] = "Hello";
+ constexpr char kExpectedUserAction2[] = "There";
+
+ zx_time_t time_start = base::TimeTicks::Now().ToZxTime();
+ LegacyMetricsUserActionRecorder buffer;
+ base::RecordComputedAction(kExpectedUserAction1);
+ EXPECT_TRUE(buffer.HasEvents());
+ base::RecordComputedAction(kExpectedUserAction2);
+
+ auto events = buffer.TakeEvents();
+ EXPECT_FALSE(buffer.HasEvents());
+ EXPECT_EQ(2u, events.size());
+
+ // Verify the contents of the buffer are as expected.
+ EXPECT_EQ(kExpectedUserAction1, events[0].name());
+ EXPECT_GE(events[0].time(), time_start);
+ EXPECT_EQ(kExpectedUserAction2, events[1].name());
+ EXPECT_GE(events[1].time(), time_start);
+ EXPECT_GE(events[1].time(), events[0].time());
+
+ // Verify that the buffer is now empty.
+ EXPECT_TRUE(buffer.TakeEvents().empty());
+
+ // Add more data to the buffer, and then verify it, to ensure that recording
+ // continues to work.
+ base::RecordComputedAction(kExpectedUserAction2);
+ EXPECT_TRUE(buffer.HasEvents());
+ events = buffer.TakeEvents();
+ EXPECT_FALSE(buffer.HasEvents());
+ EXPECT_EQ(1u, events.size());
+ EXPECT_EQ(kExpectedUserAction2, events[0].name());
+}
+
+TEST_F(LegacyMetricsUserActionRecorderTest, RecorderDeleted) {
+ auto buffer = std::make_unique<LegacyMetricsUserActionRecorder>();
+ buffer.reset();
+
+ // |buffer| was destroyed, so check that recording actions doesn't cause a
+ // use-after-free error.
+ base::RecordComputedAction("NoCrashingPlz");
+}
+
+TEST_F(LegacyMetricsUserActionRecorderTest, EmptyBuffer) {
+ LegacyMetricsUserActionRecorder buffer;
+ EXPECT_FALSE(buffer.HasEvents());
+}
+
+TEST_F(LegacyMetricsUserActionRecorderTest, EnforcesMaximumEventCount) {
+ LegacyMetricsUserActionRecorder buffer;
+
+ // Try recording twice the maximum number of locally stored events.
+ for (int i = 0, count = LegacyMetricsUserActionRecorder::kMaxEventCount * 2;
+ i < count; i++) {
+ base::RecordComputedAction("Test");
+ }
+
+ EXPECT_TRUE(buffer.HasEvents());
+ auto events = buffer.TakeEvents();
+ EXPECT_EQ(LegacyMetricsUserActionRecorder::kMaxEventCount, events.size());
+}
+
+} // namespace
+} // namespace fuchsia_legacymetrics
diff --git a/chromium/components/fullscreen_control/subtle_notification_view.cc b/chromium/components/fullscreen_control/subtle_notification_view.cc
index 78669108281..e9aec19b7dd 100644
--- a/chromium/components/fullscreen_control/subtle_notification_view.cc
+++ b/chromium/components/fullscreen_control/subtle_notification_view.cc
@@ -183,9 +183,9 @@ ADD_PROPERTY_METADATA(std::u16string, Text)
END_METADATA
SubtleNotificationView::SubtleNotificationView() : instruction_view_(nullptr) {
- std::unique_ptr<views::BubbleBorder> bubble_border(new views::BubbleBorder(
- views::BubbleBorder::NONE, views::BubbleBorder::NO_SHADOW,
- kSubtleNotificationBackgroundColor));
+ auto bubble_border = std::make_unique<views::BubbleBorder>(
+ views::BubbleBorder::NONE, views::BubbleBorder::NO_SHADOW);
+ bubble_border->SetColor(kSubtleNotificationBackgroundColor);
SetBackground(std::make_unique<views::BubbleBackground>(bubble_border.get()));
SetBorder(std::move(bubble_border));
diff --git a/chromium/components/gcm_driver/android/BUILD.gn b/chromium/components/gcm_driver/android/BUILD.gn
index f7de29aa30c..4a409b91df1 100644
--- a/chromium/components/gcm_driver/android/BUILD.gn
+++ b/chromium/components/gcm_driver/android/BUILD.gn
@@ -11,6 +11,8 @@ generate_jni("jni_headers") {
android_library("gcm_driver_java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/android_deps:com_google_code_findbugs_jsr305_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
diff --git a/chromium/components/gcm_driver/crypto/BUILD.gn b/chromium/components/gcm_driver/crypto/BUILD.gn
index 34319118279..16772078554 100644
--- a/chromium/components/gcm_driver/crypto/BUILD.gn
+++ b/chromium/components/gcm_driver/crypto/BUILD.gn
@@ -29,8 +29,8 @@ static_library("crypto") {
"//components/gcm_driver/crypto/proto",
"//components/leveldb_proto",
"//crypto",
- "//crypto:platform",
"//net",
+ "//third_party/boringssl",
"//third_party/leveldatabase",
"//third_party/protobuf:protobuf_lite",
]
@@ -70,7 +70,6 @@ source_set("unit_tests") {
"//components/gcm_driver/crypto/proto",
"//components/leveldb_proto",
"//crypto",
- "//crypto:platform",
"//net:test_support",
"//testing/gtest",
"//third_party/protobuf:protobuf_lite",
diff --git a/chromium/components/gcm_driver/crypto/encryption_header_parsers.cc b/chromium/components/gcm_driver/crypto/encryption_header_parsers.cc
index b70dc5276c6..a695f354ca9 100644
--- a/chromium/components/gcm_driver/crypto/encryption_header_parsers.cc
+++ b/chromium/components/gcm_driver/crypto/encryption_header_parsers.cc
@@ -83,16 +83,16 @@ bool EncryptionHeaderIterator::GetNext() {
const base::StringPiece name = name_value_pairs.name_piece();
const base::StringPiece value = name_value_pairs.value_piece();
- if (base::LowerCaseEqualsASCII(name, "keyid")) {
+ if (base::EqualsCaseInsensitiveASCII(name, "keyid")) {
if (found_keyid)
return false;
keyid_.assign(value.data(), value.size());
found_keyid = true;
- } else if (base::LowerCaseEqualsASCII(name, "salt")) {
+ } else if (base::EqualsCaseInsensitiveASCII(name, "salt")) {
if (found_salt || !ValueToDecodedString(value, &salt_))
return false;
found_salt = true;
- } else if (base::LowerCaseEqualsASCII(name, "rs")) {
+ } else if (base::EqualsCaseInsensitiveASCII(name, "rs")) {
if (found_rs || !RecordSizeToInt(value, &rs_))
return false;
found_rs = true;
@@ -132,16 +132,16 @@ bool CryptoKeyHeaderIterator::GetNext() {
const base::StringPiece name = name_value_pairs.name_piece();
const base::StringPiece value = name_value_pairs.value_piece();
- if (base::LowerCaseEqualsASCII(name, "keyid")) {
+ if (base::EqualsCaseInsensitiveASCII(name, "keyid")) {
if (found_keyid)
return false;
keyid_.assign(value.data(), value.size());
found_keyid = true;
- } else if (base::LowerCaseEqualsASCII(name, "aesgcm128")) {
+ } else if (base::EqualsCaseInsensitiveASCII(name, "aesgcm128")) {
if (found_aesgcm128 || !ValueToDecodedString(value, &aesgcm128_))
return false;
found_aesgcm128 = true;
- } else if (base::LowerCaseEqualsASCII(name, "dh")) {
+ } else if (base::EqualsCaseInsensitiveASCII(name, "dh")) {
if (found_dh || !ValueToDecodedString(value, &dh_))
return false;
found_dh = true;
diff --git a/chromium/components/gcm_driver/gcm_profile_service.cc b/chromium/components/gcm_driver/gcm_profile_service.cc
index 6b6bed3d2ff..27c3ea2b5d5 100644
--- a/chromium/components/gcm_driver/gcm_profile_service.cc
+++ b/chromium/components/gcm_driver/gcm_profile_service.cc
@@ -157,11 +157,17 @@ GCMProfileService::GCMProfileService(
scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
: identity_manager_(identity_manager),
url_loader_factory_(std::move(url_loader_factory)) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
signin::IdentityManager::AccountIdMigrationState id_migration =
identity_manager_->GetAccountIdMigrationState();
bool remove_account_mappings_with_email_key =
(id_migration == signin::IdentityManager::MIGRATION_IN_PROGRESS) ||
(id_migration == signin::IdentityManager::MIGRATION_DONE);
+#else
+ // Migration is done on non-ChromeOS platforms.
+ bool remove_account_mappings_with_email_key = false;
+#endif
+
driver_ = CreateGCMDriverDesktop(
std::move(gcm_client_factory), prefs,
path.Append(gcm_driver::kGCMStoreDirname),
diff --git a/chromium/components/gcm_driver/instance_id/android/BUILD.gn b/chromium/components/gcm_driver/instance_id/android/BUILD.gn
index 0e0beb64b08..251afe2d003 100644
--- a/chromium/components/gcm_driver/instance_id/android/BUILD.gn
+++ b/chromium/components/gcm_driver/instance_id/android/BUILD.gn
@@ -17,6 +17,8 @@ android_library("instance_id_driver_java") {
deps = [
"$google_play_services_package:google_play_services_iid_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/gcm_driver/android:gcm_driver_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
@@ -35,7 +37,7 @@ android_library("instance_id_driver_test_support_java") {
deps = [
":instance_id_driver_java",
"$google_play_services_package:google_play_services_iid_java",
- "//base:base_java",
+ "//base:jni_java",
]
sources = [ "javatests/src/org/chromium/components/gcm_driver/instance_id/FakeInstanceIDWithSubtype.java" ]
diff --git a/chromium/components/global_media_controls/public/media_session_item_producer.cc b/chromium/components/global_media_controls/public/media_session_item_producer.cc
index bb142c7cac3..358ee9d7f95 100644
--- a/chromium/components/global_media_controls/public/media_session_item_producer.cc
+++ b/chromium/components/global_media_controls/public/media_session_item_producer.cc
@@ -345,6 +345,7 @@ void MediaSessionItemProducer::OnMediaItemUIDismissed(const std::string& id) {
session->set_dismiss_reason(
GlobalMediaControlsDismissReason::kUserDismissedNotification);
+ session->item()->Stop();
session->item()->Dismiss();
}
diff --git a/chromium/components/global_media_controls/public/media_session_notification_item.cc b/chromium/components/global_media_controls/public/media_session_notification_item.cc
index f01ff8e11fc..5f4b739cb69 100644
--- a/chromium/components/global_media_controls/public/media_session_notification_item.cc
+++ b/chromium/components/global_media_controls/public/media_session_notification_item.cc
@@ -176,8 +176,6 @@ void MediaSessionNotificationItem::SeekTo(base::TimeDelta time) {
}
void MediaSessionNotificationItem::Dismiss() {
- if (media_controller_remote_.is_bound())
- media_controller_remote_->Stop();
delegate_->RemoveItem(request_id_);
}
@@ -185,6 +183,11 @@ media_message_center::SourceType MediaSessionNotificationItem::SourceType() {
return media_message_center::SourceType::kLocalMediaSession;
}
+void MediaSessionNotificationItem::Stop() {
+ if (media_controller_remote_.is_bound())
+ media_controller_remote_->Stop();
+}
+
void MediaSessionNotificationItem::Raise() {
if (!media_controller_remote_.is_bound())
return;
diff --git a/chromium/components/global_media_controls/public/media_session_notification_item.h b/chromium/components/global_media_controls/public/media_session_notification_item.h
index 5e8607a0efa..38f8b9db6dc 100644
--- a/chromium/components/global_media_controls/public/media_session_notification_item.h
+++ b/chromium/components/global_media_controls/public/media_session_notification_item.h
@@ -96,6 +96,9 @@ class COMPONENT_EXPORT(GLOBAL_MEDIA_CONTROLS) MediaSessionNotificationItem
void SetVolume(float volume) override {}
void SetMute(bool mute) override;
+ // Stops the media session.
+ void Stop();
+
// Calls |Raise()| on the underlying MediaSession, which will focus the
// WebContents if the MediaSession is associated with one.
void Raise();
diff --git a/chromium/components/global_media_controls/public/views/media_item_ui_view_unittest.cc b/chromium/components/global_media_controls/public/views/media_item_ui_view_unittest.cc
index fd758248cb5..95922fba858 100644
--- a/chromium/components/global_media_controls/public/views/media_item_ui_view_unittest.cc
+++ b/chromium/components/global_media_controls/public/views/media_item_ui_view_unittest.cc
@@ -60,7 +60,7 @@ class MediaItemUIViewTest : public views::ViewsTestBase {
auto device_selector =
std::make_unique<NiceMock<test::MockMediaItemUIDeviceSelector>>();
device_selector_ = device_selector.get();
- device_selector_->SetPreferredSize({400, 50});
+ device_selector_->SetPreferredSize(gfx::Size(400, 50));
item_ui_ = widget_->SetContentsView(std::make_unique<MediaItemUIView>(
kTestNotificationId, item_->GetWeakPtr(), std::move(footer),
diff --git a/chromium/components/google/core/common/google_util.cc b/chromium/components/google/core/common/google_util.cc
index 7ce2a5ebbdb..0d9cb609591 100644
--- a/chromium/components/google/core/common/google_util.cc
+++ b/chromium/components/google/core/common/google_util.cc
@@ -72,7 +72,7 @@ bool IsValidHostName(base::StringPiece host,
if (!allowed_tlds.contains(tld))
return false;
- if (base::LowerCaseEqualsASCII(host_minus_tld, domain_in_lower_case))
+ if (base::EqualsCaseInsensitiveASCII(host_minus_tld, domain_in_lower_case))
return true;
if (subdomain_permission == ALLOW_SUBDOMAIN) {
@@ -82,7 +82,7 @@ bool IsValidHostName(base::StringPiece host,
}
std::string www_domain = base::StrCat({"www.", domain_in_lower_case});
- return base::LowerCaseEqualsASCII(host_minus_tld, www_domain);
+ return base::EqualsCaseInsensitiveASCII(host_minus_tld, www_domain);
}
// True if |url| is a valid URL with HTTP or HTTPS scheme. If |port_permission|
@@ -311,7 +311,7 @@ bool IsGoogleAssociatedDomainUrl(const GURL& url) {
"googleweblight.com",
};
for (size_t i = 0; i < std::size(kHostsToSetHeadersFor); ++i) {
- if (base::LowerCaseEqualsASCII(host, kHostsToSetHeadersFor[i]))
+ if (base::EqualsCaseInsensitiveASCII(host, kHostsToSetHeadersFor[i]))
return true;
}
diff --git a/chromium/components/guest_view/browser/guest_view_base.cc b/chromium/components/guest_view/browser/guest_view_base.cc
index b2b771157cf..033264f9b6e 100644
--- a/chromium/components/guest_view/browser/guest_view_base.cc
+++ b/chromium/components/guest_view/browser/guest_view_base.cc
@@ -416,7 +416,10 @@ WebContents* GuestViewBase::GetOwnerWebContents() {
}
const GURL& GuestViewBase::GetOwnerSiteURL() const {
- return owner_web_contents()->GetMainFrame()->GetSiteInstance()->GetSiteURL();
+ return owner_web_contents()
+ ->GetPrimaryMainFrame()
+ ->GetSiteInstance()
+ ->GetSiteURL();
}
bool GuestViewBase::ShouldDestroyOnDetach() const {
@@ -554,7 +557,7 @@ double GuestViewBase::PhysicalPixelsToLogicalPixels(int physical_pixels) const {
void GuestViewBase::DidStopLoading() {
content::RenderViewHost* rvh =
- web_contents()->GetMainFrame()->GetRenderViewHost();
+ web_contents()->GetPrimaryMainFrame()->GetRenderViewHost();
if (IsPreferredSizeModeEnabled())
rvh->EnablePreferredSizeMode();
@@ -581,9 +584,9 @@ void GuestViewBase::WebContentsDestroyed() {
void GuestViewBase::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
- // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
- // frames. This caller was converted automatically to the primary main frame
- // to preserve its semantics. Follow up to confirm correctness.
+ // TODO(crbug.com/1261928): Due to the use of inner WebContents, a
+ // GuestViewBase's main frame is considered primary. This will no
+ // longer be the case once we migrate guest views to MPArch.
if (!navigation_handle->IsInPrimaryMainFrame() ||
!navigation_handle->HasCommitted())
return;
diff --git a/chromium/components/guest_view/browser/guest_view_manager.cc b/chromium/components/guest_view/browser/guest_view_manager.cc
index a16ee6cca54..167aa21b9a3 100644
--- a/chromium/components/guest_view/browser/guest_view_manager.cc
+++ b/chromium/components/guest_view/browser/guest_view_manager.cc
@@ -520,7 +520,7 @@ bool GuestViewManager::CanEmbedderAccessInstanceID(
guest_view->CanBeEmbeddedInsideCrossProcessFrames()
? guest_view->GetOwnerSiteInstance()->GetProcess()->GetID()
: guest_view->owner_web_contents()
- ->GetMainFrame()
+ ->GetPrimaryMainFrame()
->GetProcess()
->GetID();
diff --git a/chromium/components/gwp_asan/client/sampling_partitionalloc_shims.cc b/chromium/components/gwp_asan/client/sampling_partitionalloc_shims.cc
index 20393f004a4..6b15d6ba604 100644
--- a/chromium/components/gwp_asan/client/sampling_partitionalloc_shims.cc
+++ b/chromium/components/gwp_asan/client/sampling_partitionalloc_shims.cc
@@ -26,7 +26,10 @@ SamplingState<PARTITIONALLOC> sampling_state;
// for every access.
GuardedPageAllocator* gpa = nullptr;
-bool AllocationHook(void** out, int flags, size_t size, const char* type_name) {
+bool AllocationHook(void** out,
+ unsigned int flags,
+ size_t size,
+ const char* type_name) {
if (UNLIKELY(sampling_state.Sample())) {
// Ignore allocation requests with unknown flags.
constexpr int kKnownFlags = partition_alloc::AllocFlags::kReturnNull |
diff --git a/chromium/components/heap_profiling/in_process/heap_profiler_controller.cc b/chromium/components/heap_profiling/in_process/heap_profiler_controller.cc
index 77e26ad2c4a..61523cc3619 100644
--- a/chromium/components/heap_profiling/in_process/heap_profiler_controller.cc
+++ b/chromium/components/heap_profiling/in_process/heap_profiler_controller.cc
@@ -6,6 +6,7 @@
#include <cmath>
#include <limits>
+#include <utility>
#include "base/bind.h"
#include "base/feature_list.h"
@@ -140,6 +141,23 @@ void RecordUmaSnapshotInterval(base::TimeDelta interval,
constexpr base::Feature HeapProfilerController::kHeapProfilerReporting{
"HeapProfilerReporting", base::FEATURE_ENABLED_BY_DEFAULT};
+HeapProfilerController::SnapshotParams::SnapshotParams(
+ base::TimeDelta mean_interval,
+ bool use_random_interval,
+ scoped_refptr<StoppedFlag> stopped)
+ : mean_interval(mean_interval),
+ use_random_interval(use_random_interval),
+ stopped(std::move(stopped)) {}
+
+HeapProfilerController::SnapshotParams::~SnapshotParams() = default;
+
+HeapProfilerController::SnapshotParams::SnapshotParams(SnapshotParams&& other) =
+ default;
+
+HeapProfilerController::SnapshotParams&
+HeapProfilerController::SnapshotParams::operator=(SnapshotParams&& other) =
+ default;
+
HeapProfilerController::HeapProfilerController(version_info::Channel channel)
: profiling_enabled_(DecideIfCollectionIsEnabled(channel)),
stopped_(base::MakeRefCounted<StoppedFlag>()) {}
@@ -159,9 +177,10 @@ void HeapProfilerController::Start() {
base::SamplingHeapProfiler::Get()->Start();
const int interval = kCollectionIntervalMinutes.Get();
DCHECK_GT(interval, 0);
- ScheduleNextSnapshot(
- stopped_, {.interval = base::Minutes(interval),
- .use_random_interval = !suppress_randomness_for_testing_});
+ SnapshotParams params(
+ /*mean_interval=*/base::Minutes(interval),
+ /*use_random_interval=*/!suppress_randomness_for_testing_, stopped_);
+ ScheduleNextSnapshot(std::move(params));
}
void HeapProfilerController::SuppressRandomnessForTesting() {
@@ -169,31 +188,26 @@ void HeapProfilerController::SuppressRandomnessForTesting() {
}
// static
-void HeapProfilerController::ScheduleNextSnapshot(
- scoped_refptr<StoppedFlag> stopped,
- CollectionInterval heap_collection_interval) {
- base::TimeDelta interval =
- heap_collection_interval.use_random_interval
- ? RandomInterval(heap_collection_interval.interval)
- : heap_collection_interval.interval;
+void HeapProfilerController::ScheduleNextSnapshot(SnapshotParams params) {
+ base::TimeDelta interval = params.use_random_interval
+ ? RandomInterval(params.mean_interval)
+ : params.mean_interval;
RecordUmaSnapshotInterval(interval, "Scheduled");
base::ThreadPool::PostDelayedTask(
FROM_HERE, {base::TaskPriority::BEST_EFFORT},
- base::BindOnce(&HeapProfilerController::TakeSnapshot, std::move(stopped),
- heap_collection_interval, /*previous_interval=*/interval),
+ base::BindOnce(&HeapProfilerController::TakeSnapshot, std::move(params),
+ /*previous_interval=*/interval),
interval);
}
// static
-void HeapProfilerController::TakeSnapshot(
- scoped_refptr<StoppedFlag> stopped,
- CollectionInterval heap_collection_interval,
- base::TimeDelta previous_interval) {
- if (stopped->data.IsSet())
+void HeapProfilerController::TakeSnapshot(SnapshotParams params,
+ base::TimeDelta previous_interval) {
+ if (params.stopped->data.IsSet())
return;
RecordUmaSnapshotInterval(previous_interval, "Taken");
RetrieveAndSendSnapshot();
- ScheduleNextSnapshot(std::move(stopped), heap_collection_interval);
+ ScheduleNextSnapshot(std::move(params));
}
// static
diff --git a/chromium/components/heap_profiling/in_process/heap_profiler_controller.h b/chromium/components/heap_profiling/in_process/heap_profiler_controller.h
index 65ce8929580..241dc94bd3a 100644
--- a/chromium/components/heap_profiling/in_process/heap_profiler_controller.h
+++ b/chromium/components/heap_profiling/in_process/heap_profiler_controller.h
@@ -70,19 +70,38 @@ class HeapProfilerController {
private:
using StoppedFlag = base::RefCountedData<base::AtomicFlag>;
- struct CollectionInterval {
- base::TimeDelta interval;
+ // Parameters to control the snapshot sampling and reporting. This is
+ // move-only so that it can be safely passed between threads to the static
+ // snapshot functions.
+ struct SnapshotParams {
+ SnapshotParams(base::TimeDelta mean_interval,
+ bool use_random_interval,
+ scoped_refptr<StoppedFlag> stopped);
+ ~SnapshotParams();
+
+ // Move-only.
+ SnapshotParams(const SnapshotParams& other) = delete;
+ SnapshotParams& operator=(const SnapshotParams& other) = delete;
+ SnapshotParams(SnapshotParams&& other);
+ SnapshotParams& operator=(SnapshotParams&& other);
+
+ // Mean interval until the next snapshot.
+ base::TimeDelta mean_interval;
+
+ // If true, generate a random time centered around `mean_interval`.
+ // Otherwise use `mean_interval` exactly.
bool use_random_interval = false;
+
+ // Atomic flag to signal that no more snapshots should be taken.
+ scoped_refptr<StoppedFlag> stopped;
};
- static void ScheduleNextSnapshot(scoped_refptr<StoppedFlag> stopped,
- CollectionInterval heap_collection_interval);
- // Takes a heap snapshot unless the `stopped` flag is set.
- // `heap_collection_interval` is used to schedule the next snapshot.
+ static void ScheduleNextSnapshot(SnapshotParams params);
+
+ // Takes a heap snapshot unless the `params.stopped` flag is set.
// `previous_interval` is the time since the previous snapshot, which is used
// to log metrics about snapshot frequency.
- static void TakeSnapshot(scoped_refptr<StoppedFlag> stopped,
- CollectionInterval heap_collection_interval,
+ static void TakeSnapshot(SnapshotParams params,
base::TimeDelta previous_interval);
static void RetrieveAndSendSnapshot();
@@ -91,6 +110,10 @@ class HeapProfilerController {
// and the probability parameters of the HeapProfilerReporting feature.
const bool profiling_enabled_;
+ // This flag is set when the HeapProfilerController is torn down, to stop
+ // profiling. It is the only member that should be referenced by the static
+ // functions, to be sure that they can run on the thread pool while
+ // HeapProfilerController is deleted on the main thread.
scoped_refptr<StoppedFlag> stopped_;
bool suppress_randomness_for_testing_ = false;
};
diff --git a/chromium/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc b/chromium/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc
index 0fff757e680..9a74c70ac00 100644
--- a/chromium/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc
+++ b/chromium/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc
@@ -72,12 +72,9 @@ class HeapProfilerControllerTest : public ::testing::Test {
// Clear any samples set in the global SamplingHeapProfiler before the
// ScopedMuteHookedSamplesForTesting was created.
base::SamplingHeapProfiler::Get()->ClearSamplesForTesting();
- base::PoissonAllocationSampler::Get()->SuppressRandomnessForTest(true);
}
~HeapProfilerControllerTest() override {
- base::PoissonAllocationSampler::Get()->SuppressRandomnessForTest(false);
-
// Remove any callback that was set in StartHeapProfiling.
metrics::CallStackProfileBuilder::SetBrowserProcessReceiverCallback(
base::DoNothing());
@@ -120,6 +117,8 @@ class HeapProfilerControllerTest : public ::testing::Test {
// allocations aren't sampled while TaskEnvironment creates a thread. The
// sampling is crashing in the hooked FreeFunc on some test bots.
base::PoissonAllocationSampler::ScopedMuteHookedSamplesForTesting mute_hooks_;
+ base::PoissonAllocationSampler::ScopedSuppressRandomnessForTesting
+ suppress_randomness_;
// Create `feature_list_` before `task_environment_` and destroy it after to
// avoid a race in destruction.
diff --git a/chromium/components/heap_profiling/multi_process/BUILD.gn b/chromium/components/heap_profiling/multi_process/BUILD.gn
index 2f43f5da487..42400d54d5a 100644
--- a/chromium/components/heap_profiling/multi_process/BUILD.gn
+++ b/chromium/components/heap_profiling/multi_process/BUILD.gn
@@ -34,7 +34,7 @@ if (is_android) {
android_library("heap_profiling_java_test_support") {
testonly = true
sources = [ "javatests/src/org/chromium/components/heap_profiling/multi_process/HeapProfilingTestShim.java" ]
- deps = [ "//base:base_java" ]
+ deps = [ "//build/android:build_java" ]
}
}
diff --git a/chromium/components/history/core/DEPS b/chromium/components/history/core/DEPS
index 636e2001055..d60f9937cdd 100644
--- a/chromium/components/history/core/DEPS
+++ b/chromium/components/history/core/DEPS
@@ -2,6 +2,6 @@ include_rules = [
# history is a layered-component, forbid dependency on //content
"-components/history/content",
"-content",
-
+ "+components/search_engines",
"+components/url_formatter",
]
diff --git a/chromium/components/history/core/browser/BUILD.gn b/chromium/components/history/core/browser/BUILD.gn
index 453deded924..deab0cffbe6 100644
--- a/chromium/components/history/core/browser/BUILD.gn
+++ b/chromium/components/history/core/browser/BUILD.gn
@@ -20,6 +20,8 @@ static_library("browser") {
"download_types.h",
"expire_history_backend.cc",
"expire_history_backend.h",
+ "features.cc",
+ "features.h",
"history_backend.cc",
"history_backend.h",
"history_backend_client.h",
@@ -46,12 +48,20 @@ static_library("browser") {
"keyword_id.h",
"keyword_search_term.cc",
"keyword_search_term.h",
+ "keyword_search_term_util.cc",
+ "keyword_search_term_util.h",
"page_usage_data.cc",
"page_usage_data.h",
"sync/delete_directive_handler.cc",
"sync/delete_directive_handler.h",
"sync/history_delete_directives_model_type_controller.cc",
"sync/history_delete_directives_model_type_controller.h",
+ "sync/history_model_type_controller_helper.cc",
+ "sync/history_model_type_controller_helper.h",
+ "sync/history_sync_bridge.cc",
+ "sync/history_sync_bridge.h",
+ "sync/history_sync_metadata_database.cc",
+ "sync/history_sync_metadata_database.h",
"sync/typed_url_model_type_controller.cc",
"sync/typed_url_model_type_controller.h",
"sync/typed_url_sync_bridge.cc",
@@ -104,6 +114,7 @@ static_library("browser") {
"//components/keyed_service/core",
"//components/prefs",
"//components/query_parser",
+ "//components/search_engines",
"//components/signin/public/identity_manager",
"//components/sync",
"//components/url_formatter",
@@ -182,6 +193,8 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/history/history.50.sql",
"//components/test/data/history/history.52.sql",
"//components/test/data/history/history.53.sql",
+ "//components/test/data/history/history.54.sql",
+ "//components/test/data/history/history.55.sql",
"//components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable",
"//components/test/data/history/thumbnail_wild/Favicons.v2.init.sql",
"//components/test/data/history/thumbnail_wild/Favicons.v3.init.sql",
@@ -210,6 +223,7 @@ source_set("unit_tests") {
"history_service_unittest.cc",
"history_types_unittest.cc",
"sync/delete_directive_handler_unittest.cc",
+ "sync/history_sync_metadata_database_unittest.cc",
"sync/typed_url_sync_bridge_unittest.cc",
"sync/typed_url_sync_metadata_database_unittest.cc",
"top_sites_database_unittest.cc",
@@ -235,6 +249,7 @@ source_set("unit_tests") {
"//components/history/core/common",
"//components/history/core/test",
"//components/prefs:test_support",
+ "//components/search_engines",
"//components/sync:test_support",
"//net:test_support",
"//services/network:test_support",
diff --git a/chromium/components/history/core/browser/OWNERS b/chromium/components/history/core/browser/OWNERS
new file mode 100644
index 00000000000..a3afacdb4d1
--- /dev/null
+++ b/chromium/components/history/core/browser/OWNERS
@@ -0,0 +1,3 @@
+# This is for the common case of adding or renaming files. If you're doing
+# structural changes, use usual OWNERS rules.
+per-file BUILD.gn=*
diff --git a/chromium/components/history/core/browser/browsing_history_driver.h b/chromium/components/history/core/browser/browsing_history_driver.h
index 808d8e49657..9b76cc43a23 100644
--- a/chromium/components/history/core/browser/browsing_history_driver.h
+++ b/chromium/components/history/core/browser/browsing_history_driver.h
@@ -32,27 +32,30 @@ class BrowsingHistoryDriver {
virtual void OnQueryComplete(
const std::vector<BrowsingHistoryService::HistoryEntry>& results,
const BrowsingHistoryService::QueryResultsInfo& query_results_info,
- base::OnceClosure continuation_closure) = 0;
+ base::OnceClosure continuation_closure) {}
// Callback for RemoveVisits().
- virtual void OnRemoveVisitsComplete() = 0;
+ virtual void OnRemoveVisitsComplete() {}
// Callback for RemoveVisits() that fails.
- virtual void OnRemoveVisitsFailed() = 0;
+ virtual void OnRemoveVisitsFailed() {}
// Callback for RemoveVisits() with the list of expire arguments. This gives
// the driver a chance to perform embedder specific removal logic.
virtual void OnRemoveVisits(
const std::vector<ExpireHistoryArgs>& expire_list) = 0;
- // Called when HistoryService or WebHistoryService deletes one or more
- // items.
- virtual void HistoryDeleted() = 0;
+ // Called when `HistoryService` or `WebHistoryService` deletes one or more
+ // items. But notably, this call is only used for deletions initiated by a
+ // DIFFERENT tab. Deletions initiated by the tab that owns this instance are
+ // notified using `OnRemoveVisitsComplete()` or `OnRemoveVisitsFailed()`.
+ // TODO(tommycli): Investigate consolidating the deletion callbacks.
+ virtual void HistoryDeleted() {}
// Whether other forms of browsing history were found on the history
// service.
virtual void HasOtherFormsOfBrowsingHistory(bool has_other_forms,
- bool has_synced_results) = 0;
+ bool has_synced_results) {}
// If history deletions are currently allowed.
virtual bool AllowHistoryDeletions() = 0;
diff --git a/chromium/components/history/core/browser/browsing_history_service.h b/chromium/components/history/core/browser/browsing_history_service.h
index f9b9cf13c3a..7e2b18302b6 100644
--- a/chromium/components/history/core/browser/browsing_history_service.h
+++ b/chromium/components/history/core/browser/browsing_history_service.h
@@ -159,6 +159,9 @@ class BrowsingHistoryService : public HistoryServiceObserver,
base::OnceCallback<void(base::Time)> callback);
// Removes `items` from history.
+ // TODO(tommycli): Update this API to take only URLs and timestamps, because
+ // callers only have that information, and only that information is used by
+ // the actual implementation.
void RemoveVisits(const std::vector<HistoryEntry>& items);
// SyncServiceObserver implementation.
diff --git a/chromium/components/history/core/browser/expire_history_backend_unittest.cc b/chromium/components/history/core/browser/expire_history_backend_unittest.cc
index 7f3b2fdc485..098823cf011 100644
--- a/chromium/components/history/core/browser/expire_history_backend_unittest.cc
+++ b/chromium/components/history/core/browser/expire_history_backend_unittest.cc
@@ -168,9 +168,9 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier {
TopSitesImpl::RegisterPrefs(pref_service_->registry());
expirer_.SetDatabases(main_db_.get(), thumb_db_.get());
- top_sites_ =
- new TopSitesImpl(pref_service_.get(), nullptr, PrepopulatedPageList(),
- base::BindRepeating(MockCanAddURLToHistory));
+ top_sites_ = new TopSitesImpl(pref_service_.get(), nullptr, nullptr,
+ PrepopulatedPageList(),
+ base::BindRepeating(MockCanAddURLToHistory));
WaitTopSitesLoadedObserver wait_top_sites_observer(top_sites_);
top_sites_->Init(path().Append(kTopSitesFilename));
wait_top_sites_observer.Run();
@@ -201,7 +201,6 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier {
const GURL& icon_url) override {}
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) override {}
void NotifyURLsModified(const URLRows& rows,
bool is_from_expiration) override {
diff --git a/chromium/components/history/core/browser/features.cc b/chromium/components/history/core/browser/features.cc
new file mode 100644
index 00000000000..33cf5e59129
--- /dev/null
+++ b/chromium/components/history/core/browser/features.cc
@@ -0,0 +1,39 @@
+// Copyright 2022 The Chromium 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/history/core/browser/features.h"
+
+#include "components/history/core/browser/top_sites_impl.h"
+
+namespace history {
+
+// If enabled, the most repeated queries from the user browsing history are
+// shown in the Most Visited tiles.
+const base::Feature kOrganicRepeatableQueries{
+ "OrganicRepeatableQueries", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// The maximum number of repeatable queries to show in the Most Visited tiles.
+// The default behavior is having no limit, i.e., the number of the tiles.
+const base::FeatureParam<int> kMaxNumRepeatableQueries(
+ &kOrganicRepeatableQueries,
+ "MaxNumRepeatableQueries",
+ kTopSitesNumber);
+
+// Whether the scores for the repeatable queries and the most visited sites
+// should first be scaled to an equivalent range before mixing.
+// The default behavior is to mix the two lists as is.
+const base::FeatureParam<bool> kScaleRepeatableQueriesScores(
+ &kOrganicRepeatableQueries,
+ "ScaleRepeatableQueriesScores",
+ false);
+
+// Whether a repeatable query should precede a most visited site with equal
+// score. The default behavior is for the sites to precede the queries.
+// Used for tie-breaking, especially when kScaleRepeatableQueriesScores is true.
+const base::FeatureParam<bool> kPrivilegeRepeatableQueries(
+ &kOrganicRepeatableQueries,
+ "PrivilegeRepeatableQueries",
+ false);
+
+} // namespace history
diff --git a/chromium/components/history/core/browser/features.h b/chromium/components/history/core/browser/features.h
new file mode 100644
index 00000000000..b33ec9aa915
--- /dev/null
+++ b/chromium/components/history/core/browser/features.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_HISTORY_CORE_BROWSER_FEATURES_H_
+#define COMPONENTS_HISTORY_CORE_BROWSER_FEATURES_H_
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+
+namespace history {
+
+// Organic Repeatable Queries
+extern const base::Feature kOrganicRepeatableQueries;
+extern const base::FeatureParam<int> kMaxNumRepeatableQueries;
+extern const base::FeatureParam<bool> kScaleRepeatableQueriesScores;
+extern const base::FeatureParam<bool> kPrivilegeRepeatableQueries;
+
+} // namespace history
+
+#endif // COMPONENTS_HISTORY_CORE_BROWSER_FEATURES_H_
diff --git a/chromium/components/history/core/browser/history_backend.cc b/chromium/components/history/core/browser/history_backend.cc
index 226a55a9e52..d3337b4437b 100644
--- a/chromium/components/history/core/browser/history_backend.cc
+++ b/chromium/components/history/core/browser/history_backend.cc
@@ -18,6 +18,7 @@
#include "base/callback_helpers.h"
#include "base/compiler_specific.h"
#include "base/containers/flat_set.h"
+#include "base/feature_list.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
@@ -29,6 +30,7 @@
#include "base/observer_list.h"
#include "base/rand_util.h"
#include "base/ranges/ranges.h"
+#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
@@ -49,14 +51,17 @@
#include "components/history/core/browser/in_memory_history_backend.h"
#include "components/history/core/browser/keyword_search_term.h"
#include "components/history/core/browser/page_usage_data.h"
+#include "components/history/core/browser/sync/history_sync_bridge.h"
#include "components/history/core/browser/sync/typed_url_sync_bridge.h"
#include "components/history/core/browser/url_utils.h"
+#include "components/sync/base/features.h"
#include "components/sync/model/client_tag_based_model_type_processor.h"
#include "components/url_formatter/url_formatter.h"
-#include "net/base/escape.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "sql/error_delegate_util.h"
+#include "sql/sqlite_result_code.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
#include "url/url_constants.h"
@@ -197,7 +202,7 @@ std::u16string FormatUrlForRedirectComparison(const GURL& url) {
url_formatter::kFormatUrlOmitHTTP | url_formatter::kFormatUrlOmitHTTPS |
url_formatter::kFormatUrlOmitUsernamePassword |
url_formatter::kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NONE, nullptr, nullptr, nullptr);
+ base::UnescapeRule::NONE, nullptr, nullptr, nullptr);
}
base::Time MidnightNDaysLater(base::Time time, int days) {
@@ -242,19 +247,6 @@ void QueuedHistoryDBTask::DoneRun() {
is_canceled_));
}
-// HistoryBackendHelper --------------------------------------------------------
-
-// Wrapper around base::SupportsUserData with a public destructor.
-class HistoryBackendHelper : public base::SupportsUserData {
- public:
- HistoryBackendHelper();
- ~HistoryBackendHelper() override;
-};
-
-HistoryBackendHelper::HistoryBackendHelper() = default;
-
-HistoryBackendHelper::~HistoryBackendHelper() = default;
-
// HistoryBackend --------------------------------------------------------------
// static
@@ -286,9 +278,6 @@ HistoryBackend::~HistoryBackend() {
DCHECK(scheduled_commit_.IsCancelled()) << "Deleting without cleanup";
queued_history_db_tasks_.clear();
- // Release stashed embedder object before cleaning up the databases.
- supports_user_data_helper_.reset();
-
// Clear the error callback. The error callback that is installed does not
// process an error immediately, rather it uses a PostTask() with `this`. As
// `this` is being deleted, scheduling a PostTask() with `this` would be
@@ -323,21 +312,25 @@ void HistoryBackend::Init(
"that ~TestingProfile() has not been called or that the "
"ScopedTempDirectory used outlives this task.";
- // HistoryBackend is created on the UI thread by HistoryService, then the
- // HistoryBackend::Init() method is called on the DB thread. Create the
- // base::SupportsUserData on the DB thread since it is not thread-safe.
- supports_user_data_helper_ = std::make_unique<HistoryBackendHelper>();
-
if (!force_fail)
InitImpl(history_database_params);
delegate_->DBLoaded();
typed_url_sync_bridge_ = std::make_unique<TypedURLSyncBridge>(
- this, db_.get(),
+ this, db_ ? db_->GetTypedURLMetadataDB() : nullptr,
std::make_unique<ClientTagBasedModelTypeProcessor>(
syncer::TYPED_URLS, /*dump_stack=*/base::RepeatingClosure()));
typed_url_sync_bridge_->Init();
+ if (base::FeatureList::IsEnabled(syncer::kSyncEnableHistoryDataType)) {
+ // TODO(crbug.com/1318028): Plumb in syncer::ReportUnrecoverableError as the
+ // dump_stack callback.
+ history_sync_bridge_ = std::make_unique<HistorySyncBridge>(
+ this, db_ ? db_->GetHistoryMetadataDB() : nullptr,
+ std::make_unique<ClientTagBasedModelTypeProcessor>(
+ syncer::HISTORY, /*dump_stack=*/base::RepeatingClosure()));
+ }
+
memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
FROM_HERE, base::BindRepeating(&HistoryBackend::OnMemoryPressure,
base::Unretained(this)));
@@ -593,6 +586,29 @@ void HistoryBackend::AddSearchMetadataForVisit(
}
}
+void HistoryBackend::AddPageMetadataForVisit(
+ VisitID visit_id,
+ const std::string& alternative_title) {
+ TRACE_EVENT0("browser", "HistoryBackend::AddPageMetadataForVisit");
+
+ if (!db_)
+ return;
+ // Only add to the annotations table if the visit_id exists in the visits
+ // table.
+ VisitRow visit_row;
+ if (db_->GetRowForVisit(visit_id, &visit_row)) {
+ VisitContentAnnotations annotations;
+ if (db_->GetContentAnnotationsForVisit(visit_id, &annotations)) {
+ annotations.alternative_title = alternative_title;
+ db_->UpdateContentAnnotationsForVisit(visit_id, annotations);
+ } else {
+ annotations.alternative_title = alternative_title;
+ db_->AddContentAnnotationsForVisit(visit_id, annotations);
+ }
+ ScheduleCommit();
+ }
+}
+
void HistoryBackend::UpdateVisitDuration(VisitID visit_id, const Time end_ts) {
if (!db_)
return;
@@ -657,12 +673,11 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
if (!db_)
return;
- // Will be filled with the URL ID and the visit ID of the last addition.
- std::pair<URLID, VisitID> last_ids(
- 0, tracker_.GetLastVisit(request.context_id, request.nav_entry_id,
- request.referrer));
+ // Will be filled with the visit ID of the last addition.
+ VisitID last_visit_id = tracker_.GetLastVisit(
+ request.context_id, request.nav_entry_id, request.referrer);
- VisitID from_visit_id = last_ids.second;
+ const VisitID from_visit_id = last_visit_id;
// If a redirect chain is given, we expect the last item in that chain to be
// the final URL.
@@ -674,13 +689,13 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
first_recorded_time_ = request.time;
ui::PageTransition request_transition = request.transition;
- bool is_keyword_generated = ui::PageTransitionCoreTypeIs(
+ const bool is_keyword_generated = ui::PageTransitionCoreTypeIs(
request_transition, ui::PAGE_TRANSITION_KEYWORD_GENERATED);
// If the user is navigating to a not-previously-typed intranet hostname,
// change the transition to TYPED so that the omnibox will learn that this is
// a known host.
- bool has_redirects = request.redirects.size() > 1;
+ const bool has_redirects = request.redirects.size() > 1;
if (ui::PageTransitionIsMainFrame(request_transition) &&
!ui::PageTransitionCoreTypeIs(request_transition,
ui::PAGE_TRANSITION_TYPED) &&
@@ -709,20 +724,22 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
ui::PAGE_TRANSITION_CHAIN_END);
// No redirect case (one element means just the page itself).
- last_ids = AddPageVisit(request.url, request.time, last_ids.second, t,
- request.hidden, request.visit_source,
- IsTypedIncrement(t), opener_visit, request.title);
+ last_visit_id =
+ AddPageVisit(request.url, request.time, last_visit_id, t,
+ request.hidden, request.visit_source, IsTypedIncrement(t),
+ opener_visit, request.title)
+ .second;
// Update the segment for this visit. KEYWORD_GENERATED visits should not
// result in changing most visited, so we don't update segments (most
// visited db).
if (!is_keyword_generated && request.consider_for_ntp_most_visited) {
- UpdateSegments(request.url, from_visit_id, last_ids.second, t,
+ UpdateSegments(request.url, from_visit_id, last_visit_id, t,
request.time);
-
- // Update the referrer's duration.
- UpdateVisitDuration(from_visit_id, request.time);
}
+
+ // Update the referrer's duration.
+ UpdateVisitDuration(from_visit_id, request.time);
} else {
// Redirect case. Add the redirect chain.
@@ -770,7 +787,7 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
// time we won't have changed anything.
VisitRow visit_row;
if (request.did_replace_entry) {
- if (db_->GetRowForVisit(last_ids.second, &visit_row) &&
+ if (db_->GetRowForVisit(last_visit_id, &visit_row) &&
visit_row.transition & ui::PAGE_TRANSITION_CHAIN_END) {
visit_row.transition = ui::PageTransitionFromInt(
visit_row.transition & ~ui::PAGE_TRANSITION_CHAIN_END);
@@ -807,10 +824,16 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
for (size_t redirect_index = 0; redirect_index < redirects.size();
redirect_index++) {
+ constexpr int kRedirectQualifiers = ui::PAGE_TRANSITION_CHAIN_START |
+ ui::PAGE_TRANSITION_CHAIN_END |
+ ui::PAGE_TRANSITION_IS_REDIRECT_MASK;
+ // Remove any redirect-related qualifiers that `request_transition` may
+ // have (there usually shouldn't be any, except for CLIENT_REDIRECT which
+ // was already handled above), and replace them with the `redirect_info`.
ui::PageTransition t = ui::PageTransitionFromInt(
- ui::PageTransitionStripQualifier(request_transition) | redirect_info);
+ (request_transition & ~kRedirectQualifiers) | redirect_info);
- // If this is the last transition, add a CHAIN_END marker
+ // If this is the last transition, add a CHAIN_END marker.
if (redirect_index == (redirects.size() - 1)) {
t = ui::PageTransitionFromInt(t | ui::PAGE_TRANSITION_CHAIN_END);
}
@@ -827,18 +850,20 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
// them anyway, and if we ever decide to, we can reconstruct their order
// from the redirect chain. Only place the opener on the initial visit in
// the chain.
- last_ids = AddPageVisit(
- redirects[redirect_index], request.time, last_ids.second, t,
- request.hidden, request.visit_source, should_increment_typed_count,
- redirect_index == 0 ? opener_visit : 0, request.title);
+ last_visit_id =
+ AddPageVisit(redirects[redirect_index], request.time, last_visit_id,
+ t, request.hidden, request.visit_source,
+ should_increment_typed_count,
+ redirect_index == 0 ? opener_visit : 0, request.title)
+ .second;
if (t & ui::PAGE_TRANSITION_CHAIN_START) {
if (request.consider_for_ntp_most_visited) {
UpdateSegments(redirects[redirect_index], from_visit_id,
- last_ids.second, t, request.time);
+ last_visit_id, t, request.time);
}
- // Update the visit_details for this visit.
+ // Update the referrer's duration.
UpdateVisitDuration(from_visit_id, request.time);
}
@@ -860,6 +885,10 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
// views can keep in sync.
// Add the last visit to the tracker so we can get outgoing transitions.
+ // Keyword-generated visits are artificially generated. They duplicate the
+ // real navigation, and are added to ensure autocompletion in the omnibox
+ // works. As they are artificial they shouldn't be tracked for referral
+ // chains.
// TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe
// navigation anyway, so last_visit_id is always zero for them. But adding
// them here confuses main frame history, so we skip them for now.
@@ -869,7 +898,7 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
ui::PAGE_TRANSITION_MANUAL_SUBFRAME) &&
!is_keyword_generated) {
tracker_.AddVisit(request.context_id, request.nav_entry_id, request.url,
- last_ids.second);
+ last_visit_id);
}
ScheduleCommit();
@@ -1065,11 +1094,7 @@ std::pair<URLID, VisitID> HistoryBackend::AddPageVisit(
// Broadcast a notification of the visit.
if (visit_id) {
- RedirectList redirects;
- // TODO(meelapshah) Disabled due to potential PageCycler regression.
- // Re-enable this.
- // QueryRedirectsTo(url, &redirects);
- NotifyURLVisited(transition, url_info, redirects, time);
+ NotifyURLVisited(transition, url_info, time);
} else {
DVLOG(0) << "Failed to build visit insert statement: "
<< "url_id = " << url_id;
@@ -1141,7 +1166,7 @@ void HistoryBackend::SetTypedURLSyncBridgeForTest(
typed_url_sync_bridge_ = std::move(bridge);
}
-bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) {
+bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) const {
return time < expirer_.GetCurrentExpirationTime();
}
@@ -1342,6 +1367,12 @@ HistoryBackend::GetTypedURLSyncControllerDelegate() {
return typed_url_sync_bridge_->change_processor()->GetControllerDelegate();
}
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+HistoryBackend::GetHistorySyncControllerDelegate() {
+ DCHECK(history_sync_bridge_);
+ return history_sync_bridge_->change_processor()->GetControllerDelegate();
+}
+
// Statistics ------------------------------------------------------------------
HistoryCountResult HistoryBackend::GetHistoryCount(const Time& begin_time,
@@ -1610,6 +1641,12 @@ std::vector<AnnotatedVisit> HistoryBackend::ToAnnotatedVisits(
return ToAnnotatedVisits(visit_rows);
}
+base::Time HistoryBackend::FindMostRecentClusteredTime() {
+ // TODO(manukh): Implement. Since we don't have persisted clustered visits
+ // yet, there are no visits to take the timestamp of.
+ return base::Time::Now() - base::Days(kExpireDaysThreshold);
+}
+
void HistoryBackend::ReplaceClusters(
const std::vector<int64_t>& ids_to_delete,
const std::vector<Cluster>& clusters_to_add) {
@@ -1911,8 +1948,7 @@ VisibleVisitCountToHostResult HistoryBackend::GetVisibleVisitCountToHost(
return result;
}
-MostVisitedURLList HistoryBackend::QueryMostVisitedURLs(int result_count,
- int days_back) {
+MostVisitedURLList HistoryBackend::QueryMostVisitedURLs(int result_count) {
if (!db_)
return {};
@@ -1923,12 +1959,13 @@ MostVisitedURLList HistoryBackend::QueryMostVisitedURLs(int result_count,
? base::BindRepeating(&HistoryBackendClient::IsWebSafe,
base::Unretained(backend_client_.get()))
: base::NullCallback();
- std::vector<std::unique_ptr<PageUsageData>> data = db_->QuerySegmentUsage(
- base::Time::Now() - base::Days(days_back), result_count, url_filter);
+ std::vector<std::unique_ptr<PageUsageData>> data =
+ db_->QuerySegmentUsage(result_count, url_filter);
MostVisitedURLList result;
for (const std::unique_ptr<PageUsageData>& current_data : data)
- result.emplace_back(current_data->GetURL(), current_data->GetTitle());
+ result.emplace_back(current_data->GetURL(), current_data->GetTitle(),
+ current_data->GetScore());
UMA_HISTOGRAM_TIMES("History.QueryMostVisitedURLsTime",
base::TimeTicks::Now() - begin_time);
@@ -2332,6 +2369,9 @@ void HistoryBackend::ProcessDBTaskImpl() {
////////////////////////////////////////////////////////////////////////////////
void HistoryBackend::DeleteURLs(const std::vector<GURL>& urls) {
+ if (!db_)
+ return;
+
TRACE_EVENT0("browser", "HistoryBackend::DeleteURLs");
expirer_.DeleteURLs(urls, base::Time::Max());
@@ -2343,6 +2383,9 @@ void HistoryBackend::DeleteURLs(const std::vector<GURL>& urls) {
}
void HistoryBackend::DeleteURL(const GURL& url) {
+ if (!db_)
+ return;
+
TRACE_EVENT0("browser", "HistoryBackend::DeleteURL");
expirer_.DeleteURL(url, base::Time::Max());
@@ -2355,6 +2398,9 @@ void HistoryBackend::DeleteURL(const GURL& url) {
void HistoryBackend::DeleteURLsUntil(
const std::vector<std::pair<GURL, base::Time>>& urls_and_timestamps) {
+ if (!db_)
+ return;
+
TRACE_EVENT0("browser", "HistoryBackend::DeleteURLsUntil");
for (const auto& pair : urls_and_timestamps) {
@@ -2488,6 +2534,7 @@ void HistoryBackend::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
void HistoryBackend::DatabaseErrorCallback(int error, sql::Statement* stmt) {
if (!scheduled_kill_db_ && sql::IsErrorCatastrophic(error)) {
+ sql::UmaHistogramSqliteResult("History.DatabaseSqliteError", error);
scheduled_kill_db_ = true;
db_diagnostics_ = db_->GetDiagnosticInfo(error, stmt);
@@ -2509,10 +2556,12 @@ void HistoryBackend::KillHistoryDatabase() {
if (!db_)
return;
- // Notify SyncBridge about storage error. It will report failure to sync
- // engine and stop accepting remote updates.
+ // Notify the sync bridges about storage error. They'll report failures to the
+ // sync engine and stop accepting remote updates.
if (typed_url_sync_bridge_)
typed_url_sync_bridge_->OnDatabaseError();
+ if (history_sync_bridge_)
+ history_sync_bridge_->OnDatabaseError();
// Rollback transaction because Raze() cannot be called from within a
// transaction.
@@ -2520,9 +2569,6 @@ void HistoryBackend::KillHistoryDatabase() {
bool success = db_->Raze();
UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success);
- // Release stashed embedder object before cleaning up the databases.
- supports_user_data_helper_.reset();
-
// The expirer keeps tabs on the active databases. Tell it about the
// databases which will be closed.
expirer_.SetDatabases(nullptr, nullptr);
@@ -2532,19 +2578,6 @@ void HistoryBackend::KillHistoryDatabase() {
CloseAllDatabases();
}
-base::SupportsUserData::Data* HistoryBackend::GetUserData(
- const void* key) const {
- DCHECK(supports_user_data_helper_);
- return supports_user_data_helper_->GetUserData(key);
-}
-
-void HistoryBackend::SetUserData(
- const void* key,
- std::unique_ptr<base::SupportsUserData::Data> data) {
- DCHECK(supports_user_data_helper_);
- supports_user_data_helper_->SetUserData(key, std::move(data));
-}
-
void HistoryBackend::ProcessDBTask(
std::unique_ptr<HistoryDBTask> task,
scoped_refptr<base::SingleThreadTaskRunner> origin_loop,
@@ -2564,12 +2597,11 @@ void HistoryBackend::NotifyFaviconsChanged(const std::set<GURL>& page_urls,
void HistoryBackend::NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) {
for (HistoryBackendObserver& observer : observers_)
- observer.OnURLVisited(this, transition, row, redirects, visit_time);
+ observer.OnURLVisited(this, transition, row, visit_time);
- delegate_->NotifyURLVisited(transition, row, redirects, visit_time);
+ delegate_->NotifyURLVisited(transition, row, visit_time);
}
void HistoryBackend::NotifyURLsModified(const URLRows& changed_urls,
diff --git a/chromium/components/history/core/browser/history_backend.h b/chromium/components/history/core/browser/history_backend.h
index d7267520f16..fe2ce69bb84 100644
--- a/chromium/components/history/core/browser/history_backend.h
+++ b/chromium/components/history/core/browser/history_backend.h
@@ -65,8 +65,8 @@ class HistoryBackendTest;
class HistoryDatabase;
struct HistoryDatabaseParams;
class HistoryDBTask;
+class HistorySyncBridge;
class InMemoryHistoryBackend;
-class HistoryBackendHelper;
class TypedURLSyncBridge;
class URLDatabase;
@@ -154,7 +154,6 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
// be forwarded to the HistoryServiceObservers in the correct thread.
virtual void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) = 0;
// Notify HistoryService that some URLs have been modified. The event will
@@ -247,7 +246,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
// `request.time` must be unique with high probability.
void AddPage(const HistoryAddPageArgs& request);
- virtual void SetPageTitle(const GURL& url, const std::u16string& title);
+ void SetPageTitle(const GURL& url, const std::u16string& title);
void AddPageNoVisitForBookmark(const GURL& url, const std::u16string& title);
void UpdateWithPageEndTime(ContextID context_id,
int nav_entry_id,
@@ -265,6 +264,8 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
void AddSearchMetadataForVisit(VisitID visit_id,
const GURL& search_normalized_url,
const std::u16string& search_terms);
+ void AddPageMetadataForVisit(VisitID visit_id,
+ const std::string& alternative_title);
// Querying ------------------------------------------------------------------
@@ -293,9 +294,8 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
VisibleVisitCountToHostResult GetVisibleVisitCountToHost(const GURL& url);
// Request the `result_count` most visited URLs and the chain of
- // redirects leading to each of these URLs. `days_back` is the
- // number of days of history to use. Used by TopSites.
- MostVisitedURLList QueryMostVisitedURLs(int result_count, int days_back);
+ // redirects leading to each of these URLs. Used by TopSites.
+ MostVisitedURLList QueryMostVisitedURLs(int result_count);
// Statistics ----------------------------------------------------------------
@@ -468,6 +468,12 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
std::vector<AnnotatedVisit> ToAnnotatedVisits(
const std::vector<VisitID>& visit_ids);
+ // Returns the time of the most recent clustered visits; i.e. the boundary
+ // until which visits have been clustered. It's possible for there to be
+ // unclustered visits older than this boundary, since synced visits older than
+ // this boundary won't be clustered nor cause re-clustering.
+ base::Time FindMostRecentClusteredTime();
+
void ReplaceClusters(const std::vector<int64_t>& ids_to_delete,
const std::vector<Cluster>& clusters_to_add);
@@ -498,26 +504,24 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
scoped_refptr<base::SingleThreadTaskRunner> origin_loop,
const base::CancelableTaskTracker::IsCanceledCallback& is_canceled);
- virtual bool GetAllTypedURLs(URLRows* urls);
+ bool GetAllTypedURLs(URLRows* urls);
- virtual bool GetVisitsForURL(URLID id, VisitVector* visits);
+ bool GetVisitsForURL(URLID id, VisitVector* visits);
// Fetches up to `max_visits` most recent visits for the passed URL.
- virtual bool GetMostRecentVisitsForURL(URLID id,
- int max_visits,
- VisitVector* visits);
+ bool GetMostRecentVisitsForURL(URLID id, int max_visits, VisitVector* visits);
// For each element in `urls`, updates the pre-existing URLRow in the database
// with the same ID; or ignores the element if no such row exists. Returns the
// number of records successfully updated.
- virtual size_t UpdateURLs(const URLRows& urls);
+ size_t UpdateURLs(const URLRows& urls);
// While adding visits in batch, the source needs to be provided.
- virtual bool AddVisits(const GURL& url,
- const std::vector<VisitInfo>& visits,
- VisitSource visit_source);
+ bool AddVisits(const GURL& url,
+ const std::vector<VisitInfo>& visits,
+ VisitSource visit_source);
- virtual bool RemoveVisits(const VisitVector& visits);
+ bool RemoveVisits(const VisitVector& visits);
// Returns the `VisitSource` associated with each one of the passed visits.
// If there is no entry in the map for a given visit, that means the visit
@@ -527,7 +531,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
// Like `GetVisitsSource`, but for a single visit.
bool GetVisitSource(const VisitID visit_id, VisitSource* source);
- virtual bool GetURL(const GURL& url, URLRow* url_row);
+ bool GetURL(const GURL& url, URLRow* url_row);
bool GetURLByID(URLID url_id, URLRow* url_row);
@@ -536,6 +540,11 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
base::WeakPtr<syncer::ModelTypeControllerDelegate>
GetTypedURLSyncControllerDelegate();
+ // Returns the sync controller delegate for syncing history. The returned
+ // delegate is owned by `this` object.
+ base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ GetHistorySyncControllerDelegate();
+
// Deleting ------------------------------------------------------------------
void DeleteURLs(const std::vector<GURL>& urls);
@@ -589,14 +598,6 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
// managed by HistoryBackend when the history database cannot be accessed.
void KillHistoryDatabase();
- // SupportsUserData ----------------------------------------------------------
-
- // The user data allows the clients to associate data with this object.
- // Multiple user data values can be stored under different keys.
- base::SupportsUserData::Data* GetUserData(const void* key) const;
- void SetUserData(const void* key,
- std::unique_ptr<base::SupportsUserData::Data> data);
-
// Testing -------------------------------------------------------------------
// Sets the task to run and the message loop to run it on when this object
@@ -622,7 +623,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
// Returns true if the passed visit time is already expired (used by the sync
// code to avoid syncing visits that would immediately be expired).
- virtual bool IsExpiredVisitTime(const base::Time& time);
+ virtual bool IsExpiredVisitTime(const base::Time& time) const;
base::Time GetFirstRecordedTimeForTest() { return first_recorded_time_; }
@@ -766,7 +767,6 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
const GURL& icon_url) override;
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) override;
void NotifyURLsModified(const URLRows& changed_urls,
bool is_from_expiration) override;
@@ -861,11 +861,6 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
scoped_refptr<base::SequencedTaskRunner> task_runner_;
- // Used to allow embedder code to stash random data by key. Those object will
- // be deleted before closing the databases (hence the member variable instead
- // of inheritance from base::SupportsUserData).
- std::unique_ptr<HistoryBackendHelper> supports_user_data_helper_;
-
// Listens for the system being under memory pressure.
std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
@@ -877,9 +872,14 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
base::ObserverList<HistoryBackendObserver>::Unchecked observers_;
// Used to manage syncing of the typed urls datatype. It will be null before
- // HistoryBackend::Init is called. Defined after observers_ because
+ // HistoryBackend::Init() is called. Defined after `observers_` because
// it unregisters itself as observer during destruction.
std::unique_ptr<TypedURLSyncBridge> typed_url_sync_bridge_;
+
+ // Used to manage syncing of the history datatype. It will be null before
+ // HistoryBackend::Init() is called. Defined after `observers_` because
+ // it unregisters itself as observer during destruction.
+ std::unique_ptr<HistorySyncBridge> history_sync_bridge_;
};
} // namespace history
diff --git a/chromium/components/history/core/browser/history_backend_db_unittest.cc b/chromium/components/history/core/browser/history_backend_db_unittest.cc
index ca8e13e1f51..478d2789632 100644
--- a/chromium/components/history/core/browser/history_backend_db_unittest.cc
+++ b/chromium/components/history/core/browser/history_backend_db_unittest.cc
@@ -1683,7 +1683,7 @@ TEST_F(HistoryBackendDBTest, MigratePresentations) {
CreateBackendAndDatabase();
std::vector<std::unique_ptr<PageUsageData>> results =
- db_->QuerySegmentUsage(segment_time, 10, base::NullCallback());
+ db_->QuerySegmentUsage(/*max_result_count=*/10, base::NullCallback());
ASSERT_EQ(1u, results.size());
EXPECT_EQ(url, results[0]->GetURL());
EXPECT_EQ(segment_id, results[0]->GetID());
@@ -1830,7 +1830,7 @@ TEST_F(HistoryBackendDBTest, MigrateVisitSegmentNames) {
CreateBackendAndDatabase();
std::vector<std::unique_ptr<PageUsageData>> results = db_->QuerySegmentUsage(
- segment_time, /*max_result_count=*/10, base::NullCallback());
+ /*max_result_count=*/10, base::NullCallback());
ASSERT_EQ(1u, results.size());
EXPECT_THAT(results[0]->GetURL(), testing::AnyOf(url1, url2));
EXPECT_THAT(results[0]->GetTitle(), testing::AnyOf(title1, title2));
@@ -2589,6 +2589,85 @@ TEST_F(HistoryBackendDBTest,
EXPECT_TRUE(visit_content_annotations.search_terms.empty());
}
}
+TEST_F(HistoryBackendDBTest, MigrateContentAnnotationsAddPageMetadataColumns) {
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(53));
+
+ const VisitID visit_id1 = 1;
+
+ // Open the db for manual manipulation.
+ sql::Database db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+
+ const char kInsertContentAnnotationsStatement[] =
+ "INSERT INTO content_annotations "
+ "(visit_id, floc_protected_score, categories, page_topics_model_version, "
+ "annotation_flags, entities, related_searches, search_normalized_url, "
+ "search_terms) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+ // Add an entry to "content_annotations" table.
+ {
+ sql::Statement s(db.GetUniqueStatement(kInsertContentAnnotationsStatement));
+ s.BindInt64(0, visit_id1);
+ s.BindDouble(1, -1);
+ s.BindString(2, "");
+ s.BindInt64(3, -1);
+ s.BindInt64(4, 0);
+ s.BindString(5, "");
+ s.BindString(6, "");
+ s.BindString(7, "");
+ s.BindString(8, "");
+ ASSERT_TRUE(s.Run());
+ }
+
+ // Re-open the db, triggering migration.
+ CreateBackendAndDatabase();
+
+ // The version should have been updated.
+ ASSERT_GE(HistoryDatabase::GetCurrentVersion(), 54);
+
+ // After the migration, the page metadata should be empty.
+ {
+ VisitContentAnnotations visit_content_annotations;
+ db_->GetContentAnnotationsForVisit(visit_id1, &visit_content_annotations);
+ EXPECT_TRUE(visit_content_annotations.alternative_title.empty());
+ }
+}
+
+TEST_F(HistoryBackendDBTest,
+ MigrateVisitsAutoincrementIdAndAddOriginatorColumns) {
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(54));
+
+ constexpr VisitID visit_id1 = 1;
+
+ // Open the db for manual manipulation.
+ sql::Database db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+
+ const char kInsertVisitStatement[] =
+ "INSERT INTO visits "
+ "(id, url, visit_time) VALUES (?, ?, ?)";
+
+ // Add a row to `visits` table.
+ {
+ sql::Statement s(db.GetUniqueStatement(kInsertVisitStatement));
+ s.BindInt64(0, 1);
+ s.BindInt64(1, 1);
+ s.BindTime(2, base::Time::Now());
+ ASSERT_TRUE(s.Run());
+ }
+
+ // Re-open the db, triggering migration.
+ CreateBackendAndDatabase();
+
+ // After the migration, the originator columns should return default values.
+ {
+ VisitRow visit;
+ db_->GetRowForVisit(visit_id1, &visit);
+ EXPECT_EQ(visit.originator_cache_guid, "");
+ EXPECT_EQ(visit.originator_visit_id, 0);
+ }
+}
bool FilterURL(const GURL& url) {
return url.SchemeIsHTTPOrHTTPS();
@@ -2620,15 +2699,15 @@ TEST_F(HistoryBackendDBTest, QuerySegmentUsage) {
// Without a filter, the "file://" URL should win.
std::vector<std::unique_ptr<PageUsageData>> results =
- db_->QuerySegmentUsage(time, 1, base::NullCallback());
+ db_->QuerySegmentUsage(/*max_result_count=*/1, base::NullCallback());
ASSERT_EQ(1u, results.size());
EXPECT_EQ(url1, results[0]->GetURL());
EXPECT_EQ(segment_id1, results[0]->GetID());
// With the filter, the "file://" URL should be filtered out, so the "http://"
// URL should win instead.
- std::vector<std::unique_ptr<PageUsageData>> results2 =
- db_->QuerySegmentUsage(time, 1, base::BindRepeating(&FilterURL));
+ std::vector<std::unique_ptr<PageUsageData>> results2 = db_->QuerySegmentUsage(
+ /*max_result_count=*/1, base::BindRepeating(&FilterURL));
ASSERT_EQ(1u, results2.size());
EXPECT_EQ(url2, results2[0]->GetURL());
EXPECT_EQ(segment_id2, results2[0]->GetID());
diff --git a/chromium/components/history/core/browser/history_backend_notifier.h b/chromium/components/history/core/browser/history_backend_notifier.h
index df67e0295d5..93d15c27952 100644
--- a/chromium/components/history/core/browser/history_backend_notifier.h
+++ b/chromium/components/history/core/browser/history_backend_notifier.h
@@ -30,11 +30,9 @@ class HistoryBackendNotifier {
virtual void NotifyFaviconsChanged(const std::set<GURL>& page_urls,
const GURL& icon_url) = 0;
- // Sends notification that `transition` to `row` occurred at `visit_time`
- // following `redirects` (empty if there is no redirects).
+ // Sends notification that `transition` to `row` occurred at `visit_time`.
virtual void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) = 0;
// Sends notification that `changed_urls` have been changed or added.
diff --git a/chromium/components/history/core/browser/history_backend_observer.h b/chromium/components/history/core/browser/history_backend_observer.h
index 25595043c01..c02cf30b0d1 100644
--- a/chromium/components/history/core/browser/history_backend_observer.h
+++ b/chromium/components/history/core/browser/history_backend_observer.h
@@ -23,14 +23,10 @@ class HistoryBackendObserver {
// Called when user visits an URL.
//
// The `row` ID will be set to the value that is currently in effect in the
- // main history database. `redirects` is the list of redirects leading up to
- // the URL. If we have a redirect chain A -> B -> C and user is visiting C,
- // then `redirects[0]=B` and `redirects[1]=A`. If there are no redirects,
- // `redirects` is an empty vector.
+ // main history database.
virtual void OnURLVisited(HistoryBackend* history_backend,
ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) = 0;
// Called when a URL has been added or modified.
diff --git a/chromium/components/history/core/browser/history_backend_unittest.cc b/chromium/components/history/core/browser/history_backend_unittest.cc
index d9fc37824fc..4ed533536d7 100644
--- a/chromium/components/history/core/browser/history_backend_unittest.cc
+++ b/chromium/components/history/core/browser/history_backend_unittest.cc
@@ -105,10 +105,8 @@ void SimulateNotificationURLVisited(HistoryServiceObserver* observer,
rows.push_back(*row3);
base::Time visit_time;
- RedirectList redirects;
for (const URLRow& row : rows) {
- observer->OnURLVisited(nullptr, ui::PAGE_TRANSITION_LINK, row, redirects,
- visit_time);
+ observer->OnURLVisited(nullptr, ui::PAGE_TRANSITION_LINK, row, visit_time);
}
}
@@ -148,7 +146,6 @@ class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
const GURL& icon_url) override;
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) override;
void NotifyURLsModified(const URLRows& changed_urls) override;
void NotifyURLsDeleted(DeletionInfo deletion_info) override;
@@ -253,10 +250,9 @@ class HistoryBackendTestBase : public testing::Test {
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) {
// Send the notifications directly to the in-memory database.
- mem_backend_->OnURLVisited(nullptr, transition, row, redirects, visit_time);
+ mem_backend_->OnURLVisited(nullptr, transition, row, visit_time);
url_visited_notifications_.push_back(std::make_pair(transition, row));
}
@@ -347,9 +343,8 @@ void HistoryBackendTestDelegate::NotifyFaviconsChanged(
void HistoryBackendTestDelegate::NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) {
- test_->NotifyURLVisited(transition, row, redirects, visit_time);
+ test_->NotifyURLVisited(transition, row, visit_time);
}
void HistoryBackendTestDelegate::NotifyURLsModified(
@@ -613,7 +608,7 @@ class InMemoryHistoryBackendTest : public HistoryBackendTestBase {
size_t GetNumberOfMatchingSearchTerms(const int keyword_id,
const std::u16string& prefix) {
- std::vector<KeywordSearchTermVisit> matching_terms;
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>> matching_terms;
mem_backend_->db()->GetMostRecentKeywordSearchTerms(
keyword_id, prefix, 1, &matching_terms);
return matching_terms.size();
@@ -2030,6 +2025,66 @@ TEST_F(HistoryBackendTest, AddSearchMetadata) {
visit_id, &got_content_annotations));
}
+TEST_F(HistoryBackendTest, AddPageMetadata) {
+ ASSERT_TRUE(backend_.get());
+
+ GURL url("http://pagewithvisit.com");
+ ContextID context_id = reinterpret_cast<ContextID>(1);
+ int nav_entry_id = 1;
+
+ HistoryAddPageArgs request(url, base::Time::Now(), context_id, nav_entry_id,
+ GURL(), RedirectList(), ui::PAGE_TRANSITION_TYPED,
+ false, SOURCE_BROWSED, false, true, false);
+ backend_->AddPage(request);
+
+ VisitVector visits;
+ URLRow row;
+ URLID id = backend_->db()->GetRowForURL(url, &row);
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
+ ASSERT_EQ(1U, visits.size());
+ VisitID visit_id = visits[0].visit_id;
+
+ backend_->AddPageMetadataForVisit(visit_id, "alternative title");
+
+ VisitContentAnnotations got_content_annotations;
+ ASSERT_TRUE(backend_->db()->GetContentAnnotationsForVisit(
+ visit_id, &got_content_annotations));
+
+ EXPECT_EQ(VisitContentAnnotationFlag::kNone,
+ got_content_annotations.annotation_flags);
+ EXPECT_EQ(-1.0f, got_content_annotations.model_annotations.visibility_score);
+ ASSERT_TRUE(got_content_annotations.model_annotations.categories.empty());
+ EXPECT_EQ(
+ -1, got_content_annotations.model_annotations.page_topics_model_version);
+ ASSERT_TRUE(got_content_annotations.model_annotations.entities.empty());
+ ASSERT_TRUE(got_content_annotations.related_searches.empty());
+ ASSERT_TRUE(got_content_annotations.search_normalized_url.is_empty());
+ ASSERT_TRUE(got_content_annotations.search_terms.empty());
+ EXPECT_EQ(got_content_annotations.alternative_title, "alternative title");
+
+ QueryOptions options;
+ options.duplicate_policy = QueryOptions::KEEP_ALL_DUPLICATES;
+ QueryResults results = backend_->QueryHistory(/*text_query=*/{}, options);
+
+ ASSERT_EQ(results.size(), 1u);
+ EXPECT_EQ(VisitContentAnnotationFlag::kNone,
+ results[0].content_annotations().annotation_flags);
+ EXPECT_EQ(VisitContentAnnotationFlag::kNone,
+ got_content_annotations.annotation_flags);
+ EXPECT_EQ(-1.0f, got_content_annotations.model_annotations.visibility_score);
+ ASSERT_TRUE(got_content_annotations.model_annotations.categories.empty());
+ EXPECT_EQ(
+ -1, got_content_annotations.model_annotations.page_topics_model_version);
+ ASSERT_TRUE(got_content_annotations.model_annotations.entities.empty());
+ ASSERT_TRUE(got_content_annotations.related_searches.empty());
+ EXPECT_EQ(got_content_annotations.alternative_title, "alternative title");
+
+ // Now, delete the URL. Content Annotations should be deleted.
+ backend_->DeleteURL(url);
+ ASSERT_FALSE(backend_->db()->GetContentAnnotationsForVisit(
+ visit_id, &got_content_annotations));
+}
+
TEST_F(HistoryBackendTest, MixedContentAnnotationsRequestTypes) {
ASSERT_TRUE(backend_.get());
@@ -2808,6 +2863,51 @@ TEST_F(HistoryBackendTest, UpdateVisitDuration) {
ASSERT_TRUE(backend_->RemoveVisits(visits1));
}
+TEST_F(HistoryBackendTest, UpdateVisitDurationForReferrer) {
+ const ContextID context_id = reinterpret_cast<ContextID>(0x1);
+ base::Time start_ts = base::Time::Now() - base::Days(1);
+ base::Time end_ts = start_ts + base::Seconds(2);
+
+ // Add two visits, the first referring to the second. Adding the second visit
+ // should populate the visit_duration for the first one.
+
+ GURL referrer_url("https://referrer.url");
+ GURL second_url("https://other.url");
+
+ HistoryAddPageArgs referrer_args(referrer_url, start_ts, context_id,
+ /*nav_entry_id=*/0, GURL(), RedirectList(),
+ ui::PAGE_TRANSITION_TYPED, false,
+ SOURCE_BROWSED,
+ /*did_replace_entry=*/false,
+ /*consider_for_ntp_most_visited=*/false,
+ /*floc_allowed=*/false);
+ backend_->AddPage(referrer_args);
+
+ // So far, the visit duration should be empty.
+ URLRow row;
+ URLID referrer_url_id = backend_->db()->GetRowForURL(referrer_url, &row);
+ VisitVector visits;
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(referrer_url_id, &visits));
+ ASSERT_EQ(1U, visits.size());
+ ASSERT_EQ(0, visits[0].visit_duration.ToInternalValue());
+
+ HistoryAddPageArgs second_args(second_url, end_ts, context_id,
+ /*nav_entry_id=*/0, referrer_url,
+ RedirectList(), ui::PAGE_TRANSITION_TYPED,
+ false, SOURCE_BROWSED,
+ /*did_replace_entry=*/false,
+ /*consider_for_ntp_most_visited=*/false,
+ /*floc_allowed=*/false);
+ backend_->AddPage(second_args);
+
+ // Adding the second visit should have populated the visit duration for the
+ // first one.
+ ASSERT_TRUE(backend_->db()->GetVisitsForURL(referrer_url_id, &visits));
+ base::TimeDelta expected_duration = end_ts - start_ts;
+ EXPECT_EQ(expected_duration.ToInternalValue(),
+ visits[0].visit_duration.ToInternalValue());
+}
+
// Test for migration of adding visit_duration column.
TEST_F(HistoryBackendTest, MigrationVisitDuration) {
ASSERT_TRUE(backend_.get());
@@ -3080,10 +3180,16 @@ TEST_F(HistoryBackendTest, DeleteFTSIndexDatabases) {
// Tests that calling DatabaseErrorCallback doesn't cause crash. (Regression
// test for https://crbug.com/796138)
TEST_F(HistoryBackendTest, DatabaseError) {
+ base::HistogramTester histogram_tester;
+
backend_->SetTypedURLSyncBridgeForTest(nullptr);
- backend_->DatabaseErrorCallback(SQLITE_CORRUPT, nullptr);
+ backend_->DatabaseErrorCallback(SQLITE_CANTOPEN, nullptr);
// Run loop to let any posted callbacks run before TearDown().
base::RunLoop().RunUntilIdle();
+
+ histogram_tester.ExpectUniqueSample(
+ "History.DatabaseSqliteError",
+ static_cast<int>(sql::SqliteLoggedResultCode::kCantOpen), 1);
}
// Tests that calling DatabaseErrorCallback results in killing the database and
@@ -3210,6 +3316,53 @@ TEST_F(HistoryBackendTest, RedirectScoring) {
EXPECT_EQ(1, url_row.typed_count());
}
+TEST_F(HistoryBackendTest, RedirectWithQualifiers) {
+ // Create a redirect chain with 3 entries, with a page transition that
+ // includes a qualifier.
+ const ui::PageTransition page_transition = ui::PageTransitionFromInt(
+ ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
+ const char* redirects[] = {"https://foo.com/page1.html",
+ "https://foo.com/page2.html",
+ "https://foo.com/page3.html", nullptr};
+ AddRedirectChainWithTransitionAndTime(redirects, 0, page_transition,
+ base::Time::Now());
+
+ URLRow url1;
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo.com/page1.html"), &url1));
+ URLRow url2;
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo.com/page2.html"), &url2));
+ URLRow url3;
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo.com/page3.html"), &url3));
+
+ // Grab the resulting visits.
+ VisitVector visits1;
+ backend_->GetVisitsForURL(url1.id(), &visits1);
+ ASSERT_EQ(visits1.size(), 1u);
+ VisitVector visits2;
+ backend_->GetVisitsForURL(url2.id(), &visits2);
+ ASSERT_EQ(visits2.size(), 1u);
+ VisitVector visits3;
+ backend_->GetVisitsForURL(url3.id(), &visits3);
+ ASSERT_EQ(visits3.size(), 1u);
+
+ // The page transition, including the qualifier, should have been preserved
+ // across all the visits. Additionally, the appropriate redirect qualifiers
+ // should have been set.
+ EXPECT_TRUE(PageTransitionTypeIncludingQualifiersIs(
+ visits1[0].transition,
+ ui::PageTransitionFromInt(page_transition |
+ ui::PAGE_TRANSITION_CHAIN_START)));
+ EXPECT_TRUE(PageTransitionTypeIncludingQualifiersIs(
+ visits2[0].transition,
+ ui::PageTransitionFromInt(page_transition |
+ ui::PAGE_TRANSITION_SERVER_REDIRECT)));
+ EXPECT_TRUE(PageTransitionTypeIncludingQualifiersIs(
+ visits3[0].transition,
+ ui::PageTransitionFromInt(page_transition |
+ ui::PAGE_TRANSITION_SERVER_REDIRECT |
+ ui::PAGE_TRANSITION_CHAIN_END)));
+}
+
// Tests that a typed navigation will accrue the typed count even when a client
// redirect from HTTP to HTTPS occurs.
TEST_F(HistoryBackendTest, ClientRedirectScoring) {
@@ -3457,7 +3610,7 @@ TEST_F(HistoryBackendTest, QueryMostVisitedURLs) {
backend_->AddPage(args);
}
- MostVisitedURLList most_visited = backend_->QueryMostVisitedURLs(100, 100);
+ MostVisitedURLList most_visited = backend_->QueryMostVisitedURLs(100);
const std::u16string kSomeTitle; // Ignored by equality operator.
EXPECT_THAT(
diff --git a/chromium/components/history/core/browser/history_database.cc b/chromium/components/history/core/browser/history_database.cc
index 5ff83d2e1fe..5eb4b0f3708 100644
--- a/chromium/components/history/core/browser/history_database.cc
+++ b/chromium/components/history/core/browser/history_database.cc
@@ -22,6 +22,7 @@
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
+#include "components/sync/base/features.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
@@ -38,7 +39,7 @@ namespace {
// Current version number. We write databases at the "current" version number,
// but any previous version that can read the "compatible" one can make do with
// our database without *too* many bad effects.
-const int kCurrentVersionNumber = 53;
+const int kCurrentVersionNumber = 55;
const int kCompatibleVersionNumber = 16;
const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold";
@@ -92,7 +93,9 @@ HistoryDatabase::HistoryDatabase(
// Set the cache size. The page size, plus a little extra, times this
// value, tells us how much memory the cache will use maximum.
// 1000 * 4kB = 4MB
- .cache_size = 1000}) {}
+ .cache_size = 1000}),
+ typed_url_metadata_db_(&db_, &meta_table_),
+ history_metadata_db_(&db_, &meta_table_) {}
HistoryDatabase::~HistoryDatabase() = default;
@@ -122,8 +125,14 @@ sql::InitStatus HistoryDatabase::Init(const base::FilePath& history_name) {
return LogInitFailure(InitStep::META_TABLE_INIT);
if (!CreateURLTable(false) || !InitVisitTable() ||
!InitKeywordSearchTermsTable() || !InitDownloadTable() ||
- !InitSegmentTables() || !InitSyncTable() || !InitVisitAnnotationsTables())
+ !InitSegmentTables() || !typed_url_metadata_db_.Init() ||
+ !InitVisitAnnotationsTables()) {
return LogInitFailure(InitStep::CREATE_TABLES);
+ }
+ if (base::FeatureList::IsEnabled(syncer::kSyncEnableHistoryDataType) &&
+ !history_metadata_db_.Init()) {
+ return LogInitFailure(InitStep::CREATE_TABLES);
+ }
CreateMainURLIndex();
// TODO(benjhayden) Remove at some point.
@@ -372,12 +381,16 @@ void HistoryDatabase::UpdateEarlyExpirationThreshold(base::Time threshold) {
cached_early_expiration_threshold_ = threshold;
}
-sql::Database& HistoryDatabase::GetDB() {
- return db_;
+TypedURLSyncMetadataDatabase* HistoryDatabase::GetTypedURLMetadataDB() {
+ return &typed_url_metadata_db_;
}
-sql::MetaTable& HistoryDatabase::GetMetaTable() {
- return meta_table_;
+HistorySyncMetadataDatabase* HistoryDatabase::GetHistoryMetadataDB() {
+ return &history_metadata_db_;
+}
+
+sql::Database& HistoryDatabase::GetDB() {
+ return db_;
}
// Migration -------------------------------------------------------------------
@@ -591,7 +604,7 @@ sql::InitStatus HistoryDatabase::EnsureCurrentVersion() {
std::vector<URLID> visited_url_rowids_sorted;
if (!GetAllVisitedURLRowidsForMigrationToVersion40(
&visited_url_rowids_sorted) ||
- !CleanTypedURLOrphanedMetadataForMigrationToVersion40(
+ !typed_url_metadata_db_.CleanOrphanedMetadataForMigrationToVersion40(
visited_url_rowids_sorted)) {
return LogMigrationFailure(40);
}
@@ -681,6 +694,20 @@ sql::InitStatus HistoryDatabase::EnsureCurrentVersion() {
meta_table_.SetVersionNumber(cur_version);
}
+ if (cur_version == 53) {
+ if (!MigrateContentAnnotationsAddAlternativeTitle())
+ return LogMigrationFailure(53);
+ cur_version++;
+ meta_table_.SetVersionNumber(cur_version);
+ }
+
+ if (cur_version == 54) {
+ if (!MigrateVisitsAutoincrementIdAndAddOriginatorColumns())
+ return LogMigrationFailure(54);
+ cur_version++;
+ meta_table_.SetVersionNumber(cur_version);
+ }
+
// ========================= ^^ new migration code goes here ^^
// ADDING NEW MIGRATION CODE
// =========================
diff --git a/chromium/components/history/core/browser/history_database.h b/chromium/components/history/core/browser/history_database.h
index f34bf6f73f2..f7a4340f4b0 100644
--- a/chromium/components/history/core/browser/history_database.h
+++ b/chromium/components/history/core/browser/history_database.h
@@ -12,11 +12,13 @@
#include "build/build_config.h"
#include "components/history/core/browser/download_database.h"
#include "components/history/core/browser/history_types.h"
+#include "components/history/core/browser/sync/history_sync_metadata_database.h"
#include "components/history/core/browser/sync/typed_url_sync_metadata_database.h"
#include "components/history/core/browser/url_database.h"
#include "components/history/core/browser/visit_annotations_database.h"
#include "components/history/core/browser/visit_database.h"
#include "components/history/core/browser/visitsegment_database.h"
+#include "components/sync/model/model_type_store_base.h"
#include "sql/database.h"
#include "sql/init_status.h"
#include "sql/meta_table.h"
@@ -46,7 +48,6 @@ class HistoryDatabase : public DownloadDatabase,
public AndroidURLsDatabase,
public AndroidCacheDatabase,
#endif
- public TypedURLSyncMetadataDatabase,
public URLDatabase,
public VisitDatabase,
public VisitAnnotationsDatabase,
@@ -156,6 +157,14 @@ class HistoryDatabase : public DownloadDatabase,
virtual base::Time GetEarlyExpirationThreshold();
virtual void UpdateEarlyExpirationThreshold(base::Time threshold);
+ // Sync metadata storage ----------------------------------------------------
+
+ // Returns the sub-database used for storing Sync metadata for Typed URLs.
+ TypedURLSyncMetadataDatabase* GetTypedURLMetadataDB();
+
+ // Returns the sub-database used for storing Sync metadata for History.
+ HistorySyncMetadataDatabase* GetHistoryMetadataDB();
+
private:
#if BUILDFLAG(IS_ANDROID)
// AndroidProviderBackend uses the `db_`.
@@ -164,13 +173,10 @@ class HistoryDatabase : public DownloadDatabase,
#endif
friend class ::InMemoryURLIndexTest;
- // Overridden from URLDatabase, DownloadDatabase, VisitDatabase,
- // VisitSegmentDatabase and TypedURLSyncMetadataDatabase.
+ // Overridden from URLDatabase, DownloadDatabase, VisitDatabase, and
+ // VisitSegmentDatabase.
sql::Database& GetDB() override;
- // Overridden from TypedURLSyncMetadataDatabase.
- sql::MetaTable& GetMetaTable() override;
-
// Migration -----------------------------------------------------------------
// Makes sure the version is up to date, updating if necessary. If the
@@ -192,6 +198,13 @@ class HistoryDatabase : public DownloadDatabase,
sql::Database db_;
sql::MetaTable meta_table_;
+ // Most of the sub-DBs (URLDatabase etc.) are integrated into HistoryDatabase
+ // via inheritance. However, that can lead to "diamond inheritance" issues
+ // when multiple of these base classes define the same methods. Therefore the
+ // Sync metadata DBs are integrated via composition instead.
+ TypedURLSyncMetadataDatabase typed_url_metadata_db_;
+ HistorySyncMetadataDatabase history_metadata_db_;
+
base::Time cached_early_expiration_threshold_;
};
diff --git a/chromium/components/history/core/browser/history_service.cc b/chromium/components/history/core/browser/history_service.cc
index 11f06d5f724..ce9cdafeff1 100644
--- a/chromium/components/history/core/browser/history_service.cc
+++ b/chromium/components/history/core/browser/history_service.cc
@@ -101,12 +101,11 @@ class HistoryService::BackendDelegate : public HistoryBackend::Delegate {
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) override {
service_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&HistoryService::NotifyURLVisited, history_service_,
- transition, row, redirects, visit_time));
+ transition, row, visit_time));
}
void NotifyURLsModified(const URLRows& changed_urls) override {
@@ -518,6 +517,17 @@ void HistoryService::AddSearchMetadataForVisit(
search_terms));
}
+void HistoryService::AddPageMetadataForVisit(
+ const std::string& alternative_title,
+ VisitID visit_id) {
+ TRACE_EVENT0("browser", "HistoryService::AddPageMetadataForVisit");
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ ScheduleTask(PRIORITY_NORMAL,
+ base::BindOnce(&HistoryBackend::AddPageMetadataForVisit,
+ history_backend_, visit_id, alternative_title));
+}
+
void HistoryService::AddPageWithDetails(const GURL& url,
const std::u16string& title,
int visit_count,
@@ -1046,7 +1056,6 @@ base::CancelableTaskTracker::TaskId HistoryService::GetVisibleVisitCountToHost(
base::CancelableTaskTracker::TaskId HistoryService::QueryMostVisitedURLs(
int result_count,
- int days_back,
QueryMostVisitedURLsCallback callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
@@ -1054,7 +1063,7 @@ base::CancelableTaskTracker::TaskId HistoryService::QueryMostVisitedURLs(
return tracker->PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::QueryMostVisitedURLs, history_backend_,
- result_count, days_back),
+ result_count),
std::move(callback));
}
@@ -1175,6 +1184,18 @@ HistoryService::GetTypedURLSyncControllerDelegate() {
base::Unretained(history_backend_.get())));
}
+std::unique_ptr<syncer::ModelTypeControllerDelegate>
+HistoryService::GetHistorySyncControllerDelegate() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // Note that a callback is bound for GetHistorySyncControllerDelegate()
+ // because this getter itself must also run in the backend sequence, and the
+ // proxy object below will take care of that.
+ return std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
+ backend_task_runner_,
+ base::BindRepeating(&HistoryBackend::GetHistorySyncControllerDelegate,
+ base::Unretained(history_backend_.get())));
+}
+
void HistoryService::ProcessLocalDeleteDirective(
const sync_pb::HistoryDeleteDirectiveSpecifics& delete_directive) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -1344,11 +1365,10 @@ void HistoryService::OnDBLoaded() {
void HistoryService::NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (HistoryServiceObserver& observer : observers_)
- observer.OnURLVisited(this, transition, row, redirects, visit_time);
+ observer.OnURLVisited(this, transition, row, visit_time);
}
void HistoryService::NotifyURLsModified(const URLRows& changed_urls) {
diff --git a/chromium/components/history/core/browser/history_service.h b/chromium/components/history/core/browser/history_service.h
index 6e18bb033cf..ff791aa5343 100644
--- a/chromium/components/history/core/browser/history_service.h
+++ b/chromium/components/history/core/browser/history_service.h
@@ -245,6 +245,10 @@ class HistoryService : public KeyedService {
const std::u16string& search_terms,
VisitID visit_id);
+ // Updates the history database with additional page metadata.
+ void AddPageMetadataForVisit(const std::string& alternative_title,
+ VisitID visit_id);
+
// Querying ------------------------------------------------------------------
// Returns the information about the requested URL. If the URL is found,
@@ -323,14 +327,12 @@ class HistoryService : public KeyedService {
base::CancelableTaskTracker* tracker);
// Request the `result_count` most visited URLs and the chain of
- // redirects leading to each of these URLs. `days_back` is the
- // number of days of history to use. Used by TopSites.
+ // redirects leading to each of these URLs. Used by TopSites.
using QueryMostVisitedURLsCallback =
base::OnceCallback<void(MostVisitedURLList)>;
base::CancelableTaskTracker::TaskId QueryMostVisitedURLs(
int result_count,
- int days_back,
QueryMostVisitedURLsCallback callback,
base::CancelableTaskTracker* tracker);
@@ -648,6 +650,11 @@ class HistoryService : public KeyedService {
std::unique_ptr<syncer::ModelTypeControllerDelegate>
GetTypedURLSyncControllerDelegate();
+ // For sync codebase only: instantiates a controller delegate to interact with
+ // HistorySyncBridge. Must be called from the UI thread.
+ std::unique_ptr<syncer::ModelTypeControllerDelegate>
+ GetHistorySyncControllerDelegate();
+
// Override `backend_task_runner_` for testing; needs to be called before
// Init.
void set_backend_task_runner_for_testing(
@@ -714,13 +721,9 @@ class HistoryService : public KeyedService {
// Notify all HistoryServiceObservers registered that user is visiting a URL.
// The `row` ID will be set to the value that is currently in effect in the
- // main history database. `redirects` is the list of redirects leading up to
- // the URL. If we have a redirect chain A -> B -> C and user is visiting C,
- // then `redirects[0]=B` and `redirects[1]=A`. If there are no redirects,
- // `redirects` is an empty vector.
+ // main history database.
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time);
// Notify all HistoryServiceObservers registered that URLs have been added or
diff --git a/chromium/components/history/core/browser/history_service_observer.h b/chromium/components/history/core/browser/history_service_observer.h
index 5eb17271ddc..08e20b526e8 100644
--- a/chromium/components/history/core/browser/history_service_observer.h
+++ b/chromium/components/history/core/browser/history_service_observer.h
@@ -24,14 +24,10 @@ class HistoryServiceObserver {
// Called when user visits an URL.
//
// The `row` ID will be set to the value that is currently in effect in the
- // main history database. `redirects` is the list of redirects leading up to
- // the URL. If we have a redirect chain A -> B -> C and user is visiting C,
- // then `redirects[0]=B` and `redirects[1]=A`. If there are no redirects,
- // `redirects` is an empty vector.
+ // main history database.
virtual void OnURLVisited(HistoryService* history_service,
ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) {}
// Called when a URL has been added or modified.
diff --git a/chromium/components/history/core/browser/history_service_unittest.cc b/chromium/components/history/core/browser/history_service_unittest.cc
index c9a55f5fdb9..698bca0413b 100644
--- a/chromium/components/history/core/browser/history_service_unittest.cc
+++ b/chromium/components/history/core/browser/history_service_unittest.cc
@@ -128,12 +128,10 @@ class HistoryServiceTest : public testing::Test {
void QueryMostVisitedURLs() {
const int kResultCount = 20;
- const int kDaysBack = 90;
base::RunLoop run_loop;
history_service_->QueryMostVisitedURLs(
- kResultCount, kDaysBack,
- base::BindLambdaForTesting([&](MostVisitedURLList urls) {
+ kResultCount, base::BindLambdaForTesting([&](MostVisitedURLList urls) {
most_visited_urls_ = urls;
run_loop.Quit();
}),
diff --git a/chromium/components/history/core/browser/history_types.cc b/chromium/components/history/core/browser/history_types.cc
index 020c5bb30d4..25fe3c37093 100644
--- a/chromium/components/history/core/browser/history_types.cc
+++ b/chromium/components/history/core/browser/history_types.cc
@@ -12,6 +12,12 @@
namespace history {
+namespace {
+
+static constexpr float kScoreEpsilon = 1e-8;
+
+} // namespace
+
// VisitRow --------------------------------------------------------------------
VisitRow::VisitRow() = default;
@@ -33,6 +39,8 @@ VisitRow::VisitRow(URLID arg_url_id,
VisitRow::~VisitRow() = default;
+VisitRow::VisitRow(const VisitRow&) = default;
+
// QueryResults ----------------------------------------------------------------
QueryResults::QueryResults() = default;
@@ -203,8 +211,10 @@ QueryURLResult& QueryURLResult::operator=(QueryURLResult&&) noexcept = default;
MostVisitedURL::MostVisitedURL() = default;
-MostVisitedURL::MostVisitedURL(const GURL& url, const std::u16string& title)
- : url(url), title(title) {}
+MostVisitedURL::MostVisitedURL(const GURL& url,
+ const std::u16string& title,
+ double score)
+ : url(url), title(title), score(score) {}
MostVisitedURL::MostVisitedURL(const MostVisitedURL& other) = default;
@@ -417,15 +427,45 @@ ClusterVisit::ClusterVisit(ClusterVisit&&) = default;
ClusterVisit& ClusterVisit::operator=(const ClusterVisit&) = default;
ClusterVisit& ClusterVisit::operator=(ClusterVisit&&) = default;
+ClusterKeywordData::ClusterKeywordData() = default;
+ClusterKeywordData::ClusterKeywordData(
+ const std::vector<std::string>& entity_collections)
+ : entity_collections(entity_collections) {}
+ClusterKeywordData::ClusterKeywordData(
+ ClusterKeywordData::ClusterKeywordType type,
+ float score,
+ const std::vector<std::string>& entity_collections)
+ : type(type), score(score), entity_collections(entity_collections) {}
+ClusterKeywordData::ClusterKeywordData(const ClusterKeywordData&) = default;
+ClusterKeywordData::ClusterKeywordData(ClusterKeywordData&&) = default;
+ClusterKeywordData& ClusterKeywordData::operator=(const ClusterKeywordData&) =
+ default;
+ClusterKeywordData& ClusterKeywordData::operator=(ClusterKeywordData&&) =
+ default;
+ClusterKeywordData::~ClusterKeywordData() = default;
+
+bool ClusterKeywordData::operator==(const ClusterKeywordData& data) const {
+ return type == data.type && std::fabs(score - data.score) < kScoreEpsilon &&
+ entity_collections == data.entity_collections;
+}
+
+void ClusterKeywordData::MaybeUpdateKeywordType(
+ ClusterKeywordData::ClusterKeywordType other_type) {
+ if (type < other_type) {
+ type = other_type;
+ }
+}
+
Cluster::Cluster() = default;
Cluster::Cluster(int64_t cluster_id,
const std::vector<ClusterVisit>& visits,
- const std::vector<std::u16string>& keywords,
+ const base::flat_map<std::u16string, ClusterKeywordData>&
+ keyword_to_data_map,
bool should_show_on_prominent_ui_surfaces,
absl::optional<std::u16string> label)
: cluster_id(cluster_id),
visits(visits),
- keywords(keywords),
+ keyword_to_data_map(keyword_to_data_map),
should_show_on_prominent_ui_surfaces(
should_show_on_prominent_ui_surfaces),
label(label) {}
@@ -435,6 +475,14 @@ Cluster& Cluster::operator=(const Cluster&) = default;
Cluster& Cluster::operator=(Cluster&&) = default;
Cluster::~Cluster() = default;
+std::vector<std::u16string> Cluster::GetKeywords() const {
+ std::vector<std::u16string> keywords;
+ for (const auto& p : keyword_to_data_map) {
+ keywords.push_back(p.first);
+ }
+ return keywords;
+}
+
ClusterRow::ClusterRow() = default;
ClusterRow::ClusterRow(int64_t cluster_id) : cluster_id(cluster_id) {}
ClusterRow::ClusterRow(const ClusterRow&) = default;
diff --git a/chromium/components/history/core/browser/history_types.h b/chromium/components/history/core/browser/history_types.h
index d66ec7ff975..5f25889794e 100644
--- a/chromium/components/history/core/browser/history_types.h
+++ b/chromium/components/history/core/browser/history_types.h
@@ -15,12 +15,14 @@
#include <vector>
#include "base/callback.h"
+#include "base/containers/flat_map.h"
#include "base/containers/stack_container.h"
#include "base/time/time.h"
#include "components/favicon_base/favicon_types.h"
#include "components/history/core/browser/history_context.h"
#include "components/history/core/browser/url_row.h"
#include "components/query_parser/query_parser.h"
+#include "components/query_parser/snippet.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
@@ -69,6 +71,7 @@ class VisitRow {
bool arg_incremented_omnibox_typed_score,
VisitID arg_opener_visit);
~VisitRow();
+ VisitRow(const VisitRow&);
// Compares two visits based on dates, for sorting.
bool operator<(const VisitRow& other) const {
@@ -115,6 +118,12 @@ class VisitRow {
// same tab.
VisitID opener_visit = 0;
+ // These are set only for synced visits originating from a different machine.
+ // `originator_cache_guid` is the originator machine's unique client ID. It's
+ // called a "cache" just to match Chrome Sync's terminology.
+ std::string originator_cache_guid;
+ VisitID originator_visit_id = 0;
+
// We allow the implicit copy constructor and operator=.
};
@@ -332,10 +341,12 @@ struct VisibleVisitCountToHostResult {
// MostVisitedURL --------------------------------------------------------------
-// Holds the per-URL information of the most visited query.
+// Holds the information for a Most Visited page.
struct MostVisitedURL {
MostVisitedURL();
- MostVisitedURL(const GURL& url, const std::u16string& title);
+ MostVisitedURL(const GURL& url,
+ const std::u16string& title,
+ double score = 0.0);
MostVisitedURL(const MostVisitedURL& other);
MostVisitedURL(MostVisitedURL&& other) noexcept;
~MostVisitedURL();
@@ -346,8 +357,9 @@ struct MostVisitedURL {
return url == other.url;
}
- GURL url;
- std::u16string title;
+ GURL url; // The URL of the page.
+ std::u16string title; // The title of the page.
+ double score{0.0}; // The frecency score of the page.
};
// FilteredURL -----------------------------------------------------------------
@@ -842,16 +854,70 @@ struct ClusterVisit {
// should not be used by the UI.
float engagement_score = 0.0;
- // The visit URL modified for better dupe finding. The result may not be
- // navigable or even valid; it's only meant to be used for detecting
- // duplicates. This is similar in intent to
- // `AutocompleteMatch::stripped_destination_url`, but is not the same, as
- // History Clusters and Omnibox have different deduping requirements.
+ // The visit URL stripped down for aggressive deduping. This GURL may not be
+ // navigable or even valid. The stripping on `url_for_deduping` must be
+ // strictly more aggressive than on `url_for_display`. This ensures that the
+ // UI never shows two visits that look completely identical.
+ //
+ // The stripping is so aggressive that the URL should not be used alone for
+ // deduping. See `SimilarVisitDeDeduperClusterFinalizer` for an example usage
+ // that combines this with the page title as a deduping key.
GURL url_for_deduping;
// The normalized URL for the visit (i.e. a SRP URL normalized based on the
// user's default search provider).
GURL normalized_url;
+
+ // The URL used for display. Computed in the cross-platform code to provide
+ // a consistent experience between WebUI and Mobile.
+ std::u16string url_for_display;
+
+ // Which positions matched the search query in various fields.
+ query_parser::Snippet::MatchPositions title_match_positions;
+ query_parser::Snippet::MatchPositions url_for_display_match_positions;
+
+ // If true, the visit should be "below the fold" and not initially shown in
+ // any UI. It is still included in the cluster so that it can be queried over,
+ // as well as deleted when the whole cluster is deleted.
+ bool hidden = false;
+};
+
+// Additional data for a cluster keyword.
+struct ClusterKeywordData {
+ // Types are ordered according to preferences.
+ enum ClusterKeywordType {
+ kUnknown = 0,
+ kEntityCategory = 1,
+ kEntityAlias = 2,
+ kEntity = 3,
+ kSearchTerms = 4
+ };
+
+ ClusterKeywordData();
+ explicit ClusterKeywordData(
+ const std::vector<std::string>& entity_collections);
+ ClusterKeywordData(ClusterKeywordType type,
+ float score,
+ const std::vector<std::string>& entity_collections);
+ ClusterKeywordData(const ClusterKeywordData&);
+ ClusterKeywordData(ClusterKeywordData&&);
+ ClusterKeywordData& operator=(const ClusterKeywordData&);
+ ClusterKeywordData& operator=(ClusterKeywordData&&);
+ ~ClusterKeywordData();
+ bool operator==(const ClusterKeywordData& data) const;
+
+ // Updates cluster keyword type if a new type is preferred over the existing
+ // type.
+ void MaybeUpdateKeywordType(ClusterKeywordType other_type);
+
+ ClusterKeywordType type;
+
+ // A floating point score describing how important this
+ // keyword is to the containing cluster.
+ float score;
+
+ // Entity collections associated with the keyword this is attached to.
+ std::vector<std::string> entity_collections;
};
// A cluster of `ClusterVisit`s with associated metadata (i.e. `keywords` and
@@ -860,7 +926,8 @@ struct Cluster {
Cluster();
Cluster(int64_t cluster_id,
const std::vector<ClusterVisit>& visits,
- const std::vector<std::u16string>& keywords,
+ const base::flat_map<std::u16string, ClusterKeywordData>&
+ keyword_to_data_map,
bool should_show_on_prominent_ui_surfaces = true,
absl::optional<std::u16string> label = absl::nullopt);
Cluster(const Cluster&);
@@ -869,17 +936,31 @@ struct Cluster {
Cluster& operator=(Cluster&&);
~Cluster();
+ std::vector<std::u16string> GetKeywords() const;
+
int64_t cluster_id = 0;
std::vector<ClusterVisit> visits;
- // TODO(manukh): retrieve and persist `keywords`,
+ // TODO(manukh): retrieve and persist `keyword_to_data_map`,
// `should_show_on_prominent_ui_surfaces, and `label`.
- std::vector<std::u16string> keywords;
+
+ // A map of keywords to additional data.
+ base::flat_map<std::u16string, ClusterKeywordData> keyword_to_data_map;
+
// Whether the cluster should be shown prominently on UI surfaces.
bool should_show_on_prominent_ui_surfaces = true;
+
// A suitable label for the cluster. Will be nullopt if no suitable label
// could be determined.
absl::optional<std::u16string> label;
+ // The positions within the label that match the search query, if it exists.
+ query_parser::Snippet::MatchPositions label_match_positions;
+
+ // The vector of related searches for the whole cluster. This is derived from
+ // the related searches of the constituent visits, and computed in
+ // cross-platform code so we have a consistent set across platforms.
+ std::vector<std::string> related_searches;
+
// A floating point score that's positive if the cluster matches the user's
// search query, and zero otherwise. This score changes depending on the
// entered search query, so this should never be persisted. It's a
diff --git a/chromium/components/history/core/browser/in_memory_history_backend.cc b/chromium/components/history/core/browser/in_memory_history_backend.cc
index ff09e2f8f40..a4d68b8ca8f 100644
--- a/chromium/components/history/core/browser/in_memory_history_backend.cc
+++ b/chromium/components/history/core/browser/in_memory_history_backend.cc
@@ -40,7 +40,6 @@ void InMemoryHistoryBackend::DeleteAllSearchTermsForKeyword(
void InMemoryHistoryBackend::OnURLVisited(HistoryService* history_service,
ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) {
OnURLVisitedOrModified(row);
}
diff --git a/chromium/components/history/core/browser/in_memory_history_backend.h b/chromium/components/history/core/browser/in_memory_history_backend.h
index 4cbc8ad47d9..1a0f9011e3e 100644
--- a/chromium/components/history/core/browser/in_memory_history_backend.h
+++ b/chromium/components/history/core/browser/in_memory_history_backend.h
@@ -76,7 +76,6 @@ class InMemoryHistoryBackend : public HistoryServiceObserver {
void OnURLVisited(HistoryService* history_service,
ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) override;
void OnURLsModified(HistoryService* history_service,
const URLRows& changed_urls) override;
diff --git a/chromium/components/history/core/browser/keyword_id.h b/chromium/components/history/core/browser/keyword_id.h
index f7cb1f00ec9..dac76a3bd09 100644
--- a/chromium/components/history/core/browser/keyword_id.h
+++ b/chromium/components/history/core/browser/keyword_id.h
@@ -7,11 +7,13 @@
#include <stdint.h>
+#include "components/search_engines/template_url_id.h"
+
namespace history {
// ID of a keyword associated with a URL and a search term.
-// 0 is the invalid value.
-typedef int64_t KeywordID;
+// 0 is the invalid value, i.e., kInvalidTemplateURLID.
+using KeywordID = TemplateURLID;
} // namespace history
diff --git a/chromium/components/history/core/browser/keyword_search_term.cc b/chromium/components/history/core/browser/keyword_search_term.cc
index 96ef59829cc..d9074ead7f5 100644
--- a/chromium/components/history/core/browser/keyword_search_term.cc
+++ b/chromium/components/history/core/browser/keyword_search_term.cc
@@ -4,32 +4,43 @@
#include "components/history/core/browser/keyword_search_term.h"
+#include <cmath>
+
namespace history {
-NormalizedKeywordSearchTermVisit::~NormalizedKeywordSearchTermVisit() = default;
-
-double NormalizedKeywordSearchTermVisit::GetFrecency(
- base::Time now,
- int recency_decay_unit_sec,
- double frequency_exponent) const {
- const double recency_sec =
- base::TimeDelta(now - most_recent_visit_time).InSeconds();
- const double recency_decayed =
- recency_decay_unit_sec / (recency_sec + recency_decay_unit_sec);
- const double frequency_powered = pow(visits, frequency_exponent);
- return frequency_powered * recency_decayed;
+namespace {
+
+// Returns a KeywordSearchTermVisit populated with the columns returned from
+// |statement|. |statement| is expected to return the following columns which
+// match in order and type to the fields in the KeywordSearchTermVisit less the
+// score which is a calculated field.
+//+----------+-----------------+-------------+-----------------+
+//| term | normalized_term | visit_count | last_visit_time |
+//+----------+-----------------+-------------+-----------------+
+//| string16 | string16 | int | int64 |
+//+----------+-----------------+-------------+-----------------+
+std::unique_ptr<KeywordSearchTermVisit> KeywordSearchTermVisitFromStatement(
+ sql::Statement& statement) {
+ auto search_term = std::make_unique<KeywordSearchTermVisit>();
+ search_term->term = statement.ColumnString16(0);
+ search_term->normalized_term = statement.ColumnString16(1);
+ search_term->visit_count = statement.ColumnInt(2);
+ search_term->last_visit_time =
+ base::Time::FromInternalValue(statement.ColumnInt64(3));
+ return search_term;
}
-KeywordSearchTermVisit::KeywordSearchTermVisit() : visits(0) {}
-
-KeywordSearchTermVisit::~KeywordSearchTermVisit() {}
-
+} // namespace
-KeywordSearchTermRow::KeywordSearchTermRow() : keyword_id(0), url_id(0) {}
+// KeywordSearchTermVisitEnumerator --------------------------------------------
-KeywordSearchTermRow::KeywordSearchTermRow(const KeywordSearchTermRow& other) =
- default;
-
-KeywordSearchTermRow::~KeywordSearchTermRow() {}
+std::unique_ptr<KeywordSearchTermVisit>
+KeywordSearchTermVisitEnumerator::GetNextVisit() {
+ if (initialized_ && statement_.Step()) {
+ return KeywordSearchTermVisitFromStatement(statement_);
+ }
+ initialized_ = false;
+ return nullptr;
+}
} // namespace history
diff --git a/chromium/components/history/core/browser/keyword_search_term.h b/chromium/components/history/core/browser/keyword_search_term.h
index a8c88f4e9d2..785c72d34df 100644
--- a/chromium/components/history/core/browser/keyword_search_term.h
+++ b/chromium/components/history/core/browser/keyword_search_term.h
@@ -10,62 +10,66 @@
#include "base/time/time.h"
#include "components/history/core/browser/keyword_id.h"
#include "components/history/core/browser/url_row.h"
+#include "sql/statement.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace history {
-// NormalizedKeywordSearchTermVisit is returned by
-// GetMostRecentNormalizedKeywordSearchTerms. It contains the time of the most
-// recent visit and the visit count for the normalized search term aggregated
-// from the keyword visits.
-struct NormalizedKeywordSearchTermVisit {
- NormalizedKeywordSearchTermVisit() = default;
- ~NormalizedKeywordSearchTermVisit();
-
- // Returns the frecency score of the visit based on the following formula:
- // (frequency ^ frequency_exponent) * recency_decay_unit_in_seconds
- // frecency = ————————————————————————————————————————————————————————————————
- // recency_in_seconds + recency_decay_unit_in_seconds
- // This score combines frequency and recency of the visit favoring ones that
- // are more frequent and more recent (see go/local-zps-frecency-ranking).
- // `recency_decay_unit_sec` is the number of seconds until the recency
- // component of the score decays to half. `frequency_exponent` is factor by
- // which the frequency of the visit is exponentiated.
- double GetFrecency(base::Time now,
- int recency_decay_unit_sec,
- double frequency_exponent) const;
-
- std::u16string normalized_term; // The search term, in lower case and with
- // extra whitespaces collapsed.
- int visits{0}; // The visit count.
- base::Time most_recent_visit_time; // The time of the most recent visit.
-};
-
-// KeywordSearchTermVisit is returned from GetMostRecentKeywordSearchTerms. It
-// gives the time and search term of the keyword visit.
+// Represents one or more visits to a keyword search term. It contains the
+// search term and the normalized search term in addition to the visit count and
+// the last visit time. An optional frecency score may be provided by the
+// utility functions/helpers in keyword_search_term_util.h where applicable.
struct KeywordSearchTermVisit {
- KeywordSearchTermVisit();
- ~KeywordSearchTermVisit();
+ KeywordSearchTermVisit() = default;
+ KeywordSearchTermVisit(const KeywordSearchTermVisit&) = delete;
+ KeywordSearchTermVisit& operator=(const KeywordSearchTermVisit&) = delete;
+ ~KeywordSearchTermVisit() = default;
std::u16string term; // The search term that was used.
std::u16string normalized_term; // The search term, in lower case and with
// extra whitespaces collapsed.
- int visits; // The visit count.
- base::Time time; // The time of the most recent visit.
+ int visit_count{0}; // The search term visit count.
+ base::Time last_visit_time; // The time of the last visit.
+ absl::optional<double> score; // The optional calculated frecency score.
};
// Used for URLs that have a search term associated with them.
struct KeywordSearchTermRow {
- KeywordSearchTermRow();
- KeywordSearchTermRow(const KeywordSearchTermRow& other);
- ~KeywordSearchTermRow();
+ KeywordSearchTermRow() = default;
+ KeywordSearchTermRow(const KeywordSearchTermRow& other) = default;
+ ~KeywordSearchTermRow() = default;
- KeywordID keyword_id; // ID of the keyword.
- URLID url_id; // ID of the url.
+ KeywordID keyword_id{0}; // ID of the keyword.
+ URLID url_id{0}; // ID of the url.
std::u16string term; // The search term that was used.
std::u16string normalized_term; // The search term, in lower case and with
// extra whitespaces collapsed.
};
+// KeywordSearchTermVisitEnumerator --------------------------------------------
+
+// A basic enumerator to enumerate keyword search term visits. May be created
+// and initialized by URLDatabase only.
+class KeywordSearchTermVisitEnumerator {
+ public:
+ KeywordSearchTermVisitEnumerator(const KeywordSearchTermVisitEnumerator&) =
+ delete;
+ KeywordSearchTermVisitEnumerator& operator=(
+ const KeywordSearchTermVisitEnumerator&) = delete;
+
+ ~KeywordSearchTermVisitEnumerator() = default;
+
+ // Returns the next search term visit or nullptr if no more visits are left.
+ std::unique_ptr<KeywordSearchTermVisit> GetNextVisit();
+
+ private:
+ friend class URLDatabase;
+ KeywordSearchTermVisitEnumerator() = default;
+
+ sql::Statement statement_; // The statement to create KeywordSearchTermVisit.
+ bool initialized_{false}; // Whether |statement_| can be executed.
+};
+
} // namespace history
#endif // COMPONENTS_HISTORY_CORE_BROWSER_KEYWORD_SEARCH_TERM_H_
diff --git a/chromium/components/history/core/browser/keyword_search_term_util.cc b/chromium/components/history/core/browser/keyword_search_term_util.cc
new file mode 100644
index 00000000000..b06155eca24
--- /dev/null
+++ b/chromium/components/history/core/browser/keyword_search_term_util.cc
@@ -0,0 +1,272 @@
+// Copyright 2022 The Chromium 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/history/core/browser/keyword_search_term_util.h"
+
+#include "base/time/time.h"
+#include "components/history/core/browser/keyword_search_term.h"
+
+namespace history {
+
+namespace {
+
+// Calculates the score for the given number of visits in a given day.
+// Recent visits count more than historical ones, so we multiply in a boost
+// depending on how long ago this day was. This boost is a curve that
+// smoothly goes through these values: Today gets 3x, a week ago 2x, three
+// weeks ago 1.5x, falling off to 1x at the limit of how far we reach into
+// the past.
+double GetMostVisitedFrecencyScore(int visit_count,
+ base::Time day,
+ base::Time now) {
+ double day_score = 1.0 + log(static_cast<double>(visit_count));
+ int days_ago = (now - day).InDays();
+ double recency_boost = 1.0 + (2.0 * (1.0 / (1.0 + days_ago / 7.0)));
+ return recency_boost * day_score;
+}
+
+// Returns whether two search terms are identical, i.e., have the same
+// normalized search terms.
+bool IsSameSearchTerm(const KeywordSearchTermVisit& search_term,
+ const KeywordSearchTermVisit& other_search_term) {
+ return search_term.normalized_term == other_search_term.normalized_term;
+}
+
+// Return whether a visit to a search term is a duplicative visit, i.e., a visit
+// to the same search term in an interval smaller than
+// kAutocompleteDuplicateVisitIntervalThreshold.
+bool IsDuplicateVisit(const KeywordSearchTermVisit& search_term,
+ const KeywordSearchTermVisit& other_search_term) {
+ return IsSameSearchTerm(search_term, other_search_term) &&
+ (search_term.last_visit_time - other_search_term.last_visit_time <=
+ kAutocompleteDuplicateVisitIntervalThreshold);
+}
+
+// Transforms a visit time to its timeslot, i.e., day of the viist.
+base::Time VisitTimeToTimeslot(base::Time visit_time) {
+ return visit_time.LocalMidnight();
+}
+
+// Returns whether two search term visits are in the same timeslot.
+bool IsSameTimeslot(const KeywordSearchTermVisit& search_term,
+ const KeywordSearchTermVisit& other_search_term) {
+ return VisitTimeToTimeslot(search_term.last_visit_time) ==
+ VisitTimeToTimeslot(other_search_term.last_visit_time);
+}
+
+} // namespace
+
+const base::TimeDelta kAutocompleteDuplicateVisitIntervalThreshold =
+ base::Minutes(5);
+
+// Returns the frecency score of the visit based on the following formula:
+// (frequency ^ kFrequencyExponent) * kRecencyDecayUnitSec
+// frecency = ————————————————————————————————————————————————————————————————
+// recency_in_seconds + kRecencyDecayUnitSec
+double GetFrecencyScore(int visit_count,
+ base::Time visit_time,
+ base::Time now) {
+ // The number of seconds until the recency component decays by half.
+ constexpr base::TimeDelta kRecencyDecayUnitSec = base::Seconds(60);
+ // The factor by which the frequency component is exponentiated.
+ constexpr double kFrequencyExponent = 1.15;
+
+ const double recency_decayed =
+ kRecencyDecayUnitSec /
+ (base::TimeDelta(now - visit_time) + kRecencyDecayUnitSec);
+ const double frequency_powered = pow(visit_count, kFrequencyExponent);
+ return frequency_powered * recency_decayed;
+}
+
+// SearchTermHelper ------------------------------------------------------------
+
+// A helper class to return keyword search terms with visit counts accumulated
+// across visits for use as prefix or zero-prefix suggestions in the omnibox.
+class SearchTermHelper {
+ public:
+ SearchTermHelper() = default;
+
+ SearchTermHelper(const SearchTermHelper&) = delete;
+ SearchTermHelper& operator=(const SearchTermHelper&) = delete;
+
+ ~SearchTermHelper() = default;
+
+ // |enumerator| enumerates keyword search term visits from the URLDatabase.
+ // |ignore_duplicate_visits| specifies whether duplicative visits to a search
+ // term should be ignored.
+ std::unique_ptr<KeywordSearchTermVisit> GetNextSearchTermFromEnumerator(
+ KeywordSearchTermVisitEnumerator& enumerator,
+ bool ignore_duplicate_visits) {
+ // |next_search_term| acts as the fast pointer and |last_search_term_| acts
+ // as the slow pointer accumulating the search term visit count across
+ // visits.
+ while (auto next_search_term = enumerator.GetNextVisit()) {
+ if (ignore_duplicate_visits && last_search_term_ &&
+ IsDuplicateVisit(*next_search_term, *last_search_term_)) {
+ continue;
+ }
+
+ if (last_search_term_ &&
+ IsSameSearchTerm(*next_search_term, *last_search_term_)) {
+ // We encountered the same search term:
+ // 1. Move |last_search_term_| forward.
+ // 2. Add up the search term visit count.
+ int visit_count = last_search_term_->visit_count;
+ last_search_term_ = std::move(next_search_term);
+ last_search_term_->visit_count += visit_count;
+ } else if (last_search_term_) {
+ // We encountered a new search term and |last_search_term_| has a value:
+ // 1. Move |last_search_term_| forward.
+ // 2. Return the old |last_search_term_|.
+ auto search_term_to_return = std::move(last_search_term_);
+ last_search_term_ = std::move(next_search_term);
+ return search_term_to_return;
+ } else {
+ // We encountered a new search term and |last_search_term_| has no
+ // value:
+ // 1. Move |last_search_term_| forward.
+ last_search_term_ = std::move(next_search_term);
+ }
+ }
+
+ return last_search_term_ ? std::move(last_search_term_) : nullptr;
+ }
+
+ private:
+ // The last seen search term.
+ std::unique_ptr<KeywordSearchTermVisit> last_search_term_;
+};
+
+void GetAutocompleteSearchTermsFromEnumerator(
+ KeywordSearchTermVisitEnumerator& enumerator,
+ bool ignore_duplicate_visits,
+ SearchTermRankingPolicy ranking_policy,
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>>* search_terms) {
+ SearchTermHelper helper;
+ const base::Time now = base::Time::Now();
+ while (auto search_term = helper.GetNextSearchTermFromEnumerator(
+ enumerator, ignore_duplicate_visits)) {
+ if (ranking_policy == SearchTermRankingPolicy::kFrecency) {
+ search_term->score = GetFrecencyScore(search_term->visit_count,
+ search_term->last_visit_time, now);
+ }
+ search_terms->push_back(std::move(search_term));
+ }
+ // Order the search terms by descending recency or frecency.
+ std::stable_sort(search_terms->begin(), search_terms->end(),
+ [&](const auto& a, const auto& b) {
+ return ranking_policy == SearchTermRankingPolicy::kFrecency
+ ? a->score > b->score
+ : a->last_visit_time > b->last_visit_time;
+ });
+}
+
+// MostRepeatedSearchTermHelper ------------------------------------------------
+
+// A helper class to return keyword search terms with frecency scores
+// accumulated across days for use in the Most Visited tiles.
+class MostRepeatedSearchTermHelper {
+ public:
+ MostRepeatedSearchTermHelper() = default;
+
+ MostRepeatedSearchTermHelper(const MostRepeatedSearchTermHelper&) = delete;
+ MostRepeatedSearchTermHelper& operator=(const MostRepeatedSearchTermHelper&) =
+ delete;
+
+ ~MostRepeatedSearchTermHelper() = default;
+
+ // |enumerator| enumerates keyword search term visits from the URLDatabase.
+ // |now| is the time used to score the search term.
+ std::unique_ptr<KeywordSearchTermVisit> GetNextSearchTermFromEnumerator(
+ KeywordSearchTermVisitEnumerator& enumerator,
+ base::Time now) {
+ // |next_search_term| acts as the fast pointer and |last_search_term_| acts
+ // as the slow pointer accumulating the search term score across visits.
+ while (auto next_search_term = enumerator.GetNextVisit()) {
+ bool is_same_search_term =
+ last_search_term_ &&
+ IsSameSearchTerm(*next_search_term, *last_search_term_);
+ if (is_same_search_term &&
+ IsSameTimeslot(*next_search_term, *last_search_term_)) {
+ // We are in the same timeslot for the same search term:
+ // 1. Move |last_search_term_| forward.
+ // 2. Add up the search term visit count in the timeslot.
+ // 3. Carry over the search term score.
+ int visit_count = last_search_term_->visit_count;
+ double score = last_search_term_->score.value_or(0.0);
+ last_search_term_ = std::move(next_search_term);
+ last_search_term_->visit_count += visit_count;
+ last_search_term_->score =
+ last_search_term_->score.value_or(0.0) + score;
+
+ } else if (is_same_search_term) {
+ // We are in a new timeslot for the same search term:
+ // 1. Update the search term score by adding the last timeslot's score.
+ // 2. Move |last_search_term_| forward.
+ // 3. Carry over the search term score.
+ double score =
+ last_search_term_->score.value_or(0.0) +
+ GetMostVisitedFrecencyScore(
+ last_search_term_->visit_count,
+ VisitTimeToTimeslot(last_search_term_->last_visit_time), now);
+ last_search_term_ = std::move(next_search_term);
+ last_search_term_->score = score;
+
+ } else if (last_search_term_) {
+ // We encountered a new search term and |last_search_term_| has a value:
+ // 1. Update the search term score by adding the last timeslot's score.
+ // 2. Move |last_search_term_| forward.
+ // 3. Return the old |last_search_term_|.
+ double score =
+ last_search_term_->score.value_or(0.0) +
+ GetMostVisitedFrecencyScore(
+ last_search_term_->visit_count,
+ VisitTimeToTimeslot(last_search_term_->last_visit_time), now);
+ last_search_term_->score = score;
+ auto search_term_to_return = std::move(last_search_term_);
+ last_search_term_ = std::move(next_search_term);
+ return search_term_to_return;
+ } else {
+ // We encountered a new search term and |last_search_term_| has no
+ // value:
+ // 1. Move |last_search_term_| forward.
+ last_search_term_ = std::move(next_search_term);
+ }
+ }
+
+ // |last_search_term_| has a value:
+ // 1. Update the search term score by adding the last timeslot's score.
+ if (last_search_term_) {
+ double score =
+ last_search_term_->score.value_or(0.0) +
+ GetMostVisitedFrecencyScore(
+ last_search_term_->visit_count,
+ VisitTimeToTimeslot(last_search_term_->last_visit_time), now);
+ last_search_term_->score = score;
+ }
+
+ return last_search_term_ ? std::move(last_search_term_) : nullptr;
+ }
+
+ private:
+ // The last seen search term.
+ std::unique_ptr<KeywordSearchTermVisit> last_search_term_;
+};
+
+void GetMostRepeatedSearchTermsFromEnumerator(
+ KeywordSearchTermVisitEnumerator& enumerator,
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>>* search_terms) {
+ MostRepeatedSearchTermHelper helper;
+ const base::Time now = base::Time::Now();
+ while (auto search_term =
+ helper.GetNextSearchTermFromEnumerator(enumerator, now)) {
+ search_terms->push_back(std::move(search_term));
+ }
+ // Order the search terms by descending frecency scores.
+ std::stable_sort(
+ search_terms->begin(), search_terms->end(),
+ [](const auto& a, const auto& b) { return a->score > b->score; });
+}
+
+} // namespace history
diff --git a/chromium/components/history/core/browser/keyword_search_term_util.h b/chromium/components/history/core/browser/keyword_search_term_util.h
new file mode 100644
index 00000000000..a5df0270056
--- /dev/null
+++ b/chromium/components/history/core/browser/keyword_search_term_util.h
@@ -0,0 +1,66 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_HISTORY_CORE_BROWSER_KEYWORD_SEARCH_TERM_UTIL_H_
+#define COMPONENTS_HISTORY_CORE_BROWSER_KEYWORD_SEARCH_TERM_UTIL_H_
+
+#include <memory>
+#include <vector>
+
+namespace base {
+class Time;
+class TimeDelta;
+} // namespace base
+
+namespace history {
+
+class KeywordSearchTermVisitEnumerator;
+struct KeywordSearchTermVisit;
+
+enum class SearchTermRankingPolicy {
+ kRecency, // From the most recent to the least recent.
+ kFrecency // By descending frecency score calculated by |GetFrecencyScore|.
+};
+
+// The time interval within which a duplicate query is considered invalid for
+// autocomplete purposes.
+// These invalid duplicates are extracted from search query URLs which are
+// identical or nearly identical to the original search query URL and issued too
+// closely to it, i.e., within this time interval. They are typically recorded
+// as a result of back/forward navigations or user interactions in the search
+// result page and are likely not newly initiated searches.
+extern const base::TimeDelta kAutocompleteDuplicateVisitIntervalThreshold;
+
+// Returns a score combining frequency and recency of the visit favoring ones
+// that are more frequent and more recent (see go/local-zps-frecency-ranking).
+double GetFrecencyScore(int visit_count, base::Time visit_time, base::Time now);
+
+// Returns keyword search terms ordered by descending recency or frecency scores
+// for use as prefix or zero-prefix suggestions in the omnibox respectively.
+// |enumerator| enumerates keyword search term visits from the URLDatabase. It
+// must return visits ordered first by |normalized_term| and then by
+// |last_visit_time| in ascending order, i.e., from the oldest to the newest.
+// |ignore_duplicate_visits| specifies whether duplicative visits to a search
+// term should be ignored. A duplicative visit is defined as a visit to the
+// same search term in an interval smaller than
+// kAutocompleteDuplicateVisitIntervalThreshold. |ranking_policy| specifies
+// how the returned keyword search terms should be ordered.
+void GetAutocompleteSearchTermsFromEnumerator(
+ KeywordSearchTermVisitEnumerator& enumerator,
+ bool ignore_duplicate_visits,
+ SearchTermRankingPolicy ranking_policy,
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>>* search_terms);
+
+// Returns keyword search terms ordered by descending frecency scores
+// accumulated across days for use in the Most Visited tiles. |enumerator|
+// enumerates keyword search term visits from the URLDatabase. It must return
+// visits ordered first by |normalized_term| and then by |last_visit_time| in
+// ascending order, i.e., from the oldest to the newest.
+void GetMostRepeatedSearchTermsFromEnumerator(
+ KeywordSearchTermVisitEnumerator& enumerator,
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>>* search_terms);
+
+} // namespace history
+
+#endif // COMPONENTS_HISTORY_CORE_BROWSER_KEYWORD_SEARCH_TERM_UTIL_H_
diff --git a/chromium/components/history/core/browser/sync/delete_directive_handler.cc b/chromium/components/history/core/browser/sync/delete_directive_handler.cc
index b80438613e0..c038b64d5c1 100644
--- a/chromium/components/history/core/browser/sync/delete_directive_handler.cc
+++ b/chromium/components/history/core/browser/sync/delete_directive_handler.cc
@@ -31,8 +31,9 @@ std::string RandASCIIString(size_t length) {
std::string result;
const int kMin = static_cast<int>(' ');
const int kMax = static_cast<int>('~');
- for (size_t i = 0; i < length; ++i)
+ for (size_t i = 0; i < length; ++i) {
result.push_back(static_cast<char>(base::RandInt(kMin, kMax)));
+ }
return result;
}
@@ -52,10 +53,12 @@ bool TimeRangeLessThan(const syncer::SyncData& data1,
data1.GetSpecifics().history_delete_directive().time_range_directive();
const sync_pb::TimeRangeDirective& range2 =
data2.GetSpecifics().history_delete_directive().time_range_directive();
- if (range1.start_time_usec() < range2.start_time_usec())
+ if (range1.start_time_usec() < range2.start_time_usec()) {
return true;
- if (range1.start_time_usec() > range2.start_time_usec())
+ }
+ if (range1.start_time_usec() > range2.start_time_usec()) {
return false;
+ }
return range1.end_time_usec() < range2.end_time_usec();
}
@@ -91,8 +94,9 @@ void CheckDeleteDirectiveValid(
DCHECK(!delete_directive.has_time_range_directive());
DCHECK(!delete_directive.has_url_directive());
DCHECK_NE(global_id_directive.global_id_size(), 0);
- if (global_id_directive.has_start_time_usec())
+ if (global_id_directive.has_start_time_usec()) {
DCHECK_GE(global_id_directive.start_time_usec(), 0);
+ }
if (global_id_directive.has_end_time_usec()) {
DCHECK_GT(global_id_directive.end_time_usec(), 0);
@@ -143,7 +147,7 @@ class DeleteDirectiveHandler::DeleteDirectiveTask : public HistoryDBTask {
delete_directives_(delete_directive),
post_processing_action_(post_processing_action) {}
- ~DeleteDirectiveTask() override {}
+ ~DeleteDirectiveTask() override = default;
// Implements HistoryDBTask.
bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) override;
@@ -211,8 +215,9 @@ void DeleteDirectiveHandler::DeleteDirectiveTask::
ProcessGlobalIdDeleteDirectives(
HistoryBackend* history_backend,
const syncer::SyncDataList& global_id_directives) {
- if (global_id_directives.empty())
+ if (global_id_directives.empty()) {
return;
+ }
// Group times represented by global IDs by time ranges of delete directives.
// It's more efficient for backend to process all directives with same time
@@ -220,15 +225,13 @@ void DeleteDirectiveHandler::DeleteDirectiveTask::
typedef std::map<std::pair<base::Time, base::Time>, std::set<base::Time>>
GlobalIdTimesGroup;
GlobalIdTimesGroup id_times_group;
- for (size_t i = 0; i < global_id_directives.size(); ++i) {
+ for (const syncer::SyncData& global_id_directive : global_id_directives) {
DVLOG(1) << "Processing delete directive: "
- << DeleteDirectiveToString(global_id_directives[i]
- .GetSpecifics()
+ << DeleteDirectiveToString(global_id_directive.GetSpecifics()
.history_delete_directive());
const sync_pb::GlobalIdDirective& id_directive =
- global_id_directives[i]
- .GetSpecifics()
+ global_id_directive.GetSpecifics()
.history_delete_directive()
.global_id_directive();
if (id_directive.global_id_size() == 0 ||
@@ -243,8 +246,9 @@ void DeleteDirectiveHandler::DeleteDirectiveTask::
UnixUsecToTime(id_directive.end_time_usec()))]);
}
- if (id_times_group.empty())
+ if (id_times_group.empty()) {
return;
+ }
// Call backend to expire history of directives in each group.
for (const auto& [begin_and_end_times, times] : id_times_group) {
@@ -260,16 +264,17 @@ void DeleteDirectiveHandler::DeleteDirectiveTask::
ProcessTimeRangeDeleteDirectives(
HistoryBackend* history_backend,
const syncer::SyncDataList& time_range_directives) {
- if (time_range_directives.empty())
+ if (time_range_directives.empty()) {
return;
+ }
// Iterate through time range directives. Expire history in combined
// time range for multiple directives whose time ranges overlap.
base::Time current_start_time;
base::Time current_end_time;
- for (size_t i = 0; i < time_range_directives.size(); ++i) {
+ for (const syncer::SyncData& data : time_range_directives) {
const sync_pb::HistoryDeleteDirectiveSpecifics& delete_directive =
- time_range_directives[i].GetSpecifics().history_delete_directive();
+ data.GetSpecifics().history_delete_directive();
DVLOG(1) << "Processing time range directive: "
<< DeleteDirectiveToString(delete_directive);
@@ -298,8 +303,9 @@ void DeleteDirectiveHandler::DeleteDirectiveTask::
}
current_start_time = directive_start_time;
}
- if (directive_end_time > current_end_time)
+ if (directive_end_time > current_end_time) {
current_end_time = directive_end_time;
+ }
}
if (!current_start_time.is_null()) {
@@ -322,28 +328,32 @@ void DeleteDirectiveHandler::DeleteDirectiveTask::ProcessUrlDeleteDirectives(
const sync_pb::UrlDirective& url_directive =
delete_directive.url_directive();
- if (!url_directive.has_url() || !url_directive.has_end_time_usec())
+ if (!url_directive.has_url() || !url_directive.has_end_time_usec()) {
continue;
+ }
GURL url(url_directive.url());
base::Time end_time = UnixUsecToTime(url_directive.end_time_usec());
- if (url.is_valid())
+ if (url.is_valid()) {
deletions.emplace_back(url, end_time);
+ }
}
- if (!deletions.empty())
+ if (!deletions.empty()) {
history_backend->DeleteURLsUntil(deletions);
+ }
}
DeleteDirectiveHandler::DeleteDirectiveHandler(
BackendTaskScheduler backend_task_scheduler)
: backend_task_scheduler_(std::move(backend_task_scheduler)) {}
-DeleteDirectiveHandler::~DeleteDirectiveHandler() {}
+DeleteDirectiveHandler::~DeleteDirectiveHandler() = default;
void DeleteDirectiveHandler::OnBackendLoaded() {
backend_loaded_ = true;
- if (wait_until_ready_to_sync_cb_)
+ if (wait_until_ready_to_sync_cb_) {
std::move(wait_until_ready_to_sync_cb_).Run();
+ }
}
bool DeleteDirectiveHandler::CreateTimeRangeDeleteDirective(
@@ -460,10 +470,10 @@ absl::optional<syncer::ModelError> DeleteDirectiveHandler::ProcessSyncChanges(
}
syncer::SyncDataList delete_directives;
- for (auto it = change_list.begin(); it != change_list.end(); ++it) {
- switch (it->change_type()) {
+ for (const syncer::SyncChange& sync_change : change_list) {
+ switch (sync_change.change_type()) {
case syncer::SyncChange::ACTION_ADD:
- delete_directives.push_back(it->sync_data());
+ delete_directives.push_back(sync_change.sync_data());
break;
case syncer::SyncChange::ACTION_DELETE:
// TODO(akalin): Keep track of existing delete directives.
@@ -498,9 +508,9 @@ void DeleteDirectiveHandler::FinishProcessing(
if (sync_processor_.get() &&
post_processing_action == DROP_AFTER_PROCESSING) {
syncer::SyncChangeList change_list;
- for (size_t i = 0; i < delete_directives.size(); ++i) {
+ for (const syncer::SyncData& delete_directive : delete_directives) {
change_list.push_back(syncer::SyncChange(
- FROM_HERE, syncer::SyncChange::ACTION_DELETE, delete_directives[i]));
+ FROM_HERE, syncer::SyncChange::ACTION_DELETE, delete_directive));
}
sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
}
diff --git a/chromium/components/history/core/browser/sync/delete_directive_handler_unittest.cc b/chromium/components/history/core/browser/sync/delete_directive_handler_unittest.cc
index 069dd2f27ee..b4330b20f19 100644
--- a/chromium/components/history/core/browser/sync/delete_directive_handler_unittest.cc
+++ b/chromium/components/history/core/browser/sync/delete_directive_handler_unittest.cc
@@ -38,7 +38,7 @@ base::Time UnixUsecToTime(int64_t usec) {
class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
public:
- TestHistoryBackendDelegate() {}
+ TestHistoryBackendDelegate() = default;
TestHistoryBackendDelegate(const TestHistoryBackendDelegate&) = delete;
TestHistoryBackendDelegate& operator=(const TestHistoryBackendDelegate&) =
@@ -52,7 +52,6 @@ class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
const GURL& icon_url) override {}
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) override {}
void NotifyURLsModified(const URLRows& changed_urls) override {}
void NotifyURLsDeleted(DeletionInfo deletion_info) override {}
diff --git a/chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.cc b/chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.cc
index 0720af2f621..f7c00649ba6 100644
--- a/chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.cc
+++ b/chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.cc
@@ -6,7 +6,6 @@
#include <utility>
-#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "components/history/core/browser/history_service.h"
#include "components/sync/driver/sync_service.h"
@@ -32,23 +31,26 @@ HistoryDeleteDirectivesModelTypeController::
const base::RepeatingClosure& dump_stack,
syncer::SyncService* sync_service,
syncer::ModelTypeStoreService* model_type_store_service,
- HistoryService* history_service)
+ HistoryService* history_service,
+ PrefService* pref_service)
: SyncableServiceBasedModelTypeController(
syncer::HISTORY_DELETE_DIRECTIVES,
model_type_store_service->GetStoreFactory(),
GetSyncableServiceFromHistoryService(history_service),
dump_stack),
+ helper_(syncer::HISTORY_DELETE_DIRECTIVES, sync_service, pref_service),
sync_service_(sync_service) {}
HistoryDeleteDirectivesModelTypeController::
- ~HistoryDeleteDirectivesModelTypeController() {}
+ ~HistoryDeleteDirectivesModelTypeController() = default;
syncer::DataTypeController::PreconditionState
HistoryDeleteDirectivesModelTypeController::GetPreconditionState() const {
DCHECK(CalledOnValidThread());
- return sync_service_->GetUserSettings()->IsEncryptEverythingEnabled()
- ? PreconditionState::kMustStopAndClearData
- : PreconditionState::kPreconditionsMet;
+ if (sync_service_->GetUserSettings()->IsEncryptEverythingEnabled()) {
+ return PreconditionState::kMustStopAndClearData;
+ }
+ return helper_.GetPreconditionState();
}
void HistoryDeleteDirectivesModelTypeController::LoadModels(
diff --git a/chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.h b/chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.h
index 16c1a7c084a..ebd60c594a7 100644
--- a/chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.h
+++ b/chromium/components/history/core/browser/sync/history_delete_directives_model_type_controller.h
@@ -5,11 +5,13 @@
#ifndef COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_DELETE_DIRECTIVES_MODEL_TYPE_CONTROLLER_H_
#define COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_DELETE_DIRECTIVES_MODEL_TYPE_CONTROLLER_H_
-#include "base/callback_forward.h"
#include "base/memory/raw_ptr.h"
+#include "components/history/core/browser/sync/history_model_type_controller_helper.h"
#include "components/sync/driver/sync_service_observer.h"
#include "components/sync/driver/syncable_service_based_model_type_controller.h"
+class PrefService;
+
namespace syncer {
class ModelTypeStoreService;
class SyncService;
@@ -25,13 +27,14 @@ class HistoryDeleteDirectivesModelTypeController
: public syncer::SyncableServiceBasedModelTypeController,
public syncer::SyncServiceObserver {
public:
- // `sync_service` and `history_service` must not be null and must outlive this
- // object.
+ // `sync_service`, `history_service`, and `pref_service` must not be null and
+ // must outlive this object.
HistoryDeleteDirectivesModelTypeController(
const base::RepeatingClosure& dump_stack,
syncer::SyncService* sync_service,
syncer::ModelTypeStoreService* model_type_store_service,
- HistoryService* history_service);
+ HistoryService* history_service,
+ PrefService* pref_service);
HistoryDeleteDirectivesModelTypeController(
const HistoryDeleteDirectivesModelTypeController&) = delete;
@@ -51,6 +54,8 @@ class HistoryDeleteDirectivesModelTypeController
void OnStateChanged(syncer::SyncService* sync) override;
private:
+ history::HistoryModelTypeControllerHelper helper_;
+
const raw_ptr<syncer::SyncService> sync_service_;
};
diff --git a/chromium/components/history/core/browser/sync/history_model_type_controller_helper.cc b/chromium/components/history/core/browser/sync/history_model_type_controller_helper.cc
new file mode 100644
index 00000000000..a66fee8152d
--- /dev/null
+++ b/chromium/components/history/core/browser/sync/history_model_type_controller_helper.cc
@@ -0,0 +1,44 @@
+// Copyright 2022 The Chromium 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/history/core/browser/sync/history_model_type_controller_helper.h"
+
+#include "base/bind.h"
+#include "components/history/core/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/driver/sync_service.h"
+
+namespace history {
+
+HistoryModelTypeControllerHelper::HistoryModelTypeControllerHelper(
+ syncer::ModelType model_type,
+ syncer::SyncService* sync_service,
+ PrefService* pref_service)
+ : model_type_(model_type),
+ sync_service_(sync_service),
+ pref_service_(pref_service) {
+ pref_registrar_.Init(pref_service_);
+ // base::Unretained() is safe because `pref_registar_` is owned by `this`.
+ pref_registrar_.Add(
+ prefs::kSavingBrowserHistoryDisabled,
+ base::BindRepeating(&HistoryModelTypeControllerHelper::
+ OnSavingBrowserHistoryDisabledChanged,
+ base::Unretained(this)));
+}
+
+HistoryModelTypeControllerHelper::~HistoryModelTypeControllerHelper() = default;
+
+syncer::DataTypeController::PreconditionState
+HistoryModelTypeControllerHelper::GetPreconditionState() const {
+ return pref_service_->GetBoolean(prefs::kSavingBrowserHistoryDisabled)
+ ? syncer::DataTypeController::PreconditionState::
+ kMustStopAndClearData
+ : syncer::DataTypeController::PreconditionState::kPreconditionsMet;
+}
+
+void HistoryModelTypeControllerHelper::OnSavingBrowserHistoryDisabledChanged() {
+ sync_service_->DataTypePreconditionChanged(model_type_);
+}
+
+} // namespace history
diff --git a/chromium/components/history/core/browser/sync/history_model_type_controller_helper.h b/chromium/components/history/core/browser/sync/history_model_type_controller_helper.h
new file mode 100644
index 00000000000..5ad8c181eb9
--- /dev/null
+++ b/chromium/components/history/core/browser/sync/history_model_type_controller_helper.h
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_MODEL_TYPE_CONTROLLER_HELPER_H_
+#define COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_MODEL_TYPE_CONTROLLER_HELPER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/data_type_controller.h"
+
+class PrefService;
+
+namespace syncer {
+class SyncService;
+} // namespace syncer
+
+namespace history {
+
+// Helper class for implementing history-related ModelTypeControllers. It
+// implements the pref/policy kSavingBrowserHistoryDisabled: It calls
+// SyncService::DataTypePreconditionChanged() when the pref changes.
+// DataTypeControllers using this helper must call its GetPreconditionState().
+class HistoryModelTypeControllerHelper {
+ public:
+ HistoryModelTypeControllerHelper(syncer::ModelType model_type,
+ syncer::SyncService* sync_service,
+ PrefService* pref_service);
+
+ HistoryModelTypeControllerHelper(const HistoryModelTypeControllerHelper&) =
+ delete;
+ HistoryModelTypeControllerHelper& operator=(
+ const HistoryModelTypeControllerHelper&) = delete;
+
+ ~HistoryModelTypeControllerHelper();
+
+ // Must be called from DataTypeController::GetPreconditionState().
+ syncer::DataTypeController::PreconditionState GetPreconditionState() const;
+
+ private:
+ void OnSavingBrowserHistoryDisabledChanged();
+
+ const syncer::ModelType model_type_;
+ const raw_ptr<syncer::SyncService> sync_service_;
+ const raw_ptr<PrefService> pref_service_;
+
+ PrefChangeRegistrar pref_registrar_;
+};
+
+} // namespace history
+
+#endif // COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_MODEL_TYPE_CONTROLLER_HELPER_H_
diff --git a/chromium/components/history/core/browser/sync/history_sync_bridge.cc b/chromium/components/history/core/browser/sync/history_sync_bridge.cc
new file mode 100644
index 00000000000..f46d520c7b1
--- /dev/null
+++ b/chromium/components/history/core/browser/sync/history_sync_bridge.cc
@@ -0,0 +1,87 @@
+// Copyright 2022 The Chromium 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/history/core/browser/sync/history_sync_bridge.h"
+
+#include "components/sync/model/metadata_change_list.h"
+
+namespace history {
+
+HistorySyncBridge::HistorySyncBridge(
+ HistoryBackend* history_backend,
+ HistorySyncMetadataDatabase* sync_metadata_database,
+ std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
+ : ModelTypeSyncBridge(std::move(change_processor)) {
+ NOTIMPLEMENTED();
+}
+
+HistorySyncBridge::~HistorySyncBridge() = default;
+
+std::unique_ptr<syncer::MetadataChangeList>
+HistorySyncBridge::CreateMetadataChangeList() {
+ NOTIMPLEMENTED();
+ return {};
+}
+
+absl::optional<syncer::ModelError> HistorySyncBridge::MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_data) {
+ NOTIMPLEMENTED();
+ return {};
+}
+
+absl::optional<syncer::ModelError> HistorySyncBridge::ApplySyncChanges(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) {
+ NOTIMPLEMENTED();
+ return {};
+}
+
+void HistorySyncBridge::GetData(StorageKeyList storage_keys,
+ DataCallback callback) {
+ NOTIMPLEMENTED();
+}
+
+void HistorySyncBridge::GetAllDataForDebugging(DataCallback callback) {
+ NOTIMPLEMENTED();
+}
+
+std::string HistorySyncBridge::GetClientTag(
+ const syncer::EntityData& entity_data) {
+ NOTIMPLEMENTED();
+ return {};
+}
+
+std::string HistorySyncBridge::GetStorageKey(
+ const syncer::EntityData& entity_data) {
+ NOTIMPLEMENTED();
+ return {};
+}
+
+void HistorySyncBridge::OnURLVisited(HistoryBackend* history_backend,
+ ui::PageTransition transition,
+ const URLRow& row,
+ base::Time visit_time) {
+ NOTIMPLEMENTED();
+}
+
+void HistorySyncBridge::OnURLsModified(HistoryBackend* history_backend,
+ const URLRows& changed_urls,
+ bool is_from_expiration) {
+ NOTIMPLEMENTED();
+}
+
+void HistorySyncBridge::OnURLsDeleted(HistoryBackend* history_backend,
+ bool all_history,
+ bool expired,
+ const URLRows& deleted_rows,
+ const std::set<GURL>& favicon_urls) {
+ NOTIMPLEMENTED();
+}
+
+void HistorySyncBridge::OnDatabaseError() {
+ NOTIMPLEMENTED();
+}
+
+} // namespace history
diff --git a/chromium/components/history/core/browser/sync/history_sync_bridge.h b/chromium/components/history/core/browser/sync/history_sync_bridge.h
new file mode 100644
index 00000000000..b59898d290c
--- /dev/null
+++ b/chromium/components/history/core/browser/sync/history_sync_bridge.h
@@ -0,0 +1,75 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_SYNC_BRIDGE_H_
+#define COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_SYNC_BRIDGE_H_
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "components/history/core/browser/history_backend.h"
+#include "components/history/core/browser/history_backend_observer.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+
+namespace syncer {
+class MetadataChangeList;
+class ModelTypeChangeProcessor;
+} // namespace syncer
+
+namespace history {
+
+class HistorySyncMetadataDatabase;
+
+class HistorySyncBridge : public syncer::ModelTypeSyncBridge,
+ public HistoryBackendObserver {
+ public:
+ // `sync_metadata_store` is owned by `history_backend`, and must outlive
+ // HistorySyncBridge.
+ HistorySyncBridge(
+ HistoryBackend* history_backend,
+ HistorySyncMetadataDatabase* sync_metadata_store,
+ std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor);
+
+ HistorySyncBridge(const HistorySyncBridge&) = delete;
+ HistorySyncBridge& operator=(const HistorySyncBridge&) = delete;
+
+ ~HistorySyncBridge() override;
+
+ // syncer::ModelTypeSyncBridge implementation.
+ std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+ override;
+ absl::optional<syncer::ModelError> MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_data) override;
+ absl::optional<syncer::ModelError> ApplySyncChanges(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) override;
+ void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+ void GetAllDataForDebugging(DataCallback callback) override;
+ std::string GetClientTag(const syncer::EntityData& entity_data) override;
+ std::string GetStorageKey(const syncer::EntityData& entity_data) override;
+
+ // HistoryBackendObserver:
+ void OnURLVisited(HistoryBackend* history_backend,
+ ui::PageTransition transition,
+ const URLRow& row,
+ base::Time visit_time) override;
+ void OnURLsModified(HistoryBackend* history_backend,
+ const URLRows& changed_urls,
+ bool is_from_expiration) override;
+ void OnURLsDeleted(HistoryBackend* history_backend,
+ bool all_history,
+ bool expired,
+ const URLRows& deleted_rows,
+ const std::set<GURL>& favicon_urls) override;
+
+ // Called by HistoryBackend when database error is reported through
+ // DatabaseErrorCallback.
+ void OnDatabaseError();
+};
+
+} // namespace history
+
+#endif // COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_SYNC_BRIDGE_H_
diff --git a/chromium/components/history/core/browser/sync/history_sync_metadata_database.cc b/chromium/components/history/core/browser/sync/history_sync_metadata_database.cc
new file mode 100644
index 00000000000..1bc4f3b9b6d
--- /dev/null
+++ b/chromium/components/history/core/browser/sync/history_sync_metadata_database.cc
@@ -0,0 +1,188 @@
+// Copyright 2022 The Chromium 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/history/core/browser/sync/history_sync_metadata_database.h"
+
+#include <memory>
+
+#include "base/big_endian.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/protocol/entity_metadata.pb.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+
+namespace history {
+
+namespace {
+
+// Key in sql::MetaTable, the value is a serialization of syner::ModelTypeState,
+// which tracks the overall sync state of the history datatype.
+const char kHistoryModelTypeStateKey[] = "history_model_type_state";
+
+} // namespace
+
+// Description of database table:
+//
+// history_sync_metadata
+// storage_key The visit_time of an entry in the visits table (in
+// microseconds since the windows epoch, serialized into a
+// string in big-endian order), used to look up native data
+// with sync metadata records.
+// value Serialized sync EntityMetadata, which tracks the sync
+// state of each history entity.
+
+HistorySyncMetadataDatabase::HistorySyncMetadataDatabase(
+ sql::Database* db,
+ sql::MetaTable* meta_table)
+ : db_(db), meta_table_(meta_table) {}
+
+HistorySyncMetadataDatabase::~HistorySyncMetadataDatabase() = default;
+
+bool HistorySyncMetadataDatabase::Init() {
+ if (!db_->DoesTableExist("history_sync_metadata")) {
+ if (!db_->Execute(
+ "CREATE TABLE history_sync_metadata "
+ "(storage_key INTEGER PRIMARY KEY NOT NULL, value BLOB)")) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool HistorySyncMetadataDatabase::GetAllSyncMetadata(
+ syncer::MetadataBatch* metadata_batch) {
+ DCHECK(metadata_batch);
+ if (!GetAllEntityMetadata(metadata_batch)) {
+ return false;
+ }
+
+ sync_pb::ModelTypeState model_type_state;
+ if (!GetModelTypeState(&model_type_state)) {
+ return false;
+ }
+
+ metadata_batch->SetModelTypeState(model_type_state);
+ return true;
+}
+
+bool HistorySyncMetadataDatabase::UpdateSyncMetadata(
+ syncer::ModelType model_type,
+ const std::string& storage_key,
+ const sync_pb::EntityMetadata& metadata) {
+ DCHECK_EQ(model_type, syncer::HISTORY)
+ << "Only the HISTORY model type is supported";
+ DCHECK(!storage_key.empty());
+
+ sql::Statement s(
+ db_->GetUniqueStatement("INSERT OR REPLACE INTO history_sync_metadata "
+ "(storage_key, value) VALUES(?, ?)"));
+ s.BindInt64(0, StorageKeyToMicrosSinceWindowsEpoch(storage_key));
+ s.BindString(1, metadata.SerializeAsString());
+
+ return s.Run();
+}
+
+bool HistorySyncMetadataDatabase::ClearSyncMetadata(
+ syncer::ModelType model_type,
+ const std::string& storage_key) {
+ DCHECK_EQ(model_type, syncer::HISTORY)
+ << "Only the HISTORY model type is supported";
+ DCHECK(!storage_key.empty());
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM history_sync_metadata WHERE storage_key=?"));
+ s.BindInt64(0, StorageKeyToMicrosSinceWindowsEpoch(storage_key));
+
+ return s.Run();
+}
+
+bool HistorySyncMetadataDatabase::UpdateModelTypeState(
+ syncer::ModelType model_type,
+ const sync_pb::ModelTypeState& model_type_state) {
+ DCHECK_EQ(model_type, syncer::HISTORY)
+ << "Only the HISTORY model type is supported";
+ DCHECK_GT(meta_table_->GetVersionNumber(), 0);
+
+ std::string serialized_state = model_type_state.SerializeAsString();
+ return meta_table_->SetValue(kHistoryModelTypeStateKey, serialized_state);
+}
+
+bool HistorySyncMetadataDatabase::ClearModelTypeState(
+ syncer::ModelType model_type) {
+ DCHECK_EQ(model_type, syncer::HISTORY)
+ << "Only the HISTORY model type is supported";
+ DCHECK_GT(meta_table_->GetVersionNumber(), 0);
+ return meta_table_->DeleteKey(kHistoryModelTypeStateKey);
+}
+
+// static
+uint64_t HistorySyncMetadataDatabase::StorageKeyToMicrosSinceWindowsEpoch(
+ const std::string& storage_key) {
+ uint64_t microseconds_since_windows_epoch = 0;
+ DCHECK_EQ(storage_key.size(), sizeof(microseconds_since_windows_epoch));
+ base::ReadBigEndian(reinterpret_cast<const uint8_t*>(storage_key.data()),
+ &microseconds_since_windows_epoch);
+ // Make sure microseconds_since_windows_epoch is set.
+ DCHECK_NE(microseconds_since_windows_epoch, 0u);
+ return microseconds_since_windows_epoch;
+}
+
+// static
+std::string HistorySyncMetadataDatabase::StorageKeyFromMicrosSinceWindowsEpoch(
+ uint64_t micros) {
+ std::string storage_key(sizeof(uint64_t), 0);
+ base::WriteBigEndian<uint64_t>(storage_key.data(), micros);
+ return storage_key;
+}
+
+// static
+base::Time HistorySyncMetadataDatabase::StorageKeyToVisitTime(
+ const std::string& storage_key) {
+ return base::Time::FromDeltaSinceWindowsEpoch(
+ base::Microseconds(StorageKeyToMicrosSinceWindowsEpoch(storage_key)));
+}
+
+// static
+std::string HistorySyncMetadataDatabase::StorageKeyFromVisitTime(
+ base::Time visit_time) {
+ return StorageKeyFromMicrosSinceWindowsEpoch(
+ visit_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+}
+
+bool HistorySyncMetadataDatabase::GetAllEntityMetadata(
+ syncer::MetadataBatch* metadata_batch) {
+ DCHECK(metadata_batch);
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT storage_key, value FROM history_sync_metadata"));
+
+ while (s.Step()) {
+ std::string storage_key =
+ StorageKeyFromMicrosSinceWindowsEpoch(s.ColumnInt64(0));
+ std::string serialized_metadata = s.ColumnString(1);
+ auto entity_metadata = std::make_unique<sync_pb::EntityMetadata>();
+ if (!entity_metadata->ParseFromString(serialized_metadata)) {
+ DLOG(WARNING) << "Failed to deserialize HISTORY model type "
+ "sync_pb::EntityMetadata.";
+ return false;
+ }
+ metadata_batch->AddMetadata(storage_key, std::move(entity_metadata));
+ }
+ return true;
+}
+
+bool HistorySyncMetadataDatabase::GetModelTypeState(
+ sync_pb::ModelTypeState* state) {
+ DCHECK_GT(meta_table_->GetVersionNumber(), 0);
+ std::string serialized_state;
+ if (!meta_table_->GetValue(kHistoryModelTypeStateKey, &serialized_state)) {
+ *state = sync_pb::ModelTypeState();
+ return true;
+ }
+
+ return state->ParseFromString(serialized_state);
+}
+
+} // namespace history
diff --git a/chromium/components/history/core/browser/sync/history_sync_metadata_database.h b/chromium/components/history/core/browser/sync/history_sync_metadata_database.h
new file mode 100644
index 00000000000..2cb01802e3e
--- /dev/null
+++ b/chromium/components/history/core/browser/sync/history_sync_metadata_database.h
@@ -0,0 +1,86 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_SYNC_METADATA_DATABASE_H_
+#define COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_SYNC_METADATA_DATABASE_H_
+
+#include <string>
+
+#include "base/memory/raw_ptr.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/model/sync_metadata_store.h"
+
+namespace base {
+class Time;
+}
+
+namespace sql {
+class Database;
+class MetaTable;
+} // namespace sql
+
+namespace syncer {
+class MetadataBatch;
+}
+
+namespace history {
+
+// A sync metadata database maintains two things: the entity metadata table and
+// the datatype state, stored in the MetaTable. The entity metadata table
+// contains metadata (sync states) for each entity. The datatype state is the
+// overall state of the history sync datatype.
+class HistorySyncMetadataDatabase : public syncer::SyncMetadataStore {
+ public:
+ // After construction, Init() must be called before doing anything else to
+ // make sure the database is initialized.
+ HistorySyncMetadataDatabase(sql::Database* db, sql::MetaTable* meta_table);
+
+ HistorySyncMetadataDatabase(const HistorySyncMetadataDatabase&) = delete;
+ HistorySyncMetadataDatabase& operator=(const HistorySyncMetadataDatabase&) =
+ delete;
+
+ ~HistorySyncMetadataDatabase() override;
+
+ // Makes sure the tables and indices are properly set up. Must be called
+ // before anything else.
+ bool Init();
+
+ // Reads all stored metadata for History and fills `metadata_batch` with it.
+ bool GetAllSyncMetadata(syncer::MetadataBatch* metadata_batch);
+
+ // syncer::SyncMetadataStore implementation.
+ bool UpdateSyncMetadata(syncer::ModelType model_type,
+ const std::string& storage_key,
+ const sync_pb::EntityMetadata& metadata) override;
+ bool ClearSyncMetadata(syncer::ModelType model_type,
+ const std::string& storage_key) override;
+ bool UpdateModelTypeState(
+ syncer::ModelType model_type,
+ const sync_pb::ModelTypeState& model_type_state) override;
+ bool ClearModelTypeState(syncer::ModelType model_type) override;
+
+ // Conversion between timestamps and storage keys, exposed so that the bridge
+ // (and tests) can access this.
+ static uint64_t StorageKeyToMicrosSinceWindowsEpoch(
+ const std::string& storage_key);
+ static std::string StorageKeyFromMicrosSinceWindowsEpoch(uint64_t micros);
+
+ static base::Time StorageKeyToVisitTime(const std::string& storage_key);
+ static std::string StorageKeyFromVisitTime(base::Time visit_time);
+
+ private:
+ // Reads all sync_pb::EntityMetadata for History and fills `metadata_batch`
+ // with it.
+ bool GetAllEntityMetadata(syncer::MetadataBatch* metadata_batch);
+
+ // Reads sync_pb::ModelTypeState for History and fills `state` with it.
+ bool GetModelTypeState(sync_pb::ModelTypeState* state);
+
+ const raw_ptr<sql::Database> db_;
+ const raw_ptr<sql::MetaTable> meta_table_;
+};
+
+} // namespace history
+
+#endif // COMPONENTS_HISTORY_CORE_BROWSER_SYNC_HISTORY_SYNC_METADATA_DATABASE_H_
diff --git a/chromium/components/history/core/browser/sync/history_sync_metadata_database_unittest.cc b/chromium/components/history/core/browser/sync/history_sync_metadata_database_unittest.cc
new file mode 100644
index 00000000000..527b86ae490
--- /dev/null
+++ b/chromium/components/history/core/browser/sync/history_sync_metadata_database_unittest.cc
@@ -0,0 +1,207 @@
+// Copyright 2022 The Chromium 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/history/core/browser/sync/history_sync_metadata_database.h"
+
+#include "base/big_endian.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "components/history/core/browser/url_row.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/protocol/entity_metadata.pb.h"
+#include "components/sync/protocol/model_type_state.pb.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sync_pb::EntityMetadata;
+using sync_pb::ModelTypeState;
+using syncer::EntityMetadataMap;
+using syncer::MetadataBatch;
+
+namespace history {
+
+namespace {
+
+// Some arbitrary timestamps (which happen to map to 20 May 2022).
+constexpr base::Time kVisitTime1 = base::Time::FromDeltaSinceWindowsEpoch(
+ base::Microseconds(13297523045341512ull));
+constexpr base::Time kVisitTime2 = base::Time::FromDeltaSinceWindowsEpoch(
+ base::Microseconds(13297523047664774ull));
+
+class HistorySyncMetadataDatabaseTest : public testing::Test {
+ public:
+ HistorySyncMetadataDatabaseTest() : metadata_db_(&db_, &meta_table_) {}
+
+ HistorySyncMetadataDatabaseTest(const HistorySyncMetadataDatabaseTest&) =
+ delete;
+ HistorySyncMetadataDatabaseTest& operator=(
+ const HistorySyncMetadataDatabaseTest&) = delete;
+
+ ~HistorySyncMetadataDatabaseTest() override = default;
+
+ HistorySyncMetadataDatabase* metadata_db() { return &metadata_db_; }
+
+ sql::Database* sql_db() { return &db_; }
+ sql::MetaTable* sql_meta_table() { return &meta_table_; }
+
+ protected:
+ void SetUp() override {
+ EXPECT_TRUE(db_.OpenInMemory());
+ metadata_db_.Init();
+ meta_table_.Init(&db_, 1, 1);
+ }
+ void TearDown() override { db_.Close(); }
+
+ private:
+ sql::Database db_;
+ sql::MetaTable meta_table_;
+
+ HistorySyncMetadataDatabase metadata_db_;
+};
+
+TEST_F(HistorySyncMetadataDatabaseTest,
+ ConvertsBetweenStorageKeysAndTimestamps) {
+ ASSERT_NE(kVisitTime1, kVisitTime2);
+
+ const std::string storage_key1 =
+ HistorySyncMetadataDatabase::StorageKeyFromVisitTime(kVisitTime1);
+ const std::string storage_key2 =
+ HistorySyncMetadataDatabase::StorageKeyFromVisitTime(kVisitTime2);
+
+ // Different timestamps should result in different storage keys.
+ EXPECT_NE(storage_key1, storage_key2);
+
+ // StorageKeyFromMicros and StorageKeyFromVisitTime should be equivalent.
+ EXPECT_EQ(storage_key1,
+ HistorySyncMetadataDatabase::StorageKeyFromMicrosSinceWindowsEpoch(
+ kVisitTime1.ToDeltaSinceWindowsEpoch().InMicroseconds()));
+ EXPECT_EQ(storage_key2,
+ HistorySyncMetadataDatabase::StorageKeyFromMicrosSinceWindowsEpoch(
+ kVisitTime2.ToDeltaSinceWindowsEpoch().InMicroseconds()));
+
+ // Conversion from storage key back to base::Time should be lossless.
+ EXPECT_EQ(kVisitTime1,
+ HistorySyncMetadataDatabase::StorageKeyToVisitTime(storage_key1));
+ EXPECT_EQ(kVisitTime2,
+ HistorySyncMetadataDatabase::StorageKeyToVisitTime(storage_key2));
+}
+
+TEST_F(HistorySyncMetadataDatabaseTest, EmptyStateIsValid) {
+ MetadataBatch metadata_batch;
+ EXPECT_TRUE(metadata_db()->GetAllSyncMetadata(&metadata_batch));
+ EXPECT_EQ(0u, metadata_batch.TakeAllMetadata().size());
+ EXPECT_EQ(ModelTypeState().SerializeAsString(),
+ metadata_batch.GetModelTypeState().SerializeAsString());
+}
+
+TEST_F(HistorySyncMetadataDatabaseTest, StoresAndReturnsMetadata) {
+ const std::string storage_key1 =
+ HistorySyncMetadataDatabase::StorageKeyFromVisitTime(kVisitTime1);
+ const std::string storage_key2 =
+ HistorySyncMetadataDatabase::StorageKeyFromVisitTime(kVisitTime2);
+
+ // Store some data - both entity metadata and model type state.
+ EntityMetadata metadata1;
+ metadata1.set_sequence_number(1);
+ metadata1.set_client_tag_hash("client_hash1");
+ ASSERT_TRUE(metadata_db()->UpdateSyncMetadata(syncer::HISTORY, storage_key1,
+ metadata1));
+
+ ModelTypeState model_type_state;
+ model_type_state.set_initial_sync_done(true);
+ ASSERT_TRUE(
+ metadata_db()->UpdateModelTypeState(syncer::HISTORY, model_type_state));
+
+ EntityMetadata metadata2;
+ metadata2.set_sequence_number(2);
+ metadata2.set_client_tag_hash("client_hash2");
+ ASSERT_TRUE(metadata_db()->UpdateSyncMetadata(syncer::HISTORY, storage_key2,
+ metadata2));
+
+ // Read the metadata and make sure it matches what we wrote.
+ MetadataBatch metadata_batch;
+ EXPECT_TRUE(metadata_db()->GetAllSyncMetadata(&metadata_batch));
+
+ EXPECT_TRUE(metadata_batch.GetModelTypeState().initial_sync_done());
+
+ EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata();
+ EXPECT_EQ(metadata_records.size(), 2u);
+ EXPECT_EQ(metadata_records[storage_key1]->sequence_number(), 1);
+ EXPECT_EQ(metadata_records[storage_key1]->client_tag_hash(), "client_hash1");
+ EXPECT_EQ(metadata_records[storage_key2]->sequence_number(), 2);
+ EXPECT_EQ(metadata_records[storage_key2]->client_tag_hash(), "client_hash2");
+
+ // Now check that an entity update and a model type state update replace the
+ // old values.
+ metadata1.set_sequence_number(2);
+ ASSERT_TRUE(metadata_db()->UpdateSyncMetadata(syncer::HISTORY, storage_key1,
+ metadata1));
+ model_type_state.set_initial_sync_done(false);
+ ASSERT_TRUE(
+ metadata_db()->UpdateModelTypeState(syncer::HISTORY, model_type_state));
+
+ MetadataBatch metadata_batch2;
+ ASSERT_TRUE(metadata_db()->GetAllSyncMetadata(&metadata_batch2));
+ EXPECT_FALSE(metadata_batch2.GetModelTypeState().initial_sync_done());
+
+ EntityMetadataMap metadata_records2 = metadata_batch2.TakeAllMetadata();
+ EXPECT_EQ(metadata_records2.size(), 2u);
+ EXPECT_EQ(metadata_records2[storage_key1]->sequence_number(), 2);
+}
+
+TEST_F(HistorySyncMetadataDatabaseTest, DeletesSyncMetadata) {
+ const std::string storage_key =
+ HistorySyncMetadataDatabase::StorageKeyFromVisitTime(kVisitTime1);
+
+ // Write some data into the store.
+ ModelTypeState model_type_state;
+ model_type_state.set_initial_sync_done(true);
+ ASSERT_TRUE(
+ metadata_db()->UpdateModelTypeState(syncer::HISTORY, model_type_state));
+
+ EntityMetadata metadata;
+ metadata.set_client_tag_hash("client_hash");
+ ASSERT_TRUE(metadata_db()->UpdateSyncMetadata(syncer::HISTORY, storage_key,
+ metadata));
+
+ // Delete the data we just wrote.
+ ASSERT_TRUE(metadata_db()->ClearSyncMetadata(syncer::HISTORY, storage_key));
+
+ // It shouldn't be there anymore.
+ MetadataBatch metadata_batch;
+ ASSERT_TRUE(metadata_db()->GetAllSyncMetadata(&metadata_batch));
+ EXPECT_EQ(metadata_batch.GetAllMetadata().size(), 0u);
+
+ // Now delete the model type state and make sure it's gone.
+ ASSERT_NE(ModelTypeState().SerializeAsString(),
+ metadata_batch.GetModelTypeState().SerializeAsString());
+ ASSERT_TRUE(metadata_db()->ClearModelTypeState(syncer::HISTORY));
+ ASSERT_TRUE(metadata_db()->GetAllSyncMetadata(&metadata_batch));
+ EXPECT_EQ(ModelTypeState().SerializeAsString(),
+ metadata_batch.GetModelTypeState().SerializeAsString());
+}
+
+TEST_F(HistorySyncMetadataDatabaseTest, FailsToReadCorruptSyncMetadata) {
+ // Manually insert some corrupt data into the underlying sql DB.
+ sql::Statement s(sql_db()->GetUniqueStatement(
+ "INSERT OR REPLACE INTO history_sync_metadata (storage_key, value) "
+ "VALUES(1, 'unparseable')"));
+ ASSERT_TRUE(s.Run());
+
+ MetadataBatch metadata_batch;
+ EXPECT_FALSE(metadata_db()->GetAllSyncMetadata(&metadata_batch));
+}
+
+TEST_F(HistorySyncMetadataDatabaseTest, FailsToReadCorruptModelTypeState) {
+ // Insert some corrupt data into the meta table.
+ sql_meta_table()->SetValue("history_model_type_state", "unparseable");
+
+ MetadataBatch metadata_batch;
+ EXPECT_FALSE(metadata_db()->GetAllSyncMetadata(&metadata_batch));
+}
+
+} // namespace
+
+} // namespace history
diff --git a/chromium/components/history/core/browser/sync/typed_url_model_type_controller.cc b/chromium/components/history/core/browser/sync/typed_url_model_type_controller.cc
index 91b54621263..340359472a9 100644
--- a/chromium/components/history/core/browser/sync/typed_url_model_type_controller.cc
+++ b/chromium/components/history/core/browser/sync/typed_url_model_type_controller.cc
@@ -8,64 +8,56 @@
#include "base/bind.h"
#include "components/history/core/browser/history_service.h"
-#include "components/history/core/common/pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "components/sync/driver/model_type_controller.h"
-#include "components/sync/driver/sync_client.h"
-
-using syncer::SyncClient;
+#include "components/sync/base/features.h"
namespace history {
namespace {
std::unique_ptr<syncer::ModelTypeControllerDelegate>
-GetDelegateFromHistoryService(HistoryService* history_service) {
- if (history_service) {
+GetDelegateFromHistoryService(syncer::ModelType model_type,
+ HistoryService* history_service) {
+ if (!history_service) {
+ return nullptr;
+ }
+
+ if (model_type == syncer::TYPED_URLS) {
return history_service->GetTypedURLSyncControllerDelegate();
}
- return nullptr;
+ DCHECK_EQ(model_type, syncer::HISTORY);
+ return history_service->GetHistorySyncControllerDelegate();
}
} // namespace
TypedURLModelTypeController::TypedURLModelTypeController(
+ syncer::ModelType model_type,
+ syncer::SyncService* sync_service,
HistoryService* history_service,
PrefService* pref_service)
- : ModelTypeController(syncer::TYPED_URLS,
- GetDelegateFromHistoryService(history_service)),
- history_service_(history_service),
- pref_service_(pref_service) {
- pref_registrar_.Init(pref_service_);
- pref_registrar_.Add(
- prefs::kSavingBrowserHistoryDisabled,
- base::BindRepeating(
- &TypedURLModelTypeController::OnSavingBrowserHistoryDisabledChanged,
- base::AsWeakPtr(this)));
+ : ModelTypeController(
+ model_type,
+ GetDelegateFromHistoryService(model_type, history_service)),
+ helper_(model_type, sync_service, pref_service) {
+ DCHECK(model_type == syncer::TYPED_URLS || model_type == syncer::HISTORY);
+ DCHECK(model_type == syncer::TYPED_URLS ||
+ base::FeatureList::IsEnabled(syncer::kSyncEnableHistoryDataType));
}
-TypedURLModelTypeController::~TypedURLModelTypeController() {}
+TypedURLModelTypeController::~TypedURLModelTypeController() = default;
syncer::DataTypeController::PreconditionState
TypedURLModelTypeController::GetPreconditionState() const {
- return (history_service_ &&
- !pref_service_->GetBoolean(prefs::kSavingBrowserHistoryDisabled))
- ? PreconditionState::kPreconditionsMet
- : PreconditionState::kMustStopAndClearData;
-}
-
-void TypedURLModelTypeController::OnSavingBrowserHistoryDisabledChanged() {
- if (pref_service_->GetBoolean(prefs::kSavingBrowserHistoryDisabled)) {
- // We've turned off history persistence, so if we are running,
- // generate an unrecoverable error. This can be fixed by restarting
- // Chrome (on restart, typed urls will not be a registered type).
- // TODO(crbug.com/990802): Adopt DataTypePreconditionChanged().
- if (state() != NOT_RUNNING && state() != STOPPING) {
- ReportModelError(
- syncer::SyncError::DATATYPE_POLICY_ERROR,
- {FROM_HERE, "History saving is now disabled by policy."});
+ if (base::FeatureList::IsEnabled(syncer::kSyncEnableHistoryDataType)) {
+ // If the feature flag is enabled, syncer::HISTORY replaces
+ // syncer::TYPED_URLS.
+ // TODO(crbug.com/1318028): Consider whether this is the best way to go
+ // about things - maybe we'll want to keep the TypedURLs (meta)data for now?
+ if (type() == syncer::TYPED_URLS) {
+ return PreconditionState::kMustStopAndClearData;
}
}
+ return helper_.GetPreconditionState();
}
} // namespace history
diff --git a/chromium/components/history/core/browser/sync/typed_url_model_type_controller.h b/chromium/components/history/core/browser/sync/typed_url_model_type_controller.h
index a105afa4744..90fdcf5e651 100644
--- a/chromium/components/history/core/browser/sync/typed_url_model_type_controller.h
+++ b/chromium/components/history/core/browser/sync/typed_url_model_type_controller.h
@@ -5,19 +5,27 @@
#ifndef COMPONENTS_HISTORY_CORE_BROWSER_SYNC_TYPED_URL_MODEL_TYPE_CONTROLLER_H_
#define COMPONENTS_HISTORY_CORE_BROWSER_SYNC_TYPED_URL_MODEL_TYPE_CONTROLLER_H_
-#include "base/memory/raw_ptr.h"
-#include "components/prefs/pref_change_registrar.h"
+#include "components/history/core/browser/sync/history_model_type_controller_helper.h"
+#include "components/sync/base/model_type.h"
#include "components/sync/driver/model_type_controller.h"
class PrefService;
+namespace syncer {
+class SyncService;
+} // namespace syncer
+
namespace history {
class HistoryService;
+// TODO(crbug.com/1318028): Rename to HistoryModelTypeController.
class TypedURLModelTypeController : public syncer::ModelTypeController {
public:
- TypedURLModelTypeController(HistoryService* history_service,
+ // `model_type` must be either HISTORY or TYPED_URLS.
+ TypedURLModelTypeController(syncer::ModelType model_type,
+ syncer::SyncService* sync_service,
+ HistoryService* history_service,
PrefService* pref_service);
TypedURLModelTypeController(const TypedURLModelTypeController&) = delete;
@@ -30,12 +38,7 @@ class TypedURLModelTypeController : public syncer::ModelTypeController {
PreconditionState GetPreconditionState() const override;
private:
- void OnSavingBrowserHistoryDisabledChanged();
-
- const raw_ptr<HistoryService> history_service_;
- const raw_ptr<PrefService> pref_service_;
-
- PrefChangeRegistrar pref_registrar_;
+ HistoryModelTypeControllerHelper helper_;
};
} // namespace history
diff --git a/chromium/components/history/core/browser/sync/typed_url_sync_bridge.cc b/chromium/components/history/core/browser/sync/typed_url_sync_bridge.cc
index bd4efa1e4a7..48468970985 100644
--- a/chromium/components/history/core/browser/sync/typed_url_sync_bridge.cc
+++ b/chromium/components/history/core/browser/sync/typed_url_sync_bridge.cc
@@ -141,8 +141,9 @@ absl::optional<syncer::ModelError> TypedURLSyncBridge::MergeSyncData(
DCHECK(entity_change->data().specifics.has_typed_url());
const sync_pb::TypedUrlSpecifics& specifics =
entity_change->data().specifics.typed_url();
- if (ShouldIgnoreUrl(GURL(specifics.url())))
+ if (ShouldIgnoreUrl(GURL(specifics.url()))) {
continue;
+ }
// Ignore old sync urls that don't have any transition data stored with
// them, or transition data that does not match the visit data (will be
@@ -234,13 +235,15 @@ absl::optional<syncer::ModelError> TypedURLSyncBridge::ApplySyncChanges(
GURL url(specifics.url());
- if (ShouldIgnoreUrl(url))
+ if (ShouldIgnoreUrl(url)) {
continue;
+ }
DCHECK(specifics.visits_size());
sync_pb::TypedUrlSpecifics filtered_url = FilterExpiredVisits(specifics);
- if (filtered_url.visits_size() == 0)
+ if (filtered_url.visits_size() == 0) {
continue;
+ }
UpdateFromSync(filtered_url, &new_synced_visits, &deleted_visits,
&updated_synced_urls, &new_synced_urls);
@@ -373,19 +376,21 @@ bool TypedURLSyncBridge::SupportsGetStorageKey() const {
void TypedURLSyncBridge::OnURLVisited(HistoryBackend* history_backend,
ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(sync_metadata_database_);
DCHECK_GE(row.typed_count(), 0);
- if (processing_syncer_changes_)
+ if (processing_syncer_changes_) {
return; // These are changes originating from us, ignore.
+ }
- if (!change_processor()->IsTrackingMetadata())
+ if (!change_processor()->IsTrackingMetadata()) {
return; // Sync processor not yet ready, don't sync.
- if (!ShouldSyncVisit(row.typed_count(), transition))
+ }
+ if (!ShouldSyncVisit(row.typed_count(), transition)) {
return;
+ }
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
CreateMetadataChangeList();
@@ -400,11 +405,13 @@ void TypedURLSyncBridge::OnURLsModified(HistoryBackend* history_backend,
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(sync_metadata_database_);
- if (processing_syncer_changes_)
+ if (processing_syncer_changes_) {
return; // These are changes originating from us, ignore.
+ }
- if (!change_processor()->IsTrackingMetadata())
+ if (!change_processor()->IsTrackingMetadata()) {
return; // Sync processor not yet ready, don't sync.
+ }
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
CreateMetadataChangeList();
@@ -425,11 +432,13 @@ void TypedURLSyncBridge::OnURLsDeleted(HistoryBackend* history_backend,
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(sync_metadata_database_);
- if (processing_syncer_changes_)
+ if (processing_syncer_changes_) {
return; // These are changes originating from us, ignore.
+ }
- if (!change_processor()->IsTrackingMetadata())
+ if (!change_processor()->IsTrackingMetadata()) {
return; // Sync processor not yet ready, don't sync.
+ }
// Ignore URLs expired due to old age (we don't want to sync them as deletions
// to avoid extra traffic up to the server, and also to make sure that a
@@ -542,8 +551,10 @@ bool TypedURLSyncBridge::WriteToTypedUrlSpecifics(
for (const VisitRow& visit : visits) {
// Skip reload visits.
- if (PageTransitionCoreTypeIs(visit.transition, ui::PAGE_TRANSITION_RELOAD))
+ if (PageTransitionCoreTypeIs(visit.transition,
+ ui::PAGE_TRANSITION_RELOAD)) {
continue;
+ }
// If we only have room for typed visits, then only add typed visits.
if (only_typed && !PageTransitionCoreTypeIs(visit.transition,
@@ -624,11 +635,13 @@ TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls(
history_visit_index < history_num_visits) {
// Time objects are initialized to "earliest possible time".
base::Time sync_url_time, history_time;
- if (sync_url_visit_index < sync_url_num_visits)
+ if (sync_url_visit_index < sync_url_num_visits) {
sync_url_time =
base::Time::FromInternalValue(sync_url.visits(sync_url_visit_index));
- if (history_visit_index < history_num_visits)
+ }
+ if (history_visit_index < history_num_visits) {
history_time = (*visits)[history_visit_index].visit_time;
+ }
if (sync_url_visit_index >= sync_url_num_visits ||
(history_visit_index < history_num_visits &&
sync_url_time > history_time)) {
@@ -849,7 +862,7 @@ void TypedURLSyncBridge::MergeURLWithSync(
base::Time::FromInternalValue(sync_url.visits(index));
ui::PageTransition transition =
ui::PageTransitionFromInt(sync_url.visit_transitions(index));
- added_visits.push_back(VisitInfo(visit_time, transition));
+ added_visits.emplace_back(visit_time, transition);
}
new_synced_visits->emplace_back(new_url.url(), added_visits);
return;
@@ -955,8 +968,9 @@ void TypedURLSyncBridge::UpdateSyncFromLocal(
URLRow row,
bool is_from_expiration,
syncer::MetadataChangeList* metadata_change_list) {
- if (ShouldIgnoreUrl(row.url()))
+ if (ShouldIgnoreUrl(row.url())) {
return;
+ }
// Get the visits for this node.
std::vector<VisitRow> visit_vector;
@@ -1028,8 +1042,9 @@ absl::optional<syncer::ModelError> TypedURLSyncBridge::WriteToHistoryBackend(
if (new_visits) {
for (const auto& [url, visit_infos] : *new_visits) {
// If there are no visits to add, just skip this.
- if (visit_infos.empty())
+ if (visit_infos.empty()) {
continue;
+ }
if (!history_backend_->AddVisits(url, visit_infos, SOURCE_SYNCED)) {
return syncer::ModelError(FROM_HERE,
"Could not add visits to HistoryBackend.");
@@ -1071,22 +1086,26 @@ bool TypedURLSyncBridge::ShouldIgnoreUrl(const GURL& url) {
// Ignore empty URLs. Not sure how this can happen (maybe import from other
// busted browsers, or misuse of the history API, or just plain bugs) but we
// can't deal with them.
- if (url.spec().empty())
+ if (url.spec().empty()) {
return true;
+ }
// Ignore local file URLs.
- if (url.SchemeIsFile())
+ if (url.SchemeIsFile()) {
return true;
+ }
// Ignore localhost URLs.
- if (net::IsLocalhost(url))
+ if (net::IsLocalhost(url)) {
return true;
+ }
// Ignore username and password, since history backend will remove user name
// and password in database_utils::GurlToDatabaseUrl and send
// username/password removed url to sync later.
- if (url.has_username() || url.has_password())
+ if (url.has_username() || url.has_password()) {
return true;
+ }
return false;
}
@@ -1097,8 +1116,9 @@ bool TypedURLSyncBridge::ShouldIgnoreVisits(
// chromium.
static const int kFirstImportedSource = SOURCE_FIREFOX_IMPORTED;
VisitSourceMap map;
- if (!history_backend_->GetVisitsSource(visits, &map))
+ if (!history_backend_->GetVisitsSource(visits, &map)) {
return false; // If we can't read the visit, assume it's not imported.
+ }
// Walk the list of visits and look for a non-imported item.
for (const VisitRow& visit : visits) {
diff --git a/chromium/components/history/core/browser/sync/typed_url_sync_bridge.h b/chromium/components/history/core/browser/sync/typed_url_sync_bridge.h
index b48f35a5c0a..c2b6206dd01 100644
--- a/chromium/components/history/core/browser/sync/typed_url_sync_bridge.h
+++ b/chromium/components/history/core/browser/sync/typed_url_sync_bridge.h
@@ -58,7 +58,6 @@ class TypedURLSyncBridge : public syncer::ModelTypeSyncBridge,
void OnURLVisited(HistoryBackend* history_backend,
ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
base::Time visit_time) override;
void OnURLsModified(HistoryBackend* history_backend,
const std::vector<URLRow>& changed_urls,
diff --git a/chromium/components/history/core/browser/sync/typed_url_sync_bridge_unittest.cc b/chromium/components/history/core/browser/sync/typed_url_sync_bridge_unittest.cc
index d42ab8c8984..99798af67c9 100644
--- a/chromium/components/history/core/browser/sync/typed_url_sync_bridge_unittest.cc
+++ b/chromium/components/history/core/browser/sync/typed_url_sync_bridge_unittest.cc
@@ -45,15 +45,12 @@ using syncer::MetadataChangeList;
using syncer::MockModelTypeChangeProcessor;
using testing::_;
using testing::AllOf;
-using testing::Contains;
using testing::DoAll;
using testing::IsEmpty;
using testing::Mock;
using testing::NiceMock;
-using testing::Not;
using testing::Pointee;
using testing::Return;
-using testing::SizeIs;
using testing::UnorderedElementsAre;
// Constants used to limit size of visits processed. See
@@ -255,7 +252,7 @@ void StoreMetadata(const std::string& storage_key,
class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
public:
- TestHistoryBackendDelegate() {}
+ TestHistoryBackendDelegate() = default;
TestHistoryBackendDelegate(const TestHistoryBackendDelegate&) = delete;
TestHistoryBackendDelegate& operator=(const TestHistoryBackendDelegate&) =
@@ -269,7 +266,6 @@ class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
const GURL& icon_url) override {}
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
- const RedirectList& redirects,
Time visit_time) override {}
void NotifyURLsModified(const std::vector<URLRow>& changed_urls) override {}
void NotifyURLsDeleted(DeletionInfo deletion_info) override {}
@@ -291,7 +287,7 @@ class TestHistoryBackendForSync : public HistoryBackend {
std::move(backend_client),
base::ThreadTaskRunnerHandle::Get()) {}
- bool IsExpiredVisitTime(const Time& time) override {
+ bool IsExpiredVisitTime(const Time& time) const override {
return time.ToDeltaSinceWindowsEpoch().InMicroseconds() == kExpiredVisit;
}
@@ -316,14 +312,14 @@ class TestHistoryBackendForSync : public HistoryBackend {
std::vector<VisitInfo> added_visits;
for (const auto& visit : visits) {
- added_visits.push_back(VisitInfo(visit.visit_time, visit.transition));
+ added_visits.emplace_back(visit.visit_time, visit.transition);
}
AddVisits(new_url->url(), added_visits, SOURCE_SYNCED);
new_url->set_id(GetIdByUrl(new_url->url()));
}
private:
- ~TestHistoryBackendForSync() override {}
+ ~TestHistoryBackendForSync() override = default;
};
class MockHistoryBackendClient : public HistoryBackendClient {
@@ -348,10 +344,10 @@ class TypedURLSyncBridgeTest : public testing::Test {
ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
fake_history_backend_->Init(
false, TestHistoryDatabaseParamsForPath(test_dir_.GetPath()));
- std::unique_ptr<TypedURLSyncBridge> bridge =
- std::make_unique<TypedURLSyncBridge>(
- fake_history_backend_.get(), fake_history_backend_->db(),
- mock_processor_.CreateForwardingProcessor());
+ auto bridge = std::make_unique<TypedURLSyncBridge>(
+ fake_history_backend_.get(),
+ fake_history_backend_->db()->GetTypedURLMetadataDB(),
+ mock_processor_.CreateForwardingProcessor());
typed_url_sync_bridge_ = bridge.get();
typed_url_sync_bridge_->Init();
typed_url_sync_bridge_->history_backend_observation_.Reset();
@@ -843,7 +839,7 @@ TEST_F(TypedURLSyncBridgeTest, MergeUrlsWithUsernameAndPassword) {
// Notify typed url sync service of the update.
bridge()->OnURLVisited(fake_history_backend_.get(), ui::PAGE_TRANSITION_TYPED,
- server_row, RedirectList(), SinceEpoch(7));
+ server_row, SinceEpoch(7));
}
// Starting sync with both local and sync have same typed URL, but different
@@ -981,8 +977,7 @@ TEST_F(TypedURLSyncBridgeTest, ReloadVisitLocalTypedUrl) {
// Notify typed url sync service of the update.
bridge()->OnURLVisited(fake_history_backend_.get(),
- ui::PAGE_TRANSITION_RELOAD, url_row, RedirectList(),
- SinceEpoch(7));
+ ui::PAGE_TRANSITION_RELOAD, url_row, SinceEpoch(7));
// No change pass to processor
}
@@ -1011,7 +1006,7 @@ TEST_F(TypedURLSyncBridgeTest, LinkVisitLocalTypedUrl) {
ui::PageTransition transition = ui::PAGE_TRANSITION_LINK;
// Notify typed url sync service of non-typed visit, expect no change.
bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
- RedirectList(), SinceEpoch(6));
+ SinceEpoch(6));
// No change pass to processor
}
@@ -1039,7 +1034,7 @@ TEST_F(TypedURLSyncBridgeTest, TypedVisitLocalTypedUrl) {
.WillOnce(SaveArgPointeeMove<1>(&entity_data));
ui::PageTransition transition = ui::PAGE_TRANSITION_TYPED;
bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
- RedirectList(), Time());
+ Time());
const sync_pb::TypedUrlSpecifics& url_specifics =
entity_data.specifics.typed_url();
@@ -1281,12 +1276,15 @@ TEST_F(TypedURLSyncBridgeTest, MaxVisitLocalTypedUrl) {
// Add `kMaxTypedUrlVisits` + 10 visits to the url. The 10 oldest
// non-typed visits are expected to be skipped.
int i = 1;
- for (; i <= kMaxTypedUrlVisits - 20; ++i)
+ for (; i <= kMaxTypedUrlVisits - 20; ++i) {
AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
- for (; i <= kMaxTypedUrlVisits; ++i)
+ }
+ for (; i <= kMaxTypedUrlVisits; ++i) {
AddNewestVisit(ui::PAGE_TRANSITION_LINK, i, &url_row, &visits);
- for (; i <= kMaxTypedUrlVisits + 10; ++i)
+ }
+ for (; i <= kMaxTypedUrlVisits + 10; ++i) {
AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
+ }
fake_history_backend_->SetVisitsForUrl(&url_row, visits);
@@ -1296,7 +1294,7 @@ TEST_F(TypedURLSyncBridgeTest, MaxVisitLocalTypedUrl) {
EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
.WillOnce(SaveArgPointeeMove<1>(&entity_data));
bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
- RedirectList(), Time());
+ Time());
const sync_pb::TypedUrlSpecifics& url_specifics =
entity_data.specifics.typed_url();
@@ -1339,18 +1337,20 @@ TEST_F(TypedURLSyncBridgeTest, ThrottleVisitLocalTypedUrl) {
// Add enough visits to the url so that typed count is above the throttle
// limit, and not right on the interval that gets synced.
int i = 1;
- for (; i < kVisitThrottleThreshold + kVisitThrottleMultiple / 2; ++i)
+ for (; i < kVisitThrottleThreshold + kVisitThrottleMultiple / 2; ++i) {
AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
+ }
fake_history_backend_->SetVisitsForUrl(&url_row, visits);
// Notify typed url sync service of typed visit.
ui::PageTransition transition = ui::PAGE_TRANSITION_TYPED;
bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
- RedirectList(), Time());
+ Time());
visits.clear();
- for (; i % kVisitThrottleMultiple != 1; ++i)
+ for (; i % kVisitThrottleMultiple != 1; ++i) {
AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
+ }
--i; // Account for the increment before the condition ends.
fake_history_backend_->SetVisitsForUrl(&url_row, visits);
@@ -1359,7 +1359,7 @@ TEST_F(TypedURLSyncBridgeTest, ThrottleVisitLocalTypedUrl) {
EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
.WillOnce(SaveArgPointeeMove<1>(&entity_data));
bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
- RedirectList(), Time());
+ Time());
ASSERT_EQ(i, entity_data.specifics.typed_url().visits_size());
}
@@ -1498,8 +1498,8 @@ TEST_F(TypedURLSyncBridgeTest, DiffVisitsSame) {
const int64_t visits[] = {1024, 2065, 65534, 1237684};
for (int64_t visit : visits) {
- old_visits.push_back(VisitRow(0, SinceEpoch(visit), 0,
- ui::PAGE_TRANSITION_TYPED, 0, true, 0));
+ old_visits.emplace_back(0, SinceEpoch(visit), 0, ui::PAGE_TRANSITION_TYPED,
+ 0, true, 0);
new_url.add_visits(visit);
new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED);
}
@@ -1528,8 +1528,8 @@ TEST_F(TypedURLSyncBridgeTest, DiffVisitsRemove) {
const int64_t visits_removed[] = {1500, 6000, 2237684};
for (int64_t visit : visits_left) {
- old_visits.push_back(VisitRow(0, SinceEpoch(visit), 0,
- ui::PAGE_TRANSITION_TYPED, 0, true, 0));
+ old_visits.emplace_back(0, SinceEpoch(visit), 0, ui::PAGE_TRANSITION_TYPED,
+ 0, true, 0);
}
for (int64_t visit : visits_right) {
@@ -1562,8 +1562,8 @@ TEST_F(TypedURLSyncBridgeTest, DiffVisitsAdd) {
const int64_t visits_added[] = {1, 1500, 6000, 2237684};
for (int64_t visit : visits_left) {
- old_visits.push_back(VisitRow(0, SinceEpoch(visit), 0,
- ui::PAGE_TRANSITION_TYPED, 0, true, 0));
+ old_visits.emplace_back(0, SinceEpoch(visit), 0, ui::PAGE_TRANSITION_TYPED,
+ 0, true, 0);
}
for (int64_t visit : visits_right) {
diff --git a/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.cc b/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.cc
index 90fa5754df6..2207516d486 100644
--- a/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.cc
+++ b/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.cc
@@ -32,9 +32,24 @@ const char kTypedURLModelTypeStateKey[] = "typed_url_model_type_state";
// value Serialize sync EntityMetadata, which is for tracking sync
// state of each typed url.
-TypedURLSyncMetadataDatabase::TypedURLSyncMetadataDatabase() {}
-
-TypedURLSyncMetadataDatabase::~TypedURLSyncMetadataDatabase() {}
+TypedURLSyncMetadataDatabase::TypedURLSyncMetadataDatabase(
+ sql::Database* db,
+ sql::MetaTable* meta_table)
+ : db_(db), meta_table_(meta_table) {}
+
+TypedURLSyncMetadataDatabase::~TypedURLSyncMetadataDatabase() = default;
+
+bool TypedURLSyncMetadataDatabase::Init() {
+ if (!db_->DoesTableExist("typed_url_sync_metadata")) {
+ if (!db_->Execute("CREATE TABLE typed_url_sync_metadata ("
+ "storage_key INTEGER PRIMARY KEY NOT NULL,"
+ "value BLOB)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
bool TypedURLSyncMetadataDatabase::GetAllSyncMetadata(
syncer::MetadataBatch* metadata_batch) {
@@ -44,8 +59,9 @@ bool TypedURLSyncMetadataDatabase::GetAllSyncMetadata(
}
sync_pb::ModelTypeState model_type_state;
- if (!GetModelTypeState(&model_type_state))
+ if (!GetModelTypeState(&model_type_state)) {
return false;
+ }
metadata_batch->SetModelTypeState(model_type_state);
return true;
@@ -58,9 +74,9 @@ bool TypedURLSyncMetadataDatabase::UpdateSyncMetadata(
DCHECK_EQ(model_type, syncer::TYPED_URLS)
<< "Only the TYPED_URLS model type is supported";
- sql::Statement s(GetDB().GetUniqueStatement(
- "INSERT OR REPLACE INTO typed_url_sync_metadata "
- "(storage_key, value) VALUES(?, ?)"));
+ sql::Statement s(
+ db_->GetUniqueStatement("INSERT OR REPLACE INTO typed_url_sync_metadata "
+ "(storage_key, value) VALUES(?, ?)"));
s.BindInt64(0, StorageKeyToURLID(storage_key));
s.BindString(1, metadata.SerializeAsString());
@@ -73,7 +89,7 @@ bool TypedURLSyncMetadataDatabase::ClearSyncMetadata(
DCHECK_EQ(model_type, syncer::TYPED_URLS)
<< "Only the TYPED_URLS model type is supported";
- sql::Statement s(GetDB().GetUniqueStatement(
+ sql::Statement s(db_->GetUniqueStatement(
"DELETE FROM typed_url_sync_metadata WHERE storage_key=?"));
s.BindInt64(0, StorageKeyToURLID(storage_key));
@@ -85,18 +101,18 @@ bool TypedURLSyncMetadataDatabase::UpdateModelTypeState(
const sync_pb::ModelTypeState& model_type_state) {
DCHECK_EQ(model_type, syncer::TYPED_URLS)
<< "Only the TYPED_URLS model type is supported";
- DCHECK_GT(GetMetaTable().GetVersionNumber(), 0);
+ DCHECK_GT(meta_table_->GetVersionNumber(), 0);
std::string serialized_state = model_type_state.SerializeAsString();
- return GetMetaTable().SetValue(kTypedURLModelTypeStateKey, serialized_state);
+ return meta_table_->SetValue(kTypedURLModelTypeStateKey, serialized_state);
}
bool TypedURLSyncMetadataDatabase::ClearModelTypeState(
syncer::ModelType model_type) {
DCHECK_EQ(model_type, syncer::TYPED_URLS)
<< "Only the TYPED_URLS model type is supported";
- DCHECK_GT(GetMetaTable().GetVersionNumber(), 0);
- return GetMetaTable().DeleteKey(kTypedURLModelTypeStateKey);
+ DCHECK_GT(meta_table_->GetVersionNumber(), 0);
+ return meta_table_->DeleteKey(kTypedURLModelTypeStateKey);
}
// static
@@ -111,26 +127,13 @@ URLID TypedURLSyncMetadataDatabase::StorageKeyToURLID(
return storage_key_int;
}
-bool TypedURLSyncMetadataDatabase::InitSyncTable() {
- if (!GetDB().DoesTableExist("typed_url_sync_metadata")) {
- if (!GetDB().Execute("CREATE TABLE typed_url_sync_metadata ("
- "storage_key INTEGER PRIMARY KEY NOT NULL,"
- "value BLOB)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool TypedURLSyncMetadataDatabase::
- CleanTypedURLOrphanedMetadataForMigrationToVersion40(
- const std::vector<URLID>& sorted_valid_rowids) {
+bool TypedURLSyncMetadataDatabase::CleanOrphanedMetadataForMigrationToVersion40(
+ const std::vector<URLID>& sorted_valid_rowids) {
DCHECK(base::ranges::is_sorted(sorted_valid_rowids));
std::vector<URLID> invalid_metadata_rowids;
auto valid_rowids_iter = sorted_valid_rowids.begin();
- sql::Statement sorted_metadata_rowids(GetDB().GetUniqueStatement(
+ sql::Statement sorted_metadata_rowids(db_->GetUniqueStatement(
"SELECT storage_key FROM typed_url_sync_metadata ORDER BY storage_key"));
while (sorted_metadata_rowids.Step()) {
URLID metadata_rowid = sorted_metadata_rowids.ColumnInt64(0);
@@ -154,12 +157,13 @@ bool TypedURLSyncMetadataDatabase::
}
for (const URLID& rowid : invalid_metadata_rowids) {
- sql::Statement del(GetDB().GetCachedStatement(
+ sql::Statement del(db_->GetCachedStatement(
SQL_FROM_HERE,
"DELETE FROM typed_url_sync_metadata WHERE storage_key=?"));
del.BindInt64(0, rowid);
- if (!del.Run())
+ if (!del.Run()) {
return false;
+ }
}
return true;
@@ -168,7 +172,7 @@ bool TypedURLSyncMetadataDatabase::
bool TypedURLSyncMetadataDatabase::GetAllSyncEntityMetadata(
syncer::MetadataBatch* metadata_batch) {
DCHECK(metadata_batch);
- sql::Statement s(GetDB().GetUniqueStatement(
+ sql::Statement s(db_->GetUniqueStatement(
"SELECT storage_key, value FROM typed_url_sync_metadata"));
while (s.Step()) {
@@ -189,9 +193,9 @@ bool TypedURLSyncMetadataDatabase::GetAllSyncEntityMetadata(
bool TypedURLSyncMetadataDatabase::GetModelTypeState(
sync_pb::ModelTypeState* state) {
- DCHECK_GT(GetMetaTable().GetVersionNumber(), 0);
+ DCHECK_GT(meta_table_->GetVersionNumber(), 0);
std::string serialized_state;
- if (!GetMetaTable().GetValue(kTypedURLModelTypeStateKey, &serialized_state)) {
+ if (!meta_table_->GetValue(kTypedURLModelTypeStateKey, &serialized_state)) {
return true;
}
diff --git a/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.h b/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.h
index a9eda6fd8a1..416dfe1396c 100644
--- a/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.h
+++ b/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database.h
@@ -8,6 +8,7 @@
#include <string>
#include <vector>
+#include "base/memory/raw_ptr.h"
#include "components/history/core/browser/url_row.h"
#include "components/sync/base/model_type.h"
#include "components/sync/model/sync_metadata_store.h"
@@ -29,9 +30,8 @@ namespace history {
// datatype.
class TypedURLSyncMetadataDatabase : public syncer::SyncMetadataStore {
public:
- // Must call InitVisitTable() before using to make sure the database is
- // initialized.
- TypedURLSyncMetadataDatabase();
+ // Must call Init() before using to make sure the database is initialized.
+ TypedURLSyncMetadataDatabase(sql::Database* db, sql::MetaTable* meta_table);
TypedURLSyncMetadataDatabase(const TypedURLSyncMetadataDatabase&) = delete;
TypedURLSyncMetadataDatabase& operator=(const TypedURLSyncMetadataDatabase&) =
@@ -39,6 +39,10 @@ class TypedURLSyncMetadataDatabase : public syncer::SyncMetadataStore {
~TypedURLSyncMetadataDatabase() override;
+ // Makes sure the tables and indices are properly set up. Must be called
+ // before anything else.
+ bool Init();
+
// Read all the stored metadata for typed URL and fill `metadata_batch`
// with it.
bool GetAllSyncMetadata(syncer::MetadataBatch* metadata_batch);
@@ -56,24 +60,11 @@ class TypedURLSyncMetadataDatabase : public syncer::SyncMetadataStore {
static URLID StorageKeyToURLID(const std::string& storage_key);
- protected:
- // Returns the database for the functions in this interface.
- virtual sql::Database& GetDB() = 0;
-
- // Returns MetaTable, so this sync can store ModelTypeState in MetaTable.
- // Check if GetMetaTable().GetVersionNumber() is greater than 0 to make sure
- // MetaTable is initialed.
- virtual sql::MetaTable& GetMetaTable() = 0;
-
- // Called by the derived classes on initialization to make sure the tables
- // and indices are properly set up. Must be called before anything else.
- bool InitSyncTable();
-
// Cleans up orphaned metadata for typed URLs, i.e. deletes all metadata
// entries for rowids not present in `sorted_valid_rowids` (which must be
// sorted in ascending order). Returns true if the clean up finishes without
// any DB error.
- bool CleanTypedURLOrphanedMetadataForMigrationToVersion40(
+ bool CleanOrphanedMetadataForMigrationToVersion40(
const std::vector<URLID>& sorted_valid_rowids);
private:
@@ -83,6 +74,9 @@ class TypedURLSyncMetadataDatabase : public syncer::SyncMetadataStore {
// Read sync_pb::ModelTypeState for typed URL and fill `state` with it.
bool GetModelTypeState(sync_pb::ModelTypeState* state);
+
+ const raw_ptr<sql::Database> db_;
+ const raw_ptr<sql::MetaTable> meta_table_;
};
} // namespace history
diff --git a/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database_unittest.cc b/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database_unittest.cc
index a4cac59bdc2..41b91e65c19 100644
--- a/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database_unittest.cc
+++ b/chromium/components/history/core/browser/sync/typed_url_sync_metadata_database_unittest.cc
@@ -32,21 +32,18 @@ std::string IntToStorageKey(int i) {
} // namespace
-class TypedURLSyncMetadataDatabaseTest : public testing::Test,
- public TypedURLSyncMetadataDatabase {
+class TypedURLSyncMetadataDatabaseTest : public testing::Test {
public:
- TypedURLSyncMetadataDatabaseTest() {}
+ TypedURLSyncMetadataDatabaseTest() : sync_metadata_db_(&db_, &meta_table_) {}
TypedURLSyncMetadataDatabaseTest(const TypedURLSyncMetadataDatabaseTest&) =
delete;
TypedURLSyncMetadataDatabaseTest& operator=(
const TypedURLSyncMetadataDatabaseTest&) = delete;
- ~TypedURLSyncMetadataDatabaseTest() override {}
+ ~TypedURLSyncMetadataDatabaseTest() override = default;
protected:
- sql::Database& GetDB() override { return db_; }
-
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath db_file =
@@ -55,22 +52,22 @@ class TypedURLSyncMetadataDatabaseTest : public testing::Test,
EXPECT_TRUE(db_.Open(db_file));
// Initialize the tables for this test.
- InitSyncTable();
+ sync_metadata_db_.Init();
- GetMetaTable().Init(&db_, 1, 1);
+ meta_table_.Init(&db_, 1, 1);
}
void TearDown() override { db_.Close(); }
- sql::MetaTable& GetMetaTable() override { return meta_table_; }
-
base::ScopedTempDir temp_dir_;
sql::Database db_;
sql::MetaTable meta_table_;
+
+ TypedURLSyncMetadataDatabase sync_metadata_db_;
};
TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLNoMetadata) {
MetadataBatch metadata_batch;
- EXPECT_TRUE(GetAllSyncMetadata(&metadata_batch));
+ EXPECT_TRUE(sync_metadata_db_.GetAllSyncMetadata(&metadata_batch));
EXPECT_EQ(0u, metadata_batch.TakeAllMetadata().size());
EXPECT_EQ(ModelTypeState().SerializeAsString(),
metadata_batch.GetModelTypeState().SerializeAsString());
@@ -82,18 +79,21 @@ TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLGetAllSyncMetadata) {
std::string storage_key2 = IntToStorageKey(2);
metadata.set_sequence_number(1);
- EXPECT_TRUE(UpdateSyncMetadata(syncer::TYPED_URLS, storage_key, metadata));
+ EXPECT_TRUE(sync_metadata_db_.UpdateSyncMetadata(syncer::TYPED_URLS,
+ storage_key, metadata));
ModelTypeState model_type_state;
model_type_state.set_initial_sync_done(true);
- EXPECT_TRUE(UpdateModelTypeState(syncer::TYPED_URLS, model_type_state));
+ EXPECT_TRUE(sync_metadata_db_.UpdateModelTypeState(syncer::TYPED_URLS,
+ model_type_state));
metadata.set_sequence_number(2);
- EXPECT_TRUE(UpdateSyncMetadata(syncer::TYPED_URLS, storage_key2, metadata));
+ EXPECT_TRUE(sync_metadata_db_.UpdateSyncMetadata(syncer::TYPED_URLS,
+ storage_key2, metadata));
MetadataBatch metadata_batch;
- EXPECT_TRUE(GetAllSyncMetadata(&metadata_batch));
+ EXPECT_TRUE(sync_metadata_db_.GetAllSyncMetadata(&metadata_batch));
EXPECT_TRUE(metadata_batch.GetModelTypeState().initial_sync_done());
@@ -105,9 +105,10 @@ TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLGetAllSyncMetadata) {
// Now check that a model type state update replaces the old value
model_type_state.set_initial_sync_done(false);
- EXPECT_TRUE(UpdateModelTypeState(syncer::TYPED_URLS, model_type_state));
+ EXPECT_TRUE(sync_metadata_db_.UpdateModelTypeState(syncer::TYPED_URLS,
+ model_type_state));
- EXPECT_TRUE(GetAllSyncMetadata(&metadata_batch));
+ EXPECT_TRUE(sync_metadata_db_.GetAllSyncMetadata(&metadata_batch));
EXPECT_FALSE(metadata_batch.GetModelTypeState().initial_sync_done());
}
@@ -122,40 +123,43 @@ TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLWriteThenDeleteSyncMetadata) {
metadata.set_client_tag_hash("client_hash");
// Write the data into the store.
- EXPECT_TRUE(UpdateSyncMetadata(syncer::TYPED_URLS, storage_key, metadata));
- EXPECT_TRUE(UpdateModelTypeState(syncer::TYPED_URLS, model_type_state));
+ EXPECT_TRUE(sync_metadata_db_.UpdateSyncMetadata(syncer::TYPED_URLS,
+ storage_key, metadata));
+ EXPECT_TRUE(sync_metadata_db_.UpdateModelTypeState(syncer::TYPED_URLS,
+ model_type_state));
// Delete the data we just wrote.
- EXPECT_TRUE(ClearSyncMetadata(syncer::TYPED_URLS, storage_key));
+ EXPECT_TRUE(
+ sync_metadata_db_.ClearSyncMetadata(syncer::TYPED_URLS, storage_key));
// It shouldn't be there any more.
- EXPECT_TRUE(GetAllSyncMetadata(&metadata_batch));
+ EXPECT_TRUE(sync_metadata_db_.GetAllSyncMetadata(&metadata_batch));
EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata();
EXPECT_EQ(metadata_records.size(), 0u);
// Now delete the model type state.
- EXPECT_TRUE(ClearModelTypeState(syncer::TYPED_URLS));
- EXPECT_TRUE(GetAllSyncMetadata(&metadata_batch));
+ EXPECT_TRUE(sync_metadata_db_.ClearModelTypeState(syncer::TYPED_URLS));
+ EXPECT_TRUE(sync_metadata_db_.GetAllSyncMetadata(&metadata_batch));
EXPECT_EQ(ModelTypeState().SerializeAsString(),
metadata_batch.GetModelTypeState().SerializeAsString());
}
TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLCorruptSyncMetadata) {
MetadataBatch metadata_batch;
- sql::Statement s(GetDB().GetUniqueStatement(
- "INSERT OR REPLACE INTO typed_url_sync_metadata "
- "(storage_key, value) VALUES(?, ?)"));
+ sql::Statement s(
+ db_.GetUniqueStatement("INSERT OR REPLACE INTO typed_url_sync_metadata "
+ "(storage_key, value) VALUES(?, ?)"));
s.BindInt64(0, 1);
s.BindString(1, "unparseable");
EXPECT_TRUE(s.Run());
- EXPECT_FALSE(GetAllSyncMetadata(&metadata_batch));
+ EXPECT_FALSE(sync_metadata_db_.GetAllSyncMetadata(&metadata_batch));
}
TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLCorruptModelTypeState) {
MetadataBatch metadata_batch;
- GetMetaTable().SetValue("typed_url_model_type_state", "unparseable");
+ meta_table_.SetValue("typed_url_model_type_state", "unparseable");
- EXPECT_FALSE(GetAllSyncMetadata(&metadata_batch));
+ EXPECT_FALSE(sync_metadata_db_.GetAllSyncMetadata(&metadata_batch));
}
} // namespace history
diff --git a/chromium/components/history/core/browser/top_sites_impl.cc b/chromium/components/history/core/browser/top_sites_impl.cc
index 6f5d994b66e..3906137a3c4 100644
--- a/chromium/components/history/core/browser/top_sites_impl.cc
+++ b/chromium/components/history/core/browser/top_sites_impl.cc
@@ -14,6 +14,7 @@
#include "base/check.h"
#include "base/hash/md5.h"
#include "base/location.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -22,15 +23,22 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "build/build_config.h"
+#include "components/history/core/browser/features.h"
#include "components/history/core/browser/history_backend.h"
#include "components/history/core/browser/history_constants.h"
#include "components/history/core/browser/history_db_task.h"
+#include "components/history/core/browser/keyword_search_term.h"
+#include "components/history/core/browser/keyword_search_term_util.h"
#include "components/history/core/browser/page_usage_data.h"
#include "components/history/core/browser/top_sites_observer.h"
+#include "components/history/core/browser/url_database.h"
#include "components/history/core/browser/url_utils.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
+#include "components/search_engines/search_terms_data.h"
+#include "components/search_engines/template_url.h"
+#include "components/search_engines/template_url_service.h"
#include "url/gurl.h"
namespace history {
@@ -61,6 +69,20 @@ bool DoTitlesDiffer(const MostVisitedURLList& old_list,
});
}
+// Transforms |number| in the range given by |max| and |min| to a number in the
+// range given by |new_max| and |new_min| while maintaining the ratio.
+double GetNumberInNewRange(double number,
+ double max,
+ double min,
+ double new_max,
+ double new_min) {
+ DCHECK_LE(number, max);
+ DCHECK_GE(number, min);
+ DCHECK_GE(new_max, new_min);
+ const auto ratio = (max == min) ? 1 : (number - min) / (max - min);
+ return ratio * (new_max - new_min) + new_min;
+}
+
// The delay for the first HistoryService query at startup.
constexpr base::TimeDelta kFirstDelayAtStartup = base::Seconds(15);
@@ -79,19 +101,40 @@ constexpr base::TimeDelta kDelayForUpdates = base::Minutes(60);
// TODO(sky): rename actual value to 'most_visited_blocked_urls.'
const char kBlockedUrlsPrefsKey[] = "ntp.most_visited_blacklist";
+void RecordDBMetrics(const base::TimeTicks db_query_time,
+ const size_t result_size) {
+ base::UmaHistogramTimes("History.TopSites.SearchTermsExtractionTime",
+ base::TimeTicks::Now() - db_query_time);
+ base::UmaHistogramCounts10000("History.TopSites.SearchTermsExtractedCount",
+ result_size);
+}
+
} // namespace
// Initially, histogram is not recorded.
bool TopSitesImpl::histogram_recorded_ = false;
+bool GetSearchResultsPageForDefaultSearchProvider(
+ const TemplateURL& default_provider,
+ const SearchTermsData& search_terms_data,
+ const std::u16string& search_terms,
+ GURL* url) {
+ TemplateURLRef::SearchTermsArgs search_terms_args(search_terms);
+ *url = GURL(default_provider.url_ref().ReplaceSearchTerms(search_terms_args,
+ search_terms_data));
+ return url->is_valid();
+}
+
TopSitesImpl::TopSitesImpl(PrefService* pref_service,
HistoryService* history_service,
+ TemplateURLService* template_url_service,
const PrepopulatedPageList& prepopulated_pages,
const CanAddURLToHistoryFn& can_add_url_to_history)
: backend_(nullptr),
prepopulated_pages_(prepopulated_pages),
pref_service_(pref_service),
history_service_(history_service),
+ template_url_service_(template_url_service),
can_add_url_to_history_(can_add_url_to_history),
loaded_(false) {
DCHECK(pref_service_);
@@ -229,8 +272,6 @@ void TopSitesImpl::RegisterPrefs(PrefRegistrySimple* registry) {
TopSitesImpl::~TopSitesImpl() = default;
void TopSitesImpl::StartQueryForMostVisited() {
- constexpr int kDaysOfHistory = 90;
-
DCHECK(loaded_);
timer_.Stop();
@@ -238,7 +279,7 @@ void TopSitesImpl::StartQueryForMostVisited() {
return;
history_service_->QueryMostVisitedURLs(
- num_results_to_request_from_history(), kDaysOfHistory,
+ num_results_to_request_from_history(),
base::BindOnce(&TopSitesImpl::OnTopSitesAvailableFromHistory,
base::Unretained(this)),
&cancelable_task_tracker_);
@@ -283,6 +324,115 @@ void TopSitesImpl::DiffMostVisited(const MostVisitedURLList& old_list,
}
}
+MostVisitedURLList TopSitesImpl::AddMostRepeatedQueries(
+ const MostVisitedURLList& sites) {
+ history::URLDatabase* url_db = history_service_->InMemoryDatabase();
+ if (!url_db) {
+ return sites;
+ }
+
+ if (!template_url_service_) {
+ return sites;
+ }
+
+ const auto* default_provider =
+ template_url_service_->GetDefaultSearchProvider();
+ if (!default_provider) {
+ return sites;
+ }
+
+ const base::Time age_threshold = base::Time::Now() - base::Days(90);
+ auto enumerator = url_db->CreateKeywordSearchTermVisitEnumerator(
+ default_provider->id(), age_threshold);
+ if (!enumerator) {
+ return sites;
+ }
+
+ // Generate the final list of the most repeated queries in descending order of
+ // their scores. Ensure the correct search results page URLs are set.
+ // TODO(crbug.com/1317829): Investigate moving this to a background thread.
+ MostVisitedURLList repeatable_query_sites;
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>> search_terms;
+ const base::TimeTicks db_query_time = base::TimeTicks::Now();
+ history::GetMostRepeatedSearchTermsFromEnumerator(*enumerator, &search_terms);
+ RecordDBMetrics(db_query_time, search_terms.size());
+ for (const auto& search_term : search_terms) {
+ GURL url;
+ if (!GetSearchResultsPageForDefaultSearchProvider(
+ *default_provider, template_url_service_->search_terms_data(),
+ search_term->normalized_term, &url) ||
+ IsBlocked(url)) {
+ continue;
+ }
+ repeatable_query_sites.emplace_back(url, search_term->normalized_term,
+ *search_term->score);
+ if (repeatable_query_sites.size() >=
+ static_cast<size_t>(kMaxNumRepeatableQueries.Get())) {
+ break;
+ }
+ }
+
+ // If there are no most repeated queries, there is nothing left to do.
+ if (repeatable_query_sites.empty()) {
+ return sites;
+ }
+
+ // Generate the final list of the most visited sites arranged in descending
+ // order of their scores. Exclude any site that is the search results page.
+ // TODO(crbug.com/1317829): Get rid of |most_visted_sites| by passing |sites|
+ // by value and modify it destructively.
+ MostVisitedURLList most_visted_sites;
+ for (const auto& site : sites) {
+ if (template_url_service_->IsSearchResultsPageFromDefaultSearchProvider(
+ site.url) ||
+ IsBlocked(site.url)) {
+ continue;
+ }
+ most_visted_sites.emplace_back(site.url, site.title, site.score);
+ if (most_visted_sites.size() >= kTopSitesNumber) {
+ break;
+ }
+ }
+
+ // If there are no most visited sites, there is nothing left to do.
+ if (most_visted_sites.empty()) {
+ return repeatable_query_sites;
+ }
+
+ // To achieve a uniform mix of the sites and the queries as much as possible,
+ // scale the scores to the new range which includes both sites and queries.
+ if (kScaleRepeatableQueriesScores.Get()) {
+ const auto queries_max = repeatable_query_sites.front().score;
+ const auto queries_min = repeatable_query_sites.back().score;
+ const auto sites_max = most_visted_sites.front().score;
+ const auto sites_min = most_visted_sites.back().score;
+ const auto new_min = std::min(sites_min, queries_min);
+ const auto new_max = std::max(sites_max, queries_max);
+ for (auto& query : repeatable_query_sites) {
+ query.score = GetNumberInNewRange(query.score, queries_max, queries_min,
+ new_max, new_min);
+ }
+ for (auto& site : most_visted_sites) {
+ site.score = GetNumberInNewRange(site.score, sites_max, sites_min,
+ new_max, new_min);
+ }
+ }
+
+ // Merge the two sorted lists of sites and queries into a single list. Equal
+ // elements from the first list precede the elements from the second list.
+ const auto& first_list = kPrivilegeRepeatableQueries.Get()
+ ? repeatable_query_sites
+ : most_visted_sites;
+ const auto& second_list = kPrivilegeRepeatableQueries.Get()
+ ? most_visted_sites
+ : repeatable_query_sites;
+ MostVisitedURLList merged_list;
+ std::merge(first_list.begin(), first_list.end(), second_list.begin(),
+ second_list.end(), std::back_inserter(merged_list),
+ [](const auto& a, const auto& b) { return a.score > b.score; });
+ return merged_list;
+}
+
bool TopSitesImpl::AddPrepopulatedPages(MostVisitedURLList* urls) const {
bool added = false;
for (const auto& prepopulated_page : prepopulated_pages_) {
@@ -428,8 +578,12 @@ void TopSitesImpl::OnGotMostVisitedURLs(MostVisitedURLList sites) {
&TopSitesImpl::StartQueryForMostVisited);
}
-void TopSitesImpl::OnTopSitesAvailableFromHistory(MostVisitedURLList pages) {
- SetTopSites(std::move(pages), CALL_LOCATION_FROM_OTHER_PLACES);
+void TopSitesImpl::OnTopSitesAvailableFromHistory(MostVisitedURLList sites) {
+ if (base::FeatureList::IsEnabled(kOrganicRepeatableQueries)) {
+ SetTopSites(AddMostRepeatedQueries(sites), CALL_LOCATION_FROM_OTHER_PLACES);
+ } else {
+ SetTopSites(std::move(sites), CALL_LOCATION_FROM_OTHER_PLACES);
+ }
}
void TopSitesImpl::OnURLsDeleted(HistoryService* history_service,
diff --git a/chromium/components/history/core/browser/top_sites_impl.h b/chromium/components/history/core/browser/top_sites_impl.h
index fb74c9ff528..b6b6f8b763f 100644
--- a/chromium/components/history/core/browser/top_sites_impl.h
+++ b/chromium/components/history/core/browser/top_sites_impl.h
@@ -27,6 +27,9 @@
class PrefRegistrySimple;
class PrefService;
+class SearchTermsData;
+class TemplateURL;
+class TemplateURLService;
namespace base {
class FilePath;
@@ -36,6 +39,17 @@ namespace history {
class TopSitesImplTest;
+// How many top sites to store in the cache.
+static constexpr size_t kTopSitesNumber = 10;
+
+// Returns true if it can set |url| to a valid canonical search results page
+// URL for |default_provider| given the search terms.
+bool GetSearchResultsPageForDefaultSearchProvider(
+ const TemplateURL& default_provider,
+ const SearchTermsData& search_terms_data,
+ const std::u16string& search_terms,
+ GURL* url);
+
// This class allows requests for most visited urls on any thread. All other
// methods must be invoked on the UI thread. All mutations to internal state
// happen on the UI thread and are scheduled to update the db using
@@ -46,11 +60,9 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
// callable multiple time and during the whole lifetime of TopSitesImpl.
using CanAddURLToHistoryFn = base::RepeatingCallback<bool(const GURL&)>;
- // How many top sites to store in the cache.
- static constexpr size_t kTopSitesNumber = 10;
-
TopSitesImpl(PrefService* pref_service,
HistoryService* history_service,
+ TemplateURLService* template_url_service,
const PrepopulatedPageList& prepopulated_pages,
const CanAddURLToHistoryFn& can_add_url_to_history);
@@ -100,6 +112,7 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
friend class TopSitesImplTest;
FRIEND_TEST_ALL_PREFIXES(TopSitesImplTest, DiffMostVisited);
FRIEND_TEST_ALL_PREFIXES(TopSitesImplTest, DiffMostVisitedWithForced);
+ FRIEND_TEST_ALL_PREFIXES(TopSitesImplTest, GetMostVisitedURLsAndQueries);
using PendingCallback = base::OnceCallback<void(const MostVisitedURLList&)>;
@@ -122,6 +135,9 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
const MostVisitedURLList& new_list,
TopSitesDelta* delta);
+ // Adds the most repeated search terms to TopSites and returns a new list.
+ MostVisitedURLList AddMostRepeatedQueries(const MostVisitedURLList& urls);
+
// Adds prepopulated pages to TopSites. Returns true if any pages were added.
bool AddPrepopulatedPages(MostVisitedURLList* urls) const;
@@ -202,6 +218,10 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
// must outlive TopSitesImpl.
raw_ptr<HistoryService> history_service_;
+ // Used to identify and create search results page URLs for the default
+ // provider. May be nullptr. Must outlive |this| if provided.
+ raw_ptr<TemplateURLService> template_url_service_;
+
// Can URL be added to the history?
CanAddURLToHistoryFn can_add_url_to_history_;
diff --git a/chromium/components/history/core/browser/top_sites_impl_unittest.cc b/chromium/components/history/core/browser/top_sites_impl_unittest.cc
index 86ec0c3784e..1d7bf803c39 100644
--- a/chromium/components/history/core/browser/top_sites_impl_unittest.cc
+++ b/chromium/components/history/core/browser/top_sites_impl_unittest.cc
@@ -15,8 +15,11 @@
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
+#include "components/history/core/browser/features.h"
#include "components/history/core/browser/history_client.h"
#include "components/history/core/browser/history_constants.h"
#include "components/history/core/browser/history_database_params.h"
@@ -32,6 +35,8 @@
#include "components/history/core/test/wait_top_sites_loaded_observer.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
+#include "components/search_engines/template_url.h"
+#include "components/search_engines/template_url_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -117,8 +122,16 @@ class TopSitesImplTest : public HistoryUnitTestBase {
nullptr, std::unique_ptr<VisitDelegate>());
ASSERT_TRUE(history_service_->Init(
TestHistoryDatabaseParamsForPath(scoped_temp_dir_.GetPath())));
- ResetTopSites();
- WaitTopSitesLoaded();
+
+ template_url_service_ = std::make_unique<TemplateURLService>(nullptr, 0);
+ // Add the fallback default search provider to the TemplateURLService as the
+ // user selected default provider so that it gets a valid unique identifier.
+ auto* default_provider = template_url_service()->Add(
+ std::make_unique<TemplateURL>(default_search_provider()->data()));
+ template_url_service()->SetUserSelectedDefaultSearchProvider(
+ default_provider);
+
+ RecreateTopSitesAndBlock();
}
void TearDown() override {
@@ -147,6 +160,14 @@ class TopSitesImplTest : public HistoryUnitTestBase {
HistoryService* history_service() { return history_service_.get(); }
+ TemplateURLService* template_url_service() {
+ return template_url_service_.get();
+ }
+
+ const TemplateURL* default_search_provider() {
+ return template_url_service()->GetDefaultSearchProvider();
+ }
+
PrepopulatedPageList GetPrepopulatedPages() {
return top_sites()->GetPrepopulatedPages();
}
@@ -178,7 +199,19 @@ class TopSitesImplTest : public HistoryUnitTestBase {
history_service()->SetPageTitle(url, title);
}
- // Delets a url.
+ // Adds a search results page to history.
+ bool AddSearchResultsPageToHistory(const std::u16string& search_terms,
+ GURL* url) {
+ bool success = GetSearchResultsPageForDefaultSearchProvider(
+ *default_search_provider(), template_url_service()->search_terms_data(),
+ search_terms, url);
+ AddPageToHistory(*url);
+ history_service()->SetKeywordSearchTermsForURL(
+ *url, default_search_provider()->id(), search_terms);
+ return success;
+ }
+
+ // Deletes a url.
void DeleteURL(const GURL& url) { history_service()->DeleteURLs({url}); }
// Recreates top sites. This forces top sites to reread from the db.
@@ -219,8 +252,8 @@ class TopSitesImplTest : public HistoryUnitTestBase {
prepopulated_pages.push_back(
PrepopulatedPage(GURL(kPrepopulatedPageURL), std::u16string(), -1, 0));
top_sites_impl_ = new TopSitesImpl(
- pref_service_.get(), history_service_.get(), prepopulated_pages,
- base::BindRepeating(MockCanAddURLToHistory));
+ pref_service_.get(), history_service_.get(), template_url_service(),
+ prepopulated_pages, base::BindRepeating(MockCanAddURLToHistory));
top_sites_impl_->Init(scoped_temp_dir_.GetPath().Append(kTopSitesFilename));
}
@@ -246,6 +279,7 @@ class TopSitesImplTest : public HistoryUnitTestBase {
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
std::unique_ptr<HistoryService> history_service_;
+ std::unique_ptr<TemplateURLService> template_url_service_;
scoped_refptr<TopSitesImpl> top_sites_impl_;
// To cancel HistoryService tasks.
@@ -379,6 +413,125 @@ TEST_F(TopSitesImplTest, GetMostVisited) {
ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2));
}
+// Tests GetMostVisitedURLs when AddMostRepeatedQueries is called.
+TEST_F(TopSitesImplTest, GetMostVisitedURLsAndQueries) {
+ GURL news("http://news.google.com/");
+ AddPageToHistory(news);
+ GURL srp_1;
+ ASSERT_TRUE(AddSearchResultsPageToHistory(u"query 1", &srp_1));
+ GURL srp_2;
+ ASSERT_TRUE(AddSearchResultsPageToHistory(u"query 2", &srp_2));
+
+ {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndDisableFeature(kOrganicRepeatableQueries);
+ base::HistogramTester histogram_tester;
+
+ StartQueryForMostVisited();
+ WaitForHistory();
+
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ ASSERT_EQ(1, querier.number_of_callbacks());
+
+ // 2 top sites + 2 prepopulated URLs.
+ // Note that even with the repeatable queries feature disabled, up to 1
+ // search results page URL may be shown in the top sites.
+ ASSERT_EQ(2u + GetPrepopulatedPages().size(), querier.urls().size());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2));
+ EXPECT_EQ(srp_2, querier.urls()[0].url);
+ EXPECT_EQ(news, querier.urls()[1].url);
+
+ histogram_tester.ExpectTotalCount(
+ "History.TopSites.SearchTermsExtractionTime", 0);
+ histogram_tester.ExpectTotalCount(
+ "History.TopSites.SearchTermsExtractedCount", 0);
+ }
+ {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kOrganicRepeatableQueries);
+ base::HistogramTester histogram_tester;
+
+ RefreshTopSitesAndRecreate();
+
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+ ASSERT_EQ(1, querier.number_of_callbacks());
+
+ // 1 top site + 2 repeatable queries + 2 prepopulated URLs.
+ // With the repeatable queries feature enabled, both search results page
+ // URLs are shown in the top sites.
+ ASSERT_EQ(3u + GetPrepopulatedPages().size(), querier.urls().size());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 3));
+ EXPECT_EQ(news, querier.urls()[0].url);
+ EXPECT_EQ(srp_1, querier.urls()[1].url);
+ EXPECT_EQ(srp_2, querier.urls()[2].url);
+
+ histogram_tester.ExpectTotalCount(
+ "History.TopSites.SearchTermsExtractionTime", 1);
+ histogram_tester.ExpectTotalCount(
+ "History.TopSites.SearchTermsExtractedCount", 1);
+ histogram_tester.ExpectUniqueSample(
+ "History.TopSites.SearchTermsExtractedCount", 2, 1);
+ }
+ {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeatureWithParameters(
+ kOrganicRepeatableQueries,
+ {{kPrivilegeRepeatableQueries.name, "true"}});
+ base::HistogramTester histogram_tester;
+
+ RefreshTopSitesAndRecreate();
+
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+ ASSERT_EQ(1, querier.number_of_callbacks());
+
+ // 2 repeatable queries + 1 top site + 2 prepopulated URLs.
+ // Repeatable queries can be made to precede the top sites of equal scores.
+ ASSERT_EQ(3u + GetPrepopulatedPages().size(), querier.urls().size());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 3));
+ EXPECT_EQ(srp_1, querier.urls()[0].url);
+ EXPECT_EQ(srp_2, querier.urls()[1].url);
+ EXPECT_EQ(news, querier.urls()[2].url);
+
+ histogram_tester.ExpectTotalCount(
+ "History.TopSites.SearchTermsExtractionTime", 1);
+ histogram_tester.ExpectTotalCount(
+ "History.TopSites.SearchTermsExtractedCount", 1);
+ histogram_tester.ExpectUniqueSample(
+ "History.TopSites.SearchTermsExtractedCount", 2, 1);
+ }
+ {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeatureWithParameters(
+ kOrganicRepeatableQueries, {{kPrivilegeRepeatableQueries.name, "true"},
+ {kMaxNumRepeatableQueries.name, "1"}});
+ base::HistogramTester histogram_tester;
+
+ RefreshTopSitesAndRecreate();
+
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+ ASSERT_EQ(1, querier.number_of_callbacks());
+
+ // 1 repeatable query + 1 top site + 2 prepopulated URLs.
+ // The number of repeatable queries can be capped.
+ ASSERT_EQ(2u + GetPrepopulatedPages().size(), querier.urls().size());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2));
+ EXPECT_EQ(srp_1, querier.urls()[0].url);
+ EXPECT_EQ(news, querier.urls()[1].url);
+
+ histogram_tester.ExpectTotalCount(
+ "History.TopSites.SearchTermsExtractionTime", 1);
+ histogram_tester.ExpectTotalCount(
+ "History.TopSites.SearchTermsExtractedCount", 1);
+ histogram_tester.ExpectUniqueSample(
+ "History.TopSites.SearchTermsExtractedCount", 2, 1);
+ }
+}
+
// Tests GetMostVisitedURLs with a redirect.
TEST_F(TopSitesImplTest, GetMostVisitedWithRedirect) {
GURL bare("http://cnn.com/");
diff --git a/chromium/components/history/core/browser/url_database.cc b/chromium/components/history/core/browser/url_database.cc
index befdf2bffe2..43702f846b7 100644
--- a/chromium/components/history/core/browser/url_database.cc
+++ b/chromium/components/history/core/browser/url_database.cc
@@ -15,6 +15,7 @@
#include "base/time/time.h"
#include "components/database_utils/url_converter.h"
#include "components/history/core/browser/keyword_search_term.h"
+#include "components/history/core/browser/keyword_search_term_util.h"
#include "components/url_formatter/url_formatter.h"
#include "sql/statement.h"
#include "url/gurl.h"
@@ -577,7 +578,7 @@ void URLDatabase::GetMostRecentKeywordSearchTerms(
KeywordID keyword_id,
const std::u16string& prefix,
int max_count,
- std::vector<KeywordSearchTermVisit>* matches) {
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>>* visits) {
// NOTE: the keyword_id can be zero if on first run the user does a query
// before the TemplateURLService has finished loading. As the chances of this
// occurring are small, we ignore it.
@@ -587,11 +588,12 @@ void URLDatabase::GetMostRecentKeywordSearchTerms(
DCHECK(!prefix.empty());
sql::Statement statement(GetDB().GetCachedStatement(
SQL_FROM_HERE,
- "SELECT DISTINCT kv.term, u.visit_count, u.last_visit_time "
- "FROM keyword_search_terms kv "
- "JOIN urls u ON kv.url_id = u.id "
- "WHERE kv.keyword_id = ? AND kv.normalized_term >= ? AND "
- "kv.normalized_term < ? "
+ "SELECT DISTINCT kst.term, kst.normalized_term, u.visit_count, "
+ "u.last_visit_time "
+ "FROM keyword_search_terms kst "
+ "JOIN urls u ON kst.url_id = u.id "
+ "WHERE kst.keyword_id = ? AND kst.normalized_term >= ? AND "
+ "kst.normalized_term < ? "
"ORDER BY u.last_visit_time DESC LIMIT ?"));
// NOTE: Keep these CollapseWhitespace() and ToLower() calls in sync with
@@ -606,24 +608,66 @@ void URLDatabase::GetMostRecentKeywordSearchTerms(
statement.BindString16(2, next_prefix);
statement.BindInt(3, max_count);
- KeywordSearchTermVisit visit;
while (statement.Step()) {
- visit.term = statement.ColumnString16(0);
- visit.visits = statement.ColumnInt(1);
- visit.time = base::Time::FromInternalValue(statement.ColumnInt64(2));
- matches->push_back(visit);
+ auto visit = std::make_unique<KeywordSearchTermVisit>();
+ visit->term = statement.ColumnString16(0);
+ visit->normalized_term = statement.ColumnString16(1);
+ visit->visit_count = statement.ColumnInt(2);
+ visit->last_visit_time =
+ base::Time::FromInternalValue(statement.ColumnInt64(3));
+ visits->push_back(std::move(visit));
}
}
-std::vector<NormalizedKeywordSearchTermVisit>
-URLDatabase::GetMostRecentNormalizedKeywordSearchTerms(
+std::unique_ptr<KeywordSearchTermVisitEnumerator>
+URLDatabase::CreateKeywordSearchTermVisitEnumerator(
KeywordID keyword_id,
- base::Time age_threshold) {
+ const std::u16string& prefix) {
// NOTE: the keyword_id can be zero if on first run the user does a query
// before the TemplateURLService has finished loading. As the chances of this
// occurring are small, we ignore it.
if (!keyword_id)
- return {};
+ return nullptr;
+
+ auto enumerator = base::WrapUnique<KeywordSearchTermVisitEnumerator>(
+ new KeywordSearchTermVisitEnumerator());
+ enumerator->statement_.Assign(GetDB().GetCachedStatement(SQL_FROM_HERE,
+ R"(
+ SELECT
+ kst.term,
+ kst.normalized_term,
+ u.visit_count,
+ u.last_visit_time
+ FROM
+ keyword_search_terms kst JOIN urls u ON kst.url_id = u.id
+ WHERE
+ kst.keyword_id = ? AND
+ kst.normalized_term >= ? AND
+ kst.normalized_term < ?
+ ORDER BY kst.normalized_term, u.last_visit_time
+ )"));
+ // Keep CollapseWhitespace() and ToLower() in sync with search_provider.cc.
+ std::u16string normalized_prefix =
+ base::CollapseWhitespace(base::i18n::ToLower(prefix), false);
+ // This magic gives us a prefix search.
+ std::u16string next_prefix = normalized_prefix;
+ next_prefix.back() = next_prefix.back() + 1;
+ enumerator->statement_.BindInt64(0, keyword_id);
+ enumerator->statement_.BindString16(1, normalized_prefix);
+ enumerator->statement_.BindString16(2, next_prefix);
+ enumerator->initialized_ = enumerator->statement_.is_valid();
+ return enumerator;
+}
+
+void URLDatabase::GetMostRecentKeywordSearchTerms(
+ KeywordID keyword_id,
+ base::Time age_threshold,
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>>* visits) {
+ // NOTE: the keyword_id can be zero if on first run the user does a query
+ // before the TemplateURLService has finished loading. As the chances of this
+ // occurring are small, we ignore it.
+ if (!keyword_id)
+ return;
// Extracts the most recent normalized search terms from the
// keyword_search_terms table joined with the urls table. For a given search
@@ -639,23 +683,25 @@ URLDatabase::GetMostRecentNormalizedKeywordSearchTerms(
R"(
SELECT
normalized_term,
+ MAX(term) AS term,
SUM(visit_count) AS visit_count,
MAX(last_visit_time) AS last_visit_time
FROM
(
SELECT
normalized_term,
- AVG(visit_count) AS visit_count,
+ MIN(kst.term) AS term,
+ AVG(u.visit_count) AS visit_count,
MIN(u.last_visit_time) AS last_visit_time,
u.last_visit_time - (u.last_visit_time % ?) as rnd_last_visit_time
FROM
- keyword_search_terms kv JOIN urls u ON kv.url_id = u.id
+ keyword_search_terms kst JOIN urls u ON kst.url_id = u.id
WHERE
- kv.keyword_id = ?
+ kst.keyword_id = ?
AND u.last_visit_time > ?
- AND kv.normalized_term IS NOT NULL
- AND kv.normalized_term != ''
- GROUP BY normalized_term, rnd_last_visit_time
+ AND kst.normalized_term IS NOT NULL
+ AND kst.normalized_term != ''
+ GROUP BY kst.normalized_term, rnd_last_visit_time
)
GROUP BY normalized_term
ORDER BY last_visit_time DESC
@@ -666,16 +712,47 @@ URLDatabase::GetMostRecentNormalizedKeywordSearchTerms(
statement.BindInt64(1, keyword_id);
statement.BindInt64(2, age_threshold.ToInternalValue());
- std::vector<NormalizedKeywordSearchTermVisit> visits;
while (statement.Step()) {
- NormalizedKeywordSearchTermVisit visit;
- visit.normalized_term = statement.ColumnString16(0);
- visit.visits = statement.ColumnInt(1);
- visit.most_recent_visit_time =
- base::Time::FromInternalValue(statement.ColumnInt64(2));
- visits.push_back(visit);
+ auto visit = std::make_unique<KeywordSearchTermVisit>();
+ visit->normalized_term = statement.ColumnString16(0);
+ visit->term = statement.ColumnString16(1);
+ visit->visit_count = statement.ColumnInt(2);
+ visit->last_visit_time =
+ base::Time::FromInternalValue(statement.ColumnInt64(3));
+ visits->push_back(std::move(visit));
}
- return visits;
+}
+
+std::unique_ptr<KeywordSearchTermVisitEnumerator>
+URLDatabase::CreateKeywordSearchTermVisitEnumerator(KeywordID keyword_id,
+ base::Time age_threshold) {
+ // NOTE: the keyword_id can be zero if on first run the user does a query
+ // before the TemplateURLService has finished loading. As the chances of this
+ // occurring are small, we ignore it.
+ if (!keyword_id)
+ return nullptr;
+
+ auto enumerator = base::WrapUnique<KeywordSearchTermVisitEnumerator>(
+ new KeywordSearchTermVisitEnumerator());
+ enumerator->statement_.Assign(GetDB().GetCachedStatement(SQL_FROM_HERE,
+ R"(
+ SELECT
+ kst.term,
+ kst.normalized_term,
+ u.visit_count,
+ u.last_visit_time
+ FROM
+ keyword_search_terms kst JOIN urls u ON kst.url_id = u.id
+ WHERE
+ kst.keyword_id = ? AND
+ u.last_visit_time > ? AND
+ kst.normalized_term <> ''
+ ORDER BY kst.normalized_term, u.last_visit_time
+ )"));
+ enumerator->statement_.BindInt64(0, keyword_id);
+ enumerator->statement_.BindInt64(1, age_threshold.ToInternalValue());
+ enumerator->initialized_ = enumerator->statement_.is_valid();
+ return enumerator;
}
bool URLDatabase::DeleteKeywordSearchTerm(const std::u16string& term) {
@@ -774,9 +851,6 @@ const int kLowQualityMatchTypedLimit = 1;
const int kLowQualityMatchVisitLimit = 4;
const int kLowQualityMatchAgeLimitInDays = 3;
-const base::TimeDelta kAutocompleteDuplicateVisitIntervalThreshold =
- base::Minutes(5);
-
base::Time AutocompleteAgeThreshold() {
return (base::Time::Now() - base::Days(kLowQualityMatchAgeLimitInDays));
}
diff --git a/chromium/components/history/core/browser/url_database.h b/chromium/components/history/core/browser/url_database.h
index 2bbce1926d9..e40ab3e880c 100644
--- a/chromium/components/history/core/browser/url_database.h
+++ b/chromium/components/history/core/browser/url_database.h
@@ -28,9 +28,9 @@ class Database;
namespace history {
+class KeywordSearchTermVisitEnumerator;
struct KeywordSearchTermRow;
struct KeywordSearchTermVisit;
-struct NormalizedKeywordSearchTermVisit;
class VisitDatabase; // For friend statement.
@@ -219,18 +219,38 @@ class URLDatabase {
// Returns up to max_count of the most recent search terms for the specified
// keyword.
+ // TODO(crbug.com/1119654): Remove this in favor of the enumerator-based
+ // function below after experimentation.
void GetMostRecentKeywordSearchTerms(
KeywordID keyword_id,
const std::u16string& prefix,
int max_count,
- std::vector<KeywordSearchTermVisit>* matches);
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>>* visits);
+
+ // Returns an enumerator to enumerate all the KeywordSearchTermVisits starting
+ // with `prefix` for the specified keyword. The visits are ordered first by
+ // |normalized_term| and then by |last_visit_time| in ascending order, i.e.,
+ // from the oldest to the newest.
+ std::unique_ptr<KeywordSearchTermVisitEnumerator>
+ CreateKeywordSearchTermVisitEnumerator(KeywordID keyword_id,
+ const std::u16string& prefix);
+
+ // Returns the most recent (no older than `age_threshold`) search terms for
+ // the specified keyword.
+ // TODO(crbug.com/1119654): Remove this in favor of the enumerator-based
+ // function below after experimentation.
+ void GetMostRecentKeywordSearchTerms(
+ KeywordID keyword_id,
+ base::Time age_threshold,
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>>* visits);
- // Returns the most recent (i.e., no older than `age_threshold`) normalized
- // search terms (i.e., search terms in lower case with whitespaces collapsed)
- // for the specified keyword.
- std::vector<NormalizedKeywordSearchTermVisit>
- GetMostRecentNormalizedKeywordSearchTerms(KeywordID keyword_id,
- base::Time age_threshold);
+ // Returns an enumerator to enumerate all the KeywordSearchTermVisits no older
+ // than `age_threshold` for the given keyword. The visits are ordered first by
+ // |normalized_term| and then by |last_visit_time| in ascending order, i.e.,
+ // from the oldest to the newest.
+ std::unique_ptr<KeywordSearchTermVisitEnumerator>
+ CreateKeywordSearchTermVisitEnumerator(KeywordID keyword_id,
+ base::Time age_threshold);
// Deletes all searches matching `term`.
bool DeleteKeywordSearchTerm(const std::u16string& term);
@@ -292,7 +312,7 @@ class URLDatabase {
// be used in between CreateTemporaryURLTable() and CommitTemporaryURLTable().
URLID AddURLInternal(const URLRow& info, bool is_temporary);
- // Return ture if the urls table's schema contains "AUTOINCREMENT".
+ // Return true if the urls table's schema contains "AUTOINCREMENT".
// false if table do not contain AUTOINCREMENT, or the table is not created.
bool URLTableContainsAutoincrement();
@@ -333,15 +353,6 @@ extern const int kLowQualityMatchTypedLimit;
extern const int kLowQualityMatchVisitLimit;
extern const int kLowQualityMatchAgeLimitInDays;
-// The time interval within which a duplicate query is considered invalid for
-// autocomplete purposes.
-// These invalid duplicates are extracted from search query URLs which are
-// identical or nearly identical to the original search query URL and issued too
-// closely to it, i.e., within this time interval. They are typically recorded
-// as a result of back/forward navigations or user interactions in the search
-// result page and are likely not newly initiated searches.
-extern const base::TimeDelta kAutocompleteDuplicateVisitIntervalThreshold;
-
// Returns the date threshold for considering an history item as significant.
base::Time AutocompleteAgeThreshold();
diff --git a/chromium/components/history/core/browser/url_database_unittest.cc b/chromium/components/history/core/browser/url_database_unittest.cc
index e6be3eac1e7..2bdb5c256a1 100644
--- a/chromium/components/history/core/browser/url_database_unittest.cc
+++ b/chromium/components/history/core/browser/url_database_unittest.cc
@@ -8,6 +8,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/strings/utf_string_conversions.h"
#include "components/history/core/browser/keyword_search_term.h"
+#include "components/history/core/browser/keyword_search_term_util.h"
#include "sql/database.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -168,54 +169,352 @@ TEST_F(URLDatabaseTest, AddAndUpdateURL) {
// EXPECT_TRUE(db.GetURLInfo(url2, NULL) == NULL);
}
-// Tests adding, querying and deleting keyword visits.
-TEST_F(URLDatabaseTest, KeywordSearchTermVisit) {
- URLRow url_info1(GURL("http://www.google.com/"));
- url_info1.set_title(u"Google");
- url_info1.set_visit_count(4);
- url_info1.set_typed_count(2);
- url_info1.set_last_visit(Time::Now() - base::Days(1));
- url_info1.set_hidden(false);
- URLID url_id = AddURL(url_info1);
- ASSERT_NE(0, url_id);
+// Tests querying prefix keyword search terms.
+TEST_F(URLDatabaseTest, KeywordSearchTerms_Prefix) {
+ KeywordID keyword_id = 100;
+ // Choose the local midnight of yesterday as the baseline for the time.
+ base::Time local_midnight = Time::Now().LocalMidnight() - base::Days(1);
+
+ // First search for "foo".
+ URLRow foo_url_1(GURL("https://www.google.com/search?q=Foo&num=1"));
+ foo_url_1.set_visit_count(1);
+ foo_url_1.set_last_visit(local_midnight + base::Hours(1));
+ URLID foo_url_1_id = AddURL(foo_url_1);
+ ASSERT_NE(0, foo_url_1_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_1_id, keyword_id, u"Foo"));
+
+ // Second search for "foo".
+ URLRow foo_url_2(GURL("https://www.google.com/search?q=FOo&num=2"));
+ foo_url_2.set_visit_count(1);
+ foo_url_2.set_last_visit(local_midnight + base::Hours(2));
+ URLID foo_url_2_id = AddURL(foo_url_2);
+ ASSERT_NE(0, foo_url_2_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_2_id, keyword_id, u"FOo"));
+
+ // Third search for "foo".
+ URLRow foo_url_3(GURL("https://www.google.com/search?q=FOO&num=3"));
+ foo_url_3.set_visit_count(1);
+ foo_url_3.set_last_visit(local_midnight + base::Hours(3));
+ URLID foo_url_3_id = AddURL(foo_url_3);
+ ASSERT_NE(0, foo_url_3_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_3_id, keyword_id, u"FOO"));
+
+ // First search for "bar".
+ URLRow bar_url_1(GURL("https://www.google.com/search?q=BAR&num=4"));
+ bar_url_1.set_visit_count(1);
+ bar_url_1.set_last_visit(local_midnight + base::Hours(4));
+ URLID bar_url_1_id = AddURL(bar_url_1);
+ ASSERT_NE(0, bar_url_1_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(bar_url_1_id, keyword_id, u"BAR"));
+
+ // First search for "food".
+ URLRow food_url_1(GURL("https://www.google.com/search?q=Food&num=1"));
+ food_url_1.set_visit_count(1);
+ food_url_1.set_last_visit(local_midnight + base::Hours(5));
+ URLID food_url_1_id = AddURL(food_url_1);
+ ASSERT_NE(0, food_url_1_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(food_url_1_id, keyword_id, u"Food"));
+
+ // Make sure we get "food" and "foo" back with the last term and visit time
+ // that generated the normalized search terms.
+ // In fact we get near-duplicate matches if different search terms generated
+ // the same normalized search term.
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>> matches;
+ GetMostRecentKeywordSearchTerms(keyword_id, u"f", 10, &matches);
+ ASSERT_EQ(4U, matches.size());
+ EXPECT_EQ(u"Food", matches[0]->term);
+ EXPECT_EQ(u"food", matches[0]->normalized_term);
+ EXPECT_EQ(1, matches[0]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(5), matches[0]->last_visit_time);
+ EXPECT_EQ(u"FOO", matches[1]->term);
+ EXPECT_EQ(u"foo", matches[1]->normalized_term);
+ EXPECT_EQ(1, matches[1]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(3), matches[1]->last_visit_time);
+ EXPECT_EQ(u"FOo", matches[2]->term);
+ EXPECT_EQ(u"foo", matches[2]->normalized_term);
+ EXPECT_EQ(1, matches[2]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(2), matches[2]->last_visit_time);
+ EXPECT_EQ(u"Foo", matches[3]->term);
+ EXPECT_EQ(u"foo", matches[3]->normalized_term);
+ EXPECT_EQ(1, matches[3]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(1), matches[3]->last_visit_time);
+
+ // We could miss out on potential visits and/or matches if not requesting
+ // enough matches.
+ matches.clear();
+ GetMostRecentKeywordSearchTerms(keyword_id, u"f", 2, &matches);
+ ASSERT_EQ(2U, matches.size());
+ EXPECT_EQ(u"Food", matches[0]->term);
+ EXPECT_EQ(u"food", matches[0]->normalized_term);
+ EXPECT_EQ(1, matches[0]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(5), matches[0]->last_visit_time);
+ EXPECT_EQ(u"FOO", matches[1]->term);
+ EXPECT_EQ(u"foo", matches[1]->normalized_term);
+ EXPECT_EQ(1, matches[1]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(3), matches[1]->last_visit_time);
+
+ // CreateKeywordSearchTermVisitEnumerator solves that problem by accumulating
+ // the visits to unique normalized search terms.
+ auto enumerator = CreateKeywordSearchTermVisitEnumerator(keyword_id, u"f");
+ ASSERT_TRUE(enumerator);
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>> matches_v2;
+ GetAutocompleteSearchTermsFromEnumerator(
+ *enumerator, /*ignore_duplicate_visits=*/false,
+ SearchTermRankingPolicy::kRecency, &matches_v2);
+ ASSERT_EQ(2U, matches_v2.size());
+ EXPECT_EQ(u"Food", matches_v2[0]->term);
+ EXPECT_EQ(u"food", matches_v2[0]->normalized_term);
+ EXPECT_EQ(1, matches_v2[0]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(5), matches_v2[0]->last_visit_time);
+ EXPECT_EQ(u"FOO", matches_v2[1]->term);
+ EXPECT_EQ(u"foo", matches_v2[1]->normalized_term);
+ EXPECT_EQ(3, matches_v2[1]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(3), matches_v2[1]->last_visit_time);
- // Add a keyword visit.
+ KeywordSearchTermRow keyword_search_term_row;
+ ASSERT_TRUE(GetKeywordSearchTermRow(foo_url_3_id, &keyword_search_term_row));
+ EXPECT_EQ(keyword_id, keyword_search_term_row.keyword_id);
+ EXPECT_EQ(foo_url_3_id, keyword_search_term_row.url_id);
+ EXPECT_EQ(u"FOO", keyword_search_term_row.term);
+ ASSERT_TRUE(GetKeywordSearchTermRow(food_url_1_id, &keyword_search_term_row));
+ EXPECT_EQ(keyword_id, keyword_search_term_row.keyword_id);
+ EXPECT_EQ(food_url_1_id, keyword_search_term_row.url_id);
+ EXPECT_EQ(u"Food", keyword_search_term_row.term);
+
+ // Delete all the search terms for the keyword.
+ DeleteAllSearchTermsForKeyword(keyword_id);
+
+ // Make sure we get nothing back.
+ matches.clear();
+ GetMostRecentKeywordSearchTerms(keyword_id, u"f", 10, &matches);
+ ASSERT_EQ(0U, matches.size());
+
+ enumerator = CreateKeywordSearchTermVisitEnumerator(keyword_id, u"f");
+ ASSERT_TRUE(enumerator);
+ matches_v2.clear();
+ GetAutocompleteSearchTermsFromEnumerator(
+ *enumerator, /*ignore_duplicate_visits=*/false,
+ SearchTermRankingPolicy::kRecency, &matches_v2);
+ ASSERT_EQ(0U, matches_v2.size());
+
+ ASSERT_FALSE(GetKeywordSearchTermRow(foo_url_3_id, &keyword_search_term_row));
+}
+
+// Tests querying zero-prefix keyword search terms.
+TEST_F(URLDatabaseTest, KeywordSearchTerms_ZeroPrefix) {
KeywordID keyword_id = 100;
- std::u16string keyword = u" VISIT ";
- std::u16string normalized_keyword = u"visit";
- ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id, keyword_id, keyword));
-
- // Make sure we get it back.
- std::vector<KeywordSearchTermVisit> matches;
- GetMostRecentKeywordSearchTerms(keyword_id, u"vi", 10, &matches);
- ASSERT_EQ(1U, matches.size());
- ASSERT_EQ(keyword, matches[0].term);
-
- std::vector<NormalizedKeywordSearchTermVisit> zero_prefix_matches =
- GetMostRecentNormalizedKeywordSearchTerms(
- keyword_id, history::AutocompleteAgeThreshold());
- ASSERT_EQ(1U, zero_prefix_matches.size());
- ASSERT_EQ(normalized_keyword, zero_prefix_matches[0].normalized_term);
+ // Choose the local midnight of yesterday as the baseline for the time.
+ base::Time local_midnight = Time::Now().LocalMidnight() - base::Days(1);
+
+ // First search for "foo".
+ URLRow foo_url_1(GURL("https://www.google.com/search?q=Foo&num=1"));
+ foo_url_1.set_visit_count(1);
+ foo_url_1.set_last_visit(local_midnight + base::Hours(1));
+ URLID foo_url_1_id = AddURL(foo_url_1);
+ ASSERT_NE(0, foo_url_1_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_1_id, keyword_id, u"Foo"));
+
+ // Second search for "foo".
+ URLRow foo_url_2(GURL("https://www.google.com/search?q=FOo&num=2"));
+ foo_url_2.set_visit_count(1);
+ foo_url_2.set_last_visit(local_midnight + base::Hours(2));
+ URLID foo_url_2_id = AddURL(foo_url_2);
+ ASSERT_NE(0, foo_url_2_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_2_id, keyword_id, u"FOo"));
+
+ // Third search for "foo".
+ URLRow foo_url_3(GURL("https://www.google.com/search?q=FOO&num=3"));
+ foo_url_3.set_visit_count(1);
+ foo_url_3.set_last_visit(local_midnight + base::Hours(3));
+ URLID foo_url_3_id = AddURL(foo_url_3);
+ ASSERT_NE(0, foo_url_3_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_3_id, keyword_id, u"FOO"));
+
+ // First search for "bar".
+ URLRow bar_url_1(GURL("https://www.google.com/search?q=BAR&num=4"));
+ bar_url_1.set_visit_count(1);
+ bar_url_1.set_last_visit(local_midnight + base::Hours(4));
+ URLID bar_url_1_id = AddURL(bar_url_1);
+ ASSERT_NE(0, bar_url_1_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(bar_url_1_id, keyword_id, u"BAR"));
+
+ // Fourth search for "foo".
+ // This search will be ignored for being too close to previous search.
+ URLRow foo_url_4(GURL("https://www.google.com/search?q=foo&num=4"));
+ foo_url_4.set_visit_count(1);
+ foo_url_4.set_last_visit(local_midnight + base::Hours(3));
+ URLID foo_url_4_id = AddURL(foo_url_4);
+ ASSERT_NE(0, foo_url_4_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_4_id, keyword_id, u"foo"));
+
+ // Make sure we get both "foo" and "bar" back. "bar" should come first since
+ // it was searched for most recently.
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>> matches;
+ GetMostRecentKeywordSearchTerms(
+ keyword_id, history::AutocompleteAgeThreshold(), &matches);
+ ASSERT_EQ(2U, matches.size());
+ EXPECT_EQ(u"BAR", matches[0]->term);
+ EXPECT_EQ(u"bar", matches[0]->normalized_term);
+ EXPECT_EQ(1, matches[0]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(4), matches[0]->last_visit_time);
+ EXPECT_EQ(u"Foo", matches[1]->term);
+ EXPECT_EQ(u"foo", matches[1]->normalized_term);
+ EXPECT_EQ(3, matches[1]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(3), matches[1]->last_visit_time);
+
+ // Make sure we get both "foo" and "bar" back. "foo" should come first since
+ // it has more visits and thus a higher frecency score.
+ auto enumerator = CreateKeywordSearchTermVisitEnumerator(
+ keyword_id, history::AutocompleteAgeThreshold());
+ ASSERT_TRUE(enumerator);
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>> matches_v2;
+ GetAutocompleteSearchTermsFromEnumerator(
+ *enumerator, /*ignore_duplicate_visits=*/true,
+ SearchTermRankingPolicy::kFrecency, &matches_v2);
+ ASSERT_EQ(2U, matches_v2.size());
+ EXPECT_EQ(u"FOO", matches_v2[0]->term);
+ EXPECT_EQ(u"foo", matches_v2[0]->normalized_term);
+ EXPECT_EQ(3, matches_v2[0]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(3), matches_v2[0]->last_visit_time);
+ EXPECT_EQ(u"BAR", matches_v2[1]->term);
+ EXPECT_EQ(u"bar", matches_v2[1]->normalized_term);
+ EXPECT_EQ(1, matches_v2[1]->visit_count);
+ EXPECT_EQ(local_midnight + base::Hours(4), matches_v2[1]->last_visit_time);
KeywordSearchTermRow keyword_search_term_row;
- ASSERT_TRUE(GetKeywordSearchTermRow(url_id, &keyword_search_term_row));
+ ASSERT_TRUE(GetKeywordSearchTermRow(foo_url_3_id, &keyword_search_term_row));
EXPECT_EQ(keyword_id, keyword_search_term_row.keyword_id);
- EXPECT_EQ(url_id, keyword_search_term_row.url_id);
- EXPECT_EQ(keyword, keyword_search_term_row.term);
+ EXPECT_EQ(foo_url_3_id, keyword_search_term_row.url_id);
+ EXPECT_EQ(u"FOO", keyword_search_term_row.term);
+ ASSERT_TRUE(GetKeywordSearchTermRow(bar_url_1_id, &keyword_search_term_row));
+ EXPECT_EQ(keyword_id, keyword_search_term_row.keyword_id);
+ EXPECT_EQ(bar_url_1_id, keyword_search_term_row.url_id);
+ EXPECT_EQ(u"BAR", keyword_search_term_row.term);
- // Delete the keyword visit.
+ // Delete all the search terms for the keyword.
DeleteAllSearchTermsForKeyword(keyword_id);
- // Make sure we don't get it back when querying.
+ // Make sure we get nothing back.
matches.clear();
- GetMostRecentKeywordSearchTerms(keyword_id, keyword, 10, &matches);
+ GetMostRecentKeywordSearchTerms(
+ keyword_id, history::AutocompleteAgeThreshold(), &matches);
ASSERT_EQ(0U, matches.size());
- zero_prefix_matches = GetMostRecentNormalizedKeywordSearchTerms(
+ enumerator = CreateKeywordSearchTermVisitEnumerator(
+ keyword_id, history::AutocompleteAgeThreshold());
+ ASSERT_TRUE(enumerator);
+ matches_v2.clear();
+ GetAutocompleteSearchTermsFromEnumerator(
+ *enumerator, /*ignore_duplicate_visits=*/true,
+ SearchTermRankingPolicy::kFrecency, &matches_v2);
+ ASSERT_EQ(0U, matches_v2.size());
+
+ ASSERT_FALSE(GetKeywordSearchTermRow(foo_url_3_id, &keyword_search_term_row));
+}
+
+// Tests querying most repeated keyword search terms.
+TEST_F(URLDatabaseTest, KeywordSearchTerms_MostRepeated) {
+ KeywordID keyword_id = 100;
+ // Choose the local midnight of yesterday as the baseline for the time.
+ base::Time local_midnight = Time::Now().LocalMidnight() - base::Days(1);
+
+ // First search for "foo" - yesterday.
+ URLRow foo_url_1(GURL("https://www.google.com/search?q=foo&num=1"));
+ foo_url_1.set_visit_count(1);
+ foo_url_1.set_last_visit(local_midnight - base::Days(1) + base::Hours(1));
+ URLID foo_url_1_id = AddURL(foo_url_1);
+ ASSERT_NE(0, foo_url_1_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_1_id, keyword_id, u"foo"));
+
+ // First search for "bar" - yesterday.
+ URLRow bar_url_1(GURL("https://www.google.com/search?q=bar&num=1"));
+ bar_url_1.set_visit_count(1);
+ bar_url_1.set_last_visit(local_midnight - base::Days(1) + base::Hours(2));
+ URLID bar_url_1_id = AddURL(bar_url_1);
+ ASSERT_NE(0, bar_url_1_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(bar_url_1_id, keyword_id, u"bar"));
+
+ // Second search for "bar" - yesterday.
+ URLRow bar_url_2(GURL("https://www.google.com/search?q=Bar&num=2"));
+ bar_url_2.set_visit_count(1);
+ bar_url_2.set_last_visit(local_midnight - base::Days(1) + base::Hours(3));
+ URLID bar_url_2_id = AddURL(bar_url_2);
+ ASSERT_NE(0, bar_url_2_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(bar_url_2_id, keyword_id, u"Bar"));
+
+ // Second search for "foo" - yesterday.
+ URLRow foo_url_2(GURL("https://www.google.com/search?q=Foo&num=2"));
+ foo_url_2.set_visit_count(1);
+ foo_url_2.set_last_visit(local_midnight - base::Days(1) + base::Hours(4));
+ URLID foo_url_2_id = AddURL(foo_url_2);
+ ASSERT_NE(0, foo_url_2_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_2_id, keyword_id, u"Foo"));
+
+ // Third search for "bar" - today.
+ // This search will be ignored for having a visit count of 0.
+ URLRow bar_url_3(GURL("https://www.google.com/search?q=BAr&num=3"));
+ bar_url_3.set_visit_count(0);
+ bar_url_3.set_last_visit(local_midnight + base::Hours(1));
+ URLID bar_url_3_id = AddURL(bar_url_3);
+ ASSERT_NE(0, bar_url_3_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(bar_url_3_id, keyword_id, u"BAr"));
+
+ // Third search for "foo" - today.
+ // This search will be ignored for having a visit count of 0.
+ URLRow foo_url_3(GURL("https://www.google.com/search?q=FOo&num=3"));
+ foo_url_3.set_visit_count(0);
+ foo_url_3.set_last_visit(local_midnight + base::Hours(2));
+ URLID foo_url_3_id = AddURL(foo_url_3);
+ ASSERT_NE(0, foo_url_3_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_3_id, keyword_id, u"FOo"));
+
+ // Fourth search for "bar" - today.
+ URLRow bar_url_4(GURL("https://www.google.com/search?q=BAR&num=4"));
+ bar_url_4.set_visit_count(1);
+ bar_url_4.set_last_visit(local_midnight + base::Hours(3));
+ URLID bar_url_4_id = AddURL(bar_url_4);
+ ASSERT_NE(0, bar_url_4_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(bar_url_4_id, keyword_id, u"BAR"));
+
+ // Fourth search for "foo" - today.
+ URLRow foo_url_4(GURL("https://www.google.com/search?q=FOO&num=4"));
+ foo_url_4.set_visit_count(1);
+ foo_url_4.set_last_visit(local_midnight + base::Hours(4));
+ URLID foo_url_4_id = AddURL(foo_url_4);
+ ASSERT_NE(0, foo_url_4_id);
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(foo_url_4_id, keyword_id, u"FOO"));
+
+ // Make sure we get both "foo" and "bar" back. search terms with identical
+ // scores are ranked in alphabetical order.
+ auto enumerator = CreateKeywordSearchTermVisitEnumerator(
keyword_id, history::AutocompleteAgeThreshold());
- ASSERT_EQ(0U, zero_prefix_matches.size());
+ ASSERT_TRUE(enumerator);
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>> matches;
+ GetMostRepeatedSearchTermsFromEnumerator(*enumerator, &matches);
+ ASSERT_EQ(2U, matches.size());
+ EXPECT_EQ(matches[0]->score, matches[1]->score);
+ EXPECT_EQ(u"BAR", matches[0]->term);
+ EXPECT_EQ(u"bar", matches[0]->normalized_term);
+ EXPECT_EQ(u"FOO", matches[1]->term);
+ EXPECT_EQ(u"foo", matches[1]->normalized_term);
+
+ KeywordSearchTermRow keyword_search_term_row;
+ ASSERT_TRUE(GetKeywordSearchTermRow(foo_url_4_id, &keyword_search_term_row));
+ ASSERT_TRUE(GetKeywordSearchTermRow(bar_url_4_id, &keyword_search_term_row));
+
+ // Delete all the search terms for the keyword.
+ DeleteAllSearchTermsForKeyword(keyword_id);
- ASSERT_FALSE(GetKeywordSearchTermRow(url_id, &keyword_search_term_row));
+ ASSERT_FALSE(GetKeywordSearchTermRow(foo_url_4_id, &keyword_search_term_row));
+ ASSERT_FALSE(GetKeywordSearchTermRow(bar_url_4_id, &keyword_search_term_row));
+
+ // Make sure we get nothing back.
+ enumerator = CreateKeywordSearchTermVisitEnumerator(
+ keyword_id, history::AutocompleteAgeThreshold());
+ ASSERT_TRUE(enumerator);
+ matches.clear();
+ GetMostRepeatedSearchTermsFromEnumerator(*enumerator, &matches);
+ ASSERT_EQ(0U, matches.size());
}
// Make sure deleting a URL also deletes a keyword visit.
@@ -236,7 +535,7 @@ TEST_F(URLDatabaseTest, DeleteURLDeletesKeywordSearchTermVisit) {
ASSERT_TRUE(DeleteURLRow(url_id));
// Make sure the keyword visit was deleted.
- std::vector<KeywordSearchTermVisit> matches;
+ std::vector<std::unique_ptr<KeywordSearchTermVisit>> matches;
GetMostRecentKeywordSearchTerms(1, u"visit", 10, &matches);
ASSERT_EQ(0U, matches.size());
}
diff --git a/chromium/components/history/core/browser/url_row.cc b/chromium/components/history/core/browser/url_row.cc
index f8bd64ed0a5..e2a9a19eec9 100644
--- a/chromium/components/history/core/browser/url_row.cc
+++ b/chromium/components/history/core/browser/url_row.cc
@@ -133,12 +133,14 @@ VisitContentAnnotations::VisitContentAnnotations(
VisitContentModelAnnotations model_annotations,
const std::vector<std::string>& related_searches,
const GURL& search_normalized_url,
- const std::u16string& search_terms)
+ const std::u16string& search_terms,
+ const std::string& alternative_title)
: annotation_flags(annotation_flags),
model_annotations(model_annotations),
related_searches(related_searches),
search_normalized_url(search_normalized_url),
- search_terms(search_terms) {}
+ search_terms(search_terms),
+ alternative_title(alternative_title) {}
VisitContentAnnotations::VisitContentAnnotations() = default;
VisitContentAnnotations::VisitContentAnnotations(
const VisitContentAnnotations&) = default;
diff --git a/chromium/components/history/core/browser/url_row.h b/chromium/components/history/core/browser/url_row.h
index 96849ebf104..b7e03fbe519 100644
--- a/chromium/components/history/core/browser/url_row.h
+++ b/chromium/components/history/core/browser/url_row.h
@@ -240,7 +240,8 @@ struct VisitContentAnnotations {
VisitContentModelAnnotations model_annotations,
const std::vector<std::string>& related_searches,
const GURL& search_normalized_url,
- const std::u16string& search_terms);
+ const std::u16string& search_terms,
+ const std::string& alternative_title);
VisitContentAnnotations(const VisitContentAnnotations& other);
~VisitContentAnnotations();
@@ -251,6 +252,8 @@ struct VisitContentAnnotations {
std::vector<std::string> related_searches;
GURL search_normalized_url;
std::u16string search_terms;
+ // Alternative page title for the visit.
+ std::string alternative_title;
};
class URLResult : public URLRow {
diff --git a/chromium/components/history/core/browser/visit_annotations_database.cc b/chromium/components/history/core/browser/visit_annotations_database.cc
index dbeba7d4449..a6846554f7b 100644
--- a/chromium/components/history/core/browser/visit_annotations_database.cc
+++ b/chromium/components/history/core/browser/visit_annotations_database.cc
@@ -24,7 +24,7 @@ namespace {
#define HISTORY_CONTENT_ANNOTATIONS_ROW_FIELDS \
" visit_id,visibility_score,categories,page_topics_model_version," \
"annotation_flags,entities,related_searches,search_normalized_url," \
- "search_terms "
+ "search_terms,alternative_title "
#define HISTORY_CONTEXT_ANNOTATIONS_ROW_FIELDS \
" visit_id,context_annotation_flags,duration_since_last_visit," \
"page_end_reason,total_foreground_duration "
@@ -169,7 +169,8 @@ bool VisitAnnotationsDatabase::InitVisitAnnotationsTables() {
"entities VARCHAR,"
"related_searches VARCHAR,"
"search_normalized_url VARCHAR,"
- "search_terms LONGVARCHAR)")) {
+ "search_terms LONGVARCHAR,"
+ "alternative_title VARCHAR)")) {
return false;
}
@@ -226,7 +227,7 @@ void VisitAnnotationsDatabase::AddContentAnnotationsForVisit(
sql::Statement statement(GetDB().GetCachedStatement(
SQL_FROM_HERE,
"INSERT INTO content_annotations(" HISTORY_CONTENT_ANNOTATIONS_ROW_FIELDS
- ")VALUES(?,?,?,?,?,?,?,?,?)"));
+ ")VALUES(?,?,?,?,?,?,?,?,?,?)"));
statement.BindInt64(0, visit_id);
statement.BindDouble(
1, static_cast<double>(
@@ -245,6 +246,7 @@ void VisitAnnotationsDatabase::AddContentAnnotationsForVisit(
statement.BindString(7,
visit_content_annotations.search_normalized_url.spec());
statement.BindString16(8, visit_content_annotations.search_terms);
+ statement.BindString(9, visit_content_annotations.alternative_title);
if (!statement.Run()) {
DVLOG(0) << "Failed to execute 'content_annotations' insert statement: "
@@ -285,7 +287,8 @@ void VisitAnnotationsDatabase::UpdateContentAnnotationsForVisit(
"visibility_score=?,categories=?,"
"page_topics_model_version=?,"
"annotation_flags=?,entities=?,"
- "related_searches=?,search_normalized_url=?,search_terms=? "
+ "related_searches=?,search_normalized_url=?,search_terms=?,"
+ "alternative_title=? "
"WHERE visit_id=?"));
statement.BindDouble(
0, static_cast<double>(
@@ -304,7 +307,8 @@ void VisitAnnotationsDatabase::UpdateContentAnnotationsForVisit(
statement.BindString(6,
visit_content_annotations.search_normalized_url.spec());
statement.BindString16(7, visit_content_annotations.search_terms);
- statement.BindInt64(8, visit_id);
+ statement.BindString(8, visit_content_annotations.alternative_title);
+ statement.BindInt64(9, visit_id);
if (!statement.Run()) {
DVLOG(0)
@@ -370,6 +374,7 @@ bool VisitAnnotationsDatabase::GetContentAnnotationsForVisit(
out_content_annotations->search_normalized_url =
GURL(statement.ColumnString(7));
out_content_annotations->search_terms = statement.ColumnString16(8);
+ out_content_annotations->alternative_title = statement.ColumnString(9);
return true;
}
@@ -641,4 +646,19 @@ bool VisitAnnotationsDatabase::MigrateContentAnnotationsAddSearchMetadata() {
"ALTER TABLE content_annotations ADD COLUMN search_terms LONGVARCHAR");
}
+bool VisitAnnotationsDatabase::MigrateContentAnnotationsAddAlternativeTitle() {
+ if (!GetDB().DoesTableExist("content_annotations")) {
+ NOTREACHED() << "Content annotations table should exist before migration";
+ return false;
+ }
+
+ if (GetDB().DoesColumnExist("content_annotations", "alternative_title"))
+ return true;
+
+ // Add the `alternative_title`column to the older versions of the table.
+ return GetDB().Execute(
+ "ALTER TABLE content_annotations "
+ "ADD COLUMN alternative_title");
+}
+
} // namespace history
diff --git a/chromium/components/history/core/browser/visit_annotations_database.h b/chromium/components/history/core/browser/visit_annotations_database.h
index 8bf56a5193a..2c2e3dbeb84 100644
--- a/chromium/components/history/core/browser/visit_annotations_database.h
+++ b/chromium/components/history/core/browser/visit_annotations_database.h
@@ -130,6 +130,10 @@ class VisitAnnotationsDatabase {
// Called by the derived classes to migrate the older content_annotations
// table by adding the search_normalized_url and search_terms columns.
bool MigrateContentAnnotationsAddSearchMetadata();
+
+ // Called by the derived classes to migrate the older content_annotations
+ // table by adding the alternative_title column.
+ bool MigrateContentAnnotationsAddAlternativeTitle();
};
} // namespace history
diff --git a/chromium/components/history/core/browser/visit_annotations_database_unittest.cc b/chromium/components/history/core/browser/visit_annotations_database_unittest.cc
index 30480d7ca9e..d2894ad4c35 100644
--- a/chromium/components/history/core/browser/visit_annotations_database_unittest.cc
+++ b/chromium/components/history/core/browser/visit_annotations_database_unittest.cc
@@ -90,8 +90,9 @@ TEST_F(VisitAnnotationsDatabaseTest, AddContentAnnotationsForVisit) {
std::vector<std::string> related_searches{"related searches",
"búsquedas relacionadas"};
VisitContentAnnotations content_annotations{
- annotation_flags, model_annotations, related_searches,
- GURL("http://pagewithvisit.com?q=search"), u"search"};
+ annotation_flags, model_annotations,
+ related_searches, GURL("http://pagewithvisit.com?q=search"),
+ u"search", "Alternative title"};
AddContentAnnotationsForVisit(visit_id, content_annotations);
// Query for it.
@@ -119,6 +120,7 @@ TEST_F(VisitAnnotationsDatabaseTest, AddContentAnnotationsForVisit) {
EXPECT_EQ(GURL("http://pagewithvisit.com?q=search"),
got_content_annotations.search_normalized_url);
EXPECT_EQ(u"search", got_content_annotations.search_terms);
+ EXPECT_EQ("Alternative title", got_content_annotations.alternative_title);
}
TEST_F(VisitAnnotationsDatabaseTest,
@@ -172,8 +174,9 @@ TEST_F(VisitAnnotationsDatabaseTest, UpdateContentAnnotationsForVisit) {
VisitContentAnnotationFlags annotation_flags =
VisitContentAnnotationFlag::kBrowsingTopicsEligible;
VisitContentAnnotations original{
- annotation_flags, model_annotations, related_searches,
- GURL("http://pagewithvisit.com?q=search"), u"search"};
+ annotation_flags, model_annotations,
+ related_searches, GURL("http://pagewithvisit.com?q=search"),
+ u"search", "Alternative title"};
AddContentAnnotationsForVisit(visit_id, original);
// Mutate that row.
@@ -183,6 +186,7 @@ TEST_F(VisitAnnotationsDatabaseTest, UpdateContentAnnotationsForVisit) {
modification.search_normalized_url =
GURL("http://pagewithvisit.com?q=search2");
modification.search_terms = u"search2";
+ modification.alternative_title = "New alternative title";
UpdateContentAnnotationsForVisit(visit_id, modification);
// Check that the mutated version was written.
@@ -208,6 +212,7 @@ TEST_F(VisitAnnotationsDatabaseTest, UpdateContentAnnotationsForVisit) {
EXPECT_EQ(final.search_normalized_url,
GURL("http://pagewithvisit.com?q=search2"));
EXPECT_EQ(final.search_terms, u"search2");
+ EXPECT_EQ(final.alternative_title, "New alternative title");
}
TEST_F(VisitAnnotationsDatabaseTest, GetMostRecentClusterIds) {
@@ -259,8 +264,9 @@ TEST_F(VisitAnnotationsDatabaseTest, DeleteAnnotationsForVisit) {
VisitContentAnnotationFlags annotation_flags =
VisitContentAnnotationFlag::kNone;
VisitContentAnnotations content_annotations{
- annotation_flags, model_annotations, related_searches,
- GURL("http://pagewithvisit.com?q=search"), u"search"};
+ annotation_flags, model_annotations,
+ related_searches, GURL("http://pagewithvisit.com?q=search"),
+ u"search", "Alternative title"};
AddContentAnnotationsForVisit(visit_id, content_annotations);
VisitContentAnnotations got_content_annotations;
diff --git a/chromium/components/history/core/browser/visit_database.cc b/chromium/components/history/core/browser/visit_database.cc
index fb84c67b5a3..5d90a1fc1cd 100644
--- a/chromium/components/history/core/browser/visit_database.cc
+++ b/chromium/components/history/core/browser/visit_database.cc
@@ -111,7 +111,13 @@ bool VisitDatabase::InitVisitTable() {
// for each row.
if (!GetDB().Execute(
"CREATE TABLE visits("
- "id INTEGER PRIMARY KEY,"
+ // The `id` uses AUTOINCREMENT to support Sync. Chrome Sync uses the
+ // `id` in conjunction with the Client ID as a unique identifier.
+ // If this was not AUTOINCREMENT, deleting a row and creating a new
+ // one could reuse the same `id` for an entirely new visit, which
+ // would confuse Sync, as Sync would be unable to distinguish
+ // an update from a deletion plus a creation.
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
"url INTEGER NOT NULL," // key of the URL this corresponds to
"visit_time INTEGER NOT NULL,"
"from_visit INTEGER,"
@@ -121,7 +127,16 @@ bool VisitDatabase::InitVisitTable() {
// longer used and should NOT be read or written from any longer.
"visit_duration INTEGER DEFAULT 0 NOT NULL,"
"incremented_omnibox_typed_score BOOLEAN DEFAULT FALSE NOT NULL,"
- "opener_visit INTEGER)"))
+ "opener_visit INTEGER,"
+ // These two fields are non-null only for remote visits synced to
+ // the local machine. The `originator_cache_guid` is the unique
+ // identifier for the originator machine the visit was originally
+ // made on, and `originator_visit_id` is the `id` of the visit row
+ // as originally assigned by AUTOINCREMENT on the originator.
+ // The tuple of (`originator_cache_guid`, `origin_visit_id`) is
+ // globally unique.
+ "originator_cache_guid TEXT,"
+ "originator_visit_id INTEGER)"))
return false;
}
@@ -175,6 +190,8 @@ void VisitDatabase::FillVisitRow(sql::Statement& statement, VisitRow* visit) {
base::TimeDelta::FromInternalValue(statement.ColumnInt64(6));
visit->incremented_omnibox_typed_score = statement.ColumnBool(7);
visit->opener_visit = statement.ColumnInt64(8);
+ visit->originator_cache_guid = statement.ColumnString(9);
+ visit->originator_visit_id = statement.ColumnInt64(10);
}
// static
@@ -234,8 +251,9 @@ VisitID VisitDatabase::AddVisit(VisitRow* visit, VisitSource source) {
SQL_FROM_HERE,
"INSERT INTO visits "
"(url, visit_time, from_visit, transition, segment_id, "
- "visit_duration, incremented_omnibox_typed_score, opener_visit) "
- "VALUES (?,?,?,?,?,?,?,?)"));
+ "visit_duration, incremented_omnibox_typed_score, opener_visit,"
+ "originator_cache_guid,originator_visit_id) "
+ "VALUES (?,?,?,?,?,?,?,?,?,?)"));
statement.BindInt64(0, visit->url_id);
statement.BindInt64(1, visit->visit_time.ToInternalValue());
statement.BindInt64(2, visit->referring_visit);
@@ -244,6 +262,8 @@ VisitID VisitDatabase::AddVisit(VisitRow* visit, VisitSource source) {
statement.BindInt64(5, visit->visit_duration.ToInternalValue());
statement.BindBool(6, visit->incremented_omnibox_typed_score);
statement.BindInt64(7, visit->opener_visit);
+ statement.BindString(8, visit->originator_cache_guid);
+ statement.BindInt64(9, visit->originator_visit_id);
if (!statement.Run()) {
DVLOG(0) << "Failed to execute visit insert statement: "
@@ -325,7 +345,8 @@ bool VisitDatabase::UpdateVisitRow(const VisitRow& visit) {
SQL_FROM_HERE,
"UPDATE visits SET "
"url=?,visit_time=?,from_visit=?,transition=?,segment_id=?,"
- "visit_duration=?,incremented_omnibox_typed_score=?,opener_visit=? "
+ "visit_duration=?,incremented_omnibox_typed_score=?,opener_visit=?,"
+ "originator_cache_guid=?,originator_visit_id=? "
"WHERE id=?"));
statement.BindInt64(0, visit.url_id);
statement.BindInt64(1, visit.visit_time.ToInternalValue());
@@ -335,7 +356,9 @@ bool VisitDatabase::UpdateVisitRow(const VisitRow& visit) {
statement.BindInt64(5, visit.visit_duration.ToInternalValue());
statement.BindBool(6, visit.incremented_omnibox_typed_score);
statement.BindInt64(7, visit.opener_visit);
- statement.BindInt64(8, visit.visit_id);
+ statement.BindString(8, visit.originator_cache_guid);
+ statement.BindInt64(9, visit.originator_visit_id);
+ statement.BindInt64(10, visit.visit_id);
return statement.Run();
}
@@ -1061,6 +1084,67 @@ bool VisitDatabase::
transaction.Commit();
}
+bool VisitDatabase::MigrateVisitsAutoincrementIdAndAddOriginatorColumns() {
+ if (!GetDB().DoesTableExist("visits")) {
+ NOTREACHED() << " Visits table should exist before migration";
+ return false;
+ }
+
+ if (GetDB().DoesColumnExist("visits", "originator_cache_guid") &&
+ GetDB().DoesColumnExist("visits", "originator_visit_id") &&
+ VisitTableContainsAutoincrement()) {
+ return true;
+ }
+
+ sql::Transaction transaction(&GetDB());
+ return transaction.Begin() &&
+ GetDB().Execute(
+ "CREATE TABLE visits_tmp("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "url INTEGER NOT NULL," // key of the URL this corresponds to
+ "visit_time INTEGER NOT NULL,"
+ "from_visit INTEGER,"
+ "transition INTEGER DEFAULT 0 NOT NULL,"
+ "segment_id INTEGER,"
+ "visit_duration INTEGER DEFAULT 0 NOT NULL,"
+ "incremented_omnibox_typed_score BOOLEAN DEFAULT FALSE NOT NULL,"
+ "opener_visit INTEGER)") &&
+ GetDB().Execute(
+ "INSERT INTO visits_tmp SELECT "
+ "id, url, visit_time, from_visit, transition, segment_id, "
+ "visit_duration, incremented_omnibox_typed_score, opener_visit "
+ "FROM visits") &&
+ GetDB().Execute(
+ "ALTER TABLE visits_tmp ADD COLUMN originator_cache_guid TEXT") &&
+ GetDB().Execute(
+ "ALTER TABLE visits_tmp ADD COLUMN originator_visit_id INTEGER") &&
+ GetDB().Execute("DROP TABLE visits") &&
+ GetDB().Execute("ALTER TABLE visits_tmp RENAME TO visits") &&
+ transaction.Commit();
+}
+
+bool VisitDatabase::VisitTableContainsAutoincrement() {
+ // sqlite_schema has columns:
+ // type - "index" or "table".
+ // name - name of created element.
+ // tbl_name - name of element, or target table in case of index.
+ // rootpage - root page of the element in database file.
+ // sql - SQL to create the element.
+ sql::Statement statement(
+ GetDB().GetUniqueStatement("SELECT sql FROM sqlite_schema WHERE type = "
+ "'table' AND name = 'visits'"));
+
+ // visits table does not exist.
+ if (!statement.Step())
+ return false;
+
+ std::string urls_schema = statement.ColumnString(0);
+ // We check if the whole schema contains "AUTOINCREMENT", since
+ // "AUTOINCREMENT" only can be used for "INTEGER PRIMARY KEY", so we assume no
+ // other columns could contain "AUTOINCREMENT".
+ return urls_schema.find("AUTOINCREMENT") != std::string::npos;
+}
+
bool VisitDatabase::GetAllVisitedURLRowidsForMigrationToVersion40(
std::vector<URLID>* visited_url_rowids_sorted) {
DCHECK(visited_url_rowids_sorted);
diff --git a/chromium/components/history/core/browser/visit_database.h b/chromium/components/history/core/browser/visit_database.h
index 5ade58be23b..5db4fba79ca 100644
--- a/chromium/components/history/core/browser/visit_database.h
+++ b/chromium/components/history/core/browser/visit_database.h
@@ -277,6 +277,16 @@ class VisitDatabase {
// column which is no longer used.
bool MigrateVisitsWithoutOpenerVisitColumnAndDropPubliclyRoutableColumn();
+ // Called by the derived classes to migrate the older visits table which
+ // which aren't ready to accommodate Sync. It sets `id` to AUTOINCREMENT, and
+ // ensures the existence of the `originator_cache_guid` and
+ // `originator_visit_id` columns.
+ bool MigrateVisitsAutoincrementIdAndAddOriginatorColumns();
+
+ // Return true if the visits table's schema contains "AUTOINCREMENT".
+ // false if table do not contain AUTOINCREMENT, or the table is not created.
+ bool VisitTableContainsAutoincrement();
+
// A subprocedure in the process of migration to version 40.
bool GetAllVisitedURLRowidsForMigrationToVersion40(
std::vector<URLID>* visited_url_rowids_sorted);
@@ -285,7 +295,8 @@ class VisitDatabase {
// Columns, in order, of the visit table.
#define HISTORY_VISIT_ROW_FIELDS \
" id,url,visit_time,from_visit,transition,segment_id,visit_duration," \
- "incremented_omnibox_typed_score,opener_visit "
+ "incremented_omnibox_typed_score,opener_visit,originator_cache_guid," \
+ "originator_visit_id "
} // namespace history
diff --git a/chromium/components/history/core/browser/visit_database_unittest.cc b/chromium/components/history/core/browser/visit_database_unittest.cc
index fcf7ead0ab8..4ed63a1a5a7 100644
--- a/chromium/components/history/core/browser/visit_database_unittest.cc
+++ b/chromium/components/history/core/browser/visit_database_unittest.cc
@@ -32,7 +32,9 @@ bool IsVisitInfoEqual(const VisitRow& a, const VisitRow& b) {
a.visit_time == b.visit_time &&
a.referring_visit == b.referring_visit &&
ui::PageTransitionTypeIncludingQualifiersIs(a.transition,
- b.transition);
+ b.transition) &&
+ a.originator_cache_guid == b.originator_cache_guid &&
+ a.originator_visit_id == b.originator_visit_id;
}
} // namespace
@@ -76,6 +78,9 @@ TEST_F(VisitDatabaseTest, Add) {
VisitRow visit_info2(visit_info1.url_id,
visit_info1.visit_time + base::Seconds(1), 1,
ui::PAGE_TRANSITION_TYPED, 0, true, 0);
+ // Verify we can fetch originator data too.
+ visit_info2.originator_cache_guid = "foobar_client";
+ visit_info2.originator_visit_id = 42;
EXPECT_TRUE(AddVisit(&visit_info2, SOURCE_BROWSED));
// Add third visit for a different page.
@@ -145,6 +150,8 @@ TEST_F(VisitDatabaseTest, Update) {
modification.transition = ui::PAGE_TRANSITION_TYPED;
modification.visit_time = Time::Now() + base::Days(1);
modification.referring_visit = 9292;
+ modification.originator_cache_guid = "foobar_client";
+ modification.originator_visit_id = 42;
UpdateVisitRow(modification);
// Check that the mutated version was written.
diff --git a/chromium/components/history/core/browser/visitsegment_database.cc b/chromium/components/history/core/browser/visitsegment_database.cc
index 01f5ef817bf..36cfd553f74 100644
--- a/chromium/components/history/core/browser/visitsegment_database.cc
+++ b/chromium/components/history/core/browser/visitsegment_database.cc
@@ -198,7 +198,6 @@ bool VisitSegmentDatabase::IncreaseSegmentVisitCount(SegmentID segment_id,
std::vector<std::unique_ptr<PageUsageData>>
VisitSegmentDatabase::QuerySegmentUsage(
- base::Time from_time,
int max_result_count,
const base::RepeatingCallback<bool(const GURL&)>& url_filter) {
// This function gathers the highest-ranked segments in two queries.
@@ -207,16 +206,13 @@ VisitSegmentDatabase::QuerySegmentUsage(
// segments.
// Gather all the segment scores.
- sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
- "SELECT segment_id, time_slot, visit_count "
- "FROM segment_usage WHERE time_slot >= ? "
- "ORDER BY segment_id"));
+ sql::Statement statement(
+ GetDB().GetCachedStatement(SQL_FROM_HERE,
+ "SELECT segment_id, time_slot, visit_count "
+ "FROM segment_usage ORDER BY segment_id"));
if (!statement.is_valid())
return std::vector<std::unique_ptr<PageUsageData>>();
- base::Time ts = from_time.LocalMidnight();
- statement.BindInt64(0, ts.ToInternalValue());
-
std::vector<std::unique_ptr<PageUsageData>> segments;
base::Time now = base::Time::Now();
SegmentID previous_segment_id = 0;
diff --git a/chromium/components/history/core/browser/visitsegment_database.h b/chromium/components/history/core/browser/visitsegment_database.h
index 3cc8a78cfc1..450699e6231 100644
--- a/chromium/components/history/core/browser/visitsegment_database.h
+++ b/chromium/components/history/core/browser/visitsegment_database.h
@@ -53,11 +53,10 @@ class VisitSegmentDatabase {
bool IncreaseSegmentVisitCount(SegmentID segment_id, base::Time ts,
int amount);
- // Computes the segment usage since `from_time`. If `url_filter` is non-null,
- // then only URLs for which it returns true will be included.
- // Returns the highest-scored segments up to `max_result_count`.
+ // Returns the highest-scored segments up to `max_result_count`. If
+ // `url_filter` is non-null, then only URLs for which it returns true will be
+ // included.
std::vector<std::unique_ptr<PageUsageData>> QuerySegmentUsage(
- base::Time from_time,
int max_result_count,
const base::RepeatingCallback<bool(const GURL&)>& url_filter);
diff --git a/chromium/components/history_clusters/OWNERS b/chromium/components/history_clusters/OWNERS
index f9b2c42c72e..1d1ebb168ac 100644
--- a/chromium/components/history_clusters/OWNERS
+++ b/chromium/components/history_clusters/OWNERS
@@ -2,7 +2,6 @@ jdonnelly@chromium.org
mahmadi@chromium.org
manukh@chromium.org
mcrouse@chromium.org
-robertogden@chromium.org
sophiechang@chromium.org
tbansal@chromium.org
tommycli@chromium.org
diff --git a/chromium/components/history_clusters/core/BUILD.gn b/chromium/components/history_clusters/core/BUILD.gn
index e1cc95e6e57..45d7b6ab099 100644
--- a/chromium/components/history_clusters/core/BUILD.gn
+++ b/chromium/components/history_clusters/core/BUILD.gn
@@ -6,74 +6,64 @@ import("//build/buildflag_header.gni")
import("//build/config/chrome_build.gni")
import("//mojo/public/tools/bindings/mojom.gni")
-declare_args() {
- # You can set the variable 'build_with_on_device_clustering_backend' to true
- # to use the on-device clustering backend even in a developer build.
- build_with_on_device_clustering_backend = !is_android && !is_ios
-}
-
-buildflag_header("history_clusters_buildflags") {
- header = "history_clusters_buildflags.h"
- flags = [ "BUILD_WITH_ON_DEVICE_CLUSTERING_BACKEND=$build_with_on_device_clustering_backend" ]
-}
-
static_library("core") {
sources = [
+ "cluster_finalizer.h",
+ "cluster_metrics_utils.cc",
+ "cluster_metrics_utils.h",
+ "cluster_processor.h",
+ "clusterer.cc",
+ "clusterer.h",
"clustering_backend.h",
"config.cc",
"config.h",
+ "content_annotations_cluster_processor.cc",
+ "content_annotations_cluster_processor.h",
+ "content_visibility_cluster_finalizer.cc",
+ "content_visibility_cluster_finalizer.h",
"features.cc",
"features.h",
"history_clusters_db_tasks.cc",
"history_clusters_db_tasks.h",
+ "history_clusters_debug_jsons.cc",
+ "history_clusters_debug_jsons.h",
"history_clusters_prefs.cc",
"history_clusters_prefs.h",
"history_clusters_service.cc",
"history_clusters_service.h",
+ "history_clusters_service_task_get_most_recent_clusters.cc",
+ "history_clusters_service_task_get_most_recent_clusters.h",
"history_clusters_types.h",
"history_clusters_util.cc",
"history_clusters_util.h",
+ "keyword_cluster_finalizer.cc",
+ "keyword_cluster_finalizer.h",
+ "label_cluster_finalizer.cc",
+ "label_cluster_finalizer.h",
+ "noisy_cluster_finalizer.cc",
+ "noisy_cluster_finalizer.h",
+ "on_device_clustering_backend.cc",
+ "on_device_clustering_backend.h",
"on_device_clustering_features.cc",
"on_device_clustering_features.h",
+ "on_device_clustering_util.cc",
+ "on_device_clustering_util.h",
"query_clusters_state.cc",
"query_clusters_state.h",
+ "ranking_cluster_finalizer.cc",
+ "ranking_cluster_finalizer.h",
+ "similar_visit_deduper_cluster_finalizer.cc",
+ "similar_visit_deduper_cluster_finalizer.h",
+ "single_domain_cluster_finalizer.cc",
+ "single_domain_cluster_finalizer.h",
+ "single_visit_cluster_finalizer.cc",
+ "single_visit_cluster_finalizer.h",
]
- if (build_with_on_device_clustering_backend) {
- sources += [
- "cluster_finalizer.h",
- "cluster_metrics_utils.h",
- "cluster_processor.h",
- "clusterer.cc",
- "clusterer.h",
- "content_annotations_cluster_processor.cc",
- "content_annotations_cluster_processor.h",
- "content_visibility_cluster_finalizer.cc",
- "content_visibility_cluster_finalizer.h",
- "keyword_cluster_finalizer.cc",
- "keyword_cluster_finalizer.h",
- "label_cluster_finalizer.cc",
- "label_cluster_finalizer.h",
- "noisy_cluster_finalizer.cc",
- "noisy_cluster_finalizer.h",
- "on_device_clustering_backend.cc",
- "on_device_clustering_backend.h",
- "on_device_clustering_util.cc",
- "on_device_clustering_util.h",
- "ranking_cluster_finalizer.cc",
- "ranking_cluster_finalizer.h",
- "similar_visit_deduper_cluster_finalizer.cc",
- "similar_visit_deduper_cluster_finalizer.h",
- "single_visit_cluster_finalizer.cc",
- "single_visit_cluster_finalizer.h",
- "url_deduper_cluster_finalizer.cc",
- "url_deduper_cluster_finalizer.h",
- ]
- }
deps = [
- ":history_clusters_buildflags",
"//base",
"//components/history/core/browser",
"//components/keyed_service/core",
+ "//components/optimization_guide/core",
"//components/optimization_guide/core:entities",
"//components/pref_registry",
"//components/query_parser",
@@ -92,34 +82,31 @@ static_library("core") {
source_set("unit_tests") {
testonly = true
sources = [
+ "clusterer_unittest.cc",
"config_unittest.cc",
+ "content_annotations_cluster_processor_unittest.cc",
+ "content_visibility_cluster_finalizer_unittest.cc",
"history_clusters_db_tasks_unittest.cc",
"history_clusters_service_unittest.cc",
"history_clusters_util_unittest.cc",
+ "keyword_cluster_finalizer_unittest.cc",
+ "label_cluster_finalizer_unittest.cc",
+ "noisy_cluster_finalizer_unittest.cc",
+ "on_device_clustering_backend_unittest.cc",
+ "on_device_clustering_util_unittest.cc",
"query_clusters_state_unittest.cc",
+ "ranking_cluster_finalizer_unittest.cc",
+ "similar_visit_deduper_cluster_finalizer_unittest.cc",
+ "single_domain_cluster_finalizer_unittest.cc",
+ "single_visit_cluster_finalizer_unittest.cc",
]
- if (build_with_on_device_clustering_backend) {
- sources += [
- "clusterer_unittest.cc",
- "content_annotations_cluster_processor_unittest.cc",
- "content_visibility_cluster_finalizer_unittest.cc",
- "keyword_cluster_finalizer_unittest.cc",
- "label_cluster_finalizer_unittest.cc",
- "noisy_cluster_finalizer_unittest.cc",
- "on_device_clustering_backend_unittest.cc",
- "on_device_clustering_util_unittest.cc",
- "ranking_cluster_finalizer_unittest.cc",
- "similar_visit_deduper_cluster_finalizer_unittest.cc",
- "single_visit_cluster_finalizer_unittest.cc",
- "url_deduper_cluster_finalizer_unittest.cc",
- ]
- }
deps = [
":core",
":test_support",
"//base/test:test_support",
"//components/history/core/browser",
"//components/history/core/test",
+ "//components/optimization_guide/core",
"//components/optimization_guide/core:entities",
"//components/search_engines",
"//components/site_engagement/core",
@@ -136,8 +123,8 @@ source_set("test_support") {
"history_clusters_service_test_api.cc",
"history_clusters_service_test_api.h",
]
+ public_deps = [ ":core" ]
deps = [
- ":core",
"//base/test:test_support",
"//components/history/core/browser",
"//components/history/core/test",
diff --git a/chromium/components/history_clusters/core/cluster_metrics_utils.cc b/chromium/components/history_clusters/core/cluster_metrics_utils.cc
new file mode 100644
index 00000000000..3ce089bf943
--- /dev/null
+++ b/chromium/components/history_clusters/core/cluster_metrics_utils.cc
@@ -0,0 +1,59 @@
+// Copyright 2022 The Chromium 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/history_clusters/core/cluster_metrics_utils.h"
+
+#include "base/notreached.h"
+
+namespace history_clusters {
+
+std::string ClusterActionToString(ClusterAction action) {
+ switch (action) {
+ case ClusterAction::kDeleted:
+ return "Deleted";
+ case ClusterAction::kOpenedInTabGroup:
+ return "OpenedInTabGroup";
+ case ClusterAction::kRelatedSearchClicked:
+ return "RelatedSearchClicked";
+ case ClusterAction::kRelatedVisitsVisibilityToggled:
+ return "RelatedVisitsVisibilityToggled";
+ case ClusterAction::kVisitClicked:
+ return "VisitClicked";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+std::string VisitActionToString(VisitAction action) {
+ switch (action) {
+ case VisitAction::kDeleted:
+ return "Deleted";
+ case VisitAction::kClicked:
+ return "Clicked";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+std::string VisitTypeToString(VisitType action) {
+ switch (action) {
+ case VisitType::kSRP:
+ return "SRP";
+ case VisitType::kNonSRP:
+ return "nonSRP";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+std::string RelatedSearchActionToString(RelatedSearchAction action) {
+ switch (action) {
+ case RelatedSearchAction::kClicked:
+ return "Clicked";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/cluster_metrics_utils.h b/chromium/components/history_clusters/core/cluster_metrics_utils.h
index 9cb2e94736f..e9a5f1ac101 100644
--- a/chromium/components/history_clusters/core/cluster_metrics_utils.h
+++ b/chromium/components/history_clusters/core/cluster_metrics_utils.h
@@ -35,6 +35,45 @@ class ScopedFilterClusterMetricsRecorder {
const std::string filtered_reason_;
};
+/**
+ * The following enums must be kept in sync with their respective variants in
+ * //tools/metrics/histograms/metadata/history/histograms.xml and
+ * //ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
+ */
+
+// Actions that can be performed on clusters.
+enum class ClusterAction {
+ kDeleted = 0,
+ kOpenedInTabGroup = 1,
+ kRelatedSearchClicked = 2,
+ kRelatedVisitsVisibilityToggled = 3,
+ kVisitClicked = 4,
+};
+
+// Actions that can be performed on related search items.
+enum class RelatedSearchAction {
+ kClicked = 0,
+};
+
+// Actions that can be performed on visits.
+enum class VisitAction {
+ kClicked = 0,
+ kDeleted = 1,
+};
+
+// Types of visits that can be shown and acted on.
+enum class VisitType {
+ kSRP = 0,
+ kNonSRP = 1,
+};
+
+// Returns the string representation of each enum class used for
+// logging/histograms.
+std::string ClusterActionToString(ClusterAction action);
+std::string VisitActionToString(VisitAction action);
+std::string VisitTypeToString(VisitType action);
+std::string RelatedSearchActionToString(RelatedSearchAction action);
+
} // namespace history_clusters
#endif // COMPONENTS_HISTORY_CLUSTERS_CORE_CLUSTER_METRICS_UTILS_H_
diff --git a/chromium/components/history_clusters/core/clusterer.cc b/chromium/components/history_clusters/core/clusterer.cc
index 86313b53550..cefec43df1d 100644
--- a/chromium/components/history_clusters/core/clusterer.cc
+++ b/chromium/components/history_clusters/core/clusterer.cc
@@ -43,12 +43,6 @@ bool ShouldAddVisitToCluster(const history::ClusterVisit& visit,
return true;
}
-// Returns whether a visit should be omitted from all clusters.
-bool ShouldSkipVisit(const history::ClusterVisit& visit) {
- return GetConfig().hosts_to_skip_clustering_for.contains(
- visit.normalized_url.host());
-}
-
} // namespace
Clusterer::Clusterer() = default;
@@ -67,10 +61,6 @@ std::vector<history::Cluster> Clusterer::CreateInitialClustersFromVisits(
base::flat_map<history::VisitID, size_t> visit_id_to_cluster_map;
std::vector<history::Cluster> clusters;
for (const auto& visit : *visits) {
- if (ShouldSkipVisit(visit)) {
- continue;
- }
-
const auto& visit_url = visit.normalized_url;
absl::optional<size_t> cluster_idx;
history::VisitID previous_visit_id =
diff --git a/chromium/components/history_clusters/core/clusterer_unittest.cc b/chromium/components/history_clusters/core/clusterer_unittest.cc
index 775faa4d06e..77e58fe21c2 100644
--- a/chromium/components/history_clusters/core/clusterer_unittest.cc
+++ b/chromium/components/history_clusters/core/clusterer_unittest.cc
@@ -6,7 +6,6 @@
#include "base/test/task_environment.h"
#include "components/history_clusters/core/clustering_test_utils.h"
-#include "components/history_clusters/core/config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -17,12 +16,7 @@ using ::testing::ElementsAre;
class ClustererTest : public ::testing::Test {
public:
- void SetUp() override {
- config_.hosts_to_skip_clustering_for = {"www.shouldskip.com"};
- SetConfigForTesting(config_);
-
- clusterer_ = std::make_unique<Clusterer>();
- }
+ void SetUp() override { clusterer_ = std::make_unique<Clusterer>(); }
void TearDown() override { clusterer_.reset(); }
@@ -32,7 +26,6 @@ class ClustererTest : public ::testing::Test {
}
private:
- Config config_;
std::unique_ptr<Clusterer> clusterer_;
base::test::TaskEnvironment task_environment_;
};
@@ -163,14 +156,6 @@ TEST_F(ClustererTest, MultipleClusters) {
testing::CreateDefaultAnnotatedVisit(3, GURL("https://whatever.com/"));
visits.push_back(testing::CreateClusterVisit(visit3));
- history::AnnotatedVisit should_skip = testing::CreateDefaultAnnotatedVisit(
- 11, GURL("https://www.shouldskip.com/whatever"));
- history::ClusterVisit should_skip_cluster_visit =
- testing::CreateClusterVisit(should_skip);
- should_skip_cluster_visit.normalized_url =
- GURL("https://www.shouldskip.com/whatever");
- visits.push_back(should_skip_cluster_visit);
-
std::vector<history::Cluster> result_clusters =
CreateInitialClustersFromVisits(visits);
EXPECT_THAT(testing::ToVisitResults(result_clusters),
diff --git a/chromium/components/history_clusters/core/clustering_test_utils.cc b/chromium/components/history_clusters/core/clustering_test_utils.cc
index 371b820ba31..0e7da82d198 100644
--- a/chromium/components/history_clusters/core/clustering_test_utils.cc
+++ b/chromium/components/history_clusters/core/clustering_test_utils.cc
@@ -88,6 +88,8 @@ history::ClusterVisit CreateClusterVisit(
normalized_url ? *normalized_url : annotated_visit.url_row.url();
cluster_visit.url_for_deduping =
ComputeURLForDeduping(cluster_visit.normalized_url);
+ cluster_visit.url_for_display =
+ ComputeURLForDisplay(cluster_visit.normalized_url);
return cluster_visit;
}
diff --git a/chromium/components/history_clusters/core/config.cc b/chromium/components/history_clusters/core/config.cc
index 46433282b91..b7895a7e5ec 100644
--- a/chromium/components/history_clusters/core/config.cc
+++ b/chromium/components/history_clusters/core/config.cc
@@ -34,9 +34,6 @@ Config::Config() {
max_visits_to_cluster = base::GetFieldTrialParamByFeatureAsInt(
internal::kJourneys, "JourneysMaxVisitsToCluster", max_visits_to_cluster);
- max_days_to_cluster = base::GetFieldTrialParamByFeatureAsInt(
- internal::kJourneys, "JourneysMaxDaysToCluster", max_days_to_cluster);
-
max_keyword_phrases = base::GetFieldTrialParamByFeatureAsInt(
internal::kJourneys, "JourneysMaxKeywordPhrases", max_keyword_phrases);
@@ -77,6 +74,56 @@ Config::Config() {
internal::kOmniboxAction, "omnibox_action_on_noisy_urls",
omnibox_action_on_noisy_urls);
+ omnibox_action_on_navigation_intents =
+ base::GetFieldTrialParamByFeatureAsBool(
+ internal::kOmniboxAction, "omnibox_action_on_navigation_intents",
+ omnibox_action_on_navigation_intents);
+
+ omnibox_action_navigation_intent_score_threshold =
+ base::GetFieldTrialParamByFeatureAsInt(
+ internal::kOmniboxAction,
+ "omnibox_action_on_navigation_intent_score_threshold",
+ omnibox_action_navigation_intent_score_threshold);
+
+ omnibox_action_with_pedals = base::GetFieldTrialParamByFeatureAsBool(
+ internal::kOmniboxAction, "omnibox_action_with_pedals",
+ omnibox_action_with_pedals);
+
+ keyword_filter_on_entity_aliases = base::GetFieldTrialParamByFeatureAsBool(
+ history_clusters::features::kOnDeviceClusteringKeywordFiltering,
+ "keyword_filter_on_entity_aliases", keyword_filter_on_entity_aliases);
+
+ max_entity_aliases_in_keywords = base::GetFieldTrialParamByFeatureAsInt(
+ history_clusters::features::kOnDeviceClusteringKeywordFiltering,
+ "max_entity_aliases_in_keywords", max_entity_aliases_in_keywords);
+ if (max_entity_aliases_in_keywords <= 0) {
+ max_entity_aliases_in_keywords = SIZE_MAX;
+ }
+
+ keyword_filter_on_categories = GetFieldTrialParamByFeatureAsBool(
+ history_clusters::features::kOnDeviceClusteringKeywordFiltering,
+ "keyword_filter_on_categories", keyword_filter_on_categories);
+
+ keyword_filter_on_noisy_visits = GetFieldTrialParamByFeatureAsBool(
+ history_clusters::features::kOnDeviceClusteringKeywordFiltering,
+ "keyword_filter_on_noisy_visits", keyword_filter_on_noisy_visits);
+
+ keyword_filter_on_search_terms = GetFieldTrialParamByFeatureAsBool(
+ history_clusters::features::kOnDeviceClusteringKeywordFiltering,
+ "keyword_filter_on_search_terms", keyword_filter_on_search_terms);
+
+ keyword_filter_on_visit_hosts = GetFieldTrialParamByFeatureAsBool(
+ history_clusters::features::kOnDeviceClusteringKeywordFiltering,
+ "keyword_filter_on_visit_hosts", keyword_filter_on_visit_hosts);
+
+ category_keyword_score_weight = GetFieldTrialParamByFeatureAsDouble(
+ features::kOnDeviceClusteringKeywordFiltering,
+ "category_keyword_score_weight", category_keyword_score_weight);
+
+ max_num_keywords_per_cluster = GetFieldTrialParamByFeatureAsInt(
+ features::kOnDeviceClusteringKeywordFiltering,
+ "max_num_keywords_per_cluster", max_num_keywords_per_cluster);
+
non_user_visible_debug =
base::FeatureList::IsEnabled(internal::kNonUserVisibleDebug);
@@ -143,9 +190,11 @@ Config::Config() {
"hide_single_visit_clusters_on_prominent_ui_surfaces",
should_hide_single_visit_clusters_on_prominent_ui_surfaces);
- should_dedupe_similar_visits = GetFieldTrialParamByFeatureAsBool(
- features::kOnDeviceClustering, "dedupe_similar_visits",
- should_dedupe_similar_visits);
+ should_hide_single_domain_clusters_on_prominent_ui_surfaces =
+ GetFieldTrialParamByFeatureAsBool(
+ features::kOnDeviceClustering,
+ "hide_single_domain_clusters_on_prominent_ui_surfaces",
+ should_hide_single_domain_clusters_on_prominent_ui_surfaces);
should_filter_noisy_clusters = GetFieldTrialParamByFeatureAsBool(
features::kOnDeviceClustering, "filter_noisy_clusters",
@@ -197,41 +246,32 @@ Config::Config() {
"content_clustering_intersection_threshold",
cluster_interaction_threshold);
- should_include_categories_in_keywords = GetFieldTrialParamByFeatureAsBool(
- features::kOnDeviceClustering, "include_categories_in_keywords",
- should_include_categories_in_keywords);
-
- should_exclude_keywords_from_noisy_visits = GetFieldTrialParamByFeatureAsBool(
- features::kOnDeviceClustering, "exclude_keywords_from_noisy_visits",
- should_exclude_keywords_from_noisy_visits);
-
- clustering_tasks_batch_size = GetFieldTrialParamByFeatureAsInt(
- features::kSplitClusteringTasksToSmallerBatches,
- "clustering_task_batch_size", clustering_tasks_batch_size);
-
split_clusters_at_search_visits = GetFieldTrialParamByFeatureAsBool(
features::kOnDeviceClustering, "split_clusters_at_search_visits",
split_clusters_at_search_visits);
- should_label_clusters = GetFieldTrialParamByFeatureAsBool(
- features::kOnDeviceClustering, "should_label_clusters",
- should_label_clusters);
+ should_label_clusters =
+ base::FeatureList::IsEnabled(internal::kJourneysLabels);
labels_from_hostnames = GetFieldTrialParamByFeatureAsBool(
- features::kOnDeviceClustering, "labels_from_hostnames",
+ internal::kJourneysLabels, "labels_from_hostnames",
labels_from_hostnames);
labels_from_entities = GetFieldTrialParamByFeatureAsBool(
- features::kOnDeviceClustering, "labels_from_entities",
- labels_from_entities);
-
- const base::FeatureParam<std::string> kHostsToSkipClusteringFor{
- &features::kOnDeviceClusteringBlocklists, "hosts_to_skip_clustering_for",
- ""};
- auto hosts = base::SplitString(kHostsToSkipClusteringFor.Get(), ",",
- base::WhitespaceHandling::TRIM_WHITESPACE,
- base::SplitResult::SPLIT_WANT_NONEMPTY);
- hosts_to_skip_clustering_for = {hosts.begin(), hosts.end()};
+ internal::kJourneysLabels, "labels_from_entities", labels_from_entities);
+
+ should_check_hosts_to_skip_clustering_for =
+ base::FeatureList::IsEnabled(features::kOnDeviceClusteringBlocklists);
+
+ engagement_score_cache_size = GetFieldTrialParamByFeatureAsInt(
+ features::kUseEngagementScoreCache, "engagement_score_cache_size",
+ engagement_score_cache_size);
+
+ engagement_score_cache_refresh_duration =
+ base::Minutes(GetFieldTrialParamByFeatureAsInt(
+ features::kUseEngagementScoreCache,
+ "engagement_score_cache_refresh_duration_minutes",
+ engagement_score_cache_refresh_duration.InMinutes()));
use_continue_on_shutdown = base::FeatureList::IsEnabled(
internal::kHistoryClustersUseContinueOnShutdown);
diff --git a/chromium/components/history_clusters/core/config.h b/chromium/components/history_clusters/core/config.h
index adaee3add62..e88be637296 100644
--- a/chromium/components/history_clusters/core/config.h
+++ b/chromium/components/history_clusters/core/config.h
@@ -7,13 +7,16 @@
#include <string>
-#include "base/containers/flat_set.h"
#include "base/time/time.h"
namespace history_clusters {
// The default configuration. Always use |GetConfig()| to get the current
// configuration.
+//
+// Config has the same thread-safety as base::FeatureList. The first call to
+// GetConfig() (which performs initialization) must be done single threaded on
+// the main thread. After that, Config can be read from any thread.
struct Config {
// True if journeys feature is enabled as per field trial check. Does not
// check for any user-specific conditions (such as locales).
@@ -23,12 +26,6 @@ struct Config {
// the number of visits sent to the clustering backend per batch.
int max_visits_to_cluster = 1000;
- // The recency threshold controlling which visits will be clustered. This
- // isn't the only factor; i.e. visits older than `MaxDaysToCluster()` may
- // still be clustered. Only applies when using persisted visit context
- // annotations; i.e. `kPersistContextAnnotationsInHistoryDb` is true.
- int max_days_to_cluster = 9;
-
// A soft cap on the number of keyword phrases to cache. 5000 should be more
// than enough, as the 99.9th percentile of users has 2000. A few nuances:
// - We cache both entity keywords and URLs, each limited separately.
@@ -61,7 +58,7 @@ struct Config {
// If enabled, hidden visits are dropped entirely, instead of being gated
// behind a "Show More" UI control.
- bool drop_hidden_visits = false;
+ bool drop_hidden_visits = true;
// If enabled, when there is a Journeys search query, the backend re-scores
// visits within a cluster to account for whether or not that visit matches.
@@ -77,13 +74,59 @@ struct Config {
bool omnibox_action = false;
// If enabled, allows the Omnibox Action chip to also appear on URLs. This
- // does nothing if `omnibox_action` is disabled.
+ // does nothing if `omnibox_action` is disabled. Note, that if you turn this
+ // flag to true, you almost certainly will want to set
+ // `omnibox_action_on_navigation_intents` to true as well, as otherwise your
+ // desired action chips on URLs will almost certainly all be suppressed.
bool omnibox_action_on_urls = false;
// If enabled, allows the Omnibox Action chip to appear on URLs from noisy
- // visits. This does nothing if `omnibox_action_on_urls` is false.
+ // visits. This does nothing if `omnibox_action_on_urls` is disabled.
bool omnibox_action_on_noisy_urls = true;
+ // If enabled, allows the Omnibox Action chip to appear when it's likely the
+ // user is intending to perform a navigation. This does not affect which
+ // suggestions are allowed to display the chip. Does nothing if
+ // `omnibox_action` is disabled.
+ bool omnibox_action_on_navigation_intents = false;
+
+ // If `omnibox_action_on_navigation_intents` is enabled, this threshold
+ // helps determine when the user is intending to perform a navigation.
+ int omnibox_action_navigation_intent_score_threshold = 1300;
+
+ // If enabled, allows the Omnibox Action chip to appear when the suggestions
+ // contain pedals. Does nothing if `omnibox_action` is disabled.
+ bool omnibox_action_with_pedals = false;
+
+ // If enabled, adds the keywords of aliases for detected entity names to a
+ // cluster.
+ bool keyword_filter_on_entity_aliases = false;
+
+ // If greater than 0, the max number of aliases to include in keywords. If <=
+ // 0, all aliases will be included.
+ size_t max_entity_aliases_in_keywords = 0;
+
+ // If enabled, adds the keywords of categories for detected entities to a
+ // cluster.
+ bool keyword_filter_on_categories = true;
+
+ // If enabled, adds the keywords of detected entities from noisy visits to a
+ // cluster.
+ bool keyword_filter_on_noisy_visits = true;
+
+ // If enabled, adds the search terms of the visits that have them.
+ bool keyword_filter_on_search_terms = false;
+
+ // If enabled, adds the keywords of detected entities that may be for
+ // the visit's host.
+ bool keyword_filter_on_visit_hosts = true;
+
+ // The weight for category keyword scores per cluster.
+ float category_keyword_score_weight = 1.0;
+
+ // Maximum number of keywords to keep per cluster.
+ size_t max_num_keywords_per_cluster = 20;
+
// Enables debug info in non-user-visible surfaces, like Chrome Inspector.
// Does nothing if `kJourneys` is disabled.
bool non_user_visible_debug = false;
@@ -140,9 +183,9 @@ struct Config {
// Whether to hide single-visit clusters on prominent UI surfaces.
bool should_hide_single_visit_clusters_on_prominent_ui_surfaces = true;
- // Whether to collapse visits within a cluster that will show on the UI in the
- // same way.
- bool should_dedupe_similar_visits = true;
+ // Whether to hide clusters that only contain URLs from the same domain on
+ // prominent UI surfaces.
+ bool should_hide_single_domain_clusters_on_prominent_ui_surfaces = false;
// Whether to filter clusters that are noisy from the UI. This will
// heuristically remove clusters that are unlikely to be "interesting".
@@ -188,13 +231,6 @@ struct Config {
// use when clustering based on intersection score.
int cluster_interaction_threshold = 2;
- // Whether to include category names in the keywords for a cluster.
- bool should_include_categories_in_keywords = true;
-
- // Whether to exclude keywords from visits that may be considered "noisy" to
- // the user (i.e. highly engaged, non-SRP).
- bool should_exclude_keywords_from_noisy_visits = false;
-
// Returns the default batch size for annotating visits when clustering.
size_t clustering_tasks_batch_size = 250;
@@ -203,7 +239,9 @@ struct Config {
// Whether to assign labels to clusters. If the label exists, it will be shown
// in the UI. If the label doesn't exist, the UI will emphasize the top visit.
- bool should_label_clusters = false;
+ // Note: The default value here is meaningless, because the actual default
+ // value is derived from the base::Feature.
+ bool should_label_clusters = true;
// Whether to assign labels to clusters from the hostnames of the cluster.
// Does nothing if `should_label_clusters` is false. Note that since every
@@ -215,9 +253,15 @@ struct Config {
// Does nothing if `should_label_clusters` is false.
bool labels_from_entities = false;
- // The set of hosts for which all visits belonging to that host will not be in
- // any cluster.
- base::flat_set<std::string> hosts_to_skip_clustering_for;
+ // Whether to check if all visits for a host should be in resulting clusters.
+ bool should_check_hosts_to_skip_clustering_for = false;
+
+ // The max number of hosts that should be stored in the engagement score
+ // cache.
+ int engagement_score_cache_size = 100;
+
+ // The max time a host should be stored in the engagement score cache.
+ base::TimeDelta engagement_score_cache_refresh_duration = base::Minutes(120);
// True if the task runner should use trait CONTINUE_ON_SHUTDOWN.
bool use_continue_on_shutdown = true;
diff --git a/chromium/components/history_clusters/core/features.cc b/chromium/components/history_clusters/core/features.cc
index cd19bdaab54..7018296b62c 100644
--- a/chromium/components/history_clusters/core/features.cc
+++ b/chromium/components/history_clusters/core/features.cc
@@ -29,6 +29,9 @@ namespace internal {
const base::Feature kJourneys{"Journeys", enabled_by_default_desktop_only};
+const base::Feature kJourneysLabels{"JourneysLabel",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
const base::Feature kOmniboxAction{"JourneysOmniboxAction",
enabled_by_default_desktop_only};
@@ -40,7 +43,7 @@ const base::Feature kUserVisibleDebug{"JourneysUserVisibleDebug",
const base::Feature kPersistContextAnnotationsInHistoryDb{
"JourneysPersistContextAnnotationsInHistoryDb",
- enabled_by_default_desktop_only};
+ base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kHistoryClustersInternalsPage{
"HistoryClustersInternalsPage", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromium/components/history_clusters/core/features.h b/chromium/components/history_clusters/core/features.h
index c84cf422cdb..3617b09d5df 100644
--- a/chromium/components/history_clusters/core/features.h
+++ b/chromium/components/history_clusters/core/features.h
@@ -19,6 +19,9 @@ namespace internal {
// directly. Instead use `IsJourneysEnabled()` for the system language filter.
extern const base::Feature kJourneys;
+// Enables labelling of Journeys in UI.
+extern const base::Feature kJourneysLabels;
+
// Enables the Journeys Omnibox Action chip. `kJourneys` must also be enabled
// for this to take effect.
extern const base::Feature kOmniboxAction;
@@ -36,7 +39,7 @@ extern const base::Feature kUserVisibleDebug;
// enabled for all users shortly. This just provides a killswitch.
// This flag is to enable us to turn on persisting context annotations WITHOUT
-// exposing the Memories UI in general. If EITHER this flag or `kJourneys` is
+// exposing the Journeys UI in general. If EITHER this flag or `kJourneys` is
// enabled, users will have context annotations persisted into their History DB.
extern const base::Feature kPersistContextAnnotationsInHistoryDb;
diff --git a/chromium/components/history_clusters/core/history_clusters_db_tasks.cc b/chromium/components/history_clusters/core/history_clusters_db_tasks.cc
index 1fb8fd6c86b..656d3608d68 100644
--- a/chromium/components/history_clusters/core/history_clusters_db_tasks.cc
+++ b/chromium/components/history_clusters/core/history_clusters_db_tasks.cc
@@ -15,7 +15,7 @@
#include "components/history/core/browser/history_database.h"
#include "components/history/core/browser/history_types.h"
#include "components/history_clusters/core/config.h"
-#include "components/history_clusters/core/features.h"
+#include "components/history_clusters/core/history_clusters_types.h"
namespace history_clusters {
@@ -30,27 +30,32 @@ bool IsTransitionUserVisible(int32_t transition) {
// static
base::Time GetAnnotatedVisitsToCluster::GetBeginTimeOnDayBoundary(
- base::Time end_time) {
- // Conventionally, `end_time` being null means to fetch History starting from
- // right now, so we explicitly convert that to `Now()` here.
- base::Time begin_time = end_time.is_null() ? base::Time::Now() : end_time;
- begin_time -= base::Hours(12);
- begin_time = begin_time.LocalMidnight();
- begin_time += base::Hours(4);
- return begin_time;
+ base::Time time) {
+ DCHECK(!time.is_null());
+ // Subtract 16 hrs. Chosen to be halfway between boundaries; i.e. 4pm is 12
+ // hrs from 4am. This guarantees fetching at least 12 hrs of visits regardless
+ // of whether iterating recent or oldest visits first.
+ time -= base::Hours(16);
+ time = time.LocalMidnight();
+ time += base::Hours(4);
+ return time;
}
GetAnnotatedVisitsToCluster::GetAnnotatedVisitsToCluster(
- HistoryClustersService::IncompleteVisitMap incomplete_visit_map,
+ IncompleteVisitMap incomplete_visit_map,
base::Time begin_time,
- base::Time end_time,
+ QueryClustersContinuationParams continuation_params,
+ bool recent_first,
Callback callback)
: incomplete_visit_map_(incomplete_visit_map),
begin_time_limit_(
std::max(begin_time, base::Time::Now() - base::Days(90))),
- original_end_time_(end_time),
- continuation_end_time_(end_time),
- callback_(std::move(callback)) {}
+ continuation_params_(continuation_params),
+ recent_first_(recent_first),
+ callback_(std::move(callback)) {
+ // Callers shouldn't ask for more visits if they've been exhausted.
+ DCHECK(!continuation_params.exhausted_history);
+}
GetAnnotatedVisitsToCluster::~GetAnnotatedVisitsToCluster() = default;
@@ -59,56 +64,33 @@ bool GetAnnotatedVisitsToCluster::RunOnDBThread(
history::HistoryDatabase* db) {
base::ElapsedThreadTimer query_visits_timer;
- history::QueryOptions options;
-
- // History Clusters wants a complete navigation graph and internally handles
- // de-duplication.
- options.duplicate_policy = history::QueryOptions::KEEP_ALL_DUPLICATES;
+ // The end time used in the initial history request for completed visits.
+ // This is the upper bound time of all the visits fetched. Used later to add
+ // incomplete visits from the same time range we scanned for completed visits.
+ // Cached here as `continuation_params` will be updated after each history
+ // request.
+ base::Time original_end_time = continuation_params_.continuation_time;
+ history::QueryOptions options;
// Accumulate 1 day at a time of visits to avoid breaking up clusters.
- // We hard cap at `options.max_count` which is enforced at the database level
- // to avoid any one day blasting past the hard cap, causing OOM errors.
- while (annotated_visits_.empty() && !exhausted_history_) {
- // Provide a parameter-controlled hard-cap of the max visits to fetch.
- // Note in most cases we stop fetching visits far before reaching this
- // number. This is to prevent OOM errors. See https://crbug.com/1262016.
- options.max_count =
- GetConfig().max_visits_to_cluster - annotated_visits_.size();
-
- // Bound visits by `original_end_time_` and `begin_time_limit_`, fetching
- // the more recent visits 1st.
- options.end_time = continuation_end_time_;
- options.begin_time = std::max(begin_time_limit_,
- GetBeginTimeOnDayBoundary(options.end_time));
+ while (annotated_visits_.empty() && !continuation_params_.is_done) {
+ // Because `base::Time::Now()` may change during the async history request,
+ // and because determining whether history was exhausted depends on whether
+ // the query reached `Now()`, `now` tracks `Now()` at the time the query
+ // options were created.
+ const auto now = base::Time::Now();
+
+ options = GetHistoryQueryOptions(backend, now);
+ if (options.begin_time == options.end_time)
+ break;
// Tack on all the newly fetched visits onto our accumulator vector.
bool limited_by_max_count = AddUnclusteredVisits(backend, options);
-
- // If we didn't get enough visits, ask for another day's worth from History
- // and call this method again when done.
- // If `limited_by_max_count` is true, `annotated_visits_` "shouldn't" be
- // empty. But it actually can be if a visit's URL is missing from the URL
- // table. `limited_by_max_count` is set before visits are filtered to
- // those whose URL is found.
- if (limited_by_max_count && !annotated_visits_.empty()) {
- continuation_end_time_ = annotated_visits_.back().visit_row.visit_time;
- } else {
- continuation_end_time_ = options.begin_time;
- }
-
- // TODO(tommycli): Connect this to History's limit defined internally in
- // components/history.
- // `exhausted_history_` is true if we've reached `begin_time_limit_` (bound
- // to be at most 90 days old). This does not necessarily mean we've added
- // all visits; e.g. `begin_time_limit_` can be more recent than 90 days ago
- // or `original_end_time_` can be older than now.
- exhausted_history_ =
- !limited_by_max_count && continuation_end_time_ <= begin_time_limit_;
+ IncrementContinuationParams(options, limited_by_max_count, now);
}
- AddIncompleteVisits(backend);
-
- RemoveVisitsFromSync();
+ AddIncompleteVisits(backend, continuation_params_.continuation_time,
+ original_end_time);
base::UmaHistogramTimes(
"History.Clusters.Backend.QueryAnnotatedVisits.ThreadTime",
@@ -117,18 +99,76 @@ bool GetAnnotatedVisitsToCluster::RunOnDBThread(
return true;
}
+history::QueryOptions GetAnnotatedVisitsToCluster::GetHistoryQueryOptions(
+ history::HistoryBackend* backend,
+ base::Time now) {
+ history::QueryOptions options;
+
+ // History Clusters wants a complete navigation graph and internally handles
+ // de-duplication.
+ options.duplicate_policy = history::QueryOptions::KEEP_ALL_DUPLICATES;
+
+ // We hard cap at `options.max_count` which is enforced at the database level
+ // to avoid any request getting too many visits causing OOM errors. See
+ // https://crbug.com/1262016.
+ options.max_count =
+ GetConfig().max_visits_to_cluster - annotated_visits_.size();
+
+ // Determine the begin & end times.
+ // 1st, set `continuation_time`, either from `continuation_params_`for
+ // continuation requests or computed for initial requests.
+ base::Time continuation_time;
+ if (continuation_params_.is_continuation)
+ continuation_time = continuation_params_.continuation_time;
+ else if (recent_first_)
+ continuation_time = now;
+ else
+ continuation_time = backend->FindMostRecentClusteredTime();
+
+ // 2nd, derive the other boundary, approximately 1 day before or after
+ // `continuation_time`, depending on `recent_first`, and rounded to a day
+ // boundary.
+ if (recent_first_) {
+ options.begin_time = GetBeginTimeOnDayBoundary(continuation_time);
+ options.end_time = continuation_time;
+ } else {
+ options.begin_time = continuation_time;
+ options.end_time =
+ GetBeginTimeOnDayBoundary(continuation_time) + base::Days(2);
+ }
+
+ // 3rd, lastly, make sure the times don't surpass `begin_time_limit_` or
+ // `now`.
+ options.begin_time = std::clamp(options.begin_time, begin_time_limit_, now);
+ options.end_time = std::clamp(options.end_time, begin_time_limit_, now);
+ options.visit_order = recent_first_
+ ? history::QueryOptions::VisitOrder::RECENT_FIRST
+ : history::QueryOptions::VisitOrder::OLDEST_FIRST;
+
+ return options;
+}
+
bool GetAnnotatedVisitsToCluster::AddUnclusteredVisits(
history::HistoryBackend* backend,
history::QueryOptions options) {
bool limited_by_max_count = false;
- base::ranges::move(
- backend->GetAnnotatedVisits(options, &limited_by_max_count),
- std::back_inserter(annotated_visits_));
+
+ for (const auto& visit :
+ backend->GetAnnotatedVisits(options, &limited_by_max_count)) {
+ // Filter out visits from sync.
+ // TODO(manukh): Consider allowing the clustering backend to handle sync
+ // visits.
+ if (visit.source != history::SOURCE_SYNCED)
+ annotated_visits_.push_back(std::move(visit));
+ }
+
return limited_by_max_count;
}
void GetAnnotatedVisitsToCluster::AddIncompleteVisits(
- history::HistoryBackend* backend) {
+ history::HistoryBackend* backend,
+ base::Time begin_time,
+ base::Time end_time) {
// Now we have enough visits for clustering, add all incomplete visits
// between the current `options.begin_time` and `original_end_time`, as
// otherwise they will be mysteriously missing from the Clusters UI. They
@@ -154,9 +194,8 @@ void GetAnnotatedVisitsToCluster::AddIncompleteVisits(
// `options.max_count`.
const auto& visit_time =
incomplete_visit_context_annotations.visit_row.visit_time;
- if ((!continuation_end_time_.is_null() &&
- visit_time < continuation_end_time_) ||
- (!original_end_time_.is_null() && visit_time >= original_end_time_)) {
+ if ((!begin_time.is_null() && visit_time < begin_time) ||
+ (!end_time.is_null() && visit_time >= end_time)) {
continue;
}
@@ -200,26 +239,42 @@ void GetAnnotatedVisitsToCluster::AddIncompleteVisits(
}
}
-void GetAnnotatedVisitsToCluster::RemoveVisitsFromSync() {
- // Filter out visits from sync.
- // TODO(manukh): Consider allowing the clustering backend to handle sync
- // visits.
- annotated_visits_.erase(
- base::ranges::remove_if(annotated_visits_,
- [](const auto& annotated_visit) {
- return annotated_visit.source ==
- history::SOURCE_SYNCED;
- }),
- annotated_visits_.end());
+void GetAnnotatedVisitsToCluster::IncrementContinuationParams(
+ history::QueryOptions options,
+ bool limited_by_max_count,
+ base::Time now) {
+ continuation_params_.is_continuation = true;
+
+ // If `limited_by_max_count` is true, `annotated_visits_` "shouldn't" be
+ // empty. But it actually can be if a visit's URL is missing from the URL
+ // table. `limited_by_max_count` is set before visits are filtered to
+ // those whose URL is found.
+ // TODO(manukh): We shouldn't skip the day's remaining visits when
+ // `annotated_visits_` is empty.
+ if (limited_by_max_count && !annotated_visits_.empty()) {
+ continuation_params_.continuation_time =
+ annotated_visits_.back().visit_row.visit_time;
+ continuation_params_.is_partial_day = true;
+ } else {
+ continuation_params_.continuation_time =
+ recent_first_ ? options.begin_time : options.end_time;
+ continuation_params_.is_partial_day = false;
+
+ // We've exhausted history if we've reached `begin_time_limit_` (bound to be
+ // at most 90 days old) or `Now()`. This does not necessarily mean we've
+ // added all visits; e.g. `begin_time_limit_` can be more recent than 90
+ // days ago or the initial `continuation_end_time_` could have been older
+ // than now.
+ if (continuation_params_.continuation_time <= begin_time_limit_ ||
+ continuation_params_.continuation_time >= now) {
+ continuation_params_.exhausted_history = true;
+ continuation_params_.is_done = true;
+ }
+ }
}
void GetAnnotatedVisitsToCluster::DoneRunOnMainThread() {
- // Don't give a continuation end time if we exhausted all of History.
- base::Time continuation_end_time_result;
- if (!exhausted_history_)
- continuation_end_time_result = continuation_end_time_;
-
- std::move(callback_).Run(annotated_visits_, continuation_end_time_result);
+ std::move(callback_).Run(annotated_visits_, continuation_params_);
}
} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/history_clusters_db_tasks.h b/chromium/components/history_clusters/core/history_clusters_db_tasks.h
index 7c74ab2d9d4..d1a8e1877ef 100644
--- a/chromium/components/history_clusters/core/history_clusters_db_tasks.h
+++ b/chromium/components/history_clusters/core/history_clusters_db_tasks.h
@@ -12,6 +12,7 @@
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_types.h"
#include "components/history_clusters/core/history_clusters_service.h"
+#include "components/history_clusters/core/history_clusters_types.h"
namespace history_clusters {
@@ -26,17 +27,18 @@ namespace history_clusters {
class GetAnnotatedVisitsToCluster : public history::HistoryDBTask {
public:
using Callback = base::OnceCallback<void(std::vector<history::AnnotatedVisit>,
- base::Time)>;
+ QueryClustersContinuationParams)>;
// For a given `end_time`, this returns an appropriate beginning time
- // designed to avoid breaking up internet browsing sessions. In the morning,
- // it returns 4AM the previous day. In the afternoon, it returns 4AM today.
- static base::Time GetBeginTimeOnDayBoundary(base::Time end_time);
+ // designed to avoid breaking up internet browsing sessions. Before 4PM, it
+ // returns 4AM the previous day. After 4PM, it returns 4AM today.
+ static base::Time GetBeginTimeOnDayBoundary(base::Time time);
GetAnnotatedVisitsToCluster(
- HistoryClustersService::IncompleteVisitMap incomplete_visit_map,
+ IncompleteVisitMap incomplete_visit_map,
base::Time begin_time,
- base::Time end_time,
+ QueryClustersContinuationParams continuation_params,
+ bool recent_first,
Callback callback);
~GetAnnotatedVisitsToCluster() override;
@@ -46,6 +48,14 @@ class GetAnnotatedVisitsToCluster : public history::HistoryDBTask {
void DoneRunOnMainThread() override;
private:
+ // Helper for `RunOnDBThread()` that generates the `QueryOptions` for each
+ // history request. Because `base::Time::Now()` may change during the async
+ // history request, and because determining whether history was exhausted
+ // depends on whether the query reached `Now()`, `now` is set when this
+ // function is called and shared with `IncrementContinuationParams()`.
+ history::QueryOptions GetHistoryQueryOptions(history::HistoryBackend* backend,
+ base::Time now);
+
// Helper for `RunOnDBThread()` that adds complete but unclustered visits
// from `backend` to `annotated_visits_`. Returns whether the visits were
// limited by `options.max_count`.
@@ -54,34 +64,43 @@ class GetAnnotatedVisitsToCluster : public history::HistoryDBTask {
// Helper for `RunOnDBThread()` that adds incomplete visits from
// `incomplete_visit_map_` to `annotated_visits_`.
- void AddIncompleteVisits(history::HistoryBackend* backend);
+ void AddIncompleteVisits(history::HistoryBackend* backend,
+ base::Time begin_time,
+ base::Time end_time);
- // Helper for `RunOnDBThread()` that removes synced visits from
- // `annotated_visits_`.
- void RemoveVisitsFromSync();
+ // Helper for `RunOnDBThread()` that updates `continuation_params_` after each
+ // history request preparing it for the next call. See
+ // `GetHistoryQueryOptions()`'s comment regarding `now`.
+ void IncrementContinuationParams(history::QueryOptions options,
+ bool limited_by_max_count,
+ base::Time now);
// Incomplete visits that have history rows and are withing the time frame of
// the completed visits fetched will be appended to the annotated visits
// returned for clustering. It's used in the DB thread as each filtered visit
// will need to fetch its `referring_visit_of_redirect_chain_start`.
- HistoryClustersService::IncompleteVisitMap incomplete_visit_map_;
+ IncompleteVisitMap incomplete_visit_map_;
+
// The lower bound of the begin times used in the history requests for
// completed visits. This is a lower bound time of all the visits fetched,
// though the visit count cap may be reached before we've queried all the way
// to `begin_time_limit_`.
base::Time begin_time_limit_;
- // The end time used in the initial history request for completed visits.
- // This is the upper bound time of all the visits fetched.
- base::Time original_end_time_;
- // The end time used to continue the query onto the "next page".
- // This is the lower bound time of all the visits fetched.
- base::Time continuation_end_time_;
- // True if we have exhausted history up to `begin_time_limit_` or all of
- // History; i.e., we didn't hit the visit count cap.
- bool exhausted_history_ = false;
+
+ // The current continuation state representing what's already been queried and
+ // where the next query should pick up. Initially set in the constructor and
+ // updated after each history request. The final state will be returned to
+ // `callback_` to be used in the next query task.
+ QueryClustersContinuationParams continuation_params_;
+
+ // Whether to begin with the most recent visits and iterate towards older
+ // visits, or vice versa.
+ bool recent_first_ = true;
+
// Persisted visits retrieved from the history DB thread and returned through
// the callback on the main thread.
std::vector<history::AnnotatedVisit> annotated_visits_;
+
// The callback called on the main thread on completion.
Callback callback_;
};
diff --git a/chromium/components/history_clusters/core/history_clusters_db_tasks_unittest.cc b/chromium/components/history_clusters/core/history_clusters_db_tasks_unittest.cc
index 55bd57bd9fd..f79a2cea46b 100644
--- a/chromium/components/history_clusters/core/history_clusters_db_tasks_unittest.cc
+++ b/chromium/components/history_clusters/core/history_clusters_db_tasks_unittest.cc
@@ -15,16 +15,16 @@ TEST(HistoryClustersDBTasksTest, BeginTimeCalculation) {
base::Time::Exploded end_time_exploded;
base::Time::Exploded expected_begin_time_exploded;
} test_data[] = {
- // Afternoon times yield a 4AM same day `begin_time`.
+ // Times after 4PM yield a 4AM same day `begin_time`.
// 2013-10-11 at 5:30PM and 15 seconds and 400 milliseconds.
{
{2013, 10, 6, 11, 17, 30, 15, 400},
{2013, 10, 6, 11, 4, 0, 0, 0},
},
- // Early afternoon times such as 2:00PM also yield 4AM the same day.
+ // Afternoon times before 4PM such as 2:00PM yield 4AM the day before.
{
{2013, 10, 6, 11, 14, 0, 0, 0},
- {2013, 10, 6, 11, 4, 0, 0, 0},
+ {2013, 10, 5, 10, 4, 0, 0, 0},
},
// Morning times like 10:15AM yield 4AM the day before.
{
diff --git a/chromium/components/history_clusters/core/history_clusters_debug_jsons.cc b/chromium/components/history_clusters/core/history_clusters_debug_jsons.cc
new file mode 100644
index 00000000000..d656b9b18e1
--- /dev/null
+++ b/chromium/components/history_clusters/core/history_clusters_debug_jsons.cc
@@ -0,0 +1,174 @@
+// Copyright 2022 The Chromium 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 "history_clusters_debug_jsons.h"
+
+#include <utility>
+
+#include "base/json/json_writer.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+
+namespace history_clusters {
+
+// Gets a loggable JSON representation of `visits`.
+std::string GetDebugJSONForVisits(
+ const std::vector<history::AnnotatedVisit>& visits) {
+ base::ListValue debug_visits_list;
+ for (auto& visit : visits) {
+ base::DictionaryValue debug_visit;
+ debug_visit.SetIntKey("visitId", visit.visit_row.visit_id);
+ debug_visit.SetStringKey("url", visit.url_row.url().spec());
+ debug_visit.SetStringKey("title", visit.url_row.title());
+ debug_visit.SetIntKey("foreground_time_secs",
+ visit.visit_row.visit_duration.InSeconds());
+ debug_visit.SetIntKey(
+ "navigationTimeMs",
+ visit.visit_row.visit_time.ToDeltaSinceWindowsEpoch().InMilliseconds());
+ debug_visit.SetIntKey("pageEndReason",
+ visit.context_annotations.page_end_reason);
+ debug_visit.SetIntKey("pageTransition",
+ static_cast<int>(visit.visit_row.transition));
+ debug_visit.SetIntKey("referringVisitId",
+ visit.referring_visit_of_redirect_chain_start);
+ debug_visit.SetIntKey("openerVisitId",
+ visit.opener_visit_of_redirect_chain_start);
+ debug_visits_list.Append(std::move(debug_visit));
+ }
+
+ base::DictionaryValue debug_value;
+ debug_value.SetKey("visits", std::move(debug_visits_list));
+ std::string debug_string;
+ if (!base::JSONWriter::WriteWithOptions(
+ debug_value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &debug_string)) {
+ debug_string = "Error: Could not write visits to JSON.";
+ }
+ return debug_string;
+}
+
+// Gets a loggable JSON representation of `clusters`.
+std::string GetDebugJSONForClusters(
+ const std::vector<history::Cluster>& clusters) {
+ // TODO(manukh): `ListValue` is deprecated; replace with `std::vector`.
+ base::ListValue debug_clusters_list;
+ for (const auto& cluster : clusters) {
+ base::DictionaryValue debug_cluster;
+
+ debug_cluster.SetStringKey("label", cluster.label.value_or(u""));
+ base::DictionaryValue debug_keyword_to_data_map;
+ for (const auto& keyword_data_p : cluster.keyword_to_data_map) {
+ base::ListValue debug_collection;
+ for (const auto& collection : keyword_data_p.second.entity_collections) {
+ debug_collection.Append(collection);
+ }
+ base::DictionaryValue debug_keyword_data;
+ debug_keyword_data.SetKey("collections", std::move(debug_collection));
+ debug_keyword_to_data_map.SetKey(base::UTF16ToUTF8(keyword_data_p.first),
+ std::move(debug_keyword_data));
+ }
+ debug_cluster.SetKey("keyword_to_data_map",
+ std::move(debug_keyword_to_data_map));
+ debug_cluster.SetBoolKey("should_show_on_prominent_ui_surfaces",
+ cluster.should_show_on_prominent_ui_surfaces);
+
+ base::ListValue debug_visits;
+ for (const auto& visit : cluster.visits) {
+ base::DictionaryValue debug_visit;
+ debug_visit.SetIntKey("visit_id",
+ visit.annotated_visit.visit_row.visit_id);
+ debug_visit.SetDoubleKey("score", visit.score);
+ base::ListValue debug_categories;
+ for (const auto& category : visit.annotated_visit.content_annotations
+ .model_annotations.categories) {
+ base::DictionaryValue debug_category;
+ debug_category.SetStringKey("name", category.id);
+ debug_category.SetIntKey("value", category.weight);
+ debug_categories.Append(std::move(debug_category));
+ }
+ debug_visit.SetKey("categories", std::move(debug_categories));
+ base::ListValue debug_entities;
+ for (const auto& entity : visit.annotated_visit.content_annotations
+ .model_annotations.entities) {
+ base::DictionaryValue debug_entity;
+ debug_entity.SetStringKey("name", entity.id);
+ debug_entity.SetIntKey("value", entity.weight);
+ debug_entities.Append(std::move(debug_entity));
+ }
+ debug_visit.SetKey("entities", std::move(debug_entities));
+ if (!visit.annotated_visit.content_annotations.search_terms.empty()) {
+ debug_visit.SetStringKey(
+ "search_terms",
+ visit.annotated_visit.content_annotations.search_terms);
+ }
+ debug_visit.SetDoubleKey("site_engagement_score", visit.engagement_score);
+
+ base::ListValue debug_duplicate_visits;
+ for (const auto& duplicate_visit : visit.duplicate_visits) {
+ debug_duplicate_visits.Append(static_cast<int>(
+ duplicate_visit.annotated_visit.visit_row.visit_id));
+ }
+ debug_visit.SetKey("duplicate_visits", std::move(debug_duplicate_visits));
+
+ debug_visits.Append(std::move(debug_visit));
+ }
+ debug_cluster.SetKey("visits", std::move(debug_visits));
+
+ debug_clusters_list.Append(std::move(debug_cluster));
+ }
+
+ std::string debug_string;
+ if (!base::JSONWriter::WriteWithOptions(
+ debug_clusters_list, base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &debug_string)) {
+ debug_string = "Error: Could not write clusters to JSON.";
+ }
+ return debug_string;
+}
+
+template <typename T>
+std::string GetDebugJSONForUrlKeywordSet(
+ const std::unordered_set<T>& keyword_set) {
+ std::vector<base::Value> keyword_list;
+ for (const auto& keyword : keyword_set) {
+ keyword_list.emplace_back(keyword);
+ }
+
+ std::string debug_string;
+ if (!base::JSONWriter::WriteWithOptions(
+ base::Value(keyword_list), base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &debug_string)) {
+ debug_string = "Error: Could not write keywords list to JSON.";
+ }
+ return debug_string;
+}
+
+template std::string GetDebugJSONForUrlKeywordSet<std::u16string>(
+ const std::unordered_set<std::u16string>&);
+template std::string GetDebugJSONForUrlKeywordSet<std::string>(
+ const std::unordered_set<std::string>&);
+
+std::string GetDebugJSONForKeywordMap(
+ const std::unordered_map<std::u16string, history::ClusterKeywordData>&
+ keyword_to_data_map) {
+ base::DictionaryValue debug_keyword_to_data_map;
+ for (const auto& keyword_data_p : keyword_to_data_map) {
+ base::ListValue debug_collection;
+ for (const auto& collection : keyword_data_p.second.entity_collections) {
+ debug_collection.Append(collection);
+ }
+ base::DictionaryValue debug_keyword_data;
+ debug_keyword_data.SetKey("collections", std::move(debug_collection));
+ debug_keyword_to_data_map.SetKey(base::UTF16ToUTF8(keyword_data_p.first),
+ std::move(debug_keyword_data));
+ }
+ std::string debug_string;
+ if (!base::JSONWriter::WriteWithOptions(
+ debug_keyword_to_data_map, base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &debug_string)) {
+ debug_string = "Error: Could not write keywords list to JSON.";
+ }
+ return debug_string;
+}
+
+} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/history_clusters_debug_jsons.h b/chromium/components/history_clusters/core/history_clusters_debug_jsons.h
new file mode 100644
index 00000000000..e587764c2f0
--- /dev/null
+++ b/chromium/components/history_clusters/core/history_clusters_debug_jsons.h
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_DEBUG_JSONS_H_
+#define COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_DEBUG_JSONS_H_
+
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "components/history/core/browser/history_types.h"
+#include "components/history_clusters/core/history_clusters_service.h"
+
+namespace history_clusters {
+
+// Gets a loggable JSON representation of `visits`.
+std::string GetDebugJSONForVisits(
+ const std::vector<history::AnnotatedVisit>& visits);
+
+// Gets a loggable JSON representation of `clusters`.
+std::string GetDebugJSONForClusters(
+ const std::vector<history::Cluster>& clusters);
+
+template <typename T>
+std::string GetDebugJSONForUrlKeywordSet(
+ const std::unordered_set<T>& keyword_set);
+
+std::string GetDebugJSONForKeywordMap(
+ const std::unordered_map<std::u16string, history::ClusterKeywordData>&
+ keyword_to_data_map);
+
+} // namespace history_clusters
+
+#endif // COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_DEBUG_JSONS_H_
diff --git a/chromium/components/history_clusters/core/history_clusters_service.cc b/chromium/components/history_clusters/core/history_clusters_service.cc
index ae95c28c67a..c14b692448f 100644
--- a/chromium/components/history_clusters/core/history_clusters_service.cc
+++ b/chromium/components/history_clusters/core/history_clusters_service.cc
@@ -5,169 +5,34 @@
#include "components/history_clusters/core/history_clusters_service.h"
#include <algorithm>
-#include <iterator>
#include <memory>
-#include <numeric>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/containers/flat_map.h"
-#include "base/feature_list.h"
#include "base/i18n/case_conversion.h"
-#include "base/json/json_writer.h"
#include "base/metrics/histogram_functions.h"
-#include "base/observer_list.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/time/time.h"
#include "base/time/time_to_iso8601.h"
#include "base/timer/elapsed_timer.h"
-#include "base/values.h"
#include "components/history/core/browser/history_backend.h"
#include "components/history/core/browser/history_database.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_types.h"
#include "components/history_clusters/core/config.h"
-#include "components/history_clusters/core/features.h"
-#include "components/history_clusters/core/history_clusters_buildflags.h"
-#include "components/history_clusters/core/history_clusters_db_tasks.h"
+#include "components/history_clusters/core/history_clusters_debug_jsons.h"
#include "components/history_clusters/core/history_clusters_types.h"
#include "components/history_clusters/core/history_clusters_util.h"
+#include "components/history_clusters/core/on_device_clustering_backend.h"
#include "components/optimization_guide/core/entity_metadata_provider.h"
+#include "components/optimization_guide/core/new_optimization_guide_decider.h"
#include "components/site_engagement/core/site_engagement_score_provider.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "ui/base/l10n/time_format.h"
-
-#if BUILDFLAG(BUILD_WITH_ON_DEVICE_CLUSTERING_BACKEND)
-#include "components/history_clusters/core/on_device_clustering_backend.h"
-#endif
namespace history_clusters {
-namespace {
-
-// Gets a loggable JSON representation of `visits`.
-std::string GetDebugJSONForVisits(
- const std::vector<history::AnnotatedVisit>& visits) {
- base::ListValue debug_visits_list;
- for (auto& visit : visits) {
- base::DictionaryValue debug_visit;
- debug_visit.SetIntKey("visitId", visit.visit_row.visit_id);
- debug_visit.SetStringKey("url", visit.url_row.url().spec());
- debug_visit.SetStringKey("title", visit.url_row.title());
- debug_visit.SetIntKey("foreground_time_secs",
- visit.visit_row.visit_duration.InSeconds());
- debug_visit.SetIntKey(
- "navigationTimeMs",
- visit.visit_row.visit_time.ToDeltaSinceWindowsEpoch().InMilliseconds());
- debug_visit.SetIntKey("pageEndReason",
- visit.context_annotations.page_end_reason);
- debug_visit.SetIntKey("pageTransition",
- static_cast<int>(visit.visit_row.transition));
- debug_visit.SetIntKey("referringVisitId",
- visit.referring_visit_of_redirect_chain_start);
- debug_visit.SetIntKey("openerVisitId",
- visit.opener_visit_of_redirect_chain_start);
- debug_visits_list.Append(std::move(debug_visit));
- }
-
- base::DictionaryValue debug_value;
- debug_value.SetKey("visits", std::move(debug_visits_list));
- std::string debug_string;
- if (!base::JSONWriter::WriteWithOptions(
- debug_value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &debug_string)) {
- debug_string = "Error: Could not write visits to JSON.";
- }
- return debug_string;
-}
-
-// Gets a loggable JSON representation of `clusters`.
-std::string GetDebugJSONForClusters(
- const std::vector<history::Cluster>& clusters) {
- // TODO(manukh): `ListValue` is deprecated; replace with `std::vector`.
- base::ListValue debug_clusters_list;
- for (const auto& cluster : clusters) {
- base::DictionaryValue debug_cluster;
-
- debug_cluster.SetStringKey("label", cluster.label.value_or(u""));
- base::ListValue debug_keywords;
- for (const auto& keyword : cluster.keywords) {
- debug_keywords.Append(keyword);
- }
- debug_cluster.SetKey("keywords", std::move(debug_keywords));
- debug_cluster.SetBoolKey("should_show_on_prominent_ui_surfaces",
- cluster.should_show_on_prominent_ui_surfaces);
-
- base::ListValue debug_visits;
- for (const auto& visit : cluster.visits) {
- base::DictionaryValue debug_visit;
- debug_visit.SetIntKey("visit_id",
- visit.annotated_visit.visit_row.visit_id);
- debug_visit.SetDoubleKey("score", visit.score);
- base::ListValue debug_categories;
- for (const auto& category : visit.annotated_visit.content_annotations
- .model_annotations.categories) {
- base::DictionaryValue debug_category;
- debug_category.SetStringKey("name", category.id);
- debug_category.SetIntKey("value", category.weight);
- debug_categories.Append(std::move(debug_category));
- }
- debug_visit.SetKey("categories", std::move(debug_categories));
- base::ListValue debug_entities;
- for (const auto& entity : visit.annotated_visit.content_annotations
- .model_annotations.entities) {
- base::DictionaryValue debug_entity;
- debug_entity.SetStringKey("name", entity.id);
- debug_entity.SetIntKey("value", entity.weight);
- debug_entities.Append(std::move(debug_entity));
- }
- debug_visit.SetKey("entities", std::move(debug_entities));
- debug_visit.SetDoubleKey("site_engagement_score", visit.engagement_score);
-
- base::ListValue debug_duplicate_visits;
- for (const auto& duplicate_visit : visit.duplicate_visits) {
- debug_duplicate_visits.Append(static_cast<int>(
- duplicate_visit.annotated_visit.visit_row.visit_id));
- }
- debug_visit.SetKey("duplicate_visits", std::move(debug_duplicate_visits));
-
- debug_visits.Append(std::move(debug_visit));
- }
- debug_cluster.SetKey("visits", std::move(debug_visits));
-
- debug_clusters_list.Append(std::move(debug_cluster));
- }
-
- std::string debug_string;
- if (!base::JSONWriter::WriteWithOptions(
- debug_clusters_list, base::JSONWriter::OPTIONS_PRETTY_PRINT,
- &debug_string)) {
- debug_string = "Error: Could not write clusters to JSON.";
- }
- return debug_string;
-}
-
-std::string GetDebugJSONForKeywordSet(
- const HistoryClustersService::KeywordSet& keyword_set) {
- std::vector<base::Value> keyword_list;
- for (const auto& keyword : keyword_set) {
- keyword_list.emplace_back(keyword);
- }
-
- std::string debug_string;
- if (!base::JSONWriter::WriteWithOptions(
- base::Value(keyword_list), base::JSONWriter::OPTIONS_PRETTY_PRINT,
- &debug_string)) {
- debug_string = "Error: Could not write keywords list to JSON.";
- }
- return debug_string;
-}
-
-} // namespace
-
VisitDeletionObserver::VisitDeletionObserver(
HistoryClustersService* history_clusters_service)
: history_clusters_service_(history_clusters_service) {}
@@ -191,7 +56,8 @@ HistoryClustersService::HistoryClustersService(
history::HistoryService* history_service,
optimization_guide::EntityMetadataProvider* entity_metadata_provider,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- site_engagement::SiteEngagementScoreProvider* engagement_score_provider)
+ site_engagement::SiteEngagementScoreProvider* engagement_score_provider,
+ optimization_guide::NewOptimizationGuideDecider* optimization_guide_decider)
: is_journeys_enabled_(
GetConfig().is_journeys_enabled_no_locale_check &&
IsApplicationLocaleSupportedByJourneys(application_locale)),
@@ -201,10 +67,9 @@ HistoryClustersService::HistoryClustersService(
visit_deletion_observer_.AttachToHistoryService(history_service);
-#if BUILDFLAG(BUILD_WITH_ON_DEVICE_CLUSTERING_BACKEND)
backend_ = std::make_unique<OnDeviceClusteringBackend>(
- entity_metadata_provider, engagement_score_provider);
-#endif
+ entity_metadata_provider, engagement_score_provider,
+ optimization_guide_decider);
}
HistoryClustersService::~HistoryClustersService() = default;
@@ -280,72 +145,60 @@ void HistoryClustersService::CompleteVisitContextAnnotationsIfReady(
}
}
-void HistoryClustersService::QueryClusters(
+std::unique_ptr<HistoryClustersServiceTaskGetMostRecentClusters>
+HistoryClustersService::QueryClusters(
ClusteringRequestSource clustering_request_source,
base::Time begin_time,
- base::Time end_time,
- QueryClustersCallback callback,
- base::CancelableTaskTracker* task_tracker) {
+ QueryClustersContinuationParams continuation_params,
+ QueryClustersCallback callback) {
if (ShouldNotifyDebugMessage()) {
NotifyDebugMessage("HistoryClustersService::QueryClusters()");
NotifyDebugMessage(
" begin_time = " +
(begin_time.is_null() ? "null" : base::TimeToISO8601(begin_time)));
- NotifyDebugMessage(" end_time = " + (end_time.is_null()
- ? "null"
- : base::TimeToISO8601(end_time)));
- }
-
- if (!backend_) {
NotifyDebugMessage(
- "HistoryClustersService::QueryClusters Error: ClusteringBackend is "
- "nullptr. Returning empty cluster vector.");
- std::move(callback).Run({}, base::Time());
- return;
+ " end_time = " +
+ (continuation_params.continuation_time.is_null()
+ ? "null"
+ : base::TimeToISO8601(continuation_params.continuation_time)));
}
DCHECK(history_service_);
- history_service_->ScheduleDBTask(
- FROM_HERE,
- std::make_unique<GetAnnotatedVisitsToCluster>(
- incomplete_visit_context_annotations_, begin_time, end_time,
- base::BindOnce(&HistoryClustersService::OnGotHistoryVisits,
- weak_ptr_factory_.GetWeakPtr(),
- clustering_request_source, base::TimeTicks::Now(),
- std::move(callback))),
- task_tracker);
+ return std::make_unique<HistoryClustersServiceTaskGetMostRecentClusters>(
+ weak_ptr_factory_.GetWeakPtr(), incomplete_visit_context_annotations_,
+ backend_.get(), history_service_, clustering_request_source, begin_time,
+ continuation_params, std::move(callback));
}
-void HistoryClustersService::RemoveVisits(
- const std::vector<history::ExpireHistoryArgs>& expire_list,
- base::OnceClosure closure,
- base::CancelableTaskTracker* task_tracker) {
- // We expect HistoryService to internally delete any associated annotations
- // and cluster rows. In the future we may remove this indirection entirely.
- history_service_->ExpireHistory(expire_list, std::move(closure),
- task_tracker);
-}
-
-bool HistoryClustersService::DoesQueryMatchAnyCluster(
- const std::string& query) {
+absl::optional<history::ClusterKeywordData>
+HistoryClustersService::DoesQueryMatchAnyCluster(const std::string& query) {
if (!IsJourneysEnabled())
- return false;
+ return absl::nullopt;
// We don't want any omnibox jank for low-end devices.
if (base::SysInfo::IsLowEndDevice())
- return false;
+ return absl::nullopt;
StartKeywordCacheRefresh();
// Early exit for single-character queries, even if it's an exact match.
// We still want to allow for two-character exact matches like "uk".
if (query.length() <= 1)
- return false;
+ return absl::nullopt;
auto query_lower = base::i18n::ToLower(base::UTF8ToUTF16(query));
- return short_keyword_cache_.find(query_lower) != short_keyword_cache_.end() ||
- all_keywords_cache_.find(query_lower) != all_keywords_cache_.end();
+ auto short_it = short_keyword_cache_.find(query_lower);
+ if (short_it != short_keyword_cache_.end()) {
+ return short_it->second;
+ }
+
+ auto it = all_keywords_cache_.find(query_lower);
+ if (it != all_keywords_cache_.end()) {
+ return it->second;
+ }
+
+ return absl::nullopt;
}
bool HistoryClustersService::DoesURLMatchAnyCluster(
@@ -372,7 +225,7 @@ void HistoryClustersService::ClearKeywordCache() {
all_url_keywords_cache_.clear();
short_keyword_cache_.clear();
short_keyword_cache_.clear();
- cache_query_task_tracker_.TryCancelAll();
+ cache_keyword_query_task_.reset();
}
void HistoryClustersService::StartKeywordCacheRefresh() {
@@ -380,27 +233,28 @@ void HistoryClustersService::StartKeywordCacheRefresh() {
// of all clusters. Otherwise, update `short_keyword_cache_` with the
// keywords of only the clusters not represented in all_keywords_cache_.
+ // Don't make new queries if there's a pending query.
+ if (cache_keyword_query_task_ && !cache_keyword_query_task_->Done())
+ return;
+
// 2 hour threshold chosen arbitrarily for cache refresh time.
- if ((base::Time::Now() - all_keywords_cache_timestamp_) > base::Hours(2) &&
- !cache_query_task_tracker_.HasTrackedTasks()) {
+ if ((base::Time::Now() - all_keywords_cache_timestamp_) > base::Hours(2)) {
// Update the timestamp right away, to prevent this from running again.
// (The cache_query_task_tracker_ should also do this.)
all_keywords_cache_timestamp_ = base::Time::Now();
NotifyDebugMessage("Starting all_keywords_cache_ generation.");
- QueryClusters(
+ cache_keyword_query_task_ = QueryClusters(
ClusteringRequestSource::kKeywordCacheGeneration,
/*begin_time=*/base::Time(),
- /*end_time=*/base::Time(),
+ /*continuation_params=*/{},
base::BindOnce(&HistoryClustersService::PopulateClusterKeywordCache,
weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer(),
/*begin_time=*/base::Time(),
- std::make_unique<KeywordSet>(),
+ std::make_unique<KeywordMap>(),
std::make_unique<URLKeywordSet>(), &all_keywords_cache_,
- &all_url_keywords_cache_),
- &cache_query_task_tracker_);
- } else if (!cache_query_task_tracker_.HasTrackedTasks() &&
- (base::Time::Now() - all_keywords_cache_timestamp_).InSeconds() >
+ &all_url_keywords_cache_));
+ } else if ((base::Time::Now() - all_keywords_cache_timestamp_).InSeconds() >
10 &&
(base::Time::Now() - short_keyword_cache_timestamp_).InSeconds() >
10) {
@@ -408,32 +262,32 @@ void HistoryClustersService::StartKeywordCacheRefresh() {
short_keyword_cache_timestamp_ = base::Time::Now();
NotifyDebugMessage("Starting short_keywords_cache_ generation.");
- QueryClusters(
+ cache_keyword_query_task_ = QueryClusters(
ClusteringRequestSource::kKeywordCacheGeneration,
- /*begin_time=*/all_keywords_cache_timestamp_, /*end_time=*/base::Time(),
+ /*begin_time=*/all_keywords_cache_timestamp_,
+ /*continuation_params=*/{},
base::BindOnce(&HistoryClustersService::PopulateClusterKeywordCache,
weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer(),
all_keywords_cache_timestamp_,
- std::make_unique<KeywordSet>(),
+ std::make_unique<KeywordMap>(),
std::make_unique<URLKeywordSet>(), &short_keyword_cache_,
- &short_url_keywords_cache_),
- &cache_query_task_tracker_);
+ &short_url_keywords_cache_));
}
}
void HistoryClustersService::PopulateClusterKeywordCache(
base::ElapsedTimer total_latency_timer,
base::Time begin_time,
- std::unique_ptr<KeywordSet> keyword_accumulator,
+ std::unique_ptr<KeywordMap> keyword_accumulator,
std::unique_ptr<URLKeywordSet> url_keyword_accumulator,
- KeywordSet* cache,
+ KeywordMap* cache,
URLKeywordSet* url_cache,
std::vector<history::Cluster> clusters,
- base::Time continuation_end_time) {
+ QueryClustersContinuationParams continuation_params) {
base::ElapsedThreadTimer populate_keywords_thread_timer;
const size_t max_keyword_phrases = GetConfig().max_keyword_phrases;
- // Copy keywords from every cluster into a the accumulator set.
+ // Copy keywords from every cluster into the accumulator set.
for (auto& cluster : clusters) {
if (!cluster.should_show_on_prominent_ui_surfaces) {
// `clusters` doesn't have any post-processing, so we need to skip
@@ -448,8 +302,12 @@ void HistoryClustersService::PopulateClusterKeywordCache(
// Lowercase the keywords for case insensitive matching while adding to the
// accumulator.
if (keyword_accumulator->size() < max_keyword_phrases) {
- for (auto& keyword : cluster.keywords) {
- keyword_accumulator->insert(base::i18n::ToLower(keyword));
+ for (const auto& keyword_data_p : cluster.keyword_to_data_map) {
+ auto keyword = base::i18n::ToLower(keyword_data_p.first);
+ if (keyword_accumulator->find(keyword) == keyword_accumulator->end()) {
+ keyword_accumulator->insert(
+ std::make_pair(keyword, keyword_data_p.second));
+ }
}
}
@@ -479,19 +337,18 @@ void HistoryClustersService::PopulateClusterKeywordCache(
// haven't reached the soft cap `max_keyword_phrases` (or there is no cap).
constexpr char kKeywordCacheThreadTimeUmaName[] =
"History.Clusters.KeywordCache.ThreadTime";
- if (!continuation_end_time.is_null() &&
+ if (!continuation_params.is_done &&
(keyword_accumulator->size() < max_keyword_phrases ||
url_keyword_accumulator->size() < max_keyword_phrases)) {
- QueryClusters(
+ cache_keyword_query_task_ = QueryClusters(
ClusteringRequestSource::kKeywordCacheGeneration, begin_time,
- continuation_end_time,
+ continuation_params,
base::BindOnce(&HistoryClustersService::PopulateClusterKeywordCache,
weak_ptr_factory_.GetWeakPtr(),
std::move(total_latency_timer), begin_time,
// Pass on the accumulator sets to the next callback.
std::move(keyword_accumulator),
- std::move(url_keyword_accumulator), cache, url_cache),
- &cache_query_task_tracker_);
+ std::move(url_keyword_accumulator), cache, url_cache));
// Log this even if we go back for more clusters.
base::UmaHistogramTimes(kKeywordCacheThreadTimeUmaName,
populate_keywords_thread_timer.Elapsed());
@@ -504,8 +361,10 @@ void HistoryClustersService::PopulateClusterKeywordCache(
*cache = std::move(*keyword_accumulator);
*url_cache = std::move(*url_keyword_accumulator);
if (ShouldNotifyDebugMessage()) {
- NotifyDebugMessage("Cache construction complete:");
- NotifyDebugMessage(GetDebugJSONForKeywordSet(*cache));
+ NotifyDebugMessage("Cache construction complete; keyword cache:");
+ NotifyDebugMessage(GetDebugJSONForKeywordMap(*cache));
+ NotifyDebugMessage("Url cache:");
+ NotifyDebugMessage(GetDebugJSONForUrlKeywordSet(*url_cache));
}
// Record keyword phrase & keyword counts for the appropriate cache.
@@ -525,65 +384,4 @@ void HistoryClustersService::PopulateClusterKeywordCache(
total_latency_timer.Elapsed());
}
-void HistoryClustersService::OnGotHistoryVisits(
- ClusteringRequestSource clustering_request_source,
- base::TimeTicks query_visits_start,
- QueryClustersCallback callback,
- std::vector<history::AnnotatedVisit> annotated_visits,
- base::Time continuation_end_time) const {
- if (ShouldNotifyDebugMessage()) {
- NotifyDebugMessage("HistoryClustersService::OnGotHistoryVisits()");
- NotifyDebugMessage(base::StringPrintf(" annotated_visits.size() = %zu",
- annotated_visits.size()));
- NotifyDebugMessage(" continuation_end_time = " +
- (continuation_end_time.is_null()
- ? "null (i.e. exhausted history)"
- : base::TimeToISO8601(continuation_end_time)));
- }
-
- base::UmaHistogramTimes(
- "Histogram.Clusters.Backend.QueryAnnotatedVisitsLatency",
- base::TimeTicks::Now() - query_visits_start);
-
- if (annotated_visits.empty()) {
- // Early exit without calling backend if there's no annotated visits.
- std::move(callback).Run({}, continuation_end_time);
- return;
- }
-
- if (ShouldNotifyDebugMessage()) {
- NotifyDebugMessage(" Visits JSON follows:");
- NotifyDebugMessage(GetDebugJSONForVisits(annotated_visits));
- NotifyDebugMessage("Calling backend_->GetClusters()");
- }
- base::UmaHistogramCounts1000("History.Clusters.Backend.NumVisitsToCluster",
- static_cast<int>(annotated_visits.size()));
-
- backend_->GetClusters(
- clustering_request_source,
- base::BindOnce(&HistoryClustersService::OnGotRawClusters,
- weak_ptr_factory_.GetWeakPtr(), continuation_end_time,
- base::TimeTicks::Now(), std::move(callback)),
- std::move(annotated_visits));
-}
-
-void HistoryClustersService::OnGotRawClusters(
- base::Time continuation_end_time,
- base::TimeTicks cluster_start_time,
- QueryClustersCallback callback,
- std::vector<history::Cluster> clusters) const {
- base::UmaHistogramTimes("History.Clusters.Backend.GetClustersLatency",
- base::TimeTicks::Now() - cluster_start_time);
- base::UmaHistogramCounts1000("History.Clusters.Backend.NumClustersReturned",
- clusters.size());
-
- if (ShouldNotifyDebugMessage()) {
- NotifyDebugMessage("HistoryClustersService::OnGotRawClusters()");
- NotifyDebugMessage(" Raw Clusters from Backend JSON follows:");
- NotifyDebugMessage(GetDebugJSONForClusters(clusters));
- }
-
- std::move(callback).Run(clusters, continuation_end_time);
-}
-
} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/history_clusters_service.h b/chromium/components/history_clusters/core/history_clusters_service.h
index 7ce1b5925c9..36b0948b24f 100644
--- a/chromium/components/history_clusters/core/history_clusters_service.h
+++ b/chromium/components/history_clusters/core/history_clusters_service.h
@@ -24,12 +24,14 @@
#include "components/history/core/browser/history_service_observer.h"
#include "components/history/core/browser/history_types.h"
#include "components/history_clusters/core/clustering_backend.h"
+#include "components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.h"
#include "components/history_clusters/core/history_clusters_types.h"
#include "components/keyed_service/core/keyed_service.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace optimization_guide {
class EntityMetadataProvider;
+class NewOptimizationGuideDecider;
} // namespace optimization_guide
namespace site_engagement {
@@ -74,13 +76,10 @@ class HistoryClustersService : public base::SupportsUserData,
virtual void OnDebugMessage(const std::string& message) = 0;
};
- // Used to track incomplete, unpersisted visits.
- using IncompleteVisitMap =
- std::map<int64_t, IncompleteVisitContextAnnotations>;
-
- // Use std::unordered_set here because we have ~1000 elements at the 99th
+ // Use std::unordered_map here because we have ~1000 elements at the 99th
// percentile, and we do synchronous lookups as the user types in the omnibox.
- using KeywordSet = std::unordered_set<std::u16string>;
+ using KeywordMap =
+ std::unordered_map<std::u16string, history::ClusterKeywordData>;
using URLKeywordSet = std::unordered_set<std::string>;
// `url_loader_factory` is allowed to be nullptr, like in unit tests.
@@ -91,7 +90,9 @@ class HistoryClustersService : public base::SupportsUserData,
history::HistoryService* history_service,
optimization_guide::EntityMetadataProvider* entity_metadata_provider,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- site_engagement::SiteEngagementScoreProvider* engagement_score_provider);
+ site_engagement::SiteEngagementScoreProvider* engagement_score_provider,
+ optimization_guide::NewOptimizationGuideDecider*
+ optimization_guide_decider);
HistoryClustersService(const HistoryClustersService&) = delete;
HistoryClustersService& operator=(const HistoryClustersService&) = delete;
~HistoryClustersService() override;
@@ -142,36 +143,34 @@ class HistoryClustersService : public base::SupportsUserData,
// `QueryClustersState` instead.
//
// Returns the freshest clusters created from the user visit history based on
- // `query`, `begin_time`, and `end_time`.
+ // `query`, `begin_time`, and `continuation_params`.
// - `begin_time` is an inclusive lower bound. In the general case where the
// caller wants to traverse to the start of history, `base::Time()` should
// be used.
- // - `end_time` is an exclusive upper bound and should be set to
- // `base::Time()` if the caller wants the newest visits.
+ // - `continuation_params` represents where the previous request left off. It
+ // should be set to the default initialized
+ // `QueryClustersContinuationParams`
+ // if the caller wants the newest visits.
// The returned clusters are sorted in reverse-chronological order based on
// their highest scoring visit. The visits within each cluster are sorted by
// score, from highest to lowest.
//
// TODO(tommycli): Investigate entirely hiding access to this low-level method
// behind QueryClustersState.
- void QueryClusters(ClusteringRequestSource clustering_request_source,
- base::Time begin_time,
- base::Time end_time,
- QueryClustersCallback callback,
- base::CancelableTaskTracker* task_tracker);
-
- // Removes all visits to the specified URLs in the specified time ranges in
- // `expire_list`. Calls `closure` when done.
- void RemoveVisits(const std::vector<history::ExpireHistoryArgs>& expire_list,
- base::OnceClosure closure,
- base::CancelableTaskTracker* task_tracker);
-
- // Returns true synchronously if `query` matches a cluster keyword. This
- // ignores clusters with only one visit to avoid overtriggering.
- // Note: This depends on the cache state, so this may kick off a cache refresh
- // request while immediately returning false. It's expected that on the next
- // keystroke, the cache may be ready and return true then.
- bool DoesQueryMatchAnyCluster(const std::string& query);
+ std::unique_ptr<HistoryClustersServiceTaskGetMostRecentClusters>
+ QueryClusters(ClusteringRequestSource clustering_request_source,
+ base::Time begin_time,
+ QueryClustersContinuationParams continuation_params,
+ QueryClustersCallback callback);
+
+ // Returns matched keyword data from cache synchronously if `query` matches a
+ // cluster keyword. This ignores clusters with only one visit to avoid
+ // overtriggering. Note: This depends on the cache state, so this may kick off
+ // a cache refresh request while immediately returning null data. It's
+ // expected that on the next keystroke, the cache may be ready and return the
+ // matched keyword data then.
+ absl::optional<history::ClusterKeywordData> DoesQueryMatchAnyCluster(
+ const std::string& query);
// Returns true if `url_keyword` matches a URL in a significant cluster. This
// may kick off a cache refresh while still immediately returning false.
@@ -195,25 +194,12 @@ class HistoryClustersService : public base::SupportsUserData,
void PopulateClusterKeywordCache(
base::ElapsedTimer total_latency_timer,
base::Time begin_time,
- std::unique_ptr<KeywordSet> keyword_accumulator,
+ std::unique_ptr<KeywordMap> keyword_accumulator,
std::unique_ptr<URLKeywordSet> url_keyword_accumulator,
- KeywordSet* cache,
+ KeywordMap* cache,
URLKeywordSet* url_cache,
std::vector<history::Cluster> clusters,
- base::Time continuation_end_time);
-
- // Internally used callback for `QueryClusters()`.
- void OnGotHistoryVisits(ClusteringRequestSource clustering_request_source,
- base::TimeTicks query_visits_start,
- QueryClustersCallback callback,
- std::vector<history::AnnotatedVisit> annotated_visits,
- base::Time continuation_end_time) const;
-
- // Runs on UI thread. Internally used callback for `OnGotHistoryVisits()`.
- void OnGotRawClusters(base::Time continuation_end_time,
- base::TimeTicks cluster_start_time,
- QueryClustersCallback callback,
- std::vector<history::Cluster> clusters) const;
+ QueryClustersContinuationParams continuation_params);
// True if Journeys is enabled based on field trial and locale checks.
const bool is_journeys_enabled_;
@@ -233,7 +219,7 @@ class HistoryClustersService : public base::SupportsUserData,
// synchronously as the user types in the omnibox. Also save the timestamp
// the cache was generated so we can periodically re-generate.
// TODO(tommycli): Make a smarter mechanism for regenerating the cache.
- KeywordSet all_keywords_cache_;
+ KeywordMap all_keywords_cache_;
URLKeywordSet all_url_keywords_cache_;
base::Time all_keywords_cache_timestamp_;
@@ -247,11 +233,15 @@ class HistoryClustersService : public base::SupportsUserData,
// 2) Exclude keywords since keywords of size-1 clusters are not cached.
// TODO(manukh) This is a "band aid" fix to missing keywords for recent
// visits.
- KeywordSet short_keyword_cache_;
+ KeywordMap short_keyword_cache_;
URLKeywordSet short_url_keywords_cache_;
base::Time short_keyword_cache_timestamp_;
- base::CancelableTaskTracker cache_query_task_tracker_;
+ // Tracks the current keyword task. Will be `nullptr` or
+ // `cache_keyword_query_task_.Done()` will be true if there is no ongoing
+ // task.
+ std::unique_ptr<HistoryClustersServiceTaskGetMostRecentClusters>
+ cache_keyword_query_task_;
// A list of observers for this service.
base::ObserverList<Observer> observers_;
diff --git a/chromium/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.cc b/chromium/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.cc
new file mode 100644
index 00000000000..f5fe8df8adb
--- /dev/null
+++ b/chromium/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.cc
@@ -0,0 +1,148 @@
+// Copyright 2022 The Chromium 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 "history_clusters_service_task_get_most_recent_clusters.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time_to_iso8601.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history_clusters/core/clustering_backend.h"
+#include "components/history_clusters/core/config.h"
+#include "components/history_clusters/core/history_clusters_db_tasks.h"
+#include "components/history_clusters/core/history_clusters_debug_jsons.h"
+
+namespace history_clusters {
+
+HistoryClustersServiceTaskGetMostRecentClusters::
+ HistoryClustersServiceTaskGetMostRecentClusters(
+ base::WeakPtr<HistoryClustersService> weak_history_clusters_service,
+ const IncompleteVisitMap incomplete_visit_context_annotations,
+ ClusteringBackend* const backend,
+ history::HistoryService* const history_service,
+ ClusteringRequestSource clustering_request_source,
+ base::Time begin_time,
+ QueryClustersContinuationParams continuation_params,
+ QueryClustersCallback callback)
+ : weak_history_clusters_service_(std::move(weak_history_clusters_service)),
+ incomplete_visit_context_annotations_(
+ incomplete_visit_context_annotations),
+ backend_(backend),
+ history_service_(history_service),
+ clustering_request_source_(clustering_request_source),
+ begin_time_(begin_time),
+ continuation_params_(continuation_params),
+ callback_(std::move(callback)) {
+ DCHECK(weak_history_clusters_service_);
+ DCHECK(history_service_);
+ Start();
+}
+
+HistoryClustersServiceTaskGetMostRecentClusters::
+ ~HistoryClustersServiceTaskGetMostRecentClusters() = default;
+
+void HistoryClustersServiceTaskGetMostRecentClusters::Start() {
+ // Shouldn't request more clusters if history has been exhausted.
+ DCHECK(!continuation_params_.is_done);
+
+ if (!backend_) {
+ // Early exit if we won't be able to cluster visits.
+ weak_history_clusters_service_->NotifyDebugMessage(
+ "HistoryClustersService::QueryClusters Error: ClusteringBackend is "
+ "nullptr. Returning empty cluster vector.");
+ done_ = true;
+ std::move(callback_).Run({}, QueryClustersContinuationParams::DoneParams());
+
+ } else {
+ history_service_get_annotated_visits_to_cluster_start_time_ =
+ base::TimeTicks::Now();
+ history_service_->ScheduleDBTask(
+ FROM_HERE,
+ std::make_unique<GetAnnotatedVisitsToCluster>(
+ incomplete_visit_context_annotations_, begin_time_,
+ continuation_params_, true,
+ base::BindOnce(&HistoryClustersServiceTaskGetMostRecentClusters::
+ OnGotAnnotatedVisitsToCluster,
+ weak_ptr_factory_.GetWeakPtr())),
+ &task_tracker_);
+ }
+}
+
+void HistoryClustersServiceTaskGetMostRecentClusters::
+ OnGotAnnotatedVisitsToCluster(
+ std::vector<history::AnnotatedVisit> annotated_visits,
+ QueryClustersContinuationParams continuation_params) {
+ DCHECK(backend_);
+
+ if (weak_history_clusters_service_->ShouldNotifyDebugMessage()) {
+ weak_history_clusters_service_->NotifyDebugMessage(
+ "HistoryClustersServiceTaskGetMostRecentClusters::OnGotHistoryVisits("
+ ")");
+ weak_history_clusters_service_->NotifyDebugMessage(base::StringPrintf(
+ " annotated_visits.size() = %zu", annotated_visits.size()));
+ weak_history_clusters_service_->NotifyDebugMessage(
+ " continuation_time = " +
+ (continuation_params.continuation_time.is_null()
+ ? "null (i.e. exhausted history)"
+ : base::TimeToISO8601(continuation_params.continuation_time)));
+ }
+
+ base::UmaHistogramTimes(
+ "Histogram.Clusters.Backend.QueryAnnotatedVisitsLatency",
+ base::TimeTicks::Now() -
+ history_service_get_annotated_visits_to_cluster_start_time_);
+
+ if (annotated_visits.empty()) {
+ // Early exit without calling backend if there's no annotated visits.
+ done_ = true;
+ std::move(callback_).Run({}, QueryClustersContinuationParams::DoneParams());
+
+ } else {
+ if (weak_history_clusters_service_->ShouldNotifyDebugMessage()) {
+ weak_history_clusters_service_->NotifyDebugMessage(
+ " Visits JSON follows:");
+ weak_history_clusters_service_->NotifyDebugMessage(
+ GetDebugJSONForVisits(annotated_visits));
+ weak_history_clusters_service_->NotifyDebugMessage(
+ "Calling backend_->GetClusters()");
+ }
+ base::UmaHistogramCounts1000("History.Clusters.Backend.NumVisitsToCluster",
+ static_cast<int>(annotated_visits.size()));
+ backend_get_clusters_start_time_ = base::TimeTicks::Now();
+ backend_->GetClusters(
+ clustering_request_source_,
+ base::BindOnce(&HistoryClustersServiceTaskGetMostRecentClusters::
+ OnGotModelClusters,
+ weak_ptr_factory_.GetWeakPtr(), continuation_params),
+ std::move(annotated_visits));
+ }
+}
+
+void HistoryClustersServiceTaskGetMostRecentClusters::OnGotModelClusters(
+ QueryClustersContinuationParams continuation_params,
+ std::vector<history::Cluster> clusters) {
+ base::UmaHistogramTimes(
+ "History.Clusters.Backend.GetClustersLatency",
+ base::TimeTicks::Now() - backend_get_clusters_start_time_);
+ base::UmaHistogramCounts1000("History.Clusters.Backend.NumClustersReturned",
+ clusters.size());
+
+ if (weak_history_clusters_service_->ShouldNotifyDebugMessage()) {
+ weak_history_clusters_service_->NotifyDebugMessage(
+ "HistoryClustersService::OnGotRawClusters()");
+ weak_history_clusters_service_->NotifyDebugMessage(
+ " Raw Clusters from Backend JSON follows:");
+ weak_history_clusters_service_->NotifyDebugMessage(
+ GetDebugJSONForClusters(clusters));
+ }
+
+ done_ = true;
+ std::move(callback_).Run(clusters, continuation_params);
+}
+
+} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.h b/chromium/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.h
new file mode 100644
index 00000000000..b1eadbec70e
--- /dev/null
+++ b/chromium/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.h
@@ -0,0 +1,89 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_SERVICE_TASK_GET_MOST_RECENT_CLUSTERS_H_
+#define COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_SERVICE_TASK_GET_MOST_RECENT_CLUSTERS_H_
+
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "base/time/time.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/browser/history_types.h"
+#include "components/history_clusters/core/clustering_backend.h"
+#include "components/history_clusters/core/history_clusters_types.h"
+
+namespace history_clusters {
+
+class HistoryClustersService;
+
+class HistoryClustersServiceTaskGetMostRecentClusters {
+ public:
+ HistoryClustersServiceTaskGetMostRecentClusters(
+ base::WeakPtr<HistoryClustersService> weak_history_clusters_service,
+ const IncompleteVisitMap incomplete_visit_context_annotations,
+ ClusteringBackend* const backend,
+ history::HistoryService* const history_service,
+ ClusteringRequestSource clustering_request_source,
+ base::Time begin_time,
+ QueryClustersContinuationParams continuation_params,
+ QueryClustersCallback callback);
+ ~HistoryClustersServiceTaskGetMostRecentClusters();
+
+ bool Done() { return done_; }
+
+ private:
+ // Invoked during construction. Will asyncly request annotated visits from
+ // `GetAnnotatedVisitsToCluster`.
+ void Start();
+
+ // Invoked after `Start()` asyncly fetches annotated visits. Will asyncly
+ // request clusters from `ClusteringBackend`.
+ void OnGotAnnotatedVisitsToCluster(
+ std::vector<history::AnnotatedVisit> annotated_visits,
+ QueryClustersContinuationParams continuation_params);
+
+ // Invoked after `OnGotAnnotatedVisitsToCluster()` asyncly obtains clusters.
+ // Will synchronously invoke `callback_`.
+ void OnGotModelClusters(QueryClustersContinuationParams continuation_params,
+ std::vector<history::Cluster> clusters);
+
+ // Never nullptr.
+ base::WeakPtr<HistoryClustersService> weak_history_clusters_service_;
+ const IncompleteVisitMap incomplete_visit_context_annotations_;
+ // Can be nullptr.
+ ClusteringBackend* const backend_;
+ // Non-owning pointer, but never nullptr.
+ history::HistoryService* const history_service_;
+
+ // Used to make requests to `ClusteringBackend`.
+ ClusteringRequestSource clustering_request_source_;
+
+ // Used to make requests to `GetAnnotatedVisitsToCluster`.
+ base::Time begin_time_;
+ QueryClustersContinuationParams continuation_params_;
+ base::CancelableTaskTracker task_tracker_;
+
+ // Invoked after `OnGotModelClusters().
+ QueryClustersCallback callback_;
+
+ // When `Start()` kicked off the request to fetch visits to cluster.
+ base::TimeTicks history_service_get_annotated_visits_to_cluster_start_time_;
+ // When `OnGotAnnotatedVisitsToCluster()` kicked off the request to cluster
+ // the visits.
+ base::TimeTicks backend_get_clusters_start_time_;
+
+ // Set to true when `callback_` is invoked, either with clusters or no
+ // clusters.
+ bool done_ = false;
+
+ // Used for async callbacks.
+ base::WeakPtrFactory<HistoryClustersServiceTaskGetMostRecentClusters>
+ weak_ptr_factory_{this};
+};
+
+} // namespace history_clusters
+
+#endif // COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_SERVICE_TASK_GET_MOST_RECENT_CLUSTERS_H_
diff --git a/chromium/components/history_clusters/core/history_clusters_service_test_api.cc b/chromium/components/history_clusters/core/history_clusters_service_test_api.cc
index 1fad8545ec0..d62cc7420ea 100644
--- a/chromium/components/history_clusters/core/history_clusters_service_test_api.cc
+++ b/chromium/components/history_clusters/core/history_clusters_service_test_api.cc
@@ -100,7 +100,8 @@ std::vector<history::AnnotatedVisit> GetHardcodedTestVisits() {
return visits;
}
-history::ClusterVisit GetHardcodedClusterVisit(history::VisitID visit_id) {
+history::ClusterVisit GetHardcodedClusterVisit(history::VisitID visit_id,
+ float score) {
const auto& visits = GetHardcodedTestVisits();
for (const auto& visit : visits) {
if (visit.visit_row.visit_id != visit_id)
@@ -111,7 +112,7 @@ history::ClusterVisit GetHardcodedClusterVisit(history::VisitID visit_id) {
cluster_visit.normalized_url = visit.url_row.url();
cluster_visit.url_for_deduping =
ComputeURLForDeduping(cluster_visit.normalized_url);
- cluster_visit.score = 0.5;
+ cluster_visit.score = score;
return cluster_visit;
}
diff --git a/chromium/components/history_clusters/core/history_clusters_service_test_api.h b/chromium/components/history_clusters/core/history_clusters_service_test_api.h
index 8a4f0c78d6a..fd1652c610b 100644
--- a/chromium/components/history_clusters/core/history_clusters_service_test_api.h
+++ b/chromium/components/history_clusters/core/history_clusters_service_test_api.h
@@ -58,6 +58,14 @@ class HistoryClustersServiceTestApi {
history_clusters_service_->short_keyword_cache_timestamp_ = time;
}
+ void SetAllKeywordsCache(HistoryClustersService::KeywordMap cache) {
+ history_clusters_service_->all_keywords_cache_ = cache;
+ }
+
+ void SetAllUrlKeywordsCache(HistoryClustersService::URLKeywordSet cache) {
+ history_clusters_service_->all_url_keywords_cache_ = cache;
+ }
+
HistoryClustersService* const history_clusters_service_;
history::HistoryService* const history_service_;
};
@@ -66,7 +74,8 @@ class HistoryClustersServiceTestApi {
std::vector<history::AnnotatedVisit> GetHardcodedTestVisits();
// Fetches the hardcoded `ClusterVisit` with ID `visit_id`.
-history::ClusterVisit GetHardcodedClusterVisit(history::VisitID visit_id);
+history::ClusterVisit GetHardcodedClusterVisit(history::VisitID visit_id,
+ float score = 0.5);
} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/history_clusters_service_unittest.cc b/chromium/components/history_clusters/core/history_clusters_service_unittest.cc
index b54c48f1d8d..5d1db356baa 100644
--- a/chromium/components/history_clusters/core/history_clusters_service_unittest.cc
+++ b/chromium/components/history_clusters/core/history_clusters_service_unittest.cc
@@ -10,6 +10,7 @@
#include "base/callback_forward.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/location.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/task/cancelable_task_tracker.h"
@@ -21,12 +22,14 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/history/core/browser/history_context.h"
+#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/url_row.h"
#include "components/history/core/test/history_service_test_util.h"
#include "components/history_clusters/core/clustering_backend.h"
#include "components/history_clusters/core/config.h"
#include "components/history_clusters/core/features.h"
+#include "components/history_clusters/core/history_clusters_db_tasks.h"
#include "components/history_clusters/core/history_clusters_service_test_api.h"
#include "components/history_clusters/core/history_clusters_types.h"
#include "components/history_clusters/core/history_clusters_util.h"
@@ -106,8 +109,9 @@ class HistoryClustersServiceTestBase : public testing::Test {
history_clusters_service_ = std::make_unique<HistoryClustersService>(
"en-US", history_service_.get(),
/*entity_metadata_provider=*/nullptr,
+ /*url_loader_factory=*/nullptr,
/*engagement_score_provider=*/nullptr,
- /*url_loader_factory=*/nullptr);
+ /*optimization_guide_decider=*/nullptr);
history_clusters_service_test_api_ =
std::make_unique<HistoryClustersServiceTestApi>(
@@ -126,36 +130,40 @@ class HistoryClustersServiceTestBase : public testing::Test {
// Add hardcoded completed visits with context annotations to the history
// database.
void AddHardcodedTestDataToHistoryService() {
- history::ContextID context_id = reinterpret_cast<history::ContextID>(1);
-
- for (auto& visit : GetHardcodedTestVisits()) {
- history::HistoryAddPageArgs add_page_args;
- add_page_args.context_id = context_id;
- add_page_args.nav_entry_id = next_navigation_id_;
- add_page_args.url = visit.url_row.url();
- add_page_args.title = visit.url_row.title();
- add_page_args.time = visit.visit_row.visit_time;
- add_page_args.visit_source = visit.source;
- history_service_->AddPage(add_page_args);
- history_service_->UpdateWithPageEndTime(
- context_id, next_navigation_id_, visit.url_row.url(),
- visit.visit_row.visit_time + visit.visit_row.visit_duration);
-
- auto& incomplete_visit_context_annotations =
- history_clusters_service_
- ->GetOrCreateIncompleteVisitContextAnnotations(
- next_navigation_id_);
- incomplete_visit_context_annotations.visit_row = visit.visit_row;
- incomplete_visit_context_annotations.url_row = visit.url_row;
- incomplete_visit_context_annotations.context_annotations =
- visit.context_annotations;
- incomplete_visit_context_annotations.status.history_rows = true;
- incomplete_visit_context_annotations.status.navigation_ended = true;
- incomplete_visit_context_annotations.status.navigation_end_signals = true;
- history_clusters_service_->CompleteVisitContextAnnotationsIfReady(
- next_navigation_id_);
- next_navigation_id_++;
- }
+ for (auto& visit : GetHardcodedTestVisits())
+ AddCompleteVisit(visit);
+ }
+
+ void AddCompleteVisit(const history::AnnotatedVisit& visit) {
+ static const history::ContextID context_id =
+ reinterpret_cast<history::ContextID>(1);
+
+ history::HistoryAddPageArgs add_page_args;
+ add_page_args.context_id = context_id;
+ add_page_args.nav_entry_id = next_navigation_id_;
+ add_page_args.url = visit.url_row.url();
+ add_page_args.title = visit.url_row.title();
+ add_page_args.time = visit.visit_row.visit_time;
+ add_page_args.visit_source = visit.source;
+ history_service_->AddPage(add_page_args);
+ history_service_->UpdateWithPageEndTime(
+ context_id, next_navigation_id_, visit.url_row.url(),
+ visit.visit_row.visit_time + visit.visit_row.visit_duration);
+
+ auto& incomplete_visit_context_annotations =
+ history_clusters_service_->GetOrCreateIncompleteVisitContextAnnotations(
+ next_navigation_id_);
+ incomplete_visit_context_annotations.visit_row = visit.visit_row;
+ incomplete_visit_context_annotations.url_row = visit.url_row;
+ incomplete_visit_context_annotations.context_annotations =
+ visit.context_annotations;
+ incomplete_visit_context_annotations.status.history_rows = true;
+ incomplete_visit_context_annotations.status.navigation_ended = true;
+ incomplete_visit_context_annotations.status.navigation_end_signals = true;
+ history_clusters_service_->CompleteVisitContextAnnotationsIfReady(
+ next_navigation_id_);
+
+ next_navigation_id_++;
}
// Add an incomplete visit context annotations to the in memory incomplete
@@ -230,8 +238,6 @@ class HistoryClustersServiceTestBase : public testing::Test {
// Non-owning pointer. The actual owner is `history_clusters_service_`.
TestClusteringBackend* test_clustering_backend_;
- base::CancelableTaskTracker task_tracker_;
-
// Used to verify the async callback is invoked.
base::RunLoop run_loop_;
base::RepeatingClosure run_loop_quit_;
@@ -287,11 +293,11 @@ TEST_F(HistoryClustersServiceTest, HardCapOnVisitsFetchedFromHistory) {
}
history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
- history_clusters_service_->QueryClusters(
+ const auto task = history_clusters_service_->QueryClusters(
ClusteringRequestSource::kKeywordCacheGeneration,
- /*begin_time=*/base::Time(), /*end_time=*/base::Time::Now(),
- base::DoNothing(), // Only need to verify the correct request is sent.
- &task_tracker_);
+ /*begin_time=*/base::Time(), /*continuation_params=*/{},
+ base::DoNothing() // Only need to verify the correct request is sent
+ );
test_clustering_backend_->WaitForGetClustersCall();
history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
@@ -305,14 +311,12 @@ TEST_F(HistoryClustersServiceTest, QueryClustersIncompleteAndPersistedVisits) {
auto days_ago = [](int days) { return base::Time::Now() - base::Days(days); };
- // Create incomplete visits; only 3 & 4 should be returned by the query.
+ // Create incomplete visits; only 6 & 7 should be returned by the query.
AddIncompleteVisit(6, 6, days_ago(1));
AddIncompleteVisit(0, 0, days_ago(1)); // Missing history rows.
AddIncompleteVisit(7, 7, days_ago(90));
AddIncompleteVisit(8, 8, days_ago(0)); // Too recent.
AddIncompleteVisit(9, 9, days_ago(93)); // Too old.
- AddIncompleteVisit(3, 3, days_ago(90)); // Visit 3 was added to the history
- // database with source synced.
AddIncompleteVisit(
10, 10, days_ago(1),
ui::PageTransitionFromInt(805306372)); // Non-visible page transition.
@@ -320,16 +324,17 @@ TEST_F(HistoryClustersServiceTest, QueryClustersIncompleteAndPersistedVisits) {
// Helper to repeatedly call `QueryClusters()`, with the continuation time
// returned from the previous call, and return the visits sent to the
// clustering backend.
- base::Time continuation_end_time = base::Time::Now();
+ QueryClustersContinuationParams continuation_params = {};
+ continuation_params.continuation_time = base::Time::Now();
const auto next_query_clusters = [&]() {
- history_clusters_service_->QueryClusters(
+ const auto task = history_clusters_service_->QueryClusters(
ClusteringRequestSource::kJourneysPage,
- /*begin_time=*/base::Time(), continuation_end_time,
- base::BindLambdaForTesting([&](std::vector<history::Cluster> clusters,
- base::Time continuation_end_time_temp) {
- continuation_end_time = continuation_end_time_temp;
- }),
- &task_tracker_);
+ /*begin_time=*/base::Time(), continuation_params,
+ base::BindLambdaForTesting(
+ [&](std::vector<history::Cluster> clusters,
+ QueryClustersContinuationParams continuation_params_temp) {
+ continuation_params = continuation_params_temp;
+ }));
test_clustering_backend_->WaitForGetClustersCall();
history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
@@ -375,14 +380,15 @@ TEST_F(HistoryClustersServiceTest, EndToEndWithBackend) {
base::RunLoop run_loop;
auto run_loop_quit = run_loop.QuitClosure();
- history_clusters_service_->QueryClusters(
- ClusteringRequestSource::kJourneysPage,
- /*begin_time=*/base::Time(),
- /*end_time=*/base::Time(),
- // This "expect" block is not run until after the fake response is sent
- // further down in this method.
- base::BindLambdaForTesting(
- [&](std::vector<history::Cluster> clusters, base::Time) {
+ const auto task =
+ history_clusters_service_->QueryClusters(
+ ClusteringRequestSource::kJourneysPage,
+ /*begin_time=*/base::Time(),
+ /*continuation_params=*/{},
+ // This "expect" block is not run until after the fake response is
+ // sent further down in this method.
+ base::BindLambdaForTesting([&](std::vector<history::Cluster> clusters,
+ QueryClustersContinuationParams) {
ASSERT_EQ(clusters.size(), 2U);
auto& cluster = clusters[0];
@@ -414,9 +420,9 @@ TEST_F(HistoryClustersServiceTest, EndToEndWithBackend) {
.is_existing_part_of_tab_group);
EXPECT_FLOAT_EQ(visits[1].score, 0.5);
- ASSERT_EQ(cluster.keywords.size(), 2u);
- EXPECT_EQ(cluster.keywords[0], u"apples");
- EXPECT_EQ(cluster.keywords[1], u"Red Oranges");
+ ASSERT_EQ(cluster.keyword_to_data_map.size(), 2u);
+ EXPECT_TRUE(cluster.keyword_to_data_map.contains(u"apples"));
+ EXPECT_TRUE(cluster.keyword_to_data_map.contains(u"Red Oranges"));
cluster = clusters[1];
visits = cluster.visits;
@@ -427,11 +433,10 @@ TEST_F(HistoryClustersServiceTest, EndToEndWithBackend) {
GetHardcodedTestVisits()[1].visit_row.visit_time);
EXPECT_EQ(visits[0].annotated_visit.url_row.title(),
u"Code Storage Title");
- EXPECT_TRUE(cluster.keywords.empty());
+ EXPECT_TRUE(cluster.keyword_to_data_map.empty());
run_loop_quit.Run();
- }),
- &task_tracker_);
+ }));
AwaitAndVerifyTestClusteringBackendRequest();
@@ -442,7 +447,8 @@ TEST_F(HistoryClustersServiceTest, EndToEndWithBackend) {
test_clustering_backend_->GetVisitById(2),
test_clustering_backend_->GetVisitById(5),
},
- {u"apples", u"Red Oranges"},
+ {{u"apples", history::ClusterKeywordData()},
+ {u"Red Oranges", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
clusters.push_back(
history::Cluster(0,
@@ -615,7 +621,10 @@ TEST_F(HistoryClustersServiceTest, DoesQueryMatchAnyCluster) {
test_clustering_backend_->GetVisitById(5),
test_clustering_backend_->GetVisitById(2),
},
- {u"apples", u"oranges", u"z", u"apples bananas"},
+ {{u"apples", history::ClusterKeywordData()},
+ {u"oranges", history::ClusterKeywordData()},
+ {u"z", history::ClusterKeywordData()},
+ {u"apples bananas", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
clusters.push_back(
history::Cluster(0,
@@ -623,14 +632,14 @@ TEST_F(HistoryClustersServiceTest, DoesQueryMatchAnyCluster) {
test_clustering_backend_->GetVisitById(5),
test_clustering_backend_->GetVisitById(2),
},
- {u"sensitive"},
+ {{u"sensitive", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
clusters.push_back(
history::Cluster(0,
{
test_clustering_backend_->GetVisitById(5),
},
- {u"singlevisit"},
+ {{u"singlevisit", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
test_clustering_backend_->FulfillCallback(clusters);
@@ -735,7 +744,8 @@ TEST_F(HistoryClustersServiceTest, DoesQueryMatchAnyClusterSecondaryCache) {
test_clustering_backend_->GetVisitById(1),
test_clustering_backend_->GetVisitById(2),
},
- {u"peach", u""},
+ {{u"peach", history::ClusterKeywordData()},
+ {u"", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
test_clustering_backend_->FulfillCallback(clusters2);
history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
@@ -771,7 +781,10 @@ TEST_F(HistoryClustersServiceTest, DoesURLMatchAnyClusterWithNoisyURLs) {
test_clustering_backend_->GetVisitById(
/*visit_id=*/2, /*score=*/0.0, /*engagement_score=*/20.0),
},
- {u"apples", u"oranges", u"z", u"apples bananas"},
+ {{u"apples", history::ClusterKeywordData()},
+ {u"oranges", history::ClusterKeywordData()},
+ {u"z", history::ClusterKeywordData()},
+ {u"apples bananas", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
clusters.push_back(
history::Cluster(0,
@@ -779,14 +792,14 @@ TEST_F(HistoryClustersServiceTest, DoesURLMatchAnyClusterWithNoisyURLs) {
test_clustering_backend_->GetVisitById(5),
test_clustering_backend_->GetVisitById(2),
},
- {u"sensitive"},
+ {{u"sensitive", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
clusters.push_back(
history::Cluster(0,
{
test_clustering_backend_->GetVisitById(2),
},
- {u"singlevisit"},
+ {{u"singlevisit", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
test_clustering_backend_->FulfillCallback(clusters);
@@ -859,7 +872,10 @@ TEST_F(HistoryClustersServiceTest, DoesURLMatchAnyClusterNoNoisyURLs) {
test_clustering_backend_->GetVisitById(
/*visit_id=*/2, /*score=*/0.0, /*engagement_score=*/20.0),
},
- {u"apples", u"oranges", u"z", u"apples bananas"},
+ {{u"apples", history::ClusterKeywordData()},
+ {u"oranges", history::ClusterKeywordData()},
+ {u"z", history::ClusterKeywordData()},
+ {u"apples bananas", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
clusters.push_back(
history::Cluster(0,
@@ -867,14 +883,14 @@ TEST_F(HistoryClustersServiceTest, DoesURLMatchAnyClusterNoNoisyURLs) {
test_clustering_backend_->GetVisitById(5),
test_clustering_backend_->GetVisitById(2),
},
- {u"sensitive"},
+ {{u"sensitive", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
clusters.push_back(
history::Cluster(0,
{
test_clustering_backend_->GetVisitById(2),
},
- {u"singlevisit"},
+ {{u"singlevisit", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
test_clustering_backend_->FulfillCallback(clusters);
@@ -919,6 +935,76 @@ TEST_F(HistoryClustersServiceTest, DoesURLMatchAnyClusterNoNoisyURLs) {
ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/"))));
}
+TEST_F(HistoryClustersServiceTest, QueryVisitsOldestFirst) {
+ // Create 5 persisted visits with visit times 2, 1, 1, 60, and 1 days ago.
+ AddHardcodedTestDataToHistoryService();
+
+ // Add a sync visit on a day without other visits in order to verify a day
+ // with only sync visits doesn't interrupt `GetAnnotatedVisitsToCluster`'s
+ // intention of iterating until a visit is found.
+ history::AnnotatedVisit sync_visit;
+ sync_visit.url_row.set_id(1);
+ sync_visit.visit_row.visit_id = 10;
+ sync_visit.visit_row.visit_time = base::Time::Now() - base::Days(15);
+ sync_visit.source = history::VisitSource::SOURCE_SYNCED;
+ AddCompleteVisit(sync_visit);
+
+ // Helper to repeatedly schedule a `GetAnnotatedVisitsToCluster`, with the
+ // continuation time returned from the previous task, and return the visits
+ // it returns.
+ QueryClustersContinuationParams continuation_params = {};
+ const auto next_visits = [&]() {
+ std::vector<history::AnnotatedVisit> visits;
+ base::CancelableTaskTracker task_tracker;
+ history_service_->ScheduleDBTask(
+ FROM_HERE,
+ std::make_unique<GetAnnotatedVisitsToCluster>(
+ IncompleteVisitMap{}, base::Time(), continuation_params, false,
+ base::BindLambdaForTesting(
+ [&](std::vector<history::AnnotatedVisit> visits_temp,
+ QueryClustersContinuationParams continuation_params_temp) {
+ visits = visits_temp;
+ continuation_params = continuation_params_temp;
+ })),
+ &task_tracker);
+ history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
+ return visits;
+ };
+
+ {
+ // 1st query should return the oldest, 60-day-old visit.
+ const auto visits = next_visits();
+ ASSERT_EQ(visits.size(), 1u);
+ EXPECT_EQ(visits[0].visit_row.visit_id, 4);
+ EXPECT_TRUE(continuation_params.is_continuation);
+ EXPECT_FALSE(continuation_params.is_done);
+ }
+ {
+ // 2nd query should return the next oldest, 2-day-old visit.
+ const auto visits = next_visits();
+ ASSERT_EQ(visits.size(), 1u);
+ EXPECT_EQ(visits[0].visit_row.visit_id, 1);
+ EXPECT_TRUE(continuation_params.is_continuation);
+ EXPECT_FALSE(continuation_params.is_done);
+ }
+ {
+ // 3rd query should return the next oldest, 1-day-old visits. Visit 3 is
+ // excluded as it's from sync.
+ const auto visits = next_visits();
+ ASSERT_EQ(visits.size(), 2u);
+ EXPECT_EQ(visits[0].visit_row.visit_id, 5);
+ EXPECT_EQ(visits[1].visit_row.visit_id, 2);
+ EXPECT_TRUE(continuation_params.is_continuation);
+ EXPECT_FALSE(continuation_params.is_done);
+ }
+ {
+ // 4th query should return no visits; all visits were exhausted.
+ const auto visits = next_visits();
+ ASSERT_TRUE(visits.empty());
+ EXPECT_TRUE(continuation_params.is_done);
+ }
+}
+
class HistoryClustersServiceMaxKeywordsTest
: public HistoryClustersServiceTestBase {
public:
@@ -963,17 +1049,21 @@ TEST_F(HistoryClustersServiceMaxKeywordsTest,
test_clustering_backend_->GetVisitById(1),
test_clustering_backend_->GetVisitById(2),
},
- {u"one", u"two", u"three", u"four five six"},
+ {{u"one", history::ClusterKeywordData()},
+ {u"two", history::ClusterKeywordData()},
+ {u"three", history::ClusterKeywordData()},
+ {u"four five six", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
// 2) The 2nd cluster has only 1 visit. Since it's keywords won't be cached,
// they should not affect the max.
- clusters.push_back(
- history::Cluster(0,
- {
- test_clustering_backend_->GetVisitById(3),
- },
- {u"ignored not cached", u"elephant penguin kangaroo"},
- /*should_show_on_prominent_ui_surfaces=*/true));
+ clusters.push_back(history::Cluster(
+ 0,
+ {
+ test_clustering_backend_->GetVisitById(3),
+ },
+ {{u"ignored not cached", history::ClusterKeywordData()},
+ {u"elephant penguin kangaroo", history::ClusterKeywordData()}},
+ /*should_show_on_prominent_ui_surfaces=*/true));
// 3) With this 3rd cluster, we'll have 5 phrases and 7 words. Now that we've
// reached 5 phrases, the next cluster's keywords should not be cached.
clusters.push_back(
@@ -982,7 +1072,7 @@ TEST_F(HistoryClustersServiceMaxKeywordsTest,
test_clustering_backend_->GetVisitById(4),
test_clustering_backend_->GetVisitById(5),
},
- {u"seven"},
+ {{u"seven", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
// 4) The 4th cluster's keywords should not be cached since we've reached 5
// phrases.
@@ -992,7 +1082,7 @@ TEST_F(HistoryClustersServiceMaxKeywordsTest,
test_clustering_backend_->GetVisitById(6),
test_clustering_backend_->GetVisitById(7),
},
- {u"eight"},
+ {{u"eight", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
test_clustering_backend_->FulfillCallback(clusters);
history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
diff --git a/chromium/components/history_clusters/core/history_clusters_types.h b/chromium/components/history_clusters/core/history_clusters_types.h
index e164bee30c7..54699ca250f 100644
--- a/chromium/components/history_clusters/core/history_clusters_types.h
+++ b/chromium/components/history_clusters/core/history_clusters_types.h
@@ -14,19 +14,55 @@
namespace history_clusters {
-// If `continuation_end_time` is base::Time(), then we've exhausted History.
-// This matches the same semantics as returned directly from History.
+struct QueryClustersContinuationParams {
+ QueryClustersContinuationParams() = default;
+ QueryClustersContinuationParams(base::Time continuation_time,
+ bool is_continuation,
+ bool is_partial_day,
+ bool exhausted_history,
+ bool is_done)
+ : continuation_time(continuation_time),
+ is_continuation(is_continuation),
+ is_partial_day(is_partial_day),
+ exhausted_history(exhausted_history),
+ is_done(is_done) {}
+
+ // Returns a `QueryClustersContinuationParams` representing the done state.
+ // Most of the values don't matter, but `exhausted_history` and `is_done`
+ // should be true.
+ static const QueryClustersContinuationParams DoneParams() {
+ static QueryClustersContinuationParams kDoneParams = {base::Time(), true,
+ false, true, true};
+ return kDoneParams;
+ }
+
+ // The time already fetched visits up to and where the next request will
+ // continue.
+ base::Time continuation_time = base::Time();
+ // False for the first request, true otherwise.
+ bool is_continuation = false;
+ // True if left off midday; i.e. not a day boundary. This occurs when the max
+ // visit threshold was reached.
+ bool is_partial_day = false;
+ // True if unclustered visits were exhausted. If we're searching oldest to
+ // newest, this is true iff `is_done` is true. Otherwise, this may be true
+ // before `is_done` is true but not the reverse.
+ bool exhausted_history = false;
+ // True if history was exhausted.
+ bool is_done = false;
+};
+
using QueryClustersCallback =
- base::OnceCallback<void(std::vector<history::Cluster> clusters,
- base::Time continuation_end_time)>;
+ base::OnceCallback<void(std::vector<history::Cluster>,
+ QueryClustersContinuationParams)>;
// Tracks which fields have been or are pending recording. This helps 1) avoid
-// re-recording fields and 2) determine whether a visit is compete (i.e. has all
-// expected fields recorded).
+// re-recording fields and 2) determine whether a visit is complete (i.e. has
+// all expected fields recorded).
struct RecordingStatus {
// Whether `url_row` and `visit_row` have been set.
bool history_rows = false;
- // Whether a navigation has ended; i.e. another navigation has began in the
+ // Whether a navigation has ended; i.e. another navigation has begun in the
// same tab or the navigation's tab has been closed.
bool navigation_ended = false;
// Whether the `context_annotations` associated with navigation end have been
@@ -50,6 +86,9 @@ struct IncompleteVisitContextAnnotations {
history::VisitContextAnnotations context_annotations;
};
+// Used to track incomplete, unpersisted visits.
+using IncompleteVisitMap = std::map<int64_t, IncompleteVisitContextAnnotations>;
+
} // namespace history_clusters
#endif // COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_TYPES_H_
diff --git a/chromium/components/history_clusters/core/history_clusters_util.cc b/chromium/components/history_clusters/core/history_clusters_util.cc
index 544306fd828..f92e38dd46c 100644
--- a/chromium/components/history_clusters/core/history_clusters_util.cc
+++ b/chromium/components/history_clusters/core/history_clusters_util.cc
@@ -6,6 +6,7 @@
#include <algorithm>
+#include "base/containers/contains.h"
#include "base/i18n/case_conversion.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_piece.h"
@@ -16,6 +17,7 @@
#include "components/history_clusters/core/config.h"
#include "components/history_clusters/core/features.h"
#include "components/query_parser/query_parser.h"
+#include "components/query_parser/snippet.h"
#include "components/url_formatter/url_formatter.h"
namespace history_clusters {
@@ -40,34 +42,51 @@ bool DoesQueryMatchClusterKeywords(
// Flags any elements within `cluster_visits` that match `find_nodes`. The
// matching is deliberately meant to closely mirror the History implementation.
// Returns the total score of matching visits, and returns 0 if no visits match.
-float ComputeTotalMatchScore(
- const query_parser::QueryNodeVector& find_nodes,
- std::vector<history::ClusterVisit>* cluster_visits) {
- DCHECK(cluster_visits);
-
+float MarkMatchesAndGetScore(const query_parser::QueryNodeVector& find_nodes,
+ history::Cluster* cluster) {
+ DCHECK(cluster);
float total_matching_visit_score = 0.0;
- for (auto& visit : *cluster_visits) {
- query_parser::QueryWordVector find_in_words;
- GURL gurl = visit.annotated_visit.url_row.url();
-
- std::u16string url_lower =
- base::i18n::ToLower(base::UTF8ToUTF16(gurl.possibly_invalid_spec()));
- query_parser::QueryParser::ExtractQueryWords(url_lower, &find_in_words);
+ if (cluster->label &&
+ query_parser::QueryParser::DoesQueryMatch(
+ *(cluster->label), find_nodes, &(cluster->label_match_positions))) {
+ total_matching_visit_score += 1.0;
+ }
- if (gurl.is_valid()) {
- // Decode punycode to match IDN.
- std::u16string ascii = base::ASCIIToUTF16(gurl.host());
- std::u16string utf = url_formatter::IDNToUnicode(gurl.host());
- if (ascii != utf)
- query_parser::QueryParser::ExtractQueryWords(utf, &find_in_words);
- }
+ for (auto& visit : cluster->visits) {
+ bool match_found = false;
- std::u16string title_lower =
+ // Search through the visible elements and highlight match positions.
+ GURL gurl = visit.annotated_visit.url_row.url();
+ auto url_for_display_lower = base::i18n::ToLower(visit.url_for_display);
+ match_found |= query_parser::QueryParser::DoesQueryMatch(
+ url_for_display_lower, find_nodes,
+ &visit.url_for_display_match_positions);
+ auto title_lower =
base::i18n::ToLower(visit.annotated_visit.url_row.title());
- query_parser::QueryParser::ExtractQueryWords(title_lower, &find_in_words);
+ match_found |= query_parser::QueryParser::DoesQueryMatch(
+ title_lower, find_nodes, &visit.title_match_positions);
+
+ // If we couldn't find it in the visible elements, try a second search
+ // where we put all the text into one bag and try again. We need to do this
+ // to be as exhaustive as History.
+ // TODO(tommycli): Downgrade the score of these matches, or investigate if
+ // we can discard this second pass. The consequence of discarding the second
+ // pass is to omit matches that span both the URL and title.
+ if (!match_found) {
+ query_parser::QueryWordVector find_in_words;
+ std::u16string url_lower =
+ base::i18n::ToLower(base::UTF8ToUTF16(gurl.possibly_invalid_spec()));
+ query_parser::QueryParser::ExtractQueryWords(url_lower, &find_in_words);
+ query_parser::QueryParser::ExtractQueryWords(url_for_display_lower,
+ &find_in_words);
+ query_parser::QueryParser::ExtractQueryWords(title_lower, &find_in_words);
+
+ match_found |=
+ query_parser::QueryParser::DoesQueryMatch(find_in_words, find_nodes);
+ }
- if (query_parser::QueryParser::DoesQueryMatch(find_in_words, find_nodes)) {
+ if (match_found) {
visit.matches_search_query = true;
DCHECK_GE(visit.score, 0);
total_matching_visit_score += visit.score;
@@ -83,9 +102,8 @@ float ComputeTotalMatchScore(
//
// Note, this should NOT be called for `cluster_visits` with NO matching visits.
void PromoteMatchingVisitsAboveNonMatchingVisits(
- std::vector<history::ClusterVisit>* cluster_visits) {
- DCHECK(cluster_visits);
- for (auto& visit : *cluster_visits) {
+ std::vector<history::ClusterVisit>& cluster_visits) {
+ for (auto& visit : cluster_visits) {
if (visit.matches_search_query) {
// Smash all matching scores into the range that's above the fold.
visit.score =
@@ -106,7 +124,7 @@ void PromoteMatchingVisitsAboveNonMatchingVisits(
GURL ComputeURLForDeduping(const GURL& url) {
// Below is a simplified version of `AutocompleteMatch::GURLToStrippedGURL`
- // that is thread-safe, stateless, never hits the disk, a bit more aggressive,
+ // that is thread-safe, stateless, never hits the disk, a lot more aggressive,
// and without a dependency on omnibox components.
if (!url.is_valid())
return url;
@@ -124,6 +142,12 @@ GURL ComputeURLForDeduping(const GURL& url) {
if (url_for_deduping.SchemeIs(url::kHttpScheme))
replacements.SetSchemeStr(url::kHttpsScheme);
+ // It's unusual to clear the query for deduping, because it's normally
+ // considered a meaningful part of the URL. However, the return value of this
+ // function isn't used naively. Details at `ClusterVisit::url_for_deduping`.
+ if (url.has_query())
+ replacements.ClearQuery();
+
if (url.has_ref())
replacements.ClearRef();
@@ -136,9 +160,23 @@ std::string ComputeURLKeywordForLookup(const GURL& url) {
ComputeURLForDeduping(url));
}
-void StableSortVisits(std::vector<history::ClusterVisit>* visits) {
- DCHECK(visits);
- base::ranges::stable_sort(*visits, [](auto& v1, auto& v2) {
+std::u16string ComputeURLForDisplay(const GURL& url, bool trim_after_host) {
+ // Use URL formatting options similar to the omnibox popup. The url_formatter
+ // component does IDN hostname conversion as well.
+ url_formatter::FormatUrlTypes format_types =
+ url_formatter::kFormatUrlOmitDefaults |
+ url_formatter::kFormatUrlOmitHTTPS |
+ url_formatter::kFormatUrlOmitTrivialSubdomains;
+
+ if (trim_after_host)
+ format_types |= url_formatter::kFormatUrlTrimAfterHost;
+
+ return url_formatter::FormatUrl(url, format_types, base::UnescapeRule::SPACES,
+ nullptr, nullptr, nullptr);
+}
+
+void StableSortVisits(std::vector<history::ClusterVisit>& visits) {
+ base::ranges::stable_sort(visits, [](auto& v1, auto& v2) {
if (v1.score != v2.score) {
// Use v1 > v2 to get higher scored visits BEFORE lower scored visits.
return v1.score > v2.score;
@@ -151,8 +189,7 @@ void StableSortVisits(std::vector<history::ClusterVisit>* visits) {
}
void ApplySearchQuery(const std::string& query,
- std::vector<history::Cluster>* clusters) {
- DCHECK(clusters);
+ std::vector<history::Cluster>& clusters) {
if (query.empty())
return;
@@ -165,19 +202,20 @@ void ApplySearchQuery(const std::string& query,
// Move all the passed in `clusters` into `all_clusters`, and start rebuilding
// `clusters` to only contain the matching ones.
std::vector<history::Cluster> all_clusters;
- std::swap(all_clusters, *clusters);
+ std::swap(all_clusters, clusters);
for (auto& cluster : all_clusters) {
const float total_matching_visit_score =
- ComputeTotalMatchScore(find_nodes, &cluster.visits);
+ MarkMatchesAndGetScore(find_nodes, &cluster);
DCHECK_GE(total_matching_visit_score, 0);
if (total_matching_visit_score > 0 &&
GetConfig().rescore_visits_within_clusters_for_query) {
- PromoteMatchingVisitsAboveNonMatchingVisits(&cluster.visits);
+ PromoteMatchingVisitsAboveNonMatchingVisits(cluster.visits);
}
cluster.search_match_score = total_matching_visit_score;
- if (DoesQueryMatchClusterKeywords(find_nodes, cluster.keywords)) {
+
+ if (DoesQueryMatchClusterKeywords(find_nodes, cluster.GetKeywords())) {
// Arbitrarily chosen that cluster keyword matches are worth three points.
// TODO(crbug.com/1307071): Use relevancy score for each cluster keyword
// once support for that is added to the backend.
@@ -186,12 +224,12 @@ void ApplySearchQuery(const std::string& query,
if (cluster.search_match_score > 0) {
// Move the matching clusters into the final list.
- clusters->push_back(std::move(cluster));
+ clusters.push_back(std::move(cluster));
}
}
if (GetConfig().sort_clusters_within_batch_for_query) {
- base::ranges::stable_sort(*clusters, [](auto& c1, auto& c2) {
+ base::ranges::stable_sort(clusters, [](auto& c1, auto& c2) {
// Use c1 > c2 to get higher scored clusters BEFORE lower scored clusters.
return c1.search_match_score > c2.search_match_score;
});
@@ -200,37 +238,81 @@ void ApplySearchQuery(const std::string& query,
void CullNonProminentOrDuplicateClusters(
std::string query,
- std::vector<history::Cluster>* clusters,
+ std::vector<history::Cluster>& clusters,
std::set<GURL>* seen_single_visit_cluster_urls) {
- DCHECK(clusters);
DCHECK(seen_single_visit_cluster_urls);
if (query.empty()) {
// For the empty-query state, only show clusters with
// `should_show_on_prominent_ui_surfaces` set to true. This restriction is
// NOT applied when the user is searching for a specific keyword.
- clusters->erase(base::ranges::remove_if(
- *clusters,
- [](const history::Cluster& cluster) {
- return !cluster.should_show_on_prominent_ui_surfaces;
- }),
- clusters->end());
+ clusters.erase(base::ranges::remove_if(
+ clusters,
+ [](const history::Cluster& cluster) {
+ return !cluster.should_show_on_prominent_ui_surfaces;
+ }),
+ clusters.end());
} else {
- clusters->erase(base::ranges::remove_if(
- *clusters,
- [&](const history::Cluster& cluster) {
- // Erase all duplicate single-visit non-prominent
- // clusters.
- if (!cluster.should_show_on_prominent_ui_surfaces &&
- cluster.visits.size() == 1) {
- auto [unused_iterator, newly_inserted] =
- seen_single_visit_cluster_urls->insert(
- cluster.visits[0].url_for_deduping);
- return !newly_inserted;
- }
-
- return false;
- }),
- clusters->end());
+ clusters.erase(base::ranges::remove_if(
+ clusters,
+ [&](const history::Cluster& cluster) {
+ // Erase all duplicate single-visit non-prominent
+ // clusters.
+ if (!cluster.should_show_on_prominent_ui_surfaces &&
+ cluster.visits.size() == 1) {
+ auto [unused_iterator, newly_inserted] =
+ seen_single_visit_cluster_urls->insert(
+ cluster.visits[0].url_for_deduping);
+ return !newly_inserted;
+ }
+
+ return false;
+ }),
+ clusters.end());
+ }
+}
+
+void HideAndCullLowScoringVisits(std::vector<history::Cluster>& clusters) {
+ for (auto& cluster : clusters) {
+ for (size_t i = 0; i < cluster.visits.size(); ++i) {
+ auto& visit = cluster.visits[i];
+ // Even a 0.0 visit shouldn't be hidden if this is the first visit we
+ // encounter. The assumption is that the visits are always ranked by score
+ // in a descending order.
+ // TODO(crbug.com/1313631): Simplify this after removing "Show More" UI.
+ if ((visit.score == 0.0 && i != 0) ||
+ (visit.score < GetConfig().min_score_to_always_show_above_the_fold &&
+ i >= GetConfig().num_visits_to_always_show_above_the_fold)) {
+ visit.hidden = true;
+ }
+ }
+
+ if (GetConfig().drop_hidden_visits) {
+ cluster.visits.erase(
+ base::ranges::remove_if(
+ cluster.visits, [](const auto& visit) { return visit.hidden; }),
+ cluster.visits.end());
+ }
+ }
+}
+
+void CoalesceRelatedSearches(std::vector<history::Cluster>& clusters) {
+ constexpr size_t kMaxRelatedSearches = 5;
+
+ for (auto& cluster : clusters) {
+ for (const auto& visit : cluster.visits) {
+ // Coalesce the unique related searches of this visit into the cluster
+ // until the cap is reached.
+ for (const auto& search_query :
+ visit.annotated_visit.content_annotations.related_searches) {
+ if (cluster.related_searches.size() >= kMaxRelatedSearches) {
+ return;
+ }
+
+ if (!base::Contains(cluster.related_searches, search_query)) {
+ cluster.related_searches.push_back(search_query);
+ }
+ }
+ }
}
}
diff --git a/chromium/components/history_clusters/core/history_clusters_util.h b/chromium/components/history_clusters/core/history_clusters_util.h
index 9c41734be2d..88ec4487bcd 100644
--- a/chromium/components/history_clusters/core/history_clusters_util.h
+++ b/chromium/components/history_clusters/core/history_clusters_util.h
@@ -30,15 +30,21 @@ GURL ComputeURLForDeduping(const GURL& url);
// should be separately canonicalized by TemplateURLService and not sent here.
std::string ComputeURLKeywordForLookup(const GURL& url);
+// Returns a string suitable for display in the Journeys UI from the normalized
+// visit URL. Displays the host and the path. Set `trim_after_host` to true to
+// also remove the path, query, and ref.
+std::u16string ComputeURLForDisplay(const GURL& normalized_url,
+ bool trim_after_host = false);
+
// Stable sorts visits according to score, then reverse-chronologically.
-void StableSortVisits(std::vector<history::ClusterVisit>* visits);
+void StableSortVisits(std::vector<history::ClusterVisit>& visits);
// Erases all clusters that don't match `query`. Also may re-score the visits
// within matching clusters.
//
// If `query` is an empty string, leaves `clusters` unmodified.
void ApplySearchQuery(const std::string& query,
- std::vector<history::Cluster>* clusters);
+ std::vector<history::Cluster>& clusters);
// If `query` is empty, erases all non-prominent clusters.
//
@@ -48,9 +54,16 @@ void ApplySearchQuery(const std::string& query,
// `seen_single_visit_cluster_urls` and this function updates that set.
void CullNonProminentOrDuplicateClusters(
std::string query,
- std::vector<history::Cluster>* clusters,
+ std::vector<history::Cluster>& clusters,
std::set<GURL>* seen_single_visit_cluster_urls);
+// Marks low scoring visits as hidden, and drops them if necessary.
+void HideAndCullLowScoringVisits(std::vector<history::Cluster>& clusters);
+
+// Coalesces the related searches off of individual visits and places them at
+// the cluster level with numerical limits defined by flags.
+void CoalesceRelatedSearches(std::vector<history::Cluster>& clusters);
+
} // namespace history_clusters
#endif // COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_UTIL_H_
diff --git a/chromium/components/history_clusters/core/history_clusters_util_unittest.cc b/chromium/components/history_clusters/core/history_clusters_util_unittest.cc
index 5ea8c828b63..5f7338298f6 100644
--- a/chromium/components/history_clusters/core/history_clusters_util_unittest.cc
+++ b/chromium/components/history_clusters/core/history_clusters_util_unittest.cc
@@ -22,14 +22,14 @@ TEST(HistoryClustersUtilTest, ComputeURLForDeduping) {
<< "Normalizes scheme to https.";
EXPECT_EQ(
ComputeURLForDeduping(GURL("https://google.com/path?foo=bar#reftag")),
- "https://google.com/path?foo=bar")
- << "Strips ref, leaves path and query.";
+ "https://google.com/path")
+ << "Strips ref and query, leaves path.";
EXPECT_EQ(
ComputeURLForDeduping(GURL("http://www.google.com/path?foo=bar#reftag")),
- "https://google.com/path?foo=bar")
+ "https://google.com/path")
<< "Does all of the above at once.";
- EXPECT_EQ(ComputeURLForDeduping(GURL("https://google.com/path?foo=bar")),
- "https://google.com/path?foo=bar")
+ EXPECT_EQ(ComputeURLForDeduping(GURL("https://google.com/path")),
+ "https://google.com/path")
<< "Sanity check when no replacements needed.";
}
@@ -61,15 +61,18 @@ TEST(HistoryClustersUtilTest, FilterClustersMatchingQuery) {
GetHardcodedClusterVisit(2),
GetHardcodedClusterVisit(1),
},
- {u"apples", u"Red Oranges"},
- /*should_show_on_prominent_ui_surfaces=*/false));
+ {{u"apples", history::ClusterKeywordData()},
+ {u"Red Oranges", history::ClusterKeywordData()}},
+ /*should_show_on_prominent_ui_surfaces=*/false,
+ /*label=*/u"LabelOne"));
all_clusters.push_back(
history::Cluster(2,
{
GetHardcodedClusterVisit(2),
},
{},
- /*should_show_on_prominent_ui_surfaces=*/true));
+ /*should_show_on_prominent_ui_surfaces=*/true,
+ /*label=*/u"LabelTwo"));
struct TestData {
std::string query;
@@ -105,6 +108,8 @@ TEST(HistoryClustersUtilTest, FilterClustersMatchingQuery) {
// Verify that we DON'T match if the query spans both the visit and
// keywords.
{"goog red", false, false},
+ // Verify that we can find clusters by label.
+ {"labeltwo", false, true},
};
for (size_t i = 0; i < std::size(test_data); ++i) {
@@ -113,7 +118,7 @@ TEST(HistoryClustersUtilTest, FilterClustersMatchingQuery) {
test_data[i].query.c_str()));
auto clusters = all_clusters;
- ApplySearchQuery(test_data[i].query, &clusters);
+ ApplySearchQuery(test_data[i].query, clusters);
size_t expected_size =
static_cast<size_t>(test_data[i].expect_first_cluster) +
@@ -140,13 +145,14 @@ TEST(HistoryClustersUtilTest, PromoteMatchingVisitsAboveNonMatchingVisits) {
GetHardcodedClusterVisit(1),
GetHardcodedClusterVisit(2),
},
- {u"apples", u"Red Oranges"},
+ {{u"apples", history::ClusterKeywordData()},
+ {u"Red Oranges", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
// No promotion when we match a keyword.
{
std::vector clusters = all_clusters;
- ApplySearchQuery("apples", &clusters);
+ ApplySearchQuery("apples", clusters);
ASSERT_EQ(clusters.size(), 1U);
ASSERT_EQ(clusters[0].visits.size(), 2U);
EXPECT_EQ(clusters[0].visits[0].annotated_visit.visit_row.visit_id, 1);
@@ -158,7 +164,7 @@ TEST(HistoryClustersUtilTest, PromoteMatchingVisitsAboveNonMatchingVisits) {
// Promote the second visit over the first if we match the second visit.
{
std::vector clusters = all_clusters;
- ApplySearchQuery("git", &clusters);
+ ApplySearchQuery("git", clusters);
ASSERT_EQ(clusters.size(), 1U);
ASSERT_EQ(clusters[0].visits.size(), 2U);
EXPECT_EQ(clusters[0].visits[0].annotated_visit.visit_row.visit_id, 2);
@@ -176,14 +182,15 @@ TEST(HistoryClustersUtilTest, SortClustersWithinBatchForQuery) {
GetHardcodedClusterVisit(1),
GetHardcodedClusterVisit(2),
},
- {u"apples", u"Red Oranges"},
+ {{u"apples", history::ClusterKeywordData()},
+ {u"Red Oranges", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
all_clusters.push_back(
history::Cluster(2,
{
GetHardcodedClusterVisit(1),
},
- {u"search"},
+ {{u"search", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
// When the flag is off, leave the initial ordering alone.
@@ -193,7 +200,7 @@ TEST(HistoryClustersUtilTest, SortClustersWithinBatchForQuery) {
SetConfigForTesting(config);
std::vector clusters = all_clusters;
- ApplySearchQuery("search", &clusters);
+ ApplySearchQuery("search", clusters);
ASSERT_EQ(clusters.size(), 2U);
EXPECT_EQ(clusters[0].cluster_id, 1);
EXPECT_EQ(clusters[1].cluster_id, 2);
@@ -209,7 +216,7 @@ TEST(HistoryClustersUtilTest, SortClustersWithinBatchForQuery) {
SetConfigForTesting(config);
std::vector clusters = all_clusters;
- ApplySearchQuery("search", &clusters);
+ ApplySearchQuery("search", clusters);
ASSERT_EQ(clusters.size(), 2U);
EXPECT_EQ(clusters[0].cluster_id, 2);
EXPECT_EQ(clusters[1].cluster_id, 1);
@@ -224,7 +231,7 @@ TEST(HistoryClustersUtilTest, SortClustersWithinBatchForQuery) {
SetConfigForTesting(config);
std::vector clusters = all_clusters;
- ApplySearchQuery("google", &clusters);
+ ApplySearchQuery("google", clusters);
ASSERT_EQ(clusters.size(), 2U);
EXPECT_EQ(clusters[0].cluster_id, 1);
EXPECT_EQ(clusters[1].cluster_id, 2);
@@ -233,5 +240,125 @@ TEST(HistoryClustersUtilTest, SortClustersWithinBatchForQuery) {
}
}
+TEST(HistoryClustersUtilTest, HideAndCullLowScoringVisits) {
+ std::vector<history::Cluster> all_clusters;
+
+ // High scoring visits should always be above the fold.
+ history::Cluster cluster1;
+ cluster1.cluster_id = 4;
+ cluster1.visits.push_back(GetHardcodedClusterVisit(1, 1));
+ cluster1.visits.push_back(GetHardcodedClusterVisit(1, .8));
+ cluster1.visits.push_back(GetHardcodedClusterVisit(1, .5));
+ cluster1.visits.push_back(GetHardcodedClusterVisit(1, .5));
+ cluster1.visits.push_back(GetHardcodedClusterVisit(1, .5));
+ cluster1.keyword_to_data_map = {{u"keyword", history::ClusterKeywordData()}};
+
+ // Low scoring visits should be above the fold only if they're one of top 4.
+ history::Cluster cluster2;
+ cluster2.cluster_id = 6;
+ cluster2.visits.push_back(GetHardcodedClusterVisit(1, .4));
+ cluster2.visits.push_back(GetHardcodedClusterVisit(1, .4));
+ cluster2.visits.push_back(GetHardcodedClusterVisit(1, .4));
+ cluster2.visits.push_back(GetHardcodedClusterVisit(1, .4));
+ cluster2.visits.push_back(GetHardcodedClusterVisit(1, .4));
+ cluster2.keyword_to_data_map = {{u"keyword", history::ClusterKeywordData()}};
+
+ // 0 scoring visits should be above the fold only if they're 1st.
+ history::Cluster cluster3;
+ cluster3.cluster_id = 8;
+ cluster3.visits.push_back(GetHardcodedClusterVisit(1, 0.0));
+ cluster3.visits.push_back(GetHardcodedClusterVisit(1, 0.0));
+ cluster3.keyword_to_data_map = {{u"keyword", history::ClusterKeywordData()}};
+
+ all_clusters.push_back(cluster1);
+ all_clusters.push_back(cluster2);
+ all_clusters.push_back(cluster3);
+
+ {
+ Config config;
+ config.drop_hidden_visits = true;
+ SetConfigForTesting(config);
+
+ auto clusters = all_clusters;
+ HideAndCullLowScoringVisits(clusters);
+
+ EXPECT_EQ(clusters[0].cluster_id, 4);
+ auto& visits = clusters[0].visits;
+ ASSERT_EQ(visits.size(), 5u);
+ EXPECT_EQ(visits[0].hidden, false);
+ EXPECT_EQ(visits[1].hidden, false);
+ EXPECT_EQ(visits[2].hidden, false);
+ EXPECT_EQ(visits[3].hidden, false);
+ EXPECT_EQ(visits[4].hidden, false);
+
+ EXPECT_EQ(clusters[1].cluster_id, 6);
+ visits = clusters[1].visits;
+ ASSERT_EQ(visits.size(), 4u);
+ EXPECT_EQ(visits[0].hidden, false);
+ EXPECT_EQ(visits[1].hidden, false);
+ EXPECT_EQ(visits[2].hidden, false);
+ EXPECT_EQ(visits[3].hidden, false);
+
+ EXPECT_EQ(clusters[2].cluster_id, 8);
+ ASSERT_EQ(clusters[2].visits.size(), 1u);
+ EXPECT_EQ(clusters[2].visits[0].hidden, false);
+ }
+
+ {
+ Config config;
+ config.drop_hidden_visits = false;
+ SetConfigForTesting(config);
+
+ auto clusters = all_clusters;
+ HideAndCullLowScoringVisits(clusters);
+
+ EXPECT_EQ(clusters[0].cluster_id, 4);
+ EXPECT_EQ(clusters[0].visits.size(), 5u);
+
+ EXPECT_EQ(clusters[1].cluster_id, 6);
+ const auto& visits = clusters[1].visits;
+ ASSERT_EQ(visits.size(), 5u);
+ EXPECT_EQ(visits[0].hidden, false);
+ EXPECT_EQ(visits[1].hidden, false);
+ EXPECT_EQ(visits[2].hidden, false);
+ EXPECT_EQ(visits[3].hidden, false);
+ EXPECT_EQ(visits[4].hidden, true);
+
+ EXPECT_EQ(clusters[2].cluster_id, 8);
+ ASSERT_EQ(clusters[2].visits.size(), 2u);
+ EXPECT_EQ(clusters[2].visits[0].hidden, false);
+ EXPECT_EQ(clusters[2].visits[1].hidden, true);
+ }
+}
+
+TEST(HistoryClustersUtilTest, CoalesceRelatedSearches) {
+ // canonical_visit has the same URL as Visit1.
+ history::ClusterVisit visit1 = GetHardcodedClusterVisit(1);
+ visit1.annotated_visit.content_annotations.related_searches.push_back(
+ "search1");
+ visit1.annotated_visit.content_annotations.related_searches.push_back(
+ "search2");
+ visit1.annotated_visit.content_annotations.related_searches.push_back(
+ "search3");
+
+ history::ClusterVisit visit2 = GetHardcodedClusterVisit(2);
+ visit2.annotated_visit.content_annotations.related_searches.push_back(
+ "search4");
+ visit2.annotated_visit.content_annotations.related_searches.push_back(
+ "search5");
+ visit2.annotated_visit.content_annotations.related_searches.push_back(
+ "search6");
+
+ history::Cluster cluster;
+ cluster.visits = {visit1, visit2};
+ std::vector<history::Cluster> clusters;
+ clusters.push_back(cluster);
+
+ CoalesceRelatedSearches(clusters);
+ EXPECT_THAT(clusters[0].related_searches,
+ testing::ElementsAre("search1", "search2", "search3", "search4",
+ "search5"));
+}
+
} // namespace
} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/keyword_cluster_finalizer.cc b/chromium/components/history_clusters/core/keyword_cluster_finalizer.cc
index 2dbcca8cd40..a3aba01bfcd 100644
--- a/chromium/components/history_clusters/core/keyword_cluster_finalizer.cc
+++ b/chromium/components/history_clusters/core/keyword_cluster_finalizer.cc
@@ -4,41 +4,252 @@
#include "components/history_clusters/core/keyword_cluster_finalizer.h"
+#include <queue>
+
+#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/history/core/browser/history_types.h"
#include "components/history_clusters/core/config.h"
#include "components/history_clusters/core/on_device_clustering_features.h"
#include "components/history_clusters/core/on_device_clustering_util.h"
+#include "components/optimization_guide/core/entity_metadata.h"
namespace history_clusters {
-KeywordClusterFinalizer::KeywordClusterFinalizer() = default;
+namespace {
+
+static constexpr float kSearchTermsScore = 100.0;
+static constexpr float kScoreEpsilon = 1e-8;
+
+bool IsKeywordSimilarToVisitHost(
+ const std::vector<std::u16string>& lowercase_host_parts,
+ const std::u16string& keyword) {
+ std::u16string lowercase_keyword = base::ToLowerASCII(keyword);
+ if (base::Contains(lowercase_host_parts, keyword))
+ return true;
+
+ // Now check if the whitespace-stripped keyword is part of the host.
+ std::u16string stripped_lowercase_keyword;
+ base::RemoveChars(lowercase_keyword, base::kWhitespaceASCIIAs16,
+ &stripped_lowercase_keyword);
+ return base::Contains(lowercase_host_parts, stripped_lowercase_keyword);
+}
+
+// Computes an keyword score per cluster visit by mulitplying its visit-wise
+// weight and cluster visit score, and applying a weight factor.
+float ComputeKeywordScorePerClusterVisit(int visit_keyword_weight,
+ float cluster_visit_score,
+ float weight = 1.0) {
+ return static_cast<float>(visit_keyword_weight) * cluster_visit_score *
+ weight;
+}
+
+void KeepTopKeywords(
+ base::flat_map<std::u16string, history::ClusterKeywordData>&
+ keyword_to_data_map,
+ size_t max_num_keywords_per_cluster) {
+ if (keyword_to_data_map.size() <= max_num_keywords_per_cluster) {
+ return;
+ }
+
+ // Compare keywords first by their scores and then by types.
+ auto cmp_keywords =
+ [](const std::pair<std::u16string, const history::ClusterKeywordData*>&
+ left,
+ const std::pair<std::u16string, const history::ClusterKeywordData*>&
+ right) {
+ return std::fabs(left.second->score - right.second->type) >
+ kScoreEpsilon
+ ? left.second->score > right.second->score
+ : left.second->type > right.second->type;
+ };
+
+ // Minimum priority queue of top keywords.
+ std::priority_queue<
+ std::pair<std::u16string, const history::ClusterKeywordData*>,
+ std::vector<
+ std::pair<std::u16string, const history::ClusterKeywordData*>>,
+ decltype(cmp_keywords)>
+ pq(cmp_keywords);
+ for (const auto& keyword_data_p : keyword_to_data_map) {
+ bool should_insert = false;
+ if (pq.size() < max_num_keywords_per_cluster) {
+ should_insert = true;
+ } else {
+ if (pq.top().second->score < keyword_data_p.second.score) {
+ pq.pop();
+ should_insert = true;
+ }
+ }
+ if (should_insert) {
+ pq.push(std::make_pair(keyword_data_p.first, &keyword_data_p.second));
+ }
+ }
+
+ base::flat_set<std::u16string> keywords_set;
+ while (!pq.empty()) {
+ keywords_set.insert(pq.top().first);
+ pq.pop();
+ }
+
+ auto it = keyword_to_data_map.begin();
+ for (; it != keyword_to_data_map.end();) {
+ if (!keywords_set.contains(it->first)) {
+ it = keyword_to_data_map.erase(it);
+ } else {
+ it++;
+ }
+ }
+ DCHECK_EQ(keyword_to_data_map.size(), max_num_keywords_per_cluster);
+}
+
+} // namespace
+
+KeywordClusterFinalizer::KeywordClusterFinalizer(
+ const base::flat_map<std::string, optimization_guide::EntityMetadata>&
+ entity_metadata_map)
+ : entity_metadata_map_(entity_metadata_map) {}
KeywordClusterFinalizer::~KeywordClusterFinalizer() = default;
void KeywordClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
- base::flat_set<std::u16string> keywords_set;
+ base::flat_map<std::u16string, history::ClusterKeywordData>
+ keyword_to_data_map;
for (const auto& visit : cluster.visits) {
- if (GetConfig().should_exclude_keywords_from_noisy_visits &&
- IsNoisyVisit(visit)) {
+ if (!GetConfig().keyword_filter_on_noisy_visits && IsNoisyVisit(visit)) {
// Do not put keywords if user visits the page a lot and it's not a
// search-like visit.
continue;
}
+ std::vector<std::u16string> lowercase_host_parts = base::SplitString(
+ base::ToLowerASCII(
+ base::UTF8ToUTF16(visit.annotated_visit.url_row.url().host())),
+ u".", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const auto& entity :
visit.annotated_visit.content_annotations.model_annotations.entities) {
- keywords_set.insert(base::UTF8ToUTF16(entity.id));
+ base::flat_set<std::u16string> entity_keywords;
+ const std::u16string keyword_u16str = base::UTF8ToUTF16(entity.id);
+ entity_keywords.insert(keyword_u16str);
+
+ // Add an entity to keyword data.
+ const float entity_score =
+ ComputeKeywordScorePerClusterVisit(entity.weight, visit.score);
+ auto keyword_it = keyword_to_data_map.find(keyword_u16str);
+ if (keyword_it != keyword_to_data_map.end()) {
+ // Accumulate entity scores from multiple visits.
+ keyword_it->second.score += entity_score;
+ keyword_it->second.MaybeUpdateKeywordType(
+ history::ClusterKeywordData::kEntity);
+ } else {
+ keyword_to_data_map[keyword_u16str] = history::ClusterKeywordData(
+ history::ClusterKeywordData::kEntity, entity_score,
+ /*entity_collections=*/{});
+ }
+
+ // Add the top one entity collection to keyword data.
+ const auto it = entity_metadata_map_.find(entity.id);
+ if (it != entity_metadata_map_.end() && !it->second.collections.empty()) {
+ keyword_to_data_map[keyword_u16str].entity_collections = {
+ it->second.collections[0]};
+ }
+
+ if (GetConfig().keyword_filter_on_entity_aliases) {
+ if (it != entity_metadata_map_.end()) {
+ for (size_t i = 0; i < it->second.human_readable_aliases.size() &&
+ i < GetConfig().max_entity_aliases_in_keywords;
+ i++) {
+ const auto alias =
+ base::UTF8ToUTF16(it->second.human_readable_aliases[i]);
+ entity_keywords.insert(alias);
+ // Use the same score and collections of an entity for its aliases
+ // as well.
+ auto alias_it = keyword_to_data_map.find(alias);
+ if (alias_it == keyword_to_data_map.end()) {
+ keyword_to_data_map[alias] = history::ClusterKeywordData(
+ history::ClusterKeywordData::kEntityAlias, entity_score, {});
+ } else {
+ alias_it->second.score += entity_score;
+ alias_it->second.MaybeUpdateKeywordType(
+ history::ClusterKeywordData::kEntityAlias);
+ }
+ keyword_to_data_map[alias].entity_collections =
+ keyword_to_data_map[keyword_u16str].entity_collections;
+ }
+ }
+ }
+
+ if (!GetConfig().keyword_filter_on_visit_hosts) {
+ // If we do not want any keywords associated with the visit host, make
+ // sure that none of the keywords associated with the entity look like
+ // they are for the visit host.
+ bool clear_entity_keywords = false;
+ for (const auto& entity_keyword : entity_keywords) {
+ if (IsKeywordSimilarToVisitHost(lowercase_host_parts,
+ entity_keyword)) {
+ // One of the keywords is likely for the visit host, so clear out
+ // the keywords for the whole entity.
+ clear_entity_keywords = true;
+ break;
+ }
+ }
+ if (clear_entity_keywords) {
+ for (const auto& keyword : entity_keywords) {
+ keyword_to_data_map.erase(keyword);
+ }
+ }
+ }
}
- if (GetConfig().should_include_categories_in_keywords) {
+ if (GetConfig().keyword_filter_on_categories) {
for (const auto& category : visit.annotated_visit.content_annotations
.model_annotations.categories) {
- keywords_set.insert(base::UTF8ToUTF16(category.id));
+ std::u16string category_u16string = base::UTF8ToUTF16(category.id);
+ if (!GetConfig().keyword_filter_on_visit_hosts &&
+ IsKeywordSimilarToVisitHost(lowercase_host_parts,
+ category_u16string)) {
+ continue;
+ }
+ // Add a discounted keyword score for categories.
+ const float category_score = ComputeKeywordScorePerClusterVisit(
+ category.weight, visit.score,
+ GetConfig().category_keyword_score_weight);
+ auto category_it = keyword_to_data_map.find(category_u16string);
+ if (category_it == keyword_to_data_map.end()) {
+ keyword_to_data_map[category_u16string] = history::ClusterKeywordData(
+ history::ClusterKeywordData::kEntityCategory, category_score,
+ /*entity_collections=*/{});
+ } else {
+ // Accumulate category scores if there are multiple.
+ category_it->second.score += category_score;
+ category_it->second.MaybeUpdateKeywordType(
+ history::ClusterKeywordData::kEntityCategory);
+ }
+ }
+ }
+
+ if (GetConfig().keyword_filter_on_search_terms &&
+ !visit.annotated_visit.content_annotations.search_terms.empty()) {
+ const auto& search_terms =
+ visit.annotated_visit.content_annotations.search_terms;
+ auto search_it = keyword_to_data_map.find(search_terms);
+ if (search_it == keyword_to_data_map.end()) {
+ keyword_to_data_map[search_terms] = history::ClusterKeywordData(
+ history::ClusterKeywordData::kSearchTerms,
+ /*score=*/kSearchTermsScore, /*entity_collections=*/{});
+ } else {
+ search_it->second.score += kSearchTermsScore;
+ search_it->second.MaybeUpdateKeywordType(
+ history::ClusterKeywordData::kSearchTerms);
}
}
}
- cluster.keywords =
- std::vector<std::u16string>(keywords_set.begin(), keywords_set.end());
+
+ KeepTopKeywords(keyword_to_data_map,
+ GetConfig().max_num_keywords_per_cluster);
+
+ cluster.keyword_to_data_map = std::move(keyword_to_data_map);
}
} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/keyword_cluster_finalizer.h b/chromium/components/history_clusters/core/keyword_cluster_finalizer.h
index 9781abfc980..9fcd9412307 100644
--- a/chromium/components/history_clusters/core/keyword_cluster_finalizer.h
+++ b/chromium/components/history_clusters/core/keyword_cluster_finalizer.h
@@ -5,18 +5,33 @@
#ifndef COMPONENTS_HISTORY_CLUSTERS_CORE_KEYWORD_CLUSTER_FINALIZER_H_
#define COMPONENTS_HISTORY_CLUSTERS_CORE_KEYWORD_CLUSTER_FINALIZER_H_
+#include <string>
+
+#include "base/containers/flat_set.h"
#include "components/history_clusters/core/cluster_finalizer.h"
+namespace optimization_guide {
+struct EntityMetadata;
+} // namespace optimization_guide
+
namespace history_clusters {
// A cluster finalizer that determines the set of keywords for a given cluster.
class KeywordClusterFinalizer : public ClusterFinalizer {
public:
- KeywordClusterFinalizer();
+ explicit KeywordClusterFinalizer(
+ const base::flat_map<std::string, optimization_guide::EntityMetadata>&
+ entity_metadata_map);
~KeywordClusterFinalizer() override;
// ClusterFinalizer:
void FinalizeCluster(history::Cluster& cluster) override;
+
+ private:
+ // A map from human readable entity name to the metadata associated with that
+ // entity name.
+ const base::flat_map<std::string, optimization_guide::EntityMetadata>
+ entity_metadata_map_;
};
} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc b/chromium/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc
index 51779ab5f7e..94c61204717 100644
--- a/chromium/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc
+++ b/chromium/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc
@@ -9,6 +9,7 @@
#include "components/history_clusters/core/clustering_test_utils.h"
#include "components/history_clusters/core/config.h"
#include "components/history_clusters/core/on_device_clustering_features.h"
+#include "components/optimization_guide/core/entity_metadata.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -20,10 +21,21 @@ using ::testing::UnorderedElementsAre;
class KeywordClusterFinalizerTest : public ::testing::Test {
public:
void SetUp() override {
- cluster_finalizer_ = std::make_unique<KeywordClusterFinalizer>();
-
- config_.should_exclude_keywords_from_noisy_visits = true;
- config_.should_include_categories_in_keywords = false;
+ optimization_guide::EntityMetadata github_md;
+ github_md.human_readable_aliases = {"git hub", "github llc"};
+ github_md.collections = {"/collection/computer", "/collection/programming"};
+ base::flat_map<std::string, optimization_guide::EntityMetadata>
+ entity_metadata_map;
+ entity_metadata_map["github"] = github_md;
+ cluster_finalizer_ =
+ std::make_unique<KeywordClusterFinalizer>(entity_metadata_map);
+
+ config_.keyword_filter_on_noisy_visits = false;
+ config_.keyword_filter_on_categories = false;
+ config_.keyword_filter_on_entity_aliases = false;
+ config_.keyword_filter_on_search_terms = false;
+ config_.keyword_filter_on_visit_hosts =
+ false; // Drop keywords match host names.
SetConfigForTesting(config_);
}
@@ -47,6 +59,7 @@ TEST_F(KeywordClusterFinalizerTest, IncludesKeywordsBasedOnFeatureParameters) {
{"github", 1}};
visit.annotated_visit.content_annotations.model_annotations.categories = {
{"category", 1}};
+ visit.annotated_visit.content_annotations.search_terms = u"search";
history::ClusterVisit visit2 =
testing::CreateClusterVisit(testing::CreateDefaultAnnotatedVisit(
@@ -62,37 +75,51 @@ TEST_F(KeywordClusterFinalizerTest, IncludesKeywordsBasedOnFeatureParameters) {
visit3.duplicate_visits.push_back(visit);
visit3.engagement_score = 1.0;
visit3.annotated_visit.content_annotations.model_annotations.entities = {
- {"github", 1}, {"otherentity", 1}};
+ {"github", 1},
+ {"otherentity", 1},
+ {"baz", 1} /*should be filtered due to host*/};
visit3.annotated_visit.content_annotations.model_annotations.categories = {
{"category", 1}};
+ visit3.annotated_visit.content_annotations.search_terms = u"search";
history::Cluster cluster;
cluster.visits = {visit2, visit3};
FinalizeCluster(cluster);
- EXPECT_THAT(cluster.keywords,
+
+ EXPECT_THAT(cluster.GetKeywords(),
UnorderedElementsAre(u"github", u"otherentity"));
+ ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"github"));
+ EXPECT_EQ(
+ cluster.keyword_to_data_map.at(u"github"),
+ history::ClusterKeywordData(
+ history::ClusterKeywordData::kEntity, 1,
+ std::vector<std::string>{
+ "/collection/computer"} /*keep only top one entity collection*/));
+ ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"otherentity"));
+ EXPECT_EQ(
+ cluster.keyword_to_data_map.at(u"otherentity"),
+ history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 1, {}));
}
-class KeywordClusterFinalizerIncludeAllTest : public ::testing::Test {
+class KeywordClusterFinalizerIncludeAllTest
+ : public KeywordClusterFinalizerTest {
public:
void SetUp() override {
- cluster_finalizer_ = std::make_unique<KeywordClusterFinalizer>();
-
- config_.should_exclude_keywords_from_noisy_visits = false;
- config_.should_include_categories_in_keywords = true;
+ KeywordClusterFinalizerTest::SetUp();
+
+ config_.keyword_filter_on_noisy_visits = true;
+ config_.keyword_filter_on_categories = true;
+ config_.keyword_filter_on_entity_aliases = true;
+ config_.max_entity_aliases_in_keywords = 1;
+ config_.keyword_filter_on_search_terms = true;
+ config_.keyword_filter_on_visit_hosts = true;
+ config_.category_keyword_score_weight = 0.1;
+ config_.max_num_keywords_per_cluster = 7;
SetConfigForTesting(config_);
}
- void TearDown() override { cluster_finalizer_.reset(); }
-
- void FinalizeCluster(history::Cluster& cluster) {
- cluster_finalizer_->FinalizeCluster(cluster);
- }
-
private:
Config config_;
- std::unique_ptr<KeywordClusterFinalizer> cluster_finalizer_;
- base::test::TaskEnvironment task_environment_;
};
TEST_F(KeywordClusterFinalizerIncludeAllTest,
@@ -104,6 +131,7 @@ TEST_F(KeywordClusterFinalizerIncludeAllTest,
{"github", 1}};
visit.annotated_visit.content_annotations.model_annotations.categories = {
{"category", 1}};
+ visit.annotated_visit.content_annotations.search_terms = u"search";
history::ClusterVisit visit2 =
testing::CreateClusterVisit(testing::CreateDefaultAnnotatedVisit(
@@ -119,16 +147,50 @@ TEST_F(KeywordClusterFinalizerIncludeAllTest,
visit3.duplicate_visits.push_back(visit);
visit3.engagement_score = 1.0;
visit3.annotated_visit.content_annotations.model_annotations.entities = {
- {"github", 1}, {"otherentity", 1}};
+ {"github", 1}, {"otherentity", 1}, {"baz", 1}, {"search", 1}};
visit3.annotated_visit.content_annotations.model_annotations.categories = {
+ {"category2", 0}, // `category2` is dropped due to keywords capping.
{"category", 1}};
+ visit3.annotated_visit.content_annotations.search_terms =
+ u"search"; // Keyword type should be `kSearchTerms`.
history::Cluster cluster;
cluster.visits = {visit2, visit3};
FinalizeCluster(cluster);
- EXPECT_THAT(cluster.keywords,
- UnorderedElementsAre(u"github", u"category", u"onlyinnoisyvisit",
- u"otherentity"));
+
+ EXPECT_THAT(
+ cluster.GetKeywords(),
+ UnorderedElementsAre(u"github", u"git hub", u"otherentity", u"baz",
+ u"category", u"onlyinnoisyvisit", u"search"));
+ ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"github"));
+ EXPECT_EQ(cluster.keyword_to_data_map.at(u"github"),
+ history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 2,
+ {"/collection/computer"}));
+ ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"git hub"));
+ EXPECT_EQ(
+ cluster.keyword_to_data_map.at(u"git hub"),
+ history::ClusterKeywordData(history::ClusterKeywordData::kEntityAlias, 2,
+ {"/collection/computer"}));
+ ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"category"));
+ EXPECT_EQ(cluster.keyword_to_data_map.at(u"category"),
+ history::ClusterKeywordData(
+ history::ClusterKeywordData::kEntityCategory, 0.2, {}));
+ ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"onlyinnoisyvisit"));
+ EXPECT_EQ(
+ cluster.keyword_to_data_map.at(u"onlyinnoisyvisit"),
+ history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 1, {}));
+ ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"otherentity"));
+ EXPECT_EQ(
+ cluster.keyword_to_data_map.at(u"otherentity"),
+ history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 1, {}));
+ ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"search"));
+ EXPECT_EQ(cluster.keyword_to_data_map.at(u"search"),
+ history::ClusterKeywordData(
+ history::ClusterKeywordData::kSearchTerms, 101, {}));
+ ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"baz"));
+ EXPECT_EQ(
+ cluster.keyword_to_data_map.at(u"baz"),
+ history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 1, {}));
}
} // namespace
diff --git a/chromium/components/history_clusters/core/label_cluster_finalizer.cc b/chromium/components/history_clusters/core/label_cluster_finalizer.cc
index 9c5407b6ad1..403e524786b 100644
--- a/chromium/components/history_clusters/core/label_cluster_finalizer.cc
+++ b/chromium/components/history_clusters/core/label_cluster_finalizer.cc
@@ -4,10 +4,13 @@
#include "components/history_clusters/core/label_cluster_finalizer.h"
+#include <string>
+
#include "base/containers/flat_set.h"
#include "base/strings/utf_string_conversions.h"
#include "components/history/core/browser/history_types.h"
#include "components/history_clusters/core/config.h"
+#include "components/history_clusters/core/history_clusters_util.h"
#include "components/history_clusters/core/on_device_clustering_features.h"
#include "components/history_clusters/core/on_device_clustering_util.h"
#include "components/strings/grit/components_strings.h"
@@ -34,15 +37,37 @@ void LabelClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
}
}
+ // If we haven't found a label yet, use Entities, if that flag is enabled.
+ // TODO(crbug.com/1294348): Implement a configurable quality threshold, so
+ // low quality Entity labels can be ignored in favor of hostnames below.
+ if (GetConfig().labels_from_entities && !current_highest_scoring_label) {
+ base::flat_map<std::string, float> entity_to_score;
+ for (const auto& visit : cluster.visits) {
+ for (const auto& entity : visit.annotated_visit.content_annotations
+ .model_annotations.entities) {
+ auto it = entity_to_score.find(entity.id);
+ float new_score = it != entity_to_score.end()
+ ? it->second + (entity.weight * visit.score)
+ : entity.weight * visit.score;
+ if (new_score > max_label_score) {
+ max_label_score = new_score;
+ current_highest_scoring_label = base::UTF8ToUTF16(entity.id);
+ }
+ entity_to_score[entity.id] = new_score;
+ }
+ }
+ }
+
// If we haven't found a label yet, use hostnames if the flag is enabled.
if (GetConfig().labels_from_hostnames && !current_highest_scoring_label) {
- base::flat_map<std::string, float> hostname_to_score;
+ base::flat_map<std::u16string, float> hostname_to_score;
for (const auto& visit : cluster.visits) {
- std::string host = url_formatter::StripWWW(visit.normalized_url.host());
+ std::u16string host =
+ ComputeURLForDisplay(visit.normalized_url, /*trim_after_host=*/true);
float& hostname_score = hostname_to_score[host];
hostname_score += visit.score;
if (hostname_score > max_label_score) {
- current_highest_scoring_label = base::UTF8ToUTF16(host);
+ current_highest_scoring_label = host;
max_label_score = hostname_score;
}
}
@@ -56,26 +81,6 @@ void LabelClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
}
}
- // If we haven't found a label yet, use the Entity name, if that flag is
- // enabled.
- if (GetConfig().labels_from_entities && !current_highest_scoring_label) {
- base::flat_map<std::string, float> entity_to_score;
- for (const auto& visit : cluster.visits) {
- for (const auto& entity : visit.annotated_visit.content_annotations
- .model_annotations.entities) {
- auto it = entity_to_score.find(entity.id);
- float new_score = it != entity_to_score.end()
- ? it->second + (entity.weight * visit.score)
- : entity.weight * visit.score;
- if (new_score > max_label_score) {
- max_label_score = new_score;
- current_highest_scoring_label = base::UTF8ToUTF16(entity.id);
- }
- entity_to_score[entity.id] = new_score;
- }
- }
- }
-
if (current_highest_scoring_label) {
cluster.label = *current_highest_scoring_label;
}
diff --git a/chromium/components/history_clusters/core/label_cluster_finalizer_unittest.cc b/chromium/components/history_clusters/core/label_cluster_finalizer_unittest.cc
index 541f81cc432..7edc860af83 100644
--- a/chromium/components/history_clusters/core/label_cluster_finalizer_unittest.cc
+++ b/chromium/components/history_clusters/core/label_cluster_finalizer_unittest.cc
@@ -68,7 +68,9 @@ TEST_F(LabelClusterFinalizerTest, ClusterWithNoSearchTerms) {
}
{
- // With hostname labelling and entity labelling, we should use the hostname.
+ // With hostname labelling and entity labelling both enabled, we should
+ // prefer the entity because if we prefer hostnames, every cluster will have
+ // a hostname label, and no entity labels will ever get surfaced.
Config config;
config.should_label_clusters = true;
config.labels_from_hostnames = true;
@@ -78,6 +80,20 @@ TEST_F(LabelClusterFinalizerTest, ClusterWithNoSearchTerms) {
history::Cluster cluster;
cluster.visits = {visit2, visit3};
FinalizeCluster(cluster);
+ EXPECT_EQ(cluster.label, u"chosenlabel");
+ }
+
+ {
+ // With hostname labelling active only, we should use the hostname.
+ Config config;
+ config.should_label_clusters = true;
+ config.labels_from_hostnames = true;
+ config.labels_from_entities = false;
+ SetConfigForTesting(config);
+
+ history::Cluster cluster;
+ cluster.visits = {visit2, visit3};
+ FinalizeCluster(cluster);
EXPECT_EQ(cluster.label, u"baz.com and more");
}
diff --git a/chromium/components/history_clusters/core/on_device_clustering_backend.cc b/chromium/components/history_clusters/core/on_device_clustering_backend.cc
index ce359dc9150..e89a515d769 100644
--- a/chromium/components/history_clusters/core/on_device_clustering_backend.cc
+++ b/chromium/components/history_clusters/core/on_device_clustering_backend.cc
@@ -30,11 +30,13 @@
#include "components/history_clusters/core/on_device_clustering_util.h"
#include "components/history_clusters/core/ranking_cluster_finalizer.h"
#include "components/history_clusters/core/similar_visit_deduper_cluster_finalizer.h"
+#include "components/history_clusters/core/single_domain_cluster_finalizer.h"
#include "components/history_clusters/core/single_visit_cluster_finalizer.h"
-#include "components/history_clusters/core/url_deduper_cluster_finalizer.h"
#include "components/optimization_guide/core/batch_entity_metadata_task.h"
#include "components/optimization_guide/core/entity_metadata_provider.h"
+#include "components/optimization_guide/core/new_optimization_guide_decider.h"
#include "components/site_engagement/core/site_engagement_score_provider.h"
+#include "components/url_formatter/url_formatter.h"
namespace history_clusters {
@@ -49,7 +51,8 @@ void RecordBatchUpdateProcessingTime(base::TimeDelta time_delta) {
OnDeviceClusteringBackend::OnDeviceClusteringBackend(
optimization_guide::EntityMetadataProvider* entity_metadata_provider,
- site_engagement::SiteEngagementScoreProvider* engagement_score_provider)
+ site_engagement::SiteEngagementScoreProvider* engagement_score_provider,
+ optimization_guide::NewOptimizationGuideDecider* optimization_guide_decider)
: entity_metadata_provider_(entity_metadata_provider),
engagement_score_provider_(engagement_score_provider),
user_visible_task_traits_(
@@ -73,10 +76,14 @@ OnDeviceClusteringBackend::OnDeviceClusteringBackend(
? continue_on_shutdown_best_effort_task_traits_
: best_effort_task_traits_)),
engagement_score_cache_last_refresh_timestamp_(base::TimeTicks::Now()),
- engagement_score_cache_(
- GetFieldTrialParamByFeatureAsInt(features::kUseEngagementScoreCache,
- "engagement_score_cache_size",
- 100)) {}
+ engagement_score_cache_(GetConfig().engagement_score_cache_size) {
+ if (GetConfig().should_check_hosts_to_skip_clustering_for &&
+ optimization_guide_decider) {
+ optimization_guide_decider_ = optimization_guide_decider;
+ optimization_guide_decider_->RegisterOptimizationTypes(
+ {optimization_guide::proto::HISTORY_CLUSTERS});
+ }
+}
OnDeviceClusteringBackend::~OnDeviceClusteringBackend() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -166,32 +173,20 @@ void OnDeviceClusteringBackend::OnBatchEntityMetadataRetrieved(
base::TimeTicks::Now() - *entity_metadata_start);
}
- if (base::FeatureList::IsEnabled(features::kUseEngagementScoreCache) &&
- base::TimeTicks::Now() >
- engagement_score_cache_last_refresh_timestamp_ +
- base::Minutes(GetFieldTrialParamByFeatureAsInt(
- features::kUseEngagementScoreCache,
- "engagement_score_cache_refresh_duration_minutes", 120))) {
+ if (base::TimeTicks::Now() >
+ (engagement_score_cache_last_refresh_timestamp_ +
+ GetConfig().engagement_score_cache_refresh_duration)) {
engagement_score_cache_.Clear();
engagement_score_cache_last_refresh_timestamp_ = base::TimeTicks::Now();
}
- std::vector<history::ClusterVisit> cluster_visits;
- cluster_visits.reserve(annotated_visits.size());
-
- ProcessBatchOfVisits(clustering_request_source,
- /*num_batches_processed_so_far=*/0,
- /*index_to_process=*/0, std::move(cluster_visits),
- completed_task, std::move(annotated_visits),
- entity_metadata_start, std::move(callback),
- entity_metadata_map);
+ ProcessVisits(clustering_request_source, completed_task,
+ std::move(annotated_visits), entity_metadata_start,
+ std::move(callback), entity_metadata_map);
}
-void OnDeviceClusteringBackend::ProcessBatchOfVisits(
+void OnDeviceClusteringBackend::ProcessVisits(
ClusteringRequestSource clustering_request_source,
- size_t num_batches_processed_so_far,
- size_t index_to_process,
- std::vector<history::ClusterVisit> cluster_visits,
optimization_guide::BatchEntityMetadataTask* completed_task,
std::vector<history::AnnotatedVisit> annotated_visits,
absl::optional<base::TimeTicks> entity_metadata_start,
@@ -202,35 +197,25 @@ void OnDeviceClusteringBackend::ProcessBatchOfVisits(
base::ElapsedThreadTimer process_batch_timer;
- // Entries in |annotated_visits| that have index greater than or equal to
- // |index_stop_batch_processing| should not be processed in this task loop.
- size_t index_stop_batch_processing =
- index_to_process + GetConfig().clustering_tasks_batch_size;
-
- // Process all entries in one go in certain cases. e.g., if
- // |clustering_request_source| is user blocking.
- if (!base::FeatureList::IsEnabled(
- features::kSplitClusteringTasksToSmallerBatches) ||
- clustering_request_source == ClusteringRequestSource::kJourneysPage ||
- annotated_visits.size() <= 1) {
- index_stop_batch_processing = annotated_visits.size();
- }
-
- // Avoid overflows.
- index_stop_batch_processing =
- std::min(index_stop_batch_processing, annotated_visits.size());
-
- base::UmaHistogramCounts1000(
- "History.Clusters.Backend.ProcessBatchOfVisits.BatchSize",
- index_stop_batch_processing - index_to_process);
-
- while (index_to_process < index_stop_batch_processing) {
- const auto& visit = annotated_visits[index_to_process];
- ++index_to_process;
+ std::vector<history::ClusterVisit> cluster_visits;
+ base::flat_map<std::string, optimization_guide::EntityMetadata>
+ human_readable_entity_name_to_metadata_map;
+ for (const auto& visit : annotated_visits) {
history::ClusterVisit cluster_visit;
cluster_visit.annotated_visit = visit;
const std::string& visit_host = visit.url_row.url().host();
+ // Skip visits that should not be clustered.
+ if (optimization_guide_decider_) {
+ optimization_guide::OptimizationGuideDecision decision =
+ optimization_guide_decider_->CanApplyOptimization(
+ visit.url_row.url(), optimization_guide::proto::HISTORY_CLUSTERS,
+ /*optimization_metadata=*/nullptr);
+ if (decision != optimization_guide::OptimizationGuideDecision::kTrue) {
+ continue;
+ }
+ }
+
if (visit.content_annotations.search_normalized_url.is_empty()) {
cluster_visit.normalized_url = visit.url_row.url();
cluster_visit.url_for_deduping =
@@ -241,21 +226,17 @@ void OnDeviceClusteringBackend::ProcessBatchOfVisits(
// Search visits just use the `normalized_url` for deduping.
cluster_visit.url_for_deduping = cluster_visit.normalized_url;
}
+ cluster_visit.url_for_display =
+ ComputeURLForDisplay(cluster_visit.normalized_url);
if (engagement_score_provider_) {
- if (base::FeatureList::IsEnabled(features::kUseEngagementScoreCache)) {
- auto it = engagement_score_cache_.Peek(visit_host);
- if (it != engagement_score_cache_.end()) {
- cluster_visit.engagement_score = it->second;
- } else {
- float score =
- engagement_score_provider_->GetScore(visit.url_row.url());
- engagement_score_cache_.Put(visit_host, score);
- cluster_visit.engagement_score = score;
- }
+ auto it = engagement_score_cache_.Peek(visit_host);
+ if (it != engagement_score_cache_.end()) {
+ cluster_visit.engagement_score = it->second;
} else {
- cluster_visit.engagement_score =
- engagement_score_provider_->GetScore(visit.url_row.url());
+ float score = engagement_score_provider_->GetScore(visit.url_row.url());
+ engagement_score_cache_.Put(visit_host, score);
+ cluster_visit.engagement_score = score;
}
}
@@ -280,6 +261,8 @@ void OnDeviceClusteringBackend::ProcessBatchOfVisits(
rewritten_entity.id = entity_metadata_it->second.human_readable_name;
cluster_visit.annotated_visit.content_annotations.model_annotations
.entities.push_back(rewritten_entity);
+ human_readable_entity_name_to_metadata_map[rewritten_entity.id] =
+ entity_metadata_it->second;
for (const auto& category :
entity_metadata_it->second.human_readable_categories) {
@@ -307,76 +290,59 @@ void OnDeviceClusteringBackend::ProcessBatchOfVisits(
cluster_visits.push_back(cluster_visit);
}
- if (index_to_process >= annotated_visits.size()) {
- RecordBatchUpdateProcessingTime(process_batch_timer.Elapsed());
- OnAllVisitsFinishedProcessing(
- clustering_request_source, num_batches_processed_so_far + 1,
- completed_task, std::move(cluster_visits), std::move(callback));
- return;
- }
-
RecordBatchUpdateProcessingTime(process_batch_timer.Elapsed());
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&OnDeviceClusteringBackend::ProcessBatchOfVisits,
- weak_ptr_factory_.GetWeakPtr(), clustering_request_source,
- num_batches_processed_so_far + 1, index_to_process,
- std::move(cluster_visits), completed_task,
- std::move(annotated_visits), entity_metadata_start,
- std::move(callback), entity_metadata_map));
+ OnAllVisitsFinishedProcessing(
+ clustering_request_source, completed_task, std::move(cluster_visits),
+ std::move(human_readable_entity_name_to_metadata_map),
+ std::move(callback));
}
void OnDeviceClusteringBackend::OnAllVisitsFinishedProcessing(
ClusteringRequestSource clustering_request_source,
- size_t num_batches_processed,
optimization_guide::BatchEntityMetadataTask* completed_task,
std::vector<history::ClusterVisit> cluster_visits,
+ base::flat_map<std::string, optimization_guide::EntityMetadata>
+ human_readable_entity_name_to_entity_metadata_map,
ClustersCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- base::UmaHistogramCounts100(
- "History.Clusters.Backend.NumBatchesProcessedForVisits",
- num_batches_processed);
-
- // Mark the task as completed, which will destruct |entity_metadata_map|.
+ // Mark the task as completed, as we are done with it and have moved
+ // everything adequately at this point.
if (completed_task) {
auto it = in_flight_batch_entity_metadata_tasks_.find(completed_task);
- if (it != in_flight_batch_entity_metadata_tasks_.end())
+ if (it != in_flight_batch_entity_metadata_tasks_.end()) {
in_flight_batch_entity_metadata_tasks_.erase(it);
+ }
}
// Post the actual clustering work onto the thread pool, then reply on the
// calling sequence. This is to prevent UI jank.
- if (base::FeatureList::IsEnabled(
- features::kSplitClusteringTasksToSmallerBatches) &&
- clustering_request_source ==
- ClusteringRequestSource::kKeywordCacheGeneration) {
- best_effort_priority_background_task_runner_->PostTaskAndReplyWithResult(
- FROM_HERE,
- base::BindOnce(
- &OnDeviceClusteringBackend::ClusterVisitsOnBackgroundThread,
- engagement_score_provider_ != nullptr, std::move(cluster_visits)),
- std::move(callback));
- return;
- }
-
- DCHECK(clustering_request_source == ClusteringRequestSource::kJourneysPage ||
- clustering_request_source ==
- ClusteringRequestSource::kKeywordCacheGeneration);
- user_visible_priority_background_task_runner_->PostTaskAndReplyWithResult(
- FROM_HERE,
+ base::OnceCallback<std::vector<history::Cluster>()> clustering_callback =
base::BindOnce(
&OnDeviceClusteringBackend::ClusterVisitsOnBackgroundThread,
- engagement_score_provider_ != nullptr, std::move(cluster_visits)),
- std::move(callback));
+ engagement_score_provider_ != nullptr, std::move(cluster_visits),
+ std::move(human_readable_entity_name_to_entity_metadata_map));
+
+ switch (clustering_request_source) {
+ case ClusteringRequestSource::kJourneysPage:
+ user_visible_priority_background_task_runner_->PostTaskAndReplyWithResult(
+ FROM_HERE, std::move(clustering_callback), std::move(callback));
+ break;
+ case ClusteringRequestSource::kKeywordCacheGeneration:
+ best_effort_priority_background_task_runner_->PostTaskAndReplyWithResult(
+ FROM_HERE, std::move(clustering_callback), std::move(callback));
+ break;
+ }
}
// static
std::vector<history::Cluster>
OnDeviceClusteringBackend::ClusterVisitsOnBackgroundThread(
bool engagement_score_provider_is_valid,
- std::vector<history::ClusterVisit> visits) {
+ std::vector<history::ClusterVisit> visits,
+ base::flat_map<std::string, optimization_guide::EntityMetadata>
+ human_readable_entity_name_to_entity_metadata_map) {
base::ElapsedThreadTimer cluster_visits_timer;
// TODO(crbug.com/1260145): All of these objects are "stateless" between
@@ -398,25 +364,24 @@ OnDeviceClusteringBackend::ClusterVisitsOnBackgroundThread(
cluster_finalizers.push_back(
std::make_unique<ContentVisibilityClusterFinalizer>());
- if (GetConfig().should_dedupe_similar_visits) {
- cluster_finalizers.push_back(
- std::make_unique<SimilarVisitDeduperClusterFinalizer>());
- } else {
- // Otherwise, only dedupe based on normalized URL.
- cluster_finalizers.push_back(
- std::make_unique<UrlDeduperClusterFinalizer>());
- }
+ cluster_finalizers.push_back(
+ std::make_unique<SimilarVisitDeduperClusterFinalizer>());
cluster_finalizers.push_back(std::make_unique<RankingClusterFinalizer>());
if (GetConfig().should_hide_single_visit_clusters_on_prominent_ui_surfaces) {
cluster_finalizers.push_back(
std::make_unique<SingleVisitClusterFinalizer>());
}
+ if (GetConfig().should_hide_single_domain_clusters_on_prominent_ui_surfaces) {
+ cluster_finalizers.push_back(
+ std::make_unique<SingleDomainClusterFinalizer>());
+ }
// Add feature to turn on/off site engagement score filter.
if (engagement_score_provider_is_valid &&
GetConfig().should_filter_noisy_clusters) {
cluster_finalizers.push_back(std::make_unique<NoisyClusterFinalizer>());
}
- cluster_finalizers.push_back(std::make_unique<KeywordClusterFinalizer>());
+ cluster_finalizers.push_back(std::make_unique<KeywordClusterFinalizer>(
+ human_readable_entity_name_to_entity_metadata_map));
if (GetConfig().should_label_clusters) {
cluster_finalizers.push_back(std::make_unique<LabelClusterFinalizer>());
}
@@ -441,7 +406,7 @@ OnDeviceClusteringBackend::ClusterVisitsOnBackgroundThread(
finalizer->FinalizeCluster(cluster);
}
visits_in_clusters.emplace_back(cluster.visits.size());
- keyword_sizes.emplace_back(cluster.keywords.size());
+ keyword_sizes.emplace_back(cluster.keyword_to_data_map.size());
}
// It's a bit strange that this is essentially a `ClusterProcessor` but has
diff --git a/chromium/components/history_clusters/core/on_device_clustering_backend.h b/chromium/components/history_clusters/core/on_device_clustering_backend.h
index ba11d9ecf7b..fbe1599792e 100644
--- a/chromium/components/history_clusters/core/on_device_clustering_backend.h
+++ b/chromium/components/history_clusters/core/on_device_clustering_backend.h
@@ -24,6 +24,7 @@ namespace optimization_guide {
class BatchEntityMetadataTask;
struct EntityMetadata;
class EntityMetadataProvider;
+class NewOptimizationGuideDecider;
} // namespace optimization_guide
namespace site_engagement {
@@ -37,7 +38,9 @@ class OnDeviceClusteringBackend : public ClusteringBackend {
public:
OnDeviceClusteringBackend(
optimization_guide::EntityMetadataProvider* entity_metadata_provider,
- site_engagement::SiteEngagementScoreProvider* engagement_score_provider);
+ site_engagement::SiteEngagementScoreProvider* engagement_score_provider,
+ optimization_guide::NewOptimizationGuideDecider*
+ optimization_guide_decider);
~OnDeviceClusteringBackend() override;
// ClusteringBackend:
@@ -58,12 +61,11 @@ class OnDeviceClusteringBackend : public ClusteringBackend {
const base::flat_map<std::string, optimization_guide::EntityMetadata>&
entity_metadata_map);
- // ProcessBatchOfVisits is called repeatedly to process the visits in batches.
- void ProcessBatchOfVisits(
+ // ProcessVisits adds additional metadata that might be used for clustering or
+ // Journeys to each visit in |annotated_visits|, such as human-readable
+ // entities and categories, site engagement, etc.
+ void ProcessVisits(
ClusteringRequestSource clustering_request_source,
- size_t num_batches_processed_so_far,
- size_t index_to_process,
- std::vector<history::ClusterVisit> cluster_visits,
optimization_guide::BatchEntityMetadataTask* completed_task,
std::vector<history::AnnotatedVisit> annotated_visits,
absl::optional<base::TimeTicks> entity_metadata_start,
@@ -74,21 +76,31 @@ class OnDeviceClusteringBackend : public ClusteringBackend {
// Called when all visits have been processed.
void OnAllVisitsFinishedProcessing(
ClusteringRequestSource clustering_request_source,
- size_t num_batches_processed,
optimization_guide::BatchEntityMetadataTask* completed_task,
std::vector<history::ClusterVisit> cluster_visits,
+ base::flat_map<std::string, optimization_guide::EntityMetadata>
+ human_readable_entity_name_to_entity_metadata_map,
ClustersCallback callback);
// Clusters |visits| on background thread.
static std::vector<history::Cluster> ClusterVisitsOnBackgroundThread(
bool engagement_score_provider_is_valid,
- std::vector<history::ClusterVisit> visits);
+ std::vector<history::ClusterVisit> visits,
+ base::flat_map<std::string, optimization_guide::EntityMetadata>
+ human_readable_entity_name_to_entity_metadata_map);
// The object to fetch entity metadata from. Not owned. Must outlive |this|.
- optimization_guide::EntityMetadataProvider* entity_metadata_provider_;
+ optimization_guide::EntityMetadataProvider* entity_metadata_provider_ =
+ nullptr;
// The object to get engagement scores from. Not owned. Must outlive |this|.
- site_engagement::SiteEngagementScoreProvider* engagement_score_provider_;
+ site_engagement::SiteEngagementScoreProvider* engagement_score_provider_ =
+ nullptr;
+
+ // The object to fetch page load metadata from. Not owned. Must outlive
+ // |this|.
+ optimization_guide::NewOptimizationGuideDecider* optimization_guide_decider_ =
+ nullptr;
// The set of batch entity metadata tasks currently in flight.
base::flat_set<std::unique_ptr<optimization_guide::BatchEntityMetadataTask>,
diff --git a/chromium/components/history_clusters/core/on_device_clustering_backend_unittest.cc b/chromium/components/history_clusters/core/on_device_clustering_backend_unittest.cc
index 9896df329ab..16d89c9fd9d 100644
--- a/chromium/components/history_clusters/core/on_device_clustering_backend_unittest.cc
+++ b/chromium/components/history_clusters/core/on_device_clustering_backend_unittest.cc
@@ -12,6 +12,7 @@
#include "components/history_clusters/core/config.h"
#include "components/history_clusters/core/on_device_clustering_features.h"
#include "components/optimization_guide/core/entity_metadata_provider.h"
+#include "components/optimization_guide/core/new_optimization_guide_decider.h"
#include "components/site_engagement/core/site_engagement_score_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -71,6 +72,7 @@ class TestEntityMetadataProvider
{"category-" + entity_id, 0.5});
metadata.human_readable_categories.insert(
{"toolow-" + entity_id, 0.01});
+ metadata.human_readable_aliases.push_back("alias-" + entity_id);
std::move(callback).Run(entity_id == "nometadata"
? absl::nullopt
: absl::make_optional(metadata));
@@ -82,22 +84,58 @@ class TestEntityMetadataProvider
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
};
+class TestOptimizationGuideDecider
+ : public optimization_guide::NewOptimizationGuideDecider {
+ public:
+ TestOptimizationGuideDecider() = default;
+ ~TestOptimizationGuideDecider() override = default;
+
+ void RegisterOptimizationTypes(
+ const std::vector<optimization_guide::proto::OptimizationType>&
+ optimization_types) override {
+ ASSERT_EQ(optimization_types.size(), 1u);
+ ASSERT_EQ(optimization_guide::proto::HISTORY_CLUSTERS,
+ optimization_types[0]);
+ }
+
+ void CanApplyOptimization(
+ const GURL& url,
+ optimization_guide::proto::OptimizationType optimization_type,
+ optimization_guide::OptimizationGuideDecisionCallback callback) override {
+ NOTREACHED();
+ }
+
+ optimization_guide::OptimizationGuideDecision CanApplyOptimization(
+ const GURL& url,
+ optimization_guide::proto::OptimizationType optimization_type,
+ optimization_guide::OptimizationMetadata* optimization_metadata)
+ override {
+ DCHECK_EQ(optimization_guide::proto::HISTORY_CLUSTERS, optimization_type);
+ return url.host() == "shouldskip.com"
+ ? optimization_guide::OptimizationGuideDecision::kFalse
+ : optimization_guide::OptimizationGuideDecision::kTrue;
+ }
+};
+
class OnDeviceClusteringWithoutContentBackendTest : public ::testing::Test {
public:
OnDeviceClusteringWithoutContentBackendTest() {
config_.content_clustering_enabled = false;
- config_.should_dedupe_similar_visits = false;
- config_.should_include_categories_in_keywords = true;
- config_.should_exclude_keywords_from_noisy_visits = false;
+ config_.keyword_filter_on_categories = true;
+ config_.keyword_filter_on_noisy_visits = true;
+ config_.keyword_filter_on_entity_aliases = true;
+ config_.max_entity_aliases_in_keywords = 100;
config_.split_clusters_at_search_visits = false;
config_.should_label_clusters = false;
config_.entity_relevance_threshold = 60;
+ config_.should_check_hosts_to_skip_clustering_for = true;
SetConfigForTesting(config_);
}
void SetUp() override {
clustering_backend_ = std::make_unique<OnDeviceClusteringBackend>(
- /*entity_metadata_provider=*/nullptr, &test_site_engagement_provider_);
+ /*entity_metadata_provider=*/nullptr, &test_site_engagement_provider_,
+ /*optimization_guide_decider_=*/nullptr);
}
void TearDown() override { clustering_backend_.reset(); }
@@ -181,7 +219,7 @@ TEST_F(OnDeviceClusteringWithoutContentBackendTest,
ElementsAre(ElementsAre(testing::VisitResult(1, 1.0),
testing::VisitResult(2, 1.0))));
ASSERT_EQ(result_clusters.size(), 1u);
- EXPECT_THAT(result_clusters.at(0).keywords,
+ EXPECT_THAT(result_clusters.at(0).GetKeywords(),
UnorderedElementsAre(std::u16string(u"google-category"),
std::u16string(u"com"),
std::u16string(u"google-entity")));
@@ -324,8 +362,10 @@ TEST_F(OnDeviceClusteringWithoutContentBackendTest, MultipleClusters) {
visit5.referring_visit_of_redirect_chain_start = 6;
visits.push_back(visit5);
- history::AnnotatedVisit visit3 =
- testing::CreateDefaultAnnotatedVisit(3, GURL("https://whatever.com/"));
+ // Although it says shouldskip, it should not be skipped since there is no
+ // optimization guide decider.
+ history::AnnotatedVisit visit3 = testing::CreateDefaultAnnotatedVisit(
+ 3, GURL("https://shouldskip.com/butnotsincenodecider"));
visits.push_back(visit3);
std::vector<history::Cluster> result_clusters =
@@ -393,9 +433,10 @@ class OnDeviceClusteringWithContentBackendTest
public:
OnDeviceClusteringWithContentBackendTest() {
config_.content_clustering_enabled = true;
- config_.should_dedupe_similar_visits = false;
- config_.should_include_categories_in_keywords = true;
- config_.should_exclude_keywords_from_noisy_visits = false;
+ config_.keyword_filter_on_categories = true;
+ config_.keyword_filter_on_noisy_visits = true;
+ config_.keyword_filter_on_entity_aliases = true;
+ config_.should_check_hosts_to_skip_clustering_for = false;
SetConfigForTesting(config_);
}
@@ -438,7 +479,8 @@ TEST_F(OnDeviceClusteringWithContentBackendTest, ClusterOnContent) {
// visit, visit2, and visit4 but all of the visits have the same entities
// and categories so they will be clustered in the content pass.
history::AnnotatedVisit visit5 = testing::CreateDefaultAnnotatedVisit(
- 10, GURL("https://nonexistentreferrer.com/"));
+ 10, GURL("https://shouldskip.com/butnotsincehostcheckingisfalse/"
+ "andhasnonexistentreferrer"));
visit5.content_annotations.model_annotations.entities = {{"github", 100}};
visit5.content_annotations.model_annotations.categories = {
{"category", 100}, {"category2", 100}};
@@ -519,13 +561,18 @@ class OnDeviceClusteringWithAllTheBackendsTest
entity_metadata_provider_ = std::make_unique<TestEntityMetadataProvider>(
task_environment_.GetMainThreadTaskRunner());
+ optimization_guide_decider_ =
+ std::make_unique<TestOptimizationGuideDecider>();
+
clustering_backend_ = std::make_unique<OnDeviceClusteringBackend>(
entity_metadata_provider_.get(),
- /*engagement_score_provider=*/nullptr);
+ /*engagement_score_provider=*/nullptr,
+ optimization_guide_decider_.get());
}
private:
std::unique_ptr<TestEntityMetadataProvider> entity_metadata_provider_;
+ std::unique_ptr<TestOptimizationGuideDecider> optimization_guide_decider_;
};
TEST_F(OnDeviceClusteringWithAllTheBackendsTest,
@@ -569,6 +616,10 @@ TEST_F(OnDeviceClusteringWithAllTheBackendsTest,
visit3.content_annotations.model_annotations.visibility_score = 0.5;
visits.push_back(visit3);
+ history::AnnotatedVisit should_skip = testing::CreateDefaultAnnotatedVisit(
+ 11, GURL("https://shouldskip.com/whatever"));
+ visits.push_back(should_skip);
+
std::vector<history::Cluster> result_clusters =
ClusterVisits(ClusteringRequestSource::kJourneysPage, visits);
EXPECT_THAT(
@@ -607,6 +658,10 @@ TEST_F(OnDeviceClusteringWithAllTheBackendsTest,
.annotated_visit.content_annotations.model_annotations
.visibility_score,
FloatEq(0.5));
+ // Cluster should have 3 keywords.
+ EXPECT_THAT(
+ cluster.GetKeywords(),
+ UnorderedElementsAre(u"rewritten-foo", u"category-foo", u"alias-foo"));
history::Cluster cluster2 = result_clusters.at(1);
ASSERT_EQ(cluster2.visits.size(), 1u);
@@ -619,6 +674,7 @@ TEST_F(OnDeviceClusteringWithAllTheBackendsTest,
.model_annotations.entities.empty());
EXPECT_TRUE(third_result_visit.annotated_visit.content_annotations
.model_annotations.categories.empty());
+ EXPECT_TRUE(cluster2.keyword_to_data_map.empty());
histogram_tester.ExpectUniqueSample(
"History.Clusters.Backend.ClusterSize.Min", 1, 1);
@@ -627,42 +683,14 @@ TEST_F(OnDeviceClusteringWithAllTheBackendsTest,
histogram_tester.ExpectUniqueSample(
"History.Clusters.Backend.NumKeywordsPerCluster.Min", 0, 1);
histogram_tester.ExpectUniqueSample(
- "History.Clusters.Backend.NumKeywordsPerCluster.Max", 2, 1);
+ "History.Clusters.Backend.NumKeywordsPerCluster.Max", 3, 1);
histogram_tester.ExpectTotalCount(
"History.Clusters.Backend.BatchEntityLookupLatency2", 1);
histogram_tester.ExpectUniqueSample(
"History.Clusters.Backend.BatchEntityLookupSize", 2, 1);
}
-class EngagementCacheOnDeviceClusteringWithoutContentBackendTest
- : public OnDeviceClusteringWithoutContentBackendTest,
- public ::testing::WithParamInterface<bool> {
- public:
- EngagementCacheOnDeviceClusteringWithoutContentBackendTest() {
- config_.content_clustering_enabled = false;
- config_.should_dedupe_similar_visits = false;
- config_.should_include_categories_in_keywords = true;
- config_.should_exclude_keywords_from_noisy_visits = false;
- SetConfigForTesting(config_);
-
- if (GetParam()) {
- scoped_feature_list_.InitAndEnableFeature(
- features::kUseEngagementScoreCache);
- } else {
- scoped_feature_list_.InitAndDisableFeature(
- features::kUseEngagementScoreCache);
- }
- }
-
- bool IsCacheStoreFeatureEnabled() const { return GetParam(); }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
- Config config_;
-};
-
-TEST_P(EngagementCacheOnDeviceClusteringWithoutContentBackendTest,
- EngagementScoreCache) {
+TEST_F(OnDeviceClusteringWithoutContentBackendTest, EngagementScoreCache) {
base::HistogramTester histogram_tester;
std::vector<history::AnnotatedVisit> visits;
@@ -689,110 +717,13 @@ TEST_P(EngagementCacheOnDeviceClusteringWithoutContentBackendTest,
std::vector<history::Cluster> result_clusters_1 =
ClusterVisits(ClusteringRequestSource::kJourneysPage, visits);
- EXPECT_EQ(IsCacheStoreFeatureEnabled() ? 2u : 5u,
- GetSiteEngagementGetScoreInvocationCount());
+ EXPECT_EQ(2u, GetSiteEngagementGetScoreInvocationCount());
// No new queries should be issued when cache store is enabled.
std::vector<history::Cluster> result_clusters_2 =
ClusterVisits(ClusteringRequestSource::kJourneysPage, visits);
- EXPECT_EQ(IsCacheStoreFeatureEnabled() ? 2u : 10u,
- GetSiteEngagementGetScoreInvocationCount());
+ EXPECT_EQ(2u, GetSiteEngagementGetScoreInvocationCount());
}
-INSTANTIATE_TEST_SUITE_P(
- All,
- EngagementCacheOnDeviceClusteringWithoutContentBackendTest,
- ::testing::Bool());
-
-class BatchedClusteringTaskOnDeviceClusteringWithoutContentBackendTest
- : public OnDeviceClusteringWithoutContentBackendTest,
- public ::testing::WithParamInterface<
- std::tuple<bool, ClusteringRequestSource>> {
- public:
- BatchedClusteringTaskOnDeviceClusteringWithoutContentBackendTest() {
- config_.content_clustering_enabled = false;
- config_.should_dedupe_similar_visits = false;
- config_.should_include_categories_in_keywords = true;
- config_.should_exclude_keywords_from_noisy_visits = false;
- config_.clustering_tasks_batch_size = 1;
- SetConfigForTesting(config_);
-
- // expected_size_of_batches is 1.
- const base::FieldTrialParams batched_clustering_feature_parameters = {
- {"clustering_task_batch_size", "1"}};
- base::test::ScopedFeatureList::FeatureAndParams batched_clustering(
- features::kSplitClusteringTasksToSmallerBatches,
- batched_clustering_feature_parameters);
-
- if (IsBatchingEnabled()) {
- scoped_feature_list_.InitWithFeaturesAndParameters({{batched_clustering}},
- {});
- } else {
- scoped_feature_list_.InitAndDisableFeature(
- features::kSplitClusteringTasksToSmallerBatches);
- }
- }
-
- bool IsBatchingEnabled() const { return std::get<0>(GetParam()); }
-
- ClusteringRequestSource GetClusteringRequestSource() const {
- return std::get<1>(GetParam());
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
- Config config_;
-};
-
-TEST_P(BatchedClusteringTaskOnDeviceClusteringWithoutContentBackendTest,
- Baseline) {
- base::HistogramTester histogram_tester;
- std::vector<history::AnnotatedVisit> visits;
-
- // Add 1000 visits to |visits|.
- for (int i = 0; i < 1000; ++i) {
- visits.push_back(testing::CreateDefaultAnnotatedVisit(
- i + 1, GURL("https://github.com/")));
- }
-
- std::vector<history::Cluster> result_clusters_1 =
- ClusterVisits(GetClusteringRequestSource(), visits);
-
- if (base::FeatureList::IsEnabled(features::kUseEngagementScoreCache)) {
- EXPECT_EQ(1u, GetSiteEngagementGetScoreInvocationCount());
- } else {
- EXPECT_EQ(1000u, GetSiteEngagementGetScoreInvocationCount());
- }
-
- size_t expected_number_of_batches = 1;
- size_t expected_size_of_batches = 1000;
- if (IsBatchingEnabled() &&
- GetClusteringRequestSource() ==
- ClusteringRequestSource::kKeywordCacheGeneration) {
- expected_number_of_batches = 1000;
- expected_size_of_batches = 1;
- }
-
- histogram_tester.ExpectTotalCount(
- "History.Clusters.Backend.ProcessBatchOfVisits.BatchSize",
- expected_number_of_batches);
- histogram_tester.ExpectUniqueSample(
- "History.Clusters.Backend.ProcessBatchOfVisits.BatchSize",
- expected_size_of_batches, expected_number_of_batches);
- histogram_tester.ExpectUniqueSample(
- "History.Clusters.Backend.NumBatchesProcessedForVisits",
- expected_number_of_batches, 1);
-}
-
-const bool kDirectExecutorEnabled[]{true, false};
-
-INSTANTIATE_TEST_SUITE_P(
- BatchedClusteringTaskOnDeviceClusteringWithoutContentBackendTest,
- BatchedClusteringTaskOnDeviceClusteringWithoutContentBackendTest,
- ::testing::Combine(
- ::testing::ValuesIn(kDirectExecutorEnabled),
- ::testing::Values(ClusteringRequestSource::kJourneysPage,
- ClusteringRequestSource::kKeywordCacheGeneration)));
-
} // namespace
} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/on_device_clustering_features.cc b/chromium/components/history_clusters/core/on_device_clustering_features.cc
index 15d776fba10..6895176de5e 100644
--- a/chromium/components/history_clusters/core/on_device_clustering_features.cc
+++ b/chromium/components/history_clusters/core/on_device_clustering_features.cc
@@ -25,7 +25,10 @@ const base::Feature kSplitClusteringTasksToSmallerBatches{
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kOnDeviceClusteringBlocklists{
- "JourneysOnDeviceClusteringBlocklist", base::FEATURE_DISABLED_BY_DEFAULT};
+ "JourneysOnDeviceClusteringBlocklist", base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kOnDeviceClusteringKeywordFiltering{
+ "JourneysKeywordFiltering", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace features
} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/on_device_clustering_features.h b/chromium/components/history_clusters/core/on_device_clustering_features.h
index 3d86994feb8..8f9f51643ed 100644
--- a/chromium/components/history_clusters/core/on_device_clustering_features.h
+++ b/chromium/components/history_clusters/core/on_device_clustering_features.h
@@ -26,6 +26,9 @@ extern const base::Feature kSplitClusteringTasksToSmallerBatches;
// Specifies various blocklists for on-device clustering backend.
extern const base::Feature kOnDeviceClusteringBlocklists;
+// Specifies how keywords get filtered and added to a cluster.
+extern const base::Feature kOnDeviceClusteringKeywordFiltering;
+
} // namespace features
} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/on_device_clustering_util.cc b/chromium/components/history_clusters/core/on_device_clustering_util.cc
index a1c325f7757..faf9a8d75ad 100644
--- a/chromium/components/history_clusters/core/on_device_clustering_util.cc
+++ b/chromium/components/history_clusters/core/on_device_clustering_util.cc
@@ -83,7 +83,7 @@ void SortClusters(std::vector<history::Cluster>* clusters) {
DCHECK(clusters);
// Within each cluster, sort visits.
for (auto& cluster : *clusters) {
- StableSortVisits(&cluster.visits);
+ StableSortVisits(cluster.visits);
}
// After that, sort clusters reverse-chronologically based on their highest
diff --git a/chromium/components/history_clusters/core/query_clusters_state.cc b/chromium/components/history_clusters/core/query_clusters_state.cc
index e55ba8070cb..e16b694f2bc 100644
--- a/chromium/components/history_clusters/core/query_clusters_state.cc
+++ b/chromium/components/history_clusters/core/query_clusters_state.cc
@@ -30,9 +30,16 @@ class QueryClustersState::PostProcessor
std::vector<history::Cluster> PostProcess(
std::vector<history::Cluster> clusters) {
- ApplySearchQuery(query_, &clusters);
- CullNonProminentOrDuplicateClusters(query_, &clusters,
+ ApplySearchQuery(query_, clusters);
+ CullNonProminentOrDuplicateClusters(query_, clusters,
&seen_single_visit_cluster_urls_);
+ // We have to do this AFTER applying the search query, because applying the
+ // search query re-scores matching visits to promote them above non-matching
+ // visits.
+ HideAndCullLowScoringVisits(clusters);
+ // Do this AFTER we cull the low scoring visits, so those visits don't get
+ // their related searches coalesced onto the cluster level.
+ CoalesceRelatedSearches(clusters);
return clusters;
}
@@ -67,20 +74,19 @@ void QueryClustersState::LoadNextBatchOfClusters(ResultCallback callback) {
return;
base::TimeTicks query_start_time = base::TimeTicks::Now();
- base::Time end_time = continuation_end_time_.value_or(base::Time());
- service_->QueryClusters(ClusteringRequestSource::kJourneysPage,
- /*begin_time=*/base::Time(), end_time,
- base::BindOnce(&QueryClustersState::OnGotRawClusters,
- weak_factory_.GetWeakPtr(),
- query_start_time, std::move(callback)),
- &task_tracker_);
+ query_clusters_task = service_->QueryClusters(
+ ClusteringRequestSource::kJourneysPage,
+ /*begin_time=*/base::Time(), continuation_params_,
+ base::BindOnce(&QueryClustersState::OnGotRawClusters,
+ weak_factory_.GetWeakPtr(), query_start_time,
+ std::move(callback)));
}
void QueryClustersState::OnGotRawClusters(
base::TimeTicks query_start_time,
ResultCallback callback,
std::vector<history::Cluster> clusters,
- base::Time continuation_end_time) const {
+ QueryClustersContinuationParams continuation_params) const {
// Post-process the clusters (expensive task) on an anonymous thread to
// prevent janks.
base::ElapsedTimer post_processing_timer; // Create here to time the task.
@@ -93,15 +99,16 @@ void QueryClustersState::OnGotRawClusters(
base::BindOnce(
&QueryClustersState::OnGotClusters, weak_factory_.GetWeakPtr(),
std::move(post_processing_timer), clusters_from_backend_count,
- query_start_time, std::move(callback), continuation_end_time));
+ query_start_time, std::move(callback), continuation_params));
}
-void QueryClustersState::OnGotClusters(base::ElapsedTimer post_processing_timer,
- size_t clusters_from_backend_count,
- base::TimeTicks query_start_time,
- ResultCallback callback,
- base::Time continuation_end_time,
- std::vector<history::Cluster> clusters) {
+void QueryClustersState::OnGotClusters(
+ base::ElapsedTimer post_processing_timer,
+ size_t clusters_from_backend_count,
+ base::TimeTicks query_start_time,
+ ResultCallback callback,
+ QueryClustersContinuationParams continuation_params,
+ std::vector<history::Cluster> clusters) {
base::UmaHistogramTimes("History.Clusters.ProcessClustersDuration",
post_processing_timer.Elapsed());
@@ -114,9 +121,7 @@ void QueryClustersState::OnGotClusters(base::ElapsedTimer post_processing_timer,
(1.0 * clusters_from_backend_count) * 100)));
}
- continuation_end_time_.reset();
- if (!continuation_end_time.is_null())
- continuation_end_time_ = continuation_end_time;
+ continuation_params_ = continuation_params;
// In case no clusters came back, recursively ask for more here. We do this
// to fulfill the mojom contract where we always return at least one cluster,
@@ -128,16 +133,13 @@ void QueryClustersState::OnGotClusters(base::ElapsedTimer post_processing_timer,
// This is distinct from the "tall monitor" case because the page may already
// be full of clusters. In that case, the WebUI would not know to make another
// request for clusters.
- if (clusters.empty() && continuation_end_time_.has_value()) {
+ if (clusters.empty() && !continuation_params.is_done) {
LoadNextBatchOfClusters(std::move(callback));
return;
}
- bool can_load_more = continuation_end_time_.has_value();
- std::move(callback).Run(query_, std::move(clusters), can_load_more,
- is_continuation_);
-
- // Further responses should be consider continuations.
+ std::move(callback).Run(query_, std::move(clusters),
+ !continuation_params.is_done, is_continuation_);
is_continuation_ = true;
// Log metrics after delivering the results to the page.
diff --git a/chromium/components/history_clusters/core/query_clusters_state.h b/chromium/components/history_clusters/core/query_clusters_state.h
index e8dc66960c3..34dc9a967b2 100644
--- a/chromium/components/history_clusters/core/query_clusters_state.h
+++ b/chromium/components/history_clusters/core/query_clusters_state.h
@@ -15,6 +15,7 @@
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "components/history/core/browser/history_types.h"
+#include "components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.h"
#include "components/history_clusters/core/history_clusters_types.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -60,17 +61,18 @@ class QueryClustersState {
class PostProcessor;
// Callback to `LoadNextBatchOfClusters()`.
- void OnGotRawClusters(base::TimeTicks query_start_time,
- ResultCallback callback,
- std::vector<history::Cluster> clusters,
- base::Time continuation_end_time) const;
+ void OnGotRawClusters(
+ base::TimeTicks query_start_time,
+ ResultCallback callback,
+ std::vector<history::Cluster> clusters,
+ QueryClustersContinuationParams continuation_params) const;
// Callback to `OnGotRawClusters()`.
void OnGotClusters(base::ElapsedTimer post_processing_timer,
size_t clusters_from_backend_count,
base::TimeTicks query_start_time,
ResultCallback callback,
- base::Time continuation_end_time,
+ QueryClustersContinuationParams continuation_params,
std::vector<history::Cluster> clusters);
// A weak pointer to the service in case we outlive the service.
@@ -80,17 +82,16 @@ class QueryClustersState {
// The string query the user entered into the searchbox.
const std::string query_;
- // A nullopt `continuation_end_time` means we have exhausted History.
- // Note that this differs from History itself, which uses base::Time() as the
- // value to indicate we've exhausted history. I've found that to be not
- // explicit enough in practice. This value will never be base::Time().
- absl::optional<base::Time> continuation_end_time_;
+ // The continuation params used to track where the last query left off and
+ // query for the "next page".
+ QueryClustersContinuationParams continuation_params_;
// True for all 'next-page' responses, but false for the first page.
bool is_continuation_ = false;
// Used only to fast-cancel tasks in case we are destroyed.
- base::CancelableTaskTracker task_tracker_;
+ std::unique_ptr<HistoryClustersServiceTaskGetMostRecentClusters>
+ query_clusters_task;
// A task runner to run all the post-processing tasks on.
scoped_refptr<base::SequencedTaskRunner> post_processing_task_runner_;
diff --git a/chromium/components/history_clusters/core/query_clusters_state_unittest.cc b/chromium/components/history_clusters/core/query_clusters_state_unittest.cc
index 5f7ee1401a3..c4df13631a0 100644
--- a/chromium/components/history_clusters/core/query_clusters_state_unittest.cc
+++ b/chromium/components/history_clusters/core/query_clusters_state_unittest.cc
@@ -16,6 +16,18 @@
namespace history_clusters {
+namespace {
+
+// A struct containing all the params in `QueryClustersState::ResultCallback`.
+struct OnGotClustersResult {
+ std::string query;
+ std::vector<history::Cluster> cluster_batch;
+ bool can_load_more;
+ bool is_continuation;
+};
+
+} // namespace
+
class QueryClustersStateTest : public testing::Test {
public:
QueryClustersStateTest()
@@ -26,29 +38,43 @@ class QueryClustersStateTest : public testing::Test {
QueryClustersStateTest& operator=(const QueryClustersStateTest&) = delete;
protected:
- std::vector<history::Cluster> InjectRawClustersAndAwaitPostProcessing(
+ OnGotClustersResult InjectRawClustersAndAwaitPostProcessing(
QueryClustersState* state,
- const std::vector<history::Cluster>& raw_clusters) {
+ const std::vector<history::Cluster>& raw_clusters,
+ QueryClustersContinuationParams continuation_params) {
// This block injects the fake `raw_clusters` data for post-processing and
// spins the message loop until we finish post-processing.
- std::vector<history::Cluster> result;
- {
- base::RunLoop loop;
- state->OnGotRawClusters(
- base::TimeTicks(),
- base::BindLambdaForTesting(
- [&](const std::string& query,
- std::vector<history::Cluster> cluster_batch,
- bool can_load_more, bool is_continuation) {
- result = std::move(cluster_batch);
- loop.Quit();
- }),
- raw_clusters, base::Time());
- loop.Run();
- }
+ OnGotClustersResult result;
+ base::RunLoop loop;
+ state->OnGotRawClusters(
+ base::TimeTicks(),
+ base::BindLambdaForTesting(
+ [&](const std::string& query,
+ std::vector<history::Cluster> cluster_batch, bool can_load_more,
+ bool is_continuation) {
+ result = {query, cluster_batch, can_load_more, is_continuation};
+ loop.Quit();
+ }),
+ raw_clusters, continuation_params);
+ loop.Run();
return result;
}
+ void InjectRawClustersAndExpectNoCallback(
+ QueryClustersState* state,
+ const std::vector<history::Cluster>& raw_clusters,
+ QueryClustersContinuationParams continuation_params) {
+ state->OnGotRawClusters(
+ base::TimeTicks(),
+ base::BindLambdaForTesting(
+ [&](const std::string& query,
+ std::vector<history::Cluster> cluster_batch, bool can_load_more,
+ bool is_continuation) {
+ FAIL() << "Callback should not have been called.";
+ }),
+ raw_clusters, continuation_params);
+ }
+
base::test::TaskEnvironment task_environment_;
};
@@ -58,19 +84,24 @@ TEST_F(QueryClustersStateTest, PostProcessingOccursAndLogsHistograms) {
std::vector<history::Cluster> raw_clusters;
raw_clusters.push_back(
- history::Cluster(1, {}, {u"keyword_one"},
+ history::Cluster(1, {}, {{u"keyword_one", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
raw_clusters.push_back(
- history::Cluster(2, {}, {u"keyword_two"},
+ history::Cluster(2, {}, {{u"keyword_two", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
- auto result = InjectRawClustersAndAwaitPostProcessing(&state, raw_clusters);
+ auto result =
+ InjectRawClustersAndAwaitPostProcessing(&state, raw_clusters, {});
// Just a basic test to verify that post-processing did indeed occur.
// Detailed tests for the behavior of the filtering are in
// `HistoryClustersUtil`.
- ASSERT_EQ(result.size(), 1U);
- EXPECT_EQ(result[0].cluster_id, 2);
+ ASSERT_EQ(result.cluster_batch.size(), 1U);
+ EXPECT_EQ(result.cluster_batch[0].cluster_id, 2);
+
+ EXPECT_EQ(result.query, "");
+ EXPECT_EQ(result.can_load_more, true);
+ EXPECT_EQ(result.is_continuation, false);
histogram_tester.ExpectBucketCount(
"History.Clusters.PercentClustersFilteredByQuery", 50, 1);
@@ -83,20 +114,28 @@ TEST_F(QueryClustersStateTest, CrossBatchDeduplication) {
{
std::vector<history::Cluster> raw_clusters;
// Verify that non-matching prominent clusters are filtered out.
- raw_clusters.push_back(
- history::Cluster(1, {}, {u"keyword_one"},
- /*should_show_on_prominent_ui_surfaces=*/true));
+ raw_clusters.push_back(history::Cluster(
+ 1, {}, {{u"keyword_one", history::ClusterKeywordData()}},
+ /*should_show_on_prominent_ui_surfaces=*/true));
// Verify that matching non-prominent clusters still are shown.
raw_clusters.push_back(
- history::Cluster(2, {GetHardcodedClusterVisit(1)}, {u"myquery"},
+ history::Cluster(2, {GetHardcodedClusterVisit(1)},
+ {{u"myquery", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
- auto result = InjectRawClustersAndAwaitPostProcessing(&state, raw_clusters);
+ auto result =
+ InjectRawClustersAndAwaitPostProcessing(&state, raw_clusters, {});
- ASSERT_EQ(result.size(), 1U);
- EXPECT_EQ(result[0].cluster_id, 2);
- ASSERT_EQ(result[0].visits.size(), 1U);
- EXPECT_EQ(result[0].visits[0].annotated_visit.visit_row.visit_id, 1);
+ ASSERT_EQ(result.cluster_batch.size(), 1U);
+ EXPECT_EQ(result.cluster_batch[0].cluster_id, 2);
+ ASSERT_EQ(result.cluster_batch[0].visits.size(), 1U);
+ EXPECT_EQ(
+ result.cluster_batch[0].visits[0].annotated_visit.visit_row.visit_id,
+ 1);
+
+ EXPECT_EQ(result.query, "myquery");
+ EXPECT_EQ(result.can_load_more, true);
+ EXPECT_EQ(result.is_continuation, false);
}
// Send through a second batch of raw clusters. This verifies the stateful
@@ -106,24 +145,119 @@ TEST_F(QueryClustersStateTest, CrossBatchDeduplication) {
// Verify that a matching non-prominent non-duplicate cluster is still
// allowed.
raw_clusters.push_back(
- history::Cluster(3, {GetHardcodedClusterVisit(2)}, {u"myquery"},
+ history::Cluster(3, {GetHardcodedClusterVisit(2)},
+ {{u"myquery", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
// Verify that a matching non-prominent duplicate cluster is filtered out.
raw_clusters.push_back(
- history::Cluster(4, {GetHardcodedClusterVisit(1)}, {u"myquery"},
+ history::Cluster(4, {GetHardcodedClusterVisit(1)},
+ {{u"myquery", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/false));
// Verify that a matching prominent duplicate cluster is still allowed.
raw_clusters.push_back(
- history::Cluster(5, {GetHardcodedClusterVisit(1)}, {u"myquery"},
+ history::Cluster(5, {GetHardcodedClusterVisit(1)},
+ {{u"myquery", history::ClusterKeywordData()}},
/*should_show_on_prominent_ui_surfaces=*/true));
- auto result = InjectRawClustersAndAwaitPostProcessing(&state, raw_clusters);
+ auto result =
+ InjectRawClustersAndAwaitPostProcessing(&state, raw_clusters, {});
+
+ ASSERT_EQ(result.cluster_batch.size(), 2U);
+ EXPECT_EQ(result.cluster_batch[0].cluster_id, 3);
+ EXPECT_EQ(result.cluster_batch[1].cluster_id, 5);
+
+ EXPECT_EQ(result.query, "myquery");
+ EXPECT_EQ(result.can_load_more, true);
+ EXPECT_EQ(result.is_continuation, true);
+ }
+}
+
+TEST_F(QueryClustersStateTest, OnGotClusters) {
+ const history::Cluster hidden_cluster = {1, {}, {}, false};
+ const history::Cluster visible_cluster = {2, {}, {}, true};
+
+ {
+ QueryClustersState state(nullptr, "");
+
+ // If the response clusters is empty, the callback should not be invoked.
+ InjectRawClustersAndExpectNoCallback(&state, {},
+ {base::Time(),
+ /*is_continuation=*/false,
+ /*is_partial_day=*/false,
+ /*exhausted_history=*/false,
+ /*is_done=*/false});
+
+ // Likewise if the response clusters is filtered.
+ InjectRawClustersAndExpectNoCallback(&state, {hidden_cluster},
+ {base::Time(),
+ /*is_continuation=*/false,
+ /*is_partial_day=*/false,
+ /*exhausted_history=*/false,
+ /*is_done=*/false});
+
+ // Even if the clusters is empty, the callback should be called when history
+ // is exhausted.
+ // Also, `is_continuation` should be false since this is the first time the
+ // callbacks invoked even though there's been 2 earlier
+ // `HistoryClusterService` responses.
+ auto result =
+ InjectRawClustersAndAwaitPostProcessing(&state, {},
+ {base::Time(),
+ /*is_continuation=*/false,
+ /*is_partial_day=*/false,
+ /*exhausted_history=*/true,
+ /*is_done=*/true});
+ EXPECT_EQ(result.cluster_batch.size(), 0u);
+ EXPECT_EQ(result.query, "");
+ EXPECT_EQ(result.can_load_more, false);
+ EXPECT_EQ(result.is_continuation, false);
+ }
+
+ {
+ QueryClustersState state(nullptr, "");
+
+ // `is_continuation` should be false on the first callback.
+ // `can_load_more` should be true on non-last callback.
+ auto result =
+ InjectRawClustersAndAwaitPostProcessing(&state, {visible_cluster},
+ {base::Time(),
+ /*is_continuation=*/false,
+ /*is_partial_day=*/false,
+ /*exhausted_history=*/false,
+ /*is_done=*/false});
+ EXPECT_EQ(result.cluster_batch.size(), 1u);
+ EXPECT_EQ(result.query, "");
+ EXPECT_EQ(result.can_load_more, true);
+ EXPECT_EQ(result.is_continuation, false);
+
+ // `is_continuation` should be true on non-first callback.
+ // `can_load_more` should be true on non-last callback.
+ result =
+ InjectRawClustersAndAwaitPostProcessing(&state, {visible_cluster},
+ {base::Time(),
+ /*is_continuation=*/true,
+ /*is_partial_day=*/false,
+ /*exhausted_history=*/false,
+ /*is_done=*/false});
+ EXPECT_EQ(result.cluster_batch.size(), 1u);
+ EXPECT_EQ(result.query, "");
+ EXPECT_EQ(result.can_load_more, true);
+ EXPECT_EQ(result.is_continuation, true);
- ASSERT_EQ(result.size(), 2U);
- EXPECT_EQ(result[0].cluster_id, 3);
- EXPECT_EQ(result[1].cluster_id, 5);
+ // `can_load_more` should be false on the last callback.
+ result =
+ InjectRawClustersAndAwaitPostProcessing(&state, {visible_cluster},
+ {base::Time(),
+ /*is_continuation=*/true,
+ /*is_partial_day=*/false,
+ /*exhausted_history=*/true,
+ /*is_done=*/true});
+ EXPECT_EQ(result.cluster_batch.size(), 1u);
+ EXPECT_EQ(result.query, "");
+ EXPECT_EQ(result.can_load_more, false);
+ EXPECT_EQ(result.is_continuation, true);
}
}
diff --git a/chromium/components/history_clusters/core/ranking_cluster_finalizer.cc b/chromium/components/history_clusters/core/ranking_cluster_finalizer.cc
index b040a052f6a..782f4385a49 100644
--- a/chromium/components/history_clusters/core/ranking_cluster_finalizer.cc
+++ b/chromium/components/history_clusters/core/ranking_cluster_finalizer.cc
@@ -132,7 +132,6 @@ void RankingClusterFinalizer::ComputeFinalVisitScores(
}
// Determine the max score to use for normalizing all the scores.
- auto visit_url = visit.normalized_url;
auto visit_scores_it =
url_visit_scores.find(visit.annotated_visit.visit_row.visit_id);
if (visit_scores_it != url_visit_scores.end()) {
diff --git a/chromium/components/history_clusters/core/similar_visit_deduper_cluster_finalizer_unittest.cc b/chromium/components/history_clusters/core/similar_visit_deduper_cluster_finalizer_unittest.cc
index 1fd06ac30f2..9b4dd074339 100644
--- a/chromium/components/history_clusters/core/similar_visit_deduper_cluster_finalizer_unittest.cc
+++ b/chromium/components/history_clusters/core/similar_visit_deduper_cluster_finalizer_unittest.cc
@@ -40,10 +40,12 @@ TEST_F(SimilarVisitDeduperClusterFinalizerTest, DedupeExactSimilarVisit) {
visit.annotated_visit.url_row.set_title(u"sametitle");
visit.annotated_visit.context_annotations.total_foreground_duration =
base::Seconds(20);
+ visit.url_for_display = u"someurl";
history::ClusterVisit canonical_visit = testing::CreateClusterVisit(
testing::CreateDefaultAnnotatedVisit(2, GURL("https://google.com/#abc")));
canonical_visit.annotated_visit.url_row.set_title(u"sametitle");
+ canonical_visit.url_for_display = u"someurl";
history::Cluster cluster;
cluster.visits = {visit, canonical_visit};
diff --git a/chromium/components/history_clusters/core/single_domain_cluster_finalizer.cc b/chromium/components/history_clusters/core/single_domain_cluster_finalizer.cc
new file mode 100644
index 00000000000..ecce944ca93
--- /dev/null
+++ b/chromium/components/history_clusters/core/single_domain_cluster_finalizer.cc
@@ -0,0 +1,38 @@
+// Copyright 2022 The Chromium 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/history_clusters/core/single_domain_cluster_finalizer.h"
+
+#include "components/history_clusters/core/cluster_metrics_utils.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+
+namespace history_clusters {
+
+namespace {
+
+bool IsSingleDomainCluster(const history::Cluster& cluster) {
+ for (size_t i = 1; i < cluster.visits.size(); i++) {
+ if (!net::registry_controlled_domains::SameDomainOrHost(
+ cluster.visits[0].normalized_url, cluster.visits[i].normalized_url,
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+SingleDomainClusterFinalizer::SingleDomainClusterFinalizer() = default;
+SingleDomainClusterFinalizer::~SingleDomainClusterFinalizer() = default;
+
+void SingleDomainClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
+ ScopedFilterClusterMetricsRecorder metrics_recorder("SingleDomain");
+ if (IsSingleDomainCluster(cluster)) {
+ cluster.should_show_on_prominent_ui_surfaces = false;
+ metrics_recorder.set_was_filtered(true);
+ }
+}
+
+} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/single_domain_cluster_finalizer.h b/chromium/components/history_clusters/core/single_domain_cluster_finalizer.h
new file mode 100644
index 00000000000..2f164d7e8d5
--- /dev/null
+++ b/chromium/components/history_clusters/core/single_domain_cluster_finalizer.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_HISTORY_CLUSTERS_CORE_SINGLE_DOMAIN_CLUSTER_FINALIZER_H_
+#define COMPONENTS_HISTORY_CLUSTERS_CORE_SINGLE_DOMAIN_CLUSTER_FINALIZER_H_
+
+#include "components/history_clusters/core/cluster_finalizer.h"
+
+namespace history_clusters {
+
+// A cluster finalizer that finalizes single-domain clusters by not showing
+// them.
+class SingleDomainClusterFinalizer : public ClusterFinalizer {
+ public:
+ SingleDomainClusterFinalizer();
+ ~SingleDomainClusterFinalizer() override;
+
+ // ClusterFinalizer:
+ void FinalizeCluster(history::Cluster& cluster) override;
+};
+
+} // namespace history_clusters
+
+#endif // COMPONENTS_HISTORY_CLUSTERS_CORE_SINGLE_DOMAIN_CLUSTER_FINALIZER_H_
diff --git a/chromium/components/history_clusters/core/single_domain_cluster_finalizer_unittest.cc b/chromium/components/history_clusters/core/single_domain_cluster_finalizer_unittest.cc
new file mode 100644
index 00000000000..b480be2e697
--- /dev/null
+++ b/chromium/components/history_clusters/core/single_domain_cluster_finalizer_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2022 The Chromium 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/history_clusters/core/single_domain_cluster_finalizer.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "components/history_clusters/core/clustering_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace history_clusters {
+namespace {
+
+class SingleDomainClusterFinalizerTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ cluster_finalizer_ = std::make_unique<SingleDomainClusterFinalizer>();
+ }
+
+ void TearDown() override { cluster_finalizer_.reset(); }
+
+ void FinalizeCluster(history::Cluster& cluster) {
+ cluster_finalizer_->FinalizeCluster(cluster);
+ }
+
+ private:
+ std::unique_ptr<SingleDomainClusterFinalizer> cluster_finalizer_;
+ base::test::TaskEnvironment task_environment_;
+};
+
+TEST_F(SingleDomainClusterFinalizerTest, OneDomainMultipleHosts) {
+ history::ClusterVisit visit_0 = testing::CreateClusterVisit(
+ testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
+ history::ClusterVisit visit_1 =
+ testing::CreateClusterVisit(testing::CreateDefaultAnnotatedVisit(
+ 2, GURL("https://mail.google.com/")));
+
+ history::Cluster cluster;
+ cluster.visits = {visit_0, visit_1};
+ FinalizeCluster(cluster);
+ EXPECT_FALSE(cluster.should_show_on_prominent_ui_surfaces);
+}
+
+TEST_F(SingleDomainClusterFinalizerTest, OneVisit) {
+ history::ClusterVisit visit_0 = testing::CreateClusterVisit(
+ testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
+
+ history::Cluster cluster;
+ cluster.visits = {visit_0};
+ cluster.should_show_on_prominent_ui_surfaces = true;
+ FinalizeCluster(cluster);
+ EXPECT_FALSE(cluster.should_show_on_prominent_ui_surfaces);
+}
+
+TEST_F(SingleDomainClusterFinalizerTest,
+ MultipleDomainsButExplicitlyNotShownBefore) {
+ base::HistogramTester histogram_tester;
+ history::ClusterVisit visit_0 = testing::CreateClusterVisit(
+ testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
+ history::ClusterVisit visit_1 = testing::CreateClusterVisit(
+ testing::CreateDefaultAnnotatedVisit(2, GURL("https://foo.com/")));
+
+ history::Cluster cluster;
+ cluster.visits = {visit_0, visit_1};
+ cluster.should_show_on_prominent_ui_surfaces = false;
+ FinalizeCluster(cluster);
+ EXPECT_FALSE(cluster.should_show_on_prominent_ui_surfaces);
+ histogram_tester.ExpectUniqueSample(
+ "History.Clusters.Backend.WasClusterFiltered.SingleDomain", false, 1);
+}
+
+TEST_F(SingleDomainClusterFinalizerTest, MultipleDomains) {
+ base::HistogramTester histogram_tester;
+ history::ClusterVisit visit_0 = testing::CreateClusterVisit(
+ testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
+ history::ClusterVisit visit_1 = testing::CreateClusterVisit(
+ testing::CreateDefaultAnnotatedVisit(2, GURL("https://foo.com/")));
+
+ history::Cluster cluster;
+ cluster.visits = {visit_0, visit_1};
+ FinalizeCluster(cluster);
+ EXPECT_TRUE(cluster.should_show_on_prominent_ui_surfaces);
+ histogram_tester.ExpectUniqueSample(
+ "History.Clusters.Backend.WasClusterFiltered.SingleDomain", false, 1);
+}
+
+} // namespace
+} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/url_deduper_cluster_finalizer.cc b/chromium/components/history_clusters/core/url_deduper_cluster_finalizer.cc
deleted file mode 100644
index bf47f0297af..00000000000
--- a/chromium/components/history_clusters/core/url_deduper_cluster_finalizer.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2021 The Chromium 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/history_clusters/core/url_deduper_cluster_finalizer.h"
-
-#include "base/ranges/algorithm.h"
-#include "components/history_clusters/core/on_device_clustering_util.h"
-
-namespace history_clusters {
-
-UrlDeduperClusterFinalizer::UrlDeduperClusterFinalizer() = default;
-UrlDeduperClusterFinalizer::~UrlDeduperClusterFinalizer() = default;
-
-void UrlDeduperClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
- base::flat_map<std::string, history::ClusterVisit*> url_to_canonical_visit;
- // First do a prepass to find the canonical visit for each URL. This simply
- // marks the last visit in `cluster` with any given URL as the canonical one.
- for (auto& visit : cluster.visits) {
- url_to_canonical_visit[visit.url_for_deduping.possibly_invalid_spec()] =
- &visit;
- }
-
- cluster.visits.erase(
- base::ranges::remove_if(
- cluster.visits,
- [&](auto& visit) {
- // We are guaranteed to find a matching canonical visit, due to our
- // prepass above.
- auto it = url_to_canonical_visit.find(
- visit.url_for_deduping.possibly_invalid_spec());
- DCHECK(it != url_to_canonical_visit.end());
- history::ClusterVisit* canonical_visit = it->second;
-
- // If a DIFFERENT visit is the canonical visit for this key, merge
- // this visit in, and mark this visit as to be removed.
- if (&visit != canonical_visit) {
- MergeDuplicateVisitIntoCanonicalVisit(std::move(visit),
- *canonical_visit);
- return true;
- }
-
- return false;
- }),
- cluster.visits.end());
-}
-
-} // namespace history_clusters
diff --git a/chromium/components/history_clusters/core/url_deduper_cluster_finalizer.h b/chromium/components/history_clusters/core/url_deduper_cluster_finalizer.h
deleted file mode 100644
index 47bfd68dc1d..00000000000
--- a/chromium/components/history_clusters/core/url_deduper_cluster_finalizer.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2021 The Chromium 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 COMPONENTS_HISTORY_CLUSTERS_CORE_URL_DEDUPER_CLUSTER_FINALIZER_H_
-#define COMPONENTS_HISTORY_CLUSTERS_CORE_URL_DEDUPER_CLUSTER_FINALIZER_H_
-
-#include "components/history_clusters/core/cluster_finalizer.h"
-
-namespace history_clusters {
-
-// A cluster finalizer that dedupes visits based on URL.
-class UrlDeduperClusterFinalizer : public ClusterFinalizer {
- public:
- UrlDeduperClusterFinalizer();
- ~UrlDeduperClusterFinalizer() override;
-
- // ClusterFinalizer:
- void FinalizeCluster(history::Cluster& cluster) override;
-};
-
-} // namespace history_clusters
-
-#endif // COMPONENTS_HISTORY_CLUSTERS_CORE_URL_DEDUPER_CLUSTER_FINALIZER_H_
diff --git a/chromium/components/history_clusters/core/url_deduper_cluster_finalizer_unittest.cc b/chromium/components/history_clusters/core/url_deduper_cluster_finalizer_unittest.cc
deleted file mode 100644
index 51414d22f3f..00000000000
--- a/chromium/components/history_clusters/core/url_deduper_cluster_finalizer_unittest.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2021 The Chromium 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/history_clusters/core/url_deduper_cluster_finalizer.h"
-
-#include "base/test/task_environment.h"
-#include "components/history_clusters/core/clustering_test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace history_clusters {
-namespace {
-
-using ::testing::ElementsAre;
-using ::testing::UnorderedElementsAre;
-
-class UrlDeduperClusterFinalizerTest : public ::testing::Test {
- public:
- void SetUp() override {
- cluster_finalizer_ = std::make_unique<UrlDeduperClusterFinalizer>();
- }
-
- void TearDown() override { cluster_finalizer_.reset(); }
-
- void FinalizeCluster(history::Cluster& cluster) {
- cluster_finalizer_->FinalizeCluster(cluster);
- }
-
- private:
- std::unique_ptr<UrlDeduperClusterFinalizer> cluster_finalizer_;
- base::test::TaskEnvironment task_environment_;
-};
-
-TEST_F(UrlDeduperClusterFinalizerTest, DedupeExactURL) {
- // canonical_visit has the same URL as Visit1.
- history::ClusterVisit visit = testing::CreateClusterVisit(
- testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
- visit.annotated_visit.context_annotations.total_foreground_duration =
- base::Seconds(20);
-
- history::ClusterVisit canonical_visit = testing::CreateClusterVisit(
- testing::CreateDefaultAnnotatedVisit(2, GURL("https://google.com/")));
-
- history::Cluster cluster;
- cluster.visits = {visit, canonical_visit};
- FinalizeCluster(cluster);
- EXPECT_THAT(testing::ToVisitResults({cluster}),
- ElementsAre(ElementsAre(testing::VisitResult(
- 2, 1.0, {testing::VisitResult(1, 1.0)}))));
- const auto& actual_canonical_visit = cluster.visits.at(0);
- // Make sure total foreground duration is updated correctly even if some don't
- // have the field populated.
- EXPECT_EQ(actual_canonical_visit.annotated_visit.context_annotations
- .total_foreground_duration,
- base::Seconds(20));
-}
-
-TEST_F(UrlDeduperClusterFinalizerTest, DedupeRespectsDifferentURLs) {
- history::ClusterVisit visit = testing::CreateClusterVisit(
- testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
-
- history::ClusterVisit canonical_visit = testing::CreateClusterVisit(
- testing::CreateDefaultAnnotatedVisit(2, GURL("https://foo.com/")));
-
- history::Cluster cluster;
- cluster.visits = {visit, canonical_visit};
- FinalizeCluster(cluster);
- EXPECT_THAT(testing::ToVisitResults({cluster}),
- ElementsAre(ElementsAre(testing::VisitResult(1, 1.0),
- testing::VisitResult(2, 1.0))));
-}
-
-TEST_F(UrlDeduperClusterFinalizerTest, DedupeNormalizedUrl) {
- // canonical_visit has the same normalized URL as Visit1.
- history::ClusterVisit visit = testing::CreateClusterVisit(
- testing::CreateDefaultAnnotatedVisit(
- 1, GURL("https://example.com/normalized?q=whatever")),
- GURL("https://example.com/normalized"));
-
- history::ClusterVisit canonical_visit =
- testing::CreateClusterVisit(testing::CreateDefaultAnnotatedVisit(
- 2, GURL("https://example.com/normalized")));
-
- history::Cluster cluster;
- cluster.visits = {visit, canonical_visit};
- FinalizeCluster(cluster);
- EXPECT_THAT(testing::ToVisitResults({cluster}),
- ElementsAre(ElementsAre(testing::VisitResult(
- 2, 1.0, {testing::VisitResult(1, 1.0)}))));
- const auto& actual_canonical_visit = cluster.visits.at(0);
- // Make sure total foreground duration not updated if none of the visits have
- // it populated.
- EXPECT_EQ(actual_canonical_visit.annotated_visit.context_annotations
- .total_foreground_duration,
- base::Seconds(-1));
-}
-
-TEST_F(UrlDeduperClusterFinalizerTest, MergesAnnotations) {
- // canonical_visit has the same normalized URL as duplicated_visit.
- history::ClusterVisit duplicate_visit = testing::CreateClusterVisit(
- testing::CreateDefaultAnnotatedVisit(
- 1, GURL("https://example.com/normalized?q=whatever")),
- GURL("https://example.com/normalized"));
- duplicate_visit.annotated_visit.content_annotations.related_searches = {
- "xyz"};
- duplicate_visit.annotated_visit.context_annotations.omnibox_url_copied = true;
- duplicate_visit.annotated_visit.context_annotations.is_existing_bookmark =
- true;
- duplicate_visit.annotated_visit.context_annotations
- .is_existing_part_of_tab_group = true;
- duplicate_visit.annotated_visit.context_annotations.is_new_bookmark = true;
- duplicate_visit.annotated_visit.context_annotations.is_placed_in_tab_group =
- true;
- duplicate_visit.annotated_visit.context_annotations.is_ntp_custom_link = true;
- duplicate_visit.annotated_visit.context_annotations
- .total_foreground_duration = base::Seconds(20);
-
- history::ClusterVisit canonical_visit =
- testing::CreateClusterVisit(testing::CreateDefaultAnnotatedVisit(
- 2, GURL("https://example.com/normalized")));
- canonical_visit.annotated_visit.content_annotations.related_searches = {
- "abc", "xyz"};
- canonical_visit.annotated_visit.context_annotations.omnibox_url_copied =
- false;
- canonical_visit.annotated_visit.context_annotations.is_existing_bookmark =
- false;
- canonical_visit.annotated_visit.context_annotations
- .is_existing_part_of_tab_group = false;
- canonical_visit.annotated_visit.context_annotations.is_new_bookmark = false;
- canonical_visit.annotated_visit.context_annotations.is_placed_in_tab_group =
- false;
- canonical_visit.annotated_visit.context_annotations.is_ntp_custom_link =
- false;
- canonical_visit.annotated_visit.context_annotations
- .total_foreground_duration = base::Seconds(20);
-
- history::Cluster cluster;
- cluster.visits = {duplicate_visit, canonical_visit};
- FinalizeCluster(cluster);
- EXPECT_THAT(testing::ToVisitResults({cluster}),
- ElementsAre(ElementsAre(testing::VisitResult(
- 2, 1.0, {testing::VisitResult(1, 1.0)}))));
- const auto& actual_canonical_visit = cluster.visits.at(0);
- EXPECT_TRUE(actual_canonical_visit.annotated_visit.context_annotations
- .omnibox_url_copied);
- EXPECT_TRUE(actual_canonical_visit.annotated_visit.context_annotations
- .is_existing_bookmark);
- EXPECT_TRUE(actual_canonical_visit.annotated_visit.context_annotations
- .is_existing_part_of_tab_group);
- EXPECT_TRUE(actual_canonical_visit.annotated_visit.context_annotations
- .is_new_bookmark);
- EXPECT_TRUE(actual_canonical_visit.annotated_visit.context_annotations
- .is_placed_in_tab_group);
- EXPECT_TRUE(actual_canonical_visit.annotated_visit.context_annotations
- .is_ntp_custom_link);
- EXPECT_THAT(actual_canonical_visit.annotated_visit.content_annotations
- .related_searches,
- UnorderedElementsAre("abc", "xyz"));
- EXPECT_EQ(actual_canonical_visit.annotated_visit.visit_row.visit_duration,
- base::Seconds(10 * 2));
- EXPECT_EQ(actual_canonical_visit.annotated_visit.context_annotations
- .total_foreground_duration,
- base::Seconds(20 * 2));
-}
-
-} // namespace
-} // namespace history_clusters
diff --git a/chromium/components/history_clusters/history_clusters_internals/resources/BUILD.gn b/chromium/components/history_clusters/history_clusters_internals/resources/BUILD.gn
index 21f2b76e269..195b4cad6ed 100644
--- a/chromium/components/history_clusters/history_clusters_internals/resources/BUILD.gn
+++ b/chromium/components/history_clusters/history_clusters_internals/resources/BUILD.gn
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import("//tools/grit/grit_rule.gni")
-import("//tools/polymer/html_to_js.gni")
import("//tools/typescript/ts_library.gni")
import("//ui/webui/resources/tools/generate_grd.gni")
@@ -34,12 +33,11 @@ generate_grd("build_grd") {
input_files_base_dir = rebase_path(".", "//")
}
-html_to_js("web_components") {
- js_files = [ "history_clusters_internals.ts" ]
-}
-
-copy("copy_proxy") {
- sources = [ "history_clusters_internals_browser_proxy.ts" ]
+copy("copy_ts") {
+ sources = [
+ "history_clusters_internals.ts",
+ "history_clusters_internals_browser_proxy.ts",
+ ]
outputs = [ "$target_gen_dir/{{source_file_part}}" ]
}
@@ -59,13 +57,9 @@ ts_library("build_ts") {
"history_clusters_internals_browser_proxy.ts",
"history_clusters_internals.mojom-webui.js",
]
- deps = [
- "//ui/webui/resources:library",
- "//ui/webui/resources/js/browser_command:build_ts",
- ]
+ deps = [ "//ui/webui/resources:library" ]
extra_deps = [
":copy_mojo",
- ":copy_proxy",
- ":web_components",
+ ":copy_ts",
]
}
diff --git a/chromium/components/history_clusters_strings.grdp b/chromium/components/history_clusters_strings.grdp
index 50c1aba8d62..76deb682112 100644
--- a/chromium/components/history_clusters_strings.grdp
+++ b/chromium/components/history_clusters_strings.grdp
@@ -3,13 +3,16 @@
<!-- Strings for the chrome://history/journeys page -->
<grit-part>
+ <message name="IDS_HISTORY_CLUSTERS_ACTION_MENU_DESCRIPTION" desc="Text used to identify the history clusters drop-down menu for screen readers">
+ Actions
+ </message>
<message name="IDS_HISTORY_CLUSTERS_DISABLE_MENU_ITEM_LABEL" desc="A label for the menu item to turn off the Journeys feature, which collects different search and browsing activities related to a single topic into a single place, and organizes them by topic. See glossary entry for meaning of 'Journeys'.">
Turn off Journeys
</message>
<message name="IDS_HISTORY_CLUSTERS_ENABLE_MENU_ITEM_LABEL" desc="A label for the menu item to turn on the Journeys feature, which collects different search and browsing activities related to a single topic into a single place, and organizes them by topic. See glossary entry for meaning of 'Journeys'.">
Turn on Journeys
</message>
- <message name="IDS_HISTORY_CLUSTERS_JOURNEYS_TAB_LABEL" desc="A label for the section in Chrome History (chrome://history) where Journeys are collected and shown to the user. See glossary entry for meaning of 'Journeys'.">
+ <message name="IDS_HISTORY_CLUSTERS_JOURNEYS_TAB_LABEL" desc="A label for the section in Chrome History (chrome://history) where Journeys are collected and shown to the user. See glossary entry for meaning of 'Journeys'." formatter_data="android_java">
Journeys
</message>
<message name="IDS_HISTORY_CLUSTERS_LIST_TAB_LABEL" desc="A label for the section in Chrome History (chrome://history) where a list of history items are shown to the user.">
@@ -45,4 +48,7 @@
<message name="IDS_HISTORY_CLUSTERS_SHOW_MORE_BUTTON_LABEL" desc="A label for the button that expands the view showing more history items.">
Show more
</message>
+ <message name="IDS_HISTORY_CLUSTERS_SEARCH_YOUR_JOURNEYS" desc="A label for the hint text for the search box in Chrome Journeys." formatter_data="android_java">
+ Search your Journeys
+ </message>
</grit-part>
diff --git a/chromium/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_ACTION_MENU_DESCRIPTION.png.sha1 b/chromium/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_ACTION_MENU_DESCRIPTION.png.sha1
new file mode 100644
index 00000000000..3fd68031076
--- /dev/null
+++ b/chromium/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_ACTION_MENU_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+6f8b0bde3c35bec7f3f72c057324854651bdfcd6 \ No newline at end of file
diff --git a/chromium/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_SEARCH_YOUR_JOURNEYS.png.sha1 b/chromium/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_SEARCH_YOUR_JOURNEYS.png.sha1
new file mode 100644
index 00000000000..82278fa1447
--- /dev/null
+++ b/chromium/components/history_clusters_strings_grdp/IDS_HISTORY_CLUSTERS_SEARCH_YOUR_JOURNEYS.png.sha1
@@ -0,0 +1 @@
+e68313f5e34b7db95decd346ee5660595f4311fa \ No newline at end of file
diff --git a/chromium/components/image_fetcher/BUILD.gn b/chromium/components/image_fetcher/BUILD.gn
index a2927f56be3..a2aadfb5069 100644
--- a/chromium/components/image_fetcher/BUILD.gn
+++ b/chromium/components/image_fetcher/BUILD.gn
@@ -15,6 +15,8 @@ android_library("java") {
]
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/util/android:java",
"//components/embedder_support/android:simple_factory_key_java",
"//content/public/android:content_main_dex_java",
@@ -43,6 +45,7 @@ source_set("android") {
"//components/embedder_support/android:simple_factory_key",
"//components/image_fetcher/core",
"//components/image_fetcher/core:metrics",
+ "//services/data_decoder/public/cpp",
"//ui/gfx",
]
}
@@ -62,6 +65,7 @@ java_library("junit") {
":java",
"//base:base_java",
"//base:base_junit_test_support",
+ "//base:jni_java",
"//components/browser_ui/util/android:java",
"//components/embedder_support/android:simple_factory_key_java",
"//third_party/android_deps:robolectric_all_java",
diff --git a/chromium/components/image_fetcher/DEPS b/chromium/components/image_fetcher/DEPS
index 292ba5bebb8..cab5ae84ed8 100644
--- a/chromium/components/image_fetcher/DEPS
+++ b/chromium/components/image_fetcher/DEPS
@@ -3,6 +3,7 @@ include_rules = [
"+components/embedder_support/android",
"+content/public/android",
"+net",
+ "+services/data_decoder/public/cpp",
"+services/network/public/cpp",
"+services/network/public/mojom",
"+services/network/test",
diff --git a/chromium/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherBridgeTest.java b/chromium/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherBridgeTest.java
index a817ecfb20a..aef2a7f499f 100644
--- a/chromium/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherBridgeTest.java
+++ b/chromium/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherBridgeTest.java
@@ -72,8 +72,8 @@ public class ImageFetcherBridgeTest {
return null;
})
.when(mNatives)
- .fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(), eq(0),
- callbackCaptor.capture());
+ .fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(),
+ eq(WIDTH_PX), eq(HEIGHT_PX), eq(0), callbackCaptor.capture());
mBridge.fetchImage(
-1, ImageFetcher.Params.create("", "", WIDTH_PX, HEIGHT_PX), mBitmapCallback);
@@ -90,7 +90,8 @@ public class ImageFetcherBridgeTest {
})
.when(mNatives)
.fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(),
- eq(EXPIRATION_INTERVAL_MINS), callbackCaptor.capture());
+ eq(WIDTH_PX), eq(HEIGHT_PX), eq(EXPIRATION_INTERVAL_MINS),
+ callbackCaptor.capture());
mBridge.fetchImage(-1,
ImageFetcher.Params.createWithExpirationInterval(
@@ -102,6 +103,9 @@ public class ImageFetcherBridgeTest {
@Test
public void testFetchImage_imageResized() {
+ int desiredWidth = 100;
+ int desiredHeight = 100;
+
ArgumentCaptor<Callback<Bitmap>> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
final Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
doAnswer((InvocationOnMock invocation) -> {
@@ -109,10 +113,11 @@ public class ImageFetcherBridgeTest {
return null;
})
.when(mNatives)
- .fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(), eq(0),
- callbackCaptor.capture());
+ .fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(),
+ eq(desiredWidth), eq(desiredHeight), eq(0), callbackCaptor.capture());
- mBridge.fetchImage(-1, ImageFetcher.Params.create("", "", 100, 100), mBitmapCallback);
+ mBridge.fetchImage(-1, ImageFetcher.Params.create("", "", desiredWidth, desiredHeight),
+ mBitmapCallback);
ArgumentCaptor<Bitmap> bitmapCaptor = ArgumentCaptor.forClass(Bitmap.class);
verify(mBitmapCallback).onResult(bitmapCaptor.capture());
@@ -187,4 +192,4 @@ public class ImageFetcherBridgeTest {
Assert.assertNotEquals(
mBridge, ImageFetcherBridge.getForSimpleFactoryKeyHandle(mSimpleFactoryKeyHandle));
}
-} \ No newline at end of file
+}
diff --git a/chromium/components/image_fetcher/core/BUILD.gn b/chromium/components/image_fetcher/core/BUILD.gn
index 9bb332fb2e9..54a41ebbbc0 100644
--- a/chromium/components/image_fetcher/core/BUILD.gn
+++ b/chromium/components/image_fetcher/core/BUILD.gn
@@ -10,6 +10,8 @@ static_library("core") {
sources = [
"cached_image_fetcher.cc",
"cached_image_fetcher.h",
+ "features.cc",
+ "features.h",
"image_data_fetcher.cc",
"image_data_fetcher.h",
"image_decoder.h",
diff --git a/chromium/components/image_fetcher/core/cached_image_fetcher.cc b/chromium/components/image_fetcher/core/cached_image_fetcher.cc
index 9663a97e8c0..af001fdfaad 100644
--- a/chromium/components/image_fetcher/core/cached_image_fetcher.cc
+++ b/chromium/components/image_fetcher/core/cached_image_fetcher.cc
@@ -150,8 +150,9 @@ void CachedImageFetcher::OnImageFetchedFromCache(
if (!image_callback.is_null() ||
(cache_result_needs_transcoding &&
!request.params.allow_needs_transcoding_file())) {
+ auto* data_decoder = request.params.data_decoder();
GetImageDecoder()->DecodeImage(
- image_data, gfx::Size(),
+ image_data, gfx::Size(), data_decoder,
base::BindOnce(&CachedImageFetcher::OnImageDecodedFromCache,
weak_ptr_factory_.GetWeakPtr(), std::move(request),
ImageDataFetcherCallback(), std::move(image_callback),
diff --git a/chromium/components/image_fetcher/core/fake_image_decoder.cc b/chromium/components/image_fetcher/core/fake_image_decoder.cc
index cbba00c9c86..322b5890119 100644
--- a/chromium/components/image_fetcher/core/fake_image_decoder.cc
+++ b/chromium/components/image_fetcher/core/fake_image_decoder.cc
@@ -25,6 +25,7 @@ FakeImageDecoder::~FakeImageDecoder() = default;
void FakeImageDecoder::DecodeImage(
const std::string& image_data,
const gfx::Size& desired_image_frame_size,
+ data_decoder::DataDecoder* data_decoder,
image_fetcher::ImageDecodedCallback callback) {
ASSERT_TRUE(enabled_);
gfx::Image image;
diff --git a/chromium/components/image_fetcher/core/fake_image_decoder.h b/chromium/components/image_fetcher/core/fake_image_decoder.h
index 15cf63ed25c..0ac5d8b87de 100644
--- a/chromium/components/image_fetcher/core/fake_image_decoder.h
+++ b/chromium/components/image_fetcher/core/fake_image_decoder.h
@@ -26,6 +26,7 @@ class FakeImageDecoder : public image_fetcher::ImageDecoder {
void DecodeImage(const std::string& image_data,
const gfx::Size& desired_image_frame_size,
+ data_decoder::DataDecoder* data_decoder,
image_fetcher::ImageDecodedCallback callback) override;
void SetEnabled(bool enabled);
void SetBeforeImageDecoded(const base::RepeatingClosure& callback);
diff --git a/chromium/components/image_fetcher/core/features.cc b/chromium/components/image_fetcher/core/features.cc
new file mode 100644
index 00000000000..b05df4cb93b
--- /dev/null
+++ b/chromium/components/image_fetcher/core/features.cc
@@ -0,0 +1,17 @@
+// Copyright 2022 The Chromium 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/image_fetcher/core/features.h"
+
+namespace image_fetcher {
+namespace features {
+
+#if BUILDFLAG(IS_ANDROID)
+// Enables batching decoding of related images in a single process.
+const base::Feature kBatchImageDecoding{"BatchImageDecoding",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
+} // namespace features
+} // namespace image_fetcher
diff --git a/chromium/components/image_fetcher/core/features.h b/chromium/components/image_fetcher/core/features.h
new file mode 100644
index 00000000000..7d13a44564b
--- /dev/null
+++ b/chromium/components/image_fetcher/core/features.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_IMAGE_FETCHER_CORE_FEATURES_H_
+#define COMPONENTS_IMAGE_FETCHER_CORE_FEATURES_H_
+
+#include "base/feature_list.h"
+#include "build/build_config.h"
+
+namespace image_fetcher {
+namespace features {
+
+#if BUILDFLAG(IS_ANDROID)
+extern const base::Feature kBatchImageDecoding;
+#endif
+
+} // namespace features
+} // namespace image_fetcher
+
+#endif // COMPONENTS_IMAGE_FETCHER_CORE_FEATURES_H_
diff --git a/chromium/components/image_fetcher/core/image_decoder.h b/chromium/components/image_fetcher/core/image_decoder.h
index 938b33dd188..57c25747d28 100644
--- a/chromium/components/image_fetcher/core/image_decoder.h
+++ b/chromium/components/image_fetcher/core/image_decoder.h
@@ -9,6 +9,10 @@
#include "base/callback_forward.h"
+namespace data_decoder {
+class DataDecoder;
+} // namespace data_decoder
+
namespace gfx {
class Image;
class Size;
@@ -37,9 +41,13 @@ class ImageDecoder {
// close as possible to |desired_image_frame_size| is chosen (tries to take
// one in larger size if there's no precise match). Passing gfx::Size() as
// |desired_image_frame_size| is also supported and will result in chosing the
- // smallest available size.
+ // smallest available size. Pass |data_decoder| to batch multiple image
+ // decodes in the same process. If |data_decoder| is null, a new process will
+ // be created to decode this image. |data_decoder| must outlive the
+ // ImageDecoder.
virtual void DecodeImage(const std::string& image_data,
const gfx::Size& desired_image_frame_size,
+ data_decoder::DataDecoder* data_decoder,
ImageDecodedCallback callback) = 0;
};
diff --git a/chromium/components/image_fetcher/core/image_fetcher.h b/chromium/components/image_fetcher/core/image_fetcher.h
index 2a56152a0a3..cc3a41a430b 100644
--- a/chromium/components/image_fetcher/core/image_fetcher.h
+++ b/chromium/components/image_fetcher/core/image_fetcher.h
@@ -9,6 +9,7 @@
#include <utility>
#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "components/image_fetcher/core/image_fetcher_types.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
@@ -16,6 +17,10 @@
#include "ui/gfx/geometry/size.h"
#include "url/gurl.h"
+namespace data_decoder {
+class DataDecoder;
+} // namespace data_decoder
+
namespace image_fetcher {
class ImageDecoder;
@@ -97,6 +102,15 @@ class ImageFetcherParams {
expiration_interval_ = expiration_interval;
}
+ // Sets the data decoder to use for this image. Using the same data decoder
+ // across multiple image fetches allows them to be decoded in the same
+ // process.
+ void set_data_decoder(data_decoder::DataDecoder* data_decoder) {
+ data_decoder_ = data_decoder;
+ }
+
+ data_decoder::DataDecoder* data_decoder() const { return data_decoder_; }
+
private:
void set_skip_transcoding(bool skip_transcoding) {
skip_transcoding_ = skip_transcoding;
@@ -126,6 +140,10 @@ class ImageFetcherParams {
// True if allowing images that need transcoding to be stored with a prefix in
// file names.
bool allow_needs_transcoding_file_;
+
+ // The data decoder to use for decoding this image. If null, a new data
+ // decoder will be created for each fetch.
+ raw_ptr<data_decoder::DataDecoder> data_decoder_ = nullptr;
};
// A class used to fetch server images. It can be called from any thread and the
diff --git a/chromium/components/image_fetcher/core/image_fetcher_impl.cc b/chromium/components/image_fetcher/core/image_fetcher_impl.cc
index 9e80eeceaf7..a21e9c86c10 100644
--- a/chromium/components/image_fetcher/core/image_fetcher_impl.cc
+++ b/chromium/components/image_fetcher/core/image_fetcher_impl.cc
@@ -103,7 +103,7 @@ void ImageFetcherImpl::OnImageURLFetched(const GURL& image_url,
request->image_data = image_data;
request->request_metadata = metadata;
image_decoder_->DecodeImage(
- image_data, params.frame_size(),
+ image_data, params.frame_size(), params.data_decoder(),
base::BindOnce(&ImageFetcherImpl::OnImageDecoded,
weak_ptr_factory_.GetWeakPtr(), image_url, metadata));
}
diff --git a/chromium/components/image_fetcher/core/image_fetcher_metrics_reporter.cc b/chromium/components/image_fetcher/core/image_fetcher_metrics_reporter.cc
index a54a0da856e..dd2842205f8 100644
--- a/chromium/components/image_fetcher/core/image_fetcher_metrics_reporter.cc
+++ b/chromium/components/image_fetcher/core/image_fetcher_metrics_reporter.cc
@@ -8,6 +8,7 @@
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
+#include "base/numerics/clamped_math.h"
namespace image_fetcher {
diff --git a/chromium/components/image_fetcher/core/mock_image_decoder.h b/chromium/components/image_fetcher/core/mock_image_decoder.h
index 98e3c9b5cf4..cbbb2220a38 100644
--- a/chromium/components/image_fetcher/core/mock_image_decoder.h
+++ b/chromium/components/image_fetcher/core/mock_image_decoder.h
@@ -15,9 +15,10 @@ class MockImageDecoder : public image_fetcher::ImageDecoder {
public:
MockImageDecoder();
~MockImageDecoder() override;
- MOCK_METHOD3(DecodeImage,
+ MOCK_METHOD4(DecodeImage,
void(const std::string& image_data,
const gfx::Size& desired_image_frame_size,
+ data_decoder::DataDecoder* data_decoder,
image_fetcher::ImageDecodedCallback callback));
};
diff --git a/chromium/components/image_fetcher/image_fetcher_bridge.cc b/chromium/components/image_fetcher/image_fetcher_bridge.cc
index b75bdf4eb58..b84f200bb3f 100644
--- a/chromium/components/image_fetcher/image_fetcher_bridge.cc
+++ b/chromium/components/image_fetcher/image_fetcher_bridge.cc
@@ -12,14 +12,18 @@
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
+#include "base/feature_list.h"
#include "base/files/file_path.h"
+#include "base/no_destructor.h"
#include "components/embedder_support/android/simple_factory_key/simple_factory_key_handle.h"
#include "components/image_fetcher/core/cache/image_cache.h"
+#include "components/image_fetcher/core/features.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/image_fetcher/core/image_fetcher_metrics_reporter.h"
#include "components/image_fetcher/core/image_fetcher_service.h"
#include "components/image_fetcher/image_fetcher_service_provider.h"
#include "components/image_fetcher/jni_headers/ImageFetcherBridge_jni.h"
+#include "services/data_decoder/public/cpp/data_decoder.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/image/image.h"
@@ -57,6 +61,11 @@ constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
"images and thus there is no Chrome-wide policy to disable it."
})");
+data_decoder::DataDecoder* GetDataDecoder() {
+ static base::NoDestructor<data_decoder::DataDecoder> data_decoder;
+ return data_decoder.get();
+}
+
} // namespace
ImageFetcherBridge::~ImageFetcherBridge() = default;
@@ -97,6 +106,8 @@ void ImageFetcherBridge::FetchImageData(
params.set_hold_for_expiration_interval(
base::Minutes(j_expiration_interval_mins));
}
+ if (base::FeatureList::IsEnabled(features::kBatchImageDecoding))
+ params.set_data_decoder(GetDataDecoder());
// We can skip transcoding here because this method is used in java as
// ImageFetcher.fetchGif, which decodes the data in a Java-only library.
@@ -118,6 +129,8 @@ void ImageFetcherBridge::FetchImage(
const jint j_image_fetcher_config,
const JavaParamRef<jstring>& j_url,
const JavaParamRef<jstring>& j_client_name,
+ const jint j_frame_width,
+ const jint j_frame_height,
const jint j_expiration_interval_mins,
const JavaParamRef<jobject>& j_callback) {
ScopedJavaGlobalRef<jobject> callback(j_callback);
@@ -128,10 +141,13 @@ void ImageFetcherBridge::FetchImage(
base::android::ConvertJavaStringToUTF8(j_client_name);
ImageFetcherParams params(kTrafficAnnotation, client_name);
+ params.set_frame_size(gfx::Size(j_frame_width, j_frame_height));
if (j_expiration_interval_mins > 0) {
params.set_hold_for_expiration_interval(
base::Minutes(j_expiration_interval_mins));
}
+ if (base::FeatureList::IsEnabled(features::kBatchImageDecoding))
+ params.set_data_decoder(GetDataDecoder());
SimpleFactoryKey* key =
simple_factory_key::SimpleFactoryKeyFromJavaHandle(j_simple_factory_key);
@@ -208,11 +224,13 @@ void JNI_ImageFetcherBridge_FetchImage(
const jint j_image_fetcher_config,
const JavaParamRef<jstring>& j_url,
const JavaParamRef<jstring>& j_client_name,
+ const jint j_frame_width,
+ const jint j_frame_height,
const jint j_expiration_interval_mins,
const JavaParamRef<jobject>& j_callback) {
- ImageFetcherBridge::FetchImage(j_env, j_simple_factory_key,
- j_image_fetcher_config, j_url, j_client_name,
- j_expiration_interval_mins, j_callback);
+ ImageFetcherBridge::FetchImage(
+ j_env, j_simple_factory_key, j_image_fetcher_config, j_url, j_client_name,
+ j_frame_width, j_frame_height, j_expiration_interval_mins, j_callback);
}
// static
@@ -267,4 +285,4 @@ void ImageFetcherBridge::OnImageFetched(
RunObjectCallbackAndroid(callback, j_bitmap);
}
-} // namespace image_fetcher \ No newline at end of file
+} // namespace image_fetcher
diff --git a/chromium/components/image_fetcher/image_fetcher_bridge.h b/chromium/components/image_fetcher/image_fetcher_bridge.h
index 11657d45dff..786ccba144b 100644
--- a/chromium/components/image_fetcher/image_fetcher_bridge.h
+++ b/chromium/components/image_fetcher/image_fetcher_bridge.h
@@ -45,6 +45,8 @@ class ImageFetcherBridge {
const jint j_image_fetcher_config,
const base::android::JavaParamRef<jstring>& j_url,
const base::android::JavaParamRef<jstring>& j_client_name,
+ const jint j_frame_width,
+ const jint j_frame_height,
const jint j_expiration_interval_mins,
const base::android::JavaParamRef<jobject>& j_callback);
diff --git a/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm b/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm
index 1ceaf7035a7..7f770c401ee 100644
--- a/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm
+++ b/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm
@@ -37,6 +37,7 @@ class IOSImageDecoderImpl : public ImageDecoder {
// (http://crbug/697596).
void DecodeImage(const std::string& image_data,
const gfx::Size& desired_image_frame_size,
+ data_decoder::DataDecoder* data_decoder,
ImageDecodedCallback callback) override;
private:
@@ -62,6 +63,7 @@ IOSImageDecoderImpl::~IOSImageDecoderImpl() {}
void IOSImageDecoderImpl::DecodeImage(const std::string& image_data,
const gfx::Size& desired_image_frame_size,
+ data_decoder::DataDecoder* data_decoder,
ImageDecodedCallback callback) {
// Convert the |image_data| std::string to an NSData buffer.
// The data is copied as it may have to outlive the caller in
diff --git a/chromium/components/image_fetcher/ios/ios_image_decoder_impl_unittest.mm b/chromium/components/image_fetcher/ios/ios_image_decoder_impl_unittest.mm
index 8ce98072166..50a2d3de8a5 100644
--- a/chromium/components/image_fetcher/ios/ios_image_decoder_impl_unittest.mm
+++ b/chromium/components/image_fetcher/ios/ios_image_decoder_impl_unittest.mm
@@ -76,7 +76,7 @@ TEST_F(IOSImageDecoderImplTest, JPGImage) {
std::string image_data =
std::string(reinterpret_cast<char*>(kJPGImage), sizeof(kJPGImage));
ios_image_decoder_impl_->DecodeImage(
- image_data, gfx::Size(),
+ image_data, gfx::Size(), /*data_decoder=*/nullptr,
base::BindOnce(&IOSImageDecoderImplTest::OnImageDecoded,
base::Unretained(this)));
@@ -91,7 +91,7 @@ TEST_F(IOSImageDecoderImplTest, WebpImage) {
std::string image_data =
std::string(reinterpret_cast<char*>(kWEBPImage), sizeof(kWEBPImage));
ios_image_decoder_impl_->DecodeImage(
- image_data, gfx::Size(),
+ image_data, gfx::Size(), /*data_decoder=*/nullptr,
base::BindOnce(&IOSImageDecoderImplTest::OnImageDecoded,
base::Unretained(this)));
diff --git a/chromium/components/infobars/android/BUILD.gn b/chromium/components/infobars/android/BUILD.gn
index ce350d7f526..b8e7d4d7dc3 100644
--- a/chromium/components/infobars/android/BUILD.gn
+++ b/chromium/components/infobars/android/BUILD.gn
@@ -70,6 +70,8 @@ android_library("java") {
":infobar_android_enums_java",
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/styles/android:java",
"//components/browser_ui/widget/android:java",
"//components/infobars/core:infobar_enums_java",
@@ -107,13 +109,11 @@ android_library("javatests") {
]
deps = [
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//components/browser_ui/theme/android:java_resources",
"//components/browser_ui/widget/android:java",
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit:junit",
diff --git a/chromium/components/installedapp/android/BUILD.gn b/chromium/components/installedapp/android/BUILD.gn
index 66b9f366d4f..4203160b51f 100644
--- a/chromium/components/installedapp/android/BUILD.gn
+++ b/chromium/components/installedapp/android/BUILD.gn
@@ -25,6 +25,8 @@ android_library("java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/webapk/android/libs/client:java",
"//content/public/android:content_java",
"//mojo/public/java:system_java",
@@ -69,7 +71,6 @@ java_library("junit") {
[ "java/src/org/chromium/components/installedapp/PackageHashTest.java" ]
deps = [
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//base/test:test_support_java",
diff --git a/chromium/components/invalidation/impl/BUILD.gn b/chromium/components/invalidation/impl/BUILD.gn
index 84037aae4e9..7c1923b17f9 100644
--- a/chromium/components/invalidation/impl/BUILD.gn
+++ b/chromium/components/invalidation/impl/BUILD.gn
@@ -19,6 +19,8 @@ static_library("impl") {
sources = [
"channels_states.cc",
"channels_states.h",
+ "fake_ack_handler.cc",
+ "fake_ack_handler.h",
"fcm_invalidation_listener.cc",
"fcm_invalidation_listener.h",
"fcm_invalidation_service.cc",
@@ -38,8 +40,6 @@ static_library("impl") {
"invalidation_service_util.h",
"invalidator_registrar_with_memory.cc",
"invalidator_registrar_with_memory.h",
- "mock_ack_handler.cc",
- "mock_ack_handler.h",
"per_user_topic_subscription_manager.cc",
"per_user_topic_subscription_manager.h",
"per_user_topic_subscription_request.cc",
diff --git a/chromium/components/javascript_dialogs/android/BUILD.gn b/chromium/components/javascript_dialogs/android/BUILD.gn
index a1baa9e778a..bd39d1beec4 100644
--- a/chromium/components/javascript_dialogs/android/BUILD.gn
+++ b/chromium/components/javascript_dialogs/android/BUILD.gn
@@ -21,6 +21,8 @@ android_library("java") {
":java_resources",
":jni_headers",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/styles/android:java",
"//components/browser_ui/widget/android:java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/components/javascript_dialogs/app_modal_dialog_manager.cc b/chromium/components/javascript_dialogs/app_modal_dialog_manager.cc
index 78c50b449b8..0f73eb2e20b 100644
--- a/chromium/components/javascript_dialogs/app_modal_dialog_manager.cc
+++ b/chromium/components/javascript_dialogs/app_modal_dialog_manager.cc
@@ -85,8 +85,9 @@ std::u16string AppModalDialogManager::GetTitle(
return base::UTF8ToUTF16(name);
// Otherwise, return the formatted URL.
- return GetTitleImpl(web_contents->GetMainFrame()->GetLastCommittedOrigin(),
- alerting_frame_origin);
+ return GetTitleImpl(
+ web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(),
+ alerting_frame_origin);
}
namespace {
diff --git a/chromium/components/javascript_dialogs/tab_modal_dialog_manager.cc b/chromium/components/javascript_dialogs/tab_modal_dialog_manager.cc
index 85f420b5a5c..075a0782959 100644
--- a/chromium/components/javascript_dialogs/tab_modal_dialog_manager.cc
+++ b/chromium/components/javascript_dialogs/tab_modal_dialog_manager.cc
@@ -15,7 +15,6 @@
#include "components/javascript_dialogs/app_modal_dialog_manager.h"
#include "components/javascript_dialogs/tab_modal_dialog_view.h"
#include "components/navigation_metrics/navigation_metrics.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
@@ -70,12 +69,12 @@ DialogOriginRelationship GetDialogOriginRelationship(
content::WebContents* web_contents,
content::RenderFrameHost* alerting_frame) {
url::Origin main_frame_origin =
- web_contents->GetMainFrame()->GetLastCommittedOrigin();
+ web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin();
if (!main_frame_origin.GetURL().SchemeIsHTTPOrHTTPS())
return DialogOriginRelationship::NON_HTTP_MAIN_FRAME;
- if (alerting_frame == web_contents->GetMainFrame())
+ if (alerting_frame == web_contents->GetPrimaryMainFrame())
return DialogOriginRelationship::HTTP_MAIN_FRAME;
url::Origin alerting_frame_origin = alerting_frame->GetLastCommittedOrigin();
@@ -378,8 +377,9 @@ void TabModalDialogManager::LogDialogDismissalCause(DismissalCause cause) {
// WebContents that had the alert call in it. For 99.9999% of cases they're
// the same, but for instances like the <webview> tag in extensions and PDF
// files that alert they may differ.
- ukm::SourceId source_id = ukm::GetSourceIdForWebContentsDocument(
- WebContentsObserver::web_contents());
+ ukm::SourceId source_id = WebContentsObserver::web_contents()
+ ->GetPrimaryMainFrame()
+ ->GetPageUkmSourceId();
if (source_id != ukm::kInvalidSourceId) {
ukm::builders::AbusiveExperienceHeuristic_JavaScriptDialog(source_id)
.SetDismissalCause(static_cast<int64_t>(cause))
diff --git a/chromium/components/js_injection/browser/js_communication_host.cc b/chromium/components/js_injection/browser/js_communication_host.cc
index fe477ebf6e2..e171897c489 100644
--- a/chromium/components/js_injection/browser/js_communication_host.cc
+++ b/chromium/components/js_injection/browser/js_communication_host.cc
@@ -115,7 +115,7 @@ JsCommunicationHost::AddDocumentStartJavaScript(
scripts_.emplace_back(script, origin_matcher, next_script_id_++);
ForEachRenderFrameHostWithinSameWebContents(
- web_contents()->GetMainFrame(),
+ web_contents()->GetPrimaryMainFrame(),
base::BindRepeating(
&JsCommunicationHost::NotifyFrameForAddDocumentStartJavaScript,
base::Unretained(this), &*scripts_.rbegin()));
@@ -128,7 +128,7 @@ bool JsCommunicationHost::RemoveDocumentStartJavaScript(int script_id) {
if (it->script_id_ == script_id) {
scripts_.erase(it);
ForEachRenderFrameHostWithinSameWebContents(
- web_contents()->GetMainFrame(),
+ web_contents()->GetPrimaryMainFrame(),
base::BindRepeating(
&JsCommunicationHost::NotifyFrameForRemoveDocumentStartJavaScript,
base::Unretained(this), script_id));
@@ -158,7 +158,7 @@ std::u16string JsCommunicationHost::AddWebMessageHostFactory(
js_object_name, origin_matcher, std::move(factory)));
ForEachRenderFrameHostWithinSameWebContents(
- web_contents()->GetMainFrame(),
+ web_contents()->GetPrimaryMainFrame(),
base::BindRepeating(
&JsCommunicationHost::NotifyFrameForWebMessageListener,
base::Unretained(this)));
@@ -172,7 +172,7 @@ void JsCommunicationHost::RemoveWebMessageHostFactory(
if ((*iterator)->name == js_object_name) {
js_objects_.erase(iterator);
ForEachRenderFrameHostWithinSameWebContents(
- web_contents()->GetMainFrame(),
+ web_contents()->GetPrimaryMainFrame(),
base::BindRepeating(
&JsCommunicationHost::NotifyFrameForWebMessageListener,
base::Unretained(this)));
diff --git a/chromium/components/js_injection/browser/js_to_browser_messaging.cc b/chromium/components/js_injection/browser/js_to_browser_messaging.cc
index 7867deb13b3..f7a7e5fd5f6 100644
--- a/chromium/components/js_injection/browser/js_to_browser_messaging.cc
+++ b/chromium/components/js_injection/browser/js_to_browser_messaging.cc
@@ -49,7 +49,7 @@ class JsToBrowserMessaging::ReplyProxyImpl : public WebMessageReplyProxy {
~ReplyProxyImpl() override = default;
// WebMessageReplyProxy:
- void PostMessage(std::unique_ptr<WebMessage> message) override {
+ void PostWebMessage(std::unique_ptr<WebMessage> message) override {
java_to_js_messaging_->OnPostMessage(message->message);
}
bool IsInBackForwardCache() override {
@@ -112,7 +112,7 @@ void JsToBrowserMessaging::PostMessage(
if (!host_) {
const std::string origin_string = GetOriginString(source_origin);
const bool is_main_frame =
- web_contents->GetMainFrame() == render_frame_host_;
+ web_contents->GetPrimaryMainFrame() == render_frame_host_;
host_ = connection_factory_->CreateHost(origin_string, is_main_frame,
reply_proxy_.get());
@@ -127,7 +127,8 @@ void JsToBrowserMessaging::PostMessage(
// PostMessage() has been received.
#if DCHECK_IS_ON()
DCHECK_EQ(GetOriginString(source_origin), origin_string_);
- DCHECK_EQ(is_main_frame_, web_contents->GetMainFrame() == render_frame_host_);
+ DCHECK_EQ(is_main_frame_,
+ web_contents->GetPrimaryMainFrame() == render_frame_host_);
#endif
std::unique_ptr<WebMessage> web_message = std::make_unique<WebMessage>();
web_message->message = message;
diff --git a/chromium/components/js_injection/browser/web_message_reply_proxy.h b/chromium/components/js_injection/browser/web_message_reply_proxy.h
index 3b887ee9dc1..8abd3842c7f 100644
--- a/chromium/components/js_injection/browser/web_message_reply_proxy.h
+++ b/chromium/components/js_injection/browser/web_message_reply_proxy.h
@@ -16,7 +16,10 @@ struct WebMessage;
// Used to send messages to the page.
class WebMessageReplyProxy {
public:
- virtual void PostMessage(std::unique_ptr<WebMessage> message) = 0;
+ // To match the JavaScript call, this function would ideally be named
+ // PostMessage(), but that conflicts with a Windows macro, so PostWebMessage()
+ // is used.
+ virtual void PostWebMessage(std::unique_ptr<WebMessage> message) = 0;
// Returns true if the page associated with the channel is in the back
// forward cache.
diff --git a/chromium/components/keyed_service/core/BUILD.gn b/chromium/components/keyed_service/core/BUILD.gn
index 13870193a68..46ee1f44e1d 100644
--- a/chromium/components/keyed_service/core/BUILD.gn
+++ b/chromium/components/keyed_service/core/BUILD.gn
@@ -10,7 +10,6 @@ component("core") {
"dependency_manager.cc",
"dependency_manager.h",
"dependency_node.h",
- "keyed_service.cc",
"keyed_service.h",
"keyed_service_base_factory.cc",
"keyed_service_base_factory.h",
diff --git a/chromium/components/keyed_service/core/dependency_manager.cc b/chromium/components/keyed_service/core/dependency_manager.cc
index 553c937794b..d8409efd7bf 100644
--- a/chromium/components/keyed_service/core/dependency_manager.cc
+++ b/chromium/components/keyed_service/core/dependency_manager.cc
@@ -193,3 +193,7 @@ void DependencyManager::DumpDependenciesAsGraphviz(
base::WriteFile(dot_file, contents.c_str(), contents.size());
}
#endif // NDEBUG
+
+DependencyGraph& DependencyManager::GetDependencyGraphForTesting() {
+ return dependency_graph_;
+} \ No newline at end of file
diff --git a/chromium/components/keyed_service/core/dependency_manager.h b/chromium/components/keyed_service/core/dependency_manager.h
index b2712da240e..bee06bcd2af 100644
--- a/chromium/components/keyed_service/core/dependency_manager.h
+++ b/chromium/components/keyed_service/core/dependency_manager.h
@@ -42,6 +42,9 @@ class KEYED_SERVICE_EXPORT DependencyManager {
DependencyManager* dependency_manager2,
void* context2);
+ // Returns the dependency graph for Keyed Services Factory testing purposes.
+ DependencyGraph& GetDependencyGraphForTesting();
+
protected:
DependencyManager();
virtual ~DependencyManager();
diff --git a/chromium/components/keyed_service/core/keyed_service.cc b/chromium/components/keyed_service/core/keyed_service.cc
deleted file mode 100644
index 5cfd61d3366..00000000000
--- a/chromium/components/keyed_service/core/keyed_service.cc
+++ /dev/null
@@ -1,11 +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.
-
-#include "components/keyed_service/core/keyed_service.h"
-
-KeyedService::KeyedService() {}
-
-KeyedService::~KeyedService() {}
-
-void KeyedService::Shutdown() {}
diff --git a/chromium/components/keyed_service/core/keyed_service.h b/chromium/components/keyed_service/core/keyed_service.h
index 5582c8735c6..0e1003abd0c 100644
--- a/chromium/components/keyed_service/core/keyed_service.h
+++ b/chromium/components/keyed_service/core/keyed_service.h
@@ -23,18 +23,20 @@
// process will run at shutdown of a given embedder.
class KEYED_SERVICE_EXPORT KeyedService {
public:
- KeyedService();
+ KeyedService() = default;
KeyedService(const KeyedService&) = delete;
KeyedService& operator=(const KeyedService&) = delete;
+ KeyedService(KeyedService&&) = delete;
+ KeyedService& operator=(KeyedService&&) = delete;
// The second pass is the actual deletion of each object.
- virtual ~KeyedService();
+ virtual ~KeyedService() = default;
// The first pass is to call Shutdown on a KeyedService.
- // Shutdown will be called automatically for you. Don't directly invoke this
- // unless you have a specific reason and understand the implications.
- virtual void Shutdown();
+ // Shutdown will be called automatically for you. Don't directly invoke
+ // this unless you have a specific reason and understand the implications.
+ virtual void Shutdown() {}
};
#endif // COMPONENTS_KEYED_SERVICE_CORE_KEYED_SERVICE_H_
diff --git a/chromium/components/keyed_service/core/keyed_service_base_factory.h b/chromium/components/keyed_service/core/keyed_service_base_factory.h
index a8fa2e3a258..b0d544da1de 100644
--- a/chromium/components/keyed_service/core/keyed_service_base_factory.h
+++ b/chromium/components/keyed_service/core/keyed_service_base_factory.h
@@ -48,6 +48,9 @@ class KEYED_SERVICE_EXPORT KeyedServiceBaseFactory : public DependencyNode {
// factories with different type of context, or dependencies are safe to have.
Type type() { return type_; }
+ // Returns whether the service is created for the given context.
+ virtual bool IsServiceCreated(void* context) const = 0;
+
protected:
KeyedServiceBaseFactory(const char* service_name,
DependencyManager* manager,
diff --git a/chromium/components/keyed_service/core/keyed_service_factory.cc b/chromium/components/keyed_service/core/keyed_service_factory.cc
index c7e1664af02..f19ee6cfb09 100644
--- a/chromium/components/keyed_service/core/keyed_service_factory.cc
+++ b/chromium/components/keyed_service/core/keyed_service_factory.cc
@@ -122,3 +122,7 @@ void KeyedServiceFactory::SetEmptyTestingFactory(void* context) {
bool KeyedServiceFactory::HasTestingFactory(void* context) {
return base::Contains(testing_factories_, context);
}
+
+bool KeyedServiceFactory::IsServiceCreated(void* context) const {
+ return base::Contains(mapping_, context);
+} \ No newline at end of file
diff --git a/chromium/components/keyed_service/core/keyed_service_factory.h b/chromium/components/keyed_service/core/keyed_service_factory.h
index e0e4be7d3d7..c6d5d84ec8e 100644
--- a/chromium/components/keyed_service/core/keyed_service_factory.h
+++ b/chromium/components/keyed_service/core/keyed_service_factory.h
@@ -80,6 +80,7 @@ class KEYED_SERVICE_EXPORT KeyedServiceFactory
void SetEmptyTestingFactory(void* context) override;
bool HasTestingFactory(void* context) override;
+ bool IsServiceCreated(void* context) const override;
private:
friend class DependencyManager;
diff --git a/chromium/components/keyed_service/core/refcounted_keyed_service_factory.cc b/chromium/components/keyed_service/core/refcounted_keyed_service_factory.cc
index b01fd6f7be6..20b210944e3 100644
--- a/chromium/components/keyed_service/core/refcounted_keyed_service_factory.cc
+++ b/chromium/components/keyed_service/core/refcounted_keyed_service_factory.cc
@@ -124,3 +124,7 @@ bool RefcountedKeyedServiceFactory::HasTestingFactory(void* context) {
void RefcountedKeyedServiceFactory::CreateServiceNow(void* context) {
GetServiceForContext(context, true);
}
+
+bool RefcountedKeyedServiceFactory::IsServiceCreated(void* context) const {
+ return base::Contains(mapping_, context);
+} \ No newline at end of file
diff --git a/chromium/components/keyed_service/core/refcounted_keyed_service_factory.h b/chromium/components/keyed_service/core/refcounted_keyed_service_factory.h
index 347df0b2213..1f70cfccc2a 100644
--- a/chromium/components/keyed_service/core/refcounted_keyed_service_factory.h
+++ b/chromium/components/keyed_service/core/refcounted_keyed_service_factory.h
@@ -87,6 +87,7 @@ class KEYED_SERVICE_EXPORT RefcountedKeyedServiceFactory
void SetEmptyTestingFactory(void* context) override;
bool HasTestingFactory(void* context) override;
void CreateServiceNow(void* context) override;
+ bool IsServiceCreated(void* context) const override;
private:
// The mapping between a context and its refcounted service.
diff --git a/chromium/components/language/android/BUILD.gn b/chromium/components/language/android/BUILD.gn
index 1703bd30845..ee3d7ed87b2 100644
--- a/chromium/components/language/android/BUILD.gn
+++ b/chromium/components/language/android/BUILD.gn
@@ -35,6 +35,8 @@ android_library("java") {
deps = [
":ulp_delegate_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
@@ -45,21 +47,24 @@ android_library("ulp_delegate_java") {
sources = [
"java/src/org/chromium/components/language/LanguageProfileDelegate.java",
"java/src/org/chromium/components/language/LanguageProfileDelegateImpl.java",
+ "java/src/org/chromium/components/language/LocaleManagerDelegate.java",
+ "java/src/org/chromium/components/language/LocaleManagerDelegateImpl.java",
]
- deps = [ "//base:base_java" ]
-
# Allow downstream targets to provide their own implementations.
- jar_excluded_patterns = [ "*/LanguageProfileDelegateImpl.class" ]
+ jar_excluded_patterns = [
+ "*/LanguageProfileDelegateImpl.class",
+ "*/LocaleManagerDelegateImpl.class",
+ ]
}
android_library("ulp_delegate_public_java") {
- sources = [ "java/src/org/chromium/components/language/LanguageProfileDelegateImpl.java" ]
-
- deps = [
- ":ulp_delegate_java",
- "//base:base_java",
+ sources = [
+ "java/src/org/chromium/components/language/LanguageProfileDelegateImpl.java",
+ "java/src/org/chromium/components/language/LocaleManagerDelegateImpl.java",
]
+
+ deps = [ ":ulp_delegate_java" ]
}
java_library("junit") {
@@ -96,7 +101,6 @@ android_library("javatests") {
"//chrome/test/android:chrome_java_test_support",
"//content/public/test/android:content_java_test_support",
"//third_party/android_support_test_runner:rules_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit",
]
diff --git a/chromium/components/language/android/geo_language_provider_bridge.cc b/chromium/components/language/android/geo_language_provider_bridge.cc
index bd12c9d5663..627f2604ee0 100644
--- a/chromium/components/language/android/geo_language_provider_bridge.cc
+++ b/chromium/components/language/android/geo_language_provider_bridge.cc
@@ -2,17 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/android/jni_string.h"
+#include "base/android/jni_array.h"
#include "components/language/android/jni_headers/GeoLanguageProviderBridge_jni.h"
#include "components/language/content/browser/geo_language_provider.h"
-static void JNI_GeoLanguageProviderBridge_GetCurrentGeoLanguages(
- JNIEnv* env,
- const base::android::JavaParamRef<jobject>& set) {
+using base::android::ScopedJavaLocalRef;
+using base::android::ToJavaArrayOfStrings;
+
+static ScopedJavaLocalRef<jobjectArray>
+JNI_GeoLanguageProviderBridge_GetCurrentGeoLanguages(JNIEnv* env) {
const std::vector<std::string> current_geo_languages =
language::GeoLanguageProvider::GetInstance()->CurrentGeoLanguages();
- for (const auto& language : current_geo_languages) {
- Java_GeoLanguageProviderBridge_addGeoLanguageToSet(
- env, set, base::android::ConvertUTF8ToJavaString(env, language));
- }
+ return ToJavaArrayOfStrings(env, current_geo_languages);
}
diff --git a/chromium/components/language/content/browser/geo_language_provider.cc b/chromium/components/language/content/browser/geo_language_provider.cc
index ab28d4b70fa..15f2b5e45ab 100644
--- a/chromium/components/language/content/browser/geo_language_provider.cc
+++ b/chromium/components/language/content/browser/geo_language_provider.cc
@@ -34,6 +34,9 @@ GeoLanguageProvider::Binder& GetBinderOverride() {
const char GeoLanguageProvider::kCachedGeoLanguagesPref[] =
"language.geo_language_provider.cached_geo_languages";
+const char GeoLanguageProvider::kTimeOfLastGeoLanguagesUpdatePref[] =
+ "language.geo_language_provider.time_of_last_geo_languages_update";
+
GeoLanguageProvider::GeoLanguageProvider()
: creation_task_runner_(base::SequencedTaskRunnerHandle::Get()),
background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
@@ -65,6 +68,7 @@ GeoLanguageProvider* GeoLanguageProvider::GetInstance() {
void GeoLanguageProvider::RegisterLocalStatePrefs(
PrefRegistrySimple* const registry) {
registry->RegisterListPref(kCachedGeoLanguagesPref);
+ registry->RegisterDoublePref(kTimeOfLastGeoLanguagesUpdatePref, 0);
}
void GeoLanguageProvider::StartUp(PrefService* const prefs) {
@@ -79,10 +83,27 @@ void GeoLanguageProvider::StartUp(PrefService* const prefs) {
languages_.push_back(language_value.GetString());
}
- // Continue startup in the background.
- background_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&GeoLanguageProvider::BackgroundStartUp,
- base::Unretained(this)));
+ const double last_update =
+ prefs_->GetDouble(kTimeOfLastGeoLanguagesUpdatePref);
+
+ base::TimeDelta time_passed_since_update =
+ base::Time::Now() - base::Time::FromTimeT(last_update);
+
+ // Delay startup if languages have been updated within |kMinUpdatePeriod|.
+ if (time_passed_since_update < kMinUpdatePeriod) {
+ base::TimeDelta time_till_next_update =
+ kMinUpdatePeriod - time_passed_since_update;
+ background_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&GeoLanguageProvider::BackgroundStartUp,
+ base::Unretained(this)),
+ time_till_next_update);
+ } else {
+ // Continue startup in the background.
+ background_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&GeoLanguageProvider::BackgroundStartUp,
+ base::Unretained(this)));
+ }
}
std::vector<std::string> GeoLanguageProvider::CurrentGeoLanguages() const {
@@ -202,6 +223,8 @@ void GeoLanguageProvider::SetGeoLanguages(
cache_list.Append(language);
}
prefs_->Set(kCachedGeoLanguagesPref, cache_list);
+ prefs_->SetDouble(kTimeOfLastGeoLanguagesUpdatePref,
+ base::Time::Now().ToDoubleT());
}
} // namespace language
diff --git a/chromium/components/language/content/browser/geo_language_provider.h b/chromium/components/language/content/browser/geo_language_provider.h
index ce133cbd665..6bc899690bd 100644
--- a/chromium/components/language/content/browser/geo_language_provider.h
+++ b/chromium/components/language/content/browser/geo_language_provider.h
@@ -34,6 +34,7 @@ namespace language {
class GeoLanguageProvider {
public:
static const char kCachedGeoLanguagesPref[];
+ static const char kTimeOfLastGeoLanguagesUpdatePref[];
static GeoLanguageProvider* GetInstance();
diff --git a/chromium/components/language/content/browser/geo_language_provider_unittest.cc b/chromium/components/language/content/browser/geo_language_provider_unittest.cc
index 45cd11b866b..c6ecb2a0f60 100644
--- a/chromium/components/language/content/browser/geo_language_provider_unittest.cc
+++ b/chromium/components/language/content/browser/geo_language_provider_unittest.cc
@@ -63,12 +63,15 @@ class GeoLanguageProviderTest : public testing::Test {
geo_language_provider_.SetGeoLanguages(languages);
}
- void SetUpCachedLanguages(const std::vector<std::string>& languages) {
+ void SetUpCachedLanguages(const std::vector<std::string>& languages,
+ const double update_time) {
base::ListValue cache_list;
for (const std::string& language : languages) {
cache_list.Append(language);
}
local_state_.Set(GeoLanguageProvider::kCachedGeoLanguagesPref, cache_list);
+ local_state_.SetDouble(
+ GeoLanguageProvider::kTimeOfLastGeoLanguagesUpdatePref, update_time);
}
const std::vector<std::string> GetCachedLanguages() {
@@ -151,8 +154,9 @@ TEST_F(GeoLanguageProviderTest, ButDoCallInTheNextDay) {
EXPECT_EQ(expected_langs_2, GetCachedLanguages());
}
-TEST_F(GeoLanguageProviderTest, CachedLanguagesPresent) {
- SetUpCachedLanguages({"en", "fr"});
+TEST_F(GeoLanguageProviderTest, CachedLanguagesUpdatedOnStartup) {
+ SetUpCachedLanguages({"en", "fr"},
+ (base::Time::Now() - base::Hours(25)).ToDoubleT());
MoveToLocation(23.0, 80.0);
StartGeoLanguageProvider();
@@ -166,4 +170,18 @@ TEST_F(GeoLanguageProviderTest, CachedLanguagesPresent) {
EXPECT_EQ(expected_langs, GetCachedLanguages());
}
+TEST_F(GeoLanguageProviderTest, CachedLanguagesNotUpdatedOnStartup) {
+ SetUpCachedLanguages({"en", "fr"}, base::Time::Now().ToDoubleT());
+ MoveToLocation(23.0, 80.0);
+ StartGeoLanguageProvider();
+
+ std::vector<std::string> expected_langs = {"en", "fr"};
+ EXPECT_EQ(expected_langs, GetCurrentGeoLanguages());
+
+ task_environment_.RunUntilIdle();
+
+ EXPECT_EQ(expected_langs, GetCurrentGeoLanguages());
+ EXPECT_EQ(expected_langs, GetCachedLanguages());
+}
+
} // namespace language
diff --git a/chromium/components/language/core/browser/BUILD.gn b/chromium/components/language/core/browser/BUILD.gn
index fdba4ca9088..96635294065 100644
--- a/chromium/components/language/core/browser/BUILD.gn
+++ b/chromium/components/language/core/browser/BUILD.gn
@@ -4,6 +4,8 @@
static_library("browser") {
sources = [
+ "accept_languages_service.cc",
+ "accept_languages_service.h",
"language_model.cc",
"language_model.h",
"language_model_manager.cc",
@@ -24,6 +26,7 @@ static_library("browser") {
deps = [
"//base",
+ "//base:i18n",
"//build:chromeos_buildflags",
"//components/keyed_service/core",
"//components/language/core/common",
@@ -37,6 +40,7 @@ static_library("browser") {
source_set("unit_tests") {
testonly = true
sources = [
+ "accept_languages_service_unittest.cc",
"language_model_manager_unittest.cc",
"language_prefs_unittest.cc",
"language_usage_metrics_unittest.cc",
diff --git a/chromium/components/language/core/browser/accept_languages_service.cc b/chromium/components/language/core/browser/accept_languages_service.cc
new file mode 100644
index 00000000000..ef6856e2bab
--- /dev/null
+++ b/chromium/components/language/core/browser/accept_languages_service.cc
@@ -0,0 +1,72 @@
+// 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.
+
+#include "components/language/core/browser/accept_languages_service.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/i18n/rtl.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "components/language/core/common/language_util.h"
+#include "components/prefs/pref_service.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace language {
+
+AcceptLanguagesService::AcceptLanguagesService(
+ PrefService* prefs,
+ const char* accept_languages_pref)
+ : accept_languages_pref_(accept_languages_pref) {
+ InitAcceptLanguages(prefs);
+
+ // Also start listening for changes in the accept languages.
+ pref_change_registrar_.Init(prefs);
+ pref_change_registrar_.Add(
+ accept_languages_pref,
+ base::BindRepeating(&AcceptLanguagesService::InitAcceptLanguages,
+ base::Unretained(this), prefs));
+}
+
+AcceptLanguagesService::~AcceptLanguagesService() = default;
+
+// static
+bool AcceptLanguagesService::CanBeAcceptLanguage(
+ const base::StringPiece& language) {
+ std::string accept_language(language);
+ language::ToChromeLanguageSynonym(&accept_language);
+
+ const std::string ui_locale = base::i18n::GetConfiguredLocale();
+
+ return l10n_util::IsLanguageAccepted(ui_locale, accept_language);
+}
+
+bool AcceptLanguagesService::IsAcceptLanguage(
+ const base::StringPiece& language) const {
+ std::string accept_language(language);
+ language::ToChromeLanguageSynonym(&accept_language);
+ return accept_languages_.find(accept_language) != accept_languages_.end();
+}
+
+void AcceptLanguagesService::InitAcceptLanguages(PrefService* prefs) {
+ DCHECK(prefs);
+ // Build the languages.
+ accept_languages_.clear();
+ std::string accept_languages_pref = prefs->GetString(accept_languages_pref_);
+ for (const base::StringPiece& lang :
+ base::SplitStringPiece(accept_languages_pref, ",", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_ALL)) {
+ // Get rid of the locale extension if any (ex: en-US -> en), but for Chinese
+ // for which the CLD reports zh-CN and zh-TW.
+ size_t index = lang.find('-');
+ if (index != base::StringPiece::npos && lang != "zh-CN" && lang != "zh-TW")
+ accept_languages_.insert(std::string(lang.substr(0, index)));
+ accept_languages_.insert(std::string(lang));
+ }
+}
+
+} // namespace language
diff --git a/chromium/components/language/core/browser/accept_languages_service.h b/chromium/components/language/core/browser/accept_languages_service.h
new file mode 100644
index 00000000000..e84c9121dc4
--- /dev/null
+++ b/chromium/components/language/core/browser/accept_languages_service.h
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef COMPONENTS_LANGUAGE_CORE_BROWSER_ACCEPT_LANGUAGES_SERVICE_H_
+#define COMPONENTS_LANGUAGE_CORE_BROWSER_ACCEPT_LANGUAGES_SERVICE_H_
+
+#include <set>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/prefs/pref_change_registrar.h"
+
+class PrefService;
+
+namespace language {
+
+// AcceptLanguagesService tracks the value of the "Accept-Language" HTTP
+// header.
+class AcceptLanguagesService : public KeyedService {
+ public:
+ // |accept_languages_pref| is the path to the preference storing the accept
+ // languages.
+ AcceptLanguagesService(PrefService* prefs, const char* accept_languages_pref);
+
+ AcceptLanguagesService(const AcceptLanguagesService&) = delete;
+ AcceptLanguagesService& operator=(const AcceptLanguagesService&) = delete;
+
+ ~AcceptLanguagesService() override;
+
+ // Returns true if |language| is available as Accept-Languages for the given
+ // |display_locale|. |language| will be converted if it has the synonym of
+ // accept language.
+ static bool CanBeAcceptLanguage(const base::StringPiece& language);
+
+ // Returns true if the passed language has been configured by the user as an
+ // accept language. |language| will be converted if it has the synonym of
+ // accept languages.
+ bool IsAcceptLanguage(const base::StringPiece& language) const;
+
+ private:
+ // Initializes the |accept_languages_| language table based on the associated
+ // preference in |prefs|.
+ void InitAcceptLanguages(PrefService* prefs);
+
+ // Set of accept languages.
+ std::set<std::string> accept_languages_;
+
+ // Listens to accept languages changes.
+ PrefChangeRegistrar pref_change_registrar_;
+
+ // Path of accept languages preference.
+ const std::string accept_languages_pref_;
+};
+
+} // namespace language
+
+#endif // COMPONENTS_LANGUAGE_CORE_BROWSER_ACCEPT_LANGUAGES_SERVICE_H_
diff --git a/chromium/components/language/core/browser/accept_languages_service_unittest.cc b/chromium/components/language/core/browser/accept_languages_service_unittest.cc
new file mode 100644
index 00000000000..f48988cad8f
--- /dev/null
+++ b/chromium/components/language/core/browser/accept_languages_service_unittest.cc
@@ -0,0 +1,73 @@
+// 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 "components/language/core/browser/accept_languages_service.h"
+
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/translate/core/browser/translate_download_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using translate::TranslateDownloadManager;
+
+namespace language {
+namespace {
+
+// RAII class to set the TranslateDownloadManager application locale, and then
+// restore it to the original value when the object goes out of scope.
+class TranslateLocaleRestorer {
+ public:
+ explicit TranslateLocaleRestorer(const std::string& new_locale)
+ : existing_locale_(
+ TranslateDownloadManager::GetInstance()->application_locale()) {
+ TranslateDownloadManager::GetInstance()->set_application_locale(new_locale);
+ }
+ ~TranslateLocaleRestorer() {
+ TranslateDownloadManager::GetInstance()->set_application_locale(
+ existing_locale_);
+ }
+
+ private:
+ const std::string existing_locale_;
+};
+
+TEST(AcceptLanguagesServiceTest, TestIsAcceptLanguage) {
+ const char* const pref_setting = "translate-accept-languages";
+ TestingPrefServiceSimple prefs;
+ prefs.registry()->RegisterStringPref(pref_setting, "en-US,es,zh-CN");
+ AcceptLanguagesService accept_languages(&prefs, pref_setting);
+
+ // All valid.
+ EXPECT_TRUE(accept_languages.IsAcceptLanguage("en"));
+ EXPECT_TRUE(accept_languages.IsAcceptLanguage("en-US"));
+ EXPECT_TRUE(accept_languages.IsAcceptLanguage("es"));
+ EXPECT_TRUE(accept_languages.IsAcceptLanguage("zh-CN"));
+
+ // Not valid language.
+ EXPECT_FALSE(accept_languages.IsAcceptLanguage("xx"));
+
+ // zh-XX cannot be shortened to zh.
+ EXPECT_FALSE(accept_languages.IsAcceptLanguage("zh"));
+}
+
+TEST(AcceptLanguagesServiceTest, TestCanBeAcceptLanguage) {
+ TranslateLocaleRestorer locale_restorer("es");
+
+ // Valid accept languages.
+ EXPECT_TRUE(AcceptLanguagesService::CanBeAcceptLanguage("en"));
+ EXPECT_TRUE(AcceptLanguagesService::CanBeAcceptLanguage("en-US"));
+ EXPECT_TRUE(AcceptLanguagesService::CanBeAcceptLanguage("es"));
+ EXPECT_TRUE(AcceptLanguagesService::CanBeAcceptLanguage("es-419"));
+ EXPECT_TRUE(AcceptLanguagesService::CanBeAcceptLanguage("zh-CN"));
+
+ // Not valid format.
+ EXPECT_FALSE(AcceptLanguagesService::CanBeAcceptLanguage("en-us"));
+ EXPECT_FALSE(AcceptLanguagesService::CanBeAcceptLanguage("zh-Hant"));
+
+ // Not valid language.
+ EXPECT_FALSE(AcceptLanguagesService::CanBeAcceptLanguage("xx"));
+}
+
+} // namespace
+} // namespace language
diff --git a/chromium/components/language/core/browser/language_prefs_test_util.h b/chromium/components/language/core/browser/language_prefs_test_util.h
index 4d25162524a..cba72438170 100644
--- a/chromium/components/language/core/browser/language_prefs_test_util.h
+++ b/chromium/components/language/core/browser/language_prefs_test_util.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/memory/raw_ptr.h"
#include "base/strings/string_piece.h"
class PrefService;
@@ -53,7 +54,7 @@ class LanguagePrefTester {
void SetForcedLanguagePrefs(std::vector<std::string>&& languages);
private:
- PrefService* prefs_;
+ raw_ptr<PrefService> prefs_;
};
} // namespace test
diff --git a/chromium/components/language/core/common/language_experiments.cc b/chromium/components/language/core/common/language_experiments.cc
index 1c920b632fe..e9d99ac664c 100644
--- a/chromium/components/language/core/common/language_experiments.cc
+++ b/chromium/components/language/core/common/language_experiments.cc
@@ -28,8 +28,6 @@ const base::Feature kNotifySyncOnLanguageDetermined{
"NotifySyncOnLanguageDetermined", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kDetailedLanguageSettings{"DetailedLanguageSettings",
base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kDesktopRestructuredLanguageSettings{
- "DesktopRestructuredLanguageSettings", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kDesktopDetailedLanguageSettings{
"DesktopDetailedLanguageSettings", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kTranslateAssistContent{"TranslateAssistContent",
@@ -38,8 +36,6 @@ const base::Feature kTranslateIntent{"TranslateIntent",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kContentLanguagesInLanguagePicker{
"ContentLanguagesInLanguagePicker", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kUseULPLanguagesInChrome{"UseULPLanguagesInChrome",
- base::FEATURE_ENABLED_BY_DEFAULT};
// Params:
const char kBackoffThresholdKey[] = "backoff_threshold";
diff --git a/chromium/components/language/core/common/language_experiments.h b/chromium/components/language/core/common/language_experiments.h
index 3f9224b7303..839f7015373 100644
--- a/chromium/components/language/core/common/language_experiments.h
+++ b/chromium/components/language/core/common/language_experiments.h
@@ -42,10 +42,6 @@ extern const base::Feature kUseButtonTranslateBubbleUi;
// This feature enables setting the application language on Android.
extern const base::Feature kDetailedLanguageSettings;
-// This feature enables the desktop version's redesigned language settings
-// layout.
-extern const base::Feature kDesktopRestructuredLanguageSettings;
-
// This feature enables setting the application language on Desktop.
extern const base::Feature kDesktopDetailedLanguageSettings;
@@ -58,9 +54,6 @@ extern const base::Feature kTranslateIntent;
// This feature enables an intent that starts translating the foreground tab.
extern const base::Feature kContentLanguagesInLanguagePicker;
-// This feature enables use of ULP language data in Chrome.
-extern const base::Feature kUseULPLanguagesInChrome;
-
enum class OverrideLanguageModel {
DEFAULT,
GEO,
diff --git a/chromium/components/language/core/common/language_util.cc b/chromium/components/language/core/common/language_util.cc
index a14e0a9883e..4737aebab93 100644
--- a/chromium/components/language/core/common/language_util.cc
+++ b/chromium/components/language/core/common/language_util.cc
@@ -28,12 +28,9 @@ struct LanguageCodePair {
//
// If this table is updated, please sync this with the synonym table in
// chrome/browser/resources/settings/languages_page/languages.js.
-const LanguageCodePair kChromeToTranslateLanguageMap[] = {
+const LanguageCodePair kTranslateOnlySynonyms[] = {
{"no", "nb"},
- {"tl", "fil"},
-};
-const LanguageCodePair kTranslateToChromeLanguageMap[] = {
- {"tl", "fil"},
+ {"id", "in"},
};
// Some languages have changed codes over the years and sometimes the older
@@ -44,6 +41,7 @@ const LanguageCodePair kTranslateToChromeLanguageMap[] = {
const LanguageCodePair kLanguageCodeSynonyms[] = {
{"iw", "he"},
{"jw", "jv"},
+ {"tl", "fil"},
};
// Some Chinese language codes are compatible with zh-TW or zh-CN in terms of
@@ -82,14 +80,14 @@ void ToTranslateLanguageSynonym(std::string* language) {
return;
}
- for (const auto& language_pair : kChromeToTranslateLanguageMap) {
+ for (const auto& language_pair : kTranslateOnlySynonyms) {
if (main_part == language_pair.chrome_language) {
*language = language_pair.translate_language;
return;
}
}
- // Apply linear search here because number of items in the list is just four.
+ // Apply linear search here because number of items in the list is just three.
for (const auto& language_pair : kLanguageCodeSynonyms) {
if (main_part == language_pair.chrome_language) {
*language = language_pair.translate_language;
@@ -102,18 +100,11 @@ void ToTranslateLanguageSynonym(std::string* language) {
}
void ToChromeLanguageSynonym(std::string* language) {
- for (const auto& language_pair : kTranslateToChromeLanguageMap) {
- if (*language == language_pair.translate_language) {
- *language = language_pair.chrome_language;
- return;
- }
- }
-
auto [main_part, tail_part] = language::SplitIntoMainAndTail(*language);
if (main_part.empty())
return;
- // Apply linear search here because number of items in the list is just four.
+ // Apply linear search here because number of items in the list is just three.
for (const auto& language_pair : kLanguageCodeSynonyms) {
if (main_part == language_pair.translate_language) {
main_part = language_pair.chrome_language;
diff --git a/chromium/components/language/core/common/language_util_unittest.cc b/chromium/components/language/core/common/language_util_unittest.cc
index 2826f95eb93..10683859e3c 100644
--- a/chromium/components/language/core/common/language_util_unittest.cc
+++ b/chromium/components/language/core/common/language_util_unittest.cc
@@ -16,6 +16,14 @@ TEST_F(LanguageUtilTest, ToTranslateLanguageSynonym) {
language::ToTranslateLanguageSynonym(&language);
EXPECT_EQ("no", language);
+ language = std::string("in");
+ language::ToTranslateLanguageSynonym(&language);
+ EXPECT_EQ("id", language);
+
+ language = std::string("fil");
+ language::ToTranslateLanguageSynonym(&language);
+ EXPECT_EQ("tl", language);
+
// Test all known Chinese cases.
language = std::string("zh-HK");
language::ToTranslateLanguageSynonym(&language);
@@ -57,10 +65,16 @@ TEST_F(LanguageUtilTest, ToChromeLanguageSynonym) {
language = std::string("no");
language::ToChromeLanguageSynonym(&language);
EXPECT_EQ("no", language);
+
language = std::string("nb");
language::ToChromeLanguageSynonym(&language);
EXPECT_EQ("nb", language);
+ // Convert to Chrome synonym
+ language = std::string("tl");
+ language::ToChromeLanguageSynonym(&language);
+ EXPECT_EQ("fil", language);
+
// Preserve a sub code
language = std::string("iw-IL");
language::ToChromeLanguageSynonym(&language);
diff --git a/chromium/components/lens/OWNERS b/chromium/components/lens/OWNERS
index b5201a10a76..a09d9e753a1 100644
--- a/chromium/components/lens/OWNERS
+++ b/chromium/components/lens/OWNERS
@@ -1,4 +1,3 @@
-schechter@google.com
benwgold@google.com
juanmojica@google.com
-timothyallen@google.com
+yusuyoutube@google.com \ No newline at end of file
diff --git a/chromium/components/lens/lens_entrypoints.cc b/chromium/components/lens/lens_entrypoints.cc
index 8318f107628..8ef27c01577 100644
--- a/chromium/components/lens/lens_entrypoints.cc
+++ b/chromium/components/lens/lens_entrypoints.cc
@@ -19,6 +19,7 @@ constexpr char kEntryPointQueryParameter[] = "ep";
constexpr char kChromeRegionSearchMenuItem[] = "crs";
constexpr char kChromeSearchWithGoogleLensContextMenuItem[] = "ccm";
constexpr char kChromeOpenNewTabSidePanel[] = "cnts";
+constexpr char kChromeFullscreenSearchMenuItem[] = "cfs";
constexpr char kSurfaceQueryParameter[] = "s";
constexpr char kStartTimeQueryParameter[] = "st";
@@ -50,6 +51,10 @@ std::map<std::string, std::string> GetLensQueryParametersMap(
query_parameters.insert({kEntryPointQueryParameter,
kChromeSearchWithGoogleLensContextMenuItem});
break;
+ case lens::CHROME_FULLSCREEN_SEARCH_MENU_ITEM:
+ query_parameters.insert(
+ {kEntryPointQueryParameter, kChromeFullscreenSearchMenuItem});
+ break;
default:
// Empty strings are ignored when query parameters are built.
break;
diff --git a/chromium/components/lens/lens_entrypoints.h b/chromium/components/lens/lens_entrypoints.h
index 34ebe698e9b..c2007234bbd 100644
--- a/chromium/components/lens/lens_entrypoints.h
+++ b/chromium/components/lens/lens_entrypoints.h
@@ -19,6 +19,7 @@ enum EntryPoint {
CHROME_OPEN_NEW_TAB_SIDE_PANEL,
CHROME_REGION_SEARCH_MENU_ITEM,
CHROME_SEARCH_WITH_GOOGLE_LENS_CONTEXT_MENU_ITEM,
+ CHROME_FULLSCREEN_SEARCH_MENU_ITEM,
UNKNOWN
};
diff --git a/chromium/components/lens/lens_entrypoints_unittest.cc b/chromium/components/lens/lens_entrypoints_unittest.cc
index dca213ade20..652af0f2a92 100644
--- a/chromium/components/lens/lens_entrypoints_unittest.cc
+++ b/chromium/components/lens/lens_entrypoints_unittest.cc
@@ -54,6 +54,14 @@ TEST(LensEntryPointsTest, GetOpenNewTabSidePanelParameterTest) {
EXPECT_THAT(query_param, MatchesRegex("ep=cnts&s=&st=\\d+"));
}
+TEST(LensEntryPointsTest, GetFullscreenSearchQueryParameterTest) {
+ lens::EntryPoint lens_ep =
+ lens::EntryPoint::CHROME_FULLSCREEN_SEARCH_MENU_ITEM;
+ std::string query_param = lens::GetQueryParametersForLensRequest(
+ lens_ep, /*is_side_panel_request=*/false);
+ EXPECT_THAT(query_param, MatchesRegex("ep=cfs&s=&st=\\d+"));
+}
+
TEST(LensEntryPointsTest, GetUnknownEntryPointTest) {
std::string query_param = lens::GetQueryParametersForLensRequest(
lens::EntryPoint::UNKNOWN, /*is_side_panel_request=*/false);
@@ -112,6 +120,15 @@ TEST(LensEntryPointsTest, AppendOpenNewTabSidePanelParameterTest) {
EXPECT_THAT(url.query(), MatchesRegex("ep=cnts&s=&st=\\d+"));
}
+TEST(LensEntryPointsTest, AppendFullscreenSearchQueryParameterTest) {
+ lens::EntryPoint lens_ep =
+ lens::EntryPoint::CHROME_FULLSCREEN_SEARCH_MENU_ITEM;
+ GURL original_url = GURL("https://lens.google.com/");
+ GURL url = lens::AppendOrReplaceQueryParametersForLensRequest(
+ original_url, lens_ep, /*is_side_panel_request=*/false);
+ EXPECT_THAT(url.query(), MatchesRegex("ep=cfs&s=&st=\\d+"));
+}
+
TEST(LensEntryPointsTest, AppendUnknownEntryPointTest) {
GURL original_url = GURL("https://lens.google.com/");
GURL url = lens::AppendOrReplaceQueryParametersForLensRequest(
diff --git a/chromium/components/lens/lens_features.cc b/chromium/components/lens/lens_features.cc
index 9b3b90062e2..3a391d4ea78 100644
--- a/chromium/components/lens/lens_features.cc
+++ b/chromium/components/lens/lens_features.cc
@@ -13,20 +13,17 @@ namespace features {
const base::Feature kLensStandalone{"LensStandalone",
base::FEATURE_ENABLED_BY_DEFAULT};
-const base::FeatureParam<bool> kRegionSearchMacCursorFix{
- &kLensStandalone, "region-search-mac-cursor-fix", true};
-
-const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText1{
- &kLensStandalone, "use-menu-item-alt-text-1", false};
+const base::Feature kLensImageCompression{"LensImageCompression",
+ base::FEATURE_ENABLED_BY_DEFAULT};
-const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText2{
- &kLensStandalone, "use-menu-item-alt-text-2", false};
+const base::Feature kLensSearchOptimizations{"LensSearchOptimizations",
+ base::FEATURE_DISABLED_BY_DEFAULT};
-const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText3{
- &kLensStandalone, "use-menu-item-alt-text-3", false};
+const base::Feature kLensTransparentImagesFix{
+ "LensTransparentImagesFix", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText4{
- &kLensStandalone, "use-menu-item-alt-text-4", true};
+const base::FeatureParam<bool> kRegionSearchMacCursorFix{
+ &kLensStandalone, "region-search-mac-cursor-fix", true};
const base::FeatureParam<bool> kEnableUKMLoggingForRegionSearch{
&kLensStandalone, "region-search-enable-ukm-logging", true};
@@ -37,17 +34,32 @@ const base::FeatureParam<bool> kEnableUKMLoggingForImageSearch{
const base::FeatureParam<bool> kEnableSidePanelForLens{
&kLensStandalone, "enable-side-panel", true};
+constexpr base::FeatureParam<std::string> kHomepageURLForLens{
+ &kLensStandalone, "lens-homepage-url", "https://lens.google.com/"};
+
constexpr base::FeatureParam<int> kMaxPixelsForRegionSearch{
- &kLensStandalone, "region-search-dimensions-max-pixels", 1000};
+ &kLensImageCompression, "region-search-dimensions-max-pixels", 1000};
constexpr base::FeatureParam<int> kMaxAreaForRegionSearch{
- &kLensStandalone, "region-search-dimensions-max-area", 1000000};
+ &kLensImageCompression, "region-search-dimensions-max-area", 1000000};
constexpr base::FeatureParam<int> kMaxPixelsForImageSearch{
- &kLensStandalone, "dimensions-max-pixels", 1000};
+ &kLensImageCompression, "dimensions-max-pixels", 1000};
-constexpr base::FeatureParam<std::string> kHomepageURLForLens{
- &kLensStandalone, "lens-homepage-url", "https://lens.google.com/"};
+const base::FeatureParam<bool> kUseGoogleAsVisualSearchProvider{
+ &kLensSearchOptimizations, "use-google-as-visual-search-provider", false};
+
+const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText1{
+ &kLensSearchOptimizations, "use-menu-item-alt-text-1", false};
+
+const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText2{
+ &kLensSearchOptimizations, "use-menu-item-alt-text-2", false};
+
+// Default is set to true but it is only enabled if kLensSearchOptimizations is
+// enabled. This setup allows us to have fullscreen search as a toggleable
+// experience in chrome://flags
+const base::FeatureParam<bool> kEnableLensFullscreenSearch{
+ &kLensSearchOptimizations, "enable-lens-fullscreen-search", true};
bool GetEnableUKMLoggingForRegionSearch() {
return kEnableUKMLoggingForRegionSearch.Get();
@@ -73,10 +85,39 @@ std::string GetHomepageURLForLens() {
return kHomepageURLForLens.Get();
}
+bool UseRegionSearchMenuItemAltText1() {
+ return base::FeatureList::IsEnabled(kLensStandalone) &&
+ base::FeatureList::IsEnabled(kLensSearchOptimizations) &&
+ kRegionSearchUseMenuItemAltText1.Get();
+}
+
+bool UseRegionSearchMenuItemAltText2() {
+ return base::FeatureList::IsEnabled(kLensStandalone) &&
+ base::FeatureList::IsEnabled(kLensSearchOptimizations) &&
+ kRegionSearchUseMenuItemAltText2.Get();
+}
+
+bool UseGoogleAsVisualSearchProvider() {
+ return base::FeatureList::IsEnabled(kLensStandalone) &&
+ base::FeatureList::IsEnabled(kLensSearchOptimizations) &&
+ kUseGoogleAsVisualSearchProvider.Get();
+}
+
+bool IsLensFullscreenSearchEnabled() {
+ return base::FeatureList::IsEnabled(kLensStandalone) &&
+ base::FeatureList::IsEnabled(kLensSearchOptimizations) &&
+ kEnableLensFullscreenSearch.Get();
+}
+
bool IsLensSidePanelEnabled() {
return base::FeatureList::IsEnabled(kLensStandalone) &&
kEnableSidePanelForLens.Get();
}
+bool GetSendImagesAsPng() {
+ return base::FeatureList::IsEnabled(kLensStandalone) &&
+ base::FeatureList::IsEnabled(kLensTransparentImagesFix);
+}
+
} // namespace features
} // namespace lens
diff --git a/chromium/components/lens/lens_features.h b/chromium/components/lens/lens_features.h
index 73a7988ac87..f228e96ac0a 100644
--- a/chromium/components/lens/lens_features.h
+++ b/chromium/components/lens/lens_features.h
@@ -16,25 +16,30 @@ namespace features {
// Enables context menu search by image sending to the Lens homepage.
extern const base::Feature kLensStandalone;
-// Enables Lens Region Search from the context menu.
-extern const base::Feature kLensRegionSearch;
+// Feature that controls the compression of images before they are sent to Lens.
+extern const base::Feature kLensImageCompression;
+
+// Enables a variety of changes aimed to improve user's engagement with current
+// Lens features.
+extern const base::Feature kLensSearchOptimizations;
+
+// Enables a fix to properly handle transparent images in Lens Image Search
+extern const base::Feature kLensTransparentImagesFix;
// Enables a fix for cursor pointer/crosshair state over overlay on Mac.
// TODO(crbug/1266514): make default and remove feature once launched.
extern const base::FeatureParam<bool> kRegionSearchMacCursorFix;
+// Enables using `Google` as the visual search provider instead of `Google
+// Lens`.
+extern const base::FeatureParam<bool> kUseGoogleAsVisualSearchProvider;
+
// Enables alternate option 1 for the Region Search context menu item text.
extern const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText1;
// Enables alternate option 2 for the Region Search context menu item text.
extern const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText2;
-// Enables alternate option 3 for the Region Search context menu item text.
-extern const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText3;
-
-// Enables alternate option 4 for the Region Search context menu item text.
-extern const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText4;
-
// Enables UKM logging for the Lens Region Search feature.
extern const base::FeatureParam<bool> kEnableUKMLoggingForRegionSearch;
@@ -44,6 +49,9 @@ extern const base::FeatureParam<bool> kEnableUKMLoggingForImageSearch;
// Enables the side panel for Lens features on Chrome where supported.
extern const base::FeatureParam<bool> kEnableSidePanelForLens;
+// Enables Lens fullscreen search on Desktop platforms.
+extern const base::FeatureParam<bool> kEnableFullscreenSearch;
+
// Returns whether to enable UKM logging for Lens Region Search feature.
extern bool GetEnableUKMLoggingForRegionSearch();
@@ -63,9 +71,27 @@ extern int GetMaxPixelsForImageSearch();
// The URL for the Lens home page.
extern std::string GetHomepageURLForLens();
+// Returns whether Lens fullscreen search is enabled.
+extern bool IsLensFullscreenSearchEnabled();
+
+// Returns whether to use alternative option 1 for the Region Search context
+// menu item text.
+extern bool UseRegionSearchMenuItemAltText1();
+
+// Returns whether to use alternative option 2 for the Region Search context
+// menu item text.
+extern bool UseRegionSearchMenuItemAltText2();
+
+// Returns whether to use `Google` as the visual search provider for all
+// relevant Lens context menu strings.
+extern bool UseGoogleAsVisualSearchProvider();
+
// Returns whether the Lens side panel is enabled.
extern bool IsLensSidePanelEnabled();
+// Returns whether to send images to Lens Standalone as PNG
+extern bool GetSendImagesAsPng();
+
} // namespace features
} // namespace lens
diff --git a/chromium/components/live_caption/BUILD.gn b/chromium/components/live_caption/BUILD.gn
index 88e60425081..892d8c7995e 100644
--- a/chromium/components/live_caption/BUILD.gn
+++ b/chromium/components/live_caption/BUILD.gn
@@ -89,6 +89,6 @@ source_set("utils") {
}
if (is_chromeos_lacros) {
- deps += [ "//chromeos/lacros" ]
+ deps += [ "//chromeos/startup" ]
}
}
diff --git a/chromium/components/live_caption/DEPS b/chromium/components/live_caption/DEPS
index 971c4b98ab4..6e2d080655e 100644
--- a/chromium/components/live_caption/DEPS
+++ b/chromium/components/live_caption/DEPS
@@ -1,7 +1,7 @@
include_rules = [
"+ash/constants",
"+base",
- "+chromeos/lacros",
+ "+chromeos/startup",
"+components/keyed_service/core",
"+components/prefs",
"+components/pref_registry",
diff --git a/chromium/components/live_caption/caption_bubble_controller.h b/chromium/components/live_caption/caption_bubble_controller.h
index 8c26488cd6c..f39d1ff18de 100644
--- a/chromium/components/live_caption/caption_bubble_controller.h
+++ b/chromium/components/live_caption/caption_bubble_controller.h
@@ -9,7 +9,7 @@
#include <string>
#include "components/live_caption/views/caption_bubble.h"
-#include "media/mojo/mojom/speech_recognition_service.mojom.h"
+#include "media/mojo/mojom/speech_recognition.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/native_theme/caption_style.h"
diff --git a/chromium/components/live_caption/caption_util.cc b/chromium/components/live_caption/caption_util.cc
index a664cf06b78..854d119b4c3 100644
--- a/chromium/components/live_caption/caption_util.cc
+++ b/chromium/components/live_caption/caption_util.cc
@@ -25,7 +25,7 @@
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chromeos/lacros/lacros_service.h"
+#include "chromeos/startup/browser_init_params.h"
#endif
namespace {
@@ -136,9 +136,7 @@ bool IsLiveCaptionFeatureSupported() {
if (!base::FeatureList::IsEnabled(ash::features::kOnDeviceSpeechRecognition))
return false;
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
- if (!chromeos::LacrosService::Get()
- ->init_params()
- ->is_ondevice_speech_supported)
+ if (!chromeos::BrowserInitParams::Get()->is_ondevice_speech_supported)
return false;
#endif
@@ -154,11 +152,6 @@ bool IsLiveCaptionFeatureSupported() {
// The Speech On-Device API (SODA) component does not support Windows on
// arm64.
return false;
-#elif BUILDFLAG(IS_CHROMEOS_LACROS)
- // Disable Live Caption on LaCrOS. The feature has not been migrated there
- // yet, and currently fails rather gracelessly (opening a non-existent .so).
- // TODO(b/223493879): Remove this once it fails more gracefully.
- return false;
#else
return true;
#endif
diff --git a/chromium/components/live_caption/live_caption_controller.cc b/chromium/components/live_caption/live_caption_controller.cc
index 8272b54b256..c2f1f27b153 100644
--- a/chromium/components/live_caption/live_caption_controller.cc
+++ b/chromium/components/live_caption/live_caption_controller.cc
@@ -229,7 +229,7 @@ void LiveCaptionController::OnError(
OnErrorClickedCallback error_clicked_callback,
OnDoNotShowAgainClickedCallback error_silenced_callback) {
if (!caption_bubble_controller_)
- return;
+ CreateUI();
caption_bubble_controller_->OnError(caption_bubble_context, error_type,
std::move(error_clicked_callback),
std::move(error_silenced_callback));
diff --git a/chromium/components/live_caption/live_caption_controller.h b/chromium/components/live_caption/live_caption_controller.h
index 128ca6abf25..42ce5dfdc72 100644
--- a/chromium/components/live_caption/live_caption_controller.h
+++ b/chromium/components/live_caption/live_caption_controller.h
@@ -13,7 +13,7 @@
#include "components/live_caption/views/caption_bubble.h"
#include "components/soda/constants.h"
#include "components/soda/soda_installer.h"
-#include "media/mojo/mojom/speech_recognition_service.mojom.h"
+#include "media/mojo/mojom/speech_recognition.mojom.h"
#include "ui/native_theme/caption_style.h"
#include "ui/native_theme/native_theme_observer.h"
diff --git a/chromium/components/live_caption/views/caption_bubble.cc b/chromium/components/live_caption/views/caption_bubble.cc
index c44892b9f7c..ac3258fe5d1 100644
--- a/chromium/components/live_caption/views/caption_bubble.cc
+++ b/chromium/components/live_caption/views/caption_bubble.cc
@@ -195,7 +195,7 @@ class MediaFoundationRendererErrorMessageView : public views::StyledLabel {
}
private:
- CaptionBubble* const caption_bubble_; // Not owned.
+ const raw_ptr<CaptionBubble> caption_bubble_; // Not owned.
};
#endif
@@ -208,8 +208,7 @@ class CaptionBubbleFrameView : public views::BubbleFrameView {
: views::BubbleFrameView(gfx::Insets(), gfx::Insets()),
buttons_(buttons) {
auto border = std::make_unique<views::BubbleBorder>(
- views::BubbleBorder::FLOAT, views::BubbleBorder::DIALOG_SHADOW,
- gfx::kPlaceholderColor);
+ views::BubbleBorder::FLOAT, views::BubbleBorder::DIALOG_SHADOW);
border->SetCornerRadius(kCornerRadiusDip);
views::BubbleFrameView::SetBubbleBorder(std::move(border));
}
@@ -994,9 +993,7 @@ void CaptionBubble::UpdateContentSize() {
#if BUILDFLAG(IS_WIN)
// The Media Foundation renderer error message should not scale with the
// user's caption style preference.
- if (model_ && model_->HasError() &&
- model_->ErrorType() ==
- CaptionBubbleErrorType::MEDIA_FOUNDATION_RENDERER_UNSUPPORTED) {
+ if (HasMediaFoundationError()) {
width = kMaxWidthDip;
content_height =
media_foundation_renderer_error_message_->GetPreferredSize().height();
@@ -1071,7 +1068,8 @@ void CaptionBubble::Hide() {
}
void CaptionBubble::OnInactivityTimeout() {
- Hide();
+ if (HasMediaFoundationError())
+ return;
// Clear the partial and final text in the caption bubble model and the label.
// Does not affect the speech service. The speech service will emit a final
@@ -1082,6 +1080,8 @@ void CaptionBubble::OnInactivityTimeout() {
// contain text cleared by the UI.
if (model_)
model_->ClearText();
+
+ Hide();
}
void CaptionBubble::MediaFoundationErrorCheckboxPressed() {
@@ -1092,6 +1092,12 @@ void CaptionBubble::MediaFoundationErrorCheckboxPressed() {
#endif
}
+bool CaptionBubble::HasMediaFoundationError() {
+ return (model_ && model_->HasError() &&
+ model_->ErrorType() ==
+ CaptionBubbleErrorType::MEDIA_FOUNDATION_RENDERER_UNSUPPORTED);
+}
+
bool CaptionBubble::HasActivity() {
return model_ &&
((inactivity_timer_ && inactivity_timer_->IsRunning()) || HasFocus() ||
diff --git a/chromium/components/live_caption/views/caption_bubble.h b/chromium/components/live_caption/views/caption_bubble.h
index af80ad9a0fc..d60a0d9edfa 100644
--- a/chromium/components/live_caption/views/caption_bubble.h
+++ b/chromium/components/live_caption/views/caption_bubble.h
@@ -158,6 +158,7 @@ class CaptionBubble : public views::BubbleDialogDelegateView {
void OnInactivityTimeout();
void MediaFoundationErrorCheckboxPressed();
+ bool HasMediaFoundationError();
// Unowned. Owned by views hierarchy.
raw_ptr<CaptionBubbleLabel> label_;
diff --git a/chromium/components/live_caption/views/caption_bubble_controller_views.h b/chromium/components/live_caption/views/caption_bubble_controller_views.h
index 8c1e9e82dc9..6a18fa0601b 100644
--- a/chromium/components/live_caption/views/caption_bubble_controller_views.h
+++ b/chromium/components/live_caption/views/caption_bubble_controller_views.h
@@ -12,6 +12,7 @@
#include "base/memory/raw_ptr.h"
#include "components/live_caption/caption_bubble_controller.h"
#include "components/live_caption/views/caption_bubble.h"
+#include "media/mojo/mojom/speech_recognition.mojom.h"
namespace views {
class Widget;
diff --git a/chromium/components/local_state/BUILD.gn b/chromium/components/local_state/BUILD.gn
new file mode 100644
index 00000000000..bc581daf8df
--- /dev/null
+++ b/chromium/components/local_state/BUILD.gn
@@ -0,0 +1,43 @@
+# 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.
+
+import("//tools/grit/preprocess_if_expr.gni")
+import("//tools/typescript/ts_library.gni")
+
+preprocess_folder = "preprocessed"
+
+preprocess_if_expr("preprocess_ts") {
+ in_folder = "./"
+ out_folder = "$target_gen_dir/$preprocess_folder"
+ in_files = [ "local_state.ts" ]
+}
+
+ts_library("build") {
+ root_dir = "$target_gen_dir/$preprocess_folder"
+ in_files = [ "local_state.ts" ]
+ deps = [ "//ui/webui/resources:library" ]
+ extra_deps = [ ":preprocess_ts" ]
+}
+
+source_set("local_state") {
+ sources = [
+ "local_state_utils.cc",
+ "local_state_utils.h",
+ ]
+ deps = [
+ "//base",
+ "//components/prefs",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "local_state_utils_unittest.cc" ]
+
+ deps = [
+ ":local_state",
+ "//base",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/contextual_search/core/DEPS b/chromium/components/local_state/DEPS
index 2990ded3bbf..eac076192dc 100644
--- a/chromium/components/contextual_search/core/DEPS
+++ b/chromium/components/local_state/DEPS
@@ -1,5 +1,3 @@
include_rules = [
- "-content",
- "-third_party/blink",
"+components/prefs",
]
diff --git a/chromium/components/local_state/OWNERS b/chromium/components/local_state/OWNERS
new file mode 100644
index 00000000000..b962dd5b59d
--- /dev/null
+++ b/chromium/components/local_state/OWNERS
@@ -0,0 +1 @@
+file://ui/webui/PLATFORM_OWNERS \ No newline at end of file
diff --git a/chromium/components/local_state/README.md b/chromium/components/local_state/README.md
new file mode 100644
index 00000000000..4b379a2470f
--- /dev/null
+++ b/chromium/components/local_state/README.md
@@ -0,0 +1,2 @@
+This directory contains shared files for the implementation of the
+chrome://local-state WebUI page. \ No newline at end of file
diff --git a/chromium/components/local_state/local_state.html b/chromium/components/local_state/local_state.html
new file mode 100644
index 00000000000..6e98221bd3a
--- /dev/null
+++ b/chromium/components/local_state/local_state.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <meta name="color-scheme" content="light dark">
+ <title>Local State Debug Page</title>
+ <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+ <script type="module" src="local_state.js"></script>
+</head>
+<body>
+ <pre id="content">
+ Loading Local State file...
+ </pre>
+</body>
+</html>
diff --git a/chromium/components/local_state/local_state.ts b/chromium/components/local_state/local_state.ts
new file mode 100644
index 00000000000..ed90b8d8b59
--- /dev/null
+++ b/chromium/components/local_state/local_state.ts
@@ -0,0 +1,26 @@
+// 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.
+
+/**
+ * Javascript for local_state.html, served from chrome://local-state/
+ * This is used to debug the contents of the Local State file.
+ */
+
+// <if expr="is_ios">
+// This is needed for the iOS implementation of chrome.send (to communicate
+// between JS and native).
+// TODO(crbug.com/487000): Remove this once injected by web.
+import 'chrome://resources/js/ios/web_ui.js';
+
+// </if>
+
+import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {$} from 'chrome://resources/js/util.m.js';
+
+// When the page loads, request the JSON local state data from C++.
+document.addEventListener('DOMContentLoaded', function() {
+ sendWithPromise('requestJson').then((localState: string) => {
+ $('content').textContent = localState;
+ });
+});
diff --git a/chromium/components/local_state/local_state_utils.cc b/chromium/components/local_state/local_state_utils.cc
new file mode 100644
index 00000000000..366fa75b32d
--- /dev/null
+++ b/chromium/components/local_state/local_state_utils.cc
@@ -0,0 +1,68 @@
+// Copyright 2022 The Chromium 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/local_state/local_state_utils.h"
+
+#include <string>
+#include <vector>
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+#include "components/prefs/pref_service.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#define ENABLE_FILTERING true
+#else
+#define ENABLE_FILTERING false
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+namespace {
+
+// Returns true if |pref_name| starts with one of the |valid_prefixes|.
+bool HasValidPrefix(const std::string& pref_name,
+ const std::vector<std::string> valid_prefixes) {
+ for (const std::string& prefix : valid_prefixes) {
+ if (base::StartsWith(pref_name, prefix, base::CompareCase::SENSITIVE))
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+namespace internal {
+
+void FilterPrefs(const std::vector<std::string>& valid_prefixes,
+ base::Value& prefs) {
+ std::vector<std::string> prefs_to_remove;
+ for (auto it : prefs.DictItems()) {
+ if (!HasValidPrefix(it.first, valid_prefixes))
+ prefs_to_remove.push_back(it.first);
+ }
+ for (const std::string& pref_to_remove : prefs_to_remove) {
+ bool successfully_removed = prefs.RemovePath(pref_to_remove);
+ DCHECK(successfully_removed);
+ }
+}
+
+} // namespace internal
+
+bool GetPrefsAsJson(PrefService* pref_service, std::string* json_string) {
+ base::Value local_state_values =
+ pref_service->GetPreferenceValues(PrefService::EXCLUDE_DEFAULTS);
+ if (ENABLE_FILTERING) {
+ // Filter out the prefs to only include variations and UMA related fields,
+ // which don't contain PII.
+ std::vector<std::string> allowlisted_prefixes = {"variations",
+ "user_experience_metrics"};
+ internal::FilterPrefs(allowlisted_prefixes, local_state_values);
+ }
+
+ JSONStringValueSerializer serializer(json_string);
+ serializer.set_pretty_print(true);
+ return serializer.Serialize(local_state_values);
+} \ No newline at end of file
diff --git a/chromium/components/local_state/local_state_utils.h b/chromium/components/local_state/local_state_utils.h
new file mode 100644
index 00000000000..7ee0b48c346
--- /dev/null
+++ b/chromium/components/local_state/local_state_utils.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_LOCAL_STATE_LOCAL_STATE_UTILS_H_
+#define COMPONENTS_LOCAL_STATE_LOCAL_STATE_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/values.h"
+#include "components/prefs/pref_service.h"
+
+// Namespace for exposing the method for unit tests.
+namespace internal {
+
+// Removes elements from |prefs| where the key does not match any of the
+// prefixes in |valid_prefixes|.
+void FilterPrefs(const std::vector<std::string>& valid_prefixes,
+ base::Value& prefs);
+
+} // namespace internal
+
+// Gets a pretty-printed string representation of the input |pref_service|.
+// If the return value is true, the result will have been written to
+// |json_string|. On ChromeOS, the local state file contains some information
+// about other user accounts which we don't want to expose to other users. In
+// that case, this will filter out the prefs to only include variations and UMA
+// related fields, which don't contain PII.
+bool GetPrefsAsJson(PrefService* pref_service, std::string* json_string);
+
+#endif // COMPONENTS_LOCAL_STATE_LOCAL_STATE_UTILS_H_ \ No newline at end of file
diff --git a/chromium/components/local_state/local_state_utils_unittest.cc b/chromium/components/local_state/local_state_utils_unittest.cc
new file mode 100644
index 00000000000..97add61ac4d
--- /dev/null
+++ b/chromium/components/local_state/local_state_utils_unittest.cc
@@ -0,0 +1,34 @@
+// 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.
+
+#include "components/local_state/local_state_utils.h"
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(LocalStateUtilsTest, FilterPrefs) {
+ std::vector<std::string> prefixes = {"foo", "bar", "baz"};
+
+ std::vector<std::string> invalid_pref_paths = {"fo", "ar", "afoo"};
+ std::vector<std::string> valid_pref_paths = {"foo", "foom", "bar.stuff"};
+
+ std::vector<std::string> all_pref_paths = invalid_pref_paths;
+ all_pref_paths.insert(all_pref_paths.end(), valid_pref_paths.begin(),
+ valid_pref_paths.end());
+
+ base::Value prefs(base::Value::Type::DICTIONARY);
+ for (const std::string& path : all_pref_paths)
+ prefs.SetStringPath(path, path + "_value");
+
+ internal::FilterPrefs(prefixes, prefs);
+
+ for (const std::string& invalid_path : invalid_pref_paths)
+ EXPECT_FALSE(prefs.FindStringPath(invalid_path));
+
+ for (const std::string& valid_path : valid_pref_paths) {
+ const std::string* result = prefs.FindStringPath(valid_path);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(valid_path + "_value", *result);
+ }
+}
diff --git a/chromium/components/location/android/BUILD.gn b/chromium/components/location/android/BUILD.gn
index faf8947a5d6..91d22bc3e66 100644
--- a/chromium/components/location/android/BUILD.gn
+++ b/chromium/components/location/android/BUILD.gn
@@ -25,6 +25,8 @@ android_library("settings_java") {
deps = [
":location_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//ui/android:ui_no_recycler_view_java",
]
sources =
diff --git a/chromium/components/lookalikes/core/BUILD.gn b/chromium/components/lookalikes/core/BUILD.gn
index d0686c16fea..14c0e085b21 100644
--- a/chromium/components/lookalikes/core/BUILD.gn
+++ b/chromium/components/lookalikes/core/BUILD.gn
@@ -17,7 +17,6 @@ static_library("core") {
"//components/reputation/core:core",
"//components/reputation/core:proto",
"//components/security_interstitials/core",
- "//components/security_state/core:features",
"//components/strings",
"//components/url_formatter",
"//components/url_formatter/spoof_checks/common_words:common",
diff --git a/chromium/components/lookalikes/core/features.cc b/chromium/components/lookalikes/core/features.cc
index 3d1fb0c01c4..a5bb9d4d771 100644
--- a/chromium/components/lookalikes/core/features.cc
+++ b/chromium/components/lookalikes/core/features.cc
@@ -7,13 +7,6 @@
namespace lookalikes {
namespace features {
-// Note: this flag is ignored on iOS. See lookalike_url_util.cc.
-const base::Feature kDetectTargetEmbeddingLookalikes{
- "TargetEmbeddingLookalikes", base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kLookalikeInterstitialForPunycode{
- "LookalikeInterstitialForPunycode", base::FEATURE_ENABLED_BY_DEFAULT};
-
const base::Feature kLookalikeDigitalAssetLinks{
"LookalikeDigitalAssetLinks", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromium/components/lookalikes/core/features.h b/chromium/components/lookalikes/core/features.h
index d85eb6089c2..366e28e7ecc 100644
--- a/chromium/components/lookalikes/core/features.h
+++ b/chromium/components/lookalikes/core/features.h
@@ -11,14 +11,6 @@
namespace lookalikes {
namespace features {
-// This feature enables interstitial warnings for target embedding lookalikes.
-COMPONENT_EXPORT(LOOKALIKES_FEATURES)
-extern const base::Feature kDetectTargetEmbeddingLookalikes;
-
-// This feature enables interstitial warnings for certain punycode domains.
-COMPONENT_EXPORT(LOOKALIKES_FEATURES)
-extern const base::Feature kLookalikeInterstitialForPunycode;
-
// This feature enables Digital Asset Link validations for lookalikes.
COMPONENT_EXPORT(LOOKALIKES_FEATURES)
extern const base::Feature kLookalikeDigitalAssetLinks;
diff --git a/chromium/components/lookalikes/core/lookalike_url_util.cc b/chromium/components/lookalikes/core/lookalike_url_util.cc
index db6cf63f2be..3eb6de533b5 100644
--- a/chromium/components/lookalikes/core/lookalike_url_util.cc
+++ b/chromium/components/lookalikes/core/lookalike_url_util.cc
@@ -29,7 +29,6 @@
#include "components/lookalikes/core/features.h"
#include "components/reputation/core/safety_tips_config.h"
#include "components/security_interstitials/core/pref_names.h"
-#include "components/security_state/core/features.h"
#include "components/url_formatter/spoof_checks/common_words/common_words_util.h"
#include "components/url_formatter/spoof_checks/top_domains/top500_domains.h"
#include "components/url_formatter/spoof_checks/top_domains/top_domain_util.h"
@@ -58,12 +57,6 @@ const char kDigitChars[] = "0123456789";
// "baz" is shorter than kMinTargetE2LDLength.
const size_t kMinE2LDLengthForTargetEmbedding = 4;
-// This list will be added to the static list of common words so common words
-// could be added to the list using a flag if needed.
-const base::FeatureParam<std::string> kRemoveAdditionalCommonWords{
- &lookalikes::features::kDetectTargetEmbeddingLookalikes,
- "additional_common_words", ""};
-
// We might not protect a domain whose e2LD is a common word in target embedding
// based on the TLD that is paired with it. This list supplements words from
// url_formatter::common_words::IsCommonWord().
@@ -384,12 +377,6 @@ bool UsesCommonWord(const reputation::SafetyTipsConfig* config_proto,
return true;
}
}
- std::vector<std::string> additional_common_words =
- base::SplitString(kRemoveAdditionalCommonWords.Get(), ",",
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- if (base::Contains(additional_common_words, domain.domain_without_registry)) {
- return true;
- }
return false;
}
@@ -729,13 +716,10 @@ bool ShouldBlockLookalikeUrlNavigation(LookalikeUrlMatchType match_type) {
// check engaged sites. Otherwise, false positives are too high.
return false;
#else
- return base::FeatureList::IsEnabled(
- lookalikes::features::kDetectTargetEmbeddingLookalikes);
+ return true;
#endif
}
- if (match_type == LookalikeUrlMatchType::kFailedSpoofChecks &&
- base::FeatureList::IsEnabled(
- lookalikes::features::kLookalikeInterstitialForPunycode)) {
+ if (match_type == LookalikeUrlMatchType::kFailedSpoofChecks) {
return true;
}
return match_type == LookalikeUrlMatchType::kSkeletonMatchTop500;
diff --git a/chromium/components/management/resources/images/enterprise_icon.svg b/chromium/components/management/resources/images/enterprise_icon.svg
index 436a7d6a165..14e8edee0c5 100644
--- a/chromium/components/management/resources/images/enterprise_icon.svg
+++ b/chromium/components/management/resources/images/enterprise_icon.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#5f6368"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 0 24 24" width="48" fill="#5f6368"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"/></svg> \ No newline at end of file
diff --git a/chromium/components/media_control/renderer/BUILD.gn b/chromium/components/media_control/renderer/BUILD.gn
index 61da8ffc965..58a525dd133 100644
--- a/chromium/components/media_control/renderer/BUILD.gn
+++ b/chromium/components/media_control/renderer/BUILD.gn
@@ -6,7 +6,9 @@ import("//build/buildflag_header.gni")
import("//build/config/chromecast_build.gni")
declare_args() {
- enable_media_control_logging_override = is_chromecast
+ # TODO(crbug.com/1328991): Remove is_android from below.
+ enable_media_control_logging_override =
+ is_castos || (is_android && enable_cast_receiver)
}
buildflag_header("media_control_buildflags") {
diff --git a/chromium/components/media_message_center/media_notification_view_modern_impl.cc b/chromium/components/media_message_center/media_notification_view_modern_impl.cc
index 625b783b7b8..773278d65ef 100644
--- a/chromium/components/media_message_center/media_notification_view_modern_impl.cc
+++ b/chromium/components/media_message_center/media_notification_view_modern_impl.cc
@@ -377,9 +377,9 @@ MediaNotificationViewModernImpl::MediaNotificationViewModernImpl(
auto labels_container = std::make_unique<views::View>();
labels_container->SetPreferredSize(
- {kLabelsContainerBaseSize.width() -
- kNotificationControlsInsets.width(),
- kLabelsContainerBaseSize.height()});
+ gfx::Size(kLabelsContainerBaseSize.width() -
+ kNotificationControlsInsets.width(),
+ kLabelsContainerBaseSize.height()));
auto* labels_container_layout_manager =
labels_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
diff --git a/chromium/components/media_router/browser/android/BUILD.gn b/chromium/components/media_router/browser/android/BUILD.gn
index 3a028f66020..55593264b6e 100644
--- a/chromium/components/media_router/browser/android/BUILD.gn
+++ b/chromium/components/media_router/browser/android/BUILD.gn
@@ -16,15 +16,16 @@ android_library("java") {
"$google_play_services_package:google_play_services_cast_framework_java",
"$google_play_services_package:google_play_services_cast_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/media/android:java",
"//content/public/android:content_java",
"//services/media_session/public/cpp/android:media_session_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/android_media:android_media_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_collection_collection_java",
- "//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_fragment_fragment_java",
+ "//third_party/androidx:androidx_media_media_java",
"//third_party/androidx:androidx_mediarouter_mediarouter_java",
]
sources = [
@@ -102,6 +103,7 @@ android_library("test_support_java") {
":test_jni_headers",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//components/browser_ui/media/android:java",
"//content/public/android:content_java",
"//content/public/test/android:content_java_test_support",
diff --git a/chromium/components/media_router/browser/media_router_base.h b/chromium/components/media_router/browser/media_router_base.h
index 987be89d524..ca2d985e9e6 100644
--- a/chromium/components/media_router/browser/media_router_base.h
+++ b/chromium/components/media_router/browser/media_router_base.h
@@ -41,7 +41,7 @@ class MediaRouterBase : public MediaRouter {
FRIEND_TEST_ALL_PREFIXES(MediaRouterBaseTest, CreatePresentationIds);
FRIEND_TEST_ALL_PREFIXES(MediaRouterBaseTest, NotifyCallbacks);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceDelegateImplTest,
- ListenForConnnectionStateChange);
+ ListenForConnectionStateChange);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceDelegateImplTest, GetMediaRoutes);
MediaRouterBase();
diff --git a/chromium/components/media_router/browser/media_router_dialog_controller.cc b/chromium/components/media_router/browser/media_router_dialog_controller.cc
index 2f5488d5ae1..c17675b8b1a 100644
--- a/chromium/components/media_router/browser/media_router_dialog_controller.cc
+++ b/chromium/components/media_router/browser/media_router_dialog_controller.cc
@@ -92,8 +92,6 @@ bool MediaRouterDialogController::ShowMediaRouterDialogForPresentation(
}
start_presentation_context_ = std::move(context);
- MediaRouterMetrics::RecordMediaRouterDialogOrigin(
- MediaRouterDialogOpenOrigin::PAGE);
FocusOnMediaRouterDialog(true, MediaRouterDialogOpenOrigin::PAGE);
return true;
}
diff --git a/chromium/components/media_router/browser/media_router_metrics.h b/chromium/components/media_router/browser/media_router_metrics.h
index 273df93bddd..75f493b9fa2 100644
--- a/chromium/components/media_router/browser/media_router_metrics.h
+++ b/chromium/components/media_router/browser/media_router_metrics.h
@@ -53,10 +53,14 @@ enum class DialogActivationLocationAndCastMode {
kAppMenuAndTabMirror,
kAppMenuAndDesktopMirror,
kAppMenuAndLocalFile, // Obsolete.
+ kSharingHubAndPresentation,
+ kSharingHubAndTabMirror,
+ kSharingHubAndDesktopMirror,
// NOTE: Do not reorder existing entries, and add entries only immediately
- // above this line.
- kMaxValue = kAppMenuAndLocalFile
+ // above this line. Remember to also update
+ // tools/metrics/histograms/enums.xml.
+ kMaxValue = kSharingHubAndDesktopMirror
};
// Where the user clicked to open the Media Router dialog.
@@ -69,9 +73,11 @@ enum class MediaRouterDialogOpenOrigin {
PAGE = 3,
APP_MENU = 4,
SYSTEM_TRAY = 5,
+ SHARING_HUB = 6,
- // NOTE: Add entries only immediately above this line.
- TOTAL_COUNT = 6
+ // NOTE: Add entries only immediately above this line. Remember to also update
+ // tools/metrics/histograms/enums.xml.
+ TOTAL_COUNT = 7
};
// The possible outcomes from a route creation response.
@@ -80,7 +86,8 @@ enum class MediaRouterRouteCreationOutcome {
FAILURE_NO_ROUTE = 1,
FAILURE_INVALID_SINK = 2,
- // Note: Add entries only immediately above this line.
+ // Note: Add entries only immediately above this line. Remember to also update
+ // tools/metrics/histograms/enums.xml.
TOTAL_COUNT = 3,
};
@@ -95,7 +102,8 @@ enum class MediaRouterUserAction {
REPLACE_LOCAL_ROUTE = 5,
STOP_REMOTE = 6,
- // Note: Add entries only immediately above this line.
+ // Note: Add entries only immediately above this line. Remember to also update
+ // tools/metrics/histograms/enums.xml.
TOTAL_COUNT = 7
};
diff --git a/chromium/components/media_router/browser/route_message_util.cc b/chromium/components/media_router/browser/route_message_util.cc
index 52ae3d92c1a..3a67603d4ef 100644
--- a/chromium/components/media_router/browser/route_message_util.cc
+++ b/chromium/components/media_router/browser/route_message_util.cc
@@ -45,10 +45,9 @@ PresentationConnectionFromRouteMessage(RouteMessagePtr route_message) {
case RouteMessage::Type::BINARY:
return blink::mojom::PresentationConnectionMessage::NewData(
route_message->data.value());
- default:
- NOTREACHED() << "Unknown RouteMessageType " << route_message->type;
- return blink::mojom::PresentationConnectionMessage::New();
}
+ NOTREACHED() << "Unknown RouteMessageType " << route_message->type;
+ return nullptr;
}
} // namespace message_util
diff --git a/chromium/components/media_router/common/discovery/media_sink_internal.cc b/chromium/components/media_router/common/discovery/media_sink_internal.cc
index 0414cbae54c..683960367e8 100644
--- a/chromium/components/media_router/common/discovery/media_sink_internal.cc
+++ b/chromium/components/media_router/common/discovery/media_sink_internal.cc
@@ -206,7 +206,7 @@ bool CastSinkExtraData::operator==(const CastSinkExtraData& other) const {
return ip_endpoint == other.ip_endpoint && model_name == other.model_name &&
capabilities == other.capabilities &&
cast_channel_id == other.cast_channel_id &&
- discovered_by_dial == other.discovered_by_dial;
+ discovery_type == other.discovery_type;
}
} // namespace media_router
diff --git a/chromium/components/media_router/common/discovery/media_sink_internal.h b/chromium/components/media_router/common/discovery/media_sink_internal.h
index f68ea891955..b71928b22bf 100644
--- a/chromium/components/media_router/common/discovery/media_sink_internal.h
+++ b/chromium/components/media_router/common/discovery/media_sink_internal.h
@@ -17,6 +17,14 @@ namespace media_router {
// Default Cast control port to open Cast Socket.
static constexpr int kCastControlPort = 8009;
+// The method by which the cast sink was discovered.
+enum class CastDiscoveryType {
+ kMdns,
+ kDial,
+ kAccessCodeManualEntry,
+ kAccessCodeRememberedDevice,
+};
+
// Extra data for DIAL media sink.
struct DialSinkExtraData {
net::IPAddress ip_address;
@@ -53,11 +61,8 @@ struct CastSinkExtraData {
// browser reconnects to a device.
int cast_channel_id = 0;
- // True if Cast channel is opened from DIAL sink.
- bool discovered_by_dial = false;
-
- // True if Cast Device was discovered via access code.
- bool discovered_by_access_code = false;
+ // The method used to discover the cast sink.
+ CastDiscoveryType discovery_type = CastDiscoveryType::kMdns;
CastSinkExtraData();
CastSinkExtraData(const CastSinkExtraData& other);
diff --git a/chromium/components/media_router/common/discovery/media_sink_service_base.cc b/chromium/components/media_router/common/discovery/media_sink_service_base.cc
index bf927876f09..0fb1c1c0159 100644
--- a/chromium/components/media_router/common/discovery/media_sink_service_base.cc
+++ b/chromium/components/media_router/common/discovery/media_sink_service_base.cc
@@ -89,6 +89,10 @@ void MediaSinkServiceBase::SetTimerForTest(
discovery_timer_ = std::move(timer);
}
+void MediaSinkServiceBase::AddSinkForTest(const MediaSinkInternal& sink) {
+ sinks_.insert_or_assign(sink.sink().id(), sink);
+}
+
void MediaSinkServiceBase::StartTimer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (discovery_timer_->IsRunning())
diff --git a/chromium/components/media_router/common/discovery/media_sink_service_base.h b/chromium/components/media_router/common/discovery/media_sink_service_base.h
index 5512197d128..155cac3e25f 100644
--- a/chromium/components/media_router/common/discovery/media_sink_service_base.h
+++ b/chromium/components/media_router/common/discovery/media_sink_service_base.h
@@ -83,6 +83,7 @@ class MediaSinkServiceBase {
const MediaSinkInternal* GetSinkByRoute(const MediaRoute& route) const;
void SetTimerForTest(std::unique_ptr<base::OneShotTimer> timer);
+ void AddSinkForTest(const MediaSinkInternal& sink);
protected:
// Called when |discovery_timer_| expires. Informs subclass to report device
diff --git a/chromium/components/media_router/common/mojom/media_router_mojom_traits.cc b/chromium/components/media_router/common/mojom/media_router_mojom_traits.cc
index fc1e99840d3..9abe02ee1e8 100644
--- a/chromium/components/media_router/common/mojom/media_router_mojom_traits.cc
+++ b/chromium/components/media_router/common/mojom/media_router_mojom_traits.cc
@@ -52,14 +52,12 @@ UnionTraits<media_router::mojom::MediaSinkExtraDataDataView,
media_router::MediaSinkInternal>::
GetTag(const media_router::MediaSinkInternal& sink) {
if (sink.is_dial_sink()) {
- return media_router::mojom::MediaSinkExtraDataDataView::Tag::
- DIAL_MEDIA_SINK;
+ return media_router::mojom::MediaSinkExtraDataDataView::Tag::kDialMediaSink;
} else if (sink.is_cast_sink()) {
- return media_router::mojom::MediaSinkExtraDataDataView::Tag::
- CAST_MEDIA_SINK;
+ return media_router::mojom::MediaSinkExtraDataDataView::Tag::kCastMediaSink;
}
NOTREACHED();
- return media_router::mojom::MediaSinkExtraDataDataView::Tag::CAST_MEDIA_SINK;
+ return media_router::mojom::MediaSinkExtraDataDataView::Tag::kCastMediaSink;
}
// static
@@ -119,16 +117,14 @@ bool UnionTraits<media_router::mojom::MediaSinkExtraDataDataView,
Read(media_router::mojom::MediaSinkExtraDataDataView data,
media_router::MediaSinkInternal* out) {
switch (data.tag()) {
- case media_router::mojom::MediaSinkExtraDataDataView::Tag::
- DIAL_MEDIA_SINK: {
+ case media_router::mojom::MediaSinkExtraDataDataView::Tag::kDialMediaSink: {
media_router::DialSinkExtraData extra_data;
if (!data.ReadDialMediaSink(&extra_data))
return false;
out->set_dial_data(extra_data);
return true;
}
- case media_router::mojom::MediaSinkExtraDataDataView::Tag::
- CAST_MEDIA_SINK: {
+ case media_router::mojom::MediaSinkExtraDataDataView::Tag::kCastMediaSink: {
media_router::CastSinkExtraData extra_data;
if (!data.ReadCastMediaSink(&extra_data))
return false;
diff --git a/chromium/components/media_router/common/providers/cast/cast_media_source.cc b/chromium/components/media_router/common/providers/cast/cast_media_source.cc
index 67e4615c525..b862ee25400 100644
--- a/chromium/components/media_router/common/providers/cast/cast_media_source.cc
+++ b/chromium/components/media_router/common/providers/cast/cast_media_source.cc
@@ -9,6 +9,7 @@
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
+#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
@@ -17,7 +18,6 @@
#include "components/cast_channel/cast_message_util.h"
#include "components/cast_channel/enum_table.h"
#include "components/media_router/common/media_source.h"
-#include "net/base/escape.h"
#include "net/base/url_util.h"
#include "third_party/openscreen/src/cast/common/public/cast_streaming_app_ids.h"
#include "url/gurl.h"
@@ -158,7 +158,7 @@ base::flat_map<std::string, std::string> MakeQueryMap(const GURL& url) {
}
// TODO(crbug.com/1291718): Move to common utils? Should this use
-// net::UnescapeURLComponent instead of url::DecodeURLEscapeSequences?
+// base::UnescapeURLComponent instead of url::DecodeURLEscapeSequences?
std::string DecodeURLComponent(const std::string& encoded) {
url::RawCanonOutputT<char16_t> unescaped;
std::string output;
@@ -315,11 +315,11 @@ std::unique_ptr<CastMediaSource> ParseLegacyCastUrl(
base::StringPairs params;
base::SplitStringIntoKeyValuePairs(url.ref(), '=', '/', &params);
for (auto& pair : params) {
- pair.second = net::UnescapeURLComponent(
+ pair.second = base::UnescapeURLComponent(
pair.second,
- net::UnescapeRule::SPACES | net::UnescapeRule::PATH_SEPARATORS |
- net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
- net::UnescapeRule::REPLACE_PLUS_WITH_SPACE);
+ base::UnescapeRule::SPACES | base::UnescapeRule::PATH_SEPARATORS |
+ base::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
+ base::UnescapeRule::REPLACE_PLUS_WITH_SPACE);
}
// Legacy URLs can specify multiple apps.
diff --git a/chromium/components/media_router/test/android/cast_emulator/BUILD.gn b/chromium/components/media_router/test/android/cast_emulator/BUILD.gn
index 14be15c521c..62f60b3e7a6 100644
--- a/chromium/components/media_router/test/android/cast_emulator/BUILD.gn
+++ b/chromium/components/media_router/test/android/cast_emulator/BUILD.gn
@@ -22,7 +22,6 @@ android_library("cast_emulator_java") {
deps = [
"$google_play_services_package:google_play_services_cast_java",
"//base:base_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/androidx:androidx_mediarouter_mediarouter_java",
]
}
diff --git a/chromium/components/memory_pressure/system_memory_pressure_evaluator_fuchsia_unittest.cc b/chromium/components/memory_pressure/system_memory_pressure_evaluator_fuchsia_unittest.cc
index 2f9fc16726f..1a777b58697 100644
--- a/chromium/components/memory_pressure/system_memory_pressure_evaluator_fuchsia_unittest.cc
+++ b/chromium/components/memory_pressure/system_memory_pressure_evaluator_fuchsia_unittest.cc
@@ -89,8 +89,8 @@ TEST_F(SystemMemoryPressureEvaluatorFuchsiaDeathTest, ProviderUnavailable) {
// Spin the loop to allow the evaluator to notice that the Provider is not
// available and verify that this causes a fatal failure.
ASSERT_DEATH(base::RunLoop().RunUntilIdle(),
- "fuchsia\\.memorypressure\\.Provider disconnected unexpectedly: "
- "ZX_ERR_PEER_CLOSED \\(-24\\)");
+ "fuchsia\\.memorypressure\\.Provider disconnected unexpectedly, "
+ "exiting: ZX_ERR_PEER_CLOSED \\(-24\\)");
}
TEST_F(SystemMemoryPressureEvaluatorFuchsiaTest, Basic) {
diff --git a/chromium/components/memory_pressure/system_memory_pressure_evaluator_mac.cc b/chromium/components/memory_pressure/system_memory_pressure_evaluator_mac.cc
index eeeb56b88d8..f0dd3307e01 100644
--- a/chromium/components/memory_pressure/system_memory_pressure_evaluator_mac.cc
+++ b/chromium/components/memory_pressure/system_memory_pressure_evaluator_mac.cc
@@ -18,12 +18,7 @@
#include "base/memory/memory_pressure_monitor.h"
#include "base/threading/sequenced_task_runner_handle.h"
-// Redeclare for partial 10.9 availability.
-DISPATCH_EXPORT const struct dispatch_source_type_s
- _dispatch_source_type_memorypressure;
-
-namespace memory_pressure {
-namespace mac {
+namespace memory_pressure::mac {
base::MemoryPressureListener::MemoryPressureLevel
SystemMemoryPressureEvaluator::MemoryPressureLevelForMacMemoryPressureLevel(
@@ -112,5 +107,4 @@ void SystemMemoryPressureEvaluator::OnMemoryPressureChanged() {
SendCurrentVote(notify);
}
-} // namespace mac
-} // namespace memory_pressure
+} // namespace memory_pressure::mac
diff --git a/chromium/components/messages/android/BUILD.gn b/chromium/components/messages/android/BUILD.gn
index 1f7cbde1705..2617e56d54f 100644
--- a/chromium/components/messages/android/BUILD.gn
+++ b/chromium/components/messages/android/BUILD.gn
@@ -22,6 +22,8 @@ android_library("java") {
deps = [
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/banners/android:java",
"//components/browser_ui/widget/android:java",
"//content/public/android:content_java",
diff --git a/chromium/components/messages/android/internal/BUILD.gn b/chromium/components/messages/android/internal/BUILD.gn
index 46d55acde4d..9a7600f0feb 100644
--- a/chromium/components/messages/android/internal/BUILD.gn
+++ b/chromium/components/messages/android/internal/BUILD.gn
@@ -6,6 +6,7 @@ import("//build/config/android/rules.gni")
android_library("java") {
sources = [
+ "java/src/org/chromium/components/messages/MessageAnimationCoordinator.java",
"java/src/org/chromium/components/messages/MessageAutoDismissTimer.java",
"java/src/org/chromium/components/messages/MessageBannerCoordinator.java",
"java/src/org/chromium/components/messages/MessageBannerMediator.java",
@@ -29,6 +30,7 @@ android_library("java") {
"..:java_resources",
"..:manager_java",
"//base:base_java",
+ "//build/android:build_java",
"//components/browser_ui/widget/android:java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/components/messages/android/internal/java/res/layout/message_banner_view.xml b/chromium/components/messages/android/internal/java/res/layout/message_banner_view.xml
index 68afc88de31..41af96ee600 100644
--- a/chromium/components/messages/android/internal/java/res/layout/message_banner_view.xml
+++ b/chromium/components/messages/android/internal/java/res/layout/message_banner_view.xml
@@ -94,4 +94,15 @@
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:visibility="gone" />
+
+ <ProgressBar
+ style="@style/ProgressBarStyle"
+ android:id="@+id/message_primary_progress_spinner"
+ android:indeterminate="true"
+ android:maxWidth="@dimen/message_primary_button_max_width"
+ android:minWidth="@dimen/button_min_width"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/message_progress_spinner_height"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone" />
</org.chromium.components.messages.MessageBannerView>
diff --git a/chromium/components/messages/android/internal/java/res/values/dimens.xml b/chromium/components/messages/android/internal/java/res/values/dimens.xml
index 83bea478757..43a06476bf2 100644
--- a/chromium/components/messages/android/internal/java/res/values/dimens.xml
+++ b/chromium/components/messages/android/internal/java/res/values/dimens.xml
@@ -28,5 +28,6 @@
<dimen name="message_secondary_menu_max_size_small">180dp</dimen>
<dimen name="message_secondary_menu_max_size_medium">250dp</dimen>
<dimen name="message_secondary_menu_max_size_large">300dp</dimen>
+ <dimen name="message_progress_spinner_height">24dp</dimen>
</resources>
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinator.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinator.java
new file mode 100644
index 00000000000..0488402bb7f
--- /dev/null
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinator.java
@@ -0,0 +1,73 @@
+// Copyright 2022 The Chromium 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.components.messages;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.chromium.base.Log;
+import org.chromium.components.messages.MessageQueueManager.MessageState;
+
+import java.util.List;
+
+/**
+ * Coordinator for toggling animation when message is about to show or hide.
+ */
+public class MessageAnimationCoordinator {
+ private static final String TAG = MessageQueueManager.TAG;
+
+ /**
+ * mCurrentDisplayedMessage refers to the message which is currently visible on the screen
+ * including situations in which the message is already dismissed and hide animation is running.
+ */
+ private MessageState mCurrentDisplayedMessage;
+ private MessageState mLastShownMessage;
+ private MessageQueueDelegate mMessageQueueDelegate;
+
+ public void updateWithoutStacking(
+ @Nullable MessageState candidate, boolean suspended, Runnable onFinished) {
+ if (mCurrentDisplayedMessage == candidate) return;
+ if (mCurrentDisplayedMessage == null) {
+ mCurrentDisplayedMessage = candidate;
+ mMessageQueueDelegate.onStartShowing(() -> {
+ if (mCurrentDisplayedMessage == null) {
+ return;
+ }
+ Log.w(TAG,
+ "MessageStateHandler#shouldShow for message with ID %s and key %s in "
+ + "MessageQueueManager#updateCurrentDisplayedMessage "
+ + "returned %s.",
+ candidate.handler.getMessageIdentifier(), candidate.messageKey,
+ candidate.handler.shouldShow());
+ mCurrentDisplayedMessage.handler.show();
+ mLastShownMessage = mCurrentDisplayedMessage;
+ onFinished.run();
+ });
+ } else {
+ Runnable runnable = () -> {
+ mMessageQueueDelegate.onFinishHiding();
+ mCurrentDisplayedMessage = mLastShownMessage = null;
+ onFinished.run();
+ };
+ if (mLastShownMessage != mCurrentDisplayedMessage) {
+ runnable.run();
+ return;
+ }
+ mCurrentDisplayedMessage.handler.hide(!suspended, runnable);
+ }
+ }
+
+ // TODO(crbug.com/1200974): Add support for stacking animation.
+ public void updateWithStacking(
+ @NonNull List<MessageState> candidates, boolean isSuspended, Runnable onFinished) {}
+
+ void setMessageQueueDelegate(MessageQueueDelegate delegate) {
+ mMessageQueueDelegate = delegate;
+ }
+
+ MessageState getCurrentDisplayedMessage() {
+ return mCurrentDisplayedMessage;
+ }
+}
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
index d299f8c768e..22ee419e81a 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
@@ -8,8 +8,6 @@ import android.animation.Animator;
import android.content.res.Resources;
import androidx.annotation.VisibleForTesting;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import org.chromium.base.Callback;
import org.chromium.base.annotations.MockedInTests;
@@ -60,11 +58,6 @@ class MessageBannerCoordinator {
mTimer = new MessageAutoDismissTimer();
mOnTimeUp = onTimeUp;
view.setSwipeHandler(mMediator);
- ViewCompat.replaceAccessibilityAction(
- view, AccessibilityActionCompat.ACTION_DISMISS, null, (v, c) -> {
- messageDismissed.run();
- return false;
- });
view.setPopupMenuShownListener(
createPopupMenuShownListener(mTimer, mAutodismissDurationMs.get(), mOnTimeUp));
}
@@ -122,6 +115,14 @@ class MessageBannerCoordinator {
});
}
+ void cancelTimer() {
+ mTimer.cancelTimer();
+ }
+
+ void startTimer() {
+ mTimer.startTimer(mAutodismissDurationMs.get(), mOnTimeUp);
+ }
+
void setOnTouchRunnable(Runnable runnable) {
mMediator.setOnTouchRunnable(runnable);
}
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerView.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerView.java
index de98ea4a47a..1c142c21c4e 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerView.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerView.java
@@ -43,7 +43,10 @@ public class MessageBannerView extends BoundedLinearLayout {
private ImageView mIconView;
private TextView mTitle;
private TextViewWithCompoundDrawables mDescription;
+ private @PrimaryWidgetAppearance int mPrimaryWidgetAppearance =
+ PrimaryWidgetAppearance.BUTTON_IF_TEXT_IS_SET;
private TextView mPrimaryButton;
+ private View mPrimaryProgressSpinner;
private ListMenuButton mSecondaryButton;
private View mDivider;
private String mSecondaryButtonMenuText;
@@ -65,6 +68,7 @@ public class MessageBannerView extends BoundedLinearLayout {
mTitle = findViewById(R.id.message_title);
mDescription = findViewById(R.id.message_description);
mPrimaryButton = findViewById(R.id.message_primary_button);
+ mPrimaryProgressSpinner = findViewById(R.id.message_primary_progress_spinner);
mIconView = findViewById(R.id.message_icon);
mSecondaryButton = findViewById(R.id.message_secondary_button);
mDivider = findViewById(R.id.message_divider);
@@ -139,9 +143,25 @@ public class MessageBannerView extends BoundedLinearLayout {
mIconView.setImageDrawable(bitmap);
}
+ void setPrimaryWidgetAppearance(@PrimaryWidgetAppearance int primaryWidgetAppearance) {
+ mPrimaryWidgetAppearance = primaryWidgetAppearance;
+ updatePrimaryWidgetAppearance();
+ }
+
void setPrimaryButtonText(String text) {
- mPrimaryButton.setVisibility(VISIBLE);
mPrimaryButton.setText(text);
+ updatePrimaryWidgetAppearance();
+ }
+
+ private void updatePrimaryWidgetAppearance() {
+ mPrimaryButton.setVisibility(
+ mPrimaryWidgetAppearance == PrimaryWidgetAppearance.BUTTON_IF_TEXT_IS_SET
+ && !TextUtils.isEmpty(mPrimaryButton.getText())
+ ? VISIBLE
+ : GONE);
+ mPrimaryProgressSpinner.setVisibility(
+ mPrimaryWidgetAppearance == PrimaryWidgetAppearance.PROGRESS_SPINNER ? VISIBLE
+ : GONE);
}
void setPrimaryButtonClickListener(OnClickListener listener) {
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewBinder.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewBinder.java
index 654b0840bca..e69a4452b51 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewBinder.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewBinder.java
@@ -17,6 +17,7 @@ import static org.chromium.components.messages.MessageBannerProperties.ON_SECOND
import static org.chromium.components.messages.MessageBannerProperties.ON_TOUCH_RUNNABLE;
import static org.chromium.components.messages.MessageBannerProperties.PRIMARY_BUTTON_CLICK_LISTENER;
import static org.chromium.components.messages.MessageBannerProperties.PRIMARY_BUTTON_TEXT;
+import static org.chromium.components.messages.MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE;
import static org.chromium.components.messages.MessageBannerProperties.RESIZE_DESCRIPTION_ICON;
import static org.chromium.components.messages.MessageBannerProperties.SECONDARY_BUTTON_MENU_TEXT;
import static org.chromium.components.messages.MessageBannerProperties.SECONDARY_ICON;
@@ -42,7 +43,9 @@ import org.chromium.ui.modelutil.PropertyModel;
public class MessageBannerViewBinder {
@SuppressLint("ClickableViewAccessibility")
public static void bind(PropertyModel model, MessageBannerView view, PropertyKey propertyKey) {
- if (propertyKey == PRIMARY_BUTTON_TEXT) {
+ if (propertyKey == PRIMARY_WIDGET_APPEARANCE) {
+ view.setPrimaryWidgetAppearance(model.get(PRIMARY_WIDGET_APPEARANCE));
+ } else if (propertyKey == PRIMARY_BUTTON_TEXT) {
view.setPrimaryButtonText(model.get(PRIMARY_BUTTON_TEXT));
} else if (propertyKey == PRIMARY_BUTTON_CLICK_LISTENER) {
view.setPrimaryButtonClickListener(model.get(PRIMARY_BUTTON_CLICK_LISTENER));
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewTest.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewTest.java
index 76aaad605e9..a030ceddfc8 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewTest.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewTest.java
@@ -12,11 +12,13 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText;
import android.app.Activity;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.test.filters.MediumTest;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
@@ -49,6 +51,7 @@ import org.chromium.ui.test.util.DisableAnimationsTestRule;
@RunWith(BaseJUnit4ClassRunner.class)
@Batch(Batch.UNIT_TESTS)
public class MessageBannerViewTest {
+ private static final String PRIMARY_BUTTON_TEXT = "PrimaryButtonText";
private static final String SECONDARY_BUTTON_MENU_TEXT = "SecondaryActionText";
@ClassRule
@@ -66,6 +69,8 @@ public class MessageBannerViewTest {
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
+ Runnable mPrimaryActionCallback;
+ @Mock
Runnable mSecondaryActionCallback;
MessageBannerView mMessageBannerView;
@@ -224,4 +229,316 @@ public class MessageBannerViewTest {
onView(withText(SECONDARY_BUTTON_MENU_TEXT)).perform(click());
Mockito.verify(mSecondaryActionCallback).run();
}
+
+ /**
+ * Setting PRIMARY_WIDGET_APPEARANCE to BUTTON_IF_TEXT_IS_SET without setting the
+ * PRIMARY_BUTTON_TEXT should mean that no primary widget is visible.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceButtonWithUnsetText() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .with(MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE,
+ PrimaryWidgetAppearance.BUTTON_IF_TEXT_IS_SET)
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ });
+
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+ }
+
+ /**
+ * Setting PRIMARY_WIDGET_APPEARANCE to BUTTON_IF_TEXT_IS_SET with PRIMARY_BUTTON_TEXT set to
+ * null should mean that no primary widget is visible.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceButtonWithNullText() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .with(MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE,
+ PrimaryWidgetAppearance.BUTTON_IF_TEXT_IS_SET)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_TEXT, null)
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ });
+
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+ }
+
+ /**
+ * Setting PRIMARY_WIDGET_APPEARANCE to BUTTON_IF_TEXT_IS_SET with PRIMARY_BUTTON_TEXT set to an
+ * empty string should mean that no primary widget is visible.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceButtonWithEmptyText() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .with(MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE,
+ PrimaryWidgetAppearance.BUTTON_IF_TEXT_IS_SET)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_TEXT, "")
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ });
+
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+ }
+
+ /**
+ * Setting PRIMARY_WIDGET_APPEARANCE to BUTTON_IF_TEXT_IS_SET with PRIMARY_BUTTON_TEXT set to a
+ * non-empty string should show the primary action button.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceButtonWithNonEmptyText() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .with(MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE,
+ PrimaryWidgetAppearance.BUTTON_IF_TEXT_IS_SET)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_TEXT, PRIMARY_BUTTON_TEXT)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_CLICK_LISTENER,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPrimaryActionCallback.run();
+ }
+ })
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ });
+
+ Assert.assertEquals(View.VISIBLE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+
+ onView(withId(R.id.message_primary_button)).perform(click());
+ Mockito.verify(mPrimaryActionCallback).run();
+ }
+
+ /**
+ * Changing PRIMARY_BUTTON_TEXT to a non-empty string when PRIMARY_WIDGET_APPEARANCE is set to
+ * BUTTON_IF_TEXT_IS_SET should show the primary action button.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceButtonChangeTextFromEmptyToNonEmpty() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .with(MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE,
+ PrimaryWidgetAppearance.BUTTON_IF_TEXT_IS_SET)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_TEXT, "")
+ .with(MessageBannerProperties.PRIMARY_BUTTON_CLICK_LISTENER,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPrimaryActionCallback.run();
+ }
+ })
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ // Change the PRIMARY_BUTTON_TEXT to a non-empty string after the view has already been
+ // put together.
+ propertyModel.set(MessageBannerProperties.PRIMARY_BUTTON_TEXT, PRIMARY_BUTTON_TEXT);
+ });
+
+ Assert.assertEquals(View.VISIBLE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+
+ onView(withId(R.id.message_primary_button)).perform(click());
+ Mockito.verify(mPrimaryActionCallback).run();
+ }
+
+ /**
+ * Setting PRIMARY_WIDGET_APPEARANCE to PROGRESS_SPINNER should show the progress spinner.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceProgressSpinner() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .with(MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE,
+ PrimaryWidgetAppearance.PROGRESS_SPINNER)
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ });
+
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.VISIBLE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+ }
+
+ /**
+ * Changing PRIMARY_WIDGET_APPEARANCE to PROGRESS_SPINNER should show the progress spinner.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceChangeFromButtonToProgressSpinner() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .with(MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE,
+ PrimaryWidgetAppearance.BUTTON_IF_TEXT_IS_SET)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_TEXT, PRIMARY_BUTTON_TEXT)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_CLICK_LISTENER,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPrimaryActionCallback.run();
+ }
+ })
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ // Change the PRIMARY_WIDGET_APPEARANCE to PROGRESS_SPINNER after the view has already
+ // been put together.
+ propertyModel.set(MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE,
+ PrimaryWidgetAppearance.PROGRESS_SPINNER);
+ });
+
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.VISIBLE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+ }
+
+ /**
+ * Setting the PRIMARY_BUTTON_TEXT to a non-empty string should not override the
+ * PROGRESS_SPINNER appearance, so the progress spinner should be shown.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceProgressSpinnerWithNonEmptyButtonText() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .with(MessageBannerProperties.PRIMARY_WIDGET_APPEARANCE,
+ PrimaryWidgetAppearance.PROGRESS_SPINNER)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_TEXT, PRIMARY_BUTTON_TEXT)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_CLICK_LISTENER,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPrimaryActionCallback.run();
+ }
+ })
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ });
+
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.VISIBLE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+ }
+
+ /**
+ * With neither PRIMARY_WIDGET_APPEARANCE nor PRIMARY_BUTTON_TEXT set, no primary widget should
+ * be visible, since PRIMARY_WIDGET_APPEARANCE should default to BUTTON_IF_TEXT_IS_SET.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceUnsetWithUnsetText() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ });
+
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+ }
+
+ /**
+ * When PRIMARY_WIDGET_APPEARANCE is left unset, it should default to BUTTON_IF_TEXT_IS_SET, so
+ * setting PRIMARY_BUTTON_TEXT to a non-empty string should show the primary action button.
+ */
+ @Test
+ @MediumTest
+ public void testPrimaryWidgetAppearanceUnsetWithNonEmptyText() {
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ PropertyModel propertyModel =
+ new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+ .with(MessageBannerProperties.MESSAGE_IDENTIFIER,
+ MessageIdentifier.TEST_MESSAGE)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_TEXT, PRIMARY_BUTTON_TEXT)
+ .with(MessageBannerProperties.PRIMARY_BUTTON_CLICK_LISTENER,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPrimaryActionCallback.run();
+ }
+ })
+ .build();
+ PropertyModelChangeProcessor.create(
+ propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+ });
+
+ Assert.assertEquals(View.VISIBLE,
+ mMessageBannerView.findViewById(R.id.message_primary_button).getVisibility());
+ Assert.assertEquals(View.GONE,
+ mMessageBannerView.findViewById(R.id.message_primary_progress_spinner)
+ .getVisibility());
+
+ onView(withId(R.id.message_primary_button)).perform(click());
+ Mockito.verify(mPrimaryActionCallback).run();
+ }
}
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManager.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManager.java
index be6a2ef0e6a..9872f0d09c2 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManager.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManager.java
@@ -5,7 +5,6 @@
package org.chromium.components.messages;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.Log;
@@ -22,18 +21,13 @@ import java.util.Map;
* message and which message to show next.
*/
class MessageQueueManager implements ScopeChangeController.Delegate {
- private static final String TAG = "MessageQueueManager";
- /**
- * mCurrentDisplayedMessage refers to the message which is currently visible on the screen
- * including situations in which the message is already dismissed and hide animation is running.
- */
- @Nullable
- private MessageState mCurrentDisplayedMessage;
- private MessageState mLastShownMessage;
- private MessageQueueDelegate mMessageQueueDelegate;
+ static final String TAG = "MessageQueueManager";
+
// TokenHolder tracking whether the queue should be suspended.
private final TokenHolder mSuppressionTokenHolder =
new TokenHolder(this::onSuspendedStateChange);
+ private final MessageAnimationCoordinator mAnimationCoordinator =
+ new MessageAnimationCoordinator();
/**
* A {@link Map} collection which contains {@code MessageKey} as the key and the corresponding
@@ -74,8 +68,7 @@ class MessageQueueManager implements ScopeChangeController.Delegate {
List<MessageState> messageQueue = mMessageQueues.get(scopeKey);
if (messageQueue == null) {
- messageQueue = new ArrayList<>();
- mMessageQueues.put(scopeKey, messageQueue);
+ mMessageQueues.put(scopeKey, messageQueue = new ArrayList<>());
mScopeChangeController.firstMessageEnqueued(scopeKey);
}
@@ -88,7 +81,7 @@ class MessageQueueManager implements ScopeChangeController.Delegate {
Log.w(TAG, "Currently displaying message with ID %s and key %s.",
candidate.handler.getMessageIdentifier(), candidate.messageKey);
}
- updateCurrentDisplayedMessage(true, candidate);
+ updateCurrentDisplayedMessage(candidate);
if (candidate == messageState) {
MessagesMetrics.recordMessageEnqueuedVisible(message.getMessageIdentifier());
@@ -135,8 +128,8 @@ class MessageQueueManager implements ScopeChangeController.Delegate {
}
message.dismiss(dismissReason);
- if (mCurrentDisplayedMessage == messageState) {
- hideMessage(updateCurrentMessage, updateCurrentMessage);
+ if (mAnimationCoordinator.getCurrentDisplayedMessage() == messageState) {
+ updateCurrentDisplayedMessage(null);
}
MessagesMetrics.recordDismissReason(message.getMessageIdentifier(), dismissReason);
}
@@ -150,7 +143,7 @@ class MessageQueueManager implements ScopeChangeController.Delegate {
}
public void setDelegate(MessageQueueDelegate delegate) {
- mMessageQueueDelegate = delegate;
+ mAnimationCoordinator.setMessageQueueDelegate(delegate);
}
// TODO(crbug.com/1163290): Handle the case in which the scope becomes inactive when the
@@ -169,15 +162,15 @@ class MessageQueueManager implements ScopeChangeController.Delegate {
}
} else if (change.changeType == ChangeType.INACTIVE) {
mScopeStates.put(scopeKey, false);
- updateCurrentDisplayedMessage(change.animateTransition, getNextMessage());
+ updateCurrentDisplayedMessage(getNextMessage());
} else if (change.changeType == ChangeType.ACTIVE) {
mScopeStates.put(scopeKey, true);
- updateCurrentDisplayedMessage(true, getNextMessage());
+ updateCurrentDisplayedMessage(getNextMessage());
}
}
private void onSuspendedStateChange() {
- updateCurrentDisplayedMessage(true, getNextMessage());
+ updateCurrentDisplayedMessage(getNextMessage());
}
private boolean isQueueSuspended() {
@@ -188,27 +181,9 @@ class MessageQueueManager implements ScopeChangeController.Delegate {
// running when we get another scope change signal that should potentially either reverse
// the animation (i.e. going from inactive -> active quickly) or jump to the end (i.e.
// going from animate transition -> don't animate transition.
- private void updateCurrentDisplayedMessage(boolean animateTransition, MessageState candidate) {
- if (mCurrentDisplayedMessage != candidate) {
- if (mCurrentDisplayedMessage == null) {
- mCurrentDisplayedMessage = candidate;
- mMessageQueueDelegate.onStartShowing(() -> {
- if (mCurrentDisplayedMessage == null) {
- return;
- }
- Log.w(TAG,
- "MessageStateHandler#shouldShow for message with ID %s and key %s in "
- + "MessageQueueManager#updateCurrentDisplayedMessage "
- + "returned %s.",
- candidate.handler.getMessageIdentifier(), candidate.messageKey,
- candidate.handler.shouldShow());
- mCurrentDisplayedMessage.handler.show();
- mLastShownMessage = mCurrentDisplayedMessage;
- });
- } else {
- hideMessage(!isQueueSuspended() && animateTransition, !isQueueSuspended());
- }
- }
+ private void updateCurrentDisplayedMessage(MessageState candidate) {
+ mAnimationCoordinator.updateWithoutStacking(candidate, isQueueSuspended(),
+ () -> { updateCurrentDisplayedMessage(getNextMessage()); });
}
void dismissAllMessages(@DismissReason int dismissReason) {
@@ -226,22 +201,6 @@ class MessageQueueManager implements ScopeChangeController.Delegate {
return mMessages;
}
- private void hideMessage(boolean animate, boolean updateCurrentMessage) {
- assert mCurrentDisplayedMessage
- != null
- : "This should not be called when there is no valid currently displayed message.";
- Runnable runnable = () -> {
- mMessageQueueDelegate.onFinishHiding();
- mCurrentDisplayedMessage = mLastShownMessage = null;
- if (updateCurrentMessage) updateCurrentDisplayedMessage(true, getNextMessage());
- };
- if (mLastShownMessage != mCurrentDisplayedMessage) {
- runnable.run();
- return;
- }
- mCurrentDisplayedMessage.handler.hide(animate, runnable);
- }
-
/**
* Iterate the queues of each scope to get the next messages. If multiple messages meet the
* requirements, which can show in the given scope, then the message queued earliest will be
@@ -275,7 +234,6 @@ class MessageQueueManager implements ScopeChangeController.Delegate {
static class MessageState {
private static int sIdNext;
- // TODO(crbug.com/1188980): add priority if necessary.
public final int id;
public final ScopeKey scopeKey;
public final Object messageKey;
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java
index 7931c643bbc..42e15cc0637 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java
@@ -175,7 +175,10 @@ public class MessageQueueManagerTest {
queueManager.setDelegate(mEmptyDelegate);
MessageStateHandler m1 = Mockito.spy(new EmptyMessageStateHandler());
- when(m1.shouldShow()).thenReturn(true, true, true, false);
+ when(m1.shouldShow())
+ .thenReturn(true, true,
+ true, // This is called by #getNextMessage after a message is shown.
+ true, false);
// m1#shouldShow will be invoked and will return true.
queueManager.enqueueMessage(m1, m1, SCOPE_INSTANCE_ID, false);
@@ -190,7 +193,7 @@ public class MessageQueueManagerTest {
messageState = queueManager.getNextMessage();
Assert.assertNull("Next message candidate should be null.", messageState);
- verify(m1, times(4)).shouldShow();
+ verify(m1, times(5)).shouldShow();
queueManager.dismissMessage(m1, DismissReason.TIMER);
verify(m1).hide(anyBoolean(), any());
@@ -451,22 +454,12 @@ public class MessageQueueManagerTest {
MessageStateHandler m1 = Mockito.spy(new EmptyMessageStateHandler());
queueManager.enqueueMessage(m1, m1, SCOPE_INSTANCE_ID, false);
queueManager.onScopeChange(
- new MessageScopeChange(SCOPE_TYPE, SCOPE_INSTANCE_ID, ChangeType.INACTIVE, true));
+ new MessageScopeChange(SCOPE_TYPE, SCOPE_INSTANCE_ID, ChangeType.INACTIVE));
verify(m1,
description(
"A message should be hidden with animation when animationTransition is set true."))
.hide(eq(true), any());
-
- queueManager.onScopeChange(
- new MessageScopeChange(SCOPE_TYPE, SCOPE_INSTANCE_ID, ChangeType.ACTIVE));
- queueManager.onScopeChange(
- new MessageScopeChange(SCOPE_TYPE, SCOPE_INSTANCE_ID, ChangeType.INACTIVE, false));
-
- verify(m1,
- description(
- "A message should be hidden without animation when animationTransition is set false."))
- .hide(eq(false), any());
}
/**
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageScopeChange.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageScopeChange.java
index 60b1b76d6ad..16ee39fd6f5 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageScopeChange.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/MessageScopeChange.java
@@ -22,7 +22,6 @@ public class MessageScopeChange {
public final int scopeTypeId;
public final @ChangeType int changeType;
public final ScopeKey scopeInstanceKey;
- public final boolean animateTransition;
/**
* @param scopeTypeId The {@link MessageScopeType} indicating the type of scope.
@@ -31,20 +30,8 @@ public class MessageScopeChange {
*/
public MessageScopeChange(@MessageScopeType int scopeTypeId, ScopeKey scopeInstanceKey,
@ChangeType int changeType) {
- this(scopeTypeId, scopeInstanceKey, changeType, true);
- }
-
- /**
- * @param scopeTypeId The {@link MessageScopeType} indicating the type of scope.
- * @param scopeInstanceKey An identical object as a key of Scope Instance.
- * @param changeType The {@link ChangeType} indicating the type of change.
- * @param animateTransition Whether animation should be shown to reflect the scope change.
- */
- public MessageScopeChange(@MessageScopeType int scopeTypeId, ScopeKey scopeInstanceKey,
- @ChangeType int changeType, boolean animateTransition) {
this.scopeTypeId = scopeTypeId;
this.scopeInstanceKey = scopeInstanceKey;
this.changeType = changeType;
- this.animateTransition = animateTransition;
}
}
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java
index ae149043190..ef3b97b3158 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java
@@ -10,13 +10,10 @@ import androidx.annotation.Nullable;
import org.chromium.base.ActivityState;
import org.chromium.components.messages.MessageScopeChange.ChangeType;
-import org.chromium.content_public.browser.LoadCommittedDetails;
-import org.chromium.content_public.browser.NavigationController;
-import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.Visibility;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver;
-import org.chromium.ui.base.PageTransition;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.base.WindowAndroid.ActivityStateObserver;
@@ -112,24 +109,17 @@ class ScopeChangeController {
}
@Override
- public void navigationEntryCommitted(LoadCommittedDetails details) {
+ public void didFinishNavigation(NavigationHandle navigationHandle) {
if (mScopeKey.scopeType != MessageScopeType.NAVIGATION) {
return;
}
- if (!details.isMainFrame() || details.isSameDocument() || details.didReplaceEntry()) {
+
+ if (!navigationHandle.isInPrimaryMainFrame() || navigationHandle.isSameDocument()
+ || !navigationHandle.hasCommitted() || navigationHandle.isReload()) {
return;
}
- super.navigationEntryCommitted(details);
-
- NavigationController controller = mScopeKey.webContents.getNavigationController();
- NavigationEntry entry =
- controller.getEntryAtIndex(controller.getLastCommittedEntryIndex());
- int transition = entry.getTransition();
- if ((transition & PageTransition.RELOAD) != PageTransition.RELOAD
- && (transition & PageTransition.IS_REDIRECT_MASK) == 0) {
- destroy();
- }
+ destroy();
}
@Override
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java
index e2c054b8d89..02b02f973a8 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java
@@ -5,12 +5,10 @@
package org.chromium.components.messages;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.description;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import androidx.test.filters.SmallTest;
@@ -22,18 +20,20 @@ import org.mockito.Mockito;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.components.messages.MessageScopeChange.ChangeType;
-import org.chromium.content_public.browser.LoadCommittedDetails;
-import org.chromium.content_public.browser.NavigationController;
-import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.content_public.browser.test.mock.MockWebContents;
-import org.chromium.ui.base.PageTransition;
/**
* A test for {@link ScopeChangeController}.
*/
@RunWith(BaseRobolectricTestRunner.class)
public class ScopeChangeControllerTest {
+ private static final boolean IS_MAIN_FRAME = true;
+ private static final boolean IS_SAME_DOCUMENT = true;
+ private static final boolean IS_RELOAD = true;
+ private static final boolean DID_COMMIT = true;
+
@Test
@SmallTest
public void testNavigationScopeChange() {
@@ -42,12 +42,6 @@ public class ScopeChangeControllerTest {
ScopeChangeController controller = new ScopeChangeController(delegate);
MockWebContents webContents = mock(MockWebContents.class);
- NavigationController navigationController = mock(NavigationController.class);
- NavigationEntry entry = mock(NavigationEntry.class);
- when(webContents.getNavigationController()).thenReturn(navigationController);
- when(navigationController.getLastCommittedEntryIndex()).thenReturn(1);
- when(navigationController.getEntryAtIndex(anyInt())).thenReturn(entry);
- when(entry.getTransition()).thenReturn(PageTransition.HOME_PAGE);
int expectedOnScopeChangeCalls = 0;
ScopeKey key = new ScopeKey(MessageScopeType.NAVIGATION, webContents);
@@ -89,14 +83,36 @@ public class ScopeChangeControllerTest {
Assert.assertEquals("Scope type should be inactive when page is hidden",
ChangeType.INACTIVE, captor.getValue().changeType);
- observer.navigationEntryCommitted(createLoadCommittedDetails(true));
+ observer.didFinishNavigation(
+ createNavigationHandle(IS_MAIN_FRAME, !IS_SAME_DOCUMENT, IS_RELOAD, DID_COMMIT));
verify(delegate,
times(expectedOnScopeChangeCalls)
- .description("Delegate should not be called when entry is replaced"))
+ .description("Delegate should not be called for a refresh"))
.onScopeChange(any());
- observer.navigationEntryCommitted(createLoadCommittedDetails(false));
+ observer.didFinishNavigation(
+ createNavigationHandle(!IS_MAIN_FRAME, !IS_SAME_DOCUMENT, !IS_RELOAD, DID_COMMIT));
+ verify(delegate,
+ times(expectedOnScopeChangeCalls)
+ .description("Delegate should not be called for a subframe"))
+ .onScopeChange(any());
+ observer.didFinishNavigation(
+ createNavigationHandle(IS_MAIN_FRAME, !IS_SAME_DOCUMENT, !IS_RELOAD, !DID_COMMIT));
+ verify(delegate,
+ times(expectedOnScopeChangeCalls)
+ .description("Delegate should not be called for uncommitted navigations"))
+ .onScopeChange(any());
+
+ observer.didFinishNavigation(
+ createNavigationHandle(IS_MAIN_FRAME, IS_SAME_DOCUMENT, !IS_RELOAD, DID_COMMIT));
+ verify(delegate,
+ times(expectedOnScopeChangeCalls)
+ .description("Delegate should not be called for same document navigations"))
+ .onScopeChange(any());
+
+ observer.didFinishNavigation(
+ createNavigationHandle(IS_MAIN_FRAME, !IS_SAME_DOCUMENT, !IS_RELOAD, DID_COMMIT));
expectedOnScopeChangeCalls++;
verify(delegate,
times(expectedOnScopeChangeCalls)
@@ -107,7 +123,6 @@ public class ScopeChangeControllerTest {
ChangeType.DESTROY, captor.getValue().changeType);
observer.onTopLevelNativeWindowChanged(null);
-
expectedOnScopeChangeCalls++;
verify(delegate,
times(expectedOnScopeChangeCalls)
@@ -143,13 +158,18 @@ public class ScopeChangeControllerTest {
Assert.assertEquals("Scope type should be inactive when page is hidden",
ChangeType.INACTIVE, captor.getValue().changeType);
- observer.navigationEntryCommitted(createLoadCommittedDetails(false));
+ observer.didFinishNavigation(
+ createNavigationHandle(IS_MAIN_FRAME, !IS_SAME_DOCUMENT, !IS_RELOAD, DID_COMMIT));
verify(delegate,
times(1).description("Delegate should not be called when navigation is ignored"))
.onScopeChange(any());
}
- private LoadCommittedDetails createLoadCommittedDetails(boolean didReplaceEntry) {
- return new LoadCommittedDetails(-1, null, didReplaceEntry, false, true, -1);
+ private NavigationHandle createNavigationHandle(
+ boolean isMainFrame, boolean isSameDocument, boolean isReload, boolean didCommit) {
+ NavigationHandle handle = new NavigationHandle(0, null, null, null, isMainFrame,
+ isSameDocument, true, null, 0, false, false, false, false, -1, false, isReload);
+ handle.didFinish(null, false, didCommit, false, false, false, 0, 0, 0);
+ return handle;
}
}
diff --git a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java
index 09b481d2de5..d8417f0bbc0 100644
--- a/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java
+++ b/chromium/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java
@@ -15,12 +15,13 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.base.Callback;
import org.chromium.base.supplier.BooleanSupplier;
import org.chromium.base.supplier.Supplier;
+import org.chromium.components.messages.MessageContainer.MessageContainerA11yDelegate;
import org.chromium.ui.modelutil.PropertyModel;
/**
* Coordinator to show / hide a banner message on given container and delegate events.
*/
-public class SingleActionMessage implements MessageStateHandler {
+public class SingleActionMessage implements MessageStateHandler, MessageContainerA11yDelegate {
/**
* The interface that consumers of SingleActionMessage should implement to receive notification
* that the message was dismissed.
@@ -98,6 +99,7 @@ public class SingleActionMessage implements MessageStateHandler {
() -> { mDismissHandler.invoke(mModel, DismissReason.TIMER); });
}
mContainer.addMessage(mView);
+ mContainer.setA11yDelegate(this);
// Wait until the message and the container are measured before showing the message. This
// is required in case the animation set-up requires the height of the container, e.g.
@@ -150,6 +152,22 @@ public class SingleActionMessage implements MessageStateHandler {
}
return true;
}
+
+ @Override
+ public void onA11yFocused() {
+ mMessageBanner.cancelTimer();
+ }
+
+ @Override
+ public void onA11yFocusCleared() {
+ mMessageBanner.startTimer();
+ }
+
+ @Override
+ public void onA11yDismiss() {
+ mDismissHandler.invoke(mModel, DismissReason.GESTURE);
+ }
+
private void handlePrimaryAction(View v) {
// Avoid running the primary action callback if the message has already been dismissed.
if (mMessageDismissed) return;
diff --git a/chromium/components/messages/android/message_enums.h b/chromium/components/messages/android/message_enums.h
index 894bb23cc05..3cf2afdb4c9 100644
--- a/chromium/components/messages/android/message_enums.h
+++ b/chromium/components/messages/android/message_enums.h
@@ -102,6 +102,7 @@ enum class MessageIdentifier {
INSTANT_APPS = 27,
ABOUT_THIS_SITE = 28,
TRANSLATE = 29,
+ OFFER_NOTIFICATION = 30,
// Insert new values before this line.
COUNT
@@ -125,6 +126,17 @@ enum class SecondaryMenuMaxSize {
LARGE = 2, // 300dp -> @dimen/message_secondary_menu_max_size_large
};
+// The primary widget that should be shown in the message.
+//
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.messages
+enum class PrimaryWidgetAppearance {
+ // Default value. Show the primary action button if non-empty text has been
+ // set for the primary action button, otherwise no primary widget is shown.
+ BUTTON_IF_TEXT_IS_SET = 0,
+ // Show a spinning progress indicator that isn't clickable.
+ PROGRESS_SPINNER = 1,
+};
+
} // namespace messages
#endif // COMPONENTS_MESSAGES_ANDROID_MESSAGE_ENUMS_H_
diff --git a/chromium/components/messages/android/message_wrapper.cc b/chromium/components/messages/android/message_wrapper.cc
index 42e0572545d..8fa38e3e44c 100644
--- a/chromium/components/messages/android/message_wrapper.cc
+++ b/chromium/components/messages/android/message_wrapper.cc
@@ -189,7 +189,8 @@ void MessageWrapper::SetSecondaryIconResourceId(int resource_id) {
resource_id);
}
-void MessageWrapper::SetSecondaryActionCallback(base::OnceClosure callback) {
+void MessageWrapper::SetSecondaryActionCallback(
+ base::RepeatingClosure callback) {
secondary_action_callback_ = std::move(callback);
}
@@ -218,12 +219,12 @@ void MessageWrapper::HandleActionClick(JNIEnv* env) {
void MessageWrapper::HandleSecondaryActionClick(JNIEnv* env) {
if (!secondary_action_callback_.is_null())
- std::move(secondary_action_callback_).Run();
+ secondary_action_callback_.Run();
}
void MessageWrapper::HandleSecondaryMenuItemSelected(JNIEnv* env, int item_id) {
if (!secondary_menu_item_selected_callback_.is_null())
- std::move(secondary_menu_item_selected_callback_).Run(item_id);
+ secondary_menu_item_selected_callback_.Run(item_id);
}
void MessageWrapper::HandleDismissCallback(JNIEnv* env, int dismiss_reason) {
diff --git a/chromium/components/messages/android/message_wrapper.h b/chromium/components/messages/android/message_wrapper.h
index 64bc72a3699..e63148e1de1 100644
--- a/chromium/components/messages/android/message_wrapper.h
+++ b/chromium/components/messages/android/message_wrapper.h
@@ -79,7 +79,7 @@ class MessageWrapper {
int GetSecondaryIconResourceId();
void SetSecondaryIconResourceId(int resource_id);
- void SetSecondaryActionCallback(base::OnceClosure callback);
+ void SetSecondaryActionCallback(base::RepeatingClosure callback);
void SetSecondaryMenuItemSelectedCallback(
base::RepeatingCallback<void(int)> callback);
@@ -117,7 +117,7 @@ class MessageWrapper {
private:
base::android::ScopedJavaGlobalRef<jobject> java_message_wrapper_;
base::OnceClosure action_callback_;
- base::OnceClosure secondary_action_callback_;
+ base::RepeatingClosure secondary_action_callback_;
SecondaryMenuItemSelectedCallback secondary_menu_item_selected_callback_;
DismissCallback dismiss_callback_;
// True if message is in queue.
diff --git a/chromium/components/messages/android/messages_feature.cc b/chromium/components/messages/android/messages_feature.cc
index 69c3213063f..3b85d3e0087 100644
--- a/chromium/components/messages/android/messages_feature.cc
+++ b/chromium/components/messages/android/messages_feature.cc
@@ -24,7 +24,10 @@ const base::Feature kMessagesForAndroidNearOomReduction{
"MessagesForAndroidNearOomReduction", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kMessagesForAndroidNotificationBlocked{
- "MessagesForAndroidNotificationBlocked", base::FEATURE_DISABLED_BY_DEFAULT};
+ "MessagesForAndroidNotificationBlocked", base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kMessagesForAndroidOfferNotification{
+ "MessagesForAndroidOfferNotification", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kMessagesForAndroidPasswords{
"MessagesForAndroidPasswords", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -91,6 +94,11 @@ bool IsNearOomReductionMessagesUiEnabled() {
base::FeatureList::IsEnabled(kMessagesForAndroidNearOomReduction);
}
+bool IsOfferNotificationMessagesUiEnabled() {
+ return base::FeatureList::IsEnabled(kMessagesForAndroidInfrastructure) &&
+ base::FeatureList::IsEnabled(kMessagesForAndroidOfferNotification);
+}
+
bool IsPasswordMessagesUiEnabled() {
return base::FeatureList::IsEnabled(kMessagesForAndroidInfrastructure) &&
base::FeatureList::IsEnabled(kMessagesForAndroidPasswords);
diff --git a/chromium/components/messages/android/messages_feature.h b/chromium/components/messages/android/messages_feature.h
index 7865d095189..48a2f6c9414 100644
--- a/chromium/components/messages/android/messages_feature.h
+++ b/chromium/components/messages/android/messages_feature.h
@@ -34,6 +34,10 @@ extern const base::Feature kMessagesForAndroidNearOomReduction;
// Infobars infrastructure.
extern const base::Feature kMessagesForAndroidNotificationBlocked;
+// Feature that controls whether offer notifications use Messages or Infobars
+// infrastructure.
+extern const base::Feature kMessagesForAndroidOfferNotification;
+
// Feature that controls whether "save password" and "saved password
// confirmation" prompts use Messages or Infobars infrastructure.
extern const base::Feature kMessagesForAndroidPasswords;
@@ -82,6 +86,8 @@ bool IsNearOomReductionMessagesUiEnabled();
bool IsNotificationBlockedMessagesUiEnabled();
+bool IsOfferNotificationMessagesUiEnabled();
+
bool IsPasswordMessagesUiEnabled();
bool IsPermissionUpdateMessagesUiEnabled();
diff --git a/chromium/components/messages/android/test/BUILD.gn b/chromium/components/messages/android/test/BUILD.gn
index e6ce55fe1c0..6b690de4f1e 100644
--- a/chromium/components/messages/android/test/BUILD.gn
+++ b/chromium/components/messages/android/test/BUILD.gn
@@ -14,8 +14,8 @@ android_library("test_support_java") {
sources =
[ "java/src/org/chromium/components/messages/MessagesTestHelper.java" ]
deps = [
- "//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
"//components/messages/android:java",
"//components/messages/android/internal:java",
"//ui/android:ui_no_recycler_view_java",
diff --git a/chromium/components/metal_util/device_removal.mm b/chromium/components/metal_util/device_removal.mm
index 29a4cb3e61b..33a8a9253cb 100644
--- a/chromium/components/metal_util/device_removal.mm
+++ b/chromium/components/metal_util/device_removal.mm
@@ -11,23 +11,20 @@
namespace metal {
void RegisterGracefulExitOnDeviceRemoval() {
- if (@available(macOS 10.13, *)) {
- id<NSObject> deviceObserver = nil;
- MTLCopyAllDevicesWithObserver(
- &deviceObserver,
- ^(id<MTLDevice> device, MTLDeviceNotificationName name) {
- if (name == MTLDeviceRemovalRequestedNotification ||
- name == MTLDeviceWasRemovedNotification) {
- // Exit the GPU process without error. The browser process sees
- // this error code as a graceful shutdown, so relaunches the GPU
- // process without incrementing the crash count.
- //
- // Note this wouldn't work nicely with in-process-gpu (it would
- // exit the browser), but we don't support that on macOS anyway.
- base::Process::TerminateCurrentProcessImmediately(0);
- }
- });
- }
+ id<NSObject> deviceObserver = nil;
+ MTLCopyAllDevicesWithObserver(
+ &deviceObserver, ^(id<MTLDevice> device, MTLDeviceNotificationName name) {
+ if (name == MTLDeviceRemovalRequestedNotification ||
+ name == MTLDeviceWasRemovedNotification) {
+ // Exit the GPU process without error. The browser process sees
+ // this error code as a graceful shutdown, so relaunches the GPU
+ // process without incrementing the crash count.
+ //
+ // Note this wouldn't work nicely with in-process-gpu (it would
+ // exit the browser), but we don't support that on macOS anyway.
+ base::Process::TerminateCurrentProcessImmediately(0);
+ }
+ });
}
} // namespace metal
diff --git a/chromium/components/metal_util/hdr_copier_layer.mm b/chromium/components/metal_util/hdr_copier_layer.mm
index f690418f520..fe48c854454 100644
--- a/chromium/components/metal_util/hdr_copier_layer.mm
+++ b/chromium/components/metal_util/hdr_copier_layer.mm
@@ -139,8 +139,7 @@ uint32_t GetTransferFunctionIndex(const gfx::ColorSpace& color_space) {
// Convert from an IOSurface's pixel format to a MTLPixelFormat. Crash on any
// unsupported formats.
-MTLPixelFormat IOSurfaceGetMTLPixelFormat(IOSurfaceRef buffer)
- API_AVAILABLE(macos(10.13)) {
+MTLPixelFormat IOSurfaceGetMTLPixelFormat(IOSurfaceRef buffer) {
uint32_t format = IOSurfaceGetPixelFormat(buffer);
switch (format) {
case kCVPixelFormatType_64RGBAHalf:
@@ -154,7 +153,7 @@ MTLPixelFormat IOSurfaceGetMTLPixelFormat(IOSurfaceRef buffer)
}
base::scoped_nsprotocol<id<MTLRenderPipelineState>> CreateRenderPipelineState(
- id<MTLDevice> device) API_AVAILABLE(macos(10.13)) {
+ id<MTLDevice> device) {
base::scoped_nsprotocol<id<MTLRenderPipelineState>> render_pipeline_state;
base::scoped_nsprotocol<id<MTLLibrary>> library;
diff --git a/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc b/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc
index 292bca545bd..ee38195c811 100644
--- a/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc
+++ b/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc
@@ -52,8 +52,8 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
ChildCallStackProfileCollectorTest& operator=(
const ChildCallStackProfileCollectorTest&) = delete;
- void CollectEmptyProfile() {
- child_collector_.Collect(base::TimeTicks::Now(), SampledProfile());
+ void CollectEmptyProfile(base::TimeTicks start_time) {
+ child_collector_.Collect(start_time, SampledProfile());
}
const std::vector<ChildCallStackProfileCollector::ProfileState>& profiles()
@@ -61,7 +61,8 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
return child_collector_.profiles_;
}
- base::test::SingleThreadTaskEnvironment task_environment_;
+ base::test::SingleThreadTaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
mojo::PendingRemote<mojom::CallStackProfileCollector> collector_remote_;
std::unique_ptr<Receiver> receiver_impl_;
ChildCallStackProfileCollector child_collector_;
@@ -72,36 +73,35 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceProvided) {
EXPECT_EQ(0u, profiles().size());
// Add a profile before providing the interface.
- CollectEmptyProfile();
+ base::TimeTicks start_timestamp = task_environment_.NowTicks();
+ CollectEmptyProfile(start_timestamp);
ASSERT_EQ(1u, profiles().size());
- base::TimeTicks start_timestamp = profiles()[0].start_timestamp;
- EXPECT_GE(base::Milliseconds(10), base::TimeTicks::Now() - start_timestamp);
+ EXPECT_EQ(start_timestamp, profiles()[0].start_timestamp);
// Set the interface. The profiles should be passed to it.
child_collector_.SetParentProfileCollector(std::move(collector_remote_));
- base::RunLoop().RunUntilIdle();
+ task_environment_.FastForwardBy(base::Milliseconds(1));
EXPECT_EQ(0u, profiles().size());
ASSERT_EQ(1u, receiver_impl_->profile_start_times.size());
EXPECT_EQ(start_timestamp, receiver_impl_->profile_start_times[0]);
// Add a profile after providing the interface. It should also be passed.
receiver_impl_->profile_start_times.clear();
- CollectEmptyProfile();
- base::RunLoop().RunUntilIdle();
+ CollectEmptyProfile(start_timestamp);
+ task_environment_.FastForwardBy(base::Milliseconds(1));
EXPECT_EQ(0u, profiles().size());
ASSERT_EQ(1u, receiver_impl_->profile_start_times.size());
- EXPECT_GE(base::Milliseconds(10),
- (base::TimeTicks::Now() - receiver_impl_->profile_start_times[0]));
+ EXPECT_EQ(start_timestamp, receiver_impl_->profile_start_times[0]);
}
TEST_F(ChildCallStackProfileCollectorTest, InterfaceNotProvided) {
EXPECT_EQ(0u, profiles().size());
// Add a profile before providing a null interface.
- CollectEmptyProfile();
+ base::TimeTicks start_timestamp = task_environment_.NowTicks();
+ CollectEmptyProfile(start_timestamp);
ASSERT_EQ(1u, profiles().size());
- EXPECT_GE(base::Milliseconds(10),
- base::TimeTicks::Now() - profiles()[0].start_timestamp);
+ EXPECT_EQ(start_timestamp, profiles()[0].start_timestamp);
// Set the null interface. The profile should be flushed.
child_collector_.SetParentProfileCollector(mojo::NullRemote());
@@ -110,7 +110,7 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceNotProvided) {
// Add a profile after providing a null interface. They should also be
// flushed.
- CollectEmptyProfile();
+ CollectEmptyProfile(start_timestamp);
EXPECT_EQ(0u, profiles().size());
}
diff --git a/chromium/components/metrics/clean_exit_beacon.cc b/chromium/components/metrics/clean_exit_beacon.cc
index 557b53b9147..7a9ecaaeb08 100644
--- a/chromium/components/metrics/clean_exit_beacon.cc
+++ b/chromium/components/metrics/clean_exit_beacon.cc
@@ -43,41 +43,11 @@ using ::variations::kEnabledGroup;
using ::variations::kExtendedSafeModeTrial;
using ::variations::prefs::kVariationsCrashStreak;
-const char kMonitoringStageKey[] = "monitoring_stage";
-
// Denotes whether Chrome should perform clean shutdown steps: signaling that
// Chrome is exiting cleanly and then CHECKing that is has shutdown cleanly.
// This may be modified by SkipCleanShutdownStepsForTesting().
bool g_skip_clean_shutdown_steps = false;
-// Records the monitoring stage in which a previous session failed to exit
-// cleanly.
-void RecordMonitoringStage(base::Value* beacon_file_contents) {
- BeaconMonitoringStage stage;
- if (beacon_file_contents) {
- base::Value* beacon_file_stage = beacon_file_contents->FindKeyOfType(
- kMonitoringStageKey, base::Value::Type::INTEGER);
- if (beacon_file_stage) {
- stage = static_cast<BeaconMonitoringStage>(beacon_file_stage->GetInt());
- } else {
- // The beacon file of Extended Variations Safe Mode experiment group
- // clients may not include the monitoring stage as this info was not added
- // until M100.
- stage = BeaconMonitoringStage::kMissing;
- }
- } else {
- DCHECK_NE(base::FieldTrialList::FindFullName(kExtendedSafeModeTrial),
- kEnabledGroup);
- // Clients that are not in the experiment group always emit kStatusQuo.
- stage = BeaconMonitoringStage::kStatusQuo;
- }
- // The metric should not be emitted when Chrome exited cleanly, i.e. when
- // Chrome was not monitoring for crashes.
- DCHECK_NE(stage, BeaconMonitoringStage::kNotMonitoring);
- UMA_STABILITY_HISTOGRAM_ENUMERATION("UMA.CleanExitBeacon.MonitoringStage",
- stage);
-}
-
// Records the the combined state of two distinct beacons' values in the given
// histogram.
void RecordBeaconConsistency(const std::string& histogram_name,
@@ -140,12 +110,15 @@ void MaybeIncrementCrashStreak(bool did_previous_session_exit_cleanly,
// startup.
if (!did_previous_session_exit_cleanly) {
++num_crashes;
- // Schedule only a Local State write. If the client happens to be in an
- // Extended Variations Safe Mode experiment group that introduces new
- // behavior, the crash streak will be written synchronously to disk later on
- // in startup. See MaybeExtendVariationsSafeMode().
local_state->SetInteger(kVariationsCrashStreak, num_crashes);
+#if BUILDFLAG(IS_ANDROID)
+ // Schedule a Local State write on Android Chrome, WebLayer, and WebView
+ // only as this write is expensive, and other platforms use the beacon file
+ // as the source of truth. For other platforms, the crask streak is written
+ // synchronously to disk later on in startup. See
+ // MaybeExtendVariationsSafeMode() and WriteBeaconValue().
local_state->CommitPendingWrite();
+#endif
}
base::UmaHistogramSparse("Variations.SafeMode.Streak.Crashes",
base::clamp(num_crashes, 0, 100));
@@ -166,18 +139,16 @@ void RecordBeaconFileState(BeaconFileState file_state) {
// 4. The file contents are in the expected format with the expected info.
//
// The file is not expected to exist for clients that have never been in the
-// Extended Variations Safe Mode experiment group, kEnabledGroup. The file may
-// not exist for all experiment group clients because there are some are some
-// edge cases. First, MaybeGetFileContents() is called before clients are
-// assigned to an Extended Variations Safe Mode group, so a client that is later
-// assigned to the experiment group will not have the file in the first session
+// Extended Variations Safe Mode experiment's enabled group. Also, the file may
+// not exist for all enabled-group clients because there are some edge cases.
+// First, MaybeGetFileContents() is called before clients are assigned to an
+// Extended Variations Safe Mode experiment group, so a client that is later
+// assigned to the enabled group will not have the file in the first session
// after updating to or installing a Chrome version with the experiment. Second,
-// Android Chrome experiment group clients with repeated background sessions may
-// never write a beacon file. Finally, it is possible for a user to delete the
-// file or to reset their variations state with kResetVariationState.
-//
-// Note that not all beacon files are expected to have a monitoring stage as
-// this info was added in M100.
+// Android Chrome enabled-group clients with repeated background sessions may
+// never write a beacon file. Third, it is possible for a user to delete the
+// file or to switch groups by resetting their variations state. Finally,
+// clients also switch groups when the FieldTrial name is updated.
std::unique_ptr<base::Value> MaybeGetFileContents(
const base::FilePath& beacon_file_path) {
if (beacon_file_path.empty())
@@ -245,8 +216,10 @@ version_info::Channel GetChannel(version_info::Channel channel) {
}
// Sets up the Extended Variations Safe Mode experiment, whose groups have
-// channel-specific weights. Returns the name of the client's experiment group
-// name, e.g. "Control".
+// platform- and channel-specific weights. Returns the name of the client's
+// experiment group name, e.g. "Control".
+// TODO(crbug/1241702): Remove this once the experiment launches on Android
+// Chrome.
std::string SetUpExtendedSafeModeTrial(version_info::Channel channel) {
int default_group;
scoped_refptr<base::FieldTrial> trial(
@@ -254,14 +227,9 @@ std::string SetUpExtendedSafeModeTrial(version_info::Channel channel) {
kExtendedSafeModeTrial, 100, kDefaultGroup,
base::FieldTrial::ONE_TIME_RANDOMIZED, &default_group));
-#if BUILDFLAG(IS_ANDROID)
- int group_probability = channel == version_info::Channel::STABLE ? 1 : 50;
- trial->AppendGroup(kControlGroup, group_probability);
- trial->AppendGroup(kEnabledGroup, group_probability);
-#else
- // The new behavior is launched on desktop and iOS.
+ // The new behavior launched on desktop and iOS in M102 and on Android Chrome
+ // in M103.
trial->AppendGroup(kEnabledGroup, 100);
-#endif
return trial->group_name();
}
@@ -339,8 +307,6 @@ bool CleanExitBeacon::DidPreviousSessionExitCleanly(
bool did_previous_session_exit_cleanly =
use_beacon_file ? beacon_file_beacon_value.value_or(true)
: local_state_beacon_value.value_or(true);
- if (!did_previous_session_exit_cleanly)
- RecordMonitoringStage(use_beacon_file ? beacon_file_contents : nullptr);
#if BUILDFLAG(IS_IOS)
// For the time being, this is a no-op to avoid interference with the Extended
@@ -359,70 +325,60 @@ void CleanExitBeacon::WriteBeaconValue(bool exited_cleanly,
return;
UpdateLastLiveTimestamp();
+#if BUILDFLAG(IS_ANDROID)
+ if (!extended_monitoring_stage_start_time_.is_null()) {
+ // The time exists, so this is the transition from the extended browser
+ // crash monitoring stage to the status quo stage.
+ //
+ // TODO(crbug/1321989): Clean up this metric and
+ // |extended_monitoring_stage_start_time_| once Android Chrome
+ // stakeholders have enough data on the duration.
+ base::UmaHistogramLongTimes(
+ "UMA.CleanExitBeacon.ExtendedMonitoringStageDuration",
+ base::TimeTicks::Now() - extended_monitoring_stage_start_time_);
+ extended_monitoring_stage_start_time_ = base::TimeTicks(); // Null time.
+ }
+#endif // BUILDFLAG(IS_ANDROID)
+
+ if (has_exited_cleanly_ && has_exited_cleanly_.value() == exited_cleanly) {
+ // It is possible to call WriteBeaconValue() with the same value for
+ // |exited_cleanly| twice during startup and shutdown on some platforms. If
+ // the current beacon value matches |exited_cleanly|, then return here to
+ // skip redundantly updating Local State, writing a beacon file, and on
+ // Windows and iOS, writing to platform-specific locations.
+ return;
+ }
const std::string group_name =
base::FieldTrialList::FindFullName(kExtendedSafeModeTrial);
if (is_extended_safe_mode) {
+ // Only enabled-group clients should extend Variations Safe Mode.
DCHECK_EQ(group_name, kEnabledGroup);
+ // |has_exited_cleanly_| should always be unset before starting to watch for
+ // browser crashes.
+ DCHECK(!has_exited_cleanly_);
+ // When starting to watch for browser crashes in the code covered by
+ // Extended Variations Safe Mode, the only valid value for |exited_cleanly|
+ // is `false`. `true` signals that Chrome should stop watching for crashes.
DCHECK(!exited_cleanly);
-
#if BUILDFLAG(IS_ANDROID)
extended_monitoring_stage_start_time_ = base::TimeTicks::Now();
#endif
- SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
- "Variations.ExtendedSafeMode.WritePrefsTime");
- // The beacon value is written to disk synchronously twice during
- // startup for clients in the Extended Variations Safe Mode experiment
- // group. The first time is via
- // VariationsFieldTrialCreator::MaybeExtendVariationsSafeMode(). This is
- // when Chrome begins monitoring for crashes, i.e. |exited_cleanly| is
- // false. This is the only point at which (a) the WritePrefsTime metric is
- // emitted and (b) the kExtended monitoring stage is written.
- //
- // Later on in startup, such clients call CleanExitBeacon::WriteBeaconFile()
- // again with |exited_cleanly| and |is_extended_safe_mode| set to false via
- // MetricsService::LogNeedForCleanShutdown() for desktop and
- // MetricsService::OnAppEnterForeground() for mobile, which is the status
- // quo point at which Chrome monitors for crashes. At this point, a
- // different monitoring stage is written to the beacon file.
- //
- // For Android, note that Chrome does not monitor for crashes in background
- // sessions. See VariationsFieldTrialCreator::SetUpFieldTrials() and
- // MetricsService::InitializeMetricsState().
- WriteBeaconFile(exited_cleanly, BeaconMonitoringStage::kExtended);
+ WriteBeaconFile(exited_cleanly);
} else {
local_state_->SetBoolean(prefs::kStabilityExitedCleanly, exited_cleanly);
- local_state_->CommitPendingWrite(); // Schedule a write.
#if BUILDFLAG(IS_ANDROID)
- if (!extended_monitoring_stage_start_time_.is_null()) {
- // The time exists, so this is the transition from the extended browser
- // crash monitoring stage to the status quo stage. Only Extended
- // Variations Safe Mode enabled-group clients have the extended monitoring
- // stage.
- // TODO(crbug/1321989): Clean up this metric and
- // |extended_monitoring_stage_start_time_| once Android Chrome
- // stakeholders have enough data on the duration.
- base::UmaHistogramLongTimes(
- "UMA.CleanExitBeacon.ExtendedMonitoringStageDuration",
- base::TimeTicks::Now() - extended_monitoring_stage_start_time_);
- extended_monitoring_stage_start_time_ = base::TimeTicks(); // Null time.
- }
+ // Schedule a Local State write on Android for WebLayer and WebView. Other
+ // platforms use the beacon file as the source of truth.
+ local_state_->CommitPendingWrite();
#endif // BUILDFLAG(IS_ANDROID)
if (group_name == kEnabledGroup) {
// Clients in this group write to the Variations Safe Mode file whenever
// |kStabilityExitedCleanly| is updated. The file is kept in sync with the
// pref because the file is used in the next session.
- //
- // If |exited_cleanly| is true, then Chrome is not monitoring for crashes,
- // so the kNotMonitoringStage is used. Otherwise, kStatusQuo is written
- // because startup has reached the point at which the status quo
- // Variations-Safe-Mode-related code begins watching for crashes. See the
- // comment in the above if block for more details.
- WriteBeaconFile(exited_cleanly,
- exited_cleanly ? BeaconMonitoringStage::kNotMonitoring
- : BeaconMonitoringStage::kStatusQuo);
+ WriteBeaconFile(exited_cleanly);
}
}
@@ -436,6 +392,8 @@ void CleanExitBeacon::WriteBeaconValue(bool exited_cleanly,
#elif BUILDFLAG(IS_IOS)
SetUserDefaultsBeacon(exited_cleanly);
#endif // BUILDFLAG(IS_WIN)
+
+ has_exited_cleanly_ = absl::make_optional(exited_cleanly);
}
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
@@ -472,7 +430,7 @@ void CleanExitBeacon::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterTimePref(prefs::kStabilityBrowserLastLiveTimeStamp,
base::Time(), PrefRegistry::LOSSY_PREF);
- // This variations-safe-mode-related pref is registered here rather than in
+ // This Variations-Safe-Mode-related pref is registered here rather than in
// SafeSeedManager::RegisterPrefs() because the CleanExitBeacon is
// responsible for incrementing this value. (See the comments in
// MaybeIncrementCrashStreak() for more details.)
@@ -509,16 +467,13 @@ void CleanExitBeacon::SkipCleanShutdownStepsForTesting() {
g_skip_clean_shutdown_steps = true;
}
-void CleanExitBeacon::WriteBeaconFile(
- bool exited_cleanly,
- BeaconMonitoringStage monitoring_stage) const {
+void CleanExitBeacon::WriteBeaconFile(bool exited_cleanly) const {
DCHECK_EQ(base::FieldTrialList::FindFullName(kExtendedSafeModeTrial),
kEnabledGroup);
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetBoolKey(prefs::kStabilityExitedCleanly, exited_cleanly);
dict.SetIntKey(kVariationsCrashStreak,
local_state_->GetInteger(kVariationsCrashStreak));
- dict.SetIntKey(kMonitoringStageKey, static_cast<int>(monitoring_stage));
std::string json_string;
JSONStringValueSerializer serializer(&json_string);
diff --git a/chromium/components/metrics/clean_exit_beacon.h b/chromium/components/metrics/clean_exit_beacon.h
index 63f273c56b2..e6bc1619db8 100644
--- a/chromium/components/metrics/clean_exit_beacon.h
+++ b/chromium/components/metrics/clean_exit_beacon.h
@@ -51,26 +51,6 @@ enum class BeaconFileState {
kMaxValue = kMissingBeacon,
};
-// Denotes whether Chrome is monitoring for browser crashes via the
-// CleanExitBeacon, and if so, whether the monitoring is due to the Extended
-// Variations Safe Mode experiment or the status quo code. Exposed for
-// testing.
-enum class BeaconMonitoringStage {
- // The beacon file lacks a monitoring stage. This is possible because the
- // monitoring stage was added in a later milestone. Used by only experiment
- // group clients.
- kMissing = 0,
- // Chrome is not monitoring for crashes.
- kNotMonitoring = 1,
- // Chrome is monitoring for crashes earlier on in startup as a result of the
- // experiment. Used by only experiment group clients.
- kExtended = 2,
- // Chrome is monitoring for crashes in the code covered by the status quo
- // Variations Safe Mode mechanism.
- kStatusQuo = 3,
- kMaxValue = kStatusQuo,
-};
-
// Reads and updates a beacon used to detect whether the previous browser
// process exited cleanly.
class CleanExitBeacon {
@@ -172,10 +152,9 @@ class CleanExitBeacon {
// TODO(crbug/1241702): Update this comment when experimentation is over.
bool DidPreviousSessionExitCleanly(base::Value* beacon_file_contents);
- // Writes |exited_cleanly|, |monitoring_stage|, and the crash streak to the
- // file located at |beacon_file_path_|.
- void WriteBeaconFile(bool exited_cleanly,
- BeaconMonitoringStage monitoring_stage) const;
+ // Writes |exited_cleanly| and the crash streak to the file located at
+ // |beacon_file_path_|.
+ void WriteBeaconFile(bool exited_cleanly) const;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
// Returns whether Chrome exited cleanly in the previous session according to
@@ -225,6 +204,12 @@ class CleanExitBeacon {
bool did_previous_session_exit_cleanly_ = false;
+ // Denotes the current beacon value for this session, which is updated via
+ // CleanExitBeacon::WriteBeaconValue(). When `false`, Chrome is watching for
+ // browser crashes. When `true`, Chrome has stopped watching for crashes. When
+ // unset, Chrome has neither started nor stopped watching for crashes.
+ absl::optional<bool> has_exited_cleanly_ = absl::nullopt;
+
// Where the clean exit beacon and the variations crash streak may be stored
// for some clients in the Extended Variations Safe Mode experiment.
base::FilePath beacon_file_path_;
diff --git a/chromium/components/metrics/clean_exit_beacon_unittest.cc b/chromium/components/metrics/clean_exit_beacon_unittest.cc
index e4ae9b7db1c..8a2cb80e4e2 100644
--- a/chromium/components/metrics/clean_exit_beacon_unittest.cc
+++ b/chromium/components/metrics/clean_exit_beacon_unittest.cc
@@ -40,26 +40,9 @@ using ::variations::SetUpExtendedSafeModeExperiment;
const wchar_t kDummyWindowsRegistryKey[] = L"";
// Creates and returns well-formed beacon file contents with the given values.
-std::string CreateWellFormedBeaconFileContents(
- bool exited_cleanly,
- int crash_streak,
- absl::optional<BeaconMonitoringStage> stage = absl::nullopt) {
+std::string CreateWellFormedBeaconFileContents(bool exited_cleanly,
+ int crash_streak) {
const std::string exited_cleanly_str = exited_cleanly ? "true" : "false";
- if (stage) {
- const std::string stage_str =
- base::NumberToString(static_cast<int>(stage.value()));
- return base::StringPrintf(
- "{\n"
- " \"monitoring_stage\": %s,\n"
- " \"user_experience_metrics.stability.exited_cleanly\": %s,\n"
- " \"variations_crash_streak\": %s\n"
- "}",
- stage_str.data(), exited_cleanly_str.data(),
- base::NumberToString(crash_streak).data());
- }
- // The monitoring stage was added to the beacon file in a later milestone,
- // so beacon files of clients running older Chrome versions may not always
- // have it.
return base::StringPrintf(
"{\n"
" \"user_experience_metrics.stability.exited_cleanly\": %s,\n"
@@ -144,22 +127,6 @@ class BeaconFileConsistencyTest
: public testing::WithParamInterface<BeaconConsistencyTestParams>,
public CleanExitBeaconTest {};
-struct MonitoringStageTestParams {
- const std::string test_name;
- const std::string experiment_group;
- bool exited_cleanly;
- bool is_extended_safe_mode;
- absl::optional<BeaconMonitoringStage> stage;
-};
-
-class MonitoringStageMetricTest
- : public testing::WithParamInterface<MonitoringStageTestParams>,
- public CleanExitBeaconTest {};
-
-class MonitoringStageWritingTest
- : public testing::WithParamInterface<MonitoringStageTestParams>,
- public CleanExitBeaconTest {};
-
// Verify that the crash streak metric is 0 when default pref values are used.
TEST_F(CleanExitBeaconTest, CrashStreakMetricWithDefaultPrefs) {
CleanExitBeacon::ResetStabilityExitedCleanlyForTesting(&prefs_);
@@ -458,174 +425,92 @@ TEST_P(BeaconFileConsistencyTest, BeaconConsistency) {
1);
}
-INSTANTIATE_TEST_SUITE_P(
- All,
- MonitoringStageMetricTest,
- ::testing::Values(
- // Verify that UMA.CleanExitBeacon.MonitoringStage is not emitted when
- // Chrome exited cleanly.
- MonitoringStageTestParams{.test_name = "ControlGroup_CleanExit",
- .experiment_group = variations::kControlGroup,
- .exited_cleanly = true,
- .stage = absl::nullopt},
- MonitoringStageTestParams{.test_name = "ExperimentGroup_CleanExit",
- .experiment_group = variations::kEnabledGroup,
- .exited_cleanly = true,
- .stage = absl::nullopt},
- // Verify that BeaconMonitoringStage::kMissing is emitted when the
- // beacon file does not have a monitoring stage. This can happen because
- // the monitoring stage was added in a later milestone.
- MonitoringStageTestParams{
- .test_name = "ExperimentGroup_DirtyExit_Missing",
- .experiment_group = variations::kEnabledGroup,
- .exited_cleanly = false,
- .stage = BeaconMonitoringStage::kMissing},
- // Verify that BeaconMonitoringStage::kExtended is emitted when the
- // beacon file's monitoring stage indicates that the unclean exit was
- // detected due to the Extended Variations Safe Mode experiment.
- MonitoringStageTestParams{
- .test_name = "ExperimentGroup_DirtyExit_Extended",
- .experiment_group = variations::kEnabledGroup,
- .exited_cleanly = false,
- .stage = BeaconMonitoringStage::kExtended},
- // Verify that BeaconMonitoringStage::kStatusQuo is emitted when the
- // unclean exit was detected as a result of the status quo monitoring
- // code.
- MonitoringStageTestParams{
- .test_name = "ControlGroup_DirtyExit_StatusQuo",
- .experiment_group = variations::kControlGroup,
- .exited_cleanly = false,
- .stage = BeaconMonitoringStage::kStatusQuo},
- MonitoringStageTestParams{
- .test_name = "ExperimentGroup_DirtyExit_StatusQuo",
- .experiment_group = variations::kControlGroup,
- .exited_cleanly = false,
- .stage = BeaconMonitoringStage::kStatusQuo}),
- [](const ::testing::TestParamInfo<MonitoringStageTestParams>& params) {
- return params.param.test_name;
- });
-
-TEST_P(MonitoringStageMetricTest, CheckMonitoringStageMetric) {
- MonitoringStageTestParams params = GetParam();
- SetUpExtendedSafeModeExperiment(params.experiment_group);
-
- // |crash_streak|'s value is arbitrary and not important. We specify it since
- // well-formed beacon files include the streak and set it in Local State to be
- // consistent.
- const int crash_streak = 1;
- // Set up Local State prefs. If the control group behavior is under test, then
- // Local State is used and the beacon file is ignored.
- CleanExitBeacon::SetStabilityExitedCleanlyForTesting(&prefs_,
- params.exited_cleanly);
- prefs_.SetInteger(variations::prefs::kVariationsCrashStreak, crash_streak);
- // Set up the beacon file. If the experiment group behavior is under test,
- // then the beacon file is used and Local State is ignored.
+TEST_F(CleanExitBeaconTest, WriteBeaconValueWhenNotExitingCleanly) {
const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
- const base::FilePath temp_beacon_file_path =
+ const base::FilePath beacon_file_path =
user_data_dir_path.Append(variations::kVariationsFilename);
- ASSERT_LT(0, base::WriteFile(temp_beacon_file_path,
- CreateWellFormedBeaconFileContents(
- /*exited_cleanly=*/params.exited_cleanly,
- /*crash_streak=*/crash_streak,
- /*stage=*/params.stage)
- .data()));
+ ASSERT_FALSE(base::PathExists(beacon_file_path));
- // Create and initialize the CleanExitBeacon.
+ SetUpExtendedSafeModeExperiment(variations::kEnabledGroup);
TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
+ clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/false,
+ /*is_extended_safe_mode=*/true);
- if (params.exited_cleanly) {
- ASSERT_TRUE(clean_exit_beacon.exited_cleanly());
- // Verify that the metric is not emitted when Chrome exited cleanly.
- histogram_tester_.ExpectTotalCount("UMA.CleanExitBeacon.MonitoringStage",
- 0);
- } else {
- ASSERT_FALSE(clean_exit_beacon.exited_cleanly());
- // Verify that the expected BeaconMonitoringStage is emitted.
- histogram_tester_.ExpectUniqueSample("UMA.CleanExitBeacon.MonitoringStage",
- params.stage.value(), 1);
- }
-}
+ // Verify that the beacon file exists and has well-formed contents after
+ // updating the beacon value.
+ EXPECT_TRUE(base::PathExists(beacon_file_path));
+ std::string beacon_file_contents1;
+ ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents1));
+ EXPECT_EQ(beacon_file_contents1,
+ "{\"user_experience_metrics.stability.exited_cleanly\":false,"
+ "\"variations_crash_streak\":0}");
+ // Verify that the BeaconFileWrite metric was emitted.
+ histogram_tester_.ExpectUniqueSample(
+ "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1);
-INSTANTIATE_TEST_SUITE_P(
- All,
- MonitoringStageWritingTest,
- ::testing::Values(
- // Verify that the beacon file is not written for control group clients.
- MonitoringStageTestParams{.test_name = "ControlGroup_CleanExit",
- .experiment_group = variations::kControlGroup,
- .exited_cleanly = true,
- .is_extended_safe_mode = false},
- MonitoringStageTestParams{.test_name = "ControlGroup_DirtyExit",
- .experiment_group = variations::kControlGroup,
- .exited_cleanly = false,
- .is_extended_safe_mode = false},
- // Verify that signaling that Chrome should stop watching for crashes
- // for experiment group clients results in a beacon file with the
- // kNotMonitoring stage.
- MonitoringStageTestParams{
- .test_name = "ExperimentGroup_CleanExit_AsynchronousWrite",
- .experiment_group = variations::kEnabledGroup,
- .exited_cleanly = true,
- .is_extended_safe_mode = false,
- .stage = BeaconMonitoringStage::kNotMonitoring},
- // Verify that signaling that Chrome should watch for crashes with
- // |is_extended_safe_mode| set to true for experiment group clients
- // results in a beacon file with the kExtended stage.
- MonitoringStageTestParams{
- .test_name = "ExperimentGroup_DirtyExit_SynchronousWrite",
- .experiment_group = variations::kEnabledGroup,
- .exited_cleanly = false,
- .is_extended_safe_mode = true,
- .stage = BeaconMonitoringStage::kExtended},
- // Verify that signaling that Chrome should watch for crashes with
- // |is_extended_safe_mode| set to false for experiment group clients
- // results in a beacon file with the kStatusQuo stage.
- MonitoringStageTestParams{
- .test_name = "ExperimentGroup_DirtyExit_AsynchronousWrite",
- .experiment_group = variations::kEnabledGroup,
- .exited_cleanly = false,
- .is_extended_safe_mode = false,
- .stage = BeaconMonitoringStage::kStatusQuo}),
- [](const ::testing::TestParamInfo<MonitoringStageTestParams>& params) {
- return params.param.test_name;
- });
+ // Write the beacon value again. This is done because it is possible for
+ // WriteBeaconValue() to be called twice during startup or shutdown with the
+ // same value for |exited_cleanly|.
+ clean_exit_beacon.WriteBeaconValue(/*exited_cleanly*/ false,
+ /*is_extended_safe_mode=*/false);
-TEST_P(MonitoringStageWritingTest, CheckMonitoringStage) {
- MonitoringStageTestParams params = GetParam();
- const std::string group = params.experiment_group;
- SetUpExtendedSafeModeExperiment(group);
+ // Verify that the beacon file exists and has well-formed contents after
+ // updating the beacon value.
+ EXPECT_TRUE(base::PathExists(beacon_file_path));
+ std::string beacon_file_contents2;
+ ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents2));
+ EXPECT_EQ(beacon_file_contents2,
+ "{\"user_experience_metrics.stability.exited_cleanly\":false,"
+ "\"variations_crash_streak\":0}");
+ // Verify that the BeaconFileWrite metric was not emitted a second time. The
+ // beacon file should not have been written again since the beacon value did
+ // not change.
+ histogram_tester_.ExpectUniqueSample(
+ "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1);
+}
+TEST_F(CleanExitBeaconTest, WriteBeaconValueWhenExitingCleanly) {
const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
- const base::FilePath expected_beacon_file_path =
+ const base::FilePath beacon_file_path =
user_data_dir_path.Append(variations::kVariationsFilename);
- ASSERT_FALSE(base::PathExists(expected_beacon_file_path));
+ ASSERT_FALSE(base::PathExists(beacon_file_path));
- // Create and initialize the CleanExitBeacon.
+ SetUpExtendedSafeModeExperiment(variations::kEnabledGroup);
TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
+ clean_exit_beacon.WriteBeaconValue(/*exited_cleanly=*/true,
+ /*is_extended_safe_mode=*/false);
- clean_exit_beacon.WriteBeaconValue(params.exited_cleanly,
- params.is_extended_safe_mode);
-
- // Check that experiment group clients have a beacon file and that control
- // group clients do not.
- EXPECT_EQ(group == variations::kEnabledGroup,
- base::PathExists(expected_beacon_file_path));
-
- if (group == variations::kEnabledGroup) {
- // For experiment group clients, check the beacon file contents.
- std::string beacon_file_contents;
- ASSERT_TRUE(base::ReadFileToString(expected_beacon_file_path,
- &beacon_file_contents));
-
- const std::string expected_stage =
- "monitoring_stage\":" +
- base::NumberToString(static_cast<int>(params.stage.value()));
- const std::string exited_cleanly = params.exited_cleanly ? "true" : "false";
- const std::string expected_beacon_value =
- "exited_cleanly\":" + exited_cleanly;
- EXPECT_TRUE(base::Contains(beacon_file_contents, expected_stage));
- EXPECT_TRUE(base::Contains(beacon_file_contents, expected_beacon_value));
- }
+ // Verify that the beacon file exists and has well-formed contents after
+ // updating the beacon value.
+ EXPECT_TRUE(base::PathExists(beacon_file_path));
+ std::string beacon_file_contents1;
+ ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents1));
+ EXPECT_EQ(beacon_file_contents1,
+ "{\"user_experience_metrics.stability.exited_cleanly\":true,"
+ "\"variations_crash_streak\":0}");
+ // Verify that the BeaconFileWrite metric was emitted.
+ histogram_tester_.ExpectUniqueSample(
+ "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1);
+
+ // Write the beacon value again. This is done because it is possible for
+ // WriteBeaconValue() to be called twice during startup or shutdown with the
+ // same value for |exited_cleanly|.
+ clean_exit_beacon.WriteBeaconValue(/*exited_cleanly*/ true,
+ /*is_extended_safe_mode=*/false);
+
+ // Verify that the beacon file exists and has well-formed contents after
+ // updating the beacon value.
+ EXPECT_TRUE(base::PathExists(beacon_file_path));
+ std::string beacon_file_contents2;
+ ASSERT_TRUE(base::ReadFileToString(beacon_file_path, &beacon_file_contents2));
+ EXPECT_EQ(beacon_file_contents2,
+ "{\"user_experience_metrics.stability.exited_cleanly\":true,"
+ "\"variations_crash_streak\":0}");
+ // Verify that the BeaconFileWrite metric was not emitted a second time. The
+ // beacon file should not have been written again since the beacon value did
+ // not change.
+ histogram_tester_.ExpectUniqueSample(
+ "Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1);
}
// Verify that attempting to write synchronously DCHECKs for clients that do not
@@ -643,8 +528,6 @@ TEST_F(CleanExitBeaconTest,
// Verify metrics.
histogram_tester_.ExpectTotalCount(
- "Variations.ExtendedSafeMode.WritePrefsTime", 0);
- histogram_tester_.ExpectTotalCount(
"Variations.ExtendedSafeMode.BeaconFileWrite", 0);
}
diff --git a/chromium/components/metrics/content/subprocess_metrics_provider_browsertest.cc b/chromium/components/metrics/content/subprocess_metrics_provider_browsertest.cc
index df52b0bc7d0..64a2a801f99 100644
--- a/chromium/components/metrics/content/subprocess_metrics_provider_browsertest.cc
+++ b/chromium/components/metrics/content/subprocess_metrics_provider_browsertest.cc
@@ -74,18 +74,18 @@ class SubprocessMetricsProviderBrowserTest
base::PersistentHistogramAllocator* GetMainFrameAllocator() {
return get_allocators_by_id().Lookup(
- shell()->web_contents()->GetMainFrame()->GetProcess()->GetID());
+ shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID());
}
void SimulateRenderProcessExit() {
provider_->RenderProcessExited(
- shell()->web_contents()->GetMainFrame()->GetProcess(),
+ shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(),
content::ChildProcessTerminationInfo());
}
void SimulateRenderProcessHostDestroyed() {
provider_->RenderProcessHostDestroyed(
- shell()->web_contents()->GetMainFrame()->GetProcess());
+ shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
}
protected:
@@ -153,7 +153,7 @@ IN_PROC_BROWSER_TEST_F(SubprocessMetricsProviderBrowserTest,
<< " The histogram in the context is " << render_process_histogram;
auto* main_frame_process_host =
- shell()->web_contents()->GetMainFrame()->GetProcess();
+ shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
SimulateRenderProcessHostDestroyed();
// Verify the observer removed.
EXPECT_FALSE(
@@ -200,7 +200,7 @@ IN_PROC_BROWSER_TEST_F(SubprocessMetricsProviderBrowserTest,
<< " The histogram in the context is " << render_process_histogram;
auto* main_frame_process_host =
- shell()->web_contents()->GetMainFrame()->GetProcess();
+ shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
SimulateRenderProcessHostDestroyed();
// Verify the observer removed.
EXPECT_FALSE(
diff --git a/chromium/components/metrics/daily_event.h b/chromium/components/metrics/daily_event.h
index 40314280d63..db61133b66d 100644
--- a/chromium/components/metrics/daily_event.h
+++ b/chromium/components/metrics/daily_event.h
@@ -6,6 +6,7 @@
#define COMPONENTS_METRICS_DAILY_EVENT_H_
#include <memory>
+#include <string>
#include <vector>
#include "base/memory/raw_ptr.h"
diff --git a/chromium/components/metrics/field_trials_provider.cc b/chromium/components/metrics/field_trials_provider.cc
index 26936025b66..e55dea9f2bb 100644
--- a/chromium/components/metrics/field_trials_provider.cc
+++ b/chromium/components/metrics/field_trials_provider.cc
@@ -35,7 +35,6 @@ FieldTrialsProvider::~FieldTrialsProvider() = default;
void FieldTrialsProvider::GetFieldTrialIds(
std::vector<ActiveGroupId>* field_trial_ids) const {
- // We use the default field trial suffixing (no suffix).
variations::GetFieldTrialActiveGroupIds(suffix_, field_trial_ids);
}
@@ -89,7 +88,7 @@ void FieldTrialsProvider::GetAndWriteFieldTrials(
if (registry_) {
std::vector<ActiveGroupId> synthetic_trials;
registry_->GetSyntheticFieldTrialsOlderThan(log_creation_time_,
- &synthetic_trials);
+ &synthetic_trials, suffix_);
WriteFieldTrials(synthetic_trials, system_profile_proto);
}
}
diff --git a/chromium/components/metrics/field_trials_provider.h b/chromium/components/metrics/field_trials_provider.h
index 56f869aedb8..12eb221f9ac 100644
--- a/chromium/components/metrics/field_trials_provider.h
+++ b/chromium/components/metrics/field_trials_provider.h
@@ -43,9 +43,9 @@ class FieldTrialsProvider : public metrics::MetricsProvider {
void SetLogCreationTimeForTesting(base::TimeTicks time);
private:
- // Overrideable for testing.
- virtual void GetFieldTrialIds(
- std::vector<ActiveGroupId>* field_trial_ids) const;
+ // Populates |field_trial_ids| with currently active field trials groups. The
+ // trial and group names are suffixed with |suffix_| before being hashed.
+ void GetFieldTrialIds(std::vector<ActiveGroupId>* field_trial_ids) const;
// Gets active FieldTrials and SyntheticFieldTrials and populates
// |system_profile_proto| with them.
diff --git a/chromium/components/metrics/field_trials_provider_unittest.cc b/chromium/components/metrics/field_trials_provider_unittest.cc
index e7d934f0eb9..dbc26a5a2b5 100644
--- a/chromium/components/metrics/field_trials_provider_unittest.cc
+++ b/chromium/components/metrics/field_trials_provider_unittest.cc
@@ -4,6 +4,7 @@
#include "components/metrics/field_trials_provider.h"
+#include "base/metrics/field_trial.h"
#include "base/threading/platform_thread.h"
#include "components/variations/active_field_trials.h"
#include "components/variations/synthetic_trial_registry.h"
@@ -11,32 +12,36 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/system_profile.pb.h"
+using ActiveGroup = base::FieldTrial::ActiveGroup;
+
namespace variations {
namespace {
-const ActiveGroupId kFieldTrialIds[] = {{37, 43}, {13, 47}, {23, 17}};
-const ActiveGroupId kSyntheticTrialIds[] = {{55, 15}, {66, 16}};
-const ActiveGroupId kAllTrialIds[] = {{37, 43},
- {13, 47},
- {23, 17},
- {55, 15},
- {66, 16}};
-
-class TestProvider : public FieldTrialsProvider {
- public:
- TestProvider(SyntheticTrialRegistry* registry, base::StringPiece suffix)
- : FieldTrialsProvider(registry, suffix) {}
- ~TestProvider() override {}
-
- void GetFieldTrialIds(
- std::vector<ActiveGroupId>* field_trial_ids) const override {
- ASSERT_TRUE(field_trial_ids->empty());
- for (const ActiveGroupId& id : kFieldTrialIds) {
- field_trial_ids->push_back(id);
- }
- }
-};
+constexpr const char* kSuffix = "UKM";
+
+const ActiveGroup kFieldTrials[] = {{"Trial1", "Group1"},
+ {"Trial2", "Group2"},
+ {"Trial3", "Group3"}};
+const ActiveGroup kSyntheticFieldTrials[] = {{"Synthetic1", "SyntheticGroup1"},
+ {"Synthetic2", "SyntheticGroup2"}};
+
+ActiveGroupId ToActiveGroupId(ActiveGroup active_group,
+ std::string suffix = "");
+
+const ActiveGroupId kFieldTrialIds[] = {ToActiveGroupId(kFieldTrials[0]),
+ ToActiveGroupId(kFieldTrials[1]),
+ ToActiveGroupId(kFieldTrials[2])};
+const ActiveGroupId kAllTrialIds[] = {
+ ToActiveGroupId(kFieldTrials[0]), ToActiveGroupId(kFieldTrials[1]),
+ ToActiveGroupId(kFieldTrials[2]), ToActiveGroupId(kSyntheticFieldTrials[0]),
+ ToActiveGroupId(kSyntheticFieldTrials[1])};
+const ActiveGroupId kAllTrialIdsWithSuffixes[] = {
+ ToActiveGroupId(kFieldTrials[0], kSuffix),
+ ToActiveGroupId(kFieldTrials[1], kSuffix),
+ ToActiveGroupId(kFieldTrials[2], kSuffix),
+ ToActiveGroupId(kSyntheticFieldTrials[0], kSuffix),
+ ToActiveGroupId(kSyntheticFieldTrials[1], kSuffix)};
// Check that the field trials in |system_profile| correspond to |expected|.
void CheckFieldTrialsInSystemProfile(
@@ -50,26 +55,44 @@ void CheckFieldTrialsInSystemProfile(
}
}
+ActiveGroupId ToActiveGroupId(ActiveGroup active_group, std::string suffix) {
+ return MakeActiveGroupId(active_group.trial_name + suffix,
+ active_group.group_name + suffix);
+}
+
} // namespace
class FieldTrialsProviderTest : public ::testing::Test {
public:
- FieldTrialsProviderTest() {}
- ~FieldTrialsProviderTest() override {}
+ FieldTrialsProviderTest() = default;
+ ~FieldTrialsProviderTest() override = default;
protected:
+ void SetUp() override {
+ // Register the field trials.
+ for (const ActiveGroup& trial : kFieldTrials) {
+ base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial(
+ trial.trial_name, trial.group_name);
+ // Call group() to finalize and mark the field trial as active.
+ field_trial->group();
+ }
+ }
+
// Register trials which should get recorded.
void RegisterExpectedSyntheticTrials() {
- for (const ActiveGroupId& id : kSyntheticTrialIds) {
+ for (const ActiveGroup& trial : kSyntheticFieldTrials) {
registry_.RegisterSyntheticFieldTrial(SyntheticTrialGroup(
- id.name, id.group,
+ trial.trial_name, trial.group_name,
+ /*annotation_mode=*/
variations::SyntheticTrialAnnotationMode::kNextLog));
}
}
// Register trial which shouldn't get recorded.
void RegisterExtraSyntheticTrial() {
registry_.RegisterSyntheticFieldTrial(SyntheticTrialGroup(
- 100, 1000, variations::SyntheticTrialAnnotationMode::kNextLog));
+ "ExtraSynthetic", "ExtraGroup",
+ /*annotation_mode=*/
+ variations::SyntheticTrialAnnotationMode::kNextLog));
}
// Waits until base::TimeTicks::Now() no longer equals |value|. This should
@@ -84,7 +107,7 @@ class FieldTrialsProviderTest : public ::testing::Test {
};
TEST_F(FieldTrialsProviderTest, ProvideSyntheticTrials) {
- TestProvider provider(&registry_, base::StringPiece());
+ FieldTrialsProvider provider(&registry_, base::StringPiece());
RegisterExpectedSyntheticTrials();
// Make sure these trials are older than the log.
@@ -108,7 +131,7 @@ TEST_F(FieldTrialsProviderTest, ProvideSyntheticTrials) {
}
TEST_F(FieldTrialsProviderTest, NoSyntheticTrials) {
- TestProvider provider(nullptr, base::StringPiece());
+ FieldTrialsProvider provider(nullptr, base::StringPiece());
metrics::SystemProfileProto proto;
provider.ProvideSystemProfileMetricsWithLogCreationTime(base::TimeTicks(),
@@ -131,7 +154,7 @@ TEST_F(FieldTrialsProviderTest, ProvideCurrentSessionData) {
trial->set_name_id(1);
trial->set_group_id(1);
- TestProvider provider(&registry_, base::StringPiece());
+ FieldTrialsProvider provider(&registry_, base::StringPiece());
RegisterExpectedSyntheticTrials();
WaitUntilTimeChanges(base::TimeTicks::Now());
provider.SetLogCreationTimeForTesting(base::TimeTicks::Now());
@@ -143,4 +166,21 @@ TEST_F(FieldTrialsProviderTest, ProvideCurrentSessionData) {
CheckFieldTrialsInSystemProfile(uma_log.system_profile(), kAllTrialIds);
}
+TEST_F(FieldTrialsProviderTest, GetAndWriteFieldTrialsWithSuffixes) {
+ metrics::ChromeUserMetricsExtension uma_log;
+ uma_log.system_profile();
+
+ FieldTrialsProvider provider(&registry_, kSuffix);
+ RegisterExpectedSyntheticTrials();
+ WaitUntilTimeChanges(base::TimeTicks::Now());
+ provider.SetLogCreationTimeForTesting(base::TimeTicks::Now());
+
+ provider.ProvideCurrentSessionData(&uma_log);
+
+ EXPECT_EQ(std::size(kAllTrialIdsWithSuffixes),
+ static_cast<size_t>(uma_log.system_profile().field_trial_size()));
+ CheckFieldTrialsInSystemProfile(uma_log.system_profile(),
+ kAllTrialIdsWithSuffixes);
+}
+
} // namespace variations
diff --git a/chromium/components/metrics/file_metrics_provider.cc b/chromium/components/metrics/file_metrics_provider.cc
index 698f02d7e06..9c67ac91ce5 100644
--- a/chromium/components/metrics/file_metrics_provider.cc
+++ b/chromium/components/metrics/file_metrics_provider.cc
@@ -53,10 +53,8 @@ struct SourceOptions {
bool is_read_only;
};
-enum : int {
- // Opening a file typically requires at least these flags.
- STD_OPEN = base::File::FLAG_OPEN | base::File::FLAG_READ,
-};
+// Opening a file typically requires at least these flags.
+constexpr int STD_OPEN = base::File::FLAG_OPEN | base::File::FLAG_READ;
constexpr SourceOptions kSourceOptions[] = {
// SOURCE_HISTOGRAMS_ATOMIC_FILE
diff --git a/chromium/components/metrics/generate_expired_histograms_array.gni b/chromium/components/metrics/generate_expired_histograms_array.gni
index dc0e089cec5..e649e4ca735 100644
--- a/chromium/components/metrics/generate_expired_histograms_array.gni
+++ b/chromium/components/metrics/generate_expired_histograms_array.gni
@@ -118,6 +118,7 @@ template("generate_expired_histograms_array") {
"//tools/metrics/histograms/metadata/payment/histograms.xml",
"//tools/metrics/histograms/metadata/pcscan/histograms.xml",
"//tools/metrics/histograms/metadata/pdf/histograms.xml",
+ "//tools/metrics/histograms/metadata/performance_manager/histograms.xml",
"//tools/metrics/histograms/metadata/permissions/histograms.xml",
"//tools/metrics/histograms/metadata/phonehub/histograms.xml",
"//tools/metrics/histograms/metadata/platform/histograms.xml",
diff --git a/chromium/components/metrics/machine_id_provider_nonwin.cc b/chromium/components/metrics/machine_id_provider_nonwin.cc
index d37dbc36af1..71b807a5404 100644
--- a/chromium/components/metrics/machine_id_provider_nonwin.cc
+++ b/chromium/components/metrics/machine_id_provider_nonwin.cc
@@ -6,6 +6,7 @@
#include <stdint.h>
+#include "base/check.h"
#include "base/system/sys_info.h"
namespace metrics {
diff --git a/chromium/components/metrics/metrics_data_validation.cc b/chromium/components/metrics/metrics_data_validation.cc
index 99202355fc5..72cc3ba90d0 100644
--- a/chromium/components/metrics/metrics_data_validation.cc
+++ b/chromium/components/metrics/metrics_data_validation.cc
@@ -40,10 +40,9 @@ const base::FeatureParam<double> kLogNormalDelta{
const base::FeatureParam<double> kLogNormalStdDev{
&kNonUniformityValidationFeature, "stdDev", 1.238};
-int GetPseudoMetricsSample(double sample) {
- return base::saturated_cast<int>(sample *
- internal::kMultiplicativeFactor.Get() +
- internal::kAdditiveFactor.Get());
+double GetPseudoMetricsSample(double sample) {
+ return sample * internal::kMultiplicativeFactor.Get() +
+ internal::kAdditiveFactor.Get();
}
base::TimeDelta GetPseudoMetricsSample(base::TimeDelta sample) {
diff --git a/chromium/components/metrics/metrics_data_validation.h b/chromium/components/metrics/metrics_data_validation.h
index 165f017cfc6..8ee554665c1 100644
--- a/chromium/components/metrics/metrics_data_validation.h
+++ b/chromium/components/metrics/metrics_data_validation.h
@@ -58,9 +58,9 @@ extern const base::FeatureParam<double> kLogNormalStdDev;
//
// Returns the sample value for a pseudo metric given the |sample| from the real
// metric and the assigned field trial group. The input type is double because
-// we don't want to lose precision before applying transformation. The output
-// type is int because things logged to histograms are ints.
-int GetPseudoMetricsSample(double sample);
+// we don't want to lose precision before applying transformation.
+double GetPseudoMetricsSample(double sample);
+
// Returns the TimeDelta for a pseudo metric given the |sample| from the real
// metric and the assigned field trial group. The unit of the additive factor
// (b) is milliseconds.
diff --git a/chromium/components/metrics/metrics_data_validation_unittest.cc b/chromium/components/metrics/metrics_data_validation_unittest.cc
index 592440c04b3..2c60c027061 100644
--- a/chromium/components/metrics/metrics_data_validation_unittest.cc
+++ b/chromium/components/metrics/metrics_data_validation_unittest.cc
@@ -17,7 +17,7 @@ TEST(MetricsDataValidationTest, TestGetPseudoMetricsSampleNumeric) {
base::test::ScopedFeatureList scoped_feature_list;
// When the feature is not enabled, |sample| should not be changed.
- EXPECT_EQ(GetPseudoMetricsSample(sample), sample);
+ EXPECT_DOUBLE_EQ(GetPseudoMetricsSample(sample), sample);
}
{
@@ -28,7 +28,7 @@ TEST(MetricsDataValidationTest, TestGetPseudoMetricsSampleNumeric) {
{{"multiplicative_factor", "1.02"}});
// Added a small effect size. Make sure it relects on the pseudo sample.
- EXPECT_EQ(GetPseudoMetricsSample(sample), 102);
+ EXPECT_DOUBLE_EQ(GetPseudoMetricsSample(sample), 102);
}
{
@@ -40,7 +40,7 @@ TEST(MetricsDataValidationTest, TestGetPseudoMetricsSampleNumeric) {
// Added a big effect size and additive factor. Make sure it relects on the
// pseudo sample.
- EXPECT_EQ(GetPseudoMetricsSample(sample), 115);
+ EXPECT_DOUBLE_EQ(GetPseudoMetricsSample(sample), 115);
}
}
diff --git a/chromium/components/metrics/metrics_log_unittest.cc b/chromium/components/metrics/metrics_log_unittest.cc
index 3305efd71e1..ba229da5785 100644
--- a/chromium/components/metrics/metrics_log_unittest.cc
+++ b/chromium/components/metrics/metrics_log_unittest.cc
@@ -10,6 +10,7 @@
#include <string>
#include "base/base64.h"
+#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/bucket_ranges.h"
#include "base/metrics/sample_vector.h"
diff --git a/chromium/components/metrics/metrics_pref_names.cc b/chromium/components/metrics/metrics_pref_names.cc
index bd8c95dfd9d..b5e39cb3d5c 100644
--- a/chromium/components/metrics/metrics_pref_names.cc
+++ b/chromium/components/metrics/metrics_pref_names.cc
@@ -12,6 +12,18 @@ namespace prefs {
// Note: the 'uninstall_metrics' name is a legacy name and doesn't mean much.
const char kInstallDate[] = "uninstall_metrics.installation_date2";
+// A provisional metrics client GUID used for field trial group assignments
+// before metrics reporting consent is known (i.e., during first run). This GUID
+// is never reported directly. However, if the user enables UMA, this
+// provisional client GUID becomes the metrics client GUID (see
+// |kMetricsClientID|), and this pref is cleared. In that case, the GUID may
+// be reported.
+// Note: This GUID is stored in prefs because it is possible that the user
+// closes Chrome during the FRE. We re-use this GUID in subsequent FRE runs
+// until metrics reporting consent is truly known.
+const char kMetricsProvisionalClientID[] =
+ "user_experience_metrics.provisional_client_id";
+
// The metrics client GUID.
// Note: The name client_id2 is a result of creating
// new prefs to do a one-time reset of the previous values.
@@ -70,6 +82,21 @@ const char kMetricsOngoingLogsMetadata[] =
// client id and low entropy source should be reset.
const char kMetricsResetIds[] = "user_experience_metrics.reset_metrics_ids";
+#if BUILDFLAG(IS_ANDROID)
+// Boolean that determines whether to use the new sampling trial
+// "PostFREFixMetricsAndCrashSampling" and feature "PostFREFixMetricsReporting"
+// to control sampling on Android Chrome. This is set to true when disabling
+// metrics reporting, or on start up if metrics reporting is not consented to
+// (including new users going through their first run). As a result, all new UMA
+// users should have this pref set to true.
+// Note: This exists due to a bug in which the old sampling rate was not being
+// applied correctly. In order for the fix to not affect the overall sampling
+// rate, this pref controls what trial/feature to use to determine whether the
+// client is sampled. See crbug/1306481.
+const char kUsePostFREFixSamplingTrial[] =
+ "user_experience_metrics.use_post_fre_fix_sampling_trial";
+#endif // BUILDFLAG(IS_ANDROID)
+
// Boolean that specifies whether or not crash reporting and metrics reporting
// are sent over the network for analysis.
const char kMetricsReportingEnabled[] =
@@ -148,9 +175,16 @@ const char kStabilityGpuCrashCount[] =
"user_experience_metrics.stability.gpu_crash_count";
#if BUILDFLAG(IS_ANDROID)
-// Number of times the application was launched since last report.
+// Number of times the application was launched since last report. Used on
+// Android platforms as WebView may still be interested in this metric.
const char kStabilityLaunchCount[] =
"user_experience_metrics.stability.launch_count";
+
+// Number of times a renderer process successfully launched since the last
+// report. Used on Android platforms as WebView may still be interested in this
+// metric.
+const char kStabilityRendererLaunchCount[] =
+ "user_experience_metrics.stability.renderer_launch_count";
#endif
// Number of times a page load event occurred since the last report.
@@ -161,11 +195,6 @@ const char kStabilityPageLoadCount[] =
const char kStabilityRendererCrashCount[] =
"user_experience_metrics.stability.renderer_crash_count";
-// Number of times a renderer process successfully launched since the last
-// report.
-const char kStabilityRendererLaunchCount[] =
- "user_experience_metrics.stability.renderer_launch_count";
-
// Base64 encoded serialized UMA system profile proto from the previous session.
const char kStabilitySavedSystemProfile[] =
"user_experience_metrics.stability.saved_system_profile";
diff --git a/chromium/components/metrics/metrics_pref_names.h b/chromium/components/metrics/metrics_pref_names.h
index c94f0d63934..ec9bbe74ecc 100644
--- a/chromium/components/metrics/metrics_pref_names.h
+++ b/chromium/components/metrics/metrics_pref_names.h
@@ -20,11 +20,15 @@ extern const char kMetricsInitialLogs[];
extern const char kMetricsInitialLogsMetadata[];
extern const char kMetricsLowEntropySource[];
extern const char kMetricsOldLowEntropySource[];
+extern const char kMetricsProvisionalClientID[];
extern const char kMetricsPseudoLowEntropySource[];
extern const char kMetricsMachineId[];
extern const char kMetricsOngoingLogs[];
extern const char kMetricsOngoingLogsMetadata[];
extern const char kMetricsResetIds[];
+#if BUILDFLAG(IS_ANDROID)
+extern const char kUsePostFREFixSamplingTrial[];
+#endif // BUILDFLAG(IS_ANDROID)
// Preferences for cloned installs.
extern const char kClonedResetCount[];
@@ -45,6 +49,8 @@ extern const char kMetricsLastSeenPrefix[];
extern const char kStabilityBrowserLastLiveTimeStamp[];
extern const char kStabilityCrashCount[];
extern const char kStabilityCrashCountDueToGmsCoreUpdate[];
+// TODO(crbug/1241702): Remove this Local State pref once the new behavior
+// launches on Android Chrome.
extern const char kStabilityExitedCleanly[];
extern const char kStabilityExtensionRendererCrashCount[];
extern const char kStabilityFileMetricsUnsentSamplesCount[];
@@ -53,10 +59,10 @@ extern const char kStabilityGmsCoreVersion[];
extern const char kStabilityGpuCrashCount[];
#if BUILDFLAG(IS_ANDROID)
extern const char kStabilityLaunchCount[];
+extern const char kStabilityRendererLaunchCount[];
#endif
extern const char kStabilityPageLoadCount[];
extern const char kStabilityRendererCrashCount[];
-extern const char kStabilityRendererLaunchCount[];
extern const char kStabilitySavedSystemProfile[];
extern const char kStabilitySavedSystemProfileHash[];
extern const char kStabilityStatsBuildTime[];
diff --git a/chromium/components/metrics/metrics_service.cc b/chromium/components/metrics/metrics_service.cc
index d096367b109..b294e07c09f 100644
--- a/chromium/components/metrics/metrics_service.cc
+++ b/chromium/components/metrics/metrics_service.cc
@@ -129,6 +129,7 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/callback_list.h"
#include "base/location.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_flattener.h"
@@ -327,6 +328,10 @@ std::string MetricsService::GetClientId() const {
return state_manager_->client_id();
}
+void MetricsService::SetExternalClientId(const std::string& id) {
+ state_manager_->SetExternalClientId(id);
+}
+
bool MetricsService::WasLastShutdownClean() const {
return state_manager_->clean_exit_beacon()->exited_cleanly();
}
@@ -364,6 +369,8 @@ void MetricsService::EnableRecording() {
action_callback_ = base::BindRepeating(&MetricsService::OnUserAction,
base::Unretained(this));
base::AddActionCallback(action_callback_);
+
+ enablement_observers_.Notify(/*enabled=*/true);
}
void MetricsService::DisableRecording() {
@@ -378,6 +385,8 @@ void MetricsService::DisableRecording() {
delegating_provider_.OnRecordingDisabled();
PushPendingLogsToPersistentStorage();
+
+ enablement_observers_.Notify(/*enabled=*/false);
}
bool MetricsService::recording_active() const {
@@ -394,6 +403,10 @@ bool MetricsService::has_unsent_logs() const {
return reporting_service_.metrics_log_store()->has_unsent_logs();
}
+bool MetricsService::IsMetricsReportingEnabled() const {
+ return state_manager_->IsMetricsReportingEnabled();
+}
+
void MetricsService::RecordDelta(const base::HistogramBase& histogram,
const base::HistogramSamples& snapshot) {
log_manager_.current_log()->RecordHistogramDelta(histogram.histogram_name(),
@@ -463,11 +476,6 @@ void MetricsService::OnAppEnterForeground(bool force_open_new_log) {
OpenNewLog();
}
}
-
-#else
-void MetricsService::LogNeedForCleanShutdown() {
- state_manager_->LogHasSessionShutdownCleanly(false);
-}
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
void MetricsService::ClearSavedStabilityMetrics() {
@@ -554,7 +562,9 @@ void MetricsService::UpdateCurrentUserMetricsConsent(
bool user_metrics_consent) {
client_->UpdateCurrentUserMetricsConsent(user_metrics_consent);
}
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
void MetricsService::ResetClientId() {
// Pref must be cleared in order for ForceClientIdCreation to generate a new
// client ID.
@@ -562,7 +572,7 @@ void MetricsService::ResetClientId() {
state_manager_->ForceClientIdCreation();
client_->SetMetricsClientId(state_manager_->client_id());
}
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif // BUILDFLAG(IS_CHROMEOS)
variations::SyntheticTrialRegistry*
MetricsService::GetSyntheticTrialRegistry() {
@@ -963,6 +973,11 @@ std::unique_ptr<MetricsLog> MetricsService::CreateLog(
return new_metrics_log;
}
+base::CallbackListSubscription MetricsService::AddEnablementObserver(
+ const base::RepeatingCallback<void(bool)>& observer) {
+ return enablement_observers_.Add(observer);
+}
+
void MetricsService::SetPersistentSystemProfile(
const std::string& serialized_proto,
bool complete) {
diff --git a/chromium/components/metrics/metrics_service.h b/chromium/components/metrics/metrics_service.h
index 08bed985dac..6a264dabcf1 100644
--- a/chromium/components/metrics/metrics_service.h
+++ b/chromium/components/metrics/metrics_service.h
@@ -14,6 +14,9 @@
#include <memory>
#include <string>
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/callback_list.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -21,6 +24,8 @@
#include "base/metrics/histogram_flattener.h"
#include "base/metrics/histogram_snapshot_manager.h"
#include "base/metrics/user_metrics.h"
+#include "base/observer_list.h"
+#include "base/scoped_observation.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -104,6 +109,14 @@ class MetricsService : public base::HistogramFlattener {
// recording is not currently running.
std::string GetClientId() const;
+ // Set an external provided id for the metrics service. This method can be
+ // set by a caller which wants to explicitly control the *next* id used by the
+ // metrics service. Note that setting the external client id will *not* change
+ // the current metrics client id. In order to change the current client id,
+ // callers should call ResetClientId to change the current client id to the
+ // provided id.
+ void SetExternalClientId(const std::string& id);
+
// Returns the date at which the current metrics client ID was created as
// an int64_t containing seconds since the epoch.
int64_t GetMetricsReportingEnabledDate();
@@ -138,16 +151,14 @@ class MetricsService : public base::HistogramFlattener {
// Called when the application is coming out of background mode.
void OnAppEnterForeground(bool force_open_new_log = false);
-#else
- // Signals that the session has not yet exited cleanly. Calling this later
- // requires a call to LogCleanShutdown().
- void LogNeedForCleanShutdown();
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
bool recording_active() const;
bool reporting_active() const;
bool has_unsent_logs() const;
+ bool IsMetricsReportingEnabled() const;
+
// Register the specified |provider| to provide additional metrics into the
// UMA log. Should be called during MetricsService initialization only.
void RegisterMetricsProvider(std::unique_ptr<MetricsProvider> provider);
@@ -219,14 +230,16 @@ class MetricsService : public base::HistogramFlattener {
// Updates the current user metrics consent. No-ops if no user has logged in.
void UpdateCurrentUserMetricsConsent(bool user_metrics_consent);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
// Forces the client ID to be reset and generates a new client ID. This will
// be called when a user re-consents to metrics collection and the user had
// consented in the past.
//
// This is to preserve the pseudo-anonymous identifier <client_id, user_id>.
void ResetClientId();
-#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif // BUILDFLAG(IS_CHROMEOS)
variations::SyntheticTrialRegistry* GetSyntheticTrialRegistry();
@@ -241,6 +254,12 @@ class MetricsService : public base::HistogramFlattener {
return &delegating_provider_;
}
+ // Observers will be notified when the enablement state changes. The callback
+ // should accept one boolean argument, which will signal whether or not the
+ // metrics collection has been enabled.
+ base::CallbackListSubscription AddEnablementObserver(
+ const base::RepeatingCallback<void(bool)>& observer);
+
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
bool IsInForegroundForTesting() const { return is_in_foreground_; }
#endif
@@ -453,6 +472,9 @@ class MetricsService : public base::HistogramFlattener {
// Indicates if loading of independent metrics is currently active.
bool independent_loader_active_ = false;
+ // A set of observers that keeps track of the metrics reporting state.
+ base::RepeatingCallbackList<void(bool)> enablement_observers_;
+
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
// Indicates whether OnAppEnterForeground() (true) or OnAppEnterBackground
// (false) was called.
diff --git a/chromium/components/metrics/metrics_service_accessor.cc b/chromium/components/metrics/metrics_service_accessor.cc
index 3a3d439a906..897f5a5874c 100644
--- a/chromium/components/metrics/metrics_service_accessor.cc
+++ b/chromium/components/metrics/metrics_service_accessor.cc
@@ -48,21 +48,10 @@ bool MetricsServiceAccessor::RegisterSyntheticFieldTrial(
base::StringPiece trial_name,
base::StringPiece group_name,
variations::SyntheticTrialAnnotationMode annotation_mode) {
- return RegisterSyntheticFieldTrialWithNameAndGroupHash(
- metrics_service, variations::HashName(trial_name),
- variations::HashName(group_name), annotation_mode);
-}
-
-// static
-bool MetricsServiceAccessor::RegisterSyntheticFieldTrialWithNameAndGroupHash(
- MetricsService* metrics_service,
- uint32_t trial_name_hash,
- uint32_t group_name_hash,
- variations::SyntheticTrialAnnotationMode annotation_mode) {
if (!metrics_service)
return false;
- variations::SyntheticTrialGroup trial_group(trial_name_hash, group_name_hash,
+ variations::SyntheticTrialGroup trial_group(trial_name, group_name,
annotation_mode);
metrics_service->GetSyntheticTrialRegistry()->RegisterSyntheticFieldTrial(
trial_group);
diff --git a/chromium/components/metrics/metrics_service_accessor.h b/chromium/components/metrics/metrics_service_accessor.h
index 621ecab8e56..3744528a6b2 100644
--- a/chromium/components/metrics/metrics_service_accessor.h
+++ b/chromium/components/metrics/metrics_service_accessor.h
@@ -51,14 +51,6 @@ class MetricsServiceAccessor {
base::StringPiece group_name,
variations::SyntheticTrialAnnotationMode annotation_mode);
- // Same as RegisterSyntheticFieldTrial above, but takes in the trial and group
- // names as hashes rather than computing those hashes from the strings.
- static bool RegisterSyntheticFieldTrialWithNameAndGroupHash(
- MetricsService* metrics_service,
- uint32_t trial_name_hash,
- uint32_t group_name_hash,
- variations::SyntheticTrialAnnotationMode annotation_mode);
-
// IsMetricsReportingEnabled() in non-official builds unconditionally returns
// false. This results in different behavior for tests running in official vs
// non-official builds. To get consistent behavior call this with true, which
diff --git a/chromium/components/metrics/metrics_service_unittest.cc b/chromium/components/metrics/metrics_service_unittest.cc
index a6a1d605145..f481b0de2fd 100644
--- a/chromium/components/metrics/metrics_service_unittest.cc
+++ b/chromium/components/metrics/metrics_service_unittest.cc
@@ -18,6 +18,7 @@
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/statistics_recorder.h"
#include "base/metrics/user_metrics.h"
+#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/platform_thread.h"
@@ -183,9 +184,12 @@ class MetricsServiceTest : public testing::Test {
PrefService* GetLocalState() { return &testing_local_state_; }
// Sets metrics reporting as enabled for testing.
- void EnableMetricsReporting() {
- enabled_state_provider_->set_consent(true);
- enabled_state_provider_->set_enabled(true);
+ void EnableMetricsReporting() { SetMetricsReporting(true); }
+
+ // Sets metrics reporting for testing.
+ void SetMetricsReporting(bool enabled) {
+ enabled_state_provider_->set_consent(enabled);
+ enabled_state_provider_->set_enabled(enabled);
}
// Finds a histogram with the specified |name_hash| in |histograms|.
@@ -777,6 +781,51 @@ TEST_F(MetricsServiceTest, LastLiveTimestamp) {
GetLocalState()->GetTime(prefs::kStabilityBrowserLastLiveTimeStamp));
}
+TEST_F(MetricsServiceTest, EnablementObserverNotification) {
+ EnableMetricsReporting();
+ TestMetricsServiceClient client;
+ TestMetricsService service(GetMetricsStateManager(), &client,
+ GetLocalState());
+ service.InitializeMetricsRecordingState();
+
+ absl::optional<bool> enabled;
+ auto observer = [&enabled](bool notification) { enabled = notification; };
+
+ auto subscription =
+ service.AddEnablementObserver(base::BindLambdaForTesting(observer));
+
+ service.Start();
+ ASSERT_TRUE(enabled.has_value());
+ EXPECT_TRUE(enabled.value());
+
+ enabled.reset();
+
+ service.Stop();
+ ASSERT_TRUE(enabled.has_value());
+ EXPECT_FALSE(enabled.value());
+}
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+// ResetClientId is only enabled on certain targets.
+TEST_F(MetricsServiceTest, SetClientIdToExternalId) {
+ EnableMetricsReporting();
+ TestMetricsServiceClient client;
+ TestMetricsService service(GetMetricsStateManager(), &client,
+ GetLocalState());
+
+ const std::string client_id = "d92ad666-a420-4c73-8718-94311ae2ff5f";
+
+ EXPECT_NE(service.GetClientId(), client_id);
+
+ service.SetExternalClientId(client_id);
+ // Reset will cause the client id to be regenerated. If an external client id
+ // is provided, it should defer to using that id instead of creating its own.
+ service.ResetClientId();
+
+ EXPECT_EQ(service.GetClientId(), client_id);
+}
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+
#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(MetricsServiceTest,
OngoingLogNotFlushedBeforeInitialLogWhenUserLogStoreSet) {
diff --git a/chromium/components/metrics/metrics_state_manager.cc b/chromium/components/metrics/metrics_state_manager.cc
index 5da316cc612..6bbfe4b1995 100644
--- a/chromium/components/metrics/metrics_state_manager.cc
+++ b/chromium/components/metrics/metrics_state_manager.cc
@@ -28,6 +28,7 @@
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
+#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "components/metrics/cloned_install_detector.h"
#include "components/metrics/enabled_state_provider.h"
@@ -226,6 +227,9 @@ class MetricsStateMetricsProvider : public MetricsProvider {
// static
bool MetricsStateManager::instance_exists_ = false;
+// static
+bool MetricsStateManager::enable_provisional_client_id_for_testing_ = false;
+
MetricsStateManager::MetricsStateManager(
PrefService* local_state,
EnabledStateProvider* enabled_state_provider,
@@ -269,35 +273,40 @@ MetricsStateManager::MetricsStateManager(
metrics::structured::NeutrinoDevicesLocation::kMetricsStateManager);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
ForceClientIdCreation();
+ } else {
+#if BUILDFLAG(IS_ANDROID)
+ // If on start up we determine that the client has not given their consent
+ // to report their metrics, the new sampling trial should be used to
+ // determine whether the client is sampled in or out (if the user ever
+ // enables metrics reporting). This covers users that are going through
+ // the first run, as well as users that have metrics reporting disabled.
+ //
+ // See crbug/1306481 and the comment above |kUsePostFREFixSamplingTrial| in
+ // components/metrics/metrics_pref_names.cc for more details.
+ local_state_->SetBoolean(metrics::prefs::kUsePostFREFixSamplingTrial, true);
+#endif // BUILDFLAG(IS_ANDROID)
}
-#if !BUILDFLAG(IS_WIN)
- if (is_first_run) {
- // If this is a first run (no install date) and there's no client id, then
- // generate a provisional client id now. This id will be used for field
- // trial randomization on first run and will be promoted to become the
- // client id if UMA is enabled during this session, via the logic in
- // ForceClientIdCreation().
- //
- // Note: We don't do this on Windows because on Windows, there's no UMA
- // checkbox on first run and instead it comes from the install page. So if
- // UMA is not enabled at this point, it's unlikely it will be enabled in
- // the same session since that requires the user to manually do that via
- // settings page after they unchecked it on the download page.
- //
- // Note: Windows first run is covered by browser tests
- // FirstRunMasterPrefsVariationsSeedTest.PRE_SecondRun and
- // FirstRunMasterPrefsVariationsSeedTest.SecondRun. If the platform ifdef
- // for this logic changes, the tests should be updated as well.
- if (client_id_.empty())
- provisional_client_id_ = base::GenerateGUID();
+ // Generate and store a provisional client ID if necessary. This ID will be
+ // used for field trial randomization on first run (and possibly in future
+ // runs if the user closes Chrome during the FRE) and will be promoted to
+ // become the client ID if UMA is enabled during this session, via the logic
+ // in ForceClientIdCreation(). If UMA is disabled (refused), we discard it.
+ //
+ // Note: This means that if a provisional client ID is used for this session,
+ // and the user disables (refuses) UMA, then starting from the next run, the
+ // field trial randomization (group assignment) will be different.
+ if (ShouldGenerateProvisionalClientId(is_first_run)) {
+ local_state_->SetString(prefs::kMetricsProvisionalClientID,
+ base::GenerateGUID());
}
-#endif // !BUILDFLAG(IS_WIN)
// The |initial_client_id_| should only be set if UMA is enabled or there's a
// provisional client id.
initial_client_id_ =
- (client_id_.empty() ? provisional_client_id_ : client_id_);
+ (client_id_.empty()
+ ? local_state_->GetString(prefs::kMetricsProvisionalClientID)
+ : client_id_);
DCHECK(!instance_exists_);
instance_exists_ = true;
}
@@ -489,7 +498,9 @@ void MetricsStateManager::ForceClientIdCreation() {
// so generate a new one. If there's a provisional client id (e.g. UMA
// was enabled as part of first run), promote that to the client id,
// otherwise (e.g. UMA enabled in a future session), generate a new one.
- if (provisional_client_id_.empty()) {
+ std::string provisional_client_id =
+ local_state_->GetString(prefs::kMetricsProvisionalClientID);
+ if (provisional_client_id.empty()) {
client_id_ = base::GenerateGUID();
base::UmaHistogramEnumeration("UMA.ClientIdSource",
ClientIdSource::kClientIdNew);
@@ -499,8 +510,8 @@ void MetricsStateManager::ForceClientIdCreation() {
previous_client_id);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
} else {
- client_id_ = provisional_client_id_;
- provisional_client_id_.clear();
+ client_id_ = provisional_client_id;
+ local_state_->ClearPref(prefs::kMetricsProvisionalClientID);
base::UmaHistogramEnumeration("UMA.ClientIdSource",
ClientIdSource::kClientIdFromProvisionalId);
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -518,6 +529,10 @@ void MetricsStateManager::ForceClientIdCreation() {
BackUpCurrentClientInfo();
}
+void MetricsStateManager::SetExternalClientId(const std::string& id) {
+ external_client_id_ = id;
+}
+
void MetricsStateManager::CheckForClonedInstall() {
cloned_install_detector_.CheckForClonedInstall(local_state_);
}
@@ -577,9 +592,14 @@ std::unique_ptr<MetricsStateManager> MetricsStateManager::Create(
// static
void MetricsStateManager::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterStringPref(prefs::kMetricsProvisionalClientID,
+ std::string());
registry->RegisterStringPref(prefs::kMetricsClientID, std::string());
registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0);
registry->RegisterInt64Pref(prefs::kInstallDate, 0);
+#if BUILDFLAG(IS_ANDROID)
+ registry->RegisterBooleanPref(prefs::kUsePostFREFixSamplingTrial, false);
+#endif // BUILDFLAG(IS_ANDROID)
EntropyState::RegisterPrefs(registry);
ClonedInstallDetector::RegisterPrefs(registry);
@@ -653,6 +673,55 @@ void MetricsStateManager::ResetMetricsIDsIfNecessary() {
store_client_info_.Run(ClientInfo());
}
+bool MetricsStateManager::ShouldGenerateProvisionalClientId(bool is_first_run) {
+#if BUILDFLAG(IS_WIN)
+ // We do not want to generate a provisional client ID on Windows because
+ // there's no UMA checkbox on first run. Instead it comes from the install
+ // page. So if UMA is not enabled at this point, it's unlikely it will be
+ // enabled in the same session since that requires the user to manually do
+ // that via settings page after they unchecked it on the download page.
+ //
+ // Note: Windows first run is covered by browser tests
+ // FirstRunMasterPrefsVariationsSeedTest.PRE_SecondRun and
+ // FirstRunMasterPrefsVariationsSeedTest.SecondRun. If the platform ifdef
+ // for this logic changes, the tests should be updated as well.
+ return false;
+#else
+ // We should only generate a provisional client ID on the first run. If for
+ // some reason there is already a client ID, we do not generate one either.
+ // This can happen if metrics reporting is managed by a policy.
+ if (!is_first_run || !client_id_.empty())
+ return false;
+
+ // Return false if |kMetricsReportingEnabled| is managed by a policy. For
+ // example, if metrics reporting is disabled by a policy, then
+ // |kMetricsReportingEnabled| will always be set to false, so there is no
+ // reason to generate a provisional client ID. If metrics reporting is enabled
+ // by a policy, then the default value of |kMetricsReportingEnabled| will be
+ // true, and so a client ID will have already been generated (we would have
+ // returned false already because of the previous check).
+ if (local_state_->IsManagedPreference(prefs::kMetricsReportingEnabled))
+ return false;
+
+ // If this is a non-Google-Chrome-branded build, we do not want to generate a
+ // provisional client ID because metrics reporting is not enabled on those
+ // builds. This would be problematic because we store the provisional client
+ // ID in the Local State, and clear it when either 1) we enable UMA (the
+ // provisional client ID becomes the client ID), or 2) we disable UMA. Since
+ // in non-Google-Chrome-branded builds we never actually go through the code
+ // paths to either enable or disable UMA, the pref storing the provisional
+ // client ID would never be cleared. However, for test consistency between
+ // the different builds, we do not return false here if
+ // |enable_provisional_client_id_for_testing_| is set to true.
+ if (!BUILDFLAG(GOOGLE_CHROME_BRANDING) &&
+ !enable_provisional_client_id_for_testing_) {
+ return false;
+ }
+
+ return true;
+#endif // BUILDFLAG(IS_WIN)
+}
+
#if BUILDFLAG(IS_CHROMEOS_ASH)
void MetricsStateManager::LogClientIdChanged(
metrics::structured::NeutrinoDevicesLocation location,
diff --git a/chromium/components/metrics/metrics_state_manager.h b/chromium/components/metrics/metrics_state_manager.h
index 6212396a6a3..513efe4500a 100644
--- a/chromium/components/metrics/metrics_state_manager.h
+++ b/chromium/components/metrics/metrics_state_manager.h
@@ -143,6 +143,10 @@ class MetricsStateManager final {
// before recording.
void ForceClientIdCreation();
+ // Sets the external client id. Useful for callers that want explicit control
+ // of the next metrics client id.
+ void SetExternalClientId(const std::string& id);
+
// Checks if this install was cloned or imaged from another machine. If a
// clone is detected, resets the client id and low entropy source. This
// should not be called more than once.
@@ -211,7 +215,7 @@ class MetricsStateManager final {
FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest,
ProvisionalClientId_PromotedToClientId);
FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest,
- ProvisionalClientId_NotPersisted);
+ ProvisionalClientId_PersistedAcrossFirstRuns);
FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, ResetBackup);
FRIEND_TEST_ALL_PREFIXES(MetricsStateManagerTest, ResetMetricsIDs);
@@ -280,7 +284,7 @@ class MetricsStateManager final {
// Returns the high entropy source for this client, which is composed of a
// client ID and the low entropy source. This is intended to be unique for
// each install. UMA must be enabled (and |client_id_| must be set) or
- // |provisional_client_id_| must be set before calling this.
+ // |kMetricsProvisionalClientID| must be set before calling this.
std::string GetHighEntropySource();
// Returns the old low entropy source for this client.
@@ -305,6 +309,8 @@ class MetricsStateManager final {
// pref is true.
void ResetMetricsIDsIfNecessary();
+ bool ShouldGenerateProvisionalClientId(bool is_first_run);
+
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Log to structured metrics when the client id is changed.
void LogClientIdChanged(metrics::structured::NeutrinoDevicesLocation location,
@@ -337,14 +343,6 @@ class MetricsStateManager final {
// The identifier that's sent to the server with the log reports.
std::string client_id_;
- // A provisional client id that's generated at start up before we know whether
- // metrics consent has been received from the client. This id becomes the
- // |client_id_| if consent is given within the same session, or is cleared
- // otherwise. Does not control transmission of UMA metrics, only used for the
- // high entropy source used for field trial randomization so that field
- // trials don't toggle state between first and second run.
- std::string provisional_client_id_;
-
// The client id that was used do field trial randomization. This field should
// only be changed when we need to do group assignment. |initial_client_id|
// should left blank iff a client id was not used to do field trial
@@ -380,6 +378,10 @@ class MetricsStateManager final {
// used only during startup. On Android WebLayer, Android WebView, and iOS,
// the visibility is unknown at this point in startup.
const StartupVisibility startup_visibility_;
+
+ // Force enables the creation of a provisional client ID on first run even if
+ // this is not a Chrome-branded build. Used for testing.
+ static bool enable_provisional_client_id_for_testing_;
};
} // namespace metrics
diff --git a/chromium/components/metrics/metrics_state_manager_unittest.cc b/chromium/components/metrics/metrics_state_manager_unittest.cc
index caab1380822..1c7efc101c7 100644
--- a/chromium/components/metrics/metrics_state_manager_unittest.cc
+++ b/chromium/components/metrics/metrics_state_manager_unittest.cc
@@ -322,15 +322,21 @@ TEST_F(MetricsStateManagerTest,
#if !BUILDFLAG(IS_WIN)
TEST_F(MetricsStateManagerTest, ProvisionalClientId_PromotedToClientId) {
+ // Force enable the creation of a provisional client ID on first run for
+ // consistency between Chromium and Chrome builds.
+ MetricsStateManager::enable_provisional_client_id_for_testing_ = true;
+
std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
// Verify that there was a provisional client id created.
- std::string provisional_client_id = state_manager->provisional_client_id_;
+ std::string provisional_client_id =
+ prefs_.GetString(prefs::kMetricsProvisionalClientID);
VerifyClientId(provisional_client_id);
// No client id should have been stored.
EXPECT_TRUE(prefs_.FindPreference(prefs::kMetricsClientID)->IsDefaultValue());
int low_entropy_source = state_manager->GetLowEntropySource();
- // The default entropy provider should be the high entropy one.
+ // The default entropy provider should be the high entropy one since we a
+ // the provisional client ID.
state_manager->CreateDefaultEntropyProvider();
EXPECT_EQ(state_manager->entropy_source_returned(),
MetricsStateManager::ENTROPY_SOURCE_HIGH);
@@ -342,45 +348,53 @@ TEST_F(MetricsStateManagerTest, ProvisionalClientId_PromotedToClientId) {
std::string client_id = state_manager->client_id();
EXPECT_EQ(provisional_client_id, client_id);
EXPECT_EQ(prefs_.GetString(prefs::kMetricsClientID), client_id);
- EXPECT_TRUE(state_manager->provisional_client_id_.empty());
+ EXPECT_TRUE(prefs_.FindPreference(prefs::kMetricsProvisionalClientID)
+ ->IsDefaultValue());
+ EXPECT_TRUE(prefs_.GetString(prefs::kMetricsProvisionalClientID).empty());
EXPECT_EQ(state_manager->GetLowEntropySource(), low_entropy_source);
EXPECT_EQ(client_info_load_count_, 1);
}
-TEST_F(MetricsStateManagerTest, ProvisionalClientId_NotPersisted) {
+TEST_F(MetricsStateManagerTest, ProvisionalClientId_PersistedAcrossFirstRuns) {
+ // Force enable the creation of a provisional client ID on first run for
+ // consistency between Chromium and Chrome builds.
+ MetricsStateManager::enable_provisional_client_id_for_testing_ = true;
+
std::string provisional_client_id;
- int low_entropy_source;
- // First run, with a provisional client id.
+ // Simulate a first run, and verify that a provisional client id is generated.
+ // We also do not enable nor disable UMA in order to simulate exiting during
+ // the first run flow.
{
std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
// Verify that there was a provisional client id created.
- std::string provisional_client_id = state_manager->provisional_client_id_;
+ provisional_client_id =
+ prefs_.GetString(prefs::kMetricsProvisionalClientID);
VerifyClientId(provisional_client_id);
// No client id should have been stored.
EXPECT_TRUE(
prefs_.FindPreference(prefs::kMetricsClientID)->IsDefaultValue());
- low_entropy_source = state_manager->GetLowEntropySource();
- // The default entropy provider should be the high entropy one.
+ // The default entropy provider should be the high entropy one since we a
+ // the provisional client ID.
state_manager->CreateDefaultEntropyProvider();
EXPECT_EQ(state_manager->entropy_source_returned(),
MetricsStateManager::ENTROPY_SOURCE_HIGH);
}
- // Now, simulate a second run, such that UMA was not turned on during the
- // first run. This should not result in any client id existing nor any
- // provisional client id.
+ // Now, simulate a second run, and verify that the provisional client ID is
+ // the same.
{
std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
- EXPECT_TRUE(state_manager->provisional_client_id_.empty());
- EXPECT_TRUE(state_manager->client_id().empty());
- EXPECT_EQ(state_manager->GetLowEntropySource(), low_entropy_source);
- EXPECT_TRUE(
- prefs_.FindPreference(prefs::kMetricsClientID)->IsDefaultValue());
- // The default entropy provider should be the low entropy one.
+ // Verify that the same provisional client ID as the first run is used.
+ EXPECT_EQ(provisional_client_id,
+ prefs_.GetString(prefs::kMetricsProvisionalClientID));
+ // There still should not be any stored client ID.
+ EXPECT_TRUE(prefs_.FindPreference(prefs::kMetricsClientID));
+ // The default entropy provider should be the high entropy one since we a
+ // the provisional client ID.
state_manager->CreateDefaultEntropyProvider();
EXPECT_EQ(state_manager->entropy_source_returned(),
- MetricsStateManager::ENTROPY_SOURCE_LOW);
+ MetricsStateManager::ENTROPY_SOURCE_HIGH);
}
}
#endif // !BUILDFLAG(IS_WIN)
diff --git a/chromium/components/metrics/metrics_switches.cc b/chromium/components/metrics/metrics_switches.cc
index 6972bfdb260..7f8a774e1fd 100644
--- a/chromium/components/metrics/metrics_switches.cc
+++ b/chromium/components/metrics/metrics_switches.cc
@@ -4,6 +4,8 @@
#include "components/metrics/metrics_switches.h"
+#include "base/check.h"
+
namespace metrics {
namespace switches {
diff --git a/chromium/components/metrics/net/cellular_logic_helper.cc b/chromium/components/metrics/net/cellular_logic_helper.cc
index 2083b6fbf44..dc37642bb22 100644
--- a/chromium/components/metrics/net/cellular_logic_helper.cc
+++ b/chromium/components/metrics/net/cellular_logic_helper.cc
@@ -4,7 +4,6 @@
#include "components/metrics/net/cellular_logic_helper.h"
-#include "base/feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
@@ -22,14 +21,6 @@ const int kStandardUploadIntervalCellularSeconds = 15 * 60; // Fifteen minutes.
const int kStandardUploadIntervalSeconds = 30 * 60; // Thirty minutes.
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-// A feature to control whether we upload UMA logs more frequently.
-const base::Feature kMoreFrequentUmaUploads{"MoreFrequentUmaUploads",
- base::FEATURE_DISABLED_BY_DEFAULT};
-// The interval between these more-frequent uploads.
-constexpr base::TimeDelta kMoreFrequentUploadInterval = base::Minutes(5);
-#endif // IS_CHROMEOS_ASH
-
#if BUILDFLAG(IS_ANDROID)
const bool kDefaultCellularLogicEnabled = true;
#else
@@ -42,10 +33,6 @@ base::TimeDelta GetUploadInterval(bool use_cellular_upload_interval) {
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
if (use_cellular_upload_interval)
return base::Seconds(kStandardUploadIntervalCellularSeconds);
-#elif BUILDFLAG(IS_CHROMEOS_ASH)
- if (base::FeatureList::IsEnabled(kMoreFrequentUmaUploads)) {
- return kMoreFrequentUploadInterval;
- }
#endif
return base::Seconds(kStandardUploadIntervalSeconds);
}
diff --git a/chromium/components/metrics/net/net_metrics_log_uploader.cc b/chromium/components/metrics/net/net_metrics_log_uploader.cc
index 32ef71fbcb2..cf04099aa43 100644
--- a/chromium/components/metrics/net/net_metrics_log_uploader.cc
+++ b/chromium/components/metrics/net/net_metrics_log_uploader.cc
@@ -19,7 +19,6 @@
#include "net/base/load_flags.h"
#include "net/base/url_util.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
diff --git a/chromium/components/metrics/serialization/serialization_utils.cc b/chromium/components/metrics/serialization/serialization_utils.cc
index 882bdf2c387..67190badbcf 100644
--- a/chromium/components/metrics/serialization/serialization_utils.cc
+++ b/chromium/components/metrics/serialization/serialization_utils.cc
@@ -109,15 +109,15 @@ std::unique_ptr<MetricSample> SerializationUtils::ParseSample(
const std::string& name = parts[0];
const std::string& value = parts[1];
- if (base::LowerCaseEqualsASCII(name, "crash"))
+ if (base::EqualsCaseInsensitiveASCII(name, "crash"))
return MetricSample::CrashSample(value);
- if (base::LowerCaseEqualsASCII(name, "histogram"))
+ if (base::EqualsCaseInsensitiveASCII(name, "histogram"))
return MetricSample::ParseHistogram(value);
- if (base::LowerCaseEqualsASCII(name, "linearhistogram"))
+ if (base::EqualsCaseInsensitiveASCII(name, "linearhistogram"))
return MetricSample::ParseLinearHistogram(value);
- if (base::LowerCaseEqualsASCII(name, "sparsehistogram"))
+ if (base::EqualsCaseInsensitiveASCII(name, "sparsehistogram"))
return MetricSample::ParseSparseHistogram(value);
- if (base::LowerCaseEqualsASCII(name, "useraction"))
+ if (base::EqualsCaseInsensitiveASCII(name, "useraction"))
return MetricSample::UserActionSample(value);
DLOG(ERROR) << "invalid event type: " << name << ", value: " << value;
return nullptr;
diff --git a/chromium/components/metrics/stability_metrics_helper.cc b/chromium/components/metrics/stability_metrics_helper.cc
index 13dfc4fdae1..0222e88c93b 100644
--- a/chromium/components/metrics/stability_metrics_helper.cc
+++ b/chromium/components/metrics/stability_metrics_helper.cc
@@ -98,11 +98,13 @@ void StabilityMetricsHelper::ProvideStabilityMetrics(
local_state_->SetInteger(prefs::kStabilityRendererCrashCount, 0);
}
+#if BUILDFLAG(IS_ANDROID)
count = local_state_->GetInteger(prefs::kStabilityRendererLaunchCount);
if (count) {
stability_proto->set_renderer_launch_count(count);
local_state_->SetInteger(prefs::kStabilityRendererLaunchCount, 0);
}
+#endif // BUILDFLAG(IS_ANDROID)
count =
local_state_->GetInteger(prefs::kStabilityExtensionRendererCrashCount);
@@ -118,7 +120,9 @@ void StabilityMetricsHelper::ClearSavedStabilityMetrics() {
local_state_->SetInteger(prefs::kStabilityGpuCrashCount, 0);
local_state_->SetInteger(prefs::kStabilityPageLoadCount, 0);
local_state_->SetInteger(prefs::kStabilityRendererCrashCount, 0);
+#if BUILDFLAG(IS_ANDROID)
local_state_->SetInteger(prefs::kStabilityRendererLaunchCount, 0);
+#endif // BUILDFLAG(IS_ANDROID)
}
// static
@@ -128,7 +132,9 @@ void StabilityMetricsHelper::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(prefs::kStabilityGpuCrashCount, 0);
registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0);
registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
+#if BUILDFLAG(IS_ANDROID)
registry->RegisterIntegerPref(prefs::kStabilityRendererLaunchCount, 0);
+#endif // BUILDFLAG(IS_ANDROID)
}
void StabilityMetricsHelper::IncreaseRendererCrashCount() {
@@ -138,7 +144,6 @@ void StabilityMetricsHelper::IncreaseRendererCrashCount() {
void StabilityMetricsHelper::IncreaseGpuCrashCount() {
IncrementPrefValue(prefs::kStabilityGpuCrashCount);
- local_state_->CommitPendingWrite(); // Schedule a Local State write.
RecordStabilityEvent(StabilityEventType::kGpuCrash);
}
@@ -264,8 +269,10 @@ void StabilityMetricsHelper::LogRendererLaunched(bool was_extension_process) {
? StabilityEventType::kExtensionRendererLaunch
: StabilityEventType::kRendererLaunch;
RecordStabilityEvent(metric);
+#if BUILDFLAG(IS_ANDROID)
if (!was_extension_process)
IncrementPrefValue(prefs::kStabilityRendererLaunchCount);
+#endif // BUILDFLAG(IS_ANDROID)
}
void StabilityMetricsHelper::LogRendererLaunchFailed(
diff --git a/chromium/components/metrics/structured/event_base.cc b/chromium/components/metrics/structured/event_base.cc
index f04a8c03e50..d116bedca9d 100644
--- a/chromium/components/metrics/structured/event_base.cc
+++ b/chromium/components/metrics/structured/event_base.cc
@@ -23,12 +23,14 @@ EventBase::EventBase(uint64_t event_name_hash,
uint64_t project_name_hash,
IdType id_type,
IdScope id_scope,
- StructuredEventProto_EventType event_type)
+ StructuredEventProto_EventType event_type,
+ int key_rotation_period)
: event_name_hash_(event_name_hash),
project_name_hash_(project_name_hash),
id_type_(id_type),
id_scope_(id_scope),
- event_type_(event_type) {}
+ event_type_(event_type),
+ key_rotation_period_(key_rotation_period) {}
EventBase::EventBase(const EventBase& other) = default;
EventBase::~EventBase() = default;
@@ -95,7 +97,8 @@ absl::optional<EventBase> EventBase::FromEvent(const Event& event) {
project_validator.value()->project_hash(),
project_validator.value()->id_type(),
project_validator.value()->id_scope(),
- project_validator.value()->event_type());
+ project_validator.value()->event_type(),
+ project_validator.value()->key_rotation_period());
for (const auto& metric_value : event.metric_values()) {
// Validate that both name and metric type are valid structured metrics.
diff --git a/chromium/components/metrics/structured/event_base.h b/chromium/components/metrics/structured/event_base.h
index b8cc55ba570..aa610225c7e 100644
--- a/chromium/components/metrics/structured/event_base.h
+++ b/chromium/components/metrics/structured/event_base.h
@@ -76,6 +76,8 @@ class EventBase {
EventType event_type() const { return event_type_; }
+ int key_rotation_period() const { return key_rotation_period_; }
+
// Converts an unhashed,raw |event| into an EventBase. If |event| is
// malformatted (ie wrong metric name or metric vlaue type) or is not
// registered within structured.xml, then returns absl::nullopt.
@@ -86,7 +88,8 @@ class EventBase {
uint64_t project_name_hash,
IdType id_type,
IdScope id_scope,
- EventType event_type);
+ EventType event_type,
+ int max_key_rotation_period);
void AddHmacMetric(uint64_t name_hash, const std::string& value);
@@ -123,6 +126,9 @@ class EventBase {
// information.
EventType event_type_;
+ // Key rotation period for this event.
+ int key_rotation_period_;
+
std::vector<Metric> metrics_;
};
diff --git a/chromium/components/metrics/structured/key_data.cc b/chromium/components/metrics/structured/key_data.cc
index 8117aeb4d9e..44a3cfad5cd 100644
--- a/chromium/components/metrics/structured/key_data.cc
+++ b/chromium/components/metrics/structured/key_data.cc
@@ -23,9 +23,6 @@ namespace {
// The expected size of a key, in bytes.
constexpr size_t kKeySize = 32;
-// The default maximum number of days before rotating keys.
-constexpr int kDefaultRotationPeriod = 90;
-
// Generates a key, which is the string representation of
// base::UnguessableToken, and is of size |kKeySize| bytes.
std::string GenerateKey() {
@@ -92,7 +89,8 @@ void KeyData::WriteNowForTest() {
//---------------
absl::optional<std::string> KeyData::ValidateAndGetKey(
- const uint64_t project_name_hash) {
+ const uint64_t project_name_hash,
+ int key_rotation_period) {
if (!is_initialized_) {
NOTREACHED();
return absl::nullopt;
@@ -103,20 +101,26 @@ absl::optional<std::string> KeyData::ValidateAndGetKey(
// Generate or rotate key.
const int last_rotation = key.last_rotation();
+
if (key.key().empty() || last_rotation == 0) {
LogKeyValidation(KeyValidationState::kCreated);
// If the key is empty, generate a new one. Set the last rotation to a
- // uniformly selected day between today and |kDefaultRotationPeriod| days
+ // uniformly selected day between today and |key_rotation_period| days
// ago, to uniformly distribute users amongst rotation cohorts.
- const int rotation_seed = base::RandInt(0, kDefaultRotationPeriod - 1);
- UpdateKey(&key, now - rotation_seed, kDefaultRotationPeriod);
- } else if (now - last_rotation > kDefaultRotationPeriod) {
+ const int rotation_seed = base::RandInt(0, key_rotation_period - 1);
+ UpdateKey(&key, now - rotation_seed, key_rotation_period);
+ } else if (now - last_rotation > key_rotation_period) {
LogKeyValidation(KeyValidationState::kRotated);
- // If the key is outdated, generate a new one. Update the last rotation such
- // that the user stays in the same cohort.
+
+ // If the key is outdated, generate a new one. Update the last rotation
+ // such that the user stays in the same cohort.
+ //
+ // Note that if the max key rotation period has changed, the new rotation
+ // period will be used to calculate whether the key should be rotated or
+ // not.
const int new_last_rotation =
- now - (now - last_rotation) % kDefaultRotationPeriod;
- UpdateKey(&key, new_last_rotation, kDefaultRotationPeriod);
+ now - (now - last_rotation) % key_rotation_period;
+ UpdateKey(&key, new_last_rotation, key_rotation_period);
} else {
LogKeyValidation(KeyValidationState::kValid);
}
@@ -131,11 +135,11 @@ absl::optional<std::string> KeyData::ValidateAndGetKey(
}
void KeyData::UpdateKey(KeyProto* key,
- const int last_rotation,
- const int rotation_period) {
+ int last_key_rotation,
+ int key_rotation_period) {
key->set_key(GenerateKey());
- key->set_last_rotation(last_rotation);
- key->set_rotation_period(rotation_period);
+ key->set_last_rotation(last_key_rotation);
+ key->set_rotation_period(key_rotation_period);
proto_->QueueWrite();
}
@@ -143,11 +147,13 @@ void KeyData::UpdateKey(KeyProto* key,
// IDs and hashing
//----------------
-uint64_t KeyData::Id(const uint64_t project_name_hash) {
+uint64_t KeyData::Id(const uint64_t project_name_hash,
+ int key_rotation_period) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Retrieve the key for |project_name_hash|.
- const absl::optional<std::string> key = ValidateAndGetKey(project_name_hash);
+ const absl::optional<std::string> key =
+ ValidateAndGetKey(project_name_hash, key_rotation_period);
if (!key) {
NOTREACHED();
return 0u;
@@ -161,11 +167,13 @@ uint64_t KeyData::Id(const uint64_t project_name_hash) {
uint64_t KeyData::HmacMetric(const uint64_t project_name_hash,
const uint64_t metric_name_hash,
- const std::string& value) {
+ const std::string& value,
+ int key_rotation_period) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Retrieve the key for |project_name_hash|.
- const absl::optional<std::string> key = ValidateAndGetKey(project_name_hash);
+ const absl::optional<std::string> key =
+ ValidateAndGetKey(project_name_hash, key_rotation_period);
if (!key) {
NOTREACHED();
return 0u;
diff --git a/chromium/components/metrics/structured/key_data.h b/chromium/components/metrics/structured/key_data.h
index ecfbf6f4fd9..2a60b264ca9 100644
--- a/chromium/components/metrics/structured/key_data.h
+++ b/chromium/components/metrics/structured/key_data.h
@@ -69,7 +69,8 @@ class KeyData {
// Returns 0u in case of an error.
uint64_t HmacMetric(uint64_t project_name_hash,
uint64_t metric_name_hash,
- const std::string& value);
+ const std::string& value,
+ int key_rotation_period);
// Returns an ID for this (user, |project_name_hash|) pair.
// |project_name_hash| is the name of a project, represented by the first 8
@@ -84,7 +85,9 @@ class KeyData {
// the server. This means events are associated with the client ID when
// uploaded from the device. See the class comment of
// StructuredMetricsProvider for more details.
- uint64_t Id(uint64_t project_name_hash);
+ //
+ // Default |key_rotation_period| is 90 days.
+ uint64_t Id(uint64_t project_name_hash, int key_rotation_period);
// Returns when the key for |project_name_hash| was last rotated, in days
// since epoch. Returns nullopt if the key doesn't exist.
@@ -109,12 +112,15 @@ class KeyData {
void OnWrite(WriteStatus status);
// Ensure that a valid key exists for |project|, and return it. Either returns
- // a string of size |kKeySize| or absl::nullopt, which indicates an error.
- absl::optional<std::string> ValidateAndGetKey(uint64_t project_name_hash);
-
- // Regenerate |key|, also updating the |last_rotation| and |rotation_period|.
- // This triggers a save.
- void UpdateKey(KeyProto* key, int last_rotation, int rotation_period);
+ // a string of size |kKeySize| or absl::nullopt, which indicates an error. If
+ // a key doesn't exist OR if the key needs to be rotated, then a new key with
+ // |key_rotation_period| will be created.
+ absl::optional<std::string> ValidateAndGetKey(uint64_t project_name_hash,
+ int key_rotation_period);
+
+ // Regenerate |key|, also updating the |last_key_rotation| and
+ // |key_rotation_period|. This triggers a save.
+ void UpdateKey(KeyProto* key, int last_key_rotation, int key_rotation_period);
// Storage for keys.
std::unique_ptr<PersistentProto<KeyDataProto>> proto_;
diff --git a/chromium/components/metrics/structured/key_data_unittest.cc b/chromium/components/metrics/structured/key_data_unittest.cc
index b2407457065..9b06a9f541c 100644
--- a/chromium/components/metrics/structured/key_data_unittest.cc
+++ b/chromium/components/metrics/structured/key_data_unittest.cc
@@ -62,6 +62,8 @@ constexpr char kValueTwo[] = "value two";
constexpr char kValueOneHash[] = "805B8790DC69B773";
constexpr char kValueTwoHash[] = "87CEF12FB15E0B3A";
+constexpr int kKeyRotationPeriod = 90;
+
std::string HashToHex(const uint64_t hash) {
return base::HexEncode(&hash, sizeof(uint64_t));
}
@@ -161,8 +163,8 @@ class KeyDataTest : public testing::Test {
TEST_F(KeyDataTest, GeneratesKeysForProjects) {
// Make key data and use two keys, in order to generate them.
MakeKeyData();
- key_data_->Id(kProjectOneHash);
- key_data_->Id(kProjectTwoHash);
+ key_data_->Id(kProjectOneHash, kKeyRotationPeriod);
+ key_data_->Id(kProjectTwoHash, kKeyRotationPeriod);
SaveKeyData();
const std::string key_one = GetKey(kProjectOneHash).key();
@@ -186,7 +188,7 @@ TEST_F(KeyDataTest, GeneratesDistinctKeys) {
// disk.
ResetState();
MakeKeyData();
- key_data_->Id(kProjectOneHash);
+ key_data_->Id(kProjectOneHash, kKeyRotationPeriod);
SaveKeyData();
keys.insert(GetKey(kProjectOneHash).key());
@@ -201,7 +203,7 @@ TEST_F(KeyDataTest, GeneratesDistinctKeys) {
TEST_F(KeyDataTest, ReuseExistingKeys) {
// Create a file with one key.
MakeKeyData();
- const uint64_t id_one = key_data_->Id(kProjectOneHash);
+ const uint64_t id_one = key_data_->Id(kProjectOneHash, kKeyRotationPeriod);
SaveKeyData();
ExpectKeyValidation(/*valid=*/0, /*created=*/1, /*rotated=*/0);
const std::string key_one = GetKey(kProjectOneHash).key();
@@ -211,7 +213,7 @@ TEST_F(KeyDataTest, ReuseExistingKeys) {
// Open the file again and check we use the same key.
MakeKeyData();
- const uint64_t id_two = key_data_->Id(kProjectOneHash);
+ const uint64_t id_two = key_data_->Id(kProjectOneHash, kKeyRotationPeriod);
ExpectKeyValidation(/*valid=*/1, /*created=*/1, /*rotated=*/0);
SaveKeyData();
const std::string key_two = GetKey(kProjectOneHash).key();
@@ -224,8 +226,10 @@ TEST_F(KeyDataTest, ReuseExistingKeys) {
// value.
TEST_F(KeyDataTest, DifferentEventsDifferentHashes) {
MakeKeyData();
- EXPECT_NE(key_data_->HmacMetric(kProjectOneHash, kMetricOneHash, "value"),
- key_data_->HmacMetric(kProjectTwoHash, kMetricOneHash, "value"));
+ EXPECT_NE(key_data_->HmacMetric(kProjectOneHash, kMetricOneHash, "value",
+ kKeyRotationPeriod),
+ key_data_->HmacMetric(kProjectTwoHash, kMetricOneHash, "value",
+ kKeyRotationPeriod));
ExpectNoErrors();
}
@@ -233,8 +237,10 @@ TEST_F(KeyDataTest, DifferentEventsDifferentHashes) {
// value.
TEST_F(KeyDataTest, DifferentMetricsDifferentHashes) {
MakeKeyData();
- EXPECT_NE(key_data_->HmacMetric(kProjectOneHash, kMetricOneHash, "value"),
- key_data_->HmacMetric(kProjectOneHash, kMetricTwoHash, "value"));
+ EXPECT_NE(key_data_->HmacMetric(kProjectOneHash, kMetricOneHash, "value",
+ kKeyRotationPeriod),
+ key_data_->HmacMetric(kProjectOneHash, kMetricTwoHash, "value",
+ kKeyRotationPeriod));
ExpectNoErrors();
}
@@ -242,52 +248,56 @@ TEST_F(KeyDataTest, DifferentMetricsDifferentHashes) {
// metric.
TEST_F(KeyDataTest, DifferentValuesDifferentHashes) {
MakeKeyData();
- EXPECT_NE(key_data_->HmacMetric(kProjectOneHash, kMetricOneHash, "first"),
- key_data_->HmacMetric(kProjectOneHash, kMetricOneHash, "second"));
+ EXPECT_NE(key_data_->HmacMetric(kProjectOneHash, kMetricOneHash, "first",
+ kKeyRotationPeriod),
+ key_data_->HmacMetric(kProjectOneHash, kMetricOneHash, "second",
+ kKeyRotationPeriod));
ExpectNoErrors();
}
// Ensure that KeyData::UserId is the expected value of SHA256(key).
TEST_F(KeyDataTest, CheckUserIDs) {
- SetupKey(kProjectOneHash, kKey, Today(), 90);
+ SetupKey(kProjectOneHash, kKey, Today(), kKeyRotationPeriod);
MakeKeyData();
- EXPECT_EQ(HashToHex(key_data_->Id(kProjectOneHash)), kUserId);
- EXPECT_NE(HashToHex(key_data_->Id(kProjectTwoHash)), kUserId);
+ EXPECT_EQ(HashToHex(key_data_->Id(kProjectOneHash, kKeyRotationPeriod)),
+ kUserId);
+ EXPECT_NE(HashToHex(key_data_->Id(kProjectTwoHash, kKeyRotationPeriod)),
+ kUserId);
ExpectKeyValidation(/*valid=*/1, /*created=*/1, /*rotated=*/0);
ExpectNoErrors();
}
// Ensure that KeyData::Hash returns expected values for a known key and value.
TEST_F(KeyDataTest, CheckHashes) {
- SetupKey(kProjectOneHash, kKey, Today(), 90);
+ SetupKey(kProjectOneHash, kKey, Today(), kKeyRotationPeriod);
MakeKeyData();
EXPECT_EQ(HashToHex(key_data_->HmacMetric(kProjectOneHash, kMetricOneHash,
- kValueOne)),
+ kValueOne, kKeyRotationPeriod)),
kValueOneHash);
EXPECT_EQ(HashToHex(key_data_->HmacMetric(kProjectOneHash, kMetricTwoHash,
- kValueTwo)),
+ kValueTwo, kKeyRotationPeriod)),
kValueTwoHash);
ExpectKeyValidation(/*valid=*/2, /*created=*/0, /*rotated=*/0);
ExpectNoErrors();
}
-// Check that keys for a event are correctly rotated after the default 90 day
-// rotation period.
+// Check that keys for a event are correctly rotated after a given rotation
+// period.
TEST_F(KeyDataTest, KeysRotated) {
const int start_day = Today();
- SetupKey(kProjectOneHash, kKey, start_day, 90);
+ SetupKey(kProjectOneHash, kKey, start_day, kKeyRotationPeriod);
MakeKeyData();
- const uint64_t first_id = key_data_->Id(kProjectOneHash);
+ const uint64_t first_id = key_data_->Id(kProjectOneHash, kKeyRotationPeriod);
EXPECT_EQ(key_data_->LastKeyRotation(kProjectOneHash), start_day);
ExpectKeyValidation(/*valid=*/1, /*created=*/0, /*rotated=*/0);
{
- // Advancing by 50 days, the key should not be rotated.
- time_.Advance(base::Days(50));
- EXPECT_EQ(key_data_->Id(kProjectOneHash), first_id);
+ // Advancing by |kKeyRotationPeriod|-1 days, the key should not be rotated.
+ time_.Advance(base::Days(kKeyRotationPeriod - 1));
+ EXPECT_EQ(key_data_->Id(kProjectOneHash, kKeyRotationPeriod), first_id);
EXPECT_EQ(key_data_->LastKeyRotation(kProjectOneHash), start_day);
SaveKeyData();
@@ -296,33 +306,70 @@ TEST_F(KeyDataTest, KeysRotated) {
}
{
- // Advancing by another 50 days, the key should be rotated and the last
- // rotation day should be incremented by 90.
- time_.Advance(base::Days(50));
- EXPECT_NE(key_data_->Id(kProjectOneHash), first_id);
+ // Advancing by another |key_rotation_period|+1 days, the key should be
+ // rotated and the last rotation day should be incremented by
+ // |key_rotation_period|.
+ time_.Advance(base::Days(kKeyRotationPeriod + 1));
+ EXPECT_NE(key_data_->Id(kProjectOneHash, kKeyRotationPeriod), first_id);
SaveKeyData();
- EXPECT_EQ(GetKey(kProjectOneHash).last_rotation(), start_day + 90);
- EXPECT_EQ(key_data_->LastKeyRotation(kProjectOneHash), start_day + 90);
+ int expected_last_key_rotation = start_day + 2 * kKeyRotationPeriod;
+ EXPECT_EQ(GetKey(kProjectOneHash).last_rotation(),
+ expected_last_key_rotation);
+ EXPECT_EQ(key_data_->LastKeyRotation(kProjectOneHash),
+ expected_last_key_rotation);
ExpectKeyValidation(/*valid=*/2, /*created=*/0, /*rotated=*/1);
- // The rotation period could change here if it were ever updated in the xml.
- // This test relies on it being 90 days.
- ASSERT_EQ(GetKey(kProjectOneHash).rotation_period(), 90);
+ ASSERT_EQ(GetKey(kProjectOneHash).rotation_period(), kKeyRotationPeriod);
}
{
- // Advancing by 453 days, the last rotation day should now 6 periods of 90
- // days ahead.
- time_.Advance(base::Days(453));
- key_data_->Id(kProjectOneHash);
+ // Advancing by |2* kKeyRotationPeriod| days, the last rotation day should
+ // now 4 periods of |kKeyRotationPeriod| days ahead.
+ time_.Advance(base::Days(kKeyRotationPeriod * 2));
+ key_data_->Id(kProjectOneHash, kKeyRotationPeriod);
SaveKeyData();
- EXPECT_EQ(GetKey(kProjectOneHash).last_rotation(), start_day + 6 * 90);
- EXPECT_EQ(key_data_->LastKeyRotation(kProjectOneHash), start_day + 6 * 90);
+ int expected_last_key_rotation = start_day + 4 * kKeyRotationPeriod;
+ EXPECT_EQ(GetKey(kProjectOneHash).last_rotation(),
+ expected_last_key_rotation);
+ EXPECT_EQ(key_data_->LastKeyRotation(kProjectOneHash),
+ expected_last_key_rotation);
ExpectKeyValidation(/*valid=*/2, /*created=*/0, /*rotated=*/2);
}
}
+// Check that keys with updated rotations are correctly rotated.
+TEST_F(KeyDataTest, KeysWithUpdatedRotations) {
+ int first_key_rotation_period = 60;
+
+ const int start_day = Today();
+ SetupKey(kProjectOneHash, kKey, start_day, first_key_rotation_period);
+
+ MakeKeyData();
+ const uint64_t first_id =
+ key_data_->Id(kProjectOneHash, first_key_rotation_period);
+ EXPECT_EQ(key_data_->LastKeyRotation(kProjectOneHash), start_day);
+ ExpectKeyValidation(/*valid=*/1, /*created=*/0, /*rotated=*/0);
+
+ // Advance days by |new_key_rotation_period| + 1. This should fall within the
+ // rotation of the |new_key_rotation_period| but outside
+ // |first_key_rotation_period|.
+ int new_key_rotation_period = 50;
+ time_.Advance(base::Days(new_key_rotation_period + 1));
+ const uint64_t second_id =
+ key_data_->Id(kProjectOneHash, new_key_rotation_period);
+ EXPECT_NE(first_id, second_id);
+ SaveKeyData();
+
+ // Key should have been rotated with new_key_rotation_period.
+ int expected_last_key_rotation = start_day + new_key_rotation_period;
+ EXPECT_EQ(GetKey(kProjectOneHash).last_rotation(),
+ expected_last_key_rotation);
+ EXPECT_EQ(key_data_->LastKeyRotation(kProjectOneHash),
+ expected_last_key_rotation);
+ ExpectKeyValidation(/*valid=*/1, /*created=*/0, /*rotated=*/1);
+}
+
} // namespace structured
} // namespace metrics
diff --git a/chromium/components/metrics/structured/mojom/event_mojom_traits.cc b/chromium/components/metrics/structured/mojom/event_mojom_traits.cc
index f24544cb037..aa889b23650 100644
--- a/chromium/components/metrics/structured/mojom/event_mojom_traits.cc
+++ b/chromium/components/metrics/structured/mojom/event_mojom_traits.cc
@@ -22,18 +22,17 @@ UnionTraits<metrics::structured::mojom::MetricValueDataView,
GetTag(const metrics::structured::Event::MetricValue& metric_value) {
switch (metric_value.type) {
case metrics::structured::Event::MetricType::kHmac:
- return metrics::structured::mojom::MetricValueDataView::Tag::HMAC_VALUE;
+ return metrics::structured::mojom::MetricValueDataView::Tag::kHmacValue;
case metrics::structured::Event::MetricType::kLong:
- return metrics::structured::mojom::MetricValueDataView::Tag::LONG_VALUE;
+ return metrics::structured::mojom::MetricValueDataView::Tag::kLongValue;
case metrics::structured::Event::MetricType::kInt:
- return metrics::structured::mojom::MetricValueDataView::Tag::INT_VALUE;
+ return metrics::structured::mojom::MetricValueDataView::Tag::kIntValue;
case metrics::structured::Event::MetricType::kDouble:
- return metrics::structured::mojom::MetricValueDataView::Tag::DOUBLE_VALUE;
+ return metrics::structured::mojom::MetricValueDataView::Tag::kDoubleValue;
case metrics::structured::Event::MetricType::kRawString:
- return metrics::structured::mojom::MetricValueDataView::Tag::
- RAW_STR_VALUE;
+ return metrics::structured::mojom::MetricValueDataView::Tag::kRawStrValue;
case metrics::structured::Event::MetricType::kBoolean:
- return metrics::structured::mojom::MetricValueDataView::Tag::BOOL_VALUE;
+ return metrics::structured::mojom::MetricValueDataView::Tag::kBoolValue;
}
}
@@ -43,7 +42,7 @@ bool UnionTraits<metrics::structured::mojom::MetricValueDataView,
Read(metrics::structured::mojom::MetricValueDataView metric,
metrics::structured::Event::MetricValue* out) {
switch (metric.tag()) {
- case metrics::structured::mojom::MetricValueDataView::Tag::HMAC_VALUE: {
+ case metrics::structured::mojom::MetricValueDataView::Tag::kHmacValue: {
std::string hmac_value;
if (!metric.ReadHmacValue(&hmac_value))
return false;
@@ -51,19 +50,19 @@ bool UnionTraits<metrics::structured::mojom::MetricValueDataView,
out->value = base::Value(std::move(hmac_value));
break;
}
- case metrics::structured::mojom::MetricValueDataView::Tag::LONG_VALUE:
+ case metrics::structured::mojom::MetricValueDataView::Tag::kLongValue:
out->type = metrics::structured::Event::MetricType::kLong;
out->value = base::Value(base::NumberToString(metric.long_value()));
break;
- case metrics::structured::mojom::MetricValueDataView::Tag::INT_VALUE:
+ case metrics::structured::mojom::MetricValueDataView::Tag::kIntValue:
out->type = metrics::structured::Event::MetricType::kInt;
out->value = base::Value(metric.int_value());
break;
- case metrics::structured::mojom::MetricValueDataView::Tag::DOUBLE_VALUE:
+ case metrics::structured::mojom::MetricValueDataView::Tag::kDoubleValue:
out->type = metrics::structured::Event::MetricType::kDouble;
out->value = base::Value(metric.double_value());
break;
- case metrics::structured::mojom::MetricValueDataView::Tag::RAW_STR_VALUE: {
+ case metrics::structured::mojom::MetricValueDataView::Tag::kRawStrValue: {
std::string raw_str_value;
if (!metric.ReadRawStrValue(&raw_str_value))
return false;
@@ -71,7 +70,7 @@ bool UnionTraits<metrics::structured::mojom::MetricValueDataView,
out->value = base::Value(std::move(raw_str_value));
break;
}
- case metrics::structured::mojom::MetricValueDataView::Tag::BOOL_VALUE:
+ case metrics::structured::mojom::MetricValueDataView::Tag::kBoolValue:
out->type = metrics::structured::Event::MetricType::kBoolean;
out->value = base::Value(metric.bool_value());
break;
diff --git a/chromium/components/metrics/structured/project_validator.cc b/chromium/components/metrics/structured/project_validator.cc
index 44c519f3e1c..f73561e42fa 100644
--- a/chromium/components/metrics/structured/project_validator.cc
+++ b/chromium/components/metrics/structured/project_validator.cc
@@ -14,11 +14,13 @@ namespace structured {
ProjectValidator::ProjectValidator(uint64_t project_hash,
IdType id_type,
IdScope id_scope,
- EventType event_type)
+ EventType event_type,
+ int key_rotation_period)
: project_hash_(project_hash),
id_type_(id_type),
id_scope_(id_scope),
- event_type_(event_type) {}
+ event_type_(event_type),
+ key_rotation_period_(key_rotation_period) {}
ProjectValidator::~ProjectValidator() = default;
diff --git a/chromium/components/metrics/structured/project_validator.h b/chromium/components/metrics/structured/project_validator.h
index 8591d3ca210..75ac0f8d28b 100644
--- a/chromium/components/metrics/structured/project_validator.h
+++ b/chromium/components/metrics/structured/project_validator.h
@@ -34,19 +34,22 @@ class ProjectValidator {
IdType id_type() const { return id_type_; }
IdScope id_scope() const { return id_scope_; }
EventType event_type() const { return event_type_; }
+ int key_rotation_period() const { return key_rotation_period_; }
protected:
// Should not be constructed directly.
ProjectValidator(uint64_t project_hash,
IdType id_type,
IdScope id_scope,
- EventType event_type);
+ EventType event_type,
+ int key_rotation_period);
private:
const uint64_t project_hash_;
const IdType id_type_;
const IdScope id_scope_;
const EventType event_type_;
+ const int key_rotation_period_;
};
} // namespace structured
diff --git a/chromium/components/metrics/structured/recorder.cc b/chromium/components/metrics/structured/recorder.cc
index b4a7e05c862..9776b7cd305 100644
--- a/chromium/components/metrics/structured/recorder.cc
+++ b/chromium/components/metrics/structured/recorder.cc
@@ -95,6 +95,12 @@ void Recorder::OnReportingStateChanged(bool enabled) {
}
}
+void Recorder::OnHardwareClassInitialized() {
+ for (auto& observer : observers_) {
+ observer.OnHardwareClassInitialized();
+ }
+}
+
void Recorder::SetUiTaskRunner(
const scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
ui_task_runner_ = ui_task_runner;
diff --git a/chromium/components/metrics/structured/recorder.h b/chromium/components/metrics/structured/recorder.h
index 7f3b778e136..bc4a98d5157 100644
--- a/chromium/components/metrics/structured/recorder.h
+++ b/chromium/components/metrics/structured/recorder.h
@@ -49,6 +49,8 @@ class Recorder : public StructuredMetricsClient::RecordingDelegate {
virtual void OnProfileAdded(const base::FilePath& profile_path) = 0;
// Called on a call to OnReportingStateChanged.
virtual void OnReportingStateChanged(bool enabled) = 0;
+ // Called when hardware class has been loaded.
+ virtual void OnHardwareClassInitialized(){};
// Called on a call to LastKeyRotation.
virtual absl::optional<int> LastKeyRotation(uint64_t project_name_hash) = 0;
};
@@ -76,6 +78,9 @@ class Recorder : public StructuredMetricsClient::RecordingDelegate {
// Notifies observers that metrics reporting has been enabled or disabled.
void OnReportingStateChanged(bool enabled);
+ // Notifies observers that hardware class has been loaded.
+ void OnHardwareClassInitialized();
+
void SetUiTaskRunner(
const scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
diff --git a/chromium/components/metrics/structured/structured_metrics_features.cc b/chromium/components/metrics/structured/structured_metrics_features.cc
index c98b74a3a7f..6a93440f6a0 100644
--- a/chromium/components/metrics/structured/structured_metrics_features.cc
+++ b/chromium/components/metrics/structured/structured_metrics_features.cc
@@ -16,8 +16,12 @@ const base::Feature kStructuredMetrics{"EnableStructuredMetrics",
const base::Feature kBluetoothSessionizedMetrics{
"BluetoothSessionizedMetrics", base::FEATURE_ENABLED_BY_DEFAULT};
+// TODO(crbug/1249222): Clean feature up by 05/02/2023.
const base::Feature kUseCrosApiInterface{"UseCrosApiInterface",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kDelayUploadUntilHwid("DelayUploadUntilHwid",
+ base::FEATURE_DISABLED_BY_DEFAULT);
bool IsIndependentMetricsUploadEnabled() {
return base::GetFieldTrialParamByFeatureAsBool(
diff --git a/chromium/components/metrics/structured/structured_metrics_features.h b/chromium/components/metrics/structured/structured_metrics_features.h
index 96cc71c1327..e49b2587f37 100644
--- a/chromium/components/metrics/structured/structured_metrics_features.h
+++ b/chromium/components/metrics/structured/structured_metrics_features.h
@@ -20,6 +20,9 @@ extern const base::Feature kBluetoothSessionizedMetrics;
// processes not in Chrome Ash (ie Lacros) to use Structured metrics.
extern const base::Feature kUseCrosApiInterface;
+// Delays appending structured metrics events until HWID has been loaded.
+extern const base::Feature kDelayUploadUntilHwid;
+
// TODO(crbug.com/1148168): This is a temporary switch to revert structured
// metrics upload to its old behaviour. Old behaviour:
// - all metrics are uploaded in the main UMA upload
diff --git a/chromium/components/metrics/structured/structured_metrics_provider.cc b/chromium/components/metrics/structured/structured_metrics_provider.cc
index 3becf22ce98..e96b35c31fb 100644
--- a/chromium/components/metrics/structured/structured_metrics_provider.cc
+++ b/chromium/components/metrics/structured/structured_metrics_provider.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/feature_list.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/current_thread.h"
@@ -261,10 +262,18 @@ void StructuredMetricsProvider::OnReportingStateChanged(bool enabled) {
}
}
+void StructuredMetricsProvider::OnHardwareClassInitialized() {
+ hardware_class_initialized_ = true;
+}
+
void StructuredMetricsProvider::ProvideCurrentSessionData(
ChromeUserMetricsExtension* uma_proto) {
DCHECK(base::CurrentUIThread::IsSet());
- if (!recording_enabled_ || init_state_ != InitState::kInitialized) {
+ if (!recording_enabled_ || init_state_ != InitState::kInitialized)
+ return;
+
+ if (base::FeatureList::IsEnabled(kDelayUploadUntilHwid) &&
+ !hardware_class_initialized_) {
return;
}
@@ -291,6 +300,11 @@ bool StructuredMetricsProvider::HasIndependentMetrics() {
return false;
}
+ if (base::FeatureList::IsEnabled(kDelayUploadUntilHwid) &&
+ !hardware_class_initialized_) {
+ return false;
+ }
+
return events_.get()->get()->non_uma_events_size() != 0;
}
@@ -304,6 +318,12 @@ void StructuredMetricsProvider::ProvideIndependentMetrics(
return;
}
+ if (base::FeatureList::IsEnabled(kDelayUploadUntilHwid) &&
+ !hardware_class_initialized_) {
+ std::move(done_callback).Run(false);
+ return;
+ }
+
last_provided_independent_metrics_ = base::Time::Now();
LogNumEventsInUpload(events_.get()->get()->non_uma_events_size());
@@ -391,7 +411,7 @@ void StructuredMetricsProvider::RecordEventFromEventBase(
switch (event.id_type()) {
case IdType::kProjectId:
event_proto->set_profile_event_id(
- key_data->Id(event.project_name_hash()));
+ key_data->Id(event.project_name_hash(), event.key_rotation_period()));
break;
case IdType::kUmaId:
// TODO(crbug.com/1148168): Unimplemented.
@@ -427,7 +447,8 @@ void StructuredMetricsProvider::RecordEventFromEventBase(
switch (metric.type) {
case EventBase::MetricType::kHmac:
metric_proto->set_value_hmac(key_data->HmacMetric(
- event.project_name_hash(), metric.name_hash, metric.hmac_value));
+ event.project_name_hash(), metric.name_hash, metric.hmac_value,
+ event.key_rotation_period()));
break;
case EventBase::MetricType::kInt:
metric_proto->set_value_int64(metric.int_value);
diff --git a/chromium/components/metrics/structured/structured_metrics_provider.h b/chromium/components/metrics/structured/structured_metrics_provider.h
index 7af0ef56855..fc855329bf7 100644
--- a/chromium/components/metrics/structured/structured_metrics_provider.h
+++ b/chromium/components/metrics/structured/structured_metrics_provider.h
@@ -66,6 +66,7 @@ class StructuredMetricsProvider : public metrics::MetricsProvider,
private:
friend class Recorder;
friend class StructuredMetricsProviderTest;
+ friend class StructuredMetricsProviderHwidTest;
// State machine for step 4 of initialization. These are stored in three files
// that are asynchronously read from disk at startup. When all files have
@@ -88,6 +89,7 @@ class StructuredMetricsProvider : public metrics::MetricsProvider,
void OnProfileAdded(const base::FilePath& profile_path) override;
void OnRecord(const EventBase& event) override;
void OnReportingStateChanged(bool enabled) override;
+ void OnHardwareClassInitialized() override;
absl::optional<int> LastKeyRotation(uint64_t project_name_hash) override;
// metrics::MetricsProvider:
@@ -164,6 +166,9 @@ class StructuredMetricsProvider : public metrics::MetricsProvider,
// state will be purged upon initialization.
bool purge_state_on_init_ = false;
+ // Tracks whether hardware class has been loaded.
+ bool hardware_class_initialized_ = false;
+
// The last time we provided independent metrics.
base::Time last_provided_independent_metrics_;
diff --git a/chromium/components/metrics/structured/structured_metrics_provider_unittest.cc b/chromium/components/metrics/structured/structured_metrics_provider_unittest.cc
index d8dcff7b37b..77bd204e802 100644
--- a/chromium/components/metrics/structured/structured_metrics_provider_unittest.cc
+++ b/chromium/components/metrics/structured/structured_metrics_provider_unittest.cc
@@ -12,6 +12,7 @@
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
+#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_mock_clock_override.h"
@@ -268,6 +269,45 @@ class StructuredMetricsProviderTest : public testing::Test {
base::ScopedTempDir temp_dir_;
};
+// Test with kDelayUploadUntilHwid feature enabled.
+class StructuredMetricsProviderHwidTest : public StructuredMetricsProviderTest {
+ protected:
+ void SetUp() override {
+ scoped_feature_list_.InitAndEnableFeature(
+ metrics::structured::kDelayUploadUntilHwid);
+
+ StructuredMetricsProviderTest::SetUp();
+ }
+
+ void InitializeHwid() { provider_->OnHardwareClassInitialized(); }
+
+ bool events_retrieved() { return events_retrieved_; }
+
+ StructuredDataProto GetMetrics() {
+ // Independent metrics are only reported at intervals. So advance time to
+ // ensure HasIndependentMetrics will return true if there are recorded
+ // metrics.
+ task_environment_.AdvanceClock(base::Hours(1));
+
+ ChromeUserMetricsExtension uma_proto;
+
+ // Copy events from disk to proto.
+ if (provider_->HasIndependentMetrics()) {
+ provider_->ProvideIndependentMetrics(
+ base::BindLambdaForTesting(
+ [this](bool success) { events_retrieved_ = success; }),
+ &uma_proto, nullptr);
+ Wait();
+ return uma_proto.structured_data();
+ }
+
+ return StructuredDataProto::default_instance();
+ }
+
+ private:
+ bool events_retrieved_ = false;
+};
+
// Simple test to ensure initialization works correctly in the case of a
// first-time run.
TEST_F(StructuredMetricsProviderTest, ProviderInitializesFromBlankSlate) {
@@ -794,5 +834,22 @@ TEST_F(StructuredMetricsProviderTest, LastKeyRotation) {
EXPECT_GE(last_rotation, today - 90);
}
+TEST_F(StructuredMetricsProviderHwidTest, EventsNotSentIfHwidNotInitialized) {
+ Init();
+
+ events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1).Record();
+ events::v2::test_project_one::TestEventOne().SetTestMetricTwo(2).Record();
+
+ // HWID has not been set. Events should still persist in files.
+ EXPECT_EQ(GetMetrics().events_size(), 0);
+
+ InitializeHwid();
+
+ // HWID has been set. Events should be ready to upload.
+ EXPECT_EQ(GetMetrics().events_size(), 2);
+
+ ExpectNoErrors();
+}
+
} // namespace structured
} // namespace metrics
diff --git a/chromium/components/metrics/system_memory_stats_recorder_linux.cc b/chromium/components/metrics/system_memory_stats_recorder_linux.cc
index b15a05ed8eb..9e32773c176 100644
--- a/chromium/components/metrics/system_memory_stats_recorder_linux.cc
+++ b/chromium/components/metrics/system_memory_stats_recorder_linux.cc
@@ -7,7 +7,6 @@
#include "base/metrics/histogram_macros.h"
#include "base/process/process_metrics.h"
#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
namespace metrics {
@@ -31,7 +30,7 @@ namespace metrics {
UMA_HISTOGRAM_LINEAR(name, sample, 2500, 50)
void RecordMemoryStats(RecordMemoryStatsType type) {
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
// Record graphics GEM object size in a histogram with 50 MB buckets.
int mem_gpu_mb = 0;
bool mem_gpu_result = false;
@@ -58,8 +57,7 @@ void RecordMemoryStats(RecordMemoryStatsType type) {
// On Intel, graphics objects are in anonymous pages, but on ARM they are
// not. For a total "allocated count" add in graphics pages on ARM.
int mem_allocated_mb = (memory.active_anon + memory.inactive_anon) / 1024;
-#if (BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
- defined(ARCH_CPU_ARM_FAMILY)
+#if BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
mem_allocated_mb += mem_gpu_mb;
#endif
@@ -83,7 +81,7 @@ void RecordMemoryStats(RecordMemoryStatsType type) {
}
}
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
// Record shared memory (used by renderer/GPU buffers).
int mem_shmem_mb = memory.shmem / 1024;
switch (type) {
diff --git a/chromium/components/metrics/unsent_log_store.cc b/chromium/components/metrics/unsent_log_store.cc
index 6b0ce7cebe6..1db3af5a017 100644
--- a/chromium/components/metrics/unsent_log_store.cc
+++ b/chromium/components/metrics/unsent_log_store.cc
@@ -290,10 +290,12 @@ void UnsentLogStore::TrimLogs() {
std::vector<std::unique_ptr<LogInfo>> trimmed_list;
size_t bytes_used = 0;
- // The distance of the staged log from the end of the list of logs. Usually
- // this is 0 (end of list). We mark so we can correct adjust the
- // staged_log_index after log trimming.
- size_t staged_index_distance = 0;
+ // The distance of the staged log from the end of the list of logs, which is
+ // usually 0 (end of list). This is used in case there is currently a staged
+ // log, which may or may not get trimmed. We want to keep track of the new
+ // position of the staged log after trimming so that we can update
+ // |staged_log_index_|.
+ absl::optional<size_t> staged_index_distance;
// Reverse order, so newest ones are prioritized.
for (int i = list_.size() - 1; i >= 0; --i) {
@@ -330,8 +332,14 @@ void UnsentLogStore::TrimLogs() {
// We may need to adjust the staged index since the number of logs may be
// reduced. However, we want to make sure not to change the index if there is
// no log staged.
- if (staged_log_index_ != -1) {
- staged_log_index_ = list_.size() - 1 - staged_index_distance;
+ if (staged_index_distance.has_value()) {
+ staged_log_index_ = list_.size() - 1 - staged_index_distance.value();
+ } else {
+ // Set |staged_log_index_| to -1. It might already be -1. E.g., at the time
+ // we are trimming logs, there was no staged log. However, it is also
+ // possible that we trimmed away the staged log, so we need to update the
+ // index to -1.
+ staged_log_index_ = -1;
}
}
diff --git a/chromium/components/metrics/unsent_log_store_unittest.cc b/chromium/components/metrics/unsent_log_store_unittest.cc
index c79ce104872..be2888e525b 100644
--- a/chromium/components/metrics/unsent_log_store_unittest.cc
+++ b/chromium/components/metrics/unsent_log_store_unittest.cc
@@ -296,6 +296,44 @@ TEST_F(UnsentLogStoreTest, LongAndLargeLogList) {
result_unsent_log_store.ExpectNextLog(target_log);
}
+// Store a set of logs over the length limit, and over the minimum number of
+// bytes. The first log will be a staged log that should be trimmed away. This
+// should make the log store not have a staged log anymore.
+TEST_F(UnsentLogStoreTest, TrimStagedLog) {
+ TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
+
+ // Make each log byte count the limit.
+ size_t log_size = kLogByteLimit;
+
+ // Create a target log that will be the staged log that we want to trim away.
+ std::string target_log = "First that should be trimmed";
+ target_log += GenerateLogWithMinCompressedSize(log_size);
+ LogMetadata log_metadata;
+ unsent_log_store.StoreLog(target_log, log_metadata);
+ unsent_log_store.StageNextLog();
+ EXPECT_TRUE(unsent_log_store.has_staged_log());
+
+ // Add |kLogCountLimit| additional logs.
+ std::string log_data = GenerateLogWithMinCompressedSize(log_size);
+ for (size_t i = 0; i < kLogCountLimit; ++i) {
+ unsent_log_store.StoreLog(log_data, log_metadata);
+ }
+
+ EXPECT_EQ(kLogCountLimit + 1, unsent_log_store.size());
+ unsent_log_store.TrimAndPersistUnsentLogs();
+
+ // Verify that the first log (the staged one) was trimmed away, and that the
+ // log store does not consider to have any staged log anymore. The other logs
+ // are not trimmed because the most recent logs are prioritized and we trim
+ // until we have |kLogCountLimit| logs.
+ EXPECT_EQ(kLogCountLimit, unsent_log_store.size());
+ EXPECT_FALSE(unsent_log_store.has_staged_log());
+ // Verify that all of the logs in the log store are not the |target_log|.
+ while (unsent_log_store.size() > 0) {
+ unsent_log_store.ExpectNextLog(log_data);
+ }
+}
+
// Check that the store/stage/discard functions work as expected.
TEST_F(UnsentLogStoreTest, Staging) {
TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
diff --git a/chromium/components/metrics_services_manager/metrics_services_manager.cc b/chromium/components/metrics_services_manager/metrics_services_manager.cc
index 2ba4a034786..31d987c34e3 100644
--- a/chromium/components/metrics_services_manager/metrics_services_manager.cc
+++ b/chromium/components/metrics_services_manager/metrics_services_manager.cc
@@ -100,6 +100,33 @@ void MetricsServicesManager::UpdatePermissions(bool current_may_record,
}
}
+ // If metrics reporting goes from not consented to consented, create and
+ // persist a client ID (either generate a new one or promote the provisional
+ // client ID if this is the first run). This can occur in the following
+ // situations:
+ // 1. The user enables metrics reporting in the FRE
+ // 2. The user enables metrics reporting in settings, crash bubble, etc.
+ // 3. On startup, after fetching the enable status from the previous session
+ // (if enabled)
+ //
+ // ForceClientIdCreation() may be called again later on via
+ // MetricsService::EnableRecording(), but in that case,
+ // ForceClientIdCreation() will be a no-op (will return early since a client
+ // ID will already exist).
+ //
+ // ForceClientIdCreation() must be called here, otherwise, in cases where the
+ // user is sampled out, the passed |current_may_record| will be false, which
+ // will result in not calling ForceClientIdCreation() in
+ // MetricsService::EnableRecording() later on. This is problematic because
+ // in the FRE, if the user consents to metrics reporting, this will cause the
+ // provisional client ID to not be promoted/stored as the client ID. In the
+ // next run, a different client ID will be generated and stored, which will
+ // result in different trial assignments—and the client may even be sampled
+ // in at that time.
+ if (!consent_given_ && current_consent_given) {
+ client_->GetMetricsStateManager()->ForceClientIdCreation();
+ }
+
// Stash the current permissions so that we can update the services correctly
// when preferences change.
may_record_ = current_may_record;
diff --git a/chromium/components/minidump_uploader/BUILD.gn b/chromium/components/minidump_uploader/BUILD.gn
index bf257d3d595..9c46b65d8fe 100644
--- a/chromium/components/minidump_uploader/BUILD.gn
+++ b/chromium/components/minidump_uploader/BUILD.gn
@@ -31,6 +31,8 @@ static_library("minidump_uploader") {
android_library("minidump_uploader_java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/crash/android:anr_collector_java",
"//net/android:net_java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/components/minidump_uploader/OWNERS b/chromium/components/minidump_uploader/OWNERS
index c1dcc7b2be7..201c993e616 100644
--- a/chromium/components/minidump_uploader/OWNERS
+++ b/chromium/components/minidump_uploader/OWNERS
@@ -1,2 +1,4 @@
hazems@chromium.org
+mheikal@chromium.org
+smaier@chromium.org
wnwen@chromium.org
diff --git a/chromium/components/minidump_uploader/rewrite_minidumps_as_mimes.cc b/chromium/components/minidump_uploader/rewrite_minidumps_as_mimes.cc
index 0dbdc37b57c..956b63dd86a 100644
--- a/chromium/components/minidump_uploader/rewrite_minidumps_as_mimes.cc
+++ b/chromium/components/minidump_uploader/rewrite_minidumps_as_mimes.cc
@@ -103,12 +103,8 @@ bool MimeifyReportWithKeyValuePairs(
static_cast<uint32_t>(crashpad::Signals::kSimulatedSigno)) {
UMA_HISTOGRAM_ENUMERATION(
"Stability.Android.ProcessedRealMinidumps", count_type);
- } else {
- UMA_HISTOGRAM_ENUMERATION(
- "Stability.Android.ProcessedSimulatedMinidumps", count_type);
}
}
- // TODO(wnwen): Add histogram for number of null exceptions.
}
#endif // BUILDFLAG(IS_ANDROID)
}
diff --git a/chromium/components/mirroring/browser/single_client_video_capture_host_unittest.cc b/chromium/components/mirroring/browser/single_client_video_capture_host_unittest.cc
index 0c5a666c1c9..a19cc4df9fa 100644
--- a/chromium/components/mirroring/browser/single_client_video_capture_host_unittest.cc
+++ b/chromium/components/mirroring/browser/single_client_video_capture_host_unittest.cc
@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/memory/raw_ptr.h"
+#include "base/memory/unsafe_shared_memory_region.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
@@ -159,7 +160,7 @@ class MockVideoCaptureObserver final
MOCK_METHOD1(OnStateChangedCall, void(media::mojom::VideoCaptureState state));
MOCK_METHOD1(OnVideoCaptureErrorCall, void(media::VideoCaptureError error));
void OnStateChanged(media::mojom::VideoCaptureResultPtr result) override {
- if (result->which() == media::mojom::VideoCaptureResult::Tag::STATE)
+ if (result->which() == media::mojom::VideoCaptureResult::Tag::kState)
OnStateChangedCall(result->get_state());
else
OnVideoCaptureErrorCall(result->get_error_code());
@@ -254,9 +255,8 @@ class SingleClientVideoCaptureHostTest : public ::testing::Test {
EXPECT_CALL(*consumer_, OnBufferCreatedCall(expected_buffer_context_id))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
media::mojom::VideoBufferHandlePtr stub_buffer_handle =
- media::mojom::VideoBufferHandle::New();
- stub_buffer_handle->set_shared_buffer_handle(
- mojo::SharedBufferHandle::Create(10));
+ media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
+ base::UnsafeSharedMemoryRegion::Create(10));
frame_receiver_->OnNewBuffer(buffer_id, std::move(stub_buffer_handle));
run_loop.Run();
}
diff --git a/chromium/components/mirroring/mojom/mirroring_service.mojom b/chromium/components/mirroring/mojom/mirroring_service.mojom
index 343c2627622..79b6dabad21 100644
--- a/chromium/components/mirroring/mojom/mirroring_service.mojom
+++ b/chromium/components/mirroring/mojom/mirroring_service.mojom
@@ -8,6 +8,7 @@ import "components/mirroring/mojom/cast_message_channel.mojom";
import "components/mirroring/mojom/resource_provider.mojom";
import "components/mirroring/mojom/session_observer.mojom";
import "components/mirroring/mojom/session_parameters.mojom";
+import "sandbox/policy/mojom/context.mojom";
import "sandbox/policy/mojom/sandbox.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
@@ -26,6 +27,7 @@ interface MirroringService {
// to the mirroring receiver. |inbound_channel| receives the cast messages
// from the mirroring receiver to this service.
// To stop the session, just close the message pipe.
+ [AllowedContext=sandbox.mojom.Context.kBrowser]
Start(SessionParameters params,
gfx.mojom.Size max_resolution,
pending_remote<SessionObserver> observer,
diff --git a/chromium/components/mirroring/mojom/resource_provider.mojom b/chromium/components/mirroring/mojom/resource_provider.mojom
index e338e55b5f8..6b95f7770b4 100644
--- a/chromium/components/mirroring/mojom/resource_provider.mojom
+++ b/chromium/components/mirroring/mojom/resource_provider.mojom
@@ -10,6 +10,7 @@ import "media/mojo/mojom/remoting.mojom";
import "media/mojo/mojom/audio_data_pipe.mojom";
import "media/mojo/mojom/audio_input_stream.mojom";
import "media/mojo/mojom/audio_parameters.mojom";
+import "sandbox/policy/mojom/context.mojom";
import "services/viz/public/mojom/gpu.mojom";
// This interface is used by ResourceProvider to notify that an audio input
@@ -23,7 +24,8 @@ interface AudioStreamCreatorClient {
};
// This interface is used by the Mirroring Service to ask the browser to provide
-// resources.
+// resources. It passes privileged interfaces.
+[RequireContext=sandbox.mojom.Context.kPrivilegedUtility]
interface ResourceProvider {
// Acquires an interface to the GPU process.
BindGpu(pending_receiver<viz.mojom.Gpu> receiver);
@@ -33,6 +35,7 @@ interface ResourceProvider {
// events (Start, Stop, etc) from its client in the renderer process.
GetVideoCaptureHost(pending_receiver<media.mojom.VideoCaptureHost> receiver);
+ [AllowedContext=sandbox.mojom.Context.kPrivilegedUtility]
GetNetworkContext(pending_receiver<network.mojom.NetworkContext> receiver);
CreateAudioStream(pending_remote<AudioStreamCreatorClient> client,
media.mojom.AudioParameters param,
diff --git a/chromium/components/mirroring/service/BUILD.gn b/chromium/components/mirroring/service/BUILD.gn
index b36f4b9f870..61add7623b6 100644
--- a/chromium/components/mirroring/service/BUILD.gn
+++ b/chromium/components/mirroring/service/BUILD.gn
@@ -51,6 +51,7 @@ component("mirroring_service") {
"//media/capture:capture_base",
"//media/capture/mojom:video_capture",
"//media/cast:common",
+ "//media/cast:encoding",
"//media/cast:net",
"//media/cast:sender",
"//media/gpu",
diff --git a/chromium/components/mirroring/service/captured_audio_input_unittest.cc b/chromium/components/mirroring/service/captured_audio_input_unittest.cc
index b84e444c2ca..e130b978a25 100644
--- a/chromium/components/mirroring/service/captured_audio_input_unittest.cc
+++ b/chromium/components/mirroring/service/captured_audio_input_unittest.cc
@@ -18,6 +18,7 @@
#include "mojo/public/cpp/system/platform_handle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/utility/utility.h"
using ::testing::InvokeWithoutArgs;
@@ -81,7 +82,7 @@ class CapturedAudioInputTest : public ::testing::Test {
stream_client_.reset();
audio_client->StreamCreated(
std::move(pending_stream), stream_client_.BindNewPipeAndPassReceiver(),
- {base::in_place, base::ReadOnlySharedMemoryRegion::Create(1024).region,
+ {absl::in_place, base::ReadOnlySharedMemoryRegion::Create(1024).region,
mojo::PlatformHandle(foreign_socket.Take())});
}
diff --git a/chromium/components/mirroring/service/mirroring_features.cc b/chromium/components/mirroring/service/mirroring_features.cc
index 28df046e37d..a10354a920a 100644
--- a/chromium/components/mirroring/service/mirroring_features.cc
+++ b/chromium/components/mirroring/service/mirroring_features.cc
@@ -8,11 +8,6 @@
namespace mirroring {
namespace features {
-// Controls whether the Open Screen libcast SenderSession is used for
-// initializing and managing streaming sessions, or the legacy implementation.
-const base::Feature kOpenscreenCastStreamingSession{
- "OpenscreenCastStreamingSession", base::FEATURE_DISABLED_BY_DEFAULT};
-
// Controls whether offers using the AV1 codec for video encoding are included
// in mirroring negotiations in addition to the VP8 codec, or offers only
// include VP8.
diff --git a/chromium/components/mirroring/service/remoting_sender.cc b/chromium/components/mirroring/service/remoting_sender.cc
index 22ea8e5b9dd..5500c51df80 100644
--- a/chromium/components/mirroring/service/remoting_sender.cc
+++ b/chromium/components/mirroring/service/remoting_sender.cc
@@ -12,8 +12,8 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
+#include "media/cast/common/sender_encoded_frame.h"
#include "media/cast/constants.h"
-#include "media/cast/sender/sender_encoded_frame.h"
#include "media/mojo/common/mojo_data_pipe_read_write.h"
namespace mirroring {
@@ -25,10 +25,10 @@ RemotingSender::RemotingSender(
mojo::ScopedDataPipeConsumerHandle pipe,
mojo::PendingReceiver<media::mojom::RemotingDataStreamSender> stream_sender,
base::OnceClosure error_callback)
- : FrameSender(cast_environment,
- transport,
- config,
- media::cast::NewFixedCongestionControl(config.max_bitrate)),
+ : frame_sender_(media::cast::FrameSender::Create(cast_environment,
+ config,
+ transport,
+ this)),
clock_(cast_environment->Clock()),
error_callback_(std::move(error_callback)),
data_pipe_reader_(new media::MojoDataPipeReader(std::move(pipe))),
@@ -67,14 +67,13 @@ int RemotingSender::GetNumberOfFramesInEncoder() const {
return 0;
}
-base::TimeDelta RemotingSender::GetInFlightMediaDuration() const {
+base::TimeDelta RemotingSender::GetEncoderBacklogDuration() const {
NOTREACHED();
return base::TimeDelta();
}
-void RemotingSender::OnCancelSendingFrames() {
- // One or more frames were canceled. This may allow pending input operations
- // to complete.
+void RemotingSender::OnFrameCanceled(media::cast::FrameId frame_id) {
+ // The frame cancellation may allow for the next input task to complete.
ProcessNextInputTask();
}
@@ -121,40 +120,41 @@ void RemotingSender::TrySendFrame() {
}
// If there would be too many frames in-flight, do not proceed.
- if (GetUnacknowledgedFrameCount() >= media::cast::kMaxUnackedFrames) {
+ if (frame_sender_->GetUnacknowledgedFrameCount() >=
+ media::cast::kMaxUnackedFrames) {
VLOG(1) << "Cannot send frame now because too many frames are in flight.";
return;
}
- const bool is_first_frame_to_be_sent = last_send_time_.is_null();
- const media::cast::FrameId frame_id = is_first_frame_to_be_sent
- ? media::cast::FrameId::first()
- : (last_sent_frame_id_ + 1);
-
- base::TimeTicks last_frame_reference_time = last_send_time_;
+ const bool is_first_frame = (next_frame_id_ == media::cast::FrameId::first());
auto remoting_frame = std::make_unique<media::cast::SenderEncodedFrame>();
- remoting_frame->frame_id = frame_id;
+ remoting_frame->frame_id = next_frame_id_;
if (flow_restart_pending_) {
remoting_frame->dependency = media::cast::EncodedFrame::KEY;
flow_restart_pending_ = false;
} else {
- DCHECK(!is_first_frame_to_be_sent);
+ DCHECK(!is_first_frame);
remoting_frame->dependency = media::cast::EncodedFrame::DEPENDENT;
}
remoting_frame->referenced_frame_id =
remoting_frame->dependency == media::cast::EncodedFrame::KEY
- ? frame_id
- : frame_id - 1;
+ ? next_frame_id_
+ : next_frame_id_ - 1;
remoting_frame->reference_time = clock_->NowTicks();
remoting_frame->encode_completion_time = remoting_frame->reference_time;
+
+ base::TimeTicks last_frame_reference_time;
media::cast::RtpTimeTicks last_frame_rtp_timestamp;
- if (is_first_frame_to_be_sent) {
+ if (is_first_frame) {
last_frame_reference_time = remoting_frame->reference_time;
last_frame_rtp_timestamp =
media::cast::RtpTimeTicks() - media::cast::RtpTimeDelta::FromTicks(1);
} else {
- last_frame_rtp_timestamp = GetRecordedRtpTimestamp(frame_id - 1);
+ last_frame_reference_time = frame_sender_->LastSendTime();
+ last_frame_rtp_timestamp =
+ frame_sender_->GetRecordedRtpTimestamp(next_frame_id_ - 1);
}
+
// Ensure each successive frame's RTP timestamp is unique, but otherwise just
// base it on the reference time.
remoting_frame->rtp_timestamp =
@@ -165,8 +165,8 @@ void RemotingSender::TrySendFrame() {
media::cast::kRemotingRtpTimebase));
remoting_frame->data.swap(next_frame_data_);
- SendEncodedFrame(0, std::move(remoting_frame));
-
+ frame_sender_->EnqueueFrame(std::move(remoting_frame));
+ next_frame_id_++;
OnInputTaskComplete();
}
diff --git a/chromium/components/mirroring/service/remoting_sender.h b/chromium/components/mirroring/service/remoting_sender.h
index fedf825dd1d..2328a8b8e56 100644
--- a/chromium/components/mirroring/service/remoting_sender.h
+++ b/chromium/components/mirroring/service/remoting_sender.h
@@ -33,7 +33,7 @@ namespace mirroring {
// a CastTransport.
class COMPONENT_EXPORT(MIRRORING_SERVICE) RemotingSender final
: public media::mojom::RemotingDataStreamSender,
- public media::cast::FrameSender {
+ public media::cast::FrameSender::Client {
public:
// |transport| is expected to outlive this class.
RemotingSender(scoped_refptr<media::cast::CastEnvironment> cast_environment,
@@ -62,10 +62,10 @@ class COMPONENT_EXPORT(MIRRORING_SERVICE) RemotingSender final
void SendFrame(uint32_t frame_size) override;
void CancelInFlightData() override;
- // FrameSender override.
+ // FrameSender::Client overrides.
int GetNumberOfFramesInEncoder() const override;
- base::TimeDelta GetInFlightMediaDuration() const override;
- void OnCancelSendingFrames() override;
+ base::TimeDelta GetEncoderBacklogDuration() const override;
+ void OnFrameCanceled(media::cast::FrameId frame_id) override;
// Attempt to run next pending input task, popping the head of the input queue
// as each task succeeds.
@@ -90,6 +90,9 @@ class COMPONENT_EXPORT(MIRRORING_SERVICE) RemotingSender final
SEQUENCE_CHECKER(sequence_checker_);
+ // The backing frame sender implementation.
+ std::unique_ptr<media::cast::FrameSender> frame_sender_;
+
raw_ptr<const base::TickClock> clock_;
// Callback that is run to notify when a fatal error occurs.
@@ -120,6 +123,10 @@ class COMPONENT_EXPORT(MIRRORING_SERVICE) RemotingSender final
// to mark the next frame as the start of a new sequence.
bool flow_restart_pending_;
+ // The next frame's ID. Before any frames are sent, this will be the ID of
+ // the first frame.
+ media::cast::FrameId next_frame_id_ = media::cast::FrameId::first();
+
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<RemotingSender> weak_factory_{this};
};
diff --git a/chromium/components/mirroring/service/remoting_sender_unittest.cc b/chromium/components/mirroring/service/remoting_sender_unittest.cc
index 1e1b9d08d6e..cad77cd5a1a 100644
--- a/chromium/components/mirroring/service/remoting_sender_unittest.cc
+++ b/chromium/components/mirroring/service/remoting_sender_unittest.cc
@@ -11,6 +11,7 @@
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "base/time/default_tick_clock.h"
+#include "media/cast/common/encoded_frame.h"
#include "media/cast/constants.h"
#include "media/cast/net/cast_transport.h"
#include "media/cast/test/utility/default_config.h"
@@ -91,7 +92,7 @@ class FakeTransport final : public media::cast::CastTransport {
const media::cast::ReceiverRtcpEventSubscriber::RtcpEvents& e) override {}
void AddRtpReceiverReport(const media::cast::RtcpReportBlock& b) override {}
void SendRtcpFromRtpReceiver() override {}
- void SetOptions(const base::DictionaryValue& options) override {}
+ void SetOptions(const base::Value::Dict& options) override {}
private:
std::vector<media::cast::EncodedFrame> sent_frames_;
@@ -140,7 +141,8 @@ class RemotingSenderTest : public ::testing::Test {
// Give CastRemotingSender a small RTT measurement to prevent kickstart
// testing from taking too long.
- remoting_sender_->OnMeasuredRoundTripTime(base::Milliseconds(1));
+ remoting_sender_->frame_sender_->OnMeasuredRoundTripTime(
+ base::Milliseconds(1));
RunPendingTasks();
}
@@ -157,11 +159,11 @@ class RemotingSenderTest : public ::testing::Test {
protected:
media::cast::FrameId latest_acked_frame_id() const {
- return remoting_sender_->latest_acked_frame_id_;
+ return remoting_sender_->frame_sender_->LatestAckedFrameId();
}
int NumberOfFramesInFlight() {
- return remoting_sender_->GetUnacknowledgedFrameCount();
+ return remoting_sender_->frame_sender_->GetUnacknowledgedFrameCount();
}
size_t GetSizeOfNextFrameData() {
@@ -202,7 +204,7 @@ class RemotingSenderTest : public ::testing::Test {
void AckUpToAndIncluding(media::cast::FrameId frame_id) {
media::cast::RtcpCastMessage cast_feedback(receiver_ssrc_);
cast_feedback.ack_frame_id = frame_id;
- remoting_sender_->OnReceivedCastFeedback(cast_feedback);
+ remoting_sender_->frame_sender_->OnReceivedCastFeedback(cast_feedback);
}
void AckOldestInFlightFrames(int count) {
diff --git a/chromium/components/mirroring/service/session.cc b/chromium/components/mirroring/service/session.cc
index 5cc7d294d28..58843d18024 100644
--- a/chromium/components/mirroring/service/session.cc
+++ b/chromium/components/mirroring/service/session.cc
@@ -41,9 +41,9 @@
#include "media/audio/audio_input_device.h"
#include "media/base/audio_capturer_source.h"
#include "media/base/bind_to_current_loop.h"
+#include "media/cast/encoding/external_video_encoder.h"
#include "media/cast/net/cast_transport.h"
#include "media/cast/sender/audio_sender.h"
-#include "media/cast/sender/external_video_encoder.h"
#include "media/cast/sender/video_sender.h"
#include "media/gpu/gpu_video_accelerator_util.h"
#include "media/mojo/clients/mojo_video_encode_accelerator.h"
diff --git a/chromium/components/mirroring/service/video_capture_client.cc b/chromium/components/mirroring/service/video_capture_client.cc
index d520080ca47..f1ac09dca56 100644
--- a/chromium/components/mirroring/service/video_capture_client.cc
+++ b/chromium/components/mirroring/service/video_capture_client.cc
@@ -94,7 +94,7 @@ void VideoCaptureClient::RequestRefreshFrame() {
void VideoCaptureClient::OnStateChanged(
media::mojom::VideoCaptureResultPtr result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (result->which() == media::mojom::VideoCaptureResult::Tag::STATE) {
+ if (result->which() == media::mojom::VideoCaptureResult::Tag::kState) {
media::mojom::VideoCaptureState state = result->get_state();
DVLOG(2) << __func__ << " state: " << state;
switch (state) {
@@ -107,7 +107,6 @@ void VideoCaptureClient::OnStateChanged(
case media::mojom::VideoCaptureState::STOPPED:
case media::mojom::VideoCaptureState::ENDED:
client_buffers_.clear();
- mapped_buffers_.clear();
weak_factory_.InvalidateWeakPtrs();
error_callback_.Reset();
frame_deliver_callback_.Reset();
@@ -128,7 +127,7 @@ void VideoCaptureClient::OnNewBuffer(
DVLOG(3) << __func__ << ": buffer_id=" << buffer_id;
if (!buffer_handle->is_read_only_shmem_region() &&
- !buffer_handle->is_shared_buffer_handle()) {
+ !buffer_handle->is_unsafe_shmem_region()) {
#if BUILDFLAG(IS_MAC)
if (!buffer_handle->is_gpu_memory_buffer_handle()) {
NOTIMPLEMENTED();
@@ -139,8 +138,8 @@ void VideoCaptureClient::OnNewBuffer(
return;
#endif
}
- const auto insert_result = client_buffers_.emplace(
- std::make_pair(buffer_id, std::move(buffer_handle)));
+ const auto insert_result =
+ client_buffers_.insert({buffer_id, std::move(buffer_handle)});
DCHECK(insert_result.second);
}
@@ -203,55 +202,37 @@ void VideoCaptureClient::OnBufferReady(
buffer->info->visible_rect, buffer->info->timestamp);
buffer_finished_callback = media::BindToCurrentLoop(base::BindOnce(
&VideoCaptureClient::OnClientBufferFinished, weak_factory_.GetWeakPtr(),
- buffer->buffer_id, base::ReadOnlySharedMemoryMapping()));
+ buffer->buffer_id, MappingKeepAlive()));
#else
NOTREACHED();
#endif
- } else if (buffer_iter->second->is_shared_buffer_handle()) {
- // TODO(crbug.com/843117): Remove this case after migrating
- // media::VideoCaptureDeviceClient to the new shared memory API.
- auto mapping_iter = mapped_buffers_.find(buffer->buffer_id);
- const size_t buffer_size = media::VideoFrame::AllocationSize(
+ } else if (buffer_iter->second->is_unsafe_shmem_region()) {
+ base::WritableSharedMemoryMapping mapping =
+ buffer_iter->second->get_unsafe_shmem_region().Map();
+ const size_t frame_allocation_size = media::VideoFrame::AllocationSize(
buffer->info->pixel_format, buffer->info->coded_size);
- if (mapping_iter != mapped_buffers_.end() &&
- buffer_size > mapping_iter->second.second) {
- // Unmaps shared memory for too-small region.
- mapped_buffers_.erase(mapping_iter);
- mapping_iter = mapped_buffers_.end();
- }
- if (mapping_iter == mapped_buffers_.end()) {
- mojo::ScopedSharedBufferMapping mapping =
- buffer_iter->second->get_shared_buffer_handle()->Map(buffer_size);
- if (!mapping) {
- video_capture_host_->ReleaseBuffer(DeviceId(), buffer->buffer_id,
- media::VideoCaptureFeedback());
- return;
- }
- mapping_iter = mapped_buffers_
- .emplace(std::make_pair(
- buffer->buffer_id,
- MappingAndSize(std::move(mapping), buffer_size)))
- .first;
+ if (mapping.IsValid() && mapping.size() >= frame_allocation_size) {
+ frame = media::VideoFrame::WrapExternalData(
+ buffer->info->pixel_format, buffer->info->coded_size,
+ buffer->info->visible_rect, buffer->info->visible_rect.size(),
+ mapping.GetMemoryAs<uint8_t>(), frame_allocation_size,
+ buffer->info->timestamp);
}
- const auto& buffer_it = mapping_iter->second;
- frame = media::VideoFrame::WrapExternalData(
- buffer->info->pixel_format, buffer->info->coded_size,
- buffer->info->visible_rect, buffer->info->visible_rect.size(),
- reinterpret_cast<uint8_t*>(buffer_it.first.get()), buffer_it.second,
- buffer->info->timestamp);
buffer_finished_callback = media::BindToCurrentLoop(base::BindOnce(
&VideoCaptureClient::OnClientBufferFinished, weak_factory_.GetWeakPtr(),
- buffer->buffer_id, base::ReadOnlySharedMemoryMapping()));
+ buffer->buffer_id, std::move(mapping)));
} else {
base::ReadOnlySharedMemoryMapping mapping =
buffer_iter->second->get_read_only_shmem_region().Map();
const size_t frame_allocation_size = media::VideoFrame::AllocationSize(
buffer->info->pixel_format, buffer->info->coded_size);
if (mapping.IsValid() && mapping.size() >= frame_allocation_size) {
+ // TODO(https://crbug.com/1316810): This code should not be casting
+ // const-ness away from ReadOnlySharedMemoryRegion...
frame = media::VideoFrame::WrapExternalData(
buffer->info->pixel_format, buffer->info->coded_size,
buffer->info->visible_rect, buffer->info->visible_rect.size(),
- const_cast<uint8_t*>(static_cast<const uint8_t*>(mapping.memory())),
+ const_cast<uint8_t*>(mapping.GetMemoryAs<uint8_t>()),
frame_allocation_size, buffer->info->timestamp);
}
buffer_finished_callback = media::BindToCurrentLoop(base::BindOnce(
@@ -307,20 +288,15 @@ void VideoCaptureClient::OnBufferDestroyed(int32_t buffer_id) {
const auto& buffer_iter = client_buffers_.find(buffer_id);
if (buffer_iter != client_buffers_.end())
client_buffers_.erase(buffer_iter);
- const auto& mapping_iter = mapped_buffers_.find(buffer_id);
- if (mapping_iter != mapped_buffers_.end())
- mapped_buffers_.erase(mapping_iter);
}
-void VideoCaptureClient::OnClientBufferFinished(
- int buffer_id,
- base::ReadOnlySharedMemoryMapping mapping) {
+void VideoCaptureClient::OnClientBufferFinished(int buffer_id,
+ MappingKeepAlive mapping) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(3) << __func__ << ": buffer_id=" << buffer_id;
// Buffer was already destroyed.
if (client_buffers_.find(buffer_id) == client_buffers_.end()) {
- DCHECK(mapped_buffers_.find(buffer_id) == mapped_buffers_.end());
return;
}
diff --git a/chromium/components/mirroring/service/video_capture_client.h b/chromium/components/mirroring/service/video_capture_client.h
index 1e18cd0f26a..ea837083634 100644
--- a/chromium/components/mirroring/service/video_capture_client.h
+++ b/chromium/components/mirroring/service/video_capture_client.h
@@ -8,7 +8,7 @@
#include "base/callback.h"
#include "base/component_export.h"
#include "base/containers/flat_map.h"
-#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/shared_memory_mapping.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
@@ -16,7 +16,7 @@
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
-#include "mojo/public/cpp/system/buffer.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
namespace media {
class VideoFrame;
@@ -74,9 +74,12 @@ class COMPONENT_EXPORT(MIRRORING_SERVICE) VideoCaptureClient
// Called by the VideoFrame destructor.
static void DidFinishConsumingFrame(BufferFinishedCallback callback);
- // Reports the utilization, unmaps the shared memory, and returns the buffer.
+ // Reports the utilization to release the buffer for potential reuse.
+ using MappingKeepAlive = absl::variant<absl::monostate,
+ base::WritableSharedMemoryMapping,
+ base::ReadOnlySharedMemoryMapping>;
void OnClientBufferFinished(int buffer_id,
- base::ReadOnlySharedMemoryMapping mapping);
+ MappingKeepAlive mapping_keep_alive);
const media::VideoCaptureParams params_;
const mojo::Remote<media::mojom::VideoCaptureHost> video_capture_host_;
@@ -101,15 +104,6 @@ class COMPONENT_EXPORT(MIRRORING_SERVICE) VideoCaptureClient
// The callback to deliver the received frame.
FrameDeliverCallback frame_deliver_callback_;
- // TODO(crbug.com/843117): Remove the MappingMap after migrating
- // media::VideoCaptureDeviceClient to the new shared memory API.
- using MappingAndSize = std::pair<mojo::ScopedSharedBufferMapping, uint32_t>;
- using MappingMap = base::flat_map<int32_t, MappingAndSize>;
- // Stores the mapped buffers and their size. Each buffer is added the first
- // time the mapping is done or a larger size is requested.
- // |buffer_id| is the key to this map.
- MappingMap mapped_buffers_;
-
// Latest received feedback.
media::VideoCaptureFeedback feedback_;
diff --git a/chromium/components/mirroring/service/video_capture_client_unittest.cc b/chromium/components/mirroring/service/video_capture_client_unittest.cc
index de729206a42..f1e0d75c86f 100644
--- a/chromium/components/mirroring/service/video_capture_client_unittest.cc
+++ b/chromium/components/mirroring/service/video_capture_client_unittest.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/unsafe_shared_memory_region.h"
#include "base/run_loop.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
@@ -90,8 +91,8 @@ class VideoCaptureClientTest : public ::testing::Test,
const bool use_shared_buffer = GetParam();
if (use_shared_buffer) {
client_->OnNewBuffer(
- buffer_id, media::mojom::VideoBufferHandle::NewSharedBufferHandle(
- mojo::SharedBufferHandle::Create(buffer_size)));
+ buffer_id, media::mojom::VideoBufferHandle::NewUnsafeShmemRegion(
+ base::UnsafeSharedMemoryRegion::Create(buffer_size)));
} else {
client_->OnNewBuffer(
buffer_id,
diff --git a/chromium/components/ml/mojom/web_platform_model.mojom b/chromium/components/ml/mojom/web_platform_model.mojom
index eecc382e322..f0b555b4070 100644
--- a/chromium/components/ml/mojom/web_platform_model.mojom
+++ b/chromium/components/ml/mojom/web_platform_model.mojom
@@ -42,7 +42,7 @@ enum DevicePreference {
kGpu = 2,
};
-[Stable, Extensible]
+[Stable]
struct CreateModelLoaderOptions {
// #Threads used in model inference.
// 0 means the backend can decide it automatically (e.g. equals the number of
@@ -81,7 +81,7 @@ enum DataType {
// Represents the information of a tensor. The tensor infos of input and output
// tensors are sent from ml-service to the client as a result of model loading.
-[Stable, Extensible]
+[Stable]
struct TensorInfo {
// The total size of the tensor buffer in bytes. This is the most important
// information. Ml-service will use it to check whether the input tensor is
@@ -95,7 +95,7 @@ struct TensorInfo {
// Represents the model information. Currently it contains the tensor info of
// input and output tensors.
-[Stable, Extensible]
+[Stable]
struct ModelInfo {
map<string, TensorInfo> input_tensor_info@0;
map<string, TensorInfo> output_tensor_info@1;
diff --git a/chromium/components/module_installer/android/BUILD.gn b/chromium/components/module_installer/android/BUILD.gn
index 39b6d30790b..2e7478bb2ce 100644
--- a/chromium/components/module_installer/android/BUILD.gn
+++ b/chromium/components/module_installer/android/BUILD.gn
@@ -33,11 +33,14 @@ android_library("module_installer_java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
"//components/crash/android:java",
"//third_party/android_deps:com_google_android_play_core_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
+ public_deps = [ "//build/android:build_java" ]
+
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
}
@@ -75,6 +78,7 @@ java_annotation_processor("module_interface_processor") {
[ "//third_party/android_deps:auto_service_processor" ]
deps = [
":module_interface_java",
+ "//build/android:build_java",
"//third_party/android_deps:com_google_auto_service_auto_service_annotations_java",
"//third_party/android_deps:com_google_guava_guava_java",
"//third_party/android_deps:com_squareup_javapoet_java",
diff --git a/chromium/components/module_installer/android/module_desc_java.gni b/chromium/components/module_installer/android/module_desc_java.gni
index edf39d98653..d712f28d1d4 100644
--- a/chromium/components/module_installer/android/module_desc_java.gni
+++ b/chromium/components/module_installer/android/module_desc_java.gni
@@ -70,10 +70,7 @@ template("module_desc_java") {
}
android_library(_target_name) {
- deps = [
- "//base:base_java",
- "//components/module_installer/android:module_installer_java",
- ]
+ deps = [ "//components/module_installer/android:module_installer_java" ]
srcjar_deps = [ ":${_target_name}__srcjar" ]
}
}
diff --git a/chromium/components/nacl/features.gni b/chromium/components/nacl/features.gni
index a2547a2cd6d..5b430458214 100644
--- a/chromium/components/nacl/features.gni
+++ b/chromium/components/nacl/features.gni
@@ -8,11 +8,11 @@ import("//build/config/gclient_args.gni")
declare_args() {
# Enables Native Client support.
# Temporarily disable nacl on arm64 linux to get rid of compilation errors.
- # Intentionally and permanently disable nacl om arm64 mac.
+ # Intentionally and permanently disable nacl on arm64 mac.
# TODO: When mipsel-nacl-clang is available, drop the exclusion.
enable_nacl =
checkout_nacl && target_os != "ios" && !is_android && !is_fuchsia &&
- !is_chromecast && current_cpu != "mipsel" && current_cpu != "mips64el" &&
+ !is_castos && current_cpu != "mipsel" && current_cpu != "mips64el" &&
target_cpu != "arm64" && !(is_win && host_os != "win") &&
!(is_mac && (host_os != "mac" || target_cpu != "x64"))
}
diff --git a/chromium/components/navigation_interception/android/BUILD.gn b/chromium/components/navigation_interception/android/BUILD.gn
index cdd78e1339a..5894bfdc608 100644
--- a/chromium/components/navigation_interception/android/BUILD.gn
+++ b/chromium/components/navigation_interception/android/BUILD.gn
@@ -6,9 +6,8 @@ import("//build/config/android/rules.gni")
android_library("navigation_interception_java") {
deps = [
- "//base:base_java",
+ "//base:jni_java",
"//content/public/android:content_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
"//ui/android:ui_no_recycler_view_java",
"//url:gurl_java",
"//url:origin_java",
diff --git a/chromium/components/navigation_interception/intercept_navigation_delegate.cc b/chromium/components/navigation_interception/intercept_navigation_delegate.cc
index 1d85c5179a5..15c4b5166bc 100644
--- a/chromium/components/navigation_interception/intercept_navigation_delegate.cc
+++ b/chromium/components/navigation_interception/intercept_navigation_delegate.cc
@@ -10,6 +10,7 @@
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/callback.h"
+#include "base/strings/escape.h"
#include "components/navigation_interception/jni_headers/InterceptNavigationDelegate_jni.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
@@ -17,7 +18,6 @@
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
-#include "net/base/escape.h"
#include "url/android/gurl_android.h"
#include "url/gurl.h"
@@ -32,6 +32,8 @@ namespace navigation_interception {
namespace {
+const int kMaxValidityOfUserGestureCarryoverInSeconds = 10;
+
const void* const kInterceptNavigationDelegateUserDataKey =
&kInterceptNavigationDelegateUserDataKey;
@@ -104,7 +106,7 @@ InterceptNavigationDelegate::~InterceptNavigationDelegate() {
bool InterceptNavigationDelegate::ShouldIgnoreNavigation(
content::NavigationHandle* navigation_handle) {
GURL escaped_url = escape_external_handler_value_
- ? GURL(net::EscapeExternalHandlerValue(
+ ? GURL(base::EscapeExternalHandlerValue(
navigation_handle->GetURL().spec()))
: navigation_handle->GetURL();
@@ -117,9 +119,16 @@ bool InterceptNavigationDelegate::ShouldIgnoreNavigation(
if (jdelegate.is_null())
return false;
+ bool has_user_gesture = navigation_handle->HasUserGesture();
+ bool apply_user_gesture_carryover =
+ !has_user_gesture &&
+ base::TimeTicks::Now() - last_user_gesture_carryover_timestamp_ <=
+ base::Seconds(kMaxValidityOfUserGestureCarryoverInSeconds);
+
return Java_InterceptNavigationDelegate_shouldIgnoreNavigation(
env, jdelegate, navigation_handle->GetJavaNavigationHandle(),
- url::GURLAndroid::FromNativeGURL(env, escaped_url));
+ url::GURLAndroid::FromNativeGURL(env, escaped_url),
+ apply_user_gesture_carryover);
}
void InterceptNavigationDelegate::HandleExternalProtocolDialog(
@@ -128,7 +137,7 @@ void InterceptNavigationDelegate::HandleExternalProtocolDialog(
bool has_user_gesture,
const absl::optional<url::Origin>& initiating_origin) {
GURL escaped_url = escape_external_handler_value_
- ? GURL(net::EscapeExternalHandlerValue(url.spec()))
+ ? GURL(base::EscapeExternalHandlerValue(url.spec()))
: url;
if (!escaped_url.is_valid())
return;
@@ -144,4 +153,8 @@ void InterceptNavigationDelegate::HandleExternalProtocolDialog(
initiating_origin ? initiating_origin->CreateJavaObject() : nullptr);
}
+void InterceptNavigationDelegate::UpdateLastUserGestureCarryoverTimestamp() {
+ last_user_gesture_carryover_timestamp_ = base::TimeTicks::Now();
+}
+
} // namespace navigation_interception
diff --git a/chromium/components/navigation_interception/intercept_navigation_delegate.h b/chromium/components/navigation_interception/intercept_navigation_delegate.h
index 5446a2d58d4..6ae9ab57a01 100644
--- a/chromium/components/navigation_interception/intercept_navigation_delegate.h
+++ b/chromium/components/navigation_interception/intercept_navigation_delegate.h
@@ -40,7 +40,7 @@ namespace navigation_interception {
class InterceptNavigationDelegate : public base::SupportsUserData::Data {
public:
// Pass true for |escape_external_handler_value| to have
- // net::EscapeExternalHandlerValue() invoked on URLs passed to
+ // base::EscapeExternalHandlerValue() invoked on URLs passed to
// ShouldIgnoreNavigation() before the navigation is processed.
InterceptNavigationDelegate(JNIEnv* env,
jobject jdelegate,
@@ -76,8 +76,13 @@ class InterceptNavigationDelegate : public base::SupportsUserData::Data {
bool has_user_gesture,
const absl::optional<url::Origin>& initiating_origin);
+ // Updates |last_user_gesture_carryover_timestamp_| when user gesture is
+ // carried over.
+ void UpdateLastUserGestureCarryoverTimestamp();
+
private:
JavaObjectWeakGlobalRef weak_jdelegate_;
+ base::TimeTicks last_user_gesture_carryover_timestamp_;
bool escape_external_handler_value_ = false;
};
diff --git a/chromium/components/navigation_metrics/navigation_metrics.cc b/chromium/components/navigation_metrics/navigation_metrics.cc
index 3d44987ef4f..88095ae67c8 100644
--- a/chromium/components/navigation_metrics/navigation_metrics.cc
+++ b/chromium/components/navigation_metrics/navigation_metrics.cc
@@ -68,8 +68,7 @@ void RecordPrimaryMainFrameNavigation(
bool is_off_the_record,
profile_metrics::BrowserProfileType profile_type) {
Scheme scheme = GetScheme(url);
- UMA_HISTOGRAM_ENUMERATION("Navigation.MainFrameScheme2", scheme,
- Scheme::COUNT);
+ UMA_HISTOGRAM_ENUMERATION(kMainFrameScheme, scheme, Scheme::COUNT);
if (!is_same_document) {
UMA_HISTOGRAM_ENUMERATION("Navigation.MainFrameSchemeDifferentPage2",
scheme, Scheme::COUNT);
diff --git a/chromium/components/net_log/chrome_net_log.cc b/chromium/components/net_log/chrome_net_log.cc
index bb4650932ac..d3a8da148cb 100644
--- a/chromium/components/net_log/chrome_net_log.cc
+++ b/chromium/components/net_log/chrome_net_log.cc
@@ -16,34 +16,34 @@
namespace net_log {
-std::unique_ptr<base::DictionaryValue> GetPlatformConstantsForNetLog(
+base::Value::Dict GetPlatformConstantsForNetLog(
const base::CommandLine::StringType& command_line_string,
const std::string& channel_string) {
- auto constants_dict = std::make_unique<base::DictionaryValue>();
+ base::Value::Dict constants_dict;
// Add a dictionary with the version of the client and its command line
// arguments.
- base::DictionaryValue dict;
+ base::Value::Dict dict;
// We have everything we need to send the right values.
- dict.SetStringKey("name", version_info::GetProductName());
- dict.SetStringKey("version", version_info::GetVersionNumber());
- dict.SetStringKey("cl", version_info::GetLastChange());
- dict.SetStringKey("version_mod", channel_string);
- dict.SetStringKey(
- "official", version_info::IsOfficialBuild() ? "official" : "unofficial");
+ dict.Set("name", version_info::GetProductName());
+ dict.Set("version", version_info::GetVersionNumber());
+ dict.Set("cl", version_info::GetLastChange());
+ dict.Set("version_mod", channel_string);
+ dict.Set("official",
+ version_info::IsOfficialBuild() ? "official" : "unofficial");
std::string os_type = base::StringPrintf(
"%s: %s (%s)", base::SysInfo::OperatingSystemName().c_str(),
base::SysInfo::OperatingSystemVersion().c_str(),
base::SysInfo::OperatingSystemArchitecture().c_str());
- dict.SetStringKey("os_type", os_type);
+ dict.Set("os_type", os_type);
#if BUILDFLAG(IS_WIN)
- dict.SetStringKey("command_line", base::WideToUTF8(command_line_string));
+ dict.Set("command_line", base::WideToUTF8(command_line_string));
#else
- dict.SetStringKey("command_line", command_line_string);
+ dict.Set("command_line", command_line_string);
#endif
- constants_dict->SetKey("clientInfo", std::move(dict));
+ constants_dict.Set("clientInfo", std::move(dict));
return constants_dict;
}
diff --git a/chromium/components/net_log/chrome_net_log.h b/chromium/components/net_log/chrome_net_log.h
index d09324f05b0..68e72574069 100644
--- a/chromium/components/net_log/chrome_net_log.h
+++ b/chromium/components/net_log/chrome_net_log.h
@@ -9,10 +9,7 @@
#include <string>
#include "base/command_line.h"
-
-namespace base {
-class DictionaryValue;
-}
+#include "base/values.h"
namespace net_log {
@@ -23,7 +20,7 @@ namespace net_log {
// * The operating system version
//
// Safe to call on any thread.
-std::unique_ptr<base::DictionaryValue> GetPlatformConstantsForNetLog(
+base::Value::Dict GetPlatformConstantsForNetLog(
const base::CommandLine::StringType& command_line_string,
const std::string& channel_string);
diff --git a/chromium/components/net_log/net_export_file_writer.cc b/chromium/components/net_log/net_export_file_writer.cc
index d3e0659d93a..c21c4959ad4 100644
--- a/chromium/components/net_log/net_export_file_writer.cc
+++ b/chromium/components/net_log/net_export_file_writer.cc
@@ -84,8 +84,7 @@ NetExportFileWriter::NetExportFileWriter()
NetExportFileWriter::~NetExportFileWriter() {
if (net_log_exporter_) {
- net_log_exporter_->Stop(base::Value(base::Value::Type::DICTIONARY),
- base::DoNothing());
+ net_log_exporter_->Stop(base::Value::Dict(), base::DoNothing());
}
}
@@ -142,8 +141,8 @@ void NetExportFileWriter::StartNetLog(
network_context->CreateNetLogExporter(
net_log_exporter_.BindNewPipeAndPassReceiver());
- base::Value custom_constants = base::Value::FromUniquePtrValue(
- GetPlatformConstantsForNetLog(command_line_string, channel_string));
+ base::Value::Dict custom_constants =
+ GetPlatformConstantsForNetLog(command_line_string, channel_string);
net_log_exporter_.set_disconnect_handler(base::BindOnce(
&NetExportFileWriter::OnConnectionError, base::Unretained(this)));
@@ -159,7 +158,7 @@ void NetExportFileWriter::StartNetLog(
void NetExportFileWriter::StartNetLogAfterCreateFile(
net::NetLogCaptureMode capture_mode,
uint64_t max_file_size,
- base::Value custom_constants,
+ base::Value::Dict custom_constants,
base::File output_file) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(STATE_STARTING_LOG, state_);
@@ -201,8 +200,7 @@ void NetExportFileWriter::OnStartResult(net::NetLogCaptureMode capture_mode,
}
}
-void NetExportFileWriter::StopNetLog(
- std::unique_ptr<base::DictionaryValue> polled_data) {
+void NetExportFileWriter::StopNetLog(base::Value::Dict polled_data) {
DCHECK(thread_checker_.CalledOnValidThread());
if (state_ != STATE_LOGGING)
@@ -212,13 +210,10 @@ void NetExportFileWriter::StopNetLog(
NotifyStateObserversAsync();
- base::Value polled_data_value(base::Value::Type::DICTIONARY);
- if (polled_data)
- polled_data_value = base::Value::FromUniquePtrValue(std::move(polled_data));
// base::Unretained(this) is safe here since |net_log_exporter_| is owned by
// |this| and is a mojo InterfacePtr, which guarantees callback cancellation
// upon its destruction.
- net_log_exporter_->Stop(std::move(polled_data_value),
+ net_log_exporter_->Stop(std::move(polled_data),
base::BindOnce(&NetExportFileWriter::OnStopResult,
base::Unretained(this)));
}
diff --git a/chromium/components/net_log/net_export_file_writer.h b/chromium/components/net_log/net_export_file_writer.h
index 0bee7fe413a..92c615849ae 100644
--- a/chromium/components/net_log/net_export_file_writer.h
+++ b/chromium/components/net_log/net_export_file_writer.h
@@ -124,7 +124,7 @@ class NetExportFileWriter {
//
// |polled_data| is a JSON dictionary that will be appended to the end of the
// log; it's for adding additional info to the log that aren't events.
- void StopNetLog(std::unique_ptr<base::DictionaryValue> polled_data);
+ void StopNetLog(base::Value::Dict polled_data = base::Value::Dict());
// Creates a DictionaryValue summary of the state of the NetExportFileWriter
std::unique_ptr<base::DictionaryValue> GetState() const;
@@ -183,7 +183,7 @@ class NetExportFileWriter {
// logging after the output file has been created.
void StartNetLogAfterCreateFile(net::NetLogCaptureMode capture_mode,
uint64_t max_file_size,
- base::Value custom_constants,
+ base::Value::Dict custom_constants,
base::File log_file);
void OnStartResult(net::NetLogCaptureMode capture_mode, int result);
diff --git a/chromium/components/net_log/net_export_file_writer_unittest.cc b/chromium/components/net_log/net_export_file_writer_unittest.cc
index bf43d70c08e..23244ff9f63 100644
--- a/chromium/components/net_log/net_export_file_writer_unittest.cc
+++ b/chromium/components/net_log/net_export_file_writer_unittest.cc
@@ -70,14 +70,14 @@ class FakeNetLogExporter : public network::mojom::NetLogExporter {
~FakeNetLogExporter() override {}
void Start(base::File destination,
- base::Value extra_constants,
+ base::Value::Dict extra_constants,
net::NetLogCaptureMode capture_mode,
uint64_t max_file_size,
StartCallback callback) override {
std::move(callback).Run(net::OK);
}
- void Stop(base::Value polled_values, StopCallback callback) override {
+ void Stop(base::Value::Dict polled_values, StopCallback callback) override {
std::move(callback).Run(net::OK);
}
};
@@ -412,7 +412,7 @@ class NetExportFileWriterTest : public ::testing::Test {
// |default_log_path_|.
[[nodiscard]] ::testing::AssertionResult StopThenVerifyNewStateAndFile(
const base::FilePath& custom_log_path,
- std::unique_ptr<base::DictionaryValue> polled_data,
+ base::Value::Dict polled_data,
const std::string& expected_capture_mode_string) {
file_writer_.StopNetLog(std::move(polled_data));
std::unique_ptr<base::DictionaryValue> state =
@@ -570,11 +570,11 @@ TEST_F(NetExportFileWriterTest, StartAndStopWithAllCaptureModes) {
// StopNetLog(), should result in state change. The capture mode should
// match that of the first StartNetLog() call (called by
// StartThenVerifyNewState()).
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
- capture_mode_strings[i]));
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ base::FilePath(), base::Value::Dict(), capture_mode_strings[i]));
// Stopping a second time should be a no-op.
- file_writer()->StopNetLog(nullptr);
+ file_writer()->StopNetLog();
}
// Start and stop one more time just to make sure the last StopNetLog() call
@@ -583,8 +583,8 @@ TEST_F(NetExportFileWriterTest, StartAndStopWithAllCaptureModes) {
capture_mode_strings[0],
network_context()));
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
- capture_mode_strings[0]));
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ base::FilePath(), base::Value::Dict(), capture_mode_strings[0]));
}
// Verify the file sizes after two consecutive starts/stops are the same (even
@@ -596,8 +596,8 @@ TEST_F(NetExportFileWriterTest, StartClearsFile) {
base::FilePath(), net::NetLogCaptureMode::kDefault,
kCaptureModeDefaultString, network_context()));
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
- kCaptureModeDefaultString));
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ base::FilePath(), base::Value::Dict(), kCaptureModeDefaultString));
int64_t stop_file_size;
EXPECT_TRUE(base::GetFileSize(default_log_path(), &stop_file_size));
@@ -616,8 +616,8 @@ TEST_F(NetExportFileWriterTest, StartClearsFile) {
base::FilePath(), net::NetLogCaptureMode::kDefault,
kCaptureModeDefaultString, network_context()));
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
- kCaptureModeDefaultString));
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ base::FilePath(), base::Value::Dict(), kCaptureModeDefaultString));
int64_t new_stop_file_size;
EXPECT_TRUE(base::GetFileSize(default_log_path(), &new_stop_file_size));
@@ -634,8 +634,8 @@ TEST_F(NetExportFileWriterTest, AddEvent) {
base::FilePath(), net::NetLogCaptureMode::kDefault,
kCaptureModeDefaultString, network_context()));
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
- kCaptureModeDefaultString));
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ base::FilePath(), base::Value::Dict(), kCaptureModeDefaultString));
// Get file size without the event.
int64_t stop_file_size;
@@ -647,8 +647,8 @@ TEST_F(NetExportFileWriterTest, AddEvent) {
net_log()->AddGlobalEntry(net::NetLogEventType::CANCELLED);
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
- kCaptureModeDefaultString));
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ base::FilePath(), base::Value::Dict(), kCaptureModeDefaultString));
// Get file size after adding the event and make sure it's larger than before.
int64_t new_stop_file_size;
@@ -672,8 +672,8 @@ TEST_F(NetExportFileWriterTest, AddEventCustomPath) {
StartThenVerifyNewState(custom_log_path, net::NetLogCaptureMode::kDefault,
kCaptureModeDefaultString, network_context()));
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr,
- kCaptureModeDefaultString));
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ custom_log_path, base::Value::Dict(), kCaptureModeDefaultString));
// Get file size without the event.
int64_t stop_file_size;
@@ -685,8 +685,8 @@ TEST_F(NetExportFileWriterTest, AddEventCustomPath) {
net_log()->AddGlobalEntry(net::NetLogEventType::CANCELLED);
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr,
- kCaptureModeDefaultString));
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ custom_log_path, base::Value::Dict(), kCaptureModeDefaultString));
// Get file size after adding the event and make sure it's larger than before.
int64_t new_stop_file_size;
@@ -700,9 +700,8 @@ TEST_F(NetExportFileWriterTest, StopWithPolledData) {
// Create dummy polled data
const char kDummyPolledDataPath[] = "dummy_path";
const char kDummyPolledDataString[] = "dummy_info";
- std::unique_ptr<base::DictionaryValue> dummy_polled_data =
- std::make_unique<base::DictionaryValue>();
- dummy_polled_data->SetStringKey(kDummyPolledDataPath, kDummyPolledDataString);
+ base::Value::Dict dummy_polled_data;
+ dummy_polled_data.Set(kDummyPolledDataPath, kDummyPolledDataString);
ASSERT_TRUE(StartThenVerifyNewState(
base::FilePath(), net::NetLogCaptureMode::kDefault,
@@ -793,8 +792,8 @@ TEST_F(NetExportFileWriterTest, StartWithNetworkContextActive) {
base::FilePath(), net::NetLogCaptureMode::kDefault,
kCaptureModeDefaultString, network_context()));
- ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
- kCaptureModeDefaultString));
+ ASSERT_TRUE(StopThenVerifyNewStateAndFile(
+ base::FilePath(), base::Value::Dict(), kCaptureModeDefaultString));
// Read events from log file.
std::unique_ptr<base::DictionaryValue> root;
ASSERT_TRUE(ReadCompleteLogFile(default_log_path(), &root));
@@ -850,7 +849,7 @@ TEST_F(NetExportFileWriterTest, ReceiveStartWhileStoppingLog) {
kCaptureModeIncludeEverythingString, network_context()));
// Tell |file_writer_| to stop logging.
- file_writer()->StopNetLog(nullptr);
+ file_writer()->StopNetLog();
// Before running the main message loop, tell |file_writer_| to start
// logging. Not running the main message loop prevents the stopping process
diff --git a/chromium/components/neterror/resources/neterror.js b/chromium/components/neterror/resources/neterror.js
index 071cfc11f99..9d92f8e79f1 100644
--- a/chromium/components/neterror/resources/neterror.js
+++ b/chromium/components/neterror/resources/neterror.js
@@ -129,7 +129,9 @@ function detailsButtonClick() {
}
let primaryControlOnLeft = true;
-// <if expr="is_macosx or is_ios or is_linux or is_android">
+// clang-format off
+// <if expr="is_macosx or is_ios or is_linux or chromeos_ash or chromeos_lacros or is_android">
+// clang-format on
primaryControlOnLeft = false;
// </if>
diff --git a/chromium/components/neterror/resources/offline.js b/chromium/components/neterror/resources/offline.js
index bbe7ac7ec70..cf57c2276a5 100644
--- a/chromium/components/neterror/resources/offline.js
+++ b/chromium/components/neterror/resources/offline.js
@@ -4063,8 +4063,9 @@ Horizon.prototype = {
*/
addNewObstacle(currentSpeed) {
const obstacleCount =
- Runner.isAltGameModeEnabled() && !this.altGameModeActive ||
- this.altGameModeActive ?
+ Obstacle.types[Obstacle.types.length - 1].type != 'COLLECTABLE' ||
+ (Runner.isAltGameModeEnabled() && !this.altGameModeActive ||
+ this.altGameModeActive) ?
Obstacle.types.length - 1 :
Obstacle.types.length - 2;
const obstacleTypeIndex =
diff --git a/chromium/components/network_session_configurator/browser/network_session_configurator.cc b/chromium/components/network_session_configurator/browser/network_session_configurator.cc
index a4ae7d893bd..0e02e894eb0 100644
--- a/chromium/components/network_session_configurator/browser/network_session_configurator.cc
+++ b/chromium/components/network_session_configurator/browser/network_session_configurator.cc
@@ -28,6 +28,7 @@
#include "net/http/http_network_session.h"
#include "net/http/http_stream_factory.h"
#include "net/quic/quic_context.h"
+#include "net/quic/set_quic_flag.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/third_party/quiche/overrides/quiche_platform_impl/quic_flags_impl.h"
@@ -170,13 +171,13 @@ bool ShouldDisableQuic(base::StringPiece quic_trial_group,
if (is_quic_force_disabled)
return true;
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "enable_quic"), "false");
}
bool ShouldEnableQuicProxiesForHttpsUrls(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params,
"enable_quic_proxies_for_https_urls"),
"true");
@@ -184,7 +185,7 @@ bool ShouldEnableQuicProxiesForHttpsUrls(
bool ShouldRetryWithoutAltSvcOnQuicErrors(
const VariationParameters& quic_trial_params) {
- return !base::LowerCaseEqualsASCII(
+ return !base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params,
"retry_without_alt_svc_on_quic_errors"),
"false");
@@ -212,27 +213,27 @@ quic::QuicTagVector GetQuicClientConnectionOptions(
bool ShouldQuicCloseSessionsOnIpChange(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "close_sessions_on_ip_change"),
"true");
}
bool ShouldQuicGoAwaySessionsOnIpChange(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "goaway_sessions_on_ip_change"),
"true");
}
absl::optional<bool> GetExponentialBackOffOnInitialDelay(
const VariationParameters& quic_trial_params) {
- if (base::LowerCaseEqualsASCII(
+ if (base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params,
"exponential_backoff_on_initial_delay"),
"false")) {
return false;
}
- if (base::LowerCaseEqualsASCII(
+ if (base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params,
"exponential_backoff_on_initial_delay"),
"true")) {
@@ -289,13 +290,13 @@ int GetQuicMaxIdleTimeBeforeCryptoHandshakeSeconds(
bool ShouldQuicEstimateInitialRtt(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "estimate_initial_rtt"), "true");
}
bool ShouldQuicHeadersIncludeH2StreamDependencies(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params,
"headers_include_h2_stream_dependency"),
"true");
@@ -303,7 +304,7 @@ bool ShouldQuicHeadersIncludeH2StreamDependencies(
bool ShouldQuicMigrateSessionsOnNetworkChangeV2(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params,
"migrate_sessions_on_network_change_v2"),
"true");
@@ -311,20 +312,20 @@ bool ShouldQuicMigrateSessionsOnNetworkChangeV2(
bool ShouldQuicMigrateSessionsEarlyV2(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "migrate_sessions_early_v2"),
"true");
}
bool ShouldQuicAllowPortMigration(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "allow_port_migration"), "true");
+ return !base::EqualsCaseInsensitiveASCII(
+ GetVariationParam(quic_trial_params, "allow_port_migration"), "false");
}
bool ShouldQuicRetryOnAlternateNetworkBeforeHandshake(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params,
"retry_on_alternate_network_before_handshake"),
"true");
@@ -344,18 +345,18 @@ int GetQuicMaxTimeOnNonDefaultNetworkSeconds(
bool ShouldQuicMigrateIdleSessions(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "migrate_idle_sessions"), "true");
}
bool ShouldQuicDisableTlsZeroRtt(const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "disable_tls_zero_rtt"), "true");
}
bool ShouldQuicDisableGQuicZeroRtt(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "disable_gquic_zero_rtt"), "true");
}
@@ -443,6 +444,14 @@ int GetInitialDelayForBrokenAlternativeServiceSeconds(
return 0;
}
+bool NotDelayMainJobWithAvailableSpdySession(
+ const VariationParameters& quic_trial_params) {
+ return base::EqualsCaseInsensitiveASCII(
+ GetVariationParam(quic_trial_params,
+ "delay_main_job_with_available_spdy_session"),
+ "false");
+}
+
void SetQuicFlags(const VariationParameters& quic_trial_params) {
std::string flags_list =
GetVariationParam(quic_trial_params, "set_quic_flags");
@@ -452,7 +461,7 @@ void SetQuicFlags(const VariationParameters& quic_trial_params) {
flag, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (tokens.size() != 2)
continue;
- SetQuicFlagByName(tokens[0], tokens[1]);
+ net::SetQuicFlagByName(tokens[0], tokens[1]);
}
}
@@ -471,7 +480,7 @@ quic::ParsedQuicVersionVector GetQuicVersions(
GetVariationParam(quic_trial_params, "quic_version");
quic::ParsedQuicVersionVector trial_versions =
quic::ParseQuicVersionVectorString(trial_versions_str);
- const bool obsolete_versions_allowed = base::LowerCaseEqualsASCII(
+ const bool obsolete_versions_allowed = base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "obsolete_versions_allowed"),
"true");
if (!obsolete_versions_allowed) {
@@ -497,7 +506,7 @@ quic::ParsedQuicVersionVector GetQuicVersions(
bool ShouldEnableServerPushCancelation(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
+ return base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "enable_server_push_cancellation"),
"true");
}
@@ -509,7 +518,7 @@ bool AreQuicParamsValid(const base::CommandLine& command_line,
// Skip validation of params from the command line.
return true;
}
- if (!base::LowerCaseEqualsASCII(
+ if (!base::EqualsCaseInsensitiveASCII(
GetVariationParam(quic_trial_params, "enable_quic"), "true")) {
// Params that don't explicitly enable QUIC do not carry channel or epoch.
return true;
@@ -669,7 +678,9 @@ void ConfigureQuicParams(const base::CommandLine& command_line,
}
quic_params->exponential_backoff_on_initial_delay =
GetExponentialBackOffOnInitialDelay(quic_trial_params);
-
+ if (NotDelayMainJobWithAvailableSpdySession(quic_trial_params)) {
+ quic_params->delay_main_job_with_available_spdy_session = false;
+ }
SetQuicFlags(quic_trial_params);
}
diff --git a/chromium/components/network_session_configurator/browser/network_session_configurator_unittest.cc b/chromium/components/network_session_configurator/browser/network_session_configurator_unittest.cc
index 046f419d16b..e24bb0a5136 100644
--- a/chromium/components/network_session_configurator/browser/network_session_configurator_unittest.cc
+++ b/chromium/components/network_session_configurator/browser/network_session_configurator_unittest.cc
@@ -97,6 +97,7 @@ TEST_F(NetworkSessionConfiguratorTest, Defaults) {
EXPECT_TRUE(params_.quic_host_allowlist.empty());
EXPECT_TRUE(quic_params_.retransmittable_on_wire_timeout.is_zero());
EXPECT_FALSE(quic_params_.disable_tls_zero_rtt);
+ EXPECT_TRUE(quic_params_.allow_port_migration);
EXPECT_EQ(net::DefaultSupportedQuicVersions(),
quic_params_.supported_versions);
@@ -106,6 +107,7 @@ TEST_F(NetworkSessionConfiguratorTest, Defaults) {
EXPECT_FALSE(
quic_params_.initial_delay_for_broken_alternative_service.has_value());
EXPECT_FALSE(quic_params_.exponential_backoff_on_initial_delay.has_value());
+ EXPECT_TRUE(quic_params_.delay_main_job_with_available_spdy_session);
}
TEST_F(NetworkSessionConfiguratorTest, Http2FieldTrialGroupNameDoesNotMatter) {
@@ -151,7 +153,7 @@ TEST_F(NetworkSessionConfiguratorTest, EnableQuicFromParams) {
}
TEST_F(NetworkSessionConfiguratorTest, ValidQuicParams) {
- quic::ParsedQuicVersion version = net::DefaultSupportedQuicVersions().front();
+ quic::ParsedQuicVersion version = quic::AllSupportedVersions().front();
std::map<std::string, std::string> field_trial_params;
field_trial_params["enable_quic"] = "true";
field_trial_params["channel"] = "T";
@@ -171,7 +173,7 @@ TEST_F(NetworkSessionConfiguratorTest, ValidQuicParams) {
}
TEST_F(NetworkSessionConfiguratorTest, InvalidQuicParams) {
- quic::ParsedQuicVersion version = net::DefaultSupportedQuicVersions().front();
+ quic::ParsedQuicVersion version = quic::AllSupportedVersions().front();
std::map<std::string, std::string> field_trial_params;
field_trial_params["enable_quic"] = "true";
// These params are missing channel and epoch.
@@ -299,6 +301,29 @@ TEST_F(NetworkSessionConfiguratorTest,
EXPECT_FALSE(quic_params_.exponential_backoff_on_initial_delay.value());
}
+TEST_F(NetworkSessionConfiguratorTest, DelayMainJobWithAvailableSpdySession) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["delay_main_job_with_available_spdy_session"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(quic_params_.delay_main_job_with_available_spdy_session);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
+ NotDelayMainJobWithAvailableSpdySession) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["delay_main_job_with_available_spdy_session"] = "false";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_FALSE(quic_params_.delay_main_job_with_available_spdy_session);
+}
+
TEST_F(NetworkSessionConfiguratorTest,
QuicIdleConnectionTimeoutSecondsFieldTrialParams) {
std::map<std::string, std::string> field_trial_params;
@@ -501,15 +526,15 @@ TEST_F(
}
TEST_F(NetworkSessionConfiguratorTest,
- QuicAllowPortMigrationFromFieldTrialParams) {
+ DisableQuicAllowPortMigrationFromFieldTrialParams) {
std::map<std::string, std::string> field_trial_params;
- field_trial_params["allow_port_migration"] = "true";
+ field_trial_params["allow_port_migration"] = "false";
variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
ParseFieldTrials();
- EXPECT_TRUE(quic_params_.allow_port_migration);
+ EXPECT_FALSE(quic_params_.allow_port_migration);
}
TEST_F(NetworkSessionConfiguratorTest,
@@ -556,6 +581,7 @@ TEST_F(NetworkSessionConfiguratorTest, QuicVersionFromFieldTrialParams) {
std::map<std::string, std::string> field_trial_params;
field_trial_params["quic_version"] =
quic::QuicVersionToString(version.transport_version);
+ field_trial_params["obsolete_versions_allowed"] = "true";
variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
diff --git a/chromium/components/network_time/network_time_test_utils.cc b/chromium/components/network_time/network_time_test_utils.cc
index 547aea9f7e6..7ae8bc47b7a 100644
--- a/chromium/components/network_time/network_time_test_utils.cc
+++ b/chromium/components/network_time/network_time_test_utils.cc
@@ -16,33 +16,48 @@ namespace network_time {
// Update as follows:
//
-// curl -i http://clients2.google.com/time/1/current?cup2key=5:123123123
+// curl -i http://clients2.google.com/time/1/current?cup2key=6:123123123
//
-// where 5 is the key version and 123123123 is the nonce. Copy the response
+// where 6 is the key version and 123123123 is the nonce. Copy the response
// and the x-cup-server-proof header into |kGoodTimeResponseBody| and
// |kGoodTimeResponseServerProofHeader| respectively, and the
// 'current_time_millis' value of the response into
-// |kGoodTimeResponseHandlerJsTime|. Do this three times, so that the three
+// |kGoodTimeResponseHandlerJsTime|. Do this five times, so that the five
// requests appear in order below.
const char* kGoodTimeResponseBody[] = {
- ")]}'\n{\"current_time_millis\":1619464140565,"
- "\"server_nonce\":-1.656679479914492E230}",
- ")]}'\n{\"current_time_millis\":1619464273366,"
- "\"server_nonce\":2.1195306862817135E-5}",
- ")]}'\n{\"current_time_millis\":1642162812422,"
- "\"server_nonce\":3.374791108303444E207}"};
+ ")]}'\n{\"current_time_millis\":1652339069759,\"server_nonce\":7."
+ "29375327039265E-230}",
+ ")]}'\n{\"current_time_millis\":1652339136683,\"server_nonce\":1."
+ "4794255040588188E-23}",
+ ")]}'\n{\"current_time_millis\":1652339231311,\"server_nonce\":-4."
+ "419622990529329E127}",
+ ")]}'\n{\"current_time_millis\":1652339325263,\"server_nonce\":6."
+ "315542071193776E16}",
+ ")]}'\n{\"current_time_millis\":1652339380058,\"server_nonce\":-3."
+ "8130598030275436E-131}"};
+
const char* kGoodTimeResponseServerProofHeader[] = {
- "3045022100f829ced2af34ade53400f66eef6df9af732fa8bfe08517287c2805c92891e321"
- "022062fb405b2cf12bc3e2ac037985c4b8065a62e86e29a2e745ebff80fd52189c6a:"
+ "3046022100ab673cb907cd0c9139da0d50ada4c3326929d455e46f8f797f0a8c511ef"
+ "6881b02210091b0f77f463578b7c0be36d42f053de34e486eba8c0526f9f115f80c80"
+ "7a5ce4:"
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
- "3046022100c78436ad47904634aacd33f4c4bcb55bd6f7f2ed84a620fda0deaede99c32de6"
- "022100b595458bd03d83f33bfb891de1327b26620d576937f3713af59bb1f2c53f2e8b:"
+ "30440220139b1710412e68cf445d39234158943efee3e2b27859b97582b478af7dcf6"
+ "e85022004d9d7c432aae15a5207a18e25ae345675348767f784b7d3b07920b64a2ead"
+ "c3:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "3044022017d2ae7bf4507b18badd735629f1c44f1f024c88aeb271e4d52e6a849cb22"
+ "7a3022052c1223d65b4488ccb47f2c882f249c91541a55b99752f4f487a3e6abc5194"
+ "10:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "30450221009b8db5fe3000e6e0b696baf8d42d40d7b4ff9757c84b49cdd6d85fa39cd"
+ "0fca2022005144ed3eeb95707e3bc9e7369d8bd475b5d2f50ac98e5c56160bc9b1f1f"
+ "d36a:"
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
- "3045022100f50a8e6f97d8c362f878e2c988ab2c983e536de4dc2116a314699a4010d7d8b8"
- "02202ace752a45721399ec6dd704da55700c9b11626f7c55a72037db255b79992476:"
+ "3046022100ec690467b5eb550e6b91ec65810d942ed859d3dd6f966f72c9489679825"
+ "81cf8022100b2a54d11217ba6a75576e6db02f5293a70fd4bc27b02f0bda46e60f98a"
+ "b05785:"
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"};
-const double kGoodTimeResponseHandlerJsTime[] = {1619464140565, 1619464273366,
- 1642162812422};
+
+const double kGoodTimeResponseHandlerJsTime[] = {
+ 1652339069759, 1652339136683, 1652339231311, 1652339325263, 1652339380058};
std::unique_ptr<net::test_server::HttpResponse> GoodTimeResponseHandler(
const net::test_server::HttpRequest& request) {
@@ -62,7 +77,8 @@ FieldTrialTest::~FieldTrialTest() {}
void FieldTrialTest::SetFeatureParams(
bool enable,
float query_probability,
- NetworkTimeTracker::FetchBehavior fetch_behavior) {
+ NetworkTimeTracker::FetchBehavior fetch_behavior,
+ NetworkTimeTracker::ClockDriftSamples clock_drift_samples) {
scoped_feature_list_.Reset();
if (!enable) {
scoped_feature_list_.InitAndDisableFeature(kNetworkTimeServiceQuerying);
@@ -73,6 +89,7 @@ void FieldTrialTest::SetFeatureParams(
params["RandomQueryProbability"] = base::NumberToString(query_probability);
// See string format defined by `base::TimeDeltaFromString`.
params["CheckTimeInterval"] = "360s";
+ params["ClockDriftSampleDistance"] = "2s";
std::string fetch_behavior_param;
switch (fetch_behavior) {
case NetworkTimeTracker::FETCH_BEHAVIOR_UNKNOWN:
@@ -90,6 +107,24 @@ void FieldTrialTest::SetFeatureParams(
break;
}
params["FetchBehavior"] = fetch_behavior_param;
+
+ std::string num_clock_drift_samples;
+ switch (clock_drift_samples) {
+ case NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES:
+ num_clock_drift_samples = "0";
+ break;
+ case NetworkTimeTracker::ClockDriftSamples::TWO_SAMPLES:
+ num_clock_drift_samples = "2";
+ break;
+ case NetworkTimeTracker::ClockDriftSamples::FOUR_SAMPLES:
+ num_clock_drift_samples = "4";
+ break;
+ case NetworkTimeTracker::ClockDriftSamples::SIX_SAMPLES:
+ num_clock_drift_samples = "6";
+ break;
+ }
+ params["ClockDriftSamples"] = num_clock_drift_samples;
+
scoped_feature_list_.InitAndEnableFeatureWithParameters(
kNetworkTimeServiceQuerying, params);
}
diff --git a/chromium/components/network_time/network_time_test_utils.h b/chromium/components/network_time/network_time_test_utils.h
index 65efbfa2aaa..aa95bd8a6a6 100644
--- a/chromium/components/network_time/network_time_test_utils.h
+++ b/chromium/components/network_time/network_time_test_utils.h
@@ -26,16 +26,16 @@ namespace network_time {
// version and 123123123 as the nonce. Use
// NetworkTimeTracker::OverrideNonceForTesting() to set the nonce so
// that this response validates.
-extern const char* kGoodTimeResponseBody[3];
+extern const char* kGoodTimeResponseBody[5];
// The x-cup-server-proof header values that should be served along with
// |kGoodTimeResponseBody| to make a test server response be accepted by
// NetworkTimeTracker as a valid response.
-extern const char* kGoodTimeResponseServerProofHeader[3];
+extern const char* kGoodTimeResponseServerProofHeader[5];
// The times that |kGoodTimeResponseBody| uses. Can be converted to a
// base::Time with base::Time::FromJsTime.
-extern const double kGoodTimeResponseHandlerJsTime[3];
+extern const double kGoodTimeResponseHandlerJsTime[5];
// Returns a valid network time response using the constants above. See
// comments in the .cc for how to update the time returned in the response.
@@ -52,9 +52,11 @@ class FieldTrialTest {
virtual ~FieldTrialTest();
- void SetFeatureParams(bool enable,
- float query_probability,
- NetworkTimeTracker::FetchBehavior fetch_behavior);
+ void SetFeatureParams(
+ bool enable,
+ float query_probability,
+ NetworkTimeTracker::FetchBehavior fetch_behavior,
+ NetworkTimeTracker::ClockDriftSamples clock_drift_samples);
private:
base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/chromium/components/network_time/network_time_tracker.cc b/chromium/components/network_time/network_time_tracker.cc
index b53b5d1da50..0a3651c9ca5 100644
--- a/chromium/components/network_time/network_time_tracker.cc
+++ b/chromium/components/network_time/network_time_tracker.cc
@@ -137,16 +137,34 @@ const uint32_t kTimeServerMaxSkewSeconds = 10;
const char kTimeServiceURL[] = "http://clients2.google.com/time/1/current";
// This is an ECDSA prime256v1 named-curve key.
-const int kKeyVersion = 5;
+const int kKeyVersion = 6;
const uint8_t kKeyPubBytes[] = {
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03,
- 0x42, 0x00, 0x04, 0xE4, 0xA5, 0xA5, 0xA1, 0x99, 0x27, 0x83, 0x2B, 0x93,
- 0xF6, 0x30, 0xA6, 0x87, 0x78, 0x62, 0xB1, 0x81, 0x72, 0xD1, 0xA0, 0xB0,
- 0xFD, 0x48, 0x5F, 0x29, 0x60, 0x9C, 0x96, 0xC5, 0x10, 0xE3, 0x42, 0x43,
- 0x61, 0xB9, 0xDA, 0xEC, 0x30, 0xA8, 0x22, 0xA8, 0x69, 0xF7, 0x1F, 0x17,
- 0x5D, 0x83, 0xF7, 0xFD, 0xAE, 0x41, 0xDB, 0x31, 0x40, 0xAF, 0xA2, 0x32,
- 0xAE, 0x68, 0xFE, 0xD1, 0x6B, 0xB4, 0xB0};
+ 0x42, 0x00, 0x04, 0x59, 0x65, 0x1F, 0x1D, 0x36, 0x33, 0x81, 0xE1, 0xB2,
+ 0xD3, 0x78, 0x4B, 0xE1, 0x7B, 0x8D, 0x07, 0x33, 0x55, 0x4F, 0x0D, 0x67,
+ 0x1C, 0x33, 0xD2, 0xFE, 0x78, 0x02, 0xB6, 0xD2, 0xDF, 0x2F, 0x45, 0x1F,
+ 0x49, 0xBA, 0xD2, 0xE6, 0x67, 0x4E, 0x4D, 0xA9, 0x77, 0xB3, 0x34, 0x12,
+ 0xEB, 0x6D, 0xC0, 0xDC, 0x86, 0xE7, 0xBE, 0xF7, 0x09, 0x56, 0x77, 0x2A,
+ 0xF3, 0xE8, 0x4E, 0x96, 0xAB, 0xAB, 0x12};
+
+// Number of samples to be used for the computation of clock drift.
+constexpr base::FeatureParam<NetworkTimeTracker::ClockDriftSamples>::Option
+ kClockDriftSamplesOptions[] = {
+ {NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES, "0"},
+ {NetworkTimeTracker::ClockDriftSamples::TWO_SAMPLES, "2"},
+ {NetworkTimeTracker::ClockDriftSamples::FOUR_SAMPLES, "4"},
+ {NetworkTimeTracker::ClockDriftSamples::SIX_SAMPLES, "6"},
+};
+constexpr base::FeatureParam<NetworkTimeTracker::ClockDriftSamples>
+ kClockDriftSamples{&kNetworkTimeServiceQuerying, "ClockDriftSamples",
+ NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES,
+ &kClockDriftSamplesOptions};
+
+// Distance between the clock drift samples.
+constexpr base::FeatureParam<base::TimeDelta> kClockDriftSamplesDistance{
+ &kNetworkTimeServiceQuerying, "ClockDriftSamplesDistance",
+ base::Seconds(2)};
std::string GetServerProof(
scoped_refptr<net::HttpResponseHeaders> response_headers) {
@@ -316,10 +334,6 @@ void NetworkTimeTracker::OverrideNonceForTesting(uint32_t nonce) {
query_signer_->OverrideNonceForTesting(kKeyVersion, nonce);
}
-void NetworkTimeTracker::OverrideUMANoiseFactorForTesting(double noise_factor) {
- uma_noise_factor_ = noise_factor;
-}
-
base::TimeDelta NetworkTimeTracker::GetTimerDelayForTesting() const {
DCHECK(timer_.IsRunning());
return timer_.GetCurrentDelay();
@@ -434,7 +448,7 @@ void NetworkTimeTracker::CheckTime(CheckTimeType check_type) {
// timer to its default faster frequency.
QueueCheckTime(interval);
- if (!ShouldIssueTimeQuery()) {
+ if (!ShouldIssueTimeQuery(check_type)) {
return;
}
@@ -557,26 +571,47 @@ bool NetworkTimeTracker::UpdateTimeFromResponse(
}
last_fetched_time_ = current_time;
- if (check_type == CheckTimeType::BACKGROUND)
- RecordClockSkewHistograms(current_time, latency);
+ if (check_type == CheckTimeType::BACKGROUND) {
+ ProcessClockHistograms(current_time, latency);
+ }
UpdateNetworkTime(current_time, resolution, latency, tick_clock_->NowTicks());
return true;
}
-void NetworkTimeTracker::RecordClockSkewHistograms(
- base::Time current_time,
- base::TimeDelta fetch_latency) {
- // Compute the skew by comparing the reference clock to the system clock. Note
- // that the server processed our query roughly `fetch_latency/2` units of time
- // in the past. Adjust the `current_time` accordingly.
+void NetworkTimeTracker::ProcessClockHistograms(base::Time current_time,
+ base::TimeDelta latency) {
+ // Compute the skew by comparing the reference clock to the system clock.
+ // Note that the server processed our query roughly `fetch_latency/2` units
+ // of time in the past. Adjust the `current_time` accordingly.
base::TimeDelta system_clock_skew =
- clock_->Now() - (current_time + fetch_latency / 2);
-
- // Add noise for privacy reasons.
- system_clock_skew +=
- system_clock_skew * (2 * base::RandDouble() - 1) * uma_noise_factor_;
+ clock_->Now() - (current_time + latency / 2);
+ if (clock_drift_measurement_triggered_) {
+ clock_drift_skews_.push_back(system_clock_skew);
+ clock_drift_latencies_.push_back(latency);
+
+ // We need one more sample than the number used for the computation
+ // because the middle sample is not used by the central finite difference
+ // formulas.
+ if (clock_drift_skews_.size() ==
+ static_cast<uint8_t>(kClockDriftSamples.Get()) + 1) {
+ RecordClockDriftHistograms();
+ clock_drift_measurement_triggered_ = false;
+
+ // Go to sleep for a long time after we recorded the histogram.
+ QueueCheckTime(kBackoffInterval.Get());
+ }
+ } else {
+ // We always trigger the clock drift measurements right after clock skew
+ // is measured.
+ RecordClockSkewHistograms(system_clock_skew, latency);
+ MaybeTriggerClockDriftMeasurements();
+ }
+}
+void NetworkTimeTracker::RecordClockSkewHistograms(
+ base::TimeDelta system_clock_skew,
+ base::TimeDelta fetch_latency) {
// Explicitly record clock skew of zero in the "positive" histograms.
if (system_clock_skew >= base::TimeDelta()) {
UmaHistogramCustomTimesClockSkew(
@@ -598,6 +633,96 @@ void NetworkTimeTracker::RecordClockSkewHistograms(
}
}
+void NetworkTimeTracker::MaybeTriggerClockDriftMeasurements() {
+ if (clock_drift_measurement_triggered_ ||
+ static_cast<uint8_t>(kClockDriftSamples.Get()) == 0) {
+ return;
+ }
+ clock_drift_latencies_.clear();
+ clock_drift_skews_.clear();
+ clock_drift_measurement_triggered_ = true;
+ QueueCheckTime(kClockDriftSamplesDistance.Get());
+}
+
+// The clock drift is the time derivative of clock skew. We use the central
+// finite difference method to compute the derivative using the equally
+// distanced samples we have collected. The coefficients for computing the
+// derivative depending on the number of samples we use can be found at
+// https://en.wikipedia.org/wiki/Finite_difference_coefficient . Because we use
+// *central* finite differences, the middle sample does not take part in the
+// computation.
+double NetworkTimeTracker::ComputeClockDrift() {
+ if (kClockDriftSamplesDistance.Get() <= base::TimeDelta(base::Seconds(0)))
+ return std::numeric_limits<double>::infinity();
+
+ switch (kClockDriftSamples.Get()) {
+ case NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES:
+ NOTREACHED();
+ return std::numeric_limits<double>::infinity();
+ case NetworkTimeTracker::ClockDriftSamples::TWO_SAMPLES:
+ return (clock_drift_skews_[2] - clock_drift_skews_[0]) /
+ kClockDriftSamplesDistance.Get();
+ case NetworkTimeTracker::ClockDriftSamples::FOUR_SAMPLES:
+ return (-clock_drift_skews_[0] + 8 * clock_drift_skews_[1] -
+ 8 * clock_drift_skews_[3] + clock_drift_skews_[4]) /
+ (12 * kClockDriftSamplesDistance.Get());
+ case NetworkTimeTracker::ClockDriftSamples::SIX_SAMPLES:
+ return (-clock_drift_skews_[0] + 9 * clock_drift_skews_[1] -
+ 45 * clock_drift_skews_[2] + 45 * clock_drift_skews_[4] -
+ 9 * clock_drift_skews_[5] + clock_drift_skews_[6]) /
+ (60 * kClockDriftSamplesDistance.Get());
+ }
+}
+
+void NetworkTimeTracker::RecordClockDriftHistograms() {
+ DCHECK_EQ(clock_drift_skews_.size(), clock_drift_latencies_.size());
+ if (clock_drift_latencies_.size() !=
+ static_cast<uint8_t>(kClockDriftSamples.Get()) + 1 ||
+ clock_drift_skews_.size() !=
+ static_cast<uint8_t>(kClockDriftSamples.Get()) + 1) {
+ return;
+ }
+
+ double clock_drift_microseconds = ComputeClockDrift() * 1e+6;
+ if (std::isfinite(clock_drift_microseconds)) {
+ if (clock_drift_microseconds >= 0) {
+ base::UmaHistogramCounts100000(
+ "PrivacyBudget.ClockDrift.Magnitude.Positive",
+ base::ClampRound(clock_drift_microseconds));
+ } else {
+ base::UmaHistogramCounts100000(
+ "PrivacyBudget.ClockDrift.Magnitude.Negative",
+ base::ClampRound(-clock_drift_microseconds));
+ }
+ }
+
+ base::UmaHistogramCounts100000(
+ "PrivacyBudget.ClockDrift.FetchLatencyVariance",
+ base::ClampRound(ComputeClockDriftLatencyVariance()));
+}
+
+double NetworkTimeTracker::ComputeClockDriftLatencyVariance() {
+ base::TimeDelta mean = base::Seconds(0);
+ for (size_t i = 0; i < clock_drift_latencies_.size(); i++) {
+ // Exclude middle sample since we do not use it
+ if (i != clock_drift_latencies_.size() / 2)
+ mean += clock_drift_latencies_[i];
+ }
+ mean /= static_cast<uint8_t>(kClockDriftSamples.Get());
+
+ double variance = 0;
+ for (size_t i = 0; i < clock_drift_latencies_.size(); i++) {
+ base::TimeDelta diff_from_mean = mean - clock_drift_latencies_[i];
+ // Exclude middle sample since we do not use it
+ if (i != clock_drift_latencies_.size() / 2) {
+ variance +=
+ diff_from_mean.InMilliseconds() * diff_from_mean.InMilliseconds();
+ }
+ }
+
+ return variance;
+}
+
void NetworkTimeTracker::OnURLLoaderComplete(
CheckTimeType check_type,
std::unique_ptr<std::string> response_body) {
@@ -606,16 +731,22 @@ void NetworkTimeTracker::OnURLLoaderComplete(
time_query_completed_ = true;
- // After completion of a query, whether succeeded or failed, go to sleep for a
- // long time.
+ // After completion of a query, whether succeeded or failed, go to sleep
+ // for a long time.
if (!UpdateTimeFromResponse(
check_type,
std::move(response_body))) { // On error, back off.
+ clock_drift_measurement_triggered_ = false;
if (backoff_ < base::Days(2)) {
backoff_ *= 2;
}
} else {
backoff_ = kBackoffInterval.Get();
+
+ // If the clock skew measurements were triggered restrict the backoff to
+ // their distance.
+ if (clock_drift_measurement_triggered_)
+ backoff_ = kClockDriftSamplesDistance.Get();
}
QueueCheckTime(backoff_);
time_fetcher_.reset();
@@ -644,7 +775,7 @@ void NetworkTimeTracker::QueueCheckTime(base::TimeDelta delay) {
}
}
-bool NetworkTimeTracker::ShouldIssueTimeQuery() {
+bool NetworkTimeTracker::ShouldIssueTimeQuery(CheckTimeType check_type) {
// Do not query the time service if the feature is not enabled.
if (!AreTimeFetchesEnabled()) {
return false;
@@ -662,6 +793,12 @@ bool NetworkTimeTracker::ShouldIssueTimeQuery() {
return true;
}
+ // If we are in the process of measuring drift, we should query.
+ if (clock_drift_measurement_triggered_ &&
+ check_type == CheckTimeType::BACKGROUND) {
+ return true;
+ }
+
// Otherwise, make the decision at random.
double probability = kRandomQueryProbability.Get();
if (probability < 0.0 || probability > 1.0) {
diff --git a/chromium/components/network_time/network_time_tracker.h b/chromium/components/network_time/network_time_tracker.h
index 71ad26f045a..01c4a04ef4d 100644
--- a/chromium/components/network_time/network_time_tracker.h
+++ b/chromium/components/network_time/network_time_tracker.h
@@ -91,6 +91,14 @@ class NetworkTimeTracker {
FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
};
+ // Number of samples to be used for the computation of clock drift.
+ enum class ClockDriftSamples : uint8_t {
+ NO_SAMPLES = 0,
+ TWO_SAMPLES = 2,
+ FOUR_SAMPLES = 4,
+ SIX_SAMPLES = 6,
+ };
+
static void RegisterPrefs(PrefRegistrySimple* registry);
// Constructor. Arguments may be stubbed out for tests. |url_loader_factory|
@@ -163,8 +171,6 @@ class NetworkTimeTracker {
void OverrideNonceForTesting(uint32_t nonce);
- void OverrideUMANoiseFactorForTesting(double noise_factor);
-
base::TimeDelta GetTimerDelayForTesting() const;
private:
@@ -183,11 +189,20 @@ class NetworkTimeTracker {
bool UpdateTimeFromResponse(CheckTimeType check_type,
std::unique_ptr<std::string> response_body);
+ // Processes the clock skew and clock drift histograms.
+ void ProcessClockHistograms(base::Time current_time, base::TimeDelta latency);
+
// Records histograms related to clock skew. All of these histograms are
// currently local-only. See https://crbug.com/1258624.
- void RecordClockSkewHistograms(base::Time current_time,
+ void RecordClockSkewHistograms(base::TimeDelta system_clock_skew,
base::TimeDelta fetch_latency);
+ // Triggers clock drift measurements if not already triggered and if enabled.
+ void MaybeTriggerClockDriftMeasurements();
+
+ // Records histograms related to clock drift.
+ void RecordClockDriftHistograms();
+
// Called to process responses from the secure time service.
void OnURLLoaderComplete(CheckTimeType check_type,
std::unique_ptr<std::string> response_body);
@@ -198,7 +213,16 @@ class NetworkTimeTracker {
// Returns true if there's sufficient reason to suspect that
// NetworkTimeTracker does not know what time it is. This returns true
// unconditionally every once in a long while, just to be on the safe side.
- bool ShouldIssueTimeQuery();
+ bool ShouldIssueTimeQuery(CheckTimeType check_type);
+
+ // Computes clock drift value in seconds/second based on collected
+ // samples. This return value tells how many seconds the client's clock
+ // is drifting away from the roughtime clock in one second.
+ double ComputeClockDrift();
+
+ // Computes the variance of the latencies corresponding to the samples used
+ // for computing clock drift.
+ double ComputeClockDriftLatencyVariance();
// State variables for internally-managed secure time service queries.
GURL server_url_;
@@ -253,10 +277,12 @@ class NetworkTimeTracker {
// latencies.
HistoricalLatenciesContainer historical_latencies_;
- // Clock skews reported to UMA will have ±`uma_noise_factor_` noise (relative
- // to the clock skew itself) for privacy reasons. For example, specifying 0.1
- // here means ±10% noise.
- double uma_noise_factor_ = 0.1;
+ // Flag keeping track of whether clock drift measurements were triggered.
+ bool clock_drift_measurement_triggered_ = false;
+
+ // Containers for recording clock drift metrics.
+ std::vector<base::TimeDelta> clock_drift_latencies_;
+ std::vector<base::TimeDelta> clock_drift_skews_;
base::ThreadChecker thread_checker_;
};
diff --git a/chromium/components/network_time/network_time_tracker_unittest.cc b/chromium/components/network_time/network_time_tracker_unittest.cc
index 585891180fd..5435c882522 100644
--- a/chromium/components/network_time/network_time_tracker_unittest.cc
+++ b/chromium/components/network_time/network_time_tracker_unittest.cc
@@ -59,7 +59,8 @@ class NetworkTimeTrackerTest : public ::testing::Test {
field_trial_test_->SetFeatureParams(
true, 0.0 /* query probability */,
- NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+ NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
shared_url_loader_factory_ =
base::MakeRefCounted<network::TestSharedURLLoaderFactory>();
@@ -560,7 +561,8 @@ TEST_F(NetworkTimeTrackerTest, StartTimeFetchWhileSynced) {
// is not configured to allow on-demand time fetches.
TEST_F(NetworkTimeTrackerTest, StartTimeFetchWithoutVariationsParam) {
field_trial_test_->SetFeatureParams(
- true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_ONLY);
+ true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_ONLY,
+ NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
test_server_->RegisterRequestHandler(
base::BindRepeating(&GoodTimeResponseHandler));
EXPECT_TRUE(test_server_->Start());
@@ -581,7 +583,8 @@ TEST_F(NetworkTimeTrackerTest, NoNetworkQueryWhileSynced) {
tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
field_trial_test_->SetFeatureParams(
- true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+ true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
base::Time in_network_time = clock_->Now();
UpdateNetworkTime(in_network_time, resolution_, latency_,
tick_clock_->NowTicks());
@@ -592,7 +595,8 @@ TEST_F(NetworkTimeTrackerTest, NoNetworkQueryWhileSynced) {
EXPECT_EQ(base::Minutes(6), tracker_->GetTimerDelayForTesting());
field_trial_test_->SetFeatureParams(
- true, 1.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+ true, 1.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
tracker_->WaitForFetchForTesting(123123123);
EXPECT_EQ(base::Minutes(60), tracker_->GetTimerDelayForTesting());
@@ -601,14 +605,16 @@ TEST_F(NetworkTimeTrackerTest, NoNetworkQueryWhileSynced) {
TEST_F(NetworkTimeTrackerTest, NoNetworkQueryWhileFeatureDisabled) {
// Disable network time queries and check that a query is not sent.
field_trial_test_->SetFeatureParams(
- false, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+ false, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
EXPECT_FALSE(tracker_->QueryTimeServiceForTesting());
// The timer is not started when the feature is disabled.
EXPECT_EQ(base::Minutes(0), tracker_->GetTimerDelayForTesting());
// Enable time queries and check that a query is sent.
field_trial_test_->SetFeatureParams(
- true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+ true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
tracker_->WaitForFetchForTesting(123123123);
}
@@ -912,6 +918,11 @@ TEST_F(NetworkTimeTrackerTest, TimeBetweenFetchesHistogram) {
}
TEST_F(NetworkTimeTrackerTest, ClockSkewHistograms) {
+ field_trial_test_->SetFeatureParams(
+ true, 1.0 /* query probability */,
+ NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
+
MultipleGoodTimeResponseHandler response_handler;
base::HistogramTester histograms;
@@ -921,9 +932,6 @@ TEST_F(NetworkTimeTrackerTest, ClockSkewHistograms) {
EXPECT_TRUE(test_server_->Start());
tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
- // Disable noise for deterministic tests.
- tracker_->OverrideUMANoiseFactorForTesting(0.0);
-
clock_->SetNow(
base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0] + 3500));
EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
@@ -970,51 +978,165 @@ TEST_F(NetworkTimeTrackerTest, ClockSkewHistograms) {
"PrivacyBudget.ClockSkew.FetchLatencyJitter", base::Seconds(0), 1);
}
-TEST_F(NetworkTimeTrackerTest, ClockSkewHistogramsNoise) {
+TEST_F(NetworkTimeTrackerTest, ClockSkewHistogramsEmptyForOnDemandChecks) {
+ field_trial_test_->SetFeatureParams(
+ true, 1.0 /* query probability */,
+ NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
+ MultipleGoodTimeResponseHandler response_handler;
+ base::HistogramTester histograms;
+
+ test_server_->RegisterRequestHandler(
+ base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
+ base::Unretained(&response_handler)));
+ EXPECT_TRUE(test_server_->Start());
+ tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/true));
+ tracker_->WaitForFetchForTesting(123123123);
+
+ histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.Magnitude.Positive", 0);
+ histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.Magnitude.Negative", 0);
+ histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.FetchLatency", 0);
+ histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.FetchLatencyJitter", 0);
+}
+
+TEST_F(NetworkTimeTrackerTest, ClockDriftHistogramsEmptyForOnDemandChecks) {
+ MultipleGoodTimeResponseHandler response_handler;
+ base::HistogramTester histograms;
+
+ field_trial_test_->SetFeatureParams(
+ true, 1.0 /* query probability */,
+ NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::TWO_SAMPLES);
+
+ test_server_->RegisterRequestHandler(
+ base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
+ base::Unretained(&response_handler)));
+ EXPECT_TRUE(test_server_->Start());
+ tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/true));
+ tracker_->WaitForFetchForTesting(123123123);
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[1]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/true));
+ tracker_->WaitForFetchForTesting(123123123);
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[2]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/true));
+ tracker_->WaitForFetchForTesting(123123123);
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[3]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/true));
+ tracker_->WaitForFetchForTesting(123123123);
+
+ histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Positive", 0);
+ histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Negative", 0);
+ histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.FetchLatencyVariance",
+ 0);
+}
+
+TEST_F(NetworkTimeTrackerTest, ClockDriftHistogramsPositive) {
MultipleGoodTimeResponseHandler response_handler;
base::HistogramTester histograms;
+ field_trial_test_->SetFeatureParams(
+ true, 1.0 /* query probability */,
+ NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::TWO_SAMPLES);
+
test_server_->RegisterRequestHandler(
base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
base::Unretained(&response_handler)));
EXPECT_TRUE(test_server_->Start());
tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+ // This part will trigger a skew measurement fetch first, followed by a drift
+ // measurement using two samples.
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
+ tracker_->WaitForFetchForTesting(123123123);
+
+ // The next measurements are used for computing drift.
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[1]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
+ // Simulate 70ms latency.
+ tick_clock_->Advance(base::Milliseconds(70));
+ tracker_->WaitForFetchForTesting(123123123);
+
+ // We add an on demand time query in the middle to check it does not interfere
+ // with our samples.
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[2]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/true));
+ tracker_->WaitForFetchForTesting(123123123);
+
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[3]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
+ tracker_->WaitForFetchForTesting(123123123);
+
clock_->SetNow(
- base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0] + 3500));
+ base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[4] + 150));
EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
- // Simulate 1s latency.
- tick_clock_->Advance(base::Seconds(1));
+ // Simulate 50ms latency.
+ tick_clock_->Advance(base::Milliseconds(50));
tracker_->WaitForFetchForTesting(123123123);
- std::vector<base::Bucket> buckets =
- histograms.GetAllSamples("PrivacyBudget.ClockSkew.Magnitude.Positive");
+ histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Positive", 1);
+ histograms.ExpectUniqueSample(
+ "PrivacyBudget.ClockDrift.Magnitude.Positive",
+ base::Milliseconds(150 - 50 / 2 + 70 / 2).InMicroseconds() / 2.0, 1);
+
+ histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Negative", 0);
- // The real clock skew is 3000ms. With 10% noise, this means that there should
- // be exactly one sample between 2700 and 3300.
- const int64_t sum =
- histograms.GetTotalSum("PrivacyBudget.ClockSkew.Magnitude.Positive");
- EXPECT_LE(2700, sum);
- EXPECT_LE(sum, 3300);
+ histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.FetchLatencyVariance",
+ 1);
+ histograms.ExpectUniqueSample("PrivacyBudget.ClockDrift.FetchLatencyVariance",
+ 200, 1);
}
-TEST_F(NetworkTimeTrackerTest, ClockSkewHistogramsEmptyForOnDemandChecks) {
+TEST_F(NetworkTimeTrackerTest, ClockDriftHistogramsNegative) {
MultipleGoodTimeResponseHandler response_handler;
base::HistogramTester histograms;
+ field_trial_test_->SetFeatureParams(
+ true, 1.0 /* query probability */,
+ NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
+ NetworkTimeTracker::ClockDriftSamples::TWO_SAMPLES);
+
test_server_->RegisterRequestHandler(
base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
base::Unretained(&response_handler)));
EXPECT_TRUE(test_server_->Start());
tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
- EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/true));
+ // This part will trigger a skew measurement fetch first, followed by a drift
+ // measurement using two samples.
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
tracker_->WaitForFetchForTesting(123123123);
- histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.Magnitude.Positive", 0);
- histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.Magnitude.Negative", 0);
- histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.FetchLatency", 0);
- histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.FetchLatencyJitter", 0);
+ // These are the two measurements used for computing drift.
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[1]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
+ tracker_->WaitForFetchForTesting(123123123);
+
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[2]));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
+ tracker_->WaitForFetchForTesting(123123123);
+
+ clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[3] - 1));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
+ tracker_->WaitForFetchForTesting(123123123);
+
+ histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Positive", 0);
+
+ histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Negative", 1);
+ histograms.ExpectUniqueSample("PrivacyBudget.ClockDrift.Magnitude.Negative",
+ base::Milliseconds(1).InMicroseconds() / 2.0,
+ 1);
+
+ histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.FetchLatencyVariance",
+ 1);
+ histograms.ExpectUniqueSample("PrivacyBudget.ClockDrift.FetchLatencyVariance",
+ 0, 1);
}
} // namespace network_time
diff --git a/chromium/components/new_or_sad_tab_strings.grdp b/chromium/components/new_or_sad_tab_strings.grdp
index cfc50bfcc54..04e7d95b2e0 100644
--- a/chromium/components/new_or_sad_tab_strings.grdp
+++ b/chromium/components/new_or_sad_tab_strings.grdp
@@ -66,7 +66,7 @@
Close other tabs or apps
</message>
</if>
- <if expr="is_linux and not chromeos_ash and not chromeos_lacros">
+ <if expr="is_linux">
<message name="IDS_SAD_TAB_RELOAD_CLOSE_TABS" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to close other Chrome tabs or programs running on their computer.">
Close other tabs or programs
</message>
@@ -76,7 +76,7 @@
Close other apps
</message>
</if>
- <if expr="is_linux and not chromeos_ash and not chromeos_lacros">
+ <if expr="is_linux">
<message name="IDS_SAD_TAB_RELOAD_CLOSE_NOTABS" desc="One of the bullet points displayed on the web page if a reload failed to fix the issue, advising the user to close other programs running on their computer (Linux).">
Close other programs
</message>
diff --git a/chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.cc b/chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.cc
index 90be173822d..666eca66faf 100644
--- a/chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.cc
+++ b/chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.cc
@@ -210,7 +210,7 @@ void NoStatePrefetchContents::StartPrerendering(
// TODO(davidben): This logic assumes each prerender has at most one
// process. https://crbug.com/440544
no_state_prefetch_manager()->AddPrerenderProcessHost(
- GetMainFrame()->GetProcess());
+ GetPrimaryMainFrame()->GetProcess());
NotifyPrefetchStart();
@@ -385,7 +385,7 @@ void NoStatePrefetchContents::DidRedirectNavigation(
void NoStatePrefetchContents::DidFinishLoad(
content::RenderFrameHost* render_frame_host,
const GURL& validated_url) {
- if (!render_frame_host->GetParent())
+ if (render_frame_host->IsInPrimaryMainFrame())
has_finished_loading_ = true;
}
@@ -437,7 +437,7 @@ void NoStatePrefetchContents::Destroy(FinalStatus final_status) {
void NoStatePrefetchContents::DestroyWhenUsingTooManyResources() {
if (process_pid_ == base::kNullProcessId) {
- RenderFrameHost* rfh = GetMainFrame();
+ RenderFrameHost* rfh = GetPrimaryMainFrame();
if (!rfh)
return;
@@ -484,9 +484,9 @@ void NoStatePrefetchContents::DidGetMemoryUsage(
}
}
-RenderFrameHost* NoStatePrefetchContents::GetMainFrame() {
+RenderFrameHost* NoStatePrefetchContents::GetPrimaryMainFrame() {
return no_state_prefetch_contents_
- ? no_state_prefetch_contents_->GetMainFrame()
+ ? no_state_prefetch_contents_->GetPrimaryMainFrame()
: nullptr;
}
diff --git a/chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.h b/chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.h
index 6c92ac0ddd2..eabd5e4aa29 100644
--- a/chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.h
+++ b/chromium/components/no_state_prefetch/browser/no_state_prefetch_contents.h
@@ -124,7 +124,7 @@ class NoStatePrefetchContents : public content::WebContentsObserver,
// it if not.
void DestroyWhenUsingTooManyResources();
- content::RenderFrameHost* GetMainFrame();
+ content::RenderFrameHost* GetPrimaryMainFrame();
NoStatePrefetchManager* no_state_prefetch_manager() {
return no_state_prefetch_manager_;
diff --git a/chromium/components/no_state_prefetch/browser/no_state_prefetch_link_manager.h b/chromium/components/no_state_prefetch/browser/no_state_prefetch_link_manager.h
index d656aeb9a25..37d2720f321 100644
--- a/chromium/components/no_state_prefetch/browser/no_state_prefetch_link_manager.h
+++ b/chromium/components/no_state_prefetch/browser/no_state_prefetch_link_manager.h
@@ -63,7 +63,7 @@ class NoStatePrefetchLinkManager : public KeyedService,
private:
friend class PrerenderBrowserTest;
- friend class PrerenderTest;
+ friend class NoStatePrefetchTest;
// WebViewTest.NoPrerenderer needs to access the private IsEmpty() method.
FRIEND_TEST_ALL_PREFIXES(::WebViewTest, NoPrerenderer);
diff --git a/chromium/components/no_state_prefetch/browser/no_state_prefetch_manager.cc b/chromium/components/no_state_prefetch/browser/no_state_prefetch_manager.cc
index 7233ea08329..f292dcfca4d 100644
--- a/chromium/components/no_state_prefetch/browser/no_state_prefetch_manager.cc
+++ b/chromium/components/no_state_prefetch/browser/no_state_prefetch_manager.cc
@@ -374,8 +374,8 @@ std::unique_ptr<base::DictionaryValue> NoStatePrefetchManager::CopyAsValue()
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto dict_value = std::make_unique<base::DictionaryValue>();
- dict_value->SetKey("history", base::Value::FromUniquePtrValue(
- prerender_history_->CopyEntriesAsValue()));
+ dict_value->GetDict().Set("history",
+ prerender_history_->CopyEntriesAsValue());
dict_value->SetKey(
"active", base::Value::FromUniquePtrValue(GetActivePrerendersAsValue()));
dict_value->SetBoolKey("enabled",
@@ -931,7 +931,8 @@ NoStatePrefetchManager::GetActivePrerendersAsValue() const {
for (const auto& prefetch : active_prefetches_) {
auto prefetch_value = prefetch->contents()->GetAsValue();
if (prefetch_value)
- list_value->Append(std::move(prefetch_value));
+ list_value->GetList().Append(
+ base::Value::FromUniquePtrValue(std::move(prefetch_value)));
}
return list_value;
}
diff --git a/chromium/components/no_state_prefetch/browser/prerender_history.cc b/chromium/components/no_state_prefetch/browser/prerender_history.cc
index 95d2eb52d1b..eae9e696dca 100644
--- a/chromium/components/no_state_prefetch/browser/prerender_history.cc
+++ b/chromium/components/no_state_prefetch/browser/prerender_history.cc
@@ -33,24 +33,23 @@ void PrerenderHistory::Clear() {
entries_.clear();
}
-std::unique_ptr<base::Value> PrerenderHistory::CopyEntriesAsValue() const {
- auto return_list = std::make_unique<base::ListValue>();
+base::Value::List PrerenderHistory::CopyEntriesAsValue() const {
+ base::Value::List return_list;
// Javascript needs times in terms of milliseconds since Jan 1, 1970.
base::Time epoch_start = base::Time::UnixEpoch();
for (const Entry& entry : base::Reversed(entries_)) {
- auto entry_dict = std::make_unique<base::DictionaryValue>();
- entry_dict->SetString("url", entry.url.spec());
- entry_dict->SetString("final_status",
- NameFromFinalStatus(entry.final_status));
- entry_dict->SetString("origin", NameFromOrigin(entry.origin));
+ base::Value::Dict entry_dict;
+ entry_dict.Set("url", entry.url.spec());
+ entry_dict.Set("final_status", NameFromFinalStatus(entry.final_status));
+ entry_dict.Set("origin", NameFromOrigin(entry.origin));
// Use a string to prevent overflow, as Values don't support 64-bit
// integers.
- entry_dict->SetString(
+ entry_dict.Set(
"end_time",
base::NumberToString((entry.end_time - epoch_start).InMilliseconds()));
- return_list->Append(std::move(entry_dict));
+ return_list.Append(std::move(entry_dict));
}
- return std::move(return_list);
+ return return_list;
}
} // namespace prerender
diff --git a/chromium/components/no_state_prefetch/browser/prerender_history.h b/chromium/components/no_state_prefetch/browser/prerender_history.h
index 5bd6889bf79..ed0f81f3da9 100644
--- a/chromium/components/no_state_prefetch/browser/prerender_history.h
+++ b/chromium/components/no_state_prefetch/browser/prerender_history.h
@@ -11,14 +11,11 @@
#include "base/sequence_checker.h"
#include "base/time/time.h"
+#include "base/values.h"
#include "components/no_state_prefetch/common/no_state_prefetch_final_status.h"
#include "components/no_state_prefetch/common/prerender_origin.h"
#include "url/gurl.h"
-namespace base {
-class Value;
-}
-
namespace prerender {
// PrerenderHistory maintains a per-session history of prerendered pages
@@ -70,8 +67,8 @@ class PrerenderHistory {
// Deletes all history entries.
void Clear();
- // Retrieves the entries as a value which can be displayed.
- std::unique_ptr<base::Value> CopyEntriesAsValue() const;
+ // Retrieves the entries as a list of values which can be displayed.
+ base::Value::List CopyEntriesAsValue() const;
private:
std::list<Entry> entries_;
diff --git a/chromium/components/no_state_prefetch/browser/prerender_history_unittest.cc b/chromium/components/no_state_prefetch/browser/prerender_history_unittest.cc
index 56654091fd6..8e4110ec6f5 100644
--- a/chromium/components/no_state_prefetch/browser/prerender_history_unittest.cc
+++ b/chromium/components/no_state_prefetch/browser/prerender_history_unittest.cc
@@ -15,7 +15,7 @@ namespace prerender {
namespace {
-bool ListEntryMatches(base::Value::ListView list,
+bool ListEntryMatches(const base::Value::List& list,
size_t index,
const char* expected_url,
FinalStatus expected_final_status,
@@ -23,27 +23,27 @@ bool ListEntryMatches(base::Value::ListView list,
const std::string& expected_end_time) {
if (index >= list.size())
return false;
- base::Value& dict = list[index];
+ const base::Value& dict = list[index];
if (!dict.is_dict())
return false;
if (dict.DictSize() != 4u)
return false;
- std::string* url = dict.FindStringPath("url");
+ const std::string* url = dict.FindStringPath("url");
if (!url)
return false;
if (*url != expected_url)
return false;
- std::string* final_status = dict.FindStringPath("final_status");
+ const std::string* final_status = dict.FindStringPath("final_status");
if (!final_status)
return false;
if (*final_status != NameFromFinalStatus(expected_final_status))
return false;
- std::string* origin = dict.FindStringPath("origin");
+ const std::string* origin = dict.FindStringPath("origin");
if (!origin)
return false;
if (*origin != NameFromOrigin(expected_origin))
return false;
- std::string* end_time = dict.FindStringPath("end_time");
+ const std::string* end_time = dict.FindStringPath("end_time");
if (!end_time)
return false;
if (*end_time != expected_end_time)
@@ -52,16 +52,12 @@ bool ListEntryMatches(base::Value::ListView list,
}
TEST(PrerenderHistoryTest, GetAsValue) {
- std::unique_ptr<base::Value> entry_value;
-
// Create a history with only 2 values.
PrerenderHistory history(2);
// Make sure an empty list exists when retrieving as value.
- entry_value = history.CopyEntriesAsValue();
- ASSERT_TRUE(entry_value.get() != nullptr);
- ASSERT_TRUE(entry_value->is_list());
- EXPECT_TRUE(entry_value->GetListDeprecated().empty());
+ base::Value::List entry_value = history.CopyEntriesAsValue();
+ EXPECT_TRUE(entry_value.empty());
// Base time used for all events. Each event is given a time 1 millisecond
// after that of the previous one.
@@ -75,11 +71,9 @@ TEST(PrerenderHistoryTest, GetAsValue) {
kFirstOrigin, epoch_start);
history.AddEntry(entry_first);
entry_value = history.CopyEntriesAsValue();
- ASSERT_TRUE(entry_value.get() != nullptr);
- ASSERT_TRUE(entry_value->is_list());
- EXPECT_EQ(1u, entry_value->GetListDeprecated().size());
- EXPECT_TRUE(ListEntryMatches(entry_value->GetListDeprecated(), 0u, kFirstUrl,
- kFirstFinalStatus, kFirstOrigin, "0"));
+ EXPECT_EQ(1u, entry_value.size());
+ EXPECT_TRUE(ListEntryMatches(entry_value, 0u, kFirstUrl, kFirstFinalStatus,
+ kFirstOrigin, "0"));
// Add a second entry and make sure both first and second appear.
const char* const kSecondUrl = "http://www.beta.com/";
@@ -90,13 +84,11 @@ TEST(PrerenderHistoryTest, GetAsValue) {
epoch_start + base::Milliseconds(1));
history.AddEntry(entry_second);
entry_value = history.CopyEntriesAsValue();
- ASSERT_TRUE(entry_value.get() != nullptr);
- ASSERT_TRUE(entry_value->is_list());
- EXPECT_EQ(2u, entry_value->GetListDeprecated().size());
- EXPECT_TRUE(ListEntryMatches(entry_value->GetListDeprecated(), 0u, kSecondUrl,
- kSecondFinalStatus, kSecondOrigin, "1"));
- EXPECT_TRUE(ListEntryMatches(entry_value->GetListDeprecated(), 1u, kFirstUrl,
- kFirstFinalStatus, kFirstOrigin, "0"));
+ EXPECT_EQ(2u, entry_value.size());
+ EXPECT_TRUE(ListEntryMatches(entry_value, 0u, kSecondUrl, kSecondFinalStatus,
+ kSecondOrigin, "1"));
+ EXPECT_TRUE(ListEntryMatches(entry_value, 1u, kFirstUrl, kFirstFinalStatus,
+ kFirstOrigin, "0"));
// Add a third entry and make sure that the first one drops off.
const char* const kThirdUrl = "http://www.gamma.com/";
@@ -107,20 +99,16 @@ TEST(PrerenderHistoryTest, GetAsValue) {
epoch_start + base::Milliseconds(2));
history.AddEntry(entry_third);
entry_value = history.CopyEntriesAsValue();
- ASSERT_TRUE(entry_value.get() != nullptr);
- ASSERT_TRUE(entry_value->is_list());
- EXPECT_EQ(2u, entry_value->GetListDeprecated().size());
- EXPECT_TRUE(ListEntryMatches(entry_value->GetListDeprecated(), 0u, kThirdUrl,
- kThirdFinalStatus, kThirdOrigin, "2"));
- EXPECT_TRUE(ListEntryMatches(entry_value->GetListDeprecated(), 1u, kSecondUrl,
- kSecondFinalStatus, kSecondOrigin, "1"));
+ EXPECT_EQ(2u, entry_value.size());
+ EXPECT_TRUE(ListEntryMatches(entry_value, 0u, kThirdUrl, kThirdFinalStatus,
+ kThirdOrigin, "2"));
+ EXPECT_TRUE(ListEntryMatches(entry_value, 1u, kSecondUrl, kSecondFinalStatus,
+ kSecondOrigin, "1"));
// Make sure clearing history acts as expected.
history.Clear();
entry_value = history.CopyEntriesAsValue();
- ASSERT_TRUE(entry_value.get() != nullptr);
- ASSERT_TRUE(entry_value->is_list());
- EXPECT_TRUE(entry_value->GetListDeprecated().empty());
+ EXPECT_TRUE(entry_value.empty());
}
} // namespace
diff --git a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
index 5961545f7e2..015986e8ec8 100644
--- a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
+++ b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
@@ -381,17 +381,18 @@ bool ClickBasedCategoryRanker::ReadOrderFromPrefs(
void ClickBasedCategoryRanker::StoreOrderToPrefs(
const std::vector<RankedCategory>& ordered_categories) {
- base::ListValue list;
+ base::Value::List list;
for (const RankedCategory& category : ordered_categories) {
- auto dictionary = std::make_unique<base::DictionaryValue>();
- dictionary->SetIntKey(kCategoryIdKey, category.category.id());
- dictionary->SetIntKey(kClicksKey, category.clicks);
- dictionary->SetStringKey(
+ base::Value::Dict dictionary;
+ dictionary.Set(kCategoryIdKey, category.category.id());
+ dictionary.Set(kClicksKey, category.clicks);
+ dictionary.Set(
kLastDismissedKey,
base::NumberToString(SerializeTime(category.last_dismissed)));
list.Append(std::move(dictionary));
}
- pref_service_->Set(prefs::kClickBasedCategoryRankerOrderWithClicks, list);
+ pref_service_->Set(prefs::kClickBasedCategoryRankerOrderWithClicks,
+ base::Value(std::move(list)));
}
std::vector<ClickBasedCategoryRanker::RankedCategory>::iterator
diff --git a/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc b/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc
index a45809c6e39..17cdbc2d7f7 100644
--- a/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc
+++ b/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc
@@ -107,6 +107,7 @@ void CachedImageFetcher::OnImageFetchedFromDatabase(
data,
// We're not dealing with multi-frame images.
/*desired_image_frame_size=*/gfx::Size(),
+ /*data_decoder=*/nullptr,
base::BindOnce(&CachedImageFetcher::OnImageDecodedFromDatabase,
weak_ptr_factory_.GetWeakPtr(),
std::move(image_callback), suggestion_id, url));
diff --git a/chromium/components/ntp_snippets/remote/json_request.cc b/chromium/components/ntp_snippets/remote/json_request.cc
index ccc40b7aea3..6b5ca907738 100644
--- a/chromium/components/ntp_snippets/remote/json_request.cc
+++ b/chromium/components/ntp_snippets/remote/json_request.cc
@@ -98,11 +98,11 @@ std::string ISO639FromPosixLocale(const std::string& locale) {
return language;
}
-void AppendLanguageInfoToList(base::ListValue* list,
+void AppendLanguageInfoToList(base::Value::List* list,
const UrlLanguageHistogram::LanguageInfo& info) {
- auto lang = std::make_unique<base::DictionaryValue>();
- lang->SetStringKey("language", info.language_code);
- lang->SetDoubleKey("frequency", info.frequency);
+ base::Value::Dict lang;
+ lang.Set("language", info.language_code);
+ lang.Set("frequency", info.frequency);
list->Append(std::move(lang));
}
@@ -306,60 +306,58 @@ JsonRequest::Builder::BuildResourceRequest() const {
}
std::string JsonRequest::Builder::BuildBody() const {
- auto request = std::make_unique<base::DictionaryValue>();
+ base::Value::Dict request;
std::string user_locale = PosixLocaleFromBCP47Language(params_.language_code);
if (!user_locale.empty()) {
- request->SetStringKey("uiLanguage", user_locale);
+ request.Set("uiLanguage", user_locale);
}
- request->SetStringKey("priority", params_.interactive_request
- ? "USER_ACTION"
- : "BACKGROUND_PREFETCH");
+ request.Set("priority", params_.interactive_request ? "USER_ACTION"
+ : "BACKGROUND_PREFETCH");
- auto excluded = std::make_unique<base::ListValue>();
+ base::Value::List excluded;
for (const auto& id : params_.excluded_ids) {
- excluded->Append(id);
+ excluded.Append(id);
}
- request->Set("excludedSuggestionIds", std::move(excluded));
+ request.Set("excludedSuggestionIds", std::move(excluded));
if (!user_class_.empty()) {
- request->SetStringKey("userActivenessClass", user_class_);
+ request.Set("userActivenessClass", user_class_);
}
if (!display_capability_.empty()) {
- request->SetStringKey("displayCapability", display_capability_);
+ request.Set("displayCapability", display_capability_);
}
language::UrlLanguageHistogram::LanguageInfo ui_language;
language::UrlLanguageHistogram::LanguageInfo other_top_language;
PrepareLanguages(&ui_language, &other_top_language);
if (ui_language.frequency != 0 || other_top_language.frequency != 0) {
- auto language_list = std::make_unique<base::ListValue>();
+ base::Value::List language_list;
if (ui_language.frequency > 0) {
- AppendLanguageInfoToList(language_list.get(), ui_language);
+ AppendLanguageInfoToList(&language_list, ui_language);
}
if (other_top_language.frequency > 0) {
- AppendLanguageInfoToList(language_list.get(), other_top_language);
+ AppendLanguageInfoToList(&language_list, other_top_language);
}
- request->Set("topLanguages", std::move(language_list));
+ request.Set("topLanguages", std::move(language_list));
}
// TODO(vitaliii): Support count_to_fetch without requiring
// |exclusive_category|.
if (params_.exclusive_category.has_value()) {
- base::DictionaryValue exclusive_category_parameters;
- exclusive_category_parameters.SetIntKey(
- "id", params_.exclusive_category->remote_id());
- exclusive_category_parameters.SetIntKey("numSuggestions",
- params_.count_to_fetch);
- base::ListValue category_parameters;
+ base::Value::Dict exclusive_category_parameters;
+ exclusive_category_parameters.Set("id",
+ params_.exclusive_category->remote_id());
+ exclusive_category_parameters.Set("numSuggestions", params_.count_to_fetch);
+ base::Value::List category_parameters;
category_parameters.Append(std::move(exclusive_category_parameters));
- request->SetKey("categoryParameters", std::move(category_parameters));
+ request.Set("categoryParameters", std::move(category_parameters));
}
std::string request_json;
bool success = base::JSONWriter::WriteWithOptions(
- *request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json);
+ request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json);
DCHECK(success);
return request_json;
}
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
index 76eba1ebaa0..4e1e82ee651 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -1619,23 +1619,24 @@ void RemoteSuggestionsProviderImpl::StoreCategoriesToPrefs() {
return category_ranker_->Compare(left.first, right.first);
});
// Convert the relevant info into a base::ListValue for storage.
- base::ListValue list;
+ base::Value::List list;
for (const auto& entry : to_store) {
const Category& category = entry.first;
const CategoryContent& content = *entry.second;
- auto dict = std::make_unique<base::DictionaryValue>();
- dict->SetIntKey(kCategoryContentId, category.id());
+ base::Value::Dict dict;
+ dict.Set(kCategoryContentId, category.id());
// TODO(tschumann): Persist other properties of the CategoryInfo.
- dict->SetStringKey(kCategoryContentTitle, content.info.title());
- dict->SetBoolKey(kCategoryContentProvidedByServer,
- content.included_in_last_server_response);
+ dict.Set(kCategoryContentTitle, content.info.title());
+ dict.Set(kCategoryContentProvidedByServer,
+ content.included_in_last_server_response);
bool has_fetch_action = content.info.additional_action() ==
ContentSuggestionsAdditionalAction::FETCH;
- dict->SetBoolKey(kCategoryContentAllowFetchingMore, has_fetch_action);
+ dict.Set(kCategoryContentAllowFetchingMore, has_fetch_action);
list.Append(std::move(dict));
}
// Finally, store the result in the pref service.
- pref_service_->Set(prefs::kRemoteSuggestionCategories, list);
+ pref_service_->Set(prefs::kRemoteSuggestionCategories,
+ base::Value(std::move(list)));
}
RemoteSuggestionsProviderImpl::CategoryContent::CategoryContent(
diff --git a/chromium/components/ntp_tiles/icon_cacher_impl.cc b/chromium/components/ntp_tiles/icon_cacher_impl.cc
index a0bcf179b9b..40abdfb4cbe 100644
--- a/chromium/components/ntp_tiles/icon_cacher_impl.cc
+++ b/chromium/components/ntp_tiles/icon_cacher_impl.cc
@@ -20,6 +20,7 @@
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/ntp_tiles/features.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/data_decoder/public/cpp/data_decoder.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
@@ -69,10 +70,12 @@ int GetMinimumFetchingSizeForChromeSuggestionsFaviconsFromServer() {
IconCacherImpl::IconCacherImpl(
favicon::FaviconService* favicon_service,
favicon::LargeIconService* large_icon_service,
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher)
+ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
+ std::unique_ptr<data_decoder::DataDecoder> data_decoder)
: favicon_service_(favicon_service),
large_icon_service_(large_icon_service),
- image_fetcher_(std::move(image_fetcher)) {}
+ image_fetcher_(std::move(image_fetcher)),
+ data_decoder_(std::move(data_decoder)) {}
IconCacherImpl::~IconCacherImpl() = default;
@@ -129,6 +132,8 @@ void IconCacherImpl::OnGetFaviconImageForPageURLFinished(
kImageFetcherUmaClient);
// For images with multiple frames, prefer one of size 128x128px.
params.set_frame_size(gfx::Size(kDesiredFrameSize, kDesiredFrameSize));
+ if (data_decoder_)
+ params.set_data_decoder(data_decoder_.get());
image_fetcher_->FetchImage(
IconURL(site),
base::BindOnce(&IconCacherImpl::OnPopularSitesFaviconDownloaded,
@@ -192,7 +197,7 @@ IconCacherImpl::MaybeProvideDefaultIcon(
image_fetcher_->GetImageDecoder()->DecodeImage(
std::string(ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
site.default_icon_resource)),
- gfx::Size(kDesiredFrameSize, kDesiredFrameSize),
+ gfx::Size(kDesiredFrameSize, kDesiredFrameSize), data_decoder_.get(),
preliminary_callback->callback());
return preliminary_callback;
}
diff --git a/chromium/components/ntp_tiles/icon_cacher_impl.h b/chromium/components/ntp_tiles/icon_cacher_impl.h
index 61f9aec365a..b3cd6323e51 100644
--- a/chromium/components/ntp_tiles/icon_cacher_impl.h
+++ b/chromium/components/ntp_tiles/icon_cacher_impl.h
@@ -17,6 +17,10 @@
#include "components/ntp_tiles/icon_cacher.h"
#include "components/ntp_tiles/popular_sites.h"
+namespace data_decoder {
+class DataDecoder;
+} // namespace data_decoder
+
namespace favicon {
class FaviconService;
class LargeIconService;
@@ -45,7 +49,8 @@ class IconCacherImpl : public IconCacher {
// crbug.com/696563
IconCacherImpl(favicon::FaviconService* favicon_service,
favicon::LargeIconService* large_icon_service,
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher);
+ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
+ std::unique_ptr<data_decoder::DataDecoder> data_decoder);
IconCacherImpl(const IconCacherImpl&) = delete;
IconCacherImpl& operator=(const IconCacherImpl&) = delete;
@@ -102,6 +107,7 @@ class IconCacherImpl : public IconCacher {
const raw_ptr<favicon::LargeIconService> large_icon_service_;
std::unique_ptr<image_fetcher::ImageFetcher> const image_fetcher_;
std::map<GURL, std::vector<base::OnceClosure>> in_flight_requests_;
+ std::unique_ptr<data_decoder::DataDecoder> data_decoder_;
base::WeakPtrFactory<IconCacherImpl> weak_ptr_factory_{this};
};
diff --git a/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc b/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
index 3f5c97a1f60..72adbe99470 100644
--- a/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
+++ b/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
@@ -32,6 +32,7 @@
#include "components/image_fetcher/core/mock_image_fetcher.h"
#include "components/image_fetcher/core/request_metadata.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/data_decoder/public/cpp/data_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/mock_resource_bundle_delegate.h"
@@ -197,7 +198,8 @@ TEST_F(IconCacherTestPopularSites, LargeCached) {
PreloadIcon(site_.url, site_.large_icon_url,
favicon_base::IconType::kTouchIcon, 128, 128);
- IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_),
+ nullptr);
cacher.StartFetchPopularSites(site_, done.Get(), done.Get());
WaitForMainThreadTasksToFinish();
EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::IconType::kFavicon));
@@ -216,7 +218,8 @@ TEST_F(IconCacherTestPopularSites, LargeNotCachedAndFetchSucceeded) {
EXPECT_CALL(done, Run()).WillOnce(Quit(&loop));
}
- IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_),
+ nullptr);
cacher.StartFetchPopularSites(site_, done.Get(), done.Get());
loop.Run();
EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::IconType::kFavicon));
@@ -235,7 +238,8 @@ TEST_F(IconCacherTestPopularSites, SmallNotCachedAndFetchSucceeded) {
EXPECT_CALL(done, Run()).WillOnce(Quit(&loop));
}
- IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_),
+ nullptr);
cacher.StartFetchPopularSites(site_, done.Get(), done.Get());
loop.Run();
EXPECT_TRUE(IconIsCachedFor(site_.url, favicon_base::IconType::kFavicon));
@@ -253,7 +257,8 @@ TEST_F(IconCacherTestPopularSites, LargeNotCachedAndFetchFailed) {
.WillOnce(FailFetch());
}
- IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_),
+ nullptr);
cacher.StartFetchPopularSites(site_, done.Get(), done.Get());
WaitForMainThreadTasksToFinish();
EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::IconType::kFavicon));
@@ -264,7 +269,8 @@ TEST_F(IconCacherTestPopularSites, HandlesEmptyCallbacksNicely) {
base::HistogramTester histogram_tester;
EXPECT_CALL(*image_fetcher_, FetchImageAndData_(_, _, _, _))
.WillOnce(PassFetch(128, 128));
- IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_),
+ nullptr);
cacher.StartFetchPopularSites(site_, base::NullCallback(),
base::NullCallback());
WaitForHistoryThreadTasksToFinish(); // Writing the icon into the DB.
@@ -302,7 +308,8 @@ TEST_F(IconCacherTestPopularSites, ProvidesDefaultIconAndSucceedsWithFetching) {
EXPECT_CALL(icon_available, Run()).WillOnce(Quit(&fetch_loop));
}
- IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_),
+ nullptr);
site_.default_icon_resource = 12345;
cacher.StartFetchPopularSites(site_, icon_available.Get(),
preliminary_icon_available.Get());
@@ -331,7 +338,8 @@ TEST_F(IconCacherTestPopularSites, LargeNotCachedAndFetchPerformedOnlyOnce) {
EXPECT_CALL(done, Run()).WillOnce(Return()).WillOnce(Quit(&loop));
}
- IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, nullptr, std::move(image_fetcher_),
+ nullptr);
cacher.StartFetchPopularSites(site_, done.Get(), done.Get());
cacher.StartFetchPopularSites(site_, done.Get(), done.Get());
loop.Run();
@@ -363,7 +371,7 @@ TEST_F(IconCacherTestMostLikely, Cached) {
kTestDipForServerRequests, kTestIconTypeForServerRequests,
kTestGoogleServerClientParam);
IconCacherImpl cacher(&favicon_service_, &large_icon_service,
- std::move(fetcher_for_icon_cacher_));
+ std::move(fetcher_for_icon_cacher_), nullptr);
base::MockOnceClosure done;
EXPECT_CALL(done, Run()).Times(0);
@@ -394,7 +402,7 @@ TEST_F(IconCacherTestMostLikely, NotCachedAndFetchSucceeded) {
kTestDipForServerRequests, kTestIconTypeForServerRequests,
kTestGoogleServerClientParam);
IconCacherImpl cacher(&favicon_service_, &large_icon_service,
- std::move(fetcher_for_icon_cacher_));
+ std::move(fetcher_for_icon_cacher_), nullptr);
cacher.StartFetchMostLikely(page_url, done.Get());
// Both these task runners need to be flushed in order to get |done| called by
@@ -426,7 +434,7 @@ TEST_F(IconCacherTestMostLikely, NotCachedAndFetchFailed) {
kTestDipForServerRequests, kTestIconTypeForServerRequests,
kTestGoogleServerClientParam);
IconCacherImpl cacher(&favicon_service_, &large_icon_service,
- std::move(fetcher_for_icon_cacher_));
+ std::move(fetcher_for_icon_cacher_), nullptr);
cacher.StartFetchMostLikely(page_url, done.Get());
// Both these task runners need to be flushed before flushing the main thread
@@ -449,7 +457,7 @@ TEST_F(IconCacherTestMostLikely, HandlesEmptyCallbacksNicely) {
kTestDipForServerRequests, kTestIconTypeForServerRequests,
kTestGoogleServerClientParam);
IconCacherImpl cacher(&favicon_service_, &large_icon_service,
- std::move(fetcher_for_icon_cacher_));
+ std::move(fetcher_for_icon_cacher_), nullptr);
cacher.StartFetchMostLikely(page_url, base::NullCallback());
@@ -489,7 +497,7 @@ TEST_F(IconCacherTestMostLikely, NotCachedAndFetchPerformedOnlyOnce) {
kTestDipForServerRequests, kTestIconTypeForServerRequests,
kTestGoogleServerClientParam);
IconCacherImpl cacher(&favicon_service_, &large_icon_service,
- std::move(fetcher_for_icon_cacher_));
+ std::move(fetcher_for_icon_cacher_), nullptr);
cacher.StartFetchMostLikely(page_url, done.Get());
cacher.StartFetchMostLikely(page_url, done.Get());
diff --git a/chromium/components/offline_items_collection/core/BUILD.gn b/chromium/components/offline_items_collection/core/BUILD.gn
index cded3c41230..b6305a17656 100644
--- a/chromium/components/offline_items_collection/core/BUILD.gn
+++ b/chromium/components/offline_items_collection/core/BUILD.gn
@@ -114,6 +114,8 @@ if (is_android) {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//url:gurl_java",
]
@@ -148,7 +150,7 @@ if (is_android) {
testonly = true
deps = [
":core_java",
- "//base:base_java",
+ "//base:jni_java",
"//third_party/junit",
]
diff --git a/chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.cc b/chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.cc
index 7a3e4f85628..aa422c17416 100644
--- a/chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.cc
+++ b/chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.cc
@@ -71,7 +71,8 @@ void ForwardShareInfoToJavaCallback(
void RenameItemCallback(ScopedJavaGlobalRef<jobject> j_callback,
RenameResult result) {
- base::android::RunIntCallbackAndroid(j_callback, static_cast<int>(result));
+ base::android::RunIntCallbackAndroid(j_callback,
+ static_cast<int32_t>(result));
}
void RunGetAllItemsCallback(const base::android::JavaRef<jobject>& j_callback,
diff --git a/chromium/components/offline_items_collection/core/android/offline_item_bridge.cc b/chromium/components/offline_items_collection/core/android/offline_item_bridge.cc
index 0ffd468895b..da5cc12dcab 100644
--- a/chromium/components/offline_items_collection/core/android/offline_item_bridge.cc
+++ b/chromium/components/offline_items_collection/core/android/offline_item_bridge.cc
@@ -40,7 +40,7 @@ JNI_OfflineItemBridge_createOfflineItemAndMaybeAddToList(
item.is_openable, ConvertUTF8ToJavaString(env, item.file_path.value()),
ConvertUTF8ToJavaString(env, item.mime_type),
url::GURLAndroid::FromNativeGURL(env, item.url),
- ConvertUTF8ToJavaString(env, item.original_url.spec()),
+ url::GURLAndroid::FromNativeGURL(env, item.original_url),
item.is_off_the_record, ConvertUTF8ToJavaString(env, item.otr_profile_id),
static_cast<jint>(item.state), static_cast<jint>(item.fail_state),
static_cast<jint>(item.pending_state), item.is_resumable,
diff --git a/chromium/components/offline_items_collection/core/offline_content_aggregator.cc b/chromium/components/offline_items_collection/core/offline_content_aggregator.cc
index 9741f84dec4..2ed10418c01 100644
--- a/chromium/components/offline_items_collection/core/offline_content_aggregator.cc
+++ b/chromium/components/offline_items_collection/core/offline_content_aggregator.cc
@@ -254,7 +254,8 @@ void OfflineContentAggregator::OnItemRemoved(const ContentId& id) {
if (!pending_providers_.empty()) {
auto item = std::find_if(aggregated_items_.begin(), aggregated_items_.end(),
[id](const OfflineItem& p) { return p.id == id; });
- aggregated_items_.erase(item);
+ if (item != aggregated_items_.end())
+ aggregated_items_.erase(item);
}
NotifyItemRemoved(id);
}
diff --git a/chromium/components/offline_pages/content/background_loader/DEPS b/chromium/components/offline_pages/content/background_loader/DEPS
index 33226e6f72d..974b340577a 100644
--- a/chromium/components/offline_pages/content/background_loader/DEPS
+++ b/chromium/components/offline_pages/content/background_loader/DEPS
@@ -3,4 +3,5 @@ include_rules = [
"+content/public/test",
"+content/public/common/window_container_type.mojom-shared.h",
"+third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h",
+ "+third_party/blink/public/mojom/mediastream/media_stream.mojom.h",
]
diff --git a/chromium/components/offline_pages/content/background_loader/background_loader_contents.cc b/chromium/components/offline_pages/content/background_loader/background_loader_contents.cc
index 2834e3ee577..186711550a4 100644
--- a/chromium/components/offline_pages/content/background_loader/background_loader_contents.cc
+++ b/chromium/components/offline_pages/content/background_loader/background_loader_contents.cc
@@ -7,8 +7,10 @@
#include <utility>
#include "build/build_config.h"
+#include "content/public/browser/media_stream_request.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
namespace background_loader {
@@ -114,7 +116,7 @@ void BackgroundLoaderContents::RequestMediaAccessPermission(
content::MediaResponseCallback callback) {
// No permissions granted, act as if dismissed.
std::move(callback).Run(
- blink::MediaStreamDevices(),
+ blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED,
std::unique_ptr<content::MediaStreamUI>());
}
diff --git a/chromium/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc b/chromium/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
index 85de9a123a3..01898850640 100644
--- a/chromium/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
+++ b/chromium/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
@@ -12,6 +12,7 @@
#include "content/public/common/window_container_type.mojom-shared.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
#include "url/gurl.h"
namespace background_loader {
@@ -36,10 +37,11 @@ class BackgroundLoaderContentsTest : public testing::Test,
bool download() { return download_; }
bool can_download_delegate_called() { return delegate_called_; }
- void MediaAccessCallback(const blink::MediaStreamDevices& devices,
- blink::mojom::MediaStreamRequestResult result,
- std::unique_ptr<content::MediaStreamUI> ui);
- blink::MediaStreamDevices devices() { return devices_; }
+ void MediaAccessCallback(
+ const blink::mojom::StreamDevicesSet& stream_devices_set,
+ blink::mojom::MediaStreamRequestResult result,
+ std::unique_ptr<content::MediaStreamUI> ui);
+ blink::mojom::StreamDevices& devices() { return devices_; }
blink::mojom::MediaStreamRequestResult request_result() {
return request_result_;
}
@@ -50,8 +52,8 @@ class BackgroundLoaderContentsTest : public testing::Test,
private:
std::unique_ptr<BackgroundLoaderContents> contents_;
bool download_;
- bool delegate_called_;
- blink::MediaStreamDevices devices_;
+ bool delegate_called_ = false;
+ blink::mojom::StreamDevices devices_;
blink::mojom::MediaStreamRequestResult request_result_;
std::unique_ptr<content::MediaStreamUI> media_stream_ui_;
base::WaitableEvent waiter_;
@@ -59,7 +61,6 @@ class BackgroundLoaderContentsTest : public testing::Test,
BackgroundLoaderContentsTest::BackgroundLoaderContentsTest()
: download_(false),
- delegate_called_(false),
waiter_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED) {}
@@ -91,10 +92,15 @@ void BackgroundLoaderContentsTest::SetDelegate() {
}
void BackgroundLoaderContentsTest::MediaAccessCallback(
- const blink::MediaStreamDevices& devices,
+ const blink::mojom::StreamDevicesSet& stream_devices_set,
blink::mojom::MediaStreamRequestResult result,
std::unique_ptr<content::MediaStreamUI> ui) {
- devices_ = devices;
+ if (result == blink::mojom::MediaStreamRequestResult::OK) {
+ ASSERT_FALSE(stream_devices_set.stream_devices.empty());
+ devices_ = *stream_devices_set.stream_devices[0];
+ } else {
+ ASSERT_TRUE(stream_devices_set.stream_devices.empty());
+ }
request_result_ = result;
media_stream_ui_.reset(ui.get());
waiter_.Signal();
@@ -171,7 +177,8 @@ TEST_F(BackgroundLoaderContentsTest, DoesNotGiveMediaAccessPermission) {
base::Unretained(this)));
WaitForSignal();
// No devices allowed.
- ASSERT_TRUE(devices().empty());
+ ASSERT_TRUE(!devices().audio_device.has_value() &&
+ !devices().video_device.has_value());
// Permission has been dismissed rather than denied.
ASSERT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED,
request_result());
diff --git a/chromium/components/offline_pages/core/model/offline_page_model_taskified.cc b/chromium/components/offline_pages/core/model/offline_page_model_taskified.cc
index db2bdd845ee..8223668a88f 100644
--- a/chromium/components/offline_pages/core/model/offline_page_model_taskified.cc
+++ b/chromium/components/offline_pages/core/model/offline_page_model_taskified.cc
@@ -32,8 +32,8 @@
#include "components/offline_pages/core/model/update_publish_id_task.h"
#include "components/offline_pages/core/model/visuals_availability_task.h"
#include "components/offline_pages/core/offline_clock.h"
+#include "components/offline_pages/core/offline_page_archive_publisher.h"
#include "components/offline_pages/core/offline_page_client_policy.h"
-#include "components/offline_pages/core/offline_page_feature.h"
#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_page_model.h"
#include "components/offline_pages/core/offline_store_utils.h"
@@ -186,7 +186,7 @@ OfflinePageModelTaskified::OfflinePageModelTaskified(
CreateArchivesDirectoryIfNeeded();
}
-OfflinePageModelTaskified::~OfflinePageModelTaskified() {}
+OfflinePageModelTaskified::~OfflinePageModelTaskified() = default;
void OfflinePageModelTaskified::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
@@ -582,9 +582,9 @@ void OfflinePageModelTaskified::OnDeleteDone(
// Remove the page from the system download manager. We don't need to wait for
// completion before calling the delete page callback.
- task_runner_->PostTask(FROM_HERE,
- base::BindOnce(&OfflinePageModelTaskified::Unpublish,
- archive_publisher_.get(), publish_ids));
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&OfflinePageModelTaskified::Unpublish,
+ archive_publisher_->GetWeakPtr(), publish_ids));
if (!callback.is_null())
std::move(callback).Run(result);
@@ -609,9 +609,9 @@ void OfflinePageModelTaskified::OnStoreFaviconDone(int64_t offline_id,
}
void OfflinePageModelTaskified::Unpublish(
- OfflinePageArchivePublisher* publisher,
+ base::WeakPtr<OfflinePageArchivePublisher> publisher,
const std::vector<PublishedArchiveId>& publish_ids) {
- if (!publish_ids.empty())
+ if (publisher && !publish_ids.empty())
publisher->UnpublishArchives(publish_ids);
}
@@ -663,7 +663,7 @@ void OfflinePageModelTaskified::RunMaintenanceTasks(base::Time now,
void OfflinePageModelTaskified::OnPersistentPageConsistencyCheckDone(
bool success,
const std::vector<PublishedArchiveId>& ids_of_deleted_pages) {
- Unpublish(archive_publisher_.get(), ids_of_deleted_pages);
+ Unpublish(archive_publisher_->GetWeakPtr(), ids_of_deleted_pages);
}
void OfflinePageModelTaskified::OnClearCachedPagesDone(
diff --git a/chromium/components/offline_pages/core/model/offline_page_model_taskified.h b/chromium/components/offline_pages/core/model/offline_page_model_taskified.h
index 385021c6239..f94e8cae5bf 100644
--- a/chromium/components/offline_pages/core/model/offline_page_model_taskified.h
+++ b/chromium/components/offline_pages/core/model/offline_page_model_taskified.h
@@ -10,18 +10,14 @@
#include <string>
#include <vector>
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
-#include "components/keyed_service/core/keyed_service.h"
#include "components/offline_pages/core/model/clear_storage_task.h"
#include "components/offline_pages/core/offline_page_archive_publisher.h"
#include "components/offline_pages/core/offline_page_archiver.h"
#include "components/offline_pages/core/offline_page_model.h"
#include "components/offline_pages/core/offline_page_model_event_logger.h"
#include "components/offline_pages/core/offline_page_types.h"
-#include "components/offline_pages/core/offline_store_types.h"
#include "components/offline_pages/task/task_queue.h"
class GURL;
@@ -185,7 +181,7 @@ class OfflinePageModelTaskified : public OfflinePageModel,
PublishArchiveResult publish_results);
// Method for unpublishing the page from downloads.
- static void Unpublish(OfflinePageArchivePublisher* publisher,
+ static void Unpublish(base::WeakPtr<OfflinePageArchivePublisher> publisher,
const std::vector<PublishedArchiveId>& publish_ids);
// Other utility methods.
diff --git a/chromium/components/offline_pages/core/offline_page_archive_publisher.h b/chromium/components/offline_pages/core/offline_page_archive_publisher.h
index cea4b63428f..00884026918 100644
--- a/chromium/components/offline_pages/core/offline_page_archive_publisher.h
+++ b/chromium/components/offline_pages/core/offline_page_archive_publisher.h
@@ -7,7 +7,6 @@
#include <cstdint>
-#include "base/callback.h"
#include "base/files/file_path.h"
#include "components/offline_pages/core/offline_page_item.h"
#include "components/offline_pages/core/offline_page_types.h"
@@ -60,7 +59,7 @@ class OfflinePageArchivePublisher {
base::OnceCallback<void(const OfflinePageItem& /* offline_page */,
PublishArchiveResult /* archive_result */)>;
- virtual ~OfflinePageArchivePublisher() {}
+ virtual ~OfflinePageArchivePublisher() = default;
// Publishes the page on a background thread, then returns to the
// OfflinePageModelTaskified's done callback.
@@ -72,6 +71,8 @@ class OfflinePageArchivePublisher {
// Removes archives from downloads.
virtual void UnpublishArchives(
const std::vector<PublishedArchiveId>& archive_ids) const = 0;
+
+ virtual base::WeakPtr<OfflinePageArchivePublisher> GetWeakPtr() = 0;
};
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/offline_page_test_archive_publisher.cc b/chromium/components/offline_pages/core/offline_page_test_archive_publisher.cc
index 7b6128b70cf..e5d61622d89 100644
--- a/chromium/components/offline_pages/core/offline_page_test_archive_publisher.cc
+++ b/chromium/components/offline_pages/core/offline_page_test_archive_publisher.cc
@@ -8,12 +8,10 @@
#include <utility>
#include "base/bind.h"
-#include "base/files/file_util.h"
#include "base/location.h"
#include "base/task/single_thread_task_runner.h"
#include "components/offline_pages/core/archive_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
namespace offline_pages {
@@ -69,4 +67,9 @@ void OfflinePageTestArchivePublisher::UnpublishArchives(
last_removed_id_ = archive_ids.back();
}
+base::WeakPtr<OfflinePageArchivePublisher>
+OfflinePageTestArchivePublisher::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/offline_page_test_archive_publisher.h b/chromium/components/offline_pages/core/offline_page_test_archive_publisher.h
index f796016af69..1ec17695b62 100644
--- a/chromium/components/offline_pages/core/offline_page_test_archive_publisher.h
+++ b/chromium/components/offline_pages/core/offline_page_test_archive_publisher.h
@@ -7,11 +7,10 @@
#include <cstdint>
-#include "base/callback.h"
#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "components/offline_pages/core/offline_page_archive_publisher.h"
#include "components/offline_pages/core/offline_page_item.h"
-#include "components/offline_pages/core/offline_page_types.h"
namespace base {
class SequencedTaskRunner;
@@ -35,6 +34,8 @@ class OfflinePageTestArchivePublisher : public OfflinePageArchivePublisher {
void UnpublishArchives(
const std::vector<PublishedArchiveId>& archive_ids) const override;
+ base::WeakPtr<OfflinePageArchivePublisher> GetWeakPtr() override;
+
void set_archive_attempt_failure(bool fail) {
archive_attempt_failure_ = fail;
}
@@ -56,6 +57,7 @@ class OfflinePageTestArchivePublisher : public OfflinePageArchivePublisher {
mutable PublishedArchiveId last_removed_id_;
raw_ptr<ArchiveManager> archive_manager_;
+ base::WeakPtrFactory<OfflinePageArchivePublisher> weak_ptr_factory_{this};
};
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc
index 9665a7f9cf1..1a18d3349ed 100644
--- a/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc
@@ -1,5 +1,5 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
-// // Use of this source code is governed by a BSD-style license that can be
+// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
diff --git a/chromium/components/offline_pages/resources/renovations.js b/chromium/components/offline_pages/resources/renovations.js
index 16e56f0b962..0a0bcbba3ff 100644
--- a/chromium/components/offline_pages/resources/renovations.js
+++ b/chromium/components/offline_pages/resources/renovations.js
@@ -12,7 +12,7 @@ function renovation_wikipedia() {
for (let i = 0; i < elems.length; ++i) {
// If a block was already expanded, re-adding the 'open-block'
// class will do nothing; no need to check if it's there already.
- elems.item(i).className += " open-block";
+ elems.item(i).className += ' open-block';
}
// Now we force the page to load images inside the expanded
diff --git a/chromium/components/omnibox/browser/BUILD.gn b/chromium/components/omnibox/browser/BUILD.gn
index ef53d5d4600..d0dc7efb10e 100644
--- a/chromium/components/omnibox/browser/BUILD.gn
+++ b/chromium/components/omnibox/browser/BUILD.gn
@@ -53,12 +53,12 @@ aggregate_vector_icons("omnibox_vector_icons") {
"https_valid_in_chip.icon",
"incognito.icon",
"install_desktop.icon",
+ "journeys.icon",
"keyword_search.icon",
"offline_pin.icon",
"page.icon",
"pedal.icon",
"product.icon",
- "send.icon",
"share.icon",
"star.icon",
"star_active.icon",
@@ -88,8 +88,6 @@ static_library("vector_icons") {
static_library("browser") {
sources = [
- "actions/history_clusters_action.cc",
- "actions/history_clusters_action.h",
"actions/omnibox_action.cc",
"actions/omnibox_action.h",
"actions/omnibox_action_concepts.h",
@@ -257,11 +255,9 @@ static_library("browser") {
"//components/dom_distiller/core:core",
"//components/favicon/core",
"//components/favicon_base",
- "//components/history_clusters/core",
"//components/keyed_service/core",
"//components/metrics",
"//components/navigation_metrics",
- "//components/ntp_tiles",
"//components/open_from_clipboard",
"//components/pref_registry",
"//components/prefs",
@@ -294,7 +290,15 @@ static_library("browser") {
}
if (!is_ios) {
- deps += [ "//components/keyed_service/content" ]
+ sources += [
+ "actions/history_clusters_action.cc",
+ "actions/history_clusters_action.h",
+ ]
+ deps += [
+ "//components/history_clusters/core",
+ "//components/keyed_service/content",
+ "//components/optimization_guide/core:entities",
+ ]
}
if (enable_extensions) {
@@ -417,13 +421,15 @@ if (is_android) {
"android/java/src/org/chromium/components/omnibox/SecurityButtonAnimationDelegate.java",
"android/java/src/org/chromium/components/omnibox/SecurityStatusIcon.java",
"android/java/src/org/chromium/components/omnibox/SuggestionAnswer.java",
+ "android/java/src/org/chromium/components/omnibox/action/HistoryClustersAction.java",
"android/java/src/org/chromium/components/omnibox/action/OmniboxPedal.java",
]
resources_package = "org.chromium.components.omnibox"
deps = [
":java_resources",
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/widget/android:java",
"//components/embedder_support/android:util_java",
"//components/query_tiles:query_tile_java",
@@ -440,6 +446,29 @@ if (is_android) {
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
}
+ android_library("javatests") {
+ testonly = true
+ sources = [ "//components/omnibox/browser/android/javatests/src/org/chromium/components/omnibox/AutocompleteResultUnitTest.java" ]
+ deps = [
+ ":browser_java",
+ ":test_util_java",
+ "//base:base_java_test_support",
+ "//third_party/androidx:androidx_test_runner_java",
+ "//third_party/junit:junit",
+ ]
+ }
+
+ android_library("test_util_java") {
+ testonly = true
+ sources = [ "//components/omnibox/browser/android/javatests/src/org/chromium/components/omnibox/AutocompleteMatchBuilder.java" ]
+ deps = [
+ "//components/omnibox/browser:browser_java",
+ "//components/query_tiles:query_tile_java",
+ "//third_party/androidx:androidx_collection_collection_java",
+ "//url:gurl_java",
+ ]
+ }
+
java_cpp_enum("browser_java_enums_srcjar") {
sources = [
"actions/omnibox_action_concepts.h",
@@ -457,6 +486,7 @@ if (is_android) {
"android/java/src/org/chromium/components/omnibox/AutocompleteSchemeClassifier.java",
"android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java",
"android/java/src/org/chromium/components/omnibox/SuggestionAnswer.java",
+ "android/java/src/org/chromium/components/omnibox/action/HistoryClustersAction.java",
"android/java/src/org/chromium/components/omnibox/action/OmniboxPedal.java",
]
}
@@ -641,6 +671,11 @@ source_set("unit_tests") {
"//components/vector_icons:vector_icons",
]
}
+
+ if (!is_ios) {
+ sources += [ "actions/history_clusters_action_unittest.cc" ]
+ deps += [ "//components/history_clusters/core:test_support" ]
+ }
}
fuzzer_test("autocomplete_input_fuzzer") {
diff --git a/chromium/components/omnibox/browser/test_java_sources.gni b/chromium/components/omnibox/browser/test_java_sources.gni
deleted file mode 100644
index a866ca36d5a..00000000000
--- a/chromium/components/omnibox/browser/test_java_sources.gni
+++ /dev/null
@@ -1,8 +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.
-
-omnibox_test_java_sources = [
- "//components/omnibox/browser/android/javatests/src/org/chromium/components/omnibox/AutocompleteMatchBuilder.java",
- "//components/omnibox/browser/android/javatests/src/org/chromium/components/omnibox/AutocompleteResultUnitTest.java",
-]
diff --git a/chromium/components/omnibox_strings.grdp b/chromium/components/omnibox_strings.grdp
index 4cac0acd1e1..9ef03a8e44c 100644
--- a/chromium/components/omnibox_strings.grdp
+++ b/chromium/components/omnibox_strings.grdp
@@ -312,4 +312,10 @@
<message name="IDS_SEARCH_ENGINES_STARTER_PACK_SETTINGS_KEYWORD" desc = "The keyword required to trigger settings search in keyword mode. This will be prepended with an '@'.">
Settings
</message>
+ <message name="IDS_SEARCH_ENGINES_STARTER_PACK_TABS_NAME" desc = "The name of the search engine to search through open Chrome Tabs as it appears on chrome://settings/searchEngines.">
+ Chrome Tabs
+ </message>
+ <message name="IDS_SEARCH_ENGINES_STARTER_PACK_TABS_KEYWORD" desc = "The keyword required to trigger tab search in keyword mode. This will be prepended with an '@'.">
+ Tabs
+ </message>
</grit-part>
diff --git a/chromium/components/omnibox_strings_grdp/IDS_SEARCH_ENGINES_STARTER_PACK_TABS_KEYWORD.png.sha1 b/chromium/components/omnibox_strings_grdp/IDS_SEARCH_ENGINES_STARTER_PACK_TABS_KEYWORD.png.sha1
new file mode 100644
index 00000000000..28c6e250c30
--- /dev/null
+++ b/chromium/components/omnibox_strings_grdp/IDS_SEARCH_ENGINES_STARTER_PACK_TABS_KEYWORD.png.sha1
@@ -0,0 +1 @@
+a1601845088b54f885183b628c39136f5042a9f5 \ No newline at end of file
diff --git a/chromium/components/omnibox_strings_grdp/IDS_SEARCH_ENGINES_STARTER_PACK_TABS_NAME.png.sha1 b/chromium/components/omnibox_strings_grdp/IDS_SEARCH_ENGINES_STARTER_PACK_TABS_NAME.png.sha1
new file mode 100644
index 00000000000..28c6e250c30
--- /dev/null
+++ b/chromium/components/omnibox_strings_grdp/IDS_SEARCH_ENGINES_STARTER_PACK_TABS_NAME.png.sha1
@@ -0,0 +1 @@
+a1601845088b54f885183b628c39136f5042a9f5 \ No newline at end of file
diff --git a/chromium/components/optimization_guide/BUILD.gn b/chromium/components/optimization_guide/BUILD.gn
index 1492440b0ef..9c2edb76ce4 100644
--- a/chromium/components/optimization_guide/BUILD.gn
+++ b/chromium/components/optimization_guide/BUILD.gn
@@ -1,9 +1,13 @@
import("//build/buildflag_header.gni")
import("//components/optimization_guide/features.gni")
+import("//third_party/tflite/features.gni")
buildflag_header("machine_learning_tflite_buildflags") {
header = "machine_learning_tflite_buildflags.h"
- flags = [ "BUILD_WITH_TFLITE_LIB=$build_with_tflite_lib" ]
+ flags = [
+ "BUILD_WITH_TFLITE_LIB=$build_with_tflite_lib",
+ "BUILD_TFLITE_WITH_XNNPACK=$build_tflite_with_xnnpack",
+ ]
}
buildflag_header("optimization_guide_buildflags") {
diff --git a/chromium/components/optimization_guide/content/browser/BUILD.gn b/chromium/components/optimization_guide/content/browser/BUILD.gn
index 26462cd3846..978a2280010 100644
--- a/chromium/components/optimization_guide/content/browser/BUILD.gn
+++ b/chromium/components/optimization_guide/content/browser/BUILD.gn
@@ -12,6 +12,8 @@ static_library("browser") {
"optimization_guide_decider.h",
"page_content_annotations_service.cc",
"page_content_annotations_service.h",
+ "page_content_annotations_validator.cc",
+ "page_content_annotations_validator.h",
"page_content_annotations_web_contents_observer.cc",
"page_content_annotations_web_contents_observer.h",
"page_content_annotator.h",
@@ -75,7 +77,7 @@ static_library("test_support") {
source_set("unit_tests") {
testonly = true
sources = [
- "page_content_annotations_service_unittest.cc",
+ "page_content_annotations_validator_unittest.cc",
"page_content_annotations_web_contents_observer_unittest.cc",
"page_text_dump_result_unittest.cc",
"page_text_observer_unittest.cc",
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.cc b/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.cc
index 7568a9d293c..4e0db552caa 100644
--- a/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.cc
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.cc
@@ -26,120 +26,47 @@ namespace optimization_guide {
namespace {
-const char kPageTopicsModelMetadataTypeUrl[] =
- "type.googleapis.com/"
- "google.internal.chrome.optimizationguide.v1.PageTopicsModelMetadata";
-
-// The ID of the NONE category in the taxonomy. This node always exists.
-// Semantically, the none category is attached to data for which we can say
-// with certainty that no single label in the taxonomy is appropriate.
-const char kNoneCategoryId[] = "-2";
-
-// The max number of page entities that should be output.
-// TODO(crbug/1234578): Make the number of entities Finch-able once we
-// know how much the model is expected to output.
-constexpr size_t kMaxPageEntities = 5;
-
-std::unique_ptr<history::VisitContentModelAnnotations>
-GetOrCreateCurrentContentModelAnnotations(
- std::unique_ptr<history::VisitContentModelAnnotations>
- current_annotations) {
- if (current_annotations)
- return current_annotations;
- return std::make_unique<history::VisitContentModelAnnotations>();
+base::TaskTraits GetTaskTraits() {
+ base::TaskTraits task_traits = {base::MayBlock(),
+ base::TaskPriority::BEST_EFFORT};
+ if (base::FeatureList::IsEnabled(
+ features::
+ kOptimizationGuideUseContinueOnShutdownForPageContentAnnotations)) {
+ task_traits = {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
+ }
+ return task_traits;
}
} // namespace
PageContentAnnotationsModelManager::PageContentAnnotationsModelManager(
- const std::string& application_locale,
OptimizationGuideModelProvider* optimization_guide_model_provider)
- : optimization_guide_model_provider_(optimization_guide_model_provider) {
- if (features::ShouldExecutePageVisibilityModelOnPageContent(
- application_locale)) {
- SetUpPageTopicsModel(optimization_guide_model_provider);
- ordered_models_to_execute_.push_back(
- proto::OPTIMIZATION_TARGET_PAGE_TOPICS);
- }
- if (features::ShouldExecutePageEntitiesModelOnPageContent(
- application_locale)) {
- SetUpPageEntitiesModel(optimization_guide_model_provider);
- ordered_models_to_execute_.push_back(
- proto::OPTIMIZATION_TARGET_PAGE_ENTITIES);
- }
-}
+ : optimization_guide_model_provider_(optimization_guide_model_provider) {}
PageContentAnnotationsModelManager::~PageContentAnnotationsModelManager() =
default;
-void PageContentAnnotationsModelManager::Annotate(
- const std::string& text,
- PageContentAnnotatedCallback callback) {
- ExecuteNextModelOrInvokeCallback(text, /*current_annotations=*/nullptr,
- std::move(callback),
- /*current_model_index=*/absl::nullopt);
-}
-
-void PageContentAnnotationsModelManager::ExecuteNextModelOrInvokeCallback(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations> current_annotations,
- PageContentAnnotatedCallback callback,
- absl::optional<size_t> current_model_index) {
- size_t next_model_index_to_execute = current_model_index.value_or(-1) + 1;
- if (next_model_index_to_execute >= ordered_models_to_execute_.size()) {
- // We have run all the models, so run the callback.
- DCHECK(callback);
- std::move(callback).Run(current_annotations
- ? absl::make_optional(*current_annotations)
- : absl::nullopt);
- return;
- }
-
- // Execute the next model.
- proto::OptimizationTarget optimization_target =
- ordered_models_to_execute_.at(next_model_index_to_execute);
- switch (optimization_target) {
- case proto::OPTIMIZATION_TARGET_PAGE_TOPICS:
- ExecutePageTopicsModel(text, std::move(current_annotations),
- std::move(callback), next_model_index_to_execute);
- break;
- case proto::OPTIMIZATION_TARGET_PAGE_ENTITIES:
- ExecutePageEntitiesModel(text, std::move(current_annotations),
- std::move(callback),
- next_model_index_to_execute);
- break;
- default:
- NOTREACHED();
- // NOTREACHED is a no-op in release builds, so just run next model.
- ExecuteNextModelOrInvokeCallback(text, std::move(current_annotations),
- std::move(callback),
- next_model_index_to_execute);
- }
-}
-
void PageContentAnnotationsModelManager::SetUpPageEntitiesModel(
- OptimizationGuideModelProvider* optimization_guide_model_provider) {
+ OptimizationGuideModelProvider* optimization_guide_model_provider,
+ base::OnceCallback<void(bool)> callback) {
LOCAL_HISTOGRAM_BOOLEAN(
"OptimizationGuide.PageContentAnnotationsModelManager."
"PageEntitiesModelRequested",
true);
#if BUILDFLAG(BUILD_WITH_INTERNAL_OPTIMIZATION_GUIDE)
- base::TaskTraits task_traits = {base::MayBlock(),
- base::TaskPriority::BEST_EFFORT};
- if (base::FeatureList::IsEnabled(
- features::
- kOptimizationGuideUseContinueOnShutdownForPageContentAnnotations)) {
- task_traits = {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
- base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
- }
-
scoped_refptr<base::SequencedTaskRunner> background_task_runner =
- base::ThreadPool::CreateSequencedTaskRunner(task_traits);
+ base::ThreadPool::CreateSequencedTaskRunner(GetTaskTraits());
page_entities_model_executor_ =
std::make_unique<PageEntitiesModelExecutorImpl>(
optimization_guide_model_provider, background_task_runner);
+
+ page_entities_model_executor_->AddOnModelUpdatedCallback(
+ base::BindOnce(std::move(callback), true));
+#else
+ std::move(callback).Run(false);
#endif
}
@@ -150,111 +77,18 @@ void PageContentAnnotationsModelManager::
page_entities_model_executor_ = std::move(page_entities_model_executor);
}
-void PageContentAnnotationsModelManager::ExecutePageEntitiesModel(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations> current_annotations,
- PageContentAnnotatedCallback callback,
- size_t current_model_index) {
- LOCAL_HISTOGRAM_BOOLEAN(
- "OptimizationGuide.PageContentAnnotationsModelManager."
- "PageEntitiesModelExecutionRequested",
- true);
- if (page_entities_model_executor_) {
- page_entities_model_executor_->HumanReadableExecuteModelWithInput(
- text,
- base::BindOnce(&PageContentAnnotationsModelManager::
- OnPageEntitiesModelExecutionCompleted,
- weak_ptr_factory_.GetWeakPtr(), text,
- std::move(current_annotations), base::TimeTicks::Now(),
- std::move(callback), current_model_index));
- return;
- }
- OnPageEntitiesModelExecutionCompleted(
- text, std::move(current_annotations), base::TimeTicks::Now(),
- std::move(callback), current_model_index,
- /*output=*/absl::nullopt);
-}
-
-void PageContentAnnotationsModelManager::OnPageEntitiesModelExecutionCompleted(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations> current_annotations,
- base::TimeTicks execution_start,
- PageContentAnnotatedCallback callback,
- size_t current_model_index,
- const absl::optional<std::vector<ScoredEntityMetadata>>& output) {
- if (output) {
- current_annotations = GetOrCreateCurrentContentModelAnnotations(
- std::move(current_annotations));
-
- // Determine the entities with the highest weights.
- std::vector<ScoredEntityMetadata> entity_candidates = std::move(*output);
- std::sort(entity_candidates.begin(), entity_candidates.end(),
- [](const ScoredEntityMetadata& a, const ScoredEntityMetadata& b) {
- return a.score > b.score;
- });
-
- // Add the top entities to the working current annotations.
- for (const auto& entity : entity_candidates) {
- if (current_annotations->entities.size() >= kMaxPageEntities) {
- break;
- }
-
- // We expect the weight to be between 0 and 1, so that the normalization
- // from 0 to 100 makes sense.
- DCHECK(entity.score >= 0.0 && entity.score <= 1.0);
- current_annotations->entities.emplace_back(
- history::VisitContentModelAnnotations::Category(
- entity.metadata.entity_id, static_cast<int>(100 * entity.score)));
- }
- base::UmaHistogramTimes(
- "OptimizationGuide.PageContentAnnotationsService."
- "PageEntitiesExecutionLatency",
- base::TimeTicks::Now() - execution_start);
- }
- ExecuteNextModelOrInvokeCallback(text, std::move(current_annotations),
- std::move(callback), current_model_index);
-}
-
-void PageContentAnnotationsModelManager::SetUpPageTopicsModel(
- OptimizationGuideModelProvider* optimization_guide_model_provider) {
- proto::Any model_metadata;
- model_metadata.set_type_url(kPageTopicsModelMetadataTypeUrl);
- proto::PageTopicsModelMetadata page_topics_model_metadata;
- page_topics_model_metadata.add_supported_output(
- proto::PAGE_TOPICS_SUPPORTED_OUTPUT_VISIBILITY);
- page_topics_model_metadata.add_supported_output(
- proto::PAGE_TOPICS_SUPPORTED_OUTPUT_CATEGORIES);
- page_topics_model_metadata.SerializeToString(model_metadata.mutable_value());
-
- base::TaskTraits task_traits = {base::MayBlock(),
- base::TaskPriority::BEST_EFFORT};
- if (base::FeatureList::IsEnabled(
- features::
- kOptimizationGuideUseContinueOnShutdownForPageContentAnnotations)) {
- task_traits = {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
- base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
- }
-
- page_topics_model_handler_ = std::make_unique<BertModelHandler>(
- optimization_guide_model_provider,
- base::ThreadPool::CreateSequencedTaskRunner(task_traits),
- proto::OPTIMIZATION_TARGET_PAGE_TOPICS, model_metadata);
-}
-
void PageContentAnnotationsModelManager::SetUpPageTopicsV2Model(
OptimizationGuideModelProvider* optimization_guide_model_provider) {
if (!features::PageTopicsBatchAnnotationsEnabled())
return;
- if (on_demand_page_topics_model_executor_)
+ if (page_topics_model_executor_)
return;
- on_demand_page_topics_model_executor_ =
- std::make_unique<PageTopicsModelExecutor>(
- optimization_guide_model_provider,
- base::ThreadPool::CreateSequencedTaskRunner(
- {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
- absl::nullopt);
+ page_topics_model_executor_ = std::make_unique<PageTopicsModelExecutor>(
+ optimization_guide_model_provider,
+ base::ThreadPool::CreateSequencedTaskRunner(GetTaskTraits()),
+ absl::nullopt);
}
void PageContentAnnotationsModelManager::SetUpPageVisibilityModel(
@@ -268,99 +102,10 @@ void PageContentAnnotationsModelManager::SetUpPageVisibilityModel(
page_visibility_model_executor_ =
std::make_unique<PageVisibilityModelExecutor>(
optimization_guide_model_provider,
- base::ThreadPool::CreateSequencedTaskRunner(
- {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+ base::ThreadPool::CreateSequencedTaskRunner(GetTaskTraits()),
absl::nullopt);
}
-void PageContentAnnotationsModelManager::ExecutePageTopicsModel(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations> current_annotations,
- PageContentAnnotatedCallback callback,
- size_t current_model_index) {
- LOCAL_HISTOGRAM_BOOLEAN(
- "OptimizationGuide.PageContentAnnotationsModelManager."
- "PageTopicsModelExecutionRequested",
- true);
-
- bool model_available = page_topics_model_handler_->ModelAvailable();
-
- base::UmaHistogramBoolean(
- "OptimizationGuide.PageContentAnnotationsService.ModelAvailable",
- model_available);
-
- if (!model_available) {
- // TODO(crbug/1177102): Figure out if we want to enqueue it for later if
- // model isn't ready, but if we call this when the model isn't ready, it
- // will just return absl::nullopt for now.
- ExecuteNextModelOrInvokeCallback(text, std::move(current_annotations),
- std::move(callback), current_model_index);
- return;
- }
-
- absl::optional<proto::PageTopicsModelMetadata> model_metadata =
- page_topics_model_handler_->ParsedSupportedFeaturesForLoadedModel<
- proto::PageTopicsModelMetadata>();
- if (!model_metadata) {
- NOTREACHED();
- // Continue to run the callback since NOTREACHED is a no-op in prod.
- ExecuteNextModelOrInvokeCallback(text, std::move(current_annotations),
- std::move(callback), current_model_index);
- return;
- }
-
- bool has_supported_output = false;
- for (const auto supported_output : model_metadata->supported_output()) {
- if (supported_output == proto::PAGE_TOPICS_SUPPORTED_OUTPUT_CATEGORIES ||
- supported_output == proto::PAGE_TOPICS_SUPPORTED_OUTPUT_VISIBILITY) {
- has_supported_output = true;
- break;
- }
- }
- if (!has_supported_output) {
- // TODO(crbug/1177102): Add histogram.
- ExecuteNextModelOrInvokeCallback(text, std::move(current_annotations),
- std::move(callback), current_model_index);
- return;
- }
-
- page_topics_model_handler_->ExecuteModelWithInput(
- base::BindOnce(&PageContentAnnotationsModelManager::
- OnPageTopicsModelExecutionCompleted,
- weak_ptr_factory_.GetWeakPtr(), text,
- std::move(current_annotations), std::move(callback),
- current_model_index, *model_metadata),
- text);
-}
-
-void PageContentAnnotationsModelManager::OnPageTopicsModelExecutionCompleted(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations> current_annotations,
- PageContentAnnotatedCallback callback,
- size_t current_model_index,
- const proto::PageTopicsModelMetadata& model_metadata,
- const absl::optional<std::vector<tflite::task::core::Category>>& output) {
- if (output) {
- current_annotations = GetOrCreateCurrentContentModelAnnotations(
- std::move(current_annotations));
- PopulateContentModelAnnotationsFromPageTopicsModelOutput(
- model_metadata, *output, current_annotations.get());
- }
-
- ExecuteNextModelOrInvokeCallback(text, std::move(current_annotations),
- std::move(callback), current_model_index);
-}
-
-absl::optional<int64_t>
-PageContentAnnotationsModelManager::GetPageTopicsModelVersion() const {
- absl::optional<proto::PageTopicsModelMetadata> model_metadata =
- page_topics_model_handler_->ParsedSupportedFeaturesForLoadedModel<
- proto::PageTopicsModelMetadata>();
- if (model_metadata)
- return model_metadata->version();
- return absl::nullopt;
-}
-
void PageContentAnnotationsModelManager::GetMetadataForEntityId(
const std::string& entity_id,
EntityMetadataRetrievedCallback callback) {
@@ -373,134 +118,6 @@ void PageContentAnnotationsModelManager::GetMetadataForEntityId(
std::move(callback).Run(absl::nullopt);
}
-void PageContentAnnotationsModelManager::
- PopulateContentModelAnnotationsFromPageTopicsModelOutput(
- const proto::PageTopicsModelMetadata& model_metadata,
- const std::vector<tflite::task::core::Category>& model_output,
- history::VisitContentModelAnnotations* out_content_annotations) const {
- out_content_annotations->page_topics_model_version = model_metadata.version();
-
- absl::optional<std::string> visibility_category_name;
- if (model_metadata.output_postprocessing_params().has_visibility_params()) {
- visibility_category_name = model_metadata.output_postprocessing_params()
- .visibility_params()
- .category_name();
- }
- // -1 is a sentinel value that means the visibility of the page was not
- // evaluated.
- out_content_annotations->visibility_score = -1.0;
- std::vector<std::pair<std::string, float>> category_candidates;
- for (const auto& category : model_output) {
- if (visibility_category_name &&
- category.class_name == *visibility_category_name) {
- out_content_annotations->visibility_score =
- static_cast<float>(1 - category.score);
- if (!model_metadata.output_postprocessing_params()
- .has_category_params()) {
- break;
- }
- continue;
- }
-
- // Assume everything else is for categories.
- int category_id;
- if (base::StringToInt(category.class_name, &category_id)) {
- category_candidates.emplace_back(std::make_pair(
- category.class_name, static_cast<float>(category.score)));
- }
- }
-
- // Postprocess categories.
- if (!model_metadata.output_postprocessing_params().has_category_params()) {
- // No parameters for postprocessing, so just return.
- return;
- }
- const proto::PageTopicsCategoryPostprocessingParams category_params =
- model_metadata.output_postprocessing_params().category_params();
-
- // Determine the categories with the highest weights.
- std::sort(category_candidates.begin(), category_candidates.end(),
- [](const std::pair<std::string, float>& a,
- const std::pair<std::string, float>& b) {
- return a.second > b.second;
- });
- size_t max_categories = static_cast<size_t>(category_params.max_categories());
- float total_weight = 0.0;
- float sum_positive_scores = 0.0;
- absl::optional<std::pair<size_t, float>> none_idx_and_weight;
- std::vector<std::pair<std::string, float>> categories;
- categories.reserve(max_categories);
- for (size_t i = 0; i < category_candidates.size() && i < max_categories;
- i++) {
- std::pair<std::string, float> candidate = category_candidates[i];
- categories.push_back(candidate);
- total_weight += candidate.second;
-
- if (candidate.second > 0)
- sum_positive_scores += candidate.second;
-
- if (candidate.first == kNoneCategoryId) {
- none_idx_and_weight = std::make_pair(i, candidate.second);
- }
- }
-
- // Prune out categories that do not meet the minimum threshold.
- if (category_params.min_category_weight() > 0) {
- categories.erase(
- std::remove_if(categories.begin(), categories.end(),
- [&](const std::pair<std::string, float>& category) {
- return category.second <
- category_params.min_category_weight();
- }),
- categories.end());
- }
-
- // Prune out none weights.
- if (total_weight == 0) {
- return;
- }
- if (none_idx_and_weight) {
- if ((none_idx_and_weight->second / total_weight) >
- category_params.min_none_weight()) {
- // None weight is too strong.
- return;
- }
- // None weight doesn't matter, so prune it out. Note that it may have
- // already been removed above if its weight was below the category min.
- categories.erase(
- std::remove_if(categories.begin(), categories.end(),
- [&](const std::pair<std::string, float>& category) {
- return category.first == kNoneCategoryId;
- }),
- categories.end());
- }
-
- // Normalize category weights.
- float normalization_factor =
- sum_positive_scores > 0 ? sum_positive_scores : 1.0;
- categories.erase(
- std::remove_if(
- categories.begin(), categories.end(),
- [&](const std::pair<std::string, float>& category) {
- return (category.second / normalization_factor) <
- category_params.min_normalized_weight_within_top_n();
- }),
- categories.end());
-
- std::vector<history::VisitContentModelAnnotations::Category> final_categories;
- final_categories.reserve(categories.size());
- for (const auto& category : categories) {
- // We expect the weight to be between 0 and 1, so that the normalization
- // from 0 to 100 makes sense.
- DCHECK(category.second >= 0.0 && category.second <= 1.0);
- final_categories.emplace_back(
- history::VisitContentModelAnnotations::Category(
- category.first, static_cast<int>(100 * category.second)));
- }
- DCHECK_LE(final_categories.size(), max_categories);
- out_content_annotations->categories = final_categories;
-}
-
void PageContentAnnotationsModelManager::RequestAndNotifyWhenModelAvailable(
AnnotationType type,
base::OnceCallback<void(bool)> callback) {
@@ -508,8 +125,8 @@ void PageContentAnnotationsModelManager::RequestAndNotifyWhenModelAvailable(
// No-op if the executor is already setup.
SetUpPageTopicsV2Model(optimization_guide_model_provider_);
- if (on_demand_page_topics_model_executor_) {
- on_demand_page_topics_model_executor_->AddOnModelUpdatedCallback(
+ if (page_topics_model_executor_) {
+ page_topics_model_executor_->AddOnModelUpdatedCallback(
base::BindOnce(std::move(callback), true));
return;
}
@@ -526,7 +143,16 @@ void PageContentAnnotationsModelManager::RequestAndNotifyWhenModelAvailable(
}
}
- // TODO(crbug/1278828): Add support for page entities.
+ if (type == AnnotationType::kPageEntities) {
+ if (page_entities_model_executor_) {
+ page_entities_model_executor_->AddOnModelUpdatedCallback(
+ base::BindOnce(std::move(callback), true));
+ } else {
+ SetUpPageEntitiesModel(optimization_guide_model_provider_,
+ std::move(callback));
+ }
+ return;
+ }
std::move(callback).Run(false);
}
@@ -534,15 +160,16 @@ void PageContentAnnotationsModelManager::RequestAndNotifyWhenModelAvailable(
absl::optional<ModelInfo>
PageContentAnnotationsModelManager::GetModelInfoForType(
AnnotationType type) const {
- if (type == AnnotationType::kPageTopics &&
- on_demand_page_topics_model_executor_) {
- return on_demand_page_topics_model_executor_->GetModelInfo();
+ if (type == AnnotationType::kPageTopics && page_topics_model_executor_) {
+ return page_topics_model_executor_->GetModelInfo();
}
if (type == AnnotationType::kContentVisibility &&
page_visibility_model_executor_) {
return page_visibility_model_executor_->GetModelInfo();
}
- // TODO(crbug/1278828): Add support for page entities.
+ if (type == AnnotationType::kPageEntities && page_entities_model_executor_) {
+ return page_entities_model_executor_->GetModelInfo();
+ }
return absl::nullopt;
}
@@ -578,8 +205,8 @@ void PageContentAnnotationsModelManager::MaybeStartNextAnnotationJob() {
JobQueue::Pointer job_ptr = job_queue_.FirstMax();
if (job_ptr.is_null()) {
// There are no more jobs to run, so unload all models.
- if (on_demand_page_topics_model_executor_) {
- on_demand_page_topics_model_executor_->UnloadModel();
+ if (page_topics_model_executor_) {
+ page_topics_model_executor_->UnloadModel();
}
if (page_visibility_model_executor_) {
page_visibility_model_executor_->UnloadModel();
@@ -600,8 +227,8 @@ void PageContentAnnotationsModelManager::MaybeStartNextAnnotationJob() {
// Reset every other model from memory so that there aren't a bunch of models
// all loaded at the same time.
if (job->type() != AnnotationType::kPageTopics &&
- on_demand_page_topics_model_executor_) {
- on_demand_page_topics_model_executor_->UnloadModel();
+ page_topics_model_executor_) {
+ page_topics_model_executor_->UnloadModel();
}
if (job->type() != AnnotationType::kContentVisibility &&
page_visibility_model_executor_) {
@@ -609,15 +236,15 @@ void PageContentAnnotationsModelManager::MaybeStartNextAnnotationJob() {
}
if (job->type() == AnnotationType::kPageTopics) {
- if (!on_demand_page_topics_model_executor_) {
+ if (!page_topics_model_executor_) {
job->FillWithNullOutputs();
job->OnComplete();
job.reset();
std::move(on_job_complete_callback).Run();
return;
}
- on_demand_page_topics_model_executor_->ExecuteJob(
- std::move(on_job_complete_callback), std::move(job));
+ page_topics_model_executor_->ExecuteJob(std::move(on_job_complete_callback),
+ std::move(job));
return;
}
@@ -634,12 +261,16 @@ void PageContentAnnotationsModelManager::MaybeStartNextAnnotationJob() {
return;
}
- // TODO(crbug/1278828): Add support for page entities.
if (job->type() == AnnotationType::kPageEntities) {
- job->FillWithNullOutputs();
- job->OnComplete();
- job.reset();
- std::move(on_job_complete_callback).Run();
+ if (!page_entities_model_executor_) {
+ job->FillWithNullOutputs();
+ job->OnComplete();
+ job.reset();
+ std::move(on_job_complete_callback).Run();
+ return;
+ }
+ page_entities_model_executor_->ExecuteJob(
+ std::move(on_job_complete_callback), std::move(job));
return;
}
NOTREACHED();
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.h b/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.h
index 98d2fb7b88e..7e6337cb02b 100644
--- a/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.h
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager.h
@@ -6,16 +6,13 @@
#define COMPONENTS_OPTIMIZATION_GUIDE_CONTENT_BROWSER_PAGE_CONTENT_ANNOTATIONS_MODEL_MANAGER_H_
#include "base/memory/raw_ptr.h"
-#include "components/history/core/browser/url_row.h"
#include "components/optimization_guide/content/browser/page_content_annotator.h"
-#include "components/optimization_guide/core/bert_model_handler.h"
#include "components/optimization_guide/core/entity_metadata.h"
#include "components/optimization_guide/core/model_info.h"
#include "components/optimization_guide/core/page_content_annotation_job.h"
#include "components/optimization_guide/core/page_content_annotations_common.h"
#include "components/optimization_guide/core/page_topics_model_executor.h"
#include "components/optimization_guide/core/page_visibility_model_executor.h"
-#include "components/optimization_guide/proto/page_topics_model_metadata.pb.h"
#include "net/base/priority_queue.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -24,10 +21,6 @@ namespace optimization_guide {
class OptimizationGuideModelProvider;
class PageEntitiesModelExecutor;
-// Callback to inform the caller that the page content has been annotated.
-using PageContentAnnotatedCallback = base::OnceCallback<void(
- const absl::optional<history::VisitContentModelAnnotations>&)>;
-
// Callback to inform the caller that the metadata for an entity ID has been
// retrieved.
using EntityMetadataRetrievedCallback =
@@ -36,8 +29,7 @@ using EntityMetadataRetrievedCallback =
// Manages the loading and execution of models used to annotate page content.
class PageContentAnnotationsModelManager : public PageContentAnnotator {
public:
- PageContentAnnotationsModelManager(
- const std::string& application_locale,
+ explicit PageContentAnnotationsModelManager(
OptimizationGuideModelProvider* optimization_guide_model_provider);
~PageContentAnnotationsModelManager() override;
PageContentAnnotationsModelManager(
@@ -45,14 +37,11 @@ class PageContentAnnotationsModelManager : public PageContentAnnotator {
PageContentAnnotationsModelManager& operator=(
const PageContentAnnotationsModelManager&) = delete;
- // Requests to annotate |text|, will invoke |callback| when completed.
+ // Each call to |Annotate| starts one job which are are run one at a time
+ // based on their original order and priority. Each job runs to completion
+ // before the next one will start. Thus, it's ok to call |Annotate| multiple
+ // times in a row, like in a loop, and only one job will run at a time.
//
- // This will execute all supported models of the PageContentAnnotationsService
- // feature and is only used by the History service code path. See the below
- // |Annotate| for the publicly available Annotation code path.
- // TODO(crbug/1278833): Remove this.
- void Annotate(const std::string& text, PageContentAnnotatedCallback callback);
-
// PageContentAnnotator:
void Annotate(BatchAnnotationCallback callback,
const std::vector<std::string>& inputs,
@@ -63,11 +52,6 @@ class PageContentAnnotationsModelManager : public PageContentAnnotator {
AnnotationType type,
base::OnceCallback<void(bool)> callback) override;
- // Returns the version of the page topics model that is currently being used
- // to annotate page content. Will return |absl::nullopt| if no model is being
- // used to annotate page topics for received page content.
- absl::optional<int64_t> GetPageTopicsModelVersion() const;
-
// Retrieves the metadata associated with |entity_id|. Invokes |callback|
// when done.
void GetMetadataForEntityId(const std::string& entity_id,
@@ -87,8 +71,6 @@ class PageContentAnnotationsModelManager : public PageContentAnnotator {
// All publicly posted jobs will have this priority level.
kNormal = 1,
- // TODO(crbug/1278833): Add a kHigh value for internal jobs.
-
// Always keep this last and as the highest priority + 1. This value is
// passed to the priority queue ctor as "how many level of priorities are
// supported" by the queue.
@@ -103,106 +85,23 @@ class PageContentAnnotationsModelManager : public PageContentAnnotator {
kComplete = 3,
};
- // Runs the next model on |text| based on |current_model_index|. If the
- // last model in |ordered_models_to_execute_| has executed, it will invoke
- // |callback| with the contents of |current_annotations|.
- //
- // |current_annotations|, |callback|, and |current_model_index| will be passed
- // through a series of callbacks and will be invoked after the last model
- // executes.
- void ExecuteNextModelOrInvokeCallback(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations>
- current_annotations,
- PageContentAnnotatedCallback callback,
- absl::optional<size_t> current_model_index);
-
// Set up the machinery for execution of the page entities model. This should
- // only be run at construction.
- void SetUpPageEntitiesModel(OptimizationGuideModelProvider* model_provider);
-
- // Requests to execute the page entities model with |text|, populate
- // |current_annotations| with detected entities on success, and proceed to
- // execute any subsequent models.
- //
- // |current_annotations|, |callback|, and |current_model_index| will be passed
- // through a series of callbacks and will be invoked after the last model
- // executes.
- void ExecutePageEntitiesModel(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations>
- current_annotations,
- PageContentAnnotatedCallback callback,
- size_t current_model_index);
-
- // Invoked when the page entities model has finished executing. If |output| is
- // populated, |current_annotations| will be populated with the detected
- // entities. Regardless of success, this will proceed to execute any
- // subsequent models. If it is the last model to execute, it will invoke
- // |callback| with the contents of |current_annotations| after it has
- // populated it based on |output|.
- void OnPageEntitiesModelExecutionCompleted(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations>
- current_annotations,
- base::TimeTicks execution_start,
- PageContentAnnotatedCallback callback,
- size_t current_model_index,
- const absl::optional<std::vector<ScoredEntityMetadata>>& output);
-
- // Set up the machinery for execution of the page topics model. This should
- // only be run at construction.
- void SetUpPageTopicsModel(
- OptimizationGuideModelProvider* optimization_guide_model_provider);
+ // only be run at construction. Runs |callback(true)| when the model executor
+ // has a model file or |callback(false)| if the model executor is not
+ // available.
+ void SetUpPageEntitiesModel(OptimizationGuideModelProvider* model_provider,
+ base::OnceCallback<void(bool)> callback);
// Set up the machinery for execution of the page topics v2 model. This should
// only be run at construction.
- // TODO(crbug/1266504): Actually call this based on a separate feature flag.
void SetUpPageTopicsV2Model(
OptimizationGuideModelProvider* optimization_guide_model_provider);
// Set up the machinery for execution of the page visibility model. This
// should only be run at construction.
- // TODO(crbug/1266504): Actually call this based on a separate feature flag.
void SetUpPageVisibilityModel(
OptimizationGuideModelProvider* optimization_guide_model_provider);
- // Requests to execute the page topics model with |text|, populate
- // |current_annotations| with detected topics on success, and proceed to
- // execute any subsequent models.
- //
- // |current_annotations|, |callback|, and |current_model_index| will be passed
- // through a series of callbacks and will be invoked after the last model
- // executes.
- void ExecutePageTopicsModel(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations>
- current_annotations,
- PageContentAnnotatedCallback callback,
- size_t current_model_index);
-
- // Invoked when the page topics model has finished executing. If |output| is
- // populated, |current_annotations| will be populated based on that.
- // Regardless of success, this will proceed to execute any subsequent models.
- // If it is the last model to execute, it will invoke |callback| with the
- // contents of |current_annotations| after it has populated it based on
- // |output|.
- void OnPageTopicsModelExecutionCompleted(
- const std::string& text,
- std::unique_ptr<history::VisitContentModelAnnotations>
- current_annotations,
- PageContentAnnotatedCallback callback,
- size_t current_model_index,
- const proto::PageTopicsModelMetadata& model_metadata,
- const absl::optional<std::vector<tflite::task::core::Category>>& output);
-
- // Populates |out_content_annotations| based on |model_output| and
- // |model_metadata|.
- void PopulateContentModelAnnotationsFromPageTopicsModelOutput(
- const proto::PageTopicsModelMetadata& model_metadata,
- const std::vector<tflite::task::core::Category>& model_output,
- history::VisitContentModelAnnotations* out_content_annotations) const;
-
// Overrides |page_entities_model_executor_| for testing purposes.
void OverridePageEntitiesModelExecutorForTesting(
std::unique_ptr<PageEntitiesModelExecutor> page_entities_model_executor);
@@ -213,20 +112,9 @@ class PageContentAnnotationsModelManager : public PageContentAnnotator {
// Called when a |job| finishes executing, just before it is deleted.
void OnJobExecutionComplete();
- // The model executor responsible for executing the page topics model.
- //
- // Can be nullptr if the page topics model will not be running for the
- // session.
- // TODO(crbug/1266504): Deprecate this in favor of
- // |on_demand_page_topics_model_executor_|.
- std::unique_ptr<BertModelHandler> page_topics_model_handler_;
-
// The model executor responsible for executing the on demand page topics
// model.
- // TODO(crbug/1266504): Replace |page_topics_model_handler_| with
- // this.
- std::unique_ptr<PageTopicsModelExecutor>
- on_demand_page_topics_model_executor_;
+ std::unique_ptr<PageTopicsModelExecutor> page_topics_model_executor_;
// The model executor responsible for executing the page visibility model.
std::unique_ptr<PageVisibilityModelExecutor> page_visibility_model_executor_;
@@ -237,9 +125,6 @@ class PageContentAnnotationsModelManager : public PageContentAnnotator {
// session.
std::unique_ptr<PageEntitiesModelExecutor> page_entities_model_executor_;
- // The ordering of models to execute on the page content of each page load.
- std::vector<proto::OptimizationTarget> ordered_models_to_execute_;
-
// The queue of all jobs to be executed. This data structure supports FIFO
// ordering for elements of the same priority.
using JobQueue =
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager_unittest.cc b/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager_unittest.cc
index ccd448cd52f..07bb5e1064a 100644
--- a/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager_unittest.cc
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_model_manager_unittest.cc
@@ -27,38 +27,6 @@ namespace optimization_guide {
using ::testing::FloatEq;
using ::testing::UnorderedElementsAre;
-namespace {
-
-// Fetch and calculate the total number of samples from all the bins for
-// |histogram_name|.
-int GetTotalHistogramSamples(const base::HistogramTester* histogram_tester,
- const std::string& histogram_name) {
- std::vector<base::Bucket> buckets =
- histogram_tester->GetAllSamples(histogram_name);
- int total = 0;
- for (const auto& bucket : buckets)
- total += bucket.count;
-
- return total;
-}
-
-int RetryForHistogramUntilCountReached(
- const base::HistogramTester* histogram_tester,
- const std::string& histogram_name,
- int count) {
- while (true) {
- base::ThreadPoolInstance::Get()->FlushForTesting();
- base::RunLoop().RunUntilIdle();
-
- int total = GetTotalHistogramSamples(histogram_tester, histogram_name);
- if (total >= count) {
- return total;
- }
- }
-}
-
-} // namespace
-
class ModelObserverTracker : public TestOptimizationGuideModelProvider {
public:
void AddObserverForOptimizationTargetModel(
@@ -93,7 +61,7 @@ class FakePageEntitiesModelExecutor : public PageEntitiesModelExecutor {
: entries_(entries), entity_metadata_(entity_metadata) {}
~FakePageEntitiesModelExecutor() override = default;
- void HumanReadableExecuteModelWithInput(
+ void ExecuteModelWithInput(
const std::string& text,
PageEntitiesMetadataModelExecutedCallback callback) override {
auto it = entries_.find(text);
@@ -110,6 +78,14 @@ class FakePageEntitiesModelExecutor : public PageEntitiesModelExecutor {
: absl::nullopt);
}
+ void AddOnModelUpdatedCallback(base::OnceClosure callback) override {
+ std::move(callback).Run();
+ }
+
+ absl::optional<ModelInfo> GetModelInfo() const override {
+ return absl::nullopt;
+ }
+
private:
base::flat_map<std::string, std::vector<ScoredEntityMetadata>> entries_;
base::flat_map<std::string, EntityMetadata> entity_metadata_;
@@ -121,7 +97,8 @@ class PageContentAnnotationsModelManagerTest : public testing::Test {
// Enable Visibility but disable Entities.
scoped_feature_list_.InitWithFeatures(
{features::kPageVisibilityPageContentAnnotations},
- {features::kPageEntitiesPageContentAnnotations});
+ {features::kPageEntitiesPageContentAnnotations,
+ features::kPreventLongRunningPredictionModels});
}
~PageContentAnnotationsModelManagerTest() override = default;
@@ -130,7 +107,7 @@ class PageContentAnnotationsModelManagerTest : public testing::Test {
model_observer_tracker_ = std::make_unique<ModelObserverTracker>();
model_manager_ = std::make_unique<PageContentAnnotationsModelManager>(
- "en-US", model_observer_tracker_.get());
+ model_observer_tracker_.get());
}
void TearDown() override {
@@ -138,26 +115,6 @@ class PageContentAnnotationsModelManagerTest : public testing::Test {
model_observer_tracker_.reset();
}
- void SendPageTopicsModelToExecutor(
- const absl::optional<proto::Any>& model_metadata) {
- base::FilePath source_root_dir;
- base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
- base::FilePath model_file_path =
- source_root_dir.AppendASCII("components")
- .AppendASCII("test")
- .AppendASCII("data")
- .AppendASCII("optimization_guide")
- .AppendASCII("bert_page_topics_model.tflite");
- std::unique_ptr<ModelInfo> model_info =
- TestModelInfoBuilder()
- .SetModelFilePath(model_file_path)
- .SetModelMetadata(model_metadata)
- .Build();
- model_manager()->page_topics_model_handler_->OnModelUpdated(
- proto::OPTIMIZATION_TARGET_PAGE_TOPICS, *model_info);
- RunUntilIdle();
- }
-
proto::PageTopicsModelMetadata MakeValidPageTopicsModelMetadata() const {
proto::PageTopicsModelMetadata page_topics_model_metadata;
page_topics_model_metadata.set_version(123);
@@ -178,7 +135,7 @@ class PageContentAnnotationsModelManagerTest : public testing::Test {
AnnotationType::kPageTopics, base::DoNothing());
// If the feature flag is disabled, the executor won't have been created so
// skip everything else.
- if (!model_manager()->on_demand_page_topics_model_executor_)
+ if (!model_manager()->page_topics_model_executor_)
return;
proto::Any any_metadata;
@@ -189,18 +146,17 @@ class PageContentAnnotationsModelManagerTest : public testing::Test {
base::FilePath source_root_dir;
base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
+ // We know that the model executor itself works fine (that's tested
+ // elsewhere), so just make sure that all the plumbing for the model
+ // execution: job, queue, background sequences, etc, are working correctly.
base::FilePath model_file_path =
- source_root_dir.AppendASCII("components")
- .AppendASCII("test")
- .AppendASCII("data")
- .AppendASCII("optimization_guide")
- .AppendASCII("bert_page_topics_model.tflite");
+ source_root_dir.AppendASCII("non_existent_model.tflite");
std::unique_ptr<ModelInfo> model_info =
TestModelInfoBuilder()
.SetModelFilePath(model_file_path)
.SetModelMetadata(any_metadata)
.Build();
- model_manager()->on_demand_page_topics_model_executor_->OnModelUpdated(
+ model_manager()->page_topics_model_executor_->OnModelUpdated(
proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2, *model_info);
RunUntilIdle();
}
@@ -216,12 +172,11 @@ class PageContentAnnotationsModelManagerTest : public testing::Test {
base::FilePath source_root_dir;
base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
+ // We know that the model executor itself works fine (that's tested
+ // elsewhere), so just make sure that all the plumbing for the model
+ // execution: job, queue, background sequences, etc, are working correctly.
base::FilePath model_file_path =
- source_root_dir.AppendASCII("components")
- .AppendASCII("test")
- .AppendASCII("data")
- .AppendASCII("optimization_guide")
- .AppendASCII("bert_page_topics_model.tflite");
+ source_root_dir.AppendASCII("non_existent_model.tflite");
std::unique_ptr<ModelInfo> model_info =
TestModelInfoBuilder()
.SetModelFilePath(model_file_path)
@@ -241,27 +196,6 @@ class PageContentAnnotationsModelManagerTest : public testing::Test {
entity_metadata));
}
- absl::optional<history::VisitContentModelAnnotations> Annotate(
- const std::string& text) {
- absl::optional<history::VisitContentModelAnnotations> content_annotations;
- base::RunLoop run_loop;
- model_manager()->Annotate(
- "sometext",
- base::BindOnce(
- [](base::RunLoop* run_loop,
- absl::optional<history::VisitContentModelAnnotations>*
- out_content_annotations,
- const absl::optional<history::VisitContentModelAnnotations>&
- content_annotations) {
- *out_content_annotations = content_annotations;
- run_loop->Quit();
- },
- &run_loop, &content_annotations));
- run_loop.Run();
-
- return content_annotations;
- }
-
absl::optional<EntityMetadata> GetMetadataForEntityId(
const std::string& entity_id) {
absl::optional<EntityMetadata> entities_metadata;
@@ -289,15 +223,6 @@ class PageContentAnnotationsModelManagerTest : public testing::Test {
return model_manager_.get();
}
- history::VisitContentModelAnnotations GetContentModelAnnotationsFromOutput(
- const proto::PageTopicsModelMetadata& metadata,
- const std::vector<tflite::task::core::Category>& model_output) const {
- history::VisitContentModelAnnotations annotations;
- model_manager()->PopulateContentModelAnnotationsFromPageTopicsModelOutput(
- metadata, model_output, &annotations);
- return annotations;
- }
-
base::HistogramTester* histogram_tester() const {
return histogram_tester_.get();
}
@@ -315,295 +240,13 @@ class PageContentAnnotationsModelManagerTest : public testing::Test {
};
TEST_F(PageContentAnnotationsModelManagerTest,
- SetsUpModelsCorrectlyBasedOnFeatureParams) {
- absl::optional<proto::Any> registered_model_metadata;
- EXPECT_TRUE(model_observer_tracker()->DidRegisterForTarget(
- proto::OPTIMIZATION_TARGET_PAGE_TOPICS, &registered_model_metadata));
- EXPECT_TRUE(registered_model_metadata.has_value());
- absl::optional<proto::PageTopicsModelMetadata> page_topics_model_metadata =
- ParsedAnyMetadata<proto::PageTopicsModelMetadata>(
- *registered_model_metadata);
- EXPECT_TRUE(page_topics_model_metadata.has_value());
- EXPECT_EQ(page_topics_model_metadata->supported_output_size(), 2);
- EXPECT_THAT(
- page_topics_model_metadata->supported_output(),
- UnorderedElementsAre(proto::PAGE_TOPICS_SUPPORTED_OUTPUT_VISIBILITY,
- proto::PAGE_TOPICS_SUPPORTED_OUTPUT_CATEGORIES));
-
- // The feature param did not specify page entities, so we expect for it to not
- // be requested.
- histogram_tester()->ExpectTotalCount(
- "OptimizationGuide.PageContentAnnotationsModelManager."
- "PageEntitiesModelRequested",
- 0);
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetPageTopicsModelVersionNoPushedModel) {
- EXPECT_FALSE(model_manager()->GetPageTopicsModelVersion().has_value());
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetPageTopicsModelVersionFromExecutor) {
- proto::Any any_metadata;
- any_metadata.set_type_url(
- "type.googleapis.com/com.foo.PageTopicsModelMetadata");
- proto::PageTopicsModelMetadata page_topics_model_metadata;
- page_topics_model_metadata.set_version(123);
- page_topics_model_metadata.SerializeToString(any_metadata.mutable_value());
- SendPageTopicsModelToExecutor(any_metadata);
-
- absl::optional<int64_t> model_version =
- model_manager()->GetPageTopicsModelVersion();
- EXPECT_TRUE(model_version.has_value());
- EXPECT_EQ(model_version.value(), 123);
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetPageTopicsModelVersionFromExecutorBadMetadata) {
- proto::Any any_metadata;
- any_metadata.set_type_url(
- "type.googleapis.com/com.foo.whatevernotpagetopics");
- any_metadata.set_value("123");
- SendPageTopicsModelToExecutor(any_metadata);
-
- absl::optional<int64_t> model_version =
- model_manager()->GetPageTopicsModelVersion();
- EXPECT_FALSE(model_version.has_value());
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetContentModelAnnotationsFromOutputVisibilityOnly) {
- proto::PageTopicsModelMetadata model_metadata;
- model_metadata.set_version(123);
- model_metadata.mutable_output_postprocessing_params()
- ->mutable_visibility_params()
- ->set_category_name("SOMECATEGORY");
-
- std::vector<tflite::task::core::Category> model_output = {
- {"SOMECATEGORY", 0.4},
- {"-2", 0.3},
- };
- history::VisitContentModelAnnotations annotations =
- GetContentModelAnnotationsFromOutput(model_metadata, model_output);
- EXPECT_TRUE(annotations.categories.empty());
- EXPECT_THAT(annotations.visibility_score, FloatEq(0.6));
- EXPECT_EQ(annotations.page_topics_model_version, 123);
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetContentModelAnnotationsFromOutputVisibilityOnlyCategoryNotInOutput) {
- proto::PageTopicsModelMetadata model_metadata;
- model_metadata.set_version(123);
- model_metadata.mutable_output_postprocessing_params()
- ->mutable_visibility_params()
- ->set_category_name("SOMECATEGORY");
-
- std::vector<tflite::task::core::Category> model_output = {
- {"-2", 0.3},
- };
- history::VisitContentModelAnnotations annotations =
- GetContentModelAnnotationsFromOutput(model_metadata, model_output);
- EXPECT_TRUE(annotations.categories.empty());
- EXPECT_EQ(annotations.visibility_score, -1.0);
- EXPECT_EQ(annotations.page_topics_model_version, 123);
- EXPECT_TRUE(annotations.entities.empty());
-}
-
-TEST_F(
- PageContentAnnotationsModelManagerTest,
- GetContentModelAnnotationsFromOutputNonNumericAndLowWeightCategoriesPruned) {
- proto::PageTopicsModelMetadata model_metadata;
- model_metadata.set_version(123);
- auto* category_params = model_metadata.mutable_output_postprocessing_params()
- ->mutable_category_params();
- category_params->set_max_categories(4);
- category_params->set_min_none_weight(0.8);
- category_params->set_min_category_weight(0.01);
- category_params->set_min_normalized_weight_within_top_n(0.1);
-
- std::vector<tflite::task::core::Category> model_output = {
- {"0", 0.0001}, {"1", 0.1}, {"SOMECATEGORY", 0.9}, {"2", 0.2}, {"3", 0.3},
- };
- history::VisitContentModelAnnotations annotations =
- GetContentModelAnnotationsFromOutput(model_metadata, model_output);
- EXPECT_THAT(annotations.categories,
- UnorderedElementsAre(
- history::VisitContentModelAnnotations::Category("1", 10),
- history::VisitContentModelAnnotations::Category("2", 20),
- history::VisitContentModelAnnotations::Category("3", 30)));
- EXPECT_EQ(annotations.visibility_score, -1.0);
- EXPECT_EQ(annotations.page_topics_model_version, 123);
- EXPECT_TRUE(annotations.entities.empty());
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetContentModelAnnotationsFromOutputNoneWeightTooStrong) {
- proto::PageTopicsModelMetadata model_metadata;
- model_metadata.set_version(123);
- auto* category_params = model_metadata.mutable_output_postprocessing_params()
- ->mutable_category_params();
- category_params->set_max_categories(4);
- category_params->set_min_none_weight(0.1);
- category_params->set_min_category_weight(0.01);
- category_params->set_min_normalized_weight_within_top_n(0.1);
-
- std::vector<tflite::task::core::Category> model_output = {
- {"-2", 0.9999},
- {"0", 0.3},
- {"1", 0.2},
- };
- history::VisitContentModelAnnotations annotations =
- GetContentModelAnnotationsFromOutput(model_metadata, model_output);
- EXPECT_TRUE(annotations.categories.empty());
- EXPECT_EQ(annotations.visibility_score, -1.0);
- EXPECT_EQ(annotations.page_topics_model_version, 123);
- EXPECT_TRUE(annotations.entities.empty());
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetContentModelAnnotationsFromOutputNoneInTopButNotStrongSoPruned) {
- proto::PageTopicsModelMetadata model_metadata;
- model_metadata.set_version(123);
- auto* category_params = model_metadata.mutable_output_postprocessing_params()
- ->mutable_category_params();
- category_params->set_max_categories(4);
- category_params->set_min_none_weight(0.8);
- category_params->set_min_category_weight(0.01);
- category_params->set_min_normalized_weight_within_top_n(0.1);
-
- std::vector<tflite::task::core::Category> model_output = {
- {"-2", 0.1}, {"0", 0.3}, {"1", 0.2}, {"2", 0.4}, {"3", 0.05},
- };
- history::VisitContentModelAnnotations annotations =
- GetContentModelAnnotationsFromOutput(model_metadata, model_output);
- EXPECT_THAT(annotations.categories,
- UnorderedElementsAre(
- history::VisitContentModelAnnotations::Category("0", 30),
- history::VisitContentModelAnnotations::Category("1", 20),
- history::VisitContentModelAnnotations::Category("2", 40)));
- EXPECT_EQ(annotations.visibility_score, -1.0);
- EXPECT_EQ(annotations.page_topics_model_version, 123);
- EXPECT_TRUE(annotations.entities.empty());
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetContentModelAnnotationsFromOutputPrunedAfterNormalization) {
- proto::PageTopicsModelMetadata model_metadata;
- model_metadata.set_version(123);
- auto* category_params = model_metadata.mutable_output_postprocessing_params()
- ->mutable_category_params();
- category_params->set_max_categories(4);
- category_params->set_min_none_weight(0.8);
- category_params->set_min_category_weight(0.01);
- category_params->set_min_normalized_weight_within_top_n(0.25);
-
- std::vector<tflite::task::core::Category> model_output = {
- {"0", 0.3},
- {"1", 0.25},
- {"2", 0.4},
- {"3", 0.05},
- };
- history::VisitContentModelAnnotations annotations =
- GetContentModelAnnotationsFromOutput(model_metadata, model_output);
- EXPECT_THAT(annotations.categories,
- UnorderedElementsAre(
- history::VisitContentModelAnnotations::Category("0", 30),
- history::VisitContentModelAnnotations::Category("1", 25),
- history::VisitContentModelAnnotations::Category("2", 40)));
- EXPECT_EQ(annotations.visibility_score, -1.0);
- EXPECT_EQ(annotations.page_topics_model_version, 123);
- EXPECT_TRUE(annotations.entities.empty());
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetContentModelAnnotationsFromOutputNoneCategoryBelowMin) {
- proto::PageTopicsModelMetadata model_metadata;
- model_metadata.set_version(123);
- auto* category_params = model_metadata.mutable_output_postprocessing_params()
- ->mutable_category_params();
- category_params->set_max_categories(4);
- category_params->set_min_none_weight(0.8);
- category_params->set_min_category_weight(0.01);
- category_params->set_min_normalized_weight_within_top_n(0.25);
-
- std::vector<tflite::task::core::Category> model_output = {
- {"-2", 0.001}, {"0", 0.3}, {"1", 0.25}, {"2", 0.4}, {"3", 0.05},
- };
- history::VisitContentModelAnnotations annotations =
- GetContentModelAnnotationsFromOutput(model_metadata, model_output);
- EXPECT_THAT(annotations.categories,
- UnorderedElementsAre(
- history::VisitContentModelAnnotations::Category("0", 30),
- history::VisitContentModelAnnotations::Category("1", 25),
- history::VisitContentModelAnnotations::Category("2", 40)));
- EXPECT_EQ(annotations.visibility_score, -1.0);
- EXPECT_EQ(annotations.page_topics_model_version, 123);
- EXPECT_TRUE(annotations.entities.empty());
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- GetContentModelAnnotationsFromOutputCategoriesAndVisibility) {
- proto::PageTopicsModelMetadata model_metadata;
- model_metadata.set_version(123);
- auto* category_params = model_metadata.mutable_output_postprocessing_params()
- ->mutable_category_params();
- category_params->set_max_categories(4);
- category_params->set_min_none_weight(0.8);
- category_params->set_min_category_weight(0.01);
- category_params->set_min_normalized_weight_within_top_n(0.25);
- model_metadata.mutable_output_postprocessing_params()
- ->mutable_visibility_params()
- ->set_category_name("SOMECATEGORY");
-
- std::vector<tflite::task::core::Category> model_output = {
- {"0", 0.3}, {"1", 0.25}, {"2", 0.4}, {"3", 0.05}, {"SOMECATEGORY", 0.4},
- };
- history::VisitContentModelAnnotations annotations =
- GetContentModelAnnotationsFromOutput(model_metadata, model_output);
- EXPECT_THAT(annotations.categories,
- UnorderedElementsAre(
- history::VisitContentModelAnnotations::Category("0", 30),
- history::VisitContentModelAnnotations::Category("1", 25),
- history::VisitContentModelAnnotations::Category("2", 40)));
- EXPECT_THAT(annotations.visibility_score, FloatEq(0.6));
- EXPECT_EQ(annotations.page_topics_model_version, 123);
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
- AnnotateNoModelsFinishedExecuting) {
- proto::Any any_metadata;
- any_metadata.set_type_url(
- "type.googleapis.com/com.foo.PageTopicsModelMetadata");
- proto::PageTopicsModelMetadata page_topics_model_metadata;
- page_topics_model_metadata.set_version(123);
- page_topics_model_metadata.SerializeToString(any_metadata.mutable_value());
- SendPageTopicsModelToExecutor(any_metadata);
-
- EXPECT_FALSE(Annotate("sometext").has_value());
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest, AnnotateModelNotAvailable) {
- EXPECT_FALSE(Annotate("sometext").has_value());
-}
-
-TEST_F(PageContentAnnotationsModelManagerTest,
GetMetadataForEntityIdEntitiesAnnotatorNotInitialized) {
EXPECT_FALSE(GetMetadataForEntityId("someid").has_value());
}
-// TODO(crbug.com/1286473): Flaky on Chrome OS.
-#if BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_BatchAnnotate_PageTopics DISABLED_BatchAnnotate_PageTopics
-#else
-#define MAYBE_BatchAnnotate_PageTopics BatchAnnotate_PageTopics
-#endif
-TEST_F(PageContentAnnotationsModelManagerTest, MAYBE_BatchAnnotate_PageTopics) {
+TEST_F(PageContentAnnotationsModelManagerTest, PageTopics) {
SetupPageTopicsV2ModelExecutor();
- // Running the actual model can take a while.
- base::test::ScopedRunLoopTimeout scoped_timeout(FROM_HERE, base::Seconds(60));
-
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
std::vector<BatchAnnotationResult> result;
@@ -620,18 +263,15 @@ TEST_F(PageContentAnnotationsModelManagerTest, MAYBE_BatchAnnotate_PageTopics) {
AnnotationType::kPageTopics);
run_loop.Run();
- RetryForHistogramUntilCountReached(
- &histogram_tester,
- "OptimizationGuide.ModelExecutor.ExecutionStatus.PageTopicsV2", 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ModelExecutor.ExecutionStatus.PageTopicsV2",
- ExecutionStatus::kSuccess, 1);
+ ExecutionStatus::kErrorModelFileNotValid, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageContentAnnotations.BatchRequestedSize.PageTopics",
1, 1);
histogram_tester.ExpectUniqueSample(
- "OptimizationGuide.PageContentAnnotations.BatchSuccess.PageTopics", true,
+ "OptimizationGuide.PageContentAnnotations.BatchSuccess.PageTopics", false,
1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageContentAnnotations.JobExecutionTime.PageTopics",
@@ -645,13 +285,12 @@ TEST_F(PageContentAnnotationsModelManagerTest, MAYBE_BatchAnnotate_PageTopics) {
ASSERT_EQ(result.size(), 1U);
EXPECT_EQ(result[0].input(), "input");
EXPECT_EQ(result[0].type(), AnnotationType::kPageTopics);
- EXPECT_NE(result[0].topics(), absl::nullopt);
+ EXPECT_EQ(result[0].topics(), absl::nullopt);
EXPECT_EQ(result[0].entities(), absl::nullopt);
EXPECT_EQ(result[0].visibility_score(), absl::nullopt);
}
-TEST_F(PageContentAnnotationsModelManagerTest,
- BatchAnnotate_PageTopicsDisabled) {
+TEST_F(PageContentAnnotationsModelManagerTest, PageTopicsDisabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
features::kPageTopicsBatchAnnotations);
@@ -699,7 +338,25 @@ TEST_F(PageContentAnnotationsModelManagerTest,
EXPECT_EQ(result[0].visibility_score(), absl::nullopt);
}
-TEST_F(PageContentAnnotationsModelManagerTest, BatchAnnotate_PageEntities) {
+TEST_F(PageContentAnnotationsModelManagerTest, PageEntities) {
+ std::vector<ScoredEntityMetadata> input1_entities = {
+ ScoredEntityMetadata(0.5, EntityMetadata("cat", "cat", {})),
+ ScoredEntityMetadata(0.6, EntityMetadata("dog", "dog", {})),
+ };
+ std::vector<ScoredEntityMetadata> input2_entities = {
+ ScoredEntityMetadata(0.7, EntityMetadata("fish", "fish", {})),
+ };
+ SetPageEntitiesModelExecutor(
+ {
+ {"input1", input1_entities},
+ {"input2", input2_entities},
+ {"other input",
+ {
+ ScoredEntityMetadata(0.7, EntityMetadata("other", "other", {})),
+ }},
+ },
+ /*entity_metadata=*/{});
+
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
std::vector<BatchAnnotationResult> result;
@@ -712,15 +369,15 @@ TEST_F(PageContentAnnotationsModelManagerTest, BatchAnnotate_PageEntities) {
},
&run_loop, &result);
- model_manager()->Annotate(std::move(callback), {"input"},
+ model_manager()->Annotate(std::move(callback), {"input1", "input2"},
AnnotationType::kPageEntities);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageContentAnnotations.BatchRequestedSize."
"PageEntities",
- 1, 1);
+ 2, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageContentAnnotations.BatchSuccess.PageEntities",
- false, 1);
+ true, 1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageContentAnnotations.JobExecutionTime.PageEntities",
1);
@@ -730,21 +387,22 @@ TEST_F(PageContentAnnotationsModelManagerTest, BatchAnnotate_PageEntities) {
run_loop.Run();
- ASSERT_EQ(result.size(), 1U);
- EXPECT_EQ(result[0].input(), "input");
+ ASSERT_EQ(result.size(), 2U);
+
+ EXPECT_EQ(result[0].input(), "input1");
EXPECT_EQ(result[0].topics(), absl::nullopt);
- EXPECT_EQ(result[0].entities(), absl::nullopt);
+ EXPECT_EQ(result[0].entities(), absl::make_optional(input1_entities));
EXPECT_EQ(result[0].visibility_score(), absl::nullopt);
+ EXPECT_EQ(result[0].type(), AnnotationType::kPageEntities);
+
+ EXPECT_EQ(result[1].input(), "input2");
+ EXPECT_EQ(result[1].topics(), absl::nullopt);
+ EXPECT_EQ(result[1].entities(), absl::make_optional(input2_entities));
+ EXPECT_EQ(result[1].visibility_score(), absl::nullopt);
+ EXPECT_EQ(result[1].type(), AnnotationType::kPageEntities);
}
-// TODO(crbug.com/1286473): Flaky on Chrome OS.
-#if BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_BatchAnnotate_PageVisibility DISABLED_BatchAnnotate_PageVisibility
-#else
-#define MAYBE_BatchAnnotate_PageVisibility BatchAnnotate_PageVisibility
-#endif
-TEST_F(PageContentAnnotationsModelManagerTest,
- MAYBE_BatchAnnotate_PageVisibility) {
+TEST_F(PageContentAnnotationsModelManagerTest, PageVisibility) {
base::HistogramTester histogram_tester;
proto::Any any_metadata;
any_metadata.set_type_url(
@@ -768,9 +426,6 @@ TEST_F(PageContentAnnotationsModelManagerTest,
},
&run_loop, &result);
- // Running the actual model can take a while.
- base::test::ScopedRunLoopTimeout scoped_timeout(FROM_HERE, base::Seconds(60));
-
model_manager()->Annotate(std::move(callback), {"input"},
AnnotationType::kContentVisibility);
run_loop.Run();
@@ -783,7 +438,7 @@ TEST_F(PageContentAnnotationsModelManagerTest,
1, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageContentAnnotations.BatchSuccess.ContentVisibility",
- true, 1);
+ false, 1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageContentAnnotations.JobExecutionTime."
"ContentVisibility",
@@ -797,11 +452,10 @@ TEST_F(PageContentAnnotationsModelManagerTest,
EXPECT_EQ(result[0].input(), "input");
EXPECT_EQ(result[0].topics(), absl::nullopt);
EXPECT_EQ(result[0].entities(), absl::nullopt);
- EXPECT_EQ(result[0].visibility_score(), absl::make_optional(-1.0));
+ EXPECT_EQ(result[0].visibility_score(), absl::nullopt);
}
-TEST_F(PageContentAnnotationsModelManagerTest,
- BatchAnnotate_PageVisibilityDisabled) {
+TEST_F(PageContentAnnotationsModelManagerTest, PageVisibilityDisabled) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
@@ -858,22 +512,11 @@ TEST_F(PageContentAnnotationsModelManagerTest,
EXPECT_EQ(result[0].visibility_score(), absl::nullopt);
}
-// TODO(crbug.com/1286473): Flaky on Chrome OS.
-#if BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_BatchAnnotate_CalledTwice DISABLED_BatchAnnotate_CalledTwice
-#else
-#define MAYBE_BatchAnnotate_CalledTwice BatchAnnotate_CalledTwice
-#endif
-TEST_F(PageContentAnnotationsModelManagerTest,
- MAYBE_BatchAnnotate_CalledTwice) {
+TEST_F(PageContentAnnotationsModelManagerTest, CalledTwice) {
SetupPageTopicsV2ModelExecutor();
base::HistogramTester histogram_tester;
- // Running the actual model can take a while.
- base::test::ScopedRunLoopTimeout scoped_timeout(FROM_HERE,
- base::Seconds(120));
-
base::RunLoop run_loop1;
std::vector<BatchAnnotationResult> result1;
BatchAnnotationCallback callback1 = base::BindOnce(
@@ -912,7 +555,7 @@ TEST_F(PageContentAnnotationsModelManagerTest,
"OptimizationGuide.PageContentAnnotations.BatchRequestedSize.PageTopics",
1, 2);
histogram_tester.ExpectUniqueSample(
- "OptimizationGuide.PageContentAnnotations.BatchSuccess.PageTopics", true,
+ "OptimizationGuide.PageContentAnnotations.BatchSuccess.PageTopics", false,
2);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageContentAnnotations.JobExecutionTime.PageTopics",
@@ -923,18 +566,18 @@ TEST_F(PageContentAnnotationsModelManagerTest,
// The model should have only been loaded once and then used for both jobs.
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ModelExecutor.ModelAvailableToLoad.PageTopicsV2", true,
- 1);
+ 2);
ASSERT_EQ(result1.size(), 1U);
EXPECT_EQ(result1[0].input(), "input1");
EXPECT_EQ(result1[0].type(), AnnotationType::kPageTopics);
- EXPECT_NE(result1[0].topics(), absl::nullopt);
+ EXPECT_EQ(result1[0].topics(), absl::nullopt);
EXPECT_EQ(result1[0].entities(), absl::nullopt);
EXPECT_EQ(result1[0].visibility_score(), absl::nullopt);
ASSERT_EQ(result2.size(), 1U);
EXPECT_EQ(result2[0].input(), "input2");
EXPECT_EQ(result2[0].type(), AnnotationType::kPageTopics);
- EXPECT_NE(result2[0].topics(), absl::nullopt);
+ EXPECT_EQ(result2[0].topics(), absl::nullopt);
EXPECT_EQ(result2[0].entities(), absl::nullopt);
EXPECT_EQ(result2[0].visibility_score(), absl::nullopt);
}
@@ -1015,6 +658,27 @@ TEST_F(PageContentAnnotationsModelManagerTest,
EXPECT_TRUE(visibility_callback_success);
}
+TEST_F(PageContentAnnotationsModelManagerTest,
+ NotifyWhenModelAvailable_EntitiesOnly) {
+ SetPageEntitiesModelExecutor(/*entries=*/{}, /*entity_metadata=*/{});
+
+ base::RunLoop run_loop;
+ bool success = false;
+
+ model_manager()->RequestAndNotifyWhenModelAvailable(
+ AnnotationType::kPageEntities,
+ base::BindOnce(
+ [](base::RunLoop* run_loop, bool* out_success, bool success) {
+ *out_success = success;
+ run_loop->Quit();
+ },
+ &run_loop, &success));
+
+ run_loop.Run();
+
+ EXPECT_TRUE(success);
+}
+
TEST_F(PageContentAnnotationsModelManagerTest, NotifyWhenModelAvailable_Both) {
proto::Any any_metadata;
any_metadata.set_type_url(
@@ -1058,76 +722,7 @@ TEST_F(PageContentAnnotationsModelManagerTest, NotifyWhenModelAvailable_Both) {
EXPECT_TRUE(visibility_callback_success);
}
-class PageContentAnnotationsModelManagerEntitiesOnlyTest
- : public PageContentAnnotationsModelManagerTest {
- public:
- PageContentAnnotationsModelManagerEntitiesOnlyTest() {
- scoped_feature_list_.InitWithFeatures(
- {features::kPageEntitiesPageContentAnnotations},
- {features::kPageVisibilityPageContentAnnotations});
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(PageContentAnnotationsModelManagerEntitiesOnlyTest,
- SetsUpModelsCorrectlyBasedOnFeatureParams) {
- // The feature param did not specify page topics, so we expect for it to not
- // be requested.
- absl::optional<proto::Any> registered_model_metadata;
- EXPECT_FALSE(model_observer_tracker()->DidRegisterForTarget(
- proto::OPTIMIZATION_TARGET_PAGE_TOPICS, &registered_model_metadata));
-
- // But it did specify page entities.
- histogram_tester()->ExpectUniqueSample(
- "OptimizationGuide.PageContentAnnotationsModelManager."
- "PageEntitiesModelRequested",
- true, 1);
-}
-
-TEST_F(PageContentAnnotationsModelManagerEntitiesOnlyTest,
- AnnotateNoModelsFinishedExecuting) {
- SetPageEntitiesModelExecutor(
- {{"sometext",
- {
- ScoredEntityMetadata(0.6, EntityMetadata("entity6", "entity6", {})),
- ScoredEntityMetadata(0.5, EntityMetadata("entity5", "entity5", {})),
- ScoredEntityMetadata(0.4, EntityMetadata("entity4", "entity4", {})),
- ScoredEntityMetadata(0.3, EntityMetadata("entity3", "entity3", {})),
- ScoredEntityMetadata(0.2, EntityMetadata("entity2", "entity2", {})),
- }}},
- /*entity_metadata=*/{});
-
- absl::optional<history::VisitContentModelAnnotations> annotations =
- Annotate("sometext");
-
- histogram_tester()->ExpectUniqueSample(
- "OptimizationGuide.PageContentAnnotationsModelManager."
- "PageEntitiesModelExecutionRequested",
- true, 1);
-
- // We expect that the page topics model will not be requested for execution.
- histogram_tester()->ExpectTotalCount(
- "OptimizationGuide.PageContentAnnotationsModelManager."
- "PageTopicsModelExecutionRequested",
- 0);
-
- // Make sure annotations object is populated correctly.
- ASSERT_TRUE(annotations.has_value());
- EXPECT_TRUE(annotations->categories.empty());
- EXPECT_EQ(annotations->visibility_score, -1.0);
- EXPECT_THAT(
- annotations->entities,
- UnorderedElementsAre(
- history::VisitContentModelAnnotations::Category("entity6", 60),
- history::VisitContentModelAnnotations::Category("entity5", 50),
- history::VisitContentModelAnnotations::Category("entity4", 40),
- history::VisitContentModelAnnotations::Category("entity3", 30),
- history::VisitContentModelAnnotations::Category("entity2", 20)));
-}
-
-TEST_F(PageContentAnnotationsModelManagerEntitiesOnlyTest,
+TEST_F(PageContentAnnotationsModelManagerTest,
GetMetadataForEntityIdEntitiesAnnotatorInitialized) {
EntityMetadata entity_metadata;
entity_metadata.human_readable_name = "entity1";
@@ -1140,43 +735,4 @@ TEST_F(PageContentAnnotationsModelManagerEntitiesOnlyTest,
EXPECT_TRUE(GetMetadataForEntityId("entity1").has_value());
}
-class PageContentAnnotationsModelManagerMultipleModelsTest
- : public PageContentAnnotationsModelManagerTest {
- public:
- PageContentAnnotationsModelManagerMultipleModelsTest() {
- scoped_feature_list_.InitWithFeatures(
- {features::kPageEntitiesPageContentAnnotations,
- features::kPageVisibilityPageContentAnnotations},
- {});
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(PageContentAnnotationsModelManagerMultipleModelsTest,
- AnnotateRequestBothModels) {
- SetPageEntitiesModelExecutor(
- {{"sometext",
- {
- ScoredEntityMetadata(0.6, EntityMetadata("entity6", "entity6", {})),
- ScoredEntityMetadata(0.5, EntityMetadata("entity5", "entity5", {})),
- ScoredEntityMetadata(0.4, EntityMetadata("entity4", "entity4", {})),
- ScoredEntityMetadata(0.3, EntityMetadata("entity3", "entity3", {})),
- ScoredEntityMetadata(0.2, EntityMetadata("entity2", "entity2", {})),
- }}},
- /*entity_metadata=*/{});
-
- Annotate("sometext");
-
- histogram_tester()->ExpectUniqueSample(
- "OptimizationGuide.PageContentAnnotationsModelManager."
- "PageEntitiesModelExecutionRequested",
- true, 1);
-
- // We expect that the page topics model will also be requested for execution.
- histogram_tester()->ExpectTotalCount(
- "OptimizationGuide.PageContentAnnotationsService.ModelAvailable", 1);
-}
-
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_service.cc b/chromium/components/optimization_guide/content/browser/page_content_annotations_service.cc
index cd7fd07cc7e..92ae6edc33f 100644
--- a/chromium/components/optimization_guide/content/browser/page_content_annotations_service.cc
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_service.cc
@@ -4,17 +4,16 @@
#include "components/optimization_guide/content/browser/page_content_annotations_service.h"
+#include "base/barrier_closure.h"
#include "base/callback_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros_local.h"
#include "base/rand_util.h"
-#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/time/default_tick_clock.h"
-#include "base/timer/timer.h"
#include "components/history/core/browser/history_service.h"
#include "components/leveldb_proto/public/proto_database_provider.h"
+#include "components/optimization_guide/content/browser/page_content_annotations_validator.h"
#include "components/optimization_guide/core/local_page_entities_metadata_provider.h"
#include "components/optimization_guide/core/noisy_metrics_recorder.h"
#include "components/optimization_guide/core/optimization_guide_enums.h"
@@ -37,13 +36,39 @@ namespace optimization_guide {
namespace {
+// Keep this in sync with the PageContentAnnotationsStorageType variant in
+// ../optimization/histograms.xml.
+std::string PageContentAnnotationsTypeToString(
+ PageContentAnnotationsType annotation_type) {
+ switch (annotation_type) {
+ case PageContentAnnotationsType::kUnknown:
+ return "Unknown";
+ case PageContentAnnotationsType::kModelAnnotations:
+ return "ModelAnnotations";
+ case PageContentAnnotationsType::kRelatedSearches:
+ return "RelatedSearches";
+ case PageContentAnnotationsType::kSearchMetadata:
+ return "SearchMetadata";
+ case PageContentAnnotationsType::kRemoteMetdata:
+ return "RemoteMetadata";
+ }
+}
+
void LogPageContentAnnotationsStorageStatus(
- PageContentAnnotationsStorageStatus status) {
+ PageContentAnnotationsStorageStatus status,
+ PageContentAnnotationsType annotation_type) {
DCHECK_NE(status, PageContentAnnotationsStorageStatus::kUnknown);
+ DCHECK_NE(annotation_type, PageContentAnnotationsType::kUnknown);
base::UmaHistogramEnumeration(
"OptimizationGuide.PageContentAnnotationsService."
"ContentAnnotationsStorageStatus",
status);
+
+ base::UmaHistogramEnumeration(
+ "OptimizationGuide.PageContentAnnotationsService."
+ "ContentAnnotationsStorageStatus." +
+ PageContentAnnotationsTypeToString(annotation_type),
+ status);
}
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
@@ -80,14 +105,6 @@ void MaybeRecordVisibilityUKM(
}
#endif /* BUILDFLAG(BUILD_WITH_TFLITE_LIB) */
-const char* kRandomWords[] = {
- "interesting", "chunky", "maniacal", "tickle", "lettuce",
- "obsequious", "stir", "bless", "colossal", "squealing",
- "elegant", "ambitious", "eight", "frighten", "descriptive",
- "pretty", "curly", "regular", "uneven", "heap",
-};
-const size_t kCountRandomWords = 20;
-
} // namespace
PageContentAnnotationsService::PageContentAnnotationsService(
@@ -105,8 +122,21 @@ PageContentAnnotationsService::PageContentAnnotationsService(
history_service_ = history_service;
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
model_manager_ = std::make_unique<PageContentAnnotationsModelManager>(
- application_locale, optimization_guide_model_provider);
+ optimization_guide_model_provider);
annotator_ = model_manager_.get();
+
+ if (features::ShouldExecutePageVisibilityModelOnPageContent(
+ application_locale)) {
+ model_manager_->RequestAndNotifyWhenModelAvailable(
+ AnnotationType::kContentVisibility, base::DoNothing());
+ annotation_types_to_execute_.push_back(AnnotationType::kContentVisibility);
+ }
+ if (features::ShouldExecutePageEntitiesModelOnPageContent(
+ application_locale)) {
+ model_manager_->RequestAndNotifyWhenModelAvailable(
+ AnnotationType::kPageEntities, base::DoNothing());
+ annotation_types_to_execute_.push_back(AnnotationType::kPageEntities);
+ }
#endif
if (features::UseLocalPageEntitiesMetadataProvider()) {
@@ -116,22 +146,8 @@ PageContentAnnotationsService::PageContentAnnotationsService(
database_provider, database_dir, background_task_runner);
}
- if (features::BatchAnnotationsValidationEnabled()) {
- // Normally the caller would do this, but we are our own caller.
- RequestAndNotifyWhenModelAvailable(
- features::BatchAnnotationsValidationUsePageTopics()
- ? AnnotationType::kPageTopics
- : AnnotationType::kContentVisibility,
- base::DoNothing());
-
- validation_timer_ = std::make_unique<base::OneShotTimer>(
- base::DefaultTickClock::GetInstance());
- validation_timer_->Start(
- FROM_HERE, features::BatchAnnotationValidationStartupDelay(),
- base::BindRepeating(
- &PageContentAnnotationsService::RunBatchAnnotationValidation,
- weak_ptr_factory_.GetWeakPtr()));
- }
+ validator_ =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(annotator_);
}
PageContentAnnotationsService::~PageContentAnnotationsService() = default;
@@ -171,18 +187,19 @@ void PageContentAnnotationsService::Annotate(const HistoryVisit& visit) {
<< "Text: " << visit.text_to_annotate.value_or(std::string());
}
visits_to_annotate_.emplace_back(visit);
+
base::UmaHistogramBoolean(
"OptimizationGuide.PageContentAnnotations.AnnotateVisitResultCached",
false);
- if (visits_to_annotate_.size() >= features::AnnotateVisitBatchSize()) {
- if (current_visit_annotation_batch_.empty()) {
- // Used for testing.
- LOCAL_HISTOGRAM_BOOLEAN(
- "PageContentAnnotations.AnnotateVisit.BatchAnnotationStarted", true);
- current_visit_annotation_batch_ = std::move(visits_to_annotate_);
- AnnotateVisitBatch();
- return;
- }
+
+ if (MaybeStartAnnotateVisitBatch())
+ return;
+
+ // Used for testing.
+ LOCAL_HISTOGRAM_BOOLEAN(
+ "PageContentAnnotations.AnnotateVisit.AnnotationRequestQueued", true);
+
+ if (visits_to_annotate_.size() > features::AnnotateVisitBatchSize()) {
// The queue is full and an batch annotation is actively being done so
// we will remove the "oldest" visit.
visits_to_annotate_.erase(visits_to_annotate_.begin());
@@ -190,96 +207,135 @@ void PageContentAnnotationsService::Annotate(const HistoryVisit& visit) {
LOCAL_HISTOGRAM_BOOLEAN(
"PageContentAnnotations.AnnotateVisit.QueueFullVisitDropped", true);
}
- // Used for testing.
- LOCAL_HISTOGRAM_BOOLEAN(
- "PageContentAnnotations.AnnotateVisit.AnnotationRequestQueued", true);
#endif
}
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+bool PageContentAnnotationsService::MaybeStartAnnotateVisitBatch() {
+ bool is_full_batch_available =
+ visits_to_annotate_.size() >= features::AnnotateVisitBatchSize();
+ bool batch_already_running = !current_visit_annotation_batch_.empty();
+
+ if (is_full_batch_available && !batch_already_running) {
+ // Used for testing.
+ LOCAL_HISTOGRAM_BOOLEAN(
+ "PageContentAnnotations.AnnotateVisit.BatchAnnotationStarted", true);
+ current_visit_annotation_batch_ = std::move(visits_to_annotate_);
+ AnnotateVisitBatch();
+
+ return true;
+ }
+ return false;
+}
+
void PageContentAnnotationsService::AnnotateVisitBatch() {
DCHECK(!current_visit_annotation_batch_.empty());
- if (switches::StopHistoryVisitBatchAnnotateForTesting()) {
- // Code beyond this is tested in multiple places. This just ensures the
- // calls up to this point can be more easily configured.
- return;
+ std::vector<std::string> inputs;
+ for (const HistoryVisit& visit : current_visit_annotation_batch_) {
+ DCHECK(visit.text_to_annotate);
+ inputs.push_back(*visit.text_to_annotate);
}
- if (current_visit_annotation_batch_.empty()) {
- return;
+ std::unique_ptr<
+ std::vector<absl::optional<history::VisitContentModelAnnotations>>>
+ merged_annotation_outputs = std::make_unique<
+ std::vector<absl::optional<history::VisitContentModelAnnotations>>>();
+ merged_annotation_outputs->reserve(inputs.size());
+ for (size_t i = 0; i < inputs.size(); i++) {
+ merged_annotation_outputs->push_back(absl::nullopt);
}
- auto visit = current_visit_annotation_batch_.back();
- DCHECK(visit.text_to_annotate);
- if (visit.text_to_annotate) {
- model_manager_->Annotate(
- *(visit.text_to_annotate),
- base::BindOnce(&PageContentAnnotationsService::OnBatchVisitAnnotated,
- weak_ptr_factory_.GetWeakPtr(), visit));
- }
-}
-void PageContentAnnotationsService::OnBatchVisitAnnotated(
- const HistoryVisit& visit,
- const absl::optional<history::VisitContentModelAnnotations>&
- content_annotations) {
- OnPageContentAnnotated(visit, content_annotations);
- DCHECK_EQ(visit.navigation_id,
- current_visit_annotation_batch_.back().navigation_id);
- current_visit_annotation_batch_.pop_back();
- if (!current_visit_annotation_batch_.empty()) {
- AnnotateVisitBatch();
+ std::vector<absl::optional<history::VisitContentModelAnnotations>>*
+ merged_annotation_outputs_ptr = merged_annotation_outputs.get();
+
+ base::RepeatingClosure barrier_closure = base::BarrierClosure(
+ annotation_types_to_execute_.size(),
+ base::BindOnce(&PageContentAnnotationsService::OnBatchVisitsAnnotated,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(merged_annotation_outputs)));
+
+ for (AnnotationType type : annotation_types_to_execute_) {
+ annotator_->Annotate(
+ base::BindOnce(
+ &PageContentAnnotationsService::OnAnnotationBatchComplete, type,
+ merged_annotation_outputs_ptr, barrier_closure),
+ inputs, type);
}
}
-#endif
-
-void PageContentAnnotationsService::OverridePageContentAnnotatorForTesting(
- PageContentAnnotator* annotator) {
- annotator_ = annotator;
-}
// static
-std::string PageContentAnnotationsService::StringInputForPageTopicsHost(
- const std::string& host) {
- std::string output = base::ToLowerASCII(host);
+void PageContentAnnotationsService::OnAnnotationBatchComplete(
+ AnnotationType type,
+ std::vector<absl::optional<history::VisitContentModelAnnotations>>*
+ merge_to_output,
+ base::OnceClosure signal_merge_complete_callback,
+ const std::vector<BatchAnnotationResult>& batch_result) {
+ DCHECK_EQ(merge_to_output->size(), batch_result.size());
+ for (size_t i = 0; i < batch_result.size(); i++) {
+ const BatchAnnotationResult result = batch_result[i];
+ DCHECK_EQ(type, result.type());
+
+ if (!result.HasOutputForType())
+ continue;
- // Strip the 'www.' if it exists.
- if (base::StartsWith(output, "www.")) {
- output = output.substr(4);
- }
+ history::VisitContentModelAnnotations current_annotations;
+
+ if (type == AnnotationType::kContentVisibility) {
+ DCHECK(result.visibility_score());
+ current_annotations.visibility_score = *result.visibility_score();
+ }
+
+ if (type == AnnotationType::kPageEntities) {
+ DCHECK(result.entities());
+ for (const ScoredEntityMetadata& scored_md : *result.entities()) {
+ DCHECK(scored_md.score >= 0.0 && scored_md.score <= 1.0);
+ history::VisitContentModelAnnotations::Category category(
+ scored_md.metadata.entity_id,
+ static_cast<int>(100 * scored_md.score));
+ history::VisitContentModelAnnotations::MergeCategoryIntoVector(
+ category, &current_annotations.entities);
+ }
+ }
- const char kCharsToReplaceWithSpace[] = {'-', '_', '.', '+'};
- for (char c : kCharsToReplaceWithSpace) {
- std::replace(output.begin(), output.end(), c, ' ');
+ history::VisitContentModelAnnotations previous_annotations =
+ merge_to_output->at(i).value_or(
+ history::VisitContentModelAnnotations());
+ current_annotations.MergeFrom(previous_annotations);
+
+ merge_to_output->at(i) = current_annotations;
}
- return output;
+ // This needs to be ran last because |merge_to_output| may be deleted when
+ // run.
+ std::move(signal_merge_complete_callback).Run();
}
-void PageContentAnnotationsService::BatchAnnotatePageTopics(
- BatchAnnotationCallback callback,
- const std::vector<std::string>& hosts) {
- std::vector<std::string> tokenized_hosts;
- for (const std::string& host : hosts) {
- tokenized_hosts.emplace_back(StringInputForPageTopicsHost(host));
+void PageContentAnnotationsService::OnBatchVisitsAnnotated(
+ std::unique_ptr<
+ std::vector<absl::optional<history::VisitContentModelAnnotations>>>
+ merged_annotation_outputs) {
+ DCHECK_EQ(merged_annotation_outputs->size(),
+ current_visit_annotation_batch_.size());
+ for (size_t i = 0; i < merged_annotation_outputs->size(); i++) {
+ OnPageContentAnnotated(current_visit_annotation_batch_[i],
+ merged_annotation_outputs->at(i));
}
- if (!annotator_) {
- std::move(callback).Run(CreateEmptyBatchAnnotationResults(tokenized_hosts));
- return;
- }
+ current_visit_annotation_batch_.clear();
+ MaybeStartAnnotateVisitBatch();
+}
+#endif
- annotator_->Annotate(std::move(callback), tokenized_hosts,
- AnnotationType::kPageTopics);
+void PageContentAnnotationsService::OverridePageContentAnnotatorForTesting(
+ PageContentAnnotator* annotator) {
+ annotator_ = annotator;
}
void PageContentAnnotationsService::BatchAnnotate(
BatchAnnotationCallback callback,
const std::vector<std::string>& inputs,
AnnotationType annotation_type) {
- DCHECK_NE(annotation_type, AnnotationType::kPageTopics)
- << "Please use |BatchAnnotatePageTopics| instead";
-
if (!annotator_) {
std::move(callback).Run(CreateEmptyBatchAnnotationResults(inputs));
return;
@@ -315,7 +371,8 @@ void PageContentAnnotationsService::PersistSearchMetadata(
base::BindOnce(&history::HistoryService::AddSearchMetadataForVisit,
history_service_->AsWeakPtr(),
search_metadata.normalized_url,
- search_metadata.search_terms));
+ search_metadata.search_terms),
+ PageContentAnnotationsType::kSearchMetadata);
}
void PageContentAnnotationsService::ExtractRelatedSearches(
@@ -350,7 +407,8 @@ void PageContentAnnotationsService::OnPageContentAnnotated(
QueryURL(visit,
base::BindOnce(
&history::HistoryService::AddContentModelAnnotationsForVisit,
- history_service_->AsWeakPtr(), *content_annotations));
+ history_service_->AsWeakPtr(), *content_annotations),
+ PageContentAnnotationsType::kModelAnnotations);
}
#endif
@@ -393,34 +451,44 @@ void PageContentAnnotationsService::OnRelatedSearchesExtracted(
QueryURL(visit,
base::BindOnce(&history::HistoryService::AddRelatedSearchesForVisit,
- history_service_->AsWeakPtr(), related_searches));
+ history_service_->AsWeakPtr(), related_searches),
+ PageContentAnnotationsType::kRelatedSearches);
}
void PageContentAnnotationsService::QueryURL(
const HistoryVisit& visit,
- PersistAnnotationsCallback callback) {
+ PersistAnnotationsCallback callback,
+ PageContentAnnotationsType annotation_type) {
history_service_->QueryURL(
visit.url, /*want_visits=*/true,
base::BindOnce(&PageContentAnnotationsService::OnURLQueried,
- weak_ptr_factory_.GetWeakPtr(), visit,
- std::move(callback)),
+ weak_ptr_factory_.GetWeakPtr(), visit, std::move(callback),
+ annotation_type),
&history_service_task_tracker_);
}
void PageContentAnnotationsService::OnURLQueried(
const HistoryVisit& visit,
PersistAnnotationsCallback callback,
+ PageContentAnnotationsType annotation_type,
history::QueryURLResult url_result) {
if (!url_result.success) {
LogPageContentAnnotationsStorageStatus(
- PageContentAnnotationsStorageStatus::kNoVisitsForUrl);
+ PageContentAnnotationsStorageStatus::kNoVisitsForUrl, annotation_type);
return;
}
+ base::TimeDelta min_magnitude_between_visits = base::TimeDelta::Max();
bool did_store_content_annotations = false;
for (const auto& visit_for_url : url_result.visits) {
- if (visit.nav_entry_timestamp != visit_for_url.visit_time)
+ if (visit.nav_entry_timestamp != visit_for_url.visit_time) {
+ base::TimeDelta magnitude_between_visits =
+ (visit.nav_entry_timestamp - visit_for_url.visit_time).magnitude();
+ if (magnitude_between_visits < min_magnitude_between_visits) {
+ min_magnitude_between_visits = magnitude_between_visits;
+ }
continue;
+ }
std::move(callback).Run(visit_for_url.visit_id);
@@ -428,7 +496,21 @@ void PageContentAnnotationsService::OnURLQueried(
break;
}
LogPageContentAnnotationsStorageStatus(
- did_store_content_annotations ? kSuccess : kSpecificVisitForUrlNotFound);
+ did_store_content_annotations ? kSuccess : kSpecificVisitForUrlNotFound,
+ annotation_type);
+ if (!did_store_content_annotations) {
+ DCHECK_NE(min_magnitude_between_visits, base::TimeDelta::Max());
+ base::UmaHistogramTimes(
+ "OptimizationGuide.PageContentAnnotationsService."
+ "ContentAnnotationsStorageMinMagnitudeForVisitNotFound",
+ min_magnitude_between_visits);
+
+ base::UmaHistogramTimes(
+ "OptimizationGuide.PageContentAnnotationsService."
+ "ContentAnnotationsStorageMinMagnitudeForVisitNotFound." +
+ PageContentAnnotationsTypeToString(annotation_type),
+ min_magnitude_between_visits);
+ }
}
void PageContentAnnotationsService::GetMetadataForEntityId(
@@ -457,33 +539,22 @@ void PageContentAnnotationsService::PersistRemotePageEntities(
QueryURL(history_visit,
base::BindOnce(
&history::HistoryService::AddContentModelAnnotationsForVisit,
- history_service_->AsWeakPtr(), annotations));
+ history_service_->AsWeakPtr(), annotations),
+ // Even though we are persisting remote page entities, we store
+ // these as an override to the model annotations.
+ PageContentAnnotationsType::kModelAnnotations);
}
-void PageContentAnnotationsService::RunBatchAnnotationValidation() {
- DCHECK(features::BatchAnnotationsValidationEnabled());
- DCHECK(validation_timer_);
- validation_timer_.reset();
-
- std::vector<std::string> dummy_inputs;
- dummy_inputs.reserve(features::BatchAnnotationsValidationBatchSize());
- for (size_t i = 0; i < features::BatchAnnotationsValidationBatchSize(); i++) {
- const char* word1 = kRandomWords[base::RandGenerator(kCountRandomWords)];
- const char* word2 = kRandomWords[base::RandGenerator(kCountRandomWords)];
- dummy_inputs.emplace_back(base::StringPrintf("%s-%s.com", word1, word2));
- }
-
- LOCAL_HISTOGRAM_COUNTS_100(
- "OptimizationGuide.PageContentAnnotationsService.ValidationRun",
- dummy_inputs.size());
-
- if (!features::BatchAnnotationsValidationUsePageTopics()) {
- BatchAnnotate(base::DoNothing(), dummy_inputs,
- AnnotationType::kContentVisibility);
+void PageContentAnnotationsService::PersistRemotePageMetadata(
+ const HistoryVisit& visit,
+ const proto::PageEntitiesMetadata& page_metadata) {
+ if (!page_metadata.has_alternative_title())
return;
- }
-
- BatchAnnotatePageTopics(base::DoNothing(), dummy_inputs);
+ QueryURL(visit,
+ base::BindOnce(&history::HistoryService::AddPageMetadataForVisit,
+ history_service_->AsWeakPtr(),
+ page_metadata.alternative_title()),
+ PageContentAnnotationsType::kRemoteMetdata);
}
// static
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_service.h b/chromium/components/optimization_guide/content/browser/page_content_annotations_service.h
index abba5693ff7..e9cac58aa03 100644
--- a/chromium/components/optimization_guide/content/browser/page_content_annotations_service.h
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_service.h
@@ -6,6 +6,7 @@
#define COMPONENTS_OPTIMIZATION_GUIDE_CONTENT_BROWSER_PAGE_CONTENT_ANNOTATIONS_SERVICE_H_
#include <string>
+#include <vector>
#include "base/callback_forward.h"
#include "base/containers/lru_cache.h"
@@ -29,13 +30,10 @@
#include "components/optimization_guide/core/model_info.h"
#include "components/optimization_guide/core/page_content_annotations_common.h"
#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
+#include "components/optimization_guide/proto/page_entities_metadata.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
-namespace base {
-class OneShotTimer;
-} // namespace base
-
namespace content {
class WebContents;
} // namespace content
@@ -53,8 +51,8 @@ namespace optimization_guide {
class LocalPageEntitiesMetadataProvider;
class OptimizationGuideModelProvider;
class PageContentAnnotationsModelManager;
-class PageContentAnnotationsServiceTest;
class PageContentAnnotationsServiceBrowserTest;
+class PageContentAnnotationsValidator;
class PageContentAnnotationsWebContentsObserver;
// The information used by HistoryService to identify a visit to a URL.
@@ -84,6 +82,20 @@ struct SearchMetadata {
std::u16string search_terms;
};
+// The type of page content annotations stored in the history database.
+enum class PageContentAnnotationsType {
+ kUnknown = 0,
+ // Results from executing the models on page content or annotations received
+ // from the remote Optimization Guide service.
+ kModelAnnotations = 1,
+ // Related searches for the Google Search Results page.
+ kRelatedSearches = 2,
+ // Metadata for "search-like" pages.
+ kSearchMetadata = 3,
+ // Metadata received from the remote Optimization Guide service.
+ kRemoteMetdata = 4,
+};
+
// A KeyedService that annotates page content.
class PageContentAnnotationsService : public KeyedService,
public EntityMetadataProvider {
@@ -108,11 +120,6 @@ class PageContentAnnotationsService : public KeyedService,
const std::vector<std::string>& inputs,
AnnotationType annotation_type);
- // Calls |BatchAnnotate| with pre-processing the hosts into tokens, all
- // specific to PageTopics.
- void BatchAnnotatePageTopics(BatchAnnotationCallback callback,
- const std::vector<std::string>& inputs);
-
// Requests that the given model for |type| be loaded in the background and
// then runs |callback| with true when the model is ready to execute. If the
// model is ready now, the callback is run immediately. If the model file will
@@ -135,35 +142,47 @@ class PageContentAnnotationsService : public KeyedService,
void OverridePageContentAnnotatorForTesting(PageContentAnnotator* annotator);
private:
- friend class PageContentAnnotationsServiceTest;
- static std::string StringInputForPageTopicsHost(const std::string& host);
-
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
- // Callback invoked when |visit| has been annotated.
+ // Callback invoked when a single |visit| has been annotated.
void OnPageContentAnnotated(
const HistoryVisit& visit,
const absl::optional<history::VisitContentModelAnnotations>&
content_annotations);
+ // Maybe calls |AnnotateVisitBatch| to start a new batch of content
+ // annotations. Returns true if a new batch is started. Returns false if a
+ // batch is already running, or if there batch queue is not full.
+ bool MaybeStartAnnotateVisitBatch();
+
// Runs the page annotation models available to |model_manager_| on all the
// visits within |current_visit_annotation_batch_|.
void AnnotateVisitBatch();
- // Callback run after the annotations for a |visit| of a batch has been
- // determined. |current_visit_annotation_batch_| is updated to remove
- // the annotated visit and will trigger the next visit to be annotated.
- void OnBatchVisitAnnotated(
- const HistoryVisit& visit,
- const absl::optional<history::VisitContentModelAnnotations>&
- content_annotations);
+ // Runs when a single annotation job of |type| is completed and |batch_result|
+ // can be merged into |merge_to_output|. |signal_merge_complete_callback|
+ // should be run last as it is a |base::BarrierClosure| that may trigger
+ // |OnBatchVisitsAnnotated| to run.
+ static void OnAnnotationBatchComplete(
+ AnnotationType type,
+ std::vector<absl::optional<history::VisitContentModelAnnotations>>*
+ merge_to_output,
+ base::OnceClosure signal_merge_complete_callback,
+ const std::vector<BatchAnnotationResult>& batch_result);
+
+ // Callback run after all annotation types in |annotation_types_to_execute_|
+ // for all of |current_visit_annotation_batch_| has been completed.
+ void OnBatchVisitsAnnotated(
+ std::unique_ptr<
+ std::vector<absl::optional<history::VisitContentModelAnnotations>>>
+ merged_annotation_outputs);
std::unique_ptr<PageContentAnnotationsModelManager> model_manager_;
#endif
- // The annotator to use for requests to |BatchAnnotate|. In prod, this is
- // simply |model_manager_.get()| but is set as a separate pointer here in
- // order to be override-able for testing.
+ // The annotator to use for requests to |BatchAnnotate| and |Annotate|. In
+ // prod, this is simply |model_manager_.get()| but is set as a separate
+ // pointer here in order to be override-able for testing.
raw_ptr<PageContentAnnotator> annotator_;
// Requests to annotate |text|, which is associated with |web_contents|.
@@ -210,22 +229,30 @@ class PageContentAnnotationsService : public KeyedService,
const std::vector<history::VisitContentModelAnnotations::Category>&
entities);
+ // Persist |page_metadata| for |visit| in |history_service_|.
+ //
+ // Virtualized for testing.
+ virtual void PersistRemotePageMetadata(
+ const HistoryVisit& visit,
+ const proto::PageEntitiesMetadata& page_metadata);
+
using PersistAnnotationsCallback = base::OnceCallback<void(history::VisitID)>;
// Queries |history_service| for all the visits to the visited URL of |visit|.
// |callback| will be invoked to write the bound content annotations to
- // |history_service| once the visits to the given URL have returned.
- void QueryURL(const HistoryVisit& visit, PersistAnnotationsCallback callback);
+ // |history_service| once the visits to the given URL have returned. The
+ // |annotation_type| of data to be stored in History Service is passed along
+ // for metrics purposes.
+ void QueryURL(const HistoryVisit& visit,
+ PersistAnnotationsCallback callback,
+ PageContentAnnotationsType annotation_type);
// Callback invoked when |history_service| has returned results for the visits
// to a URL. In turn invokes |callback| to write the bound content annotations
// to |history_service|.
void OnURLQueried(const HistoryVisit& visit,
PersistAnnotationsCallback callback,
+ PageContentAnnotationsType annotation_type,
history::QueryURLResult url_result);
- // Runs a batch annotation validation, that is calls |BatchAnnotate| with
- // dummy input and discards the output.
- void RunBatchAnnotationValidation();
-
// A metadata-only provider for page entities (as opposed to |model_manager_|
// which does both entity model execution and metadata providing) that uses a
// local database to provide the metadata for a given entity id. This is only
@@ -258,12 +285,16 @@ class PageContentAnnotationsService : public KeyedService,
// and annotations can be scheduled with minimal impact to browsing.
std::vector<HistoryVisit> visits_to_annotate_;
+ // The set of |AnnotationType|'s to run on each of |visits_to_annotate_|.
+ std::vector<AnnotationType> annotation_types_to_execute_;
+
// The batch of visits being annotated. If this is empty, it is assumed that
// no visits are actively be annotated and a new batch can be started.
std::vector<HistoryVisit> current_visit_annotation_batch_;
- // Is only ever set when the feature is enabled.
- std::unique_ptr<base::OneShotTimer> validation_timer_;
+ // Set during this' ctor if the corresponding command line or feature flags
+ // are set.
+ std::unique_ptr<PageContentAnnotationsValidator> validator_;
base::WeakPtrFactory<PageContentAnnotationsService> weak_ptr_factory_{this};
};
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc b/chromium/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc
deleted file mode 100644
index 5d493192a6f..00000000000
--- a/chromium/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2022 The Chromium 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/optimization_guide/content/browser/page_content_annotations_service.h"
-
-#include <string>
-#include <vector>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace optimization_guide {
-
-class PageContentAnnotationsServiceTest : public testing::Test {
- public:
- PageContentAnnotationsServiceTest() = default;
- ~PageContentAnnotationsServiceTest() override = default;
-
- std::string CallStringInputForPageTopicsHost(const std::string& host) {
- return PageContentAnnotationsService::StringInputForPageTopicsHost(host);
- }
-};
-
-TEST_F(PageContentAnnotationsServiceTest, PageTopicsHost) {
- std::vector<std::pair<std::string, std::string>> tests = {
- {"www.chromium.org", "chromium org"},
- {"foo-bar.com", "foo bar com"},
- {"foo_bar.com", "foo bar com"},
- {"cats.co.uk", "cats co uk"},
- {"cats+dogs.com", "cats dogs com"},
- {"www.foo-bar_.baz.com", "foo bar baz com"},
- {"www.foo-bar-baz.com", "foo bar baz com"},
- {"WwW.LOWER-CASE.com", "lower case com"},
- };
-
- for (const auto& test : tests) {
- std::string host = test.first;
- std::string expected = test.second;
- std::string got = CallStringInputForPageTopicsHost(host);
-
- EXPECT_EQ(expected, got) << host;
- }
-}
-
-} // namespace optimization_guide \ No newline at end of file
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_validator.cc b/chromium/components/optimization_guide/content/browser/page_content_annotations_validator.cc
new file mode 100644
index 00000000000..552977d199d
--- /dev/null
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_validator.cc
@@ -0,0 +1,139 @@
+// Copyright 2022 The Chromium 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/optimization_guide/content/browser/page_content_annotations_validator.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/rand_util.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/task/thread_pool.h"
+#include "base/time/default_tick_clock.h"
+#include "components/optimization_guide/content/browser/page_content_annotator.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
+#include "components/optimization_guide/core/optimization_guide_switches.h"
+#include "components/optimization_guide/core/page_content_annotations_common.h"
+
+namespace optimization_guide {
+
+namespace {
+
+const char* kRandomNouns[] = {
+ "Airplane", "Boat", "Book", "Dinosaur", "Earth",
+ "Football", "Fork", "Hummingbird", "Magic Wand", "Mailbox",
+ "Molecule", "Pizza", "Record Player", "Skeleton", "Soda",
+ "Sphere", "Strawberry", "Tiger", "Turkey", "Wolf",
+};
+const size_t kCountRandomNouns = 20;
+
+void LogLinesToFileOnBackgroundSequence(const base::FilePath& path,
+ const std::vector<std::string>& lines) {
+ std::string data = base::JoinString(lines, "\n") + "\n";
+ if (base::PathExists(path)) {
+ base::AppendToFile(path, data);
+ return;
+ }
+ base::WriteFile(path, data);
+}
+
+void MaybeLogAnnotationResultToFile(
+ const std::vector<BatchAnnotationResult>& results) {
+ absl::optional<base::FilePath> path =
+ switches::PageContentAnnotationsValidationWriteToFile();
+ if (!path) {
+ return;
+ }
+
+ std::vector<std::string> lines;
+ for (const BatchAnnotationResult& result : results) {
+ lines.push_back(result.ToJSON());
+ }
+
+ base::ThreadPool::PostTask(
+ FROM_HERE, {base::MayBlock()},
+ base::BindOnce(&LogLinesToFileOnBackgroundSequence, *path, lines));
+}
+
+} // namespace
+
+PageContentAnnotationsValidator::~PageContentAnnotationsValidator() = default;
+PageContentAnnotationsValidator::PageContentAnnotationsValidator(
+ PageContentAnnotator* annotator)
+ : annotator_(annotator) {
+ DCHECK(annotator);
+ for (AnnotationType type : {
+ AnnotationType::kPageTopics,
+ AnnotationType::kPageEntities,
+ AnnotationType::kContentVisibility,
+ }) {
+ if (features::PageContentAnnotationValidationEnabledForType(type)) {
+ enabled_annotation_types_.push_back(type);
+ annotator_->RequestAndNotifyWhenModelAvailable(type, base::DoNothing());
+ }
+ }
+
+ timer_.Start(FROM_HERE,
+ features::PageContentAnnotationValidationStartupDelay(),
+ base::BindOnce(&PageContentAnnotationsValidator::Run,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+// static
+std::unique_ptr<PageContentAnnotationsValidator>
+PageContentAnnotationsValidator::MaybeCreateAndStartTimer(
+ PageContentAnnotator* annotator) {
+ // This can happen with certain build/feature flags.
+ if (!annotator) {
+ return nullptr;
+ }
+
+ bool enabled_for_any_type = false;
+ for (AnnotationType type : {
+ AnnotationType::kPageTopics,
+ AnnotationType::kPageEntities,
+ AnnotationType::kContentVisibility,
+ }) {
+ enabled_for_any_type |=
+ features::PageContentAnnotationValidationEnabledForType(type);
+ }
+ if (!enabled_for_any_type) {
+ return nullptr;
+ }
+
+ // This is done because |PageContentAnnotationsValidator| has a private ctor.
+ return base::WrapUnique(new PageContentAnnotationsValidator(annotator));
+}
+
+void PageContentAnnotationsValidator::Run() {
+ for (AnnotationType type : enabled_annotation_types_) {
+ annotator_->Annotate(base::BindOnce(&MaybeLogAnnotationResultToFile),
+ BuildInputsForType(type), type);
+ }
+}
+
+// static
+std::vector<std::string> PageContentAnnotationsValidator::BuildInputsForType(
+ AnnotationType type) {
+ absl::optional<std::vector<std::string>> cmd_line_input =
+ switches::PageContentAnnotationsValidationInputForType(type);
+ if (cmd_line_input) {
+ return *cmd_line_input;
+ }
+
+ std::vector<std::string> inputs;
+ for (size_t i = 0; i < features::PageContentAnnotationsValidationBatchSize();
+ i++) {
+ const char* word1 = kRandomNouns[base::RandGenerator(kCountRandomNouns)];
+ const char* word2 = kRandomNouns[base::RandGenerator(kCountRandomNouns)];
+ inputs.emplace_back(base::StrCat({word1, " ", word2}));
+ }
+ return inputs;
+}
+
+} // namespace optimization_guide \ No newline at end of file
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_validator.h b/chromium/components/optimization_guide/content/browser/page_content_annotations_validator.h
new file mode 100644
index 00000000000..8300e28d6ba
--- /dev/null
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_validator.h
@@ -0,0 +1,57 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_OPTIMIZATION_GUIDE_CONTENT_BROWSER_PAGE_CONTENT_ANNOTATIONS_VALIDATOR_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CONTENT_BROWSER_PAGE_CONTENT_ANNOTATIONS_VALIDATOR_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/optimization_guide/core/page_content_annotation_type.h"
+
+namespace optimization_guide {
+
+class PageContentAnnotator;
+
+// This class manages validation runs of the PageContentAnnotationsService,
+// running the ML model for a given AnnotationType on dummy data after some
+// delay from browser startup. This feature can be controlled by experimental
+// feature flags and command line.
+class PageContentAnnotationsValidator {
+ public:
+ ~PageContentAnnotationsValidator();
+
+ // If the appropriate feature flag or command line switch is given, an
+ // instance of |this| is created, else nullptr.
+ static std::unique_ptr<PageContentAnnotationsValidator>
+ MaybeCreateAndStartTimer(PageContentAnnotator* annotator);
+
+ private:
+ explicit PageContentAnnotationsValidator(PageContentAnnotator* annotator);
+
+ // Runs the validation for all enabled AnnotationTypes.
+ void Run();
+
+ // Creates a set of dummy input data to run for the given |type|, either
+ // randomly generated off of experiment parameters or given on the command
+ // line.
+ static std::vector<std::string> BuildInputsForType(AnnotationType type);
+
+ std::vector<AnnotationType> enabled_annotation_types_;
+
+ // Out lives |this|, not owned.
+ raw_ptr<PageContentAnnotator> annotator_;
+
+ // Starts in the ctor, roughly on browser start, and calls |Run|.
+ base::OneShotTimer timer_;
+
+ base::WeakPtrFactory<PageContentAnnotationsValidator> weak_ptr_factory_{this};
+};
+
+} // namespace optimization_guide
+
+#endif // COMPONENTS_OPTIMIZATION_GUIDE_CONTENT_BROWSER_PAGE_CONTENT_ANNOTATIONS_VALIDATOR_H_
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_validator_unittest.cc b/chromium/components/optimization_guide/content/browser/page_content_annotations_validator_unittest.cc
new file mode 100644
index 00000000000..7201771c6cb
--- /dev/null
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_validator_unittest.cc
@@ -0,0 +1,325 @@
+// Copyright 2022 The Chromium 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/optimization_guide/content/browser/page_content_annotations_validator.h"
+
+#include "base/command_line.h"
+#include "base/test/scoped_command_line.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "components/optimization_guide/content/browser/test_page_content_annotator.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
+#include "components/optimization_guide/core/optimization_guide_switches.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace optimization_guide {
+
+TEST(PageContentAnnotationsValidatorTest, DoesNothing) {
+ TestPageContentAnnotator annotator;
+ EXPECT_EQ(nullptr, PageContentAnnotationsValidator::MaybeCreateAndStartTimer(
+ &annotator));
+}
+
+TEST(PageContentAnnotationsValidatorTest, NoAnnotator) {
+ TestPageContentAnnotator annotator;
+ EXPECT_EQ(nullptr,
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(nullptr));
+}
+
+TEST(PageContentAnnotationsValidatorTest,
+ DoesNothing_FeatureEnabledButNoTypes) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ features::kPageContentAnnotationsValidation);
+
+ TestPageContentAnnotator annotator;
+ EXPECT_EQ(nullptr, PageContentAnnotationsValidator::MaybeCreateAndStartTimer(
+ &annotator));
+}
+
+TEST(PageContentAnnotationsValidatorTest, AllEnabledByExperiment) {
+ base::test::TaskEnvironment task_env{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kPageContentAnnotationsValidation,
+ {
+ {"PageTopics", "true"},
+ {"PageEntities", "true"},
+ {"ContentVisibility", "true"},
+ });
+
+ TestPageContentAnnotator annotator;
+ auto validator =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(&annotator);
+ ASSERT_TRUE(validator);
+ task_env.FastForwardBy(base::Seconds(30));
+
+ const auto& annotation_requests = annotator.annotation_requests();
+ ASSERT_EQ(3U, annotation_requests.size());
+ EXPECT_EQ(annotation_requests[0].second, AnnotationType::kPageTopics);
+ EXPECT_EQ(annotation_requests[1].second, AnnotationType::kPageEntities);
+ EXPECT_EQ(annotation_requests[2].second, AnnotationType::kContentVisibility);
+}
+
+TEST(PageContentAnnotationsValidatorTest, AllEnabledByCommandLine) {
+ base::test::TaskEnvironment task_env{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+
+ cmd->AppendSwitchASCII(switches::kPageContentAnnotationsValidationPageTopics,
+ "page topics,pt input2, pt keeps whitespace ");
+ cmd->AppendSwitchASCII(
+ switches::kPageContentAnnotationsValidationPageEntities,
+ "page entities,pe input2, pe keeps whitespace ");
+ cmd->AppendSwitchASCII(
+ switches::kPageContentAnnotationsValidationContentVisibility,
+ "content viz,cv input2, cv keeps whitespace ");
+
+ TestPageContentAnnotator annotator;
+ auto validator =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(&annotator);
+ ASSERT_TRUE(validator);
+ task_env.FastForwardBy(base::Seconds(30));
+
+ const auto& annotation_requests = annotator.annotation_requests();
+ ASSERT_EQ(3U, annotation_requests.size());
+
+ EXPECT_THAT(annotation_requests[0].first, testing::ElementsAreArray({
+ "page topics",
+ "pt input2",
+ " pt keeps whitespace ",
+ }));
+ EXPECT_EQ(annotation_requests[0].second, AnnotationType::kPageTopics);
+
+ EXPECT_THAT(annotation_requests[1].first, testing::ElementsAreArray({
+ "page entities",
+ "pe input2",
+ " pe keeps whitespace ",
+ }));
+ EXPECT_EQ(annotation_requests[1].second, AnnotationType::kPageEntities);
+
+ EXPECT_THAT(annotation_requests[2].first, testing::ElementsAreArray({
+ "content viz",
+ "cv input2",
+ " cv keeps whitespace ",
+ }));
+ EXPECT_EQ(annotation_requests[2].second, AnnotationType::kContentVisibility);
+}
+
+TEST(PageContentAnnotationsValidatorTest, OnlyOneEnabled_Cmd) {
+ base::test::TaskEnvironment task_env{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+ for (AnnotationType type : {
+ AnnotationType::kPageTopics,
+ AnnotationType::kPageEntities,
+ AnnotationType::kContentVisibility,
+ }) {
+ SCOPED_TRACE(AnnotationTypeToString(type));
+ base::test::ScopedCommandLine scoped_cmd;
+ base::CommandLine* cmd = scoped_cmd.GetProcessCommandLine();
+
+ switch (type) {
+ case AnnotationType::kPageTopics:
+ cmd->AppendSwitch(
+ switches::kPageContentAnnotationsValidationPageTopics);
+ break;
+ case AnnotationType::kPageEntities:
+ cmd->AppendSwitch(
+ switches::kPageContentAnnotationsValidationPageEntities);
+ break;
+ case AnnotationType::kContentVisibility:
+ cmd->AppendSwitch(
+ switches::kPageContentAnnotationsValidationContentVisibility);
+ break;
+ default:
+ break;
+ }
+
+ TestPageContentAnnotator annotator;
+ auto validator =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(&annotator);
+ ASSERT_TRUE(validator);
+ task_env.FastForwardBy(base::Seconds(30));
+
+ const auto& annotation_requests = annotator.annotation_requests();
+ ASSERT_EQ(1U, annotation_requests.size());
+
+ EXPECT_EQ(annotation_requests[0].second, type);
+ }
+}
+
+TEST(PageContentAnnotationsValidatorTest, OnlyOneEnabled_Feature) {
+ base::test::TaskEnvironment task_env{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+ for (AnnotationType type : {
+ AnnotationType::kPageTopics,
+ AnnotationType::kPageEntities,
+ AnnotationType::kContentVisibility,
+ }) {
+ SCOPED_TRACE(AnnotationTypeToString(type));
+ base::test::ScopedFeatureList scoped_feature_list;
+
+ switch (type) {
+ case AnnotationType::kPageTopics:
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kPageContentAnnotationsValidation,
+ {{"PageTopics", "true"}});
+ break;
+ case AnnotationType::kPageEntities:
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kPageContentAnnotationsValidation,
+ {{"PageEntities", "true"}});
+ break;
+ case AnnotationType::kContentVisibility:
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kPageContentAnnotationsValidation,
+ {{"ContentVisibility", "true"}});
+ break;
+ default:
+ break;
+ }
+
+ TestPageContentAnnotator annotator;
+ auto validator =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(&annotator);
+ ASSERT_TRUE(validator);
+ task_env.FastForwardBy(base::Seconds(30));
+
+ const auto& annotation_requests = annotator.annotation_requests();
+ ASSERT_EQ(1U, annotation_requests.size());
+ EXPECT_EQ(annotation_requests[0].second, type);
+ }
+}
+
+TEST(PageContentAnnotationsValidatorTest, TimerDelayByCmd) {
+ base::test::TaskEnvironment task_env{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+
+ cmd->AppendSwitch(switches::kPageContentAnnotationsValidationPageTopics);
+ cmd->AppendSwitchASCII(
+ switches::kPageContentAnnotationsValidationStartupDelaySeconds, "5");
+
+ TestPageContentAnnotator annotator;
+ auto validator =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(&annotator);
+ ASSERT_TRUE(validator);
+ EXPECT_TRUE(annotator.annotation_requests().empty());
+
+ task_env.FastForwardBy(base::Milliseconds(4999));
+ EXPECT_TRUE(annotator.annotation_requests().empty());
+
+ task_env.FastForwardBy(base::Milliseconds(1));
+ EXPECT_FALSE(annotator.annotation_requests().empty());
+}
+
+TEST(PageContentAnnotationsValidatorTest, TimerDelayByFeature) {
+ base::test::TaskEnvironment task_env{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kPageContentAnnotationsValidation, {
+ {"PageTopics", "true"},
+ {"startup_delay", "5"},
+ });
+
+ TestPageContentAnnotator annotator;
+ auto validator =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(&annotator);
+ ASSERT_TRUE(validator);
+ EXPECT_TRUE(annotator.annotation_requests().empty());
+
+ task_env.FastForwardBy(base::Milliseconds(4999));
+ EXPECT_TRUE(annotator.annotation_requests().empty());
+
+ task_env.FastForwardBy(base::Milliseconds(1));
+ EXPECT_FALSE(annotator.annotation_requests().empty());
+}
+
+TEST(PageContentAnnotationsValidatorTest, BatchSizeByCmd) {
+ base::test::TaskEnvironment task_env{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+
+ cmd->AppendSwitch(switches::kPageContentAnnotationsValidationPageTopics);
+ cmd->AppendSwitchASCII(
+ switches::kPageContentAnnotationsValidationBatchSizeOverride, "5");
+
+ TestPageContentAnnotator annotator;
+ auto validator =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(&annotator);
+ ASSERT_TRUE(validator);
+ task_env.FastForwardBy(base::Seconds(30));
+
+ const auto& annotation_requests = annotator.annotation_requests();
+ ASSERT_EQ(1U, annotation_requests.size());
+ EXPECT_EQ(annotation_requests[0].first.size(), 5U);
+}
+
+TEST(PageContentAnnotationsValidatorTest, BatchSizeByFeature) {
+ base::test::TaskEnvironment task_env{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kPageContentAnnotationsValidation, {
+ {"PageTopics", "true"},
+ {"batch_size", "5"},
+ });
+
+ TestPageContentAnnotator annotator;
+ auto validator =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(&annotator);
+ ASSERT_TRUE(validator);
+ task_env.FastForwardBy(base::Seconds(30));
+
+ const auto& annotation_requests = annotator.annotation_requests();
+ ASSERT_EQ(1U, annotation_requests.size());
+ EXPECT_EQ(annotation_requests[0].first.size(), 5U);
+}
+
+TEST(PageContentAnnotationsValidatorTest, CommandOverridesFeature) {
+ base::test::TaskEnvironment task_env{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kPageContentAnnotationsValidation,
+ {
+ {"PageTopics", "true"},
+ {"PageEntities", "true"},
+ {"ContentVisibility", "true"},
+ {"batch_size", "3"},
+ });
+
+ cmd->AppendSwitchASCII(switches::kPageContentAnnotationsValidationPageTopics,
+ "page topics");
+ cmd->AppendSwitchASCII(
+ switches::kPageContentAnnotationsValidationBatchSizeOverride, "5");
+
+ TestPageContentAnnotator annotator;
+ auto validator =
+ PageContentAnnotationsValidator::MaybeCreateAndStartTimer(&annotator);
+ ASSERT_TRUE(validator);
+ task_env.FastForwardBy(base::Seconds(30));
+
+ const auto& annotation_requests = annotator.annotation_requests();
+ ASSERT_EQ(3U, annotation_requests.size());
+
+ EXPECT_THAT(annotation_requests[0].first,
+ testing::ElementsAre("page topics"));
+ EXPECT_EQ(annotation_requests[0].second, AnnotationType::kPageTopics);
+
+ EXPECT_EQ(annotation_requests[1].first.size(), 5U);
+ EXPECT_EQ(annotation_requests[1].second, AnnotationType::kPageEntities);
+
+ EXPECT_EQ(annotation_requests[2].first.size(), 5U);
+ EXPECT_EQ(annotation_requests[2].second, AnnotationType::kContentVisibility);
+}
+
+} // namespace optimization_guide \ No newline at end of file
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.cc b/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.cc
index e68dc88503f..773eacfedb7 100644
--- a/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.cc
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/i18n/case_conversion.h"
+#include "base/metrics/histogram_functions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/google/core/common/google_util.h"
#include "components/optimization_guide/content/browser/optimization_guide_decider.h"
@@ -22,14 +23,27 @@ namespace optimization_guide {
namespace {
+// Return whether or not we should fetch remote metadata.
+bool FetchRemoteMetadataEnabled() {
+ return features::RemotePageEntitiesEnabled() ||
+ features::RemotePageMetadataEnabled();
+}
+
// Returns search metadata if |url| is a valid Search URL according to
// |template_url_service|.
absl::optional<SearchMetadata> ExtractSearchMetadata(
- const TemplateURLService* template_url_service,
+ TemplateURLService* template_url_service,
const GURL& url) {
if (!template_url_service)
return absl::nullopt;
+ if (!template_url_service->loaded()) {
+ if (switches::ShouldLogPageContentAnnotationsInput()) {
+ LOG(ERROR) << "Template URL Service not loaded";
+ }
+ return absl::nullopt;
+ }
+
const TemplateURL* template_url =
template_url_service->GetTemplateURLForHost(url.host());
const SearchTermsData& search_terms_data =
@@ -40,16 +54,30 @@ absl::optional<SearchMetadata> ExtractSearchMetadata(
template_url->ExtractSearchTermsFromURL(
url, search_terms_data, &search_terms) &&
!search_terms.empty();
- if (!is_valid_search_url)
+ if (!is_valid_search_url) {
+ if (switches::ShouldLogPageContentAnnotationsInput()) {
+ LOG(ERROR) << "Url " << url << " is not a valid search URL";
+ LOG(ERROR) << "Matching TemplateURL instances for host: "
+ << template_url_service->GetTemplateURLCountForHostForLogging(
+ url.host());
+ }
return absl::nullopt;
+ }
const std::u16string& normalized_search_query =
base::i18n::ToLower(base::CollapseWhitespace(search_terms, false));
TemplateURLRef::SearchTermsArgs search_terms_args(normalized_search_query);
const TemplateURLRef& search_url_ref = template_url->url_ref();
- if (!search_url_ref.SupportsReplacement(search_terms_data))
+ if (!search_url_ref.SupportsReplacement(search_terms_data)) {
+ if (switches::ShouldLogPageContentAnnotationsInput()) {
+ LOG(ERROR) << "Url " << url << " does not support replacement";
+ }
return absl::nullopt;
+ }
+ if (switches::ShouldLogPageContentAnnotationsInput()) {
+ LOG(ERROR) << "Url " << url << " is a valid search URL";
+ }
return SearchMetadata{
GURL(search_url_ref.ReplaceSearchTerms(search_terms_args,
search_terms_data)),
@@ -96,36 +124,17 @@ PageContentAnnotationsWebContentsObserver::
*web_contents),
page_content_annotations_service_(page_content_annotations_service),
template_url_service_(template_url_service),
- optimization_guide_decider_(optimization_guide_decider),
- max_size_for_text_dump_(features::MaxSizeForPageContentTextDump()) {
+ optimization_guide_decider_(optimization_guide_decider) {
DCHECK(page_content_annotations_service_);
- if (!features::ShouldAnnotateTitleInsteadOfPageContent()) {
- // Make sure we always attach ourselves to a PageTextObserver if we are
- // annotating page content.
- PageTextObserver* observer =
- PageTextObserver::GetOrCreateForWebContents(web_contents);
- observer->AddConsumer(this);
- }
-
- if (features::RemotePageEntitiesEnabled() && optimization_guide_decider_) {
+ if (FetchRemoteMetadataEnabled() && optimization_guide_decider_) {
optimization_guide_decider_->RegisterOptimizationTypes(
{proto::PAGE_ENTITIES});
}
}
PageContentAnnotationsWebContentsObserver::
- ~PageContentAnnotationsWebContentsObserver() {
- // Only detach ourselves if |web_contents()| as well as PageTextObserver for
- // |web_contents()| are still alive.
- if (!web_contents())
- return;
-
- PageTextObserver* observer =
- PageTextObserver::FromWebContents(web_contents());
- if (observer)
- observer->RemoveConsumer(this);
-}
+ ~PageContentAnnotationsWebContentsObserver() = default;
void PageContentAnnotationsWebContentsObserver::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
@@ -137,30 +146,40 @@ void PageContentAnnotationsWebContentsObserver::DidFinishNavigation(
if (!navigation_handle->GetURL().SchemeIsHTTPOrHTTPS())
return;
- PageData* page_data = nullptr;
- if (features::ShouldAnnotateTitleInsteadOfPageContent()) {
- page_data = PageData::GetOrCreateForPage(web_contents()->GetPrimaryPage());
- page_data->set_navigation_id(navigation_handle->GetNavigationId());
- }
+ PageData* page_data =
+ PageData::GetOrCreateForPage(web_contents()->GetPrimaryPage());
+ page_data->set_navigation_id(navigation_handle->GetNavigationId());
optimization_guide::HistoryVisit history_visit = optimization_guide::
PageContentAnnotationsService::CreateHistoryVisitFromWebContents(
web_contents(), navigation_handle->GetNavigationId());
-
- if (features::RemotePageEntitiesEnabled() && optimization_guide_decider_) {
+ if (FetchRemoteMetadataEnabled() && optimization_guide_decider_) {
optimization_guide_decider_->CanApplyOptimizationAsync(
navigation_handle, proto::PAGE_ENTITIES,
base::BindOnce(&PageContentAnnotationsWebContentsObserver::
- OnRemotePageEntitiesReceived,
+ OnRemotePageMetadataReceived,
weak_ptr_factory_.GetWeakPtr(), history_visit));
}
- if (google_util::IsGoogleSearchUrl(navigation_handle->GetURL())) {
- // Extract related searches.
- if (optimization_guide::features::ShouldExtractRelatedSearches()) {
- page_content_annotations_service_->ExtractRelatedSearches(history_visit,
- web_contents());
- }
+ bool is_google_search_url =
+ google_util::IsGoogleSearchUrl(navigation_handle->GetURL());
+ // Extract related searches.
+ if (is_google_search_url &&
+ optimization_guide::features::ShouldExtractRelatedSearches()) {
+ page_content_annotations_service_->ExtractRelatedSearches(history_visit,
+ web_contents());
+ }
+
+ // Persist search metadata, if applicable if it's a Google search URL or if
+ // it's a search-y URL as determined by the TemplateURLService if the flag is
+ // enabled.
+ if (is_google_search_url ||
+ optimization_guide::features::
+ ShouldPersistSearchMetadataForNonGoogleSearches()) {
+ base::UmaHistogramBoolean(
+ "OptimizationGuide.PageContentAnnotations."
+ "TemplateURLServiceLoadedAtNavigationFinish",
+ template_url_service_ && template_url_service_->loaded());
absl::optional<SearchMetadata> search_metadata = ExtractSearchMetadata(
template_url_service_, navigation_handle->GetURL());
@@ -184,9 +203,10 @@ void PageContentAnnotationsWebContentsObserver::DidFinishNavigation(
}
}
- // TODO(crbug/1177102): Remove this title hack once the PageTextObserver works
- // for same-document navigations.
- if (navigation_handle->IsSameDocument()) {
+ // Same-document navigations and reloads do not trigger |TitleWasSet|, so we
+ // need to capture the title text here for these cases.
+ if (navigation_handle->IsSameDocument() ||
+ navigation_handle->GetReloadType() != content::ReloadType::NONE) {
if (page_data) {
page_data->set_annotation_was_requested();
}
@@ -232,69 +252,7 @@ void PageContentAnnotationsWebContentsObserver::TitleWasSet(
}
}
-std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest>
-PageContentAnnotationsWebContentsObserver::MaybeRequestFrameTextDump(
- content::NavigationHandle* navigation_handle) {
- DCHECK(!features::ShouldAnnotateTitleInsteadOfPageContent());
-
- DCHECK(navigation_handle->HasCommitted());
-
- DCHECK(navigation_handle->IsInPrimaryMainFrame());
-
- if (!navigation_handle->GetURL().SchemeIsHTTPOrHTTPS())
- return nullptr;
-
- if (navigation_handle->IsSameDocument())
- return nullptr;
-
- if (google_util::IsGoogleSearchUrl(navigation_handle->GetURL()))
- return nullptr;
-
- std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest> request =
- std::make_unique<PageTextObserver::ConsumerTextDumpRequest>();
- request->max_size = max_size_for_text_dump_;
- request->events = {mojom::TextDumpEvent::kFirstLayout};
- request->dump_amp_subframes = true;
- request->callback = base::BindOnce(
- &PageContentAnnotationsWebContentsObserver::OnTextDumpReceived,
- weak_ptr_factory_.GetWeakPtr(),
- PageContentAnnotationsService::CreateHistoryVisitFromWebContents(
- navigation_handle->GetWebContents(),
- navigation_handle->GetNavigationId()));
- return request;
-}
-
-void PageContentAnnotationsWebContentsObserver::OnTextDumpReceived(
- HistoryVisit visit,
- const PageTextDumpResult& result) {
- DCHECK(!features::ShouldAnnotateTitleInsteadOfPageContent());
-
- if (result.empty()) {
- return;
- }
-
- // If the page had AMP frames, then only use that content. Otherwise, use the
- // mainframe.
- if (result.GetAMPTextContent()) {
- visit.text_to_annotate = *result.GetAMPTextContent();
- page_content_annotations_service_->Annotate(visit);
- if (switches::ShouldLogPageContentAnnotationsInput()) {
- LOG(ERROR) << "Annotating AMP text content: \n"
- << "URL: " << visit.url << "\n"
- << "Text: " << *(visit.text_to_annotate);
- }
- return;
- }
- visit.text_to_annotate = *result.GetMainFrameTextContent();
- page_content_annotations_service_->Annotate(visit);
- if (switches::ShouldLogPageContentAnnotationsInput()) {
- LOG(ERROR) << "Annotating main frame text content: \n"
- << "URL: " << visit.url << "\n"
- << "Text: " << *(visit.text_to_annotate);
- }
-}
-
-void PageContentAnnotationsWebContentsObserver::OnRemotePageEntitiesReceived(
+void PageContentAnnotationsWebContentsObserver::OnRemotePageMetadataReceived(
const HistoryVisit& history_visit,
OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
@@ -303,22 +261,36 @@ void PageContentAnnotationsWebContentsObserver::OnRemotePageEntitiesReceived(
absl::optional<proto::PageEntitiesMetadata> page_entities_metadata =
metadata.ParsedMetadata<proto::PageEntitiesMetadata>();
- if (!page_entities_metadata || page_entities_metadata->entities().size() == 0)
+ if (!page_entities_metadata)
return;
- std::vector<history::VisitContentModelAnnotations::Category> entities;
- for (const auto& entity : page_entities_metadata->entities()) {
- if (entity.entity_id().empty())
- continue;
+ // Persist entities to VisitContentModelAnnotations if that feature is
+ // enabled.
+ if (page_entities_metadata->entities().size() != 0 &&
+ features::RemotePageEntitiesEnabled()) {
+ std::vector<history::VisitContentModelAnnotations::Category> entities;
+ for (const auto& entity : page_entities_metadata->entities()) {
+ if (entity.entity_id().empty())
+ continue;
- if (entity.score() < 0 || entity.score() > 100)
- continue;
+ if (entity.score() < 0 || entity.score() > 100)
+ continue;
- entities.emplace_back(history::VisitContentModelAnnotations::Category(
- entity.entity_id(), entity.score()));
+ entities.emplace_back(history::VisitContentModelAnnotations::Category(
+ entity.entity_id(), entity.score()));
+ }
+ page_content_annotations_service_->PersistRemotePageEntities(history_visit,
+ entities);
+ }
+ if (!features::RemotePageMetadataEnabled()) {
+ return;
+ }
+ // Persist any other metadata to VisitContentAnnotations.
+ page_entities_metadata->clear_entities();
+ if (page_entities_metadata->has_alternative_title()) {
+ page_content_annotations_service_->PersistRemotePageMetadata(
+ history_visit, *page_entities_metadata);
}
- page_content_annotations_service_->PersistRemotePageEntities(history_visit,
- entities);
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(PageContentAnnotationsWebContentsObserver);
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.h b/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.h
index de084693c39..1cfdc3e8a96 100644
--- a/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.h
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer.h
@@ -7,8 +7,7 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/content/browser/page_text_dump_result.h"
-#include "components/optimization_guide/content/browser/page_text_observer.h"
+#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
class TemplateURLService;
@@ -30,8 +29,7 @@ class PageContentAnnotationsService;
class PageContentAnnotationsWebContentsObserver
: public content::WebContentsObserver,
public content::WebContentsUserData<
- PageContentAnnotationsWebContentsObserver>,
- public PageTextObserver::Consumer {
+ PageContentAnnotationsWebContentsObserver> {
public:
~PageContentAnnotationsWebContentsObserver() override;
@@ -56,17 +54,9 @@ class PageContentAnnotationsWebContentsObserver
void DidFinishNavigation(content::NavigationHandle* handle) override;
void TitleWasSet(content::NavigationEntry* navigation_entry) override;
- // PageTextObserver::Consumer:
- std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest>
- MaybeRequestFrameTextDump(
- content::NavigationHandle* navigation_handle) override;
-
- // Callback invoked when a text dump has been received for the |visit|.
- void OnTextDumpReceived(HistoryVisit visit, const PageTextDumpResult& result);
-
- // Callback invoked when the page entities have been received from
+ // Callback invoked when the page metadata has been received from
// |optimization_guide_decider_| for |visit|.
- void OnRemotePageEntitiesReceived(const HistoryVisit& visit,
+ void OnRemotePageMetadataReceived(const HistoryVisit& visit,
OptimizationGuideDecision decision,
const OptimizationMetadata& metadata);
@@ -74,14 +64,11 @@ class PageContentAnnotationsWebContentsObserver
raw_ptr<PageContentAnnotationsService> page_content_annotations_service_;
// Not owned. Guaranteed to outlive |this|.
- raw_ptr<const TemplateURLService> template_url_service_;
+ raw_ptr<TemplateURLService> template_url_service_;
// Not owned. Guaranteed to outlive |this|.
raw_ptr<OptimizationGuideDecider> optimization_guide_decider_;
- // The max size to request for text dump.
- const uint64_t max_size_for_text_dump_;
-
base::WeakPtrFactory<PageContentAnnotationsWebContentsObserver>
weak_ptr_factory_{this};
diff --git a/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer_unittest.cc b/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer_unittest.cc
index ea9af5caf85..e53fc62bf3e 100644
--- a/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer_unittest.cc
+++ b/chromium/components/optimization_guide/content/browser/page_content_annotations_web_contents_observer_unittest.cc
@@ -11,7 +11,6 @@
#include "components/google/core/common/google_switches.h"
#include "components/history/core/browser/history_service.h"
#include "components/optimization_guide/content/browser/page_content_annotations_service.h"
-#include "components/optimization_guide/content/browser/page_text_dump_result.h"
#include "components/optimization_guide/content/browser/test_optimization_guide_decider.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
@@ -38,23 +37,6 @@ const TemplateURLService::Initializer kTemplateURLData[] = {
};
const char16_t kDefaultTemplateURLKeyword[] = u"default-engine.com";
-class TestPageTextObserver : public PageTextObserver {
- public:
- explicit TestPageTextObserver(content::WebContents* web_contents)
- : PageTextObserver(web_contents) {}
-
- void AddConsumer(PageTextObserver::Consumer* consumer) override {
- add_consumer_called_ = true;
- }
- bool add_consumer_called() const { return add_consumer_called_; }
-
- // We don't test remove consumer since there is no guaranteed ordering when
- // WebContentsObservers are destroyed, so we may hit a segfault.
-
- private:
- bool add_consumer_called_ = false;
-};
-
class FakePageContentAnnotationsService : public PageContentAnnotationsService {
public:
explicit FakePageContentAnnotationsService(
@@ -114,6 +96,17 @@ class FakePageContentAnnotationsService : public PageContentAnnotationsService {
return last_search_metadata_;
}
+ void PersistRemotePageMetadata(
+ const HistoryVisit& visit,
+ const proto::PageEntitiesMetadata& page_metadata) override {
+ last_page_metadata_ = page_metadata;
+ }
+
+ absl::optional<proto::PageEntitiesMetadata> last_page_metadata_persisted()
+ const {
+ return last_page_metadata_;
+ }
+
private:
absl::optional<HistoryVisit> last_annotation_request_;
absl::optional<std::pair<HistoryVisit, content::WebContents*>>
@@ -123,6 +116,7 @@ class FakePageContentAnnotationsService : public PageContentAnnotationsService {
std::vector<history::VisitContentModelAnnotations::Category>>>
last_entities_persistence_request_;
absl::optional<SearchMetadata> last_search_metadata_;
+ absl::optional<proto::PageEntitiesMetadata> last_page_metadata_;
};
class FakeOptimizationGuideDecider : public TestOptimizationGuideDecider {
@@ -164,6 +158,15 @@ class FakeOptimizationGuideDecider : public TestOptimizationGuideDecider {
std::move(callback).Run(OptimizationGuideDecision::kTrue, metadata);
return;
}
+ if (navigation_handle->GetURL() == GURL("http://hasmetadata.com/")) {
+ proto::PageEntitiesMetadata page_entities_metadata;
+ page_entities_metadata.set_alternative_title("alternative title");
+
+ OptimizationMetadata metadata;
+ metadata.SetAnyMetadataForTesting(page_entities_metadata);
+ std::move(callback).Run(OptimizationGuideDecision::kTrue, metadata);
+ return;
+ }
if (navigation_handle->GetURL() == GURL("http://noentities.com/")) {
proto::PageEntitiesMetadata page_entities_metadata;
OptimizationMetadata metadata;
@@ -192,8 +195,8 @@ class PageContentAnnotationsWebContentsObserverTest
scoped_feature_list_.InitAndEnableFeatureWithParameters(
features::kPageContentAnnotations,
{{"extract_related_searches", "false"},
- {"annotate_title_instead_of_page_content", "false"},
- {"fetch_remote_page_entities", "false"}});
+ {"fetch_remote_page_entities", "false"},
+ {"persist_search_metadata_for_non_google_searches", "true"}});
}
void SetUp() override {
@@ -216,10 +219,6 @@ class PageContentAnnotationsWebContentsObserverTest
optimization_guide_decider_ =
std::make_unique<FakeOptimizationGuideDecider>();
- page_text_observer_ = new TestPageTextObserver(web_contents());
- web_contents()->SetUserData(TestPageTextObserver::UserDataKey(),
- base::WrapUnique(page_text_observer_.get()));
-
PageContentAnnotationsWebContentsObserver::CreateForWebContents(
web_contents(), page_content_annotations_service_.get(),
template_url_service_.get(), optimization_guide_decider_.get());
@@ -230,7 +229,6 @@ class PageContentAnnotationsWebContentsObserverTest
}
void TearDown() override {
- page_text_observer_ = nullptr;
page_content_annotations_service_.reset();
optimization_guide_model_provider_.reset();
template_url_service_.reset();
@@ -248,21 +246,12 @@ class PageContentAnnotationsWebContentsObserverTest
web_contents());
}
- TestPageTextObserver* page_text_observer() { return page_text_observer_; }
-
FakeOptimizationGuideDecider* optimization_guide_decider() {
return optimization_guide_decider_.get();
}
- std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest>
- RequestTextDumpForUrl(const GURL& url, bool is_same_document = false) {
- content::MockNavigationHandle navigation_handle(url, main_rfh());
- navigation_handle.set_url(url);
- // PageTextObserver is guaranteed to call MaybeRequestFrameTextDump after
- // the navigation has been committed.
- navigation_handle.set_has_committed(true);
- navigation_handle.set_is_same_document(is_same_document);
- return helper()->MaybeRequestFrameTextDump(&navigation_handle);
+ void SetTemplateURLServiceLoaded(bool loaded) {
+ template_url_service_->set_loaded(loaded);
}
private:
@@ -274,7 +263,6 @@ class PageContentAnnotationsWebContentsObserverTest
page_content_annotations_service_;
std::unique_ptr<TemplateURLService> template_url_service_;
raw_ptr<TemplateURL> template_url_;
- raw_ptr<TestPageTextObserver> page_text_observer_;
std::unique_ptr<FakeOptimizationGuideDecider> optimization_guide_decider_;
};
@@ -284,66 +272,26 @@ TEST_F(PageContentAnnotationsWebContentsObserverTest, DoesNotRegisterType) {
}
TEST_F(PageContentAnnotationsWebContentsObserverTest,
- HooksIntoPageTextObserver) {
- EXPECT_TRUE(page_text_observer()->add_consumer_called());
-}
-
-TEST_F(PageContentAnnotationsWebContentsObserverTest,
- DoesNotRequestForNonHttpHttps) {
- EXPECT_EQ(RequestTextDumpForUrl(GURL("chrome://new-tab")), nullptr);
-}
-
-TEST_F(PageContentAnnotationsWebContentsObserverTest,
- DoesNotRequestForSameDocument) {
- EXPECT_EQ(
- RequestTextDumpForUrl(GURL("http://test.com"), /*is_same_document=*/true),
- nullptr);
-}
-
-TEST_F(PageContentAnnotationsWebContentsObserverTest,
- DoesNotRequestForGoogleSRP) {
- EXPECT_EQ(RequestTextDumpForUrl(GURL("http://default-engine.com/search?q=a")),
- nullptr);
-}
-
-TEST_F(PageContentAnnotationsWebContentsObserverTest,
- RequestsForMainFrameHttpUrlCallbackDispatchesToService) {
- // Navigate and commit so there is an entry. In actual situations, we are
- // guaranteed that MaybeRequestFrameTextDump will only be called for
- // committed frames.
+ MainFrameNavigationAnnotatesTitle) {
+ // Navigate.
content::NavigationSimulator::NavigateAndCommitFromBrowser(
- web_contents(), GURL("http://test.com"));
+ web_contents(), GURL("http://www.foo.com/someurl"));
- std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest> request =
- RequestTextDumpForUrl(GURL("http://test.com"));
- ASSERT_TRUE(request);
- ASSERT_TRUE(request->callback);
- EXPECT_EQ(features::MaxSizeForPageContentTextDump(), request->max_size);
- EXPECT_TRUE(request->dump_amp_subframes);
- EXPECT_EQ(std::set<mojom::TextDumpEvent>{mojom::TextDumpEvent::kFirstLayout},
- request->events);
-
- // Invoke OnTextDumpReceived.
- FrameTextDumpResult frame_result =
- FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
- content::GlobalRenderFrameHostId(),
- /*amp_frame=*/false,
- /*unique_navigation_id=*/1)
- .CompleteWithContents(u"some text");
- PageTextDumpResult result;
- result.AddFrameTextDumpResult(frame_result);
- std::move(request->callback).Run(std::move(result));
+ // Set title.
+ std::u16string title(u"Title");
+ web_contents()->UpdateTitleForEntry(controller().GetLastCommittedEntry(),
+ title);
+ // The title should be what is requested to be annotated.
absl::optional<HistoryVisit> last_annotation_request =
service()->last_annotation_request();
EXPECT_TRUE(last_annotation_request.has_value());
- EXPECT_EQ(last_annotation_request->url, GURL("http://test.com"));
- EXPECT_EQ(last_annotation_request->text_to_annotate, "some text");
+ EXPECT_EQ(last_annotation_request->url, GURL("http://www.foo.com/someurl"));
+ EXPECT_EQ(last_annotation_request->text_to_annotate, "Title");
service()->ClearLastAnnotationRequest();
- // Update title - make sure we don't annotate if we intend to annotate
- // content.
+ // Update title again - make sure we don't reannotate for same page.
web_contents()->UpdateTitleForEntry(controller().GetLastCommittedEntry(),
u"newtitle");
EXPECT_FALSE(service()->last_annotation_request());
@@ -376,6 +324,8 @@ TEST_F(PageContentAnnotationsWebContentsObserverTest,
TEST_F(PageContentAnnotationsWebContentsObserverTest,
SRPURLsAnnotateSearchTerms) {
+ base::HistogramTester histogram_tester;
+
// Navigate and commit so there is an entry.
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://default-engine.com/search?q=a"));
@@ -394,6 +344,40 @@ TEST_F(PageContentAnnotationsWebContentsObserverTest,
EXPECT_EQ(last_search_metadata_persisted->normalized_url,
GURL("http://default-engine.com/search?q=a"));
EXPECT_EQ(last_search_metadata_persisted->search_terms, u"a");
+
+ histogram_tester.ExpectUniqueSample(
+ "OptimizationGuide.PageContentAnnotations."
+ "TemplateURLServiceLoadedAtNavigationFinish",
+ true, 1);
+}
+
+TEST_F(PageContentAnnotationsWebContentsObserverTest,
+ NonGoogleSRPURLsAnnotateSearchTerms) {
+ base::HistogramTester histogram_tester;
+
+ // Navigate and commit so there is an entry.
+ content::NavigationSimulator::NavigateAndCommitFromBrowser(
+ web_contents(), GURL("http://non-default-engine.com/?q=a"));
+
+ // The search query should be what is requested to be annotated.
+ absl::optional<HistoryVisit> last_annotation_request =
+ service()->last_annotation_request();
+ ASSERT_TRUE(last_annotation_request.has_value());
+ EXPECT_EQ(last_annotation_request->url,
+ GURL("http://non-default-engine.com/?q=a"));
+ EXPECT_EQ(last_annotation_request->text_to_annotate, "a");
+
+ absl::optional<SearchMetadata> last_search_metadata_persisted =
+ service()->last_search_metadata_persisted();
+ ASSERT_TRUE(last_search_metadata_persisted.has_value());
+ EXPECT_EQ(last_search_metadata_persisted->normalized_url,
+ GURL("http://non-default-engine.com/?q=a"));
+ EXPECT_EQ(last_search_metadata_persisted->search_terms, u"a");
+
+ histogram_tester.ExpectUniqueSample(
+ "OptimizationGuide.PageContentAnnotations."
+ "TemplateURLServiceLoadedAtNavigationFinish",
+ true, 1);
}
TEST_F(PageContentAnnotationsWebContentsObserverTest,
@@ -448,41 +432,38 @@ TEST_F(PageContentAnnotationsWebContentsObserverRelatedSearchesTest,
EXPECT_EQ(last_request->second, web_contents());
}
-class PageContentAnnotationsWebContentsObserverAnnotateTitleTest
+class
+ PageContentAnnotationsWebContentsObserverOnlyPersistGoogleSearchMetadataTest
: public PageContentAnnotationsWebContentsObserverTest {
public:
- PageContentAnnotationsWebContentsObserverAnnotateTitleTest() {
+ PageContentAnnotationsWebContentsObserverOnlyPersistGoogleSearchMetadataTest() {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
features::kPageContentAnnotations,
- {{"annotate_title_instead_of_page_content", "true"}});
+ {{"persist_search_metadata_for_non_google_searches", "false"}});
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
-TEST_F(PageContentAnnotationsWebContentsObserverAnnotateTitleTest,
- SameDocumentNavigationsStillAnnotatesTitle) {
+TEST_F(
+ PageContentAnnotationsWebContentsObserverOnlyPersistGoogleSearchMetadataTest,
+ AnnotatesTitleInsteadOfSearchTerms) {
// Navigate.
- content::NavigationSimulator::NavigateAndCommitFromDocument(
- GURL("http://foo"), main_rfh());
+ content::NavigationSimulator::NavigateAndCommitFromBrowser(
+ web_contents(), GURL("http://non-default-engine.com/?q=a"));
- // Set title and favicon.
+ // Set title.
std::u16string title(u"Title");
web_contents()->UpdateTitleForEntry(controller().GetLastCommittedEntry(),
title);
- // history.pushState() is called for url2.
- GURL url2("http://foo#foo");
- std::unique_ptr<content::NavigationSimulator> navigation_simulator =
- content::NavigationSimulator::CreateRendererInitiated(url2, main_rfh());
- navigation_simulator->CommitSameDocument();
-
// The title should be what is requested to be annotated.
absl::optional<HistoryVisit> last_annotation_request =
service()->last_annotation_request();
EXPECT_TRUE(last_annotation_request.has_value());
- EXPECT_EQ(last_annotation_request->url, url2);
+ EXPECT_EQ(last_annotation_request->url,
+ GURL("http://non-default-engine.com/?q=a"));
EXPECT_EQ(last_annotation_request->text_to_annotate, "Title");
service()->ClearLastAnnotationRequest();
@@ -491,35 +472,46 @@ TEST_F(PageContentAnnotationsWebContentsObserverAnnotateTitleTest,
web_contents()->UpdateTitleForEntry(controller().GetLastCommittedEntry(),
u"newtitle");
EXPECT_FALSE(service()->last_annotation_request());
+
+ // Search metadata should not be persisted.
+ absl::optional<SearchMetadata> last_search_metadata_persisted =
+ service()->last_search_metadata_persisted();
+ ASSERT_FALSE(last_search_metadata_persisted.has_value());
}
-TEST_F(PageContentAnnotationsWebContentsObserverAnnotateTitleTest,
- AnnotatesTitleInsteadOfContent) {
- // Navigate.
- content::NavigationSimulator::NavigateAndCommitFromBrowser(
- web_contents(), GURL("http://www.foo.com/someurl"));
+TEST_F(
+ PageContentAnnotationsWebContentsObserverOnlyPersistGoogleSearchMetadataTest,
+ SRPURLsAnnotateTitleIfTemplateURLServiceNotLoaded) {
+ SetTemplateURLServiceLoaded(false);
- // Make sure we didn't register with the PageTextObserver.
- EXPECT_EQ(page_text_observer()->outstanding_requests(), 0u);
+ base::HistogramTester histogram_tester;
+
+ // Navigate and commit so there is an entry.
+ content::NavigationSimulator::NavigateAndCommitFromBrowser(
+ web_contents(), GURL("http://default-engine.com/search?q=a"));
// Set title.
std::u16string title(u"Title");
web_contents()->UpdateTitleForEntry(controller().GetLastCommittedEntry(),
title);
+ // We don't know what the search terms are so no search metadata is persisted.
+ absl::optional<SearchMetadata> last_search_metadata_persisted =
+ service()->last_search_metadata_persisted();
+ ASSERT_FALSE(last_search_metadata_persisted.has_value());
+
// The title should be what is requested to be annotated.
absl::optional<HistoryVisit> last_annotation_request =
service()->last_annotation_request();
EXPECT_TRUE(last_annotation_request.has_value());
- EXPECT_EQ(last_annotation_request->url, GURL("http://www.foo.com/someurl"));
+ EXPECT_EQ(last_annotation_request->url,
+ GURL("http://default-engine.com/search?q=a"));
EXPECT_EQ(last_annotation_request->text_to_annotate, "Title");
- service()->ClearLastAnnotationRequest();
-
- // Update title again - make sure we don't reannotate for same page.
- web_contents()->UpdateTitleForEntry(controller().GetLastCommittedEntry(),
- u"newtitle");
- EXPECT_FALSE(service()->last_annotation_request());
+ histogram_tester.ExpectUniqueSample(
+ "OptimizationGuide.PageContentAnnotations."
+ "TemplateURLServiceLoadedAtNavigationFinish",
+ false, 1);
}
class PageContentAnnotationsWebContentsObserverRemotePageEntitiesTest
@@ -527,8 +519,8 @@ class PageContentAnnotationsWebContentsObserverRemotePageEntitiesTest
public:
PageContentAnnotationsWebContentsObserverRemotePageEntitiesTest() {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
- features::kPageContentAnnotations,
- {{"fetch_remote_page_entities", "true"}});
+ features::kRemotePageMetadata,
+ {{"persist_page_entities", "true"}, {"persist_page_metadata", "true"}});
}
private:
@@ -588,4 +580,15 @@ TEST_F(PageContentAnnotationsWebContentsObserverRemotePageEntitiesTest,
history::VisitContentModelAnnotations::Category("entity1", 50)));
}
+TEST_F(PageContentAnnotationsWebContentsObserverRemotePageEntitiesTest,
+ RequestsToPersistIfHasPageMetadata) {
+ // Navigate.
+ content::NavigationSimulator::NavigateAndCommitFromBrowser(
+ web_contents(), GURL("http://hasmetadata.com/"));
+
+ absl::optional<proto::PageEntitiesMetadata> metadata =
+ service()->last_page_metadata_persisted();
+ EXPECT_EQ(metadata->alternative_title(), "alternative title");
+}
+
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/content/browser/page_text_dump_result.cc b/chromium/components/optimization_guide/content/browser/page_text_dump_result.cc
index a2093461f7c..cb1810bb55f 100644
--- a/chromium/components/optimization_guide/content/browser/page_text_dump_result.cc
+++ b/chromium/components/optimization_guide/content/browser/page_text_dump_result.cc
@@ -115,7 +115,10 @@ FrameTextDumpResult FrameTextDumpResult::CompleteWithContents(
DCHECK(!IsCompleted());
FrameTextDumpResult copy = *this;
+ // Always trim whitespace from |contents| because it can non-deterministically
+ // have trailing whitespace which makes testing and parsing harder.
copy.contents_ = contents;
+ base::TrimWhitespace(contents, base::TRIM_ALL, &(copy.contents_.value()));
return copy;
}
diff --git a/chromium/components/optimization_guide/content/browser/page_text_dump_result_unittest.cc b/chromium/components/optimization_guide/content/browser/page_text_dump_result_unittest.cc
index 420a55a8df7..3b0f6c2f1ab 100644
--- a/chromium/components/optimization_guide/content/browser/page_text_dump_result_unittest.cc
+++ b/chromium/components/optimization_guide/content/browser/page_text_dump_result_unittest.cc
@@ -127,6 +127,18 @@ TEST(FrameTextDumpResultTest, Ordering) {
}));
}
+TEST(PageTextDumpResultTest, WhitespaceTrimmed) {
+ FrameTextDumpResult frame_result =
+ FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+ content::GlobalRenderFrameHostId(1, 2),
+ /*amp_frame=*/false,
+ /*unique_navigation_id=*/0)
+ .CompleteWithContents(u" abc\n\n");
+
+ ASSERT_TRUE(frame_result.contents());
+ EXPECT_EQ(*frame_result.contents(), u"abc");
+}
+
TEST(PageTextDumpResultTest, Empty) {
PageTextDumpResult page_result;
EXPECT_TRUE(page_result.empty());
diff --git a/chromium/components/optimization_guide/content/browser/page_text_observer.cc b/chromium/components/optimization_guide/content/browser/page_text_observer.cc
index 6bfaf5bfa43..0d12ceb5e91 100644
--- a/chromium/components/optimization_guide/content/browser/page_text_observer.cc
+++ b/chromium/components/optimization_guide/content/browser/page_text_observer.cc
@@ -373,12 +373,11 @@ void PageTextObserver::DidFinishNavigation(content::NavigationHandle* handle) {
}
bool PageTextObserver::IsOOPIF(content::RenderFrameHost* rfh) const {
- return rfh->GetProcess()->GetID() !=
- rfh->GetMainFrame()->GetProcess()->GetID();
+ return rfh->IsCrossProcessSubframe();
}
void PageTextObserver::RenderFrameCreated(content::RenderFrameHost* rfh) {
- if (!IsOOPIF(rfh)) {
+ if (!IsOOPIF(rfh) || !rfh->GetPage().IsPrimary()) {
return;
}
diff --git a/chromium/components/optimization_guide/content/browser/page_text_observer_unittest.cc b/chromium/components/optimization_guide/content/browser/page_text_observer_unittest.cc
index e792f40a844..97f8acf5baa 100644
--- a/chromium/components/optimization_guide/content/browser/page_text_observer_unittest.cc
+++ b/chromium/components/optimization_guide/content/browser/page_text_observer_unittest.cc
@@ -205,7 +205,8 @@ class TestPageTextObserver : public PageTextObserver {
}
void CallDidFinishLoad() {
- PageTextObserver::DidFinishLoad(web_contents()->GetMainFrame(), GURL());
+ PageTextObserver::DidFinishLoad(web_contents()->GetPrimaryMainFrame(),
+ GURL());
}
private:
@@ -273,7 +274,7 @@ TEST_F(PageTextObserverTest, ConsumerNotCalledSubframe) {
content::NavigationSimulator::NavigateAndCommitFromDocument(
GURL("http://subframe.com"),
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
EXPECT_FALSE(consumer.was_called());
@@ -1112,7 +1113,7 @@ TEST_F(PageTextObserverWithPrerenderTest,
// Activate the prerendered page.
content::NavigationSimulator::NavigateAndCommitFromDocument(
- prerender_url, web_contents()->GetMainFrame());
+ prerender_url, web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(prerender_frame->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kActive);
EXPECT_TRUE(consumer.was_called());
@@ -1120,4 +1121,154 @@ TEST_F(PageTextObserverWithPrerenderTest,
EXPECT_EQ(observer()->outstanding_requests(), 0U);
}
+TEST_F(PageTextObserverWithPrerenderTest, AMPRequestedOnOOPIFInPrerendering) {
+ TestConsumer consumer;
+ observer()->AddConsumer(&consumer);
+
+ consumer.PopulateRequest(
+ /*max_size=*/1024,
+ /*events=*/{mojom::TextDumpEvent::kFirstLayout},
+ /*request_amp=*/true);
+
+ NavigateAndCommit(GURL("http://www.test.com"));
+
+ consumer.Reset();
+
+ // Add a prerender page.
+ const GURL prerender_url = GURL("http://www.test.com/?prerender");
+ content::RenderFrameHost* prerender_frame = AddPrerender(prerender_url);
+
+ FakePageTextService fake_renderer_service;
+ fake_renderer_service.SetRemoteResponsesForEvent(
+ mojom::TextDumpEvent::kFirstLayout, {
+ u"abc",
+ u"def",
+ absl::nullopt,
+ });
+ blink::AssociatedInterfaceProvider* remote_interfaces =
+ prerender_frame->GetRemoteAssociatedInterfaces();
+ remote_interfaces->OverrideBinderForTesting(
+ mojom::PageTextService::Name_,
+ base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+ base::Unretained(&fake_renderer_service)));
+ EXPECT_FALSE(consumer.was_called());
+
+ // Add an OOPIF subframe.
+ content::RenderFrameHost* oopif_subframe =
+ content::RenderFrameHostTester::For(prerender_frame)
+ ->AppendChild("subframe");
+ observer()->SetIsOOPIF(oopif_subframe, true);
+
+ FakePageTextService subframe_fake_renderer_service;
+ blink::AssociatedInterfaceProvider* subframe_remote_interfaces =
+ oopif_subframe->GetRemoteAssociatedInterfaces();
+ subframe_remote_interfaces->OverrideBinderForTesting(
+ mojom::PageTextService::Name_,
+ base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+ base::Unretained(&subframe_fake_renderer_service)));
+ subframe_fake_renderer_service.SetRemoteResponsesForEvent(
+ mojom::TextDumpEvent::kFinishedLoad, {
+ u"amp",
+ absl::nullopt,
+ });
+
+ observer()->RenderFrameCreated(oopif_subframe);
+ observer()->CallDidFinishLoad();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(consumer.was_called());
+ EXPECT_FALSE(consumer.result());
+}
+
+class PageTextObserverFencedFramesTest : public PageTextObserverTest {
+ public:
+ PageTextObserverFencedFramesTest() {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ blink::features::kFencedFrames, {{"implementation_type", "mparch"}});
+ }
+ ~PageTextObserverFencedFramesTest() override = default;
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(PageTextObserverFencedFramesTest, AMPRequestedOnOOPIFInFencedFrame) {
+ TestConsumer consumer;
+ observer()->AddConsumer(&consumer);
+
+ consumer.PopulateRequest(
+ /*max_size=*/1024,
+ /*events=*/{mojom::TextDumpEvent::kFirstLayout},
+ /*request_amp=*/true);
+
+ FakePageTextService fake_renderer_service;
+ fake_renderer_service.SetRemoteResponsesForEvent(
+ mojom::TextDumpEvent::kFirstLayout, {
+ u"abc",
+ u"def",
+ absl::nullopt,
+ });
+
+ blink::AssociatedInterfaceProvider* remote_interfaces =
+ main_rfh()->GetRemoteAssociatedInterfaces();
+ remote_interfaces->OverrideBinderForTesting(
+ mojom::PageTextService::Name_,
+ base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+ base::Unretained(&fake_renderer_service)));
+
+ content::NavigationSimulator::NavigateAndCommitFromBrowser(
+ web_contents(), GURL("http://test.com"));
+ EXPECT_TRUE(consumer.was_called());
+
+ content::RenderFrameHost* fenced_frame_rfh =
+ content::RenderFrameHostTester::For(main_rfh())->AppendFencedFrame();
+ GURL kFencedFrameUrl("http://fencedframe.com");
+ std::unique_ptr<content::NavigationSimulator> navigation_simulator =
+ content::NavigationSimulator::CreateRendererInitiated(kFencedFrameUrl,
+ fenced_frame_rfh);
+ navigation_simulator->Commit();
+ fenced_frame_rfh = navigation_simulator->GetFinalRenderFrameHost();
+
+ // Add an OOPIF subframe.
+ content::RenderFrameHost* oopif_subframe =
+ content::RenderFrameHostTester::For(fenced_frame_rfh)
+ ->AppendChild("subframe");
+ observer()->SetIsOOPIF(oopif_subframe, true);
+
+ FakePageTextService subframe_fake_renderer_service;
+ blink::AssociatedInterfaceProvider* subframe_remote_interfaces =
+ oopif_subframe->GetRemoteAssociatedInterfaces();
+ subframe_remote_interfaces->OverrideBinderForTesting(
+ mojom::PageTextService::Name_,
+ base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+ base::Unretained(&subframe_fake_renderer_service)));
+ subframe_fake_renderer_service.SetRemoteResponsesForEvent(
+ mojom::TextDumpEvent::kFinishedLoad, {
+ u"amp",
+ absl::nullopt,
+ });
+
+ observer()->RenderFrameCreated(oopif_subframe);
+ observer()->CallDidFinishLoad();
+ consumer.WaitForPageText();
+
+ EXPECT_THAT(
+ fake_renderer_service.requests(),
+ ::testing::UnorderedElementsAreArray({
+ mojom::PageTextDumpRequest(1024U, mojom::TextDumpEvent::kFirstLayout),
+ }));
+ EXPECT_TRUE(subframe_fake_renderer_service.requests().empty());
+
+ ASSERT_TRUE(consumer.result());
+ EXPECT_THAT(
+ consumer.result()->frame_results(),
+ ::testing::UnorderedElementsAreArray({
+ MakeFrameDump(
+ mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
+ /*amp_frame=*/false,
+ web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
+ u"abcdef"),
+ }));
+}
+
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/content/browser/test_page_content_annotator.cc b/chromium/components/optimization_guide/content/browser/test_page_content_annotator.cc
index c03689974e3..563cc9c4480 100644
--- a/chromium/components/optimization_guide/content/browser/test_page_content_annotator.cc
+++ b/chromium/components/optimization_guide/content/browser/test_page_content_annotator.cc
@@ -12,6 +12,8 @@ TestPageContentAnnotator::TestPageContentAnnotator() = default;
void TestPageContentAnnotator::Annotate(BatchAnnotationCallback callback,
const std::vector<std::string>& inputs,
AnnotationType annotation_type) {
+ annotation_requests_.emplace_back(std::make_pair(inputs, annotation_type));
+
std::vector<BatchAnnotationResult> results;
if (annotation_type == AnnotationType::kPageTopics) {
@@ -90,9 +92,15 @@ void TestPageContentAnnotator::UseVisibilityScores(
visibility_scores_for_input_ = visibility_scores_for_input;
}
+bool TestPageContentAnnotator::ModelRequestedForType(
+ AnnotationType type) const {
+ return model_requests_.contains(type);
+}
+
void TestPageContentAnnotator::RequestAndNotifyWhenModelAvailable(
AnnotationType type,
base::OnceCallback<void(bool)> callback) {
+ model_requests_.insert(type);
std::move(callback).Run(true);
}
diff --git a/chromium/components/optimization_guide/content/browser/test_page_content_annotator.h b/chromium/components/optimization_guide/content/browser/test_page_content_annotator.h
index 4004d47b226..713e9450275 100644
--- a/chromium/components/optimization_guide/content/browser/test_page_content_annotator.h
+++ b/chromium/components/optimization_guide/content/browser/test_page_content_annotator.h
@@ -40,6 +40,16 @@ class TestPageContentAnnotator : public PageContentAnnotator {
const absl::optional<ModelInfo>& model_info,
const base::flat_map<std::string, double>& visibility_scores_for_input);
+ // Returns true iff |RequestAndNotifyWhenModelAvailable| was called for
+ // |type|.
+ bool ModelRequestedForType(AnnotationType type) const;
+
+ using AnnotateInputsAndType =
+ std::pair<std::vector<std::string>, AnnotationType>;
+ const std::vector<AnnotateInputsAndType>& annotation_requests() const {
+ return annotation_requests_;
+ }
+
// PageContentAnnotator:
void Annotate(BatchAnnotationCallback callback,
const std::vector<std::string>& inputs,
@@ -60,6 +70,10 @@ class TestPageContentAnnotator : public PageContentAnnotator {
absl::optional<ModelInfo> visibility_scores_model_info_;
base::flat_map<std::string, double> visibility_scores_for_input_;
+
+ std::vector<AnnotateInputsAndType> annotation_requests_;
+
+ base::flat_set<AnnotationType> model_requests_;
};
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/BUILD.gn b/chromium/components/optimization_guide/core/BUILD.gn
index 9abc153e55c..1b844bf6289 100644
--- a/chromium/components/optimization_guide/core/BUILD.gn
+++ b/chromium/components/optimization_guide/core/BUILD.gn
@@ -5,7 +5,9 @@
if (is_android) {
import("//build/config/android/rules.gni")
}
+import("//build/buildflag_header.gni")
import("//components/optimization_guide/features.gni")
+import("//third_party/tflite/features.gni")
static_library("bloomfilter") {
sources = [
@@ -84,6 +86,8 @@ if (build_with_tflite_lib) {
"tflite_op_resolver.h",
]
deps = [
+ ":features",
+ "//components/optimization_guide:machine_learning_tflite_buildflags",
"//third_party/tflite",
"//third_party/tflite:tflite_public_headers",
]
@@ -101,9 +105,11 @@ static_library("features") {
"optimization_guide_prefs.h",
"optimization_guide_switches.cc",
"optimization_guide_switches.h",
+ "page_content_annotation_type.cc",
+ "page_content_annotation_type.h",
]
+ public_deps = [ "//base" ]
deps = [
- "//base",
"//components/optimization_guide:machine_learning_tflite_buildflags",
"//components/optimization_guide/proto:optimization_guide_proto",
"//components/prefs",
@@ -183,6 +189,7 @@ static_library("core") {
"model_validator.h",
"page_content_annotation_job_executor.cc",
"page_content_annotation_job_executor.h",
+ "page_entities_model_executor.cc",
"page_entities_model_executor.h",
"page_topics_model_executor.cc",
"page_topics_model_executor.h",
@@ -247,6 +254,8 @@ static_library("prediction") {
"prediction_model_download_manager.cc",
"prediction_model_download_manager.h",
"prediction_model_download_observer.h",
+ "prediction_model_override.cc",
+ "prediction_model_override.h",
]
deps = [
"//components/crx_file",
@@ -319,6 +328,8 @@ if (is_ios) {
"optimization_guide_switches.h",
"optimization_guide_test_util.cc",
"optimization_guide_test_util.h",
+ "page_content_annotation_type.cc",
+ "page_content_annotation_type.h",
]
deps = [
"//base",
@@ -342,6 +353,7 @@ source_set("unit_tests") {
"insertion_ordered_set_unittest.cc",
"local_page_entities_metadata_provider_unittest.cc",
"model_handler_unittest.cc",
+ "model_util_unittest.cc",
"noisy_metrics_recorder_unittest.cc",
"optimization_filter_unittest.cc",
"optimization_guide_features_unittest.cc",
diff --git a/chromium/components/optimization_guide/core/base_model_executor.h b/chromium/components/optimization_guide/core/base_model_executor.h
index 78e064b4256..0ee9c084dcb 100644
--- a/chromium/components/optimization_guide/core/base_model_executor.h
+++ b/chromium/components/optimization_guide/core/base_model_executor.h
@@ -7,6 +7,7 @@
#include "components/optimization_guide/core/base_model_executor_helpers.h"
#include "components/optimization_guide/core/execution_status.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/core/tflite_model_executor.h"
#include "components/optimization_guide/core/tflite_op_resolver.h"
#include "third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/base_task_api.h"
@@ -29,6 +30,21 @@ class BaseModelExecutor : public TFLiteModelExecutor<OutputType, InputTypes...>,
BaseModelExecutor(const BaseModelExecutor&) = delete;
BaseModelExecutor& operator=(const BaseModelExecutor&) = delete;
+ public:
+ // TFLiteModelExecutor:
+ void InitializeAndMoveToExecutionThread(
+ absl::optional<base::TimeDelta> model_inference_timeout,
+ proto::OptimizationTarget optimization_target,
+ scoped_refptr<base::SequencedTaskRunner> execution_task_runner,
+ scoped_refptr<base::SequencedTaskRunner> reply_task_runner) override {
+ num_threads_ = features::OverrideNumThreadsForOptTarget(optimization_target)
+ .value_or(-1);
+ TFLiteModelExecutor<OutputType, InputTypes...>::
+ InitializeAndMoveToExecutionThread(
+ model_inference_timeout, optimization_target, execution_task_runner,
+ reply_task_runner);
+ }
+
protected:
absl::optional<OutputType> Execute(ModelExecutionTask* execution_task,
ExecutionStatus* out_status,
@@ -53,9 +69,12 @@ class BaseModelExecutor : public TFLiteModelExecutor<OutputType, InputTypes...>,
return nullptr;
}
+ auto compute_settings = tflite::proto::ComputeSettings();
+ compute_settings.mutable_tflite_settings()
+ ->mutable_cpu_settings()
+ ->set_num_threads(num_threads_);
absl::Status interpreter_status =
- tflite_engine->InitInterpreter(tflite::proto::ComputeSettings(),
- /*num_threads=*/1);
+ tflite_engine->InitInterpreter(compute_settings);
if (!interpreter_status.ok()) {
DLOG(ERROR) << "Failed to initialize model interpreter: "
<< interpreter_status.ToString();
@@ -73,6 +92,10 @@ class BaseModelExecutor : public TFLiteModelExecutor<OutputType, InputTypes...>,
InputTypes... input) override = 0;
absl::optional<OutputType> Postprocess(
const std::vector<const TfLiteTensor*>& output_tensors) override = 0;
+
+ private:
+ // -1 tells TFLite to use its own default number of threads.
+ int num_threads_ = -1;
};
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/bert_model_executor.cc b/chromium/components/optimization_guide/core/bert_model_executor.cc
index f103d44d1f1..caa617e1110 100644
--- a/chromium/components/optimization_guide/core/bert_model_executor.cc
+++ b/chromium/components/optimization_guide/core/bert_model_executor.cc
@@ -6,6 +6,7 @@
#include "base/trace_event/trace_event.h"
#include "components/optimization_guide/core/model_util.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/core/tflite_op_resolver.h"
#include "third_party/tflite_support/src/tensorflow_lite_support/cc/task/text/bert_nl_classifier.h"
@@ -13,7 +14,9 @@ namespace optimization_guide {
BertModelExecutor::BertModelExecutor(
proto::OptimizationTarget optimization_target)
- : optimization_target_(optimization_target) {}
+ : optimization_target_(optimization_target),
+ num_threads_(features::OverrideNumThreadsForOptTarget(optimization_target)
+ .value_or(-1)) {}
BertModelExecutor::~BertModelExecutor() = default;
absl::optional<std::vector<tflite::task::core::Category>>
@@ -30,7 +33,7 @@ BertModelExecutor::Execute(ModelExecutionTask* execution_task,
auto status_or_result =
static_cast<tflite::task::text::BertNLClassifier*>(execution_task)
- ->Classify(input);
+ ->ClassifyText(input);
if (absl::IsCancelled(status_or_result.status())) {
*out_status = ExecutionStatus::kErrorCancelled;
return absl::nullopt;
@@ -51,6 +54,11 @@ BertModelExecutor::BuildModelExecutionTask(base::MemoryMappedFile* model_file,
->mutable_model_file()
->mutable_file_content() = std::string(
reinterpret_cast<const char*>(model_file->data()), model_file->length());
+ options.mutable_base_options()
+ ->mutable_compute_settings()
+ ->mutable_tflite_settings()
+ ->mutable_cpu_settings()
+ ->set_num_threads(num_threads_);
auto maybe_nl_classifier =
tflite::task::text::BertNLClassifier::CreateFromOptions(
std::move(options), std::make_unique<TFLiteOpResolver>());
diff --git a/chromium/components/optimization_guide/core/bert_model_executor.h b/chromium/components/optimization_guide/core/bert_model_executor.h
index 823a9362686..c9b9db608b0 100644
--- a/chromium/components/optimization_guide/core/bert_model_executor.h
+++ b/chromium/components/optimization_guide/core/bert_model_executor.h
@@ -33,6 +33,9 @@ class BertModelExecutor
private:
const proto::OptimizationTarget optimization_target_;
+
+ // -1 tells TFLite to use its own default number of threads.
+ const int num_threads_ = -1;
};
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/bert_model_executor_unittest.cc b/chromium/components/optimization_guide/core/bert_model_executor_unittest.cc
index 25bf24d4dd0..1d52a680598 100644
--- a/chromium/components/optimization_guide/core/bert_model_executor_unittest.cc
+++ b/chromium/components/optimization_guide/core/bert_model_executor_unittest.cc
@@ -5,7 +5,9 @@
#include "components/optimization_guide/core/bert_model_handler.h"
#include "base/path_service.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/core/test_model_info_builder.h"
#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -14,6 +16,11 @@ namespace optimization_guide {
class BertModelExecutorTest : public testing::Test {
public:
+ BertModelExecutorTest() {
+ scoped_feature_list_.InitAndDisableFeature(
+ features::kPreventLongRunningPredictionModels);
+ }
+
void SetUp() override {
optimization_guide_model_provider_ =
std::make_unique<TestOptimizationGuideModelProvider>();
@@ -54,6 +61,7 @@ class BertModelExecutorTest : public testing::Test {
BertModelHandler* model_handler() { return model_handler_.get(); }
private:
+ base::test::ScopedFeatureList scoped_feature_list_;
base::test::TaskEnvironment task_environment_;
std::unique_ptr<TestOptimizationGuideModelProvider>
diff --git a/chromium/components/optimization_guide/core/bert_model_handler.cc b/chromium/components/optimization_guide/core/bert_model_handler.cc
index 0da838cca30..ec8ed4b0dcb 100644
--- a/chromium/components/optimization_guide/core/bert_model_handler.cc
+++ b/chromium/components/optimization_guide/core/bert_model_handler.cc
@@ -18,6 +18,7 @@ BertModelHandler::BertModelHandler(
model_provider,
background_task_runner,
std::make_unique<BertModelExecutor>(optimization_target),
+ /*model_inference_timeout=*/absl::nullopt,
optimization_target,
model_metadata) {}
diff --git a/chromium/components/optimization_guide/core/entity_annotator_native_library.cc b/chromium/components/optimization_guide/core/entity_annotator_native_library.cc
index 9be3ff6cd40..32350cb569b 100644
--- a/chromium/components/optimization_guide/core/entity_annotator_native_library.cc
+++ b/chromium/components/optimization_guide/core/entity_annotator_native_library.cc
@@ -235,6 +235,26 @@ void EntityAnnotatorNativeLibrary::LoadFunctions() {
native_library_,
"OptimizationGuideEntityMetadataGetHumanReadableCategoryScoreAtIn"
"dex"));
+ entity_metadata_get_human_readable_aliases_count_func_ =
+ reinterpret_cast<EntityMetadataGetHumanReadableAliasesCountFunc>(
+ base::GetFunctionPointerFromNativeLibrary(
+ native_library_,
+ "OptimizationGuideEntityMetadataGetHumanReadableAliasesCount"));
+ entity_metadata_get_human_readable_alias_at_index_func_ =
+ reinterpret_cast<EntityMetadataGetHumanReadableAliasAtIndexFunc>(
+ base::GetFunctionPointerFromNativeLibrary(
+ native_library_,
+ "OptimizationGuideEntityMetadataGetHumanReadableAliasAtIndex"));
+ entity_metadata_get_collections_count_func_ =
+ reinterpret_cast<EntityMetadataGetCollectionsCountFunc>(
+ base::GetFunctionPointerFromNativeLibrary(
+ native_library_,
+ "OptimizationGuideEntityMetadataGetCollectionsCount"));
+ entity_metadata_get_collection_at_index_func_ =
+ reinterpret_cast<EntityMetadataGetCollectionAtIndexFunc>(
+ base::GetFunctionPointerFromNativeLibrary(
+ native_library_,
+ "OptimizationGuideEntityMetadataGetCollectionAtIndex"));
}
DISABLE_CFI_ICALL
@@ -255,7 +275,11 @@ bool EntityAnnotatorNativeLibrary::IsValid() const {
entity_metadata_get_human_readable_name_func_ &&
entity_metadata_get_human_readable_categories_count_func_ &&
entity_metadata_get_human_readable_category_name_at_index_func_ &&
- entity_metadata_get_human_readable_category_score_at_index_func_;
+ entity_metadata_get_human_readable_category_score_at_index_func_ &&
+ entity_metadata_get_human_readable_aliases_count_func_ &&
+ entity_metadata_get_human_readable_alias_at_index_func_ &&
+ entity_metadata_get_collections_count_func_ &&
+ entity_metadata_get_collection_at_index_func_;
}
DISABLE_CFI_ICALL
@@ -294,10 +318,11 @@ void* EntityAnnotatorNativeLibrary::CreateEntityAnnotator(
recorder.set_status(EntityAnnotatorCreationStatus::kInitializationFailure);
DeleteEntityAnnotator(entity_annotator);
entity_annotator = nullptr;
+ } else {
+ recorder.set_status(EntityAnnotatorCreationStatus::kSuccess);
}
options_delete_func_(options);
- recorder.set_status(EntityAnnotatorCreationStatus::kSuccess);
return entity_annotator;
}
@@ -494,6 +519,23 @@ EntityMetadata EntityAnnotatorNativeLibrary::
og_entity_metadata, i);
entity_metadata.human_readable_categories[category_name] = category_score;
}
+
+ int32_t human_readable_aliases_count =
+ entity_metadata_get_human_readable_aliases_count_func_(
+ og_entity_metadata);
+ for (int32_t i = 0; i < human_readable_aliases_count; i++) {
+ entity_metadata.human_readable_aliases.push_back(
+ entity_metadata_get_human_readable_alias_at_index_func_(
+ og_entity_metadata, i));
+ }
+
+ int32_t collections_count =
+ entity_metadata_get_collections_count_func_(og_entity_metadata);
+ for (int32_t i = 0; i < collections_count; i++) {
+ std::string collection =
+ entity_metadata_get_collection_at_index_func_(og_entity_metadata, i);
+ entity_metadata.collections.push_back(collection);
+ }
return entity_metadata;
}
diff --git a/chromium/components/optimization_guide/core/entity_annotator_native_library.h b/chromium/components/optimization_guide/core/entity_annotator_native_library.h
index a397ed889d2..aeb99a0c517 100644
--- a/chromium/components/optimization_guide/core/entity_annotator_native_library.h
+++ b/chromium/components/optimization_guide/core/entity_annotator_native_library.h
@@ -179,6 +179,21 @@ class EntityAnnotatorNativeLibrary {
EntityMetadataGetHumanReadableCategoryScoreAtIndexFunc
entity_metadata_get_human_readable_category_score_at_index_func_ =
nullptr;
+ using EntityMetadataGetHumanReadableAliasesCountFunc =
+ int32_t (*)(const void*);
+ EntityMetadataGetHumanReadableAliasesCountFunc
+ entity_metadata_get_human_readable_aliases_count_func_ = nullptr;
+ using EntityMetadataGetHumanReadableAliasAtIndexFunc =
+ const char* (*)(const void*, int32_t);
+ EntityMetadataGetHumanReadableAliasAtIndexFunc
+ entity_metadata_get_human_readable_alias_at_index_func_ = nullptr;
+ using EntityMetadataGetCollectionsCountFunc = int32_t (*)(const void*);
+ EntityMetadataGetCollectionsCountFunc
+ entity_metadata_get_collections_count_func_ = nullptr;
+ using EntityMetadataGetCollectionAtIndexFunc = const char* (*)(const void*,
+ int32_t);
+ EntityMetadataGetCollectionAtIndexFunc
+ entity_metadata_get_collection_at_index_func_ = nullptr;
};
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/entity_metadata.cc b/chromium/components/optimization_guide/core/entity_metadata.cc
index 720cf322ea8..06d5d974b7e 100644
--- a/chromium/components/optimization_guide/core/entity_metadata.cc
+++ b/chromium/components/optimization_guide/core/entity_metadata.cc
@@ -8,31 +8,278 @@
#include <string>
#include <vector>
+#include "base/json/json_writer.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
namespace optimization_guide {
+PageEntityCollection GetPageEntityCollectionForString(
+ const std::string& collection_str) {
+ // A const map of raw entity collection strings to enum values.
+ //
+ // The map keys need to be kept consistent with the source of the original
+ // collection list:
+ // https://source.corp.google.com/piper///depot/google3/production/borg/webref/ondevice/model-building-conf-chrome.gcl?q=labelling_collections_hrids
+ static const base::flat_map<std::string, PageEntityCollection>
+ kPageEntityCollectionMap = {
+ {"/collection/accommodations", PageEntityCollection::kAccommodations},
+ {"/collection/actors", PageEntityCollection::kActors},
+ {"/collection/airports", PageEntityCollection::kAirports},
+ {"/collection/anatomical_structures",
+ PageEntityCollection::kAnatomicalStructures},
+ {"/collection/artworks", PageEntityCollection::kArtworks},
+ {"/collection/athletes", PageEntityCollection::kAthletes},
+ {"/collection/authors", PageEntityCollection::kAuthors},
+ {"/collection/book_editions", PageEntityCollection::kBookEditions},
+ {"/collection/business_operations",
+ PageEntityCollection::kBusinessOperations},
+ {"/collection/cars", PageEntityCollection::kCars},
+ {"/collection/causes_of_death", PageEntityCollection::kCausesOfDeath},
+ {"/collection/celestial_object_with_coordinate_systems",
+ PageEntityCollection::kCelestialObjectWithCoordinateSystems},
+ {"/collection/chemical_compounds",
+ PageEntityCollection::kChemicalCompounds},
+ {"/collection/consumer_products",
+ PageEntityCollection::kConsumerProducts},
+ {"/collection/cuisines", PageEntityCollection::kCuisines},
+ {"/collection/culinary_measures",
+ PageEntityCollection::kCulinaryMeasures},
+ {"/collection/currencies", PageEntityCollection::kCurrencies},
+ {"/collection/diets", PageEntityCollection::kDiets},
+ {"/collection/disease_or_medical_conditions",
+ PageEntityCollection::kDiseaseOrMedicalConditions},
+ {"/collection/educational_institutions",
+ PageEntityCollection::kEducationalInstitutions},
+ {"/collection/employers", PageEntityCollection::kEmployers},
+ {"/collection/events", PageEntityCollection::kEvents},
+ {"/collection/fictional_characters",
+ PageEntityCollection::kFictionalCharacters},
+ {"/collection/film_actors", PageEntityCollection::kFilmActors},
+ {"/collection/film_screening_venues",
+ PageEntityCollection::kFilmScreeningVenues},
+ {"/collection/film_series", PageEntityCollection::kFilmSeries},
+ {"/collection/films", PageEntityCollection::kFilms},
+ {"/collection/foods", PageEntityCollection::kFoods},
+ {"/collection/garments", PageEntityCollection::kGarments},
+ {"/collection/geo/business_chain",
+ PageEntityCollection::kGeoBusinessChain},
+ {"/collection/geo/establishment",
+ PageEntityCollection::kGeoEstablishment},
+ {"/collection/geo/locality", PageEntityCollection::kGeoLocality},
+ {"/collection/geo/natural_feature",
+ PageEntityCollection::kGeoNaturalFeature},
+ {"/collection/geo/political", PageEntityCollection::kGeoPolitical},
+ {"/collection/holidays", PageEntityCollection::kHolidays},
+ {"/collection/human_languages",
+ PageEntityCollection::kHumanLanguages},
+ {"/collection/software", PageEntityCollection::kSoftware},
+ {"/collection/job_titles", PageEntityCollection::kJobTitles},
+ {"/collection/literary_series",
+ PageEntityCollection::kLiterarySeries},
+ {"/collection/local_shopping_buyables",
+ PageEntityCollection::kLocalShoppingBuyables},
+ {"/collection/materials", PageEntityCollection::kMaterials},
+ {"/collection/medical_treatments",
+ PageEntityCollection::kMedicalTreatments},
+ {"/collection/models", PageEntityCollection::kModels},
+ {"/collection/music_group_members",
+ PageEntityCollection::kMusicGroupMembers},
+ {"/collection/musical_albums", PageEntityCollection::kMusicalAlbums},
+ {"/collection/musical_artists",
+ PageEntityCollection::kMusicalArtists},
+ {"/collection/musical_genres", PageEntityCollection::kMusicalGenres},
+ {"/collection/musical_groups", PageEntityCollection::kMusicalGroups},
+ {"/collection/musical_recordings",
+ PageEntityCollection::kMusicalRecordings},
+ {"/collection/musical_releases",
+ PageEntityCollection::kMusicalReleases},
+ {"/collection/musicians", PageEntityCollection::kMusicians},
+ {"/collection/organism_classifications",
+ PageEntityCollection::kOrganismClassifications},
+ {"/collection/organizations", PageEntityCollection::kOrganizations},
+ {"/collection/people", PageEntityCollection::kPeople},
+ {"/collection/periodicals", PageEntityCollection::kPeriodicals},
+ {"/collection/politicians", PageEntityCollection::kPoliticians},
+ {"/collection/recording_clusters",
+ PageEntityCollection::kRecordingClusters},
+ {"/collection/religions", PageEntityCollection::kReligions},
+ {"/collection/restaurants", PageEntityCollection::kRestaurants},
+ {"/collection/ride_offering_services",
+ PageEntityCollection::kRideOfferingServices},
+ {"/collection/shopping_centers",
+ PageEntityCollection::kShoppingCenters},
+ {"/collection/social_network_service_websites",
+ PageEntityCollection::kSocialNetworkServiceWebsites},
+ {"/collection/sports", PageEntityCollection::kSports},
+ {"/collection/sports_teams", PageEntityCollection::kSportsTeams},
+ {"/collection/structures", PageEntityCollection::kStructures},
+ {"/collection/tourist_attractions",
+ PageEntityCollection::kTouristAttractions},
+ {"/collection/travel_destinations",
+ PageEntityCollection::kTravelDestinations},
+ {"/collection/tv_actors", PageEntityCollection::kTvActors},
+ {"/collection/tv_episodes", PageEntityCollection::kTvEpisodes},
+ {"/collection/tv_programs", PageEntityCollection::kTvPrograms},
+ {"/collection/venues", PageEntityCollection::kVenues},
+ {"/collection/video_games", PageEntityCollection::kVideoGames},
+ {"/collection/websites", PageEntityCollection::kWebsites},
+ {"/collection/written_works", PageEntityCollection::kWrittenWorks}};
+
+ const auto it = kPageEntityCollectionMap.find(collection_str);
+ return it != kPageEntityCollectionMap.end() ? it->second
+ : PageEntityCollection::kUnknown;
+}
+
+std::string GetPageEntityCollectionLabel(const std::string& collection_str) {
+ static constexpr char kUnknown[] = "Unknown";
+ // A const map of raw entity collection strings to labels.
+ //
+ // The map keys need to be kept consistent with the source of the original
+ // collection list:
+ // https://source.corp.google.com/piper///depot/google3/production/borg/webref/ondevice/model-building-conf-chrome.gcl?q=labelling_collections_hrids
+ static const base::flat_map<std::string, std::string>
+ kPageEntityCollectionLabelMap = {
+ {"/collection/accommodations", "Accommodations"},
+ {"/collection/actors", "Actors"},
+ {"/collection/airports", "Airports"},
+ {"/collection/anatomical_structures", "AnatomicalStructures"},
+ {"/collection/artworks", "Artworks"},
+ {"/collection/athletes", "Athletes"},
+ {"/collection/authors", "Authors"},
+ {"/collection/book_editions", "BookEditions"},
+ {"/collection/business_operations", "BusinessOperations"},
+ {"/collection/cars", "Cars"},
+ {"/collection/causes_of_death", "CausesOfDeath"},
+ {"/collection/celestial_object_with_coordinate_systems",
+ "CelestialObjectWithCoordinateSystems"},
+ {"/collection/chemical_compounds", "HemicalCompounds"},
+ {"/collection/consumer_products", "ConsumerProducts"},
+ {"/collection/cuisines", "Cuisines"},
+ {"/collection/culinary_measures", "CulinaryMeasures"},
+ {"/collection/currencies", "Currencies"},
+ {"/collection/diets", "Diets"},
+ {"/collection/disease_or_medical_conditions",
+ "DiseaseOrMedicalConditions"},
+ {"/collection/educational_institutions", "EducationalInstitutions"},
+ {"/collection/employers", "Employers"},
+ {"/collection/events", "Events"},
+ {"/collection/fictional_characters", "FictionalCharacters"},
+ {"/collection/film_actors", "FilmActors"},
+ {"/collection/film_screening_venues", "FilmScreeningVenues"},
+ {"/collection/film_series", "FilmSeries"},
+ {"/collection/films", "Films"},
+ {"/collection/foods", "Foods"},
+ {"/collection/garments", "Garments"},
+ {"/collection/geo/business_chain", "GeoBusinessChain"},
+ {"/collection/geo/establishment", "GeoEstablishment"},
+ {"/collection/geo/locality", "GeoLocality"},
+ {"/collection/geo/natural_feature", "GeoNaturalFeature"},
+ {"/collection/geo/political", "GeoPolitical"},
+ {"/collection/holidays", "Holidays"},
+ {"/collection/human_languages", "HumanLanguages"},
+ {"/collection/software", "Software"},
+ {"/collection/job_titles", "JobTitles"},
+ {"/collection/literary_series", "LiterarySeries"},
+ {"/collection/local_shopping_buyables", "LocalShoppingBuyables"},
+ {"/collection/materials", "Materials"},
+ {"/collection/medical_treatments", "MedicalTreatments"},
+ {"/collection/models", "Models"},
+ {"/collection/music_group_members", "MusicGroupMembers"},
+ {"/collection/musical_albums", "MusicalAlbums"},
+ {"/collection/musical_artists", "MusicalArtists"},
+ {"/collection/musical_genres", "MusicalGenres"},
+ {"/collection/musical_groups", "MusicalGroups"},
+ {"/collection/musical_recordings", "MusicalRecordings"},
+ {"/collection/musical_releases", "MusicalReleases"},
+ {"/collection/musicians", "Musicians"},
+ {"/collection/organism_classifications", "OrganismClassifications"},
+ {"/collection/organizations", "Organizations"},
+ {"/collection/people", "People"},
+ {"/collection/periodicals", "Periodicals"},
+ {"/collection/politicians", "Politicians"},
+ {"/collection/recording_clusters", "RecordingClusters"},
+ {"/collection/religions", "Religions"},
+ {"/collection/restaurants", "Restaurants"},
+ {"/collection/ride_offering_services", "RideOfferingServices"},
+ {"/collection/shopping_centers", "ShoppingCenters"},
+ {"/collection/social_network_service_websites",
+ "SocialNetworkServiceWebsites"},
+ {"/collection/sports", "Sports"},
+ {"/collection/sports_teams", "SportsTeams"},
+ {"/collection/structures", "Structures"},
+ {"/collection/tourist_attractions", "TouristAttractions"},
+ {"/collection/travel_destinations", "TravelDestinations"},
+ {"/collection/tv_actors", "TvActors"},
+ {"/collection/tv_episodes", "TvEpisodes"},
+ {"/collection/tv_programs", "TvPrograms"},
+ {"/collection/venues", "Venues"},
+ {"/collection/video_games", "VideoGames"},
+ {"/collection/websites", "Websites"},
+ {"/collection/written_works", "WrittenWorks"}};
+
+ const auto it = kPageEntityCollectionLabelMap.find(collection_str);
+ if (it != kPageEntityCollectionLabelMap.end()) {
+ return it->second;
+ } else {
+ return kUnknown;
+ }
+}
+
EntityMetadata::EntityMetadata() = default;
EntityMetadata::EntityMetadata(
const std::string& entity_id,
const std::string& human_readable_name,
- const base::flat_map<std::string, float>& human_readable_categories)
+ const base::flat_map<std::string, float>& human_readable_categories,
+ const std::vector<std::string>& human_readable_aliases,
+ const std::vector<std::string>& collections)
: entity_id(entity_id),
human_readable_name(human_readable_name),
- human_readable_categories(human_readable_categories) {}
+ human_readable_categories(human_readable_categories),
+ human_readable_aliases(human_readable_aliases),
+ collections(collections) {}
EntityMetadata::EntityMetadata(const EntityMetadata&) = default;
EntityMetadata::~EntityMetadata() = default;
+base::Value EntityMetadata::AsValue() const {
+ base::Value::List categories;
+ for (const auto& iter : human_readable_categories) {
+ base::Value::Dict category;
+ category.Set("category", iter.first);
+ category.Set("score", iter.second);
+ categories.Append(std::move(category));
+ }
+ base::Value::List aliases_list;
+ for (const auto& alias : human_readable_aliases) {
+ aliases_list.Append(alias);
+ }
+ base::Value::List collection_list;
+ for (const auto& collection : collections) {
+ collection_list.Append(collection);
+ }
+
+ base::Value::Dict metadata;
+ metadata.Set("entity_id", entity_id);
+ metadata.Set("human_readable_name", human_readable_name);
+ metadata.Set("categories", std::move(categories));
+ metadata.Set("human_readable_aliases", std::move(aliases_list));
+ metadata.Set("collections", std::move(collection_list));
+
+ return base::Value(std::move(metadata));
+}
+
std::string EntityMetadata::ToString() const {
std::vector<std::string> categories;
for (const auto& iter : human_readable_categories) {
categories.push_back(
base::StringPrintf("{%s,%f}", iter.first.c_str(), iter.second));
}
- return base::StringPrintf("EntityMetadata{%s, %s, {%s}}", entity_id.c_str(),
- human_readable_name.c_str(),
- base::JoinString(categories, ",").c_str());
+
+ return base::StringPrintf(
+ "EntityMetadata{%s, %s, {%s}, {%s}, {%s}}", entity_id.c_str(),
+ human_readable_name.c_str(), base::JoinString(categories, ",").c_str(),
+ base::JoinString(human_readable_aliases, ",").c_str(),
+ base::JoinString(collections, ",").c_str());
}
std::ostream& operator<<(std::ostream& out, const EntityMetadata& md) {
@@ -43,7 +290,9 @@ std::ostream& operator<<(std::ostream& out, const EntityMetadata& md) {
bool operator==(const EntityMetadata& lhs, const EntityMetadata& rhs) {
return lhs.entity_id == rhs.entity_id &&
lhs.human_readable_name == rhs.human_readable_name &&
- lhs.human_readable_categories == rhs.human_readable_categories;
+ lhs.human_readable_categories == rhs.human_readable_categories &&
+ lhs.human_readable_aliases == rhs.human_readable_aliases &&
+ lhs.collections == rhs.collections;
}
ScoredEntityMetadata::ScoredEntityMetadata() = default;
@@ -54,6 +303,13 @@ ScoredEntityMetadata::ScoredEntityMetadata(const ScoredEntityMetadata&) =
default;
ScoredEntityMetadata::~ScoredEntityMetadata() = default;
+base::Value ScoredEntityMetadata::AsValue() const {
+ base::Value::Dict scored_md;
+ scored_md.Set("metadata", metadata.AsValue());
+ scored_md.Set("score", score);
+ return base::Value(std::move(scored_md));
+}
+
std::string ScoredEntityMetadata::ToString() const {
return base::StringPrintf("ScoredEntityMetadata{%f, %s}", score,
metadata.ToString().c_str());
diff --git a/chromium/components/optimization_guide/core/entity_metadata.h b/chromium/components/optimization_guide/core/entity_metadata.h
index 73cdfa0765d..e1bb19a9e73 100644
--- a/chromium/components/optimization_guide/core/entity_metadata.h
+++ b/chromium/components/optimization_guide/core/entity_metadata.h
@@ -6,18 +6,120 @@
#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_ENTITY_METADATA_H_
#include <string>
+#include <vector>
#include "base/containers/flat_map.h"
+#include "base/values.h"
namespace optimization_guide {
+// Corresponds to `OptimizationGuidePageEntityCollection` in
+// tools/metrics/histograms/enums.xml.
+//
+// Source of the original collection list:
+// https://source.corp.google.com/piper///depot/google3/production/borg/webref/ondevice/model-building-conf-chrome.gcl?q=labelling_collections_hrids
+//
+// Use `GetPageEntityCollectionForString` to map a raw collection string to its
+// enum value.
+//
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class PageEntityCollection {
+ kUnknown = 0,
+ kAccommodations = 1,
+ kActors = 2,
+ kAirports = 3,
+ kAnatomicalStructures = 4,
+ kArtworks = 5,
+ kAthletes = 6,
+ kAuthors = 7,
+ kBookEditions = 8,
+ kBusinessOperations = 9,
+ kCars = 10,
+ kCausesOfDeath = 11,
+ kCelestialObjectWithCoordinateSystems = 12,
+ kChemicalCompounds = 13,
+ kConsumerProducts = 14,
+ kCuisines = 15,
+ kCulinaryMeasures = 16,
+ kCurrencies = 17,
+ kDiets = 18,
+ kDiseaseOrMedicalConditions = 19,
+ kEducationalInstitutions = 20,
+ kEmployers = 21,
+ kEvents = 22,
+ kFictionalCharacters = 23,
+ kFilmActors = 24,
+ kFilmScreeningVenues = 25,
+ kFilmSeries = 26,
+ kFilms = 27,
+ kFoods = 28,
+ kGarments = 29,
+ kGeoBusinessChain = 30,
+ kGeoEstablishment = 31,
+ kGeoLocality = 32,
+ kGeoNaturalFeature = 33,
+ kGeoPolitical = 34,
+ kHolidays = 35,
+ kHumanLanguages = 36,
+ kSoftware = 37,
+ kJobTitles = 38,
+ kLiterarySeries = 39,
+ kLocalShoppingBuyables = 40,
+ kMaterials = 41,
+ kMedicalTreatments = 42,
+ kModels = 43,
+ kMusicGroupMembers = 44,
+ kMusicalAlbums = 45,
+ kMusicalArtists = 46,
+ kMusicalGenres = 47,
+ kMusicalGroups = 48,
+ kMusicalRecordings = 49,
+ kMusicalReleases = 50,
+ kMusicians = 51,
+ kOrganismClassifications = 52,
+ kOrganizations = 53,
+ kPeople = 54,
+ kPeriodicals = 55,
+ kPoliticians = 56,
+ kRecordingClusters = 57,
+ kReligions = 58,
+ kRestaurants = 59,
+ kRideOfferingServices = 60,
+ kShoppingCenters = 61,
+ kSocialNetworkServiceWebsites = 62,
+ kSports = 63,
+ kSportsTeams = 64,
+ kStructures = 65,
+ kTouristAttractions = 66,
+ kTravelDestinations = 67,
+ kTvActors = 68,
+ kTvEpisodes = 69,
+ kTvPrograms = 70,
+ kVenues = 71,
+ kVideoGames = 72,
+ kWebsites = 73,
+ kWrittenWorks = 74,
+ kMaxValue = kWrittenWorks
+};
+
+// Returns a collection enum value corresponding to the raw entity collection
+// string.
+PageEntityCollection GetPageEntityCollectionForString(
+ const std::string& collection_str);
+
+// Returns a label for the given raw entity collection string.
+std::string GetPageEntityCollectionLabel(const std::string& collection_str);
+
// The metadata associated with a single entity.
struct EntityMetadata {
EntityMetadata();
EntityMetadata(
const std::string& entity_id,
const std::string& human_readable_name,
- const base::flat_map<std::string, float>& human_readable_categories);
+ const base::flat_map<std::string, float>& human_readable_categories,
+ const std::vector<std::string>& human_readable_aliases = {},
+ const std::vector<std::string>& collections = {});
EntityMetadata(const EntityMetadata&);
~EntityMetadata();
@@ -32,8 +134,18 @@ struct EntityMetadata {
// contain the top 5 entries based on confidence score.
base::flat_map<std::string, float> human_readable_categories;
+ // The ordered set of aliases for this entity in the user's locale.
+ std::vector<std::string> human_readable_aliases;
+
+ // A vector of collections of the entity. Will contain the top 5 collections.
+ // For UMA metrics, use `GetPageEntityCollectionForString` to convert strings
+ // to enum values.
+ std::vector<std::string> collections;
+
std::string ToString() const;
+ base::Value AsValue() const;
+
friend std::ostream& operator<<(std::ostream& out, const EntityMetadata& md);
friend bool operator==(const EntityMetadata& lhs, const EntityMetadata& rhs);
};
@@ -53,6 +165,8 @@ struct ScoredEntityMetadata {
std::string ToString() const;
+ base::Value AsValue() const;
+
friend std::ostream& operator<<(std::ostream& out,
const ScoredEntityMetadata& md);
friend bool operator==(const ScoredEntityMetadata& lhs,
diff --git a/chromium/components/optimization_guide/core/hints_fetcher.cc b/chromium/components/optimization_guide/core/hints_fetcher.cc
index 257e28b3c38..7d58512dc42 100644
--- a/chromium/components/optimization_guide/core/hints_fetcher.cc
+++ b/chromium/components/optimization_guide/core/hints_fetcher.cc
@@ -94,7 +94,7 @@ HintsFetcher::HintsFetcher(
CHECK(optimization_guide_service_url_.SchemeIs(url::kHttpsScheme) ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kOptimizationGuideServiceGetHintsURL));
- DCHECK(features::IsRemoteFetchingEnabled(pref_service));
+ DCHECK(features::IsRemoteFetchingEnabled());
}
HintsFetcher::~HintsFetcher() {
diff --git a/chromium/components/optimization_guide/core/hints_fetcher_unittest.cc b/chromium/components/optimization_guide/core/hints_fetcher_unittest.cc
index b7c2c5e647b..fc39a06fc5a 100644
--- a/chromium/components/optimization_guide/core/hints_fetcher_unittest.cc
+++ b/chromium/components/optimization_guide/core/hints_fetcher_unittest.cc
@@ -42,8 +42,7 @@ class HintsFetcherTest : public testing::Test,
shared_url_loader_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_)) {
- base::test::ScopedFeatureList scoped_list;
- scoped_list.InitWithFeaturesAndParameters(
+ scoped_list_.InitWithFeaturesAndParameters(
{{features::kRemoteOptimizationGuideFetching, {}},
{features::kOptimizationHints,
{{"persist_hints_to_disk",
@@ -156,6 +155,7 @@ class HintsFetcherTest : public testing::Test,
variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
variations::VariationsIdsProvider::Mode::kUseSignedInState};
bool hints_fetched_ = false;
+ base::test::ScopedFeatureList scoped_list_;
base::test::TaskEnvironment task_environment_;
std::unique_ptr<HintsFetcher> hints_fetcher_;
@@ -241,6 +241,8 @@ TEST_P(HintsFetcherTest, FetchInProgress) {
// Tests that the hints are refreshed again for hosts for whom hints were
// fetched recently.
TEST_P(HintsFetcherTest, FetchInProgress_HostsHintsRefreshed) {
+ if (!ShouldPersistHintsToDisk())
+ return;
base::SimpleTestClock test_clock;
SetTimeClockForTesting(&test_clock);
@@ -495,6 +497,9 @@ TEST_P(HintsFetcherTest, HintsFetcherHostNotCovered) {
pref_service(), prefs::kHintsFetcherHostsSuccessfullyFetched);
EXPECT_EQ(2u, hosts_fetched->DictSize());
+ if (!ShouldPersistHintsToDisk())
+ return;
+
EXPECT_TRUE(WasHostCoveredByFetch(hosts[0]));
EXPECT_TRUE(WasHostCoveredByFetch(hosts[1]));
EXPECT_FALSE(WasHostCoveredByFetch("newhost.com"));
diff --git a/chromium/components/optimization_guide/core/hints_manager.cc b/chromium/components/optimization_guide/core/hints_manager.cc
index f19a6215b6d..e3753a28dde 100644
--- a/chromium/components/optimization_guide/core/hints_manager.cc
+++ b/chromium/components/optimization_guide/core/hints_manager.cc
@@ -76,15 +76,8 @@ void MaybeRunUpdateClosure(base::OnceClosure update_closure) {
std::move(update_closure).Run();
}
-// Returns whether the particular component version can be processed, and if it
-// can be, locks the semaphore (in the form of a pref) to signal that the
-// processing of this particular version has started.
-bool CanProcessComponentVersion(PrefService* pref_service,
- const base::Version& version,
- ProcessHintsComponentResult* out_result) {
- DCHECK(version.IsValid());
- DCHECK(out_result);
-
+absl::optional<base::Version>
+GetPendingOptimizationHintsComponentVersionFromPref(PrefService* pref_service) {
const std::string previous_attempted_version_string =
pref_service->GetString(prefs::kPendingHintsProcessingVersion);
if (!previous_attempted_version_string.empty()) {
@@ -94,21 +87,11 @@ bool CanProcessComponentVersion(PrefService* pref_service,
DLOG(ERROR) << "Bad contents in hints processing pref";
// Clear pref for fresh start next time.
pref_service->ClearPref(prefs::kPendingHintsProcessingVersion);
- *out_result =
- ProcessHintsComponentResult::kFailedPreviouslyAttemptedVersionInvalid;
- return false;
- }
- if (previous_attempted_version.CompareTo(version) == 0) {
- *out_result = ProcessHintsComponentResult::kFailedFinishProcessing;
- // Previously attempted same version without completion.
- return false;
+ return absl::nullopt;
}
+ return absl::make_optional(previous_attempted_version);
}
-
- // Write config version to pref.
- pref_service->SetString(prefs::kPendingHintsProcessingVersion,
- version.GetString());
- return true;
+ return absl::nullopt;
}
// Returns whether |optimization_type| is allowlisted by |optimizations|. If
@@ -306,7 +289,9 @@ HintsManager::HintsManager(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<PushNotificationManager> push_notification_manager,
OptimizationGuideLogger* optimization_guide_logger)
- : is_off_the_record_(is_off_the_record),
+ : failed_component_version_(
+ GetPendingOptimizationHintsComponentVersionFromPref(pref_service)),
+ is_off_the_record_(is_off_the_record),
application_locale_(application_locale),
pref_service_(pref_service),
hint_cache_(
@@ -329,6 +314,10 @@ HintsManager::HintsManager(
if (push_notification_manager_)
push_notification_manager_->SetDelegate(this);
+ // Register as an observer to get updates for the component. This is
+ // needed as a signal during testing.
+ OptimizationHintsComponentUpdateListener::GetInstance()->AddObserver(this);
+
hint_cache_->Initialize(
switches::ShouldPurgeOptimizationGuideStoreOnStartup(),
base::BindOnce(&HintsManager::OnHintCacheInitialized,
@@ -343,9 +332,10 @@ void HintsManager::Shutdown() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
OptimizationHintsComponentUpdateListener::GetInstance()->RemoveObserver(this);
- base::UmaHistogramBoolean("OptimizationGuide.ProcessingComponentAtShutdown",
- is_processing_component_);
- if (is_processing_component_) {
+ base::UmaHistogramBoolean(
+ "OptimizationGuide.ProcessingComponentAtShutdown",
+ currently_processing_component_version_.has_value());
+ if (currently_processing_component_version_) {
// If we are currently processing the component and we are asked to shut
// down, we should clear the pref since the function to clear the pref will
// not run after shut down and we will think that we failed to process the
@@ -379,6 +369,19 @@ HintsManager::GetOptimizationGuideDecisionFromOptimizationTypeDecision(
void HintsManager::OnHintsComponentAvailable(const HintsComponentInfo& info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (currently_processing_component_version_ &&
+ *currently_processing_component_version_ == info.version) {
+ OPTIMIZATION_GUIDE_LOGGER(optimization_guide_logger_)
+ << "Already in the middle of processing OptimizationHints component "
+ "version: "
+ << info.version.GetString();
+ return;
+ }
+
+ OPTIMIZATION_GUIDE_LOGGER(optimization_guide_logger_)
+ << "Received OptimizationHints component version: "
+ << info.version.GetString();
+
// Check for if hint component is disabled. This check is needed because the
// optimization guide still registers with the service as an observer for
// components as a signal during testing.
@@ -387,12 +390,21 @@ void HintsManager::OnHintsComponentAvailable(const HintsComponentInfo& info) {
return;
}
- ProcessHintsComponentResult out_result;
- if (!CanProcessComponentVersion(pref_service_, info.version, &out_result)) {
- RecordProcessHintsComponentResult(out_result);
+ if (features::ShouldCheckFailedComponentVersionPref() &&
+ failed_component_version_ &&
+ failed_component_version_->CompareTo(info.version) >= 0) {
+ OPTIMIZATION_GUIDE_LOGGER(optimization_guide_logger_)
+ << "Skipping processing OptimizationHints component version: "
+ << info.version.GetString()
+ << " as it had failed in a previous session";
+ RecordProcessHintsComponentResult(
+ ProcessHintsComponentResult::kFailedFinishProcessing);
MaybeRunUpdateClosure(std::move(next_update_closure_));
return;
}
+ // Write version that we are currently processing to prefs.
+ pref_service_->SetString(prefs::kPendingHintsProcessingVersion,
+ info.version.GetString());
std::unique_ptr<StoreUpdateData> update_data =
is_off_the_record_
@@ -407,7 +419,10 @@ void HintsManager::OnHintsComponentAvailable(const HintsComponentInfo& info) {
// processing will be skipped.
// base::Unretained(this) is safe since |this| owns |background_task_runner_|
// and the callback will be canceled if destroyed.
- is_processing_component_ = true;
+ currently_processing_component_version_ = info.version;
+ OPTIMIZATION_GUIDE_LOGGER(optimization_guide_logger_)
+ << "Processing OptimizationHints component version: "
+ << currently_processing_component_version_->GetString();
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&ReadComponentFile, info),
base::BindOnce(&HintsManager::UpdateComponentHints,
@@ -427,6 +442,8 @@ void HintsManager::ProcessOptimizationFilters(
allowlist_optimization_filters,
const google::protobuf::RepeatedPtrField<proto::OptimizationFilter>&
blocklist_optimization_filters) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
optimization_types_with_filter_.clear();
allowlist_optimization_filters_.clear();
blocklist_optimization_filters_.clear();
@@ -440,6 +457,8 @@ void HintsManager::ProcessOptimizationFilterSet(
const google::protobuf::RepeatedPtrField<proto::OptimizationFilter>&
filters,
bool is_allowlist) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
for (const auto& filter : filters) {
if (filter.optimization_type() != proto::TYPE_UNSPECIFIED) {
optimization_types_with_filter_.insert(filter.optimization_type());
@@ -471,6 +490,8 @@ void HintsManager::ProcessOptimizationFilterSet(
std::unique_ptr<OptimizationFilter> optimization_filter =
ProcessOptimizationFilter(filter, &status);
if (optimization_filter) {
+ OPTIMIZATION_GUIDE_LOGGER(optimization_guide_logger_)
+ << "Loaded optimization filter for " << filter.optimization_type();
if (is_allowlist) {
allowlist_optimization_filters_.insert(
{filter.optimization_type(), std::move(optimization_filter)});
@@ -486,6 +507,9 @@ void HintsManager::ProcessOptimizationFilterSet(
void HintsManager::OnHintCacheInitialized() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ OPTIMIZATION_GUIDE_LOGGER(optimization_guide_logger_)
+ << "Hint cache initialized";
+
if (push_notification_manager_) {
push_notification_manager_->OnDelegateReady();
}
@@ -514,9 +538,11 @@ void HintsManager::OnHintCacheInitialized() {
should_clear_hints_for_new_type_ = false;
}
- // Register as an observer regardless of hint proto override usage. This is
- // needed as a signal during testing.
- OptimizationHintsComponentUpdateListener::GetInstance()->AddObserver(this);
+ // This is used as a signal for testing so that tests can push hints via the
+ // component. Fixing the logic appropriately is a lot more work for something
+ // we don't actually do in the wild.
+ LOCAL_HISTOGRAM_BOOLEAN("OptimizationGuide.HintsManager.HintCacheInitialized",
+ true);
}
void HintsManager::UpdateComponentHints(
@@ -527,7 +553,9 @@ void HintsManager::UpdateComponentHints(
// If we get here, the component file has been processed correctly and did not
// crash the device.
- is_processing_component_ = false;
+ OPTIMIZATION_GUIDE_LOGGER(optimization_guide_logger_)
+ << "Component successfully processed";
+ currently_processing_component_version_ = absl::nullopt;
pref_service_->ClearPref(prefs::kPendingHintsProcessingVersion);
if (!config) {
diff --git a/chromium/components/optimization_guide/core/hints_manager.h b/chromium/components/optimization_guide/core/hints_manager.h
index 4390f639501..b68eca3461e 100644
--- a/chromium/components/optimization_guide/core/hints_manager.h
+++ b/chromium/components/optimization_guide/core/hints_manager.h
@@ -404,8 +404,12 @@ class HintsManager : public OptimizationHintsComponentObserver,
// |optimization_guide_service_|.
absl::optional<HintsComponentInfo> hints_component_info_;
- // Whether the component is currently being processed.
- bool is_processing_component_ = false;
+ // The component version that failed to process in the last session, if
+ // applicable.
+ const absl::optional<base::Version> failed_component_version_;
+
+ // The version of the component that is currently being processed.
+ absl::optional<base::Version> currently_processing_component_version_;
// The set of optimization types that have been registered with the hints
// manager.
diff --git a/chromium/components/optimization_guide/core/hints_manager_unittest.cc b/chromium/components/optimization_guide/core/hints_manager_unittest.cc
index fa406d622ed..63c583290a8 100644
--- a/chromium/components/optimization_guide/core/hints_manager_unittest.cc
+++ b/chromium/components/optimization_guide/core/hints_manager_unittest.cc
@@ -285,9 +285,18 @@ class TestHintsFetcherFactory : public HintsFetcherFactory {
class HintsManagerTest : public ProtoDatabaseProviderTestBase {
public:
HintsManagerTest() {
- scoped_feature_list_.InitAndEnableFeatureWithParameters(
- features::kOptimizationHints,
- GetOptimizationHintsDefaultFeatureParams());
+ scoped_feature_list_.InitWithFeaturesAndParameters(
+ {{features::kOptimizationHints,
+ GetOptimizationHintsDefaultFeatureParams()},
+ {features::kOptimizationHintsComponent,
+ {{"check_failed_component_version_pref", "true"}}}},
+ /*disabled_features=*/{});
+
+ pref_service_ =
+ std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
+ prefs::RegisterProfilePrefs(pref_service_->registry());
+ unified_consent::UnifiedConsentService::RegisterPrefs(
+ pref_service_->registry());
}
~HintsManagerTest() override = default;
@@ -301,6 +310,7 @@ class HintsManagerTest : public ProtoDatabaseProviderTestBase {
void TearDown() override {
ResetHintsManager();
+ pref_service_.reset();
ProtoDatabaseProviderTestBase::TearDown();
}
@@ -308,12 +318,6 @@ class HintsManagerTest : public ProtoDatabaseProviderTestBase {
if (hints_manager_)
ResetHintsManager();
- pref_service_ =
- std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
- prefs::RegisterProfilePrefs(pref_service_->registry());
- unified_consent::UnifiedConsentService::RegisterPrefs(
- pref_service_->registry());
-
url_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
@@ -341,7 +345,6 @@ class HintsManagerTest : public ProtoDatabaseProviderTestBase {
hints_manager_.reset();
tab_url_provider_.reset();
hint_store_.reset();
- pref_service_.reset();
RunUntilIdle();
}
@@ -731,6 +734,7 @@ TEST_F(HintsManagerTest, ComponentInfoDidNotContainConfig) {
TEST_F(HintsManagerTest, ProcessHintsWithExistingPref) {
// Write hints processing pref for version 2.0.0.
pref_service()->SetString(prefs::kPendingHintsProcessingVersion, "2.0.0");
+ CreateHintsManager(/*top_host_provider=*/nullptr);
// Verify config not processed for same version (2.0.0) and pref not cleared.
{
@@ -761,6 +765,7 @@ TEST_F(HintsManagerTest,
ProcessHintsWithExistingPrefDoesNotClearOrCountAsMidProcessing) {
// Write hints processing pref for version 2.0.0.
pref_service()->SetString(prefs::kPendingHintsProcessingVersion, "2.0.0");
+ CreateHintsManager(/*top_host_provider=*/nullptr);
// Verify component for same version counts as "failed".
base::HistogramTester histogram_tester;
@@ -782,22 +787,9 @@ TEST_F(HintsManagerTest,
TEST_F(HintsManagerTest, ProcessHintsWithInvalidPref) {
// Create pref file with invalid version.
pref_service()->SetString(prefs::kPendingHintsProcessingVersion, "bad-2.0.0");
+ CreateHintsManager(/*top_host_provider=*/nullptr);
- // Verify config not processed for existing pref with bad value but
- // that the pref is cleared.
- {
- base::HistogramTester histogram_tester;
- InitializeWithDefaultConfig("2.0.0");
- EXPECT_TRUE(pref_service()
- ->GetString(prefs::kPendingHintsProcessingVersion)
- .empty());
- histogram_tester.ExpectUniqueSample(
- "OptimizationGuide.ProcessHintsResult",
- ProcessHintsComponentResult::kFailedPreviouslyAttemptedVersionInvalid,
- 1);
- }
-
- // Now verify config is processed with pref cleared.
+ // Verify config is processed with pref cleared.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("2.0.0");
@@ -3333,4 +3325,47 @@ TEST_F(HintsManagerFetchingNoBatchUpdateTest,
EXPECT_FALSE(active_tabs_batch_update_hints_fetcher());
}
+class HintsManagerComponentSkipProcessingTest : public HintsManagerTest {
+ public:
+ HintsManagerComponentSkipProcessingTest() {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ features::kOptimizationHintsComponent,
+ {{"check_failed_component_version_pref", "false"}});
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(HintsManagerComponentSkipProcessingTest, ProcessHintsWithExistingPref) {
+ // Write hints processing pref for version 2.0.0.
+ pref_service()->SetString(prefs::kPendingHintsProcessingVersion, "2.0.0");
+ CreateHintsManager(/*top_host_provider=*/nullptr);
+
+ // Verify config still processed even though pref is existing.
+ {
+ base::HistogramTester histogram_tester;
+ InitializeWithDefaultConfig("2.0.0");
+ histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
+ ProcessHintsComponentResult::kSuccess,
+ 1);
+ // If it processed correctly, it should clear the pref.
+ EXPECT_TRUE(pref_service()
+ ->GetString(prefs::kPendingHintsProcessingVersion)
+ .empty());
+ }
+
+ // Now verify config is processed for different version and pref cleared.
+ {
+ base::HistogramTester histogram_tester;
+ InitializeWithDefaultConfig("3.0.0");
+ EXPECT_TRUE(pref_service()
+ ->GetString(prefs::kPendingHintsProcessingVersion)
+ .empty());
+ histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
+ ProcessHintsComponentResult::kSuccess,
+ 1);
+ }
+}
+
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/insertion_ordered_set.h b/chromium/components/optimization_guide/core/insertion_ordered_set.h
index 061d4a3c09d..56b67f60522 100644
--- a/chromium/components/optimization_guide/core/insertion_ordered_set.h
+++ b/chromium/components/optimization_guide/core/insertion_ordered_set.h
@@ -33,6 +33,8 @@ class InsertionOrderedSet {
bool empty() const { return vector_.empty(); }
+ size_t size() const { return vector_.size(); }
+
const std::vector<T>& vector() { return vector_; }
const base::flat_set<T>& set() { return set_; }
diff --git a/chromium/components/optimization_guide/core/local_page_entities_metadata_provider_unittest.cc b/chromium/components/optimization_guide/core/local_page_entities_metadata_provider_unittest.cc
index 7a24cbe2430..8d96bf67443 100644
--- a/chromium/components/optimization_guide/core/local_page_entities_metadata_provider_unittest.cc
+++ b/chromium/components/optimization_guide/core/local_page_entities_metadata_provider_unittest.cc
@@ -4,6 +4,7 @@
#include "components/optimization_guide/core/local_page_entities_metadata_provider.h"
+#include "base/memory/raw_ptr.h"
#include "base/test/task_environment.h"
#include "components/leveldb_proto/testing/fake_db.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
@@ -39,7 +40,7 @@ class LocalPageEntitiesMetadataProviderTest : public testing::Test {
private:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<LocalPageEntitiesMetadataProvider> provider_;
- leveldb_proto::test::FakeDB<proto::EntityMetadataStorage>* db_;
+ raw_ptr<leveldb_proto::test::FakeDB<proto::EntityMetadataStorage>> db_;
std::map<std::string, proto::EntityMetadataStorage> db_store_;
};
diff --git a/chromium/components/optimization_guide/core/model_execution_timeout_watchdog.h b/chromium/components/optimization_guide/core/model_execution_timeout_watchdog.h
index 3238f533c90..a619a70bd36 100644
--- a/chromium/components/optimization_guide/core/model_execution_timeout_watchdog.h
+++ b/chromium/components/optimization_guide/core/model_execution_timeout_watchdog.h
@@ -7,9 +7,9 @@
#include "base/metrics/histogram_functions.h"
#include "base/synchronization/lock.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/threading/watchdog.h"
+#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
+#include "base/timer/timer.h"
#include "components/optimization_guide/core/model_util.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/base_task_api.h"
@@ -27,8 +27,7 @@ void RecordDidTimeoutHistogram(proto::OptimizationTarget optimization_target,
} // namespace
// This is a helper class to |TFLiteModelExecutor| that watches for a model
-// execution that runs for too long. This is done using a |base::Watchdog| that
-// uses PlatformThread under the hood.
+// execution that runs for too long.
//
// Background/Motivation: TFLite Model Execution occurs on a background task
// runner, but we've seen from metrics that some models are extremely long
@@ -52,18 +51,32 @@ void RecordDidTimeoutHistogram(proto::OptimizationTarget optimization_target,
// Care must be taken to ensure |ArmWithTask| and |DisarmOnExecutionComplete|
// are always called so that the internal |task_| pointer can be (re)set with
// the same timing as the model execution.
+//
+// The watchdog class is working on two sequences: execution sequence and
+// wachdog sequence. |ArmWithTask| and |DisarmOnExecutionComplete| are called
+// on the execution sequence. The watchdog is implemented with a
+// base::OneShotTimer which lives and runs on the watchdog sequence. In case of
+// an execution timeout, the watchdog notification is received by
+// |AlarmOnWatchdogSequence| on the watchdog sequence. The deletion of this
+// class must happen on the watchdog sequence to ensure that timer task can be
+// cancelled on the right sequence.
+
template <class OutputType, class... InputTypes>
-class ModelExecutionTimeoutWatchdog : private base::Watchdog {
+class ModelExecutionTimeoutWatchdog {
public:
explicit ModelExecutionTimeoutWatchdog(
+ scoped_refptr<base::SequencedTaskRunner> watchdog_task_runner,
proto::OptimizationTarget optimization_target,
base::TimeDelta duration)
- : base::Watchdog(
- duration,
- /*thread_watched_name=*/"OptGuideModelExecution_" +
- GetStringNameForOptimizationTarget(optimization_target),
- /*enabled=*/true),
- optimization_target_(optimization_target) {}
+ : watchdog_task_runner_(watchdog_task_runner),
+ optimization_target_(optimization_target),
+ duration_(duration) {
+ DCHECK_GE(duration, base::TimeDelta());
+ }
+
+ ~ModelExecutionTimeoutWatchdog() {
+ DCHECK(watchdog_task_runner_->RunsTasksInCurrentSequence());
+ }
void ArmWithTask(
tflite::task::core::BaseTaskApi<OutputType, InputTypes...>* task) {
@@ -71,7 +84,13 @@ class ModelExecutionTimeoutWatchdog : private base::Watchdog {
base::AutoLock lock(task_lock_);
task_ = task;
}
- Arm();
+
+ // Arm the watchdog timer. Since the dtor is on the watchdog sequence,
+ // using base::Unretained is safe.
+ watchdog_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ModelExecutionTimeoutWatchdog::ArmOnWatchdogSequence,
+ base::Unretained(this)));
}
void DisarmOnExecutionComplete() {
@@ -84,16 +103,34 @@ class ModelExecutionTimeoutWatchdog : private base::Watchdog {
}
task_ = nullptr;
}
- Disarm();
+ // Disarm the watchdog timer. Since the dtor is on the watchdog sequence,
+ // using base::Unretained is safe.
+ watchdog_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ModelExecutionTimeoutWatchdog::DisarmOnWatchdogSequence,
+ base::Unretained(this)));
RecordDidTimeoutHistogram(optimization_target_, false);
}
private:
- // base::Watchdog:
- void Alarm() override {
- base::Watchdog::Alarm();
+ void ArmOnWatchdogSequence() {
+ DCHECK(watchdog_task_runner_->RunsTasksInCurrentSequence());
+ // Since the dtor is on the watchdog sequence, using base::Unretained is
+ // safe. If the timer is released, the pending task will be canceled.
+ watchdog_timer_.Start(
+ FROM_HERE, duration_,
+ base::BindOnce(&ModelExecutionTimeoutWatchdog::AlarmOnWatchdogSequence,
+ base::Unretained(this)));
+ }
+ void DisarmOnWatchdogSequence() {
+ DCHECK(watchdog_task_runner_->RunsTasksInCurrentSequence());
+ watchdog_timer_.Stop();
+ }
+
+ void AlarmOnWatchdogSequence() {
+ DCHECK(watchdog_task_runner_->RunsTasksInCurrentSequence());
{
base::AutoLock lock(task_lock_);
if (!task_) {
@@ -108,7 +145,11 @@ class ModelExecutionTimeoutWatchdog : private base::Watchdog {
RecordDidTimeoutHistogram(optimization_target_, true);
}
+ scoped_refptr<base::SequencedTaskRunner> watchdog_task_runner_;
+ base::OneShotTimer watchdog_timer_;
+
const proto::OptimizationTarget optimization_target_;
+ const base::TimeDelta duration_;
base::Lock task_lock_;
raw_ptr<tflite::task::core::BaseTaskApi<OutputType, InputTypes...>> task_
diff --git a/chromium/components/optimization_guide/core/model_executor.h b/chromium/components/optimization_guide/core/model_executor.h
index 305b6a9e24b..2b4dab3dfe7 100644
--- a/chromium/components/optimization_guide/core/model_executor.h
+++ b/chromium/components/optimization_guide/core/model_executor.h
@@ -37,7 +37,10 @@ class ModelExecutor {
ModelExecutor() = default;
virtual ~ModelExecutor() = default;
+ // If |model_inference_timeout| is nullopt a default value will be used,
+ // controlled by the optimization guide.
virtual void InitializeAndMoveToExecutionThread(
+ absl::optional<base::TimeDelta> model_inference_timeout,
proto::OptimizationTarget optimization_target,
scoped_refptr<base::SequencedTaskRunner> execution_task_runner,
scoped_refptr<base::SequencedTaskRunner> reply_task_runner) = 0;
diff --git a/chromium/components/optimization_guide/core/model_handler.h b/chromium/components/optimization_guide/core/model_handler.h
index b172d9e6b8d..6b4af98a488 100644
--- a/chromium/components/optimization_guide/core/model_handler.h
+++ b/chromium/components/optimization_guide/core/model_handler.h
@@ -37,6 +37,8 @@ class ModelHandler : public OptimizationTargetModelObserver {
OptimizationGuideModelProvider* model_provider,
scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner,
std::unique_ptr<ModelExecutor<OutputType, InputTypes...>> model_executor,
+ // Passing nullopt will use a default value.
+ absl::optional<base::TimeDelta> model_inference_timeout,
proto::OptimizationTarget optimization_target,
const absl::optional<proto::Any>& model_metadata)
: model_provider_(model_provider),
@@ -54,11 +56,15 @@ class ModelHandler : public OptimizationTargetModelObserver {
true);
handler_created_time_ = base::TimeTicks::Now();
+
+ model_executor_->InitializeAndMoveToExecutionThread(
+ model_inference_timeout, optimization_target_,
+ model_executor_task_runner_, base::SequencedTaskRunnerHandle::Get());
+
+ // Run this after the executor is initialized in case the model is already
+ // available.
model_provider_->AddObserverForOptimizationTargetModel(
optimization_target_, model_metadata, this);
- model_executor_->InitializeAndMoveToExecutionThread(
- optimization_target_, model_executor_task_runner_,
- base::SequencedTaskRunnerHandle::Get());
}
~ModelHandler() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chromium/components/optimization_guide/core/model_util.cc b/chromium/components/optimization_guide/core/model_util.cc
index 936d0e4c4cb..eb3ab99bd0e 100644
--- a/chromium/components/optimization_guide/core/model_util.cc
+++ b/chromium/components/optimization_guide/core/model_util.cc
@@ -6,14 +6,29 @@
#include "base/base64.h"
#include "base/containers/flat_set.h"
+#include "base/logging.h"
#include "base/notreached.h"
+#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
+#include "components/optimization_guide/core/optimization_guide_switches.h"
#include "net/base/url_util.h"
#include "url/url_canon.h"
namespace optimization_guide {
+namespace {
+
+// The ":" character is reserved in Windows as part of an absolute file path,
+// e.g.: C:\model.tflite, so we use a different separtor.
+#if BUILDFLAG(IS_WIN)
+const char kModelOverrideSeparator[] = "|";
+#else
+const char kModelOverrideSeparator[] = ":";
+#endif
+
+} // namespace
+
// These names are persisted to histograms, so don't change them.
std::string GetStringNameForOptimizationTarget(
optimization_guide::proto::OptimizationTarget optimization_target) {
@@ -52,6 +67,10 @@ std::string GetStringNameForOptimizationTarget(
return "PageTopicsV2";
case proto::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
return "SegmentationChromeLowUserEngagement";
+ case proto::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
+ return "SegmentationFeedUser";
+ case proto::OPTIMIZATION_TARGET_CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING:
+ return "ContextualPageActionPriceTracking";
// Whenever a new value is added, make sure to add it to the OptTarget
// variant list in
// //tools/metrics/histograms/metadata/optimization/histograms.xml.
@@ -83,4 +102,70 @@ base::FilePath GetBaseFileNameForModels() {
return base::FilePath(FILE_PATH_LITERAL("model.tflite"));
}
+std::string ModelOverrideSeparator() {
+ return kModelOverrideSeparator;
+}
+
+absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OptimizationTarget optimization_target) {
+ auto model_override_switch_value = switches::GetModelOverride();
+ if (!model_override_switch_value)
+ return absl::nullopt;
+
+ std::vector<std::string> model_overrides =
+ base::SplitString(*model_override_switch_value, ",",
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ for (const auto& model_override : model_overrides) {
+ std::vector<std::string> override_parts =
+ base::SplitString(model_override, kModelOverrideSeparator,
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (override_parts.size() != 2 && override_parts.size() != 3) {
+ // Input is malformed.
+ DLOG(ERROR) << "Invalid string format provided to the Model Override";
+ return absl::nullopt;
+ }
+
+ optimization_guide::proto::OptimizationTarget recv_optimization_target;
+ if (!optimization_guide::proto::OptimizationTarget_Parse(
+ override_parts[0], &recv_optimization_target)) {
+ // Optimization target is invalid.
+ DLOG(ERROR)
+ << "Invalid optimization target provided to the Model Override";
+ return absl::nullopt;
+ }
+ if (optimization_target != recv_optimization_target)
+ continue;
+
+ std::string file_name = override_parts[1];
+ base::FilePath file_path = *StringToFilePath(file_name);
+ if (!file_path.IsAbsolute()) {
+ DLOG(ERROR) << "Provided model file path must be absolute " << file_name;
+ return absl::nullopt;
+ }
+
+ if (override_parts.size() == 2) {
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>
+ file_path_and_metadata = std::make_pair(file_name, absl::nullopt);
+ return file_path_and_metadata;
+ }
+
+ std::string binary_pb;
+ if (!base::Base64Decode(override_parts[2], &binary_pb)) {
+ DLOG(ERROR) << "Invalid base64 encoding of the Model Override";
+ return absl::nullopt;
+ }
+ optimization_guide::proto::Any model_metadata;
+ if (!model_metadata.ParseFromString(binary_pb)) {
+ DLOG(ERROR) << "Invalid model metadata provided to the Model Override";
+ return absl::nullopt;
+ }
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>
+ file_path_and_metadata = std::make_pair(file_name, model_metadata);
+ return file_path_and_metadata;
+ }
+ return absl::nullopt;
+}
+
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/model_util.h b/chromium/components/optimization_guide/core/model_util.h
index dbcf1d476db..1cb46acb345 100644
--- a/chromium/components/optimization_guide/core/model_util.h
+++ b/chromium/components/optimization_guide/core/model_util.h
@@ -32,6 +32,17 @@ std::string FilePathToString(const base::FilePath& file_path);
// Returns the base file name to use for storing all prediction models.
base::FilePath GetBaseFileNameForModels();
+// Returns the separator used in the model override switch below, which differs
+// between platforms.
+std::string ModelOverrideSeparator();
+
+// Returns the file path string and metadata for the model provided via
+// command-line for |optimization_target|, if applicable.
+absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OptimizationTarget optimization_target);
+
} // namespace optimization_guide
#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_UTIL_H_
diff --git a/chromium/components/optimization_guide/core/model_util_unittest.cc b/chromium/components/optimization_guide/core/model_util_unittest.cc
new file mode 100644
index 00000000000..691304f4cd0
--- /dev/null
+++ b/chromium/components/optimization_guide/core/model_util_unittest.cc
@@ -0,0 +1,172 @@
+// Copyright 2022 The Chromium 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/optimization_guide/core/model_util.h"
+
+#include "base/base64.h"
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "components/optimization_guide/core/optimization_guide_switches.h"
+#include "components/optimization_guide/core/optimization_guide_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace optimization_guide {
+
+namespace {
+
+#if BUILDFLAG(IS_WIN)
+const char kOtherAbsoluteFilePath[] = "C:\\other\\absolute\\file\\path";
+#else
+const char kOtherAbsoluteFilePath[] = "/other/abs/file/path";
+#endif
+
+} // namespace
+
+TEST(ModelUtilTest, GetModelOverrideForOptimizationTargetSwitchNotSet) {
+ absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+ file_path_and_metadata = GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+
+ EXPECT_EQ(absl::nullopt, file_path_and_metadata);
+ EXPECT_FALSE(switches::IsModelOverridePresent());
+}
+
+TEST(ModelUtilTest, GetModelOverrideForOptimizationTargetEmptyInput) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kModelOverride);
+
+ absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+ file_path_and_metadata = GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+
+ EXPECT_EQ(absl::nullopt, file_path_and_metadata);
+}
+
+TEST(ModelUtilTest, GetModelOverrideForOptimizationTargetBadInput) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kModelOverride, "whatever");
+
+ absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+ file_path_and_metadata = GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+
+ EXPECT_EQ(absl::nullopt, file_path_and_metadata);
+}
+
+TEST(ModelUtilTest,
+ GetModelOverrideForOptimizationTargetInvalidOptimizationTarget) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kModelOverride,
+ "notanoptimizationtarget:" + std::string(kTestAbsoluteFilePath));
+
+ absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+ file_path_and_metadata = GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+
+ EXPECT_EQ(absl::nullopt, file_path_and_metadata);
+}
+
+TEST(ModelUtilTest, GetModelOverrideForOptimizationTargetRelativeFilePath) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kModelOverride, "OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:" +
+ std::string(kTestRelativeFilePath));
+
+ absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+ file_path_and_metadata = GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+
+ EXPECT_EQ(absl::nullopt, file_path_and_metadata);
+}
+
+TEST(ModelUtilTest,
+ GetModelOverrideForOptimizationTargetRelativeFilePathWithMetadata) {
+ optimization_guide::proto::Any metadata;
+ metadata.set_type_url("sometypeurl");
+ std::string encoded_metadata;
+ metadata.SerializeToString(&encoded_metadata);
+ base::Base64Encode(encoded_metadata, &encoded_metadata);
+
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kModelOverride,
+ base::StringPrintf("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:%s:%s",
+ kTestRelativeFilePath, encoded_metadata.c_str()));
+
+ absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+ file_path_and_metadata = GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+
+ EXPECT_EQ(absl::nullopt, file_path_and_metadata);
+}
+
+TEST(ModelUtilTest, GetModelOverrideForOptimizationTargetOneFilePath) {
+ optimization_guide::proto::Any metadata;
+ metadata.set_type_url("sometypeurl");
+ std::string encoded_metadata;
+ metadata.SerializeToString(&encoded_metadata);
+ base::Base64Encode(encoded_metadata, &encoded_metadata);
+#if BUILDFLAG(IS_WIN)
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kModelOverride,
+ base::StringPrintf("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD|%s|%s",
+ kTestAbsoluteFilePath, encoded_metadata.c_str()));
+#else
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kModelOverride,
+ base::StringPrintf("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:%s:%s",
+ kTestAbsoluteFilePath, encoded_metadata.c_str()));
+#endif
+
+ absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+ file_path_and_metadata = GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+
+ ASSERT_TRUE(file_path_and_metadata);
+ EXPECT_EQ(kTestAbsoluteFilePath, file_path_and_metadata->first);
+ EXPECT_EQ("sometypeurl", file_path_and_metadata->second->type_url());
+}
+
+TEST(ModelUtilTest, GetModelOverrideForOptimizationTargetMultipleFilePath) {
+ optimization_guide::proto::Any metadata;
+ metadata.set_type_url("sometypeurl");
+ std::string encoded_metadata;
+ metadata.SerializeToString(&encoded_metadata);
+ base::Base64Encode(encoded_metadata, &encoded_metadata);
+#if BUILDFLAG(IS_WIN)
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kModelOverride,
+ base::StringPrintf("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD|%s,"
+ "OPTIMIZATION_TARGET_PAGE_TOPICS|%s|%s",
+ kTestAbsoluteFilePath, kOtherAbsoluteFilePath,
+ encoded_metadata.c_str()));
+#else
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kModelOverride,
+ base::StringPrintf("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:%s,"
+ "OPTIMIZATION_TARGET_PAGE_TOPICS:%s:%s",
+ kTestAbsoluteFilePath, kOtherAbsoluteFilePath,
+ encoded_metadata.c_str()));
+#endif
+
+ absl::optional<
+ std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
+ file_path_and_metadata = GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+ ASSERT_TRUE(file_path_and_metadata);
+ EXPECT_EQ(kTestAbsoluteFilePath, file_path_and_metadata->first);
+
+ file_path_and_metadata = GetModelOverrideForOptimizationTarget(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAGE_TOPICS);
+ ASSERT_TRUE(file_path_and_metadata);
+ EXPECT_EQ(kOtherAbsoluteFilePath, file_path_and_metadata->first);
+ EXPECT_EQ("sometypeurl", file_path_and_metadata->second->type_url());
+}
+
+} // namespace optimization_guide \ No newline at end of file
diff --git a/chromium/components/optimization_guide/core/model_validator.cc b/chromium/components/optimization_guide/core/model_validator.cc
index bb09c0c8e80..f047a8d7c9d 100644
--- a/chromium/components/optimization_guide/core/model_validator.cc
+++ b/chromium/components/optimization_guide/core/model_validator.cc
@@ -19,6 +19,7 @@ ModelValidatorHandler::ModelValidatorHandler(
model_provider,
background_task_runner,
std::make_unique<ModelValidatorExecutor>(),
+ /*model_inference_timeout=*/absl::nullopt,
proto::OPTIMIZATION_TARGET_MODEL_VALIDATION,
/*model_metadata=*/absl::nullopt) {}
diff --git a/chromium/components/optimization_guide/core/optimization_guide_features.cc b/chromium/components/optimization_guide/core/optimization_guide_features.cc
index 82b84160de6..4bf3a8a2b95 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_features.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_features.cc
@@ -12,13 +12,12 @@
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
+#include "base/system/sys_info.h"
#include "build/build_config.h"
#include "components/optimization_guide/core/insertion_ordered_set.h"
#include "components/optimization_guide/core/optimization_guide_constants.h"
-#include "components/optimization_guide/core/optimization_guide_prefs.h"
#include "components/optimization_guide/core/optimization_guide_switches.h"
#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
-#include "components/prefs/pref_service.h"
#include "components/variations/hashing.h"
#include "google_apis/google_api_keys.h"
#include "net/base/url_util.h"
@@ -90,14 +89,9 @@ const base::Feature kOptimizationHintsFieldTrials{
const base::Feature kRemoteOptimizationGuideFetching{
"OptimizationHintsFetching", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kRemoteOptimizationGuideFetchingAnonymousDataConsent {
- "OptimizationHintsFetchingAnonymousDataConsent",
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
- base::FEATURE_ENABLED_BY_DEFAULT
-#else // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
- base::FEATURE_DISABLED_BY_DEFAULT
-#endif // BUILDFLAG(IS_ANDROID)
-};
+const base::Feature kRemoteOptimizationGuideFetchingAnonymousDataConsent{
+ "OptimizationHintsFetchingAnonymousDataConsent",
+ base::FEATURE_ENABLED_BY_DEFAULT};
// Enables performance info in the context menu and fetching from a remote
// Optimization Guide Service.
@@ -123,6 +117,10 @@ const base::Feature kOptimizationGuideModelDownloading {
const base::Feature kPageContentAnnotations{"PageContentAnnotations",
enabled_by_default_desktop_only};
+// Enables fetching page metadata from the remote Optimization Guide service.
+const base::Feature kRemotePageMetadata{"RemotePageMetadata",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Enables the page entities model to be annotated on every page load.
const base::Feature kPageEntitiesPageContentAnnotations{
"PageEntitiesPageContentAnnotations", enabled_by_default_desktop_only};
@@ -160,17 +158,27 @@ const base::Feature kPageVisibilityBatchAnnotations{
const base::Feature kUseLocalPageEntitiesMetadataProvider{
"UseLocalPageEntitiesMetadataProvider", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kBatchAnnotationsValidation{
- "BatchAnnotationsValidation", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kPageContentAnnotationsValidation{
+ "PageContentAnnotationsValidation", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kPreventLongRunningPredictionModels{
- "PreventLongRunningPredictionModels", base::FEATURE_DISABLED_BY_DEFAULT};
+ "PreventLongRunningPredictionModels", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature
kOptimizationGuideUseContinueOnShutdownForPageContentAnnotations{
"OptimizationGuideUseContinueOnShutdownForPageContentAnnotations",
base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kOverrideNumThreadsForModelExecution{
+ "OverrideNumThreadsForModelExecution", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kOptGuideEnableXNNPACKDelegateWithTFLite{
+ "OptGuideEnableXNNPACKDelegateWithTFLite",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kOptimizationHintsComponent{
+ "OptimizationHintsComponent", base::FEATURE_ENABLED_BY_DEFAULT};
+
// The default value here is a bit of a guess.
// TODO(crbug/1163244): This should be tuned once metrics are available.
base::TimeDelta PageTextExtractionOutstandingRequestsGracePeriod() {
@@ -267,10 +275,8 @@ bool IsOptimizationHintsEnabled() {
return base::FeatureList::IsEnabled(kOptimizationHints);
}
-bool IsRemoteFetchingEnabled(PrefService* pref_service) {
- return base::FeatureList::IsEnabled(kRemoteOptimizationGuideFetching) &&
- pref_service->GetBoolean(
- optimization_guide::prefs::kOptimizationGuideFetchingEnabled);
+bool IsRemoteFetchingEnabled() {
+ return base::FeatureList::IsEnabled(kRemoteOptimizationGuideFetching);
}
bool IsPushNotificationsEnabled() {
@@ -418,7 +424,7 @@ base::TimeDelta PredictionModelFetchRetryDelay() {
base::TimeDelta PredictionModelFetchStartupDelay() {
return base::Milliseconds(GetFieldTrialParamByFeatureAsInt(
- kOptimizationTargetPrediction, "fetch_startup_delay_ms", 2000));
+ kOptimizationTargetPrediction, "fetch_startup_delay_ms", 10000));
}
base::TimeDelta PredictionModelFetchInterval() {
@@ -426,12 +432,20 @@ base::TimeDelta PredictionModelFetchInterval() {
kOptimizationTargetPrediction, "fetch_interval_hours", 24));
}
-absl::optional<base::TimeDelta> ModelExecutionTimeout() {
- if (!base::FeatureList::IsEnabled(kPreventLongRunningPredictionModels)) {
- return absl::nullopt;
- }
+bool IsModelExecutionWatchdogEnabled() {
+ return base::FeatureList::IsEnabled(kPreventLongRunningPredictionModels);
+}
+
+base::TimeDelta ModelExecutionWatchdogDefaultTimeout() {
return base::Milliseconds(GetFieldTrialParamByFeatureAsInt(
- kPreventLongRunningPredictionModels, "model_execution_timeout_ms", 2000));
+ kPreventLongRunningPredictionModels, "model_execution_timeout_ms",
+#if defined(_DEBUG)
+ // Debug builds take a much longer time to run.
+ 60 * 1000
+#else
+ 2000
+#endif
+ ));
}
base::flat_set<uint32_t> FieldTrialNameHashesAllowedForFetch() {
@@ -464,14 +478,10 @@ bool IsPageContentAnnotationEnabled() {
return base::FeatureList::IsEnabled(kPageContentAnnotations);
}
-uint64_t MaxSizeForPageContentTextDump() {
- return static_cast<uint64_t>(base::GetFieldTrialParamByFeatureAsInt(
- kPageContentAnnotations, "max_size_for_text_dump_in_bytes", 1024));
-}
-
-bool ShouldAnnotateTitleInsteadOfPageContent() {
+bool ShouldPersistSearchMetadataForNonGoogleSearches() {
return base::GetFieldTrialParamByFeatureAsBool(
- kPageContentAnnotations, "annotate_title_instead_of_page_content", true);
+ kPageContentAnnotations,
+ "persist_search_metadata_for_non_google_searches", true);
}
bool ShouldWriteContentAnnotationsToHistoryService() {
@@ -504,8 +514,13 @@ bool ShouldExecutePageVisibilityModelOnPageContent(const std::string& locale) {
}
bool RemotePageEntitiesEnabled() {
- return GetFieldTrialParamByFeatureAsBool(kPageContentAnnotations,
- "fetch_remote_page_entities", false);
+ return GetFieldTrialParamByFeatureAsBool(kRemotePageMetadata,
+ "persist_page_entities", false);
+}
+
+bool RemotePageMetadataEnabled() {
+ return GetFieldTrialParamByFeatureAsBool(kRemotePageMetadata,
+ "persist_page_metadata", false);
}
base::TimeDelta GetOnloadDelayForHintsFetching() {
@@ -563,25 +578,45 @@ size_t AnnotateVisitBatchSize() {
"annotate_visit_batch_size", 1));
}
-bool BatchAnnotationsValidationEnabled() {
- return base::FeatureList::IsEnabled(kBatchAnnotationsValidation);
-}
+bool PageContentAnnotationValidationEnabledForType(AnnotationType type) {
+ if (base::FeatureList::IsEnabled(kPageContentAnnotationsValidation)) {
+ if (GetFieldTrialParamByFeatureAsBool(kPageContentAnnotationsValidation,
+ AnnotationTypeToString(type),
+ false)) {
+ return true;
+ }
+ }
+
+ base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
+ switch (type) {
+ case AnnotationType::kPageTopics:
+ return cmd->HasSwitch(
+ switches::kPageContentAnnotationsValidationPageTopics);
+ case AnnotationType::kPageEntities:
+ return cmd->HasSwitch(
+ switches::kPageContentAnnotationsValidationPageEntities);
+ case AnnotationType::kContentVisibility:
+ return cmd->HasSwitch(
+ switches::kPageContentAnnotationsValidationContentVisibility);
+ default:
+ NOTREACHED();
+ break;
+ }
-base::TimeDelta BatchAnnotationValidationStartupDelay() {
- return base::Seconds(
- std::max(1, GetFieldTrialParamByFeatureAsInt(kBatchAnnotationsValidation,
- "startup_delay", 30)));
+ return false;
}
-size_t BatchAnnotationsValidationBatchSize() {
- int batch_size = GetFieldTrialParamByFeatureAsInt(kBatchAnnotationsValidation,
- "batch_size", 25);
- return std::max(1, batch_size);
+base::TimeDelta PageContentAnnotationValidationStartupDelay() {
+ return switches::PageContentAnnotationsValidationStartupDelay().value_or(
+ base::Seconds(std::max(
+ 1, GetFieldTrialParamByFeatureAsInt(kPageContentAnnotationsValidation,
+ "startup_delay", 30))));
}
-bool BatchAnnotationsValidationUsePageTopics() {
- return GetFieldTrialParamByFeatureAsBool(kBatchAnnotationsValidation,
- "use_page_topics", false);
+size_t PageContentAnnotationsValidationBatchSize() {
+ return switches::PageContentAnnotationsValidationBatchSize().value_or(
+ std::max(1, GetFieldTrialParamByFeatureAsInt(
+ kPageContentAnnotationsValidation, "batch_size", 25)));
}
size_t MaxVisitAnnotationCacheSize() {
@@ -590,5 +625,34 @@ size_t MaxVisitAnnotationCacheSize() {
return std::max(1, batch_size);
}
+absl::optional<int> OverrideNumThreadsForOptTarget(
+ proto::OptimizationTarget opt_target) {
+ if (!base::FeatureList::IsEnabled(kOverrideNumThreadsForModelExecution)) {
+ return absl::nullopt;
+ }
+
+ // 0 is an invalid value to pass to TFLite, so make that nullopt. -1 is valid,
+ // but not anything less than that.
+ int num_threads = GetFieldTrialParamByFeatureAsInt(
+ kOverrideNumThreadsForModelExecution,
+ proto::OptimizationTarget_Name(opt_target), 0);
+ if (num_threads == 0 || num_threads < -1) {
+ return absl::nullopt;
+ }
+
+ // Cap to the number of CPUs on the device.
+ return std::min(num_threads, base::SysInfo::NumberOfProcessors());
+}
+
+bool TFLiteXNNPACKDelegateEnabled() {
+ return base::FeatureList::IsEnabled(kOptGuideEnableXNNPACKDelegateWithTFLite);
+}
+
+bool ShouldCheckFailedComponentVersionPref() {
+ return GetFieldTrialParamByFeatureAsBool(
+ kOptimizationHintsComponent, "check_failed_component_version_pref",
+ false);
+}
+
} // namespace features
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/optimization_guide_features.h b/chromium/components/optimization_guide/core/optimization_guide_features.h
index 952d53006e9..e1b15dc0498 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_features.h
+++ b/chromium/components/optimization_guide/core/optimization_guide_features.h
@@ -12,14 +12,13 @@
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/time/time.h"
+#include "components/optimization_guide/core/page_content_annotation_type.h"
#include "components/optimization_guide/proto/hints.pb.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "net/nqe/effective_connection_type.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
-class PrefService;
-
namespace optimization_guide {
namespace features {
@@ -42,8 +41,12 @@ extern const base::Feature kPageVisibilityBatchAnnotations;
extern const base::Feature kPageEntitiesModelResetOnShutdown;
extern const base::Feature kPageEntitiesModelBypassFilters;
extern const base::Feature kUseLocalPageEntitiesMetadataProvider;
-extern const base::Feature kBatchAnnotationsValidation;
+extern const base::Feature kPageContentAnnotationsValidation;
extern const base::Feature kPreventLongRunningPredictionModels;
+extern const base::Feature kOverrideNumThreadsForModelExecution;
+extern const base::Feature kOptGuideEnableXNNPACKDelegateWithTFLite;
+extern const base::Feature kRemotePageMetadata;
+extern const base::Feature kOptimizationHintsComponent;
// Enables use of task runner with trait CONTINUE_ON_SHUTDOWN for page content
// annotations on-device models.
@@ -90,8 +93,8 @@ bool IsOptimizationTargetPredictionEnabled();
bool IsOptimizationHintsEnabled();
// Returns true if the feature to fetch from the remote Optimization Guide
-// Service is enabled.
-bool IsRemoteFetchingEnabled(PrefService* pref_service);
+// Service is enabled. This controls the fetching of both hints and models.
+bool IsRemoteFetchingEnabled();
// Returns true if the feature to fetch data for users that have consented to
// anonymous data collection is enabled but are not Data Saver users.
@@ -203,8 +206,11 @@ base::TimeDelta PredictionModelFetchStartupDelay();
// refresh models.
base::TimeDelta PredictionModelFetchInterval();
-// The timeout for executing models, if enabled.
-absl::optional<base::TimeDelta> ModelExecutionTimeout();
+// Whether to use the model execution watchdog.
+bool IsModelExecutionWatchdogEnabled();
+
+// The default timeout for the watchdog to use if none is given by the caller.
+base::TimeDelta ModelExecutionWatchdogDefaultTimeout();
// Returns a set of field trial name hashes that can be sent in the request to
// the remote Optimization Guide Service if the client is in one of the
@@ -221,12 +227,9 @@ bool IsUnrestrictedModelDownloadingEnabled();
// Returns whether the feature to annotate page content is enabled.
bool IsPageContentAnnotationEnabled();
-// Returns the max size that should be requested for a page content text dump.
-uint64_t MaxSizeForPageContentTextDump();
-
-// Returns whether the title should always be annotated instead of a page
-// content text dump.
-bool ShouldAnnotateTitleInsteadOfPageContent();
+// Whether search metadata should be persisted for non-Google searches, as
+// identified by the TemplateURLService.
+bool ShouldPersistSearchMetadataForNonGoogleSearches();
// Whether we should write content annotations to History Service.
bool ShouldWriteContentAnnotationsToHistoryService();
@@ -251,6 +254,10 @@ bool ShouldExecutePageVisibilityModelOnPageContent(const std::string& locale);
// Optimization Guide service.
bool RemotePageEntitiesEnabled();
+// Returns whether page metadata should be retrieved from the remote
+// Optimization Guide service.
+bool RemotePageMetadataEnabled();
+
// The time to wait beyond the onload event before sending the hints request for
// link predictions.
base::TimeDelta GetOnloadDelayForHintsFetching();
@@ -280,23 +287,32 @@ bool UseLocalPageEntitiesMetadataProvider();
// immediately after requested.
size_t AnnotateVisitBatchSize();
-// Whether the batch annotation validation feature is enabled.
-bool BatchAnnotationsValidationEnabled();
+// Whether the page content annotation validation feature or command line flag
+// is enabled for the given annotation type.
+bool PageContentAnnotationValidationEnabledForType(AnnotationType type);
-// The time period between browser start and running a running batch annotation
-// validation.
-base::TimeDelta BatchAnnotationValidationStartupDelay();
+// The time period between browser start and running a running page content
+// annotation validation.
+base::TimeDelta PageContentAnnotationValidationStartupDelay();
-// The size of batches to run for validation.
-size_t BatchAnnotationsValidationBatchSize();
-
-// True if the batch annotations feature should use the PageTopics annotation
-// type instead of ContentVisibility.
-bool BatchAnnotationsValidationUsePageTopics();
+// The size of batches to run for page content validation.
+size_t PageContentAnnotationsValidationBatchSize();
// The maximum size of the visit annotation cache.
size_t MaxVisitAnnotationCacheSize();
+// Returns the number of threads to use for model inference on the given
+// optimization target.
+absl::optional<int> OverrideNumThreadsForOptTarget(
+ proto::OptimizationTarget opt_target);
+
+// Whether XNNPACK should be used with TFLite, on platforms where it is
+// supported. This is a no-op on unsupported platforms.
+bool TFLiteXNNPACKDelegateEnabled();
+
+// Whether to check the pref for whether a previous component version failed.
+bool ShouldCheckFailedComponentVersionPref();
+
} // namespace features
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/optimization_guide_features_unittest.cc b/chromium/components/optimization_guide/core/optimization_guide_features_unittest.cc
index 48433bff52b..af09288ae48 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_features_unittest.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_features_unittest.cc
@@ -4,13 +4,17 @@
#include "components/optimization_guide/core/optimization_guide_features.h"
+#include <limits>
#include <string>
#include "base/feature_list.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/system/sys_info.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
+#include "components/optimization_guide/core/model_util.h"
#include "components/optimization_guide/core/optimization_guide_constants.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -136,6 +140,140 @@ TEST(OptimizationGuideFeaturesTest,
features::ShouldExecutePageVisibilityModelOnPageContent("zh-CN"));
}
+TEST(OptimizationGuideFeaturesTest, TestOverrideNumThreadsForOptTarget) {
+ struct TestCase {
+ std::string label;
+ bool enabled;
+ std::map<std::string, std::string> params;
+ std::vector<std::pair<proto::OptimizationTarget, absl::optional<int>>> want;
+ };
+
+ struct TestCase tests[] = {
+ {
+ .label = "feature disabled",
+ .enabled = false,
+ .params = {},
+ .want =
+ {
+ {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, absl::nullopt},
+ {proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2, absl::nullopt},
+ },
+ },
+ {
+ .label = "feature enabled, but no params",
+ .enabled = true,
+ .params = {},
+ .want =
+ {
+ {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, absl::nullopt},
+ {proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2, absl::nullopt},
+ },
+ },
+ {
+ .label = "one target overriden",
+ .enabled = true,
+ .params =
+ {
+ {"OPTIMIZATION_TARGET_PAGE_TOPICS_V2", "1"},
+ },
+ .want =
+ {
+ {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, absl::nullopt},
+ {proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2, 1},
+ },
+ },
+ {
+ .label = "zero is nullopt",
+ .enabled = true,
+ .params =
+ {
+ {"OPTIMIZATION_TARGET_PAGE_TOPICS_V2", "0"},
+ },
+ .want =
+ {
+ {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, absl::nullopt},
+ {proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2, absl::nullopt},
+ },
+ },
+ {
+ .label = "less than -1",
+ .enabled = true,
+ .params =
+ {
+ {"OPTIMIZATION_TARGET_PAGE_TOPICS_V2", "-2"},
+ },
+ .want =
+ {
+ {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, absl::nullopt},
+ {proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2, absl::nullopt},
+ },
+ },
+ {
+ .label = "-1 is valid",
+ .enabled = true,
+ .params =
+ {
+ {"OPTIMIZATION_TARGET_PAGE_TOPICS_V2", "-1"},
+ },
+ .want =
+ {
+ {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, absl::nullopt},
+ {proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2, -1},
+ },
+ },
+ {
+ .label = "two targets overriden",
+ .enabled = true,
+ .params =
+ {
+ {"OPTIMIZATION_TARGET_PAGE_TOPICS_V2", "1"},
+ {"OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD", "-1"},
+ },
+ .want =
+ {
+ {proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2, 1},
+ {proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, -1},
+ },
+ },
+ {
+ .label = "capped at num cpu",
+ .enabled = true,
+ .params =
+ {
+ {"OPTIMIZATION_TARGET_PAGE_TOPICS_V2",
+ base::NumberToString(std::numeric_limits<int>::max())},
+ },
+ .want =
+ {
+ {proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2,
+ base::SysInfo::NumberOfProcessors()},
+ },
+ },
+ };
+
+ for (const TestCase& test : tests) {
+ SCOPED_TRACE(test.label);
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ if (test.enabled) {
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kOverrideNumThreadsForModelExecution, test.params);
+ } else {
+ scoped_feature_list.InitAndDisableFeature(
+ features::kOverrideNumThreadsForModelExecution);
+ }
+
+ for (const auto& expectation : test.want) {
+ proto::OptimizationTarget opt_target = expectation.first;
+ absl::optional<int> num_threads = expectation.second;
+
+ EXPECT_EQ(num_threads,
+ features::OverrideNumThreadsForOptTarget(opt_target))
+ << GetStringNameForOptimizationTarget(opt_target);
+ }
+ }
+}
+
} // namespace
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/optimization_guide_logger.cc b/chromium/components/optimization_guide/core/optimization_guide_logger.cc
index b799bb67431..76eb8734cb8 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_logger.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_logger.cc
@@ -28,6 +28,12 @@ OptimizationGuideLogger::LogMessageBuilder::LogMessageBuilder(
optimization_guide_logger_(optimization_guide_logger) {}
OptimizationGuideLogger::LogMessageBuilder::~LogMessageBuilder() {
+ if (!optimization_guide_logger_) {
+ // It is possible for this to not be available in tests, so just return
+ // here.
+ return;
+ }
+
std::string message = base::StrCat(messages_);
optimization_guide_logger_->OnLogMessageAdded(base::Time::Now(), source_file_,
source_line_, message);
diff --git a/chromium/components/optimization_guide/core/optimization_guide_logger.h b/chromium/components/optimization_guide/core/optimization_guide_logger.h
index 42373e164bf..9b40e75748d 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_logger.h
+++ b/chromium/components/optimization_guide/core/optimization_guide_logger.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/containers/circular_deque.h"
+#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
@@ -73,7 +74,7 @@ class OptimizationGuideLogger {
std::string source_file_;
int source_line_;
std::vector<std::string> messages_;
- OptimizationGuideLogger* optimization_guide_logger_;
+ raw_ptr<OptimizationGuideLogger> optimization_guide_logger_;
};
private:
diff --git a/chromium/components/optimization_guide/core/optimization_guide_permissions_util.cc b/chromium/components/optimization_guide/core/optimization_guide_permissions_util.cc
index ab81b717c35..fec08802020 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_permissions_util.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_permissions_util.cc
@@ -40,7 +40,7 @@ bool IsUserPermittedToFetchFromRemoteOptimizationGuide(
return true;
}
- if (!features::IsRemoteFetchingEnabled(pref_service))
+ if (!features::IsRemoteFetchingEnabled())
return false;
if (features::IsRemoteFetchingExplicitlyAllowedForPerformanceInfo())
diff --git a/chromium/components/optimization_guide/core/optimization_guide_permissions_util.h b/chromium/components/optimization_guide/core/optimization_guide_permissions_util.h
index 9b80ab969dc..da88197a4ae 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_permissions_util.h
+++ b/chromium/components/optimization_guide/core/optimization_guide_permissions_util.h
@@ -11,6 +11,9 @@ namespace optimization_guide {
// Returns true if the user, as represented by |profile| is permitted to make
// calls to the remote Optimization Guide Service.
+//
+// Note that this does not include the additional enterprise policy check that
+// gates model downloads.
bool IsUserPermittedToFetchFromRemoteOptimizationGuide(
bool is_off_the_record,
PrefService* pref_service);
diff --git a/chromium/components/optimization_guide/core/optimization_guide_permissions_util_unittest.cc b/chromium/components/optimization_guide/core/optimization_guide_permissions_util_unittest.cc
index 7e78be643b7..015955ccc26 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_permissions_util_unittest.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_permissions_util_unittest.cc
@@ -8,7 +8,6 @@
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_prefs.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/unified_consent/pref_names.h"
#include "components/unified_consent/unified_consent_service.h"
@@ -21,7 +20,6 @@ class OptimizationGuidePermissionsUtilTest : public testing::Test {
void SetUp() override {
unified_consent::UnifiedConsentService::RegisterPrefs(
pref_service_.registry());
- prefs::RegisterProfilePrefs(pref_service_.registry());
}
void SetUrlKeyedAnonymizedDataCollectionEnabled(bool enabled) {
@@ -30,10 +28,6 @@ class OptimizationGuidePermissionsUtilTest : public testing::Test {
enabled);
}
- void SetOptimizationGuideFetchingPrefEnabled(bool enabled) {
- pref_service_.SetBoolean(prefs::kOptimizationGuideFetchingEnabled, enabled);
- }
-
PrefService* pref_service() { return &pref_service_; }
private:
@@ -106,15 +100,6 @@ TEST_F(OptimizationGuidePermissionsUtilTest,
}
TEST_F(OptimizationGuidePermissionsUtilTest,
- IsUserPermittedToFetchHintsAllFeaturesEnabledButPrefDisabled) {
- SetUrlKeyedAnonymizedDataCollectionEnabled(true);
- SetOptimizationGuideFetchingPrefEnabled(false);
-
- EXPECT_FALSE(IsUserPermittedToFetchFromRemoteOptimizationGuide(
- /*is_off_the_record=*/false, pref_service()));
-}
-
-TEST_F(OptimizationGuidePermissionsUtilTest,
IsUserPermittedToFetchHintsPerformanceInfoFlagExplicitlyAllows) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
diff --git a/chromium/components/optimization_guide/core/optimization_guide_prefs.cc b/chromium/components/optimization_guide/core/optimization_guide_prefs.cc
index 388fefdacda..87ccde8f964 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_prefs.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_prefs.cc
@@ -49,10 +49,6 @@ const char kPendingHintsProcessingVersion[] =
const char kPreviouslyRegisteredOptimizationTypes[] =
"optimization_guide.previously_registered_optimization_types";
-// A boolean pref that stores whether fetching is enabled. True by default.
-const char kOptimizationGuideFetchingEnabled[] =
- "optimization_guide.fetching_enabled";
-
// A dictionary pref that stores the file paths that need to be deleted as keys.
// The value will not be used.
const char kStoreFilePathsToDelete[] =
@@ -76,7 +72,6 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
PrefRegistry::LOSSY_PREF);
registry->RegisterDictionaryPref(kPreviouslyRegisteredOptimizationTypes,
PrefRegistry::LOSSY_PREF);
- registry->RegisterBooleanPref(kOptimizationGuideFetchingEnabled, true);
registry->RegisterDictionaryPref(kStoreFilePathsToDelete,
PrefRegistry::LOSSY_PREF);
}
diff --git a/chromium/components/optimization_guide/core/optimization_guide_prefs.h b/chromium/components/optimization_guide/core/optimization_guide_prefs.h
index 9a503d5e72b..220147b6761 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_prefs.h
+++ b/chromium/components/optimization_guide/core/optimization_guide_prefs.h
@@ -16,7 +16,6 @@ extern const char kModelLastFetchSuccess[];
extern const char kHintsFetcherHostsSuccessfullyFetched[];
extern const char kPendingHintsProcessingVersion[];
extern const char kPreviouslyRegisteredOptimizationTypes[];
-extern const char kOptimizationGuideFetchingEnabled[];
extern const char kStoreFilePathsToDelete[];
// Registers the optimization guide's prefs.
diff --git a/chromium/components/optimization_guide/core/optimization_guide_store.cc b/chromium/components/optimization_guide/core/optimization_guide_store.cc
index e9d942d725f..16fdf9169d4 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_store.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_store.cc
@@ -1018,8 +1018,7 @@ void OptimizationGuideStore::OnLoadModelsToBeUpdated(
// directory or file. But in the case of a directory, it is recursively
// deleted.
store_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(base::GetDeletePathRecursivelyCallback(),
- path_to_delete));
+ FROM_HERE, base::GetDeletePathRecursivelyCallback(path_to_delete));
}
}
}
diff --git a/chromium/components/optimization_guide/core/optimization_guide_switches.cc b/chromium/components/optimization_guide/core/optimization_guide_switches.cc
index e2026ddc1cf..6bf5467d428 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_switches.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_switches.cc
@@ -7,6 +7,7 @@
#include "base/base64.h"
#include "base/command_line.h"
#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "build/build_config.h"
#include "components/optimization_guide/proto/hints.pb.h"
@@ -81,14 +82,29 @@ const char kModelOverride[] = "optimization-guide-model-override";
// Triggers validation of the model. Used for manual testing.
const char kModelValidate[] = "optimization-guide-model-validate";
-// Prevents any models from being executing when in annotating a batch
-// of visits. This is used for testing only.
-const char kStopHistoryVisitBatchAnnotateForTesting[] =
- "stop-history-visit-batch-annotate";
-
const char kPageContentAnnotationsLoggingEnabled[] =
"enable-page-content-annotations-logging";
+const char kPageContentAnnotationsValidationStartupDelaySeconds[] =
+ "page-content-annotations-validation-startup-delay-seconds";
+
+const char kPageContentAnnotationsValidationBatchSizeOverride[] =
+ "page-content-annotations-validation-batch-size";
+
+// Enables the specific annotation type to run validation at startup after a
+// delay. A comma separated list of inputs can be given as a value which will be
+// used as input for the validation job.
+const char kPageContentAnnotationsValidationPageTopics[] =
+ "page-content-annotations-validation-page-topics";
+const char kPageContentAnnotationsValidationPageEntities[] =
+ "page-content-annotations-validation-page-entities";
+const char kPageContentAnnotationsValidationContentVisibility[] =
+ "page-content-annotations-validation-content-visibility";
+
+// Writes the output of page content annotation validations to the given file.
+const char kPageContentAnnotationsValidationWriteToFile[] =
+ "page-content-annotations-validation-write-to-file";
+
bool IsHintComponentProcessingDisabled() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(kHintsProtoOverride);
}
@@ -188,26 +204,10 @@ bool ShouldValidateModel() {
}
absl::optional<std::string> GetModelOverride() {
-#if BUILDFLAG(IS_WIN)
- // TODO(crbug/1227996): The parsing below is not supported on Windows because
- // ':' is used as a delimiter, but this must be used in the absolute file path
- // on Windows.
- DLOG(ERROR)
- << "--optimization-guide-model-override is not available on Windows";
- return absl::nullopt;
-#else
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(kModelOverride))
return absl::nullopt;
return command_line->GetSwitchValueASCII(kModelOverride);
-#endif
-}
-
-bool StopHistoryVisitBatchAnnotateForTesting() {
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(kStopHistoryVisitBatchAnnotateForTesting))
- return true;
- return false;
}
bool ShouldLogPageContentAnnotationsInput() {
@@ -215,5 +215,86 @@ bool ShouldLogPageContentAnnotationsInput() {
kPageContentAnnotationsLoggingEnabled);
}
+absl::optional<base::TimeDelta> PageContentAnnotationsValidationStartupDelay() {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (!command_line->HasSwitch(
+ kPageContentAnnotationsValidationStartupDelaySeconds)) {
+ return absl::nullopt;
+ }
+
+ std::string value = command_line->GetSwitchValueASCII(
+ kPageContentAnnotationsValidationStartupDelaySeconds);
+
+ size_t seconds = 0;
+ if (base::StringToSizeT(value, &seconds)) {
+ return base::Seconds(seconds);
+ }
+ return absl::nullopt;
+}
+
+absl::optional<size_t> PageContentAnnotationsValidationBatchSize() {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (!command_line->HasSwitch(
+ kPageContentAnnotationsValidationBatchSizeOverride)) {
+ return absl::nullopt;
+ }
+
+ std::string value = command_line->GetSwitchValueASCII(
+ kPageContentAnnotationsValidationBatchSizeOverride);
+
+ size_t size = 0;
+ if (base::StringToSizeT(value, &size)) {
+ return size;
+ }
+ return absl::nullopt;
+}
+
+bool LogPageContentAnnotationsValidationToConsole() {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ return command_line->HasSwitch(kPageContentAnnotationsValidationPageTopics) ||
+ command_line->HasSwitch(
+ kPageContentAnnotationsValidationPageEntities) ||
+ command_line->HasSwitch(
+ kPageContentAnnotationsValidationContentVisibility);
+}
+
+absl::optional<std::vector<std::string>>
+PageContentAnnotationsValidationInputForType(AnnotationType type) {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+ std::string value;
+ switch (type) {
+ case AnnotationType::kPageTopics:
+ value = command_line->GetSwitchValueASCII(
+ kPageContentAnnotationsValidationPageTopics);
+ break;
+ case AnnotationType::kPageEntities:
+ value = command_line->GetSwitchValueASCII(
+ kPageContentAnnotationsValidationPageEntities);
+ break;
+ case AnnotationType::kContentVisibility:
+ value = command_line->GetSwitchValueASCII(
+ kPageContentAnnotationsValidationContentVisibility);
+ break;
+ default:
+ break;
+ }
+ if (value.empty()) {
+ return absl::nullopt;
+ }
+
+ return base::SplitString(value, ",", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_ALL);
+}
+
+absl::optional<base::FilePath> PageContentAnnotationsValidationWriteToFile() {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (!command_line->HasSwitch(kPageContentAnnotationsValidationWriteToFile)) {
+ return absl::nullopt;
+ }
+ return command_line->GetSwitchValuePath(
+ kPageContentAnnotationsValidationWriteToFile);
+}
+
} // namespace switches
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/optimization_guide_switches.h b/chromium/components/optimization_guide/core/optimization_guide_switches.h
index c7e80ceeabb..57814148858 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_switches.h
+++ b/chromium/components/optimization_guide/core/optimization_guide_switches.h
@@ -9,6 +9,9 @@
#include <string>
#include <vector>
+#include "base/files/file_path.h"
+#include "base/time/time.h"
+#include "components/optimization_guide/core/page_content_annotation_type.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -33,8 +36,13 @@ extern const char kDisableModelDownloadVerificationForTesting[];
extern const char kModelOverride[];
extern const char kDebugLoggingEnabled[];
extern const char kModelValidate[];
-extern const char kStopHistoryVisitBatchAnnotateForTesting[];
extern const char kPageContentAnnotationsLoggingEnabled[];
+extern const char kPageContentAnnotationsValidationStartupDelaySeconds[];
+extern const char kPageContentAnnotationsValidationBatchSizeOverride[];
+extern const char kPageContentAnnotationsValidationPageTopics[];
+extern const char kPageContentAnnotationsValidationPageEntities[];
+extern const char kPageContentAnnotationsValidationContentVisibility[];
+extern const char kPageContentAnnotationsValidationWriteToFile[];
// Returns whether the hint component should be processed.
// Available hint components are only processed if a proto override isn't being
@@ -89,13 +97,30 @@ absl::optional<std::string> GetModelOverride();
// Returns true if debug logs are enabled for the optimization guide.
bool IsDebugLogsEnabled();
-// Whether to prevent annotations from happening when in a batch. For testing
-// purposes only.
-bool StopHistoryVisitBatchAnnotateForTesting();
-
// Returns true if page content annotations input should be logged.
bool ShouldLogPageContentAnnotationsInput();
+// Returns the delay to use for page content annotations validation, if given
+// and valid on the command line.
+absl::optional<base::TimeDelta> PageContentAnnotationsValidationStartupDelay();
+
+// Returns the size of the batch to use for page content annotations validation,
+// if given and valid on the command line.
+absl::optional<size_t> PageContentAnnotationsValidationBatchSize();
+
+// Whether the result of page content annotations validation should be sent to
+// the console. True when any one of the corresponding command line flags is
+// enabled.
+bool LogPageContentAnnotationsValidationToConsole();
+
+// Returns a set on inputs to run the validation on for the given |type|,
+// using comma separated input from the command line.
+absl::optional<std::vector<std::string>>
+PageContentAnnotationsValidationInputForType(AnnotationType type);
+
+// Returns the file path to write page content annotation validation results to.
+absl::optional<base::FilePath> PageContentAnnotationsValidationWriteToFile();
+
} // namespace switches
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/optimization_guide_test_util.cc b/chromium/components/optimization_guide/core/optimization_guide_test_util.cc
index e349570a811..76d17e05123 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_test_util.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_test_util.cc
@@ -10,8 +10,8 @@
namespace optimization_guide {
#if BUILDFLAG(IS_WIN)
-const char kTestAbsoluteFilePath[] = "C:\\absolute/file/path";
-const char kTestRelativeFilePath[] = "relative/file/path";
+const char kTestAbsoluteFilePath[] = "C:\\absolute\\file\\path";
+const char kTestRelativeFilePath[] = "relative\\file\\path";
#else
const char kTestAbsoluteFilePath[] = "/absolutefilepath";
const char kTestRelativeFilePath[] = "relativefilepath";
diff --git a/chromium/components/optimization_guide/core/optimization_guide_util.cc b/chromium/components/optimization_guide/core/optimization_guide_util.cc
index 15acd17ad35..39f39576ce7 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_util.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_util.cc
@@ -4,15 +4,11 @@
#include "components/optimization_guide/core/optimization_guide_util.h"
-#include "base/base64.h"
#include "base/containers/flat_set.h"
#include "base/notreached.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
#include "components/optimization_guide/core/optimization_guide_decision.h"
#include "components/optimization_guide/core/optimization_guide_enums.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_switches.h"
#include "components/variations/active_field_trials.h"
#include "net/base/url_util.h"
#include "url/url_canon.h"
@@ -78,72 +74,4 @@ std::string GetStringForOptimizationGuideDecision(
return std::string();
}
-absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
-GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OptimizationTarget optimization_target) {
-#if BUILDFLAG(IS_WIN)
- // TODO(crbug/1227996): The parsing below is not supported on Windows because
- // ':' is used as a delimiter, but this must be used in the absolute file path
- // on Windows.
- DLOG(ERROR)
- << "--optimization-guide-model-override is not available on Windows";
- return absl::nullopt;
-#else
- auto model_override_switch_value = switches::GetModelOverride();
- if (!model_override_switch_value)
- return absl::nullopt;
-
- std::vector<std::string> model_overrides =
- base::SplitString(*model_override_switch_value, ",",
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- for (const auto& model_override : model_overrides) {
- std::vector<std::string> override_parts = base::SplitString(
- model_override, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- if (override_parts.size() != 2 && override_parts.size() != 3) {
- // Input is malformed.
- DLOG(ERROR) << "Invalid string format provided to the Model Override";
- return absl::nullopt;
- }
-
- optimization_guide::proto::OptimizationTarget recv_optimization_target;
- if (!optimization_guide::proto::OptimizationTarget_Parse(
- override_parts[0], &recv_optimization_target)) {
- // Optimization target is invalid.
- DLOG(ERROR)
- << "Invalid optimization target provided to the Model Override";
- return absl::nullopt;
- }
- if (optimization_target != recv_optimization_target)
- continue;
-
- std::string file_name = override_parts[1];
- if (!base::FilePath(file_name).IsAbsolute()) {
- DLOG(ERROR) << "Provided model file path must be absolute " << file_name;
- return absl::nullopt;
- }
-
- if (override_parts.size() == 2) {
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>
- file_path_and_metadata = std::make_pair(file_name, absl::nullopt);
- return file_path_and_metadata;
- }
- std::string binary_pb;
- if (!base::Base64Decode(override_parts[2], &binary_pb)) {
- DLOG(ERROR) << "Invalid base64 encoding of the Model Override";
- return absl::nullopt;
- }
- optimization_guide::proto::Any model_metadata;
- if (!model_metadata.ParseFromString(binary_pb)) {
- DLOG(ERROR) << "Invalid model metadata provided to the Model Override";
- return absl::nullopt;
- }
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>
- file_path_and_metadata = std::make_pair(file_name, model_metadata);
- return file_path_and_metadata;
- }
- return absl::nullopt;
-#endif
-}
-
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/optimization_guide_util.h b/chromium/components/optimization_guide/core/optimization_guide_util.h
index 90b70f34bc1..f20e3a2902b 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_util.h
+++ b/chromium/components/optimization_guide/core/optimization_guide_util.h
@@ -72,13 +72,6 @@ absl::optional<T> ParsedAnyMetadata(const proto::Any& any_metadata) {
std::string GetStringForOptimizationGuideDecision(
OptimizationGuideDecision decision);
-// Returns the file path string and metadata for the model provided via
-// command-line for |optimization_target|, if applicable.
-absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
-GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OptimizationTarget optimization_target);
-
} // namespace optimization_guide
#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_OPTIMIZATION_GUIDE_UTIL_H_
diff --git a/chromium/components/optimization_guide/core/optimization_guide_util_unittest.cc b/chromium/components/optimization_guide/core/optimization_guide_util_unittest.cc
index f3a7fee11ae..2842e6b321c 100644
--- a/chromium/components/optimization_guide/core/optimization_guide_util_unittest.cc
+++ b/chromium/components/optimization_guide/core/optimization_guide_util_unittest.cc
@@ -4,11 +4,6 @@
#include "components/optimization_guide/core/optimization_guide_util.h"
-#include "base/base64.h"
-#include "base/command_line.h"
-#include "base/strings/stringprintf.h"
-#include "build/build_config.h"
-#include "components/optimization_guide/core/optimization_guide_switches.h"
#include "components/optimization_guide/core/optimization_guide_test_util.h"
#include "components/optimization_guide/proto/loading_predictor_metadata.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -63,141 +58,4 @@ TEST(OptimizationGuideUtilTest, ParsedAnyMetadataTest) {
EXPECT_TRUE(parsed_subresource.preconnect_only());
}
-#if !BUILDFLAG(IS_WIN)
-
-TEST(OptimizationGuideUtilTest,
- GetModelOverrideForOptimizationTargetSwitchNotSet) {
- absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
- file_path_and_metadata = GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-
- EXPECT_EQ(absl::nullopt, file_path_and_metadata);
- EXPECT_FALSE(switches::IsModelOverridePresent());
-}
-
-TEST(OptimizationGuideUtilTest,
- GetModelOverrideForOptimizationTargetEmptyInput) {
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kModelOverride);
-
- absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
- file_path_and_metadata = GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-
- EXPECT_EQ(absl::nullopt, file_path_and_metadata);
-}
-
-TEST(OptimizationGuideUtilTest, GetModelOverrideForOptimizationTargetBadInput) {
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kModelOverride, "whatever");
-
- absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
- file_path_and_metadata = GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-
- EXPECT_EQ(absl::nullopt, file_path_and_metadata);
-}
-
-TEST(OptimizationGuideUtilTest,
- GetModelOverrideForOptimizationTargetInvalidOptimizationTarget) {
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kModelOverride,
- "notanoptimizationtarget:" + std::string(kTestAbsoluteFilePath));
-
- absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
- file_path_and_metadata = GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-
- EXPECT_EQ(absl::nullopt, file_path_and_metadata);
-}
-
-TEST(OptimizationGuideUtilTest,
- GetModelOverrideForOptimizationTargetRelativeFilePath) {
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kModelOverride, "OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:" +
- std::string(kTestRelativeFilePath));
-
- absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
- file_path_and_metadata = GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-
- EXPECT_EQ(absl::nullopt, file_path_and_metadata);
-}
-
-TEST(OptimizationGuideUtilTest,
- GetModelOverrideForOptimizationTargetRelativeFilePathWithMetadata) {
- optimization_guide::proto::Any metadata;
- metadata.set_type_url("sometypeurl");
- std::string encoded_metadata;
- metadata.SerializeToString(&encoded_metadata);
- base::Base64Encode(encoded_metadata, &encoded_metadata);
-
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kModelOverride,
- base::StringPrintf("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:%s:%s",
- kTestRelativeFilePath, encoded_metadata.c_str()));
-
- absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
- file_path_and_metadata = GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-
- EXPECT_EQ(absl::nullopt, file_path_and_metadata);
-}
-
-TEST(OptimizationGuideUtilTest,
- GetModelOverrideForOptimizationTargetOneFilePath) {
- optimization_guide::proto::Any metadata;
- metadata.set_type_url("sometypeurl");
- std::string encoded_metadata;
- metadata.SerializeToString(&encoded_metadata);
- base::Base64Encode(encoded_metadata, &encoded_metadata);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kModelOverride,
- base::StringPrintf("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:%s:%s",
- kTestAbsoluteFilePath, encoded_metadata.c_str()));
-
- absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
- file_path_and_metadata = GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-
- EXPECT_EQ(kTestAbsoluteFilePath, file_path_and_metadata->first);
- EXPECT_EQ("sometypeurl", file_path_and_metadata->second->type_url());
-}
-
-TEST(OptimizationGuideUtilTest,
- GetModelOverrideForOptimizationTargetMultipleFilePath) {
- const char kOtherAbsoluteFilePath[] = "/other/file/path";
- optimization_guide::proto::Any metadata;
- metadata.set_type_url("sometypeurl");
- std::string encoded_metadata;
- metadata.SerializeToString(&encoded_metadata);
- base::Base64Encode(encoded_metadata, &encoded_metadata);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kModelOverride,
- base::StringPrintf("OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:%s,"
- "OPTIMIZATION_TARGET_PAGE_TOPICS:%s:%s",
- kTestAbsoluteFilePath, kOtherAbsoluteFilePath,
- encoded_metadata.c_str()));
-
- absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
- file_path_and_metadata = GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
- EXPECT_EQ(kTestAbsoluteFilePath, file_path_and_metadata->first);
-
- file_path_and_metadata = GetModelOverrideForOptimizationTarget(
- optimization_guide::proto::OPTIMIZATION_TARGET_PAGE_TOPICS);
- EXPECT_EQ(kOtherAbsoluteFilePath, file_path_and_metadata->first);
- EXPECT_EQ("sometypeurl", file_path_and_metadata->second->type_url());
-}
-
-#endif
-
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/page_content_annotation_type.cc b/chromium/components/optimization_guide/core/page_content_annotation_type.cc
new file mode 100644
index 00000000000..d0fd790a5a1
--- /dev/null
+++ b/chromium/components/optimization_guide/core/page_content_annotation_type.cc
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium 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/optimization_guide/core/page_content_annotation_type.h"
+
+namespace optimization_guide {
+
+// Each of these string values is used in UMA histograms so please update the
+// variants there when any changes are made.
+// //tools/metrics/histograms/metadata/optimization/histograms.xml
+std::string AnnotationTypeToString(AnnotationType type) {
+ switch (type) {
+ case AnnotationType::kUnknown:
+ return "Unknown";
+ case AnnotationType::kPageTopics:
+ return "PageTopics";
+ case AnnotationType::kContentVisibility:
+ return "ContentVisibility";
+ case AnnotationType::kPageEntities:
+ return "PageEntities";
+ }
+}
+
+} // namespace optimization_guide \ No newline at end of file
diff --git a/chromium/components/optimization_guide/core/page_content_annotation_type.h b/chromium/components/optimization_guide/core/page_content_annotation_type.h
new file mode 100644
index 00000000000..716a95f8fff
--- /dev/null
+++ b/chromium/components/optimization_guide/core/page_content_annotation_type.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_OPTIMIZATION_GUIDE_CORE_PAGE_CONTENT_ANNOTATION_TYPE_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_PAGE_CONTENT_ANNOTATION_TYPE_H_
+
+#include <string>
+
+namespace optimization_guide {
+
+// The type of annotation that is being done on the given input.
+//
+// Each of these is used in UMA histograms so please update the variants there
+// when any changes are made.
+// //tools/metrics/histograms/metadata/optimization/histograms.xml
+enum class AnnotationType {
+ kUnknown,
+
+ // The input will be annotated with the topics on the page. These topics are
+ // fairly high-level like "sports" or "news".
+ kPageTopics,
+
+ // The input will be annotated for the visibility of the content.
+ kContentVisibility,
+
+ // The input will be annotated with the entities on the page. If the entities
+ // will be persisted, make sure that only the entity IDs are persisted. To map
+ // the IDs back to human-readable strings, use `EntityMetadataProvider`.
+ kPageEntities,
+};
+
+std::string AnnotationTypeToString(AnnotationType type);
+
+} // namespace optimization_guide
+
+#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_PAGE_CONTENT_ANNOTATION_TYPE_H_ \ No newline at end of file
diff --git a/chromium/components/optimization_guide/core/page_content_annotations_common.cc b/chromium/components/optimization_guide/core/page_content_annotations_common.cc
index a5e3d5b67a3..2c7eda17c93 100644
--- a/chromium/components/optimization_guide/core/page_content_annotations_common.cc
+++ b/chromium/components/optimization_guide/core/page_content_annotations_common.cc
@@ -5,30 +5,17 @@
#include "components/optimization_guide/core/page_content_annotations_common.h"
#include <algorithm>
+#include <ostream>
#include "base/check_op.h"
+#include "base/json/json_writer.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
namespace optimization_guide {
-// Each of these string values is used in UMA histograms so please update the
-// variants there when any changes are made.
-// //tools/metrics/histograms/metadata/optimization/histograms.xml
-std::string AnnotationTypeToString(AnnotationType type) {
- switch (type) {
- case AnnotationType::kUnknown:
- return "Unknown";
- case AnnotationType::kPageTopics:
- return "PageTopics";
- case AnnotationType::kContentVisibility:
- return "ContentVisibility";
- case AnnotationType::kPageEntities:
- return "PageEntities";
- }
-}
-
WeightedIdentifier::WeightedIdentifier(int32_t value, double weight)
: value_(value), weight_(weight) {
DCHECK_GE(weight_, 0.0);
@@ -47,6 +34,13 @@ std::string WeightedIdentifier::ToString() const {
return base::StringPrintf("WeightedIdentifier{%d,%f}", value(), weight());
}
+base::Value WeightedIdentifier::AsValue() const {
+ base::Value::Dict wi;
+ wi.Set("value", value());
+ wi.Set("weight", weight());
+ return base::Value(std::move(wi));
+}
+
std::ostream& operator<<(std::ostream& stream, const WeightedIdentifier& ws) {
stream << ws.ToString();
return stream;
@@ -57,6 +51,55 @@ BatchAnnotationResult::BatchAnnotationResult(const BatchAnnotationResult&) =
default;
BatchAnnotationResult::~BatchAnnotationResult() = default;
+bool BatchAnnotationResult::HasOutputForType() const {
+ switch (type()) {
+ case AnnotationType::kUnknown:
+ return false;
+ case AnnotationType::kPageTopics:
+ return !!topics();
+ case AnnotationType::kContentVisibility:
+ return !!visibility_score();
+ case AnnotationType::kPageEntities:
+ return !!entities();
+ }
+}
+
+base::Value BatchAnnotationResult::AsValue() const {
+ base::Value::Dict result;
+ result.Set("input", input());
+ result.Set("type", AnnotationTypeToString(type()));
+
+ if (topics()) {
+ base::Value::List list;
+ for (const auto& wi : *topics()) {
+ list.Append(wi.AsValue());
+ }
+ result.Set("topics", std::move(list));
+ }
+
+ if (entities()) {
+ base::Value::List list;
+ for (const auto& md : *entities()) {
+ list.Append(md.AsValue());
+ }
+ result.Set("entities", std::move(list));
+ }
+
+ if (visibility_score()) {
+ result.Set("visibility_score", *visibility_score());
+ }
+
+ return base::Value(std::move(result));
+}
+
+std::string BatchAnnotationResult::ToJSON() const {
+ std::string json;
+ if (base::JSONWriter::Write(AsValue(), &json)) {
+ return json;
+ }
+ return std::string();
+}
+
std::string BatchAnnotationResult::ToString() const {
std::string output = "nullopt";
if (topics_) {
@@ -76,10 +119,10 @@ std::string BatchAnnotationResult::ToString() const {
}
return base::StringPrintf(
"BatchAnnotationResult{"
- "\"<input with length %zu>\", "
+ "\"%s\", "
"type: %s, "
"output: %s}",
- input_.size(), AnnotationTypeToString(type_).c_str(), output.c_str());
+ input_.c_str(), AnnotationTypeToString(type_).c_str(), output.c_str());
}
std::ostream& operator<<(std::ostream& stream,
diff --git a/chromium/components/optimization_guide/core/page_content_annotations_common.h b/chromium/components/optimization_guide/core/page_content_annotations_common.h
index 0a7f14cb2a2..d53dc3c9ce3 100644
--- a/chromium/components/optimization_guide/core/page_content_annotations_common.h
+++ b/chromium/components/optimization_guide/core/page_content_annotations_common.h
@@ -9,34 +9,13 @@
#include <vector>
#include "base/callback.h"
+#include "base/values.h"
#include "components/optimization_guide/core/entity_metadata.h"
+#include "components/optimization_guide/core/page_content_annotation_type.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace optimization_guide {
-// The type of annotation that is being done on the given input.
-//
-// Each of these is used in UMA histograms so please update the variants there
-// when any changes are made.
-// //tools/metrics/histograms/metadata/optimization/histograms.xml
-enum class AnnotationType {
- kUnknown,
-
- // The input will be annotated with the topics on the page. These topics are
- // fairly high-level like "sports" or "news".
- kPageTopics,
-
- // The input will be annotated for the visibility of the content.
- kContentVisibility,
-
- // The input will be annotated with the entity IDs on the page, for example
- // listing the IDs of all the proper nouns on a page. To map the IDs back to
- // human-readable strings, use `EntityMetadataProvider`.
- kPageEntities,
-};
-
-std::string AnnotationTypeToString(AnnotationType type);
-
// A weighted ID value.
class WeightedIdentifier {
public:
@@ -49,6 +28,8 @@ class WeightedIdentifier {
std::string ToString() const;
+ base::Value AsValue() const;
+
bool operator==(const WeightedIdentifier& other) const;
friend std::ostream& operator<<(std::ostream& stream,
@@ -86,6 +67,9 @@ class BatchAnnotationResult {
BatchAnnotationResult(const BatchAnnotationResult&);
~BatchAnnotationResult();
+ // Returns true if the output corresponding to |type| is not nullopt;
+ bool HasOutputForType() const;
+
const std::string& input() const { return input_; }
AnnotationType type() const { return type_; }
const absl::optional<std::vector<WeightedIdentifier>>& topics() const {
@@ -97,6 +81,9 @@ class BatchAnnotationResult {
absl::optional<double> visibility_score() const { return visibility_score_; }
std::string ToString() const;
+ std::string ToJSON() const;
+
+ base::Value AsValue() const;
bool operator==(const BatchAnnotationResult& other) const;
diff --git a/chromium/components/optimization_guide/core/page_entities_model_executor.cc b/chromium/components/optimization_guide/core/page_entities_model_executor.cc
new file mode 100644
index 00000000000..7b5b978bd80
--- /dev/null
+++ b/chromium/components/optimization_guide/core/page_entities_model_executor.cc
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium 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/optimization_guide/core/page_entities_model_executor.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+
+namespace optimization_guide {
+
+void PageEntitiesModelExecutor::ExecuteOnSingleInput(
+ AnnotationType annotation_type,
+ const std::string& input,
+ base::OnceCallback<void(const BatchAnnotationResult&)> callback) {
+ DCHECK_EQ(annotation_type, AnnotationType::kPageEntities);
+
+ ExecuteModelWithInput(
+ input, base::BindOnce(
+ [](const std::string& input,
+ base::OnceCallback<void(const BatchAnnotationResult&)>
+ pca_callback,
+ const absl::optional<std::vector<ScoredEntityMetadata>>&
+ entity_metadata) {
+ std::move(pca_callback)
+ .Run(BatchAnnotationResult::CreatePageEntitiesResult(
+ input, entity_metadata));
+ },
+ input, std::move(callback)));
+}
+
+} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/page_entities_model_executor.h b/chromium/components/optimization_guide/core/page_entities_model_executor.h
index 5ea55c6e243..2dca8a020f4 100644
--- a/chromium/components/optimization_guide/core/page_entities_model_executor.h
+++ b/chromium/components/optimization_guide/core/page_entities_model_executor.h
@@ -10,29 +10,16 @@
#include "base/callback.h"
#include "components/optimization_guide/core/entity_metadata.h"
+#include "components/optimization_guide/core/model_info.h"
+#include "components/optimization_guide/core/page_content_annotation_job_executor.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/category.h"
namespace optimization_guide {
-// TODO(crbug/1278828): Remove this entirely.
-class HumanReadablePageEntitiesModelExecutor {
- public:
- virtual ~HumanReadablePageEntitiesModelExecutor() = default;
-
- using PageEntitiesModelExecutedCallback = base::OnceCallback<void(
- const absl::optional<std::vector<tflite::task::core::Category>>&)>;
-
- // Annotates |text| with page entities likely represented on the page. Invokes
- // |callback| when done.
- virtual void ExecuteModelWithInput(
- const std::string& text,
- PageEntitiesModelExecutedCallback callback) = 0;
-};
-
// The PageEntitiesModelExecutor is responsible for executing the PAGE_ENTITIES
// model.
-class PageEntitiesModelExecutor {
+class PageEntitiesModelExecutor : public PageContentAnnotationJobExecutor {
public:
virtual ~PageEntitiesModelExecutor() = default;
@@ -42,7 +29,7 @@ class PageEntitiesModelExecutor {
// Annotates |text| with page entities likely represented on the page,
// returning the entity metadata in the reader's locale with associated score.
// Invokes |callback| when done.
- virtual void HumanReadableExecuteModelWithInput(
+ virtual void ExecuteModelWithInput(
const std::string& text,
PageEntitiesMetadataModelExecutedCallback callback) = 0;
@@ -54,6 +41,19 @@ class PageEntitiesModelExecutor {
virtual void GetMetadataForEntityId(
const std::string& entity_id,
PageEntitiesModelEntityMetadataRetrievedCallback callback) = 0;
+
+ // Runs |callback| now if a model is loaded or the next time |OnModelUpdated|
+ // is called.
+ virtual void AddOnModelUpdatedCallback(base::OnceClosure callback) = 0;
+
+ // Returns the ModelInfo for a currently loaded model, if available.
+ virtual absl::optional<ModelInfo> GetModelInfo() const = 0;
+
+ // PageContentAnnotationJobExecutor:
+ void ExecuteOnSingleInput(
+ AnnotationType annotation_type,
+ const std::string& input,
+ base::OnceCallback<void(const BatchAnnotationResult&)> callback) override;
};
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/page_entities_model_executor_impl.cc b/chromium/components/optimization_guide/core/page_entities_model_executor_impl.cc
index bbf144eeec5..7c078358100 100644
--- a/chromium/components/optimization_guide/core/page_entities_model_executor_impl.cc
+++ b/chromium/components/optimization_guide/core/page_entities_model_executor_impl.cc
@@ -4,6 +4,8 @@
#include "components/optimization_guide/core/page_entities_model_executor_impl.h"
+#include <algorithm>
+
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/threading/sequenced_task_runner_handle.h"
@@ -21,6 +23,11 @@ const char kPageEntitiesModelMetadataTypeUrl[] =
"type.googleapis.com/"
"google.internal.chrome.optimizationguide.v1.PageEntitiesModelMetadata";
+// The max number of page entities that should be output.
+// TODO(crbug/1234578): Make the number of entities Finch-able once we
+// know how much the model is expected to output.
+constexpr size_t kMaxPageEntities = 5;
+
PageEntitiesModelExecutorConfig& GetPageEntitiesModelExecutorConfigInternal() {
static base::NoDestructor<PageEntitiesModelExecutorConfig> s_config;
return *s_config;
@@ -152,6 +159,19 @@ void EntityAnnotatorHolder::AnnotateEntitiesMetadataModelOnBackgroundThread(
"ModelThreadExecutionLatency.PageEntities",
annotate_timer.Elapsed());
}
+
+ if (scored_md) {
+ // Determine the entities with the highest weights.
+ std::sort(scored_md->begin(), scored_md->end(),
+ [](const ScoredEntityMetadata& a, const ScoredEntityMetadata& b) {
+ return a.score > b.score;
+ });
+
+ // Limit the output to the top |kMaxPageEntities| items.
+ if (scored_md->size() > kMaxPageEntities) {
+ scored_md->resize(kMaxPageEntities);
+ }
+ }
reply_task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), scored_md));
}
@@ -226,20 +246,42 @@ PageEntitiesModelExecutorImpl::~PageEntitiesModelExecutorImpl() {
std::move(entity_annotator_holder_));
}
+void PageEntitiesModelExecutorImpl::AddOnModelUpdatedCallback(
+ base::OnceClosure callback) {
+ if (model_info_) {
+ std::move(callback).Run();
+ return;
+ }
+
+ // callbacks are not bound locally and are safe to be destroyed at any time.
+ on_model_updated_callbacks_.AddUnsafe(std::move(callback));
+}
+
void PageEntitiesModelExecutorImpl::OnModelUpdated(
proto::OptimizationTarget optimization_target,
const ModelInfo& model_info) {
if (optimization_target != proto::OPTIMIZATION_TARGET_PAGE_ENTITIES)
return;
+ model_info_ = model_info;
+
background_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&EntityAnnotatorHolder::CreateAndSetEntityAnnotatorOnBackgroundThread,
entity_annotator_holder_->GetBackgroundWeakPtr(), model_info));
+
+ // Run any observing callbacks after the model file is posted to the
+ // model executor thread so that any model execution requests are posted to
+ // the model executor thread after the model update.
+ on_model_updated_callbacks_.Notify();
+}
+
+absl::optional<ModelInfo> PageEntitiesModelExecutorImpl::GetModelInfo() const {
+ return model_info_;
}
-void PageEntitiesModelExecutorImpl::HumanReadableExecuteModelWithInput(
+void PageEntitiesModelExecutorImpl::ExecuteModelWithInput(
const std::string& text,
PageEntitiesMetadataModelExecutedCallback callback) {
if (text.empty()) {
diff --git a/chromium/components/optimization_guide/core/page_entities_model_executor_impl.h b/chromium/components/optimization_guide/core/page_entities_model_executor_impl.h
index dbec4c9a223..2ba30713c21 100644
--- a/chromium/components/optimization_guide/core/page_entities_model_executor_impl.h
+++ b/chromium/components/optimization_guide/core/page_entities_model_executor_impl.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_PAGE_ENTITIES_MODEL_EXECUTOR_IMPL_H_
#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_PAGE_ENTITIES_MODEL_EXECUTOR_IMPL_H_
+#include "base/callback_list.h"
+#include "base/memory/raw_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
@@ -90,7 +92,7 @@ class EntityAnnotatorHolder {
std::unique_ptr<EntityAnnotatorNativeLibrary>
entity_annotator_native_library_;
- void* entity_annotator_ = nullptr;
+ raw_ptr<void> entity_annotator_ = nullptr;
base::WeakPtrFactory<EntityAnnotatorHolder> background_weak_ptr_factory_{
this};
@@ -112,9 +114,11 @@ class PageEntitiesModelExecutorImpl : public OptimizationTargetModelObserver,
void GetMetadataForEntityId(
const std::string& entity_id,
PageEntitiesModelEntityMetadataRetrievedCallback callback) override;
- void HumanReadableExecuteModelWithInput(
+ void ExecuteModelWithInput(
const std::string& text,
PageEntitiesMetadataModelExecutedCallback callback) override;
+ void AddOnModelUpdatedCallback(base::OnceClosure callback) override;
+ absl::optional<ModelInfo> GetModelInfo() const override;
// OptimizationTargetModelObserver:
void OnModelUpdated(proto::OptimizationTarget optimization_target,
@@ -132,6 +136,13 @@ class PageEntitiesModelExecutorImpl : public OptimizationTargetModelObserver,
// The holder used to hold the annotator used to annotate entities.
std::unique_ptr<EntityAnnotatorHolder> entity_annotator_holder_;
+ // The most recent model info given to |OnModelUpdated|.
+ absl::optional<ModelInfo> model_info_;
+
+ // Populated with callbacks if |AddOnModelUpdatedCallback| is called before a
+ // model file is available, then is notified when |OnModelUpdated| is called.
+ base::OnceClosureList on_model_updated_callbacks_;
+
base::WeakPtrFactory<PageEntitiesModelExecutorImpl> weak_ptr_factory_{this};
};
diff --git a/chromium/components/optimization_guide/core/page_entities_model_executor_impl_unittest.cc b/chromium/components/optimization_guide/core/page_entities_model_executor_impl_unittest.cc
index 964f7946f53..18575219da9 100644
--- a/chromium/components/optimization_guide/core/page_entities_model_executor_impl_unittest.cc
+++ b/chromium/components/optimization_guide/core/page_entities_model_executor_impl_unittest.cc
@@ -84,12 +84,12 @@ class PageEntitiesModelExecutorImplTest : public testing::Test {
task_environment_.RunUntilIdle();
}
- absl::optional<std::vector<ScoredEntityMetadata>> ExecuteHumanReadableModel(
+ absl::optional<std::vector<ScoredEntityMetadata>> ExecuteModel(
const std::string& text) {
absl::optional<std::vector<ScoredEntityMetadata>> entity_metadata;
base::RunLoop run_loop;
- model_executor_->HumanReadableExecuteModelWithInput(
+ model_executor_->ExecuteModelWithInput(
text, base::BindOnce(
[](base::RunLoop* run_loop,
absl::optional<std::vector<ScoredEntityMetadata>>*
@@ -102,14 +102,6 @@ class PageEntitiesModelExecutorImplTest : public testing::Test {
&run_loop, &entity_metadata));
run_loop.Run();
- // Sort the result by score to make validating the output easier.
- if (entity_metadata) {
- std::sort(
- entity_metadata->begin(), entity_metadata->end(),
- [](const ScoredEntityMetadata& a, const ScoredEntityMetadata& b) {
- return a.score > b.score;
- });
- }
return entity_metadata;
}
@@ -133,6 +125,8 @@ class PageEntitiesModelExecutorImplTest : public testing::Test {
return entity_metadata;
}
+ PageEntitiesModelExecutor* model_executor() { return model_executor_.get(); }
+
ModelObserverTracker* model_observer_tracker() const {
return model_observer_tracker_.get();
}
@@ -166,7 +160,7 @@ TEST_F(PageEntitiesModelExecutorImplTest, CreateNoMetadata) {
// We expect that there will be no model to evaluate even for this input that
// has output in the test model.
- EXPECT_EQ(ExecuteHumanReadableModel("Taylor Swift singer"), absl::nullopt);
+ EXPECT_EQ(ExecuteModel("Taylor Swift singer"), absl::nullopt);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully", false,
@@ -184,18 +178,18 @@ TEST_F(PageEntitiesModelExecutorImplTest, CreateMetadataWrongType) {
proto::FieldTrial garbage;
garbage.SerializeToString(any.mutable_value());
- proto::PredictionModel model;
- model.mutable_model()->set_download_url(
- FilePathToString(GetModelTestDataDir().AppendASCII("model.tflite")));
- model.mutable_model_info()->set_version(123);
- *model.mutable_model_info()->mutable_model_metadata() = any;
- std::unique_ptr<ModelInfo> model_info = ModelInfo::Create(model);
+ std::unique_ptr<ModelInfo> model_info =
+ TestModelInfoBuilder()
+ .SetModelFilePath(GetModelTestDataDir().AppendASCII("model.tflite"))
+ .SetVersion(123)
+ .SetModelMetadata(any)
+ .Build();
ASSERT_TRUE(model_info);
PushModelInfoToObservers(*model_info);
// We expect that there will be no model to evaluate even for this input that
// has output in the test model.
- EXPECT_EQ(ExecuteHumanReadableModel("Taylor Swift singer"), absl::nullopt);
+ EXPECT_EQ(ExecuteModel("Taylor Swift singer"), absl::nullopt);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully", false,
@@ -213,18 +207,18 @@ TEST_F(PageEntitiesModelExecutorImplTest, CreateNoSlices) {
any.set_type_url(metadata.GetTypeName());
metadata.SerializeToString(any.mutable_value());
- proto::PredictionModel model;
- model.mutable_model()->set_download_url(
- FilePathToString(GetModelTestDataDir().AppendASCII("model.tflite")));
- model.mutable_model_info()->set_version(123);
- *model.mutable_model_info()->mutable_model_metadata() = any;
- std::unique_ptr<ModelInfo> model_info = ModelInfo::Create(model);
+ std::unique_ptr<ModelInfo> model_info =
+ TestModelInfoBuilder()
+ .SetModelFilePath(GetModelTestDataDir().AppendASCII("model.tflite"))
+ .SetVersion(123)
+ .SetModelMetadata(any)
+ .Build();
ASSERT_TRUE(model_info);
PushModelInfoToObservers(*model_info);
// We expect that there will be no model to evaluate even for this input that
// has output in the test model.
- EXPECT_EQ(ExecuteHumanReadableModel("Taylor Swift singer"), absl::nullopt);
+ EXPECT_EQ(ExecuteModel("Taylor Swift singer"), absl::nullopt);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully", false,
@@ -236,6 +230,35 @@ TEST_F(PageEntitiesModelExecutorImplTest, CreateNoSlices) {
1);
}
+TEST_F(PageEntitiesModelExecutorImplTest, ModelInfoUpdated) {
+ bool callback_run = false;
+ model_executor()->AddOnModelUpdatedCallback(
+ base::BindOnce([](bool* flag) { *flag = true; }, &callback_run));
+
+ EXPECT_FALSE(callback_run);
+ EXPECT_FALSE(model_executor()->GetModelInfo());
+
+ std::unique_ptr<ModelInfo> model_info =
+ TestModelInfoBuilder()
+ .SetModelFilePath(GetModelTestDataDir().AppendASCII("test.tflite"))
+ .SetVersion(1337)
+ .Build();
+ PushModelInfoToObservers(*model_info);
+ EXPECT_TRUE(callback_run);
+
+ ASSERT_TRUE(model_executor()->GetModelInfo());
+ EXPECT_EQ(model_executor()->GetModelInfo()->GetModelFilePath(),
+ model_info->GetModelFilePath());
+ EXPECT_EQ(model_executor()->GetModelInfo()->GetVersion(),
+ model_info->GetVersion());
+
+ bool immediate_callback_run = false;
+ model_executor()->AddOnModelUpdatedCallback(base::BindOnce(
+ [](bool* flag) { *flag = true; }, &immediate_callback_run));
+ // This callback should be run immediately because the model is loaded.
+ EXPECT_TRUE(immediate_callback_run);
+}
+
TEST_F(PageEntitiesModelExecutorImplTest, CreateMissingFiles) {
proto::Any any;
proto::PageEntitiesModelMetadata metadata;
@@ -268,26 +291,25 @@ TEST_F(PageEntitiesModelExecutorImplTest, CreateMissingFiles) {
for (const auto& missing_file_and_status : expected_additional_files) {
base::HistogramTester histogram_tester;
- proto::PredictionModel model;
- model.mutable_model()->set_download_url(
- FilePathToString(dir_path.AppendASCII("model.tflite")));
- model.mutable_model_info()->set_version(123);
- *model.mutable_model_info()->mutable_model_metadata() = any;
+ base::flat_set<base::FilePath> additional_files;
for (const auto& additional_file : expected_additional_files) {
- if (additional_file.first == missing_file_and_status.first) {
- // Don't add the file if it's supposed to be missing.
- continue;
+ if (additional_file.first != missing_file_and_status.first) {
+ additional_files.insert(*StringToFilePath(additional_file.first));
}
- model.mutable_model_info()->add_additional_files()->set_file_path(
- additional_file.first);
}
- std::unique_ptr<ModelInfo> model_info = ModelInfo::Create(model);
+ std::unique_ptr<ModelInfo> model_info =
+ TestModelInfoBuilder()
+ .SetModelFilePath(GetModelTestDataDir().AppendASCII("model.tflite"))
+ .SetVersion(123)
+ .SetModelMetadata(any)
+ .SetAdditionalFiles(additional_files)
+ .Build();
ASSERT_TRUE(model_info);
PushModelInfoToObservers(*model_info);
// We expect that there will be no model to evaluate even for this input
// that has output in the test model.
- EXPECT_EQ(ExecuteHumanReadableModel("Taylor Swift singer"), absl::nullopt);
+ EXPECT_EQ(ExecuteModel("Taylor Swift singer"), absl::nullopt);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageEntitiesModelExecutor.CreatedSuccessfully",
@@ -303,8 +325,8 @@ TEST_F(PageEntitiesModelExecutorImplTest, GetMetadataForEntityIdNoModel) {
EXPECT_EQ(GetMetadataForEntityId("/m/0dl567"), absl::nullopt);
}
-TEST_F(PageEntitiesModelExecutorImplTest, ExecuteHumanReadableModelNoModel) {
- EXPECT_EQ(ExecuteHumanReadableModel("Taylor Swift singer"), absl::nullopt);
+TEST_F(PageEntitiesModelExecutorImplTest, ExecuteModelNoModel) {
+ EXPECT_EQ(ExecuteModel("Taylor Swift singer"), absl::nullopt);
}
TEST_F(PageEntitiesModelExecutorImplTest,
diff --git a/chromium/components/optimization_guide/core/page_topics_model_executor.cc b/chromium/components/optimization_guide/core/page_topics_model_executor.cc
index 387c62bca6e..ebba65cd6cb 100644
--- a/chromium/components/optimization_guide/core/page_topics_model_executor.cc
+++ b/chromium/components/optimization_guide/core/page_topics_model_executor.cc
@@ -5,8 +5,10 @@
#include "components/optimization_guide/core/page_topics_model_executor.h"
#include "base/barrier_closure.h"
+#include "base/containers/contains.h"
#include "base/files/file_util.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
#include "components/optimization_guide/core/optimization_guide_model_provider.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "components/optimization_guide/proto/page_topics_model_metadata.pb.h"
@@ -125,24 +127,46 @@ void PageTopicsModelExecutor::ExecuteJob(
std::move(on_job_complete_callback), std::move(job));
}
+// static
+std::string PageTopicsModelExecutor::PreprocessHost(const std::string& host) {
+ std::string output = base::ToLowerASCII(host);
+
+ // Strip the 'www.' if it exists.
+ if (base::StartsWith(output, "www.")) {
+ output = output.substr(4);
+ }
+
+ static const char kCharsToReplaceWithSpace[] = {'-', '_', '.', '+'};
+ for (char c : kCharsToReplaceWithSpace) {
+ std::replace(output.begin(), output.end(), c, ' ');
+ }
+
+ return output;
+}
+
void PageTopicsModelExecutor::ExecuteOnSingleInput(
AnnotationType annotation_type,
- const std::string& input,
+ const std::string& raw_input,
base::OnceCallback<void(const BatchAnnotationResult&)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(annotation_type, AnnotationType::kPageTopics);
+ // |processed_input| is needed by the override list and the model, but we pass
+ // the |raw_input| to where the BatchAnnotationResult is created so that the
+ // original input is passed back to the caller.
+ std::string processed_input = PreprocessHost(raw_input);
+
if (override_list_) {
DCHECK(override_list_file_path_);
- auto iter = override_list_->find(input);
+ auto iter = override_list_->find(processed_input);
base::UmaHistogramBoolean(
"OptimizationGuide.PageTopicsOverrideList.UsedOverride",
iter != override_list_->end());
if (iter != override_list_->end()) {
- std::move(callback).Run(
- BatchAnnotationResult::CreatePageTopicsResult(input, iter->second));
+ std::move(callback).Run(BatchAnnotationResult::CreatePageTopicsResult(
+ raw_input, iter->second));
return;
}
}
@@ -151,8 +175,8 @@ void PageTopicsModelExecutor::ExecuteOnSingleInput(
base::BindOnce(&PageTopicsModelExecutor::
PostprocessCategoriesToBatchAnnotationResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
- annotation_type, input),
- input);
+ annotation_type, raw_input),
+ processed_input);
}
void PageTopicsModelExecutor::OnOverrideListLoadAttemptDone(
@@ -177,7 +201,7 @@ void PageTopicsModelExecutor::OnOverrideListLoadAttemptDone(
void PageTopicsModelExecutor::PostprocessCategoriesToBatchAnnotationResult(
base::OnceCallback<void(const BatchAnnotationResult&)> callback,
AnnotationType annotation_type,
- const std::string& input,
+ const std::string& raw_input,
const absl::optional<std::vector<tflite::task::core::Category>>& output) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(annotation_type, AnnotationType::kPageTopics);
@@ -187,7 +211,7 @@ void PageTopicsModelExecutor::PostprocessCategoriesToBatchAnnotationResult(
categories = ExtractCategoriesFromModelOutput(*output);
}
std::move(callback).Run(
- BatchAnnotationResult::CreatePageTopicsResult(input, categories));
+ BatchAnnotationResult::CreatePageTopicsResult(raw_input, categories));
}
absl::optional<std::vector<WeightedIdentifier>>
diff --git a/chromium/components/optimization_guide/core/page_topics_model_executor.h b/chromium/components/optimization_guide/core/page_topics_model_executor.h
index 4cd9ea06655..fc14486a258 100644
--- a/chromium/components/optimization_guide/core/page_topics_model_executor.h
+++ b/chromium/components/optimization_guide/core/page_topics_model_executor.h
@@ -38,7 +38,7 @@ class PageTopicsModelExecutor : public PageContentAnnotationJobExecutor,
std::unique_ptr<PageContentAnnotationJob> job) override;
void ExecuteOnSingleInput(
AnnotationType annotation_type,
- const std::string& input,
+ const std::string& raw_input,
base::OnceCallback<void(const BatchAnnotationResult&)> callback) override;
// BertModelHandler:
@@ -52,7 +52,7 @@ class PageTopicsModelExecutor : public PageContentAnnotationJobExecutor,
void PostprocessCategoriesToBatchAnnotationResult(
base::OnceCallback<void(const BatchAnnotationResult&)> callback,
AnnotationType annotation_type,
- const std::string& input,
+ const std::string& raw_input,
const absl::optional<std::vector<tflite::task::core::Category>>& output);
// Extracts the scored categories from the output of the model.
@@ -69,6 +69,9 @@ class PageTopicsModelExecutor : public PageContentAnnotationJobExecutor,
std::unordered_map<std::string, std::vector<WeightedIdentifier>>>
override_list);
+ // Does the required preprocessing on a input domain.
+ static std::string PreprocessHost(const std::string& host);
+
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
// Set whenever a valid override list file is passed along with the model file
@@ -79,7 +82,8 @@ class PageTopicsModelExecutor : public PageContentAnnotationJobExecutor,
// Set whenever an override list file is available and the model file is
// loaded into memory. Reset whenever the model file is unloaded.
- // Used on the UI thread.
+ // Used on the UI thread. Lookups in this mapping should have |PreprocessHost|
+ // applied first.
absl::optional<
std::unordered_map<std::string, std::vector<WeightedIdentifier>>>
override_list_;
diff --git a/chromium/components/optimization_guide/core/page_topics_model_executor_unittest.cc b/chromium/components/optimization_guide/core/page_topics_model_executor_unittest.cc
index 92274a2d72b..cbfa83d28f0 100644
--- a/chromium/components/optimization_guide/core/page_topics_model_executor_unittest.cc
+++ b/chromium/components/optimization_guide/core/page_topics_model_executor_unittest.cc
@@ -8,6 +8,7 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
+#include "base/task/thread_pool.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
@@ -49,19 +50,43 @@ class ModelObserverTracker : public TestOptimizationGuideModelProvider {
registered_model_metadata_;
};
+class TestPageTopicsModelExecutor : public PageTopicsModelExecutor {
+ public:
+ TestPageTopicsModelExecutor(
+ OptimizationGuideModelProvider* model_provider,
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+ const absl::optional<proto::Any>& model_metadata)
+ : PageTopicsModelExecutor(model_provider,
+ background_task_runner,
+ model_metadata) {}
+ ~TestPageTopicsModelExecutor() override = default;
+
+ void ExecuteModelWithInput(ExecutionCallback callback,
+ const std::string& input) override {
+ inputs_.push_back(input);
+ std::move(callback).Run(absl::nullopt);
+ }
+
+ const std::vector<std::string>& inputs() const { return inputs_; }
+
+ private:
+ std::vector<std::string> inputs_;
+};
+
class PageTopicsModelExecutorTest : public testing::Test {
public:
PageTopicsModelExecutorTest() {
- scoped_feature_list_.InitAndEnableFeature(
- features::kPageContentAnnotations);
+ scoped_feature_list_.InitWithFeatures(
+ {features::kPageContentAnnotations},
+ {features::kPreventLongRunningPredictionModels});
}
~PageTopicsModelExecutorTest() override = default;
void SetUp() override {
model_observer_tracker_ = std::make_unique<ModelObserverTracker>();
- model_executor_ = std::make_unique<PageTopicsModelExecutor>(
+ model_executor_ = std::make_unique<TestPageTopicsModelExecutor>(
model_observer_tracker_.get(),
- task_environment_.GetMainThreadTaskRunner(),
+ base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}),
/*model_metadata=*/absl::nullopt);
}
@@ -95,7 +120,7 @@ class PageTopicsModelExecutorTest : public testing::Test {
return model_observer_tracker_.get();
}
- PageTopicsModelExecutor* model_executor() const {
+ TestPageTopicsModelExecutor* model_executor() const {
return model_executor_.get();
}
@@ -105,7 +130,7 @@ class PageTopicsModelExecutorTest : public testing::Test {
base::test::TaskEnvironment task_environment_;
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<ModelObserverTracker> model_observer_tracker_;
- std::unique_ptr<PageTopicsModelExecutor> model_executor_;
+ std::unique_ptr<TestPageTopicsModelExecutor> model_executor_;
};
TEST_F(
@@ -335,6 +360,38 @@ TEST_F(PageTopicsModelExecutorTest,
BatchAnnotationResult::CreatePageTopicsResult("", absl::nullopt));
}
+TEST_F(PageTopicsModelExecutorTest, HostPreprocessing) {
+ std::vector<std::pair<std::string, std::string>> tests = {
+ {"www.chromium.org", "chromium org"},
+ {"foo-bar.com", "foo bar com"},
+ {"foo_bar.com", "foo bar com"},
+ {"cats.co.uk", "cats co uk"},
+ {"cats+dogs.com", "cats dogs com"},
+ {"www.foo-bar_.baz.com", "foo bar baz com"},
+ {"www.foo-bar-baz.com", "foo bar baz com"},
+ {"WwW.LOWER-CASE.com", "lower case com"},
+ };
+
+ for (const auto& test : tests) {
+ std::string raw_host = test.first;
+ std::string processed_host = test.second;
+
+ std::string got_input;
+ // The callback is run synchronously in this test.
+ model_executor()->ExecuteOnSingleInput(
+ AnnotationType::kPageTopics, raw_host,
+ base::BindOnce(
+ [](std::string* got_input_out,
+ const BatchAnnotationResult& result) {
+ EXPECT_EQ(result.type(), AnnotationType::kPageTopics);
+ *got_input_out = result.input();
+ },
+ &got_input));
+ EXPECT_EQ(raw_host, got_input);
+ EXPECT_EQ(processed_host, model_executor()->inputs().back());
+ }
+}
+
class PageTopicsModelExecutorOverrideListTest
: public PageTopicsModelExecutorTest {
public:
@@ -492,7 +549,7 @@ TEST_F(PageTopicsModelExecutorOverrideListTest, SuccessCase) {
proto::PageTopicsOverrideList override_list;
proto::PageTopicsOverrideEntry* entry = override_list.add_entries();
- entry->set_domain("input");
+ entry->set_domain("input com");
entry->mutable_topics()->add_topic_ids(1337);
std::string enc_pb;
@@ -508,14 +565,15 @@ TEST_F(PageTopicsModelExecutorOverrideListTest, SuccessCase) {
std::make_unique<PageContentAnnotationJob>(
base::BindOnce([](const std::vector<BatchAnnotationResult>& results) {
ASSERT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0].input(), "input");
+ EXPECT_EQ(results[0].input(), "www.input.com");
EXPECT_EQ(results[0].type(), AnnotationType::kPageTopics);
ASSERT_TRUE(results[0].topics());
EXPECT_EQ(*results[0].topics(), (std::vector<WeightedIdentifier>{
WeightedIdentifier(1337, 1.0),
}));
}),
- std::vector<std::string>{"input"}, AnnotationType::kPageTopics));
+ std::vector<std::string>{"www.input.com"},
+ AnnotationType::kPageTopics));
run_loop.Run();
histogram_tester.ExpectUniqueSample(
diff --git a/chromium/components/optimization_guide/core/page_visibility_model_executor_unittest.cc b/chromium/components/optimization_guide/core/page_visibility_model_executor_unittest.cc
index 8bede16fa61..bf6f5703bfd 100644
--- a/chromium/components/optimization_guide/core/page_visibility_model_executor_unittest.cc
+++ b/chromium/components/optimization_guide/core/page_visibility_model_executor_unittest.cc
@@ -6,6 +6,7 @@
#include "base/containers/flat_map.h"
#include "base/path_service.h"
+#include "base/task/thread_pool.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
@@ -56,7 +57,7 @@ class PageVisibilityModelExecutorTest : public testing::Test {
model_observer_tracker_ = std::make_unique<ModelObserverTracker>();
model_executor_ = std::make_unique<PageVisibilityModelExecutor>(
model_observer_tracker_.get(),
- task_environment_.GetMainThreadTaskRunner(),
+ base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}),
/*model_metadata=*/absl::nullopt);
}
diff --git a/chromium/components/optimization_guide/core/prediction_manager.cc b/chromium/components/optimization_guide/core/prediction_manager.cc
index 8c50fc10ccf..817933c47fe 100644
--- a/chromium/components/optimization_guide/core/prediction_manager.cc
+++ b/chromium/components/optimization_guide/core/prediction_manager.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <utility>
+#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/containers/flat_tree.h"
@@ -36,19 +37,22 @@
#include "components/optimization_guide/core/optimization_target_model_observer.h"
#include "components/optimization_guide/core/prediction_model_download_manager.h"
#include "components/optimization_guide/core/prediction_model_fetcher_impl.h"
+#include "components/optimization_guide/core/prediction_model_override.h"
#include "components/optimization_guide/core/store_update_data.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "components/prefs/pref_service.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
+namespace optimization_guide {
+
namespace {
// Provide a random time delta in seconds before fetching models.
base::TimeDelta RandomFetchDelay() {
- return base::Seconds(base::RandInt(
- optimization_guide::features::PredictionModelFetchRandomMinDelaySecs(),
- optimization_guide::features::PredictionModelFetchRandomMaxDelaySecs()));
+ return base::Seconds(
+ base::RandInt(features::PredictionModelFetchRandomMinDelaySecs(),
+ features::PredictionModelFetchRandomMaxDelaySecs()));
}
// Util class for recording the state of a prediction model. The result is
@@ -56,31 +60,26 @@ base::TimeDelta RandomFetchDelay() {
class ScopedPredictionManagerModelStatusRecorder {
public:
explicit ScopedPredictionManagerModelStatusRecorder(
- optimization_guide::proto::OptimizationTarget optimization_target)
- : status_(optimization_guide::PredictionManagerModelStatus::kUnknown),
- optimization_target_(optimization_target) {}
+ proto::OptimizationTarget optimization_target)
+ : optimization_target_(optimization_target) {}
~ScopedPredictionManagerModelStatusRecorder() {
- DCHECK_NE(status_,
- optimization_guide::PredictionManagerModelStatus::kUnknown);
+ DCHECK_NE(status_, PredictionManagerModelStatus::kUnknown);
base::UmaHistogramEnumeration(
"OptimizationGuide.ShouldTargetNavigation.PredictionModelStatus",
status_);
base::UmaHistogramEnumeration(
"OptimizationGuide.ShouldTargetNavigation.PredictionModelStatus." +
- optimization_guide::GetStringNameForOptimizationTarget(
- optimization_target_),
+ GetStringNameForOptimizationTarget(optimization_target_),
status_);
}
- void set_status(optimization_guide::PredictionManagerModelStatus status) {
- status_ = status;
- }
+ void set_status(PredictionManagerModelStatus status) { status_ = status; }
private:
- optimization_guide::PredictionManagerModelStatus status_;
- const optimization_guide::proto::OptimizationTarget optimization_target_;
+ PredictionManagerModelStatus status_ = PredictionManagerModelStatus::kUnknown;
+ const proto::OptimizationTarget optimization_target_;
};
// Util class for recording the construction and validation of a prediction
@@ -89,7 +88,7 @@ class ScopedPredictionManagerModelStatusRecorder {
class ScopedPredictionModelConstructionAndValidationRecorder {
public:
explicit ScopedPredictionModelConstructionAndValidationRecorder(
- optimization_guide::proto::OptimizationTarget optimization_target)
+ proto::OptimizationTarget optimization_target)
: validation_start_time_(base::TimeTicks::Now()),
optimization_target_(optimization_target) {}
@@ -98,8 +97,7 @@ class ScopedPredictionModelConstructionAndValidationRecorder {
is_valid_);
base::UmaHistogramBoolean(
"OptimizationGuide.IsPredictionModelValid." +
- optimization_guide::GetStringNameForOptimizationTarget(
- optimization_target_),
+ GetStringNameForOptimizationTarget(optimization_target_),
is_valid_);
// Only record the timing if the model is valid and was able to be
@@ -112,8 +110,7 @@ class ScopedPredictionModelConstructionAndValidationRecorder {
validation_latency);
base::UmaHistogramTimes(
"OptimizationGuide.PredictionModelValidationLatency." +
- optimization_guide::GetStringNameForOptimizationTarget(
- optimization_target_),
+ GetStringNameForOptimizationTarget(optimization_target_),
validation_latency);
}
}
@@ -123,64 +120,33 @@ class ScopedPredictionModelConstructionAndValidationRecorder {
private:
bool is_valid_ = true;
const base::TimeTicks validation_start_time_;
- const optimization_guide::proto::OptimizationTarget optimization_target_;
+ const proto::OptimizationTarget optimization_target_;
};
-void RecordModelUpdateVersion(
- const optimization_guide::proto::ModelInfo& model_info) {
+void RecordModelUpdateVersion(const proto::ModelInfo& model_info) {
base::UmaHistogramSparse(
"OptimizationGuide.PredictionModelUpdateVersion." +
- optimization_guide::GetStringNameForOptimizationTarget(
- model_info.optimization_target()),
+ GetStringNameForOptimizationTarget(model_info.optimization_target()),
model_info.version());
}
-void RecordLifecycleState(
- optimization_guide::proto::OptimizationTarget optimization_target,
- optimization_guide::ModelDeliveryEvent event) {
+void RecordLifecycleState(proto::OptimizationTarget optimization_target,
+ ModelDeliveryEvent event) {
base::UmaHistogramEnumeration(
"OptimizationGuide.PredictionManager.ModelDeliveryEvents." +
- optimization_guide::GetStringNameForOptimizationTarget(
- optimization_target),
+ GetStringNameForOptimizationTarget(optimization_target),
event);
}
// Returns whether models should be fetched from the
// remote Optimization Guide Service.
-bool ShouldFetchModels(bool off_the_record, PrefService* pref_service) {
- return optimization_guide::features::IsRemoteFetchingEnabled(pref_service) &&
- !off_the_record &&
- optimization_guide::features::IsModelDownloadingEnabled();
-}
-
-std::unique_ptr<optimization_guide::proto::PredictionModel>
-BuildPredictionModelFromCommandLineForOptimizationTarget(
- optimization_guide::proto::OptimizationTarget optimization_target) {
- absl::optional<
- std::pair<std::string, absl::optional<optimization_guide::proto::Any>>>
- model_file_path_and_metadata =
- optimization_guide::GetModelOverrideForOptimizationTarget(
- optimization_target);
- if (!model_file_path_and_metadata)
- return nullptr;
-
- std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
- std::make_unique<optimization_guide::proto::PredictionModel>();
- prediction_model->mutable_model_info()->set_optimization_target(
- optimization_target);
- prediction_model->mutable_model_info()->set_version(123);
- if (model_file_path_and_metadata->second) {
- *prediction_model->mutable_model_info()->mutable_model_metadata() =
- model_file_path_and_metadata->second.value();
- }
- prediction_model->mutable_model()->set_download_url(
- model_file_path_and_metadata->first);
- return prediction_model;
+bool ShouldFetchModels(bool off_the_record, bool component_updates_enabled) {
+ return features::IsRemoteFetchingEnabled() && !off_the_record &&
+ features::IsModelDownloadingEnabled() && component_updates_enabled;
}
// Returns whether the model metadata proto is on the server allowlist.
-bool IsModelMetadataTypeOnServerAllowlist(
- const optimization_guide::proto::Any& model_metadata) {
+bool IsModelMetadataTypeOnServerAllowlist(const proto::Any& model_metadata) {
return model_metadata.type_url() ==
"type.googleapis.com/"
"google.internal.chrome.optimizationguide.v1."
@@ -200,19 +166,16 @@ bool IsModelMetadataTypeOnServerAllowlist(
}
void RecordModelAvailableAtRegistration(
- optimization_guide::proto::OptimizationTarget optimization_target,
+ proto::OptimizationTarget optimization_target,
bool model_available_at_registration) {
base::UmaHistogramBoolean(
"OptimizationGuide.PredictionManager.ModelAvailableAtRegistration." +
- optimization_guide::GetStringNameForOptimizationTarget(
- optimization_target),
+ GetStringNameForOptimizationTarget(optimization_target),
model_available_at_registration);
}
} // namespace
-namespace optimization_guide {
-
PredictionManager::PredictionManager(
base::WeakPtr<OptimizationGuideStore> model_and_features_store,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
@@ -221,12 +184,14 @@ PredictionManager::PredictionManager(
const std::string& application_locale,
const base::FilePath& models_dir_path,
OptimizationGuideLogger* optimization_guide_logger,
- BackgroundDownloadServiceProvider background_download_service_provider)
+ BackgroundDownloadServiceProvider background_download_service_provider,
+ ComponentUpdatesEnabledProvider component_updates_enabled_provider)
: prediction_model_download_manager_(nullptr),
model_and_features_store_(model_and_features_store),
url_loader_factory_(url_loader_factory),
optimization_guide_logger_(optimization_guide_logger),
pref_service_(pref_service),
+ component_updates_enabled_provider_(component_updates_enabled_provider),
clock_(base::DefaultClock::GetInstance()),
off_the_record_(off_the_record),
application_locale_(application_locale),
@@ -692,20 +657,27 @@ void PredictionManager::OnStoreInitialized(
MaybeScheduleFirstModelFetch();
}
+void PredictionManager::OnPredictionModelOverrideLoaded(
+ proto::OptimizationTarget optimization_target,
+ std::unique_ptr<proto::PredictionModel> prediction_model) {
+ OnLoadPredictionModel(optimization_target,
+ /*record_availability_metrics=*/false,
+ std::move(prediction_model));
+ RecordModelAvailableAtRegistration(optimization_target,
+ prediction_model != nullptr);
+}
+
void PredictionManager::LoadPredictionModels(
const base::flat_set<proto::OptimizationTarget>& optimization_targets) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (switches::IsModelOverridePresent()) {
for (proto::OptimizationTarget optimization_target : optimization_targets) {
- std::unique_ptr<proto::PredictionModel> prediction_model =
- BuildPredictionModelFromCommandLineForOptimizationTarget(
- optimization_target);
- OnLoadPredictionModel(optimization_target,
- /*record_availability_metrics=*/false,
- std::move(prediction_model));
- RecordModelAvailableAtRegistration(optimization_target,
- prediction_model != nullptr);
+ BuildPredictionModelFromCommandLineForOptimizationTarget(
+ optimization_target,
+ base::BindOnce(&PredictionManager::OnPredictionModelOverrideLoaded,
+ ui_weak_ptr_factory_.GetWeakPtr(),
+ optimization_target));
}
return;
}
@@ -757,11 +729,10 @@ void PredictionManager::OnProcessLoadedModel(
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (success) {
- base::UmaHistogramSparse(
- "OptimizationGuide.PredictionModelLoadedVersion." +
- optimization_guide::GetStringNameForOptimizationTarget(
- model.model_info().optimization_target()),
- model.model_info().version());
+ base::UmaHistogramSparse("OptimizationGuide.PredictionModelLoadedVersion." +
+ GetStringNameForOptimizationTarget(
+ model.model_info().optimization_target()),
+ model.model_info().version());
return;
}
@@ -770,11 +741,10 @@ void PredictionManager::OnProcessLoadedModel(
if (model_and_features_store_ &&
model_and_features_store_->FindPredictionModelEntryKey(
model.model_info().optimization_target(), &model_entry_key)) {
- LOCAL_HISTOGRAM_BOOLEAN(
- "OptimizationGuide.PredictionModelRemoved." +
- optimization_guide::GetStringNameForOptimizationTarget(
- model.model_info().optimization_target()),
- true);
+ LOCAL_HISTOGRAM_BOOLEAN("OptimizationGuide.PredictionModelRemoved." +
+ GetStringNameForOptimizationTarget(
+ model.model_info().optimization_target()),
+ true);
model_and_features_store_->RemovePredictionModelFromEntryKey(
model_entry_key);
}
@@ -849,7 +819,8 @@ void PredictionManager::StoreLoadedModelInfo(
}
void PredictionManager::MaybeScheduleFirstModelFetch() {
- if (!ShouldFetchModels(off_the_record_, pref_service_))
+ if (!ShouldFetchModels(off_the_record_,
+ component_updates_enabled_provider_.Run()))
return;
// Add a slight delay to allow the rest of the browser startup process to
diff --git a/chromium/components/optimization_guide/core/prediction_manager.h b/chromium/components/optimization_guide/core/prediction_manager.h
index a13c323eef2..ea909f36b44 100644
--- a/chromium/components/optimization_guide/core/prediction_manager.h
+++ b/chromium/components/optimization_guide/core/prediction_manager.h
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
+#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/containers/lru_cache.h"
@@ -53,8 +54,11 @@ class PredictionManager : public PredictionModelDownloadObserver {
// BackgroundDownloadService is only available once the profile is fully
// initialized and that cannot be done as part of |Initialize|. Get a provider
// to retrieve the service when it is needed.
- typedef base::OnceCallback<download::BackgroundDownloadService*(void)>
- BackgroundDownloadServiceProvider;
+ using BackgroundDownloadServiceProvider =
+ base::OnceCallback<download::BackgroundDownloadService*(void)>;
+
+ // Callback to whether component updates are enabled for the browser.
+ using ComponentUpdatesEnabledProvider = base::RepeatingCallback<bool(void)>;
PredictionManager(
base::WeakPtr<OptimizationGuideStore> model_and_features_store,
@@ -64,7 +68,8 @@ class PredictionManager : public PredictionModelDownloadObserver {
const std::string& application_locale,
const base::FilePath& models_dir_path,
OptimizationGuideLogger* optimization_guide_logger,
- BackgroundDownloadServiceProvider background_dowload_service_provider);
+ BackgroundDownloadServiceProvider background_download_service_provider,
+ ComponentUpdatesEnabledProvider component_updates_enabled_provider);
PredictionManager(const PredictionManager&) = delete;
PredictionManager& operator=(const PredictionManager&) = delete;
@@ -187,6 +192,12 @@ class PredictionManager : public PredictionModelDownloadObserver {
bool record_availability_metrics,
std::unique_ptr<proto::PredictionModel> prediction_model);
+ // Callback run after a prediction model is loaded from a command-line
+ // override.
+ void OnPredictionModelOverrideLoaded(
+ proto::OptimizationTarget optimization_target,
+ std::unique_ptr<proto::PredictionModel> prediction_model);
+
// Process loaded |model| into memory. Return true if a prediction
// model object was created and successfully stored, otherwise false.
bool ProcessAndStoreLoadedModel(const proto::PredictionModel& model);
@@ -280,6 +291,10 @@ class PredictionManager : public PredictionModelDownloadObserver {
// A reference to the PrefService for this profile. Not owned.
raw_ptr<PrefService> pref_service_ = nullptr;
+ // The repeating callback that will be used to determine if component updates
+ // are enabled.
+ ComponentUpdatesEnabledProvider component_updates_enabled_provider_;
+
// Time the prediction manager got initialized.
base::TimeTicks init_time_;
diff --git a/chromium/components/optimization_guide/core/prediction_manager_unittest.cc b/chromium/components/optimization_guide/core/prediction_manager_unittest.cc
index 7e05fbe1c64..d4ad3d7ff0d 100644
--- a/chromium/components/optimization_guide/core/prediction_manager_unittest.cc
+++ b/chromium/components/optimization_guide/core/prediction_manager_unittest.cc
@@ -10,6 +10,7 @@
#include <utility>
#include "base/base64.h"
+#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/test/gtest_util.h"
@@ -348,6 +349,7 @@ class TestPredictionManager : public PredictionManager {
base::WeakPtr<OptimizationGuideStore> model_and_features_store,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* pref_service,
+ ComponentUpdatesEnabledProvider component_updates_enabled_provider,
bool off_the_record,
const std::string& application_locale,
const base::FilePath& models_dir_path)
@@ -360,7 +362,8 @@ class TestPredictionManager : public PredictionManager {
models_dir_path,
&optimization_guide_logger_,
/*background_download_service_provider=*/
- base::OnceCallback<download::BackgroundDownloadService*()>()) {}
+ base::OnceCallback<download::BackgroundDownloadService*()>(),
+ component_updates_enabled_provider) {}
~TestPredictionManager() override = default;
@@ -402,7 +405,11 @@ class PredictionManagerTestBase : public ProtoDatabaseProviderTestBase {
model_and_features_store_ = CreateModelAndHostModelFeaturesStore();
prediction_manager_ = std::make_unique<TestPredictionManager>(
model_and_features_store_->AsWeakPtr(), url_loader_factory_,
- pref_service_.get(), false, "en-US", temp_dir());
+ pref_service_.get(),
+ base::BindRepeating(
+ &PredictionManagerTestBase::AreComponentUpdatesEnabled,
+ base::Unretained(this)),
+ false, "en-US", temp_dir());
prediction_manager_->SetClockForTesting(task_environment_.GetMockClock());
}
@@ -434,8 +441,9 @@ class PredictionManagerTestBase : public ProtoDatabaseProviderTestBase {
models_and_features_store()->RunInitCallback(load_models,
have_models_in_store);
RunUntilIdle();
- // Move clock forward for any short delays added for the fetcher.
- MoveClockForwardBy(base::Seconds(2));
+ // Move clock forward for any short delays added for the fetcher, until the
+ // startup fetch could start.
+ MoveClockForwardBy(base::Seconds(12));
}
void MoveClockForwardBy(base::TimeDelta time_delta) {
@@ -472,11 +480,12 @@ class PredictionManagerTestBase : public ProtoDatabaseProviderTestBase {
base::test::TaskEnvironment* task_environment() { return &task_environment_; }
- void SetOptimizationGuideFetchingPrefEnabled(bool enabled) {
- pref_service_->SetBoolean(prefs::kOptimizationGuideFetchingEnabled,
- enabled);
+ void SetComponentUpdatesPrefEnabled(bool enabled) {
+ component_updates_enabled_ = enabled;
}
+ bool AreComponentUpdatesEnabled() const { return component_updates_enabled_; }
+
protected:
// |feature_list_| needs to be destroyed after |task_environment_|, to avoid
// tsan flakes caused by other tasks running while |feature_list_| is
@@ -493,6 +502,8 @@ class PredictionManagerTestBase : public ProtoDatabaseProviderTestBase {
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+ std::unique_ptr<TestingPrefServiceSimple> local_state_prefs_;
+ bool component_updates_enabled_ = true;
};
class PredictionManagerRemoteFetchingDisabledTest
@@ -566,7 +577,7 @@ class PredictionManagerTest : public PredictionManagerTestBase {
};
TEST_F(PredictionManagerTest, RemoteFetchingPrefDisabled) {
- SetOptimizationGuideFetchingPrefEnabled(false);
+ SetComponentUpdatesPrefEnabled(false);
CreatePredictionManager();
prediction_manager()->SetPredictionModelFetcherForTesting(
diff --git a/chromium/components/optimization_guide/core/prediction_model_download_manager.cc b/chromium/components/optimization_guide/core/prediction_model_download_manager.cc
index 1a111aa178b..1e7f86fbff3 100644
--- a/chromium/components/optimization_guide/core/prediction_model_download_manager.cc
+++ b/chromium/components/optimization_guide/core/prediction_model_download_manager.cc
@@ -103,6 +103,11 @@ PredictionModelDownloadManager::PredictionModelDownloadManager(
PredictionModelDownloadManager::~PredictionModelDownloadManager() = default;
+// static
+base::FilePath::StringType PredictionModelDownloadManager::ModelInfoFileName() {
+ return kModelInfoFileName;
+}
+
void PredictionModelDownloadManager::StartDownload(
const GURL& download_url,
proto::OptimizationTarget optimization_target) {
@@ -219,8 +224,8 @@ void PredictionModelDownloadManager::OnDownloadSucceeded(
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
- base::BindOnce(&PredictionModelDownloadManager::ProcessDownload,
- base::Unretained(this), file_path),
+ base::BindOnce(&PredictionModelDownloadManager::VerifyDownload, file_path,
+ /*delete_file_on_error=*/true),
base::BindOnce(&PredictionModelDownloadManager::StartUnzipping,
ui_weak_ptr_factory_.GetWeakPtr(), optimization_target));
}
@@ -237,11 +242,11 @@ void PredictionModelDownloadManager::OnDownloadFailed(
NotifyModelDownloadFailed(*optimization_target);
}
+// static
+// Note: This function runs on a background sequence!
absl::optional<std::pair<base::FilePath, base::FilePath>>
-PredictionModelDownloadManager::ProcessDownload(
- const base::FilePath& file_path) {
- DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
-
+PredictionModelDownloadManager::VerifyDownload(const base::FilePath& file_path,
+ bool delete_file_on_error) {
if (!switches::ShouldSkipModelDownloadVerificationForTesting()) {
// Verify that the |file_path| contains a valid CRX file.
std::string public_key;
@@ -253,9 +258,11 @@ PredictionModelDownloadManager::ProcessDownload(
if (verifier_result != crx_file::VerifierResult::OK_FULL) {
RecordPredictionModelDownloadStatus(
PredictionModelDownloadStatus::kFailedCrxVerification);
- base::ThreadPool::PostTask(
- FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
- base::BindOnce(base::GetDeleteFileCallback(), file_path));
+ if (delete_file_on_error) {
+ base::ThreadPool::PostTask(
+ FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+ base::GetDeleteFileCallback(file_path));
+ }
return absl::nullopt;
}
@@ -270,22 +277,26 @@ PredictionModelDownloadManager::ProcessDownload(
if (publisher_key_hash != public_key_hash) {
RecordPredictionModelDownloadStatus(
PredictionModelDownloadStatus::kFailedCrxInvalidPublisher);
- base::ThreadPool::PostTask(
- FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
- base::BindOnce(base::GetDeleteFileCallback(), file_path));
+ if (delete_file_on_error) {
+ base::ThreadPool::PostTask(
+ FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+ base::GetDeleteFileCallback(file_path));
+ }
return absl::nullopt;
}
}
- // Unzip download.
+ // Create a temp directory to unzip the model package.
base::FilePath temp_dir_path;
if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
&temp_dir_path)) {
RecordPredictionModelDownloadStatus(
PredictionModelDownloadStatus::kFailedUnzipDirectoryCreation);
- base::ThreadPool::PostTask(
- FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
- base::BindOnce(base::GetDeleteFileCallback(), file_path));
+ if (delete_file_on_error) {
+ base::ThreadPool::PostTask(
+ FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+ base::GetDeleteFileCallback(file_path));
+ }
return absl::nullopt;
}
@@ -326,8 +337,7 @@ void PredictionModelDownloadManager::OnDownloadUnzipped(
// Clean up original download file when this function finishes.
background_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(base::GetDeleteFileCallback(), original_file_path));
+ FROM_HERE, base::GetDeleteFileCallback(original_file_path));
if (!success) {
if (optimization_target) {
@@ -353,8 +363,7 @@ PredictionModelDownloadManager::ProcessUnzippedContents(
const base::FilePath& unzipped_dir_path) {
// Clean up temp dir when this function finishes.
base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(base::GetDeletePathRecursivelyCallback(),
- unzipped_dir_path));
+ FROM_HERE, base::GetDeletePathRecursivelyCallback(unzipped_dir_path));
// Unpack and verify model info file.
base::FilePath model_info_path = unzipped_dir_path.Append(kModelInfoFileName);
@@ -379,7 +388,6 @@ PredictionModelDownloadManager::ProcessUnzippedContents(
if (model_dir_path.empty()) {
RecordPredictionModelDownloadStatus(
PredictionModelDownloadStatus::kOptGuideDirectoryDoesNotExist);
-
return absl::nullopt;
}
diff --git a/chromium/components/optimization_guide/core/prediction_model_download_manager.h b/chromium/components/optimization_guide/core/prediction_model_download_manager.h
index 20191e2d11b..c5d13054468 100644
--- a/chromium/components/optimization_guide/core/prediction_model_download_manager.h
+++ b/chromium/components/optimization_guide/core/prediction_model_download_manager.h
@@ -62,12 +62,24 @@ class PredictionModelDownloadManager {
virtual void StartDownload(const GURL& download_url,
proto::OptimizationTarget optimization_target);
+ // Verifies the download came from a trusted source and process the downloaded
+ // contents. Returns a pair of file paths of the form (src, dst) if
+ // |file_path| is successfully verified.
+ //
+ // Must be called on a background thread, as it performs file I/O.
+ static absl::optional<std::pair<base::FilePath, base::FilePath>>
+ VerifyDownload(const base::FilePath& file_path, bool delete_file_on_error);
+
// Cancels all pending downloads.
virtual void CancelAllPendingDownloads();
// Returns whether the downloader can download models.
virtual bool IsAvailableForDownloads() const;
+ // Returns the basename of the model info file when it is packaged in a crx
+ // archive.
+ static base::FilePath::StringType ModelInfoFileName();
+
// Adds and removes observers.
//
// All methods called on observers will be invoked on the UI thread.
@@ -113,14 +125,6 @@ class PredictionModelDownloadManager {
absl::optional<proto::OptimizationTarget> optimization_target,
const std::string& failed_download_guid);
- // Verifies the download came from a trusted source and process the downloaded
- // contents. Returns a pair of file paths of the form (src, dst) if
- // |file_path| is successfully verified.
- //
- // Must be called on the background thread, as it performs file I/O.
- absl::optional<std::pair<base::FilePath, base::FilePath>> ProcessDownload(
- const base::FilePath& file_path);
-
// Starts unzipping the contents of |unzip_paths|, if present. |unzip_paths|
// is a pair of the form (src, dst), if present.
void StartUnzipping(
diff --git a/chromium/components/optimization_guide/core/prediction_model_download_manager_unittest.cc b/chromium/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
index 1cfc7ccdeb5..fb675b84894 100644
--- a/chromium/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
+++ b/chromium/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
@@ -4,6 +4,7 @@
#include "components/optimization_guide/core/prediction_model_download_manager.h"
+#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
diff --git a/chromium/components/optimization_guide/core/prediction_model_override.cc b/chromium/components/optimization_guide/core/prediction_model_override.cc
new file mode 100644
index 00000000000..11f347c1c1b
--- /dev/null
+++ b/chromium/components/optimization_guide/core/prediction_model_override.cc
@@ -0,0 +1,161 @@
+// Copyright 2022 The Chromium 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/optimization_guide/core/prediction_model_override.h"
+
+#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/optimization_guide/core/model_util.h"
+#include "components/optimization_guide/core/prediction_model_download_manager.h"
+#include "components/services/unzip/public/cpp/unzip.h"
+
+#if BUILDFLAG(IS_IOS)
+#include "components/services/unzip/in_process_unzipper.h" // nogncheck
+#else
+#include "components/services/unzip/content/unzip_service.h" // nogncheck
+#endif
+
+namespace optimization_guide {
+
+namespace {
+
+void OnModelOverrideProcessed(OnPredictionModelBuiltCallback callback,
+ std::unique_ptr<proto::PredictionModel> model) {
+ std::move(callback).Run(std::move(model));
+}
+
+std::unique_ptr<proto::PredictionModel> ProcessModelOverrideOnBGThread(
+ proto::OptimizationTarget optimization_target,
+ const base::FilePath& unzipped_dir_path) {
+ // Unpack and verify model info file.
+ base::FilePath model_info_path = unzipped_dir_path.Append(
+ PredictionModelDownloadManager::ModelInfoFileName());
+ std::string binary_model_info_pb;
+ if (!base::ReadFileToString(model_info_path, &binary_model_info_pb)) {
+ LOG(ERROR) << "Failed to read " << FilePathToString(model_info_path);
+ return nullptr;
+ }
+ proto::ModelInfo model_info;
+ if (!model_info.ParseFromString(binary_model_info_pb)) {
+ LOG(ERROR) << "Failed to parse " << FilePathToString(model_info_path);
+ return nullptr;
+ }
+
+ if (!model_info.has_version() || !model_info.has_optimization_target()) {
+ LOG(ERROR) << FilePathToString(model_info_path)
+ << "is invalid because it does not contain a version and/or "
+ "optimization target";
+ return nullptr;
+ }
+
+ for (int i = 0; i < model_info.additional_files_size(); i++) {
+ proto::AdditionalModelFile* additional_file =
+ model_info.mutable_additional_files(i);
+
+ base::FilePath additional_file_basename =
+ *StringToFilePath(additional_file->file_path());
+ base::FilePath additional_file_absolute =
+ unzipped_dir_path.Append(additional_file_basename);
+ additional_file->set_file_path(FilePathToString(additional_file_absolute));
+ }
+
+ std::unique_ptr<proto::PredictionModel> model =
+ std::make_unique<proto::PredictionModel>();
+ *model->mutable_model_info() = model_info;
+ model->mutable_model()->set_download_url(
+ FilePathToString(unzipped_dir_path.Append(GetBaseFileNameForModels())));
+
+ return model;
+}
+
+void OnModelOverrideUnzipped(proto::OptimizationTarget optimization_target,
+ const base::FilePath& unzipped_dir_path,
+ OnPredictionModelBuiltCallback callback,
+ bool success) {
+ if (!success) {
+ LOG(ERROR) << FilePathToString(unzipped_dir_path) << "failed to unzip";
+ std::move(callback).Run(nullptr);
+ return;
+ }
+
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+ base::BindOnce(&ProcessModelOverrideOnBGThread, optimization_target,
+ unzipped_dir_path),
+ base::BindOnce(&OnModelOverrideProcessed, std::move(callback)));
+}
+
+void OnModelOverrideVerified(
+ proto::OptimizationTarget optimization_target,
+ const std::string& passed_crx_file_path,
+ OnPredictionModelBuiltCallback callback,
+ absl::optional<std::pair<base::FilePath, base::FilePath>> src_dst) {
+ if (!src_dst) {
+ LOG(ERROR) << passed_crx_file_path << " failed verification";
+ std::move(callback).Run(nullptr);
+ return;
+ }
+
+#if BUILDFLAG(IS_IOS)
+ auto unzipper = unzip::LaunchInProcessUnzipper();
+#else
+ auto unzipper = unzip::LaunchUnzipper();
+#endif
+ unzip::Unzip(std::move(unzipper), src_dst->first, src_dst->second,
+ base::BindOnce(&OnModelOverrideUnzipped, optimization_target,
+ src_dst->second, std::move(callback)));
+}
+
+} // namespace
+
+void BuildPredictionModelFromCommandLineForOptimizationTarget(
+ proto::OptimizationTarget optimization_target,
+ OnPredictionModelBuiltCallback callback) {
+ absl::optional<std::pair<std::string, absl::optional<proto::Any>>>
+ model_file_path_and_metadata =
+ GetModelOverrideForOptimizationTarget(optimization_target);
+ if (!model_file_path_and_metadata) {
+ std::move(callback).Run(nullptr);
+ return;
+ }
+
+ if (base::EndsWith(model_file_path_and_metadata->first, ".crx3")) {
+ DVLOG(0) << "Attempting to parse the model override at "
+ << model_file_path_and_metadata->first
+ << " as a crx model package for "
+ << GetStringNameForOptimizationTarget(optimization_target);
+ if (model_file_path_and_metadata->second) {
+ LOG(ERROR) << "Ignoring the metadata that was passed since a crx package "
+ "was given";
+ }
+
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+ base::BindOnce(PredictionModelDownloadManager::VerifyDownload,
+ *StringToFilePath(model_file_path_and_metadata->first),
+ /*delete_file_on_error=*/false),
+ base::BindOnce(&OnModelOverrideVerified, optimization_target,
+ model_file_path_and_metadata->first,
+ std::move(callback)));
+ return;
+ }
+
+ std::unique_ptr<proto::PredictionModel> prediction_model =
+ std::make_unique<proto::PredictionModel>();
+ prediction_model->mutable_model_info()->set_optimization_target(
+ optimization_target);
+ prediction_model->mutable_model_info()->set_version(123);
+ if (model_file_path_and_metadata->second) {
+ *prediction_model->mutable_model_info()->mutable_model_metadata() =
+ model_file_path_and_metadata->second.value();
+ }
+ prediction_model->mutable_model()->set_download_url(
+ model_file_path_and_metadata->first);
+ std::move(callback).Run(std::move(prediction_model));
+}
+
+} // namespace optimization_guide \ No newline at end of file
diff --git a/chromium/components/optimization_guide/core/prediction_model_override.h b/chromium/components/optimization_guide/core/prediction_model_override.h
new file mode 100644
index 00000000000..b8adc3a07eb
--- /dev/null
+++ b/chromium/components/optimization_guide/core/prediction_model_override.h
@@ -0,0 +1,24 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_OVERRIDE_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_OVERRIDE_H_
+
+#include "base/callback.h"
+#include "components/optimization_guide/proto/models.pb.h"
+
+namespace optimization_guide {
+
+// Attempts to parse the result from |GetModelOverrideForOptimizationTarget|
+// into a |proto::PredictionModel|, returning the result in the given callback
+// or nullptr if there was an error. In the event of an error, check LOG(ERROR).
+using OnPredictionModelBuiltCallback =
+ base::OnceCallback<void(std::unique_ptr<proto::PredictionModel>)>;
+void BuildPredictionModelFromCommandLineForOptimizationTarget(
+ proto::OptimizationTarget optimization_target,
+ OnPredictionModelBuiltCallback callback);
+
+} // namespace optimization_guide
+
+#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_OVERRIDE_H_ \ No newline at end of file
diff --git a/chromium/components/optimization_guide/core/store_update_data.cc b/chromium/components/optimization_guide/core/store_update_data.cc
index d5c636ef16b..056a5be48ad 100644
--- a/chromium/components/optimization_guide/core/store_update_data.cc
+++ b/chromium/components/optimization_guide/core/store_update_data.cc
@@ -150,7 +150,7 @@ void StoreUpdateData::CopyPredictionModelIntoUpdateData(
expiry_duration =
base::Seconds(prediction_model.model_info().valid_duration().seconds());
} else {
- expiry_duration = features::StoredFetchedHintsFreshnessDuration();
+ expiry_duration = features::StoredModelsValidDuration();
}
expiry_time_ = base::Time::Now() + expiry_duration;
entry_proto.set_expiry_time_secs(
diff --git a/chromium/components/optimization_guide/core/store_update_data_unittest.cc b/chromium/components/optimization_guide/core/store_update_data_unittest.cc
index e36087ea00c..2e2c1bf6d22 100644
--- a/chromium/components/optimization_guide/core/store_update_data_unittest.cc
+++ b/chromium/components/optimization_guide/core/store_update_data_unittest.cc
@@ -136,7 +136,7 @@ TEST(StoreUpdateDataTest, BuildPredictionModelUpdateData) {
// Verify there is 1 store entry.
const auto update_entries = prediction_model_update->TakeUpdateEntries();
EXPECT_EQ(1ul, update_entries->size());
- // Verify expiry time taken from hint rather than the default expiry time of
+ // Verify expiry time taken from model rather than the default expiry time of
// the store update data.
bool found_prediction_model_entry = false;
for (const auto& entry : *update_entries) {
@@ -153,6 +153,44 @@ TEST(StoreUpdateDataTest, BuildPredictionModelUpdateData) {
EXPECT_TRUE(found_prediction_model_entry);
}
+TEST(StoreUpdateDataTest, DefaultExpiryPredictionModelUpdateData) {
+ // Verify creating a Prediction Model update data.
+ proto::PredictionModel prediction_model;
+
+ proto::ModelInfo* model_info = prediction_model.mutable_model_info();
+ model_info->set_version(1);
+ model_info->set_optimization_target(
+ proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+ model_info->add_supported_model_engine_versions(
+ proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
+ model_info->set_keep_beyond_valid_duration(false);
+
+ std::unique_ptr<StoreUpdateData> prediction_model_update =
+ StoreUpdateData::CreatePredictionModelStoreUpdateData(base::Time::Now());
+ prediction_model_update->CopyPredictionModelIntoUpdateData(prediction_model);
+ EXPECT_FALSE(prediction_model_update->component_version().has_value());
+ EXPECT_FALSE(prediction_model_update->update_time().has_value());
+ // Verify there is 1 store entry.
+ const auto update_entries = prediction_model_update->TakeUpdateEntries();
+ EXPECT_EQ(1ul, update_entries->size());
+ // Verify expiry time taken from the default expiry time of model.
+ bool found_prediction_model_entry = false;
+ for (const auto& entry : *update_entries) {
+ proto::StoreEntry store_entry = entry.second;
+ if (store_entry.entry_type() == proto::PREDICTION_MODEL) {
+ found_prediction_model_entry = true;
+ base::Time expected_expiry_time =
+ base::Time::Now() + features::StoredModelsValidDuration();
+ EXPECT_EQ(expected_expiry_time.ToDeltaSinceWindowsEpoch().InSeconds(),
+ store_entry.expiry_time_secs());
+ EXPECT_EQ(store_entry.keep_beyond_valid_duration(),
+ model_info->keep_beyond_valid_duration());
+ break;
+ }
+ }
+ EXPECT_TRUE(found_prediction_model_entry);
+}
+
} // namespace
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/core/test_model_executor.h b/chromium/components/optimization_guide/core/test_model_executor.h
index 987942810e8..515b7242386 100644
--- a/chromium/components/optimization_guide/core/test_model_executor.h
+++ b/chromium/components/optimization_guide/core/test_model_executor.h
@@ -16,6 +16,7 @@ class TestModelExecutor
~TestModelExecutor() override = default;
void InitializeAndMoveToExecutionThread(
+ absl::optional<base::TimeDelta>,
proto::OptimizationTarget,
scoped_refptr<base::SequencedTaskRunner>,
scoped_refptr<base::SequencedTaskRunner>) override {}
diff --git a/chromium/components/optimization_guide/core/test_model_handler.h b/chromium/components/optimization_guide/core/test_model_handler.h
index bd9163ea6af..dbf37284505 100644
--- a/chromium/components/optimization_guide/core/test_model_handler.h
+++ b/chromium/components/optimization_guide/core/test_model_handler.h
@@ -22,6 +22,7 @@ class TestModelHandler
model_provider,
background_task_runner,
std::move(executor),
+ /*model_inference_timeout=*/absl::nullopt,
proto::OptimizationTarget::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
/*model_metadata=*/absl::nullopt) {}
~TestModelHandler() override = default;
diff --git a/chromium/components/optimization_guide/core/test_tflite_model_handler.h b/chromium/components/optimization_guide/core/test_tflite_model_handler.h
index fe06c15130a..d2cdbb4cde3 100644
--- a/chromium/components/optimization_guide/core/test_tflite_model_handler.h
+++ b/chromium/components/optimization_guide/core/test_tflite_model_handler.h
@@ -22,6 +22,7 @@ class TestTFLiteModelHandler
model_provider,
background_task_runner,
std::move(executor),
+ /*model_inference_timeout=*/absl::nullopt,
proto::OptimizationTarget::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
/*model_metadata=*/absl::nullopt) {}
~TestTFLiteModelHandler() override = default;
diff --git a/chromium/components/optimization_guide/core/tflite_model_executor.h b/chromium/components/optimization_guide/core/tflite_model_executor.h
index f8f8a80eb17..1d68a7538a9 100644
--- a/chromium/components/optimization_guide/core/tflite_model_executor.h
+++ b/chromium/components/optimization_guide/core/tflite_model_executor.h
@@ -13,6 +13,7 @@
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_functions.h"
#include "base/sequence_checker.h"
+#include "base/task/thread_pool.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
@@ -75,24 +76,16 @@ class ScopedExecutionStatusResultRecorder {
template <class OutputType, class... InputTypes>
class TFLiteModelExecutor : public ModelExecutor<OutputType, InputTypes...> {
public:
- TFLiteModelExecutor() = default;
+ TFLiteModelExecutor()
+ : watchdog_(nullptr, base::OnTaskRunnerDeleter(nullptr)) {}
~TFLiteModelExecutor() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- // |watchdog_| uses a thread internally so we need to allow sync primitives
- // when destroying (joining) it.
- //
- // Note that this dtor is already being called on a background task runner
- // via DeleteSoon.
- if (watchdog_) {
- base::ScopedAllowBaseSyncPrimitives allow_sync_primitives;
- watchdog_.reset();
- }
}
// Should be called on the same sequence as the ctor, but once called |this|
// must only be used from the |execution_task_runner| thread/sequence.
void InitializeAndMoveToExecutionThread(
+ absl::optional<base::TimeDelta> model_inference_timeout,
proto::OptimizationTarget optimization_target,
scoped_refptr<base::SequencedTaskRunner> execution_task_runner,
scoped_refptr<base::SequencedTaskRunner> reply_task_runner) override {
@@ -105,17 +98,28 @@ class TFLiteModelExecutor : public ModelExecutor<OutputType, InputTypes...> {
optimization_target_ = optimization_target;
execution_task_runner_ = execution_task_runner;
reply_task_runner_ = reply_task_runner;
- if (features::ModelExecutionTimeout()) {
- watchdog_ = std::make_unique<
- ModelExecutionTimeoutWatchdog<OutputType, InputTypes...>>(
- optimization_target_, *features::ModelExecutionTimeout());
+ if (features::IsModelExecutionWatchdogEnabled()) {
+ // The sequence |watchdog_sequence| is used to run watchdog's task. The
+ // watchdog must be deleted on that sequence to guarantee that pending
+ // tasks can safely be executed.
+ scoped_refptr<base::SequencedTaskRunner> watchdog_sequence =
+ base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
+ using WatchdogType =
+ ModelExecutionTimeoutWatchdog<OutputType, InputTypes...>;
+ watchdog_ = std::unique_ptr<WatchdogType, base::OnTaskRunnerDeleter>(
+ new WatchdogType(
+ watchdog_sequence, optimization_target_,
+ model_inference_timeout.value_or(
+ features::ModelExecutionWatchdogDefaultTimeout())),
+ base::OnTaskRunnerDeleter(watchdog_sequence));
}
}
// Called when a model file is available to load. Depending on feature flags,
// the model may or may not be immediately loaded.
void UpdateModelFile(const base::FilePath& file_path) override {
- DCHECK(execution_task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(execution_task_runner_ &&
+ execution_task_runner_->RunsTasksInCurrentSequence());
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UnloadModel();
@@ -328,7 +332,8 @@ class TFLiteModelExecutor : public ModelExecutor<OutputType, InputTypes...> {
bool should_unload_model_on_complete_ = true;
- std::unique_ptr<ModelExecutionTimeoutWatchdog<OutputType, InputTypes...>>
+ std::unique_ptr<ModelExecutionTimeoutWatchdog<OutputType, InputTypes...>,
+ base::OnTaskRunnerDeleter>
watchdog_;
scoped_refptr<base::SequencedTaskRunner> execution_task_runner_;
diff --git a/chromium/components/optimization_guide/core/tflite_model_executor_unittest.cc b/chromium/components/optimization_guide/core/tflite_model_executor_unittest.cc
index 9fb084ebbc0..fc315676c29 100644
--- a/chromium/components/optimization_guide/core/tflite_model_executor_unittest.cc
+++ b/chromium/components/optimization_guide/core/tflite_model_executor_unittest.cc
@@ -3,9 +3,11 @@
// found in the LICENSE file.
#include "base/path_service.h"
+#include "base/task/thread_pool.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
+#include "base/threading/thread_restrictions.h"
#include "components/optimization_guide/core/test_model_info_builder.h"
#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
#include "components/optimization_guide/core/test_tflite_model_executor.h"
@@ -74,6 +76,9 @@ class TFLiteModelExecutorTest : public testing::Test {
test_model_provider_ =
std::make_unique<TestOptimizationGuideModelProvider>();
+
+ execution_sequence_ =
+ base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
}
void TearDown() override { ResetModelHandler(); }
@@ -83,7 +88,7 @@ class TFLiteModelExecutorTest : public testing::Test {
model_handler_.reset();
model_handler_ = std::make_unique<TestTFLiteModelHandler>(
- test_model_provider(), task_environment_.GetMainThreadTaskRunner());
+ test_model_provider(), execution_sequence_);
}
void ResetModelHandler(
@@ -112,6 +117,9 @@ class TFLiteModelExecutorTest : public testing::Test {
return test_model_provider_.get();
}
+ base::SequencedTaskRunner* execution_sequence() {
+ return execution_sequence_.get();
+ }
base::test::TaskEnvironment* task_environment() { return &task_environment_; }
void RunUntilIdle() { task_environment_.RunUntilIdle(); }
@@ -120,6 +128,7 @@ class TFLiteModelExecutorTest : public testing::Test {
std::unique_ptr<TestTFLiteModelHandler> model_handler_;
private:
+ scoped_refptr<base::SequencedTaskRunner> execution_sequence_;
base::test::TaskEnvironment task_environment_;
base::FilePath model_file_path_;
std::unique_ptr<TestOptimizationGuideModelProvider> test_model_provider_;
@@ -141,6 +150,9 @@ TEST_F(TFLiteModelExecutorTest, ExecuteReturnsImmediatelyIfNoModelLoaded) {
std::vector<float>{1, 1, 1});
run_loop->Run();
+ // Ensures pending tasks are processed. They are generating UMA metrics.
+ RunUntilIdle();
+
histogram_tester.ExpectTotalCount(
"OptimizationGuide.ModelExecutor.TaskExecutionLatency." +
optimization_guide::GetStringNameForOptimizationTarget(
@@ -197,6 +209,9 @@ TEST_F(TFLiteModelExecutorTest, ExecuteWithLoadedModel) {
input);
run_loop->Run();
+ // Ensures pending tasks are processed. They are generating UMA metrics.
+ RunUntilIdle();
+
histogram_tester.ExpectTotalCount(
"OptimizationGuide.ModelExecutor.TaskExecutionLatency." +
optimization_guide::GetStringNameForOptimizationTarget(
@@ -251,6 +266,8 @@ TEST_F(TFLiteModelExecutorTest, ExecuteTwiceWithLoadedModel) {
run_loop.get()),
input);
run_loop->Run();
+
+ // Ensures pending tasks are processed. They are generating UMA metrics.
RunUntilIdle();
histogram_tester.ExpectTotalCount(
@@ -282,6 +299,9 @@ TEST_F(TFLiteModelExecutorTest, ExecuteTwiceWithLoadedModel) {
input);
run_loop->Run();
+ // Ensures pending tasks are processed. They are generating UMA metrics.
+ RunUntilIdle();
+
// The model should have been loaded a second time.
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ModelExecutor.ModelAvailableToLoad." +
@@ -319,7 +339,7 @@ TEST_F(TFLiteModelExecutorTest, ExecuteTwiceWithLoadedModel) {
TEST_F(TFLiteModelExecutorTest, DoNotUnloadAfterExecution) {
base::HistogramTester histogram_tester;
ResetModelHandler(std::make_unique<NoUnloadingTestTFLiteModelHandler>(
- test_model_provider(), task_environment()->GetMainThreadTaskRunner()));
+ test_model_provider(), execution_sequence()));
proto::Any any_metadata;
any_metadata.set_type_url("type.googleapis.com/com.foo.Duration");
@@ -355,7 +375,9 @@ TEST_F(TFLiteModelExecutorTest, DoNotUnloadAfterExecution) {
input);
run_loop->Run();
+ // Ensures pending tasks are processed. They are generating UMA metrics.
RunUntilIdle();
+
EXPECT_TRUE(model_handler()->ModelAvailable());
EXPECT_TRUE(model_handler()
->ParsedSupportedFeaturesForLoadedModel<proto::Duration>());
@@ -394,6 +416,9 @@ TEST_F(TFLiteModelExecutorTest, DoNotUnloadAfterExecution) {
input);
run_loop->Run();
+ // Ensures pending tasks are processed. They are generating UMA metrics.
+ RunUntilIdle();
+
histogram_tester.ExpectTotalCount(
"OptimizationGuide.ModelExecutor.TaskSchedulingLatency." +
optimization_guide::GetStringNameForOptimizationTarget(
@@ -420,11 +445,9 @@ class CancelledTFLiteModelExecutorTest : public TFLiteModelExecutorTest {
}
~CancelledTFLiteModelExecutorTest() override = default;
- void SetUp() override { TFLiteModelExecutorTest::SetUp(); }
-
void CreateModelHandler() override {
model_handler_ = std::make_unique<EnsureCancelledTestTFLiteModelHandler>(
- test_model_provider(), task_environment()->GetMainThreadTaskRunner());
+ test_model_provider(), execution_sequence());
}
private:
@@ -459,6 +482,9 @@ TEST_F(CancelledTFLiteModelExecutorTest, RunsTooLong) {
input);
run_loop.Run();
+ // Ensures pending tasks are processed. They are generating UMA metrics.
+ RunUntilIdle();
+
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ModelExecutor.ExecutionStatus.PainfulPageLoad",
ExecutionStatus::kErrorCancelled, 1);
diff --git a/chromium/components/optimization_guide/core/tflite_op_resolver.cc b/chromium/components/optimization_guide/core/tflite_op_resolver.cc
index 8674eb8d700..996fa9476e9 100644
--- a/chromium/components/optimization_guide/core/tflite_op_resolver.cc
+++ b/chromium/components/optimization_guide/core/tflite_op_resolver.cc
@@ -3,10 +3,16 @@
// found in the LICENSE file.
#include "components/optimization_guide/core/tflite_op_resolver.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
+#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
#include "third_party/tflite/src/tensorflow/lite/c/common.h"
#include "third_party/tflite/src/tensorflow/lite/kernels/builtin_op_kernels.h"
#include "third_party/tflite/src/tensorflow/lite/schema/schema_generated.h"
+#if BUILDFLAG(BUILD_TFLITE_WITH_XNNPACK)
+#include "third_party/tflite/src/tensorflow/lite/tflite_with_xnnpack_optional.h"
+#endif
+
namespace optimization_guide {
TFLiteOpResolver::TFLiteOpResolver() {
@@ -375,6 +381,14 @@ TFLiteOpResolver::TFLiteOpResolver() {
tflite::ops::builtin::Register_GELU(),
/* min_version = */ 1,
/* max_version = */ 2);
+
+#if BUILDFLAG(BUILD_TFLITE_WITH_XNNPACK)
+ if (features::TFLiteXNNPACKDelegateEnabled()) {
+ delegate_creators_.push_back([](int num_threads) {
+ return tflite::MaybeCreateXNNPACKDelegate(num_threads);
+ });
+ }
+#endif
}
} // namespace optimization_guide
diff --git a/chromium/components/optimization_guide/optimization_guide_internals/resources/BUILD.gn b/chromium/components/optimization_guide/optimization_guide_internals/resources/BUILD.gn
index 22359bed596..8de928f3ac5 100644
--- a/chromium/components/optimization_guide/optimization_guide_internals/resources/BUILD.gn
+++ b/chromium/components/optimization_guide/optimization_guide_internals/resources/BUILD.gn
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import("//tools/grit/grit_rule.gni")
-import("//tools/polymer/html_to_js.gni")
import("//tools/typescript/ts_library.gni")
import("//ui/webui/resources/tools/generate_grd.gni")
@@ -34,12 +33,11 @@ generate_grd("build_grd") {
input_files_base_dir = rebase_path(".", "//")
}
-html_to_js("web_components") {
- js_files = [ "optimization_guide_internals.ts" ]
-}
-
-copy("copy_proxy") {
- sources = [ "optimization_guide_internals_browser_proxy.ts" ]
+copy("copy_ts") {
+ sources = [
+ "optimization_guide_internals.ts",
+ "optimization_guide_internals_browser_proxy.ts",
+ ]
outputs = [ "$target_gen_dir/{{source_file_part}}" ]
}
@@ -61,11 +59,10 @@ ts_library("build_ts") {
]
deps = [
"//ui/webui/resources:library",
- "//ui/webui/resources/js/browser_command:build_ts",
+ "//ui/webui/resources/mojo:library",
]
extra_deps = [
":copy_mojo",
- ":copy_proxy",
- ":web_components",
+ ":copy_ts",
]
}
diff --git a/chromium/components/optimization_guide/proto/models.proto b/chromium/components/optimization_guide/proto/models.proto
index a1b2f0fc8ab..f93899b80be 100644
--- a/chromium/components/optimization_guide/proto/models.proto
+++ b/chromium/components/optimization_guide/proto/models.proto
@@ -275,6 +275,11 @@ enum OptimizationTarget {
OPTIMIZATION_TARGET_PAGE_TOPICS_V2 = 15;
// Target for segmentation: Determine users with low engagement with chrome.
OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT = 16;
+ // Target for segmentation: Determine users who prefer to use Feed.
+ OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER = 17;
+ // Target for segmentation: Determine whether price tracking should be shown
+ // as a contextual page action.
+ OPTIMIZATION_TARGET_CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING = 18;
}
// The model engine versions that can be used to do model inference.
diff --git a/chromium/components/optimization_guide/proto/page_entities_metadata.proto b/chromium/components/optimization_guide/proto/page_entities_metadata.proto
index 1b5fa334a18..222b8b7f265 100644
--- a/chromium/components/optimization_guide/proto/page_entities_metadata.proto
+++ b/chromium/components/optimization_guide/proto/page_entities_metadata.proto
@@ -29,6 +29,8 @@ message Entity {
message PageEntitiesMetadata {
// A set of entities that are expected to be present on the page.
repeated Entity entities = 1;
+ // A string representing an alternative title for the page.
+ optional string alternative_title = 2;
}
// The metadata associated with an |Entity|.
@@ -38,4 +40,4 @@ message PageEntitiesMetadata {
// opposed to the opaque entity_id.
message EntityMetadataStorage {
optional string entity_name = 1;
-} \ No newline at end of file
+}
diff --git a/chromium/components/os_crypt/BUILD.gn b/chromium/components/os_crypt/BUILD.gn
index 0afd3f7e942..9d7a8ad7a54 100644
--- a/chromium/components/os_crypt/BUILD.gn
+++ b/chromium/components/os_crypt/BUILD.gn
@@ -45,9 +45,6 @@ component("os_crypt") {
"//build:chromeos_buildflags",
"//components/prefs",
"//crypto",
-
- # TODO(tfarina): Remove this dep when http://crbug.com/363749 is fixed.
- "//crypto:platform",
]
configs += [ "//build/config/compiler:wexit_time_destructors" ]
@@ -57,7 +54,7 @@ component("os_crypt") {
defines += [ "ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE" ]
}
- if ((is_posix || is_fuchsia) && !is_apple && (!is_linux || is_chromecast)) {
+ if ((is_posix || is_fuchsia) && !is_apple && !(is_linux && !is_castos)) {
sources += [ "os_crypt_posix.cc" ]
}
@@ -74,7 +71,7 @@ component("os_crypt") {
libs = [ "crypt32.lib" ]
}
- if (is_linux && !is_chromecast) {
+ if (is_linux && !is_castos) {
sources += [
"key_storage_config_linux.cc",
"key_storage_config_linux.h",
@@ -130,7 +127,7 @@ static_library("test_support") {
"//base",
"//testing/gtest",
]
- if (is_linux && !is_chromecast) {
+ if (is_linux && !is_castos) {
sources += [
"os_crypt_mocker_linux.cc",
"os_crypt_mocker_linux.h",
@@ -168,7 +165,7 @@ source_set("unit_tests") {
sources += [ "keychain_password_mac_unittest.mm" ]
}
- if (is_linux && !is_chromecast) {
+ if (is_linux && !is_castos) {
sources += [
"key_storage_linux_unittest.cc",
"key_storage_util_linux_unittest.cc",
diff --git a/chromium/components/os_crypt/key_storage_keyring_unittest.cc b/chromium/components/os_crypt/key_storage_keyring_unittest.cc
index 071a1bd5185..cbd4c72d6a4 100644
--- a/chromium/components/os_crypt/key_storage_keyring_unittest.cc
+++ b/chromium/components/os_crypt/key_storage_keyring_unittest.cc
@@ -9,6 +9,7 @@
#include "base/test/test_simple_task_runner.h"
#include "build/branding_buildflags.h"
+#include "build/build_config.h"
#include "components/os_crypt/keyring_util_linux.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -130,8 +131,7 @@ class GnomeKeyringTest : public testing::Test {
};
GnomeKeyringTest::GnomeKeyringTest()
- : task_runner_(new base::TestSimpleTaskRunner()),
- keyring_(task_runner_, kApplicationName) {
+ : task_runner_(new base::TestSimpleTaskRunner()), keyring_(task_runner_, "chromium") {
MockGnomeKeyringLoader::ResetForOSCrypt();
}
@@ -139,7 +139,13 @@ GnomeKeyringTest::~GnomeKeyringTest() {
MockGnomeKeyringLoader::TearDown();
}
-TEST_F(GnomeKeyringTest, KeyringRepeats) {
+// crbug.com/1211311 Disable due to persistently failing.
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && BUILDFLAG(IS_LINUX)
+#define MAYBE_KeyringRepeats DISABLED_KeyringRepeats
+#else
+#define MAYBE_KeyringRepeats KeyringRepeats
+#endif
+TEST_F(GnomeKeyringTest, MAYBE_KeyringRepeats) {
absl::optional<std::string> password = keyring_.GetKey();
EXPECT_TRUE(password.has_value());
EXPECT_FALSE(password.value().empty());
@@ -148,7 +154,13 @@ TEST_F(GnomeKeyringTest, KeyringRepeats) {
EXPECT_EQ(password.value(), password_repeat.value());
}
-TEST_F(GnomeKeyringTest, KeyringCreatesRandomised) {
+// crbug.com/1211311 Disable due to persistently failing.
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && BUILDFLAG(IS_LINUX)
+#define MAYBE_KeyringCreatesRandomised DISABLED_KeyringCreatesRandomised
+#else
+#define MAYBE_KeyringCreatesRandomised KeyringCreatesRandomised
+#endif
+TEST_F(GnomeKeyringTest, MAYBE_KeyringCreatesRandomised) {
absl::optional<std::string> password = keyring_.GetKey();
MockGnomeKeyringLoader::ResetForOSCrypt();
absl::optional<std::string> password_new = keyring_.GetKey();
diff --git a/chromium/components/os_crypt/key_storage_util_linux.cc b/chromium/components/os_crypt/key_storage_util_linux.cc
index b11eac50ee9..3578b48e36f 100644
--- a/chromium/components/os_crypt/key_storage_util_linux.cc
+++ b/chromium/components/os_crypt/key_storage_util_linux.cc
@@ -59,6 +59,7 @@ SelectedLinuxBackend SelectBackend(const std::string& type,
case base::nix::DESKTOP_ENVIRONMENT_KDE5:
return SelectedLinuxBackend::KWALLET5;
case base::nix::DESKTOP_ENVIRONMENT_CINNAMON:
+ case base::nix::DESKTOP_ENVIRONMENT_DEEPIN:
case base::nix::DESKTOP_ENVIRONMENT_GNOME:
case base::nix::DESKTOP_ENVIRONMENT_PANTHEON:
case base::nix::DESKTOP_ENVIRONMENT_UKUI:
diff --git a/chromium/components/os_crypt/libsecret_util_linux.cc b/chromium/components/os_crypt/libsecret_util_linux.cc
index 8567fce74d8..f2a8741f667 100644
--- a/chromium/components/os_crypt/libsecret_util_linux.cc
+++ b/chromium/components/os_crypt/libsecret_util_linux.cc
@@ -6,6 +6,7 @@
#include <dlfcn.h>
+#include "base/check_op.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
diff --git a/chromium/components/os_crypt/os_crypt.h b/chromium/components/os_crypt/os_crypt.h
index ee821609454..80cf8d52815 100644
--- a/chromium/components/os_crypt/os_crypt.h
+++ b/chromium/components/os_crypt/os_crypt.h
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
+#include "base/callback.h"
#include "base/component_export.h"
#include "base/memory/ref_counted.h"
#include "base/task/single_thread_task_runner.h"
@@ -23,6 +24,12 @@ class PrefRegistrySimple;
class PrefService;
#endif
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)
+namespace crypto {
+class SymmetricKey;
+}
+#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)
+
namespace os_crypt {
struct Config;
}
@@ -75,32 +82,38 @@ COMPONENT_EXPORT(OS_CRYPT) void UseMockKeyForTesting(bool use_mock);
COMPONENT_EXPORT(OS_CRYPT) void SetLegacyEncryptionForTesting(bool legacy);
COMPONENT_EXPORT(OS_CRYPT) void ResetStateForTesting();
#endif // BUILDFLAG(IS_WIN)
-#if (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMECAST))
+#if (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
COMPONENT_EXPORT(OS_CRYPT)
void UseMockKeyStorageForTesting(
- std::unique_ptr<KeyStorageLinux> (*get_key_storage_mock)());
+ base::OnceCallback<std::unique_ptr<KeyStorageLinux>()>
+ storage_provider_factory);
COMPONENT_EXPORT(OS_CRYPT) void ClearCacheForTesting();
COMPONENT_EXPORT(OS_CRYPT)
void SetEncryptionPasswordForTesting(const std::string& password);
-#endif // (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMECAST))
+#endif // (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
} // namespace OSCrypt
// The OSCryptImpl class gives access to simple encryption and decryption of
// strings. Note that on Mac, access to the system Keychain is required and
// these calls can block the current thread to collect user input. The same is
// true for Linux, if a password management tool is available.
-class OSCryptImpl {
+class COMPONENT_EXPORT(OS_CRYPT) OSCryptImpl {
public:
- OSCryptImpl() = delete;
+ OSCryptImpl();
+ ~OSCryptImpl();
OSCryptImpl(const OSCryptImpl&) = delete;
+ OSCryptImpl(OSCryptImpl&&) = delete;
OSCryptImpl& operator=(const OSCryptImpl&) = delete;
+ OSCryptImpl& operator=(OSCryptImpl&&) = delete;
+
+ // Returns singleton instance of OSCryptImpl.
+ static OSCryptImpl* GetInstance();
#if BUILDFLAG(IS_LINUX)
// Set the configuration of OSCryptImpl.
// This method, or SetRawEncryptionKey(), must be called before using
// EncryptString() and DecryptString().
- static void SetConfig(
- std::unique_ptr<os_crypt::Config> config);
+ void SetConfig(std::unique_ptr<os_crypt::Config> config);
#endif // BUILDFLAG(IS_LINUX)
// On Linux returns true iff the real secret key (not hardcoded one) is
@@ -109,64 +122,54 @@ class OSCryptImpl {
// locked mock Keychain). On Windows returns true if non mock encryption
// key is available. On other platforms, returns false as OSCryptImpl will use
// a hardcoded key.
- static bool IsEncryptionAvailable();
+ bool IsEncryptionAvailable();
// Encrypt a string16. The output (second argument) is really an array of
// bytes, but we're passing it back as a std::string.
- static bool EncryptString16(
- const std::u16string& plaintext,
- std::string* ciphertext);
+ bool EncryptString16(const std::u16string& plaintext,
+ std::string* ciphertext);
// Decrypt an array of bytes obtained with EncryptString16 back into a
// string16. Note that the input (first argument) is a std::string, so you
// need to first get your (binary) data into a string.
- static bool DecryptString16(
- const std::string& ciphertext,
- std::u16string* plaintext);
+ bool DecryptString16(const std::string& ciphertext,
+ std::u16string* plaintext);
// Encrypt a string.
- static bool EncryptString(
- const std::string& plaintext,
- std::string* ciphertext);
+ bool EncryptString(const std::string& plaintext, std::string* ciphertext);
// Decrypt an array of bytes obtained with EnctryptString back into a string.
// Note that the input (first argument) is a std::string, so you need to first
// get your (binary) data into a string.
- static bool DecryptString(
- const std::string& ciphertext,
- std::string* plaintext);
+ bool DecryptString(const std::string& ciphertext, std::string* plaintext);
#if BUILDFLAG(IS_WIN)
// Registers preferences used by OSCryptImpl.
- static void RegisterLocalPrefs(
- PrefRegistrySimple* registry);
+ static void RegisterLocalPrefs(PrefRegistrySimple* registry);
// Initialises OSCryptImpl.
// This method should be called on the main UI thread before any calls to
// encryption or decryption. Returns |true| if os_crypt successfully
// initialized.
- static bool Init(PrefService* local_state);
+ bool Init(PrefService* local_state);
// Initialises OSCryptImpl using an encryption key present in the
// |local_state|. It is similar to the Init() method above, however, it will
// not create a new encryption key if it is not present in the |local_state|.
- static OSCrypt::InitResult
- InitWithExistingKey(PrefService* local_state);
+ OSCrypt::InitResult InitWithExistingKey(PrefService* local_state);
#endif
#if BUILDFLAG(IS_APPLE)
// For unit testing purposes we instruct the Encryptor to use a mock Keychain
// on the Mac. The default is to use the real Keychain. Use OSCryptMocker,
// instead of calling this method directly.
- static void UseMockKeychainForTesting(
- bool use_mock);
+ void UseMockKeychainForTesting(bool use_mock);
// When Keychain is locked, it's not possible to get the encryption key. This
// is used only for testing purposes. Enabling locked Keychain also enables
// mock Keychain. Use OSCryptMocker, instead of calling this method directly.
- static void UseLockedMockKeychainForTesting(
- bool use_locked);
+ void UseLockedMockKeychainForTesting(bool use_locked);
#endif
// Get the raw encryption key to be used for all AES encryption. The result
@@ -176,46 +179,111 @@ class OSCryptImpl {
// - key generation error
// - if a hardcoded password is used instead of a random per-user key
// This method is thread-safe.
- static std::string GetRawEncryptionKey();
+ std::string GetRawEncryptionKey();
// Set the raw encryption key to be used for all AES encryption.
// On platforms that may use a hardcoded key, |key| can be empty and
// OSCryptImpl will default to the hardcoded key. This method is thread-safe.
- static void SetRawEncryptionKey(
- const std::string& key);
+ void SetRawEncryptionKey(const std::string& key);
#if BUILDFLAG(IS_WIN)
// For unit testing purposes we instruct the Encryptor to use a mock Key. The
// default is to use the real Key bound to profile. Use OSCryptMocker, instead
// of calling this method directly.
- static void UseMockKeyForTesting(bool use_mock);
+ void UseMockKeyForTesting(bool use_mock);
// For unit testing purposes, encrypt data using the older DPAPI method rather
// than using a session key.
- static void SetLegacyEncryptionForTesting(
- bool legacy);
+ void SetLegacyEncryptionForTesting(bool legacy);
// For unit testing purposes, reset the state of OSCryptImpl so a new key can
// be loaded via Init() or SetRawEncryptionkey().
- static void ResetStateForTesting();
+ void ResetStateForTesting();
#endif
-#if (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMECAST))
+#if (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
// For unit testing purposes, inject methods to be used.
- // |get_key_storage_mock| provides the desired |KeyStorage| implementation.
- // If the provider returns |nullptr|, a hardcoded password will be used.
- // If |get_key_storage_mock| is nullptr, restores the real implementation.
- static void UseMockKeyStorageForTesting(
- std::unique_ptr<KeyStorageLinux> (*get_key_storage_mock)());
+ // |storage_provider_factory| provides the desired |KeyStorage|
+ // implementation. If the provider returns |nullptr|, a hardcoded password
+ // will be used. If |storage_provider_factory| is null callback, restores the
+ // real implementation.
+ void UseMockKeyStorageForTesting(
+ base::OnceCallback<std::unique_ptr<KeyStorageLinux>()>
+ storage_provider_factory);
// Clears any caching and most lazy initialisations performed by the
// production code. Should be used after any test which required a password.
- static void ClearCacheForTesting();
+ void ClearCacheForTesting();
// Sets the password with which the encryption key is derived, e.g. "peanuts".
- static void SetEncryptionPasswordForTesting(
- const std::string& password);
-#endif // (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMECAST))
+ void SetEncryptionPasswordForTesting(const std::string& password);
+#endif // (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
+ private:
+#if BUILDFLAG(IS_APPLE)
+ // Generates a newly allocated SymmetricKey object based on the password found
+ // in the Keychain. The generated key is for AES encryption. Returns NULL
+ // key in the case password access is denied or key generation error occurs.
+ crypto::SymmetricKey* GetEncryptionKey();
+#endif // BUILDFLAG(IS_APPLE)
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)
+ // This lock is used to make the GetEncryptionKey and
+ // GetRawEncryptionKey methods thread-safe.
+ static base::Lock& GetLock();
+#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)
+
+#if BUILDFLAG(IS_LINUX)
+ // Create the KeyStorage. Will be null if no service is found. A Config must
+ // be set before every call to this method.
+ std::unique_ptr<KeyStorageLinux> CreateKeyStorage();
+
+ // Returns a cached string of "peanuts". Is thread-safe.
+ crypto::SymmetricKey* GetPasswordV10();
+
+ // Caches and returns the password from the KeyStorage or null if there is no
+ // service. Is thread-safe.
+ crypto::SymmetricKey* GetPasswordV11();
+
+ // For password_v10, nullptr means uninitialised.
+ std::unique_ptr<crypto::SymmetricKey> password_v10_cache_;
+
+ // For password_v11, nullptr means no backend.
+ std::unique_ptr<crypto::SymmetricKey> password_v11_cache_;
+
+ bool is_password_v11_cached_ = false;
+
+ // |config_| is used to initialise |password_v11_cache_| and then cleared.
+ std::unique_ptr<os_crypt::Config> config_;
+
+ base::OnceCallback<std::unique_ptr<KeyStorageLinux>()>
+ storage_provider_factory_;
+#endif // BUILDFLAG(IS_LINUX)
+
+#if BUILDFLAG(IS_WIN)
+ // Use mock key instead of a real encryption key. Used for testing.
+ bool use_mock_key_ = false;
+
+ // Store data using the legacy (DPAPI) method rather than session key.
+ bool use_legacy_ = false;
+
+ // Encryption Key. Set either by calling Init() or SetRawEncryptionKey().
+ std::string encryption_key_;
+
+ // Mock Encryption Key. Only set and used if use_mock_key_ is true.
+ std::string mock_encryption_key_;
+#endif // BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(IS_APPLE)
+ // true if |cached_encryption_key_| has been initialized.
+ bool key_is_cached_ = false;
+ // The cached AES encryption key.
+ std::unique_ptr<crypto::SymmetricKey> cached_encryption_key_;
+ // TODO(dhollowa): Refactor to allow dependency injection of Keychain.
+ bool use_mock_keychain_ = false;
+ // This flag is used to make the GetEncryptionKey method return NULL if used
+ // along with mock Keychain.
+ bool use_locked_mock_keychain_ = false;
+#endif
};
#endif // COMPONENTS_OS_CRYPT_OS_CRYPT_H_
diff --git a/chromium/components/os_crypt/os_crypt_linux.cc b/chromium/components/os_crypt/os_crypt_linux.cc
index 175723ee6b5..67688595e66 100644
--- a/chromium/components/os_crypt/os_crypt_linux.cc
+++ b/chromium/components/os_crypt/os_crypt_linux.cc
@@ -11,8 +11,9 @@
#include <memory>
#include "base/cxx17_backports.h"
-#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
@@ -45,37 +46,6 @@ constexpr size_t kIVBlockSizeAES128 = 16;
constexpr char kObfuscationPrefixV10[] = "v10";
constexpr char kObfuscationPrefixV11[] = "v11";
-// Everything in Cache may be leaked on shutdown.
-struct Cache {
- // For password_v10, null means uninitialised.
- std::unique_ptr<crypto::SymmetricKey> password_v10_cache;
- // For password_v11, null means no backend.
- std::unique_ptr<crypto::SymmetricKey> password_v11_cache;
- bool is_password_v11_cached = false;
- // |config| is used to initialise |password_v11_cache| and then cleared.
- std::unique_ptr<os_crypt::Config> config;
- // Guards access to |g_cache|, making lazy initialization of individual parts
- // thread safe.
- base::Lock lock;
-};
-
-base::LazyInstance<Cache>::Leaky g_cache = LAZY_INSTANCE_INITIALIZER;
-
-// Create the KeyStorage. Will be null if no service is found. A Config must be
-// set before every call to this function.
-std::unique_ptr<KeyStorageLinux> CreateKeyStorage() {
- CHECK(g_cache.Get().config);
- std::unique_ptr<KeyStorageLinux> key_storage =
- KeyStorageLinux::CreateService(*g_cache.Get().config);
- g_cache.Get().config.reset();
- return key_storage;
-}
-
-// Pointer to a function that creates and returns the |KeyStorage| instance to
-// be used. The function maintains ownership of the pointer.
-std::unique_ptr<KeyStorageLinux> (*g_key_storage_provider)() =
- &CreateKeyStorage;
-
// Generates a newly allocated SymmetricKey object based on a password.
// Ownership of the key is passed to the caller. Returns null key if a key
// generation error occurs.
@@ -93,80 +63,65 @@ std::unique_ptr<crypto::SymmetricKey> GenerateEncryptionKey(
return encryption_key;
}
-// Returns a cached string of "peanuts". Is thread-safe.
-crypto::SymmetricKey* GetPasswordV10() {
- base::AutoLock auto_lock(g_cache.Get().lock);
- if (!g_cache.Get().password_v10_cache.get()) {
- g_cache.Get().password_v10_cache = GenerateEncryptionKey("peanuts");
- }
- return g_cache.Get().password_v10_cache.get();
-}
-
-// Caches and returns the password from the KeyStorage or null if there is no
-// service. Is thread-safe.
-crypto::SymmetricKey* GetPasswordV11() {
- base::AutoLock auto_lock(g_cache.Get().lock);
- if (!g_cache.Get().is_password_v11_cached) {
- std::unique_ptr<KeyStorageLinux> key_storage = g_key_storage_provider();
- if (key_storage) {
- absl::optional<std::string> key = key_storage->GetKey();
- if (key.has_value()) {
- g_cache.Get().password_v11_cache = GenerateEncryptionKey(*key);
- }
- }
- g_cache.Get().is_password_v11_cached = true;
- }
- return g_cache.Get().password_v11_cache.get();
-}
-
} // namespace
namespace OSCrypt {
void SetConfig(std::unique_ptr<os_crypt::Config> config) {
- OSCryptImpl::SetConfig(std::move(config));
+ OSCryptImpl::GetInstance()->SetConfig(std::move(config));
}
bool EncryptString16(const std::u16string& plaintext, std::string* ciphertext) {
- return OSCryptImpl::EncryptString16(plaintext, ciphertext);
+ return OSCryptImpl::GetInstance()->EncryptString16(plaintext, ciphertext);
}
bool DecryptString16(const std::string& ciphertext, std::u16string* plaintext) {
- return OSCryptImpl::DecryptString16(ciphertext, plaintext);
+ return OSCryptImpl::GetInstance()->DecryptString16(ciphertext, plaintext);
}
bool EncryptString(const std::string& plaintext, std::string* ciphertext) {
- return OSCryptImpl::EncryptString(plaintext, ciphertext);
+ return OSCryptImpl::GetInstance()->EncryptString(plaintext, ciphertext);
}
bool DecryptString(const std::string& ciphertext, std::string* plaintext) {
- return OSCryptImpl::DecryptString(ciphertext, plaintext);
+ return OSCryptImpl::GetInstance()->DecryptString(ciphertext, plaintext);
}
std::string GetRawEncryptionKey() {
- return OSCryptImpl::GetRawEncryptionKey();
+ return OSCryptImpl::GetInstance()->GetRawEncryptionKey();
}
void SetRawEncryptionKey(const std::string& key) {
- OSCryptImpl::SetRawEncryptionKey(key);
+ OSCryptImpl::GetInstance()->SetRawEncryptionKey(key);
}
bool IsEncryptionAvailable() {
- return OSCryptImpl::IsEncryptionAvailable();
+ return OSCryptImpl::GetInstance()->IsEncryptionAvailable();
}
void UseMockKeyStorageForTesting(
- std::unique_ptr<KeyStorageLinux> (*get_key_storage_mock)()) {
- OSCryptImpl::UseMockKeyStorageForTesting(std::move(get_key_storage_mock));
+ base::OnceCallback<std::unique_ptr<KeyStorageLinux>()>
+ storage_provider_factory) {
+ OSCryptImpl::GetInstance()->UseMockKeyStorageForTesting(
+ std::move(storage_provider_factory));
}
void ClearCacheForTesting() {
- OSCryptImpl::ClearCacheForTesting();
+ OSCryptImpl::GetInstance()->ClearCacheForTesting();
}
void SetEncryptionPasswordForTesting(const std::string& password) {
- OSCryptImpl::SetEncryptionPasswordForTesting(password);
+ OSCryptImpl::GetInstance()->SetEncryptionPasswordForTesting(password);
}
} // namespace OSCrypt
-// static
+OSCryptImpl* OSCryptImpl::GetInstance() {
+ return base::Singleton<OSCryptImpl,
+ base::LeakySingletonTraits<OSCryptImpl>>::get();
+}
+
+OSCryptImpl::OSCryptImpl()
+ : storage_provider_factory_(base::BindOnce(&OSCryptImpl::CreateKeyStorage,
+ base::Unretained(this))) {}
+
+OSCryptImpl::~OSCryptImpl() = default;
+
bool OSCryptImpl::EncryptString16(const std::u16string& plaintext,
- std::string* ciphertext) {
+ std::string* ciphertext) {
return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext);
}
-// static
bool OSCryptImpl::DecryptString16(const std::string& ciphertext,
- std::u16string* plaintext) {
+ std::u16string* plaintext) {
std::string utf8;
if (!DecryptString(ciphertext, &utf8))
return false;
@@ -175,9 +130,8 @@ bool OSCryptImpl::DecryptString16(const std::string& ciphertext,
return true;
}
-// static
bool OSCryptImpl::EncryptString(const std::string& plaintext,
- std::string* ciphertext) {
+ std::string* ciphertext) {
if (plaintext.empty()) {
ciphertext->clear();
return true;
@@ -208,9 +162,8 @@ bool OSCryptImpl::EncryptString(const std::string& plaintext,
return true;
}
-// static
bool OSCryptImpl::DecryptString(const std::string& ciphertext,
- std::string* plaintext) {
+ std::string* plaintext) {
if (ciphertext.empty()) {
plaintext->clear();
return true;
@@ -258,65 +211,103 @@ bool OSCryptImpl::DecryptString(const std::string& ciphertext,
return true;
}
-// static
void OSCryptImpl::SetConfig(std::unique_ptr<os_crypt::Config> config) {
// Setting initialisation parameters makes no sense after initializing.
- DCHECK(!g_cache.Get().is_password_v11_cached);
- g_cache.Get().config = std::move(config);
+ DCHECK(!is_password_v11_cached_);
+ config_ = std::move(config);
}
-// static
bool OSCryptImpl::IsEncryptionAvailable() {
return GetPasswordV11();
}
-// static
void OSCryptImpl::SetRawEncryptionKey(const std::string& raw_key) {
- base::AutoLock auto_lock(g_cache.Get().lock);
+ base::AutoLock auto_lock(OSCryptImpl::GetLock());
// Check if the v11 password is already cached. If it is, then data encrypted
// with the old password might not be decryptable.
- DCHECK(!g_cache.Get().is_password_v11_cached);
+ DCHECK(!is_password_v11_cached_);
// The config won't be used if this function is being called. Callers should
// choose between setting a config and setting a raw encryption key.
- DCHECK(!g_cache.Get().config);
+ DCHECK(!config_);
if (!raw_key.empty()) {
- g_cache.Get().password_v11_cache =
+ password_v11_cache_ =
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key);
}
- // Always set |is_password_v11_cached|, even if given an empty string.
+ // Always set |is_password_v11_cached_|, even if given an empty string.
// Note that |raw_key| can be an empty string if real V11 encryption is not
- // available, and setting |is_password_v11_cached| causes GetPasswordV11() to
+ // available, and setting |is_password_v11_cached_| causes GetPasswordV11() to
// correctly return nullptr in that case.
- g_cache.Get().is_password_v11_cached = true;
+ is_password_v11_cached_ = true;
}
-// static
std::string OSCryptImpl::GetRawEncryptionKey() {
if (crypto::SymmetricKey* key = GetPasswordV11())
return key->key();
return std::string();
}
-// static
void OSCryptImpl::ClearCacheForTesting() {
- g_cache.Get().password_v10_cache.reset();
- g_cache.Get().password_v11_cache.reset();
- g_cache.Get().is_password_v11_cached = false;
- g_cache.Get().config.reset();
+ password_v10_cache_.reset();
+ password_v11_cache_.reset();
+ is_password_v11_cached_ = false;
+ config_.reset();
}
-// static
void OSCryptImpl::UseMockKeyStorageForTesting(
- std::unique_ptr<KeyStorageLinux> (*get_key_storage_mock)()) {
- if (get_key_storage_mock)
- g_key_storage_provider = get_key_storage_mock;
+ base::OnceCallback<std::unique_ptr<KeyStorageLinux>()>
+ storage_provider_factory) {
+ if (storage_provider_factory)
+ storage_provider_factory_ = std::move(storage_provider_factory);
else
- g_key_storage_provider = &CreateKeyStorage;
+ storage_provider_factory_ =
+ base::BindOnce(&OSCryptImpl::CreateKeyStorage, base::Unretained(this));
+}
+
+// Create the KeyStorage. Will be null if no service is found. A Config must be
+// set before every call to this function.
+std::unique_ptr<KeyStorageLinux> OSCryptImpl::CreateKeyStorage() {
+ CHECK(config_);
+ std::unique_ptr<KeyStorageLinux> key_storage =
+ KeyStorageLinux::CreateService(*config_);
+ config_.reset();
+ return key_storage;
}
-// static
void OSCryptImpl::SetEncryptionPasswordForTesting(const std::string& password) {
ClearCacheForTesting(); // IN-TEST
- g_cache.Get().password_v11_cache = GenerateEncryptionKey(password);
- g_cache.Get().is_password_v11_cached = true;
+ password_v11_cache_ = GenerateEncryptionKey(password);
+ is_password_v11_cached_ = true;
+}
+
+// Returns a cached string of "peanuts". Is thread-safe.
+crypto::SymmetricKey* OSCryptImpl::GetPasswordV10() {
+ base::AutoLock auto_lock(OSCryptImpl::GetLock());
+ if (!password_v10_cache_.get()) {
+ password_v10_cache_ = GenerateEncryptionKey("peanuts");
+ }
+ return password_v10_cache_.get();
+}
+
+// Caches and returns the password from the KeyStorage or null if there is no
+// service. Is thread-safe.
+crypto::SymmetricKey* OSCryptImpl::GetPasswordV11() {
+ base::AutoLock auto_lock(OSCryptImpl::GetLock());
+ if (!is_password_v11_cached_) {
+ std::unique_ptr<KeyStorageLinux> key_storage =
+ std::move(storage_provider_factory_).Run();
+ if (key_storage) {
+ absl::optional<std::string> key = key_storage->GetKey();
+ if (key.has_value()) {
+ password_v11_cache_ = GenerateEncryptionKey(*key);
+ }
+ }
+ is_password_v11_cached_ = true;
+ }
+ return password_v11_cache_.get();
+}
+
+// static
+base::Lock& OSCryptImpl::GetLock() {
+ static base::NoDestructor<base::Lock> os_crypt_lock;
+ return *os_crypt_lock;
}
diff --git a/chromium/components/os_crypt/os_crypt_linux_unittest.cc b/chromium/components/os_crypt/os_crypt_linux_unittest.cc
index b75c9f726b3..604dbe0d267 100644
--- a/chromium/components/os_crypt/os_crypt_linux_unittest.cc
+++ b/chromium/components/os_crypt/os_crypt_linux_unittest.cc
@@ -4,6 +4,7 @@
#include <string>
+#include "base/bind.h"
#include "components/os_crypt/key_storage_linux.h"
#include "components/os_crypt/os_crypt.h"
#include "components/os_crypt/os_crypt_mocker_linux.h"
@@ -72,7 +73,7 @@ TEST_F(OSCryptLinuxTest, IsEncryptionAvailable) {
EXPECT_TRUE(OSCrypt::IsEncryptionAvailable());
OSCrypt::ClearCacheForTesting();
// Mock the GetKeyStorage function.
- OSCrypt::UseMockKeyStorageForTesting(GetNullKeyStorage);
+ OSCrypt::UseMockKeyStorageForTesting(base::BindOnce(&GetNullKeyStorage));
EXPECT_FALSE(OSCrypt::IsEncryptionAvailable());
}
diff --git a/chromium/components/os_crypt/os_crypt_mac.mm b/chromium/components/os_crypt/os_crypt_mac.mm
index 0d59444f99f..de92559dd28 100644
--- a/chromium/components/os_crypt/os_crypt_mac.mm
+++ b/chromium/components/os_crypt/os_crypt_mac.mm
@@ -11,6 +11,8 @@
#include "base/debug/leak_annotations.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "build/build_config.h"
@@ -38,46 +40,70 @@ constexpr size_t kDerivedKeySizeInBits = 128;
// Constant for Symmetic key derivation.
constexpr size_t kEncryptionIterations = 1003;
-// TODO(dhollowa): Refactor to allow dependency injection of Keychain.
-bool use_mock_keychain = false;
-
-// This flag is used to make the GetEncryptionKey method return NULL if used
-// along with mock Keychain.
-bool use_locked_mock_keychain = false;
-
// Prefix for cypher text returned by current encryption version. We prefix
// the cypher text with this string so that future data migration can detect
// this and migrate to different encryption without data loss.
constexpr char kEncryptionVersionPrefix[] = "v10";
-// This lock is used to make the GetEncrytionKey and
-// OSCrypt::GetRawEncryptionKey methods thread-safe.
-base::LazyInstance<base::Lock>::Leaky g_lock = LAZY_INSTANCE_INITIALIZER;
+} // namespace
+
+namespace OSCrypt {
+bool EncryptString16(const std::u16string& plaintext, std::string* ciphertext) {
+ return OSCryptImpl::GetInstance()->EncryptString16(plaintext, ciphertext);
+}
+bool DecryptString16(const std::string& ciphertext, std::u16string* plaintext) {
+ return OSCryptImpl::GetInstance()->DecryptString16(ciphertext, plaintext);
+}
+bool EncryptString(const std::string& plaintext, std::string* ciphertext) {
+ return OSCryptImpl::GetInstance()->EncryptString(plaintext, ciphertext);
+}
+bool DecryptString(const std::string& ciphertext, std::string* plaintext) {
+ return OSCryptImpl::GetInstance()->DecryptString(ciphertext, plaintext);
+}
+void UseMockKeychainForTesting(bool use_mock) {
+ OSCryptImpl::GetInstance()->UseMockKeychainForTesting(use_mock);
+}
+void UseLockedMockKeychainForTesting(bool use_locked) {
+ OSCryptImpl::GetInstance()->UseLockedMockKeychainForTesting(use_locked);
+}
+std::string GetRawEncryptionKey() {
+ return OSCryptImpl::GetInstance()->GetRawEncryptionKey();
+}
+void SetRawEncryptionKey(const std::string& key) {
+ OSCryptImpl::GetInstance()->SetRawEncryptionKey(key);
+}
+bool IsEncryptionAvailable() {
+ return OSCryptImpl::GetInstance()->IsEncryptionAvailable();
+}
+} // namespace OSCrypt
-// The cached AES encryption key singleton.
-crypto::SymmetricKey* g_cached_encryption_key = nullptr;
+// static
+OSCryptImpl* OSCryptImpl::GetInstance() {
+ return base::Singleton<OSCryptImpl,
+ base::LeakySingletonTraits<OSCryptImpl>>::get();
+}
-// true if |g_cached_encryption_key| has been initialized.
-bool g_key_is_cached = false;
+OSCryptImpl::OSCryptImpl() = default;
+OSCryptImpl::~OSCryptImpl() = default;
// Generates a newly allocated SymmetricKey object based on the password found
// in the Keychain. The generated key is for AES encryption. Returns NULL key
// in the case password access is denied or key generation error occurs.
-crypto::SymmetricKey* GetEncryptionKey() {
- base::AutoLock auto_lock(g_lock.Get());
+crypto::SymmetricKey* OSCryptImpl::GetEncryptionKey() {
+ base::AutoLock auto_lock(OSCryptImpl::GetLock());
- if (use_mock_keychain && use_locked_mock_keychain)
+ if (use_mock_keychain_ && use_locked_mock_keychain_)
return nullptr;
- if (g_key_is_cached)
- return g_cached_encryption_key;
+ if (key_is_cached_)
+ return cached_encryption_key_.get();
- static bool mock_keychain_command_line_flag =
+ const bool mock_keychain_command_line_flag =
base::CommandLine::ForCurrentProcess()->HasSwitch(
os_crypt::switches::kUseMockKeychain);
std::string password;
- if (use_mock_keychain || mock_keychain_command_line_flag) {
+ if (use_mock_keychain_ || mock_keychain_command_line_flag) {
crypto::MockAppleKeychain keychain;
password = keychain.GetEncryptionPassword();
} else {
@@ -86,75 +112,33 @@ crypto::SymmetricKey* GetEncryptionKey() {
password = encryptor_password.GetPassword();
}
- // Subsequent code must guarantee that the correct key is cached before
- // returning.
- g_key_is_cached = true;
-
+ key_is_cached_ = true;
if (password.empty())
- return g_cached_encryption_key;
+ return cached_encryption_key_.get();
const std::string salt(kSalt);
- // Create an encryption key from our password and salt. The key is
- // intentionally leaked.
- g_cached_encryption_key =
+ // Create an encryption key from our password and salt.
+ cached_encryption_key_ =
crypto::SymmetricKey::DeriveKeyFromPasswordUsingPbkdf2(
crypto::SymmetricKey::AES, password, salt, kEncryptionIterations,
- kDerivedKeySizeInBits)
- .release();
- ANNOTATE_LEAKING_OBJECT_PTR(g_cached_encryption_key);
- DCHECK(g_cached_encryption_key);
- return g_cached_encryption_key;
-}
-
-} // namespace
-
-namespace OSCrypt {
-bool EncryptString16(const std::u16string& plaintext, std::string* ciphertext) {
- return OSCryptImpl::EncryptString16(plaintext, ciphertext);
-}
-bool DecryptString16(const std::string& ciphertext, std::u16string* plaintext) {
- return OSCryptImpl::DecryptString16(ciphertext, plaintext);
-}
-bool EncryptString(const std::string& plaintext, std::string* ciphertext) {
- return OSCryptImpl::EncryptString(plaintext, ciphertext);
-}
-bool DecryptString(const std::string& ciphertext, std::string* plaintext) {
- return OSCryptImpl::DecryptString(ciphertext, plaintext);
-}
-void UseMockKeychainForTesting(bool use_mock) {
- OSCryptImpl::UseMockKeychainForTesting(use_mock);
-}
-void UseLockedMockKeychainForTesting(bool use_locked) {
- OSCryptImpl::UseLockedMockKeychainForTesting(use_locked);
-}
-std::string GetRawEncryptionKey() {
- return OSCryptImpl::GetRawEncryptionKey();
-}
-void SetRawEncryptionKey(const std::string& key) {
- OSCryptImpl::SetRawEncryptionKey(key);
-}
-bool IsEncryptionAvailable() {
- return OSCryptImpl::IsEncryptionAvailable();
+ kDerivedKeySizeInBits);
+ DCHECK(cached_encryption_key_);
+ return cached_encryption_key_.get();
}
-} // namespace OSCrypt
-// static
std::string OSCryptImpl::GetRawEncryptionKey() {
if (crypto::SymmetricKey* key = GetEncryptionKey())
return key->key();
return std::string();
}
-// static
void OSCryptImpl::SetRawEncryptionKey(const std::string& raw_key) {
- base::AutoLock auto_lock(g_lock.Get());
- DCHECK(!g_key_is_cached) << "Encryption key already set.";
- if (!raw_key.empty()) {
- auto key = crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key);
- g_cached_encryption_key = key.release();
- }
- g_key_is_cached = true;
+ base::AutoLock auto_lock(OSCryptImpl::GetLock());
+ DCHECK(!cached_encryption_key_) << "Encryption key already set.";
+ cached_encryption_key_ =
+ crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key);
+ key_is_cached_ = true;
}
bool OSCryptImpl::EncryptString16(const std::u16string& plaintext,
@@ -241,13 +225,19 @@ bool OSCryptImpl::IsEncryptionAvailable() {
}
void OSCryptImpl::UseMockKeychainForTesting(bool use_mock) {
- use_mock_keychain = use_mock;
- if (!use_mock_keychain)
- use_locked_mock_keychain = false;
+ use_mock_keychain_ = use_mock;
+ if (!use_mock_keychain_)
+ use_locked_mock_keychain_ = false;
}
void OSCryptImpl::UseLockedMockKeychainForTesting(bool use_locked) {
- use_locked_mock_keychain = use_locked;
- if (use_locked_mock_keychain)
- use_mock_keychain = true;
+ use_locked_mock_keychain_ = use_locked;
+ if (use_locked_mock_keychain_)
+ use_mock_keychain_ = true;
+}
+
+// static
+base::Lock& OSCryptImpl::GetLock() {
+ static base::NoDestructor<base::Lock> os_crypt_lock;
+ return *os_crypt_lock;
}
diff --git a/chromium/components/os_crypt/os_crypt_mocker_linux.cc b/chromium/components/os_crypt/os_crypt_mocker_linux.cc
index 01ab41d7031..224b6a5b0c5 100644
--- a/chromium/components/os_crypt/os_crypt_mocker_linux.cc
+++ b/chromium/components/os_crypt/os_crypt_mocker_linux.cc
@@ -7,6 +7,7 @@
#include <memory>
#include "base/base64.h"
+#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/rand_util.h"
#include "components/os_crypt/key_storage_config_linux.h"
@@ -30,12 +31,12 @@ std::string* OSCryptMockerLinux::GetKeyPtr() {
// static
void OSCryptMockerLinux::SetUp() {
- OSCrypt::UseMockKeyStorageForTesting(&CreateNewMock);
+ OSCrypt::UseMockKeyStorageForTesting(base::BindOnce(&CreateNewMock));
}
// static
void OSCryptMockerLinux::TearDown() {
- OSCrypt::UseMockKeyStorageForTesting(nullptr);
+ OSCrypt::UseMockKeyStorageForTesting(base::NullCallback());
OSCrypt::ClearCacheForTesting();
}
diff --git a/chromium/components/os_crypt/os_crypt_win.cc b/chromium/components/os_crypt/os_crypt_win.cc
index 91f2c6d4f79..305a8caa4eb 100644
--- a/chromium/components/os_crypt/os_crypt_win.cc
+++ b/chromium/components/os_crypt/os_crypt_win.cc
@@ -6,8 +6,8 @@
#include "base/base64.h"
#include "base/logging.h"
+#include "base/memory/singleton.h"
#include "base/metrics/histogram_functions.h"
-#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/wincrypt_shim.h"
@@ -35,27 +35,6 @@ constexpr char kEncryptionVersionPrefix[] = "v10";
// Key prefix for a key encrypted with DPAPI.
constexpr char kDPAPIKeyPrefix[] = "DPAPI";
-// Use mock key instead of a real encryption key. Used for testing.
-bool g_use_mock_key = false;
-
-// Store data using the legacy (DPAPI) method rather than session key.
-bool g_use_legacy = false;
-
-// These two keys must have no destructors to allow OSCrypt calls to function
-// correctly during shutdown.
-
-// Encryption Key. Set either by calling Init() or SetRawEncryptionKey().
-std::string& GetEncryptionKeyFactory() {
- static base::NoDestructor<std::string> encryption_key;
- return *encryption_key;
-}
-
-// Mock Encryption Key. Only set and used if g_use_mock_key is true.
-std::string& GetMockEncryptionKeyFactory() {
- static base::NoDestructor<std::string> mock_encryption_key;
- return *mock_encryption_key;
-}
-
bool EncryptStringWithDPAPI(const std::string& plaintext,
std::string* ciphertext) {
DATA_BLOB input;
@@ -98,72 +77,63 @@ bool DecryptStringWithDPAPI(const std::string& ciphertext,
LocalFree(output.pbData);
return true;
}
-
-const std::string& GetEncryptionKeyInternal() {
- if (g_use_mock_key) {
- if (GetMockEncryptionKeyFactory().empty())
- GetMockEncryptionKeyFactory().assign(
- crypto::HkdfSha256("peanuts", "salt", "info", kKeyLength));
- DCHECK(!GetMockEncryptionKeyFactory().empty())
- << "Failed to initialize mock key.";
- return GetMockEncryptionKeyFactory();
- }
-
- DCHECK(!GetEncryptionKeyFactory().empty()) << "No key.";
- return GetEncryptionKeyFactory();
-}
-
} // namespace
namespace OSCrypt {
bool EncryptString16(const std::u16string& plaintext, std::string* ciphertext) {
- return OSCryptImpl::EncryptString16(plaintext, ciphertext);
+ return OSCryptImpl::GetInstance()->EncryptString16(plaintext, ciphertext);
}
bool DecryptString16(const std::string& ciphertext, std::u16string* plaintext) {
- return OSCryptImpl::DecryptString16(ciphertext, plaintext);
+ return OSCryptImpl::GetInstance()->DecryptString16(ciphertext, plaintext);
}
bool EncryptString(const std::string& plaintext, std::string* ciphertext) {
- return OSCryptImpl::EncryptString(plaintext, ciphertext);
+ return OSCryptImpl::GetInstance()->EncryptString(plaintext, ciphertext);
}
bool DecryptString(const std::string& ciphertext, std::string* plaintext) {
- return OSCryptImpl::DecryptString(ciphertext, plaintext);
+ return OSCryptImpl::GetInstance()->DecryptString(ciphertext, plaintext);
}
void RegisterLocalPrefs(PrefRegistrySimple* registry) {
OSCryptImpl::RegisterLocalPrefs(registry);
}
InitResult InitWithExistingKey(PrefService* local_state) {
- return OSCryptImpl::InitWithExistingKey(local_state);
+ return OSCryptImpl::GetInstance()->InitWithExistingKey(local_state);
}
bool Init(PrefService* local_state) {
- return OSCryptImpl::Init(local_state);
+ return OSCryptImpl::GetInstance()->Init(local_state);
}
std::string GetRawEncryptionKey() {
- return OSCryptImpl::GetRawEncryptionKey();
+ return OSCryptImpl::GetInstance()->GetRawEncryptionKey();
}
void SetRawEncryptionKey(const std::string& key) {
- OSCryptImpl::SetRawEncryptionKey(key);
+ OSCryptImpl::GetInstance()->SetRawEncryptionKey(key);
}
bool IsEncryptionAvailable() {
- return OSCryptImpl::IsEncryptionAvailable();
+ return OSCryptImpl::GetInstance()->IsEncryptionAvailable();
}
void UseMockKeyForTesting(bool use_mock) {
- OSCryptImpl::UseMockKeyForTesting(use_mock);
+ OSCryptImpl::GetInstance()->UseMockKeyForTesting(use_mock);
}
void SetLegacyEncryptionForTesting(bool legacy) {
- OSCryptImpl::SetLegacyEncryptionForTesting(legacy);
+ OSCryptImpl::GetInstance()->SetLegacyEncryptionForTesting(legacy);
}
void ResetStateForTesting() {
- OSCryptImpl::ResetStateForTesting();
+ OSCryptImpl::GetInstance()->ResetStateForTesting();
}
} // namespace OSCrypt
-// static
+OSCryptImpl::OSCryptImpl() = default;
+OSCryptImpl::~OSCryptImpl() = default;
+
+OSCryptImpl* OSCryptImpl::GetInstance() {
+ return base::Singleton<OSCryptImpl,
+ base::LeakySingletonTraits<OSCryptImpl>>::get();
+}
+
bool OSCryptImpl::EncryptString16(const std::u16string& plaintext,
std::string* ciphertext) {
return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext);
}
-// static
bool OSCryptImpl::DecryptString16(const std::string& ciphertext,
std::u16string* plaintext) {
std::string utf8;
@@ -174,15 +144,14 @@ bool OSCryptImpl::DecryptString16(const std::string& ciphertext,
return true;
}
-// static
bool OSCryptImpl::EncryptString(const std::string& plaintext,
std::string* ciphertext) {
- if (g_use_legacy)
+ if (use_legacy_)
return EncryptStringWithDPAPI(plaintext, ciphertext);
crypto::Aead aead(crypto::Aead::AES_256_GCM);
- const auto key = GetEncryptionKeyInternal();
+ const auto key = GetRawEncryptionKey();
aead.Init(&key);
// Note: can only check these once AEAD is initialized.
@@ -200,7 +169,6 @@ bool OSCryptImpl::EncryptString(const std::string& plaintext,
return true;
}
-// static
bool OSCryptImpl::DecryptString(const std::string& ciphertext,
std::string* plaintext) {
if (!base::StartsWith(ciphertext, kEncryptionVersionPrefix,
@@ -209,7 +177,7 @@ bool OSCryptImpl::DecryptString(const std::string& ciphertext,
crypto::Aead aead(crypto::Aead::AES_256_GCM);
- auto key = GetEncryptionKeyInternal();
+ const auto key = GetRawEncryptionKey();
aead.Init(&key);
// Obtain the nonce.
@@ -227,7 +195,6 @@ void OSCryptImpl::RegisterLocalPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(kOsCryptEncryptedKeyPrefName, "");
}
-// static
bool OSCryptImpl::Init(PrefService* local_state) {
// Try to pull the key from the local state.
switch (InitWithExistingKey(local_state)) {
@@ -255,13 +222,12 @@ bool OSCryptImpl::Init(PrefService* local_state) {
std::string base64_key;
base::Base64Encode(encrypted_key, &base64_key);
local_state->SetString(kOsCryptEncryptedKeyPrefName, base64_key);
- GetEncryptionKeyFactory().assign(key);
+ encryption_key_.assign(key);
return true;
}
-// static
OSCrypt::InitResult OSCryptImpl::InitWithExistingKey(PrefService* local_state) {
- DCHECK(GetEncryptionKeyFactory().empty()) << "Key already exists.";
+ DCHECK(encryption_key_.empty()) << "Key already exists.";
// Try and pull the key from the local state.
if (!local_state->HasPrefPath(kOsCryptEncryptedKeyPrefName))
return OSCrypt::kKeyDoesNotExist;
@@ -289,42 +255,45 @@ OSCrypt::InitResult OSCryptImpl::InitWithExistingKey(PrefService* local_state) {
return OSCrypt::kDecryptionFailed;
}
- GetEncryptionKeyFactory().assign(key);
+ encryption_key_.assign(key);
return OSCrypt::kSuccess;
}
-// static
void OSCryptImpl::SetRawEncryptionKey(const std::string& raw_key) {
- DCHECK(!g_use_mock_key) << "Mock key in use.";
+ DCHECK(!use_mock_key_) << "Mock key in use.";
DCHECK(!raw_key.empty()) << "Bad key.";
- DCHECK(GetEncryptionKeyFactory().empty()) << "Key already set.";
- GetEncryptionKeyFactory().assign(raw_key);
+ DCHECK(encryption_key_.empty()) << "Key already set.";
+ encryption_key_.assign(raw_key);
}
-// static
std::string OSCryptImpl::GetRawEncryptionKey() {
- return GetEncryptionKeyInternal();
+ if (use_mock_key_) {
+ if (mock_encryption_key_.empty())
+ mock_encryption_key_.assign(
+ crypto::HkdfSha256("peanuts", "salt", "info", kKeyLength));
+ DCHECK(!mock_encryption_key_.empty()) << "Failed to initialize mock key.";
+ return mock_encryption_key_;
+ }
+
+ DCHECK(!encryption_key_.empty()) << "No key.";
+ return encryption_key_;
}
-// static
bool OSCryptImpl::IsEncryptionAvailable() {
- return !GetEncryptionKeyFactory().empty();
+ return !encryption_key_.empty();
}
-// static
void OSCryptImpl::UseMockKeyForTesting(bool use_mock) {
- g_use_mock_key = use_mock;
+ use_mock_key_ = use_mock;
}
-// static
void OSCryptImpl::SetLegacyEncryptionForTesting(bool legacy) {
- g_use_legacy = legacy;
+ use_legacy_ = legacy;
}
-// static
void OSCryptImpl::ResetStateForTesting() {
- g_use_legacy = false;
- g_use_mock_key = false;
- GetEncryptionKeyFactory().clear();
- GetMockEncryptionKeyFactory().clear();
+ use_legacy_ = false;
+ use_mock_key_ = false;
+ encryption_key_.clear();
+ mock_encryption_key_.clear();
}
diff --git a/chromium/components/ownership/BUILD.gn b/chromium/components/ownership/BUILD.gn
index 1c2d554e85f..0b2634d26eb 100644
--- a/chromium/components/ownership/BUILD.gn
+++ b/chromium/components/ownership/BUILD.gn
@@ -34,7 +34,7 @@ if (is_chromeos_ash) {
]
if (use_nss_certs) {
- public_deps += [ "//crypto:platform" ]
+ public_configs = [ "//build/config/linux/nss" ]
}
}
diff --git a/chromium/components/page_info/DEPS b/chromium/components/page_info/DEPS
index 4907b521f5e..24f8fd78bb6 100644
--- a/chromium/components/page_info/DEPS
+++ b/chromium/components/page_info/DEPS
@@ -27,6 +27,7 @@ include_rules = [
"+components/vector_icons",
"+content/public",
"+media/base",
+ "+net",
"+net/cert",
"+net/ssl",
"+ppapi",
diff --git a/chromium/components/page_info/android/BUILD.gn b/chromium/components/page_info/android/BUILD.gn
index c5e0d7fdfd8..5ba0df8b935 100644
--- a/chromium/components/page_info/android/BUILD.gn
+++ b/chromium/components/page_info/android/BUILD.gn
@@ -41,7 +41,6 @@ android_resources("java_resources") {
"java/res/layout/page_info_container.xml",
"java/res/layout/page_info_row.xml",
"java/res/layout/page_info_summary.xml",
- "java/res/layout/page_zoom_view.xml",
"java/res/values-night/dimens.xml",
"java/res/values/colors.xml",
"java/res/values/dimens.xml",
@@ -76,8 +75,6 @@ android_library("java") {
"java/src/org/chromium/components/page_info/PageInfoFeatures.java",
"java/src/org/chromium/components/page_info/PageInfoHighlight.java",
"java/src/org/chromium/components/page_info/PageInfoMainController.java",
- "java/src/org/chromium/components/page_info/PageInfoPageZoomController.java",
- "java/src/org/chromium/components/page_info/PageInfoPageZoomView.java",
"java/src/org/chromium/components/page_info/PageInfoPermissionsController.java",
"java/src/org/chromium/components/page_info/PageInfoPreferenceSubpageController.java",
"java/src/org/chromium/components/page_info/PageInfoRowView.java",
@@ -92,6 +89,8 @@ android_library("java") {
":java_resources",
":page_info_action_enum_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/settings/android:java",
"//components/browser_ui/site_settings/android:java",
"//components/browser_ui/styles/android:java",
@@ -110,8 +109,11 @@ android_library("java") {
"//components/url_formatter/android:url_formatter_java",
"//content/public/android:content_java",
"//services/device/public/java:device_feature_list_java",
- "//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/androidx:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_java",
+ "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+ "//third_party/androidx:androidx_core_core_java",
+ "//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/androidx:androidx_preference_preference_java",
"//ui/android:ui_java",
"//url:gurl_java",
diff --git a/chromium/components/page_info/core/BUILD.gn b/chromium/components/page_info/core/BUILD.gn
index a1c64c0cbeb..e9f429b0f95 100644
--- a/chromium/components/page_info/core/BUILD.gn
+++ b/chromium/components/page_info/core/BUILD.gn
@@ -41,6 +41,7 @@ static_library("core") {
"//components/optimization_guide/core",
"//components/page_info/core:proto",
"//components/strings:components_strings_grit",
+ "//net:net",
"//services/metrics/public/cpp:metrics_cpp",
"//services/metrics/public/cpp:ukm_builders",
"//ui/base:base",
diff --git a/chromium/components/page_info/core/about_this_site_service.cc b/chromium/components/page_info/core/about_this_site_service.cc
index 4169f785d33..9c2accda5e8 100644
--- a/chromium/components/page_info/core/about_this_site_service.cc
+++ b/chromium/components/page_info/core/about_this_site_service.cc
@@ -10,6 +10,7 @@
#include "components/page_info/core/about_this_site_validation.h"
#include "components/page_info/core/features.h"
#include "components/page_info/core/proto/about_this_site_metadata.pb.h"
+#include "net/base/url_util.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "url/gurl.h"
@@ -53,6 +54,16 @@ absl::optional<proto::SiteInfo> AboutThisSiteService::GetAboutThisSiteInfo(
.SetStatus(static_cast<int>(status))
.Record(ukm::UkmRecorder::Get());
if (status == AboutThisSiteStatus::kValid) {
+ if (about_this_site_metadata->site_info().has_more_about()) {
+ // Append a context parameter to identify that this URL is visited from
+ // Chrome. If we add more UI surfaces that can open this URL, we should
+ // pass in different context parameters.
+ proto::MoreAbout* more_about =
+ about_this_site_metadata->mutable_site_info()->mutable_more_about();
+ GURL more_about_url =
+ net::AppendQueryParameter(GURL(more_about->url()), "ctx", "chrome");
+ more_about->set_url(more_about_url.spec());
+ }
return about_this_site_metadata->site_info();
}
@@ -60,15 +71,21 @@ absl::optional<proto::SiteInfo> AboutThisSiteService::GetAboutThisSiteInfo(
page_info::proto::SiteInfo site_info;
if (url == GURL("https://example.com")) {
auto* description = site_info.mutable_description();
+ description->set_name("Example website");
+ description->set_subtitle("Website");
description->set_description(
"A domain used in illustrative examples in documents.");
description->mutable_source()->set_url("https://example.com");
description->mutable_source()->set_label("Example source");
+ site_info.mutable_more_about()->set_url(
+ "https://example.com/#more-about");
return site_info;
}
if (url == GURL("https://permission.site")) {
auto* description = site_info.mutable_description();
+ description->set_name("Permission Site");
+ description->set_subtitle("Testing site");
description->set_description(
"A site containing test buttons for various browser APIs, in order"
" to trigger permission dialogues and similar UI in modern "
diff --git a/chromium/components/page_info/core/about_this_site_service_unittest.cc b/chromium/components/page_info/core/about_this_site_service_unittest.cc
index c4e208c5ab7..e314dc3785e 100644
--- a/chromium/components/page_info/core/about_this_site_service_unittest.cc
+++ b/chromium/components/page_info/core/about_this_site_service_unittest.cc
@@ -8,6 +8,7 @@
#include "base/test/metrics/histogram_tester.h"
#include "components/optimization_guide/proto/common_types.pb.h"
#include "components/page_info/core/about_this_site_validation.h"
+#include "components/page_info/core/features.h"
#include "components/page_info/core/proto/about_this_site_metadata.pb.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
@@ -41,6 +42,8 @@ proto::AboutThisSiteMetadata CreateValidMetadata() {
description->set_name("Example");
description->mutable_source()->set_url("https://example.com");
description->mutable_source()->set_label("Example source");
+ metadata.mutable_site_info()->mutable_more_about()->set_url(
+ "https://google.com/ats/example.com");
return metadata;
}
@@ -105,10 +108,22 @@ TEST_F(AboutThisSiteServiceTest, ValidResponse) {
auto info = service()->GetAboutThisSiteInfo(
GURL("https://foo.com"), ukm::UkmRecorder::GetNewSourceID());
EXPECT_TRUE(info.has_value());
+ EXPECT_EQ(info->more_about().url(),
+ "https://google.com/ats/example.com?ctx=chrome");
t.ExpectUniqueSample("Security.PageInfo.AboutThisSiteStatus",
AboutThisSiteStatus::kValid, 1);
}
+// Tests the language specific feature check.
+TEST_F(AboutThisSiteServiceTest, FeatureCheck) {
+ EXPECT_TRUE(page_info::IsAboutThisSiteFeatureEnabled("en-US"));
+ EXPECT_TRUE(page_info::IsAboutThisSiteFeatureEnabled("en-GB"));
+ EXPECT_TRUE(page_info::IsAboutThisSiteFeatureEnabled("en"));
+
+ EXPECT_FALSE(page_info::IsAboutThisSiteFeatureEnabled("de-DE"));
+ EXPECT_FALSE(page_info::IsAboutThisSiteFeatureEnabled("de"));
+}
+
// Tests that incorrect proto messages are discarded.
TEST_F(AboutThisSiteServiceTest, InvalidResponse) {
base::HistogramTester t;
diff --git a/chromium/components/page_info/core/about_this_site_validation.cc b/chromium/components/page_info/core/about_this_site_validation.cc
index 8b97f9cae57..bba00d4db3d 100644
--- a/chromium/components/page_info/core/about_this_site_validation.cc
+++ b/chromium/components/page_info/core/about_this_site_validation.cc
@@ -4,6 +4,8 @@
#include "components/page_info/core/about_this_site_validation.h"
+#include "base/feature_list.h"
+#include "components/page_info/core/features.h"
#include "components/page_info/core/proto/about_this_site_metadata.pb.h"
#include "url/gurl.h"
@@ -55,6 +57,13 @@ AboutThisSiteStatus ValidateFirstSeen(const proto::SiteFirstSeen& first_seen) {
return AboutThisSiteStatus::kValid;
}
+AboutThisSiteStatus ValidateMoreAbout(const proto::MoreAbout& more_info) {
+ if (!more_info.has_url() || !GURL(more_info.url()).is_valid())
+ return AboutThisSiteStatus::kInvalidMoreAbout;
+
+ return AboutThisSiteStatus::kValid;
+}
+
AboutThisSiteStatus ValidateSiteInfo(const proto::SiteInfo& site_info) {
if (!site_info.has_description() && !site_info.has_first_seen())
return AboutThisSiteStatus::kEmptySiteInfo;
@@ -62,12 +71,26 @@ AboutThisSiteStatus ValidateSiteInfo(const proto::SiteInfo& site_info) {
AboutThisSiteStatus status = AboutThisSiteStatus::kValid;
if (!site_info.has_description())
return AboutThisSiteStatus::kMissingDescription;
+
status = ValidateDescription(site_info.description());
if (status != AboutThisSiteStatus::kValid)
return status;
if (site_info.has_first_seen())
status = ValidateFirstSeen(site_info.first_seen());
+
+ if (status != AboutThisSiteStatus::kValid)
+ return status;
+
+ // The kPageInfoAboutThisSiteMoreInfo requires a 'MoreAbout' URL.
+ if (base::FeatureList::IsEnabled(kPageInfoAboutThisSiteMoreInfo) &&
+ !site_info.has_more_about()) {
+ return AboutThisSiteStatus::kMissingMoreAbout;
+ }
+
+ if (site_info.has_more_about())
+ status = ValidateMoreAbout(site_info.more_about());
+
return status;
}
diff --git a/chromium/components/page_info/core/about_this_site_validation.h b/chromium/components/page_info/core/about_this_site_validation.h
index d810f0338d6..339b1133de7 100644
--- a/chromium/components/page_info/core/about_this_site_validation.h
+++ b/chromium/components/page_info/core/about_this_site_validation.h
@@ -31,8 +31,10 @@ enum class AboutThisSiteStatus {
kMissingDescriptionLang = 13,
kMissingDescriptionSource = 14,
kMissingBannerInfo = 15,
+ kInvalidMoreAbout = 16,
+ kMissingMoreAbout = 17,
- kMaxValue = kMissingBannerInfo,
+ kMaxValue = kMissingMoreAbout,
};
AboutThisSiteStatus ValidateMetadata(
@@ -42,6 +44,7 @@ AboutThisSiteStatus ValidateSource(const proto::Hyperlink& link);
AboutThisSiteStatus ValidateDescription(
const proto::SiteDescription& description);
AboutThisSiteStatus ValidateFirstSeen(const proto::SiteFirstSeen& first_seen);
+AboutThisSiteStatus ValidateMoreAbout(const proto::MoreAbout& banner_info);
AboutThisSiteStatus ValidateSiteInfo(const proto::SiteInfo& site_info);
AboutThisSiteStatus ValidateBannerInfo(const proto::BannerInfo& banner_info);
diff --git a/chromium/components/page_info/core/about_this_site_validation_unittest.cc b/chromium/components/page_info/core/about_this_site_validation_unittest.cc
index e28ac20a93d..1b3802b8a01 100644
--- a/chromium/components/page_info/core/about_this_site_validation_unittest.cc
+++ b/chromium/components/page_info/core/about_this_site_validation_unittest.cc
@@ -4,11 +4,12 @@
#include "components/page_info/core/about_this_site_validation.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/page_info/core/features.h"
#include "components/page_info/core/proto/about_this_site_metadata.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
-namespace page_info {
-namespace about_this_site_validation {
+namespace page_info::about_this_site_validation {
proto::Hyperlink GetSampleSource() {
proto::Hyperlink link;
@@ -34,6 +35,12 @@ proto::SiteFirstSeen GetSampleFirstSeen() {
return first_seen;
}
+proto::MoreAbout GetSampleMoreAbout() {
+ proto::MoreAbout more_about;
+ more_about.set_url("https://example.com");
+ return more_about;
+}
+
proto::BannerInfo GetBannerInfo() {
proto::BannerInfo banner_info;
banner_info.set_title("Title");
@@ -47,6 +54,7 @@ proto::AboutThisSiteMetadata GetSampleMetaData() {
auto* site_info = metadata.mutable_site_info();
*site_info->mutable_description() = GetSampleDescription();
*site_info->mutable_first_seen() = GetSampleFirstSeen();
+ *site_info->mutable_more_about() = GetSampleMoreAbout();
return metadata;
}
@@ -55,7 +63,7 @@ TEST(AboutThisSiteValidation, ValidateProtos) {
auto metadata = GetSampleMetaData();
EXPECT_EQ(ValidateMetadata(metadata), AboutThisSiteStatus::kValid);
- // The proto should still be valid without a timestamp or without description.
+ // The proto should still be valid without a timestamp.
metadata.mutable_site_info()->clear_first_seen();
EXPECT_EQ(ValidateMetadata(metadata), AboutThisSiteStatus::kValid);
}
@@ -132,6 +140,36 @@ TEST(AboutThisSiteValidation, InvalidFirstSeenDuration) {
AboutThisSiteStatus::kInvalidTimeStamp);
}
-} // namespace about_this_site_validation
+TEST(AboutThisSiteValidation, InvalidMoreAbout) {
+ proto::MoreAbout more_about = GetSampleMoreAbout();
+ more_about.clear_url();
+ EXPECT_EQ(ValidateMoreAbout(more_about),
+ AboutThisSiteStatus::kInvalidMoreAbout);
+
+ more_about = GetSampleMoreAbout();
+ more_about.set_url("not a url");
+ EXPECT_EQ(ValidateMoreAbout(more_about),
+ AboutThisSiteStatus::kInvalidMoreAbout);
+}
+
+TEST(AboutThisSiteValidation, MissingMoreAbout_FeatureEDisabled) {
+ proto::AboutThisSiteMetadata meta_data = GetSampleMetaData();
+ EXPECT_EQ(ValidateMetadata(meta_data), AboutThisSiteStatus::kValid);
+
+ meta_data.mutable_site_info()->clear_more_about();
+ EXPECT_EQ(ValidateMetadata(meta_data), AboutThisSiteStatus::kValid);
+}
+
+TEST(AboutThisSiteValidation, MissingMoreAbout_FeatureEnabled) {
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(kPageInfoAboutThisSiteMoreInfo);
+
+ proto::AboutThisSiteMetadata meta_data = GetSampleMetaData();
+ EXPECT_EQ(ValidateMetadata(meta_data), AboutThisSiteStatus::kValid);
+
+ meta_data.mutable_site_info()->clear_more_about();
+ EXPECT_EQ(ValidateMetadata(meta_data),
+ AboutThisSiteStatus::kMissingMoreAbout);
+}
-} // namespace page_info
+} // namespace page_info::about_this_site_validation
diff --git a/chromium/components/page_info/core/features.cc b/chromium/components/page_info/core/features.cc
index 4454453bc0f..911fbaee2a6 100644
--- a/chromium/components/page_info/core/features.cc
+++ b/chromium/components/page_info/core/features.cc
@@ -7,6 +7,7 @@
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "build/build_config.h"
+#include "ui/base/l10n/l10n_util.h"
namespace page_info {
@@ -20,12 +21,25 @@ const base::Feature kPageInfoDiscoverability{"PageInfoDiscoverability",
base::FEATURE_ENABLED_BY_DEFAULT};
#endif
-const base::Feature kPageInfoAboutThisSite{"PageInfoAboutThisSite",
- base::FEATURE_DISABLED_BY_DEFAULT};
+extern bool IsAboutThisSiteFeatureEnabled(const std::string& locale) {
+ if (l10n_util::GetLanguage(locale) == "en") {
+ return base::FeatureList::IsEnabled(kPageInfoAboutThisSiteEn);
+ } else {
+ return base::FeatureList::IsEnabled(kPageInfoAboutThisSiteNonEn);
+ }
+}
-const base::FeatureParam<bool> kShowSampleContent{&kPageInfoAboutThisSite,
+const base::Feature kPageInfoAboutThisSiteEn{"PageInfoAboutThisSiteEn",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kPageInfoAboutThisSiteNonEn{
+ "PageInfoAboutThisSiteNonEn", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::FeatureParam<bool> kShowSampleContent{&kPageInfoAboutThisSiteEn,
"ShowSampleContent", false};
+const base::Feature kPageInfoAboutThisSiteMoreInfo{
+ "PageInfoAboutThisSiteMoreInfo", base::FEATURE_DISABLED_BY_DEFAULT};
+
const base::Feature kAboutThisSiteBanner{"AboutThisSiteBanner",
base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromium/components/page_info/core/features.h b/chromium/components/page_info/core/features.h
index 9a7df22f11a..c048b94d13c 100644
--- a/chromium/components/page_info/core/features.h
+++ b/chromium/components/page_info/core/features.h
@@ -27,11 +27,16 @@ extern const base::Feature kPageInfoDiscoverability;
#endif
// Enables the "About this site" section in Page Info.
-extern const base::Feature kPageInfoAboutThisSite;
+extern bool IsAboutThisSiteFeatureEnabled(const std::string& locale);
+extern const base::Feature kPageInfoAboutThisSiteEn;
+extern const base::Feature kPageInfoAboutThisSiteNonEn;
// Whether we show hard-coded content for some sites like https://example.com.
extern const base::FeatureParam<bool> kShowSampleContent;
+// Shows a link with more info about a site in PageInfo.
+extern const base::Feature kPageInfoAboutThisSiteMoreInfo;
+
// Enables the "About this site" banner.
extern const base::Feature kAboutThisSiteBanner;
diff --git a/chromium/components/page_info/core/proto/about_this_site_metadata.proto b/chromium/components/page_info/core/proto/about_this_site_metadata.proto
index 1f72bb93556..973fdf4f92e 100644
--- a/chromium/components/page_info/core/proto/about_this_site_metadata.proto
+++ b/chromium/components/page_info/core/proto/about_this_site_metadata.proto
@@ -36,7 +36,18 @@ enum DurationPrecision {
}
message SiteDescription {
+ // The name of the site, e.g. "The New York Times".
+ // May not be present.
optional string name = 1;
+
+ // The subtitle for the site.
+ // May not be present.
+ // If title is missing, subtitle will not be present either, but even for
+ // cases where there is a title there may not be a subtitle.
+ optional string subtitle = 5;
+
+ // The description of the site, e.g. "The New York Times is an American daily
+ // newspaper based in New York City..."
optional string description = 2;
// Information about the source of the description. Note that if your product
@@ -54,6 +65,12 @@ message Hyperlink {
optional string url = 2;
}
+// Link to learn more about the input url.
+message MoreAbout {
+ // The absolute url for the link.
+ optional string url = 1;
+}
+
// Info that is shown in PageInfo.
message SiteInfo {
// First-seen date information related to this host/domain.
@@ -62,6 +79,9 @@ message SiteInfo {
// Description for this site.
optional SiteDescription description = 2;
+
+ // A link to a page with more information about the page, if available.
+ optional MoreAbout more_about = 3;
}
// Info for a site-specific banner.
diff --git a/chromium/components/page_info/page_info.cc b/chromium/components/page_info/page_info.cc
index 42ef7f15dad..8af14cdd2d1 100644
--- a/chromium/components/page_info/page_info.cc
+++ b/chromium/components/page_info/page_info.cc
@@ -15,7 +15,6 @@
#include "base/i18n/time_formatting.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
@@ -39,11 +38,10 @@
#include "components/permissions/permission_result.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
+#include "components/permissions/permissions_client.h"
#if BUILDFLAG(IS_ANDROID)
#include "components/resources/android/theme_resources.h"
#endif
-#include "base/debug/crash_logging.h"
-#include "base/debug/dump_without_crashing.h"
#include "build/chromeos_buildflags.h"
#include "components/page_info/core/features.h"
#include "components/safe_browsing/buildflags.h"
@@ -56,7 +54,6 @@
#include "components/strings/grit/components_chromium_strings.h"
#include "components/strings/grit/components_strings.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "components/url_formatter/elide_url.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
@@ -101,7 +98,7 @@ ContentSettingsType kPermissionType[] = {
ContentSettingsType::BACKGROUND_SYNC,
ContentSettingsType::SOUND,
ContentSettingsType::AUTOMATIC_DOWNLOADS,
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
#endif
ContentSettingsType::MIDI_SYSEX,
@@ -121,6 +118,7 @@ ContentSettingsType kPermissionType[] = {
ContentSettingsType::VR,
ContentSettingsType::AR,
ContentSettingsType::IDLE_DETECTION,
+ ContentSettingsType::FEDERATED_IDENTITY_API,
};
// Determines whether to show permission |type| in the Page Info UI. Only
@@ -292,10 +290,6 @@ const char kPageInfoTimePrefix[] = "Security.PageInfo.TimeOpen";
const char kPageInfoTimeActionPrefix[] = "Security.PageInfo.TimeOpen.Action";
const char kPageInfoTimeNoActionPrefix[] =
"Security.PageInfo.TimeOpen.NoAction";
-const char kPageInfoTimeAboutThisShown[] =
- "Security.PageInfo.TimeOpen.AboutThisSiteShown";
-const char kPageInfoTimeAboutThisNotShown[] =
- "Security.PageInfo.TimeOpen.AboutThisSiteNotShown";
} // namespace
@@ -338,9 +332,9 @@ PageInfo::~PageInfo() {
did_revoke_user_ssl_decisions_ ? USER_CERT_DECISIONS_REVOKED
: USER_CERT_DECISIONS_NOT_REVOKED;
if (show_ssl_decision_revoke_button_) {
- UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.did_user_revoke_decisions2",
- user_decision,
- END_OF_SSL_CERTIFICATE_DECISIONS_DID_REVOKE_ENUM);
+ base::UmaHistogramEnumeration(
+ "interstitial.ssl.did_user_revoke_decisions2", user_decision,
+ END_OF_SSL_CERTIFICATE_DECISIONS_DID_REVOKE_ENUM);
}
// Record the total time the Page Info UI was open for all opens as well as
@@ -368,13 +362,6 @@ PageInfo::~PageInfo() {
safety_tip_info_.status),
start_time_);
}
- if (base::FeatureList::IsEnabled(page_info::kPageInfoAboutThisSite)) {
- if (was_about_this_site_shown_) {
- LogTimeOpenHistogram(kPageInfoTimeAboutThisShown, start_time_);
- } else {
- LogTimeOpenHistogram(kPageInfoTimeAboutThisNotShown, start_time_);
- }
- }
}
// static
@@ -428,11 +415,11 @@ void PageInfo::RecordPageInfoAction(PageInfoAction action) {
delegate_->OnPageInfoActionOccurred(action);
#endif
- UMA_HISTOGRAM_ENUMERATION("WebsiteSettings.Action", action, PAGE_INFO_COUNT);
+ base::UmaHistogramEnumeration("WebsiteSettings.Action", action);
if (web_contents_) {
ukm::builders::PageInfoBubble(
- ukm::GetSourceIdForWebContentsDocument(web_contents_.get()))
+ web_contents_->GetPrimaryMainFrame()->GetPageUkmSourceId())
.SetActionTaken(action)
.Record(ukm::UkmRecorder::Get());
}
@@ -440,7 +427,7 @@ void PageInfo::RecordPageInfoAction(PageInfoAction action) {
base::UmaHistogramEnumeration(
security_state::GetSafetyTipHistogramName(
"Security.SafetyTips.PageInfo.Action", safety_tip_info_.status),
- action, PAGE_INFO_COUNT);
+ action);
auto* settings = GetPageSpecificContentSettings();
if (!settings)
@@ -450,10 +437,9 @@ void PageInfo::RecordPageInfoAction(PageInfoAction action) {
bool has_fledge = settings->HasJoinedUserToInterestGroup();
switch (action) {
case PageInfoAction::PAGE_INFO_OPENED:
- if (has_fledge || has_topic) {
- base::RecordAction(
- base::UserMetricsAction("PageInfo.OpenedWithAdsPersonalization"));
- }
+ base::RecordAction(base::UserMetricsAction("PageInfo.Opened"));
+ base::UmaHistogramBoolean("Security.PageInfo.AdPersonalizationRowShown",
+ has_fledge || has_topic);
break;
case PageInfoAction::PAGE_INFO_AD_PERSONALIZATION_PAGE_OPENED:
if (has_fledge && has_topic) {
@@ -471,7 +457,96 @@ void PageInfo::RecordPageInfoAction(PageInfoAction action) {
base::RecordAction(base::UserMetricsAction(
"PageInfo.AdPersonalization.ManageInterestClicked"));
break;
- default:
+ case PAGE_INFO_CERTIFICATE_DIALOG_OPENED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.Security.Certificate.Opened"));
+ break;
+ case PAGE_INFO_CONNECTION_HELP_OPENED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.Security.ConnectionHelp.Opened"));
+ break;
+ case PAGE_INFO_SECURITY_DETAILS_OPENED:
+ base::RecordAction(base::UserMetricsAction("PageInfo.Security.Opened"));
+ break;
+ case PAGE_INFO_SITE_SETTINGS_OPENED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.SiteSettings.Opened"));
+ break;
+ case PAGE_INFO_COOKIES_DIALOG_OPENED:
+ base::RecordAction(base::UserMetricsAction("PageInfo.Cookies.Opened"));
+ break;
+ case PAGE_INFO_COOKIES_ALLOWED_FOR_SITE:
+ base::RecordAction(base::UserMetricsAction("PageInfo.Cookies.Allowed"));
+ break;
+ case PAGE_INFO_COOKIES_BLOCKED_FOR_SITE:
+ base::RecordAction(base::UserMetricsAction("PageInfo.Cookies.Blocked"));
+ break;
+ case PAGE_INFO_COOKIES_CLEARED:
+ base::RecordAction(base::UserMetricsAction("PageInfo.Cookies.Cleared"));
+ break;
+ case PAGE_INFO_PERMISSION_DIALOG_OPENED:
+ base::RecordAction(base::UserMetricsAction("PageInfo.Permission.Opened"));
+ break;
+ case PAGE_INFO_CHANGED_PERMISSION:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.Permission.Changed"));
+ break;
+ case PAGE_INFO_PERMISSIONS_CLEARED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.Permission.Cleared"));
+ break;
+ case PAGE_INFO_CHOOSER_OBJECT_DELETED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.Permission.ChooserObjectDeleted"));
+ break;
+ case PAGE_INFO_RESET_DECISIONS_CLICKED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.Permission.ResetDecisions"));
+ break;
+ case PAGE_INFO_FORGET_SITE_OPENED:
+ base::RecordAction(base::UserMetricsAction("PageInfo.ForgetSite.Opened"));
+ break;
+ case PAGE_INFO_FORGET_SITE_CLEARED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.ForgetSite.Cleared"));
+ break;
+ case PAGE_INFO_HISTORY_OPENED:
+ base::RecordAction(base::UserMetricsAction("PageInfo.History.Opened"));
+ break;
+ case PAGE_INFO_HISTORY_ENTRY_REMOVED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.History.EntryRemoved"));
+ break;
+ case PAGE_INFO_HISTORY_ENTRY_CLICKED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.History.EntryClicked"));
+ break;
+ case PAGE_INFO_PASSWORD_REUSE_ALLOWED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.PasswordReuseAllowed"));
+ break;
+ case PAGE_INFO_CHANGE_PASSWORD_PRESSED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.ChangePasswordPressed"));
+ break;
+ case PAGE_INFO_SAFETY_TIP_HELP_OPENED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.SafetyTip.HelpOpened"));
+ break;
+ case PAGE_INFO_STORE_INFO_CLICKED:
+ base::RecordAction(base::UserMetricsAction("PageInfo.StoreInfo.Opened"));
+ break;
+ case PAGE_INFO_ABOUT_THIS_SITE_PAGE_OPENED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.AboutThisSite.Opened"));
+ break;
+ case PAGE_INFO_ABOUT_THIS_SITE_SOURCE_LINK_CLICKED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.AboutThisSite.SourceLinkClicked"));
+ break;
+ case PAGE_INFO_ABOUT_THIS_SITE_MORE_ABOUT_CLICKED:
+ base::RecordAction(
+ base::UserMetricsAction("PageInfo.AboutThisSite.MoreAboutClicked"));
break;
}
}
@@ -490,15 +565,15 @@ void PageInfo::OnSitePermissionChanged(ContentSettingsType type,
// the Page Info UI.
size_t num_values;
int histogram_value = ContentSettingTypeToHistogramValue(type, &num_values);
- UMA_HISTOGRAM_EXACT_LINEAR("WebsiteSettings.OriginInfo.PermissionChanged",
- histogram_value, num_values);
+ base::UmaHistogramExactLinear("WebsiteSettings.OriginInfo.PermissionChanged",
+ histogram_value, num_values);
if (setting == ContentSetting::CONTENT_SETTING_ALLOW) {
- UMA_HISTOGRAM_EXACT_LINEAR(
+ base::UmaHistogramExactLinear(
"WebsiteSettings.OriginInfo.PermissionChanged.Allowed", histogram_value,
num_values);
} else if (setting == ContentSetting::CONTENT_SETTING_BLOCK) {
- UMA_HISTOGRAM_EXACT_LINEAR(
+ base::UmaHistogramExactLinear(
"WebsiteSettings.OriginInfo.PermissionChanged.Blocked", histogram_value,
num_values);
}
@@ -833,15 +908,13 @@ void PageInfo::ComputeUIInputs(const GURL& url) {
safety_tip_info_ = visible_security_state.safety_tip_info;
#if BUILDFLAG(IS_ANDROID)
- if (security_state::IsSafetyTipUIFeatureEnabled()) {
- // identity_status_description_android_ is only displayed on Android when
- // the user taps "Details" link on the page info. Reuse the description from
- // page info UI.
- std::unique_ptr<PageInfoUI::SecurityDescription> security_description =
- PageInfoUI::CreateSafetyTipSecurityDescription(safety_tip_info_);
- if (security_description) {
- identity_status_description_android_ = security_description->details;
- }
+ // identity_status_description_android_ is only displayed on Android when
+ // the user taps "Details" link on the page info. Reuse the description from
+ // page info UI.
+ std::unique_ptr<PageInfoUI::SecurityDescription> security_description =
+ PageInfoUI::CreateSafetyTipSecurityDescription(safety_tip_info_);
+ if (security_description) {
+ identity_status_description_android_ = security_description->details;
}
#endif
@@ -978,14 +1051,26 @@ void PageInfo::PresentSitePermissions() {
nullptr);
}
- // For permissions that are still prompting the user and haven't been
- // explicitly set by another source, check its embargo status.
- if (permissions::PermissionUtil::IsPermission(permission_info.type) &&
+ // Check embargo status if the content setting supports embargo.
+ if (permissions::PermissionDecisionAutoBlocker::IsEnabledForContentSetting(
+ permission_info.type) &&
permission_info.setting == CONTENT_SETTING_DEFAULT &&
permission_info.source ==
content_settings::SettingSource::SETTING_SOURCE_USER) {
- permissions::PermissionResult permission_result =
- delegate_->GetPermissionStatus(permission_info.type, site_url_);
+ permissions::PermissionResult permission_result(
+ CONTENT_SETTING_DEFAULT,
+ permissions::PermissionStatusSource::UNSPECIFIED);
+ if (permissions::PermissionUtil::IsPermission(permission_info.type)) {
+ permission_result =
+ delegate_->GetPermissionStatus(permission_info.type, site_url_);
+ } else if (permission_info.type ==
+ ContentSettingsType::FEDERATED_IDENTITY_API) {
+ absl::optional<permissions::PermissionResult> embargo_result =
+ delegate_->GetPermissionDecisionAutoblocker()->GetEmbargoResult(
+ site_url_, permission_info.type);
+ if (embargo_result)
+ permission_result = *embargo_result;
+ }
// If under embargo, update |permission_info| to reflect that.
if (permission_result.content_setting == CONTENT_SETTING_BLOCK &&
@@ -1052,10 +1137,7 @@ void PageInfo::PresentSiteDataInternal(base::OnceClosure done) {
void PageInfo::PresentSiteData(base::OnceClosure done) {
auto* settings = GetPageSpecificContentSettings();
- if (!settings) {
- SCOPED_CRASH_KEY_STRING256("page_info", "site_scheme", site_url_.scheme());
- base::debug::DumpWithoutCrashing();
- } else {
+ if (settings) {
settings->allowed_local_shared_objects().UpdateIgnoredEmptyStorageKeys(
base::BindOnce(&PageInfo::PresentSiteDataInternal,
weak_factory_.GetWeakPtr(), std::move(done)));
@@ -1075,9 +1157,7 @@ void PageInfo::PresentSiteIdentity() {
info.identity_status = site_identity_status_;
info.safe_browsing_status = safe_browsing_status_;
info.safe_browsing_details = safe_browsing_details_;
- if (security_state::IsSafetyTipUIFeatureEnabled()) {
- info.safety_tip_info = safety_tip_info_;
- }
+ info.safety_tip_info = safety_tip_info_;
#if BUILDFLAG(IS_ANDROID)
info.identity_status_description_android =
UTF16ToUTF8(identity_status_description_android_);
@@ -1208,7 +1288,7 @@ PageInfo::GetPageSpecificContentSettings() const {
// anything?
DCHECK(web_contents_);
return content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents_->GetMainFrame());
+ web_contents_->GetPrimaryMainFrame());
}
bool PageInfo::HasContentSettingChangedViaPageInfo(ContentSettingsType type) {
diff --git a/chromium/components/page_info/page_info.h b/chromium/components/page_info/page_info.h
index 6d5405f4a45..d63ebbee26b 100644
--- a/chromium/components/page_info/page_info.h
+++ b/chromium/components/page_info/page_info.h
@@ -150,7 +150,8 @@ class PageInfo {
PAGE_INFO_ABOUT_THIS_SITE_SOURCE_LINK_CLICKED = 29,
PAGE_INFO_AD_PERSONALIZATION_PAGE_OPENED = 30,
PAGE_INFO_AD_PERSONALIZATION_SETTINGS_OPENED = 31,
- PAGE_INFO_COUNT
+ PAGE_INFO_ABOUT_THIS_SITE_MORE_ABOUT_CLICKED = 32,
+ kMaxValue = PAGE_INFO_ABOUT_THIS_SITE_MORE_ABOUT_CLICKED
};
struct ChooserUIInfo {
diff --git a/chromium/components/page_info/page_info_ui.cc b/chromium/components/page_info/page_info_ui.cc
index f48979d3476..79897b1d405 100644
--- a/chromium/components/page_info/page_info_ui.cc
+++ b/chromium/components/page_info/page_info_ui.cc
@@ -16,6 +16,7 @@
#include "components/page_info/core/features.h"
#include "components/page_info/page_info_ui_delegate.h"
#include "components/permissions/features.h"
+#include "components/permissions/permission_decision_auto_blocker.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_result.h"
#include "components/permissions/permission_util.h"
@@ -153,7 +154,7 @@ base::span<const PageInfoUI::PermissionUIInfo> GetContentSettingsUIInfo() {
{ContentSettingsType::BACKGROUND_SYNC,
IDS_SITE_SETTINGS_TYPE_BACKGROUND_SYNC,
IDS_SITE_SETTINGS_TYPE_BACKGROUND_SYNC_MID_SENTENCE},
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
{ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
IDS_SITE_SETTINGS_TYPE_PROTECTED_MEDIA_ID,
IDS_SITE_SETTINGS_TYPE_PROTECTED_MEDIA_ID_MID_SENTENCE},
@@ -195,6 +196,9 @@ base::span<const PageInfoUI::PermissionUIInfo> GetContentSettingsUIInfo() {
IDS_SITE_SETTINGS_TYPE_IDLE_DETECTION_MID_SENTENCE},
#if !BUILDFLAG(IS_ANDROID)
// Page Info Permissions that are not defined in Android.
+ {ContentSettingsType::FEDERATED_IDENTITY_API,
+ IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API,
+ IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API_MID_SENTENCE},
{ContentSettingsType::FILE_SYSTEM_WRITE_GUARD,
IDS_SITE_SETTINGS_TYPE_FILE_SYSTEM_ACCESS_WRITE,
IDS_SITE_SETTINGS_TYPE_FILE_SYSTEM_ACCESS_WRITE_MID_SENTENCE},
@@ -746,9 +750,20 @@ std::u16string PageInfoUI::PermissionAutoBlockedToUIString(
// TODO(crbug.com/1063023): PageInfo::PermissionInfo should be modified
// to contain all needed information regarding Automatically Blocked flag.
if (permission.setting == CONTENT_SETTING_BLOCK &&
- permissions::PermissionUtil::IsPermission(permission.type)) {
- permissions::PermissionResult permission_result =
- delegate->GetPermissionStatus(permission.type);
+ permissions::PermissionDecisionAutoBlocker::IsEnabledForContentSetting(
+ permission.type)) {
+ permissions::PermissionResult permission_result(
+ CONTENT_SETTING_DEFAULT,
+ permissions::PermissionStatusSource::UNSPECIFIED);
+ if (permissions::PermissionUtil::IsPermission(permission.type)) {
+ permission_result = delegate->GetPermissionStatus(permission.type);
+ } else if (permission.type == ContentSettingsType::FEDERATED_IDENTITY_API) {
+ absl::optional<permissions::PermissionResult> embargo_result =
+ delegate->GetEmbargoResult(permission.type);
+ if (embargo_result)
+ permission_result = *embargo_result;
+ }
+
switch (permission_result.source) {
case permissions::PermissionStatusSource::MULTIPLE_DISMISSALS:
message_id = IDS_PAGE_INFO_PERMISSION_AUTOMATICALLY_BLOCKED;
@@ -766,33 +781,6 @@ std::u16string PageInfoUI::PermissionAutoBlockedToUIString(
}
// static
-std::u16string PageInfoUI::PermissionDecisionReasonToUIString(
- PageInfoUiDelegate* delegate,
- const PageInfo::PermissionInfo& permission) {
- ContentSetting effective_setting = GetEffectiveSetting(
- permission.type, permission.setting, permission.default_setting);
- int message_id = kInvalidResourceID;
- switch (permission.source) {
- case content_settings::SettingSource::SETTING_SOURCE_POLICY:
- message_id = kPermissionButtonTextIDPolicyManaged[effective_setting];
- break;
- case content_settings::SettingSource::SETTING_SOURCE_EXTENSION:
- message_id = kPermissionButtonTextIDExtensionManaged[effective_setting];
- break;
- default:
- break;
- }
-
- auto auto_block_text = PermissionAutoBlockedToUIString(delegate, permission);
- if (!auto_block_text.empty())
- return auto_block_text;
-
- if (message_id == kInvalidResourceID)
- return std::u16string();
- return l10n_util::GetStringUTF16(message_id);
-}
-
-// static
void PageInfoUI::ToggleBetweenAllowAndBlock(
PageInfo::PermissionInfo& permission) {
auto opposite_to_block_setting =
diff --git a/chromium/components/page_info/page_info_ui.h b/chromium/components/page_info/page_info_ui.h
index f4187c6aa9b..3736da6ccdc 100644
--- a/chromium/components/page_info/page_info_ui.h
+++ b/chromium/components/page_info/page_info_ui.h
@@ -195,12 +195,6 @@ class PageInfoUI {
content_settings::SettingSource source,
bool is_one_time);
- // Returns a string indicating whether the permission was blocked via an
- // extension, enterprise policy, or embargo.
- static std::u16string PermissionDecisionReasonToUIString(
- PageInfoUiDelegate* delegate,
- const PageInfo::PermissionInfo& permission);
-
static std::u16string PermissionStateToUIString(
PageInfoUiDelegate* delegate,
const PageInfo::PermissionInfo& permission);
diff --git a/chromium/components/page_info/page_info_ui_delegate.h b/chromium/components/page_info/page_info_ui_delegate.h
index bb8a34a6a62..641db3517b7 100644
--- a/chromium/components/page_info/page_info_ui_delegate.h
+++ b/chromium/components/page_info/page_info_ui_delegate.h
@@ -8,6 +8,7 @@
#include "build/build_config.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/permissions/permission_result.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
class PageInfoUiDelegate {
public:
@@ -18,6 +19,8 @@ class PageInfoUiDelegate {
#endif
virtual permissions::PermissionResult GetPermissionStatus(
ContentSettingsType type) = 0;
+ virtual absl::optional<permissions::PermissionResult> GetEmbargoResult(
+ ContentSettingsType type) = 0;
};
#endif // COMPONENTS_PAGE_INFO_PAGE_INFO_UI_DELEGATE_H_
diff --git a/chromium/components/page_info_strings.grdp b/chromium/components/page_info_strings.grdp
index 5d727e107f6..0feea7dc66d 100644
--- a/chromium/components/page_info_strings.grdp
+++ b/chromium/components/page_info_strings.grdp
@@ -692,13 +692,18 @@
<message name="IDS_PAGE_INFO_ABOUT_THIS_SITE_HEADER" desc="The header label of the 'About this site' subpage in Page Info bubble.">
From the web
</message>
+ <message name="IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE" desc="The title of the 'About this page' row in the Page Info bubble.">
+ About this page
+ </message>
<message name="IDS_PAGE_INFO_ABOUT_THIS_SITE_TOOLTIP" desc="The tooltip of the button that opens 'About this site' subpage in Page Info bubble.">
Show information from the web
</message>
+ <message name="IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP" desc="The tooltip of the button that opens 'About this page' details in Page Info bubble.">
+ Learn about this page's source &amp; topic
+ </message>
<message name="IDS_PAGE_INFO_ABOUT_THIS_SITE_SUBPAGE_FROM_LABEL" desc="The label containing the source of the description in the 'About this site' subpage in Page Info bubble.">
From <ph name="SOURCE_NAME">$1<ex>Wikipedia</ex></ph>
</message>
-
<!-- History strings -->
<message name="IDS_PAGE_INFO_HISTORY" desc="The button label of the 'History' row in Page Info bubble. The button opens history page for the site.">
History
diff --git a/chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha1 b/chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha1
new file mode 100644
index 00000000000..660d392af8f
--- /dev/null
+++ b/chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha1
@@ -0,0 +1 @@
+23952a4790bc0a6e4c9f97a712746539771af8ef \ No newline at end of file
diff --git a/chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP.png.sha1 b/chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP.png.sha1
new file mode 100644
index 00000000000..660d392af8f
--- /dev/null
+++ b/chromium/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+23952a4790bc0a6e4c9f97a712746539771af8ef \ No newline at end of file
diff --git a/chromium/components/page_load_metrics/OWNERS b/chromium/components/page_load_metrics/OWNERS
index ac1e8d093be..6b50b55c792 100644
--- a/chromium/components/page_load_metrics/OWNERS
+++ b/chromium/components/page_load_metrics/OWNERS
@@ -5,3 +5,5 @@ csharrison@chromium.org
jkarlin@chromium.org
ryansturm@chromium.org
johnidel@chromium.org
+toyoshim@chromium.org
+
diff --git a/chromium/components/page_load_metrics/browser/BUILD.gn b/chromium/components/page_load_metrics/browser/BUILD.gn
index 2a8ee1fac69..bcccd230d3f 100644
--- a/chromium/components/page_load_metrics/browser/BUILD.gn
+++ b/chromium/components/page_load_metrics/browser/BUILD.gn
@@ -44,6 +44,8 @@ source_set("browser") {
"page_load_metrics_observer.h",
"page_load_metrics_observer_delegate.cc",
"page_load_metrics_observer_delegate.h",
+ "page_load_metrics_observer_interface.cc",
+ "page_load_metrics_observer_interface.h",
"page_load_metrics_update_dispatcher.cc",
"page_load_metrics_update_dispatcher.h",
"page_load_metrics_util.cc",
@@ -59,7 +61,6 @@ source_set("browser") {
]
deps = [
"//build:chromeos_buildflags",
- "//components/data_reduction_proxy/core/browser",
"//components/keyed_service/content:content",
"//components/keyed_service/core:core",
"//components/metrics",
diff --git a/chromium/components/page_load_metrics/browser/DEPS b/chromium/components/page_load_metrics/browser/DEPS
index 4ca9a1aab6e..b9720601f9f 100644
--- a/chromium/components/page_load_metrics/browser/DEPS
+++ b/chromium/components/page_load_metrics/browser/DEPS
@@ -3,7 +3,6 @@ include_rules = [
"+content/public/browser",
"+content/public/test",
"+components/blocklist",
- "+components/data_reduction_proxy/core/browser",
"+components/heavy_ad_intervention",
"+components/keyed_service/content",
"+components/keyed_service/core",
diff --git a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc
index e403fac44cf..6b076d8cf0f 100644
--- a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc
+++ b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.cc
@@ -249,7 +249,8 @@ MetricsWebContentsObserver::MetricsWebContentsObserver(
if (embedder_interface_->IsNoStatePrefetch(web_contents))
in_foreground_ = false;
- RegisterInputEventObserver(web_contents->GetMainFrame()->GetRenderViewHost());
+ RegisterInputEventObserver(
+ web_contents->GetPrimaryMainFrame()->GetRenderViewHost());
}
void MetricsWebContentsObserver::WillStartNavigationRequestImpl(
@@ -281,11 +282,15 @@ void MetricsWebContentsObserver::WillStartNavigationRequestImpl(
// Prepare ukm::SourceId that is based on outermost page's navigation ID.
ukm::SourceId source_id = ukm::kInvalidSourceId;
base::WeakPtr<PageLoadTracker> parent_tracker;
- if (navigation_handle->IsInPrimaryMainFrame() ||
- navigation_handle->IsInPrerenderedMainFrame()) {
- // Primary and Prerender pages use own page's navigation ID.
+ if (navigation_handle->IsInPrimaryMainFrame()) {
+ // Primary pages use own page's navigation ID.
source_id = ukm::ConvertToSourceId(navigation_handle->GetNavigationId(),
ukm::SourceIdType::NAVIGATION_ID);
+ } else if (navigation_handle->IsInPrerenderedMainFrame()) {
+ // Prerendering pages should not record UKM until its activation. So, we
+ // start with ukm::kInvalidSourceId and set a correct ukm::SourceId on
+ // activation.
+ DCHECK_EQ(ukm::kInvalidSourceId, source_id);
} else if (navigation_handle->GetNavigatingFrameType() ==
content::FrameType::kFencedFrameRoot) {
// For FencedFrames, use the primary page's ukm::SourceId. `primary_page_`
@@ -293,11 +298,24 @@ void MetricsWebContentsObserver::WillStartNavigationRequestImpl(
if (primary_page_) {
source_id = primary_page_->GetPageUkmSourceId();
parent_tracker = primary_page_->GetWeakPtr();
+ } else {
+ // Use ukm::NoURLSourceId() rather than kInvalidSourceId to avoid
+ // unexpected check failure. This happens on tests that create a
+ // FencedFrame via FencedFrameTestHelper directly without a correct setup
+ // being finished on the embedder frame.
+ source_id = ukm::NoURLSourceId();
}
} else {
NOTREACHED();
}
+ // For prerendered page activations, we don't create a new PageLoadTracker,
+ // but reuse an existing one that was created for the initial prerendering
+ // navigation so that the same instance will bee OnPrerenderStart and
+ // DidActivatePrerenderedPage.
+ if (navigation_handle->IsPrerenderedPageActivation())
+ return;
+
// Passing raw pointers to `embedder_interface_` is safe because the
// MetricsWebContentsObserver owns them both list and they are torn down after
// the PageLoadTracker. The PageLoadTracker does not hold on to
@@ -404,7 +422,7 @@ void MetricsWebContentsObserver::ResourceLoadComplete(
const blink::mojom::CommonNetworkInfoPtr& network_info =
resource_load_info.network_info;
ExtraRequestCompleteInfo extra_request_complete_info(
- url::Origin::Create(resource_load_info.final_url),
+ url::SchemeHostPort(resource_load_info.final_url),
network_info->remote_endpoint.value(),
render_frame_host->GetFrameTreeNodeId(), resource_load_info.was_cached,
resource_load_info.raw_body_bytes, original_content_length,
diff --git a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h
index 4199163900d..890e0d27b0f 100644
--- a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h
+++ b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer.h
@@ -117,13 +117,14 @@ class MetricsWebContentsObserver
const content::CookieAccessDetails& details) override;
void OnCookiesAccessed(content::RenderFrameHost* rfh,
const content::CookieAccessDetails& details) override;
+ void DidActivatePortal(content::WebContents* predecessor_web_contents,
+ base::TimeTicks activation_time) override;
+
void OnStorageAccessed(content::RenderFrameHost* rfh,
const GURL& url,
const GURL& first_party_url,
bool blocked_by_policy,
StorageType storage_type);
- void DidActivatePortal(content::WebContents* predecessor_web_contents,
- base::TimeTicks activation_time) override;
// These methods are forwarded from the MetricsNavigationThrottle.
void WillStartNavigationRequest(content::NavigationHandle* navigation_handle);
diff --git a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
index 09bc006c2a2..5bcd6eff101 100644
--- a/chromium/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
@@ -30,6 +30,7 @@
#include "services/network/public/mojom/fetch_api.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/utility/utility.h"
#include "third_party/blink/public/common/use_counter/use_counter_feature.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
#include "third_party/blink/public/mojom/use_counter/use_counter_feature.mojom-shared.h"
@@ -111,18 +112,18 @@ class MetricsWebContentsObserverTest
}
void SimulateTimingUpdate(const mojom::PageLoadTiming& timing) {
- SimulateTimingUpdate(timing, web_contents()->GetMainFrame());
+ SimulateTimingUpdate(timing, web_contents()->GetPrimaryMainFrame());
}
void SimulateCpuTimingUpdate(const mojom::CpuTiming& timing,
content::RenderFrameHost* render_frame_host) {
observer()->OnTimingUpdated(
render_frame_host, previous_timing_->Clone(),
- mojom::FrameMetadataPtr(base::in_place),
+ mojom::FrameMetadataPtr(absl::in_place),
std::vector<blink::UseCounterFeature>(),
std::vector<mojom::ResourceDataUpdatePtr>(),
- mojom::FrameRenderDataUpdatePtr(base::in_place), timing.Clone(),
- mojom::InputTimingPtr(base::in_place), blink::MobileFriendliness());
+ mojom::FrameRenderDataUpdatePtr(absl::in_place), timing.Clone(),
+ mojom::InputTimingPtr(absl::in_place), blink::MobileFriendliness());
}
void SimulateTimingUpdate(const mojom::PageLoadTiming& timing,
@@ -141,12 +142,12 @@ class MetricsWebContentsObserverTest
content::RenderFrameHost* render_frame_host) {
previous_timing_ = timing.Clone();
observer()->OnTimingUpdated(render_frame_host, timing.Clone(),
- mojom::FrameMetadataPtr(base::in_place),
+ mojom::FrameMetadataPtr(absl::in_place),
std::vector<blink::UseCounterFeature>(),
std::vector<mojom::ResourceDataUpdatePtr>(),
- mojom::FrameRenderDataUpdatePtr(base::in_place),
- mojom::CpuTimingPtr(base::in_place),
- mojom::InputTimingPtr(base::in_place),
+ mojom::FrameRenderDataUpdatePtr(absl::in_place),
+ mojom::CpuTimingPtr(absl::in_place),
+ mojom::InputTimingPtr(absl::in_place),
blink::MobileFriendliness());
}
@@ -1110,7 +1111,7 @@ TEST_F(MetricsWebContentsObserverTest,
CheckNoErrorEvents();
}
-TEST_F(MetricsWebContentsObserverTest, DISABLED_LongestInputInMainFrame) {
+TEST_F(MetricsWebContentsObserverTest, LongestInputInMainFrame) {
// We need to navigate before we can navigate the subframe.
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL(kDefaultTestUrl));
@@ -1293,31 +1294,31 @@ TEST_F(MetricsWebContentsObserverTest, OnLoadedResource_MainFrame) {
auto navigation_simulator =
content::NavigationSimulator::CreateRendererInitiated(
- main_resource_url, web_contents()->GetMainFrame());
+ main_resource_url, web_contents()->GetPrimaryMainFrame());
navigation_simulator->Start();
navigation_simulator->Commit();
const auto request_id = navigation_simulator->GetGlobalRequestID();
observer()->ResourceLoadComplete(
- web_contents()->GetMainFrame(), request_id,
+ web_contents()->GetPrimaryMainFrame(), request_id,
*CreateResourceLoadInfo(main_resource_url,
network::mojom::RequestDestination::kFrame));
EXPECT_EQ(1u, loaded_resources().size());
- EXPECT_EQ(url::Origin::Create(main_resource_url),
- loaded_resources().back().origin_of_final_url);
+ EXPECT_EQ(url::SchemeHostPort(main_resource_url),
+ loaded_resources().back().final_url);
NavigateToUntrackedUrl();
// Deliver a second main frame resource. This one should be ignored, since the
// specified |request_id| is no longer associated with any tracked page loads.
observer()->ResourceLoadComplete(
- web_contents()->GetMainFrame(), request_id,
+ web_contents()->GetPrimaryMainFrame(), request_id,
*CreateResourceLoadInfo(main_resource_url,
network::mojom::RequestDestination::kFrame));
EXPECT_EQ(1u, loaded_resources().size());
- EXPECT_EQ(url::Origin::Create(main_resource_url),
- loaded_resources().back().origin_of_final_url);
+ EXPECT_EQ(url::SchemeHostPort(main_resource_url),
+ loaded_resources().back().final_url);
}
TEST_F(MetricsWebContentsObserverTest, OnLoadedResource_Subresource) {
@@ -1325,13 +1326,13 @@ TEST_F(MetricsWebContentsObserverTest, OnLoadedResource_Subresource) {
web_contents(), GURL(kDefaultTestUrl));
GURL loaded_resource_url("http://www.other.com/");
observer()->ResourceLoadComplete(
- web_contents()->GetMainFrame(), content::GlobalRequestID(),
+ web_contents()->GetPrimaryMainFrame(), content::GlobalRequestID(),
*CreateResourceLoadInfo(loaded_resource_url,
network::mojom::RequestDestination::kScript));
EXPECT_EQ(1u, loaded_resources().size());
- EXPECT_EQ(url::Origin::Create(loaded_resource_url),
- loaded_resources().back().origin_of_final_url);
+ EXPECT_EQ(url::SchemeHostPort(loaded_resource_url),
+ loaded_resources().back().final_url);
}
TEST_F(MetricsWebContentsObserverTest,
@@ -1339,7 +1340,7 @@ TEST_F(MetricsWebContentsObserverTest,
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL(kDefaultTestUrl));
- content::RenderFrameHost* old_rfh = web_contents()->GetMainFrame();
+ content::RenderFrameHost* old_rfh = web_contents()->GetPrimaryMainFrame();
content::LeaveInPendingDeletionState(old_rfh);
content::NavigationSimulator::NavigateAndCommitFromBrowser(
@@ -1360,7 +1361,7 @@ TEST_F(MetricsWebContentsObserverTest,
web_contents(), GURL(kDefaultTestUrl));
GURL loaded_resource_url("data:text/html,Hello world");
observer()->ResourceLoadComplete(
- web_contents()->GetMainFrame(), content::GlobalRequestID(),
+ web_contents()->GetPrimaryMainFrame(), content::GlobalRequestID(),
*CreateResourceLoadInfo(loaded_resource_url,
network::mojom::RequestDestination::kScript));
@@ -1567,6 +1568,13 @@ class MetricsWebContentsObserverNonPrimaryPageTest
MetricsWebContentsObserverNonPrimaryPageTest* owner)
: owner_(owner) {}
+ // TODO(https://crbug.com/1317494): Audit and use appropriate policy.
+ ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override {
+ return STOP_OBSERVING;
+ }
+
ObservePolicy OnCommit(content::NavigationHandle* handle) override {
committed_url_ = handle->GetURL();
return CONTINUE_OBSERVING;
diff --git a/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.cc
index 347cbe3371c..d68306d1ee5 100644
--- a/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -35,7 +35,6 @@
#include "components/subresource_filter/core/common/common_features.h"
#include "components/subresource_filter/core/common/load_policy.h"
#include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/navigation_handle.h"
@@ -51,7 +50,7 @@
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom-shared.h"
#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
+#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
#include "ui/base/page_transition_types.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
@@ -169,13 +168,19 @@ AdsPageLoadMetricsObserver::CreateIfNeeded(
}
// static
-bool AdsPageLoadMetricsObserver::IsSubframeSameOriginToMainFrame(
- content::RenderFrameHost* sub_host) {
- DCHECK(sub_host);
- content::RenderFrameHost* main_host = sub_host->GetOutermostMainFrame();
- url::Origin subframe_origin = sub_host->GetLastCommittedOrigin();
- url::Origin mainframe_origin = main_host->GetLastCommittedOrigin();
- return subframe_origin.IsSameOriginWith(mainframe_origin);
+bool AdsPageLoadMetricsObserver::IsFrameSameOriginToOutermostMainFrame(
+ content::RenderFrameHost* host) {
+ DCHECK(host);
+ // In navigation for prerendering, `AdsPageLoadMetricsObserver` is removed
+ // from PageLoadTracker.
+ // TODO(https://crbug.com/1317494): Enable it if possible.
+ DCHECK_NE(content::RenderFrameHost::LifecycleState::kPrerendering,
+ host->GetLifecycleState());
+ content::RenderFrameHost* outermost_main_host = host->GetOutermostMainFrame();
+ url::Origin frame_origin = host->GetLastCommittedOrigin();
+ url::Origin outermost_mainframe_origin =
+ outermost_main_host->GetLastCommittedOrigin();
+ return frame_origin.IsSameOriginWith(outermost_mainframe_origin);
}
AdsPageLoadMetricsObserver::FrameInstance::FrameInstance()
@@ -289,13 +294,12 @@ PageLoadMetricsObserver::ObservePolicy AdsPageLoadMetricsObserver::OnCommit(
}
void AdsPageLoadMetricsObserver::OnTimingUpdate(
- content::RenderFrameHost* subframe_rfh,
+ content::RenderFrameHost* frame_rfh,
const mojom::PageLoadTiming& timing) {
- if (!subframe_rfh)
+ if (!frame_rfh)
return;
- FrameTreeData* ancestor_data =
- FindFrameData(subframe_rfh->GetFrameTreeNodeId());
+ FrameTreeData* ancestor_data = FindFrameData(frame_rfh->GetFrameTreeNodeId());
if (!ancestor_data)
return;
@@ -312,10 +316,8 @@ void AdsPageLoadMetricsObserver::OnTimingUpdate(
// set Creative Origin Status.
if (has_new_fcp) {
OriginStatus origin_status =
- AdsPageLoadMetricsObserver::IsSubframeSameOriginToMainFrame(
- subframe_rfh)
- ? OriginStatus::kSame
- : OriginStatus::kCross;
+ IsFrameSameOriginToOutermostMainFrame(frame_rfh) ? OriginStatus::kSame
+ : OriginStatus::kCross;
ancestor_data->set_creative_origin_status(origin_status);
}
}
@@ -456,6 +458,11 @@ void AdsPageLoadMetricsObserver::ReadyToCommitNextNavigation(
// ignore any such messages when a navigation is about to commit.
if (!navigation_handle->IsInMainFrame())
return;
+ // Prerendering navigation doesn't get here since this observer in
+ // prerendering is removed from PageLoadTracker.
+ // TODO(https://crbug.com/1317494): Consider enabling this observer for
+ // prerendering.
+ DCHECK(!navigation_handle->IsInPrerenderedMainFrame());
process_display_state_updates_ = false;
}
@@ -498,7 +505,7 @@ void AdsPageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
const GURL& last_committed_url =
navigation_handle->GetRenderFrameHost()->GetLastCommittedURL();
- const GURL& main_frame_last_committed_url =
+ const GURL& outermost_main_frame_last_committed_url =
navigation_handle->GetRenderFrameHost()
->GetOutermostMainFrame()
->GetLastCommittedURL();
@@ -508,7 +515,7 @@ void AdsPageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
(*load_policy != subresource_filter::LoadPolicy::DISALLOW) &&
(*load_policy != subresource_filter::LoadPolicy::WOULD_DISALLOW) &&
net::registry_controlled_domains::SameDomainOrHost(
- last_committed_url, main_frame_last_committed_url,
+ last_committed_url, outermost_main_frame_last_committed_url,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
should_ignore_detected_ad =
navigation_is_explicitly_allowed || should_ignore_same_domain_ad;
@@ -602,16 +609,13 @@ void AdsPageLoadMetricsObserver::MediaStartedPlaying(
ancestor_data->set_media_status(MediaStatus::kPlayed);
}
-void AdsPageLoadMetricsObserver::OnFrameIntersectionUpdate(
+void AdsPageLoadMetricsObserver::OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* render_frame_host,
- const mojom::FrameIntersectionUpdate& intersection_update) {
- if (!intersection_update.main_frame_intersection_rect)
- return;
-
+ const gfx::Rect& main_frame_intersection_rect) {
int frame_tree_node_id = render_frame_host->GetFrameTreeNodeId();
- if (render_frame_host == GetDelegate().GetWebContents()->GetMainFrame()) {
- page_ad_density_tracker_.UpdateMainFrameRect(
- *intersection_update.main_frame_intersection_rect);
+ if (render_frame_host ==
+ GetDelegate().GetWebContents()->GetPrimaryMainFrame()) {
+ page_ad_density_tracker_.UpdateMainFrameRect(main_frame_intersection_rect);
return;
}
@@ -623,15 +627,20 @@ void AdsPageLoadMetricsObserver::OnFrameIntersectionUpdate(
page_ad_density_tracker_.RemoveRect(frame_tree_node_id);
// Only add frames if they are visible.
if (!ancestor_data->is_display_none()) {
- page_ad_density_tracker_.AddRect(
- frame_tree_node_id,
- *intersection_update.main_frame_intersection_rect);
+ page_ad_density_tracker_.AddRect(frame_tree_node_id,
+ main_frame_intersection_rect);
}
}
CheckForAdDensityViolation();
}
+void AdsPageLoadMetricsObserver::OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) {
+ page_ad_density_tracker_.UpdateMainFrameViewportRect(
+ main_frame_viewport_rect);
+}
+
// TODO(https://crbug.com/1142669): Evaluate imposing width requirements
// for ad density violations.
void AdsPageLoadMetricsObserver::CheckForAdDensityViolation() {
@@ -653,7 +662,7 @@ void AdsPageLoadMetricsObserver::CheckForAdDensityViolation() {
// violations after the first are ignored. Ad frame violations are
// attributed to the main frame url.
throttle_manager->OnAdsViolationTriggered(
- GetDelegate().GetWebContents()->GetMainFrame(),
+ GetDelegate().GetWebContents()->GetPrimaryMainFrame(),
subresource_filter::mojom::AdsViolation::
kMobileAdDensityByHeightAbove30);
}
@@ -706,8 +715,9 @@ void AdsPageLoadMetricsObserver::OnV8MemoryChanged(
UpdateAggregateMemoryUsage(update.delta_bytes,
ad_frame_data->visibility());
} else if (!render_frame_host->GetParentOrOuterDocument()) {
- // |render_frame_host| is the main frame.
- aggregate_frame_data_->update_main_frame_memory(update.delta_bytes);
+ // |render_frame_host| is the outermost main frame.
+ aggregate_frame_data_->update_outermost_main_frame_memory(
+ update.delta_bytes);
}
}
}
@@ -728,6 +738,10 @@ void AdsPageLoadMetricsObserver::OnPageActivationComputed(
navigation_handle->GetNavigationId() == navigation_id_ &&
activation_state.activation_level ==
subresource_filter::mojom::ActivationLevel::kEnabled) {
+ // Prerendering navigation is filtered out by checking `navigation_id_`.
+ // TODO(https://crbug.com/1317494): Consider enabling this observer for
+ // prerendering.
+ DCHECK(!navigation_handle->IsInPrerenderedMainFrame());
DCHECK(!subresource_filter_is_enabled_);
subresource_filter_is_enabled_ = true;
}
@@ -761,11 +775,12 @@ void AdsPageLoadMetricsObserver::ProcessResourceForPage(
int process_id = render_frame_host->GetProcess()->GetID();
auto mime_type = ResourceLoadAggregator::GetResourceMimeType(resource);
int unaccounted_ad_bytes = GetUnaccountedAdBytes(process_id, resource);
- bool is_main_frame = !render_frame_host->GetParentOrOuterDocument();
- aggregate_frame_data_->ProcessResourceLoadInFrame(resource, is_main_frame);
+ bool is_outermost_main_frame = !render_frame_host->GetParentOrOuterDocument();
+ aggregate_frame_data_->ProcessResourceLoadInFrame(resource,
+ is_outermost_main_frame);
if (unaccounted_ad_bytes)
aggregate_frame_data_->AdjustAdBytes(unaccounted_ad_bytes, mime_type,
- is_main_frame);
+ is_outermost_main_frame);
}
void AdsPageLoadMetricsObserver::ProcessResourceForFrame(
@@ -852,7 +867,8 @@ void AdsPageLoadMetricsObserver::RecordPageResourceTotalHistograms(
resource_data.GetAdNetworkBytesForMime(ResourceMimeType::kVideo) >>
10)
.SetMainframeAdBytes(ukm::GetExponentialBucketMinForBytes(
- aggregate_frame_data_->main_frame_resource_data().ad_network_bytes()))
+ aggregate_frame_data_->outermost_main_frame_resource_data()
+ .ad_network_bytes()))
.SetMaxAdDensityByArea(page_ad_density_tracker_.MaxPageAdDensityByArea())
.SetMaxAdDensityByHeight(
page_ad_density_tracker_.MaxPageAdDensityByHeight());
@@ -861,6 +877,12 @@ void AdsPageLoadMetricsObserver::RecordPageResourceTotalHistograms(
builder.SetAdCpuTime(
aggregate_frame_data_->total_ad_cpu_usage().InMilliseconds());
builder.Record(ukm_recorder->Get());
+
+ // Record custom sampling metrics
+ ukm::builders::AdPageLoadCustomSampling custom_sampling_builder(source_id);
+ custom_sampling_builder.SetAverageViewportAdDensity(
+ page_ad_density_tracker_.AverageViewportAdDensityByArea());
+ custom_sampling_builder.Record(ukm_recorder->Get());
}
void AdsPageLoadMetricsObserver::RecordHistograms(ukm::SourceId source_id) {
@@ -980,19 +1002,20 @@ void AdsPageLoadMetricsObserver::RecordAggregateHistogramsForAdTagging(
if (visibility != FrameVisibility::kAnyVisibility)
return;
- const auto& main_frame_resource_data =
- aggregate_frame_data_->main_frame_resource_data();
+ const auto& outermost_main_frame_resource_data =
+ aggregate_frame_data_->outermost_main_frame_resource_data();
ADS_HISTOGRAM("Bytes.MainFrame.Network", PAGE_BYTES_HISTOGRAM, visibility,
- main_frame_resource_data.network_bytes());
+ outermost_main_frame_resource_data.network_bytes());
ADS_HISTOGRAM("Bytes.MainFrame.Total2", PAGE_BYTES_HISTOGRAM, visibility,
- main_frame_resource_data.bytes());
+ outermost_main_frame_resource_data.bytes());
ADS_HISTOGRAM("Bytes.MainFrame.Ads.Network", PAGE_BYTES_HISTOGRAM, visibility,
- main_frame_resource_data.ad_network_bytes());
+ outermost_main_frame_resource_data.ad_network_bytes());
ADS_HISTOGRAM("Bytes.MainFrame.Ads.Total2", PAGE_BYTES_HISTOGRAM, visibility,
- main_frame_resource_data.ad_bytes());
+ outermost_main_frame_resource_data.ad_bytes());
if (base::FeatureList::IsEnabled(::features::kV8PerFrameMemoryMonitoring)) {
- PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Memory.MainFrame.Max",
- aggregate_frame_data_->main_frame_max_memory());
+ PAGE_BYTES_HISTOGRAM(
+ "PageLoad.Clients.Ads.Memory.MainFrame.Max",
+ aggregate_frame_data_->outermost_main_frame_max_memory());
UMA_HISTOGRAM_COUNTS_10000("PageLoad.Clients.Ads.Memory.UpdateCount",
memory_update_count_);
}
@@ -1189,7 +1212,7 @@ void AdsPageLoadMetricsObserver::MaybeTriggerStrictHeavyAdIntervention() {
// violations after the first are ignored. Ad frame violations are
// attributed to the main frame url.
throttle_manager->OnAdsViolationTriggered(
- GetDelegate().GetWebContents()->GetMainFrame(),
+ GetDelegate().GetWebContents()->GetPrimaryMainFrame(),
subresource_filter::mojom::AdsViolation::
kHeavyAdsInterventionAtHostLimit);
}
diff --git a/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.h
index 84e6fd3ce52..0aec57f6b16 100644
--- a/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.h
@@ -82,9 +82,9 @@ class AdsPageLoadMetricsObserver
heavy_ad_intervention::HeavyAdService* heavy_ad_service,
const ApplicationLocaleGetter& application_local_getter);
- // For a given subframe, returns whether or not the subframe's url would be
- // considering same origin to the main frame's url.
- static bool IsSubframeSameOriginToMainFrame(
+ // For a given frame, returns whether or not the frame's url would be
+ // considered same origin to the outermost main frame's url.
+ static bool IsFrameSameOriginToOutermostMainFrame(
content::RenderFrameHost* sub_host);
// |clock| and |blocklist| should be set only by tests. In particular,
@@ -132,9 +132,11 @@ class AdsPageLoadMetricsObserver
void MediaStartedPlaying(
const content::WebContentsObserver::MediaPlayerInfo& video_type,
content::RenderFrameHost* render_frame_host) override;
- void OnFrameIntersectionUpdate(
+ void OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* render_frame_host,
- const mojom::FrameIntersectionUpdate& intersection_update) override;
+ const gfx::Rect& main_frame_intersection_rect) override;
+ void OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) override;
void OnSubFrameDeleted(int frame_tree_node_id) override;
void SetHeavyAdThresholdNoiseProviderForTesting(
diff --git a/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index 4b361f16dfa..215ab4441e7 100644
--- a/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -60,6 +60,7 @@
#include "net/base/host_port_pair.h"
#include "services/metrics/public/cpp/metrics_utils.h"
#include "services/metrics/public/cpp/ukm_builders.h"
+#include "third_party/abseil-cpp/absl/utility/utility.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
#include "url/gurl.h"
@@ -202,15 +203,15 @@ class ResourceLoadingCancellingThrottle
resource->is_complete = true;
resource->is_primary_frame_resource = true;
resources.push_back(std::move(resource));
- auto timing = mojom::PageLoadTimingPtr(base::in_place);
+ auto timing = mojom::PageLoadTimingPtr(absl::in_place);
InitPageLoadTimingForTest(timing.get());
observer->OnTimingUpdated(
navigation_handle()->GetRenderFrameHost(), std::move(timing),
- mojom::FrameMetadataPtr(base::in_place),
+ mojom::FrameMetadataPtr(absl::in_place),
std::vector<blink::UseCounterFeature>(), resources,
- mojom::FrameRenderDataUpdatePtr(base::in_place),
- mojom::CpuTimingPtr(base::in_place),
- mojom::InputTimingPtr(base::in_place), blink::MobileFriendliness());
+ mojom::FrameRenderDataUpdatePtr(absl::in_place),
+ mojom::CpuTimingPtr(absl::in_place),
+ mojom::InputTimingPtr(absl::in_place), blink::MobileFriendliness());
}
};
@@ -481,7 +482,7 @@ class AdsPageLoadMetricsObserverTest
// Returns the final RenderFrameHost after navigation commits.
RenderFrameHost* NavigateMainFrame(const std::string& url) {
- return NavigateFrame(url, web_contents()->GetMainFrame());
+ return NavigateFrame(url, web_contents()->GetPrimaryMainFrame());
}
void OnCpuTimingUpdate(RenderFrameHost* render_frame_host,
@@ -516,11 +517,7 @@ class AdsPageLoadMetricsObserverTest
std::unique_ptr<NavigationSimulator> CreateNavigationSimulator(
const std::string& url,
content::RenderFrameHost* frame) {
- if (WithFencedFrames() && !frame->IsInPrimaryMainFrame()) {
- return NavigationSimulator::CreateForFencedFrame(GURL(url), frame);
- } else {
- return NavigationSimulator::CreateRendererInitiated(GURL(url), frame);
- }
+ return NavigationSimulator::CreateRendererInitiated(GURL(url), frame);
}
// Returns the final RenderFrameHost after navigation commits.
@@ -1248,8 +1245,8 @@ TEST_P(AdsPageLoadMetricsObserverTest, UntaggingAdFrame) {
TEST_P(AdsPageLoadMetricsObserverTest, MainFrameResource) {
// Start main-frame navigation
- auto navigation_simulator =
- CreateNavigationSimulator(kNonAdUrl, web_contents()->GetMainFrame());
+ auto navigation_simulator = CreateNavigationSimulator(
+ kNonAdUrl, web_contents()->GetPrimaryMainFrame());
navigation_simulator->Start();
navigation_simulator->Commit();
@@ -1293,8 +1290,8 @@ TEST_P(AdsPageLoadMetricsObserverTest, MainFrameResource) {
TEST_P(AdsPageLoadMetricsObserverTest, NoBytesLoaded_NoHistogramsRecorded) {
// Start main-frame navigation
- auto navigation_simulator =
- CreateNavigationSimulator(kNonAdUrl, web_contents()->GetMainFrame());
+ auto navigation_simulator = CreateNavigationSimulator(
+ kNonAdUrl, web_contents()->GetPrimaryMainFrame());
navigation_simulator->Start();
navigation_simulator->Commit();
diff --git a/chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.cc b/chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.cc
index 9993f84326f..b07ba5b3386 100644
--- a/chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.cc
+++ b/chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.cc
@@ -26,18 +26,23 @@ void AggregateFrameData::UpdateCpuUsage(base::TimeTicks update_time,
void AggregateFrameData::ProcessResourceLoadInFrame(
const mojom::ResourceDataUpdatePtr& resource,
- bool is_main_frame) {
+ bool is_outermost_main_frame) {
resource_data_.ProcessResourceLoad(resource);
- if (is_main_frame)
- main_frame_resource_data_.ProcessResourceLoad(resource);
+ if (is_outermost_main_frame) {
+ outermost_main_frame_resource_data_.ProcessResourceLoad(resource);
+ }
}
void AggregateFrameData::AdjustAdBytes(int64_t unaccounted_ad_bytes,
ResourceMimeType mime_type,
- bool is_main_frame) {
+ bool is_outermost_main_frame) {
+ // TODO(https://crbug.com/1301880): Test coverage isn't enough for this
+ // method. Add more tests.
resource_data_.AdjustAdBytes(unaccounted_ad_bytes, mime_type);
- if (is_main_frame)
- main_frame_resource_data_.AdjustAdBytes(unaccounted_ad_bytes, mime_type);
+ if (is_outermost_main_frame) {
+ outermost_main_frame_resource_data_.AdjustAdBytes(unaccounted_ad_bytes,
+ mime_type);
+ }
}
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.h b/chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.h
index 1bf07aa6ff8..0cf1c9bff4c 100644
--- a/chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.h
+++ b/chromium/components/page_load_metrics/browser/observers/ad_metrics/aggregate_frame_data.h
@@ -22,12 +22,12 @@ class AggregateFrameData {
~AggregateFrameData();
void ProcessResourceLoadInFrame(const mojom::ResourceDataUpdatePtr& resource,
- bool is_main_frame);
+ bool is_outermost_main_frame);
// Adjusts the overall page and potentially main frame ad bytes.
void AdjustAdBytes(int64_t unaccounted_ad_bytes,
ResourceMimeType mime_type,
- bool is_main_frame);
+ bool is_outermost_main_frame);
// Updates the cpu usage for the page, given whether update is for an ad.
void UpdateCpuUsage(base::TimeTicks update_time,
@@ -77,16 +77,16 @@ class AggregateFrameData {
}
// Updates the memory for the main frame of the page.
- void update_main_frame_memory(int64_t delta_memory) {
- main_frame_memory_.UpdateUsage(delta_memory);
+ void update_outermost_main_frame_memory(int64_t delta_memory) {
+ outermost_main_frame_memory_.UpdateUsage(delta_memory);
}
// Updates the total ad cpu usage for the page.
void update_ad_cpu_usage(base::TimeDelta usage) { ad_cpu_usage_ += usage; }
// Get the total memory usage for this page.
- int64_t main_frame_max_memory() const {
- return main_frame_memory_.max_bytes_used();
+ int64_t outermost_main_frame_max_memory() const {
+ return outermost_main_frame_memory_.max_bytes_used();
}
// Get the total cpu usage of this page.
@@ -95,8 +95,8 @@ class AggregateFrameData {
// Accessor for the total resource data of the page.
const ResourceLoadAggregator& resource_data() const { return resource_data_; }
- const ResourceLoadAggregator& main_frame_resource_data() const {
- return main_frame_resource_data_;
+ const ResourceLoadAggregator& outermost_main_frame_resource_data() const {
+ return outermost_main_frame_resource_data_;
}
private:
@@ -108,12 +108,12 @@ class AggregateFrameData {
base::TimeDelta cpu_usage_ = base::TimeDelta();
base::TimeDelta ad_cpu_usage_ = base::TimeDelta();
- // The memory used by the main frame.
- MemoryUsageAggregator main_frame_memory_;
+ // The memory used by the outermost main frame.
+ MemoryUsageAggregator outermost_main_frame_memory_;
// The resource data for this page.
ResourceLoadAggregator resource_data_;
- ResourceLoadAggregator main_frame_resource_data_;
+ ResourceLoadAggregator outermost_main_frame_resource_data_;
// The peak cpu usages for this page.
PeakCpuAggregator total_peak_cpu_;
diff --git a/chromium/components/page_load_metrics/browser/observers/ad_metrics/frame_tree_data.cc b/chromium/components/page_load_metrics/browser/observers/ad_metrics/frame_tree_data.cc
index 651547b0161..19aa321c469 100644
--- a/chromium/components/page_load_metrics/browser/observers/ad_metrics/frame_tree_data.cc
+++ b/chromium/components/page_load_metrics/browser/observers/ad_metrics/frame_tree_data.cc
@@ -245,10 +245,11 @@ void FrameTreeData::UpdateForNavigation(
SetFrameSize(*(render_frame_host->GetFrameSize()));
// For frames triggered on render, their origin is their parent's origin.
- origin_status_ = AdsPageLoadMetricsObserver::IsSubframeSameOriginToMainFrame(
- render_frame_host)
- ? OriginStatus::kSame
- : OriginStatus::kCross;
+ origin_status_ =
+ AdsPageLoadMetricsObserver::IsFrameSameOriginToOutermostMainFrame(
+ render_frame_host)
+ ? OriginStatus::kSame
+ : OriginStatus::kCross;
root_frame_depth_ = GetFullFrameDepth(render_frame_host);
}
diff --git a/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.cc b/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.cc
index cd48b17da9c..11195dc2375 100644
--- a/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.cc
+++ b/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.cc
@@ -11,15 +11,22 @@ namespace page_load_metrics {
namespace {
-// Calculates the combined length of a set of line segments. This counts
-// each overlapping area a single time and does not include areas where there
-// is no line segment.
+int CalculateIntersectedLength(int start1, int end1, int start2, int end2) {
+ DCHECK_LE(start1, end1);
+ DCHECK_LE(start2, end2);
+
+ return std::max(0, std::min(end1, end2) - std::max(start1, start2));
+}
+
+// Calculates the combined length of a set of line segments within boundaries.
+// This counts each overlapping area a single time and does not include areas
+// where there is no line segment.
//
// TODO(https://crbug.com/1068586): Optimize segment length calculation.
// AddSegment and RemoveSegment are both logarithmic operations, making this
// linearithmic with the number of segments. However the expected number
// of segments at any given time in the density calculation is low.
-class SegmentLength {
+class BoundedSegmentLength {
public:
// An event to process corresponding to the left or right point of each
// line segment.
@@ -61,26 +68,36 @@ class SegmentLength {
std::set<SegmentEvent>::const_iterator end_it;
};
- SegmentLength() = default;
+ BoundedSegmentLength(int bound_start, int bound_end)
+ : bound_start_(bound_start), bound_end_(bound_end) {
+ DCHECK_LE(bound_start_, bound_end_);
+ }
- SegmentLength(const SegmentLength&) = delete;
- SegmentLength& operator=(const SegmentLength&) = delete;
+ BoundedSegmentLength(const BoundedSegmentLength&) = delete;
+ BoundedSegmentLength& operator=(const BoundedSegmentLength&) = delete;
- ~SegmentLength() = default;
+ ~BoundedSegmentLength() = default;
// Add a line segment to the set of active line segments, the segment
// corresponds to the bottom or top of a rect.
void AddSegment(int segment_id, int start, int end) {
- // Safe as insert will never return an invalid iterator, it will
- // point to the existing element if already in the set.
- auto start_it =
- active_segments_
- .insert(SegmentEvent(segment_id, start, true /*is_segment_start*/))
- .first;
- auto end_it =
- active_segments_
- .insert(SegmentEvent(segment_id, end, false /*is_segment_start*/))
- .first;
+ DCHECK_LE(start, end);
+
+ int clipped_start = std::max(bound_start_, start);
+ int clipped_end = std::min(bound_end_, end);
+ if (clipped_start >= clipped_end)
+ return;
+
+ // Safe as insert will never return an invalid iterator, it will point to
+ // the existing element if already in the set.
+ auto start_it = active_segments_
+ .insert(SegmentEvent(segment_id, clipped_start,
+ true /*is_segment_start*/))
+ .first;
+ auto end_it = active_segments_
+ .insert(SegmentEvent(segment_id, clipped_end,
+ false /*is_segment_start*/))
+ .first;
segment_event_iterators_.emplace(
segment_id, SegmentEventSetIterators(start_it, end_it));
@@ -89,7 +106,8 @@ class SegmentLength {
// Remove a segment from the set of active line segmnets.
void RemoveSegment(int segment_id) {
auto it = segment_event_iterators_.find(segment_id);
- DCHECK(it != segment_event_iterators_.end());
+ if (it == segment_event_iterators_.end())
+ return;
const SegmentEventSetIterators& set_its = it->second;
active_segments_.erase(set_its.start_it);
@@ -98,7 +116,7 @@ class SegmentLength {
}
// Calculate the combined length of segments in the active set of segments by
- // iterating over the the sorted set of segment events.
+ // iterating over the sorted set of segment events.
absl::optional<int> Length() {
base::CheckedNumeric<int> length = 0;
absl::optional<int> last_event_pos;
@@ -128,6 +146,9 @@ class SegmentLength {
}
private:
+ int bound_start_;
+ int bound_end_;
+
std::set<SegmentEvent> active_segments_;
// Map from the segment_id passed by user to the Segment struct.
@@ -151,18 +172,43 @@ PageAdDensityTracker::RectEventSetIterators::RectEventSetIterators(
PageAdDensityTracker::RectEventSetIterators::RectEventSetIterators(
const RectEventSetIterators& other) = default;
-PageAdDensityTracker::PageAdDensityTracker() = default;
+PageAdDensityTracker::PageAdDensityTracker() {
+ start_time_ = base::TimeTicks::Now();
+ last_viewport_density_recording_time_ = start_time_;
+}
PageAdDensityTracker::~PageAdDensityTracker() = default;
-int PageAdDensityTracker::MaxPageAdDensityByHeight() {
+int PageAdDensityTracker::MaxPageAdDensityByHeight() const {
return max_page_ad_density_by_height_;
}
-int PageAdDensityTracker::MaxPageAdDensityByArea() {
+int PageAdDensityTracker::MaxPageAdDensityByArea() const {
return max_page_ad_density_by_area_;
}
+int PageAdDensityTracker::AverageViewportAdDensityByArea() const {
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ base::TimeDelta total_elapsed_time = now - start_time_;
+ if (total_elapsed_time == base::TimeDelta())
+ return -1;
+
+ base::TimeDelta last_elapsed_time =
+ now - last_viewport_density_recording_time_;
+
+ double total_viewport_ad_density_by_area =
+ cumulative_viewport_ad_density_by_area_ +
+ (last_viewport_ad_density_by_area_ * last_elapsed_time.InMicrosecondsF());
+
+ return std::lround(total_viewport_ad_density_by_area /
+ total_elapsed_time.InMicrosecondsF());
+}
+
+int PageAdDensityTracker::ViewportAdDensityByArea() const {
+ return last_viewport_ad_density_by_area_;
+}
+
void PageAdDensityTracker::AddRect(int rect_id, const gfx::Rect& rect) {
// Check that we do not already have rect events for the rect.
DCHECK(rect_events_iterators_.find(rect_id) == rect_events_iterators_.end());
@@ -187,7 +233,9 @@ void PageAdDensityTracker::AddRect(int rect_id, const gfx::Rect& rect) {
// TODO(https://crbug.com/1068586): Improve performance by adding additional
// throttling to only calculate when max density can decrease (frame deleted
// or moved).
- CalculateDensity();
+ CalculatePageAdDensity();
+
+ CalculateViewportAdDensity();
}
void PageAdDensityTracker::RemoveRect(int rect_id) {
@@ -203,20 +251,62 @@ void PageAdDensityTracker::RemoveRect(int rect_id) {
}
void PageAdDensityTracker::UpdateMainFrameRect(const gfx::Rect& rect) {
- if (!last_main_frame_size_ || rect != *last_main_frame_size_) {
- last_main_frame_size_ = rect;
- CalculateDensity();
+ if (rect == last_main_frame_rect_)
+ return;
+
+ last_main_frame_rect_ = rect;
+ CalculatePageAdDensity();
+}
+
+void PageAdDensityTracker::UpdateMainFrameViewportRect(const gfx::Rect& rect) {
+ if (rect == last_main_frame_viewport_rect_)
+ return;
+
+ last_main_frame_viewport_rect_ = rect;
+ CalculateViewportAdDensity();
+}
+
+void PageAdDensityTracker::CalculatePageAdDensity() {
+ AdDensityCalculationResult result =
+ CalculateDensityWithin(last_main_frame_rect_);
+ if (result.ad_density_by_area) {
+ max_page_ad_density_by_area_ = std::max(result.ad_density_by_area.value(),
+ max_page_ad_density_by_area_);
+ }
+ if (result.ad_density_by_height) {
+ max_page_ad_density_by_height_ = std::max(
+ result.ad_density_by_height.value(), max_page_ad_density_by_height_);
}
}
+void PageAdDensityTracker::CalculateViewportAdDensity() {
+ AdDensityCalculationResult result =
+ CalculateDensityWithin(last_main_frame_viewport_rect_);
+ if (!result.ad_density_by_area)
+ return;
+
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeDelta elapsed_time = now - last_viewport_density_recording_time_;
+
+ cumulative_viewport_ad_density_by_area_ +=
+ last_viewport_ad_density_by_area_ * elapsed_time.InMicrosecondsF();
+
+ last_viewport_density_recording_time_ = now;
+
+ last_viewport_ad_density_by_area_ = result.ad_density_by_area.value();
+}
+
// Ad density measurement uses a modified Bentley's Algorithm, the high level
// approach is described on: http://jeffe.cs.illinois.edu/open/klee.html.
-void PageAdDensityTracker::CalculateDensity() {
- // Cannot calculate density if there is no main frame rect.
- if (!last_main_frame_size_)
- return;
+PageAdDensityTracker::AdDensityCalculationResult
+PageAdDensityTracker::CalculateDensityWithin(const gfx::Rect& bounding_rect) {
+ // Cannot calculate density if `bounding_rect` is empty.
+ if (bounding_rect.IsEmpty())
+ return {};
- SegmentLength segment_length_tracker;
+ BoundedSegmentLength horizontal_segment_length_tracker(
+ /*bound_start=*/bounding_rect.x(),
+ /*bound_end=*/bounding_rect.x() + bounding_rect.width());
absl::optional<int> last_y;
base::CheckedNumeric<int> total_area = 0;
@@ -224,11 +314,10 @@ void PageAdDensityTracker::CalculateDensity() {
for (const auto& rect_event : rect_events_) {
if (!last_y) {
DCHECK(rect_event.is_bottom);
- segment_length_tracker.AddSegment(
+ horizontal_segment_length_tracker.AddSegment(
rect_event.rect_id, rect_event.rect.x(),
rect_event.rect.x() + rect_event.rect.width());
- last_y =
- rect_event.is_bottom ? rect_event.rect.bottom() : rect_event.rect.y();
+ last_y = rect_event.rect.bottom();
}
int current_y =
@@ -236,30 +325,35 @@ void PageAdDensityTracker::CalculateDensity() {
DCHECK_LE(current_y, last_y.value());
// If the segment length value is invalid, skip this ad density calculation.
- absl::optional<int> segment_length = segment_length_tracker.Length();
- if (!segment_length)
- return;
+ absl::optional<int> horizontal_segment_length =
+ horizontal_segment_length_tracker.Length();
+ if (!horizontal_segment_length)
+ return {};
// Check that the segment length multiplied by the height of the block
// does not overflow an int.
- base::CheckedNumeric<int> current_area = *segment_length;
- current_area *= (last_y.value() - current_y);
+ base::CheckedNumeric<int> current_area = *horizontal_segment_length;
+ int vertical_segment_length = CalculateIntersectedLength(
+ current_y, last_y.value(), bounding_rect.y(), bounding_rect.bottom());
+
+ current_area *= vertical_segment_length;
+
if (!current_area.IsValid())
- return;
+ return {};
- total_area += *segment_length * (last_y.value() - current_y);
+ total_area += current_area;
- if (*segment_length > 0)
- total_height += (last_y.value() - current_y);
+ if (*horizontal_segment_length > 0)
+ total_height += vertical_segment_length;
// As we are iterating from the bottom of the page to the top, add segments
// when we see the start (bottom) of a new rect.
if (rect_event.is_bottom) {
- segment_length_tracker.AddSegment(
+ horizontal_segment_length_tracker.AddSegment(
rect_event.rect_id, rect_event.rect.x(),
rect_event.rect.x() + rect_event.rect.width());
} else {
- segment_length_tracker.RemoveSegment(rect_event.rect_id);
+ horizontal_segment_length_tracker.RemoveSegment(rect_event.rect_id);
}
last_y = current_y;
}
@@ -267,21 +361,28 @@ void PageAdDensityTracker::CalculateDensity() {
// If the measured height or area is invalid, skip recording this ad density
// calculation.
if (!total_height.IsValid() || !total_area.IsValid())
- return;
+ return {};
+
+ AdDensityCalculationResult result;
+ // TODO(yaoxia): For viewport density we don't care about density by height.
+ // Consider having a param which skips the height calculation.
base::CheckedNumeric<int> ad_density_by_height =
- total_height * 100 / last_main_frame_size_->height();
- if (ad_density_by_height.IsValid() &&
- ad_density_by_height.ValueOrDie() > max_page_ad_density_by_height_)
- max_page_ad_density_by_height_ = ad_density_by_height.ValueOrDie();
+ total_height * 100 / bounding_rect.height();
+ if (ad_density_by_height.IsValid()) {
+ result.ad_density_by_height = ad_density_by_height.ValueOrDie();
+ }
// Invalidate the check numeric if the checked area is invalid.
base::CheckedNumeric<int> ad_density_by_area =
total_area * 100 /
- (last_main_frame_size_->size().GetCheckedArea().ValueOrDefault(0));
- if (ad_density_by_area.IsValid() &&
- ad_density_by_area.ValueOrDie() > max_page_ad_density_by_area_)
- max_page_ad_density_by_area_ = ad_density_by_area.ValueOrDie();
+ (bounding_rect.size().GetCheckedArea().ValueOrDefault(
+ std::numeric_limits<int>::max()));
+ if (ad_density_by_area.IsValid()) {
+ result.ad_density_by_area = ad_density_by_area.ValueOrDie();
+ }
+
+ return result;
}
bool PageAdDensityTracker::RectEvent::operator<(const RectEvent& rhs) const {
diff --git a/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.h b/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.h
index cadd162911f..2a6ff473de8 100644
--- a/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.h
+++ b/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.h
@@ -8,6 +8,7 @@
#include <set>
#include <unordered_map>
+#include "base/time/time.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/rect.h"
@@ -15,12 +16,19 @@ namespace page_load_metrics {
// Tracks the ad density of a page through the page's lifecycle.
// It has the following usage:
-// 1. Set subframe and mainframe rects using subframe and mainframe rect
-// operations (AddRect, RemoveRect, UpdateMainFrameRect).
-// 2. Once a page has a main frame rect, get current density using
-// DensityByHeight or DensityByArea.
+// 1. Set subframe, mainframe, and viewport rects using operations (AddRect,
+// RemoveRect, UpdateMainFrameRect, UpdateMainFrameViewportRect).
+// 2. When the main frame rect or a subframe rect is updated, get current
+// page ad density using CalculatePageAdDensity.
+// 3. When the main frame viewport rect or a subframe rect is updated, get
+// current viewport ad density using CalculateViewportAdDensity.
class PageAdDensityTracker {
public:
+ struct AdDensityCalculationResult {
+ absl::optional<int> ad_density_by_height;
+ absl::optional<int> ad_density_by_area;
+ };
+
PageAdDensityTracker();
~PageAdDensityTracker();
@@ -35,18 +43,33 @@ class PageAdDensityTracker {
void RemoveRect(int rect_id);
// Operations to track the main frame dimensions. The main frame rect has to
- // be set to calculate density.
+ // be set to calculate the page ad density.
void UpdateMainFrameRect(const gfx::Rect& rect);
+ // Operations to track the main frame viewport position and dimensions. This
+ // rect has to be set to calculate the viewport ad density.
+ void UpdateMainFrameViewportRect(const gfx::Rect& rect);
+
// Returns the density by height, as a value from 0-100. If the density
// calculation fails (i.e. no main frame size), this returns -1. Percentage
// density by height is calculated as the the combined height of ads divided
// by the page's height.
- int MaxPageAdDensityByHeight();
+ int MaxPageAdDensityByHeight() const;
// Returns the density by area, as a value from 0-100. If the density
// calculation fails (i.e. no main frame size), this returns -1.
- int MaxPageAdDensityByArea();
+ int MaxPageAdDensityByArea() const;
+
+ // Returns the moving average of viewport ad density by area, as a value from
+ // 0-100. If the density calculation fails (i.e. elapsed time is 0), this
+ // returns -1. If the density calculation didn't happen (i.e. no main frame
+ // viewport), this returns 0.
+ int AverageViewportAdDensityByArea() const;
+
+ // Returns the last calculated viewport ad density by area, as a value from
+ // 0-100. If the density calculation didn't happen (i.e. no main frame
+ // viewport), this returns 0.
+ int ViewportAdDensityByArea() const;
private:
// An event to process corresponding to the top or bottom of each rect.
@@ -76,9 +99,13 @@ class PageAdDensityTracker {
std::set<RectEvent>::const_iterator bottom_it;
};
- // Calculates the combined area and height of the set of rects, this populates
- // total_height_ and total_area_.
- void CalculateDensity();
+ void CalculatePageAdDensity();
+ void CalculateViewportAdDensity();
+
+ // Calculates the combined area and height of the set of rects bounded by
+ // `bounding_rect`, and further derive the ad density by area and height.
+ AdDensityCalculationResult CalculateDensityWithin(
+ const gfx::Rect& bounding_rect);
// Maintain a sorted set of rect events for use in calculating ad area.
std::set<RectEvent> rect_events_;
@@ -92,9 +119,27 @@ class PageAdDensityTracker {
int max_page_ad_density_by_area_ = -1;
int max_page_ad_density_by_height_ = -1;
- absl::optional<gfx::Rect> last_main_frame_size_;
+ // The last main frame size (a rectangle at position (0,0)).
+ gfx::Rect last_main_frame_rect_;
+
+ // The last main frame viewport rectangle in `last_main_frame_rect_`'s
+ // coordinate system.
+ gfx::Rect last_main_frame_viewport_rect_;
+
+ // The time when this `PageAdDensityTracker` is created.
+ base::TimeTicks start_time_;
+
+ // The last time when `last_viewport_ad_density_by_area_` is recorded/checked
+ // and `cumulative_viewport_ad_density_by_area_` is updated.
+ base::TimeTicks last_viewport_density_recording_time_;
+
+ // The last calculated ad density within the main frame viewport.
+ int last_viewport_ad_density_by_area_ = 0;
+
+ // The time cumulative ad density within the main frame viewport.
+ double cumulative_viewport_ad_density_by_area_ = 0;
};
} // namespace page_load_metrics
-#endif // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_AD_METRICS_PAGE_AD_DENSITY_TRACKER_H_"
+#endif // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_AD_METRICS_PAGE_AD_DENSITY_TRACKER_H_
diff --git a/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker_unittest.cc b/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker_unittest.cc
index f039bde69ac..463e3e9b108 100644
--- a/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker_unittest.cc
@@ -4,13 +4,14 @@
#include <limits>
+#include "base/test/task_environment.h"
#include "components/page_load_metrics/browser/observers/ad_metrics/page_ad_density_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
namespace page_load_metrics {
-TEST(PageAdDensityTrackerTest, MultipleRects_MaxPageDensityByAreaCalculated) {
+TEST(PageAdDensityTrackerTest, MultipleRects_MaxDensity) {
PageAdDensityTracker tracker;
// Page ad density is -1 before there is a main frame or subframes.
@@ -19,37 +20,20 @@ TEST(PageAdDensityTrackerTest, MultipleRects_MaxPageDensityByAreaCalculated) {
tracker.UpdateMainFrameRect(gfx::Rect(0, 0, 100, 100));
tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 100, 10));
EXPECT_EQ(tracker.MaxPageAdDensityByArea(), 10);
+ EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 10);
- tracker.AddRect(2 /* rect_id */, gfx::Rect(5, 5, 100, 10));
+ tracker.AddRect(2 /* rect_id */, gfx::Rect(50, 0, 100, 20));
EXPECT_EQ(tracker.MaxPageAdDensityByArea(), 15);
+ EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 20);
tracker.AddRect(3 /* rect_id */, gfx::Rect(50, 50, 50, 50));
EXPECT_EQ(tracker.MaxPageAdDensityByArea(), 40);
+ EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 70);
// Removing a rect should not change the maximum ad density.
tracker.RemoveRect(3 /* rect_id */);
EXPECT_EQ(tracker.MaxPageAdDensityByArea(), 40);
-}
-
-TEST(PageAdDensityTrackerTest, MultipleRects_MaxPageDensityByHeightCalculated) {
- PageAdDensityTracker tracker;
-
- // Page ad density is -1 before there is a main frame or subframes.
- EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), -1);
-
- tracker.UpdateMainFrameRect(gfx::Rect(0, 0, 100, 100));
- tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 100, 10));
- EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 10);
-
- tracker.AddRect(2 /* rect_id */, gfx::Rect(5, 5, 100, 10));
- EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 15);
-
- tracker.AddRect(3 /* rect_id */, gfx::Rect(50, 50, 50, 50));
- EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 65);
-
- // Removing a rect should not change the maximum ad density.
- tracker.RemoveRect(3 /* rect_id */);
- EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 65);
+ EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 70);
}
// Remove a rect that was added twice, the second RemoveRect is
@@ -79,7 +63,7 @@ TEST(PageAdDensityTrackerTest, SeperateRects_SameDimensions) {
}
// Create 2 rects whose total area overflow an int.
-TEST(PageAdDensityTrackerTest, OverflowTotalAreaAndHeight) {
+TEST(PageAdDensityTrackerTest, TwoRectsOverflowTotalAreaAndHeight) {
PageAdDensityTracker tracker;
tracker.AddRect(1 /* rect_id */, gfx::Rect(std::numeric_limits<int>::min(), 0,
@@ -93,8 +77,24 @@ TEST(PageAdDensityTrackerTest, OverflowTotalAreaAndHeight) {
// Update main frame rect to force a calculation.
tracker.UpdateMainFrameRect(gfx::Rect(0, 0, 100, 100));
- // Density should not be updated as the sum of area
- // or height overflows.
+ // Density should be 0, as there's no intersected area.
+ EXPECT_EQ(tracker.MaxPageAdDensityByArea(), 0);
+ EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 0);
+}
+
+// Add a main frame rect whose area overflow an int.
+TEST(PageAdDensityTrackerTest, OverflowTotalAreaAndHeight) {
+ PageAdDensityTracker tracker;
+
+ tracker.AddRect(1 /* rect_id */,
+ gfx::Rect(0, 0, std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()));
+
+ // Update main frame rect to force a calculation.
+ tracker.UpdateMainFrameRect(gfx::Rect(0, 0, std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()));
+
+ // Density should not be updated as the sum of area or height overflows.
EXPECT_EQ(tracker.MaxPageAdDensityByArea(), -1);
EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), -1);
}
@@ -109,7 +109,237 @@ TEST(PageAdDensityTrackerTest, RectAtSpecialPosition) {
tracker.AddRect(1 /* rect_id */, gfx::Rect(-1, -1, 1, 1));
EXPECT_EQ(tracker.MaxPageAdDensityByArea(), 0);
- EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 1);
+ EXPECT_EQ(tracker.MaxPageAdDensityByHeight(), 0);
+}
+
+// Add a viewport rect whose area overflow an int.
+TEST(PageAdDensityTrackerTest, ViewportAdDensity_OverflowViewportArea) {
+ PageAdDensityTracker tracker;
+
+ tracker.AddRect(1 /* rect_id */,
+ gfx::Rect(0, 0, std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()));
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(
+ 0, 0, std::numeric_limits<int>::max(), std::numeric_limits<int>::max()));
+
+ // Density should not be updated as the sum of ara overflows.
+ EXPECT_EQ(tracker.ViewportAdDensityByArea(), 0);
+}
+
+TEST(PageAdDensityTrackerTest, ViewportAdDensity_RectSameSize) {
+ PageAdDensityTracker tracker;
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 100, 100));
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 100, 100));
+
+ EXPECT_EQ(tracker.ViewportAdDensityByArea(), 100);
+}
+
+TEST(PageAdDensityTrackerTest, ViewportAdDensity_RectHalfSize) {
+ PageAdDensityTracker tracker;
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 100, 100));
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 100));
+
+ EXPECT_EQ(tracker.ViewportAdDensityByArea(), 50);
+}
+
+TEST(PageAdDensityTrackerTest, ViewportAdDensity_RectOutOfViewport) {
+ PageAdDensityTracker tracker;
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 100, 100));
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(100, 0, 100, 100));
+
+ EXPECT_EQ(tracker.ViewportAdDensityByArea(), 0);
+}
+
+TEST(PageAdDensityTrackerTest, ViewportAdDensity_RectClipsViewport) {
+ PageAdDensityTracker tracker;
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 100, 100));
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(50, 50, 100, 100));
+
+ EXPECT_EQ(tracker.ViewportAdDensityByArea(), 25);
+}
+
+TEST(PageAdDensityTrackerTest, ViewportAdDensity_TwoRectsClipViewport) {
+ PageAdDensityTracker tracker;
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 100, 100));
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(30, 70, 100, 100));
+ tracker.AddRect(2 /* rect_id */, gfx::Rect(70, 30, 100, 100));
+
+ EXPECT_EQ(tracker.ViewportAdDensityByArea(),
+ 33); // ((30 * 70 * 2) - 30 * 30) / 10000 * 100
+}
+
+TEST(PageAdDensityTrackerTest,
+ AverageViewportAdDensity_NoTimeLapseSincePageLoad) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 100, 100));
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 50));
+
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), -1);
+}
+
+TEST(PageAdDensityTrackerTest, AverageViewportAdDensity_NoViewportRectUpdate) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 50));
+ task_environment.FastForwardBy(base::Seconds(1));
+
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 0);
+}
+
+TEST(PageAdDensityTrackerTest, AverageViewportAdDensity_NoAdRectUpdate) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 100, 100));
+ task_environment.FastForwardBy(base::Seconds(1));
+
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 0);
+}
+
+TEST(PageAdDensityTrackerTest,
+ AverageViewportAdDensity_CalculateInOneSecondAndImmediateQuery) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ task_environment.FastForwardBy(base::Seconds(1));
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 50, 50));
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 50));
+
+ EXPECT_EQ(tracker.ViewportAdDensityByArea(), 100);
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 0);
+}
+
+TEST(PageAdDensityTrackerTest,
+ AverageViewportAdDensity_CalculateInOneSecondAndQueryLater) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ task_environment.FastForwardBy(base::Seconds(1));
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 50, 50));
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 50));
+
+ task_environment.FastForwardBy(base::Seconds(1));
+
+ EXPECT_EQ(tracker.ViewportAdDensityByArea(), 100);
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 50);
+}
+
+TEST(PageAdDensityTrackerTest,
+ AverageViewportAdDensity_ViewportRect_SizeUpdate) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 50, 100));
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 50));
+
+ task_environment.FastForwardBy(base::Seconds(1));
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 50);
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 50, 50));
+
+ task_environment.FastForwardBy(base::Seconds(1));
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 75);
+}
+
+TEST(PageAdDensityTrackerTest,
+ AverageViewportAdDensity_ViewportRect_OffsetUpdate) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 50));
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 50, 50));
+
+ task_environment.FastForwardBy(base::Seconds(1));
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 100);
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(50, 50, 50, 50));
+
+ task_environment.FastForwardBy(base::Seconds(1));
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 50);
+}
+
+TEST(PageAdDensityTrackerTest, AverageViewportAdDensity_AdRectUpdate) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 50));
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 50, 100));
+
+ task_environment.FastForwardBy(base::Seconds(1));
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 50);
+
+ tracker.RemoveRect(1 /* rect_id */);
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 100));
+
+ task_environment.FastForwardBy(base::Seconds(1));
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 75);
+}
+
+TEST(PageAdDensityTrackerTest,
+ AverageViewportAdDensity_MultipleUnequalTimePeriods) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 50));
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(0, 0, 50, 100));
+
+ task_environment.FastForwardBy(base::Seconds(1));
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), 50);
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(25, 0, 50, 100));
+
+ task_environment.FastForwardBy(base::Seconds(2));
+
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(),
+ std::lround((50 * 1 + 25 * 2) / 3.0));
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(50, 0, 50, 100));
+
+ task_environment.FastForwardBy(base::Seconds(3));
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(),
+ std::lround((50 * 1 + 25 * 2) / 6.0));
+}
+
+TEST(PageAdDensityTrackerTest,
+ AverageViewportAdDensity_MultipleRectsInViewportRect) {
+ base::test::SingleThreadTaskEnvironment task_environment(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+ PageAdDensityTracker tracker;
+
+ tracker.UpdateMainFrameViewportRect(gfx::Rect(50, 0, 50, 100));
+
+ // Rect(1) is not within the viewport.
+ tracker.AddRect(1 /* rect_id */, gfx::Rect(0, 0, 50, 50));
+
+ // Rect(2) occupy 1/4 of the viewport.
+ tracker.AddRect(2 /* rect_id */, gfx::Rect(25, 0, 50, 50));
+
+ // Rect(3) occupy 1/4 of the viewport; 1/8 of the viewport is occupied by both
+ // Rect(2) and Rect(3)
+ tracker.AddRect(3 /* rect_id */, gfx::Rect(25, 25, 50, 50));
+
+ task_environment.FastForwardBy(base::Seconds(1));
+ EXPECT_EQ(tracker.AverageViewportAdDensityByArea(), int(3 * 100 / 8));
}
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc
index 26a19a1bbaf..75705072103 100644
--- a/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.cc
@@ -111,6 +111,14 @@ BackForwardCachePageLoadMetricsObserver::OnStart(
return CONTINUE_OBSERVING;
}
+// TODO(https://crbug.com/1317494): Audit and use appropriate policy.
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+BackForwardCachePageLoadMetricsObserver::OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) {
+ return STOP_OBSERVING;
+}
+
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
BackForwardCachePageLoadMetricsObserver::OnHidden(
const page_load_metrics::mojom::PageLoadTiming& timing) {
diff --git a/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h
index b073e830cff..5db58c0a030 100644
--- a/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer.h
@@ -65,6 +65,9 @@ class BackForwardCachePageLoadMetricsObserver
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url,
bool started_in_foreground) override;
+ page_load_metrics::PageLoadMetricsObserver::ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override;
page_load_metrics::PageLoadMetricsObserver::ObservePolicy OnHidden(
const page_load_metrics::mojom::PageLoadTiming& timing) override;
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
diff --git a/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer_unittest.cc b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer_unittest.cc
index e0d7b0fb4a0..bffe402abe3 100644
--- a/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/observers/back_forward_cache_page_load_metrics_observer_unittest.cc
@@ -133,7 +133,7 @@ TEST_F(BackForwardCachePageLoadMetricsObserverTest,
navigation_handle_.set_is_served_from_bfcache(true);
tester()->SimulateMetadataUpdate(NonAmpMetadata(),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
observer_->OnRestoreFromBackForwardCache(timing_, &navigation_handle_);
AssertHistoryNavigationRecordedAmpNavigation(false);
@@ -145,7 +145,7 @@ TEST_F(BackForwardCachePageLoadMetricsObserverTest,
navigation_handle_.set_is_served_from_bfcache(true);
tester()->SimulateMetadataUpdate(AmpMetadata(),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
observer_->OnRestoreFromBackForwardCache(timing_, &navigation_handle_);
AssertHistoryNavigationRecordedAmpNavigation(true);
@@ -157,7 +157,7 @@ TEST_F(BackForwardCachePageLoadMetricsObserverTest,
navigation_handle_.set_is_served_from_bfcache(false);
tester()->SimulateMetadataUpdate(NonAmpMetadata(),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
// Since there was no call to observer_->OnRestoreFromBackForwardCache, there
// should be no HistoryNavigation UKM entry.
diff --git a/chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.cc b/chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.cc
index 78c9d3e850a..be6321b8010 100644
--- a/chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.cc
+++ b/chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.cc
@@ -10,7 +10,6 @@
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/site_instance.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "third_party/blink/public/common/performance/largest_contentful_paint_type.h"
namespace page_load_metrics {
@@ -21,6 +20,21 @@ static bool g_disable_subframe_navigation_start_offset = false;
namespace {
+absl::optional<base::TimeDelta> AdjustedTime(
+ absl::optional<base::TimeDelta> candidate_time,
+ base::TimeDelta navigation_start_offset) {
+ // If |candidate_time| is not positive, this means that the candidate is an
+ // image that has not finished loading. Preserve its meaning by not adding the
+ // |navigation_start_offset|.
+ absl::optional<base::TimeDelta> new_time = absl::nullopt;
+ if (candidate_time) {
+ new_time = candidate_time->is_positive()
+ ? navigation_start_offset + candidate_time.value()
+ : base::TimeDelta();
+ }
+ return new_time;
+}
+
const ContentfulPaintTimingInfo& MergeTimingsBySizeAndTime(
const ContentfulPaintTimingInfo& timing1,
const ContentfulPaintTimingInfo& timing2) {
@@ -39,20 +53,20 @@ const ContentfulPaintTimingInfo& MergeTimingsBySizeAndTime(
// When both sizes are equal
DCHECK(timing1.Time());
DCHECK(timing2.Time());
- if (timing1.Time().value() < timing2.Time().value())
+ // The size can be nonzero while the time can be 0 since a time of 0 is sent
+ // when the image is still painting. When we merge the two
+ // |ContentfulPaintTimingInfo| objects, we should ignore the one with 0 time.
+ if (timing1.Time() == base::TimeDelta())
+ return timing2;
+ if (timing2.Time() == base::TimeDelta())
return timing1;
- return timing2;
+ return timing1.Time().value() < timing2.Time().value() ? timing1 : timing2;
}
void MergeForSubframesWithAdjustedTime(
ContentfulPaintTimingInfo* inout_timing,
- const absl::optional<base::TimeDelta>& candidate_new_time,
- const uint64_t& candidate_new_size) {
+ const ContentfulPaintTimingInfo& new_candidate) {
DCHECK(inout_timing);
- const ContentfulPaintTimingInfo new_candidate(
- candidate_new_time, candidate_new_size, inout_timing->TextOrImage(),
- inout_timing->InMainFrame(), inout_timing->Type(),
- inout_timing->ImageBPP());
const ContentfulPaintTimingInfo& merged_candidate =
MergeTimingsBySizeAndTime(new_candidate, *inout_timing);
inout_timing->Reset(merged_candidate.Time(), merged_candidate.Size(),
@@ -64,7 +78,8 @@ bool IsSubframe(content::RenderFrameHost* subframe_rfh) {
}
void Reset(ContentfulPaintTimingInfo& timing) {
- timing.Reset(absl::nullopt, 0u, /*type=*/0, /*image_bpp=*/0.0);
+ timing.Reset(absl::nullopt, 0u, blink::LargestContentfulPaintType::kNone,
+ /*image_bpp=*/0.0);
}
bool IsSameSite(const GURL& url1, const GURL& url2) {
@@ -82,7 +97,7 @@ bool IsSameSite(const GURL& url1, const GURL& url2) {
ContentfulPaintTimingInfo::ContentfulPaintTimingInfo(
LargestContentTextOrImage text_or_image,
bool in_main_frame,
- uint32_t type)
+ blink::LargestContentfulPaintType type)
: size_(0),
text_or_image_(text_or_image),
type_(type),
@@ -93,7 +108,7 @@ ContentfulPaintTimingInfo::ContentfulPaintTimingInfo(
const LargestContentTextOrImage text_or_image,
double image_bpp,
bool in_main_frame,
- uint32_t type)
+ blink::LargestContentfulPaintType type)
: time_(time),
size_(size),
text_or_image_(text_or_image),
@@ -114,7 +129,8 @@ ContentfulPaintTimingInfo::DataAsTraceValue() const {
data->SetBoolean("inMainFrame", InMainFrame());
data->SetBoolean(
"isAnimated",
- Type() & blink::LargestContentfulPaintType::kLCPTypeAnimatedImage);
+ (Type() & blink::LargestContentfulPaintType::kAnimatedImage) ==
+ blink::LargestContentfulPaintType::kAnimatedImage);
return data;
}
@@ -138,14 +154,15 @@ void LargestContentfulPaintHandler::SetTestMode(bool enabled) {
void ContentfulPaintTimingInfo::Reset(
const absl::optional<base::TimeDelta>& time,
const uint64_t& size,
- uint32_t type,
+ blink::LargestContentfulPaintType type,
double image_bpp) {
size_ = size;
time_ = time;
type_ = type;
image_bpp_ = image_bpp;
}
-ContentfulPaint::ContentfulPaint(bool in_main_frame, uint32_t type)
+ContentfulPaint::ContentfulPaint(bool in_main_frame,
+ blink::LargestContentfulPaintType type)
: text_(ContentfulPaintTimingInfo::LargestContentTextOrImage::kText,
in_main_frame,
type),
@@ -193,10 +210,13 @@ bool LargestContentfulPaintHandler::AssignTimeAndSizeForLargestContentfulPaint(
}
LargestContentfulPaintHandler::LargestContentfulPaintHandler()
- : main_frame_contentful_paint_(true /*in_main_frame*/, 0 /*type*/),
- subframe_contentful_paint_(false /*in_main_frame*/, 0 /*type*/),
- cross_site_subframe_contentful_paint_(false /*in_main_frame*/,
- 0 /*type*/) {}
+ : main_frame_contentful_paint_(true /*in_main_frame*/,
+ blink::LargestContentfulPaintType::kNone),
+ subframe_contentful_paint_(false /*in_main_frame*/,
+ blink::LargestContentfulPaintType::kNone),
+ cross_site_subframe_contentful_paint_(
+ false /*in_main_frame*/,
+ blink::LargestContentfulPaintType::kNone) {}
LargestContentfulPaintHandler::~LargestContentfulPaintHandler() = default;
@@ -251,28 +271,68 @@ void LargestContentfulPaintHandler::RecordSubframeTiming(
const base::TimeDelta& navigation_start_offset) {
UpdateFirstInputOrScrollNotified(first_input_or_scroll_notified_timestamp,
navigation_start_offset);
- MergeForSubframes(&subframe_contentful_paint_.Text(),
- largest_contentful_paint.largest_text_paint,
- largest_contentful_paint.largest_text_paint_size,
- navigation_start_offset);
- MergeForSubframes(&subframe_contentful_paint_.Image(),
- largest_contentful_paint.largest_image_paint,
- largest_contentful_paint.largest_image_paint_size,
- navigation_start_offset);
+ DCHECK(!subframe_contentful_paint_.Text().InMainFrame());
+ DCHECK(!subframe_contentful_paint_.Image().InMainFrame());
+ ContentfulPaintTimingInfo new_text_candidate(
+ AdjustedTime(largest_contentful_paint.largest_text_paint,
+ navigation_start_offset),
+ largest_contentful_paint.largest_text_paint_size,
+ ContentfulPaintTimingInfo::LargestContentTextOrImage::kText,
+ /*image_bpp=*/0.0, /*in_main_frame=*/false,
+ static_cast<blink::LargestContentfulPaintType>(
+ largest_contentful_paint.type));
+ if (IsValid(new_text_candidate.Time())) {
+ MergeForSubframesWithAdjustedTime(&subframe_contentful_paint_.Text(),
+ new_text_candidate);
+ }
+ // TODO(iclelland): Use the remainder of the fields from
+ // largest_contentful_paint to construct the ContentfulPaintTimingInfo here
+ ContentfulPaintTimingInfo new_image_candidate(
+ AdjustedTime(largest_contentful_paint.largest_image_paint,
+ navigation_start_offset),
+ largest_contentful_paint.largest_image_paint_size,
+ ContentfulPaintTimingInfo::LargestContentTextOrImage::kImage,
+ largest_contentful_paint.image_bpp, /*in_main_frame=*/false,
+ static_cast<blink::LargestContentfulPaintType>(
+ largest_contentful_paint.type));
+ if (IsValid(new_image_candidate.Time())) {
+ MergeForSubframesWithAdjustedTime(&subframe_contentful_paint_.Image(),
+ new_image_candidate);
+ }
}
void LargestContentfulPaintHandler::RecordCrossSiteSubframeTiming(
const page_load_metrics::mojom::LargestContentfulPaintTiming&
largest_contentful_paint,
const base::TimeDelta& navigation_start_offset) {
- MergeForSubframes(&cross_site_subframe_contentful_paint_.Text(),
- largest_contentful_paint.largest_text_paint,
- largest_contentful_paint.largest_text_paint_size,
- navigation_start_offset);
- MergeForSubframes(&cross_site_subframe_contentful_paint_.Image(),
- largest_contentful_paint.largest_image_paint,
- largest_contentful_paint.largest_image_paint_size,
- navigation_start_offset);
+ DCHECK(!cross_site_subframe_contentful_paint_.Text().InMainFrame());
+ DCHECK(!cross_site_subframe_contentful_paint_.Image().InMainFrame());
+ ContentfulPaintTimingInfo new_text_candidate(
+ AdjustedTime(largest_contentful_paint.largest_text_paint,
+ navigation_start_offset),
+ largest_contentful_paint.largest_text_paint_size,
+ ContentfulPaintTimingInfo::LargestContentTextOrImage::kText,
+ /*image_bpp=*/0.0, /*in_main_frame=*/false,
+ static_cast<blink::LargestContentfulPaintType>(
+ largest_contentful_paint.type));
+ if (IsValid(new_text_candidate.Time())) {
+ MergeForSubframesWithAdjustedTime(
+ &cross_site_subframe_contentful_paint_.Text(), new_text_candidate);
+ }
+ // TODO(iclelland): Use the remainder of the fields from
+ // largest_contentful_paint to construct the ContentfulPaintTimingInfo here
+ ContentfulPaintTimingInfo new_image_candidate(
+ AdjustedTime(largest_contentful_paint.largest_image_paint,
+ navigation_start_offset),
+ largest_contentful_paint.largest_image_paint_size,
+ ContentfulPaintTimingInfo::LargestContentTextOrImage::kImage,
+ largest_contentful_paint.image_bpp, /*in_main_frame=*/false,
+ static_cast<blink::LargestContentfulPaintType>(
+ largest_contentful_paint.type));
+ if (IsValid(new_image_candidate.Time())) {
+ MergeForSubframesWithAdjustedTime(
+ &cross_site_subframe_contentful_paint_.Image(), new_image_candidate);
+ }
}
void LargestContentfulPaintHandler::RecordMainFrameTiming(
@@ -286,14 +346,17 @@ void LargestContentfulPaintHandler::RecordMainFrameTiming(
if (IsValid(largest_contentful_paint.largest_text_paint)) {
main_frame_contentful_paint_.Text().Reset(
largest_contentful_paint.largest_text_paint,
- largest_contentful_paint.largest_text_paint_size, /*type=*/0,
+ largest_contentful_paint.largest_text_paint_size,
+ blink::LargestContentfulPaintType::kNone,
/*image_bpp=*/0.0);
}
if (IsValid(largest_contentful_paint.largest_image_paint)) {
main_frame_contentful_paint_.Image().Reset(
largest_contentful_paint.largest_image_paint,
largest_contentful_paint.largest_image_paint_size,
- largest_contentful_paint.type, largest_contentful_paint.image_bpp);
+ static_cast<blink::LargestContentfulPaintType>(
+ largest_contentful_paint.type),
+ largest_contentful_paint.image_bpp);
}
}
@@ -356,23 +419,4 @@ void LargestContentfulPaintHandler::OnSubFrameDeleted(int frame_tree_node_id) {
subframe_navigation_start_offset_.erase(frame_tree_node_id);
}
-void LargestContentfulPaintHandler::MergeForSubframes(
- ContentfulPaintTimingInfo* inout_timing,
- const absl::optional<base::TimeDelta>& candidate_new_time,
- const uint64_t& candidate_new_size,
- base::TimeDelta navigation_start_offset) {
- absl::optional<base::TimeDelta> new_time = absl::nullopt;
- if (candidate_new_time) {
- // If |candidate_new_time| is TimeDelta(), this means that the candidate is
- // an image that has not finished loading. Preserve its meaning by not
- // adding the |navigation_start_offset|.
- new_time = candidate_new_time->is_positive()
- ? navigation_start_offset + candidate_new_time.value()
- : base::TimeDelta();
- }
- if (IsValid(new_time))
- MergeForSubframesWithAdjustedTime(inout_timing, new_time,
- candidate_new_size);
-}
-
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h b/chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h
index 9d47a27c3aa..a32e7395036 100644
--- a/chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h
+++ b/chromium/components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h
@@ -34,22 +34,22 @@ class ContentfulPaintTimingInfo {
ContentfulPaintTimingInfo(LargestContentTextOrImage largest_content_type,
bool in_main_frame,
- blink::LargestContentfulPaintTypeMask type);
+ blink::LargestContentfulPaintType type);
ContentfulPaintTimingInfo(
const absl::optional<base::TimeDelta>&,
const uint64_t& size,
const LargestContentTextOrImage largest_content_type,
double image_bpp,
bool in_main_frame,
- blink::LargestContentfulPaintTypeMask type);
+ blink::LargestContentfulPaintType type);
ContentfulPaintTimingInfo(const ContentfulPaintTimingInfo& other);
void Reset(const absl::optional<base::TimeDelta>&,
const uint64_t& size,
- blink::LargestContentfulPaintTypeMask type,
+ blink::LargestContentfulPaintType type,
double image_bpp);
absl::optional<base::TimeDelta> Time() const { return time_; }
bool InMainFrame() const { return in_main_frame_; }
- blink::LargestContentfulPaintTypeMask Type() const { return type_; }
+ blink::LargestContentfulPaintType Type() const { return type_; }
uint64_t Size() const { return size_; }
LargestContentTextOrImage TextOrImage() const { return text_or_image_; }
double ImageBPP() const { return image_bpp_; }
@@ -76,7 +76,8 @@ class ContentfulPaintTimingInfo {
absl::optional<base::TimeDelta> time_;
uint64_t size_;
LargestContentTextOrImage text_or_image_;
- blink::LargestContentfulPaintTypeMask type_ = 0;
+ blink::LargestContentfulPaintType type_ =
+ blink::LargestContentfulPaintType::kNone;
double image_bpp_ = 0.0;
bool in_main_frame_;
};
@@ -84,7 +85,7 @@ class ContentfulPaintTimingInfo {
class ContentfulPaint {
public:
explicit ContentfulPaint(bool in_main_frame,
- blink::LargestContentfulPaintTypeMask type);
+ blink::LargestContentfulPaintType type);
ContentfulPaintTimingInfo& Text() { return text_; }
const ContentfulPaintTimingInfo& Text() const { return text_; }
ContentfulPaintTimingInfo& Image() { return image_; }
@@ -179,11 +180,6 @@ class LargestContentfulPaintHandler {
void UpdateFirstInputOrScrollNotified(
const absl::optional<base::TimeDelta>& candidate_new_time,
const base::TimeDelta& navigation_start_offset);
- void MergeForSubframes(
- ContentfulPaintTimingInfo* inout_timing,
- const absl::optional<base::TimeDelta>& candidate_new_time,
- const uint64_t& candidate_new_size,
- base::TimeDelta navigation_start_offset);
bool IsValid(const absl::optional<base::TimeDelta>& time) {
// When |time| is not present, this means that there is no current
// candidate. If |time| is 0, it corresponds to an image that has not
diff --git a/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.cc
index f0d72890869..6c60fe40328 100644
--- a/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.cc
@@ -403,6 +403,14 @@ UmaPageLoadMetricsObserver::UmaPageLoadMetricsObserver()
UmaPageLoadMetricsObserver::~UmaPageLoadMetricsObserver() {}
+// TODO(https://crbug.com/1317494): Audit and use appropriate policy.
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+UmaPageLoadMetricsObserver::OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) {
+ return STOP_OBSERVING;
+}
+
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
UmaPageLoadMetricsObserver::OnRedirect(
content::NavigationHandle* navigation_handle) {
diff --git a/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.h
index 59085af7d7e..08772bfdb13 100644
--- a/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.h
@@ -180,6 +180,9 @@ class UmaPageLoadMetricsObserver
~UmaPageLoadMetricsObserver() override;
// page_load_metrics::PageLoadMetricsObserver:
+ ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override;
ObservePolicy OnRedirect(
content::NavigationHandle* navigation_handle) override;
ObservePolicy OnCommit(content::NavigationHandle* navigation_handle) override;
diff --git a/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer_unittest.cc b/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer_unittest.cc
index 1ed74d21b40..d58fde4e8df 100644
--- a/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer_unittest.cc
@@ -678,9 +678,11 @@ TEST_F(UmaPageLoadMetricsObserverTest, BytesAndResourcesCounted) {
TEST_F(UmaPageLoadMetricsObserverTest, CpuUsageCounted) {
NavigateAndCommit(GURL(kDefaultTestUrl));
- OnCpuTimingUpdate(web_contents()->GetMainFrame(), base::Milliseconds(750));
+ OnCpuTimingUpdate(web_contents()->GetPrimaryMainFrame(),
+ base::Milliseconds(750));
web_contents()->WasHidden(); // Set the web contents as backgrounded.
- OnCpuTimingUpdate(web_contents()->GetMainFrame(), base::Milliseconds(250));
+ OnCpuTimingUpdate(web_contents()->GetPrimaryMainFrame(),
+ base::Milliseconds(250));
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectUniqueSample(
@@ -784,7 +786,7 @@ TEST_F(UmaPageLoadMetricsObserverTest,
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
@@ -827,7 +829,7 @@ TEST_F(UmaPageLoadMetricsObserverTest,
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
@@ -864,7 +866,7 @@ TEST_F(UmaPageLoadMetricsObserverTest,
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
@@ -910,7 +912,7 @@ TEST_F(UmaPageLoadMetricsObserverTest,
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
@@ -955,7 +957,7 @@ TEST_F(UmaPageLoadMetricsObserverTest,
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
@@ -996,7 +998,7 @@ TEST_F(UmaPageLoadMetricsObserverTest,
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
@@ -1044,7 +1046,7 @@ TEST_F(
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
@@ -1399,7 +1401,7 @@ TEST_F(UmaPageLoadMetricsObserverTest, SingleSubFrame_MaxMemoryBytesRecorded) {
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://google.com/subframe.html"),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Notify that memory measurements are available for each frame.
@@ -1430,12 +1432,12 @@ TEST_F(UmaPageLoadMetricsObserverTest, MultiSubFrames_MaxMemoryBytesRecorded) {
RenderFrameHost* subframe1 =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://google.com/subframe.html"),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe1"));
RenderFrameHost* subframe2 =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://google.com/subframe2.html"),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe2"));
RenderFrameHost* subframe3 =
NavigationSimulator::NavigateAndCommitFromDocument(
@@ -1502,7 +1504,7 @@ TEST_F(UmaPageLoadMetricsObserverTest,
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
@@ -1545,7 +1547,7 @@ TEST_F(UmaPageLoadMetricsObserverTest,
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
@@ -1599,12 +1601,12 @@ TEST_F(UmaPageLoadMetricsObserverTest,
RenderFrameHost* first_party_subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSameSiteSubFrameTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe1"));
RenderFrameHost* cross_site_subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kCrossSiteSubFrameTestUrl),
- RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendChild("subframe2"));
// Simulate timing updates in the main frame and the subframe.
diff --git a/chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.cc
index 51e1b540af9..89b07aa46cf 100644
--- a/chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.cc
@@ -30,6 +30,14 @@ EarlyHintsPageLoadMetricsObserver::~EarlyHintsPageLoadMetricsObserver() =
default;
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+EarlyHintsPageLoadMetricsObserver::OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) {
+ // This class doesn't use subframe information. No need to forward.
+ return STOP_OBSERVING;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
EarlyHintsPageLoadMetricsObserver::OnCommit(
content::NavigationHandle* navigation_handle) {
// Continue observing when 103 Early Hints are received during the navigation
diff --git a/chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.h
index 021581cc46f..b6cb5b68dc3 100644
--- a/chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/observers/early_hints_page_load_metrics_observer.h
@@ -24,6 +24,9 @@ class EarlyHintsPageLoadMetricsObserver
~EarlyHintsPageLoadMetricsObserver() override;
// page_load_metrics::PageLoadMetricsObserver implementation:
+ ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override;
ObservePolicy OnCommit(content::NavigationHandle* navigation_handle) override;
ObservePolicy FlushMetricsOnAppEnterBackground(
const page_load_metrics::mojom::PageLoadTiming& timing) override;
diff --git a/chromium/components/page_load_metrics/browser/observers/fenced_frames_page_load_metrics_observer_unittest.cc b/chromium/components/page_load_metrics/browser/observers/fenced_frames_page_load_metrics_observer_unittest.cc
index 4a46d54bd39..9e709e5581e 100644
--- a/chromium/components/page_load_metrics/browser/observers/fenced_frames_page_load_metrics_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/observers/fenced_frames_page_load_metrics_observer_unittest.cc
@@ -82,11 +82,11 @@ TEST_F(FencedFramesPageLoadMetricsObserverTest, Foreground) {
NavigateAndCommit(GURL(kTestUrl));
content::RenderFrameHost* fenced_frame_root =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendFencedFrame();
ASSERT_TRUE(fenced_frame_root->IsFencedFrameRoot());
- auto simulator = content::NavigationSimulator::CreateForFencedFrame(
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL(kFencedFramesUrl), fenced_frame_root);
ASSERT_NE(nullptr, simulator);
simulator->Commit();
@@ -129,11 +129,11 @@ TEST_F(FencedFramesPageLoadMetricsObserverTest, Background) {
web_contents()->WasHidden();
content::RenderFrameHost* fenced_frame_root =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendFencedFrame();
ASSERT_TRUE(fenced_frame_root->IsFencedFrameRoot());
- auto simulator = content::NavigationSimulator::CreateForFencedFrame(
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL(kFencedFramesUrl), fenced_frame_root);
ASSERT_NE(nullptr, simulator);
simulator->Commit();
diff --git a/chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.cc
index bfb9c40ea8b..f95b8d87b03 100644
--- a/chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.cc
@@ -29,6 +29,14 @@ LayoutPageLoadMetricsObserver::LayoutPageLoadMetricsObserver() = default;
LayoutPageLoadMetricsObserver::~LayoutPageLoadMetricsObserver() = default;
+// TODO(https://crbug.com/1317494): Audit and use appropriate policy.
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+LayoutPageLoadMetricsObserver::OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) {
+ return STOP_OBSERVING;
+}
+
void LayoutPageLoadMetricsObserver::OnComplete(const mojom::PageLoadTiming&) {
Record(GetDelegate().GetPageRenderData());
}
diff --git a/chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.h
index ecc953941ce..31c952fbb6a 100644
--- a/chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/observers/layout_page_load_metrics_observer.h
@@ -16,6 +16,9 @@ class LayoutPageLoadMetricsObserver : public PageLoadMetricsObserver {
~LayoutPageLoadMetricsObserver() override;
// page_load_metrics::PageLoadMetricsObserver overrides.
+ ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override;
void OnComplete(const mojom::PageLoadTiming& timing) override;
ObservePolicy FlushMetricsOnAppEnterBackground(
const mojom::PageLoadTiming& timing) override;
diff --git a/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.cc b/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.cc
index 50a26f67fe8..dde7d5a8942 100644
--- a/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.cc
+++ b/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.cc
@@ -11,13 +11,25 @@
#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
+#include "third_party/blink/public/common/features.h"
#include "url/gurl.h"
namespace page_load_metrics {
PageLoadMetricsObserverContentTestHarness::
- PageLoadMetricsObserverContentTestHarness()
- : content::RenderViewHostTestHarness() {}
+ PageLoadMetricsObserverContentTestHarness() {
+ scoped_feature_list_.InitWithFeaturesAndParameters(
+ {
+ {blink::features::kPrerender2, {}},
+ {blink::features::kFencedFrames, {{"implementation_type", "mparch"}}},
+ {blink::features::kInitialNavigationEntry, {}},
+ },
+ {
+ // Disable the memory requirement of Prerender2
+ // so the test can run on any bot.
+ {blink::features::kPrerender2MemoryControls},
+ });
+}
PageLoadMetricsObserverContentTestHarness::
~PageLoadMetricsObserverContentTestHarness() {}
diff --git a/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h b/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h
index 22a988f1c8a..b8b1d4bb852 100644
--- a/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h
+++ b/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/memory/raw_ptr.h"
+#include "base/test/scoped_feature_list.h"
#include "components/page_load_metrics/browser/metrics_navigation_throttle.h"
#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
@@ -49,6 +50,7 @@ class PageLoadMetricsObserverContentTestHarness
std::unique_ptr<PageLoadMetricsObserverTester> tester_;
PageLoadMetricsTestContentBrowserClient browser_client_;
raw_ptr<content::ContentBrowserClient> original_browser_client_ = nullptr;
+ base::test::ScopedFeatureList scoped_feature_list_;
};
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc b/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
index 3800cfeed0c..c2e00f04d98 100644
--- a/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
+++ b/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
@@ -22,6 +22,7 @@
#include "content/public/test/test_renderer_host.h"
#include "net/base/ip_endpoint.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/utility/utility.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
#include "third_party/blink/public/mojom/mobile_metrics/mobile_friendliness.mojom.h"
@@ -124,7 +125,7 @@ void PageLoadMetricsObserverTester::NavigateToUntrackedUrl() {
void PageLoadMetricsObserverTester::SimulateTimingUpdate(
const mojom::PageLoadTiming& timing) {
- SimulateTimingUpdate(timing, web_contents()->GetMainFrame());
+ SimulateTimingUpdate(timing, web_contents()->GetPrimaryMainFrame());
}
void PageLoadMetricsObserverTester::SimulateTimingUpdate(
@@ -138,13 +139,13 @@ void PageLoadMetricsObserverTester::SimulateTimingUpdate(
void PageLoadMetricsObserverTester::SimulateCpuTimingUpdate(
const mojom::CpuTiming& cpu_timing) {
- SimulateCpuTimingUpdate(cpu_timing, web_contents()->GetMainFrame());
+ SimulateCpuTimingUpdate(cpu_timing, web_contents()->GetPrimaryMainFrame());
}
void PageLoadMetricsObserverTester::SimulateCpuTimingUpdate(
const mojom::CpuTiming& cpu_timing,
content::RenderFrameHost* rfh) {
- auto timing = page_load_metrics::mojom::PageLoadTimingPtr(base::in_place);
+ auto timing = page_load_metrics::mojom::PageLoadTimingPtr(absl::in_place);
page_load_metrics::InitPageLoadTimingForTest(timing.get());
SimulatePageLoadTimingUpdate(
*timing, mojom::FrameMetadata(), /* new_features= */ {},
@@ -154,7 +155,8 @@ void PageLoadMetricsObserverTester::SimulateCpuTimingUpdate(
void PageLoadMetricsObserverTester::SimulateInputTimingUpdate(
const mojom::InputTiming& input_timing) {
- SimulateInputTimingUpdate(input_timing, web_contents()->GetMainFrame());
+ SimulateInputTimingUpdate(input_timing,
+ web_contents()->GetPrimaryMainFrame());
}
void PageLoadMetricsObserverTester::SimulateMobileFriendlinessUpdate(
@@ -171,7 +173,7 @@ void PageLoadMetricsObserverTester::SimulateMobileFriendlinessUpdate(
void PageLoadMetricsObserverTester::SimulateInputTimingUpdate(
const mojom::InputTiming& input_timing,
content::RenderFrameHost* rfh) {
- auto timing = page_load_metrics::mojom::PageLoadTimingPtr(base::in_place);
+ auto timing = page_load_metrics::mojom::PageLoadTimingPtr(absl::in_place);
page_load_metrics::InitPageLoadTimingForTest(timing.get());
SimulatePageLoadTimingUpdate(
*timing, mojom::FrameMetadata(), /* new_features= */ {},
@@ -185,7 +187,7 @@ void PageLoadMetricsObserverTester::SimulateTimingAndMetadataUpdate(
SimulatePageLoadTimingUpdate(
timing, metadata, /* new_features= */ {}, mojom::FrameRenderDataUpdate(),
mojom::CpuTiming(), mojom::InputTiming(), blink::MobileFriendliness(),
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
}
void PageLoadMetricsObserverTester::SimulateMetadataUpdate(
@@ -204,12 +206,12 @@ void PageLoadMetricsObserverTester::SimulateFeaturesUpdate(
SimulatePageLoadTimingUpdate(
mojom::PageLoadTiming(), mojom::FrameMetadata(), new_features,
mojom::FrameRenderDataUpdate(), mojom::CpuTiming(), mojom::InputTiming(),
- blink::MobileFriendliness(), web_contents()->GetMainFrame());
+ blink::MobileFriendliness(), web_contents()->GetPrimaryMainFrame());
}
void PageLoadMetricsObserverTester::SimulateRenderDataUpdate(
const mojom::FrameRenderDataUpdate& render_data) {
- SimulateRenderDataUpdate(render_data, web_contents()->GetMainFrame());
+ SimulateRenderDataUpdate(render_data, web_contents()->GetPrimaryMainFrame());
}
void PageLoadMetricsObserverTester::SimulateRenderDataUpdate(
@@ -246,21 +248,22 @@ void PageLoadMetricsObserverTester::SimulatePageLoadTimingUpdate(
void PageLoadMetricsObserverTester::SimulateResourceDataUseUpdate(
const std::vector<mojom::ResourceDataUpdatePtr>& resources) {
- SimulateResourceDataUseUpdate(resources, web_contents()->GetMainFrame());
+ SimulateResourceDataUseUpdate(resources,
+ web_contents()->GetPrimaryMainFrame());
}
void PageLoadMetricsObserverTester::SimulateResourceDataUseUpdate(
const std::vector<mojom::ResourceDataUpdatePtr>& resources,
content::RenderFrameHost* render_frame_host) {
- auto timing = mojom::PageLoadTimingPtr(base::in_place);
+ auto timing = mojom::PageLoadTimingPtr(absl::in_place);
InitPageLoadTimingForTest(timing.get());
metrics_web_contents_observer_->OnTimingUpdated(
render_frame_host, std::move(timing),
- mojom::FrameMetadataPtr(base::in_place),
+ mojom::FrameMetadataPtr(absl::in_place),
std::vector<blink::UseCounterFeature>(), resources,
- mojom::FrameRenderDataUpdatePtr(base::in_place),
- mojom::CpuTimingPtr(base::in_place),
- mojom::InputTimingPtr(base::in_place), blink::MobileFriendliness());
+ mojom::FrameRenderDataUpdatePtr(absl::in_place),
+ mojom::CpuTimingPtr(absl::in_place),
+ mojom::InputTimingPtr(absl::in_place), blink::MobileFriendliness());
}
void PageLoadMetricsObserverTester::SimulateLoadedResource(
@@ -278,7 +281,7 @@ void PageLoadMetricsObserverTester::SimulateLoadedResource(
}
blink::mojom::ResourceLoadInfo resource_load_info;
- resource_load_info.final_url = info.origin_of_final_url.GetURL();
+ resource_load_info.final_url = info.final_url.GetURL();
resource_load_info.was_cached = info.was_cached;
resource_load_info.raw_body_bytes = info.raw_body_bytes;
resource_load_info.total_received_bytes =
@@ -293,7 +296,7 @@ void PageLoadMetricsObserverTester::SimulateLoadedResource(
resource_load_info.load_timing_info.request_start = base::TimeTicks::Now();
metrics_web_contents_observer_->ResourceLoadComplete(
- web_contents()->GetMainFrame(), request_id, resource_load_info);
+ web_contents()->GetPrimaryMainFrame(), request_id, resource_load_info);
}
void PageLoadMetricsObserverTester::SimulateFrameReceivedUserActivation(
@@ -312,17 +315,22 @@ void PageLoadMetricsObserverTester::SimulateAppEnterBackground() {
}
void PageLoadMetricsObserverTester::SimulateMediaPlayed() {
+ SimulateMediaPlayed(web_contents()->GetPrimaryMainFrame());
+}
+
+void PageLoadMetricsObserverTester::SimulateMediaPlayed(
+ content::RenderFrameHost* rfh) {
content::WebContentsObserver::MediaPlayerInfo video_type(
true /* has_video*/, true /* has_audio */);
- content::RenderFrameHost* render_frame_host = web_contents()->GetMainFrame();
metrics_web_contents_observer_->MediaStartedPlaying(
- video_type, content::MediaPlayerId(render_frame_host->GetGlobalId(), 0));
+ video_type, content::MediaPlayerId(rfh->GetGlobalId(), 0));
}
void PageLoadMetricsObserverTester::SimulateCookieAccess(
const content::CookieAccessDetails& details) {
metrics_web_contents_observer_->OnCookiesAccessed(
- metrics_web_contents_observer_->web_contents()->GetMainFrame(), details);
+ metrics_web_contents_observer_->web_contents()->GetPrimaryMainFrame(),
+ details);
}
void PageLoadMetricsObserverTester::SimulateStorageAccess(
@@ -331,8 +339,8 @@ void PageLoadMetricsObserverTester::SimulateStorageAccess(
bool blocked_by_policy,
StorageType storage_type) {
metrics_web_contents_observer_->OnStorageAccessed(
- metrics_web_contents_observer_->web_contents()->GetMainFrame(), url,
- first_party_url, blocked_by_policy, storage_type);
+ metrics_web_contents_observer_->web_contents()->GetPrimaryMainFrame(),
+ url, first_party_url, blocked_by_policy, storage_type);
}
void PageLoadMetricsObserverTester::SimulateMobileFriendlinessUpdate(
@@ -340,7 +348,7 @@ void PageLoadMetricsObserverTester::SimulateMobileFriendlinessUpdate(
SimulatePageLoadTimingUpdate(
mojom::PageLoadTiming(), mojom::FrameMetadata(), /* new_features= */ {},
mojom::FrameRenderDataUpdate(), mojom::CpuTiming(), mojom::InputTiming(),
- mobile_friendliness, web_contents()->GetMainFrame());
+ mobile_friendliness, web_contents()->GetPrimaryMainFrame());
}
const PageLoadMetricsObserverDelegate&
diff --git a/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h b/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
index a87d14d2834..37f862f1678 100644
--- a/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
+++ b/chromium/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
@@ -139,6 +139,7 @@ class PageLoadMetricsObserverTester : public test::WeakMockTimerProvider {
// Simulate playing a media element.
void SimulateMediaPlayed();
+ void SimulateMediaPlayed(content::RenderFrameHost* rfh);
// Simulate accessingcookies.
void SimulateCookieAccess(const content::CookieAccessDetails& details);
diff --git a/chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.cc
index 042893705e5..07228669f93 100644
--- a/chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.cc
@@ -30,6 +30,11 @@ const char kHistogramPrerenderCumulativeShiftScore[] =
const char kHistogramPrerenderCumulativeShiftScoreMainFrame[] =
"PageLoad.Clients.Prerender.LayoutInstability.CumulativeShiftScore."
"MainFrame";
+const char
+ kHistogramPrerenderMaxCumulativeShiftScoreSessionWindowGap1000msMax5000ms2
+ [] = "PageLoad.Clients.Prerender.LayoutInstability."
+ "MaxCumulativeShiftScore.SessionWindow."
+ "Gap1000ms.Max5000ms2";
} // namespace internal
@@ -37,20 +42,28 @@ PrerenderPageLoadMetricsObserver::PrerenderPageLoadMetricsObserver() = default;
PrerenderPageLoadMetricsObserver::~PrerenderPageLoadMetricsObserver() = default;
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-PrerenderPageLoadMetricsObserver::OnPrerenderStart(
+PrerenderPageLoadMetricsObserver::OnStart(
content::NavigationHandle* navigation_handle,
- const GURL& currently_committed_url) {
- return CONTINUE_OBSERVING;
+ const GURL& currently_committed_url,
+ bool started_in_foreground) {
+ return STOP_OBSERVING;
}
+// TODO(https://crbug.com/1317494): Audit and use appropriate policy.
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-PrerenderPageLoadMetricsObserver::OnStart(
+PrerenderPageLoadMetricsObserver::OnFencedFramesStart(
content::NavigationHandle* navigation_handle,
- const GURL& currently_committed_url,
- bool started_in_foreground) {
+ const GURL& currently_committed_url) {
return STOP_OBSERVING;
}
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+PrerenderPageLoadMetricsObserver::OnPrerenderStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) {
+ return CONTINUE_OBSERVING;
+}
+
void PrerenderPageLoadMetricsObserver::DidActivatePrerenderedPage(
content::NavigationHandle* navigation_handle) {
// Copy the trigger type and histogram suffix for an embedder. These data will
@@ -114,10 +127,15 @@ void PrerenderPageLoadMetricsObserver::OnFirstInputInPage(
timing.interactive_timing->first_input_timestamp, GetDelegate())) {
return;
}
+
+ base::TimeDelta first_input_delay =
+ timing.interactive_timing->first_input_delay.value();
base::UmaHistogramCustomTimes(
AppendSuffix(internal::kHistogramPrerenderFirstInputDelay4),
- timing.interactive_timing->first_input_delay.value(),
- base::Milliseconds(1), base::Seconds(60), 50);
+ first_input_delay, base::Milliseconds(1), base::Seconds(60), 50);
+ ukm::builders::PrerenderPageLoad(GetDelegate().GetPageUkmSourceId())
+ .SetInteractiveTiming_FirstInputDelay4(first_input_delay.InMilliseconds())
+ .Record(ukm::UkmRecorder::Get());
}
void PrerenderPageLoadMetricsObserver::OnComplete(
@@ -141,6 +159,7 @@ void PrerenderPageLoadMetricsObserver::RecordSessionEndHistograms(
return;
}
+ // Records Largest Contentful Paint (LCP) to UMA and UKM.
const page_load_metrics::ContentfulPaintTimingInfo& largest_contentful_paint =
GetDelegate()
.GetLargestContentfulPaintHandler()
@@ -161,6 +180,7 @@ void PrerenderPageLoadMetricsObserver::RecordSessionEndHistograms(
.Record(ukm::UkmRecorder::Get());
}
+ // Records Cumulative Layout Shift Score (CLS) to UMA and UKM.
base::UmaHistogramCounts100(
AppendSuffix(internal::kHistogramPrerenderCumulativeShiftScore),
page_load_metrics::LayoutShiftUmaValue(
@@ -169,6 +189,24 @@ void PrerenderPageLoadMetricsObserver::RecordSessionEndHistograms(
AppendSuffix(internal::kHistogramPrerenderCumulativeShiftScoreMainFrame),
page_load_metrics::LayoutShiftUmaValue(
GetDelegate().GetMainFrameRenderData().layout_shift_score));
+
+ const page_load_metrics::NormalizedCLSData& normalized_cls_data =
+ GetDelegate().GetNormalizedCLSData(
+ page_load_metrics::PageLoadMetricsObserverDelegate::BfcacheStrategy::
+ ACCUMULATE);
+ if (!normalized_cls_data.data_tainted) {
+ page_load_metrics::UmaMaxCumulativeShiftScoreHistogram10000x(
+ AppendSuffix(
+ internal::
+ kHistogramPrerenderMaxCumulativeShiftScoreSessionWindowGap1000msMax5000ms2),
+ normalized_cls_data);
+ const float max_cls =
+ normalized_cls_data.session_windows_gap1000ms_max5000ms_max_cls;
+ ukm::builders::PrerenderPageLoad(GetDelegate().GetPageUkmSourceId())
+ .SetLayoutInstability_MaxCumulativeShiftScore_SessionWindow_Gap1000ms_Max5000ms(
+ page_load_metrics::LayoutShiftUkmValue(max_cls))
+ .Record(ukm::UkmRecorder::Get());
+ }
}
std::string PrerenderPageLoadMetricsObserver::AppendSuffix(
diff --git a/chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.h
index 884d86dca86..9f02ccafb64 100644
--- a/chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.h
@@ -18,6 +18,9 @@ extern const char kHistogramPrerenderActivationToLargestContentfulPaint2[];
extern const char kHistogramPrerenderFirstInputDelay4[];
extern const char kHistogramPrerenderCumulativeShiftScore[];
extern const char kHistogramPrerenderCumulativeShiftScoreMainFrame[];
+extern const char
+ kHistogramPrerenderMaxCumulativeShiftScoreSessionWindowGap1000msMax5000ms2
+ [];
} // namespace internal
@@ -33,6 +36,9 @@ class PrerenderPageLoadMetricsObserver
ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url,
bool started_in_foreground) override;
+ ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override;
ObservePolicy OnPrerenderStart(content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url) override;
void DidActivatePrerenderedPage(
diff --git a/chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc b/chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
index f761e58fe77..be70e68a9fb 100644
--- a/chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
+++ b/chromium/components/page_load_metrics/browser/observers/use_counter/ukm_features.cc
@@ -218,12 +218,22 @@ UseCounterPageLoadMetricsObserver::GetAllowedUkmFeatures() {
WebFeature::kWebCodecsVideoTrackReader,
WebFeature::kWebCodecsImageDecoder,
WebFeature::kWebCodecsAudioEncoder,
- WebFeature::kWebCodecsVideoFrameDefaultTimestamp,
WebFeature::kWebCodecsVideoFrameFromImage,
WebFeature::kWebCodecsVideoFrameFromBuffer,
WebFeature::kOpenWebDatabaseInsecureContext,
WebFeature::kPrivateNetworkAccessIgnoredPreflightError,
WebFeature::kWebBluetoothGetAvailability,
+ WebFeature::kCookieHasNotBeenRefreshedIn201To300Days,
+ WebFeature::kCookieHasNotBeenRefreshedIn301To350Days,
+ WebFeature::kCookieHasNotBeenRefreshedIn351To400Days,
+ WebFeature::kPartitionedCookies,
+ WebFeature::kScriptSchedulingType_Defer,
+ WebFeature::kScriptSchedulingType_ParserBlocking,
+ WebFeature::kScriptSchedulingType_ParserBlockingInline,
+ WebFeature::kScriptSchedulingType_InOrder,
+ WebFeature::kScriptSchedulingType_Async,
+ WebFeature::kClientHintsMetaHTTPEquivAcceptCH,
+ WebFeature::kClientHintsMetaNameAcceptCH,
}));
return *opt_in_features;
}
diff --git a/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
index 6169a33afd4..225db506186 100644
--- a/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
@@ -20,6 +20,10 @@ using PermissionsPolicyFeature = blink::mojom::PermissionsPolicyFeature;
using UserAgentOverrideHistogram =
blink::UserAgentOverride::UserAgentOverrideHistogram;
+#define FEATURE_HISTOGRAM_NAME(name, is_in_fenced_frames) \
+ is_in_fenced_frames ? "Blink.UseCounter.FencedFrames." name \
+ : "Blink.UseCounter." name
+
namespace {
// It's always recommended to use the deprecation API in blink. If the feature
@@ -165,18 +169,15 @@ void UseCounterPageLoadMetricsObserver::RecordUseCounterFeature(
content::RenderFrameHost* rfh,
const blink::UseCounterFeature& feature) {
switch (feature.type()) {
- case FeatureType::kWebFeature: {
+ case FeatureType::kWebFeature:
if (TestAndSet(features_recorded_, feature.value()))
return;
base::UmaHistogramEnumeration(
- is_in_fenced_frames_page_
- ? internal::kFeaturesHistogramFencedFramesName
- : internal::kFeaturesHistogramName,
+ FEATURE_HISTOGRAM_NAME("Features", is_in_fenced_frames_page_),
static_cast<WebFeature>(feature.value()));
PossiblyWarnFeatureDeprecation(rfh,
static_cast<WebFeature>(feature.value()));
break;
- }
// There are about 600 enums, so the memory required for a vector
// histogram is about 600 * 8 byes = 5KB 50% of the time there are about
// 100 CSS properties recorded per page load. Storage in sparce
@@ -190,18 +191,15 @@ void UseCounterPageLoadMetricsObserver::RecordUseCounterFeature(
if (TestAndSet(css_properties_recorded_, feature.value()))
return;
base::UmaHistogramEnumeration(
- is_in_fenced_frames_page_
- ? internal::kCssPropertiesHistogramFencedFramesName
- : internal::kCssPropertiesHistogramName,
+ FEATURE_HISTOGRAM_NAME("CSSProperties", is_in_fenced_frames_page_),
static_cast<CSSSampleId>(feature.value()));
break;
case FeatureType::kAnimatedCssProperty:
if (TestAndSet(animated_css_properties_recorded_, feature.value()))
return;
base::UmaHistogramEnumeration(
- is_in_fenced_frames_page_
- ? internal::kAnimatedCssPropertiesHistogramFencedFramesName
- : internal::kAnimatedCssPropertiesHistogramName,
+ FEATURE_HISTOGRAM_NAME("AnimatedCSSProperties",
+ is_in_fenced_frames_page_),
static_cast<CSSSampleId>(feature.value()));
break;
@@ -210,9 +208,8 @@ void UseCounterPageLoadMetricsObserver::RecordUseCounterFeature(
feature.value()))
return;
base::UmaHistogramEnumeration(
- is_in_fenced_frames_page_
- ? internal::kPermissionsPolicyViolationHistogramFencedFramesName
- : internal::kPermissionsPolicyViolationHistogramName,
+ FEATURE_HISTOGRAM_NAME("PermissionsPolicy.Violation.Enforce",
+ is_in_fenced_frames_page_),
static_cast<PermissionsPolicyFeature>(feature.value()));
break;
case FeatureType::kPermissionsPolicyHeader:
@@ -220,9 +217,8 @@ void UseCounterPageLoadMetricsObserver::RecordUseCounterFeature(
feature.value()))
return;
base::UmaHistogramEnumeration(
- is_in_fenced_frames_page_
- ? internal::kPermissionsPolicyHeaderHistogramFencedFramesName
- : internal::kPermissionsPolicyHeaderHistogramName,
+ FEATURE_HISTOGRAM_NAME("PermissionsPolicy.Header2",
+ is_in_fenced_frames_page_),
static_cast<PermissionsPolicyFeature>(feature.value()));
break;
case FeatureType::kPermissionsPolicyIframeAttribute:
@@ -230,19 +226,16 @@ void UseCounterPageLoadMetricsObserver::RecordUseCounterFeature(
feature.value()))
return;
base::UmaHistogramEnumeration(
- is_in_fenced_frames_page_
- ? internal::
- kPermissionsPolicyIframeAttributeHistogramFencedFramesName
- : internal::kPermissionsPolicyIframeAttributeHistogramName,
+ FEATURE_HISTOGRAM_NAME("PermissionsPolicy.Allow2",
+ is_in_fenced_frames_page_),
static_cast<PermissionsPolicyFeature>(feature.value()));
break;
case FeatureType::kUserAgentOverride:
if (TestAndSet(user_agent_override_features_recorded_, feature.value()))
return;
base::UmaHistogramEnumeration(
- is_in_fenced_frames_page_
- ? internal::kUserAgentOverrideHistogramFencedFramesName
- : internal::kUserAgentOverrideHistogramName,
+ FEATURE_HISTOGRAM_NAME("UserAgentOverride",
+ is_in_fenced_frames_page_),
static_cast<UserAgentOverrideHistogram>(feature.value()));
break;
}
@@ -262,9 +255,7 @@ void UseCounterPageLoadMetricsObserver::RecordMainFrameWebFeature(
return;
}
base::UmaHistogramEnumeration(
- is_in_fenced_frames_page_
- ? internal::kFeaturesHistogramFencedFramesMainFrameName
- : internal::kFeaturesHistogramMainFrameName,
+ FEATURE_HISTOGRAM_NAME("MainFrame.Features", is_in_fenced_frames_page_),
web_feature);
}
diff --git a/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h
index 5cb82885371..0d6405e5cfd 100644
--- a/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h
@@ -6,52 +6,14 @@
#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_USE_COUNTER_PAGE_LOAD_METRICS_OBSERVER_H_
#include <bitset>
+#include <string>
+
#include "base/containers/flat_set.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
-#include "third_party/blink/public/mojom/use_counter/css_property_id.mojom.h"
+#include "third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom.h"
+#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
#include "third_party/blink/public/mojom/use_counter/use_counter_feature.mojom-forward.h"
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
-
-namespace internal {
-
-// Histogram name definitions for the primary uses.
-const char kFeaturesHistogramName[] = "Blink.UseCounter.Features";
-const char kFeaturesHistogramMainFrameName[] =
- "Blink.UseCounter.MainFrame.Features";
-const char kCssPropertiesHistogramName[] = "Blink.UseCounter.CSSProperties";
-const char kAnimatedCssPropertiesHistogramName[] =
- "Blink.UseCounter.AnimatedCSSProperties";
-const char kPermissionsPolicyViolationHistogramName[] =
- "Blink.UseCounter.PermissionsPolicy.Violation.Enforce";
-const char kPermissionsPolicyHeaderHistogramName[] =
- "Blink.UseCounter.PermissionsPolicy.Header2";
-const char kPermissionsPolicyIframeAttributeHistogramName[] =
- "Blink.UseCounter.PermissionsPolicy.Allow2";
-const char kUserAgentOverrideHistogramName[] =
- "Blink.UseCounter.UserAgentOverride";
-
-// Histogram name definitions for FencedFrames page variants.
-// TODO(https://crbug.com/1301880): Generate the name dynamically rather than
-// preparing prefixed names. See review comments at https://crrev.com/c/3573433.
-const char kFeaturesHistogramFencedFramesName[] =
- "Blink.UseCounter.FencedFrames.Features";
-const char kFeaturesHistogramFencedFramesMainFrameName[] =
- "Blink.UseCounter.FencedFrames.MainFrame.Features";
-const char kCssPropertiesHistogramFencedFramesName[] =
- "Blink.UseCounter.FencedFrames.CSSProperties";
-const char kAnimatedCssPropertiesHistogramFencedFramesName[] =
- "Blink.UseCounter.FencedFrames.AnimatedCSSProperties";
-const char kPermissionsPolicyViolationHistogramFencedFramesName[] =
- "Blink.UseCounter.FencedFrames.PermissionsPolicy.Violation.Enforce";
-const char kPermissionsPolicyHeaderHistogramFencedFramesName[] =
- "Blink.UseCounter.FencedFrames.PermissionsPolicy.Header2";
-const char kPermissionsPolicyIframeAttributeHistogramFencedFramesName[] =
- "Blink.UseCounter.FencedFrames.PermissionsPolicy.Allow2";
-const char kUserAgentOverrideHistogramFencedFramesName[] =
- "Blink.UseCounter.FencedFrames.UserAgentOverride";
-
-} // namespace internal
// This class reports several use counters coming from Blink.
// For FencedFrames, it reports the use counters with a "FencedFrames" prefix.
diff --git a/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer_unittest.cc b/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer_unittest.cc
index 83234ca1ad4..faa74c4a570 100644
--- a/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer_unittest.cc
@@ -16,7 +16,7 @@
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
+#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
#include "url/gurl.h"
namespace {
@@ -28,22 +28,27 @@ using CSSSampleId = blink::mojom::CSSSampleId;
using FeatureType = blink::mojom::UseCounterFeatureType;
const char* GetUseCounterHistogramName(
- blink::mojom::UseCounterFeatureType feature_type) {
+ blink::mojom::UseCounterFeatureType feature_type,
+ bool is_in_main_frame = false) {
+ if (is_in_main_frame) {
+ CHECK_EQ(FeatureType::kWebFeature, feature_type);
+ return "Blink.UseCounter.MainFrame.Features";
+ }
switch (feature_type) {
case FeatureType::kWebFeature:
- return internal::kFeaturesHistogramName;
+ return "Blink.UseCounter.Features";
case FeatureType::kCssProperty:
- return internal::kCssPropertiesHistogramName;
+ return "Blink.UseCounter.CSSProperties";
case FeatureType::kAnimatedCssProperty:
- return internal::kAnimatedCssPropertiesHistogramName;
+ return "Blink.UseCounter.AnimatedCSSProperties";
case FeatureType::kPermissionsPolicyViolationEnforce:
- return internal::kPermissionsPolicyViolationHistogramName;
+ return "Blink.UseCounter.PermissionsPolicy.Violation.Enforce";
case FeatureType::kPermissionsPolicyHeader:
- return internal::kPermissionsPolicyHeaderHistogramName;
+ return "Blink.UseCounter.PermissionsPolicy.Header2";
case FeatureType::kPermissionsPolicyIframeAttribute:
- return internal::kPermissionsPolicyIframeAttributeHistogramName;
+ return "Blink.UseCounter.PermissionsPolicy.Allow2";
case FeatureType::kUserAgentOverride:
- return internal::kUserAgentOverrideHistogramName;
+ return "Blink.UseCounter.UserAgentOverride";
}
}
@@ -67,7 +72,7 @@ class UseCounterPageLoadMetricsObserverTest
size_t count) {
if (feature.type() == blink::mojom::UseCounterFeatureType::kWebFeature) {
tester()->histogram_tester().ExpectBucketCount(
- internal::kFeaturesHistogramMainFrameName,
+ GetUseCounterHistogramName(FeatureType::kWebFeature, true),
static_cast<base::Histogram::Sample>(feature.value()), count);
}
@@ -83,11 +88,12 @@ class UseCounterPageLoadMetricsObserverTest
if (WithFencedFrames()) {
content::RenderFrameHost* fenced_frame_root =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(
+ web_contents()->GetPrimaryMainFrame())
->AppendFencedFrame();
ASSERT_TRUE(fenced_frame_root->IsFencedFrameRoot());
- auto simulator = content::NavigationSimulator::CreateForFencedFrame(
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL(kFencedFramesUrl), fenced_frame_root);
ASSERT_NE(nullptr, simulator);
simulator->Commit();
@@ -96,17 +102,17 @@ class UseCounterPageLoadMetricsObserverTest
tester()->SimulateFeaturesUpdate(first_features);
// Verify that kPageVisits is observed on commit.
tester()->histogram_tester().ExpectBucketCount(
- internal::kFeaturesHistogramName,
+ GetUseCounterHistogramName(FeatureType::kWebFeature),
static_cast<base::Histogram::Sample>(WebFeature::kPageVisits), 1);
tester()->histogram_tester().ExpectBucketCount(
- internal::kFeaturesHistogramMainFrameName,
+ GetUseCounterHistogramName(FeatureType::kWebFeature, true),
static_cast<base::Histogram::Sample>(WebFeature::kPageVisits), 1);
// Verify that page visit is recorded for CSS histograms.
tester()->histogram_tester().ExpectBucketCount(
- internal::kCssPropertiesHistogramName,
+ GetUseCounterHistogramName(FeatureType::kCssProperty),
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
tester()->histogram_tester().ExpectBucketCount(
- internal::kAnimatedCssPropertiesHistogramName,
+ GetUseCounterHistogramName(FeatureType::kAnimatedCssProperty),
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
for (const auto& feature : first_features)
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.cc
index 529fe4f440b..f43eabc2189 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.cc
@@ -7,7 +7,7 @@
namespace page_load_metrics {
PageLoadMetricsForwardObserver::PageLoadMetricsForwardObserver(
- base::WeakPtr<PageLoadMetricsObserver> parent_observer)
+ base::WeakPtr<PageLoadMetricsObserverInterface> parent_observer)
: parent_observer_(std::move(parent_observer)) {
DCHECK(parent_observer_);
}
@@ -21,35 +21,42 @@ const char* PageLoadMetricsForwardObserver::GetObserverName() const {
// forwarded to the parent page.
if (parent_observer_)
return parent_observer_->GetObserverName();
- return PageLoadMetricsObserver::GetObserverName();
+ return nullptr;
}
-// OnStart, OnFencedFramesStart, and OnPrerenderingStart should not be forwarded
-// as they are expected to be called only once at the beginning.
-PageLoadMetricsObserver::ObservePolicy PageLoadMetricsForwardObserver::OnStart(
+// Registration and initialization of PageLoadMetricsForwardObserver is
+// different from ones of other PageLoadMetricsObserver subclasses.
+// PageLoadMetricsForwardObserver is registered in
+// `components/page_load_metrics/browser/page_load_tracker.cc` and methods
+// OnStart, OnFencedFramesStart, and OnPrerenderingStart are never called.
+PageLoadMetricsObserverInterface::ObservePolicy
+PageLoadMetricsForwardObserver::OnStart(
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url,
bool started_in_foreground) {
- return CONTINUE_OBSERVING;
+ NOTREACHED();
+ return STOP_OBSERVING;
}
-PageLoadMetricsObserver::ObservePolicy
+PageLoadMetricsObserverInterface::ObservePolicy
PageLoadMetricsForwardObserver::OnFencedFramesStart(
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url) {
- return CONTINUE_OBSERVING;
+ NOTREACHED();
+ return STOP_OBSERVING;
}
-PageLoadMetricsObserver::ObservePolicy
+PageLoadMetricsObserverInterface::ObservePolicy
PageLoadMetricsForwardObserver::OnPrerenderStart(
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url) {
- return CONTINUE_OBSERVING;
+ NOTREACHED();
+ return STOP_OBSERVING;
}
// Main frame events will be converted as sub-frame events on forwarding, and
// OnRedirect is an event only for the main frame. We just mask it here.
-PageLoadMetricsObserver::ObservePolicy
+PageLoadMetricsObserverInterface::ObservePolicy
PageLoadMetricsForwardObserver::OnRedirect(
content::NavigationHandle* navigation_handle) {
return CONTINUE_OBSERVING;
@@ -58,7 +65,8 @@ PageLoadMetricsForwardObserver::OnRedirect(
// OnCommit and OnDidInternalNavigationAbort are handled at PageLoadTracker to
// forward events as a sub-frame navigation regardless of each observer's
// policy.
-PageLoadMetricsObserver::ObservePolicy PageLoadMetricsForwardObserver::OnCommit(
+PageLoadMetricsObserverInterface::ObservePolicy
+PageLoadMetricsForwardObserver::OnCommit(
content::NavigationHandle* navigation_handle) {
return CONTINUE_OBSERVING;
}
@@ -84,17 +92,17 @@ void PageLoadMetricsForwardObserver::OnCommitSameDocumentNavigation(
// Inner pages' OnHidden and OnShown are ignored to avoid duplicated calls in
// the parent observer.
-PageLoadMetricsObserver::ObservePolicy PageLoadMetricsForwardObserver::OnHidden(
- const mojom::PageLoadTiming& timing) {
+PageLoadMetricsObserverInterface::ObservePolicy
+PageLoadMetricsForwardObserver::OnHidden(const mojom::PageLoadTiming& timing) {
return CONTINUE_OBSERVING;
}
-PageLoadMetricsObserver::ObservePolicy
+PageLoadMetricsObserverInterface::ObservePolicy
PageLoadMetricsForwardObserver::OnShown() {
return CONTINUE_OBSERVING;
}
-PageLoadMetricsObserver::ObservePolicy
+PageLoadMetricsObserverInterface::ObservePolicy
PageLoadMetricsForwardObserver::OnEnterBackForwardCache(
const mojom::PageLoadTiming& timing) {
NOTREACHED() << "Not supported.";
@@ -107,7 +115,7 @@ void PageLoadMetricsForwardObserver::OnRestoreFromBackForwardCache(
NOTREACHED() << "Not supported.";
}
-PageLoadMetricsObserver::ObservePolicy
+PageLoadMetricsObserverInterface::ObservePolicy
PageLoadMetricsForwardObserver::ShouldObserveMimeType(
const std::string& mime_type) const {
if (!parent_observer_ ||
@@ -148,12 +156,11 @@ void PageLoadMetricsForwardObserver::OnCpuTimingUpdate(
content::RenderFrameHost* subframe_rfh,
const mojom::CpuTiming& timing) {}
+// OnUserInput is always dispatched only to the primary page.
void PageLoadMetricsForwardObserver::OnUserInput(
const blink::WebInputEvent& event,
const mojom::PageLoadTiming& timing) {
- if (!parent_observer_)
- return;
- parent_observer_->OnUserInput(event, timing);
+ NOTREACHED();
}
// Following events should be ignored as they are controlled at
@@ -210,13 +217,11 @@ void PageLoadMetricsForwardObserver::OnFirstMeaningfulPaintInMainFrameDocument(
void PageLoadMetricsForwardObserver::OnFirstInputInPage(
const mojom::PageLoadTiming& timing) {}
+// OnLoadingBehaviorObserved is called through PageLoadTracker::UpdateMetrics.
+// So, the event is always forwarded at the PageLoadTracker layer.
void PageLoadMetricsForwardObserver::OnLoadingBehaviorObserved(
content::RenderFrameHost* rfh,
- int behavior_flags) {
- if (!parent_observer_)
- return;
- parent_observer_->OnLoadingBehaviorObserved(rfh, behavior_flags);
-}
+ int behavior_flags) {}
void PageLoadMetricsForwardObserver::OnFeaturesUsageObserved(
content::RenderFrameHost* rfh,
@@ -248,17 +253,25 @@ void PageLoadMetricsForwardObserver::MediaStartedPlaying(
parent_observer_->MediaStartedPlaying(video_type, render_frame_host);
}
-void PageLoadMetricsForwardObserver::OnFrameIntersectionUpdate(
+void PageLoadMetricsForwardObserver::OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const mojom::FrameIntersectionUpdate& intersection_update) {
+ const gfx::Rect& main_frame_intersection_rect) {
if (!parent_observer_)
return;
- parent_observer_->OnFrameIntersectionUpdate(rfh, intersection_update);
+ parent_observer_->OnMainFrameIntersectionRectChanged(
+ rfh, main_frame_intersection_rect);
+}
+
+void PageLoadMetricsForwardObserver::OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) {
+ if (!parent_observer_)
+ return;
+ parent_observer_->OnMainFrameViewportRectChanged(main_frame_viewport_rect);
}
// Don't need to forward FlushMetricsOnAppEnterBackground and OnComplete as they
// are dispatched to all trackers.
-PageLoadMetricsObserver::ObservePolicy
+PageLoadMetricsObserverInterface::ObservePolicy
PageLoadMetricsForwardObserver::FlushMetricsOnAppEnterBackground(
const mojom::PageLoadTiming& timing) {
return CONTINUE_OBSERVING;
@@ -345,9 +358,8 @@ void PageLoadMetricsForwardObserver::OnStorageAccessed(
}
void PageLoadMetricsForwardObserver::OnPrefetchLikely() {
- if (!parent_observer_)
- return;
- parent_observer_->OnPrefetchLikely();
+ // This event is delivered only for the primary page.
+ NOTREACHED();
}
void PageLoadMetricsForwardObserver::DidActivatePortal(
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.h b/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.h
index a39bb0fb6f8..01583a604d6 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer.h
@@ -6,7 +6,7 @@
#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_FORWARD_OBSERVER_H_
#include "base/memory/weak_ptr.h"
-#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
+#include "components/page_load_metrics/browser/page_load_metrics_observer_interface.h"
namespace page_load_metrics {
@@ -15,13 +15,14 @@ namespace page_load_metrics {
// PageLoadTracker.
// Note: This class should override all virtual methods so to forward the
// callback correctly.
-class PageLoadMetricsForwardObserver final : public PageLoadMetricsObserver {
+class PageLoadMetricsForwardObserver final
+ : public PageLoadMetricsObserverInterface {
public:
// Refers `parent_observer` as a weak pointer because it may be destructed
// at anytime when it returns STOP_OBSERVING in the callbacks for the events
// happening in the parent page.
explicit PageLoadMetricsForwardObserver(
- base::WeakPtr<PageLoadMetricsObserver> parent_observer);
+ base::WeakPtr<PageLoadMetricsObserverInterface> parent_observer);
PageLoadMetricsForwardObserver(const PageLoadMetricsForwardObserver&) =
delete;
@@ -31,7 +32,7 @@ class PageLoadMetricsForwardObserver final : public PageLoadMetricsObserver {
~PageLoadMetricsForwardObserver() override;
private:
- // PageLoadMetricsObserver implementation:
+ // PageLoadMetricsObserverInterface implementation:
const char* GetObserverName() const override;
ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url,
@@ -110,9 +111,11 @@ class PageLoadMetricsForwardObserver final : public PageLoadMetricsObserver {
void MediaStartedPlaying(
const content::WebContentsObserver::MediaPlayerInfo& video_type,
content::RenderFrameHost* render_frame_host) override;
- void OnFrameIntersectionUpdate(
+ void OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const mojom::FrameIntersectionUpdate& intersection_update) override;
+ const gfx::Rect& main_frame_intersection_rect) override;
+ void OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) override;
ObservePolicy FlushMetricsOnAppEnterBackground(
const mojom::PageLoadTiming& timing) override;
void OnComplete(const mojom::PageLoadTiming& timing) override;
@@ -149,7 +152,7 @@ class PageLoadMetricsForwardObserver final : public PageLoadMetricsObserver {
const std::vector<MemoryUpdate>& memory_updates) override;
// Holds the forward target observer running in the parent PageLoadTracker.
- base::WeakPtr<PageLoadMetricsObserver> parent_observer_;
+ base::WeakPtr<PageLoadMetricsObserverInterface> parent_observer_;
};
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer_unittest.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer_unittest.cc
index 26442d4b629..8b6e1daba68 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_forward_observer_unittest.cc
@@ -110,10 +110,10 @@ TEST_F(PageLoadMetricsForwardObserverTest, Basic) {
// Add a fenced frame.
content::RenderFrameHost* fenced_frame_root =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendFencedFrame();
{
- auto simulator = content::NavigationSimulator::CreateForFencedFrame(
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL(kTestUrl), fenced_frame_root);
ASSERT_NE(nullptr, simulator);
simulator->Commit();
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_memory_tracker_unittest.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_memory_tracker_unittest.cc
index a4f90c64471..e7c87ba92e0 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_memory_tracker_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_memory_tracker_unittest.cc
@@ -151,7 +151,7 @@ class PageLoadMetricsMemoryTrackerTest
// Returns the final RenderFrameHost after navigation commits.
content::RenderFrameHost* NavigateMainFrame(const std::string& url) {
- return NavigateFrame(url, web_contents()->GetMainFrame());
+ return NavigateFrame(url, web_contents()->GetPrimaryMainFrame());
}
// Returns the final RenderFrameHost after navigation commits.
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc
index 88025886e09..73b3b6fae4d 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_observer.cc
@@ -40,7 +40,7 @@ MemoryUpdate::MemoryUpdate(content::GlobalRenderFrameHostId id, int64_t delta)
: routing_id(id), delta_bytes(delta) {}
ExtraRequestCompleteInfo::ExtraRequestCompleteInfo(
- const url::Origin& origin_of_final_url,
+ const url::SchemeHostPort& final_url,
const net::IPEndPoint& remote_endpoint,
int frame_tree_node_id,
bool was_cached,
@@ -49,7 +49,7 @@ ExtraRequestCompleteInfo::ExtraRequestCompleteInfo(
network::mojom::RequestDestination request_destination,
int net_error,
std::unique_ptr<net::LoadTimingInfo> load_timing_info)
- : origin_of_final_url(origin_of_final_url),
+ : final_url(final_url),
remote_endpoint(remote_endpoint),
frame_tree_node_id(frame_tree_node_id),
was_cached(was_cached),
@@ -61,7 +61,7 @@ ExtraRequestCompleteInfo::ExtraRequestCompleteInfo(
ExtraRequestCompleteInfo::ExtraRequestCompleteInfo(
const ExtraRequestCompleteInfo& other)
- : origin_of_final_url(other.origin_of_final_url),
+ : final_url(other.final_url),
remote_endpoint(other.remote_endpoint),
frame_tree_node_id(other.frame_tree_node_id),
was_cached(other.was_cached),
@@ -86,10 +86,6 @@ const char* PageLoadMetricsObserver::GetObserverName() const {
return nullptr;
}
-base::WeakPtr<PageLoadMetricsObserver> PageLoadMetricsObserver::GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
-}
-
PageLoadMetricsObserver::ObservePolicy PageLoadMetricsObserver::OnStart(
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url,
@@ -98,13 +94,6 @@ PageLoadMetricsObserver::ObservePolicy PageLoadMetricsObserver::OnStart(
}
PageLoadMetricsObserver::ObservePolicy
-PageLoadMetricsObserver::OnFencedFramesStart(
- content::NavigationHandle* navigation_handle,
- const GURL& currently_committed_url) {
- return STOP_OBSERVING;
-}
-
-PageLoadMetricsObserver::ObservePolicy
PageLoadMetricsObserver::OnPrerenderStart(
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url) {
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_observer.h b/chromium/components/page_load_metrics/browser/page_load_metrics_observer.h
index 14ade007f3e..f27556a323c 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_observer.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_observer.h
@@ -8,9 +8,10 @@
#include <memory>
#include <string>
-#include "base/memory/weak_ptr.h"
+#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer_delegate.h"
+#include "components/page_load_metrics/browser/page_load_metrics_observer_interface.h"
#include "components/page_load_metrics/common/page_load_timing.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents_observer.h"
@@ -23,15 +24,6 @@
#include "third_party/blink/public/common/use_counter/use_counter_feature.h"
#include "url/gurl.h"
-namespace content {
-class NavigationHandle;
-class RenderFrameHost;
-} // namespace content
-
-namespace net {
-struct LoadTimingInfo;
-}
-
namespace page_load_metrics {
// Get bucketed value of viewport initial scale from given MobileFriendliness
@@ -42,32 +34,6 @@ int GetBucketedViewportInitialScale(const blink::MobileFriendliness& mf);
// metrics.
int GetBucketedViewportHardcodedWidth(const blink::MobileFriendliness& mf);
-// Struct for storing per-frame memory update data.
-struct MemoryUpdate {
- content::GlobalRenderFrameHostId routing_id;
- int64_t delta_bytes;
- MemoryUpdate(content::GlobalRenderFrameHostId id, int64_t delta);
-};
-
-// Storage types reported to page load metrics observers on storage
-// accesses.
-enum class StorageType {
- kLocalStorage,
- kSessionStorage,
- kFileSystem,
- kIndexedDb,
- kCacheStorage
-};
-
-// Information related to failed provisional loads.
-struct FailedProvisionalLoadInfo {
- FailedProvisionalLoadInfo(base::TimeDelta interval, net::Error error);
- ~FailedProvisionalLoadInfo();
-
- base::TimeDelta time_to_failed_provisional_load;
- net::Error error;
-};
-
// Information related to whether an associated action, such as a navigation or
// an abort, was initiated by a user. Clicking a link or tapping on a UI
// element are examples of user initiation actions.
@@ -140,8 +106,6 @@ struct PageRenderData {
// Information related to layout shift normalization for different strategies.
struct NormalizedCLSData {
- NormalizedCLSData() = default;
-
// Maximum CLS of session windows. The gap between two consecutive shifts is
// not bigger than 1000ms and the maximum window size is 5000ms.
float session_windows_gap1000ms_max5000ms_max_cls = 0.0;
@@ -150,89 +114,12 @@ struct NormalizedCLSData {
bool data_tainted = false;
};
-// Container for various information about a completed request within a page
-// load.
-struct ExtraRequestCompleteInfo {
- ExtraRequestCompleteInfo(
- const url::Origin& origin_of_final_url,
- const net::IPEndPoint& remote_endpoint,
- int frame_tree_node_id,
- bool was_cached,
- int64_t raw_body_bytes,
- int64_t original_network_content_length,
- network::mojom::RequestDestination request_destination,
- int net_error,
- std::unique_ptr<net::LoadTimingInfo> load_timing_info);
-
- ExtraRequestCompleteInfo(const ExtraRequestCompleteInfo& other);
-
- ~ExtraRequestCompleteInfo();
-
- // The origin of the final URL for the request (final = after redirects).
- //
- // The full URL is not available, because in some cases the path and query
- // be sanitized away - see https://crbug.com/973885.
- const url::Origin origin_of_final_url;
-
- // The host (IP address) and port for the request.
- const net::IPEndPoint remote_endpoint;
-
- // The frame tree node id that initiated the request.
- const int frame_tree_node_id;
-
- // True if the resource was loaded from cache.
- const bool was_cached;
-
- // The number of body (not header) prefilter bytes.
- const int64_t raw_body_bytes;
-
- // The number of body (not header) bytes that the data reduction proxy saw
- // before it compressed the requests.
- const int64_t original_network_content_length;
-
- // The type of the request as gleaned from the mime type. This may
- // be more accurate than the type in the ExtraRequestStartInfo since we can
- // examine the type headers that arrived with the request. During XHRs, we
- // sometimes see resources come back as a different type than we expected.
- const network::mojom::RequestDestination request_destination;
-
- // The network error encountered by the request, as defined by
- // net/base/net_error_list.h. If no error was encountered, this value will be
- // 0.
- const int net_error;
-
- // Additional timing information.
- const std::unique_ptr<net::LoadTimingInfo> load_timing_info;
-};
-
-// Interface for PageLoadMetrics observers. All instances of this class are
+// Base class for PageLoadMetrics observers. All instances of this class are
// owned by the PageLoadTracker tracking a page load. The page would be a
// primary page, Prerendering page, FencedFrames page, or pages for new other
// features based on MPArch.
-// TODO(https://crbug.com/1301880): Split observer interfaces into a pure
-// virtual class so that PageLoadMetricsForwardObserver can override it
-// directly. It helps to ensure that the class override all virtual methods
-// to forward all events certainly. Other inheritances will override it via
-// PageLoadMetricsObserver.
-class PageLoadMetricsObserver {
+class PageLoadMetricsObserver : public PageLoadMetricsObserverInterface {
public:
- // ObservePolicy is used as a return value on some PageLoadMetricsObserver
- // callbacks to indicate how the observer would like to handle subsequent
- // callbacks. Observers that wish to continue observing metric callbacks
- // should return CONTINUE_OBSERVING; observers that wish to stop observing
- // callbacks should return STOP_OBSERVING; observers that wish to forward
- // callbacks to the one bound with the parent page should return
- // FORWARD_OBSERVING. Observers that return STOP_OBSERVING or
- // FORWARD_OBSERVING may be deleted. If the observer in the parent page
- // receives forward metrics via FORWARD_OBSERVING, and returns STOP_OBSERVING,
- // It just stop observing forward metrics, and still see other callbacks for
- // the orinally bound page.
- enum ObservePolicy {
- CONTINUE_OBSERVING,
- STOP_OBSERVING,
- FORWARD_OBSERVING,
- };
-
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class LargestContentState {
@@ -243,370 +130,135 @@ class PageLoadMetricsObserver {
kMaxValue = kFoundButNotReported,
};
- using FrameTreeNodeId = int;
-
PageLoadMetricsObserver();
- virtual ~PageLoadMetricsObserver();
+ ~PageLoadMetricsObserver() override;
static bool IsStandardWebPageMimeType(const std::string& mime_type);
- // Obtains a weak pointer for this instance.
- base::WeakPtr<PageLoadMetricsObserver> GetWeakPtr();
-
// Gets/Sets the delegate. The delegate must outlive the observer and is
// normally set when the observer is first registered for the page load. The
// delegate can only be set once.
const PageLoadMetricsObserverDelegate& GetDelegate() const;
void SetDelegate(PageLoadMetricsObserverDelegate*);
- // Returns the observer name. It should points a fixed address that is bound
- // to the class as we use the pointer as a key in a map at PageLoadTracker.
- // Should be implemented when the class needs to return FORWARD_OBSERVING.
- // TODO(https://crbug.com/1301880): Make all inheritances override this method
- // and make it pure virtual method.
- virtual const char* GetObserverName() const;
-
- // The page load started, with the given navigation handle.
- // currently_committed_url contains the URL of the committed page load at the
- // time the navigation for navigation_handle was initiated, or the empty URL
- // if there was no committed page load at the time the navigation was
- // initiated.
- virtual ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
- const GURL& currently_committed_url,
- bool started_in_foreground);
-
- // For FencedFrames pages, OnFencedFramesStart is called instead of OnStart.
- // The default implementation returns STOP_OBSERVING, so that observers that
- // are not aware of FencedFrames will not mix FencedFrames metrics into the
- // existing reports. FencedFrames will show different characteristics as it's
- // content is likely a subframe rather than a main frame.
- // TODO(crbug.com/1301880): FencedFrames support is still in progress.
- virtual ObservePolicy OnFencedFramesStart(
- content::NavigationHandle* navigation_handle,
- const GURL& currently_committed_url);
-
- // For prerendered pages, OnPrerenderStart is called instead of OnStart. The
- // default implementation returns STOP_OBSERVING, so that observers that are
- // not aware of prerender will not see prerendered page loads.
- // TODO(crbug.com/1190112): Prerender support is still in progress. Observers
- // may not receive some signals.
- virtual ObservePolicy OnPrerenderStart(
- content::NavigationHandle* navigation_handle,
- const GURL& currently_committed_url);
-
- // OnRedirect is triggered when a page load redirects to another URL.
- // The navigation handle holds relevant data for the navigation, but will
- // be destroyed soon after this call. Don't hold a reference to it. This can
- // be called multiple times.
- virtual ObservePolicy OnRedirect(
- content::NavigationHandle* navigation_handle);
-
- // OnCommit is triggered when a page load commits, i.e. when we receive the
- // first data for the request. The navigation handle holds relevant data for
- // the navigation, but will be destroyed soon after this call. Don't hold a
- // reference to it.
- // Observers that return STOP_OBSERVING will not receive any additional
- // callbacks, and will be deleted after invocation of this method returns.
- virtual ObservePolicy OnCommit(content::NavigationHandle* navigation_handle);
-
- // OnDidInternalNavigationAbort is triggered when the main frame navigation
- // aborts with HTTP responses that don't commit, such as HTTP 204 responses
- // and downloads. Note that |navigation_handle| will be destroyed
- // soon after this call. Don't hold a reference to it.
- virtual void OnDidInternalNavigationAbort(
- content::NavigationHandle* navigation_handle) {}
-
- // ReadyToCommitNextNavigation is triggered when a frame navigation is
- // ready to commit, but has not yet been committed. This is only called by
- // a PageLoadTracker for a committed load, meaning that this call signals we
- // are ready to commit a navigation to a new page.
- virtual void ReadyToCommitNextNavigation(
- content::NavigationHandle* navigation_handle) {}
-
- // OnDidFinishSubFrameNavigation is triggered when a sub-frame of the
- // committed page has finished navigating. It has either committed, aborted,
- // was a same document navigation, or has been replaced. It is up to the
- // observer to query |navigation_handle| to determine which happened. Note
- // that |navigation_handle| will be destroyed soon after this call. Don't
- // hold a reference to it.
- virtual void OnDidFinishSubFrameNavigation(
- content::NavigationHandle* navigation_handle) {}
-
- // OnCommitSameDocumentNavigation is triggered when a same-document navigation
- // commits within the main frame of the current page. Note that
- // |navigation_handle| will be destroyed soon after this call. Don't hold a
- // reference to it.
- virtual void OnCommitSameDocumentNavigation(
- content::NavigationHandle* navigation_handle) {}
-
- // OnHidden is triggered when a page leaves the foreground. It does not fire
- // when a foreground page is permanently closed; for that, listen to
- // OnComplete instead.
- virtual ObservePolicy OnHidden(const mojom::PageLoadTiming& timing);
-
- // OnShown is triggered when a page is brought to the foreground. It does not
- // fire when the page first loads; for that, listen for OnStart instead.
- virtual ObservePolicy OnShown();
-
- // OnEnterBackForwardCache is triggered when a page is put into the
- // back-forward cache. This page can be reused in the future for a
- // back-forward navigation, in this case this OnRestoreFromBackForwardCache
- // will be called for this PageLoadMetricsObserver. Note that the page in the
- // back-forward cache can be evicted at any moment, and in this case
- // OnComplete will be called.
- //
- // At the moment, the default implementtion of OnEnterBackForwardCache()
- // invokes OnComplete and returns STOP_OBSERVING, so the page will not be
- // tracked after it is stored in the back-forward cache and after it is
- // restored. Return CONTINUE_OBSERVING explicitly to ensure that you cover the
- // entire lifetime of the page, which is important for cases like tracking
- // feature use counts or total network usage.
- //
- // TODO(hajimehoshi): Consider to remove |timing| argument by adding a
- // function to PageLoadMetricsObserverDelegate. This would require
- // investigation to determine exposing the timing from the delegate would be
- // really safe.
- virtual ObservePolicy OnEnterBackForwardCache(
- const mojom::PageLoadTiming& timing);
-
- // OnRestoreFromBackForwardCache is triggered when a page is restored from
- // the back-forward cache.
- virtual void OnRestoreFromBackForwardCache(
+ // PageLoadMetricsObserverInterface implementation:
+ const char* GetObserverName() const override;
+ ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url,
+ bool started_in_foreground) override;
+ ObservePolicy OnPrerenderStart(content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override;
+ ObservePolicy OnRedirect(
+ content::NavigationHandle* navigation_handle) override;
+ ObservePolicy OnCommit(content::NavigationHandle* navigation_handle) override;
+ void OnDidInternalNavigationAbort(
+ content::NavigationHandle* navigation_handle) override {}
+ void ReadyToCommitNextNavigation(
+ content::NavigationHandle* navigation_handle) override {}
+ void OnDidFinishSubFrameNavigation(
+ content::NavigationHandle* navigation_handle) override {}
+ void OnCommitSameDocumentNavigation(
+ content::NavigationHandle* navigation_handle) override {}
+ ObservePolicy OnHidden(const mojom::PageLoadTiming& timing) override;
+ ObservePolicy OnShown() override;
+ ObservePolicy OnEnterBackForwardCache(
+ const mojom::PageLoadTiming& timing) override;
+ void OnRestoreFromBackForwardCache(
const mojom::PageLoadTiming& timing,
- content::NavigationHandle* navigation_handle) {}
-
- // Called before OnCommit. The observer should return whether it wishes to
- // observe navigations whose main resource has MIME type |mine_type|. The
- // default is to observe HTML and XHTML only. Note that PageLoadTrackers only
- // track XHTML, HTML, and MHTML (related/multipart).
- virtual ObservePolicy ShouldObserveMimeType(
- const std::string& mime_type) const;
-
- // The callbacks below are only invoked after a navigation commits, for
- // tracked page loads. Page loads that don't meet the criteria for being
- // tracked at the time a navigation commits will not receive any of the
- // callbacks below.
-
- // OnTimingUpdate is triggered when an updated PageLoadTiming is available at
- // the page (page is essentially main frame, with merged values across all
- // frames for some paint timing values) or subframe level. This method may be
- // called multiple times over the course of the page load. This method is
- // currently only intended for use in testing. Most implementers should
- // implement one of the On* callbacks, such as OnFirstContentfulPaint or
- // OnDomContentLoadedEventStart. Please email loading-dev@chromium.org if you
- // intend to override this method.
- //
- // If |subframe_rfh| is nullptr, the update took place in the main frame.
- virtual void OnTimingUpdate(content::RenderFrameHost* subframe_rfh,
- const mojom::PageLoadTiming& timing) {}
-
- virtual void OnMobileFriendlinessUpdate(
- const blink::MobileFriendliness& mobile_friendliness) {}
-
- // OnInputTimingUpdate is triggered when an updated InputTiming is available
- // at the subframe level. This method may be called multiple times over the
- // course of the page load.
- virtual void OnInputTimingUpdate(
+ content::NavigationHandle* navigation_handle) override {}
+ ObservePolicy ShouldObserveMimeType(
+ const std::string& mime_type) const override;
+ void OnTimingUpdate(content::RenderFrameHost* subframe_rfh,
+ const mojom::PageLoadTiming& timing) override {}
+ void OnMobileFriendlinessUpdate(
+ const blink::MobileFriendliness& mobile_friendliness) override {}
+ void OnInputTimingUpdate(
content::RenderFrameHost* subframe_rfh,
- const mojom::InputTiming& input_timing_delta) {}
-
- // OnRenderDataUpdate is triggered when an updated PageRenderData is available
- // at the subframe level. This method may be called multiple times over the
- // course of the page load.
- virtual void OnSubFrameRenderDataUpdate(
+ const mojom::InputTiming& input_timing_delta) override {}
+ void OnSubFrameRenderDataUpdate(
content::RenderFrameHost* subframe_rfh,
- const mojom::FrameRenderDataUpdate& render_data) {}
-
- // Triggered when an updated CpuTiming is available at the page or subframe
- // level. This method is intended for monitoring cpu usage and load across
- // the frames on a page during navigation.
- virtual void OnCpuTimingUpdate(content::RenderFrameHost* subframe_rfh,
- const mojom::CpuTiming& timing) {}
-
- // OnUserInput is triggered when a new user input is passed in to
- // web_contents.
- virtual void OnUserInput(const blink::WebInputEvent& event,
- const mojom::PageLoadTiming& timing) {}
-
- // The following methods are invoked at most once, when the timing for the
- // associated event first becomes available.
- virtual void OnDomContentLoadedEventStart(
- const mojom::PageLoadTiming& timing) {}
- virtual void OnLoadEventStart(const mojom::PageLoadTiming& timing) {}
- virtual void OnFirstLayout(const mojom::PageLoadTiming& timing) {}
- virtual void OnParseStart(const mojom::PageLoadTiming& timing) {}
- virtual void OnParseStop(const mojom::PageLoadTiming& timing) {}
-
- // On*PaintInPage(...) are invoked when the first relevant paint in the page,
- // across all frames, is observed.
- virtual void OnFirstPaintInPage(const mojom::PageLoadTiming& timing) {}
- virtual void OnFirstImagePaintInPage(const mojom::PageLoadTiming& timing) {}
- virtual void OnFirstContentfulPaintInPage(
- const mojom::PageLoadTiming& timing) {}
-
- // These are called once every time when the page is restored from the
- // back-forward cache. |index| indicates |index|-th restore.
- virtual void OnFirstPaintAfterBackForwardCacheRestoreInPage(
+ const mojom::FrameRenderDataUpdate& render_data) override {}
+ void OnCpuTimingUpdate(content::RenderFrameHost* subframe_rfh,
+ const mojom::CpuTiming& timing) override {}
+ void OnUserInput(const blink::WebInputEvent& event,
+ const mojom::PageLoadTiming& timing) override {}
+ void OnDomContentLoadedEventStart(
+ const mojom::PageLoadTiming& timing) override {}
+ void OnLoadEventStart(const mojom::PageLoadTiming& timing) override {}
+ void OnFirstLayout(const mojom::PageLoadTiming& timing) override {}
+ void OnParseStart(const mojom::PageLoadTiming& timing) override {}
+ void OnParseStop(const mojom::PageLoadTiming& timing) override {}
+ void OnFirstPaintInPage(const mojom::PageLoadTiming& timing) override {}
+ void OnFirstImagePaintInPage(const mojom::PageLoadTiming& timing) override {}
+ void OnFirstContentfulPaintInPage(
+ const mojom::PageLoadTiming& timing) override {}
+ void OnFirstPaintAfterBackForwardCacheRestoreInPage(
const mojom::BackForwardCacheTiming& timing,
- size_t index) {}
- virtual void OnFirstInputAfterBackForwardCacheRestoreInPage(
+ size_t index) override {}
+ void OnFirstInputAfterBackForwardCacheRestoreInPage(
const mojom::BackForwardCacheTiming& timing,
- size_t index) {}
-
- // This is called several times on requestAnimationFrame after the page is
- // restored from the back-forward cache. The number of the calls is hard-
- // coded as WebPerformance::
- // kRequestAnimationFramesToRecordAfterBackForwardCacheRestore.
- virtual void OnRequestAnimationFramesAfterBackForwardCacheRestoreInPage(
+ size_t index) override {}
+ void OnRequestAnimationFramesAfterBackForwardCacheRestoreInPage(
const mojom::BackForwardCacheTiming& timing,
- size_t index) {}
-
- // Unlike other paint callbacks, OnFirstMeaningfulPaintInMainFrameDocument is
- // tracked per document, and is reported for the main frame document only.
- virtual void OnFirstMeaningfulPaintInMainFrameDocument(
- const mojom::PageLoadTiming& timing) {}
-
- virtual void OnFirstInputInPage(const mojom::PageLoadTiming& timing) {}
-
- // Invoked when there is an update to the loading behavior_flags in the given
- // frame.
- virtual void OnLoadingBehaviorObserved(content::RenderFrameHost* rfh,
- int behavior_flags) {}
-
- // Invoked when new use counter features are observed across all frames.
- virtual void OnFeaturesUsageObserved(
+ size_t index) override {}
+ void OnFirstMeaningfulPaintInMainFrameDocument(
+ const mojom::PageLoadTiming& timing) override {}
+ void OnFirstInputInPage(const mojom::PageLoadTiming& timing) override {}
+ void OnLoadingBehaviorObserved(content::RenderFrameHost* rfh,
+ int behavior_flags) override {}
+ void OnFeaturesUsageObserved(
content::RenderFrameHost* rfh,
- const std::vector<blink::UseCounterFeature>& features) {}
-
- // The smoothness metrics is shared over shared-memory. The observer should
- // create a mapping (by calling |shared_memory.Map()|) so that they are able
- // to read from the shared memory.
- virtual void SetUpSharedMemoryForSmoothness(
- const base::ReadOnlySharedMemoryRegion& shared_memory) {}
-
- // Invoked when there is data use for loading a resource on the page
- // for a given render frame host. This only contains resources that have had
- // new data use since the last callback. Resources loaded from the cache only
- // receive a single update. Multiple updates can be received for the same
- // resource if it is loaded in multiple documents.
- virtual void OnResourceDataUseObserved(
+ const std::vector<blink::UseCounterFeature>& features) override {}
+ void SetUpSharedMemoryForSmoothness(
+ const base::ReadOnlySharedMemoryRegion& shared_memory) override {}
+ void OnResourceDataUseObserved(
content::RenderFrameHost* rfh,
- const std::vector<mojom::ResourceDataUpdatePtr>& resources) {}
-
- // Invoked when a media element starts playing.
- virtual void MediaStartedPlaying(
+ const std::vector<mojom::ResourceDataUpdatePtr>& resources) override {}
+ void MediaStartedPlaying(
const content::WebContentsObserver::MediaPlayerInfo& video_type,
- content::RenderFrameHost* render_frame_host) {}
-
- // Invoked when a frame's intersections with page elements changes and an
- // update is received. The main_frame_document_intersection_rect
- // returns an empty rect for out of view subframes and the root document size
- // for the main frame.
- // TODO(crbug/1048175): Expose intersections to observers via shared delegate.
- virtual void OnFrameIntersectionUpdate(
+ content::RenderFrameHost* render_frame_host) override {}
+ void OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const mojom::FrameIntersectionUpdate& intersection_update) {}
-
- // Invoked when the UMA metrics subsystem is persisting metrics as the
- // application goes into the background, on platforms where the browser
- // process may be killed after backgrounding (Android). Implementers should
- // persist any metrics that have been buffered in memory in this callback, as
- // the application may be killed at any time after this method is invoked
- // without further notification. Note that this may be called both for
- // provisional loads as well as committed loads. Implementations that only
- // want to track committed loads should check GetDelegate().DidCommit()
- // to determine if the load had committed. If the implementation returns
- // CONTINUE_OBSERVING, this method may be called multiple times per observer,
- // once for each time that the application enters the background.
- //
- // The default implementation does nothing, and returns CONTINUE_OBSERVING.
- virtual ObservePolicy FlushMetricsOnAppEnterBackground(
- const mojom::PageLoadTiming& timing);
-
- // One of OnComplete or OnFailedProvisionalLoad is invoked for tracked page
- // loads, immediately before the observer is deleted. These callbacks will not
- // be invoked for page loads that did not meet the criteria for being tracked
- // at the time the navigation completed. The PageLoadTiming struct contains
- // timing data. Other useful data collected over the course of the page load
- // is exposed by the observer delegate API. Most observers should not need
- // to implement these callbacks, and should implement the On* timing callbacks
- // instead.
-
- // OnComplete is invoked for tracked page loads that committed, immediately
- // before the observer is deleted. Observers that implement OnComplete may
- // also want to implement FlushMetricsOnAppEnterBackground, to avoid loss of
- // data if the application is killed while in the background (this happens
- // frequently on Android).
- virtual void OnComplete(const mojom::PageLoadTiming& timing) {}
-
- // OnFailedProvisionalLoad is invoked for tracked page loads that did not
- // commit, immediately before the observer is deleted.
- virtual void OnFailedProvisionalLoad(
- const FailedProvisionalLoadInfo& failed_provisional_load_info) {}
-
- // Called whenever a request is loaded for this page load. This is restricted
- // to requests with HTTP or HTTPS only schemes.
- virtual void OnLoadedResource(
- const ExtraRequestCompleteInfo& extra_request_complete_info) {}
-
- virtual void FrameReceivedUserActivation(
- content::RenderFrameHost* render_frame_host) {}
-
- // Called when the display property changes on the frame.
- virtual void FrameDisplayStateChanged(
- content::RenderFrameHost* render_frame_host,
- bool is_display_none) {}
-
- // Called when a frames size changes.
- virtual void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
- const gfx::Size& frame_size) {}
-
- virtual void OnRenderFrameDeleted(
- content::RenderFrameHost* render_frame_host) {}
- virtual void OnSubFrameDeleted(int frame_tree_node_id) {}
-
- // Called when a cookie is read for a resource request or by document.cookie.
- virtual void OnCookiesRead(const GURL& url,
- const GURL& first_party_url,
- const net::CookieList& cookie_list,
- bool blocked_by_policy) {}
-
- // Called when a cookie is set by a header or via document.cookie.
- virtual void OnCookieChange(const GURL& url,
- const GURL& first_party_url,
- const net::CanonicalCookie& cookie,
- bool blocked_by_policy) {}
-
- // Called when a storage access attempt by the origin |url| to |storage_type|
- // is checked by the content settings manager. |blocked_by_policy| is false
- // when cookie access is not allowed for |url|.
- virtual void OnStorageAccessed(const GURL& url,
- const GURL& first_party_url,
- bool blocked_by_policy,
- StorageType access_type) {}
-
- // Called when prefetch is likely to occur in this page load.
- virtual void OnPrefetchLikely() {}
-
- // Called when the page tracked was just activated after being loaded inside a
- // portal.
- virtual void DidActivatePortal(base::TimeTicks activation_time) {}
-
- // Called when the page tracked was just activated after being prerendered.
- // |navigation_handle| is for the activation navigation.
- virtual void DidActivatePrerenderedPage(
- content::NavigationHandle* navigation_handle) {}
-
- // Called when V8 per-frame memory usage updates are available. Each
- // MemoryUpdate consists of a GlobalRenderFrameHostId and a nonzero int64_t
- // change in bytes used.
- virtual void OnV8MemoryChanged(
- const std::vector<MemoryUpdate>& memory_updates) {}
+ const gfx::Rect& main_frame_intersection_rect) override {}
+ void OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) override {}
+ ObservePolicy FlushMetricsOnAppEnterBackground(
+ const mojom::PageLoadTiming& timing) override;
+ void OnComplete(const mojom::PageLoadTiming& timing) override {}
+ void OnFailedProvisionalLoad(
+ const FailedProvisionalLoadInfo& failed_provisional_load_info) override {}
+ void OnLoadedResource(
+ const ExtraRequestCompleteInfo& extra_request_complete_info) override {}
+ void FrameReceivedUserActivation(
+ content::RenderFrameHost* render_frame_host) override {}
+ void FrameDisplayStateChanged(content::RenderFrameHost* render_frame_host,
+ bool is_display_none) override {}
+ void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
+ const gfx::Size& frame_size) override {}
+ void OnRenderFrameDeleted(
+ content::RenderFrameHost* render_frame_host) override {}
+ void OnSubFrameDeleted(int frame_tree_node_id) override {}
+ void OnCookiesRead(const GURL& url,
+ const GURL& first_party_url,
+ const net::CookieList& cookie_list,
+ bool blocked_by_policy) override {}
+ void OnCookieChange(const GURL& url,
+ const GURL& first_party_url,
+ const net::CanonicalCookie& cookie,
+ bool blocked_by_policy) override {}
+ void OnStorageAccessed(const GURL& url,
+ const GURL& first_party_url,
+ bool blocked_by_policy,
+ StorageType access_type) override {}
+ void OnPrefetchLikely() override {}
+ void DidActivatePortal(base::TimeTicks activation_time) override {}
+ void DidActivatePrerenderedPage(
+ content::NavigationHandle* navigation_handle) override {}
+ void OnV8MemoryChanged(
+ const std::vector<MemoryUpdate>& memory_updates) override {}
private:
- PageLoadMetricsObserverDelegate* delegate_ = nullptr;
-
- base::WeakPtrFactory<PageLoadMetricsObserver> weak_factory_{this};
+ raw_ptr<PageLoadMetricsObserverDelegate> delegate_ = nullptr;
};
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h
index a0191012b1b..521703e5789 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_delegate.h
@@ -13,6 +13,7 @@
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/scoped_visibility_tracker.h"
+#include "url/gurl.h"
namespace content {
class WebContents;
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_observer_interface.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_interface.cc
new file mode 100644
index 00000000000..4e91d1dce38
--- /dev/null
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_interface.cc
@@ -0,0 +1,12 @@
+// Copyright 2022 The Chromium 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/page_load_metrics/browser/page_load_metrics_observer_interface.h"
+
+namespace page_load_metrics {
+
+PageLoadMetricsObserverInterface::PageLoadMetricsObserverInterface() = default;
+PageLoadMetricsObserverInterface::~PageLoadMetricsObserverInterface() = default;
+
+} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_observer_interface.h b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_interface.h
new file mode 100644
index 00000000000..9f31d28b6b1
--- /dev/null
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_observer_interface.h
@@ -0,0 +1,537 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_OBSERVER_INTERFACE_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_OBSERVER_INTERFACE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "components/page_load_metrics/common/page_load_metrics.mojom.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "net/base/net_errors.h"
+#include "net/cookies/canonical_cookie.h"
+#include "third_party/blink/public/common/input/web_input_event.h"
+#include "third_party/blink/public/common/mobile_metrics/mobile_friendliness.h"
+#include "third_party/blink/public/common/use_counter/use_counter_feature.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
+
+#include "url/gurl.h"
+
+namespace content {
+class NavigationHandle;
+class RenderFrameHost;
+} // namespace content
+
+namespace net {
+struct LoadTimingInfo;
+}
+
+namespace page_load_metrics {
+
+// Storage types reported to page load metrics observers on storage accesses.
+enum class StorageType {
+ kLocalStorage,
+ kSessionStorage,
+ kFileSystem,
+ kIndexedDb,
+ kCacheStorage
+};
+
+// Container for various information about a completed request within a page
+// load.
+struct ExtraRequestCompleteInfo {
+ ExtraRequestCompleteInfo(
+ const url::SchemeHostPort& final_url,
+ const net::IPEndPoint& remote_endpoint,
+ int frame_tree_node_id,
+ bool was_cached,
+ int64_t raw_body_bytes,
+ int64_t original_network_content_length,
+ network::mojom::RequestDestination request_destination,
+ int net_error,
+ std::unique_ptr<net::LoadTimingInfo> load_timing_info);
+
+ ExtraRequestCompleteInfo(const ExtraRequestCompleteInfo& other);
+
+ ~ExtraRequestCompleteInfo();
+
+ // The scheme/host/port of the final URL for the request
+ // (final = after redirects).
+ //
+ // The full URL is not available, because in some cases the path and query
+ // may be sanitized away - see https://crbug.com/973885.
+ const url::SchemeHostPort final_url;
+
+ // The host (IP address) and port for the request.
+ const net::IPEndPoint remote_endpoint;
+
+ // The frame tree node id that initiated the request.
+ const int frame_tree_node_id;
+
+ // True if the resource was loaded from cache.
+ const bool was_cached;
+
+ // The number of body (not header) prefilter bytes.
+ const int64_t raw_body_bytes;
+
+ // The number of body (not header) bytes that the data reduction proxy saw
+ // before it compressed the requests.
+ const int64_t original_network_content_length;
+
+ // The type of the request as gleaned from the mime type. This may
+ // be more accurate than the type in the ExtraRequestStartInfo since we can
+ // examine the type headers that arrived with the request. During XHRs, we
+ // sometimes see resources come back as a different type than we expected.
+ const network::mojom::RequestDestination request_destination;
+
+ // The network error encountered by the request, as defined by
+ // net/base/net_error_list.h. If no error was encountered, this value will
+ // be 0.
+ const int net_error;
+
+ // Additional timing information.
+ const std::unique_ptr<net::LoadTimingInfo> load_timing_info;
+};
+
+// Information related to failed provisional loads.
+struct FailedProvisionalLoadInfo {
+ FailedProvisionalLoadInfo(base::TimeDelta interval, net::Error error);
+ ~FailedProvisionalLoadInfo();
+
+ base::TimeDelta time_to_failed_provisional_load;
+ net::Error error;
+};
+
+// Struct for storing per-frame memory update data.
+struct MemoryUpdate {
+ content::GlobalRenderFrameHostId routing_id;
+ int64_t delta_bytes;
+ MemoryUpdate(content::GlobalRenderFrameHostId id, int64_t delta);
+};
+
+// Interface for PageLoadMetrics observers. Only PageLoadMetricsForwardObserver
+// should inherit this interface directly, and others should do
+// PageLoadMetricsObserver class.
+// All virtual methods in this class should be pure virtual.
+class PageLoadMetricsObserverInterface {
+ public:
+ // ObservePolicy is used as a return value on some PageLoadMetricsObserver
+ // callbacks to indicate how the observer would like to handle subsequent
+ // callbacks. Observers that wish to continue observing metric callbacks
+ // should return CONTINUE_OBSERVING; observers that wish to stop observing
+ // callbacks should return STOP_OBSERVING; observers that wish to forward
+ // callbacks to the one bound with the parent page should return
+ // FORWARD_OBSERVING. Observers that return STOP_OBSERVING or
+ // FORWARD_OBSERVING may be deleted. If the observer in the parent page
+ // receives forward metrics via FORWARD_OBSERVING, and returns STOP_OBSERVING,
+ // It just stop observing forward metrics, and still see other callbacks for
+ // the orinally bound page.
+ // Most events requiring preprocesses, such as lifecycle events, are forwarded
+ // to the outer page at the PageLoadTracker layer, and only events that are
+ // directly delivered to the observers need FORWARD_OBSERVING. See
+ // PageLoadMetricsForwardObserver to know which events need the observer layer
+ // forwarding. Eventually, we may treat all forwarding at the PageLoadTracker
+ // layer to deprecate the FORWARD_OBSERVING for simplicity. FORWARD_OBSERVING
+ // is available only for OnFencedFramesStart().
+ enum ObservePolicy {
+ CONTINUE_OBSERVING,
+ STOP_OBSERVING,
+ FORWARD_OBSERVING,
+ };
+
+ using FrameTreeNodeId = int;
+
+ PageLoadMetricsObserverInterface();
+ virtual ~PageLoadMetricsObserverInterface();
+
+ PageLoadMetricsObserverInterface(const PageLoadMetricsObserverInterface&) =
+ delete;
+ PageLoadMetricsObserverInterface& operator=(
+ const PageLoadMetricsObserverInterface&) = delete;
+
+ // Obtains a weak pointer for this instance.
+ base::WeakPtr<PageLoadMetricsObserverInterface> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ // Returns the observer name. It should points a fixed address that is bound
+ // to the class as we use the pointer as a key in a map at PageLoadTracker.
+ // Should be implemented when the class needs to return FORWARD_OBSERVING.
+ // TODO(https://crbug.com/1301880): Make all inheritances override this method
+ // and make it pure virtual method.
+ virtual const char* GetObserverName() const = 0;
+
+ // The page load started, with the given navigation handle.
+ // currently_committed_url contains the URL of the committed page load at the
+ // time the navigation for navigation_handle was initiated, or the empty URL
+ // if there was no committed page load at the time the navigation was
+ // initiated.
+ virtual ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url,
+ bool started_in_foreground) = 0;
+
+ // For FencedFrames pages, OnFencedFramesStart is called instead of OnStart.
+ // This method is pure virtual and each observer must explicitly override it
+ // and return appropriate policy. Currently, all observers that are not aware
+ // of FencedFrames return STOP_OBSERVING not to mix FencedFrames metrics into
+ // the existing reports. FencedFrames will show different characteristics as
+ // its content is likely a subframe rather than a main frame. A guideline is:
+ //
+ // - FORWARD_OBSERVING: Default. Use it if no special reason.
+ // - CONTINUE_OBSERVING: Use it if the observer want to capture events for
+ // FencedFrames in a different way, e.g. using a FencedFrames variant for
+ // name of histogram.
+ // - STOP_OBSERVING: Use it if the observer want to exclude metrics to
+ // FencedFrames from the reports. Even with this policy, FencedFrames still
+ // affect per-outermost page lifecycle events that are preprocessed in the
+ // PageLoadTracker
+ //
+ // TODO(crbug.com/1317494): FencedFrames support is still in progress. Update
+ // the above description once we fixed all subclasses.
+ virtual ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) = 0;
+
+ // For prerendered pages, OnPrerenderStart is called instead of OnStart. The
+ // default implementation returns STOP_OBSERVING, so that observers that are
+ // not aware of prerender will not see prerendered page loads.
+ // TODO(crbug.com/1190112): Prerender support is still in progress. Observers
+ // may not receive some signals.
+ virtual ObservePolicy OnPrerenderStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) = 0;
+
+ // OnRedirect is triggered when a page load redirects to another URL.
+ // The navigation handle holds relevant data for the navigation, but will
+ // be destroyed soon after this call. Don't hold a reference to it. This can
+ // be called multiple times.
+ virtual ObservePolicy OnRedirect(
+ content::NavigationHandle* navigation_handle) = 0;
+
+ // OnCommit is triggered when a page load commits, i.e. when we receive the
+ // first data for the request. The navigation handle holds relevant data for
+ // the navigation, but will be destroyed soon after this call. Don't hold a
+ // reference to it.
+ // Observers that return STOP_OBSERVING will not receive any additional
+ // callbacks, and will be deleted after invocation of this method returns.
+ virtual ObservePolicy OnCommit(
+ content::NavigationHandle* navigation_handle) = 0;
+
+ // OnDidInternalNavigationAbort is triggered when the main frame navigation
+ // aborts with HTTP responses that don't commit, such as HTTP 204 responses
+ // and downloads. Note that |navigation_handle| will be destroyed
+ // soon after this call. Don't hold a reference to it.
+ virtual void OnDidInternalNavigationAbort(
+ content::NavigationHandle* navigation_handle) = 0;
+
+ // ReadyToCommitNextNavigation is triggered when a frame navigation is
+ // ready to commit, but has not yet been committed. This is only called by
+ // a PageLoadTracker for a committed load, meaning that this call signals we
+ // are ready to commit a navigation to a new page.
+ virtual void ReadyToCommitNextNavigation(
+ content::NavigationHandle* navigation_handle) = 0;
+
+ // OnDidFinishSubFrameNavigation is triggered when a sub-frame of the
+ // committed page has finished navigating. It has either committed, aborted,
+ // was a same document navigation, or has been replaced. It is up to the
+ // observer to query |navigation_handle| to determine which happened. Note
+ // that |navigation_handle| will be destroyed soon after this call. Don't
+ // hold a reference to it.
+ virtual void OnDidFinishSubFrameNavigation(
+ content::NavigationHandle* navigation_handle) = 0;
+
+ // OnCommitSameDocumentNavigation is triggered when a same-document navigation
+ // commits within the main frame of the current page. Note that
+ // |navigation_handle| will be destroyed soon after this call. Don't hold a
+ // reference to it.
+ virtual void OnCommitSameDocumentNavigation(
+ content::NavigationHandle* navigation_handle) = 0;
+
+ // OnHidden is triggered when a page leaves the foreground. It does not fire
+ // when a foreground page is permanently closed; for that, listen to
+ // OnComplete instead.
+ virtual ObservePolicy OnHidden(const mojom::PageLoadTiming& timing) = 0;
+
+ // OnShown is triggered when a page is brought to the foreground. It does not
+ // fire when the page first loads; for that, listen for OnStart instead.
+ virtual ObservePolicy OnShown() = 0;
+
+ // OnEnterBackForwardCache is triggered when a page is put into the
+ // back-forward cache. This page can be reused in the future for a
+ // back-forward navigation, in this case this OnRestoreFromBackForwardCache
+ // will be called for this PageLoadMetricsObserver. Note that the page in the
+ // back-forward cache can be evicted at any moment, and in this case
+ // OnComplete will be called.
+ //
+ // At the moment, the default implementtion of OnEnterBackForwardCache()
+ // invokes OnComplete and returns STOP_OBSERVING, so the page will not be
+ // tracked after it is stored in the back-forward cache and after it is
+ // restored. Return CONTINUE_OBSERVING explicitly to ensure that you cover the
+ // entire lifetime of the page, which is important for cases like tracking
+ // feature use counts or total network usage.
+ //
+ // TODO(hajimehoshi): Consider to remove |timing| argument by adding a
+ // function to PageLoadMetricsObserverDelegate. This would require
+ // investigation to determine exposing the timing from the delegate would be
+ // really safe.
+ virtual ObservePolicy OnEnterBackForwardCache(
+ const mojom::PageLoadTiming& timing) = 0;
+
+ // OnRestoreFromBackForwardCache is triggered when a page is restored from
+ // the back-forward cache.
+ virtual void OnRestoreFromBackForwardCache(
+ const mojom::PageLoadTiming& timing,
+ content::NavigationHandle* navigation_handle) = 0;
+
+ // Called before OnCommit. The observer should return whether it wishes to
+ // observe navigations whose main resource has MIME type |mine_type|. The
+ // default is to observe HTML and XHTML only. Note that PageLoadTrackers only
+ // track XHTML, HTML, and MHTML (related/multipart).
+ virtual ObservePolicy ShouldObserveMimeType(
+ const std::string& mime_type) const = 0;
+
+ // The callbacks below are only invoked after a navigation commits, for
+ // tracked page loads. Page loads that don't meet the criteria for being
+ // tracked at the time a navigation commits will not receive any of the
+ // callbacks below.
+
+ // OnTimingUpdate is triggered when an updated PageLoadTiming is available at
+ // the page (page is essentially main frame, with merged values across all
+ // frames for some paint timing values) or subframe level. This method may be
+ // called multiple times over the course of the page load. This method is
+ // currently only intended for use in testing. Most implementers should
+ // implement one of the On* callbacks, such as OnFirstContentfulPaint or
+ // OnDomContentLoadedEventStart. Please email loading-dev@chromium.org if you
+ // intend to override this method.
+ //
+ // If |subframe_rfh| is nullptr, the update took place in the main frame.
+ virtual void OnTimingUpdate(content::RenderFrameHost* subframe_rfh,
+ const mojom::PageLoadTiming& timing) = 0;
+
+ virtual void OnMobileFriendlinessUpdate(
+ const blink::MobileFriendliness& mobile_friendliness) = 0;
+
+ // OnInputTimingUpdate is triggered when an updated InputTiming is available
+ // at the subframe level. This method may be called multiple times over the
+ // course of the page load.
+ virtual void OnInputTimingUpdate(
+ content::RenderFrameHost* subframe_rfh,
+ const mojom::InputTiming& input_timing_delta) = 0;
+
+ // OnRenderDataUpdate is triggered when an updated PageRenderData is available
+ // at the subframe level. This method may be called multiple times over the
+ // course of the page load.
+ virtual void OnSubFrameRenderDataUpdate(
+ content::RenderFrameHost* subframe_rfh,
+ const mojom::FrameRenderDataUpdate& render_data) = 0;
+
+ // Triggered when an updated CpuTiming is available at the page or subframe
+ // level. This method is intended for monitoring cpu usage and load across
+ // the frames on a page during navigation.
+ virtual void OnCpuTimingUpdate(content::RenderFrameHost* subframe_rfh,
+ const mojom::CpuTiming& timing) = 0;
+
+ // OnUserInput is triggered when a new user input is passed in to
+ // web_contents.
+ virtual void OnUserInput(const blink::WebInputEvent& event,
+ const mojom::PageLoadTiming& timing) = 0;
+
+ // The following methods are invoked at most once, when the timing for the
+ // associated event first becomes available.
+ virtual void OnDomContentLoadedEventStart(
+ const mojom::PageLoadTiming& timing) = 0;
+ virtual void OnLoadEventStart(const mojom::PageLoadTiming& timing) = 0;
+ virtual void OnFirstLayout(const mojom::PageLoadTiming& timing) = 0;
+ virtual void OnParseStart(const mojom::PageLoadTiming& timing) = 0;
+ virtual void OnParseStop(const mojom::PageLoadTiming& timing) = 0;
+
+ // On*PaintInPage(...) are invoked when the first relevant paint in the
+ // page, across all frames, is observed.
+ virtual void OnFirstPaintInPage(const mojom::PageLoadTiming& timing) = 0;
+ virtual void OnFirstImagePaintInPage(const mojom::PageLoadTiming& timing) = 0;
+ virtual void OnFirstContentfulPaintInPage(
+ const mojom::PageLoadTiming& timing) = 0;
+
+ // These are called once every time when the page is restored from the
+ // back-forward cache. |index| indicates |index|-th restore.
+ virtual void OnFirstPaintAfterBackForwardCacheRestoreInPage(
+ const mojom::BackForwardCacheTiming& timing,
+ size_t index) = 0;
+ virtual void OnFirstInputAfterBackForwardCacheRestoreInPage(
+ const mojom::BackForwardCacheTiming& timing,
+ size_t index) = 0;
+
+ // This is called several times on requestAnimationFrame after the page is
+ // restored from the back-forward cache. The number of the calls is hard-
+ // coded as WebPerformance::
+ // kRequestAnimationFramesToRecordAfterBackForwardCacheRestore.
+ virtual void OnRequestAnimationFramesAfterBackForwardCacheRestoreInPage(
+ const mojom::BackForwardCacheTiming& timing,
+ size_t index) = 0;
+
+ // Unlike other paint callbacks, OnFirstMeaningfulPaintInMainFrameDocument is
+ // tracked per document, and is reported for the main frame document only.
+ virtual void OnFirstMeaningfulPaintInMainFrameDocument(
+ const mojom::PageLoadTiming& timing) = 0;
+
+ virtual void OnFirstInputInPage(const mojom::PageLoadTiming& timing) = 0;
+
+ // Invoked when there is an update to the loading behavior_flags in the given
+ // frame.
+ virtual void OnLoadingBehaviorObserved(content::RenderFrameHost* rfh,
+ int behavior_flags) = 0;
+
+ // Invoked when new use counter features are observed across all frames.
+ virtual void OnFeaturesUsageObserved(
+ content::RenderFrameHost* rfh,
+ const std::vector<blink::UseCounterFeature>& features) = 0;
+
+ // The smoothness metrics is shared over shared-memory. The observer should
+ // create a mapping (by calling |shared_memory.Map()|) so that they are able
+ // to read from the shared memory.
+ virtual void SetUpSharedMemoryForSmoothness(
+ const base::ReadOnlySharedMemoryRegion& shared_memory) = 0;
+
+ // Invoked when there is data use for loading a resource on the page
+ // for a given render frame host. This only contains resources that have had
+ // new data use since the last callback. Resources loaded from the cache only
+ // receive a single update. Multiple updates can be received for the same
+ // resource if it is loaded in multiple documents.
+ virtual void OnResourceDataUseObserved(
+ content::RenderFrameHost* rfh,
+ const std::vector<mojom::ResourceDataUpdatePtr>& resources) = 0;
+
+ // Invoked when a media element starts playing.
+ virtual void MediaStartedPlaying(
+ const content::WebContentsObserver::MediaPlayerInfo& video_type,
+ content::RenderFrameHost* render_frame_host) = 0;
+
+ // For the main frame, called when the main frame's dimensions have changed,
+ // e.g. resizing a tab causes the document width to change; loading additional
+ // content causes the document height to increase; explicitly changing the
+ // height of the body element.
+ //
+ // For a subframe, called when the intersection rect between the main frame
+ // and the subframe has changed, e.g. the subframe is initially added; the
+ // subframe's position is updated explicitly or inherently (e.g. sticky
+ // position while the page is being scrolled).
+ //
+ // TODO(crbug/1048175): Expose intersections to observers via shared delegate.
+ virtual void OnMainFrameIntersectionRectChanged(
+ content::RenderFrameHost* rfh,
+ const gfx::Rect& main_frame_intersection_rect) = 0;
+
+ // Called when the main frame's viewport rectangle (the viewport dimensions
+ // and the scroll position) changed, e.g. the user scrolled the main frame or
+ // the viewport dimensions themselves changed. Only invoked on the main frame.
+ virtual void OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) = 0;
+
+ // Invoked when the UMA metrics subsystem is persisting metrics as the
+ // application goes into the background, on platforms where the browser
+ // process may be killed after backgrounding (Android). Implementers should
+ // persist any metrics that have been buffered in memory in this callback, as
+ // the application may be killed at any time after this method is invoked
+ // without further notification. Note that this may be called both for
+ // provisional loads as well as committed loads. Implementations that only
+ // want to track committed loads should check GetDelegate().DidCommit()
+ // to determine if the load had committed. If the implementation returns
+ // CONTINUE_OBSERVING, this method may be called multiple times per observer,
+ // once for each time that the application enters the background.
+ //
+ // The default implementation does nothing, and returns CONTINUE_OBSERVING.
+ virtual ObservePolicy FlushMetricsOnAppEnterBackground(
+ const mojom::PageLoadTiming& timing) = 0;
+
+ // One of OnComplete or OnFailedProvisionalLoad is invoked for tracked page
+ // loads, immediately before the observer is deleted. These callbacks will not
+ // be invoked for page loads that did not meet the criteria for being tracked
+ // at the time the navigation completed. The PageLoadTiming struct contains
+ // timing data. Other useful data collected over the course of the page load
+ // is exposed by the observer delegate API. Most observers should not need
+ // to implement these callbacks, and should implement the On* timing callbacks
+ // instead.
+
+ // OnComplete is invoked for tracked page loads that committed, immediately
+ // before the observer is deleted. Observers that implement OnComplete may
+ // also want to implement FlushMetricsOnAppEnterBackground, to avoid loss of
+ // data if the application is killed while in the background (this happens
+ // frequently on Android).
+ virtual void OnComplete(const mojom::PageLoadTiming& timing) = 0;
+
+ // OnFailedProvisionalLoad is invoked for tracked page loads that did not
+ // commit, immediately before the observer is deleted.
+ virtual void OnFailedProvisionalLoad(
+ const FailedProvisionalLoadInfo& failed_provisional_load_info) = 0;
+
+ // Called whenever a request is loaded for this page load. This is restricted
+ // to requests with HTTP or HTTPS only schemes.
+ virtual void OnLoadedResource(
+ const ExtraRequestCompleteInfo& extra_request_complete_info) = 0;
+
+ virtual void FrameReceivedUserActivation(
+ content::RenderFrameHost* render_frame_host) = 0;
+
+ // Called when the display property changes on the frame.
+ virtual void FrameDisplayStateChanged(
+ content::RenderFrameHost* render_frame_host,
+ bool is_display_none) = 0;
+
+ // Called when a frames size changes.
+ virtual void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
+ const gfx::Size& frame_size) = 0;
+
+ virtual void OnRenderFrameDeleted(
+ content::RenderFrameHost* render_frame_host) = 0;
+ virtual void OnSubFrameDeleted(int frame_tree_node_id) = 0;
+
+ // Called when a cookie is read for a resource request or by document.cookie.
+ virtual void OnCookiesRead(const GURL& url,
+ const GURL& first_party_url,
+ const net::CookieList& cookie_list,
+ bool blocked_by_policy) = 0;
+
+ // Called when a cookie is set by a header or via document.cookie.
+ virtual void OnCookieChange(const GURL& url,
+ const GURL& first_party_url,
+ const net::CanonicalCookie& cookie,
+ bool blocked_by_policy) = 0;
+
+ // Called when a storage access attempt by the origin |url| to |storage_type|
+ // is checked by the content settings manager. |blocked_by_policy| is false
+ // when cookie access is not allowed for |url|.
+ virtual void OnStorageAccessed(const GURL& url,
+ const GURL& first_party_url,
+ bool blocked_by_policy,
+ StorageType access_type) = 0;
+
+ // Called when prefetch is likely to occur in this page load.
+ virtual void OnPrefetchLikely() = 0;
+
+ // Called when the page tracked was just activated after being loaded inside a
+ // portal.
+ virtual void DidActivatePortal(base::TimeTicks activation_time) = 0;
+
+ // Called when the page tracked was just activated after being prerendered.
+ // |navigation_handle| is for the activation navigation.
+ virtual void DidActivatePrerenderedPage(
+ content::NavigationHandle* navigation_handle) = 0;
+
+ // Called when V8 per-frame memory usage updates are available. Each
+ // MemoryUpdate consists of a GlobalRenderFrameHostId and a nonzero int64_t
+ // change in bytes used.
+ virtual void OnV8MemoryChanged(
+ const std::vector<MemoryUpdate>& memory_updates) = 0;
+
+ private:
+ base::WeakPtrFactory<PageLoadMetricsObserverInterface> weak_factory_{this};
+};
+
+} // namespace page_load_metrics
+
+#endif // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_OBSERVER_INTERFACE_H_
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc
index 9e864999fe3..cd98b01d6f6 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.cc
@@ -5,6 +5,7 @@
#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
#include "base/check_op.h"
+#include "base/i18n/number_formatting.h"
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
#include "components/page_load_metrics/common/page_load_metrics.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -27,17 +28,24 @@ bool IsSubset(const Set& set1, const Set& set2) {
// PageLoadMetricsObserver used by the PageLoadMetricsTestWaiter to observe
// metrics updates.
-class WaiterMetricsObserver : public PageLoadMetricsObserver {
+class WaiterMetricsObserver final : public PageLoadMetricsObserver {
public:
using FrameTreeNodeId = PageLoadMetricsObserver::FrameTreeNodeId;
// We use a WeakPtr to the PageLoadMetricsTestWaiter because |waiter| can be
// destroyed before this WaiterMetricsObserver.
explicit WaiterMetricsObserver(
- base::WeakPtr<PageLoadMetricsTestWaiter> waiter)
- : waiter_(waiter) {}
+ base::WeakPtr<PageLoadMetricsTestWaiter> waiter,
+ const char* observer_name)
+ : waiter_(waiter), observer_name_(observer_name) {}
~WaiterMetricsObserver() override = default;
+ const char* GetObserverName() const override;
+
+ ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override;
+
void OnTimingUpdate(content::RenderFrameHost* subframe_rfh,
const mojom::PageLoadTiming& timing) override;
@@ -61,20 +69,28 @@ class WaiterMetricsObserver : public PageLoadMetricsObserver {
content::NavigationHandle* navigation_handle) override;
void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
const gfx::Size& frame_size) override;
- void OnFrameIntersectionUpdate(
+ void OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const mojom::FrameIntersectionUpdate& frame_intersection_update) override;
-
+ const gfx::Rect& main_frame_intersection_rect) override;
+ void OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) override;
void OnV8MemoryChanged(
const std::vector<MemoryUpdate>& memory_updates) override;
private:
const base::WeakPtr<PageLoadMetricsTestWaiter> waiter_;
+ const char* observer_name_;
};
PageLoadMetricsTestWaiter::PageLoadMetricsTestWaiter(
content::WebContents* web_contents)
- : MetricsLifecycleObserver(web_contents) {}
+ : MetricsLifecycleObserver(web_contents),
+ observer_name_("WaiterMetricsObserver") {}
+
+PageLoadMetricsTestWaiter::PageLoadMetricsTestWaiter(
+ content::WebContents* web_contents,
+ const char* observer_name_)
+ : MetricsLifecycleObserver(web_contents), observer_name_(observer_name_) {}
PageLoadMetricsTestWaiter::~PageLoadMetricsTestWaiter() {
CHECK(did_add_observer_);
@@ -102,6 +118,11 @@ void PageLoadMetricsTestWaiter::SetMainFrameIntersectionExpectation() {
expected_.did_set_main_frame_intersection_ = true;
}
+void PageLoadMetricsTestWaiter::AddMainFrameViewportRectExpectation(
+ const gfx::Rect& rect) {
+ expected_.main_frame_viewport_rect_ = rect;
+}
+
void PageLoadMetricsTestWaiter::AddSubFrameExpectation(TimingField field) {
CHECK_NE(field, TimingField::kLoadTimingInfo)
<< "LOAD_TIMING_INFO should only be used as a page-level expectation";
@@ -267,15 +288,20 @@ void PageLoadMetricsTestWaiter::OnFeaturesUsageObserved(
run_loop_->Quit();
}
-void PageLoadMetricsTestWaiter::OnFrameIntersectionUpdate(
+void PageLoadMetricsTestWaiter::OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const page_load_metrics::mojom::FrameIntersectionUpdate&
- frame_intersection_update) {
- if (frame_intersection_update.main_frame_intersection_rect) {
- observed_.did_set_main_frame_intersection_ = true;
- observed_.main_frame_intersections_.push_back(
- *frame_intersection_update.main_frame_intersection_rect);
- }
+ const gfx::Rect& main_frame_intersection_rect) {
+ observed_.did_set_main_frame_intersection_ = true;
+ observed_.main_frame_intersections_.push_back(main_frame_intersection_rect);
+
+ if (ExpectationsSatisfied() && run_loop_)
+ run_loop_->Quit();
+}
+
+void PageLoadMetricsTestWaiter::OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) {
+ observed_.main_frame_viewport_rect_ = main_frame_viewport_rect;
+
if (ExpectationsSatisfied() && run_loop_)
run_loop_->Quit();
}
@@ -319,8 +345,15 @@ PageLoadMetricsTestWaiter::GetMatchedBits(
matched_bits.Set(TimingField::kFirstContentfulPaint);
if (timing.paint_timing->first_meaningful_paint)
matched_bits.Set(TimingField::kFirstMeaningfulPaint);
- if (timing.paint_timing->largest_contentful_paint->largest_image_paint ||
- timing.paint_timing->largest_contentful_paint->largest_text_paint) {
+ // The largest contentful paint's size can be nonzero while the time can be 0
+ // since a time of 0 is sent when the image is still painting. We set
+ // LargestContentfulPaint to be observed when its time is non-zero.
+ if ((timing.paint_timing->largest_contentful_paint->largest_image_paint &&
+ !timing.paint_timing->largest_contentful_paint->largest_image_paint
+ ->is_zero()) ||
+ (timing.paint_timing->largest_contentful_paint->largest_text_paint &&
+ !timing.paint_timing->largest_contentful_paint->largest_text_paint
+ ->is_zero())) {
matched_bits.Set(TimingField::kLargestContentfulPaint);
}
if (timing.paint_timing->first_input_or_scroll_notified_timestamp)
@@ -344,6 +377,8 @@ PageLoadMetricsTestWaiter::GetMatchedBits(
TimingField::kRequestAnimationFrameAfterBackForwardCacheRestore);
}
}
+ if (timing.interactive_timing->first_scroll_delay)
+ matched_bits.Set(TimingField::kFirstScrollDelay);
if (render_data) {
double layout_shift_score = render_data->layout_shift_score;
@@ -385,8 +420,8 @@ void PageLoadMetricsTestWaiter::OnActivate(
void PageLoadMetricsTestWaiter::AddObserver(
page_load_metrics::PageLoadTracker* tracker) {
ASSERT_FALSE(did_add_observer_);
- tracker->AddObserver(
- std::make_unique<WaiterMetricsObserver>(weak_factory_.GetWeakPtr()));
+ tracker->AddObserver(std::make_unique<WaiterMetricsObserver>(
+ weak_factory_.GetWeakPtr(), observer_name_));
did_add_observer_ = true;
}
@@ -448,6 +483,13 @@ bool PageLoadMetricsTestWaiter::MainFrameIntersectionExpectationsSatisfied()
return true;
}
+bool PageLoadMetricsTestWaiter::MainFrameViewportRectExpectationsSatisfied()
+ const {
+ return !expected_.main_frame_viewport_rect_ ||
+ observed_.main_frame_viewport_rect_ ==
+ expected_.main_frame_viewport_rect_;
+}
+
bool PageLoadMetricsTestWaiter::MemoryUpdateExpectationsSatisfied() const {
return IsSubset(expected_.memory_update_frame_ids_,
observed_.memory_update_frame_ids_);
@@ -464,6 +506,7 @@ bool PageLoadMetricsTestWaiter::ExpectationsSatisfied() const {
LoadingBehaviorExpectationsSatisfied() &&
CpuTimeExpectationsSatisfied() &&
MainFrameIntersectionExpectationsSatisfied() &&
+ MainFrameViewportRectExpectationsSatisfied() &&
MemoryUpdateExpectationsSatisfied();
}
@@ -475,6 +518,17 @@ void PageLoadMetricsTestWaiter::ResetExpectations() {
expected_minimum_aggregate_cpu_time_ = base::TimeDelta();
}
+const char* WaiterMetricsObserver::GetObserverName() const {
+ return observer_name_;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+WaiterMetricsObserver::OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) {
+ return FORWARD_OBSERVING;
+}
+
void WaiterMetricsObserver::OnTimingUpdate(
content::RenderFrameHost* subframe_rfh,
const page_load_metrics::mojom::PageLoadTiming& timing) {
@@ -517,12 +571,20 @@ void WaiterMetricsObserver::OnFeaturesUsageObserved(
waiter_->OnFeaturesUsageObserved(nullptr, features);
}
-void WaiterMetricsObserver::OnFrameIntersectionUpdate(
+void WaiterMetricsObserver::OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const page_load_metrics::mojom::FrameIntersectionUpdate&
- frame_intersection_update) {
- if (waiter_)
- waiter_->OnFrameIntersectionUpdate(rfh, frame_intersection_update);
+ const gfx::Rect& main_frame_intersection_rect) {
+ if (waiter_) {
+ waiter_->OnMainFrameIntersectionRectChanged(rfh,
+ main_frame_intersection_rect);
+ }
+}
+
+void WaiterMetricsObserver::OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) {
+ if (waiter_) {
+ waiter_->OnMainFrameViewportRectChanged(main_frame_viewport_rect);
+ }
}
void WaiterMetricsObserver::OnDidFinishSubFrameNavigation(
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h b/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h
index eff6d606176..3853bb2ff8b 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_test_waiter.h
@@ -40,11 +40,14 @@ class PageLoadMetricsTestWaiter : public MetricsLifecycleObserver {
kFirstInputDelayAfterBackForwardCacheRestore = 1 << 10,
kLayoutShift = 1 << 11,
kRequestAnimationFrameAfterBackForwardCacheRestore = 1 << 12,
+ kFirstScrollDelay = 1 << 13,
};
using FrameTreeNodeId =
page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
explicit PageLoadMetricsTestWaiter(content::WebContents* web_contents);
+ explicit PageLoadMetricsTestWaiter(content::WebContents* web_contents,
+ const char* observer_name_);
~PageLoadMetricsTestWaiter() override;
@@ -68,6 +71,11 @@ class PageLoadMetricsTestWaiter : public MetricsLifecycleObserver {
// TODO(skobes): Unify this API with AddMainFrameIntersectionExpectation.
void SetMainFrameIntersectionExpectation();
+ // Add a main frame viewport intersection expectation. Expects that the
+ // mainframe receives its viewport rectangle in the main frame document's
+ // coornidate. Subsequent calls overwrite unmet expectations.
+ void AddMainFrameViewportRectExpectation(const gfx::Rect& rect);
+
// Add a single WebFeature expectation.
void AddWebFeatureExpectation(blink::mojom::WebFeature web_feature);
@@ -127,6 +135,8 @@ class PageLoadMetricsTestWaiter : public MetricsLifecycleObserver {
virtual void ResetExpectations();
private:
+ const char* observer_name_;
+
// Manages a bitset of TimingFields.
class TimingFieldBitSet {
public:
@@ -211,10 +221,12 @@ class PageLoadMetricsTestWaiter : public MetricsLifecycleObserver {
void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
const gfx::Size& frame_size);
- void OnFrameIntersectionUpdate(
+ void OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const page_load_metrics::mojom::FrameIntersectionUpdate&
- frame_intersection_update);
+ const gfx::Rect& main_frame_intersection_rect);
+
+ void OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect);
void OnDidFinishSubFrameNavigation(
content::NavigationHandle* navigation_handle);
@@ -237,6 +249,7 @@ class PageLoadMetricsTestWaiter : public MetricsLifecycleObserver {
bool SubframeNavigationExpectationsSatisfied() const;
bool SubframeDataExpectationsSatisfied() const;
bool MainFrameIntersectionExpectationsSatisfied() const;
+ bool MainFrameViewportRectExpectationsSatisfied() const;
bool MemoryUpdateExpectationsSatisfied() const;
void AddObserver(page_load_metrics::PageLoadTracker* tracker);
@@ -258,6 +271,7 @@ class PageLoadMetricsTestWaiter : public MetricsLifecycleObserver {
std::set<gfx::Size, FrameSizeComparator> frame_sizes_;
bool did_set_main_frame_intersection_ = false;
std::vector<gfx::Rect> main_frame_intersections_;
+ absl::optional<gfx::Rect> main_frame_viewport_rect_;
std::unordered_set<content::GlobalRenderFrameHostId,
content::GlobalRenderFrameHostIdHasher>
memory_update_frame_ids_;
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc b/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc
index 45de538942f..5f5b3994d8d 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc
@@ -428,8 +428,6 @@ PageLoadMetricsUpdateDispatcher::PageLoadMetricsUpdateDispatcher(
page_input_timing_(mojom::InputTiming::New()),
mobile_friendliness_(blink::MobileFriendliness()),
is_prerendered_page_load_(navigation_handle->IsInPrerenderedMainFrame()) {
- page_input_timing_->max_event_durations =
- mojom::UserInteractionLatencies::New();
}
PageLoadMetricsUpdateDispatcher::~PageLoadMetricsUpdateDispatcher() {
@@ -607,12 +605,18 @@ void PageLoadMetricsUpdateDispatcher::UpdateFrameCpuTiming(
void PageLoadMetricsUpdateDispatcher::UpdateSubFrameMetadata(
content::RenderFrameHost* render_frame_host,
mojom::FrameMetadataPtr subframe_metadata) {
+ if (subframe_metadata->main_frame_viewport_rect) {
+ mojo::ReportBadMessage(
+ "Unexpected main_frame_viewport_rect set for a subframe.");
+ return;
+ }
+
// Merge the subframe loading behavior flags with any we've already observed,
// possibly from other subframes.
subframe_metadata_->behavior_flags |= subframe_metadata->behavior_flags;
client_->OnSubframeMetadataChanged(render_frame_host, *subframe_metadata);
- MaybeUpdateFrameIntersection(render_frame_host, subframe_metadata);
+ MaybeUpdateMainFrameIntersectionRect(render_frame_host, subframe_metadata);
}
void PageLoadMetricsUpdateDispatcher::UpdateMainFrameMobileFriendliness(
@@ -625,11 +629,11 @@ void PageLoadMetricsUpdateDispatcher::UpdateSubFrameMobileFriendliness(
client_->OnSubFrameMobileFriendlinessChanged(mobile_friendliness);
}
-void PageLoadMetricsUpdateDispatcher::MaybeUpdateFrameIntersection(
+void PageLoadMetricsUpdateDispatcher::MaybeUpdateMainFrameIntersectionRect(
content::RenderFrameHost* render_frame_host,
const mojom::FrameMetadataPtr& frame_metadata) {
// Handle intersection updates if included in the metadata.
- if (frame_metadata->intersection_update.is_null())
+ if (!frame_metadata->main_frame_intersection_rect)
return;
// Do not notify intersections for untracked loads,
@@ -645,18 +649,31 @@ void PageLoadMetricsUpdateDispatcher::MaybeUpdateFrameIntersection(
}
auto existing_intersection_it =
- frame_intersection_updates_.find(frame_tree_node_id);
-
- // Check if we already have a frame intersection update for the frame,
- // dispatch updates for the first frame intersection update or if
- // the intersection has changed.
- if (existing_intersection_it == frame_intersection_updates_.end() ||
- !existing_intersection_it->second.Equals(
- *frame_metadata->intersection_update)) {
- frame_intersection_updates_[frame_tree_node_id] =
- *frame_metadata->intersection_update;
- client_->OnFrameIntersectionUpdate(render_frame_host,
- *frame_metadata->intersection_update);
+ main_frame_intersection_rects_.find(frame_tree_node_id);
+
+ // Check if we already have a frame intersection rect for the frame, dispatch
+ // updates for the first frame intersection rect or if the intersection has
+ // changed.
+ if (existing_intersection_it == main_frame_intersection_rects_.end() ||
+ existing_intersection_it->second !=
+ *frame_metadata->main_frame_intersection_rect) {
+ main_frame_intersection_rects_[frame_tree_node_id] =
+ *frame_metadata->main_frame_intersection_rect;
+ client_->OnMainFrameIntersectionRectChanged(
+ render_frame_host, *frame_metadata->main_frame_intersection_rect);
+ }
+}
+
+void PageLoadMetricsUpdateDispatcher::MaybeUpdateMainFrameViewportRect(
+ const mojom::FrameMetadataPtr& frame_metadata) {
+ // Handle viewport updates if included in the metadata.
+ if (!frame_metadata->main_frame_viewport_rect)
+ return;
+
+ if (!main_frame_viewport_rect_ ||
+ *frame_metadata->main_frame_viewport_rect != *main_frame_viewport_rect_) {
+ main_frame_viewport_rect_ = *frame_metadata->main_frame_viewport_rect;
+ client_->OnMainFrameViewportRectChanged(*main_frame_viewport_rect_);
}
}
@@ -720,8 +737,11 @@ void PageLoadMetricsUpdateDispatcher::UpdateMainFrameMetadata(
main_frame_metadata_ = std::move(new_metadata);
client_->OnMainFrameMetadataChanged();
- if (!main_frame_metadata_.is_null())
- MaybeUpdateFrameIntersection(render_frame_host, main_frame_metadata_);
+ if (!main_frame_metadata_.is_null()) {
+ MaybeUpdateMainFrameIntersectionRect(render_frame_host,
+ main_frame_metadata_);
+ MaybeUpdateMainFrameViewportRect(main_frame_metadata_);
+ }
}
void PageLoadMetricsUpdateDispatcher::UpdatePageInputTiming(
diff --git a/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h b/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h
index a5dd8b3217a..6410d785941 100644
--- a/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h
+++ b/chromium/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.h
@@ -137,9 +137,11 @@ class PageLoadMetricsUpdateDispatcher {
const std::vector<mojom::ResourceDataUpdatePtr>& resources) = 0;
virtual void UpdateFrameCpuTiming(content::RenderFrameHost* rfh,
const mojom::CpuTiming& timing) = 0;
- virtual void OnFrameIntersectionUpdate(
+ virtual void OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const mojom::FrameIntersectionUpdate& frame_intersection_update) = 0;
+ const gfx::Rect& main_frame_intersection_rect) = 0;
+ virtual void OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) = 0;
virtual void SetUpSharedMemoryForSmoothness(
base::ReadOnlySharedMemoryRegion shared_memory) = 0;
};
@@ -251,9 +253,11 @@ class PageLoadMetricsUpdateDispatcher {
const blink::MobileFriendliness& mobile_friendliness);
void UpdatePageInputTiming(const mojom::InputTiming& input_timing_delta);
- void MaybeUpdateFrameIntersection(
+ void MaybeUpdateMainFrameIntersectionRect(
content::RenderFrameHost* render_frame_host,
const mojom::FrameMetadataPtr& frame_metadata);
+ void MaybeUpdateMainFrameViewportRect(
+ const mojom::FrameMetadataPtr& frame_metadata);
void UpdatePageRenderData(const mojom::FrameRenderDataUpdate& render_data);
void UpdateMainFrameRenderData(
@@ -320,10 +324,13 @@ class PageLoadMetricsUpdateDispatcher {
PageRenderData page_render_data_;
PageRenderData main_frame_render_data_;
- // The last main frame intersection dispatched to page load metrics
+ // The last main frame intersection rects dispatched to page load metrics
+ // observers.
+ std::map<FrameTreeNodeId, gfx::Rect> main_frame_intersection_rects_;
+
+ // The last main frame viewport rect dispatched to page load metrics
// observers.
- std::map<FrameTreeNodeId, mojom::FrameIntersectionUpdate>
- frame_intersection_updates_;
+ absl::optional<gfx::Rect> main_frame_viewport_rect_;
LayoutShiftNormalization layout_shift_normalization_;
// Layout shift normalization data for bfcache which needs to be reset each
diff --git a/chromium/components/page_load_metrics/browser/page_load_tracker.cc b/chromium/components/page_load_metrics/browser/page_load_tracker.cc
index 49068a948b2..028162cef48 100644
--- a/chromium/components/page_load_metrics/browser/page_load_tracker.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_tracker.cc
@@ -30,50 +30,6 @@
#include "content/public/browser/web_contents_observer.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
-// This macro invokes the specified method on each observer, passing the
-// variable length arguments as the method's arguments, and removes the observer
-// from the list of observers if the given method returns STOP_OBSERVING.
-// TODO(https://crbug.com/1301880): Convert this macro to a templace method that
-// takes a closure to execute its own callback.
-#define INVOKE_AND_PRUNE_OBSERVERS(Method, ...) \
- { \
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), \
- "PageLoadMetricsObserver::" #Method); \
- std::vector<std::unique_ptr<PageLoadMetricsObserver>> forward_observers; \
- for (auto it = observers_.begin(); it != observers_.end();) { \
- switch ((*it)->Method(__VA_ARGS__)) { \
- case PageLoadMetricsObserver::CONTINUE_OBSERVING: \
- ++it; \
- break; \
- case PageLoadMetricsObserver::STOP_OBSERVING: \
- if ((*it)->GetObserverName()) \
- observers_map_.erase((*it)->GetObserverName()); \
- it = observers_.erase(it); \
- break; \
- case PageLoadMetricsObserver::FORWARD_OBSERVING: \
- DCHECK((*it)->GetObserverName()) \
- << "GetObserverName should be implemented"; \
- auto target_observer = \
- parent_tracker_ \
- ? parent_tracker_->FindObserver((*it)->GetObserverName()) \
- : nullptr; \
- if (target_observer) { \
- forward_observers.emplace_back( \
- std::make_unique<PageLoadMetricsForwardObserver>( \
- target_observer)); \
- } \
- observers_map_.erase((*it)->GetObserverName()); \
- it = observers_.erase(it); \
- break; \
- } \
- } \
- for (auto& observer : forward_observers) { \
- DCHECK(observers_map_.find(observer->GetObserverName()) == \
- observers_map_.end()); \
- AddObserver(std::move(observer)); \
- } \
- }
-
namespace page_load_metrics {
namespace internal {
@@ -137,7 +93,7 @@ void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) {
}
void DispatchEventsAfterBackForwardCacheRestore(
- PageLoadMetricsObserver* observer,
+ PageLoadMetricsObserverInterface* observer,
const std::vector<mojo::StructPtr<mojom::BackForwardCacheTiming>>&
last_timings,
const std::vector<mojo::StructPtr<mojom::BackForwardCacheTiming>>&
@@ -186,7 +142,7 @@ void DispatchEventsAfterBackForwardCacheRestore(
}
}
-void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer,
+void DispatchObserverTimingCallbacks(PageLoadMetricsObserverInterface* observer,
const mojom::PageLoadTiming& last_timing,
const mojom::PageLoadTiming& new_timing) {
if (!last_timing.Equals(new_timing))
@@ -265,20 +221,51 @@ PageLoadTracker::PageLoadTracker(
embedder_interface_->RegisterObservers(this);
if (navigation_handle->IsInPrerenderedMainFrame()) {
DCHECK(!started_in_foreground_);
- INVOKE_AND_PRUNE_OBSERVERS(OnPrerenderStart, navigation_handle,
- currently_committed_url);
+ DCHECK_EQ(ukm::kInvalidSourceId, source_id_);
+ InvokeAndPruneObservers("PageLoadMetricsObserver::OnPrerenderStart",
+ base::BindRepeating(
+ [](content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url,
+ PageLoadMetricsObserverInterface* observer) {
+ return observer->OnPrerenderStart(
+ navigation_handle,
+ currently_committed_url);
+ },
+ navigation_handle, currently_committed_url),
+ /*permit_forwarding=*/false);
base::UmaHistogramEnumeration(
internal::kPageLoadPrerender2Event,
internal::PageLoadPrerenderEvent::kNavigationInPrerenderedMainFrame);
RecordPageType(internal::PageLoadTrackerPageType::kPrerenderPage);
} else if (navigation_handle->GetNavigatingFrameType() ==
content::FrameType::kFencedFrameRoot) {
- INVOKE_AND_PRUNE_OBSERVERS(OnFencedFramesStart, navigation_handle,
- currently_committed_url);
+ DCHECK_NE(ukm::kInvalidSourceId, source_id_);
+ InvokeAndPruneObservers("PageLoadMetricsObserver::OnFencedFramesStart",
+ base::BindRepeating(
+ [](content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url,
+ PageLoadMetricsObserverInterface* observer) {
+ return observer->OnFencedFramesStart(
+ navigation_handle,
+ currently_committed_url);
+ },
+ navigation_handle, currently_committed_url),
+ /*permit_forwarding=*/true);
RecordPageType(internal::PageLoadTrackerPageType::kFencedFramesPage);
} else {
- INVOKE_AND_PRUNE_OBSERVERS(OnStart, navigation_handle,
- currently_committed_url, started_in_foreground_);
+ DCHECK_NE(ukm::kInvalidSourceId, source_id_);
+ InvokeAndPruneObservers(
+ "PageLoadMetricsObserver::OnStart",
+ base::BindRepeating(
+ [](content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url, bool started_in_foreground,
+ PageLoadMetricsObserverInterface* observer) {
+ return observer->OnStart(navigation_handle,
+ currently_committed_url,
+ started_in_foreground);
+ },
+ navigation_handle, currently_committed_url, started_in_foreground_),
+ /*permit_forwarding=*/false);
RecordPageType(internal::PageLoadTrackerPageType::kPrimaryPage);
}
@@ -351,7 +338,14 @@ void PageLoadTracker::PageHidden() {
}
}
visibility_tracker_.OnHidden();
- INVOKE_AND_PRUNE_OBSERVERS(OnHidden, metrics_update_dispatcher_.timing());
+ InvokeAndPruneObservers("PageLoadMetricsObserver::OnHidden",
+ base::BindRepeating(
+ [](const mojom::PageLoadTiming* timing,
+ PageLoadMetricsObserverInterface* observer) {
+ return observer->OnHidden(*timing);
+ },
+ &metrics_update_dispatcher_.timing()),
+ /*permit_forwarding=*/false);
}
void PageLoadTracker::PageShown() {
@@ -369,7 +363,12 @@ void PageLoadTracker::PageShown() {
}
visibility_tracker_.OnShown();
- INVOKE_AND_PRUNE_OBSERVERS(OnShown);
+ InvokeAndPruneObservers(
+ "PageLoadMetricsObserver::OnShown",
+ base::BindRepeating([](PageLoadMetricsObserverInterface* observer) {
+ return observer->OnShown();
+ }),
+ /*permit_forwarding=*/false);
}
void PageLoadTracker::SubFrameDeleted(int frame_tree_node_id) {
@@ -407,6 +406,12 @@ void PageLoadTracker::Commit(content::NavigationHandle* navigation_handle) {
// Notify the parent of the inner main frame navigation as a sub-frame
// navigation.
parent_tracker_->DidFinishSubFrameNavigation(navigation_handle);
+ } else if (navigation_handle->IsPrerenderedPageActivation()) {
+ // We don't deliver OnCommit() for activation. Prerendered pages will see
+ // DidActivatePrerenderedPage() instead.
+ // Event records below are also not needed as we did them for the initial
+ // navigation on starting prerendering.
+ return;
}
did_commit_ = true;
@@ -420,11 +425,23 @@ void PageLoadTracker::Commit(content::NavigationHandle* navigation_handle) {
experimental_largest_contentful_paint_handler_.RecordMainFrameTreeNodeId(
navigation_handle->GetFrameTreeNodeId());
}
-
- const std::string& mime_type =
- navigation_handle->GetWebContents()->GetContentsMimeType();
- INVOKE_AND_PRUNE_OBSERVERS(ShouldObserveMimeType, mime_type);
- INVOKE_AND_PRUNE_OBSERVERS(OnCommit, navigation_handle);
+ InvokeAndPruneObservers(
+ "PageLoadMetricsObserver::ShouldObserveMimeType",
+ base::BindRepeating(
+ [](const std::string& mime_type,
+ PageLoadMetricsObserverInterface* observer) {
+ return observer->ShouldObserveMimeType(mime_type);
+ },
+ navigation_handle->GetWebContents()->GetContentsMimeType()),
+ /*permit_forwarding=*/false);
+ InvokeAndPruneObservers("PageLoadMetricsObserver::OnCommit",
+ base::BindRepeating(
+ [](content::NavigationHandle* navigation_handle,
+ PageLoadMetricsObserverInterface* observer) {
+ return observer->OnCommit(navigation_handle);
+ },
+ navigation_handle),
+ /*permit_forwarding=*/false);
}
void PageLoadTracker::DidActivatePrerenderedPage(
@@ -514,7 +531,14 @@ void PageLoadTracker::FailedProvisionalLoad(
void PageLoadTracker::Redirect(content::NavigationHandle* navigation_handle) {
url_ = navigation_handle->GetURL();
- INVOKE_AND_PRUNE_OBSERVERS(OnRedirect, navigation_handle);
+ InvokeAndPruneObservers("PageLoadMetricsObserver::Redirect",
+ base::BindRepeating(
+ [](content::NavigationHandle* navigation_handle,
+ PageLoadMetricsObserverInterface* observer) {
+ return observer->OnRedirect(navigation_handle);
+ },
+ navigation_handle),
+ /*permit_forwarding=*/false);
}
void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) {
@@ -531,8 +555,15 @@ void PageLoadTracker::FlushMetricsOnAppEnterBackground() {
app_entered_background_ = true;
}
- INVOKE_AND_PRUNE_OBSERVERS(FlushMetricsOnAppEnterBackground,
- metrics_update_dispatcher_.timing());
+ InvokeAndPruneObservers(
+ "PageLoadMetricsObserver::FlushMetricsOnAppEnterBackground",
+ base::BindRepeating(
+ [](const mojom::PageLoadTiming* timing,
+ PageLoadMetricsObserverInterface* observer) {
+ return observer->FlushMetricsOnAppEnterBackground(*timing);
+ },
+ &metrics_update_dispatcher_.timing()),
+ /*permit_forwarding=*/false);
}
void PageLoadTracker::OnLoadedResource(
@@ -603,15 +634,24 @@ void PageLoadTracker::StopTracking() {
void PageLoadTracker::AddObserver(
std::unique_ptr<PageLoadMetricsObserver> observer) {
observer->SetDelegate(this);
+ AddObserverInterface(std::move(observer));
+}
+
+void PageLoadTracker::AddObserverInterface(
+ std::unique_ptr<PageLoadMetricsObserverInterface> observer) {
if (observer->GetObserverName()) {
DCHECK(observers_map_.find(observer->GetObserverName()) ==
- observers_map_.end());
+ observers_map_.end())
+ << "We expect that observer's class and name is unique in trackers. "
+ "Note that observer's class can be non-unique in test, e.g. "
+ "PageLoadMetricsTestWaiter. In that case, use a unique name in "
+ "the test. See also constructor of PageLoadMetricsTestWaiter.";
observers_map_.emplace(observer->GetObserverName(), observer.get());
}
observers_.push_back(std::move(observer));
}
-base::WeakPtr<PageLoadMetricsObserver> PageLoadTracker::FindObserver(
+base::WeakPtr<PageLoadMetricsObserverInterface> PageLoadTracker::FindObserver(
const char* name) {
auto it = observers_map_.find(name);
if (it != observers_map_.end())
@@ -876,11 +916,19 @@ void PageLoadTracker::UpdateFrameCpuTiming(content::RenderFrameHost* rfh,
}
}
-void PageLoadTracker::OnFrameIntersectionUpdate(
+void PageLoadTracker::OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const mojom::FrameIntersectionUpdate& frame_intersection_update) {
+ const gfx::Rect& main_frame_intersection_rect) {
+ for (const auto& observer : observers_) {
+ observer->OnMainFrameIntersectionRectChanged(rfh,
+ main_frame_intersection_rect);
+ }
+}
+
+void PageLoadTracker::OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) {
for (const auto& observer : observers_) {
- observer->OnFrameIntersectionUpdate(rfh, frame_intersection_update);
+ observer->OnMainFrameViewportRectChanged(main_frame_viewport_rect);
}
}
@@ -1019,6 +1067,9 @@ PageLoadTracker::GetExperimentalLargestContentfulPaintHandler() const {
}
ukm::SourceId PageLoadTracker::GetPageUkmSourceId() const {
+ DCHECK_NE(ukm::kInvalidSourceId, source_id_)
+ << "GetPageUkmSourceId was called on a prerendered page before its "
+ "activation. We should not collect UKM while prerendering pages.";
return source_id_;
}
@@ -1031,8 +1082,15 @@ void PageLoadTracker::OnEnterBackForwardCache() {
// PageLoadMetricsUpdateDispatcher before the page is hidden to enable
// recording metrics that requires the page to be in foreground before
// entering BackForwardCache on navigation.
- INVOKE_AND_PRUNE_OBSERVERS(OnEnterBackForwardCache,
- metrics_update_dispatcher_.timing());
+ InvokeAndPruneObservers(
+ "PageLoadMetricsObserver::OnEnterBackForwardCache",
+ base::BindRepeating(
+ [](const mojom::PageLoadTiming* timing,
+ PageLoadMetricsObserverInterface* observer) {
+ return observer->OnEnterBackForwardCache(*timing);
+ },
+ &metrics_update_dispatcher_.timing()),
+ /*permit_forwarding=*/false);
metrics_update_dispatcher_.UpdateLayoutShiftNormalizationForBfcache();
metrics_update_dispatcher_
.UpdateResponsivenessMetricsNormalizationForBfcache();
@@ -1103,4 +1161,47 @@ base::WeakPtr<PageLoadTracker> PageLoadTracker::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
+void PageLoadTracker::InvokeAndPruneObservers(
+ const char* trace_name,
+ PageLoadTracker::InvokeCallback callback,
+ bool permit_forwarding) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), trace_name);
+ std::vector<std::unique_ptr<PageLoadMetricsObserverInterface>>
+ forward_observers;
+ for (auto it = observers_.begin(); it != observers_.end();) {
+ auto policy = callback.Run(it->get());
+ switch (policy) {
+ case PageLoadMetricsObserver::CONTINUE_OBSERVING:
+ ++it;
+ break;
+ case PageLoadMetricsObserver::STOP_OBSERVING:
+ if ((*it)->GetObserverName())
+ observers_map_.erase((*it)->GetObserverName());
+ it = observers_.erase(it);
+ break;
+ case PageLoadMetricsObserver::FORWARD_OBSERVING:
+ DCHECK(permit_forwarding);
+ DCHECK((*it)->GetObserverName())
+ << "GetObserverName should be implemented";
+ auto target_observer =
+ parent_tracker_
+ ? parent_tracker_->FindObserver((*it)->GetObserverName())
+ : nullptr;
+ if (target_observer) {
+ forward_observers.emplace_back(
+ std::make_unique<PageLoadMetricsForwardObserver>(
+ target_observer));
+ }
+ observers_map_.erase((*it)->GetObserverName());
+ it = observers_.erase(it);
+ break;
+ }
+ }
+ for (auto& observer : forward_observers) {
+ DCHECK(observers_map_.find(observer->GetObserverName()) ==
+ observers_map_.end());
+ AddObserverInterface(std::move(observer));
+ }
+}
+
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/page_load_tracker.h b/chromium/components/page_load_metrics/browser/page_load_tracker.h
index 162fcb2ba0f..feb81268981 100644
--- a/chromium/components/page_load_metrics/browser/page_load_tracker.h
+++ b/chromium/components/page_load_metrics/browser/page_load_tracker.h
@@ -223,9 +223,11 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
const std::vector<mojom::ResourceDataUpdatePtr>& resources) override;
void UpdateFrameCpuTiming(content::RenderFrameHost* rfh,
const mojom::CpuTiming& timing) override;
- void OnFrameIntersectionUpdate(
+ void OnMainFrameIntersectionRectChanged(
content::RenderFrameHost* rfh,
- const mojom::FrameIntersectionUpdate& frame_intersection_update) override;
+ const gfx::Rect& main_frame_intersection_rect) override;
+ void OnMainFrameViewportRectChanged(
+ const gfx::Rect& main_frame_viewport_rect) override;
void SetUpSharedMemoryForSmoothness(
base::ReadOnlySharedMemoryRegion shared_memory) override;
@@ -330,7 +332,8 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
base::TimeTicks page_end_time() const { return page_end_time_; }
void AddObserver(std::unique_ptr<PageLoadMetricsObserver> observer);
- base::WeakPtr<PageLoadMetricsObserver> FindObserver(char const* name);
+ base::WeakPtr<PageLoadMetricsObserverInterface> FindObserver(
+ char const* name);
// If the user performs some abort-like action while we are tracking this page
// load, notify the tracker. Note that we may not classify this as an abort if
@@ -414,6 +417,9 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
// tracker list after the provisional load is committed.
void SetPageMainFrame(content::RenderFrameHost* rfh);
+ // Gets a bound ukm::SourceId without any check for testing.
+ ukm::SourceId GetPageUkmSourceIdForTesting() const { return source_id_; }
+
// Obtains a weak pointer for this instance.
base::WeakPtr<PageLoadTracker> GetWeakPtr();
@@ -437,6 +443,16 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
absl::optional<base::TimeDelta> DurationSinceNavigationStartForTime(
const absl::optional<base::TimeTicks>& time) const;
+ using InvokeCallback =
+ base::RepeatingCallback<PageLoadMetricsObserver::ObservePolicy(
+ PageLoadMetricsObserverInterface*)>;
+ void InvokeAndPruneObservers(const char* trace_name,
+ InvokeCallback callback,
+ bool permit_forwarding);
+
+ void AddObserverInterface(
+ std::unique_ptr<PageLoadMetricsObserverInterface> observer);
+
// Whether we stopped tracking this navigation after it was initiated. We may
// stop tracking a navigation if it doesn't meet the criteria for tracking
// metrics in DidFinishNavigation.
@@ -502,12 +518,13 @@ class PageLoadTracker : public PageLoadMetricsUpdateDispatcher::Client,
// Interface to chrome features. Must outlive the class.
const raw_ptr<PageLoadMetricsEmbedderInterface> embedder_interface_;
- // Holds active PageLoadMetricsObserver instances bound to the tracking page.
- std::vector<std::unique_ptr<PageLoadMetricsObserver>> observers_;
+ // Holds active PageLoadMetricsObserverInterface inheritances' instances bound
+ // to the tracking page.
+ std::vector<std::unique_ptr<PageLoadMetricsObserverInterface>> observers_;
// Observer's name pointer to instance map. Can be raw_ptr as the instance is
// owned `observers` above, and is removed from the map on destruction.
- base::flat_map<const char*, base::raw_ptr<PageLoadMetricsObserver>>
+ base::flat_map<const char*, base::raw_ptr<PageLoadMetricsObserverInterface>>
observers_map_;
PageLoadMetricsUpdateDispatcher metrics_update_dispatcher_;
diff --git a/chromium/components/page_load_metrics/browser/page_load_tracker_unittest.cc b/chromium/components/page_load_metrics/browser/page_load_tracker_unittest.cc
index 6c31ee1d5ba..0a3f8a47a67 100644
--- a/chromium/components/page_load_metrics/browser/page_load_tracker_unittest.cc
+++ b/chromium/components/page_load_metrics/browser/page_load_tracker_unittest.cc
@@ -5,13 +5,11 @@
#include "components/page_load_metrics/browser/page_load_tracker.h"
#include "base/containers/flat_map.h"
-#include "base/test/scoped_feature_list.h"
#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
-#include "third_party/blink/public/common/features.h"
namespace page_load_metrics {
@@ -26,6 +24,7 @@ struct PageLoadMetricsObserverEvents final {
bool was_prerender_started = false;
bool was_committed = false;
bool was_sub_frame_deleted = false;
+ bool was_prerendered_page_activated = false;
size_t sub_frame_navigation_count = 0;
};
@@ -40,27 +39,32 @@ class TestPageLoadMetricsObserver final : public PageLoadMetricsObserver {
private:
void ReadyToCommitNextNavigation(
content::NavigationHandle* navigation_handle) override {
+ EXPECT_FALSE(events_->was_ready_to_commit_next_navigation);
events_->was_ready_to_commit_next_navigation = true;
}
ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url,
bool started_in_foreground) override {
+ EXPECT_FALSE(events_->was_started);
events_->was_started = true;
return CONTINUE_OBSERVING;
}
ObservePolicy OnFencedFramesStart(
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url) override {
+ EXPECT_FALSE(events_->was_fenced_frames_started);
events_->was_fenced_frames_started = true;
return stop_on_fenced_frames_ ? STOP_OBSERVING : CONTINUE_OBSERVING;
}
ObservePolicy OnPrerenderStart(content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url) override {
+ EXPECT_FALSE(events_->was_prerender_started);
events_->was_prerender_started = true;
return stop_on_prerender_ ? STOP_OBSERVING : CONTINUE_OBSERVING;
}
ObservePolicy OnCommit(
content::NavigationHandle* navigation_handle) override {
+ EXPECT_FALSE(events_->was_committed);
events_->was_committed = true;
return CONTINUE_OBSERVING;
}
@@ -71,6 +75,13 @@ class TestPageLoadMetricsObserver final : public PageLoadMetricsObserver {
void OnSubFrameDeleted(int frame_tree_node_id) override {
events_->was_sub_frame_deleted = true;
}
+ void DidActivatePrerenderedPage(
+ content::NavigationHandle* navigation_handle) override {
+ EXPECT_FALSE(events_->was_prerendered_page_activated);
+ events_->was_prerendered_page_activated = true;
+
+ EXPECT_NE(ukm::kInvalidSourceId, GetDelegate().GetPageUkmSourceId());
+ }
bool stop_on_prerender_ = false;
bool stop_on_fenced_frames_ = false;
@@ -82,17 +93,8 @@ class TestPageLoadMetricsObserver final : public PageLoadMetricsObserver {
class PageLoadTrackerTest : public PageLoadMetricsObserverContentTestHarness {
public:
- PageLoadTrackerTest() : observer_(new TestPageLoadMetricsObserver(&events_)) {
- scoped_feature_list_.InitWithFeaturesAndParameters(
- {
- {blink::features::kPrerender2, {}},
- {blink::features::kPrerender2MemoryControls, {}},
- {blink::features::kFencedFrames,
- {{"implementation_type", "mparch"}}},
- {blink::features::kInitialNavigationEntry, {}},
- },
- {});
- }
+ PageLoadTrackerTest()
+ : observer_(new TestPageLoadMetricsObserver(&events_)) {}
protected:
void SetTargetUrl(const std::string& url) { target_url_ = GURL(url); }
@@ -109,12 +111,12 @@ class PageLoadTrackerTest : public PageLoadMetricsObserverContentTestHarness {
private:
void RegisterObservers(PageLoadTracker* tracker) override {
ukm_source_ids_.emplace(tracker->GetUrl().spec(),
- tracker->GetPageUkmSourceId());
+ tracker->GetPageUkmSourceIdForTesting());
if (tracker->GetUrl() != target_url_)
return;
- DCHECK(!is_observer_passed_);
+ EXPECT_FALSE(is_observer_passed_);
tracker->AddObserver(std::unique_ptr<PageLoadMetricsObserver>(observer_));
is_observer_passed_ = true;
}
@@ -125,7 +127,6 @@ class PageLoadTrackerTest : public PageLoadMetricsObserverContentTestHarness {
raw_ptr<TestPageLoadMetricsObserver> observer_;
bool is_observer_passed_ = false;
- base::test::ScopedFeatureList scoped_feature_list_;
GURL target_url_;
};
@@ -152,6 +153,9 @@ TEST_F(PageLoadTrackerTest, PrimaryPageType) {
// Check observer behaviors.
EXPECT_TRUE(GetEvents().was_ready_to_commit_next_navigation);
+
+ // Check ukm::SourceId.
+ EXPECT_NE(ukm::kInvalidSourceId, GetObservedUkmSourceIdFor(kTestUrl));
}
TEST_F(PageLoadTrackerTest, EventForwarding) {
@@ -164,11 +168,11 @@ TEST_F(PageLoadTrackerTest, EventForwarding) {
// Add a fenced frame.
content::RenderFrameHost* fenced_frame_root =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendFencedFrame();
{
const char kFencedFramesUrl[] = "https://a.test/fenced_frames";
- auto simulator = content::NavigationSimulator::CreateForFencedFrame(
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL(kFencedFramesUrl), fenced_frame_root);
ASSERT_NE(nullptr, simulator);
simulator->Commit();
@@ -187,7 +191,7 @@ TEST_F(PageLoadTrackerTest, EventForwarding) {
// Navigate out.
{
const char kFencedFramesNavigationUrl[] = "https://b.test/fenced_frames";
- auto simulator = content::NavigationSimulator::CreateForFencedFrame(
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL(kFencedFramesNavigationUrl), fenced_frame_root);
ASSERT_NE(nullptr, simulator);
simulator->Commit();
@@ -201,13 +205,7 @@ TEST_F(PageLoadTrackerTest, EventForwarding) {
EXPECT_EQ(2u, GetEvents().sub_frame_navigation_count);
}
-// TODO(https://crbug.com/1312096): Enable the test on Android.
-#if BUILDFLAG(IS_ANDROID)
-#define MAYBE_PrerenderPageType DISABLED_PrerenderPageType
-#else
-#define MAYBE_PrerenderPageType PrerenderPageType
-#endif
-TEST_F(PageLoadTrackerTest, MAYBE_PrerenderPageType) {
+TEST_F(PageLoadTrackerTest, PrerenderPageType) {
// Target URL to monitor the tracker via the test observer.
const char kPrerenderingUrl[] = "https://a.test/prerender";
SetTargetUrl(kPrerenderingUrl);
@@ -232,8 +230,10 @@ TEST_F(PageLoadTrackerTest, MAYBE_PrerenderPageType) {
tester()->histogram_tester().ExpectBucketCount(
internal::kPageLoadTrackerPageType,
internal::PageLoadTrackerPageType::kPrerenderPage, 1);
- EXPECT_NE(GetObservedUkmSourceIdFor(kTestUrl),
- GetObservedUkmSourceIdFor(kPrerenderingUrl));
+
+ // Check ukm::SourceId.
+ EXPECT_NE(ukm::kInvalidSourceId, GetObservedUkmSourceIdFor(kTestUrl));
+ EXPECT_EQ(ukm::kInvalidSourceId, GetObservedUkmSourceIdFor(kPrerenderingUrl));
}
TEST_F(PageLoadTrackerTest, FencedFramesPageType) {
@@ -246,10 +246,10 @@ TEST_F(PageLoadTrackerTest, FencedFramesPageType) {
// Add a fenced frame.
content::RenderFrameHost* fenced_frame_root =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendFencedFrame();
{
- auto simulator = content::NavigationSimulator::CreateForFencedFrame(
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL(kFencedFramesUrl), fenced_frame_root);
ASSERT_NE(nullptr, simulator);
simulator->Commit();
@@ -269,12 +269,15 @@ TEST_F(PageLoadTrackerTest, FencedFramesPageType) {
tester()->histogram_tester().ExpectBucketCount(
internal::kPageLoadTrackerPageType,
internal::PageLoadTrackerPageType::kFencedFramesPage, 1);
+
+ // Check ukm::SourceId.
+ EXPECT_NE(ukm::kInvalidSourceId, GetObservedUkmSourceIdFor(kTestUrl));
EXPECT_EQ(GetObservedUkmSourceIdFor(kTestUrl),
GetObservedUkmSourceIdFor(kFencedFramesUrl));
// Navigate out.
{
- auto simulator = content::NavigationSimulator::CreateForFencedFrame(
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL(kTestUrl), fenced_frame_root);
ASSERT_NE(nullptr, simulator);
simulator->Commit();
@@ -284,13 +287,7 @@ TEST_F(PageLoadTrackerTest, FencedFramesPageType) {
EXPECT_TRUE(GetEvents().was_ready_to_commit_next_navigation);
}
-// TODO(https://crbug.com/1312096): Enable the test on Android.
-#if BUILDFLAG(IS_ANDROID)
-#define MAYBE_StopObservingOnPrerender DISABLED_StopObservingOnPrerender
-#else
-#define MAYBE_StopObservingOnPrerender StopObservingOnPrerender
-#endif
-TEST_F(PageLoadTrackerTest, MAYBE_StopObservingOnPrerender) {
+TEST_F(PageLoadTrackerTest, StopObservingOnPrerender) {
// Target URL to monitor the tracker via the test observer.
const char kPrerenderingUrl[] = "https://a.test/prerender";
SetTargetUrl(kPrerenderingUrl);
@@ -321,9 +318,9 @@ TEST_F(PageLoadTrackerTest, StopObservingOnFencedFrames) {
// Add a fenced frame.
content::RenderFrameHost* fenced_frame_root =
- content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+ content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
->AppendFencedFrame();
- auto simulator = content::NavigationSimulator::CreateForFencedFrame(
+ auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL(kFencedFramesUrl), fenced_frame_root);
ASSERT_NE(nullptr, simulator);
simulator->Commit();
@@ -335,6 +332,32 @@ TEST_F(PageLoadTrackerTest, StopObservingOnFencedFrames) {
EXPECT_FALSE(GetEvents().was_committed);
}
+TEST_F(PageLoadTrackerTest, ResumeOnPrerenderActivation) {
+ // Target URL to monitor the tracker via the test observer.
+ const char kPrerenderingUrl[] = "https://a.test/prerender";
+ SetTargetUrl(kPrerenderingUrl);
+
+ // Navigate in.
+ NavigateAndCommit(GURL(kTestUrl));
+
+ // Add a prerender page.
+ content::WebContentsTester::For(web_contents())
+ ->AddPrerenderAndCommitNavigation(GURL(kPrerenderingUrl));
+
+ // Check observer behaviors.
+ EXPECT_FALSE(GetEvents().was_started);
+ EXPECT_FALSE(GetEvents().was_fenced_frames_started);
+ EXPECT_TRUE(GetEvents().was_prerender_started);
+ EXPECT_TRUE(GetEvents().was_committed);
+ EXPECT_FALSE(GetEvents().was_prerendered_page_activated);
+
+ // Activate the prerendered page.
+ content::WebContentsTester::For(web_contents())
+ ->ActivatePrerenderedPage(GURL(kPrerenderingUrl));
+
+ EXPECT_TRUE(GetEvents().was_prerendered_page_activated);
+}
+
} // namespace
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc b/chromium/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc
index 0540ec6b41b..8e508b7e41e 100644
--- a/chromium/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc
+++ b/chromium/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc
@@ -18,9 +18,10 @@ namespace {
// Simple PageLoadMetricsObserver that copies observed PageLoadTimings into the
// provided std::vector, so they can be analyzed by unit tests.
-class TestPageLoadMetricsObserver : public PageLoadMetricsObserver {
+class TimingLoggingPageLoadMetricsObserver final
+ : public PageLoadMetricsObserver {
public:
- TestPageLoadMetricsObserver(
+ TimingLoggingPageLoadMetricsObserver(
std::vector<mojom::PageLoadTimingPtr>* updated_timings,
std::vector<mojom::PageLoadTimingPtr>* updated_subframe_timings,
std::vector<mojom::PageLoadTimingPtr>* complete_timings,
@@ -43,6 +44,11 @@ class TestPageLoadMetricsObserver : public PageLoadMetricsObserver {
is_first_navigation_in_web_contents),
count_on_enter_back_forward_cache_(count_on_enter_back_forward_cache) {}
+ const char* GetObserverName() const override {
+ static const char kName[] = "TimingLoggingPageLoadMetricsObserver";
+ return kName;
+ }
+
ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url,
bool started_in_foreground) override {
@@ -52,6 +58,12 @@ class TestPageLoadMetricsObserver : public PageLoadMetricsObserver {
return CONTINUE_OBSERVING;
}
+ ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override {
+ return FORWARD_OBSERVING;
+ }
+
void OnTimingUpdate(content::RenderFrameHost* subframe_rfh,
const mojom::PageLoadTiming& timing) override {
if (subframe_rfh) {
@@ -115,12 +127,17 @@ class TestPageLoadMetricsObserver : public PageLoadMetricsObserver {
// Test PageLoadMetricsObserver that stops observing page loads with certain
// substrings in the URL.
-class FilteringPageLoadMetricsObserver : public PageLoadMetricsObserver {
+class FilteringPageLoadMetricsObserver final : public PageLoadMetricsObserver {
public:
explicit FilteringPageLoadMetricsObserver(
std::vector<GURL>* completed_filtered_urls)
: completed_filtered_urls_(completed_filtered_urls) {}
+ const char* GetObserverName() const override {
+ static const char kName[] = "FilteringPageLoadMetricsObserver";
+ return kName;
+ }
+
ObservePolicy OnStart(content::NavigationHandle* handle,
const GURL& currently_committed_url,
bool started_in_foreground) override {
@@ -129,6 +146,12 @@ class FilteringPageLoadMetricsObserver : public PageLoadMetricsObserver {
return should_ignore ? STOP_OBSERVING : CONTINUE_OBSERVING;
}
+ ObservePolicy OnFencedFramesStart(
+ content::NavigationHandle* navigation_handle,
+ const GURL& currently_committed_url) override {
+ return FORWARD_OBSERVING;
+ }
+
ObservePolicy OnCommit(content::NavigationHandle* handle) override {
const bool should_ignore =
handle->GetURL().spec().find("ignore-on-commit") != std::string::npos;
@@ -157,7 +180,7 @@ bool TestMetricsWebContentsObserverEmbedder::IsNewTabPageUrl(const GURL& url) {
void TestMetricsWebContentsObserverEmbedder::RegisterObservers(
PageLoadTracker* tracker) {
- tracker->AddObserver(std::make_unique<TestPageLoadMetricsObserver>(
+ tracker->AddObserver(std::make_unique<TimingLoggingPageLoadMetricsObserver>(
&updated_timings_, &updated_subframe_timings_, &complete_timings_,
&updated_cpu_timings_, &loaded_resources_, &observed_committed_urls_,
&observed_aborted_urls_, &observed_features_,
diff --git a/chromium/components/page_load_metrics/common/page_load_metrics.mojom b/chromium/components/page_load_metrics/common/page_load_metrics.mojom
index feb82dd29ac..6b2d6cf4c15 100644
--- a/chromium/components/page_load_metrics/common/page_load_metrics.mojom
+++ b/chromium/components/page_load_metrics/common/page_load_metrics.mojom
@@ -7,11 +7,10 @@ module page_load_metrics.mojom;
import "ui/gfx/geometry/mojom/geometry.mojom";
import "mojo/public/mojom/base/shared_memory.mojom";
import "mojo/public/mojom/base/time.mojom";
-import "third_party/blink/public/mojom/web_feature/web_feature.mojom";
+import "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom";
import
"third_party/blink/public/mojom/mobile_metrics/mobile_friendliness.mojom";
import "third_party/blink/public/mojom/use_counter/use_counter_feature.mojom";
-import "url/mojom/origin.mojom";
// TimeDeltas below relative to navigation start.
struct DocumentTiming {
@@ -39,7 +38,7 @@ struct LargestContentfulPaintTiming {
// These are packed blink::LargestContentfulPaintType enums, indicating
// the largest LCP candidate's type characteristics.
- uint32 type;
+ uint64 type;
// Computed entropy of the page's largest image, calculated as the image file
// size, in bits, divided by the image's rendered size, in pixels.
@@ -188,23 +187,24 @@ struct PageLoadTiming {
// If you add additional members, also be sure to update page_load_timing.h.
};
-// FrameIntersectionUpdate contains a frame's intersections with other elements
-// in a page load.
-struct FrameIntersectionUpdate {
- // The frame's current intersection rect with the main frame in the main
- // frame's coordinate system.. The intersection rect is
- // an empty rect when there is no intersection with the main frame and
- // returns the document size of the root document for the main frame. This
- // is only set the first time an intersection changes and is null otherwise.
- gfx.mojom.Rect? main_frame_intersection_rect;
-};
-
struct FrameMetadata {
// These are packed blink::LoadingBehaviorFlag enums.
int32 behavior_flags = 0;
- // The frame's intersection with page elements.
- FrameIntersectionUpdate? intersection_update;
+ // For the main frame, the rect is the main frame document size (at (0,0));
+ // for a subframe, the rect is frame's intersection rect with the main frame
+ // in the main frame's coordinate system, and is an empty rect when there is
+ // no intersection with the main frame. This is only set for the first time
+ // the intersection rectangle is initially computed, and for any subsequent
+ // changes, and is null otherwise (i.e. hasn't changed).
+ gfx.mojom.Rect? main_frame_intersection_rect;
+
+ // The main frame's viewport rectangle (encapsulating the dimensions and the
+ // scroll position) in the main frame's coordinate system. This is only set
+ // for the main frame, for the first time the viewport rectangle is initially
+ // computed, and for any subsequent changes, and is null otherwise (i.e.
+ // hasn't changed).
+ gfx.mojom.Rect? main_frame_viewport_rect;
};
// Enumeration of distinct cache types.
@@ -261,9 +261,6 @@ struct ResourceDataUpdate {
// Whether this resource was fetched via proxy.
bool proxy_used;
- // The origin of this resource.
- url.mojom.Origin origin;
-
// Whether this resource completed loading, either by network or cache, before
// FCP in the frame it belongs to. This flag can be set to true at any point
// during a resource load. A more recent ResourceDataUpdate can have a
diff --git a/chromium/components/page_load_metrics/renderer/BUILD.gn b/chromium/components/page_load_metrics/renderer/BUILD.gn
index b43722dd1b3..c80874c3d79 100644
--- a/chromium/components/page_load_metrics/renderer/BUILD.gn
+++ b/chromium/components/page_load_metrics/renderer/BUILD.gn
@@ -16,7 +16,6 @@ source_set("renderer") {
]
deps = [
"//base",
- "//components/data_reduction_proxy/core/common",
"//components/page_load_metrics/common",
"//components/page_load_metrics/common:page_load_metrics_mojom",
"//components/subresource_filter/content/renderer",
@@ -35,6 +34,7 @@ source_set("unit_tests") {
"fake_page_timing_sender.cc",
"fake_page_timing_sender.h",
"metrics_render_frame_observer_unittest.cc",
+ "page_timing_metadata_recorder_unittest.cc",
"page_timing_metrics_sender_unittest.cc",
]
diff --git a/chromium/components/page_load_metrics/renderer/DEPS b/chromium/components/page_load_metrics/renderer/DEPS
index 8b9c4a89054..c843d441163 100644
--- a/chromium/components/page_load_metrics/renderer/DEPS
+++ b/chromium/components/page_load_metrics/renderer/DEPS
@@ -2,7 +2,6 @@ include_rules = [
"+base",
"+content/public/common",
"+content/public/renderer",
- "+components/data_reduction_proxy/core/common",
"+components/subresource_filter/content/renderer",
"+services/network/public/cpp",
"+services/network/public/mojom",
diff --git a/chromium/components/page_load_metrics/renderer/fake_page_timing_sender.cc b/chromium/components/page_load_metrics/renderer/fake_page_timing_sender.cc
index a2ade2f0b17..3ab172efa98 100644
--- a/chromium/components/page_load_metrics/renderer/fake_page_timing_sender.cc
+++ b/chromium/components/page_load_metrics/renderer/fake_page_timing_sender.cc
@@ -124,12 +124,15 @@ void FakePageTimingSender::PageTimingValidator::VerifyExpectedRenderData()
}
void FakePageTimingSender::PageTimingValidator::
- VerifyExpectedFrameIntersectionUpdate() const {
- if (!expected_frame_intersection_update_.is_null()) {
- EXPECT_FALSE(actual_frame_intersection_update_.is_null());
- EXPECT_TRUE(expected_frame_intersection_update_->Equals(
- *actual_frame_intersection_update_));
- }
+ VerifyExpectedMainFrameIntersectionRect() const {
+ EXPECT_EQ(expected_main_frame_intersection_rect_,
+ actual_main_frame_intersection_rect_);
+}
+
+void FakePageTimingSender::PageTimingValidator::
+ VerifyExpectedMainFrameViewportRect() const {
+ EXPECT_EQ(expected_main_frame_viewport_rect_,
+ actual_main_frame_viewport_rect_);
}
void FakePageTimingSender::PageTimingValidator::UpdateTiming(
@@ -153,7 +156,8 @@ void FakePageTimingSender::PageTimingValidator::UpdateTiming(
}
actual_render_data_.layout_shift_delta = render_data.layout_shift_delta;
- actual_frame_intersection_update_ = metadata->intersection_update.Clone();
+ actual_main_frame_intersection_rect_ = metadata->main_frame_intersection_rect;
+ actual_main_frame_viewport_rect_ = metadata->main_frame_viewport_rect;
actual_input_timing->num_input_events += new_input_timing->num_input_events;
actual_input_timing->total_input_delay += new_input_timing->total_input_delay;
@@ -166,7 +170,8 @@ void FakePageTimingSender::PageTimingValidator::UpdateTiming(
VerifyExpectedCpuTimings();
VerifyExpectedFeatures();
VerifyExpectedRenderData();
- VerifyExpectedFrameIntersectionUpdate();
+ VerifyExpectedMainFrameIntersectionRect();
+ VerifyExpectedMainFrameViewportRect();
VerifyExpectedMobileFriendliness();
}
diff --git a/chromium/components/page_load_metrics/renderer/fake_page_timing_sender.h b/chromium/components/page_load_metrics/renderer/fake_page_timing_sender.h
index 77c9ff541f2..99d60b2fbb1 100644
--- a/chromium/components/page_load_metrics/renderer/fake_page_timing_sender.h
+++ b/chromium/components/page_load_metrics/renderer/fake_page_timing_sender.h
@@ -79,16 +79,22 @@ class FakePageTimingSender : public PageTimingSender {
void UpdateExpectedMobileFriendliness(
const blink::MobileFriendliness& mobile_friendliness);
- void UpdateExpectFrameIntersectionUpdate(
- const mojom::FrameIntersectionUpdate& frame_intersection_update) {
- expected_frame_intersection_update_ = frame_intersection_update.Clone();
+ void UpdateExpectedMainFrameIntersectionRect(
+ const gfx::Rect& main_frame_intersection_rect) {
+ expected_main_frame_intersection_rect_ = main_frame_intersection_rect;
+ }
+
+ void UpdateExpectedMainFrameViewportRect(
+ const gfx::Rect& main_frame_viewport_rect) {
+ expected_main_frame_viewport_rect_ = main_frame_viewport_rect;
}
// Forces verification that actual features sent through SendTiming match
// expected features provided via ExpectPageLoadFeatures.
void VerifyExpectedFeatures() const;
void VerifyExpectedRenderData() const;
- void VerifyExpectedFrameIntersectionUpdate() const;
+ void VerifyExpectedMainFrameIntersectionRect() const;
+ void VerifyExpectedMainFrameViewportRect() const;
const std::vector<mojom::PageLoadTimingPtr>& expected_timings() const {
return expected_timings_;
@@ -116,8 +122,10 @@ class FakePageTimingSender : public PageTimingSender {
std::set<blink::UseCounterFeature> actual_features_;
mojom::FrameRenderDataUpdatePtr expected_render_data_;
mojom::FrameRenderDataUpdate actual_render_data_;
- mojom::FrameIntersectionUpdatePtr expected_frame_intersection_update_;
- mojom::FrameIntersectionUpdatePtr actual_frame_intersection_update_;
+ absl::optional<gfx::Rect> expected_main_frame_intersection_rect_;
+ absl::optional<gfx::Rect> actual_main_frame_intersection_rect_;
+ absl::optional<gfx::Rect> expected_main_frame_viewport_rect_;
+ absl::optional<gfx::Rect> actual_main_frame_viewport_rect_;
mojom::InputTimingPtr expected_input_timing;
mojom::InputTimingPtr actual_input_timing;
absl::optional<blink::MobileFriendliness> expected_mobile_friendliness;
diff --git a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
index 9e19bc64034..5de46437259 100644
--- a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
+++ b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
@@ -157,7 +157,7 @@ void MetricsRenderFrameObserver::DidObserveLayoutNg(uint32_t all_block_count,
}
void MetricsRenderFrameObserver::DidStartResponse(
- const GURL& response_url,
+ const url::SchemeHostPort& final_response_url,
int request_id,
const network::mojom::URLResponseHead& response_head,
network::mojom::RequestDestination request_destination) {
@@ -168,10 +168,10 @@ void MetricsRenderFrameObserver::DidStartResponse(
// case. There should be a guarantee that DidStartProvisionalLoad be called
// before DidStartResponse for the frame request.
provisional_frame_resource_data_use_->DidStartResponse(
- response_url, request_id, response_head, request_destination);
+ final_response_url, request_id, response_head, request_destination);
} else if (page_timing_metrics_sender_) {
page_timing_metrics_sender_->DidStartResponse(
- response_url, request_id, response_head, request_destination);
+ final_response_url, request_id, response_head, request_destination);
UpdateResourceMetadata(request_id);
}
}
@@ -314,10 +314,8 @@ void MetricsRenderFrameObserver::DidCreateDocumentElement() {
CreatePageTimingSender(true /* limited_sending_mode */), CreateTimer(),
std::move(timing.relative_timing), timing.monotonic_timing,
std::make_unique<PageResourceDataUse>());
- if (ukm_smoothness_data_.IsValid()) {
- page_timing_metrics_sender_->SetUpSmoothnessReporting(
- std::move(ukm_smoothness_data_));
- }
+
+ OnMetricsSenderCreated();
}
void MetricsRenderFrameObserver::DidCommitProvisionalLoad(
@@ -342,10 +340,8 @@ void MetricsRenderFrameObserver::DidCommitProvisionalLoad(
CreatePageTimingSender(false /* limited_sending_mode*/), CreateTimer(),
std::move(timing.relative_timing), timing.monotonic_timing,
std::move(provisional_frame_resource_data_use_));
- if (ukm_smoothness_data_.IsValid()) {
- page_timing_metrics_sender_->SetUpSmoothnessReporting(
- std::move(ukm_smoothness_data_));
- }
+
+ OnMetricsSenderCreated();
}
void MetricsRenderFrameObserver::SetAdResourceTracker(
@@ -366,10 +362,23 @@ void MetricsRenderFrameObserver::OnAdResourceObserved(int request_id) {
}
void MetricsRenderFrameObserver::OnMainFrameIntersectionChanged(
- const gfx::Rect& main_frame_intersection) {
- if (page_timing_metrics_sender_)
+ const gfx::Rect& main_frame_intersection_rect) {
+ if (page_timing_metrics_sender_) {
page_timing_metrics_sender_->OnMainFrameIntersectionChanged(
- main_frame_intersection);
+ main_frame_intersection_rect);
+ return;
+ }
+
+ main_frame_intersection_rect_before_metrics_sender_created_ =
+ main_frame_intersection_rect;
+}
+
+void MetricsRenderFrameObserver::OnMainFrameViewportRectangleChanged(
+ const gfx::Rect& main_frame_viewport_rect) {
+ if (page_timing_metrics_sender_) {
+ page_timing_metrics_sender_->OnMainFrameViewportRectangleChanged(
+ main_frame_viewport_rect);
+ }
}
void MetricsRenderFrameObserver::OnMobileFriendlinessChanged(
@@ -470,6 +479,21 @@ void MetricsRenderFrameObserver::SendMetrics() {
timing.monotonic_timing);
}
+void MetricsRenderFrameObserver::OnMetricsSenderCreated() {
+ if (ukm_smoothness_data_.IsValid()) {
+ page_timing_metrics_sender_->SetUpSmoothnessReporting(
+ std::move(ukm_smoothness_data_));
+ }
+
+ // Send the latest the frame intersection update, as otherwise we may miss
+ // this information for a frame completely if there are no future updates.
+ if (main_frame_intersection_rect_before_metrics_sender_created_) {
+ page_timing_metrics_sender_->OnMainFrameIntersectionChanged(
+ *main_frame_intersection_rect_before_metrics_sender_created_);
+ main_frame_intersection_rect_before_metrics_sender_created_.reset();
+ }
+}
+
MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
const {
const blink::WebPerformance& perf =
@@ -486,11 +510,16 @@ MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
}
if (perf.FirstInputDelay().has_value()) {
timing->interactive_timing->first_input_delay = *perf.FirstInputDelay();
+ monotonic_timing.first_input_delay = perf.FirstInputDelay();
}
if (perf.FirstInputTimestamp().has_value()) {
timing->interactive_timing->first_input_timestamp =
ClampDelta((*perf.FirstInputTimestamp()).InSecondsF(), start);
}
+ if (perf.FirstInputTimestampAsMonotonicTime()) {
+ monotonic_timing.first_input_timestamp =
+ perf.FirstInputTimestampAsMonotonicTime();
+ }
if (perf.LongestInputDelay().has_value()) {
timing->interactive_timing->longest_input_delay = *perf.LongestInputDelay();
}
@@ -580,7 +609,7 @@ MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
? base::TimeDelta()
: ClampDelta(perf.LargestImagePaint(), start);
timing->paint_timing->largest_contentful_paint->type =
- perf.LargestContentfulPaintType();
+ LargestContentfulPaintTypeToUKMFlags(perf.LargestContentfulPaintType());
timing->paint_timing->largest_contentful_paint->image_bpp =
perf.LargestContentfulPaintImageBPP();
}
@@ -606,7 +635,7 @@ MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
? base::TimeDelta()
: ClampDelta(perf.ExperimentalLargestImagePaint(), start);
timing->paint_timing->experimental_largest_contentful_paint->type =
- perf.LargestContentfulPaintType();
+ LargestContentfulPaintTypeToUKMFlags(perf.LargestContentfulPaintType());
}
if (perf.ExperimentalLargestTextPaintSize() > 0) {
// ExperimentalLargestTextPaint and ExperimentalLargestTextPaintSize should
diff --git a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h
index 4606a7751f2..ba1ba570f84 100644
--- a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h
+++ b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer.h
@@ -63,7 +63,7 @@ class MetricsRenderFrameObserver
uint32_t all_call_count,
uint32_t ng_call_count) override;
void DidStartResponse(
- const GURL& response_url,
+ const url::SchemeHostPort& final_response_url,
int request_id,
const network::mojom::URLResponseHead& response_head,
network::mojom::RequestDestination request_destination) override;
@@ -103,7 +103,9 @@ class MetricsRenderFrameObserver
void OnAdResourceObserved(int request_id) override;
void OnMainFrameIntersectionChanged(
- const gfx::Rect& main_frame_intersection) override;
+ const gfx::Rect& main_frame_intersection_rect) override;
+ void OnMainFrameViewportRectangleChanged(
+ const gfx::Rect& main_frame_viewport_rect) override;
void OnMobileFriendlinessChanged(const blink::MobileFriendliness&) override;
bool SetUpSmoothnessReporting(
@@ -136,6 +138,7 @@ class MetricsRenderFrameObserver
void MaybeSetCompletedBeforeFCP(int request_id);
void SendMetrics();
+ void OnMetricsSenderCreated();
virtual Timing GetTiming() const;
virtual std::unique_ptr<base::OneShotTimer> CreateTimer();
virtual std::unique_ptr<PageTimingSender> CreatePageTimingSender(
@@ -164,6 +167,12 @@ class MetricsRenderFrameObserver
// Handle to the shared memory for transporting smoothness related ukm data.
base::ReadOnlySharedMemoryRegion ukm_smoothness_data_;
+ // The main frame intersection rectangle signal received before
+ // `page_timing_metrics_sender_` is created. The signal will be send out right
+ // after `page_timing_metrics_sender_` is created.
+ absl::optional<gfx::Rect>
+ main_frame_intersection_rect_before_metrics_sender_created_;
+
// Will be null when we're not actively sending metrics.
std::unique_ptr<PageTimingMetricsSender> page_timing_metrics_sender_;
};
diff --git a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc
index b2d3b276a74..5add04aae8f 100644
--- a/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc
+++ b/chromium/components/page_load_metrics/renderer/metrics_render_frame_observer_unittest.cc
@@ -52,6 +52,12 @@ class TestMetricsRenderFrameObserver : public MetricsRenderFrameObserver,
fake_timing_ = timing.Clone();
}
+ void ExpectMainFrameIntersectionRect(
+ const gfx::Rect& main_frame_intersection_rect) {
+ validator_.UpdateExpectedMainFrameIntersectionRect(
+ main_frame_intersection_rect);
+ }
+
Timing GetTiming() const override {
EXPECT_NE(nullptr, fake_timing_.get());
return Timing(std::move(fake_timing_),
@@ -99,6 +105,26 @@ TEST_F(MetricsRenderFrameObserverTest, SingleMetric) {
observer.GetMockTimer()->Fire();
}
+TEST_F(MetricsRenderFrameObserverTest,
+ MainFrameIntersectionUpdateBeforeMetricsSenderCreated) {
+ base::Time nav_start = base::Time::FromDoubleT(10);
+
+ TestMetricsRenderFrameObserver observer;
+ observer.OnMainFrameIntersectionChanged(gfx::Rect(1, 2, 3, 4));
+
+ mojom::PageLoadTiming timing;
+ page_load_metrics::InitPageLoadTimingForTest(&timing);
+ timing.navigation_start = nav_start;
+ observer.ExpectPageLoadTiming(timing);
+ observer.DidStartNavigation(GURL(), absl::nullopt);
+ observer.ReadyToCommitNavigation(nullptr);
+ observer.DidCommitProvisionalLoad(ui::PAGE_TRANSITION_LINK);
+
+ observer.ExpectMainFrameIntersectionRect(gfx::Rect(1, 2, 3, 4));
+
+ observer.GetMockTimer()->Fire();
+}
+
// Verify that when two CpuTimings come in, they're grouped into a single
// Message with the total being the sum of the two.
TEST_F(MetricsRenderFrameObserverTest, SingleCpuMetric) {
diff --git a/chromium/components/page_load_metrics/renderer/page_resource_data_use.cc b/chromium/components/page_load_metrics/renderer/page_resource_data_use.cc
index 8d30de0f202..79e728b7ca4 100644
--- a/chromium/components/page_load_metrics/renderer/page_resource_data_use.cc
+++ b/chromium/components/page_load_metrics/renderer/page_resource_data_use.cc
@@ -8,6 +8,7 @@
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/common/loader/resource_type_util.h"
#include "url/gurl.h"
+#include "url/scheme_host_port.h"
namespace page_load_metrics {
@@ -30,7 +31,7 @@ PageResourceDataUse::PageResourceDataUse(const PageResourceDataUse& other) =
PageResourceDataUse::~PageResourceDataUse() = default;
void PageResourceDataUse::DidStartResponse(
- const GURL& response_url,
+ const url::SchemeHostPort& final_response_url,
int resource_id,
const network::mojom::URLResponseHead& response_head,
network::mojom::RequestDestination request_destination) {
@@ -40,10 +41,9 @@ void PageResourceDataUse::DidStartResponse(
mime_type_ = response_head.mime_type;
if (response_head.was_fetched_via_cache)
cache_type_ = mojom::CacheType::kHttp;
- is_secure_scheme_ = response_url.SchemeIsCryptographic();
+ is_secure_scheme_ = GURL::SchemeIsCryptographic(final_response_url.scheme());
is_primary_frame_resource_ =
blink::IsRequestDestinationFrame(request_destination);
- origin_ = url::Origin::Create(response_url);
}
void PageResourceDataUse::DidReceiveTransferSizeUpdate(
@@ -71,7 +71,6 @@ void PageResourceDataUse::DidLoadFromMemoryCache(const GURL& response_url,
int request_id,
int64_t encoded_body_length,
const std::string& mime_type) {
- origin_ = url::Origin::Create(response_url);
resource_id_ = request_id;
mime_type_ = mime_type;
is_secure_scheme_ = response_url.SchemeIsCryptographic();
@@ -126,7 +125,6 @@ mojom::ResourceDataUpdatePtr PageResourceDataUse::GetResourceDataUpdate() {
resource_data_update->is_secure_scheme = is_secure_scheme_;
resource_data_update->proxy_used = proxy_used_;
resource_data_update->is_primary_frame_resource = is_primary_frame_resource_;
- resource_data_update->origin = origin_;
resource_data_update->completed_before_fcp = completed_before_fcp_;
return resource_data_update;
}
diff --git a/chromium/components/page_load_metrics/renderer/page_resource_data_use.h b/chromium/components/page_load_metrics/renderer/page_resource_data_use.h
index 5b873ed5c80..9c29051498c 100644
--- a/chromium/components/page_load_metrics/renderer/page_resource_data_use.h
+++ b/chromium/components/page_load_metrics/renderer/page_resource_data_use.h
@@ -8,7 +8,6 @@
#include "components/page_load_metrics/common/page_load_metrics.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom-forward.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
-#include "url/origin.h"
class GURL;
@@ -16,6 +15,10 @@ namespace network {
struct URLLoaderCompletionStatus;
} // namespace network
+namespace url {
+class SchemeHostPort;
+} // namespace url
+
namespace page_load_metrics {
// PageResourceDataUse contains the data use information of one resource. Data
@@ -29,7 +32,7 @@ class PageResourceDataUse {
~PageResourceDataUse();
- void DidStartResponse(const GURL& response_url,
+ void DidStartResponse(const url::SchemeHostPort& final_response_url,
int resource_id,
const network::mojom::URLResponseHead& response_head,
network::mojom::RequestDestination request_destination);
@@ -89,8 +92,6 @@ class PageResourceDataUse {
mojom::CacheType cache_type_;
- url::Origin origin_;
-
std::string mime_type_;
};
diff --git a/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.cc b/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.cc
index fc7402261af..2cf6e5835d2 100644
--- a/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.cc
+++ b/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.cc
@@ -4,10 +4,7 @@
#include "components/page_load_metrics/renderer/page_timing_metadata_recorder.h"
-#include "base/profiler/sample_metadata.h"
-
namespace page_load_metrics {
-
namespace {
bool IsTimeTicksRangeSensible(base::TimeTicks start, base::TimeTicks end) {
return start <= end && end <= base::TimeTicks::Now();
@@ -38,23 +35,59 @@ PageTimingMetadataRecorder::PageTimingMetadataRecorder(
PageTimingMetadataRecorder::~PageTimingMetadataRecorder() = default;
void PageTimingMetadataRecorder::UpdateMetadata(const MonotonicTiming& timing) {
+ UpdateFirstContentfulPaintMetadata(timing.navigation_start,
+ timing.first_contentful_paint);
+ UpdateFirstInputDelayMetadata(timing.first_input_timestamp,
+ timing.first_input_delay);
+ timing_ = timing;
+}
+
+void PageTimingMetadataRecorder::ApplyMetadataToPastSamples(
+ base::TimeTicks period_start,
+ base::TimeTicks period_end,
+ base::StringPiece name,
+ int64_t key,
+ int64_t value,
+ base::SampleMetadataScope scope) {
+ base::ApplyMetadataToPastSamples(period_start, period_end, name, key, value,
+ scope);
+}
+
+void PageTimingMetadataRecorder::UpdateFirstInputDelayMetadata(
+ const absl::optional<base::TimeTicks>& first_input_timestamp,
+ const absl::optional<base::TimeDelta>& first_input_delay) {
// Applying metadata to past samples has non-trivial cost so only do so if
// the relevant values changed.
const bool should_apply_metadata =
- timing.navigation_start.has_value() &&
- timing.first_contentful_paint.has_value() &&
- (timing_.navigation_start != timing.navigation_start ||
- timing_.first_contentful_paint != timing.first_contentful_paint);
- if (should_apply_metadata &&
- IsTimeTicksRangeSensible(*timing.navigation_start,
- *timing.first_contentful_paint)) {
- base::ApplyMetadataToPastSamples(
- *timing.navigation_start, *timing.first_contentful_paint,
- "PageLoad.PaintTiming.NavigationToFirstContentfulPaint", instance_id_,
- 1, base::SampleMetadataScope::kProcess);
+ first_input_timestamp.has_value() && first_input_delay.has_value() &&
+ (timing_.first_input_timestamp != first_input_timestamp ||
+ timing_.first_input_delay != first_input_delay);
+
+ if (should_apply_metadata) {
+ ApplyMetadataToPastSamples(
+ *first_input_timestamp, *first_input_timestamp + *first_input_delay,
+ "PageLoad.InteractiveTiming.FirstInputDelay4", /* key=*/instance_id_,
+ /* value=*/1, base::SampleMetadataScope::kProcess);
}
+}
- timing_ = timing;
+void PageTimingMetadataRecorder::UpdateFirstContentfulPaintMetadata(
+ const absl::optional<base::TimeTicks>& navigation_start,
+ const absl::optional<base::TimeTicks>& first_contentful_paint) {
+ // Applying metadata to past samples has non-trivial cost so only do so if
+ // the relevant values changed.
+ const bool should_apply_metadata =
+ navigation_start.has_value() && first_contentful_paint.has_value() &&
+ (timing_.navigation_start != navigation_start ||
+ timing_.first_contentful_paint != first_contentful_paint);
+ if (should_apply_metadata &&
+ IsTimeTicksRangeSensible(*navigation_start, *first_contentful_paint)) {
+ ApplyMetadataToPastSamples(
+ *navigation_start, *first_contentful_paint,
+ "PageLoad.PaintTiming.NavigationToFirstContentfulPaint",
+ /* key=*/instance_id_,
+ /* value=*/1, base::SampleMetadataScope::kProcess);
+ }
}
} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.h b/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.h
index 6bf38a9a2c8..285f39fd5c5 100644
--- a/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.h
+++ b/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_PAGE_LOAD_METRICS_RENDERER_PAGE_TIMING_METADATA_RECORDER_H_
#define COMPONENTS_PAGE_LOAD_METRICS_RENDERER_PAGE_TIMING_METADATA_RECORDER_H_
+#include "base/profiler/sample_metadata.h"
#include "base/time/time.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -16,7 +17,9 @@ namespace page_load_metrics {
// reach out to page_load_metrics owners to discuss it.
class PageTimingMetadataRecorder {
public:
- // Records the monotonic times that define first contentful paint.
+ // Records the monotonic times that define
+ // - First contentful paint
+ // - First input delay
struct MonotonicTiming {
MonotonicTiming();
@@ -27,6 +30,9 @@ class PageTimingMetadataRecorder {
absl::optional<base::TimeTicks> navigation_start;
absl::optional<base::TimeTicks> first_contentful_paint;
+
+ absl::optional<base::TimeTicks> first_input_timestamp;
+ absl::optional<base::TimeDelta> first_input_delay;
};
PageTimingMetadataRecorder(const MonotonicTiming& initial_timing);
@@ -36,15 +42,32 @@ class PageTimingMetadataRecorder {
PageTimingMetadataRecorder& operator=(const PageTimingMetadataRecorder&) =
delete;
+ // Updates the metadata on past samples based on given timing. Called whenever
+ // `PageTimingMetricsSender::Update` is called.
void UpdateMetadata(const MonotonicTiming& timing);
+ protected:
+ // To be overridden by test class.
+ virtual void ApplyMetadataToPastSamples(base::TimeTicks period_start,
+ base::TimeTicks period_end,
+ base::StringPiece name,
+ int64_t key,
+ int64_t value,
+ base::SampleMetadataScope scope);
+
private:
+ void UpdateFirstInputDelayMetadata(
+ const absl::optional<base::TimeTicks>& first_input_timestamp,
+ const absl::optional<base::TimeDelta>& first_input_delay);
+ void UpdateFirstContentfulPaintMetadata(
+ const absl::optional<base::TimeTicks>& navigation_start,
+ const absl::optional<base::TimeTicks>& first_contentful_paint);
+
// Uniquely identifies an instance of the PageTimingMetadataRecorder. Used to
// distinguish page loads for different documents when applying sample
// metadata.
const int instance_id_;
- // Records the monotonic times that define first contentful paint.
MonotonicTiming timing_;
};
diff --git a/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder_unittest.cc b/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder_unittest.cc
new file mode 100644
index 00000000000..aac32f794fa
--- /dev/null
+++ b/chromium/components/page_load_metrics/renderer/page_timing_metadata_recorder_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2022 The Chromium 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/page_load_metrics/renderer/page_timing_metadata_recorder.h"
+
+#include <vector>
+
+#include "base/profiler/sample_metadata.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace page_load_metrics {
+
+struct MetadataTaggingRequest {
+ base::TimeTicks period_start;
+ base::TimeTicks period_end;
+ base::StringPiece name;
+ int64_t key;
+ int64_t value;
+};
+
+class TestPageTimingMetadataRecorder : public PageTimingMetadataRecorder {
+ public:
+ explicit TestPageTimingMetadataRecorder(const MonotonicTiming& initial_timing)
+ : PageTimingMetadataRecorder(initial_timing) {}
+
+ void ApplyMetadataToPastSamples(base::TimeTicks period_start,
+ base::TimeTicks period_end,
+ base::StringPiece name,
+ int64_t key,
+ int64_t value,
+ base::SampleMetadataScope scope) override {
+ requests_.push_back({
+ period_start,
+ period_end,
+ name,
+ key,
+ value,
+ });
+ }
+
+ const std::vector<MetadataTaggingRequest>& GetMetadataTaggingRequests()
+ const {
+ return requests_;
+ }
+
+ private:
+ std::vector<MetadataTaggingRequest> requests_ = {};
+};
+
+using PageTimingMetadataRecorderTest = testing::Test;
+
+TEST_F(PageTimingMetadataRecorderTest, FirstContentfulPaintUpdate) {
+ PageTimingMetadataRecorder::MonotonicTiming timing = {};
+ // The PageTimingMetadataRecorder constructor is supposed to call
+ // UpdateMetadata once, but due to class construction limitation, the
+ // call to ApplyMetadataToPastSample will not be captured by test class,
+ // as the test class is not ready yet.
+ TestPageTimingMetadataRecorder recorder(timing);
+ const std::vector<MetadataTaggingRequest>& requests =
+ recorder.GetMetadataTaggingRequests();
+
+ timing.navigation_start = base::TimeTicks::Now() - base::Milliseconds(500);
+ timing.first_contentful_paint =
+ *timing.navigation_start + base::Milliseconds(10);
+ recorder.UpdateMetadata(timing);
+ ASSERT_EQ(1u, requests.size());
+ EXPECT_EQ(*timing.navigation_start, requests.at(0).period_start);
+ EXPECT_EQ(*timing.first_contentful_paint, requests.at(0).period_end);
+ EXPECT_EQ("PageLoad.PaintTiming.NavigationToFirstContentfulPaint",
+ requests.at(0).name);
+
+ // Update first contentful paint timetick should sends another request.
+ timing.first_contentful_paint =
+ *timing.navigation_start + base::Milliseconds(20);
+ recorder.UpdateMetadata(timing);
+ ASSERT_EQ(2u, requests.size());
+ EXPECT_EQ(*timing.navigation_start, requests.at(1).period_start);
+ EXPECT_EQ(*timing.first_contentful_paint, requests.at(1).period_end);
+ EXPECT_EQ("PageLoad.PaintTiming.NavigationToFirstContentfulPaint",
+ requests.at(1).name);
+
+ // If nothing modified, should not send any requests.
+ recorder.UpdateMetadata(timing);
+ EXPECT_EQ(2u, requests.size());
+}
+
+TEST_F(PageTimingMetadataRecorderTest, FirstInputDelayUpdate) {
+ PageTimingMetadataRecorder::MonotonicTiming timing = {};
+ // The PageTimingMetadataRecorder constructor is supposed to call
+ // UpdateMetadata once, but due to class construction limitation, the
+ // call to ApplyMetadataToPastSample will not be captured by test class,
+ // as the test class is not ready yet.
+ TestPageTimingMetadataRecorder recorder(timing);
+ const std::vector<MetadataTaggingRequest>& requests =
+ recorder.GetMetadataTaggingRequests();
+
+ timing.first_input_delay = base::Milliseconds(10);
+ timing.first_input_timestamp = base::TimeTicks::Now();
+ recorder.UpdateMetadata(timing);
+ ASSERT_EQ(1u, requests.size());
+ EXPECT_EQ(*timing.first_input_timestamp, requests.at(0).period_start);
+ EXPECT_EQ(*timing.first_input_timestamp + *timing.first_input_delay,
+ requests.at(0).period_end);
+ EXPECT_EQ("PageLoad.InteractiveTiming.FirstInputDelay4", requests.at(0).name);
+
+ // Update first input delay should sends another request.
+ timing.first_input_delay = base::Milliseconds(11);
+ recorder.UpdateMetadata(timing);
+ ASSERT_EQ(2u, requests.size());
+ EXPECT_EQ(*timing.first_input_timestamp, requests.at(1).period_start);
+ EXPECT_EQ(*timing.first_input_timestamp + *timing.first_input_delay,
+ requests.at(1).period_end);
+ EXPECT_EQ("PageLoad.InteractiveTiming.FirstInputDelay4", requests.at(1).name);
+
+ // If nothing modified, should not send any requests.
+ recorder.UpdateMetadata(timing);
+ EXPECT_EQ(2u, requests.size());
+}
+
+} // namespace page_load_metrics
diff --git a/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc b/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc
index 237b98878bd..0fefca8d8ae 100644
--- a/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc
+++ b/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.cc
@@ -18,8 +18,8 @@
#include "services/network/public/cpp/url_loader_completion_status.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-shared.h"
#include "third_party/blink/public/mojom/use_counter/use_counter_feature.mojom-shared.h"
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-shared.h"
#include "ui/gfx/geometry/rect.h"
namespace page_load_metrics {
@@ -125,7 +125,7 @@ void PageTimingMetricsSender::DidObserveMobileFriendlinessChanged(
}
void PageTimingMetricsSender::DidStartResponse(
- const GURL& response_url,
+ const url::SchemeHostPort& final_response_url,
int resource_id,
const network::mojom::URLResponseHead& response_head,
network::mojom::RequestDestination request_destination) {
@@ -135,7 +135,7 @@ void PageTimingMetricsSender::DidStartResponse(
std::piecewise_construct, std::forward_as_tuple(resource_id),
std::forward_as_tuple(std::make_unique<PageResourceDataUse>()));
resource_it.first->second->DidStartResponse(
- response_url, resource_id, response_head, request_destination);
+ final_response_url, resource_id, response_head, request_destination);
}
void PageTimingMetricsSender::DidReceiveTransferSizeUpdate(
@@ -205,9 +205,14 @@ void PageTimingMetricsSender::DidLoadResourceFromMemoryCache(
}
void PageTimingMetricsSender::OnMainFrameIntersectionChanged(
- const gfx::Rect& main_frame_intersection) {
- metadata_->intersection_update =
- mojom::FrameIntersectionUpdate::New(main_frame_intersection);
+ const gfx::Rect& main_frame_intersection_rect) {
+ metadata_->main_frame_intersection_rect = main_frame_intersection_rect;
+ EnsureSendTimer();
+}
+
+void PageTimingMetricsSender::OnMainFrameViewportRectangleChanged(
+ const gfx::Rect& main_frame_viewport_rect) {
+ metadata_->main_frame_viewport_rect = main_frame_viewport_rect;
EnsureSendTimer();
}
@@ -319,7 +324,8 @@ void PageTimingMetricsSender::SendNow() {
mobile_friendliness_ = absl::nullopt;
InitiateUserInteractionTiming();
new_features_.clear();
- metadata_->intersection_update.reset();
+ metadata_->main_frame_intersection_rect.reset();
+ metadata_->main_frame_viewport_rect.reset();
last_cpu_timing_->task_time = base::TimeDelta();
modified_resources_.clear();
render_data_.new_layout_shifts.clear();
diff --git a/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.h b/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.h
index 8e215deedbf..5bd2b621666 100644
--- a/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.h
+++ b/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender.h
@@ -58,7 +58,7 @@ class PageTimingMetricsSender {
uint32_t ng_call_count);
void DidObserveMobileFriendlinessChanged(const blink::MobileFriendliness&);
- void DidStartResponse(const GURL& response_url,
+ void DidStartResponse(const url::SchemeHostPort& final_response_url,
int resource_id,
const network::mojom::URLResponseHead& response_head,
network::mojom::RequestDestination request_destination);
@@ -70,7 +70,10 @@ class PageTimingMetricsSender {
int request_id,
int64_t encoded_body_length,
const std::string& mime_type);
- void OnMainFrameIntersectionChanged(const gfx::Rect& intersect_rect);
+ void OnMainFrameIntersectionChanged(
+ const gfx::Rect& main_frame_intersection_rect);
+ void OnMainFrameViewportRectangleChanged(
+ const gfx::Rect& main_frame_viewport_rect);
void DidObserveInputDelay(base::TimeDelta input_delay);
void DidObserveUserInteraction(base::TimeDelta max_event_duration,
diff --git a/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender_unittest.cc b/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender_unittest.cc
index 8cc6cf4dcf6..a7b31d0cd2f 100644
--- a/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender_unittest.cc
+++ b/chromium/components/page_load_metrics/renderer/page_timing_metrics_sender_unittest.cc
@@ -318,7 +318,7 @@ TEST_F(PageTimingMetricsSenderTest, SendPageRenderData) {
validator_.VerifyExpectedRenderData();
}
-TEST_F(PageTimingMetricsSenderTest, SendFrameIntersectionUpdate) {
+TEST_F(PageTimingMetricsSenderTest, SendMainFrameIntersectionRect) {
mojom::PageLoadTiming timing;
InitPageLoadTimingForTest(&timing);
metrics_sender_->Update(timing.Clone(),
@@ -326,12 +326,24 @@ TEST_F(PageTimingMetricsSenderTest, SendFrameIntersectionUpdate) {
validator_.ExpectPageLoadTiming(timing);
metrics_sender_->OnMainFrameIntersectionChanged(gfx::Rect(0, 0, 1, 1));
- mojom::FrameIntersectionUpdate frame_intersection_update(
- gfx::Rect(0, 0, 1, 1));
- validator_.UpdateExpectFrameIntersectionUpdate(frame_intersection_update);
+ validator_.UpdateExpectedMainFrameIntersectionRect(gfx::Rect(0, 0, 1, 1));
metrics_sender_->mock_timer()->Fire();
- validator_.VerifyExpectedFrameIntersectionUpdate();
+ validator_.VerifyExpectedMainFrameIntersectionRect();
+}
+
+TEST_F(PageTimingMetricsSenderTest, SendMainFrameViewportRect) {
+ mojom::PageLoadTiming timing;
+ InitPageLoadTimingForTest(&timing);
+ metrics_sender_->Update(timing.Clone(),
+ PageTimingMetadataRecorder::MonotonicTiming());
+ validator_.ExpectPageLoadTiming(timing);
+
+ metrics_sender_->OnMainFrameViewportRectangleChanged(gfx::Rect(2, 2, 1, 1));
+ validator_.UpdateExpectedMainFrameViewportRect(gfx::Rect(2, 2, 1, 1));
+
+ metrics_sender_->mock_timer()->Fire();
+ validator_.VerifyExpectedMainFrameViewportRect();
}
TEST_F(PageTimingMetricsSenderTest, FirstContentfulPaintForcesSend) {
diff --git a/chromium/components/paint_preview/DEPS b/chromium/components/paint_preview/DEPS
index 6f79916e84a..bcc62885693 100644
--- a/chromium/components/paint_preview/DEPS
+++ b/chromium/components/paint_preview/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+components/crash/core/common/crash_key.h",
"+components/memory_pressure",
"+mojo/public/cpp",
"+third_party/harfbuzz-ng/src/src",
diff --git a/chromium/components/paint_preview/browser/paint_preview_base_service.cc b/chromium/components/paint_preview/browser/paint_preview_base_service.cc
index 9d12df92980..d607b58aee5 100644
--- a/chromium/components/paint_preview/browser/paint_preview_base_service.cc
+++ b/chromium/components/paint_preview/browser/paint_preview_base_service.cc
@@ -38,7 +38,7 @@ void PaintPreviewBaseService::CapturePaintPreview(CaptureParams capture_params,
content::WebContents* web_contents = capture_params.web_contents;
content::RenderFrameHost* render_frame_host =
capture_params.render_frame_host ? capture_params.render_frame_host
- : web_contents->GetMainFrame();
+ : web_contents->GetPrimaryMainFrame();
if (policy_ && !policy_->SupportedForContents(web_contents)) {
std::move(callback).Run(CaptureStatus::kContentUnsupported, {});
return;
@@ -57,7 +57,7 @@ void PaintPreviewBaseService::CapturePaintPreview(CaptureParams capture_params,
}
params.inner.clip_rect = capture_params.clip_rect;
params.inner.is_main_frame =
- (render_frame_host == web_contents->GetMainFrame());
+ (render_frame_host == web_contents->GetPrimaryMainFrame());
params.inner.capture_links = capture_params.capture_links;
params.inner.max_capture_size = capture_params.max_per_capture_size;
params.inner.max_decoded_image_size_bytes =
diff --git a/chromium/components/paint_preview/browser/paint_preview_client.cc b/chromium/components/paint_preview/browser/paint_preview_client.cc
index a44b1d9958b..1aa9e549b3f 100644
--- a/chromium/components/paint_preview/browser/paint_preview_client.cc
+++ b/chromium/components/paint_preview/browser/paint_preview_client.cc
@@ -21,7 +21,6 @@
#include "components/paint_preview/common/mojom/paint_preview_recorder.mojom-forward.h"
#include "components/paint_preview/common/proto_validator.h"
#include "components/paint_preview/common/version.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/global_routing_id.h"
@@ -197,15 +196,13 @@ PaintPreviewClient::InProgressDocumentCaptureState::
for (const auto& subframe_guid : awaiting_subframes) {
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
- base::BindOnce(base::GetDeleteFileCallback(),
- FilePathForFrame(subframe_guid)));
+ base::GetDeleteFileCallback(FilePathForFrame(subframe_guid)));
}
for (const auto& subframe_guid : finished_subframes) {
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
- base::BindOnce(base::GetDeleteFileCallback(),
- FilePathForFrame(subframe_guid)));
+ base::GetDeleteFileCallback(FilePathForFrame(subframe_guid)));
}
}
}
@@ -317,8 +314,7 @@ void PaintPreviewClient::CapturePaintPreview(
chromeVersion->set_build(CHROME_VERSION_BUILD);
chromeVersion->set_patch(CHROME_VERSION_PATCH);
document_data.callback = std::move(callback);
- document_data.source_id =
- ukm::GetSourceIdForWebContentsDocument(web_contents());
+ document_data.source_id = render_frame_host->GetPageUkmSourceId();
document_data.accepted_tokens = CreateAcceptedTokenList(render_frame_host);
auto token = render_frame_host->GetEmbeddingToken();
if (token.has_value()) {
@@ -373,7 +369,7 @@ void PaintPreviewClient::RenderFrameDeleted(
if (!maybe_token.has_value())
return;
- bool is_main_frame = render_frame_host->GetParent() == nullptr;
+ bool is_main_frame = render_frame_host->GetParentOrOuterDocument() == nullptr;
base::UnguessableToken frame_guid = maybe_token.value();
auto it = pending_previews_on_subframe_.find(frame_guid);
if (it == pending_previews_on_subframe_.end())
@@ -469,6 +465,7 @@ void PaintPreviewClient::RequestCaptureOnUIThread(
// If the render frame host navigated or is no longer around treat this as a
// failure as a navigation occurring during capture is bad.
auto* render_frame_host = content::RenderFrameHost::FromID(render_frame_id);
+
if (!render_frame_host ||
render_frame_host->GetEmbeddingToken().value_or(
base::UnguessableToken::Null()) != frame_guid ||
diff --git a/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc b/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc
index 2875e69fd91..be5dcad2cd1 100644
--- a/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc
+++ b/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc
@@ -137,7 +137,7 @@ class PaintPreviewClientRenderViewHostTest
void OverrideInterface(MockPaintPreviewRecorder* service) {
blink::AssociatedInterfaceProvider* remote_interfaces =
- web_contents()->GetMainFrame()->GetRemoteAssociatedInterfaces();
+ web_contents()->GetPrimaryMainFrame()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PaintPreviewRecorder::Name_,
base::BindRepeating(&MockPaintPreviewRecorder::BindRequest,
diff --git a/chromium/components/paint_preview/common/BUILD.gn b/chromium/components/paint_preview/common/BUILD.gn
index ed85de2f1a8..13abb8e769b 100644
--- a/chromium/components/paint_preview/common/BUILD.gn
+++ b/chromium/components/paint_preview/common/BUILD.gn
@@ -34,6 +34,7 @@ source_set("common") {
deps = [
"//base",
+ "//components/crash/core/common:crash_key_lib",
"//skia",
"//third_party:freetype_harfbuzz",
"//third_party/harfbuzz-ng:hb_scoped_util",
@@ -80,6 +81,7 @@ source_set("unit_tests") {
":test_utils",
"//base",
"//base/test:test_support",
+ "//components/crash/core/common:crash_key_lib",
"//skia",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/components/paint_preview/common/mojom/paint_preview_types_mojom_traits.cc b/chromium/components/paint_preview/common/mojom/paint_preview_types_mojom_traits.cc
index 297becc8621..1962cffed27 100644
--- a/chromium/components/paint_preview/common/mojom/paint_preview_types_mojom_traits.cc
+++ b/chromium/components/paint_preview/common/mojom/paint_preview_types_mojom_traits.cc
@@ -52,13 +52,13 @@ UnionTraits<paint_preview::mojom::SerializedRecordingDataView,
GetTag(const paint_preview::SerializedRecording& serialized_recording) {
switch (serialized_recording.persistence_) {
case paint_preview::RecordingPersistence::kFileSystem:
- return paint_preview::mojom::SerializedRecordingDataView::Tag::FILE;
+ return paint_preview::mojom::SerializedRecordingDataView::Tag::kFile;
case paint_preview::RecordingPersistence::kMemoryBuffer:
- return paint_preview::mojom::SerializedRecordingDataView::Tag::BUFFER;
+ return paint_preview::mojom::SerializedRecordingDataView::Tag::kBuffer;
}
NOTREACHED();
- return paint_preview::mojom::SerializedRecordingDataView::Tag::FILE;
+ return paint_preview::mojom::SerializedRecordingDataView::Tag::kFile;
}
} // namespace mojo
diff --git a/chromium/components/paint_preview/common/subset_font.cc b/chromium/components/paint_preview/common/subset_font.cc
index 43a448ec101..9e7cb724b00 100644
--- a/chromium/components/paint_preview/common/subset_font.cc
+++ b/chromium/components/paint_preview/common/subset_font.cc
@@ -16,6 +16,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/numerics/safe_conversions.h"
+#include "components/crash/core/common/crash_key.h"
#include "third_party/harfbuzz-ng/utils/hb_scoped.h"
#include "third_party/skia/include/core/SkStream.h"
#include "third_party/skia/include/core/SkTypeface.h"
@@ -63,6 +64,12 @@ void AddGlyphs(hb_set_t* glyph_id_set, uint16_t glyph_id) {
// Implementation based on SkPDFSubsetFont() using harfbuzz.
sk_sp<SkData> SubsetFont(SkTypeface* typeface, const GlyphUsage& usage) {
+ static crash_reporter::CrashKeyString<128> crash_key(
+ "PaintPreview-SubsetFont");
+ SkString family_name;
+ typeface->getFamilyName(&family_name);
+ crash_reporter::ScopedCrashKeyString auto_clear(&crash_key,
+ family_name.c_str());
int ttc_index = 0;
sk_sp<SkData> data = StreamToData(typeface->openStream(&ttc_index));
HbScoped<hb_face_t> face(hb_face_create(MakeBlob(data).get(), ttc_index));
diff --git a/chromium/components/paint_preview/player/android/BUILD.gn b/chromium/components/paint_preview/player/android/BUILD.gn
index ee9c7cac0e7..82b047b7d13 100644
--- a/chromium/components/paint_preview/player/android/BUILD.gn
+++ b/chromium/components/paint_preview/player/android/BUILD.gn
@@ -90,6 +90,9 @@ android_library("java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
+ "//chrome/browser/flags:java",
"//components/browser_ui/styles/android:java",
"//components/paint_preview/browser/android:java",
"//content/public/android:content_java",
@@ -115,6 +118,8 @@ android_library("player_java_test_support") {
":java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/paint_preview/browser/android:java",
"//components/signin/public/android:java",
"//components/signin/public/android:signin_java_test_support",
@@ -141,9 +146,9 @@ android_library("javatests") {
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/androidx:androidx_test_runner_java",
+ "//third_party/androidx:androidx_test_uiautomator_uiautomator_java",
"//third_party/hamcrest:hamcrest_java",
"//third_party/junit",
- "//third_party/ub-uiautomator:ub_uiautomator_java",
"//ui/android:ui_java_test_support",
"//url:gurl_java",
]
diff --git a/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java b/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
index 2b335aba4c3..40133c31fd2 100644
--- a/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
+++ b/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
@@ -8,14 +8,14 @@ import android.graphics.Rect;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.support.test.InstrumentationRegistry;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
import android.util.Size;
import android.view.View;
import android.view.ViewGroup;
import androidx.test.filters.MediumTest;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
import org.hamcrest.Matchers;
import org.junit.Assert;
diff --git a/chromium/components/paint_preview/player/player_compositor_delegate.cc b/chromium/components/paint_preview/player/player_compositor_delegate.cc
index 2d5bf16f704..2480d26aff8 100644
--- a/chromium/components/paint_preview/player/player_compositor_delegate.cc
+++ b/chromium/components/paint_preview/player/player_compositor_delegate.cc
@@ -124,7 +124,7 @@ void PlayerCompositorDelegate::Initialize(
const GURL& expected_url,
const DirectoryKey& key,
bool main_frame_mode,
- base::OnceCallback<void(int)> compositor_error,
+ CompositorErrorCallback compositor_error,
base::TimeDelta timeout_duration,
std::array<size_t, PressureLevelCount::kLevels> max_requests_map) {
TRACE_EVENT0("paint_preview", "PlayerCompositorDelegate::Initialize");
@@ -164,7 +164,7 @@ void PlayerCompositorDelegate::InitializeWithFakeServiceForTest(
const GURL& expected_url,
const DirectoryKey& key,
bool main_frame_mode,
- base::OnceCallback<void(int)> compositor_error,
+ CompositorErrorCallback compositor_error,
base::TimeDelta timeout_duration,
std::array<size_t, PressureLevelCount::kLevels> max_requests_map,
std::unique_ptr<PaintPreviewCompositorService, base::OnTaskRunnerDeleter>
@@ -184,7 +184,7 @@ void PlayerCompositorDelegate::InitializeInternal(
const GURL& expected_url,
const DirectoryKey& key,
bool main_frame_mode,
- base::OnceCallback<void(int)> compositor_error,
+ CompositorErrorCallback compositor_error,
base::TimeDelta timeout_duration,
std::array<size_t, PressureLevelCount::kLevels> max_requests_map) {
max_requests_map_ = max_requests_map;
diff --git a/chromium/components/paint_preview/player/player_compositor_delegate.h b/chromium/components/paint_preview/player/player_compositor_delegate.h
index 9ef3139f052..4af6fde855b 100644
--- a/chromium/components/paint_preview/player/player_compositor_delegate.h
+++ b/chromium/components/paint_preview/player/player_compositor_delegate.h
@@ -51,13 +51,16 @@ class PlayerCompositorDelegate {
PlayerCompositorDelegate(const PlayerCompositorDelegate&) = delete;
PlayerCompositorDelegate& operator=(const PlayerCompositorDelegate&) = delete;
+ // Callback used for compositor error
+ using CompositorErrorCallback = base::OnceCallback<void(int32_t)>;
+
// Initializes the compositor.
void Initialize(
PaintPreviewBaseService* paint_preview_service,
const GURL& url,
const DirectoryKey& key,
bool main_frame_mode,
- base::OnceCallback<void(int)> compositor_error,
+ CompositorErrorCallback compositor_error,
base::TimeDelta timeout_duration,
std::array<size_t, PressureLevelCount::kLevels> max_requests_map);
@@ -116,7 +119,7 @@ class PlayerCompositorDelegate {
const GURL& expected_url,
const DirectoryKey& key,
bool main_frame_mode,
- base::OnceCallback<void(int)> compositor_error,
+ CompositorErrorCallback compositor_error,
base::TimeDelta timeout_duration,
std::array<size_t, PressureLevelCount::kLevels> max_requests_map,
std::unique_ptr<PaintPreviewCompositorService, base::OnTaskRunnerDeleter>
@@ -131,7 +134,7 @@ class PlayerCompositorDelegate {
}
protected:
- base::OnceCallback<void(int)> compositor_error_;
+ CompositorErrorCallback compositor_error_;
virtual base::MemoryPressureMonitor* memory_pressure_monitor();
@@ -141,7 +144,7 @@ class PlayerCompositorDelegate {
const GURL& expected_url,
const DirectoryKey& key,
bool main_frame_mode,
- base::OnceCallback<void(int)> compositor_error,
+ CompositorErrorCallback compositor_error,
base::TimeDelta timeout_duration,
std::array<size_t, PressureLevelCount::kLevels> max_requests_map);
diff --git a/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc b/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc
index 8655a174e14..961cd83cb5f 100644
--- a/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc
+++ b/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc
@@ -203,7 +203,8 @@ PaintPreviewRecorderImpl::PaintPreviewRecorderImpl(
content::RenderFrame* render_frame)
: content::RenderFrameObserver(render_frame),
is_painting_preview_(false),
- is_main_frame_(render_frame->IsMainFrame()) {
+ is_main_frame_(render_frame->IsMainFrame() &&
+ !render_frame->IsInFencedFrameTree()) {
render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
base::BindRepeating(&PaintPreviewRecorderImpl::BindPaintPreviewRecorder,
weak_ptr_factory_.GetWeakPtr()));
diff --git a/chromium/components/password_manager/content/browser/BUILD.gn b/chromium/components/password_manager/content/browser/BUILD.gn
index ea71bed638a..0bf13e246de 100644
--- a/chromium/components/password_manager/content/browser/BUILD.gn
+++ b/chromium/components/password_manager/content/browser/BUILD.gn
@@ -33,6 +33,7 @@ static_library("browser") {
"//components/password_manager/core/browser",
"//components/password_manager/core/common",
"//components/prefs",
+ "//components/user_prefs",
"//content/public/browser",
"//content/public/common",
"//mojo/public/cpp/system",
@@ -56,6 +57,7 @@ source_set("unit_tests") {
"content_password_manager_driver_factory_unittest.cc",
"content_password_manager_driver_unittest.cc",
"form_submission_tracker_util_unittest.cc",
+ "password_change_success_tracker_factory_unittest.cc",
"password_manager_log_router_factory_unittest.cc",
]
deps = [
@@ -65,7 +67,9 @@ source_set("unit_tests") {
"//components/password_manager/core/browser:test_support",
"//components/prefs:test_support",
"//components/safe_browsing:buildflags",
+ "//components/ukm:test_support",
"//content/test:test_support",
+ "//services/metrics/public/cpp:metrics_cpp",
"//testing/gmock",
"//testing/gtest",
]
diff --git a/chromium/components/password_manager/content/browser/DEPS b/chromium/components/password_manager/content/browser/DEPS
index 47c9c9109ac..225871a78de 100644
--- a/chromium/components/password_manager/content/browser/DEPS
+++ b/chromium/components/password_manager/content/browser/DEPS
@@ -5,8 +5,11 @@ include_rules = [
"+components/keyed_service/content",
"+components/keyed_service/core",
"+components/safe_browsing/buildflags.h",
+ "+components/ukm",
+ "+components/user_prefs",
"+content/public/browser",
"+net",
+ "+services/metrics/public/cpp",
"+services/service_manager/public/cpp",
"+third_party/blink/public/common",
]
diff --git a/chromium/components/password_manager/content/browser/content_password_manager_driver.cc b/chromium/components/password_manager/content/browser/content_password_manager_driver.cc
index 557447a5be4..75854f62c22 100644
--- a/chromium/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/chromium/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -401,6 +401,7 @@ void ContentPasswordManagerDriver::ShowPasswordSuggestions(
TransformToRootCoordinates(render_frame_host_, bounds));
}
+#if BUILDFLAG(IS_ANDROID)
void ContentPasswordManagerDriver::ShowTouchToFill(
autofill::mojom::SubmissionReadinessState submission_readiness) {
if (!password_manager::bad_message::CheckFrameNotPrerendering(
@@ -408,6 +409,7 @@ void ContentPasswordManagerDriver::ShowTouchToFill(
return;
client_->ShowTouchToFill(this, submission_readiness);
}
+#endif
void ContentPasswordManagerDriver::CheckSafeBrowsingReputation(
const GURL& form_action,
diff --git a/chromium/components/password_manager/content/browser/content_password_manager_driver.h b/chromium/components/password_manager/content/browser/content_password_manager_driver.h
index 3e2f081bc1d..9a48e2b26b6 100644
--- a/chromium/components/password_manager/content/browser/content_password_manager_driver.h
+++ b/chromium/components/password_manager/content/browser/content_password_manager_driver.h
@@ -139,8 +139,10 @@ class ContentPasswordManagerDriver
const std::u16string& typed_username,
int options,
const gfx::RectF& bounds) override;
+#if BUILDFLAG(IS_ANDROID)
void ShowTouchToFill(
autofill::mojom::SubmissionReadinessState submission_readiness) override;
+#endif
void CheckSafeBrowsingReputation(const GURL& form_action,
const GURL& frame_url) override;
void FocusedInputChanged(
diff --git a/chromium/components/password_manager/content/browser/content_password_manager_driver_factory.cc b/chromium/components/password_manager/content/browser/content_password_manager_driver_factory.cc
index f3fb11060cf..163d7ae98b7 100644
--- a/chromium/components/password_manager/content/browser/content_password_manager_driver_factory.cc
+++ b/chromium/components/password_manager/content/browser/content_password_manager_driver_factory.cc
@@ -66,8 +66,10 @@ void ContentPasswordManagerDriverFactory::BindPasswordManagerDriver(
if (!factory)
return;
- factory->GetDriverForFrame(render_frame_host)
- ->BindPendingReceiver(std::move(pending_receiver));
+ // TODO(crbug.com/1294378): Remove nullptr check once
+ // EnablePasswordManagerWithinFencedFrame is launched.
+ if (auto* driver = factory->GetDriverForFrame(render_frame_host))
+ driver->BindPendingReceiver(std::move(pending_receiver));
}
ContentPasswordManagerDriver*
@@ -119,9 +121,10 @@ void ContentPasswordManagerDriverFactory::DidFinishNavigation(
password_client_->GetPasswordManager());
// A committed navigation always has a live RenderFrameHost.
CHECK(navigation->GetRenderFrameHost()->IsRenderFrameLive());
- GetDriverForFrame(navigation->GetRenderFrameHost())
- ->GetPasswordAutofillManager()
- ->DidNavigateMainFrame();
+ // TODO(crbug.com/1294378): Remove nullptr check once
+ // EnablePasswordManagerWithinFencedFrame is launched.
+ if (auto* driver = GetDriverForFrame(navigation->GetRenderFrameHost()))
+ driver->GetPasswordAutofillManager()->DidNavigateMainFrame();
}
void ContentPasswordManagerDriverFactory::RequestSendLoggingAvailability() {
diff --git a/chromium/components/password_manager/content/browser/content_password_manager_driver_unittest.cc b/chromium/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
index 9e58067ebdc..8ba8d72069c 100644
--- a/chromium/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
+++ b/chromium/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
@@ -184,7 +184,7 @@ MATCHER(WerePasswordsCleared, "Passwords not cleared") {
}
MATCHER_P(FormDataEqualTo, form_data, "") {
- return autofill::FormDataEqualForTesting(arg, form_data);
+ return autofill::FormData::DeepEqual(arg, form_data);
}
} // namespace
@@ -199,7 +199,7 @@ class ContentPasswordManagerDriverTest
.WillByDefault(Return(&log_manager_));
blink::AssociatedInterfaceProvider* remote_interfaces =
- web_contents()->GetMainFrame()->GetRemoteAssociatedInterfaces();
+ web_contents()->GetPrimaryMainFrame()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
autofill::mojom::PasswordAutofillAgent::Name_,
base::BindRepeating(&FakePasswordAutofillAgent::BindPendingReceiver,
@@ -286,13 +286,14 @@ TEST_F(ContentPasswordManagerDriverTest, SetFrameAndFormMetaDataOfForm) {
autofill::FormData form;
autofill::FormData form2 = GetFormWithFrameAndFormMetaData(main_rfh(), form);
- EXPECT_EQ(form2.host_frame,
- autofill::LocalFrameToken(
- web_contents()->GetMainFrame()->GetFrameToken().value()));
+ EXPECT_EQ(
+ form2.host_frame,
+ autofill::LocalFrameToken(
+ web_contents()->GetPrimaryMainFrame()->GetFrameToken().value()));
EXPECT_EQ(form2.url, GURL("https://hostname/path"));
EXPECT_EQ(form2.full_url, GURL("https://hostname/path?query#hash"));
EXPECT_EQ(form2.main_frame_origin,
- web_contents()->GetMainFrame()->GetLastCommittedOrigin());
+ web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
EXPECT_EQ(form2.main_frame_origin,
url::Origin::CreateFromNormalizedTuple("https", "hostname", 443));
}
@@ -321,7 +322,7 @@ class ContentPasswordManagerDriverURLTest
expected_form.main_frame_origin =
url::Origin::CreateFromNormalizedTuple("https", "hostname", 443);
expected_form.host_frame = autofill::LocalFrameToken(
- web_contents()->GetMainFrame()->GetFrameToken().value());
+ web_contents()->GetPrimaryMainFrame()->GetFrameToken().value());
return expected_form;
}
@@ -407,9 +408,10 @@ TEST_F(ContentPasswordManagerDriverFencedFramesTest,
// Navigate a fenced frame.
GURL fenced_frame_url = GURL("https://hostname/path?query#hash");
std::unique_ptr<content::NavigationSimulator> navigation_simulator =
- content::NavigationSimulator::CreateForFencedFrame(fenced_frame_url,
- fenced_frame_root);
+ content::NavigationSimulator::CreateRendererInitiated(fenced_frame_url,
+ fenced_frame_root);
navigation_simulator->Commit();
+ fenced_frame_root = navigation_simulator->GetFinalRenderFrameHost();
autofill::FormData initial_form;
autofill::FormData form_in_fenced_frame =
@@ -427,7 +429,7 @@ TEST_F(ContentPasswordManagerDriverFencedFramesTest,
EXPECT_EQ(form_in_fenced_frame.main_frame_origin,
fenced_frame_root->GetLastCommittedOrigin());
EXPECT_NE(form_in_fenced_frame.main_frame_origin,
- web_contents()->GetMainFrame()->GetLastCommittedOrigin());
+ web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
EXPECT_EQ(form_in_fenced_frame.main_frame_origin,
url::Origin::CreateFromNormalizedTuple("https", "hostname", 443));
}
diff --git a/chromium/components/password_manager/content/browser/password_change_success_tracker_factory.cc b/chromium/components/password_manager/content/browser/password_change_success_tracker_factory.cc
index 104c0a2a18e..63d83a2f5af 100644
--- a/chromium/components/password_manager/content/browser/password_change_success_tracker_factory.cc
+++ b/chromium/components/password_manager/content/browser/password_change_success_tracker_factory.cc
@@ -7,6 +7,7 @@
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/password_manager/core/browser/password_change_success_tracker_impl.h"
+#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
namespace password_manager {
@@ -36,7 +37,13 @@ PasswordChangeSuccessTrackerFactory::GetForBrowserContext(
KeyedService* PasswordChangeSuccessTrackerFactory::BuildServiceInstanceFor(
content::BrowserContext* browser_context) const {
- return new PasswordChangeSuccessTrackerImpl();
+ auto* tracker = new PasswordChangeSuccessTrackerImpl(
+ user_prefs::UserPrefs::Get(browser_context));
+ tracker->AddMetricsRecorder(
+ std::make_unique<PasswordChangeMetricsRecorderUma>());
+ tracker->AddMetricsRecorder(
+ std::make_unique<PasswordChangeMetricsRecorderUkm>());
+ return tracker;
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/content/browser/password_change_success_tracker_factory_unittest.cc b/chromium/components/password_manager/content/browser/password_change_success_tracker_factory_unittest.cc
new file mode 100644
index 00000000000..ff94b83fa33
--- /dev/null
+++ b/chromium/components/password_manager/content/browser/password_change_success_tracker_factory_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright 2022 The Chromium 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/password_manager/content/browser/password_change_success_tracker_factory.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/password_manager/core/browser/password_change_success_tracker.h"
+#include "components/password_manager/core/browser/password_change_success_tracker_impl.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using UkmEntry = ukm::builders::PasswordManager_PasswordChangeFlowDuration;
+
+namespace password_manager {
+
+namespace {
+
+constexpr char kUrl[] = "https://www.example.com";
+constexpr char kUsername[] = "Paul";
+
+} // namespace
+
+class PasswordChangeSuccessTrackerFactoryTest : public testing::Test {
+ public:
+ void SetUp() override {
+ BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(
+ browser_context());
+
+ // Set up a fake preference service and register it to user prefs.
+ pref_service_.registry()->RegisterIntegerPref(
+ prefs::kPasswordChangeSuccessTrackerVersion, 0);
+ pref_service_.registry()->RegisterListPref(
+ prefs::kPasswordChangeSuccessTrackerFlows);
+ user_prefs::UserPrefs::Set(browser_context(), &pref_service_);
+ }
+
+ void TearDown() override {
+ BrowserContextDependencyManager::GetInstance()
+ ->DestroyBrowserContextServices(browser_context());
+ }
+
+ protected:
+ content::TestBrowserContext* browser_context() { return &browser_context_; }
+
+ content::BrowserTaskEnvironment task_environment_;
+ content::TestBrowserContext browser_context_;
+ TestingPrefServiceSimple pref_service_;
+};
+
+TEST_F(PasswordChangeSuccessTrackerFactoryTest,
+ CheckThatMetricsRecorderUmaIsSetUp) {
+ base::HistogramTester histogram_tester;
+
+ PasswordChangeSuccessTracker* tracker =
+ PasswordChangeSuccessTrackerFactory::GetForBrowserContext(
+ browser_context());
+
+ tracker->OnChangePasswordFlowStarted(
+ GURL(kUrl), kUsername,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+
+ tracker->OnChangePasswordFlowCompleted(
+ GURL(kUrl), kUsername,
+ PasswordChangeSuccessTracker::EndEvent::kAutomatedFlowOwnPasswordChosen);
+
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakWarningDialog."
+ "AutomatedFlow",
+ 1);
+
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakWarningDialog."
+ "AutomatedFlow.AutomatedFlowPasswordChosen",
+ 1);
+}
+
+TEST_F(PasswordChangeSuccessTrackerFactoryTest,
+ CheckThatMetricsRecorderUkmIsSetUp) {
+ ukm::TestAutoSetUkmRecorder ukm_tester;
+
+ PasswordChangeSuccessTracker* tracker =
+ PasswordChangeSuccessTrackerFactory::GetForBrowserContext(
+ browser_context());
+
+ tracker->OnChangePasswordFlowStarted(
+ GURL(kUrl), kUsername,
+ PasswordChangeSuccessTracker::StartEvent::kManualHomepageFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings);
+
+ tracker->OnChangePasswordFlowCompleted(
+ GURL(kUrl), kUsername,
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowGeneratedPasswordChosen);
+
+ // Check that UKM logging is correct.
+ const auto& entries = ukm_tester.GetEntriesByName(UkmEntry::kEntryName);
+ EXPECT_EQ(1u, entries.size());
+ for (const auto* entry : entries) {
+ EXPECT_EQ(entry->source_id, ukm::NoURLSourceId());
+ ukm_tester.ExpectEntryMetric(
+ entry, UkmEntry::kStartEventName,
+ static_cast<int64_t>(
+ PasswordChangeSuccessTracker::StartEvent::kManualHomepageFlow));
+ ukm_tester.ExpectEntryMetric(
+ entry, UkmEntry::kEndEventName,
+ static_cast<int64_t>(PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowGeneratedPasswordChosen));
+ ukm_tester.ExpectEntryMetric(
+ entry, UkmEntry::kEntryPointName,
+ static_cast<int64_t>(
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings));
+ }
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/BUILD.gn b/chromium/components/password_manager/core/browser/BUILD.gn
index b138dad9902..b8daef28eca 100644
--- a/chromium/components/password_manager/core/browser/BUILD.gn
+++ b/chromium/components/password_manager/core/browser/BUILD.gn
@@ -166,6 +166,8 @@ static_library("browser") {
"password_manager_metrics_recorder.h",
"password_manager_metrics_util.cc",
"password_manager_metrics_util.h",
+ "password_manager_setting.h",
+ "password_manager_settings_service.h",
"password_manager_util.cc",
"password_manager_util.h",
"password_notes_table.cc",
@@ -244,6 +246,8 @@ static_library("browser") {
"ui/bulk_leak_check_service_adapter.cc",
"ui/bulk_leak_check_service_adapter.h",
"ui/credential_provider_interface.h",
+ "ui/credential_ui_entry.cc",
+ "ui/credential_ui_entry.h",
"ui/credential_utils.h",
"ui/export_flow.h",
"ui/export_progress_status.h",
@@ -276,11 +280,11 @@ static_library("browser") {
]
deps = [
":affiliation",
+ ":affiliation_proto",
":csv",
":hash_password_manager",
":password_generator",
":password_hash_data",
- ":proto",
"//base",
"//base:i18n",
"//build:chromeos_buildflags",
@@ -333,27 +337,28 @@ static_library("browser") {
sources += [
"built_in_backend_to_android_backend_migrator.cc",
"built_in_backend_to_android_backend_migrator.h",
- "capabilities_service.h",
- "capabilities_service_impl.cc",
- "capabilities_service_impl.h",
- "password_scripts_fetcher.h",
- "password_scripts_fetcher_impl.cc",
- "password_scripts_fetcher_impl.h",
"password_store_backend_migration_decorator.cc",
"password_store_backend_migration_decorator.h",
"password_store_proxy_backend.cc",
"password_store_proxy_backend.h",
- "saved_passwords_capabilities_fetcher.cc",
- "saved_passwords_capabilities_fetcher.h",
]
- deps += [ "//components/autofill_assistant/browser/public" ]
+ deps += [ ":unified_password_manager_proto" ]
}
if (!is_ios) {
sources += [
+ "capabilities_service.h",
+ "capabilities_service_impl.cc",
+ "capabilities_service_impl.h",
"http_credentials_cleaner.cc",
"http_credentials_cleaner.h",
+ "password_scripts_fetcher.h",
+ "password_scripts_fetcher_impl.cc",
+ "password_scripts_fetcher_impl.h",
+ "saved_passwords_capabilities_fetcher.cc",
+ "saved_passwords_capabilities_fetcher.h",
]
+ deps += [ "//components/autofill_assistant/browser/public" ]
}
if (!is_android && !is_ios) {
@@ -411,6 +416,7 @@ static_library("csv") {
deps = [
":affiliation",
+ "//components/password_manager/core/browser/form_parsing",
"//url",
]
}
@@ -470,11 +476,12 @@ if (is_android) {
"android_backend_error.h",
"manage_passwords_referrer.h",
"password_manager_metrics_util.h",
+ "password_manager_setting.h",
]
}
}
-fuzzable_proto_library("proto") {
+fuzzable_proto_library("affiliation_proto") {
sources = [ "android_affiliation/affiliation_api.proto" ]
}
@@ -533,6 +540,8 @@ static_library("test_support") {
"mock_password_feature_manager.h",
"mock_password_form_manager_for_ui.cc",
"mock_password_form_manager_for_ui.h",
+ "mock_password_manager_settings_service.cc",
+ "mock_password_manager_settings_service.h",
"mock_password_reuse_manager.cc",
"mock_password_reuse_manager.h",
"mock_password_store_backend.cc",
@@ -688,6 +697,7 @@ source_set("unit_tests") {
"password_account_storage_settings_watcher_unittest.cc",
"password_autofill_manager_unittest.cc",
"password_bubble_experiment_unittest.cc",
+ "password_change_success_tracker_impl_unittest.cc",
"password_feature_manager_impl_unittest.cc",
"password_form_filling_unittest.cc",
"password_form_manager_unittest.cc",
@@ -700,6 +710,7 @@ source_set("unit_tests") {
"password_manager_client_helper_unittest.cc",
"password_manager_features_util_unittest.cc",
"password_manager_metrics_recorder_unittest.cc",
+ "password_manager_metrics_util_unittest.cc",
"password_manager_unittest.cc",
"password_manager_util_unittest.cc",
"password_notes_table_unittest.cc",
@@ -736,18 +747,20 @@ source_set("unit_tests") {
if (is_android) {
sources += [
"built_in_backend_to_android_backend_migrator_unittest.cc",
- "capabilities_service_impl_unittest.cc",
- "password_scripts_fetcher_impl_unittests.cc",
"password_store_backend_metrics_recorder_unittest.cc",
"password_store_backend_migration_decorator_unittest.cc",
"password_store_proxy_backend_unittest.cc",
- "saved_passwords_capabilities_fetcher_unittest.cc",
]
}
if (is_ios) {
sources += [ "login_database_ios_unittest.cc" ]
} else {
- sources += [ "http_credentials_cleaner_unittest.cc" ]
+ sources += [
+ "capabilities_service_impl_unittest.cc",
+ "http_credentials_cleaner_unittest.cc",
+ "password_scripts_fetcher_impl_unittest.cc",
+ "saved_passwords_capabilities_fetcher_unittest.cc",
+ ]
}
if (is_win || is_mac || is_linux || is_chromeos) {
@@ -782,7 +795,7 @@ source_set("unit_tests") {
"//components/favicon/core/test:test_support",
"//components/os_crypt",
"//components/os_crypt:test_support",
- "//components/password_manager/core/browser:proto",
+ "//components/password_manager/core/browser:affiliation_proto",
"//components/password_manager/core/browser/form_parsing:form_parsing",
"//components/password_manager/core/browser/form_parsing:unit_tests",
"//components/password_manager/core/browser/form_parsing/fuzzer:unit_tests",
@@ -819,7 +832,7 @@ source_set("unit_tests") {
"//url",
]
- if (is_android) {
+ if (!is_ios) {
deps += [
"//components/autofill_assistant/browser/public:public",
"//components/autofill_assistant/browser/public:unit_test_support",
@@ -844,8 +857,8 @@ if (use_libfuzzer) {
sources =
[ "android_affiliation/lookup_affiliation_response_parser_fuzzer.cc" ]
deps = [
+ ":affiliation_proto",
":browser",
- ":proto",
"//base:base",
"//base:i18n",
"//third_party/libprotobuf-mutator",
@@ -893,3 +906,25 @@ source_set("affiliation_unittests") {
"//url",
]
}
+
+if (is_android) {
+ import("//build/config/android/rules.gni")
+
+ fuzzable_proto_library("unified_password_manager_proto") {
+ sources = [
+ "protos/list_passwords_result.proto",
+ "protos/password_with_local_data.proto",
+ ]
+ deps = [ "//components/sync/protocol" ]
+ proto_in_dir = "//"
+ }
+
+ proto_java_library("unified_password_manager_proto_java") {
+ proto_path = "//"
+ sources = [
+ "protos/list_passwords_result.proto",
+ "protos/password_with_local_data.proto",
+ ]
+ deps = [ "//components/sync/protocol:protocol_java" ]
+ }
+}
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc
index aa2d429c6b4..3eb6e20c5cd 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc
@@ -8,10 +8,10 @@
#include <ostream>
#include "base/base64.h"
+#include "base/strings/escape.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "components/url_formatter/elide_url.h"
-#include "net/base/escape.h"
#include "url/third_party/mozilla/url_parse.h"
#include "url/url_canon_stdstring.h"
@@ -93,7 +93,8 @@ bool CanonicalizeHashComponent(const base::StringPiece& input_hash,
// safe" base64 alphabet; plus the padding ('=').
const char kBase64NonAlphanumericChars[] = "-_=";
- std::string base64_encoded_hash = net::UnescapeBinaryURLComponent(input_hash);
+ std::string base64_encoded_hash =
+ base::UnescapeBinaryURLComponent(input_hash);
if (!base64_encoded_hash.empty() &&
CanonicalizeBase64Padding(&base64_encoded_hash) &&
@@ -117,7 +118,7 @@ bool CanonicalizePackageNameComponent(
const char kPackageNameNonAlphanumericChars[] = "._";
std::string package_name =
- net::UnescapeBinaryURLComponent(input_package_name);
+ base::UnescapeBinaryURLComponent(input_package_name);
// TODO(engedy): We might want to use a regex to check this more throughly.
if (!package_name.empty() &&
@@ -174,9 +175,9 @@ bool ParseAndCanonicalizeFacetURI(const std::string& input_uri,
url::ParseStandardURL(input_uri.c_str(), input_uri.size(), &input_parsed);
base::StringPiece scheme = ComponentString(input_uri, input_parsed.scheme);
- if (base::LowerCaseEqualsASCII(scheme, url::kHttpsScheme)) {
+ if (base::EqualsCaseInsensitiveASCII(scheme, url::kHttpsScheme)) {
return CanonicalizeWebFacetURI(input_uri, input_parsed, canonical_uri);
- } else if (base::LowerCaseEqualsASCII(scheme, kAndroidAppScheme)) {
+ } else if (base::EqualsCaseInsensitiveASCII(scheme, kAndroidAppScheme)) {
return CanonicalizeAndroidFacetURI(input_uri, input_parsed, canonical_uri);
}
return false;
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.cc b/chromium/components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.cc
index 9211a0920e7..840c44a9b51 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.cc
@@ -4,6 +4,8 @@
#include "components/password_manager/core/browser/android_affiliation/lookup_affiliation_response_parser.h"
+#include "base/containers/flat_set.h"
+
namespace password_manager {
namespace {
@@ -16,6 +18,8 @@ bool ParseFacets(const std::vector<FacetURI>& requested_facet_uris,
const MessageT& response,
std::vector<std::vector<Facet>>& result) {
std::map<FacetURI, size_t> facet_uri_to_class_index;
+ base::flat_set<FacetURI> requested_facets(requested_facet_uris);
+
for (const auto& equivalence_class : response) {
std::vector<Facet> facets;
facets.reserve(equivalence_class.facet().size());
@@ -44,8 +48,12 @@ bool ParseFacets(const std::vector<FacetURI>& requested_facet_uris,
// Ignore equivalence classes that are duplicates of earlier ones. However,
// fail in the case of a partial overlap, which violates the invariant that
- // affiliations must form an equivalence relation.
+ // affiliations must form an equivalence relation. Also check, if the class
+ // was requested.
+ bool is_class_requested = false;
for (const Facet& facet : facets) {
+ if (requested_facets.count(facet.uri))
+ is_class_requested = true;
if (!facet_uri_to_class_index.count(facet.uri))
facet_uri_to_class_index[facet.uri] = result.size();
if (facet_uri_to_class_index[facet.uri] !=
@@ -54,8 +62,9 @@ bool ParseFacets(const std::vector<FacetURI>& requested_facet_uris,
}
}
- // Filter out duplicate equivalence classes in the response.
- if (facet_uri_to_class_index[facets[0].uri] == result.size()) {
+ // Filter out duplicate or nonrequested equivalence classes in the response.
+ if (is_class_requested &&
+ facet_uri_to_class_index[facets[0].uri] == result.size()) {
result.push_back(std::move(facets));
}
}
diff --git a/chromium/components/password_manager/core/browser/android_backend_error.h b/chromium/components/password_manager/core/browser/android_backend_error.h
index 85783f07b4a..3227ae5e10f 100644
--- a/chromium/components/password_manager/core/browser/android_backend_error.h
+++ b/chromium/components/password_manager/core/browser/android_backend_error.h
@@ -26,7 +26,9 @@ enum class AndroidBackendErrorType {
kGMSVersionNotSupported = 6,
// API was successfully called, but returned an error.
kExternalError = 7,
- kMaxValue = kExternalError,
+ // Task was cleaned-up without a proper response.
+ kCleanedUpWithoutResponse = 8,
+ kMaxValue = kCleanedUpWithoutResponse,
};
struct AndroidBackendError {
diff --git a/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc b/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
index e61fb0efccd..2c61439d3ce 100644
--- a/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
+++ b/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
@@ -3,16 +3,15 @@
// found in the LICENSE file.
#include "components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.h"
-#include "base/memory/raw_ptr.h"
#include "base/barrier_callback.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/containers/flat_set.h"
-#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
-#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
+#include "components/password_manager/core/browser/password_form.h"
+#include "components/password_manager/core/browser/password_store_backend.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_service.h"
@@ -32,6 +31,11 @@ bool IsMigrationNeeded(PrefService* prefs) {
prefs::kCurrentMigrationVersionToGoogleMobileServices);
}
+bool IsBlacklistedFormWithValues(const PasswordForm& form) {
+ return form.blocked_by_user &&
+ (!form.username_value.empty() || !form.password_value.empty());
+}
+
} // namespace
struct BuiltInBackendToAndroidBackendMigrator::IsPasswordLess {
@@ -160,13 +164,21 @@ void BuiltInBackendToAndroidBackendMigrator::PrepareForMigration() {
if (sync_delegate_->IsSyncingPasswordsEnabled()) {
// Sync is enabled. Migrate non-syncable data from the built-in backend
// to android backend.
- built_in_backend_->GetAllLoginsAsync(base::BindOnce(
+ // During the migration username and password values are also cleaned up
+ // from the blacklisted entries stored in the built in backend.
+ auto callback_chain = base::BindOnce(
&BuiltInBackendToAndroidBackendMigrator::MigrateNonSyncableData,
- weak_ptr_factory_.GetWeakPtr(), android_backend_));
+ weak_ptr_factory_.GetWeakPtr(), android_backend_);
+ callback_chain = base::BindOnce(&BuiltInBackendToAndroidBackendMigrator::
+ RemoveBlacklistedFormsWithValues,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(built_in_backend_),
+ std::move(callback_chain));
+ built_in_backend_->GetAllLoginsAsync(std::move(callback_chain));
return;
- } else if (prefs_->GetBoolean(
- prefs::kRequiresMigrationAfterSyncStatusChange) &&
- !features::ManagesLocalPasswordsInUnifiedPasswordManager()) {
+ }
+ if (prefs_->GetBoolean(prefs::kRequiresMigrationAfterSyncStatusChange) &&
+ !features::ManagesLocalPasswordsInUnifiedPasswordManager()) {
// Sync was disabled, while the local GMS storage is not supported.
// Migrate non-syncable data that is associated with a previously
// synced account from the android backend to the built-in backend.
@@ -179,23 +191,7 @@ void BuiltInBackendToAndroidBackendMigrator::PrepareForMigration() {
}
}
- auto barrier_callback = base::BarrierCallback<BackendAndLoginsResults>(
- 2, base::BindOnce(&BuiltInBackendToAndroidBackendMigrator::
- MigratePasswordsBetweenAndroidAndBuiltInBackends,
- weak_ptr_factory_.GetWeakPtr()));
-
- auto bind_backend_to_logins = [](PasswordStoreBackend* backend,
- LoginsResultOrError result) {
- return BackendAndLoginsResults(backend, std::move(result));
- };
-
- built_in_backend_->GetAllLoginsAsync(
- base::BindOnce(bind_backend_to_logins,
- base::Unretained(built_in_backend_))
- .Then(barrier_callback));
- android_backend_->GetAllLoginsAsync(
- base::BindOnce(bind_backend_to_logins, base::Unretained(android_backend_))
- .Then(barrier_callback));
+ RunRollingMigration();
}
void BuiltInBackendToAndroidBackendMigrator::MigrateNonSyncableData(
@@ -232,6 +228,45 @@ void BuiltInBackendToAndroidBackendMigrator::MigrateNonSyncableData(
std::move(callbacks_chain).Run();
}
+void BuiltInBackendToAndroidBackendMigrator::RunRollingMigration() {
+ auto barrier_callback = base::BarrierCallback<BackendAndLoginsResults>(
+ 2, base::BindOnce(&BuiltInBackendToAndroidBackendMigrator::
+ MigratePasswordsBetweenAndroidAndBuiltInBackends,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ auto bind_backend_to_logins = [](PasswordStoreBackend* backend,
+ LoginsResultOrError result) {
+ return BackendAndLoginsResults(backend, std::move(result));
+ };
+
+ auto builtin_backend_callback_chain =
+ base::BindOnce(bind_backend_to_logins,
+ base::Unretained(built_in_backend_))
+ .Then(barrier_callback);
+
+ // Cleanup blacklisted forms in the built in backend before binding.
+ builtin_backend_callback_chain = base::BindOnce(
+ &BuiltInBackendToAndroidBackendMigrator::RemoveBlacklistedFormsWithValues,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(built_in_backend_),
+ std::move(builtin_backend_callback_chain));
+
+ built_in_backend_->GetAllLoginsAsync(
+ std::move(builtin_backend_callback_chain));
+
+ auto android_backend_callback_chain =
+ base::BindOnce(bind_backend_to_logins, base::Unretained(android_backend_))
+ .Then(barrier_callback);
+
+ // Cleanup blacklisted forms in the android backend before binding.
+ android_backend_callback_chain = base::BindOnce(
+ &BuiltInBackendToAndroidBackendMigrator::RemoveBlacklistedFormsWithValues,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(android_backend_),
+ std::move(android_backend_callback_chain));
+
+ android_backend_->GetAllLoginsAsync(
+ std::move(android_backend_callback_chain));
+}
+
void BuiltInBackendToAndroidBackendMigrator::
MigratePasswordsBetweenAndroidAndBuiltInBackends(
std::vector<BackendAndLoginsResults> results) {
@@ -451,13 +486,22 @@ void BuiltInBackendToAndroidBackendMigrator::RemoveLoginFromBackend(
void BuiltInBackendToAndroidBackendMigrator::RunCallbackOrAbortMigration(
base::OnceClosure callback,
- absl::optional<PasswordStoreChangeList> changelist) {
- if (!changelist.has_value() || !changelist.value().empty()) {
+ PasswordChangesOrError changes_or_error) {
+ PasswordChanges* changes = absl::get_if<PasswordChanges>(&changes_or_error);
+ if (absl::holds_alternative<PasswordStoreBackendError>(changes_or_error)) {
+ MigrationFinished(/*is_success=*/false);
+ return;
+ }
+
+ // Nullopt changelist is returned on success by the backends that do not
+ // provide exact changelist (e.g. Android). This indicates success operation
+ // as well as non-empty changelist.
+ if (!changes->has_value() || !changes->value().empty()) {
// The step was successful, continue the migration.
std::move(callback).Run();
return;
}
- // Migration failed.
+ // Migration failed (changelist is present but empty).
MigrationFinished(/*is_success=*/false);
}
@@ -471,14 +515,38 @@ void BuiltInBackendToAndroidBackendMigrator::MigrationFinished(
}
bool BuiltInBackendToAndroidBackendMigrator::ShouldMigrateNonSyncableData() {
- // 1. Check that feature and prefs state allow migration.
+ // 1. Check that pref state allows migration.
// 2. Check that the user either needs migration due to a sync setting change,
// or because sync is enabled and the user needs initial migration of
// non-syncable data (e.g. after enrolling into the experiment).
- return features::RequiresMigrationForUnifiedPasswordManager() &&
- IsMigrationNeeded(prefs_) &&
+ return IsMigrationNeeded(prefs_) &&
(prefs_->GetBoolean(prefs::kRequiresMigrationAfterSyncStatusChange) ||
sync_delegate_->IsSyncingPasswordsEnabled());
}
+void BuiltInBackendToAndroidBackendMigrator::RemoveBlacklistedFormsWithValues(
+ PasswordStoreBackend* backend,
+ LoginsOrErrorReply result_callback,
+ LoginsResultOrError logins_or_error) {
+ if (absl::holds_alternative<PasswordStoreBackendError>(logins_or_error)) {
+ std::move(result_callback).Run(std::move(logins_or_error));
+ return;
+ }
+
+ auto& forms = absl::get<LoginsResult>(logins_or_error);
+
+ LoginsResult clean_forms;
+ clean_forms.reserve(forms.size());
+
+ for (std::unique_ptr<PasswordForm>& form : forms) {
+ if (IsBlacklistedFormWithValues(*form)) {
+ RemoveLoginFromBackend(backend, *form, base::DoNothing());
+ continue;
+ }
+ clean_forms.push_back(std::move(form));
+ }
+
+ std::move(result_callback).Run(std::move(clean_forms));
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.h b/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.h
index 4864e9fb358..4e097cedd3d 100644
--- a/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.h
+++ b/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.h
@@ -57,6 +57,11 @@ class BuiltInBackendToAndroidBackendMigrator {
void MigrateNonSyncableData(PasswordStoreBackend* target_backend,
LoginsResultOrError logins_or_error);
+ // Performs the rolling migration that synchronises entries between
+ // |built_in_backend_| and |android_backend_| to keep them in consistent
+ // state. Calls |MigratePasswordsBetweenAndroidAndBuiltInBackends| internally.
+ void RunRollingMigration();
+
// Migrates password between |built_in_backend_| and |android_backend_|.
// |result| consists of passwords from the |built_in_backend_| let's call them
// |A| and passwords from the |android_backend_| - |B|. If initial migration
@@ -94,9 +99,8 @@ class BuiltInBackendToAndroidBackendMigrator {
// If |changelist| is an empty changelist, migration is aborted by calling
// MigrationFinished() indicating the migration is *not* successful.
// Otherwise, |callback| is invoked.
- void RunCallbackOrAbortMigration(
- base::OnceClosure callback,
- absl::optional<PasswordStoreChangeList> changelist);
+ void RunCallbackOrAbortMigration(base::OnceClosure callback,
+ PasswordChangesOrError changelist);
// Reports metrics and deletes |metrics_reporter_|
void MigrationFinished(bool is_success);
@@ -105,6 +109,15 @@ class BuiltInBackendToAndroidBackendMigrator {
// migration.
bool ShouldMigrateNonSyncableData();
+ // Removes blocklisted forms with non-empty |username_value| or
+ // |password_value| from |backend|.
+ // |result_callback| is called with the |LoginsResult| containing valid forms
+ // only or |PasswordStoreBackendError| if it contained in |logins_or_error|.
+ // |logins_or_error| is modified in place.
+ void RemoveBlacklistedFormsWithValues(PasswordStoreBackend* backend,
+ LoginsOrErrorReply result_callback,
+ LoginsResultOrError logins_or_error);
+
const raw_ptr<PasswordStoreBackend> built_in_backend_;
const raw_ptr<PasswordStoreBackend> android_backend_;
diff --git a/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc b/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
index 519abecce4f..f58b225821f 100644
--- a/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
+++ b/chromium/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
@@ -14,6 +14,7 @@
#include "components/password_manager/core/browser/fake_password_store_backend.h"
#include "components/password_manager/core/browser/mock_password_store_backend.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
+#include "components/password_manager/core/browser/password_store_backend.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_registry.h"
@@ -26,9 +27,11 @@
using ::testing::Eq;
using ::testing::Invoke;
+using ::testing::IsEmpty;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::UnorderedElementsAreArray;
+using ::testing::VariantWith;
using ::testing::WithArg;
namespace password_manager {
@@ -324,6 +327,81 @@ TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
RunUntilIdle();
}
+// Tests that migration removes blocklisted entries with non-empty username or
+// values from the built in backlend before writing to the Android backend.
+TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
+ MigrationClearsBlocklistedCredentials) {
+ feature_list().InitAndEnableFeatureWithParameters(
+ /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+ {{"migration_version", "1"}, {"stage", "0"}});
+
+ Init();
+ EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
+ .WillRepeatedly(Return(true));
+
+ // Add two incorrect entries to the local database to check if they will be
+ // removed before writing to the android backend
+ PasswordForm form_1 = CreateTestPasswordForm(1);
+ form_1.blocked_by_user = true;
+ form_1.username_value.clear();
+ built_in_backend().AddLoginAsync(form_1, base::DoNothing());
+
+ PasswordForm form_2 = CreateTestPasswordForm(2);
+ form_2.blocked_by_user = true;
+ form_1.password_value.clear();
+ built_in_backend().AddLoginAsync(form_2, base::DoNothing());
+
+ migrator()->StartMigrationIfNecessary();
+ RunUntilIdle();
+
+ base::MockCallback<LoginsOrErrorReply> mock_reply;
+ // Credentials should be cleaned in both android and built in backends.
+ EXPECT_CALL(mock_reply, Run(VariantWith<LoginsResult>((IsEmpty())))).Times(2);
+ android_backend().GetAllLoginsAsync(mock_reply.Get());
+ built_in_backend().GetAllLoginsAsync(mock_reply.Get());
+ RunUntilIdle();
+}
+
+// Tests that migration does not affect username and password for
+// non-blocklisted entries.
+TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
+ MigrationDoesNotClearNonBlocklistedCredentials) {
+ feature_list().InitAndEnableFeatureWithParameters(
+ /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+ {{"migration_version", "1"}, {"stage", "0"}});
+
+ Init();
+ EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
+ .WillRepeatedly(Return(true));
+
+ // Add two incorrect entries to the local database to check if they will be
+ // fixed before writing to the android backend
+ PasswordForm form_1 = CreateTestPasswordForm(1);
+ built_in_backend().AddLoginAsync(form_1, base::DoNothing());
+
+ PasswordForm form_2 = CreateTestPasswordForm(2);
+ built_in_backend().AddLoginAsync(form_2, base::DoNothing());
+
+ // Add one form to be updated.
+ android_backend().AddLoginAsync(form_1, base::DoNothing());
+ RunUntilIdle();
+
+ migrator()->StartMigrationIfNecessary();
+ RunUntilIdle();
+
+ base::MockCallback<LoginsOrErrorReply> mock_reply;
+ std::vector<std::unique_ptr<PasswordForm>> expected_logins;
+ expected_logins.push_back(std::make_unique<PasswordForm>(form_1));
+ expected_logins.push_back(std::make_unique<PasswordForm>(form_2));
+
+ // Credentials should be cleaned in both android and built in backends.
+ EXPECT_CALL(mock_reply, Run(LoginsResultsOrErrorAre(&expected_logins)))
+ .Times(2);
+ android_backend().GetAllLoginsAsync(mock_reply.Get());
+ built_in_backend().GetAllLoginsAsync(mock_reply.Get());
+ RunUntilIdle();
+}
+
// Holds the built in and android backend's logins and the expected result after
// the migration.
struct MigrationParam {
@@ -672,14 +750,14 @@ TEST_F(BuiltInBackendToAndroidBackendMigratorWithMockAndroidBackendTest,
built_in_backend().AddLoginAsync(CreateTestPasswordForm(/*index=*/2),
base::DoNothing());
- // Simulate an Android backend that fails to write by returning an empty
- // changelist.
+ // Simulate an Android backend that fails to write.
ON_CALL(android_backend_, UpdateLoginAsync)
.WillByDefault(
- WithArg<1>(Invoke([](PasswordStoreChangeListReply callback) -> void {
+ WithArg<1>(Invoke([](PasswordChangesOrErrorReply callback) -> void {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
- base::BindOnce(std::move(callback), PasswordStoreChangeList()));
+ base::BindOnce(std::move(callback),
+ PasswordStoreBackendError::kUnspecified));
})));
// Once one UpdateLoginAsync() call fails, all consecutive ones will not be
@@ -719,14 +797,14 @@ TEST_F(BuiltInBackendToAndroidBackendMigratorWithMockAndroidBackendTest,
FROM_HERE, base::BindOnce(std::move(reply), LoginsResult()));
})));
- // Simulate an Android backend that fails to write by returning an empty
- // changelist.
+ // Simulate an Android backend that fails to write.
ON_CALL(android_backend_, AddLoginAsync)
.WillByDefault(
- WithArg<1>(Invoke([](PasswordStoreChangeListReply callback) -> void {
+ WithArg<1>(Invoke([](PasswordChangesOrErrorReply callback) -> void {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
- base::BindOnce(std::move(callback), PasswordStoreChangeList()));
+ base::BindOnce(std::move(callback),
+ PasswordStoreBackendError::kUnspecified));
})));
// Once one AddLoginAsync() call fails, all consecutive ones will not be
diff --git a/chromium/components/password_manager/core/browser/credential_manager_impl.cc b/chromium/components/password_manager/core/browser/credential_manager_impl.cc
index 3edcffb238b..58f8a496ae9 100644
--- a/chromium/components/password_manager/core/browser/credential_manager_impl.cc
+++ b/chromium/components/password_manager/core/browser/credential_manager_impl.cc
@@ -163,9 +163,7 @@ void CredentialManagerImpl::Get(CredentialMediationRequirement mediation,
}
bool CredentialManagerImpl::IsZeroClickAllowed() const {
- return password_manager_util::IsAutoSignInEnabled(
- client_->GetPrefs(), client_->GetSyncService()) &&
- !client_->IsIncognito();
+ return client_->IsAutoSignInEnabled() && !client_->IsIncognito();
}
PasswordFormDigest CredentialManagerImpl::GetSynthesizedFormForOrigin() const {
diff --git a/chromium/components/password_manager/core/browser/credential_manager_impl_unittest.cc b/chromium/components/password_manager/core/browser/credential_manager_impl_unittest.cc
index 9047065d6b8..a1dbd3c5093 100644
--- a/chromium/components/password_manager/core/browser/credential_manager_impl_unittest.cc
+++ b/chromium/components/password_manager/core/browser/credential_manager_impl_unittest.cc
@@ -86,8 +86,6 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
account_store_(account_store),
password_manager_(this) {
prefs_ = std::make_unique<TestingPrefServiceSimple>();
- prefs_->registry()->RegisterBooleanPref(prefs::kCredentialsEnableAutosignin,
- true);
prefs_->registry()->RegisterBooleanPref(
prefs::kWasAutoSignInFirstRunExperienceShown, true);
prefs_->registry()->RegisterBooleanPref(
@@ -104,6 +102,8 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
delete;
~MockPasswordManagerClient() override = default;
+ bool IsAutoSignInEnabled() const override { return auto_sign_in_enabled_; }
+
bool PromptUserToSaveOrUpdatePassword(
std::unique_ptr<PasswordFormManagerForUI> manager,
bool update_password) override {
@@ -161,7 +161,7 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
PasswordFormManagerForUI* pending_manager() const { return manager_.get(); }
void set_zero_click_enabled(bool zero_click_enabled) {
- prefs_->SetBoolean(prefs::kCredentialsEnableAutosignin, zero_click_enabled);
+ auto_sign_in_enabled_ = zero_click_enabled;
}
void set_first_run_seen(bool first_run_seen) {
@@ -180,6 +180,7 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
std::unique_ptr<PasswordFormManagerForUI> manager_;
PasswordManager password_manager_;
GURL last_committed_url_{kTestWebOrigin};
+ bool auto_sign_in_enabled_ = true;
};
// Callbacks from CredentialManagerImpl methods
diff --git a/chromium/components/password_manager/core/browser/export/password_csv_writer.cc b/chromium/components/password_manager/core/browser/export/password_csv_writer.cc
index 9f878ec6466..c21c551e5ec 100644
--- a/chromium/components/password_manager/core/browser/export/password_csv_writer.cc
+++ b/chromium/components/password_manager/core/browser/export/password_csv_writer.cc
@@ -6,7 +6,7 @@
#include "base/strings/utf_string_conversions.h"
#include "components/password_manager/core/browser/export/csv_writer.h"
-#include "components/password_manager/core/browser/password_form.h"
+#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
namespace password_manager {
@@ -21,7 +21,7 @@ const char kPasswordColumnName[] = "password";
// static
std::string PasswordCSVWriter::SerializePasswords(
- const std::vector<std::unique_ptr<PasswordForm>>& passwords) {
+ const std::vector<CredentialUIEntry>& credentials) {
std::vector<std::string> header(4);
header[0] = kTitleColumnName;
header[1] = kUrlColumnName;
@@ -29,9 +29,9 @@ std::string PasswordCSVWriter::SerializePasswords(
header[3] = kPasswordColumnName;
std::vector<std::map<std::string, std::string>> records;
- records.reserve(passwords.size());
- for (const auto& password : passwords) {
- records.push_back(PasswordFormToRecord(*password));
+ records.reserve(credentials.size());
+ for (const auto& credential : credentials) {
+ records.push_back(PasswordFormToRecord(credential));
}
std::string result;
@@ -40,12 +40,12 @@ std::string PasswordCSVWriter::SerializePasswords(
}
std::map<std::string, std::string> PasswordCSVWriter::PasswordFormToRecord(
- const PasswordForm& form) {
+ const CredentialUIEntry& credential) {
std::map<std::string, std::string> record;
- record[kUrlColumnName] = form.url.spec();
- record[kUsernameColumnName] = base::UTF16ToUTF8(form.username_value);
- record[kPasswordColumnName] = base::UTF16ToUTF8(form.password_value);
- record[kTitleColumnName] = form.url.host();
+ record[kUrlColumnName] = credential.url.spec();
+ record[kUsernameColumnName] = base::UTF16ToUTF8(credential.username);
+ record[kPasswordColumnName] = base::UTF16ToUTF8(credential.password);
+ record[kTitleColumnName] = credential.url.host();
return record;
}
diff --git a/chromium/components/password_manager/core/browser/export/password_csv_writer.h b/chromium/components/password_manager/core/browser/export/password_csv_writer.h
index ed930586918..6f8f4318a13 100644
--- a/chromium/components/password_manager/core/browser/export/password_csv_writer.h
+++ b/chromium/components/password_manager/core/browser/export/password_csv_writer.h
@@ -12,7 +12,7 @@
namespace password_manager {
-struct PasswordForm;
+struct CredentialUIEntry;
// Static-only class bundling together the API for serializing passwords into
// CSV format.
@@ -22,16 +22,17 @@ class PasswordCSVWriter {
PasswordCSVWriter(const PasswordCSVWriter&) = delete;
PasswordCSVWriter& operator=(const PasswordCSVWriter&) = delete;
- // Creates a CSV representation of the forms stored in |password|. Note that
- // this loses all the metadata except for the origin, username and password.
+ // Creates a CSV representation of the credential stored in |credentials|.
+ // Note that this loses all the metadata except for the origin, username and
+ // password.
static std::string SerializePasswords(
- const std::vector<std::unique_ptr<PasswordForm>>& passwords);
+ const std::vector<CredentialUIEntry>& credentials);
private:
- // Converts |form| into a single line in the CSV format. Metadata are lost,
- // see SerializePasswords.
+ // Converts |credential| into a single line in the CSV format. Metadata are
+ // lost, see SerializePasswords.
static std::map<std::string, std::string> PasswordFormToRecord(
- const PasswordForm& form);
+ const CredentialUIEntry& credential);
};
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/export/password_csv_writer_unittest.cc b/chromium/components/password_manager/core/browser/export/password_csv_writer_unittest.cc
index d8126336499..6a07ad37b7f 100644
--- a/chromium/components/password_manager/core/browser/export/password_csv_writer_unittest.cc
+++ b/chromium/components/password_manager/core/browser/export/password_csv_writer_unittest.cc
@@ -10,7 +10,7 @@
#include "base/strings/utf_string_conversions.h"
#include "components/password_manager/core/browser/import/csv_password.h"
#include "components/password_manager/core/browser/import/csv_password_sequence.h"
-#include "components/password_manager/core/browser/password_form.h"
+#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -23,58 +23,58 @@ namespace {
MATCHER_P3(FormHasOriginUsernamePassword, origin, username, password, "") {
return arg.signon_realm == origin && arg.url == GURL(origin) &&
- arg.username_value == base::UTF8ToUTF16(username) &&
- arg.password_value == base::UTF8ToUTF16(password);
+ arg.username == base::UTF8ToUTF16(username) &&
+ arg.password == base::UTF8ToUTF16(password);
}
} // namespace
TEST(PasswordCSVWriterTest, SerializePasswords_ZeroPasswords) {
- std::vector<std::unique_ptr<PasswordForm>> passwords;
+ std::vector<CredentialUIEntry> credentials;
- CSVPasswordSequence seq(PasswordCSVWriter::SerializePasswords(passwords));
+ CSVPasswordSequence seq(PasswordCSVWriter::SerializePasswords(credentials));
ASSERT_EQ(CSVPassword::Status::kOK, seq.result());
EXPECT_EQ(seq.begin(), seq.end());
}
TEST(PasswordCSVWriterTest, SerializePasswords_SinglePassword) {
- std::vector<std::unique_ptr<PasswordForm>> passwords;
+ std::vector<CredentialUIEntry> credentials;
PasswordForm form;
form.url = GURL("http://example.com");
form.username_value = u"Someone";
form.password_value = u"Secret";
- passwords.push_back(std::make_unique<PasswordForm>(form));
+ credentials.emplace_back(form);
- CSVPasswordSequence seq(PasswordCSVWriter::SerializePasswords(passwords));
+ CSVPasswordSequence seq(PasswordCSVWriter::SerializePasswords(credentials));
ASSERT_EQ(CSVPassword::Status::kOK, seq.result());
- std::vector<PasswordForm> pwds;
+ std::vector<CredentialUIEntry> pwds;
for (const auto& pwd : seq) {
- pwds.push_back(pwd.ParseValid());
+ pwds.emplace_back(pwd.ToPasswordForm());
}
EXPECT_THAT(pwds, ElementsAre(FormHasOriginUsernamePassword(
"http://example.com/", "Someone", "Secret")));
}
TEST(PasswordCSVWriterTest, SerializePasswords_TwoPasswords) {
- std::vector<std::unique_ptr<PasswordForm>> passwords;
+ std::vector<CredentialUIEntry> credentials;
PasswordForm form;
form.url = GURL("http://example.com");
form.username_value = u"Someone";
form.password_value = u"Secret";
- passwords.push_back(std::make_unique<PasswordForm>(form));
+ credentials.emplace_back(form);
form.url = GURL("http://other.org");
form.username_value = u"Anyone";
form.password_value = u"None";
- passwords.push_back(std::make_unique<PasswordForm>(form));
+ credentials.emplace_back(form);
- CSVPasswordSequence seq(PasswordCSVWriter::SerializePasswords(passwords));
+ CSVPasswordSequence seq(PasswordCSVWriter::SerializePasswords(credentials));
ASSERT_EQ(CSVPassword::Status::kOK, seq.result());
- std::vector<PasswordForm> pwds;
+ std::vector<CredentialUIEntry> pwds;
for (const auto& pwd : seq) {
- pwds.push_back(pwd.ParseValid());
+ pwds.emplace_back(pwd.ToPasswordForm());
}
EXPECT_THAT(pwds, ElementsAre(FormHasOriginUsernamePassword(
"http://example.com/", "Someone", "Secret"),
diff --git a/chromium/components/password_manager/core/browser/export/password_manager_exporter.cc b/chromium/components/password_manager/core/browser/export/password_manager_exporter.cc
index ec452c44f7e..331db1865fc 100644
--- a/chromium/components/password_manager/core/browser/export/password_manager_exporter.cc
+++ b/chromium/components/password_manager/core/browser/export/password_manager_exporter.cc
@@ -15,10 +15,9 @@
#include "base/task/task_runner_util.h"
#include "build/build_config.h"
#include "components/password_manager/core/browser/export/password_csv_writer.h"
-#include "components/password_manager/core/browser/password_form.h"
-#include "components/password_manager/core/browser/password_list_sorter.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/password_manager/core/browser/ui/credential_provider_interface.h"
+#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
+#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
namespace password_manager {
@@ -58,25 +57,12 @@ bool DefaultDeleteFunction(const base::FilePath& file) {
return base::DeleteFile(file);
}
-std::vector<std::unique_ptr<PasswordForm>> DeduplicatePasswordsAcrossStores(
- std::vector<std::unique_ptr<PasswordForm>> passwords) {
- auto get_sort_key = [](const auto& password) {
- return CreateSortKey(*password, IgnoreStore(true));
- };
- auto cmp = [&](const auto& lhs, const auto& rhs) {
- return get_sort_key(lhs) < get_sort_key(rhs);
- };
- base::flat_set<std::unique_ptr<PasswordForm>, decltype(cmp)> unique_passwords(
- std::move(passwords), cmp);
- return std::move(unique_passwords).extract();
-}
-
} // namespace
PasswordManagerExporter::PasswordManagerExporter(
- CredentialProviderInterface* credential_provider_interface,
+ SavedPasswordsPresenter* presenter,
ProgressCallback on_progress)
- : credential_provider_interface_(credential_provider_interface),
+ : presenter_(presenter),
on_progress_(std::move(on_progress)),
last_progress_status_(ExportProgressStatus::NOT_STARTED),
write_function_(base::BindRepeating(&DefaultWriteFunction)),
@@ -96,22 +82,18 @@ PasswordManagerExporter::~PasswordManagerExporter() = default;
void PasswordManagerExporter::PreparePasswordsForExport() {
DCHECK_EQ(GetProgressStatus(), ExportProgressStatus::NOT_STARTED);
- std::vector<std::unique_ptr<PasswordForm>> password_list =
- credential_provider_interface_->GetAllPasswords();
-
- // Deduplicate passwords that are present in multiple stores, so the output
- // file doesn't contain repeated data.
- std::vector<std::unique_ptr<PasswordForm>> deduplicated_password_list =
- DeduplicatePasswordsAcrossStores(std::move(password_list));
+ std::vector<CredentialUIEntry> credentials =
+ presenter_->GetSavedCredentials();
+ // Clear blocked credentials.
+ base::EraseIf(credentials, [](const auto& credential) {
+ return credential.blocked_by_user;
+ });
- size_t deduplicated_password_list_size = deduplicated_password_list.size();
base::PostTaskAndReplyWithResult(
task_runner_.get(), FROM_HERE,
- base::BindOnce(&PasswordCSVWriter::SerializePasswords,
- std::move(deduplicated_password_list)),
+ base::BindOnce(&PasswordCSVWriter::SerializePasswords, credentials),
base::BindOnce(&PasswordManagerExporter::SetSerialisedPasswordList,
- weak_factory_.GetWeakPtr(),
- deduplicated_password_list_size));
+ weak_factory_.GetWeakPtr(), credentials.size()));
}
void PasswordManagerExporter::SetDestination(
diff --git a/chromium/components/password_manager/core/browser/export/password_manager_exporter.h b/chromium/components/password_manager/core/browser/export/password_manager_exporter.h
index d8007dc3ea3..28c430e3f3b 100644
--- a/chromium/components/password_manager/core/browser/export/password_manager_exporter.h
+++ b/chromium/components/password_manager/core/browser/export/password_manager_exporter.h
@@ -17,7 +17,7 @@
namespace password_manager {
-class CredentialProviderInterface;
+class SavedPasswordsPresenter;
// Controls the exporting of passwords. One instance per export flow.
// PasswordManagerExporter will perform the export asynchronously as soon as all
@@ -33,9 +33,8 @@ class PasswordManagerExporter {
using SetPosixFilePermissionsCallback =
base::RepeatingCallback<bool(const base::FilePath&, int)>;
- explicit PasswordManagerExporter(
- CredentialProviderInterface* credential_provider_interface,
- ProgressCallback on_progress);
+ explicit PasswordManagerExporter(SavedPasswordsPresenter* presenter,
+ ProgressCallback on_progress);
PasswordManagerExporter(const PasswordManagerExporter&) = delete;
PasswordManagerExporter& operator=(const PasswordManagerExporter&) = delete;
@@ -96,7 +95,7 @@ class PasswordManagerExporter {
void Cleanup();
// The source of the password list which will be exported.
- const raw_ptr<CredentialProviderInterface> credential_provider_interface_;
+ const raw_ptr<SavedPasswordsPresenter> presenter_;
// Callback to the UI.
ProgressCallback on_progress_;
diff --git a/chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc b/chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc
index c2e610604ce..2c1fe8c922b 100644
--- a/chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc
+++ b/chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc
@@ -15,10 +15,11 @@
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "components/password_manager/core/browser/export/password_csv_writer.h"
-#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/password_manager/core/browser/ui/credential_provider_interface.h"
+#include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
#include "components/password_manager/core/browser/ui/export_progress_status.h"
+#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -50,68 +51,50 @@ const base::FilePath::CharType kNullFileName[] = FILE_PATH_LITERAL("/nul");
const base::FilePath::CharType kNullFileName[] = FILE_PATH_LITERAL("/dev/null");
#endif
-// Provides a predetermined set of credentials
-class FakeCredentialProvider : public CredentialProviderInterface {
- public:
- FakeCredentialProvider() = default;
-
- FakeCredentialProvider(const FakeCredentialProvider&) = delete;
- FakeCredentialProvider& operator=(const FakeCredentialProvider&) = delete;
-
- void SetPasswordList(
- const std::vector<std::unique_ptr<PasswordForm>>& password_list) {
- password_list_.clear();
- for (const auto& form : password_list) {
- password_list_.push_back(std::make_unique<PasswordForm>(*form));
- }
- }
-
- // CredentialProviderInterface:
- std::vector<std::unique_ptr<PasswordForm>> GetAllPasswords() override {
- std::vector<std::unique_ptr<PasswordForm>> ret_val;
- for (const auto& form : password_list_) {
- ret_val.push_back(std::make_unique<PasswordForm>(*form));
- }
- return ret_val;
- }
-
- private:
- std::vector<std::unique_ptr<PasswordForm>> password_list_;
-};
-
// Creates a hardcoded set of credentials for tests.
-std::vector<std::unique_ptr<PasswordForm>> CreatePasswordList() {
- auto password_form = std::make_unique<PasswordForm>();
- password_form->url = GURL("http://accounts.google.com/a/LoginAuth");
- password_form->username_value = u"test@gmail.com";
- password_form->password_value = u"test1";
-
- std::vector<std::unique_ptr<PasswordForm>> password_forms;
- password_forms.push_back(std::move(password_form));
- return password_forms;
+PasswordForm CreateTestPassword() {
+ PasswordForm password_form;
+ password_form.url = GURL("http://accounts.google.com/a/LoginAuth");
+ password_form.username_value = u"test@gmail.com";
+ password_form.password_value = u"test1";
+ password_form.in_store = PasswordForm::Store::kProfileStore;
+ return password_form;
}
class PasswordManagerExporterTest : public testing::Test {
public:
PasswordManagerExporterTest()
: task_environment_(base::test::TaskEnvironment::MainThreadType::UI),
- exporter_(&fake_credential_provider_, mock_on_progress_.Get()),
+ exporter_(&presenter_, mock_on_progress_.Get()),
destination_path_(kNullFileName) {
exporter_.SetWriteForTesting(mock_write_file_.Get());
exporter_.SetDeleteForTesting(mock_delete_file_.Get());
exporter_.SetSetPosixFilePermissionsForTesting(
mock_set_posix_file_permissions_.Get());
+ store_->Init(/*prefs=*/nullptr, /*affiliated_match_helper=*/nullptr);
}
PasswordManagerExporterTest(const PasswordManagerExporterTest&) = delete;
PasswordManagerExporterTest& operator=(const PasswordManagerExporterTest&) =
delete;
- ~PasswordManagerExporterTest() override = default;
+ ~PasswordManagerExporterTest() override {
+ store_->ShutdownOnUIThread();
+ task_environment_.RunUntilIdle();
+ }
+
+ void SetPasswordList(const std::vector<PasswordForm>& forms) {
+ for (const auto& form : forms) {
+ store_->AddLogin(form);
+ }
+ task_environment_.RunUntilIdle();
+ }
protected:
base::test::TaskEnvironment task_environment_;
- FakeCredentialProvider fake_credential_provider_;
+ scoped_refptr<TestPasswordStore> store_ =
+ base::MakeRefCounted<TestPasswordStore>();
+ SavedPasswordsPresenter presenter_{store_};
base::MockCallback<
base::RepeatingCallback<void(ExportProgressStatus, const std::string&)>>
mock_on_progress_;
@@ -125,11 +108,10 @@ class PasswordManagerExporterTest : public testing::Test {
};
TEST_F(PasswordManagerExporterTest, PasswordExportSetPasswordListFirst) {
- std::vector<std::unique_ptr<PasswordForm>> password_list =
- CreatePasswordList();
- fake_credential_provider_.SetPasswordList(password_list);
+ PasswordForm form = CreateTestPassword();
+ SetPasswordList({form});
const std::string serialised(
- PasswordCSVWriter::SerializePasswords(password_list));
+ PasswordCSVWriter::SerializePasswords({CredentialUIEntry(form)}));
EXPECT_CALL(mock_write_file_, Run(destination_path_, StrEq(serialised)))
.WillOnce(Return(true));
@@ -147,7 +129,7 @@ TEST_F(PasswordManagerExporterTest, PasswordExportSetPasswordListFirst) {
// When writing fails, we should notify the UI of the failure and try to cleanup
// a possibly partial passwords file.
TEST_F(PasswordManagerExporterTest, WriteFileFailed) {
- fake_credential_provider_.SetPasswordList(CreatePasswordList());
+ SetPasswordList({CreateTestPassword()});
const std::string destination_folder_name(
destination_path_.DirName().BaseName().AsUTF8Unsafe());
@@ -167,11 +149,10 @@ TEST_F(PasswordManagerExporterTest, WriteFileFailed) {
// Test that GetProgressStatus() returns the last ExportProgressStatus sent
// to the callback.
TEST_F(PasswordManagerExporterTest, GetProgressReturnsLastCallbackStatus) {
- std::vector<std::unique_ptr<PasswordForm>> password_list =
- CreatePasswordList();
- fake_credential_provider_.SetPasswordList(password_list);
+ PasswordForm form = CreateTestPassword();
+ SetPasswordList({form});
const std::string serialised(
- PasswordCSVWriter::SerializePasswords(password_list));
+ PasswordCSVWriter::SerializePasswords({CredentialUIEntry(form)}));
const std::string destination_folder_name(
destination_path_.DirName().BaseName().AsUTF8Unsafe());
@@ -191,7 +172,7 @@ TEST_F(PasswordManagerExporterTest, GetProgressReturnsLastCallbackStatus) {
}
TEST_F(PasswordManagerExporterTest, DontExportWithOnlyDestination) {
- fake_credential_provider_.SetPasswordList(CreatePasswordList());
+ SetPasswordList({CreateTestPassword()});
EXPECT_CALL(mock_write_file_, Run(_, _)).Times(0);
EXPECT_CALL(mock_on_progress_,
@@ -203,7 +184,7 @@ TEST_F(PasswordManagerExporterTest, DontExportWithOnlyDestination) {
}
TEST_F(PasswordManagerExporterTest, CancelAfterPasswords) {
- fake_credential_provider_.SetPasswordList(CreatePasswordList());
+ SetPasswordList({CreateTestPassword()});
EXPECT_CALL(mock_write_file_, Run(_, _)).Times(0);
EXPECT_CALL(mock_on_progress_,
@@ -216,7 +197,7 @@ TEST_F(PasswordManagerExporterTest, CancelAfterPasswords) {
}
TEST_F(PasswordManagerExporterTest, CancelWhileExporting) {
- fake_credential_provider_.SetPasswordList(CreatePasswordList());
+ SetPasswordList({CreateTestPassword()});
EXPECT_CALL(mock_write_file_, Run(_, _)).Times(0);
EXPECT_CALL(mock_delete_file_, Run(destination_path_));
@@ -235,7 +216,7 @@ TEST_F(PasswordManagerExporterTest, CancelWhileExporting) {
// The "Cancel" button may still be visible on the UI after we've completed
// exporting. If they choose to cancel, we should clear the file.
TEST_F(PasswordManagerExporterTest, CancelAfterExporting) {
- fake_credential_provider_.SetPasswordList(CreatePasswordList());
+ SetPasswordList({CreateTestPassword()});
EXPECT_CALL(mock_write_file_, Run(_, _)).WillOnce(Return(true));
EXPECT_CALL(mock_delete_file_, Run(destination_path_));
@@ -259,7 +240,7 @@ TEST_F(PasswordManagerExporterTest, CancelAfterExporting) {
// Chrome creates files using the broadest permissions allowed. Passwords are
// sensitive and should be explicitly limited to the owner.
TEST_F(PasswordManagerExporterTest, OutputHasRestrictedPermissions) {
- fake_credential_provider_.SetPasswordList(CreatePasswordList());
+ SetPasswordList({CreateTestPassword()});
EXPECT_CALL(mock_write_file_, Run(_, _)).WillOnce(Return(true));
EXPECT_CALL(mock_set_posix_file_permissions_, Run(destination_path_, 0600))
@@ -274,21 +255,18 @@ TEST_F(PasswordManagerExporterTest, OutputHasRestrictedPermissions) {
#endif
TEST_F(PasswordManagerExporterTest, DeduplicatesAcrossPasswordStores) {
- auto password = std::make_unique<PasswordForm>();
- password->in_store = PasswordForm::Store::kProfileStore;
- password->url = GURL("http://g.com/auth");
- password->username_value = u"user";
- password->password_value = u"password";
+ PasswordForm password;
+ password.in_store = PasswordForm::Store::kProfileStore;
+ password.url = GURL("http://g.com/auth");
+ password.username_value = u"user";
+ password.password_value = u"password";
- auto password_duplicate = std::make_unique<PasswordForm>(*password);
- password_duplicate->in_store = PasswordForm::Store::kAccountStore;
+ PasswordForm password_duplicate = password;
+ password_duplicate.in_store = PasswordForm::Store::kAccountStore;
- std::vector<std::unique_ptr<PasswordForm>> password_list;
- password_list.push_back(std::move(password));
const std::string single_password_serialised(
- PasswordCSVWriter::SerializePasswords(password_list));
- password_list.push_back(std::move(password_duplicate));
- fake_credential_provider_.SetPasswordList(password_list);
+ PasswordCSVWriter::SerializePasswords({CredentialUIEntry(password)}));
+ SetPasswordList({password, password_duplicate});
// The content written to the file should be the same as what would be
// computed before the duplicated password was added.
diff --git a/chromium/components/password_manager/core/browser/fake_password_store_backend.cc b/chromium/components/password_manager/core/browser/fake_password_store_backend.cc
index f8e62903dae..540bcb0aa65 100644
--- a/chromium/components/password_manager/core/browser/fake_password_store_backend.cc
+++ b/chromium/components/password_manager/core/browser/fake_password_store_backend.cc
@@ -70,7 +70,7 @@ void FakePasswordStoreBackend::GetAllLoginsForAccountAsync(
}
void FakePasswordStoreBackend::FillMatchingLoginsAsync(
- LoginsReply callback,
+ LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) {
base::SequencedTaskRunnerHandle::Get()->PostTaskAndReplyWithResult(
@@ -82,7 +82,7 @@ void FakePasswordStoreBackend::FillMatchingLoginsAsync(
void FakePasswordStoreBackend::AddLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
base::SequencedTaskRunnerHandle::Get()->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&FakePasswordStoreBackend::AddLoginInternal,
@@ -92,7 +92,7 @@ void FakePasswordStoreBackend::AddLoginAsync(
void FakePasswordStoreBackend::UpdateLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
base::SequencedTaskRunnerHandle::Get()->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&FakePasswordStoreBackend::UpdateLoginInternal,
@@ -102,7 +102,7 @@ void FakePasswordStoreBackend::UpdateLoginAsync(
void FakePasswordStoreBackend::RemoveLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
base::SequencedTaskRunnerHandle::Get()->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&FakePasswordStoreBackend::RemoveLoginInternal,
@@ -115,14 +115,14 @@ void FakePasswordStoreBackend::RemoveLoginsByURLAndTimeAsync(
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
NOTIMPLEMENTED();
}
void FakePasswordStoreBackend::RemoveLoginsCreatedBetweenAsync(
base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
NOTIMPLEMENTED();
}
diff --git a/chromium/components/password_manager/core/browser/fake_password_store_backend.h b/chromium/components/password_manager/core/browser/fake_password_store_backend.h
index c65a2bad1e5..945368feeaa 100644
--- a/chromium/components/password_manager/core/browser/fake_password_store_backend.h
+++ b/chromium/components/password_manager/core/browser/fake_password_store_backend.h
@@ -53,25 +53,25 @@ class FakePasswordStoreBackend : public PasswordStoreBackend {
void GetAllLoginsForAccountAsync(absl::optional<std::string> account,
LoginsOrErrorReply callback) override;
void FillMatchingLoginsAsync(
- LoginsReply callback,
+ LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) override;
void AddLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void UpdateLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginsByURLAndTimeAsync(
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginsCreatedBetweenAsync(
base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void DisableAutoSignInForOriginsAsync(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::OnceClosure completion) override;
diff --git a/chromium/components/password_manager/core/browser/form_parsing/form_parser.cc b/chromium/components/password_manager/core/browser/form_parsing/form_parser.cc
index bdddb297254..e3d58f365e1 100644
--- a/chromium/components/password_manager/core/browser/form_parsing/form_parser.cc
+++ b/chromium/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -64,16 +64,18 @@ AutocompleteFlag ExtractAutocompleteFlag(const std::string& attribute) {
return AutocompleteFlag::kNone;
const base::StringPiece& field_type = tokens.back();
- if (base::LowerCaseEqualsASCII(field_type, kAutocompleteUsername))
+ if (base::EqualsCaseInsensitiveASCII(field_type, kAutocompleteUsername))
return AutocompleteFlag::kUsername;
- if (base::LowerCaseEqualsASCII(field_type, kAutocompleteCurrentPassword))
+ if (base::EqualsCaseInsensitiveASCII(field_type,
+ kAutocompleteCurrentPassword))
return AutocompleteFlag::kCurrentPassword;
- if (base::LowerCaseEqualsASCII(field_type, kAutocompleteNewPassword))
+ if (base::EqualsCaseInsensitiveASCII(field_type, kAutocompleteNewPassword))
return AutocompleteFlag::kNewPassword;
- if (base::LowerCaseEqualsASCII(field_type, kAutocompleteWebAuthn))
+ if (base::EqualsCaseInsensitiveASCII(field_type, kAutocompleteWebAuthn))
return AutocompleteFlag::kWebAuthn;
- if (base::LowerCaseEqualsASCII(field_type, kAutocompleteOneTimePassword) ||
+ if (base::EqualsCaseInsensitiveASCII(field_type,
+ kAutocompleteOneTimePassword) ||
base::StartsWith(field_type, kAutocompleteCreditCardPrefix,
base::CompareCase::SENSITIVE)) {
return AutocompleteFlag::kNonPassword;
diff --git a/chromium/components/password_manager/core/browser/form_saver_impl.cc b/chromium/components/password_manager/core/browser/form_saver_impl.cc
index afcb055c4f8..73bd11bf4f4 100644
--- a/chromium/components/password_manager/core/browser/form_saver_impl.cc
+++ b/chromium/components/password_manager/core/browser/form_saver_impl.cc
@@ -12,8 +12,6 @@
#include "base/time/time.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_store_interface.h"
-#include "google_apis/gaia/gaia_auth_util.h"
-#include "google_apis/gaia/gaia_urls.h"
#include "url/gurl.h"
#include "url/origin.h"
diff --git a/chromium/components/password_manager/core/browser/import/csv_password.cc b/chromium/components/password_manager/core/browser/import/csv_password.cc
index bff7a80de8b..67510bab53b 100644
--- a/chromium/components/password_manager/core/browser/import/csv_password.cc
+++ b/chromium/components/password_manager/core/browser/import/csv_password.cc
@@ -11,7 +11,9 @@
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
#include "components/password_manager/core/browser/import/csv_field_parser.h"
+
#include "url/gurl.h"
namespace password_manager {
@@ -29,85 +31,72 @@ std::u16string Convert(base::StringPiece str) {
} // namespace
-CSVPassword::CSVPassword(const ColumnMap& map, base::StringPiece csv_row)
- : map_(map), row_(csv_row) {}
-
-CSVPassword::~CSVPassword() = default;
-
-CSVPassword::Status CSVPassword::Parse(PasswordForm* form) const {
- DCHECK(form) << "Null target PasswordForm. Use TryParse() if the resulting "
- "PasswordForm is not needed.";
- return ParseImpl(form);
-}
-
-CSVPassword::Status CSVPassword::TryParse() const {
- return ParseImpl(nullptr);
-}
-
-PasswordForm CSVPassword::ParseValid() const {
- PasswordForm result;
- Status status = ParseImpl(&result);
- DCHECK_EQ(Status::kOK, status);
- return result;
-}
-
-CSVPassword::Status CSVPassword::ParseImpl(PasswordForm* form) const {
- // |map_| must be an (1) injective and (2) surjective (3) partial map. (3) is
- // enforced by its type, (2) is checked later in the code and (1) follows from
- // (2) and the following size() check.
- if (map_.size() != kLabelCount)
- return Status::kSemanticError;
+CSVPassword::CSVPassword(const ColumnMap& map, base::StringPiece row) {
+ if (map.size() != kLabelCount) {
+ status_ = Status::kSemanticError;
+ return;
+ }
size_t field_idx = 0;
- CSVFieldParser parser(row_);
- GURL origin;
- base::StringPiece username;
- base::StringPiece password;
+ CSVFieldParser parser(row);
bool username_set = false;
+ status_ = Status::kOK;
+
while (parser.HasMoreFields()) {
base::StringPiece field;
- if (!parser.NextField(&field))
- return Status::kSyntaxError;
- auto meaning_it = map_.find(field_idx++);
- if (meaning_it == map_.end())
+ if (!parser.NextField(&field)) {
+ status_ = Status::kSyntaxError;
+ return;
+ }
+ auto meaning_it = map.find(field_idx++);
+ if (meaning_it == map.end())
continue;
switch (meaning_it->second) {
case Label::kOrigin:
- if (!base::IsStringASCII(field))
- return Status::kSyntaxError;
- origin = GURL(field);
+ if (!base::IsStringASCII(field)) {
+ status_ = Status::kSyntaxError;
+ return;
+ }
+ url_ = GURL(field);
break;
case Label::kUsername:
- username = field;
+ username_ = field;
username_set = true;
break;
case Label::kPassword:
- password = field;
+ password_ = field;
break;
}
}
- // While all of origin, username and password must be set in the CSV data row,
- // username is permitted to be an empty string, while password and origin are
- // not.
- if (!origin.is_valid() || !username_set || password.empty())
- return Status::kSemanticError;
- if (!form)
- return Status::kOK;
+ // While all of origin, username and password must be set in the CSV data
+ // row, username is permitted to be an empty string, while password and
+ // origin are not.
+ if (!url_.is_valid() || !username_set || password_.empty()) {
+ status_ = Status::kSemanticError;
+ }
+}
+
+CSVPassword::Status CSVPassword::GetParseStatus() const {
+ return status_;
+}
+
+PasswordForm CSVPassword::ToPasswordForm() const {
+ // Only valid PasswordForms are allowed to be created.
+ DCHECK_EQ(this->GetParseStatus(), Status::kOK);
// There is currently no way to import non-HTML credentials.
- form->scheme = PasswordForm::Scheme::kHtml;
- // GURL::GetOrigin() returns an empty GURL for Android credentials due
- // to the non-standard scheme ("android://"). Hence the following
- // explicit check is necessary to set |signon_realm| correctly for both
- // regular and Android credentials.
- form->signon_realm = IsValidAndroidFacetURI(origin.spec())
- ? origin.spec()
- : origin.DeprecatedGetOriginAsURL().spec();
- form->url = std::move(origin);
- form->username_value = Convert(username);
- form->password_value = Convert(password);
- form->date_created = base::Time::Now();
- form->date_password_modified = form->date_created;
- return Status::kOK;
+ PasswordForm form;
+ form.scheme = PasswordForm::Scheme::kHtml;
+ // Android credentials have a non-standard scheme ("android://"). Hence the
+ // following explicit check is necessary to set |signon_realm| correctly for
+ // both regular and Android credentials.
+ form.signon_realm =
+ IsValidAndroidFacetURI(url_.spec()) ? url_.spec() : GetSignonRealm(url_);
+ form.url = url_;
+ form.username_value = Convert(username_);
+ form.password_value = Convert(password_);
+ form.date_created = base::Time::Now();
+ form.date_password_modified = form.date_created;
+ return form;
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/import/csv_password.h b/chromium/components/password_manager/core/browser/import/csv_password.h
index 186d8153bdc..e536680e1df 100644
--- a/chromium/components/password_manager/core/browser/import/csv_password.h
+++ b/chromium/components/password_manager/core/browser/import/csv_password.h
@@ -32,31 +32,20 @@ class CSVPassword {
CSVPassword(CSVPassword&&) = delete;
CSVPassword& operator=(const CSVPassword&) = delete;
CSVPassword& operator=(CSVPassword&&) = delete;
- ~CSVPassword();
- // Returns whether the associated CSV row can be parsed successfully. If
- // returning success, it also stores the parsed result in |*form|.
- Status Parse(PasswordForm* form) const;
- // TryParse() returns the same value as Parse(). However, TryParse() does not
- // attempt to create and store the corresponding PasswordForm anywhere.
- // Therefore TryParse() is faster than Parse() and a better choice for only
- // checking a correctness of a CSV serialization of a credential.
- Status TryParse() const;
- // Convenience wrapper around Parse() for cases known to be correctly
- // parseable.
- PasswordForm ParseValid() const;
+ // Returns the status of the parse.
+ Status GetParseStatus() const;
- private:
- // ParseImpl is the common base of Parse() and TryParse().
- Status ParseImpl(PasswordForm* form) const;
+ // Returns PasswordForm populated with parsed data, if initial parsing
+ // completed successfully.
+ PasswordForm ToPasswordForm() const;
- // The members |map_| and |row_| are only modified in constructor or
- // operator=().
+ private:
+ GURL url_;
+ base::StringPiece username_;
+ base::StringPiece password_;
- // |map_| stores the meaning of particular columns in the row.
- const ColumnMap& map_;
- // |row_| contains the CSV row from which the PasswordForm is parsed.
- base::StringPiece row_;
+ Status status_;
};
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/import/csv_password_iterator.cc b/chromium/components/password_manager/core/browser/import/csv_password_iterator.cc
index 9ad5593ad6e..30394365b2c 100644
--- a/chromium/components/password_manager/core/browser/import/csv_password_iterator.cc
+++ b/chromium/components/password_manager/core/browser/import/csv_password_iterator.cc
@@ -83,7 +83,8 @@ void CSVPasswordIterator::SeekToNextValidRow() {
// Skip over empty lines, and
(csv_row_.empty() && !csv_rest_.empty()) ||
// lines which are not correctly encoded passwords.
- (!csv_row_.empty() && password_->TryParse() != CSVPassword::Status::kOK));
+ (!csv_row_.empty() &&
+ password_->GetParseStatus() != CSVPassword::Status::kOK));
}
base::StringPiece ConsumeCSVLine(base::StringPiece* input) {
diff --git a/chromium/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc b/chromium/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc
index be748be850c..339ec9ae1f3 100644
--- a/chromium/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc
+++ b/chromium/components/password_manager/core/browser/import/csv_password_iterator_unittest.cc
@@ -36,7 +36,8 @@ TEST(CSVPasswordIteratorTest, Operations) {
{
base::test::SingleThreadTaskEnvironment env(
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
- EXPECT_EQ(iter->ParseValid(), CSVPassword(kColMap, kCSV).ParseValid());
+ EXPECT_EQ(iter->ToPasswordForm(),
+ CSVPassword(kColMap, kCSV).ToPasswordForm());
}
// Copy.
@@ -88,13 +89,13 @@ TEST(CSVPasswordIteratorTest, MostRowsCorrect) {
CSVPasswordIterator check = iter;
for (size_t i = 0; i < std::size(kExpectedUsernames); ++i) {
- EXPECT_EQ(CSVPassword::Status::kOK, (check++)->TryParse())
+ EXPECT_EQ(CSVPassword::Status::kOK, (check++)->GetParseStatus())
<< "on line " << i;
}
- EXPECT_NE(CSVPassword::Status::kOK, check->TryParse());
+ EXPECT_NE(CSVPassword::Status::kOK, check->GetParseStatus());
for (const base::StringPiece& expected_username : kExpectedUsernames) {
- PasswordForm result = (iter++)->ParseValid();
+ PasswordForm result = (iter++)->ToPasswordForm();
// Detailed checks of the parsed result are made in the test for
// CSVPassword. Here only the last field (username) is checked to (1) ensure
// that lines are processed in the expected sequence, and (2) line breaks
@@ -118,13 +119,14 @@ TEST(CSVPasswordIteratorTest, LastRowCorrect) {
CSVPasswordIterator iter(kColMap, kCSVBlob);
- PasswordForm pf;
// The iterator should skip all the faulty rows and land on the last one.
- EXPECT_EQ(CSVPassword::Status::kOK, (iter++)->Parse(&pf));
+ EXPECT_EQ(CSVPassword::Status::kOK, iter->GetParseStatus());
+ PasswordForm pf = iter->ToPasswordForm();
EXPECT_EQ("http://no-failure.example.com/", pf.signon_realm);
+ iter++;
// After iterating over all lines, there is no more data to parse.
- EXPECT_NE(CSVPassword::Status::kOK, iter->TryParse());
+ EXPECT_NE(CSVPassword::Status::kOK, iter->GetParseStatus());
}
TEST(CSVPasswordIteratorTest, NoRowCorrect) {
diff --git a/chromium/components/password_manager/core/browser/import/csv_password_sequence_unittest.cc b/chromium/components/password_manager/core/browser/import/csv_password_sequence_unittest.cc
index 350ab842bfb..bbe57fa3e6c 100644
--- a/chromium/components/password_manager/core/browser/import/csv_password_sequence_unittest.cc
+++ b/chromium/components/password_manager/core/browser/import/csv_password_sequence_unittest.cc
@@ -108,7 +108,7 @@ TEST(CSVPasswordSequenceTest, Iteration) {
size_t order = 0;
for (const CSVPassword& pwd : seq) {
ASSERT_LT(order, std::size(kExpectedCredentials));
- PasswordForm parsed = pwd.ParseValid();
+ PasswordForm parsed = pwd.ToPasswordForm();
const auto& expected = kExpectedCredentials[order];
EXPECT_EQ(GURL(expected.url), parsed.url);
EXPECT_EQ(base::ASCIIToUTF16(expected.username), parsed.username_value);
@@ -125,7 +125,7 @@ TEST(CSVPasswordSequenceTest, MissingEolAtEof) {
EXPECT_EQ(CSVPassword::Status::kOK, seq.result());
ASSERT_EQ(1, std::distance(seq.begin(), seq.end()));
- PasswordForm parsed = seq.begin()->ParseValid();
+ PasswordForm parsed = seq.begin()->ToPasswordForm();
EXPECT_EQ(GURL("http://a.com"), parsed.url);
EXPECT_EQ(u"l", parsed.username_value);
EXPECT_EQ(u"p", parsed.password_value);
diff --git a/chromium/components/password_manager/core/browser/import/csv_password_unittest.cc b/chromium/components/password_manager/core/browser/import/csv_password_unittest.cc
index 5881e7c6f4d..7944b8bbf6e 100644
--- a/chromium/components/password_manager/core/browser/import/csv_password_unittest.cc
+++ b/chromium/components/password_manager/core/browser/import/csv_password_unittest.cc
@@ -28,7 +28,7 @@ TEST(CSVPasswordTest, Construction) {
};
// Use const to check that ParseValid does not mutate the CSVPassword.
const CSVPassword csv_pwd(kColMap, "http://example.com,user,password");
- const PasswordForm result = csv_pwd.ParseValid();
+ const PasswordForm result = csv_pwd.ToPasswordForm();
const GURL expected_origin("http://example.com");
EXPECT_EQ(expected_origin, result.url);
EXPECT_EQ(expected_origin.DeprecatedGetOriginAsURL().spec(),
@@ -107,24 +107,21 @@ class CSVPasswordTestSuccess : public ::testing::TestWithParam<TestCase> {
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
};
-TEST_P(CSVPasswordTestSuccess, Parse) {
+TEST_P(CSVPasswordTestSuccess, ShouldParseToPasswordForm) {
const TestCase& test_case = GetParam();
SCOPED_TRACE(test_case.name);
const CSVPassword csv_pwd(test_case.map, test_case.csv);
- EXPECT_EQ(Status::kOK, csv_pwd.TryParse());
+ EXPECT_EQ(Status::kOK, csv_pwd.GetParseStatus());
- const PasswordForm result = csv_pwd.ParseValid();
+ const PasswordForm result = csv_pwd.ToPasswordForm();
const GURL expected_origin(test_case.origin);
EXPECT_EQ(expected_origin, result.url);
- EXPECT_EQ(expected_origin.DeprecatedGetOriginAsURL().spec(),
- result.signon_realm);
+ EXPECT_EQ(test_case.signon_realm, result.signon_realm);
EXPECT_EQ(base::UTF8ToUTF16(test_case.username), result.username_value);
EXPECT_EQ(base::UTF8ToUTF16(test_case.password), result.password_value);
EXPECT_EQ(base::Time::Now(), result.date_created);
-
- EXPECT_EQ(result, csv_pwd.ParseValid());
}
INSTANTIATE_TEST_SUITE_P(
@@ -165,9 +162,9 @@ INSTANTIATE_TEST_SUITE_P(
.Map({{2, Label::kOrigin},
{1, Label::kUsername},
{0, Label::kPassword}})
- .CSV("pwd,the-user,android://example,Y,X")
- .Origin("android://example")
- .SignonRealm("android://example")
+ .CSV("pwd,the-user,android://host@example,Y,X")
+ .Origin("android://host@example")
+ .SignonRealm("android://host@example")
.Username("the-user")
.Password("pwd")
.Build(),
@@ -260,11 +257,11 @@ INSTANTIATE_TEST_SUITE_P(
class CSVPasswordTestFailure : public ::testing::TestWithParam<TestCase> {};
-TEST_P(CSVPasswordTestFailure, Parse) {
+TEST_P(CSVPasswordTestFailure, ShouldFailWithStatus) {
const TestCase& test_case = GetParam();
SCOPED_TRACE(test_case.name);
EXPECT_EQ(test_case.status,
- CSVPassword(test_case.map, test_case.csv).TryParse());
+ CSVPassword(test_case.map, test_case.csv).GetParseStatus());
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/chromium/components/password_manager/core/browser/import/password_csv_reader_fuzzer.cc b/chromium/components/password_manager/core/browser/import/password_csv_reader_fuzzer.cc
index d9e6819325f..244065c386f 100644
--- a/chromium/components/password_manager/core/browser/import/password_csv_reader_fuzzer.cc
+++ b/chromium/components/password_manager/core/browser/import/password_csv_reader_fuzzer.cc
@@ -42,15 +42,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
CHECK(IsValid(seq.result()))
<< "Invalid parsing result of the whole sequence: "
<< static_cast<int>(seq.result());
- PasswordForm form, copy;
+ PasswordForm copy;
for (const auto& pwd : seq) {
- const CSVPassword::Status status = pwd.Parse(&form);
+ const CSVPassword::Status status = pwd.GetParseStatus();
CHECK(IsValid(status)) << "Invalid parsing result of one row: "
<< static_cast<int>(status);
if (status == CSVPassword::Status::kOK) {
// Copy the parsed password to access all its data members and allow the
// ASAN to detect any corrupted memory inside.
- copy = form;
+ copy = pwd.ToPasswordForm();
}
}
return 0;
diff --git a/chromium/components/password_manager/core/browser/import/password_importer_unittest.cc b/chromium/components/password_manager/core/browser/import/password_importer_unittest.cc
index 4b52f33e7cd..0e5f341004d 100644
--- a/chromium/components/password_manager/core/browser/import/password_importer_unittest.cc
+++ b/chromium/components/password_manager/core/browser/import/password_importer_unittest.cc
@@ -50,7 +50,7 @@ class PasswordImporterTest : public testing::Test {
if (result != password_manager::PasswordImporter::SUCCESS)
return;
for (const auto& pwd : seq) {
- imported_passwords_.push_back(pwd.ParseValid());
+ imported_passwords_.push_back(pwd.ToPasswordForm());
}
}
diff --git a/chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl.cc b/chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl.cc
index 69c48b8219c..46f5a9fdbbf 100644
--- a/chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl.cc
+++ b/chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl.cc
@@ -211,10 +211,8 @@ void LeakDetectionCheckImpl::Start(const GURL& url,
}
payload_helper_->PreparePayload(
base::UTF16ToUTF8(username_), base::UTF16ToUTF8(password_),
- TimeCallback(
- base::BindOnce(&LeakDetectionCheckImpl::OnRequestDataReady,
- weak_ptr_factory_.GetWeakPtr()),
- "PasswordManager.LeakDetection.PrepareSingleLeakRequestTime"));
+ base::BindOnce(&LeakDetectionCheckImpl::OnRequestDataReady,
+ weak_ptr_factory_.GetWeakPtr()));
}
void LeakDetectionCheckImpl::OnAccessTokenRequestCompleted(
diff --git a/chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl_unittest.cc b/chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl_unittest.cc
index ec2608df580..a0b7db72230 100644
--- a/chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl_unittest.cc
+++ b/chromium/components/password_manager/core/browser/leak_detection/leak_detection_check_impl_unittest.cc
@@ -141,10 +141,6 @@ PayloadAndCallback LeakDetectionCheckImplTest::ImitateNetworkRequest(
// Crypto stuff is done here.
task_env().RunUntilIdle();
- histogram_tester().ExpectUniqueSample(
- "PasswordManager.LeakDetection.PrepareSingleLeakRequestTime",
- kMockElapsedTime, 1);
-
return {std::move(raw_request->encrypted_payload_),
std::move(raw_request->callback_)};
}
@@ -201,10 +197,6 @@ TEST_P(LeakDetectionCheckImplTest, GetAccessTokenBeforeEncryption) {
.WillOnce(Return(ByMove(std::move(network_request))));
// Crypto stuff is done here.
task_env().RunUntilIdle();
-
- histogram_tester().ExpectUniqueSample(
- "PasswordManager.LeakDetection.PrepareSingleLeakRequestTime",
- kMockElapsedTime, 1);
}
TEST_P(LeakDetectionCheckImplTest, GetAccessTokenAfterEncryption) {
@@ -219,10 +211,6 @@ TEST_P(LeakDetectionCheckImplTest, GetAccessTokenAfterEncryption) {
// crypto stuff is done here.
task_env().RunUntilIdle();
- histogram_tester().ExpectUniqueSample(
- "PasswordManager.LeakDetection.PrepareSingleLeakRequestTime",
- kMockElapsedTime, 1);
-
const std::string access_token = "access_token";
auto network_request = std::make_unique<MockLeakDetectionRequest>();
EXPECT_CALL(
@@ -286,10 +274,6 @@ TEST_P(LeakDetectionCheckImplTest, PassesAPIKeys) {
// Crypto stuff is done here.
task_env().RunUntilIdle();
-
- histogram_tester().ExpectUniqueSample(
- "PasswordManager.LeakDetection.PrepareSingleLeakRequestTime",
- kMockElapsedTime, 1);
}
// Perform the whole cycle of a leak check. The server returns data that
diff --git a/chromium/components/password_manager/core/browser/leak_detection_delegate_helper.cc b/chromium/components/password_manager/core/browser/leak_detection_delegate_helper.cc
index 818311df2d0..fddcfea5687 100644
--- a/chromium/components/password_manager/core/browser/leak_detection_delegate_helper.cc
+++ b/chromium/components/password_manager/core/browser/leak_detection_delegate_helper.cc
@@ -12,6 +12,7 @@
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_scripts_fetcher.h"
#include "components/password_manager/core/browser/password_store_interface.h"
+#include "components/password_manager/core/browser/psl_matching_helper.h"
namespace password_manager {
@@ -90,12 +91,27 @@ void LeakDetectionDelegateHelper::ProcessResults() {
}
}
- IsSaved is_saved(
- base::ranges::any_of(partial_results_, [this](const auto& form) {
- return form->url == url_ && form->username_value == username_ &&
+ // Returns true if the urls are identical or one is a PSL match of the other.
+ auto are_urls_equivalent = [&](const GURL& url1, const GURL& url2) -> bool {
+ return url1 == url2 || IsPublicSuffixDomainMatch(url1.spec(), url2.spec());
+ };
+
+ IsSaved is_saved(base::ranges::any_of(
+ partial_results_, [this, are_urls_equivalent](const auto& form) {
+ return are_urls_equivalent(form->url, url_) &&
+ form->username_value == username_ &&
form->password_value == password_;
}));
- IsReused is_reused(partial_results_.size() > (is_saved ? 1 : 0));
+
+ // Check if the password is reused on a different origin, or on the same
+ // origin with a different username.
+ IsReused is_reused(base::ranges::any_of(
+ partial_results_, [this, are_urls_equivalent](const auto& form) {
+ return form->password_value == password_ &&
+ (!are_urls_equivalent(form->url, url_) ||
+ form->username_value != username_);
+ }));
+
HasChangeScript has_change_script(script_is_available_);
std::move(callback_).Run(is_saved, is_reused, has_change_script,
diff --git a/chromium/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc b/chromium/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
index ec4ba4c1ada..53f276a915b 100644
--- a/chromium/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
+++ b/chromium/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
@@ -31,6 +31,7 @@ namespace password_manager {
namespace {
constexpr char16_t kLeakedPassword[] = u"leaked_password";
+constexpr char16_t kOtherPassword[] = u"other_password";
constexpr char16_t kLeakedUsername[] = u"leaked_username";
constexpr char16_t kLeakedUsernameNonCanonicalized[] =
u"Leaked_Username@gmail.com";
@@ -104,7 +105,7 @@ class LeakDetectionDelegateHelperTest
}
// Sets the |PasswordForm|s which are retrieve from the |PasswordStore|.
- void SetGetLoginByPasswordConsumerInvocation(
+ void SetGetAutofillableLoginsConsumerInvocation(
std::vector<PasswordForm> password_forms) {
EXPECT_CALL(*store_, GetAutofillableLogins)
.WillOnce(testing::WithArg<0>(
@@ -123,9 +124,10 @@ class LeakDetectionDelegateHelperTest
// Credentials are neither saved nor is the password reused.
TEST_F(LeakDetectionDelegateHelperTest, NeitherSaveNotReused) {
- std::vector<PasswordForm> password_forms;
+ std::vector<PasswordForm> password_forms = {
+ CreateForm(kOtherOrigin, kOtherUsername, kOtherPassword)};
- SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+ SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(IsSaved(false), IsReused(false),
HasChangeScript(false));
InitiateGetCredentialLeakType();
@@ -136,7 +138,7 @@ TEST_F(LeakDetectionDelegateHelperTest, SavedLeakedCredentials) {
std::vector<PasswordForm> password_forms = {
CreateForm(kLeakedOrigin, kLeakedUsername)};
- SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+ SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(IsSaved(true), IsReused(false),
HasChangeScript(false),
{GURL(kLeakedOrigin)});
@@ -151,7 +153,7 @@ TEST_F(LeakDetectionDelegateHelperTest,
CreateForm(kLeakedOrigin, kLeakedUsername),
CreateForm(kOtherOrigin, kLeakedUsername)};
- SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+ SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(
IsSaved(true), IsReused(true), HasChangeScript(false),
{GURL(kLeakedOrigin), GURL(kOtherOrigin)});
@@ -167,7 +169,7 @@ TEST_F(LeakDetectionDelegateHelperTest,
CreateForm(kLeakedOrigin, kLeakedUsername),
CreateForm(kLeakedOrigin, kOtherUsername)};
- SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+ SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(IsSaved(true), IsReused(true),
HasChangeScript(false),
{GURL(kLeakedOrigin)});
@@ -180,7 +182,7 @@ TEST_F(LeakDetectionDelegateHelperTest, ReusedPasswordWithOtherUsername) {
std::vector<PasswordForm> password_forms = {
CreateForm(kLeakedOrigin, kOtherUsername)};
- SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+ SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
// Don't expect anything in |all_urls_with_leaked_credentials| since it should
// only contain url:username pairs for which both the username and password
// match.
@@ -194,7 +196,7 @@ TEST_F(LeakDetectionDelegateHelperTest, ReusedPasswordOnOtherOrigin) {
std::vector<PasswordForm> password_forms = {
CreateForm(kOtherOrigin, kLeakedUsername)};
- SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+ SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(IsSaved(false), IsReused(true),
HasChangeScript(false),
{GURL(kOtherOrigin)});
@@ -208,7 +210,7 @@ TEST_F(LeakDetectionDelegateHelperTest, ReusedPassword) {
std::vector<PasswordForm> password_forms = {
CreateForm(kOtherOrigin, kOtherUsername)};
- SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+ SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
SetOnShowLeakDetectionNotificationExpectation(IsSaved(false), IsReused(true),
HasChangeScript(false));
InitiateGetCredentialLeakType();
@@ -222,9 +224,9 @@ TEST_F(LeakDetectionDelegateHelperTest, SaveLeakedCredentials) {
CreateForm(kOtherOrigin, kLeakedUsername, kLeakedPassword);
PasswordForm leaked_origin_other_username =
CreateForm(kLeakedOrigin, kOtherUsername, kLeakedPassword);
- SetGetLoginByPasswordConsumerInvocation({leaked_origin,
- other_origin_same_credential,
- leaked_origin_other_username});
+ SetGetAutofillableLoginsConsumerInvocation({leaked_origin,
+ other_origin_same_credential,
+ leaked_origin_other_username});
SetOnShowLeakDetectionNotificationExpectation(
IsSaved(true), IsReused(true), HasChangeScript(false),
@@ -245,7 +247,7 @@ TEST_F(LeakDetectionDelegateHelperTest, SaveLeakedCredentials) {
TEST_F(LeakDetectionDelegateHelperTest, SaveLeakedCredentialsCanonicalized) {
PasswordForm non_canonicalized_username = CreateForm(
kOtherOrigin, kLeakedUsernameNonCanonicalized, kLeakedPassword);
- SetGetLoginByPasswordConsumerInvocation({non_canonicalized_username});
+ SetGetAutofillableLoginsConsumerInvocation({non_canonicalized_username});
SetOnShowLeakDetectionNotificationExpectation(IsSaved(false), IsReused(true),
HasChangeScript(false),
{GURL(kOtherOrigin)});
@@ -258,29 +260,6 @@ TEST_F(LeakDetectionDelegateHelperTest, SaveLeakedCredentialsCanonicalized) {
InitiateGetCredentialLeakType();
}
-// Credentials are saved and the password is reused on the same origin &
-// username but with a different password.
-TEST_F(LeakDetectionDelegateHelperTest,
- SavedCredentialsAndReusedPasswordWithOtherPassword) {
- std::vector<PasswordForm> password_forms = {
- CreateForm(kLeakedOrigin, kLeakedUsername),
- CreateForm(kLeakedOrigin, kLeakedUsername)};
- password_forms.back().password_value = u"another_password";
-
- SetGetLoginByPasswordConsumerInvocation(password_forms);
- // There's at least one set of leaked username:password on kLeakedOrigin, so
- // it should be in |all_urls_with_leaked_credentials| even though another set
- // of credentials on that origin has a different password.
- SetOnShowLeakDetectionNotificationExpectation(IsSaved(true), IsReused(true),
- HasChangeScript(false),
- {GURL(kLeakedOrigin)});
- password_forms.at(0).password_issues.insert_or_assign(
- InsecureType::kLeaked,
- InsecurityMetadata(base::Time::Now(), IsMuted(false)));
- EXPECT_CALL(*store_, UpdateLogin(password_forms[0]));
- InitiateGetCredentialLeakType();
-}
-
namespace {
class LeakDetectionDelegateHelperWithTwoStoreTest
: public testing::Test,
@@ -362,6 +341,10 @@ class FakePasswordScriptsFetcher : public PasswordScriptsFetcher {
return base::Value::Dict();
}
+ base::Value::List GetCacheEntries() const override {
+ return base::Value::List();
+ }
+
private:
ResponseCallback callback_;
};
diff --git a/chromium/components/password_manager/core/browser/leak_detection_dialog_utils.cc b/chromium/components/password_manager/core/browser/leak_detection_dialog_utils.cc
index fef59b6a395..24c408c50e0 100644
--- a/chromium/components/password_manager/core/browser/leak_detection_dialog_utils.cc
+++ b/chromium/components/password_manager/core/browser/leak_detection_dialog_utils.cc
@@ -26,10 +26,6 @@ using metrics_util::LeakDialogType;
constexpr char kPasswordCheckupURL[] =
"https://passwords.google.com/checkup/start?hideExplanation=true";
-constexpr base::FeatureParam<bool> kPasswordChangeUseBasicCloseLabel{
- &password_manager::features::kPasswordChange, "use_basic_close_label",
- false};
-
CredentialLeakType CreateLeakType(IsSaved is_saved,
IsReused is_reused,
IsSyncing is_syncing,
@@ -84,12 +80,6 @@ std::u16string GetAcceptButtonLabel(CredentialLeakType leak_type) {
}
std::u16string GetCancelButtonLabel(CredentialLeakType leak_type) {
- if (ShouldShowAutomaticChangePasswordButton(leak_type) &&
- !kPasswordChangeUseBasicCloseLabel.Get()) {
- return l10n_util::GetStringUTF16(
- IDS_CREDENTIAL_LEAK_DONT_CHANGE_AUTOMATICALLY);
- }
-
return l10n_util::GetStringUTF16(IDS_CLOSE);
}
@@ -106,9 +96,12 @@ std::u16string GetDescription(CredentialLeakType leak_type) {
password_manager_util::UsesPasswordManagerGoogleBranding(
IsSyncingPasswordsNormally(leak_type));
#else
- // TODO(crbug.com/1309480): Update to support Desktop branding.
- const bool uses_password_manager_updated_naming = false;
- const bool uses_password_manager_google_branding = false;
+ const bool uses_password_manager_updated_naming =
+ base::FeatureList::IsEnabled(
+ password_manager::features::kUnifiedPasswordManagerDesktop);
+ const bool uses_password_manager_google_branding =
+ password_manager_util::UsesPasswordManagerGoogleBranding(
+ IsSyncingPasswordsNormally(leak_type));
#endif
if (uses_password_manager_updated_naming) {
if (ShouldShowAutomaticChangePasswordButton(leak_type)) {
@@ -163,8 +156,9 @@ std::u16string GetTitle(CredentialLeakType leak_type) {
const bool uses_password_manager_updated_naming =
password_manager::features::UsesUnifiedPasswordManagerUi();
#else
- // TODO(crbug.com/1309480): Update to support Desktop branding.
- const bool uses_password_manager_updated_naming = false;
+ const bool uses_password_manager_updated_naming =
+ base::FeatureList::IsEnabled(
+ password_manager::features::kUnifiedPasswordManagerDesktop);
#endif
if (uses_password_manager_updated_naming) {
return l10n_util::GetStringUTF16(ShouldCheckPasswords(leak_type)
@@ -241,4 +235,44 @@ GURL GetPasswordCheckupURL(PasswordCheckupReferrer referrer) {
return net::AppendQueryParameter(url, "utm_campaign", campaign);
}
+LeakDialogTraits::LeakDialogTraits(CredentialLeakType leak_type)
+ :
+#if BUILDFLAG(IS_IOS)
+ uses_password_manager_updated_naming_(base::FeatureList::IsEnabled(
+ password_manager::features::kIOSEnablePasswordManagerBrandingUpdate)),
+ uses_password_manager_google_branding_(true)
+#elif BUILDFLAG(IS_ANDROID)
+ uses_password_manager_updated_naming_(
+ password_manager::features::UsesUnifiedPasswordManagerUi()),
+ uses_password_manager_google_branding_(
+ password_manager_util::UsesPasswordManagerGoogleBranding(
+ IsSyncingPasswordsNormally(leak_type)))
+#else
+ uses_password_manager_updated_naming_(base::FeatureList::IsEnabled(
+ password_manager::features::kUnifiedPasswordManagerDesktop)),
+ uses_password_manager_google_branding_(
+ password_manager_util::UsesPasswordManagerGoogleBranding(
+ IsSyncingPasswordsNormally(leak_type)))
+#endif
+{
+}
+
+std::unique_ptr<LeakDialogTraits> CreateDialogTraits(
+ CredentialLeakType leak_type) {
+ switch (password_manager::GetLeakDialogType(leak_type)) {
+ case LeakDialogType::kChange:
+ return std::make_unique<LeakDialogTraitsImp<LeakDialogType::kChange>>(
+ leak_type);
+ case LeakDialogType::kCheckup:
+ return std::make_unique<LeakDialogTraitsImp<LeakDialogType::kCheckup>>(
+ leak_type);
+ case LeakDialogType::kCheckupAndChange:
+ return std::make_unique<
+ LeakDialogTraitsImp<LeakDialogType::kCheckupAndChange>>(leak_type);
+ case LeakDialogType::kChangeAutomatically:
+ return std::make_unique<
+ LeakDialogTraitsImp<LeakDialogType::kChangeAutomatically>>(leak_type);
+ }
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/leak_detection_dialog_utils.h b/chromium/components/password_manager/core/browser/leak_detection_dialog_utils.h
index 306d031eedc..ced5b5aaf32 100644
--- a/chromium/components/password_manager/core/browser/leak_detection_dialog_utils.h
+++ b/chromium/components/password_manager/core/browser/leak_detection_dialog_utils.h
@@ -10,6 +10,8 @@
#include "base/types/strong_alias.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/range/range.h"
#include "url/gurl.h"
@@ -91,6 +93,222 @@ metrics_util::LeakDialogType GetLeakDialogType(CredentialLeakType leak_type);
GURL GetPasswordCheckupURL(PasswordCheckupReferrer referrer =
PasswordCheckupReferrer::kLeakDetectionDialog);
+// Captures common traits needed for a leak dialog.
+class LeakDialogTraits {
+ public:
+ explicit LeakDialogTraits(CredentialLeakType leak_type);
+
+ virtual ~LeakDialogTraits() = default;
+
+ // Returns the label for the accept button.
+ virtual std::u16string GetAcceptButtonLabel() const = 0;
+
+ // Returns the label for the cancel button.
+ virtual std::u16string GetCancelButtonLabel() const = 0;
+
+ // Returns the dialog message based on credential leak type.
+ virtual std::u16string GetDescription() const = 0;
+
+ // Returns the dialog title based on credential leak type.
+ virtual std::u16string GetTitle() const = 0;
+
+ // Checks whether the dialog should prompt user to password checkup.
+ virtual bool ShouldCheckPasswords() const = 0;
+
+ // Checks whether the dialog should show cancel button.
+ virtual bool ShouldShowCancelButton() const = 0;
+
+ protected:
+ bool uses_password_manager_updated_naming() const {
+ return uses_password_manager_updated_naming_;
+ }
+
+ bool uses_password_manager_google_branding() const {
+ return uses_password_manager_google_branding_;
+ }
+
+ private:
+ // Set iff Unified Password Manager / Updated branding strings are used.
+ const bool uses_password_manager_updated_naming_;
+ // Set iff Google Chrome Branding strings are used.
+ const bool uses_password_manager_google_branding_;
+};
+
+// Creates a dialog traits object.
+std::unique_ptr<LeakDialogTraits> CreateDialogTraits(
+ CredentialLeakType leak_type);
+
+template <metrics_util::LeakDialogType kDialogType>
+class LeakDialogTraitsImp : public LeakDialogTraits {
+ public:
+ explicit LeakDialogTraitsImp(CredentialLeakType leak_type)
+ : LeakDialogTraits(leak_type) {}
+ LeakDialogTraitsImp(const LeakDialogTraitsImp&) = delete;
+ LeakDialogTraitsImp& operator=(const LeakDialogTraitsImp&) = delete;
+
+ std::u16string GetAcceptButtonLabel() const override;
+ std::u16string GetCancelButtonLabel() const override;
+ std::u16string GetDescription() const override;
+ std::u16string GetTitle() const override;
+ bool ShouldCheckPasswords() const override;
+ bool ShouldShowCancelButton() const override;
+};
+
+// Implementation of a leak checkup dialog.
+template <>
+class LeakDialogTraitsImp<metrics_util::LeakDialogType::kCheckup>
+ : public LeakDialogTraits {
+ public:
+ explicit LeakDialogTraitsImp(CredentialLeakType leak_type)
+ : LeakDialogTraits(leak_type) {}
+ LeakDialogTraitsImp(const LeakDialogTraitsImp&) = delete;
+ LeakDialogTraitsImp& operator=(const LeakDialogTraitsImp&) = delete;
+
+ std::u16string GetAcceptButtonLabel() const override {
+ return l10n_util::GetStringUTF16(IDS_LEAK_CHECK_CREDENTIALS);
+ }
+
+ std::u16string GetCancelButtonLabel() const override {
+ return l10n_util::GetStringUTF16(IDS_CLOSE);
+ }
+
+ std::u16string GetDescription() const override {
+ if (uses_password_manager_updated_naming()) {
+ return l10n_util::GetStringUTF16(
+ uses_password_manager_google_branding()
+ ? IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED
+ : IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE_GPM_NON_BRANDED);
+ } else {
+ return l10n_util::GetStringUTF16(
+ IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE);
+ }
+ }
+
+ std::u16string GetTitle() const override {
+ return l10n_util::GetStringUTF16(uses_password_manager_updated_naming()
+ ? IDS_CREDENTIAL_LEAK_TITLE_CHECK_GPM
+ : IDS_CREDENTIAL_LEAK_TITLE_CHECK);
+ }
+
+ bool ShouldCheckPasswords() const override { return true; }
+
+ bool ShouldShowCancelButton() const override { return true; };
+};
+
+// Implementation of a leak change dialog.
+template <>
+class LeakDialogTraitsImp<metrics_util::LeakDialogType::kChange>
+ : public LeakDialogTraits {
+ public:
+ explicit LeakDialogTraitsImp(CredentialLeakType leak_type)
+ : LeakDialogTraits(leak_type) {}
+ LeakDialogTraitsImp(const LeakDialogTraitsImp&) = delete;
+ LeakDialogTraitsImp& operator=(const LeakDialogTraitsImp&) = delete;
+
+ std::u16string GetAcceptButtonLabel() const override {
+ return l10n_util::GetStringUTF16(IDS_OK);
+ }
+
+ std::u16string GetCancelButtonLabel() const override {
+ return l10n_util::GetStringUTF16(IDS_CLOSE);
+ }
+
+ std::u16string GetDescription() const override {
+ if (uses_password_manager_updated_naming()) {
+ return l10n_util::GetStringUTF16(
+ uses_password_manager_google_branding()
+ ? IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_BRANDED
+ : IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_NON_BRANDED);
+ } else {
+ return l10n_util::GetStringUTF16(
+ IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE);
+ }
+ }
+
+ std::u16string GetTitle() const override {
+ return l10n_util::GetStringUTF16(IDS_CREDENTIAL_LEAK_TITLE_CHANGE);
+ }
+
+ bool ShouldCheckPasswords() const override { return false; }
+
+ bool ShouldShowCancelButton() const override { return false; };
+};
+
+// Implementation of a leak checkup and change dialog.
+template <>
+class LeakDialogTraitsImp<metrics_util::LeakDialogType::kCheckupAndChange>
+ : public LeakDialogTraits {
+ public:
+ explicit LeakDialogTraitsImp(CredentialLeakType leak_type)
+ : LeakDialogTraits(leak_type) {}
+ LeakDialogTraitsImp(const LeakDialogTraitsImp&) = delete;
+ LeakDialogTraitsImp& operator=(const LeakDialogTraitsImp&) = delete;
+
+ std::u16string GetAcceptButtonLabel() const override {
+ return l10n_util::GetStringUTF16(IDS_LEAK_CHECK_CREDENTIALS);
+ }
+
+ std::u16string GetCancelButtonLabel() const override {
+ return l10n_util::GetStringUTF16(IDS_CLOSE);
+ }
+
+ std::u16string GetDescription() const override {
+ if (uses_password_manager_updated_naming())
+ return l10n_util::GetStringUTF16(
+ uses_password_manager_google_branding()
+ ? IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED
+ : IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE_GPM_NON_BRANDED);
+ else
+ return l10n_util::GetStringUTF16(
+ IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE);
+ }
+
+ std::u16string GetTitle() const override {
+ return l10n_util::GetStringUTF16(uses_password_manager_updated_naming()
+ ? IDS_CREDENTIAL_LEAK_TITLE_CHECK_GPM
+ : IDS_CREDENTIAL_LEAK_TITLE_CHECK);
+ }
+
+ bool ShouldCheckPasswords() const override { return true; }
+
+ bool ShouldShowCancelButton() const override { return true; };
+};
+
+// Implementation of a leak automatic change dialog.
+template <>
+class LeakDialogTraitsImp<metrics_util::LeakDialogType::kChangeAutomatically>
+ : public LeakDialogTraits {
+ public:
+ explicit LeakDialogTraitsImp(CredentialLeakType leak_type)
+ : LeakDialogTraits(leak_type) {}
+ LeakDialogTraitsImp(const LeakDialogTraitsImp&) = delete;
+ LeakDialogTraitsImp& operator=(const LeakDialogTraitsImp&) = delete;
+
+ std::u16string GetAcceptButtonLabel() const override {
+ return l10n_util::GetStringUTF16(IDS_CREDENTIAL_LEAK_CHANGE_AUTOMATICALLY);
+ }
+
+ std::u16string GetCancelButtonLabel() const override {
+ return l10n_util::GetStringUTF16(IDS_CLOSE);
+ }
+
+ std::u16string GetDescription() const override {
+ return l10n_util::GetStringUTF16(
+ uses_password_manager_updated_naming()
+ ? IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_AUTOMATICALLY_MESSAGE_GPM
+ : IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_AUTOMATICALLY_MESSAGE);
+ }
+
+ std::u16string GetTitle() const override {
+ return l10n_util::GetStringUTF16(
+ IDS_CREDENTIAL_LEAK_TITLE_CHANGE_AUTOMATICALLY);
+ }
+
+ bool ShouldCheckPasswords() const override { return false; }
+
+ bool ShouldShowCancelButton() const override { return true; };
+};
+
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DIALOG_UTILS_H_
diff --git a/chromium/components/password_manager/core/browser/leak_detection_dialog_utils_unittest.cc b/chromium/components/password_manager/core/browser/leak_detection_dialog_utils_unittest.cc
index ee27ebf6215..795e77d2160 100644
--- a/chromium/components/password_manager/core/browser/leak_detection_dialog_utils_unittest.cc
+++ b/chromium/components/password_manager/core/browser/leak_detection_dialog_utils_unittest.cc
@@ -23,6 +23,7 @@ using password_manager::CredentialLeakType;
using password_manager::IsReused;
using password_manager::IsSaved;
using password_manager::IsSyncing;
+using password_manager::metrics_util::LeakDialogType;
namespace password_manager {
@@ -45,74 +46,44 @@ const struct {
IsSyncing(false),
HasChangeScript(false)),
IDS_OK, IDS_CLOSE,
-#if BUILDFLAG(IS_IOS) || \
- (BUILDFLAG(IS_ANDROID) && BUILDFLAG(GOOGLE_CHROME_BRANDING))
+#if BUILDFLAG(IS_IOS) || BUILDFLAG(GOOGLE_CHROME_BRANDING)
IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_BRANDED,
-#elif BUILDFLAG(IS_ANDROID) && !BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#elif !BUILDFLAG(GOOGLE_CHROME_BRANDING)
IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_NON_BRANDED,
-#else
- IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE,
#endif
IDS_CREDENTIAL_LEAK_TITLE_CHANGE, false, false},
{CreateLeakType(IsSaved(false),
IsReused(false),
IsSyncing(true),
HasChangeScript(false)),
- IDS_OK, IDS_CLOSE,
-#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
- IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_BRANDED,
-#else
- IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE,
-#endif
+ IDS_OK, IDS_CLOSE, IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_BRANDED,
IDS_CREDENTIAL_LEAK_TITLE_CHANGE, false, false},
{CreateLeakType(IsSaved(false),
IsReused(true),
IsSyncing(true),
HasChangeScript(false)),
IDS_LEAK_CHECK_CREDENTIALS, IDS_CLOSE,
-#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED,
- IDS_CREDENTIAL_LEAK_TITLE_CHECK_GPM,
-#else
- IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE,
- IDS_CREDENTIAL_LEAK_TITLE_CHECK,
-#endif
- true, true},
+ IDS_CREDENTIAL_LEAK_TITLE_CHECK_GPM, true, true},
{CreateLeakType(IsSaved(false),
IsReused(false),
IsSyncing(true),
HasChangeScript(true)),
- IDS_OK, IDS_CLOSE,
-#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
- IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_BRANDED,
-#else
- IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE,
-#endif
+ IDS_OK, IDS_CLOSE, IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_BRANDED,
IDS_CREDENTIAL_LEAK_TITLE_CHANGE, false, false},
{CreateLeakType(IsSaved(true),
IsReused(false),
IsSyncing(true),
HasChangeScript(false)),
- IDS_OK, IDS_CLOSE,
-#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
- IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_BRANDED,
-#else
- IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE,
-#endif
+ IDS_OK, IDS_CLOSE, IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE_GPM_BRANDED,
IDS_CREDENTIAL_LEAK_TITLE_CHANGE, false, false},
{CreateLeakType(IsSaved(true),
IsReused(true),
IsSyncing(true),
HasChangeScript(false)),
IDS_LEAK_CHECK_CREDENTIALS, IDS_CLOSE,
-#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED,
- IDS_CREDENTIAL_LEAK_TITLE_CHECK_GPM,
-#else
- IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE,
- IDS_CREDENTIAL_LEAK_TITLE_CHECK,
-#endif
- true, true},
+ IDS_CREDENTIAL_LEAK_TITLE_CHECK_GPM, true, true},
};
struct BulkCheckParams {
@@ -155,6 +126,9 @@ class CredentialLeakDialogUtilsTest : public testing::Test {
#elif BUILDFLAG(IS_ANDROID)
feature_list_.InitAndEnableFeature(
features::kUnifiedPasswordManagerAndroid);
+#else
+ feature_list_.InitAndEnableFeature(
+ features::kUnifiedPasswordManagerDesktop);
#endif
}
@@ -171,6 +145,16 @@ TEST_F(CredentialLeakDialogUtilsTest, GetAcceptButtonLabel) {
}
}
+TEST_F(CredentialLeakDialogUtilsTest, LeakDialogTraits_GetAcceptButtonLabel) {
+ for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << i);
+ EXPECT_EQ(
+ l10n_util::GetStringUTF16(kLeakTypesTestCases[i].accept_button_id),
+ CreateDialogTraits(kLeakTypesTestCases[i].leak_type)
+ ->GetAcceptButtonLabel());
+ }
+}
+
TEST_F(CredentialLeakDialogUtilsTest, GetCancelButtonLabel) {
for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
SCOPED_TRACE(testing::Message() << i);
@@ -180,6 +164,16 @@ TEST_F(CredentialLeakDialogUtilsTest, GetCancelButtonLabel) {
}
}
+TEST_F(CredentialLeakDialogUtilsTest, LeakDialogTraits_GetCancelButtonLabel) {
+ for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << i);
+ EXPECT_EQ(
+ l10n_util::GetStringUTF16(kLeakTypesTestCases[i].cancel_button_id),
+ CreateDialogTraits(kLeakTypesTestCases[i].leak_type)
+ ->GetCancelButtonLabel());
+ }
+}
+
TEST_F(CredentialLeakDialogUtilsTest, GetDescription) {
for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
SCOPED_TRACE(testing::Message() << i);
@@ -190,6 +184,15 @@ TEST_F(CredentialLeakDialogUtilsTest, GetDescription) {
}
}
+TEST_F(CredentialLeakDialogUtilsTest, LeakDialogTraits_GetDescription) {
+ for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << i);
+ EXPECT_EQ(
+ l10n_util::GetStringUTF16(kLeakTypesTestCases[i].leak_message_id),
+ CreateDialogTraits(kLeakTypesTestCases[i].leak_type)->GetDescription());
+ }
+}
+
TEST_F(CredentialLeakDialogUtilsTest, GetTitle) {
for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
SCOPED_TRACE(testing::Message() << i);
@@ -198,6 +201,14 @@ TEST_F(CredentialLeakDialogUtilsTest, GetTitle) {
}
}
+TEST_F(CredentialLeakDialogUtilsTest, LeakDialogTraits_GetTitle) {
+ for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << i);
+ EXPECT_EQ(l10n_util::GetStringUTF16(kLeakTypesTestCases[i].leak_title_id),
+ CreateDialogTraits(kLeakTypesTestCases[i].leak_type)->GetTitle());
+ }
+}
+
TEST_F(CredentialLeakDialogUtilsTest, ShouldCheckPasswords) {
for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
SCOPED_TRACE(testing::Message() << i);
@@ -206,6 +217,15 @@ TEST_F(CredentialLeakDialogUtilsTest, ShouldCheckPasswords) {
}
}
+TEST_F(CredentialLeakDialogUtilsTest, LeakDialogTraits_ShouldCheckPasswords) {
+ for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << i);
+ EXPECT_EQ(kLeakTypesTestCases[i].should_check_passwords,
+ CreateDialogTraits(kLeakTypesTestCases[i].leak_type)
+ ->ShouldCheckPasswords());
+ }
+}
+
TEST_F(CredentialLeakDialogUtilsTest, ShouldShowCancelButton) {
for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
SCOPED_TRACE(testing::Message() << i);
@@ -214,6 +234,15 @@ TEST_F(CredentialLeakDialogUtilsTest, ShouldShowCancelButton) {
}
}
+TEST_F(CredentialLeakDialogUtilsTest, LeakDialogTraits_ShouldShowCancelButton) {
+ for (size_t i = 0; i < std::size(kLeakTypesTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << i);
+ EXPECT_EQ(kLeakTypesTestCases[i].should_show_cancel_button,
+ CreateDialogTraits(kLeakTypesTestCases[i].leak_type)
+ ->ShouldShowCancelButton());
+ }
+}
+
class BulkCheckCredentialLeakDialogUtilsTest
: public testing::TestWithParam<BulkCheckParams> {
public:
@@ -308,8 +337,7 @@ struct PasswordChangeParams {
IsReused(false),
IsSyncing(true),
HasChangeScript(true)),
- IDS_CREDENTIAL_LEAK_CHANGE_AUTOMATICALLY,
- IDS_CREDENTIAL_LEAK_DONT_CHANGE_AUTOMATICALLY, true, true},
+ IDS_CREDENTIAL_LEAK_CHANGE_AUTOMATICALLY, IDS_CLOSE, true, true},
{CreateLeakType(IsSaved(true),
IsReused(true),
IsSyncing(false),
@@ -319,8 +347,7 @@ struct PasswordChangeParams {
IsReused(true),
IsSyncing(true),
HasChangeScript(true)),
- IDS_CREDENTIAL_LEAK_CHANGE_AUTOMATICALLY,
- IDS_CREDENTIAL_LEAK_DONT_CHANGE_AUTOMATICALLY, true, true}};
+ IDS_CREDENTIAL_LEAK_CHANGE_AUTOMATICALLY, IDS_CLOSE, true, true}};
class PasswordChangeCredentialLeakDialogUtilsTest
: public testing::TestWithParam<PasswordChangeParams> {
diff --git a/chromium/components/password_manager/core/browser/login_database.cc b/chromium/components/password_manager/core/browser/login_database.cc
index b35556cf271..925670108ae 100644
--- a/chromium/components/password_manager/core/browser/login_database.cc
+++ b/chromium/components/password_manager/core/browser/login_database.cc
@@ -47,8 +47,6 @@
#include "components/sync/model/metadata_batch.h"
#include "components/sync/protocol/entity_metadata.pb.h"
#include "components/sync/protocol/model_type_state.pb.h"
-#include "google_apis/gaia/gaia_auth_util.h"
-#include "google_apis/gaia/gaia_urls.h"
#include "sql/database.h"
#include "sql/statement.h"
#include "sql/transaction.h"
@@ -535,7 +533,23 @@ bool InsecureCredentialsPostMigrationStepCallback(
sql::Database* db,
unsigned new_version) {
if (new_version == 29) {
- if (!insecure_credentials_builder->CreateTable(db)) {
+ std::string create_table_statement =
+ "CREATE TABLE insecure_credentials ("
+ "parent_id INTEGER REFERENCES logins ON UPDATE CASCADE ON DELETE "
+ "CASCADE DEFERRABLE INITIALLY DEFERRED, "
+ "insecurity_type INTEGER NOT NULL, "
+ "create_time INTEGER NOT NULL, "
+ "is_muted INTEGER NOT NULL DEFAULT 0, "
+ "UNIQUE (parent_id, insecurity_type))";
+ std::string create_index_statement =
+ "CREATE INDEX foreign_key_index ON insecure_credentials "
+ "(parent_id)";
+ sql::Transaction creation_transaction(db);
+ bool table_creation_success = creation_transaction.Begin() &&
+ db->Execute(create_table_statement.c_str()) &&
+ db->Execute(create_index_statement.c_str()) &&
+ creation_transaction.Commit();
+ if (!table_creation_success) {
LOG(ERROR) << "Failed to create the 'insecure_credentials' table";
LogDatabaseInitError(INIT_COMPROMISED_CREDENTIALS_ERROR);
return false;
@@ -570,7 +584,23 @@ bool PasswordNotesPostMigrationStepCallback(
sql::Database* db,
unsigned new_version) {
if (new_version == 33) {
- if (!password_notes_builder->CreateTable(db)) {
+ std::string create_table_statement =
+ "CREATE TABLE password_notes ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "parent_id INTEGER NOT NULL REFERENCES logins ON UPDATE CASCADE ON "
+ "DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, "
+ "key VARCHAR NOT NULL, "
+ "value BLOB, "
+ "date_created INTEGER NOT NULL, "
+ "confidential INTEGER, "
+ "UNIQUE (parent_id, key))";
+ std::string create_index_statement =
+ "CREATE INDEX foreign_key_index_notes ON password_notes (parent_id)";
+ sql::Transaction transaction(db);
+ bool table_creation_success =
+ transaction.Begin() && db->Execute(create_table_statement.c_str()) &&
+ db->Execute(create_index_statement.c_str()) && transaction.Commit();
+ if (!table_creation_success) {
LOG(ERROR) << "Failed to create the 'password_notes' table";
LogDatabaseInitError(INIT_PASSWORD_NOTES_ERROR);
return false;
@@ -1015,7 +1045,7 @@ PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form,
UpdateInsecureCredentials(primary_key,
form_with_encrypted_password.password_issues);
}
- UpdatePasswordNote(primary_key, form.note);
+ UpdatePasswordNotes(primary_key, form.notes);
list.emplace_back(PasswordStoreChange::ADD,
std::move(form_with_encrypted_password), primary_key,
/*password_changed=*/false);
@@ -1044,7 +1074,7 @@ PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form,
insecure_changed = UpdateInsecureCredentials(
primary_key, form_with_encrypted_password.password_issues);
}
- UpdatePasswordNote(primary_key, form_with_encrypted_password.note);
+ UpdatePasswordNotes(primary_key, form_with_encrypted_password.notes);
list.emplace_back(PasswordStoreChange::ADD,
std::move(form_with_encrypted_password),
FormPrimaryKey(db_.GetLastInsertRowId()),
@@ -1161,8 +1191,8 @@ PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form,
InsecureCredentialsChanged insecure_changed = UpdateInsecureCredentials(
FormPrimaryKey(old_primary_key_password.primary_key),
form_with_encrypted_password.password_issues);
- UpdatePasswordNote(FormPrimaryKey(old_primary_key_password.primary_key),
- form.note);
+ UpdatePasswordNotes(FormPrimaryKey(old_primary_key_password.primary_key),
+ form.notes);
PasswordStoreChangeList list;
FillFormInStore(&form_with_encrypted_password);
@@ -1388,7 +1418,7 @@ LoginDatabase::EncryptionResult LoginDatabase::InitPasswordFormFromStatement(
form->date_password_modified = base::Time::FromDeltaSinceWindowsEpoch(
base::Microseconds(s.ColumnInt64(COLUMN_DATE_PASSWORD_MODIFIED)));
PopulateFormWithPasswordIssues(FormPrimaryKey(*primary_key), form);
- PopulateFormWithNote(FormPrimaryKey(*primary_key), form);
+ PopulateFormWithNotes(FormPrimaryKey(*primary_key), form);
return ENCRYPTION_RESULT_SUCCESS;
}
@@ -1996,24 +2026,22 @@ InsecureCredentialsChanged LoginDatabase::UpdateInsecureCredentials(
return InsecureCredentialsChanged(changed);
}
-void LoginDatabase::PopulateFormWithNote(FormPrimaryKey primary_key,
- PasswordForm* form) const {
+void LoginDatabase::PopulateFormWithNotes(FormPrimaryKey primary_key,
+ PasswordForm* form) const {
if (!base::FeatureList::IsEnabled(features::kPasswordNotes))
return;
- absl::optional<PasswordNote> note =
- password_notes_table_.GetPasswordNote(primary_key);
- form->note = note ? note.value() : PasswordNote();
+ form->notes = password_notes_table_.GetPasswordNotes(primary_key);
}
-void LoginDatabase::UpdatePasswordNote(FormPrimaryKey primary_key,
- PasswordNote note) {
+void LoginDatabase::UpdatePasswordNotes(
+ FormPrimaryKey primary_key,
+ const std::vector<PasswordNote>& notes) {
if (!base::FeatureList::IsEnabled(features::kPasswordNotes))
return;
- if (note.value.empty()) {
- password_notes_table_.RemovePasswordNote(primary_key);
- return;
- }
- password_notes_table_.InsertOrReplace(primary_key, note);
+
+ password_notes_table_.RemovePasswordNotes(primary_key);
+ for (const PasswordNote& note : notes)
+ password_notes_table_.InsertOrReplace(primary_key, note);
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/login_database.h b/chromium/components/password_manager/core/browser/login_database.h
index 0e7b38b23e9..3ea08ca91d1 100644
--- a/chromium/components/password_manager/core/browser/login_database.h
+++ b/chromium/components/password_manager/core/browser/login_database.h
@@ -329,15 +329,15 @@ class LoginDatabase : public PasswordStoreSync::MetadataStore {
FormPrimaryKey primary_key,
const base::flat_map<InsecureType, InsecurityMetadata>& password_issues);
- // Reads the `password_notes` table for the note with `primary_key` and fills
- // the `form->note` field. If there are no notes for `primary_key`, the form
- // is set to empty note.
- void PopulateFormWithNote(FormPrimaryKey primary_key,
- PasswordForm* form) const;
-
- // Updates the `password_notes` table if `note.value` changed for
- // `primary_key`.
- void UpdatePasswordNote(FormPrimaryKey primary_key, PasswordNote note);
+ // Reads the `password_notes` table for the notes with `primary_key` and fills
+ // the `form->notes` field. If there are no notes for `primary_key`, the form
+ // is set to empty notes.
+ void PopulateFormWithNotes(FormPrimaryKey primary_key,
+ PasswordForm* form) const;
+
+ // Updates the `password_notes` table if `notes` changed for `primary_key`.
+ void UpdatePasswordNotes(FormPrimaryKey primary_key,
+ const std::vector<PasswordNote>& notes);
const base::FilePath db_path_;
const IsAccountStore is_account_store_;
diff --git a/chromium/components/password_manager/core/browser/login_database_async_helper.cc b/chromium/components/password_manager/core/browser/login_database_async_helper.cc
index ab2a00cee13..dc13cb736e5 100644
--- a/chromium/components/password_manager/core/browser/login_database_async_helper.cc
+++ b/chromium/components/password_manager/core/browser/login_database_async_helper.cc
@@ -86,26 +86,17 @@ bool LoginDatabaseAsyncHelper::Initialize(
return success;
}
-LoginsResult LoginDatabaseAsyncHelper::GetAllLogins(
- PasswordStoreBackendMetricsRecorder metrics_recorder) {
+LoginsResultOrError LoginDatabaseAsyncHelper::GetAllLogins() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
PrimaryKeyToFormMap key_to_form_map;
if (!login_db_) {
- metrics_recorder.RecordMetrics(
- /*success=*/false,
- /*error=*/absl::optional<ErrorFromPasswordStoreOrAndroidBackend>(
- PasswordStoreBackendError::kUnrecoverable));
- return {};
+ return PasswordStoreBackendError::kUnrecoverable;
}
FormRetrievalResult result = login_db_->GetAllLogins(&key_to_form_map);
if (result != FormRetrievalResult::kSuccess &&
result != FormRetrievalResult::kEncryptionServiceFailureWithPartialData) {
- metrics_recorder.RecordMetrics(
- /*success=*/false,
- /*error=*/absl::optional<ErrorFromPasswordStoreOrAndroidBackend>(
- PasswordStoreBackendError::kUnrecoverable));
- return {};
+ return PasswordStoreBackendError::kUnrecoverable;
}
std::vector<std::unique_ptr<PasswordForm>> obtained_forms;
@@ -113,53 +104,36 @@ LoginsResult LoginDatabaseAsyncHelper::GetAllLogins(
for (auto& pair : key_to_form_map) {
obtained_forms.push_back(std::move(pair.second));
}
- metrics_recorder.RecordMetrics(/*success=*/true, /*error=*/absl::nullopt);
return obtained_forms;
}
-LoginsResult LoginDatabaseAsyncHelper::GetAutofillableLogins(
- PasswordStoreBackendMetricsRecorder metrics_recorder) {
+LoginsResultOrError LoginDatabaseAsyncHelper::GetAutofillableLogins() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<std::unique_ptr<PasswordForm>> results;
if (!login_db_ || !login_db_->GetAutofillableLogins(&results)) {
- metrics_recorder.RecordMetrics(
- /*success=*/false,
- /*error=*/absl::optional<ErrorFromPasswordStoreOrAndroidBackend>(
- PasswordStoreBackendError::kUnrecoverable));
- return {};
+ return PasswordStoreBackendError::kUnrecoverable;
}
- metrics_recorder.RecordMetrics(/*success=*/true, /*error=*/absl::nullopt);
return results;
}
-LoginsResult LoginDatabaseAsyncHelper::FillMatchingLogins(
+LoginsResultOrError LoginDatabaseAsyncHelper::FillMatchingLogins(
const std::vector<PasswordFormDigest>& forms,
- bool include_psl,
- PasswordStoreBackendMetricsRecorder metrics_recorder) {
+ bool include_psl) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<std::unique_ptr<PasswordForm>> results;
- bool success = false;
for (const auto& form : forms) {
std::vector<std::unique_ptr<PasswordForm>> matched_forms;
- if (login_db_ && !login_db_->GetLogins(form, include_psl, &matched_forms))
- continue;
- success = true;
+ if (!login_db_ || !login_db_->GetLogins(form, include_psl, &matched_forms))
+ return PasswordStoreBackendError::kUnrecoverable;
results.insert(results.end(),
std::make_move_iterator(matched_forms.begin()),
std::make_move_iterator(matched_forms.end()));
}
- metrics_recorder.RecordMetrics(
- success,
- /*error=*/success
- ? absl::nullopt
- : absl::optional<ErrorFromPasswordStoreOrAndroidBackend>(
- PasswordStoreBackendError::kUnrecoverable));
return results;
}
-PasswordStoreChangeList LoginDatabaseAsyncHelper::AddLogin(
- const PasswordForm& form,
- PasswordStoreBackendMetricsRecorder metrics_recorder) {
+PasswordChangesOrError LoginDatabaseAsyncHelper::AddLogin(
+ const PasswordForm& form) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
BeginTransaction();
AddLoginError error = AddLoginError::kNone;
@@ -171,18 +145,14 @@ PasswordStoreChangeList LoginDatabaseAsyncHelper::AddLogin(
// because sync codebase needs to update metadata atomically together with
// the login data.
CommitTransaction();
- metrics_recorder.RecordMetrics(
- /*success=*/error == AddLoginError::kNone,
- /*error=*/error == AddLoginError::kNone
- ? absl::nullopt
- : absl::optional<ErrorFromPasswordStoreOrAndroidBackend>(
- PasswordStoreBackendError::kUnrecoverable));
- return changes;
+ return error == AddLoginError::kNone
+ ? changes
+ : PasswordChangesOrError(
+ PasswordStoreBackendError::kUnrecoverable);
}
-PasswordStoreChangeList LoginDatabaseAsyncHelper::UpdateLogin(
- const PasswordForm& form,
- PasswordStoreBackendMetricsRecorder metrics_recorder) {
+PasswordChangesOrError LoginDatabaseAsyncHelper::UpdateLogin(
+ const PasswordForm& form) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
BeginTransaction();
UpdateLoginError error = UpdateLoginError::kNone;
@@ -194,18 +164,14 @@ PasswordStoreChangeList LoginDatabaseAsyncHelper::UpdateLogin(
// because sync codebase needs to update metadata atomically together with
// the login data.
CommitTransaction();
- metrics_recorder.RecordMetrics(
- /*success=*/error == UpdateLoginError::kNone,
- /*error=*/error == UpdateLoginError::kNone
- ? absl::nullopt
- : absl::optional<ErrorFromPasswordStoreOrAndroidBackend>(
- PasswordStoreBackendError::kUnrecoverable));
- return changes;
+ return error == UpdateLoginError::kNone
+ ? changes
+ : PasswordChangesOrError(
+ PasswordStoreBackendError::kUnrecoverable);
}
-PasswordStoreChangeList LoginDatabaseAsyncHelper::RemoveLogin(
- const PasswordForm& form,
- PasswordStoreBackendMetricsRecorder metrics_recorder) {
+PasswordChangesOrError LoginDatabaseAsyncHelper::RemoveLogin(
+ const PasswordForm& form) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
BeginTransaction();
PasswordStoreChangeList changes;
@@ -218,19 +184,12 @@ PasswordStoreChangeList LoginDatabaseAsyncHelper::RemoveLogin(
// because sync codebase needs to update metadata atomically together with
// the login data.
CommitTransaction();
- metrics_recorder.RecordMetrics(
- /*success=*/!changes.empty(),
- /*error=*/!changes.empty()
- ? absl::nullopt
- : absl::optional<ErrorFromPasswordStoreOrAndroidBackend>(
- PasswordStoreBackendError::kUnrecoverable));
return changes;
}
-PasswordStoreChangeList LoginDatabaseAsyncHelper::RemoveLoginsCreatedBetween(
+PasswordChangesOrError LoginDatabaseAsyncHelper::RemoveLoginsCreatedBetween(
base::Time delete_begin,
- base::Time delete_end,
- PasswordStoreBackendMetricsRecorder metrics_recorder) {
+ base::Time delete_end) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
BeginTransaction();
PasswordStoreChangeList changes;
@@ -243,21 +202,16 @@ PasswordStoreChangeList LoginDatabaseAsyncHelper::RemoveLoginsCreatedBetween(
// because sync codebase needs to update metadata atomically together with
// the login data.
CommitTransaction();
- metrics_recorder.RecordMetrics(
- success,
- /*error=*/success
- ? absl::nullopt
- : absl::optional<ErrorFromPasswordStoreOrAndroidBackend>(
- PasswordStoreBackendError::kUnrecoverable));
- return changes;
+ return success ? changes
+ : PasswordChangesOrError(
+ PasswordStoreBackendError::kUnrecoverable);
}
-PasswordStoreChangeList LoginDatabaseAsyncHelper::RemoveLoginsByURLAndTime(
+PasswordChangesOrError LoginDatabaseAsyncHelper::RemoveLoginsByURLAndTime(
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
- base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreBackendMetricsRecorder metrics_recorder) {
+ base::OnceCallback<void(bool)> sync_completion) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
BeginTransaction();
PrimaryKeyToFormMap key_to_form_map;
@@ -298,13 +252,9 @@ PasswordStoreChangeList LoginDatabaseAsyncHelper::RemoveLoginsByURLAndTime(
if (!GetMetadataStore()->HasUnsyncedDeletions())
NotifyDeletionsHaveSynced(/*success=*/true);
}
- metrics_recorder.RecordMetrics(
- success,
- /*error=*/success
- ? absl::nullopt
- : absl::optional<ErrorFromPasswordStoreOrAndroidBackend>(
- PasswordStoreBackendError::kUnrecoverable));
- return changes;
+ return success ? changes
+ : PasswordChangesOrError(
+ PasswordStoreBackendError::kUnrecoverable);
}
PasswordStoreChangeList LoginDatabaseAsyncHelper::DisableAutoSignInForOrigins(
diff --git a/chromium/components/password_manager/core/browser/login_database_async_helper.h b/chromium/components/password_manager/core/browser/login_database_async_helper.h
index c7526fb101d..f3aa267e1eb 100644
--- a/chromium/components/password_manager/core/browser/login_database_async_helper.h
+++ b/chromium/components/password_manager/core/browser/login_database_async_helper.h
@@ -9,7 +9,6 @@
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "components/password_manager/core/browser/password_store_backend.h"
-#include "components/password_manager/core/browser/password_store_backend_metrics_recorder.h"
#include "components/password_manager/core/browser/password_store_sync.h"
namespace syncer {
@@ -42,33 +41,22 @@ class LoginDatabaseAsyncHelper : private PasswordStoreSync {
base::RepeatingClosure sync_enabled_or_disabled_cb);
// Synchronous implementation of PasswordStoreBackend interface.
- LoginsResult GetAllLogins(
- PasswordStoreBackendMetricsRecorder metrics_recorder);
- LoginsResult GetAutofillableLogins(
- PasswordStoreBackendMetricsRecorder metrics_recorder);
- LoginsResult FillMatchingLogins(
+ LoginsResultOrError GetAllLogins();
+ LoginsResultOrError GetAutofillableLogins();
+ LoginsResultOrError FillMatchingLogins(
const std::vector<PasswordFormDigest>& forms,
- bool include_psl,
- PasswordStoreBackendMetricsRecorder metrics_recorder);
- PasswordStoreChangeList AddLogin(
- const PasswordForm& form,
- PasswordStoreBackendMetricsRecorder metrics_recorder);
- PasswordStoreChangeList UpdateLogin(
- const PasswordForm& form,
- PasswordStoreBackendMetricsRecorder metrics_recorder);
- PasswordStoreChangeList RemoveLogin(
- const PasswordForm& form,
- PasswordStoreBackendMetricsRecorder metrics_recorder);
- PasswordStoreChangeList RemoveLoginsCreatedBetween(
- base::Time delete_begin,
- base::Time delete_end,
- PasswordStoreBackendMetricsRecorder metrics_recorder);
- PasswordStoreChangeList RemoveLoginsByURLAndTime(
+ bool include_psl);
+
+ PasswordChangesOrError AddLogin(const PasswordForm& form);
+ PasswordChangesOrError UpdateLogin(const PasswordForm& form);
+ PasswordChangesOrError RemoveLogin(const PasswordForm& form);
+ PasswordChangesOrError RemoveLoginsCreatedBetween(base::Time delete_begin,
+ base::Time delete_end);
+ PasswordChangesOrError RemoveLoginsByURLAndTime(
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
- base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreBackendMetricsRecorder metrics_recorder);
+ base::OnceCallback<void(bool)> sync_completion);
PasswordStoreChangeList DisableAutoSignInForOrigins(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter);
diff --git a/chromium/components/password_manager/core/browser/login_database_unittest.cc b/chromium/components/password_manager/core/browser/login_database_unittest.cc
index d91d069ca85..fa6b7037000 100644
--- a/chromium/components/password_manager/core/browser/login_database_unittest.cc
+++ b/chromium/components/password_manager/core/browser/login_database_unittest.cc
@@ -2092,7 +2092,7 @@ TEST_F(LoginDatabaseUndecryptableLoginsTest, DeleteUndecryptableLoginsTest) {
base::HistogramTester histogram_tester;
ASSERT_TRUE(db.Init());
-#if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
// Make sure that we can't get any logins when database is corrupted.
// Disabling the checks in chromecast because encryption is unavailable.
std::vector<std::unique_ptr<PasswordForm>> result;
@@ -2110,7 +2110,7 @@ TEST_F(LoginDatabaseUndecryptableLoginsTest, DeleteUndecryptableLoginsTest) {
EXPECT_THAT(result, IsEmpty());
RunUntilIdle();
-#elif (BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_CHROMECAST))
+#elif BUILDFLAG(IS_CASTOS)
EXPECT_EQ(DatabaseCleanupResult::kEncryptionUnavailable,
db.DeleteUndecryptableLogins());
#else
@@ -2118,7 +2118,7 @@ TEST_F(LoginDatabaseUndecryptableLoginsTest, DeleteUndecryptableLoginsTest) {
#endif
// Check histograms.
-#if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMECAST))
+#if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
histogram_tester.ExpectUniqueSample("PasswordManager.CleanedUpPasswords", 2,
1);
histogram_tester.ExpectUniqueSample(
@@ -2360,7 +2360,7 @@ TEST_F(LoginDatabaseTest, RetrievesNoteWithLogin) {
/* should_PSL_matching_apply */ true, &results));
PasswordForm expected_form = form;
- expected_form.note = note;
+ expected_form.notes = {note};
EXPECT_THAT(results, UnorderedElementsAre(Pointee(expected_form)));
}
@@ -2370,62 +2370,30 @@ TEST_F(LoginDatabaseTest, AddLoginWithNotePersistsThem) {
PasswordForm form = GenerateExamplePasswordForm();
PasswordNote note(u"example note", base::Time::Now());
- form.note = note;
+ form.notes = {note};
std::ignore = db().AddLogin(form);
- EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
+ EXPECT_EQ(db().password_notes_table().GetPasswordNotes(FormPrimaryKey(1))[0],
note);
}
-TEST_F(LoginDatabaseTest, AddLoginWithEmptyNoteDeletesTheNote) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(features::kPasswordNotes);
-
- PasswordForm form = GenerateExamplePasswordForm();
- form.note = PasswordNote(std::u16string(), base::Time::Now());
-
- std::ignore = db().AddLogin(form);
-
- EXPECT_THAT(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
- absl::nullopt);
-}
-
-TEST_F(LoginDatabaseTest, UpdateLoginWithEmptyNoteDeletesExistingNote) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(features::kPasswordNotes);
-
- PasswordForm form = GenerateExamplePasswordForm();
- PasswordNote note = PasswordNote(u"example note", base::Time::Now());
- form.note = note;
-
- std::ignore = db().AddLogin(form);
- EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
- note);
-
- form.note = PasswordNote(u"", base::Time::Now());
- std::ignore = db().UpdateLogin(form);
-
- EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
- absl::nullopt);
-}
-
TEST_F(LoginDatabaseTest, RemoveLoginRemovesNoteAttachedToTheLogin) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kPasswordNotes);
PasswordForm form = GenerateExamplePasswordForm();
PasswordNote note = PasswordNote(u"example note", base::Time::Now());
- form.note = note;
+ form.notes = {note};
std::ignore = db().AddLogin(form);
- EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
+ EXPECT_EQ(db().password_notes_table().GetPasswordNotes(FormPrimaryKey(1))[0],
note);
PasswordStoreChangeList list;
EXPECT_TRUE(db().RemoveLogin(form, &list));
- EXPECT_EQ(db().password_notes_table().GetPasswordNote(FormPrimaryKey(1)),
- absl::nullopt);
+ EXPECT_TRUE(
+ db().password_notes_table().GetPasswordNotes(FormPrimaryKey(1)).empty());
}
TEST_F(LoginDatabaseTest, RemovingLoginRemovesInsecureCredentials) {
diff --git a/chromium/components/password_manager/core/browser/manage_passwords_referrer.h b/chromium/components/password_manager/core/browser/manage_passwords_referrer.h
index cdc8a551da0..cef995aad6d 100644
--- a/chromium/components/password_manager/core/browser/manage_passwords_referrer.h
+++ b/chromium/components/password_manager/core/browser/manage_passwords_referrer.h
@@ -52,7 +52,13 @@ enum class ManagePasswordsReferrer {
// On Desktop, the Google Password Manager link was clicked in the footer of
// Save/Update bubble.
kSaveUpdateBubble = 11,
- kMaxValue = kSaveUpdateBubble,
+ // On Desktop, the Google Password Manager link was clicked in the password
+ // generation prompt in the Autofill dropdown.
+ kPasswordGenerationPrompt = 12,
+ // Corresponds to the situation when Chrome opens native Password Manager UI
+ // when navigating to specified website.
+ kPasswordsGoogleWebsite = 13,
+ kMaxValue = kPasswordsGoogleWebsite,
};
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/mock_password_change_success_tracker.h b/chromium/components/password_manager/core/browser/mock_password_change_success_tracker.h
index 43ff34fd2cc..3bc6636b85e 100644
--- a/chromium/components/password_manager/core/browser/mock_password_change_success_tracker.h
+++ b/chromium/components/password_manager/core/browser/mock_password_change_success_tracker.h
@@ -16,14 +16,39 @@ class MockPasswordChangeSuccessTracker : public PasswordChangeSuccessTracker {
MockPasswordChangeSuccessTracker();
~MockPasswordChangeSuccessTracker() override;
- MOCK_METHOD3(OnChangePasswordFlowStarted,
- void(const GURL& url,
- const std::string& username,
- StartEvent event_type));
- MOCK_METHOD3(OnChangePasswordFlowCompleted,
- void(const GURL& url,
- const std::string& username,
- EndEvent event_type));
+ MOCK_METHOD(void,
+ OnChangePasswordFlowStarted,
+ (const GURL& url,
+ const std::string& username,
+ StartEvent event_type,
+ EntryPoint entry_point),
+ (override));
+
+ MOCK_METHOD(void,
+ OnManualChangePasswordFlowStarted,
+ (const GURL& url,
+ const std::string& username,
+ EntryPoint entry_point),
+ (override));
+
+ MOCK_METHOD(void,
+ OnChangePasswordFlowModified,
+ (const GURL& url, StartEvent new_event_type),
+ (override));
+
+ MOCK_METHOD(void,
+ OnChangePasswordFlowModified,
+ (const GURL& url,
+ const std::string& username,
+ StartEvent new_event_type),
+ (override));
+
+ MOCK_METHOD(void,
+ OnChangePasswordFlowCompleted,
+ (const GURL& url,
+ const std::string& username,
+ EndEvent event_type),
+ (override));
};
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/mock_password_manager_settings_service.cc b/chromium/components/password_manager/core/browser/mock_password_manager_settings_service.cc
new file mode 100644
index 00000000000..e031ba282da
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/mock_password_manager_settings_service.cc
@@ -0,0 +1,10 @@
+// Copyright 2022 The Chromium 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/password_manager/core/browser/mock_password_manager_settings_service.h"
+
+MockPasswordManagerSettingsService::MockPasswordManagerSettingsService() =
+ default;
+MockPasswordManagerSettingsService::~MockPasswordManagerSettingsService() =
+ default;
diff --git a/chromium/components/password_manager/core/browser/mock_password_manager_settings_service.h b/chromium/components/password_manager/core/browser/mock_password_manager_settings_service.h
new file mode 100644
index 00000000000..494c307183c
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/mock_password_manager_settings_service.h
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_PASSWORD_MANAGER_SETTINGS_SERVICE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_PASSWORD_MANAGER_SETTINGS_SERVICE_H_
+
+#include "components/password_manager/core/browser/password_manager_setting.h"
+#include "components/password_manager/core/browser/password_manager_settings_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+class MockPasswordManagerSettingsService
+ : public PasswordManagerSettingsService {
+ public:
+ MockPasswordManagerSettingsService();
+ ~MockPasswordManagerSettingsService() override;
+
+ MOCK_METHOD(bool,
+ IsSettingEnabled,
+ (password_manager::PasswordManagerSetting),
+ (override));
+ MOCK_METHOD(void, RequestSettingsFromBackend, (), (override));
+
+ MOCK_METHOD(void, TurnOffAutoSignIn, (), (override));
+};
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_PASSWORD_MANAGER_SETTINGS_SERVICE_H_
diff --git a/chromium/components/password_manager/core/browser/mock_password_store_backend.h b/chromium/components/password_manager/core/browser/mock_password_store_backend.h
index 9cd33fd0719..b4c05263c8b 100644
--- a/chromium/components/password_manager/core/browser/mock_password_store_backend.h
+++ b/chromium/components/password_manager/core/browser/mock_password_store_backend.h
@@ -58,21 +58,21 @@ class MockPasswordStoreBackend : public PasswordStoreBackend {
(override));
MOCK_METHOD(void,
FillMatchingLoginsAsync,
- (LoginsReply callback,
+ (LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms),
(override));
MOCK_METHOD(void,
AddLoginAsync,
- (const PasswordForm& form, PasswordStoreChangeListReply callback),
+ (const PasswordForm& form, PasswordChangesOrErrorReply callback),
(override));
MOCK_METHOD(void,
UpdateLoginAsync,
- (const PasswordForm& form, PasswordStoreChangeListReply callback),
+ (const PasswordForm& form, PasswordChangesOrErrorReply callback),
(override));
MOCK_METHOD(void,
RemoveLoginAsync,
- (const PasswordForm& form, PasswordStoreChangeListReply callback),
+ (const PasswordForm& form, PasswordChangesOrErrorReply callback),
(override));
MOCK_METHOD(void,
RemoveLoginsByURLAndTimeAsync,
@@ -80,13 +80,13 @@ class MockPasswordStoreBackend : public PasswordStoreBackend {
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback),
+ PasswordChangesOrErrorReply callback),
(override));
MOCK_METHOD(void,
RemoveLoginsCreatedBetweenAsync,
(base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback),
+ PasswordChangesOrErrorReply callback),
(override));
MOCK_METHOD(void,
DisableAutoSignInForOriginsAsync,
diff --git a/chromium/components/password_manager/core/browser/move_password_to_account_store_helper.cc b/chromium/components/password_manager/core/browser/move_password_to_account_store_helper.cc
index fd3f2a2ffeb..2c25ef8d00c 100644
--- a/chromium/components/password_manager/core/browser/move_password_to_account_store_helper.cc
+++ b/chromium/components/password_manager/core/browser/move_password_to_account_store_helper.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/barrier_closure.h"
#include "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/password_form_digest.h"
#include "components/password_manager/core/browser/password_form_metrics_recorder.h"
@@ -13,6 +14,23 @@
namespace password_manager {
+namespace {
+
+// Object which holds a vector of MovePasswordToAccountStoreHelper.
+class MovePasswordOperations {
+ public:
+ void ClearAllOperations() { helpers_.clear(); }
+
+ void AddHelper(std::unique_ptr<MovePasswordToAccountStoreHelper> helper) {
+ helpers_.push_back(std::move(helper));
+ }
+
+ private:
+ std::vector<std::unique_ptr<MovePasswordToAccountStoreHelper>> helpers_;
+};
+
+} // namespace
+
MovePasswordToAccountStoreHelper::MovePasswordToAccountStoreHelper(
const PasswordForm& form,
PasswordManagerClient* client,
@@ -45,4 +63,23 @@ void MovePasswordToAccountStoreHelper::OnFetchCompleted() {
// |this| might be deleted now!
}
+void MovePasswordsToAccountStore(
+ const std::vector<PasswordForm>& forms,
+ PasswordManagerClient* client,
+ metrics_util::MoveToAccountStoreTrigger trigger) {
+ std::unique_ptr<MovePasswordOperations> operations =
+ std::make_unique<MovePasswordOperations>();
+
+ const raw_ptr<MovePasswordOperations> raw_helper = operations.get();
+
+ auto repeating_callback = base::BarrierClosure(
+ forms.size(), base::BindOnce(&MovePasswordOperations::ClearAllOperations,
+ base::Owned(std::move(operations))));
+
+ for (const auto& form : forms) {
+ raw_helper->AddHelper(std::make_unique<MovePasswordToAccountStoreHelper>(
+ form, client, trigger, repeating_callback));
+ }
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/move_password_to_account_store_helper.h b/chromium/components/password_manager/core/browser/move_password_to_account_store_helper.h
index 9845b9b18e7..7a54ad563de 100644
--- a/chromium/components/password_manager/core/browser/move_password_to_account_store_helper.h
+++ b/chromium/components/password_manager/core/browser/move_password_to_account_store_helper.h
@@ -39,6 +39,13 @@ class MovePasswordToAccountStoreHelper : public FormFetcher::Consumer {
std::unique_ptr<FormFetcher> form_fetcher_;
};
+// Helper functions which moves a batch of passwords and takes care of memory
+// management.
+void MovePasswordsToAccountStore(
+ const std::vector<PasswordForm>& forms,
+ PasswordManagerClient* client,
+ metrics_util::MoveToAccountStoreTrigger trigger);
+
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOVE_PASSWORD_TO_ACCOUNT_STORE_HELPER_H_
diff --git a/chromium/components/password_manager/core/browser/password_access_authenticator.cc b/chromium/components/password_manager/core/browser/password_access_authenticator.cc
index 1c4f947d686..0465b38e95f 100644
--- a/chromium/components/password_manager/core/browser/password_access_authenticator.cc
+++ b/chromium/components/password_manager/core/browser/password_access_authenticator.cc
@@ -30,7 +30,11 @@ PasswordAccessAuthenticator::~PasswordAccessAuthenticator() = default;
void PasswordAccessAuthenticator::EnsureUserIsAuthenticated(
ReauthPurpose purpose,
AuthResultCallback callback) {
- if (base::Time::Now() <= last_authentication_time_ + kAuthValidityPeriod) {
+ // This is to address crbug.com/1317549. If the current time is earlier than the
+ // `last_authentication_time_`, `last_authentication_time_` is invalid.
+ // So need ForceUserReauthentication()
+ if (last_authentication_time_ < base::Time::Now() &&
+ base::Time::Now() <= last_authentication_time_ + kAuthValidityPeriod) {
LogPasswordSettingsReauthResult(ReauthResult::kSkipped);
std::move(callback).Run(true);
} else {
diff --git a/chromium/components/password_manager/core/browser/password_autofill_manager.cc b/chromium/components/password_manager/core/browser/password_autofill_manager.cc
index 606755fe02f..6bcfd28acf8 100644
--- a/chromium/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_autofill_manager.cc
@@ -124,12 +124,13 @@ void AppendSuggestionIfMatching(
bool replaced_username;
autofill::Suggestion suggestion(
ReplaceEmptyUsername(field_suggestion, &replaced_username));
- suggestion.is_value_secondary = replaced_username;
+ suggestion.main_text.is_primary =
+ autofill::Suggestion::Text::IsPrimary(!replaced_username);
suggestion.label = GetHumanReadableRealm(signon_realm);
suggestion.additional_label =
std::u16string(password_length, kPasswordReplacementChar);
suggestion.voice_over = l10n_util::GetStringFUTF16(
- IDS_PASSWORD_MANAGER_PASSWORD_FOR_ACCOUNT, suggestion.value);
+ IDS_PASSWORD_MANAGER_PASSWORD_FOR_ACCOUNT, suggestion.main_text.value);
if (!suggestion.label.empty()) {
// The domainname is only shown for passwords with a common eTLD+1
// but different subdomain.
@@ -187,7 +188,7 @@ void GetSuggestions(const autofill::PasswordFormFillData& fill_data,
std::sort(suggestions->begin() + prefered_match, suggestions->end(),
[](const autofill::Suggestion& a, const autofill::Suggestion& b) {
- return a.value < b.value;
+ return a.main_text.value < b.main_text.value;
});
// Prefix matches should precede other token matches.
@@ -418,10 +419,11 @@ void PasswordAutofillManager::OnUnlockItemAccepted(
autofill_client_->GetReopenPopupArgs()));
}
-void PasswordAutofillManager::DidAcceptSuggestion(const std::u16string& value,
- int frontend_id,
- const std::string& backend_id,
- int position) {
+void PasswordAutofillManager::DidAcceptSuggestion(
+ const std::u16string& value,
+ int frontend_id,
+ const autofill::Suggestion::Payload& payload,
+ int position) {
using metrics_util::PasswordDropdownSelectedOption;
if (frontend_id == autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY) {
password_client_->GeneratePassword(PasswordGenerationType::kAutomatic);
@@ -466,7 +468,9 @@ void PasswordAutofillManager::DidAcceptSuggestion(const std::u16string& value,
PasswordDropdownSelectedOption::kWebAuthn,
password_client_->IsIncognito());
password_client_->GetWebAuthnCredentialsDelegate()
- ->SelectWebAuthnCredential(backend_id);
+ ->SelectWebAuthnCredential(absl::holds_alternative<std::string>(payload)
+ ? absl::get<std::string>(payload)
+ : std::string());
} else {
metrics_util::LogPasswordDropdownItemSelected(
PasswordDropdownSelectedOption::kPassword,
@@ -665,10 +669,9 @@ std::vector<autofill::Suggestion> PasswordAutofillManager::BuildSuggestions(
}
// Add WebAuthn credentials suitable for an ongoing request if available.
- if (show_webauthn_credentials) {
- WebAuthnCredentialsDelegate* delegate =
- password_client_->GetWebAuthnCredentialsDelegate();
- DCHECK(delegate->IsWebAuthnAutofillEnabled());
+ WebAuthnCredentialsDelegate* delegate =
+ password_client_->GetWebAuthnCredentialsDelegate();
+ if (show_webauthn_credentials && delegate->IsWebAuthnAutofillEnabled()) {
std::vector<autofill::Suggestion> webauthn_suggestions =
delegate->GetWebAuthnSuggestions();
suggestions.insert(suggestions.end(), webauthn_suggestions.begin(),
diff --git a/chromium/components/password_manager/core/browser/password_autofill_manager.h b/chromium/components/password_manager/core/browser/password_autofill_manager.h
index c4599c8f928..fcbae65bfdd 100644
--- a/chromium/components/password_manager/core/browser/password_autofill_manager.h
+++ b/chromium/components/password_manager/core/browser/password_autofill_manager.h
@@ -15,6 +15,7 @@
#include "base/types/strong_alias.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/ui/autofill_popup_delegate.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
@@ -58,7 +59,7 @@ class PasswordAutofillManager : public autofill::AutofillPopupDelegate {
const std::string& backend_id) override;
void DidAcceptSuggestion(const std::u16string& value,
int frontend_id,
- const std::string& backend_id,
+ const autofill::Suggestion::Payload& payload,
int position) override;
bool GetDeletionConfirmationText(const std::u16string& value,
int frontend_id,
diff --git a/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index cdd00337361..da8b395a060 100644
--- a/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -70,7 +70,7 @@ using autofill::Suggestion;
using autofill::SuggestionVectorIconsAre;
using autofill::SuggestionVectorIdsAre;
using autofill::SuggestionVectorLabelsAre;
-using autofill::SuggestionVectorValuesAre;
+using autofill::SuggestionVectorMainTextsAre;
using autofill::password_generation::PasswordGenerationType;
using base::test::RunOnceCallback;
using device_reauth::BiometricAuthRequester;
@@ -964,10 +964,15 @@ TEST_F(PasswordAutofillManagerTest, ExtractSuggestions) {
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, std::u16string(),
autofill::ShowPasswordSuggestionsOptions(), element_bounds);
- EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(testing::UnorderedElementsAre(
- test_username_, u"Gohn Foo", u"John Foo", u"Kohn Foo",
- GetManagePasswordsTitle())));
+ EXPECT_THAT(
+ open_args.suggestions,
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(test_username_, Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(u"Gohn Foo", Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(u"John Foo", Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(u"Kohn Foo", Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
EXPECT_THAT(open_args.suggestions,
SuggestionVectorLabelsAre(testing::Contains(u"foo.com")));
EXPECT_THAT(open_args.suggestions,
@@ -980,18 +985,26 @@ TEST_F(PasswordAutofillManagerTest, ExtractSuggestions) {
base::i18n::RIGHT_TO_LEFT, u"John",
autofill::ShowPasswordSuggestionsOptions(), element_bounds);
EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(
- ElementsAre(additional.username, GetManagePasswordsTitle())));
+ SuggestionVectorMainTextsAre(testing::ElementsAre(
+ Suggestion::Text(additional.username,
+ Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
// Finally, simulate displaying all suggestions, without any prefix matching.
EXPECT_CALL(autofill_client, ShowAutofillPopup)
.WillOnce(testing::SaveArg<0>(&open_args));
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, u"xyz", autofill::SHOW_ALL, element_bounds);
- EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(
- ElementsAre(test_username_, u"Gohn Foo", u"John Foo",
- u"Kohn Foo", GetManagePasswordsTitle())));
+ EXPECT_THAT(
+ open_args.suggestions,
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(test_username_, Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(u"Gohn Foo", Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(u"John Foo", Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(u"Kohn Foo", Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
}
// Verify that, for Android application credentials, the prettified realms of
@@ -1050,9 +1063,12 @@ TEST_F(PasswordAutofillManagerTest, FillSuggestionPasswordField) {
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, test_username_, autofill::IS_PASSWORD_FIELD,
element_bounds);
- EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(
- ElementsAre(test_username_, GetManagePasswordsTitle())));
+ EXPECT_THAT(
+ open_args.suggestions,
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(test_username_, Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
EXPECT_FALSE(open_args.autoselect_first_suggestion);
EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
}
@@ -1089,8 +1105,12 @@ TEST_F(PasswordAutofillManagerTest, DisplaySuggestionsWithMatchingTokens) {
base::i18n::RIGHT_TO_LEFT, u"foo",
autofill::ShowPasswordSuggestionsOptions(), element_bounds);
EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(testing::UnorderedElementsAre(
- username, additional.username, GetManagePasswordsTitle())));
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(username, Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(additional.username,
+ Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
EXPECT_FALSE(open_args.autoselect_first_suggestion);
EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
}
@@ -1160,8 +1180,11 @@ TEST_F(PasswordAutofillManagerTest,
base::i18n::RIGHT_TO_LEFT, u"foo@exam",
autofill::ShowPasswordSuggestionsOptions(), element_bounds);
EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(
- ElementsAre(additional.username, GetManagePasswordsTitle())));
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(additional.username,
+ Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
EXPECT_FALSE(open_args.autoselect_first_suggestion);
EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
}
@@ -1200,8 +1223,13 @@ TEST_F(PasswordAutofillManagerTest,
base::i18n::RIGHT_TO_LEFT, u"foo",
autofill::ShowPasswordSuggestionsOptions(), element_bounds);
EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(ElementsAre(
- username, additional.username, GetManagePasswordsTitle())));
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(username, Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(additional.username,
+ Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
+
EXPECT_FALSE(open_args.autoselect_first_suggestion);
EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
}
@@ -1270,9 +1298,12 @@ TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnPasswordField) {
histograms.ExpectUniqueSample(kDropdownShownHistogram,
metrics_util::PasswordDropdownState::kStandard,
1);
- EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(
- ElementsAre(test_username_, GetManagePasswordsTitle())));
+ EXPECT_THAT(
+ open_args.suggestions,
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(test_username_, Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
// Clicking at the "Show all passwords row" should trigger a call to open
// the Password Manager settings page and hide the popup.
@@ -1323,9 +1354,12 @@ TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnNonPasswordField) {
password_autofill_manager_->OnShowPasswordSuggestions(
base::i18n::RIGHT_TO_LEFT, test_username_,
autofill::ShowPasswordSuggestionsOptions(), element_bounds);
- EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(
- ElementsAre(test_username_, GetManagePasswordsTitle())));
+ EXPECT_THAT(
+ open_args.suggestions,
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(test_username_, Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
EXPECT_FALSE(open_args.autoselect_first_suggestion);
EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
}
@@ -1376,9 +1410,14 @@ TEST_F(PasswordAutofillManagerTest,
EXPECT_THAT(open_args.suggestions,
SuggestionVectorIconsAre(ElementsAre("globeIcon", "keyIcon",
GetManagePasswordsIcon())));
- EXPECT_THAT(open_args.suggestions, SuggestionVectorValuesAre(ElementsAre(
- test_username_, generation_string,
- GetManagePasswordsTitle())));
+ EXPECT_THAT(
+ open_args.suggestions,
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(test_username_, Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(generation_string,
+ Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
// Click "Generate password".
EXPECT_CALL(client, GeneratePassword(PasswordGenerationType::kAutomatic));
@@ -1420,9 +1459,13 @@ TEST_F(PasswordAutofillManagerTest,
/*show_password_suggestions=*/false));
EXPECT_THAT(open_args.suggestions, SuggestionVectorIconsAre(ElementsAre(
"keyIcon", GetManagePasswordsIcon())));
+
EXPECT_THAT(open_args.suggestions,
- SuggestionVectorValuesAre(
- ElementsAre(generation_string, GetManagePasswordsTitle())));
+ SuggestionVectorMainTextsAre(ElementsAre(
+ Suggestion::Text(generation_string,
+ Suggestion::Text::IsPrimary(true)),
+ Suggestion::Text(GetManagePasswordsTitle(),
+ Suggestion::Text::IsPrimary(true)))));
}
// Test that if the "opt in and generate" button gets displayed, the regular
@@ -1796,7 +1839,7 @@ TEST_F(PasswordAutofillManagerTest, ShowsWebAuthnSuggestions) {
const std::u16string kDisplayName = u"Nadeshiko Kagamihara";
autofill::Suggestion webauthn_credential(kDisplayName);
webauthn_credential.frontend_id = autofill::POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL;
- webauthn_credential.backend_id = kId;
+ webauthn_credential.payload = kId;
webauthn_credential.label = kName;
ON_CALL(webauthn_credentials_delegate, IsWebAuthnAutofillEnabled)
.WillByDefault(Return(true));
@@ -1821,10 +1864,10 @@ TEST_F(PasswordAutofillManagerTest, ShowsWebAuthnSuggestions) {
autofill::POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL,
autofill::POPUP_ITEM_ID_USERNAME_ENTRY,
autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY)));
- EXPECT_EQ(open_args.suggestions[0].backend_id, kId);
+ EXPECT_EQ(absl::get<std::string>(open_args.suggestions[0].payload), kId);
EXPECT_EQ(open_args.suggestions[0].frontend_id,
autofill::POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL);
- EXPECT_EQ(open_args.suggestions[0].value, kDisplayName);
+ EXPECT_EQ(open_args.suggestions[0].main_text.value, kDisplayName);
EXPECT_EQ(open_args.suggestions[0].label, kName);
testing::Mock::VerifyAndClearExpectations(client.mock_driver());
diff --git a/chromium/components/password_manager/core/browser/password_bubble_experiment.cc b/chromium/components/password_manager/core/browser/password_bubble_experiment.cc
index 720d9cb71ce..94f492d9993 100644
--- a/chromium/components/password_manager/core/browser/password_bubble_experiment.cc
+++ b/chromium/components/password_manager/core/browser/password_bubble_experiment.cc
@@ -45,9 +45,4 @@ void RecordAutoSignInPromptFirstRunExperienceWasShown(PrefService* prefs) {
password_manager::prefs::kWasAutoSignInFirstRunExperienceShown, true);
}
-void TurnOffAutoSignin(PrefService* prefs) {
- prefs->SetBoolean(password_manager::prefs::kCredentialsEnableAutosignin,
- false);
-}
-
} // namespace password_bubble_experiment
diff --git a/chromium/components/password_manager/core/browser/password_bubble_experiment.h b/chromium/components/password_manager/core/browser/password_bubble_experiment.h
index b21ea62ab18..6d889ff09fd 100644
--- a/chromium/components/password_manager/core/browser/password_bubble_experiment.h
+++ b/chromium/components/password_manager/core/browser/password_bubble_experiment.h
@@ -27,9 +27,6 @@ bool ShouldShowAutoSignInPromptFirstRunExperience(PrefService* prefs);
// first run experience for the auto sign-in prompt.
void RecordAutoSignInPromptFirstRunExperienceWasShown(PrefService* prefs);
-// Turns off the auto signin experience setting.
-void TurnOffAutoSignin(PrefService* prefs);
-
} // namespace password_bubble_experiment
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_BUBBLE_EXPERIMENT_H_
diff --git a/chromium/components/password_manager/core/browser/password_change_success_tracker.h b/chromium/components/password_manager/core/browser/password_change_success_tracker.h
index 03a54947da5..0effab968f0 100644
--- a/chromium/components/password_manager/core/browser/password_change_success_tracker.h
+++ b/chromium/components/password_manager/core/browser/password_change_success_tracker.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/time/time.h"
#include "components/keyed_service/core/keyed_service.h"
class GURL;
@@ -14,61 +15,133 @@ class GURL;
namespace password_manager {
// Abstract interface to track whether a change password flow leads to a
-// successful password update.
+// successful update of a credential.
// Usage notes:
-// - |OnChangePasswordFlowStarted| is supposed to be called when a change flow
-// starts.
-// - |OnChangePasswordFlowSucceeded| is supposed to be called when a compromised
-// password is updated or at the end of kAutomatedResetLinkRequestFlow when
-// the email with the reset link or code was requested.
-// - |url| param is the url of the current page. The tracker is supposed to
-// normalize it to eTLD+1 for matching.
+// - The type of a change password flow is defined by three enums, a
+// |StartEvent| defining how the flow was started, an |EndEvent| defining
+// how the flow terminated and an |EntryPoint| defining from where in the
+// browser the flow was started. See the enum definitions for details.
+// - URL parameters in the passed to the tracker are the URLs of the current
+// page. The tracker normalizes these to eTLD+1 for matching.
class PasswordChangeSuccessTracker : public KeyedService {
public:
+ // Timeout length between the start and end events of a password change flow.
+ // Since the flows include password reset for which reset emails may be
+ // delayed, a somewhat lengthy timeout is chosen.
+ static constexpr base::TimeDelta kFlowTimeout = base::Minutes(60);
+
+ // Timeout length between the start of a manual change flow and its type
+ // refinement. The expectation is that this should be near instant and
+ // the timeout is just a safe-keeping measure.
+ static constexpr base::TimeDelta kFlowTypeRefinementTimeout =
+ base::Seconds(30);
+
// Start and end events for automated (i.e. Autofill Assistant-driven) and
// manual flows (i.e. Chrome opens a CCT and a user updates a password on
// their own).
+ // These values are persisted to prefs and used in enums.xml; do not reorder
+ // or renumber entries!
enum class StartEvent {
- // Some automated flow started. A specific type will be known only at the
- // end.
+ // An automated password change flow.
kAutomatedFlow = 0,
- // Flow started with opening an origin of a stored credential (which is
- // supposed to be the homepage).
- kManualHomepageFlow = 1,
- // Flow started with opening the .well-known/change-password path.
- KManualWellKnownUrlFlow = 2,
- // Flow started with opening a domain-specific URL.
+ // A manual password change flow of unknown type since no navigation has
+ // been attempted yet.
+ kManualUnknownFlow = 1,
+
+ // A manual password change flow for a domain that supports
+ // /.well-known/change-password.
+ kManualWellKnownUrlFlow = 2,
+
+ // A manual password change flow with a domain-specific URL.
kManualChangePasswordUrlFlow = 3,
- // Flow started as an automated flow that requested a reset link, now a user
- // is supposed to open the link and update the password manually. Should be
- // used only internally when kAutomatedResetLinkRequestFlow ends.
- kManualResetLinkOpenningFlow = 4,
+ // A manual password change flow that starts at the origin of a stored
+ // credential (supposed to be the homepage).
+ kManualHomepageFlow = 4,
+
+ // A manual password reset flow. This StartEvent can currently only be
+ // triggered during an automated password change flow for which login fails
+ // and the user chooses to request a password reset.
+ kManualResetLinkFlow = 5,
+
+ kMaxValue = kManualResetLinkFlow
};
+
+ // These values are persisted to prefs and used in enums.xml; do not reorder
+ // or renumber entries!
enum class EndEvent {
- // Fully automated flow completed.
- kAutomatedGeneratedPasswordFlow = 0,
- // Flow started as an automated flow, but a user chose to create their own
- // password.
- kAutomatedOwnPasswordFlow = 1,
+ // Automated password change flow completed with a generated password.
+ kAutomatedFlowGeneratedPasswordChosen = 0,
+
+ // Automated password change flow completed with a user-chosen password.
+ kAutomatedFlowOwnPasswordChosen = 1,
+
// Password-reset link was requested. Autofill Assistant's part is done and
// a user is supposed to continue the flow on their own.
- kAutomatedResetLinkRequestFlow = 2,
+ kAutomatedFlowResetLinkRequested = 2,
+
+ // A manual password change flow or password reset flow completed with a
+ // generated password.
+ kManualFlowGeneratedPasswordChosen = 3,
- // Some manual flow completed. A specific type is known only at the start.
- kManualFlow = 3,
+ // A manual password change flow or password reset flow completed with a
+ // user-chosen password.
+ kManualFlowOwnPasswordChosen = 4,
+
+ // The password change flow timed out.
+ kTimeout = 5,
+
+ kMaxValue = kTimeout
};
- // Called when a change flow starts. Emits a report that a flow has started
- // and stores an entry to wait for a matching FlowSucceeded call. If there is
- // no matching call for a while, the entry will expire.
+ // The place in Chrome where the password change flow originated.
+ // These values are persisted to prefs and used in enums.xml; do not reorder
+ // or renumber entries!
+ enum class EntryPoint {
+ // Started after performing a password check in settings / the password
+ // manager.
+ kLeakCheckInSettings = 0,
+
+ // Started after receiving a warning after logging into a website with
+ // a leaked credential.
+ kLeakWarningDialog = 1,
+
+ kMaxValue = kLeakWarningDialog
+ };
+
+ // Called when a change flow starts and its |StartEvent| is fully known (
+ // currently true only for automated flows). It stores an entry to wait
+ // for a matching |OnChangePasswordFlowModified()| or
+ // |OnChangePasswordFlowCompleted()| call. Times out after |kFlowTimeout|
+ // if no matching EndEvent is received.
virtual void OnChangePasswordFlowStarted(const GURL& url,
const std::string& username,
- StartEvent event_type) = 0;
+ StartEvent event_type,
+ EntryPoint entry_point) = 0;
+
+ // Called when a manual change flow is started. At that point, the
+ // exact |StartEvent| (i.e. whether it supports .well-known/change-password)
+ // is not yet known and the flow is stored temporarily
+ // with |StartEvent::kManualUnknownFlow|.
+ virtual void OnManualChangePasswordFlowStarted(const GURL& url,
+ const std::string& username,
+ EntryPoint entry_point) = 0;
+
+ // Called when a change flow with an unknown username is refined, e.g. the
+ // exact |StartEvent| of a manual flow is specified.
+ virtual void OnChangePasswordFlowModified(const GURL& url,
+ StartEvent new_event_type) = 0;
+
+ // Call when a change flow with a known username is modified, e.g. a flow
+ // that started as an automated password change and became a password reset.
+ virtual void OnChangePasswordFlowModified(const GURL& url,
+ const std::string& username,
+ StartEvent new_event_type) = 0;
- // Called when a change flow succeeds. If there is a recent matching
- // FlowStarted call, it emits a report about a successful password change.
+ // Called when a change flow succeeds and a password is updated.
+ // If there is a matching flow that for which a start was registered,
+ // it emits a report about a successful password change.
virtual void OnChangePasswordFlowCompleted(const GURL& url,
const std::string& username,
EndEvent event_type) = 0;
diff --git a/chromium/components/password_manager/core/browser/password_change_success_tracker_impl.cc b/chromium/components/password_manager/core/browser/password_change_success_tracker_impl.cc
index e082438766a..fe0d85b8c43 100644
--- a/chromium/components/password_manager/core/browser/password_change_success_tracker_impl.cc
+++ b/chromium/components/password_manager/core/browser/password_change_success_tracker_impl.cc
@@ -4,26 +4,399 @@
#include "components/password_manager/core/browser/password_change_success_tracker_impl.h"
+#include "base/containers/circular_deque.h"
+#include "base/json/values_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "services/metrics/public/cpp/metrics_utils.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
+#include "url/origin.h"
+
+using base::StringPiece;
namespace password_manager {
-PasswordChangeSuccessTrackerImpl::PasswordChangeSuccessTrackerImpl() = default;
+namespace {
+
+constexpr char kKeyEtldPlus1[] = "etld_plus_1";
+constexpr char kKeyUsername[] = "username";
+constexpr char kKeyStartEvent[] = "start_event";
+constexpr char kKeyEntryPoint[] = "entry_point";
+constexpr char kKeyStartTime[] = "start_time";
+
+// Overloaded helper methods to convert the |PasswordChangeSuccessTracker|
+// enums to strings for building metrics keys.
+StringPiece SerializeEnumForUma(
+ PasswordChangeSuccessTracker::StartEvent event) {
+ switch (event) {
+ case PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow:
+ return ".AutomatedFlow";
+ // Combine all manual flows for UMA reporting to reduce number of
+ // histograms.
+ case PasswordChangeSuccessTracker::StartEvent::kManualUnknownFlow:
+ case PasswordChangeSuccessTracker::StartEvent::kManualWellKnownUrlFlow:
+ case PasswordChangeSuccessTracker::StartEvent::kManualChangePasswordUrlFlow:
+ case PasswordChangeSuccessTracker::StartEvent::kManualHomepageFlow:
+ return ".ManualFlow";
+ case PasswordChangeSuccessTracker::StartEvent::kManualResetLinkFlow:
+ return ".ManualResetLinkFlow";
+ }
+}
+
+StringPiece SerializeEnumForUma(PasswordChangeSuccessTracker::EndEvent event) {
+ switch (event) {
+ // Combine automated flow end events for UMA reporting.
+ case PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen:
+ case PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen:
+ return ".AutomatedFlowPasswordChosen";
+ case PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowResetLinkRequested:
+ return ".AutomatedFlowResetLinkRequested";
+ // Combine manual flow end events for UMA reporting.
+ case PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowGeneratedPasswordChosen:
+ case PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen:
+ return ".ManualFlowPasswordChosen";
+ case PasswordChangeSuccessTracker::EndEvent::kTimeout:
+ return ".Timeout";
+ }
+}
+
+StringPiece SerializeEnumForUma(
+ PasswordChangeSuccessTracker::EntryPoint entry_point) {
+ switch (entry_point) {
+ case PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings:
+ return ".LeakCheckInSettings";
+ case PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog:
+ return ".LeakWarningDialog";
+ }
+}
+
+// Helper method to create a flow serialized as a |Value::Dict|.
+base::Value::Dict CreateFlow(
+ const std::string& etld_plus_1,
+ const std::string& username,
+ PasswordChangeSuccessTracker::StartEvent start_event,
+ PasswordChangeSuccessTracker::EntryPoint entry_point,
+ base::Time start_time) {
+ base::Value::Dict flow;
+ flow.Set(kKeyEtldPlus1, base::Value(etld_plus_1));
+ flow.Set(kKeyUsername, base::Value(username));
+ // Cast enums to ints, since they are one of the supported types of
+ // |Value|.
+ flow.Set(kKeyStartEvent, base::Value(static_cast<int>(start_event)));
+ flow.Set(kKeyEntryPoint, base::Value(static_cast<int>(entry_point)));
+ flow.Set(kKeyStartTime, base::TimeToValue(start_time));
+
+ return flow;
+}
+
+} // namespace
+
+PasswordChangeMetricsRecorderUma::~PasswordChangeMetricsRecorderUma() = default;
+
+void PasswordChangeMetricsRecorderUma::OnFlowRecorded(
+ const std::string& etld_plus_1,
+ PasswordChangeSuccessTracker::StartEvent start_event,
+ PasswordChangeSuccessTracker::EndEvent end_event,
+ PasswordChangeSuccessTracker::EntryPoint entry_point,
+ base::TimeDelta duration) {
+ // Record metrics aggregated over end events.
+ std::string entry_key =
+ base::StrCat({kUmaKey, SerializeEnumForUma(entry_point),
+ SerializeEnumForUma(start_event)});
+ UmaHistogramLongTimes100(entry_key, duration);
+
+ // Record metrics specified by start and end events. This does not
+ // differentiate between different manual starts and between own or generated
+ // passwords.
+ base::StrAppend(&entry_key, {SerializeEnumForUma(end_event)});
+ UmaHistogramLongTimes100(entry_key, duration);
+}
+
+PasswordChangeMetricsRecorderUkm::~PasswordChangeMetricsRecorderUkm() = default;
+
+void PasswordChangeMetricsRecorderUkm::OnFlowRecorded(
+ const std::string& etld_plus_1,
+ PasswordChangeSuccessTracker::StartEvent start_event,
+ PasswordChangeSuccessTracker::EndEvent end_event,
+ PasswordChangeSuccessTracker::EntryPoint entry_point,
+ base::TimeDelta duration) {
+ int64_t bucketed_duration =
+ ukm::GetExponentialBucketMin(duration.InSeconds(), kBucketSpacing);
+ ukm::builders::PasswordManager_PasswordChangeFlowDuration(
+ ukm::NoURLSourceId())
+ .SetStartEvent(static_cast<int64_t>(start_event))
+ .SetEndEvent(static_cast<int64_t>(end_event))
+ .SetEntryPoint(static_cast<int64_t>(entry_point))
+ .SetDuration(bucketed_duration)
+ .Record(ukm::UkmRecorder::Get());
+}
+
+PasswordChangeSuccessTrackerImpl::IncompleteFlow::IncompleteFlow(
+ const std::string& etld_plus_1,
+ const std::string& username,
+ EntryPoint entry_point)
+ : etld_plus_1(etld_plus_1),
+ username(username),
+ entry_point(entry_point),
+ start_time(base::Time::Now()) {}
+
+PasswordChangeSuccessTrackerImpl::FlowView::FlowView(
+ const base::Value::Dict* value)
+ : value_(value) {
+ DCHECK(value_);
+}
+
+std::string PasswordChangeSuccessTrackerImpl::FlowView::GetEtldPlus1() const {
+ const std::string* etld_plus_1 = value_->FindString(kKeyEtldPlus1);
+ return etld_plus_1 ? *etld_plus_1 : std::string();
+}
+
+std::string PasswordChangeSuccessTrackerImpl::FlowView::GetUsername() const {
+ const std::string* username = value_->FindString(kKeyUsername);
+ return username ? *username : std::string();
+}
+
+PasswordChangeSuccessTracker::StartEvent
+PasswordChangeSuccessTrackerImpl::FlowView::GetStartEvent() const {
+ absl::optional<int> start_event = value_->FindInt(kKeyStartEvent);
+ // The value should never be empty and be within the range of the enum.
+ DCHECK(start_event.has_value());
+ DCHECK(start_event.value() >= 0);
+ DCHECK(start_event.value() <=
+ static_cast<int>(PasswordChangeSuccessTracker::StartEvent::kMaxValue));
+
+ return static_cast<PasswordChangeSuccessTracker::StartEvent>(
+ start_event.value());
+}
+
+PasswordChangeSuccessTracker::EntryPoint
+PasswordChangeSuccessTrackerImpl::FlowView::GetEntryPoint() const {
+ absl::optional<int> entry_point = value_->FindInt(kKeyEntryPoint);
+ // The value should never be empty and be within the range of the enum.
+ DCHECK(entry_point.has_value());
+ DCHECK(entry_point.value() >= 0);
+ DCHECK(entry_point.value() <=
+ static_cast<int>(PasswordChangeSuccessTracker::EntryPoint::kMaxValue));
+
+ return static_cast<PasswordChangeSuccessTracker::EntryPoint>(
+ entry_point.value_or(0));
+}
+
+base::Time PasswordChangeSuccessTrackerImpl::FlowView::GetStartTime() const {
+ absl::optional<base::Time> start_time =
+ base::ValueToTime(value_->Find(kKeyStartTime));
+ DCHECK(start_time.has_value());
+ return start_time.value_or(base::Time::Min());
+}
+
+PasswordChangeSuccessTrackerImpl::PasswordChangeSuccessTrackerImpl(
+ PrefService* pref_service)
+ : pref_service_(pref_service) {
+ DCHECK(pref_service_);
+ // Check whether the saved entries belong to an old version. If so,
+ // remove all old flows.
+ if (pref_service->GetInteger(prefs::kPasswordChangeSuccessTrackerVersion) <
+ kTrackerVersion) {
+ pref_service->SetInteger(prefs::kPasswordChangeSuccessTrackerVersion,
+ kTrackerVersion);
+ pref_service->ClearPref(prefs::kPasswordChangeSuccessTrackerFlows);
+ }
+}
PasswordChangeSuccessTrackerImpl::~PasswordChangeSuccessTrackerImpl() = default;
void PasswordChangeSuccessTrackerImpl::OnChangePasswordFlowStarted(
const GURL& url,
const std::string& username,
- StartEvent event_type) {
- // TODO(crbug.com/1281844): Implement metrics recoding.
+ StartEvent event_type,
+ EntryPoint entry_point) {
+ ListPrefUpdate update(pref_service_,
+ prefs::kPasswordChangeSuccessTrackerFlows);
+ base::Value::List& flows = update->GetList();
+ RemoveFlowsWithTimeout(flows);
+
+ flows.Append(
+ base::Value(CreateFlow(ExtractEtldPlus1(url), username, event_type,
+ entry_point, base::Time::Now())));
+}
+
+void PasswordChangeSuccessTrackerImpl::OnManualChangePasswordFlowStarted(
+ const GURL& url,
+ const std::string& username,
+ EntryPoint entry_point) {
+ RemoveIncompleteFlowsWithTimeout();
+ incomplete_manual_flows_.emplace_back(ExtractEtldPlus1(url), username,
+ entry_point);
+}
+
+void PasswordChangeSuccessTrackerImpl::OnChangePasswordFlowModified(
+ const GURL& url,
+ StartEvent new_event_type) {
+ RemoveIncompleteFlowsWithTimeout();
+ if (incomplete_manual_flows_.empty())
+ return;
+
+ // We always take the first match. We do not expect conflicts and, if they,
+ // occur, the information for both flows should be nearly identical.
+ auto predicate = [target_etld_plus_1 =
+ ExtractEtldPlus1(url)](const IncompleteFlow& flow) {
+ return flow.etld_plus_1 == target_etld_plus_1;
+ };
+ if (auto it = std::find_if(incomplete_manual_flows_.cbegin(),
+ incomplete_manual_flows_.cend(), predicate);
+ it != incomplete_manual_flows_.cend()) {
+ ListPrefUpdate update(pref_service_,
+ prefs::kPasswordChangeSuccessTrackerFlows);
+ base::Value::List& flows = update->GetList();
+ RemoveFlowsWithTimeout(flows);
+
+ flows.Append(
+ base::Value(CreateFlow(it->etld_plus_1, it->username, new_event_type,
+ it->entry_point, it->start_time)));
+ incomplete_manual_flows_.erase(it);
+ }
+}
+
+void PasswordChangeSuccessTrackerImpl::OnChangePasswordFlowModified(
+ const GURL& url,
+ const std::string& username,
+ StartEvent new_event_type) {
+ ListPrefUpdate update(pref_service_,
+ prefs::kPasswordChangeSuccessTrackerFlows);
+ base::Value::List& flows = update->GetList();
+ RemoveFlowsWithTimeout(flows);
+
+ // Currently, this method can only get called if a request link is requested
+ // inside an automated flow.
+ DCHECK(new_event_type == StartEvent::kManualResetLinkFlow);
+
+ // In the unlikely case that there are two flows with the same url and
+ // username, we take the last entry.
+ std::string target_etld_plus_1 = ExtractEtldPlus1(url);
+ for (size_t i = flows.size(); i-- > 0;) {
+ FlowView view(&flows[i].GetDict());
+ if (view.GetStartEvent() == StartEvent::kAutomatedFlow &&
+ view.GetEtldPlus1() == target_etld_plus_1 &&
+ view.GetUsername() == username) {
+ EntryPoint entry_point = view.GetEntryPoint();
+ RecordMetrics(view.GetEtldPlus1(), view.GetStartEvent(),
+ EndEvent::kAutomatedFlowResetLinkRequested, entry_point,
+ base::Time::Now() - view.GetStartTime());
+ flows.erase(flows.begin() + i);
+
+ // Add a new flow and reset the timer.
+ flows.Append(base::Value(CreateFlow(target_etld_plus_1, username,
+ StartEvent::kManualResetLinkFlow,
+ entry_point, base::Time::Now())));
+
+ return;
+ }
+ }
}
void PasswordChangeSuccessTrackerImpl::OnChangePasswordFlowCompleted(
const GURL& url,
const std::string& username,
EndEvent event_type) {
- // TODO(crbug.com/1281844): Implement metrics recoding.
+ // If there are no ongoing change flows, return immediately to avoid disk
+ // writes.
+ const base::Value* read_flows =
+ pref_service_->GetList(prefs::kPasswordChangeSuccessTrackerFlows);
+ if (!read_flows || read_flows->GetList().empty())
+ return;
+
+ ListPrefUpdate update(pref_service_,
+ prefs::kPasswordChangeSuccessTrackerFlows);
+ base::Value::List& flows = update->GetList();
+ RemoveFlowsWithTimeout(flows);
+
+ // In the unlikely case that there are two flows with the same eTLD+1 and
+ // username, we take the last entry. The underlying assumption is that
+ // the first flow was abandoned but has not timed out yet.
+ std::string target_etld_plus_1 = ExtractEtldPlus1(url);
+ for (size_t i = flows.size(); i-- > 0;) {
+ FlowView view(&flows[i].GetDict());
+ if (view.GetEtldPlus1() == target_etld_plus_1 &&
+ view.GetUsername() == username) {
+ RecordMetrics(view.GetEtldPlus1(), view.GetStartEvent(), event_type,
+ view.GetEntryPoint(),
+ base::Time::Now() - view.GetStartTime());
+ flows.erase(flows.begin() + i);
+ return;
+ }
+ }
+}
+
+void PasswordChangeSuccessTrackerImpl::AddMetricsRecorder(
+ std::unique_ptr<PasswordChangeMetricsRecorder> recorder) {
+ metrics_recorders_.push_back(std::move(recorder));
+}
+
+std::string PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(
+ const GURL& url) {
+ return net::registry_controlled_domains::GetDomainAndRegistry(
+ url, net::registry_controlled_domains::PrivateRegistryFilter::
+ INCLUDE_PRIVATE_REGISTRIES);
+}
+
+void PasswordChangeSuccessTrackerImpl::RemoveIncompleteFlowsWithTimeout() {
+ base::Time now = base::Time::Now();
+ while (!incomplete_manual_flows_.empty() &&
+ now - incomplete_manual_flows_.front().start_time >=
+ kFlowTypeRefinementTimeout) {
+ incomplete_manual_flows_.pop_front();
+ }
+ // Also remove entries that are in the future (e.g., from a time change).
+ while (!incomplete_manual_flows_.empty() &&
+ incomplete_manual_flows_.back().start_time > now) {
+ incomplete_manual_flows_.pop_back();
+ }
+}
+
+// Assumes that |flows| is a reference to the list containing all currently
+// active flows and that the calling method takes care of persisting these
+// |flows|.
+void PasswordChangeSuccessTrackerImpl::RemoveFlowsWithTimeout(
+ base::Value::List& flows) {
+ base::Time now = base::Time::Now();
+ for (auto it = flows.begin(); it != flows.end();) {
+ FlowView view(&it->GetDict());
+ if (base::TimeDelta duration = now - view.GetStartTime();
+ duration > kFlowTimeout) {
+ RecordMetrics(view.GetEtldPlus1(), view.GetStartEvent(),
+ EndEvent::kTimeout, view.GetEntryPoint(), kFlowTimeout);
+ it = flows.erase(it);
+ } else {
+ // Flows are expected to be ordered by their time of creation.
+ break;
+ }
+ }
+}
+
+void PasswordChangeSuccessTrackerImpl::RecordMetrics(
+ const std::string& etld_plus_1,
+ StartEvent start_event,
+ EndEvent end_event,
+ EntryPoint entry_point,
+ base::TimeDelta duration) {
+ for (const auto& recorder : metrics_recorders_) {
+ recorder->OnFlowRecorded(etld_plus_1, start_event, end_event, entry_point,
+ duration);
+ }
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_change_success_tracker_impl.h b/chromium/components/password_manager/core/browser/password_change_success_tracker_impl.h
index e3d835a33c2..3ef8cdbfc06 100644
--- a/chromium/components/password_manager/core/browser/password_change_success_tracker_impl.h
+++ b/chromium/components/password_manager/core/browser/password_change_success_tracker_impl.h
@@ -7,24 +7,180 @@
#include "components/password_manager/core/browser/password_change_success_tracker.h"
-class GURL;
+#include "base/containers/circular_deque.h"
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "url/gurl.h"
+
+class PrefService;
namespace password_manager {
+// Observer-like interface for metric recordering by
+// |PasswordChangeSuccessTrackerImpl|. This allows easier testing and
+// separately adding support for UMA and UKM recording.
+class PasswordChangeMetricsRecorder {
+ public:
+ virtual ~PasswordChangeMetricsRecorder() = default;
+
+ // Record a password change flow whose top level domain plus 1 is
+ // |etld_plus_1|.
+ virtual void OnFlowRecorded(
+ const std::string& etld_plus_1,
+ PasswordChangeSuccessTracker::StartEvent start_event,
+ PasswordChangeSuccessTracker::EndEvent end_event,
+ PasswordChangeSuccessTracker::EntryPoint entry_point,
+ base::TimeDelta duration) = 0;
+};
+
+// Implementation of |PasswordChangeMetricsRecorder| for UMA metrics.
+class PasswordChangeMetricsRecorderUma : public PasswordChangeMetricsRecorder {
+ public:
+ static constexpr char kUmaKey[] =
+ "PasswordManager.PasswordChangeFlowDuration";
+
+ PasswordChangeMetricsRecorderUma() = default;
+ ~PasswordChangeMetricsRecorderUma() override;
+
+ PasswordChangeMetricsRecorderUma(const PasswordChangeMetricsRecorderUma&) =
+ delete;
+ PasswordChangeMetricsRecorderUma& operator=(
+ const PasswordChangeMetricsRecorderUma&) = delete;
+
+ // PasswordChangeMetricsRecorder:
+ void OnFlowRecorded(const std::string& etld_plus_1,
+ PasswordChangeSuccessTracker::StartEvent start_event,
+ PasswordChangeSuccessTracker::EndEvent end_event,
+ PasswordChangeSuccessTracker::EntryPoint entry_point,
+ base::TimeDelta duration) override;
+};
+
+// Implementation of the |PasswordChangeMetricsRecorder| for UKM metrics.
+// It currently does not associate the record with the current navigation;
+// instead, it writes everything to the id |ukm::NoUrlSourceId()|.
+class PasswordChangeMetricsRecorderUkm : public PasswordChangeMetricsRecorder {
+ public:
+ // The exponential factor used for bucket spacing for the UKM recorder.
+ // Choosing a factor of 1.1 gives 70 unique buckets between 1 and 3600.
+ // A sufficient good resolution is important, since we expect the majority of
+ // flows to have durations much shorter than 3600 seconds.
+ static constexpr double kBucketSpacing = 1.1;
+
+ PasswordChangeMetricsRecorderUkm() = default;
+ ~PasswordChangeMetricsRecorderUkm() override;
+
+ PasswordChangeMetricsRecorderUkm(const PasswordChangeMetricsRecorderUkm&) =
+ delete;
+ PasswordChangeMetricsRecorderUkm& operator=(
+ const PasswordChangeMetricsRecorderUkm&) = delete;
+
+ // PasswordChangeMetricsRecorder:
+ void OnFlowRecorded(const std::string& etld_plus_1,
+ PasswordChangeSuccessTracker::StartEvent start_event,
+ PasswordChangeSuccessTracker::EndEvent end_event,
+ PasswordChangeSuccessTracker::EntryPoint entry_point,
+ base::TimeDelta duration) override;
+};
+
+// Implementation of the |PasswordChangeSuccessTracker| interface.
class PasswordChangeSuccessTrackerImpl
: public password_manager::PasswordChangeSuccessTracker {
public:
- PasswordChangeSuccessTrackerImpl();
+ // Current record version for flows that are persisted in preferences.
+ static constexpr int kTrackerVersion = 1;
+
+ // Describes a manually started flow for which no information on the exact
+ // |StartEvent| has been received yet.
+ struct IncompleteFlow {
+ IncompleteFlow(const std::string& etld_plus_1,
+ const std::string& username,
+ EntryPoint entry_point);
+ // The url is stored as a string, since that is what |base::Value| supports.
+ std::string etld_plus_1;
+ std::string username;
+ EntryPoint entry_point;
+ base::Time start_time;
+ };
+
+ // Provides helper functions for obtaining the flow properties such as |url|
+ // or |username| from the underlying |Value::Dict| object. Requires
+ // the raw pointer passed to the constructor to outlive the |FlowView|.
+ class FlowView {
+ public:
+ explicit FlowView(const base::Value::Dict* value);
+
+ std::string GetEtldPlus1() const;
+ std::string GetUsername() const;
+ StartEvent GetStartEvent() const;
+ EntryPoint GetEntryPoint() const;
+ base::Time GetStartTime() const;
+
+ private:
+ // Reference to the underlying |Value::Dict|, which must outlive the
+ // |FlowView|.
+ const raw_ptr<const base::Value::Dict> value_;
+ };
+
+ explicit PasswordChangeSuccessTrackerImpl(PrefService* pref_service);
~PasswordChangeSuccessTrackerImpl() override;
+ // PasswordChangeSuccessTracker:
void OnChangePasswordFlowStarted(const GURL& url,
const std::string& username,
- StartEvent event_type) override;
-
+ StartEvent event_type,
+ EntryPoint entry_point) override;
+ void OnManualChangePasswordFlowStarted(const GURL& url,
+ const std::string& username,
+ EntryPoint entry_point) override;
+ void OnChangePasswordFlowModified(const GURL& url,
+ StartEvent new_event_type) override;
+ void OnChangePasswordFlowModified(const GURL& url,
+ const std::string& username,
+ StartEvent new_event_type) override;
void OnChangePasswordFlowCompleted(const GURL& url,
const std::string& username,
EndEvent event_type) override;
+
+ // Add a |PasswordChangeMetricsRecorder| to listen for |OnFlowRecorded()|
+ // events. The caller passes ownership to the |PasswordChangeSuccessTracker|.
+ void AddMetricsRecorder(
+ std::unique_ptr<PasswordChangeMetricsRecorder> recorder);
+
+ // Convert the |url| to eTLD+1 serialized as a string. Exposed as a static
+ // method for easier testing.
+ static std::string ExtractEtldPlus1(const GURL& url);
+
+ private:
+ // Remove incomplete flows that have been around for longer than
+ // |kFlowTypeRefinementTimeout|.
+ void RemoveIncompleteFlowsWithTimeout();
+
+ // Remove and record flows that have not been completed within |kFlowTimeout|.
+ void RemoveFlowsWithTimeout(base::Value::List& flows);
+
+ // Record a completed or timed out flow.
+ void RecordMetrics(const std::string& etld_plus_1,
+ StartEvent start_event,
+ EndEvent end_event,
+ EntryPoint entry_point,
+ base::TimeDelta duration);
+
+ // Pointer to the |PrefService| used for persisting events.
+ raw_ptr<PrefService> pref_service_;
+
+ // Manually changed flows for which the |StartEvent| is not yet known,
+ // which are waiting for a |OnChangePasswordFlowModified()| call.
+ // These are not persisted across restarts and therefore only kept in
+ // memory. The events are in increasing order by their creation time.
+ base::circular_deque<IncompleteFlow> incomplete_manual_flows_;
+
+ // A list of |PasswordChangeMetricsRecorders| that process
+ // |OnFlowRecorded()| events. For simplicity, they are owned by the
+ // |PasswordChangeSuccessTracker|.
+ std::vector<std::unique_ptr<PasswordChangeMetricsRecorder>>
+ metrics_recorders_;
};
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_change_success_tracker_impl_unittest.cc b/chromium/components/password_manager/core/browser/password_change_success_tracker_impl_unittest.cc
new file mode 100644
index 00000000000..3fb2db22ccd
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_change_success_tracker_impl_unittest.cc
@@ -0,0 +1,696 @@
+// Copyright 2022 The Chromium 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/password_manager/core/browser/password_change_success_tracker_impl.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "base/values.h"
+#include "components/password_manager/core/browser/password_change_success_tracker.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+
+using password_manager::PasswordChangeMetricsRecorder;
+using password_manager::PasswordChangeMetricsRecorderUkm;
+using password_manager::PasswordChangeMetricsRecorderUma;
+using password_manager::PasswordChangeSuccessTracker;
+using password_manager::PasswordChangeSuccessTrackerImpl;
+using testing::_;
+using testing::StrictMock;
+using UkmEntry = ukm::builders::PasswordManager_PasswordChangeFlowDuration;
+
+constexpr char kUrl1[] = "https://www.example.com";
+constexpr char kEtldPlus1[] = "example.com";
+constexpr char kUrl2[] = "https://www.example.co.uk";
+constexpr char kUrl2WithPath[] = "https://www.example.co.uk/login.php";
+constexpr char kUsername1[] = "Paul";
+constexpr char kUsername2[] = "Lori";
+
+namespace {
+
+void RegisterPasswordChangeSuccessTrackerPreferences(
+ PrefRegistrySimple* registry) {
+ registry->RegisterIntegerPref(
+ password_manager::prefs::kPasswordChangeSuccessTrackerVersion, 0);
+ registry->RegisterListPref(
+ password_manager::prefs::kPasswordChangeSuccessTrackerFlows);
+}
+
+class MockPasswordChangeMetricsRecorder
+ : public password_manager::PasswordChangeMetricsRecorder {
+ public:
+ MockPasswordChangeMetricsRecorder() = default;
+ ~MockPasswordChangeMetricsRecorder() override = default;
+
+ MOCK_METHOD(void,
+ OnFlowRecorded,
+ (const std::string& url,
+ PasswordChangeSuccessTracker::StartEvent start_event,
+ PasswordChangeSuccessTracker::EndEvent end_event,
+ PasswordChangeSuccessTracker::EntryPoint entry_point,
+ base::TimeDelta duration),
+ (override));
+};
+
+} // namespace
+
+// Tests of |PasswordChangeMetricsRecorderUma|.
+class PasswordChangeMetricsRecorderUmaTest : public ::testing::Test {
+ public:
+ PasswordChangeMetricsRecorderUmaTest() = default;
+ ~PasswordChangeMetricsRecorderUmaTest() override = default;
+
+ protected:
+ const base::HistogramTester& histogram_tester() { return histogram_tester_; }
+ PasswordChangeMetricsRecorderUma& recorder() { return recorder_; }
+
+ private:
+ base::HistogramTester histogram_tester_;
+ PasswordChangeMetricsRecorderUma recorder_;
+};
+
+TEST_F(PasswordChangeMetricsRecorderUmaTest, RecordSingleMetricsEvent) {
+ constexpr PasswordChangeSuccessTracker::StartEvent start_event =
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow;
+ constexpr PasswordChangeSuccessTracker::EndEvent end_event =
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen;
+ constexpr PasswordChangeSuccessTracker::EntryPoint entry_point =
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog;
+
+ recorder().OnFlowRecorded(kEtldPlus1, start_event, end_event, entry_point,
+ base::Seconds(30));
+
+ histogram_tester().ExpectUniqueTimeSample(
+ "PasswordManager.PasswordChangeFlowDuration.LeakWarningDialog."
+ "AutomatedFlow",
+ base::Seconds(30), 1);
+
+ histogram_tester().ExpectUniqueTimeSample(
+ "PasswordManager.PasswordChangeFlowDuration.LeakWarningDialog."
+ "AutomatedFlow.AutomatedFlowPasswordChosen",
+ base::Seconds(30), 1);
+}
+
+TEST_F(PasswordChangeMetricsRecorderUmaTest, RecordMultipleMetricsEvents) {
+ constexpr PasswordChangeSuccessTracker::StartEvent start_event1 =
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow;
+ constexpr PasswordChangeSuccessTracker::EndEvent end_event1 =
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen;
+ constexpr PasswordChangeSuccessTracker::EndEvent end_event2 =
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowGeneratedPasswordChosen;
+ constexpr PasswordChangeSuccessTracker::EntryPoint entry_point1 =
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog;
+
+ recorder().OnFlowRecorded(kEtldPlus1, start_event1, end_event1, entry_point1,
+ base::Seconds(30));
+ recorder().OnFlowRecorded(kEtldPlus1, start_event1, end_event2, entry_point1,
+ base::Seconds(30));
+
+ histogram_tester().ExpectUniqueTimeSample(
+ "PasswordManager.PasswordChangeFlowDuration.LeakWarningDialog."
+ "AutomatedFlow",
+ base::Seconds(30), 2);
+
+ histogram_tester().ExpectUniqueTimeSample(
+ "PasswordManager.PasswordChangeFlowDuration.LeakWarningDialog."
+ "AutomatedFlow.AutomatedFlowPasswordChosen",
+ base::Seconds(30), 1);
+
+ histogram_tester().ExpectUniqueTimeSample(
+ "PasswordManager.PasswordChangeFlowDuration.LeakWarningDialog."
+ "AutomatedFlow.ManualFlowPasswordChosen",
+ base::Seconds(30), 1);
+}
+
+TEST_F(PasswordChangeMetricsRecorderUmaTest,
+ RecordMultipleMetricsEventsWithDifferentDurations) {
+ constexpr PasswordChangeSuccessTracker::StartEvent start_event =
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow;
+ constexpr PasswordChangeSuccessTracker::EndEvent end_event =
+ PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen;
+ constexpr PasswordChangeSuccessTracker::EntryPoint entry_point =
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings;
+
+ const base::TimeDelta duration1 = base::Seconds(30);
+ const base::TimeDelta duration2 = base::Minutes(30);
+
+ recorder().OnFlowRecorded(kEtldPlus1, start_event, end_event, entry_point,
+ duration1);
+ recorder().OnFlowRecorded(kEtldPlus1, start_event, end_event, entry_point,
+ duration2);
+
+ histogram_tester().ExpectTimeBucketCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakCheckInSettings."
+ "AutomatedFlow",
+ duration1, 1);
+ histogram_tester().ExpectTimeBucketCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakCheckInSettings."
+ "AutomatedFlow",
+ duration2, 1);
+ histogram_tester().ExpectTotalCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakCheckInSettings."
+ "AutomatedFlow",
+ 2);
+
+ histogram_tester().ExpectTimeBucketCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakCheckInSettings."
+ "AutomatedFlow."
+ "ManualFlowPasswordChosen",
+ duration1, 1);
+ histogram_tester().ExpectTimeBucketCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakCheckInSettings."
+ "AutomatedFlow.ManualFlowPasswordChosen",
+ duration2, 1);
+ histogram_tester().ExpectTotalCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakCheckInSettings."
+ "AutomatedFlow.ManualFlowPasswordChosen",
+ 2);
+}
+
+// Tests of |PasswordChangeMetricsRecorderUkm|.
+class PasswordChangeMetricsRecorderUkmTest : public ::testing::Test {
+ public:
+ PasswordChangeMetricsRecorderUkmTest() = default;
+ ~PasswordChangeMetricsRecorderUkmTest() override = default;
+
+ protected:
+ const ukm::TestAutoSetUkmRecorder& ukm_tester() { return test_ukm_recorder_; }
+ PasswordChangeMetricsRecorderUkm& recorder() { return recorder_; }
+
+ private:
+ base::test::TaskEnvironment task_environment_;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
+
+ // The object to test.
+ PasswordChangeMetricsRecorderUkm recorder_;
+};
+
+TEST_F(PasswordChangeMetricsRecorderUkmTest, RecordSingleMetricsEvent) {
+ constexpr PasswordChangeSuccessTracker::StartEvent start_event =
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow;
+ constexpr PasswordChangeSuccessTracker::EndEvent end_event =
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen;
+ constexpr PasswordChangeSuccessTracker::EntryPoint entry_point =
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog;
+
+ recorder().OnFlowRecorded(kEtldPlus1, start_event, end_event, entry_point,
+ base::Seconds(30));
+
+ // Check that UKM logging is correct.
+ const auto& entries = ukm_tester().GetEntriesByName(UkmEntry::kEntryName);
+ EXPECT_EQ(1u, entries.size());
+ for (const auto* entry : entries) {
+ EXPECT_EQ(entry->source_id, ukm::NoURLSourceId());
+ ukm_tester().ExpectEntryMetric(entry, UkmEntry::kStartEventName,
+ static_cast<int64_t>(start_event));
+ ukm_tester().ExpectEntryMetric(entry, UkmEntry::kEndEventName,
+ static_cast<int64_t>(end_event));
+ ukm_tester().ExpectEntryMetric(entry, UkmEntry::kEntryPointName,
+ static_cast<int64_t>(entry_point));
+ // Exponential bucketing maps 30 seconds to the 29 second bucket.
+ ukm_tester().ExpectEntryMetric(entry, UkmEntry::kDurationName, 29);
+ }
+}
+
+TEST_F(PasswordChangeMetricsRecorderUkmTest,
+ RecordSingleMetricsEventWithTimeout) {
+ constexpr PasswordChangeSuccessTracker::StartEvent start_event =
+ PasswordChangeSuccessTracker::StartEvent::kManualChangePasswordUrlFlow;
+ constexpr PasswordChangeSuccessTracker::EndEvent end_event =
+ PasswordChangeSuccessTracker::EndEvent::kTimeout;
+ constexpr PasswordChangeSuccessTracker::EntryPoint entry_point =
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings;
+
+ recorder().OnFlowRecorded(kEtldPlus1, start_event, end_event, entry_point,
+ PasswordChangeSuccessTracker::kFlowTimeout);
+
+ // Check that UKM logging is correct.
+ const auto& entries = ukm_tester().GetEntriesByName(UkmEntry::kEntryName);
+ EXPECT_EQ(1u, entries.size());
+ for (const auto* entry : entries) {
+ EXPECT_EQ(entry->source_id, ukm::NoURLSourceId());
+ ukm_tester().ExpectEntryMetric(entry, UkmEntry::kStartEventName,
+ static_cast<int64_t>(start_event));
+ ukm_tester().ExpectEntryMetric(entry, UkmEntry::kEndEventName,
+ static_cast<int64_t>(end_event));
+ ukm_tester().ExpectEntryMetric(entry, UkmEntry::kEntryPointName,
+ static_cast<int64_t>(entry_point));
+ // With a bucket spacing of 1.1, 3600 seconds are mapped to the bucket
+ // with 3299 seconds.
+ ukm_tester().ExpectEntryMetric(entry, UkmEntry::kDurationName, 3299);
+ }
+}
+
+// Tests of |PasswordChangeSuccessTrackerImpl|.
+class PasswordChangeSuccessTrackerImplTest : public ::testing::Test {
+ public:
+ PasswordChangeSuccessTrackerImplTest() {
+ RegisterPasswordChangeSuccessTrackerPreferences(pref_service_.registry());
+
+ password_change_success_tracker_ =
+ std::make_unique<PasswordChangeSuccessTrackerImpl>(&pref_service_);
+
+ auto recorder =
+ std::make_unique<StrictMock<MockPasswordChangeMetricsRecorder>>();
+ metrics_recorder_ = recorder.get();
+ password_change_success_tracker_->AddMetricsRecorder(std::move(recorder));
+ }
+
+ ~PasswordChangeSuccessTrackerImplTest() override = default;
+
+ protected:
+ PrefService* pref_service() { return &pref_service_; }
+
+ PasswordChangeSuccessTracker* tracker() {
+ return password_change_success_tracker_.get();
+ }
+
+ MockPasswordChangeMetricsRecorder* metrics_recorder() {
+ return metrics_recorder_;
+ }
+
+ void AddMetricsRecorder(
+ std::unique_ptr<PasswordChangeMetricsRecorder> recorder) {
+ password_change_success_tracker_->AddMetricsRecorder(std::move(recorder));
+ }
+
+ void FastForwardBy(base::TimeDelta time_step) {
+ task_environment_.FastForwardBy(time_step);
+ }
+
+ private:
+ base::test::TaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ TestingPrefServiceSimple pref_service_;
+ std::unique_ptr<PasswordChangeSuccessTrackerImpl>
+ password_change_success_tracker_;
+ raw_ptr<MockPasswordChangeMetricsRecorder> metrics_recorder_;
+};
+
+TEST(PasswordChangeSuccessTrackerImpl, DeletedOutdatedEventRecords) {
+ base::test::TaskEnvironment task_environment_;
+ TestingPrefServiceSimple pref_service_;
+ RegisterPasswordChangeSuccessTrackerPreferences(pref_service_.registry());
+
+ // Set an outdated version that contains flows.
+ pref_service_.SetInteger(
+ password_manager::prefs::kPasswordChangeSuccessTrackerVersion, 0);
+
+ base::Value::List flows;
+ flows.Append(base::Value::Dict());
+ flows.Append(base::Value::Dict());
+ pref_service_.SetList(
+ password_manager::prefs::kPasswordChangeSuccessTrackerFlows,
+ std::move(flows));
+
+ const base::Value* value = pref_service_.Get(
+ password_manager::prefs::kPasswordChangeSuccessTrackerFlows);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(value->GetList().size(), 2u);
+
+ std::unique_ptr<PasswordChangeSuccessTracker>
+ password_change_success_tracker_ =
+ std::make_unique<PasswordChangeSuccessTrackerImpl>(&pref_service_);
+
+ // Version has been updated and old records have been deleted.
+ absl::optional<int> version = pref_service_.GetInteger(
+ password_manager::prefs::kPasswordChangeSuccessTrackerVersion);
+ ASSERT_TRUE(version);
+ EXPECT_EQ(version.value(), PasswordChangeSuccessTrackerImpl::kTrackerVersion);
+
+ value = pref_service_.Get(
+ password_manager::prefs::kPasswordChangeSuccessTrackerFlows);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(value->GetList().size(), 0u);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest,
+ SuccessfulAutomatedFlowFromSettings) {
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings);
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl2), kUsername2,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings);
+
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl2)),
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings, _));
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl1)),
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings, _));
+
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl2), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen);
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest,
+ SuccessfulAutomatedFlowFromLeakWarning) {
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+
+ // This flow completion cannot be matched due to a different username,
+ // so there is no call to the recorder.
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen);
+
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl1)),
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog, _));
+
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::EndEvent::kAutomatedFlowOwnPasswordChosen);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest,
+ SuccessfulAutomatedFlowWithChangedUrl) {
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl2WithPath), kUsername2,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+
+ // This flow completion cannot be matched due to a different url,
+ // so there is no call to the recorder.
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen);
+
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl2)),
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog, _));
+
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl2), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::kAutomatedFlowOwnPasswordChosen);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest, SuccessfulManualFlows) {
+ tracker()->OnManualChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername2,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+
+ tracker()->OnChangePasswordFlowModified(
+ GURL(kUrl2),
+ PasswordChangeSuccessTracker::StartEvent::kManualHomepageFlow);
+ tracker()->OnChangePasswordFlowModified(
+ GURL(kUrl1),
+ PasswordChangeSuccessTracker::StartEvent::kManualWellKnownUrlFlow);
+ tracker()->OnChangePasswordFlowModified(
+ GURL(kUrl1),
+ PasswordChangeSuccessTracker::StartEvent::kManualHomepageFlow);
+
+ // The first candidate with matching url is used.
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl1)),
+ PasswordChangeSuccessTracker::StartEvent::kManualWellKnownUrlFlow,
+ PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog, _));
+
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest,
+ SuccessfulFlowSeveralMatchingCandidates) {
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings);
+
+ // The second entry should be matched. Since there cannot be simultaneous
+ // automated flows, we assume implicitly that the first one would have been
+ // abandoned.
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl1)),
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings, _));
+
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest, AutomatedFlowEndsInPasswordReset) {
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername2,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings);
+
+ // There are two calls: One to terminate the automated change flow and one
+ // to record the password reset flow.
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl1)),
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowResetLinkRequested,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings, _));
+
+ tracker()->OnChangePasswordFlowModified(
+ GURL(kUrl1), kUsername2,
+ PasswordChangeSuccessTracker::StartEvent::kManualResetLinkFlow);
+
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl1)),
+ PasswordChangeSuccessTracker::StartEvent::kManualResetLinkFlow,
+ PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings, _));
+
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest,
+ PasswordResetSeveralMatchingCandidates) {
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings);
+
+ // The second entry should be matched. Since there cannot be simultaneous
+ // automated flows, we assume implicitly that the first one would have been
+ // abandoned.
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl1)),
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowResetLinkRequested,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings, _));
+ tracker()->OnChangePasswordFlowModified(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::StartEvent::kManualResetLinkFlow);
+
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl1)),
+ PasswordChangeSuccessTracker::StartEvent::kManualResetLinkFlow,
+ PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings, _));
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::EndEvent::kManualFlowOwnPasswordChosen);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest, TimeoutForIncompleteFlow) {
+ tracker()->OnManualChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+
+ FastForwardBy(2 * PasswordChangeSuccessTracker::kFlowTypeRefinementTimeout);
+
+ tracker()->OnChangePasswordFlowModified(
+ GURL(kUrl1),
+ PasswordChangeSuccessTracker::StartEvent::kManualChangePasswordUrlFlow);
+
+ // We expect no call.
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowGeneratedPasswordChosen);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest, TimeoutForFlow) {
+ tracker()->OnManualChangePasswordFlowStarted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+ tracker()->OnChangePasswordFlowModified(
+ GURL(kUrl1),
+ PasswordChangeSuccessTracker::StartEvent::kManualChangePasswordUrlFlow);
+
+ FastForwardBy(2 * PasswordChangeSuccessTracker::kFlowTimeout);
+
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl1)),
+ PasswordChangeSuccessTracker::StartEvent::
+ kManualChangePasswordUrlFlow,
+ PasswordChangeSuccessTracker::EndEvent::kTimeout,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog, _));
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername1,
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowGeneratedPasswordChosen);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest,
+ IntegrationTestWithMetricsRecorderUma) {
+ base::HistogramTester histogram_tester;
+
+ // Manually add the Uma recorder.
+ AddMetricsRecorder(std::make_unique<PasswordChangeMetricsRecorderUma>());
+
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl2WithPath), kUsername2,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+
+ // This flow completion cannot be matched due to a different url,
+ // so there is no call to the recorder.
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen);
+
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl2)),
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog, _));
+
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl2), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::kAutomatedFlowOwnPasswordChosen);
+
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakWarningDialog."
+ "AutomatedFlow",
+ 1);
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.PasswordChangeFlowDuration.LeakWarningDialog."
+ "AutomatedFlow.AutomatedFlowPasswordChosen",
+ 1);
+}
+
+TEST_F(PasswordChangeSuccessTrackerImplTest,
+ IntegrationTestWithMetricsRecorderUkm) {
+ ukm::TestAutoSetUkmRecorder ukm_tester;
+
+ // Manually add the Ukm recorder.
+ AddMetricsRecorder(std::make_unique<PasswordChangeMetricsRecorderUkm>());
+
+ tracker()->OnChangePasswordFlowStarted(
+ GURL(kUrl2WithPath), kUsername2,
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog);
+
+ // This flow completion cannot be matched due to a different url,
+ // so there is no call to the recorder.
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl1), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowGeneratedPasswordChosen);
+
+ EXPECT_CALL(
+ *metrics_recorder(),
+ OnFlowRecorded(
+ PasswordChangeSuccessTrackerImpl::ExtractEtldPlus1(GURL(kUrl2)),
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow,
+ PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen,
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog, _));
+
+ tracker()->OnChangePasswordFlowCompleted(
+ GURL(kUrl2), kUsername2,
+ PasswordChangeSuccessTracker::EndEvent::kAutomatedFlowOwnPasswordChosen);
+
+ // Check that UKM logging is correct.
+ const auto& entries = ukm_tester.GetEntriesByName(UkmEntry::kEntryName);
+ EXPECT_EQ(1u, entries.size());
+ for (const auto* entry : entries) {
+ EXPECT_EQ(entry->source_id, ukm::NoURLSourceId());
+ ukm_tester.ExpectEntryMetric(
+ entry, UkmEntry::kStartEventName,
+ static_cast<int64_t>(
+ PasswordChangeSuccessTracker::StartEvent::kAutomatedFlow));
+ ukm_tester.ExpectEntryMetric(
+ entry, UkmEntry::kEndEventName,
+ static_cast<int64_t>(PasswordChangeSuccessTracker::EndEvent::
+ kAutomatedFlowOwnPasswordChosen));
+ ukm_tester.ExpectEntryMetric(
+ entry, UkmEntry::kEntryPointName,
+ static_cast<int64_t>(
+ PasswordChangeSuccessTracker::EntryPoint::kLeakWarningDialog));
+ }
+}
diff --git a/chromium/components/password_manager/core/browser/password_form.cc b/chromium/components/password_manager/core/browser/password_form.cc
index cbf5715883c..bb8fbd6ba05 100644
--- a/chromium/components/password_manager/core/browser/password_form.cc
+++ b/chromium/components/password_manager/core/browser/password_form.cc
@@ -189,10 +189,17 @@ void PasswordFormToJSON(const PasswordForm& form, base::Value* target) {
target->SetKey("password_issues ", base::Value(password_issues));
- base::Value note_value(base::Value::Type::DICTIONARY);
- note_value.SetStringKey("note_value", form.note.value);
- note_value.SetKey("date_created", base::TimeToValue(form.note.date_created));
- target->SetKey("note", std::move(note_value));
+ std::vector<base::Value> password_notes;
+ password_notes.reserve(form.notes.size());
+ for (const auto& note : form.notes) {
+ base::Value note_value(base::Value::Type::DICTIONARY);
+ note_value.SetStringKey("unique_display_name", note.unique_display_name);
+ note_value.SetStringKey("value", note.value);
+ note_value.SetKey("date_created", base::TimeToValue(note.date_created));
+ note_value.SetBoolKey("hide_by_default", note.hide_by_default);
+ password_notes.push_back(std::move(note_value));
+ }
+ target->SetKey("notes", base::Value(password_notes));
target->SetStringKey("previously_associated_sync_account_email",
form.previously_associated_sync_account_email);
@@ -215,6 +222,15 @@ PasswordNote::PasswordNote() = default;
PasswordNote::PasswordNote(std::u16string value, base::Time date_created)
: value(std::move(value)), date_created(std::move(date_created)) {}
+PasswordNote::PasswordNote(std::u16string unique_display_name,
+ std::u16string value,
+ base::Time date_created,
+ bool hide_by_default)
+ : unique_display_name(std::move(unique_display_name)),
+ value(std::move(value)),
+ date_created(date_created),
+ hide_by_default(hide_by_default) {}
+
PasswordNote::PasswordNote(const PasswordNote& rhs) = default;
PasswordNote::PasswordNote(PasswordNote&& rhs) = default;
@@ -226,7 +242,13 @@ PasswordNote& PasswordNote::operator=(PasswordNote&& rhs) = default;
PasswordNote::~PasswordNote() = default;
bool operator==(const PasswordNote& lhs, const PasswordNote& rhs) {
- return lhs.value == rhs.value && lhs.date_created == rhs.date_created;
+ return lhs.unique_display_name == rhs.unique_display_name &&
+ lhs.value == rhs.value && lhs.date_created == rhs.date_created &&
+ lhs.hide_by_default == rhs.hide_by_default;
+}
+
+bool operator!=(const PasswordNote& lhs, const PasswordNote& rhs) {
+ return !(lhs == rhs);
}
PasswordForm::PasswordForm() = default;
@@ -337,7 +359,7 @@ bool operator==(const PasswordForm& lhs, const PasswordForm& rhs) {
lhs.is_new_password_reliable == rhs.is_new_password_reliable &&
lhs.in_store == rhs.in_store &&
lhs.moving_blocked_for_list == rhs.moving_blocked_for_list &&
- lhs.password_issues == rhs.password_issues && lhs.note == rhs.note &&
+ lhs.password_issues == rhs.password_issues && lhs.notes == rhs.notes &&
lhs.previously_associated_sync_account_email ==
rhs.previously_associated_sync_account_email;
}
diff --git a/chromium/components/password_manager/core/browser/password_form.h b/chromium/components/password_manager/core/browser/password_form.h
index 160f2982761..b90781295f4 100644
--- a/chromium/components/password_manager/core/browser/password_form.h
+++ b/chromium/components/password_manager/core/browser/password_form.h
@@ -66,19 +66,30 @@ bool operator==(const InsecurityMetadata& lhs, const InsecurityMetadata& rhs);
struct PasswordNote {
PasswordNote();
PasswordNote(std::u16string value, base::Time date_created);
+ PasswordNote(std::u16string unique_display_name,
+ std::u16string value,
+ base::Time date_created,
+ bool hide_by_default);
PasswordNote(const PasswordNote& rhs);
PasswordNote(PasswordNote&& rhs);
PasswordNote& operator=(const PasswordNote& rhs);
PasswordNote& operator=(PasswordNote&& rhs);
~PasswordNote();
+ // The name displayed in the UI labeling this note. Currently unused and added
+ // for future compatibility.
+ std::u16string unique_display_name;
// The value of the note.
std::u16string value;
// The date when the note was created.
base::Time date_created;
+ // Whether the value of the note will be hidden by default in the UI similar
+ // to password values. Currently unused and added for future compatibility.
+ bool hide_by_default = false;
};
bool operator==(const PasswordNote& lhs, const PasswordNote& rhs);
+bool operator!=(const PasswordNote& lhs, const PasswordNote& rhs);
// The PasswordForm struct encapsulates information about a login form,
// which can be an HTML form or a dialog with username/password text fields.
@@ -395,8 +406,8 @@ struct PasswordForm {
// to its metadata (e.g. time it was discovered, whether alerts are muted).
base::flat_map<InsecureType, InsecurityMetadata> password_issues;
- // Attached note to the credential.
- PasswordNote note;
+ // Attached notes to the credential.
+ std::vector<PasswordNote> notes;
// Email address of the last sync account this password was associated with.
// This field is non empty only if the password is NOT currently associated
diff --git a/chromium/components/password_manager/core/browser/password_form_manager.cc b/chromium/components/password_manager/core/browser/password_form_manager.cc
index 743eff994d3..921055c6518 100644
--- a/chromium/components/password_manager/core/browser/password_form_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_form_manager.cc
@@ -36,6 +36,7 @@
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/possible_username_data.h"
+#include "components/password_manager/core/browser/psl_matching_helper.h"
#include "components/password_manager/core/browser/statistics_table.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/signin/public/identity_manager/identity_manager.h"
@@ -296,10 +297,14 @@ void PasswordFormManager::Save() {
// This is potentially the conclusion of a password change flow. It might also
// not be related to such a flow at all, but the tracker will figure it out.
+ PasswordChangeSuccessTracker::EndEvent end_event =
+ HasGeneratedPassword() ? PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowGeneratedPasswordChosen
+ : PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowOwnPasswordChosen;
client_->GetPasswordChangeSuccessTracker()->OnChangePasswordFlowCompleted(
parsed_submitted_form_->url,
- base::UTF16ToUTF8(GetPendingCredentials().username_value),
- PasswordChangeSuccessTracker::EndEvent::kManualFlow);
+ base::UTF16ToUTF8(GetPendingCredentials().username_value), end_event);
password_save_manager_->Save(observed_form(), *parsed_submitted_form_);
@@ -309,10 +314,14 @@ void PasswordFormManager::Save() {
void PasswordFormManager::Update(const PasswordForm& credentials_to_update) {
// This is potentially the conclusion of a password change flow. It might also
// not be related to such a flow at all, but the tracker will figure it out.
+ PasswordChangeSuccessTracker::EndEvent end_event =
+ HasGeneratedPassword() ? PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowGeneratedPasswordChosen
+ : PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowOwnPasswordChosen;
client_->GetPasswordChangeSuccessTracker()->OnChangePasswordFlowCompleted(
parsed_submitted_form_->url,
- base::UTF16ToUTF8(GetPendingCredentials().username_value),
- PasswordChangeSuccessTracker::EndEvent::kManualFlow);
+ base::UTF16ToUTF8(GetPendingCredentials().username_value), end_event);
password_save_manager_->Update(credentials_to_update, observed_form(),
*parsed_submitted_form_);
@@ -735,6 +744,7 @@ bool PasswordFormManager::ProvisionallySave(
submitted_form_ = submitted_form;
is_submitted_ = true;
CalculateFillingAssistanceMetric(submitted_form);
+ CalculateSubmittedFormFrameMetric();
metrics_recorder_->set_possible_username_used(false);
votes_uploader_.clear_single_username_vote_data();
@@ -1037,6 +1047,35 @@ void PasswordFormManager::CalculateFillingAssistanceMetric(
->ComputePasswordAccountStorageUsageLevel());
}
+void PasswordFormManager::CalculateSubmittedFormFrameMetric() {
+ if (!driver_)
+ return;
+
+ const PasswordForm& form = *GetSubmittedForm();
+ metrics_util::SubmittedFormFrame frame;
+ if (driver_->IsInPrimaryMainFrame()) {
+ frame = metrics_util::SubmittedFormFrame::MAIN_FRAME;
+ } else if (form.url == client_->GetLastCommittedURL()) {
+ frame =
+ metrics_util::SubmittedFormFrame::IFRAME_WITH_SAME_URL_AS_MAIN_FRAME;
+ } else {
+ std::string main_frame_signon_realm =
+ GetSignonRealm(client_->GetLastCommittedURL());
+ if (main_frame_signon_realm == form.signon_realm) {
+ frame = metrics_util::SubmittedFormFrame::
+ IFRAME_WITH_DIFFERENT_URL_SAME_SIGNON_REALM_AS_MAIN_FRAME;
+ } else if (IsPublicSuffixDomainMatch(form.signon_realm,
+ main_frame_signon_realm)) {
+ frame = metrics_util::SubmittedFormFrame::
+ IFRAME_WITH_PSL_MATCHED_SIGNON_REALM;
+ } else {
+ frame = metrics_util::SubmittedFormFrame::
+ IFRAME_WITH_DIFFERENT_AND_NOT_PSL_MATCHED_SIGNON_REALM;
+ }
+ }
+ metrics_recorder_->set_submitted_form_frame(frame);
+}
+
bool PasswordFormManager::IsPossibleSingleUsernameAvailable(
const PossibleUsernameData* possible_username) const {
if (!possible_username) {
diff --git a/chromium/components/password_manager/core/browser/password_form_manager.h b/chromium/components/password_manager/core/browser/password_form_manager.h
index 430698fb071..dbeffa93fb7 100644
--- a/chromium/components/password_manager/core/browser/password_form_manager.h
+++ b/chromium/components/password_manager/core/browser/password_form_manager.h
@@ -319,6 +319,11 @@ class PasswordFormManager : public PasswordFormManagerForUI,
void CalculateFillingAssistanceMetric(
const autofill::FormData& submitted_form);
+ // Calculates SubmittedPasswordFormFrame metric value (main frame, iframe,
+ // etc) for |submitted_form|. The metric is recorded when the form manager is
+ // destroyed.
+ void CalculateSubmittedFormFrameMetric();
+
// Save/update |pending_credentials_| to the password store.
void SavePendingToStore(bool update);
diff --git a/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc
index c7109267a24..36fd0ba9e42 100644
--- a/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -39,6 +39,7 @@
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_save_manager_impl.h"
#include "components/password_manager/core/browser/possible_username_data.h"
+#include "components/password_manager/core/browser/psl_matching_helper.h"
#include "components/password_manager/core/browser/stub_form_saver.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
@@ -118,15 +119,20 @@ MATCHER_P(FormHasPassword, password_value, "") {
}
MATCHER_P(FormDataPointeeEqualTo, form_data, "") {
- return autofill::FormDataEqualForTesting(*arg, form_data);
+ return autofill::FormData::DeepEqual(*arg, form_data);
}
class MockPasswordManagerDriver : public StubPasswordManagerDriver {
public:
- MOCK_METHOD1(FillPasswordForm, void(const PasswordFormFillData&));
- MOCK_METHOD1(AllowPasswordGenerationForForm, void(const PasswordForm&));
- MOCK_METHOD1(FormEligibleForGenerationFound,
- void(const autofill::PasswordFormGenerationData&));
+ MOCK_METHOD(void,
+ FillPasswordForm,
+ (const PasswordFormFillData&),
+ (override));
+ MOCK_METHOD(void,
+ FormEligibleForGenerationFound,
+ (const autofill::PasswordFormGenerationData&),
+ (override));
+ MOCK_METHOD(bool, IsInPrimaryMainFrame, (), (const, override));
};
class MockAutofillDownloadManager : public autofill::AutofillDownloadManager {
@@ -172,6 +178,7 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
MOCK_METHOD(FieldInfoManager*, GetFieldInfoManager, (), (const, override));
MOCK_METHOD(signin::IdentityManager*, GetIdentityManager, (), (override));
MOCK_METHOD(PrefService*, GetPrefs, (), (const, override));
+ MOCK_METHOD(const GURL&, GetLastCommittedURL, (), (const, override));
MOCK_METHOD(WebAuthnCredentialsDelegate*,
GetWebAuthnCredentialsDelegate,
(),
@@ -188,9 +195,7 @@ void CheckPendingCredentials(const PasswordForm& expected,
EXPECT_EQ(expected.username_element, actual.username_element);
EXPECT_EQ(expected.password_element, actual.password_element);
EXPECT_EQ(expected.blocked_by_user, actual.blocked_by_user);
- FormData::IdentityComparator less;
- EXPECT_FALSE(less(expected.form_data, actual.form_data));
- EXPECT_FALSE(less(actual.form_data, expected.form_data));
+ EXPECT_TRUE(FormData::DeepEqual(expected.form_data, actual.form_data));
}
struct ExpectedGenerationUKM {
@@ -446,6 +451,8 @@ class PasswordFormManagerTest : public testing::Test,
ON_CALL(*client_.GetPasswordFeatureManager(), GetDefaultPasswordStore)
.WillByDefault(Return(PasswordForm::Store::kProfileStore));
+ ON_CALL(client_, GetLastCommittedURL())
+ .WillByDefault(ReturnRef(observed_form_.url));
ON_CALL(client_, GetWebAuthnCredentialsDelegate)
.WillByDefault(Return(&webauthn_credentials_delegate_));
ON_CALL(webauthn_credentials_delegate_, IsWebAuthnAutofillEnabled)
@@ -1124,7 +1131,8 @@ TEST_P(PasswordFormManagerTest, UpdatePasswordOnChangePasswordForm) {
*client_.GetPasswordChangeSuccessTracker(),
OnChangePasswordFlowCompleted(
submitted_form.url, base::UTF16ToUTF8(saved_match_.username_value),
- PasswordChangeSuccessTracker::EndEvent::kManualFlow));
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowOwnPasswordChosen));
form_manager_->Save();
@@ -1883,7 +1891,8 @@ TEST_P(PasswordFormManagerTest, Update) {
EXPECT_CALL(*client_.GetPasswordChangeSuccessTracker(),
OnChangePasswordFlowCompleted(
submitted_form.url, base::UTF16ToUTF8(username),
- PasswordChangeSuccessTracker::EndEvent::kManualFlow));
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowOwnPasswordChosen));
EXPECT_CALL(client_, UpdateFormManagers());
const base::Time kNow = base::Time::Now();
@@ -2689,6 +2698,95 @@ TEST_P(PasswordFormManagerTest, MovableToAccountStore) {
EXPECT_TRUE(form_manager_->IsMovableToAccountStore());
}
+TEST_P(PasswordFormManagerTest, ReportSubmittedFormFrameMainFrame) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(driver_, IsInPrimaryMainFrame).WillRepeatedly(Return(true));
+
+ form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr);
+
+ // Check metrics recorded on the form manager destruction.
+ form_manager_.reset();
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.SubmittedFormFrame2",
+ metrics_util::SubmittedFormFrame::MAIN_FRAME, 1);
+}
+
+TEST_P(PasswordFormManagerTest, ReportSubmittedFormFrameSameOriginIframe) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(driver_, IsInPrimaryMainFrame).WillRepeatedly(Return(false));
+
+ EXPECT_CALL(client_, GetLastCommittedURL)
+ .WillOnce(ReturnRef(submitted_form_.url));
+ form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr);
+
+ // Check metrics recorded on the form manager destruction.
+ form_manager_.reset();
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.SubmittedFormFrame2",
+ metrics_util::SubmittedFormFrame::IFRAME_WITH_SAME_URL_AS_MAIN_FRAME, 1);
+}
+
+TEST_P(PasswordFormManagerTest, ReportSubmittedFormFrameSameSignOnRealmIframe) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(driver_, IsInPrimaryMainFrame).WillRepeatedly(Return(false));
+
+ GURL main_frame_url = GURL(GetSignonRealm(submitted_form_.url));
+ ASSERT_NE(submitted_form_.url, main_frame_url);
+ EXPECT_CALL(client_, GetLastCommittedURL)
+ .WillRepeatedly(ReturnRef(main_frame_url));
+ form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr);
+
+ // Check metrics recorded on the form manager destruction.
+ form_manager_.reset();
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.SubmittedFormFrame2",
+ metrics_util::SubmittedFormFrame::
+ IFRAME_WITH_DIFFERENT_URL_SAME_SIGNON_REALM_AS_MAIN_FRAME,
+ 1);
+}
+
+TEST_P(PasswordFormManagerTest, ReportSubmittedFormFramePSLMatchedIframe) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(driver_, IsInPrimaryMainFrame).WillRepeatedly(Return(false));
+
+ submitted_form_.url = GURL("http://facebook.com");
+ GURL main_frame_url = GURL("http://m.facebook.com");
+ ASSERT_TRUE(IsPublicSuffixDomainMatch(submitted_form_.url.spec(),
+ main_frame_url.spec()));
+ EXPECT_CALL(client_, GetLastCommittedURL)
+ .WillRepeatedly(ReturnRef(main_frame_url));
+ form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr);
+
+ // Check metrics recorded on the form manager destruction.
+ form_manager_.reset();
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.SubmittedFormFrame2",
+ metrics_util::SubmittedFormFrame::IFRAME_WITH_PSL_MATCHED_SIGNON_REALM,
+ 1);
+}
+
+TEST_P(PasswordFormManagerTest, ReportSubmittedFormFrameCrossOriginIframe) {
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(driver_, IsInPrimaryMainFrame).WillRepeatedly(Return(false));
+
+ GURL main_frame_url = GURL("http://www.crossorigin.com/login");
+ ASSERT_NE(GetSignonRealm(submitted_form_.url),
+ GetSignonRealm(main_frame_url));
+ ASSERT_FALSE(IsPublicSuffixDomainMatch(submitted_form_.url.spec(),
+ main_frame_url.spec()));
+ EXPECT_CALL(client_, GetLastCommittedURL)
+ .WillRepeatedly(ReturnRef(main_frame_url));
+ form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr);
+
+ // Check metrics recorded on the form manager destruction.
+ form_manager_.reset();
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.SubmittedFormFrame2",
+ metrics_util::SubmittedFormFrame::
+ IFRAME_WITH_DIFFERENT_AND_NOT_PSL_MATCHED_SIGNON_REALM,
+ 1);
+}
+
INSTANTIATE_TEST_SUITE_P(All,
PasswordFormManagerTest,
testing::Values(false, true));
@@ -2818,6 +2916,14 @@ TEST_F(PasswordFormManagerTestWithMockedSaver, SaveCredentials) {
Save(FormDataPointeeEqualTo(observed_form_), _))
.WillOnce(SaveArg<1>(&updated_form));
EXPECT_CALL(client_, UpdateFormManagers());
+ EXPECT_CALL(*mock_password_save_manager(), HasGeneratedPassword)
+ .WillOnce(Return(false));
+ EXPECT_CALL(
+ *client_.GetPasswordChangeSuccessTracker(),
+ OnChangePasswordFlowCompleted(
+ submitted_form.url, base::UTF16ToUTF8(saved_match_.username_value),
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowOwnPasswordChosen));
form_manager_->Save();
std::string expected_signon_realm =
submitted_form.url.DeprecatedGetOriginAsURL().spec();
@@ -3035,6 +3141,12 @@ TEST_F(PasswordFormManagerTestWithMockedSaver,
CreatePendingCredentials(_, _, _, _, _));
EXPECT_TRUE(
form_manager_->ProvisionallySave(submitted_form_, &driver_, nullptr));
+ EXPECT_CALL(
+ *client_.GetPasswordChangeSuccessTracker(),
+ OnChangePasswordFlowCompleted(
+ submitted_form_.url, base::UTF16ToUTF8(saved_match_.username_value),
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowGeneratedPasswordChosen));
EXPECT_CALL(*mock_password_save_manager(),
Save(FormDataPointeeEqualTo(submitted_form_), _))
.WillOnce(SaveArg<1>(&updated_form));
@@ -3120,6 +3232,14 @@ TEST_F(PasswordFormManagerTestWithMockedSaver, SaveHttpAuthNoHttpAuthStored) {
EXPECT_CALL(*mock_password_save_manager(),
CreatePendingCredentials(http_auth_form, _, _, true, _));
ASSERT_TRUE(form_manager_->ProvisionallySaveHttpAuthForm(http_auth_form));
+ EXPECT_CALL(*mock_password_save_manager(), HasGeneratedPassword)
+ .WillOnce(Return(false));
+ EXPECT_CALL(
+ *client_.GetPasswordChangeSuccessTracker(),
+ OnChangePasswordFlowCompleted(
+ http_auth_form.url, base::UTF16ToUTF8(saved_match_.username_value),
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowOwnPasswordChosen));
// Check that the password save manager is invoked.
EXPECT_CALL(*mock_password_save_manager(), Save(_, http_auth_form));
form_manager_->Save();
@@ -3139,6 +3259,14 @@ TEST_F(PasswordFormManagerTestWithMockedSaver, HTTPAuthAlreadySaved) {
EXPECT_CALL(*mock_password_save_manager(),
CreatePendingCredentials(http_auth_form, _, _, true, _));
ASSERT_TRUE(form_manager_->ProvisionallySaveHttpAuthForm(http_auth_form));
+ EXPECT_CALL(*mock_password_save_manager(), HasGeneratedPassword)
+ .WillOnce(Return(false));
+ EXPECT_CALL(
+ *client_.GetPasswordChangeSuccessTracker(),
+ OnChangePasswordFlowCompleted(
+ http_auth_form.url, base::UTF16ToUTF8(saved_match_.username_value),
+ PasswordChangeSuccessTracker::EndEvent::
+ kManualFlowOwnPasswordChosen));
// Check that the password save manager is invoked.
EXPECT_CALL(*mock_password_save_manager(), Save(_, http_auth_form));
form_manager_->Save();
diff --git a/chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc
index c2dfadad495..345fcb96ff7 100644
--- a/chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc
+++ b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc
@@ -273,6 +273,12 @@ PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() {
generated_password_status_.value()));
}
+ if (submitted_form_frame_.has_value()) {
+ base::UmaHistogramEnumeration(
+ "PasswordManager.SubmittedFormFrame2", submitted_form_frame_.value(),
+ metrics_util::SubmittedFormFrame::SUBMITTED_FORM_FRAME_COUNT);
+ }
+
if (password_generation_popup_shown_ !=
PasswordGenerationPopupShown::kNotShown) {
UMA_HISTOGRAM_ENUMERATION("PasswordGeneration.PopupShown",
diff --git a/chromium/components/password_manager/core/browser/password_form_metrics_recorder.h b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.h
index 41033acba1c..3aa74907fdd 100644
--- a/chromium/components/password_manager/core/browser/password_form_metrics_recorder.h
+++ b/chromium/components/password_manager/core/browser/password_form_metrics_recorder.h
@@ -430,6 +430,11 @@ class PasswordFormMetricsRecorder
void set_clock_for_testing(base::Clock* clock) { clock_ = clock; }
+ void set_submitted_form_frame(
+ metrics_util::SubmittedFormFrame submitted_form_frame) {
+ submitted_form_frame_ = submitted_form_frame;
+ }
+
private:
friend class base::RefCounted<PasswordFormMetricsRecorder>;
@@ -518,6 +523,7 @@ class PasswordFormMetricsRecorder
absl::optional<FillingSource> filling_source_;
absl::optional<metrics_util::PasswordAccountStorageUsageLevel>
account_storage_usage_level_;
+ absl::optional<metrics_util::SubmittedFormFrame> submitted_form_frame_;
// Whether a single username candidate was populated in prompt.
bool possible_username_used_ = false;
diff --git a/chromium/components/password_manager/core/browser/password_form_prediction_waiter.h b/chromium/components/password_manager/core/browser/password_form_prediction_waiter.h
index 0d1332d7361..0be56fe7c79 100644
--- a/chromium/components/password_manager/core/browser/password_form_prediction_waiter.h
+++ b/chromium/components/password_manager/core/browser/password_form_prediction_waiter.h
@@ -6,6 +6,7 @@
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_PREDICTION_WAITER_H_
#include "base/callback_forward.h"
+#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
@@ -51,7 +52,7 @@ class PasswordFormPredictionWaiter {
// The client owns the waiter so this pointer will survive this object's
// lifetime.
- Client* client_;
+ raw_ptr<Client> client_;
base::OneShotTimer timer_;
diff --git a/chromium/components/password_manager/core/browser/password_manager.cc b/chromium/components/password_manager/core/browser/password_manager.cc
index 3df583eb3a8..67bffe14b29 100644
--- a/chromium/components/password_manager/core/browser/password_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_manager.cc
@@ -48,6 +48,7 @@
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "google_apis/gaia/gaia_auth_util.h"
+#include "google_apis/gaia/gaia_urls.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#if BUILDFLAG(IS_WIN)
@@ -255,12 +256,19 @@ void PasswordManager::RegisterProfilePrefs(
#if BUILDFLAG(IS_ANDROID)
registry->RegisterBooleanPref(prefs::kOfferToSavePasswordsEnabledGMS, true);
registry->RegisterBooleanPref(prefs::kAutoSignInEnabledGMS, true);
+ registry->RegisterBooleanPref(prefs::kSettingsMigratedToUPM, false);
registry->RegisterIntegerPref(
prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);
registry->RegisterDoublePref(prefs::kTimeOfLastMigrationAttempt, 0.0);
registry->RegisterBooleanPref(prefs::kRequiresMigrationAfterSyncStatusChange,
false);
+ registry->RegisterBooleanPref(prefs::kPasswordsPrefWithNewLabelUsed, false);
+ registry->RegisterBooleanPref(
+ prefs::kUnenrolledFromGoogleMobileServicesDueToErrors, false);
#endif
+ // Preferences for |PasswordChangeSuccessTracker|.
+ registry->RegisterIntegerPref(prefs::kPasswordChangeSuccessTrackerVersion, 0);
+ registry->RegisterListPref(prefs::kPasswordChangeSuccessTrackerFlows);
}
// static
@@ -464,7 +472,9 @@ void PasswordManager::OnDynamicFormSubmission(
const PasswordForm* submitted_form = submitted_manager->GetSubmittedForm();
- if (gaia::IsGaiaSignonRealm(GURL(submitted_form->signon_realm))) {
+ const GURL gaia_signon_realm =
+ GaiaUrls::GetInstance()->gaia_origin().GetURL();
+ if (GURL(submitted_form->signon_realm) == gaia_signon_realm) {
// The GAIA signon realm (i.e. https://accounts.google.com) will always
// perform a full page redirect once the user cleared the login flow. Thus
// don't respond to other JavaScript based signals that would result in
@@ -559,6 +569,8 @@ void PasswordManager::HideManualFallbackForSaving() {
void PasswordManager::OnPasswordFormsParsed(
PasswordManagerDriver* driver,
const std::vector<FormData>& form_data) {
+ if (NewFormsParsed(driver, form_data))
+ client_->RefreshPasswordManagerSettingsIfNeeded();
CreatePendingLoginManagers(driver, form_data);
PasswordGenerationFrameHelper* password_generation_manager =
@@ -718,8 +730,6 @@ PasswordFormManager* PasswordManager::ProvisionallySaveForm(
// compare the landing URL against the cached and report the difference.
submitted_form_url_ = submitted_url;
- ReportSubmittedFormFrameMetric(driver, *matched_manager->GetSubmittedForm());
-
return matched_manager;
}
@@ -1275,30 +1285,6 @@ PasswordFormManager* PasswordManager::GetMatchedManager(
return nullptr;
}
-void PasswordManager::ReportSubmittedFormFrameMetric(
- const PasswordManagerDriver* driver,
- const PasswordForm& form) {
- if (!driver)
- return;
-
- metrics_util::SubmittedFormFrame frame;
- if (driver->IsInPrimaryMainFrame()) {
- frame = metrics_util::SubmittedFormFrame::MAIN_FRAME;
- } else if (form.url == client()->GetLastCommittedURL()) {
- frame =
- metrics_util::SubmittedFormFrame::IFRAME_WITH_SAME_URL_AS_MAIN_FRAME;
- } else {
- std::string main_frame_signon_realm =
- GetSignonRealm(client()->GetLastCommittedURL());
- frame = (main_frame_signon_realm == form.signon_realm)
- ? metrics_util::SubmittedFormFrame::
- IFRAME_WITH_DIFFERENT_URL_SAME_SIGNON_REALM_AS_MAIN_FRAME
- : metrics_util::SubmittedFormFrame::
- IFRAME_WITH_DIFFERENT_SIGNON_REALM;
- }
- metrics_util::LogSubmittedFormFrame(frame);
-}
-
void PasswordManager::TryToFindPredictionsToPossibleUsernameData() {
if (!possible_username_ || possible_username_->form_predictions)
return;
@@ -1354,6 +1340,13 @@ void PasswordManager::ShowManualFallbackForSaving(
}
}
+bool PasswordManager::NewFormsParsed(PasswordManagerDriver* driver,
+ const std::vector<FormData>& form_data) {
+ return base::ranges::any_of(form_data, [driver, this](const FormData& form) {
+ return !GetMatchedManager(driver, form.unique_renderer_id);
+ });
+}
+
void PasswordManager::ResetPendingCredentials() {
for (auto& form_manager : form_managers_)
form_manager->ResetState();
diff --git a/chromium/components/password_manager/core/browser/password_manager.h b/chromium/components/password_manager/core/browser/password_manager.h
index fbf36c15e0b..bb070841c31 100644
--- a/chromium/components/password_manager/core/browser/password_manager.h
+++ b/chromium/components/password_manager/core/browser/password_manager.h
@@ -314,10 +314,6 @@ class PasswordManager : public PasswordManagerInterface {
PasswordFormManager* GetMatchedManager(PasswordManagerDriver* driver,
autofill::FormRendererId form_id);
- // Log a frame (main frame, iframe) of a submitted password form.
- void ReportSubmittedFormFrameMetric(const PasswordManagerDriver* driver,
- const PasswordForm& form);
-
// If |possible_username_.form_predictions| is missing, this functions tries
// to find predictions for the form which contains |possible_username_| in
// |predictions_|.
@@ -328,6 +324,11 @@ class PasswordManager : public PasswordManagerInterface {
void ShowManualFallbackForSaving(PasswordFormManager* form_manager,
const autofill::FormData& form_data);
+ // Returns true if |form_data| contains forms that are parsed for the first
+ // time and have no dedicated PasswordFormsManagers yet.
+ bool NewFormsParsed(PasswordManagerDriver* driver,
+ const std::vector<autofill::FormData>& form_data);
+
// Returns the timeout for the disabling Password Manager's prompts.
base::TimeDelta GetTimeoutForDisablingPrompts();
diff --git a/chromium/components/password_manager/core/browser/password_manager_client.cc b/chromium/components/password_manager/core/browser/password_manager_client.cc
index 6fc1788af80..d111f041663 100644
--- a/chromium/components/password_manager/core/browser/password_manager_client.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_client.cc
@@ -27,11 +27,17 @@ bool PasswordManagerClient::IsFillingFallbackEnabled(const GURL& url) const {
return true;
}
+bool PasswordManagerClient::IsAutoSignInEnabled() const {
+ return false;
+}
+
+#if BUILDFLAG(IS_ANDROID)
void PasswordManagerClient::ShowTouchToFill(
PasswordManagerDriver* driver,
autofill::mojom::SubmissionReadinessState submission_readiness) {}
void PasswordManagerClient::OnPasswordSelected(const std::u16string& text) {}
+#endif
scoped_refptr<device_reauth::BiometricAuthenticator>
PasswordManagerClient::GetBiometricAuthenticator() {
@@ -161,4 +167,8 @@ version_info::Channel PasswordManagerClient::GetChannel() const {
return version_info::Channel::UNKNOWN;
}
+void PasswordManagerClient::RefreshPasswordManagerSettingsIfNeeded() const {
+ // For most implementations settings do not need to be refreshed.
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_client.h b/chromium/components/password_manager/core/browser/password_manager_client.h
index 41060f74bf0..a814a06846b 100644
--- a/chromium/components/password_manager/core/browser/password_manager_client.h
+++ b/chromium/components/password_manager/core/browser/password_manager_client.h
@@ -134,6 +134,9 @@ class PasswordManagerClient {
// address.
virtual bool IsFillingFallbackEnabled(const GURL& url) const;
+ // Checks if the auto sign-in functionality is enabled.
+ virtual bool IsAutoSignInEnabled() const;
+
// Informs the embedder of a password form that can be saved or updated in
// password store if the user allows it. The embedder is not required to
// prompt the user if it decides that this form doesn't need to be saved or
@@ -186,6 +189,7 @@ class PasswordManagerClient {
const url::Origin& origin,
CredentialsCallback callback) = 0;
+#if BUILDFLAG(IS_ANDROID)
// Instructs the client to show the Touch To Fill UI.
virtual void ShowTouchToFill(
PasswordManagerDriver* driver,
@@ -194,6 +198,7 @@ class PasswordManagerClient {
// Informs `PasswordReuseDetectionManager` about reused passwords selected
// from the AllPasswordsBottomSheet.
virtual void OnPasswordSelected(const std::u16string& text);
+#endif
// Returns a pointer to a BiometricAuthenticator. Might be null if
// BiometricAuthentication is not available for a given platform.
@@ -472,6 +477,9 @@ class PasswordManagerClient {
// Returns the Chrome channel for the installation.
virtual version_info::Channel GetChannel() const;
+
+ // Refreshes password manager settings stored in prefs.
+ virtual void RefreshPasswordManagerSettingsIfNeeded() const;
};
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_client_helper.cc b/chromium/components/password_manager/core/browser/password_manager_client_helper.cc
index 9a1b804df35..83d0a89d878 100644
--- a/chromium/components/password_manager/core/browser/password_manager_client_helper.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_client_helper.cc
@@ -99,9 +99,7 @@ bool PasswordManagerClientHelper::ShouldPromptToEnableAutoSignIn() const {
return password_bubble_experiment::
ShouldShowAutoSignInPromptFirstRunExperience(
delegate_->GetPrefs()) &&
- password_manager_util::IsAutoSignInEnabled(
- delegate_->GetPrefs(), delegate_->GetSyncService()) &&
- !delegate_->IsIncognito();
+ delegate_->IsAutoSignInEnabled() && !delegate_->IsIncognito();
}
bool PasswordManagerClientHelper::ShouldPromptToMovePasswordToAccount(
diff --git a/chromium/components/password_manager/core/browser/password_manager_client_helper_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_client_helper_unittest.cc
index 8c5c3c34491..0811f850778 100644
--- a/chromium/components/password_manager/core/browser/password_manager_client_helper_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_client_helper_unittest.cc
@@ -41,6 +41,7 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
public:
MockPasswordManagerClient() = default;
+ MOCK_METHOD(bool, IsAutoSignInEnabled, (), (const, override));
MOCK_METHOD(void,
PromptUserToMovePasswordToAccount,
(std::unique_ptr<PasswordFormManagerForUI>),
@@ -79,10 +80,7 @@ class PasswordManagerClientHelperTest : public testing::Test {
PasswordManagerClientHelperTest() : helper_(&client_) {
prefs_.registry()->RegisterBooleanPref(
prefs::kWasAutoSignInFirstRunExperienceShown, false);
- prefs_.registry()->RegisterBooleanPref(prefs::kCredentialsEnableAutosignin,
- true);
prefs_.SetBoolean(prefs::kWasAutoSignInFirstRunExperienceShown, false);
- prefs_.SetBoolean(prefs::kCredentialsEnableAutosignin, true);
ON_CALL(client_, GetPrefs()).WillByDefault(Return(&prefs_));
ON_CALL(*client(), GetIdentityManager)
@@ -107,6 +105,7 @@ class PasswordManagerClientHelperTest : public testing::Test {
};
TEST_F(PasswordManagerClientHelperTest, PromptAutosigninAfterSuccessfulLogin) {
+ EXPECT_CALL(*client(), IsAutoSignInEnabled).WillOnce(Return(true));
EXPECT_CALL(*client(), PromptUserToEnableAutosignin);
EXPECT_CALL(*client(), PromptUserToMovePasswordToAccount).Times(0);
diff --git a/chromium/components/password_manager/core/browser/password_manager_constants.cc b/chromium/components/password_manager/core/browser/password_manager_constants.cc
index 17b90207f73..9b68ac28248 100644
--- a/chromium/components/password_manager/core/browser/password_manager_constants.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_constants.cc
@@ -22,4 +22,11 @@ const char kPasswordManagerHelpCenteriOSURL[] =
const char kPasswordManagerHelpCenterSmartLock[] =
"https://support.google.com/accounts?p=smart_lock_chrome";
+const char kManageMyPasswordsURL[] = "https://passwords.google.com/app";
+
+const char kReferrerURL[] = "https://passwords.google/";
+
+const char kTestingReferrerURL[] =
+ "https://xl-password-manager-staging.uc.r.appspot.com/";
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_constants.h b/chromium/components/password_manager/core/browser/password_manager_constants.h
index b0020d25dd0..b9c4af9829a 100644
--- a/chromium/components/password_manager/core/browser/password_manager_constants.h
+++ b/chromium/components/password_manager/core/browser/password_manager_constants.h
@@ -23,6 +23,17 @@ extern const char kPasswordManagerHelpCenteriOSURL[];
// TODO(crbug.com/862269): remove when "Smart Lock" is completely gone.
extern const char kPasswordManagerHelpCenterSmartLock[];
+// URL which open native Password Manager UI.
+extern const char kManageMyPasswordsURL[];
+
+// URL from which native Password Manager UI can be opened.
+extern const char kReferrerURL[];
+
+// URL for a testing website from which native Password Manager UI can be
+// opened.
+// TODO(crbug.com/1329165): remove when the main website is launched.
+extern const char kTestingReferrerURL[];
+
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_CONSTANTS_H_
diff --git a/chromium/components/password_manager/core/browser/password_manager_features_util.cc b/chromium/components/password_manager/core/browser/password_manager_features_util.cc
index eed2bef8966..33d166cd5af 100644
--- a/chromium/components/password_manager/core/browser/password_manager_features_util.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_features_util.cc
@@ -19,7 +19,7 @@
#include "components/signin/public/identity_manager/account_info.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/driver/sync_user_settings.h"
-#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/gaia_auth_util.h"
using autofill::GaiaIdHash;
using password_manager::metrics_util::PasswordAccountStorageUsageLevel;
@@ -224,8 +224,7 @@ bool ShouldShowAccountStorageReSignin(const PrefService* pref_service,
return false;
}
- if (current_page_url.DeprecatedGetOriginAsURL() ==
- GaiaUrls::GetInstance()->gaia_url().DeprecatedGetOriginAsURL()) {
+ if (gaia::HasGaiaSchemeHostPort(current_page_url)) {
return false;
}
diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_util.cc b/chromium/components/password_manager/core/browser/password_manager_metrics_util.cc
index 833fdd5ead8..81d9945a793 100644
--- a/chromium/components/password_manager/core/browser/password_manager_metrics_util.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_metrics_util.cc
@@ -5,14 +5,19 @@
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "base/metrics/histogram_functions.h"
+#include "base/rand_util.h"
#include "base/strings/strcat.h"
#include "components/autofill/core/common/password_generation_util.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
using autofill::password_generation::PasswordGenerationType;
-namespace password_manager {
+namespace ukm::builders {
+class PasswordManager_LeakWarningDialog;
+} // namespace ukm::builders
-namespace metrics_util {
+namespace password_manager::metrics_util {
std::string GetPasswordAccountStorageUserStateHistogramSuffix(
PasswordAccountStorageUserState user_state) {
@@ -51,6 +56,43 @@ std::string GetPasswordAccountStorageUsageLevelHistogramSuffix(
return std::string();
}
+LeakDialogMetricsRecorder::LeakDialogMetricsRecorder(ukm::SourceId source_id,
+ LeakDialogType type)
+ : source_id_(source_id), type_(type) {}
+
+void LeakDialogMetricsRecorder::LogLeakDialogTypeAndDismissalReason(
+ LeakDialogDismissalReason reason) {
+ // Always record UMA.
+ base::UmaHistogramEnumeration(kHistogram, reason);
+ base::UmaHistogramEnumeration(base::StrCat({kHistogram, ".", GetUMASuffix()}),
+ reason);
+
+ // For UKM, sample the recorded events.
+ if (base::RandDouble() > ukm_sampling_rate_)
+ return;
+
+ // The entire event is made up of these two fields, so we can build and
+ // record it in one step.
+ ukm ::builders::PasswordManager_LeakWarningDialog ukm_builder(source_id_);
+ ukm_builder.SetPasswordLeakDetectionDialogType(static_cast<int64_t>(type_));
+ ukm_builder.SetPasswordLeakDetectionDialogDismissalReason(
+ static_cast<int64_t>(reason));
+ ukm_builder.Record(ukm::UkmRecorder::Get());
+}
+
+const char* LeakDialogMetricsRecorder::GetUMASuffix() const {
+ switch (type_) {
+ case LeakDialogType::kCheckup:
+ return "Checkup";
+ case LeakDialogType::kChange:
+ return "Change";
+ case LeakDialogType::kCheckupAndChange:
+ return "CheckupAndChange";
+ case LeakDialogType::kChangeAutomatically:
+ return "ChangeAutomatically";
+ }
+}
+
void LogGeneralUIDismissalReason(UIDismissalReason reason) {
base::UmaHistogramEnumeration("PasswordManager.UIDismissalReason", reason,
NUM_UI_RESPONSES);
@@ -111,28 +153,6 @@ void LogMoveUIDismissalReason(UIDismissalReason reason,
NUM_UI_RESPONSES);
}
-void LogLeakDialogTypeAndDismissalReason(LeakDialogType type,
- LeakDialogDismissalReason reason) {
- static constexpr char kHistogram[] =
- "PasswordManager.LeakDetection.DialogDismissalReason";
- auto GetSuffix = [type] {
- switch (type) {
- case LeakDialogType::kCheckup:
- return "Checkup";
- case LeakDialogType::kChange:
- return "Change";
- case LeakDialogType::kCheckupAndChange:
- return "CheckupAndChange";
- case LeakDialogType::kChangeAutomatically:
- return "ChangeAutomatically";
- }
- };
-
- base::UmaHistogramEnumeration(kHistogram, reason);
- base::UmaHistogramEnumeration(base::StrCat({kHistogram, ".", GetSuffix()}),
- reason);
-}
-
void LogUIDisplayDisposition(UIDisplayDisposition disposition) {
base::UmaHistogramEnumeration("PasswordBubble.DisplayDisposition",
disposition, NUM_DISPLAY_DISPOSITIONS);
@@ -246,11 +266,6 @@ void LogPasswordAcceptedSaveUpdateSubmissionIndicatorEvent(
"PasswordManager.AcceptedSaveUpdateSubmissionIndicatorEvent", event);
}
-void LogSubmittedFormFrame(SubmittedFormFrame frame) {
- base::UmaHistogramEnumeration("PasswordManager.SubmittedFormFrame", frame,
- SubmittedFormFrame::SUBMITTED_FORM_FRAME_COUNT);
-}
-
void LogPasswordsCountFromAccountStoreAfterUnlock(
int account_store_passwords_count) {
base::UmaHistogramCounts100(
@@ -283,15 +298,25 @@ void LogDeleteUndecryptableLoginsReturnValue(
"PasswordManager.DeleteUndecryptableLoginsReturnValue", result);
}
-void LogNewlySavedPasswordIsGenerated(
- bool value,
+void LogNewlySavedPasswordMetrics(
+ bool is_generated_password,
+ bool is_username_empty,
PasswordAccountStorageUsageLevel account_storage_usage_level) {
base::UmaHistogramBoolean("PasswordManager.NewlySavedPasswordIsGenerated",
- value);
+ is_generated_password);
std::string suffix = GetPasswordAccountStorageUsageLevelHistogramSuffix(
account_storage_usage_level);
base::UmaHistogramBoolean(
- "PasswordManager.NewlySavedPasswordIsGenerated." + suffix, value);
+ "PasswordManager.NewlySavedPasswordIsGenerated." + suffix,
+ is_generated_password);
+
+ base::UmaHistogramBoolean(
+ "PasswordManager.NewlySavedPasswordHasEmptyUsername.Overall",
+ is_username_empty);
+ base::UmaHistogramBoolean(
+ base::StrCat({"PasswordManager.NewlySavedPasswordHasEmptyUsername.",
+ is_generated_password ? "AutoGenerated" : "UserCreated"}),
+ is_username_empty);
}
void LogGenerationDialogChoice(GenerationDialogChoice choice,
@@ -376,6 +401,9 @@ void LogUserInteractionsWhenAddingCredentialFromSettings(
add_credential_from_settings_user_interaction);
}
-} // namespace metrics_util
+void LogPasswordNoteActionInSettings(PasswordNoteAction action) {
+ base::UmaHistogramEnumeration("PasswordManager.PasswordNoteActionInSettings",
+ action);
+}
-} // namespace password_manager
+} // namespace password_manager::metrics_util
diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_util.h b/chromium/components/password_manager/core/browser/password_manager_metrics_util.h
index a798a87f4db..c97506f4a67 100644
--- a/chromium/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/chromium/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -12,6 +12,7 @@
#include "components/autofill/core/common/mojom/autofill_types.mojom.h"
#include "components/autofill/core/common/password_generation_util.h"
#include "components/password_manager/core/common/credential_manager_types.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
namespace password_manager {
@@ -65,7 +66,8 @@ enum UIDismissalReason {
};
// Enum representing the different leak detection dialogs shown to the user.
-// Corresponds to LeakDetectionDialogType suffix in histogram_suffixes_list.xml.
+// Corresponds to LeakDetectionDialogType suffix in histogram_suffixes_list.xml
+// and PasswordLeakDetectionDialogType in enums.xml.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class LeakDialogType {
@@ -212,7 +214,10 @@ enum class SubmittedFormFrame {
MAIN_FRAME = 0,
IFRAME_WITH_SAME_URL_AS_MAIN_FRAME = 1,
IFRAME_WITH_DIFFERENT_URL_SAME_SIGNON_REALM_AS_MAIN_FRAME = 2,
- IFRAME_WITH_DIFFERENT_SIGNON_REALM = 3,
+ // Deprecated and replaced with a combination of buckets 4 & 5.
+ // IFRAME_WITH_DIFFERENT_SIGNON_REALM = 3,
+ IFRAME_WITH_PSL_MATCHED_SIGNON_REALM = 4,
+ IFRAME_WITH_DIFFERENT_AND_NOT_PSL_MATCHED_SIGNON_REALM = 5,
SUBMITTED_FORM_FRAME_COUNT
};
@@ -526,6 +531,24 @@ enum class PasswordEditUpdatedValues {
kMaxValue = kBoth,
};
+// Used to record usage of the note field in password editing / adding flows in
+// the settings UI. These values are persisted to logs. Entries should not be
+// renumbered and numeric values should never be reused.
+enum class PasswordNoteAction {
+ // A new credential is added from settings, with the note field not empty.
+ kNoteAddedInAddDialog = 0,
+ // Note changed from empty to non-empty from the password edit dialog in
+ // settings.
+ kNoteAddedInEditDialog = 1,
+ // Note changed from non-empty to another non-empty from the password edit
+ // dialog in settings.
+ kNoteEditedInEditDialog = 2,
+ // Note changed from non-empty to empty from the password edit dialog in
+ // settings.
+ kNoteRemovedInEditDialog = 3,
+ kMaxValue = kNoteRemovedInEditDialog,
+};
+
std::string GetPasswordAccountStorageUserStateHistogramSuffix(
PasswordAccountStorageUserState user_state);
@@ -544,9 +567,75 @@ enum class PasswordAccountStorageUsageLevel {
// The user has enabled Sync.
kSyncing = 2,
};
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class PasswordViewPageInteractions {
+ // The credential row is clicked in the password list in settings.
+ kCredentialRowClicked = 0,
+ // The user opens the password view page to view an existing credential.
+ kCredentialFound = 1,
+ // The user opens the password view page to view an non-existing credential.
+ // This will close the settings password view page.
+ kCredentialNotFound = 2,
+ // The copy username button in settings password view page is clicked.
+ kUsernameCopyButtonClicked = 3,
+ // The copy password button in settings password view page is clicked.
+ kPasswordCopyButtonClicked = 4,
+ // The show password button in settings password view page is clicked and the
+ // password is revealed.
+ kPasswordShowButtonClicked = 5,
+ // The edit button in settings password view page is clicked.
+ kPasswordEditButtonClicked = 6,
+ // The delete button in settings password view page is clicked.
+ kPasswordDeleteButtonClicked = 7,
+ // The credential's username, password or note is edited in settings password
+ // view page.
+ kCredentialEdited = 8,
+ kMaxValue = kCredentialEdited,
+};
+
std::string GetPasswordAccountStorageUsageLevelHistogramSuffix(
PasswordAccountStorageUsageLevel usage_level);
+// Records the `type` of a leak dialog shown to the user and the `reason`
+// why it was dismissed.
+class LeakDialogMetricsRecorder {
+ public:
+ // Create a LeakDialogMetricsRecorder corresponding to a navigation with
+ // `source_id`.
+ LeakDialogMetricsRecorder(ukm::SourceId source_id, LeakDialogType type);
+ LeakDialogMetricsRecorder(const LeakDialogMetricsRecorder&) = delete;
+ LeakDialogMetricsRecorder& operator=(const LeakDialogMetricsRecorder&) =
+ delete;
+
+ // Log the `reason` for dismissing the leak warning dialog, e.g. signaling
+ // that the user ignored it or that they asked for an automatic password
+ // change.
+ void LogLeakDialogTypeAndDismissalReason(LeakDialogDismissalReason reason);
+
+ // Helper method to overwrite the sampling rate during unit tests.
+ void SetSamplingRateForTesting(double rate) { ukm_sampling_rate_ = rate; }
+
+ private:
+ // The UMA prefix.
+ static constexpr char kHistogram[] =
+ "PasswordManager.LeakDetection.DialogDismissalReason";
+
+ // The sampling rate for UKM recording. A value of 0.1 corresponds to a
+ // sampling rate of 10%.
+ double ukm_sampling_rate_ = 0.1;
+
+ // Helper method to determine the suffix for the UMA.
+ const char* GetUMASuffix() const;
+
+ // The source id associated with the navigation.
+ ukm::SourceId source_id_;
+
+ // The type of the leak dialog.
+ LeakDialogType type_;
+};
+
// Log the |reason| a user dismissed the password manager UI except save/update
// bubbles.
void LogGeneralUIDismissalReason(UIDismissalReason reason);
@@ -576,11 +665,6 @@ void LogUpdateUIDismissalReason(
void LogMoveUIDismissalReason(UIDismissalReason reason,
PasswordAccountStorageUserState user_state);
-// Log the |type| of a leak dialog shown to the user and the |reason| why it was
-// dismissed.
-void LogLeakDialogTypeAndDismissalReason(LeakDialogType type,
- LeakDialogDismissalReason reason);
-
// Log the appropriate display disposition.
void LogUIDisplayDisposition(UIDisplayDisposition disposition);
@@ -636,9 +720,6 @@ void LogPasswordSuccessfulSubmissionIndicatorEvent(
void LogPasswordAcceptedSaveUpdateSubmissionIndicatorEvent(
autofill::mojom::SubmissionIndicatorEvent event);
-// Log a frame of a submitted password form.
-void LogSubmittedFormFrame(SubmittedFormFrame frame);
-
// Logs how many account-stored passwords are available for filling in the
// current password form right after unlock.
void LogPasswordsCountFromAccountStoreAfterUnlock(
@@ -663,9 +744,11 @@ void LogPasswordSettingsReauthResult(ReauthResult result);
void LogDeleteUndecryptableLoginsReturnValue(
DeleteCorruptedPasswordsResult result);
-// Log whether a saved password was generated.
-void LogNewlySavedPasswordIsGenerated(
- bool value,
+// Log metrics about a newly saved password (e.g. whether a saved password was
+// generated).
+void LogNewlySavedPasswordMetrics(
+ bool is_generated_password,
+ bool is_username_empty,
PasswordAccountStorageUsageLevel account_storage_usage_level);
// Log whether the generated password was accepted or rejected for generation of
@@ -697,6 +780,10 @@ void LogUserInteractionsWhenAddingCredentialFromSettings(
AddCredentialFromSettingsUserInteractions
add_credential_from_settings_user_interaction);
+// Log how the user interaction with the note field in password add / edit
+// dialogs.
+void LogPasswordNoteActionInSettings(PasswordNoteAction action);
+
} // namespace metrics_util
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_util_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_metrics_util_unittest.cc
new file mode 100644
index 00000000000..95f0312c4b1
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_manager_metrics_util_unittest.cc
@@ -0,0 +1,131 @@
+// Copyright 2022 The Chromium 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/password_manager/core/browser/password_manager_metrics_util.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager::metrics_util {
+
+namespace {
+
+constexpr ukm::SourceId kTestSourceId = 0x1234;
+
+using UkmEntry = ukm::builders::PasswordManager_LeakWarningDialog;
+
+// Create a LeakDialogMetricsRecorder for a test source id.
+// Tests in this unit test are somewhat perfunctory due to the limited
+// functionality of the class. On top of this, the unit tests for
+// CredentialLeakDialogControllerImpl also test metrics recording.
+LeakDialogMetricsRecorder CreateMetricsRecorder(LeakDialogType dialog_type) {
+ return LeakDialogMetricsRecorder(kTestSourceId, dialog_type);
+}
+
+} // namespace
+
+TEST(PasswordManagerMetricsUtilLeakDialogMetricsRecorder,
+ AutomaticPasswordChangeClicked) {
+ base::test::TaskEnvironment task_environment_;
+ base::HistogramTester histogram_tester;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+
+ LeakDialogMetricsRecorder recorder(
+ CreateMetricsRecorder(LeakDialogType::kChangeAutomatically));
+ recorder.SetSamplingRateForTesting(1.0);
+ recorder.LogLeakDialogTypeAndDismissalReason(
+ LeakDialogDismissalReason::kClickedChangePasswordAutomatically);
+
+ // Check that UMA logging is correct.
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.LeakDetection.DialogDismissalReason",
+ LeakDialogDismissalReason::kClickedChangePasswordAutomatically, 1);
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.LeakDetection.DialogDismissalReason.ChangeAutomatically",
+ LeakDialogDismissalReason::kClickedChangePasswordAutomatically, 1);
+
+ // Check that UKM logging is correct.
+ const auto& entries =
+ test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName);
+ EXPECT_EQ(1u, entries.size());
+ for (const auto* entry : entries) {
+ EXPECT_EQ(kTestSourceId, entry->source_id);
+ test_ukm_recorder.ExpectEntryMetric(
+ entry, UkmEntry::kPasswordLeakDetectionDialogTypeName,
+ static_cast<int64_t>(LeakDialogType::kChangeAutomatically));
+ test_ukm_recorder.ExpectEntryMetric(
+ entry, UkmEntry::kPasswordLeakDetectionDialogDismissalReasonName,
+ static_cast<int64_t>(
+ LeakDialogDismissalReason::kClickedChangePasswordAutomatically));
+ }
+}
+
+TEST(PasswordManagerMetricsUtilLeakDialogMetricsRecorder, CheckupIgnored) {
+ base::test::TaskEnvironment task_environment_;
+ base::HistogramTester histogram_tester;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+
+ LeakDialogMetricsRecorder recorder(
+ CreateMetricsRecorder(LeakDialogType::kCheckup));
+ recorder.SetSamplingRateForTesting(1.0);
+ recorder.LogLeakDialogTypeAndDismissalReason(
+ LeakDialogDismissalReason::kNoDirectInteraction);
+
+ // Check that UMA logging is correct.
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.LeakDetection.DialogDismissalReason",
+ LeakDialogDismissalReason::kNoDirectInteraction, 1);
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.LeakDetection.DialogDismissalReason.Checkup",
+ LeakDialogDismissalReason::kNoDirectInteraction, 1);
+
+ // Check that UKM logging is correct.
+ const auto& entries =
+ test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName);
+ EXPECT_EQ(1u, entries.size());
+ for (const auto* entry : entries) {
+ EXPECT_EQ(kTestSourceId, entry->source_id);
+ test_ukm_recorder.ExpectEntryMetric(
+ entry, UkmEntry::kPasswordLeakDetectionDialogTypeName,
+ static_cast<int64_t>(LeakDialogType::kCheckup));
+ test_ukm_recorder.ExpectEntryMetric(
+ entry, UkmEntry::kPasswordLeakDetectionDialogDismissalReasonName,
+ static_cast<int64_t>(LeakDialogDismissalReason::kNoDirectInteraction));
+ }
+}
+
+TEST(PasswordManagerMetricsUtil, LogNewlySavedPasswordMetrics) {
+ base::HistogramTester histogram_tester;
+
+ constexpr bool kIsGeneratedPassword = true;
+ constexpr bool kIsUsernameEmpty = true;
+ LogNewlySavedPasswordMetrics(
+ /*is_generated_password=*/true, /*is_username_empty=*/true,
+ PasswordAccountStorageUsageLevel::kNotUsingAccountStorage);
+
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.NewlySavedPasswordIsGenerated", kIsGeneratedPassword, 1);
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.NewlySavedPasswordIsGenerated.NotUsingAccountStorage",
+ kIsGeneratedPassword, 1);
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.NewlySavedPasswordIsGenerated.UsingAccountStorage", 0);
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.NewlySavedPasswordIsGenerated.Syncing", 0);
+
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.NewlySavedPasswordHasEmptyUsername.Overall",
+ kIsUsernameEmpty, 1);
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.NewlySavedPasswordHasEmptyUsername.AutoGenerated",
+ kIsUsernameEmpty, 1);
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.NewlySavedPasswordHasEmptyUsername.UserCreated", 0);
+}
+
+} // namespace password_manager::metrics_util
diff --git a/chromium/components/password_manager/core/browser/password_manager_setting.h b/chromium/components/password_manager/core/browser/password_manager_setting.h
new file mode 100644
index 00000000000..e18e32c236a
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_manager_setting.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_SETTING_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_SETTING_H_
+
+namespace password_manager {
+
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.password_manager
+enum class PasswordManagerSetting {
+ // Setting controlling whether the password manager offers password
+ // saving.
+ kOfferToSavePasswords = 0,
+
+ // Setting controlling whether the password manager can use the
+ // automatically sign in users based on their saved passwords on sites
+ // which support this.
+ kAutoSignIn = 1,
+
+ kMaxValue = kAutoSignIn,
+};
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_SETTING_H_
diff --git a/chromium/components/password_manager/core/browser/password_manager_settings_service.h b/chromium/components/password_manager/core/browser/password_manager_settings_service.h
new file mode 100644
index 00000000000..ba20b642d4b
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_manager_settings_service.h
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_SETTINGS_SERVICE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_SETTINGS_SERVICE_H_
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/password_manager/core/browser/password_manager_setting.h"
+
+// Service used to access the password manager settings.
+class PasswordManagerSettingsService : public KeyedService {
+ public:
+ // Checks if `setting` is enabled. It ensures that the correct pref is checked
+ // on Android, which depends on the unified password manager status.
+ virtual bool IsSettingEnabled(
+ password_manager::PasswordManagerSetting setting) = 0;
+
+ // Asynchronously fetch password settings from backend.
+ virtual void RequestSettingsFromBackend() = 0;
+
+ // Sets the auto sign in setting to off. Used by the auto sign in first run
+ // dialog.
+ virtual void TurnOffAutoSignIn() = 0;
+
+ protected:
+ ~PasswordManagerSettingsService() override = default;
+};
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_SETTINGS_SERVICE_H_
diff --git a/chromium/components/password_manager/core/browser/password_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_unittest.cc
index 291de2f8168..0f3b06d77fd 100644
--- a/chromium/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_unittest.cc
@@ -221,6 +221,10 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
(),
(const, override));
MOCK_METHOD(version_info::Channel, GetChannel, (), (const override));
+ MOCK_METHOD(void,
+ RefreshPasswordManagerSettingsIfNeeded,
+ (),
+ (const, override));
MOCK_METHOD(WebAuthnCredentialsDelegate*,
GetWebAuthnCredentialsDelegate,
(),
@@ -4456,6 +4460,21 @@ TEST_P(PasswordManagerTest, StartLeakCheckWhenForUsernameNotMuted) {
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
+TEST_P(PasswordManagerTest, ParsingNewFormsTriggersSettingFetch) {
+ // Check that seeing the form for the first time triggers fetching settings.
+ std::vector<FormData> observed;
+ observed.emplace_back(MakeSignUpFormData());
+ EXPECT_CALL(client_, RefreshPasswordManagerSettingsIfNeeded);
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+
+ // Check that settings are not refetched if the already seen form dynamically
+ // changes and is parsed again.
+ FormFieldData new_field;
+ observed[0].fields.push_back(new_field);
+ EXPECT_CALL(client_, RefreshPasswordManagerSettingsIfNeeded).Times(0);
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+}
+
INSTANTIATE_TEST_SUITE_P(, PasswordManagerTest, testing::Bool());
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_util.cc b/chromium/components/password_manager/core/browser/password_manager_util.cc
index aacaab95649..f530660dc1e 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_util.cc
@@ -71,60 +71,6 @@ bool IsBetterMatch(const PasswordForm* lhs, const PasswordForm* rhs) {
} // namespace
-bool IsSavingPasswordsEnabled(const PrefService* pref_service,
- const syncer::SyncService* sync_service) {
- DCHECK(pref_service);
- const PrefService::Preference* save_passwords_pref =
- pref_service->FindPreference(
- password_manager::prefs::kCredentialsEnableService);
- DCHECK(save_passwords_pref);
-#if BUILDFLAG(IS_ANDROID)
- if (!password_bubble_experiment::HasChosenToSyncPasswords(sync_service)) {
- return save_passwords_pref->GetValue()->GetBool();
- }
-
- if (!password_manager::features::UsesUnifiedPasswordManagerUi()) {
- return save_passwords_pref->GetValue()->GetBool();
- }
-
- if (save_passwords_pref->IsManaged()) {
- return save_passwords_pref->GetValue()->GetBool();
- }
-
- return pref_service->GetBoolean(
- password_manager::prefs::kOfferToSavePasswordsEnabledGMS);
-#else
- return save_passwords_pref->GetValue()->GetBool();
-#endif
-}
-
-bool IsAutoSignInEnabled(const PrefService* pref_service,
- const syncer::SyncService* sync_service) {
- DCHECK(pref_service);
- const PrefService::Preference* auto_sign_in_pref =
- pref_service->FindPreference(
- password_manager::prefs::kCredentialsEnableAutosignin);
- DCHECK(auto_sign_in_pref);
-#if BUILDFLAG(IS_ANDROID)
- if (!password_bubble_experiment::HasChosenToSyncPasswords(sync_service)) {
- return auto_sign_in_pref->GetValue()->GetBool();
- }
-
- if (!password_manager::features::UsesUnifiedPasswordManagerUi()) {
- return auto_sign_in_pref->GetValue()->GetBool();
- }
-
- if (auto_sign_in_pref->IsManaged()) {
- return auto_sign_in_pref->GetValue()->GetBool();
- }
-
- return pref_service->GetBoolean(
- password_manager::prefs::kAutoSignInEnabledGMS);
-#else
- return auto_sign_in_pref->GetValue()->GetBool();
-#endif
-}
-
// Update |credential| to reflect usage.
void UpdateMetadataForUsage(PasswordForm* credential) {
++credential->times_used;
diff --git a/chromium/components/password_manager/core/browser/password_manager_util.h b/chromium/components/password_manager/core/browser/password_manager_util.h
index 5fb4ae6109b..769ddf5e348 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util.h
+++ b/chromium/components/password_manager/core/browser/password_manager_util.h
@@ -53,18 +53,6 @@ enum class GetLoginMatchType {
kPSL,
};
-// Checks if saving passwords is enabled. On Android, it ensures that the
-// correct pref is checked on Android, which depends on the unified password
-// manager status.
-bool IsSavingPasswordsEnabled(const PrefService* pref_service,
- const syncer::SyncService* sync_service);
-
-// Checks if auto sign in is enabled. On Android, it ensures that the
-// correct pref is checked on Android, which depends on the unified password
-// manager status.
-bool IsAutoSignInEnabled(const PrefService* pref_service,
- const syncer::SyncService* sync_service);
-
// Update |credential| to reflect usage.
void UpdateMetadataForUsage(password_manager::PasswordForm* credential);
diff --git a/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc
index 947f6bc7738..9e7032695f2 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_util_unittest.cc
@@ -232,7 +232,7 @@ class MockAutofillClient : public autofill::AutofillClient {
MOCK_METHOD(bool, IsPasswordManagerEnabled, (), (override));
MOCK_METHOD(void,
PropagateAutofillPredictions,
- (content::RenderFrameHost*,
+ (autofill::AutofillDriver*,
const std::vector<autofill::FormStructure*>&),
(override));
MOCK_METHOD(void,
@@ -255,6 +255,10 @@ class MockAutofillClient : public autofill::AutofillClient {
LoadRiskData,
(base::OnceCallback<void(const std::string&)>),
(override));
+ MOCK_METHOD(void,
+ OpenPromoCodeOfferDetailsURL,
+ (const GURL& url),
+ (override));
};
PasswordForm GetTestAndroidCredential() {
@@ -314,146 +318,6 @@ class PasswordManagerUtilTest : public testing::Test {
syncer::TestSyncService sync_service_;
};
-#if BUILDFLAG(IS_ANDROID)
-TEST_F(PasswordManagerUtilTest, SavePasswordsSettingNoUPM) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndDisableFeature(
- password_manager::features::kUnifiedPasswordManagerAndroid);
- sync_service_.GetUserSettings()->SetSelectedTypes(
- false, {syncer::UserSelectableType::kPasswords});
-
- pref_service_.SetUserPref(password_manager::prefs::kCredentialsEnableService,
- base::Value(true));
- pref_service_.SetUserPref(
- password_manager::prefs::kOfferToSavePasswordsEnabledGMS,
- base::Value(false));
- EXPECT_TRUE(password_manager_util::IsSavingPasswordsEnabled(&pref_service_,
- &sync_service_));
-}
-
-TEST_F(PasswordManagerUtilTest, SavePasswordsSettingNotSyncing) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- password_manager::features::kUnifiedPasswordManagerAndroid);
- sync_service_.GetUserSettings()->SetSelectedTypes(false, {});
- pref_service_.SetUserPref(password_manager::prefs::kCredentialsEnableService,
- base::Value(true));
- pref_service_.SetUserPref(
- password_manager::prefs::kOfferToSavePasswordsEnabledGMS,
- base::Value(false));
- EXPECT_TRUE(password_manager_util::IsSavingPasswordsEnabled(&pref_service_,
- &sync_service_));
-}
-
-TEST_F(PasswordManagerUtilTest, SavePasswordsSettingSyncingUPMManaged) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- password_manager::features::kUnifiedPasswordManagerAndroid);
- sync_service_.GetUserSettings()->SetSelectedTypes(
- false, {syncer::UserSelectableType::kPasswords});
- pref_service_.SetManagedPref(
- password_manager::prefs::kCredentialsEnableService, base::Value(false));
- pref_service_.SetUserPref(
- password_manager::prefs::kOfferToSavePasswordsEnabledGMS,
- base::Value(true));
- EXPECT_FALSE(password_manager_util::IsSavingPasswordsEnabled(&pref_service_,
- &sync_service_));
-}
-
-TEST_F(PasswordManagerUtilTest, SavePasswordsSettingSyncingUPMNotManaged) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- password_manager::features::kUnifiedPasswordManagerAndroid);
- sync_service_.GetUserSettings()->SetSelectedTypes(
- false, {syncer::UserSelectableType::kPasswords});
- pref_service_.SetUserPref(password_manager::prefs::kCredentialsEnableService,
- base::Value(true));
- pref_service_.SetUserPref(
- password_manager::prefs::kOfferToSavePasswordsEnabledGMS,
- base::Value(false));
- EXPECT_FALSE(password_manager_util::IsSavingPasswordsEnabled(&pref_service_,
- &sync_service_));
-}
-
-TEST_F(PasswordManagerUtilTest, AutoSignInSettingNoUPM) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndDisableFeature(
- password_manager::features::kUnifiedPasswordManagerAndroid);
- sync_service_.GetUserSettings()->SetSelectedTypes(
- false, {syncer::UserSelectableType::kPasswords});
-
- pref_service_.SetUserPref(
- password_manager::prefs::kCredentialsEnableAutosignin, base::Value(true));
- pref_service_.SetUserPref(password_manager::prefs::kAutoSignInEnabledGMS,
- base::Value(false));
- EXPECT_TRUE(password_manager_util::IsAutoSignInEnabled(&pref_service_,
- &sync_service_));
-}
-
-TEST_F(PasswordManagerUtilTest, AutoSignInSettingNotSyncing) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- password_manager::features::kUnifiedPasswordManagerAndroid);
- sync_service_.GetUserSettings()->SetSelectedTypes(false, {});
- pref_service_.SetUserPref(
- password_manager::prefs::kCredentialsEnableAutosignin, base::Value(true));
- pref_service_.SetUserPref(password_manager::prefs::kAutoSignInEnabledGMS,
- base::Value(false));
- EXPECT_TRUE(password_manager_util::IsAutoSignInEnabled(&pref_service_,
- &sync_service_));
-}
-
-TEST_F(PasswordManagerUtilTest, AutoSignInSettingSyncingUPMManaged) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- password_manager::features::kUnifiedPasswordManagerAndroid);
- sync_service_.GetUserSettings()->SetSelectedTypes(
- false, {syncer::UserSelectableType::kPasswords});
- pref_service_.SetManagedPref(
- password_manager::prefs::kCredentialsEnableAutosignin,
- base::Value(false));
- pref_service_.SetUserPref(password_manager::prefs::kAutoSignInEnabledGMS,
- base::Value(true));
- EXPECT_FALSE(password_manager_util::IsAutoSignInEnabled(&pref_service_,
- &sync_service_));
-}
-
-TEST_F(PasswordManagerUtilTest, AutoSignInSettingSyncingUPMNotManaged) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- password_manager::features::kUnifiedPasswordManagerAndroid);
- sync_service_.GetUserSettings()->SetSelectedTypes(
- false, {syncer::UserSelectableType::kPasswords});
- pref_service_.SetUserPref(
- password_manager::prefs::kCredentialsEnableAutosignin, base::Value(true));
- pref_service_.SetUserPref(password_manager::prefs::kAutoSignInEnabledGMS,
- base::Value(false));
- EXPECT_FALSE(password_manager_util::IsAutoSignInEnabled(&pref_service_,
- &sync_service_));
-}
-#else // !BUILDFLAG(IS_ANDROID)
-TEST_F(PasswordManagerUtilTest, SavePasswordsSettingNotAndroid) {
- sync_service_.GetUserSettings()->SetSelectedTypes(
- false, {syncer::UserSelectableType::kPasswords});
-
- pref_service_.SetUserPref(password_manager::prefs::kCredentialsEnableService,
- base::Value(false));
- EXPECT_FALSE(password_manager_util::IsSavingPasswordsEnabled(&pref_service_,
- &sync_service_));
-}
-
-TEST_F(PasswordManagerUtilTest, AutoSignInSettingNotAndroid) {
- sync_service_.GetUserSettings()->SetSelectedTypes(
- false, {syncer::UserSelectableType::kPasswords});
-
- pref_service_.SetUserPref(
- password_manager::prefs::kCredentialsEnableAutosignin,
- base::Value(false));
- EXPECT_FALSE(password_manager_util::IsAutoSignInEnabled(&pref_service_,
- &sync_service_));
-}
-#endif // BUILDFLAG(IS_ANDROID)
-
TEST(PasswordManagerUtil, TrimUsernameOnlyCredentials) {
std::vector<std::unique_ptr<PasswordForm>> forms;
std::vector<std::unique_ptr<PasswordForm>> expected_forms;
diff --git a/chromium/components/password_manager/core/browser/password_notes_table.cc b/chromium/components/password_manager/core/browser/password_notes_table.cc
index c6072849422..e7b3615f22e 100644
--- a/chromium/components/password_manager/core/browser/password_notes_table.cc
+++ b/chromium/components/password_manager/core/browser/password_notes_table.cc
@@ -22,24 +22,27 @@
namespace password_manager {
namespace {
-// Helper function to return a password note map from the SQL statement.
-std::map<FormPrimaryKey, PasswordNote> StatementToPasswordNotes(
+// Helper function to return a password notes map from the SQL statement.
+std::map<FormPrimaryKey, std::vector<PasswordNote>> StatementToPasswordNotes(
sql::Statement* s) {
- std::map<FormPrimaryKey, PasswordNote> results;
+ std::map<FormPrimaryKey, std::vector<PasswordNote>> results;
while (s->Step()) {
+ std::u16string unique_display_name = s->ColumnString16(1);
std::string encrypted_value;
- s->ColumnBlobAsString(1, &encrypted_value);
+ s->ColumnBlobAsString(2, &encrypted_value);
std::u16string decrypted_value;
if (LoginDatabase::DecryptedString(encrypted_value, &decrypted_value) !=
LoginDatabase::ENCRYPTION_RESULT_SUCCESS) {
continue;
}
base::Time date_created = base::Time::FromDeltaSinceWindowsEpoch(
- base::Microseconds(s->ColumnInt64(2)));
+ base::Microseconds(s->ColumnInt64(3)));
+ bool hide_by_default = s->ColumnBool(4);
- results.emplace(
- FormPrimaryKey(s->ColumnInt(0)),
- PasswordNote(std::move(decrypted_value), std::move(date_created)));
+ std::vector<PasswordNote>& notes = results[FormPrimaryKey(s->ColumnInt(0))];
+ notes.emplace_back(std::move(unique_display_name),
+ std::move(decrypted_value), date_created,
+ hide_by_default);
}
return results;
}
@@ -64,20 +67,20 @@ bool PasswordNotesTable::InsertOrReplace(FormPrimaryKey parent_id,
sql::Statement s(db_->GetCachedStatement(
SQL_FROM_HERE,
base::StringPrintf("INSERT OR REPLACE INTO %s (parent_id, key, value, "
- "date_created) VALUES (?, ?, ?, ?)",
+ "date_created, confidential) VALUES (?, ?, ?, ?, ?)",
kTableName)
.c_str()));
s.BindInt(0, parent_id.value());
- // Key column is not used but added for future compatibility.
- s.BindString(1, "");
+ s.BindString16(1, note.unique_display_name);
s.BindString(2, encrypted_value);
s.BindInt64(3, note.date_created.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ s.BindBool(4, note.hide_by_default);
return s.Run() && db_->GetLastChangeCount();
}
-bool PasswordNotesTable::RemovePasswordNote(FormPrimaryKey parent_id) {
+bool PasswordNotesTable::RemovePasswordNotes(FormPrimaryKey parent_id) {
DCHECK(db_);
sql::Statement s(db_->GetCachedStatement(
SQL_FROM_HERE,
@@ -88,27 +91,30 @@ bool PasswordNotesTable::RemovePasswordNote(FormPrimaryKey parent_id) {
return s.Run() && db_->GetLastChangeCount();
}
-absl::optional<PasswordNote> PasswordNotesTable::GetPasswordNote(
+std::vector<PasswordNote> PasswordNotesTable::GetPasswordNotes(
FormPrimaryKey parent_id) const {
DCHECK(db_);
sql::Statement s(db_->GetCachedStatement(
- SQL_FROM_HERE, base::StringPrintf("SELECT parent_id, value, date_created "
- "FROM %s WHERE parent_id = ? ",
- kTableName)
- .c_str()));
+ SQL_FROM_HERE,
+ base::StringPrintf(
+ "SELECT parent_id, key, value, date_created, confidential "
+ "FROM %s WHERE parent_id = ? ",
+ kTableName)
+ .c_str()));
s.BindInt(0, parent_id.value());
- auto notes = StatementToPasswordNotes(&s);
- return notes.empty() ? absl::optional<PasswordNote>() : notes[parent_id];
+ return StatementToPasswordNotes(&s)[parent_id];
}
-std::map<FormPrimaryKey, PasswordNote>
+std::map<FormPrimaryKey, std::vector<PasswordNote>>
PasswordNotesTable::GetAllPasswordNotesForTest() const {
DCHECK(db_);
sql::Statement s(db_->GetCachedStatement(
- SQL_FROM_HERE, base::StringPrintf("SELECT parent_id, value, date_created "
- "FROM %s",
- kTableName)
- .c_str()));
+ SQL_FROM_HERE,
+ base::StringPrintf(
+ "SELECT parent_id, key, value, date_created, confidential "
+ "FROM %s",
+ kTableName)
+ .c_str()));
return StatementToPasswordNotes(&s);
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_notes_table.h b/chromium/components/password_manager/core/browser/password_notes_table.h
index ce7e942761e..e02a0ea20bd 100644
--- a/chromium/components/password_manager/core/browser/password_notes_table.h
+++ b/chromium/components/password_manager/core/browser/password_notes_table.h
@@ -34,16 +34,16 @@ class PasswordNotesTable {
// Adds the note if it doesn't exist.
// If it does, it removes the previous entry and adds the new one.
- // Note that it sets the key column as empty string.
bool InsertOrReplace(FormPrimaryKey parent_id, const PasswordNote& note);
- // Removes the note corresponding to `parent_id`.
- bool RemovePasswordNote(FormPrimaryKey parent_id);
+ // Removes the notes corresponding to `parent_id`.
+ bool RemovePasswordNotes(FormPrimaryKey parent_id);
- // Gets the note in the database for `parent_id`.
- absl::optional<PasswordNote> GetPasswordNote(FormPrimaryKey parent_id) const;
+ // Gets the notes in the database for `parent_id`.
+ std::vector<PasswordNote> GetPasswordNotes(FormPrimaryKey parent_id) const;
- std::map<FormPrimaryKey, PasswordNote> GetAllPasswordNotesForTest() const;
+ std::map<FormPrimaryKey, std::vector<PasswordNote>>
+ GetAllPasswordNotesForTest() const;
private:
sql::Database* db_ = nullptr;
diff --git a/chromium/components/password_manager/core/browser/password_notes_table_unittest.cc b/chromium/components/password_manager/core/browser/password_notes_table_unittest.cc
index 7d1474de368..8fd3ceab163 100644
--- a/chromium/components/password_manager/core/browser/password_notes_table_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_notes_table_unittest.cc
@@ -38,6 +38,8 @@ using testing::IsEmpty;
using testing::SizeIs;
using testing::UnorderedElementsAre;
+// TODO(crbug.com/1326554): Update the tests in this file to cover
+// reading/writing of fields other than the note value.
class PasswordNotesTableTest : public testing::Test {
protected:
void SetUp() override {
@@ -75,12 +77,14 @@ TEST_F(PasswordNotesTableTest,
EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kFirstNote));
EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kSecondNote));
- EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(1)), kSecondNote);
+ EXPECT_THAT(table()->GetPasswordNotes(FormPrimaryKey(1)),
+ ElementsAre(kSecondNote));
EXPECT_THAT(table()->GetAllPasswordNotesForTest(), SizeIs(1));
EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kThirdNote));
- EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(1)), kThirdNote);
+ EXPECT_THAT(table()->GetPasswordNotes(FormPrimaryKey(1)),
+ ElementsAre(kThirdNote));
EXPECT_THAT(table()->GetAllPasswordNotesForTest(), SizeIs(1));
}
@@ -93,26 +97,31 @@ TEST_F(PasswordNotesTableTest, ReloadingDatabasePersistsEntries) {
ReloadDatabase();
EXPECT_THAT(table()->GetAllPasswordNotesForTest(),
- ElementsAre(std::make_pair(FormPrimaryKey(1), kFirstNote),
- std::make_pair(FormPrimaryKey(2), kSecondNote)));
+ UnorderedElementsAre(
+ std::make_pair(FormPrimaryKey(1),
+ std::vector<PasswordNote>({kFirstNote})),
+ std::make_pair(FormPrimaryKey(2),
+ std::vector<PasswordNote>({kSecondNote}))));
}
-TEST_F(PasswordNotesTableTest, GetPasswordNote) {
+TEST_F(PasswordNotesTableTest, GetPasswordNotes) {
EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user1")), SizeIs(1));
EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user2")), SizeIs(1));
EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kFirstNote));
EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(2), kSecondNote));
- EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(1)), kFirstNote);
+ EXPECT_THAT(table()->GetPasswordNotes(FormPrimaryKey(1)),
+ ElementsAre(kFirstNote));
- EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(2)), kSecondNote);
+ EXPECT_THAT(table()->GetPasswordNotes(FormPrimaryKey(2)),
+ ElementsAre(kSecondNote));
}
-TEST_F(PasswordNotesTableTest, GetPasswordNoteWhenParentIdDoesntExist) {
- EXPECT_EQ(table()->GetPasswordNote(FormPrimaryKey(2)), absl::nullopt);
+TEST_F(PasswordNotesTableTest, GetPasswordNotesWhenParentIdDoesntExist) {
+ EXPECT_TRUE(table()->GetPasswordNotes(FormPrimaryKey(2)).empty());
}
-TEST_F(PasswordNotesTableTest, RemovePasswordNote) {
+TEST_F(PasswordNotesTableTest, RemovePasswordNotes) {
EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user1")), SizeIs(1));
EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user2")), SizeIs(1));
EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user3")), SizeIs(1));
@@ -120,20 +129,24 @@ TEST_F(PasswordNotesTableTest, RemovePasswordNote) {
EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(2), kSecondNote));
EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(3), kThirdNote));
- EXPECT_TRUE(table()->RemovePasswordNote(FormPrimaryKey(2)));
+ EXPECT_TRUE(table()->RemovePasswordNotes(FormPrimaryKey(2)));
- EXPECT_THAT(table()->GetAllPasswordNotesForTest(),
- ElementsAre(std::make_pair(FormPrimaryKey(1), kFirstNote),
- std::make_pair(FormPrimaryKey(3), kThirdNote)));
+ EXPECT_THAT(
+ table()->GetAllPasswordNotesForTest(),
+ ElementsAre(std::make_pair(FormPrimaryKey(1),
+ std::vector<PasswordNote>({kFirstNote})),
+ std::make_pair(FormPrimaryKey(3),
+ std::vector<PasswordNote>({kThirdNote}))));
}
-TEST_F(PasswordNotesTableTest, RemovePasswordNoteWithNonExistingKey) {
+TEST_F(PasswordNotesTableTest, RemovePasswordNotesWithNonExistingKey) {
EXPECT_THAT(login_db()->AddLogin(CreatePasswordForm(u"user1")), SizeIs(1));
EXPECT_TRUE(table()->InsertOrReplace(FormPrimaryKey(1), kFirstNote));
- EXPECT_FALSE(table()->RemovePasswordNote(FormPrimaryKey(1000)));
+ EXPECT_FALSE(table()->RemovePasswordNotes(FormPrimaryKey(1000)));
EXPECT_THAT(table()->GetAllPasswordNotesForTest(),
- ElementsAre(std::make_pair(FormPrimaryKey(1), kFirstNote)));
+ ElementsAre(std::make_pair(
+ FormPrimaryKey(1), std::vector<PasswordNote>({kFirstNote}))));
}
} // namespace
diff --git a/chromium/components/password_manager/core/browser/password_reuse_detector.cc b/chromium/components/password_manager/core/browser/password_reuse_detector.cc
index fc34aab95cb..bb517ec3d61 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detector.cc
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector.cc
@@ -17,10 +17,6 @@
#include "components/password_manager/core/browser/psl_matching_helper.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "google_apis/gaia/gaia_auth_util.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "url/origin.h"
-
-using url::Origin;
namespace password_manager {
@@ -195,9 +191,7 @@ absl::optional<PasswordHashData> PasswordReuseDetector::CheckGaiaPasswordReuse(
}
// Skips password reuse check if |domain| matches Gaia origin.
- const Origin gaia_origin = Origin::Create(
- GaiaUrls::GetInstance()->gaia_url().DeprecatedGetOriginAsURL());
- if (Origin::Create(GURL(domain)).IsSameOriginWith(gaia_origin))
+ if (gaia::HasGaiaSchemeHostPort(GURL(domain)))
return absl::nullopt;
return FindPasswordReuse(input, gaia_password_hash_data_list_.value());
diff --git a/chromium/components/password_manager/core/browser/password_save_manager_impl.cc b/chromium/components/password_manager/core/browser/password_save_manager_impl.cc
index 2a403d55199..d20089559b2 100644
--- a/chromium/components/password_manager/core/browser/password_save_manager_impl.cc
+++ b/chromium/components/password_manager/core/browser/password_save_manager_impl.cc
@@ -780,8 +780,9 @@ void PasswordSaveManagerImpl::UploadVotesAndMetrics(
#endif // !BUILDFLAG(IS_ANDROID)
if (IsNewLogin()) {
- metrics_util::LogNewlySavedPasswordIsGenerated(
+ metrics_util::LogNewlySavedPasswordMetrics(
pending_credentials_.type == PasswordForm::Type::kGenerated,
+ pending_credentials_.username_value.empty(),
client_->GetPasswordFeatureManager()
->ComputePasswordAccountStorageUsageLevel());
// Don't send votes if there was no observed form.
diff --git a/chromium/components/password_manager/core/browser/password_save_manager_impl_unittest.cc b/chromium/components/password_manager/core/browser/password_save_manager_impl_unittest.cc
index d05e850c805..d4139bb6e56 100644
--- a/chromium/components/password_manager/core/browser/password_save_manager_impl_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_save_manager_impl_unittest.cc
@@ -72,7 +72,7 @@ void CheckPendingCredentials(const PasswordForm& expected,
EXPECT_EQ(expected.password_element, actual.password_element);
EXPECT_EQ(expected.blocked_by_user, actual.blocked_by_user);
EXPECT_TRUE(
- autofill::FormDataEqualForTesting(expected.form_data, actual.form_data));
+ autofill::FormData::DeepEqual(expected.form_data, actual.form_data));
}
struct ExpectedGenerationUKM {
diff --git a/chromium/components/password_manager/core/browser/password_scripts_fetcher.h b/chromium/components/password_manager/core/browser/password_scripts_fetcher.h
index ac0db636414..dc640f48b5f 100644
--- a/chromium/components/password_manager/core/browser/password_scripts_fetcher.h
+++ b/chromium/components/password_manager/core/browser/password_scripts_fetcher.h
@@ -62,6 +62,9 @@ class PasswordScriptsFetcher : public KeyedService {
// Return high-level state summary of the PasswordScriptsFetcher in form
// of a `base::Value::Dict` for display on chrome://apc-internals.
virtual base::Value::Dict GetDebugInformationForInternals() const = 0;
+
+ // Return a list of all entries currently held in the cache.
+ virtual base::Value::List GetCacheEntries() const = 0;
};
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.cc b/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.cc
index 9e32e62ef10..fa13330ef47 100644
--- a/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.cc
+++ b/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.cc
@@ -79,6 +79,12 @@ base::flat_set<ParsingResult> ParseDomainSpecificParamaters(
warnings.insert(ParsingResult::kInvalidUrl);
continue;
}
+
+ if (url.SchemeIs(url::kHttpScheme)) {
+ // Http schemes are not supported.
+ continue;
+ }
+
supported_domains.insert(std::make_pair(url::Origin::Create(url), version));
}
@@ -97,17 +103,21 @@ constexpr base::FeatureParam<std::string> kScriptsListUrlParam{
kDefaultChangePasswordScriptsListUrl};
PasswordScriptsFetcherImpl::PasswordScriptsFetcherImpl(
+ bool is_supervised_user,
const base::Version& version,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
- : PasswordScriptsFetcherImpl(version,
+ : PasswordScriptsFetcherImpl(is_supervised_user,
+ version,
std::move(url_loader_factory),
kScriptsListUrlParam.Get()) {}
PasswordScriptsFetcherImpl::PasswordScriptsFetcherImpl(
+ bool is_supervised_user,
const base::Version& version,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::string scripts_list_url)
- : version_(version),
+ : is_supervised_user_(is_supervised_user),
+ version_(version),
scripts_list_url_(std::move(scripts_list_url)),
url_loader_factory_(std::move(url_loader_factory)) {}
@@ -276,6 +286,12 @@ base::flat_set<ParsingResult> PasswordScriptsFetcherImpl::ParseResponse(
}
bool PasswordScriptsFetcherImpl::IsCacheStale() const {
+ // For supervised users, we always simulate a fresh cache to avoid fetching
+ // scripts.
+ if (is_supervised_user_) {
+ return false;
+ }
+
static const base::TimeDelta kCacheTimeout(
base::Minutes(kCacheTimeoutInMinutes));
return last_fetch_timestamp_.is_null() ||
@@ -298,4 +314,16 @@ base::Value::Dict PasswordScriptsFetcherImpl::GetDebugInformationForInternals()
return result;
}
+base::Value::List PasswordScriptsFetcherImpl::GetCacheEntries() const {
+ base::Value::List cache_entries;
+
+ for (const auto& [origin, version] : password_change_domains_) {
+ base::Value::Dict entry;
+ entry.Set("url", origin.Serialize());
+ cache_entries.Append(std::move(entry));
+ }
+
+ return cache_entries;
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.h b/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.h
index dd0e35ebb5e..c0d7860593c 100644
--- a/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.h
+++ b/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl.h
@@ -19,14 +19,14 @@
#include "components/password_manager/core/browser/password_scripts_fetcher.h"
#include "services/network/public/cpp/simple_url_loader.h"
-namespace url {
-class Origin;
-}
-
namespace network {
class SharedURLLoaderFactory;
}
+namespace url {
+class Origin;
+}
+
namespace password_manager {
extern const char kDefaultChangePasswordScriptsListUrl[];
@@ -51,9 +51,11 @@ class PasswordScriptsFetcherImpl
// The first constructor calls the second one. The second one is called
// directly only from tests.
PasswordScriptsFetcherImpl(
+ bool is_supervised_user,
const base::Version& version,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
PasswordScriptsFetcherImpl(
+ bool is_supervised_user,
const base::Version& version,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::string scripts_list_url);
@@ -68,6 +70,7 @@ class PasswordScriptsFetcherImpl
ResponseCallback callback) override;
bool IsScriptAvailable(const url::Origin& origin) const override;
base::Value::Dict GetDebugInformationForInternals() const override;
+ base::Value::List GetCacheEntries() const override;
#if defined(UNIT_TEST)
void make_cache_stale_for_testing() {
@@ -94,6 +97,11 @@ class PasswordScriptsFetcherImpl
// Runs |callback| immediately with the script availability for |origin|.
void RunResponseCallback(url::Origin origin, ResponseCallback callback);
+ // Indicates whether the user has a supervised account - for those, script
+ // availability already returns `false` unless overwritten by the
+ // `kForceEnablePasswordDomainCapabilities` feature.
+ const bool is_supervised_user_;
+
const base::Version version_;
// URL to fetch a list of scripts from.
diff --git a/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl_unittests.cc b/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl_unittest.cc
index b67dee0359b..20235a159e0 100644
--- a/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl_unittests.cc
+++ b/chromium/components/password_manager/core/browser/password_scripts_fetcher_impl_unittest.cc
@@ -16,6 +16,7 @@
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Pair;
+using ::testing::Return;
using ::testing::UnorderedElementsAre;
namespace {
@@ -68,7 +69,8 @@ base::Version GetVersion() {
namespace password_manager {
class PasswordScriptsFetcherImplTest : public ::testing::Test {
public:
- void Reinitialize(const base::Version& version) {
+ void Reinitialize(const base::Version& version,
+ bool is_supervised_user = false) {
recorded_responses_.clear();
test_url_loader_factory_ =
std::make_unique<network::TestURLLoaderFactory>();
@@ -76,7 +78,8 @@ class PasswordScriptsFetcherImplTest : public ::testing::Test {
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
test_url_loader_factory_.get());
fetcher_ = std::make_unique<PasswordScriptsFetcherImpl>(
- version, test_shared_loader_factory_);
+ /* is_supervised_user= */ is_supervised_user, version,
+ test_shared_loader_factory_);
}
void SetUp() override {
@@ -113,6 +116,10 @@ class PasswordScriptsFetcherImplTest : public ::testing::Test {
RequestSingleScriptAvailability(GetOriginWithoutScript());
}
+ void RequestSingleScriptAvailability(const url::Origin& origin) {
+ fetcher_->FetchScriptAvailability(origin, GenerateResponseCallback(origin));
+ }
+
int GetNumberOfPendingRequests() {
return test_url_loader_factory_->NumPending();
}
@@ -124,10 +131,6 @@ class PasswordScriptsFetcherImplTest : public ::testing::Test {
PasswordScriptsFetcherImpl* fetcher() { return fetcher_.get(); }
private:
- void RequestSingleScriptAvailability(const url::Origin& origin) {
- fetcher_->FetchScriptAvailability(origin, GenerateResponseCallback(origin));
- }
-
void RecordResponse(url::Origin origin, bool has_script) {
const auto& it = recorded_responses_.find(origin);
if (it != recorded_responses_.end()) {
@@ -219,6 +222,32 @@ TEST_F(PasswordScriptsFetcherImplTest, SlowResponse) {
PasswordScriptsFetcher::CacheState::kWaiting, 1u);
}
+TEST_F(PasswordScriptsFetcherImplTest, NoHttpSupport) {
+ fetcher()->PrewarmCache();
+ EXPECT_EQ(1, GetNumberOfPendingRequests());
+ SimulateResponseWithContent(
+ R"({
+ "test.com":
+ {
+ "domains": ["https://test.com", "http://wrong.scheme.test.com"],
+ "min_version": "86"
+ }
+ })");
+ base::RunLoop().RunUntilIdle();
+
+ const url::Origin kOriginWithHttpScheme =
+ url::Origin::Create(GURL("http://wrong.scheme.test.com"));
+ const url::Origin kOriginWithHttpsScheme =
+ url::Origin::Create(GURL("https://test.com"));
+
+ RequestSingleScriptAvailability(kOriginWithHttpsScheme);
+ RequestSingleScriptAvailability(kOriginWithHttpScheme);
+ EXPECT_THAT(recorded_responses(),
+ UnorderedElementsAre(Pair(kOriginWithHttpsScheme, true),
+ Pair(kOriginWithHttpScheme, false)));
+ EXPECT_EQ(0, GetNumberOfPendingRequests());
+}
+
TEST_F(PasswordScriptsFetcherImplTest, NoPrewarmCache) {
base::HistogramTester histogram_tester;
StartBulkCheck(); // Without preceding |PrewarmCache|.
@@ -336,6 +365,41 @@ TEST_F(PasswordScriptsFetcherImplTest, IsScriptAvailable) {
EXPECT_EQ(0, GetNumberOfPendingRequests());
}
+TEST_F(PasswordScriptsFetcherImplTest, IsScriptAvailableForSupervisedUser) {
+ // Simulate a supervised user.
+ Reinitialize(GetVersion(), /*is_supervised_user=*/true);
+ // `IsScriptAvailable` does not trigger any network requests and returns
+ // false.
+ EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithScript1()));
+ EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithoutScript()));
+ EXPECT_EQ(0, GetNumberOfPendingRequests());
+ StartBulkCheck();
+ // No request is ever started.
+ EXPECT_EQ(0, GetNumberOfPendingRequests());
+
+ // The results are still false.
+ EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithScript1()));
+ EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithoutScript()));
+}
+
+TEST_F(PasswordScriptsFetcherImplTest,
+ IsScriptAvailableForSupervisedUserWithDomainFlagSet) {
+ // Simulate a supervised user.
+ Reinitialize(GetVersion(), /*is_supervised_user=*/true);
+
+ EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithScript1()));
+ EXPECT_FALSE(fetcher()->IsScriptAvailable(GetOriginWithoutScript()));
+ EXPECT_EQ(0, GetNumberOfPendingRequests());
+
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(
+ password_manager::features::kForceEnablePasswordDomainCapabilities);
+
+ // The flag overrides even supervised users.
+ EXPECT_TRUE(fetcher()->IsScriptAvailable(GetOriginWithScript1()));
+ EXPECT_TRUE(fetcher()->IsScriptAvailable(GetOriginWithoutScript()));
+}
+
TEST_F(PasswordScriptsFetcherImplTest, EnablePasswordDomainCapabilitiesFlag) {
// |kEnablePasswordDomainCapabilities| flag is disabled, |IsScriptAvailable|
// returns the default value (false).
@@ -377,7 +441,8 @@ TEST_F(PasswordScriptsFetcherImplTest, AnotherScriptsListUrl) {
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory);
- PasswordScriptsFetcherImpl fetcher(GetVersion(), test_shared_loader_factory,
+ PasswordScriptsFetcherImpl fetcher(/*is_supervised_user=*/false, GetVersion(),
+ test_shared_loader_factory,
kNonDefaultScriptsListUrl);
fetcher.PrewarmCache();
@@ -433,4 +498,32 @@ TEST_F(PasswordScriptsFetcherImplTest, DebugInformationForInternals) {
*script_list_url);
}
+TEST_F(PasswordScriptsFetcherImplTest, CheckCacheEntries) {
+ fetcher()->PrewarmCache();
+ EXPECT_EQ(1, GetNumberOfPendingRequests());
+
+ // Cache should still be empty, too.
+ base::Value::List cache_entries = fetcher()->GetCacheEntries();
+ EXPECT_EQ(cache_entries.size(), 0u);
+
+ SimulateResponse();
+ EXPECT_EQ(0, GetNumberOfPendingRequests());
+
+ // Cache should now contain three entries.
+ cache_entries = fetcher()->GetCacheEntries();
+ EXPECT_EQ(cache_entries.size(), 3u);
+
+ std::vector<std::string> urls;
+ // Only `kOriginWithoutScript` is not expected to have a script.
+ for (auto it = cache_entries.begin(); it != cache_entries.end(); ++it) {
+ const std::string* url = it->GetDict().FindString("url");
+ ASSERT_TRUE(url);
+ urls.push_back(*url);
+ }
+
+ // There should be entries for all sites with a script.
+ EXPECT_THAT(urls, UnorderedElementsAre(kOriginWithScript1, kOriginWithScript2,
+ kOriginWithScript3));
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_store.cc b/chromium/components/password_manager/core/browser/password_store.cc
index bf1c84eb919..6bcfe92e654 100644
--- a/chromium/components/password_manager/core/browser/password_store.cc
+++ b/chromium/components/password_manager/core/browser/password_store.cc
@@ -32,6 +32,7 @@
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_reuse_manager_impl.h"
+#include "components/password_manager/core/browser/password_store_backend.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "components/password_manager/core/browser/password_store_signin_notifier.h"
#include "components/password_manager/core/browser/password_store_util.h"
@@ -55,13 +56,13 @@ bool FormSupportsPSL(const PasswordFormDigest& digest) {
// Helper function which invokes |notifying_callback| and |completion_callback|
// when changes are received.
void InvokeCallbacksForSuspectedChanges(
- PasswordStoreChangeListReply notifying_callback,
+ base::OnceCallback<void(PasswordChanges)> notifying_callback,
base::OnceCallback<void(bool)> completion_callback,
- absl::optional<PasswordStoreChangeList> changes) {
+ PasswordChanges changes) {
DCHECK(notifying_callback);
// Two cases *presumably* have changes that need to be reported:
// 1. `changes` contains a non-empty PasswordStoreChangeList.
- // 2. `changes` contains no PasswordStoreChangeList at all because the
+ // 2. `changes` contains nullopt PasswordStoreChangeList because the
// backend can't compute it. A full list will be requested instead.
// Only if `changes` contains an empty PasswordStoreChangeList, Chrome knows
// for certain that no changes have happened:
@@ -111,8 +112,10 @@ void PasswordStore::AddLogin(const PasswordForm& form) {
if (!backend_)
return; // Once the shutdown started, ignore new requests.
backend_->AddLoginAsync(
- form, base::BindOnce(&PasswordStore::NotifyLoginsChangedOnMainSequence,
- this, LoginsChangedTrigger::Addition));
+ form, base::BindOnce(&GetPasswordChangesOrEmptyListOnFailure)
+ .Then(base::BindOnce(
+ &PasswordStore::NotifyLoginsChangedOnMainSequence, this,
+ LoginsChangedTrigger::Addition)));
}
void PasswordStore::UpdateLogin(const PasswordForm& form) {
@@ -120,8 +123,10 @@ void PasswordStore::UpdateLogin(const PasswordForm& form) {
if (!backend_)
return; // Once the shutdown started, ignore new requests.
backend_->UpdateLoginAsync(
- form, base::BindOnce(&PasswordStore::NotifyLoginsChangedOnMainSequence,
- this, LoginsChangedTrigger::Update));
+ form, base::BindOnce(&GetPasswordChangesOrEmptyListOnFailure)
+ .Then(base::BindOnce(
+ &PasswordStore::NotifyLoginsChangedOnMainSequence, this,
+ LoginsChangedTrigger::Update)));
}
void PasswordStore::UpdateLoginWithPrimaryKey(
@@ -134,20 +139,24 @@ void PasswordStore::UpdateLoginWithPrimaryKey(
// TODO(crbug.com/1223022): Re-evaluate this once all places that call
// UpdateLoginWithPrimaryKey() have properly set the |password_issues|
// field.
- if (new_form.username_value != old_primary_key.username_value ||
- new_form.password_value != old_primary_key.password_value) {
- // If the password or the username changes, the password issues aren't valid
+ if (new_form.password_value != old_primary_key.password_value) {
+ // If the password changes, the password issues aren't valid
// any more. Make sure they are cleared before storing the new form.
- new_form_with_correct_password_issues.password_issues =
- base::flat_map<InsecureType, InsecurityMetadata>();
+ new_form_with_correct_password_issues.password_issues.clear();
+ } else if (new_form.username_element != old_primary_key.username_element) {
+ // If the username changed then the phished and leaked issues aren't valid
+ // any more. Make sure they are erased before storing the new form.
+ new_form_with_correct_password_issues.password_issues.erase(
+ InsecureType::kLeaked);
+ new_form_with_correct_password_issues.password_issues.erase(
+ InsecureType::kPhished);
}
- auto barrier_callback =
- base::BarrierCallback<absl::optional<PasswordStoreChangeList>>(
- 2, base::BindOnce(&JoinPasswordStoreChanges)
- .Then(base::BindOnce(
- &PasswordStore::NotifyLoginsChangedOnMainSequence, this,
- LoginsChangedTrigger::Update)));
+ auto barrier_callback = base::BarrierCallback<PasswordChangesOrError>(
+ 2, base::BindOnce(&JoinPasswordStoreChanges)
+ .Then(base::BindOnce(
+ &PasswordStore::NotifyLoginsChangedOnMainSequence, this,
+ LoginsChangedTrigger::Update)));
backend_->RemoveLoginAsync(old_primary_key, barrier_callback);
backend_->AddLoginAsync(new_form_with_correct_password_issues,
@@ -159,8 +168,10 @@ void PasswordStore::RemoveLogin(const PasswordForm& form) {
if (!backend_)
return; // Once the shutdown started, ignore new requests.
backend_->RemoveLoginAsync(
- form, base::BindOnce(&PasswordStore::NotifyLoginsChangedOnMainSequence,
- this, LoginsChangedTrigger::Deletion));
+ form, base::BindOnce(&GetPasswordChangesOrEmptyListOnFailure)
+ .Then(base::BindOnce(
+ &PasswordStore::NotifyLoginsChangedOnMainSequence, this,
+ LoginsChangedTrigger::Deletion)));
}
void PasswordStore::RemoveLoginsByURLAndTime(
@@ -176,8 +187,10 @@ void PasswordStore::RemoveLoginsByURLAndTime(
}
backend_->RemoveLoginsByURLAndTimeAsync(
url_filter, delete_begin, delete_end, std::move(sync_completion),
- base::BindOnce(&PasswordStore::NotifyLoginsChangedOnMainSequence, this,
- LoginsChangedTrigger::BatchDeletion)
+ base::BindOnce(&GetPasswordChangesOrEmptyListOnFailure)
+ .Then(
+ base::BindOnce(&PasswordStore::NotifyLoginsChangedOnMainSequence,
+ this, LoginsChangedTrigger::BatchDeletion))
.Then(std::move(completion)));
}
@@ -195,8 +208,9 @@ void PasswordStore::RemoveLoginsCreatedBetween(
LoginsChangedTrigger::BatchDeletion);
backend_->RemoveLoginsCreatedBetweenAsync(
delete_begin, delete_end,
- base::BindOnce(&InvokeCallbacksForSuspectedChanges, std::move(callback),
- std::move(completion)));
+ base::BindOnce(&GetPasswordChangesOrEmptyListOnFailure)
+ .Then(base::BindOnce(&InvokeCallbacksForSuspectedChanges,
+ std::move(callback), std::move(completion))));
}
void PasswordStore::DisableAutoSignInForOrigins(
@@ -215,8 +229,9 @@ void PasswordStore::Unblocklist(const PasswordFormDigest& form_digest,
if (!backend_)
return; // Once the shutdown started, ignore new requests.
backend_->FillMatchingLoginsAsync(
- base::BindOnce(&PasswordStore::UnblocklistInternal, this,
- std::move(completion)),
+ base::BindOnce(&GetLoginsOrEmptyListOnFailure)
+ .Then(base::BindOnce(&PasswordStore::UnblocklistInternal, this,
+ std::move(completion))),
FormSupportsPSL(form_digest), {form_digest});
}
@@ -234,8 +249,10 @@ void PasswordStore::GetLogins(const PasswordFormDigest& form,
if (affiliated_match_helper_) {
auto branding_injection_for_affiliations_callback =
- base::BindOnce(&PasswordStore::InjectAffiliationAndBrandingInformation,
- this, request_handler->AffiliatedLoginsClosure());
+ base::BindOnce(&GetLoginsOrEmptyListOnFailure)
+ .Then(base::BindOnce(
+ &PasswordStore::InjectAffiliationAndBrandingInformation, this,
+ request_handler->AffiliatedLoginsClosure()));
// `Shutdown` resets the affiliated_match_helper_ before shutting down the
// backend_. Therefore, base::Unretained is safe here.
affiliated_match_helper_->GetAffiliatedAndroidAndWebRealms(
@@ -249,8 +266,10 @@ void PasswordStore::GetLogins(const PasswordFormDigest& form,
}
auto branding_injection_callback =
- base::BindOnce(&PasswordStore::InjectAffiliationAndBrandingInformation,
- this, request_handler->LoginsForFormClosure());
+ base::BindOnce(&GetLoginsOrEmptyListOnFailure)
+ .Then(base::BindOnce(
+ &PasswordStore::InjectAffiliationAndBrandingInformation, this,
+ request_handler->LoginsForFormClosure()));
backend_->FillMatchingLoginsAsync(std::move(branding_injection_callback),
FormSupportsPSL(form), {form});
@@ -462,10 +481,9 @@ void PasswordStore::UnblocklistInternal(
if (completion)
notify_callback = std::move(notify_callback).Then(std::move(completion));
- auto barrier_callback =
- base::BarrierCallback<absl::optional<PasswordStoreChangeList>>(
- forms_to_remove.size(), base::BindOnce(&JoinPasswordStoreChanges)
- .Then(std::move(notify_callback)));
+ auto barrier_callback = base::BarrierCallback<PasswordChangesOrError>(
+ forms_to_remove.size(), base::BindOnce(&JoinPasswordStoreChanges)
+ .Then(std::move(notify_callback)));
for (const auto& form : forms_to_remove) {
backend_->RemoveLoginAsync(form, barrier_callback);
diff --git a/chromium/components/password_manager/core/browser/password_store_backend.h b/chromium/components/password_manager/core/browser/password_store_backend.h
index eaee80df30b..a245f79f88b 100644
--- a/chromium/components/password_manager/core/browser/password_store_backend.h
+++ b/chromium/components/password_manager/core/browser/password_store_backend.h
@@ -38,8 +38,12 @@ enum class PasswordStoreBackendError {
using LoginsResult = std::vector<std::unique_ptr<PasswordForm>>;
using LoginsReply = base::OnceCallback<void(LoginsResult)>;
-using PasswordStoreChangeListReply =
- base::OnceCallback<void(absl::optional<PasswordStoreChangeList>)>;
+
+using PasswordChanges = absl::optional<PasswordStoreChangeList>;
+using PasswordChangesOrError =
+ absl::variant<PasswordChanges, PasswordStoreBackendError>;
+using PasswordChangesOrErrorReply =
+ base::OnceCallback<void(PasswordChangesOrError)>;
using LoginsResultOrError =
absl::variant<LoginsResult, PasswordStoreBackendError>;
@@ -113,9 +117,8 @@ class PasswordStoreBackend {
// If |include_psl|==true, the PSL-matched forms are also included.
// If multiple forms are given, those will be concatenated.
// Callback is called on the main sequence.
- // TODO(crbug.com/1217071): Check whether this needs OptionalLoginsReply, too.
virtual void FillMatchingLoginsAsync(
- LoginsReply callback,
+ LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) = 0;
@@ -126,29 +129,28 @@ class PasswordStoreBackend {
// TODO(crbug.com/1217071): Delete corresponding Impl method from
// PasswordStore and the async method on backend_ instead.
- // The completion callback in each of the write operations below receive an
- // optional PasswordStoreChangeList. In case of success that the changelist
- // will be populated with the executed changes. An empty changelist indicates
- // that some error has occurred during the execution. The absence of the
- // changelist indicates that the used backend (e.g. on Android) cannot
- // confirm of the execution and a re-fetch is required to know the current
- // state of the backend.
+ // The completion callback in each of the write operations below receive a
+ // variant of optional PasswordStoreChangeList or PasswordStoreBackendError.
+ // In case of success that the changelist will be populated with the executed
+ // changes. The absence of the changelist indicates that the used backend
+ // (e.g. on Android) cannot confirm of the execution and a re-fetch is
+ // required to know the current state of the backend.
virtual void AddLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) = 0;
+ PasswordChangesOrErrorReply callback) = 0;
virtual void UpdateLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) = 0;
+ PasswordChangesOrErrorReply callback) = 0;
virtual void RemoveLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) = 0;
+ PasswordChangesOrErrorReply callback) = 0;
virtual void RemoveLoginsByURLAndTimeAsync(
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback) = 0;
+ PasswordChangesOrErrorReply callback) = 0;
virtual void RemoveLoginsCreatedBetweenAsync(
base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback) = 0;
+ PasswordChangesOrErrorReply callback) = 0;
virtual void DisableAutoSignInForOriginsAsync(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::OnceClosure completion) = 0;
diff --git a/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.cc b/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.cc
index 37cb492ec0f..fbd51a1b43f 100644
--- a/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.cc
+++ b/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.cc
@@ -4,8 +4,32 @@
#include "components/password_manager/core/browser/password_store_backend_metrics_recorder.h"
+#include <utility>
+
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
+
namespace password_manager {
+namespace {
+constexpr char kMetricPrefix[] = "PasswordManager.PasswordStore";
+
+bool ShouldRecordLatency(
+ PasswordStoreBackendMetricsRecorder::SuccessStatus success_status) {
+ switch (success_status) {
+ case PasswordStoreBackendMetricsRecorder::SuccessStatus::kSuccess:
+ case PasswordStoreBackendMetricsRecorder::SuccessStatus::kError:
+ return true;
+ case PasswordStoreBackendMetricsRecorder::SuccessStatus::kCancelled:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace
+
PasswordStoreBackendMetricsRecorder::PasswordStoreBackendMetricsRecorder() =
default;
@@ -26,47 +50,86 @@ PasswordStoreBackendMetricsRecorder::~PasswordStoreBackendMetricsRecorder() =
default;
void PasswordStoreBackendMetricsRecorder::RecordMetrics(
- bool success,
+ SuccessStatus success_status,
absl::optional<ErrorFromPasswordStoreOrAndroidBackend> error) const {
- auto BuildMetricName = [this](base::StringPiece suffix) {
- return base::StrCat({"PasswordManager.PasswordStore", *backend_infix_, ".",
- *metric_infix_, ".", suffix});
- };
- auto BuildOverallMetricName = [this](base::StringPiece suffix) {
- return base::StrCat(
- {"PasswordManager.PasswordStoreBackend.", *metric_infix_, ".", suffix});
- };
- base::TimeDelta duration = base::Time::Now() - start_;
- base::UmaHistogramMediumTimes(BuildMetricName("Latency"), duration);
- base::UmaHistogramBoolean(BuildMetricName("Success"), success);
- base::UmaHistogramMediumTimes(BuildOverallMetricName("Latency"), duration);
- base::UmaHistogramBoolean(BuildOverallMetricName("Success"), success);
+ RecordSuccess(success_status);
+ if (ShouldRecordLatency(success_status)) {
+ RecordLatency();
+ }
+ if (error.has_value()) {
+ DCHECK_NE(success_status, SuccessStatus::kSuccess);
+ if (absl::holds_alternative<AndroidBackendError>(error.value())) {
+ RecordErrorCode(std::move(absl::get<1>(error.value())));
+ }
+ }
+}
+
+void PasswordStoreBackendMetricsRecorder::RecordMetricsForUnenrolledClients(
+ const absl::optional<AndroidBackendError>& error) const {
+ base::UmaHistogramBoolean(BuildMetricName("UnenrolledFromUPM.Success"),
+ !error.has_value());
if (!error.has_value())
return;
- DCHECK(!success);
- ErrorFromPasswordStoreOrAndroidBackend error_variant =
- std::move(error.value());
- // In case of AndroidBackend error, we report additional metrics.
- if (absl::holds_alternative<AndroidBackendError>(error_variant)) {
- AndroidBackendError backend_error = std::move(absl::get<1>(error_variant));
- base::UmaHistogramEnumeration(
- "PasswordManager.PasswordStoreAndroidBackend.ErrorCode",
- backend_error.type);
- base::UmaHistogramEnumeration(BuildMetricName("ErrorCode"),
- backend_error.type);
- if (backend_error.type == AndroidBackendErrorType::kExternalError) {
- DCHECK(backend_error.api_error_code.has_value());
- base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
- "PasswordManager.PasswordStoreAndroidBackend.APIError",
- base::HistogramBase::kUmaTargetedHistogramFlag);
- histogram->Add(backend_error.api_error_code.value());
- histogram = base::SparseHistogram::FactoryGet(
- BuildMetricName("APIError"),
- base::HistogramBase::kUmaTargetedHistogramFlag);
- histogram->Add(backend_error.api_error_code.value());
- }
+ base::UmaHistogramEnumeration(BuildMetricName("UnenrolledFromUPM.ErrorCode"),
+ error->type);
+ if (error->type == AndroidBackendErrorType::kExternalError) {
+ DCHECK(error->api_error_code.has_value());
+ base::UmaHistogramSparse(BuildMetricName("UnenrolledFromUPM.APIError"),
+ error->api_error_code.value());
}
}
+base::TimeDelta
+PasswordStoreBackendMetricsRecorder::GetElapsedTimeSinceCreation() const {
+ return base::Time::Now() - start_;
+}
+
+void PasswordStoreBackendMetricsRecorder::RecordSuccess(
+ SuccessStatus success_status) const {
+ base::UmaHistogramBoolean(BuildMetricName("Success"),
+ success_status == SuccessStatus::kSuccess);
+ base::UmaHistogramBoolean(BuildOverallMetricName("Success"),
+ success_status == SuccessStatus::kSuccess);
+}
+
+void PasswordStoreBackendMetricsRecorder::RecordErrorCode(
+ const AndroidBackendError& backend_error) const {
+ base::UmaHistogramEnumeration(
+ base::StrCat({kMetricPrefix, "AndroidBackend.ErrorCode"}),
+ backend_error.type);
+ base::UmaHistogramEnumeration(BuildMetricName("ErrorCode"),
+ backend_error.type);
+ if (backend_error.type == AndroidBackendErrorType::kExternalError) {
+ DCHECK(backend_error.api_error_code.has_value());
+ RecordApiErrorCode(backend_error.api_error_code.value());
+ LOG(ERROR) << "Password Manager API call for " << metric_infix_
+ << " failed with error code: "
+ << backend_error.api_error_code.value();
+ }
+}
+
+void PasswordStoreBackendMetricsRecorder::RecordLatency() const {
+ base::TimeDelta duration = GetElapsedTimeSinceCreation();
+ base::UmaHistogramMediumTimes(BuildMetricName("Latency"), duration);
+ base::UmaHistogramMediumTimes(BuildOverallMetricName("Latency"), duration);
+}
+
+void PasswordStoreBackendMetricsRecorder::RecordApiErrorCode(
+ int api_error_code) const {
+ base::UmaHistogramSparse(
+ base::StrCat({kMetricPrefix, "AndroidBackend.APIError"}), api_error_code);
+ base::UmaHistogramSparse(BuildMetricName("APIError"), api_error_code);
+}
+
+std::string PasswordStoreBackendMetricsRecorder::BuildMetricName(
+ base::StringPiece suffix) const {
+ return base::StrCat(
+ {kMetricPrefix, *backend_infix_, ".", *metric_infix_, ".", suffix});
+}
+
+std::string PasswordStoreBackendMetricsRecorder::BuildOverallMetricName(
+ base::StringPiece suffix) const {
+ return base::StrCat({kMetricPrefix, "Backend.", *metric_infix_, ".", suffix});
+}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.h b/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.h
index 5b5458a0758..b094b69350f 100644
--- a/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.h
+++ b/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder.h
@@ -5,12 +5,14 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_STORE_BACKEND_METRICS_RECORDER_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_STORE_BACKEND_METRICS_RECORDER_H_
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/strings/strcat.h"
+#include <string>
+
+#include "base/strings/string_piece_forward.h"
+#include "base/time/time.h"
#include "components/password_manager/core/browser/android_backend_error.h"
#include "components/password_manager/core/browser/password_store_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
namespace password_manager {
@@ -26,6 +28,8 @@ using BackendInfix = base::StrongAlias<struct BackendNameTag, std::string>;
// assumption.
class PasswordStoreBackendMetricsRecorder {
public:
+ enum class SuccessStatus { kSuccess, kError, kCancelled };
+
PasswordStoreBackendMetricsRecorder();
explicit PasswordStoreBackendMetricsRecorder(BackendInfix backend_name,
MetricInfix metric_name);
@@ -34,22 +38,46 @@ class PasswordStoreBackendMetricsRecorder {
PasswordStoreBackendMetricsRecorder&&);
~PasswordStoreBackendMetricsRecorder();
+ // Records metrics from `RecordSuccess`.
+ // Records metrics from `RecordLatency`.
+ // Records metrics from `RecordErrorCode` if `error` is specified.
+ void RecordMetrics(
+ SuccessStatus success_status,
+ absl::optional<ErrorFromPasswordStoreOrAndroidBackend> error) const;
+
+ // Records success, error and API error metrics from clients unenrolled from
+ // the UPM experiment after experiencing errors in the android backend.
+ void RecordMetricsForUnenrolledClients(
+ const absl::optional<AndroidBackendError>& error) const;
+
+ // Returns the delta between creating this recorder and calling this method.
+ base::TimeDelta GetElapsedTimeSinceCreation() const;
+
+ private:
// Records the following metrics:
- // - "PasswordManager.PasswordStore<backend_infix_>.<metric_infix_>.Latency"
// - "PasswordManager.PasswordStore<backend_infix_>.<metric_infix_>.Success"
- // - "PasswordManager.PasswordStoreBackend.<metric_infix_>.Latency"
// - "PasswordManager.PasswordStoreBackend.<metric_infix_>.Success"
- // In case of Android backend, when |error| is specified, the following
- // metrcis are recorded in addition:
- // - "PasswordManager.PasswordStoreAndroidBackend.APIError"
+ void RecordSuccess(SuccessStatus success_status) const;
+
+ // Records metrics from `RecordApiErrorCode` if `backend_error`
+ // requires it. Additionally records the following metrics:
// - "PasswordManager.PasswordStoreAndroidBackend.ErrorCode"
- // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.APIError"
// - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.ErrorCode"
- void RecordMetrics(
- bool success,
- absl::optional<ErrorFromPasswordStoreOrAndroidBackend> error) const;
+ void RecordErrorCode(const AndroidBackendError& backend_error) const;
+
+ // Records the following metrics:
+ // - "PasswordManager.PasswordStore<backend_infix_>.<metric_infix_>.Latency"
+ // - "PasswordManager.PasswordStoreBackend.<metric_infix_>.Latency"
+ void RecordLatency() const;
+
+ // Records the following metrics:
+ // - "PasswordManager.PasswordStoreAndroidBackend.APIError"
+ // - "PasswordManager.PasswordStoreAndroidBackend.<metric_infix_>.APIError"
+ void RecordApiErrorCode(int api_error_code) const;
+
+ std::string BuildMetricName(base::StringPiece suffix) const;
+ std::string BuildOverallMetricName(base::StringPiece suffix) const;
- private:
BackendInfix backend_infix_;
MetricInfix metric_infix_;
base::Time start_ = base::Time::Now();
diff --git a/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder_unittest.cc b/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder_unittest.cc
index fafe2b8a2dc..63f7be330ad 100644
--- a/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_store_backend_metrics_recorder_unittest.cc
@@ -14,7 +14,30 @@
namespace password_manager {
namespace {
+using testing::ElementsAre;
+
+using SuccessStatus = PasswordStoreBackendMetricsRecorder::SuccessStatus;
+
constexpr auto kLatencyDelta = base::Milliseconds(123u);
+
+constexpr char kSomeBackend[] = "SomeBackend";
+constexpr char kSomeMethod[] = "MethodName";
+constexpr char kDurationMetric[] =
+ "PasswordManager.PasswordStoreSomeBackend.MethodName.Latency";
+constexpr char kSuccessMetric[] =
+ "PasswordManager.PasswordStoreSomeBackend.MethodName.Success";
+constexpr char kErrorCodeMetric[] =
+ "PasswordManager.PasswordStoreSomeBackend.MethodName.ErrorCode";
+constexpr char kApiErrorMetric[] =
+ "PasswordManager.PasswordStoreSomeBackend.MethodName.APIError";
+constexpr char kDurationOverallMetric[] =
+ "PasswordManager.PasswordStoreBackend.MethodName.Latency";
+constexpr char kSuccessOverallMetric[] =
+ "PasswordManager.PasswordStoreBackend.MethodName.Success";
+constexpr char kErrorCodeOverallMetric[] =
+ "PasswordManager.PasswordStoreAndroidBackend.ErrorCode";
+constexpr char kApiErrorOverallMetric[] =
+ "PasswordManager.PasswordStoreAndroidBackend.APIError";
} // anonymous namespace
class PasswordStoreBackendMetricsRecorderTest : public testing::Test {
@@ -33,37 +56,91 @@ class PasswordStoreBackendMetricsRecorderTest : public testing::Test {
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
};
-TEST_F(PasswordStoreBackendMetricsRecorderTest, RecordMetrics) {
- const char kDurationMetric[] =
- "PasswordManager.PasswordStoreSomeBackend.MethodName.Latency";
- const char kSuccessMetric[] =
- "PasswordManager.PasswordStoreSomeBackend.MethodName.Success";
- const char kDurationOverallMetric[] =
- "PasswordManager.PasswordStoreBackend.MethodName.Latency";
- const char kSuccessOverallMetric[] =
- "PasswordManager.PasswordStoreBackend.MethodName.Success";
+TEST_F(PasswordStoreBackendMetricsRecorderTest, RecordMetrics_Success) {
+ using base::Bucket;
+ base::HistogramTester histogram_tester;
+
+ PasswordStoreBackendMetricsRecorder metrics_recorder =
+ PasswordStoreBackendMetricsRecorder(BackendInfix(kSomeBackend),
+ MetricInfix(kSomeMethod));
+
+ AdvanceClock(kLatencyDelta);
+
+ metrics_recorder.RecordMetrics(SuccessStatus::kSuccess,
+ /*error=*/absl::nullopt);
+
+ // Checking records in the backend-specific histogram
+ histogram_tester.ExpectTotalCount(kDurationMetric, 1);
+ histogram_tester.ExpectTimeBucketCount(kDurationMetric, kLatencyDelta, 1);
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSuccessMetric),
+ ElementsAre(Bucket(true, 1)));
+
+ // Checking records in the overall histogram
+ histogram_tester.ExpectTotalCount(kDurationOverallMetric, 1);
+ histogram_tester.ExpectTimeBucketCount(kDurationOverallMetric, kLatencyDelta,
+ 1);
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSuccessOverallMetric),
+ ElementsAre(Bucket(true, 1)));
+}
+
+TEST_F(PasswordStoreBackendMetricsRecorderTest, RecordMetrics_ExternalError) {
+ using base::Bucket;
base::HistogramTester histogram_tester;
PasswordStoreBackendMetricsRecorder metrics_recorder =
- PasswordStoreBackendMetricsRecorder(BackendInfix("SomeBackend"),
- MetricInfix("MethodName"));
+ PasswordStoreBackendMetricsRecorder(BackendInfix(kSomeBackend),
+ MetricInfix(kSomeMethod));
AdvanceClock(kLatencyDelta);
- metrics_recorder.RecordMetrics(/*success=*/true, /*error=*/absl::nullopt);
+ AndroidBackendError error(AndroidBackendErrorType::kExternalError);
+ error.api_error_code = 11010;
+ metrics_recorder.RecordMetrics(SuccessStatus::kError, std::move(error));
// Checking records in the backend-specific histogram
histogram_tester.ExpectTotalCount(kDurationMetric, 1);
histogram_tester.ExpectTimeBucketCount(kDurationMetric, kLatencyDelta, 1);
- histogram_tester.ExpectTotalCount(kSuccessMetric, 1);
- histogram_tester.ExpectBucketCount(kSuccessMetric, true, 1);
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSuccessMetric),
+ ElementsAre(Bucket(false, 1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(kErrorCodeMetric),
+ ElementsAre(Bucket(7, 1))); // External
+ EXPECT_THAT(histogram_tester.GetAllSamples(kApiErrorMetric),
+ ElementsAre(Bucket(11010, 1))); // No access.
// Checking records in the overall histogram
histogram_tester.ExpectTotalCount(kDurationOverallMetric, 1);
histogram_tester.ExpectTimeBucketCount(kDurationOverallMetric, kLatencyDelta,
1);
- histogram_tester.ExpectTotalCount(kSuccessOverallMetric, 1);
- histogram_tester.ExpectBucketCount(kSuccessOverallMetric, true, 1);
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSuccessOverallMetric),
+ ElementsAre(Bucket(false, 1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(kErrorCodeOverallMetric),
+ ElementsAre(Bucket(7, 1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(kApiErrorOverallMetric),
+ ElementsAre(Bucket(11010, 1)));
+}
+
+TEST_F(PasswordStoreBackendMetricsRecorderTest, RecordMetrics_Cancelled) {
+ using base::Bucket;
+ base::HistogramTester histogram_tester;
+
+ PasswordStoreBackendMetricsRecorder metrics_recorder =
+ PasswordStoreBackendMetricsRecorder(BackendInfix(kSomeBackend),
+ MetricInfix(kSomeMethod));
+
+ AdvanceClock(kLatencyDelta);
+
+ metrics_recorder.RecordMetrics(SuccessStatus::kCancelled,
+ /*error=*/absl::nullopt);
+
+ // Checking records in the backend-specific histogram
+ histogram_tester.ExpectTotalCount(kDurationMetric, 0);
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSuccessMetric),
+ ElementsAre(Bucket(false, 1)));
+
+ // Checking records in the overall histogram
+ histogram_tester.ExpectTotalCount(kDurationOverallMetric, 0);
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSuccessOverallMetric),
+ ElementsAre(Bucket(false, 1)));
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.cc b/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
index 7be3110573f..1a488a48082 100644
--- a/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
+++ b/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
@@ -24,6 +24,14 @@ namespace {
// the Android backend is delayed.
constexpr int kMigrationToAndroidBackendDelay = 30;
+// Check the experiment stage allows migration and that user wasn't kicked out
+// from the experiment after receiving errors from the backend.
+bool ShouldAttemptMigration(PrefService* prefs) {
+ return features::RequiresMigrationForUnifiedPasswordManager() &&
+ !prefs->GetBoolean(
+ prefs::kUnenrolledFromGoogleMobileServicesDueToErrors);
+}
+
} // namespace
PasswordStoreBackendMigrationDecorator::PasswordStoreBackendMigrationDecorator(
@@ -142,7 +150,7 @@ void PasswordStoreBackendMigrationDecorator::InitBackend(
// Only start the migration when launching the UPM which needs chrome-local
// data in the remote store. For shadow traffic, this doesn't matter.
- if (features::RequiresMigrationForUnifiedPasswordManager()) {
+ if (ShouldAttemptMigration(prefs_)) {
migrator_ = std::make_unique<BuiltInBackendToAndroidBackendMigrator>(
built_in_backend_.get(), android_backend_.get(), prefs_,
sync_delegate_.get());
@@ -193,7 +201,7 @@ void PasswordStoreBackendMigrationDecorator::GetAllLoginsForAccountAsync(
}
void PasswordStoreBackendMigrationDecorator::FillMatchingLoginsAsync(
- LoginsReply callback,
+ LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) {
active_backend_->FillMatchingLoginsAsync(std::move(callback), include_psl,
@@ -202,19 +210,19 @@ void PasswordStoreBackendMigrationDecorator::FillMatchingLoginsAsync(
void PasswordStoreBackendMigrationDecorator::AddLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
active_backend_->AddLoginAsync(form, std::move(callback));
}
void PasswordStoreBackendMigrationDecorator::UpdateLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
active_backend_->UpdateLoginAsync(form, std::move(callback));
}
void PasswordStoreBackendMigrationDecorator::RemoveLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
active_backend_->RemoveLoginAsync(form, std::move(callback));
}
@@ -223,7 +231,7 @@ void PasswordStoreBackendMigrationDecorator::RemoveLoginsByURLAndTimeAsync(
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
active_backend_->RemoveLoginsByURLAndTimeAsync(
url_filter, std::move(delete_begin), std::move(delete_end),
std::move(sync_completion), std::move(callback));
@@ -232,7 +240,7 @@ void PasswordStoreBackendMigrationDecorator::RemoveLoginsByURLAndTimeAsync(
void PasswordStoreBackendMigrationDecorator::RemoveLoginsCreatedBetweenAsync(
base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
active_backend_->RemoveLoginsCreatedBetweenAsync(
std::move(delete_begin), std::move(delete_end), std::move(callback));
}
@@ -296,7 +304,7 @@ void PasswordStoreBackendMigrationDecorator::StartMigrationAfterInit() {
}
void PasswordStoreBackendMigrationDecorator::SyncStatusChanged() {
- if (!features::RequiresMigrationForUnifiedPasswordManager())
+ if (!ShouldAttemptMigration(prefs_))
return;
sync_settings_helper_.SyncStatusChangeApplied();
diff --git a/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.h b/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.h
index 90b42add116..0dd6724664c 100644
--- a/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.h
+++ b/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator.h
@@ -106,25 +106,25 @@ class PasswordStoreBackendMigrationDecorator : public PasswordStoreBackend {
void GetAllLoginsForAccountAsync(absl::optional<std::string> account,
LoginsOrErrorReply callback) override;
void FillMatchingLoginsAsync(
- LoginsReply callback,
+ LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) override;
void AddLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void UpdateLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginsByURLAndTimeAsync(
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginsCreatedBetweenAsync(
base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void DisableAutoSignInForOriginsAsync(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::OnceClosure completion) override;
diff --git a/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc b/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
index 1047837b66e..202f97e5e1c 100644
--- a/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
@@ -44,6 +44,8 @@ class PasswordStoreBackendMigrationDecoratorTest : public testing::Test {
prefs::kRequiresMigrationAfterSyncStatusChange, false);
prefs_.registry()->RegisterStringPref(::prefs::kGoogleServicesLastUsername,
"testaccount@gmail.com");
+ prefs_.registry()->RegisterBooleanPref(
+ prefs::kUnenrolledFromGoogleMobileServicesDueToErrors, false);
feature_list_.InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
@@ -437,4 +439,52 @@ TEST_F(PasswordStoreBackendMigrationDecoratorTest,
RunUntilIdle();
}
+TEST_F(PasswordStoreBackendMigrationDecoratorTest,
+ NonSyncableDataMigrationDoesNotStartForUsersUnenrolledFromUPM) {
+ prefs().SetBoolean(prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
+ true);
+ base::MockCallback<base::OnceCallback<void(bool)>> mock_completion_callback;
+ base::RepeatingClosure sync_status_changed_closure;
+
+ // Init backend.
+ EXPECT_CALL(mock_completion_callback, Run(/*success=*/true));
+ EXPECT_CALL(*built_in_backend(), InitBackend)
+ .WillOnce(WithArgs<1, 2>(
+ [&sync_status_changed_closure](auto sync_status_changed,
+ auto completion_callback) {
+ std::move(completion_callback).Run(/*success=*/true);
+ // Capture |sync_enabled_or_disabled_cb| passed to the
+ // build_in_backend.
+ sync_status_changed_closure = std::move(sync_status_changed);
+ }));
+ EXPECT_CALL(*android_backend(), InitBackend)
+ .WillOnce(WithArg<2>([](auto completion_callback) {
+ std::move(completion_callback).Run(/*success=*/true);
+ }));
+ backend_migration_decorator()->InitBackend(
+ /*remote_form_changes_received=*/base::DoNothing(),
+ /*sync_enabled_or_disabled_cb=*/base::DoNothing(),
+ /*completion=*/mock_completion_callback.Get());
+
+ InitSyncService(/*is_password_sync_enabled=*/false);
+
+ // Invoke sync callback to simulate a change in sync status. Set expectation
+ // for sync to be turned on.
+ EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
+ .WillRepeatedly(Return(true));
+ // Migration of non-syncable data to the android backend will not start and
+ // therefore will not trigger any logins retrieval.
+ EXPECT_CALL(*built_in_backend(), GetAllLoginsAsync).Times(0);
+ EXPECT_CALL(*android_backend(), GetAllLoginsAsync).Times(0);
+
+ sync_status_changed_closure.Run();
+ RunUntilIdle();
+
+ // Verify that migration attempt did not happen by checking that the time of
+ // the last migration attempt did not change.
+ EXPECT_EQ(
+ prefs().GetDouble(password_manager::prefs::kTimeOfLastMigrationAttempt),
+ 0.0);
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_store_built_in_backend.cc b/chromium/components/password_manager/core/browser/password_store_built_in_backend.cc
index b43b6d39d5f..2989e0b3b2e 100644
--- a/chromium/components/password_manager/core/browser/password_store_built_in_backend.cc
+++ b/chromium/components/password_manager/core/browser/password_store_built_in_backend.cc
@@ -10,10 +10,40 @@
#include "base/task/thread_pool.h"
#include "components/password_manager/core/browser/login_database.h"
#include "components/password_manager/core/browser/login_database_async_helper.h"
+#include "components/password_manager/core/browser/password_store_backend.h"
+#include "components/password_manager/core/browser/password_store_backend_metrics_recorder.h"
+#include "components/password_manager/core/browser/password_store_util.h"
#include "components/sync/model/proxy_model_type_controller_delegate.h"
namespace password_manager {
+namespace {
+
+using SuccessStatus = PasswordStoreBackendMetricsRecorder::SuccessStatus;
+
+// Template function to create a callback which accepts LoginsResultOrError or
+// PasswordChangesOrError as a result.
+template <typename Result>
+base::OnceCallback<Result(Result)> ReportMetricsForResultCallback(
+ MetricInfix infix) {
+ PasswordStoreBackendMetricsRecorder metrics_reporter(
+ BackendInfix("BuiltInBackend"), infix);
+ return base::BindOnce(
+ [](PasswordStoreBackendMetricsRecorder reporter,
+ Result result) -> Result {
+ if (absl::holds_alternative<PasswordStoreBackendError>(result)) {
+ reporter.RecordMetrics(SuccessStatus::kError,
+ absl::get<PasswordStoreBackendError>(result));
+ } else {
+ reporter.RecordMetrics(SuccessStatus::kSuccess, absl::nullopt);
+ }
+ return result;
+ },
+ std::move(metrics_reporter));
+}
+
+} // namespace
+
PasswordStoreBuiltInBackend::PasswordStoreBuiltInBackend(
std::unique_ptr<LoginDatabase> login_db,
std::unique_ptr<UnsyncedCredentialsDeletionNotifier> notifier) {
@@ -59,31 +89,28 @@ void PasswordStoreBuiltInBackend::GetAllLoginsAsync(
LoginsOrErrorReply callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(helper_);
- PasswordStoreBackendMetricsRecorder metrics_recorder =
- PasswordStoreBackendMetricsRecorder(BackendInfix("BuiltInBackend"),
- MetricInfix("GetAllLoginsAsync"));
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
- base::BindOnce(&LoginDatabaseAsyncHelper::GetAllLogins,
- base::Unretained(helper_.get()),
- std::move(metrics_recorder)), // Safe until `Shutdown()`.
- std::move(callback));
+ base::BindOnce(
+ &LoginDatabaseAsyncHelper::GetAllLogins,
+ base::Unretained(helper_.get())), // Safe until `Shutdown()`.
+ ReportMetricsForResultCallback<LoginsResultOrError>(
+ MetricInfix("GetAllLoginsAsync"))
+ .Then(std::move(callback)));
}
void PasswordStoreBuiltInBackend::GetAutofillableLoginsAsync(
LoginsOrErrorReply callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(helper_);
- PasswordStoreBackendMetricsRecorder metrics_recorder =
- PasswordStoreBackendMetricsRecorder(
- BackendInfix("BuiltInBackend"),
- MetricInfix("GetAutofillableLoginsAsync"));
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
- base::BindOnce(&LoginDatabaseAsyncHelper::GetAutofillableLogins,
- base::Unretained(helper_.get()),
- std::move(metrics_recorder)), // Safe until `Shutdown()`.
- std::move(callback));
+ base::BindOnce(
+ &LoginDatabaseAsyncHelper::GetAutofillableLogins,
+ base::Unretained(helper_.get())), // Safe until `Shutdown()`.
+ ReportMetricsForResultCallback<LoginsResultOrError>(
+ MetricInfix("GetAutofillableLoginsAsync"))
+ .Then(std::move(callback)));
}
void PasswordStoreBuiltInBackend::GetAllLoginsForAccountAsync(
@@ -93,17 +120,13 @@ void PasswordStoreBuiltInBackend::GetAllLoginsForAccountAsync(
}
void PasswordStoreBuiltInBackend::FillMatchingLoginsAsync(
- LoginsReply callback,
+ LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(helper_);
- PasswordStoreBackendMetricsRecorder metrics_recorder =
- PasswordStoreBackendMetricsRecorder(
- BackendInfix("BuiltInBackend"),
- MetricInfix("FillMatchingLoginsAsync"));
if (forms.empty()) {
- std::move(callback).Run({});
+ std::move(callback).Run(LoginsResult());
return;
}
@@ -112,76 +135,72 @@ void PasswordStoreBuiltInBackend::FillMatchingLoginsAsync(
base::BindOnce(
&LoginDatabaseAsyncHelper::FillMatchingLogins,
base::Unretained(helper_.get()), // Safe until `Shutdown()`.
- forms, include_psl, std::move(metrics_recorder)),
- std::move(callback));
+ forms, include_psl),
+ ReportMetricsForResultCallback<LoginsResultOrError>(
+ MetricInfix("FillMatchingLoginsAsync"))
+ .Then(base::BindOnce(&GetLoginsOrEmptyListOnFailure))
+ .Then(std::move(callback)));
}
void PasswordStoreBuiltInBackend::AddLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(helper_);
- PasswordStoreBackendMetricsRecorder metrics_recorder =
- PasswordStoreBackendMetricsRecorder(BackendInfix("BuiltInBackend"),
- MetricInfix("AddLoginAsync"));
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&LoginDatabaseAsyncHelper::AddLogin,
- base::Unretained(helper_.get()), form,
- std::move(metrics_recorder)),
- std::move(callback));
+ base::Unretained(helper_.get()), form),
+ ReportMetricsForResultCallback<PasswordChangesOrError>(
+ MetricInfix("AddLoginAsync"))
+ .Then(std::move(callback)));
}
void PasswordStoreBuiltInBackend::UpdateLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(helper_);
- PasswordStoreBackendMetricsRecorder metrics_recorder =
- PasswordStoreBackendMetricsRecorder(BackendInfix("BuiltInBackend"),
- MetricInfix("UpdateLoginAsync"));
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&LoginDatabaseAsyncHelper::UpdateLogin,
- base::Unretained(helper_.get()), form,
- std::move(metrics_recorder)),
- std::move(callback));
+ base::Unretained(helper_.get()), form),
+ ReportMetricsForResultCallback<PasswordChangesOrError>(
+ MetricInfix("UpdateLoginAsync"))
+ .Then(std::move(callback)));
}
void PasswordStoreBuiltInBackend::RemoveLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(helper_);
- PasswordStoreBackendMetricsRecorder metrics_recorder =
- PasswordStoreBackendMetricsRecorder(BackendInfix("BuiltInBackend"),
- MetricInfix("RemoveLoginAsync"));
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
&LoginDatabaseAsyncHelper::RemoveLogin,
base::Unretained(helper_.get()), // Safe until `Shutdown()`.
- form, std::move(metrics_recorder)),
- std::move(callback));
+ form),
+ ReportMetricsForResultCallback<PasswordChangesOrError>(
+ MetricInfix("RemoveLoginAsync"))
+ .Then(std::move(callback)));
}
void PasswordStoreBuiltInBackend::RemoveLoginsCreatedBetweenAsync(
base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(helper_);
- PasswordStoreBackendMetricsRecorder metrics_recorder =
- PasswordStoreBackendMetricsRecorder(
- BackendInfix("BuiltInBackend"),
- MetricInfix("RemoveLoginsCreatedBetweenAsync"));
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
&LoginDatabaseAsyncHelper::RemoveLoginsCreatedBetween,
base::Unretained(helper_.get()), // Safe until `Shutdown()`.
- delete_begin, delete_end, std::move(metrics_recorder)),
- std::move(callback));
+ delete_begin, delete_end),
+ ReportMetricsForResultCallback<PasswordChangesOrError>(
+ MetricInfix("RemoveLoginsCreatedBetweenAsync"))
+ .Then(std::move(callback)));
}
void PasswordStoreBuiltInBackend::RemoveLoginsByURLAndTimeAsync(
@@ -189,21 +208,18 @@ void PasswordStoreBuiltInBackend::RemoveLoginsByURLAndTimeAsync(
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(helper_);
- PasswordStoreBackendMetricsRecorder metrics_recorder =
- PasswordStoreBackendMetricsRecorder(
- BackendInfix("BuiltInBackend"),
- MetricInfix("RemoveLoginsByURLAndTimeAsync"));
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
&LoginDatabaseAsyncHelper::RemoveLoginsByURLAndTime,
base::Unretained(helper_.get()), // Safe until `Shutdown()`.
- url_filter, delete_begin, delete_end, std::move(sync_completion),
- std::move(metrics_recorder)),
- std::move(callback));
+ url_filter, delete_begin, delete_end, std::move(sync_completion)),
+ ReportMetricsForResultCallback<PasswordChangesOrError>(
+ MetricInfix("RemoveLoginsByURLAndTimeAsync"))
+ .Then(std::move(callback)));
}
void PasswordStoreBuiltInBackend::DisableAutoSignInForOriginsAsync(
diff --git a/chromium/components/password_manager/core/browser/password_store_built_in_backend.h b/chromium/components/password_manager/core/browser/password_store_built_in_backend.h
index 743a17da2e1..6d0711c0013 100644
--- a/chromium/components/password_manager/core/browser/password_store_built_in_backend.h
+++ b/chromium/components/password_manager/core/browser/password_store_built_in_backend.h
@@ -57,25 +57,25 @@ class PasswordStoreBuiltInBackend : public PasswordStoreBackend,
void GetAllLoginsForAccountAsync(absl::optional<std::string> account,
LoginsOrErrorReply callback) override;
void FillMatchingLoginsAsync(
- LoginsReply callback,
+ LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) override;
void AddLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void UpdateLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginsCreatedBetweenAsync(
base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginsByURLAndTimeAsync(
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void DisableAutoSignInForOriginsAsync(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::OnceClosure completion) override;
diff --git a/chromium/components/password_manager/core/browser/password_store_built_in_backend_unittest.cc b/chromium/components/password_manager/core/browser/password_store_built_in_backend_unittest.cc
index aa279dfdf0f..13a68c8a9d0 100644
--- a/chromium/components/password_manager/core/browser/password_store_built_in_backend_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_store_built_in_backend_unittest.cc
@@ -23,6 +23,7 @@
#include "components/os_crypt/os_crypt_mocker.h"
#include "components/password_manager/core/browser/login_database.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
+#include "components/password_manager/core/browser/password_store_backend.h"
#include "components/password_manager/core/browser/password_store_change.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -33,6 +34,7 @@ using testing::ElementsAre;
using testing::ElementsAreArray;
using testing::IsEmpty;
using testing::Optional;
+using testing::VariantWith;
namespace password_manager {
@@ -79,14 +81,8 @@ class MockPasswordStoreConsumer : public PasswordStoreConsumer {
class MockPasswordStoreBackendTester {
public:
- MOCK_METHOD(void, HandleChanges, (absl::optional<PasswordStoreChangeList>));
- MOCK_METHOD(void,
- LoginsReceivedConstRef,
- (const std::vector<std::unique_ptr<PasswordForm>>&));
+ MOCK_METHOD(void, LoginsReceivedConstRef, (const LoginsResult&));
- void HandleLogins(std::vector<std::unique_ptr<PasswordForm>> results) {
- LoginsReceivedConstRef(results);
- }
void HandleLoginsOrError(LoginsResultOrError results) {
LoginsReceivedConstRef(std::move(absl::get<LoginsResult>(results)));
}
@@ -236,11 +232,11 @@ TEST_F(PasswordStoreBuiltInBackendTest, TestAddLoginAsync) {
const PasswordStoreChange add_change =
PasswordStoreChange(PasswordStoreChange::ADD, form);
- testing::StrictMock<MockPasswordStoreBackendTester> tester;
- EXPECT_CALL(tester, HandleChanges(Optional(ElementsAre(add_change))));
- backend->AddLoginAsync(
- form, base::BindOnce(&MockPasswordStoreBackendTester::HandleChanges,
- base::Unretained(&tester)));
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
+ EXPECT_CALL(
+ mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(ElementsAre(add_change)))));
+ backend->AddLoginAsync(form, mock_reply.Get());
RunUntilIdle();
}
@@ -255,11 +251,11 @@ TEST_F(PasswordStoreBuiltInBackendTest, TestUpdateLoginAsync) {
const PasswordStoreChange update_change =
PasswordStoreChange(PasswordStoreChange::UPDATE, form);
- testing::StrictMock<MockPasswordStoreBackendTester> tester;
- EXPECT_CALL(tester, HandleChanges(Optional(ElementsAre(update_change))));
- backend->UpdateLoginAsync(
- form, base::BindOnce(&MockPasswordStoreBackendTester::HandleChanges,
- base::Unretained(&tester)));
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
+ EXPECT_CALL(
+ mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(ElementsAre(update_change)))));
+ backend->UpdateLoginAsync(form, mock_reply.Get());
RunUntilIdle();
}
@@ -273,76 +269,11 @@ TEST_F(PasswordStoreBuiltInBackendTest, TestRemoveLoginAsync) {
PasswordStoreChange remove_change =
PasswordStoreChange(PasswordStoreChange::REMOVE, form);
- testing::StrictMock<MockPasswordStoreBackendTester> tester;
- EXPECT_CALL(tester, HandleChanges(Optional(ElementsAre(remove_change))));
- backend->RemoveLoginAsync(
- form, base::BindOnce(&MockPasswordStoreBackendTester::HandleChanges,
- base::Unretained(&tester)));
- RunUntilIdle();
-}
-
-// Verify that operations on a PasswordStore with a bad database cause no
-// explosions, but fail without side effect, return no data and trigger no
-// notifications.
-TEST_F(PasswordStoreBuiltInBackendTest, OperationsOnABadDatabaseSilentlyFail) {
- PasswordStoreBackend* bad_backend =
- InitializeWithDatabase(std::make_unique<BadLoginDatabase>());
- RunUntilIdle();
-
- testing::StrictMock<MockPasswordStoreBackendTester> tester;
-
- // Add a new autofillable login + a blocked login.
- std::unique_ptr<PasswordForm> form =
- FillPasswordFormWithData(CreateTestPasswordFormData());
- std::unique_ptr<PasswordForm> blocked_form(new PasswordForm(*form));
- blocked_form->signon_realm = "http://foo.example.com";
- blocked_form->url = GURL("http://foo.example.com/origin");
- blocked_form->action = GURL("http://foo.example.com/action");
- blocked_form->blocked_by_user = true;
-
- base::RepeatingCallback<void(absl::optional<PasswordStoreChangeList>)>
- handle_changes =
- base::BindRepeating(&MockPasswordStoreBackendTester::HandleChanges,
- base::Unretained(&tester));
- base::RepeatingCallback<void(LoginsResult)> handle_logins =
- base::BindRepeating(&MockPasswordStoreBackendTester::HandleLogins,
- base::Unretained(&tester));
- base::RepeatingCallback<void(LoginsResultOrError)> handle_logins_or_error =
- base::BindRepeating(&MockPasswordStoreBackendTester::HandleLoginsOrError,
- base::Unretained(&tester));
-
- EXPECT_CALL(tester, HandleChanges(Optional(IsEmpty())));
- bad_backend->AddLoginAsync(*form, handle_changes);
- RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(&tester);
-
- EXPECT_CALL(tester, HandleChanges(Optional(IsEmpty())));
- bad_backend->AddLoginAsync(*blocked_form, handle_changes);
- RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(&tester);
-
- // Get PSL matched logins; all logins; autofillable logins.
- EXPECT_CALL(tester, LoginsReceivedConstRef(IsEmpty()));
- bad_backend->FillMatchingLoginsAsync(handle_logins, true,
- {PasswordFormDigest(*form)});
- RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(&tester);
-
- EXPECT_CALL(tester, LoginsReceivedConstRef(IsEmpty()));
- bad_backend->GetAutofillableLoginsAsync(handle_logins_or_error);
- RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(&tester);
-
- base::MockCallback<LoginsOrErrorReply> mock_reply;
- std::vector<std::unique_ptr<PasswordForm>> expected_logins;
- EXPECT_CALL(mock_reply, Run(LoginsResultsOrErrorAre(&expected_logins)));
- bad_backend->GetAllLoginsAsync(mock_reply.Get());
- RunUntilIdle();
-
- testing::Mock::VerifyAndClearExpectations(&tester);
-
- EXPECT_CALL(tester, HandleChanges(Optional(IsEmpty())));
- bad_backend->RemoveLoginAsync(*form, handle_changes);
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
+ EXPECT_CALL(
+ mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(ElementsAre(remove_change)))));
+ backend->RemoveLoginAsync(form, mock_reply.Get());
RunUntilIdle();
}
@@ -351,7 +282,7 @@ TEST_F(PasswordStoreBuiltInBackendTest, GetAllLoginsAsync) {
// Populate store with test credentials.
std::vector<std::unique_ptr<PasswordForm>> all_credentials;
- base::MockCallback<PasswordStoreChangeListReply> reply;
+ base::MockCallback<PasswordChangesOrErrorReply> reply;
EXPECT_CALL(reply, Run).Times(6);
for (const auto& test_credential : kTestCredentials) {
all_credentials.push_back(FillPasswordFormWithData(test_credential));
@@ -388,11 +319,11 @@ TEST_F(PasswordStoreBuiltInBackendTest, GetAllLoginsAsyncMetrics) {
const PasswordStoreChange add_change =
PasswordStoreChange(PasswordStoreChange::ADD, form);
- testing::StrictMock<MockPasswordStoreBackendTester> tester;
- EXPECT_CALL(tester, HandleChanges(Optional(ElementsAre(add_change))));
- backend->AddLoginAsync(
- form, base::BindOnce(&MockPasswordStoreBackendTester::HandleChanges,
- base::Unretained(&tester)));
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
+ EXPECT_CALL(
+ mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(ElementsAre(add_change)))));
+ backend->AddLoginAsync(form, mock_reply.Get());
// Get the logins
backend->GetAllLoginsAsync(base::DoNothing());
@@ -448,11 +379,11 @@ TEST_F(PasswordStoreBuiltInBackendTest, GetAutofillableLoginsAsyncMetrics) {
const PasswordStoreChange add_change =
PasswordStoreChange(PasswordStoreChange::ADD, form);
- testing::StrictMock<MockPasswordStoreBackendTester> tester;
- EXPECT_CALL(tester, HandleChanges(Optional(ElementsAre(add_change))));
- backend->AddLoginAsync(
- form, base::BindOnce(&MockPasswordStoreBackendTester::HandleChanges,
- base::Unretained(&tester)));
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
+ EXPECT_CALL(
+ mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(ElementsAre(add_change)))));
+ backend->AddLoginAsync(form, mock_reply.Get());
// Get the logins
backend->GetAutofillableLoginsAsync(base::DoNothing());
@@ -532,11 +463,11 @@ TEST_F(PasswordStoreBuiltInBackendTest, UpdateLoginAsyncMetrics) {
const PasswordStoreChange update_change =
PasswordStoreChange(PasswordStoreChange::UPDATE, form);
- testing::StrictMock<MockPasswordStoreBackendTester> tester;
- EXPECT_CALL(tester, HandleChanges(Optional(ElementsAre(update_change))));
- backend->UpdateLoginAsync(
- form, base::BindOnce(&MockPasswordStoreBackendTester::HandleChanges,
- base::Unretained(&tester)));
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
+ EXPECT_CALL(
+ mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(ElementsAre(update_change)))));
+ backend->UpdateLoginAsync(form, mock_reply.Get());
AdvanceClock(kLatencyDelta);
RunUntilIdle();
@@ -599,8 +530,6 @@ TEST_F(PasswordStoreBuiltInBackendTest, RemoveLoginAsyncMetrics) {
TEST_F(PasswordStoreBuiltInBackendTest, RemoveLoginAsyncFailsMetrics) {
const char kDurationMetric[] =
"PasswordManager.PasswordStoreBuiltInBackend.RemoveLoginAsync.Latency";
- const char kSuccessMetric[] =
- "PasswordManager.PasswordStoreBuiltInBackend.RemoveLoginAsync.Success";
base::HistogramTester histogram_tester;
PasswordStoreBackend* bad_backend =
@@ -620,8 +549,6 @@ TEST_F(PasswordStoreBuiltInBackendTest, RemoveLoginAsyncFailsMetrics) {
histogram_tester.ExpectTotalCount(kDurationMetric, 1);
histogram_tester.ExpectTimeBucketCount(kDurationMetric, kLatencyDelta, 1);
- histogram_tester.ExpectTotalCount(kSuccessMetric, 1);
- histogram_tester.ExpectBucketCount(kSuccessMetric, false, 1);
}
TEST_F(PasswordStoreBuiltInBackendTest,
diff --git a/chromium/components/password_manager/core/browser/password_store_proxy_backend.cc b/chromium/components/password_manager/core/browser/password_store_proxy_backend.cc
index cac0e347854..23835032be8 100644
--- a/chromium/components/password_manager/core/browser/password_store_proxy_backend.cc
+++ b/chromium/components/password_manager/core/browser/password_store_proxy_backend.cc
@@ -17,10 +17,13 @@
#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
#include "components/password_manager/core/browser/field_info_table.h"
+#include "components/password_manager/core/browser/password_sync_util.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_service.h"
+#include "components/sync/driver/sync_service.h"
#include "components/sync/model/proxy_model_type_controller_delegate.h"
+#include "google_apis/gaia/google_service_auth_error.h"
namespace password_manager {
@@ -52,7 +55,11 @@ bool ShouldExecuteReadOperationsOnShadowBackend(PrefService* prefs,
case features::UpmExperimentVariation::kEnableForSyncingUsers:
case features::UpmExperimentVariation::kEnableOnlyBackendForSyncingUsers:
case features::UpmExperimentVariation::kEnableForAllUsers:
- return false;
+ // Emit shadow list calls for clients unenrolled form the UPM
+ // experiment to source error data.
+ return (prefs->GetBoolean(
+ password_manager::prefs::
+ kUnenrolledFromGoogleMobileServicesDueToErrors));
case features::UpmExperimentVariation::kShadowSyncingUsers:
return true;
}
@@ -89,30 +96,6 @@ bool ShouldExecuteDeletionsOnShadowBackend(PrefService* prefs,
return false;
}
-// This helper is used to determine main *and* shadow backends. Technically,
-// some "Enable" groups don't require shadow traffic but they use it for safe
-// deletions.
-bool UsesAndroidBackendAsMainBackend(bool is_syncing) {
- if (!is_syncing)
- return false;
-
- if (!base::FeatureList::IsEnabled(features::kUnifiedPasswordManagerAndroid))
- return false;
-
- features::UpmExperimentVariation variation =
- features::kUpmExperimentVariationParam.Get();
- switch (variation) {
- case features::UpmExperimentVariation::kEnableForSyncingUsers:
- case features::UpmExperimentVariation::kEnableOnlyBackendForSyncingUsers:
- case features::UpmExperimentVariation::kEnableForAllUsers:
- return true;
- case features::UpmExperimentVariation::kShadowSyncingUsers:
- return false;
- }
- NOTREACHED() << "Define explicitly whether Android is the main backend!";
- return false;
-}
-
bool IsBuiltInBackendSyncEnabled() {
DCHECK(
base::FeatureList::IsEnabled(features::kUnifiedPasswordManagerAndroid));
@@ -195,12 +178,19 @@ struct LoginsResultOrErrorImpl {
}
};
-struct PasswordStoreChangeListImpl {
- using ResultType = absl::optional<PasswordStoreChangeList>;
+// An `ApiMethodImpl` for `ShadowTrafficMetricsRecorder` implementing support
+// for the database modification methods returning `PasswordChangesOrError`.
+struct PasswordChangesOrErrorImpl {
+ using ResultType = PasswordChangesOrError;
using ElementsType = PasswordStoreChangeList;
- static PasswordStoreChangeList* GetElements(
- absl::optional<PasswordStoreChangeList>& changelist) {
+ static const PasswordStoreChangeList* GetElements(
+ const PasswordChangesOrError& changelist_or_error) {
+ if (absl::holds_alternative<PasswordStoreBackendError>(changelist_or_error))
+ return nullptr;
+ const PasswordChanges& changelist =
+ absl::get<PasswordChanges>(changelist_or_error);
+
return changelist.has_value() ? &changelist.value() : nullptr;
}
@@ -457,83 +447,93 @@ void PasswordStoreProxyBackend::GetAllLoginsForAccountAsync(
}
void PasswordStoreProxyBackend::FillMatchingLoginsAsync(
- LoginsReply callback,
+ LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) {
- auto handler =
- base::MakeRefCounted<ShadowTrafficMetricsRecorder<LoginsResultImpl>>(
- MethodName("FillMatchingLoginsAsync"));
+ auto handler = base::MakeRefCounted<
+ ShadowTrafficMetricsRecorder<LoginsResultOrErrorImpl>>(
+ MethodName("FillMatchingLoginsAsync"));
main_backend()->FillMatchingLoginsAsync(
- base::BindOnce(
- &ShadowTrafficMetricsRecorder<LoginsResultImpl>::RecordMainResult,
- handler)
+ base::BindOnce(&ShadowTrafficMetricsRecorder<
+ LoginsResultOrErrorImpl>::RecordMainResult,
+ handler)
.Then(std::move(callback)),
include_psl, forms);
if (ShouldExecuteReadOperationsOnShadowBackend(
prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
shadow_backend()->FillMatchingLoginsAsync(
- base::BindOnce(
- &ShadowTrafficMetricsRecorder<LoginsResultImpl>::RecordShadowResult,
- handler),
+ base::BindOnce(&ShadowTrafficMetricsRecorder<
+ LoginsResultOrErrorImpl>::RecordShadowResult,
+ handler),
include_psl, forms);
}
}
void PasswordStoreProxyBackend::AddLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
auto handler = base::MakeRefCounted<
- ShadowTrafficMetricsRecorder<PasswordStoreChangeListImpl>>(
+ ShadowTrafficMetricsRecorder<PasswordChangesOrErrorImpl>>(
MethodName("AddLoginAsync"));
+ auto maybe_retry_callback =
+ base::BindOnce(&PasswordStoreProxyBackend::MaybeRetryToAddLoginOnFail,
+ weak_ptr_factory_.GetWeakPtr(), form, std::move(callback),
+ UsesAndroidBackendAsMainBackend());
+
main_backend()->AddLoginAsync(
form, base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordMainResult,
+ PasswordChangesOrErrorImpl>::RecordMainResult,
handler)
- .Then(std::move(callback)));
+ .Then(std::move(maybe_retry_callback)));
if (ShouldExecuteModifyOperationsOnShadowBackend(
prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
shadow_backend()->AddLoginAsync(
form,
base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordShadowResult,
+ PasswordChangesOrErrorImpl>::RecordShadowResult,
handler));
}
}
void PasswordStoreProxyBackend::UpdateLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
auto handler = base::MakeRefCounted<
- ShadowTrafficMetricsRecorder<PasswordStoreChangeListImpl>>(
+ ShadowTrafficMetricsRecorder<PasswordChangesOrErrorImpl>>(
MethodName("UpdateLoginAsync"));
+ auto maybe_retry_callback =
+ base::BindOnce(&PasswordStoreProxyBackend::MaybeRetryToUpdateLoginOnFail,
+ weak_ptr_factory_.GetWeakPtr(), form, std::move(callback),
+ UsesAndroidBackendAsMainBackend());
+
main_backend()->UpdateLoginAsync(
form, base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordMainResult,
+ PasswordChangesOrErrorImpl>::RecordMainResult,
handler)
- .Then(std::move(callback)));
+ .Then(std::move(maybe_retry_callback)));
if (ShouldExecuteModifyOperationsOnShadowBackend(
prefs_, sync_delegate_->IsSyncingPasswordsEnabled())) {
shadow_backend()->UpdateLoginAsync(
form,
base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordShadowResult,
+ PasswordChangesOrErrorImpl>::RecordShadowResult,
handler));
}
}
void PasswordStoreProxyBackend::RemoveLoginAsync(
const PasswordForm& form,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
auto handler = base::MakeRefCounted<
- ShadowTrafficMetricsRecorder<PasswordStoreChangeListImpl>>(
+ ShadowTrafficMetricsRecorder<PasswordChangesOrErrorImpl>>(
MethodName("RemoveLoginAsync"));
main_backend()->RemoveLoginAsync(
form, base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordMainResult,
+ PasswordChangesOrErrorImpl>::RecordMainResult,
handler)
.Then(std::move(callback)));
if (ShouldExecuteDeletionsOnShadowBackend(
@@ -541,7 +541,7 @@ void PasswordStoreProxyBackend::RemoveLoginAsync(
shadow_backend()->RemoveLoginAsync(
form,
base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordShadowResult,
+ PasswordChangesOrErrorImpl>::RecordShadowResult,
handler));
}
}
@@ -551,15 +551,15 @@ void PasswordStoreProxyBackend::RemoveLoginsByURLAndTimeAsync(
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
auto handler = base::MakeRefCounted<
- ShadowTrafficMetricsRecorder<PasswordStoreChangeListImpl>>(
+ ShadowTrafficMetricsRecorder<PasswordChangesOrErrorImpl>>(
MethodName("RemoveLoginsByURLAndTimeAsync"));
main_backend()->RemoveLoginsByURLAndTimeAsync(
url_filter, delete_begin, delete_end, std::move(sync_completion),
base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordMainResult,
+ PasswordChangesOrErrorImpl>::RecordMainResult,
handler)
.Then(std::move(callback)));
if (ShouldExecuteDeletionsOnShadowBackend(
@@ -568,7 +568,7 @@ void PasswordStoreProxyBackend::RemoveLoginsByURLAndTimeAsync(
url_filter, std::move(delete_begin), std::move(delete_end),
base::OnceCallback<void(bool)>(),
base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordShadowResult,
+ PasswordChangesOrErrorImpl>::RecordShadowResult,
handler));
}
}
@@ -576,15 +576,15 @@ void PasswordStoreProxyBackend::RemoveLoginsByURLAndTimeAsync(
void PasswordStoreProxyBackend::RemoveLoginsCreatedBetweenAsync(
base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback) {
+ PasswordChangesOrErrorReply callback) {
auto handler = base::MakeRefCounted<
- ShadowTrafficMetricsRecorder<PasswordStoreChangeListImpl>>(
+ ShadowTrafficMetricsRecorder<PasswordChangesOrErrorImpl>>(
MethodName("RemoveLoginsCreatedBetweenAsync"));
main_backend()->RemoveLoginsCreatedBetweenAsync(
delete_begin, delete_end,
base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordMainResult,
+ PasswordChangesOrErrorImpl>::RecordMainResult,
handler)
.Then(std::move(callback)));
if (ShouldExecuteDeletionsOnShadowBackend(
@@ -592,7 +592,7 @@ void PasswordStoreProxyBackend::RemoveLoginsCreatedBetweenAsync(
shadow_backend()->RemoveLoginsCreatedBetweenAsync(
std::move(delete_begin), std::move(delete_end),
base::BindOnce(&ShadowTrafficMetricsRecorder<
- PasswordStoreChangeListImpl>::RecordShadowResult,
+ PasswordChangesOrErrorImpl>::RecordShadowResult,
handler));
}
}
@@ -644,21 +644,48 @@ void PasswordStoreProxyBackend::ClearAllLocalPasswords() {
void PasswordStoreProxyBackend::OnSyncServiceInitialized(
syncer::SyncService* sync_service) {
+ sync_service_ = sync_service;
android_backend_->OnSyncServiceInitialized(sync_service);
}
+void PasswordStoreProxyBackend::MaybeRetryToAddLoginOnFail(
+ const PasswordForm& form,
+ PasswordChangesOrErrorReply callback,
+ bool was_using_android_backend,
+ PasswordChangesOrError result) {
+ if (was_using_android_backend &&
+ absl::holds_alternative<PasswordStoreBackendError>(result) &&
+ absl::get<PasswordStoreBackendError>(result) ==
+ PasswordStoreBackendError::kUnrecoverable) {
+ built_in_backend_->AddLoginAsync(form, std::move(callback));
+ } else {
+ std::move(callback).Run(result);
+ }
+}
+
+void PasswordStoreProxyBackend::MaybeRetryToUpdateLoginOnFail(
+ const PasswordForm& form,
+ PasswordChangesOrErrorReply callback,
+ bool was_using_android_backend,
+ const PasswordChangesOrError& result) {
+ if (was_using_android_backend &&
+ absl::holds_alternative<PasswordStoreBackendError>(result) &&
+ absl::get<PasswordStoreBackendError>(result) ==
+ PasswordStoreBackendError::kUnrecoverable) {
+ built_in_backend_->UpdateLoginAsync(form, std::move(callback));
+ } else {
+ std::move(callback).Run(result);
+ }
+}
+
PasswordStoreBackend* PasswordStoreProxyBackend::main_backend() {
- return UsesAndroidBackendAsMainBackend(
- sync_delegate_->IsSyncingPasswordsEnabled())
- ? android_backend_
- : built_in_backend_;
+ return UsesAndroidBackendAsMainBackend() ? android_backend_
+ : built_in_backend_;
}
PasswordStoreBackend* PasswordStoreProxyBackend::shadow_backend() {
- return UsesAndroidBackendAsMainBackend(
- sync_delegate_->IsSyncingPasswordsEnabled())
- ? built_in_backend_
- : android_backend_;
+ return UsesAndroidBackendAsMainBackend() ? built_in_backend_
+ : android_backend_;
}
void PasswordStoreProxyBackend::OnRemoteFormChangesReceived(
@@ -668,11 +695,33 @@ void PasswordStoreProxyBackend::OnRemoteFormChangesReceived(
// `remote_form_changes_received` is used to inform observers about changes in
// the backend. This check guarantees observers are informed only about
// changes in the main backend.
- if (originates_from_android.value() ==
- UsesAndroidBackendAsMainBackend(
- sync_delegate_->IsSyncingPasswordsEnabled())) {
+ if (originates_from_android.value() == UsesAndroidBackendAsMainBackend()) {
remote_form_changes_received.Run(std::move(changes));
}
}
+bool PasswordStoreProxyBackend::UsesAndroidBackendAsMainBackend() {
+ if (prefs_->GetBoolean(prefs::kUnenrolledFromGoogleMobileServicesDueToErrors))
+ return false;
+
+ if (!sync_delegate_->IsSyncingPasswordsEnabled())
+ return false;
+
+ if (!base::FeatureList::IsEnabled(features::kUnifiedPasswordManagerAndroid))
+ return false;
+
+ features::UpmExperimentVariation variation =
+ features::kUpmExperimentVariationParam.Get();
+ switch (variation) {
+ case features::UpmExperimentVariation::kEnableForSyncingUsers:
+ case features::UpmExperimentVariation::kEnableOnlyBackendForSyncingUsers:
+ case features::UpmExperimentVariation::kEnableForAllUsers:
+ return true;
+ case features::UpmExperimentVariation::kShadowSyncingUsers:
+ return false;
+ }
+ NOTREACHED() << "Define explicitly whether Android is the main backend!";
+ return false;
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_store_proxy_backend.h b/chromium/components/password_manager/core/browser/password_store_proxy_backend.h
index 4eed518e30c..2ce86067737 100644
--- a/chromium/components/password_manager/core/browser/password_store_proxy_backend.h
+++ b/chromium/components/password_manager/core/browser/password_store_proxy_backend.h
@@ -51,25 +51,25 @@ class PasswordStoreProxyBackend : public PasswordStoreBackend {
void GetAllLoginsForAccountAsync(absl::optional<std::string> account,
LoginsOrErrorReply callback) override;
void FillMatchingLoginsAsync(
- LoginsReply callback,
+ LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) override;
void AddLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void UpdateLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginAsync(const PasswordForm& form,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginsByURLAndTimeAsync(
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void RemoveLoginsCreatedBetweenAsync(
base::Time delete_begin,
base::Time delete_end,
- PasswordStoreChangeListReply callback) override;
+ PasswordChangesOrErrorReply callback) override;
void DisableAutoSignInForOriginsAsync(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::OnceClosure completion) override;
@@ -92,6 +92,26 @@ class PasswordStoreProxyBackend : public PasswordStoreBackend {
CallbackOriginatesFromAndroidBackend originatesFromAndroid,
base::RepeatingClosure sync_enabled_or_disabled_cb);
+ // Helper used to determine main *and* shadow backends. Some UPM experiment
+ // groups use shadow traffic to compare the two backends, other may need it
+ // to execute login deletions on both backends, to avoid recovery of deleted
+ // data.
+ bool UsesAndroidBackendAsMainBackend();
+
+ // Retries to add/update login into |built_in_backend| in case of an
+ // unrecoverable error inside |android_backend|. |form| and
+ // |original_callback| are the original parameters passed to
+ // Add/UpdateLoginAsync.
+ void MaybeRetryToAddLoginOnFail(const PasswordForm& form,
+ PasswordChangesOrErrorReply original_callback,
+ bool was_using_android_backend,
+ PasswordChangesOrError result);
+ void MaybeRetryToUpdateLoginOnFail(
+ const PasswordForm& form,
+ PasswordChangesOrErrorReply original_callback,
+ bool was_using_android_backend,
+ const PasswordChangesOrError& result);
+
PasswordStoreBackend* main_backend();
PasswordStoreBackend* shadow_backend();
@@ -99,6 +119,7 @@ class PasswordStoreProxyBackend : public PasswordStoreBackend {
const raw_ptr<PasswordStoreBackend> android_backend_;
raw_ptr<PrefService> const prefs_ = nullptr;
const raw_ptr<SyncDelegate> sync_delegate_;
+ raw_ptr<syncer::SyncService> sync_service_ = nullptr;
base::WeakPtrFactory<PasswordStoreProxyBackend> weak_ptr_factory_{this};
};
diff --git a/chromium/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc b/chromium/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
index d600a59ab64..cd808fe44a2 100644
--- a/chromium/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
@@ -35,10 +35,12 @@ using ::testing::AnyNumber;
using ::testing::AtMost;
using ::testing::Eq;
using ::testing::Invoke;
+using ::testing::Optional;
using ::testing::Pointer;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
+using ::testing::VariantWith;
using ::testing::WithArg;
using Type = PasswordStoreChange::Type;
using RemoveChangesReceived = PasswordStoreBackend::RemoteChangesReceived;
@@ -70,6 +72,19 @@ bool FilterNoUrl(const GURL& gurl) {
return true;
}
+MATCHER_P(PasswordChangesAre, expectations, "") {
+ if (absl::holds_alternative<PasswordStoreBackendError>(arg)) {
+ return false;
+ }
+
+ auto changes = absl::get<PasswordChanges>(arg);
+ if (!changes.has_value()) {
+ return false;
+ }
+
+ return changes.value() == expectations;
+}
+
} // namespace
class PasswordStoreProxyBackendTest : public testing::Test {
@@ -83,6 +98,12 @@ class PasswordStoreProxyBackendTest : public testing::Test {
prefs_.registry()->RegisterIntegerPref(
prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);
+ prefs_.registry()->RegisterBooleanPref(
+ prefs::kUnenrolledFromGoogleMobileServicesDueToErrors, false);
+
+ // Initialize sync service.
+ EXPECT_CALL(android_backend(), OnSyncServiceInitialized(&sync_service_));
+ proxy_backend().OnSyncServiceInitialized(&sync_service_);
}
void TearDown() override {
@@ -98,6 +119,7 @@ class PasswordStoreProxyBackendTest : public testing::Test {
MockPasswordStoreBackend& built_in_backend() { return built_in_backend_; }
MockPasswordStoreBackend& android_backend() { return android_backend_; }
TestingPrefServiceSimple* prefs() { return &prefs_; }
+ syncer::TestSyncService* sync_service() { return &sync_service_; }
private:
base::test::ScopedFeatureList feature_list_;
@@ -106,6 +128,7 @@ class PasswordStoreProxyBackendTest : public testing::Test {
testing::NiceMock<MockPasswordBackendSyncDelegate> sync_delegate_;
StrictMock<MockPasswordStoreBackend> built_in_backend_;
StrictMock<MockPasswordStoreBackend> android_backend_;
+ syncer::TestSyncService sync_service_;
};
TEST_F(PasswordStoreProxyBackendTest, CallCompletionCallbackAfterInit) {
@@ -263,13 +286,12 @@ TEST_F(PasswordStoreProxyBackendTest,
}
TEST_F(PasswordStoreProxyBackendTest, UseMainBackendToFillMatchingLoginsAsync) {
- base::MockCallback<LoginsReply> mock_reply;
+ base::MockCallback<LoginsOrErrorReply> mock_reply;
std::vector<std::unique_ptr<PasswordForm>> expected_logins =
CreateTestLogins();
- EXPECT_CALL(mock_reply,
- Run(UnorderedPasswordFormElementsAre(&expected_logins)));
+ EXPECT_CALL(mock_reply, Run(LoginsResultsOrErrorAre(&expected_logins)));
EXPECT_CALL(built_in_backend(), FillMatchingLoginsAsync)
- .WillOnce(WithArg<0>(Invoke([](LoginsReply reply) -> void {
+ .WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
std::move(reply).Run(CreateTestLogins());
})));
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
@@ -281,48 +303,51 @@ TEST_F(PasswordStoreProxyBackendTest, UseMainBackendToFillMatchingLoginsAsync) {
}
TEST_F(PasswordStoreProxyBackendTest, UseMainBackendToAddLoginAsync) {
- base::MockCallback<PasswordStoreChangeListReply> mock_reply;
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
PasswordForm form = CreateTestForm();
PasswordStoreChangeList change_list;
change_list.push_back(PasswordStoreChange(Type::ADD, form));
- EXPECT_CALL(mock_reply, Run(Eq(change_list)));
+ EXPECT_CALL(mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(change_list))));
// This test doesn't care about the shadow backend.
EXPECT_CALL(android_backend(), AddLoginAsync).Times(AnyNumber());
EXPECT_CALL(built_in_backend(), AddLoginAsync(Eq(form), _))
.WillOnce(WithArg<1>(
- Invoke([&change_list](PasswordStoreChangeListReply reply) -> void {
+ Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(change_list);
})));
proxy_backend().AddLoginAsync(form, mock_reply.Get());
}
TEST_F(PasswordStoreProxyBackendTest, UseMainBackendToUpdateLoginAsync) {
- base::MockCallback<PasswordStoreChangeListReply> mock_reply;
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
PasswordForm form = CreateTestForm();
PasswordStoreChangeList change_list;
change_list.push_back(PasswordStoreChange(Type::UPDATE, form));
- EXPECT_CALL(mock_reply, Run(Eq(change_list)));
+ EXPECT_CALL(mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(change_list))));
// This test doesn't care about the shadow backend.
EXPECT_CALL(android_backend(), UpdateLoginAsync).Times(AnyNumber());
EXPECT_CALL(built_in_backend(), UpdateLoginAsync(Eq(form), _))
.WillOnce(WithArg<1>(
- Invoke([&change_list](PasswordStoreChangeListReply reply) -> void {
+ Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(change_list);
})));
proxy_backend().UpdateLoginAsync(form, mock_reply.Get());
}
TEST_F(PasswordStoreProxyBackendTest, UseMainBackendToRemoveLoginAsync) {
- base::MockCallback<PasswordStoreChangeListReply> mock_reply;
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
PasswordForm form = CreateTestForm();
PasswordStoreChangeList change_list;
change_list.push_back(PasswordStoreChange(Type::REMOVE, form));
- EXPECT_CALL(mock_reply, Run(Eq(change_list)));
+ EXPECT_CALL(mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(change_list))));
// This test doesn't care about the shadow backend.
EXPECT_CALL(android_backend(), RemoveLoginAsync).Times(AnyNumber());
EXPECT_CALL(built_in_backend(), RemoveLoginAsync(Eq(form), _))
.WillOnce(WithArg<1>(
- Invoke([&change_list](PasswordStoreChangeListReply reply) -> void {
+ Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(change_list);
})));
proxy_backend().RemoveLoginAsync(form, mock_reply.Get());
@@ -332,17 +357,18 @@ TEST_F(PasswordStoreProxyBackendTest,
UseMainBackendToRemoveLoginsByURLAndTimeAsync) {
base::Time kStart = base::Time::FromTimeT(111111);
base::Time kEnd = base::Time::FromTimeT(22222222);
- base::MockCallback<PasswordStoreChangeListReply> mock_reply;
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
PasswordStoreChangeList change_list;
change_list.push_back(PasswordStoreChange(Type::REMOVE, CreateTestForm()));
- EXPECT_CALL(mock_reply, Run(Eq(change_list)));
+ EXPECT_CALL(mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(change_list))));
// This test doesn't care about the shadow backend.
EXPECT_CALL(android_backend(), RemoveLoginsByURLAndTimeAsync)
.Times(AnyNumber());
EXPECT_CALL(built_in_backend(),
RemoveLoginsByURLAndTimeAsync(_, Eq(kStart), Eq(kEnd), _, _))
.WillOnce(WithArg<4>(
- Invoke([&change_list](PasswordStoreChangeListReply reply) -> void {
+ Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(change_list);
})));
proxy_backend().RemoveLoginsByURLAndTimeAsync(
@@ -354,17 +380,18 @@ TEST_F(PasswordStoreProxyBackendTest,
UseMainBackendToRemoveLoginsCreatedBetweenAsync) {
base::Time kStart = base::Time::FromTimeT(111111);
base::Time kEnd = base::Time::FromTimeT(22222222);
- base::MockCallback<PasswordStoreChangeListReply> mock_reply;
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
PasswordStoreChangeList change_list;
change_list.push_back(PasswordStoreChange(Type::REMOVE, CreateTestForm()));
- EXPECT_CALL(mock_reply, Run(Eq(change_list)));
+ EXPECT_CALL(mock_reply,
+ Run(VariantWith<PasswordChanges>(Optional(change_list))));
// This test doesn't care about the shadow backend.
EXPECT_CALL(android_backend(), RemoveLoginsCreatedBetweenAsync)
.Times(AnyNumber());
EXPECT_CALL(built_in_backend(),
RemoveLoginsCreatedBetweenAsync(Eq(kStart), Eq(kEnd), _))
.WillOnce(WithArg<2>(
- Invoke([&change_list](PasswordStoreChangeListReply reply) -> void {
+ Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(change_list);
})));
proxy_backend().RemoveLoginsCreatedBetweenAsync(kStart, kEnd,
@@ -706,6 +733,136 @@ TEST_F(PasswordStoreProxyBackendTest,
proxy_backend().OnSyncServiceInitialized(&sync_service);
}
+TEST_F(PasswordStoreProxyBackendTest,
+ UsesAndroidBackendAsMainBackendPasswordSyncDisabledInSettings) {
+ base::test::ScopedFeatureList feature_list;
+ // Enable UPM for syncing users only.
+ feature_list.InitAndEnableFeatureWithParameters(
+ features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
+
+ // Imitate password sync being disabled in settings.
+ EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
+ .WillRepeatedly(Return(false));
+
+ // Verify that android backend is not used.
+ EXPECT_CALL(android_backend(), GetAllLoginsAsync).Times(0);
+ EXPECT_CALL(built_in_backend(), GetAllLoginsAsync);
+ proxy_backend().GetAllLoginsAsync(base::DoNothing());
+}
+
+TEST_F(
+ PasswordStoreProxyBackendTest,
+ UsesAndroidBackendAsMainBackendGetLoginsOperationsUserUnenrolledFromUPM) {
+ base::test::ScopedFeatureList feature_list;
+ // Enable UPM for syncing users only.
+ feature_list.InitAndEnableFeatureWithParameters(
+ features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
+ prefs()->SetBoolean(prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
+ true);
+
+ EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
+ .WillRepeatedly(Return(true));
+
+ // Logins should be retrieved from the built-in backend.
+ EXPECT_CALL(built_in_backend(), GetAllLoginsAsync);
+ // Shadow getAllLogins call should happen on the android backend.
+ EXPECT_CALL(android_backend(), GetAllLoginsAsync);
+
+ proxy_backend().GetAllLoginsAsync(base::DoNothing());
+}
+
+TEST_F(PasswordStoreProxyBackendTest,
+ UsesAndroidBackendAsMainBackendAddLoginUserUnenrolledFromUPM) {
+ base::test::ScopedFeatureList feature_list;
+ // Enable UPM for syncing users only.
+ feature_list.InitAndEnableFeatureWithParameters(
+ features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
+ prefs()->SetBoolean(prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
+ true);
+
+ EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
+ .WillRepeatedly(Return(true));
+
+ // Logins should be added to the built-in backend.
+ EXPECT_CALL(built_in_backend(), AddLoginAsync);
+ // There should be no shadow traffic for modifying operations.
+ EXPECT_CALL(android_backend(), AddLoginAsync).Times(0);
+
+ proxy_backend().AddLoginAsync(CreateTestForm(), base::DoNothing());
+}
+
+TEST_F(PasswordStoreProxyBackendTest,
+ RetriesAddLoginOnBuiltInBackendWhenOnAndroidFails) {
+ base::test::ScopedFeatureList feature_list;
+ // Enable UPM for syncing users only.
+ feature_list.InitAndEnableFeatureWithParameters(
+ features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
+ EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
+ .WillRepeatedly(Return(true));
+
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
+
+ EXPECT_CALL(android_backend(), AddLoginAsync)
+ .WillOnce(WithArg<1>(Invoke([](auto reply) -> void {
+ std::move(reply).Run(PasswordStoreBackendError::kUnrecoverable);
+ })));
+ const PasswordStoreChangeList changes = {
+ PasswordStoreChange(PasswordStoreChange::Type::ADD, CreateTestForm())};
+ EXPECT_CALL(built_in_backend(), AddLoginAsync)
+ .WillOnce(WithArg<1>(Invoke(
+ [&changes](auto reply) -> void { std::move(reply).Run(changes); })));
+ // Check that caller doesn't receive an error from android backend.
+ EXPECT_CALL(mock_reply, Run(PasswordChangesAre(changes)));
+ proxy_backend().AddLoginAsync(CreateTestForm(), mock_reply.Get());
+}
+
+TEST_F(PasswordStoreProxyBackendTest, DoesntRetryAddLoginOnRecoverableError) {
+ base::test::ScopedFeatureList feature_list;
+ // Enable UPM for syncing users only.
+ feature_list.InitAndEnableFeatureWithParameters(
+ features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
+ EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
+ .WillRepeatedly(Return(true));
+
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
+
+ EXPECT_CALL(android_backend(), AddLoginAsync)
+ .WillOnce(WithArg<1>(Invoke([](auto reply) -> void {
+ std::move(reply).Run(PasswordStoreBackendError::kRecoverable);
+ })));
+ EXPECT_CALL(built_in_backend(), AddLoginAsync).Times(0);
+ // Check that caller doesn't receive an error from android backend.
+ EXPECT_CALL(
+ mock_reply,
+ Run(PasswordChangesOrError(PasswordStoreBackendError::kRecoverable)));
+ proxy_backend().AddLoginAsync(CreateTestForm(), mock_reply.Get());
+}
+
+TEST_F(PasswordStoreProxyBackendTest,
+ RetriesUpdateLoginOnBuiltInBackendWhenOnAndroidFails) {
+ base::test::ScopedFeatureList feature_list;
+ // Enable UPM for syncing users only.
+ feature_list.InitAndEnableFeatureWithParameters(
+ features::kUnifiedPasswordManagerAndroid, {{"stage", "2"}});
+ EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
+ .WillRepeatedly(Return(true));
+
+ base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
+
+ EXPECT_CALL(android_backend(), UpdateLoginAsync)
+ .WillOnce(WithArg<1>(Invoke([](auto reply) -> void {
+ std::move(reply).Run(PasswordStoreBackendError::kUnrecoverable);
+ })));
+ const PasswordStoreChangeList changes = {
+ PasswordStoreChange(PasswordStoreChange::Type::UPDATE, CreateTestForm())};
+ EXPECT_CALL(built_in_backend(), UpdateLoginAsync)
+ .WillOnce(WithArg<1>(Invoke(
+ [&changes](auto reply) -> void { std::move(reply).Run(changes); })));
+ // Check that caller doesn't receive an error from android backend.
+ EXPECT_CALL(mock_reply, Run(PasswordChangesAre(changes)));
+ proxy_backend().UpdateLoginAsync(CreateTestForm(), mock_reply.Get());
+}
+
// Holds the main and shadow backend's logins and the expected number of common
// and different logins.
struct LoginsMetricsParam {
@@ -879,6 +1036,14 @@ class PasswordStoreProxyBackendTestForExperimentStages
base::NumberToString(static_cast<int>(GetParam().variation))}});
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(GetParam().is_sync_enabled));
+
+ if (GetParam().is_sync_enabled) {
+ sync_service()->GetUserSettings()->SetSelectedTypes(
+ /*sync_everything=*/false, {syncer::UserSelectableType::kPasswords});
+ } else {
+ sync_service()->GetUserSettings()->SetSelectedTypes(
+ /*sync_everything=*/false, {});
+ }
}
private:
diff --git a/chromium/components/password_manager/core/browser/password_store_unittest.cc b/chromium/components/password_manager/core/browser/password_store_unittest.cc
index 161a136528c..c12994790d0 100644
--- a/chromium/components/password_manager/core/browser/password_store_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_store_unittest.cc
@@ -1022,7 +1022,7 @@ TEST_F(PasswordStoreTest, CallOnLoginsChangedIfRemovalProvidesChanges) {
// reply with a `PasswordStoreChangeList`.
EXPECT_CALL(*mock_backend, RemoveLoginAsync(Eq(kTestForm), _))
.WillOnce(
- WithArg<1>(Invoke([&](PasswordStoreChangeListReply reply) -> void {
+ WithArg<1>(Invoke([&](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(
CreateChangeList(PasswordStoreChange::REMOVE, kTestForm));
})));
@@ -1048,7 +1048,7 @@ TEST_F(PasswordStoreTest, CallOnLoginsChangedIfAdditionProvidesChanges) {
// reply with a `PasswordStoreChangeList`.
EXPECT_CALL(*mock_backend, AddLoginAsync(Eq(kTestForm), _))
.WillOnce(
- WithArg<1>(Invoke([&](PasswordStoreChangeListReply reply) -> void {
+ WithArg<1>(Invoke([&](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(
CreateChangeList(PasswordStoreChange::ADD, kTestForm));
})));
@@ -1073,7 +1073,7 @@ TEST_F(PasswordStoreTest, CallOnLoginsChangedIfUpdateProvidesChanges) {
// reply with a `PasswordStoreChangeList`.
EXPECT_CALL(*mock_backend, UpdateLoginAsync(Eq(kTestForm), _))
.WillOnce(
- WithArg<1>(Invoke([&](PasswordStoreChangeListReply reply) -> void {
+ WithArg<1>(Invoke([&](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(
CreateChangeList(PasswordStoreChange::UPDATE, kTestForm));
})));
@@ -1108,7 +1108,7 @@ TEST_F(PasswordStoreTest, CallOnLoginsRetainedIfUpdateProvidesNoChanges) {
// reply with a nullopt.
EXPECT_CALL(*mock_backend, UpdateLoginAsync(Eq(kTestForm), _))
.WillOnce(
- WithArg<1>(Invoke([](PasswordStoreChangeListReply reply) -> void {
+ WithArg<1>(Invoke([](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(absl::nullopt);
})));
EXPECT_CALL(*mock_backend, GetAllLoginsAsync(_))
@@ -1145,7 +1145,7 @@ TEST_F(PasswordStoreTest, RecordsPotentialOnLoginsRetainedInvokations) {
// GMS backend was active — therefore record the potential call.
EXPECT_CALL(*mock_backend, UpdateLoginAsync(Eq(kTestForm), _))
.WillOnce(
- WithArg<1>(Invoke([](PasswordStoreChangeListReply reply) -> void {
+ WithArg<1>(Invoke([](PasswordChangesOrErrorReply reply) -> void {
std::move(reply).Run(PasswordStoreChangeList());
})));
EXPECT_CALL(mock_observer, OnLoginsRetained).Times(0);
diff --git a/chromium/components/password_manager/core/browser/password_store_util.cc b/chromium/components/password_manager/core/browser/password_store_util.cc
index 78a021b8c96..679d9cb1c7c 100644
--- a/chromium/components/password_manager/core/browser/password_store_util.cc
+++ b/chromium/components/password_manager/core/browser/password_store_util.cc
@@ -3,16 +3,22 @@
// found in the LICENSE file.
#include "components/password_manager/core/browser/password_store_util.h"
+#include "components/password_manager/core/browser/password_store_backend.h"
+#include "components/password_manager/core/browser/password_store_change.h"
namespace password_manager {
-absl::optional<PasswordStoreChangeList> JoinPasswordStoreChanges(
- std::vector<absl::optional<PasswordStoreChangeList>> changes) {
+PasswordChanges JoinPasswordStoreChanges(
+ const std::vector<PasswordChangesOrError>& changes_to_join) {
PasswordStoreChangeList joined_changes;
- for (auto changes_list : changes) {
- if (!changes_list.has_value())
+ for (const auto& changes_or_error : changes_to_join) {
+ if (absl::holds_alternative<PasswordStoreBackendError>(changes_or_error))
return absl::nullopt;
- std::move(changes_list->begin(), changes_list->end(),
+ const PasswordChanges& changes =
+ absl::get<PasswordChanges>(changes_or_error);
+ if (!changes.has_value())
+ return absl::nullopt;
+ std::copy(changes->begin(), changes->end(),
std::back_inserter(joined_changes));
}
return joined_changes;
@@ -25,13 +31,12 @@ LoginsResult GetLoginsOrEmptyListOnFailure(LoginsResultOrError result) {
return std::move(absl::get<LoginsResult>(result));
}
-PasswordStoreChangeListReply IgnoreChangeListAndRunCallback(
- base::OnceClosure callback) {
- return base::BindOnce(
- [](base::OnceClosure callback, absl::optional<PasswordStoreChangeList>) {
- std::move(callback).Run();
- },
- std::move(callback));
+PasswordChanges GetPasswordChangesOrEmptyListOnFailure(
+ PasswordChangesOrError result) {
+ if (absl::holds_alternative<PasswordStoreBackendError>(result)) {
+ return PasswordStoreChangeList();
+ }
+ return std::move(absl::get<PasswordChanges>(result));
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_store_util.h b/chromium/components/password_manager/core/browser/password_store_util.h
index 346f08989db..0e91687cb91 100644
--- a/chromium/components/password_manager/core/browser/password_store_util.h
+++ b/chromium/components/password_manager/core/browser/password_store_util.h
@@ -13,19 +13,20 @@
namespace password_manager {
-// Aggregates a vector of PasswordStoreChangeLists into a single
-// PasswordStoreChangeList. Does not check for duplicate values.
-absl::optional<PasswordStoreChangeList> JoinPasswordStoreChanges(
- std::vector<absl::optional<PasswordStoreChangeList>> changes);
+// Aggregates a vector of PasswordChangesOrError into a single
+// PasswordChangesOrError. Does not check for duplicate values.
+// Will return first occurred error if any.
+PasswordChanges JoinPasswordStoreChanges(
+ const std::vector<PasswordChangesOrError>& changes);
// Returns logins if |result| holds them, or an empty list if |result|
// holds an error.
LoginsResult GetLoginsOrEmptyListOnFailure(LoginsResultOrError result);
-// Helper function allowing to bind base::OnceClosure to
-// PasswordStoreChangeListReply.
-PasswordStoreChangeListReply IgnoreChangeListAndRunCallback(
- base::OnceClosure callback);
+// Returns password changes if |result| holds them, empty changelist if the
+// |result| holds an error and absl::nullopt if the result is absl::nullopt.
+PasswordChanges GetPasswordChangesOrEmptyListOnFailure(
+ PasswordChangesOrError result);
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_sync_util.cc b/chromium/components/password_manager/core/browser/password_sync_util.cc
index 90d7d920e71..9d4633a05c6 100644
--- a/chromium/components/password_manager/core/browser/password_sync_util.cc
+++ b/chromium/components/password_manager/core/browser/password_sync_util.cc
@@ -82,8 +82,11 @@ bool IsSyncAccountEmail(const std::string& username,
}
bool IsGaiaCredentialPage(const std::string& signon_realm) {
- return gaia::IsGaiaSignonRealm(GURL(signon_realm)) ||
- signon_realm == kGoogleChangePasswordSignonRealm;
+ const GURL signon_realm_url = GURL(signon_realm);
+ const GURL gaia_signon_realm_url =
+ GaiaUrls::GetInstance()->gaia_origin().GetURL();
+ return signon_realm_url == gaia_signon_realm_url ||
+ signon_realm_url == GURL(kGoogleChangePasswordSignonRealm);
}
bool ShouldSaveEnterprisePasswordHash(const PasswordForm& form,
diff --git a/chromium/components/password_manager/core/browser/protos/list_passwords_result.proto b/chromium/components/password_manager/core/browser/protos/list_passwords_result.proto
new file mode 100644
index 00000000000..3417a1e9f98
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/protos/list_passwords_result.proto
@@ -0,0 +1,24 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// If you change or add any fields in this file, update proto_visitors.h and
+// potentially proto_enum_conversions.{h, cc}.
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "org.chromium.components.password_manager.core.browser.proto";
+
+option optimize_for = LITE_RUNTIME;
+
+package password_manager;
+
+import "components/password_manager/core/browser/protos/password_with_local_data.proto";
+
+// Response to a request sent to Google Mobile Services to request a list of
+// passwords.
+message ListPasswordsResult {
+ // The list of password entries and corresponding additional info.
+ repeated PasswordWithLocalData password_data = 1;
+}
diff --git a/chromium/components/password_manager/core/browser/protos/password_with_local_data.proto b/chromium/components/password_manager/core/browser/protos/password_with_local_data.proto
new file mode 100644
index 00000000000..510e71a5082
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/protos/password_with_local_data.proto
@@ -0,0 +1,48 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// If you change or add any fields in this file, update proto_visitors.h and
+// potentially proto_enum_conversions.{h, cc}.
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "org.chromium.components.password_manager.core.browser.proto";
+
+option optimize_for = LITE_RUNTIME;
+
+package password_manager;
+
+import "components/sync/protocol/password_specifics.proto";
+
+// Wrapper for a set of credentials that consists of (possibly) synced password
+// data and local data that is exclusively read and modified by Chrome.
+message PasswordWithLocalData {
+ // A potentially synced set of credentials.
+ optional sync_pb.PasswordSpecificsData password_specifics_data = 1;
+
+ reserved 2;
+ reserved "local_chrome_data";
+
+ // Local data that is related to the `password_specifics_data`. Although it is
+ // stored close to the `password_specifics_data`, it always stays local to the
+ // device the password is stored on and is never synced.
+ message LocalData {
+ // Metadata that is opaque to the provider. The provider does not read,
+ // modify or interpret it, only Chrome does. This data will for example
+ // contain `FormData` or device-specific properties like
+ // `PasswordForm::skip_zero_click` that prevents a credential to be used for
+ // Credential Management requests on this device.
+ optional bytes opaque_metadata = 1;
+
+ // Email address of the last sync account this password was associated with.
+ // This field is maintained by Chrome as well as by the provider.
+ // This field is present only if the password is NOT currently associated
+ // with a syncing account AND it was associated with one in the past.
+ // E.g. test@gmail.com (lowercase and canonicalized).
+ optional string previously_associated_sync_account_email = 2;
+ }
+
+ optional LocalData local_data = 3;
+}
diff --git a/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.cc b/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.cc
index 7867ed149af..9be036a5278 100644
--- a/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.cc
+++ b/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.cc
@@ -243,6 +243,11 @@ std::vector<url::Origin>
SavedPasswordsCapabilitiesFetcher::GetOriginsOfStoredPasswords() const {
std::vector<url::Origin> origins;
for (const auto& form : saved_passwords_presenter_.GetSavedPasswords()) {
+ if (form.url.SchemeIs(url::kHttpScheme)) {
+ // Http schemes are not supported.
+ continue;
+ }
+
url::Origin origin = url::Origin::Create(form.url);
if (!origin.opaque()) {
origins.push_back(origin);
@@ -313,4 +318,16 @@ SavedPasswordsCapabilitiesFetcher::GetDebugInformationForInternals() const {
return result;
}
+base::Value::List SavedPasswordsCapabilitiesFetcher::GetCacheEntries() const {
+ base::Value::List cache_entries;
+ for (const auto& [origin, capabilities] : cache_) {
+ base::Value::Dict entry;
+ entry.Set("url", origin.Serialize());
+ entry.Set("has_script", capabilities.has_script);
+ cache_entries.Append(std::move(entry));
+ }
+
+ return cache_entries;
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h b/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h
index 9fa49c8e57f..f603d75a347 100644
--- a/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h
+++ b/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h
@@ -49,6 +49,7 @@ class SavedPasswordsCapabilitiesFetcher
ResponseCallback callback) override;
bool IsScriptAvailable(const url::Origin& origin) const override;
base::Value::Dict GetDebugInformationForInternals() const override;
+ base::Value::List GetCacheEntries() const override;
private:
using CacheState = PasswordScriptsFetcher::CacheState;
diff --git a/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher_unittest.cc b/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher_unittest.cc
index acdeab136eb..ffdcdd16640 100644
--- a/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher_unittest.cc
+++ b/chromium/components/password_manager/core/browser/saved_passwords_capabilities_fetcher_unittest.cc
@@ -36,6 +36,7 @@ constexpr char kOriginWithScript2[] = "https://mobile.example.com";
constexpr char kOriginWithScript3[] = "https://test.com";
constexpr char kOriginWithoutScript[] = "https://no-script.com";
constexpr char kExampleApp[] = "android://hash@com.example.app";
+constexpr char kHttpOriginWithScript[] = "http://scheme-example.com";
constexpr char16_t kUsername1[] = u"alice";
constexpr char16_t kUsername2[] = u"bob";
@@ -132,6 +133,10 @@ class SavedPasswordsCapabilitiesFetcherTest : public ::testing::Test {
store_->AddLogin(MakeSavedAndroidPassword(kExampleApp, kUsername2,
"Example App", kOriginWithScript1,
kPassword1));
+ // Set http url. Should not be made part of the cache.
+ store_->AddLogin(
+ MakeSavedPassword(kHttpOriginWithScript, kUsername2, kPassword3));
+
RunUntilIdle();
}
@@ -162,6 +167,7 @@ class SavedPasswordsCapabilitiesFetcherTest : public ::testing::Test {
}
void ExpectCacheRefresh() {
+ // Also checks the http credential is not part of the cache.
EXPECT_CALL(*mock_capabilities_service_,
QueryPasswordChangeScriptAvailability(
UnorderedElementsAre(
@@ -587,4 +593,30 @@ TEST_F(SavedPasswordsCapabilitiesFetcherTest, DebugInformationForInternals) {
CheckScriptAvailabilityDefaultResults();
}
+TEST_F(SavedPasswordsCapabilitiesFetcherTest, CheckCacheEntries) {
+ ExpectCacheRefresh();
+ fetcher_->PrewarmCache();
+
+ // Cache should now contain four entries.
+ base::Value::List cache_entries = fetcher_->GetCacheEntries();
+ EXPECT_EQ(cache_entries.size(), 4u);
+
+ std::vector<std::string> urls;
+ // Only `kOriginWithoutScript` is not expected to have a script.
+ for (auto it = cache_entries.begin(); it != cache_entries.end(); ++it) {
+ base::Value::Dict& entry = it->GetDict();
+ const std::string* url = entry.FindString("url");
+ absl::optional<bool> has_script = entry.FindBool("has_script");
+ EXPECT_TRUE(url);
+ EXPECT_TRUE(has_script.has_value());
+ EXPECT_EQ(*url != kOriginWithoutScript, has_script.value());
+ urls.push_back(*url);
+ }
+
+ // There should be entries for all requested sites.
+ EXPECT_THAT(urls,
+ UnorderedElementsAre(kOriginWithoutScript, kOriginWithScript1,
+ kOriginWithScript2, kOriginWithScript3));
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data_unittest.cc b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data_unittest.cc
index 66a250e2d3a..5e778ef7a90 100644
--- a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data_unittest.cc
+++ b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_data_unittest.cc
@@ -15,7 +15,7 @@ using ::testing::ElementsAre;
using ::testing::UnorderedElementsAre;
TEST(AssetLinkData, NonJSON) {
- constexpr char json[] = u8R"([trash])";
+ constexpr char json[] = R"([trash])";
AssetLinkData data;
EXPECT_FALSE(data.Parse(json));
EXPECT_THAT(data.includes(), IsEmpty());
@@ -24,7 +24,7 @@ TEST(AssetLinkData, NonJSON) {
TEST(AssetLinkData, NotList) {
constexpr char json[] =
- u8R"({
+ R"({
"include": "https://example/.well-known/assetlinks.json"
})";
AssetLinkData data;
@@ -35,7 +35,7 @@ TEST(AssetLinkData, NotList) {
TEST(AssetLinkData, IncludeWrongValue) {
constexpr char json[] =
- u8R"([{
+ R"([{
"include": 24
}])";
AssetLinkData data;
@@ -46,7 +46,7 @@ TEST(AssetLinkData, IncludeWrongValue) {
TEST(AssetLinkData, IncludeFile) {
constexpr char json[] =
- u8R"([{
+ R"([{
"include": "https://example/.well-known/assetlinks.json"
}])";
AssetLinkData data;
@@ -58,7 +58,7 @@ TEST(AssetLinkData, IncludeFile) {
TEST(AssetLinkData, IncludeHTTPFile) {
constexpr char json[] =
- u8R"([{
+ R"([{
"include": "http://example/.well-known/assetlinks.json"
}])";
AssetLinkData data;
@@ -69,7 +69,7 @@ TEST(AssetLinkData, IncludeHTTPFile) {
TEST(AssetLinkData, IncludeInvalidFile) {
constexpr char json[] =
- u8R"([{
+ R"([{
"include": "www.example/assetlinks.json"
}])";
AssetLinkData data;
@@ -80,7 +80,7 @@ TEST(AssetLinkData, IncludeInvalidFile) {
TEST(AssetLinkData, HandleURLsPermission) {
constexpr char json[] =
- u8R"([{
+ R"([{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "web",
@@ -102,7 +102,7 @@ TEST(AssetLinkData, HandleURLsPermission) {
TEST(AssetLinkData, BrokenRelation) {
constexpr char json[] =
- u8R"([{
+ R"([{
"relation": "delegate_permission/common.get_login_creds",
"target": {
"namespace": "web",
@@ -117,7 +117,7 @@ TEST(AssetLinkData, BrokenRelation) {
TEST(AssetLinkData, GetLoginCredsPermission) {
constexpr char json[] =
- u8R"([{
+ R"([{
"relation": ["delegate_permission/common.get_login_creds"],
"target": {
"namespace": "web",
@@ -154,7 +154,7 @@ TEST(AssetLinkData, GetLoginCredsPermission) {
TEST(AssetLinkData, MultiplePermissions) {
constexpr char json[] =
- u8R"([{
+ R"([{
"relation": ["something","delegate_permission/common.get_login_creds"],
"target": {
"namespace": "web",
@@ -191,7 +191,7 @@ TEST(AssetLinkData, MultiplePermissions) {
TEST(AssetLinkData, MixedStatements) {
constexpr char json[] =
- u8R"([{
+ R"([{
"relation": ["delegate_permission/common.get_login_creds"],
"target": {
"namespace": "web",
diff --git a/chromium/components/password_manager/core/browser/site_affiliation/hash_affiliation_fetcher_unittest.cc b/chromium/components/password_manager/core/browser/site_affiliation/hash_affiliation_fetcher_unittest.cc
index bffabc25885..c27b23049a8 100644
--- a/chromium/components/password_manager/core/browser/site_affiliation/hash_affiliation_fetcher_unittest.cc
+++ b/chromium/components/password_manager/core/browser/site_affiliation/hash_affiliation_fetcher_unittest.cc
@@ -380,6 +380,37 @@ TEST_F(HashAffiliationFetcherTest, DuplicateEquivalenceClassesAreIgnored) {
Facet{FacetURI::FromCanonicalSpec(kExampleAndroidFacetURI)}));
}
+TEST_F(HashAffiliationFetcherTest, NonRequestedEquivalenceClassesAreIgnored) {
+ affiliation_pb::LookupAffiliationResponse test_response;
+ // Equivalence class that was not requested and was added to affiliation
+ // response because of some error (for example hash collision.)
+ affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
+ eq_class1->add_facet()->set_id(kExampleWebFacet1URI);
+ affiliation_pb::Affiliation* eq_class2 = test_response.add_affiliation();
+ eq_class2->add_facet()->set_id(kExampleWebFacet2URI);
+ eq_class2->add_facet()->set_id(kExampleAndroidFacetURI);
+
+ std::vector<FacetURI> requested_uris;
+ requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet2URI));
+
+ SetupSuccessfulResponse(test_response.SerializeAsString());
+ testing::StrictMock<MockAffiliationFetcherDelegate> mock_delegate;
+ HashAffiliationFetcher fetcher(test_shared_loader_factory(), &mock_delegate);
+ std::unique_ptr<AffiliationFetcherDelegate::Result> result;
+ EXPECT_CALL(mock_delegate, OnFetchSucceeded(&fetcher, testing::_))
+ .WillOnce(MoveArg<1>(&result));
+ fetcher.StartRequest(requested_uris, {});
+ WaitForResponse();
+
+ ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_delegate));
+
+ ASSERT_EQ(1u, result->affiliations.size());
+ EXPECT_THAT(result->affiliations[0],
+ testing::UnorderedElementsAre(
+ Facet{FacetURI::FromCanonicalSpec(kExampleWebFacet2URI)},
+ Facet{FacetURI::FromCanonicalSpec(kExampleAndroidFacetURI)}));
+}
+
TEST_F(HashAffiliationFetcherTest, EmptyEquivalenceClassesAreIgnored) {
affiliation_pb::LookupAffiliationResponse test_response;
affiliation_pb::Affiliation* eq_class1 = test_response.add_affiliation();
diff --git a/chromium/components/password_manager/core/browser/store_metrics_reporter.cc b/chromium/components/password_manager/core/browser/store_metrics_reporter.cc
index eb05f454997..6d83d2b34ae 100644
--- a/chromium/components/password_manager/core/browser/store_metrics_reporter.cc
+++ b/chromium/components/password_manager/core/browser/store_metrics_reporter.cc
@@ -6,6 +6,7 @@
#include <memory>
#include "base/metrics/histogram_functions.h"
+#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
@@ -18,6 +19,7 @@
#include "components/password_manager/core/browser/password_reuse_manager.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "components/password_manager/core/browser/password_sync_util.h"
+#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_urls.h"
@@ -195,6 +197,35 @@ void ReportLoginsWithSchemesMetrics(
LogNumberOfAccountsForScheme(suffix_for_store, "Other", other_logins);
}
+void ReportPasswordNotesMetrics(
+ bool is_account_store,
+ const std::vector<std::unique_ptr<PasswordForm>>& forms) {
+ if (!base::FeatureList::IsEnabled(features::kPasswordNotes)) {
+ return;
+ }
+
+ base::StringPiece suffix_for_store =
+ GetMetricsSuffixForStore(is_account_store);
+
+ int credentials_with_non_empty_notes_count =
+ base::ranges::count_if(forms, [](const auto& form) {
+ return base::ranges::any_of(
+ form->notes, [](const auto& note) { return !note.value.empty(); });
+ });
+
+ base::UmaHistogramCounts1000(
+ base::StrCat({kPasswordManager, suffix_for_store,
+ ".PasswordNotes.CountCredentialsWithNonEmptyNotes"}),
+ credentials_with_non_empty_notes_count);
+
+ const std::string histogram_name =
+ base::StrCat({kPasswordManager, suffix_for_store,
+ ".PasswordNotes.CountNotesPerCredential"});
+ base::ranges::for_each(forms, [histogram_name](const auto& form) {
+ base::UmaHistogramCounts100(histogram_name, form->notes.size());
+ });
+}
+
void ReportTimesPasswordUsedMetrics(
bool is_account_store,
bool custom_passphrase_sync_enabled,
@@ -233,11 +264,11 @@ void ReportTimesPasswordUsedMetrics(
void ReportSyncingAccountStateMetrics(
const std::string& sync_username,
const std::vector<std::unique_ptr<PasswordForm>>& forms) {
- std::string signon_realm =
- GaiaUrls::GetInstance()->gaia_url().DeprecatedGetOriginAsURL().spec();
+ const GURL gaia_signon_realm =
+ GaiaUrls::GetInstance()->gaia_origin().GetURL();
bool syncing_account_saved = base::ranges::any_of(
- forms, [&signon_realm, &sync_username](const auto& form) {
- return signon_realm == form->signon_realm &&
+ forms, [&gaia_signon_realm, &sync_username](const auto& form) {
+ return gaia_signon_realm == GURL(form->signon_realm) &&
gaia::AreEmailsSame(sync_username,
base::UTF16ToUTF8(form->username_value));
});
@@ -526,6 +557,7 @@ void StoreMetricsReporter::ReportStoreMetrics(
ReportLoginsWithSchemesMetrics(is_account_store, results);
ReportTimesPasswordUsedMetrics(is_account_store,
custom_passphrase_sync_enabled_, results);
+ ReportPasswordNotesMetrics(is_account_store, results);
// The remaining metrics are not recorded for the account store:
// - SyncingAccountState2 just doesn't make sense, since syncing users only
diff --git a/chromium/components/password_manager/core/browser/store_metrics_reporter_unittest.cc b/chromium/components/password_manager/core/browser/store_metrics_reporter_unittest.cc
index 63bc8a372a2..6daf898d03d 100644
--- a/chromium/components/password_manager/core/browser/store_metrics_reporter_unittest.cc
+++ b/chromium/components/password_manager/core/browser/store_metrics_reporter_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "components/password_manager/core/browser/store_metrics_reporter.h"
+#include <string>
#include "base/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
@@ -10,9 +11,11 @@
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
#include "components/os_crypt/os_crypt_mocker.h"
#include "components/password_manager/core/browser/mock_password_reuse_manager.h"
#include "components/password_manager/core/browser/mock_password_store_interface.h"
+#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_manager_features_util.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
@@ -132,8 +135,9 @@ class StoreMetricsReporterTest : public SyncUsernameTestBase {
// should be mocked.
OSCryptMocker::SetUp();
- feature_list_.InitWithFeatures({features::kPasswordReuseDetectionEnabled},
- {});
+ feature_list_.InitWithFeatures(
+ {features::kPasswordReuseDetectionEnabled, features::kPasswordNotes},
+ {});
prefs_.registry()->RegisterBooleanPref(prefs::kCredentialsEnableService,
false);
@@ -178,6 +182,8 @@ TEST_P(StoreMetricsReporterTestWithParams, StoreIndependentMetrics) {
password_manager_enabled, 1);
}
+INSTANTIATE_TEST_SUITE_P(All, StoreMetricsReporterTestWithParams, Bool());
+
TEST_F(StoreMetricsReporterTest, ReportMetricsAtMostOncePerDay) {
auto profile_store =
base::MakeRefCounted<TestPasswordStore>(IsAccountStore(false));
@@ -987,6 +993,90 @@ TEST_F(StoreMetricsReporterTest, ReportMetricsForAdvancedProtection) {
RunUntilIdle();
}
-INSTANTIATE_TEST_SUITE_P(All, StoreMetricsReporterTestWithParams, Bool());
+TEST_F(StoreMetricsReporterTest, ReportPasswordNoteMetrics) {
+ auto profile_store =
+ base::MakeRefCounted<TestPasswordStore>(IsAccountStore(false));
+ profile_store->Init(&prefs_, /*affiliated_match_helper=*/nullptr);
+
+ PasswordForm password_form;
+ password_form.url = GURL("http://example.com");
+ password_form.username_value = u"test1@gmail.com";
+ password_form.notes = {PasswordNote(u"note", base::Time::Now())};
+ profile_store->AddLogin(password_form);
+ // ProfileStore - CountCredentialsWithNonEmptyNotes: 1
+
+ password_form.username_value = u"test2@gmail.com";
+ password_form.notes = {PasswordNote(u"another note", base::Time::Now()),
+ PasswordNote(std::u16string(), base::Time::Now())};
+ profile_store->AddLogin(password_form);
+ // ProfileStore - CountCredentialsWithNonEmptyNotes: 2
+
+ password_form.username_value = u"test3@gmail.com";
+ password_form.notes = {PasswordNote(std::u16string(), base::Time::Now()),
+ PasswordNote(u"some note", base::Time::Now())};
+ profile_store->AddLogin(password_form);
+ // ProfileStore - CountCredentialsWithNonEmptyNotes: 3
+
+ password_form.username_value = u"test4@gmail.com";
+ password_form.notes = {PasswordNote(std::u16string(), base::Time::Now())};
+ profile_store->AddLogin(password_form);
+ // ProfileStore - CountCredentialsWithNonEmptyNotes: 3
+
+ password_form.username_value = u"test5@gmail.com";
+ password_form.notes = {};
+ profile_store->AddLogin(password_form);
+ // ProfileStore - CountCredentialsWithNonEmptyNotes: 3
+
+ auto account_store =
+ base::MakeRefCounted<TestPasswordStore>(IsAccountStore(true));
+ account_store->Init(&prefs_, /*affiliated_match_helper=*/nullptr);
+
+ account_store->AddLogin(password_form);
+ // AccountStore - CountCredentialsWithNonEmptyNotes: 0
+
+ password_form.username_value = u"test6@gmail.com";
+ password_form.notes = {PasswordNote(std::u16string(), base::Time::Now())};
+ account_store->AddLogin(password_form);
+ // AccountStore - CountCredentialsWithNonEmptyNotes: 0
+
+ password_form.username_value = u"test7@gmail.com";
+ password_form.notes = {PasswordNote(u"note", base::Time::Now())};
+ account_store->AddLogin(password_form);
+ // AccountStore - CountCredentialsWithNonEmptyNotes: 1
+
+ base::HistogramTester histogram_tester;
+ StoreMetricsReporter reporter(profile_store.get(), account_store.get(),
+ sync_service(), identity_manager(), &prefs_,
+ /*password_reuse_manager=*/nullptr,
+ /*is_under_advanced_protection=*/false,
+ /*done_callback*/ base::DoNothing());
+
+ RunUntilIdle();
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "PasswordManager.ProfileStore.PasswordNotes.CountNotesPerCredential"),
+ BucketsAre(base::Bucket(0, 1), base::Bucket(1, 2), base::Bucket(2, 2)));
+ histogram_tester.ExpectBucketCount(
+ "PasswordManager.ProfileStore.PasswordNotes."
+ "CountCredentialsWithNonEmptyNotes",
+ 3, 1);
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "PasswordManager.AccountStore.PasswordNotes.CountNotesPerCredential"),
+ BucketsAre(base::Bucket(0, 1), base::Bucket(1, 2)));
+ histogram_tester.ExpectBucketCount(
+ "PasswordManager.AccountStore.PasswordNotes."
+ "CountCredentialsWithNonEmptyNotes",
+ 1, 1);
+
+ account_store->ShutdownOnUIThread();
+ profile_store->ShutdownOnUIThread();
+ // Make sure the PasswordStore destruction parts on the background sequence
+ // finish, otherwise we get memory leak reports.
+ RunUntilIdle();
+}
+
} // namespace
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/sync/password_model_type_controller.h b/chromium/components/password_manager/core/browser/sync/password_model_type_controller.h
index 91c967713d4..33bd26475a1 100644
--- a/chromium/components/password_manager/core/browser/sync/password_model_type_controller.h
+++ b/chromium/components/password_manager/core/browser/sync/password_model_type_controller.h
@@ -7,7 +7,6 @@
#include <memory>
-#include "base/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/password_manager/core/browser/password_account_storage_settings_watcher.h"
diff --git a/chromium/components/password_manager/core/browser/sync/password_proto_utils.cc b/chromium/components/password_manager/core/browser/sync/password_proto_utils.cc
index 6e52eb3755f..ccf7b15eee9 100644
--- a/chromium/components/password_manager/core/browser/sync/password_proto_utils.cc
+++ b/chromium/components/password_manager/core/browser/sync/password_proto_utils.cc
@@ -37,6 +37,46 @@ base::Time ConvertToBaseTime(uint64_t time) {
base::Microseconds(time));
}
+// Trims the notes field in the sync_pb::PasswordSpecificsData proto. If neither
+// the high level notes field nor any of the individual notes contains populated
+// fields, the high level field is cleared.
+void TrimPasswordSpecificsDataNotesForCaching(
+ sync_pb::PasswordSpecificsData& trimmed_password_data) {
+ // `notes` field should be cleared if all notes are empty.
+ bool non_empty_note_exists = false;
+ // Iterate over all notes and clear all supported fields.
+ for (sync_pb::PasswordSpecificsData_Notes_Note& note :
+ *trimmed_password_data.mutable_notes()->mutable_note()) {
+ // Remember the `unique_display_name` such that if this note needs to be
+ // cached, the `unique_display_name` is required to be able reconcile cached
+ // notes during commit.
+ std::string unique_display_name = note.unique_display_name();
+ note.clear_unique_display_name();
+ note.clear_value();
+ note.clear_date_created_windows_epoch_micros();
+ note.clear_hide_by_default();
+ if (note.ByteSizeLong() != 0) {
+ non_empty_note_exists = true;
+ // Set the `unique_display_name` since it's required during the
+ // reconciliation step in PasswordNotesToProto().
+ note.set_unique_display_name(unique_display_name);
+ }
+ }
+ if (non_empty_note_exists) {
+ // Since some of the notes contain populated fields, no more trimming is
+ // possible.
+ return;
+ } else {
+ trimmed_password_data.mutable_notes()->clear_note();
+ }
+ // None of the individual notes contains populated fields. If the high level
+ // Notes proto doesn't contain unknown fields either, we should clear the
+ // notes field when trimming.
+ if (trimmed_password_data.notes().unknown_fields().empty()) {
+ trimmed_password_data.clear_notes();
+ }
+}
+
} // namespace
sync_pb::PasswordSpecificsData_PasswordIssues PasswordIssuesMapToProto(
@@ -102,6 +142,52 @@ base::flat_map<InsecureType, InsecurityMetadata> PasswordIssuesMapFromProto(
return form_issues;
}
+std::vector<PasswordNote> PasswordNotesFromProto(
+ const sync_pb::PasswordSpecificsData_Notes& notes_proto) {
+ std::vector<PasswordNote> notes;
+ for (const sync_pb::PasswordSpecificsData_Notes_Note& note :
+ notes_proto.note()) {
+ notes.emplace_back(
+ base::UTF8ToUTF16(note.unique_display_name()),
+ base::UTF8ToUTF16(note.value()),
+ ConvertToBaseTime(note.date_created_windows_epoch_micros()),
+ note.hide_by_default());
+ }
+ return notes;
+}
+
+sync_pb::PasswordSpecificsData_Notes PasswordNotesToProto(
+ const std::vector<PasswordNote>& notes,
+ const sync_pb::PasswordSpecificsData_Notes& base_notes) {
+ sync_pb::PasswordSpecificsData_Notes notes_proto = base_notes;
+ for (const PasswordNote& note : notes) {
+ sync_pb::PasswordSpecificsData_Notes_Note* note_proto = nullptr;
+ // Try to find a corresponding cached note. Since `unique_display_name` is
+ // unique per password, and immutable, it can be used to reconcile notes.
+ // `unique_display_name` is cached in TrimPasswordSpecificsDataForCaching().
+ for (sync_pb::PasswordSpecificsData_Notes_Note& cached_note :
+ *notes_proto.mutable_note()) {
+ if (cached_note.unique_display_name() ==
+ base::UTF16ToUTF8(note.unique_display_name)) {
+ note_proto = &cached_note;
+ break;
+ }
+ }
+ // If no corresponding cached note is found, add a new one.
+ if (!note_proto) {
+ note_proto = notes_proto.add_note();
+ note_proto->set_unique_display_name(
+ base::UTF16ToUTF8(note.unique_display_name));
+ }
+
+ note_proto->set_value(base::UTF16ToUTF8(note.value));
+ note_proto->set_date_created_windows_epoch_micros(
+ note.date_created.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ note_proto->set_hide_by_default(note.hide_by_default);
+ }
+ return notes_proto;
+}
+
sync_pb::PasswordSpecificsData TrimPasswordSpecificsDataForCaching(
const sync_pb::PasswordSpecificsData& password_specifics_data) {
sync_pb::PasswordSpecificsData trimmed_password_data =
@@ -124,28 +210,36 @@ sync_pb::PasswordSpecificsData TrimPasswordSpecificsDataForCaching(
trimmed_password_data.clear_date_last_used();
trimmed_password_data.clear_password_issues();
trimmed_password_data.clear_date_password_modified_windows_epoch_micros();
+
+ TrimPasswordSpecificsDataNotesForCaching(trimmed_password_data);
+
return trimmed_password_data;
}
sync_pb::PasswordSpecifics SpecificsFromPassword(
- const PasswordForm& password_form) {
- sync_pb::PasswordSpecifics specifics;
- *specifics.mutable_client_only_encrypted_data() =
- SpecificsDataFromPassword(password_form);
-
+ const PasswordForm& password_form,
+ const sync_pb::PasswordSpecificsData& base_password_data) {
// WARNING: if you are adding support for new `PasswordSpecificsData` fields,
- // you need to update following functions accordingly:
+ // you need to update the following functions accordingly:
// `TrimPasswordSpecificsDataForCaching`
// `TrimRemoteSpecificsForCachingPreservesOnlyUnknownFields`
DCHECK_EQ(0u, TrimPasswordSpecificsDataForCaching(
- specifics.client_only_encrypted_data())
+ SpecificsDataFromPassword(password_form,
+ /*base_password_data=*/{}))
.ByteSizeLong());
+
+ sync_pb::PasswordSpecifics specifics;
+ *specifics.mutable_client_only_encrypted_data() =
+ SpecificsDataFromPassword(password_form, base_password_data);
return specifics;
}
sync_pb::PasswordSpecificsData SpecificsDataFromPassword(
- const PasswordForm& password_form) {
- sync_pb::PasswordSpecificsData password_data;
+ const PasswordForm& password_form,
+ const sync_pb::PasswordSpecificsData& base_password_data) {
+ // Repeated fields in base_password_data might need to be cleared
+ // before adding entries from password_form to avoid duplicates.
+ sync_pb::PasswordSpecificsData password_data = base_password_data;
password_data.set_scheme(static_cast<int>(password_form.scheme));
password_data.set_signon_realm(password_form.signon_realm);
password_data.set_origin(password_form.url.spec());
@@ -176,7 +270,8 @@ sync_pb::PasswordSpecificsData SpecificsDataFromPassword(
: password_form.federation_origin.Serialize());
*password_data.mutable_password_issues() =
PasswordIssuesMapToProto(password_form.password_issues);
-
+ *password_data.mutable_notes() =
+ PasswordNotesToProto(password_form.notes, base_password_data.notes());
return password_data;
}
@@ -221,7 +316,7 @@ sync_pb::PasswordWithLocalData PasswordWithLocalDataFromPassword(
sync_pb::PasswordWithLocalData password_with_local_data;
*password_with_local_data.mutable_password_specifics_data() =
- SpecificsDataFromPassword(password_form);
+ SpecificsDataFromPassword(password_form, /*base_password_data=*/{});
auto* local_data = password_with_local_data.mutable_local_data();
local_data->set_opaque_metadata(SerializeOpaqueLocalData(password_form));
@@ -268,7 +363,7 @@ PasswordForm PasswordFromSpecifics(
password.federation_origin =
url::Origin::Create(GURL(password_data.federation_url()));
password.password_issues = PasswordIssuesMapFromProto(password_data);
-
+ password.notes = PasswordNotesFromProto(password_data.notes());
return password;
}
@@ -278,8 +373,9 @@ bool DeserializeFormData(base::Value::Dict& serialized_data,
std::string* form_url = serialized_data.FindString(kUrlKey);
std::string* form_action = serialized_data.FindString(kActionKey);
base::Value::List* fields = serialized_data.FindList(kFieldsKey);
- if (!form_name || !form_url || !form_action || !fields)
+ if (!form_name || !form_url || !form_action || !fields) {
return false;
+ }
form_data.name = base::UTF8ToUTF16(*form_name);
form_data.url = GURL(*form_url);
form_data.action = GURL(*form_action);
@@ -287,14 +383,16 @@ bool DeserializeFormData(base::Value::Dict& serialized_data,
for (auto& serialized_field : *fields) {
base::Value::Dict* serialized_field_dictionary =
serialized_field.GetIfDict();
- if (!serialized_field_dictionary)
+ if (!serialized_field_dictionary) {
return false;
+ }
FormFieldData field;
std::string* field_name = serialized_field_dictionary->FindString(kNameKey);
std::string* field_type =
serialized_field_dictionary->FindString(kFormControlTypeKey);
- if (!field_name || !field_type)
+ if (!field_name || !field_type) {
return false;
+ }
field.name = base::UTF8ToUTF16(*field_name);
field.form_control_type = *field_type;
form_data.fields.push_back(field);
@@ -307,17 +405,20 @@ void DeserializeOpaqueLocalData(const std::string& opaque_metadata,
JSONStringValueDeserializer json_deserializer(opaque_metadata);
std::unique_ptr<base::Value> root(
json_deserializer.Deserialize(nullptr, nullptr));
- if (!root.get() || !root->is_dict())
+ if (!root.get() || !root->is_dict()) {
return;
+ }
base::Value::Dict serialized_data(std::move(root->GetDict()));
auto skip_zero_click = serialized_data.FindBool(kSkipZeroClickKey);
auto* serialized_form_data = serialized_data.FindDict(kFormDataKey);
- if (!skip_zero_click.has_value() || !serialized_form_data)
+ if (!skip_zero_click.has_value() || !serialized_form_data) {
return;
+ }
FormData form_data;
- if (!DeserializeFormData(*serialized_form_data, form_data))
+ if (!DeserializeFormData(*serialized_form_data, form_data)) {
return;
+ }
password_form.skip_zero_click = *skip_zero_click;
password_form.form_data = std::move(form_data);
}
diff --git a/chromium/components/password_manager/core/browser/sync/password_proto_utils.h b/chromium/components/password_manager/core/browser/sync/password_proto_utils.h
index ef32d0fcb7c..9fbd0e3cca0 100644
--- a/chromium/components/password_manager/core/browser/sync/password_proto_utils.h
+++ b/chromium/components/password_manager/core/browser/sync/password_proto_utils.h
@@ -14,6 +14,7 @@ namespace sync_pb {
class PasswordSpecifics;
class PasswordSpecificsData;
class PasswordSpecificsData_PasswordIssues;
+class PasswordSpecificsData_Notes;
class PasswordWithLocalData;
class ListPasswordsResult;
} // namespace sync_pb
@@ -31,13 +32,32 @@ sync_pb::PasswordSpecificsData_PasswordIssues PasswordIssuesMapToProto(
base::flat_map<InsecureType, InsecurityMetadata> PasswordIssuesMapFromProto(
const sync_pb::PasswordSpecificsData& password_data);
+// Converts a sync_pb::PasswordSpecificsData_Notes to a
+// std::vector<PasswordNote>.
+std::vector<PasswordNote> PasswordNotesFromProto(
+ const sync_pb::PasswordSpecificsData_Notes& notes_proto);
+
+// Converts a std::vector<PasswordNote> to a
+// sync_pb::PasswordSpecificsData_Notes. `base_notes` is intended for carrying
+// over unknown and unsupported note fields when there is a local modification
+// to an existing sync entity.
+sync_pb::PasswordSpecificsData_Notes PasswordNotesToProto(
+ const std::vector<PasswordNote>& notes,
+ const sync_pb::PasswordSpecificsData_Notes& base_notes);
+
// Returns sync_pb::PasswordSpecifics based on given `password_form`.
+// `base_password_data` is intended for carrying over unknown and unsupported
+// fields when there is a local modification to an existing sync entity.
sync_pb::PasswordSpecifics SpecificsFromPassword(
- const PasswordForm& password_form);
+ const PasswordForm& password_form,
+ const sync_pb::PasswordSpecificsData& base_password_data);
// Returns sync_pb::PasswordSpecificsData based on given `password_form`.
+// `base_password_data` is intended for carrying over unknown and unsupported
+// fields when there is a local modification to an existing sync entity.
sync_pb::PasswordSpecificsData SpecificsDataFromPassword(
- const PasswordForm& password_form);
+ const PasswordForm& password_form,
+ const sync_pb::PasswordSpecificsData& base_password_data);
// Returns sync_pb::PasswordWithLocalData based on given `password_form`.
sync_pb::PasswordWithLocalData PasswordWithLocalDataFromPassword(
diff --git a/chromium/components/password_manager/core/browser/sync/password_proto_utils_unittest.cc b/chromium/components/password_manager/core/browser/sync/password_proto_utils_unittest.cc
index 99693002fbf..5fdcf60d29a 100644
--- a/chromium/components/password_manager/core/browser/sync/password_proto_utils_unittest.cc
+++ b/chromium/components/password_manager/core/browser/sync/password_proto_utils_unittest.cc
@@ -88,6 +88,9 @@ sync_pb::PasswordSpecificsData CreateSpecificsData(
password_specifics.set_federation_url(std::string());
*password_specifics.mutable_password_issues() =
CreateSpecificsDataIssues(issue_types);
+ // The current code always populates notes for outgoing protos even when
+ // non-exists.
+ password_specifics.mutable_notes();
return password_specifics;
}
@@ -106,6 +109,84 @@ TEST(PasswordProtoUtilsTest, ConvertIssueProtoToMapAndBack) {
Eq(specifics_data.password_issues().SerializeAsString()));
}
+TEST(PasswordProtoUtilsTest, ConvertPasswordNoteToNotesProtoAndBack) {
+ std::vector<PasswordNote> notes;
+ notes.emplace_back(u"unique_display_name", u"value",
+ /*date_created*/ base::Time::Now(),
+ /*hide_by_default=*/true);
+ notes.emplace_back(u"unique_display_name2", u"value2",
+ /*date_created*/ base::Time::Now() - base::Hours(1),
+ /*hide_by_default=*/false);
+ sync_pb::PasswordSpecificsData_Notes base_notes_proto;
+ EXPECT_EQ(notes, PasswordNotesFromProto(
+ PasswordNotesToProto(notes, base_notes_proto)));
+}
+
+TEST(PasswordProtoUtilsTest,
+ CacheNoteUniqueDisplayNameWhenNoteContainsUnknownField) {
+ const std::string kNoteUniqueDisplayName = "Note Unique Display Name";
+ sync_pb::PasswordSpecificsData password_specifics_data;
+ sync_pb::PasswordSpecificsData_Notes_Note* note =
+ password_specifics_data.mutable_notes()->add_note();
+ note->set_unique_display_name(kNoteUniqueDisplayName);
+ *note->mutable_unknown_fields() = "unknown_fields";
+ sync_pb::PasswordSpecificsData trimmed_specifics =
+ TrimPasswordSpecificsDataForCaching(password_specifics_data);
+ // The unique_display_name field should be cached since it's necessary for
+ // reconciliation of notes with cached ones during commit.
+ EXPECT_EQ(kNoteUniqueDisplayName,
+ trimmed_specifics.notes().note(0).unique_display_name());
+}
+
+TEST(PasswordProtoUtilsTest, ReconcileCachedNotesUsingUnqiueDisplayName) {
+ const std::string kNoteUniqueDisplayName1 = "Note Unique Display Name 1";
+ const std::string kNoteValue1 = "Note Value 1";
+ const std::string kNoteUnknownFields1 = "Note Unknown Fields 1";
+ const std::string kNoteUniqueDisplayName2 = "Note Unique Display Name 2";
+ const std::string kNoteValue2 = "Note Value 2";
+ const std::string kNoteUnknownFields2 = "Note Unknown Fields 2";
+
+ // Create a base note proto that contains two notes with unknown fields.
+ sync_pb::PasswordSpecificsData_Notes base_notes;
+
+ sync_pb::PasswordSpecificsData_Notes_Note* note_proto1 =
+ base_notes.add_note();
+ note_proto1->set_unique_display_name(kNoteUniqueDisplayName1);
+ *note_proto1->mutable_unknown_fields() = kNoteUnknownFields1;
+
+ sync_pb::PasswordSpecificsData_Notes_Note* note_proto2 =
+ base_notes.add_note();
+ note_proto2->set_unique_display_name(kNoteUniqueDisplayName2);
+ *note_proto2->mutable_unknown_fields() = kNoteUnknownFields2;
+
+ // Create the notes to be committed with the same unique display names in the
+ // base specifics. Notes will be reconciled using the unique display name and
+ // hence the order shouldn't matter.
+ std::vector<PasswordNote> notes;
+ notes.emplace_back(base::UTF8ToUTF16(kNoteUniqueDisplayName2),
+ base::UTF8ToUTF16(kNoteValue2),
+ /*date_created=*/base::Time::Now(),
+ /*hide_by_default=*/true);
+ notes.emplace_back(base::UTF8ToUTF16(kNoteUniqueDisplayName1),
+ base::UTF8ToUTF16(kNoteValue1),
+ /*date_created=*/base::Time::Now(),
+ /*hide_by_default=*/true);
+
+ // Reconciliation should preserve the order of the notes in the base specifics
+ // and carry over the known fields.
+ sync_pb::PasswordSpecificsData_Notes reconciled_notes =
+ PasswordNotesToProto(notes, base_notes);
+ EXPECT_EQ(kNoteUniqueDisplayName1,
+ reconciled_notes.note(0).unique_display_name());
+ EXPECT_EQ(kNoteValue1, reconciled_notes.note(0).value());
+ EXPECT_EQ(kNoteUnknownFields1, reconciled_notes.note(0).unknown_fields());
+
+ EXPECT_EQ(kNoteUniqueDisplayName2,
+ reconciled_notes.note(1).unique_display_name());
+ EXPECT_EQ(kNoteValue2, reconciled_notes.note(1).value());
+ EXPECT_EQ(kNoteUnknownFields2, reconciled_notes.note(1).unknown_fields());
+}
+
TEST(PasswordProtoUtilsTest, ConvertSpecificsToFormAndBack) {
sync_pb::PasswordSpecifics specifics;
*specifics.mutable_client_only_encrypted_data() =
@@ -114,11 +195,51 @@ TEST(PasswordProtoUtilsTest, ConvertSpecificsToFormAndBack) {
/*issue_types=*/{});
EXPECT_THAT(SpecificsFromPassword(
- PasswordFromSpecifics(specifics.client_only_encrypted_data()))
+ PasswordFromSpecifics(specifics.client_only_encrypted_data()),
+ /*base_password_data=*/{})
.SerializeAsString(),
Eq(specifics.SerializeAsString()));
}
+TEST(PasswordProtoUtilsTest, SpecificsDataFromPasswordPreservesUnknownFields) {
+ sync_pb::PasswordSpecificsData specifics =
+ CreateSpecificsData("http://www.origin.com/", "username_element",
+ "username_value", "password_element", "signon_realm",
+ /*issue_types=*/{});
+
+ PasswordForm form = PasswordFromSpecifics(specifics);
+
+ *specifics.mutable_unknown_fields() = "unknown_fields";
+
+ sync_pb::PasswordSpecificsData specifics_with_only_unknown_fields;
+ *specifics_with_only_unknown_fields.mutable_unknown_fields() =
+ "unknown_fields";
+
+ EXPECT_EQ(SpecificsDataFromPassword(form, specifics_with_only_unknown_fields)
+ .SerializeAsString(),
+ specifics.SerializeAsString());
+}
+
+TEST(PasswordProtoUtilsTest, SpecificsFromPasswordPreservesUnknownFields) {
+ sync_pb::PasswordSpecificsData specifics =
+ CreateSpecificsData("http://www.origin.com/", "username_element",
+ "username_value", "password_element", "signon_realm",
+ /*issue_types=*/{});
+
+ PasswordForm form = PasswordFromSpecifics(specifics);
+
+ *specifics.mutable_unknown_fields() = "unknown_fields";
+
+ sync_pb::PasswordSpecificsData specifics_with_only_unknown_fields;
+ *specifics_with_only_unknown_fields.mutable_unknown_fields() =
+ "unknown_fields";
+
+ EXPECT_EQ(SpecificsFromPassword(form, specifics_with_only_unknown_fields)
+ .client_only_encrypted_data()
+ .SerializeAsString(),
+ specifics.SerializeAsString());
+}
+
TEST(PasswordProtoUtilsTest,
ConvertPasswordWithLocalDataToFullPasswordFormAndBack) {
sync_pb::PasswordWithLocalData password_data;
diff --git a/chromium/components/password_manager/core/browser/sync/password_sync_bridge.cc b/chromium/components/password_manager/core/browser/sync/password_sync_bridge.cc
index af17fccd64d..3def8b319c0 100644
--- a/chromium/components/password_manager/core/browser/sync/password_sync_bridge.cc
+++ b/chromium/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -15,6 +15,7 @@
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
+#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
@@ -24,13 +25,13 @@
#include "components/password_manager/core/browser/password_store_sync.h"
#include "components/password_manager/core/browser/sync/password_proto_utils.h"
#include "components/password_manager/core/common/password_manager_features.h"
+#include "components/sync/base/features.h"
#include "components/sync/model/in_memory_metadata_change_list.h"
#include "components/sync/model/metadata_batch.h"
#include "components/sync/model/metadata_change_list.h"
#include "components/sync/model/model_type_change_processor.h"
#include "components/sync/model/mutable_data_batch.h"
#include "components/sync/model/sync_metadata_store_change_list.h"
-#include "net/base/escape.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
@@ -58,11 +59,11 @@ enum class SyncMetadataReadError {
std::string ComputeClientTag(
const sync_pb::PasswordSpecificsData& password_data) {
- return net::EscapePath(GURL(password_data.origin()).spec()) + "|" +
- net::EscapePath(password_data.username_element()) + "|" +
- net::EscapePath(password_data.username_value()) + "|" +
- net::EscapePath(password_data.password_element()) + "|" +
- net::EscapePath(password_data.signon_realm());
+ return base::EscapePath(GURL(password_data.origin()).spec()) + "|" +
+ base::EscapePath(password_data.username_element()) + "|" +
+ base::EscapePath(password_data.username_value()) + "|" +
+ base::EscapePath(password_data.password_element()) + "|" +
+ base::EscapePath(password_data.signon_realm());
}
base::Time ConvertToBaseTime(uint64_t time) {
@@ -72,17 +73,20 @@ base::Time ConvertToBaseTime(uint64_t time) {
base::Microseconds(time));
}
-PasswordForm PasswordFromEntityChange(const syncer::EntityChange& entity_change,
- base::Time sync_time) {
+PasswordForm PasswordFromEntityChange(
+ const syncer::EntityChange& entity_change) {
DCHECK(entity_change.data().specifics.has_password());
const sync_pb::PasswordSpecificsData& password_data =
entity_change.data().specifics.password().client_only_encrypted_data();
return PasswordFromSpecifics(password_data);
}
-std::unique_ptr<syncer::EntityData> CreateEntityData(const PasswordForm& form) {
+std::unique_ptr<syncer::EntityData> CreateEntityData(
+ const PasswordForm& form,
+ const sync_pb::PasswordSpecificsData& base_password_data) {
auto entity_data = std::make_unique<syncer::EntityData>();
- *entity_data->specifics.mutable_password() = SpecificsFromPassword(form);
+ *entity_data->specifics.mutable_password() =
+ SpecificsFromPassword(form, base_password_data);
entity_data->name = form.signon_realm;
return entity_data;
}
@@ -129,7 +133,10 @@ bool AreLocalAndRemotePasswordsEqualExcludingIssues(
password_specifics.display_name() &&
password_form.icon_url.spec() == password_specifics.avatar_url() &&
url::Origin::Create(GURL(password_specifics.federation_url()))
- .Serialize() == password_form.federation_origin.Serialize());
+ .Serialize() ==
+ password_form.federation_origin.Serialize()) &&
+ password_form.notes ==
+ PasswordNotesFromProto(password_specifics.notes());
}
// Returns true iff |password_specifics| and |password_form| are equal
@@ -247,6 +254,17 @@ PasswordSyncBridge::PasswordSyncBridge(
password_store_sync_->GetMetadataStore()->DeleteAllSyncMetadata();
batch = std::make_unique<syncer::MetadataBatch>();
sync_metadata_read_error = SyncMetadataReadError::kReadSuccessButCleared;
+ } else if (base::FeatureList::IsEnabled(
+ syncer::kCacheBaseEntitySpecificsInMetadata) &&
+ SyncMetadataCacheContainsSupportedFields(
+ batch->GetAllMetadata())) {
+ // Caching entity specifics is meant to preserve fields not supported in a
+ // given browser version during commits to the server. If the cache
+ // contains supported fields, this means that the browser was updated and
+ // we should force the initial sync flow to propagate the cached data into
+ // the local model.
+ password_store_sync_->GetMetadataStore()->DeleteAllSyncMetadata();
+ batch = std::make_unique<syncer::MetadataBatch>();
}
}
base::UmaHistogramEnumeration("PasswordManager.SyncMetadataReadError",
@@ -288,8 +306,13 @@ void PasswordSyncBridge::ActOnPasswordStoreChanges(
switch (change.type()) {
case PasswordStoreChange::ADD:
case PasswordStoreChange::UPDATE: {
- change_processor()->Put(storage_key, CreateEntityData(change.form()),
- &metadata_change_list);
+ change_processor()->Put(
+ storage_key,
+ CreateEntityData(
+ change.form(),
+ GetPossiblyTrimmedPasswordSpecificsData(storage_key)),
+ &metadata_change_list);
+
break;
}
case PasswordStoreChange::REMOVE: {
@@ -373,7 +396,6 @@ absl::optional<syncer::ModelError> PasswordSyncBridge::MergeSyncData(
PasswordStoreChangeList password_store_changes;
{
ScopedStoreTransaction transaction(password_store_sync_);
- const base::Time time_now = base::Time::Now();
// For any local password that doesn't exist in the remote passwords, issue
// a change_processor()->Put(). For any local password that exists in the
// remote passwords, both should be merged by picking the most recently
@@ -382,8 +404,12 @@ absl::optional<syncer::ModelError> PasswordSyncBridge::MergeSyncData(
std::unordered_set<std::string> client_tags_of_local_passwords;
for (const auto& [primary_key, local_password_form] :
key_to_local_form_map) {
+ const std::string storage_key = base::NumberToString(primary_key.value());
std::unique_ptr<syncer::EntityData> local_form_entity_data =
- CreateEntityData(*local_password_form);
+ CreateEntityData(
+ *local_password_form,
+ GetPossiblyTrimmedPasswordSpecificsData(storage_key));
+
const std::string client_tag_of_local_password =
GetClientTag(*local_form_entity_data);
client_tags_of_local_passwords.insert(client_tag_of_local_password);
@@ -392,9 +418,8 @@ absl::optional<syncer::ModelError> PasswordSyncBridge::MergeSyncData(
client_tag_of_local_password) == 0) {
// Local password doesn't exist in the remote model, Put() it in the
// processor.
- change_processor()->Put(
- /*storage_key=*/base::NumberToString(primary_key.value()),
- std::move(local_form_entity_data), metadata_change_list.get());
+ change_processor()->Put(storage_key, std::move(local_form_entity_data),
+ metadata_change_list.get());
continue;
}
@@ -408,10 +433,7 @@ absl::optional<syncer::ModelError> PasswordSyncBridge::MergeSyncData(
// First, we need to inform the processor about the storage key anyway.
change_processor()->UpdateStorageKey(
- remote_entity_change.data(),
- /*storage_key=*/
- base::NumberToString(primary_key.value()),
- metadata_change_list.get());
+ remote_entity_change.data(), storage_key, metadata_change_list.get());
if (AreLocalAndRemotePasswordsEqual(remote_password_specifics,
*local_password_form)) {
@@ -429,15 +451,13 @@ absl::optional<syncer::ModelError> PasswordSyncBridge::MergeSyncData(
// local password has been marked as phished. While all other types of
// issues are easy to recompute (e.g. via Password Check) phished
// entries are only found locally, so persisting them is important.
- change_processor()->Put(
- /*storage_key=*/base::NumberToString(primary_key.value()),
- std::move(local_form_entity_data), metadata_change_list.get());
+ change_processor()->Put(storage_key, std::move(local_form_entity_data),
+ metadata_change_list.get());
} else {
// The remote password is more recent, update the local model.
UpdateLoginError update_login_error;
const PasswordForm form =
- PasswordFromEntityChange(remote_entity_change,
- /*sync_time=*/time_now);
+ PasswordFromEntityChange(remote_entity_change);
PasswordStoreChangeList changes =
password_store_sync_->UpdateLoginSync(form, &update_login_error);
DCHECK_LE(changes.size(), 1U);
@@ -477,8 +497,7 @@ absl::optional<syncer::ModelError> PasswordSyncBridge::MergeSyncData(
AddLoginError add_login_error;
PasswordStoreChangeList changes = password_store_sync_->AddLoginSync(
- PasswordFromEntityChange(*entity_change, /*sync_time=*/time_now),
- &add_login_error);
+ PasswordFromEntityChange(*entity_change), &add_login_error);
base::UmaHistogramEnumeration(
"PasswordManager.MergeSyncData.AddLoginSyncError", add_login_error);
@@ -578,8 +597,6 @@ absl::optional<syncer::ModelError> PasswordSyncBridge::ApplySyncChanges(
base::AutoReset<bool> processing_changes(&is_processing_remote_sync_changes_,
true);
- const base::Time time_now = base::Time::Now();
-
// This is used to keep track of all the changes applied to the password store
// to notify other observers of the password store.
PasswordStoreChangeList password_store_changes;
@@ -594,8 +611,7 @@ absl::optional<syncer::ModelError> PasswordSyncBridge::ApplySyncChanges(
case syncer::EntityChange::ACTION_ADD:
AddLoginError add_login_error;
changes = password_store_sync_->AddLoginSync(
- PasswordFromEntityChange(*entity_change, /*sync_time=*/time_now),
- &add_login_error);
+ PasswordFromEntityChange(*entity_change), &add_login_error);
base::UmaHistogramEnumeration(
"PasswordManager.ApplySyncChanges.AddLoginSyncError",
add_login_error);
@@ -647,8 +663,7 @@ absl::optional<syncer::ModelError> PasswordSyncBridge::ApplySyncChanges(
continue;
}
UpdateLoginError update_login_error;
- PasswordForm form =
- PasswordFromEntityChange(*entity_change, /*sync_time=*/time_now);
+ PasswordForm form = PasswordFromEntityChange(*entity_change);
changes =
password_store_sync_->UpdateLoginSync(form, &update_login_error);
FormPrimaryKey primary_key =
@@ -746,7 +761,10 @@ void PasswordSyncBridge::GetData(StorageKeyList storage_keys,
for (const std::string& storage_key : storage_keys) {
FormPrimaryKey primary_key = ParsePrimaryKey(storage_key);
if (key_to_form_map.count(primary_key) != 0) {
- batch->Put(storage_key, CreateEntityData(*key_to_form_map[primary_key]));
+ batch->Put(storage_key,
+ CreateEntityData(
+ *key_to_form_map[primary_key],
+ GetPossiblyTrimmedPasswordSpecificsData(storage_key)));
}
}
std::move(callback).Run(std::move(batch));
@@ -766,8 +784,10 @@ void PasswordSyncBridge::GetAllDataForDebugging(DataCallback callback) {
auto batch = std::make_unique<syncer::MutableDataBatch>();
for (const auto& [primary_key, form] : key_to_form_map) {
form->password_value = u"<redacted>";
- batch->Put(base::NumberToString(primary_key.value()),
- CreateEntityData(*form));
+ const std::string storage_key = base::NumberToString(primary_key.value());
+ batch->Put(storage_key,
+ CreateEntityData(*form, GetPossiblyTrimmedPasswordSpecificsData(
+ storage_key)));
}
std::move(callback).Run(std::move(batch));
}
@@ -845,7 +865,7 @@ void PasswordSyncBridge::ApplyStopSyncChanges(
}
sync_pb::EntitySpecifics PasswordSyncBridge::TrimRemoteSpecificsForCaching(
- const sync_pb::EntitySpecifics& entity_specifics) {
+ const sync_pb::EntitySpecifics& entity_specifics) const {
DCHECK(entity_specifics.has_password());
sync_pb::EntitySpecifics trimmed_entity_specifics;
*trimmed_entity_specifics.mutable_password()
@@ -855,6 +875,40 @@ sync_pb::EntitySpecifics PasswordSyncBridge::TrimRemoteSpecificsForCaching(
return trimmed_entity_specifics;
}
+const sync_pb::PasswordSpecificsData&
+PasswordSyncBridge::GetPossiblyTrimmedPasswordSpecificsData(
+ const std::string& storage_key) {
+ return change_processor()
+ ->GetPossiblyTrimmedRemoteSpecifics(storage_key)
+ .password()
+ .client_only_encrypted_data();
+}
+
+// TODO(crbug.com/1296159): Consider moving this logic to processor. If not
+// moved, add a metric for read errors where this function is being called.
+bool PasswordSyncBridge::SyncMetadataCacheContainsSupportedFields(
+ const syncer::EntityMetadataMap& metadata_map) const {
+ for (const auto& metadata_entry : metadata_map) {
+ // Serialize the cached specifics and parse them back to a proto. Any fields
+ // that were cached as unknown and are known in the current browser version
+ // should be parsed correctly.
+ std::string serialized_specifics;
+ metadata_entry.second->possibly_trimmed_base_specifics().SerializeToString(
+ &serialized_specifics);
+ sync_pb::EntitySpecifics parsed_specifics;
+ parsed_specifics.ParseFromString(serialized_specifics);
+
+ // If `parsed_specifics` contain any supported fields, they would be cleared
+ // by the trimming function.
+ if (parsed_specifics.ByteSizeLong() !=
+ TrimRemoteSpecificsForCaching(parsed_specifics).ByteSizeLong()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
std::set<FormPrimaryKey> PasswordSyncBridge::GetUnsyncedPasswordsStorageKeys() {
std::set<FormPrimaryKey> storage_keys;
DCHECK(password_store_sync_);
diff --git a/chromium/components/password_manager/core/browser/sync/password_sync_bridge.h b/chromium/components/password_manager/core/browser/sync/password_sync_bridge.h
index bc9b1aa1bd2..04d01f8d322 100644
--- a/chromium/components/password_manager/core/browser/sync/password_sync_bridge.h
+++ b/chromium/components/password_manager/core/browser/sync/password_sync_bridge.h
@@ -10,6 +10,7 @@
#include "base/sequence_checker.h"
#include "components/password_manager/core/browser/password_store_change.h"
#include "components/password_manager/core/browser/password_store_sync.h"
+#include "components/sync/model/metadata_batch.h"
#include "components/sync/model/model_type_sync_bridge.h"
namespace syncer {
@@ -64,7 +65,7 @@ class PasswordSyncBridge : public syncer::ModelTypeSyncBridge {
void ApplyStopSyncChanges(std::unique_ptr<syncer::MetadataChangeList>
delete_metadata_change_list) override;
sync_pb::EntitySpecifics TrimRemoteSpecificsForCaching(
- const sync_pb::EntitySpecifics& entity_specifics) override;
+ const sync_pb::EntitySpecifics& entity_specifics) const override;
static std::string ComputeClientTagForTesting(
const sync_pb::PasswordSpecificsData& password_data);
@@ -80,6 +81,16 @@ class PasswordSyncBridge : public syncer::ModelTypeSyncBridge {
// Retrieves the storage keys of all unsynced passwords in the store.
std::set<FormPrimaryKey> GetUnsyncedPasswordsStorageKeys();
+ // If available, returns cached possibly trimmed PasswordSpecificsData for
+ // given |storage_key|. By default, empty PasswordSpecificsData is returned.
+ const sync_pb::PasswordSpecificsData& GetPossiblyTrimmedPasswordSpecificsData(
+ const std::string& storage_key);
+
+ // Checks whether any password entity on `metadata_map` persists specifics
+ // fields in cache that are supported in the current browser version.
+ bool SyncMetadataCacheContainsSupportedFields(
+ const syncer::EntityMetadataMap& metadata_map) const;
+
// Password store responsible for persistence.
const raw_ptr<PasswordStoreSync> password_store_sync_;
diff --git a/chromium/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/chromium/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
index 35c26e45698..bccd9965396 100644
--- a/chromium/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
+++ b/chromium/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -22,6 +22,7 @@
#include "components/password_manager/core/browser/password_store_sync.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/sync/base/client_tag_hash.h"
+#include "components/sync/base/features.h"
#include "components/sync/model/data_batch.h"
#include "components/sync/model/entity_change.h"
#include "components/sync/model/in_memory_metadata_change_list.h"
@@ -46,8 +47,8 @@ using testing::Eq;
using testing::Invoke;
using testing::NotNull;
using testing::Return;
+using testing::ReturnRef;
using testing::UnorderedElementsAre;
-using testing::UnorderedElementsAreArray;
constexpr char kSignonRealm1[] = "abc";
constexpr char kSignonRealm2[] = "def";
@@ -150,9 +151,10 @@ sync_pb::PasswordSpecifics CreateSpecifics(
password_data->set_username_value(username_value);
password_data->set_password_element(password_element);
password_data->set_signon_realm(signon_realm);
- if (!issue_types.empty())
+ if (!issue_types.empty()) {
*password_data->mutable_password_issues() =
CreateSpecificsIssues(issue_types);
+ }
return password_specifics.password();
}
@@ -396,6 +398,9 @@ class PasswordSyncBridgeTest : public testing::Test {
.WillByDefault(testing::Return(true));
ON_CALL(mock_sync_metadata_store_sync_, ClearModelTypeState)
.WillByDefault(testing::Return(true));
+
+ ON_CALL(mock_processor_, GetPossiblyTrimmedRemoteSpecifics)
+ .WillByDefault(ReturnRef(sync_pb::EntitySpecifics::default_instance()));
}
// Creates an EntityData around a copy of the given specifics.
@@ -884,6 +889,76 @@ TEST_F(PasswordSyncBridgeTest, ShouldRemoveSyncMetadataWhenReadAllLoginsFails) {
}
TEST_F(PasswordSyncBridgeTest,
+ ShouldRemoveSyncMetadataWhenSpecificsCacheContainsSupportedFields) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(
+ syncer::kCacheBaseEntitySpecificsInMetadata);
+
+ ON_CALL(*mock_sync_metadata_store_sync(), GetAllSyncMetadata())
+ .WillByDefault([&]() {
+ // Create entity with a cached supported field.
+ auto metadata_batch = std::make_unique<syncer::MetadataBatch>();
+ sync_pb::PasswordSpecificsData password_data;
+ password_data.set_username_value("username");
+ sync_pb::EntitySpecifics entity_specifics;
+ *entity_specifics.mutable_password()
+ ->mutable_client_only_encrypted_data() = password_data;
+ sync_pb::EntityMetadata entity_metadata;
+ *entity_metadata.mutable_possibly_trimmed_base_specifics() =
+ entity_specifics;
+ auto metadata_ptr =
+ std::make_unique<sync_pb::EntityMetadata>(entity_metadata);
+ metadata_batch->AddMetadata("storage_key", std::move(metadata_ptr));
+ return metadata_batch;
+ });
+
+ EXPECT_CALL(*mock_sync_metadata_store_sync(), DeleteAllSyncMetadata());
+ EXPECT_CALL(mock_processor(), ModelReadyToSync(MetadataBatchContains(
+ /*state=*/syncer::HasNotInitialSyncDone(),
+ /*entities=*/testing::SizeIs(0))));
+
+ auto bridge = std::make_unique<PasswordSyncBridge>(
+ mock_processor().CreateForwardingProcessor(), mock_password_store_sync(),
+ base::DoNothing());
+}
+
+TEST_F(
+ PasswordSyncBridgeTest,
+ ShouldNotRemoveSyncMetadataWhenSpecificsCacheContainsUnsupportedFieldsOnly) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(
+ syncer::kCacheBaseEntitySpecificsInMetadata);
+
+ ON_CALL(*mock_sync_metadata_store_sync(), GetAllSyncMetadata())
+ .WillByDefault([&]() {
+ // Create entity with a cached unsupported field.
+ auto metadata_batch = std::make_unique<syncer::MetadataBatch>();
+ sync_pb::PasswordSpecificsData password_data;
+ *password_data.mutable_unknown_fields() = "unknown";
+ sync_pb::EntitySpecifics entity_specifics;
+ *entity_specifics.mutable_password()
+ ->mutable_client_only_encrypted_data() = password_data;
+ sync_pb::EntityMetadata entity_metadata;
+ *entity_metadata.mutable_possibly_trimmed_base_specifics() =
+ entity_specifics;
+ auto metadata_ptr =
+ std::make_unique<sync_pb::EntityMetadata>(entity_metadata);
+ metadata_batch->AddMetadata("storage_key", std::move(metadata_ptr));
+ return metadata_batch;
+ });
+
+ EXPECT_CALL(mock_processor(), ModelReadyToSync(MetadataBatchContains(
+ /*state=*/syncer::HasNotInitialSyncDone(),
+ /*entities=*/testing::SizeIs(1))));
+ EXPECT_CALL(*mock_sync_metadata_store_sync(), DeleteAllSyncMetadata())
+ .Times(0);
+
+ auto bridge = std::make_unique<PasswordSyncBridge>(
+ mock_processor().CreateForwardingProcessor(), mock_password_store_sync(),
+ base::DoNothing());
+}
+
+TEST_F(PasswordSyncBridgeTest,
ShouldNotRemoveSyncMetadataWhenReadAllLoginsSucceeds) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
diff --git a/chromium/components/password_manager/core/browser/ui/credential_ui_entry.cc b/chromium/components/password_manager/core/browser/ui/credential_ui_entry.cc
new file mode 100644
index 00000000000..209c2c3c809
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/ui/credential_ui_entry.cc
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium 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/password_manager/core/browser/ui/credential_ui_entry.h"
+
+#include "components/password_manager/core/browser/password_list_sorter.h"
+
+namespace password_manager {
+
+CredentialUIEntry::CredentialUIEntry(const PasswordForm& form)
+ : signon_realm(form.signon_realm),
+ url(form.url),
+ affiliated_web_realm(form.affiliated_web_realm),
+ app_display_name(form.app_display_name),
+ username(form.username_value),
+ password(form.password_value),
+ federation_origin(form.federation_origin),
+ password_issues(form.password_issues),
+ blocked_by_user(form.blocked_by_user),
+ key_(CredentialKey(CreateSortKey(form, IgnoreStore(true)))) {
+ // Only one-note with an empty `unique_display_name` is supported in the
+ // settings UI.
+ for (const PasswordNote& n : form.notes) {
+ if (n.unique_display_name.empty()) {
+ note = n;
+ break;
+ }
+ }
+ if (form.IsUsingAccountStore())
+ stored_in.insert(PasswordForm::Store::kAccountStore);
+ if (form.IsUsingProfileStore())
+ stored_in.insert(PasswordForm::Store::kProfileStore);
+}
+CredentialUIEntry::CredentialUIEntry(const CredentialUIEntry& other) = default;
+CredentialUIEntry::CredentialUIEntry(CredentialUIEntry&& other) = default;
+CredentialUIEntry::~CredentialUIEntry() = default;
+
+CredentialUIEntry& CredentialUIEntry::operator=(
+ const CredentialUIEntry& other) = default;
+CredentialUIEntry& CredentialUIEntry::operator=(CredentialUIEntry&& other) =
+ default;
+
+bool CredentialUIEntry::IsLeaked() const {
+ return password_issues.contains(InsecureType::kLeaked);
+}
+
+bool CredentialUIEntry::IsPhished() const {
+ return password_issues.contains(InsecureType::kPhished);
+}
+
+bool operator==(const CredentialUIEntry& lhs, const CredentialUIEntry& rhs) {
+ return lhs.key() == rhs.key();
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/ui/credential_ui_entry.h b/chromium/components/password_manager/core/browser/ui/credential_ui_entry.h
new file mode 100644
index 00000000000..f73f04d90f9
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/ui/credential_ui_entry.h
@@ -0,0 +1,97 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_UI_CREDENTIAL_UI_ENTRY_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_UI_CREDENTIAL_UI_ENTRY_H_
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "components/password_manager/core/browser/password_form.h"
+
+namespace password_manager {
+
+using CredentialKey = base::StrongAlias<class CredentialKeyTag, std::string>;
+
+// Simple struct that represents an entry inside Settings UI. Allows implicit
+// construction from PasswordForm for convenience. A single entry might
+// correspond to multiple PasswordForms.
+struct CredentialUIEntry {
+ struct Less {
+ bool operator()(const CredentialUIEntry& lhs,
+ const CredentialUIEntry& rhs) const {
+ return lhs.key() < rhs.key();
+ }
+ };
+
+ explicit CredentialUIEntry(const PasswordForm& form);
+ CredentialUIEntry(const CredentialUIEntry& other);
+ CredentialUIEntry(CredentialUIEntry&& other);
+ ~CredentialUIEntry();
+
+ CredentialUIEntry& operator=(const CredentialUIEntry& other);
+ CredentialUIEntry& operator=(CredentialUIEntry&& other);
+
+ // The "Realm" for the sign-on. This is scheme, host, port for SCHEME_HTML.
+ // Dialog based forms also contain the HTTP realm. Android based forms will
+ // contain a string of the form "android://<hash of cert>@<package name>"
+ std::string signon_realm;
+
+ // An URL consists of the scheme, host, port and path; the rest is stripped.
+ // This is the primary data used by the PasswordManager to decide (in
+ // longest matching prefix fashion) whether or not a given PasswordForm
+ // result from the database is a good fit for a particular form on a page.
+ GURL url;
+
+ // The web realm affiliated with the Android application, if the it is an
+ // Android credential. Otherwise, the string is empty.
+ std::string affiliated_web_realm;
+
+ // The display name (e.g. Play Store name) of the Android application if
+ // it is an Android credential. Otherwise, the string is empty.
+ std::string app_display_name;
+
+ // The current password.
+ std::u16string username;
+
+ // The current password.
+ std::u16string password;
+
+ // The origin of identity provider used for federated login.
+ url::Origin federation_origin;
+
+ // Indicates the stores where the credential is stored.
+ base::flat_set<PasswordForm::Store> stored_in;
+
+ // A mapping from the credential insecurity type (e.g. leaked, phished),
+ // to its metadata (e.g. time it was discovered, whether alerts are muted).
+ base::flat_map<InsecureType, InsecurityMetadata> password_issues;
+
+ // Attached note to the credential. This is a single entry since settings UI
+ // currently supports manipulation of one note only with an empty
+ // `unique_display_name`. The storage layer however supports multiple-notes
+ // for forward compatibility.
+ PasswordNote note;
+
+ // Tracks if the user opted to never remember passwords for this website.
+ bool blocked_by_user;
+
+ const CredentialKey& key() const { return key_; }
+
+ // Information about password insecurities.
+ bool IsLeaked() const;
+ bool IsPhished() const;
+
+ private:
+ // Key which is constructed from an original PasswordForm.
+ CredentialKey key_;
+};
+
+bool operator==(const CredentialUIEntry& lhs, const CredentialUIEntry& rhs);
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_UI_CREDENTIAL_UI_ENTRY_H_
diff --git a/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.cc b/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
index e5f2b57f638..239aff30038 100644
--- a/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
+++ b/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
@@ -23,6 +23,7 @@
#include "components/password_manager/core/browser/insecure_credentials_table.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_list_sorter.h"
+#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
#include "components/password_manager/core/browser/ui/credential_utils.h"
#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
#include "components/password_manager/core/common/password_manager_features.h"
@@ -91,6 +92,11 @@ bool IsPasswordFormPhished(const PasswordForm& form) {
form.password_issues.end();
}
+bool SupportsMuteOperation(InsecureType insecure_type) {
+ return (insecure_type == InsecureType::kLeaked ||
+ insecure_type == InsecureType::kPhished);
+}
+
// This function takes two lists: weak passwords and saved passwords and joins
// them, producing a map that contains CredentialWithPassword as keys and
// vector<PasswordForm> as values.
@@ -124,7 +130,8 @@ CredentialPasswordsMap GetInsecureCredentialsFromPasswords(
credential_to_form.type |= ConvertInsecureType(pair.first);
credential_to_form.latest_time =
std::max(credential_to_form.latest_time, pair.second.create_time);
- credential_to_form.is_muted = pair.second.is_muted;
+ if (SupportsMuteOperation(pair.first))
+ credential_to_form.is_muted = pair.second.is_muted;
}
// Populate the map. The values are vectors, because it is
// possible that multiple saved passwords match to the same
@@ -273,66 +280,27 @@ void InsecureCredentialsManager::SaveInsecureCredential(
}
bool InsecureCredentialsManager::MuteCredential(
- const CredentialView& credential) {
- auto it = credentials_to_forms_.find(credential);
- if (it == credentials_to_forms_.end())
- return false;
-
- // Mute all matching compromised credentials from the store.
- // For a match, all insecureity types saved in the store are muted.
- // Return whether any credentials were muted.
- const auto& saved_passwords = it->second.forms;
- bool muted = false;
- for (const PasswordForm& saved_password : saved_passwords) {
- PasswordForm form_to_update = saved_password;
- bool form_changed = false;
- for (const auto& password_issue : saved_password.password_issues) {
- if (!password_issue.second.is_muted.value()) {
- form_to_update.password_issues.insert_or_assign(
- password_issue.first,
- InsecurityMetadata(password_issue.second.create_time,
- IsMuted(true)));
- form_changed = true;
- }
- }
- if (form_changed) {
- GetStoreFor(saved_password).UpdateLogin(form_to_update);
- muted = true;
+ const CredentialUIEntry& credential) {
+ CredentialUIEntry updated_credential = credential;
+ for (auto& password_issue : updated_credential.password_issues) {
+ if (!password_issue.second.is_muted.value() &&
+ SupportsMuteOperation(password_issue.first)) {
+ password_issue.second.is_muted = IsMuted(true);
}
}
- return muted;
+ return presenter_->EditSavedCredentials(updated_credential);
}
bool InsecureCredentialsManager::UnmuteCredential(
- const CredentialView& credential) {
- auto it = credentials_to_forms_.find(credential);
- if (it == credentials_to_forms_.end())
- return false;
-
- // Unmute all matching compromised credentials from the store.
- // For a match, all insecureity types saved in the store are unmuted.
- // Return whether any credentials were unmuted.
- const auto& saved_passwords = it->second.forms;
- bool unmuted = false;
-
- for (const PasswordForm& saved_password : saved_passwords) {
- PasswordForm form_to_update = saved_password;
- bool form_changed = false;
- for (const auto& password_issue : saved_password.password_issues) {
- if (password_issue.second.is_muted.value()) {
- form_to_update.password_issues.insert_or_assign(
- password_issue.first,
- InsecurityMetadata(password_issue.second.create_time,
- IsMuted(false)));
- form_changed = true;
- }
- }
- if (form_changed) {
- GetStoreFor(saved_password).UpdateLogin(form_to_update);
- unmuted = true;
+ const CredentialUIEntry& credential) {
+ CredentialUIEntry updated_credential = credential;
+ for (auto& password_issue : updated_credential.password_issues) {
+ if (password_issue.second.is_muted.value() &&
+ SupportsMuteOperation(password_issue.first)) {
+ password_issue.second.is_muted = IsMuted(false);
}
}
- return unmuted;
+ return presenter_->EditSavedCredentials(updated_credential);
}
bool InsecureCredentialsManager::UpdateCredential(
@@ -376,6 +344,20 @@ InsecureCredentialsManager::GetInsecureCredentials() const {
return ExtractInsecureCredentials(credentials_to_forms_, &IsInsecure);
}
+std::vector<CredentialUIEntry>
+InsecureCredentialsManager::GetInsecureCredentialEntries() const {
+ DCHECK(presenter_);
+ std::vector<CredentialUIEntry> credentials =
+ presenter_->GetSavedCredentials();
+ // Erase entries which aren't leaked and finished.
+ base::EraseIf(credentials, [](const auto& credential) {
+ return !credential.password_issues.contains(InsecureType::kLeaked) &&
+ !credential.password_issues.contains(InsecureType::kPhished);
+ });
+
+ return credentials;
+}
+
std::vector<CredentialWithPassword>
InsecureCredentialsManager::GetWeakCredentials() const {
std::vector<CredentialWithPassword> weak_credentials =
@@ -389,6 +371,17 @@ InsecureCredentialsManager::GetWeakCredentials() const {
return weak_credentials;
}
+std::vector<CredentialUIEntry>
+InsecureCredentialsManager::GetWeakCredentialEntries() const {
+ DCHECK(presenter_);
+ std::vector<CredentialUIEntry> credentials =
+ presenter_->GetSavedCredentials();
+ base::EraseIf(credentials, [this](const auto& credential) {
+ return !weak_passwords_.contains(credential.password);
+ });
+ return credentials;
+}
+
SavedPasswordsPresenter::SavedPasswordsView
InsecureCredentialsManager::GetSavedPasswordsFor(
const CredentialView& credential) const {
diff --git a/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.h b/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.h
index 8766bd5fd29..2ccd09cfbd1 100644
--- a/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.h
+++ b/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager.h
@@ -30,6 +30,7 @@
namespace password_manager {
+struct CredentialUIEntry;
class LeakCheckCredential;
enum class InsecureCredentialTypeFlags {
@@ -65,12 +66,13 @@ constexpr InsecureCredentialTypeFlags& operator|=(
return lhs;
}
-// Unsets the bit responsible for the weak credential in the |flag|.
-constexpr InsecureCredentialTypeFlags UnsetWeakCredentialTypeFlag(
+// Unsets the bits responsible for the reused and weak credential in the |flag|.
+constexpr InsecureCredentialTypeFlags UnsetWeakAndReusedCredentialTypeFlags(
InsecureCredentialTypeFlags flag) {
return static_cast<InsecureCredentialTypeFlags>(
static_cast<int>(flag) &
- ~(static_cast<int>(InsecureCredentialTypeFlags::kWeakCredential)));
+ ~(static_cast<int>(InsecureCredentialTypeFlags::kWeakCredential |
+ InsecureCredentialTypeFlags::kReusedCredential)));
}
// Checks that |flag| contains at least one of insecure types.
@@ -187,17 +189,21 @@ class InsecureCredentialsManager : public SavedPasswordsPresenter::Observer {
// Attempts to mute |credential| from the password store.
// Returns whether the mute succeeded.
- bool MuteCredential(const CredentialView& credential);
+ bool MuteCredential(const CredentialUIEntry& credential);
// Attempts to unmute |credential| from the password store.
// Returns whether the unmute succeeded.
- bool UnmuteCredential(const CredentialView& credential);
+ bool UnmuteCredential(const CredentialUIEntry& credential);
// Returns a vector of currently insecure credentials.
+ // TODO(crbug.com/1330549): Use CredentialUIEntry only.
std::vector<CredentialWithPassword> GetInsecureCredentials() const;
+ std::vector<CredentialUIEntry> GetInsecureCredentialEntries() const;
// Returns a vector of currently weak credentials.
+ // TODO(crbug.com/1330549): Use CredentialUIEntry only.
std::vector<CredentialWithPassword> GetWeakCredentials() const;
+ std::vector<CredentialUIEntry> GetWeakCredentialEntries() const;
// Returns password forms which map to provided insecure credential.
// In most of the cases vector will have 1 element only.
diff --git a/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc b/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
index 177bbb43d93..ddbabe542d9 100644
--- a/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
@@ -15,6 +15,7 @@
#include "components/password_manager/core/browser/insecure_credentials_table.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -73,6 +74,7 @@ PasswordForm MakeSavedPassword(base::StringPiece signon_realm,
form.username_value = std::u16string(username);
form.password_value = std::u16string(password);
form.username_element = std::u16string(username_element);
+ form.in_store = PasswordForm::Store::kProfileStore;
return form;
}
@@ -703,14 +705,16 @@ TEST_F(InsecureCredentialsManagerTest, MuteCompromisedCredential) {
store().AddLogin(password);
RunUntilIdle();
- CredentialWithPassword expected = MakeCompromisedCredential(password);
-
- EXPECT_THAT(provider().GetInsecureCredentials(), ElementsAre(expected));
- EXPECT_FALSE(provider().GetInsecureCredentials()[0].is_muted);
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password)));
- EXPECT_TRUE(provider().MuteCredential(expected));
+ EXPECT_TRUE(provider().MuteCredential(CredentialUIEntry(password)));
RunUntilIdle();
- EXPECT_TRUE(provider().GetInsecureCredentials()[0].is_muted);
+
+ EXPECT_TRUE(provider()
+ .GetInsecureCredentialEntries()[0]
+ .password_issues.at(InsecureType::kLeaked)
+ .is_muted.value());
EXPECT_TRUE(store()
.stored_passwords()
.at(kExampleCom)
@@ -728,15 +732,15 @@ TEST_F(InsecureCredentialsManagerTest, UnmuteCompromisedMutedCredential) {
store().AddLogin(password);
RunUntilIdle();
- CredentialWithPassword expected = MakeCompromisedCredential(
- password, InsecureCredentialTypeFlags::kCredentialLeaked, true);
-
- EXPECT_THAT(provider().GetInsecureCredentials(), ElementsAre(expected));
- EXPECT_TRUE(provider().GetInsecureCredentials()[0].is_muted);
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password)));
- EXPECT_TRUE(provider().UnmuteCredential(expected));
+ EXPECT_TRUE(provider().UnmuteCredential(CredentialUIEntry(password)));
RunUntilIdle();
- EXPECT_FALSE(provider().GetInsecureCredentials()[0].is_muted);
+ EXPECT_FALSE(provider()
+ .GetInsecureCredentialEntries()[0]
+ .password_issues.at(InsecureType::kLeaked)
+ .is_muted.value());
EXPECT_FALSE(store()
.stored_passwords()
.at(kExampleCom)
@@ -755,15 +759,15 @@ TEST_F(InsecureCredentialsManagerTest, UnmuteCompromisedNotMutedCredential) {
store().AddLogin(password);
RunUntilIdle();
- CredentialWithPassword expected = MakeCompromisedCredential(
- password, InsecureCredentialTypeFlags::kCredentialLeaked, false);
-
- EXPECT_THAT(provider().GetInsecureCredentials(), ElementsAre(expected));
- EXPECT_FALSE(provider().GetInsecureCredentials()[0].is_muted);
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password)));
- EXPECT_FALSE(provider().UnmuteCredential(expected));
+ EXPECT_FALSE(provider().UnmuteCredential(CredentialUIEntry(password)));
RunUntilIdle();
- EXPECT_FALSE(provider().GetInsecureCredentials()[0].is_muted);
+ EXPECT_FALSE(provider()
+ .GetInsecureCredentialEntries()[0]
+ .password_issues.at(InsecureType::kLeaked)
+ .is_muted.value());
EXPECT_FALSE(store()
.stored_passwords()
.at(kExampleCom)
@@ -785,19 +789,61 @@ TEST_F(InsecureCredentialsManagerTest,
store().AddLogin(password);
RunUntilIdle();
- CredentialWithPassword expected = MakeCompromisedCredential(
- password,
- InsecureCredentialTypeFlags::kCredentialLeaked |
- InsecureCredentialTypeFlags::kCredentialPhished,
- true);
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password)));
- EXPECT_THAT(provider().GetInsecureCredentials(), ElementsAre(expected));
+ EXPECT_TRUE(provider().UnmuteCredential(CredentialUIEntry(password)));
+ RunUntilIdle();
+ EXPECT_FALSE(provider()
+ .GetInsecureCredentialEntries()[0]
+ .password_issues.at(InsecureType::kLeaked)
+ .is_muted.value());
+ EXPECT_FALSE(provider()
+ .GetInsecureCredentialEntries()[0]
+ .password_issues.at(InsecureType::kPhished)
+ .is_muted.value());
+ EXPECT_FALSE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kLeaked)
+ .is_muted.value());
+ EXPECT_FALSE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kPhished)
+ .is_muted.value());
+}
+
+TEST_F(InsecureCredentialsManagerTest,
+ FilterThenUnmuteMultipleInsecurityTypes) {
+ PasswordForm password =
+ MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
+ password.password_issues.insert(
+ {InsecureType::kLeaked, InsecurityMetadata(base::Time(), IsMuted(true))});
+ password.password_issues.insert(
+ {InsecureType::kPhished,
+ InsecurityMetadata(base::Time(), IsMuted(true))});
+ password.password_issues.insert(
+ {InsecureType::kReused, InsecurityMetadata(base::Time(), IsMuted(true))});
+ password.password_issues.insert(
+ {InsecureType::kWeak, InsecurityMetadata(base::Time(), IsMuted(true))});
+
+ store().AddLogin(password);
+ RunUntilIdle();
- EXPECT_TRUE(provider().GetInsecureCredentials()[0].is_muted);
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password)));
- EXPECT_TRUE(provider().UnmuteCredential(expected));
+ EXPECT_TRUE(provider().UnmuteCredential(CredentialUIEntry(password)));
RunUntilIdle();
- EXPECT_FALSE(provider().GetInsecureCredentials()[0].is_muted);
+
+ PasswordForm expected = password;
+ expected.password_issues[InsecureType::kLeaked].is_muted = IsMuted(false);
+ expected.password_issues[InsecureType::kPhished].is_muted = IsMuted(false);
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(expected)));
EXPECT_FALSE(store()
.stored_passwords()
.at(kExampleCom)
@@ -810,6 +856,18 @@ TEST_F(InsecureCredentialsManagerTest,
.back()
.password_issues.at(InsecureType::kPhished)
.is_muted.value());
+ EXPECT_TRUE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kReused)
+ .is_muted.value());
+ EXPECT_TRUE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kWeak)
+ .is_muted.value());
}
TEST_F(InsecureCredentialsManagerTest, MuteCompromisedCredentialOnMutedIsNoOp) {
@@ -821,15 +879,13 @@ TEST_F(InsecureCredentialsManagerTest, MuteCompromisedCredentialOnMutedIsNoOp) {
store().AddLogin(password);
RunUntilIdle();
- CredentialWithPassword expected = MakeCompromisedCredential(
- password, InsecureCredentialTypeFlags::kCredentialLeaked, true);
-
- EXPECT_THAT(provider().GetInsecureCredentials(), ElementsAre(expected));
- EXPECT_TRUE(provider().GetInsecureCredentials()[0].is_muted);
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password)));
- EXPECT_FALSE(provider().MuteCredential(expected));
+ EXPECT_FALSE(provider().MuteCredential(CredentialUIEntry(password)));
RunUntilIdle();
- EXPECT_TRUE(provider().GetInsecureCredentials()[0].is_muted);
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password)));
EXPECT_TRUE(store()
.stored_passwords()
.at(kExampleCom)
@@ -852,19 +908,60 @@ TEST_F(InsecureCredentialsManagerTest,
store().AddLogin(password);
RunUntilIdle();
- CredentialWithPassword expected = MakeCompromisedCredential(
- password,
- InsecureCredentialTypeFlags::kCredentialLeaked |
- InsecureCredentialTypeFlags::kCredentialPhished,
- false);
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password)));
- EXPECT_THAT(provider().GetInsecureCredentials(), ElementsAre(expected));
+ EXPECT_TRUE(provider().MuteCredential(CredentialUIEntry(password)));
+ RunUntilIdle();
+ PasswordForm expected = password;
+ expected.password_issues[InsecureType::kLeaked].is_muted = IsMuted(true);
+ expected.password_issues[InsecureType::kPhished].is_muted = IsMuted(true);
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(expected)));
+ EXPECT_TRUE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kLeaked)
+ .is_muted.value());
+ EXPECT_TRUE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kPhished)
+ .is_muted.value());
+}
- EXPECT_FALSE(provider().GetInsecureCredentials()[0].is_muted);
+TEST_F(InsecureCredentialsManagerTest, FilterThenMuteMultipleInsecurityTypes) {
+ PasswordForm password =
+ MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
+ password.password_issues.insert(
+ {InsecureType::kLeaked,
+ InsecurityMetadata(base::Time(), IsMuted(false))});
+ password.password_issues.insert(
+ {InsecureType::kPhished,
+ InsecurityMetadata(base::Time(), IsMuted(false))});
+ password.password_issues.insert(
+ {InsecureType::kReused,
+ InsecurityMetadata(base::Time(), IsMuted(false))});
+ password.password_issues.insert(
+ {InsecureType::kWeak, InsecurityMetadata(base::Time(), IsMuted(false))});
- EXPECT_TRUE(provider().MuteCredential(expected));
+ store().AddLogin(password);
RunUntilIdle();
- EXPECT_TRUE(provider().GetInsecureCredentials()[0].is_muted);
+
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password)));
+
+ EXPECT_TRUE(provider().MuteCredential(CredentialUIEntry(password)));
+
+ RunUntilIdle();
+
+ PasswordForm expected = password;
+ expected.password_issues[InsecureType::kLeaked].is_muted = IsMuted(true);
+ expected.password_issues[InsecureType::kPhished].is_muted = IsMuted(true);
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(expected)));
EXPECT_TRUE(store()
.stored_passwords()
.at(kExampleCom)
@@ -877,6 +974,120 @@ TEST_F(InsecureCredentialsManagerTest,
.back()
.password_issues.at(InsecureType::kPhished)
.is_muted.value());
+ EXPECT_FALSE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kReused)
+ .is_muted.value());
+ EXPECT_FALSE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kWeak)
+ .is_muted.value());
+}
+
+TEST_F(InsecureCredentialsManagerTest, MuteWeakPasswordNoOp) {
+ PasswordForm password =
+ MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
+
+ password.password_issues.insert(
+ {InsecureType::kWeak, InsecurityMetadata(base::Time(), IsMuted(false))});
+
+ store().AddLogin(password);
+ RunUntilIdle();
+
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(), IsEmpty());
+
+ EXPECT_FALSE(provider().MuteCredential(CredentialUIEntry(password)));
+
+ RunUntilIdle();
+
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(), IsEmpty());
+ EXPECT_FALSE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kWeak)
+ .is_muted.value());
+}
+
+TEST_F(InsecureCredentialsManagerTest, UnMuteWeakPasswordNoOp) {
+ PasswordForm password =
+ MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
+
+ password.password_issues.insert(
+ {InsecureType::kWeak, InsecurityMetadata(base::Time(), IsMuted(true))});
+
+ store().AddLogin(password);
+ RunUntilIdle();
+
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(), IsEmpty());
+
+ EXPECT_FALSE(provider().UnmuteCredential(CredentialUIEntry(password)));
+
+ RunUntilIdle();
+
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(), IsEmpty());
+
+ EXPECT_TRUE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kWeak)
+ .is_muted.value());
+}
+
+TEST_F(InsecureCredentialsManagerTest, MuteReusedPasswordNoOp) {
+ PasswordForm password =
+ MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
+
+ password.password_issues.insert(
+ {InsecureType::kReused,
+ InsecurityMetadata(base::Time(), IsMuted(false))});
+
+ store().AddLogin(password);
+ RunUntilIdle();
+
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(), IsEmpty());
+
+ EXPECT_FALSE(provider().MuteCredential(CredentialUIEntry(password)));
+
+ RunUntilIdle();
+
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(), IsEmpty());
+ EXPECT_FALSE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kReused)
+ .is_muted.value());
+}
+
+TEST_F(InsecureCredentialsManagerTest, UnMuteReusedPasswordNoOp) {
+ PasswordForm password =
+ MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
+
+ password.password_issues.insert(
+ {InsecureType::kReused, InsecurityMetadata(base::Time(), IsMuted(true))});
+
+ store().AddLogin(password);
+ RunUntilIdle();
+
+ ASSERT_THAT(provider().GetInsecureCredentialEntries(), IsEmpty());
+
+ EXPECT_FALSE(provider().UnmuteCredential(CredentialUIEntry(password)));
+
+ RunUntilIdle();
+
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(), IsEmpty());
+ EXPECT_TRUE(store()
+ .stored_passwords()
+ .at(kExampleCom)
+ .back()
+ .password_issues.at(InsecureType::kReused)
+ .is_muted.value());
}
// Test verifies that editing Compromised Credential via provider change the
@@ -1053,8 +1264,66 @@ TEST_F(InsecureCredentialsManagerTest, GetWeakCredentialsReturnsSortedData) {
MakeWeakCredential(password_forms[2]),
MakeWeakCredential(password_forms[3])));
}
+
+// Verifues that GetWeakCredentials() returns sorted weak credentials by using
+// CreateSortKey.
+TEST_F(InsecureCredentialsManagerTest, GetWeakCredentialEntries) {
+ const std::vector<PasswordForm> password_forms = {
+ MakeSavedPassword("http://example-a.com", u"user_a1", u"pwd"),
+ MakeSavedPassword("http://example-a.com", u"user_a2", u"pwd")};
+ store().AddLogin(password_forms[0]);
+ store().AddLogin(password_forms[1]);
+ RunUntilIdle();
+
+ provider().StartWeakCheck();
+ RunUntilIdle();
+
+ EXPECT_THAT(provider().GetWeakCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password_forms[0]),
+ CredentialUIEntry(password_forms[1])));
+}
#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+TEST_F(InsecureCredentialsManagerTest, GetInsecureCredentialsFiltersWeak) {
+ PasswordForm password1 =
+ MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
+ PasswordForm password2 =
+ MakeSavedPassword(kExampleCom, kUsername2, kPassword216);
+
+ password1.password_issues.insert(
+ {InsecureType::kLeaked, InsecurityMetadata()});
+ password2.password_issues.insert({InsecureType::kWeak, InsecurityMetadata()});
+
+ store().AddLogin(password1);
+ store().AddLogin(password2);
+
+ RunUntilIdle();
+
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password1)));
+}
+
+TEST_F(InsecureCredentialsManagerTest,
+ GetInsecureCredentialsFiltersDuplicates) {
+ PasswordForm password1 =
+ MakeSavedPassword(kExampleCom, kUsername1, kPassword1);
+ PasswordForm password2 = MakeSavedPassword(kExampleCom, kUsername1,
+ kPassword1, u"username_element");
+
+ password1.password_issues.insert(
+ {InsecureType::kLeaked, InsecurityMetadata()});
+ password2.password_issues.insert(
+ {InsecureType::kLeaked, InsecurityMetadata()});
+
+ store().AddLogin(password1);
+ store().AddLogin(password2);
+
+ RunUntilIdle();
+
+ EXPECT_THAT(provider().GetInsecureCredentialEntries(),
+ ElementsAre(CredentialUIEntry(password1)));
+}
+
namespace {
class InsecureCredentialsManagerWithTwoStoresTest : public ::testing::Test {
protected:
@@ -1249,6 +1518,26 @@ TEST_F(InsecureCredentialsManagerWithTwoStoresTest, RemoveWeakCredential) {
EXPECT_THAT(profile_store().stored_passwords().at(kExampleCom), IsEmpty());
EXPECT_THAT(account_store().stored_passwords().at(kExampleCom), IsEmpty());
}
+
+TEST_F(InsecureCredentialsManagerWithTwoStoresTest,
+ GetInsecureCredentialsWeak) {
+ profile_store().AddLogin(
+ MakeSavedPassword(kExampleCom, kUsername1, kWeakPassword1));
+ account_store().AddLogin(
+ MakeSavedPassword(kExampleCom, kUsername1, kWeakPassword1));
+ RunUntilIdle();
+ provider().StartWeakCheck();
+ RunUntilIdle();
+
+ PasswordForm expected_form =
+ MakeSavedPassword(kExampleCom, kUsername1, kWeakPassword1);
+ expected_form.in_store =
+ PasswordForm::Store::kProfileStore | PasswordForm::Store::kAccountStore;
+
+ EXPECT_THAT(provider().GetWeakCredentialEntries(),
+ ElementsAre(CredentialUIEntry(expected_form)));
+}
+
#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.cc b/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.cc
index 4994bc3086e..51de29a4dc3 100644
--- a/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.cc
+++ b/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.cc
@@ -11,18 +11,21 @@
#include "base/check.h"
#include "base/containers/cxx20_erase.h"
+#include "base/notreached.h"
#include "base/observer_list.h"
#include "base/ranges/algorithm.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_list_sorter.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
+#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
#include "url/gurl.h"
namespace {
using password_manager::metrics_util::IsPasswordChanged;
using password_manager::metrics_util::IsPasswordNoteChanged;
using password_manager::metrics_util::IsUsernameChanged;
+using password_manager::metrics_util::PasswordNoteAction;
using PasswordNote = password_manager::PasswordNote;
using Store = password_manager::PasswordForm::Store;
using SavedPasswordsView =
@@ -45,12 +48,33 @@ bool IsUsernameAlreadyUsed(SavedPasswordsView all_forms,
return base::ranges::any_of(all_forms, has_conflicting_username);
}
-// Returns trues if there is at least one password store that contains both
-// passwords.
-constexpr bool ShareSameStore(const password_manager::PasswordForm& lhs,
- const password_manager::PasswordForm& rhs) {
- return (lhs.in_store & rhs.in_store) != Store::kNotSet;
+password_manager::PasswordForm GenerateFormFromCredential(
+ password_manager::CredentialUIEntry credential) {
+ password_manager::PasswordForm form;
+ form.url = credential.url;
+ form.signon_realm = credential.signon_realm;
+ form.username_value = credential.username;
+ form.password_value = credential.password;
+ if (!credential.note.value.empty())
+ form.notes = {credential.note};
+
+ DCHECK(!credential.stored_in.empty());
+ form.in_store = *credential.stored_in.begin();
+ return form;
}
+
+PasswordNoteAction CalculatePasswordNoteAction(bool old_note_empty,
+ bool new_note_empty) {
+ if (old_note_empty && !new_note_empty)
+ return PasswordNoteAction::kNoteAddedInEditDialog;
+ if (!old_note_empty && new_note_empty)
+ return PasswordNoteAction::kNoteRemovedInEditDialog;
+ if (!old_note_empty && !new_note_empty)
+ return PasswordNoteAction::kNoteEditedInEditDialog;
+ NOTREACHED();
+ return PasswordNoteAction::kNoteEditedInEditDialog;
+}
+
} // namespace
namespace password_manager {
@@ -82,46 +106,69 @@ void SavedPasswordsPresenter::Init() {
}
void SavedPasswordsPresenter::RemovePassword(const PasswordForm& form) {
- std::string current_form_key = CreateSortKey(form, IgnoreStore(true));
- const auto range = sort_key_to_password_forms_.equal_range(current_form_key);
+ RemoveCredential(CredentialUIEntry(form));
+}
+
+bool SavedPasswordsPresenter::RemoveCredential(
+ const CredentialUIEntry& credential) {
+ const auto range =
+ sort_key_to_password_forms_.equal_range(credential.key().value());
+ bool removed = false;
std::for_each(range.first, range.second, [&](const auto& pair) {
const auto& current_form = pair.second;
// Make sure |form| and |current_form| share the same store.
- if (ShareSameStore(form, current_form)) {
+ if (credential.stored_in.contains(current_form.in_store)) {
// |current_form| is unchanged result obtained from
// 'OnGetPasswordStoreResultsFrom'. So it can be present only in one store
// at a time..
GetStoreFor(current_form).RemoveLogin(current_form);
+ removed = true;
}
});
+ return removed;
}
bool SavedPasswordsPresenter::AddPassword(const PasswordForm& form) {
- if (!password_manager_util::IsValidPasswordURL(form.url))
+ return AddCredential(CredentialUIEntry(form));
+}
+
+bool SavedPasswordsPresenter::AddCredential(
+ const CredentialUIEntry& credential) {
+ if (!password_manager_util::IsValidPasswordURL(credential.url))
return false;
- if (form.password_value.empty())
+ if (credential.password.empty())
return false;
- auto have_equal_username_and_realm = [&form](const PasswordForm& entry) {
- return form.signon_realm == entry.signon_realm &&
- form.username_value == entry.username_value;
- };
+ auto have_equal_username_and_realm =
+ [&credential](const PasswordForm& entry) {
+ return credential.signon_realm == entry.signon_realm &&
+ credential.username == entry.username_value;
+ };
if (base::ranges::any_of(passwords_, have_equal_username_and_realm))
return false;
// Try to unblocklist in both stores anyway because if credentials don't
// exist, the unblocklist operation is no-op.
- auto form_digest = PasswordFormDigest(PasswordForm::Scheme::kHtml,
- form.signon_realm, form.url);
+ auto form_digest = PasswordFormDigest(
+ PasswordForm::Scheme::kHtml, credential.signon_realm, credential.url);
profile_store_->Unblocklist(form_digest);
if (account_store_)
account_store_->Unblocklist(form_digest);
+ PasswordForm form = GenerateFormFromCredential(credential);
+ form.type = password_manager::PasswordForm::Type::kManuallyAdded;
+ form.date_created = base::Time::Now();
+ form.date_password_modified = base::Time::Now();
+
GetStoreFor(form).AddLogin(form);
metrics_util::LogUserInteractionsWhenAddingCredentialFromSettings(
metrics_util::AddCredentialFromSettingsUserInteractions::
kCredentialAdded);
+ if (!form.notes.empty() && form.notes[0].value.length() > 0) {
+ metrics_util::LogPasswordNoteActionInSettings(
+ PasswordNoteAction::kNoteAddedInAddDialog);
+ }
return true;
}
@@ -161,6 +208,39 @@ bool SavedPasswordsPresenter::EditSavedPasswords(
return EditSavedPasswords(forms_to_change, new_username, new_password);
}
+bool SavedPasswordsPresenter::EditSavedCredentials(
+ const CredentialUIEntry& credential) {
+ const auto range =
+ sort_key_to_password_forms_.equal_range(credential.key().value());
+ std::vector<PasswordForm> forms_to_change;
+ base::ranges::transform(range.first, range.second,
+ std::back_inserter(forms_to_change),
+ [](const auto& pair) { return pair.second; });
+
+ if (forms_to_change.empty())
+ return false;
+
+ const auto& old_note_itr =
+ base::ranges::find_if(forms_to_change[0].notes, &std::u16string::empty,
+ &PasswordNote::unique_display_name);
+
+ // TODO(crbug.com/1184691): Merge into a single method.
+ if (credential.username != forms_to_change[0].username_value ||
+ credential.password != forms_to_change[0].password_value ||
+ (old_note_itr != forms_to_change[0].notes.end() &&
+ credential.note != *old_note_itr)) {
+ return EditSavedPasswords(forms_to_change, credential.username,
+ credential.password, credential.note.value);
+ } else if (credential.password_issues != forms_to_change[0].password_issues) {
+ for (auto& old_form : forms_to_change) {
+ old_form.password_issues = credential.password_issues;
+ GetStoreFor(old_form).UpdateLogin(old_form);
+ }
+ return true;
+ }
+ return false;
+}
+
bool SavedPasswordsPresenter::EditSavedPasswords(
const SavedPasswordsView forms,
const std::u16string& new_username,
@@ -170,14 +250,21 @@ bool SavedPasswordsPresenter::EditSavedPasswords(
return false;
IsUsernameChanged username_changed(new_username != forms[0].username_value);
IsPasswordChanged password_changed(new_password != forms[0].password_value);
- IsPasswordNoteChanged note_changed =
- IsPasswordNoteChanged(forms[0].note.value != new_note);
+
+ const auto& old_note_itr =
+ base::ranges::find_if(forms[0].notes, &std::u16string::empty,
+ &PasswordNote::unique_display_name);
+ bool old_note_exists = old_note_itr != forms[0].notes.end();
+ IsPasswordNoteChanged note_changed = IsPasswordNoteChanged(
+ (old_note_exists && old_note_itr->value != new_note) ||
+ (!old_note_exists && !new_note.empty()));
if (new_password.empty())
return false;
if (username_changed &&
- IsUsernameAlreadyUsed(passwords_, forms, new_username))
+ IsUsernameAlreadyUsed(passwords_, forms, new_username)) {
return false;
+ }
// An updated username implies a change in the primary key, thus we need to
// make sure to call the right API. Update every entry in the equivalence
@@ -186,22 +273,41 @@ bool SavedPasswordsPresenter::EditSavedPasswords(
for (const auto& old_form : forms) {
PasswordStoreInterface& store = GetStoreFor(old_form);
PasswordForm new_form = old_form;
- new_form.username_value = new_username;
- new_form.password_value = new_password;
- new_form.password_issues.clear();
- if (password_changed)
+ if (password_changed) {
+ new_form.password_value = new_password;
new_form.date_password_modified = base::Time::Now();
+ new_form.password_issues.clear();
+ }
if (note_changed) {
- // if the old note is empty, the note is just created.
- if (old_form.note.value.empty()) {
- new_form.note.date_created = base::Time::Now();
+ bool old_note_empty = false;
+ // if the old note doesn't exist, the note is just created.
+ const auto& note_itr =
+ base::ranges::find_if(new_form.notes, &std::u16string::empty,
+ &PasswordNote::unique_display_name);
+ if (note_itr == new_form.notes.end()) {
+ new_form.notes.emplace_back(new_note,
+ /*date_created=*/base::Time::Now());
+ old_note_empty = true;
+ } else {
+ if (note_itr->value.empty()) {
+ note_itr->date_created = base::Time::Now();
+ old_note_empty = true;
+ }
+ note_itr->value = new_note;
}
- new_form.note.value = new_note;
+
+ metrics_util::LogPasswordNoteActionInSettings(
+ CalculatePasswordNoteAction(old_note_empty, new_note.empty()));
}
if (username_changed) {
+ new_form.username_value = new_username;
+ // Phished and leaked issues are no longer relevant on username change.
+ // Weak and reused issues are still relevant.
+ new_form.password_issues.erase(InsecureType::kPhished);
+ new_form.password_issues.erase(InsecureType::kLeaked);
// Changing username requires deleting old form and adding new one. So
// the different API should be called.
store.UpdateLoginWithPrimaryKey(new_form, old_form);
@@ -242,6 +348,17 @@ std::vector<PasswordForm> SavedPasswordsPresenter::GetUniquePasswordForms()
return forms;
}
+std::vector<CredentialUIEntry> SavedPasswordsPresenter::GetSavedCredentials()
+ const {
+ std::vector<PasswordForm> forms = GetUniquePasswordForms();
+ std::vector<CredentialUIEntry> credentials;
+ credentials.reserve(forms.size());
+ base::ranges::transform(
+ forms, std::back_inserter(credentials),
+ [](const PasswordForm& form) { return CredentialUIEntry(form); });
+ return credentials;
+}
+
std::vector<std::u16string> SavedPasswordsPresenter::GetUsernamesForRealm(
const std::string& signon_realm,
bool is_using_account_store) {
diff --git a/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.h b/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.h
index 85cb74f5546..134906dea1b 100644
--- a/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.h
+++ b/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter.h
@@ -19,6 +19,7 @@
namespace password_manager {
struct PasswordForm;
+struct CredentialUIEntry;
// This interface provides a way for clients to obtain a list of all saved
// passwords and register themselves as observers for changes. In contrast to
@@ -66,25 +67,31 @@ class SavedPasswordsPresenter : public PasswordStoreInterface::Observer,
void Init();
// Removes the credential and all its duplicates from the store.
+ // TODO(crbug.com/1330906): Remove in favor of EditSavedCredentials.
void RemovePassword(const PasswordForm& form);
+ bool RemoveCredential(const CredentialUIEntry& credential);
// Adds the credential to the store specified in the |form|. Returns true
// if the password was added, false if |form|'s data is not valid (invalid
// url/empty password), or an entry with such signon_realm and username
// already exists in any (profile or account) store.
+ // TODO(crbug.com/1330906): Remove in favor of EditSavedCredentials.
bool AddPassword(const PasswordForm& form);
+ bool AddCredential(const CredentialUIEntry& credential);
// Tries to edit |password|. After checking whether |form| is present in
// |passwords_|, this will ask the password store to change the underlying
// password_value to |new_password| in case it was found. This will also
// notify clients that an edit event happened in case |form| was present
// in |passwords_|.
+ // TODO(crbug.com/1330906): Remove in favor of EditSavedCredentials.
bool EditPassword(const PasswordForm& form, std::u16string new_password);
// Modifies the provided password form and its duplicates
// with `new_username` and `new_password`.
//
// Note: this will also change duplicates of 'form' in all stores.
+ // TODO(crbug.com/1330906): Remove in favor of EditSavedCredentials.
bool EditSavedPasswords(const PasswordForm& form,
const std::u16string& new_username,
const std::u16string& new_password);
@@ -92,21 +99,28 @@ class SavedPasswordsPresenter : public PasswordStoreInterface::Observer,
// Modifies provided password forms, with |new_username|, |new_password| and
// |new_note|. |forms| must represent single credential, with its duplicates,
// or the same form saved on another store type.
+ // TODO(crbug.com/1330906): Remove in favor of EditSavedCredentials.
bool EditSavedPasswords(const SavedPasswordsView forms,
const std::u16string& new_username,
const std::u16string& new_password,
const std::u16string& new_note = std::u16string());
+ // Modifies all the saved credentials with a matching key. Only username,
+ // password and notes are modified.
+ bool EditSavedCredentials(const CredentialUIEntry& credential);
+
// Returns a list of the currently saved credentials.
SavedPasswordsView GetSavedPasswords() const;
- // Returns a list of unique password forms which includes normal credentials,
+ // Returns a list of unique passwords which includes normal credentials,
// federated credentials and blocked forms. If a same form is present both on
// account and profile stores it will be represented as a single entity.
// Uniqueness is determined using site name, username, password. For Android
// credentials package name is also taken into account and for Federated
// credentials federation origin.
+ // TODO(crbug.com/1330906): Replace all API to work with CredentialUIEntry.
std::vector<PasswordForm> GetUniquePasswordForms() const;
+ std::vector<CredentialUIEntry> GetSavedCredentials() const;
// Returns all the usernames for credentials saved for `signon_realm`. If
// `is_using_account_store` is true, this method will only consider
diff --git a/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc b/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc
index 1641b943ad7..bdd532ff4b9 100644
--- a/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc
+++ b/chromium/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc
@@ -16,6 +16,7 @@
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/browser/ui/credential_ui_entry.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -138,6 +139,8 @@ TEST_F(SavedPasswordsPresenterTest, AddPasswordFailWhenInvalidUrl) {
PasswordForm form =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
form.url = GURL("https://;/invalid");
+
+ base::HistogramTester histogram_tester;
EXPECT_CALL(observer, OnSavedPasswordsChanged).Times(0);
EXPECT_FALSE(presenter().AddPassword(form));
RunUntilIdle();
@@ -148,6 +151,8 @@ TEST_F(SavedPasswordsPresenterTest, AddPasswordFailWhenInvalidUrl) {
EXPECT_FALSE(presenter().AddPassword(form));
RunUntilIdle();
EXPECT_TRUE(store().IsEmpty());
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.PasswordNoteActionInSettings", 0);
presenter().RemoveObserver(&observer);
}
@@ -159,10 +164,14 @@ TEST_F(SavedPasswordsPresenterTest, AddPasswordFailWhenEmptyPassword) {
PasswordForm form =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
form.password_value = u"";
+
+ base::HistogramTester histogram_tester;
EXPECT_CALL(observer, OnSavedPasswordsChanged).Times(0);
EXPECT_FALSE(presenter().AddPassword(form));
RunUntilIdle();
EXPECT_TRUE(store().IsEmpty());
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.PasswordNoteActionInSettings", 0);
presenter().RemoveObserver(&observer);
}
@@ -170,6 +179,10 @@ TEST_F(SavedPasswordsPresenterTest, AddPasswordFailWhenEmptyPassword) {
TEST_F(SavedPasswordsPresenterTest, AddPasswordUnblocklistsOrigin) {
PasswordForm form_to_add =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
+ form_to_add.type = password_manager::PasswordForm::Type::kManuallyAdded;
+ form_to_add.date_created = base::Time::Now();
+ form_to_add.date_password_modified = base::Time::Now();
+
PasswordForm blocked_form;
blocked_form.blocked_by_user = true;
blocked_form.signon_realm = form_to_add.signon_realm;
@@ -286,6 +299,54 @@ TEST_F(SavedPasswordsPresenterTest, EditOnlyUsername) {
presenter().RemoveObserver(&observer);
}
+TEST_F(SavedPasswordsPresenterTest, EditOnlyUsernameClearsPartialIssues) {
+ PasswordForm form =
+ CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
+ // Make sure the form has some issues and expect that only phished and leaked
+ // are cleared because of the username change.
+ form.password_issues = {
+ {InsecureType::kLeaked,
+ InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))},
+ {InsecureType::kPhished,
+ InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))},
+ {InsecureType::kReused,
+ InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))},
+ {InsecureType::kWeak,
+ InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))}};
+
+ StrictMockSavedPasswordsPresenterObserver observer;
+ presenter().AddObserver(&observer);
+
+ EXPECT_CALL(observer, OnSavedPasswordsChanged);
+ store().AddLogin(form);
+ RunUntilIdle();
+ EXPECT_FALSE(store().IsEmpty());
+
+ std::vector<PasswordForm> forms = {form};
+
+ const std::u16string new_username = u"new_username";
+ // The result of the update should have a new username and weak and reused
+ // password issues.
+ PasswordForm updated_username = form;
+ updated_username.username_value = new_username;
+ updated_username.password_issues = {
+ {InsecureType::kReused,
+ InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))},
+ {InsecureType::kWeak,
+ InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))}};
+
+ EXPECT_CALL(observer, OnEdited(updated_username));
+ EXPECT_CALL(observer, OnSavedPasswordsChanged(ElementsAre(updated_username)));
+ EXPECT_TRUE(
+ presenter().EditSavedPasswords(forms, new_username, form.password_value));
+ RunUntilIdle();
+ EXPECT_THAT(
+ store().stored_passwords(),
+ ElementsAre(Pair(form.signon_realm, ElementsAre(updated_username))));
+
+ presenter().RemoveObserver(&observer);
+}
+
TEST_F(SavedPasswordsPresenterTest, EditOnlyPassword) {
PasswordForm form =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
@@ -326,6 +387,8 @@ TEST_F(SavedPasswordsPresenterTest, EditOnlyPassword) {
histogram_tester.ExpectUniqueSample(
"PasswordManager.PasswordEditUpdatedValues",
metrics_util::PasswordEditUpdatedValues::kPassword, 1);
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.PasswordNoteActionInSettings", 0);
presenter().RemoveObserver(&observer);
}
@@ -333,6 +396,41 @@ TEST_F(SavedPasswordsPresenterTest, EditOnlyPassword) {
TEST_F(SavedPasswordsPresenterTest, EditOnlyNoteFirstTime) {
PasswordForm form =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
+ form.notes.emplace_back(u"display name", u"note with non-empty display name",
+ /*date_created=*/base::Time::Now(),
+ /*hide_by_default=*/true);
+
+ store().AddLogin(form);
+ RunUntilIdle();
+ std::vector<PasswordForm> forms = {form};
+
+ const std::u16string kNewNoteValue = u"new note";
+
+ base::HistogramTester histogram_tester;
+ EXPECT_TRUE(presenter().EditSavedPasswords(
+ forms, form.username_value, form.password_value, kNewNoteValue));
+ RunUntilIdle();
+
+ // The note with the non-empty display name should be untouched. Another note
+ // with an empty display name should be added.
+ PasswordForm expected_updated_form = form;
+ expected_updated_form.notes.emplace_back(kNewNoteValue,
+ /*date_created=*/base::Time::Now());
+ EXPECT_THAT(
+ store().stored_passwords(),
+ ElementsAre(Pair(form.signon_realm, ElementsAre(expected_updated_form))));
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.PasswordNoteActionInSettings",
+ metrics_util::PasswordNoteAction::kNoteAddedInEditDialog, 1);
+}
+
+TEST_F(SavedPasswordsPresenterTest, EditingNotesShouldNotResetPasswordIssues) {
+ PasswordForm form =
+ CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
+
+ form.password_issues.insert(
+ {InsecureType::kLeaked,
+ InsecurityMetadata(base::Time(), IsMuted(false))});
store().AddLogin(form);
RunUntilIdle();
@@ -345,7 +443,8 @@ TEST_F(SavedPasswordsPresenterTest, EditOnlyNoteFirstTime) {
RunUntilIdle();
PasswordForm expected_updated_form = form;
- expected_updated_form.note = PasswordNote(kNewNoteValue, base::Time::Now());
+ expected_updated_form.notes = {
+ PasswordNote(kNewNoteValue, base::Time::Now())};
EXPECT_THAT(
store().stored_passwords(),
ElementsAre(Pair(form.signon_realm, ElementsAre(expected_updated_form))));
@@ -356,7 +455,7 @@ TEST_F(SavedPasswordsPresenterTest, EditOnlyNoteSecondTime) {
PasswordNote(u"existing note", base::Time::Now());
PasswordForm form =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
- form.note = kExistingNote;
+ form.notes = {kExistingNote};
store().AddLogin(form);
RunUntilIdle();
@@ -364,35 +463,68 @@ TEST_F(SavedPasswordsPresenterTest, EditOnlyNoteSecondTime) {
const std::u16string kNewNoteValue = u"new note";
+ base::HistogramTester histogram_tester;
EXPECT_TRUE(presenter().EditSavedPasswords(
forms, form.username_value, form.password_value, kNewNoteValue));
RunUntilIdle();
PasswordForm expected_updated_form = form;
- expected_updated_form.note.value = kNewNoteValue;
+ expected_updated_form.notes[0].value = kNewNoteValue;
EXPECT_THAT(
store().stored_passwords(),
ElementsAre(Pair(form.signon_realm, ElementsAre(expected_updated_form))));
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.PasswordNoteActionInSettings",
+ metrics_util::PasswordNoteAction::kNoteEditedInEditDialog, 1);
}
TEST_F(SavedPasswordsPresenterTest, EditNoteAsEmpty) {
PasswordForm form =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
- form.note = PasswordNote(u"existing note", base::Time::Now());
+ form.notes = {PasswordNote(u"existing note", base::Time::Now())};
std::vector<PasswordForm> forms = {form};
store().AddLogin(form);
RunUntilIdle();
+ base::HistogramTester histogram_tester;
EXPECT_TRUE(presenter().EditSavedPasswords(forms, form.username_value,
form.password_value, u""));
RunUntilIdle();
PasswordForm expected_updated_form = form;
- expected_updated_form.note.value = u"";
+ expected_updated_form.notes[0].value = u"";
EXPECT_THAT(
store().stored_passwords(),
ElementsAre(Pair(form.signon_realm, ElementsAre(expected_updated_form))));
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.PasswordNoteActionInSettings",
+ metrics_util::PasswordNoteAction::kNoteRemovedInEditDialog, 1);
+}
+
+TEST_F(SavedPasswordsPresenterTest,
+ GetSavedCredentialsReturnNotesWithEmptyDisplayName) {
+ // Create form with two notes, first is with a non-empty display name, and the
+ // second with an empty one.
+ PasswordNote kNoteWithEmptyDisplayName =
+ PasswordNote(u"note with empty display name",
+ /*date_created=*/base::Time::Now());
+ PasswordForm form =
+ CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
+ form.notes.emplace_back(u"display name", u"note with non-empty display name",
+ /*date_created=*/base::Time::Now(),
+ /*hide_by_default=*/true);
+ form.notes.push_back(kNoteWithEmptyDisplayName);
+
+ store().AddLogin(form);
+ RunUntilIdle();
+
+ // The expect credential UI entry should contain only the note with that empty
+ // display name.
+ std::vector<CredentialUIEntry> saved_credentials =
+ presenter().GetSavedCredentials();
+ ASSERT_EQ(1U, saved_credentials.size());
+ EXPECT_EQ(kNoteWithEmptyDisplayName, saved_credentials[0].note);
}
TEST_F(SavedPasswordsPresenterTest, EditUsernameAndPassword) {
@@ -437,6 +569,8 @@ TEST_F(SavedPasswordsPresenterTest, EditUsernameAndPassword) {
histogram_tester.ExpectBucketCount(
"PasswordManager.PasswordEditUpdatedValues",
metrics_util::PasswordEditUpdatedValues::kBoth, 1);
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.PasswordNoteActionInSettings", 0);
presenter().RemoveObserver(&observer);
}
@@ -591,6 +725,10 @@ TEST_F(SavedPasswordsPresenterTest,
EXPECT_THAT(presenter().GetUniquePasswordForms(),
UnorderedElementsAre(form, blocked_form, federated_form));
+ EXPECT_THAT(presenter().GetSavedCredentials(),
+ UnorderedElementsAre(CredentialUIEntry(form),
+ CredentialUIEntry(blocked_form),
+ CredentialUIEntry(federated_form)));
}
namespace {
@@ -684,6 +822,11 @@ TEST_F(SavedPasswordsPresenterWithTwoStoresTest,
// store.
PasswordForm profile_store_form =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore, /*index=*/0);
+ profile_store_form.type =
+ password_manager::PasswordForm::Type::kManuallyAdded;
+ profile_store_form.date_created = base::Time::Now();
+ profile_store_form.date_password_modified = base::Time::Now();
+
EXPECT_CALL(observer,
OnSavedPasswordsChanged(ElementsAre(profile_store_form)));
EXPECT_TRUE(presenter().AddPassword(profile_store_form));
@@ -696,6 +839,11 @@ TEST_F(SavedPasswordsPresenterWithTwoStoresTest,
// Now add a password to the account store, check it's added only there too.
PasswordForm account_store_form =
CreateTestPasswordForm(PasswordForm::Store::kAccountStore, /*index=*/1);
+ account_store_form.type =
+ password_manager::PasswordForm::Type::kManuallyAdded;
+ account_store_form.date_created = base::Time::Now();
+ account_store_form.date_password_modified = base::Time::Now();
+
EXPECT_CALL(observer, OnSavedPasswordsChanged(UnorderedElementsAre(
profile_store_form, account_store_form)));
EXPECT_TRUE(presenter().AddPassword(account_store_form));
@@ -710,6 +858,51 @@ TEST_F(SavedPasswordsPresenterWithTwoStoresTest,
presenter().RemoveObserver(&observer);
}
+// Tests AddPassword stores passwords with or without note
+TEST_F(SavedPasswordsPresenterWithTwoStoresTest,
+ AddPasswordStoresNoteIfExists) {
+ StrictMockSavedPasswordsPresenterObserver observer;
+ presenter().AddObserver(&observer);
+
+ // Add a password without a note.
+ PasswordForm form =
+ CreateTestPasswordForm(PasswordForm::Store::kProfileStore, /*index=*/0);
+ form.type = password_manager::PasswordForm::Type::kManuallyAdded;
+ form.date_created = base::Time::Now();
+ form.date_password_modified = base::Time::Now();
+
+ PasswordForm form2 =
+ CreateTestPasswordForm(PasswordForm::Store::kProfileStore, /*index=*/1);
+ form2.type = password_manager::PasswordForm::Type::kManuallyAdded;
+ form2.date_created = base::Time::Now();
+ form2.date_password_modified = base::Time::Now();
+ form2.notes = {PasswordNote(u"new note", base::Time::Now())};
+
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(observer, OnSavedPasswordsChanged(ElementsAre(form)));
+ EXPECT_TRUE(presenter().AddPassword(form));
+ RunUntilIdle();
+ EXPECT_THAT(profile_store().stored_passwords(),
+ ElementsAre(Pair(form.signon_realm, ElementsAre(form))));
+ histogram_tester.ExpectTotalCount(
+ "PasswordManager.PasswordNoteActionInSettings", 0);
+
+ // Add a password with note.
+ EXPECT_CALL(observer,
+ OnSavedPasswordsChanged(UnorderedElementsAre(form, form2)));
+ EXPECT_TRUE(presenter().AddPassword(form2));
+ RunUntilIdle();
+ EXPECT_THAT(
+ profile_store().stored_passwords(),
+ UnorderedElementsAre(Pair(form.signon_realm, ElementsAre(form)),
+ Pair(form2.signon_realm, ElementsAre(form2))));
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.PasswordNoteActionInSettings",
+ metrics_util::PasswordNoteAction::kNoteAddedInAddDialog, 1);
+
+ presenter().RemoveObserver(&observer);
+}
+
TEST_F(SavedPasswordsPresenterWithTwoStoresTest,
AddPasswordFailWhenUsernameAlreadyExistsForTheSameDomain) {
StrictMockSavedPasswordsPresenterObserver observer;
@@ -717,6 +910,10 @@ TEST_F(SavedPasswordsPresenterWithTwoStoresTest,
PasswordForm form =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
+ form.type = password_manager::PasswordForm::Type::kManuallyAdded;
+ form.date_created = base::Time::Now();
+ form.date_password_modified = base::Time::Now();
+
EXPECT_CALL(observer, OnSavedPasswordsChanged(UnorderedElementsAre(form)));
EXPECT_TRUE(presenter().AddPassword(form));
RunUntilIdle();
@@ -751,6 +948,10 @@ TEST_F(SavedPasswordsPresenterWithTwoStoresTest,
AddPasswordUnblocklistsOriginInDifferentStore) {
PasswordForm form_to_add =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore);
+ form_to_add.type = password_manager::PasswordForm::Type::kManuallyAdded;
+ form_to_add.date_created = base::Time::Now();
+ form_to_add.date_password_modified = base::Time::Now();
+
PasswordForm blocked_form;
blocked_form.blocked_by_user = true;
blocked_form.signon_realm = form_to_add.signon_realm;
@@ -779,8 +980,8 @@ TEST_F(SavedPasswordsPresenterWithTwoStoresTest,
TEST_F(SavedPasswordsPresenterWithTwoStoresTest, EditUsername) {
PasswordForm profile_store_form =
CreateTestPasswordForm(PasswordForm::Store::kProfileStore, /*index=*/0);
- // Make sure the form has some issues and expect that they are cleared
- // because of the password change.
+ // Make sure the form has a leaked issue and expect that it is cleared
+ // because of a username change.
profile_store_form.password_issues = {
{InsecureType::kLeaked,
InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))}};
@@ -976,6 +1177,8 @@ TEST_F(SavedPasswordsPresenterWithTwoStoresTest, GetUniquePasswords) {
PasswordForm::Store::kProfileStore | PasswordForm::Store::kAccountStore;
EXPECT_THAT(presenter().GetUniquePasswordForms(), ElementsAre(expected_form));
+ EXPECT_THAT(presenter().GetSavedCredentials(),
+ ElementsAre(CredentialUIEntry(expected_form)));
}
// Prefixes like [m, mobile, www] are considered as "same-site".
@@ -1021,6 +1224,12 @@ TEST_F(SavedPasswordsPresenterWithTwoStoresTest, EditPasswordBothStores) {
// because of the password change.
profile_store_form.password_issues = {
{InsecureType::kLeaked,
+ InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))},
+ {InsecureType::kReused,
+ InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))},
+ {InsecureType::kWeak,
+ InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))},
+ {InsecureType::kPhished,
InsecurityMetadata(base::Time::FromTimeT(1), IsMuted(false))}};
PasswordForm account_store_form = profile_store_form;
diff --git a/chromium/components/password_manager/core/common/password_manager_features.cc b/chromium/components/password_manager/core/common/password_manager_features.cc
index 9b81043bb4e..c8f30b5b92a 100644
--- a/chromium/components/password_manager/core/common/password_manager_features.cc
+++ b/chromium/components/password_manager/core/common/password_manager_features.cc
@@ -90,13 +90,25 @@ const base::Feature kIOSEnablePasswordManagerBrandingUpdate{
base::FEATURE_DISABLED_BY_DEFAULT};
// Enables (un)muting compromised passwords from bulk leak check in settings.
-const base::Feature kMuteCompromisedPasswords{
- "MuteCompromisedPasswords", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kMuteCompromisedPasswords {
+ "MuteCompromisedPasswords",
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+ base::FEATURE_DISABLED_BY_DEFAULT
+#else
+ base::FEATURE_ENABLED_BY_DEFAULT
+#endif
+};
// Enables adding, displaying and modifying extra notes to stored credentials.
+// When enabled, "PasswordViewPageInSettings" feature is ignored and the new
+// password view subpage is force enabled.
const base::Feature kPasswordNotes{"PasswordNotes",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables the new password viewing subpage.
+const base::Feature kPasswordViewPageInSettings{
+ "PasswordViewPageInSettings", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Enables sending credentials from the settings UI.
const base::Feature kSendPasswords{"SendPasswords",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -107,7 +119,11 @@ const base::Feature kLeakDetectionUnauthenticated = {
// Enables automatic password change flow from leaked password dialog.
const base::Feature kPasswordChange = {"PasswordChange",
+#if BUILDFLAG(IS_ANDROID)
+ base::FEATURE_ENABLED_BY_DEFAULT};
+#else
base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
// Enables password change flow from bulk leak check in settings.
const base::Feature kPasswordChangeInSettings = {
@@ -158,7 +174,7 @@ const base::Feature kSkipUndecryptablePasswords = {
// Enables the addition of passwords in Chrome Settings.
// TODO(crbug/1226008): Remove once it's launched.
const base::Feature kSupportForAddPasswordsInSettings = {
- "SupportForAddPasswordsInSettings", base::FEATURE_DISABLED_BY_DEFAULT};
+ "SupportForAddPasswordsInSettings", base::FEATURE_ENABLED_BY_DEFAULT};
#if BUILDFLAG(IS_LINUX)
// When enabled, all undecryptable passwords are deleted from the local database
@@ -203,12 +219,14 @@ const base::Feature kUnifiedPasswordManagerDesktop = {
// Enables support of sending votes on username first flow. The votes are sent
// on single username forms and are based on user interaction with the save
// prompt.
+// TODO(crbug.com/959776): Clean up code 2-3 milestones after the launch.
const base::Feature kUsernameFirstFlow = {"UsernameFirstFlow",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
// Enables support of filling and saving on username first flow.
+// TODO(crbug.com/959776): Clean up code 2-3 milestones after the launch.
const base::Feature kUsernameFirstFlowFilling = {
- "UsernameFirstFlowFilling", base::FEATURE_DISABLED_BY_DEFAULT};
+ "UsernameFirstFlowFilling", base::FEATURE_ENABLED_BY_DEFAULT};
// Enables support of sending additional votes on username first flow. The votes
// are sent on single password forms and contain information about preceding
@@ -264,6 +282,11 @@ bool IsPasswordScriptsFetchingEnabled() {
base::FeatureList::IsEnabled(kPasswordDomainCapabilitiesFetching);
}
+bool IsAutomatedPasswordChangeEnabled() {
+ return base::FeatureList::IsEnabled(kPasswordChangeInSettings) ||
+ base::FeatureList::IsEnabled(kPasswordChange);
+}
+
#if BUILDFLAG(IS_ANDROID)
bool UsesUnifiedPasswordManagerUi() {
if (!base::FeatureList::IsEnabled(kUnifiedPasswordManagerAndroid))
diff --git a/chromium/components/password_manager/core/common/password_manager_features.h b/chromium/components/password_manager/core/common/password_manager_features.h
index cee5ff609e1..7b94eea164a 100644
--- a/chromium/components/password_manager/core/common/password_manager_features.h
+++ b/chromium/components/password_manager/core/common/password_manager_features.h
@@ -38,6 +38,7 @@ extern const base::Feature kInferConfirmationPasswordField;
extern const base::Feature kIOSEnablePasswordManagerBrandingUpdate;
extern const base::Feature kMuteCompromisedPasswords;
extern const base::Feature kPasswordNotes;
+extern const base::Feature kPasswordViewPageInSettings;
extern const base::Feature kSendPasswords;
extern const base::Feature kLeakDetectionUnauthenticated;
extern const base::Feature kPasswordChange;
@@ -122,6 +123,10 @@ extern const char kTouchToFillPasswordSubmissionWithConservativeHeuristics[];
// enabled.
bool IsPasswordScriptsFetchingEnabled();
+// Returns true if any of the features that unlock entry points for password
+// change flows are enabled.
+bool IsAutomatedPasswordChangeEnabled();
+
#if BUILDFLAG(IS_ANDROID)
// Returns true if the unified password manager feature is active and in a stage
// that allows to use the new UI.
diff --git a/chromium/components/password_manager/core/common/password_manager_pref_names.cc b/chromium/components/password_manager/core/common/password_manager_pref_names.cc
index c75b2a03e8f..508b7487211 100644
--- a/chromium/components/password_manager/core/common/password_manager_pref_names.cc
+++ b/chromium/components/password_manager/core/common/password_manager_pref_names.cc
@@ -16,6 +16,7 @@ const char kCredentialsEnableService[] = "credentials_enable_service";
const char kAutoSignInEnabledGMS[] = "profile.auto_sign_in_enabled_gms";
const char kOfferToSavePasswordsEnabledGMS[] =
"profile.save_passwords_enabed_gms";
+const char kSettingsMigratedToUPM[] = "profile.settings_migrated_to_upm";
const char kCurrentMigrationVersionToGoogleMobileServices[] =
"current_migration_version_to_google_mobile_services";
@@ -24,6 +25,12 @@ const char kTimeOfLastMigrationAttempt[] = "time_of_last_migration_attempt";
const char kRequiresMigrationAfterSyncStatusChange[] =
"requires_migration_after_sync_status_change";
+
+const char kPasswordsPrefWithNewLabelUsed[] =
+ "passwords_pref_with_new_label_used";
+
+const char kUnenrolledFromGoogleMobileServicesDueToErrors[] =
+ "unenrolled_from_google_mobile_services_due_to_errors";
#endif
#if BUILDFLAG(IS_WIN)
@@ -75,5 +82,10 @@ const char kProfileStoreDateLastUsedForFilling[] =
const char kAccountStoreDateLastUsedForFilling[] =
"password_manager.account_store_date_last_used_for_filling";
+const char kPasswordChangeSuccessTrackerFlows[] =
+ "password_manager.password_change_success_tracker.flows";
+const char kPasswordChangeSuccessTrackerVersion[] =
+ "password_manager.password_change_success_tracker.version";
+
} // namespace prefs
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/common/password_manager_pref_names.h b/chromium/components/password_manager/core/common/password_manager_pref_names.h
index 693759dd573..0e5b1b99e20 100644
--- a/chromium/components/password_manager/core/common/password_manager_pref_names.h
+++ b/chromium/components/password_manager/core/common/password_manager_pref_names.h
@@ -49,6 +49,10 @@ extern const char kAutoSignInEnabledGMS[];
// mapped to `kCredentialEnableService` will be applied.
extern const char kOfferToSavePasswordsEnabledGMS[];
+// Boolean value indicating whether the regular prefs were migrated to UPM
+// settings.
+extern const char kSettingsMigratedToUPM[];
+
// Integer value which indicates the version used to migrate passwords from
// built in storage to Google Mobile Services.
extern const char kCurrentMigrationVersionToGoogleMobileServices[];
@@ -60,6 +64,17 @@ extern const char kTimeOfLastMigrationAttempt[];
// Boolean value that indicated the need of data migration between the two
// backends due to sync settings change.
extern const char kRequiresMigrationAfterSyncStatusChange[];
+
+// Boolean value indicating if the user has clicked on the "Password Manager"
+// item in settings after switching to the Unified Password Manager. A "New"
+// label is shown for the users who have not clicked on this item yet.
+// TODO(crbug.com/1217070): Remove this once the feature is rolled out.
+extern const char kPasswordsPrefWithNewLabelUsed[];
+
+// Boolean value indicating if the user should not get UPM experience because
+// of user-unresolvable errors received on communication with Google Mobile
+// Services.
+extern const char kUnenrolledFromGoogleMobileServicesDueToErrors[];
#endif
#if BUILDFLAG(IS_WIN)
@@ -129,6 +144,15 @@ extern const char kPasswordDismissCompromisedAlertEnabled[];
extern const char kProfileStoreDateLastUsedForFilling[];
extern const char kAccountStoreDateLastUsedForFilling[];
+// A list of ongoing PasswordChangeSuccessTracker flows that is persisted in
+// case Chrome is temporarily shut down while, e.g., a user retrieves a
+// password reset email.
+extern const char kPasswordChangeSuccessTrackerFlows[];
+
+// Integer indicating the format version of the list saved under
+// |kPasswordChangeSuccessTrackerFlows|.
+extern const char kPasswordChangeSuccessTrackerVersion[];
+
} // namespace prefs
} // namespace password_manager
diff --git a/chromium/components/password_manager/ios/BUILD.gn b/chromium/components/password_manager/ios/BUILD.gn
index 9ebf405e156..2d3a6393068 100644
--- a/chromium/components/password_manager/ios/BUILD.gn
+++ b/chromium/components/password_manager/ios/BUILD.gn
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//ios/web/js_compile.gni")
+import("//ios/web/public/js_messaging/optimize_js.gni")
component("ios") {
configs += [ "//build/config/compiler:enable_arc" ]
@@ -77,8 +77,8 @@ source_set("account_select_fill_data") {
]
}
-js_compile_bundle("password_controller_js") {
- closure_entry_point = "__crWeb.passwords"
+optimize_js("password_controller_js") {
+ primary_script = "resources/password_controller.js"
sources = [ "resources/password_controller.js" ]
}
diff --git a/chromium/components/password_manager/ios/password_manager_java_script_feature.mm b/chromium/components/password_manager/ios/password_manager_java_script_feature.mm
index 41b6fa1f90d..95c15b7cb6a 100644
--- a/chromium/components/password_manager/ios/password_manager_java_script_feature.mm
+++ b/chromium/components/password_manager/ios/password_manager_java_script_feature.mm
@@ -23,7 +23,7 @@ using autofill::CreateStringCallback;
namespace password_manager {
namespace {
-constexpr char kScriptName[] = "password_controller_js";
+constexpr char kScriptName[] = "password_controller";
// The timeout for any JavaScript call in this file.
constexpr int64_t kJavaScriptExecutionTimeoutInSeconds = 5;
@@ -50,20 +50,21 @@ std::unique_ptr<base::Value> SerializeFillData(
root_dict->SetInteger("unique_renderer_id",
FormRendererIdToJsParameter(form_renderer_id));
- auto fieldList = std::make_unique<base::ListValue>();
+ base::Value::List fieldList;
- auto usernameField = std::make_unique<base::DictionaryValue>();
- usernameField->SetInteger("unique_renderer_id",
- FieldRendererIdToJsParameter(username_element));
- usernameField->SetString("value", username_value);
- fieldList->Append(std::move(usernameField));
+ base::Value::Dict usernameField;
+ usernameField.Set("unique_renderer_id",
+ FieldRendererIdToJsParameter(username_element));
+ usernameField.Set("value", username_value);
+ fieldList.Append(std::move(usernameField));
- auto passwordField = std::make_unique<base::DictionaryValue>();
- passwordField->SetInteger("unique_renderer_id", password_element.value());
- passwordField->SetString("value", password_value);
- fieldList->Append(std::move(passwordField));
+ base::Value::Dict passwordField;
+ passwordField.Set("unique_renderer_id",
+ static_cast<int>(password_element.value()));
+ passwordField.Set("value", password_value);
+ fieldList.Append(std::move(passwordField));
- root_dict->Set("fields", std::move(fieldList));
+ root_dict->GetDict().Set("fields", std::move(fieldList));
return root_dict;
}
diff --git a/chromium/components/password_manager/ios/resources/password_controller.js b/chromium/components/password_manager/ios/resources/password_controller.js
index d016faee704..f1cdd906494 100644
--- a/chromium/components/password_manager/ios/resources/password_controller.js
+++ b/chromium/components/password_manager/ios/resources/password_controller.js
@@ -11,11 +11,6 @@
* be translated to struct FormData for further processing.
*/
-goog.provide('__crWeb.passwords');
-
-/* Beginning of anonymous object. */
-(function() {
-
/**
* Namespace for this file. It depends on |__gCrWeb| having already been
* injected.
@@ -60,6 +55,21 @@ const hasPasswordField = function(win) {
};
/**
+ * Checks whether the two URLs are from the same origin.
+ * @param {string} url_one
+ * @param {string} url_two
+ * @return {boolean} Whether the two URLs have the same origin.
+ */
+function isSameOrigin_(url_one, url_two) {
+ if (!url_one || !url_two) {
+ // Attempting to create URL representations of an empty string throws an
+ // exception.
+ return false;
+ }
+ return new URL(url_one).origin == new URL(url_two).origin;
+}
+
+/**
* Returns the contentWindow of all iframes that are from the the same origin
* as the containing window.
* @param {Window} win The window in which to look for frames.
@@ -70,7 +80,7 @@ const getSameOriginFrames = function(win) {
const result = [];
for (let i = 0; i < frames.length; i++) {
try {
- if (__gCrWeb.common.isSameOrigin(
+ if (isSameOrigin_(
win.location.href, frames[i].contentWindow.location.href)) {
result.push(frames[i].contentWindow);
}
@@ -211,7 +221,7 @@ __gCrWeb.passwords['fillPasswordForm'] = function(
const normalizedOrigin =
__gCrWeb.common.removeQueryAndReferenceFromURL(window.location.href);
const origin = /** @type {string} */ (formData['origin']);
- if (!__gCrWeb.common.isSameOrigin(origin, normalizedOrigin)) {
+ if (!isSameOrigin_(origin, normalizedOrigin)) {
return false;
}
return fillPasswordFormWithData(formData, username, password, window);
@@ -411,7 +421,7 @@ __gCrWeb.passwords.getPasswordFormDataFromUnownedElements = function(window) {
if (unownedControlElements.length === 0) {
return;
}
- const unownedForm = new __gCrWeb['common'].JSONSafeObject;
+ const unownedForm = new __gCrWeb['common'].JSONSafeObject();
const hasUnownedForm =
__gCrWeb.fill.unownedFormElementsAndFieldSetsToFormData(
window, fieldsets, unownedControlElements, extractMask, false,
@@ -434,5 +444,3 @@ __gCrWeb.passwords.getPasswordFormData = function(formElement, win) {
null /* field */);
return ok ? formData : null;
};
-
-}()); // End of anonymous object
diff --git a/chromium/components/password_manager_strings.grdp b/chromium/components/password_manager_strings.grdp
index 8a66e840816..8b9f3c8e33b 100644
--- a/chromium/components/password_manager_strings.grdp
+++ b/chromium/components/password_manager_strings.grdp
@@ -13,9 +13,6 @@
<message name="IDS_CREDENTIAL_LEAK_CHANGE_AUTOMATICALLY" desc="The text of the OK button in the credential leak dialog when Chrome is offering to automatically change the user's password.">
Change automatically
</message>
- <message name="IDS_CREDENTIAL_LEAK_DONT_CHANGE_AUTOMATICALLY" desc="The text of the Cancel button in the credential leak dialog when Chrome is offering to automatically change the user's password. The string expresses that instead of letting Chrome fix the password automatically, the user will fix it themselves.">
- Change it myself
- </message>
<message name="IDS_CREDENTIAL_LEAK_TITLE_CHANGE" desc="The title of the credential leak dialog when the user should change the password now.">
Change your password
</message>
@@ -50,7 +47,7 @@
The password you just used was found in a data breach. Google Password Manager recommends changing this password now.
</message>
<message name="IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED" desc="The text that is used in credential leak detection dialog when the leaked credentials were not saved but used on multiple sites. The leaked credentials may have been leaked by the current website, some other third-party website or even a third-party app used by the user. It could also be coincidental reuse of a trivial password used by some other users in the world and exposed in a public leak. Google Password Manager updated phrasing, branded.">
- The password you just used was found in a data breach. To secure your accounts, Google Password Manager recommends changing it now and then checking your saved passwords.
+ The password you just used was found in a data breach. To secure your accounts, Google Password Manager recommends changing it now and checking your saved passwords.
</message>
<message name="IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE_GPM_NON_BRANDED" desc="The text that is used in credential leak detection dialog when the leaked credentials are saved and used on multiple sites. The leaked credentials may have been leaked by the current website, some other third-party website or even a third-party app used by the user. It could also be coincidental reuse of a trivial password used by some other users in the world and exposed in a public leak. Google Password Manager updated phrasing, non branded.">
The password you just used was found in a data breach. To secure your accounts, Password Manager recommends checking your saved passwords.
diff --git a/chromium/components/password_manager_strings_grdp/IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED.png.sha1 b/chromium/components/password_manager_strings_grdp/IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED.png.sha1
index 29cbf2e117e..8a3a255b938 100644
--- a/chromium/components/password_manager_strings_grdp/IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED.png.sha1
+++ b/chromium/components/password_manager_strings_grdp/IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE_GPM_BRANDED.png.sha1
@@ -1 +1 @@
-a432fe2830864638180f88f4027015d2595509dd \ No newline at end of file
+30af1f2fc143cb2fae6d4fecf1a2a81d3dc27008 \ No newline at end of file
diff --git a/chromium/components/password_manager_strings_grdp/IDS_CREDENTIAL_LEAK_DONT_CHANGE_AUTOMATICALLY.png.sha1 b/chromium/components/password_manager_strings_grdp/IDS_CREDENTIAL_LEAK_DONT_CHANGE_AUTOMATICALLY.png.sha1
deleted file mode 100644
index 2b3746f7ac0..00000000000
--- a/chromium/components/password_manager_strings_grdp/IDS_CREDENTIAL_LEAK_DONT_CHANGE_AUTOMATICALLY.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b17add0b7f312d386f286d9ca0b0b44630561f57 \ No newline at end of file
diff --git a/chromium/components/payments/content/BUILD.gn b/chromium/components/payments/content/BUILD.gn
index 6483e7cf8f8..3d36fee6be8 100644
--- a/chromium/components/payments/content/BUILD.gn
+++ b/chromium/components/payments/content/BUILD.gn
@@ -55,6 +55,8 @@ static_library("content") {
"secure_payment_confirmation_model.h",
"secure_payment_confirmation_no_creds.cc",
"secure_payment_confirmation_no_creds.h",
+ "secure_payment_confirmation_no_creds_model.cc",
+ "secure_payment_confirmation_no_creds_model.h",
"secure_payment_confirmation_no_creds_view.cc",
"secure_payment_confirmation_no_creds_view.h",
"secure_payment_confirmation_view.h",
@@ -217,6 +219,7 @@ source_set("unit_tests") {
"payment_event_response_util_unittest.cc",
"payment_method_manifest_table_unittest.cc",
"secure_payment_confirmation_model_unittest.cc",
+ "secure_payment_confirmation_no_creds_model_unittest.cc",
"service_worker_payment_app_finder_unittest.cc",
"web_app_manifest_section_table_unittest.cc",
]
diff --git a/chromium/components/payments/content/android/BUILD.gn b/chromium/components/payments/content/android/BUILD.gn
index fcd5e1be5ed..b6bb4f8c564 100644
--- a/chromium/components/payments/content/android/BUILD.gn
+++ b/chromium/components/payments/content/android/BUILD.gn
@@ -123,6 +123,7 @@ android_library("service_java") {
deps = [
":feature_list_java",
"//base:base_java",
+ "//base:jni_java",
"//components/payments/mojom:mojom_java",
"//content/public/android:content_main_dex_java",
"//third_party/androidx:androidx_annotation_annotation_java",
@@ -139,6 +140,8 @@ android_library("feature_list_java") {
[ "java/src/org/chromium/components/payments/PaymentFeatureList.java" ]
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
}
@@ -194,6 +197,8 @@ android_library("full_java") {
":minimal_java",
":service_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/autofill/android:autofill_java",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/widget/android:java_resources",
@@ -239,6 +244,8 @@ android_library("minimal_java") {
deps = [
":service_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/autofill/android:payments_autofill_java",
"//components/payments/mojom:mojom_java",
"//content/public/android:content_java",
@@ -313,7 +320,6 @@ java_library("junit_test_support") {
]
deps = [
":java",
- "//base:base_java",
"//components/payments/content/android:service_java",
"//components/payments/mojom:mojom_java",
"//content/public/android:content_java",
@@ -340,7 +346,6 @@ java_library("junit") {
deps = [
":java",
":junit_test_support",
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//components/browser_ui/bottomsheet/android:java",
@@ -351,7 +356,6 @@ java_library("junit") {
"//mojo/public/java:system_java",
"//third_party/android_deps:material_design_java",
"//third_party/android_deps:robolectric_all_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_collection_collection_java",
"//third_party/blink/public/mojom:android_mojo_bindings_java",
"//third_party/junit",
diff --git a/chromium/components/payments/content/android/jni_payment_app.cc b/chromium/components/payments/content/android/jni_payment_app.cc
index e3f6e47caab..a4660ce6524 100644
--- a/chromium/components/payments/content/android/jni_payment_app.cc
+++ b/chromium/components/payments/content/android/jni_payment_app.cc
@@ -120,11 +120,6 @@ bool JniPaymentApp::CanPreselect(JNIEnv* env) {
return payment_app_->CanPreselect();
}
-bool JniPaymentApp::IsUserGestureRequiredToSkipUi(JNIEnv* env) {
- // All payment apps require a user gesture to skip UI by default.
- return true;
-}
-
void JniPaymentApp::InvokePaymentApp(JNIEnv* env,
const JavaParamRef<jobject>& jcallback) {
invoke_callback_ = jcallback;
diff --git a/chromium/components/payments/content/android/jni_payment_app.h b/chromium/components/payments/content/android/jni_payment_app.h
index 81ef2a1e064..9cdb6952f59 100644
--- a/chromium/components/payments/content/android/jni_payment_app.h
+++ b/chromium/components/payments/content/android/jni_payment_app.h
@@ -46,8 +46,6 @@ class JniPaymentApp : public PaymentApp::Delegate {
bool CanPreselect(JNIEnv* env);
- bool IsUserGestureRequiredToSkipUi(JNIEnv* env);
-
void InvokePaymentApp(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcallback);
diff --git a/chromium/components/payments/content/android/journey_logger_android.cc b/chromium/components/payments/content/android/journey_logger_android.cc
index 8dbaeb66a88..0c71ca2cb81 100644
--- a/chromium/components/payments/content/android/journey_logger_android.cc
+++ b/chromium/components/payments/content/android/journey_logger_android.cc
@@ -7,7 +7,6 @@
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "components/payments/content/android/jni_headers/JourneyLogger_jni.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/web_contents.h"
namespace payments {
@@ -198,7 +197,8 @@ static jlong JNI_JourneyLogger_InitJourneyLoggerAndroid(
content::WebContents::FromJavaWebContents(jweb_contents);
DCHECK(web_contents); // Verified in Java before invoking this function.
return reinterpret_cast<jlong>(new JourneyLoggerAndroid(
- jis_incognito, ukm::GetSourceIdForWebContentsDocument(web_contents)));
+ jis_incognito,
+ web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId()));
}
} // namespace payments
diff --git a/chromium/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java b/chromium/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java
index ba52106e3d4..a95c00a81f0 100644
--- a/chromium/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java
+++ b/chromium/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java
@@ -65,7 +65,6 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
private boolean mWarnNoFaviconCalled;
private boolean mIsClientClosed;
private MojoException mConnectionError;
- private boolean mIsUserGestureDefaultValue = true;
private boolean mWaitForUpdatedDetailsDefaultValue;
private PaymentAppService mPaymentAppService;
private PaymentAppFactoryDelegate mPaymentAppFactoryDelegate;
@@ -115,11 +114,6 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
@Override
public void dismissInstrument() {}
-
- @Override
- public boolean isUserGestureRequiredToSkipUi() {
- return false;
- }
};
Mockito.doReturn(app).when(mBrowserPaymentRequest).getSelectedPaymentApp();
List<PaymentApp> apps = new ArrayList();
@@ -225,7 +219,7 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
}
private void show(PaymentRequestService service) {
- service.show(mIsUserGestureDefaultValue, mWaitForUpdatedDetailsDefaultValue);
+ service.show(mWaitForUpdatedDetailsDefaultValue);
}
private void updateWith(PaymentRequestService service) {
@@ -351,7 +345,7 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
@Feature({"Payments"})
public void testNullDetailsFailsUpdateWith() {
PaymentRequestService service = defaultBuilder().build();
- service.show(mIsUserGestureDefaultValue, false);
+ service.show(false);
assertNoError();
service.updateWith(null);
assertErrorAndReason(ErrorStrings.INVALID_PAYMENT_DETAILS,
@@ -364,7 +358,7 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
@Feature({"Payments"})
public void testDetailsWithIdFailsUpdateWith() {
PaymentRequestService service = defaultBuilder().build();
- service.show(mIsUserGestureDefaultValue, false);
+ service.show(false);
PaymentDetails details = getDefaultPaymentDetailsUpdate();
details.id = "testId";
assertNoError();
@@ -379,7 +373,7 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
@Feature({"Payments"})
public void testOnPaymentDetailsUpdatedIsInvoked() {
PaymentRequestService service = defaultBuilder().build();
- service.show(mIsUserGestureDefaultValue, false);
+ service.show(false);
updateWith(service);
assertNoError();
Mockito.verify(mBrowserPaymentRequest, Mockito.times(1))
@@ -390,7 +384,7 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
@Feature({"Payments"})
public void testNullDetailsFailsContinueShow() {
PaymentRequestService service = defaultBuilder().build();
- service.show(mIsUserGestureDefaultValue, true);
+ service.show(true);
assertNoError();
service.updateWith(null);
assertErrorAndReason(ErrorStrings.INVALID_PAYMENT_DETAILS, PaymentErrorReason.USER_CANCEL);
@@ -401,7 +395,7 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
@Feature({"Payments"})
public void testDetailsWithIdFailsContinueShow() {
PaymentRequestService service = defaultBuilder().build();
- service.show(mIsUserGestureDefaultValue, true);
+ service.show(true);
assertNoError();
PaymentDetails details = getDefaultPaymentDetailsUpdate();
details.id = "testId";
@@ -414,7 +408,7 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
@Feature({"Payments"})
public void testContinueShowIsInvoked() {
PaymentRequestService service = defaultBuilder().build();
- service.show(mIsUserGestureDefaultValue, true);
+ service.show(true);
updateWith(service);
assertNoError();
verifyContinuedShowWithUpdatedDetails(1);
@@ -625,10 +619,10 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
PaymentRequestService service = defaultBuilder().build();
show(service);
Mockito.verify(mBrowserPaymentRequest, Mockito.never())
- .onShowCalledAndAppsQueriedAndDetailsFinalized(Mockito.anyBoolean());
+ .onShowCalledAndAppsQueriedAndDetailsFinalized();
queryPaymentApps();
Mockito.verify(mBrowserPaymentRequest, Mockito.times(1))
- .onShowCalledAndAppsQueriedAndDetailsFinalized(Mockito.anyBoolean());
+ .onShowCalledAndAppsQueriedAndDetailsFinalized();
verifyJourneyLoggerRecordedTransactionAmount();
}
@@ -637,15 +631,15 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
@Feature({"Payments"})
public void testWaitingForUpdatedDetailsDeterUiSkipMethod() {
PaymentRequestService service = defaultBuilder().build();
- service.show(mIsUserGestureDefaultValue, true);
+ service.show(true);
Mockito.verify(mBrowserPaymentRequest, Mockito.never())
- .onShowCalledAndAppsQueriedAndDetailsFinalized(Mockito.anyBoolean());
+ .onShowCalledAndAppsQueriedAndDetailsFinalized();
queryPaymentApps();
Mockito.verify(mBrowserPaymentRequest, Mockito.never())
- .onShowCalledAndAppsQueriedAndDetailsFinalized(Mockito.anyBoolean());
+ .onShowCalledAndAppsQueriedAndDetailsFinalized();
updateWith(service);
Mockito.verify(mBrowserPaymentRequest, Mockito.times(1))
- .onShowCalledAndAppsQueriedAndDetailsFinalized(Mockito.anyBoolean());
+ .onShowCalledAndAppsQueriedAndDetailsFinalized();
verifyJourneyLoggerRecordedTransactionAmount();
}
@@ -654,15 +648,15 @@ public class PaymentRequestServiceTest implements PaymentRequestClient {
@Feature({"Payments"})
public void testQueryFinishCanTriggerUiSkipped() {
PaymentRequestService service = defaultBuilder().build();
- service.show(mIsUserGestureDefaultValue, true);
+ service.show(true);
Mockito.verify(mBrowserPaymentRequest, Mockito.never())
- .onShowCalledAndAppsQueriedAndDetailsFinalized(Mockito.anyBoolean());
+ .onShowCalledAndAppsQueriedAndDetailsFinalized();
updateWith(service);
Mockito.verify(mBrowserPaymentRequest, Mockito.never())
- .onShowCalledAndAppsQueriedAndDetailsFinalized(Mockito.anyBoolean());
+ .onShowCalledAndAppsQueriedAndDetailsFinalized();
queryPaymentApps();
Mockito.verify(mBrowserPaymentRequest, Mockito.times(1))
- .onShowCalledAndAppsQueriedAndDetailsFinalized(Mockito.anyBoolean());
+ .onShowCalledAndAppsQueriedAndDetailsFinalized();
verifyJourneyLoggerRecordedTransactionAmount();
}
diff --git a/chromium/components/payments/content/android_payment_app_factory_unittest.cc b/chromium/components/payments/content/android_payment_app_factory_unittest.cc
index 6732cbe570c..56fbc531780 100644
--- a/chromium/components/payments/content/android_payment_app_factory_unittest.cc
+++ b/chromium/components/payments/content/android_payment_app_factory_unittest.cc
@@ -183,7 +183,7 @@ TEST_F(AndroidPaymentAppFactoryTest, FindAppsThatDoNotHaveReadyToPayService) {
.WillRepeatedly(testing::Return("com.example.app"));
EXPECT_CALL(delegate_, GetInitiatorRenderFrameHost())
.WillRepeatedly(
- testing::Return(delegate_.GetWebContents()->GetMainFrame()));
+ testing::Return(delegate_.GetWebContents()->GetPrimaryMainFrame()));
EXPECT_CALL(delegate_, OnDoneCreatingPaymentApps());
EXPECT_CALL(delegate_, OnPaymentAppCreationError(testing::_, testing::_))
@@ -226,7 +226,7 @@ TEST_F(AndroidPaymentAppFactoryTest,
.WillRepeatedly(testing::Return("com.example.app"));
EXPECT_CALL(delegate_, GetInitiatorRenderFrameHost())
.WillRepeatedly(
- testing::Return(delegate_.GetWebContents()->GetMainFrame()));
+ testing::Return(delegate_.GetWebContents()->GetPrimaryMainFrame()));
EXPECT_CALL(delegate_, OnDoneCreatingPaymentApps());
EXPECT_CALL(delegate_, OnPaymentAppCreationError(testing::_, testing::_))
@@ -266,7 +266,7 @@ TEST_F(AndroidPaymentAppFactoryTest,
.WillRepeatedly(testing::Return("com.twa.app"));
EXPECT_CALL(delegate_, GetInitiatorRenderFrameHost())
.WillRepeatedly(
- testing::Return(delegate_.GetWebContents()->GetMainFrame()));
+ testing::Return(delegate_.GetWebContents()->GetPrimaryMainFrame()));
EXPECT_CALL(delegate_, OnDoneCreatingPaymentApps());
EXPECT_CALL(delegate_, OnPaymentAppCreationError(testing::_, testing::_))
@@ -302,7 +302,7 @@ TEST_F(AndroidPaymentAppFactoryTest, IgnoreAppsThatAreNotReadyToPay) {
.WillRepeatedly(testing::Return("com.example.app"));
EXPECT_CALL(delegate_, GetInitiatorRenderFrameHost())
.WillRepeatedly(
- testing::Return(delegate_.GetWebContents()->GetMainFrame()));
+ testing::Return(delegate_.GetWebContents()->GetPrimaryMainFrame()));
EXPECT_CALL(delegate_, OnDoneCreatingPaymentApps());
EXPECT_CALL(delegate_, OnPaymentAppCreationError(testing::_, testing::_))
.Times(0);
@@ -333,7 +333,7 @@ TEST_F(AndroidPaymentAppFactoryTest, FindTheCorrectTwaAppInTwaMode) {
.WillRepeatedly(testing::Return("com.correct-twa.app"));
EXPECT_CALL(delegate_, GetInitiatorRenderFrameHost())
.WillRepeatedly(
- testing::Return(delegate_.GetWebContents()->GetMainFrame()));
+ testing::Return(delegate_.GetWebContents()->GetPrimaryMainFrame()));
EXPECT_CALL(delegate_, OnDoneCreatingPaymentApps());
EXPECT_CALL(delegate_, OnPaymentAppCreationError(testing::_, testing::_))
@@ -480,7 +480,7 @@ TEST_F(AndroidPaymentAppFactoryTest,
.WillRepeatedly(testing::Return("com.twa.app"));
EXPECT_CALL(delegate_, GetInitiatorRenderFrameHost())
.WillRepeatedly(
- testing::Return(delegate_.GetWebContents()->GetMainFrame()));
+ testing::Return(delegate_.GetWebContents()->GetPrimaryMainFrame()));
EXPECT_CALL(delegate_, OnDoneCreatingPaymentApps());
EXPECT_CALL(delegate_, OnPaymentAppCreationError(testing::_, testing::_))
.Times(0);
diff --git a/chromium/components/payments/content/android_payment_app_unittest.cc b/chromium/components/payments/content/android_payment_app_unittest.cc
index 0378ca7b7e9..ded169866a8 100644
--- a/chromium/components/payments/content/android_payment_app_unittest.cc
+++ b/chromium/components/payments/content/android_payment_app_unittest.cc
@@ -54,7 +54,7 @@ class AndroidPaymentAppTest : public testing::Test,
GURL("https://top-level-origin.com"),
GURL("https://payment-request-origin.com"), "payment-request-id",
std::move(description), communication,
- web_contents->GetMainFrame()->GetGlobalId());
+ web_contents->GetPrimaryMainFrame()->GetGlobalId());
}
AndroidPaymentAppTest()
diff --git a/chromium/components/payments/content/content_payment_request_delegate.h b/chromium/components/payments/content/content_payment_request_delegate.h
index c30feee33ec..388850b69e9 100644
--- a/chromium/components/payments/content/content_payment_request_delegate.h
+++ b/chromium/components/payments/content/content_payment_request_delegate.h
@@ -83,7 +83,9 @@ class ContentPaymentRequestDelegate : public PaymentRequestDelegate {
virtual void ShowNoMatchingPaymentCredentialDialog(
const std::u16string& merchant_name,
- base::OnceClosure response_callback) = 0;
+ const std::string& rp_id,
+ base::OnceClosure response_callback,
+ base::OnceClosure opt_out_callback) = 0;
// Returns a weak pointer to this delegate.
base::WeakPtr<ContentPaymentRequestDelegate> GetContentWeakPtr();
diff --git a/chromium/components/payments/content/developer_console_logger.cc b/chromium/components/payments/content/developer_console_logger.cc
index 3b8974abe8a..2781b5a3fcd 100644
--- a/chromium/components/payments/content/developer_console_logger.cc
+++ b/chromium/components/payments/content/developer_console_logger.cc
@@ -18,8 +18,8 @@ DeveloperConsoleLogger::~DeveloperConsoleLogger() = default;
void DeveloperConsoleLogger::Warn(const std::string& warning_message) const {
if (!enabled_)
return;
- if (web_contents_ && web_contents_->GetMainFrame()) {
- web_contents_->GetMainFrame()->AddMessageToConsole(
+ if (web_contents_ && web_contents_->GetPrimaryMainFrame()) {
+ web_contents_->GetPrimaryMainFrame()->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kWarning, warning_message);
} else {
ErrorLogger::Warn(warning_message);
@@ -29,8 +29,8 @@ void DeveloperConsoleLogger::Warn(const std::string& warning_message) const {
void DeveloperConsoleLogger::Error(const std::string& error_message) const {
if (!enabled_)
return;
- if (web_contents_ && web_contents_->GetMainFrame()) {
- web_contents_->GetMainFrame()->AddMessageToConsole(
+ if (web_contents_ && web_contents_->GetPrimaryMainFrame()) {
+ web_contents_->GetPrimaryMainFrame()->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kError, error_message);
} else {
ErrorLogger::Error(error_message);
diff --git a/chromium/components/payments/content/installable_payment_app_crawler.cc b/chromium/components/payments/content/installable_payment_app_crawler.cc
index 010dd8db9c9..841e4347bb9 100644
--- a/chromium/components/payments/content/installable_payment_app_crawler.cc
+++ b/chromium/components/payments/content/installable_payment_app_crawler.cc
@@ -21,12 +21,12 @@
#include "content/public/browser/manifest_icon_downloader.h"
#include "content/public/browser/payment_app_provider_util.h"
#include "content/public/browser/permission_controller.h"
-#include "content/public/browser/permission_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "third_party/blink/public/common/manifest/manifest_icon_selector.h"
+#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
#include "ui/gfx/geometry/size.h"
#include "url/gurl.h"
@@ -186,7 +186,7 @@ void InstallablePaymentAppCrawler::OnPaymentMethodManifestParsed(
}
if (permission_controller->GetPermissionStatusForOriginWithoutContext(
- content::PermissionType::PAYMENT_HANDLER,
+ blink::PermissionType::PAYMENT_HANDLER,
url::Origin::Create(web_app_manifest_url)) !=
blink::mojom::PermissionStatus::GRANTED) {
// Do not download the web app manifest if it is blocked.
diff --git a/chromium/components/payments/content/payment_app.cc b/chromium/components/payments/content/payment_app.cc
index 6957530d69d..0350041f738 100644
--- a/chromium/components/payments/content/payment_app.cc
+++ b/chromium/components/payments/content/payment_app.cc
@@ -131,12 +131,12 @@ bool PaymentApp::operator<(const PaymentApp& other) const {
if (completeness != 0)
return completeness > 0;
- // Sort autofill cards using their frecency scores as tie breaker.
+ // Sort autofill cards using their ranking scores as tie breaker.
if (type_ == Type::AUTOFILL) {
DCHECK_EQ(other.type(), Type::AUTOFILL);
return static_cast<const AutofillPaymentApp*>(this)
->credit_card()
- ->HasGreaterFrecencyThan(
+ ->HasGreaterRankingThan(
static_cast<const AutofillPaymentApp*>(&other)->credit_card(),
autofill::AutofillClock::Now());
}
diff --git a/chromium/components/payments/content/payment_method_manifest_table.cc b/chromium/components/payments/content/payment_method_manifest_table.cc
index 1aeac11625c..9743e4fb68d 100644
--- a/chromium/components/payments/content/payment_method_manifest_table.cc
+++ b/chromium/components/payments/content/payment_method_manifest_table.cc
@@ -249,16 +249,10 @@ PaymentMethodManifestTable::GetSecurePaymentConfirmationCredentials(
const std::string& relying_party_id) {
std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> credentials;
sql::Statement s(
- base::FeatureList::IsEnabled(features::kSecurePaymentConfirmationAPIV3)
- ? db_->GetUniqueStatement(
- "SELECT relying_party_id, user_id "
- "FROM secure_payment_confirmation_instrument "
- "WHERE credential_id=? "
- "AND relying_party_id=?")
- : db_->GetUniqueStatement(
- "SELECT relying_party_id, user_id "
- "FROM secure_payment_confirmation_instrument "
- "WHERE credential_id=?"));
+ db_->GetUniqueStatement("SELECT relying_party_id, user_id "
+ "FROM secure_payment_confirmation_instrument "
+ "WHERE credential_id=? "
+ "AND relying_party_id=?"));
// The `credential_id` temporary variable is not `const` because it is
// std::move()'d into the credential below.
for (auto& credential_id : credential_ids) {
@@ -267,8 +261,7 @@ PaymentMethodManifestTable::GetSecurePaymentConfirmationCredentials(
continue;
s.BindBlob(0, credential_id);
- if (base::FeatureList::IsEnabled(features::kSecurePaymentConfirmationAPIV3))
- s.BindString(1, relying_party_id);
+ s.BindString(1, relying_party_id);
if (!s.Step())
continue;
diff --git a/chromium/components/payments/content/payment_method_manifest_table_unittest.cc b/chromium/components/payments/content/payment_method_manifest_table_unittest.cc
index 6a78ab69e75..4d5d759f3db 100644
--- a/chromium/components/payments/content/payment_method_manifest_table_unittest.cc
+++ b/chromium/components/payments/content/payment_method_manifest_table_unittest.cc
@@ -211,6 +211,10 @@ TEST_F(PaymentMethodManifestTableTest, AddAndGetOneValidCredential) {
->GetSecurePaymentConfirmationCredentials(
{std::vector<uint8_t>()}, relying_party_id)
.empty());
+ EXPECT_TRUE(table
+ ->GetSecurePaymentConfirmationCredentials(
+ CreateCredentialIdList(), /*relying_party_id=*/"")
+ .empty());
}
TEST_F(PaymentMethodManifestTableTest, AddingInvalidCredentialReturnsFalse) {
@@ -273,6 +277,11 @@ TEST_F(PaymentMethodManifestTableTest,
CreateCredentialIdList(/*first_byte=*/0), "relying-party-1.example");
ExpectOneValidCredential({0, 1, 2, 3}, "relying-party-1.example",
{4, 5, 6, 7}, std::move(credentials));
+ EXPECT_TRUE(table
+ ->GetSecurePaymentConfirmationCredentials(
+ CreateCredentialIdList(/*first_byte=*/0),
+ "relying-party-2.example")
+ .empty());
}
TEST_F(PaymentMethodManifestTableTest, RelyingPartyCanHaveMultipleCredentials) {
@@ -508,86 +517,5 @@ TEST_F(PaymentMethodManifestTableTest, CredentialTableUserIdMigration) {
.size());
}
-// Tests where there's additional test expectations when SPC API V3 is enabled.
-// These are very similar to their equivalent PaymentMethodManifestTableTests,
-// just with some additional expectations at the end of each test.
-class PaymentMethodManifestTableSecurePaymentConfirmationAPIV3Test
- : public PaymentMethodManifestTableTest {
- public:
- PaymentMethodManifestTableSecurePaymentConfirmationAPIV3Test() = default;
- ~PaymentMethodManifestTableSecurePaymentConfirmationAPIV3Test() override =
- default;
-
- PaymentMethodManifestTableSecurePaymentConfirmationAPIV3Test(
- const PaymentMethodManifestTableSecurePaymentConfirmationAPIV3Test&
- other) = delete;
- PaymentMethodManifestTableSecurePaymentConfirmationAPIV3Test& operator=(
- const PaymentMethodManifestTableSecurePaymentConfirmationAPIV3Test&
- other) = delete;
-
- protected:
- void SetUp() override {
- scoped_feature_list_.InitAndEnableFeature(
- features::kSecurePaymentConfirmationAPIV3);
- PaymentMethodManifestTableTest::SetUp();
- }
-
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(PaymentMethodManifestTableSecurePaymentConfirmationAPIV3Test,
- AddAndGetOneValidCredential) {
- PaymentMethodManifestTable* table =
- PaymentMethodManifestTable::FromWebDatabase(db_.get());
-
- std::string relying_party_id("relying-party.example");
- EXPECT_TRUE(table->AddSecurePaymentConfirmationCredential(
- SecurePaymentConfirmationCredential(CreateCredentialId(),
- relying_party_id, CreateUserId())));
-
- auto credentials = table->GetSecurePaymentConfirmationCredentials(
- CreateCredentialIdList(), relying_party_id);
-
- ExpectOneValidCredential({0, 1, 2, 3}, relying_party_id, {4, 5, 6, 7},
- std::move(credentials));
-
- EXPECT_TRUE(
- table->GetSecurePaymentConfirmationCredentials({}, relying_party_id)
- .empty());
- EXPECT_TRUE(table
- ->GetSecurePaymentConfirmationCredentials(
- {std::vector<uint8_t>()}, relying_party_id)
- .empty());
- EXPECT_TRUE(table
- ->GetSecurePaymentConfirmationCredentials(
- CreateCredentialIdList(), /*relying_party_id=*/"")
- .empty());
-}
-
-TEST_F(PaymentMethodManifestTableSecurePaymentConfirmationAPIV3Test,
- DifferentRelyingPartiesCannotUseSameCredentialIdentifier) {
- PaymentMethodManifestTable* table =
- PaymentMethodManifestTable::FromWebDatabase(db_.get());
- EXPECT_TRUE(table->AddSecurePaymentConfirmationCredential(
- SecurePaymentConfirmationCredential(CreateCredentialId(/*first_byte=*/0),
- "relying-party-1.example",
- CreateUserId(/*first_byte=*/4))));
-
- EXPECT_FALSE(table->AddSecurePaymentConfirmationCredential(
- SecurePaymentConfirmationCredential(CreateCredentialId(/*first_byte=*/0),
- "relying-party-2.example",
- CreateUserId(/*first_byte=*/5))));
-
- auto credentials = table->GetSecurePaymentConfirmationCredentials(
- CreateCredentialIdList(/*first_byte=*/0), "relying-party-1.example");
- ExpectOneValidCredential({0, 1, 2, 3}, "relying-party-1.example",
- {4, 5, 6, 7}, std::move(credentials));
- EXPECT_TRUE(table
- ->GetSecurePaymentConfirmationCredentials(
- CreateCredentialIdList(/*first_byte=*/0),
- "relying-party-2.example")
- .empty());
-}
-
} // namespace
} // namespace payments
diff --git a/chromium/components/payments/content/payment_request.cc b/chromium/components/payments/content/payment_request.cc
index 39fab27e384..25b7d024c52 100644
--- a/chromium/components/payments/content/payment_request.cc
+++ b/chromium/components/payments/content/payment_request.cc
@@ -33,7 +33,6 @@
#include "components/payments/core/payments_validators.h"
#include "components/payments/core/url_util.h"
#include "components/prefs/pref_service.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "components/url_formatter/elide_url.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
@@ -89,7 +88,7 @@ PaymentRequest::PaymentRequest(
spc_transaction_mode_(spc_transaction_mode),
observer_for_testing_(observer_for_testing),
journey_logger_(delegate_->IsOffTheRecord(),
- ukm::GetSourceIdForWebContentsDocument(web_contents())) {
+ render_frame_host->GetPageUkmSourceId()) {
payment_handler_host_ = std::make_unique<PaymentHandlerHost>(
web_contents(), weak_ptr_factory_.GetWeakPtr());
}
@@ -118,7 +117,7 @@ void PaymentRequest::Init(
if (is_initialized_) {
log_.Error(errors::kAttemptedInitializationTwice);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -130,7 +129,7 @@ void PaymentRequest::Init(
const GURL last_committed_url = delegate_->GetLastCommittedURL();
if (!network::IsUrlPotentiallyTrustworthy(last_committed_url)) {
log_.Error(errors::kNotInASecureOrigin);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -155,13 +154,13 @@ void PaymentRequest::Init(
client_->OnError(
mojom::PaymentErrorReason::NOT_SUPPORTED_FOR_INVALID_ORIGIN_OR_SSL,
reject_show_error_message_);
- delete this;
+ ResetAndDeleteThis();
return;
}
if (method_data.empty()) {
log_.Error(errors::kMethodDataRequired);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -170,26 +169,26 @@ void PaymentRequest::Init(
return !datum || datum->supported_method.empty();
})) {
log_.Error(errors::kMethodNameRequired);
- delete this;
+ ResetAndDeleteThis();
return;
}
if (!details || !details->id || !details->total) {
log_.Error(errors::kInvalidPaymentDetails);
- delete this;
+ ResetAndDeleteThis();
return;
}
if (!options) {
log_.Error(errors::kInvalidPaymentOptions);
- delete this;
+ ResetAndDeleteThis();
return;
}
std::string error;
if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
log_.Error(error);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -276,16 +275,16 @@ void PaymentRequest::Init(
}
}
-void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
+void PaymentRequest::Show(bool wait_for_updated_details) {
if (!IsInitialized()) {
log_.Error(errors::kCannotShowWithoutInit);
- delete this;
+ ResetAndDeleteThis();
return;
}
if (is_show_called_) {
log_.Error(errors::kCannotShowTwice);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -306,7 +305,7 @@ void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS);
client_->OnError(mojom::PaymentErrorReason::ALREADY_SHOWING,
errors::kAnotherUiShowing);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -317,12 +316,10 @@ void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
journey_logger_.SetNotShown(JourneyLogger::NOT_SHOWN_REASON_OTHER);
client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
errors::kCannotShowInBackgroundTab);
- delete this;
+ ResetAndDeleteThis();
return;
}
- is_show_user_gesture_ = is_user_gesture;
-
if (wait_for_updated_details) {
// Put |spec_| into uninitialized state, so the UI knows to show a spinner.
// This method does not block.
@@ -342,7 +339,6 @@ void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
if (!spec_->IsAppStoreBillingAlsoRequested())
display_handle_->Show(weak_ptr_factory_.GetWeakPtr());
- state_->set_is_show_user_gesture(is_show_user_gesture_);
state_->AreRequestedMethodsSupported(
base::BindOnce(&PaymentRequest::AreRequestedMethodsSupportedCallback,
weak_ptr_factory_.GetWeakPtr()));
@@ -351,13 +347,13 @@ void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
void PaymentRequest::Retry(mojom::PaymentValidationErrorsPtr errors) {
if (!IsInitialized()) {
log_.Error(errors::kCannotRetryWithoutInit);
- delete this;
+ ResetAndDeleteThis();
return;
}
if (!IsThisPaymentRequestShowing()) {
log_.Error(errors::kCannotRetryWithoutShow);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -366,7 +362,7 @@ void PaymentRequest::Retry(mojom::PaymentValidationErrorsPtr errors) {
&error)) {
log_.Error(error);
client_->OnError(mojom::PaymentErrorReason::USER_CANCEL, error);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -381,27 +377,27 @@ void PaymentRequest::Retry(mojom::PaymentValidationErrorsPtr errors) {
void PaymentRequest::UpdateWith(mojom::PaymentDetailsPtr details) {
if (!IsInitialized()) {
log_.Error(errors::kCannotUpdateWithoutInit);
- delete this;
+ ResetAndDeleteThis();
return;
}
if (!IsThisPaymentRequestShowing()) {
log_.Error(errors::kCannotUpdateWithoutShow);
- delete this;
+ ResetAndDeleteThis();
return;
}
// ID cannot be updated. Updating the total is optional.
if (!details || details->id) {
log_.Error(errors::kInvalidPaymentDetails);
- delete this;
+ ResetAndDeleteThis();
return;
}
std::string error;
if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
log_.Error(error);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -409,7 +405,7 @@ void PaymentRequest::UpdateWith(mojom::PaymentDetailsPtr details) {
!PaymentsValidators::IsValidAddressErrorsFormat(
details->shipping_address_errors, &error)) {
log_.Error(error);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -453,13 +449,13 @@ void PaymentRequest::OnPaymentDetailsNotUpdated() {
// be more verbose.
if (!IsInitialized()) {
log_.Error(errors::kNotInitialized);
- delete this;
+ ResetAndDeleteThis();
return;
}
if (!IsThisPaymentRequestShowing()) {
log_.Error(errors::kNotShown);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -474,13 +470,13 @@ void PaymentRequest::OnPaymentDetailsNotUpdated() {
void PaymentRequest::Abort() {
if (!IsInitialized()) {
log_.Error(errors::kCannotAbortWithoutInit);
- delete this;
+ ResetAndDeleteThis();
return;
}
if (!IsThisPaymentRequestShowing()) {
log_.Error(errors::kCannotAbortWithoutShow);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -506,13 +502,13 @@ void PaymentRequest::Abort() {
void PaymentRequest::Complete(mojom::PaymentComplete result) {
if (!IsInitialized()) {
log_.Error(errors::kCannotCompleteWithoutInit);
- delete this;
+ ResetAndDeleteThis();
return;
}
if (!IsThisPaymentRequestShowing()) {
log_.Error(errors::kCannotAbortWithoutShow);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -544,7 +540,7 @@ void PaymentRequest::Complete(mojom::PaymentComplete result) {
void PaymentRequest::CanMakePayment() {
if (!IsInitialized()) {
log_.Error(errors::kCannotCallCanMakePaymentWithoutInit);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -565,7 +561,7 @@ void PaymentRequest::CanMakePayment() {
void PaymentRequest::HasEnrolledInstrument() {
if (!IsInitialized()) {
log_.Error(errors::kCannotCallHasEnrolledInstrumentWithoutInit);
- delete this;
+ ResetAndDeleteThis();
return;
}
@@ -653,12 +649,19 @@ void PaymentRequest::AreRequestedMethodsSupportedCallback(
// card art icon - because we download it in all cases, revealing a
// failure doesn't leak any information about the user to the site.
error_reason != AppCreationFailureReason::ICON_DOWNLOAD_FAILED) {
+ auto opt_out_callback =
+ spec_->method_data().front()->secure_payment_confirmation->show_opt_out
+ ? base::BindOnce(&PaymentRequest::OnUserOptedOut,
+ weak_ptr_factory_.GetWeakPtr())
+ : base::NullCallback();
delegate_->ShowNoMatchingPaymentCredentialDialog(
url_formatter::FormatUrlForSecurityDisplay(
state_->GetTopOrigin(),
url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC),
+ spec_->method_data().front()->secure_payment_confirmation->rp_id,
base::BindOnce(&PaymentRequest::OnUserCancelled,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ std::move(opt_out_callback));
if (observer_for_testing_)
observer_for_testing_->OnErrorDisplayed();
return;
@@ -686,7 +689,7 @@ void PaymentRequest::AreRequestedMethodsSupportedCallback(
(error_message.empty() ? "" : " " + error_message));
if (observer_for_testing_)
observer_for_testing_->OnNotSupportedError();
- delete this;
+ ResetAndDeleteThis();
}
}
@@ -748,8 +751,7 @@ bool PaymentRequest::SatisfiesSkipUIConstraints() {
delegate_->SkipUiForBasicCard()) &&
base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
- is_show_user_gesture_ && state()->IsInitialized() &&
- spec()->IsInitialized() &&
+ state()->IsInitialized() && spec()->IsInitialized() &&
OnlySingleAppCanProvideAllRequiredInformation() &&
// The available app should be preselectable.
state()->selected_app() != nullptr;
@@ -820,7 +822,26 @@ void PaymentRequest::OnUserCancelled() {
? errors::kWebAuthnOperationTimedOutOrNotAllowed
: (!reject_show_error_message_.empty() ? reject_show_error_message_
: errors::kUserCancelled));
- delete this;
+
+ ResetAndDeleteThis();
+}
+
+void PaymentRequest::OnUserOptedOut() {
+ // This should only be called for SPC.
+ DCHECK(spec_->IsSecurePaymentConfirmationRequested());
+
+ // If |client_| is not bound, then the object is already being destroyed as
+ // a result of a renderer event.
+ if (!client_.is_bound())
+ return;
+
+ RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
+
+ // This sends an error to the renderer, which informs the API user.
+ client_->OnError(mojom::PaymentErrorReason::USER_OPT_OUT,
+ errors::kSpcUserOptedOut);
+
+ ResetAndDeleteThis();
}
void PaymentRequest::ReadyToCommitNavigation(
diff --git a/chromium/components/payments/content/payment_request.h b/chromium/components/payments/content/payment_request.h
index 8a6c6a55329..1e3eef07972 100644
--- a/chromium/components/payments/content/payment_request.h
+++ b/chromium/components/payments/content/payment_request.h
@@ -94,7 +94,7 @@ class PaymentRequest : public content::DocumentService<mojom::PaymentRequest>,
std::vector<mojom::PaymentMethodDataPtr> method_data,
mojom::PaymentDetailsPtr details,
mojom::PaymentOptionsPtr options) override;
- void Show(bool is_user_gesture, bool wait_for_updated_details) override;
+ void Show(bool wait_for_updated_details) override;
void Retry(mojom::PaymentValidationErrorsPtr errors) override;
void UpdateWith(mojom::PaymentDetailsPtr details) override;
void OnPaymentDetailsNotUpdated() override;
@@ -128,6 +128,10 @@ class PaymentRequest : public content::DocumentService<mojom::PaymentRequest>,
// object and close any related connections.
void OnUserCancelled();
+ // Called when the user explicitly opts out of the flow. Only used for
+ // SecurePaymentConfirmation currently.
+ void OnUserOptedOut();
+
// Called when the PaymentRequest is about to be destroyed. This reports
// the reason for destruction.
void WillBeDestroyed(content::DocumentServiceDestructionReason reason) final;
@@ -142,7 +146,6 @@ class PaymentRequest : public content::DocumentService<mojom::PaymentRequest>,
void OnPaymentHandlerOpenWindowCalled();
bool skipped_payment_request_ui() { return skipped_payment_request_ui_; }
- bool is_show_user_gesture() const { return is_show_user_gesture_; }
SPCTransactionMode spc_transaction_mode() const {
return spc_transaction_mode_;
}
@@ -255,11 +258,6 @@ class PaymentRequest : public content::DocumentService<mojom::PaymentRequest>,
// Whether a completion was already recorded for this Payment Request.
bool has_recorded_completion_ = false;
- // Whether PaymentRequest.show() was invoked with a user gesture.
- // TODO(crbug.com/825270): Remove this member now that user gesture is always
- // required for show().
- bool is_show_user_gesture_ = false;
-
// Whether PaymentRequest.show() was invoked by skipping payment request UI.
bool skipped_payment_request_ui_ = false;
diff --git a/chromium/components/payments/content/payment_request_dialog.h b/chromium/components/payments/content/payment_request_dialog.h
index 552cd821ba9..53f12c7b8a4 100644
--- a/chromium/components/payments/content/payment_request_dialog.h
+++ b/chromium/components/payments/content/payment_request_dialog.h
@@ -46,6 +46,10 @@ class PaymentRequestDialog {
// Confirms payment. Used only in tests.
virtual void ConfirmPaymentForTesting() = 0;
+
+ // Clicks the opt-out link. Returns true if the link was visible to the user,
+ // false otherwise. Used only in tests.
+ virtual bool ClickOptOutForTesting() = 0;
};
} // namespace payments
diff --git a/chromium/components/payments/content/payment_request_state.h b/chromium/components/payments/content/payment_request_state.h
index 589f6abe13b..3a243e123fe 100644
--- a/chromium/components/payments/content/payment_request_state.h
+++ b/chromium/components/payments/content/payment_request_state.h
@@ -299,10 +299,6 @@ class PaymentRequestState : public PaymentAppFactory::Delegate,
base::WeakPtr<PaymentRequestState> AsWeakPtr();
- void set_is_show_user_gesture(bool is_show_user_gesture) {
- is_show_user_gesture_ = is_show_user_gesture;
- }
-
private:
// Fetches the Autofill Profiles for this user from the PersonalDataManager,
// and stores copies of them, owned by this PaymentRequestState, in
@@ -416,9 +412,6 @@ class PaymentRequestState : public PaymentAppFactory::Delegate,
base::ObserverList<Observer>::Unchecked observers_;
- // Whether PaymentRequest.show() was invoked with a user gesture.
- bool is_show_user_gesture_ = false;
-
// If set to true, then both GetCanMakePaymentValue() and
// GetHasEnrolledInstrumentValue() will return true, regardless of presence of
// payment apps. This is used by secure payment confirmation, where
diff --git a/chromium/components/payments/content/payment_request_state_unittest.cc b/chromium/components/payments/content/payment_request_state_unittest.cc
index b499886ee98..77c3033141a 100644
--- a/chromium/components/payments/content/payment_request_state_unittest.cc
+++ b/chromium/components/payments/content/payment_request_state_unittest.cc
@@ -149,7 +149,7 @@ class PaymentRequestStateTestBase : public testing::Test,
/*observer=*/nullptr, "en-US");
PaymentAppServiceFactory::SetForTesting(std::move(app_service));
state_ = std::make_unique<PaymentRequestState>(
- web_contents_->GetMainFrame(), GURL("https://example.com"),
+ web_contents_->GetPrimaryMainFrame(), GURL("https://example.com"),
GURL("https://example.com/pay"),
url::Origin::Create(GURL("https://example.com")), spec_->AsWeakPtr(),
weak_ptr_factory_.GetWeakPtr(), "en-US", &test_personal_data_manager_,
diff --git a/chromium/components/payments/content/payment_request_web_contents_manager_unittest.cc b/chromium/components/payments/content/payment_request_web_contents_manager_unittest.cc
index ca92af9e78a..b2a0923a3c3 100644
--- a/chromium/components/payments/content/payment_request_web_contents_manager_unittest.cc
+++ b/chromium/components/payments/content/payment_request_web_contents_manager_unittest.cc
@@ -37,7 +37,7 @@ class PaymentRequestWebContentsManagerTest : public testing::Test {
// PaymentRequest is a DocumentService, whose lifetime is managed by the
// RenderFrameHost passed in here.
- return new PaymentRequest(web_contents()->GetMainFrame(),
+ return new PaymentRequest(web_contents()->GetPrimaryMainFrame(),
std::move(delegate), std::move(display_manager),
std::move(receiver), mode,
/*observer_for_testing=*/nullptr);
diff --git a/chromium/components/payments/content/secure_payment_confirmation_app.cc b/chromium/components/payments/content/secure_payment_confirmation_app.cc
index 0c1868a98f6..ce6f5020c02 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_app.cc
+++ b/chromium/components/payments/content/secure_payment_confirmation_app.cc
@@ -106,6 +106,9 @@ void SecurePaymentConfirmationApp::InvokePaymentApp(
options->allow_credentials = std::move(credentials);
options->challenge = request_->challenge;
+ // TODO(crbug.com/1325854): The 'showOptOut' flag status must also be signed
+ // in the assertion, so that the verifier can check that the caller offered
+ // the experience if desired.
authenticator_->SetPaymentOptions(blink::mojom::PaymentOptions::New(
spec_->GetTotal(/*selected_app=*/this)->amount.Clone(),
request_->instrument.Clone(), request_->payee_name,
diff --git a/chromium/components/payments/content/secure_payment_confirmation_app_factory.cc b/chromium/components/payments/content/secure_payment_confirmation_app_factory.cc
index 3edb36a0ebc..c2591119404 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_app_factory.cc
+++ b/chromium/components/payments/content/secure_payment_confirmation_app_factory.cc
@@ -285,11 +285,7 @@ void SecurePaymentConfirmationAppFactory::OnAppIcon(
// this check ahead of checking whether any credential matched, as otherwise
// an attacker could deliberately pass an invalid icon and do a timing
// attack to see if a credential matches.
- // Before SecurePaymentConfirmationAPIV3, the iconMustBeShown option is not
- // available and we always reject in this case.
- if (!base::FeatureList::IsEnabled(
- features::kSecurePaymentConfirmationAPIV3) ||
- request->mojo_request->instrument->iconMustBeShown) {
+ if (request->mojo_request->instrument->iconMustBeShown) {
request->delegate->OnPaymentAppCreationError(
errors::kInvalidIcon, AppCreationFailureReason::ICON_DOWNLOAD_FAILED);
request->delegate->OnDoneCreatingPaymentApps();
diff --git a/chromium/components/payments/content/secure_payment_confirmation_app_unittest.cc b/chromium/components/payments/content/secure_payment_confirmation_app_unittest.cc
index 2b0c53a0709..97a1d9f15ac 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_app_unittest.cc
+++ b/chromium/components/payments/content/secure_payment_confirmation_app_unittest.cc
@@ -56,7 +56,7 @@ class MockAuthenticator : public webauthn::InternalAuthenticator {
MOCK_METHOD1(VerifyChallenge, void(const std::vector<uint8_t>&));
content::RenderFrameHost* GetRenderFrameHost() override {
- return web_contents_->GetMainFrame();
+ return web_contents_->GetPrimaryMainFrame();
}
// Implements an webauthn::InternalAuthenticator method to delegate fields of
diff --git a/chromium/components/payments/content/secure_payment_confirmation_controller.cc b/chromium/components/payments/content/secure_payment_confirmation_controller.cc
index 4486a3e1c39..d59c32d1ad7 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_controller.cc
+++ b/chromium/components/payments/content/secure_payment_confirmation_controller.cc
@@ -140,6 +140,21 @@ void SecurePaymentConfirmationController::
request_->state()->GetApplicationLocale())
.Format(total->amount->value)}));
+ model_.set_opt_out_visible(request_->spec()
+ ->method_data()
+ .front()
+ ->secure_payment_confirmation->show_opt_out);
+ model_.set_opt_out_label(
+ l10n_util::GetStringUTF16(IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LABEL));
+ model_.set_opt_out_link_label(l10n_util::GetStringUTF16(
+ IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LINK_LABEL));
+
+ model_.set_relying_party_id(
+ base::UTF8ToUTF16(request_->spec()
+ ->method_data()
+ .front()
+ ->secure_payment_confirmation->rp_id));
+
view_ = SecurePaymentConfirmationView::Create(
request_->state()->GetPaymentRequestDelegate()->GetPaymentUIObserver());
view_->ShowDialog(
@@ -147,6 +162,8 @@ void SecurePaymentConfirmationController::
base::BindOnce(&SecurePaymentConfirmationController::OnConfirm,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&SecurePaymentConfirmationController::OnCancel,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::BindOnce(&SecurePaymentConfirmationController::OnOptOut,
weak_ptr_factory_.GetWeakPtr()));
// For automated testing, SPC can be placed in an 'autoaccept' or
@@ -212,16 +229,18 @@ void SecurePaymentConfirmationController::ConfirmPaymentForTesting() {
OnConfirm();
}
+bool SecurePaymentConfirmationController::ClickOptOutForTesting() {
+ // This should only be called when the view is showing.
+ DCHECK(view_);
+ return view_->ClickOptOutForTesting();
+}
+
void SecurePaymentConfirmationController::OnInitialized(
InitializationTask* initialization_task) {
if (--number_of_initialization_tasks_ == 0)
SetupModelAndShowDialogIfApplicable();
}
-void SecurePaymentConfirmationController::OnDismiss() {
- OnCancel();
-}
-
void SecurePaymentConfirmationController::OnCancel() {
CloseDialog();
@@ -232,6 +251,16 @@ void SecurePaymentConfirmationController::OnCancel() {
FROM_HERE, base::BindOnce(&PaymentRequest::OnUserCancelled, request_));
}
+void SecurePaymentConfirmationController::OnOptOut() {
+ CloseDialog();
+
+ if (!request_)
+ return;
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&PaymentRequest::OnUserOptedOut, request_));
+}
+
void SecurePaymentConfirmationController::OnConfirm() {
if (!request_)
return;
diff --git a/chromium/components/payments/content/secure_payment_confirmation_controller.h b/chromium/components/payments/content/secure_payment_confirmation_controller.h
index d2530fc861d..6b7400471a2 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_controller.h
+++ b/chromium/components/payments/content/secure_payment_confirmation_controller.h
@@ -45,14 +45,15 @@ class SecurePaymentConfirmationController
const GURL& url,
PaymentHandlerOpenWindowCallback callback) override;
void ConfirmPaymentForTesting() override;
+ bool ClickOptOutForTesting() override;
// InitializationTask::Observer:
void OnInitialized(InitializationTask* initialization_task) override;
// Callbacks for user interaction.
- void OnDismiss();
void OnCancel();
void OnConfirm();
+ void OnOptOut();
base::WeakPtr<SecurePaymentConfirmationController> GetWeakPtr();
diff --git a/chromium/components/payments/content/secure_payment_confirmation_model.h b/chromium/components/payments/content/secure_payment_confirmation_model.h
index 5abcea16346..223209a55f3 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_model.h
+++ b/chromium/components/payments/content/secure_payment_confirmation_model.h
@@ -130,6 +130,28 @@ class SecurePaymentConfirmationModel {
cancel_button_visible_ = cancel_button_visible;
}
+ // Opt Out text visibility and label.
+ bool opt_out_visible() const { return opt_out_visible_; }
+ void set_opt_out_visible(const bool opt_out_visible) {
+ opt_out_visible_ = opt_out_visible;
+ }
+ const std::u16string& opt_out_label() const { return opt_out_label_; }
+ void set_opt_out_label(const std::u16string& opt_out_label) {
+ opt_out_label_ = opt_out_label;
+ }
+ const std::u16string& opt_out_link_label() const {
+ return opt_out_link_label_;
+ }
+ void set_opt_out_link_label(const std::u16string& opt_out_link_label) {
+ opt_out_link_label_ = opt_out_link_label;
+ }
+
+ // Relying Party id (origin); used in the opt out dialog.
+ const std::u16string& relying_party_id() const { return relying_party_id_; }
+ void set_relying_party_id(const std::u16string& relying_party_id) {
+ relying_party_id_ = relying_party_id;
+ }
+
base::WeakPtr<SecurePaymentConfirmationModel> GetWeakPtr();
private:
@@ -157,6 +179,12 @@ class SecurePaymentConfirmationModel {
bool cancel_button_enabled_ = true;
bool cancel_button_visible_ = true;
+ bool opt_out_visible_ = false;
+ std::u16string opt_out_label_;
+ std::u16string opt_out_link_label_;
+
+ std::u16string relying_party_id_;
+
base::WeakPtrFactory<SecurePaymentConfirmationModel> weak_ptr_factory_{this};
};
diff --git a/chromium/components/payments/content/secure_payment_confirmation_model_unittest.cc b/chromium/components/payments/content/secure_payment_confirmation_model_unittest.cc
index f1fc0cb0d91..4e5fabdea02 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_model_unittest.cc
+++ b/chromium/components/payments/content/secure_payment_confirmation_model_unittest.cc
@@ -29,6 +29,9 @@ TEST_F(SecurePaymentConfirmationModelTest, SmokeTest) {
std::u16string total_value(u"$20.00 USD");
std::u16string verify_button_label(u"Verify");
std::u16string cancel_button_label(u"Cancel");
+ std::u16string opt_out_label(u"Opt Out");
+ std::u16string opt_out_link_text(u"Opt Out Link");
+ std::u16string relying_party_id(u"example.com");
model.set_title(title);
EXPECT_EQ(title, model.title());
@@ -63,24 +66,36 @@ TEST_F(SecurePaymentConfirmationModelTest, SmokeTest) {
model.set_cancel_button_label(cancel_button_label);
EXPECT_EQ(cancel_button_label, model.cancel_button_label());
+ model.set_opt_out_label(opt_out_label);
+ EXPECT_EQ(opt_out_label, model.opt_out_label());
+
+ model.set_opt_out_link_label(opt_out_link_text);
+ EXPECT_EQ(opt_out_link_text, model.opt_out_link_label());
+
+ model.set_relying_party_id(relying_party_id);
+ EXPECT_EQ(relying_party_id, model.relying_party_id());
+
// Default values for visibility and enabled states
EXPECT_FALSE(model.progress_bar_visible());
EXPECT_TRUE(model.verify_button_enabled());
EXPECT_TRUE(model.verify_button_visible());
EXPECT_TRUE(model.cancel_button_enabled());
EXPECT_TRUE(model.cancel_button_visible());
+ EXPECT_FALSE(model.opt_out_visible());
model.set_progress_bar_visible(true);
model.set_verify_button_enabled(false);
model.set_verify_button_visible(false);
model.set_cancel_button_enabled(false);
model.set_cancel_button_visible(false);
+ model.set_opt_out_visible(true);
EXPECT_TRUE(model.progress_bar_visible());
EXPECT_FALSE(model.verify_button_enabled());
EXPECT_FALSE(model.verify_button_visible());
EXPECT_FALSE(model.cancel_button_enabled());
EXPECT_FALSE(model.cancel_button_visible());
+ EXPECT_TRUE(model.opt_out_visible());
}
} // namespace payments
diff --git a/chromium/components/payments/content/secure_payment_confirmation_no_creds.cc b/chromium/components/payments/content/secure_payment_confirmation_no_creds.cc
index bcdfec48146..ad5dc41645d 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_no_creds.cc
+++ b/chromium/components/payments/content/secure_payment_confirmation_no_creds.cc
@@ -6,6 +6,7 @@
#include <memory>
+#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/payments/content/secure_payment_confirmation_no_creds_view.h"
#include "components/strings/grit/components_strings.h"
@@ -29,17 +30,26 @@ SecurePaymentConfirmationNoCreds::~SecurePaymentConfirmationNoCreds() {
void SecurePaymentConfirmationNoCreds::ShowDialog(
content::WebContents* web_contents,
const std::u16string& merchant_name,
- ResponseCallback response_callback) {
+ const std::string& rp_id,
+ ResponseCallback response_callback,
+ OptOutCallback opt_out_callback) {
#if BUILDFLAG(IS_ANDROID)
NOTREACHED();
#endif // BUILDFLAG(IS_ANDROID)
DCHECK(!view_);
+ model_.set_no_creds_text(l10n_util::GetStringFUTF16(
+ IDS_NO_MATCHING_CREDENTIAL_DESCRIPTION, merchant_name));
+ model_.set_opt_out_visible(!opt_out_callback.is_null());
+ model_.set_opt_out_label(
+ l10n_util::GetStringUTF16(IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LABEL));
+ model_.set_opt_out_link_label(l10n_util::GetStringUTF16(
+ IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LINK_LABEL));
+ model_.set_relying_party_id(base::UTF8ToUTF16(rp_id));
+
view_ = SecurePaymentConfirmationNoCredsView::Create();
- view_->ShowDialog(web_contents,
- l10n_util::GetStringFUTF16(
- IDS_NO_MATCHING_CREDENTIAL_DESCRIPTION, merchant_name),
- std::move(response_callback));
+ view_->ShowDialog(web_contents, model_.GetWeakPtr(),
+ std::move(response_callback), std::move(opt_out_callback));
}
void SecurePaymentConfirmationNoCreds::CloseDialog() {
@@ -48,4 +58,10 @@ void SecurePaymentConfirmationNoCreds::CloseDialog() {
view_->HideDialog();
}
+bool SecurePaymentConfirmationNoCreds::ClickOptOutForTesting() {
+ if (!view_)
+ return false;
+ return view_->ClickOptOutForTesting();
+}
+
} // namespace payments
diff --git a/chromium/components/payments/content/secure_payment_confirmation_no_creds.h b/chromium/components/payments/content/secure_payment_confirmation_no_creds.h
index 57aa01953bb..fceef725477 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_no_creds.h
+++ b/chromium/components/payments/content/secure_payment_confirmation_no_creds.h
@@ -7,6 +7,7 @@
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
+#include "components/payments/content/secure_payment_confirmation_no_creds_model.h"
namespace content {
class WebContents;
@@ -21,6 +22,7 @@ class SecurePaymentConfirmationNoCredsView;
class SecurePaymentConfirmationNoCreds {
public:
using ResponseCallback = base::OnceClosure;
+ using OptOutCallback = base::OnceClosure;
SecurePaymentConfirmationNoCreds();
~SecurePaymentConfirmationNoCreds();
@@ -34,8 +36,11 @@ class SecurePaymentConfirmationNoCreds {
void ShowDialog(content::WebContents* web_contents,
const std::u16string& merchant_name,
- ResponseCallback response_callback);
+ const std::string& rp_id,
+ ResponseCallback response_callback,
+ OptOutCallback opt_out_callback);
void CloseDialog();
+ bool ClickOptOutForTesting();
private:
// On desktop, the SecurePaymentConfirmationNoCredsView object is memory
@@ -43,6 +48,8 @@ class SecurePaymentConfirmationNoCreds {
// and views::DialogDelegateView::DeleteDelegate() is called by its
// corresponding views::Widget.
base::WeakPtr<SecurePaymentConfirmationNoCredsView> view_;
+
+ SecurePaymentConfirmationNoCredsModel model_;
};
} // namespace payments
diff --git a/chromium/components/payments/content/secure_payment_confirmation_no_creds_model.cc b/chromium/components/payments/content/secure_payment_confirmation_no_creds_model.cc
new file mode 100644
index 00000000000..0057ba999cc
--- /dev/null
+++ b/chromium/components/payments/content/secure_payment_confirmation_no_creds_model.cc
@@ -0,0 +1,20 @@
+// Copyright 2022 The Chromium 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/payments/content/secure_payment_confirmation_no_creds_model.h"
+
+namespace payments {
+
+SecurePaymentConfirmationNoCredsModel::SecurePaymentConfirmationNoCredsModel() =
+ default;
+
+SecurePaymentConfirmationNoCredsModel::
+ ~SecurePaymentConfirmationNoCredsModel() = default;
+
+base::WeakPtr<SecurePaymentConfirmationNoCredsModel>
+SecurePaymentConfirmationNoCredsModel::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/content/secure_payment_confirmation_no_creds_model.h b/chromium/components/payments/content/secure_payment_confirmation_no_creds_model.h
new file mode 100644
index 00000000000..f05db98e384
--- /dev/null
+++ b/chromium/components/payments/content/secure_payment_confirmation_no_creds_model.h
@@ -0,0 +1,71 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PAYMENTS_CONTENT_SECURE_PAYMENT_CONFIRMATION_NO_CREDS_MODEL_H_
+#define COMPONENTS_PAYMENTS_CONTENT_SECURE_PAYMENT_CONFIRMATION_NO_CREDS_MODEL_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+
+namespace payments {
+
+// The data model for the secure payment confirmation 'no matching credentials'
+// dialog.
+class SecurePaymentConfirmationNoCredsModel {
+ public:
+ SecurePaymentConfirmationNoCredsModel();
+ ~SecurePaymentConfirmationNoCredsModel();
+
+ // Disallow copy and assign.
+ SecurePaymentConfirmationNoCredsModel(
+ const SecurePaymentConfirmationNoCredsModel& other) = delete;
+ SecurePaymentConfirmationNoCredsModel& operator=(
+ const SecurePaymentConfirmationNoCredsModel& other) = delete;
+
+ const std::u16string& no_creds_text() const { return no_creds_text_; }
+ void set_no_creds_text(const std::u16string& no_creds_text) {
+ no_creds_text_ = no_creds_text;
+ }
+
+ // Opt Out text visibility and label.
+ bool opt_out_visible() const { return opt_out_visible_; }
+ void set_opt_out_visible(const bool opt_out_visible) {
+ opt_out_visible_ = opt_out_visible;
+ }
+ const std::u16string& opt_out_label() const { return opt_out_label_; }
+ void set_opt_out_label(const std::u16string& opt_out_label) {
+ opt_out_label_ = opt_out_label;
+ }
+ const std::u16string& opt_out_link_label() const {
+ return opt_out_link_label_;
+ }
+ void set_opt_out_link_label(const std::u16string& opt_out_link_label) {
+ opt_out_link_label_ = opt_out_link_label;
+ }
+
+ // Relying Party id (origin); used in the opt out dialog.
+ const std::u16string& relying_party_id() const { return relying_party_id_; }
+ void set_relying_party_id(const std::u16string& relying_party_id) {
+ relying_party_id_ = relying_party_id;
+ }
+
+ base::WeakPtr<SecurePaymentConfirmationNoCredsModel> GetWeakPtr();
+
+ private:
+ std::u16string no_creds_text_;
+
+ bool opt_out_visible_ = false;
+ std::u16string opt_out_label_;
+ std::u16string opt_out_link_label_;
+
+ std::u16string relying_party_id_;
+
+ base::WeakPtrFactory<SecurePaymentConfirmationNoCredsModel> weak_ptr_factory_{
+ this};
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CONTENT_SECURE_PAYMENT_CONFIRMATION_NO_CREDS_MODEL_H_
diff --git a/chromium/components/payments/content/secure_payment_confirmation_no_creds_model_unittest.cc b/chromium/components/payments/content/secure_payment_confirmation_no_creds_model_unittest.cc
new file mode 100644
index 00000000000..e4ed10d5053
--- /dev/null
+++ b/chromium/components/payments/content/secure_payment_confirmation_no_creds_model_unittest.cc
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium 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/payments/content/secure_payment_confirmation_no_creds_model.h"
+
+#include <string>
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/vector_icon_types.h"
+
+namespace payments {
+
+class SecurePaymentConfirmationNoCredsModelTest : public testing::Test {};
+
+TEST_F(SecurePaymentConfirmationNoCredsModelTest, SmokeTest) {
+ base::WeakPtr<SecurePaymentConfirmationNoCredsModel> weak_ptr;
+ {
+ SecurePaymentConfirmationNoCredsModel model;
+
+ std::u16string no_creds_text(
+ u"example.com may need to take additional steps to verify your "
+ u"payment");
+ std::u16string opt_out_label(u"Opt Out");
+ std::u16string opt_out_link_text(u"Opt Out Link");
+ std::u16string relying_party_id(u"example.com");
+
+ model.set_no_creds_text(no_creds_text);
+ EXPECT_EQ(no_creds_text, model.no_creds_text());
+
+ model.set_opt_out_label(opt_out_label);
+ EXPECT_EQ(opt_out_label, model.opt_out_label());
+
+ model.set_opt_out_link_label(opt_out_link_text);
+ EXPECT_EQ(opt_out_link_text, model.opt_out_link_label());
+
+ model.set_relying_party_id(relying_party_id);
+ EXPECT_EQ(relying_party_id, model.relying_party_id());
+
+ // Opt out is not visible by default.
+ EXPECT_FALSE(model.opt_out_visible());
+ model.set_opt_out_visible(true);
+ EXPECT_TRUE(model.opt_out_visible());
+
+ weak_ptr = model.GetWeakPtr();
+ ASSERT_NE(nullptr, weak_ptr.get());
+ }
+ ASSERT_EQ(nullptr, weak_ptr.get());
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/content/secure_payment_confirmation_no_creds_view.h b/chromium/components/payments/content/secure_payment_confirmation_no_creds_view.h
index 0709aab841a..43f8b6ed9b4 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_no_creds_view.h
+++ b/chromium/components/payments/content/secure_payment_confirmation_no_creds_view.h
@@ -5,8 +5,6 @@
#ifndef COMPONENTS_PAYMENTS_CONTENT_SECURE_PAYMENT_CONFIRMATION_NO_CREDS_VIEW_H_
#define COMPONENTS_PAYMENTS_CONTENT_SECURE_PAYMENT_CONFIRMATION_NO_CREDS_VIEW_H_
-#include <string>
-
#include "base/callback_forward.h"
#include "base/memory/weak_ptr.h"
@@ -16,20 +14,26 @@ class WebContents;
namespace payments {
+class SecurePaymentConfirmationNoCredsModel;
+
// Draws the user interface in the payment credential enrollment no matching
// credentials flow.
class SecurePaymentConfirmationNoCredsView {
public:
using ResponseCallback = base::OnceClosure;
+ using OptOutCallback = base::OnceClosure;
static base::WeakPtr<SecurePaymentConfirmationNoCredsView> Create();
virtual ~SecurePaymentConfirmationNoCredsView() = 0;
- virtual void ShowDialog(content::WebContents* web_contents,
- const std::u16string& no_creds_text,
- ResponseCallback response_callback) = 0;
+ virtual void ShowDialog(
+ content::WebContents* web_contents,
+ base::WeakPtr<SecurePaymentConfirmationNoCredsModel> model,
+ ResponseCallback response_callback,
+ OptOutCallback opt_out_callback) = 0;
virtual void HideDialog() = 0;
+ virtual bool ClickOptOutForTesting() = 0;
protected:
SecurePaymentConfirmationNoCredsView();
diff --git a/chromium/components/payments/content/secure_payment_confirmation_view.h b/chromium/components/payments/content/secure_payment_confirmation_view.h
index f052395233d..6e30ea07f45 100644
--- a/chromium/components/payments/content/secure_payment_confirmation_view.h
+++ b/chromium/components/payments/content/secure_payment_confirmation_view.h
@@ -24,7 +24,8 @@ enum class SecurePaymentConfirmationAuthenticationDialogResult {
kCanceled = 0,
kAccepted = 1,
kClosed = 2,
- kMaxValue = kClosed,
+ kOptOut = 3,
+ kMaxValue = kOptOut,
};
// Draws the user interface in the secure payment confirmation flow. Owned by
@@ -33,6 +34,7 @@ class SecurePaymentConfirmationView {
public:
using VerifyCallback = base::OnceCallback<void()>;
using CancelCallback = base::OnceCallback<void()>;
+ using OptOutCallback = base::OnceCallback<void()>;
static base::WeakPtr<SecurePaymentConfirmationView> Create(
const base::WeakPtr<PaymentUIObserver> payment_ui_observer);
@@ -42,9 +44,11 @@ class SecurePaymentConfirmationView {
virtual void ShowDialog(content::WebContents* web_contents,
base::WeakPtr<SecurePaymentConfirmationModel> model,
VerifyCallback verify_callback,
- CancelCallback cancel_callback) = 0;
+ CancelCallback cancel_callback,
+ OptOutCallback opt_out_callback) = 0;
virtual void OnModelUpdated() = 0;
virtual void HideDialog() = 0;
+ virtual bool ClickOptOutForTesting() = 0;
protected:
SecurePaymentConfirmationView();
diff --git a/chromium/components/payments/content/test_content_payment_request_delegate.cc b/chromium/components/payments/content/test_content_payment_request_delegate.cc
index 9d858b16125..fc8f283b9c0 100644
--- a/chromium/components/payments/content/test_content_payment_request_delegate.cc
+++ b/chromium/components/payments/content/test_content_payment_request_delegate.cc
@@ -155,6 +155,8 @@ TestContentPaymentRequestDelegate::GetPaymentUIObserver() const {
void TestContentPaymentRequestDelegate::ShowNoMatchingPaymentCredentialDialog(
const std::u16string& merchant_name,
- base::OnceClosure response_callback) {}
+ const std::string& rp_id,
+ base::OnceClosure response_callback,
+ base::OnceClosure opt_out_callback) {}
} // namespace payments
diff --git a/chromium/components/payments/content/test_content_payment_request_delegate.h b/chromium/components/payments/content/test_content_payment_request_delegate.h
index e15ae6bafb3..66640401e9a 100644
--- a/chromium/components/payments/content/test_content_payment_request_delegate.h
+++ b/chromium/components/payments/content/test_content_payment_request_delegate.h
@@ -78,7 +78,9 @@ class TestContentPaymentRequestDelegate : public ContentPaymentRequestDelegate {
const base::WeakPtr<PaymentUIObserver> GetPaymentUIObserver() const override;
void ShowNoMatchingPaymentCredentialDialog(
const std::u16string& merchant_name,
- base::OnceClosure response_callback) override;
+ const std::string& rp_id,
+ base::OnceClosure response_callback,
+ base::OnceClosure opt_out_callback) override;
private:
TestPaymentRequestDelegate core_delegate_;
diff --git a/chromium/components/payments/core/error_strings.cc b/chromium/components/payments/core/error_strings.cc
index aaccb971bd5..4a26ea50ed7 100644
--- a/chromium/components/payments/core/error_strings.cc
+++ b/chromium/components/payments/core/error_strings.cc
@@ -38,6 +38,7 @@ const char kStrictBasicCardShowReject[] = "User does not have valid information
const char kTotalRequired[] = "Total required.";
const char kUserCancelled[] = "User closed the Payment Request UI.";
const char kWebAuthnOperationTimedOutOrNotAllowed[] = "The operation either timed out or was not allowed. See: https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.";
+const char kSpcUserOptedOut[] = "User opted out of the process.";
const char kInvalidPaymentDetails[] = "Invalid payment details.";
const char kInvalidPaymentOptions[] = "Invalid payment options.";
// clang-format on
diff --git a/chromium/components/payments/core/error_strings.h b/chromium/components/payments/core/error_strings.h
index 12ddd29b952..5383e4711bb 100644
--- a/chromium/components/payments/core/error_strings.h
+++ b/chromium/components/payments/core/error_strings.h
@@ -105,6 +105,9 @@ extern const char kUserCancelled[];
// credentials
extern const char kWebAuthnOperationTimedOutOrNotAllowed[];
+// Used when the user opts out of SPC for a given RP.
+extern const char kSpcUserOptedOut[];
+
// Used when the renderer does not provide valid payment details, such as a null
// struct or missing ID or total.
extern const char kInvalidPaymentDetails[];
diff --git a/chromium/components/payments_strings.grdp b/chromium/components/payments_strings.grdp
index 65fd678d7ec..71fac4e8e71 100644
--- a/chromium/components/payments_strings.grdp
+++ b/chromium/components/payments_strings.grdp
@@ -610,16 +610,25 @@
<message name="IDS_SECURE_PAYMENT_CONFIRMATION_VERIFY_PURCHASE" desc="The title string on a payment confirmation dialog that confirms the details of the payment that the user is about to verify. The user has the option to click 'Cancel' or 'Continue', and clicking 'Continue' will open the system interface to verify their identity, for example with their fingerprint." formatter_data="android_java">
Verify your purchase
</message>
+ <message name="IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LABEL" desc="Text on a dialog that explains to the user that they previously opted into this behavior for a given provider. Includes link text that they can click to request that the provider deletes their data.">
+ You chose to verify with an authenticator device on websites that use <ph name="PROVIDER_ORIGIN">$1<ex>example.com</ex></ph>. This provider may have stored information about your payment method, which you can <ph name="LINK_TEXT">$2<ex>request to be deleted</ex></ph>.
+ </message>
</if> <!-- not is_win and not is_macosx -->
<if expr="is_macosx">
<message name="IDS_SECURE_PAYMENT_CONFIRMATION_VERIFY_PURCHASE" desc="The title string on a payment confirmation dialog that confirms the details of the payment that the user is about to verify. The user has the option to click 'Cancel' or 'Verify', and clicking 'Verify' will open the system interface to verify their identity with the Touch ID fingerprint sensor.">
Use Touch ID to verify and complete your purchase?
</message>
+ <message name="IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LABEL" desc="Text on a dialog that explains to the user that they previously opted into this behavior for a given provider. Includes link text that they can click to request that the provider deletes their data.">
+ You chose to verify with Touch ID on websites that use <ph name="PROVIDER_ORIGIN">$1<ex>example.com</ex></ph>. This provider may have stored information about your payment method, which you can <ph name="LINK_TEXT">$2<ex>request to be deleted</ex></ph>.
+ </message>
</if> <!-- is_macosx -->
<if expr="is_win">
<message name="IDS_SECURE_PAYMENT_CONFIRMATION_VERIFY_PURCHASE" desc="The title string on a payment confirmation dialog that confirms the details of the payment that the user is about to verify. The user has the option to click 'Cancel' or 'Verify', and clicking 'Verify' will open the system interface to verify their identity with the Windows Hello biometric sensor, such as fingerprint or face recognition.">
Use Windows Hello to verify and complete your purchase?
</message>
+ <message name="IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LABEL" desc="Text on a dialog that explains to the user that they previously opted into this behavior for a given provider. Includes link text that they can click to request that the provider deletes their data.">
+ You chose to verify with Windows Hello on websites that use <ph name="PROVIDER_ORIGIN">$1<ex>example.com</ex></ph>. This provider may have stored information about your payment method, which you can <ph name="LINK_TEXT">$2<ex>request to be deleted</ex></ph>.
+ </message>
</if> <!-- is_win -->
<message name="IDS_SECURE_PAYMENT_CONFIRMATION_STORE_LABEL" desc="In a secure payment confirmation dialog, this is the label for the row of data that shows the store name, e.g. merchant.com." formatter_data="android_java">
Store
@@ -630,6 +639,9 @@
<message name="IDS_SECURE_PAYMENT_CONFIRMATION_VERIFY_BUTTON_LABEL" desc="As in 'Verify my identity', this is the label of the affirmitive button in a payment confirmation dialog that asks the user to verify their identity with a platform authenticator such as a fingerprint reader.">
Verify
</message>
+ <message name="IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LINK_LABEL" desc="Part of a longer text string where the user is being offered the choice to request that previously stored information on them be deleted. The longer text string is 'This provider may have stored information about your payment method, which you can [request to be deleted].'">
+ request to be deleted
+ </message>
<if expr="is_android">
<message name="IDS_NO_MATCHING_CREDENTIAL_DESCRIPTION" desc="Description on a dialog that appears when there are no matching payment credentials to authenticate. Informs users that additional steps are needed to verify the payment." formatter_data="android_java">
<ph name="URL">%1$s<ex>merchant.com</ex></ph> may need to take additional steps to verify your payment
diff --git a/chromium/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LABEL.png.sha1 b/chromium/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LABEL.png.sha1
new file mode 100644
index 00000000000..17aad716f15
--- /dev/null
+++ b/chromium/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LABEL.png.sha1
@@ -0,0 +1 @@
+dde017462d8fe2cc16af29c2e02eb981457a4ec5 \ No newline at end of file
diff --git a/chromium/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LINK_LABEL.png.sha1 b/chromium/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LINK_LABEL.png.sha1
new file mode 100644
index 00000000000..a8ff5860d29
--- /dev/null
+++ b/chromium/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_OPT_OUT_LINK_LABEL.png.sha1
@@ -0,0 +1 @@
+3a78515f80eebf6c1a19cd0d1f118e5583c3342a \ No newline at end of file
diff --git a/chromium/components/pdf/README.md b/chromium/components/pdf/README.md
index ebcbbb3562e..0ddfe9e0b21 100644
--- a/chromium/components/pdf/README.md
+++ b/chromium/components/pdf/README.md
@@ -1,7 +1,5 @@
The PDF component contains code necessary for using the PDF plugin in
-content-based clients. It provides an implementation for the PPB\_PDF PPAPI
-interface, and the necessary browser and renderer-side code for processing the
-relevant IPC messages. The PDF plugin code lives in `//pdf`.
-
-TODO(crbug.com/702993): Update description when the PDF plugin no longer uses
-PPAPI and is also no longer a plugin process.
+content-based clients. The PDF plugin code that lives in `//pdf` cannot depend
+on `//content` directly, so it uses a variety of delegate interfaces which are
+implement here. This component also contains code shared among content-based
+clients that should not live in `//chrome`.
diff --git a/chromium/components/pdf/browser/mock_url_loader_client.h b/chromium/components/pdf/browser/mock_url_loader_client.h
index 6d2f8ad17de..c379953caa5 100644
--- a/chromium/components/pdf/browser/mock_url_loader_client.h
+++ b/chromium/components/pdf/browser/mock_url_loader_client.h
@@ -48,10 +48,6 @@ class MockURLLoaderClient : public network::mojom::URLLoaderClient {
(int32_t transfer_size_diff),
(override));
MOCK_METHOD(void,
- OnStartLoadingResponseBody,
- (mojo::ScopedDataPipeConsumerHandle body),
- (override));
- MOCK_METHOD(void,
OnComplete,
(const network::URLLoaderCompletionStatus& status),
(override));
diff --git a/chromium/components/pdf/browser/pdf_web_contents_helper_browsertest.cc b/chromium/components/pdf/browser/pdf_web_contents_helper_browsertest.cc
index 02f603b6bfa..58e1be42d14 100644
--- a/chromium/components/pdf/browser/pdf_web_contents_helper_browsertest.cc
+++ b/chromium/components/pdf/browser/pdf_web_contents_helper_browsertest.cc
@@ -53,7 +53,7 @@ class TestPDFWebContentsHelperClient : public PDFWebContentsHelperClient {
// PDFWebContentsHelperClient:
content::RenderFrameHost* FindPdfFrame(
content::WebContents* contents) override {
- return contents->GetMainFrame();
+ return contents->GetPrimaryMainFrame();
}
void UpdateContentRestrictions(content::WebContents* contents,
diff --git a/chromium/components/pdf/browser/plugin_response_writer.cc b/chromium/components/pdf/browser/plugin_response_writer.cc
index 44ea938ad64..8c4af89bf46 100644
--- a/chromium/components/pdf/browser/plugin_response_writer.cc
+++ b/chromium/components/pdf/browser/plugin_response_writer.cc
@@ -100,8 +100,6 @@ void PluginResponseWriter::Start(base::OnceClosure done_callback) {
response->headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
response->mime_type = "text/html";
- client_->OnReceiveResponse(std::move(response),
- mojo::ScopedDataPipeConsumerHandle());
mojo::ScopedDataPipeProducerHandle producer;
mojo::ScopedDataPipeConsumerHandle consumer;
@@ -112,7 +110,7 @@ void PluginResponseWriter::Start(base::OnceClosure done_callback) {
return;
}
- client_->OnStartLoadingResponseBody(std::move(consumer));
+ client_->OnReceiveResponse(std::move(response), std::move(consumer));
producer_ = std::make_unique<mojo::DataPipeProducer>(std::move(producer));
diff --git a/chromium/components/pdf/browser/plugin_response_writer_unittest.cc b/chromium/components/pdf/browser/plugin_response_writer_unittest.cc
index 19f59446d2b..6cc52a98533 100644
--- a/chromium/components/pdf/browser/plugin_response_writer_unittest.cc
+++ b/chromium/components/pdf/browser/plugin_response_writer_unittest.cc
@@ -79,8 +79,9 @@ class BodyDrainer {
class PluginResponseWriterTest : public testing::Test {
protected:
PluginResponseWriterTest() {
- ON_CALL(mock_client_, OnStartLoadingResponseBody)
- .WillByDefault([this](mojo::ScopedDataPipeConsumerHandle body) {
+ ON_CALL(mock_client_, OnReceiveResponse)
+ .WillByDefault([this](network::mojom::URLResponseHeadPtr head,
+ mojo::ScopedDataPipeConsumerHandle body) {
body_drainer_ = std::make_unique<BodyDrainer>(std::move(body));
});
}
@@ -139,14 +140,13 @@ TEST_F(PluginResponseWriterTest, Start) {
testing::InSequence in_sequence;
EXPECT_CALL(mock_client_, OnReceiveResponse)
- .WillOnce([](network::mojom::URLResponseHeadPtr head,
- mojo::ScopedDataPipeConsumerHandle body) {
+ .WillOnce([this](network::mojom::URLResponseHeadPtr head,
+ mojo::ScopedDataPipeConsumerHandle body) {
EXPECT_EQ(200, head->headers->response_code());
EXPECT_EQ("text/html", head->mime_type);
+ body_drainer_ = std::make_unique<BodyDrainer>(std::move(body));
});
- EXPECT_CALL(mock_client_, OnStartLoadingResponseBody);
-
EXPECT_CALL(mock_client_, OnComplete)
.WillOnce(
[&run_loop](const network::URLLoaderCompletionStatus& status) {
diff --git a/chromium/components/pdf/renderer/BUILD.gn b/chromium/components/pdf/renderer/BUILD.gn
index 20b186ca8ff..d5e9d3f1eed 100644
--- a/chromium/components/pdf/renderer/BUILD.gn
+++ b/chromium/components/pdf/renderer/BUILD.gn
@@ -34,6 +34,7 @@ static_library("renderer") {
"//content/public/common",
"//gin",
"//ipc",
+ "//net",
"//pdf:accessibility",
"//pdf:buildflags",
"//pdf:features",
@@ -43,6 +44,8 @@ static_library("renderer") {
"//third_party/blink/public:blink",
"//third_party/blink/public/strings:accessibility_strings",
"//third_party/icu",
+ "//ui/display",
+ "//ui/gfx/geometry",
"//url",
"//v8",
]
diff --git a/chromium/components/pdf/renderer/DEPS b/chromium/components/pdf/renderer/DEPS
index 80d962d0a98..0fc18e1210f 100644
--- a/chromium/components/pdf/renderer/DEPS
+++ b/chromium/components/pdf/renderer/DEPS
@@ -4,6 +4,7 @@ include_rules = [
"+components/printing/renderer/print_render_frame_helper.h",
"+components/strings/grit/components_strings.h",
"+mojo/public/cpp/bindings",
+ "+net",
"+pdf/accessibility_structs.h",
"+pdf/buildflags.h",
"+pdf/mojom/pdf.mojom.h",
@@ -16,4 +17,6 @@ include_rules = [
"+third_party/skia/include/core",
"+ui/accessibility",
"+ui/base",
+ "+ui/display",
+ "+v8/include",
]
diff --git a/chromium/components/pdf/renderer/internal_plugin_renderer_helpers.cc b/chromium/components/pdf/renderer/internal_plugin_renderer_helpers.cc
index 9ac46449868..e4823e7fff5 100644
--- a/chromium/components/pdf/renderer/internal_plugin_renderer_helpers.cc
+++ b/chromium/components/pdf/renderer/internal_plugin_renderer_helpers.cc
@@ -67,12 +67,12 @@ blink::WebPlugin* CreateInternalPlugin(
CHECK(!delegate->IsAllowedOrigin(frame->GetSecurityOrigin()));
CHECK(parent_frame->IsWebRemoteFrame());
- mojo::AssociatedRemote<pdf::mojom::PdfService> pdf_service_remote;
+ mojo::AssociatedRemote<pdf::mojom::PdfService> pdf_service;
render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
- pdf_service_remote.BindNewEndpointAndPassReceiver());
+ pdf_service.BindNewEndpointAndPassReceiver());
return new chrome_pdf::PdfViewWebPlugin(
std::make_unique<PdfViewWebPluginClient>(render_frame),
- std::move(pdf_service_remote), params);
+ std::move(pdf_service), params);
}
} // namespace pdf
diff --git a/chromium/components/pdf/renderer/pdf_accessibility_tree.cc b/chromium/components/pdf/renderer/pdf_accessibility_tree.cc
index c1f00e891d2..6faecc062b7 100644
--- a/chromium/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/chromium/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -7,11 +7,15 @@
#include <algorithm>
#include <utility>
+#include "base/bind.h"
+#include "base/callback.h"
#include "base/i18n/break_iterator.h"
+#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/strings/utf_string_conversion_utils.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "components/pdf/renderer/pdf_ax_action_target.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/renderer/render_accessibility.h"
@@ -1293,6 +1297,16 @@ bool PdfAccessibilityTree::IsDataFromPluginValid(
}
void PdfAccessibilityTree::SetAccessibilityViewportInfo(
+ chrome_pdf::AccessibilityViewportInfo viewport_info) {
+ // This call may trigger layout, and ultimately self-deletion; see
+ // crbug.com/1274376 for details.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PdfAccessibilityTree::DoSetAccessibilityViewportInfo,
+ weak_ptr_factory_.GetWeakPtr(), std::move(viewport_info)));
+}
+
+void PdfAccessibilityTree::DoSetAccessibilityViewportInfo(
const chrome_pdf::AccessibilityViewportInfo& viewport_info) {
zoom_ = viewport_info.zoom;
scale_ = viewport_info.scale;
@@ -1319,6 +1333,16 @@ void PdfAccessibilityTree::SetAccessibilityViewportInfo(
}
void PdfAccessibilityTree::SetAccessibilityDocInfo(
+ chrome_pdf::AccessibilityDocInfo doc_info) {
+ // This call may trigger layout, and ultimately self-deletion; see
+ // crbug.com/1274376 for details.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PdfAccessibilityTree::DoSetAccessibilityDocInfo,
+ weak_ptr_factory_.GetWeakPtr(), std::move(doc_info)));
+}
+
+void PdfAccessibilityTree::DoSetAccessibilityDocInfo(
const chrome_pdf::AccessibilityDocInfo& doc_info) {
content::RenderAccessibility* render_accessibility =
GetRenderAccessibilityIfEnabled();
@@ -1342,6 +1366,21 @@ void PdfAccessibilityTree::SetAccessibilityDocInfo(
}
void PdfAccessibilityTree::SetAccessibilityPageInfo(
+ chrome_pdf::AccessibilityPageInfo page_info,
+ std::vector<chrome_pdf::AccessibilityTextRunInfo> text_runs,
+ std::vector<chrome_pdf::AccessibilityCharInfo> chars,
+ chrome_pdf::AccessibilityPageObjects page_objects) {
+ // This call may trigger layout, and ultimately self-deletion; see
+ // crbug.com/1274376 for details.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PdfAccessibilityTree::DoSetAccessibilityPageInfo,
+ weak_ptr_factory_.GetWeakPtr(), std::move(page_info),
+ std::move(text_runs), std::move(chars),
+ std::move(page_objects)));
+}
+
+void PdfAccessibilityTree::DoSetAccessibilityPageInfo(
const chrome_pdf::AccessibilityPageInfo& page_info,
const std::vector<chrome_pdf::AccessibilityTextRunInfo>& text_runs,
const std::vector<chrome_pdf::AccessibilityCharInfo>& chars,
diff --git a/chromium/components/pdf/renderer/pdf_accessibility_tree.h b/chromium/components/pdf/renderer/pdf_accessibility_tree.h
index 18a5e81578a..9506d33bb96 100644
--- a/chromium/components/pdf/renderer/pdf_accessibility_tree.h
+++ b/chromium/components/pdf/renderer/pdf_accessibility_tree.h
@@ -69,14 +69,14 @@ class PdfAccessibilityTree : public content::PluginAXTreeSource,
// chrome_pdf::PdfAccessibilityDataHandler:
void SetAccessibilityViewportInfo(
- const chrome_pdf::AccessibilityViewportInfo& viewport_info) override;
+ chrome_pdf::AccessibilityViewportInfo viewport_info) override;
void SetAccessibilityDocInfo(
- const chrome_pdf::AccessibilityDocInfo& doc_info) override;
+ chrome_pdf::AccessibilityDocInfo doc_info) override;
void SetAccessibilityPageInfo(
- const chrome_pdf::AccessibilityPageInfo& page_info,
- const std::vector<chrome_pdf::AccessibilityTextRunInfo>& text_runs,
- const std::vector<chrome_pdf::AccessibilityCharInfo>& chars,
- const chrome_pdf::AccessibilityPageObjects& page_objects) override;
+ chrome_pdf::AccessibilityPageInfo page_info,
+ std::vector<chrome_pdf::AccessibilityTextRunInfo> text_runs,
+ std::vector<chrome_pdf::AccessibilityCharInfo> chars,
+ chrome_pdf::AccessibilityPageObjects page_objects) override;
void HandleAction(const chrome_pdf::AccessibilityActionData& action_data);
absl::optional<AnnotationInfo> GetPdfAnnotationInfoFromAXNode(
@@ -118,6 +118,16 @@ class PdfAccessibilityTree : public content::PluginAXTreeSource,
// Update the AXTreeData when the selected range changed.
void UpdateAXTreeDataFromSelection();
+ void DoSetAccessibilityViewportInfo(
+ const chrome_pdf::AccessibilityViewportInfo& viewport_info);
+ void DoSetAccessibilityDocInfo(
+ const chrome_pdf::AccessibilityDocInfo& doc_info);
+ void DoSetAccessibilityPageInfo(
+ const chrome_pdf::AccessibilityPageInfo& page_info,
+ const std::vector<chrome_pdf::AccessibilityTextRunInfo>& text_runs,
+ const std::vector<chrome_pdf::AccessibilityCharInfo>& chars,
+ const chrome_pdf::AccessibilityPageObjects& page_objects);
+
// Given a 0-based page index and 0-based character index within a page,
// find the node ID of the associated static text AXNode, and the character
// index within that text node. Used to find the start and end of the
diff --git a/chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc b/chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
index ceab43fa399..88894ac670b 100644
--- a/chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
+++ b/chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
@@ -2,8 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/callback.h"
+#include "base/location.h"
#include "base/path_service.h"
+#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/pdf/renderer/pdf_accessibility_tree.h"
#include "components/strings/grit/components_strings.h"
@@ -103,6 +107,14 @@ class TestPdfAccessibilityActionHandler
chrome_pdf::AccessibilityActionData received_action_data_;
};
+// Waits for tasks posted to the thread's task runner to complete.
+void WaitForThreadTasks() {
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ run_loop.QuitClosure());
+ run_loop.Run();
+}
+
} // namespace
class PdfAccessibilityTreeTest : public content::RenderViewTest {
@@ -156,6 +168,7 @@ TEST_F(PdfAccessibilityTreeTest, TestEmptyPDFPage) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
EXPECT_EQ(ax::mojom::Role::kPdfRoot,
pdf_accessibility_tree.GetRoot()->GetRole());
@@ -171,6 +184,7 @@ TEST_F(PdfAccessibilityTreeTest, TestAccessibilityDisabledDuringPDFLoad) {
pdf_accessibility_tree.SetAccessibilityViewportInfo(viewport_info_);
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
+ WaitForThreadTasks();
// Disable accessibility while the PDF is loading, make sure this
// doesn't crash.
@@ -178,6 +192,7 @@ TEST_F(PdfAccessibilityTreeTest, TestAccessibilityDisabledDuringPDFLoad) {
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
}
TEST_F(PdfAccessibilityTreeTest, TestPdfAccessibilityTreeReload) {
@@ -201,6 +216,7 @@ TEST_F(PdfAccessibilityTreeTest, TestPdfAccessibilityTreeReload) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
ASSERT_TRUE(root_node);
@@ -263,6 +279,7 @@ TEST_F(PdfAccessibilityTreeTest, TestPdfAccessibilityTreeCreation) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
/*
* Expected tree structure
@@ -374,6 +391,7 @@ TEST_F(PdfAccessibilityTreeTest, TestOverlappingAnnots) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
/*
* Expected tree structure
@@ -459,6 +477,7 @@ TEST_F(PdfAccessibilityTreeTest, TestHighlightCreation) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
/*
* Expected tree structure
@@ -580,6 +599,7 @@ TEST_F(PdfAccessibilityTreeTest, TestTextFieldNodeCreation) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
/*
* Expected tree structure
@@ -736,6 +756,7 @@ TEST_F(PdfAccessibilityTreeTest, TestButtonNodeCreation) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
/*
* Expected tree structure
@@ -924,6 +945,7 @@ TEST_F(PdfAccessibilityTreeTest, TestListboxNodeCreation) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
/*
* Expected tree structure
@@ -1114,6 +1136,8 @@ TEST_F(PdfAccessibilityTreeTest, TestComboboxNodeCreation) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
/*
* Expected tree structure
* Document
@@ -1320,6 +1344,7 @@ TEST_F(PdfAccessibilityTreeTest, TestPreviousNextOnLine) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
/*
* Expected tree structure
@@ -1442,6 +1467,8 @@ TEST_F(PdfAccessibilityTreeTest, TextRunsAndCharsMismatch) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
// In case of invalid data, only the initialized data should be in the tree.
ASSERT_EQ(ax::mojom::Role::kUnknown,
pdf_accessibility_tree.GetRoot()->GetRole());
@@ -1486,6 +1513,8 @@ TEST_F(PdfAccessibilityTreeTest, UnsortedLinkVector) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
// In case of invalid data, only the initialized data should be in the tree.
ASSERT_EQ(ax::mojom::Role::kUnknown,
pdf_accessibility_tree.GetRoot()->GetRole());
@@ -1521,6 +1550,8 @@ TEST_F(PdfAccessibilityTreeTest, OutOfBoundLink) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
// In case of invalid data, only the initialized data should be in the tree.
ASSERT_EQ(ax::mojom::Role::kUnknown,
pdf_accessibility_tree.GetRoot()->GetRole());
@@ -1563,6 +1594,8 @@ TEST_F(PdfAccessibilityTreeTest, UnsortedImageVector) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
// In case of invalid data, only the initialized data should be in the tree.
ASSERT_EQ(ax::mojom::Role::kUnknown,
pdf_accessibility_tree.GetRoot()->GetRole());
@@ -1596,6 +1629,8 @@ TEST_F(PdfAccessibilityTreeTest, OutOfBoundImage) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
// In case of invalid data, only the initialized data should be in the tree.
ASSERT_EQ(ax::mojom::Role::kUnknown,
pdf_accessibility_tree.GetRoot()->GetRole());
@@ -1643,6 +1678,8 @@ TEST_F(PdfAccessibilityTreeTest, UnsortedHighlightVector) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
// In case of invalid data, only the initialized data should be in the tree.
ASSERT_EQ(ax::mojom::Role::kUnknown,
pdf_accessibility_tree.GetRoot()->GetRole());
@@ -1679,6 +1716,8 @@ TEST_F(PdfAccessibilityTreeTest, OutOfBoundHighlight) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
// In case of invalid data, only the initialized data should be in the tree.
ASSERT_EQ(ax::mojom::Role::kUnknown,
pdf_accessibility_tree.GetRoot()->GetRole());
@@ -1699,6 +1738,7 @@ TEST_F(PdfAccessibilityTreeTest, TestActionDataConversion) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
std::unique_ptr<ui::AXActionTarget> pdf_action_target =
@@ -1761,6 +1801,7 @@ TEST_F(PdfAccessibilityTreeTest, TestScrollToGlobalPointDataConversion) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
std::unique_ptr<ui::AXActionTarget> pdf_action_target =
@@ -1822,6 +1863,8 @@ TEST_F(PdfAccessibilityTreeTest, TestClickActionDataConversion) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
const std::vector<ui::AXNode*>& page_nodes = root_node->children();
ASSERT_EQ(1u, page_nodes.size());
@@ -1867,6 +1910,7 @@ TEST_F(PdfAccessibilityTreeTest, TestEmptyPdfAxActions) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
std::unique_ptr<ui::AXActionTarget> pdf_action_target =
@@ -1908,6 +1952,7 @@ TEST_F(PdfAccessibilityTreeTest, TestZoomAndScaleChanges) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
viewport_info_.zoom = 1.0;
viewport_info_.scale = 1.0;
@@ -1915,6 +1960,8 @@ TEST_F(PdfAccessibilityTreeTest, TestZoomAndScaleChanges) {
viewport_info_.offset = gfx::Point(57, 0);
pdf_accessibility_tree.SetAccessibilityViewportInfo(viewport_info_);
+ WaitForThreadTasks();
+
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
ASSERT_TRUE(root_node);
ASSERT_EQ(1u, root_node->children().size());
@@ -1935,6 +1982,7 @@ TEST_F(PdfAccessibilityTreeTest, TestZoomAndScaleChanges) {
viewport_info_.zoom = new_zoom;
viewport_info_.scale = new_device_scale;
pdf_accessibility_tree.SetAccessibilityViewportInfo(viewport_info_);
+ WaitForThreadTasks();
rect = para_node->data().relative_bounds.bounds;
transform = root_node->data().relative_bounds.transform.get();
@@ -1959,6 +2007,8 @@ TEST_F(PdfAccessibilityTreeTest, TestSelectionActionDataConversion) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
ASSERT_TRUE(root_node);
const std::vector<ui::AXNode*>& page_nodes = root_node->children();
@@ -2053,6 +2103,8 @@ TEST_F(PdfAccessibilityTreeTest, TestShowContextMenuAction) {
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
+ WaitForThreadTasks();
+
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
ASSERT_TRUE(root_node);
diff --git a/chromium/components/pdf/renderer/pdf_view_web_plugin_client.cc b/chromium/components/pdf/renderer/pdf_view_web_plugin_client.cc
index 05a48dd5703..d5c0436b9d9 100644
--- a/chromium/components/pdf/renderer/pdf_view_web_plugin_client.cc
+++ b/chromium/components/pdf/renderer/pdf_view_web_plugin_client.cc
@@ -4,12 +4,40 @@
#include "components/pdf/renderer/pdf_view_web_plugin_client.h"
-#include "base/check.h"
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/check_op.h"
+#include "base/values.h"
#include "components/pdf/renderer/pdf_accessibility_tree.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/v8_value_converter.h"
+#include "net/cookies/site_for_cookies.h"
#include "printing/buildflags/buildflags.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/public/web/web_associated_url_loader.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_dom_message_event.h"
#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_frame_widget.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_local_frame_client.h"
+#include "third_party/blink/public/web/web_plugin_container.h"
+#include "third_party/blink/public/web/web_serialized_script_value.h"
+#include "third_party/blink/public/web/web_view.h"
+#include "third_party/blink/public/web/web_widget.h"
+#include "ui/display/screen_info.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-value.h"
#if BUILDFLAG(ENABLE_PRINTING)
#include "components/printing/renderer/print_render_frame_helper.h"
@@ -20,7 +48,8 @@ namespace pdf {
PdfViewWebPluginClient::PdfViewWebPluginClient(
content::RenderFrame* render_frame)
: render_frame_(render_frame),
- v8_value_converter_(content::V8ValueConverter::Create()) {
+ v8_value_converter_(content::V8ValueConverter::Create()),
+ isolate_(blink::MainThreadIsolate()) {
DCHECK(render_frame_);
}
@@ -32,18 +61,188 @@ std::unique_ptr<base::Value> PdfViewWebPluginClient::FromV8Value(
return v8_value_converter_->FromV8Value(value, context);
}
-v8::Local<v8::Value> PdfViewWebPluginClient::ToV8Value(
- const base::Value& value,
- v8::Local<v8::Context> context) {
- return v8_value_converter_->ToV8Value(&value, context);
-}
-
base::WeakPtr<chrome_pdf::PdfViewWebPlugin::Client>
PdfViewWebPluginClient::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
-void PdfViewWebPluginClient::Print(const blink::WebElement& element) {
+void PdfViewWebPluginClient::SetPluginContainer(
+ blink::WebPluginContainer* container) {
+ plugin_container_ = container;
+}
+
+blink::WebPluginContainer* PdfViewWebPluginClient::PluginContainer() {
+ return plugin_container_;
+}
+
+net::SiteForCookies PdfViewWebPluginClient::SiteForCookies() const {
+ return plugin_container_->GetDocument().SiteForCookies();
+}
+
+blink::WebURL PdfViewWebPluginClient::CompleteURL(
+ const blink::WebString& partial_url) const {
+ return plugin_container_->GetDocument().CompleteURL(partial_url);
+}
+
+void PdfViewWebPluginClient::PostMessage(base::Value::Dict message) {
+ v8::Isolate::Scope isolate_scope(isolate_);
+ v8::HandleScope handle_scope(isolate_);
+ v8::Local<v8::Context> context = GetFrame()->MainWorldScriptContext();
+ DCHECK_EQ(isolate_, context->GetIsolate());
+ v8::Context::Scope context_scope(context);
+
+ base::Value message_as_value(std::move(message));
+ v8::Local<v8::Value> converted_message =
+ v8_value_converter_->ToV8Value(&message_as_value, context);
+
+ plugin_container_->EnqueueMessageEvent(
+ blink::WebSerializedScriptValue::Serialize(isolate_, converted_message));
+}
+
+void PdfViewWebPluginClient::Invalidate() {
+ plugin_container_->Invalidate();
+}
+
+void PdfViewWebPluginClient::RequestTouchEventType(
+ blink::WebPluginContainer::TouchEventRequestType request_type) {
+ plugin_container_->RequestTouchEventType(request_type);
+}
+
+void PdfViewWebPluginClient::ReportFindInPageMatchCount(int identifier,
+ int total,
+ bool final_update) {
+ plugin_container_->ReportFindInPageMatchCount(identifier, total,
+ final_update);
+}
+
+void PdfViewWebPluginClient::ReportFindInPageSelection(int identifier,
+ int index) {
+ plugin_container_->ReportFindInPageSelection(identifier, index);
+}
+
+void PdfViewWebPluginClient::ReportFindInPageTickmarks(
+ const std::vector<gfx::Rect>& tickmarks) {
+ blink::WebLocalFrame* frame = GetFrame();
+ if (frame) {
+ frame->SetTickmarks(blink::WebElement(),
+ blink::WebVector<gfx::Rect>(tickmarks));
+ }
+}
+
+float PdfViewWebPluginClient::DeviceScaleFactor() {
+ // Do not rely on `blink::WebPluginContainer::DeviceScaleFactor()`, since it
+ // doesn't always reflect the real screen's device scale. Instead, get the
+ // device scale from the top-level frame's `display::ScreenInfo`.
+ blink::WebWidget* widget = GetFrame()->LocalRoot()->FrameWidget();
+ return widget->GetOriginalScreenInfo().device_scale_factor;
+}
+
+gfx::PointF PdfViewWebPluginClient::GetScrollPosition() {
+ // Note that `blink::WebLocalFrame::GetScrollOffset()` actually returns a
+ // scroll position (a point relative to the top-left corner).
+ return GetFrame()->GetScrollOffset();
+}
+
+void PdfViewWebPluginClient::UsePluginAsFindHandler() {
+ plugin_container_->UsePluginAsFindHandler();
+}
+
+void PdfViewWebPluginClient::SetReferrerForRequest(
+ blink::WebURLRequest& request,
+ const blink::WebURL& referrer_url) {
+ GetFrame()->SetReferrerForRequest(request, referrer_url);
+}
+
+void PdfViewWebPluginClient::Alert(const blink::WebString& message) {
+ blink::WebLocalFrame* frame = GetFrame();
+ if (frame)
+ frame->Alert(message);
+}
+
+bool PdfViewWebPluginClient::Confirm(const blink::WebString& message) {
+ blink::WebLocalFrame* frame = GetFrame();
+ return frame && frame->Confirm(message);
+}
+
+blink::WebString PdfViewWebPluginClient::Prompt(
+ const blink::WebString& message,
+ const blink::WebString& default_value) {
+ blink::WebLocalFrame* frame = GetFrame();
+ return frame ? frame->Prompt(message, default_value) : blink::WebString();
+}
+
+void PdfViewWebPluginClient::TextSelectionChanged(
+ const blink::WebString& selection_text,
+ uint32_t offset,
+ const gfx::Range& range) {
+ // Focus the plugin's containing frame before changing the text selection.
+ // TODO(crbug.com/1234559): Would it make more sense not to change the text
+ // selection at all in this case? Maybe we only have this problem because we
+ // support a "selectAll" message.
+ blink::WebLocalFrame* frame = GetFrame();
+ frame->View()->SetFocusedFrame(frame);
+
+ frame->TextSelectionChanged(selection_text, offset, range);
+}
+
+std::unique_ptr<blink::WebAssociatedURLLoader>
+PdfViewWebPluginClient::CreateAssociatedURLLoader(
+ const blink::WebAssociatedURLLoaderOptions& options) {
+ return GetFrame()->CreateAssociatedURLLoader(options);
+}
+
+void PdfViewWebPluginClient::UpdateTextInputState() {
+ // `widget` is null in Print Preview.
+ auto* widget = GetFrame()->FrameWidget();
+ if (widget)
+ widget->UpdateTextInputState();
+}
+
+void PdfViewWebPluginClient::UpdateSelectionBounds() {
+ // `widget` is null in Print Preview.
+ auto* widget = GetFrame()->FrameWidget();
+ if (widget)
+ widget->UpdateSelectionBounds();
+}
+
+std::string PdfViewWebPluginClient::GetEmbedderOriginString() {
+ auto* frame = GetFrame();
+ if (!frame)
+ return {};
+
+ auto* parent_frame = frame->Parent();
+ if (!parent_frame)
+ return {};
+
+ return GURL(parent_frame->GetSecurityOrigin().ToString().Utf8()).spec();
+}
+
+bool PdfViewWebPluginClient::HasFrame() const {
+ return plugin_container_ && GetFrame();
+}
+
+blink::WebLocalFrame* PdfViewWebPluginClient::GetFrame() const {
+ return plugin_container_->GetDocument().GetFrame();
+}
+
+void PdfViewWebPluginClient::DidStartLoading() {
+ blink::WebLocalFrameClient* frame_client = GetFrame()->Client();
+ if (!frame_client)
+ return;
+
+ frame_client->DidStartLoading();
+}
+
+void PdfViewWebPluginClient::DidStopLoading() {
+ blink::WebLocalFrameClient* frame_client = GetFrame()->Client();
+ if (!frame_client)
+ return;
+
+ frame_client->DidStopLoading();
+}
+
+void PdfViewWebPluginClient::Print() {
+ blink::WebElement element = plugin_container_->GetElement();
DCHECK(!element.IsNull());
#if BUILDFLAG(ENABLE_PRINTING)
printing::PrintRenderFrameHelper::Get(render_frame_)->PrintNode(element);
diff --git a/chromium/components/pdf/renderer/pdf_view_web_plugin_client.h b/chromium/components/pdf/renderer/pdf_view_web_plugin_client.h
index 5ddf93d1d00..1a1bf3c6140 100644
--- a/chromium/components/pdf/renderer/pdf_view_web_plugin_client.h
+++ b/chromium/components/pdf/renderer/pdf_view_web_plugin_client.h
@@ -10,11 +10,20 @@
#include "base/memory/weak_ptr.h"
#include "pdf/pdf_view_web_plugin.h"
+namespace blink {
+class WebLocalFrame;
+class WebPluginContainer;
+} // namespace blink
+
namespace content {
class RenderFrame;
class V8ValueConverter;
} // namespace content
+namespace v8 {
+class Isolate;
+} // namespace v8
+
namespace pdf {
class PdfViewWebPluginClient : public chrome_pdf::PdfViewWebPlugin::Client {
@@ -28,19 +37,56 @@ class PdfViewWebPluginClient : public chrome_pdf::PdfViewWebPlugin::Client {
std::unique_ptr<base::Value> FromV8Value(
v8::Local<v8::Value> value,
v8::Local<v8::Context> context) override;
- v8::Local<v8::Value> ToV8Value(const base::Value& value,
- v8::Local<v8::Context> context) override;
base::WeakPtr<chrome_pdf::PdfViewWebPlugin::Client> GetWeakPtr() override;
- void Print(const blink::WebElement& element) override;
+ void SetPluginContainer(blink::WebPluginContainer* container) override;
+ blink::WebPluginContainer* PluginContainer() override;
+ net::SiteForCookies SiteForCookies() const override;
+ blink::WebURL CompleteURL(const blink::WebString& partial_url) const override;
+ void PostMessage(base::Value::Dict message) override;
+ void Invalidate() override;
+ void RequestTouchEventType(
+ blink::WebPluginContainer::TouchEventRequestType request_type) override;
+ void ReportFindInPageMatchCount(int identifier,
+ int total,
+ bool final_update) override;
+ void ReportFindInPageSelection(int identifier, int index) override;
+ void ReportFindInPageTickmarks(
+ const std::vector<gfx::Rect>& tickmarks) override;
+ float DeviceScaleFactor() override;
+ gfx::PointF GetScrollPosition() override;
+ void UsePluginAsFindHandler() override;
+ void SetReferrerForRequest(blink::WebURLRequest& request,
+ const blink::WebURL& referrer_url) override;
+ void Alert(const blink::WebString& message) override;
+ bool Confirm(const blink::WebString& message) override;
+ blink::WebString Prompt(const blink::WebString& message,
+ const blink::WebString& default_value) override;
+ void TextSelectionChanged(const blink::WebString& selection_text,
+ uint32_t offset,
+ const gfx::Range& range) override;
+ std::unique_ptr<blink::WebAssociatedURLLoader> CreateAssociatedURLLoader(
+ const blink::WebAssociatedURLLoaderOptions& options) override;
+ void UpdateTextInputState() override;
+ void UpdateSelectionBounds() override;
+ std::string GetEmbedderOriginString() override;
+ bool HasFrame() const override;
+ void DidStartLoading() override;
+ void DidStopLoading() override;
+ void Print() override;
void RecordComputedAction(const std::string& action) override;
std::unique_ptr<chrome_pdf::PdfAccessibilityDataHandler>
CreateAccessibilityDataHandler(
chrome_pdf::PdfAccessibilityActionHandler* action_handler) override;
private:
+ blink::WebLocalFrame* GetFrame() const;
+
content::RenderFrame* const render_frame_;
const std::unique_ptr<content::V8ValueConverter> v8_value_converter_;
+ v8::Isolate* const isolate_;
+
+ blink::WebPluginContainer* plugin_container_;
base::WeakPtrFactory<PdfViewWebPluginClient> weak_factory_{this};
};
diff --git a/chromium/components/performance_manager/BUILD.gn b/chromium/components/performance_manager/BUILD.gn
index 66e69e78d78..2d58f6cbced 100644
--- a/chromium/components/performance_manager/BUILD.gn
+++ b/chromium/components/performance_manager/BUILD.gn
@@ -22,7 +22,6 @@ static_library("performance_manager") {
"decorators/process_hosted_content_types_aggregator.cc",
"decorators/process_hosted_content_types_aggregator.h",
"decorators/process_metrics_decorator.cc",
- "decorators/tab_properties_decorator.cc",
"embedder/binders.h",
"embedder/graph_features.h",
"embedder/performance_manager_lifetime.h",
@@ -98,6 +97,7 @@ static_library("performance_manager") {
"graph/worker_node_impl_describer.h",
"graph_features.cc",
"metrics/metrics_collector.cc",
+ "metrics/metrics_provider.cc",
"owned_objects.h",
"performance_manager.cc",
"performance_manager_feature_observer_client.cc",
@@ -115,7 +115,6 @@ static_library("performance_manager") {
"public/decorators/page_live_state_decorator.h",
"public/decorators/page_load_tracker_decorator_helper.h",
"public/decorators/process_metrics_decorator.h",
- "public/decorators/tab_properties_decorator.h",
"public/execution_context/execution_context.h",
"public/execution_context/execution_context_attached_data.h",
"public/execution_context/execution_context_registry.h",
@@ -140,6 +139,7 @@ static_library("performance_manager") {
"public/graph/worker_node.h",
"public/metrics/background_metrics_reporter.h",
"public/metrics/metrics_collector.h",
+ "public/metrics/metrics_provider.h",
"public/performance_manager.h",
"public/performance_manager_main_thread_mechanism.h",
"public/performance_manager_main_thread_observer.h",
@@ -191,7 +191,9 @@ static_library("performance_manager") {
deps = [
"//build:chromeos_buildflags",
+ "//components/metrics",
"//components/pref_registry:pref_registry",
+ "//components/prefs:prefs",
"//third_party/blink/public/common:headers",
]
@@ -256,7 +258,6 @@ source_set("unit_tests") {
"decorators/page_live_state_decorator_unittest.cc",
"decorators/page_load_tracker_decorator_unittest.cc",
"decorators/process_hosted_content_types_aggregator_unittest.cc",
- "decorators/tab_properties_decorator_unittest.cc",
"execution_context/execution_context_attached_data_unittest.cc",
"execution_context/execution_context_registry_impl_unittest.cc",
"execution_context_priority/ad_frame_voter_unittest.cc",
@@ -284,6 +285,7 @@ source_set("unit_tests") {
"graph/worker_node_impl_unittest.cc",
"graph_features_unittest.cc",
"metrics/metrics_collector_unittest.cc",
+ "metrics/metrics_provider_unittest.cc",
"owned_objects_unittest.cc",
"performance_manager_impl_unittest.cc",
"performance_manager_registry_impl_unittest.cc",
@@ -310,6 +312,7 @@ source_set("unit_tests") {
"test_support:test_support_common",
"//base/test:test_support",
"//components/memory_pressure:test_support",
+ "//components/prefs:test_support",
"//components/services/storage/public/cpp",
"//components/ukm:test_support",
"//content/test:test_support",
diff --git a/chromium/components/performance_manager/DEPS b/chromium/components/performance_manager/DEPS
index e33abccb962..35a6efc0464 100644
--- a/chromium/components/performance_manager/DEPS
+++ b/chromium/components/performance_manager/DEPS
@@ -1,6 +1,8 @@
include_rules = [
"+components/memory_pressure",
+ "+components/metrics",
"+components/pref_registry",
+ "+components/prefs",
"+components/services/storage/public/cpp",
"+components/ukm",
"+content/public/common",
diff --git a/chromium/components/performance_manager/OWNERS b/chromium/components/performance_manager/OWNERS
index 5977711af6b..7936ab3f64d 100644
--- a/chromium/components/performance_manager/OWNERS
+++ b/chromium/components/performance_manager/OWNERS
@@ -2,6 +2,9 @@ chrisha@chromium.org
fdoray@chromium.org
joenotcharles@chromium.org
+# Preferred reviewer for service/dedicated/shared worker changes.
+pmonette@chromium.org
+
# For IPC security review
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.cc b/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.cc
index 750e54ca679..4bbb737443d 100644
--- a/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.cc
+++ b/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.cc
@@ -6,7 +6,9 @@
#include "base/check.h"
#include "components/performance_manager/graph/frame_node_impl.h"
+#include "components/performance_manager/graph/page_node_impl.h"
#include "components/performance_manager/graph/process_node_impl.h"
+#include "components/performance_manager/graph/worker_node_impl.h"
#include "components/performance_manager/public/graph/process_node.h"
namespace performance_manager {
@@ -19,22 +21,50 @@ ProcessHostedContentTypesAggregator::~ProcessHostedContentTypesAggregator() =
void ProcessHostedContentTypesAggregator::OnPassedToGraph(Graph* graph) {
DCHECK(graph->HasOnlySystemNode());
+ graph->AddPageNodeObserver(this);
graph->AddFrameNodeObserver(this);
+ graph->AddWorkerNodeObserver(this);
}
void ProcessHostedContentTypesAggregator::OnTakenFromGraph(Graph* graph) {
+ graph->RemoveWorkerNodeObserver(this);
graph->RemoveFrameNodeObserver(this);
+ graph->RemovePageNodeObserver(this);
+}
+
+void ProcessHostedContentTypesAggregator::OnTypeChanged(
+ const PageNode* page_node) {
+ if (page_node->GetType() == PageType::kExtension) {
+ // `PageType::kExtension` should be set early on the `PageNode`, before it
+ // has the opportunity to create more than one main frame or any subframe.
+ //
+ // TODO(1241218): Change CHECKs to DCHECKs in September 2022 if
+ // there are no crash report indicating that expectations are incorrect.
+ CHECK_LE(page_node->GetMainFrameNodes().size(), 1U);
+ if (auto* main_frame = page_node->GetMainFrameNode()) {
+ CHECK(main_frame->GetChildFrameNodes().empty());
+ FrameNodeImpl::FromNode(main_frame)
+ ->process_node()
+ ->add_hosted_content_type(ProcessNode::ContentType::kExtension);
+ }
+ }
}
void ProcessHostedContentTypesAggregator::OnFrameNodeAdded(
const FrameNode* frame_node) {
+ // TODO(1241909): Decide if prerendered frames should be handled differently.
+ //
+ // TODO(1241218, 1111084): A fenced frame should not be treated the same way
+ // as a main frame.
auto* frame_node_impl = FrameNodeImpl::FromNode(frame_node);
+ auto* process_node_impl = frame_node_impl->process_node();
+ process_node_impl->add_hosted_content_type(
+ frame_node_impl->IsMainFrame() ? ProcessNode::ContentType::kMainFrame
+ : ProcessNode::ContentType::kSubframe);
- // TODO(1241909): Figure out how if prerendered frames should be handle
- // differently.
- if (frame_node_impl->IsMainFrame()) {
- frame_node_impl->process_node()->add_hosted_content_type(
- ProcessNode::ContentType::kMainFrame);
+ if (frame_node_impl->page_node()->type() == PageType::kExtension) {
+ process_node_impl->add_hosted_content_type(
+ ProcessNode::ContentType::kExtension);
}
}
@@ -49,4 +79,19 @@ void ProcessHostedContentTypesAggregator::OnIsAdFrameChanged(
}
}
+void ProcessHostedContentTypesAggregator::OnURLChanged(
+ const FrameNode* frame_node,
+ const GURL& previous_value) {
+ auto* frame_node_impl = FrameNodeImpl::FromNode(frame_node);
+ frame_node_impl->process_node()->add_hosted_content_type(
+ ProcessNode::ContentType::kNavigatedFrame);
+}
+
+void ProcessHostedContentTypesAggregator::OnWorkerNodeAdded(
+ const WorkerNode* worker_node) {
+ auto* worker_node_impl = WorkerNodeImpl::FromNode(worker_node);
+ worker_node_impl->process_node()->add_hosted_content_type(
+ ProcessNode::ContentType::kWorker);
+}
+
} // namespace performance_manager
diff --git a/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.h b/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.h
index 505ad87a0ed..f12763bd1db 100644
--- a/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.h
+++ b/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator.h
@@ -7,6 +7,8 @@
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/graph.h"
+#include "components/performance_manager/public/graph/page_node.h"
+#include "components/performance_manager/public/graph/worker_node.h"
namespace performance_manager {
@@ -14,7 +16,9 @@ namespace performance_manager {
// |hosted_content_types()| property.
class ProcessHostedContentTypesAggregator
: public GraphOwnedDefaultImpl,
- public FrameNode::ObserverDefaultImpl {
+ public PageNode::ObserverDefaultImpl,
+ public FrameNode::ObserverDefaultImpl,
+ public WorkerNode::ObserverDefaultImpl {
public:
ProcessHostedContentTypesAggregator();
~ProcessHostedContentTypesAggregator() override;
@@ -28,9 +32,17 @@ class ProcessHostedContentTypesAggregator
void OnPassedToGraph(Graph* graph) override;
void OnTakenFromGraph(Graph* graph) override;
+ // PageNodeObserver:
+ void OnTypeChanged(const PageNode* page_node) override;
+
// FrameNodeObserver:
void OnFrameNodeAdded(const FrameNode* frame_node) override;
void OnIsAdFrameChanged(const FrameNode* frame_node) override;
+ void OnURLChanged(const FrameNode* frame_node,
+ const GURL& previous_value) override;
+
+ // WorkerNodeObserver:
+ void OnWorkerNodeAdded(const WorkerNode* worker_node) override;
private:
};
diff --git a/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator_unittest.cc b/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator_unittest.cc
index 574a9e27ea3..89a0088fe3e 100644
--- a/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator_unittest.cc
+++ b/chromium/components/performance_manager/decorators/process_hosted_content_types_aggregator_unittest.cc
@@ -6,10 +6,13 @@
#include <memory>
+#include "components/performance_manager/public/graph/worker_node.h"
#include "components/performance_manager/test_support/graph_test_harness.h"
namespace performance_manager {
+using ContentType = ProcessNode::ContentType;
+
class ProcessHostedContentTypesAggregatorTest : public GraphTestHarness {
public:
using Super = GraphTestHarness;
@@ -22,77 +25,174 @@ class ProcessHostedContentTypesAggregatorTest : public GraphTestHarness {
graph()->PassToGraph(
std::make_unique<ProcessHostedContentTypesAggregator>());
}
+
+ bool IsHosting(const TestNodeWrapper<ProcessNodeImpl>& process_node,
+ ContentType content_type) {
+ return process_node->hosted_content_types().Has(content_type);
+ }
};
+TEST_F(ProcessHostedContentTypesAggregatorTest,
+ Extension_FrameCreatedAfterSetType) {
+ // Create a process node.
+ auto process_node = CreateNode<ProcessNodeImpl>();
+
+ // Add an extension frame to it.
+ auto page_node = CreateNode<PageNodeImpl>();
+ page_node->SetType(PageType::kExtension);
+ auto frame_node = CreateFrameNodeAutoId(process_node.get(), page_node.get());
+
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kExtension));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kMainFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kSubframe));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kNavigatedFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kAd));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kWorker));
+
+ // Remove the extension frame. The process is still counted as having hosted
+ // an extension.
+ frame_node.reset();
+ page_node.reset();
+ EXPECT_TRUE(graph()->GetAllFrameNodes().empty());
+ EXPECT_TRUE(graph()->GetAllPageNodes().empty());
+
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kExtension));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kMainFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kSubframe));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kNavigatedFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kAd));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kWorker));
+}
+
+TEST_F(ProcessHostedContentTypesAggregatorTest,
+ Extension_FrameCreatedBeforeSetType) {
+ // Create a process node.
+ auto process_node = CreateNode<ProcessNodeImpl>();
+
+ // Create a page node with a main frame node.
+ auto page_node = CreateNode<PageNodeImpl>();
+ auto frame_node = CreateFrameNodeAutoId(process_node.get(), page_node.get());
+
+ // Set the page type after creating the frame.
+ page_node->SetType(PageType::kExtension);
+
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kExtension));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kMainFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kSubframe));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kNavigatedFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kAd));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kWorker));
+}
+
TEST_F(ProcessHostedContentTypesAggregatorTest, MainFrameAndChildFrame) {
auto page_node = CreateNode<PageNodeImpl>();
EXPECT_FALSE(page_node->is_visible());
- // Create the first process node. It is not hosting any main frames at first.
+ // Create a main frame in a first process.
auto process_node_1 = CreateNode<ProcessNodeImpl>();
- EXPECT_FALSE(process_node_1->hosted_content_types().Has(
- ProcessNode::ContentType::kMainFrame));
-
- // Create the first frame node. It is considered a main frame since it has no
- // parent frame.
- auto frame_node_1 =
+ EXPECT_TRUE(process_node_1->hosted_content_types().Empty());
+ auto main_frame_node =
CreateFrameNodeAutoId(process_node_1.get(), page_node.get());
- EXPECT_TRUE(process_node_1->hosted_content_types().Has(
- ProcessNode::ContentType::kMainFrame));
+
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kExtension));
+ EXPECT_TRUE(IsHosting(process_node_1, ContentType::kMainFrame));
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kSubframe));
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kNavigatedFrame));
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kAd));
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kWorker));
// Create a child frame node in another process.
auto process_node_2 = CreateNode<ProcessNodeImpl>();
- EXPECT_FALSE(process_node_2->hosted_content_types().Has(
- ProcessNode::ContentType::kMainFrame));
+ EXPECT_TRUE(process_node_2->hosted_content_types().Empty());
auto child_frame_node = CreateFrameNodeAutoId(
- process_node_2.get(), page_node.get(), frame_node_1.get());
- EXPECT_FALSE(process_node_2->hosted_content_types().Has(
- ProcessNode::ContentType::kMainFrame));
+ process_node_2.get(), page_node.get(), main_frame_node.get());
+
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kExtension));
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kMainFrame));
+ EXPECT_TRUE(IsHosting(process_node_2, ContentType::kSubframe));
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kNavigatedFrame));
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kAd));
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kWorker));
+
+ // Remove the frames. This shouldn't affect hosted content types.
+ child_frame_node.reset();
+ main_frame_node.reset();
+ EXPECT_TRUE(graph()->GetAllFrameNodes().empty());
+
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kExtension));
+ EXPECT_TRUE(IsHosting(process_node_1, ContentType::kMainFrame));
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kSubframe));
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kNavigatedFrame));
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kAd));
+ EXPECT_FALSE(IsHosting(process_node_1, ContentType::kWorker));
+
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kExtension));
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kMainFrame));
+ EXPECT_TRUE(IsHosting(process_node_2, ContentType::kSubframe));
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kNavigatedFrame));
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kAd));
+ EXPECT_FALSE(IsHosting(process_node_2, ContentType::kWorker));
}
TEST_F(ProcessHostedContentTypesAggregatorTest, AdFrame) {
auto page_node = CreateNode<PageNodeImpl>();
- EXPECT_FALSE(page_node->is_visible());
- // Create the first process node.
- auto process_node = CreateNode<ProcessNodeImpl>();
- auto frame_node = CreateFrameNodeAutoId(process_node.get(), page_node.get());
- EXPECT_FALSE(
- process_node->hosted_content_types().Has(ProcessNode::ContentType::kAd));
+ // Create a main frame to host the ad frame.
+ auto main_frame_process_node = CreateNode<ProcessNodeImpl>();
+ auto main_frame_node =
+ CreateFrameNodeAutoId(main_frame_process_node.get(), page_node.get());
- // Make it an ad frame.
- frame_node->SetIsAdFrame(true);
- EXPECT_TRUE(
- process_node->hosted_content_types().Has(ProcessNode::ContentType::kAd));
+ // Create an ad frame in another process.
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ auto ad_frame_node = CreateFrameNodeAutoId(
+ process_node.get(), page_node.get(), main_frame_node.get());
+ ad_frame_node->OnNavigationCommitted(GURL("https://example.com"),
+ /* same_document=*/false);
+ ad_frame_node->SetIsAdFrame(true);
+
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kExtension));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kMainFrame));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kSubframe));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kNavigatedFrame));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kAd));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kWorker));
// Untag the frame as an ad. The process is still counted as having hosted an
// ad frame.
- frame_node->SetIsAdFrame(false);
- EXPECT_TRUE(
- process_node->hosted_content_types().Has(ProcessNode::ContentType::kAd));
+ ad_frame_node->SetIsAdFrame(false);
+
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kExtension));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kMainFrame));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kSubframe));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kNavigatedFrame));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kAd));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kWorker));
}
-TEST_F(ProcessHostedContentTypesAggregatorTest, ContentTypeIsPermanent) {
- auto page_node = CreateNode<PageNodeImpl>();
- EXPECT_FALSE(page_node->is_visible());
-
- // Create the first process node. It is not hosting any main frames at first.
+TEST_F(ProcessHostedContentTypesAggregatorTest, Worker) {
+ // Create a worker node.
auto process_node = CreateNode<ProcessNodeImpl>();
- EXPECT_FALSE(process_node->hosted_content_types().Has(
- ProcessNode::ContentType::kMainFrame));
-
- // Create a frame node. It is considered a main frame since it has no parent
- // frame.
- auto frame_node = CreateFrameNodeAutoId(process_node.get(), page_node.get());
- EXPECT_TRUE(process_node->hosted_content_types().Has(
- ProcessNode::ContentType::kMainFrame));
-
- // Remove the frame node. The process is still considered as having hosted a
- // main frame.
- frame_node.reset();
- EXPECT_TRUE(graph()->GetAllFrameNodes().empty());
- EXPECT_TRUE(process_node->hosted_content_types().Has(
- ProcessNode::ContentType::kMainFrame));
+ auto worker_node = CreateNode<WorkerNodeImpl>(
+ WorkerNode::WorkerType::kDedicated, process_node.get());
+
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kExtension));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kMainFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kSubframe));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kNavigatedFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kAd));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kWorker));
+
+ // Remove the worker node. The process is still counted as having hosted a
+ // worker.
+ worker_node.reset();
+ EXPECT_TRUE(graph()->GetAllWorkerNodes().empty());
+
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kExtension));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kMainFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kSubframe));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kNavigatedFrame));
+ EXPECT_FALSE(IsHosting(process_node, ContentType::kAd));
+ EXPECT_TRUE(IsHosting(process_node, ContentType::kWorker));
}
} // namespace performance_manager
diff --git a/chromium/components/performance_manager/decorators/tab_properties_decorator.cc b/chromium/components/performance_manager/decorators/tab_properties_decorator.cc
deleted file mode 100644
index d629cb88243..00000000000
--- a/chromium/components/performance_manager/decorators/tab_properties_decorator.cc
+++ /dev/null
@@ -1,95 +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 "components/performance_manager/public/decorators/tab_properties_decorator.h"
-
-#include "components/performance_manager/decorators/decorators_utils.h"
-#include "components/performance_manager/graph/node_attached_data_impl.h"
-#include "components/performance_manager/graph/page_node_impl.h"
-#include "components/performance_manager/public/graph/node_data_describer_registry.h"
-#include "components/performance_manager/public/performance_manager.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace performance_manager {
-
-namespace {
-
-class TabPropertiesDataImpl
- : public TabPropertiesDecorator::Data,
- public NodeAttachedDataImpl<TabPropertiesDataImpl> {
- public:
- struct Traits : public NodeAttachedDataInMap<PageNodeImpl> {};
- ~TabPropertiesDataImpl() override = default;
- TabPropertiesDataImpl(const TabPropertiesDataImpl& other) = delete;
- TabPropertiesDataImpl& operator=(const TabPropertiesDataImpl&) = delete;
-
- // TabPropertiesDecorator::Data implementation.
- bool IsInTabStrip() const override { return is_tab_; }
-
- void set_is_tab(bool is_tab) { is_tab_ = is_tab; }
-
- private:
- // Make the impl our friend so it can access the constructor and any
- // storage providers.
- friend class ::performance_manager::NodeAttachedDataImpl<
- TabPropertiesDataImpl>;
-
- explicit TabPropertiesDataImpl(const PageNodeImpl* page_node) {}
-
- bool is_tab_ = false;
-};
-
-const char kDescriberName[] = "TabPropertiesDecorator";
-
-} // namespace
-
-void TabPropertiesDecorator::SetIsTab(content::WebContents* contents,
- bool is_tab) {
- SetPropertyForWebContentsPageNode(contents,
- &TabPropertiesDataImpl::set_is_tab, is_tab);
-}
-
-void TabPropertiesDecorator::SetIsTabForTesting(PageNode* page_node,
- bool is_tab) {
- auto* data =
- TabPropertiesDataImpl::GetOrCreate(PageNodeImpl::FromNode(page_node));
- DCHECK(data);
- data->set_is_tab(is_tab);
-}
-
-void TabPropertiesDecorator::OnPassedToGraph(Graph* graph) {
- graph->GetNodeDataDescriberRegistry()->RegisterDescriber(this,
- kDescriberName);
-}
-
-void TabPropertiesDecorator::OnTakenFromGraph(Graph* graph) {
- graph->GetNodeDataDescriberRegistry()->UnregisterDescriber(this);
-}
-
-base::Value TabPropertiesDecorator::DescribePageNodeData(
- const PageNode* node) const {
- auto* data = TabPropertiesDecorator::Data::FromPageNode(node);
- if (!data)
- return base::Value();
-
- base::Value ret(base::Value::Type::DICTIONARY);
- ret.SetBoolKey("IsInTabStrip", data->IsInTabStrip());
-
- return ret;
-}
-
-TabPropertiesDecorator::Data::Data() = default;
-TabPropertiesDecorator::Data::~Data() = default;
-
-const TabPropertiesDecorator::Data* TabPropertiesDecorator::Data::FromPageNode(
- const PageNode* page_node) {
- return TabPropertiesDataImpl::Get(PageNodeImpl::FromNode(page_node));
-}
-
-TabPropertiesDecorator::Data*
-TabPropertiesDecorator::Data::GetOrCreateForTesting(const PageNode* page_node) {
- return TabPropertiesDataImpl::GetOrCreate(PageNodeImpl::FromNode(page_node));
-}
-
-} // namespace performance_manager
diff --git a/chromium/components/performance_manager/decorators/tab_properties_decorator_unittest.cc b/chromium/components/performance_manager/decorators/tab_properties_decorator_unittest.cc
deleted file mode 100644
index 38714507e13..00000000000
--- a/chromium/components/performance_manager/decorators/tab_properties_decorator_unittest.cc
+++ /dev/null
@@ -1,40 +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 "components/performance_manager/public/decorators/tab_properties_decorator.h"
-
-#include "components/performance_manager/test_support/decorators_utils.h"
-#include "components/performance_manager/test_support/performance_manager_test_harness.h"
-#include "content/public/browser/web_contents.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace performance_manager {
-
-class TabPropertiesDecoratorTest : public PerformanceManagerTestHarness {
- public:
- TabPropertiesDecoratorTest() = default;
- ~TabPropertiesDecoratorTest() override = default;
- TabPropertiesDecoratorTest(const TabPropertiesDecoratorTest& other) = delete;
- TabPropertiesDecoratorTest& operator=(const TabPropertiesDecoratorTest&) =
- delete;
-
- void SetUp() override {
- PerformanceManagerTestHarness::SetUp();
- SetContents(CreateTestWebContents());
- }
-
- void TearDown() override {
- DeleteContents();
- PerformanceManagerTestHarness::TearDown();
- }
-};
-
-TEST_F(TabPropertiesDecoratorTest, SetIsTab) {
- testing::EndToEndBooleanPropertyTest(
- web_contents(), &TabPropertiesDecorator::Data::GetOrCreateForTesting,
- &TabPropertiesDecorator::Data::IsInTabStrip,
- &TabPropertiesDecorator::SetIsTab);
-}
-
-} // namespace performance_manager \ No newline at end of file
diff --git a/chromium/components/performance_manager/embedder/performance_manager_registry.h b/chromium/components/performance_manager/embedder/performance_manager_registry.h
index 65d8250cbb0..cf4b04da9e7 100644
--- a/chromium/components/performance_manager/embedder/performance_manager_registry.h
+++ b/chromium/components/performance_manager/embedder/performance_manager_registry.h
@@ -8,6 +8,7 @@
#include <memory>
#include <vector>
+#include "components/performance_manager/public/graph/page_node.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "services/service_manager/public/cpp/binder_registry.h"
@@ -61,6 +62,10 @@ class PerformanceManagerRegistry {
virtual void CreatePageNodeForWebContents(
content::WebContents* web_contents) = 0;
+ // Sets the page type for a WebContents.
+ virtual void SetPageType(content::WebContents* web_contents,
+ PageType type) = 0;
+
// Must be invoked for a NavigationHandle when it is committed, allowing the
// PM the opportunity to apply NavigationThrottles. Typically wired up to
// ContentBrowserClient::CreateThrottlesForNavigation.
diff --git a/chromium/components/performance_manager/features.cc b/chromium/components/performance_manager/features.cc
index 193c27e851b..67ce5ffaa7f 100644
--- a/chromium/components/performance_manager/features.cc
+++ b/chromium/components/performance_manager/features.cc
@@ -49,6 +49,15 @@ const base::Feature kBackgroundTabLoadingFromPerformanceManager{
const base::Feature kHighPMFDiscardPolicy{"HighPMFDiscardPolicy",
base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kHighEfficiencyModeAvailable{
+ "HighEfficiencyModeAvailable", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kBatterySaverModeAvailable{
+ "BatterySaverModeAvailable", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::FeatureParam<base::TimeDelta> kHighEfficiencyModeTimeBeforeDiscard{
+ &kHighEfficiencyModeAvailable, "time_before_discard", base::Minutes(5)};
#endif
const base::Feature kBFCachePerformanceManagerPolicy{
diff --git a/chromium/components/performance_manager/graph/graph_impl_unittest.cc b/chromium/components/performance_manager/graph/graph_impl_unittest.cc
index 7d2d1375a5f..2be01e396ae 100644
--- a/chromium/components/performance_manager/graph/graph_impl_unittest.cc
+++ b/chromium/components/performance_manager/graph/graph_impl_unittest.cc
@@ -41,7 +41,8 @@ TEST_F(GraphImplTest, GetProcessNodeByPid) {
const base::Process self = base::Process::Current();
EXPECT_EQ(nullptr, graph()->GetProcessNodeByPid(self.Pid()));
- process->SetProcess(self.Duplicate(), base::Time::Now());
+ process->SetProcess(self.Duplicate(),
+ /* launch_time=*/base::TimeTicks::Now());
EXPECT_TRUE(process->process().IsValid());
EXPECT_EQ(self.Pid(), process->process_id());
EXPECT_EQ(process.get(), graph()->GetProcessNodeByPid(self.Pid()));
@@ -68,7 +69,8 @@ TEST_F(GraphImplTest, PIDReuse) {
TestNodeWrapper<ProcessNodeImpl> process2 =
TestNodeWrapper<ProcessNodeImpl>::Create(graph());
- process1->SetProcess(self.Duplicate(), base::Time::Now());
+ process1->SetProcess(self.Duplicate(),
+ /* launch_time=*/base::TimeTicks::Now());
EXPECT_EQ(process1.get(), graph()->GetProcessNodeByPid(self.Pid()));
// First process exits, but hasn't been deleted yet.
@@ -76,7 +78,8 @@ TEST_F(GraphImplTest, PIDReuse) {
EXPECT_EQ(process1.get(), graph()->GetProcessNodeByPid(self.Pid()));
// The second registration for the same PID should override the first one.
- process2->SetProcess(self.Duplicate(), base::Time::Now());
+ process2->SetProcess(self.Duplicate(),
+ /* launch_time=*/base::TimeTicks::Now());
EXPECT_EQ(process2.get(), graph()->GetProcessNodeByPid(self.Pid()));
// The destruction of the first process node shouldn't clear the PID
diff --git a/chromium/components/performance_manager/graph/page_node.cc b/chromium/components/performance_manager/graph/page_node.cc
index 81e198e67e1..e9b65a687f7 100644
--- a/chromium/components/performance_manager/graph/page_node.cc
+++ b/chromium/components/performance_manager/graph/page_node.cc
@@ -22,6 +22,19 @@ const char* PageNode::ToString(PageNode::EmbeddingType embedding_type) {
}
// static
+const char* PageNode::ToString(PageType type) {
+ switch (type) {
+ case PageType::kTab:
+ return "kTab";
+ case PageType::kExtension:
+ return "kExtension";
+ case PageType::kUnknown:
+ return "kUnknown";
+ }
+ NOTREACHED();
+}
+
+// static
const char* PageNode::ToString(PageNode::LoadingState loading_state) {
switch (loading_state) {
case LoadingState::kLoadingNotStarted:
diff --git a/chromium/components/performance_manager/graph/page_node_impl.cc b/chromium/components/performance_manager/graph/page_node_impl.cc
index f4fed68546d..644e684bec3 100644
--- a/chromium/components/performance_manager/graph/page_node_impl.cc
+++ b/chromium/components/performance_manager/graph/page_node_impl.cc
@@ -114,6 +114,11 @@ void PageNodeImpl::SetLoadingState(LoadingState loading_state) {
loading_state_.SetAndMaybeNotify(this, loading_state);
}
+void PageNodeImpl::SetType(PageType type) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ type_.SetAndMaybeNotify(this, type);
+}
+
void PageNodeImpl::SetIsVisible(bool is_visible) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (is_visible_.SetAndMaybeNotify(this, is_visible)) {
@@ -216,6 +221,11 @@ PageNodeImpl::EmbeddingType PageNodeImpl::embedding_type() const {
return embedding_type_;
}
+PageType PageNodeImpl::type() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return type_.value();
+}
+
bool PageNodeImpl::is_visible() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return is_visible_.value();
@@ -453,6 +463,11 @@ PageNodeImpl::EmbeddingType PageNodeImpl::GetEmbeddingType() const {
return embedding_type();
}
+PageType PageNodeImpl::GetType() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return type();
+}
+
bool PageNodeImpl::IsVisible() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return is_visible();
diff --git a/chromium/components/performance_manager/graph/page_node_impl.h b/chromium/components/performance_manager/graph/page_node_impl.h
index be6f46dde27..cc872517bd0 100644
--- a/chromium/components/performance_manager/graph/page_node_impl.h
+++ b/chromium/components/performance_manager/graph/page_node_impl.h
@@ -59,6 +59,7 @@ class PageNodeImpl
// dereferenced on the UI thread.
const WebContentsProxy& contents_proxy() const;
+ void SetType(PageType type);
void SetIsVisible(bool is_visible);
void SetIsAudible(bool is_audible);
void SetLoadingState(LoadingState loading_state);
@@ -90,6 +91,7 @@ class PageNodeImpl
FrameNodeImpl* opener_frame_node() const;
FrameNodeImpl* embedder_frame_node() const;
EmbeddingType embedding_type() const;
+ PageType type() const;
bool is_visible() const;
bool is_audible() const;
LoadingState loading_state() const;
@@ -200,6 +202,7 @@ class PageNodeImpl
const FrameNode* GetOpenerFrameNode() const override;
const FrameNode* GetEmbedderFrameNode() const override;
EmbeddingType GetEmbeddingType() const override;
+ PageType GetType() const override;
bool IsVisible() const override;
base::TimeDelta GetTimeSinceLastVisibilityChange() const override;
bool IsAudible() const override;
@@ -293,6 +296,11 @@ class PageNodeImpl
EmbeddingType embedding_type_ GUARDED_BY_CONTEXT(sequence_checker_) =
EmbeddingType::kInvalid;
+ // The type of the page.
+ ObservedProperty::NotifiesOnlyOnChanges<PageType,
+ &PageNodeObserver::OnTypeChanged>
+ type_ GUARDED_BY_CONTEXT(sequence_checker_){PageType::kUnknown};
+
// Whether or not the page is visible. Driven by browser instrumentation.
// Initialized on construction.
ObservedProperty::NotifiesOnlyOnChanges<bool,
diff --git a/chromium/components/performance_manager/graph/page_node_impl_describer.cc b/chromium/components/performance_manager/graph/page_node_impl_describer.cc
index 10a4a226a88..d4708416792 100644
--- a/chromium/components/performance_manager/graph/page_node_impl_describer.cc
+++ b/chromium/components/performance_manager/graph/page_node_impl_describer.cc
@@ -69,6 +69,8 @@ base::Value PageNodeImplDescriber::DescribePageNodeData(
page_node_impl->contents_mime_type_);
result.SetStringKey("browser_context_id",
page_node_impl->browser_context_id_);
+ result.SetStringKey("type",
+ PageNode::ToString(page_node_impl->type_.value()));
result.SetBoolKey("is_visible", page_node_impl->is_visible_.value());
result.SetBoolKey("is_audible", page_node_impl->is_audible_.value());
result.SetStringKey(
diff --git a/chromium/components/performance_manager/graph/page_node_impl_unittest.cc b/chromium/components/performance_manager/graph/page_node_impl_unittest.cc
index e4bc73de34b..ddc3963e798 100644
--- a/chromium/components/performance_manager/graph/page_node_impl_unittest.cc
+++ b/chromium/components/performance_manager/graph/page_node_impl_unittest.cc
@@ -227,6 +227,7 @@ class LenientMockObserver : public PageNodeImpl::Observer {
void(const PageNode*, const FrameNode*));
MOCK_METHOD3(OnEmbedderFrameNodeChanged,
void(const PageNode*, const FrameNode*, EmbeddingType));
+ MOCK_METHOD1(OnTypeChanged, void(const PageNode*));
MOCK_METHOD1(OnIsVisibleChanged, void(const PageNode*));
MOCK_METHOD1(OnIsAudibleChanged, void(const PageNode*));
MOCK_METHOD2(OnLoadingStateChanged,
diff --git a/chromium/components/performance_manager/graph/policies/process_priority_policy_unittest.cc b/chromium/components/performance_manager/graph/policies/process_priority_policy_unittest.cc
index 444d6df9e19..c5c69e46ca3 100644
--- a/chromium/components/performance_manager/graph/policies/process_priority_policy_unittest.cc
+++ b/chromium/components/performance_manager/graph/policies/process_priority_policy_unittest.cc
@@ -112,7 +112,7 @@ TEST_F(ProcessPriorityPolicyTest, GraphReflectedToRenderProcessHost) {
// Set the active contents in the RenderViewHostTestHarness.
SetContents(CreateTestWebContents());
- auto* rvh = web_contents()->GetMainFrame()->GetRenderViewHost();
+ auto* rvh = web_contents()->GetPrimaryMainFrame()->GetRenderViewHost();
DCHECK(rvh);
auto* rph = rvh->GetProcess();
DCHECK(rph);
diff --git a/chromium/components/performance_manager/graph/process_node_impl.cc b/chromium/components/performance_manager/graph/process_node_impl.cc
index eac44ad35cc..0e9a5774545 100644
--- a/chromium/components/performance_manager/graph/process_node_impl.cc
+++ b/chromium/components/performance_manager/graph/process_node_impl.cc
@@ -32,7 +32,7 @@ void FireBackgroundTracingTriggerOnUI(
// Renderer-initiated background tracing triggers are always "preemptive"
// traces so we expect a scenario to be active.
if (!manager)
- manager = content::BackgroundTracingManager::GetInstance();
+ manager = &content::BackgroundTracingManager::GetInstance();
if (!manager->HasActiveScenario())
return;
@@ -168,8 +168,13 @@ void ProcessNodeImpl::SetProcessExitStatus(int32_t exit_status) {
receiver_.reset();
}
+void ProcessNodeImpl::SetProcessMetricsName(const std::string& metrics_name) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ metrics_name_ = metrics_name;
+}
+
void ProcessNodeImpl::SetProcess(base::Process process,
- base::Time launch_time) {
+ base::TimeTicks launch_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(process.IsValid());
// Either this is the initial process associated with this process node,
@@ -258,8 +263,9 @@ base::WeakPtr<ProcessNodeImpl> ProcessNodeImpl::GetWeakPtr() {
void ProcessNodeImpl::SetProcessImpl(base::Process process,
base::ProcessId new_pid,
- base::Time launch_time) {
+ base::TimeTicks launch_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(process.IsValid());
graph()->BeforeProcessPidChange(this, new_pid);
@@ -293,7 +299,7 @@ const base::Process& ProcessNodeImpl::GetProcess() const {
return process();
}
-base::Time ProcessNodeImpl::GetLaunchTime() const {
+base::TimeTicks ProcessNodeImpl::GetLaunchTime() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return launch_time();
}
@@ -303,6 +309,11 @@ absl::optional<int32_t> ProcessNodeImpl::GetExitStatus() const {
return exit_status();
}
+const std::string& ProcessNodeImpl::GetMetricsName() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return metrics_name();
+}
+
bool ProcessNodeImpl::VisitFrameNodes(const FrameNodeVisitor& visitor) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto* frame_impl : frame_nodes()) {
diff --git a/chromium/components/performance_manager/graph/process_node_impl.h b/chromium/components/performance_manager/graph/process_node_impl.h
index 5e93a59828c..502ef6dd4c6 100644
--- a/chromium/components/performance_manager/graph/process_node_impl.h
+++ b/chromium/components/performance_manager/graph/process_node_impl.h
@@ -84,7 +84,8 @@ class ProcessNodeImpl
void FireBackgroundTracingTrigger(const std::string& trigger_name) override;
void SetProcessExitStatus(int32_t exit_status);
- void SetProcess(base::Process process, base::Time launch_time);
+ void SetProcessMetricsName(const std::string& metrics_name);
+ void SetProcess(base::Process process, base::TimeTicks launch_time);
// Private implementation properties.
void set_private_footprint_kb(uint64_t private_footprint_kb) {
@@ -130,7 +131,7 @@ class ProcessNodeImpl
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return process_.value();
}
- base::Time launch_time() const {
+ base::TimeTicks launch_time() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return launch_time_;
}
@@ -138,6 +139,10 @@ class ProcessNodeImpl
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return exit_status_;
}
+ const std::string& metrics_name() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return metrics_name_;
+ }
bool main_thread_task_load_is_low() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -188,7 +193,7 @@ class ProcessNodeImpl
protected:
void SetProcessImpl(base::Process process,
base::ProcessId process_id,
- base::Time launch_time);
+ base::TimeTicks launch_time);
private:
friend class FrozenFrameAggregatorAccess;
@@ -200,8 +205,9 @@ class ProcessNodeImpl
content::ProcessType GetProcessType() const override;
base::ProcessId GetProcessId() const override;
const base::Process& GetProcess() const override;
- base::Time GetLaunchTime() const override;
+ base::TimeTicks GetLaunchTime() const override;
absl::optional<int32_t> GetExitStatus() const override;
+ const std::string& GetMetricsName() const override;
bool VisitFrameNodes(const FrameNodeVisitor& visitor) const override;
base::flat_set<const FrameNode*> GetFrameNodes() const override;
base::flat_set<const WorkerNode*> GetWorkerNodes() const override;
@@ -232,8 +238,9 @@ class ProcessNodeImpl
&ProcessNodeObserver::OnProcessLifetimeChange>
process_ GUARDED_BY_CONTEXT(sequence_checker_);
- base::Time launch_time_ GUARDED_BY_CONTEXT(sequence_checker_);
+ base::TimeTicks launch_time_ GUARDED_BY_CONTEXT(sequence_checker_);
absl::optional<int32_t> exit_status_ GUARDED_BY_CONTEXT(sequence_checker_);
+ std::string metrics_name_ GUARDED_BY_CONTEXT(sequence_checker_);
const content::ProcessType process_type_
GUARDED_BY_CONTEXT(sequence_checker_);
diff --git a/chromium/components/performance_manager/graph/process_node_impl_describer.cc b/chromium/components/performance_manager/graph/process_node_impl_describer.cc
index 18c10eaf9b3..9da1a37ccb0 100644
--- a/chromium/components/performance_manager/graph/process_node_impl_describer.cc
+++ b/chromium/components/performance_manager/graph/process_node_impl_describer.cc
@@ -10,11 +10,13 @@
#include "base/process/process_handle.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
#include "base/task/task_traits.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/performance_manager/public/graph/node_data_describer_registry.h"
+#include "components/performance_manager/public/graph/process_node.h"
#include "content/public/common/child_process_host.h"
#if BUILDFLAG(IS_WIN)
@@ -33,8 +35,14 @@ std::string ContentTypeToString(ProcessNode::ContentType content_type) {
return "Extension";
case ProcessNode::ContentType::kMainFrame:
return "Main frame";
+ case ProcessNode::ContentType::kSubframe:
+ return "Subframe";
+ case ProcessNode::ContentType::kNavigatedFrame:
+ return "Navigated Frame";
case ProcessNode::ContentType::kAd:
return "Ad";
+ case ProcessNode::ContentType::kWorker:
+ return "Worker";
}
}
@@ -104,6 +112,15 @@ base::Value GetProcessValueDict(const base::Process& process) {
return ret;
}
+// Converts TimeTicks to Time. The conversion will be incorrect if system
+// time is adjusted between `ticks` and now.
+base::Time TicksToTime(base::TimeTicks ticks) {
+ base::Time now_time = base::Time::Now();
+ base::TimeTicks now_ticks = base::TimeTicks::Now();
+ base::TimeDelta elapsed_since_ticks = now_ticks - ticks;
+ return now_time - elapsed_since_ticks;
+}
+
} // namespace
void ProcessNodeImplDescriber::OnPassedToGraph(Graph* graph) {
@@ -150,12 +167,16 @@ base::Value ProcessNodeImplDescriber::DescribeProcessNodeData(
ret.SetKey("process", GetProcessValueDict(impl->process()));
ret.SetStringKey("launch_time", base::TimeFormatTimeOfDayWithMilliseconds(
- impl->launch_time()));
+ TicksToTime(impl->launch_time())));
if (impl->exit_status()) {
ret.SetIntKey("exit_status", impl->exit_status().value());
}
+ if (!impl->metrics_name().empty()) {
+ ret.SetStringKey("metrics_name", impl->metrics_name());
+ }
+
ret.SetBoolKey("main_thread_task_load_is_low",
impl->main_thread_task_load_is_low());
diff --git a/chromium/components/performance_manager/graph/process_node_impl_unittest.cc b/chromium/components/performance_manager/graph/process_node_impl_unittest.cc
index b954180c137..6c5f26f467d 100644
--- a/chromium/components/performance_manager/graph/process_node_impl_unittest.cc
+++ b/chromium/components/performance_manager/graph/process_node_impl_unittest.cc
@@ -57,7 +57,7 @@ TEST_F(ProcessNodeImplTest, ProcessLifeCycle) {
// Next go through PID->exit status.
const base::Process self = base::Process::Current();
- const base::Time launch_time = base::Time::Now();
+ const base::TimeTicks launch_time = base::TimeTicks::Now();
process_node->SetProcess(self.Duplicate(), launch_time);
EXPECT_TRUE(process_node->process().IsValid());
EXPECT_EQ(self.Pid(), process_node->process_id());
@@ -84,7 +84,7 @@ TEST_F(ProcessNodeImplTest, ProcessLifeCycle) {
// Resurrect again and verify the launch time and measurements
// are cleared.
- const base::Time launch2_time = launch_time + base::Seconds(1);
+ const base::TimeTicks launch2_time = launch_time + base::Seconds(1);
process_node->SetProcess(self.Duplicate(), launch2_time);
EXPECT_EQ(launch2_time, process_node->launch_time());
@@ -165,7 +165,8 @@ TEST_F(ProcessNodeImplTest, ObserverWorks) {
// Test process creation and exit events.
EXPECT_CALL(obs, OnProcessLifetimeChange(_));
- process_node->SetProcess(base::Process::Current(), base::Time::Now());
+ process_node->SetProcess(base::Process::Current(),
+ /* launch_time=*/base::TimeTicks::Now());
EXPECT_CALL(obs, OnProcessLifetimeChange(_));
process_node->SetProcessExitStatus(10);
@@ -230,8 +231,8 @@ TEST_F(ProcessNodeImplTest, PublicInterface) {
public_process_node->GetProcessType());
const base::Process self = base::Process::Current();
- const base::Time launch_time = base::Time::Now();
- process_node->SetProcess(self.Duplicate(), launch_time);
+ process_node->SetProcess(self.Duplicate(),
+ /* launch_time=*/base::TimeTicks::Now());
EXPECT_EQ(process_node->process_id(), public_process_node->GetProcessId());
EXPECT_EQ(&process_node->process(), &public_process_node->GetProcess());
EXPECT_EQ(process_node->launch_time(), public_process_node->GetLaunchTime());
@@ -240,6 +241,12 @@ TEST_F(ProcessNodeImplTest, PublicInterface) {
process_node->SetProcessExitStatus(kExitStatus);
EXPECT_EQ(process_node->exit_status(), public_process_node->GetExitStatus());
+ const std::string kMetricsName("TestUtilityProcess");
+ process_node->SetProcessMetricsName(kMetricsName);
+ EXPECT_EQ(process_node->metrics_name(), kMetricsName);
+ EXPECT_EQ(process_node->metrics_name(),
+ public_process_node->GetMetricsName());
+
const auto& frame_nodes = process_node->frame_nodes();
auto public_frame_nodes = public_process_node->GetFrameNodes();
EXPECT_EQ(frame_nodes.size(), public_frame_nodes.size());
diff --git a/chromium/components/performance_manager/graph_features.cc b/chromium/components/performance_manager/graph_features.cc
index 0194c989097..ee55b08fae3 100644
--- a/chromium/components/performance_manager/graph_features.cc
+++ b/chromium/components/performance_manager/graph_features.cc
@@ -18,7 +18,6 @@
#include "components/performance_manager/graph/process_node_impl_describer.h"
#include "components/performance_manager/graph/worker_node_impl_describer.h"
#include "components/performance_manager/public/decorators/page_live_state_decorator.h"
-#include "components/performance_manager/public/decorators/tab_properties_decorator.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/metrics/metrics_collector.h"
#include "components/performance_manager/v8_memory/v8_context_tracker.h"
@@ -60,8 +59,6 @@ void GraphFeatures::ConfigureGraph(Graph* graph) const {
Install<ProcessHostedContentTypesAggregator>(graph);
if (flags_.process_node_impl_describer)
Install<ProcessNodeImplDescriber>(graph);
- if (flags_.tab_properties_decorator)
- Install<TabPropertiesDecorator>(graph);
if (flags_.worker_node_impl_describer)
Install<WorkerNodeImplDescriber>(graph);
diff --git a/chromium/components/performance_manager/graph_features_unittest.cc b/chromium/components/performance_manager/graph_features_unittest.cc
index 00776c7262f..5f0ec9da5a1 100644
--- a/chromium/components/performance_manager/graph_features_unittest.cc
+++ b/chromium/components/performance_manager/graph_features_unittest.cc
@@ -53,7 +53,7 @@ TEST(GraphFeaturesTest, EnableDefault) {
execution_context::ExecutionContextRegistry::GetFromGraph(&graph));
EXPECT_FALSE(v8_memory::V8ContextTracker::GetFromGraph(&graph));
- size_t graph_owned_count = 13;
+ size_t graph_owned_count = 12;
#if !BUILDFLAG(IS_ANDROID)
// The SiteDataRecorder is not available on Android.
graph_owned_count++;
@@ -64,7 +64,7 @@ TEST(GraphFeaturesTest, EnableDefault) {
features.ConfigureGraph(&graph);
EXPECT_EQ(graph_owned_count, graph.GraphOwnedCountForTesting());
EXPECT_EQ(3u, graph.GraphRegisteredCountForTesting());
- EXPECT_EQ(9u, graph.NodeDataDescriberCountForTesting());
+ EXPECT_EQ(8u, graph.NodeDataDescriberCountForTesting());
// Ensure the GraphRegistered objects can be queried directly.
EXPECT_TRUE(
execution_context::ExecutionContextRegistry::GetFromGraph(&graph));
diff --git a/chromium/components/performance_manager/metrics/metrics_collector.cc b/chromium/components/performance_manager/metrics/metrics_collector.cc
index 8d5919504cf..b76d6a737dd 100644
--- a/chromium/components/performance_manager/metrics/metrics_collector.cc
+++ b/chromium/components/performance_manager/metrics/metrics_collector.cc
@@ -5,9 +5,12 @@
#include "components/performance_manager/public/metrics/metrics_collector.h"
#include <set>
+#include <string>
#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
#include "base/time/time.h"
#include "components/performance_manager/public/graph/graph_operations.h"
#include "components/performance_manager/public/graph/node_attached_data.h"
@@ -15,6 +18,16 @@
namespace performance_manager {
+namespace {
+
+void RecordProcessLifetime(const std::string& histogram_name,
+ base::TimeDelta lifetime) {
+ base::UmaHistogramCustomTimes(histogram_name, lifetime, base::Seconds(1),
+ base::Days(1), 100);
+}
+
+} // namespace
+
class MetricsReportRecordHolder
: public ExternalNodeAttachedDataImpl<MetricsReportRecordHolder> {
public:
@@ -96,24 +109,21 @@ void MetricsCollector::OnFaviconUpdated(const PageNode* page_node) {
graph_->GetUkmRecorder());
}
-void MetricsCollector::OnBeforeProcessNodeRemoved(
+void MetricsCollector::OnProcessLifetimeChange(
const ProcessNode* process_node) {
- // Skip non-renderers.
- if (process_node->GetProcessType() != content::PROCESS_TYPE_RENDERER)
+ // Ignore process creation.
+ if (!process_node->GetExitStatus().has_value())
return;
- const base::TimeDelta lifetime =
- base::Time::Now() - process_node->GetLaunchTime();
-
- // Do not record in the rare case system time was adjusted and now < launch
- // time. This could also happen if the process was never launched.
- if (lifetime <= base::TimeDelta())
- return;
+ OnProcessDestroyed(process_node);
+}
- UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.ProcessLifetime2.HighResolution",
- lifetime, base::Seconds(1), base::Minutes(5), 100);
- UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.ProcessLifetime2.LowResolution",
- lifetime, base::Seconds(1), base::Days(1), 100);
+void MetricsCollector::OnBeforeProcessNodeRemoved(
+ const ProcessNode* process_node) {
+ // If the ProcessNode is destroyed with a valid process handle, consider this
+ // the end of the process' life.
+ if (process_node->GetProcess().IsValid())
+ OnProcessDestroyed(process_node);
}
void MetricsCollector::OnTitleUpdated(const PageNode* page_node) {
@@ -185,4 +195,48 @@ void MetricsCollector::MetricsReportRecord::Reset() {
first_title_updated.Reset();
}
+void MetricsCollector::OnProcessDestroyed(const ProcessNode* process_node) {
+ if (process_node->GetProcessType() != content::PROCESS_TYPE_RENDERER)
+ return;
+
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeTicks launch_time = process_node->GetLaunchTime();
+
+ if (launch_time.is_null()) {
+ // Terminating a process quickly after initiating its launch (for example
+ // with FastShutdownIfPossible()) may result in receiving
+ // RenderProcessHostObserver::RenderProcessExited() without a corresponding
+ // RenderProcessHostCreationObserver::OnRenderProcessHostCreated(). In this
+ // case, GetLaunchTime() won't be set. It's correct to report a lifetime of
+ // 0 in this case.
+ launch_time = now;
+ }
+
+ const base::TimeDelta lifetime = now - launch_time;
+ RecordProcessLifetime("Renderer.ProcessLifetime3", lifetime);
+
+ ProcessNode::ContentTypes content_types =
+ process_node->GetHostedContentTypes();
+ if (content_types.Has(ProcessNode::ContentType::kExtension)) {
+ RecordProcessLifetime("Renderer.ProcessLifetime3.Extension", lifetime);
+ } else if (!content_types.Has(ProcessNode::ContentType::kNavigatedFrame)) {
+ if (content_types.Has(ProcessNode::ContentType::kWorker)) {
+ RecordProcessLifetime("Renderer.ProcessLifetime3.Worker", lifetime);
+ } else if (content_types.Has(ProcessNode::ContentType::kMainFrame) ||
+ content_types.Has(ProcessNode::ContentType::kSubframe)) {
+ RecordProcessLifetime("Renderer.ProcessLifetime3.Speculative", lifetime);
+ } else {
+ RecordProcessLifetime("Renderer.ProcessLifetime3.Empty", lifetime);
+ }
+ } else if (content_types.Has(ProcessNode::ContentType::kMainFrame)) {
+ RecordProcessLifetime("Renderer.ProcessLifetime3.MainFrame", lifetime);
+ } else if (content_types.Has(ProcessNode::ContentType::kAd)) {
+ RecordProcessLifetime("Renderer.ProcessLifetime3.Subframe_Ad", lifetime);
+ } else if (content_types.Has(ProcessNode::ContentType::kSubframe)) {
+ RecordProcessLifetime("Renderer.ProcessLifetime3.Subframe_NoAd", lifetime);
+ } else {
+ NOTREACHED();
+ }
+}
+
} // namespace performance_manager
diff --git a/chromium/components/performance_manager/metrics/metrics_collector_unittest.cc b/chromium/components/performance_manager/metrics/metrics_collector_unittest.cc
index 6d55a7028cc..eaa2eac7167 100644
--- a/chromium/components/performance_manager/metrics/metrics_collector_unittest.cc
+++ b/chromium/components/performance_manager/metrics/metrics_collector_unittest.cc
@@ -13,29 +13,27 @@
#include "components/performance_manager/graph/process_node_impl.h"
#include "components/performance_manager/test_support/graph_test_harness.h"
#include "components/ukm/test_ukm_recorder.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"
namespace performance_manager {
+using ContentType = ProcessNode::ContentType;
+using testing::ElementsAre;
+using testing::Pair;
+
const base::TimeDelta kTestMetricsReportDelayTimeout =
kMetricsReportDelayTimeout + base::Seconds(1);
const char kHtmlMimeType[] = "text/html";
-// TODO(crbug.com/759905) Enable on Windows once this bug is fixed.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_MetricsCollectorTest DISABLED_MetricsCollectorTest
-#else
-#define MAYBE_MetricsCollectorTest MetricsCollectorTest
-#endif
-class MAYBE_MetricsCollectorTest : public GraphTestHarness {
+class MetricsCollectorTest : public GraphTestHarness {
public:
using Super = GraphTestHarness;
- MAYBE_MetricsCollectorTest() : GraphTestHarness() {}
+ MetricsCollectorTest() = default;
- MAYBE_MetricsCollectorTest(const MAYBE_MetricsCollectorTest&) = delete;
- MAYBE_MetricsCollectorTest& operator=(const MAYBE_MetricsCollectorTest&) =
- delete;
+ MetricsCollectorTest(const MetricsCollectorTest&) = delete;
+ MetricsCollectorTest& operator=(const MetricsCollectorTest&) = delete;
void SetUp() override {
Super::SetUp();
@@ -59,7 +57,7 @@ class MAYBE_MetricsCollectorTest : public GraphTestHarness {
raw_ptr<MetricsCollector> metrics_collector_ = nullptr;
};
-TEST_F(MAYBE_MetricsCollectorTest, FromBackgroundedToFirstTitleUpdatedUMA) {
+TEST_F(MetricsCollectorTest, FromBackgroundedToFirstTitleUpdatedUMA) {
auto page_node = CreateNode<PageNodeImpl>();
page_node->OnMainFrameNavigationCommitted(
@@ -92,7 +90,7 @@ TEST_F(MAYBE_MetricsCollectorTest, FromBackgroundedToFirstTitleUpdatedUMA) {
2);
}
-TEST_F(MAYBE_MetricsCollectorTest,
+TEST_F(MetricsCollectorTest,
FromBackgroundedToFirstTitleUpdatedUMA5MinutesTimeout) {
auto page_node = CreateNode<PageNodeImpl>();
@@ -111,7 +109,7 @@ TEST_F(MAYBE_MetricsCollectorTest,
1);
}
-TEST_F(MAYBE_MetricsCollectorTest,
+TEST_F(MetricsCollectorTest,
FromBackgroundedToFirstNonPersistentNotificationCreatedUMA) {
auto process_node = CreateNode<ProcessNodeImpl>();
auto page_node = CreateNode<PageNodeImpl>();
@@ -148,7 +146,7 @@ TEST_F(MAYBE_MetricsCollectorTest,
}
TEST_F(
- MAYBE_MetricsCollectorTest,
+ MetricsCollectorTest,
FromBackgroundedToFirstNonPersistentNotificationCreatedUMA5MinutesTimeout) {
auto process_node = CreateNode<ProcessNodeImpl>();
auto page_node = CreateNode<PageNodeImpl>();
@@ -169,7 +167,7 @@ TEST_F(
kTabFromBackgroundedToFirstNonPersistentNotificationCreatedUMA, 1);
}
-TEST_F(MAYBE_MetricsCollectorTest, FromBackgroundedToFirstFaviconUpdatedUMA) {
+TEST_F(MetricsCollectorTest, FromBackgroundedToFirstFaviconUpdatedUMA) {
auto page_node = CreateNode<PageNodeImpl>();
page_node->OnMainFrameNavigationCommitted(
@@ -202,7 +200,7 @@ TEST_F(MAYBE_MetricsCollectorTest, FromBackgroundedToFirstFaviconUpdatedUMA) {
kTabFromBackgroundedToFirstFaviconUpdatedUMA, 2);
}
-TEST_F(MAYBE_MetricsCollectorTest,
+TEST_F(MetricsCollectorTest,
FromBackgroundedToFirstFaviconUpdatedUMA5MinutesTimeout) {
auto page_node = CreateNode<PageNodeImpl>();
@@ -221,4 +219,171 @@ TEST_F(MAYBE_MetricsCollectorTest,
kTabFromBackgroundedToFirstFaviconUpdatedUMA, 1);
}
+TEST_F(MetricsCollectorTest, ProcessLifetime_LaunchAndExit) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node->SetProcessExitStatus(42);
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Empty", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_LaunchAndDelete) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Empty", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_ExitWithoutLaunch) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->SetProcessExitStatus(42);
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Empty", 1)));
+ histogram_tester_.ExpectUniqueTimeSample("Renderer.ProcessLifetime3",
+ base::TimeDelta(), 1);
+ histogram_tester_.ExpectUniqueTimeSample("Renderer.ProcessLifetime3.Empty",
+ base::TimeDelta(), 1);
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_NoLaunchAndNoExit) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre());
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_Extension) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->add_hosted_content_type(ContentType::kMainFrame);
+ process_node->add_hosted_content_type(ContentType::kExtension);
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Extension", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_Extension_Mixed) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->add_hosted_content_type(ContentType::kMainFrame);
+ process_node->add_hosted_content_type(ContentType::kExtension);
+ process_node->add_hosted_content_type(ContentType::kNavigatedFrame);
+ process_node->add_hosted_content_type(ContentType::kSubframe);
+ process_node->add_hosted_content_type(ContentType::kExtension);
+ process_node->add_hosted_content_type(ContentType::kWorker);
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Extension", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_Worker) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->add_hosted_content_type(ContentType::kWorker);
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Worker", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_Speculative) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->add_hosted_content_type(ContentType::kMainFrame);
+ process_node->add_hosted_content_type(ContentType::kSubframe);
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Speculative", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_Empty) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Empty", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_MainFrame) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->add_hosted_content_type(ContentType::kMainFrame);
+ process_node->add_hosted_content_type(ContentType::kNavigatedFrame);
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.MainFrame", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_MainFrame_Mixed) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->add_hosted_content_type(ContentType::kMainFrame);
+ process_node->add_hosted_content_type(ContentType::kNavigatedFrame);
+ process_node->add_hosted_content_type(ContentType::kSubframe);
+ process_node->add_hosted_content_type(ContentType::kAd);
+ process_node->add_hosted_content_type(ContentType::kWorker);
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.MainFrame", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_Subframe_Ad) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->add_hosted_content_type(ContentType::kSubframe);
+ process_node->add_hosted_content_type(ContentType::kAd);
+ process_node->add_hosted_content_type(ContentType::kNavigatedFrame);
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Subframe_Ad", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_Subframe_NoAd) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->add_hosted_content_type(ContentType::kSubframe);
+ process_node->add_hosted_content_type(ContentType::kNavigatedFrame);
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Subframe_NoAd", 1)));
+}
+
+TEST_F(MetricsCollectorTest, ProcessLifetime_Subframe_NoAd_Mixed) {
+ auto process_node = CreateNode<ProcessNodeImpl>();
+ process_node->add_hosted_content_type(ContentType::kSubframe);
+ process_node->add_hosted_content_type(ContentType::kNavigatedFrame);
+ process_node->add_hosted_content_type(ContentType::kWorker);
+ process_node->SetProcess(base::Process::Current(), base::TimeTicks::Now());
+ process_node.reset();
+ EXPECT_THAT(
+ histogram_tester_.GetTotalCountsForPrefix("Renderer.ProcessLifetime3"),
+ ElementsAre(Pair("Renderer.ProcessLifetime3", 1),
+ Pair("Renderer.ProcessLifetime3.Subframe_NoAd", 1)));
+}
+
} // namespace performance_manager
diff --git a/chromium/components/performance_manager/metrics/metrics_provider.cc b/chromium/components/performance_manager/metrics/metrics_provider.cc
new file mode 100644
index 00000000000..b39f80cd734
--- /dev/null
+++ b/chromium/components/performance_manager/metrics/metrics_provider.cc
@@ -0,0 +1,72 @@
+// Copyright 2022 The Chromium 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/performance_manager/public/metrics/metrics_provider.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "components/performance_manager/public/user_tuning/prefs.h"
+#include "components/prefs/pref_service.h"
+
+namespace performance_manager {
+
+MetricsProvider::MetricsProvider(PrefService* local_state)
+ : local_state_(local_state) {
+ pref_change_registrar_.Init(local_state_);
+ pref_change_registrar_.Add(
+ performance_manager::user_tuning::prefs::kHighEfficiencyModeEnabled,
+ base::BindRepeating(&MetricsProvider::OnEfficiencyModeChanged,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ performance_manager::user_tuning::prefs::kBatterySaverModeEnabled,
+ base::BindRepeating(&MetricsProvider::OnEfficiencyModeChanged,
+ base::Unretained(this)));
+
+ current_mode_ = ComputeCurrentMode();
+}
+
+MetricsProvider::~MetricsProvider() = default;
+
+void MetricsProvider::ProvideCurrentSessionData(
+ metrics::ChromeUserMetricsExtension* uma_proto) {
+ base::UmaHistogramEnumeration("PerformanceManager.UserTuning.EfficiencyMode",
+ current_mode_);
+
+ // Set `current_mode_` to represent the state of the modes as they are now, so
+ // that this mode is what is adequately reported at the next report, unless it
+ // changes in the meantime.
+ current_mode_ = ComputeCurrentMode();
+}
+
+void MetricsProvider::OnEfficiencyModeChanged() {
+ EfficiencyMode new_mode = ComputeCurrentMode();
+
+ // If the mode changes between UMA reports, mark it as Mixed for this
+ // interval.
+ if (current_mode_ != new_mode) {
+ current_mode_ = EfficiencyMode::kMixed;
+ }
+}
+
+MetricsProvider::EfficiencyMode MetricsProvider::ComputeCurrentMode() const {
+ bool high_efficiency_enabled = local_state_->GetBoolean(
+ performance_manager::user_tuning::prefs::kHighEfficiencyModeEnabled);
+ bool battery_saver_enabled = local_state_->GetBoolean(
+ performance_manager::user_tuning::prefs::kBatterySaverModeEnabled);
+
+ if (high_efficiency_enabled && battery_saver_enabled) {
+ return EfficiencyMode::kBoth;
+ }
+
+ if (high_efficiency_enabled) {
+ return EfficiencyMode::kHighEfficiency;
+ }
+
+ if (battery_saver_enabled) {
+ return EfficiencyMode::kBatterySaver;
+ }
+
+ return EfficiencyMode::kNormal;
+}
+
+} // namespace performance_manager \ No newline at end of file
diff --git a/chromium/components/performance_manager/metrics/metrics_provider_unittest.cc b/chromium/components/performance_manager/metrics/metrics_provider_unittest.cc
new file mode 100644
index 00000000000..ef18ea4cfc4
--- /dev/null
+++ b/chromium/components/performance_manager/metrics/metrics_provider_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright 2022 The Chromium 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/performance_manager/public/metrics/metrics_provider.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "components/performance_manager/public/user_tuning/prefs.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class PerformanceManagerMetricsProviderTest : public testing::Test {
+ protected:
+ PrefService* local_state() { return &local_state_; }
+
+ void SetHighEfficiencyEnabled(bool enabled) {
+ local_state()->SetBoolean(
+ performance_manager::user_tuning::prefs::kHighEfficiencyModeEnabled,
+ enabled);
+ }
+
+ void SetBatterySaverEnabled(bool enabled) {
+ local_state()->SetBoolean(
+ performance_manager::user_tuning::prefs::kBatterySaverModeEnabled,
+ enabled);
+ }
+
+ void ExpectSingleUniqueSample(
+ const base::HistogramTester& tester,
+ performance_manager::MetricsProvider::EfficiencyMode sample) {
+ tester.ExpectUniqueSample("PerformanceManager.UserTuning.EfficiencyMode",
+ sample, 1);
+ }
+
+ private:
+ void SetUp() override {
+ performance_manager::user_tuning::prefs::RegisterLocalStatePrefs(
+ local_state_.registry());
+ }
+
+ TestingPrefServiceSimple local_state_;
+};
+
+TEST_F(PerformanceManagerMetricsProviderTest, TestNormalMode) {
+ base::HistogramTester tester;
+
+ performance_manager::MetricsProvider provider(local_state());
+ provider.ProvideCurrentSessionData(nullptr);
+
+ ExpectSingleUniqueSample(
+ tester, performance_manager::MetricsProvider::EfficiencyMode::kNormal);
+}
+
+TEST_F(PerformanceManagerMetricsProviderTest, TestMixedMode) {
+ performance_manager::MetricsProvider provider(local_state());
+
+ {
+ base::HistogramTester tester;
+ // Start in normal mode
+ provider.ProvideCurrentSessionData(nullptr);
+ ExpectSingleUniqueSample(
+ tester, performance_manager::MetricsProvider::EfficiencyMode::kNormal);
+ }
+
+ {
+ base::HistogramTester tester;
+ // Enabled High-Efficiency Mode, the next reported value should be "mixed"
+ // because we transitioned from normal to High-Efficiency during the
+ // interval.
+ SetHighEfficiencyEnabled(true);
+ provider.ProvideCurrentSessionData(nullptr);
+ ExpectSingleUniqueSample(
+ tester, performance_manager::MetricsProvider::EfficiencyMode::kMixed);
+ }
+
+ {
+ base::HistogramTester tester;
+ // If another UMA upload happens without mode changes, this one will report
+ // High-Efficiency Mode.
+ provider.ProvideCurrentSessionData(nullptr);
+ ExpectSingleUniqueSample(
+ tester,
+ performance_manager::MetricsProvider::EfficiencyMode::kHighEfficiency);
+ }
+}
+
+TEST_F(PerformanceManagerMetricsProviderTest, TestBothModes) {
+ SetHighEfficiencyEnabled(true);
+ SetBatterySaverEnabled(true);
+
+ performance_manager::MetricsProvider provider(local_state());
+
+ {
+ base::HistogramTester tester;
+ // Start with both modes enabled (such as a Chrome startup after having
+ // enabled both modes in a previous session).
+ provider.ProvideCurrentSessionData(nullptr);
+ ExpectSingleUniqueSample(
+ tester, performance_manager::MetricsProvider::EfficiencyMode::kBoth);
+ }
+
+ {
+ base::HistogramTester tester;
+ // Disabling High-Efficiency Mode will cause the next report to be "mixed".
+ SetHighEfficiencyEnabled(false);
+ provider.ProvideCurrentSessionData(nullptr);
+ ExpectSingleUniqueSample(
+ tester, performance_manager::MetricsProvider::EfficiencyMode::kMixed);
+ }
+
+ {
+ base::HistogramTester tester;
+ // No changes until the following report, "Battery saver" is reported
+ provider.ProvideCurrentSessionData(nullptr);
+ ExpectSingleUniqueSample(
+ tester,
+ performance_manager::MetricsProvider::EfficiencyMode::kBatterySaver);
+ }
+
+ {
+ base::HistogramTester tester;
+ // Re-enabling High-Efficiency Mode will cause the next report to indicate
+ // "mixed".
+ SetHighEfficiencyEnabled(true);
+ provider.ProvideCurrentSessionData(nullptr);
+ ExpectSingleUniqueSample(
+ tester, performance_manager::MetricsProvider::EfficiencyMode::kMixed);
+ }
+
+ {
+ base::HistogramTester tester;
+ // One more report with no changes, this one reports "both" again.
+ provider.ProvideCurrentSessionData(nullptr);
+ ExpectSingleUniqueSample(
+ tester, performance_manager::MetricsProvider::EfficiencyMode::kBoth);
+ }
+} \ No newline at end of file
diff --git a/chromium/components/performance_manager/performance_manager.cc b/chromium/components/performance_manager/performance_manager.cc
index a8f30d7661c..802410e4ecd 100644
--- a/chromium/components/performance_manager/performance_manager.cc
+++ b/chromium/components/performance_manager/performance_manager.cc
@@ -95,8 +95,8 @@ base::WeakPtr<FrameNode> PerformanceManager::GetFrameNodeForRenderFrameHost(
auto* frame_node = helper->GetFrameNode(rfh);
if (!frame_node) {
// This should only happen if GetFrameNodeForRenderFrameHost is called
- // before the RenderFrameCreate notification is dispatched.
- DCHECK(!rfh->IsRenderFrameCreated());
+ // before the RenderFrameCreated notification is dispatched.
+ DCHECK(!rfh->IsRenderFrameLive());
return nullptr;
}
return frame_node->GetWeakPtrOnUIThread();
diff --git a/chromium/components/performance_manager/performance_manager_browsertest.cc b/chromium/components/performance_manager/performance_manager_browsertest.cc
index 39e742ab431..0f21d74aa54 100644
--- a/chromium/components/performance_manager/performance_manager_browsertest.cc
+++ b/chromium/components/performance_manager/performance_manager_browsertest.cc
@@ -43,8 +43,8 @@ IN_PROC_BROWSER_TEST_F(PerformanceManagerBrowserTest,
content::WebContents* contents = shell()->web_contents();
ASSERT_EQ(contents, old_contents);
ASSERT_EQ(contents->GetLastCommittedURL().possibly_invalid_spec(), kUrl);
- content::RenderFrameHost* rfh = contents->GetMainFrame();
- ASSERT_TRUE(rfh->IsRenderFrameCreated());
+ content::RenderFrameHost* rfh = contents->GetPrimaryMainFrame();
+ ASSERT_TRUE(rfh->IsRenderFrameLive());
base::WeakPtr<FrameNode> frame_node =
PerformanceManager::GetFrameNodeForRenderFrameHost(rfh);
@@ -152,7 +152,7 @@ IN_PROC_BROWSER_TEST_F(PerformanceManagerFencedFrameBrowserTest,
embedded_test_server()->GetURL("/fenced_frames/title1.html");
content::RenderFrameHost* fenced_frame_host =
fenced_frame_test_helper().CreateFencedFrame(
- GetWebContents()->GetMainFrame(), fenced_frame_url);
+ GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url);
// Jump into the graph and make sure |fenced_frame_host| does not have a
// parent frame node.
diff --git a/chromium/components/performance_manager/performance_manager_lifetime.cc b/chromium/components/performance_manager/performance_manager_lifetime.cc
index 9224873a249..3a991d1c7bb 100644
--- a/chromium/components/performance_manager/performance_manager_lifetime.cc
+++ b/chromium/components/performance_manager/performance_manager_lifetime.cc
@@ -17,7 +17,6 @@
#include "components/performance_manager/graph/worker_node_impl_describer.h"
#include "components/performance_manager/performance_manager_impl.h"
#include "components/performance_manager/public/decorators/page_live_state_decorator.h"
-#include "components/performance_manager/public/decorators/tab_properties_decorator.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/v8_memory/v8_context_tracker.h"
diff --git a/chromium/components/performance_manager/performance_manager_registry_impl.cc b/chromium/components/performance_manager/performance_manager_registry_impl.cc
index 0533f7cfb27..10dc73d2350 100644
--- a/chromium/components/performance_manager/performance_manager_registry_impl.cc
+++ b/chromium/components/performance_manager/performance_manager_registry_impl.cc
@@ -9,12 +9,14 @@
#include "base/observer_list.h"
#include "components/performance_manager/embedder/binders.h"
+#include "components/performance_manager/graph/page_node_impl.h"
#include "components/performance_manager/performance_manager_tab_helper.h"
#include "components/performance_manager/public/mojom/coordination_unit.mojom.h"
#include "components/performance_manager/public/performance_manager.h"
#include "components/performance_manager/public/performance_manager_main_thread_mechanism.h"
#include "components/performance_manager/public/performance_manager_main_thread_observer.h"
#include "components/performance_manager/public/performance_manager_owned.h"
+#include "components/performance_manager/render_process_user_data.h"
#include "components/performance_manager/service_worker_context_adapter.h"
#include "components/performance_manager/worker_watcher.h"
#include "content/public/browser/browser_context.h"
@@ -134,6 +136,22 @@ void PerformanceManagerRegistryImpl::CreatePageNodeForWebContents(
observer.OnPageNodeCreatedForWebContents(web_contents);
}
+void PerformanceManagerRegistryImpl::SetPageType(
+ content::WebContents* web_contents,
+ PageType type) {
+ PerformanceManagerTabHelper* tab_helper =
+ PerformanceManagerTabHelper::FromWebContents(web_contents);
+ DCHECK(tab_helper);
+
+ PerformanceManager::CallOnGraph(
+ FROM_HERE,
+ // Unretained() is safe because PerformanceManagerTabHelper owns the
+ // PageNodeImpl and deletes it by posting a task to the PerformanceManager
+ // sequence, which will be sequenced after the task posted here.
+ base::BindOnce(&PageNodeImpl::SetType,
+ base::Unretained(tab_helper->primary_page_node()), type));
+}
+
PerformanceManagerRegistryImpl::Throttles
PerformanceManagerRegistryImpl::CreateThrottlesForNavigation(
content::NavigationHandle* handle) {
@@ -295,4 +313,17 @@ void PerformanceManagerRegistryImpl::EnsureProcessNodeForRenderProcessHost(
}
}
+void PerformanceManagerRegistryImpl::OnRenderProcessHostCreated(
+ content::RenderProcessHost* host) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Create the ProcessNode if it doesn't already exist. This is the case in
+ // web_tests and content_browsertests which do not invoke
+ // CreateProcessNodeAndExposeInterfacesToRendererProcess().
+ EnsureProcessNodeForRenderProcessHost(host);
+
+ // Notify the ProcessNode that its process was launched.
+ RenderProcessUserData::GetForRenderProcessHost(host)->OnProcessLaunched();
+}
+
} // namespace performance_manager
diff --git a/chromium/components/performance_manager/performance_manager_registry_impl.h b/chromium/components/performance_manager/performance_manager_registry_impl.h
index 309c702f27f..e69e5316e72 100644
--- a/chromium/components/performance_manager/performance_manager_registry_impl.h
+++ b/chromium/components/performance_manager/performance_manager_registry_impl.h
@@ -20,6 +20,7 @@
#include "components/performance_manager/registered_objects.h"
#include "components/performance_manager/render_process_user_data.h"
#include "components/performance_manager/tab_helper_frame_node_source.h"
+#include "content/public/browser/render_process_host_creation_observer.h"
namespace content {
class RenderProcessHost;
@@ -34,7 +35,8 @@ class ServiceWorkerContextAdapter;
class WorkerWatcher;
class PerformanceManagerRegistryImpl
- : public PerformanceManagerRegistry,
+ : public content::RenderProcessHostCreationObserver,
+ public PerformanceManagerRegistry,
public PerformanceManagerTabHelper::DestructionObserver,
public RenderProcessUserData::DestructionObserver {
public:
@@ -75,6 +77,7 @@ class PerformanceManagerRegistryImpl
// PerformanceManagerRegistry:
void CreatePageNodeForWebContents(
content::WebContents* web_contents) override;
+ void SetPageType(content::WebContents* web_contents, PageType type) override;
Throttles CreateThrottlesForNavigation(
content::NavigationHandle* handle) override;
void NotifyBrowserContextAdded(
@@ -115,6 +118,9 @@ class PerformanceManagerRegistryImpl
private:
SEQUENCE_CHECKER(sequence_checker_);
+ // content::RenderProcessHostCreationObserver:
+ void OnRenderProcessHostCreated(content::RenderProcessHost* host) override;
+
// Tracks WebContents and RenderProcessHost for which we have created user
// data. Used to destroy all user data when the registry is destroyed.
base::flat_set<content::WebContents*> web_contents_
diff --git a/chromium/components/performance_manager/performance_manager_tab_helper.cc b/chromium/components/performance_manager/performance_manager_tab_helper.cc
index 433e7726416..3be2b58f03f 100644
--- a/chromium/components/performance_manager/performance_manager_tab_helper.cc
+++ b/chromium/components/performance_manager/performance_manager_tab_helper.cc
@@ -39,9 +39,12 @@ bool ConnectWindowOpenRelationshipIfExists(PerformanceManagerTabHelper* helper,
if (!opener_rfh) {
// If the child page is opened with "noopener" then the parent document
// maintains the ability to close the child, but the child can't reach back
- // and see it's parent. In this case there will be no "Opener", but there
- // will be an "OriginalOpener".
- opener_rfh = web_contents->GetOriginalOpener();
+ // and see it's parent. In this case there will be no "opener", but there
+ // will be an "original opener".
+ if (content::WebContents* original_opener_wc =
+ web_contents->GetFirstWebContentsInLiveOriginalOpenerChain()) {
+ opener_rfh = original_opener_wc->GetPrimaryMainFrame();
+ }
}
if (!opener_rfh)
@@ -83,7 +86,7 @@ PerformanceManagerTabHelper::PerformanceManagerTabHelper(
// We have an early WebContents creation hook so should see it when there is
// only a single frame, and it is not yet created. We sanity check that here.
#if DCHECK_IS_ON()
- DCHECK(!web_contents->GetMainFrame()->IsRenderFrameCreated());
+ DCHECK(!web_contents->GetPrimaryMainFrame()->IsRenderFrameLive());
size_t frame_count = 0;
web_contents->ForEachRenderFrameHost(base::BindRepeating(
[](size_t* frame_count, content::RenderFrameHost* render_frame_host) {
@@ -103,7 +106,7 @@ PerformanceManagerTabHelper::PerformanceManagerTabHelper(
web_contents->IsCurrentlyAudible(), web_contents->GetLastActiveTime(),
// TODO(crbug.com/1211368): Support MPArch fully!
PageNode::PageState::kActive);
- content::RenderFrameHost* main_rfh = web_contents->GetMainFrame();
+ content::RenderFrameHost* main_rfh = web_contents->GetPrimaryMainFrame();
DCHECK(main_rfh);
page->main_frame_tree_node_id = main_rfh->GetFrameTreeNodeId();
primary_page_ = page.get();
@@ -265,7 +268,7 @@ void PerformanceManagerTabHelper::RenderFrameHostChanged(
if (it != frames_.end()) {
new_frame = it->second.get();
} else {
- DCHECK(!new_host->IsRenderFrameCreated())
+ DCHECK(!new_host->IsRenderFrameLive())
<< "There shouldn't be a case where RenderFrameHostChanged is "
"dispatched before RenderFrameCreated with a live RenderFrame\n";
}
diff --git a/chromium/components/performance_manager/performance_manager_tab_helper_unittest.cc b/chromium/components/performance_manager/performance_manager_tab_helper_unittest.cc
index 6d829aa13de..b9b42eb2aa6 100644
--- a/chromium/components/performance_manager/performance_manager_tab_helper_unittest.cc
+++ b/chromium/components/performance_manager/performance_manager_tab_helper_unittest.cc
@@ -240,7 +240,7 @@ TEST_F(PerformanceManagerTabHelperTest, GetFrameNode) {
// GetFrameNode() can return nullptr. In this test, it is achieved by using an
// empty RenderFrameHost.
- auto* empty_frame = web_contents()->GetMainFrame();
+ auto* empty_frame = web_contents()->GetPrimaryMainFrame();
DCHECK(empty_frame);
auto* empty_frame_node = tab_helper->GetFrameNode(empty_frame);
@@ -278,13 +278,13 @@ TEST_F(PerformanceManagerTabHelperTest,
content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
GURL(kParentUrl));
- auto* first_nav_main_rfh = web_contents()->GetMainFrame();
+ auto* first_nav_main_rfh = web_contents()->GetPrimaryMainFrame();
content::LeaveInPendingDeletionState(first_nav_main_rfh);
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL(kCousinFreddyUrl));
- EXPECT_NE(web_contents()->GetMainFrame(), first_nav_main_rfh);
+ EXPECT_NE(web_contents()->GetPrimaryMainFrame(), first_nav_main_rfh);
// Mock observer, this can only be used from the PM sequence.
MockPageNodeObserver observer;
@@ -325,8 +325,8 @@ TEST_F(PerformanceManagerTabHelperTest,
// Sanity check to ensure that notification sent to the active main frame are
// forwarded. DidUpdateFaviconURL needs to be called twice as the first
// favicon change is always ignored.
- tab_helper->DidUpdateFaviconURL(web_contents()->GetMainFrame(), {});
- tab_helper->DidUpdateFaviconURL(web_contents()->GetMainFrame(), {});
+ tab_helper->DidUpdateFaviconURL(web_contents()->GetPrimaryMainFrame(), {});
+ tab_helper->DidUpdateFaviconURL(web_contents()->GetPrimaryMainFrame(), {});
{
base::RunLoop run_loop;
diff --git a/chromium/components/performance_manager/performance_manager_unittest.cc b/chromium/components/performance_manager/performance_manager_unittest.cc
index 39ada3e6f5a..d65e7bb52ff 100644
--- a/chromium/components/performance_manager/performance_manager_unittest.cc
+++ b/chromium/components/performance_manager/performance_manager_unittest.cc
@@ -50,7 +50,7 @@ class PerformanceManagerTest : public PerformanceManagerTestHarness {
TEST_F(PerformanceManagerTest, NodeAccessors) {
auto contents = CreateTestWebContents();
- content::RenderFrameHost* rfh = contents->GetMainFrame();
+ content::RenderFrameHost* rfh = contents->GetPrimaryMainFrame();
ASSERT_TRUE(rfh);
content::RenderProcessHost* rph = rfh->GetProcess();
ASSERT_TRUE(rph);
diff --git a/chromium/components/performance_manager/prerendering_browsertest.cc b/chromium/components/performance_manager/prerendering_browsertest.cc
index dfaf6aa6f4d..4bc4501f4a3 100644
--- a/chromium/components/performance_manager/prerendering_browsertest.cc
+++ b/chromium/components/performance_manager/prerendering_browsertest.cc
@@ -131,7 +131,7 @@ IN_PROC_BROWSER_TEST_F(PerformanceManagerPrerenderingBrowserTest,
// Activate the prerendered document. Test that GetMainFrameNode now returns
// its main frame, and the original frame tree is gone.
content::RenderFrameDeletedObserver deleted_observer(
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
content::test::PrerenderHostObserver prerender_observer(*web_contents(),
kPrerenderingUrl);
prerender_helper_.NavigatePrimaryPage(kPrerenderingUrl);
diff --git a/chromium/components/performance_manager/public/decorators/tab_properties_decorator.h b/chromium/components/performance_manager/public/decorators/tab_properties_decorator.h
deleted file mode 100644
index 68bf028298f..00000000000
--- a/chromium/components/performance_manager/public/decorators/tab_properties_decorator.h
+++ /dev/null
@@ -1,65 +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 COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_DECORATORS_TAB_PROPERTIES_DECORATOR_H_
-#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_DECORATORS_TAB_PROPERTIES_DECORATOR_H_
-
-#include "components/performance_manager/public/graph/graph.h"
-#include "components/performance_manager/public/graph/node_data_describer.h"
-
-namespace content {
-class WebContents;
-} // namespace content
-
-namespace performance_manager {
-
-class PageNode;
-
-// The TabProperties decorator is responsible for tracking properties of
-// PageNodes that are tabs. All the functions that take a WebContents* as a
-// parameter should only be called from the UI thread, the event will be
-// forwarded to the corresponding PageNode on the Performance Manager's
-// sequence.
-class TabPropertiesDecorator : public GraphOwned,
- public NodeDataDescriberDefaultImpl {
- public:
- class Data;
-
- // This object should only be used via its static methods.
- TabPropertiesDecorator() = default;
- ~TabPropertiesDecorator() override = default;
- TabPropertiesDecorator(const TabPropertiesDecorator& other) = delete;
- TabPropertiesDecorator& operator=(const TabPropertiesDecorator&) = delete;
-
- // Set the is_tab property of a PageNode.
- static void SetIsTab(content::WebContents* contents, bool is_tab);
-
- static void SetIsTabForTesting(PageNode* page_node, bool is_tab);
-
- private:
- // GraphOwned implementation:
- void OnPassedToGraph(Graph* graph) override;
- void OnTakenFromGraph(Graph* graph) override;
-
- // NodeDataDescriber implementation:
- base::Value DescribePageNodeData(const PageNode* node) const override;
-};
-
-class TabPropertiesDecorator::Data {
- public:
- Data();
- virtual ~Data();
- Data(const Data& other) = delete;
- Data& operator=(const Data&) = delete;
-
- // Indicates if a PageNode belongs to a tab strip.
- virtual bool IsInTabStrip() const = 0;
-
- static const Data* FromPageNode(const PageNode* page_node);
- static Data* GetOrCreateForTesting(const PageNode* page_node);
-};
-
-} // namespace performance_manager
-
-#endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_DECORATORS_TAB_PROPERTIES_DECORATOR_H_
diff --git a/chromium/components/performance_manager/public/features.h b/chromium/components/performance_manager/public/features.h
index 5e9402f2ec4..2863748ee0b 100644
--- a/chromium/components/performance_manager/public/features.h
+++ b/chromium/components/performance_manager/public/features.h
@@ -60,6 +60,17 @@ extern const base::Feature kHighPMFDiscardPolicy;
// Enable background tab loading of pages (restored via session restore)
// directly from Performance Manager rather than via TabLoader.
extern const base::Feature kBackgroundTabLoadingFromPerformanceManager;
+
+// Make the High-Efficiency or Battery Saver Modes available to users. If this
+// is enabled, it doesn't mean the specific Mode is enabled, just that the user
+// has the option of toggling it.
+extern const base::Feature kHighEfficiencyModeAvailable;
+extern const base::Feature kBatterySaverModeAvailable;
+
+// Defines the time in seconds before a background tab is discarded for
+// High-Efficiency Mode.
+extern const base::FeatureParam<base::TimeDelta>
+ kHighEfficiencyModeTimeBeforeDiscard;
#endif
// Policy that evicts the BFCache of pages that become non visible or the
diff --git a/chromium/components/performance_manager/public/graph/page_node.h b/chromium/components/performance_manager/public/graph/page_node.h
index 059e101c2d0..d15a2f9d35d 100644
--- a/chromium/components/performance_manager/public/graph/page_node.h
+++ b/chromium/components/performance_manager/public/graph/page_node.h
@@ -25,6 +25,15 @@ namespace performance_manager {
class FrameNode;
class PageNodeObserver;
+enum class PageType {
+ // A browser tab.
+ kTab,
+ // An extension background page.
+ kExtension,
+ // Anything else.
+ kUnknown,
+};
+
// A PageNode represents the root of a FrameTree, or equivalently a WebContents.
// These may correspond to normal tabs, WebViews, Portals, Chrome Apps or
// Extensions.
@@ -70,7 +79,8 @@ class PageNode : public Node {
kLoadedIdle,
};
- // Returns a string for a PageNode::LoadingState enumeration.
+ // Returns a string for an enumeration value.
+ static const char* ToString(PageType type);
static const char* ToString(PageNode::LoadingState loading_state);
// State of a page. Pages can be born in "kActive" or "kPrerendering" state.
@@ -114,6 +124,9 @@ class PageNode : public Node {
// an embedder.
virtual EmbeddingType GetEmbeddingType() const = 0;
+ // Returns the type of the page.
+ virtual PageType GetType() const = 0;
+
// Returns true if this page is currently visible, false otherwise.
// See PageNodeObserver::OnIsVisibleChanged.
virtual bool IsVisible() const = 0;
@@ -249,6 +262,9 @@ class PageNodeObserver {
const FrameNode* previous_embedder,
EmbeddingType previous_embedder_type) = 0;
+ // Invoked when the GetType property changes.
+ virtual void OnTypeChanged(const PageNode* page_node) = 0;
+
// Invoked when the IsVisible property changes.
virtual void OnIsVisibleChanged(const PageNode* page_node) = 0;
@@ -327,6 +343,7 @@ class PageNode::ObserverDefaultImpl : public PageNodeObserver {
const PageNode* page_node,
const FrameNode* previous_embedder,
EmbeddingType previous_embedding_type) override {}
+ void OnTypeChanged(const PageNode* page_node) override {}
void OnIsVisibleChanged(const PageNode* page_node) override {}
void OnIsAudibleChanged(const PageNode* page_node) override {}
void OnLoadingStateChanged(const PageNode* page_node,
diff --git a/chromium/components/performance_manager/public/graph/process_node.h b/chromium/components/performance_manager/public/graph/process_node.h
index 27072bbe052..399f7512f4e 100644
--- a/chromium/components/performance_manager/public/graph/process_node.h
+++ b/chromium/components/performance_manager/public/graph/process_node.h
@@ -46,13 +46,23 @@ class ProcessNode : public Node {
// The type of content a renderer can host.
enum class ContentType : uint32_t {
+ // Hosted an extension.
kExtension = 1 << 0,
+ // Hosted a frame with no parent.
kMainFrame = 1 << 1,
- kAd = 1 << 2,
+ // Hosted a frame with a parent.
+ kSubframe = 1 << 2,
+ // Hosted a frame (main frame or subframe) with a committed navigation. A
+ // "speculative" frame will not have a committed navigation.
+ kNavigatedFrame = 1 << 3,
+ // Hosted a frame that was tagged as an ad.
+ kAd = 1 << 4,
+ // Hosted a worker (service worker, dedicated worker, shared worker).
+ kWorker = 1 << 5,
};
using ContentTypes =
- base::EnumSet<ContentType, ContentType::kExtension, ContentType::kAd>;
+ base::EnumSet<ContentType, ContentType::kExtension, ContentType::kWorker>;
ProcessNode();
@@ -70,21 +80,24 @@ class ProcessNode : public Node {
// process ID for a process that has exited (at least until the underlying
// RenderProcessHost gets reused in the case of a crash). Refrain from using
// this as a unique identifier as on some platforms PIDs are reused
- // aggressively. See GetLaunchTime for more information.
+ // aggressively.
virtual base::ProcessId GetProcessId() const = 0;
// Returns the base::Process backing this process. This will be an invalid
// process if it has not yet started, or if it has exited.
virtual const base::Process& GetProcess() const = 0;
- // Returns the launch time associated with the process. Combined with the
- // process ID this can be used as a unique identifier for the process.
- virtual base::Time GetLaunchTime() const = 0;
+ // Returns a time captured as early as possible after the process is launched.
+ virtual base::TimeTicks GetLaunchTime() const = 0;
// Returns the exit status of this process. This will be empty if the process
// has not yet exited.
virtual absl::optional<int32_t> GetExitStatus() const = 0;
+ // Returns the non-localized name of the process used for metrics reporting
+ // metrics as specified in content::ChildProcessData during process creation.
+ virtual const std::string& GetMetricsName() const = 0;
+
// Visits the frame nodes that are hosted in this process. The iteration is
// halted if the visitor returns false. Returns true if every call to the
// visitor returned true, false otherwise.
diff --git a/chromium/components/performance_manager/public/metrics/metrics_collector.h b/chromium/components/performance_manager/public/metrics/metrics_collector.h
index bca57824c2d..7d501ac43ce 100644
--- a/chromium/components/performance_manager/public/metrics/metrics_collector.h
+++ b/chromium/components/performance_manager/public/metrics/metrics_collector.h
@@ -52,6 +52,7 @@ class MetricsCollector : public FrameNode::ObserverDefaultImpl,
void OnTitleUpdated(const PageNode* page_node) override;
// ProcessNodeObserver implementation:
+ void OnProcessLifetimeChange(const ProcessNode* process_node) override;
void OnBeforeProcessNodeRemoved(const ProcessNode* process_node) override;
protected:
@@ -98,7 +99,9 @@ class MetricsCollector : public FrameNode::ObserverDefaultImpl,
bool ShouldReportMetrics(const PageNode* page_node);
void UpdateUkmSourceIdForPage(const PageNode* page_node,
ukm::SourceId ukm_source_id);
- void ResetMetricsReportRecord(const PageNode* page_nod);
+ void ResetMetricsReportRecord(const PageNode* page_node);
+
+ void OnProcessDestroyed(const ProcessNode* process_node);
// The graph to which this object belongs.
raw_ptr<Graph> graph_ = nullptr;
diff --git a/chromium/components/performance_manager/public/metrics/metrics_provider.h b/chromium/components/performance_manager/public/metrics/metrics_provider.h
new file mode 100644
index 00000000000..d940b9f9c66
--- /dev/null
+++ b/chromium/components/performance_manager/public/metrics/metrics_provider.h
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_METRICS_METRICS_PROVIDER_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_METRICS_METRICS_PROVIDER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "components/metrics/metrics_provider.h"
+
+#include "components/prefs/pref_change_registrar.h"
+
+class PrefService;
+
+namespace performance_manager {
+
+// A metrics provider to add some performance manager related metrics to the UMA
+// protos on each upload.
+class MetricsProvider : public metrics::MetricsProvider {
+ public:
+ enum class EfficiencyMode {
+ // No efficiency mode for the entire upload window
+ kNormal = 0,
+ // In high efficiency mode for the entire upload window
+ kHighEfficiency = 1,
+ // In battery saver mode for the entire upload window
+ kBatterySaver = 2,
+ // Both modes enabled for the entire upload window
+ kBoth = 3,
+ // The modes were changed during the upload window
+ kMixed = 4,
+ // Max value, used in UMA histograms macros
+ kMaxValue = kMixed
+ };
+
+ explicit MetricsProvider(PrefService* local_state);
+ ~MetricsProvider() override;
+
+ // metrics::MetricsProvider:
+ // This is only called from UMA code but is public for testing.
+ void ProvideCurrentSessionData(
+ metrics::ChromeUserMetricsExtension* uma_proto) override;
+
+ private:
+ void OnEfficiencyModeChanged();
+ EfficiencyMode ComputeCurrentMode() const;
+
+ PrefChangeRegistrar pref_change_registrar_;
+ const raw_ptr<PrefService> local_state_;
+ EfficiencyMode current_mode_ = EfficiencyMode::kNormal;
+};
+
+} // namespace performance_manager
+
+#endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_METRICS_METRICS_PROVIDER_H_
diff --git a/chromium/components/performance_manager/public/user_tuning/prefs.h b/chromium/components/performance_manager/public/user_tuning/prefs.h
index a2ea8c2d426..50edf6a0461 100644
--- a/chromium/components/performance_manager/public/user_tuning/prefs.h
+++ b/chromium/components/performance_manager/public/user_tuning/prefs.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_USER_TUNING_PREFS_H_
#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_USER_TUNING_PREFS_H_
+class PrefRegistrySimple;
+
namespace user_prefs {
class PrefRegistrySyncable;
}
@@ -15,6 +17,10 @@ extern const char kHighEfficiencyModeEnabled[];
extern const char kBatterySaverModeEnabled[];
+extern const char kTabDiscardingExceptions[];
+
+void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+
void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
} // namespace performance_manager::user_tuning::prefs
diff --git a/chromium/components/performance_manager/render_process_host_proxy_browsertest.cc b/chromium/components/performance_manager/render_process_host_proxy_browsertest.cc
index 5e019d65c74..0ec8a5a9c78 100644
--- a/chromium/components/performance_manager/render_process_host_proxy_browsertest.cc
+++ b/chromium/components/performance_manager/render_process_host_proxy_browsertest.cc
@@ -44,7 +44,7 @@ IN_PROC_BROWSER_TEST_F(RenderProcessHostProxyTest,
// Get the RPH associated with the main frame.
content::RenderProcessHost* host =
- shell()->web_contents()->GetMainFrame()->GetProcess();
+ shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
// And its associated ProcessNode.
auto* render_process_user_data =
diff --git a/chromium/components/performance_manager/render_process_user_data.cc b/chromium/components/performance_manager/render_process_user_data.cc
index 640ef3a0c65..e8e2b57d3f1 100644
--- a/chromium/components/performance_manager/render_process_user_data.cc
+++ b/chromium/components/performance_manager/render_process_user_data.cc
@@ -63,6 +63,15 @@ void RenderProcessUserData::SetDestructionObserver(
destruction_observer_ = destruction_observer;
}
+void RenderProcessUserData::OnProcessLaunched() {
+ DCHECK(host_->GetProcess().IsValid());
+ PerformanceManagerImpl::CallOnGraphImpl(
+ FROM_HERE, base::BindOnce(&ProcessNodeImpl::SetProcess,
+ base::Unretained(process_node_.get()),
+ host_->GetProcess().Duplicate(),
+ /* launch_time=*/base::TimeTicks::Now()));
+}
+
// static
RenderProcessUserData* RenderProcessUserData::CreateForRenderProcessHost(
content::RenderProcessHost* host) {
@@ -74,24 +83,6 @@ RenderProcessUserData* RenderProcessUserData::CreateForRenderProcessHost(
return raw_user_data;
}
-void RenderProcessUserData::RenderProcessReady(
- content::RenderProcessHost* host) {
- const base::Time launch_time =
-#if BUILDFLAG(IS_ANDROID)
- // Process::CreationTime() is not available on Android. Since this
- // method is called immediately after the process is launched, the
- // process launch time can be approximated with the current time.
- base::Time::Now();
-#else
- host->GetProcess().CreationTime();
-#endif
-
- PerformanceManagerImpl::CallOnGraphImpl(
- FROM_HERE, base::BindOnce(&ProcessNodeImpl::SetProcess,
- base::Unretained(process_node_.get()),
- host->GetProcess().Duplicate(), launch_time));
-}
-
void RenderProcessUserData::RenderProcessExited(
content::RenderProcessHost* host,
const content::ChildProcessTerminationInfo& info) {
diff --git a/chromium/components/performance_manager/render_process_user_data.h b/chromium/components/performance_manager/render_process_user_data.h
index 29049fb888f..2a393111999 100644
--- a/chromium/components/performance_manager/render_process_user_data.h
+++ b/chromium/components/performance_manager/render_process_user_data.h
@@ -50,6 +50,10 @@ class RenderProcessUserData : public base::SupportsUserData::Data,
// vice-versa.
void SetDestructionObserver(DestructionObserver* destruction_observer);
+ // Invoked when a process is launched for this RenderProcessHost
+ // (immediately after RenderProcessHost::GetProcess() becomes valid).
+ void OnProcessLaunched();
+
ProcessNodeImpl* process_node() { return process_node_.get(); }
private:
@@ -64,7 +68,6 @@ class RenderProcessUserData : public base::SupportsUserData::Data,
content::RenderProcessHost* host);
// RenderProcessHostObserver overrides
- void RenderProcessReady(content::RenderProcessHost* host) override;
void RenderProcessExited(
content::RenderProcessHost* host,
const content::ChildProcessTerminationInfo& info) override;
diff --git a/chromium/components/performance_manager/service_worker_context_adapter.cc b/chromium/components/performance_manager/service_worker_context_adapter.cc
index 0f88c050b7d..032e6baa7ce 100644
--- a/chromium/components/performance_manager/service_worker_context_adapter.cc
+++ b/chromium/components/performance_manager/service_worker_context_adapter.cc
@@ -194,6 +194,21 @@ void ServiceWorkerContextAdapter::StartWorkerForScope(
NOTIMPLEMENTED();
}
+bool ServiceWorkerContextAdapter::IsLiveRunningServiceWorker(
+ int64_t service_worker_version_id) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+service_manager::InterfaceProvider&
+ServiceWorkerContextAdapter::GetRemoteInterfaces(
+ int64_t service_worker_version_id) {
+ NOTIMPLEMENTED();
+ static service_manager::InterfaceProvider interface_provider(
+ base::ThreadTaskRunnerHandle::Get());
+ return interface_provider;
+}
+
void ServiceWorkerContextAdapter::StartServiceWorkerAndDispatchMessage(
const GURL& scope,
const blink::StorageKey& key,
diff --git a/chromium/components/performance_manager/service_worker_context_adapter.h b/chromium/components/performance_manager/service_worker_context_adapter.h
index b65457560e4..42ba7363f61 100644
--- a/chromium/components/performance_manager/service_worker_context_adapter.h
+++ b/chromium/components/performance_manager/service_worker_context_adapter.h
@@ -100,6 +100,9 @@ class ServiceWorkerContextAdapter
const base::flat_map<int64_t /* version_id */,
content::ServiceWorkerRunningInfo>&
GetRunningServiceWorkerInfos() override;
+ bool IsLiveRunningServiceWorker(int64_t service_worker_version_id) override;
+ service_manager::InterfaceProvider& GetRemoteInterfaces(
+ int64_t service_worker_version_id) override;
// content::ServiceWorkerContextObserver:
void OnRegistrationCompleted(const GURL& scope) override;
diff --git a/chromium/components/performance_manager/test_support/mock_graphs.cc b/chromium/components/performance_manager/test_support/mock_graphs.cc
index 2d71ead4d3e..d490b73c0e5 100644
--- a/chromium/components/performance_manager/test_support/mock_graphs.cc
+++ b/chromium/components/performance_manager/test_support/mock_graphs.cc
@@ -23,7 +23,7 @@ TestProcessNodeImpl::TestProcessNodeImpl()
void TestProcessNodeImpl::SetProcessWithPid(base::ProcessId pid,
base::Process process,
- base::Time launch_time) {
+ base::TimeTicks launch_time) {
SetProcessImpl(std::move(process), pid, launch_time);
}
@@ -33,7 +33,8 @@ MockSinglePageInSingleProcessGraph::MockSinglePageInSingleProcessGraph(
process(TestNodeWrapper<TestProcessNodeImpl>::Create(graph)),
page(TestNodeWrapper<PageNodeImpl>::Create(graph)),
frame(graph->CreateFrameNodeAutoId(process.get(), page.get())) {
- process->SetProcessWithPid(1, base::Process::Current(), base::Time::Now());
+ process->SetProcessWithPid(1, base::Process::Current(),
+ /* launch_time=*/base::TimeTicks::Now());
}
MockSinglePageInSingleProcessGraph::~MockSinglePageInSingleProcessGraph() {
@@ -64,7 +65,7 @@ MockSinglePageWithMultipleProcessesGraph::
page.get(),
frame.get())) {
other_process->SetProcessWithPid(2, base::Process::Current(),
- base::Time::Now());
+ /* launch_time=*/base::TimeTicks::Now());
}
MockSinglePageWithMultipleProcessesGraph::
@@ -78,7 +79,7 @@ MockMultiplePagesWithMultipleProcessesGraph::
other_page.get(),
other_frame.get())) {
other_process->SetProcessWithPid(2, base::Process::Current(),
- base::Time::Now());
+ /* launch_time=*/base::TimeTicks::Now());
}
MockMultiplePagesWithMultipleProcessesGraph::
diff --git a/chromium/components/performance_manager/test_support/mock_graphs.h b/chromium/components/performance_manager/test_support/mock_graphs.h
index ac3ef83731c..e25e997693f 100644
--- a/chromium/components/performance_manager/test_support/mock_graphs.h
+++ b/chromium/components/performance_manager/test_support/mock_graphs.h
@@ -22,7 +22,7 @@ class TestProcessNodeImpl : public ProcessNodeImpl {
void SetProcessWithPid(base::ProcessId pid,
base::Process process,
- base::Time launch_time);
+ base::TimeTicks launch_time);
};
// The following graph topology is created to emulate a scenario when a single
diff --git a/chromium/components/performance_manager/user_tuning/prefs.cc b/chromium/components/performance_manager/user_tuning/prefs.cc
index 0c698319226..997d5534bdb 100644
--- a/chromium/components/performance_manager/user_tuning/prefs.cc
+++ b/chromium/components/performance_manager/user_tuning/prefs.cc
@@ -5,6 +5,7 @@
#include "components/performance_manager/public/user_tuning/prefs.h"
#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry_simple.h"
namespace performance_manager::user_tuning::prefs {
@@ -14,9 +15,16 @@ const char kHighEfficiencyModeEnabled[] =
const char kBatterySaverModeEnabled[] =
"performance_tuning.battery_saver_mode.enabled";
-void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
+const char kTabDiscardingExceptions[] =
+ "performance_tuning.tab_discarding.exceptions";
+
+void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(kHighEfficiencyModeEnabled, false);
registry->RegisterBooleanPref(kBatterySaverModeEnabled, false);
}
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterListPref(kTabDiscardingExceptions);
+}
+
} // namespace performance_manager::user_tuning::prefs \ No newline at end of file
diff --git a/chromium/components/performance_manager/v8_memory/v8_context_tracker_browsertest.cc b/chromium/components/performance_manager/v8_memory/v8_context_tracker_browsertest.cc
index 6e62622eef6..4f279026ea9 100644
--- a/chromium/components/performance_manager/v8_memory/v8_context_tracker_browsertest.cc
+++ b/chromium/components/performance_manager/v8_memory/v8_context_tracker_browsertest.cc
@@ -65,7 +65,7 @@ IN_PROC_BROWSER_TEST_F(V8ContextTrackerTest, SameOriginIframeAttributionData) {
NavigateAndWaitForConsoleMessage(contents, urla, "a.html loaded"));
// Get pointers to the RFHs for each frame.
- content::RenderFrameHost* main_rfh = contents->GetMainFrame();
+ content::RenderFrameHost* main_rfh = contents->GetPrimaryMainFrame();
content::RenderFrameHost* child_rfh = ChildFrameAt(main_rfh, 0);
ASSERT_TRUE(child_rfh);
@@ -90,7 +90,7 @@ IN_PROC_BROWSER_TEST_F(V8ContextTrackerTest, CrossOriginIframeAttributionData) {
NavigateAndWaitForConsoleMessage(contents, urla, "b.html loaded"));
// Get pointers to the RFHs for each frame.
- content::RenderFrameHost* main_rfh = contents->GetMainFrame();
+ content::RenderFrameHost* main_rfh = contents->GetPrimaryMainFrame();
content::RenderFrameHost* child_rfh = ChildFrameAt(main_rfh, 0);
ASSERT_TRUE(child_rfh);
auto frame_node =
@@ -119,7 +119,7 @@ IN_PROC_BROWSER_TEST_F(V8ContextTrackerTest, SameDocNavigation) {
ExpectCounts(2, 2, 0, 0);
// Get pointers to the RFHs for each frame.
- content::RenderFrameHost* rfha = contents->GetMainFrame();
+ content::RenderFrameHost* rfha = contents->GetPrimaryMainFrame();
content::RenderFrameHost* rfhb = ChildFrameAt(rfha, 0);
// Execute a same document navigation in the child frame. This causes a
@@ -143,7 +143,7 @@ IN_PROC_BROWSER_TEST_F(V8ContextTrackerTest, DetachedContext) {
ExpectCounts(2, 2, 0, 0);
// Get pointers to the RFHs for each frame.
- content::RenderFrameHost* rfha = contents->GetMainFrame();
+ content::RenderFrameHost* rfha = contents->GetPrimaryMainFrame();
// Keep a pointer to the window associated with the child iframe, but
// unload it.
diff --git a/chromium/components/performance_manager/worker_watcher.cc b/chromium/components/performance_manager/worker_watcher.cc
index a410fddb7a3..4ff683032ea 100644
--- a/chromium/components/performance_manager/worker_watcher.cc
+++ b/chromium/components/performance_manager/worker_watcher.cc
@@ -23,12 +23,6 @@ using WorkerNodeSet = base::flat_set<WorkerNodeImpl*>;
namespace {
-// Emits a boolean value that indicates if the client frame's node was found
-// when trying to connect the worker to a client frame.
-void RecordWorkerClientFound(bool found) {
- UMA_HISTOGRAM_BOOLEAN("PerformanceManager.WorkerClientFound", found);
-}
-
// Helper function to add |client_frame_node| as a client of |worker_node| on
// the PM sequence.
void ConnectClientFrameOnGraph(WorkerNodeImpl* worker_node,
@@ -544,7 +538,6 @@ void WorkerWatcher::AddFrameClientConnection(
// accessible. If it isn't, this means there is a missing
// CreatePageNodeForWebContents() somewhere.
if (!frame_node) {
- RecordWorkerClientFound(false);
#if DCHECK_IS_ON()
// A call to RemoveFrameClientConnection() is still expected to be received
// for this worker and frame pair.
@@ -553,8 +546,6 @@ void WorkerWatcher::AddFrameClientConnection(
return;
}
- RecordWorkerClientFound(true);
-
// Keep track of the workers that this frame is a client to.
bool is_first_child_worker = false;
bool is_first_child_worker_connection = false;
diff --git a/chromium/components/permissions/BUILD.gn b/chromium/components/permissions/BUILD.gn
index 1347f0fb4b9..ca123ef391d 100644
--- a/chromium/components/permissions/BUILD.gn
+++ b/chromium/components/permissions/BUILD.gn
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# TODO(698985): Make `permissions_common` source_set.
+# TODO(crbug.com/1327384): Make `permissions_common` source_set.
component("permissions_common") {
sources = [
"constants.cc",
@@ -57,6 +57,8 @@ source_set("permissions") {
"contexts/webxr_permission_context.h",
"contexts/window_placement_permission_context.cc",
"contexts/window_placement_permission_context.h",
+ "notifications_engagement_service.cc",
+ "notifications_engagement_service.h",
"object_permission_context_base.cc",
"object_permission_context_base.h",
"permission_actions_history.cc",
@@ -145,10 +147,17 @@ source_set("permissions") {
"android/bluetooth_scanning_prompt_android.cc",
"android/bluetooth_scanning_prompt_android.h",
"android/bluetooth_scanning_prompt_android_delegate.h",
- "android/permission_dialog_delegate.cc",
- "android/permission_dialog_delegate.h",
- "android/permission_prompt_android.cc",
- "android/permission_prompt_android.h",
+ "android/permission_prompt/permission_dialog.cc",
+ "android/permission_prompt/permission_dialog.h",
+ "android/permission_prompt/permission_dialog_delegate.cc",
+ "android/permission_prompt/permission_dialog_delegate.h",
+ "android/permission_prompt/permission_infobar.cc",
+ "android/permission_prompt/permission_infobar.h",
+ "android/permission_prompt/permission_message.cc",
+ "android/permission_prompt/permission_message.h",
+ "android/permission_prompt/permission_prompt_android.cc",
+ "android/permission_prompt/permission_prompt_android.h",
+ "android/permission_prompt/permission_prompt_android_factory.cc",
"android/permissions_android_feature_list.cc",
"android/permissions_android_feature_list.h",
"contexts/geolocation_permission_context_android.cc",
@@ -260,7 +269,8 @@ source_set("unit_tests") {
]
}
if (is_android) {
- sources += [ "android/permission_dialog_delegate_unittest.cc" ]
+ sources +=
+ [ "android/permission_prompt/permission_dialog_delegate_unittest.cc" ]
}
deps = [
":permissions",
diff --git a/chromium/components/permissions/DEPS b/chromium/components/permissions/DEPS
index c92ff8b5be9..910209afffd 100644
--- a/chromium/components/permissions/DEPS
+++ b/chromium/components/permissions/DEPS
@@ -23,8 +23,10 @@ include_rules = [
"+sql",
"+services/network/public/cpp/is_potentially_trustworthy.h",
"+third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h",
+ "+third_party/blink/public/common/permissions/permission_utils.h",
"+third_party/blink/public/common/web_preferences",
"+third_party/blink/public/mojom/bluetooth",
+ "+third_party/blink/public/mojom/permissions/permission_status.mojom.h",
"+third_party/blink/public/mojom/permissions_policy",
"+third_party/blink/public/mojom/quota",
"+third_party/sqlite",
diff --git a/chromium/components/permissions/README.md b/chromium/components/permissions/README.md
index bc567894372..258750f28fe 100644
--- a/chromium/components/permissions/README.md
+++ b/chromium/components/permissions/README.md
@@ -2,22 +2,59 @@
[TOC]
+## PermissionController
+
+The
+[PermissionController](https://cs.chromium.org/chromium/src/content/public/browser/permission_controller.h)
+is the entry point for clients of the permissions infrastructure from both the
+`//content` and the embedder (e.g. `//chrome`) layers.
+[PermissionController](https://cs.chromium.org/chromium/src/content/public/browser/permission_controller.h)
+provides access to the permissions API and can be reached as
+`content::BrowserContext::GetPermissionController()` or
+`Profile::GetPermissionController()`.
+
+[PermissionController](https://cs.chromium.org/chromium/src/content/public/browser/permission_controller.h)
+has the following API:
+* `blink::mojom::PermissionStatus PermissionController::GetPermissionStatusForWorker`
+* `blink::mojom::PermissionStatus PermissionController::GetPermissionStatusForCurrentDocument`
+* `blink::mojom::PermissionStatus PermissionController::GetPermissionStatusForOriginWithoutContext`
+ Use this API only in special cases when there is no active document or
+ worker. E.g., `PermissionType::PAYMENT_HANDLER` permission verification of
+ payment providers in a PWA's manifest.
+* `PermissionController::RequestPermissionFromCurrentDocument`
+* `PermissionController::RequestPermissionsFromCurrentDocument`
+
+## PermissionControllerImpl
+
+The [PermissionControllerImpl](https://cs.chromium.org/chromium/src/content/browser/permissions/permission_controller_impl.h)
+is the implementation of the
+[PermissionController](https://cs.chromium.org/chromium/src/content/public/browser/permission_controller.h).
+[PermissionControllerImpl](https://cs.chromium.org/chromium/src/content/browser/permissions/permission_controller_impl.h)
+is meant to be used only internally in `//content` and is not available for
+external clients.
+
+[PermissionControllerImpl](https://cs.chromium.org/chromium/src/content/browser/permissions/permission_controller_impl.h)
+provides various functionality such as:
+
+* Reset a permission's state to the default
+* Observe permissions changes
+* Override permission status for DevTools.
+
## PermissionManager and PermissionContextBase
The
[PermissionManager](https://cs.chromium.org/chromium/src/components/permissions/permission_manager.h)
-is the entry point for clients of the permissions infrastructure.
+is an implementation of
+[PermissionControllerDelegate](https://cs.chromium.org/chromium/src/content/public/browser/permission_controller_delegate.h).
[PermissionManager](https://cs.chromium.org/chromium/src/components/permissions/permission_manager.h)
is a
[KeyedService](https://cs.chromium.org/chromium/src/components/keyed_service/core/keyed_service.h)
which means it is attached to a
[BrowserContext](https://cs.chromium.org/chromium/src/content/public/browser/browser_context.h).
-Clients can perform various operations such as:
-
-* Query the status of a permission
-* Request a permission from the user
-* Reset a permission's state to the default
-* Observe permissions changes
+It allows to get permission status for
+[ContentSettingsType](https://cs.chromium.org/chromium/src/components/content_settings/core/common/content_settings_types.h).
+That API should be used only to display permission status in UI like PageInfo
+and SiteSettings.
Internally,
[PermissionManager](https://cs.chromium.org/chromium/src/components/permissions/permission_manager.h)
diff --git a/chromium/components/permissions/android/BUILD.gn b/chromium/components/permissions/android/BUILD.gn
index f82ec33a705..a0711ec39bf 100644
--- a/chromium/components/permissions/android/BUILD.gn
+++ b/chromium/components/permissions/android/BUILD.gn
@@ -119,6 +119,8 @@ android_library("java") {
":core_java",
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/modaldialog/android:java",
"//components/content_settings/android:content_settings_enums_java",
"//components/location/android:location_java",
@@ -142,8 +144,9 @@ android_library("core_java") {
]
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/content_settings/android:content_settings_enums_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
"//ui/android:ui_no_recycler_view_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/chromium/components/permissions/android/permission_prompt/permission_dialog.cc b/chromium/components/permissions/android/permission_prompt/permission_dialog.cc
new file mode 100644
index 00000000000..9d46dbe3c7f
--- /dev/null
+++ b/chromium/components/permissions/android/permission_prompt/permission_dialog.cc
@@ -0,0 +1,25 @@
+// 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.
+
+#include "components/permissions/android/permission_prompt/permission_dialog.h"
+
+#include "components/permissions/android/permission_prompt/permission_dialog_delegate.h"
+
+namespace permissions {
+
+PermissionDialog::PermissionDialog(content::WebContents* web_contents,
+ Delegate* delegate)
+ : PermissionPromptAndroid(web_contents, delegate) {
+ DCHECK(web_contents);
+
+ PermissionDialogDelegate::Create(web_contents, this);
+}
+
+PermissionDialog::~PermissionDialog() = default;
+
+PermissionPromptDisposition PermissionDialog::GetPromptDisposition() const {
+ return PermissionPromptDisposition::MODAL_DIALOG;
+}
+
+} // namespace permissions
diff --git a/chromium/components/permissions/android/permission_prompt/permission_dialog.h b/chromium/components/permissions/android/permission_prompt/permission_dialog.h
new file mode 100644
index 00000000000..fc2aa7a7e56
--- /dev/null
+++ b/chromium/components/permissions/android/permission_prompt/permission_dialog.h
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_DIALOG_H_
+#define COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_DIALOG_H_
+
+#include "components/permissions/android/permission_prompt/permission_prompt_android.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace permissions {
+
+class PermissionDialog : public PermissionPromptAndroid {
+ public:
+ PermissionDialog(content::WebContents* web_contents, Delegate* delegate);
+
+ PermissionDialog(const PermissionDialog&) = delete;
+ PermissionDialog& operator=(const PermissionDialog&) = delete;
+
+ ~PermissionDialog() override;
+
+ PermissionPromptDisposition GetPromptDisposition() const override;
+};
+
+} // namespace permissions
+
+#endif // COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_DIALOG_H_
diff --git a/chromium/components/permissions/android/permission_dialog_delegate.cc b/chromium/components/permissions/android/permission_prompt/permission_dialog_delegate.cc
index c76378df51a..a39e62bf9e4 100644
--- a/chromium/components/permissions/android/permission_dialog_delegate.cc
+++ b/chromium/components/permissions/android/permission_prompt/permission_dialog_delegate.cc
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/permissions/android/permission_dialog_delegate.h"
+#include "components/permissions/android/permission_prompt/permission_dialog_delegate.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "components/permissions/android/jni_headers/PermissionDialogController_jni.h"
#include "components/permissions/android/jni_headers/PermissionDialogDelegate_jni.h"
-#include "components/permissions/android/permission_prompt_android.h"
+#include "components/permissions/android/permission_prompt/permission_prompt_android.h"
#include "components/permissions/permissions_client.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
diff --git a/chromium/components/permissions/android/permission_dialog_delegate.h b/chromium/components/permissions/android/permission_prompt/permission_dialog_delegate.h
index 69e8a1a78e3..f29dc0cbf8c 100644
--- a/chromium/components/permissions/android/permission_dialog_delegate.h
+++ b/chromium/components/permissions/android/permission_prompt/permission_dialog_delegate.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_DIALOG_DELEGATE_H_
-#define COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_DIALOG_DELEGATE_H_
+#ifndef COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_DIALOG_DELEGATE_H_
+#define COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_DIALOG_DELEGATE_H_
#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr.h"
@@ -14,7 +14,7 @@ using base::android::JavaParamRef;
namespace content {
class WebContents;
class Page;
-}
+} // namespace content
namespace permissions {
@@ -94,4 +94,4 @@ class PermissionDialogDelegate : public content::WebContentsObserver {
} // namespace permissions
-#endif // COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_DIALOG_DELEGATE_H_
+#endif // COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_DIALOG_DELEGATE_H_
diff --git a/chromium/components/permissions/android/permission_dialog_delegate_unittest.cc b/chromium/components/permissions/android/permission_prompt/permission_dialog_delegate_unittest.cc
index fcc3d098f93..d8452f535fd 100644
--- a/chromium/components/permissions/android/permission_dialog_delegate_unittest.cc
+++ b/chromium/components/permissions/android/permission_prompt/permission_dialog_delegate_unittest.cc
@@ -4,7 +4,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "components/permissions/android/permission_dialog_delegate.h"
+#include "components/permissions/android/permission_prompt/permission_dialog_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
diff --git a/chromium/components/permissions/android/permission_prompt/permission_infobar.cc b/chromium/components/permissions/android/permission_prompt/permission_infobar.cc
new file mode 100644
index 00000000000..fe22303dc29
--- /dev/null
+++ b/chromium/components/permissions/android/permission_prompt/permission_infobar.cc
@@ -0,0 +1,71 @@
+// Copyright 2022 The Chromium 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/permissions/android/permission_prompt/permission_infobar.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/infobars/core/infobar.h"
+#include "components/infobars/core/infobar_manager.h"
+#include "components/permissions/permissions_client.h"
+#include "content/public/browser/web_contents.h"
+
+namespace permissions {
+
+PermissionInfobar::PermissionInfobar(content::WebContents* web_contents,
+ Delegate* delegate)
+ : PermissionPromptAndroid(web_contents, delegate) {
+ auto* permission_client = PermissionsClient::Get();
+ permission_infobar_ = permission_client->MaybeCreateInfoBar(
+ web_contents, GetContentSettingType(0u /* position */),
+ weak_factory_.GetWeakPtr());
+ if (permission_infobar_)
+ permission_client->GetInfoBarManager(web_contents)->AddObserver(this);
+}
+
+PermissionInfobar::~PermissionInfobar() {
+ infobars::InfoBarManager* infobar_manager =
+ PermissionsClient::Get()->GetInfoBarManager(web_contents());
+ if (!infobar_manager)
+ return;
+ // RemoveObserver before RemoveInfoBar to not get notified about the removal
+ // of the `permission_infobar_` infobar.
+ infobar_manager->RemoveObserver(this);
+ if (permission_infobar_) {
+ infobar_manager->RemoveInfoBar(permission_infobar_);
+ }
+}
+
+// static
+std::unique_ptr<PermissionInfobar> PermissionInfobar::Create(
+ content::WebContents* web_contents,
+ Delegate* delegate) {
+ auto prompt = base::WrapUnique(new PermissionInfobar(web_contents, delegate));
+ if (prompt->permission_infobar_)
+ return prompt;
+ return nullptr;
+}
+
+void PermissionInfobar::OnInfoBarRemoved(infobars::InfoBar* infobar,
+ bool animate) {
+ if (infobar != permission_infobar_)
+ return;
+
+ permission_infobar_ = nullptr;
+ infobars::InfoBarManager* infobar_manager =
+ PermissionsClient::Get()->GetInfoBarManager(web_contents());
+ if (infobar_manager)
+ infobar_manager->RemoveObserver(this);
+}
+
+void PermissionInfobar::OnManagerShuttingDown(
+ infobars::InfoBarManager* manager) {
+ permission_infobar_ = nullptr;
+ manager->RemoveObserver(this);
+}
+
+PermissionPromptDisposition PermissionInfobar::GetPromptDisposition() const {
+ return PermissionPromptDisposition::MINI_INFOBAR;
+}
+
+} // namespace permissions
diff --git a/chromium/components/permissions/android/permission_prompt/permission_infobar.h b/chromium/components/permissions/android/permission_prompt/permission_infobar.h
new file mode 100644
index 00000000000..708cf826bee
--- /dev/null
+++ b/chromium/components/permissions/android/permission_prompt/permission_infobar.h
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_INFOBAR_H_
+#define COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_INFOBAR_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "components/infobars/core/infobar_manager.h"
+#include "components/permissions/android/permission_prompt/permission_prompt_android.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace permissions {
+
+class PermissionInfobar : public PermissionPromptAndroid,
+ public infobars::InfoBarManager::Observer {
+ public:
+ PermissionInfobar(const PermissionInfobar&) = delete;
+ PermissionInfobar& operator=(const PermissionInfobar&) = delete;
+ ~PermissionInfobar() override;
+
+ PermissionPromptDisposition GetPromptDisposition() const override;
+
+ static std::unique_ptr<PermissionInfobar> Create(
+ content::WebContents* web_contents,
+ Delegate* delegate);
+
+ // InfoBar::Manager:
+ void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override;
+ void OnManagerShuttingDown(infobars::InfoBarManager* manager) override;
+
+ private:
+ PermissionInfobar(content::WebContents* web_contents, Delegate* delegate);
+
+ // The infobar used to display the permission request. Never assume that this
+ // pointer is currently alive.
+ raw_ptr<infobars::InfoBar> permission_infobar_;
+
+ base::WeakPtrFactory<PermissionInfobar> weak_factory_{this};
+};
+
+} // namespace permissions
+
+#endif // COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_INFOBAR_H_
diff --git a/chromium/components/permissions/android/permission_prompt/permission_message.cc b/chromium/components/permissions/android/permission_prompt/permission_message.cc
new file mode 100644
index 00000000000..a0cd85468a2
--- /dev/null
+++ b/chromium/components/permissions/android/permission_prompt/permission_message.cc
@@ -0,0 +1,36 @@
+// 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.
+
+#include "components/permissions/android/permission_prompt/permission_message.h"
+
+#include "base/memory/ptr_util.h"
+
+namespace permissions {
+
+PermissionMessage::PermissionMessage(content::WebContents* web_contents,
+ Delegate* delegate)
+ : PermissionPromptAndroid(web_contents, delegate) {
+ auto* permission_client = PermissionsClient::Get();
+ message_delegate_ = permission_client->MaybeCreateMessageUI(
+ web_contents, GetContentSettingType(0u /* position */),
+ weak_factory_.GetWeakPtr());
+}
+
+PermissionMessage::~PermissionMessage() = default;
+
+// static
+std::unique_ptr<PermissionMessage> PermissionMessage::Create(
+ content::WebContents* web_contents,
+ Delegate* delegate) {
+ auto prompt = base::WrapUnique(new PermissionMessage(web_contents, delegate));
+ if (prompt->message_delegate_)
+ return prompt;
+ return nullptr;
+}
+
+PermissionPromptDisposition PermissionMessage::GetPromptDisposition() const {
+ return PermissionPromptDisposition::MESSAGE_UI;
+}
+
+} // namespace permissions
diff --git a/chromium/components/permissions/android/permission_prompt/permission_message.h b/chromium/components/permissions/android/permission_prompt/permission_message.h
new file mode 100644
index 00000000000..8a4637d4082
--- /dev/null
+++ b/chromium/components/permissions/android/permission_prompt/permission_message.h
@@ -0,0 +1,43 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_MESSAGE_H_
+#define COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_MESSAGE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "components/permissions/android/permission_prompt/permission_prompt_android.h"
+#include "components/permissions/permissions_client.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace permissions {
+
+class PermissionMessage : public PermissionPromptAndroid {
+ public:
+ PermissionMessage(const PermissionMessage&) = delete;
+ PermissionMessage& operator=(const PermissionMessage&) = delete;
+ ~PermissionMessage() override;
+
+ static std::unique_ptr<PermissionMessage> Create(
+ content::WebContents* web_contents,
+ Delegate* delegate);
+
+ PermissionPromptDisposition GetPromptDisposition() const override;
+
+ private:
+ PermissionMessage(content::WebContents* web_contents, Delegate* delegate);
+
+ std::unique_ptr<PermissionsClient::PermissionMessageDelegate>
+ message_delegate_;
+
+ base::WeakPtrFactory<PermissionMessage> weak_factory_{this};
+};
+
+} // namespace permissions
+
+#endif // COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_MESSAGE_H_
diff --git a/chromium/components/permissions/android/permission_prompt/permission_prompt_android.cc b/chromium/components/permissions/android/permission_prompt/permission_prompt_android.cc
new file mode 100644
index 00000000000..230400f83f6
--- /dev/null
+++ b/chromium/components/permissions/android/permission_prompt/permission_prompt_android.cc
@@ -0,0 +1,124 @@
+// 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.
+
+#include "components/permissions/android/permission_prompt/permission_prompt_android.h"
+
+#include <vector>
+
+#include "components/permissions/permission_request.h"
+#include "components/resources/android/theme_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/url_formatter/elide_url.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace permissions {
+
+PermissionPromptAndroid::PermissionPromptAndroid(
+ content::WebContents* web_contents,
+ Delegate* delegate)
+ : web_contents_(web_contents), delegate_(delegate) {
+ DCHECK(web_contents);
+}
+
+PermissionPromptAndroid::~PermissionPromptAndroid() = default;
+
+void PermissionPromptAndroid::UpdateAnchor() {
+ NOTIMPLEMENTED();
+}
+
+PermissionPrompt::TabSwitchingBehavior
+PermissionPromptAndroid::GetTabSwitchingBehavior() {
+ return TabSwitchingBehavior::kKeepPromptAlive;
+}
+
+void PermissionPromptAndroid::Closing() {
+ delegate_->Dismiss();
+}
+
+void PermissionPromptAndroid::Accept() {
+ delegate_->Accept();
+}
+
+void PermissionPromptAndroid::Deny() {
+ delegate_->Deny();
+}
+
+void PermissionPromptAndroid::SetManageClicked() {
+ delegate_->SetManageClicked();
+}
+
+void PermissionPromptAndroid::SetLearnMoreClicked() {
+ delegate_->SetLearnMoreClicked();
+}
+
+bool PermissionPromptAndroid::ShouldCurrentRequestUseQuietUI() {
+ return delegate_->ShouldCurrentRequestUseQuietUI();
+}
+
+absl::optional<PermissionUiSelector::QuietUiReason>
+PermissionPromptAndroid::ReasonForUsingQuietUi() const {
+ return delegate_->ReasonForUsingQuietUi();
+}
+
+size_t PermissionPromptAndroid::PermissionCount() const {
+ return delegate_->Requests().size();
+}
+
+ContentSettingsType PermissionPromptAndroid::GetContentSettingType(
+ size_t position) const {
+ const std::vector<PermissionRequest*>& requests = delegate_->Requests();
+ CHECK_LT(position, requests.size());
+ return requests[position]->GetContentSettingsType();
+}
+
+static bool IsValidMediaRequestGroup(
+ const std::vector<PermissionRequest*>& requests) {
+ if (requests.size() < 2)
+ return false;
+ return ((requests[0]->request_type() == RequestType::kMicStream &&
+ requests[1]->request_type() == RequestType::kCameraStream) ||
+ (requests[0]->request_type() == RequestType::kCameraStream &&
+ requests[1]->request_type() == RequestType::kMicStream));
+}
+
+// Grouped permission requests can only be Mic+Camera, Camera+Mic.
+static void CheckValidRequestGroup(
+ const std::vector<PermissionRequest*>& requests) {
+ DCHECK_EQ(static_cast<size_t>(2u), requests.size());
+ DCHECK((IsValidMediaRequestGroup(requests)));
+}
+
+int PermissionPromptAndroid::GetIconId() const {
+ const std::vector<PermissionRequest*>& requests = delegate_->Requests();
+ if (requests.size() == 1)
+ return permissions::GetIconId(requests[0]->request_type());
+ CheckValidRequestGroup(requests);
+ return IDR_ANDROID_INFOBAR_MEDIA_STREAM_CAMERA;
+}
+
+std::u16string PermissionPromptAndroid::GetMessageText() const {
+ const std::vector<PermissionRequest*>& requests = delegate_->Requests();
+ if (requests.size() == 1) {
+ if (requests[0]->request_type() == RequestType::kStorageAccess) {
+ return l10n_util::GetStringFUTF16(
+ IDS_STORAGE_ACCESS_INFOBAR_TEXT,
+ url_formatter::FormatUrlForSecurityDisplay(
+ delegate_->GetRequestingOrigin(),
+ url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC),
+ url_formatter::FormatUrlForSecurityDisplay(
+ delegate_->GetEmbeddingOrigin(),
+ url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
+ } else {
+ return requests[0]->GetDialogMessageText();
+ }
+ }
+ CheckValidRequestGroup(requests);
+ return l10n_util::GetStringFUTF16(
+ IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_INFOBAR_TEXT,
+ url_formatter::FormatUrlForSecurityDisplay(
+ delegate_->GetRequestingOrigin(),
+ url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
+}
+
+} // namespace permissions
diff --git a/chromium/components/permissions/android/permission_prompt_android.h b/chromium/components/permissions/android/permission_prompt/permission_prompt_android.h
index 375817ab4c8..9da918050ca 100644
--- a/chromium/components/permissions/android/permission_prompt_android.h
+++ b/chromium/components/permissions/android/permission_prompt/permission_prompt_android.h
@@ -2,16 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_ANDROID_H_
-#define COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_ANDROID_H_
+#ifndef COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_PROMPT_ANDROID_H_
+#define COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_PROMPT_ANDROID_H_
#include <memory>
#include <string>
#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
#include "components/content_settings/core/common/content_settings_types.h"
-#include "components/infobars/core/infobar_manager.h"
#include "components/permissions/permission_prompt.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permissions_client.h"
@@ -19,14 +17,11 @@
namespace content {
class WebContents;
}
-namespace infobars {
-class InfoBar;
-}
namespace permissions {
-class PermissionPromptAndroid : public permissions::PermissionPrompt,
- public infobars::InfoBarManager::Observer {
+// Virtual class that is the base class for all Android permission prompts.
+class PermissionPromptAndroid : public PermissionPrompt {
public:
PermissionPromptAndroid(content::WebContents* web_contents,
Delegate* delegate);
@@ -42,11 +37,9 @@ class PermissionPromptAndroid : public permissions::PermissionPrompt,
// 3. A higher priority request comes in.
~PermissionPromptAndroid() override;
- // permissions::PermissionPrompt:
+ // PermissionPrompt:
void UpdateAnchor() override;
TabSwitchingBehavior GetTabSwitchingBehavior() override;
- permissions::PermissionPromptDisposition GetPromptDisposition()
- const override;
void Closing();
void Accept();
@@ -65,36 +58,18 @@ class PermissionPromptAndroid : public permissions::PermissionPrompt,
std::u16string GetTitleText() const;
std::u16string GetMessageText() const;
- const content::WebContents* web_contents() { return web_contents_; }
-
- // InfoBar::Manager:
- void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override;
- void OnManagerShuttingDown(infobars::InfoBarManager* manager) override;
+ content::WebContents* web_contents() { return web_contents_; }
private:
// PermissionPromptAndroid is owned by PermissionRequestManager, so it should
// be safe to hold a raw WebContents pointer here because this class is
// destroyed before the WebContents.
const raw_ptr<content::WebContents> web_contents_;
+
// |delegate_| is the PermissionRequestManager, which owns this object.
const raw_ptr<Delegate> delegate_;
-
- // The infobar used to display the permission request, if displayed in that
- // format. Never assume that this pointer is currently alive.
- raw_ptr<infobars::InfoBar> permission_infobar_;
-
- // Message UI is alternative to infobars. So it should be impossible that
- // both |message_delegate_| and |permission_infobar_| are non-null at the
- // same moment.
- std::unique_ptr<PermissionsClient::PermissionMessageDelegate>
- message_delegate_;
-
- permissions::PermissionPromptDisposition prompt_disposition_ =
- permissions::PermissionPromptDisposition::NOT_APPLICABLE;
-
- base::WeakPtrFactory<PermissionPromptAndroid> weak_factory_{this};
};
} // namespace permissions
-#endif // COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_ANDROID_H_
+#endif // COMPONENTS_PERMISSIONS_ANDROID_PERMISSION_PROMPT_PERMISSION_PROMPT_ANDROID_H_
diff --git a/chromium/components/permissions/android/permission_prompt/permission_prompt_android_factory.cc b/chromium/components/permissions/android/permission_prompt/permission_prompt_android_factory.cc
new file mode 100644
index 00000000000..6ada7f41ebe
--- /dev/null
+++ b/chromium/components/permissions/android/permission_prompt/permission_prompt_android_factory.cc
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium 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 <memory>
+
+#include "components/permissions/android/permission_prompt/permission_dialog.h"
+#include "components/permissions/android/permission_prompt/permission_infobar.h"
+#include "components/permissions/android/permission_prompt/permission_message.h"
+#include "components/permissions/permission_prompt.h"
+#include "content/public/browser/web_contents.h"
+
+namespace permissions {
+
+std::unique_ptr<PermissionPrompt> PermissionPrompt::Create(
+ content::WebContents* web_contents,
+ Delegate* delegate) {
+ // Quiet UI (non-modal, less intrusive) is preferred over loud one, if
+ // necessary conditions are met. The message UI is preferred over the infobar
+ // UI.
+ auto message_ui = PermissionMessage::Create(web_contents, delegate);
+ if (message_ui)
+ return message_ui;
+
+ auto infobar = PermissionInfobar::Create(web_contents, delegate);
+ if (infobar)
+ return infobar;
+
+ return std::make_unique<PermissionDialog>(web_contents, delegate);
+}
+
+} // namespace permissions
diff --git a/chromium/components/permissions/android/permission_prompt_android.cc b/chromium/components/permissions/android/permission_prompt_android.cc
deleted file mode 100644
index 0486024f375..00000000000
--- a/chromium/components/permissions/android/permission_prompt_android.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-// 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.
-
-#include "components/permissions/android/permission_prompt_android.h"
-
-#include <vector>
-
-#include "components/infobars/core/infobar.h"
-#include "components/infobars/core/infobar_manager.h"
-#include "components/messages/android/message_dispatcher_bridge.h"
-#include "components/permissions/android/permission_dialog_delegate.h"
-#include "components/permissions/permission_request.h"
-#include "components/resources/android/theme_resources.h"
-#include "components/strings/grit/components_strings.h"
-#include "components/url_formatter/elide_url.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace permissions {
-
-PermissionPromptAndroid::PermissionPromptAndroid(
- content::WebContents* web_contents,
- Delegate* delegate)
- : web_contents_(web_contents),
- delegate_(delegate),
- permission_infobar_(nullptr),
- weak_factory_(this) {
- DCHECK(web_contents);
-
- auto* permission_client = PermissionsClient::Get();
- if ((message_delegate_ = permission_client->MaybeCreateMessageUI(
- web_contents, GetContentSettingType(0u /* position */),
- weak_factory_.GetWeakPtr()))) {
- prompt_disposition_ = permissions::PermissionPromptDisposition::MESSAGE_UI;
- } else if ((permission_infobar_ = permission_client->MaybeCreateInfoBar(
- web_contents, GetContentSettingType(0u /* position */),
- weak_factory_.GetWeakPtr()))) {
- prompt_disposition_ =
- permissions::PermissionPromptDisposition::MINI_INFOBAR;
- permission_client->GetInfoBarManager(web_contents_)->AddObserver(this);
- } else {
- prompt_disposition_ =
- permissions::PermissionPromptDisposition::MODAL_DIALOG;
- PermissionDialogDelegate::Create(web_contents_, this);
- }
-}
-
-PermissionPromptAndroid::~PermissionPromptAndroid() {
- if (message_delegate_) {
- return;
- }
- infobars::InfoBarManager* infobar_manager =
- PermissionsClient::Get()->GetInfoBarManager(web_contents_);
- if (!infobar_manager)
- return;
- // RemoveObserver before RemoveInfoBar to not get notified about the removal
- // of the `permission_infobar_` infobar.
- infobar_manager->RemoveObserver(this);
- if (permission_infobar_) {
- infobar_manager->RemoveInfoBar(permission_infobar_);
- }
-}
-
-void PermissionPromptAndroid::UpdateAnchor() {
- NOTIMPLEMENTED();
-}
-
-permissions::PermissionPrompt::TabSwitchingBehavior
-PermissionPromptAndroid::GetTabSwitchingBehavior() {
- return TabSwitchingBehavior::kKeepPromptAlive;
-}
-
-permissions::PermissionPromptDisposition
-PermissionPromptAndroid::GetPromptDisposition() const {
- return prompt_disposition_;
-}
-
-void PermissionPromptAndroid::Closing() {
- delegate_->Dismiss();
-}
-
-void PermissionPromptAndroid::Accept() {
- delegate_->Accept();
-}
-
-void PermissionPromptAndroid::Deny() {
- delegate_->Deny();
-}
-
-void PermissionPromptAndroid::SetManageClicked() {
- delegate_->SetManageClicked();
-}
-
-void PermissionPromptAndroid::SetLearnMoreClicked() {
- delegate_->SetLearnMoreClicked();
-}
-
-bool PermissionPromptAndroid::ShouldCurrentRequestUseQuietUI() {
- return delegate_->ShouldCurrentRequestUseQuietUI();
-}
-
-absl::optional<PermissionUiSelector::QuietUiReason>
-PermissionPromptAndroid::ReasonForUsingQuietUi() const {
- return delegate_->ReasonForUsingQuietUi();
-}
-
-size_t PermissionPromptAndroid::PermissionCount() const {
- return delegate_->Requests().size();
-}
-
-ContentSettingsType PermissionPromptAndroid::GetContentSettingType(
- size_t position) const {
- const std::vector<permissions::PermissionRequest*>& requests =
- delegate_->Requests();
- CHECK_LT(position, requests.size());
- return requests[position]->GetContentSettingsType();
-}
-
-static bool IsValidMediaRequestGroup(
- const std::vector<permissions::PermissionRequest*>& requests) {
- if (requests.size() < 2)
- return false;
- return (
- (requests[0]->request_type() == permissions::RequestType::kMicStream &&
- requests[1]->request_type() ==
- permissions::RequestType::kCameraStream) ||
- (requests[0]->request_type() == permissions::RequestType::kCameraStream &&
- requests[1]->request_type() == permissions::RequestType::kMicStream));
-}
-
-static bool IsValidARCameraAccessRequestGroup(
- const std::vector<permissions::PermissionRequest*>& requests) {
- if (requests.size() < 2)
- return false;
- return (
- (requests[0]->request_type() == permissions::RequestType::kArSession &&
- requests[1]->request_type() ==
- permissions::RequestType::kCameraStream) ||
- (requests[0]->request_type() == permissions::RequestType::kCameraStream &&
- requests[1]->request_type() == permissions::RequestType::kArSession));
-}
-
-// Grouped permission requests can only be Mic+Camera, Camera+Mic,
-// AR + Camera, or Camera + AR.
-static void CheckValidRequestGroup(
- const std::vector<permissions::PermissionRequest*>& requests) {
- DCHECK_EQ(static_cast<size_t>(2u), requests.size());
- DCHECK((IsValidMediaRequestGroup(requests)) ||
- (IsValidARCameraAccessRequestGroup(requests)));
-}
-
-int PermissionPromptAndroid::GetIconId() const {
- const std::vector<permissions::PermissionRequest*>& requests =
- delegate_->Requests();
- if (requests.size() == 1)
- return permissions::GetIconId(requests[0]->request_type());
- CheckValidRequestGroup(requests);
- return IDR_ANDROID_INFOBAR_MEDIA_STREAM_CAMERA;
-}
-
-std::u16string PermissionPromptAndroid::GetMessageText() const {
- const std::vector<permissions::PermissionRequest*>& requests =
- delegate_->Requests();
- if (requests.size() == 1) {
- if (requests[0]->request_type() ==
- permissions::RequestType::kStorageAccess) {
- return l10n_util::GetStringFUTF16(
- IDS_STORAGE_ACCESS_INFOBAR_TEXT,
- url_formatter::FormatUrlForSecurityDisplay(
- delegate_->GetRequestingOrigin(),
- url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC),
- url_formatter::FormatUrlForSecurityDisplay(
- delegate_->GetEmbeddingOrigin(),
- url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
- } else {
- return requests[0]->GetDialogMessageText();
- }
- }
- CheckValidRequestGroup(requests);
- if (IsValidARCameraAccessRequestGroup(requests)) {
- return l10n_util::GetStringFUTF16(
- IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT,
- url_formatter::FormatUrlForSecurityDisplay(
- delegate_->GetRequestingOrigin(),
- url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
- } else {
- return l10n_util::GetStringFUTF16(
- IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_INFOBAR_TEXT,
- url_formatter::FormatUrlForSecurityDisplay(
- delegate_->GetRequestingOrigin(),
- url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
- }
-}
-
-void PermissionPromptAndroid::OnInfoBarRemoved(infobars::InfoBar* infobar,
- bool animate) {
- if (infobar != permission_infobar_)
- return;
-
- permission_infobar_ = nullptr;
- infobars::InfoBarManager* infobar_manager =
- PermissionsClient::Get()->GetInfoBarManager(web_contents_);
- if (infobar_manager)
- infobar_manager->RemoveObserver(this);
-}
-
-void PermissionPromptAndroid::OnManagerShuttingDown(
- infobars::InfoBarManager* manager) {
- permission_infobar_ = nullptr;
- manager->RemoveObserver(this);
-}
-
-// static
-std::unique_ptr<permissions::PermissionPrompt>
-permissions::PermissionPrompt::Create(content::WebContents* web_contents,
- Delegate* delegate) {
- return std::make_unique<PermissionPromptAndroid>(web_contents, delegate);
-}
-
-} // namespace permissions
diff --git a/chromium/components/permissions/bluetooth_delegate_impl.cc b/chromium/components/permissions/bluetooth_delegate_impl.cc
index 29cfaaebd0d..76612dd5f80 100644
--- a/chromium/components/permissions/bluetooth_delegate_impl.cc
+++ b/chromium/components/permissions/bluetooth_delegate_impl.cc
@@ -46,6 +46,14 @@ void BluetoothDelegateImpl::ShowDeviceCredentialsPrompt(
std::move(callback));
}
+void BluetoothDelegateImpl::ShowDevicePairConfirmPrompt(
+ RenderFrameHost* frame,
+ const std::u16string& device_identifier,
+ PairConfirmCallback callback) {
+ client_->ShowBluetoothDevicePairConfirmDialog(frame, device_identifier,
+ std::move(callback));
+}
+
WebBluetoothDeviceId BluetoothDelegateImpl::GetWebBluetoothDeviceId(
RenderFrameHost* frame,
const std::string& device_address) {
diff --git a/chromium/components/permissions/bluetooth_delegate_impl.h b/chromium/components/permissions/bluetooth_delegate_impl.h
index 71a81c6ddc0..fda295c5759 100644
--- a/chromium/components/permissions/bluetooth_delegate_impl.h
+++ b/chromium/components/permissions/bluetooth_delegate_impl.h
@@ -74,6 +74,16 @@ class BluetoothDelegateImpl : public content::BluetoothDelegate {
content::RenderFrameHost* frame,
const std::u16string& device_identifier,
content::BluetoothDelegate::CredentialsCallback callback) = 0;
+
+ // Prompt the user to consent pairing device.
+ //
+ // The |device_identifier| is a localized string (device name, address,
+ // etc.) displayed to the user for identification purposes. When the
+ // prompt is complete |callback| is called with the result.
+ virtual void ShowBluetoothDevicePairConfirmDialog(
+ content::RenderFrameHost* frame,
+ const std::u16string& device_identifier,
+ content::BluetoothDelegate::PairConfirmCallback callback) = 0;
};
explicit BluetoothDelegateImpl(std::unique_ptr<Client> client);
@@ -93,6 +103,9 @@ class BluetoothDelegateImpl : public content::BluetoothDelegate {
void ShowDeviceCredentialsPrompt(content::RenderFrameHost* frame,
const std::u16string& device_identifier,
CredentialsCallback callback) override;
+ void ShowDevicePairConfirmPrompt(content::RenderFrameHost* frame,
+ const std::u16string& device_identifier,
+ PairConfirmCallback callback) override;
blink::WebBluetoothDeviceId GetWebBluetoothDeviceId(
content::RenderFrameHost* frame,
const std::string& device_address) override;
diff --git a/chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.cc b/chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.cc
index 15df3baef39..7046fc1f090 100644
--- a/chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.cc
+++ b/chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.cc
@@ -6,15 +6,34 @@
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_request_id.h"
+#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
#include "components/webrtc/media_stream_device_enumerator.h"
+#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/permission_controller.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
namespace permissions {
+// TODO(crbug.com/1271543): This method is a temporary solution because of
+// inconsistency between the new permissions API that is migrated to
+// `blink::mojom::PermissionStatus` and its callsites that still use
+// `ContentSetting`.
+void CallbackWrapper(base::OnceCallback<void(ContentSetting)> callback,
+ blink::mojom::PermissionStatus status) {
+ ContentSetting result = CONTENT_SETTING_ASK;
+ if (status == blink::mojom::PermissionStatus::GRANTED) {
+ result = CONTENT_SETTING_ALLOW;
+ } else if (status == blink::mojom::PermissionStatus::DENIED) {
+ result = CONTENT_SETTING_BLOCK;
+ }
+ std::move(callback).Run(result);
+}
+
CameraPanTiltZoomPermissionContext::CameraPanTiltZoomPermissionContext(
content::BrowserContext* browser_context,
std::unique_ptr<Delegate> delegate,
@@ -36,7 +55,6 @@ CameraPanTiltZoomPermissionContext::~CameraPanTiltZoomPermissionContext() {
}
void CameraPanTiltZoomPermissionContext::RequestPermission(
- content::WebContents* web_contents,
const permissions::PermissionRequestID& id,
const GURL& requesting_frame_origin,
bool user_gesture,
@@ -44,22 +62,27 @@ void CameraPanTiltZoomPermissionContext::RequestPermission(
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (HasAvailableCameraPtzDevices()) {
- PermissionContextBase::RequestPermission(web_contents, id,
- requesting_frame_origin,
+ PermissionContextBase::RequestPermission(id, requesting_frame_origin,
user_gesture, std::move(callback));
return;
}
// If there is no camera with PTZ capabilities, let's request a "regular"
// camera permission instead.
- content::RenderFrameHost* frame = content::RenderFrameHost::FromID(
- id.render_process_id(), id.render_frame_id());
- permissions::PermissionManager* permission_manager =
- permissions::PermissionsClient::Get()->GetPermissionManager(
- web_contents->GetBrowserContext());
- permission_manager->RequestPermission(ContentSettingsType::MEDIASTREAM_CAMERA,
- frame, requesting_frame_origin,
- user_gesture, std::move(callback));
+ content::RenderFrameHost* render_frame_host =
+ content::RenderFrameHost::FromID(id.render_process_id(),
+ id.render_frame_id());
+
+ if (requesting_frame_origin !=
+ render_frame_host->GetLastCommittedOrigin().GetURL()) {
+ std::move(callback).Run(CONTENT_SETTING_BLOCK);
+ return;
+ }
+ render_frame_host->GetBrowserContext()
+ ->GetPermissionController()
+ ->RequestPermissionFromCurrentDocument(
+ blink::PermissionType::VIDEO_CAPTURE, render_frame_host, user_gesture,
+ base::BindOnce(&CallbackWrapper, std::move(callback)));
}
ContentSetting CameraPanTiltZoomPermissionContext::GetPermissionStatusInternal(
diff --git a/chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.h b/chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.h
index 61d15acc185..6f251875abe 100644
--- a/chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.h
+++ b/chromium/components/permissions/contexts/camera_pan_tilt_zoom_permission_context.h
@@ -53,7 +53,6 @@ class CameraPanTiltZoomPermissionContext
private:
// PermissionContextBase
void RequestPermission(
- content::WebContents* web_contents,
const permissions::PermissionRequestID& id,
const GURL& requesting_frame_origin,
bool user_gesture,
diff --git a/chromium/components/permissions/contexts/geolocation_permission_context.cc b/chromium/components/permissions/contexts/geolocation_permission_context.cc
index 54b1737253d..d0c8fdd4d57 100644
--- a/chromium/components/permissions/contexts/geolocation_permission_context.cc
+++ b/chromium/components/permissions/contexts/geolocation_permission_context.cc
@@ -28,7 +28,6 @@ GeolocationPermissionContext::GeolocationPermissionContext(
GeolocationPermissionContext::~GeolocationPermissionContext() = default;
void GeolocationPermissionContext::DecidePermission(
- content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
@@ -36,10 +35,10 @@ void GeolocationPermissionContext::DecidePermission(
BrowserPermissionCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- if (!delegate_->DecidePermission(web_contents, id, requesting_origin,
- user_gesture, &callback, this)) {
+ if (!delegate_->DecidePermission(id, requesting_origin, user_gesture,
+ &callback, this)) {
DCHECK(callback);
- PermissionContextBase::DecidePermission(web_contents, id, requesting_origin,
+ PermissionContextBase::DecidePermission(id, requesting_origin,
embedding_origin, user_gesture,
std::move(callback));
}
diff --git a/chromium/components/permissions/contexts/geolocation_permission_context.h b/chromium/components/permissions/contexts/geolocation_permission_context.h
index 7463e472071..bbc7ba561be 100644
--- a/chromium/components/permissions/contexts/geolocation_permission_context.h
+++ b/chromium/components/permissions/contexts/geolocation_permission_context.h
@@ -33,8 +33,7 @@ class GeolocationPermissionContext : public PermissionContextBase {
// Allows the delegate to override the context's DecidePermission() logic.
// If this returns true, the base context's DecidePermission() will not be
// called.
- virtual bool DecidePermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ virtual bool DecidePermission(const PermissionRequestID& id,
const GURL& requesting_origin,
bool user_gesture,
BrowserPermissionCallback* callback,
@@ -62,8 +61,7 @@ class GeolocationPermissionContext : public PermissionContextBase {
~GeolocationPermissionContext() override;
- void DecidePermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ void DecidePermission(const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
diff --git a/chromium/components/permissions/contexts/geolocation_permission_context_android.cc b/chromium/components/permissions/contexts/geolocation_permission_context_android.cc
index 8b4fe96b096..4bf702d7569 100644
--- a/chromium/components/permissions/contexts/geolocation_permission_context_android.cc
+++ b/chromium/components/permissions/contexts/geolocation_permission_context_android.cc
@@ -94,7 +94,6 @@ void GeolocationPermissionContextAndroid::AddDayOffsetForTesting(int days) {
}
void GeolocationPermissionContextAndroid::RequestPermission(
- content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_frame_origin,
bool user_gesture,
@@ -102,6 +101,9 @@ void GeolocationPermissionContextAndroid::RequestPermission(
content::RenderFrameHost* const render_frame_host =
content::RenderFrameHost::FromID(id.render_process_id(),
id.render_frame_id());
+
+ content::WebContents* web_contents =
+ content::WebContents::FromRenderFrameHost(render_frame_host);
const GURL embedding_origin = PermissionUtil::GetLastCommittedOriginAsURL(
render_frame_host->GetMainFrame());
@@ -133,8 +135,7 @@ void GeolocationPermissionContextAndroid::RequestPermission(
}
GeolocationPermissionContext::RequestPermission(
- web_contents, id, requesting_frame_origin, user_gesture,
- std::move(callback));
+ id, requesting_frame_origin, user_gesture, std::move(callback));
}
void GeolocationPermissionContextAndroid::UserMadePermissionDecision(
diff --git a/chromium/components/permissions/contexts/geolocation_permission_context_android.h b/chromium/components/permissions/contexts/geolocation_permission_context_android.h
index ec317016c93..f469555d691 100644
--- a/chromium/components/permissions/contexts/geolocation_permission_context_android.h
+++ b/chromium/components/permissions/contexts/geolocation_permission_context_android.h
@@ -72,8 +72,7 @@ class GeolocationPermissionContextAndroid
private:
// GeolocationPermissionContext:
- void RequestPermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ void RequestPermission(const PermissionRequestID& id,
const GURL& requesting_frame_origin,
bool user_gesture,
BrowserPermissionCallback callback) override;
diff --git a/chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc b/chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc
index 6241238006e..309c4aea576 100644
--- a/chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc
+++ b/chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc
@@ -38,6 +38,7 @@
#include "components/permissions/permission_request_id.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/test/mock_permission_prompt_factory.h"
+#include "components/permissions/test/permission_test_util.h"
#include "components/permissions/test/test_permissions_client.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/browser/browser_context.h"
@@ -47,21 +48,24 @@
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
+#include "content/public/browser/permission_controller.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/permissions/permission_utils.h"
+#include "url/origin.h"
#if BUILDFLAG(IS_ANDROID)
#include "components/location/android/location_settings_dialog_outcome.h"
#include "components/location/android/mock_location_settings.h"
#include "components/permissions/contexts/geolocation_permission_context_android.h"
#include "components/prefs/pref_service.h"
-#include "content/public/browser/permission_type.h"
#endif
#if BUILDFLAG(IS_MAC)
@@ -87,8 +91,7 @@ class TestGeolocationPermissionContextDelegate
#endif
}
- bool DecidePermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ bool DecidePermission(const PermissionRequestID& id,
const GURL& requesting_origin,
bool user_gesture,
BrowserPermissionCallback* callback,
@@ -131,15 +134,19 @@ class GeolocationPermissionContextTests
// RenderViewHostTestHarness:
void SetUp() override;
void TearDown() override;
+ std::unique_ptr<content::BrowserContext> CreateBrowserContext() override;
PermissionRequestID RequestID(int request_id);
PermissionRequestID RequestIDForTab(int tab, int request_id);
- void RequestGeolocationPermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ void RequestGeolocationPermission(const PermissionRequestID& id,
const GURL& requesting_frame,
bool user_gesture);
+ blink::mojom::PermissionStatus GetPermissionStatus(
+ blink::PermissionType permission,
+ const GURL& requesting_origin);
+
void PermissionResponse(const PermissionRequestID& id,
ContentSetting content_setting);
void CheckPermissionMessageSent(int request_id, bool allowed);
@@ -177,7 +184,7 @@ class GeolocationPermissionContextTests
std::u16string GetPromptText();
TestPermissionsClient client_;
- // owned by |manager_|
+ // owned by |BrowserContest::GetPermissionControllerDelegate()|
raw_ptr<GeolocationPermissionContext> geolocation_permission_context_ =
nullptr;
// owned by |geolocation_permission_context_|
@@ -185,7 +192,6 @@ class GeolocationPermissionContextTests
std::vector<std::unique_ptr<content::WebContents>> extra_tabs_;
std::vector<std::unique_ptr<MockPermissionPromptFactory>>
mock_permission_prompt_factories_;
- std::unique_ptr<PermissionManager> manager_;
#if BUILDFLAG(IS_MAC)
std::unique_ptr<device::FakeGeolocationManager> fake_geolocation_manager_;
@@ -203,8 +209,8 @@ class GeolocationPermissionContextTests
PermissionRequestID GeolocationPermissionContextTests::RequestID(
int request_id) {
return PermissionRequestID(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId(request_id));
}
@@ -212,23 +218,32 @@ PermissionRequestID GeolocationPermissionContextTests::RequestIDForTab(
int tab,
int request_id) {
return PermissionRequestID(
- extra_tabs_[tab]->GetMainFrame()->GetProcess()->GetID(),
- extra_tabs_[tab]->GetMainFrame()->GetRoutingID(),
+ extra_tabs_[tab]->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ extra_tabs_[tab]->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId(request_id));
}
void GeolocationPermissionContextTests::RequestGeolocationPermission(
- content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_frame,
bool user_gesture) {
geolocation_permission_context_->RequestPermission(
- web_contents, id, requesting_frame, user_gesture,
+ id, requesting_frame, user_gesture,
base::BindOnce(&GeolocationPermissionContextTests::PermissionResponse,
base::Unretained(this), id));
content::RunAllTasksUntilIdle();
}
+blink::mojom::PermissionStatus
+GeolocationPermissionContextTests::GetPermissionStatus(
+ blink::PermissionType permission,
+ const GURL& requesting_origin) {
+ return browser_context()
+ ->GetPermissionController()
+ ->GetPermissionStatusForOriginWithoutContext(
+ permission, url::Origin::Create(requesting_origin));
+}
+
void GeolocationPermissionContextTests::PermissionResponse(
const PermissionRequestID& id,
ContentSetting content_setting) {
@@ -261,7 +276,7 @@ void GeolocationPermissionContextTests::CheckPermissionMessageSentForTab(
bool allowed) {
CheckPermissionMessageSentInternal(
static_cast<MockRenderProcessHost*>(
- extra_tabs_[tab]->GetMainFrame()->GetProcess()),
+ extra_tabs_[tab]->GetPrimaryMainFrame()->GetProcess()),
request_id, allowed);
}
@@ -290,13 +305,22 @@ void GeolocationPermissionContextTests::CheckTabContentsState(
ContentSetting expected_content_setting) {
auto* content_settings =
content_settings::PageSpecificContentSettings::GetForFrame(
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
expected_content_setting == CONTENT_SETTING_BLOCK
? content_settings->IsContentBlocked(ContentSettingsType::GEOLOCATION)
: content_settings->IsContentAllowed(ContentSettingsType::GEOLOCATION);
}
+std::unique_ptr<content::BrowserContext>
+GeolocationPermissionContextTests::CreateBrowserContext() {
+ std::unique_ptr<content::TestBrowserContext> test_browser_contest =
+ std::make_unique<content::TestBrowserContext>();
+ test_browser_contest->SetPermissionControllerDelegate(
+ permissions::GetPermissionControllerDelegate(test_browser_contest.get()));
+ return test_browser_contest;
+}
+
void GeolocationPermissionContextTests::SetUp() {
RenderViewHostTestHarness::SetUp();
@@ -336,18 +360,18 @@ void GeolocationPermissionContextTests::SetUp() {
geolocation_permission_context_ = context.get();
- PermissionManager::PermissionContextMap context_map;
- context_map[ContentSettingsType::GEOLOCATION] = std::move(context);
- manager_ = std::make_unique<PermissionManager>(browser_context(),
- std::move(context_map));
+ PermissionManager* permission_manager = static_cast<PermissionManager*>(
+ browser_context()->GetPermissionControllerDelegate());
+
+ permission_manager
+ ->PermissionContextsForTesting()[ContentSettingsType::GEOLOCATION] =
+ std::move(context);
}
void GeolocationPermissionContextTests::TearDown() {
mock_permission_prompt_factories_.clear();
extra_tabs_.clear();
DeleteContents();
- manager_->Shutdown();
- manager_ = nullptr;
RenderViewHostTestHarness::TearDown();
}
@@ -371,7 +395,7 @@ bool GeolocationPermissionContextTests::RequestPermissionIsLSDShown(
NavigateAndCommit(origin);
RequestManagerDocumentLoadCompleted();
MockLocationSettings::ClearHasShownLocationSettingsDialog();
- RequestGeolocationPermission(web_contents(), RequestID(0), origin, true);
+ RequestGeolocationPermission(RequestID(0), origin, true);
return MockLocationSettings::HasShownLocationSettingsDialog();
}
@@ -381,7 +405,7 @@ bool GeolocationPermissionContextTests::
NavigateAndCommit(origin);
RequestManagerDocumentLoadCompleted();
MockLocationSettings::ClearHasShownLocationSettingsDialog();
- RequestGeolocationPermission(web_contents(), RequestID(0), origin, true);
+ RequestGeolocationPermission(RequestID(0), origin, true);
EXPECT_TRUE(HasActivePrompt());
AcceptPrompt();
@@ -480,8 +504,7 @@ TEST_F(GeolocationPermissionContextTests, SinglePermissionPrompt) {
RequestManagerDocumentLoadCompleted();
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
}
@@ -492,8 +515,7 @@ TEST_F(GeolocationPermissionContextTests,
RequestManagerDocumentLoadCompleted();
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
ASSERT_FALSE(HasActivePrompt());
}
@@ -507,8 +529,7 @@ TEST_F(GeolocationPermissionContextTests, GeolocationEnabledDisabled) {
MockLocationSettings::SetLocationStatus(true /* android */,
true /* system */);
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
EXPECT_TRUE(HasActivePrompt());
histograms.ExpectTotalCount("Permissions.Action.Geolocation", 0);
@@ -519,8 +540,7 @@ TEST_F(GeolocationPermissionContextTests, GeolocationEnabledDisabled) {
true /* system */);
MockLocationSettings::SetCanPromptForAndroidPermission(false);
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
histograms.ExpectUniqueSample("Permissions.Action.Geolocation",
static_cast<int>(PermissionAction::IGNORED), 1);
EXPECT_FALSE(HasActivePrompt());
@@ -533,8 +553,7 @@ TEST_F(GeolocationPermissionContextTests, AndroidEnabledCanPrompt) {
MockLocationSettings::SetLocationStatus(false /* android */,
true /* system */);
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
AcceptPrompt();
CheckTabContentsState(requesting_frame, CONTENT_SETTING_ALLOW);
@@ -549,8 +568,7 @@ TEST_F(GeolocationPermissionContextTests, AndroidEnabledCantPrompt) {
true /* system */);
MockLocationSettings::SetCanPromptForAndroidPermission(false);
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
EXPECT_FALSE(HasActivePrompt());
}
@@ -561,8 +579,7 @@ TEST_F(GeolocationPermissionContextTests, SystemLocationOffLSDDisabled) {
MockLocationSettings::SetLocationStatus(true /* android */,
false /* system */);
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
EXPECT_FALSE(HasActivePrompt());
EXPECT_FALSE(MockLocationSettings::HasShownLocationSettingsDialog());
}
@@ -572,8 +589,7 @@ TEST_F(GeolocationPermissionContextTests, SystemLocationOnNoLSD) {
NavigateAndCommit(requesting_frame);
RequestManagerDocumentLoadCompleted();
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
AcceptPrompt();
CheckTabContentsState(requesting_frame, CONTENT_SETTING_ALLOW);
@@ -592,8 +608,7 @@ TEST_F(GeolocationPermissionContextTests, SystemLocationOffLSDAccept) {
MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
GRANTED);
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
AcceptPrompt();
CheckTabContentsState(requesting_frame, CONTENT_SETTING_ALLOW);
@@ -616,8 +631,7 @@ TEST_F(GeolocationPermissionContextTests, SystemLocationOffLSDReject) {
MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
DENIED);
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
AcceptPrompt();
CheckTabContentsState(requesting_frame, CONTENT_SETTING_BLOCK);
@@ -783,15 +797,15 @@ TEST_F(GeolocationPermissionContextTests, LSDBackOffPermissionStatus) {
// The permission status should reflect that the LSD will be shown.
ASSERT_EQ(PermissionStatus::ASK,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
EXPECT_TRUE(RequestPermissionIsLSDShown(requesting_frame));
// Now that the LSD is in backoff, the permission status should reflect it.
EXPECT_FALSE(RequestPermissionIsLSDShown(requesting_frame));
ASSERT_EQ(PermissionStatus::DENIED,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
}
TEST_F(GeolocationPermissionContextTests, LSDBackOffAskPromptsDespiteBackOff) {
@@ -814,8 +828,8 @@ TEST_F(GeolocationPermissionContextTests, LSDBackOffAskPromptsDespiteBackOff) {
SetGeolocationContentSetting(requesting_frame, requesting_frame,
CONTENT_SETTING_ASK);
ASSERT_EQ(PermissionStatus::ASK,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
EXPECT_TRUE(
RequestPermissionIsLSDShownWithPermissionPrompt(requesting_frame));
}
@@ -904,8 +918,7 @@ TEST_F(GeolocationPermissionContextTests, HashIsIgnored) {
// Check permission is requested.
ASSERT_FALSE(HasActivePrompt());
const bool user_gesture = true;
- RequestGeolocationPermission(web_contents(), RequestID(0), url_a,
- user_gesture);
+ RequestGeolocationPermission(RequestID(0), url_a, user_gesture);
ASSERT_TRUE(HasActivePrompt());
// Change the hash, we'll still be on the same page.
@@ -928,8 +941,7 @@ TEST_F(GeolocationPermissionContextTests, DISABLED_PermissionForFileScheme) {
// Check permission is requested.
ASSERT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
EXPECT_TRUE(HasActivePrompt());
// Accept the frame.
@@ -952,7 +964,7 @@ TEST_F(GeolocationPermissionContextTests, CancelGeolocationPermissionRequest) {
ASSERT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), frame_0, true);
+ RequestGeolocationPermission(RequestID(0), frame_0, true);
ASSERT_TRUE(HasActivePrompt());
std::u16string text_0 = GetPromptText();
@@ -975,8 +987,7 @@ TEST_F(GeolocationPermissionContextTests, InvalidURL) {
// Nothing should be displayed.
EXPECT_FALSE(HasActivePrompt());
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- true);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, true);
EXPECT_FALSE(HasActivePrompt());
CheckPermissionMessageSent(0, false);
}
@@ -992,11 +1003,9 @@ TEST_F(GeolocationPermissionContextTests, SameOriginMultipleTabs) {
RequestManagerDocumentLoadCompleted(extra_tabs_[1].get());
// Request permission in all three tabs.
- RequestGeolocationPermission(web_contents(), RequestID(0), url_a, true);
- RequestGeolocationPermission(extra_tabs_[0].get(), RequestIDForTab(0, 0),
- url_b, true);
- RequestGeolocationPermission(extra_tabs_[1].get(), RequestIDForTab(1, 0),
- url_a, true);
+ RequestGeolocationPermission(RequestID(0), url_a, true);
+ RequestGeolocationPermission(RequestIDForTab(0, 0), url_b, true);
+ RequestGeolocationPermission(RequestIDForTab(1, 0), url_a, true);
ASSERT_TRUE(HasActivePrompt()); // For A0.
ASSERT_TRUE(HasActivePrompt(extra_tabs_[0].get()));
ASSERT_TRUE(HasActivePrompt(extra_tabs_[1].get()));
@@ -1021,8 +1030,7 @@ TEST_F(GeolocationPermissionContextTests, TabDestroyed) {
RequestManagerDocumentLoadCompleted();
// Request permission for two frames.
- RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame,
- false);
+ RequestGeolocationPermission(RequestID(0), requesting_frame, false);
ASSERT_TRUE(HasActivePrompt());
EXPECT_EQ(CONTENT_SETTING_ASK,
@@ -1040,24 +1048,24 @@ TEST_F(GeolocationPermissionContextTests, GeolocationStatusAndroidDisabled) {
MockLocationSettings::SetLocationStatus(false /* android */,
true /* system */);
ASSERT_EQ(PermissionStatus::ASK,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
// With the Android permission off, and location blocked for a domain, the
// permission status should still be BLOCK.
SetGeolocationContentSetting(requesting_frame, requesting_frame,
CONTENT_SETTING_BLOCK);
ASSERT_EQ(PermissionStatus::DENIED,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
// With the Android permission off, and location prompt for a domain, the
// permission status should still be ASK.
SetGeolocationContentSetting(requesting_frame, requesting_frame,
CONTENT_SETTING_ASK);
ASSERT_EQ(PermissionStatus::ASK,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
}
TEST_F(GeolocationPermissionContextTests, GeolocationStatusSystemDisabled) {
@@ -1072,14 +1080,14 @@ TEST_F(GeolocationPermissionContextTests, GeolocationStatusSystemDisabled) {
MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
DENIED);
ASSERT_EQ(PermissionStatus::ASK,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
MockLocationSettings::SetLocationSettingsDialogStatus(false /* enabled */,
GRANTED);
ASSERT_EQ(PermissionStatus::DENIED,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
// The result should be the same if the location permission is ASK.
SetGeolocationContentSetting(requesting_frame, requesting_frame,
@@ -1087,14 +1095,14 @@ TEST_F(GeolocationPermissionContextTests, GeolocationStatusSystemDisabled) {
MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
GRANTED);
ASSERT_EQ(PermissionStatus::ASK,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
MockLocationSettings::SetLocationSettingsDialogStatus(false /* enabled */,
GRANTED);
ASSERT_EQ(PermissionStatus::DENIED,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
// With the Android permission off, and location blocked for a domain, the
// permission status should still be BLOCK.
@@ -1103,8 +1111,8 @@ TEST_F(GeolocationPermissionContextTests, GeolocationStatusSystemDisabled) {
MockLocationSettings::SetLocationSettingsDialogStatus(true /* enabled */,
GRANTED);
ASSERT_EQ(PermissionStatus::DENIED,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
}
#endif // BUILDFLAG(IS_ANDROID)
@@ -1146,10 +1154,9 @@ TEST_F(GeolocationPermissionContextTests,
test_case.site_permission);
fake_geolocation_manager_->SetSystemPermission(test_case.system_permission);
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(
- test_case.expected_effective_site_permission,
- manager_->GetPermissionStatus(content::PermissionType::GEOLOCATION,
- requesting_frame, requesting_frame));
+ ASSERT_EQ(test_case.expected_effective_site_permission,
+ GetPermissionStatus(blink::PermissionType::GEOLOCATION,
+ requesting_frame));
}
}
diff --git a/chromium/components/permissions/contexts/local_fonts_permission_context.cc b/chromium/components/permissions/contexts/local_fonts_permission_context.cc
index 45d632ec8c6..0f963b80d9f 100644
--- a/chromium/components/permissions/contexts/local_fonts_permission_context.cc
+++ b/chromium/components/permissions/contexts/local_fonts_permission_context.cc
@@ -9,10 +9,10 @@
LocalFontsPermissionContext::LocalFontsPermissionContext(
content::BrowserContext* browser_context)
- : PermissionContextBase(browser_context,
- ContentSettingsType::LOCAL_FONTS,
- blink::mojom::PermissionsPolicyFeature::kNotFound) {
-}
+ : PermissionContextBase(
+ browser_context,
+ ContentSettingsType::LOCAL_FONTS,
+ blink::mojom::PermissionsPolicyFeature::kLocalFonts) {}
LocalFontsPermissionContext::~LocalFontsPermissionContext() = default;
diff --git a/chromium/components/permissions/contexts/midi_sysex_permission_context_unittest.cc b/chromium/components/permissions/contexts/midi_sysex_permission_context_unittest.cc
index 49c853050b5..e9113d8681f 100644
--- a/chromium/components/permissions/contexts/midi_sysex_permission_context_unittest.cc
+++ b/chromium/components/permissions/contexts/midi_sysex_permission_context_unittest.cc
@@ -76,11 +76,11 @@ TEST_F(MidiSysexPermissionContextTests, TestInsecureRequestingUrl) {
content::WebContentsTester::For(web_contents())->NavigateAndCommit(url);
const PermissionRequestID id(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
permissions::PermissionRequestID::RequestLocalId());
permission_context.RequestPermission(
- web_contents(), id, url, true,
+ id, url, true,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
diff --git a/chromium/components/permissions/contexts/nfc_permission_context.cc b/chromium/components/permissions/contexts/nfc_permission_context.cc
index efda9258313..9a2937e39c7 100644
--- a/chromium/components/permissions/contexts/nfc_permission_context.cc
+++ b/chromium/components/permissions/contexts/nfc_permission_context.cc
@@ -31,7 +31,6 @@ ContentSetting NfcPermissionContext::GetPermissionStatusInternal(
#endif
void NfcPermissionContext::DecidePermission(
- content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
@@ -42,7 +41,7 @@ void NfcPermissionContext::DecidePermission(
return;
}
permissions::PermissionContextBase::DecidePermission(
- web_contents, id, requesting_origin, embedding_origin, user_gesture,
+ id, requesting_origin, embedding_origin, user_gesture,
std::move(callback));
}
diff --git a/chromium/components/permissions/contexts/nfc_permission_context.h b/chromium/components/permissions/contexts/nfc_permission_context.h
index 8c52b53d915..a965187e3a3 100644
--- a/chromium/components/permissions/contexts/nfc_permission_context.h
+++ b/chromium/components/permissions/contexts/nfc_permission_context.h
@@ -42,8 +42,7 @@ class NfcPermissionContext : public PermissionContextBase {
const GURL& requesting_origin,
const GURL& embedding_origin) const override;
#endif
- void DecidePermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ void DecidePermission(const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
diff --git a/chromium/components/permissions/contexts/nfc_permission_context_unittest.cc b/chromium/components/permissions/contexts/nfc_permission_context_unittest.cc
index e5762016a87..5e9f92d42f6 100644
--- a/chromium/components/permissions/contexts/nfc_permission_context_unittest.cc
+++ b/chromium/components/permissions/contexts/nfc_permission_context_unittest.cc
@@ -46,8 +46,7 @@ class NfcPermissionContextTests : public content::RenderViewHostTestHarness {
PermissionRequestID RequestID(int request_id);
- void RequestNfcPermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ void RequestNfcPermission(const PermissionRequestID& id,
const GURL& requesting_frame,
bool user_gesture);
@@ -84,18 +83,17 @@ class NfcPermissionContextTests : public content::RenderViewHostTestHarness {
PermissionRequestID NfcPermissionContextTests::RequestID(int request_id) {
return PermissionRequestID(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
permissions::PermissionRequestID::RequestLocalId(request_id));
}
void NfcPermissionContextTests::RequestNfcPermission(
- content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_frame,
bool user_gesture) {
nfc_permission_context_->RequestPermission(
- web_contents, id, requesting_frame, user_gesture,
+ id, requesting_frame, user_gesture,
base::BindOnce(&NfcPermissionContextTests::PermissionResponse,
base::Unretained(this), id));
content::RunAllTasksUntilIdle();
@@ -231,8 +229,7 @@ TEST_F(NfcPermissionContextTests, SinglePermissionPrompt) {
RequestManagerDocumentLoadCompleted();
EXPECT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame,
- true /* user_gesture */);
+ RequestNfcPermission(RequestID(0), requesting_frame, true /* user_gesture */);
#if BUILDFLAG(IS_ANDROID)
ASSERT_TRUE(HasActivePrompt());
@@ -247,7 +244,7 @@ TEST_F(NfcPermissionContextTests, SinglePermissionPromptFailsOnInsecureOrigin) {
RequestManagerDocumentLoadCompleted();
EXPECT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame, true);
+ RequestNfcPermission(RequestID(0), requesting_frame, true);
ASSERT_FALSE(HasActivePrompt());
}
@@ -260,7 +257,7 @@ TEST_F(NfcPermissionContextTests,
RequestManagerDocumentLoadCompleted();
MockNfcSystemLevelSetting::SetNfcSystemLevelSettingEnabled(false);
EXPECT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame, true);
+ RequestNfcPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
ASSERT_FALSE(MockNfcSystemLevelSetting::HasShownNfcSettingPrompt());
AcceptPrompt();
@@ -275,7 +272,7 @@ TEST_F(NfcPermissionContextTests,
RequestManagerDocumentLoadCompleted();
MockNfcSystemLevelSetting::SetNfcSystemLevelSettingEnabled(false);
EXPECT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame, true);
+ RequestNfcPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
ASSERT_FALSE(MockNfcSystemLevelSetting::HasShownNfcSettingPrompt());
DenyPrompt();
@@ -292,7 +289,7 @@ TEST_F(NfcPermissionContextTests,
RequestManagerDocumentLoadCompleted();
MockNfcSystemLevelSetting::SetNfcSystemLevelSettingEnabled(false);
EXPECT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame, true);
+ RequestNfcPermission(RequestID(0), requesting_frame, true);
ASSERT_FALSE(HasActivePrompt());
ASSERT_TRUE(MockNfcSystemLevelSetting::HasShownNfcSettingPrompt());
}
@@ -305,7 +302,7 @@ TEST_F(NfcPermissionContextTests,
NavigateAndCommit(requesting_frame);
RequestManagerDocumentLoadCompleted();
EXPECT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame, true);
+ RequestNfcPermission(RequestID(0), requesting_frame, true);
ASSERT_FALSE(HasActivePrompt());
ASSERT_FALSE(MockNfcSystemLevelSetting::HasShownNfcSettingPrompt());
}
@@ -318,7 +315,7 @@ TEST_F(NfcPermissionContextTests,
MockNfcSystemLevelSetting::SetNfcSystemLevelSettingEnabled(false);
MockNfcSystemLevelSetting::SetNfcAccessIsPossible(false);
EXPECT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame, true);
+ RequestNfcPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
ASSERT_FALSE(MockNfcSystemLevelSetting::HasShownNfcSettingPrompt());
AcceptPrompt();
@@ -335,7 +332,7 @@ TEST_F(NfcPermissionContextTests,
MockNfcSystemLevelSetting::SetNfcSystemLevelSettingEnabled(false);
MockNfcSystemLevelSetting::SetNfcAccessIsPossible(false);
EXPECT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame, true);
+ RequestNfcPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
ASSERT_FALSE(MockNfcSystemLevelSetting::HasShownNfcSettingPrompt());
DenyPrompt();
@@ -354,7 +351,7 @@ TEST_F(NfcPermissionContextTests,
MockNfcSystemLevelSetting::SetNfcSystemLevelSettingEnabled(false);
MockNfcSystemLevelSetting::SetNfcAccessIsPossible(false);
EXPECT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame, true);
+ RequestNfcPermission(RequestID(0), requesting_frame, true);
ASSERT_FALSE(HasActivePrompt());
ASSERT_FALSE(MockNfcSystemLevelSetting::HasShownNfcSettingPrompt());
CheckPermissionMessageSent(0 /* request _id */, true /* allowed */);
@@ -370,7 +367,7 @@ TEST_F(NfcPermissionContextTests, CancelNfcPermissionRequest) {
ASSERT_FALSE(HasActivePrompt());
- RequestNfcPermission(web_contents(), RequestID(0), requesting_frame, true);
+ RequestNfcPermission(RequestID(0), requesting_frame, true);
ASSERT_TRUE(HasActivePrompt());
diff --git a/chromium/components/permissions/contexts/payment_handler_permission_context.cc b/chromium/components/permissions/contexts/payment_handler_permission_context.cc
index ae16592c2d5..0257a74cdfa 100644
--- a/chromium/components/permissions/contexts/payment_handler_permission_context.cc
+++ b/chromium/components/permissions/contexts/payment_handler_permission_context.cc
@@ -23,7 +23,6 @@ PaymentHandlerPermissionContext::PaymentHandlerPermissionContext(
PaymentHandlerPermissionContext::~PaymentHandlerPermissionContext() {}
void PaymentHandlerPermissionContext::DecidePermission(
- content::WebContents* web_contents,
const permissions::PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
diff --git a/chromium/components/permissions/contexts/payment_handler_permission_context.h b/chromium/components/permissions/contexts/payment_handler_permission_context.h
index cdbab6225cf..6b38df85881 100644
--- a/chromium/components/permissions/contexts/payment_handler_permission_context.h
+++ b/chromium/components/permissions/contexts/payment_handler_permission_context.h
@@ -35,7 +35,6 @@ class PaymentHandlerPermissionContext
private:
// PermissionContextBase
void DecidePermission(
- content::WebContents* web_contents,
const permissions::PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
diff --git a/chromium/components/permissions/contexts/webxr_permission_context.cc b/chromium/components/permissions/contexts/webxr_permission_context.cc
index 4dee1e961bf..fa05ee1676c 100644
--- a/chromium/components/permissions/contexts/webxr_permission_context.cc
+++ b/chromium/components/permissions/contexts/webxr_permission_context.cc
@@ -37,13 +37,8 @@ bool WebXrPermissionContext::IsRestrictedToSecureOrigins() const {
// There are two other permissions that need to check corresponding OS-level
// permissions, and they take two different approaches to this. Geolocation only
// stores the permission ContentSetting if both requests are granted (or if the
-// site permission is "Block"). The media permissions follow something more
-// similar to this approach, first querying and storing the site-specific
-// ContentSetting and then querying for the additional OS permissions as needed.
-// However, this is done in MediaStreamDevicesController, not their permission
-// context. By persisting and then running additional code as needed, we thus
-// mimic that flow, but keep all logic contained into the permission context
-// class.
+// site permission is "Block"). The media permissions are now following the
+// approach found here.
void WebXrPermissionContext::NotifyPermissionSet(
const PermissionRequestID& id,
const GURL& requesting_origin,
@@ -53,6 +48,15 @@ void WebXrPermissionContext::NotifyPermissionSet(
ContentSetting content_setting,
bool is_one_time) {
DCHECK(!is_one_time);
+
+ // Note that this method calls into base class implementation version of
+ // `NotifyPermissionSet()`, which would call `UpdateTabContext()`.
+ // This is fine, even in cases where we call the base method with a parameter
+ // that does not correspond to user's answer to Chrome-level permission,
+ // because `WebXrPermissionContext` does *not* have a custom implementation
+ // for `UpdateTabContext()` - if it did, we'd need to stop calling into base
+ // class with the parameter not matching user's answer.
+
// Only AR needs to check for additional permissions, and then only if it was
// actually allowed.
if (!(content_settings_type_ == ContentSettingsType::AR &&
@@ -135,5 +139,14 @@ void WebXrPermissionContext::OnAndroidPermissionDecided(
id, requesting_origin, embedding_origin, std::move(callback),
false /*persist*/, setting, /*is_one_time=*/false);
}
+
+void WebXrPermissionContext::UpdateTabContext(
+ const permissions::PermissionRequestID& id,
+ const GURL& requesting_origin,
+ bool allowed) {
+ // See the comment in `NotifyPermissionSet()` for context on why this method
+ // should be empty.
+}
+
#endif // BUILDFLAG(IS_ANDROID)
} // namespace permissions
diff --git a/chromium/components/permissions/contexts/webxr_permission_context.h b/chromium/components/permissions/contexts/webxr_permission_context.h
index 596b2ef72f7..4da3d4efe72 100644
--- a/chromium/components/permissions/contexts/webxr_permission_context.h
+++ b/chromium/components/permissions/contexts/webxr_permission_context.h
@@ -40,6 +40,10 @@ class WebXrPermissionContext : public PermissionContextBase {
ContentSetting content_setting,
bool is_one_time) override;
+ void UpdateTabContext(const permissions::PermissionRequestID& id,
+ const GURL& requesting_origin,
+ bool allowed) override;
+
void OnAndroidPermissionDecided(const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
diff --git a/chromium/components/permissions/contexts/window_placement_permission_context.cc b/chromium/components/permissions/contexts/window_placement_permission_context.cc
index 34a7914400e..bc39e59d181 100644
--- a/chromium/components/permissions/contexts/window_placement_permission_context.cc
+++ b/chromium/components/permissions/contexts/window_placement_permission_context.cc
@@ -21,6 +21,17 @@ WindowPlacementPermissionContext::WindowPlacementPermissionContext(
WindowPlacementPermissionContext::~WindowPlacementPermissionContext() = default;
+#if BUILDFLAG(IS_ANDROID)
+ContentSetting WindowPlacementPermissionContext::GetPermissionStatusInternal(
+ content::RenderFrameHost* render_frame_host,
+ const GURL& requesting_origin,
+ const GURL& embedding_origin) const {
+ // TODO(crbug.com/897300): Add window-placement support on Android.
+ NOTIMPLEMENTED_LOG_ONCE() << "window-placement permission is not supported";
+ return CONTENT_SETTING_BLOCK;
+}
+#endif // IS_ANDROID
+
bool WindowPlacementPermissionContext::IsRestrictedToSecureOrigins() const {
return true;
}
diff --git a/chromium/components/permissions/contexts/window_placement_permission_context.h b/chromium/components/permissions/contexts/window_placement_permission_context.h
index 7c13ae1afba..a6620494933 100644
--- a/chromium/components/permissions/contexts/window_placement_permission_context.h
+++ b/chromium/components/permissions/contexts/window_placement_permission_context.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_PERMISSIONS_CONTEXTS_WINDOW_PLACEMENT_PERMISSION_CONTEXT_H_
#define COMPONENTS_PERMISSIONS_CONTEXTS_WINDOW_PLACEMENT_PERMISSION_CONTEXT_H_
+#include "build/build_config.h"
#include "components/permissions/permission_context_base.h"
namespace permissions {
@@ -22,6 +23,12 @@ class WindowPlacementPermissionContext : public PermissionContextBase {
protected:
// PermissionContextBase:
+#if BUILDFLAG(IS_ANDROID)
+ ContentSetting GetPermissionStatusInternal(
+ content::RenderFrameHost* render_frame_host,
+ const GURL& requesting_origin,
+ const GURL& embedding_origin) const override;
+#endif // IS_ANDROID
bool IsRestrictedToSecureOrigins() const override;
void UserMadePermissionDecision(const PermissionRequestID& id,
const GURL& requesting_origin,
diff --git a/chromium/components/permissions/features.cc b/chromium/components/permissions/features.cc
index 5dceacc2f6b..da05a5241dc 100644
--- a/chromium/components/permissions/features.cc
+++ b/chromium/components/permissions/features.cc
@@ -24,6 +24,9 @@ const base::Feature kBlockRepeatedNotificationPermissionPrompts{
"BlockRepeatedNotificationPermissionPrompts",
base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kNotificationInteractionHistory{
+ "NotificationInteractionHistory", base::FEATURE_DISABLED_BY_DEFAULT};
+
const base::Feature kOneTimeGeolocationPermission{
"OneTimeGeolocationPermission", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -32,11 +35,10 @@ const base::Feature kOneTimeGeolocationPermission{
const base::Feature kPermissionChip{"PermissionChip",
base::FEATURE_DISABLED_BY_DEFAULT};
-// Enables an experimental less prominent permission prompt that uses a chip in
-// the location bar. Requires chrome://flags/#quiet-notification-prompts to be
-// enabled.
+// Enables a less prominent permission prompt that uses a chip in the location
+// bar. Requires chrome://flags/#quiet-notification-prompts to be enabled.
const base::Feature kPermissionQuietChip{"PermissionQuietChip",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kPermissionChipAutoDismiss{
"PermissionChipAutoDismiss", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -55,12 +57,6 @@ const base::Feature kPermissionChipGestureSensitive{
const base::Feature kPermissionChipRequestTypeSensitive{
"PermissionChipRequestTypeSensitive", base::FEATURE_DISABLED_BY_DEFAULT};
-// When kPermissionChip (above) is enabled, controls whether or not the
-// permission chip should be shown in the prominent style (white on blue) or in
-// the secondary style (blue on white).
-const base::Feature kPermissionChipIsProminentStyle{
- "PermissionChipIsProminentStyle", base::FEATURE_DISABLED_BY_DEFAULT};
-
// When enabled, use the value of the `service_url` FeatureParam as the url
// for the Web Permission Predictions Service.
const base::Feature kPermissionPredictionServiceUseUrlOverride{
diff --git a/chromium/components/permissions/features.h b/chromium/components/permissions/features.h
index 2c17e642ae4..ea08620d9ff 100644
--- a/chromium/components/permissions/features.h
+++ b/chromium/components/permissions/features.h
@@ -23,6 +23,9 @@ COMPONENT_EXPORT(PERMISSIONS_COMMON)
extern const base::Feature kBlockRepeatedNotificationPermissionPrompts;
COMPONENT_EXPORT(PERMISSIONS_COMMON)
+extern const base::Feature kNotificationInteractionHistory;
+
+COMPONENT_EXPORT(PERMISSIONS_COMMON)
extern const base::Feature kOneTimeGeolocationPermission;
COMPONENT_EXPORT(PERMISSIONS_COMMON)
@@ -44,9 +47,6 @@ COMPONENT_EXPORT(PERMISSIONS_COMMON)
extern const base::Feature kPermissionChipRequestTypeSensitive;
COMPONENT_EXPORT(PERMISSIONS_COMMON)
-extern const base::Feature kPermissionChipIsProminentStyle;
-
-COMPONENT_EXPORT(PERMISSIONS_COMMON)
extern const base::Feature kPermissionPredictionServiceUseUrlOverride;
COMPONENT_EXPORT(PERMISSIONS_COMMON)
diff --git a/chromium/components/permissions/notifications_engagement_service.cc b/chromium/components/permissions/notifications_engagement_service.cc
new file mode 100644
index 00000000000..2be8a5992bc
--- /dev/null
+++ b/chromium/components/permissions/notifications_engagement_service.cc
@@ -0,0 +1,154 @@
+// Copyright 2022 The Chromium 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/permissions/notifications_engagement_service.h"
+
+#include "base/logging.h"
+#include "components/permissions/permissions_client.h"
+#include "url/gurl.h"
+
+namespace permissions {
+
+namespace {
+
+// For each origin that has the |ContentSettingsType::NOTIFICATIONS|
+// permission, we record the number of notifications that were displayed
+// and interacted with. The data is stored in the website setting
+// |NOTIFICATION_INTERACTIONS| keyed to the same origin. The internal
+// structure of this metadata is a dictionary:
+//
+// {"1644163200": {"display_count": 3}, # Implied click_count = 0.
+// "1644768000": {"display_count": 6, "click_count": 1}}
+//
+// Where the value stored for date_i summarizes notification activity
+// for the time period between date_i and date_i+1 (or today for the
+// last entry). Currently, the dates will be space one week apart, and
+// correspond to Monday midnights.
+
+constexpr char kEngagementKey[] = "click_count";
+constexpr char kDisplayedKey[] = "display_count";
+
+// Entries in notifications engagement expire after they become this old.
+constexpr base::TimeDelta kMaxAge = base::Days(90);
+
+// Discards notification interactions stored in `engagement` for time
+// periods older than |kMaxAge|.
+void EraseStaleEntries(base::Value::Dict& engagement) {
+ const base::Time cutoff = base::Time::Now() - kMaxAge;
+
+ for (auto it = engagement.begin(); it != engagement.end();) {
+ const auto& [key, value] = *it;
+
+ absl::optional<base::Time> last_time =
+ NotificationsEngagementService::ParsePeriodBeginFromBucketLabel(key);
+ if (!last_time.has_value() || last_time.value() < cutoff) {
+ it = engagement.erase(it);
+ continue;
+ }
+ ++it;
+ }
+}
+} // namespace
+
+NotificationsEngagementService::NotificationsEngagementService(
+ content::BrowserContext* context,
+ PrefService* pref_service)
+ : pref_service_(pref_service) {
+ settings_map_ =
+ permissions::PermissionsClient::Get()->GetSettingsMap(context);
+}
+
+void NotificationsEngagementService::Shutdown() {
+ settings_map_ = nullptr;
+}
+
+void NotificationsEngagementService::RecordNotificationDisplayed(
+ const GURL& url) {
+ IncrementCounts(url, 1 /*display_count_delta*/, 0 /*click_count_delta*/);
+}
+
+void NotificationsEngagementService::RecordNotificationInteraction(
+ const GURL& url) {
+ IncrementCounts(url, 0 /*display_count_delta*/, 1 /*click_count_delta*/);
+}
+
+void NotificationsEngagementService::IncrementCounts(const GURL& url,
+ int display_count_delta,
+ int click_count_delta) {
+ base::Value engagement_as_value = settings_map_->GetWebsiteSetting(
+ url, GURL(), ContentSettingsType::NOTIFICATION_INTERACTIONS, nullptr);
+
+ base::Value::Dict engagement;
+ if (engagement_as_value.is_dict())
+ engagement = std::move(engagement_as_value.GetDict());
+
+ std::string date = GetBucketLabelForLastMonday(base::Time::Now());
+ if (date == std::string())
+ return;
+
+ EraseStaleEntries(engagement);
+ base::Value::Dict* bucket = engagement.FindDict(date);
+ if (!bucket) {
+ bucket = &engagement.Set(date, base::Value::Dict())->GetDict();
+ }
+ if (display_count_delta) {
+ bucket->Set(kDisplayedKey, display_count_delta +
+ bucket->FindInt(kDisplayedKey).value_or(0));
+ }
+ if (click_count_delta) {
+ bucket->Set(
+ kEngagementKey,
+ click_count_delta + bucket->FindInt(kEngagementKey).value_or(0));
+ }
+
+ // Set the website setting of this origin with the updated |engagement|.
+ settings_map_->SetWebsiteSettingDefaultScope(
+ url, GURL(), ContentSettingsType::NOTIFICATION_INTERACTIONS,
+ base::Value(std::move(engagement)));
+}
+
+// static
+std::string NotificationsEngagementService::GetBucketLabelForLastMonday(
+ base::Time date) {
+ // For human-readability, return the UTC Monday midnight on the same date as
+ // local midnight.
+ base::Time::Exploded date_exploded;
+ date.LocalExplode(&date_exploded);
+ base::Time local_monday =
+ (date - base::Days((date_exploded.day_of_week + 6) % 7)).LocalMidnight();
+
+ base::Time::Exploded local_monday_exploded;
+ local_monday.LocalExplode(&local_monday_exploded);
+ // Intentionally converting a locally exploded time, to an UTC time, so that
+ // the Monday Midnight in UTC is on the same date the last Monday on local
+ // time.
+ base::Time last_monday;
+ bool converted =
+ base::Time::FromUTCExploded(local_monday_exploded, &last_monday);
+
+ if (converted)
+ return base::NumberToString(last_monday.base::Time::ToTimeT());
+
+ return std::string();
+}
+
+// static
+absl::optional<base::Time>
+NotificationsEngagementService::ParsePeriodBeginFromBucketLabel(
+ const std::string& label) {
+ int maybe_engagement_time;
+ base::Time local_period_begin;
+
+ // Store the time as local time.
+ if (base::StringToInt(label.c_str(), &maybe_engagement_time)) {
+ base::Time::Exploded date_exploded;
+ base::Time::FromTimeT(maybe_engagement_time).UTCExplode(&date_exploded);
+ if (base::Time::FromLocalExploded(date_exploded, &local_period_begin))
+ return local_period_begin;
+ }
+
+ return absl::nullopt;
+}
+
+} // namespace permissions
diff --git a/chromium/components/permissions/notifications_engagement_service.h b/chromium/components/permissions/notifications_engagement_service.h
new file mode 100644
index 00000000000..c0d4e671746
--- /dev/null
+++ b/chromium/components/permissions/notifications_engagement_service.h
@@ -0,0 +1,64 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_PERMISSIONS_NOTIFICATIONS_ENGAGEMENT_SERVICE_H_
+#define COMPONENTS_PERMISSIONS_NOTIFICATIONS_ENGAGEMENT_SERVICE_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class GURL;
+class PrefService;
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace permissions {
+
+// This class records and stores notification engagement per origin for the
+// past 90 days. Engagements per origin are bucketed by week: A notification
+// engagement or display is assigned to the last Monday midnight in local time.
+class NotificationsEngagementService : public KeyedService {
+ public:
+ explicit NotificationsEngagementService(content::BrowserContext* context,
+ PrefService* pref_service);
+
+ NotificationsEngagementService(const NotificationsEngagementService&) =
+ delete;
+ NotificationsEngagementService& operator=(
+ const NotificationsEngagementService&) = delete;
+
+ ~NotificationsEngagementService() override = default;
+
+ // KeyedService implementation.
+ void Shutdown() override;
+
+ void RecordNotificationDisplayed(const GURL& url);
+ void RecordNotificationInteraction(const GURL& url);
+
+ // ISO8601 defines Monday as the first day of the week. Additionally, in most
+ // of the world the workweek starts with Monday, so Monday is used
+ // specifically here, as notification usage is a function of work/personal
+ // settings.
+ static std::string GetBucketLabelForLastMonday(base::Time time);
+ static absl::optional<base::Time> ParsePeriodBeginFromBucketLabel(
+ const std::string& label);
+
+ private:
+ void IncrementCounts(const GURL& url,
+ const int display_count_delta,
+ const int click_count_delta);
+
+ // Used to update the notification engagement per URL.
+ raw_ptr<HostContentSettingsMap> settings_map_;
+
+ raw_ptr<PrefService> pref_service_;
+};
+
+} // namespace permissions
+
+#endif // COMPONENTS_PERMISSIONS_NOTIFICATIONS_ENGAGEMENT_SERVICE_H_
diff --git a/chromium/components/permissions/permission_context_base.cc b/chromium/components/permissions/permission_context_base.cc
index c2c6fc7eea9..f3ea3f8cc45 100644
--- a/chromium/components/permissions/permission_context_base.cc
+++ b/chromium/components/permissions/permission_context_base.cc
@@ -120,7 +120,6 @@ PermissionContextBase::~PermissionContextBase() {
}
void PermissionContextBase::RequestPermission(
- content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_frame,
bool user_gesture,
@@ -129,6 +128,13 @@ void PermissionContextBase::RequestPermission(
content::RenderFrameHost* const rfh = content::RenderFrameHost::FromID(
id.render_process_id(), id.render_frame_id());
+
+ if (!rfh) {
+ // Permission request is not allowed without a valid RenderFrameHost.
+ std::move(callback).Run(CONTENT_SETTING_ASK);
+ return;
+ }
+
const GURL requesting_origin = requesting_frame.DeprecatedGetOriginAsURL();
const GURL embedding_origin =
PermissionUtil::GetLastCommittedOriginAsURL(rfh->GetMainFrame());
@@ -220,8 +226,8 @@ void PermissionContextBase::RequestPermission(
PermissionUmaUtil::RecordEmbargoPromptSuppression(
PermissionEmbargoStatus::NOT_EMBARGOED);
- DecidePermission(web_contents, id, requesting_origin, embedding_origin,
- user_gesture, std::move(callback));
+ DecidePermission(id, requesting_origin, embedding_origin, user_gesture,
+ std::move(callback));
}
void PermissionContextBase::UserMadePermissionDecision(
@@ -311,13 +317,16 @@ PermissionResult PermissionContextBase::GetPermissionStatus(
PermissionStatusSource::UNSPECIFIED);
}
- PermissionResult result =
+ absl::optional<PermissionResult> result =
PermissionsClient::Get()
->GetPermissionDecisionAutoBlocker(browser_context_)
->GetEmbargoResult(requesting_origin, content_settings_type_);
- DCHECK(result.content_setting == CONTENT_SETTING_ASK ||
- result.content_setting == CONTENT_SETTING_BLOCK);
- return result;
+ if (result) {
+ DCHECK(result->content_setting == CONTENT_SETTING_BLOCK);
+ return *result;
+ }
+ return PermissionResult(CONTENT_SETTING_ASK,
+ PermissionStatusSource::UNSPECIFIED);
}
bool PermissionContextBase::IsPermissionAvailableToOrigins(
@@ -379,7 +388,6 @@ ContentSetting PermissionContextBase::GetPermissionStatusInternal(
}
void PermissionContextBase::DecidePermission(
- content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
@@ -396,6 +404,12 @@ void PermissionContextBase::DecidePermission(
requesting_origin == embedding_origin ||
content_settings_type_ == ContentSettingsType::STORAGE_ACCESS);
+ content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
+ id.render_process_id(), id.render_frame_id());
+ DCHECK(rfh);
+
+ content::WebContents* web_contents =
+ content::WebContents::FromRenderFrameHost(rfh);
PermissionRequestManager* permission_request_manager =
PermissionRequestManager::FromWebContents(web_contents);
// TODO(felt): sometimes |permission_request_manager| is null. This check is
@@ -418,15 +432,6 @@ void PermissionContextBase::DecidePermission(
.second;
DCHECK(inserted) << "Duplicate id " << id.ToString();
- content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
- id.render_process_id(), id.render_frame_id());
-
- if (!rfh) {
- request->Cancelled();
- request->RequestFinished();
- return;
- }
-
permission_request_manager->AddRequest(rfh, request);
}
diff --git a/chromium/components/permissions/permission_context_base.h b/chromium/components/permissions/permission_context_base.h
index 9266ced5ba3..60c8efd7110 100644
--- a/chromium/components/permissions/permission_context_base.h
+++ b/chromium/components/permissions/permission_context_base.h
@@ -88,8 +88,7 @@ class PermissionContextBase : public KeyedService,
// |callback| is called upon resolution of the request, but not if a prompt
// is shown and ignored.
- virtual void RequestPermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ virtual void RequestPermission(const PermissionRequestID& id,
const GURL& requesting_frame,
bool user_gesture,
BrowserPermissionCallback callback);
@@ -134,8 +133,7 @@ class PermissionContextBase : public KeyedService,
// Called if generic checks (existing content setting, embargo, etc.) fail to
// resolve a permission request. The default implementation prompts the user.
- virtual void DecidePermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ virtual void DecidePermission(const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
diff --git a/chromium/components/permissions/permission_context_base_unittest.cc b/chromium/components/permissions/permission_context_base_unittest.cc
index 4b08004f0b6..e8e69cecc2b 100644
--- a/chromium/components/permissions/permission_context_base_unittest.cc
+++ b/chromium/components/permissions/permission_context_base_unittest.cc
@@ -88,26 +88,23 @@ class TestPermissionContext : public PermissionContextBase {
content_settings_type());
}
- void RequestPermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ void RequestPermission(const PermissionRequestID& id,
const GURL& requesting_frame,
bool user_gesture,
BrowserPermissionCallback callback) override {
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
- PermissionContextBase::RequestPermission(web_contents, id, requesting_frame,
- true /* user_gesture */,
- std::move(callback));
+ PermissionContextBase::RequestPermission(
+ id, requesting_frame, true /* user_gesture */, std::move(callback));
run_loop.Run();
}
- void DecidePermission(content::WebContents* web_contents,
- const PermissionRequestID& id,
+ void DecidePermission(const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
BrowserPermissionCallback callback) override {
- PermissionContextBase::DecidePermission(web_contents, id, requesting_origin,
+ PermissionContextBase::DecidePermission(id, requesting_origin,
embedding_origin, user_gesture,
std::move(callback));
if (respond_permission_) {
@@ -231,14 +228,14 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
base::HistogramTester histograms;
const PermissionRequestID id(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId());
permission_context.SetRespondPermissionCallback(base::BindOnce(
&PermissionContextBaseTests::RespondToPermission,
base::Unretained(this), &permission_context, id, url, decision));
permission_context.RequestPermission(
- web_contents(), id, url, true /* user_gesture */,
+ id, url, true /* user_gesture */,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
ASSERT_EQ(1u, permission_context.decisions().size());
@@ -331,8 +328,8 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
TestPermissionContext permission_context(browser_context(),
content_settings_type);
const PermissionRequestID id(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId());
permission_context.SetRespondPermissionCallback(
@@ -341,7 +338,7 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
CONTENT_SETTING_ASK));
permission_context.RequestPermission(
- web_contents(), id, url, true /* user_gesture */,
+ id, url, true /* user_gesture */,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
histograms.ExpectTotalCount(
@@ -384,8 +381,8 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
TestPermissionContext permission_context(browser_context(),
content_settings_type);
const PermissionRequestID id(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId());
permission_context.SetRespondPermissionCallback(
@@ -394,7 +391,7 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
CONTENT_SETTING_ASK));
permission_context.RequestPermission(
- web_contents(), id, url, true /* user_gesture */,
+ id, url, true /* user_gesture */,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
@@ -424,8 +421,8 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
browser_context(), ContentSettingsType::GEOLOCATION);
const PermissionRequestID id(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId(i + 1));
permission_context.SetRespondPermissionCallback(
@@ -433,7 +430,7 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
base::Unretained(this), &permission_context, id, url,
CONTENT_SETTING_ASK));
permission_context.RequestPermission(
- web_contents(), id, url, true /* user_gesture */,
+ id, url, true /* user_gesture */,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
histograms.ExpectTotalCount(
@@ -499,15 +496,15 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
ContentSettingsType::MIDI_SYSEX);
const PermissionRequestID id(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId(i + 1));
permission_context.SetRespondPermissionCallback(
base::BindOnce(&PermissionContextBaseTests::RespondToPermission,
base::Unretained(this), &permission_context, id, url,
CONTENT_SETTING_ASK));
permission_context.RequestPermission(
- web_contents(), id, url, true /* user_gesture */,
+ id, url, true /* user_gesture */,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
@@ -561,11 +558,11 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
std::string());
const PermissionRequestID id(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId());
permission_context.RequestPermission(
- web_contents(), id, url, true /* user_gesture */,
+ id, url, true /* user_gesture */,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
@@ -586,8 +583,8 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
SetUpUrl(url);
const PermissionRequestID id(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId());
permission_context.SetRespondPermissionCallback(
base::BindOnce(&PermissionContextBaseTests::RespondToPermission,
@@ -595,7 +592,7 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
CONTENT_SETTING_ALLOW));
permission_context.RequestPermission(
- web_contents(), id, url, true /* user_gesture */,
+ id, url, true /* user_gesture */,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
@@ -669,17 +666,17 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
SetUpUrl(url);
const PermissionRequestID id1(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId(1));
const PermissionRequestID id2(
- web_contents()->GetMainFrame()->GetProcess()->GetID(),
- web_contents()->GetMainFrame()->GetRoutingID(),
+ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
+ web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
PermissionRequestID::RequestLocalId(2));
// Request a permission without setting the callback to DecidePermission.
permission_context.RequestPermission(
- web_contents(), id1, url, true /* user_gesture */,
+ id1, url, true /* user_gesture */,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
@@ -690,7 +687,7 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
&PermissionContextBaseTests::RespondToPermission,
base::Unretained(this), &permission_context, id1, url, response));
permission_context.RequestPermission(
- web_contents(), id2, url, true /* user_gesture */,
+ id2, url, true /* user_gesture */,
base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
base::Unretained(&permission_context)));
@@ -714,7 +711,7 @@ class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
virtual_url);
PermissionResult result = permission_context.GetPermissionStatus(
- web_contents()->GetMainFrame(), virtual_url, virtual_url);
+ web_contents()->GetPrimaryMainFrame(), virtual_url, virtual_url);
EXPECT_EQ(result.content_setting, want_response);
EXPECT_EQ(result.source, want_source);
}
diff --git a/chromium/components/permissions/permission_decision_auto_blocker.cc b/chromium/components/permissions/permission_decision_auto_blocker.cc
index bcd1210bd66..f6f55ca4cf9 100644
--- a/chromium/components/permissions/permission_decision_auto_blocker.cc
+++ b/chromium/components/permissions/permission_decision_auto_blocker.cc
@@ -4,10 +4,12 @@
#include "components/permissions/permission_decision_auto_blocker.h"
+#include <algorithm>
#include <memory>
#include <string>
#include <utility>
+#include "base/cxx17_backports.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
@@ -27,6 +29,18 @@ constexpr int kDefaultDismissalsBeforeBlockWithQuietUi = 1;
constexpr int kDefaultIgnoresBeforeBlockWithQuietUi = 2;
constexpr int kDefaultEmbargoDays = 7;
+// The number of times that users may explicitly dismiss a
+// FEDERATED_IDENTITY_API permission prompt from an origin before it is
+// automatically blocked.
+constexpr int kFederatedIdentityApiDismissalsBeforeBlock = 1;
+
+// The durations that an origin will stay under embargo for the
+// FEDERATED_IDENTITY_API permission due to the user explicitly dismissing the
+// permission prompt.
+constexpr base::TimeDelta kFederatedIdentityApiEmbargoDurationDismiss[] = {
+ base::Hours(2) /* 1st dismissal */, base::Days(1) /* 2nd dismissal */,
+ base::Days(7), base::Days(28)};
+
// The number of times that users may explicitly dismiss a permission prompt
// from an origin before it is automatically blocked.
int g_dismissals_before_block = kDefaultDismissalsBeforeBlock;
@@ -53,6 +67,12 @@ int g_dismissal_embargo_days = kDefaultEmbargoDays;
// permission due to repeated ignores.
int g_ignore_embargo_days = kDefaultEmbargoDays;
+std::string GetStringForContentType(ContentSettingsType content_type) {
+ if (content_type == ContentSettingsType::FEDERATED_IDENTITY_API)
+ return "FederatedIdentityApi";
+ return PermissionUtil::GetPermissionString(content_type);
+}
+
std::unique_ptr<base::Value> GetOriginAutoBlockerData(
HostContentSettingsMap* settings,
const GURL& origin_url) {
@@ -83,7 +103,7 @@ int RecordActionInWebsiteSettings(const GURL& url,
GetOriginAutoBlockerData(settings_map, url);
base::Value* permission_dict = GetOrCreatePermissionDict(
- dict.get(), PermissionUtil::GetPermissionString(permission));
+ dict.get(), GetStringForContentType(permission));
base::Value* value =
permission_dict->FindKeyOfType(key, base::Value::Type::INTEGER);
@@ -104,13 +124,38 @@ int GetActionCount(const GURL& url,
std::unique_ptr<base::Value> dict =
GetOriginAutoBlockerData(settings_map, url);
base::Value* permission_dict = GetOrCreatePermissionDict(
- dict.get(), PermissionUtil::GetPermissionString(permission));
+ dict.get(), GetStringForContentType(permission));
base::Value* value =
permission_dict->FindKeyOfType(key, base::Value::Type::INTEGER);
return value ? value->GetInt() : 0;
}
+// Returns the number of times that users may explicitly dismiss a permission
+// prompt for an origin for the passed-in |permission| before it is
+// automatically blocked.
+int GetDismissalsBeforeBlockForContentSettingsType(
+ ContentSettingsType permission) {
+ return (permission == ContentSettingsType::FEDERATED_IDENTITY_API)
+ ? kFederatedIdentityApiDismissalsBeforeBlock
+ : g_dismissals_before_block;
+}
+
+// The duration that an origin will stay under embargo for the passed-in
+// |permission| due to the user explicitly dismissing the permission prompt.
+base::TimeDelta GetEmbargoDurationForContentSettingsType(
+ ContentSettingsType permission,
+ int dismiss_count) {
+ if (permission == ContentSettingsType::FEDERATED_IDENTITY_API) {
+ int duration_index = base::clamp(
+ dismiss_count - 1, 0,
+ static_cast<int>(
+ std::size(kFederatedIdentityApiEmbargoDurationDismiss) - 1));
+ return kFederatedIdentityApiEmbargoDurationDismiss[duration_index];
+ }
+ return base::Days(g_dismissal_embargo_days);
+}
+
base::Time GetEmbargoStartTime(base::Value* permission_dict,
const base::Feature& feature,
const char* key) {
@@ -176,22 +221,33 @@ const char PermissionDecisionAutoBlocker::kPermissionIgnoreEmbargoKey[] =
"ignore_embargo_days";
// static
-PermissionResult PermissionDecisionAutoBlocker::GetEmbargoResult(
+bool PermissionDecisionAutoBlocker::IsEnabledForContentSetting(
+ ContentSettingsType content_setting) {
+ return PermissionUtil::IsPermission(content_setting) ||
+ content_setting == ContentSettingsType::FEDERATED_IDENTITY_API;
+}
+
+// static
+absl::optional<PermissionResult>
+PermissionDecisionAutoBlocker::GetEmbargoResult(
HostContentSettingsMap* settings_map,
const GURL& request_origin,
ContentSettingsType permission,
base::Time current_time) {
DCHECK(settings_map);
- DCHECK(PermissionUtil::IsPermission(permission));
+ DCHECK(IsEnabledForContentSetting(permission));
std::unique_ptr<base::Value> dict =
GetOriginAutoBlockerData(settings_map, request_origin);
base::Value* permission_dict = GetOrCreatePermissionDict(
- dict.get(), PermissionUtil::GetPermissionString(permission));
+ dict.get(), GetStringForContentType(permission));
+ int dismiss_count = GetActionCount(request_origin, permission,
+ kPromptDismissCountKey, settings_map);
if (IsUnderEmbargo(permission_dict, features::kBlockPromptsIfDismissedOften,
kPermissionDismissalEmbargoKey, current_time,
- base::Days(g_dismissal_embargo_days))) {
+ GetEmbargoDurationForContentSettingsType(permission,
+ dismiss_count))) {
return PermissionResult(CONTENT_SETTING_BLOCK,
PermissionStatusSource::MULTIPLE_DISMISSALS);
}
@@ -203,8 +259,7 @@ PermissionResult PermissionDecisionAutoBlocker::GetEmbargoResult(
PermissionStatusSource::MULTIPLE_IGNORES);
}
- return PermissionResult(CONTENT_SETTING_ASK,
- PermissionStatusSource::UNSPECIFIED);
+ return absl::nullopt;
}
// static
@@ -249,7 +304,14 @@ void PermissionDecisionAutoBlocker::UpdateFromVariations() {
kDefaultEmbargoDays);
}
-PermissionResult PermissionDecisionAutoBlocker::GetEmbargoResult(
+bool PermissionDecisionAutoBlocker::IsEmbargoed(
+ const GURL& request_origin,
+ ContentSettingsType permission) {
+ return GetEmbargoResult(request_origin, permission).has_value();
+}
+
+absl::optional<PermissionResult>
+PermissionDecisionAutoBlocker::GetEmbargoResult(
const GURL& request_origin,
ContentSettingsType permission) {
return GetEmbargoResult(settings_map_, request_origin, permission,
@@ -263,7 +325,7 @@ base::Time PermissionDecisionAutoBlocker::GetEmbargoStartTime(
std::unique_ptr<base::Value> dict =
GetOriginAutoBlockerData(settings_map_, request_origin);
base::Value* permission_dict = GetOrCreatePermissionDict(
- dict.get(), PermissionUtil::GetPermissionString(permission));
+ dict.get(), GetStringForContentType(permission));
// A permission may have a record for both dismisal and ignore, return the
// most recent. A permission will only actually be under one embargo, but
@@ -293,13 +355,10 @@ std::set<GURL> PermissionDecisionAutoBlocker::GetEmbargoedOrigins(
std::set<GURL> origins;
for (const auto& e : embargo_settings) {
for (auto content_type : content_types) {
- if (!PermissionUtil::IsPermission(content_type))
+ if (!IsEnabledForContentSetting(content_type))
continue;
const GURL url(e.primary_pattern.ToString());
- PermissionResult result =
- GetEmbargoResult(settings_map_, url, content_type, clock_->Now());
- if (result.source == PermissionStatusSource::MULTIPLE_DISMISSALS ||
- result.source == PermissionStatusSource::MULTIPLE_IGNORES) {
+ if (IsEmbargoed(url, content_type)) {
origins.insert(url);
break;
}
@@ -342,7 +401,8 @@ bool PermissionDecisionAutoBlocker::RecordDismissAndEmbargo(
// 2. Not calling RecordDismissAndEmbargo means no repeated dismissal metrics
// are recorded
if (base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften)) {
- if (current_dismissal_count >= g_dismissals_before_block) {
+ if (current_dismissal_count >=
+ GetDismissalsBeforeBlockForContentSettingsType(permission)) {
PlaceUnderEmbargo(url, permission, kPermissionDismissalEmbargoKey);
return true;
}
@@ -393,13 +453,13 @@ bool PermissionDecisionAutoBlocker::RecordIgnoreAndEmbargo(
void PermissionDecisionAutoBlocker::RemoveEmbargoAndResetCounts(
const GURL& url,
ContentSettingsType permission) {
- if (!PermissionUtil::IsPermission(permission))
+ if (!IsEnabledForContentSetting(permission))
return;
std::unique_ptr<base::Value> dict =
GetOriginAutoBlockerData(settings_map_, url);
- dict->RemoveKey(PermissionUtil::GetPermissionString(permission));
+ dict->RemoveKey(GetStringForContentType(permission));
settings_map_->SetWebsiteSettingDefaultScope(
url, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
@@ -443,7 +503,7 @@ void PermissionDecisionAutoBlocker::PlaceUnderEmbargo(
std::unique_ptr<base::Value> dict =
GetOriginAutoBlockerData(settings_map_, request_origin);
base::Value* permission_dict = GetOrCreatePermissionDict(
- dict.get(), PermissionUtil::GetPermissionString(permission));
+ dict.get(), GetStringForContentType(permission));
permission_dict->SetKey(
key, base::Value(static_cast<double>(clock_->Now().ToInternalValue())));
settings_map_->SetWebsiteSettingDefaultScope(
diff --git a/chromium/components/permissions/permission_decision_auto_blocker.h b/chromium/components/permissions/permission_decision_auto_blocker.h
index e44f1fd6408..122ca9974e0 100644
--- a/chromium/components/permissions/permission_decision_auto_blocker.h
+++ b/chromium/components/permissions/permission_decision_auto_blocker.h
@@ -15,6 +15,7 @@
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/permissions/permission_result.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
class GURL;
@@ -50,23 +51,32 @@ class PermissionDecisionAutoBlocker : public KeyedService {
~PermissionDecisionAutoBlocker() override;
+ // Returns whether the permission auto blocker is enabled for the passed-in
+ // content setting.
+ static bool IsEnabledForContentSetting(ContentSettingsType content_setting);
+
// Checks the status of the content setting to determine if |request_origin|
// is under embargo for |permission|. This checks all types of embargo.
// Prefer to use PermissionManager::GetPermissionStatus when possible. This
// method is only exposed to facilitate permission checks from threads other
// than the UI thread. See https://crbug.com/658020.
- static PermissionResult GetEmbargoResult(HostContentSettingsMap* settings_map,
- const GURL& request_origin,
- ContentSettingsType permission,
- base::Time current_time);
+ static absl::optional<PermissionResult> GetEmbargoResult(
+ HostContentSettingsMap* settings_map,
+ const GURL& request_origin,
+ ContentSettingsType permission,
+ base::Time current_time);
// Updates the threshold to start blocking prompts from the field trial.
static void UpdateFromVariations();
+ // Returns whether |request_origin| is under embargo for |permission|.
+ bool IsEmbargoed(const GURL& request_origin, ContentSettingsType permission);
+
// Checks the status of the content setting to determine if |request_origin|
// is under embargo for |permission|. This checks all types of embargo.
- PermissionResult GetEmbargoResult(const GURL& request_origin,
- ContentSettingsType permission);
+ absl::optional<PermissionResult> GetEmbargoResult(
+ const GURL& request_origin,
+ ContentSettingsType permission);
// Returns the most recent recorded time either an ignore or dismiss embargo
// was started. Records of embargo start times persist beyond the duration of
diff --git a/chromium/components/permissions/permission_decision_auto_blocker_unittest.cc b/chromium/components/permissions/permission_decision_auto_blocker_unittest.cc
index cc7612728f7..01abf4187ff 100644
--- a/chromium/components/permissions/permission_decision_auto_blocker_unittest.cc
+++ b/chromium/components/permissions/permission_decision_auto_blocker_unittest.cc
@@ -101,18 +101,18 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, RemoveEmbargoAndResetCounts) {
url2, ContentSettingsType::GEOLOCATION, false));
// Verify all dismissals recorded above resulted in embargo.
- PermissionResult result =
+ absl::optional<PermissionResult> result =
autoblocker()->GetEmbargoResult(url1, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
result =
autoblocker()->GetEmbargoResult(url1, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
result =
autoblocker()->GetEmbargoResult(url2, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
// Remove the embargo on notifications. Verify it is no longer under embargo,
// but location still is.
@@ -120,18 +120,17 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, RemoveEmbargoAndResetCounts) {
url1, ContentSettingsType::NOTIFICATIONS);
result =
autoblocker()->GetEmbargoResult(url1, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
result =
autoblocker()->GetEmbargoResult(url1, ContentSettingsType::NOTIFICATIONS);
- // If not under embargo, GetEmbargoResult() returns a setting of ASK.
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ // If not under embargo, GetEmbargoResult() returns absl::nullopt.
+ EXPECT_FALSE(result.has_value());
// Verify |url2|'s embargo is still intact as well.
result =
autoblocker()->GetEmbargoResult(url2, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
}
// Test it does not take one more dismissal to re-trigger embargo after
@@ -149,26 +148,24 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest,
url, ContentSettingsType::GEOLOCATION, false));
// Verify location is under embargo.
- PermissionResult result =
+ absl::optional<PermissionResult> result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
// Remove embargo and verify this is true.
autoblocker()->RemoveEmbargoAndResetCounts(url,
ContentSettingsType::GEOLOCATION);
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Record another dismissal and verify location is not under embargo again.
autoblocker()->RecordDismissAndEmbargo(url, ContentSettingsType::GEOLOCATION,
false);
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
}
TEST_F(PermissionDecisionAutoBlockerUnitTest,
@@ -438,10 +435,9 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, CheckEmbargoStatus) {
clock()->SetNow(base::Time::Now());
// Check the default state.
- PermissionResult result =
+ absl::optional<PermissionResult> result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Place under embargo and verify.
EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
@@ -452,21 +448,20 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, CheckEmbargoStatus) {
url, ContentSettingsType::GEOLOCATION, false));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
// Check that the origin is not under embargo for a different permission.
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Confirm embargo status during the embargo period.
clock()->Advance(base::Days(5));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
// Check embargo is lifted on expiry day. A small offset after the exact
// embargo expiration date has been added to account for any precision errors
@@ -474,15 +469,13 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, CheckEmbargoStatus) {
clock()->Advance(base::Hours(3 * 24 + 1));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Check embargo is lifted well after the expiry day.
clock()->Advance(base::Days(1));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Place under embargo again and verify the embargo status.
EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
@@ -494,12 +487,11 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, CheckEmbargoStatus) {
clock()->Advance(base::Days(1));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
}
// Check that GetEmbargoStartTime returns the correct time for embargoes whether
@@ -554,10 +546,9 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, CheckEmbargoStartTime) {
embargo_start_time =
autoblocker()->GetEmbargoStartTime(url, ContentSettingsType::GEOLOCATION);
EXPECT_EQ(test_time, embargo_start_time);
- PermissionResult result =
+ absl::optional<PermissionResult> result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Advance time, reinstate embargo and confirm that time is updated.
test_time += base::Days(9);
@@ -633,50 +624,47 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, TestDismissEmbargoBackoff) {
url, ContentSettingsType::GEOLOCATION, false));
// A request with < 3 prior dismisses should not be automatically blocked.
- PermissionResult result =
+ absl::optional<PermissionResult> result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// After the 3rd dismiss subsequent permission requests should be autoblocked.
EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
url, ContentSettingsType::GEOLOCATION, false));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
// Accelerate time forward, check that the embargo status is lifted and the
// request won't be automatically blocked.
clock()->Advance(base::Days(8));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Record another dismiss, subsequent requests should be autoblocked again.
EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
url, ContentSettingsType::GEOLOCATION, false));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
// Accelerate time again, check embargo is lifted and another permission
// request is let through.
clock()->Advance(base::Days(8));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Record another dismiss, subsequent requests should be autoblocked again.
EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
url, ContentSettingsType::GEOLOCATION, false));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::GEOLOCATION);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
}
// Tests the alternating pattern of the block on multiple ignores behaviour.
@@ -692,10 +680,9 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, TestIgnoreEmbargoBackoff) {
url, ContentSettingsType::MIDI_SYSEX, false));
// A request with < 4 prior ignores should not be automatically blocked.
- PermissionResult result =
+ absl::optional<PermissionResult> result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::MIDI_SYSEX);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// After the 4th ignore subsequent permission requests should be autoblocked.
EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo(
@@ -704,40 +691,38 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, TestIgnoreEmbargoBackoff) {
url, ContentSettingsType::MIDI_SYSEX, false));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::MIDI_SYSEX);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result->source);
// Accelerate time forward, check that the embargo status is lifted and the
// request won't be automatically blocked.
clock()->Advance(base::Days(8));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::MIDI_SYSEX);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Record another dismiss, subsequent requests should be autoblocked again.
EXPECT_TRUE(autoblocker()->RecordIgnoreAndEmbargo(
url, ContentSettingsType::MIDI_SYSEX, false));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::MIDI_SYSEX);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result->source);
// Accelerate time again, check embargo is lifted and another permission
// request is let through.
clock()->Advance(base::Days(8));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::MIDI_SYSEX);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Record another dismiss, subsequent requests should be autoblocked again.
EXPECT_TRUE(autoblocker()->RecordIgnoreAndEmbargo(
url, ContentSettingsType::MIDI_SYSEX, false));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::MIDI_SYSEX);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result->source);
}
// Test that quiet ui embargo has a different threshold for ignores.
@@ -746,34 +731,31 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, TestIgnoreEmbargoUsingQuietUi) {
clock()->SetNow(base::Time::Now());
// Check the default state.
- PermissionResult result =
+ absl::optional<PermissionResult> result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// One quiet ui ignore is not enough to trigger embargo.
EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo(
url, ContentSettingsType::NOTIFICATIONS, true));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// Loud ui ignores are counted separately.
EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo(
url, ContentSettingsType::NOTIFICATIONS, false));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// The second quiet ui ignore puts the url under embargo.
EXPECT_TRUE(autoblocker()->RecordIgnoreAndEmbargo(
url, ContentSettingsType::NOTIFICATIONS, true));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result->source);
}
// Test that quiet ui embargo has a different threshold for dismisses.
@@ -782,26 +764,101 @@ TEST_F(PermissionDecisionAutoBlockerUnitTest, TestDismissEmbargoUsingQuietUi) {
clock()->SetNow(base::Time::Now());
// Check the default state.
- PermissionResult result =
+ absl::optional<PermissionResult> result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// One loud ui dismiss does not trigger embargo.
EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
url, ContentSettingsType::NOTIFICATIONS, false));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
+ EXPECT_FALSE(result.has_value());
// One quiet ui dismiss puts the url under embargo.
EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
url, ContentSettingsType::NOTIFICATIONS, true));
result =
autoblocker()->GetEmbargoResult(url, ContentSettingsType::NOTIFICATIONS);
- EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
- EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
+}
+
+namespace {
+
+// Checks that embargo on federated identity permission is lifted only after the
+// passed-in |time_delta| has elapsed.
+void CheckFederatedIdentityApiEmbargoLiftedAfterTimeElapsing(
+ PermissionDecisionAutoBlocker* autoblocker,
+ base::SimpleTestClock* clock,
+ const GURL& url,
+ base::TimeDelta time_delta) {
+ ASSERT_LT(base::Minutes(1), time_delta);
+
+ clock->Advance(time_delta - base::Minutes(1));
+ absl::optional<PermissionResult> result = autoblocker->GetEmbargoResult(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API);
+ EXPECT_EQ(CONTENT_SETTING_BLOCK, result->content_setting);
+ EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result->source);
+
+ clock->Advance(base::Minutes(2));
+ result = autoblocker->GetEmbargoResult(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API);
+ EXPECT_FALSE(result.has_value());
+}
+
+} // namespace
+
+TEST_F(PermissionDecisionAutoBlockerUnitTest,
+ TestDismissFederatedIdentityApiBackoff) {
+ GURL url("https://www.google.com");
+ clock()->SetNow(base::Time::Now());
+
+ absl::optional<PermissionResult> result = autoblocker()->GetEmbargoResult(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API);
+ EXPECT_FALSE(result.has_value());
+
+ // 2 hour embargo for 1st dismissal
+ EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API, false));
+ CheckFederatedIdentityApiEmbargoLiftedAfterTimeElapsing(
+ autoblocker(), clock(), url, base::Hours(2));
+
+ // 1 day embargo for 2nd dismissal
+ EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API, false));
+ CheckFederatedIdentityApiEmbargoLiftedAfterTimeElapsing(
+ autoblocker(), clock(), url, base::Days(1));
+
+ // 7 day embargo for 3rd dismissal
+ EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API, false));
+ CheckFederatedIdentityApiEmbargoLiftedAfterTimeElapsing(
+ autoblocker(), clock(), url, base::Days(7));
+
+ // 28 day embargo for 4th dismissal (and all additional dismissals)
+ EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API, false));
+ CheckFederatedIdentityApiEmbargoLiftedAfterTimeElapsing(
+ autoblocker(), clock(), url, base::Days(28));
+
+ EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API, false));
+ CheckFederatedIdentityApiEmbargoLiftedAfterTimeElapsing(
+ autoblocker(), clock(), url, base::Days(28));
+
+ // Return to 2 hour embargo after
+ // PermissionDecisionAutoBlocker::RemoveEmbargoAndResetCounts()
+ autoblocker()->RemoveEmbargoAndResetCounts(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API);
+ result = autoblocker()->GetEmbargoResult(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API);
+ EXPECT_FALSE(result.has_value());
+
+ EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+ url, ContentSettingsType::FEDERATED_IDENTITY_API, false));
+ CheckFederatedIdentityApiEmbargoLiftedAfterTimeElapsing(
+ autoblocker(), clock(), url, base::Hours(2));
}
} // namespace permissions
diff --git a/chromium/components/permissions/permission_manager.cc b/chromium/components/permissions/permission_manager.cc
index 442050e3d16..96d7af0319c 100644
--- a/chromium/components/permissions/permission_manager.cc
+++ b/chromium/components/permissions/permission_manager.cc
@@ -24,13 +24,13 @@
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/permission_controller.h"
-#include "content/public/browser/permission_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/common/permissions/permission_utils.h"
+using blink::PermissionType;
using blink::mojom::PermissionStatus;
-using content::PermissionType;
namespace permissions {
namespace {
@@ -73,92 +73,6 @@ PermissionStatus ContentSettingToPermissionStatus(ContentSetting setting) {
return PermissionStatus::DENIED;
}
-ContentSetting PermissionStatusToContentSetting(PermissionStatus status) {
- switch (status) {
- case PermissionStatus::GRANTED:
- return CONTENT_SETTING_ALLOW;
- case PermissionStatus::ASK:
- return CONTENT_SETTING_ASK;
- case PermissionStatus::DENIED:
- default:
- return CONTENT_SETTING_BLOCK;
- }
-
- NOTREACHED();
- return CONTENT_SETTING_DEFAULT;
-}
-
-// Helper method to convert PermissionType to ContentSettingType.
-// If PermissionType is not supported or found, returns
-// ContentSettingsType::DEFAULT.
-ContentSettingsType PermissionTypeToContentSettingSafe(
- PermissionType permission) {
- switch (permission) {
- case PermissionType::MIDI:
- return ContentSettingsType::MIDI;
- case PermissionType::MIDI_SYSEX:
- return ContentSettingsType::MIDI_SYSEX;
- case PermissionType::NOTIFICATIONS:
- return ContentSettingsType::NOTIFICATIONS;
- case PermissionType::GEOLOCATION:
- return ContentSettingsType::GEOLOCATION;
- case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
- return ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER;
-#else
- break;
-#endif
- case PermissionType::DURABLE_STORAGE:
- return ContentSettingsType::DURABLE_STORAGE;
- case PermissionType::AUDIO_CAPTURE:
- return ContentSettingsType::MEDIASTREAM_MIC;
- case PermissionType::VIDEO_CAPTURE:
- return ContentSettingsType::MEDIASTREAM_CAMERA;
- case PermissionType::BACKGROUND_SYNC:
- return ContentSettingsType::BACKGROUND_SYNC;
- case PermissionType::SENSORS:
- return ContentSettingsType::SENSORS;
- case PermissionType::ACCESSIBILITY_EVENTS:
- return ContentSettingsType::ACCESSIBILITY_EVENTS;
- case PermissionType::CLIPBOARD_READ_WRITE:
- return ContentSettingsType::CLIPBOARD_READ_WRITE;
- case PermissionType::CLIPBOARD_SANITIZED_WRITE:
- return ContentSettingsType::CLIPBOARD_SANITIZED_WRITE;
- case PermissionType::PAYMENT_HANDLER:
- return ContentSettingsType::PAYMENT_HANDLER;
- case PermissionType::BACKGROUND_FETCH:
- return ContentSettingsType::BACKGROUND_FETCH;
- case PermissionType::IDLE_DETECTION:
- return ContentSettingsType::IDLE_DETECTION;
- case PermissionType::PERIODIC_BACKGROUND_SYNC:
- return ContentSettingsType::PERIODIC_BACKGROUND_SYNC;
- case PermissionType::WAKE_LOCK_SCREEN:
- return ContentSettingsType::WAKE_LOCK_SCREEN;
- case PermissionType::WAKE_LOCK_SYSTEM:
- return ContentSettingsType::WAKE_LOCK_SYSTEM;
- case PermissionType::NFC:
- return ContentSettingsType::NFC;
- case PermissionType::VR:
- return ContentSettingsType::VR;
- case PermissionType::AR:
- return ContentSettingsType::AR;
- case PermissionType::STORAGE_ACCESS_GRANT:
- return ContentSettingsType::STORAGE_ACCESS;
- case PermissionType::CAMERA_PAN_TILT_ZOOM:
- return ContentSettingsType::CAMERA_PAN_TILT_ZOOM;
- case PermissionType::WINDOW_PLACEMENT:
- return ContentSettingsType::WINDOW_PLACEMENT;
- case PermissionType::LOCAL_FONTS:
- return ContentSettingsType::LOCAL_FONTS;
- case PermissionType::DISPLAY_CAPTURE:
- return ContentSettingsType::DISPLAY_CAPTURE;
- case PermissionType::NUM:
- break;
- }
-
- return ContentSettingsType::DEFAULT;
-}
-
PermissionDelegationMode GetPermissionDelegationMode(
ContentSettingsType permission) {
// TODO(crbug.com/987654): Generalize this to other "background permissions",
@@ -177,13 +91,6 @@ void SubscriptionCallbackWrapper(
std::move(callback).Run(ContentSettingToPermissionStatus(content_setting));
}
-void PermissionStatusCallbackWrapper(
- base::OnceCallback<void(PermissionStatus)> callback,
- const std::vector<ContentSetting>& vector) {
- DCHECK_EQ(1ul, vector.size());
- std::move(callback).Run(ContentSettingToPermissionStatus(vector.at(0)));
-}
-
void PermissionStatusVectorCallbackWrapper(
base::OnceCallback<void(const std::vector<PermissionStatus>&)> callback,
const std::vector<ContentSetting>& content_settings) {
@@ -194,17 +101,6 @@ void PermissionStatusVectorCallbackWrapper(
std::move(callback).Run(permission_statuses);
}
-void ContentSettingCallbackWrapper(
- base::OnceCallback<void(ContentSetting)> callback,
- const std::vector<ContentSetting>& vector) {
- DCHECK_EQ(1ul, vector.size());
- std::move(callback).Run(vector.at(0));
-}
-
-// TODO(crbug.com/698985): As part of the permission API refactoring, this
-// method should be used in all places where we check or request permissions.
-// Currently it is used only in `RequestPermissions` and
-// `GetPermissionStatusForFrame`.
GURL GetEmbeddingOrigin(content::RenderFrameHost* const render_frame_host,
const GURL& requesting_origin) {
content::WebContents* const web_contents =
@@ -354,38 +250,6 @@ PermissionManager::~PermissionManager() {
DCHECK(subscriptions_.IsEmpty());
}
-void PermissionManager::Shutdown() {
- is_shutting_down_ = true;
-
- if (!subscriptions_.IsEmpty()) {
- subscriptions_.Clear();
- for (const auto& type_to_count : subscription_type_counts_) {
- if (type_to_count.second > 0) {
- PermissionContextBase* context =
- GetPermissionContext(type_to_count.first);
- context->RemoveObserver(this);
- }
- }
- subscription_type_counts_.clear();
- }
-}
-
-// static
-ContentSettingsType PermissionManager::PermissionTypeToContentSetting(
- PermissionType permission) {
- ContentSettingsType content_setting =
- PermissionTypeToContentSettingSafe(permission);
- DCHECK_NE(content_setting, ContentSettingsType::DEFAULT)
- << "Unknown content setting for permission "
- << static_cast<int>(permission);
- return content_setting;
-}
-
-PermissionContextBase* PermissionManager::GetPermissionContextForTesting(
- ContentSettingsType type) {
- return GetPermissionContext(type);
-}
-
GURL PermissionManager::GetCanonicalOrigin(ContentSettingsType permission,
const GURL& requesting_origin,
const GURL& embedding_origin) const {
@@ -399,110 +263,15 @@ GURL PermissionManager::GetCanonicalOrigin(ContentSettingsType permission,
case PermissionDelegationMode::kDelegated:
return embedding_origin;
case PermissionDelegationMode::kDoubleKeyed:
- return requesting_origin;
case PermissionDelegationMode::kUndelegated:
return requesting_origin;
}
}
-void PermissionManager::RequestPermission(
- ContentSettingsType content_settings_type,
- content::RenderFrameHost* render_frame_host,
- const GURL& requesting_origin,
- bool user_gesture,
- base::OnceCallback<void(ContentSetting)> callback) {
- RequestPermissions(
- std::vector<ContentSettingsType>(1, content_settings_type),
- render_frame_host, requesting_origin, user_gesture,
- base::BindOnce(&ContentSettingCallbackWrapper, std::move(callback)));
-}
-
-void PermissionManager::RequestPermissions(
- const std::vector<ContentSettingsType>& permissions,
- content::RenderFrameHost* render_frame_host,
- const GURL& requesting_origin,
- bool user_gesture,
- base::OnceCallback<void(const std::vector<ContentSetting>&)> callback) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- if (permissions.empty()) {
- std::move(callback).Run(std::vector<ContentSetting>());
- return;
- }
-
- auto request_local_id = request_local_id_generator_.GenerateNextId();
- pending_requests_.AddWithID(
- std::make_unique<PendingRequest>(render_frame_host, permissions,
- std::move(callback)),
- request_local_id);
-
- const PermissionRequestID request_id(render_frame_host, request_local_id);
- const GURL embedding_origin =
- GetEmbeddingOrigin(render_frame_host, requesting_origin);
-
- for (size_t i = 0; i < permissions.size(); ++i) {
- const ContentSettingsType permission = permissions[i];
- const GURL canonical_requesting_origin =
- GetCanonicalOrigin(permission, requesting_origin, embedding_origin);
-
- auto response_callback =
- std::make_unique<PermissionResponseCallback>(this, request_local_id, i);
- if (IsPermissionBlockedInPartition(permission, requesting_origin,
- render_frame_host->GetProcess())) {
- response_callback->OnPermissionsRequestResponseStatus(
- CONTENT_SETTING_BLOCK);
- continue;
- }
-
- auto status = GetPermissionOverrideForDevTools(
- url::Origin::Create(canonical_requesting_origin), permission);
- if (status != CONTENT_SETTING_DEFAULT) {
- response_callback->OnPermissionsRequestResponseStatus(
- CONTENT_SETTING_ALLOW);
- continue;
- }
-
- PermissionContextBase* context = GetPermissionContext(permission);
- DCHECK(context);
-
- context->RequestPermission(
- content::WebContents::FromRenderFrameHost(render_frame_host),
- request_id, canonical_requesting_origin, user_gesture,
- base::BindOnce(
- &PermissionResponseCallback::OnPermissionsRequestResponseStatus,
- std::move(response_callback)));
- }
-}
-
-void PermissionManager::RequestPermissionFromCurrentDocument(
- ContentSettingsType content_settings_type,
- content::RenderFrameHost* render_frame_host,
- bool user_gesture,
- base::OnceCallback<void(ContentSetting)> callback) {
- RequestPermissionsFromCurrentDocument(
- std::vector<ContentSettingsType>(1, content_settings_type),
- render_frame_host, user_gesture,
- base::BindOnce(&ContentSettingCallbackWrapper, std::move(callback)));
-}
-
-void PermissionManager::RequestPermissionsFromCurrentDocument(
- const std::vector<ContentSettingsType>& permissions,
- content::RenderFrameHost* render_frame_host,
- bool user_gesture,
- base::OnceCallback<void(const std::vector<ContentSetting>&)> callback) {
- RequestPermissions(
- permissions, render_frame_host,
- PermissionUtil::GetLastCommittedOriginAsURL(render_frame_host),
- user_gesture, std::move(callback));
-}
-
-PermissionResult PermissionManager::GetPermissionStatus(
+PermissionResult PermissionManager::GetPermissionStatusDeprecated(
ContentSettingsType permission,
const GURL& requesting_origin,
const GURL& embedding_origin) {
- // With permission delegation enabled, this function should only ever be
- // called for the top level origin (or a service worker origin).
- // GetPermissionStatusForFrame should be called when to determine the status
- // for an embedded frame.
DCHECK_EQ(requesting_origin, embedding_origin);
return GetPermissionStatusHelper(permission,
@@ -520,19 +289,6 @@ PermissionResult PermissionManager::GetPermissionStatusForDisplayOnSettingsUI(
origin);
}
-PermissionResult PermissionManager::GetPermissionStatusForFrame(
- ContentSettingsType permission,
- content::RenderFrameHost* render_frame_host,
- const GURL& requesting_origin) {
- const GURL embedding_origin =
- GetEmbeddingOrigin(render_frame_host, requesting_origin);
-
- return GetPermissionStatusHelper(permission,
- /*render_process_host=*/nullptr,
- render_frame_host, requesting_origin,
- embedding_origin);
-}
-
PermissionResult PermissionManager::GetPermissionStatusForCurrentDocument(
ContentSettingsType permission,
content::RenderFrameHost* render_frame_host) {
@@ -547,74 +303,61 @@ PermissionResult PermissionManager::GetPermissionStatusForCurrentDocument(
embedding_origin);
}
-PermissionResult PermissionManager::GetPermissionStatusForWorker(
- ContentSettingsType permission,
- content::RenderProcessHost* render_process_host,
- const url::Origin& worker_origin) {
- GURL worker_url = worker_origin.GetURL();
- return GetPermissionStatusHelper(permission, render_process_host,
- /*render_frame_host=*/nullptr, worker_url,
- worker_url);
+void PermissionManager::Shutdown() {
+ is_shutting_down_ = true;
+
+ if (!subscriptions_.IsEmpty()) {
+ subscriptions_.Clear();
+ for (const auto& type_to_count : subscription_type_counts_) {
+ if (type_to_count.second > 0) {
+ PermissionContextBase* context =
+ GetPermissionContext(type_to_count.first);
+ context->RemoveObserver(this);
+ }
+ }
+ subscription_type_counts_.clear();
+ }
+}
+
+PermissionContextBase* PermissionManager::GetPermissionContextForTesting(
+ ContentSettingsType type) {
+ return GetPermissionContext(type);
+}
+
+PermissionContextBase* PermissionManager::GetPermissionContext(
+ ContentSettingsType type) {
+ const auto& it = permission_contexts_.find(type);
+ return it == permission_contexts_.end() ? nullptr : it->second.get();
}
+// TODO(crbug.com/1271543): Remove
+// PermissionControllerDelegate::RequestPermission.
void PermissionManager::RequestPermission(
PermissionType permission,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
bool user_gesture,
base::OnceCallback<void(PermissionStatus)> callback) {
- ContentSettingsType content_settings_type =
- PermissionTypeToContentSetting(permission);
- RequestPermissions(
- std::vector<ContentSettingsType>(1, content_settings_type),
- render_frame_host, requesting_origin, user_gesture,
- base::BindOnce(&PermissionStatusCallbackWrapper, std::move(callback)));
+ NOTIMPLEMENTED();
}
+// TODO(crbug.com/1271543): Remove
+// PermissionControllerDelegate::RequestPermissions.
void PermissionManager::RequestPermissions(
const std::vector<PermissionType>& permissions,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
bool user_gesture,
base::OnceCallback<void(const std::vector<PermissionStatus>&)> callback) {
- std::vector<ContentSettingsType> content_settings_types;
- std::transform(permissions.begin(), permissions.end(),
- back_inserter(content_settings_types),
- PermissionTypeToContentSetting);
- RequestPermissions(content_settings_types, render_frame_host,
- requesting_origin, user_gesture,
- base::BindOnce(&PermissionStatusVectorCallbackWrapper,
- std::move(callback)));
-}
-
-PermissionContextBase* PermissionManager::GetPermissionContext(
- ContentSettingsType type) {
- const auto& it = permission_contexts_.find(type);
- return it == permission_contexts_.end() ? nullptr : it->second.get();
-}
-
-void PermissionManager::OnPermissionsRequestResponseStatus(
- PendingRequestLocalId request_local_id,
- int permission_id,
- ContentSetting content_setting) {
- PendingRequest* pending_request = pending_requests_.Lookup(request_local_id);
- if (!pending_request)
- return;
-
- pending_request->SetContentSetting(permission_id, content_setting);
-
- if (!pending_request->IsComplete())
- return;
-
- pending_request->TakeCallback().Run(pending_request->results());
- pending_requests_.Remove(request_local_id);
+ NOTIMPLEMENTED();
}
void PermissionManager::ResetPermission(PermissionType permission,
const GURL& requesting_origin,
const GURL& embedding_origin) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- ContentSettingsType type = PermissionTypeToContentSetting(permission);
+ ContentSettingsType type =
+ PermissionUtil::PermissionTypeToContentSetting(permission);
PermissionContextBase* context = GetPermissionContext(type);
if (!context)
return;
@@ -623,6 +366,73 @@ void PermissionManager::ResetPermission(PermissionType permission,
embedding_origin.DeprecatedGetOriginAsURL());
}
+void PermissionManager::RequestPermissionsFromCurrentDocument(
+ const std::vector<PermissionType>& permissions_types,
+ content::RenderFrameHost* render_frame_host,
+ bool user_gesture,
+ base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)>
+ permission_status_callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ std::vector<ContentSettingsType> permissions;
+ std::transform(permissions_types.begin(), permissions_types.end(),
+ back_inserter(permissions),
+ PermissionUtil::PermissionTypeToContentSetting);
+
+ base::OnceCallback<void(const std::vector<ContentSetting>&)> callback =
+ base::BindOnce(&PermissionStatusVectorCallbackWrapper,
+ std::move(permission_status_callback));
+
+ if (permissions.empty()) {
+ std::move(callback).Run(std::vector<ContentSetting>());
+ return;
+ }
+
+ auto request_local_id = request_local_id_generator_.GenerateNextId();
+ pending_requests_.AddWithID(
+ std::make_unique<PendingRequest>(render_frame_host, permissions,
+ std::move(callback)),
+ request_local_id);
+
+ const PermissionRequestID request_id(render_frame_host, request_local_id);
+ const GURL requesting_origin =
+ PermissionUtil::GetLastCommittedOriginAsURL(render_frame_host);
+ const GURL embedding_origin =
+ GetEmbeddingOrigin(render_frame_host, requesting_origin);
+
+ for (size_t i = 0; i < permissions.size(); ++i) {
+ const ContentSettingsType permission = permissions[i];
+ const GURL canonical_requesting_origin =
+ GetCanonicalOrigin(permission, requesting_origin, embedding_origin);
+
+ auto response_callback =
+ std::make_unique<PermissionResponseCallback>(this, request_local_id, i);
+ if (IsPermissionBlockedInPartition(permission, requesting_origin,
+ render_frame_host->GetProcess())) {
+ response_callback->OnPermissionsRequestResponseStatus(
+ CONTENT_SETTING_BLOCK);
+ continue;
+ }
+
+ auto status = GetPermissionOverrideForDevTools(
+ url::Origin::Create(canonical_requesting_origin), permission);
+ if (status != CONTENT_SETTING_DEFAULT) {
+ response_callback->OnPermissionsRequestResponseStatus(
+ CONTENT_SETTING_ALLOW);
+ continue;
+ }
+
+ PermissionContextBase* context = GetPermissionContext(permission);
+ DCHECK(context);
+
+ context->RequestPermission(
+ request_id, canonical_requesting_origin, user_gesture,
+ base::BindOnce(
+ &PermissionResponseCallback::OnPermissionsRequestResponseStatus,
+ std::move(response_callback)));
+ }
+}
+
PermissionStatus PermissionManager::GetPermissionStatus(
PermissionType permission,
const GURL& requesting_origin,
@@ -630,22 +440,30 @@ PermissionStatus PermissionManager::GetPermissionStatus(
// TODO(benwells): split this into two functions, GetPermissionStatus and
// GetPermissionStatusForPermissionsAPI.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- PermissionResult result =
- GetPermissionStatus(PermissionTypeToContentSetting(permission),
- requesting_origin, embedding_origin);
+ PermissionResult result = GetPermissionStatusHelper(
+ PermissionUtil::PermissionTypeToContentSetting(permission),
+ /*render_process_host=*/nullptr,
+ /*render_frame_host=*/nullptr, requesting_origin, embedding_origin);
return ContentSettingToPermissionStatus(result.content_setting);
}
-PermissionStatus PermissionManager::GetPermissionStatusForFrame(
+PermissionStatus PermissionManager::GetPermissionStatusForCurrentDocument(
PermissionType permission,
- content::RenderFrameHost* render_frame_host,
- const GURL& requesting_origin) {
- // TODO(benwells): split this into two functions, GetPermissionStatus and
- // GetPermissionStatusForPermissionsAPI.
+ content::RenderFrameHost* render_frame_host) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- ContentSettingsType type = PermissionTypeToContentSetting(permission);
- PermissionResult result =
- GetPermissionStatusForFrame(type, render_frame_host, requesting_origin);
+ ContentSettingsType type =
+ PermissionUtil::PermissionTypeToContentSetting(permission);
+
+ const GURL requesting_origin =
+ PermissionUtil::GetLastCommittedOriginAsURL(render_frame_host);
+ const GURL embedding_origin =
+ GetEmbeddingOrigin(render_frame_host, requesting_origin);
+
+ PermissionResult result = GetPermissionStatusHelper(
+ type,
+ /*render_process_host=*/nullptr, render_frame_host, requesting_origin,
+ embedding_origin);
+
return ContentSettingToPermissionStatus(result.content_setting);
}
@@ -654,25 +472,20 @@ PermissionStatus PermissionManager::GetPermissionStatusForWorker(
content::RenderProcessHost* render_process_host,
const GURL& worker_origin) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- ContentSettingsType type = PermissionTypeToContentSetting(permission);
- PermissionResult result = GetPermissionStatusForWorker(
- type, render_process_host, url::Origin::Create(worker_origin));
+ ContentSettingsType type =
+ PermissionUtil::PermissionTypeToContentSetting(permission);
+ PermissionResult result = GetPermissionStatusHelper(
+ type, render_process_host,
+ /*render_frame_host=*/nullptr, worker_origin, worker_origin);
return ContentSettingToPermissionStatus(result.content_setting);
}
-PermissionStatus PermissionManager::GetPermissionStatusForCurrentDocument(
- PermissionType permission,
- content::RenderFrameHost* render_frame_host) {
- return GetPermissionStatusForFrame(
- permission, render_frame_host,
- render_frame_host->GetLastCommittedOrigin().GetURL());
-}
-
bool PermissionManager::IsPermissionOverridableByDevTools(
- content::PermissionType permission,
+ PermissionType permission,
const absl::optional<url::Origin>& origin) {
- ContentSettingsType type = PermissionTypeToContentSettingSafe(permission);
+ ContentSettingsType type =
+ PermissionUtil::PermissionTypeToContentSettingSafe(permission);
PermissionContextBase* context = GetPermissionContext(type);
if (!context || context->IsPermissionKillSwitchOn())
@@ -694,7 +507,8 @@ PermissionManager::SubscribePermissionStatusChange(
if (is_shutting_down_)
return SubscriptionId();
- ContentSettingsType content_type = PermissionTypeToContentSetting(permission);
+ ContentSettingsType content_type =
+ PermissionUtil::PermissionTypeToContentSetting(permission);
auto& type_count = subscription_type_counts_[content_type];
if (type_count == 0) {
PermissionContextBase* context = GetPermissionContext(content_type);
@@ -714,17 +528,21 @@ PermissionManager::SubscribePermissionStatusChange(
subscription->render_frame_id = render_frame_host->GetRoutingID();
subscription->render_process_id = render_frame_host->GetProcess()->GetID();
subscription->current_value =
- GetPermissionStatusForFrame(content_type, render_frame_host,
- requesting_origin)
+ GetPermissionStatusHelper(content_type,
+ /*render_process_host=*/nullptr,
+ render_frame_host, requesting_origin,
+ embedding_origin)
.content_setting;
+
} else {
embedding_origin = requesting_origin;
subscription->render_frame_id = -1;
subscription->render_process_id =
render_process_host ? render_process_host->GetID() : -1;
subscription->current_value =
- GetPermissionStatusForWorker(content_type, render_process_host,
- url::Origin::Create(requesting_origin))
+ GetPermissionStatusHelper(content_type, render_process_host,
+ /*render_frame_host=*/nullptr,
+ requesting_origin, embedding_origin)
.content_setting;
}
@@ -761,10 +579,21 @@ void PermissionManager::UnsubscribePermissionStatusChange(
}
}
-bool PermissionManager::IsPermissionKillSwitchOn(
- ContentSettingsType permission) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- return GetPermissionContext(permission)->IsPermissionKillSwitchOn();
+void PermissionManager::OnPermissionsRequestResponseStatus(
+ PendingRequestLocalId request_local_id,
+ int permission_id,
+ ContentSetting content_setting) {
+ PendingRequest* pending_request = pending_requests_.Lookup(request_local_id);
+ if (!pending_request)
+ return;
+
+ pending_request->SetContentSetting(permission_id, content_setting);
+
+ if (!pending_request->IsComplete())
+ return;
+
+ pending_request->TakeCallback().Run(pending_request->results());
+ pending_requests_.Remove(request_local_id);
}
void PermissionManager::OnPermissionChanged(
@@ -787,6 +616,7 @@ void PermissionManager::OnPermissionChanged(
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
subscription->render_process_id, subscription->render_frame_id);
GURL embedding_origin;
+ GURL requesting_origin = subscription->requesting_origin;
if (rfh) {
embedding_origin =
GetEmbeddingOrigin(rfh, subscription->requesting_origin);
@@ -794,30 +624,21 @@ void PermissionManager::OnPermissionChanged(
embedding_origin = subscription->requesting_origin;
}
- if (!primary_pattern.Matches(subscription->requesting_origin) ||
+ if (!primary_pattern.Matches(requesting_origin) ||
!secondary_pattern.Matches(embedding_origin)) {
continue;
}
content::RenderProcessHost* rph =
- content::RenderProcessHost::FromID(subscription->render_process_id);
-
- ContentSetting new_value;
- if (rfh) {
- new_value = GetPermissionStatusForFrame(subscription->permission, rfh,
- subscription->requesting_origin)
- .content_setting;
- } else if (rph) {
- new_value = GetPermissionStatusForWorker(
- subscription->permission, rph,
- url::Origin::Create(subscription->requesting_origin))
- .content_setting;
- } else {
- new_value = GetPermissionStatus(subscription->permission,
- subscription->requesting_origin,
- subscription->requesting_origin)
- .content_setting;
- }
+ rfh ? nullptr
+ : content::RenderProcessHost::FromID(
+ subscription->render_process_id);
+
+ ContentSetting new_value =
+ GetPermissionStatusHelper(subscription->permission, rph, rfh,
+ subscription->requesting_origin,
+ embedding_origin)
+ .content_setting;
if (subscription->current_value == new_value)
continue;
@@ -868,15 +689,32 @@ PermissionResult PermissionManager::GetPermissionStatusHelper(
return result;
}
+ContentSetting PermissionManager::GetPermissionOverrideForDevTools(
+ const url::Origin& origin,
+ ContentSettingsType permission) {
+ auto it = devtools_permission_overrides_.find(origin);
+ if (it == devtools_permission_overrides_.end())
+ it = devtools_permission_overrides_.find(devtools_global_overrides_origin_);
+ if (it == devtools_permission_overrides_.end())
+ return CONTENT_SETTING_DEFAULT;
+
+ auto setting_it = it->second.find(permission);
+ if (setting_it == it->second.end())
+ return CONTENT_SETTING_DEFAULT;
+
+ return setting_it->second;
+}
+
void PermissionManager::SetPermissionOverridesForDevTools(
const absl::optional<url::Origin>& optional_origin,
const PermissionOverrides& overrides) {
ContentSettingsTypeOverrides result;
for (const auto& item : overrides) {
ContentSettingsType content_setting =
- PermissionTypeToContentSettingSafe(item.first);
+ PermissionUtil::PermissionTypeToContentSettingSafe(item.first);
if (content_setting != ContentSettingsType::DEFAULT)
- result[content_setting] = PermissionStatusToContentSetting(item.second);
+ result[content_setting] =
+ PermissionUtil::PermissionStatusToContentSetting(item.second);
}
const url::Origin& origin =
optional_origin.value_or(devtools_global_overrides_origin_);
@@ -887,20 +725,4 @@ void PermissionManager::ResetPermissionOverridesForDevTools() {
devtools_permission_overrides_.clear();
}
-ContentSetting PermissionManager::GetPermissionOverrideForDevTools(
- const url::Origin& origin,
- ContentSettingsType permission) {
- auto it = devtools_permission_overrides_.find(origin);
- if (it == devtools_permission_overrides_.end())
- it = devtools_permission_overrides_.find(devtools_global_overrides_origin_);
- if (it == devtools_permission_overrides_.end())
- return CONTENT_SETTING_DEFAULT;
-
- auto setting_it = it->second.find(permission);
- if (setting_it == it->second.end())
- return CONTENT_SETTING_DEFAULT;
-
- return setting_it->second;
-}
-
} // namespace permissions
diff --git a/chromium/components/permissions/permission_manager.h b/chromium/components/permissions/permission_manager.h
index 478e6eea14f..bace9811475 100644
--- a/chromium/components/permissions/permission_manager.h
+++ b/chromium/components/permissions/permission_manager.h
@@ -19,18 +19,25 @@
#include "components/permissions/permission_request_id.h"
#include "components/permissions/permission_util.h"
#include "content/public/browser/permission_controller_delegate.h"
-#include "content/public/browser/permission_type.h"
#include "url/origin.h"
+namespace blink {
+enum class PermissionType;
+}
+
namespace content {
class BrowserContext;
class RenderFrameHost;
class RenderProcessHost;
}
+class GeolocationPermissionContextDelegateTests;
+class SubscriptionInterceptingPermissionManager;
+
namespace permissions {
class PermissionContextBase;
struct PermissionResult;
+class PermissionManagerTest;
class PermissionManager : public KeyedService,
public content::PermissionControllerDelegate,
@@ -63,40 +70,11 @@ class PermissionManager : public KeyedService,
const GURL& requesting_origin,
const GURL& embedding_origin) const;
- // Callers from within chrome/ should use the methods which take the
- // ContentSettingsType enum. The methods which take PermissionType values
- // are for the content::PermissionControllerDelegate overrides and shouldn't
- // be used from chrome/.
- // Deprecated. Use `RequestPermissionFromCurrentDocument` instead.
- void RequestPermission(ContentSettingsType permission,
- content::RenderFrameHost* render_frame_host,
- const GURL& requesting_origin,
- bool user_gesture,
- base::OnceCallback<void(ContentSetting)> callback);
- // Deprecated. Use `RequestPermissionsFromCurrentDocument` instead.
- void RequestPermissions(
- const std::vector<ContentSettingsType>& permissions,
- content::RenderFrameHost* render_frame_host,
- const GURL& requesting_origin,
- bool user_gesture,
- base::OnceCallback<void(const std::vector<ContentSetting>&)> callback);
- void RequestPermissionFromCurrentDocument(
- ContentSettingsType permission,
- content::RenderFrameHost* render_frame_host,
- bool user_gesture,
- base::OnceCallback<void(ContentSetting)> callback);
- // Requests the given `permission` on behalf of the last committed document in
- // `render_frame_host`, also performing additional checks such as Permission
- // Policy.
- void RequestPermissionsFromCurrentDocument(
- const std::vector<ContentSettingsType>& permissions,
- content::RenderFrameHost* render_frame_host,
- bool user_gesture,
- base::OnceCallback<void(const std::vector<ContentSetting>&)> callback);
-
- PermissionResult GetPermissionStatus(ContentSettingsType permission,
- const GURL& requesting_origin,
- const GURL& embedding_origin);
+ // This method is deprecated. Use `GetPermissionStatusForCurrentDocument`
+ // instead or `GetPermissionStatusForDisplayOnSettingsUI`.
+ PermissionResult GetPermissionStatusDeprecated(ContentSettingsType permission,
+ const GURL& requesting_origin,
+ const GURL& embedding_origin);
// Returns the permission status for a given `permission` and displayed,
// top-level `origin`. This should be used only for displaying on the
@@ -106,18 +84,6 @@ class PermissionManager : public KeyedService,
ContentSettingsType permission,
const GURL& origin);
- // Returns the permission status for a given frame. This should be preferred
- // over GetPermissionStatus as additional checks can be performed when we know
- // the exact context the request is coming from.
- // TODO(raymes): Currently we still pass the |requesting_origin| as a separate
- // parameter because we can't yet guarantee that it matches the last committed
- // origin of the RenderFrameHost. See crbug.com/698985.
- // Deprecated. Use `GetPermissionStatusForCurrentDocument` instead.
- PermissionResult GetPermissionStatusForFrame(
- ContentSettingsType permission,
- content::RenderFrameHost* render_frame_host,
- const GURL& requesting_origin);
-
// Returns the status for the given `permission` on behalf of the last
// committed document in `render_frame_host`, also performing additional
// checks such as Permission Policy.
@@ -125,52 +91,81 @@ class PermissionManager : public KeyedService,
ContentSettingsType permission,
content::RenderFrameHost* render_frame_host);
- // Returns the status of the given `permission` for a worker on `origin`
- // running in the renderer corresponding to `render_process_host`.
- PermissionResult GetPermissionStatusForWorker(
- ContentSettingsType permission,
- content::RenderProcessHost* render_process_host,
- const url::Origin& worker_origin);
+ // KeyedService implementation.
+ void Shutdown() override;
+
+ PermissionContextBase* GetPermissionContextForTesting(
+ ContentSettingsType type);
+
+ PermissionContextMap& PermissionContextsForTesting() {
+ return permission_contexts_;
+ }
+
+ private:
+ friend class PermissionManagerTest;
+ friend class ::GeolocationPermissionContextDelegateTests;
+ friend class ::SubscriptionInterceptingPermissionManager;
+
+ // The `PendingRequestLocalId` will be unique within the `PermissionManager`
+ // instance, thus within a `BrowserContext`, which overachieves the
+ // requirement from `PermissionRequestID` that the `RequestLocalId` be unique
+ // within each frame.
+ class PendingRequest;
+ using PendingRequestLocalId = PermissionRequestID::RequestLocalId;
+ using PendingRequestsMap =
+ base::IDMap<std::unique_ptr<PendingRequest>, PendingRequestLocalId>;
+
+ class PermissionResponseCallback;
+
+ struct Subscription;
+ using SubscriptionsMap =
+ base::IDMap<std::unique_ptr<Subscription>, SubscriptionId>;
+ using SubscriptionTypeCounts = base::flat_map<ContentSettingsType, size_t>;
+
+ PermissionContextBase* GetPermissionContext(ContentSettingsType type);
// content::PermissionControllerDelegate implementation.
void RequestPermission(
- content::PermissionType permission,
+ blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
bool user_gesture,
base::OnceCallback<void(blink::mojom::PermissionStatus)> callback)
override;
void RequestPermissions(
- const std::vector<content::PermissionType>& permissions,
+ const std::vector<blink::PermissionType>& permissions,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
bool user_gesture,
base::OnceCallback<
void(const std::vector<blink::mojom::PermissionStatus>&)> callback)
override;
- void ResetPermission(content::PermissionType permission,
+ void ResetPermission(blink::PermissionType permission,
const GURL& requesting_origin,
const GURL& embedding_origin) override;
+ void RequestPermissionsFromCurrentDocument(
+ const std::vector<blink::PermissionType>& permissions,
+ content::RenderFrameHost* render_frame_host,
+ bool user_gesture,
+ base::OnceCallback<
+ void(const std::vector<blink::mojom::PermissionStatus>&)> callback)
+ override;
blink::mojom::PermissionStatus GetPermissionStatus(
- content::PermissionType permission,
+ blink::PermissionType permission,
const GURL& requesting_origin,
const GURL& embedding_origin) override;
- blink::mojom::PermissionStatus GetPermissionStatusForFrame(
- content::PermissionType permission,
- content::RenderFrameHost* render_frame_host,
- const GURL& requesting_origin) override;
blink::mojom::PermissionStatus GetPermissionStatusForCurrentDocument(
- content::PermissionType permission,
+ blink::PermissionType permission,
content::RenderFrameHost* render_frame_host) override;
blink::mojom::PermissionStatus GetPermissionStatusForWorker(
- content::PermissionType permission,
+ blink::PermissionType permission,
content::RenderProcessHost* render_process_host,
const GURL& worker_origin) override;
bool IsPermissionOverridableByDevTools(
- content::PermissionType permission,
+ blink::PermissionType permission,
const absl::optional<url::Origin>& origin) override;
SubscriptionId SubscribePermissionStatusChange(
- content::PermissionType permission,
+ blink::PermissionType permission,
content::RenderProcessHost* render_process_host,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
@@ -179,49 +174,6 @@ class PermissionManager : public KeyedService,
void UnsubscribePermissionStatusChange(
SubscriptionId subscription_id) override;
- // TODO(raymes): Rather than exposing this, use the denial reason from
- // GetPermissionStatus in callers to determine whether a permission is
- // denied due to the kill switch.
- bool IsPermissionKillSwitchOn(ContentSettingsType);
-
- // For the given |origin|, overrides permissions that belong to |overrides|.
- // These permissions are in-sync with the PermissionController.
- void SetPermissionOverridesForDevTools(
- const absl::optional<url::Origin>& origin,
- const PermissionOverrides& overrides) override;
- void ResetPermissionOverridesForDevTools() override;
-
- // KeyedService implementation
- void Shutdown() override;
-
- // Helper method to convert PermissionType to ContentSettingType.
- static ContentSettingsType PermissionTypeToContentSetting(
- content::PermissionType permission);
-
- PermissionContextBase* GetPermissionContextForTesting(
- ContentSettingsType type);
-
- private:
- friend class PermissionManagerTest;
-
- // The `PendingRequestLocalId` will be unique within the `PermissionManager`
- // instance, thus within a `BrowserContext`, which overachieves the
- // requirement from `PermissionRequestID` that the `RequestLocalId` be unique
- // within each frame.
- class PendingRequest;
- using PendingRequestLocalId = PermissionRequestID::RequestLocalId;
- using PendingRequestsMap =
- base::IDMap<std::unique_ptr<PendingRequest>, PendingRequestLocalId>;
-
- class PermissionResponseCallback;
-
- struct Subscription;
- using SubscriptionsMap =
- base::IDMap<std::unique_ptr<Subscription>, SubscriptionId>;
- using SubscriptionTypeCounts = base::flat_map<ContentSettingsType, size_t>;
-
- PermissionContextBase* GetPermissionContext(ContentSettingsType type);
-
// Called when a permission was decided for a given PendingRequest. The
// PendingRequest is identified by its |request_local_id| and the permission
// is identified by its |permission_id|. If the PendingRequest contains more
@@ -251,6 +203,14 @@ class PermissionManager : public KeyedService,
const url::Origin& origin,
ContentSettingsType permission);
+ // content::PermissionControllerDelegate implementation.
+ // For the given |origin|, overrides permissions that belong to |overrides|.
+ // These permissions are in-sync with the PermissionController.
+ void SetPermissionOverridesForDevTools(
+ const absl::optional<url::Origin>& origin,
+ const PermissionOverrides& overrides) override;
+ void ResetPermissionOverridesForDevTools() override;
+
raw_ptr<content::BrowserContext> browser_context_;
PendingRequestsMap pending_requests_;
diff --git a/chromium/components/permissions/permission_manager_unittest.cc b/chromium/components/permissions/permission_manager_unittest.cc
index 6dc3b6453ec..33a824d1174 100644
--- a/chromium/components/permissions/permission_manager_unittest.cc
+++ b/chromium/components/permissions/permission_manager_unittest.cc
@@ -7,6 +7,7 @@
#include <memory>
#include "base/bind.h"
+#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "build/build_config.h"
@@ -20,7 +21,6 @@
#include "components/permissions/test/permission_test_util.h"
#include "components/permissions/test/test_permissions_client.h"
#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/permission_type.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/mock_render_process_host.h"
@@ -28,13 +28,15 @@
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/permissions/permission_utils.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/build_info.h"
#endif // BUILDFLAG(IS_ANDROID)
+using blink::PermissionType;
+using blink::mojom::PermissionsPolicyFeature;
using blink::mojom::PermissionStatus;
-using content::PermissionType;
namespace permissions {
namespace {
@@ -64,7 +66,7 @@ class ScopedPartitionedOriginBrowserClient
private:
url::Origin app_origin_;
- content::ContentBrowserClient* old_client_;
+ raw_ptr<content::ContentBrowserClient> old_client_;
};
#if BUILDFLAG(IS_ANDROID)
@@ -118,8 +120,10 @@ class PermissionManagerTest : public content::RenderViewHostTestHarness {
void CheckPermissionResult(ContentSettingsType type,
ContentSetting expected_status,
PermissionStatusSource expected_status_source) {
- PermissionResult result = GetPermissionManager()->GetPermissionStatus(
- type, url_.DeprecatedGetOriginAsURL(), url_.DeprecatedGetOriginAsURL());
+ PermissionResult result =
+ GetPermissionManager()->GetPermissionStatusDeprecated(
+ type, url_.DeprecatedGetOriginAsURL(),
+ url_.DeprecatedGetOriginAsURL());
EXPECT_EQ(expected_status, result.content_setting);
EXPECT_EQ(expected_status_source, result.source);
}
@@ -135,18 +139,86 @@ class PermissionManagerTest : public content::RenderViewHostTestHarness {
type, value);
}
- void RequestPermission(PermissionType type,
- content::RenderFrameHost* rfh,
- const GURL& origin) {
+ void RequestPermissionFromCurrentDocument(PermissionType type,
+ content::RenderFrameHost* rfh) {
base::RunLoop loop;
quit_closure_ = loop.QuitClosure();
- GetPermissionManager()->RequestPermission(
- type, rfh, origin, true,
- base::BindOnce(&PermissionManagerTest::OnPermissionChange,
- base::Unretained(this)));
+ GetPermissionManager()->RequestPermissionsFromCurrentDocument(
+ std::vector(1, type), rfh, true,
+ base::BindOnce(
+ [](base::OnceCallback<void(blink::mojom::PermissionStatus)>
+ callback,
+ const std::vector<blink::mojom::PermissionStatus>& state) {
+ DCHECK_EQ(state.size(), 1U);
+ std::move(callback).Run(state[0]);
+ },
+ base::BindOnce(&PermissionManagerTest::OnPermissionChange,
+ base::Unretained(this))));
loop.Run();
}
+ void RequestPermissionFromCurrentDocumentNonBlocking(
+ PermissionType type,
+ content::RenderFrameHost* rfh) {
+ GetPermissionManager()->RequestPermissionsFromCurrentDocument(
+ std::vector(1, type), rfh, true,
+ base::BindOnce(
+ [](base::OnceCallback<void(blink::mojom::PermissionStatus)>
+ callback,
+ const std::vector<blink::mojom::PermissionStatus>& state) {
+ DCHECK_EQ(state.size(), 1U);
+ std::move(callback).Run(state[0]);
+ },
+ base::BindOnce(&PermissionManagerTest::OnPermissionChange,
+ base::Unretained(this))));
+ }
+
+ PermissionStatus GetPermissionStatusForCurrentDocument(
+ PermissionType permission,
+ content::RenderFrameHost* render_frame_host) {
+ return GetPermissionManager()->GetPermissionStatusForCurrentDocument(
+ permission, render_frame_host);
+ }
+
+ PermissionStatus GetPermissionStatusForWorker(
+ PermissionType permission,
+ content::RenderProcessHost* render_process_host,
+ const GURL& worker_origin) {
+ return GetPermissionManager()->GetPermissionStatusForWorker(
+ permission, render_process_host, worker_origin);
+ }
+
+ content::PermissionControllerDelegate::SubscriptionId
+ SubscribePermissionStatusChange(
+ PermissionType permission,
+ content::RenderProcessHost* render_process_host,
+ content::RenderFrameHost* render_frame_host,
+ const GURL& requesting_origin,
+ base::RepeatingCallback<void(PermissionStatus)> callback) {
+ return GetPermissionManager()->SubscribePermissionStatusChange(
+ permission, render_process_host, render_frame_host, requesting_origin,
+ std::move(callback));
+ }
+
+ void UnsubscribePermissionStatusChange(
+ content::PermissionControllerDelegate::SubscriptionId subscription_id) {
+ GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ }
+
+ bool IsPermissionOverridableByDevTools(
+ PermissionType permission,
+ const absl::optional<url::Origin>& origin) {
+ return GetPermissionManager()->IsPermissionOverridableByDevTools(permission,
+ origin);
+ }
+
+ void ResetPermission(PermissionType permission,
+ const GURL& requesting_origin,
+ const GURL& embedding_origin) {
+ GetPermissionManager()->ResetPermission(permission, requesting_origin,
+ embedding_origin);
+ }
+
const GURL& url() const { return url_; }
const GURL& other_url() const { return other_url_; }
@@ -169,10 +241,9 @@ class PermissionManagerTest : public content::RenderViewHostTestHarness {
// The header policy should only be set once on page load, so we refresh the
// page to simulate that.
- void RefreshPageAndSetHeaderPolicy(
- content::RenderFrameHost** rfh,
- blink::mojom::PermissionsPolicyFeature feature,
- const std::vector<std::string>& origins) {
+ void RefreshPageAndSetHeaderPolicy(content::RenderFrameHost** rfh,
+ PermissionsPolicyFeature feature,
+ const std::vector<std::string>& origins) {
content::RenderFrameHost* current = *rfh;
auto navigation = content::NavigationSimulator::CreateRendererInitiated(
current->GetLastCommittedURL(), current);
@@ -188,10 +259,9 @@ class PermissionManagerTest : public content::RenderViewHostTestHarness {
content::RenderFrameHost* AddChildRFH(
content::RenderFrameHost* parent,
const GURL& origin,
- blink::mojom::PermissionsPolicyFeature feature =
- blink::mojom::PermissionsPolicyFeature::kNotFound) {
+ PermissionsPolicyFeature feature = PermissionsPolicyFeature::kNotFound) {
blink::ParsedPermissionsPolicy frame_policy = {};
- if (feature != blink::mojom::PermissionsPolicyFeature::kNotFound) {
+ if (feature != PermissionsPolicyFeature::kNotFound) {
frame_policy.push_back(
{feature, std::vector<url::Origin>{url::Origin::Create(origin)},
false, false});
@@ -244,6 +314,11 @@ TEST_F(PermissionManagerTest, GetPermissionStatusDefault) {
#if BUILDFLAG(IS_ANDROID)
CheckPermissionStatus(PermissionType::PROTECTED_MEDIA_IDENTIFIER,
GetDefaultProtectedMediaIdentifierPermissionStatus());
+ CheckPermissionStatus(PermissionType::WINDOW_PLACEMENT,
+ PermissionStatus::DENIED);
+#else
+ CheckPermissionStatus(PermissionType::WINDOW_PLACEMENT,
+ PermissionStatus::ASK);
#endif
}
@@ -263,6 +338,14 @@ TEST_F(PermissionManagerTest, GetPermissionStatusAfterSet) {
CONTENT_SETTING_ALLOW);
CheckPermissionStatus(PermissionType::PROTECTED_MEDIA_IDENTIFIER,
PermissionStatus::GRANTED);
+
+ SetPermission(ContentSettingsType::WINDOW_PLACEMENT, CONTENT_SETTING_ALLOW);
+ CheckPermissionStatus(PermissionType::WINDOW_PLACEMENT,
+ PermissionStatus::DENIED);
+#else
+ SetPermission(ContentSettingsType::WINDOW_PLACEMENT, CONTENT_SETTING_ALLOW);
+ CheckPermissionStatus(PermissionType::WINDOW_PLACEMENT,
+ PermissionStatus::GRANTED);
#endif
}
@@ -306,7 +389,7 @@ TEST_F(PermissionManagerTest, CheckPermissionResultAfterSet) {
TEST_F(PermissionManagerTest, SubscriptionDestroyedCleanlyWithoutUnsubscribe) {
// Test that the PermissionManager shuts down cleanly with subscriptions that
// haven't been removed, crbug.com/720071.
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr, main_rfh(),
url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -315,7 +398,7 @@ TEST_F(PermissionManagerTest, SubscriptionDestroyedCleanlyWithoutUnsubscribe) {
TEST_F(PermissionManagerTest, SubscribeUnsubscribeAfterShutdown) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -326,22 +409,22 @@ TEST_F(PermissionManagerTest, SubscribeUnsubscribeAfterShutdown) {
// reenterant.
GetPermissionManager()->Shutdown();
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
// Check that subscribe/unsubscribe after shutdown don't crash.
content::PermissionControllerDelegate::SubscriptionId subscription2_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
base::Unretained(this)));
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription2_id);
+ UnsubscribePermissionStatusChange(subscription2_id);
}
TEST_F(PermissionManagerTest, SameTypeChangeNotifies) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -353,12 +436,12 @@ TEST_F(PermissionManagerTest, SameTypeChangeNotifies) {
EXPECT_TRUE(callback_called());
EXPECT_EQ(PermissionStatus::GRANTED, callback_result());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, DifferentTypeChangeDoesNotNotify) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -369,18 +452,18 @@ TEST_F(PermissionManagerTest, DifferentTypeChangeDoesNotNotify) {
EXPECT_FALSE(callback_called());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, ChangeAfterUnsubscribeDoesNotNotify) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
base::Unretained(this)));
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
GetHostContentSettingsMap()->SetContentSettingDefaultScope(
url(), url(), ContentSettingsType::GEOLOCATION, CONTENT_SETTING_ALLOW);
@@ -391,19 +474,19 @@ TEST_F(PermissionManagerTest, ChangeAfterUnsubscribeDoesNotNotify) {
TEST_F(PermissionManagerTest,
ChangeAfterUnsubscribeOnlyNotifiesActiveSubscribers) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
base::Unretained(this)));
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr, main_rfh(),
url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
base::Unretained(this)));
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
GetHostContentSettingsMap()->SetContentSettingDefaultScope(
url(), url(), ContentSettingsType::GEOLOCATION, CONTENT_SETTING_ALLOW);
@@ -413,7 +496,7 @@ TEST_F(PermissionManagerTest,
TEST_F(PermissionManagerTest, DifferentPrimaryUrlDoesNotNotify) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -425,12 +508,12 @@ TEST_F(PermissionManagerTest, DifferentPrimaryUrlDoesNotNotify) {
EXPECT_FALSE(callback_called());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, DifferentSecondaryUrlDoesNotNotify) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::STORAGE_ACCESS_GRANT, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -442,12 +525,12 @@ TEST_F(PermissionManagerTest, DifferentSecondaryUrlDoesNotNotify) {
EXPECT_FALSE(callback_called());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, WildCardPatternNotifies) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -459,7 +542,7 @@ TEST_F(PermissionManagerTest, WildCardPatternNotifies) {
EXPECT_TRUE(callback_called());
EXPECT_EQ(PermissionStatus::GRANTED, callback_result());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, ClearSettingsNotifies) {
@@ -467,7 +550,7 @@ TEST_F(PermissionManagerTest, ClearSettingsNotifies) {
url(), url(), ContentSettingsType::GEOLOCATION, CONTENT_SETTING_ALLOW);
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -479,12 +562,12 @@ TEST_F(PermissionManagerTest, ClearSettingsNotifies) {
EXPECT_TRUE(callback_called());
EXPECT_EQ(PermissionStatus::ASK, callback_result());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, NewValueCorrectlyPassed) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -496,7 +579,7 @@ TEST_F(PermissionManagerTest, NewValueCorrectlyPassed) {
EXPECT_TRUE(callback_called());
EXPECT_EQ(PermissionStatus::DENIED, callback_result());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, ChangeWithoutPermissionChangeDoesNotNotify) {
@@ -504,7 +587,7 @@ TEST_F(PermissionManagerTest, ChangeWithoutPermissionChangeDoesNotNotify) {
url(), url(), ContentSettingsType::GEOLOCATION, CONTENT_SETTING_ALLOW);
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -515,7 +598,7 @@ TEST_F(PermissionManagerTest, ChangeWithoutPermissionChangeDoesNotNotify) {
EXPECT_FALSE(callback_called());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, ChangesBackAndForth) {
@@ -523,7 +606,7 @@ TEST_F(PermissionManagerTest, ChangesBackAndForth) {
url(), url(), ContentSettingsType::GEOLOCATION, CONTENT_SETTING_ASK);
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -543,7 +626,7 @@ TEST_F(PermissionManagerTest, ChangesBackAndForth) {
EXPECT_TRUE(callback_called());
EXPECT_EQ(PermissionStatus::ASK, callback_result());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, ChangesBackAndForthWorker) {
@@ -551,7 +634,7 @@ TEST_F(PermissionManagerTest, ChangesBackAndForthWorker) {
url(), url(), ContentSettingsType::GEOLOCATION, CONTENT_SETTING_ASK);
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, process(), /*render_frame_host=*/nullptr,
url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -571,12 +654,12 @@ TEST_F(PermissionManagerTest, ChangesBackAndForthWorker) {
EXPECT_TRUE(callback_called());
EXPECT_EQ(PermissionStatus::ASK, callback_result());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, SubscribeMIDIPermission) {
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::MIDI, /*render_process_host=*/nullptr, main_rfh(),
url(),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -589,7 +672,7 @@ TEST_F(PermissionManagerTest, SubscribeMIDIPermission) {
EXPECT_FALSE(callback_called());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, PermissionIgnoredCleanup) {
@@ -601,10 +684,8 @@ TEST_F(PermissionManagerTest, PermissionIgnoredCleanup) {
NavigateAndCommit(url());
- GetPermissionManager()->RequestPermission(
- PermissionType::GEOLOCATION, main_rfh(), url(), /*user_gesture=*/true,
- base::BindOnce(&PermissionManagerTest::OnPermissionChange,
- base::Unretained(this)));
+ RequestPermissionFromCurrentDocumentNonBlocking(PermissionType::GEOLOCATION,
+ main_rfh());
EXPECT_FALSE(PendingRequestsEmpty());
@@ -620,9 +701,10 @@ TEST_F(PermissionManagerTest, InsecureOrigin) {
GURL insecure_frame("http://www.example.com/geolocation");
NavigateAndCommit(insecure_frame);
- PermissionResult result = GetPermissionManager()->GetPermissionStatusForFrame(
- ContentSettingsType::GEOLOCATION, web_contents()->GetMainFrame(),
- insecure_frame);
+ PermissionResult result =
+ GetPermissionManager()->GetPermissionStatusForCurrentDocument(
+ ContentSettingsType::GEOLOCATION,
+ web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
EXPECT_EQ(PermissionStatusSource::INSECURE_ORIGIN, result.source);
@@ -630,9 +712,8 @@ TEST_F(PermissionManagerTest, InsecureOrigin) {
GURL secure_frame("https://www.example.com/geolocation");
NavigateAndCommit(secure_frame);
- result = GetPermissionManager()->GetPermissionStatusForFrame(
- ContentSettingsType::GEOLOCATION, web_contents()->GetMainFrame(),
- secure_frame);
+ result = GetPermissionManager()->GetPermissionStatusForCurrentDocument(
+ ContentSettingsType::GEOLOCATION, web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
@@ -643,28 +724,28 @@ TEST_F(PermissionManagerTest, InsecureOriginIsNotOverridable) {
url::Origin::Create(GURL("http://example.com/geolocation"));
const url::Origin kSecureOrigin =
url::Origin::Create(GURL("https://example.com/geolocation"));
- EXPECT_FALSE(GetPermissionManager()->IsPermissionOverridableByDevTools(
- PermissionType::GEOLOCATION, kInsecureOrigin));
- EXPECT_TRUE(GetPermissionManager()->IsPermissionOverridableByDevTools(
- PermissionType::GEOLOCATION, kSecureOrigin));
+ EXPECT_FALSE(IsPermissionOverridableByDevTools(PermissionType::GEOLOCATION,
+ kInsecureOrigin));
+ EXPECT_TRUE(IsPermissionOverridableByDevTools(PermissionType::GEOLOCATION,
+ kSecureOrigin));
}
TEST_F(PermissionManagerTest, MissingContextIsNotOverridable) {
// Permissions that are not implemented should be denied overridability.
#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
- EXPECT_FALSE(GetPermissionManager()->IsPermissionOverridableByDevTools(
+ EXPECT_FALSE(IsPermissionOverridableByDevTools(
PermissionType::PROTECTED_MEDIA_IDENTIFIER,
url::Origin::Create(GURL("http://localhost"))));
#endif
- EXPECT_TRUE(GetPermissionManager()->IsPermissionOverridableByDevTools(
+ EXPECT_TRUE(IsPermissionOverridableByDevTools(
PermissionType::MIDI_SYSEX,
url::Origin::Create(GURL("http://localhost"))));
}
TEST_F(PermissionManagerTest, KillSwitchOnIsNotOverridable) {
const url::Origin kLocalHost = url::Origin::Create(GURL("http://localhost"));
- EXPECT_TRUE(GetPermissionManager()->IsPermissionOverridableByDevTools(
- PermissionType::GEOLOCATION, kLocalHost));
+ EXPECT_TRUE(IsPermissionOverridableByDevTools(PermissionType::GEOLOCATION,
+ kLocalHost));
// Turn on kill switch for GEOLOCATION.
std::map<std::string, std::string> params;
@@ -677,8 +758,8 @@ TEST_F(PermissionManagerTest, KillSwitchOnIsNotOverridable) {
base::FieldTrialList::CreateFieldTrial(
PermissionContextBase::kPermissionsKillSwitchFieldStudy, "TestGroup");
- EXPECT_FALSE(GetPermissionManager()->IsPermissionOverridableByDevTools(
- PermissionType::GEOLOCATION, kLocalHost));
+ EXPECT_FALSE(IsPermissionOverridableByDevTools(PermissionType::GEOLOCATION,
+ kLocalHost));
}
TEST_F(PermissionManagerTest, GetPermissionStatusDelegation) {
@@ -691,26 +772,17 @@ TEST_F(PermissionManagerTest, GetPermissionStatusDelegation) {
content::RenderFrameHost* child = AddChildRFH(parent, GURL(kOrigin2));
// By default the parent should be able to request access, but not the child.
- EXPECT_EQ(CONTENT_SETTING_ASK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- parent, GURL(kOrigin1))
- .content_setting);
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- child, GURL(kOrigin2))
- .content_setting);
+ EXPECT_EQ(PermissionStatus::ASK, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, parent));
+ EXPECT_EQ(PermissionStatus::DENIED, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
// Enabling geolocation by FP should allow the child to request access also.
child = AddChildRFH(parent, GURL(kOrigin2),
- blink::mojom::PermissionsPolicyFeature::kGeolocation);
+ PermissionsPolicyFeature::kGeolocation);
- EXPECT_EQ(CONTENT_SETTING_ASK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- child, GURL(kOrigin2))
- .content_setting);
+ EXPECT_EQ(PermissionStatus::ASK, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
// When the child requests location a prompt should be displayed for the
// parent.
@@ -721,53 +793,34 @@ TEST_F(PermissionManagerTest, GetPermissionStatusDelegation) {
prompt_factory->set_response_type(PermissionRequestManager::ACCEPT_ALL);
prompt_factory->DocumentOnLoadCompletedInPrimaryMainFrame();
- RequestPermission(PermissionType::GEOLOCATION, child, GURL(kOrigin2));
+ RequestPermissionFromCurrentDocument(PermissionType::GEOLOCATION, child);
EXPECT_TRUE(prompt_factory->RequestOriginSeen(GURL(kOrigin1)));
// Now the child frame should have location, as well as the parent frame.
- EXPECT_EQ(CONTENT_SETTING_ALLOW,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- parent, GURL(kOrigin1))
- .content_setting);
- EXPECT_EQ(CONTENT_SETTING_ALLOW,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- child, GURL(kOrigin2))
- .content_setting);
+ EXPECT_EQ(PermissionStatus::GRANTED,
+ GetPermissionStatusForCurrentDocument(PermissionType::GEOLOCATION,
+ parent));
+ EXPECT_EQ(PermissionStatus::GRANTED, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
// Revoking access from the parent should cause the child not to have access
// either.
- GetPermissionManager()->ResetPermission(PermissionType::GEOLOCATION,
- GURL(kOrigin1), GURL(kOrigin1));
- EXPECT_EQ(CONTENT_SETTING_ASK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- parent, GURL(kOrigin1))
- .content_setting);
- EXPECT_EQ(CONTENT_SETTING_ASK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- child, GURL(kOrigin2))
- .content_setting);
+ ResetPermission(PermissionType::GEOLOCATION, GURL(kOrigin1), GURL(kOrigin1));
+ EXPECT_EQ(PermissionStatus::ASK, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, parent));
+ EXPECT_EQ(PermissionStatus::ASK, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
// If the parent changes its policy, the child should be blocked.
- RefreshPageAndSetHeaderPolicy(
- &parent, blink::mojom::PermissionsPolicyFeature::kGeolocation,
- {kOrigin1});
+ RefreshPageAndSetHeaderPolicy(&parent, PermissionsPolicyFeature::kGeolocation,
+ {kOrigin1});
child = AddChildRFH(parent, GURL(kOrigin2));
- EXPECT_EQ(CONTENT_SETTING_ASK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- parent, GURL(kOrigin1))
- .content_setting);
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- child, GURL(kOrigin2))
- .content_setting);
+ EXPECT_EQ(PermissionStatus::ASK, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, parent));
+ EXPECT_EQ(PermissionStatus::DENIED, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
prompt_factory.reset();
}
@@ -781,7 +834,7 @@ TEST_F(PermissionManagerTest, SubscribeWithPermissionDelegation) {
content::RenderFrameHost* child = AddChildRFH(parent, GURL(kOrigin2));
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr, child,
GURL(kOrigin2),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -789,36 +842,27 @@ TEST_F(PermissionManagerTest, SubscribeWithPermissionDelegation) {
EXPECT_FALSE(callback_called());
// Location should be blocked for the child because it's not delegated.
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- child, GURL(kOrigin2))
- .content_setting);
+ EXPECT_EQ(PermissionStatus::DENIED, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
// Allow access for the top level origin.
GetHostContentSettingsMap()->SetContentSettingDefaultScope(
url(), url(), ContentSettingsType::GEOLOCATION, CONTENT_SETTING_ALLOW);
// The child's permission should still be block and no callback should be run.
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- child, GURL(kOrigin2))
- .content_setting);
+ EXPECT_EQ(PermissionStatus::DENIED, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
EXPECT_FALSE(callback_called());
// Enabling geolocation by FP should allow the child to request access also.
child = AddChildRFH(parent, GURL(kOrigin2),
- blink::mojom::PermissionsPolicyFeature::kGeolocation);
+ PermissionsPolicyFeature::kGeolocation);
- EXPECT_EQ(CONTENT_SETTING_ALLOW,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- child, GURL(kOrigin2))
- .content_setting);
+ EXPECT_EQ(PermissionStatus::GRANTED, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
- subscription_id = GetPermissionManager()->SubscribePermissionStatusChange(
+ subscription_id = SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr, child,
GURL(kOrigin2),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -833,13 +877,10 @@ TEST_F(PermissionManagerTest, SubscribeWithPermissionDelegation) {
EXPECT_TRUE(callback_called());
EXPECT_EQ(PermissionStatus::DENIED, callback_result());
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(ContentSettingsType::GEOLOCATION,
- child, GURL(kOrigin2))
- .content_setting);
+ EXPECT_EQ(PermissionStatus::DENIED, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
}
TEST_F(PermissionManagerTest, SubscribeUnsubscribeAndResubscribe) {
@@ -847,7 +888,7 @@ TEST_F(PermissionManagerTest, SubscribeUnsubscribeAndResubscribe) {
NavigateAndCommit(GURL(kOrigin1));
content::PermissionControllerDelegate::SubscriptionId subscription_id =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), GURL(kOrigin1),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -860,7 +901,7 @@ TEST_F(PermissionManagerTest, SubscribeUnsubscribeAndResubscribe) {
EXPECT_EQ(callback_count(), 1);
EXPECT_EQ(PermissionStatus::GRANTED, callback_result());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id);
+ UnsubscribePermissionStatusChange(subscription_id);
// ensure no callbacks are received when unsubscribed.
GetHostContentSettingsMap()->SetContentSettingDefaultScope(
@@ -871,7 +912,7 @@ TEST_F(PermissionManagerTest, SubscribeUnsubscribeAndResubscribe) {
EXPECT_EQ(callback_count(), 1);
content::PermissionControllerDelegate::SubscriptionId subscription_id_2 =
- GetPermissionManager()->SubscribePermissionStatusChange(
+ SubscribePermissionStatusChange(
PermissionType::GEOLOCATION, /*render_process_host=*/nullptr,
main_rfh(), GURL(kOrigin1),
base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
@@ -884,7 +925,7 @@ TEST_F(PermissionManagerTest, SubscribeUnsubscribeAndResubscribe) {
EXPECT_EQ(callback_count(), 2);
EXPECT_EQ(PermissionStatus::DENIED, callback_result());
- GetPermissionManager()->UnsubscribePermissionStatusChange(subscription_id_2);
+ UnsubscribePermissionStatusChange(subscription_id_2);
}
TEST_F(PermissionManagerTest, GetCanonicalOrigin) {
@@ -924,77 +965,49 @@ TEST_F(PermissionManagerTest, RequestPermissionInDifferentStoragePartition) {
NavigateAndCommit(kOrigin);
content::RenderFrameHost* parent = main_rfh();
- content::RenderFrameHost* child = AddChildRFH(
- parent, kOrigin2, blink::mojom::PermissionsPolicyFeature::kGeolocation);
- content::RenderFrameHost* partitioned_child =
- AddChildRFH(parent, kPartitionedOrigin,
- blink::mojom::PermissionsPolicyFeature::kGeolocation);
+ content::RenderFrameHost* child =
+ AddChildRFH(parent, kOrigin2, PermissionsPolicyFeature::kGeolocation);
+ content::RenderFrameHost* partitioned_child = AddChildRFH(
+ parent, kPartitionedOrigin, PermissionsPolicyFeature::kGeolocation);
// The parent should have geolocation access which is delegated to child and
// partitioned_child.
- EXPECT_EQ(CONTENT_SETTING_ALLOW,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(
- ContentSettingsType::GEOLOCATION, parent,
- parent->GetLastCommittedOrigin().GetURL())
- .content_setting);
- EXPECT_EQ(CONTENT_SETTING_ALLOW,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(
- ContentSettingsType::GEOLOCATION, child,
- child->GetLastCommittedOrigin().GetURL())
- .content_setting);
- EXPECT_EQ(CONTENT_SETTING_ALLOW,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(
- ContentSettingsType::GEOLOCATION, partitioned_child,
- partitioned_child->GetLastCommittedOrigin().GetURL())
- .content_setting);
+ EXPECT_EQ(PermissionStatus::GRANTED,
+ GetPermissionStatusForCurrentDocument(PermissionType::GEOLOCATION,
+ parent));
+ EXPECT_EQ(PermissionStatus::GRANTED, GetPermissionStatusForCurrentDocument(
+ PermissionType::GEOLOCATION, child));
+ EXPECT_EQ(PermissionStatus::GRANTED,
+ GetPermissionStatusForCurrentDocument(PermissionType::GEOLOCATION,
+ partitioned_child));
// The parent should not have notification permission.
- EXPECT_EQ(CONTENT_SETTING_ASK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(
- ContentSettingsType::NOTIFICATIONS, parent,
- parent->GetLastCommittedOrigin().GetURL())
- .content_setting);
- EXPECT_EQ(CONTENT_SETTING_ASK,
- GetPermissionManager()
- ->GetPermissionStatusForWorker(
- ContentSettingsType::NOTIFICATIONS, parent->GetProcess(),
- parent->GetLastCommittedOrigin())
- .content_setting);
+ EXPECT_EQ(PermissionStatus::ASK, GetPermissionStatusForCurrentDocument(
+ PermissionType::NOTIFICATIONS, parent));
+ EXPECT_EQ(PermissionStatus::ASK,
+ GetPermissionStatusForWorker(
+ PermissionType::NOTIFICATIONS, parent->GetProcess(),
+ parent->GetLastCommittedOrigin().GetURL()));
// The non-partitioned child should have notification permission.
- EXPECT_EQ(CONTENT_SETTING_ALLOW,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(
- ContentSettingsType::NOTIFICATIONS, child,
- child->GetLastCommittedOrigin().GetURL())
- .content_setting);
- EXPECT_EQ(CONTENT_SETTING_ALLOW,
- GetPermissionManager()
- ->GetPermissionStatusForWorker(
- ContentSettingsType::NOTIFICATIONS, child->GetProcess(),
- child->GetLastCommittedOrigin())
- .content_setting);
+ EXPECT_EQ(PermissionStatus::GRANTED,
+ GetPermissionStatusForCurrentDocument(PermissionType::NOTIFICATIONS,
+ child));
+ EXPECT_EQ(PermissionStatus::GRANTED,
+ GetPermissionStatusForWorker(
+ PermissionType::NOTIFICATIONS, child->GetProcess(),
+ child->GetLastCommittedOrigin().GetURL()));
// The partitioned child should not have notification permission because it
// belongs to a different StoragePartition, even though its origin would have
// permission if loaded in a main frame.
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
- GetPermissionManager()
- ->GetPermissionStatusForFrame(
- ContentSettingsType::NOTIFICATIONS, partitioned_child,
- partitioned_child->GetLastCommittedOrigin().GetURL())
- .content_setting);
- EXPECT_EQ(CONTENT_SETTING_BLOCK,
- GetPermissionManager()
- ->GetPermissionStatusForWorker(
- ContentSettingsType::NOTIFICATIONS,
- partitioned_child->GetProcess(),
- partitioned_child->GetLastCommittedOrigin())
- .content_setting);
+ EXPECT_EQ(PermissionStatus::DENIED,
+ GetPermissionStatusForCurrentDocument(PermissionType::NOTIFICATIONS,
+ partitioned_child));
+ EXPECT_EQ(PermissionStatus::DENIED,
+ GetPermissionStatusForWorker(
+ PermissionType::NOTIFICATIONS, partitioned_child->GetProcess(),
+ partitioned_child->GetLastCommittedOrigin().GetURL()));
}
} // namespace permissions
diff --git a/chromium/components/permissions/permission_prompt.h b/chromium/components/permissions/permission_prompt.h
index e657a069928..26c490ce5ee 100644
--- a/chromium/components/permissions/permission_prompt.h
+++ b/chromium/components/permissions/permission_prompt.h
@@ -99,6 +99,8 @@ class PermissionPrompt {
// Set when the user made any decision for clicking on learn more link.
virtual void SetLearnMoreClicked() = 0;
+
+ virtual base::WeakPtr<Delegate> GetWeakPtr() = 0;
};
typedef base::RepeatingCallback<
diff --git a/chromium/components/permissions/permission_request_manager.cc b/chromium/components/permissions/permission_request_manager.cc
index e7fe078d259..71e22bb3f6c 100644
--- a/chromium/components/permissions/permission_request_manager.cc
+++ b/chromium/components/permissions/permission_request_manager.cc
@@ -112,10 +112,6 @@ bool IsMediaRequest(RequestType type) {
return type == RequestType::kMicStream || type == RequestType::kCameraStream;
}
-bool IsArOrCameraRequest(RequestType type) {
- return type == RequestType::kArSession || type == RequestType::kCameraStream;
-}
-
bool ShouldGroupRequests(PermissionRequest* a, PermissionRequest* b) {
if (a->requesting_origin() != b->requesting_origin())
return false;
@@ -125,12 +121,6 @@ bool ShouldGroupRequests(PermissionRequest* a, PermissionRequest* b) {
return true;
}
- // Group if the requests are an AR and a Camera Access request.
- if (IsArOrCameraRequest(a->request_type()) &&
- IsArOrCameraRequest(b->request_type())) {
- return true;
- }
-
return false;
}
@@ -227,7 +217,7 @@ void PermissionRequestManager::AddRequest(
// any other renderer-side nav initiations?). Double-check this for
// correct behavior on interstitials -- we probably want to basically queue
// any request for which GetVisibleURL != GetLastCommittedURL.
- CHECK_EQ(source_frame->GetMainFrame(), web_contents()->GetMainFrame());
+ CHECK_EQ(source_frame->GetMainFrame(), web_contents()->GetPrimaryMainFrame());
const GURL main_frame_origin =
PermissionUtil::GetLastCommittedOriginAsURL(source_frame->GetMainFrame());
bool is_main_frame =
@@ -487,7 +477,7 @@ GURL PermissionRequestManager::GetRequestingOrigin() const {
GURL PermissionRequestManager::GetEmbeddingOrigin() const {
return PermissionUtil::GetLastCommittedOriginAsURL(
- web_contents()->GetMainFrame());
+ web_contents()->GetPrimaryMainFrame());
}
void PermissionRequestManager::Accept() {
@@ -591,6 +581,11 @@ void PermissionRequestManager::SetLearnMoreClicked() {
set_learn_more_clicked();
}
+base::WeakPtr<PermissionPrompt::Delegate>
+PermissionRequestManager::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
PermissionRequestManager::PermissionRequestManager(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
@@ -1080,7 +1075,7 @@ PermissionRequestManager::DetermineCurrentRequestUIDispositionReasonForUMA() {
}
void PermissionRequestManager::LogWarningToConsole(const char* message) {
- web_contents()->GetMainFrame()->AddMessageToConsole(
+ web_contents()->GetPrimaryMainFrame()->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kWarning, message);
}
diff --git a/chromium/components/permissions/permission_request_manager.h b/chromium/components/permissions/permission_request_manager.h
index 4dd36213151..f6f94e01b62 100644
--- a/chromium/components/permissions/permission_request_manager.h
+++ b/chromium/components/permissions/permission_request_manager.h
@@ -150,6 +150,7 @@ class PermissionRequestManager
void SetDecisionTime() override;
void SetManageClicked() override;
void SetLearnMoreClicked() override;
+ base::WeakPtr<PermissionPrompt::Delegate> GetWeakPtr() override;
void set_manage_clicked() { did_click_manage_ = true; }
void set_learn_more_clicked() { did_click_learn_more_ = true; }
diff --git a/chromium/components/permissions/permission_request_manager_unittest.cc b/chromium/components/permissions/permission_request_manager_unittest.cc
index f9bb25b178b..49d4a55140f 100644
--- a/chromium/components/permissions/permission_request_manager_unittest.cc
+++ b/chromium/components/permissions/permission_request_manager_unittest.cc
@@ -58,8 +58,16 @@ class PermissionRequestManagerTest
RequestType::kStorageAccess),
iframe_request_mic_other_domain_(GURL("https://www.youtube.com"),
RequestType::kMicStream) {
- feature_list_.InitWithFeatureState(permissions::features::kPermissionChip,
- GetParam());
+
+ if (GetParam()) {
+ feature_list_.InitWithFeatures(
+ {permissions::features::kPermissionChip},
+ {permissions::features::kPermissionQuietChip});
+ } else {
+ feature_list_.InitWithFeatures(
+ {}, {permissions::features::kPermissionChip,
+ permissions::features::kPermissionQuietChip});
+ }
}
void SetUp() override {
@@ -124,7 +132,7 @@ class PermissionRequestManagerTest
std::unique_ptr<MockPermissionRequest> request =
std::make_unique<MockPermissionRequest>(
type, PermissionRequestGestureType::GESTURE);
- manager_->AddRequest(web_contents()->GetMainFrame(), request.get());
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), request.get());
WaitForBubbleToBeShown();
if (should_be_seen) {
EXPECT_TRUE(prompt_factory_->RequestTypeSeen(type));
@@ -175,7 +183,7 @@ TEST_P(PermissionRequestManagerTest, NoRequests) {
}
TEST_P(PermissionRequestManagerTest, SingleRequest) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -186,7 +194,7 @@ TEST_P(PermissionRequestManagerTest, SingleRequest) {
}
TEST_P(PermissionRequestManagerTest, SequentialRequests) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -194,7 +202,7 @@ TEST_P(PermissionRequestManagerTest, SequentialRequests) {
EXPECT_TRUE(request1_.granted());
EXPECT_FALSE(prompt_factory_->is_visible());
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
Accept();
@@ -203,9 +211,9 @@ TEST_P(PermissionRequestManagerTest, SequentialRequests) {
}
TEST_P(PermissionRequestManagerTest, ForgetRequestsOnPageNavigation) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_other_domain_);
WaitForBubbleToBeShown();
@@ -224,24 +232,24 @@ TEST_P(PermissionRequestManagerTest, ForgetRequestsOnPageNavigation) {
TEST_P(PermissionRequestManagerTest, RequestsDontNeedUserGesture) {
WaitForFrameLoad();
WaitForBubbleToBeShown();
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_other_domain_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2_);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(prompt_factory_->is_visible());
}
TEST_P(PermissionRequestManagerTest, RequestsNotSupported) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
Accept();
EXPECT_TRUE(request1_.granted());
manager_->set_web_contents_supports_permission_requests(false);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2_);
EXPECT_TRUE(request2_.cancelled());
}
@@ -255,8 +263,8 @@ TEST_P(PermissionRequestManagerTest, TwoRequestsUngrouped) {
if (GetParam())
return;
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -278,9 +286,9 @@ TEST_P(PermissionRequestManagerTest, ThreeRequestsStackOrderChip) {
return;
// Test new permissions order, requests shouldn't be grouped.
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_mic_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -309,13 +317,13 @@ TEST_P(PermissionRequestManagerTest, ThreeRequestsOneByOneStackOrderChip) {
if (!GetParam())
return;
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2_);
WaitForBubbleToBeShown();
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_mic_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -341,8 +349,8 @@ TEST_P(PermissionRequestManagerTest, ThreeRequestsOneByOneStackOrderChip) {
// Only mic/camera requests from the same origin should be grouped.
TEST_P(PermissionRequestManagerTest, MicCameraGrouped) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_mic_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_camera_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -356,9 +364,9 @@ TEST_P(PermissionRequestManagerTest, MicCameraGrouped) {
// If mic/camera requests come from different origins, they should not be
// grouped.
TEST_P(PermissionRequestManagerTest, MicCameraDifferentOrigins) {
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_mic_other_domain_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_camera_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -368,8 +376,8 @@ TEST_P(PermissionRequestManagerTest, MicCameraDifferentOrigins) {
#if !BUILDFLAG(IS_ANDROID)
// Only camera/ptz requests from the same origin should be grouped.
TEST_P(PermissionRequestManagerTest, CameraPtzGrouped) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_ptz_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_camera_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_ptz_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -383,9 +391,9 @@ TEST_P(PermissionRequestManagerTest, CameraPtzGrouped) {
TEST_P(PermissionRequestManagerTest, CameraPtzDifferentOrigins) {
// If camera/ptz requests come from different origins, they should not be
// grouped.
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_camera_other_domain_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_ptz_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_ptz_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -394,9 +402,9 @@ TEST_P(PermissionRequestManagerTest, CameraPtzDifferentOrigins) {
// Only mic/camera/ptz requests from the same origin should be grouped.
TEST_P(PermissionRequestManagerTest, MicCameraPtzGrouped) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_ptz_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_mic_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_camera_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_ptz_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -411,10 +419,10 @@ TEST_P(PermissionRequestManagerTest, MicCameraPtzGrouped) {
// If mic/camera/ptz requests come from different origins, they should not be
// grouped.
TEST_P(PermissionRequestManagerTest, MicCameraPtzDifferentOrigins) {
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_mic_other_domain_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_ptz_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_camera_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_ptz_);
WaitForBubbleToBeShown();
// Requests should be split into two groups and each one will contain less
@@ -431,9 +439,9 @@ TEST_P(PermissionRequestManagerTest, MicCameraPtzDifferentOrigins) {
// Tests mix of grouped media requests and non-groupable request.
TEST_P(PermissionRequestManagerTest, MixOfMediaAndNotMediaRequests) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_camera_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_mic_);
WaitForBubbleToBeShown();
// Requests should be split into two groups and each one will contain less
@@ -454,8 +462,8 @@ TEST_P(PermissionRequestManagerTest, MixOfMediaAndNotMediaRequests) {
////////////////////////////////////////////////////////////////////////////////
TEST_P(PermissionRequestManagerTest, TwoRequestsTabSwitch) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_mic_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_camera_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -480,7 +488,7 @@ TEST_P(PermissionRequestManagerTest, TwoRequestsTabSwitch) {
TEST_P(PermissionRequestManagerTest, PermissionRequestWhileTabSwitchedAway) {
MockTabSwitchAway();
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
EXPECT_FALSE(prompt_factory_->is_visible());
@@ -494,8 +502,8 @@ TEST_P(PermissionRequestManagerTest, PermissionRequestWhileTabSwitchedAway) {
////////////////////////////////////////////////////////////////////////////////
TEST_P(PermissionRequestManagerTest, SameRequestRejected) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
EXPECT_FALSE(request1_.finished());
WaitForBubbleToBeShown();
@@ -508,17 +516,19 @@ TEST_P(PermissionRequestManagerTest, SameRequestRejected) {
}
TEST_P(PermissionRequestManagerTest, DuplicateRequest) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2_);
auto dupe_request = request1_.CreateDuplicateRequest();
- manager_->AddRequest(web_contents()->GetMainFrame(), dupe_request.get());
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
+ dupe_request.get());
EXPECT_FALSE(dupe_request->finished());
EXPECT_FALSE(request1_.finished());
auto dupe_request2 = request2_.CreateDuplicateRequest();
- manager_->AddRequest(web_contents()->GetMainFrame(), dupe_request2.get());
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
+ dupe_request2.get());
EXPECT_FALSE(dupe_request2->finished());
EXPECT_FALSE(request2_.finished());
@@ -548,7 +558,7 @@ TEST_P(PermissionRequestManagerTest, DuplicateRequest) {
////////////////////////////////////////////////////////////////////////////////
TEST_P(PermissionRequestManagerTest, MainFrameNoRequestIFrameRequest) {
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_same_domain_);
WaitForBubbleToBeShown();
WaitForFrameLoad();
@@ -559,8 +569,8 @@ TEST_P(PermissionRequestManagerTest, MainFrameNoRequestIFrameRequest) {
}
TEST_P(PermissionRequestManagerTest, MainFrameAndIFrameRequestSameDomain) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_same_domain_);
WaitForFrameLoad();
WaitForBubbleToBeShown();
@@ -589,8 +599,8 @@ TEST_P(PermissionRequestManagerTest, MainFrameAndIFrameRequestSameDomain) {
}
TEST_P(PermissionRequestManagerTest, MainFrameAndIFrameRequestOtherDomain) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_other_domain_);
WaitForFrameLoad();
WaitForBubbleToBeShown();
@@ -615,11 +625,11 @@ TEST_P(PermissionRequestManagerTest, MainFrameAndIFrameRequestOtherDomain) {
}
TEST_P(PermissionRequestManagerTest, IFrameRequestWhenMainRequestVisible) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_same_domain_);
WaitForFrameLoad();
ASSERT_EQ(prompt_factory_->request_count(), 1);
@@ -644,11 +654,11 @@ TEST_P(PermissionRequestManagerTest, IFrameRequestWhenMainRequestVisible) {
TEST_P(PermissionRequestManagerTest,
IFrameRequestOtherDomainWhenMainRequestVisible) {
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
- manager_->AddRequest(web_contents()->GetMainFrame(),
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
&iframe_request_other_domain_);
WaitForFrameLoad();
Closing();
@@ -678,7 +688,7 @@ TEST_P(PermissionRequestManagerTest,
TEST_P(PermissionRequestManagerTest, UMAForSimpleDeniedBubbleAlternatePath) {
base::HistogramTester histograms;
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
// No need to test UMA for showing prompts again, they were tested in
// UMAForSimpleAcceptedBubble.
@@ -693,7 +703,7 @@ TEST_P(PermissionRequestManagerTest, UMAForSimpleDeniedBubbleAlternatePath) {
TEST_P(PermissionRequestManagerTest, UMAForTabSwitching) {
base::HistogramTester histograms;
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_);
WaitForBubbleToBeShown();
histograms.ExpectUniqueSample(PermissionUmaUtil::kPermissionsPromptShown,
static_cast<base::HistogramBase::Sample>(
@@ -801,7 +811,7 @@ TEST_P(PermissionRequestManagerTest,
manager_, PermissionUiSelector::QuietUiReason::kEnabledInPrefs,
false /* async */);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_camera_);
WaitForBubbleToBeShown();
ASSERT_TRUE(prompt_factory_->is_visible());
@@ -831,7 +841,7 @@ TEST_P(PermissionRequestManagerTest, UiSelectorUsedForNotifications) {
MockPermissionRequest request(RequestType::kNotifications,
PermissionRequestGestureType::GESTURE);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -851,7 +861,7 @@ TEST_P(PermissionRequestManagerTest,
manager_, QuietUiReason::kEnabledInPrefs, true);
MockPermissionRequest request1(RequestType::kNotifications,
PermissionRequestGestureType::GESTURE);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request1);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1);
WaitForBubbleToBeShown();
EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
@@ -861,7 +871,7 @@ TEST_P(PermissionRequestManagerTest,
manager_->clear_permission_ui_selector_for_testing();
MockNotificationPermissionUiSelector::CreateForManager(
manager_, PermissionUiSelector::Decision::UseNormalUi(), true);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2);
WaitForBubbleToBeShown();
EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
@@ -923,7 +933,7 @@ TEST_P(PermissionRequestManagerTest, MultipleUiSelectors) {
MockPermissionRequest request(RequestType::kNotifications,
PermissionRequestGestureType::GESTURE);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -980,7 +990,7 @@ TEST_P(PermissionRequestManagerTest, SelectorsPredictionLikelihood) {
MockPermissionRequest request(RequestType::kNotifications,
PermissionRequestGestureType::GESTURE);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
@@ -1008,7 +1018,7 @@ TEST_P(PermissionRequestManagerTest, SelectorRequestTypes) {
for (const auto& test : kTests) {
MockPermissionRequest request(test.request_type,
PermissionRequestGestureType::GESTURE);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request);
WaitForBubbleToBeShown();
EXPECT_EQ(test.should_request_use_quiet_ui,
manager_->ShouldCurrentRequestUseQuietUI());
@@ -1020,7 +1030,7 @@ TEST_P(PermissionRequestManagerTest, SelectorRequestTypes) {
// Now the RequestType::kCameraStream should show a quiet UI as well
MockPermissionRequest request2(RequestType::kCameraStream,
PermissionRequestGestureType::GESTURE);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request2);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2);
WaitForBubbleToBeShown();
EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
@@ -1034,7 +1044,7 @@ TEST_P(PermissionRequestManagerTest, NotificationsSingleBubbleAndChipRequest) {
MockPermissionRequest request(RequestType::kNotifications,
PermissionRequestGestureType::GESTURE);
- manager_->AddRequest(web_contents()->GetMainFrame(), &request);
+ manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
diff --git a/chromium/components/permissions/permission_uma_util.cc b/chromium/components/permissions/permission_uma_util.cc
index 5b3b205c765..1b503d0249b 100644
--- a/chromium/components/permissions/permission_uma_util.cc
+++ b/chromium/components/permissions/permission_uma_util.cc
@@ -20,13 +20,12 @@
#include "components/permissions/prediction_service/prediction_common.h"
#include "components/permissions/prediction_service/prediction_request_features.h"
#include "components/permissions/request_type.h"
-#include "components/ukm/content/source_url_recorder.h"
-#include "content/public/browser/permission_type.h"
#include "content/public/browser/web_contents.h"
#include "services/metrics/public/cpp/metrics_utils.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_ANDROID)
@@ -50,7 +49,7 @@ namespace permissions {
permission_bubble_type); \
}
-using content::PermissionType;
+using blink::PermissionType;
namespace {
@@ -778,7 +777,7 @@ PermissionUmaUtil::ScopedRevocationReporter::~ScopedRevocationReporter() {
void PermissionUmaUtil::RecordPermissionUsage(
ContentSettingsType permission_type,
content::BrowserContext* browser_context,
- const content::WebContents* web_contents,
+ content::WebContents* web_contents,
const GURL& requesting_origin) {
PermissionsClient::Get()->GetUkmSourceId(
browser_context, web_contents, requesting_origin,
@@ -794,7 +793,7 @@ void PermissionUmaUtil::RecordPermissionAction(
PermissionPromptDisposition ui_disposition,
absl::optional<PermissionPromptDispositionReason> ui_reason,
const GURL& requesting_origin,
- const content::WebContents* web_contents,
+ content::WebContents* web_contents,
content::BrowserContext* browser_context,
absl::optional<PredictionGrantLikelihood> predicted_grant_likelihood,
absl::optional<bool> prediction_decision_held_back) {
diff --git a/chromium/components/permissions/permission_uma_util.h b/chromium/components/permissions/permission_uma_util.h
index 357b5f8d3aa..eb95d1fd455 100644
--- a/chromium/components/permissions/permission_uma_util.h
+++ b/chromium/components/permissions/permission_uma_util.h
@@ -342,7 +342,7 @@ class PermissionUmaUtil {
static void RecordPermissionUsage(ContentSettingsType permission_type,
content::BrowserContext* browser_context,
- const content::WebContents* web_contents,
+ content::WebContents* web_contents,
const GURL& requesting_origin);
static void RecordTimeElapsedBetweenGrantAndUse(ContentSettingsType type,
@@ -430,7 +430,7 @@ class PermissionUmaUtil {
PermissionPromptDisposition ui_disposition,
absl::optional<PermissionPromptDispositionReason> ui_reason,
const GURL& requesting_origin,
- const content::WebContents* web_contents,
+ content::WebContents* web_contents,
content::BrowserContext* browser_context,
absl::optional<PredictionGrantLikelihood> predicted_grant_likelihood,
absl::optional<bool> prediction_decision_held_back);
diff --git a/chromium/components/permissions/permission_util.cc b/chromium/components/permissions/permission_util.cc
index a2c74e5f8d5..82fb2361ae8 100644
--- a/chromium/components/permissions/permission_util.cc
+++ b/chromium/components/permissions/permission_util.cc
@@ -10,14 +10,14 @@
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/permissions/features.h"
-#include "content/public/browser/permission_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "url/gurl.h"
#include "url/origin.h"
-using content::PermissionType;
+using blink::PermissionType;
namespace permissions {
@@ -191,7 +191,7 @@ bool PermissionUtil::IsPermission(ContentSettingsType type) {
case ContentSettingsType::MEDIASTREAM_CAMERA:
case ContentSettingsType::MEDIASTREAM_MIC:
case ContentSettingsType::BACKGROUND_SYNC:
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER:
#endif
case ContentSettingsType::SENSORS:
@@ -251,10 +251,10 @@ GURL PermissionUtil::GetLastCommittedOriginAsURL(
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
- // If `allow_universal_access_from_file_urls` flag is enabled, a file can
- // introduce discrepancy between GetLastCommittedURL and
- // GetLastCommittedOrigin. In that case GetLastCommittedURL should be used
- // for requesting and verifying permissions.
+ // If `allow_universal_access_from_file_urls` flag is enabled, a file:/// can
+ // change its url via history.pushState/replaceState to any other url,
+ // including about:blank. To avoid user confusion we should always use a
+ // visible url, in other words `GetLastCommittedURL`.
if (web_contents->GetOrCreateWebPreferences()
.allow_universal_access_from_file_urls &&
render_frame_host->GetLastCommittedOrigin().GetURL().SchemeIsFile()) {
@@ -264,4 +264,98 @@ GURL PermissionUtil::GetLastCommittedOriginAsURL(
return render_frame_host->GetLastCommittedOrigin().GetURL();
}
+ContentSettingsType PermissionUtil::PermissionTypeToContentSettingSafe(
+ PermissionType permission) {
+ switch (permission) {
+ case PermissionType::MIDI:
+ return ContentSettingsType::MIDI;
+ case PermissionType::MIDI_SYSEX:
+ return ContentSettingsType::MIDI_SYSEX;
+ case PermissionType::NOTIFICATIONS:
+ return ContentSettingsType::NOTIFICATIONS;
+ case PermissionType::GEOLOCATION:
+ return ContentSettingsType::GEOLOCATION;
+ case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
+ return ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER;
+#else
+ break;
+#endif
+ case PermissionType::DURABLE_STORAGE:
+ return ContentSettingsType::DURABLE_STORAGE;
+ case PermissionType::AUDIO_CAPTURE:
+ return ContentSettingsType::MEDIASTREAM_MIC;
+ case PermissionType::VIDEO_CAPTURE:
+ return ContentSettingsType::MEDIASTREAM_CAMERA;
+ case PermissionType::BACKGROUND_SYNC:
+ return ContentSettingsType::BACKGROUND_SYNC;
+ case PermissionType::SENSORS:
+ return ContentSettingsType::SENSORS;
+ case PermissionType::ACCESSIBILITY_EVENTS:
+ return ContentSettingsType::ACCESSIBILITY_EVENTS;
+ case PermissionType::CLIPBOARD_READ_WRITE:
+ return ContentSettingsType::CLIPBOARD_READ_WRITE;
+ case PermissionType::CLIPBOARD_SANITIZED_WRITE:
+ return ContentSettingsType::CLIPBOARD_SANITIZED_WRITE;
+ case PermissionType::PAYMENT_HANDLER:
+ return ContentSettingsType::PAYMENT_HANDLER;
+ case PermissionType::BACKGROUND_FETCH:
+ return ContentSettingsType::BACKGROUND_FETCH;
+ case PermissionType::IDLE_DETECTION:
+ return ContentSettingsType::IDLE_DETECTION;
+ case PermissionType::PERIODIC_BACKGROUND_SYNC:
+ return ContentSettingsType::PERIODIC_BACKGROUND_SYNC;
+ case PermissionType::WAKE_LOCK_SCREEN:
+ return ContentSettingsType::WAKE_LOCK_SCREEN;
+ case PermissionType::WAKE_LOCK_SYSTEM:
+ return ContentSettingsType::WAKE_LOCK_SYSTEM;
+ case PermissionType::NFC:
+ return ContentSettingsType::NFC;
+ case PermissionType::VR:
+ return ContentSettingsType::VR;
+ case PermissionType::AR:
+ return ContentSettingsType::AR;
+ case PermissionType::STORAGE_ACCESS_GRANT:
+ return ContentSettingsType::STORAGE_ACCESS;
+ case PermissionType::CAMERA_PAN_TILT_ZOOM:
+ return ContentSettingsType::CAMERA_PAN_TILT_ZOOM;
+ case PermissionType::WINDOW_PLACEMENT:
+ return ContentSettingsType::WINDOW_PLACEMENT;
+ case PermissionType::LOCAL_FONTS:
+ return ContentSettingsType::LOCAL_FONTS;
+ case PermissionType::DISPLAY_CAPTURE:
+ return ContentSettingsType::DISPLAY_CAPTURE;
+ case PermissionType::NUM:
+ break;
+ }
+
+ return ContentSettingsType::DEFAULT;
+}
+
+ContentSettingsType PermissionUtil::PermissionTypeToContentSetting(
+ PermissionType permission) {
+ ContentSettingsType content_setting =
+ PermissionTypeToContentSettingSafe(permission);
+ DCHECK_NE(content_setting, ContentSettingsType::DEFAULT)
+ << "Unknown content setting for permission "
+ << static_cast<int>(permission);
+ return content_setting;
+}
+
+ContentSetting PermissionUtil::PermissionStatusToContentSetting(
+ blink::mojom::PermissionStatus status) {
+ switch (status) {
+ case blink::mojom::PermissionStatus::GRANTED:
+ return CONTENT_SETTING_ALLOW;
+ case blink::mojom::PermissionStatus::ASK:
+ return CONTENT_SETTING_ASK;
+ case blink::mojom::PermissionStatus::DENIED:
+ default:
+ return CONTENT_SETTING_BLOCK;
+ }
+
+ NOTREACHED();
+ return CONTENT_SETTING_DEFAULT;
+}
+
} // namespace permissions
diff --git a/chromium/components/permissions/permission_util.h b/chromium/components/permissions/permission_util.h
index 4c1e97022dd..4a443626592 100644
--- a/chromium/components/permissions/permission_util.h
+++ b/chromium/components/permissions/permission_util.h
@@ -11,9 +11,13 @@
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/permissions/permission_request.h"
+#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
-namespace content {
+namespace blink {
enum class PermissionType;
+}
+
+namespace content {
class RenderFrameHost;
} // namespace content
@@ -54,7 +58,7 @@ class PermissionUtil {
// to remove the usage in PermissionUmaUtil, which uses PermissionType as a
// histogram value to count permission request metrics.
static bool GetPermissionType(ContentSettingsType type,
- content::PermissionType* out);
+ blink::PermissionType* out);
// Checks whether the given ContentSettingsType is a permission. Use this
// to determine whether a specific ContentSettingsType is supported by the
@@ -71,8 +75,23 @@ class PermissionUtil {
// Returns the authoritative `embedding origin`, as a GURL, to be used for
// permission decisions in `render_frame_host`.
+ // TODO(crbug.com/1327384): Remove this method when possible.
static GURL GetLastCommittedOriginAsURL(
content::RenderFrameHost* render_frame_host);
+
+ // Helper method to convert PermissionType to ContentSettingType.
+ // If PermissionType is not supported or found, returns
+ // ContentSettingsType::DEFAULT.
+ static ContentSettingsType PermissionTypeToContentSettingSafe(
+ blink::PermissionType permission);
+
+ // Helper method to convert PermissionType to ContentSettingType.
+ static ContentSettingsType PermissionTypeToContentSetting(
+ blink::PermissionType permission);
+
+ // Helper method to convert PermissionStatus to ContentSetting.
+ static ContentSetting PermissionStatusToContentSetting(
+ blink::mojom::PermissionStatus status);
};
} // namespace permissions
diff --git a/chromium/components/permissions/permissions_client.cc b/chromium/components/permissions/permissions_client.cc
index a8e604a4d1b..f7273857672 100644
--- a/chromium/components/permissions/permissions_client.cc
+++ b/chromium/components/permissions/permissions_client.cc
@@ -56,7 +56,7 @@ bool PermissionsClient::IsCookieDeletionDisabled(
#endif
void PermissionsClient::GetUkmSourceId(content::BrowserContext* browser_context,
- const content::WebContents* web_contents,
+ content::WebContents* web_contents,
const GURL& requesting_origin,
GetUkmSourceIdCallback callback) {
std::move(callback).Run(absl::nullopt);
diff --git a/chromium/components/permissions/permissions_client.h b/chromium/components/permissions/permissions_client.h
index 480e8e55612..3cd33e2c165 100644
--- a/chromium/components/permissions/permissions_client.h
+++ b/chromium/components/permissions/permissions_client.h
@@ -130,7 +130,7 @@ class PermissionsClient {
using GetUkmSourceIdCallback =
base::OnceCallback<void(absl::optional<ukm::SourceId>)>;
virtual void GetUkmSourceId(content::BrowserContext* browser_context,
- const content::WebContents* web_contents,
+ content::WebContents* web_contents,
const GURL& requesting_origin,
GetUkmSourceIdCallback callback);
diff --git a/chromium/components/permissions/prediction_service/prediction_model_handler.cc b/chromium/components/permissions/prediction_service/prediction_model_handler.cc
index bb7bd962955..a8f555b633c 100644
--- a/chromium/components/permissions/prediction_service/prediction_model_handler.cc
+++ b/chromium/components/permissions/prediction_service/prediction_model_handler.cc
@@ -28,6 +28,7 @@ PredictionModelHandler::PredictionModelHandler(
model_provider,
background_task_runner,
std::make_unique<PredictionModelExecutor>(),
+ /*model_inference_timeout=*/absl::nullopt,
optimization_guide::proto::OptimizationTarget::
OPTIMIZATION_TARGET_NOTIFICATION_PERMISSION_PREDICTIONS,
absl::nullopt) {}
diff --git a/chromium/components/permissions_strings.grdp b/chromium/components/permissions_strings.grdp
index ae25a6a47b1..89fcc650a87 100644
--- a/chromium/components/permissions_strings.grdp
+++ b/chromium/components/permissions_strings.grdp
@@ -24,12 +24,6 @@
<message name="IDS_AR_INFOBAR_TEXT" desc="Text requesting permission for a site to use AR">
<ph name="URL">$1<ex>google.com</ex></ph> wants to create a 3D map of your surroundings and track camera position
</message>
- <!-- TODO(https://bugs.chromium.org/p/chromium/issues/detail?id=1106874) -->
- <message name="IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT" desc="Text requesting permission for a site to use AR and the Camera Access Feature">
- <ph name="URL">$1<ex>google.com</ex></ph> wants to:
- • Create a 3D map of your surroundings and track camera position
- • Use your camera
- </message>
<message name="IDS_IDLE_DETECTION_INFOBAR_TEXT" desc="Text requesting permission for a site to know when the user is idle.">
<ph name="URL">$1<ex>google.com</ex></ph> wants to know when you're actively using this device
</message>
diff --git a/chromium/components/permissions_strings_grdp/IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT.png.sha1 b/chromium/components/permissions_strings_grdp/IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT.png.sha1
deleted file mode 100644
index 338e2afad5f..00000000000
--- a/chromium/components/permissions_strings_grdp/IDS_AR_AND_MEDIA_CAPTURE_VIDEO_INFOBAR_TEXT.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-190759b575d8b4da48fc1bdec0a14e94a34c648e \ No newline at end of file
diff --git a/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc b/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc
index dc6ff6bc92f..a3787ded29a 100644
--- a/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc
+++ b/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/json/string_escape.h"
+#include "base/metrics/user_metrics_action.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
diff --git a/chromium/components/plugins/renderer/plugin_placeholder.cc b/chromium/components/plugins/renderer/plugin_placeholder.cc
index ff67198c46c..5b6fcaa0020 100644
--- a/chromium/components/plugins/renderer/plugin_placeholder.cc
+++ b/chromium/components/plugins/renderer/plugin_placeholder.cc
@@ -4,6 +4,7 @@
#include "components/plugins/renderer/plugin_placeholder.h"
+#include "base/metrics/user_metrics_action.h"
#include "base/strings/string_util.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h"
diff --git a/chromium/components/policy/BUILD.gn b/chromium/components/policy/BUILD.gn
index 950db347458..ea36722998f 100644
--- a/chromium/components/policy/BUILD.gn
+++ b/chromium/components/policy/BUILD.gn
@@ -11,6 +11,7 @@ import("//build/toolchain/toolchain.gni")
import("//components/policy/resources/policy_templates.gni")
import("//third_party/libprotobuf-mutator/fuzzable_proto_library.gni")
import("//third_party/protobuf/proto_library.gni")
+import("//tools/grit/grit_args.gni")
import("//tools/grit/grit_rule.gni")
# To generate policy documentation for local use, set this to true for the
@@ -205,7 +206,7 @@ action("policy_templates") {
policy_templates_languages_str,
"--version_path",
chrome_version_path,
- ] + grit_defines
+ ] + grit_args
if (gen_policy_templates_local) {
args += [ "--local" ]
diff --git a/chromium/components/policy/ENTERPRISE_POLICY_OWNERS b/chromium/components/policy/ENTERPRISE_POLICY_OWNERS
index f85292d2051..7220ed56944 100644
--- a/chromium/components/policy/ENTERPRISE_POLICY_OWNERS
+++ b/chromium/components/policy/ENTERPRISE_POLICY_OWNERS
@@ -7,6 +7,7 @@ enterprise-policy-review@google.com
anqing@chromium.org
emaxx@chromium.org
hendrich@chromium.org
+igorcov@chromium.org
pastarmovj@chromium.org
pmarko@chromium.org
poromov@chromium.org
diff --git a/chromium/components/policy/android/BUILD.gn b/chromium/components/policy/android/BUILD.gn
index da1e407c09d..8447a8bec36 100644
--- a/chromium/components/policy/android/BUILD.gn
+++ b/chromium/components/policy/android/BUILD.gn
@@ -22,6 +22,8 @@ java_cpp_strings("java_switches_srcjar") {
android_library("policy_java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
srcjar_deps = [ ":java_switches_srcjar" ]
@@ -75,6 +77,7 @@ java_library("components_policy_junit_tests") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//build/android:build_java",
"//third_party/android_deps:robolectric_all_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/hamcrest:hamcrest_java",
@@ -93,7 +96,7 @@ android_library("native_test_support_java") {
testonly = true
deps = [
":policy_java",
- "//base:base_java",
+ "//base:jni_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
]
diff --git a/chromium/components/policy/core/browser/browser_policy_connector.cc b/chromium/components/policy/core/browser/browser_policy_connector.cc
index 56f8f7fdc9d..877cb99e9a9 100644
--- a/chromium/components/policy/core/browser/browser_policy_connector.cc
+++ b/chromium/components/policy/core/browser/browser_policy_connector.cc
@@ -141,30 +141,31 @@ bool BrowserPolicyConnector::ProviderHasPolicies(
}
std::string BrowserPolicyConnector::GetDeviceManagementUrl() const {
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kDeviceManagementUrl) &&
- IsCommandLineSwitchSupported())
- return command_line->GetSwitchValueASCII(switches::kDeviceManagementUrl);
- else
- return kDefaultDeviceManagementServerUrl;
+ return GetUrlOverride(switches::kDeviceManagementUrl,
+ kDefaultDeviceManagementServerUrl);
}
std::string BrowserPolicyConnector::GetRealtimeReportingUrl() const {
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kRealtimeReportingUrl) &&
- IsCommandLineSwitchSupported())
- return command_line->GetSwitchValueASCII(switches::kRealtimeReportingUrl);
- else
- return kDefaultRealtimeReportingServerUrl;
+ return GetUrlOverride(switches::kRealtimeReportingUrl,
+ kDefaultRealtimeReportingServerUrl);
}
std::string BrowserPolicyConnector::GetEncryptedReportingUrl() const {
+ return GetUrlOverride(switches::kEncryptedReportingUrl,
+ kDefaultEncryptedReportingServerUrl);
+}
+
+std::string BrowserPolicyConnector::GetUrlOverride(
+ const char* flag,
+ const char* default_value) const {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kEncryptedReportingUrl) &&
- IsCommandLineSwitchSupported())
- return command_line->GetSwitchValueASCII(switches::kEncryptedReportingUrl);
- else
- return kDefaultEncryptedReportingServerUrl;
+ if (command_line->HasSwitch(flag)) {
+ if (IsCommandLineSwitchSupported())
+ return command_line->GetSwitchValueASCII(flag);
+ else
+ LOG(WARNING) << flag << " not supported on this channel";
+ }
+ return default_value;
}
// static
diff --git a/chromium/components/policy/core/browser/browser_policy_connector.h b/chromium/components/policy/core/browser/browser_policy_connector.h
index 6babaffe924..d03a5f19529 100644
--- a/chromium/components/policy/core/browser/browser_policy_connector.h
+++ b/chromium/components/policy/core/browser/browser_policy_connector.h
@@ -98,6 +98,11 @@ class POLICY_EXPORT BrowserPolicyConnector : public BrowserPolicyConnectorBase {
bool ProviderHasPolicies(const ConfigurationPolicyProvider* provider) const;
private:
+ // Helper function to read URL overriding flags. If `flag` isn't set or if the
+ // Chrome channel doesn't allowing overriding, `default_value` is returned
+ // instead.
+ std::string GetUrlOverride(const char* flag, const char* default_value) const;
+
std::unique_ptr<PolicyStatisticsCollector> policy_statistics_collector_;
std::unique_ptr<DeviceManagementService> device_management_service_;
diff --git a/chromium/components/policy/core/browser/cloud/message_util.cc b/chromium/components/policy/core/browser/cloud/message_util.cc
index 385884d3eae..680594318b1 100644
--- a/chromium/components/policy/core/browser/cloud/message_util.cc
+++ b/chromium/components/policy/core/browser/cloud/message_util.cc
@@ -55,6 +55,8 @@ int GetIDSForDMStatus(DeviceManagementStatus status) {
return IDS_POLICY_DM_STATUS_UNKNOWN_ERROR;
case DM_STATUS_SERVICE_TOO_MANY_REQUESTS:
return IDS_POLICY_DM_STATUS_SERVICE_TOO_MANY_REQUESTS;
+ case DM_STATUS_SERVICE_DEVICE_NEEDS_RESET:
+ return IDS_POLICY_DM_STATUS_SERVICE_DEVICE_NEEDS_RESET;
case DM_STATUS_SERVICE_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE:
return IDS_POLICY_DM_STATUS_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE;
case DM_STATUS_SERVICE_ENTERPRISE_ACCOUNT_IS_NOT_ELIGIBLE_TO_ENROLL:
diff --git a/chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.cc b/chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.cc
index 219f5b61aaf..f4e2097cfcc 100644
--- a/chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.cc
+++ b/chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.cc
@@ -18,7 +18,6 @@
#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/identity_manager/account_info.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace em = enterprise_management;
diff --git a/chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.h b/chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.h
index cbb425fe9b3..973065bf556 100644
--- a/chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.h
+++ b/chromium/components/policy/core/browser/cloud/user_policy_signin_service_base.h
@@ -18,14 +18,11 @@
#include "components/policy/core/common/cloud/cloud_policy_service.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "google_apis/gaia/core_account_id.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
class AccountId;
class PrefService;
-namespace network {
-class SharedURLLoaderFactory;
-}
-
namespace policy {
class DeviceManagementService;
diff --git a/chromium/components/policy/core/browser/configuration_policy_handler.cc b/chromium/components/policy/core/browser/configuration_policy_handler.cc
index c58f855a36a..b0c5f4daaa8 100644
--- a/chromium/components/policy/core/browser/configuration_policy_handler.cc
+++ b/chromium/components/policy/core/browser/configuration_policy_handler.cc
@@ -257,8 +257,10 @@ bool StringMappingListPolicyHandler::Convert(const base::Value* input,
std::unique_ptr<base::Value> mapped_value = Map(entry.GetString());
if (mapped_value) {
- if (output)
- output->Append(std::move(mapped_value));
+ if (output) {
+ output->GetList().Append(
+ base::Value::FromUniquePtrValue(std::move(mapped_value)));
+ }
} else if (errors) {
errors->AddError(policy_name(), index, IDS_POLICY_OUT_OF_RANGE_ERROR);
}
diff --git a/chromium/components/policy/core/browser/policy_conversions.cc b/chromium/components/policy/core/browser/policy_conversions.cc
index 2050fd9dfbb..47bd1c225ac 100644
--- a/chromium/components/policy/core/browser/policy_conversions.cc
+++ b/chromium/components/policy/core/browser/policy_conversions.cc
@@ -4,9 +4,11 @@
#include "components/policy/core/browser/policy_conversions.h"
+#include <string>
#include <utility>
#include "base/check.h"
+#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/policy/core/browser/policy_conversions_client.h"
@@ -90,10 +92,6 @@ PolicyConversions& PolicyConversions::SetDropDefaultValues(bool enabled) {
return *this;
}
-std::string PolicyConversions::ToJSON() {
- return client_->ConvertValueToJSON(ToValue());
-}
-
/**
* DictionaryPolicyConversions
*/
@@ -103,57 +101,124 @@ DictionaryPolicyConversions::DictionaryPolicyConversions(
: PolicyConversions(std::move(client)) {}
DictionaryPolicyConversions::~DictionaryPolicyConversions() = default;
-Value DictionaryPolicyConversions::ToValue() {
- Value all_policies(Value::Type::DICTIONARY);
+#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+DictionaryPolicyConversions& DictionaryPolicyConversions::WithUpdaterPolicies(
+ std::unique_ptr<PolicyMap> policies) {
+ PolicyConversions::WithUpdaterPolicies(std::move(policies));
+ return *this;
+}
+
+DictionaryPolicyConversions&
+DictionaryPolicyConversions::WithUpdaterPolicySchemas(
+ PolicyToSchemaMap schemas) {
+ PolicyConversions::WithUpdaterPolicySchemas(std::move(schemas));
+ return *this;
+}
+#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
+DictionaryPolicyConversions& DictionaryPolicyConversions::EnableConvertTypes(
+ bool enabled) {
+ PolicyConversions::EnableConvertTypes(enabled);
+ return *this;
+}
+
+DictionaryPolicyConversions& DictionaryPolicyConversions::EnableConvertValues(
+ bool enabled) {
+ PolicyConversions::EnableConvertValues(enabled);
+ return *this;
+}
+
+DictionaryPolicyConversions&
+DictionaryPolicyConversions::EnableDeviceLocalAccountPolicies(bool enabled) {
+ PolicyConversions::EnableDeviceLocalAccountPolicies(enabled);
+ return *this;
+}
+
+DictionaryPolicyConversions& DictionaryPolicyConversions::EnableDeviceInfo(
+ bool enabled) {
+ PolicyConversions::EnableDeviceInfo(enabled);
+ return *this;
+}
+
+DictionaryPolicyConversions& DictionaryPolicyConversions::EnablePrettyPrint(
+ bool enabled) {
+ PolicyConversions::EnablePrettyPrint(enabled);
+ return *this;
+}
+
+DictionaryPolicyConversions& DictionaryPolicyConversions::EnableUserPolicies(
+ bool enabled) {
+ PolicyConversions::EnableUserPolicies(enabled);
+ return *this;
+}
+
+DictionaryPolicyConversions& DictionaryPolicyConversions::SetDropDefaultValues(
+ bool enabled) {
+ PolicyConversions::SetDropDefaultValues(enabled);
+ return *this;
+}
+
+std::string DictionaryPolicyConversions::ToJSON() {
+ return client()->ConvertValueToJSON(Value(ToValueDict()));
+}
+
+Value::Dict DictionaryPolicyConversions::ToValueDict() {
+ Value::Dict all_policies;
if (client()->HasUserPolicies()) {
- all_policies.SetKey("chromePolicies", client()->GetChromePolicies());
+ all_policies.Set("chromePolicies", client()->GetChromePolicies());
#if BUILDFLAG(ENABLE_EXTENSIONS)
- all_policies.SetKey("extensionPolicies",
- GetExtensionPolicies(POLICY_DOMAIN_EXTENSIONS));
+ all_policies.Set("extensionPolicies",
+ GetExtensionPolicies(POLICY_DOMAIN_EXTENSIONS));
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
}
#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
if (client()->HasUpdaterPolicies())
- all_policies.SetKey("updaterPolicies", client()->GetUpdaterPolicies());
+ all_policies.Set("updaterPolicies", client()->GetUpdaterPolicies());
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS) && BUILDFLAG(IS_CHROMEOS_ASH)
- all_policies.SetKey("loginScreenExtensionPolicies",
- GetExtensionPolicies(POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+ all_policies.Set("loginScreenExtensionPolicies",
+ GetExtensionPolicies(POLICY_DOMAIN_SIGNIN_EXTENSIONS));
#endif // BUILDFLAG(ENABLE_EXTENSIONS) && BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_CHROMEOS_ASH)
- all_policies.SetKey("deviceLocalAccountPolicies",
- GetDeviceLocalAccountPolicies());
- Value identity_fields = client()->GetIdentityFields();
- if (!identity_fields.is_none())
- all_policies.MergeDictionary(&identity_fields);
+ all_policies.Set("deviceLocalAccountPolicies",
+ GetDeviceLocalAccountPolicies());
+ Value::Dict identity_fields = client()->GetIdentityFields();
+ if (!identity_fields.empty())
+ all_policies.Merge(identity_fields);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
return all_policies;
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
-Value DictionaryPolicyConversions::GetDeviceLocalAccountPolicies() {
- Value policies = client()->GetDeviceLocalAccountPolicies();
- Value device_values(Value::Type::DICTIONARY);
- for (auto&& policy : policies.GetListDeprecated()) {
- device_values.SetKey(policy.FindKey("id")->GetString(),
- std::move(*policy.FindKey("policies")));
+Value::Dict DictionaryPolicyConversions::GetDeviceLocalAccountPolicies() {
+ Value::List policies = client()->GetDeviceLocalAccountPolicies();
+ Value::Dict device_values;
+ for (auto&& policy : policies) {
+ const std::string* id = policy.GetDict().FindString("id");
+ Value* policies_value = policy.GetDict().Find("policies");
+ DCHECK(id);
+ DCHECK(policies_value);
+ device_values.Set(*id, std::move(*policies_value));
}
return device_values;
}
#endif
-Value DictionaryPolicyConversions::GetExtensionPolicies(
+Value::Dict DictionaryPolicyConversions::GetExtensionPolicies(
PolicyDomain policy_domain) {
- Value policies = client()->GetExtensionPolicies(policy_domain);
- Value extension_values(Value::Type::DICTIONARY);
- for (auto&& policy : policies.GetListDeprecated()) {
- extension_values.SetKey(policy.FindKey("id")->GetString(),
- std::move(*policy.FindKey("policies")));
+ Value::List policies = client()->GetExtensionPolicies(policy_domain);
+ Value::Dict extension_values;
+ for (auto&& policy : policies) {
+ const std::string* id = policy.GetDict().FindString("id");
+ Value* policies_value = policy.GetDict().Find("policies");
+ DCHECK(id);
+ DCHECK(policies_value);
+ extension_values.Set(*id, std::move(*policies_value));
}
return extension_values;
}
@@ -173,8 +238,53 @@ void ArrayPolicyConversions::WithAdditionalChromePolicies(Value&& policies) {
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
-Value ArrayPolicyConversions::ToValue() {
- Value all_policies(Value::Type::LIST);
+ArrayPolicyConversions& ArrayPolicyConversions::EnableConvertTypes(
+ bool enabled) {
+ PolicyConversions::EnableConvertTypes(enabled);
+ return *this;
+}
+
+ArrayPolicyConversions& ArrayPolicyConversions::EnableConvertValues(
+ bool enabled) {
+ PolicyConversions::EnableConvertValues(enabled);
+ return *this;
+}
+
+ArrayPolicyConversions&
+ArrayPolicyConversions::EnableDeviceLocalAccountPolicies(bool enabled) {
+ PolicyConversions::EnableDeviceLocalAccountPolicies(enabled);
+ return *this;
+}
+
+ArrayPolicyConversions& ArrayPolicyConversions::EnableDeviceInfo(bool enabled) {
+ PolicyConversions::EnableDeviceInfo(enabled);
+ return *this;
+}
+
+ArrayPolicyConversions& ArrayPolicyConversions::EnablePrettyPrint(
+ bool enabled) {
+ PolicyConversions::EnablePrettyPrint(enabled);
+ return *this;
+}
+
+ArrayPolicyConversions& ArrayPolicyConversions::EnableUserPolicies(
+ bool enabled) {
+ PolicyConversions::EnableUserPolicies(enabled);
+ return *this;
+}
+
+ArrayPolicyConversions& ArrayPolicyConversions::SetDropDefaultValues(
+ bool enabled) {
+ PolicyConversions::SetDropDefaultValues(enabled);
+ return *this;
+}
+
+std::string ArrayPolicyConversions::ToJSON() {
+ return client()->ConvertValueToJSON(Value(ToValueList()));
+}
+
+Value::List ArrayPolicyConversions::ToValueList() {
+ Value::List all_policies;
if (client()->HasUserPolicies()) {
all_policies.Append(GetChromePolicies());
@@ -191,9 +301,8 @@ Value ArrayPolicyConversions::ToValue() {
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
- for (auto& policy : client()
- ->GetExtensionPolicies(POLICY_DOMAIN_EXTENSIONS)
- .TakeListDeprecated()) {
+ for (auto& policy :
+ client()->GetExtensionPolicies(POLICY_DOMAIN_EXTENSIONS)) {
all_policies.Append(std::move(policy));
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
@@ -201,20 +310,17 @@ Value ArrayPolicyConversions::ToValue() {
#if BUILDFLAG(ENABLE_EXTENSIONS) && BUILDFLAG(IS_CHROMEOS_ASH)
for (auto& policy :
- client()
- ->GetExtensionPolicies(POLICY_DOMAIN_SIGNIN_EXTENSIONS)
- .TakeListDeprecated()) {
+ client()->GetExtensionPolicies(POLICY_DOMAIN_SIGNIN_EXTENSIONS)) {
all_policies.Append(std::move(policy));
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS) && BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_CHROMEOS_ASH)
- for (auto& device_policy :
- client()->GetDeviceLocalAccountPolicies().TakeListDeprecated())
+ for (auto& device_policy : client()->GetDeviceLocalAccountPolicies())
all_policies.Append(std::move(device_policy));
- Value identity_fields = client()->GetIdentityFields();
- if (!identity_fields.is_none())
+ Value::Dict identity_fields = client()->GetIdentityFields();
+ if (!identity_fields.empty())
all_policies.Append(std::move(identity_fields));
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -222,36 +328,47 @@ Value ArrayPolicyConversions::ToValue() {
}
#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-Value ArrayPolicyConversions::GetUpdaterPolicies() {
- Value chrome_policies_data(Value::Type::DICTIONARY);
- chrome_policies_data.SetKey("name", Value("Google Update Policies"));
- chrome_policies_data.SetKey("id", Value("updater"));
- chrome_policies_data.SetKey("policies", client()->GetUpdaterPolicies());
+Value::Dict ArrayPolicyConversions::GetUpdaterPolicies() {
+ Value::Dict chrome_policies_data;
+ chrome_policies_data.Set("name", "Google Update Policies");
+ chrome_policies_data.Set("id", "updater");
+ chrome_policies_data.Set("policies", client()->GetUpdaterPolicies());
return chrome_policies_data;
}
+
+ArrayPolicyConversions& ArrayPolicyConversions::WithUpdaterPolicies(
+ std::unique_ptr<PolicyMap> policies) {
+ PolicyConversions::WithUpdaterPolicies(std::move(policies));
+ return *this;
+}
+
+ArrayPolicyConversions& ArrayPolicyConversions::WithUpdaterPolicySchemas(
+ PolicyToSchemaMap schemas) {
+ PolicyConversions::WithUpdaterPolicySchemas(std::move(schemas));
+ return *this;
+}
#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-Value ArrayPolicyConversions::GetChromePolicies() {
- Value chrome_policies_data(Value::Type::DICTIONARY);
- chrome_policies_data.SetKey("id", Value("chrome"));
- chrome_policies_data.SetKey("name", Value("Chrome Policies"));
- Value chrome_policies = client()->GetChromePolicies();
+Value::Dict ArrayPolicyConversions::GetChromePolicies() {
+ Value::Dict chrome_policies_data;
+ chrome_policies_data.Set("id", "chrome");
+ chrome_policies_data.Set("name", "Chrome Policies");
+ Value::Dict chrome_policies = client()->GetChromePolicies();
#if BUILDFLAG(IS_CHROMEOS_LACROS)
if (additional_chrome_policies_ != base::Value())
- chrome_policies.MergeDictionary(&additional_chrome_policies_);
+ chrome_policies.Merge(additional_chrome_policies_.GetDict());
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
- chrome_policies_data.SetKey("policies", std::move(chrome_policies));
+ chrome_policies_data.Set("policies", std::move(chrome_policies));
return chrome_policies_data;
}
-Value ArrayPolicyConversions::GetPrecedencePolicies() {
- Value precedence_policies_data(Value::Type::DICTIONARY);
- precedence_policies_data.SetKey("id", Value("precedence"));
- precedence_policies_data.SetKey("name", Value("Policy Precedence"));
- precedence_policies_data.SetKey("policies",
- client()->GetPrecedencePolicies());
- precedence_policies_data.SetKey("precedenceOrder",
- client()->GetPrecedenceOrder());
+Value::Dict ArrayPolicyConversions::GetPrecedencePolicies() {
+ Value::Dict precedence_policies_data;
+ precedence_policies_data.Set("id", "precedence");
+ precedence_policies_data.Set("name", "Policy Precedence");
+ precedence_policies_data.Set("policies", client()->GetPrecedencePolicies());
+ precedence_policies_data.Set("precedenceOrder",
+ client()->GetPrecedenceOrder());
return precedence_policies_data;
}
diff --git a/chromium/components/policy/core/browser/policy_conversions.h b/chromium/components/policy/core/browser/policy_conversions.h
index e0ba0154261..501ebbf0c9c 100644
--- a/chromium/components/policy/core/browser/policy_conversions.h
+++ b/chromium/components/policy/core/browser/policy_conversions.h
@@ -45,39 +45,38 @@ class POLICY_EXPORT PolicyConversions {
// Set to get policy types as human friendly string instead of enum integer.
// Policy types includes policy source, policy scope and policy level.
// Enabled by default.
- PolicyConversions& EnableConvertTypes(bool enabled);
+ virtual PolicyConversions& EnableConvertTypes(bool enabled);
// Set to get dictionary policy value as JSON string.
// Disabled by default.
- PolicyConversions& EnableConvertValues(bool enabled);
+ virtual PolicyConversions& EnableConvertValues(bool enabled);
// Set to get device local account policies on ChromeOS.
// Disabled by default.
- PolicyConversions& EnableDeviceLocalAccountPolicies(bool enabled);
+ virtual PolicyConversions& EnableDeviceLocalAccountPolicies(bool enabled);
// Set to get device basic information on ChromeOS.
// Disabled by default.
- PolicyConversions& EnableDeviceInfo(bool enabled);
+ virtual PolicyConversions& EnableDeviceInfo(bool enabled);
// Set to enable pretty print for all JSON string.
// Enabled by default.
- PolicyConversions& EnablePrettyPrint(bool enabled);
+ virtual PolicyConversions& EnablePrettyPrint(bool enabled);
// Set to get all user scope policies.
// Enabled by default.
- PolicyConversions& EnableUserPolicies(bool enabled);
+ virtual PolicyConversions& EnableUserPolicies(bool enabled);
// Set to drop the policies of which value is a default one set by the policy
// provider. Disabled by default.
- PolicyConversions& SetDropDefaultValues(bool enabled);
+ virtual PolicyConversions& SetDropDefaultValues(bool enabled);
#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
// Sets the updater policies.
- PolicyConversions& WithUpdaterPolicies(std::unique_ptr<PolicyMap> policies);
+ virtual PolicyConversions& WithUpdaterPolicies(
+ std::unique_ptr<PolicyMap> policies);
// Sets the updater policy schemas.
- PolicyConversions& WithUpdaterPolicySchemas(PolicyToSchemaMap schemas);
+ virtual PolicyConversions& WithUpdaterPolicySchemas(
+ PolicyToSchemaMap schemas);
#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
- // Returns the policy data as a base::Value object.
- virtual base::Value ToValue() = 0;
-
// Returns the policy data as a JSON string;
- virtual std::string ToJSON();
+ virtual std::string ToJSON() = 0;
protected:
PolicyConversionsClient* client() { return client_.get(); }
@@ -95,13 +94,40 @@ class POLICY_EXPORT DictionaryPolicyConversions : public PolicyConversions {
delete;
~DictionaryPolicyConversions() override;
- base::Value ToValue() override;
+ DictionaryPolicyConversions& EnableConvertTypes(bool enabled) override;
+
+ DictionaryPolicyConversions& EnableConvertValues(bool enabled) override;
+
+ DictionaryPolicyConversions& EnableDeviceLocalAccountPolicies(
+ bool enabled) override;
+
+ DictionaryPolicyConversions& EnableDeviceInfo(bool enabled) override;
+
+ DictionaryPolicyConversions& EnablePrettyPrint(bool enabled) override;
+
+ DictionaryPolicyConversions& EnableUserPolicies(bool enabled) override;
+
+ DictionaryPolicyConversions& SetDropDefaultValues(bool enabled) override;
+
+#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+ // Sets the updater policies.
+ DictionaryPolicyConversions& WithUpdaterPolicies(
+ std::unique_ptr<PolicyMap> policies) override;
+
+ // Sets the updater policy schemas.
+ DictionaryPolicyConversions& WithUpdaterPolicySchemas(
+ PolicyToSchemaMap schemas) override;
+#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
+ std::string ToJSON() override;
+
+ base::Value::Dict ToValueDict();
private:
- base::Value GetExtensionPolicies(PolicyDomain policy_domain);
+ base::Value::Dict GetExtensionPolicies(PolicyDomain policy_domain);
#if BUILDFLAG(IS_CHROMEOS_ASH)
- base::Value GetDeviceLocalAccountPolicies();
+ base::Value::Dict GetDeviceLocalAccountPolicies();
#endif
};
@@ -113,7 +139,34 @@ class POLICY_EXPORT ArrayPolicyConversions : public PolicyConversions {
ArrayPolicyConversions& operator=(const ArrayPolicyConversions&) = delete;
~ArrayPolicyConversions() override;
- base::Value ToValue() override;
+ ArrayPolicyConversions& EnableConvertTypes(bool enabled) override;
+
+ ArrayPolicyConversions& EnableConvertValues(bool enabled) override;
+
+ ArrayPolicyConversions& EnableDeviceLocalAccountPolicies(
+ bool enabled) override;
+
+ ArrayPolicyConversions& EnableDeviceInfo(bool enabled) override;
+
+ ArrayPolicyConversions& EnablePrettyPrint(bool enabled) override;
+
+ ArrayPolicyConversions& EnableUserPolicies(bool enabled) override;
+
+ ArrayPolicyConversions& SetDropDefaultValues(bool enabled) override;
+
+#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+ // Sets the updater policies.
+ ArrayPolicyConversions& WithUpdaterPolicies(
+ std::unique_ptr<PolicyMap> policies) override;
+
+ // Sets the updater policy schemas.
+ ArrayPolicyConversions& WithUpdaterPolicySchemas(
+ PolicyToSchemaMap schemas) override;
+#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
+ std::string ToJSON() override;
+
+ base::Value::List ToValueList();
#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Additional Chrome policies that need to be displayed, though not available
@@ -122,11 +175,11 @@ class POLICY_EXPORT ArrayPolicyConversions : public PolicyConversions {
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
private:
- base::Value GetChromePolicies();
- base::Value GetPrecedencePolicies();
+ base::Value::Dict GetChromePolicies();
+ base::Value::Dict GetPrecedencePolicies();
#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
- base::Value GetUpdaterPolicies();
+ base::Value::Dict GetUpdaterPolicies();
#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chromium/components/policy/core/browser/policy_conversions_client.cc b/chromium/components/policy/core/browser/policy_conversions_client.cc
index cf0e25c3570..0af84154b8e 100644
--- a/chromium/components/policy/core/browser/policy_conversions_client.cc
+++ b/chromium/components/policy/core/browser/policy_conversions_client.cc
@@ -10,6 +10,7 @@
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
#include "build/build_config.h"
#include "components/policy/core/browser/configuration_policy_handler_list.h"
#include "components/policy/core/browser/policy_error_map.h"
@@ -77,7 +78,7 @@ std::string PolicyConversionsClient::ConvertValueToJSON(
return json_string;
}
-base::Value PolicyConversionsClient::GetChromePolicies() {
+base::Value::Dict PolicyConversionsClient::GetChromePolicies() {
DCHECK(HasUserPolicies());
PolicyService* policy_service = GetPolicyService();
@@ -85,7 +86,7 @@ base::Value PolicyConversionsClient::GetChromePolicies() {
auto* schema_registry = GetPolicySchemaRegistry();
if (!schema_registry) {
LOG(ERROR) << "Cannot dump Chrome policies, no schema registry";
- return Value(Value::Type::DICTIONARY);
+ return Value::Dict();
}
const scoped_refptr<SchemaMap> schema_map = schema_registry->schema_map();
@@ -111,7 +112,7 @@ base::Value PolicyConversionsClient::GetChromePolicies() {
GetKnownPolicies(schema_map, policy_namespace));
}
-base::Value PolicyConversionsClient::GetPrecedencePolicies() {
+base::Value::Dict PolicyConversionsClient::GetPrecedencePolicies() {
DCHECK(HasUserPolicies());
PolicyNamespace policy_namespace =
@@ -122,28 +123,28 @@ base::Value PolicyConversionsClient::GetPrecedencePolicies() {
auto* schema_registry = GetPolicySchemaRegistry();
if (!schema_registry) {
LOG(ERROR) << "Cannot dump Chrome precedence policies, no schema registry";
- return Value(Value::Type::DICTIONARY);
+ return Value::Dict();
}
- base::Value values(base::Value::Type::DICTIONARY);
+ base::Value::Dict values;
// Iterate through all precedence metapolicies and retrieve their value only
// if they are set in the PolicyMap.
for (auto* policy : metapolicy::kPrecedence) {
auto* entry = chrome_policies.Get(policy);
if (entry) {
- values.SetKey(
- policy, GetPolicyValue(policy, entry->DeepCopy(), PoliciesSet(),
- PoliciesSet(), nullptr,
- GetKnownPolicies(schema_registry->schema_map(),
- policy_namespace)));
+ values.Set(policy,
+ GetPolicyValue(policy, entry->DeepCopy(), PoliciesSet(),
+ PoliciesSet(), nullptr,
+ GetKnownPolicies(schema_registry->schema_map(),
+ policy_namespace)));
}
}
return values;
}
-base::Value PolicyConversionsClient::GetPrecedenceOrder() {
+base::Value::List PolicyConversionsClient::GetPrecedenceOrder() {
DCHECK(HasUserPolicies());
PolicyNamespace policy_namespace =
@@ -194,10 +195,9 @@ base::Value PolicyConversionsClient::GetPrecedenceOrder() {
}
}
- base::Value precedence_order_localized(base::Value::Type::LIST);
+ base::Value::List precedence_order_localized;
for (int label_id : precedence_order) {
- precedence_order_localized.Append(
- base::Value(l10n_util::GetStringUTF16(label_id)));
+ precedence_order_localized.Append(l10n_util::GetStringUTF16(label_id));
}
return precedence_order_localized;
@@ -218,7 +218,7 @@ Value PolicyConversionsClient::CopyAndMaybeConvert(
return value_copy;
}
- Value result(Value::Type::LIST);
+ Value::List result;
for (const auto& element : value_copy.GetListDeprecated()) {
if (element.is_dict()) {
result.Append(Value(ConvertValueToJSON(element)));
@@ -226,10 +226,10 @@ Value PolicyConversionsClient::CopyAndMaybeConvert(
result.Append(element.Clone());
}
}
- return result;
+ return base::Value(std::move(result));
}
-Value PolicyConversionsClient::GetPolicyValue(
+Value::Dict PolicyConversionsClient::GetPolicyValue(
const std::string& policy_name,
const PolicyMap::Entry& policy,
const PoliciesSet& deprecated_policies,
@@ -239,23 +239,22 @@ Value PolicyConversionsClient::GetPolicyValue(
known_policy_schemas) const {
absl::optional<Schema> known_policy_schema =
GetKnownPolicySchema(known_policy_schemas, policy_name);
- Value value(Value::Type::DICTIONARY);
- value.SetKey("value", CopyAndMaybeConvert(*policy.value_unsafe(),
- known_policy_schema));
+ Value::Dict value;
+ value.Set("value",
+ CopyAndMaybeConvert(*policy.value_unsafe(), known_policy_schema));
if (convert_types_enabled_) {
- value.SetKey(
- "scope",
- Value((policy.scope == POLICY_SCOPE_USER) ? "user" : "machine"));
- value.SetKey("level", Value(Value((policy.level == POLICY_LEVEL_RECOMMENDED)
- ? "recommended"
- : "mandatory")));
- value.SetKey("source", Value(policy.IsDefaultValue()
- ? "sourceDefault"
- : kPolicySources[policy.source].name));
+ value.Set("scope",
+ (policy.scope == POLICY_SCOPE_USER) ? "user" : "machine");
+ value.Set("level", (policy.level == POLICY_LEVEL_RECOMMENDED)
+ ? "recommended"
+ : "mandatory");
+ value.Set("source", policy.IsDefaultValue()
+ ? "sourceDefault"
+ : kPolicySources[policy.source].name);
} else {
- value.SetKey("scope", Value(policy.scope));
- value.SetKey("level", Value(policy.level));
- value.SetKey("source", Value(policy.source));
+ value.Set("scope", policy.scope);
+ value.Set("level", policy.level);
+ value.Set("source", policy.source);
}
// Policies that have at least one source that could not be merged will
@@ -275,8 +274,8 @@ Value PolicyConversionsClient::GetPolicyValue(
policy_has_unmerged_source = true;
break;
}
- value.SetKey("allSourcesMerged", Value(policy.conflicts.size() <= 1 ||
- !policy_has_unmerged_source));
+ value.Set("allSourcesMerged",
+ (policy.conflicts.size() <= 1 || !policy_has_unmerged_source));
}
std::u16string error;
@@ -301,37 +300,37 @@ Value PolicyConversionsClient::GetPolicyValue(
{policy_map_errors, errors->GetErrors(policy_name)}, u"\n");
}
if (!error.empty())
- value.SetKey("error", Value(error));
+ value.Set("error", error);
std::u16string warning = policy.GetLocalizedMessages(
PolicyMap::MessageType::kWarning,
base::BindRepeating(&l10n_util::GetStringUTF16));
if (!warning.empty())
- value.SetKey("warning", Value(warning));
+ value.Set("warning", warning);
std::u16string info = policy.GetLocalizedMessages(
PolicyMap::MessageType::kInfo,
base::BindRepeating(&l10n_util::GetStringUTF16));
if (!info.empty())
- value.SetKey("info", Value(info));
+ value.Set("info", info);
if (policy.ignored())
- value.SetBoolKey("ignored", true);
+ value.Set("ignored", true);
if (deprecated_policies.find(policy_name) != deprecated_policies.end())
- value.SetBoolKey("deprecated", true);
+ value.Set("deprecated", true);
if (future_policies.find(policy_name) != future_policies.end())
- value.SetBoolKey("future", true);
+ value.Set("future", true);
if (!policy.conflicts.empty()) {
- Value override_values(Value::Type::LIST);
- Value supersede_values(Value::Type::LIST);
+ Value::List override_values;
+ Value::List supersede_values;
bool has_override_values = false;
bool has_supersede_values = false;
for (const auto& conflict : policy.conflicts) {
- base::Value conflicted_policy_value =
+ base::Value::Dict conflicted_policy_value =
GetPolicyValue(policy_name, conflict.entry(), deprecated_policies,
future_policies, errors, known_policy_schemas);
switch (conflict.conflict_type()) {
@@ -348,24 +347,24 @@ Value PolicyConversionsClient::GetPolicyValue(
}
}
if (has_override_values) {
- value.SetKey("conflicts", std::move(override_values));
+ value.Set("conflicts", std::move(override_values));
}
if (has_supersede_values) {
- value.SetKey("superseded", std::move(supersede_values));
+ value.Set("superseded", std::move(supersede_values));
}
}
return value;
}
-Value PolicyConversionsClient::GetPolicyValues(
+Value::Dict PolicyConversionsClient::GetPolicyValues(
const PolicyMap& map,
PolicyErrorMap* errors,
const PoliciesSet& deprecated_policies,
const PoliciesSet& future_policies,
const absl::optional<PolicyConversions::PolicyToSchemaMap>&
known_policy_schemas) const {
- base::Value values(base::Value::Type::DICTIONARY);
+ base::Value::Dict values;
for (const auto& entry : map) {
const std::string& policy_name = entry.first;
const PolicyMap::Entry& policy = entry.second;
@@ -373,10 +372,10 @@ Value PolicyConversionsClient::GetPolicyValues(
continue;
if (policy.IsDefaultValue() && drop_default_values_enabled_)
continue;
- base::Value value =
+ base::Value::Dict value =
GetPolicyValue(policy_name, policy, deprecated_policies,
future_policies, errors, known_policy_schemas);
- values.SetKey(policy_name, std::move(value));
+ values.Set(policy_name, std::move(value));
}
return values;
}
@@ -427,11 +426,11 @@ bool PolicyConversionsClient::GetUserPoliciesEnabled() const {
}
#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-Value PolicyConversionsClient::GetUpdaterPolicies() {
+Value::Dict PolicyConversionsClient::GetUpdaterPolicies() {
return updater_policies_
? GetPolicyValues(*updater_policies_, nullptr, PoliciesSet(),
PoliciesSet(), updater_policy_schemas_)
- : base::Value(base::Value::Type::DICTIONARY);
+ : base::Value::Dict();
}
bool PolicyConversionsClient::PolicyConversionsClient::HasUpdaterPolicies()
diff --git a/chromium/components/policy/core/browser/policy_conversions_client.h b/chromium/components/policy/core/browser/policy_conversions_client.h
index 69fb45b8366..6cc847a9da7 100644
--- a/chromium/components/policy/core/browser/policy_conversions_client.h
+++ b/chromium/components/policy/core/browser/policy_conversions_client.h
@@ -69,7 +69,7 @@ class POLICY_EXPORT PolicyConversionsClient {
// Returns true if this client is able to return information on the updater's
// policies.
bool HasUpdaterPolicies() const;
- base::Value GetUpdaterPolicies();
+ base::Value::Dict GetUpdaterPolicies();
// Sets the updater policy schemas.
void SetUpdaterPolicySchemas(PolicyConversions::PolicyToSchemaMap schemas);
@@ -81,27 +81,28 @@ class POLICY_EXPORT PolicyConversionsClient {
// Returns policies for Chrome browser. Must only be called if
// |HasUserPolicies()| returns true.
- base::Value GetChromePolicies();
+ base::Value::Dict GetChromePolicies();
// Returns precedence-related policies for Chrome browser. Must only be called
// if |HasUserPolicies()| returns true.
- base::Value GetPrecedencePolicies();
+ base::Value::Dict GetPrecedencePolicies();
// Returns an array containing the ordered precedence strings.
- base::Value GetPrecedenceOrder();
+ base::Value::List GetPrecedenceOrder();
// Returns true if this client is able to return information on user
// policies.
virtual bool HasUserPolicies() const = 0;
- // Returns policies for Chrome extensions.
- virtual base::Value GetExtensionPolicies(PolicyDomain policy_domain) = 0;
+ // Returns policies for Chrome extensions in a list of base::Value::Dict.
+ virtual base::Value::List GetExtensionPolicies(
+ PolicyDomain policy_domain) = 0;
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Returns policies for ChromeOS device.
- virtual base::Value GetDeviceLocalAccountPolicies() = 0;
+ virtual base::Value::List GetDeviceLocalAccountPolicies() = 0;
// Returns device specific information if this device is enterprise managed.
- virtual base::Value GetIdentityFields() = 0;
+ virtual base::Value::Dict GetIdentityFields() = 0;
#endif
// Returns the embedder's PolicyService.
@@ -126,7 +127,7 @@ class POLICY_EXPORT PolicyConversionsClient {
// policy namespace of |map|. |deprecated_policies| holds deprecated policies.
// |future_policies| holds unreleased policies. A policy without an entry in
// |known_policy_schemas| is an unknown policy.
- base::Value GetPolicyValue(
+ base::Value::Dict GetPolicyValue(
const std::string& policy_name,
const PolicyMap::Entry& policy,
const PoliciesSet& deprecated_policies,
@@ -141,7 +142,7 @@ class POLICY_EXPORT PolicyConversionsClient {
// policy namespace of |map|. |deprecated_policies| holds deprecated policies.
// |future_policies| holds unreleased policies. A policy in |map| but without
// an entry |known_policy_schemas| is an unknown policy.
- base::Value GetPolicyValues(
+ base::Value::Dict GetPolicyValues(
const PolicyMap& map,
PolicyErrorMap* errors,
const PoliciesSet& deprecated_policies,
diff --git a/chromium/components/policy/core/browser/policy_conversions_client_unittest.cc b/chromium/components/policy/core/browser/policy_conversions_client_unittest.cc
index 21c06ad3502..2f904fac8ee 100644
--- a/chromium/components/policy/core/browser/policy_conversions_client_unittest.cc
+++ b/chromium/components/policy/core/browser/policy_conversions_client_unittest.cc
@@ -25,12 +25,14 @@ class MockPolicyConversionsClient : public PolicyConversionsClient {
private:
// PolicyConversionsClient.
bool HasUserPolicies() const override { return false; }
- base::Value GetExtensionPolicies(PolicyDomain policy_domain) override {
- return base::Value();
+ base::Value::List GetExtensionPolicies(PolicyDomain policy_domain) override {
+ return base::Value::List();
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
- base::Value GetDeviceLocalAccountPolicies() override { return base::Value(); }
- base::Value GetIdentityFields() override { return base::Value(); }
+ base::Value::List GetDeviceLocalAccountPolicies() override {
+ return base::Value::List();
+ }
+ base::Value::Dict GetIdentityFields() override { return base::Value::Dict(); }
#endif
PolicyService* GetPolicyService() const override { return nullptr; }
SchemaRegistry* GetPolicySchemaRegistry() const override { return nullptr; }
@@ -51,7 +53,7 @@ class PolicyConversionsClientTest : public ::testing::Test {
return entry;
}
- base::Value GetPolicyValues(
+ base::Value::Dict GetPolicyValues(
const PolicyConversionsClient& client,
const PolicyMap& map,
const absl::optional<PolicyConversions::PolicyToSchemaMap>&
@@ -78,22 +80,22 @@ TEST_F(PolicyConversionsClientTest, SetDropDefaultValues) {
// All policies should exist because |drop_default_values_enabled_| is false
// by default.
- base::Value policies1 = GetPolicyValues(client, policy_map, policy_schemas);
- const base::Value::Dict& policies_dict1 = policies1.GetDict();
- EXPECT_EQ(3u, policies_dict1.size());
- EXPECT_NE(nullptr, policies_dict1.FindDict(kPolicyName1));
- EXPECT_NE(nullptr, policies_dict1.FindDict(kPolicyName2));
- EXPECT_NE(nullptr, policies_dict1.FindDict(kPolicyName3));
+ base::Value::Dict policies1 =
+ GetPolicyValues(client, policy_map, policy_schemas);
+ EXPECT_EQ(3u, policies1.size());
+ EXPECT_NE(nullptr, policies1.FindDict(kPolicyName1));
+ EXPECT_NE(nullptr, policies1.FindDict(kPolicyName2));
+ EXPECT_NE(nullptr, policies1.FindDict(kPolicyName3));
// Enable dropping default values.
client.SetDropDefaultValues(true);
- base::Value policies2 = GetPolicyValues(client, policy_map, policy_schemas);
+ base::Value::Dict policies2 =
+ GetPolicyValues(client, policy_map, policy_schemas);
// A default valued policy should not exist.
- const base::Value::Dict& policies_dict2 = policies2.GetDict();
- EXPECT_EQ(2u, policies_dict2.size());
- EXPECT_NE(nullptr, policies_dict2.FindDict(kPolicyName1));
- EXPECT_NE(nullptr, policies_dict2.FindDict(kPolicyName3));
+ EXPECT_EQ(2u, policies2.size());
+ EXPECT_NE(nullptr, policies2.FindDict(kPolicyName1));
+ EXPECT_NE(nullptr, policies2.FindDict(kPolicyName3));
}
} // namespace policy
diff --git a/chromium/components/policy/core/browser/policy_error_map.cc b/chromium/components/policy/core/browser/policy_error_map.cc
index 3f21746b719..638c3cc904f 100644
--- a/chromium/components/policy/core/browser/policy_error_map.cc
+++ b/chromium/components/policy/core/browser/policy_error_map.cc
@@ -262,19 +262,6 @@ PolicyErrorMap::const_iterator PolicyErrorMap::end() {
void PolicyErrorMap::Clear() {
CheckReadyAndConvert();
map_.clear();
- debug_infos_.clear();
-}
-
-void PolicyErrorMap::SetDebugInfo(const std::string& policy,
- const std::string& debug_infos) {
- debug_infos_[policy] = debug_infos;
-}
-
-const std::string PolicyErrorMap::GetDebugInfo(const std::string& policy) {
- auto it = debug_infos_.find(policy);
- if (it != debug_infos_.end())
- return it->second;
- return std::string();
}
void PolicyErrorMap::AddError(std::unique_ptr<PendingError> error) {
diff --git a/chromium/components/policy/core/browser/policy_error_map.h b/chromium/components/policy/core/browser/policy_error_map.h
index 250663f7185..1eeb2102928 100644
--- a/chromium/components/policy/core/browser/policy_error_map.h
+++ b/chromium/components/policy/core/browser/policy_error_map.h
@@ -99,15 +99,6 @@ class POLICY_EXPORT PolicyErrorMap {
void Clear();
- // Sets the debug info |debug_info| for the policy with key |policy|.
- // This is intended to be developer-friendly, non-localized detailed
- // information from validation of |policy|.
- void SetDebugInfo(const std::string& policy, const std::string& debug_info);
-
- // Returns the debug info set for the key |policy| by |SetDebugInfo| or an
- // empty string if no debug info was set.
- const std::string GetDebugInfo(const std::string& policy);
-
private:
// Maps the error when ready, otherwise adds it to the pending errors list.
void AddError(std::unique_ptr<PendingError> error);
@@ -120,9 +111,6 @@ class POLICY_EXPORT PolicyErrorMap {
std::vector<std::unique_ptr<PendingError>> pending_;
PolicyMapType map_;
-
- // Maps policy keys to debug infos set through |SetDebugInfo|.
- std::map<std::string, std::string> debug_infos_;
};
} // namespace policy
diff --git a/chromium/components/policy/core/browser/url_blocklist_manager.cc b/chromium/components/policy/core/browser/url_blocklist_manager.cc
index b00a452af06..555636ae7a5 100644
--- a/chromium/components/policy/core/browser/url_blocklist_manager.cc
+++ b/chromium/components/policy/core/browser/url_blocklist_manager.cc
@@ -148,7 +148,7 @@ bool URLBlocklist::IsURLBlocked(const GURL& url) const {
URLBlocklist::URLBlocklistState URLBlocklist::GetURLBlocklistState(
const GURL& url) const {
- std::set<URLMatcherConditionSet::ID> matching_ids =
+ std::set<base::MatcherStringPattern::ID> matching_ids =
url_matcher_->MatchURL(url);
const FilterComponents* max = nullptr;
diff --git a/chromium/components/policy/core/browser/url_blocklist_manager.h b/chromium/components/policy/core/browser/url_blocklist_manager.h
index 35ee99c852e..fd66142bb35 100644
--- a/chromium/components/policy/core/browser/url_blocklist_manager.h
+++ b/chromium/components/policy/core/browser/url_blocklist_manager.h
@@ -75,9 +75,8 @@ class POLICY_EXPORT URLBlocklist {
const url_matcher::util::FilterComponents& lhs,
const url_matcher::util::FilterComponents& rhs);
- url_matcher::URLMatcherConditionSet::ID id_ = 0;
- std::map<url_matcher::URLMatcherConditionSet::ID,
- url_matcher::util::FilterComponents>
+ base::MatcherStringPattern::ID id_ = 0;
+ std::map<base::MatcherStringPattern::ID, url_matcher::util::FilterComponents>
filters_;
std::unique_ptr<url_matcher::URLMatcher> url_matcher_;
};
diff --git a/chromium/components/policy/core/browser/webui/json_generation.cc b/chromium/components/policy/core/browser/webui/json_generation.cc
index 994a3a3908e..e0da8ac231f 100644
--- a/chromium/components/policy/core/browser/webui/json_generation.cc
+++ b/chromium/components/policy/core/browser/webui/json_generation.cc
@@ -17,14 +17,34 @@
namespace policy {
+const char kChromeMetadataVersionKey[] = "version";
+const char kChromeMetadataOSKey[] = "OS";
+const char kChromeMetadataPlatformKey[] = "platform";
+const char kChromeMetadataRevisionKey[] = "revision";
+
JsonGenerationParams::JsonGenerationParams() = default;
JsonGenerationParams::~JsonGenerationParams() = default;
+JsonGenerationParams::JsonGenerationParams(JsonGenerationParams&&) = default;
std::string GenerateJson(std::unique_ptr<PolicyConversionsClient> client,
base::Value status,
const JsonGenerationParams& params) {
- base::Value chrome_metadata(base::Value::Type::DICTIONARY);
- chrome_metadata.SetKey("application", base::Value(params.application_name));
+ base::Value::Dict dict =
+ policy::DictionaryPolicyConversions(std::move(client)).ToValueDict();
+
+ dict.Set("chromeMetadata", GetChromeMetadataValue(params));
+ dict.Set("status", std::move(status));
+
+ std::string json_policies;
+ base::JSONWriter::WriteWithOptions(
+ dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_policies);
+
+ return json_policies;
+}
+
+base::Value::Dict GetChromeMetadataValue(const JsonGenerationParams& params) {
+ base::Value::Dict chrome_metadata;
+ chrome_metadata.Set("application", params.application_name);
std::string version = base::StringPrintf(
"%s (%s)%s %s%s", version_info::GetVersionNumber().c_str(),
@@ -36,31 +56,21 @@ std::string GenerateJson(std::unique_ptr<PolicyConversionsClient> client,
params.processor_variation.c_str(),
params.cohort_name ? params.cohort_name->c_str() : "");
- chrome_metadata.SetKey("version", base::Value(version));
+ chrome_metadata.Set(kChromeMetadataVersionKey, version);
if (params.os_name && !params.os_name->empty()) {
- chrome_metadata.SetKey("OS", base::Value(params.os_name.value()));
+ chrome_metadata.Set(kChromeMetadataOSKey, params.os_name.value());
}
if (params.platform_name && !params.platform_name->empty()) {
- chrome_metadata.SetKey("platform",
- base::Value(params.platform_name.value()));
+ chrome_metadata.Set(kChromeMetadataPlatformKey,
+ params.platform_name.value());
}
- chrome_metadata.SetKey("revision",
- base::Value(version_info::GetLastChange()));
-
- base::Value dict =
- policy::DictionaryPolicyConversions(std::move(client)).ToValue();
-
- dict.SetKey("chromeMetadata", std::move(chrome_metadata));
- dict.SetKey("status", std::move(status));
-
- std::string json_policies;
- base::JSONWriter::WriteWithOptions(
- dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_policies);
+ chrome_metadata.Set(kChromeMetadataRevisionKey,
+ version_info::GetLastChange());
- return json_policies;
+ return chrome_metadata;
}
} // namespace policy
diff --git a/chromium/components/policy/core/browser/webui/json_generation.h b/chromium/components/policy/core/browser/webui/json_generation.h
index 3bdcabe2780..fe85af7aef2 100644
--- a/chromium/components/policy/core/browser/webui/json_generation.h
+++ b/chromium/components/policy/core/browser/webui/json_generation.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/values.h"
#include "components/policy/policy_export.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -17,12 +18,19 @@ class Value;
namespace policy {
class PolicyConversionsClient;
+POLICY_EXPORT extern const char kChromeMetadataVersionKey[];
+POLICY_EXPORT extern const char kChromeMetadataOSKey[];
+POLICY_EXPORT extern const char kChromeMetadataPlatformKey[];
+POLICY_EXPORT extern const char kChromeMetadataRevisionKey[];
+
// Simple object containing parameters used to generate a string of JSON from
// a set of policies.
struct POLICY_EXPORT JsonGenerationParams {
explicit JsonGenerationParams();
~JsonGenerationParams();
+ JsonGenerationParams(JsonGenerationParams&&);
+
JsonGenerationParams& with_application_name(
const std::string& other_application_name) {
application_name = other_application_name;
@@ -73,6 +81,11 @@ POLICY_EXPORT std::string GenerateJson(
base::Value status,
const JsonGenerationParams& params);
+// Returns metadata about the current device/build, based both on what
+// is stored in |params| and also information that is statically available.
+POLICY_EXPORT base::Value::Dict GetChromeMetadataValue(
+ const JsonGenerationParams& params);
+
} // namespace policy
#endif // COMPONENTS_POLICY_CORE_BROWSER_WEBUI_JSON_GENERATION_H_
diff --git a/chromium/components/policy/core/browser/webui/policy_status_provider.cc b/chromium/components/policy/core/browser/webui/policy_status_provider.cc
index 826c8d7f3a1..60bf9f7db17 100644
--- a/chromium/components/policy/core/browser/webui/policy_status_provider.cc
+++ b/chromium/components/policy/core/browser/webui/policy_status_provider.cc
@@ -95,17 +95,22 @@ void PolicyStatusProvider::GetStatusFromCore(const CloudPolicyCore* core,
refresh_scheduler ? refresh_scheduler->GetActualRefreshDelay()
: CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs);
+ const bool is_push_available =
+ refresh_scheduler && refresh_scheduler->invalidations_available();
+
bool no_error = store->status() == CloudPolicyStore::STATUS_OK && client &&
client->status() == DM_STATUS_SUCCESS;
dict->SetBoolKey("error", !no_error);
- dict->SetBoolKey(
- "policiesPushAvailable",
- refresh_scheduler ? refresh_scheduler->invalidations_available() : false);
+ dict->SetBoolKey("policiesPushAvailable", is_push_available);
dict->SetStringKey("status", status);
- dict->SetStringKey(
- "refreshInterval",
- ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
- ui::TimeFormat::LENGTH_SHORT, refresh_interval));
+ // If push is on, policy update will be done via push. Hide policy fetch
+ // interval label to prevent users from misunderstanding.
+ if (!is_push_available) {
+ dict->SetStringKey(
+ "refreshInterval",
+ ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+ ui::TimeFormat::LENGTH_SHORT, refresh_interval));
+ }
base::Time last_refresh_time =
policy && policy->has_timestamp()
? base::Time::FromJavaTime(policy->timestamp())
@@ -139,6 +144,16 @@ void PolicyStatusProvider::GetStatusFromPolicyData(
dict->SetStringKey("clientId", client_id);
dict->SetStringKey("username", username);
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ // Include the "Managed by:" attribute for the user policy legend.
+ if (policy->state() == enterprise_management::PolicyData::ACTIVE) {
+ if (policy->has_managed_by())
+ dict->SetStringKey("enterpriseDomainManager", policy->managed_by());
+ else if (policy->has_display_domain())
+ dict->SetStringKey("enterpriseDomainManager", policy->display_domain());
+ }
+#endif
}
// CloudPolicyStore errors take precedence to show in the status message.
diff --git a/chromium/components/policy/core/common/BUILD.gn b/chromium/components/policy/core/common/BUILD.gn
index bd7b51fe04e..f1df16fe37a 100644
--- a/chromium/components/policy/core/common/BUILD.gn
+++ b/chromium/components/policy/core/common/BUILD.gn
@@ -60,6 +60,7 @@ source_set("internal") {
"cloud/cloud_policy_manager.h",
"cloud/cloud_policy_refresh_scheduler.cc",
"cloud/cloud_policy_refresh_scheduler.h",
+ "cloud/cloud_policy_refresh_scheduler_observer.h",
"cloud/cloud_policy_service.cc",
"cloud/cloud_policy_service.h",
"cloud/cloud_policy_store.cc",
@@ -125,8 +126,6 @@ source_set("internal") {
"features.h",
"json_schema_constants.cc",
"json_schema_constants.h",
- "legacy_chrome_policy_migrator.cc",
- "legacy_chrome_policy_migrator.h",
"management/management_service.cc",
"management/management_service.h",
"management/platform_management_service.cc",
@@ -332,8 +331,8 @@ source_set("internal") {
sources += [
"policy_loader_mac.h",
"policy_loader_mac.mm",
- "preferences_mac.cc",
"preferences_mac.h",
+ "preferences_mac.mm",
]
frameworks += [ "SystemConfiguration.framework" ]
}
@@ -465,7 +464,6 @@ source_set("unit_tests") {
"cloud/user_info_fetcher_unittest.cc",
"command_line_policy_provider_unittest.cc",
"generate_policy_source_unittest.cc",
- "legacy_chrome_policy_migrator_unittest.cc",
"management/management_service_unittest.cc",
"policy_bundle_unittest.cc",
"policy_loader_command_line_unittest.cc",
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_client.cc b/chromium/components/policy/core/common/cloud/cloud_policy_client.cc
index 2c622efdef1..d32eb1744b9 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -9,12 +9,12 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
-#include "base/feature_list.h"
#include "base/guid.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/observer_list.h"
+#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/policy/core/common/cloud/client_data_delegate.h"
@@ -26,7 +26,6 @@
#include "components/policy/core/common/cloud/encrypted_reporting_job_configuration.h"
#include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
#include "components/policy/core/common/cloud/signing_service.h"
-#include "components/policy/core/common/features.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
@@ -153,6 +152,18 @@ DecodeRemoteCommands(DeviceManagementStatus status,
ToVector(response.remote_command_response().secure_commands()));
}
+// Returns a separator-less string with MAC address to match the format of
+// reporting MAC addresses.
+std::string FormatMacAddress(const CloudPolicyClient::MacAddress& mac_address) {
+ CHECK_EQ(mac_address.size(), 6u);
+ // Print 2-digit (02) upper-case hex (X) values of MAC address.
+ std::string mac_address_string = base::StringPrintf(
+ "%02X%02X%02X%02X%02X%02X", mac_address[0], mac_address[1],
+ mac_address[2], mac_address[3], mac_address[4], mac_address[5]);
+ DCHECK_EQ(mac_address_string.size(), 12u);
+ return mac_address_string;
+}
+
} // namespace
CloudPolicyClient::RegistrationParameters::RegistrationParameters(
@@ -186,8 +197,8 @@ CloudPolicyClient::CloudPolicyClient(
const std::string& machine_model,
const std::string& brand_code,
const std::string& attested_device_id,
- const std::string& ethernet_mac_address,
- const std::string& dock_mac_address,
+ absl::optional<MacAddress> ethernet_mac_address,
+ absl::optional<MacAddress> dock_mac_address,
const std::string& manufacture_date,
DeviceManagementService* service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
@@ -196,8 +207,12 @@ CloudPolicyClient::CloudPolicyClient(
machine_model_(machine_model),
brand_code_(brand_code),
attested_device_id_(attested_device_id),
- ethernet_mac_address_(ethernet_mac_address),
- dock_mac_address_(dock_mac_address),
+ ethernet_mac_address_(ethernet_mac_address.has_value()
+ ? FormatMacAddress(ethernet_mac_address.value())
+ : std::string()),
+ dock_mac_address_(dock_mac_address.has_value()
+ ? FormatMacAddress(dock_mac_address.value())
+ : std::string()),
manufacture_date_(manufacture_date),
service_(service), // Can be null for unit tests.
device_dm_token_callback_(device_dm_token_callback),
@@ -278,7 +293,7 @@ void CloudPolicyClient::RegisterWithCertificate(
const std::string& client_id,
const std::string& pem_certificate_chain,
const std::string& sub_organization,
- SigningService* signing_service) {
+ std::unique_ptr<SigningService> signing_service) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(signing_service);
DCHECK(service_);
@@ -299,16 +314,19 @@ void CloudPolicyClient::RegisterWithCertificate(
configuration->set_device_owner(sub_organization);
}
- signing_service->SignData(
+ SigningService* signing_service_ptr = signing_service.get();
+ signing_service_ptr->SignData(
data.SerializeAsString(),
base::BindOnce(&CloudPolicyClient::OnRegisterWithCertificateRequestSigned,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(signing_service)));
}
void CloudPolicyClient::RegisterWithToken(
const std::string& token,
const std::string& client_id,
- const ClientDataDelegate& client_data_delegate) {
+ const ClientDataDelegate& client_data_delegate,
+ bool is_mandatory) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(service_);
DCHECK(!token.empty());
@@ -325,6 +343,10 @@ void CloudPolicyClient::RegisterWithToken(
base::BindOnce(&CloudPolicyClient::OnRegisterCompleted,
weak_ptr_factory_.GetWeakPtr()));
+ // sets CBCM enrollment timeout to 30 seconds when CBCM enrollment is optional
+ if (!is_mandatory)
+ config->SetTimeoutDuration(base::Seconds(30));
+
enterprise_management::RegisterBrowserRequest* request =
config->request()->mutable_register_browser_request();
client_data_delegate.FillRegisterBrowserRequest(
@@ -333,8 +355,11 @@ void CloudPolicyClient::RegisterWithToken(
}
void CloudPolicyClient::OnRegisterWithCertificateRequestSigned(
+ std::unique_ptr<SigningService> signing_service,
bool success,
em::SignedData signed_data) {
+ signing_service.reset();
+
if (!success) {
const em::DeviceManagementResponse response;
OnRegisterCompleted(nullptr, DM_STATUS_CANNOT_SIGN_REQUEST, 0, response);
@@ -425,10 +450,8 @@ void CloudPolicyClient::FetchPolicy() {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
// Only set browser device identifier for CBCM Chrome cloud policy on
// desktop.
- if (base::FeatureList::IsEnabled(
- features::kUploadBrowserDeviceIdentifier) &&
- type_to_fetch.first ==
- dm_protocol::kChromeMachineLevelUserCloudPolicyType) {
+ if (type_to_fetch.first ==
+ dm_protocol::kChromeMachineLevelUserCloudPolicyType) {
fetch_request->set_allocated_browser_device_identifier(
GetBrowserDeviceIdentifier().release());
}
@@ -695,15 +718,22 @@ void CloudPolicyClient::UploadEncryptedReport(
return;
}
- std::unique_ptr<EncryptedReportingJobConfiguration> config =
- std::make_unique<EncryptedReportingJobConfiguration>(
- this, service()->configuration()->GetEncryptedReportingServerUrl(),
- std::move(merging_payload),
- base::BindOnce(&CloudPolicyClient::OnEncryptedReportUploadCompleted,
- weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+ auto config = std::make_unique<EncryptedReportingJobConfiguration>(
+ this, service()->configuration()->GetEncryptedReportingServerUrl(),
+ std::move(merging_payload),
+ base::BindOnce(&CloudPolicyClient::OnEncryptedReportUploadCompleted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
if (context.has_value()) {
config->UpdateContext(std::move(context.value()));
}
+ const auto delay = config->WhenIsAllowedToProceed();
+ if (delay.is_positive()) {
+ // Reject upload.
+ config->CancelNotAllowedJob(); // Invokes callback to response back.
+ return;
+ }
+ // Accept upload.
+ config->AccountForAllowedJob();
request_jobs_.push_back(service_->CreateJob(std::move(config)));
}
@@ -1156,12 +1186,16 @@ void CloudPolicyClient::OnRegisterCompleted(
dm_token_ = response.register_response().device_management_token();
reregistration_dm_token_.clear();
if (response.register_response().has_configuration_seed()) {
- configuration_seed_ =
- base::DictionaryValue::From(base::JSONReader::ReadDeprecated(
- response.register_response().configuration_seed(),
- base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS));
- if (!configuration_seed_)
+ absl::optional<base::Value> configuration_seed = base::JSONReader::Read(
+ response.register_response().configuration_seed(),
+ base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
+ if (configuration_seed && configuration_seed->is_dict()) {
+ configuration_seed_ = std::make_unique<base::Value::Dict>(
+ std::move(configuration_seed->GetDict()));
+ } else {
+ configuration_seed_.reset();
LOG(ERROR) << "Failed to parse configuration seed";
+ }
}
DVLOG(1) << "Client registration complete - DMToken = " << dm_token_;
@@ -1266,7 +1300,8 @@ void CloudPolicyClient::OnPolicyFetchCompleted(
VLOG(2) << "Policy fetch error: " << status;
- if (status == DM_STATUS_SERVICE_DEVICE_NOT_FOUND) {
+ if (status == DM_STATUS_SERVICE_DEVICE_NOT_FOUND ||
+ status == DM_STATUS_SERVICE_DEVICE_NEEDS_RESET) {
// Mark as unregistered and initialize re-registration flow.
reregistration_dm_token_ = dm_token_;
dm_token_.clear();
@@ -1406,7 +1441,7 @@ void CloudPolicyClient::OnRealtimeReportUploadCompleted(
StatusCallback callback,
DeviceManagementService::Job* job,
DeviceManagementStatus status,
- int net_error,
+ int reponse_code,
absl::optional<base::Value::Dict> response) {
status_ = status;
if (status != DM_STATUS_SUCCESS)
@@ -1423,8 +1458,9 @@ void CloudPolicyClient::OnEncryptedReportUploadCompleted(
ResponseCallback callback,
DeviceManagementService::Job* job,
DeviceManagementStatus status,
- int net_error,
+ int reponse_code,
absl::optional<base::Value::Dict> response) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (job == nullptr) {
std::move(callback).Run(absl::nullopt);
return;
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_client.h b/chromium/components/policy/core/common/cloud/cloud_policy_client.h
index e29d81120a2..e3cb04447fa 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_client.h
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_client.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include <array>
#include <map>
#include <memory>
#include <set>
@@ -106,6 +107,8 @@ class POLICY_EXPORT CloudPolicyClient {
absl::optional<int64_t> try_later,
const std::string& pem_encoded_certificate)>;
+ using MacAddress = std::array<uint8_t, 6>;
+
// Observer interface for state and policy changes.
class POLICY_EXPORT Observer {
public:
@@ -196,8 +199,8 @@ class POLICY_EXPORT CloudPolicyClient {
const std::string& machine_model,
const std::string& brand_code,
const std::string& attested_device_id,
- const std::string& ethernet_mac_address,
- const std::string& dock_mac_address,
+ absl::optional<MacAddress> ethernet_mac_address,
+ absl::optional<MacAddress> dock_mac_address,
const std::string& manufacture_date,
DeviceManagementService* service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
@@ -235,21 +238,23 @@ class POLICY_EXPORT CloudPolicyClient {
// |OnRegistrationStateChanged| or |OnClientError|.
// TODO(crbug.com/1236148): Remove SigningService from CloudPolicyClient and
// make callees sign their data themselves.
- virtual void RegisterWithCertificate(const RegistrationParameters& parameters,
- const std::string& client_id,
- const std::string& pem_certificate_chain,
- const std::string& sub_organization,
- SigningService* signing_service);
+ virtual void RegisterWithCertificate(
+ const RegistrationParameters& parameters,
+ const std::string& client_id,
+ const std::string& pem_certificate_chain,
+ const std::string& sub_organization,
+ std::unique_ptr<SigningService> signing_service);
// Attempts to enroll with the device management service using an enrollment
// token. Results in a registration change or error notification.
// This method is used to register browser (e.g. for machine-level policies).
// Device registration with enrollment token should be performed using
- // RegisterWithCertificate method.
- virtual void RegisterWithToken(
- const std::string& token,
- const std::string& client_id,
- const ClientDataDelegate& client_data_delegate);
+ // RegisterWithCertificate method, and this request will timeout after 30
+ // seconds if the enrollment is not mandatory.
+ virtual void RegisterWithToken(const std::string& token,
+ const std::string& client_id,
+ const ClientDataDelegate& client_data_delegate,
+ bool is_mandatory);
// Sets information about a policy invalidation. Subsequent fetch operations
// will use the given info, and callers can use fetched_invalidation_version
@@ -579,7 +584,7 @@ class POLICY_EXPORT CloudPolicyClient {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return client_id_;
}
- const base::DictionaryValue* configuration_seed() const {
+ const base::Value::Dict* configuration_seed() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return configuration_seed_.get();
}
@@ -646,6 +651,7 @@ class POLICY_EXPORT CloudPolicyClient {
// This is called when a RegisterWithCertiifcate request has been signed.
void OnRegisterWithCertificateRequestSigned(
+ std::unique_ptr<SigningService> signing_service,
bool success,
enterprise_management::SignedData signed_data);
@@ -802,7 +808,7 @@ class POLICY_EXPORT CloudPolicyClient {
std::string oauth_token_;
std::string dm_token_;
- std::unique_ptr<base::DictionaryValue> configuration_seed_;
+ std::unique_ptr<base::Value::Dict> configuration_seed_;
DeviceMode device_mode_ = DEVICE_MODE_NOT_SET;
std::string client_id_;
base::Time last_policy_timestamp_;
@@ -893,9 +899,10 @@ class POLICY_EXPORT CloudPolicyClient {
void CreateUniqueRequestJob(
std::unique_ptr<RegistrationJobConfiguration> config);
- // Used to store a copy of the previously used |dm_token_|. This is used
+ // Used to store a copy of the previously used `dm_token_`. This is used
// during re-registration, which gets triggered by a failed policy fetch with
- // error |DM_STATUS_SERVICE_DEVICE_NOT_FOUND|.
+ // errors `DM_STATUS_SERVICE_DEVICE_NOT_FOUND` and
+ // `DM_STATUS_SERVICE_DEVICE_NEEDS_RESET`.
std::string reregistration_dm_token_;
// Whether extra enterprise connectors URL parameters should be included
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc b/chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc
index 0c9a8237fc5..c1605da32e7 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc
@@ -117,12 +117,14 @@ void CloudPolicyClientRegistrationHelper::StartRegistrationWithEnrollmentToken(
const std::string& token,
const std::string& client_id,
const ClientDataDelegate& client_data_delegate,
+ bool is_mandatory,
base::OnceClosure callback) {
DVLOG(1) << "Starting registration process with enrollment token";
DCHECK(!client_->is_registered());
callback_ = std::move(callback);
client_->AddObserver(this);
- client_->RegisterWithToken(token, client_id, client_data_delegate);
+ client_->RegisterWithToken(token, client_id, client_data_delegate,
+ is_mandatory);
}
void CloudPolicyClientRegistrationHelper::OnTokenFetched(
@@ -154,9 +156,9 @@ void CloudPolicyClientRegistrationHelper::OnGetUserInfoFailure(
}
void CloudPolicyClientRegistrationHelper::OnGetUserInfoSuccess(
- const base::DictionaryValue* data) {
+ const base::Value::Dict& data) {
user_info_fetcher_.reset();
- if (!data->FindKey(kGetHostedDomainKey)) {
+ if (!data.Find(kGetHostedDomainKey)) {
DVLOG(1) << "User not from a hosted domain - skipping registration";
RequestCompleted();
return;
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.h b/chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.h
index fc42763ca68..2b6eb26bc22 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.h
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_client_registration_helper.h
@@ -12,6 +12,7 @@
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
+#include "base/values.h"
#include "build/build_config.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/cloud/user_info_fetcher.h"
@@ -62,6 +63,7 @@ class POLICY_EXPORT CloudPolicyClientRegistrationHelper
const std::string& token,
const std::string& client_id,
const ClientDataDelegate& client_data_delegate,
+ bool is_mandatory,
base::OnceClosure callback);
private:
@@ -70,7 +72,7 @@ class POLICY_EXPORT CloudPolicyClientRegistrationHelper
void OnTokenFetched(const std::string& oauth_access_token);
// UserInfoFetcher::Delegate implementation:
- void OnGetUserInfoSuccess(const base::DictionaryValue* response) override;
+ void OnGetUserInfoSuccess(const base::Value::Dict& response) override;
void OnGetUserInfoFailure(const GoogleServiceAuthError& error) override;
// CloudPolicyClient::Observer implementation:
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/chromium/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index ce5e29ed00e..43158ebabd4 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -17,7 +17,6 @@
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/compiler_specific.h"
-#include "base/feature_list.h"
#include "base/json/json_reader.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
@@ -89,8 +88,12 @@ constexpr char kMachineID[] = "fake-machine-id";
constexpr char kMachineModel[] = "fake-machine-model";
constexpr char kBrandCode[] = "fake-brand-code";
constexpr char kAttestedDeviceId[] = "fake-attested-device-id";
-constexpr char kEthernetMacAddress[] = "fake-ethernet-mac-address";
-constexpr char kDockMacAddress[] = "fake-dock-mac-address";
+constexpr CloudPolicyClient::MacAddress kEthernetMacAddress = {0, 1, 2,
+ 3, 4, 5};
+constexpr char kEthernetMacAddressStr[] = "000102030405";
+constexpr CloudPolicyClient::MacAddress kDockMacAddress = {170, 187, 204,
+ 221, 238, 255};
+constexpr char kDockMacAddressStr[] = "AABBCCDDEEFF";
constexpr char kManufactureDate[] = "fake-manufacture-date";
constexpr char kOAuthToken[] = "fake-oauth-token";
constexpr char kDMToken[] = "fake-dm-token";
@@ -251,8 +254,8 @@ em::DeviceManagementRequest GetRegistrationRequest() {
register_request->set_brand_code(kBrandCode);
register_request->mutable_device_register_identification()
->set_attested_device_id(kAttestedDeviceId);
- register_request->set_ethernet_mac_address(kEthernetMacAddress);
- register_request->set_dock_mac_address(kDockMacAddress);
+ register_request->set_ethernet_mac_address(kEthernetMacAddressStr);
+ register_request->set_dock_mac_address(kDockMacAddressStr);
register_request->set_manufacture_date(kManufactureDate);
register_request->set_lifetime(
em::DeviceRegisterRequest::LIFETIME_INDEFINITE);
@@ -280,8 +283,8 @@ em::DeviceManagementRequest GetReregistrationRequest() {
reregister_request->set_brand_code(kBrandCode);
reregister_request->mutable_device_register_identification()
->set_attested_device_id(kAttestedDeviceId);
- reregister_request->set_ethernet_mac_address(kEthernetMacAddress);
- reregister_request->set_dock_mac_address(kDockMacAddress);
+ reregister_request->set_ethernet_mac_address(kEthernetMacAddressStr);
+ reregister_request->set_dock_mac_address(kDockMacAddressStr);
reregister_request->set_manufacture_date(kManufactureDate);
reregister_request->set_lifetime(
em::DeviceRegisterRequest::LIFETIME_INDEFINITE);
@@ -314,8 +317,8 @@ em::DeviceManagementRequest GetCertBasedRegistrationRequest(
register_request->set_brand_code(kBrandCode);
register_request->mutable_device_register_identification()
->set_attested_device_id(kAttestedDeviceId);
- register_request->set_ethernet_mac_address(kEthernetMacAddress);
- register_request->set_dock_mac_address(kDockMacAddress);
+ register_request->set_ethernet_mac_address(kEthernetMacAddressStr);
+ register_request->set_dock_mac_address(kDockMacAddressStr);
register_request->set_manufacture_date(kManufactureDate);
register_request->set_lifetime(
em::DeviceRegisterRequest::LIFETIME_INDEFINITE);
@@ -520,6 +523,7 @@ class CloudPolicyClientTest : public testing::Test {
.WillOnce(DoAll(service_.CaptureJobType(&job_type_),
service_.CaptureAuthData(&auth_data_),
service_.CaptureQueryParams(&query_params_),
+ service_.CaptureTimeout(&timeout_),
service_.CaptureRequest(&job_request_),
service_.SendJobOKAsync(response)));
}
@@ -529,6 +533,7 @@ class CloudPolicyClientTest : public testing::Test {
.WillOnce(DoAll(service_.CaptureJobType(&job_type_),
service_.CaptureAuthData(&auth_data_),
service_.CaptureQueryParams(&query_params_),
+ service_.CaptureTimeout(&timeout_),
service_.CapturePayload(&job_payload_),
service_.SendJobOKAsync(response)));
}
@@ -539,6 +544,7 @@ class CloudPolicyClientTest : public testing::Test {
DoAll(service_.CaptureJobType(&job_type_),
service_.CaptureAuthData(&auth_data_),
service_.CaptureQueryParams(&query_params_),
+ service_.CaptureTimeout(&timeout_),
service_.CaptureRequest(&job_request_),
service_.SendJobResponseAsync(net_error, response_code)));
}
@@ -573,6 +579,7 @@ class CloudPolicyClientTest : public testing::Test {
DeviceManagementService::JobConfiguration::JobType job_type_;
DeviceManagementService::JobConfiguration::ParameterMap query_params_;
DMAuth auth_data_;
+ base::TimeDelta timeout_;
em::DeviceManagementRequest job_request_;
std::string job_payload_;
std::string client_id_;
@@ -586,7 +593,6 @@ class CloudPolicyClientTest : public testing::Test {
StrictMock<MockRobotAuthCodeCallbackObserver>
robot_auth_code_callback_observer_;
StrictMock<MockResponseCallbackObserver> response_callback_observer_;
- FakeSigningService fake_signing_service_;
std::unique_ptr<CloudPolicyClient> client_;
network::TestURLLoaderFactory url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
@@ -656,10 +662,6 @@ TEST_F(CloudPolicyClientTest, SetupRegistrationAndPolicyFetchWithOAuthToken) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || \
(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
TEST_F(CloudPolicyClientTest, RegistrationWithTokenAndPolicyFetch) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- features::kUploadBrowserDeviceIdentifier);
-
const em::DeviceManagementResponse policy_response = GetPolicyResponse();
ExpectAndCaptureJob(GetRegistrationResponse());
@@ -670,7 +672,7 @@ TEST_F(CloudPolicyClientTest, RegistrationWithTokenAndPolicyFetch) {
.WillOnce(Return(kDeviceDMToken));
FakeClientDataDelegate client_data_delegate;
client_->RegisterWithToken(kEnrollmentToken, "device_id",
- client_data_delegate);
+ client_data_delegate, /*is_mandatory=*/true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_TOKEN_ENROLLMENT,
job_type_);
@@ -679,6 +681,7 @@ TEST_F(CloudPolicyClientTest, RegistrationWithTokenAndPolicyFetch) {
EXPECT_TRUE(client_->is_registered());
EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
+ EXPECT_EQ(base::Seconds(0), timeout_);
ExpectAndCaptureJob(policy_response);
EXPECT_CALL(observer_, OnPolicyFetched);
@@ -692,6 +695,20 @@ TEST_F(CloudPolicyClientTest, RegistrationWithTokenAndPolicyFetch) {
EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
CheckPolicyResponse(policy_response);
}
+
+TEST_F(CloudPolicyClientTest, RegistrationWithTokenTestTimeout) {
+ ExpectAndCaptureJob(GetRegistrationResponse());
+ EXPECT_CALL(observer_, OnRegistrationStateChanged);
+ EXPECT_CALL(device_dmtoken_callback_observer_,
+ OnDeviceDMTokenRequested(
+ /*user_affiliation_ids=*/std::vector<std::string>()))
+ .WillOnce(Return(kDeviceDMToken));
+ FakeClientDataDelegate client_data_delegate;
+ client_->RegisterWithToken(kEnrollmentToken, "device_id",
+ client_data_delegate, /*is_mandatory=*/false);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::Seconds(30), timeout_);
+}
#endif
TEST_F(CloudPolicyClientTest, RegistrationAndPolicyFetch) {
@@ -717,6 +734,7 @@ TEST_F(CloudPolicyClientTest, RegistrationAndPolicyFetch) {
VerifyQueryParameter();
EXPECT_TRUE(client_->is_registered());
EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
+ EXPECT_EQ(base::Seconds(0), timeout_);
EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
ExpectAndCaptureJob(policy_response);
@@ -780,24 +798,27 @@ TEST_F(CloudPolicyClientTest, RegistrationWithCertificateAndPolicyFetch) {
/*user_affiliation_ids=*/std::vector<std::string>()))
.WillOnce(Return(kDeviceDMToken));
ExpectAndCaptureJob(GetRegistrationResponse());
- fake_signing_service_.set_success(true);
+ auto fake_signing_service = std::make_unique<FakeSigningService>();
+ fake_signing_service->set_success(true);
+ const std::string expected_job_request_string =
+ GetCertBasedRegistrationRequest(
+ fake_signing_service.get(),
+ /*psm_execution_result=*/absl::nullopt,
+ /*psm_determination_timestamp=*/absl::nullopt)
+ .SerializePartialAsString();
EXPECT_CALL(observer_, OnRegistrationStateChanged);
CloudPolicyClient::RegistrationParameters device_attestation(
em::DeviceRegisterRequest::DEVICE,
em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_ATTESTATION);
client_->RegisterWithCertificate(
device_attestation, std::string() /* client_id */, kEnrollmentCertificate,
- std::string() /* sub_organization */, &fake_signing_service_);
+ std::string() /* sub_organization */, std::move(fake_signing_service));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(
DeviceManagementService::JobConfiguration::TYPE_CERT_BASED_REGISTRATION,
job_type_);
EXPECT_EQ(job_request_.SerializePartialAsString(),
- GetCertBasedRegistrationRequest(
- &fake_signing_service_,
- /*psm_execution_result=*/absl::nullopt,
- /*psm_determination_timestamp=*/absl::nullopt)
- .SerializePartialAsString());
+ expected_job_request_string);
EXPECT_TRUE(client_->is_registered());
EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
@@ -816,14 +837,15 @@ TEST_F(CloudPolicyClientTest, RegistrationWithCertificateAndPolicyFetch) {
}
TEST_F(CloudPolicyClientTest, RegistrationWithCertificateFailToSignRequest) {
- fake_signing_service_.set_success(false);
+ auto fake_signing_service = std::make_unique<FakeSigningService>();
+ fake_signing_service->set_success(false);
EXPECT_CALL(observer_, OnClientError);
CloudPolicyClient::RegistrationParameters device_attestation(
em::DeviceRegisterRequest::DEVICE,
em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_ATTESTATION);
client_->RegisterWithCertificate(
device_attestation, std::string() /* client_id */, kEnrollmentCertificate,
- std::string() /* sub_organization */, &fake_signing_service_);
+ std::string() /* sub_organization */, std::move(fake_signing_service));
EXPECT_FALSE(client_->is_registered());
EXPECT_EQ(DM_STATUS_CANNOT_SIGN_REQUEST, client_->status());
}
@@ -1070,10 +1092,6 @@ TEST_F(CloudPolicyClientTest, PolicyFetchWithInvalidationNoPayload) {
TEST_F(CloudPolicyClientTest, PolicyFetchWithBrowserDeviceIdentifier) {
RegisterClient();
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- features::kUploadBrowserDeviceIdentifier);
-
// Add the policy type that contains browser device identifier.
client_->AddPolicyTypeToFetch(
dm_protocol::kChromeMachineLevelUserCloudPolicyType, std::string());
@@ -1706,7 +1724,13 @@ TEST_P(CloudPolicyClientRegisterWithPsmParamsTest,
/*user_affiliation_ids=*/std::vector<std::string>()))
.WillOnce(Return(kDeviceDMToken));
ExpectAndCaptureJob(GetRegistrationResponse());
- fake_signing_service_.set_success(true);
+ auto fake_signing_service = std::make_unique<FakeSigningService>();
+ fake_signing_service->set_success(true);
+ const std::string expected_job_request_string =
+ GetCertBasedRegistrationRequest(fake_signing_service.get(),
+ psm_execution_result,
+ kExpectedPsmDeterminationTimestamp)
+ .SerializePartialAsString();
EXPECT_CALL(observer_, OnRegistrationStateChanged);
CloudPolicyClient::RegistrationParameters device_attestation(
em::DeviceRegisterRequest::DEVICE,
@@ -1716,16 +1740,13 @@ TEST_P(CloudPolicyClientRegisterWithPsmParamsTest,
device_attestation.SetPsmExecutionResult(psm_execution_result);
client_->RegisterWithCertificate(
device_attestation, std::string() /* client_id */, kEnrollmentCertificate,
- std::string() /* sub_organization */, &fake_signing_service_);
+ std::string() /* sub_organization */, std::move(fake_signing_service));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(
DeviceManagementService::JobConfiguration::TYPE_CERT_BASED_REGISTRATION,
job_type_);
EXPECT_EQ(job_request_.SerializePartialAsString(),
- GetCertBasedRegistrationRequest(&fake_signing_service_,
- psm_execution_result,
- kExpectedPsmDeterminationTimestamp)
- .SerializePartialAsString());
+ expected_job_request_string);
EXPECT_TRUE(client_->is_registered());
EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
@@ -2507,6 +2528,85 @@ TEST_F(CloudPolicyClientTest, PolicyReregistrationFailsWithNonMatchingDMToken) {
EXPECT_EQ(DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID, client_->status());
}
+#if !BUILDFLAG(IS_CHROMEOS)
+TEST_F(CloudPolicyClientTest, PolicyReregistrationAfterDMTokenDeletion) {
+ // Enable the DMToken deletion feature.
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(features::kDmTokenDeletion);
+
+ RegisterClient();
+
+ // Handle 410 (device needs reset) on policy fetch.
+ EXPECT_TRUE(client_->is_registered());
+ EXPECT_FALSE(client_->requires_reregistration());
+ DeviceManagementService::JobConfiguration::JobType upload_type;
+ em::DeviceManagementResponse response;
+ response.add_error_detail(em::CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN);
+ EXPECT_CALL(job_creation_handler_, OnJobCreation)
+ .WillOnce(DoAll(
+ service_.CaptureJobType(&upload_type),
+ service_.SendJobResponseAsync(
+ net::OK, DeviceManagementService::kDeviceNotFound, response)));
+ EXPECT_CALL(observer_, OnRegistrationStateChanged);
+ EXPECT_CALL(observer_, OnClientError);
+ client_->FetchPolicy();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(DM_STATUS_SERVICE_DEVICE_NEEDS_RESET, client_->status());
+ EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
+ EXPECT_FALSE(client_->is_registered());
+ EXPECT_TRUE(client_->requires_reregistration());
+
+ // Re-register.
+ ExpectAndCaptureJob(GetRegistrationResponse());
+ EXPECT_CALL(observer_, OnRegistrationStateChanged);
+ EXPECT_CALL(device_dmtoken_callback_observer_,
+ OnDeviceDMTokenRequested(
+ /*user_affiliation_ids=*/std::vector<std::string>()))
+ .WillOnce(Return(kDeviceDMToken));
+ CloudPolicyClient::RegistrationParameters user_recovery(
+ em::DeviceRegisterRequest::USER,
+ em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_RECOVERY);
+ client_->Register(user_recovery, client_id_, kOAuthToken);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
+ upload_type);
+ EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_REGISTRATION,
+ job_type_);
+ EXPECT_EQ(auth_data_, DMAuth::NoAuth());
+ VerifyQueryParameter();
+ EXPECT_EQ(job_request_.SerializePartialAsString(),
+ GetReregistrationRequest().SerializePartialAsString());
+ EXPECT_TRUE(client_->is_registered());
+ EXPECT_FALSE(client_->requires_reregistration());
+ EXPECT_FALSE(client_->GetPolicyFor(policy_type_, std::string()));
+ EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
+}
+
+TEST_F(CloudPolicyClientTest, PolicyFetchDMTokenDeletion_FeatureDisabled) {
+ RegisterClient();
+ EXPECT_TRUE(client_->is_registered());
+ EXPECT_FALSE(client_->requires_reregistration());
+
+ DeviceManagementService::JobConfiguration::JobType upload_type;
+ em::DeviceManagementResponse response;
+ response.add_error_detail(em::CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN);
+ EXPECT_CALL(job_creation_handler_, OnJobCreation)
+ .WillOnce(DoAll(
+ service_.CaptureJobType(&upload_type),
+ service_.SendJobResponseAsync(
+ net::OK, DeviceManagementService::kDeviceNotFound, response)));
+ EXPECT_CALL(observer_, OnRegistrationStateChanged);
+ EXPECT_CALL(observer_, OnClientError);
+
+ client_->FetchPolicy();
+ base::RunLoop().RunUntilIdle();
+
+ // Because the feature is disabled by default, the presence of the token
+ // deletion error detail still results in the "not found" DM status.
+ EXPECT_EQ(DM_STATUS_SERVICE_DEVICE_NOT_FOUND, client_->status());
+}
+#endif // !BUILDFLAG(IS_CHROMEOS)
+
TEST_F(CloudPolicyClientTest, RequestFetchRobotAuthCodes) {
RegisterClient();
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_constants.cc b/chromium/components/policy/core/common/cloud/cloud_policy_constants.cc
index 3afc5d9f064..00ca22ef99b 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_constants.cc
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_constants.cc
@@ -78,11 +78,7 @@ const char kChromeUserPolicyType[] = "google/chromeos/user";
#elif BUILDFLAG(IS_ANDROID)
const char kChromeUserPolicyType[] = "google/android/user";
#elif BUILDFLAG(IS_IOS)
-// TODO(crbug.com/1312263): Change this for "google/ios/user" once supported
-// by the dmserver. The type for Desktop is temporarily used on iOS to allow
-// early testing of the feature before the DMServer can support iOS User
-// Policy.
-const char kChromeUserPolicyType[] = "google/chrome/user";
+const char kChromeUserPolicyType[] = "google/ios/user";
#else
const char kChromeUserPolicyType[] = "google/chrome/user";
#endif
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_constants.h b/chromium/components/policy/core/common/cloud/cloud_policy_constants.h
index 8ca4a521f14..6c2e5b19879 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_constants.h
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_constants.h
@@ -136,6 +136,8 @@ enum DeviceManagementStatus {
DM_STATUS_REQUEST_TOO_LARGE = 16,
// Client error: Too many request.
DM_STATUS_SERVICE_TOO_MANY_REQUESTS = 17,
+ // Service error: The device needs to be reset (ex. for re-enrollment).
+ DM_STATUS_SERVICE_DEVICE_NEEDS_RESET = 18,
// Service error: Policy not found. Error code defined by the DM folks.
DM_STATUS_SERVICE_POLICY_NOT_FOUND = 902,
// Service error: ARC is not enabled on this domain.
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_core.cc b/chromium/components/policy/core/common/cloud/cloud_policy_core.cc
index 244794f9692..c7b7fa43f32 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_core.cc
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_core.cc
@@ -25,8 +25,9 @@ namespace policy {
CloudPolicyCore::Observer::~Observer() = default;
void CloudPolicyCore::Observer::OnRemoteCommandsServiceStarted(
- CloudPolicyCore* core) {
-}
+ CloudPolicyCore* core) {}
+
+void CloudPolicyCore::Observer::OnCoreDestruction(CloudPolicyCore* core) {}
CloudPolicyCore::CloudPolicyCore(
const std::string& policy_type,
@@ -41,7 +42,11 @@ CloudPolicyCore::CloudPolicyCore(
network_connection_tracker_getter_(
std::move(network_connection_tracker_getter)) {}
-CloudPolicyCore::~CloudPolicyCore() = default;
+CloudPolicyCore::~CloudPolicyCore() {
+ Disconnect();
+ for (auto& observer : observers_)
+ observer.OnCoreDestruction(this);
+}
void CloudPolicyCore::Connect(std::unique_ptr<CloudPolicyClient> client) {
CHECK(!client_);
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_core.h b/chromium/components/policy/core/common/cloud/cloud_policy_core.h
index 8c6495b93b4..fe80cf50467 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_core.h
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_core.h
@@ -56,6 +56,9 @@ class POLICY_EXPORT CloudPolicyCore {
// Called after the remote commands service is started. Defaults to be
// empty.
virtual void OnRemoteCommandsServiceStarted(CloudPolicyCore* core);
+
+ // Called upon core destruction. Defaults to be empty.
+ virtual void OnCoreDestruction(CloudPolicyCore* core);
};
// |task_runner| is the runner for policy refresh tasks.
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_core_unittest.cc b/chromium/components/policy/core/common/cloud/cloud_policy_core_unittest.cc
index 4b0ca2bb1e5..88784c27ebf 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_core_unittest.cc
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_core_unittest.cc
@@ -4,6 +4,8 @@
#include "components/policy/core/common/cloud/cloud_policy_core.h"
+#include <memory>
+
#include "base/base64.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -26,27 +28,25 @@ class CloudPolicyCoreTest : public testing::Test,
CloudPolicyCoreTest& operator=(const CloudPolicyCoreTest&) = delete;
protected:
- CloudPolicyCoreTest()
- : core_(dm_protocol::kChromeUserPolicyType,
- std::string(),
- &store_,
- base::ThreadTaskRunnerHandle::Get(),
- network::TestNetworkConnectionTracker::CreateGetter()),
- core_connected_callback_count_(0),
- refresh_scheduler_started_callback_count_(0),
- core_disconnecting_callback_count_(0),
- bad_callback_count_(0) {
+ CloudPolicyCoreTest() {
+ core_ = std::make_unique<CloudPolicyCore>(
+ dm_protocol::kChromeUserPolicyType, std::string(), &store_,
+ base::ThreadTaskRunnerHandle::Get(),
+ network::TestNetworkConnectionTracker::CreateGetter());
prefs_.registry()->RegisterIntegerPref(
policy_prefs::kUserPolicyRefreshRate,
CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs);
- core_.AddObserver(this);
+ core_->AddObserver(this);
}
- ~CloudPolicyCoreTest() override { core_.RemoveObserver(this); }
+ ~CloudPolicyCoreTest() override {
+ if (core_)
+ core_->RemoveObserver(this);
+ }
void OnCoreConnected(CloudPolicyCore* core) override {
// Make sure core is connected at callback time.
- if (core_.client())
+ if (core_->client())
core_connected_callback_count_++;
else
bad_callback_count_++;
@@ -54,7 +54,7 @@ class CloudPolicyCoreTest : public testing::Test,
void OnRefreshSchedulerStarted(CloudPolicyCore* core) override {
// Make sure refresh scheduler is started at callback time.
- if (core_.refresh_scheduler())
+ if (core_->refresh_scheduler())
refresh_scheduler_started_callback_count_++;
else
bad_callback_count_++;
@@ -62,78 +62,94 @@ class CloudPolicyCoreTest : public testing::Test,
void OnCoreDisconnecting(CloudPolicyCore* core) override {
// Make sure core is still connected at callback time.
- if (core_.client())
+ if (core_->client())
core_disconnecting_callback_count_++;
else
bad_callback_count_++;
}
+ void OnCoreDestruction(policy::CloudPolicyCore* core) override {
+ core_destruction_callback_count_++;
+ core->RemoveObserver(this);
+ CloudPolicyCore::Observer::OnCoreDestruction(core);
+ }
+
base::test::SingleThreadTaskEnvironment task_environment_;
TestingPrefServiceSimple prefs_;
MockCloudPolicyStore store_;
- CloudPolicyCore core_;
+ std::unique_ptr<CloudPolicyCore> core_;
- int core_connected_callback_count_;
- int refresh_scheduler_started_callback_count_;
- int core_disconnecting_callback_count_;
- int bad_callback_count_;
+ int core_connected_callback_count_ = 0;
+ int refresh_scheduler_started_callback_count_ = 0;
+ int core_disconnecting_callback_count_ = 0;
+ int core_destruction_callback_count_ = 0;
+ int bad_callback_count_ = 0;
};
-TEST_F(CloudPolicyCoreTest, ConnectAndDisconnect) {
- EXPECT_TRUE(core_.store());
- EXPECT_FALSE(core_.client());
- EXPECT_FALSE(core_.service());
- EXPECT_FALSE(core_.refresh_scheduler());
+TEST_F(CloudPolicyCoreTest, ConnectAndDisconnectAndDestroy) {
+ EXPECT_TRUE(core_->store());
+ EXPECT_FALSE(core_->client());
+ EXPECT_FALSE(core_->service());
+ EXPECT_FALSE(core_->refresh_scheduler());
// Connect() brings up client and service.
- core_.Connect(
+ core_->Connect(
std::unique_ptr<CloudPolicyClient>(new MockCloudPolicyClient()));
- EXPECT_TRUE(core_.client());
- EXPECT_TRUE(core_.service());
- EXPECT_FALSE(core_.refresh_scheduler());
+ EXPECT_TRUE(core_->client());
+ EXPECT_TRUE(core_->service());
+ EXPECT_FALSE(core_->refresh_scheduler());
EXPECT_EQ(1, core_connected_callback_count_);
EXPECT_EQ(0, refresh_scheduler_started_callback_count_);
EXPECT_EQ(0, core_disconnecting_callback_count_);
// Disconnect() goes back to no client and service.
- core_.Disconnect();
- EXPECT_FALSE(core_.client());
- EXPECT_FALSE(core_.service());
- EXPECT_FALSE(core_.refresh_scheduler());
+ core_->Disconnect();
+ EXPECT_FALSE(core_->client());
+ EXPECT_FALSE(core_->service());
+ EXPECT_FALSE(core_->refresh_scheduler());
EXPECT_EQ(1, core_connected_callback_count_);
EXPECT_EQ(0, refresh_scheduler_started_callback_count_);
EXPECT_EQ(1, core_disconnecting_callback_count_);
// Calling Disconnect() twice doesn't do bad things.
- core_.Disconnect();
- EXPECT_FALSE(core_.client());
- EXPECT_FALSE(core_.service());
- EXPECT_FALSE(core_.refresh_scheduler());
+ core_->Disconnect();
+ EXPECT_FALSE(core_->client());
+ EXPECT_FALSE(core_->service());
+ EXPECT_FALSE(core_->refresh_scheduler());
+ EXPECT_EQ(1, core_connected_callback_count_);
+ EXPECT_EQ(0, refresh_scheduler_started_callback_count_);
+ EXPECT_EQ(1, core_disconnecting_callback_count_);
+ EXPECT_EQ(0, core_destruction_callback_count_);
+ EXPECT_EQ(0, bad_callback_count_);
+
+ // Destruction callback is called.
+ core_.reset();
EXPECT_EQ(1, core_connected_callback_count_);
EXPECT_EQ(0, refresh_scheduler_started_callback_count_);
EXPECT_EQ(1, core_disconnecting_callback_count_);
+ EXPECT_EQ(1, core_destruction_callback_count_);
EXPECT_EQ(0, bad_callback_count_);
}
TEST_F(CloudPolicyCoreTest, RefreshScheduler) {
- EXPECT_FALSE(core_.refresh_scheduler());
- core_.Connect(
+ EXPECT_FALSE(core_->refresh_scheduler());
+ core_->Connect(
std::unique_ptr<CloudPolicyClient>(new MockCloudPolicyClient()));
- core_.StartRefreshScheduler();
- ASSERT_TRUE(core_.refresh_scheduler());
+ core_->StartRefreshScheduler();
+ ASSERT_TRUE(core_->refresh_scheduler());
int default_refresh_delay =
- core_.refresh_scheduler()->GetActualRefreshDelay();
+ core_->refresh_scheduler()->GetActualRefreshDelay();
const int kRefreshRate = 1000 * 60 * 60;
prefs_.SetInteger(policy_prefs::kUserPolicyRefreshRate, kRefreshRate);
- core_.TrackRefreshDelayPref(&prefs_, policy_prefs::kUserPolicyRefreshRate);
- EXPECT_EQ(kRefreshRate, core_.refresh_scheduler()->GetActualRefreshDelay());
+ core_->TrackRefreshDelayPref(&prefs_, policy_prefs::kUserPolicyRefreshRate);
+ EXPECT_EQ(kRefreshRate, core_->refresh_scheduler()->GetActualRefreshDelay());
prefs_.ClearPref(policy_prefs::kUserPolicyRefreshRate);
EXPECT_EQ(default_refresh_delay,
- core_.refresh_scheduler()->GetActualRefreshDelay());
+ core_->refresh_scheduler()->GetActualRefreshDelay());
EXPECT_EQ(1, core_connected_callback_count_);
EXPECT_EQ(1, refresh_scheduler_started_callback_count_);
@@ -148,4 +164,13 @@ TEST_F(CloudPolicyCoreTest, DmProtocolBase64Constants) {
EXPECT_EQ(encoded, dm_protocol::kChromeMachineLevelUserCloudPolicyTypeBase64);
}
+TEST_F(CloudPolicyCoreTest, DestroyWithoutConnecting) {
+ core_.reset();
+ EXPECT_EQ(0, core_connected_callback_count_);
+ EXPECT_EQ(0, refresh_scheduler_started_callback_count_);
+ EXPECT_EQ(0, core_disconnecting_callback_count_);
+ EXPECT_EQ(1, core_destruction_callback_count_);
+ EXPECT_EQ(0, bad_callback_count_);
+}
+
} // namespace policy
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.cc b/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.cc
index 68d01ce9c1c..9b9b26a3112 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.cc
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.cc
@@ -117,6 +117,8 @@ CloudPolicyRefreshScheduler::~CloudPolicyRefreshScheduler() {
client_->RemoveObserver(this);
if (network_connection_tracker_)
network_connection_tracker_->RemoveNetworkConnectionObserver(this);
+ for (auto& observer : observers_)
+ observer.OnRefreshSchedulerDestruction(this);
}
void CloudPolicyRefreshScheduler::SetDesiredRefreshDelay(
@@ -263,43 +265,19 @@ void CloudPolicyRefreshScheduler::UpdateLastRefreshFromPolicy() {
}
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
- // Refreshing on mobile platforms:
- // - if no user is signed-in then the |client_| is never registered and
- // nothing happens here.
- // - if the user is signed-in but isn't enterprise then the |client_| is
- // never registered and nothing happens here.
- // - if the user is signed-in but isn't registered for policy yet then the
- // |client_| isn't registered either; the UserPolicySigninService will try
- // to register, and OnRegistrationStateChanged() will be invoked later.
- // - if the client is signed-in and has policy then its timestamp is used to
- // determine when to perform the next fetch, which will be once the cached
- // version is considered "old enough".
- //
- // If there is an old policy cache then a fetch will be performed "soon"; if
- // that fetch fails then a retry is attempted after a delay, with exponential
- // backoff. If those fetches keep failing then the cached timestamp is *not*
- // updated, and another fetch (and subsequent retries) will be attempted
- // again on the next startup.
- //
- // But if the cached policy is considered fresh enough then we try to avoid
- // fetching again on startup; the Android logic differs from the desktop in
- // this aspect.
- if (store_->has_policy() && store_->policy()->has_timestamp()) {
- last_refresh_ = base::Time::FromJavaTime(store_->policy()->timestamp());
- last_refresh_ticks_ =
- GetTickClock()->NowTicks() + (last_refresh_ - GetClock()->Now());
- }
+ // On mobile platforms the client is only registered for enterprise users.
+ constexpr bool should_update = true;
#else
- // If there is a cached non-managed response, make sure to only re-query the
- // server after kUnmanagedRefreshDelayMs. NB: For existing policy, an
- // immediate refresh is intentional.
+ // Only delay refresh for a cached non-managed response.
+ const bool should_update = !store_->is_managed();
+#endif
+
if (store_->has_policy() && store_->policy()->has_timestamp() &&
- !store_->is_managed()) {
+ should_update) {
last_refresh_ = base::Time::FromJavaTime(store_->policy()->timestamp());
last_refresh_ticks_ =
GetTickClock()->NowTicks() + (last_refresh_ - GetClock()->Now());
}
-#endif
}
void CloudPolicyRefreshScheduler::ScheduleRefresh() {
@@ -348,6 +326,7 @@ void CloudPolicyRefreshScheduler::ScheduleRefresh() {
case DM_STATUS_SERVICE_MISSING_LICENSES:
case DM_STATUS_SERVICE_DEPROVISIONED:
case DM_STATUS_SERVICE_DOMAIN_MISMATCH:
+ case DM_STATUS_SERVICE_DEVICE_NEEDS_RESET:
case DM_STATUS_SERVICE_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE:
case DM_STATUS_SERVICE_ENTERPRISE_ACCOUNT_IS_NOT_ELIGIBLE_TO_ENROLL:
case DM_STATUS_SERVICE_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED:
@@ -417,6 +396,8 @@ void CloudPolicyRefreshScheduler::CancelRefresh() {
void CloudPolicyRefreshScheduler::UpdateLastRefresh() {
last_refresh_ = GetClock()->Now();
last_refresh_ticks_ = GetTickClock()->NowTicks();
+ for (auto& observer : observers_)
+ observer.OnFetchAttempt(this);
}
void CloudPolicyRefreshScheduler::OnPolicyRefreshed(bool success) {
@@ -444,4 +425,14 @@ CloudPolicyRefreshScheduler::OverrideTickClockForTesting(
base::BindOnce([]() { tick_clock_for_testing_ = nullptr; }));
}
+void CloudPolicyRefreshScheduler::AddObserver(
+ CloudPolicyRefreshSchedulerObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void CloudPolicyRefreshScheduler::RemoveObserver(
+ CloudPolicyRefreshSchedulerObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
} // namespace policy
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h b/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h
index 090ba2acca7..a3df3bd5184 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h
@@ -15,6 +15,7 @@
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
+#include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler_observer.h"
#include "components/policy/core/common/cloud/cloud_policy_store.h"
#include "components/policy/policy_export.h"
#include "services/network/public/cpp/network_connection_tracker.h"
@@ -29,6 +30,32 @@ class CloudPolicyService;
// Observes CloudPolicyClient and CloudPolicyStore to trigger periodic policy
// fetches and issue retries on error conditions.
+//
+// Refreshing non-managed responses:
+// - If there is a cached non-managed response, make sure to only re-query the
+// server after kUnmanagedRefreshDelayMs.
+// - NB: For existing policy, an immediate refresh is intentional.
+//
+// Refreshing on mobile platforms:
+// - if no user is signed-in then the |client_| is never registered.
+// - if the user is signed-in but isn't enterprise then the |client_| is
+// never registered.
+// - if the user is signed-in but isn't registered for policy yet then the
+// |client_| isn't registered either; the UserPolicySigninService will try
+// to register, and OnRegistrationStateChanged() will be invoked later.
+// - if the client is signed-in and has policy then its timestamp is used to
+// determine when to perform the next fetch, which will be once the cached
+// version is considered "old enough".
+//
+// If there is an old policy cache then a fetch will be performed "soon"; if
+// that fetch fails then a retry is attempted after a delay, with exponential
+// backoff. If those fetches keep failing then the cached timestamp is *not*
+// updated, and another fetch (and subsequent retries) will be attempted
+// again on the next startup.
+//
+// But if the cached policy is considered fresh enough then we try to avoid
+// fetching again on startup; the Android logic differs from the desktop in
+// this aspect.
class POLICY_EXPORT CloudPolicyRefreshScheduler
: public CloudPolicyClient::Observer,
public CloudPolicyStore::Observer,
@@ -106,6 +133,12 @@ class POLICY_EXPORT CloudPolicyRefreshScheduler
static base::ScopedClosureRunner OverrideTickClockForTesting(
base::TickClock* tick_clock_for_testing);
+ // Registers an observer to be notified.
+ void AddObserver(CloudPolicyRefreshSchedulerObserver* observer);
+
+ // Removes the specified observer.
+ void RemoveObserver(CloudPolicyRefreshSchedulerObserver* observer);
+
private:
// Initializes |last_refresh_| to the policy timestamp from |store_| in case
// there is policy present that indicates this client is not managed. This
@@ -181,6 +214,8 @@ class POLICY_EXPORT CloudPolicyRefreshScheduler
// its initial status.
base::Time creation_time_;
+ base::ObserverList<CloudPolicyRefreshSchedulerObserver, true> observers_;
+
base::WeakPtrFactory<CloudPolicyRefreshScheduler> weak_factory_{this};
};
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_observer.h b/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_observer.h
new file mode 100644
index 00000000000..0c9cc24e82c
--- /dev/null
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_observer.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_POLICY_CORE_COMMON_CLOUD_CLOUD_POLICY_REFRESH_SCHEDULER_OBSERVER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_CLOUD_CLOUD_POLICY_REFRESH_SCHEDULER_OBSERVER_H_
+
+#include "base/observer_list_types.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+class CloudPolicyRefreshScheduler;
+
+// Callbacks for policy refresh scheduler events. Unlike most other observers in
+// this directory, this class is defined as non-nested to allow it being used in
+// other external classes without pulling too many other dependencies. Please
+// see comments on https://crrev.com/c/3536708 for more context.
+class POLICY_EXPORT CloudPolicyRefreshSchedulerObserver
+ : public base::CheckedObserver {
+ public:
+ // Called before each attempt to fetch policy.
+ virtual void OnFetchAttempt(CloudPolicyRefreshScheduler* scheduler) = 0;
+
+ // Called upon refresh scheduler destruction.
+ virtual void OnRefreshSchedulerDestruction(
+ CloudPolicyRefreshScheduler* scheduler) = 0;
+};
+
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_CLOUD_CLOUD_POLICY_REFRESH_SCHEDULER_OBSERVER_H_
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_unittest.cc b/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_unittest.cc
index a901918dfd9..bacafeac7cd 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_unittest.cc
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_refresh_scheduler_unittest.cc
@@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/compiler_specific.h"
+#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/test/simple_test_clock.h"
@@ -20,6 +21,7 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler_observer.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_service.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
@@ -29,8 +31,9 @@
namespace em = enterprise_management;
-using testing::Mock;
using testing::_;
+using testing::Invoke;
+using testing::Mock;
namespace policy {
@@ -40,12 +43,25 @@ const int64_t kPolicyRefreshRate = 4 * 60 * 60 * 1000;
const int64_t kInitialCacheAgeMinutes = 1;
+class MockObserver : public CloudPolicyRefreshSchedulerObserver {
+ public:
+ MOCK_METHOD(void,
+ OnFetchAttempt,
+ (CloudPolicyRefreshScheduler * scheduler),
+ (override));
+ MOCK_METHOD(void,
+ OnRefreshSchedulerDestruction,
+ (CloudPolicyRefreshScheduler * scheduler),
+ (override));
+};
+
} // namespace
class CloudPolicyRefreshSchedulerTest : public testing::Test {
protected:
CloudPolicyRefreshSchedulerTest()
- : service_(std::make_unique<MockCloudPolicyService>(&client_, &store_)),
+ : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+ service_(std::make_unique<MockCloudPolicyService>(&client_, &store_)),
task_runner_(new base::TestSimpleTaskRunner()),
mock_clock_(std::make_unique<base::SimpleTestClock>()) {}
@@ -63,6 +79,12 @@ class CloudPolicyRefreshSchedulerTest : public testing::Test {
last_update_ = base::Time::FromJavaTime(store_.policy()->timestamp());
last_update_ticks_ = base::TimeTicks::Now() +
(last_update_ - base::Time::NowFromSystemTime());
+
+ // Remove mock observer from any scheduler that is being destroyed.
+ ON_CALL(mock_observer_, OnRefreshSchedulerDestruction)
+ .WillByDefault(Invoke([&](CloudPolicyRefreshScheduler* scheduler) {
+ scheduler->RemoveObserver(&mock_observer_);
+ }));
}
CloudPolicyRefreshScheduler* CreateRefreshScheduler() {
@@ -73,6 +95,7 @@ class CloudPolicyRefreshSchedulerTest : public testing::Test {
// Make sure the NetworkConnectionTracker has been set up.
base::RunLoop().RunUntilIdle();
scheduler->SetDesiredRefreshDelay(kPolicyRefreshRate);
+ scheduler->AddObserver(&mock_observer_);
return scheduler;
}
@@ -191,6 +214,7 @@ class CloudPolicyRefreshSchedulerTest : public testing::Test {
em::PolicyData policy_data_;
std::unique_ptr<MockCloudPolicyService> service_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ MockObserver mock_observer_;
// Base time for the refresh that the scheduler should be using.
base::Time last_update_;
@@ -201,8 +225,7 @@ class CloudPolicyRefreshSchedulerTest : public testing::Test {
TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshNoPolicy) {
store_.set_policy_data_for_testing(std::make_unique<em::PolicyData>());
- std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
- CreateRefreshScheduler());
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
EXPECT_TRUE(task_runner_->HasPendingTask());
EXPECT_EQ(GetLastDelay(), base::TimeDelta());
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
@@ -214,8 +237,7 @@ TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshUnmanaged) {
policy_data_.set_state(em::PolicyData::UNMANAGED);
store_.set_policy_data_for_testing(
std::make_unique<em::PolicyData>(policy_data_));
- std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
- CreateRefreshScheduler());
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
CheckTiming(scheduler.get(),
CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs);
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
@@ -224,8 +246,7 @@ TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshUnmanaged) {
}
TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshManagedNotYetFetched) {
- std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
- CreateRefreshScheduler());
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
EXPECT_TRUE(task_runner_->HasPendingTask());
CheckInitialRefresh(scheduler.get(), false);
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
@@ -237,8 +258,7 @@ TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshManagedAlreadyFetched) {
SetLastUpdateToNow();
client_.SetPolicy(dm_protocol::kChromeUserPolicyType, std::string(),
em::PolicyFetchResponse());
- std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
- CreateRefreshScheduler());
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
CheckTiming(scheduler.get(), kPolicyRefreshRate);
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
@@ -247,8 +267,7 @@ TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshManagedAlreadyFetched) {
TEST_F(CloudPolicyRefreshSchedulerTest, Unregistered) {
client_.SetDMToken(std::string());
- std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
- CreateRefreshScheduler());
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
client_.NotifyPolicyFetched();
client_.NotifyRegistrationStateChanged();
client_.NotifyClientError();
@@ -259,8 +278,7 @@ TEST_F(CloudPolicyRefreshSchedulerTest, Unregistered) {
}
TEST_F(CloudPolicyRefreshSchedulerTest, RefreshSoon) {
- std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
- CreateRefreshScheduler());
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
scheduler->RefreshSoon();
@@ -269,8 +287,7 @@ TEST_F(CloudPolicyRefreshSchedulerTest, RefreshSoon) {
}
TEST_F(CloudPolicyRefreshSchedulerTest, RefreshSoonOverriding) {
- std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
- CreateRefreshScheduler());
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
// The refresh scheduled for soon overrides the previously scheduled refresh.
scheduler->RefreshSoon();
@@ -430,8 +447,7 @@ TEST_F(CloudPolicyRefreshSchedulerTest, InvalidationsDisconnected) {
TEST_F(CloudPolicyRefreshSchedulerTest, OnConnectionChangedUnregistered) {
client_.SetDMToken(std::string());
- std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
- CreateRefreshScheduler());
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
client_.NotifyClientError();
EXPECT_FALSE(task_runner_->HasPendingTask());
@@ -447,8 +463,7 @@ TEST_F(CloudPolicyRefreshSchedulerTest, OnConnectionChangedUnregistered) {
// pending task and queue a new task to run earlier. It is desirable to
// simulate that flow here.
TEST_F(CloudPolicyRefreshSchedulerTest, OnConnectionChangedAfterSleep) {
- std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
- CreateRefreshScheduler());
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
client_.SetPolicy(dm_protocol::kChromeUserPolicyType, std::string(),
em::PolicyFetchResponse());
@@ -462,6 +477,39 @@ TEST_F(CloudPolicyRefreshSchedulerTest, OnConnectionChangedAfterSleep) {
task_runner_->ClearPendingTasks();
}
+TEST_F(CloudPolicyRefreshSchedulerTest, FetchAttemptCallback) {
+ EXPECT_CALL(mock_observer_, OnFetchAttempt).Times(0);
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
+ Mock::VerifyAndClearExpectations(&mock_observer_);
+
+ EXPECT_CALL(mock_observer_, OnFetchAttempt).Times(1);
+ task_runner_->RunUntilIdle();
+ Mock::VerifyAndClearExpectations(&mock_observer_);
+}
+
+TEST_F(CloudPolicyRefreshSchedulerTest, DestructionCallback) {
+ EXPECT_CALL(mock_observer_, OnRefreshSchedulerDestruction).Times(0);
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
+ task_runner_->RunUntilIdle();
+ Mock::VerifyAndClearExpectations(&mock_observer_);
+
+ EXPECT_CALL(mock_observer_, OnRefreshSchedulerDestruction).Times(1);
+ scheduler.reset();
+ Mock::VerifyAndClearExpectations(&mock_observer_);
+}
+
+TEST_F(CloudPolicyRefreshSchedulerTest, DestructionCallbackBeforeFetchAttempt) {
+ EXPECT_CALL(mock_observer_, OnRefreshSchedulerDestruction).Times(0);
+ EXPECT_CALL(mock_observer_, OnFetchAttempt).Times(0);
+ auto scheduler = base::WrapUnique(CreateRefreshScheduler());
+ Mock::VerifyAndClearExpectations(&mock_observer_);
+
+ EXPECT_CALL(mock_observer_, OnRefreshSchedulerDestruction(scheduler.get()))
+ .Times(1);
+ scheduler.reset();
+ Mock::VerifyAndClearExpectations(&mock_observer_);
+}
+
class CloudPolicyRefreshSchedulerSteadyStateTest
: public CloudPolicyRefreshSchedulerTest {
protected:
@@ -558,6 +606,7 @@ static const ClientErrorTestParam kClientErrorTestCases[] = {
{DM_STATUS_SERVICE_ENTERPRISE_ACCOUNT_IS_NOT_ELIGIBLE_TO_ENROLL, -1, 1},
{DM_STATUS_SERVICE_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED, -1, 1},
{DM_STATUS_SERVICE_TOO_MANY_REQUESTS, kPolicyRefreshRate, 1},
+ {DM_STATUS_SERVICE_DEVICE_NEEDS_RESET, -1, 1},
{DM_STATUS_SERVICE_ILLEGAL_ACCOUNT_FOR_PACKAGED_EDU_LICENSE, -1, 1},
};
diff --git a/chromium/components/policy/core/common/cloud/cloud_policy_util.cc b/chromium/components/policy/core/common/cloud/cloud_policy_util.cc
index 699ca03c7c6..5b497688c0a 100644
--- a/chromium/components/policy/core/common/cloud/cloud_policy_util.cc
+++ b/chromium/components/policy/core/common/cloud/cloud_policy_util.cc
@@ -56,6 +56,10 @@
#include "components/user_manager/user_manager.h"
#endif
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/startup/browser_init_params.h"
+#endif
+
#if BUILDFLAG(IS_WIN)
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
@@ -159,7 +163,7 @@ std::string GetOSArchitecture() {
}
std::string GetOSUsername() {
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_APPLE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)
struct passwd* creds = getpwuid(getuid());
if (!creds || !creds->pw_name)
return std::string();
@@ -185,6 +189,18 @@ std::string GetOSUsername() {
if (!user)
return std::string();
return user->GetAccountId().GetUserEmail();
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+ const crosapi::mojom::BrowserInitParams* init_params =
+ chromeos::BrowserInitParams::Get();
+ if (init_params && init_params->device_account) {
+ return init_params->device_account->raw_email;
+ }
+ // Fallback if init_params are missing.
+ struct passwd* creds = getpwuid(getuid());
+ if (!creds || !creds->pw_name)
+ return std::string();
+
+ return creds->pw_name;
#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
// TODO(crbug.com/1257674): This should be fully implemented when there is
// support in fuchsia.
@@ -214,6 +230,14 @@ std::string GetDeviceName() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
return chromeos::system::StatisticsProvider::GetInstance()
->GetEnterpriseMachineID();
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+ const crosapi::mojom::BrowserInitParams* init_params =
+ chromeos::BrowserInitParams::Get();
+ if (init_params && init_params->device_properties &&
+ init_params->device_properties->serial_number.has_value()) {
+ return init_params->device_properties->serial_number.value();
+ }
+ return GetMachineName();
#else
return GetMachineName();
#endif
diff --git a/chromium/components/policy/core/common/cloud/component_cloud_policy_service.cc b/chromium/components/policy/core/common/cloud/component_cloud_policy_service.cc
index 1961deb1fa0..dfa1e504f5a 100644
--- a/chromium/components/policy/core/common/cloud/component_cloud_policy_service.cc
+++ b/chromium/components/policy/core/common/cloud/component_cloud_policy_service.cc
@@ -27,13 +27,12 @@
#include "components/policy/core/common/cloud/resource_cache.h"
#include "components/policy/core/common/schema.h"
#include "components/policy/core/common/schema_map.h"
+#include "components/policy/core/common/values_util.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace em = enterprise_management;
-using ComponentPolicyMap =
- policy::ComponentCloudPolicyServiceObserver::ComponentPolicyMap;
using ScopedResponseMap = std::unordered_map<policy::PolicyNamespace,
em::PolicyFetchResponse,
policy::PolicyNamespaceHash>;
@@ -208,9 +207,8 @@ void ComponentCloudPolicyService::Backend::InitIfNeeded() {
bundle->CopyFrom(store_.policy());
service_task_runner_->PostTask(
FROM_HERE,
- base::BindOnce(
- &ComponentCloudPolicyService::SetPolicy, service_, std::move(bundle),
- std::make_unique<ComponentPolicyMap>(store_.serialized_policy())));
+ base::BindOnce(&ComponentCloudPolicyService::SetPolicy, service_,
+ std::move(bundle), store_.GetJsonPolicyMap()));
initialized_ = true;
@@ -241,9 +239,8 @@ void ComponentCloudPolicyService::Backend::
bundle->CopyFrom(store_.policy());
service_task_runner_->PostTask(
FROM_HERE,
- base::BindOnce(
- &ComponentCloudPolicyService::SetPolicy, service_, std::move(bundle),
- std::make_unique<ComponentPolicyMap>(store_.serialized_policy())));
+ base::BindOnce(&ComponentCloudPolicyService::SetPolicy, service_,
+ std::move(bundle), store_.GetJsonPolicyMap()));
}
void ComponentCloudPolicyService::Backend::UpdateWithLastFetchedPolicy() {
@@ -510,13 +507,13 @@ void ComponentCloudPolicyService::Disconnect() {
void ComponentCloudPolicyService::SetPolicy(
std::unique_ptr<PolicyBundle> policy,
- std::unique_ptr<ComponentPolicyMap> serialized_policy) {
+ const ComponentPolicyMap& component_policy) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Store the current unfiltered policies.
unfiltered_policy_ = std::move(policy);
- NotifyComponentPolicyUpdated(std::move(serialized_policy));
+ NotifyComponentPolicyUpdated(component_policy);
FilterAndInstallPolicy();
}
@@ -539,9 +536,9 @@ void ComponentCloudPolicyService::FilterAndInstallPolicy() {
}
void ComponentCloudPolicyService::NotifyComponentPolicyUpdated(
- std::unique_ptr<ComponentPolicyMap> serialized_policy) {
+ const ComponentPolicyMap& component_policy) {
for (auto& observer : observers_) {
- observer.OnComponentPolicyUpdated(*serialized_policy);
+ observer.OnComponentPolicyUpdated(component_policy);
}
}
diff --git a/chromium/components/policy/core/common/cloud/component_cloud_policy_service.h b/chromium/components/policy/core/common/cloud/component_cloud_policy_service.h
index b705ad47e52..89ebb4f5382 100644
--- a/chromium/components/policy/core/common/cloud/component_cloud_policy_service.h
+++ b/chromium/components/policy/core/common/cloud/component_cloud_policy_service.h
@@ -22,6 +22,7 @@
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/core/common/schema_registry.h"
+#include "components/policy/core/common/values_util.h"
#include "components/policy/policy_export.h"
namespace base {
@@ -148,14 +149,10 @@ class POLICY_EXPORT ComponentCloudPolicyService
void UpdateFromClient();
void UpdateFromSchemaRegistry();
void Disconnect();
- void SetPolicy(
- std::unique_ptr<PolicyBundle> policy,
- std::unique_ptr<ComponentCloudPolicyServiceObserver::ComponentPolicyMap>
- serialized_policy);
+ void SetPolicy(std::unique_ptr<PolicyBundle> policy,
+ const ComponentPolicyMap& component_policy);
void FilterAndInstallPolicy();
- void NotifyComponentPolicyUpdated(
- std::unique_ptr<ComponentCloudPolicyServiceObserver::ComponentPolicyMap>
- serialized_policy);
+ void NotifyComponentPolicyUpdated(const ComponentPolicyMap& component_policy);
std::string policy_type_;
raw_ptr<Delegate> delegate_;
diff --git a/chromium/components/policy/core/common/cloud/component_cloud_policy_service_observer.h b/chromium/components/policy/core/common/cloud/component_cloud_policy_service_observer.h
index 80562a53cc8..48e5e4c763d 100644
--- a/chromium/components/policy/core/common/cloud/component_cloud_policy_service_observer.h
+++ b/chromium/components/policy/core/common/cloud/component_cloud_policy_service_observer.h
@@ -6,6 +6,7 @@
#define COMPONENTS_POLICY_CORE_COMMON_CLOUD_COMPONENT_CLOUD_POLICY_SERVICE_OBSERVER_H_
#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/values_util.h"
namespace policy {
@@ -15,16 +16,12 @@ class ComponentCloudPolicyService;
class POLICY_EXPORT ComponentCloudPolicyServiceObserver
: public base::CheckedObserver {
public:
- using ComponentPolicyMap =
- base::flat_map<policy::PolicyNamespace, std::vector<uint8_t>>;
-
~ComponentCloudPolicyServiceObserver() override = default;
// Called on changes to store->policy() and/or store->policy_map(). The
- // values in the `serialized_policy` map are the serialized
- // PolicyFetchResponse objects received from the server.
+ // values in the `policy` map are the JSON data received from the server.
virtual void OnComponentPolicyUpdated(
- const ComponentPolicyMap& serialized_policy) = 0;
+ const ComponentPolicyMap& component_policy) = 0;
virtual void OnComponentPolicyServiceDestruction(
ComponentCloudPolicyService* service) = 0;
};
diff --git a/chromium/components/policy/core/common/cloud/component_cloud_policy_store.cc b/chromium/components/policy/core/common/cloud/component_cloud_policy_store.cc
index 60d7925c4bb..55194c0a578 100644
--- a/chromium/components/policy/core/common/cloud/component_cloud_policy_store.cc
+++ b/chromium/components/policy/core/common/cloud/component_cloud_policy_store.cc
@@ -23,6 +23,7 @@
#include "components/policy/core/common/cloud/resource_cache.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_proto_decoders.h"
#include "components/policy/proto/chrome_extension_policy.pb.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "crypto/sha2.h"
@@ -80,6 +81,25 @@ const ComponentCloudPolicyStore::DomainConstants* GetDomainConstantsForType(
return nullptr;
}
+base::Value::Dict TranslatePolicyMapEntryToJson(const PolicyMap::Entry& entry) {
+ base::Value::Dict result;
+ // This is actually safe because this code just copies the value,
+ // not caring about its type.
+ result.Set(kValue, entry.value_unsafe()->Clone());
+ if (entry.level == POLICY_LEVEL_RECOMMENDED) {
+ result.Set(kLevel, base::StringPiece(kRecommended));
+ }
+ return result;
+}
+
+base::Value::Dict TranslatePolicyMapToJson(const PolicyMap& policy_map) {
+ base::Value::Dict result;
+ for (const auto& [key, entry] : policy_map) {
+ result.Set(key, TranslatePolicyMapEntryToJson(entry));
+ }
+ return result;
+}
+
} // namespace
ComponentCloudPolicyStore::Delegate::~Delegate() = default;
@@ -197,9 +217,6 @@ void ComponentCloudPolicyStore::Load() {
cached_hashes_[ns] = payload.secure_hash();
stored_policy_times_[ns] =
base::Time::FromJavaTime(policy_data.timestamp());
-
- serialized_policy_[ns] =
- std::vector<uint8_t>(it->second.begin(), it->second.end());
}
delegate_->OnComponentCloudPolicyStoreUpdated();
}
@@ -232,8 +249,6 @@ bool ComponentCloudPolicyStore::Store(const PolicyNamespace& ns,
policy_bundle_.Get(ns).Swap(&policy);
cached_hashes_[ns] = secure_hash;
stored_policy_times_[ns] = base::Time::FromJavaTime(policy_data->timestamp());
- serialized_policy_[ns] =
- std::vector<uint8_t>(serialized_policy.begin(), serialized_policy.end());
delegate_->OnComponentCloudPolicyStoreUpdated();
return true;
}
@@ -250,7 +265,6 @@ void ComponentCloudPolicyStore::Delete(const PolicyNamespace& ns) {
if (!policy_bundle_.Get(ns).empty()) {
policy_bundle_.Get(ns).Clear();
- serialized_policy_.erase(ns);
delegate_->OnComponentCloudPolicyStoreUpdated();
}
}
@@ -286,7 +300,6 @@ void ComponentCloudPolicyStore::Purge(const PurgeFilter& filter) {
cached_hashes_.erase(prev);
DCHECK(stored_policy_times_.count(ns));
stored_policy_times_.erase(ns);
- serialized_policy_.erase(ns);
} else {
++it;
}
@@ -305,7 +318,6 @@ void ComponentCloudPolicyStore::Clear() {
cached_hashes_.clear();
stored_policy_times_.clear();
- serialized_policy_.clear();
const PolicyBundle empty_bundle;
if (!policy_bundle_.Equals(empty_bundle)) {
policy_bundle_.Clear();
@@ -430,37 +442,17 @@ bool ComponentCloudPolicyStore::ParsePolicy(const std::string& data,
return false;
}
- // Each top-level key maps a policy name to its description.
- //
- // Each description is an object that contains the policy value under the
- // "Value" key. The optional "Level" key is either "Mandatory" (default) or
- // "Recommended".
- for (auto it : json.DictItems()) {
- const std::string& policy_name = it.first;
- base::Value description = std::move(it.second);
- if (!description.is_dict()) {
- *error = "The JSON blob dictionary value is not a dictionary.";
- return false;
- }
-
- absl::optional<base::Value> value = description.ExtractKey(kValue);
- if (!value.has_value()) {
- *error = base::StrCat(
- {"The JSON blob dictionary value doesn't contain the required ",
- kValue, " field."});
- return false;
- }
-
- PolicyLevel level = POLICY_LEVEL_MANDATORY;
- const std::string* level_string = description.FindStringKey(kLevel);
- if (level_string && *level_string == kRecommended)
- level = POLICY_LEVEL_RECOMMENDED;
+ return ParseComponentPolicy(std::move(json), domain_constants_->scope,
+ POLICY_SOURCE_CLOUD, policy, error);
+}
- policy->Set(policy_name, level, domain_constants_->scope,
- POLICY_SOURCE_CLOUD, std::move(value.value()), nullptr);
+ComponentPolicyMap ComponentCloudPolicyStore::GetJsonPolicyMap() {
+ ComponentPolicyMap result;
+ for (const auto& [policy_namespace, policy_map] : policy_bundle_) {
+ result[policy_namespace] =
+ base::Value(TranslatePolicyMapToJson(policy_map));
}
-
- return true;
+ return result;
}
} // namespace policy
diff --git a/chromium/components/policy/core/common/cloud/component_cloud_policy_store.h b/chromium/components/policy/core/common/cloud/component_cloud_policy_store.h
index a1f59299fb6..f63a42684a1 100644
--- a/chromium/components/policy/core/common/cloud/component_cloud_policy_store.h
+++ b/chromium/components/policy/core/common/cloud/component_cloud_policy_store.h
@@ -16,6 +16,7 @@
#include "components/policy/core/common/policy_bundle.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/values_util.h"
#include "components/policy/policy_export.h"
namespace enterprise_management {
@@ -78,11 +79,8 @@ class POLICY_EXPORT ComponentCloudPolicyStore {
// The current list of policies.
const PolicyBundle& policy() const { return policy_bundle_; }
- // Returns the map of serialized policy for each namespace.
- const base::flat_map<PolicyNamespace, std::vector<uint8_t>>&
- serialized_policy() {
- return serialized_policy_;
- }
+ // Returns the map of JSON policy value for each namespace.
+ ComponentPolicyMap GetJsonPolicyMap();
// The cached hash for namespace |ns|, or the empty string if |ns| is not
// cached.
@@ -174,9 +172,6 @@ class POLICY_EXPORT ComponentCloudPolicyStore {
// Mapping from policy namespace to policy timestamp for each currently
// exposed component.
std::map<PolicyNamespace, base::Time> stored_policy_times_;
- // Mapping from policy namespace to serialized policy data for each currently
- // exposed component. Only the policy that passed the validation is stored.
- base::flat_map<PolicyNamespace, std::vector<uint8_t>> serialized_policy_;
raw_ptr<const DomainConstants> domain_constants_;
diff --git a/chromium/components/policy/core/common/cloud/device_management_service.cc b/chromium/components/policy/core/common/cloud/device_management_service.cc
index 4128cf74b95..0ee6b828552 100644
--- a/chromium/components/policy/core/common/cloud/device_management_service.cc
+++ b/chromium/components/policy/core/common/cloud/device_management_service.cc
@@ -10,13 +10,13 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
+#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "build/build_config.h"
#include "components/policy/proto/device_management_backend.pb.h"
-#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/url_util.h"
@@ -360,6 +360,10 @@ DeviceManagementService::Job::RetryMethod JobConfigurationBase::ShouldRetry(
return DeviceManagementService::Job::NO_RETRY;
}
+absl::optional<base::TimeDelta> JobConfigurationBase::GetTimeoutDuration() {
+ return timeout_;
+}
+
// A device management service job implementation.
class DeviceManagementService::JobImpl : public Job {
public:
@@ -427,6 +431,8 @@ void DeviceManagementService::JobImpl::CreateUrlLoader() {
url_loader_ = network::SimpleURLLoader::Create(std::move(rr), annotation);
url_loader_->AttachStringForUpload(config_->GetPayload(), kPostContentType);
url_loader_->SetAllowHttpErrorResults(true);
+ if (config_->GetTimeoutDuration())
+ url_loader_->SetTimeoutDuration(config_->GetTimeoutDuration().value());
}
void DeviceManagementService::JobImpl::OnURLLoaderComplete(
diff --git a/chromium/components/policy/core/common/cloud/device_management_service.h b/chromium/components/policy/core/common/cloud/device_management_service.h
index 2b619cc803a..684d6098f3a 100644
--- a/chromium/components/policy/core/common/cloud/device_management_service.h
+++ b/chromium/components/policy/core/common/cloud/device_management_service.h
@@ -276,6 +276,8 @@ class POLICY_EXPORT DeviceManagementService {
int net_error,
int response_code,
const std::string& response_body) = 0;
+
+ virtual absl::optional<base::TimeDelta> GetTimeoutDuration() = 0;
};
explicit DeviceManagementService(
@@ -366,6 +368,7 @@ class POLICY_EXPORT JobConfigurationBase
DeviceManagementService::Job::RetryMethod ShouldRetry(
int response_code,
const std::string& response_body) override;
+ absl::optional<base::TimeDelta> GetTimeoutDuration() override;
protected:
JobConfigurationBase(JobType type,
@@ -383,6 +386,9 @@ class POLICY_EXPORT JobConfigurationBase
// Derived classes should return the base URL for the request.
virtual GURL GetURL(int last_error) const = 0;
+ // Timeout for job request
+ absl::optional<base::TimeDelta> timeout_;
+
private:
JobType type_;
scoped_refptr<network::SharedURLLoaderFactory> factory_;
diff --git a/chromium/components/policy/core/common/cloud/device_management_service_unittest.cc b/chromium/components/policy/core/common/cloud/device_management_service_unittest.cc
index 3525da64855..bb9a223656d 100644
--- a/chromium/components/policy/core/common/cloud/device_management_service_unittest.cc
+++ b/chromium/components/policy/core/common/cloud/device_management_service_unittest.cc
@@ -13,16 +13,18 @@
#include "base/callback_helpers.h"
#include "base/metrics/statistics_recorder.h"
#include "base/run_loop.h"
+#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/dm_auth.h"
#include "components/policy/core/common/cloud/mock_device_management_service.h"
-#include "net/base/escape.h"
+#include "components/policy/core/common/features.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
@@ -62,12 +64,23 @@ const char kEnrollmentToken[] = "enrollment_token";
const char kOAuthAuthorizationHeaderPrefix[] = "OAuth ";
#endif
+// Helper function which generates a DMServer response and populates the
+// `error_detail` field.
+std::string GenerateResponseWithErrorDetail(
+ em::DeviceManagementErrorDetail error_detail) {
+ em::DeviceManagementResponse response;
+ response.add_error_detail(error_detail);
+ return response.SerializeAsString();
+}
+
// Unit tests for the device management policy service. The tests are run
// against a TestURLLoaderFactory that is used to short-circuit the request
// without calling into the actual network stack.
class DeviceManagementServiceTestBase : public testing::Test {
protected:
- DeviceManagementServiceTestBase() {
+ explicit DeviceManagementServiceTestBase(
+ base::test::TaskEnvironment::TimeSource time_source)
+ : task_environment_(time_source) {
// Set retry delay to prevent timeouts.
policy::DeviceManagementService::SetRetryDelayForTesting(0);
@@ -78,6 +91,10 @@ class DeviceManagementServiceTestBase : public testing::Test {
InitializeService();
}
+ DeviceManagementServiceTestBase()
+ : DeviceManagementServiceTestBase(
+ base::test::TaskEnvironment::TimeSource::DEFAULT) {}
+
~DeviceManagementServiceTestBase() override {
service_.reset();
base::RunLoop().RunUntilIdle();
@@ -126,7 +143,8 @@ class DeviceManagementServiceTestBase : public testing::Test {
absl::optional<std::string> oauth_token,
const std::string& payload = std::string(),
DeviceManagementService::Job::RetryMethod method =
- DeviceManagementService::Job::NO_RETRY) {
+ DeviceManagementService::Job::NO_RETRY,
+ base::TimeDelta timeout = base::Seconds(0)) {
last_job_type_ =
DeviceManagementService::JobConfiguration::GetJobTypeAsString(type);
std::unique_ptr<FakeJobConfiguration> config =
@@ -142,6 +160,7 @@ class DeviceManagementServiceTestBase : public testing::Test {
base::Unretained(this)));
config->SetRequestPayload(payload);
config->SetShouldRetryResponse(method);
+ config->SetTimeoutDuration(timeout);
return service_->CreateJob(std::move(config));
}
@@ -166,11 +185,12 @@ class DeviceManagementServiceTestBase : public testing::Test {
std::unique_ptr<DeviceManagementService::Job> StartTokenEnrollmentJob(
const std::string& payload = std::string(),
DeviceManagementService::Job::RetryMethod method =
- DeviceManagementService::Job::NO_RETRY) {
+ DeviceManagementService::Job::NO_RETRY,
+ base::TimeDelta timeout = base::Seconds(0)) {
return StartJob(
DeviceManagementService::JobConfiguration::TYPE_TOKEN_ENROLLMENT,
/*critical=*/false, DMAuth::FromEnrollmentToken(kEnrollmentToken),
- std::string(), payload, method);
+ std::string(), payload, method, timeout);
}
std::unique_ptr<DeviceManagementService::Job> StartApiAuthCodeFetchJob(
@@ -287,7 +307,7 @@ class DeviceManagementServiceTestBase : public testing::Test {
MOCK_METHOD4(OnJobDone,
void(DeviceManagementService::Job*,
DeviceManagementStatus,
- int,
+ int /*net_error*/,
const std::string&));
MOCK_METHOD2(OnJobRetry,
@@ -336,7 +356,15 @@ void PrintTo(const FailedRequestParams& params, std::ostream* os) {
// the same for all kinds of requests.
class DeviceManagementServiceFailedRequestTest
: public DeviceManagementServiceTestBase,
- public testing::WithParamInterface<FailedRequestParams> {};
+ public testing::WithParamInterface<FailedRequestParams> {
+ protected:
+ DeviceManagementServiceFailedRequestTest() {
+ feature_list_.InitAndEnableFeature(features::kDmTokenDeletion);
+ }
+
+ private:
+ base::test::ScopedFeatureList feature_list_;
+};
TEST_P(DeviceManagementServiceFailedRequestTest, RegisterRequest) {
EXPECT_CALL(*this, OnJobDone(_, GetParam().expected_status_, _, _));
@@ -441,6 +469,10 @@ INSTANTIATE_TEST_SUITE_P(
DeviceManagementServiceFailedRequestTest,
testing::Values(
FailedRequestParams(DM_STATUS_REQUEST_FAILED, net::ERR_FAILED, 0, ""),
+ FailedRequestParams(DM_STATUS_REQUEST_FAILED,
+ net::ERR_TIMED_OUT,
+ 0,
+ ""),
FailedRequestParams(DM_STATUS_HTTP_STATUS_ERROR,
net::OK,
666,
@@ -461,6 +493,22 @@ INSTANTIATE_TEST_SUITE_P(
net::OK,
410,
PROTO_STRING(kResponseEmpty)),
+ FailedRequestParams(
+ DM_STATUS_SERVICE_DEVICE_NOT_FOUND,
+ net::OK,
+ 410,
+ GenerateResponseWithErrorDetail(
+ em::CBCM_DELETION_POLICY_PREFERENCE_INVALIDATE_TOKEN)),
+ FailedRequestParams(
+#if BUILDFLAG(IS_CHROMEOS)
+ DM_STATUS_SERVICE_DEVICE_NOT_FOUND,
+#else // BUILDFLAG(IS_CHROMEOS)
+ DM_STATUS_SERVICE_DEVICE_NEEDS_RESET,
+#endif // BUILDFLAG(IS_CHROMEOS)
+ net::OK,
+ 410,
+ GenerateResponseWithErrorDetail(
+ em::CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN)),
FailedRequestParams(DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID,
net::OK,
401,
@@ -528,11 +576,11 @@ class QueryParams {
std::vector<std::string> GetParams(const std::string& name) {
std::vector<std::string> results;
for (const auto& param : params_) {
- std::string unescaped_name = net::UnescapeBinaryURLComponent(
- param.first, net::UnescapeRule::REPLACE_PLUS_WITH_SPACE);
+ std::string unescaped_name = base::UnescapeBinaryURLComponent(
+ param.first, base::UnescapeRule::REPLACE_PLUS_WITH_SPACE);
if (unescaped_name == name) {
- std::string value = net::UnescapeBinaryURLComponent(
- param.second, net::UnescapeRule::REPLACE_PLUS_WITH_SPACE);
+ std::string value = base::UnescapeBinaryURLComponent(
+ param.second, base::UnescapeRule::REPLACE_PLUS_WITH_SPACE);
results.push_back(value);
}
}
@@ -1219,4 +1267,30 @@ TEST_F(DeviceManagementRequestAuthTest, CannotUseOAuthTokenAsAuthData) {
}
#endif // GTEST_HAS_DEATH_TEST
+class DeviceManagementServiceTestWithTimeManipulation
+ : public DeviceManagementServiceTestBase {
+ protected:
+ DeviceManagementServiceTestWithTimeManipulation()
+ : DeviceManagementServiceTestBase(
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+ base::TimeDelta GetTimeoutDuration() const { return timeout_test_duration_; }
+ static constexpr base::TimeDelta timeout_test_duration_ = base::Seconds(30);
+};
+
+TEST_F(DeviceManagementServiceTestWithTimeManipulation,
+ TokenEnrollmentRequestWithTimeout) {
+ // In enrollment timeout cases, expected status is DM_STATUS_REQUEST_FAILED,
+ // and expected net error is NET_ERROR(TIMED_OUT, -7)
+ EXPECT_CALL(*this, OnJobDone(_, DM_STATUS_REQUEST_FAILED, _, ""));
+ EXPECT_CALL(*this, OnJobRetry(_, _)).Times(0);
+
+ std::unique_ptr<DeviceManagementService::Job> request_job(
+ StartTokenEnrollmentJob("", DeviceManagementService::Job::NO_RETRY,
+ GetTimeoutDuration()));
+ ASSERT_TRUE(GetPendingRequest());
+
+ // fast forward 30+ seconds
+ task_environment_.FastForwardBy(GetTimeoutDuration() + base::Seconds(1));
+}
+
} // namespace policy
diff --git a/chromium/components/policy/core/common/cloud/dm_token.cc b/chromium/components/policy/core/common/cloud/dm_token.cc
index 0b9aa782720..12c41c95ec8 100644
--- a/chromium/components/policy/core/common/cloud/dm_token.cc
+++ b/chromium/components/policy/core/common/cloud/dm_token.cc
@@ -4,6 +4,8 @@
#include "components/policy/core/common/cloud/dm_token.h"
+#include "base/check.h"
+
namespace policy {
// static
diff --git a/chromium/components/policy/core/common/cloud/dmserver_job_configurations.cc b/chromium/components/policy/core/common/cloud/dmserver_job_configurations.cc
index a654ad3e54e..02d5ca53a06 100644
--- a/chromium/components/policy/core/common/cloud/dmserver_job_configurations.cc
+++ b/chromium/components/policy/core/common/cloud/dmserver_job_configurations.cc
@@ -4,10 +4,12 @@
#include "components/policy/core/common/cloud/dmserver_job_configurations.h"
+#include "base/feature_list.h"
#include "base/strings/string_number_conversions.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/device_management_service.h"
+#include "components/policy/core/common/features.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "net/base/url_util.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -151,87 +153,78 @@ DMServerJobConfiguration::DMServerJobConfiguration(
DMServerJobConfiguration::~DMServerJobConfiguration() {}
DeviceManagementStatus
-DMServerJobConfiguration::MapNetErrorAndResponseCodeToDMStatus(
+DMServerJobConfiguration::MapNetErrorAndResponseToDMStatus(
int net_error,
- int response_code) {
- DeviceManagementStatus code;
- if (net_error != net::OK) {
- code = DM_STATUS_REQUEST_FAILED;
- } else {
- switch (response_code) {
- case DeviceManagementService::kSuccess:
- code = DM_STATUS_SUCCESS;
- break;
- case DeviceManagementService::kInvalidArgument:
- code = DM_STATUS_REQUEST_INVALID;
- break;
- case DeviceManagementService::kInvalidAuthCookieOrDMToken:
- code = DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID;
- break;
- case DeviceManagementService::kMissingLicenses:
- code = DM_STATUS_SERVICE_MISSING_LICENSES;
- break;
- case DeviceManagementService::kDeviceManagementNotAllowed:
- code = DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED;
- break;
- case DeviceManagementService::kPendingApproval:
- code = DM_STATUS_SERVICE_ACTIVATION_PENDING;
- break;
- case DeviceManagementService::kRequestTooLarge:
- code = DM_STATUS_REQUEST_TOO_LARGE;
- break;
- case DeviceManagementService::kConsumerAccountWithPackagedLicense:
- code = DM_STATUS_SERVICE_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE;
- break;
- case DeviceManagementService::kInvalidURL:
- case DeviceManagementService::kInternalServerError:
- case DeviceManagementService::kServiceUnavailable:
- code = DM_STATUS_TEMPORARY_UNAVAILABLE;
- break;
- case DeviceManagementService::kDeviceNotFound:
- code = DM_STATUS_SERVICE_DEVICE_NOT_FOUND;
- break;
- case DeviceManagementService::kPolicyNotFound:
- code = DM_STATUS_SERVICE_POLICY_NOT_FOUND;
- break;
- case DeviceManagementService::kInvalidSerialNumber:
- code = DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER;
- break;
- case DeviceManagementService::kTooManyRequests:
- code = DM_STATUS_SERVICE_TOO_MANY_REQUESTS;
- break;
- case DeviceManagementService::kDomainMismatch:
- code = DM_STATUS_SERVICE_DOMAIN_MISMATCH;
- break;
- case DeviceManagementService::kDeprovisioned:
- code = DM_STATUS_SERVICE_DEPROVISIONED;
- break;
- case DeviceManagementService::kDeviceIdConflict:
- code = DM_STATUS_SERVICE_DEVICE_ID_CONFLICT;
- break;
- case DeviceManagementService::kArcDisabled:
- code = DM_STATUS_SERVICE_ARC_DISABLED;
- break;
- case DeviceManagementService::kInvalidDomainlessCustomer:
- code = DM_STATUS_SERVICE_ENTERPRISE_ACCOUNT_IS_NOT_ELIGIBLE_TO_ENROLL;
- break;
- case DeviceManagementService::kTosHasNotBeenAccepted:
- code = DM_STATUS_SERVICE_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED;
- break;
- case DeviceManagementService::kIllegalAccountForPackagedEDULicense:
- code = DM_STATUS_SERVICE_ILLEGAL_ACCOUNT_FOR_PACKAGED_EDU_LICENSE;
- break;
- default:
- // Handle all unknown 5xx HTTP error codes as temporary and any other
- // unknown error as one that needs more time to recover.
- if (response_code >= 500 && response_code <= 599)
- code = DM_STATUS_TEMPORARY_UNAVAILABLE;
- else
- code = DM_STATUS_HTTP_STATUS_ERROR;
- break;
+ int response_code,
+ const std::string& response_body) {
+ if (net_error != net::OK)
+ return DM_STATUS_REQUEST_FAILED;
+
+ switch (response_code) {
+ case DeviceManagementService::kSuccess:
+ return DM_STATUS_SUCCESS;
+ case DeviceManagementService::kInvalidArgument:
+ return DM_STATUS_REQUEST_INVALID;
+ case DeviceManagementService::kInvalidAuthCookieOrDMToken:
+ return DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID;
+ case DeviceManagementService::kMissingLicenses:
+ return DM_STATUS_SERVICE_MISSING_LICENSES;
+ case DeviceManagementService::kDeviceManagementNotAllowed:
+ return DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED;
+ case DeviceManagementService::kPendingApproval:
+ return DM_STATUS_SERVICE_ACTIVATION_PENDING;
+ case DeviceManagementService::kRequestTooLarge:
+ return DM_STATUS_REQUEST_TOO_LARGE;
+ case DeviceManagementService::kConsumerAccountWithPackagedLicense:
+ return DM_STATUS_SERVICE_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE;
+ case DeviceManagementService::kInvalidURL:
+ case DeviceManagementService::kInternalServerError:
+ case DeviceManagementService::kServiceUnavailable:
+ return DM_STATUS_TEMPORARY_UNAVAILABLE;
+ case DeviceManagementService::kDeviceNotFound: {
+#if !BUILDFLAG(IS_CHROMEOS)
+ // The `kDeviceNotFound` response code can correspond to different DM
+ // statuses depending on the contents of the response body.
+ em::DeviceManagementResponse response;
+ if (base::FeatureList::IsEnabled(features::kDmTokenDeletion) &&
+ response.ParseFromString(response_body) &&
+ std::find(response.error_detail().begin(),
+ response.error_detail().end(),
+ em::CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN) !=
+ response.error_detail().end()) {
+ return DM_STATUS_SERVICE_DEVICE_NEEDS_RESET;
+ }
+#endif // !BUILDFLAG(IS_CHROMEOS)
+ return DM_STATUS_SERVICE_DEVICE_NOT_FOUND;
}
+ case DeviceManagementService::kPolicyNotFound:
+ return DM_STATUS_SERVICE_POLICY_NOT_FOUND;
+ case DeviceManagementService::kInvalidSerialNumber:
+ return DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER;
+ case DeviceManagementService::kTooManyRequests:
+ return DM_STATUS_SERVICE_TOO_MANY_REQUESTS;
+ case DeviceManagementService::kDomainMismatch:
+ return DM_STATUS_SERVICE_DOMAIN_MISMATCH;
+ case DeviceManagementService::kDeprovisioned:
+ return DM_STATUS_SERVICE_DEPROVISIONED;
+ case DeviceManagementService::kDeviceIdConflict:
+ return DM_STATUS_SERVICE_DEVICE_ID_CONFLICT;
+ case DeviceManagementService::kArcDisabled:
+ return DM_STATUS_SERVICE_ARC_DISABLED;
+ case DeviceManagementService::kInvalidDomainlessCustomer:
+ return DM_STATUS_SERVICE_ENTERPRISE_ACCOUNT_IS_NOT_ELIGIBLE_TO_ENROLL;
+ case DeviceManagementService::kTosHasNotBeenAccepted:
+ return DM_STATUS_SERVICE_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED;
+ case DeviceManagementService::kIllegalAccountForPackagedEDULicense:
+ return DM_STATUS_SERVICE_ILLEGAL_ACCOUNT_FOR_PACKAGED_EDU_LICENSE;
+ default:
+ // Handle all unknown 5xx HTTP error codes as temporary and any other
+ // unknown error as one that needs more time to recover.
+ if (response_code >= 500 && response_code <= 599)
+ return DM_STATUS_TEMPORARY_UNAVAILABLE;
+
+ return DM_STATUS_HTTP_STATUS_ERROR;
}
- return code;
}
std::string DMServerJobConfiguration::GetPayload() {
@@ -250,7 +243,7 @@ void DMServerJobConfiguration::OnURLLoadComplete(
int response_code,
const std::string& response_body) {
DeviceManagementStatus code =
- MapNetErrorAndResponseCodeToDMStatus(net_error, response_code);
+ MapNetErrorAndResponseToDMStatus(net_error, response_code, response_body);
em::DeviceManagementResponse response;
if (code == DM_STATUS_SUCCESS && !response.ParseFromString(response_body)) {
@@ -299,6 +292,10 @@ RegistrationJobConfiguration::RegistrationJobConfiguration(
oauth_token,
std::move(callback)) {}
+void RegistrationJobConfiguration::SetTimeoutDuration(base::TimeDelta timeout) {
+ timeout_ = timeout;
+}
+
void RegistrationJobConfiguration::OnBeforeRetry(
int response_code,
const std::string& response_body) {
diff --git a/chromium/components/policy/core/common/cloud/dmserver_job_configurations.h b/chromium/components/policy/core/common/cloud/dmserver_job_configurations.h
index 1411db8f616..9663e97d638 100644
--- a/chromium/components/policy/core/common/cloud/dmserver_job_configurations.h
+++ b/chromium/components/policy/core/common/cloud/dmserver_job_configurations.h
@@ -66,9 +66,10 @@ class POLICY_EXPORT DMServerJobConfiguration : public JobConfigurationBase {
}
protected:
- DeviceManagementStatus MapNetErrorAndResponseCodeToDMStatus(
+ DeviceManagementStatus MapNetErrorAndResponseToDMStatus(
int net_error,
- int response_code);
+ int response_code,
+ const std::string& response_body);
private:
// JobConfiguration interface.
@@ -106,6 +107,8 @@ class POLICY_EXPORT RegistrationJobConfiguration
RegistrationJobConfiguration& operator=(const RegistrationJobConfiguration&) =
delete;
+ void SetTimeoutDuration(base::TimeDelta timeout);
+
private:
// JobConfiguration interface.
void OnBeforeRetry(int response_code,
diff --git a/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc b/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc
index 45d5535630f..3700f19df2f 100644
--- a/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc
+++ b/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc
@@ -6,10 +6,12 @@
#include "base/base64.h"
#include "base/containers/contains.h"
+#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/reporting/proto/synced/record_constants.pb.h"
+#include "net/base/backoff_entry.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -19,10 +21,120 @@ namespace {
// EncryptedReportingJobConfiguration strings
constexpr char kEncryptedRecordListKey[] = "encryptedRecord";
+constexpr char kSequenceInformationKey[] = "sequenceInformation";
+constexpr char kSequenceId[] = "sequencingId";
+constexpr char kGenerationId[] = "generationId";
+constexpr char kPriority[] = "priority";
constexpr char kAttachEncryptionSettingsKey[] = "attachEncryptionSettings";
constexpr char kDeviceKey[] = "device";
constexpr char kBrowserKey[] = "browser";
+// Generate new backoff entry.
+std::unique_ptr<::net::BackoffEntry> GetBackoffEntry(
+ ::reporting::Priority priority) {
+ // Retry policy for SECURITY queue.
+ static const ::net::BackoffEntry::Policy kSecurityUploadBackoffPolicy = {
+ // Number of initial errors to ignore before applying
+ // exponential back-off rules.
+ /*num_errors_to_ignore=*/0,
+
+ // Initial delay is 10 seconds.
+ /*initial_delay_ms=*/10 * 1000,
+
+ // Factor by which the waiting time will be multiplied.
+ /*multiply_factor=*/2,
+
+ // Fuzzing percentage.
+ /*jitter_factor=*/0.1,
+
+ // Maximum delay is 1 minute.
+ /*maximum_backoff_ms=*/1 * 60 * 1000,
+
+ // It's up to the caller to reset the backoff time.
+ /*entry_lifetime_ms=*/-1,
+
+ /*always_use_initial_delay=*/true,
+ };
+ // Retry policy for all other queues, including initial key delivery.
+ static const ::net::BackoffEntry::Policy kDefaultUploadBackoffPolicy = {
+ // Number of initial errors to ignore before applying
+ // exponential back-off rules.
+ /*num_errors_to_ignore=*/0,
+
+ // Initial delay is 10 seconds.
+ /*initial_delay_ms=*/10 * 1000,
+
+ // Factor by which the waiting time will be multiplied.
+ /*multiply_factor=*/2,
+
+ // Fuzzing percentage.
+ /*jitter_factor=*/0.1,
+
+ // Maximum delay is 24 hours.
+ /*maximum_backoff_ms=*/24 * 60 * 60 * 1000,
+
+ // It's up to the caller to reset the backoff time.
+ /*entry_lifetime_ms=*/-1,
+
+ /*always_use_initial_delay=*/true,
+ };
+ // Maximum backoff is set per priority. Current proposal is to set SECURITY
+ // events to be backed off only slightly: max delay is set to 1 minute.
+ // For all other priorities max delay is set to 24 hours.
+ auto backoff_entry = std::make_unique<::net::BackoffEntry>(
+ priority == ::reporting::SECURITY ? &kSecurityUploadBackoffPolicy
+ : &kDefaultUploadBackoffPolicy);
+ return backoff_entry;
+}
+
+// State of single priority queue uploads.
+// It is a singleton, protected implicitly by the fact that all relevant
+// EncryptedReportingJobConfiguration actions are called on the sequenced task
+// runner.
+struct UploadState {
+ // Highest sequence id that has been posted for upload.
+ int64_t last_sequence_id;
+ // Generation id that has been posted for upload.
+ int64_t last_generation_id;
+
+ // Time when the next request will be allowed.
+ // This is essentially the cache value of the backoff->GetReleaseTime().
+ // When the time is reached, one request is allowed, backoff is updated as if
+ // the request failed, and the new release time is cached.
+ base::TimeTicks earliest_retry_timestamp;
+
+ // Current backoff entry for this prioririty.
+ std::unique_ptr<::net::BackoffEntry> backoff_entry;
+};
+// Map of all the queues states.
+using UploadStateMap = base::flat_map<::reporting::Priority, UploadState>;
+
+UploadStateMap* state_map() {
+ static base::NoDestructor<UploadStateMap> map;
+ return map.get();
+}
+
+UploadState* AccessState(::reporting::Priority priority,
+ int64_t generation_id,
+ int64_t sequence_id) {
+ auto state_it = state_map()->find(priority);
+ if (state_it == state_map()->end() ||
+ state_it->second.last_generation_id != generation_id) {
+ // This priority pops up for the first time or (rare case) generation has
+ // changed. Record new state and allow upload.
+ state_it = state_map()
+ ->insert_or_assign(
+ priority,
+ UploadState{.last_sequence_id = sequence_id,
+ .last_generation_id = generation_id,
+ .backoff_entry = GetBackoffEntry(priority)})
+ .first;
+ state_it->second.earliest_retry_timestamp =
+ state_it->second.backoff_entry->GetReleaseTime();
+ }
+ return &state_it->second;
+}
+
} // namespace
EncryptedReportingJobConfiguration::EncryptedReportingJobConfiguration(
@@ -38,6 +150,42 @@ EncryptedReportingJobConfiguration::EncryptedReportingJobConfiguration(
std::move(complete_cb)) {
// Merge it into the base class payload.
payload_.Merge(merging_payload);
+ // Retrieve priorities and figure out maximum sequence id for each.
+ // Payload is expected to be correctly formed, any malformed piece is ignored.
+ // TODO(b/214040103): if batching is enabled, multiple priorities may be
+ // found. Before that, each payload can only have no more than one, and the
+ // highest sequence id comes from the last record.
+ // TODO(b/232455728): if test_request_payload is moved to components/
+ // we would be able to use it here.
+ const auto* const encrypted_record_list =
+ payload_.FindList(kEncryptedRecordListKey);
+ // If there are no records, assume UNDEFINED priority and seq_id = -1.
+ priority_ = ::reporting::UNDEFINED_PRIORITY;
+ generation_id_ = -1;
+ sequence_id_ = -1;
+ if (encrypted_record_list != nullptr && !encrypted_record_list->empty()) {
+ const auto sequence_information_it =
+ std::prev(encrypted_record_list->cend());
+ const auto* const sequence_information =
+ sequence_information_it->GetDict().FindDict(kSequenceInformationKey);
+ if (sequence_information != nullptr) {
+ const auto maybe_priority = sequence_information->FindInt(kPriority);
+ auto* const generation_id_ptr =
+ sequence_information->FindString(kGenerationId);
+ auto* const sequence_id_ptr =
+ sequence_information->FindString(kSequenceId);
+ if (maybe_priority.has_value() &&
+ ::reporting::Priority_IsValid(maybe_priority.value())) {
+ priority_ = static_cast<::reporting::Priority>(maybe_priority.value());
+ }
+ if (generation_id_ptr != nullptr) {
+ base::StringToInt64(*generation_id_ptr, &generation_id_);
+ }
+ if (sequence_id_ptr != nullptr) {
+ base::StringToInt64(*sequence_id_ptr, &sequence_id_);
+ }
+ }
+ }
}
EncryptedReportingJobConfiguration::~EncryptedReportingJobConfiguration() {
@@ -46,7 +194,7 @@ EncryptedReportingJobConfiguration::~EncryptedReportingJobConfiguration() {
// failure to the callback.
std::move(callback_).Run(/*job=*/nullptr,
DeviceManagementStatus::DM_STATUS_REQUEST_FAILED,
- /*net_error=*/418,
+ /*response_code=*/418,
/*response_body=*/absl::nullopt);
}
}
@@ -67,6 +215,92 @@ void EncryptedReportingJobConfiguration::UpdateContext(
context_ = std::move(context);
}
+base::TimeDelta EncryptedReportingJobConfiguration::WhenIsAllowedToProceed()
+ const {
+ // Now pick up the state.
+ const auto* const state =
+ AccessState(priority_, generation_id_, sequence_id_);
+ // Use and update previously recorded state, base upload decision on it.
+ if (state->last_sequence_id > sequence_id_) {
+ // Sequence id decreased, the upload is outdated, reject it forever.
+ return base::TimeDelta::Max();
+ }
+ if (state->last_sequence_id < sequence_id_) {
+ // Sequence id increased, keep validating.
+ switch (priority_) {
+ case ::reporting::SECURITY:
+ // For SECURITY events the request is allowed.
+ return base::TimeDelta(); // 0 - allowed right away.
+ default: {
+ // For all other priorities we will act like in case of request’s
+ // last_sequence_id is == last_sequence_id above - observing the
+ // backoff time expiration.
+ }
+ }
+ }
+ // Allow upload only if earliest retry time has passed.
+ // Return delta till the allowed time - if positive, upload is going to be
+ // rejected.
+ return state->earliest_retry_timestamp -
+ state->backoff_entry->GetTimeTicksNow();
+}
+
+void EncryptedReportingJobConfiguration::CancelNotAllowedJob() {
+ std::move(callback_).Run(
+ /*job=*/nullptr, DeviceManagementStatus::DM_STATUS_REQUEST_FAILED,
+ /*response_code=*/DeviceManagementService::kTooManyRequests,
+ /*response_body=*/absl::nullopt);
+}
+
+void EncryptedReportingJobConfiguration::AccountForAllowedJob() {
+ auto* const state = AccessState(priority_, generation_id_, sequence_id_);
+ // Update state to reflect highest sequence_id_ (we never allow upload with
+ // lower sequence_id_).
+ state->last_sequence_id = sequence_id_;
+ // Calculate delay as exponential backoff (based on the retry_count).
+ // Update backoff under assumption that this request fails.
+ // If it is responded successfully, we will reset it.
+ state->backoff_entry->InformOfRequest(/*succeeded=*/false);
+ state->earliest_retry_timestamp = state->backoff_entry->GetReleaseTime();
+}
+
+DeviceManagementService::Job::RetryMethod
+EncryptedReportingJobConfiguration::ShouldRetry(
+ int response_code,
+ const std::string& response_body) {
+ // Do not retry on the Job level - ERP has its own retry mechanism.
+ return DeviceManagementService::Job::NO_RETRY;
+}
+
+void EncryptedReportingJobConfiguration::OnURLLoadComplete(
+ DeviceManagementService::Job* job,
+ int net_error,
+ int response_code,
+ const std::string& response_body) {
+ // Analyze the net error and update upload state for possible future retries.
+ auto* const state = AccessState(priority_, generation_id_, sequence_id_);
+ if (net_error != ::net::OK) {
+ // Network error
+ } else if (response_code >= 400 && response_code <= 499 &&
+ response_code != 409 /* Overlapping seq_id ranges detected */) {
+ // Permanent error code returned by server, impose artificial 24h backoff.
+ state->backoff_entry->SetCustomReleaseTime(
+ state->backoff_entry->GetTimeTicksNow() + base::Days(1));
+ }
+ // For all other cases keep the currently set retry time.
+ // In case of success, inform backoff entry about that.
+ if (net_error == ::net::OK &&
+ response_code == DeviceManagementService::kSuccess) {
+ state->backoff_entry->InformOfRequest(/*succeeded=*/true);
+ }
+ // Cache earliest retry time based on the current backoff entry.
+ state->earliest_retry_timestamp = state->backoff_entry->GetReleaseTime();
+
+ // Then deliver response and status by making a call to the base class.
+ ReportingJobConfigurationBase::OnURLLoadComplete(
+ job, net_error, response_code, response_body);
+}
+
std::string EncryptedReportingJobConfiguration::GetUmaString() const {
return "Enterprise.EncryptedReportingSuccess";
}
@@ -79,4 +313,9 @@ EncryptedReportingJobConfiguration::GetTopLevelKeyAllowList() {
return kTopLevelKeyAllowList;
}
+// static
+void EncryptedReportingJobConfiguration::ResetUploadsStateForTest() {
+ state_map()->clear();
+}
+
} // namespace policy
diff --git a/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.h b/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.h
index be6efc526a2..361503375cb 100644
--- a/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.h
+++ b/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration.h
@@ -9,6 +9,7 @@
#include <string>
#include "base/callback.h"
+#include "base/time/time.h"
#include "base/values.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/cloud/device_management_service.h"
@@ -18,7 +19,7 @@
namespace policy {
-// {{{Note}}} ERP Payload Overview
+// {{{Note}}} ERP Request Payload Overview
//
// EncryptedReportingJobConfiguration configures a payload for the Encrypted
// server endpoint. A JSON version of the payload looks like this:
@@ -93,15 +94,43 @@ class POLICY_EXPORT EncryptedReportingJobConfiguration
// fields (check reporting::GetContext for specifics).
void UpdateContext(base::Value::Dict context);
+ // Checks the new job against the history, determines how soon the upload will
+ // be allowed. Returns positive value if not allowed, and 0 or negative
+ // otherwise.
+ base::TimeDelta WhenIsAllowedToProceed() const;
+
+ // Account for the job, that was allowed to proceed.
+ void AccountForAllowedJob();
+
+ // Cancels the job, that was not allowed to proceed.
+ void CancelNotAllowedJob();
+
+ // Callback to process error codes and, in case of success, response body.
+ void OnURLLoadComplete(DeviceManagementService::Job* job,
+ int net_error,
+ int response_code,
+ const std::string& response_body) override;
+
+ // Test-only method that resets collected uploads state.
+ static void ResetUploadsStateForTest();
+
protected:
void UpdatePayloadBeforeGetInternal() override;
+ // DeviceManagementService::JobConfiguration
+ DeviceManagementService::Job::RetryMethod ShouldRetry(
+ int response_code,
+ const std::string& response_body) override;
+
std::string GetUmaString() const override;
private:
- friend class EncryptedReportingJobConfigurationTest;
-
std::set<std::string> GetTopLevelKeyAllowList();
+
+ // Parameters populated from the payload_.
+ ::reporting::Priority priority_;
+ int64_t generation_id_{-1};
+ int64_t sequence_id_{-1};
};
} // namespace policy
diff --git a/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration_unittest.cc b/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration_unittest.cc
index ff0f3729ac1..f9add066176 100644
--- a/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration_unittest.cc
+++ b/chromium/components/policy/core/common/cloud/encrypted_reporting_job_configuration_unittest.cc
@@ -11,6 +11,7 @@
#include "base/test/task_environment.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/cloud_policy_util.h"
#include "components/policy/core/common/cloud/dm_auth.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
@@ -28,6 +29,7 @@
using ::testing::_;
using ::testing::ByRef;
using ::testing::Eq;
+using ::testing::Ge;
using ::testing::IsNull;
using ::testing::MockFunction;
using ::testing::NotNull;
@@ -38,7 +40,7 @@ using ::testing::StrictMock;
namespace policy {
namespace {
-constexpr uint64_t kGenerationId = 4321;
+constexpr int64_t kGenerationId = 4321;
constexpr ::reporting::Priority kPriority = ::reporting::Priority::IMMEDIATE;
// Default values for EncryptionInfo
@@ -55,9 +57,9 @@ constexpr char kEncryptedRecordListKey[] = "encryptedRecord";
// Encryption settings request key
constexpr char kAttachEncryptionSettingsKey[] = "attachEncryptionSettings";
-// Keys for EncrypedRecord
+// Keys for EncryptedRecord
constexpr char kEncryptedWrappedRecordKey[] = "encryptedWrappedRecord";
-constexpr char kSequenceInformationKey[] = "sequencingInformation";
+constexpr char kSequenceInformationKey[] = "sequenceInformation";
constexpr char kEncryptionInfoKey[] = "encryptionInfo";
// Keys for internal encryption information dictionaries.
@@ -82,29 +84,6 @@ uint64_t GetNextSequenceId() {
return kSequencingId++;
}
-base::Value GenerateSingleRecord(base::StringPiece encrypted_wrapped_record) {
- base::Value record_dictionary{base::Value::Type::DICTIONARY};
- std::string base64_encode;
- base::Base64Encode(encrypted_wrapped_record, &base64_encode);
- record_dictionary.SetStringKey(kEncryptedWrappedRecordKey, base64_encode);
-
- base::Value* const sequencing_dictionary = record_dictionary.SetKey(
- kSequenceInformationKey, base::Value{base::Value::Type::DICTIONARY});
- sequencing_dictionary->SetStringKey(
- kSequencingIdKey, base::NumberToString(GetNextSequenceId()));
- sequencing_dictionary->SetStringKey(kGenerationIdKey,
- base::NumberToString(kGenerationId));
- sequencing_dictionary->SetIntKey(kPriorityKey, kPriority);
-
- base::Value* const encryption_info_dictionary = record_dictionary.SetKey(
- kEncryptionInfoKey, base::Value{base::Value::Type::DICTIONARY});
- encryption_info_dictionary->SetStringKey(kEncryptionKey, kEncryptionKeyValue);
- encryption_info_dictionary->SetStringKey(
- kPublicKeyIdKey, base::NumberToString(kPublicKeyIdValue));
-
- return record_dictionary;
-}
-
class RequestPayloadBuilder {
public:
explicit RequestPayloadBuilder(bool attach_encryption_settings = false) {
@@ -238,10 +217,61 @@ class EncryptedReportingJobConfigurationTest : public testing::Test {
}
protected:
- using MockCompleteCb = MockFunction<void(DeviceManagementService::Job* job,
+ using MockCompleteCb = MockFunction<void(DeviceManagementService::Job* upload,
DeviceManagementStatus code,
- int net_error,
+ int response_code,
absl::optional<base::Value::Dict>)>;
+ struct TestUpload {
+ std::unique_ptr<EncryptedReportingJobConfiguration> configuration;
+ std::unique_ptr<StrictMock<MockCompleteCb>> completion_cb;
+ base::Value::Dict response;
+ DeviceManagementService::Job job;
+ };
+
+ void SetUp() override {
+ EncryptedReportingJobConfiguration::ResetUploadsStateForTest();
+ }
+
+ TestUpload CreateTestUpload(const base::Value& record_value) {
+ TestUpload test_upload;
+ test_upload.response = ResponseValueBuilder::CreateResponse(
+ *record_value.FindDictKey(kSequenceInformationKey), absl::nullopt);
+ test_upload.completion_cb = std::make_unique<StrictMock<MockCompleteCb>>();
+ test_upload.configuration =
+ std::make_unique<EncryptedReportingJobConfiguration>(
+ &client_,
+ service_.configuration()->GetEncryptedReportingServerUrl(),
+ RequestPayloadBuilder().AddRecord(record_value).Build(),
+ base::BindOnce(&MockCompleteCb::Call,
+ base::Unretained(test_upload.completion_cb.get())));
+ return test_upload;
+ }
+
+ base::Value GenerateSingleRecord(base::StringPiece encrypted_wrapped_record,
+ ::reporting::Priority priority = kPriority) {
+ base::Value record_dictionary{base::Value::Type::DICTIONARY};
+ std::string base64_encode;
+ base::Base64Encode(encrypted_wrapped_record, &base64_encode);
+ record_dictionary.SetStringKey(kEncryptedWrappedRecordKey, base64_encode);
+
+ base::Value* const sequencing_dictionary = record_dictionary.SetKey(
+ kSequenceInformationKey, base::Value{base::Value::Type::DICTIONARY});
+ sequencing_dictionary->SetStringKey(
+ kSequencingIdKey, base::NumberToString(GetNextSequenceId()));
+ sequencing_dictionary->SetStringKey(kGenerationIdKey,
+ base::NumberToString(kGenerationId));
+ sequencing_dictionary->SetIntKey(kPriorityKey, priority);
+
+ base::Value* const encryption_info_dictionary = record_dictionary.SetKey(
+ kEncryptionInfoKey, base::Value{base::Value::Type::DICTIONARY});
+ encryption_info_dictionary->SetStringKey(kEncryptionKey,
+ kEncryptionKeyValue);
+ encryption_info_dictionary->SetStringKey(
+ kPublicKeyIdKey, base::NumberToString(kPublicKeyIdValue));
+
+ return record_dictionary;
+ }
+
static base::Value::Dict GenerateContext(base::StringPiece key,
base::StringPiece value) {
base::Value::Dict context;
@@ -274,7 +304,8 @@ class EncryptedReportingJobConfigurationTest : public testing::Test {
return &payload_;
}
- base::test::SingleThreadTaskEnvironment task_environment_;
+ base::test::SingleThreadTaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
#if BUILDFLAG(IS_CHROMEOS_ASH)
chromeos::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
@@ -295,9 +326,6 @@ class EncryptedReportingJobConfigurationTest : public testing::Test {
StrictMock<MockJobCreationHandler> job_creation_handler_;
FakeDeviceManagementService service_{&job_creation_handler_};
MockCloudPolicyClient client_;
- StrictMock<MockCompleteCb> complete_cb_;
-
- DeviceManagementService::Job job_;
private:
base::Value payload_;
@@ -306,11 +334,12 @@ class EncryptedReportingJobConfigurationTest : public testing::Test {
// Validates that the non-Record portions of the payload are generated
// correctly.
TEST_F(EncryptedReportingJobConfigurationTest, ValidatePayload) {
- EXPECT_CALL(complete_cb_, Call(_, _, _, _)).Times(1);
+ StrictMock<MockCompleteCb> completion_cb;
+ EXPECT_CALL(completion_cb, Call(_, _, _, _)).Times(1);
EncryptedReportingJobConfiguration configuration(
&client_, service_.configuration()->GetEncryptedReportingServerUrl(),
RequestPayloadBuilder().Build(),
- base::BindOnce(&MockCompleteCb::Call, base::Unretained(&complete_cb_)));
+ base::BindOnce(&MockCompleteCb::Call, base::Unretained(&completion_cb)));
auto* payload = GetPayload(&configuration);
EXPECT_FALSE(GetDeviceName().empty());
EXPECT_EQ(
@@ -345,12 +374,13 @@ TEST_F(EncryptedReportingJobConfigurationTest, ValidatePayload) {
TEST_F(EncryptedReportingJobConfigurationTest, CorrectlyAddEncryptedRecord) {
const std::string kEncryptedWrappedRecord = "TEST_INFO";
base::Value record_value = GenerateSingleRecord(kEncryptedWrappedRecord);
+ StrictMock<MockCompleteCb> completion_cb;
- EXPECT_CALL(complete_cb_, Call(_, _, _, _)).Times(1);
+ EXPECT_CALL(completion_cb, Call(_, _, _, _)).Times(1);
EncryptedReportingJobConfiguration configuration(
&client_, service_.configuration()->GetEncryptedReportingServerUrl(),
RequestPayloadBuilder().AddRecord(record_value).Build(),
- base::BindOnce(&MockCompleteCb::Call, base::Unretained(&complete_cb_)));
+ base::BindOnce(&MockCompleteCb::Call, base::Unretained(&completion_cb)));
base::Value* record_list = nullptr;
GetRecordList(&configuration, &record_list);
@@ -378,11 +408,12 @@ TEST_F(EncryptedReportingJobConfigurationTest, CorrectlyAddsMultipleRecords) {
builder.AddRecord(records.back());
}
- EXPECT_CALL(complete_cb_, Call(_, _, _, _)).Times(1);
+ StrictMock<MockCompleteCb> completion_cb;
+ EXPECT_CALL(completion_cb, Call(_, _, _, _)).Times(1);
EncryptedReportingJobConfiguration configuration(
&client_, service_.configuration()->GetEncryptedReportingServerUrl(),
builder.Build(),
- base::BindOnce(&MockCompleteCb::Call, base::Unretained(&complete_cb_)));
+ base::BindOnce(&MockCompleteCb::Call, base::Unretained(&completion_cb)));
base::Value* record_list = nullptr;
GetRecordList(&configuration, &record_list);
@@ -402,11 +433,12 @@ TEST_F(EncryptedReportingJobConfigurationTest, CorrectlyAddsMultipleRecords) {
TEST_F(EncryptedReportingJobConfigurationTest,
AllowsAttachEncryptionSettingsAlone) {
RequestPayloadBuilder builder{/*attach_encryption_settings=*/true};
- EXPECT_CALL(complete_cb_, Call(_, _, _, _)).Times(1);
+ StrictMock<MockCompleteCb> completion_cb;
+ EXPECT_CALL(completion_cb, Call(_, _, _, _)).Times(1);
EncryptedReportingJobConfiguration configuration(
&client_, service_.configuration()->GetEncryptedReportingServerUrl(),
builder.Build(),
- base::BindOnce(&MockCompleteCb::Call, base::Unretained(&complete_cb_)));
+ base::BindOnce(&MockCompleteCb::Call, base::Unretained(&completion_cb)));
base::Value* record_list = nullptr;
GetRecordList(&configuration, &record_list);
@@ -427,11 +459,12 @@ TEST_F(EncryptedReportingJobConfigurationTest,
builder.AddRecord(records.back());
}
- EXPECT_CALL(complete_cb_, Call(_, _, _, _)).Times(1);
+ StrictMock<MockCompleteCb> completion_cb;
+ EXPECT_CALL(completion_cb, Call(_, _, _, _)).Times(1);
EncryptedReportingJobConfiguration configuration(
&client_, service_.configuration()->GetEncryptedReportingServerUrl(),
builder.Build(),
- base::BindOnce(&MockCompleteCb::Call, base::Unretained(&complete_cb_)));
+ base::BindOnce(&MockCompleteCb::Call, base::Unretained(&completion_cb)));
base::Value* record_list = nullptr;
GetRecordList(&configuration, &record_list);
@@ -448,11 +481,12 @@ TEST_F(EncryptedReportingJobConfigurationTest,
// Ensures that the context can be updated.
TEST_F(EncryptedReportingJobConfigurationTest, CorrectlyAddsAndUpdatesContext) {
- EXPECT_CALL(complete_cb_, Call(_, _, _, _)).Times(1);
+ StrictMock<MockCompleteCb> completion_cb;
+ EXPECT_CALL(completion_cb, Call(_, _, _, _)).Times(1);
EncryptedReportingJobConfiguration configuration(
&client_, service_.configuration()->GetEncryptedReportingServerUrl(),
RequestPayloadBuilder().Build(),
- base::BindOnce(&MockCompleteCb::Call, base::Unretained(&complete_cb_)));
+ base::BindOnce(&MockCompleteCb::Call, base::Unretained(&completion_cb)));
const std::string kTestKey = "device.name";
const std::string kTestValue = "1701-A";
@@ -463,7 +497,7 @@ TEST_F(EncryptedReportingJobConfigurationTest, CorrectlyAddsAndUpdatesContext) {
base::Value* payload = GetPayload(&configuration);
std::string* good_result = payload->FindStringPath(kTestKey);
ASSERT_THAT(good_result, NotNull());
- EXPECT_EQ(*good_result, kTestValue);
+ EXPECT_THAT(*good_result, StrEq(kTestValue));
// Add a path that isn't in the allow list.
const std::string kBadTestKey = "profile.string";
@@ -494,39 +528,187 @@ TEST_F(EncryptedReportingJobConfigurationTest, CorrectlyAddsAndUpdatesContext) {
TEST_F(EncryptedReportingJobConfigurationTest, OnURLLoadComplete_Success) {
const std::string kEncryptedWrappedRecord = "TEST_INFO";
base::Value record_value = GenerateSingleRecord(kEncryptedWrappedRecord);
+ auto upload = CreateTestUpload(record_value);
- base::Value::Dict response = ResponseValueBuilder::CreateResponse(
- *record_value.FindDictKey(kSequenceInformationKey), absl::nullopt);
-
- EXPECT_CALL(complete_cb_,
- Call(&job_, DM_STATUS_SUCCESS, net::OK, Eq(ByRef(response))))
+ EXPECT_CALL(*upload.completion_cb, Call(&upload.job, DM_STATUS_SUCCESS,
+ DeviceManagementService::kSuccess,
+ Eq(ByRef(upload.response))))
.Times(1);
- EncryptedReportingJobConfiguration configuration(
- &client_, service_.configuration()->GetEncryptedReportingServerUrl(),
- RequestPayloadBuilder().AddRecord(record_value).Build(),
- base::BindOnce(&MockCompleteCb::Call, base::Unretained(&complete_cb_)));
const std::string kTestString = "device.clientId";
const std::string kTestInt = "1701-A";
base::Value::Dict context = GenerateContext(kTestString, kTestInt);
- configuration.UpdateContext(std::move(context));
+ upload.configuration->UpdateContext(std::move(context));
- configuration.OnURLLoadComplete(
- &job_, net::OK, DeviceManagementService::kSuccess,
- ResponseValueBuilder::CreateResponseString(response));
+ upload.configuration->OnURLLoadComplete(
+ &upload.job, net::OK, DeviceManagementService::kSuccess,
+ ResponseValueBuilder::CreateResponseString(upload.response));
}
// Ensures that upload failure is handled correctly.
TEST_F(EncryptedReportingJobConfigurationTest, OnURLLoadComplete_NetError) {
- int net_error = net::ERR_CONNECTION_RESET;
- EXPECT_CALL(complete_cb_, Call(&job_, DM_STATUS_REQUEST_FAILED, net_error,
- testing::Eq(absl::nullopt)))
+ StrictMock<MockCompleteCb> completion_cb;
+ DeviceManagementService::Job job;
+ EXPECT_CALL(completion_cb, Call(&job, DM_STATUS_REQUEST_FAILED, _,
+ testing::Eq(absl::nullopt)))
.Times(1);
EncryptedReportingJobConfiguration configuration(
&client_, service_.configuration()->GetEncryptedReportingServerUrl(),
RequestPayloadBuilder().Build(),
- base::BindOnce(&MockCompleteCb::Call, base::Unretained(&complete_cb_)));
- configuration.OnURLLoadComplete(&job_, net_error, 0, "");
+ base::BindOnce(&MockCompleteCb::Call, base::Unretained(&completion_cb)));
+ configuration.OnURLLoadComplete(&job, net::ERR_CONNECTION_RESET,
+ 0 /* ignored */, "");
+}
+
+TEST_F(EncryptedReportingJobConfigurationTest,
+ IdenticalUploadRetriesThrottled) {
+ const size_t kTotalRetries = 10;
+ const std::string kEncryptedWrappedRecord = "TEST_INFO";
+ base::Value record_value =
+ GenerateSingleRecord(kEncryptedWrappedRecord, kPriority);
+
+ base::TimeDelta expected_delay_after = base::Seconds(10);
+ for (size_t i = 0; i < kTotalRetries; ++i) {
+ auto upload = CreateTestUpload(record_value);
+ // Expect upload to fail with a temporary error, to justify a retry.
+ EXPECT_CALL(*upload.completion_cb,
+ Call(&upload.job, DM_STATUS_TEMPORARY_UNAVAILABLE,
+ DeviceManagementService::kServiceUnavailable,
+ Eq(ByRef(upload.response))))
+ .Times(1);
+
+ auto allowed_delay = upload.configuration->WhenIsAllowedToProceed();
+ if (i == 0) {
+ // First upload allowed immediately.
+ EXPECT_FALSE(allowed_delay.is_positive());
+ } else {
+ // Further uploads allowed with delay.
+ EXPECT_THAT(allowed_delay, Ge(expected_delay_after));
+ // Double the expectation for the next retry.
+ expected_delay_after *= 2;
+ // Move forward to allow.
+ task_environment_.FastForwardBy(allowed_delay - base::Seconds(1));
+ EXPECT_TRUE(upload.configuration->WhenIsAllowedToProceed().is_positive());
+ task_environment_.FastForwardBy(base::Seconds(1));
+ }
+
+ EXPECT_FALSE(upload.configuration->WhenIsAllowedToProceed().is_positive());
+ upload.configuration->AccountForAllowedJob();
+ // Process temporary error response code.
+ upload.configuration->OnURLLoadComplete(
+ &upload.job, net::OK, DeviceManagementService::kServiceUnavailable,
+ ResponseValueBuilder::CreateResponseString(upload.response));
+ }
}
+TEST_F(EncryptedReportingJobConfigurationTest, UploadsSequenceThrottled) {
+ const size_t kTotalRetries = 10;
+ const std::string kEncryptedWrappedRecord = "TEST_INFO";
+
+ std::vector<TestUpload> uploads;
+ base::TimeDelta expected_delay_after = base::Seconds(10);
+ for (size_t i = 0; i < kTotalRetries; ++i) {
+ // Create new record with next seq id.
+ base::Value record_value =
+ GenerateSingleRecord(kEncryptedWrappedRecord, kPriority);
+
+ uploads.emplace_back(CreateTestUpload(record_value));
+ auto allowed_delay = uploads.back().configuration->WhenIsAllowedToProceed();
+ if (i == 0) {
+ EXPECT_FALSE(allowed_delay.is_positive());
+ // Next retry not before 10 sec.
+ } else {
+ EXPECT_THAT(allowed_delay, Ge(expected_delay_after));
+ // Double the expectation for the next upload.
+ expected_delay_after *= 2;
+ // Move forward to allow.
+ task_environment_.FastForwardBy(allowed_delay - base::Seconds(1));
+ EXPECT_TRUE(
+ uploads.back().configuration->WhenIsAllowedToProceed().is_positive());
+ task_environment_.FastForwardBy(base::Seconds(1));
+ }
+
+ EXPECT_FALSE(
+ uploads.back().configuration->WhenIsAllowedToProceed().is_positive());
+ uploads.back().configuration->AccountForAllowedJob();
+ }
+
+ // Now complete all created uploads.
+ for (auto& upload : uploads) {
+ EXPECT_CALL(*upload.completion_cb, Call(&upload.job, DM_STATUS_SUCCESS,
+ DeviceManagementService::kSuccess,
+ Eq(ByRef(upload.response))))
+ .Times(1);
+ upload.configuration->OnURLLoadComplete(
+ &upload.job, net::OK, DeviceManagementService::kSuccess,
+ ResponseValueBuilder::CreateResponseString(upload.response));
+ }
+}
+
+TEST_F(EncryptedReportingJobConfigurationTest,
+ SecurityUploadsSequenceNotThrottled) {
+ const size_t kTotalRetries = 10;
+ const std::string kEncryptedWrappedRecord = "TEST_INFO";
+
+ std::vector<TestUpload> uploads;
+ for (size_t i = 0; i < kTotalRetries; ++i) {
+ // Create new record with next seq id.
+ base::Value record_value = GenerateSingleRecord(
+ kEncryptedWrappedRecord, ::reporting::Priority::SECURITY);
+
+ uploads.emplace_back(CreateTestUpload(record_value));
+ auto allowed_delay = uploads.back().configuration->WhenIsAllowedToProceed();
+ EXPECT_FALSE(allowed_delay.is_positive());
+ uploads.back().configuration->AccountForAllowedJob();
+ }
+
+ // Now complete all created uploads.
+ for (auto& upload : uploads) {
+ EXPECT_CALL(*upload.completion_cb, Call(&upload.job, DM_STATUS_SUCCESS,
+ DeviceManagementService::kSuccess,
+ Eq(ByRef(upload.response))))
+ .Times(1);
+ upload.configuration->OnURLLoadComplete(
+ &upload.job, net::OK, DeviceManagementService::kSuccess,
+ ResponseValueBuilder::CreateResponseString(upload.response));
+ }
+}
+
+TEST_F(EncryptedReportingJobConfigurationTest, FailedUploadsSequenceThrottled) {
+ const size_t kTotalRetries = 10;
+ const std::string kEncryptedWrappedRecord = "TEST_INFO";
+
+ for (size_t i = 0; i < kTotalRetries; ++i) {
+ // Create new record with next seq id.
+ base::Value record_value =
+ GenerateSingleRecord(kEncryptedWrappedRecord, kPriority);
+
+ auto upload = CreateTestUpload(record_value);
+
+ EXPECT_CALL(*upload.completion_cb,
+ Call(&upload.job, DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID,
+ DeviceManagementService::kInvalidAuthCookieOrDMToken,
+ Eq(ByRef(upload.response))))
+ .Times(1);
+
+ auto allowed_delay = upload.configuration->WhenIsAllowedToProceed();
+ if (i == 0) {
+ // The very first upload is allowed.
+ EXPECT_FALSE(allowed_delay.is_positive());
+ } else {
+ EXPECT_THAT(allowed_delay, Ge(base::Days(1)));
+ // Move forward to allow.
+ task_environment_.FastForwardBy(allowed_delay - base::Seconds(1));
+ EXPECT_TRUE(upload.configuration->WhenIsAllowedToProceed().is_positive());
+ task_environment_.FastForwardBy(base::Seconds(1));
+ }
+
+ EXPECT_FALSE(upload.configuration->WhenIsAllowedToProceed().is_positive());
+ upload.configuration->AccountForAllowedJob();
+ upload.configuration->OnURLLoadComplete(
+ &upload.job, net::OK,
+ DeviceManagementService::kInvalidAuthCookieOrDMToken,
+ ResponseValueBuilder::CreateResponseString(upload.response));
+ }
+}
} // namespace policy
diff --git a/chromium/components/policy/core/common/cloud/mock_device_management_service.cc b/chromium/components/policy/core/common/cloud/mock_device_management_service.cc
index 1074baabdc6..0a8d5644d6e 100644
--- a/chromium/components/policy/core/common/cloud/mock_device_management_service.cc
+++ b/chromium/components/policy/core/common/cloud/mock_device_management_service.cc
@@ -149,6 +149,17 @@ FakeDeviceManagementService::CaptureRequest(
}
FakeDeviceManagementService::JobAction
+FakeDeviceManagementService::CaptureTimeout(base::TimeDelta* timeout) {
+ return [timeout](DeviceManagementService::JobForTesting job) mutable {
+ if (job.IsActive()) {
+ auto to = job.GetConfigurationForTesting()->GetTimeoutDuration();
+ if (to)
+ *timeout = to.value();
+ }
+ };
+}
+
+FakeDeviceManagementService::JobAction
FakeDeviceManagementService::SendJobResponseAsync(int net_error,
int response_code,
const std::string& response,
@@ -270,6 +281,10 @@ void FakeJobConfiguration::SetShouldRetryResponse(
should_retry_response_ = method;
}
+void FakeJobConfiguration::SetTimeoutDuration(base::TimeDelta timeout) {
+ timeout_ = timeout;
+}
+
DeviceManagementService::Job::RetryMethod FakeJobConfiguration::ShouldRetry(
int response_code,
const std::string& response_body) {
@@ -286,9 +301,9 @@ void FakeJobConfiguration::OnURLLoadComplete(DeviceManagementService::Job* job,
int net_error,
int response_code,
const std::string& response_body) {
- DeviceManagementStatus code =
- MapNetErrorAndResponseCodeToDMStatus(net_error, response_code);
- std::move(callback_).Run(job, code, net_error, response_body);
+ DeviceManagementStatus status =
+ MapNetErrorAndResponseToDMStatus(net_error, response_code, response_body);
+ std::move(callback_).Run(job, status, net_error, response_body);
}
} // namespace policy
diff --git a/chromium/components/policy/core/common/cloud/mock_device_management_service.h b/chromium/components/policy/core/common/cloud/mock_device_management_service.h
index 131908f3237..14cdcae7052 100644
--- a/chromium/components/policy/core/common/cloud/mock_device_management_service.h
+++ b/chromium/components/policy/core/common/cloud/mock_device_management_service.h
@@ -80,6 +80,7 @@ class FakeDeviceManagementService : public DeviceManagementService {
std::map<std::string, std::string>* query_params);
JobAction CaptureRequest(
enterprise_management::DeviceManagementRequest* request);
+ JobAction CaptureTimeout(base::TimeDelta* timeout);
// Convenience actions to post a task which will call |SetResponseForTesting|
// on the job.
@@ -163,6 +164,7 @@ class FakeJobConfiguration : public DMServerJobConfiguration {
void SetRequestPayload(const std::string& request_payload);
void SetShouldRetryResponse(DeviceManagementService::Job::RetryMethod method);
+ void SetTimeoutDuration(base::TimeDelta timeout);
private:
DeviceManagementService::Job::RetryMethod ShouldRetry(
diff --git a/chromium/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc b/chromium/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc
index a2ec2629e87..1215adf618c 100644
--- a/chromium/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc
+++ b/chromium/components/policy/core/common/cloud/realtime_reporting_job_configuration_unittest.cc
@@ -53,14 +53,10 @@ class MockCallbackObserver {
MOCK_METHOD4(OnURLLoadComplete,
void(DeviceManagementService::Job* job,
DeviceManagementStatus code,
- int net_error,
+ int response_code,
absl::optional<base::Value::Dict>));
};
-MATCHER_P(MatchDict, expected, "matches DictionaryValue") {
- return arg == expected;
-}
-
class RealtimeReportingJobConfigurationTest : public testing::Test {
public:
RealtimeReportingJobConfigurationTest()
@@ -220,7 +216,8 @@ TEST_F(RealtimeReportingJobConfigurationTest, ValidatePayload) {
TEST_F(RealtimeReportingJobConfigurationTest, OnURLLoadComplete_Success) {
base::Value::Dict response = CreateResponse({ids[0], ids[1], ids[2]}, {}, {});
EXPECT_CALL(callback_observer_,
- OnURLLoadComplete(&job_, DM_STATUS_SUCCESS, net::OK,
+ OnURLLoadComplete(&job_, DM_STATUS_SUCCESS,
+ DeviceManagementService::kSuccess,
testing::Eq(testing::ByRef(response))));
configuration_->OnURLLoadComplete(&job_, net::OK,
DeviceManagementService::kSuccess,
@@ -228,17 +225,18 @@ TEST_F(RealtimeReportingJobConfigurationTest, OnURLLoadComplete_Success) {
}
TEST_F(RealtimeReportingJobConfigurationTest, OnURLLoadComplete_NetError) {
- int net_error = net::ERR_CONNECTION_RESET;
EXPECT_CALL(callback_observer_,
- OnURLLoadComplete(&job_, DM_STATUS_REQUEST_FAILED, net_error,
+ OnURLLoadComplete(&job_, DM_STATUS_REQUEST_FAILED, _,
testing::Eq(absl::nullopt)));
- configuration_->OnURLLoadComplete(&job_, net_error, 0, "");
+ configuration_->OnURLLoadComplete(&job_, net::ERR_CONNECTION_RESET,
+ 0 /* ignored */, "");
}
TEST_F(RealtimeReportingJobConfigurationTest,
OnURLLoadComplete_InvalidRequest) {
EXPECT_CALL(callback_observer_,
- OnURLLoadComplete(&job_, DM_STATUS_REQUEST_INVALID, net::OK,
+ OnURLLoadComplete(&job_, DM_STATUS_REQUEST_INVALID,
+ DeviceManagementService::kInvalidArgument,
testing::Eq(absl::nullopt)));
configuration_->OnURLLoadComplete(
&job_, net::OK, DeviceManagementService::kInvalidArgument, "");
@@ -249,7 +247,8 @@ TEST_F(RealtimeReportingJobConfigurationTest,
EXPECT_CALL(
callback_observer_,
OnURLLoadComplete(&job_, DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID,
- net::OK, testing::Eq(absl::nullopt)));
+ DeviceManagementService::kInvalidAuthCookieOrDMToken,
+ testing::Eq(absl::nullopt)));
configuration_->OnURLLoadComplete(
&job_, net::OK, DeviceManagementService::kInvalidAuthCookieOrDMToken, "");
}
@@ -258,14 +257,16 @@ TEST_F(RealtimeReportingJobConfigurationTest, OnURLLoadComplete_NotSupported) {
EXPECT_CALL(
callback_observer_,
OnURLLoadComplete(&job_, DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED,
- net::OK, testing::Eq(absl::nullopt)));
+ DeviceManagementService::kDeviceManagementNotAllowed,
+ testing::Eq(absl::nullopt)));
configuration_->OnURLLoadComplete(
&job_, net::OK, DeviceManagementService::kDeviceManagementNotAllowed, "");
}
TEST_F(RealtimeReportingJobConfigurationTest, OnURLLoadComplete_TempError) {
EXPECT_CALL(callback_observer_,
- OnURLLoadComplete(&job_, DM_STATUS_TEMPORARY_UNAVAILABLE, net::OK,
+ OnURLLoadComplete(&job_, DM_STATUS_TEMPORARY_UNAVAILABLE,
+ DeviceManagementService::kServiceUnavailable,
testing::Eq(absl::nullopt)));
configuration_->OnURLLoadComplete(
&job_, net::OK, DeviceManagementService::kServiceUnavailable, "");
@@ -273,7 +274,8 @@ TEST_F(RealtimeReportingJobConfigurationTest, OnURLLoadComplete_TempError) {
TEST_F(RealtimeReportingJobConfigurationTest, OnURLLoadComplete_UnknownError) {
EXPECT_CALL(callback_observer_,
- OnURLLoadComplete(&job_, DM_STATUS_HTTP_STATUS_ERROR, net::OK,
+ OnURLLoadComplete(&job_, DM_STATUS_HTTP_STATUS_ERROR,
+ DeviceManagementService::kInvalidURL,
testing::Eq(absl::nullopt)));
configuration_->OnURLLoadComplete(&job_, net::OK,
DeviceManagementService::kInvalidURL, "");
diff --git a/chromium/components/policy/core/common/cloud/reporting_job_configuration_base.cc b/chromium/components/policy/core/common/cloud/reporting_job_configuration_base.cc
index 022064e9750..fb034eee9fa 100644
--- a/chromium/components/policy/core/common/cloud/reporting_job_configuration_base.cc
+++ b/chromium/components/policy/core/common/cloud/reporting_job_configuration_base.cc
@@ -215,30 +215,30 @@ void ReportingJobConfigurationBase::OnURLLoadComplete(
// Parse the response even if |response_code| is not a success since the
// response data may contain an error message.
// Map the net_error/response_code to a DeviceManagementStatus.
- DeviceManagementStatus code;
+ DeviceManagementStatus status;
if (net_error != net::OK) {
- code = DM_STATUS_REQUEST_FAILED;
+ status = DM_STATUS_REQUEST_FAILED;
} else {
switch (response_code) {
case DeviceManagementService::kSuccess:
- code = DM_STATUS_SUCCESS;
+ status = DM_STATUS_SUCCESS;
break;
case DeviceManagementService::kInvalidArgument:
- code = DM_STATUS_REQUEST_INVALID;
+ status = DM_STATUS_REQUEST_INVALID;
break;
case DeviceManagementService::kInvalidAuthCookieOrDMToken:
- code = DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID;
+ status = DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID;
break;
case DeviceManagementService::kDeviceManagementNotAllowed:
- code = DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED;
+ status = DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED;
break;
default:
// Handle all unknown 5xx HTTP error codes as temporary and any other
// unknown error as one that needs more time to recover.
if (response_code >= 500 && response_code <= 599)
- code = DM_STATUS_TEMPORARY_UNAVAILABLE;
+ status = DM_STATUS_TEMPORARY_UNAVAILABLE;
else
- code = DM_STATUS_HTTP_STATUS_ERROR;
+ status = DM_STATUS_HTTP_STATUS_ERROR;
break;
}
}
@@ -246,7 +246,8 @@ void ReportingJobConfigurationBase::OnURLLoadComplete(
auto response_dict = response && response->is_dict()
? absl::make_optional(std::move(response->GetDict()))
: absl::nullopt;
- std::move(callback_).Run(job, code, net_error, std::move(response_dict));
+ std::move(callback_).Run(job, status, response_code,
+ std::move(response_dict));
}
DeviceManagementService::Job::RetryMethod
diff --git a/chromium/components/policy/core/common/cloud/reporting_job_configuration_base.h b/chromium/components/policy/core/common/cloud/reporting_job_configuration_base.h
index 46c568a3c2c..09ab650bb09 100644
--- a/chromium/components/policy/core/common/cloud/reporting_job_configuration_base.h
+++ b/chromium/components/policy/core/common/cloud/reporting_job_configuration_base.h
@@ -42,8 +42,8 @@ class POLICY_EXPORT ReportingJobConfigurationBase
// Callback used once the job is complete.
using UploadCompleteCallback =
base::OnceCallback<void(DeviceManagementService::Job* job,
- DeviceManagementStatus code,
- int net_error,
+ DeviceManagementStatus status,
+ int response_code,
absl::optional<base::Value::Dict>)>;
// Builds a Device dictionary for uploading information about the device to
diff --git a/chromium/components/policy/core/common/cloud/user_cloud_policy_store.cc b/chromium/components/policy/core/common/cloud/user_cloud_policy_store.cc
index 3c4590092cf..8d6993d4aa8 100644
--- a/chromium/components/policy/core/common/cloud/user_cloud_policy_store.cc
+++ b/chromium/components/policy/core/common/cloud/user_cloud_policy_store.cc
@@ -122,10 +122,10 @@ void DesktopCloudPolicyStore::LoadImmediately() {
void DesktopCloudPolicyStore::Clear() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- background_task_runner()->PostTask(
- FROM_HERE, base::BindOnce(base::GetDeleteFileCallback(), policy_path_));
- background_task_runner()->PostTask(
- FROM_HERE, base::BindOnce(base::GetDeleteFileCallback(), key_path_));
+ background_task_runner()->PostTask(FROM_HERE,
+ base::GetDeleteFileCallback(policy_path_));
+ background_task_runner()->PostTask(FROM_HERE,
+ base::GetDeleteFileCallback(key_path_));
ResetPolicy();
policy_map_.Clear();
policy_signature_public_key_.clear();
diff --git a/chromium/components/policy/core/common/cloud/user_info_fetcher.cc b/chromium/components/policy/core/common/cloud/user_info_fetcher.cc
index 749456c6800..ae6c9069aa7 100644
--- a/chromium/components/policy/core/common/cloud/user_info_fetcher.cc
+++ b/chromium/components/policy/core/common/cloud/user_info_fetcher.cc
@@ -106,11 +106,10 @@ void UserInfoFetcher::OnFetchComplete(
// to the delegate.
DCHECK(unparsed_data);
DVLOG(1) << "Received UserInfo response: " << *unparsed_data;
- std::unique_ptr<base::Value> parsed_value =
- base::JSONReader::ReadDeprecated(*unparsed_data);
- base::DictionaryValue* dict;
- if (parsed_value.get() && parsed_value->GetAsDictionary(&dict)) {
- delegate_->OnGetUserInfoSuccess(dict);
+ absl::optional<base::Value> parsed_value =
+ base::JSONReader::Read(*unparsed_data);
+ if (parsed_value && parsed_value->is_dict()) {
+ delegate_->OnGetUserInfoSuccess(parsed_value->GetDict());
} else {
NOTREACHED() << "Could not parse userinfo response from server";
delegate_->OnGetUserInfoFailure(GoogleServiceAuthError(
diff --git a/chromium/components/policy/core/common/cloud/user_info_fetcher.h b/chromium/components/policy/core/common/cloud/user_info_fetcher.h
index fe2e9f30656..5d782c90d47 100644
--- a/chromium/components/policy/core/common/cloud/user_info_fetcher.h
+++ b/chromium/components/policy/core/common/cloud/user_info_fetcher.h
@@ -10,14 +10,11 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
+#include "base/values.h"
#include "components/policy/policy_export.h"
class GoogleServiceAuthError;
-namespace base {
-class DictionaryValue;
-}
-
namespace network {
class SharedURLLoaderFactory;
class SimpleURLLoader;
@@ -34,8 +31,7 @@ class POLICY_EXPORT UserInfoFetcher {
// Invoked when the UserInfo request has succeeded, passing the parsed
// response in |response|. Delegate may free the UserInfoFetcher in this
// callback.
- virtual void OnGetUserInfoSuccess(
- const base::DictionaryValue* response) = 0;
+ virtual void OnGetUserInfoSuccess(const base::Value::Dict& response) = 0;
// Invoked when the UserInfo request has failed, passing the associated
// error in |error|. Delegate may free the UserInfoFetcher in this
diff --git a/chromium/components/policy/core/common/cloud/user_info_fetcher_unittest.cc b/chromium/components/policy/core/common/cloud/user_info_fetcher_unittest.cc
index 82daf13dd70..fd85ed87733 100644
--- a/chromium/components/policy/core/common/cloud/user_info_fetcher_unittest.cc
+++ b/chromium/components/policy/core/common/cloud/user_info_fetcher_unittest.cc
@@ -34,11 +34,11 @@ class MockUserInfoFetcherDelegate : public UserInfoFetcher::Delegate {
~MockUserInfoFetcherDelegate() {}
MOCK_METHOD1(OnGetUserInfoFailure,
void(const GoogleServiceAuthError& error));
- MOCK_METHOD1(OnGetUserInfoSuccess, void(const base::DictionaryValue* result));
+ MOCK_METHOD1(OnGetUserInfoSuccess, void(const base::Value::Dict& result));
};
-MATCHER_P(MatchDict, expected, "matches DictionaryValue") {
- return *arg == *expected;
+MATCHER_P(MatchDict, expected, "matches Value::Dict") {
+ return arg == *expected;
}
class UserInfoFetcherTest : public testing::Test {
@@ -78,10 +78,10 @@ TEST_F(UserInfoFetcherTest, SuccessfulFetch) {
// Generate what we expect our result will look like (should match
// parsed kUserInfoResponse).
- base::Value dict(base::Value::Type::DICTIONARY);
- dict.SetKey("email", base::Value("test_user@test.com"));
- dict.SetKey("verified_email", base::Value(true));
- dict.SetKey("hd", base::Value("test.com"));
+ base::Value::Dict dict;
+ dict.Set("email", "test_user@test.com");
+ dict.Set("verified_email", true);
+ dict.Set("hd", "test.com");
// Fake a successful fetch - should result in the data being parsed and
// the values passed off to the success callback.
diff --git a/chromium/components/policy/core/common/configuration_policy_provider_test.cc b/chromium/components/policy/core/common/configuration_policy_provider_test.cc
index 85b79f1cf8e..d610443d52f 100644
--- a/chromium/components/policy/core/common/configuration_policy_provider_test.cc
+++ b/chromium/components/policy/core/common/configuration_policy_provider_test.cc
@@ -363,14 +363,14 @@ TEST_P(Configuration3rdPartyPolicyProviderTest, Load3rdParty) {
policy_dict.SetIntKey("int", 789);
policy_dict.SetStringKey("string", "string value");
- base::ListValue list;
+ base::Value::List list;
for (int i = 0; i < 2; ++i) {
- auto dict = std::make_unique<base::DictionaryValue>();
- dict->SetIntKey("subdictindex", i);
- dict->SetKey("subdict", policy_dict.Clone());
+ base::Value::Dict dict;
+ dict.Set("subdictindex", i);
+ dict.Set("subdict", policy_dict.Clone());
list.Append(std::move(dict));
}
- policy_dict.SetKey("list", std::move(list));
+ policy_dict.SetKey("list", base::Value(std::move(list)));
policy_dict.SetKey("dict", policy_dict.Clone());
// Install these policies as a Chrome policy.
diff --git a/chromium/components/policy/core/common/default_chrome_apps_migrator.cc b/chromium/components/policy/core/common/default_chrome_apps_migrator.cc
index ecee5085eca..4e00965e638 100644
--- a/chromium/components/policy/core/common/default_chrome_apps_migrator.cc
+++ b/chromium/components/policy/core/common/default_chrome_apps_migrator.cc
@@ -12,7 +12,10 @@ namespace policy {
namespace {
std::map<std::string, std::string> GetChromeAppToWebAppMapping() {
- return std::map<std::string, std::string>();
+ return std::map<std::string, std::string>({
+ {"ejjicmeblgpmajnghnpcppodonldlgfn",
+ "https://calendar.google.com/calendar/installwebapp?usp=chrome_default"},
+ });
}
} // namespace
@@ -35,24 +38,23 @@ void DefaultChromeAppsMigrator::Migrate(PolicyMap* policies) const {
std::vector<std::string> chrome_app_ids =
RemoveChromeAppsFromExtensionForcelist(policies);
- // If no chrome apps need to be replaced, we have nothing to do.
+ // If no Chrome Apps need to be replaced, we have nothing to do.
if (chrome_app_ids.empty())
return;
- EnsurePolicyValueIsList(policies, key::kExtensionInstallBlocklist);
- base::Value* blocklist_value = policies->GetMutableValue(
- key::kExtensionInstallBlocklist, base::Value::Type::LIST);
- for (const std::string& chrome_app_id : chrome_app_ids) {
- blocklist_value->Append(chrome_app_id);
- }
-
EnsurePolicyValueIsList(policies, key::kWebAppInstallForceList);
- base::Value* web_app_policy_value = policies->GetMutableValue(
- key::kWebAppInstallForceList, base::Value::Type::LIST);
+ base::Value::List& web_app_policy_value =
+ policies
+ ->GetMutableValue(key::kWebAppInstallForceList,
+ base::Value::Type::LIST)
+ ->GetList();
for (const std::string& chrome_app_id : chrome_app_ids) {
- base::Value web_app(base::Value::Type::DICTIONARY);
- web_app.SetStringKey("url", chrome_app_to_web_app_.at(chrome_app_id));
- web_app_policy_value->Append(std::move(web_app));
+ base::Value::Dict web_app;
+ web_app.Set("url", chrome_app_to_web_app_.at(chrome_app_id));
+ base::Value::List uninstall_list;
+ uninstall_list.Append(chrome_app_id);
+ web_app.Set("uninstall_and_replace", std::move(uninstall_list));
+ web_app_policy_value.Append(std::move(web_app));
}
MigratePinningPolicy(policies);
@@ -73,7 +75,7 @@ DefaultChromeAppsMigrator::RemoveChromeAppsFromExtensionForcelist(
std::vector<std::string> chrome_app_ids;
base::Value new_forcelist_value(base::Value::Type::LIST);
- for (const auto& list_entry : forcelist_value->GetListDeprecated()) {
+ for (const auto& list_entry : forcelist_value->GetList()) {
if (!list_entry.is_string()) {
new_forcelist_value.Append(list_entry.Clone());
continue;
@@ -120,7 +122,7 @@ void DefaultChromeAppsMigrator::MigratePinningPolicy(
key::kPinnedLauncherApps, base::Value::Type::LIST);
if (!pinned_apps_value)
return;
- for (auto& list_entry : pinned_apps_value->GetListDeprecated()) {
+ for (auto& list_entry : pinned_apps_value->GetList()) {
if (!list_entry.is_string())
continue;
const std::string pinned_app = list_entry.GetString();
diff --git a/chromium/components/policy/core/common/default_chrome_apps_migrator.h b/chromium/components/policy/core/common/default_chrome_apps_migrator.h
index 6220db402fa..4a72d081294 100644
--- a/chromium/components/policy/core/common/default_chrome_apps_migrator.h
+++ b/chromium/components/policy/core/common/default_chrome_apps_migrator.h
@@ -15,10 +15,9 @@ namespace policy {
// This class is used as a temporary solution to handle force install policies
// for deprecated Chrome apps. It replaces ExtensionInstallForcelist policy
-// for Chrome app with ExtensionInstallBlocklist for Chrome app and
-// WebAppInstallForceList policy for the corresponding Web App. To preserve the
-// pinning state, PinnedLauncherApps policy for Chrome app is replaced with the
-// one for Web App.
+// for Chrome app with WebAppInstallForceList policy for the corresponding Web
+// App. To preserve the pinning state, PinnedLauncherApps policy for Chrome app
+// is replaced with the one for Web App.
// This code will be removed when the following steps are done:
// 1. Build discoverability for default apps in Admin panel (Dpanel).
// 2. Build new control logic for blocking installation (but not blocking use
@@ -39,12 +38,12 @@ class POLICY_EXPORT DefaultChromeAppsMigrator {
~DefaultChromeAppsMigrator();
- // Replaces ExtensionInstallForcelist policy for Chrome apps listed in
+ // Replaces ExtensionInstallForcelist policy for Chrome Apps listed in
// `chrome_app_to_web_app_`.
void Migrate(PolicyMap* policies) const;
private:
- // Removes chrome apps listed in `chrome_app_to_web_app_` from
+ // Removes chrome Apps listed in `chrome_app_to_web_app_` from
// ExtensionInstallForcelist policy. Returns ids of removed apps.
std::vector<std::string> RemoveChromeAppsFromExtensionForcelist(
PolicyMap* policies) const;
@@ -54,12 +53,12 @@ class POLICY_EXPORT DefaultChromeAppsMigrator {
void EnsurePolicyValueIsList(PolicyMap* policies,
const std::string& policy_name) const;
- // Replaces policy to pin Chrome app from `chrome_app_to_web_app_` with policy
+ // Replaces policy to pin Chrome App from `chrome_app_to_web_app_` with policy
// to pin corresponding Web App. It only changes PinnedLauncherApps policy,
// which specifies pinned apps on Chrome OS.
void MigratePinningPolicy(PolicyMap* policies) const;
- // Maps from ids of Chrome apps that need to be replaced to Web App urls.
+ // Maps from ids of Chrome Apps that need to be replaced to Web App urls.
std::map<std::string, std::string> chrome_app_to_web_app_;
};
diff --git a/chromium/components/policy/core/common/default_chrome_apps_migrator_unittest.cc b/chromium/components/policy/core/common/default_chrome_apps_migrator_unittest.cc
index d62ca8c4a49..3efc6473283 100644
--- a/chromium/components/policy/core/common/default_chrome_apps_migrator_unittest.cc
+++ b/chromium/components/policy/core/common/default_chrome_apps_migrator_unittest.cc
@@ -19,6 +19,20 @@ constexpr char kAppId1[] = "aaaa";
constexpr char kAppId2[] = "bbbb";
constexpr char kWebAppUrl1[] = "https://gmail.com";
constexpr char kWebAppUrl2[] = "https://google.com";
+constexpr char kUninstallAndReplaceKey[] = "uninstall_and_replace";
+
+// Creates Dict object for WebAppInstallForceList policy from Web App
+// parameters.
+base::Value::Dict CreateWebAppDict(std::string url,
+ std::string replaced_extension_id) {
+ base::Value::Dict web_app;
+ web_app.Set("url", url);
+ base::Value::List uninstall_list;
+ uninstall_list.Append(replaced_extension_id);
+ web_app.Set(kUninstallAndReplaceKey, std::move(uninstall_list));
+ return web_app;
+}
+
} // namespace
class DefaultChromeAppsMigratorTest : public testing::Test {
@@ -32,25 +46,19 @@ class DefaultChromeAppsMigratorTest : public testing::Test {
POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
base::Value(base::Value::Type::LIST), nullptr);
- base::Value blocklist_value(base::Value::Type::LIST);
- blocklist_value.Append("eeee");
- policy_map_.Set(key::kExtensionInstallBlocklist, POLICY_LEVEL_MANDATORY,
- POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
- std::move(blocklist_value), nullptr);
-
- base::Value web_app_value(base::Value::Type::LIST);
- base::Value maps_web_app(base::Value::Type::DICTIONARY);
- maps_web_app.SetStringKey("url", "https://google.com/maps");
- web_app_value.Append(std::move(maps_web_app));
+ base::Value::List web_app_list;
+ base::Value::Dict maps_web_app;
+ maps_web_app.Set("url", "https://google.com/maps");
+ web_app_list.Append(std::move(maps_web_app));
policy_map_.Set(key::kWebAppInstallForceList, POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
- std::move(web_app_value), nullptr);
+ base::Value(std::move(web_app_list)), nullptr);
- base::Value pinned_apps_value(base::Value::Type::LIST);
- pinned_apps_value.Append("ffff");
+ base::Value::List pinned_apps_list;
+ pinned_apps_list.Append("ffff");
policy_map_.Set(key::kPinnedLauncherApps, POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
- std::move(pinned_apps_value), nullptr);
+ base::Value(std::move(pinned_apps_list)), nullptr);
}
protected:
@@ -62,7 +70,7 @@ TEST_F(DefaultChromeAppsMigratorTest, NoChromeApps) {
PolicyMap expected_map(policy_map_.Clone());
migrator_.Migrate(&policy_map_);
- // No chrome apps in ExtensionInstallForcelist policy, policy map should not
+ // No Chrome Apps in ExtensionInstallForcelist policy, policy map should not
// change.
EXPECT_TRUE(policy_map_.Equals(expected_map));
}
@@ -70,22 +78,20 @@ TEST_F(DefaultChromeAppsMigratorTest, NoChromeApps) {
TEST_F(DefaultChromeAppsMigratorTest, ChromeAppWithUpdateUrl) {
PolicyMap expected_map(policy_map_.Clone());
- // Add force installed chrome app that should be migrated.
+ // Add force installed Chrome App that should be migrated.
base::Value* forcelist_value = policy_map_.GetMutableValue(
key::kExtensionInstallForcelist, base::Value::Type::LIST);
forcelist_value->Append(std::string(kAppId1) + ";https://example.com");
- // Chrome app should be blocked after migration.
- base::Value* blocklist_value = expected_map.GetMutableValue(
- key::kExtensionInstallBlocklist, base::Value::Type::LIST);
- blocklist_value->Append(std::string(kAppId1));
-
// Corresponding web app should be force installed after migration.
- base::Value first_app(base::Value::Type::DICTIONARY);
- first_app.SetStringKey("url", kWebAppUrl1);
- base::Value* web_app_value = expected_map.GetMutableValue(
- key::kWebAppInstallForceList, base::Value::Type::LIST);
- web_app_value->Append(std::move(first_app));
+ base::Value::Dict web_app = CreateWebAppDict(kWebAppUrl1, kAppId1);
+
+ base::Value::List& web_app_list =
+ expected_map
+ .GetMutableValue(key::kWebAppInstallForceList,
+ base::Value::Type::LIST)
+ ->GetList();
+ web_app_list.Append(std::move(web_app));
migrator_.Migrate(&policy_map_);
@@ -95,7 +101,7 @@ TEST_F(DefaultChromeAppsMigratorTest, ChromeAppWithUpdateUrl) {
TEST_F(DefaultChromeAppsMigratorTest, ChromeAppsAndExtensions) {
PolicyMap expected_map(policy_map_.Clone());
- // Add two force installed chrome apps and two extensions.
+ // Add two force installed Chrome Apps and two extensions.
base::Value* forcelist_value = policy_map_.GetMutableValue(
key::kExtensionInstallForcelist, base::Value::Type::LIST);
forcelist_value->Append("extension1");
@@ -109,57 +115,16 @@ TEST_F(DefaultChromeAppsMigratorTest, ChromeAppsAndExtensions) {
expected_forcelist->Append("extension1");
expected_forcelist->Append("extension2");
- // Chrome apps should be blocked after migration.
- base::Value* blocklist_value = expected_map.GetMutableValue(
- key::kExtensionInstallBlocklist, base::Value::Type::LIST);
- blocklist_value->Append(kAppId1);
- blocklist_value->Append(kAppId2);
-
// Corresponding web apps should be force installed after migration.
- base::Value first_app(base::Value::Type::DICTIONARY);
- first_app.SetStringKey("url", kWebAppUrl1);
- base::Value second_app(base::Value::Type::DICTIONARY);
- second_app.SetStringKey("url", kWebAppUrl2);
- base::Value* web_app_value = expected_map.GetMutableValue(
- key::kWebAppInstallForceList, base::Value::Type::LIST);
- web_app_value->Append(std::move(first_app));
- web_app_value->Append(std::move(second_app));
-
- migrator_.Migrate(&policy_map_);
-
- EXPECT_TRUE(policy_map_.Equals(expected_map));
-}
-
-// Tests the case when ExtensionInstallBlocklist is initially set to wrong type
-// and we have to append chrome app id to it. The value should be overridden and
-// error message should be added.
-TEST_F(DefaultChromeAppsMigratorTest, ExtensionBlocklistPolicyWrongType) {
- PolicyMap expected_map(policy_map_.Clone());
-
- // Add force installed chrome app.
- base::Value* forcelist_value = policy_map_.GetMutableValue(
- key::kExtensionInstallForcelist, base::Value::Type::LIST);
- forcelist_value->Append(kAppId1);
-
- // Set ExtensionInstallBlocklist to non-list type.
- base::Value blocklist_value(base::Value::Type::DICTIONARY);
- policy_map_.GetMutable(key::kExtensionInstallBlocklist)
- ->set_value(std::move(blocklist_value));
-
- base::Value blocklist_expected_value(base::Value::Type::LIST);
- blocklist_expected_value.Append(kAppId1);
- PolicyMap::Entry* blocklist_expected_entry =
- expected_map.GetMutable(key::kExtensionInstallBlocklist);
- blocklist_expected_entry->set_value(std::move(blocklist_expected_value));
- blocklist_expected_entry->AddMessage(PolicyMap::MessageType::kError,
- IDS_POLICY_TYPE_ERROR);
-
- // Corresponding web app should be force installed after migration.
- base::Value first_app(base::Value::Type::DICTIONARY);
- first_app.SetStringKey("url", kWebAppUrl1);
- base::Value* web_app_value = expected_map.GetMutableValue(
- key::kWebAppInstallForceList, base::Value::Type::LIST);
- web_app_value->Append(std::move(first_app));
+ base::Value::Dict first_app = CreateWebAppDict(kWebAppUrl1, kAppId1);
+ base::Value::Dict second_app = CreateWebAppDict(kWebAppUrl2, kAppId2);
+ base::Value::List& web_app_list =
+ expected_map
+ .GetMutableValue(key::kWebAppInstallForceList,
+ base::Value::Type::LIST)
+ ->GetList();
+ web_app_list.Append(std::move(first_app));
+ web_app_list.Append(std::move(second_app));
migrator_.Migrate(&policy_map_);
@@ -172,7 +137,7 @@ TEST_F(DefaultChromeAppsMigratorTest, ExtensionBlocklistPolicyWrongType) {
TEST_F(DefaultChromeAppsMigratorTest, WebAppPolicyWrongType) {
PolicyMap expected_map(policy_map_.Clone());
- // Add force installed chrome app.
+ // Add force installed Chrome App.
base::Value* forcelist_value = policy_map_.GetMutableValue(
key::kExtensionInstallForcelist, base::Value::Type::LIST);
forcelist_value->Append(kAppId1);
@@ -182,18 +147,13 @@ TEST_F(DefaultChromeAppsMigratorTest, WebAppPolicyWrongType) {
policy_map_.GetMutable(key::kWebAppInstallForceList)
->set_value(std::move(web_app_value));
- // Chrome app should be blocked after migration.
- base::Value* blocklist_value = expected_map.GetMutableValue(
- key::kExtensionInstallBlocklist, base::Value::Type::LIST);
- blocklist_value->Append(kAppId1);
-
- base::Value web_app_expected_value(base::Value::Type::LIST);
- base::Value first_app(base::Value::Type::DICTIONARY);
- first_app.SetStringKey("url", kWebAppUrl1);
- web_app_expected_value.Append(std::move(first_app));
+ base::Value::List web_app_expected_list;
+ base::Value::Dict web_app = CreateWebAppDict(kWebAppUrl1, kAppId1);
+ web_app_expected_list.Append(std::move(web_app));
PolicyMap::Entry* web_app_expected_entry =
expected_map.GetMutable(key::kWebAppInstallForceList);
- web_app_expected_entry->set_value(std::move(web_app_expected_value));
+ web_app_expected_entry->set_value(
+ base::Value(std::move(web_app_expected_list)));
web_app_expected_entry->AddMessage(PolicyMap::MessageType::kError,
IDS_POLICY_TYPE_ERROR);
@@ -205,27 +165,24 @@ TEST_F(DefaultChromeAppsMigratorTest, WebAppPolicyWrongType) {
TEST_F(DefaultChromeAppsMigratorTest, PinnedApp) {
PolicyMap expected_map(policy_map_.Clone());
- // Add force installed chrome app that should be migrated.
+ // Add force installed Chrome App that should be migrated.
base::Value* forcelist_value = policy_map_.GetMutableValue(
key::kExtensionInstallForcelist, base::Value::Type::LIST);
forcelist_value->Append(std::string(kAppId1));
- // Make the chrome app pinned.
+ // Make the Chrome App pinned.
base::Value* pinned_apps_value = policy_map_.GetMutableValue(
key::kPinnedLauncherApps, base::Value::Type::LIST);
pinned_apps_value->Append(std::string(kAppId1));
- // Chrome app should be blocked after migration.
- base::Value* blocklist_value = expected_map.GetMutableValue(
- key::kExtensionInstallBlocklist, base::Value::Type::LIST);
- blocklist_value->Append(std::string(kAppId1));
-
// Corresponding web app should be force installed after migration.
- base::Value first_app(base::Value::Type::DICTIONARY);
- first_app.SetStringKey("url", kWebAppUrl1);
- base::Value* web_app_value = expected_map.GetMutableValue(
- key::kWebAppInstallForceList, base::Value::Type::LIST);
- web_app_value->Append(std::move(first_app));
+ base::Value::Dict web_app = CreateWebAppDict(kWebAppUrl1, kAppId1);
+ base::Value::List& web_app_list =
+ expected_map
+ .GetMutableValue(key::kWebAppInstallForceList,
+ base::Value::Type::LIST)
+ ->GetList();
+ web_app_list.Append(std::move(web_app));
// The corresponding Web App should be pinned.
base::Value* pinned_expected_value = expected_map.GetMutableValue(
diff --git a/chromium/components/policy/core/common/features.cc b/chromium/components/policy/core/common/features.cc
index b1b69e0b35d..954caa6db91 100644
--- a/chromium/components/policy/core/common/features.cc
+++ b/chromium/components/policy/core/common/features.cc
@@ -13,28 +13,29 @@ namespace features {
const base::Feature kDefaultChromeAppsMigration{
"EnableDefaultAppsMigration", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kUploadBrowserDeviceIdentifier{
- "UploadBrowserDeviceIdentifier", base::FEATURE_ENABLED_BY_DEFAULT};
-
const base::Feature kLoginEventReporting{"LoginEventReporting",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kPasswordBreachEventReporting{
"PasswordBreachEventReporting", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kChromeManagementPageAndroid{
- "ChromeManagementPageAndroid", base::FEATURE_ENABLED_BY_DEFAULT};
-
const base::Feature kEnableUserCloudSigninRestrictionPolicyFetcher{
"UserCloudSigninRestrictionPolicyFetcher",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kActivateMetricsReportingEnabledPolicyAndroid{
"ActivateMetricsReportingEnabledPolicyAndroid",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kEnableCachedManagementStatus{
"EnableCachedManagementStatus", base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kDmTokenDeletion{"DmTokenDeletion",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kPolicyScopeDetectionMac{"PolicyScopeDetectionMac",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
} // namespace features
} // namespace policy
diff --git a/chromium/components/policy/core/common/features.h b/chromium/components/policy/core/common/features.h
index 6ade4f6b922..e36c04c8234 100644
--- a/chromium/components/policy/core/common/features.h
+++ b/chromium/components/policy/core/common/features.h
@@ -14,15 +14,9 @@
namespace policy {
namespace features {
-// Enable chrome://management page on Android.
-POLICY_EXPORT extern const base::Feature kChromeManagementPageAndroid;
-
// Enable force installed Chrome apps policy migration.
POLICY_EXPORT extern const base::Feature kDefaultChromeAppsMigration;
-// Update browser device identifier during enrollment and fetching policies.
-POLICY_EXPORT extern const base::Feature kUploadBrowserDeviceIdentifier;
-
// Enable reporting Login events to the reporting connector when the Password
// Manager detects that the user logged in to a web page.
POLICY_EXPORT extern const base::Feature kLoginEventReporting;
@@ -44,6 +38,14 @@ POLICY_EXPORT extern const base::Feature
// Enable caching the value of the ManagementStatus.
POLICY_EXPORT extern const base::Feature kEnableCachedManagementStatus;
+// Causes the DMToken to be deleted (rather than invalidated) when a browser is
+// deleted from CBCM.
+POLICY_EXPORT extern const base::Feature kDmTokenDeletion;
+
+// Allow mac to detect policy scope with a private API. The feature is limited
+// in the policy component only.
+extern const base::Feature kPolicyScopeDetectionMac;
+
} // namespace features
} // namespace policy
diff --git a/chromium/components/policy/core/common/legacy_chrome_policy_migrator.cc b/chromium/components/policy/core/common/legacy_chrome_policy_migrator.cc
deleted file mode 100644
index bf3a1f05d09..00000000000
--- a/chromium/components/policy/core/common/legacy_chrome_policy_migrator.cc
+++ /dev/null
@@ -1,34 +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 "components/policy/core/common/legacy_chrome_policy_migrator.h"
-
-#include <string>
-
-#include "components/policy/core/common/policy_bundle.h"
-#include "components/policy/core/common/policy_map.h"
-#include "components/policy/core/common/policy_namespace.h"
-
-namespace policy {
-
-LegacyChromePolicyMigrator::LegacyChromePolicyMigrator(const char* old_name,
- const char* new_name)
- : migration_(old_name, new_name) {}
-
-LegacyChromePolicyMigrator::LegacyChromePolicyMigrator(
- const char* old_name,
- const char* new_name,
- Migration::ValueTransform transform)
- : migration_(old_name, new_name, transform) {}
-
-LegacyChromePolicyMigrator::~LegacyChromePolicyMigrator() = default;
-
-void LegacyChromePolicyMigrator::Migrate(policy::PolicyBundle* bundle) {
- policy::PolicyMap& chrome_map =
- bundle->Get(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, ""));
-
- CopyPolicyIfUnset(chrome_map, &chrome_map, migration_);
-}
-
-} // namespace policy
diff --git a/chromium/components/policy/core/common/legacy_chrome_policy_migrator.h b/chromium/components/policy/core/common/legacy_chrome_policy_migrator.h
deleted file mode 100644
index 0012b6463ec..00000000000
--- a/chromium/components/policy/core/common/legacy_chrome_policy_migrator.h
+++ /dev/null
@@ -1,40 +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 COMPONENTS_POLICY_CORE_COMMON_LEGACY_CHROME_POLICY_MIGRATOR_H_
-#define COMPONENTS_POLICY_CORE_COMMON_LEGACY_CHROME_POLICY_MIGRATOR_H_
-
-#include "components/policy/core/common/policy_migrator.h"
-
-namespace policy {
-
-// LegacyChromePolicyMigrator migrates a deprecated Chrome domain policy to a
-// new name, setting up the new policy based on the old one.
-//
-// This is intended to be used for policies that do not have a corresponding
-// pref. If the policy has a pref, please use
-// |LegacyPoliciesDeprecatingPolicyHandler| instead.
-class POLICY_EXPORT LegacyChromePolicyMigrator : public PolicyMigrator {
- public:
- using Migration = PolicyMigrator::Migration;
-
- LegacyChromePolicyMigrator(const char* old_name, const char* new_name);
- LegacyChromePolicyMigrator(const char* old_name,
- const char* new_name,
- Migration::ValueTransform transform);
- ~LegacyChromePolicyMigrator() override;
-
- LegacyChromePolicyMigrator(const LegacyChromePolicyMigrator&) = delete;
- LegacyChromePolicyMigrator& operator=(const LegacyChromePolicyMigrator&) =
- delete;
-
- void Migrate(policy::PolicyBundle* bundle) override;
-
- private:
- Migration migration_;
-};
-
-} // namespace policy
-
-#endif // COMPONENTS_POLICY_CORE_COMMON_LEGACY_CHROME_POLICY_MIGRATOR_H_
diff --git a/chromium/components/policy/core/common/legacy_chrome_policy_migrator_unittest.cc b/chromium/components/policy/core/common/legacy_chrome_policy_migrator_unittest.cc
deleted file mode 100644
index 3259f5013d8..00000000000
--- a/chromium/components/policy/core/common/legacy_chrome_policy_migrator_unittest.cc
+++ /dev/null
@@ -1,124 +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 "components/policy/core/common/legacy_chrome_policy_migrator.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace policy {
-
-namespace {
-const char kOldPolicy[] = "OldPolicy";
-const char kNewPolicy[] = "NewPolicy";
-const char kOtherPolicy[] = "OtherPolicy";
-
-const int kOldValue = 111;
-const int kNewValue = 222;
-const int kTransformedValue = 333;
-const int kOtherValue = 999;
-
-void MultiplyByThree(base::Value* val) {
- *val = base::Value(val->GetInt() * 3);
-}
-
-void SetPolicy(PolicyMap* policy, const char* policy_name, base::Value value) {
- policy->Set(policy_name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
- POLICY_SOURCE_CLOUD, std::move(value), nullptr);
-}
-
-} // namespace
-
-TEST(LegacyChromePolicyMigratorTest, CopyPolicyIfUnset) {
- PolicyBundle bundle;
-
- PolicyMap& chrome_map = bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
-
- SetPolicy(&chrome_map, kOldPolicy, base::Value(kOldValue));
- SetPolicy(&chrome_map, kOtherPolicy, base::Value(kOtherValue));
-
- LegacyChromePolicyMigrator migrator(kOldPolicy, kNewPolicy);
-
- migrator.Migrate(&bundle);
-
- // kOldPolicy should have been copied to kNewPolicy, kOtherPolicy remains
- EXPECT_EQ(3u, chrome_map.size());
- ASSERT_TRUE(chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
- // Old Value should be copied over.
- EXPECT_EQ(base::Value(kOldValue),
- *chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
- // Other Value should be unchanged.
- EXPECT_EQ(base::Value(kOtherValue),
- *chrome_map.GetValue(kOtherPolicy, base::Value::Type::INTEGER));
- base::RepeatingCallback<std::u16string(int)> l10nlookup =
- base::BindRepeating(&l10n_util::GetStringUTF16);
- // Old policy should always be marked deprecated
- EXPECT_FALSE(
- chrome_map.Get(kOldPolicy)
- ->GetLocalizedMessages(PolicyMap::MessageType::kError, l10nlookup)
- .empty());
- EXPECT_FALSE(
- chrome_map.Get(kNewPolicy)
- ->GetLocalizedMessages(PolicyMap::MessageType::kWarning, l10nlookup)
- .empty());
-}
-
-TEST(LegacyChromePolicyMigratorTest, TransformPolicy) {
- PolicyBundle bundle;
-
- PolicyMap& chrome_map = bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
-
- SetPolicy(&chrome_map, kOldPolicy, base::Value(kOldValue));
-
- LegacyChromePolicyMigrator migrator(kOldPolicy, kNewPolicy,
- base::BindRepeating(&MultiplyByThree));
-
- migrator.Migrate(&bundle);
-
- ASSERT_TRUE(chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
- // Old Value should be transformed
- EXPECT_EQ(base::Value(kTransformedValue),
- *chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
-}
-
-TEST(LegacyChromePolicyMigratorTest, IgnoreOldIfNewIsSet) {
- PolicyBundle bundle;
-
- PolicyMap& chrome_map = bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
-
- SetPolicy(&chrome_map, kOldPolicy, base::Value(kOldValue));
- SetPolicy(&chrome_map, kNewPolicy, base::Value(kNewValue));
-
- LegacyChromePolicyMigrator migrator(kOldPolicy, kNewPolicy);
-
- migrator.Migrate(&bundle);
- // New Value is unchanged
- EXPECT_EQ(base::Value(kNewValue),
- *chrome_map.GetValue(kNewPolicy, base::Value::Type::INTEGER));
- // Should be no warning on new policy
- base::RepeatingCallback<std::u16string(int)> l10nlookup =
- base::BindRepeating(&l10n_util::GetStringUTF16);
- // Old policy should always be marked deprecated
- EXPECT_FALSE(
- chrome_map.Get(kOldPolicy)
- ->GetLocalizedMessages(PolicyMap::MessageType::kError, l10nlookup)
- .empty());
- // No warnings on new policy because it was unchanged.
- EXPECT_TRUE(
- chrome_map.Get(kNewPolicy)
- ->GetLocalizedMessages(PolicyMap::MessageType::kWarning, l10nlookup)
- .empty());
- EXPECT_TRUE(
- chrome_map.Get(kNewPolicy)
- ->GetLocalizedMessages(PolicyMap::MessageType::kError, l10nlookup)
- .empty());
-}
-
-} // namespace policy
diff --git a/chromium/components/policy/core/common/mac_util.cc b/chromium/components/policy/core/common/mac_util.cc
index c94255aaa7d..2c6cf83753f 100644
--- a/chromium/components/policy/core/common/mac_util.cc
+++ b/chromium/components/policy/core/common/mac_util.cc
@@ -39,8 +39,10 @@ void DictionaryEntryToValue(const void* key, const void* value, void* context) {
void ArrayEntryToValue(const void* value, void* context) {
std::unique_ptr<base::Value> converted =
PropertyToValue(static_cast<CFPropertyListRef>(value));
- if (converted)
- static_cast<base::ListValue*>(context)->Append(std::move(converted));
+ if (converted) {
+ static_cast<base::ListValue*>(context)->GetList().Append(
+ base::Value::FromUniquePtrValue(std::move(converted)));
+ }
}
} // namespace
diff --git a/chromium/components/policy/core/common/mac_util_unittest.cc b/chromium/components/policy/core/common/mac_util_unittest.cc
index b3e7826b871..dcfa1186587 100644
--- a/chromium/components/policy/core/common/mac_util_unittest.cc
+++ b/chromium/components/policy/core/common/mac_util_unittest.cc
@@ -41,9 +41,9 @@ TEST(PolicyMacUtilTest, PropertyToValue) {
root.Set("emptyl", std::make_unique<base::Value>(base::Value::Type::LIST));
base::ListValue list;
for (base::DictionaryValue::Iterator it(root); !it.IsAtEnd(); it.Advance())
- list.Append(std::make_unique<base::Value>(it.value().Clone()));
- EXPECT_EQ(root.DictSize(), list.GetListDeprecated().size());
- list.Append(std::make_unique<base::Value>(root.Clone()));
+ list.GetList().Append(it.value().Clone());
+ EXPECT_EQ(root.DictSize(), list.GetList().size());
+ list.GetList().Append(root.Clone());
root.SetKey("list", list.Clone());
// base::Value::Type::DICTIONARY
diff --git a/chromium/components/policy/core/common/management/platform_management_status_provider_mac.cc b/chromium/components/policy/core/common/management/platform_management_status_provider_mac.cc
index 9a2e8206550..14151be31c1 100644
--- a/chromium/components/policy/core/common/management/platform_management_status_provider_mac.cc
+++ b/chromium/components/policy/core/common/management/platform_management_status_provider_mac.cc
@@ -4,6 +4,7 @@
#include "components/policy/core/common/management/platform_management_status_provider_mac.h"
+#include "base/enterprise_util.h"
#include "components/policy/core/common/policy_pref_names.h"
namespace policy {
@@ -15,7 +16,7 @@ DomainEnrollmentStatusProvider::DomainEnrollmentStatusProvider() {
DomainEnrollmentStatusProvider::~DomainEnrollmentStatusProvider() = default;
EnterpriseManagementAuthority DomainEnrollmentStatusProvider::FetchAuthority() {
- return domain_join_state_.device_joined || domain_join_state_.user_joined
+ return base::IsEnterpriseDevice()
? EnterpriseManagementAuthority::DOMAIN_LOCAL
: EnterpriseManagementAuthority::NONE;
}
@@ -28,26 +29,8 @@ EnterpriseMDMManagementStatusProvider::
EnterpriseManagementAuthority
EnterpriseMDMManagementStatusProvider::FetchAuthority() {
- base::MacDeviceManagementStateNew mdm_state_new =
- base::IsDeviceRegisteredWithManagementNew();
-
- bool managed = false;
- switch (mdm_state_new) {
- case base::MacDeviceManagementStateNew::kLimitedMDMEnrollment:
- case base::MacDeviceManagementStateNew::kFullMDMEnrollment:
- case base::MacDeviceManagementStateNew::kDEPMDMEnrollment:
- managed = true;
- break;
- case base::MacDeviceManagementStateNew::kFailureAPIUnavailable:
- managed = base::MacDeviceManagementStateOld::kMDMEnrollment ==
- base::IsDeviceRegisteredWithManagementOld();
- break;
- default:
- break;
- }
-
- return managed ? EnterpriseManagementAuthority::CLOUD
- : EnterpriseManagementAuthority::NONE;
+ return base::IsManagedDevice() ? EnterpriseManagementAuthority::CLOUD
+ : EnterpriseManagementAuthority::NONE;
}
} // namespace policy
diff --git a/chromium/components/policy/core/common/policy_loader_common.cc b/chromium/components/policy/core/common/policy_loader_common.cc
index 5d64293bfd6..7e7edccc3fe 100644
--- a/chromium/components/policy/core/common/policy_loader_common.cc
+++ b/chromium/components/policy/core/common/policy_loader_common.cc
@@ -46,6 +46,7 @@ const char* kSensitivePolicies[] = {
key::kChromeCleanupReportingEnabled,
key::kCommandLineFlagSecurityWarningsEnabled,
key::kDefaultSearchProviderEnabled,
+ key::kFirstPartySetsOverrides,
key::kHomepageIsNewTabPage,
key::kHomepageLocation,
key::kMetricsReportingEnabled,
@@ -87,8 +88,8 @@ bool FilterSensitiveExtensionsInstallForcelist(PolicyMap::Entry* map_entry) {
continue;
// Only allow custom update urls in enterprise environments.
- if (!base::LowerCaseEqualsASCII(entry.substr(pos + 1),
- kChromeWebstoreUpdateURL)) {
+ if (!base::EqualsCaseInsensitiveASCII(entry.substr(pos + 1),
+ kChromeWebstoreUpdateURL)) {
policy_list_value->GetListDeprecated()[i] =
base::Value(kBlockedExtensionPrefix + entry);
has_invalid_policies = true;
@@ -130,8 +131,8 @@ bool FilterSensitiveExtensionSettings(PolicyMap::Entry* map_entry) {
continue;
}
std::string* update_url = entry.second.FindStringKey(kUpdateUrl);
- if (!update_url ||
- base::LowerCaseEqualsASCII(*update_url, kChromeWebstoreUpdateURL)) {
+ if (!update_url || base::EqualsCaseInsensitiveASCII(
+ *update_url, kChromeWebstoreUpdateURL)) {
continue;
}
diff --git a/chromium/components/policy/core/common/policy_loader_ios.mm b/chromium/components/policy/core/common/policy_loader_ios.mm
index df43149d399..5faa0610ffa 100644
--- a/chromium/components/policy/core/common/policy_loader_ios.mm
+++ b/chromium/components/policy/core/common/policy_loader_ios.mm
@@ -160,7 +160,9 @@ base::Value PolicyLoaderIOS::ConvertPolicyDataIfNecessary(
}
// Handle the case of a JSON-encoded string for a dict policy.
- if (schema.type() == base::Value::Type::DICTIONARY && value.is_string()) {
+ if ((schema.type() == base::Value::Type::DICTIONARY ||
+ schema.type() == base::Value::Type::LIST) &&
+ value.is_string()) {
absl::optional<base::Value> decoded_value = base::JSONReader::Read(
value.GetString(), base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
if (decoded_value.has_value()) {
diff --git a/chromium/components/policy/core/common/policy_loader_ios_unittest.mm b/chromium/components/policy/core/common/policy_loader_ios_unittest.mm
index d5add46cda4..c58c38ea2c5 100644
--- a/chromium/components/policy/core/common/policy_loader_ios_unittest.mm
+++ b/chromium/components/policy/core/common/policy_loader_ios_unittest.mm
@@ -132,7 +132,17 @@ void TestHarness::InstallStringListPolicy(const std::string& policy_name,
NSString* key = base::SysUTF8ToNSString(policy_name);
base::ScopedCFTypeRef<CFPropertyListRef> value(
ValueToProperty(*policy_value));
- AddPolicies(@{key : (__bridge NSArray*)(value.get())});
+
+ if (encode_complex_data_as_json_) {
+ // Convert |policy_value| to a JSON-encoded string.
+ std::string json_string;
+ JSONStringValueSerializer serializer(&json_string);
+ ASSERT_TRUE(serializer.Serialize(*policy_value));
+
+ AddPolicies(@{key : base::SysUTF8ToNSString(json_string)});
+ } else {
+ AddPolicies(@{key : (__bridge NSArray*)(value.get())});
+ }
}
void TestHarness::InstallDictionaryPolicy(const std::string& policy_name,
diff --git a/chromium/components/policy/core/common/policy_loader_lacros.cc b/chromium/components/policy/core/common/policy_loader_lacros.cc
index 65a40e90dc6..3dcfa818223 100644
--- a/chromium/components/policy/core/common/policy_loader_lacros.cc
+++ b/chromium/components/policy/core/common/policy_loader_lacros.cc
@@ -15,6 +15,7 @@
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/lacros/lacros_service.h"
+#include "chromeos/startup/browser_init_params.h"
#include "components/policy/core/common/cloud/affiliation.h"
#include "components/policy/core/common/cloud/cloud_policy_validator.h"
#include "components/policy/core/common/policy_bundle.h"
@@ -42,7 +43,7 @@ bool IsManaged(const enterprise_management::PolicyData& policy_data) {
// Returns whether a primary device account for this session is child.
bool IsChildSession() {
const crosapi::mojom::BrowserInitParams* init_params =
- chromeos::LacrosService::Get()->init_params();
+ chromeos::BrowserInitParams::Get();
if (!init_params) {
return false;
}
@@ -59,18 +60,23 @@ PolicyLoaderLacros::PolicyLoaderLacros(
PolicyPerProfileFilter per_profile)
: AsyncPolicyLoader(task_runner, /*periodic_updates=*/false),
per_profile_(per_profile) {
- auto* lacros_service = chromeos::LacrosService::Get();
const crosapi::mojom::BrowserInitParams* init_params =
- lacros_service->init_params();
+ chromeos::BrowserInitParams::Get();
if (!init_params) {
LOG(ERROR) << "No init params";
return;
}
+ if (per_profile_ == PolicyPerProfileFilter::kTrue &&
+ init_params->device_account_component_policy) {
+ SetComponentPolicy(init_params->device_account_component_policy.value());
+ }
if (!init_params->device_account_policy) {
LOG(ERROR) << "No policy data";
return;
}
policy_fetch_response_ = init_params->device_account_policy.value();
+ last_fetch_timestamp_ =
+ base::Time::FromTimeT(init_params->last_policy_fetch_attempt_timestamp);
}
PolicyLoaderLacros::~PolicyLoaderLacros() {
@@ -98,6 +104,10 @@ std::unique_ptr<PolicyBundle> PolicyLoaderLacros::Load() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unique_ptr<PolicyBundle> bundle = std::make_unique<PolicyBundle>();
+ // If per_profile loader is used, apply policy for extensions.
+ if (per_profile_ == PolicyPerProfileFilter::kTrue && component_policy_)
+ bundle->MergeFrom(*component_policy_);
+
if (!policy_fetch_response_ || policy_fetch_response_->empty()) {
return bundle;
}
@@ -155,6 +165,47 @@ void PolicyLoaderLacros::OnPolicyUpdated(
Reload(true);
}
+void PolicyLoaderLacros::OnPolicyFetchAttempt() {
+ last_fetch_timestamp_ = base::Time::Now();
+}
+
+void PolicyLoaderLacros::OnComponentPolicyUpdated(
+ const policy::ComponentPolicyMap& component_policy) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // The component policy is per_profile=true policy. If Lacros is using
+ // secondary profile, that policy is loaded directly from DMServer. In case
+ // it is using the device account, there are two PolicyLoaderLacros objects
+ // present, and we need to store it only in the object with per_profile:True.
+ if (per_profile_ == PolicyPerProfileFilter::kFalse) {
+ return;
+ }
+
+ SetComponentPolicy(component_policy);
+ Reload(true);
+}
+
+void PolicyLoaderLacros::SetComponentPolicy(
+ const policy::ComponentPolicyMap& component_policy) {
+ if (component_policy_) {
+ component_policy_->Clear();
+ } else {
+ component_policy_ = std::make_unique<PolicyBundle>();
+ }
+ for (auto& policy_pair : component_policy) {
+ PolicyMap component_policy_map;
+ std::string error;
+ // The component policy received from Ash is the JSON data corresponding to
+ // the policy for the namespace.
+ ParseComponentPolicy(policy_pair.second.Clone(), POLICY_SCOPE_USER,
+ POLICY_SOURCE_CLOUD_FROM_ASH, &component_policy_map,
+ &error);
+ DCHECK(error.empty());
+
+ // The data is also good; expose the policies.
+ component_policy_->Get(policy_pair.first).Swap(&component_policy_map);
+ }
+}
+
enterprise_management::PolicyData* PolicyLoaderLacros::GetPolicyData() {
if (!policy_fetch_response_ || !policy_data_)
return nullptr;
@@ -165,7 +216,7 @@ enterprise_management::PolicyData* PolicyLoaderLacros::GetPolicyData() {
// static
bool PolicyLoaderLacros::IsDeviceLocalAccountUser() {
const crosapi::mojom::BrowserInitParams* init_params =
- chromeos::LacrosService::Get()->init_params();
+ chromeos::BrowserInitParams::Get();
if (!init_params) {
return false;
}
@@ -185,7 +236,7 @@ bool PolicyLoaderLacros::IsMainUserAffiliated() {
const enterprise_management::PolicyData* policy =
policy::PolicyLoaderLacros::main_user_policy_data();
const crosapi::mojom::BrowserInitParams* init_params =
- chromeos::LacrosService::Get()->init_params();
+ chromeos::BrowserInitParams::Get();
// To align with `DeviceLocalAccountUserBase::IsAffiliated()`, a device local
// account user is always treated as affiliated.
diff --git a/chromium/components/policy/core/common/policy_loader_lacros.h b/chromium/components/policy/core/common/policy_loader_lacros.h
index dcbc7e31c8e..2e9199f3632 100644
--- a/chromium/components/policy/core/common/policy_loader_lacros.h
+++ b/chromium/components/policy/core/common/policy_loader_lacros.h
@@ -12,9 +12,11 @@
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
#include "chromeos/lacros/lacros_service.h"
#include "components/policy/core/common/async_policy_loader.h"
#include "components/policy/core/common/policy_proto_decoders.h"
+#include "components/policy/core/common/values_util.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -51,11 +53,24 @@ class POLICY_EXPORT PolicyLoaderLacros
// initial load was not done yet.
enterprise_management::PolicyData* GetPolicyData();
- // LacrosChromeServiceDelegateImpl::Observer implementation.
+ // chromeos::LacrosService::Observer implementation.
// Update and reload the policy with new data.
void OnPolicyUpdated(
const std::vector<uint8_t>& policy_fetch_response) override;
+ // chromeos::LacrosService::Observer implementation.
+ // Update the latest policy fetch attempt timestamp.
+ void OnPolicyFetchAttempt() override;
+
+ // chromeos::LacrosService::Observer implementation.
+ void OnComponentPolicyUpdated(
+ const policy::ComponentPolicyMap& component_policy) override;
+
+ // Returns the current device account policies for components.
+ const PolicyBundle* component_policy() const {
+ return component_policy_.get();
+ }
+
// Return if the main user is a device local account (i.e. Kiosk, MGS) user.
static bool IsDeviceLocalAccountUser();
@@ -73,16 +88,26 @@ class POLICY_EXPORT PolicyLoaderLacros
static void set_main_user_policy_data_for_testing(
const enterprise_management::PolicyData& policy_data);
+ base::Time last_fetch_timestamp() { return last_fetch_timestamp_; }
+
private:
+ void SetComponentPolicy(const policy::ComponentPolicyMap& component_policy);
+
// The filter for policy data to install.
const PolicyPerProfileFilter per_profile_;
// Serialized blob of PolicyFetchResponse object received from the server.
absl::optional<std::vector<uint8_t>> policy_fetch_response_;
+ // The component policy of the device account.
+ std::unique_ptr<PolicyBundle> component_policy_;
+
// The parsed policy objects received from Ash.
std::unique_ptr<enterprise_management::PolicyData> policy_data_;
+ // Timestamp at which last policy fetch was attempted.
+ base::Time last_fetch_timestamp_;
+
// Checks that the method is called on the right sequence.
SEQUENCE_CHECKER(sequence_checker_);
};
diff --git a/chromium/components/policy/core/common/policy_loader_lacros_unittest.cc b/chromium/components/policy/core/common/policy_loader_lacros_unittest.cc
index 5143e4ef980..209b1c288ec 100644
--- a/chromium/components/policy/core/common/policy_loader_lacros_unittest.cc
+++ b/chromium/components/policy/core/common/policy_loader_lacros_unittest.cc
@@ -9,8 +9,8 @@
#include "base/values.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
-#include "chromeos/lacros/lacros_service.h"
#include "chromeos/lacros/lacros_test_helper.h"
+#include "chromeos/startup/browser_init_params.h"
#include "components/policy/core/common/async_policy_provider.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/configuration_policy_provider_test.h"
@@ -72,8 +72,7 @@ class PolicyLoaderLacrosTest : public PolicyTestBase {
std::vector<uint8_t> data = GetValidPolicyFetchResponseWithAllPolicy();
auto init_params = crosapi::mojom::BrowserInitParams::New();
init_params->device_account_policy = data;
- chromeos::LacrosService::Get()->SetInitParamsForTests(
- std::move(init_params));
+ chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
}
void CheckProfilePolicies(const PolicyMap& policy_map) const {
@@ -124,8 +123,7 @@ class PolicyLoaderLacrosTest : public PolicyTestBase {
crosapi::mojom::SessionType session_type) {
auto init_params = crosapi::mojom::BrowserInitParams::New();
init_params->session_type = session_type;
- chromeos::LacrosService::Get()->SetInitParamsForTests(
- std::move(init_params));
+ chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
EXPECT_TRUE(PolicyLoaderLacros::IsDeviceLocalAccountUser());
EXPECT_TRUE(PolicyLoaderLacros::IsMainUserAffiliated());
}
@@ -159,7 +157,7 @@ TEST_F(PolicyLoaderLacrosTest, UpdateTestProfilePolicies) {
per_profile_ = PolicyPerProfileFilter::kTrue;
auto init_params = crosapi::mojom::BrowserInitParams::New();
- chromeos::LacrosService::Get()->SetInitParamsForTests(std::move(init_params));
+ chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
PolicyLoaderLacros* loader = new PolicyLoaderLacros(
task_environment_.GetMainThreadTaskRunner(), per_profile_);
@@ -181,7 +179,7 @@ TEST_F(PolicyLoaderLacrosTest, UpdateTestSystemWidePolicies) {
per_profile_ = PolicyPerProfileFilter::kFalse;
auto init_params = crosapi::mojom::BrowserInitParams::New();
- chromeos::LacrosService::Get()->SetInitParamsForTests(std::move(init_params));
+ chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
PolicyLoaderLacros* loader = new PolicyLoaderLacros(
task_environment_.GetMainThreadTaskRunner(), per_profile_);
@@ -202,7 +200,7 @@ TEST_F(PolicyLoaderLacrosTest, UpdateTestSystemWidePolicies) {
TEST_F(PolicyLoaderLacrosTest, TwoLoaders) {
auto init_params = crosapi::mojom::BrowserInitParams::New();
- chromeos::LacrosService::Get()->SetInitParamsForTests(std::move(init_params));
+ chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
PolicyLoaderLacros* system_wide_loader =
new PolicyLoaderLacros(task_environment_.GetMainThreadTaskRunner(),
@@ -251,7 +249,7 @@ TEST_F(PolicyLoaderLacrosTest, ChildUsersNoEnterpriseDefaults) {
auto init_params = crosapi::mojom::BrowserInitParams::New();
init_params->session_type = crosapi::mojom::SessionType::kChildSession;
init_params->device_account_policy = std::move(data);
- chromeos::LacrosService::Get()->SetInitParamsForTests(std::move(init_params));
+ chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
// Load the policy.
PolicyLoaderLacros loader(task_environment_.GetMainThreadTaskRunner(),
diff --git a/chromium/components/policy/core/common/policy_loader_mac.mm b/chromium/components/policy/core/common/policy_loader_mac.mm
index b717d39fbf1..13540d0d7d0 100644
--- a/chromium/components/policy/core/common/policy_loader_mac.mm
+++ b/chromium/components/policy/core/common/policy_loader_mac.mm
@@ -6,11 +6,12 @@
#include <utility>
+#include <Foundation/Foundation.h>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/enterprise_util.h"
-#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/mac/foundation_util.h"
@@ -20,7 +21,6 @@
#include "base/values.h"
#include "build/build_config.h"
#include "components/policy/core/common/external_data_fetcher.h"
-#include "components/policy/core/common/features.h"
#include "components/policy/core/common/mac_util.h"
#include "components/policy/core/common/policy_bundle.h"
#include "components/policy/core/common/policy_load_status.h"
@@ -38,25 +38,10 @@ namespace {
// Encapsulates logic to determine if enterprise policies should be honored.
bool ShouldHonorPolicies() {
- // Only honor sensitive policies if the Mac is managed externally.
- base::DeviceUserDomainJoinState join_state =
- base::AreDeviceAndUserJoinedToDomain();
- if (join_state.device_joined)
- return true;
-
- // IsDeviceRegisteredWithManagementNew is only available after 10.13.4.
- // Eventually switch to it when that is the minimum OS required by Chromium.
- if (@available(macOS 10.13.4, *)) {
- base::MacDeviceManagementStateNew mdm_state =
- base::IsDeviceRegisteredWithManagementNew();
- return mdm_state ==
- base::MacDeviceManagementStateNew::kLimitedMDMEnrollment ||
- mdm_state == base::MacDeviceManagementStateNew::kFullMDMEnrollment ||
- mdm_state == base::MacDeviceManagementStateNew::kDEPMDMEnrollment;
- }
- base::MacDeviceManagementStateOld mdm_state =
- base::IsDeviceRegisteredWithManagementOld();
- return mdm_state == base::MacDeviceManagementStateOld::kMDMEnrollment;
+ // Only honor sensitive policies if the Mac is managed or connected to an
+ // enterprise.
+ // TODO (crbug.com/1322121): Use PlatformManagementService instead.
+ return base::IsManagedOrEnterpriseDevice();
}
} // namespace
@@ -99,10 +84,13 @@ void PolicyLoaderMac::InitOnBackgroundThread() {
managed_policy_file_exists = true;
}
+ base::UmaHistogramBoolean("EnterpriseCheck.IsManagedOrEnterpriseDevice",
+ base::IsManagedOrEnterpriseDevice());
+
base::UmaHistogramBoolean("EnterpriseCheck.IsManaged2",
managed_policy_file_exists);
base::UmaHistogramBoolean("EnterpriseCheck.IsEnterpriseUser",
- base::IsMachineExternallyManaged());
+ base::IsEnterpriseDevice());
base::UmaHistogramEnumeration("EnterpriseCheck.Mac.IsDeviceMDMEnrolledOld",
base::IsDeviceRegisteredWithManagementOld());
@@ -140,11 +128,16 @@ std::unique_ptr<PolicyBundle> PolicyLoaderMac::Load() {
bool forced = preferences_->AppValueIsForced(name, application_id_);
PolicyLevel level =
forced ? POLICY_LEVEL_MANDATORY : POLICY_LEVEL_RECOMMENDED;
- // TODO(joaodasilva): figure the policy scope.
+ PolicyScope scope = POLICY_SCOPE_USER;
+ if (forced) {
+ scope = preferences_->IsManagedPolicyAvailableForMachineScope(name)
+ ? POLICY_SCOPE_MACHINE
+ : POLICY_SCOPE_USER;
+ }
std::unique_ptr<base::Value> policy = PropertyToValue(value);
if (policy) {
- chrome_policy.Set(it.key(), level, POLICY_SCOPE_MACHINE,
- POLICY_SOURCE_PLATFORM, std::move(*policy), nullptr);
+ chrome_policy.Set(it.key(), level, scope, POLICY_SOURCE_PLATFORM,
+ std::move(*policy), nullptr);
} else {
status.Add(POLICY_LOAD_STATUS_PARSE_ERROR);
}
@@ -238,9 +231,15 @@ void PolicyLoaderMac::LoadPolicyForComponent(
bool forced = preferences_->AppValueIsForced(pref_name, bundle_id);
PolicyLevel level =
forced ? POLICY_LEVEL_MANDATORY : POLICY_LEVEL_RECOMMENDED;
+ PolicyScope scope = POLICY_SCOPE_USER;
+ if (forced) {
+ scope = preferences_->IsManagedPolicyAvailableForMachineScope(pref_name)
+ ? POLICY_SCOPE_MACHINE
+ : POLICY_SCOPE_USER;
+ }
std::unique_ptr<base::Value> policy_value = PropertyToValue(value);
if (policy_value) {
- policy->Set(it.key(), level, POLICY_SCOPE_MACHINE, POLICY_SOURCE_PLATFORM,
+ policy->Set(it.key(), level, scope, POLICY_SOURCE_PLATFORM,
std::move(*policy_value), nullptr);
}
}
diff --git a/chromium/components/policy/core/common/policy_loader_mac_unittest.cc b/chromium/components/policy/core/common/policy_loader_mac_unittest.cc
index c8f0ca85231..ed39ba03c18 100644
--- a/chromium/components/policy/core/common/policy_loader_mac_unittest.cc
+++ b/chromium/components/policy/core/common/policy_loader_mac_unittest.cc
@@ -87,7 +87,7 @@ void TestHarness::InstallStringPolicy(const std::string& policy_name,
const std::string& policy_value) {
ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name));
ScopedCFTypeRef<CFStringRef> value(base::SysUTF8ToCFStringRef(policy_value));
- prefs_->AddTestItem(name, value, true);
+ prefs_->AddTestItem(name, value, /*is_forced=*/true, /*is_machine=*/true);
}
void TestHarness::InstallIntegerPolicy(const std::string& policy_name,
@@ -95,15 +95,14 @@ void TestHarness::InstallIntegerPolicy(const std::string& policy_name,
ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name));
ScopedCFTypeRef<CFNumberRef> value(
CFNumberCreate(NULL, kCFNumberIntType, &policy_value));
- prefs_->AddTestItem(name, value, true);
+ prefs_->AddTestItem(name, value, /*is_forced=*/true, /*is_machine=*/true);
}
void TestHarness::InstallBooleanPolicy(const std::string& policy_name,
bool policy_value) {
ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name));
- prefs_->AddTestItem(name,
- policy_value ? kCFBooleanTrue : kCFBooleanFalse,
- true);
+ prefs_->AddTestItem(name, policy_value ? kCFBooleanTrue : kCFBooleanFalse,
+ /*is_forced=*/true, /*is_machine=*/true);
}
void TestHarness::InstallStringListPolicy(const std::string& policy_name,
@@ -111,7 +110,7 @@ void TestHarness::InstallStringListPolicy(const std::string& policy_name,
ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name));
ScopedCFTypeRef<CFPropertyListRef> array(ValueToProperty(*policy_value));
ASSERT_TRUE(array);
- prefs_->AddTestItem(name, array, true);
+ prefs_->AddTestItem(name, array, /*is_forced=*/true, /*is_machine=*/true);
}
void TestHarness::InstallDictionaryPolicy(const std::string& policy_name,
@@ -119,7 +118,7 @@ void TestHarness::InstallDictionaryPolicy(const std::string& policy_name,
ScopedCFTypeRef<CFStringRef> name(base::SysUTF8ToCFStringRef(policy_name));
ScopedCFTypeRef<CFPropertyListRef> dict(ValueToProperty(*policy_value));
ASSERT_TRUE(dict);
- prefs_->AddTestItem(name, dict, true);
+ prefs_->AddTestItem(name, dict, /*is_forced=*/true, /*is_machine=*/true);
}
// static
@@ -169,8 +168,10 @@ TEST_F(PolicyLoaderMacTest, Invalid) {
CFDataCreate(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(buffer),
std::size(buffer)));
ASSERT_TRUE(invalid_data);
- prefs_->AddTestItem(name, invalid_data.get(), true);
- prefs_->AddTestItem(name, invalid_data.get(), false);
+ prefs_->AddTestItem(name, invalid_data.get(), /*is_forced=*/true,
+ /*is_machine=*/true);
+ prefs_->AddTestItem(name, invalid_data.get(), /*is_forced=*/false,
+ /*is_machine=*/true);
// Make the provider read the updated |prefs_|.
provider_->RefreshPolicies();
@@ -185,16 +186,35 @@ TEST_F(PolicyLoaderMacTest, TestNonForcedValue) {
ScopedCFTypeRef<CFPropertyListRef> test_value(
base::SysUTF8ToCFStringRef("string value"));
ASSERT_TRUE(test_value.get());
- prefs_->AddTestItem(name, test_value.get(), false);
+ prefs_->AddTestItem(name, test_value.get(), /*is_forced=*/false,
+ /*is_machine=*/true);
// Make the provider read the updated |prefs_|.
provider_->RefreshPolicies();
task_environment_.RunUntilIdle();
PolicyBundle expected_bundle;
expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
- .Set(test_keys::kKeyString, POLICY_LEVEL_RECOMMENDED,
- POLICY_SCOPE_MACHINE, POLICY_SOURCE_PLATFORM,
- base::Value("string value"), nullptr);
+ .Set(test_keys::kKeyString, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+ POLICY_SOURCE_PLATFORM, base::Value("string value"), nullptr);
+ EXPECT_TRUE(provider_->policies().Equals(expected_bundle));
+}
+
+TEST_F(PolicyLoaderMacTest, TestUserScopeValue) {
+ ScopedCFTypeRef<CFStringRef> name(
+ base::SysUTF8ToCFStringRef(test_keys::kKeyString));
+ ScopedCFTypeRef<CFPropertyListRef> test_value(
+ base::SysUTF8ToCFStringRef("string value"));
+ ASSERT_TRUE(test_value.get());
+ prefs_->AddTestItem(name, test_value.get(), /*is_forced=*/true,
+ /*is_machine=*/false);
+
+ // Make the provider read the updated |prefs_|.
+ provider_->RefreshPolicies();
+ task_environment_.RunUntilIdle();
+ PolicyBundle expected_bundle;
+ expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+ .Set(test_keys::kKeyString, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+ POLICY_SOURCE_PLATFORM, base::Value("string value"), nullptr);
EXPECT_TRUE(provider_->policies().Equals(expected_bundle));
}
diff --git a/chromium/components/policy/core/common/policy_loader_win.cc b/chromium/components/policy/core/common/policy_loader_win.cc
index d11e2aa15b2..fe063a3414a 100644
--- a/chromium/components/policy/core/common/policy_loader_win.cc
+++ b/chromium/components/policy/core/common/policy_loader_win.cc
@@ -175,13 +175,15 @@ void CollectEnterpriseUMAs() {
base::win::OSInfo::GetInstance()->version_type(),
base::win::SUITE_LAST);
+ base::UmaHistogramBoolean("EnterpriseCheck.IsManagedOrEnterpriseDevice",
+ base::IsManagedOrEnterpriseDevice());
base::UmaHistogramBoolean("EnterpriseCheck.IsDomainJoined", IsDomainJoined());
base::UmaHistogramBoolean("EnterpriseCheck.InDomain",
base::win::IsEnrolledToDomain());
base::UmaHistogramBoolean("EnterpriseCheck.IsManaged2",
base::win::IsDeviceRegisteredWithManagement());
base::UmaHistogramBoolean("EnterpriseCheck.IsEnterpriseUser",
- base::IsMachineExternallyManaged());
+ base::IsEnterpriseDevice());
base::UmaHistogramBoolean("EnterpriseCheck.IsJoinedToAzureAD",
base::win::IsJoinedToAzureAD());
diff --git a/chromium/components/policy/core/common/policy_loader_win_unittest.cc b/chromium/components/policy/core/common/policy_loader_win_unittest.cc
index 71472eeb856..685b4d53a7e 100644
--- a/chromium/components/policy/core/common/policy_loader_win_unittest.cc
+++ b/chromium/components/policy/core/common/policy_loader_win_unittest.cc
@@ -585,8 +585,8 @@ TEST_F(PolicyLoaderWinTest, LoadStringEncodedValues) {
policy.SetIntKey("int", -123);
policy.SetDoubleKey("double", 456.78e9);
base::ListValue list;
- list.Append(std::make_unique<base::Value>(policy.Clone()));
- list.Append(std::make_unique<base::Value>(policy.Clone()));
+ list.GetList().Append(policy.Clone());
+ list.GetList().Append(policy.Clone());
policy.SetKey("list", list.Clone());
// Encode |policy| before adding the "dict" entry.
std::string encoded_dict;
diff --git a/chromium/components/policy/core/common/policy_pref_names.cc b/chromium/components/policy/core/common/policy_pref_names.cc
index f7b947ab693..5b8bac235cc 100644
--- a/chromium/components/policy/core/common/policy_pref_names.cc
+++ b/chromium/components/policy/core/common/policy_pref_names.cc
@@ -21,6 +21,9 @@ const char kEnterpriseMDMManagementWindows[] =
// Integer pref that stores the Mac enterprise MDM management authority.
const char kEnterpriseMDMManagementMac[] =
"management.platform.enterprise_mdm_mac";
+// Boolean pref that indicates whether integration with macOS Screen Time should
+// be enabled.
+const char kScreenTimeEnabled[] = "policy.screen_time";
#endif
// 64-bit serialization of the time last policy usage statistics were collected
@@ -81,6 +84,11 @@ const char kIntensiveWakeUpThrottlingEnabled[] =
const char kSetTimeoutWithout1MsClampEnabled[] =
"policy.set_timeout_without_1ms_clamp";
+// Boolean policy preference for force enabling or disabling the
+// MaxUnthrottledTimeoutNestingLevel web feature.
+const char kUnthrottledNestedTimeoutEnabled[] =
+ "policy.unthrottled_nested_timeout";
+
#if BUILDFLAG(IS_ANDROID)
// Boolean policy preference to disable the BackForwardCache feature.
const char kBackForwardCacheEnabled[] = "policy.back_forward_cache_enabled";
@@ -110,5 +118,10 @@ const char kWebSQLAccess[] = "policy.web_sql_access";
const char kLastPolicyCheckTime[] = "policy.last_policy_check_time";
#endif
+#if BUILDFLAG(IS_IOS)
+const char kUserPolicyNotificationWasShown[] =
+ "policy.user_policy_notification_was_shown";
+#endif
+
} // namespace policy_prefs
} // namespace policy
diff --git a/chromium/components/policy/core/common/policy_pref_names.h b/chromium/components/policy/core/common/policy_pref_names.h
index 6dc51003113..53fed9e2b4d 100644
--- a/chromium/components/policy/core/common/policy_pref_names.h
+++ b/chromium/components/policy/core/common/policy_pref_names.h
@@ -21,6 +21,7 @@ POLICY_EXPORT extern const char kDlpReportingEnabled[];
POLICY_EXPORT extern const char kDlpRulesList[];
#if BUILDFLAG(IS_MAC)
POLICY_EXPORT extern const char kEnterpriseMDMManagementMac[];
+POLICY_EXPORT extern const char kScreenTimeEnabled[];
#endif
POLICY_EXPORT extern const char kLastPolicyStatisticsUpdate[];
POLICY_EXPORT extern const char kNativeWindowOcclusionEnabled[];
@@ -34,6 +35,7 @@ POLICY_EXPORT extern const char kIntensiveWakeUpThrottlingEnabled[];
POLICY_EXPORT extern const char kUserAgentClientHintsGREASEUpdateEnabled[];
POLICY_EXPORT extern const char kUrlParamFilterEnabled[];
POLICY_EXPORT extern const char kSetTimeoutWithout1MsClampEnabled[];
+POLICY_EXPORT extern const char kUnthrottledNestedTimeoutEnabled[];
#if BUILDFLAG(IS_ANDROID)
POLICY_EXPORT extern const char kBackForwardCacheEnabled[];
#endif // BUILDFLAG(IS_ANDROID)
@@ -42,6 +44,9 @@ POLICY_EXPORT extern const char kWebSQLAccess[];
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
POLICY_EXPORT extern const char kLastPolicyCheckTime[];
#endif
+#if BUILDFLAG(IS_IOS)
+POLICY_EXPORT extern const char kUserPolicyNotificationWasShown[];
+#endif
} // namespace policy_prefs
} // namespace policy
diff --git a/chromium/components/policy/core/common/policy_proto_decoders.cc b/chromium/components/policy/core/common/policy_proto_decoders.cc
index 11521b3b083..963c27e2e2c 100644
--- a/chromium/components/policy/core/common/policy_proto_decoders.cc
+++ b/chromium/components/policy/core/common/policy_proto_decoders.cc
@@ -10,6 +10,7 @@
#include "base/json/json_reader.h"
#include "base/logging.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
@@ -25,6 +26,10 @@ namespace em = enterprise_management;
namespace {
+const char kValue[] = "Value";
+const char kLevel[] = "Level";
+const char kRecommended[] = "Recommended";
+
// Returns true and sets |level| to a PolicyLevel if the policy has been set
// at that level. Returns false if the policy is not set, or has been set at
// the level of PolicyOptions::UNSET.
@@ -210,4 +215,42 @@ void DecodeProtoFields(
}
}
+bool ParseComponentPolicy(base::Value json,
+ PolicyScope scope,
+ PolicySource source,
+ PolicyMap* policy,
+ std::string* error) {
+ // Each top-level key maps a policy name to its description.
+ //
+ // Each description is an object that contains the policy value under the
+ // "Value" key. The optional "Level" key is either "Mandatory" (default) or
+ // "Recommended".
+ for (auto it : json.DictItems()) {
+ const std::string& policy_name = it.first;
+ base::Value description = std::move(it.second);
+ if (!description.is_dict()) {
+ *error = "The JSON blob dictionary value is not a dictionary.";
+ return false;
+ }
+
+ absl::optional<base::Value> value = description.ExtractKey(kValue);
+ if (!value.has_value()) {
+ *error = base::StrCat(
+ {"The JSON blob dictionary value doesn't contain the required ",
+ kValue, " field."});
+ return false;
+ }
+
+ PolicyLevel level = POLICY_LEVEL_MANDATORY;
+ const std::string* level_string = description.FindStringKey(kLevel);
+ if (level_string && *level_string == kRecommended)
+ level = POLICY_LEVEL_RECOMMENDED;
+
+ policy->Set(policy_name, level, scope, source, std::move(value.value()),
+ nullptr);
+ }
+
+ return true;
+}
+
} // namespace policy
diff --git a/chromium/components/policy/core/common/policy_proto_decoders.h b/chromium/components/policy/core/common/policy_proto_decoders.h
index 4a47256f188..b9a241f75fb 100644
--- a/chromium/components/policy/core/common/policy_proto_decoders.h
+++ b/chromium/components/policy/core/common/policy_proto_decoders.h
@@ -6,6 +6,7 @@
#define COMPONENTS_POLICY_CORE_COMMON_POLICY_PROTO_DECODERS_H_
#include "base/memory/weak_ptr.h"
+#include "base/values.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/policy_export.h"
@@ -40,6 +41,16 @@ POLICY_EXPORT void DecodeProtoFields(
PolicyMap* map,
PolicyPerProfileFilter per_profile);
+// Parses the JSON policy in |data| into |policy|, and returns true if the
+// parse was successful. The |scope| and |source| are set as scope and source of
+// the policy in the result. In case of failure, the |error| is populated with
+// error message and false is returned.
+POLICY_EXPORT bool ParseComponentPolicy(base::Value json,
+ PolicyScope scope,
+ PolicySource source,
+ PolicyMap* policy,
+ std::string* error);
+
} // namespace policy
#endif // COMPONENTS_POLICY_CORE_COMMON_POLICY_PROTO_DECODERS_H_
diff --git a/chromium/components/policy/core/common/policy_statistics_collector.cc b/chromium/components/policy/core/common/policy_statistics_collector.cc
index c322e0ddce0..d3ade046123 100644
--- a/chromium/components/policy/core/common/policy_statistics_collector.cc
+++ b/chromium/components/policy/core/common/policy_statistics_collector.cc
@@ -13,6 +13,9 @@
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/task/task_runner.h"
+#include "base/time/time.h"
+#include "components/policy/core/common/cloud/enterprise_metrics.h"
+#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/core/common/policy_types.h"
@@ -22,8 +25,71 @@
namespace policy {
-const int PolicyStatisticsCollector::kStatisticsUpdateRate =
- 24 * 60 * 60 * 1000; // 24 hours.
+namespace {
+
+constexpr char kPoliciesSourceMetricsName[] = "Enterprise.Policies.Sources";
+
+constexpr const char* kCBCMEnrollmentPolicies[] = {
+ "CloudManagementEnrollmentToken", "CloudManagementEnrollmentMandatory"};
+
+enum SimplePolicySource {
+ kNone = 0,
+ kCloud = 1 << 0,
+ kPlatform = 1 << 1,
+ kMerge = kCloud | kPlatform,
+ kEnrollment = 1 << 2,
+};
+
+SimplePolicySource SimplifyPolicySource(PolicySource source,
+ const std::string& policy_name) {
+ switch (source) {
+ case POLICY_SOURCE_CLOUD:
+ case POLICY_SOURCE_CLOUD_FROM_ASH:
+ return kCloud;
+ case POLICY_SOURCE_PLATFORM:
+ case POLICY_SOURCE_ACTIVE_DIRECTORY:
+ // Adjust for enrollment policies which can never be set from cloud.
+ // Count them as cloud policy so that a device is considered as cloud
+ // managed even if there is enrollment token only.
+ for (const char* enrollment_policy : kCBCMEnrollmentPolicies) {
+ if (policy_name == enrollment_policy)
+ return kEnrollment;
+ }
+ return kPlatform;
+ case POLICY_SOURCE_MERGED:
+ return kMerge;
+ default:
+ // Other sources are only used for speicial cases and will not be counted.
+ return kNone;
+ }
+}
+
+void RecordPoliciesSources(SimplePolicySource source) {
+ if ((source & kMerge) == kMerge) {
+ base::UmaHistogramEnumeration(kPoliciesSourceMetricsName,
+ PoliciesSources::kHybrid);
+ } else if (source & kPlatform) {
+ base::UmaHistogramEnumeration(kPoliciesSourceMetricsName,
+ PoliciesSources::kPlatformOnly);
+ } else if (source & kCloud) {
+ if (source & kEnrollment) {
+ base::UmaHistogramEnumeration(
+ kPoliciesSourceMetricsName,
+ PoliciesSources::kCloudOnlyExceptEnrollment);
+ } else {
+ base::UmaHistogramEnumeration(kPoliciesSourceMetricsName,
+ PoliciesSources::kCloudOnly);
+ }
+ } else if (source & kEnrollment) {
+ base::UmaHistogramEnumeration(kPoliciesSourceMetricsName,
+ PoliciesSources::kEnrollmentOnly);
+ }
+}
+
+} // namespace
+
+const base::TimeDelta PolicyStatisticsCollector::kStatisticsUpdateRate =
+ base::Days(1);
PolicyStatisticsCollector::PolicyStatisticsCollector(
const GetChromePolicyDetailsCallback& get_details,
@@ -35,22 +101,19 @@ PolicyStatisticsCollector::PolicyStatisticsCollector(
chrome_schema_(chrome_schema),
policy_service_(policy_service),
prefs_(prefs),
- task_runner_(task_runner) {
-}
+ task_runner_(task_runner) {}
-PolicyStatisticsCollector::~PolicyStatisticsCollector() {
-}
+PolicyStatisticsCollector::~PolicyStatisticsCollector() = default;
void PolicyStatisticsCollector::Initialize() {
- using base::Time;
-
- base::TimeDelta update_rate = base::Milliseconds(kStatisticsUpdateRate);
- Time last_update = prefs_->GetTime(policy_prefs::kLastPolicyStatisticsUpdate);
- base::TimeDelta delay = std::max(Time::Now() - last_update, base::Days(0));
- if (delay >= update_rate)
+ base::Time last_update =
+ prefs_->GetTime(policy_prefs::kLastPolicyStatisticsUpdate);
+ base::TimeDelta delay =
+ std::max(base::Time::Now() - last_update, base::TimeDelta());
+ if (delay >= kStatisticsUpdateRate)
CollectStatistics();
else
- ScheduleUpdate(update_rate - delay);
+ ScheduleUpdate(kStatisticsUpdateRate - delay);
}
// static
@@ -80,22 +143,25 @@ void PolicyStatisticsCollector::CollectStatistics() {
const PolicyMap& policies = policy_service_->GetPolicies(
PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+ int source = kNone;
// Collect statistics.
for (Schema::Iterator it(chrome_schema_.GetPropertiesIterator());
!it.IsAtEnd(); it.Advance()) {
- if (policies.Get(it.key())) {
- const PolicyDetails* details = get_details_.Run(it.key());
- if (details) {
- RecordPolicyUse(details->id, kDefault);
- if (policies.Get(it.key())->level == POLICY_LEVEL_MANDATORY) {
- RecordPolicyUse(details->id, kMandatory);
- } else {
- RecordPolicyUse(details->id, kRecommended);
- }
+ const PolicyMap::Entry* policy_entry = policies.Get(it.key());
+ if (!policy_entry)
+ continue;
+ const PolicyDetails* details = get_details_.Run(it.key());
+ if (details) {
+ RecordPolicyUse(details->id, kDefault);
+ if (policies.Get(it.key())->level == POLICY_LEVEL_MANDATORY) {
+ RecordPolicyUse(details->id, kMandatory);
} else {
- NOTREACHED();
+ RecordPolicyUse(details->id, kRecommended);
}
+ } else {
+ NOTREACHED();
}
+ source |= SimplifyPolicySource(policy_entry->source, it.key());
}
for (size_t i = 0; i < kPolicyAtomicGroupMappingsLength; ++i) {
@@ -114,9 +180,11 @@ void PolicyStatisticsCollector::CollectStatistics() {
}
}
+ RecordPoliciesSources(static_cast<SimplePolicySource>(source));
+
// Take care of next update.
prefs_->SetTime(policy_prefs::kLastPolicyStatisticsUpdate, base::Time::Now());
- ScheduleUpdate(base::Milliseconds(kStatisticsUpdateRate));
+ ScheduleUpdate(kStatisticsUpdateRate);
}
void PolicyStatisticsCollector::ScheduleUpdate(base::TimeDelta delay) {
@@ -125,4 +193,4 @@ void PolicyStatisticsCollector::ScheduleUpdate(base::TimeDelta delay) {
task_runner_->PostDelayedTask(FROM_HERE, update_callback_.callback(), delay);
}
-} // namespace policy \ No newline at end of file
+} // namespace policy
diff --git a/chromium/components/policy/core/common/policy_statistics_collector.h b/chromium/components/policy/core/common/policy_statistics_collector.h
index 5f5552b1146..0d13321f6b0 100644
--- a/chromium/components/policy/core/common/policy_statistics_collector.h
+++ b/chromium/components/policy/core/common/policy_statistics_collector.h
@@ -32,11 +32,20 @@ enum Condition {
kIgnoredByAtomicGroup,
};
+enum class PoliciesSources {
+ kCloudOnly = 0,
+ kCloudOnlyExceptEnrollment = 1,
+ kPlatformOnly = 2,
+ kHybrid = 3,
+ kEnrollmentOnly = 4,
+ kMaxValue = kEnrollmentOnly,
+};
+
// Manages regular updates of policy usage UMA histograms.
class POLICY_EXPORT PolicyStatisticsCollector {
public:
// Policy usage statistics update rate, in milliseconds.
- static const int kStatisticsUpdateRate;
+ static const base::TimeDelta kStatisticsUpdateRate;
// Neither |policy_service| nor |prefs| can be NULL and must stay valid
// throughout the lifetime of PolicyStatisticsCollector.
@@ -55,11 +64,8 @@ class POLICY_EXPORT PolicyStatisticsCollector {
static void RegisterPrefs(PrefRegistrySimple* registry);
- protected:
- // protected virtual for mocking.
- virtual void RecordPolicyUse(int id, Condition condition);
-
private:
+ void RecordPolicyUse(int id, Condition condition);
void CollectStatistics();
void ScheduleUpdate(base::TimeDelta delay);
@@ -75,4 +81,4 @@ class POLICY_EXPORT PolicyStatisticsCollector {
} // namespace policy
-#endif // COMPONENTS_POLICY_CORE_COMMON_POLICY_STATISTICS_COLLECTOR_H_ \ No newline at end of file
+#endif // COMPONENTS_POLICY_CORE_COMMON_POLICY_STATISTICS_COLLECTOR_H_
diff --git a/chromium/components/policy/core/common/policy_statistics_collector_unittest.cc b/chromium/components/policy/core/common/policy_statistics_collector_unittest.cc
index 422ddc68844..31a38164431 100644
--- a/chromium/components/policy/core/common/policy_statistics_collector_unittest.cc
+++ b/chromium/components/policy/core/common/policy_statistics_collector_unittest.cc
@@ -37,25 +37,34 @@ const char kTestPolicy1[] = "Test Policy 1";
const char kTestPolicy2[] = "Test Policy 2";
const char* kTestPolicy3 = key::kExtensionInstallBlocklist;
+const char kEnrollmentTokenPolicy[] = "CloudManagementEnrollmentToken";
+const char kEnrollmentOptionPolicy[] = "CloudManagementEnrollmentMandatory";
+
const int kTestPolicy1Id = 42;
const int kTestPolicy2Id = 123;
const int kTestPolicy3Id = 32;
-
-const char kTestChromeSchema[] =
- "{"
- " \"type\": \"object\","
- " \"properties\": {"
- " \"Test Policy 1\": { \"type\": \"string\" },"
- " \"Test Policy 2\": { \"type\": \"string\" },"
- " \"ExtensionInstallBlocklist\": { \"type\": \"string\" },"
- " }"
- "}";
+const int kEnrollmentTokenPolicyId = 510;
+const int kEnrollmentOptionPolicyId = 505;
+
+const char kTestChromeSchema[] = R"(
+ {
+ "type": "object",
+ "properties": {
+ "Test Policy 1": { "type": "boolean" },
+ "Test Policy 2": { "type": "boolean" },
+ "ExtensionInstallBlocklist": { "type": "boolean" },
+ "CloudManagementEnrollmentToken": { "type": "boolean" },
+ "CloudManagementEnrollmentMandatory": { "type": "boolean" },
+ }
+ })";
const PolicyDetails kTestPolicyDetails[] = {
// is_deprecated is_future is_device_policy id max_external_data_size
{false, false, false, kTestPolicy1Id, 0},
{false, false, false, kTestPolicy2Id, 0},
{false, false, false, kTestPolicy3Id, 0},
+ {false, false, false, kEnrollmentTokenPolicyId, 0},
+ {false, false, false, kEnrollmentOptionPolicyId, 0},
};
} // namespace
@@ -63,9 +72,7 @@ const PolicyDetails kTestPolicyDetails[] = {
class PolicyStatisticsCollectorTest : public testing::Test {
protected:
PolicyStatisticsCollectorTest()
- : update_delay_(base::Milliseconds(
- PolicyStatisticsCollector::kStatisticsUpdateRate)),
- task_runner_(new base::TestSimpleTaskRunner()) {}
+ : task_runner_(new base::TestSimpleTaskRunner()) {}
void SetUp() override {
std::string error;
@@ -75,18 +82,17 @@ class PolicyStatisticsCollectorTest : public testing::Test {
policy_details_.SetDetails(kTestPolicy1, &kTestPolicyDetails[0]);
policy_details_.SetDetails(kTestPolicy2, &kTestPolicyDetails[1]);
policy_details_.SetDetails(kTestPolicy3, &kTestPolicyDetails[2]);
+ policy_details_.SetDetails(kEnrollmentTokenPolicy, &kTestPolicyDetails[3]);
+ policy_details_.SetDetails(kEnrollmentOptionPolicy, &kTestPolicyDetails[4]);
prefs_.registry()->RegisterInt64Pref(
policy_prefs::kLastPolicyStatisticsUpdate, 0);
// Set up default function behaviour.
- EXPECT_CALL(policy_service_,
- GetPolicies(PolicyNamespace(POLICY_DOMAIN_CHROME,
- std::string())))
+ EXPECT_CALL(policy_service_, GetPolicies(PolicyNamespace(
+ POLICY_DOMAIN_CHROME, std::string())))
.WillRepeatedly(ReturnRef(policy_map_));
- // Arbitrary negative value (so it'll be different from |update_delay_|).
- last_delay_ = base::Days(-1);
policy_map_.Clear();
policy_statistics_collector_ = std::make_unique<PolicyStatisticsCollector>(
policy_details_.GetCallback(), chrome_schema_, &policy_service_,
@@ -96,7 +102,12 @@ class PolicyStatisticsCollectorTest : public testing::Test {
void SetPolicy(const std::string& name,
PolicyLevel level = POLICY_LEVEL_MANDATORY) {
policy_map_.Set(name, level, POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
- base::Value(true), nullptr);
+ base::Value(true), /*external_data_fetcher=*/nullptr);
+ }
+
+ void SetPolicy(const std::string& name, PolicySource source) {
+ policy_map_.Set(name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, source,
+ base::Value(true), /*external_data_fetcher=*/nullptr);
}
void SetPolicyIgnoredByAtomicGroup(const std::string& name) {
@@ -113,10 +124,6 @@ class PolicyStatisticsCollectorTest : public testing::Test {
return task_runner_->NextPendingTaskDelay();
}
- const base::TimeDelta update_delay_;
-
- base::TimeDelta last_delay_;
-
PolicyDetailsMap policy_details_;
Schema chrome_schema_;
TestingPrefServiceSimple prefs_;
@@ -129,12 +136,18 @@ class PolicyStatisticsCollectorTest : public testing::Test {
base::HistogramTester histogram_tester_;
};
+TEST_F(PolicyStatisticsCollectorTest, NoPolicy) {
+ policy_statistics_collector_->Initialize();
+
+ histogram_tester_.ExpectTotalCount("Enterprise.Policies.Mandatory", 0);
+ histogram_tester_.ExpectTotalCount("Enterprise.Policies.Recommended", 0);
+ histogram_tester_.ExpectTotalCount("Enterprise.Policies", 0);
+ histogram_tester_.ExpectTotalCount("Enterprise.Policies.Sources", 0);
+}
+
TEST_F(PolicyStatisticsCollectorTest, CollectPending) {
SetPolicy(kTestPolicy1, POLICY_LEVEL_MANDATORY);
- prefs_.SetTime(policy_prefs::kLastPolicyStatisticsUpdate,
- base::Time::Now() - update_delay_);
-
policy_statistics_collector_->Initialize();
histogram_tester_.ExpectBucketCount("Enterprise.Policies", kTestPolicy1Id, 1);
@@ -159,8 +172,9 @@ TEST_F(PolicyStatisticsCollectorTest, CollectPendingVeryOld) {
TEST_F(PolicyStatisticsCollectorTest, CollectLater) {
SetPolicy(kTestPolicy1, POLICY_LEVEL_MANDATORY);
- prefs_.SetTime(policy_prefs::kLastPolicyStatisticsUpdate,
- base::Time::Now() - update_delay_ / 2);
+ prefs_.SetTime(
+ policy_prefs::kLastPolicyStatisticsUpdate,
+ base::Time::Now() - PolicyStatisticsCollector::kStatisticsUpdateRate / 2);
policy_statistics_collector_->Initialize();
@@ -173,9 +187,6 @@ TEST_F(PolicyStatisticsCollectorTest, MultiplePolicies) {
SetPolicy(kTestPolicy1, POLICY_LEVEL_MANDATORY);
SetPolicy(kTestPolicy2, POLICY_LEVEL_RECOMMENDED);
- prefs_.SetTime(policy_prefs::kLastPolicyStatisticsUpdate,
- base::Time::Now() - update_delay_);
-
policy_statistics_collector_->Initialize();
histogram_tester_.ExpectBucketCount("Enterprise.Policies", kTestPolicy1Id, 1);
@@ -196,9 +207,6 @@ TEST_F(PolicyStatisticsCollectorTest, PolicyIgnoredByAtomicGroup) {
DCHECK(extensions);
- prefs_.SetTime(policy_prefs::kLastPolicyStatisticsUpdate,
- base::Time::Now() - update_delay_);
-
policy_statistics_collector_->Initialize();
histogram_tester_.ExpectUniqueSample(
@@ -208,9 +216,6 @@ TEST_F(PolicyStatisticsCollectorTest, PolicyIgnoredByAtomicGroup) {
TEST_F(PolicyStatisticsCollectorTest, MandatoryPolicy) {
SetPolicy(kTestPolicy1, POLICY_LEVEL_MANDATORY);
- prefs_.SetTime(policy_prefs::kLastPolicyStatisticsUpdate,
- base::Time::Now() - update_delay_);
-
policy_statistics_collector_->Initialize();
histogram_tester_.ExpectUniqueSample("Enterprise.Policies.Mandatory",
@@ -221,9 +226,6 @@ TEST_F(PolicyStatisticsCollectorTest, MandatoryPolicy) {
TEST_F(PolicyStatisticsCollectorTest, RecommendedPolicy) {
SetPolicy(kTestPolicy2, POLICY_LEVEL_RECOMMENDED);
- prefs_.SetTime(policy_prefs::kLastPolicyStatisticsUpdate,
- base::Time::Now() - update_delay_);
-
policy_statistics_collector_->Initialize();
histogram_tester_.ExpectUniqueSample("Enterprise.Policies.Recommended",
@@ -231,4 +233,57 @@ TEST_F(PolicyStatisticsCollectorTest, RecommendedPolicy) {
histogram_tester_.ExpectTotalCount("Enterprise.Policies.Mandatory", 0);
}
+TEST_F(PolicyStatisticsCollectorTest, CloudOnly) {
+ SetPolicy(kTestPolicy1, POLICY_SOURCE_CLOUD);
+ SetPolicy(kTestPolicy2, POLICY_SOURCE_CLOUD_FROM_ASH);
+
+ policy_statistics_collector_->Initialize();
+
+ histogram_tester_.ExpectUniqueSample("Enterprise.Policies.Sources",
+ PoliciesSources::kCloudOnly, 1);
+}
+
+TEST_F(PolicyStatisticsCollectorTest, PlatformOnly) {
+ SetPolicy(kTestPolicy1, POLICY_SOURCE_PLATFORM);
+ SetPolicy(kTestPolicy2, POLICY_SOURCE_ACTIVE_DIRECTORY);
+ SetPolicy(kEnrollmentTokenPolicy, POLICY_SOURCE_PLATFORM);
+
+ policy_statistics_collector_->Initialize();
+
+ histogram_tester_.ExpectUniqueSample("Enterprise.Policies.Sources",
+ PoliciesSources::kPlatformOnly, 1);
+}
+
+TEST_F(PolicyStatisticsCollectorTest, Hybrid) {
+ SetPolicy(kTestPolicy1, POLICY_SOURCE_PLATFORM);
+ SetPolicy(kTestPolicy2, POLICY_SOURCE_CLOUD);
+
+ policy_statistics_collector_->Initialize();
+
+ histogram_tester_.ExpectUniqueSample("Enterprise.Policies.Sources",
+ PoliciesSources::kHybrid, 1);
+}
+
+TEST_F(PolicyStatisticsCollectorTest, CloudExcepptEnrollment) {
+ SetPolicy(kTestPolicy1, POLICY_SOURCE_CLOUD);
+ SetPolicy(kEnrollmentTokenPolicy, POLICY_SOURCE_PLATFORM);
+ SetPolicy(kEnrollmentOptionPolicy, POLICY_SOURCE_PLATFORM);
+
+ policy_statistics_collector_->Initialize();
+
+ histogram_tester_.ExpectUniqueSample(
+ "Enterprise.Policies.Sources",
+ PoliciesSources::kCloudOnlyExceptEnrollment, 1);
+}
+
+TEST_F(PolicyStatisticsCollectorTest, EnrollmentOnly) {
+ SetPolicy(kEnrollmentTokenPolicy, POLICY_SOURCE_PLATFORM);
+ SetPolicy(kEnrollmentOptionPolicy, POLICY_SOURCE_PLATFORM);
+
+ policy_statistics_collector_->Initialize();
+
+ histogram_tester_.ExpectUniqueSample("Enterprise.Policies.Sources",
+ PoliciesSources::kEnrollmentOnly, 1);
+}
+
} // namespace policy
diff --git a/chromium/components/policy/core/common/preferences_mac.cc b/chromium/components/policy/core/common/preferences_mac.cc
deleted file mode 100644
index e65b1623460..00000000000
--- a/chromium/components/policy/core/common/preferences_mac.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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 "components/policy/core/common/preferences_mac.h"
-
-Boolean MacPreferences::AppSynchronize(CFStringRef applicationID) {
- return CFPreferencesAppSynchronize(applicationID);
-}
-
-CFPropertyListRef MacPreferences::CopyAppValue(CFStringRef key,
- CFStringRef applicationID) {
- return CFPreferencesCopyAppValue(key, applicationID);
-}
-
-Boolean MacPreferences::AppValueIsForced(CFStringRef key,
- CFStringRef applicationID) {
- return CFPreferencesAppValueIsForced(key, applicationID);
-}
diff --git a/chromium/components/policy/core/common/preferences_mac.h b/chromium/components/policy/core/common/preferences_mac.h
index baa7d7e156c..e897f9e3a7b 100644
--- a/chromium/components/policy/core/common/preferences_mac.h
+++ b/chromium/components/policy/core/common/preferences_mac.h
@@ -7,27 +7,50 @@
#include <CoreFoundation/CoreFoundation.h>
+#include <memory>
+
#include "components/policy/policy_export.h"
-// Wraps a small part of the CFPreferences API surface in a very thin layer, to
-// allow it to be mocked out for testing.
+// Wraps a small part of the `CFPreferences` and `CFPrefsManagedSource` API
+// surface.
-// See CFPreferences documentation for function documentation, as these call
-// through directly to their CFPreferences equivalents (Foo ->
-// CFPreferencesFoo).
+// See CFPreferences documentation for following functions' documentation:
+// AppSynchronize()
+// CopyAppValue()
+// AppValueIsForced()
class POLICY_EXPORT MacPreferences {
public:
- MacPreferences() {}
+ // Wraps Apple's private `CFPrefsManagedSource` API to determine the scope of
+ // a policy.
+ class PolicyScope {
+ public:
+ virtual ~PolicyScope() = default;
+ virtual void Init(CFStringRef application_id) = 0;
+ virtual Boolean IsManagedPolicyAvailable(CFStringRef key) = 0;
+ };
+
+ MacPreferences();
MacPreferences(const MacPreferences&) = delete;
MacPreferences& operator=(const MacPreferences&) = delete;
- virtual ~MacPreferences() {}
+ virtual ~MacPreferences();
- virtual Boolean AppSynchronize(CFStringRef applicationID);
+ // Calls CFPreferencesAppSynchronize and initialize `policy_scope_`.
+ virtual Boolean AppSynchronize(CFStringRef application_id);
+ // Calls CFPreferencesCopyAppValue.
virtual CFPropertyListRef CopyAppValue(CFStringRef key,
- CFStringRef applicationID);
+ CFStringRef application_id);
+
+ // Calls CFPreferencesAppValueIsForced.
+ virtual Boolean AppValueIsForced(CFStringRef key, CFStringRef application_id);
+
+ // Calls CFPrefsManagedSource.copyValueForKey to determine if the policy is
+ // set at the machine scope for `application_id` that is set by
+ // `AppSynchronize()` function above.
+ virtual Boolean IsManagedPolicyAvailableForMachineScope(CFStringRef key);
- virtual Boolean AppValueIsForced(CFStringRef key, CFStringRef applicationID);
+ private:
+ std::unique_ptr<PolicyScope> policy_scope_;
};
#endif // COMPONENTS_POLICY_CORE_COMMON_PREFERENCES_MAC_H_
diff --git a/chromium/components/policy/core/common/preferences_mac.mm b/chromium/components/policy/core/common/preferences_mac.mm
new file mode 100644
index 00000000000..bcf4db0dbcc
--- /dev/null
+++ b/chromium/components/policy/core/common/preferences_mac.mm
@@ -0,0 +1,119 @@
+// 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.
+
+#import <Foundation/Foundation.h>
+#import <objc/runtime.h>
+
+#include "base/feature_list.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_nsobject.h"
+#include "components/policy/core/common/features.h"
+#include "components/policy/core/common/preferences_mac.h"
+
+// `CFPrefsManagedSource` and `_CFXPreferences` are used to determine the scope
+// of a policy. A policy can be read with `copyValueForKey()` below with
+// `kCFPreferencesAnyUser` will be treated as machine scope policy. Otherwise,
+// it will be user scope policy. The implementation of these two interfaces are
+// only available during runtime and will be obtained with `objc_getClass()`.
+@interface _CFXPreferences : NSObject
+@end
+
+@interface CFPrefsManagedSource : NSObject
+- (instancetype)initWithDomain:(NSString*)domain
+ user:(NSString*)user
+ byHost:(BOOL)by_host
+ containerPath:(NSString*)path
+ containingPreferences:(_CFXPreferences*)contain_prefs;
+- (id)copyValueForKey:(NSString*)key;
+@end
+
+namespace {
+
+base::scoped_nsobject<_CFXPreferences> CreateCFXPrefs() {
+ // _CFXPreferences is only available during runtime.
+ return base::scoped_nsobject<_CFXPreferences>(
+ [[objc_getClass("_CFXPreferences") alloc] init]);
+}
+
+base::scoped_nsobject<CFPrefsManagedSource>
+CreateCFPrefsManagedSourceForMachine(CFStringRef application_id, id cfxPrefs) {
+ if (!cfxPrefs)
+ return base::scoped_nsobject<CFPrefsManagedSource>();
+
+ // CFPrefsManagedSource is only available during runtime.
+ base::scoped_nsobject<CFPrefsManagedSource> source(
+ [objc_getClass("CFPrefsManagedSource") alloc]);
+
+ if (![source respondsToSelector:@selector
+ (initWithDomain:
+ user:byHost:containerPath:containingPreferences:)] ||
+ ![source respondsToSelector:@selector(copyValueForKey:)]) {
+ return base::scoped_nsobject<CFPrefsManagedSource>();
+ }
+
+ [source initWithDomain:base::mac::CFToNSCast(application_id)
+ user:base::mac::CFToNSCast(kCFPreferencesAnyUser)
+ byHost:YES
+ containerPath:nil
+ containingPreferences:cfxPrefs];
+ return source;
+}
+
+class MachinePolicyScope : public MacPreferences::PolicyScope {
+ public:
+ MachinePolicyScope() = default;
+ ~MachinePolicyScope() override = default;
+
+ void Init(CFStringRef application_id) override {
+ if (!base::FeatureList::IsEnabled(
+ policy::features::kPolicyScopeDetectionMac)) {
+ return;
+ }
+ if (!cfx_prefs_)
+ cfx_prefs_.reset(CreateCFXPrefs());
+ machine_scope_.reset(
+ CreateCFPrefsManagedSourceForMachine(application_id, cfx_prefs_));
+ }
+
+ Boolean IsManagedPolicyAvailable(CFStringRef key) override {
+ if (!base::FeatureList::IsEnabled(
+ policy::features::kPolicyScopeDetectionMac)) {
+ return YES;
+ }
+ if (!machine_scope_)
+ return YES;
+ return base::scoped_nsobject<id>([machine_scope_
+ copyValueForKey:base::mac::CFToNSCast(key)]) != nil;
+ }
+
+ private:
+ base::scoped_nsobject<_CFXPreferences> cfx_prefs_;
+ base::scoped_nsobject<CFPrefsManagedSource> machine_scope_;
+};
+
+} // namespace
+
+MacPreferences::MacPreferences()
+ : policy_scope_(std::make_unique<MachinePolicyScope>()) {}
+MacPreferences::~MacPreferences() = default;
+
+Boolean MacPreferences::AppSynchronize(CFStringRef application_id) {
+ policy_scope_->Init(application_id);
+ return CFPreferencesAppSynchronize(application_id);
+}
+
+CFPropertyListRef MacPreferences::CopyAppValue(CFStringRef key,
+ CFStringRef application_id) {
+ return CFPreferencesCopyAppValue(key, application_id);
+}
+
+Boolean MacPreferences::AppValueIsForced(CFStringRef key,
+ CFStringRef application_id) {
+ return CFPreferencesAppValueIsForced(key, application_id);
+}
+
+Boolean MacPreferences::IsManagedPolicyAvailableForMachineScope(
+ CFStringRef key) {
+ return policy_scope_->IsManagedPolicyAvailable(key);
+}
diff --git a/chromium/components/policy/core/common/preferences_mock_mac.cc b/chromium/components/policy/core/common/preferences_mock_mac.cc
index bc51c9a3523..76ec203e741 100644
--- a/chromium/components/policy/core/common/preferences_mock_mac.cc
+++ b/chromium/components/policy/core/common/preferences_mock_mac.cc
@@ -12,17 +12,19 @@ MockPreferences::MockPreferences() {
forced_.reset(CFSetCreateMutable(kCFAllocatorDefault,
0,
&kCFTypeSetCallBacks));
+ machine_.reset(
+ CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks));
}
MockPreferences::~MockPreferences() {
}
-Boolean MockPreferences::AppSynchronize(CFStringRef applicationID) {
+Boolean MockPreferences::AppSynchronize(CFStringRef application_id) {
return true;
}
CFPropertyListRef MockPreferences::CopyAppValue(CFStringRef key,
- CFStringRef applicationID) {
+ CFStringRef application_id) {
CFPropertyListRef value;
Boolean found = CFDictionaryGetValueIfPresent(values_,
key,
@@ -34,14 +36,22 @@ CFPropertyListRef MockPreferences::CopyAppValue(CFStringRef key,
}
Boolean MockPreferences::AppValueIsForced(CFStringRef key,
- CFStringRef applicationID) {
+ CFStringRef application_id) {
return CFSetContainsValue(forced_, key);
}
+Boolean MockPreferences::IsManagedPolicyAvailableForMachineScope(
+ CFStringRef key) {
+ return CFSetContainsValue(machine_, key);
+}
+
void MockPreferences::AddTestItem(CFStringRef key,
CFPropertyListRef value,
- bool is_forced) {
+ bool is_forced,
+ bool is_machine) {
CFDictionarySetValue(values_, key, value);
if (is_forced)
CFSetAddValue(forced_, key);
+ if (is_machine)
+ CFSetAddValue(machine_, key);
}
diff --git a/chromium/components/policy/core/common/preferences_mock_mac.h b/chromium/components/policy/core/common/preferences_mock_mac.h
index 8f3df0c8c77..4ebd9fcd78f 100644
--- a/chromium/components/policy/core/common/preferences_mock_mac.h
+++ b/chromium/components/policy/core/common/preferences_mock_mac.h
@@ -15,19 +15,24 @@ class POLICY_EXPORT MockPreferences : public MacPreferences {
MockPreferences();
~MockPreferences() override;
- Boolean AppSynchronize(CFStringRef applicationID) override;
-
+ // MacPreferences
+ Boolean AppSynchronize(CFStringRef application_id) override;
CFPropertyListRef CopyAppValue(CFStringRef key,
- CFStringRef applicationID) override;
-
- Boolean AppValueIsForced(CFStringRef key, CFStringRef applicationID) override;
+ CFStringRef application_id) override;
+ Boolean AppValueIsForced(CFStringRef key,
+ CFStringRef application_id) override;
+ Boolean IsManagedPolicyAvailableForMachineScope(CFStringRef key) override;
// Adds a preference item with the given info to the test set.
- void AddTestItem(CFStringRef key, CFPropertyListRef value, bool is_forced);
+ void AddTestItem(CFStringRef key,
+ CFPropertyListRef value,
+ bool is_forced,
+ bool is_machine);
private:
base::ScopedCFTypeRef<CFMutableDictionaryRef> values_;
base::ScopedCFTypeRef<CFMutableSetRef> forced_;
+ base::ScopedCFTypeRef<CFMutableSetRef> machine_;
};
#endif // COMPONENTS_POLICY_CORE_COMMON_PREFERENCES_MOCK_MAC_H_
diff --git a/chromium/components/policy/core/common/registry_dict.cc b/chromium/components/policy/core/common/registry_dict.cc
index ff487c9a2f3..96cc77a01ae 100644
--- a/chromium/components/policy/core/common/registry_dict.cc
+++ b/chromium/components/policy/core/common/registry_dict.cc
@@ -60,7 +60,7 @@ absl::optional<base::Value> ConvertRegistryValue(const base::Value& value,
absl::optional<base::Value> converted =
ConvertRegistryValue(entry, schema.GetItems());
if (converted.has_value())
- result.Append(std::move(converted.value()));
+ result.GetList().Append(std::move(converted.value()));
}
return result;
}
@@ -113,7 +113,7 @@ absl::optional<base::Value> ConvertRegistryValue(const base::Value& value,
absl::optional<base::Value> converted =
ConvertRegistryValue(it.second, schema.GetItems());
if (converted.has_value())
- result.Append(std::move(converted.value()));
+ result.GetList().Append(std::move(converted.value()));
}
return result;
}
@@ -338,7 +338,8 @@ std::unique_ptr<base::Value> RegistryDict::ConvertToJSON(
std::unique_ptr<base::Value> converted =
entry->second->ConvertToJSON(item_schema);
if (converted)
- result->Append(std::move(converted));
+ result->GetList().Append(
+ base::Value::FromUniquePtrValue(std::move(converted)));
}
for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
entry != values_.end(); ++entry) {
@@ -347,7 +348,7 @@ std::unique_ptr<base::Value> RegistryDict::ConvertToJSON(
absl::optional<base::Value> converted =
ConvertRegistryValue(entry->second, item_schema);
if (converted.has_value())
- result->Append(std::move(converted.value()));
+ result->GetList().Append(std::move(converted.value()));
}
return std::move(result);
}
diff --git a/chromium/components/policy/core/common/registry_dict_unittest.cc b/chromium/components/policy/core/common/registry_dict_unittest.cc
index 78db1774893..4b2918aec3d 100644
--- a/chromium/components/policy/core/common/registry_dict_unittest.cc
+++ b/chromium/components/policy/core/common/registry_dict_unittest.cc
@@ -234,7 +234,7 @@ TEST(RegistryDictTest, ConvertToJSON) {
expected_subdict->SetKey("two", string_value.Clone());
expected.Set("three", std::move(expected_subdict));
auto expected_list = std::make_unique<base::ListValue>();
- expected_list->Append(std::make_unique<base::Value>(string_value.Clone()));
+ expected_list->GetList().Append(string_value.Clone());
expected.Set("dict-to-list", std::move(expected_list));
expected.SetBoolKey("int-to-bool", true);
expected.SetDoubleKey("int-to-double", 42.0);
@@ -242,7 +242,7 @@ TEST(RegistryDictTest, ConvertToJSON) {
expected.SetDoubleKey("string-to-double", 0.0);
expected.SetIntKey("string-to-int", static_cast<int>(0));
expected_list = std::make_unique<base::ListValue>();
- expected_list->Append(std::make_unique<base::Value>("value"));
+ expected_list->GetList().Append("value");
expected_subdict = std::make_unique<base::DictionaryValue>();
expected_subdict->Set("key", std::move(expected_list));
expected.Set("string-to-dict", std::move(expected_subdict));
@@ -278,9 +278,9 @@ TEST(RegistryDictTest, NonSequentialConvertToJSON) {
base::DictionaryValue expected;
std::unique_ptr<base::ListValue> expected_list(new base::ListValue());
- expected_list->Append(base::Value("1").Clone());
- expected_list->Append(base::Value("2").Clone());
- expected_list->Append(base::Value("4").Clone());
+ expected_list->GetList().Append(base::Value("1").Clone());
+ expected_list->GetList().Append(base::Value("2").Clone());
+ expected_list->GetList().Append(base::Value("4").Clone());
expected.Set("dict-to-list", std::move(expected_list));
EXPECT_EQ(expected, *actual);
@@ -345,7 +345,7 @@ TEST(RegistryDictTest, PatternPropertySchema) {
std::unique_ptr<base::DictionaryValue> expected_extension_settings(
new base::DictionaryValue());
std::unique_ptr<base::ListValue> list_value(new base::ListValue());
- list_value->Append("*://*.google.com");
+ list_value->GetList().Append("*://*.google.com");
std::unique_ptr<base::DictionaryValue> restrictions_properties(
new base::DictionaryValue());
restrictions_properties->Set(
diff --git a/chromium/components/policy/core/common/schema.cc b/chromium/components/policy/core/common/schema.cc
index 32f578aae54..ff75b196a5f 100644
--- a/chromium/components/policy/core/common/schema.cc
+++ b/chromium/components/policy/core/common/schema.cc
@@ -231,6 +231,10 @@ bool StrategyAllowInvalidListEntry(SchemaOnErrorStrategy strategy) {
return strategy == SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY;
}
+bool StrategyAllowUnknownWithoutWarning(SchemaOnErrorStrategy strategy) {
+ return strategy == SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING;
+}
+
void SchemaErrorFound(std::string* out_error_path,
std::string* out_error,
const std::string& msg) {
@@ -1219,8 +1223,10 @@ bool Schema::Validate(const base::Value& value,
SchemaList schema_list = GetMatchingProperties(dict_item.first);
if (schema_list.empty()) {
// Unknown property was detected.
- SchemaErrorFound(out_error_path, out_error,
- "Unknown property: " + dict_item.first);
+ if (!StrategyAllowUnknownWithoutWarning(strategy)) {
+ SchemaErrorFound(out_error_path, out_error,
+ "Unknown property: " + dict_item.first);
+ }
if (!StrategyAllowUnknown(strategy))
return false;
} else {
@@ -1311,11 +1317,15 @@ bool Schema::Normalize(base::Value* value,
SchemaList schema_list = GetMatchingProperties(dict_item.first);
if (schema_list.empty()) {
// Unknown property was detected.
- SchemaErrorFound(out_error_path, out_error,
- "Unknown property: " + dict_item.first);
+ if (!StrategyAllowUnknownWithoutWarning(strategy)) {
+ SchemaErrorFound(out_error_path, out_error,
+ "Unknown property: " + dict_item.first);
+ }
if (!StrategyAllowUnknown(strategy))
return false;
- drop_list.push_back(dict_item.first);
+ if (!StrategyAllowUnknownWithoutWarning(strategy)) {
+ drop_list.push_back(dict_item.first);
+ }
} else {
for (const auto& subschema : schema_list) {
std::string new_error;
@@ -1435,7 +1445,8 @@ absl::optional<base::Value> Schema::ParseToDictAndValidate(
std::string* error) {
base::JSONReader::ValueWithError value_with_error =
base::JSONReader::ReadAndReturnValueWithError(
- schema, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
+ schema, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS |
+ base::JSONParserOptions::JSON_PARSE_CHROMIUM_EXTENSIONS);
*error = value_with_error.error_message;
if (!value_with_error.value)
diff --git a/chromium/components/policy/core/common/schema.h b/chromium/components/policy/core/common/schema.h
index d07629f1078..111e295cfa9 100644
--- a/chromium/components/policy/core/common/schema.h
+++ b/chromium/components/policy/core/common/schema.h
@@ -49,6 +49,11 @@ enum SchemaOnErrorStrategy {
// is safe. For example, can't be used if an empty list has a special meaning,
// like allowing everything.
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY,
+ // Same as |SCHEMA_ALLOW_UNKNOWN|, but unknown properties won't cause errors
+ // messages to be added. Used to allow adding extra fields to the policy
+ // internally, without adding those fields to the schema. This option should
+ // be avoided, since it suppresses the errors.
+ SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
};
// Schema validation options for Schema::ParseToDictAndValidate().
diff --git a/chromium/components/policy/core/common/schema_fuzzer.cc b/chromium/components/policy/core/common/schema_fuzzer.cc
index a236f8870d7..8eb3ef06ea6 100644
--- a/chromium/components/policy/core/common/schema_fuzzer.cc
+++ b/chromium/components/policy/core/common/schema_fuzzer.cc
@@ -38,7 +38,8 @@ void TestParsing(const Environment& env, const std::string& data) {
void TestValidation(const Environment& env, const base::Value& parsed_json) {
// Exercise with every possible strategy.
for (auto strategy : {SCHEMA_STRICT, SCHEMA_ALLOW_UNKNOWN,
- SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY}) {
+ SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY,
+ SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING}) {
env.chrome_policy_schema.Validate(parsed_json, strategy,
/*out_error_path=*/nullptr,
/*out_error=*/nullptr);
diff --git a/chromium/components/policy/core/common/schema_unittest.cc b/chromium/components/policy/core/common/schema_unittest.cc
index ff5bf4bb5ef..bc2b6a214d0 100644
--- a/chromium/components/policy/core/common/schema_unittest.cc
+++ b/chromium/components/policy/core/common/schema_unittest.cc
@@ -180,6 +180,9 @@ void TestSchemaValidationHelper(const std::string& source,
schema.Normalize(&cloned_value, strategy, nullptr, &error, &touched);
EXPECT_EQ(expected_return_value, returned) << source << ": " << error;
+ if (strategy == SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING)
+ return;
+
bool strictly_valid = schema.Validate(value, SCHEMA_STRICT, nullptr, &error);
EXPECT_EQ(touched, !strictly_valid && returned) << source;
@@ -789,6 +792,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(schema, bundle,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, true);
+ TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ true);
TestSchemaValidationWithPath(schema, bundle, "");
bundle.RemoveKey("boom");
@@ -798,6 +803,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(schema, bundle,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, false);
+ TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ false);
TestSchemaValidationWithPath(schema, bundle, "Boolean");
bundle.SetBoolKey("Boolean", true);
@@ -813,6 +820,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, true);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ true);
TestSchemaValidationWithPath(subschema, root, "Object");
root.RemovePath("Object.three");
@@ -822,6 +831,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, false);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ false);
TestSchemaValidationWithPath(subschema, root, "Object.one");
root.RemovePath("Object.one");
}
@@ -841,6 +852,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, true);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ true);
TestSchemaValidationWithPath(subschema, root, "items[0]");
root.EraseListIter(root_view.begin() + (root_view.size() - 1));
@@ -852,6 +865,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, true);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ false);
TestSchemaValidationWithPath(subschema, root, "items[0].two");
root.EraseListIter(root_view.begin() + (root_view.size() - 1));
}
@@ -871,6 +886,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, true);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ true);
// Invalid list item.
list_value->Append("blabla");
@@ -878,6 +895,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, true);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ false);
TestSchemaValidationWithPath(subschema, root, "List.items[1]");
}
@@ -887,24 +906,27 @@ TEST(SchemaTest, Validate) {
ASSERT_TRUE(subschema.valid());
base::ListValue root;
- auto dict_value = std::make_unique<base::DictionaryValue>();
- base::ListValue* list_value =
- dict_value->SetList("List", std::make_unique<base::ListValue>());
+ base::Value::Dict dict_value;
+ base::Value* list_value = dict_value.Set("List", base::Value::List());
root.Append(std::move(dict_value));
// Test that there are not errors here.
- list_value->Append("blabla");
+ list_value->GetList().Append("blabla");
TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, true);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ true);
// Invalid list item.
- list_value->Append(12345);
+ list_value->GetList().Append(12345);
TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, true);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ false);
TestSchemaValidationWithPath(subschema, root, "items[0].List.items[1]");
}
@@ -971,6 +993,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, true);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ true);
root.RemoveKey("foobar");
}
@@ -987,6 +1011,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, false);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ false);
// Invalid required property.
root.SetIntKey("String", 123);
@@ -994,6 +1020,8 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, false);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ false);
root.SetStringKey("String", "a string");
// Invalid subschema of required property with multiple subschemas.
@@ -1008,12 +1036,16 @@ TEST(SchemaTest, Validate) {
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, false);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ false);
root.SetIntKey("Integer", 3);
TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root,
SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY, false);
+ TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_WITHOUT_WARNING,
+ false);
}
// Test that integer to double promotion is allowed.
diff --git a/chromium/components/policy/core/common/values_util.cc b/chromium/components/policy/core/common/values_util.cc
index c2b269f38d8..5254b6671cf 100644
--- a/chromium/components/policy/core/common/values_util.cc
+++ b/chromium/components/policy/core/common/values_util.cc
@@ -25,4 +25,12 @@ base::flat_set<std::string> ValueToStringSet(const base::Value* value) {
return base::flat_set<std::string>(std::move(item_vector));
}
+ComponentPolicyMap CopyComponentPolicyMap(const ComponentPolicyMap& map) {
+ ComponentPolicyMap new_map;
+ for (const auto& [policy_namespace, value] : map) {
+ new_map[policy_namespace] = value.Clone();
+ }
+ return new_map;
+}
+
} // namespace policy
diff --git a/chromium/components/policy/core/common/values_util.h b/chromium/components/policy/core/common/values_util.h
index 2134f00f306..025d8b21b0d 100644
--- a/chromium/components/policy/core/common/values_util.h
+++ b/chromium/components/policy/core/common/values_util.h
@@ -9,15 +9,28 @@
#include "base/containers/flat_set.h"
#include "base/values.h"
+#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/policy_export.h"
namespace policy {
+// A map that represents component policy values as downloaded by the server.
+// The key is the component represented as a PolicyNamespace (e.g. a chrome
+// extension).
+// The value is a JSON value in the format understood by ComponentPolicyStore
+// (Chrome OS section of
+// https://www.chromium.org/administrators/configuring-policy-for-extensions/)
+using ComponentPolicyMap = base::flat_map<PolicyNamespace, base::Value>;
+
// Converts a list of string value to string flat set. Returns empty
// set if |value| is not set. Non-string items will be ignored.
POLICY_EXPORT base::flat_set<std::string> ValueToStringSet(
const base::Value* value);
+// Returns a copy of provided map.
+POLICY_EXPORT ComponentPolicyMap
+CopyComponentPolicyMap(const ComponentPolicyMap& map);
+
} // namespace policy
#endif // COMPONENTS_POLICY_CORE_COMMON_VALUES_UTIL_H_
diff --git a/chromium/components/policy/proto/BUILD.gn b/chromium/components/policy/proto/BUILD.gn
index 6dbc5cbd949..9a52353308e 100644
--- a/chromium/components/policy/proto/BUILD.gn
+++ b/chromium/components/policy/proto/BUILD.gn
@@ -11,6 +11,11 @@ import("//third_party/protobuf/proto_library.gni")
component("proto") {
output_name = "policy_proto"
public_deps = [ ":proto_internal" ]
+
+ # Make all direct and indirect depedendencies include the config generated by
+ # the proto_internal target below. This config adds include paths needed to
+ # compile the generated protobuf headers.
+ all_dependent_configs = [ ":proto_internal_config" ]
}
proto_library("policy_common_definitions_compile_proto") {
diff --git a/chromium/components/policy/proto/chrome_device_policy.proto b/chromium/components/policy/proto/chrome_device_policy.proto
index 826b585c5c3..34026434355 100644
--- a/chromium/components/policy/proto/chrome_device_policy.proto
+++ b/chromium/components/policy/proto/chrome_device_policy.proto
@@ -277,10 +277,9 @@ message AutoUpdateSettingsProto {
// Enumerates network connection types.
enum ConnectionType {
- reserved 2;
-
CONNECTION_TYPE_ETHERNET = 0;
CONNECTION_TYPE_WIFI = 1;
+ CONNECTION_TYPE_WIMAX = 2;
CONNECTION_TYPE_BLUETOOTH = 3;
CONNECTION_TYPE_CELLULAR = 4;
}
diff --git a/chromium/components/policy/proto/device_management_backend.proto b/chromium/components/policy/proto/device_management_backend.proto
index 717377cf63d..60923100119 100644
--- a/chromium/components/policy/proto/device_management_backend.proto
+++ b/chromium/components/policy/proto/device_management_backend.proto
@@ -74,6 +74,7 @@ message DeviceRegisterRequest {
DEVICE = 2; // Register for Chrome OS device policies.
BROWSER = 3; // Register for desktop Chrome browser user policies.
ANDROID_BROWSER = 4; // Register for Android Chrome browser user policies.
+ IOS_BROWSER = 6; // Register for iOS Chrome browser user policies.
}
// NOTE: we also use this field to detect client version. If this
// field is missing, then the request comes from TT. We will remove
@@ -148,6 +149,14 @@ message DeviceRegisterRequest {
// is mandatory, but it failed and we are doing a fallback to manual
// enrollment.
FLAVOR_ENROLLMENT_ATTESTATION_INITIAL_MANUAL_FALLBACK = 15;
+ // An enterprise rollback just took place and the device was wiped.
+ // Attempt to re-enroll with attestation. This is forced from the
+ // client side.
+ FLAVOR_ENROLLMENT_ATTESTATION_ROLLBACK_FORCED = 16;
+ // An enterprise rollback just took place and the device was wiped.
+ // Attestation re-enrollment just failed, attempt manual enrollment as
+ // fallback.
+ FLAVOR_ENROLLMENT_ATTESTATION_ROLLBACK_MANUAL_FALLBACK = 17;
}
// Indicates the registration flavor. This is passed to the server FYI when
@@ -711,6 +720,11 @@ message PolicyData {
// device's managing OU's SSO profile. Currently, this points to the OU's
// SAML settings. May support OIDC in the future.
optional string sso_profile = 37;
+
+ // Indicates which kind of licenses this device is using, so that chrome OS
+ // can check that. The value is from ChromeOsDeviceInfo
+ // http://google3/ccc/hosted/devices/services/chromeos/common.proto;rcl=436571779;l=549
+ optional string license_sku = 38;
}
message ClientActionRequired {
@@ -1427,6 +1441,17 @@ message NetworkAdapterInfo {
repeated string driver = 7;
}
+// Information about the list of browsers and profiles that are
+// running and installed on the device in the DeviceStatusReport
+message LaCrOsBrowserReport {
+ // Limited to one user since LaCrosBrowserReport is repeated based on each
+ // user. If multiple lacros user profiles exist, each LaCrOsBrowserReport
+ // message (one per lacros user profile) will contain the same |device_user|.
+ optional DeviceUser device_user = 1;
+ // Group of data regarding the browser and user profile.
+ optional BrowserReport browser_report = 2;
+}
+
// Report device level status.
message DeviceStatusReportRequest {
reserved 4, 7, 13, 20;
@@ -1565,6 +1590,9 @@ message DeviceStatusReportRequest {
// Information about the device's network hardware.
repeated NetworkAdapterInfo network_adapter_info = 45;
+
+ // Information about LaCrOs
+ repeated LaCrOsBrowserReport lacros_browser_report = 46;
}
message OsUpdateStatus {
@@ -4003,6 +4031,20 @@ message BrowserPublicKeyUploadResponse {
optional ResponseCode response_code = 1;
}
+// Additional detail about the error which can be used to clarify user messaging
+// around the error or to influence Chrome's error-handling behavior.
+enum DeviceManagementErrorDetail {
+ NO_ERROR_DETAIL = 0;
+
+ // Only valid for CBCM browser DEVICE_NOT_FOUND errors. Indicates that the
+ // DMToken should be deleted entirely from its storage location.
+ CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN = 1;
+
+ // Only valid for CBCM browser DEVICE_NOT_FOUND errors. Indicates that the
+ // DMToken should be invalidated, preventing device re-enrollment.
+ CBCM_DELETION_POLICY_PREFERENCE_INVALIDATE_TOKEN = 2;
+}
+
// Request from the DMAgent on the device to the DMServer. This is
// container for all requests from device to server. The overall HTTP
// request MUST be in the following format:
@@ -4272,6 +4314,9 @@ message DeviceManagementResponse {
// Error message.
optional string error_message = 2;
+ // Additional details about the error message.
+ repeated DeviceManagementErrorDetail error_detail = 39;
+
// Register response
optional DeviceRegisterResponse register_response = 3;
@@ -4383,7 +4428,7 @@ message DeviceManagementResponse {
// Response to a Chrome Profile report request.
optional ChromeProfileReportResponse chrome_profile_report_response = 38;
- // Next id: 39.
+ // Next id: 40.
}
// Device State Information stored in the server is retrieval at
diff --git a/chromium/components/policy/resources/webui/BUILD.gn b/chromium/components/policy/resources/webui/BUILD.gn
new file mode 100644
index 00000000000..42595783971
--- /dev/null
+++ b/chromium/components/policy/resources/webui/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2022 The Chromium 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("//tools/polymer/html_to_js.gni")
+
+html_to_js("web_components") {
+ js_files = [
+ "policy_conflict.js",
+ "policy_row.js",
+ "policy_precedence_row.js",
+ "policy_table.js",
+ "status_box.js",
+ ]
+}
diff --git a/chromium/components/policy/test_support/policy_storage.h b/chromium/components/policy/test_support/policy_storage.h
index bbb831862da..06edb0a09e9 100644
--- a/chromium/components/policy/test_support/policy_storage.h
+++ b/chromium/components/policy/test_support/policy_storage.h
@@ -157,6 +157,15 @@ class PolicyStorage {
std::vector<std::string> GetMatchingSerialHashes(uint64_t modulus,
uint64_t remainder) const;
+ void set_error_detail(
+ enterprise_management::DeviceManagementErrorDetail error_detail) {
+ error_detail_ = error_detail;
+ }
+
+ enterprise_management::DeviceManagementErrorDetail error_detail() const {
+ return error_detail_;
+ }
+
private:
// Maps policy keys to a serialized proto representing the policies to be
// applied for the type (e.g. CloudPolicySettings, ChromeDeviceSettingsProto).
@@ -193,6 +202,11 @@ class PolicyStorage {
// Maps brand serial ID to InitialEnrollmentState.
base::flat_map<std::string, InitialEnrollmentState>
initial_enrollment_states_;
+
+ // Determines whether the DMToken should be invalidated or deleted during
+ // browser unenrollment.
+ enterprise_management::DeviceManagementErrorDetail error_detail_ =
+ enterprise_management::CBCM_DELETION_POLICY_PREFERENCE_INVALIDATE_TOKEN;
};
} // namespace policy
diff --git a/chromium/components/policy/test_support/request_handler_for_policy.cc b/chromium/components/policy/test_support/request_handler_for_policy.cc
index 569d98b67d1..dbc94bb0304 100644
--- a/chromium/components/policy/test_support/request_handler_for_policy.cc
+++ b/chromium/components/policy/test_support/request_handler_for_policy.cc
@@ -59,11 +59,16 @@ std::unique_ptr<HttpResponse> RequestHandlerForPolicy::HandleRequest(
if (!GetDeviceTokenFromRequest(request, &request_device_token))
return CreateHttpResponse(net::HTTP_UNAUTHORIZED, "Invalid device token.");
+ em::DeviceManagementResponse device_management_response;
const ClientStorage::ClientInfo* client_info =
client_storage()->GetClientOrNull(
KeyValueFromUrl(request.GetURL(), dm_protocol::kParamDeviceID));
- if (!client_info || client_info->device_token != request_device_token)
- return CreateHttpResponse(net::HTTP_GONE, "Invalid device token.");
+ if (!client_info || client_info->device_token != request_device_token) {
+ device_management_response.add_error_detail(
+ policy_storage()->error_detail());
+ return CreateHttpResponse(net::HTTP_GONE,
+ device_management_response.SerializeAsString());
+ }
em::DeviceManagementRequest device_management_request;
device_management_request.ParseFromString(request.content);
@@ -82,7 +87,6 @@ std::unique_ptr<HttpResponse> RequestHandlerForPolicy::HandleRequest(
}
}
- em::DeviceManagementResponse device_management_response;
for (const auto& fetch_request :
device_management_request.policy_request().requests()) {
const std::string& policy_type = fetch_request.policy_type();
diff --git a/chromium/components/policy/tools/generate_policy_source.py b/chromium/components/policy/tools/generate_policy_source.py
index 10863698283..226c91e7671 100755
--- a/chromium/components/policy/tools/generate_policy_source.py
+++ b/chromium/components/policy/tools/generate_policy_source.py
@@ -110,6 +110,13 @@ class PolicyDetails:
self.has_enterprise_default = 'default_for_enterprise_users' in policy
if self.has_enterprise_default:
self.enterprise_default = policy['default_for_enterprise_users']
+ if self.has_enterprise_default:
+ self.default_policy_level = policy.get('default_policy_level', '')
+ if self.default_policy_level == 'recommended' and not self.can_be_recommended:
+ raise RuntimeError('Policy ' + self.name +
+ ' has default_policy_level set to ' +
+ self.default_policy_level + ', '
+ 'but can_be_recommended feature is not set to True')
self.cloud_only = features.get('cloud_only', False)
self.platforms = set()
@@ -483,19 +490,26 @@ def _GetSupportedChromeUserPolicies(policies, protobuf_type):
]
+# Returns the policies supported by at least one platform.
+def _GetSupportedPolicies(policies):
+ return [
+ policy for policy in policies
+ if len(policy.platforms) + len(policy.future_on) > 0
+ ]
+
#------------------ policy constants header ------------------------#
# Return a list of all policies of type |metapolicy_type|.
def _GetMetapoliciesOfType(policies, metapolicy_type):
return [
- policy.name for policy in policies
- if policy.metapolicy_type == metapolicy_type
+ policy for policy in policies if policy.metapolicy_type == metapolicy_type
]
-def _WritePolicyConstantHeader(policies, policy_atomic_groups, target_platform,
- f, risk_tags):
+def _WritePolicyConstantHeader(all_policies, policy_atomic_groups,
+ target_platform, f, risk_tags):
+ policies = _GetSupportedPolicies(all_policies)
f.write('''#ifndef COMPONENTS_POLICY_POLICY_CONSTANTS_H_
#define COMPONENTS_POLICY_POLICY_CONSTANTS_H_
@@ -1083,8 +1097,10 @@ def _GenerateDefaultValue(value):
return [], None
-def _WritePolicyConstantSource(policies, policy_atomic_groups, target_platform,
- f, risk_tags):
+def _WritePolicyConstantSource(all_policies, policy_atomic_groups,
+ target_platform, f, risk_tags):
+ policies = _GetSupportedPolicies(all_policies)
+ policy_names = [policy.name for policy in policies]
f.write('''#include "components/policy/policy_constants.h"
#include <algorithm>
@@ -1198,16 +1214,20 @@ namespace policy {
else:
declare_default = ''
+ policy_level = "POLICY_LEVEL_MANDATORY"
+ if policy.default_policy_level == 'recommended':
+ policy_level = "POLICY_LEVEL_RECOMMENDED"
+
setting_enterprise_default = ''' if (!policy_map->Get(key::k%s)) {
%s
policy_map->Set(key::k%s,
- POLICY_LEVEL_MANDATORY,
+ %s,
POLICY_SCOPE_USER,
POLICY_SOURCE_ENTERPRISE_DEFAULT,
%s,
nullptr);
}
-''' % (policy.name, declare_default, policy.name, fetch_default)
+''' % (policy.name, declare_default, policy.name, policy_level, fetch_default)
if policy.per_profile:
profile_policy_enterprise_defaults += setting_enterprise_default
@@ -1285,7 +1305,8 @@ void SetEnterpriseUsersDefaults(PolicyMap* policy_map) {
for group in policy_atomic_groups:
f.write('const char* const %s[] = {' % (group.name))
for policy in group.policies:
- f.write('key::k%s, ' % (policy))
+ if policy in policy_names:
+ f.write('key::k%s, ' % (policy))
f.write('nullptr};\n')
f.write('\n} // namespace\n')
f.write('\n} // namespace group\n\n')
@@ -1308,7 +1329,7 @@ void SetEnterpriseUsersDefaults(PolicyMap* policy_map) {
METAPOLICY_TYPE['merge'])
f.write('const char* const kMerge[%s] = {\n' % len(merge_metapolicies))
for metapolicy in merge_metapolicies:
- f.write(' key::k%s,\n' % metapolicy)
+ f.write(' key::k%s,\n' % metapolicy.name)
f.write('};\n\n')
# Populate precedence metapolicy array.
@@ -1317,7 +1338,7 @@ void SetEnterpriseUsersDefaults(PolicyMap* policy_map) {
f.write('const char* const kPrecedence[%s] = {\n' %
len(precedence_metapolicies))
for metapolicy in precedence_metapolicies:
- f.write(' key::k%s,\n' % metapolicy)
+ f.write(' key::k%s,\n' % metapolicy.name)
f.write('};\n\n')
f.write('} // namespace metapolicy\n\n')
diff --git a/chromium/components/policy/tools/generate_policy_source_test.py b/chromium/components/policy/tools/generate_policy_source_test.py
index 26282724ca0..732235e7053 100755
--- a/chromium/components/policy/tools/generate_policy_source_test.py
+++ b/chromium/components/policy/tools/generate_policy_source_test.py
@@ -56,7 +56,8 @@ class PolicyGenerationTest(unittest.TestCase):
"schema": {
"type": "boolean"
},
- "supported_on": ["chrome_os:1-"],
+ "supported_on":
+ ["chrome_os:1-", "chrome.*:1-", "android:1-", "ios:1-"],
"features": {
"metapolicy_type": "merge",
},
@@ -70,7 +71,8 @@ class PolicyGenerationTest(unittest.TestCase):
"schema": {
"type": "boolean"
},
- "supported_on": ["chrome_os:1-"],
+ "supported_on":
+ ["chrome_os:1-", "chrome.*:1-", "android:1-", "ios:1-"],
"features": {
"metapolicy_type": "precedence",
},
@@ -158,12 +160,22 @@ class PolicyGenerationTest(unittest.TestCase):
"tags": [],
"caption": "ChunkTwoLastFieldStringPolicy caption",
"desc": "ChunkTwoLastFieldStringPolicy desc"
+ }, {
+ "name": "UnsupportedPolicy",
+ "type": "string",
+ "schema": {
+ "type": "string"
+ },
+ "supported_on": [],
+ "id": 2616,
+ "tags": [],
+ "caption": "UnsupportedPolicy caption",
+ "desc": "UnsupportedPolicy desc"
}],
"policy_atomic_group_definitions": []
}
def setUp(self):
- self.maxDiff = 10000
self.chrome_major_version = 94
self.target_platform = 'chrome_os'
self.all_target_platforms = ['win', 'mac', 'linux', 'chromeos', 'fuchsia']
@@ -314,18 +326,17 @@ class PolicyGenerationTest(unittest.TestCase):
def testGetMetapoliciesOfType(self):
merge_metapolicies = generate_policy_source._GetMetapoliciesOfType(
self.policies, "merge")
- self.assertListEqual(["ExampleBoolMergeMetapolicy"], merge_metapolicies)
self.assertEqual(1, len(merge_metapolicies))
+ self.assertEqual("ExampleBoolMergeMetapolicy", merge_metapolicies[0].name)
precedence_metapolicies = generate_policy_source._GetMetapoliciesOfType(
self.policies, "precedence")
- self.assertListEqual(["ExampleBoolPrecedenceMetapolicy"],
- precedence_metapolicies)
self.assertEqual(1, len(precedence_metapolicies))
+ self.assertEqual("ExampleBoolPrecedenceMetapolicy",
+ precedence_metapolicies[0].name)
invalid_metapolicies = generate_policy_source._GetMetapoliciesOfType(
self.policies, "invalid")
- self.assertListEqual([], invalid_metapolicies)
self.assertEqual(0, len(invalid_metapolicies))
def testWritePolicyConstantHeader(self):
diff --git a/chromium/components/policy/tools/generate_policy_source_test_data.py b/chromium/components/policy/tools/generate_policy_source_test_data.py
index 2bc4db62ca7..6c321812712 100644
--- a/chromium/components/policy/tools/generate_policy_source_test_data.py
+++ b/chromium/components/policy/tools/generate_policy_source_test_data.py
@@ -79,7 +79,7 @@ message ExampleBoolPolicyProto {
//
// ExampleBoolMergeMetapolicy desc
//
-// Supported on: chrome_os
+// Supported on: android, chrome_os, fuchsia, ios, linux, mac, win
message ExampleBoolMergeMetapolicyProto {
optional PolicyOptions policy_options = 1;
optional bool ExampleBoolMergeMetapolicy = 2;
@@ -89,7 +89,7 @@ message ExampleBoolMergeMetapolicyProto {
//
// ExampleBoolPrecedenceMetapolicy desc
//
-// Supported on: chrome_os
+// Supported on: android, chrome_os, fuchsia, ios, linux, mac, win
message ExampleBoolPrecedenceMetapolicyProto {
optional PolicyOptions policy_options = 1;
optional bool ExampleBoolPrecedenceMetapolicy = 2;
@@ -165,6 +165,16 @@ message ChunkTwoLastFieldStringPolicyProto {
optional string ChunkTwoLastFieldStringPolicy = 2;
}
+// UnsupportedPolicy caption
+//
+// UnsupportedPolicy desc
+//
+// Supported on:
+message UnsupportedPolicyProto {
+ optional PolicyOptions policy_options = 1;
+ optional string UnsupportedPolicy = 2;
+}
+
// --------------------------------------------------
// PBs for policies with ID > 1015.
@@ -178,6 +188,10 @@ message ChromeSettingsSubProto2 {
optional ChunkTwoLastFieldStringPolicyProto ChunkTwoLastFieldStringPolicy = 800;
}
+message ChromeSettingsSubProto3 {
+ optional UnsupportedPolicyProto UnsupportedPolicy = 1;
+}
+
// --------------------------------------------------
// Big wrapper PB containing the above groups.
@@ -191,6 +205,7 @@ message ChromeSettingsProto {
optional ChunkZeroLastFieldBooleanPolicyProto ChunkZeroLastFieldBooleanPolicy = 1017;
optional ChromeSettingsSubProto1 subProto1 = 1018;
optional ChromeSettingsSubProto2 subProto2 = 1019;
+ optional ChromeSettingsSubProto3 subProto3 = 1020;
}
"""
diff --git a/chromium/components/policy/tools/syntax_check_policy_template_json.py b/chromium/components/policy/tools/syntax_check_policy_template_json.py
index 18f53cd4c68..1f064d4d18e 100755
--- a/chromium/components/policy/tools/syntax_check_policy_template_json.py
+++ b/chromium/components/policy/tools/syntax_check_policy_template_json.py
@@ -826,6 +826,7 @@ class PolicyTemplateChecker(object):
'default',
'default_for_enterprise_users',
'default_for_managed_devices_doc_only',
+ 'default_policy_level',
'arc_support',
'supported_chrome_os_management',
):
@@ -1018,6 +1019,26 @@ class PolicyTemplateChecker(object):
'also have to done in other components if they read the '
'proto directly. Details: crbug.com/809653')
+ default_policy_level = self._CheckContains(
+ policy,
+ 'default_policy_level',
+ str,
+ optional=True,
+ regexp_check=re.compile('^(recommended|mandatory)$'))
+
+ if default_policy_level:
+ if 'default_for_enterprise_users' not in policy:
+ self._Error('default_for_enteprise_users should be set when '
+ 'default_policy_level is set ')
+ if (default_policy_level == 'recommended'
+ and not features.get('can_be_recommended', False)):
+ self._Error('can_be_recommended should be set to True when '
+ 'default_policy_level is set to "recommended"')
+ if (default_policy_level == 'mandatory'
+ and not features.get('can_be_mandatory', True)):
+ self._Error('can_be_mandatory should be set to True when '
+ 'default_policy_level is set to "recommended"')
+
if (not policy.get('device_only', False) and
'default_for_managed_devices_doc_only' in policy):
self._Error('default_for_managed_devices_doc_only should only be used '
diff --git a/chromium/components/policy/tools/template_writers/PRESUBMIT.py b/chromium/components/policy/tools/template_writers/PRESUBMIT.py
index e159b0d3b7a..af30ccf3dd5 100755
--- a/chromium/components/policy/tools/template_writers/PRESUBMIT.py
+++ b/chromium/components/policy/tools/template_writers/PRESUBMIT.py
@@ -13,8 +13,10 @@ USE_PYTHON3 = True
def RunUnittests(input_api, output_api):
- return input_api.canned_checks.RunPythonUnitTests(input_api, output_api,
- ['test_suite_all'])
+ return input_api.canned_checks.RunPythonUnitTests(input_api,
+ output_api,
+ ['test_suite_all'],
+ python3=True)
def CheckChangeOnUpload(input_api, output_api):
diff --git a/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer.py b/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer.py
index a5a61f9d404..74d09067523 100755
--- a/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer.py
+++ b/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer.py
@@ -138,7 +138,9 @@ class IOSAppConfigWriter(xml_formatted_writer.XMLFormattedWriter):
constraint = self.AddElement(parent, 'constraint', attrs)
if 'enum' in policy['type']:
values_element = self.AddElement(constraint, 'values', {})
- for v in policy['schema']['enum']:
+ enum = policy['schema']['enum'] if 'enum' in policy['schema'] else policy[
+ 'schema']['items']['enum']
+ for v in enum:
value = self.AddElement(values_element, 'value', {})
self.AddText(value,
_ParseSchemaTypeValueToString(v, policy['schema']['type']))
diff --git a/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py b/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py
index 8774f2e84a3..89cd7f47334 100755
--- a/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py
+++ b/chromium/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py
@@ -229,8 +229,11 @@ class IOSAppConfigWriterUnitTests(writer_unittest_common.WriterUnittestCommon):
'desc':
'string-enum-list description',
'schema': {
- 'type': 'string',
- 'enum': ['0', '1'],
+ 'type': 'array',
+ 'items': {
+ 'type': 'string',
+ 'enum': ['0', '1'],
+ },
},
'items': [{
'name': 'item0',
diff --git a/chromium/components/policy_strings.grdp b/chromium/components/policy_strings.grdp
index c283c6ae60c..883263511f1 100644
--- a/chromium/components/policy_strings.grdp
+++ b/chromium/components/policy_strings.grdp
@@ -61,6 +61,9 @@
<message name="IDS_POLICY_DM_STATUS_SERVICE_TOO_MANY_REQUESTS" desc="Message indicating the enterprise server receives too many requests in a short time period.">
Too many requests
</message>
+ <message name="IDS_POLICY_DM_STATUS_SERVICE_DEVICE_NEEDS_RESET" desc="Message indicating that the device should be reset.">
+ Device reset required
+ </message>
<message name="IDS_POLICY_DM_STATUS_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE" desc="Message to show when the user tries to enroll a device with a packaged license using a consumer account.">
Can't enroll with consumer account (packaged license available).
</message>
diff --git a/chromium/components/policy_strings_grdp/IDS_POLICY_DM_STATUS_SERVICE_DEVICE_NEEDS_RESET.png.sha1 b/chromium/components/policy_strings_grdp/IDS_POLICY_DM_STATUS_SERVICE_DEVICE_NEEDS_RESET.png.sha1
new file mode 100644
index 00000000000..a88e3d5e029
--- /dev/null
+++ b/chromium/components/policy_strings_grdp/IDS_POLICY_DM_STATUS_SERVICE_DEVICE_NEEDS_RESET.png.sha1
@@ -0,0 +1 @@
+a0a0221d106eec1d7699d7b3394fedac9642a935 \ No newline at end of file
diff --git a/chromium/components/power_metrics/resource_coalition_mac_unittest.mm b/chromium/components/power_metrics/resource_coalition_mac_unittest.mm
index ee113cd7d6b..6db428ee802 100644
--- a/chromium/components/power_metrics/resource_coalition_mac_unittest.mm
+++ b/chromium/components/power_metrics/resource_coalition_mac_unittest.mm
@@ -76,7 +76,7 @@ void BurnCPU() {
[[maybe_unused]] volatile double number = 1;
while (base::TimeTicks::Now() < (begin + busy_time)) {
for (int i = 0; i < 10000; ++i)
- number *= base::RandDouble();
+ number = number * base::RandDouble();
}
}
diff --git a/chromium/components/prefs/android/BUILD.gn b/chromium/components/prefs/android/BUILD.gn
index 4d5e50fdcda..e2157481e8b 100644
--- a/chromium/components/prefs/android/BUILD.gn
+++ b/chromium/components/prefs/android/BUILD.gn
@@ -11,7 +11,8 @@ generate_jni("jni_headers") {
android_library("java") {
sources = [ "java/src/org/chromium/components/prefs/PrefService.java" ]
deps = [
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
@@ -24,7 +25,6 @@ java_library("junit") {
sources = [ "java/src/org/chromium/components/prefs/PrefServiceTest.java" ]
deps = [
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//base/test:test_support_java",
diff --git a/chromium/components/prefs/pref_member_unittest.cc b/chromium/components/prefs/pref_member_unittest.cc
index df51f9942f3..c11ddf59ffa 100644
--- a/chromium/components/prefs/pref_member_unittest.cc
+++ b/chromium/components/prefs/pref_member_unittest.cc
@@ -207,13 +207,13 @@ TEST_F(PrefMemberTest, BasicGetAndSet) {
EXPECT_FALSE(string.IsDefaultValue());
// Test string list
- base::Value expected_list(base::Value::Type::LIST);
+ base::Value::List expected_list;
std::vector<std::string> expected_vector;
StringListPrefMember string_list;
string_list.Init(kStringListPref, &prefs);
// Check the defaults
- EXPECT_EQ(expected_list, *prefs.GetList(kStringListPref));
+ EXPECT_EQ(expected_list, *prefs.GetValueList(kStringListPref));
EXPECT_EQ(expected_vector, string_list.GetValue());
EXPECT_EQ(expected_vector, *string_list);
EXPECT_TRUE(string_list.IsDefaultValue());
@@ -223,7 +223,7 @@ TEST_F(PrefMemberTest, BasicGetAndSet) {
expected_vector.push_back("foo");
string_list.SetValue(expected_vector);
- EXPECT_EQ(expected_list, *prefs.GetList(kStringListPref));
+ EXPECT_EQ(expected_list, *prefs.GetValueList(kStringListPref));
EXPECT_EQ(expected_vector, string_list.GetValue());
EXPECT_EQ(expected_vector, *string_list);
EXPECT_FALSE(string_list.IsDefaultValue());
@@ -231,20 +231,19 @@ TEST_F(PrefMemberTest, BasicGetAndSet) {
// Try adding through the pref.
expected_list.Append("bar");
expected_vector.push_back("bar");
- prefs.Set(kStringListPref, expected_list);
+ prefs.SetList(kStringListPref, expected_list.Clone());
- EXPECT_EQ(expected_list, *prefs.GetList(kStringListPref));
+ EXPECT_EQ(expected_list, *prefs.GetValueList(kStringListPref));
EXPECT_EQ(expected_vector, string_list.GetValue());
EXPECT_EQ(expected_vector, *string_list);
EXPECT_FALSE(string_list.IsDefaultValue());
// Try removing through the pref.
- EXPECT_TRUE(
- expected_list.EraseListIter(expected_list.GetListDeprecated().begin()));
+ expected_list.erase(expected_list.begin());
expected_vector.erase(expected_vector.begin());
- prefs.Set(kStringListPref, expected_list);
+ prefs.SetList(kStringListPref, expected_list.Clone());
- EXPECT_EQ(expected_list, *prefs.GetList(kStringListPref));
+ EXPECT_EQ(expected_list, *prefs.GetValueList(kStringListPref));
EXPECT_EQ(expected_vector, string_list.GetValue());
EXPECT_EQ(expected_vector, *string_list);
EXPECT_FALSE(string_list.IsDefaultValue());
diff --git a/chromium/components/prefs/pref_registry.h b/chromium/components/prefs/pref_registry.h
index 18f9df3a1a3..672106397c1 100644
--- a/chromium/components/prefs/pref_registry.h
+++ b/chromium/components/prefs/pref_registry.h
@@ -37,19 +37,19 @@ class COMPONENTS_PREFS_EXPORT PrefRegistry
// behave or be stored. This will be passed in a bitmask when the pref is
// registered. Subclasses of PrefRegistry can specify their own flags. Care
// must be taken to ensure none of these overlap with the flags below.
- enum PrefRegistrationFlags : uint32_t {
- // No flags are specified.
- NO_REGISTRATION_FLAGS = 0,
+ using PrefRegistrationFlags = uint32_t;
- // The first 8 bits are reserved for subclasses of PrefRegistry to use.
+ // No flags are specified.
+ static constexpr PrefRegistrationFlags NO_REGISTRATION_FLAGS = 0;
- // This marks the pref as "lossy". There is no strict time guarantee on when
- // a lossy pref will be persisted to permanent storage when it is modified.
- LOSSY_PREF = 1 << 8,
+ // The first 8 bits are reserved for subclasses of PrefRegistry to use.
- // Registering a pref as public allows other services to access it.
- PUBLIC = 1 << 9,
- };
+ // This marks the pref as "lossy". There is no strict time guarantee on when
+ // a lossy pref will be persisted to permanent storage when it is modified.
+ static constexpr PrefRegistrationFlags LOSSY_PREF = 1 << 8;
+
+ // Registering a pref as public allows other services to access it.
+ static constexpr PrefRegistrationFlags PUBLIC = 1 << 9;
typedef PrefValueMap::const_iterator const_iterator;
typedef std::unordered_map<std::string, uint32_t> PrefRegistrationFlagsMap;
diff --git a/chromium/components/prefs/pref_registry_simple.cc b/chromium/components/prefs/pref_registry_simple.cc
index 23af262a71f..aaf000eea2d 100644
--- a/chromium/components/prefs/pref_registry_simple.cc
+++ b/chromium/components/prefs/pref_registry_simple.cc
@@ -56,6 +56,12 @@ void PrefRegistrySimple::RegisterListPref(const std::string& path,
RegisterPreference(path, std::move(default_value), flags);
}
+void PrefRegistrySimple::RegisterListPref(const std::string& path,
+ base::Value::List default_value,
+ uint32_t flags) {
+ RegisterPreference(path, base::Value(std::move(default_value)), flags);
+}
+
void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path,
uint32_t flags) {
RegisterPreference(path, base::Value(base::Value::Type::DICTIONARY), flags);
@@ -67,6 +73,12 @@ void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path,
RegisterPreference(path, std::move(default_value), flags);
}
+void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path,
+ base::Value::Dict default_value,
+ uint32_t flags) {
+ RegisterPreference(path, base::Value(std::move(default_value)), flags);
+}
+
void PrefRegistrySimple::RegisterInt64Pref(const std::string& path,
int64_t default_value,
uint32_t flags) {
diff --git a/chromium/components/prefs/pref_registry_simple.h b/chromium/components/prefs/pref_registry_simple.h
index 387075f08aa..d870eff2b4d 100644
--- a/chromium/components/prefs/pref_registry_simple.h
+++ b/chromium/components/prefs/pref_registry_simple.h
@@ -11,11 +11,11 @@
#include <string>
#include "base/time/time.h"
+#include "base/values.h"
#include "components/prefs/pref_registry.h"
#include "components/prefs/prefs_export.h"
namespace base {
-class Value;
class FilePath;
}
@@ -56,6 +56,10 @@ class COMPONENTS_PREFS_EXPORT PrefRegistrySimple : public PrefRegistry {
base::Value default_value,
uint32_t flags = NO_REGISTRATION_FLAGS);
+ void RegisterListPref(const std::string& path,
+ base::Value::List default_value,
+ uint32_t flags = NO_REGISTRATION_FLAGS);
+
void RegisterDictionaryPref(const std::string& path,
uint32_t flags = NO_REGISTRATION_FLAGS);
@@ -63,6 +67,10 @@ class COMPONENTS_PREFS_EXPORT PrefRegistrySimple : public PrefRegistry {
base::Value default_value,
uint32_t flags = NO_REGISTRATION_FLAGS);
+ void RegisterDictionaryPref(const std::string& path,
+ base::Value::Dict default_value,
+ uint32_t flags = NO_REGISTRATION_FLAGS);
+
void RegisterInt64Pref(const std::string& path,
int64_t default_value,
uint32_t flags = NO_REGISTRATION_FLAGS);
diff --git a/chromium/components/prefs/pref_service.cc b/chromium/components/prefs/pref_service.cc
index 11ab7df3502..344e68dc7b3 100644
--- a/chromium/components/prefs/pref_service.cc
+++ b/chromium/components/prefs/pref_service.cc
@@ -268,6 +268,26 @@ base::FilePath PrefService::GetFilePath(const std::string& path) const {
return *result;
}
+const base::Value::Dict* PrefService::GetValueDict(
+ const std::string& path) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ const base::Value* value = GetDictionary(path);
+ if (!value)
+ return nullptr;
+ return &value->GetDict();
+}
+
+const base::Value::List* PrefService::GetValueList(
+ const std::string& path) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ const base::Value* value = GetList(path);
+ if (!value)
+ return nullptr;
+ return &value->GetList();
+}
+
bool PrefService::HasPrefPath(const std::string& path) const {
const Preference* pref = FindPreference(path);
return pref && !pref->IsDefaultValue();
diff --git a/chromium/components/prefs/pref_service.h b/chromium/components/prefs/pref_service.h
index 41be3d16cd4..0c8ca54a489 100644
--- a/chromium/components/prefs/pref_service.h
+++ b/chromium/components/prefs/pref_service.h
@@ -241,11 +241,15 @@ class COMPONENTS_PREFS_EXPORT PrefService {
double GetDouble(const std::string& path) const;
std::string GetString(const std::string& path) const;
base::FilePath GetFilePath(const std::string& path) const;
+ const base::Value::Dict* GetValueDict(const std::string& path) const;
+ const base::Value::List* GetValueList(const std::string& path) const;
// Returns the branch if it exists, or the registered default value otherwise.
// Note that |path| must point to a registered preference. In that case, these
// functions will never return NULL.
const base::Value* Get(const std::string& path) const;
+ // DEPRECATED: Prefer Get(), GetValueDict(), and GetValueList().
+ // TODO(https://crbug.com/1334665): Remove these methods.
const base::Value* GetDictionary(const std::string& path) const;
const base::Value* GetList(const std::string& path) const;
@@ -474,7 +478,7 @@ class COMPONENTS_PREFS_EXPORT PrefService {
void OnInitializationCompleted(bool succeeded) override;
private:
- PrefService* pref_service_ = nullptr;
+ raw_ptr<PrefService> pref_service_ = nullptr;
};
// Sends notification of a changed preference. This needs to be called by
diff --git a/chromium/components/prefs/scoped_user_pref_update_unittest.cc b/chromium/components/prefs/scoped_user_pref_update_unittest.cc
index 18eaa5114e6..f344c79c211 100644
--- a/chromium/components/prefs/scoped_user_pref_update_unittest.cc
+++ b/chromium/components/prefs/scoped_user_pref_update_unittest.cc
@@ -79,24 +79,23 @@ TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnything) {
}
TEST_F(ScopedUserPrefUpdateTest, UpdatingListPrefWithDefaults) {
- base::Value::ListStorage defaults;
- defaults.emplace_back("firstvalue");
- defaults.emplace_back("secondvalue");
+ base::Value::List defaults;
+ defaults.Append("firstvalue");
+ defaults.Append("secondvalue");
std::string pref_name = "mypref";
- prefs_.registry()->RegisterListPref(pref_name,
- base::Value(std::move(defaults)));
- EXPECT_EQ(2u, prefs_.GetList(pref_name)->GetListDeprecated().size());
+ prefs_.registry()->RegisterListPref(pref_name, std::move(defaults));
+ EXPECT_EQ(2u, prefs_.GetValueList(pref_name)->size());
ListPrefUpdate update(&prefs_, pref_name);
update->Append("thirdvalue");
- EXPECT_EQ(3u, prefs_.GetList(pref_name)->GetListDeprecated().size());
+ EXPECT_EQ(3u, prefs_.GetValueList(pref_name)->size());
}
TEST_F(ScopedUserPrefUpdateTest, UpdatingDictionaryPrefWithDefaults) {
- base::Value defaults(base::Value::Type::DICTIONARY);
- defaults.SetStringKey("firstkey", "value");
- defaults.SetStringKey("secondkey", "value");
+ base::Value::Dict defaults;
+ defaults.Set("firstkey", "value");
+ defaults.Set("secondkey", "value");
std::string pref_name = "mypref";
prefs_.registry()->RegisterDictionaryPref(pref_name, std::move(defaults));
diff --git a/chromium/components/printing/browser/print_manager.cc b/chromium/components/printing/browser/print_manager.cc
index 8c7d2b53e40..1f1b46af4d5 100644
--- a/chromium/components/printing/browser/print_manager.cc
+++ b/chromium/components/printing/browser/print_manager.cc
@@ -44,7 +44,8 @@ void PrintManager::DidPrintDocument(mojom::DidPrintDocumentParamsPtr params,
void PrintManager::ShowInvalidPrinterSettingsError() {}
-void PrintManager::PrintingFailed(int32_t cookie) {
+void PrintManager::PrintingFailed(int32_t cookie,
+ mojom::PrintFailureReason reason) {
// Note: Not redundant with cookie checks in the same method in other parts of
// the class hierarchy.
if (!IsValidCookie(cookie))
diff --git a/chromium/components/printing/browser/print_manager.h b/chromium/components/printing/browser/print_manager.h
index a282cf24ef1..75c74535903 100644
--- a/chromium/components/printing/browser/print_manager.h
+++ b/chromium/components/printing/browser/print_manager.h
@@ -48,7 +48,8 @@ class PrintManager : public content::WebContentsObserver,
DidPrintDocumentCallback callback) override;
void DidShowPrintDialog() override;
void ShowInvalidPrinterSettingsError() override;
- void PrintingFailed(int32_t cookie) override;
+ void PrintingFailed(int32_t cookie,
+ mojom::PrintFailureReason reason) override;
void ClearPrintRenderFramesForTesting();
diff --git a/chromium/components/printing/browser/print_to_pdf/BUILD.gn b/chromium/components/printing/browser/print_to_pdf/BUILD.gn
index a6cf91e02eb..028212e8ec5 100644
--- a/chromium/components/printing/browser/print_to_pdf/BUILD.gn
+++ b/chromium/components/printing/browser/print_to_pdf/BUILD.gn
@@ -33,6 +33,8 @@ source_set("unit_tests") {
deps = [
":print_to_pdf",
+ "//printing",
+ "//testing/gmock",
"//testing/gtest",
]
}
diff --git a/chromium/components/printing/browser/print_to_pdf/pdf_print_manager.cc b/chromium/components/printing/browser/print_to_pdf/pdf_print_manager.cc
index 3ba45e77f66..82591f8c2ab 100644
--- a/chromium/components/printing/browser/print_to_pdf/pdf_print_manager.cc
+++ b/chromium/components/printing/browser/print_to_pdf/pdf_print_manager.cc
@@ -81,6 +81,8 @@ std::string PdfPrintManager::PrintResultToString(PrintResult result) {
return "The previous printing job hasn't finished";
case PAGE_RANGE_SYNTAX_ERROR:
return "Page range syntax error";
+ case PAGE_RANGE_INVALID_RANGE:
+ return "Page range is invalid (start > end)";
case PAGE_COUNT_EXCEEDED:
return "Page range exceeds page count";
default:
@@ -92,7 +94,6 @@ std::string PdfPrintManager::PrintResultToString(PrintResult result) {
void PdfPrintManager::PrintToPdf(
content::RenderFrameHost* rfh,
const std::string& page_ranges,
- bool ignore_invalid_page_ranges,
printing::mojom::PrintPagesParamsPtr print_pages_params,
PrintToPdfCallback callback) {
DCHECK(callback);
@@ -109,25 +110,35 @@ void PdfPrintManager::PrintToPdf(
return;
}
+ absl::variant<printing::PageRanges, PageRangeError> parsed_ranges =
+ TextPageRangesToPageRanges(page_ranges);
+ if (absl::holds_alternative<PageRangeError>(parsed_ranges)) {
+ PrintResult print_result;
+ switch (absl::get<PageRangeError>(parsed_ranges)) {
+ case PageRangeError::kSyntaxError:
+ print_result = PAGE_RANGE_SYNTAX_ERROR;
+ break;
+ case PageRangeError::kInvalidRange:
+ print_result = PAGE_RANGE_INVALID_RANGE;
+ break;
+ }
+ std::move(callback).Run(print_result,
+ base::MakeRefCounted<base::RefCountedString>());
+ return;
+ }
+
printing_rfh_ = rfh;
- page_ranges_ = page_ranges;
- ignore_invalid_page_ranges_ = ignore_invalid_page_ranges;
- print_pages_params_ = std::move(print_pages_params);
- set_cookie(print_pages_params_->params->document_cookie);
+ print_pages_params->pages = absl::get<printing::PageRanges>(parsed_ranges);
+ set_cookie(print_pages_params->params->document_cookie);
callback_ = std::move(callback);
- GetPrintRenderFrame(rfh)->PrintRequestedPages();
+ GetPrintRenderFrame(rfh)->PrintWithParams(std::move(print_pages_params));
}
void PdfPrintManager::GetDefaultPrintSettings(
GetDefaultPrintSettingsCallback callback) {
- if (!printing_rfh_) {
- DLOG(ERROR) << "Unexpected message received before PrintToPdf is "
- "called: GetDefaultPrintSettings";
- std::move(callback).Run(printing::mojom::PrintParams::New());
- return;
- }
- std::move(callback).Run(print_pages_params_->params->Clone());
+ DLOG(ERROR) << "Scripted print is not supported";
+ std::move(callback).Run(printing::mojom::PrintParams::New());
}
void PdfPrintManager::ScriptedPrint(
@@ -135,50 +146,20 @@ void PdfPrintManager::ScriptedPrint(
ScriptedPrintCallback callback) {
auto default_param = printing::mojom::PrintPagesParams::New();
default_param->params = printing::mojom::PrintParams::New();
- if (!printing_rfh_) {
- DLOG(ERROR) << "Unexpected message received before PrintToPdf is "
- "called: ScriptedPrint";
- std::move(callback).Run(std::move(default_param));
- return;
- }
- if (params->is_scripted &&
- GetCurrentTargetFrame()->IsNestedWithinFencedFrame()) {
- DLOG(ERROR) << "Unexpected message received. Script Print is not allowed"
- " in a fenced frame.";
- std::move(callback).Run(std::move(default_param));
- return;
- }
- absl::variant<printing::PageRanges, PageRangeError> page_ranges =
- TextPageRangesToPageRanges(page_ranges_, ignore_invalid_page_ranges_,
- params->expected_pages_count);
- if (absl::holds_alternative<PageRangeError>(page_ranges)) {
- PrintResult print_result;
- switch (absl::get<PageRangeError>(page_ranges)) {
- case PageRangeError::SYNTAX_ERROR:
- print_result = PAGE_RANGE_SYNTAX_ERROR;
- break;
- case PageRangeError::LIMIT_ERROR:
- print_result = PAGE_COUNT_EXCEEDED;
- break;
- }
- ReleaseJob(print_result);
- std::move(callback).Run(std::move(default_param));
- return;
- }
-
- DCHECK(absl::holds_alternative<printing::PageRanges>(page_ranges));
- print_pages_params_->pages = printing::PageRange::GetPages(
- absl::get<printing::PageRanges>(page_ranges));
-
- std::move(callback).Run(print_pages_params_->Clone());
+ DLOG(ERROR) << "Scripted print is not supported";
+ std::move(callback).Run(std::move(default_param));
}
void PdfPrintManager::ShowInvalidPrinterSettingsError() {
ReleaseJob(INVALID_PRINTER_SETTINGS);
}
-void PdfPrintManager::PrintingFailed(int32_t cookie) {
- ReleaseJob(PRINTING_FAILED);
+void PdfPrintManager::PrintingFailed(
+ int32_t cookie,
+ printing::mojom::PrintFailureReason reason) {
+ ReleaseJob(reason == printing::mojom::PrintFailureReason::kInvalidPageRange
+ ? PAGE_COUNT_EXCEEDED
+ : PRINTING_FAILED);
}
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
@@ -273,7 +254,6 @@ void PdfPrintManager::DidPrintDocument(
void PdfPrintManager::Reset() {
printing_rfh_ = nullptr;
callback_.Reset();
- print_pages_params_.reset();
data_.clear();
}
diff --git a/chromium/components/printing/browser/print_to_pdf/pdf_print_manager.h b/chromium/components/printing/browser/print_to_pdf/pdf_print_manager.h
index b6e655291b4..bb2508e2c6e 100644
--- a/chromium/components/printing/browser/print_to_pdf/pdf_print_manager.h
+++ b/chromium/components/printing/browser/print_to_pdf/pdf_print_manager.h
@@ -34,6 +34,7 @@ class PdfPrintManager : public printing::PrintManager,
METAFILE_GET_DATA_ERROR,
SIMULTANEOUS_PRINT_ACTIVE,
PAGE_RANGE_SYNTAX_ERROR,
+ PAGE_RANGE_INVALID_RANGE,
PAGE_COUNT_EXCEEDED,
};
@@ -55,7 +56,6 @@ class PdfPrintManager : public printing::PrintManager,
void PrintToPdf(content::RenderFrameHost* rfh,
const std::string& page_ranges,
- bool ignore_invalid_page_ranges,
printing::mojom::PrintPagesParamsPtr print_page_params,
PrintToPdfCallback callback);
@@ -74,7 +74,8 @@ class PdfPrintManager : public printing::PrintManager,
void ScriptedPrint(printing::mojom::ScriptedPrintParamsPtr params,
ScriptedPrintCallback callback) override;
void ShowInvalidPrinterSettingsError() override;
- void PrintingFailed(int32_t cookie) override;
+ void PrintingFailed(int32_t cookie,
+ printing::mojom::PrintFailureReason reason) override;
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
void UpdatePrintSettings(int32_t cookie,
base::Value::Dict job_settings,
@@ -101,9 +102,6 @@ class PdfPrintManager : public printing::PrintManager,
void ReleaseJob(PrintResult result);
raw_ptr<content::RenderFrameHost> printing_rfh_ = nullptr;
- std::string page_ranges_;
- bool ignore_invalid_page_ranges_ = false;
- printing::mojom::PrintPagesParamsPtr print_pages_params_;
PrintToPdfCallback callback_;
std::string data_;
diff --git a/chromium/components/printing/browser/print_to_pdf/pdf_print_utils.cc b/chromium/components/printing/browser/print_to_pdf/pdf_print_utils.cc
index ef1d32d233a..cf6a92ca3f7 100644
--- a/chromium/components/printing/browser/print_to_pdf/pdf_print_utils.cc
+++ b/chromium/components/printing/browser/print_to_pdf/pdf_print_utils.cc
@@ -30,9 +30,7 @@ static constexpr double kDefaultMarginInInches =
} // namespace
absl::variant<printing::PageRanges, PageRangeError> TextPageRangesToPageRanges(
- base::StringPiece page_range_text,
- bool ignore_invalid_page_ranges,
- uint32_t expected_page_count) {
+ base::StringPiece page_range_text) {
printing::PageRanges page_ranges;
for (const auto& range_string :
base::SplitStringPiece(page_range_text, ",", base::TRIM_WHITESPACE,
@@ -40,49 +38,41 @@ absl::variant<printing::PageRanges, PageRangeError> TextPageRangesToPageRanges(
printing::PageRange range;
if (range_string.find("-") == base::StringPiece::npos) {
if (!base::StringToUint(range_string, &range.from))
- return PageRangeError::SYNTAX_ERROR;
+ return PageRangeError::kSyntaxError;
range.to = range.from;
} else if (range_string == "-") {
range.from = 1;
- range.to = expected_page_count;
+ // Set last page to max value so it gets capped with actual
+ // page count once it becomes known in renderer during printing.
+ static_assert(printing::PageRange::kMaxPage <
+ std::numeric_limits<uint32_t>::max());
+ range.to = printing::PageRange::kMaxPage + 1;
} else if (base::StartsWith(range_string, "-")) {
range.from = 1;
if (!base::StringToUint(range_string.substr(1), &range.to))
- return PageRangeError::SYNTAX_ERROR;
+ return PageRangeError::kSyntaxError;
} else if (base::EndsWith(range_string, "-")) {
- range.to = expected_page_count;
+ // See comment regarding kMaxPage above.
+ range.to = printing::PageRange::kMaxPage + 1;
if (!base::StringToUint(range_string.substr(0, range_string.length() - 1),
&range.from)) {
- return PageRangeError::SYNTAX_ERROR;
+ return PageRangeError::kSyntaxError;
}
} else {
auto tokens = base::SplitStringPiece(
range_string, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (tokens.size() != 2 || !base::StringToUint(tokens[0], &range.from) ||
!base::StringToUint(tokens[1], &range.to)) {
- return PageRangeError::SYNTAX_ERROR;
+ return PageRangeError::kSyntaxError;
}
}
- if (range.from < 1 || range.from > range.to) {
- if (!ignore_invalid_page_ranges)
- return PageRangeError::SYNTAX_ERROR;
- continue;
- }
- if (range.from > expected_page_count) {
- if (!ignore_invalid_page_ranges)
- return PageRangeError::LIMIT_ERROR;
- continue;
- }
-
- if (range.to > expected_page_count)
- range.to = expected_page_count;
+ if (range.from < 1 || range.from > range.to)
+ return PageRangeError::kInvalidRange;
// Page numbers are 1-based in the dictionary.
// Page numbers are 0-based for the print settings.
- range.from--;
- range.to--;
- page_ranges.push_back(range);
+ page_ranges.push_back({range.from - 1, range.to - 1});
}
return page_ranges;
}
diff --git a/chromium/components/printing/browser/print_to_pdf/pdf_print_utils.h b/chromium/components/printing/browser/print_to_pdf/pdf_print_utils.h
index ae9aa963539..3d7d435acf7 100644
--- a/chromium/components/printing/browser/print_to_pdf/pdf_print_utils.h
+++ b/chromium/components/printing/browser/print_to_pdf/pdf_print_utils.h
@@ -16,17 +16,12 @@
namespace print_to_pdf {
-enum class PageRangeError {
- SYNTAX_ERROR,
- LIMIT_ERROR,
-};
+enum class PageRangeError { kSyntaxError, kInvalidRange };
// Converts textual representation of the page range to printing::PageRanges,
// page range error is returned as the PageRangeError variant case.
absl::variant<printing::PageRanges, PageRangeError> TextPageRangesToPageRanges(
- base::StringPiece page_range_text,
- bool ignore_invalid_page_ranges,
- uint32_t expected_page_count);
+ base::StringPiece page_range_text);
// Converts print settings to printing::mojom::PrintPagesParamsPtr,
// document error is returned as the string variant case.
diff --git a/chromium/components/printing/browser/print_to_pdf/pdf_print_utils_unittest.cc b/chromium/components/printing/browser/print_to_pdf/pdf_print_utils_unittest.cc
index e2204a5f9fc..e0778702016 100644
--- a/chromium/components/printing/browser/print_to_pdf/pdf_print_utils_unittest.cc
+++ b/chromium/components/printing/browser/print_to_pdf/pdf_print_utils_unittest.cc
@@ -3,94 +3,56 @@
// found in the LICENSE file.
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
+
+#include "printing/page_number.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace print_to_pdf {
namespace {
-std::vector<uint32_t> GetPages(
- absl::variant<printing::PageRanges, PageRangeError> result) {
- return printing::PageRange::GetPages(absl::get<printing::PageRanges>(result));
-}
-
-PageRangeError GetError(
- absl::variant<printing::PageRanges, PageRangeError> result) {
- return absl::get<PageRangeError>(result);
-}
+using printing::PageRange;
+using printing::PageRanges;
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+using testing::VariantWith;
TEST(PageRangeTextToPagesTest, General) {
- absl::variant<printing::PageRanges, PageRangeError> result;
- std::vector<uint32_t> expected_pages;
-
- // "-" is the full range of pages.
- result = TextPageRangesToPageRanges("-", false, 10);
- EXPECT_TRUE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_FALSE(absl::holds_alternative<PageRangeError>(result));
- expected_pages = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- EXPECT_EQ(GetPages(result), expected_pages);
+ EXPECT_THAT(
+ TextPageRangesToPageRanges("-"),
+ VariantWith<PageRanges>(ElementsAre(PageRange{0, PageRange::kMaxPage})));
// If no start page is specified, we start at the first page.
- result = TextPageRangesToPageRanges("-5", false, 10);
- EXPECT_TRUE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_FALSE(absl::holds_alternative<PageRangeError>(result));
- expected_pages = {0, 1, 2, 3, 4};
- EXPECT_EQ(GetPages(result), expected_pages);
+ EXPECT_THAT(TextPageRangesToPageRanges("-5"),
+ VariantWith<PageRanges>(ElementsAre(PageRange{0, 4})));
// If no end page is specified, we end at the last page.
- result = TextPageRangesToPageRanges("5-", false, 10);
- EXPECT_TRUE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_FALSE(absl::holds_alternative<PageRangeError>(result));
- expected_pages = {4, 5, 6, 7, 8, 9};
- EXPECT_EQ(GetPages(result), expected_pages);
+ EXPECT_THAT(
+ TextPageRangesToPageRanges("5-"),
+ VariantWith<PageRanges>(ElementsAre(PageRange{4, PageRange::kMaxPage})));
// Multiple ranges are separated by commas.
- result = TextPageRangesToPageRanges("1-3,9-10,4-6", false, 10);
- EXPECT_TRUE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_FALSE(absl::holds_alternative<PageRangeError>(result));
- expected_pages = {0, 1, 2, 3, 4, 5, 8, 9};
- EXPECT_EQ(GetPages(result), expected_pages);
+ EXPECT_THAT(TextPageRangesToPageRanges("1-3,9-10,4-6"),
+ VariantWith<PageRanges>(
+ ElementsAreArray<PageRange>({{0, 2}, {8, 9}, {3, 5}})));
// White space is ignored.
- result = TextPageRangesToPageRanges("1- 3, 9-10,4 -6", false, 10);
- EXPECT_TRUE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_FALSE(absl::holds_alternative<PageRangeError>(result));
- expected_pages = {0, 1, 2, 3, 4, 5, 8, 9};
- EXPECT_EQ(GetPages(result), expected_pages);
-
- // End page beyond number of pages is supported and capped to number
- // of pages.
- result = TextPageRangesToPageRanges("1-10", false, 5);
- EXPECT_TRUE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_FALSE(absl::holds_alternative<PageRangeError>(result));
- expected_pages = {0, 1, 2, 3, 4};
- EXPECT_EQ(GetPages(result), expected_pages);
-
- // Start page beyond number of pages results in an error.
- result = TextPageRangesToPageRanges("1-3,9-10,4-6", false, 5);
- EXPECT_FALSE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_TRUE(absl::holds_alternative<PageRangeError>(result));
- EXPECT_EQ(GetError(result), PageRangeError::LIMIT_ERROR);
+ EXPECT_THAT(TextPageRangesToPageRanges("1- 3, 9-10,4 -6"),
+ VariantWith<PageRanges>(
+ ElementsAreArray<PageRange>({{0, 2}, {8, 9}, {3, 5}})));
- // Invalid page ranges are ignored if |ignore_invalid_page_ranges| is
- // in effect.
- result = TextPageRangesToPageRanges("9-10,4-6,3-1", true, 5);
- EXPECT_TRUE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_FALSE(absl::holds_alternative<PageRangeError>(result));
- expected_pages = {3, 4};
- EXPECT_EQ(GetPages(result), expected_pages);
+ // Range with a start page greater than the end page results in an error.
+ EXPECT_THAT(TextPageRangesToPageRanges("6-4"),
+ VariantWith<PageRangeError>(PageRangeError::kInvalidRange));
// Invalid input results in an error.
- result = TextPageRangesToPageRanges("abcd", false, 10);
- EXPECT_FALSE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_TRUE(absl::holds_alternative<PageRangeError>(result));
- EXPECT_EQ(GetError(result), PageRangeError::SYNTAX_ERROR);
+ EXPECT_THAT(TextPageRangesToPageRanges("abcd"),
+ VariantWith<PageRangeError>(PageRangeError::kSyntaxError));
// Invalid input results in an error.
- result = TextPageRangesToPageRanges("1-3,9-a10,4-6", false, 10);
- EXPECT_FALSE(absl::holds_alternative<printing::PageRanges>(result));
- EXPECT_TRUE(absl::holds_alternative<PageRangeError>(result));
- EXPECT_EQ(GetError(result), PageRangeError::SYNTAX_ERROR);
+ EXPECT_THAT(TextPageRangesToPageRanges("1-3,9-a10,4-6"),
+ VariantWith<PageRangeError>(PageRangeError::kSyntaxError));
}
} // namespace
diff --git a/chromium/components/printing/common/print.mojom b/chromium/components/printing/common/print.mojom
index 156b987aa9c..f2c17a8fecc 100644
--- a/chromium/components/printing/common/print.mojom
+++ b/chromium/components/printing/common/print.mojom
@@ -173,7 +173,7 @@ struct PrintPagesParams {
// value for all the document.
PrintParams params;
// If empty, this means a request to render all the printed pages.
- array<uint32> pages;
+ array<PageRange> pages;
};
// Parameters to describe a rendered page.
@@ -209,7 +209,7 @@ interface PrintRenderer {
// The returned |preview_document_region| contains the preview document data
// as a flattened PDF. It will be invalid if errors occurred while rendering
// the preview document.
- CreatePreviewDocument(mojo_base.mojom.DeprecatedDictionaryValue job_settings)
+ CreatePreviewDocument(mojo_base.mojom.DictionaryValue job_settings)
=> (mojo_base.mojom.ReadOnlySharedMemoryRegion? preview_document_region);
};
@@ -270,6 +270,11 @@ interface PrintPreviewUI {
DidStartPreview(DidStartPreviewParams params, int32 request_id);
};
+enum PrintFailureReason {
+ kGeneralFailure,
+ kInvalidPageRange,
+};
+
// Render process interface exposed to the browser to handle most of the
// printing grunt work for RenderView.
interface PrintRenderFrame {
@@ -277,6 +282,15 @@ interface PrintRenderFrame {
// requested page, and then switch back the CSS to display media type.
PrintRequestedPages();
+ // Requests the frame to be printed with specified parameters. This is used
+ // to programmatically produce PDF by request from the browser (e.g. over
+ // DevTools protocol) and, unlike other printing code paths, is not designed
+ // around the legacy protocol between renderer and the browser to support
+ // interactive print preview -- the final print settings are supplied by
+ // the caller and printing is completed without further round-trips to the
+ // browser.
+ PrintWithParams(PrintPagesParams params);
+
// Tells the RenderFrame to switch the CSS to print media type, render every
// requested page using the print preview document's frame/node, and then
// switch the CSS back to display media type.
@@ -297,7 +311,7 @@ interface PrintRenderFrame {
// every requested page for print preview using the given |settings|. This
// gets called multiple times as the user updates settings.
[EnableIf=enable_print_preview]
- PrintPreview(mojo_base.mojom.DeprecatedDictionaryValue settings);
+ PrintPreview(mojo_base.mojom.DictionaryValue settings);
// Tells the RenderFrame that the print preview dialog was closed.
[EnableIf=enable_print_preview]
@@ -354,7 +368,7 @@ interface PrintManagerHost {
ShowInvalidPrinterSettingsError();
// Tells the browser printing failed.
- PrintingFailed(int32 cookie);
+ PrintingFailed(int32 cookie, PrintFailureReason reason);
// Update the current print settings with new |job_settings|.
[EnableIf=enable_print_preview, Sync]
diff --git a/chromium/components/printing/renderer/print_render_frame_helper.cc b/chromium/components/printing/renderer/print_render_frame_helper.cc
index e0ecf8f3f09..66ac411e4a7 100644
--- a/chromium/components/printing/renderer/print_render_frame_helper.cc
+++ b/chromium/components/printing/renderer/print_render_frame_helper.cc
@@ -25,6 +25,7 @@
#include "base/numerics/safe_conversions.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
+#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
@@ -36,10 +37,10 @@
#include "content/public/renderer/render_thread.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "net/base/escape.h"
#include "printing/buildflags/buildflags.h"
#include "printing/metafile_skia.h"
#include "printing/mojom/print.mojom.h"
+#include "printing/page_number.h"
#include "printing/print_job_constants.h"
#include "printing/units.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
@@ -314,18 +315,18 @@ void EnsureOrientationMatches(const mojom::PrintParams& css_params,
page_params->printable_area.width()));
}
-void ComputeWebKitPrintParamsInDesiredDpi(
+blink::WebPrintParams ComputeWebKitPrintParamsInDesiredDpi(
const mojom::PrintParams& print_params,
- bool source_is_pdf,
- blink::WebPrintParams* webkit_print_params) {
+ bool source_is_pdf) {
+ blink::WebPrintParams webkit_print_params;
int dpi = GetDPI(print_params);
- webkit_print_params->printer_dpi = dpi;
+ webkit_print_params.printer_dpi = dpi;
if (source_is_pdf) {
// The |scale_factor| in print_params comes from the |scale_factor| in
// PrintSettings, which converts an integer percentage between 10 and 200
// to a float in PrintSettingsFromJobSettings. As a result, it can be
// converted back safely for the integer |scale_factor| in WebPrintParams.
- webkit_print_params->scale_factor =
+ webkit_print_params.scale_factor =
static_cast<int>(print_params.scale_factor * 100);
#if BUILDFLAG(IS_APPLE)
@@ -333,31 +334,33 @@ void ComputeWebKitPrintParamsInDesiredDpi(
// correct except when rastering PDFs, which uses |printer_dpi|, and the
// value for |printer_dpi| is too low. Adjust that here.
// See https://crbug.com/943462
- webkit_print_params->printer_dpi = kDefaultPdfDpi;
+ webkit_print_params.printer_dpi = kDefaultPdfDpi;
#endif
if (print_params.rasterize_pdf && print_params.rasterize_pdf_dpi > 0)
- webkit_print_params->printer_dpi = print_params.rasterize_pdf_dpi;
+ webkit_print_params.printer_dpi = print_params.rasterize_pdf_dpi;
}
- webkit_print_params->rasterize_pdf = print_params.rasterize_pdf;
- webkit_print_params->print_scaling_option = print_params.print_scaling_option;
+ webkit_print_params.rasterize_pdf = print_params.rasterize_pdf;
+ webkit_print_params.print_scaling_option = print_params.print_scaling_option;
- webkit_print_params->print_content_area.set_size(gfx::Size(
+ webkit_print_params.print_content_area.set_size(gfx::Size(
ConvertUnit(print_params.content_size.width(), dpi, kPointsPerInch),
ConvertUnit(print_params.content_size.height(), dpi, kPointsPerInch)));
- webkit_print_params->printable_area = gfx::Rect(
+ webkit_print_params.printable_area = gfx::Rect(
ConvertUnit(print_params.printable_area.x(), dpi, kPointsPerInch),
ConvertUnit(print_params.printable_area.y(), dpi, kPointsPerInch),
ConvertUnit(print_params.printable_area.width(), dpi, kPointsPerInch),
ConvertUnit(print_params.printable_area.height(), dpi, kPointsPerInch));
- webkit_print_params->paper_size = gfx::Size(
+ webkit_print_params.paper_size = gfx::Size(
ConvertUnit(print_params.page_size.width(), dpi, kPointsPerInch),
ConvertUnit(print_params.page_size.height(), dpi, kPointsPerInch));
// The following settings is for N-up mode.
- webkit_print_params->pages_per_sheet = print_params.pages_per_sheet;
+ webkit_print_params.pages_per_sheet = print_params.pages_per_sheet;
+
+ return webkit_print_params;
}
bool IsPrintingPdfFrame(blink::WebLocalFrame* frame,
@@ -1068,8 +1071,8 @@ void PrepareFrameAndViewForPrint::ComputeScalingAndPrintParams(
bool is_pdf,
bool ignore_css_margins,
bool fit_to_page) {
- ComputeWebKitPrintParamsInDesiredDpi(*print_params, is_pdf,
- &web_print_params_);
+ web_print_params_ =
+ ComputeWebKitPrintParamsInDesiredDpi(*print_params, is_pdf);
frame->PrintBegin(web_print_params_, node_to_print_);
double scale_factor = PrintRenderFrameHelper::GetScaleFactor(
print_params->scale_factor, is_pdf);
@@ -1079,8 +1082,8 @@ void PrepareFrameAndViewForPrint::ComputeScalingAndPrintParams(
if (selection)
*selection = frame->SelectionAsMarkup().Utf8();
frame->PrintEnd();
- ComputeWebKitPrintParamsInDesiredDpi(*print_params, is_pdf,
- &web_print_params_);
+ web_print_params_ =
+ ComputeWebKitPrintParamsInDesiredDpi(*print_params, is_pdf);
}
void PrepareFrameAndViewForPrint::DidStopLoading() {
@@ -1330,6 +1333,41 @@ void PrintRenderFrameHelper::PrintRequestedPages() {
// just return.
}
+void PrintRenderFrameHelper::PrintWithParams(
+ mojom::PrintPagesParamsPtr settings) {
+ DCHECK(!settings->params->dpi.IsEmpty());
+ DCHECK(settings->params->document_cookie);
+
+ ScopedIPC scoped_ipc(weak_ptr_factory_.GetWeakPtr());
+ if (ipc_nesting_level_ > kAllowedIpcDepthForPrint)
+ return;
+
+ blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+ frame->DispatchBeforePrintEvent(/*print_client=*/nullptr);
+ // Don't print if the RenderFrame is gone.
+ if (render_frame_gone_)
+ return;
+
+ // If we are printing a frame with an internal PDF plugin element, find the
+ // plugin node and print that instead.
+ auto plugin_node = delegate_->GetPdfElement(frame);
+
+ // TODO(caseq): have this logic on the caller side?
+ const bool fit_to_paper = !IsPrintingPdfFrame(frame, plugin_node);
+ settings->params->print_scaling_option =
+ fit_to_paper && !settings->params->prefer_css_page_size
+ ? mojom::PrintScalingOption::kFitToPrintableArea
+ : mojom::PrintScalingOption::kSourceSize;
+ SetPrintPagesParams(*settings);
+ prep_frame_view_ = std::make_unique<PrepareFrameAndViewForPrint>(
+ *settings->params, frame, plugin_node, /* ignore_css_margins=*/false);
+ PrintPages();
+ FinishFramePrinting();
+
+ if (!render_frame_gone_)
+ frame->DispatchAfterPrintEvent();
+}
+
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
void PrintRenderFrameHelper::PrintForSystemDialog() {
ScopedIPC scoped_ipc(weak_ptr_factory_.GetWeakPtr());
@@ -1397,7 +1435,7 @@ void PrintRenderFrameHelper::InitiatePrintPreview(
/*already_notified_frame=*/false);
}
-void PrintRenderFrameHelper::PrintPreview(base::Value settings) {
+void PrintRenderFrameHelper::PrintPreview(base::Value::Dict settings) {
ScopedIPC scoped_ipc(weak_ptr_factory_.GetWeakPtr());
if (ipc_nesting_level_ > kAllowedIpcDepthForPrint)
return;
@@ -1417,8 +1455,7 @@ void PrintRenderFrameHelper::PrintPreview(base::Value settings) {
}
if (!UpdatePrintSettings(print_preview_context_.source_frame(),
- print_preview_context_.source_node(),
- settings.GetDict())) {
+ print_preview_context_.source_node(), settings)) {
DidFinishPrinting(INVALID_SETTINGS);
return;
}
@@ -1567,9 +1604,8 @@ void PrintRenderFrameHelper::SnapshotForContentAnalysis(
blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
blink::WebNode node = delegate_->GetPdfElement(frame);
bool is_pdf = IsPrintingPdfFrame(frame, node);
- blink::WebPrintParams web_print_params;
- ComputeWebKitPrintParamsInDesiredDpi(*print_pages_params.params, is_pdf,
- &web_print_params);
+ blink::WebPrintParams web_print_params =
+ ComputeWebKitPrintParamsInDesiredDpi(*print_pages_params.params, is_pdf);
uint32_t page_count = frame->PrintBegin(web_print_params, node);
if (page_count == 0) {
frame->PrintEnd();
@@ -1692,7 +1728,6 @@ PrintRenderFrameHelper::CreatePreviewDocument() {
#endif
const mojom::PrintParams& print_params = *print_pages_params_->params;
- const std::vector<uint32_t>& pages = print_pages_params_->pages;
bool require_document_metafile =
print_params.printed_doc_type != mojom::SkiaDocumentType::kMSKP;
@@ -1701,11 +1736,19 @@ PrintRenderFrameHelper::CreatePreviewDocument() {
#endif
if (!print_preview_context_.CreatePreviewDocument(
- std::move(prep_frame_view_), pages, print_params.printed_doc_type,
- print_params.document_cookie, require_document_metafile)) {
+ std::move(prep_frame_view_), print_pages_params_->pages,
+ print_params.printed_doc_type, print_params.document_cookie,
+ require_document_metafile)) {
return CREATE_FAIL;
}
+ // If tagged PDF exporting is enabled, we also need to capture an
+ // accessibility tree. AXTreeSnapshotter should stay alive through the end of
+ // the scope of printing, because text drawing commands are only annotated
+ // with a DOMNodeId if accessibility is enabled.
+ if (delegate_->ShouldGenerateTaggedPDF())
+ snapshotter_ = render_frame()->CreateAXTreeSnapshotter(ui::AXMode::kPDF);
+
double scale_factor =
GetScaleFactor(print_params.scale_factor,
/*is_pdf=*/!print_preview_context_.IsModifiable());
@@ -2097,9 +2140,13 @@ void PrintRenderFrameHelper::DidFinishPrinting(PrintingResult result) {
DCHECK(!notify_browser_of_print_failure_);
break;
+ case INVALID_PAGE_RANGE:
case FAIL_PRINT:
if (notify_browser_of_print_failure_ && print_pages_params_) {
- GetPrintManagerHost()->PrintingFailed(cookie);
+ GetPrintManagerHost()->PrintingFailed(
+ cookie, result == INVALID_PAGE_RANGE
+ ? mojom::PrintFailureReason::kInvalidPageRange
+ : mojom::PrintFailureReason::kGeneralFailure);
}
break;
@@ -2148,38 +2195,35 @@ void PrintRenderFrameHelper::PrintPages() {
return DidFinishPrinting(FAIL_PRINT);
}
- const mojom::PrintPagesParams& params = *print_pages_params_;
- const mojom::PrintParams& print_params = *params.params;
-
// TODO(vitalybuka): should be page_count or valid pages from params.pages.
// See http://crbug.com/161576
- GetPrintManagerHost()->DidGetPrintedPagesCount(print_params.document_cookie,
- page_count);
-
- if (print_params.preview_ui_id < 0) {
- // Printing for system dialog.
- int printed_count = params.pages.empty() ? page_count : params.pages.size();
- base::UmaHistogramCounts1M("PrintPreview.PageCount.SystemDialog",
- printed_count);
- }
-
- bool is_pdf =
- IsPrintingPdfFrame(prep_frame_view_->frame(), prep_frame_view_->node());
- if (!PrintPagesNative(prep_frame_view_->frame(), page_count, is_pdf)) {
+ GetPrintManagerHost()->DidGetPrintedPagesCount(
+ print_pages_params_->params->document_cookie, page_count);
+
+ std::vector<uint32_t> pages_to_print =
+ PageNumber::GetPages(print_pages_params_->pages, page_count);
+ if (pages_to_print.empty())
+ return DidFinishPrinting(INVALID_PAGE_RANGE);
+ if (!PrintPagesNative(prep_frame_view_->frame(), page_count,
+ pages_to_print)) {
LOG(ERROR) << "Printing failed.";
return DidFinishPrinting(FAIL_PRINT);
}
}
-bool PrintRenderFrameHelper::PrintPagesNative(blink::WebLocalFrame* frame,
- uint32_t page_count,
- bool is_pdf) {
+bool PrintRenderFrameHelper::PrintPagesNative(
+ blink::WebLocalFrame* frame,
+ uint32_t page_count,
+ const std::vector<uint32_t>& printed_pages) {
const mojom::PrintPagesParams& params = *print_pages_params_;
const mojom::PrintParams& print_params = *params.params;
- std::vector<uint32_t> printed_pages = GetPrintedPages(params, page_count);
- if (printed_pages.empty())
- return false;
+ DCHECK(!printed_pages.empty());
+ if (print_params.preview_ui_id < 0) {
+ // Printing for system dialog.
+ base::UmaHistogramCounts1M("PrintPreview.PageCount.SystemDialog",
+ printed_pages.size());
+ }
ContentProxySet typeface_content_info;
MetafileSkia metafile(print_params.printed_doc_type,
@@ -2214,6 +2258,8 @@ bool PrintRenderFrameHelper::PrintPagesNative(blink::WebLocalFrame* frame,
page_size_in_dpi = nullptr;
content_area_in_dpi = nullptr;
#endif
+ bool is_pdf =
+ IsPrintingPdfFrame(prep_frame_view_->frame(), prep_frame_view_->node());
PrintPageInternal(print_params, printed_pages[0], page_count,
GetScaleFactor(print_params.scale_factor, is_pdf), frame,
&metafile, page_size_in_dpi, content_area_in_dpi);
@@ -2262,25 +2308,6 @@ PrintRenderFrameHelper::ComputePageLayoutInPointsForCss(
return CalculatePageLayoutFromPrintParams(*params, input_scale_factor);
}
-// static - Not anonymous so that platform implementations can use it.
-std::vector<uint32_t> PrintRenderFrameHelper::GetPrintedPages(
- const mojom::PrintPagesParams& params,
- uint32_t page_count) {
- std::vector<uint32_t> printed_pages;
- if (params.pages.empty()) {
- for (uint32_t i = 0; i < page_count; ++i) {
- printed_pages.push_back(i);
- }
- } else {
- for (uint32_t page : params.pages) {
- if (page != kInvalidPageIndex && page < page_count) {
- printed_pages.push_back(page);
- }
- }
- }
- return printed_pages;
-}
-
void PrintRenderFrameHelper::IPCReceived() {
// The class is not designed to handle recursive messages. This is not
// expected during regular flow. However, during rendering of content for
@@ -2606,13 +2633,6 @@ void PrintRenderFrameHelper::RequestPrintPreview(PrintPreviewRequestType type,
const bool is_modifiable = print_preview_context_.IsModifiable();
const bool has_selection = print_preview_context_.HasSelection();
- // If tagged PDF exporting is enabled, we also need to capture an
- // accessibility tree. AXTreeSnapshotter should stay alive through the end of
- // the scope of printing, because text drawing commands are only annotated
- // with a DOMNodeId if accessibility is enabled.
- if (delegate_->ShouldGenerateTaggedPDF())
- snapshotter_ = render_frame()->CreateAXTreeSnapshotter(ui::AXMode::kPDF);
-
auto params = mojom::RequestPrintPreviewParams::New();
#if BUILDFLAG(IS_CHROMEOS_ASH)
params->is_from_arc = is_from_arc;
@@ -2818,7 +2838,7 @@ void PrintRenderFrameHelper::PrintPreviewContext::OnPrintPreview() {
bool PrintRenderFrameHelper::PrintPreviewContext::CreatePreviewDocument(
std::unique_ptr<PrepareFrameAndViewForPrint> prepared_frame,
- const std::vector<uint32_t>& pages,
+ const PageRanges& pages,
mojom::SkiaDocumentType doc_type,
int document_cookie,
bool require_document_metafile) {
@@ -2843,24 +2863,14 @@ bool PrintRenderFrameHelper::PrintPreviewContext::CreatePreviewDocument(
}
current_page_index_ = 0;
- pages_to_render_ = pages;
- // Sort and make unique.
- std::sort(pages_to_render_.begin(), pages_to_render_.end());
- pages_to_render_.resize(
- std::unique(pages_to_render_.begin(), pages_to_render_.end()) -
- pages_to_render_.begin());
- // Remove invalid pages.
- pages_to_render_.resize(std::lower_bound(pages_to_render_.begin(),
- pages_to_render_.end(),
- total_page_count_) -
- pages_to_render_.begin());
-
- if (pages_to_render_.empty()) {
- // Render all pages.
- pages_to_render_.reserve(total_page_count_);
- for (uint32_t i = 0; i < total_page_count_; ++i)
- pages_to_render_.push_back(i);
- }
+ pages_to_render_ = PageNumber::GetPages(pages, total_page_count_);
+ // If preview settings along with specified ranges resulted in 0 pages,
+ // (e.g. page "2" with a document of a single page), print the entire
+ // document. This is a legacy behavior that only makes sense for preview,
+ // where the client expects that and will adjust page ranges based on
+ // actual document returned.
+ if (pages_to_render_.empty())
+ pages_to_render_ = PageNumber::GetPages({}, total_page_count_);
print_ready_metafile_page_count_ = pages_to_render_.size();
document_render_time_ = base::TimeDelta();
diff --git a/chromium/components/printing/renderer/print_render_frame_helper.h b/chromium/components/printing/renderer/print_render_frame_helper.h
index 15c367bd667..f118cae62de 100644
--- a/chromium/components/printing/renderer/print_render_frame_helper.h
+++ b/chromium/components/printing/renderer/print_render_frame_helper.h
@@ -190,6 +190,7 @@ class PrintRenderFrameHelper
OK,
FAIL_PRINT_INIT,
FAIL_PRINT,
+ INVALID_PAGE_RANGE,
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
FAIL_PREVIEW,
INVALID_SETTINGS,
@@ -255,6 +256,7 @@ class PrintRenderFrameHelper
// printing::mojom::PrintRenderFrame:
void PrintRequestedPages() override;
+ void PrintWithParams(mojom::PrintPagesParamsPtr params) override;
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
void PrintForSystemDialog() override;
void SetPrintPreviewUI(
@@ -262,7 +264,7 @@ class PrintRenderFrameHelper
void InitiatePrintPreview(
mojo::PendingAssociatedRemote<mojom::PrintRenderer> print_renderer,
bool has_selection) override;
- void PrintPreview(base::Value settings) override;
+ void PrintPreview(base::Value::Dict settings) override;
void OnPrintPreviewDialogClosed() override;
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
void PrintFrameContent(mojom::PrintFrameContentParamsPtr params,
@@ -367,7 +369,7 @@ class PrintRenderFrameHelper
void PrintPages();
bool PrintPagesNative(blink::WebLocalFrame* frame,
uint32_t page_count,
- bool is_pdf);
+ const std::vector<uint32_t>& pages_to_print);
void FinishFramePrinting();
// Render the frame for printing.
bool RenderPagesForPrint(blink::WebLocalFrame* frame,
@@ -410,12 +412,6 @@ class PrintRenderFrameHelper
bool ignore_css_margins,
double* scale_factor);
- // Return an array of pages to print given the print |params| and an expected
- // |page_count|. Page numbers are zero-based.
- static std::vector<uint32_t> GetPrintedPages(
- const mojom::PrintPagesParams& params,
- uint32_t page_count);
-
// Given the |device| and |canvas| to draw on, prints the appropriate headers
// and footers using strings from |header_footer_info| on to the canvas.
static void PrintHeaderAndFooter(
@@ -491,7 +487,7 @@ class PrintRenderFrameHelper
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Settings used by a PrintRenderer to create a preview document.
- base::Value print_renderer_job_settings_;
+ base::Value::Dict print_renderer_job_settings_;
// Used to render print documents from an external source (ARC, Crostini,
// etc.).
@@ -532,7 +528,7 @@ class PrintRenderFrameHelper
// Create the print preview document. |pages| is empty to print all pages.
bool CreatePreviewDocument(
std::unique_ptr<PrepareFrameAndViewForPrint> prepared_frame,
- const std::vector<uint32_t>& pages,
+ const PageRanges& pages,
mojom::SkiaDocumentType doc_type,
int document_cookie,
bool require_document_metafile);
diff --git a/chromium/components/privacy_sandbox/privacy_sandbox_features.cc b/chromium/components/privacy_sandbox/privacy_sandbox_features.cc
index b247aec0341..b6fb00b2767 100644
--- a/chromium/components/privacy_sandbox/privacy_sandbox_features.cc
+++ b/chromium/components/privacy_sandbox/privacy_sandbox_features.cc
@@ -14,8 +14,9 @@ const base::FeatureParam<bool> kPrivacySandboxSettings3ConsentRequired{
&kPrivacySandboxSettings3, "consent-required", false};
const base::FeatureParam<bool> kPrivacySandboxSettings3NoticeRequired{
&kPrivacySandboxSettings3, "notice-required", false};
-const base::FeatureParam<bool> kPrivacySandboxSettings3DisableDialogForTesting{
- &kPrivacySandboxSettings3, "disable-dialog-for-testing", false};
+const base::FeatureParam<bool> kPrivacySandboxSettings3NewNotice{
+ &kPrivacySandboxSettings3, "new-notice", false};
+
const base::FeatureParam<bool>
kPrivacySandboxSettings3ForceShowConsentForTesting{
&kPrivacySandboxSettings3, "force-show-consent-for-testing", false};
@@ -24,9 +25,14 @@ const base::FeatureParam<bool>
&kPrivacySandboxSettings3, "force-show-notice-for-testing", false};
const base::FeatureParam<bool> kPrivacySandboxSettings3ShowSampleDataForTesting{
&kPrivacySandboxSettings3, "show-sample-data", false};
+const base::FeatureParam<bool> kPrivacySandboxSettings3DisableDialogForTesting{
+ &kPrivacySandboxSettings3, "disable-dialog-for-testing", false};
const base::Feature kOverridePrivacySandboxSettingsLocalTesting{
"OverridePrivacySandboxSettingsLocalTesting",
base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kDisablePrivacySandboxPrompts{
+ "DisablePrivacySandboxPrompts", base::FEATURE_DISABLED_BY_DEFAULT};
+
} // namespace privacy_sandbox
diff --git a/chromium/components/privacy_sandbox/privacy_sandbox_features.h b/chromium/components/privacy_sandbox/privacy_sandbox_features.h
index 62e75e0f0c5..86cffbf026b 100644
--- a/chromium/components/privacy_sandbox/privacy_sandbox_features.h
+++ b/chromium/components/privacy_sandbox/privacy_sandbox_features.h
@@ -23,22 +23,34 @@ extern const base::FeatureParam<bool> kPrivacySandboxSettings3ConsentRequired;
// 3 APIs will become active. Only one of this and the above consent feature
// should be enabled at any one time.
extern const base::FeatureParam<bool> kPrivacySandboxSettings3NoticeRequired;
+// Determines whether the user will be shown a new version of the notice UI.
+// The notice will be shown only if `kPrivacySandboxSettings3NoticeRequired` is
+// true. This parameter only determines which UI version will be shown.
+extern const base::FeatureParam<bool> kPrivacySandboxSettings3NewNotice;
// Feature parameters which should exclusively be used for testing purposes.
// Enabling any of these parameters may result in the Privacy Sandbox prefs
// (unsynced) entering an unexpected state, requiring profile deletion to
// resolve.
extern const base::FeatureParam<bool>
- kPrivacySandboxSettings3DisableDialogForTesting;
-extern const base::FeatureParam<bool>
kPrivacySandboxSettings3ForceShowConsentForTesting;
extern const base::FeatureParam<bool>
kPrivacySandboxSettings3ForceShowNoticeForTesting;
extern const base::FeatureParam<bool>
kPrivacySandboxSettings3ShowSampleDataForTesting;
+// This parameter will suppress all Privacy Sandbox prompts, but is supersceeded
+// by the kDisablePrivacySandboxPrompts feature below, and will be removed when
+// the PrivacySandboxSettings3 feature is fully launched & solidified.
+extern const base::FeatureParam<bool>
+ kPrivacySandboxSettings3DisableDialogForTesting;
extern const base::Feature kOverridePrivacySandboxSettingsLocalTesting;
+// Disables any Privacy Sandbox related prompts. Should only be used for testing
+// purposes. This feature is used to support external automated testing using
+// Chrome, where additional prompts break behavior expectations.
+extern const base::Feature kDisablePrivacySandboxPrompts;
+
} // namespace privacy_sandbox
#endif // COMPONENTS_PRIVACY_SANDBOX_PRIVACY_SANDBOX_FEATURES_H_
diff --git a/chromium/components/privacy_sandbox/privacy_sandbox_settings.cc b/chromium/components/privacy_sandbox/privacy_sandbox_settings.cc
index 3f43dc20222..9eeffe9664c 100644
--- a/chromium/components/privacy_sandbox/privacy_sandbox_settings.cc
+++ b/chromium/components/privacy_sandbox/privacy_sandbox_settings.cc
@@ -338,6 +338,18 @@ std::vector<GURL> PrivacySandboxSettings::FilterFledgeAllowedParties(
return allowed_parties;
}
+bool PrivacySandboxSettings::IsSharedStorageAllowed(
+ const url::Origin& top_frame_origin,
+ const url::Origin& accessing_origin) const {
+ ContentSettingsForOneType cookie_settings;
+ cookie_settings_->GetCookieSettings(&cookie_settings);
+
+ // Ensures that Shared Storage is only allowed if both Privacy Sandbox is
+ // enabled and full cookie access is enabled for this context.
+ return IsPrivacySandboxEnabledForContext(accessing_origin.GetURL(),
+ top_frame_origin, cookie_settings);
+}
+
bool PrivacySandboxSettings::IsPrivacySandboxEnabled() const {
// If the delegate is restricting access, or indicates confirmation has not
// occurred, the Privacy Sandbox is disabled.
diff --git a/chromium/components/privacy_sandbox/privacy_sandbox_settings.h b/chromium/components/privacy_sandbox/privacy_sandbox_settings.h
index b1a4b3fc472..289b9c2cac5 100644
--- a/chromium/components/privacy_sandbox/privacy_sandbox_settings.h
+++ b/chromium/components/privacy_sandbox/privacy_sandbox_settings.h
@@ -155,6 +155,12 @@ class PrivacySandboxSettings : public KeyedService {
const url::Origin& top_frame_origin,
const std::vector<GURL>& auction_parties);
+ // Determines whether Shared Storage is allowable in a particular context.
+ // `top_frame_origin` can be the same as `accessing_origin` in the case of a
+ // top-level document calling Shared Storage.
+ bool IsSharedStorageAllowed(const url::Origin& top_frame_origin,
+ const url::Origin& accessing_origin) const;
+
// Returns whether the profile has the Privacy Sandbox enabled. This consults
// the main preference, as well as the delegate to check whether the sandbox
// is restricted, or has not been confirmed. It does not consider any cookie
diff --git a/chromium/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc b/chromium/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc
index b9246e3013f..d45cdd71f5e 100644
--- a/chromium/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc
+++ b/chromium/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc
@@ -141,6 +141,10 @@ TEST_P(PrivacySandboxSettingsTest, DefaultContentSettingBlockOverridePref) {
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+ EXPECT_FALSE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
+
// An allow default or exception, whether via user or policy, should not
// override the preference value.
privacy_sandbox_test_util::SetupTestState(
@@ -185,6 +189,10 @@ TEST_P(PrivacySandboxSettingsTest, DefaultContentSettingBlockOverridePref) {
url::Origin::Create(GURL("https://test.com")),
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+
+ EXPECT_FALSE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
}
TEST_P(PrivacySandboxSettingsTest, CookieExceptionsApply) {
@@ -225,6 +233,10 @@ TEST_P(PrivacySandboxSettingsTest, CookieExceptionsApply) {
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+ EXPECT_FALSE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
+
// The default managed content setting should apply, overriding any user ones,
// and disabling Topics calculations.
privacy_sandbox_test_util::SetupTestState(
@@ -263,6 +275,10 @@ TEST_P(PrivacySandboxSettingsTest, CookieExceptionsApply) {
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+ EXPECT_FALSE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
+
// Managed content setting exceptions should override both the privacy
// sandbox pref and any user settings.
privacy_sandbox_test_util::SetupTestState(
@@ -313,6 +329,10 @@ TEST_P(PrivacySandboxSettingsTest, CookieExceptionsApply) {
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+ EXPECT_FALSE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
+
// A less specific block exception should not override a more specific allow
// exception. The effective content setting in this scenario is still allow,
// even though a block exception exists.
@@ -377,6 +397,10 @@ TEST_P(PrivacySandboxSettingsTest, CookieExceptionsApply) {
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+ EXPECT_TRUE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://another-test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
+
// Exceptions which specify a wildcard top frame origin should match both
// empty top frames and non empty top frames.
privacy_sandbox_test_util::SetupTestState(
@@ -412,6 +436,10 @@ TEST_P(PrivacySandboxSettingsTest, CookieExceptionsApply) {
url::Origin::Create(GURL("https://test.com")),
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+
+ EXPECT_FALSE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
}
TEST_P(PrivacySandboxSettingsTest, ThirdPartyCookies) {
@@ -447,6 +475,10 @@ TEST_P(PrivacySandboxSettingsTest, ThirdPartyCookies) {
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+ EXPECT_FALSE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
+
// Privacy Sandbox APIs should be disabled if all cookies are blocked.
privacy_sandbox_test_util::SetupTestState(
prefs(), host_content_settings_map(),
@@ -479,6 +511,10 @@ TEST_P(PrivacySandboxSettingsTest, ThirdPartyCookies) {
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+ EXPECT_FALSE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
+
// Privacy Sandbox APIs should be disabled if the privacy sandbox is disabled,
// regardless of other cookie settings.
privacy_sandbox_test_util::SetupTestState(
@@ -515,6 +551,10 @@ TEST_P(PrivacySandboxSettingsTest, ThirdPartyCookies) {
url::Origin::Create(GURL("https://test.com")),
{GURL("https://embedded.com"),
GURL("https://another-embedded.com")}));
+
+ EXPECT_FALSE(privacy_sandbox_settings()->IsSharedStorageAllowed(
+ url::Origin::Create(GURL("https://test.com")),
+ url::Origin::Create(GURL("https://embedded.com"))));
}
TEST_P(PrivacySandboxSettingsTest, IsPrivacySandboxEnabled) {
diff --git a/chromium/components/proxy_config/proxy_policy_handler.cc b/chromium/components/proxy_config/proxy_policy_handler.cc
index 1fbbbee4e55..926f2badb3b 100644
--- a/chromium/components/proxy_config/proxy_policy_handler.cc
+++ b/chromium/components/proxy_config/proxy_policy_handler.cc
@@ -48,7 +48,7 @@ struct ProxyModeValidationEntry {
// List of entries determining which proxy policies can be specified, depending
// on the ProxyMode.
-const ProxyModeValidationEntry kProxyModeValidationMap[] = {
+constexpr ProxyModeValidationEntry kProxyModeValidationMap[] = {
{ProxyPrefs::kDirectProxyModeName, false, false, false, false,
IDS_POLICY_PROXY_MODE_DISABLED_ERROR},
{ProxyPrefs::kAutoDetectProxyModeName, false, false, false, false,
@@ -61,7 +61,9 @@ const ProxyModeValidationEntry kProxyModeValidationMap[] = {
IDS_POLICY_PROXY_MODE_SYSTEM_ERROR},
};
-const char* kDeprecatedProxyPolicies[] = {
+// Cannot be constexpr because the values of the strings are defined in an
+// automatically generated .cc file.
+const char* const kDeprecatedProxyPolicies[] = {
kProxyMode, kProxyServerMode, kProxyServer, kProxyPacUrl, kProxyBypassList,
};
diff --git a/chromium/components/query_tiles/BUILD.gn b/chromium/components/query_tiles/BUILD.gn
index 3eb0305e059..866d35794a9 100644
--- a/chromium/components/query_tiles/BUILD.gn
+++ b/chromium/components/query_tiles/BUILD.gn
@@ -101,6 +101,8 @@ if (is_android) {
deps = [
":query_tile_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/widget/android:java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//ui/android:ui_java",
diff --git a/chromium/components/query_tiles/internal/tile_iterator.cc b/chromium/components/query_tiles/internal/tile_iterator.cc
index d2c6a4c5ff5..ac560ee3e9c 100644
--- a/chromium/components/query_tiles/internal/tile_iterator.cc
+++ b/chromium/components/query_tiles/internal/tile_iterator.cc
@@ -6,6 +6,7 @@
#include <ostream>
+#include "base/check_op.h"
#include "components/query_tiles/internal/tile_group.h"
#include "components/query_tiles/tile.h"
diff --git a/chromium/components/query_tiles/internal/tile_service_scheduler_unittest.cc b/chromium/components/query_tiles/internal/tile_service_scheduler_unittest.cc
index 8ab092c80e1..ff4dda98ada 100644
--- a/chromium/components/query_tiles/internal/tile_service_scheduler_unittest.cc
+++ b/chromium/components/query_tiles/internal/tile_service_scheduler_unittest.cc
@@ -8,6 +8,7 @@
#include <vector>
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/test/scoped_command_line.h"
#include "base/test/simple_test_clock.h"
diff --git a/chromium/components/query_tiles/internal/tile_utils.cc b/chromium/components/query_tiles/internal/tile_utils.cc
index 6202e243780..62bcf1f3b22 100644
--- a/chromium/components/query_tiles/internal/tile_utils.cc
+++ b/chromium/components/query_tiles/internal/tile_utils.cc
@@ -5,8 +5,10 @@
#include "components/query_tiles/internal/tile_utils.h"
#include <algorithm>
+#include <cmath>
#include <limits>
+#include "base/memory/raw_ptr.h"
#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "components/query_tiles/internal/tile_config.h"
@@ -23,7 +25,7 @@ struct TileComparator {
return (*tile_score_map)[a->id] > (*tile_score_map)[b->id];
}
- std::map<std::string, double>* tile_score_map;
+ raw_ptr<std::map<std::string, double>> tile_score_map;
};
void SortTiles(std::vector<std::unique_ptr<Tile>>* tiles,
diff --git a/chromium/components/quirks/quirks_client.cc b/chromium/components/quirks/quirks_client.cc
index f661f4d9e85..360d9f59640 100644
--- a/chromium/components/quirks/quirks_client.cc
+++ b/chromium/components/quirks/quirks_client.cc
@@ -8,12 +8,12 @@
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
+#include "base/strings/escape.h"
#include "base/strings/stringprintf.h"
#include "base/task/task_runner_util.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/quirks/quirks_manager.h"
#include "components/version_info/version_info.h"
-#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/resource_request.h"
@@ -78,8 +78,8 @@ void QuirksClient::StartDownload() {
kQuirksUrlFormat, IdToHexString(product_id_).c_str(), major_version);
if (!display_name_.empty()) {
- url +=
- "display_name=" + net::EscapeQueryParamValue(display_name_, true) + "&";
+ url += "display_name=" + base::EscapeQueryParamValue(display_name_, true) +
+ "&";
}
VLOG(2) << "Preparing to download\n " << url << "\nto file "
diff --git a/chromium/components/remote_cocoa/app_shim/alert.mm b/chromium/components/remote_cocoa/app_shim/alert.mm
index dee0a104bd0..d24c473a97b 100644
--- a/chromium/components/remote_cocoa/app_shim/alert.mm
+++ b/chromium/components/remote_cocoa/app_shim/alert.mm
@@ -146,7 +146,7 @@ const int kMessageTextMaxSlots = 2000;
if (message_has_rtl && message_text_field) {
base::scoped_nsobject<NSMutableParagraphStyle> alignment(
[[NSParagraphStyle defaultParagraphStyle] mutableCopy]);
- [alignment setAlignment:NSRightTextAlignment];
+ [alignment setAlignment:NSTextAlignmentRight];
NSDictionary* alignment_attributes =
@{NSParagraphStyleAttributeName : alignment};
@@ -164,8 +164,8 @@ const int kMessageTextMaxSlots = 2000;
base::scoped_nsobject<NSMutableParagraphStyle> alignment(
[[NSParagraphStyle defaultParagraphStyle] mutableCopy]);
[alignment setAlignment:(direction == base::i18n::RIGHT_TO_LEFT)
- ? NSRightTextAlignment
- : NSLeftTextAlignment];
+ ? NSTextAlignmentRight
+ : NSTextAlignmentLeft];
NSDictionary* alignment_attributes =
@{NSParagraphStyleAttributeName : alignment};
diff --git a/chromium/components/remote_cocoa/app_shim/application_bridge.h b/chromium/components/remote_cocoa/app_shim/application_bridge.h
index 1e9fd8dd884..bfb0f33315a 100644
--- a/chromium/components/remote_cocoa/app_shim/application_bridge.h
+++ b/chromium/components/remote_cocoa/app_shim/application_bridge.h
@@ -35,6 +35,7 @@ class REMOTE_COCOA_APP_SHIM_EXPORT ApplicationBridge
// TODO(https://crbug.com/888290): Move these types from content to
// remote_cocoa.
using RenderWidgetHostNSViewCreateCallback = base::RepeatingCallback<void(
+ uint64_t view_id,
mojo::ScopedInterfaceEndpointHandle host_handle,
mojo::ScopedInterfaceEndpointHandle view_request_handle)>;
using WebContentsNSViewCreateCallback = base::RepeatingCallback<void(
@@ -58,6 +59,7 @@ class REMOTE_COCOA_APP_SHIM_EXPORT ApplicationBridge
mojo::PendingAssociatedRemote<mojom::TextInputHost> text_input_host)
override;
void CreateRenderWidgetHostNSView(
+ uint64_t view_id,
mojo::PendingAssociatedRemote<mojom::StubInterface> host,
mojo::PendingAssociatedReceiver<mojom::StubInterface> view_receiver)
override;
diff --git a/chromium/components/remote_cocoa/app_shim/application_bridge.mm b/chromium/components/remote_cocoa/app_shim/application_bridge.mm
index 04691696ee6..306db835fe2 100644
--- a/chromium/components/remote_cocoa/app_shim/application_bridge.mm
+++ b/chromium/components/remote_cocoa/app_shim/application_bridge.mm
@@ -150,11 +150,12 @@ void ApplicationBridge::CreateNativeWidgetNSWindow(
}
void ApplicationBridge::CreateRenderWidgetHostNSView(
+ uint64_t view_id,
mojo::PendingAssociatedRemote<mojom::StubInterface> host,
mojo::PendingAssociatedReceiver<mojom::StubInterface> view_receiver) {
if (!render_widget_host_create_callback_)
return;
- render_widget_host_create_callback_.Run(host.PassHandle(),
+ render_widget_host_create_callback_.Run(view_id, host.PassHandle(),
view_receiver.PassHandle());
}
diff --git a/chromium/components/remote_cocoa/app_shim/bridged_content_view.mm b/chromium/components/remote_cocoa/app_shim/bridged_content_view.mm
index b5d505af82c..1fc2ac0dabf 100644
--- a/chromium/components/remote_cocoa/app_shim/bridged_content_view.mm
+++ b/chromium/components/remote_cocoa/app_shim/bridged_content_view.mm
@@ -288,7 +288,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
if (ui::PlatformEventSource::ShouldIgnoreNativePlatformEvents())
return;
- BOOL isScrollEvent = [theEvent type] == NSScrollWheel;
+ BOOL isScrollEvent = [theEvent type] == NSEventTypeScrollWheel;
// If it's the view's window, process normally.
if ([target isEqual:source]) {
@@ -296,7 +296,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
[self scrollWheel:theEvent];
} else {
[self mouseEvent:theEvent];
- if ([theEvent type] == NSLeftMouseUp)
+ if ([theEvent type] == NSEventTypeLeftMouseUp)
[self handleLeftMouseUp:theEvent];
}
return;
@@ -413,7 +413,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
// Always propagate the shift modifier if present. Shift doesn't always alter
// the command selector, but should always be passed along. Control and Alt
// have different meanings on Mac, so they do not propagate automatically.
- if ([_keyDownEvent modifierFlags] & NSShiftKeyMask)
+ if ([_keyDownEvent modifierFlags] & NSEventModifierFlagShift)
eventFlags |= ui::EF_SHIFT_DOWN;
// Generate a synthetic event with the keycode toolkit-views expects.
@@ -606,7 +606,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
if (!_bridge)
return;
- DCHECK([theEvent type] != NSScrollWheel);
+ DCHECK([theEvent type] != NSEventTypeScrollWheel);
auto event = std::make_unique<ui::MouseEvent>(theEvent);
[self adjustUiEventLocation:event.get() fromNativeEvent:theEvent];
diff --git a/chromium/components/remote_cocoa/app_shim/bridged_content_view_touch_bar.mm b/chromium/components/remote_cocoa/app_shim/bridged_content_view_touch_bar.mm
index aecd87cd036..ce7cc676e85 100644
--- a/chromium/components/remote_cocoa/app_shim/bridged_content_view_touch_bar.mm
+++ b/chromium/components/remote_cocoa/app_shim/bridged_content_view_touch_bar.mm
@@ -34,8 +34,7 @@ NSString* const kTouchBarCancelId = @"com.google.chrome-CANCEL";
// NSTouchBarDelegate protocol implementation.
- (NSTouchBarItem*)touchBar:(NSTouchBar*)touchBar
- makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
- API_AVAILABLE(macos(10.12.2)) {
+ makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier {
if (!_bridge)
return nil;
diff --git a/chromium/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm b/chromium/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
index cf88f696a46..4d8f9f89f03 100644
--- a/chromium/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
+++ b/chromium/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
@@ -53,14 +53,6 @@
return YES;
}
-// On 10.10, this prevents the window server from treating the title bar as an
-// unconditionally-draggable region, and allows -[BridgedContentView hitTest:]
-// to choose case-by-case whether to take a mouse event or let it turn into a
-// window drag. Not needed for newer macOS. See r549802 for details.
-- (NSRect)_draggableFrame NS_DEPRECATED_MAC(10_10, 10_11) {
- return NSZeroRect;
-}
-
@end
@implementation BrowserNativeWidgetWindow
diff --git a/chromium/components/remote_cocoa/app_shim/certificate_viewer.mm b/chromium/components/remote_cocoa/app_shim/certificate_viewer.mm
index 0c12e8bda38..56fb1e1dc22 100644
--- a/chromium/components/remote_cocoa/app_shim/certificate_viewer.mm
+++ b/chromium/components/remote_cocoa/app_shim/certificate_viewer.mm
@@ -11,8 +11,7 @@
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/notreached.h"
-#include "net/cert/x509_util_ios_and_mac.h"
-#include "net/cert/x509_util_mac.h"
+#include "net/cert/x509_util_apple.h"
namespace remote_cocoa {
@@ -45,21 +44,13 @@ void ShowCertificateViewerForWindow(NSWindow* owning_window,
}
// Add a basic X.509 policy, in order to match the behaviour of
// SFCertificatePanel when no policies are specified.
- base::ScopedCFTypeRef<SecPolicyRef> basic_policy;
- OSStatus status =
- net::x509_util::CreateBasicX509Policy(basic_policy.InitializeInto());
- if (status != noErr) {
+ base::ScopedCFTypeRef<SecPolicyRef> basic_policy(SecPolicyCreateBasicX509());
+ if (!basic_policy) {
NOTREACHED();
return;
}
CFArrayAppendValue(policies, basic_policy.get());
- status = net::x509_util::CreateRevocationPolicies(false, policies);
- if (status != noErr) {
- NOTREACHED();
- return;
- }
-
SFCertificatePanel* panel = [[SFCertificatePanel alloc] init];
[panel setPolicies:base::mac::CFToNSCast(policies.get())];
[panel beginSheetForWindow:owning_window
diff --git a/chromium/components/remote_cocoa/app_shim/mouse_capture.mm b/chromium/components/remote_cocoa/app_shim/mouse_capture.mm
index aca6d6a0365..e77ae43b6c9 100644
--- a/chromium/components/remote_cocoa/app_shim/mouse_capture.mm
+++ b/chromium/components/remote_cocoa/app_shim/mouse_capture.mm
@@ -72,14 +72,16 @@ NSWindow* CocoaMouseCapture::ActiveEventTap::GetGlobalCaptureWindow() {
}
void CocoaMouseCapture::ActiveEventTap::Init() {
- // Consume most things, but not NSMouseEntered/Exited: The Widget doing
- // capture will still see its own Entered/Exit events, but not those for other
- // NSViews, since consuming those would break their tracking area logic.
- NSEventMask event_mask =
- NSLeftMouseDownMask | NSLeftMouseUpMask | NSRightMouseDownMask |
- NSRightMouseUpMask | NSMouseMovedMask | NSLeftMouseDraggedMask |
- NSRightMouseDraggedMask | NSScrollWheelMask | NSOtherMouseDownMask |
- NSOtherMouseUpMask | NSOtherMouseDraggedMask;
+ // Consume most things, but not NSEventTypeMouseEntered/Exited: The Widget
+ // doing capture will still see its own Entered/Exit events, but not those for
+ // other NSViews, since consuming those would break their tracking area logic.
+ NSEventMask event_mask = NSEventMaskLeftMouseDown | NSEventMaskLeftMouseUp |
+ NSEventMaskRightMouseDown | NSEventMaskRightMouseUp |
+ NSEventMaskMouseMoved | NSEventMaskLeftMouseDragged |
+ NSEventMaskRightMouseDragged |
+ NSEventMaskScrollWheel | NSEventMaskOtherMouseDown |
+ NSEventMaskOtherMouseUp |
+ NSEventMaskOtherMouseDragged;
// Capture a WeakPtr via NSObject. This allows the block to detect another
// event monitor for the same event deleting |owner_|.
diff --git a/chromium/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h b/chromium/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h
index 805f4efc70f..970fa443d8e 100644
--- a/chromium/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h
+++ b/chromium/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h
@@ -8,9 +8,9 @@
#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
// Overrides contentRect <-> frameRect conversion methods to keep them equal to
-// each other, even for windows that do not use NSBorderlessWindowMask. This
-// allows an NSWindow to be frameless without attaining undesired side-effects
-// of NSBorderlessWindowMask.
+// each other, even for windows that do not use NSWindowStyleMaskBorderless.
+// This allows an NSWindow to be frameless without attaining undesired
+// side-effects of NSWindowStyleMaskBorderless.
@interface NativeWidgetMacFramelessNSWindow : NativeWidgetMacNSWindow
@end
diff --git a/chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h b/chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
index 5a23ea75588..67ebc56bd7e 100644
--- a/chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
+++ b/chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
@@ -57,6 +57,11 @@ REMOTE_COCOA_APP_SHIM_EXPORT
// https://crbug.com/960904
- (void)enforceNeverMadeVisible;
+// Order the window to the front (space switch if necessary), and ensure that
+// the window maintains its key state. A space switch will normally activate a
+// window, so this function prevents that if the window is currently inactive.
+- (void)orderFrontKeepWindowKeyState;
+
// Identifier for the NativeWidgetMac from which this window was created. This
// may be used to look up the NativeWidgetMacNSWindowHost in the browser process
// or the NativeWidgetNSWindowBridge in a display process.
diff --git a/chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
index 83e08e8e56f..48c86f5aa95 100644
--- a/chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
+++ b/chromium/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -4,6 +4,7 @@
#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
+#include "base/auto_reset.h"
#include "base/debug/dump_without_crashing.h"
#include "base/mac/foundation_util.h"
#include "base/trace_event/trace_event.h"
@@ -15,6 +16,66 @@
#import "ui/base/cocoa/user_interface_item_command_handler.h"
#import "ui/base/cocoa/window_size_constants.h"
+namespace {
+
+// AppKit quirk: -[NSWindow orderWindow] does not handle reordering for children
+// windows. Their order is fixed to the attachment order (the last attached
+// window is on the top). Therefore, work around it by re-parenting in our
+// desired order.
+void OrderChildWindow(NSWindow* child_window,
+ NSWindow* other_window,
+ NSWindowOrderingMode ordering_mode) {
+ NSWindow* parent = [child_window parentWindow];
+ DCHECK(parent);
+
+ // `ordered_children` sorts children windows back to front.
+ NSArray<NSWindow*>* children = [[child_window parentWindow] childWindows];
+ std::vector<std::pair<NSInteger, NSWindow*>> ordered_children;
+ for (NSWindow* child in children)
+ ordered_children.push_back({[child orderedIndex], child});
+ std::sort(ordered_children.begin(), ordered_children.end(), std::greater<>());
+
+ // If `other_window` is nullptr, place `child_window` in front of (or behind)
+ // all other children windows.
+ if (other_window == nullptr) {
+ other_window = ordering_mode == NSWindowAbove
+ ? ordered_children.back().second
+ : parent;
+ }
+
+ if (child_window == other_window)
+ return;
+
+ const bool relative_to_parent = parent == other_window;
+ DCHECK(ordering_mode != NSWindowBelow || !relative_to_parent)
+ << "Placing a child window behind its parent is not supported.";
+
+ for (NSWindow* child in children)
+ [parent removeChildWindow:child];
+
+ // If `relative_to_parent` is true, `child_window` is the first child of its
+ // parent.
+ if (relative_to_parent)
+ [parent addChildWindow:child_window ordered:NSWindowAbove];
+
+ // Re-parent children windows in the desired order.
+ for (auto [ordered_index, child] : ordered_children) {
+ if (child != child_window && child != other_window) {
+ [parent addChildWindow:child ordered:NSWindowAbove];
+ } else if (child == other_window && !relative_to_parent) {
+ if (ordering_mode == NSWindowAbove) {
+ [parent addChildWindow:other_window ordered:NSWindowAbove];
+ [parent addChildWindow:child_window ordered:NSWindowAbove];
+ } else {
+ [parent addChildWindow:child_window ordered:NSWindowAbove];
+ [parent addChildWindow:other_window ordered:NSWindowAbove];
+ }
+ }
+ }
+}
+
+} // namespace
+
@interface NSWindow (Private)
+ (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle;
- (BOOL)hasKeyAppearance;
@@ -58,7 +119,7 @@
return NO;
}
// The base implementation just tests [self class] == [NSThemeFrame class].
-- (BOOL)_shouldFlipTrafficLightsForRTL API_AVAILABLE(macos(10.12)) {
+- (BOOL)_shouldFlipTrafficLightsForRTL {
return [[self window] windowTitlebarLayoutDirection] ==
NSUserInterfaceLayoutDirectionRightToLeft;
}
@@ -83,6 +144,8 @@
remote_cocoa::NativeWidgetNSWindowBridge* _bridge;
BOOL _willUpdateRestorableState;
BOOL _isEnforcingNeverMadeVisible;
+ BOOL _preventKeyWindow;
+ BOOL _isAddingChildWindow;
}
@synthesize bridgedNativeWidgetId = _bridgedNativeWidgetId;
@synthesize bridge = _bridge;
@@ -98,6 +161,7 @@
defer:deferCreation])) {
_commandDispatcher.reset([[CommandDispatcher alloc] initWithOwner:self]);
}
+ _isAddingChildWindow = NO;
return self;
}
@@ -113,6 +177,15 @@
[super dealloc];
}
+- (void)addChildWindow:(NSWindow*)childWin ordered:(NSWindowOrderingMode)place {
+ base::AutoReset<BOOL> isAddingChildWindow(&_isAddingChildWindow, YES);
+ // Attaching a window to be a child window resets the window level, so
+ // restore the window level afterwards.
+ NSInteger level = childWin.level;
+ [super addChildWindow:childWin ordered:place];
+ childWin.level = level;
+}
+
- (void)enforceNeverMadeVisible {
if (_isEnforcingNeverMadeVisible)
return;
@@ -161,6 +234,28 @@
_touchBarDelegate = delegate;
}
+- (void)orderFrontKeepWindowKeyState {
+ if ([self isOnActiveSpace]) {
+ [self orderWindow:NSWindowAbove relativeTo:0];
+ return;
+ }
+ // The OS will activate the window if it causes a space switch.
+ // Temporarily prevent the window from becoming the key window until after
+ // the space change completes.
+ _preventKeyWindow = ![self isKeyWindow];
+ NSNotificationCenter* notificationCenter =
+ [[NSWorkspace sharedWorkspace] notificationCenter];
+ __block id observer = [notificationCenter
+ addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification
+ object:[NSWorkspace sharedWorkspace]
+ queue:[NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification* notification) {
+ _preventKeyWindow = NO;
+ [notificationCenter removeObserver:observer];
+ }];
+ [self orderWindow:NSWindowAbove relativeTo:0];
+}
+
// Private methods.
- (ViewsNSWindowDelegate*)viewsNSWindowDelegate {
@@ -212,10 +307,12 @@
}
// Ignore [super canBecome{Key,Main}Window]. The default is NO for windows with
-// NSBorderlessWindowMask, which is not the desired behavior.
+// NSWindowStyleMaskBorderless, which is not the desired behavior.
// Note these can be called via -[NSWindow close] while the widget is being torn
// down, so check for a delegate.
- (BOOL)canBecomeKeyWindow {
+ if (_preventKeyWindow)
+ return NO;
bool canBecomeKey = NO;
if (_bridge)
_bridge->host()->GetCanWindowBecomeKey(&canBecomeKey);
@@ -273,12 +370,12 @@
// still suppress right-clicks in a draggable region. Forwarding right-clicks
// allows the underlying views to respond to right-click to potentially bring
// up a frame context menu.
- if (type == NSRightMouseDown) {
+ if (type == NSEventTypeRightMouseDown) {
if ([[self contentView] hitTest:event.locationInWindow] == nil) {
[[self contentView] rightMouseDown:event];
return;
}
- } else if (type == NSRightMouseUp) {
+ } else if (type == NSEventTypeRightMouseUp) {
if ([[self contentView] hitTest:event.locationInWindow] == nil) {
[[self contentView] rightMouseUp:event];
return;
@@ -286,10 +383,10 @@
} else if ([self hasViewsMenuActive]) {
// Send to the menu, after converting the event into an action message using
// the content view.
- if (type == NSKeyDown) {
+ if (type == NSEventTypeKeyDown) {
[[self contentView] keyDown:event];
return;
- } else if (type == NSKeyUp) {
+ } else if (type == NSEventTypeKeyUp) {
[[self contentView] keyUp:event];
return;
}
@@ -298,13 +395,41 @@
[super sendEvent:event];
}
-// Override window order functions to intercept other visibility changes. This
-// is needed in addition to the -[NSWindow display] override because Cocoa
-// hardly ever calls display, and reports -[NSWindow isVisible] incorrectly
-// when ordering in a window for the first time.
+// Override window order functions to
+// 1. Intercept other visibility changes
+// This is needed in addition to the -[NSWindow display] override because
+// Cocoa hardly ever calls display, and reports -[NSWindow isVisible]
+// incorrectly when ordering in a window for the first time.
+// 2. Handle child windows
+// -[NSWindow orderWindow] does not work for child windows. To order
+// children, we need to detach them from thier parent and re-attach them
+// in our desire order.
- (void)orderWindow:(NSWindowOrderingMode)orderingMode
relativeTo:(NSInteger)otherWindowNumber {
+ NativeWidgetMacNSWindow* parent =
+ static_cast<NativeWidgetMacNSWindow*>([self parentWindow]);
+ // Cocoa will call -[NSWindow orderWindow] during -[NSWindow addChildWindow].
+ // To prevent re-entrancy, skip re-parenting if adding children.
+ if (parent && parent->_isAddingChildWindow) {
+ [super orderWindow:orderingMode relativeTo:otherWindowNumber];
+ [[self viewsNSWindowDelegate] onWindowOrderChanged:nil];
+ return;
+ }
+
+ // `otherWindow` is nil if `otherWindowNumber` is 0. In this case, place
+ // `self` at the top / bottom, depending on `orderingMode`.
+ NSWindow* otherWindow = [NSApp windowWithWindowNumber:otherWindowNumber];
+
+ // For unknown reason chrome will freeze during startup without this line.
[super orderWindow:orderingMode relativeTo:otherWindowNumber];
+
+ // During shutdown the parent may have changed at this point, so reacquire
+ // `parent`.
+ parent = static_cast<NativeWidgetMacNSWindow*>([self parentWindow]);
+ if (parent && (otherWindow == nullptr ||
+ parent == [otherWindow parentWindow] || parent == otherWindow))
+ OrderChildWindow(self, otherWindow, orderingMode);
+
[[self viewsNSWindowDelegate] onWindowOrderChanged:nil];
}
@@ -337,7 +462,7 @@
[super cursorUpdate:theEvent];
}
-- (NSTouchBar*)makeTouchBar API_AVAILABLE(macos(10.12.2)) {
+- (NSTouchBar*)makeTouchBar {
return _touchBarDelegate ? [_touchBarDelegate makeTouchBar] : nil;
}
@@ -392,8 +517,8 @@
}
}
-// On newer SDKs, _canMiniaturize respects NSMiniaturizableWindowMask in the
-// window's styleMask. Views assumes that Widgets can always be minimized,
+// On newer SDKs, _canMiniaturize respects NSWindowStyleMaskMiniaturizable in
+// the window's styleMask. Views assumes that Widgets can always be minimized,
// regardless of their window style, so override that behavior here.
- (BOOL)_canMiniaturize {
return YES;
diff --git a/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h b/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
index 0da752b2530..d11ee8c1fea 100644
--- a/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
+++ b/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
@@ -20,10 +20,12 @@
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/accelerated_widget_mac/ca_transaction_observer.h"
#include "ui/accelerated_widget_mac/display_ca_layer_tree.h"
#include "ui/base/cocoa/command_dispatcher.h"
#include "ui/base/cocoa/weak_ptr_nsobject.h"
+#include "ui/base/cursor/cursor.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/display/display_observer.h"
@@ -124,19 +126,6 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowBridge
// Called internally by the NSWindowDelegate when the window is closing.
void OnWindowWillClose();
- // Called by the NSWindowDelegate when a fullscreen operation begins. If
- // |target_fullscreen_state| is true, the target state is fullscreen.
- // Otherwise, a transition has begun to come out of fullscreen.
- void OnFullscreenTransitionStart(bool target_fullscreen_state);
-
- // Called when a fullscreen transition completes. If target_fullscreen_state()
- // does not match |actual_fullscreen_state|, a new transition will begin.
- void OnFullscreenTransitionComplete(bool actual_fullscreen_state);
-
- // Transition the window into or out of fullscreen. This will immediately
- // invert the value of target_fullscreen_state().
- void ToggleDesiredFullscreenState(bool async = false);
-
// Called by the NSWindowDelegate when the size of the window changes.
void OnSizeChanged();
@@ -181,20 +170,16 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowBridge
return child_windows_;
}
- NativeWidgetNSWindowFullscreenController* fullscreen_controller() {
- return fullscreen_controller_.get();
+ NativeWidgetNSWindowFullscreenController& fullscreen_controller() {
+ return fullscreen_controller_;
}
bool target_fullscreen_state() const {
- if (fullscreen_controller_)
- return fullscreen_controller_->GetTargetFullscreenState();
- return target_fullscreen_state_;
+ return fullscreen_controller_.GetTargetFullscreenState();
}
- bool window_visible() const { return window_visible_; }
+ bool window_visible() const;
bool wants_to_be_visible() const { return wants_to_be_visible_; }
bool in_fullscreen_transition() const {
- if (fullscreen_controller_)
- return fullscreen_controller_->IsInFullscreenTransition();
- return in_fullscreen_transition_;
+ return fullscreen_controller_.IsInFullscreenTransition();
}
// Whether to run a custom animation for the provided |transition|.
@@ -214,9 +199,10 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowBridge
// NativeWidgetNSWindowFullscreenController::Client:
void FullscreenControllerTransitionStart(bool is_target_fullscreen) override;
void FullscreenControllerTransitionComplete(bool is_fullscreen) override;
- void FullscreenControllerSetFrame(const gfx::Rect& frame,
- bool animate,
- base::TimeDelta& transition_time) override;
+ void FullscreenControllerSetFrame(
+ const gfx::Rect& frame,
+ bool animate,
+ base::OnceCallback<void()> completion_callback) override;
void FullscreenControllerToggleFullscreen() override;
void FullscreenControllerCloseWindow() override;
int64_t FullscreenControllerGetDisplayId() const override;
@@ -229,7 +215,6 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowBridge
base::TimeDelta PreCommitTimeout() override;
// remote_cocoa::mojom::NativeWidgetNSWindow:
- void CreateFullscreenController() override;
void CreateWindow(mojom::CreateWindowParamsPtr params) override;
void SetParent(uint64_t parent_id) override;
void CreateSelectFileDialog(
@@ -258,7 +243,6 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowBridge
void SetTransitionsToAnimate(
remote_cocoa::mojom::VisibilityTransition transitions) override;
void SetVisibleOnAllSpaces(bool always_visible) override;
- void SetFullscreen(bool fullscreen) override;
void EnterFullscreen(int64_t target_display_id) override;
void ExitFullscreen() override;
void SetCanAppearInExistingFullscreenSpaces(
@@ -291,6 +275,7 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowBridge
const mojom::WindowControlsOverlayNSViewType overlay_type) override;
void RemoveWindowControlsOverlayNSView(
const mojom::WindowControlsOverlayNSViewType overlay_type) override;
+ void SetCursor(const ui::Cursor& cursor) override;
// Return true if [NSApp updateWindows] needs to be called after updating the
// TextInputClient.
@@ -357,7 +342,6 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowBridge
std::unique_ptr<CocoaWindowMoveLoop> window_move_loop_;
ui::ModalType modal_type_ = ui::MODAL_TYPE_NONE;
bool is_translucent_window_ = false;
- bool is_headless_mode_window_ = false;
id key_down_event_monitor_ = nil;
// Intended for PWAs with window controls overlay display override. These two
@@ -393,26 +377,8 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowBridge
remote_cocoa::mojom::VisibilityTransition transitions_to_animate_ =
remote_cocoa::mojom::VisibilityTransition::kBoth;
- // Whether this window wants to be fullscreen. If a fullscreen animation is in
- // progress then it might not be actually fullscreen.
- bool target_fullscreen_state_ = false;
-
- // Whether this window is in a fullscreen transition, and the fullscreen state
- // can not currently be changed.
- bool in_fullscreen_transition_ = false;
-
- // Trying to close an NSWindow during a fullscreen transition will cause the
- // window to lock up. Use this to track if CloseWindow was called during a
- // fullscreen transition, to defer the -[NSWindow close] call until the
- // transition is complete.
- // https://crbug.com/945237
- bool has_deferred_window_close_ = false;
-
- // Manager of fullscreen state transitions. If this is non-nullptr, then it
- // replaces `target_fullscreen_state_`, `in_fullscreen_transition_`, and
- // `has_deferred_window_close_`.
- std::unique_ptr<NativeWidgetNSWindowFullscreenController>
- fullscreen_controller_;
+ // Manager of fullscreen state transitions.
+ NativeWidgetNSWindowFullscreenController fullscreen_controller_{this};
// Stores the value last read from -[NSWindow isVisible], to detect visibility
// changes.
@@ -434,6 +400,17 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowBridge
// on the first call to SetVisibilityState().
std::vector<uint8_t> pending_restoration_data_;
+ // This tracks headless window visibility and fullscreen states.
+ // In headless mode the platform window is never made visible or change its
+ // state, so this structure holds the requested state for reporting.
+ struct HeadlessModeWindow {
+ bool visibility_state = false;
+ bool fullscreen_state = false;
+ };
+
+ // This is present iff the window has been created in headless mode.
+ absl::optional<HeadlessModeWindow> headless_mode_window_;
+
display::ScopedDisplayObserver display_observer_{this};
mojo::AssociatedReceiver<remote_cocoa::mojom::NativeWidgetNSWindow>
diff --git a/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
index 6394980da8a..4c5bb2cd395 100644
--- a/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+++ b/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -35,12 +35,14 @@
#include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
#include "ui/base/cocoa/cocoa_base_utils.h"
#import "ui/base/cocoa/constrained_window/constrained_window_animation.h"
+#include "ui/base/cocoa/cursor_utils.h"
#include "ui/base/cocoa/remote_accessibility_api.h"
#import "ui/base/cocoa/window_size_constants.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/hit_test.h"
#include "ui/base/layout.h"
#include "ui/base/ui_base_switches.h"
+#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/gfx/geometry/dip_util.h"
@@ -72,6 +74,11 @@ bool AreWindowShadowsDisabled() {
return is_headless;
}
+// Returns the display that the specified window is on.
+display::Display GetDisplayForWindow(NSWindow* window) {
+ return display::Screen::GetScreen()->GetDisplayNearestWindow(window);
+}
+
} // namespace
// The NSView that hosts the composited CALayer drawing the UI. It fills the
@@ -416,19 +423,13 @@ void NativeWidgetNSWindowBridge::StackAbove(uint64_t sibling_id) {
}
void NativeWidgetNSWindowBridge::StackAtTop() {
- [window_ setOrderedIndex:0];
+ [window_ orderWindow:NSWindowAbove relativeTo:0];
}
void NativeWidgetNSWindowBridge::ShowEmojiPanel() {
ui::ShowEmojiPanel();
}
-void NativeWidgetNSWindowBridge::CreateFullscreenController() {
- DCHECK(!fullscreen_controller_);
- fullscreen_controller_ =
- std::make_unique<NativeWidgetNSWindowFullscreenController>(this);
-}
-
void NativeWidgetNSWindowBridge::CreateWindow(
mojom::CreateWindowParamsPtr params) {
SetWindow(CreateNSWindow(params.get()));
@@ -438,9 +439,11 @@ void NativeWidgetNSWindowBridge::InitWindow(
mojom::NativeWidgetNSWindowInitParamsPtr params) {
modal_type_ = params->modal_type;
is_translucent_window_ = params->is_translucent;
- is_headless_mode_window_ = params->is_headless_mode_window;
pending_restoration_data_ = params->state_restoration_data;
+ if (params->is_headless_mode_window)
+ headless_mode_window_ = absl::make_optional<HeadlessModeWindow>();
+
// Register for application hide notifications so that visibility can be
// properly tracked. This is not done in the delegate so that the lifetime is
// tied to the C++ object, rather than the delegate (which may be reference
@@ -462,10 +465,10 @@ void NativeWidgetNSWindowBridge::InitWindow(
// Validate the window's initial state, otherwise the bridge's initial
// tracking state will be incorrect.
DCHECK(![window_ isVisible]);
- DCHECK_EQ(0u, [window_ styleMask] & NSFullScreenWindowMask);
+ DCHECK_EQ(0u, [window_ styleMask] & NSWindowStyleMaskFullScreen);
// Include "regular" windows without the standard frame in the window cycle.
- // These use NSBorderlessWindowMask so do not get it by default.
+ // These use NSWindowStyleMaskBorderless so do not get it by default.
if (params->force_into_collection_cycle) {
[window_
setCollectionBehavior:[window_ collectionBehavior] |
@@ -527,9 +530,19 @@ void NativeWidgetNSWindowBridge::SetBounds(
new_bounds.origin(),
GetWindowSizeForClientSize(window_, clamped_content_size));
+ NSScreen* previous_screen = [window_ screen];
+
[window_ setFrame:gfx::ScreenRectToNSRect(actual_new_bounds)
display:YES
animate:NO];
+
+ // If the window has focus but is not on the active space and the window was
+ // moved to a different display, re-activate it to switch the space to the
+ // active window. (crbug.com/1316543)
+ if ([window_ isKeyWindow] && ![window_ isOnActiveSpace] &&
+ [window_ screen] != previous_screen) {
+ SetVisibilityState(WindowVisibilityState::kShowAndActivateWindow);
+ }
}
void NativeWidgetNSWindowBridge::SetSizeAndCenter(
@@ -592,13 +605,8 @@ void NativeWidgetNSWindowBridge::CreateContentView(uint64_t ns_view_id,
}
void NativeWidgetNSWindowBridge::CloseWindow() {
- if (fullscreen_controller_) {
- if (fullscreen_controller_->HasDeferredWindowClose())
- return;
- } else {
- if (has_deferred_window_close_)
- return;
- }
+ if (fullscreen_controller_.HasDeferredWindowClose())
+ return;
// Keep |window| on the stack so that the ObjectiveC block below can capture
// it and properly increment the reference count bound to the posted task.
@@ -641,16 +649,9 @@ void NativeWidgetNSWindowBridge::CloseWindow() {
[window orderOut:nil];
// Defer closing windows until after fullscreen transitions complete.
- if (fullscreen_controller_) {
- fullscreen_controller_->OnWindowWantsToClose();
- if (fullscreen_controller_->HasDeferredWindowClose())
- return;
- } else {
- if (in_fullscreen_transition_) {
- has_deferred_window_close_ = true;
- return;
- }
- }
+ fullscreen_controller_.OnWindowWantsToClose();
+ if (fullscreen_controller_.HasDeferredWindowClose())
+ return;
// Many tests assume that base::RunLoop().RunUntilIdle() is always sufficient
// to execute a close. However, in rare cases, -performSelector:..afterDelay:0
@@ -673,9 +674,16 @@ void NativeWidgetNSWindowBridge::CloseWindowNow() {
void NativeWidgetNSWindowBridge::SetVisibilityState(
WindowVisibilityState new_state) {
- // Avoid changing headless mode window visibility state.
- if (is_headless_mode_window_)
+ // In headless mode the platform window is always hidden, so instead of
+ // changing its visibility state just maintain a local flag to track the
+ // expected visibility state and lie to the upper layer pretending the
+ // window did change its visibility state.
+ if (headless_mode_window_) {
+ headless_mode_window_->visibility_state =
+ new_state != WindowVisibilityState::kHideWindow;
+ host_->OnVisibilityChanged(headless_mode_window_->visibility_state);
return;
+ }
// During session restore this method gets called from RestoreTabsToBrowser()
// with new_state = kShowAndActivateWindow. We consume restoration data on our
@@ -763,11 +771,21 @@ void NativeWidgetNSWindowBridge::SetVisibilityState(
if (new_state == WindowVisibilityState::kShowAndActivateWindow) {
[window_ makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
- } else if (!parent_ && ![window_ isMiniaturized]) {
- // When showing a window without activation, avoid making it the front
- // window (with e.g. orderFront:), which can cause a space switch.
- [window_ orderWindow:NSWindowBelow
- relativeTo:NSApp.mainWindow.windowNumber];
+ } else if (new_state == WindowVisibilityState::kShowInactive && !parent_ &&
+ ![window_ isMiniaturized]) {
+ if ([[NSApp mainWindow] screen] == [window_ screen] ||
+ ![[NSApp mainWindow] isKeyWindow]) {
+ // When the new window is on the same display as the main window or the
+ // main window is inactive, order the window relative to the main window.
+ // Avoid making it the front window (with e.g. orderFront:), which can
+ // cause a space switch.
+ [window_ orderWindow:NSWindowBelow
+ relativeTo:NSApp.mainWindow.windowNumber];
+ } else {
+ // When opening an inactive window on another screen, put the window at
+ // the front and trigger a space switch.
+ [window_ orderFrontKeepWindowKeyState];
+ }
}
// For non-sheet modal types, use the constrained window animations to make
@@ -841,7 +859,7 @@ void NativeWidgetNSWindowBridge::SetLocalEventMonitorEnabled(bool enabled) {
return event_handled ? nil : event;
};
key_down_event_monitor_ =
- [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask
+ [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown
handler:block];
} else {
// Destroy the event monitor if it exists.
@@ -896,18 +914,12 @@ void NativeWidgetNSWindowBridge::SetCursor(NSCursor* cursor) {
[window_delegate_ setCursor:cursor];
}
+void NativeWidgetNSWindowBridge::SetCursor(const ui::Cursor& cursor) {
+ SetCursor(ui::GetNativeCursor(cursor));
+}
+
void NativeWidgetNSWindowBridge::OnWindowWillClose() {
- if (fullscreen_controller_) {
- fullscreen_controller_->OnWindowWillClose();
- } else {
- // If a window closes while in a fullscreen transition, then the window will
- // hang in a zombie-like state.
- // https://crbug.com/945237
- if (in_fullscreen_transition_) {
- DLOG(ERROR) << "-[NSWindow close] while in fullscreen transition will "
- "trigger zombie windows.";
- }
- }
+ fullscreen_controller_.OnWindowWillClose();
[window_ setCommandHandler:nil];
[window_ setCommandDispatcherDelegate:nil];
@@ -946,92 +958,6 @@ void NativeWidgetNSWindowBridge::OnWindowWillClose() {
// Note: |this| and its host will be deleted here.
}
-void NativeWidgetNSWindowBridge::OnFullscreenTransitionStart(
- bool target_fullscreen_state) {
- DCHECK(!fullscreen_controller_);
- DCHECK_NE(target_fullscreen_state, target_fullscreen_state_);
- target_fullscreen_state_ = target_fullscreen_state;
- in_fullscreen_transition_ = true;
-
- host_->OnWindowFullscreenTransitionStart(target_fullscreen_state);
-}
-
-void NativeWidgetNSWindowBridge::OnFullscreenTransitionComplete(
- bool actual_fullscreen_state) {
- DCHECK(!fullscreen_controller_);
- in_fullscreen_transition_ = false;
-
- // Add any children that were skipped during the fullscreen transition.
- OrderChildren();
-
- if (has_deferred_window_close_) {
- [ns_window() close];
- return;
- }
-
- if (target_fullscreen_state_ == actual_fullscreen_state) {
- host_->OnWindowFullscreenTransitionComplete(actual_fullscreen_state);
- return;
- }
-
- // The transition completed, but into the wrong state. This can happen when
- // there are calls to change the fullscreen state whilst mid-transition.
- // First update to reflect reality so that OnTargetFullscreenStateChanged()
- // expects the change.
- target_fullscreen_state_ = actual_fullscreen_state;
- ToggleDesiredFullscreenState(true /* async */);
-}
-
-void NativeWidgetNSWindowBridge::ToggleDesiredFullscreenState(bool async) {
- DCHECK(!fullscreen_controller_);
- // If there is currently an animation into or out of fullscreen, then AppKit
- // emits the string "not in fullscreen state" to stdio and does nothing. For
- // this case, schedule a transition back into the desired state when the
- // animation completes.
- if (in_fullscreen_transition_) {
- target_fullscreen_state_ = !target_fullscreen_state_;
- return;
- }
-
- // Going fullscreen implicitly makes the window visible. AppKit does this.
- // That is, -[NSWindow isVisible] is always true after a call to -[NSWindow
- // toggleFullScreen:]. Unfortunately, this change happens after AppKit calls
- // -[NSWindowDelegate windowWillEnterFullScreen:], and AppKit doesn't send an
- // orderWindow message. So intercepting the implicit change is hard.
- // Luckily, to trigger externally, the window typically needs to be visible in
- // the first place. So we can just ensure the window is visible here instead
- // of relying on AppKit to do it, and not worry that OnVisibilityChanged()
- // won't be called for externally triggered fullscreen requests.
- if (!window_visible_)
- SetVisibilityState(WindowVisibilityState::kShowInactive);
-
- // Enable fullscreen collection behavior because:
- // 1: -[NSWindow toggleFullscreen:] would otherwise be ignored,
- // 2: the fullscreen button must be enabled so the user can leave fullscreen.
- // This will be reset when a transition out of fullscreen completes.
- gfx::SetNSWindowCanFullscreen(window_, true);
-
- // Until 10.13, AppKit would obey a call to -toggleFullScreen: made inside
- // OnFullscreenTransitionComplete(). Starting in 10.13, it behaves as though
- // the transition is still in progress and just emits "not in a fullscreen
- // state" when trying to exit fullscreen in the same runloop that entered it.
- // To handle this case, invoke -toggleFullScreen: asynchronously.
- if (async) {
- [window_ performSelector:@selector(toggleFullScreen:)
- withObject:nil
- afterDelay:0];
- } else {
- // Suppress synchronous CA transactions during AppKit fullscreen transition
- // since there is no need for updates during such transition.
- // Re-layout and re-paint will be done after the transtion. See
- // https://crbug.com/875707 for potiential problems if we don't suppress.
- // |ca_transaction_sync_suppressed_| will be reset to false when the next
- // frame comes in.
- ca_transaction_sync_suppressed_ = true;
- [window_ toggleFullScreen:nil];
- }
-}
-
void NativeWidgetNSWindowBridge::OnSizeChanged() {
UpdateWindowGeometry();
}
@@ -1102,16 +1028,10 @@ void NativeWidgetNSWindowBridge::SetSizeConstraints(const gfx::Size& min_size,
const gfx::Size& max_size,
bool is_resizable,
bool is_maximizable) {
- if (fullscreen_controller_) {
- if (!fullscreen_controller_->CanResize())
- return;
- } else {
- // Don't modify the size constraints or fullscreen collection behavior while
- // in fullscreen or during a transition. OnFullscreenTransitionComplete will
- // reset these after leaving fullscreen.
- if (target_fullscreen_state_ || in_fullscreen_transition_)
- return;
- }
+ // Don't modify the size constraints or fullscreen collection behavior while
+ // in fullscreen or during a transition.
+ if (!fullscreen_controller_.CanResize())
+ return;
bool shows_resize_controls =
is_resizable && (min_size.IsEmpty() || min_size != max_size);
@@ -1291,7 +1211,7 @@ void NativeWidgetNSWindowBridge::FullscreenControllerTransitionStart(
void NativeWidgetNSWindowBridge::FullscreenControllerTransitionComplete(
bool is_fullscreen) {
- DCHECK(!fullscreen_controller_->IsInFullscreenTransition());
+ DCHECK(!fullscreen_controller_.IsInFullscreenTransition());
UpdateWindowGeometry();
UpdateWindowDisplay();
@@ -1303,17 +1223,46 @@ void NativeWidgetNSWindowBridge::FullscreenControllerTransitionComplete(
void NativeWidgetNSWindowBridge::FullscreenControllerSetFrame(
const gfx::Rect& frame,
bool animate,
- base::TimeDelta& transition_time) {
+ base::OnceCallback<void()> completion_callback) {
NSRect ns_frame = gfx::ScreenRectToNSRect(frame);
+ base::TimeDelta transition_time = base::Seconds(0);
if (animate)
transition_time = base::Seconds([window_ animationResizeTime:ns_frame]);
- else
- transition_time = base::Seconds(0);
- [window_ setFrame:ns_frame display:NO animate:animate];
+
+ __block base::OnceCallback<void()> complete = std::move(completion_callback);
+ [NSAnimationContext
+ runAnimationGroup:^(NSAnimationContext* context) {
+ [context setDuration:transition_time.InSecondsF()];
+ [[window_ animator] setFrame:ns_frame display:YES animate:animate];
+ }
+ completionHandler:^{
+ std::move(complete).Run();
+ }];
}
void NativeWidgetNSWindowBridge::FullscreenControllerToggleFullscreen() {
+ // AppKit implicitly makes the fullscreen window visible, so avoid going
+ // fullscreen in headless mode. Instead, toggle the expected fullscreen state
+ // and fake the relevant callbacks for the fullscreen controller to
+ // believe the fullscreen state was toggled.
+ if (headless_mode_window_) {
+ headless_mode_window_->fullscreen_state =
+ !headless_mode_window_->fullscreen_state;
+ if (headless_mode_window_->fullscreen_state) {
+ fullscreen_controller_.OnWindowWillEnterFullscreen();
+ fullscreen_controller_.OnWindowDidEnterFullscreen();
+ } else {
+ fullscreen_controller_.OnWindowWillExitFullscreen();
+ fullscreen_controller_.OnWindowDidExitFullscreen();
+ }
+ return;
+ }
+
+ bool is_key_window = [window_ isKeyWindow];
[window_ toggleFullScreen:nil];
+ // Ensure the transitioning window maintains focus (crbug.com/1338659).
+ if (is_key_window)
+ [window_ makeKeyAndOrderFront:nil];
}
void NativeWidgetNSWindowBridge::FullscreenControllerCloseWindow() {
@@ -1321,9 +1270,7 @@ void NativeWidgetNSWindowBridge::FullscreenControllerCloseWindow() {
}
int64_t NativeWidgetNSWindowBridge::FullscreenControllerGetDisplayId() const {
- return display::Screen::GetScreen()
- ->GetDisplayNearestWindow(window_.get())
- .id();
+ return GetDisplayForWindow(window_.get()).id();
}
gfx::Rect NativeWidgetNSWindowBridge::FullscreenControllerGetFrameForDisplay(
@@ -1331,7 +1278,10 @@ gfx::Rect NativeWidgetNSWindowBridge::FullscreenControllerGetFrameForDisplay(
display::Display display;
if (display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id,
&display)) {
- return display.work_area();
+ // Use the current window size to avoid unexpected window resizes on
+ // subsequent cross-screen window drag and drops; see crbug.com/1338664
+ return gfx::Rect(display.work_area().origin(),
+ FullscreenControllerGetFrame().size());
}
return gfx::Rect();
}
@@ -1350,14 +1300,12 @@ bool NativeWidgetNSWindowBridge::ShouldWaitInPreCommit() {
return false;
if (!bridged_view_)
return false;
- if (fullscreen_controller_) {
- // Suppress synchronous CA transactions during AppKit fullscreen transition
- // since there is no need for updates during such transition.
- // Re-layout and re-paint will be done after the transition. See
- // https://crbug.com/875707 for potiential problems if we don't suppress.
- if (fullscreen_controller_->IsInFullscreenTransition())
- return false;
- }
+ // Suppress synchronous CA transactions during AppKit fullscreen transition
+ // since there is no need for updates during such transition.
+ // Re-layout and re-paint will be done after the transition. See
+ // https://crbug.com/875707 for potiential problems if we don't suppress.
+ if (fullscreen_controller_.IsInFullscreenTransition())
+ return false;
return content_dip_size_ != compositor_frame_dip_size_;
}
@@ -1389,15 +1337,7 @@ void NativeWidgetNSWindowBridge::SetVisibleOnAllSpaces(bool always_visible) {
gfx::SetNSWindowVisibleOnAllWorkspaces(window_, always_visible);
}
-void NativeWidgetNSWindowBridge::SetFullscreen(bool fullscreen) {
- DCHECK(!fullscreen_controller_);
- if (fullscreen == target_fullscreen_state_)
- return;
- ToggleDesiredFullscreenState();
-}
-
void NativeWidgetNSWindowBridge::EnterFullscreen(int64_t target_display_id) {
- DCHECK(fullscreen_controller_);
// Going fullscreen implicitly makes the window visible. AppKit does this.
// That is, -[NSWindow isVisible] is always true after a call to -[NSWindow
// toggleFullScreen:]. Unfortunately, this change happens after AppKit calls
@@ -1418,12 +1358,11 @@ void NativeWidgetNSWindowBridge::EnterFullscreen(int64_t target_display_id) {
// completes.
gfx::SetNSWindowCanFullscreen(window_, true);
- fullscreen_controller_->EnterFullscreen(target_display_id);
+ fullscreen_controller_.EnterFullscreen(target_display_id);
}
void NativeWidgetNSWindowBridge::ExitFullscreen() {
- DCHECK(fullscreen_controller_);
- fullscreen_controller_->ExitFullscreen();
+ fullscreen_controller_.ExitFullscreen();
}
void NativeWidgetNSWindowBridge::SetCanAppearInExistingFullscreenSpaces(
@@ -1440,10 +1379,20 @@ void NativeWidgetNSWindowBridge::SetCanAppearInExistingFullscreenSpaces(
}
void NativeWidgetNSWindowBridge::SetMiniaturized(bool miniaturized) {
+ // In headless mode the platform window is always hidden and WebKit
+ // will not deminiaturize hidden windows. So instead of changing the window
+ // miniaturization state just lie to the upper layer pretending the window did
+ // change its state. We don't need to keep track of the requested state here
+ // because the host will do this.
+ if (headless_mode_window_) {
+ host_->OnWindowMiniaturizedChanged(miniaturized);
+ return;
+ }
+
if (miniaturized) {
// Calling performMiniaturize: will momentarily highlight the button, but
// AppKit will reject it if there is no miniaturize button.
- if ([window_ styleMask] & NSMiniaturizableWindowMask)
+ if ([window_ styleMask] & NSWindowStyleMaskMiniaturizable)
[window_ performMiniaturize:nil];
else
[window_ miniaturize:nil];
@@ -1535,10 +1484,7 @@ void NativeWidgetNSWindowBridge::SetWindowTitle(const std::u16string& title) {
}
void NativeWidgetNSWindowBridge::ClearTouchBar() {
- if (@available(macOS 10.12.2, *)) {
- if ([bridged_view_ respondsToSelector:@selector(setTouchBar:)])
- [bridged_view_ setTouchBar:nil];
- }
+ [bridged_view_ setTouchBar:nil];
}
void NativeWidgetNSWindowBridge::UpdateTooltip() {
@@ -1597,11 +1543,7 @@ void NativeWidgetNSWindowBridge::OrderChildren() {
} else {
if (child_window.parentWindow == window)
continue;
- // Attaching a window to be a child window resets the window level, so
- // restore the window level afterwards.
- NSInteger level = child_window.level;
[window addChildWindow:child_window ordered:NSWindowAbove];
- child_window.level = level;
}
}
}
@@ -1663,12 +1605,10 @@ void NativeWidgetNSWindowBridge::UpdateWindowGeometry() {
}
void NativeWidgetNSWindowBridge::UpdateWindowDisplay() {
- if (fullscreen_controller_ &&
- fullscreen_controller_->IsInFullscreenTransition())
+ if (fullscreen_controller_.IsInFullscreenTransition())
return;
- host_->OnWindowDisplayChanged(
- display::Screen::GetScreen()->GetDisplayNearestWindow(window_.get()));
+ host_->OnWindowDisplayChanged(GetDisplayForWindow(window_.get()));
}
bool NativeWidgetNSWindowBridge::IsWindowModalSheet() const {
@@ -1718,4 +1658,13 @@ void NativeWidgetNSWindowBridge::ShowAsModalSheet() {
}
}
+bool NativeWidgetNSWindowBridge::window_visible() const {
+ // In headless mode the platform window is always hidden, so instead of
+ // returning the actual platform window visibility state tracked by
+ // OnVisibilityChanged() callback, return the expected visibility state
+ // maintained by SetVisibilityState() call.
+ return headless_mode_window_ ? headless_mode_window_->visibility_state
+ : window_visible_;
+}
+
} // namespace remote_cocoa
diff --git a/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.h b/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.h
index 4d5df029d18..df23bfe5283 100644
--- a/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.h
+++ b/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_NS_WINDOW_FULLSCREEN_CONTROLLER_H_
#define COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_NS_WINDOW_FULLSCREEN_CONTROLLER_H_
+#include "base/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
@@ -32,13 +33,12 @@ class REMOTE_COCOA_APP_SHIM_EXPORT NativeWidgetNSWindowFullscreenController {
virtual void FullscreenControllerTransitionComplete(bool is_fullscreen) = 0;
// Set the window's frame to the specified rectangle. If `animate` is true,
- // then animate the transition and populate `transition_time` with the time
- // that it will take for this transition to complete. If `animate` is false,
- // then populate `transition_time` with zero.
+ // then animate the transition. Runs the `completion_callback` callback once
+ // the animation is complete, or immediately when `animate` is false.
virtual void FullscreenControllerSetFrame(
const gfx::Rect& frame,
bool animate,
- base::TimeDelta& transition_time) = 0;
+ base::OnceCallback<void()> completion_callback) = 0;
// Call -[NSWindow toggleFullscreen:].
virtual void FullscreenControllerToggleFullscreen() = 0;
diff --git a/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.mm b/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.mm
index 2ff5dcc2cc1..d82a61204aa 100644
--- a/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.mm
+++ b/chromium/components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.mm
@@ -4,6 +4,7 @@
#include "components/remote_cocoa/app_shim/native_widget_ns_window_fullscreen_controller.h"
+#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/base/base_window.h"
@@ -34,10 +35,9 @@ void NativeWidgetNSWindowFullscreenController::EnterFullscreen(
windowed_frame_ = client_->FullscreenControllerGetFrame();
const gfx::Rect kFakeFullscreenRect(0, 0, 1024, 768);
- base::TimeDelta animation_time;
client_->FullscreenControllerSetFrame(kFakeFullscreenRect,
- /*animate=*/false, animation_time);
-
+ /*animate=*/false,
+ base::DoNothing());
state_ = State::kFullscreen;
client_->FullscreenControllerTransitionComplete(true);
}
@@ -71,11 +71,9 @@ void NativeWidgetNSWindowFullscreenController::ExitFullscreen() {
if (state_ == State::kFullscreen) {
state_ = State::kExitFullscreenTransition;
client_->FullscreenControllerTransitionStart(false);
-
- base::TimeDelta animation_time;
client_->FullscreenControllerSetFrame(windowed_frame_.value(),
- /*animate=*/false, animation_time);
-
+ /*animate=*/false,
+ base::DoNothing());
state_ = State::kWindowed;
client_->FullscreenControllerTransitionComplete(false);
}
@@ -102,26 +100,15 @@ void NativeWidgetNSWindowFullscreenController::
gfx::Rect display_frame =
client_->FullscreenControllerGetFrameForDisplay(target_display_id);
- base::TimeDelta animation_time;
if (!display_frame.IsEmpty()) {
restore_windowed_frame_ = true;
- client_->FullscreenControllerSetFrame(display_frame, /*animate=*/true,
- animation_time);
+ SetStateAndCancelPostedTasks(State::kEnterFullscreenTransition);
+ client_->FullscreenControllerSetFrame(
+ display_frame, /*animate=*/true,
+ base::BindOnce(
+ &NativeWidgetNSWindowFullscreenController::ToggleFullscreen,
+ weak_factory_.GetWeakPtr()));
}
-
- // Calling toggleFullscreen immediately after calling setFrame causes the
- // fullscreen toggle to happen on the target display, and does not cause
- // unexpected focus losses. This is the desired behavior. Were this not
- // the case, we could consider not posting ToggleFullscreen until after
- // `animation_time` has completed and we have received the notification
- // NSWorkspaceActiveDisplayDidChangeNotification.
- // https://crbug.com/1210548#c27
- SetStateAndCancelPostedTasks(State::kEnterFullscreenTransition);
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(
- &NativeWidgetNSWindowFullscreenController::ToggleFullscreen,
- weak_factory_.GetWeakPtr()));
}
void NativeWidgetNSWindowFullscreenController::RestoreWindowedFrame() {
@@ -129,9 +116,8 @@ void NativeWidgetNSWindowFullscreenController::RestoreWindowedFrame() {
DCHECK(restore_windowed_frame_);
DCHECK(windowed_frame_);
- base::TimeDelta animation_time;
client_->FullscreenControllerSetFrame(windowed_frame_.value(),
- /*animate=*/true, animation_time);
+ /*animate=*/true, base::DoNothing());
restore_windowed_frame_ = false;
windowed_frame_.reset();
diff --git a/chromium/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm b/chromium/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
index a9befe6673d..1feb0b172cc 100644
--- a/chromium/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
+++ b/chromium/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
@@ -16,7 +16,6 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/hang_watcher.h"
#include "base/threading/thread_restrictions.h"
-#import "ui/base/cocoa/controls/textfield_utils.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/strings/grit/ui_strings.h"
@@ -48,9 +47,9 @@ NSString* GetDescriptionFromExtension(const base::FilePath::StringType& ext) {
base::scoped_nsobject<NSView> CreateAccessoryView() {
// The label. Add attributes per-OS to match the labels that macOS uses.
- NSTextField* label = [TextFieldUtils
- labelWithString:l10n_util::GetNSString(
- IDS_SAVE_PAGE_FILE_FORMAT_PROMPT_MAC)];
+ NSTextField* label =
+ [NSTextField labelWithString:l10n_util::GetNSString(
+ IDS_SAVE_PAGE_FILE_FORMAT_PROMPT_MAC)];
label.translatesAutoresizingMaskIntoConstraints = NO;
if (base::mac::IsAtLeastOS10_14())
label.textColor = NSColor.secondaryLabelColor;
@@ -145,15 +144,6 @@ base::scoped_nsobject<NSView> CreateAccessoryView() {
[constraints
addObject:[view.bottomAnchor constraintEqualToAnchor:group.bottomAnchor]];
- // Maybe minimum width (through macOS 10.12).
- if (base::mac::IsAtMostOS10_12()) {
- // Through macOS 10.12, the file dialog didn't properly constrain the width
- // of the accessory view. Therefore, add in a "can you please make this at
- // least so big" constraint in so it doesn't collapse width-wise.
- [constraints addObject:[view.widthAnchor
- constraintGreaterThanOrEqualToConstant:400]];
- }
-
[NSLayoutConstraint activateConstraints:constraints];
return scoped_view;
@@ -192,7 +182,7 @@ NSSavePanel* g_last_created_panel_for_testing = nil;
// Refuse to accept users closing the dialog with a key repeat, since the key
// may have been first pressed while the user was looking at insecure content.
// See https://crbug.com/637098.
- if ([[NSApp currentEvent] type] == NSKeyDown &&
+ if ([[NSApp currentEvent] type] == NSEventTypeKeyDown &&
[[NSApp currentEvent] isARepeat]) {
return NO;
}
@@ -365,7 +355,7 @@ void SelectFileDialogBridge::Show(
weak_factory_.GetWeakPtr());
[dialog beginSheetModalForWindow:owning_window_
completionHandler:^(NSInteger result) {
- callback.Run(result != NSFileHandlingPanelOKButton);
+ callback.Run(result != NSModalResponseOK);
}];
}
diff --git a/chromium/components/remote_cocoa/app_shim/views_nswindow_delegate.mm b/chromium/components/remote_cocoa/app_shim/views_nswindow_delegate.mm
index ede47ec6893..e75bdb1a641 100644
--- a/chromium/components/remote_cocoa/app_shim/views_nswindow_delegate.mm
+++ b/chromium/components/remote_cocoa/app_shim/views_nswindow_delegate.mm
@@ -205,45 +205,22 @@
}
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
- if (_parent->fullscreen_controller())
- _parent->fullscreen_controller()->OnWindowWillEnterFullscreen();
- else
- _parent->OnFullscreenTransitionStart(true);
+ _parent->fullscreen_controller().OnWindowWillEnterFullscreen();
}
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
- if (_parent->fullscreen_controller())
- _parent->fullscreen_controller()->OnWindowDidEnterFullscreen();
- else
- _parent->OnFullscreenTransitionComplete(true);
+ _parent->fullscreen_controller().OnWindowDidEnterFullscreen();
}
- (void)windowWillExitFullScreen:(NSNotification*)notification {
- if (_parent->fullscreen_controller())
- _parent->fullscreen_controller()->OnWindowWillExitFullscreen();
- else
- _parent->OnFullscreenTransitionStart(false);
+ _parent->fullscreen_controller().OnWindowWillExitFullscreen();
}
- (void)windowDidExitFullScreen:(NSNotification*)notification {
- if (base::mac::IsOS10_12()) {
- // There is a window activation/fullscreen bug present only in macOS 10.12
- // that might cause a security surface to appear over the wrong parent
- // window. As much as this code appears to be a no-op, it is not; it causes
- // AppKit to shuffle all the windows around to properly obey the
- // relationships that they should already be obeying.
- [[NSApp orderedWindows][0] performSelector:@selector(orderFront:)
- withObject:self
- afterDelay:0];
- }
-
- if (_parent->fullscreen_controller())
- _parent->fullscreen_controller()->OnWindowDidExitFullscreen();
- else
- _parent->OnFullscreenTransitionComplete(false);
+ _parent->fullscreen_controller().OnWindowDidExitFullscreen();
}
-// Allow non-resizable windows (without NSResizableWindowMask) to fill the
+// Allow non-resizable windows (without NSWindowStyleMaskResizable) to fill the
// screen in fullscreen mode. This only happens when
// -[NSWindow toggleFullscreen:] is called since non-resizable windows have no
// fullscreen button. Without this they would only enter fullscreen at their
diff --git a/chromium/components/remote_cocoa/app_shim/window_move_loop.mm b/chromium/components/remote_cocoa/app_shim/window_move_loop.mm
index 66dc541768c..eb93843006d 100644
--- a/chromium/components/remote_cocoa/app_shim/window_move_loop.mm
+++ b/chromium/components/remote_cocoa/app_shim/window_move_loop.mm
@@ -86,8 +86,8 @@ bool CocoaWindowMoveLoop::Run() {
// Esc keypress is handled by EscapeTracker, which is installed by
// TabDragController.
- NSEventMask mask =
- NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSMouseMovedMask;
+ NSEventMask mask = NSEventMaskLeftMouseUp | NSEventMaskLeftMouseDragged |
+ NSEventMaskMouseMoved;
auto handler = ^NSEvent*(NSEvent* event) {
// The docs say this always runs on the main thread, but if it didn't,
// it would explain https://crbug.com/876493, so let's make sure.
@@ -102,7 +102,7 @@ bool CocoaWindowMoveLoop::Run() {
return event;
}
- if ([event type] == NSLeftMouseDragged) {
+ if ([event type] == NSEventTypeLeftMouseDragged) {
const NSPoint mouse_in_screen = [NSEvent mouseLocation];
NSRect ns_frame = NSOffsetRect(
@@ -119,12 +119,12 @@ bool CocoaWindowMoveLoop::Run() {
return event;
}
- // In theory, we shouldn't see any kind of NSMouseMoved, but if we see one
- // and the left button isn't pressed, we know for a fact that we missed a
- // NSLeftMouseUp.
- BOOL unexpectedMove = [event type] == NSMouseMoved &&
+ // In theory, we shouldn't see any kind of NSEventTypeMouseMoved, but if we
+ // see one and the left button isn't pressed, we know for a fact that we
+ // missed a NSEventTypeLeftMouseUp.
+ BOOL unexpectedMove = [event type] == NSEventTypeMouseMoved &&
([NSEvent pressedMouseButtons] & 1) != 1;
- if (unexpectedMove || [event type] == NSLeftMouseUp) {
+ if (unexpectedMove || [event type] == NSEventTypeLeftMouseUp) {
*strong->exit_reason_ref_ = MOUSE_UP;
std::move(strong->quit_closure_).Run();
}
diff --git a/chromium/components/remote_cocoa/app_shim/window_touch_bar_delegate.h b/chromium/components/remote_cocoa/app_shim/window_touch_bar_delegate.h
index 30d751ca058..dfd5c053753 100644
--- a/chromium/components/remote_cocoa/app_shim/window_touch_bar_delegate.h
+++ b/chromium/components/remote_cocoa/app_shim/window_touch_bar_delegate.h
@@ -13,7 +13,7 @@
@protocol WindowTouchBarDelegate <NSObject>
// Creates and returns a touch bar for the browser window.
-- (NSTouchBar*)makeTouchBar API_AVAILABLE(macos(10.12.2));
+- (NSTouchBar*)makeTouchBar;
@end
diff --git a/chromium/components/remote_cocoa/common/BUILD.gn b/chromium/components/remote_cocoa/common/BUILD.gn
index 5005f8ee78b..6a35864a818 100644
--- a/chromium/components/remote_cocoa/common/BUILD.gn
+++ b/chromium/components/remote_cocoa/common/BUILD.gn
@@ -21,6 +21,7 @@ mojom("mojo") {
"//mojo/public/mojom/base",
"//services/network/public/mojom",
"//ui/base/accelerators/mojom",
+ "//ui/base/cursor/mojom",
"//ui/base/mojom",
"//ui/display/mojom",
"//ui/events/mojom",
diff --git a/chromium/components/remote_cocoa/common/application.mojom b/chromium/components/remote_cocoa/common/application.mojom
index f011beba97c..ff18c64d28a 100644
--- a/chromium/components/remote_cocoa/common/application.mojom
+++ b/chromium/components/remote_cocoa/common/application.mojom
@@ -48,14 +48,17 @@ interface Application {
// Create and take ownership of the NSView for a RenderWidgetHostView. The
// resulting object will be destroyed when the connection is closed.
+ // The value of |view_id| may be used to look up the NSView (e.g, to add
+ // child NSViews or get a point relative to that NSView).
CreateRenderWidgetHostNSView(
+ uint64 view_id,
pending_associated_remote<StubInterface> host,
pending_associated_receiver<StubInterface> view_receiver);
// Create and take ownership of the NSView for a WebContentsView. The
// resulting object will be destroyed when the connection is closed.
// The value of |view_id| may be used to look up the NSView (e.g, to add
- // child NSViews.
+ // child NSViews).
CreateWebContentsNSView(
uint64 view_id,
pending_associated_remote<StubInterface> host,
diff --git a/chromium/components/remote_cocoa/common/native_widget_ns_window.mojom b/chromium/components/remote_cocoa/common/native_widget_ns_window.mojom
index 9414d5337b4..98292d625a4 100644
--- a/chromium/components/remote_cocoa/common/native_widget_ns_window.mojom
+++ b/chromium/components/remote_cocoa/common/native_widget_ns_window.mojom
@@ -7,6 +7,7 @@ module remote_cocoa.mojom;
import "components/remote_cocoa/common/select_file_dialog.mojom";
import "mojo/public/mojom/base/string16.mojom";
import "services/network/public/mojom/network_param.mojom";
+import "ui/base/cursor/mojom/cursor.mojom";
import "ui/base/mojom/ui_base_types.mojom";
import "ui/events/mojom/event_constants.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
@@ -72,7 +73,7 @@ struct NativeWidgetNSWindowInitParams {
bool has_window_server_shadow;
// If true, the NSWindow's collection behavior is set to include
// NSWindowCollectionBehaviorParticipatesInCycle (this is not the
- // default for NSWindows with NSBorderlessWindowMask).
+ // default for NSWindows with NSWindowStyleMaskBorderless).
bool force_into_collection_cycle;
// If true, the window was created in headless mode.
bool is_headless_mode_window;
@@ -90,14 +91,6 @@ enum WindowControlsOverlayNSViewType {
// The interface through which a NativeWidgetMac may interact with an NSWindow
// (possibly in a process separate from the browser process).
interface NativeWidgetNSWindow {
- // Enable fullscreen control through NativeWidgetNSWindowFullscreenController.
- // This is done via a method instead of using a base::Feature because the app
- // shim process does not synchronize its base::Features with the browser
- // process.
- // TODO(https://crbug.com/1302857): Remove this once FullscreenControllerMac
- // is on by default.
- CreateFullscreenController();
-
// Create and set the NSWindow for the bridge.
CreateWindow(CreateWindowParams params);
@@ -179,12 +172,6 @@ interface NativeWidgetNSWindow {
// on all spaces.
SetVisibleOnAllSpaces(bool always_visible);
- // Called by NativeWidgetMac to initiate a transition to the specified target
- // fullscreen state.
- // TODO(https://crbug.com/1302857): Remove this once FullscreenControllerMac
- // is on by default.
- SetFullscreen(bool fullscreen);
-
// Initiate a transition to fullscreen on the specified display. If
// `target_display_id` is invalid, then fullscreen should be entered on the
// current display.
@@ -264,4 +251,7 @@ interface NativeWidgetNSWindow {
// overlay display override for a |overlay_type|.
RemoveWindowControlsOverlayNSView(
WindowControlsOverlayNSViewType overlay_type);
+
+ // Set the cursor type to display.
+ SetCursor(ui.mojom.Cursor cursor);
};
diff --git a/chromium/components/renderer_context_menu/context_menu_content_type.cc b/chromium/components/renderer_context_menu/context_menu_content_type.cc
index 08a46044b06..e48fcf29f94 100644
--- a/chromium/components/renderer_context_menu/context_menu_content_type.cc
+++ b/chromium/components/renderer_context_menu/context_menu_content_type.cc
@@ -159,6 +159,10 @@ bool ContextMenuContentType::SupportsGroupInternal(int group) {
return params_.input_field_type ==
blink::mojom::ContextMenuDataInputFieldType::kPassword;
+ case ITEM_GROUP_AUTOFILL:
+ return params_.input_field_type !=
+ blink::mojom::ContextMenuDataInputFieldType::kNone;
+
default:
NOTREACHED();
return false;
diff --git a/chromium/components/renderer_context_menu/context_menu_content_type.h b/chromium/components/renderer_context_menu/context_menu_content_type.h
index cfd3c4598ef..45a3c1f6a5e 100644
--- a/chromium/components/renderer_context_menu/context_menu_content_type.h
+++ b/chromium/components/renderer_context_menu/context_menu_content_type.h
@@ -49,7 +49,8 @@ class ContextMenuContentType {
ITEM_GROUP_DEVTOOLS_UNPACKED_EXT,
ITEM_GROUP_PRINT_PREVIEW,
ITEM_GROUP_PASSWORD,
- ITEM_GROUP_EXISTING_LINK_TO_TEXT
+ ITEM_GROUP_EXISTING_LINK_TO_TEXT,
+ ITEM_GROUP_AUTOFILL
};
// Returns if |group| is enabled.
diff --git a/chromium/components/renderer_context_menu/render_view_context_menu_base.cc b/chromium/components/renderer_context_menu/render_view_context_menu_base.cc
index a3ba440acb3..92bf0cec02a 100644
--- a/chromium/components/renderer_context_menu/render_view_context_menu_base.cc
+++ b/chromium/components/renderer_context_menu/render_view_context_menu_base.cc
@@ -322,7 +322,7 @@ void RenderViewContextMenuBase::RemoveSeparatorBeforeMenuItem(int command_id) {
}
RenderViewHost* RenderViewContextMenuBase::GetRenderViewHost() const {
- return source_web_contents_->GetMainFrame()->GetRenderViewHost();
+ return source_web_contents_->GetPrimaryMainFrame()->GetRenderViewHost();
}
WebContents* RenderViewContextMenuBase::GetWebContents() const {
diff --git a/chromium/components/reporting/client/mock_dm_token_retriever.cc b/chromium/components/reporting/client/mock_dm_token_retriever.cc
index e7c849544d1..17988da2e85 100644
--- a/chromium/components/reporting/client/mock_dm_token_retriever.cc
+++ b/chromium/components/reporting/client/mock_dm_token_retriever.cc
@@ -11,11 +11,11 @@
#include "components/reporting/client/dm_token_retriever.h"
#include "components/reporting/util/statusor.h"
-namespace reporting {
-
using ::base::test::RunOnceCallback;
using ::testing::_;
+namespace reporting {
+
MockDMTokenRetriever::MockDMTokenRetriever() = default;
MockDMTokenRetriever::~MockDMTokenRetriever() = default;
diff --git a/chromium/components/reporting/client/mock_dm_token_retriever.h b/chromium/components/reporting/client/mock_dm_token_retriever.h
index 9b6618cea18..56895e7a4d2 100644
--- a/chromium/components/reporting/client/mock_dm_token_retriever.h
+++ b/chromium/components/reporting/client/mock_dm_token_retriever.h
@@ -5,9 +5,11 @@
#ifndef COMPONENTS_REPORTING_CLIENT_MOCK_DM_TOKEN_RETRIEVER_H_
#define COMPONENTS_REPORTING_CLIENT_MOCK_DM_TOKEN_RETRIEVER_H_
+#include <cstddef>
#include <string>
#include "components/reporting/client/dm_token_retriever.h"
+#include "components/reporting/util/statusor.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace reporting {
diff --git a/chromium/components/reporting/client/mock_report_queue.cc b/chromium/components/reporting/client/mock_report_queue.cc
index 3738cb66115..035ba4dbb52 100644
--- a/chromium/components/reporting/client/mock_report_queue.cc
+++ b/chromium/components/reporting/client/mock_report_queue.cc
@@ -4,10 +4,34 @@
#include "components/reporting/client/mock_report_queue.h"
+#include <utility>
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::Invoke;
+
namespace reporting {
-MockReportQueue::MockReportQueue() = default;
+MockReportQueueStrict::MockReportQueueStrict() {
+ // Default action makes a synchronous call to record_producer and passes the
+ // result over to plain-text mock AddRecord. Can be overridden, if necessary.
+ ON_CALL(*this, AddProducedRecord)
+ .WillByDefault(
+ Invoke(this, &MockReportQueueStrict::ForwardProducedRecord));
+}
-MockReportQueue::~MockReportQueue() = default;
+MockReportQueueStrict::~MockReportQueueStrict() = default;
+void MockReportQueueStrict::ForwardProducedRecord(
+ RecordProducer record_producer,
+ Priority priority,
+ EnqueueCallback callback) {
+ auto record_result = std::move(record_producer).Run();
+ if (!record_result.ok()) {
+ std::move(callback).Run(record_result.status());
+ return;
+ }
+ AddRecord(std::move(record_result.ValueOrDie()), priority,
+ std::move(callback));
+}
} // namespace reporting
diff --git a/chromium/components/reporting/client/mock_report_queue.h b/chromium/components/reporting/client/mock_report_queue.h
index 89bda0f6b6d..fd38ea78208 100644
--- a/chromium/components/reporting/client/mock_report_queue.h
+++ b/chromium/components/reporting/client/mock_report_queue.h
@@ -6,6 +6,7 @@
#define COMPONENTS_REPORTING_CLIENT_MOCK_REPORT_QUEUE_H_
#include <memory>
+#include <string>
#include "base/callback.h"
#include "components/reporting/client/report_queue.h"
@@ -18,25 +19,43 @@
namespace reporting {
// A mock of ReportQueue for use in testing.
-class MockReportQueue : public ReportQueue {
+class MockReportQueueStrict : public ReportQueue {
public:
- MockReportQueue();
- ~MockReportQueue() override;
+ MockReportQueueStrict();
+ ~MockReportQueueStrict() override;
+ // Mock AddRecord with record producer.
+ // Rarely used, by default calls plain-text AddRecord.
MOCK_METHOD(void,
- AddRecord,
- (base::StringPiece, Priority, ReportQueue::EnqueueCallback),
+ AddProducedRecord,
+ (RecordProducer, Priority, EnqueueCallback),
(const override));
- MOCK_METHOD(void, Flush, (Priority, ReportQueue::FlushCallback), (override));
+ MOCK_METHOD(void,
+ AddRecord,
+ (std::string, Priority, EnqueueCallback),
+ (const));
+
+ MOCK_METHOD(void, Flush, (Priority, FlushCallback), (override));
MOCK_METHOD(
(base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)>),
PrepareToAttachActualQueue,
(),
(const override));
+
+ private:
+ // Helper method that executes |record_producer| and in case of success
+ // forwards the result to |AddRecord|. In case of failure passes Status to
+ // |callback|.
+ void ForwardProducedRecord(RecordProducer record_producer,
+ Priority priority,
+ EnqueueCallback callback);
};
+// Most of the time no need to log uninterested calls.
+typedef ::testing::NiceMock<MockReportQueueStrict> MockReportQueue;
+
} // namespace reporting
#endif // COMPONENTS_REPORTING_CLIENT_MOCK_REPORT_QUEUE_H_
diff --git a/chromium/components/reporting/client/mock_report_queue_provider.cc b/chromium/components/reporting/client/mock_report_queue_provider.cc
index bb4d59380f1..e225d4457dd 100644
--- a/chromium/components/reporting/client/mock_report_queue_provider.cc
+++ b/chromium/components/reporting/client/mock_report_queue_provider.cc
@@ -63,7 +63,7 @@ void MockReportQueueProvider::
.WillRepeatedly([]() {
auto report_queue =
std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>(
- new NiceMock<MockReportQueue>(),
+ new MockReportQueue(),
base::OnTaskRunnerDeleter(
base::ThreadPool::CreateSequencedTaskRunner({})));
diff --git a/chromium/components/reporting/client/report_queue.cc b/chromium/components/reporting/client/report_queue.cc
index 59a6d1ba7e0..6fcb9cac1de 100644
--- a/chromium/components/reporting/client/report_queue.cc
+++ b/chromium/components/reporting/client/report_queue.cc
@@ -26,7 +26,7 @@ namespace reporting {
namespace {
-StatusOr<std::string> ValueToJson(const base::Value::Dict& record) {
+StatusOr<std::string> ValueToJson(base::Value::Dict record) {
std::string json_record;
if (!base::JSONWriter::Write(record, &json_record)) {
return Status(error::INVALID_ARGUMENT,
@@ -36,7 +36,7 @@ StatusOr<std::string> ValueToJson(const base::Value::Dict& record) {
}
StatusOr<std::string> ProtoToString(
- const google::protobuf::MessageLite* record) {
+ std::unique_ptr<const google::protobuf::MessageLite> record) {
std::string protobuf_record;
if (!record->SerializeToString(&protobuf_record)) {
return Status(error::INVALID_ARGUMENT,
@@ -50,26 +50,30 @@ StatusOr<std::string> ProtoToString(
ReportQueue::~ReportQueue() = default;
-void ReportQueue::Enqueue(base::StringPiece record,
+void ReportQueue::Enqueue(std::string record,
Priority priority,
ReportQueue::EnqueueCallback callback) const {
- AddRecord(record, priority, std::move(callback));
+ AddProducedRecord(base::BindOnce(
+ [](std::string record) -> StatusOr<std::string> {
+ return std::move(record);
+ },
+ std::move(record)),
+ priority, std::move(callback));
}
-void ReportQueue::Enqueue(const base::Value::Dict& record,
+void ReportQueue::Enqueue(base::Value::Dict record,
Priority priority,
ReportQueue::EnqueueCallback callback) const {
- ASSIGN_OR_ONCE_CALLBACK_AND_RETURN(std::string json_record, callback,
- ValueToJson(record));
- AddRecord(json_record, priority, std::move(callback));
+ AddProducedRecord(base::BindOnce(&ValueToJson, std::move(record)), priority,
+ std::move(callback));
}
-void ReportQueue::Enqueue(const google::protobuf::MessageLite* record,
- Priority priority,
- ReportQueue::EnqueueCallback callback) const {
- ASSIGN_OR_ONCE_CALLBACK_AND_RETURN(std::string protobuf_record, callback,
- ProtoToString(record));
- AddRecord(protobuf_record, priority, std::move(callback));
+void ReportQueue::Enqueue(
+ std::unique_ptr<const google::protobuf::MessageLite> record,
+ Priority priority,
+ ReportQueue::EnqueueCallback callback) const {
+ AddProducedRecord(base::BindOnce(&ProtoToString, std::move(record)), priority,
+ std::move(callback));
}
} // namespace reporting
diff --git a/chromium/components/reporting/client/report_queue.h b/chromium/components/reporting/client/report_queue.h
index 065890a4026..a6e76ace8cd 100644
--- a/chromium/components/reporting/client/report_queue.h
+++ b/chromium/components/reporting/client/report_queue.h
@@ -104,6 +104,9 @@ namespace reporting {
class ReportQueue {
public:
+ // A callback to asynchronously generate data to be added to |Storage|.
+ using RecordProducer = base::OnceCallback<StatusOr<std::string>()>;
+
// An EnqueueCallback is called on the completion of any |Enqueue| call.
using EnqueueCallback = base::OnceCallback<void(Status)>;
@@ -122,20 +125,21 @@ class ReportQueue {
// (destination : requirement)
// UPLOAD_EVENTS : UploadEventsRequest
//
- // |record| will be sent as a string with no conversion.
- void Enqueue(base::StringPiece record,
+ // |record| string (owned) will be sent with no conversion.
+ void Enqueue(std::string record,
Priority priority,
EnqueueCallback callback) const;
- // |record| will be converted to a JSON string with base::JsonWriter::Write.
- void Enqueue(const base::Value::Dict& record,
+ // |record| as a dictionary (owned) will be converted to a JSON string with
+ // base::JsonWriter::Write.
+ void Enqueue(base::Value::Dict record,
Priority priority,
EnqueueCallback callback) const;
- // |record| will be converted to a string with SerializeToString(). The
- // handler is responsible for converting the record back to a proto with a
- // ParseFromString() call.
- void Enqueue(const google::protobuf::MessageLite* record,
+ // |record| as a protobuf (owned) will be converted to a string with
+ // SerializeToString(). The handler is responsible for converting the record
+ // back to a proto with a ParseFromString() call.
+ void Enqueue(std::unique_ptr<const google::protobuf::MessageLite> record,
Priority priority,
EnqueueCallback callback) const;
@@ -147,13 +151,20 @@ class ReportQueue {
// Prepares a callback to attach actual queue to the speculative.
// Implemented only in SpeculativeReportQueue, CHECKs in a regular one.
- virtual base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)>
+ [[nodiscard]] virtual base::OnceCallback<
+ void(StatusOr<std::unique_ptr<ReportQueue>>)>
PrepareToAttachActualQueue() const = 0;
- protected:
- virtual void AddRecord(base::StringPiece record,
- Priority priority,
- EnqueueCallback callback) const = 0;
+ private:
+ // Allow SpeculativeReportQueue access to |AddProducedRecord|.
+ friend class SpeculativeReportQueueImpl;
+
+ // Invokes |record_producer| and posts resulting data to the queue storage.
+ // |record_producer| is expected to be called asynchronously.
+ // Should only be used within ReportQueue implementation and its derivatives.
+ virtual void AddProducedRecord(RecordProducer record_producer,
+ Priority priority,
+ EnqueueCallback callback) const = 0;
};
} // namespace reporting
diff --git a/chromium/components/reporting/client/report_queue_factory.cc b/chromium/components/reporting/client/report_queue_factory.cc
index 7e9d7a198af..d963a6c1f40 100644
--- a/chromium/components/reporting/client/report_queue_factory.cc
+++ b/chromium/components/reporting/client/report_queue_factory.cc
@@ -22,29 +22,6 @@
namespace reporting {
// static
-void ReportQueueFactory::Create(base::StringPiece dm_token,
- Destination destination,
- SuccessCallback success_cb) {
- DCHECK(base::ThreadTaskRunnerHandle::IsSet());
-
- auto config_result = ReportQueueConfiguration::Create(
- dm_token, destination,
- base::BindRepeating([]() { return Status::StatusOK(); }));
- if (!config_result.ok()) {
- LOG_WITH_STATUS(1, "ReportQueueConfiguration is invalid.", config_result);
- return;
- }
-
- // Asynchronously create and try to set ReportQueue.
- auto try_set_cb = CreateTrySetCallback(destination, std::move(success_cb),
- GetBackoffEntry());
- base::ThreadPool::PostTask(
- FROM_HERE, base::BindOnce(ReportQueueProvider::CreateQueue,
- std::move(config_result.ValueOrDie()),
- std::move(try_set_cb)));
-}
-
-// static
void ReportQueueFactory::Create(EventType event_type,
Destination destination,
SuccessCallback success_cb) {
@@ -68,65 +45,30 @@ void ReportQueueFactory::Create(EventType event_type,
}
// static
-std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter>
-ReportQueueFactory::CreateSpeculativeReportQueue(
- base::StringPiece dm_token_value,
- Destination destination) {
- DCHECK(base::SequencedTaskRunnerHandle::IsSet());
-
- auto config_result = ::reporting::ReportQueueConfiguration::Create(
- dm_token_value, destination,
- base::BindRepeating([]() { return ::reporting::Status::StatusOK(); }));
-
- if (!config_result.ok()) {
- DVLOG(1)
- << "Cannot initialize report queue. Invalid ReportQueueConfiguration: "
- << config_result.status();
- return std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter>(
- nullptr,
- base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get()));
- }
-
- auto speculative_queue_result =
- ::reporting::ReportQueueProvider::CreateSpeculativeQueue(
- std::move(config_result.ValueOrDie()));
- if (!speculative_queue_result.ok()) {
- DVLOG(1) << "Failed to create speculative queue: "
- << speculative_queue_result.status();
- return std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter>(
- nullptr,
- base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get()));
- }
-
- return std::move(speculative_queue_result.ValueOrDie());
-}
-
-// static
-std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter>
+std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter>
ReportQueueFactory::CreateSpeculativeReportQueue(EventType event_type,
Destination destination) {
DCHECK(base::SequencedTaskRunnerHandle::IsSet());
- auto config_result = ::reporting::ReportQueueConfiguration::Create(
+ auto config_result = ReportQueueConfiguration::Create(
event_type, destination,
- base::BindRepeating([]() { return ::reporting::Status::StatusOK(); }));
+ base::BindRepeating([]() { return Status::StatusOK(); }));
if (!config_result.ok()) {
DVLOG(1)
<< "Cannot initialize report queue. Invalid ReportQueueConfiguration: "
<< config_result.status();
- return std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter>(
+ return std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter>(
nullptr,
base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get()));
}
- auto speculative_queue_result =
- ::reporting::ReportQueueProvider::CreateSpeculativeQueue(
- std::move(config_result.ValueOrDie()));
+ auto speculative_queue_result = ReportQueueProvider::CreateSpeculativeQueue(
+ std::move(config_result.ValueOrDie()));
if (!speculative_queue_result.ok()) {
DVLOG(1) << "Failed to create speculative queue: "
<< speculative_queue_result.status();
- return std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter>(
+ return std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter>(
nullptr,
base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get()));
}
@@ -138,7 +80,7 @@ ReportQueueFactory::TrySetReportQueueCallback
ReportQueueFactory::CreateTrySetCallback(
Destination destination,
SuccessCallback success_cb,
- std::unique_ptr<net::BackoffEntry> backoff_entry) {
+ std::unique_ptr<::net::BackoffEntry> backoff_entry) {
return base::BindPostTask(
base::ThreadTaskRunnerHandle::Get(),
base::BindOnce(&ReportQueueFactory::TrySetReportQueue,
diff --git a/chromium/components/reporting/client/report_queue_factory.h b/chromium/components/reporting/client/report_queue_factory.h
index cec30cfc094..0efef47eb58 100644
--- a/chromium/components/reporting/client/report_queue_factory.h
+++ b/chromium/components/reporting/client/report_queue_factory.h
@@ -13,10 +13,7 @@
#include "components/reporting/client/report_queue.h"
#include "components/reporting/client/report_queue_configuration.h"
#include "components/reporting/util/statusor.h"
-
-namespace net {
-class BackoffEntry;
-} // namespace net
+#include "net/base/backoff_entry.h"
namespace reporting {
@@ -44,21 +41,11 @@ class ReportQueueFactory {
using TrySetReportQueueCallback =
base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)>;
- // Deprecated
- static void Create(base::StringPiece dm_token_value,
- Destination destination,
- SuccessCallback done_cb);
-
static void Create(EventType event_type,
Destination destination,
SuccessCallback done_cb);
- // Deprecated
- static std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter>
- CreateSpeculativeReportQueue(base::StringPiece dm_token_value,
- Destination destination);
-
- static std::unique_ptr<::reporting::ReportQueue, base::OnTaskRunnerDeleter>
+ static std::unique_ptr<ReportQueue, base::OnTaskRunnerDeleter>
CreateSpeculativeReportQueue(EventType event_type, Destination destination);
private:
@@ -69,7 +56,7 @@ class ReportQueueFactory {
static TrySetReportQueueCallback CreateTrySetCallback(
Destination destination,
SuccessCallback success_cb,
- std::unique_ptr<net::BackoffEntry> backoff_entry);
+ std::unique_ptr<::net::BackoffEntry> backoff_entry);
};
} // namespace reporting
diff --git a/chromium/components/reporting/client/report_queue_factory_unittest.cc b/chromium/components/reporting/client/report_queue_factory_unittest.cc
index 7f3707af31f..86f02042f59 100644
--- a/chromium/components/reporting/client/report_queue_factory_unittest.cc
+++ b/chromium/components/reporting/client/report_queue_factory_unittest.cc
@@ -72,23 +72,6 @@ class ReportQueueFactoryTest : public ::testing::Test {
std::unique_ptr<MockReportQueueProvider> provider_;
};
-// Tests deprecated flow and will be deleted once all consumers are migrated
-// over to use event types instead
-TEST_F(ReportQueueFactoryTest, CreateAndGetQueueUsingDMToken) {
- // Initially the queue must be an uninitialized unique_ptr
- EXPECT_FALSE(consumer_->GetReportQueue());
- {
- test::TestCallbackAutoWaiter set_waiter;
- reporting::ReportQueueFactory::Create(
- /*dm_token_value=*/"TOKEN", destination_,
- consumer_->GetReportQueueSetter(&set_waiter));
- EXPECT_CALL(*provider_.get(), OnInitCompletedMock()).Times(1);
- provider_->ExpectCreateNewQueueAndReturnNewMockQueue(1);
- }
- // We expect the report queue to be existing in the consumer.
- EXPECT_TRUE(consumer_->GetReportQueue());
-}
-
TEST_F(ReportQueueFactoryTest, CreateAndGetQueue) {
// Initially the queue must be an uninitialized unique_ptr
EXPECT_FALSE(consumer_->GetReportQueue());
@@ -132,20 +115,6 @@ TEST_F(ReportQueueFactoryTest, CreateSpeculativeQueueWithInvalidConfig) {
EXPECT_THAT(report_queue, IsNull());
}
-TEST_F(ReportQueueFactoryTest, EmptyDmToken) {
- // Initially the queue must be an uninitialized unique_ptr
- EXPECT_FALSE(consumer_->GetReportQueue());
- {
- test::TestCallbackAutoWaiter set_waiter;
- reporting::ReportQueueFactory::Create(
- "", destination_, consumer_->GetReportQueueSetter(&set_waiter));
- EXPECT_CALL(*provider_.get(), OnInitCompletedMock()).Times(1);
- provider_->ExpectCreateNewQueueAndReturnNewMockQueue(1);
- }
- // We expect the report queue to be existing in the consumer.
- EXPECT_TRUE(consumer_->GetReportQueue());
-}
-
// Tests if two consumers use the same provider and create two queues.
TEST_F(ReportQueueFactoryTest, SameProviderForMultipleThreads) {
auto consumer2 = std::make_unique<MockReportQueueConsumer>();
diff --git a/chromium/components/reporting/client/report_queue_impl.cc b/chromium/components/reporting/client/report_queue_impl.cc
index 8986ab35e83..9f564c8b272 100644
--- a/chromium/components/reporting/client/report_queue_impl.cc
+++ b/chromium/components/reporting/client/report_queue_impl.cc
@@ -28,10 +28,49 @@
#include "components/reporting/proto/synced/record_constants.pb.h"
#include "components/reporting/storage/storage_module_interface.h"
#include "components/reporting/util/status.h"
-#include "components/reporting/util/status_macros.h"
#include "components/reporting/util/statusor.h"
namespace reporting {
+namespace {
+// Calls |record_producer|, checks the result and in case of success, forwards
+// it to the storage. In production code should be invoked asynchronously, on a
+// thread pool (no synchronization expected).
+void AddRecordToStorage(scoped_refptr<StorageModuleInterface> storage,
+ Priority priority,
+ std::string dm_token,
+ Destination destination,
+ ReportQueue::RecordProducer record_producer,
+ StorageModuleInterface::EnqueueCallback callback) {
+ // Generate record data.
+ auto record_result = std::move(record_producer).Run();
+ if (!record_result.ok()) {
+ std::move(callback).Run(record_result.status());
+ return;
+ }
+
+ // Augment data.
+ Record record;
+ *record.mutable_data() = std::move(record_result.ValueOrDie());
+ record.set_destination(destination);
+
+ // |record| with no DM token is assumed to be associated with device DM token
+ if (!dm_token.empty()) {
+ *record.mutable_dm_token() = std::move(dm_token);
+ }
+
+ // Calculate timestamp in microseconds - to match Spanner expectations.
+ const int64_t time_since_epoch_us =
+ base::Time::Now().ToJavaTime() * base::Time::kMicrosecondsPerMillisecond;
+ record.set_timestamp_us(time_since_epoch_us);
+ if (!record_result.ok()) {
+ std::move(callback).Run(record_result.status());
+ return;
+ }
+
+ // Add resulting Record to the storage.
+ storage->AddRecord(priority, std::move(record), std::move(callback));
+}
+} // namespace
void ReportQueueImpl::Create(
std::unique_ptr<ReportQueueConfiguration> config,
@@ -41,21 +80,16 @@ void ReportQueueImpl::Create(
new ReportQueueImpl(std::move(config), storage)));
}
-ReportQueueImpl::~ReportQueueImpl() = default;
-
ReportQueueImpl::ReportQueueImpl(
std::unique_ptr<ReportQueueConfiguration> config,
scoped_refptr<StorageModuleInterface> storage)
- : config_(std::move(config)),
- storage_(storage),
- sequenced_task_runner_(
- base::ThreadPool::CreateSequencedTaskRunner(base::TaskTraits())) {
- DETACH_FROM_SEQUENCE(sequence_checker_);
-}
+ : config_(std::move(config)), storage_(storage) {}
-void ReportQueueImpl::AddRecord(base::StringPiece record,
- Priority priority,
- EnqueueCallback callback) const {
+ReportQueueImpl::~ReportQueueImpl() = default;
+
+void ReportQueueImpl::AddProducedRecord(RecordProducer record_producer,
+ Priority priority,
+ EnqueueCallback callback) const {
const Status status = config_->CheckPolicy();
if (!status.ok()) {
std::move(callback).Run(status);
@@ -68,34 +102,13 @@ void ReportQueueImpl::AddRecord(base::StringPiece record,
return;
}
- sequenced_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&ReportQueueImpl::SendRecordToStorage,
- base::Unretained(this), std::string(record),
- priority, std::move(callback)));
-}
-
-void ReportQueueImpl::SendRecordToStorage(base::StringPiece record_data,
- Priority priority,
- EnqueueCallback callback) const {
- storage_->AddRecord(priority, AugmentRecord(record_data),
- std::move(callback));
-}
-
-Record ReportQueueImpl::AugmentRecord(base::StringPiece record_data) const {
- Record record;
- record.set_data(std::string(record_data));
- record.set_destination(config_->destination());
-
- // record with no DM token is assumed to be associated with device DM token
- if (!config_->dm_token().empty()) {
- record.set_dm_token(config_->dm_token());
- }
-
- // Calculate timestamp in microseconds - to match Spanner expectations.
- const int64_t time_since_epoch_us =
- base::Time::Now().ToJavaTime() * base::Time::kMicrosecondsPerMillisecond;
- record.set_timestamp_us(time_since_epoch_us);
- return record;
+ // Execute |record_producer| on arbitrary thread, analyze the result and send
+ // it to the Storage, returning with the callback.
+ base::ThreadPool::PostTask(
+ FROM_HERE, {base::TaskPriority::BEST_EFFORT},
+ base::BindOnce(&AddRecordToStorage, storage_, priority,
+ config_->dm_token(), config_->destination(),
+ std::move(record_producer), std::move(callback)));
}
void ReportQueueImpl::Flush(Priority priority, FlushCallback callback) {
@@ -109,6 +122,29 @@ ReportQueueImpl::PrepareToAttachActualQueue() const {
[](StatusOr<std::unique_ptr<ReportQueue>>) { NOTREACHED(); });
}
+// Implementation of SpeculativeReportQueueImpl::PendingRecordProducer
+
+SpeculativeReportQueueImpl::PendingRecordProducer::PendingRecordProducer(
+ RecordProducer producer,
+ Priority priority)
+ : record_producer(std::move(producer)), record_priority(priority) {}
+
+SpeculativeReportQueueImpl::PendingRecordProducer::PendingRecordProducer(
+ PendingRecordProducer&& other)
+ : record_producer(std::move(other.record_producer)),
+ record_priority(other.record_priority) {}
+
+SpeculativeReportQueueImpl::PendingRecordProducer::~PendingRecordProducer() =
+ default;
+
+SpeculativeReportQueueImpl::PendingRecordProducer&
+SpeculativeReportQueueImpl::PendingRecordProducer::operator=(
+ PendingRecordProducer&& other) {
+ record_producer = std::move(other.record_producer);
+ record_priority = other.record_priority;
+ return *this;
+}
+
// static
std::unique_ptr<SpeculativeReportQueueImpl, base::OnTaskRunnerDeleter>
SpeculativeReportQueueImpl::Create() {
@@ -153,59 +189,63 @@ void SpeculativeReportQueueImpl::Flush(Priority priority,
priority, std::move(callback), weak_ptr_factory_.GetWeakPtr()));
}
-void SpeculativeReportQueueImpl::AddRecord(base::StringPiece record,
- Priority priority,
- EnqueueCallback callback) const {
+void SpeculativeReportQueueImpl::AddProducedRecord(
+ RecordProducer record_producer,
+ Priority priority,
+ EnqueueCallback callback) const {
+ // Invoke producer on a thread pool, then enqueue record on sequenced task
+ // runner.
sequenced_task_runner_->PostTask(
FROM_HERE,
- base::BindOnce(&SpeculativeReportQueueImpl::MaybeEnqueueRecord,
- weak_ptr_factory_.GetWeakPtr(), std::string(record),
- priority, std::move(callback)));
+ base::BindOnce(&SpeculativeReportQueueImpl::MaybeEnqueueRecordProducer,
+ weak_ptr_factory_.GetWeakPtr(), priority,
+ std::move(callback), std::move(record_producer)));
}
-void SpeculativeReportQueueImpl::MaybeEnqueueRecord(
- base::StringPiece record,
+void SpeculativeReportQueueImpl::MaybeEnqueueRecordProducer(
Priority priority,
- EnqueueCallback callback) const {
+ EnqueueCallback callback,
+ RecordProducer record_producer) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!report_queue_) {
// Queue is not ready yet, store the record in the memory
// queue.
- pending_records_.emplace(record, priority);
+ pending_record_producers_.emplace(std::move(record_producer), priority);
std::move(callback).Run(Status::StatusOK());
return;
}
// Queue is ready. If memory queue is empty, just forward the
// record.
- if (pending_records_.empty()) {
- report_queue_->Enqueue(record, priority, std::move(callback));
+ if (pending_record_producers_.empty()) {
+ report_queue_->AddProducedRecord(std::move(record_producer), priority,
+ std::move(callback));
return;
}
// If memory queue is not empty, attach the new record at the
// end and initiate enqueuing of everything from there.
- pending_records_.emplace(record, priority);
- EnqueuePendingRecords(std::move(callback));
+ pending_record_producers_.emplace(std::move(record_producer), priority);
+ EnqueuePendingRecordProducers(std::move(callback));
}
-void SpeculativeReportQueueImpl::EnqueuePendingRecords(
+void SpeculativeReportQueueImpl::EnqueuePendingRecordProducers(
EnqueueCallback callback) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(report_queue_);
- if (pending_records_.empty()) {
+ if (pending_record_producers_.empty()) {
std::move(callback).Run(Status::StatusOK());
return;
}
- std::string record(pending_records_.front().first);
- Priority priority = pending_records_.front().second;
- pending_records_.pop();
- if (pending_records_.empty()) {
+ auto head = std::move(pending_record_producers_.front());
+ pending_record_producers_.pop();
+ if (pending_record_producers_.empty()) {
// Last of the pending records.
- report_queue_->Enqueue(record, priority, std::move(callback));
+ report_queue_->AddProducedRecord(std::move(head.record_producer),
+ head.record_priority, std::move(callback));
return;
}
- report_queue_->Enqueue(
- record, priority,
+ report_queue_->AddProducedRecord(
+ std::move(head.record_producer), head.record_priority,
base::BindPostTask(
sequenced_task_runner_,
base::BindOnce(
@@ -221,10 +261,9 @@ void SpeculativeReportQueueImpl::EnqueuePendingRecords(
return;
}
self->sequenced_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(
- &SpeculativeReportQueueImpl::EnqueuePendingRecords,
- self, std::move(callback)));
+ FROM_HERE, base::BindOnce(&SpeculativeReportQueueImpl::
+ EnqueuePendingRecordProducers,
+ self, std::move(callback)));
},
weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
}
@@ -266,8 +305,8 @@ void SpeculativeReportQueueImpl::AttachActualQueue(
return;
}
self->report_queue_ = std::move(actual_queue);
- if (!self->pending_records_.empty()) {
- self->EnqueuePendingRecords(
+ if (!self->pending_record_producers_.empty()) {
+ self->EnqueuePendingRecordProducers(
base::BindOnce([](Status enqueue_status) {
if (!enqueue_status.ok()) {
LOG(ERROR) << "Pending records failed to enqueue, status="
diff --git a/chromium/components/reporting/client/report_queue_impl.h b/chromium/components/reporting/client/report_queue_impl.h
index 2b603c99174..3cee147f652 100644
--- a/chromium/components/reporting/client/report_queue_impl.h
+++ b/chromium/components/reporting/client/report_queue_impl.h
@@ -47,14 +47,14 @@ class ReportQueueImpl : public ReportQueue {
scoped_refptr<StorageModuleInterface> storage,
base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)> cb);
- ~ReportQueueImpl() override;
ReportQueueImpl(const ReportQueueImpl& other) = delete;
ReportQueueImpl& operator=(const ReportQueueImpl& other) = delete;
+ ~ReportQueueImpl() override;
void Flush(Priority priority, FlushCallback callback) override;
// Dummy implementation for a regular queue.
- base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)>
+ [[nodiscard]] base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)>
PrepareToAttachActualQueue() const override;
protected:
@@ -62,77 +62,85 @@ class ReportQueueImpl : public ReportQueue {
scoped_refptr<StorageModuleInterface> storage);
private:
- void AddRecord(base::StringPiece record,
- Priority priority,
- EnqueueCallback callback) const override;
-
- void SendRecordToStorage(base::StringPiece record,
- Priority priority,
- EnqueueCallback callback) const;
+ void AddProducedRecord(RecordProducer record_producer,
+ Priority priority,
+ EnqueueCallback callback) const override;
- reporting::Record AugmentRecord(base::StringPiece record_data) const;
-
- std::unique_ptr<ReportQueueConfiguration> config_;
- scoped_refptr<StorageModuleInterface> storage_;
- SEQUENCE_CHECKER(sequence_checker_);
-
- scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
+ const std::unique_ptr<ReportQueueConfiguration> config_;
+ const scoped_refptr<StorageModuleInterface> storage_;
};
class SpeculativeReportQueueImpl : public ReportQueue {
public:
- ~SpeculativeReportQueueImpl() override;
-
// Factory method returns a smart pointer with on-thread deleter.
static std::unique_ptr<SpeculativeReportQueueImpl, base::OnTaskRunnerDeleter>
Create();
+ SpeculativeReportQueueImpl(const SpeculativeReportQueueImpl& other) = delete;
+ SpeculativeReportQueueImpl& operator=(
+ const SpeculativeReportQueueImpl& other) = delete;
+ ~SpeculativeReportQueueImpl() override;
+
// Forwards |Flush| to |ReportQueue|, if already created.
// Returns with failure otherwise.
void Flush(Priority priority, FlushCallback callback) override;
// Provides a callback to attach initialized actual queue to the speculative
// queue.
- base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)>
+ [[nodiscard]] base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)>
PrepareToAttachActualQueue() const override;
// Substitutes actual queue to the speculative, when ready.
// Initiates processesing of all pending records.
void AttachActualQueue(std::unique_ptr<ReportQueue> actual_queue);
- protected:
- // Forwards |AddRecord| to |ReportQueue|, if already created.
- // Records the record internally otherwise.
- void AddRecord(base::StringPiece record,
- Priority priority,
- EnqueueCallback callback) const override;
-
private:
+ // Moveable, non-copyable struct holding a pending record producer for the
+ // |pending_record_producers_| queue below.
+ struct PendingRecordProducer {
+ PendingRecordProducer(RecordProducer producer, Priority priority);
+ PendingRecordProducer(PendingRecordProducer&& other);
+ PendingRecordProducer& operator=(PendingRecordProducer&& other);
+ ~PendingRecordProducer();
+
+ RecordProducer record_producer;
+ Priority record_priority;
+ };
+
// Private constructor, used by the factory method only.
explicit SpeculativeReportQueueImpl(
scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner);
- // Enqueues head of the |pending_records_| and reapplies for the rest of it.
- void EnqueuePendingRecords(EnqueueCallback callback) const;
+ // Forwards |AddProducedRecord| to |ReportQueue|, if already created.
+ // Records the record internally otherwise.
+ void AddProducedRecord(RecordProducer record_producer,
+ Priority priority,
+ EnqueueCallback callback) const override;
+
+ // Enqueues head of the |pending_record_producers_| and reapplies for the rest
+ // of it.
+ void EnqueuePendingRecordProducers(EnqueueCallback callback) const;
- // Optionally enqueues |record| to actual queue, if ready.
- // Otherwise adds it to the end of |pending_records_|.
- void MaybeEnqueueRecord(base::StringPiece record,
- Priority priority,
- EnqueueCallback callback) const;
+ // Optionally enqueues |record_producer| (owned) to actual queue, if ready.
+ // Otherwise adds it to the end of |pending_record_producers_|.
+ void MaybeEnqueueRecordProducer(Priority priority,
+ EnqueueCallback callback,
+ RecordProducer record_producer) const;
- // Task runner that protects |report_queue_| and |pending_records_|
+ // Task runner that protects |report_queue_| and |pending_record_producers_|
// and allows to synchronize the initialization.
const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
SEQUENCE_CHECKER(sequence_checker_);
// Actual |ReportQueue|, once created.
- std::unique_ptr<ReportQueue> report_queue_;
-
- // Queue of the pending records, collected before actual queue has been
- // created. Declared 'mutable', because it is accessed by 'const' methods.
- mutable std::queue<std::pair<std::string /*record*/, Priority /*priority*/>>
- pending_records_;
+ std::unique_ptr<ReportQueue> report_queue_
+ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // Queue of the pending record producers, collected before actual queue has
+ // been created. Declared 'mutable', because it is accessed by 'const'
+ // methods.
+ mutable std::queue<PendingRecordProducer> pending_record_producers_
+ GUARDED_BY_CONTEXT(sequence_checker_);
// Weak pointer factory.
base::WeakPtrFactory<SpeculativeReportQueueImpl> weak_ptr_factory_{this};
diff --git a/chromium/components/reporting/client/report_queue_impl_unittest.cc b/chromium/components/reporting/client/report_queue_impl_unittest.cc
index 600cca17f48..d369f228cb5 100644
--- a/chromium/components/reporting/client/report_queue_impl_unittest.cc
+++ b/chromium/components/reporting/client/report_queue_impl_unittest.cc
@@ -41,6 +41,8 @@ using ::reporting::test::TestStorageModule;
namespace reporting {
namespace {
+constexpr char kTestMessage[] = "TEST_MESSAGE";
+
// Creates a |ReportQueue| using |TestStorageModule| and
// |TestEncryptionModule|. Allows access to the storage module for checking
// stored values.
@@ -117,7 +119,7 @@ TEST_F(ReportQueueImplTest, SuccessfulBaseValueRecord) {
base::Value::Dict test_dict;
test_dict.Set(kTestKey, kTestValue);
test::TestEvent<Status> a;
- report_queue_->Enqueue(test_dict, priority_, a.cb());
+ report_queue_->Enqueue(test_dict.Clone(), priority_, a.cb());
EXPECT_OK(a.result());
EXPECT_EQ(test_storage_module()->priority(), priority_);
@@ -131,15 +133,16 @@ TEST_F(ReportQueueImplTest, SuccessfulBaseValueRecord) {
// Enqueues a |TestMessage| and ensures that it arrives unaltered in the
// |StorageModuleInterface|.
TEST_F(ReportQueueImplTest, SuccessfulProtoRecord) {
- reporting::test::TestMessage test_message;
- test_message.set_test("TEST_MESSAGE");
+ test::TestMessage test_message;
+ test_message.set_test(kTestMessage);
test::TestEvent<Status> a;
- report_queue_->Enqueue(&test_message, priority_, a.cb());
+ report_queue_->Enqueue(std::make_unique<test::TestMessage>(test_message),
+ priority_, a.cb());
EXPECT_OK(a.result());
EXPECT_EQ(test_storage_module()->priority(), priority_);
- reporting::test::TestMessage result_message;
+ test::TestMessage result_message;
ASSERT_TRUE(
result_message.ParseFromString(test_storage_module()->record().data()));
ASSERT_EQ(result_message.test(), test_message.test());
@@ -155,10 +158,11 @@ TEST_F(ReportQueueImplTest, CallSuccessCallbackFailure) {
std::move(callback).Run(Status(error::UNKNOWN, "Failing for Test"));
})));
- reporting::test::TestMessage test_message;
- test_message.set_test("TEST_MESSAGE");
+ test::TestMessage test_message;
+ test_message.set_test(kTestMessage);
test::TestEvent<Status> a;
- report_queue_->Enqueue(&test_message, priority_, a.cb());
+ report_queue_->Enqueue(std::make_unique<test::TestMessage>(test_message),
+ priority_, a.cb());
const auto result = a.result();
EXPECT_FALSE(result.ok());
EXPECT_EQ(result.error_code(), error::UNKNOWN);
@@ -169,7 +173,7 @@ TEST_F(ReportQueueImplTest, EnqueueStringFailsOnPolicy) {
.WillOnce(Return(Status(error::UNAUTHENTICATED, "Failing for tests")));
constexpr char kTestString[] = "El-Chupacabra";
test::TestEvent<Status> a;
- report_queue_->Enqueue(kTestString, priority_, a.cb());
+ report_queue_->Enqueue(std::string(kTestString), priority_, a.cb());
const auto result = a.result();
EXPECT_FALSE(result.ok());
EXPECT_EQ(result.error_code(), error::UNAUTHENTICATED);
@@ -178,10 +182,11 @@ TEST_F(ReportQueueImplTest, EnqueueStringFailsOnPolicy) {
TEST_F(ReportQueueImplTest, EnqueueProtoFailsOnPolicy) {
EXPECT_CALL(mocked_policy_check_, Call())
.WillOnce(Return(Status(error::UNAUTHENTICATED, "Failing for tests")));
- reporting::test::TestMessage test_message;
- test_message.set_test("TEST_MESSAGE");
+ test::TestMessage test_message;
+ test_message.set_test(kTestMessage);
test::TestEvent<Status> a;
- report_queue_->Enqueue(&test_message, priority_, a.cb());
+ report_queue_->Enqueue(std::make_unique<test::TestMessage>(test_message),
+ priority_, a.cb());
const auto result = a.result();
EXPECT_FALSE(result.ok());
EXPECT_EQ(result.error_code(), error::UNAUTHENTICATED);
@@ -195,17 +200,18 @@ TEST_F(ReportQueueImplTest, EnqueueValueFailsOnPolicy) {
base::Value::Dict test_dict;
test_dict.Set(kTestKey, kTestValue);
test::TestEvent<Status> a;
- report_queue_->Enqueue(test_dict, priority_, a.cb());
+ report_queue_->Enqueue(test_dict.Clone(), priority_, a.cb());
const auto result = a.result();
EXPECT_FALSE(result.ok());
EXPECT_EQ(result.error_code(), error::UNAUTHENTICATED);
}
TEST_F(ReportQueueImplTest, EnqueueAndFlushSuccess) {
- reporting::test::TestMessage test_message;
- test_message.set_test("TEST_MESSAGE");
+ test::TestMessage test_message;
+ test_message.set_test(kTestMessage);
test::TestEvent<Status> a;
- report_queue_->Enqueue(&test_message, priority_, a.cb());
+ report_queue_->Enqueue(std::make_unique<test::TestMessage>(test_message),
+ priority_, a.cb());
EXPECT_OK(a.result());
test::TestEvent<Status> f;
report_queue_->Flush(priority_, f.cb());
@@ -213,10 +219,11 @@ TEST_F(ReportQueueImplTest, EnqueueAndFlushSuccess) {
}
TEST_F(ReportQueueImplTest, EnqueueSuccessFlushFailure) {
- reporting::test::TestMessage test_message;
- test_message.set_test("TEST_MESSAGE");
+ test::TestMessage test_message;
+ test_message.set_test(kTestMessage);
test::TestEvent<Status> a;
- report_queue_->Enqueue(&test_message, priority_, a.cb());
+ report_queue_->Enqueue(std::make_unique<test::TestMessage>(test_message),
+ priority_, a.cb());
EXPECT_OK(a.result());
EXPECT_CALL(*test_storage_module(), Flush(Eq(priority_), _))
@@ -238,7 +245,8 @@ TEST_F(ReportQueueImplTest, SuccessfulSpeculativeStringRecord) {
constexpr char kTestString[] = "El-Chupacabra";
test::TestEvent<Status> a;
auto speculative_report_queue = SpeculativeReportQueueImpl::Create();
- speculative_report_queue->Enqueue(kTestString, priority_, a.cb());
+ speculative_report_queue->Enqueue(std::string(kTestString), priority_,
+ a.cb());
EXPECT_OK(a.result());
speculative_report_queue->AttachActualQueue(std::move(report_queue_));
@@ -261,29 +269,31 @@ TEST_F(ReportQueueImplTest, OverlappingStringRecords) {
// Call `Enqueue` for 2 records before report queue is ready, both will be
// added to pending records.
- speculative_report_queue->Enqueue(kTestString1, priority_, event1.cb());
+ speculative_report_queue->Enqueue(std::string(kTestString1), priority_,
+ event1.cb());
EXPECT_OK(event1.result());
- speculative_report_queue->Enqueue(kTestString2, priority_, event2.cb());
+ speculative_report_queue->Enqueue(std::string(kTestString2), priority_,
+ event2.cb());
EXPECT_OK(event2.result());
base::queue<ReportQueue::EnqueueCallback> enqueue_cb_queue;
int enqueue_count = 0;
- auto mock_queue = std::make_unique<testing::NiceMock<MockReportQueue>>();
+ auto mock_queue = std::make_unique<MockReportQueue>();
EXPECT_CALL(*mock_queue, AddRecord)
.Times(3)
- .WillRepeatedly(
- [&enqueue_cb_queue, &enqueue_count](base::StringPiece record_string,
- Priority event_priority,
- ReportQueue::EnqueueCallback cb) {
- ++enqueue_count;
- enqueue_cb_queue.emplace(std::move(cb));
- });
+ .WillRepeatedly([&enqueue_cb_queue, &enqueue_count](
+ std::string record_string, Priority event_priority,
+ ReportQueue::EnqueueCallback cb) {
+ ++enqueue_count;
+ enqueue_cb_queue.emplace(std::move(cb));
+ });
// First record should be enqueued after calling `AttachActualQueue`.
speculative_report_queue->AttachActualQueue(std::move(mock_queue));
// Second record should be enqueued after calling `Enqueue` for the third
// record, and third record should be added to pending records.
- speculative_report_queue->Enqueue(kTestString3, priority_, event3.cb());
+ speculative_report_queue->Enqueue(std::string(kTestString3), priority_,
+ event3.cb());
task_environment_.RunUntilIdle();
ASSERT_EQ(enqueue_count, 2);
@@ -308,5 +318,114 @@ TEST_F(ReportQueueImplTest, OverlappingStringRecords) {
task_environment_.RunUntilIdle();
}
+TEST_F(ReportQueueImplTest, EnqueueRecordWithInvalidPriority) {
+ test::TestEvent<Status> event;
+ report_queue_->Enqueue(std::string(kTestMessage),
+ Priority::UNDEFINED_PRIORITY, event.cb());
+ const auto result = event.result();
+
+ ASSERT_FALSE(result.ok());
+ EXPECT_EQ(result.code(), error::INVALID_ARGUMENT);
+}
+
+TEST_F(ReportQueueImplTest, FlushSpeculativeReportQueue) {
+ test::TestEvent<Status> event;
+
+ // Set up speculative report queue
+ auto speculative_report_queue = SpeculativeReportQueueImpl::Create();
+ speculative_report_queue->AttachActualQueue(std::move(report_queue_));
+ task_environment_.RunUntilIdle();
+
+ EXPECT_CALL(*test_storage_module(), Flush(Eq(priority_), _))
+ .WillOnce(
+ WithArg<1>(Invoke([](base::OnceCallback<void(Status)> callback) {
+ std::move(callback).Run(Status::StatusOK());
+ })));
+
+ speculative_report_queue->Flush(priority_, event.cb());
+ const auto result = event.result();
+ ASSERT_OK(result);
+}
+
+TEST_F(ReportQueueImplTest, FlushUninitializedSpeculativeReportQueue) {
+ test::TestEvent<Status> event;
+
+ auto speculative_report_queue = SpeculativeReportQueueImpl::Create();
+ speculative_report_queue->Flush(priority_, event.cb());
+
+ const auto result = event.result();
+ ASSERT_FALSE(result.ok());
+ EXPECT_EQ(result.error_code(), error::FAILED_PRECONDITION);
+}
+
+TEST_F(ReportQueueImplTest, AsyncProcessingReportQueue) {
+ auto mock_queue = std::make_unique<MockReportQueue>();
+ EXPECT_CALL(*mock_queue, AddProducedRecord)
+ .Times(3)
+ .WillRepeatedly([](ReportQueue::RecordProducer record_producer,
+ Priority event_priority,
+ ReportQueue::EnqueueCallback cb) {
+ std::move(cb).Run(Status::StatusOK());
+ });
+
+ test::TestEvent<Status> a_string;
+ mock_queue->Enqueue(std::string(kTestMessage), priority_, a_string.cb());
+
+ test::TestEvent<Status> a_proto;
+ test::TestMessage test_message;
+ test_message.set_test(kTestMessage);
+ mock_queue->Enqueue(std::make_unique<test::TestMessage>(test_message),
+ priority_, a_proto.cb());
+
+ test::TestEvent<Status> a_json;
+ constexpr char kTestKey[] = "TEST_KEY";
+ constexpr char kTestValue[] = "TEST_VALUE";
+ base::Value::Dict test_dict;
+ test_dict.Set(kTestKey, kTestValue);
+ mock_queue->Enqueue(std::move(test_dict), priority_, a_json.cb());
+
+ EXPECT_OK(a_string.result());
+ EXPECT_OK(a_proto.result());
+ EXPECT_OK(a_json.result());
+}
+
+TEST_F(ReportQueueImplTest, AsyncProcessingSpeculativeReportQueue) {
+ auto speculative_report_queue = SpeculativeReportQueueImpl::Create();
+
+ test::TestEvent<Status> a_string;
+ speculative_report_queue->Enqueue(std::string(kTestMessage), priority_,
+ a_string.cb());
+
+ test::TestEvent<Status> a_proto;
+ test::TestMessage test_message;
+ test_message.set_test(kTestMessage);
+ speculative_report_queue->Enqueue(
+ std::make_unique<test::TestMessage>(test_message), priority_,
+ a_proto.cb());
+
+ test::TestEvent<Status> a_json;
+ constexpr char kTestKey[] = "TEST_KEY";
+ constexpr char kTestValue[] = "TEST_VALUE";
+ base::Value::Dict test_dict;
+ test_dict.Set(kTestKey, kTestValue);
+ speculative_report_queue->Enqueue(std::move(test_dict), priority_,
+ a_json.cb());
+
+ EXPECT_OK(a_string.result());
+ EXPECT_OK(a_proto.result());
+ EXPECT_OK(a_json.result());
+
+ auto mock_queue = std::make_unique<MockReportQueue>();
+ EXPECT_CALL(*mock_queue, AddProducedRecord)
+ .Times(3)
+ .WillRepeatedly([](ReportQueue::RecordProducer record_producer,
+ Priority event_priority,
+ ReportQueue::EnqueueCallback cb) {
+ std::move(cb).Run(Status::StatusOK());
+ });
+ speculative_report_queue->AttachActualQueue(std::move(mock_queue));
+ // Let everything ongoing to finish.
+ task_environment_.RunUntilIdle();
+}
} // namespace
} // namespace reporting
diff --git a/chromium/components/reporting/client/report_queue_provider_unittest.cc b/chromium/components/reporting/client/report_queue_provider_unittest.cc
index 51c66c991d5..1300e647c5b 100644
--- a/chromium/components/reporting/client/report_queue_provider_unittest.cc
+++ b/chromium/components/reporting/client/report_queue_provider_unittest.cc
@@ -36,7 +36,6 @@ class ReportQueueProviderTest : public ::testing::Test {
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
- base::test::ScopedFeatureList scoped_feature_list_;
const Destination destination_ = Destination::UPLOAD_EVENTS;
ReportQueueConfiguration::PolicyCheckCallback policy_checker_callback_ =
base::BindRepeating([]() { return Status::StatusOK(); });
@@ -65,10 +64,9 @@ TEST_F(ReportQueueProviderTest, CreateAndGetQueue) {
// Asynchronously create ReportingQueue.
base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)>
queue_cb = base::BindOnce(
- [](base::StringPiece data,
- reporting::ReportQueue::EnqueueCallback done_cb,
- reporting::StatusOr<std::unique_ptr<
- reporting::ReportQueue>> report_queue_result) {
+ [](std::string data, ReportQueue::EnqueueCallback done_cb,
+ StatusOr<std::unique_ptr<ReportQueue>>
+ report_queue_result) {
// Bail out if queue failed to create.
if (!report_queue_result.ok()) {
std::move(done_cb).Run(report_queue_result.status());
@@ -83,11 +81,11 @@ TEST_F(ReportQueueProviderTest, CreateAndGetQueue) {
std::move(cb).Run(Status::StatusOK());
})));
report_queue_result.ValueOrDie()->Enqueue(
- data, FAST_BATCH, std::move(done_cb));
+ std::move(data), FAST_BATCH, std::move(done_cb));
},
std::string(data), std::move(done_cb));
- reporting::ReportQueueProvider::CreateQueue(std::move(config),
- std::move(queue_cb));
+ ReportQueueProvider::CreateQueue(std::move(config),
+ std::move(queue_cb));
},
kTestMessage, e.cb(), std::move(config_result.ValueOrDie())));
const auto res = e.result();
@@ -95,5 +93,42 @@ TEST_F(ReportQueueProviderTest, CreateAndGetQueue) {
report_queue_provider_test_helper::SetForTesting(nullptr);
}
+TEST_F(ReportQueueProviderTest,
+ CreateReportQueueWithEncryptedReportingPipelineDisabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndDisableFeature(
+ ReportQueueProvider::kEncryptedReportingPipeline);
+
+ // Create configuration
+ auto config_result = ReportQueueConfiguration::Create(
+ EventType::kDevice, destination_, policy_checker_callback_);
+ ASSERT_OK(config_result);
+
+ test::TestEvent<ReportQueueProvider::CreateReportQueueResponse> event;
+ ReportQueueProvider::CreateQueue(std::move(config_result.ValueOrDie()),
+ event.cb());
+ const auto result = event.result();
+
+ ASSERT_FALSE(result.ok());
+ EXPECT_EQ(result.status().code(), error::FAILED_PRECONDITION);
+}
+
+TEST_F(ReportQueueProviderTest,
+ CreateSpeculativeReportQueueWithEncryptedReportingPipelineDisabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndDisableFeature(
+ ReportQueueProvider::kEncryptedReportingPipeline);
+
+ // Create configuration
+ auto config_result = ReportQueueConfiguration::Create(
+ EventType::kDevice, destination_, policy_checker_callback_);
+ ASSERT_OK(config_result);
+
+ const auto result = ReportQueueProvider::CreateSpeculativeQueue(
+ std::move(config_result.ValueOrDie()));
+ ASSERT_FALSE(result.ok());
+ EXPECT_EQ(result.status().code(), error::FAILED_PRECONDITION);
+}
+
} // namespace
} // namespace reporting
diff --git a/chromium/components/reporting/compression/BUILD.gn b/chromium/components/reporting/compression/BUILD.gn
index 175bbb3d151..656b7d667bd 100644
--- a/chromium/components/reporting/compression/BUILD.gn
+++ b/chromium/components/reporting/compression/BUILD.gn
@@ -35,6 +35,7 @@ static_library("test_support") {
"//base",
"//base/test:test_support",
"//components/reporting/proto:record_proto",
+ "//components/reporting/resources:resource_interface",
"//components/reporting/util:status",
"//components/reporting/util:status_macros",
"//testing/gmock",
@@ -53,6 +54,7 @@ source_set("unit_tests") {
"//base",
"//base/test:test_support",
"//components/reporting/proto:record_proto",
+ "//components/reporting/resources:resource_interface",
"//components/reporting/util:status",
"//components/reporting/util:status_macros",
"//components/reporting/util:test_callbacks_support",
diff --git a/chromium/components/reporting/compression/compression_module.cc b/chromium/components/reporting/compression/compression_module.cc
index 580285723d8..fd823eb6b6c 100644
--- a/chromium/components/reporting/compression/compression_module.cc
+++ b/chromium/components/reporting/compression/compression_module.cc
@@ -3,10 +3,12 @@
// found in the LICENSE file.
#include "components/reporting/compression/compression_module.h"
-#include "base/feature_list.h"
+#include <string>
+#include <utility>
#include "base/bind.h"
#include "base/callback.h"
+#include "base/feature_list.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_piece.h"
@@ -55,6 +57,7 @@ scoped_refptr<CompressionModule> CompressionModule::Create(
void CompressionModule::CompressRecord(
std::string record,
+ scoped_refptr<ResourceInterface> memory_resource,
base::OnceCallback<void(std::string,
absl::optional<CompressionInformation>)> cb) const {
if (!is_enabled()) {
@@ -89,7 +92,7 @@ void CompressionModule::CompressRecord(
}
// Before doing compression, we must make sure there is enough memory - we
// are going to temporarily double the record.
- ScopedReservation scoped_reservation(record.size(), GetMemoryResource());
+ ScopedReservation scoped_reservation(record.size(), memory_resource);
if (!scoped_reservation.reserved()) {
base::UmaHistogramEnumeration(
kCompressionThresholdCountMetricsName,
diff --git a/chromium/components/reporting/compression/compression_module.h b/chromium/components/reporting/compression/compression_module.h
index d08bc47989a..1c99a027d53 100644
--- a/chromium/components/reporting/compression/compression_module.h
+++ b/chromium/components/reporting/compression/compression_module.h
@@ -1,17 +1,20 @@
// Copyright 2021 The Chromium 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/feature_list.h"
+#ifndef COMPONENTS_REPORTING_COMPRESSION_COMPRESSION_MODULE_H_
+#define COMPONENTS_REPORTING_COMPRESSION_COMPRESSION_MODULE_H_
+
+#include <string>
+
+#include "base/feature_list.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string_piece.h"
#include "components/reporting/proto/synced/record.pb.h"
+#include "components/reporting/resources/resource_interface.h"
#include "components/reporting/util/statusor.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-#ifndef COMPONENTS_REPORTING_COMPRESSION_COMPRESSION_MODULE_H_
-#define COMPONENTS_REPORTING_COMPRESSION_COMPRESSION_MODULE_H_
-
namespace reporting {
class CompressionModule : public base::RefCountedThreadSafe<CompressionModule> {
@@ -37,6 +40,7 @@ class CompressionModule : public base::RefCountedThreadSafe<CompressionModule> {
// std::move(record).
void CompressRecord(
std::string record,
+ scoped_refptr<ResourceInterface> memory_resource,
base::OnceCallback<
void(std::string, absl::optional<CompressionInformation>)> cb) const;
diff --git a/chromium/components/reporting/compression/compression_module_unittest.cc b/chromium/components/reporting/compression/compression_module_unittest.cc
index fd6ddfc551b..c69a71196c8 100644
--- a/chromium/components/reporting/compression/compression_module_unittest.cc
+++ b/chromium/components/reporting/compression/compression_module_unittest.cc
@@ -6,11 +6,13 @@
#include <memory>
#include <string>
+#include <tuple>
#include <utility>
#include "base/bind.h"
#include "base/containers/flat_map.h"
#include "base/hash/hash.h"
+#include "base/memory/scoped_refptr.h"
#include "base/rand_util.h"
#include "base/strings/strcat.h"
#include "base/synchronization/waitable_event.h"
@@ -20,7 +22,8 @@
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "components/reporting/proto/synced/record.pb.h"
-
+#include "components/reporting/resources/memory_resource_impl.h"
+#include "components/reporting/resources/resource_interface.h"
#include "components/reporting/util/test_support_callbacks.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -51,8 +54,12 @@ constexpr char kSnappyCompressedRecordSizeMetricsName[] =
class CompressionModuleTest : public ::testing::Test {
protected:
- CompressionModuleTest() = default;
+ CompressionModuleTest()
+ : memory_resource_(base::MakeRefCounted<MemoryResourceImpl>(
+ 4u * 1024LLu * 1024LLu)) // 4 MiB
+ {}
+ void TearDown() override { ASSERT_THAT(memory_resource_->GetUsed(), Eq(0u)); }
std::string BenchmarkCompressRecordSnappy(std::string record_string) {
std::string output;
snappy::Compress(record_string.data(), record_string.size(), &output);
@@ -70,6 +77,7 @@ class CompressionModuleTest : public ::testing::Test {
{}, {CompressionModule::kCompressReportingFeature});
}
+ scoped_refptr<ResourceInterface> memory_resource_;
scoped_refptr<CompressionModule> compression_module_;
base::test::TaskEnvironment task_environment_{};
@@ -103,7 +111,7 @@ TEST_F(CompressionModuleTest, CompressRecordSnappy) {
test::TestMultiEvent<std::string, absl::optional<CompressionInformation>>
compressed_record_event;
// Compress string with CompressionModule
- test_compression_module->CompressRecord(kTestString,
+ test_compression_module->CompressRecord(kTestString, memory_resource_,
compressed_record_event.cb());
const std::tuple<std::string, absl::optional<CompressionInformation>>
@@ -160,7 +168,7 @@ TEST_F(CompressionModuleTest, CompressRecordBelowThreshold) {
test::TestMultiEvent<std::string, absl::optional<CompressionInformation>>
compressed_record_event;
// Compress string with CompressionModule
- test_compression_module->CompressRecord(kTestString,
+ test_compression_module->CompressRecord(kTestString, memory_resource_,
compressed_record_event.cb());
const std::tuple<std::string, absl::optional<CompressionInformation>>
@@ -219,7 +227,7 @@ TEST_F(CompressionModuleTest, CompressRecordCompressionDisabled) {
compressed_record_event;
// Compress string with CompressionModule
- test_compression_module->CompressRecord(kTestString,
+ test_compression_module->CompressRecord(kTestString, memory_resource_,
compressed_record_event.cb());
const std::tuple<std::string, absl::optional<CompressionInformation>>
@@ -273,7 +281,7 @@ TEST_F(CompressionModuleTest, CompressRecordCompressionNone) {
compressed_record_event;
// Compress string with CompressionModule
- test_compression_module->CompressRecord(kTestString,
+ test_compression_module->CompressRecord(kTestString, memory_resource_,
compressed_record_event.cb());
const std::tuple<std::string, absl::optional<CompressionInformation>>
compressed_record_tuple = compressed_record_event.result();
diff --git a/chromium/components/reporting/compression/decompression.cc b/chromium/components/reporting/compression/decompression.cc
index b8e1f386593..ad91e3253f4 100644
--- a/chromium/components/reporting/compression/decompression.cc
+++ b/chromium/components/reporting/compression/decompression.cc
@@ -3,7 +3,8 @@
// found in the LICENSE file.
#include "components/reporting/compression/decompression.h"
-#include "base/feature_list.h"
+#include <string>
+#include <utility>
#include "base/bind.h"
#include "base/callback.h"
diff --git a/chromium/components/reporting/compression/decompression.h b/chromium/components/reporting/compression/decompression.h
index b87e5ed5d8c..140b0a5493b 100644
--- a/chromium/components/reporting/compression/decompression.h
+++ b/chromium/components/reporting/compression/decompression.h
@@ -29,7 +29,7 @@ class Decompression : public base::RefCountedThreadSafe<Decompression> {
// string then can be further updated by the caller. std::string is used
// instead of base::StringPiece because ownership is taken of |record| through
// std::move(record).
- static std::string DecompressRecord(
+ [[nodiscard]] static std::string DecompressRecord(
std::string record,
CompressionInformation compression_information);
@@ -46,4 +46,4 @@ class Decompression : public base::RefCountedThreadSafe<Decompression> {
} // namespace reporting
-#endif // COMPONENTS_REPORTING_COMPRESSION_DECOMPRESSION_H_ \ No newline at end of file
+#endif // COMPONENTS_REPORTING_COMPRESSION_DECOMPRESSION_H_
diff --git a/chromium/components/reporting/compression/test_compression_module.cc b/chromium/components/reporting/compression/test_compression_module.cc
index 9265ec4fc41..df3a1416b45 100644
--- a/chromium/components/reporting/compression/test_compression_module.cc
+++ b/chromium/components/reporting/compression/test_compression_module.cc
@@ -4,10 +4,15 @@
#include "components/reporting/compression/test_compression_module.h"
+#include <string>
+#include <utility>
+
#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
#include "base/strings/string_piece.h"
#include "components/reporting/proto/synced/record.pb.h"
-#include "components/reporting/util/statusor.h"
+#include "components/reporting/resources/resource_interface.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
using ::testing::Invoke;
@@ -23,6 +28,7 @@ TestCompressionModuleStrict::TestCompressionModuleStrict()
ON_CALL(*this, CompressRecord)
.WillByDefault(Invoke(
[](std::string record,
+ scoped_refptr<ResourceInterface> resource_interface,
base::OnceCallback<void(
std::string, absl::optional<CompressionInformation>)> cb) {
// compression_info is not set.
diff --git a/chromium/components/reporting/compression/test_compression_module.h b/chromium/components/reporting/compression/test_compression_module.h
index 39a4b4669bf..8d383c69b78 100644
--- a/chromium/components/reporting/compression/test_compression_module.h
+++ b/chromium/components/reporting/compression/test_compression_module.h
@@ -5,13 +5,17 @@
#ifndef COMPONENTS_REPORTING_COMPRESSION_TEST_COMPRESSION_MODULE_H_
#define COMPONENTS_REPORTING_COMPRESSION_TEST_COMPRESSION_MODULE_H_
+#include <string>
+
#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
#include "base/strings/string_piece.h"
#include "components/reporting/compression/compression_module.h"
#include "components/reporting/proto/synced/record.pb.h"
-#include "components/reporting/util/statusor.h"
+#include "components/reporting/resources/resource_interface.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace reporting {
namespace test {
@@ -25,6 +29,7 @@ class TestCompressionModuleStrict : public CompressionModule {
void,
CompressRecord,
(std::string record,
+ scoped_refptr<ResourceInterface> memory_resource,
base::OnceCallback<void(std::string,
absl::optional<CompressionInformation>)> cb),
(const override));
diff --git a/chromium/components/reporting/encryption/BUILD.gn b/chromium/components/reporting/encryption/BUILD.gn
index adb13b5bfa3..c7f4c47724b 100644
--- a/chromium/components/reporting/encryption/BUILD.gn
+++ b/chromium/components/reporting/encryption/BUILD.gn
@@ -25,7 +25,6 @@ static_library("primitives") {
deps = [
"//base",
"//crypto",
- "//crypto:platform",
"//third_party/boringssl",
]
}
@@ -41,7 +40,6 @@ static_library("testing_primitives") {
"//base",
"//base/test:test_support",
"//crypto",
- "//crypto:platform",
"//third_party/boringssl",
]
}
@@ -118,6 +116,7 @@ static_library("test_support") {
"//base",
"//base/test:test_support",
"//components/reporting/proto:record_proto",
+ "//components/reporting/resources:resource_interface",
"//components/reporting/util:status",
"//components/reporting/util:status_macros",
"//testing/gmock",
@@ -146,6 +145,7 @@ source_set("unit_tests") {
"//base",
"//base/test:test_support",
"//components/reporting/proto:record_proto",
+ "//components/reporting/resources:resource_interface",
"//components/reporting/util:status",
"//components/reporting/util:status_macros",
"//components/reporting/util:test_callbacks_support",
diff --git a/chromium/components/reporting/encryption/decryption.h b/chromium/components/reporting/encryption/decryption.h
index fb9f8ae0d3f..cb80f901319 100644
--- a/chromium/components/reporting/encryption/decryption.h
+++ b/chromium/components/reporting/encryption/decryption.h
@@ -14,7 +14,6 @@
#include "base/strings/string_piece.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
#include "components/reporting/encryption/encryption.h"
#include "components/reporting/util/status.h"
#include "components/reporting/util/statusor.h"
diff --git a/chromium/components/reporting/encryption/encryption.cc b/chromium/components/reporting/encryption/encryption.cc
index 9413af3888f..d2e9eb863dd 100644
--- a/chromium/components/reporting/encryption/encryption.cc
+++ b/chromium/components/reporting/encryption/encryption.cc
@@ -4,6 +4,7 @@
#include "components/reporting/encryption/encryption.h"
+#include <memory>
#include <string>
#include <utility>
diff --git a/chromium/components/reporting/encryption/encryption_module_interface.cc b/chromium/components/reporting/encryption/encryption_module_interface.cc
index 49b5585c7ed..ce7aa15ff11 100644
--- a/chromium/components/reporting/encryption/encryption_module_interface.cc
+++ b/chromium/components/reporting/encryption/encryption_module_interface.cc
@@ -4,7 +4,11 @@
#include "components/reporting/encryption/encryption_module_interface.h"
+#include <utility>
+
+#include "base/bind.h"
#include "base/callback.h"
+#include "base/callback_helpers.h"
#include "base/feature_list.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
diff --git a/chromium/components/reporting/encryption/encryption_module_unittest.cc b/chromium/components/reporting/encryption/encryption_module_unittest.cc
index 6e0beb20525..51cf9ab137d 100644
--- a/chromium/components/reporting/encryption/encryption_module_unittest.cc
+++ b/chromium/components/reporting/encryption/encryption_module_unittest.cc
@@ -25,6 +25,7 @@
#include "components/reporting/encryption/primitives.h"
#include "components/reporting/encryption/testing_primitives.h"
#include "components/reporting/proto/synced/record.pb.h"
+#include "components/reporting/resources/resource_interface.h"
#include "components/reporting/util/status.h"
#include "components/reporting/util/status_macros.h"
#include "components/reporting/util/statusor.h"
@@ -128,9 +129,6 @@ class EncryptionModuleTest : public ::testing::Test {
scoped_refptr<EncryptionModuleInterface> encryption_module_;
scoped_refptr<test::Decryptor> decryptor_;
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(EncryptionModuleTest, EncryptAndDecrypt) {
diff --git a/chromium/components/reporting/encryption/primitives.cc b/chromium/components/reporting/encryption/primitives.cc
index 137516de573..e3ff770b28f 100644
--- a/chromium/components/reporting/encryption/primitives.cc
+++ b/chromium/components/reporting/encryption/primitives.cc
@@ -9,6 +9,7 @@
#include <memory>
#include <string>
+#include "base/check_op.h"
#include "crypto/aead.h"
#include "crypto/openssl_util.h"
#include "third_party/boringssl/src/include/openssl/curve25519.h"
diff --git a/chromium/components/reporting/encryption/test_encryption_module.cc b/chromium/components/reporting/encryption/test_encryption_module.cc
index 6f762582279..0b728b2466f 100644
--- a/chromium/components/reporting/encryption/test_encryption_module.cc
+++ b/chromium/components/reporting/encryption/test_encryption_module.cc
@@ -4,6 +4,9 @@
#include "components/reporting/encryption/test_encryption_module.h"
+#include <string>
+#include <utility>
+
#include "base/callback.h"
#include "base/strings/string_piece.h"
#include "components/reporting/proto/synced/record.pb.h"
diff --git a/chromium/components/reporting/encryption/testing_primitives.cc b/chromium/components/reporting/encryption/testing_primitives.cc
index 2977104b1aa..3d35848bd9e 100644
--- a/chromium/components/reporting/encryption/testing_primitives.cc
+++ b/chromium/components/reporting/encryption/testing_primitives.cc
@@ -6,7 +6,6 @@
#include <cstddef>
#include <cstdint>
-#include <memory>
#include <string>
#include "base/strings/string_piece.h"
diff --git a/chromium/components/reporting/metrics/fake_metric_report_queue.cc b/chromium/components/reporting/metrics/fake_metric_report_queue.cc
index 67f1b8aaff4..6635d0af970 100644
--- a/chromium/components/reporting/metrics/fake_metric_report_queue.cc
+++ b/chromium/components/reporting/metrics/fake_metric_report_queue.cc
@@ -4,6 +4,9 @@
#include "components/reporting/metrics/fake_metric_report_queue.h"
+#include <memory>
+#include <vector>
+
#include "base/task/sequenced_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/reporting/proto/synced/record_constants.pb.h"
@@ -35,9 +38,10 @@ FakeMetricReportQueue::FakeMetricReportQueue(
default_rate,
rate_unit_to_ms) {}
-void FakeMetricReportQueue::Enqueue(const MetricData& metric_data,
- ReportQueue::EnqueueCallback callback) {
- reported_data_.emplace_back(metric_data);
+void FakeMetricReportQueue::Enqueue(
+ std::unique_ptr<const MetricData> metric_data,
+ ReportQueue::EnqueueCallback callback) {
+ reported_data_.emplace_back(std::move(metric_data));
std::move(callback).Run(Status());
}
@@ -47,7 +51,8 @@ void FakeMetricReportQueue::Flush() {
num_flush_++;
}
-std::vector<MetricData> FakeMetricReportQueue::GetMetricDataReported() const {
+const std::vector<std::unique_ptr<const MetricData>>&
+FakeMetricReportQueue::GetMetricDataReported() const {
return reported_data_;
}
diff --git a/chromium/components/reporting/metrics/fake_metric_report_queue.h b/chromium/components/reporting/metrics/fake_metric_report_queue.h
index beee1d1bd53..3f79237328f 100644
--- a/chromium/components/reporting/metrics/fake_metric_report_queue.h
+++ b/chromium/components/reporting/metrics/fake_metric_report_queue.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_REPORTING_METRICS_FAKE_METRIC_REPORT_QUEUE_H_
#define COMPONENTS_REPORTING_METRICS_FAKE_METRIC_REPORT_QUEUE_H_
+#include <memory>
#include <vector>
#include "components/reporting/client/report_queue.h"
@@ -29,17 +30,18 @@ class FakeMetricReportQueue : public MetricReportQueue {
~FakeMetricReportQueue() override;
- void Enqueue(const MetricData& metric_data,
+ void Enqueue(std::unique_ptr<const MetricData> metric_data,
ReportQueue::EnqueueCallback callback) override;
- std::vector<MetricData> GetMetricDataReported() const;
+ const std::vector<std::unique_ptr<const MetricData>>& GetMetricDataReported()
+ const;
int GetNumFlush() const;
private:
void Flush() override;
- std::vector<MetricData> reported_data_;
+ std::vector<std::unique_ptr<const MetricData>> reported_data_;
int num_flush_ = 0;
};
diff --git a/chromium/components/reporting/metrics/fake_sampler.cc b/chromium/components/reporting/metrics/fake_sampler.cc
index d92ada76c48..c66526ad31b 100644
--- a/chromium/components/reporting/metrics/fake_sampler.cc
+++ b/chromium/components/reporting/metrics/fake_sampler.cc
@@ -14,12 +14,16 @@
namespace reporting {
namespace test {
-void FakeSampler::Collect(MetricCallback cb) {
+FakeSampler::FakeSampler() = default;
+
+FakeSampler::~FakeSampler() = default;
+
+void FakeSampler::MaybeCollect(OptionalMetricCallback cb) {
num_calls_++;
std::move(cb).Run(metric_data_);
}
-void FakeSampler::SetMetricData(MetricData metric_data) {
+void FakeSampler::SetMetricData(absl::optional<MetricData> metric_data) {
metric_data_ = std::move(metric_data);
}
diff --git a/chromium/components/reporting/metrics/fake_sampler.h b/chromium/components/reporting/metrics/fake_sampler.h
index 3fd02f5b926..47cdf898490 100644
--- a/chromium/components/reporting/metrics/fake_sampler.h
+++ b/chromium/components/reporting/metrics/fake_sampler.h
@@ -7,27 +7,28 @@
#include "components/reporting/metrics/sampler.h"
#include "components/reporting/proto/synced/metric_data.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace reporting {
namespace test {
class FakeSampler : public Sampler {
public:
- FakeSampler() = default;
+ FakeSampler();
FakeSampler(const FakeSampler& other) = delete;
FakeSampler& operator=(const FakeSampler& other) = delete;
- ~FakeSampler() override = default;
+ ~FakeSampler() override;
- void Collect(MetricCallback cb) override;
+ void MaybeCollect(OptionalMetricCallback cb) override;
- void SetMetricData(MetricData metric_data);
+ void SetMetricData(absl::optional<MetricData> metric_data);
int GetNumCollectCalls() const;
private:
- MetricData metric_data_;
+ absl::optional<MetricData> metric_data_;
int num_calls_ = 0;
};
diff --git a/chromium/components/reporting/metrics/metric_data_collector.cc b/chromium/components/reporting/metrics/metric_data_collector.cc
index 98580070636..083afcfbc1a 100644
--- a/chromium/components/reporting/metrics/metric_data_collector.cc
+++ b/chromium/components/reporting/metrics/metric_data_collector.cc
@@ -31,11 +31,11 @@ void CollectorBase::Collect() {
auto on_collected_cb = base::BindOnce(&CollectorBase::OnMetricDataCollected,
weak_ptr_factory_.GetWeakPtr());
- sampler_->Collect(base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
- std::move(on_collected_cb)));
+ sampler_->MaybeCollect(base::BindPostTask(
+ base::SequencedTaskRunnerHandle::Get(), std::move(on_collected_cb)));
}
-void CollectorBase::ReportMetricData(const MetricData& metric_data,
+void CollectorBase::ReportMetricData(MetricData metric_data,
base::OnceClosure on_data_reported) {
auto enqueue_cb = base::BindOnce(
[](base::OnceClosure on_data_reported, Status status) {
@@ -46,7 +46,9 @@ void CollectorBase::ReportMetricData(const MetricData& metric_data,
std::move(on_data_reported).Run();
},
std::move(on_data_reported));
- metric_report_queue_->Enqueue(metric_data, std::move(enqueue_cb));
+ metric_report_queue_->Enqueue(
+ std::make_unique<MetricData>(std::move(metric_data)),
+ std::move(enqueue_cb));
}
OneShotCollector::OneShotCollector(Sampler* sampler,
@@ -75,12 +77,17 @@ void OneShotCollector::Collect() {
CollectorBase::Collect();
}
-void OneShotCollector::OnMetricDataCollected(MetricData metric_data) {
+void OneShotCollector::OnMetricDataCollected(
+ absl::optional<MetricData> metric_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(on_data_reported_);
+ if (!metric_data.has_value()) {
+ return;
+ }
- metric_data.set_timestamp_ms(base::Time::Now().ToJavaTime());
- ReportMetricData(metric_data, std::move(on_data_reported_));
+ metric_data->set_timestamp_ms(base::Time::Now().ToJavaTime());
+ ReportMetricData(std::move(metric_data.value()),
+ std::move(on_data_reported_));
}
PeriodicCollector::PeriodicCollector(Sampler* sampler,
@@ -110,11 +117,15 @@ PeriodicCollector::PeriodicCollector(Sampler* sampler,
PeriodicCollector::~PeriodicCollector() = default;
-void PeriodicCollector::OnMetricDataCollected(MetricData metric_data) {
+void PeriodicCollector::OnMetricDataCollected(
+ absl::optional<MetricData> metric_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (!metric_data.has_value()) {
+ return;
+ }
- metric_data.set_timestamp_ms(base::Time::Now().ToJavaTime());
- ReportMetricData(metric_data);
+ metric_data->set_timestamp_ms(base::Time::Now().ToJavaTime());
+ ReportMetricData(std::move(metric_data.value()));
}
void PeriodicCollector::StartPeriodicCollection() {
@@ -139,23 +150,25 @@ AdditionalSamplersCollector::~AdditionalSamplersCollector() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
-void AdditionalSamplersCollector::CollectAll(MetricCallback on_all_collected_cb,
- MetricData metric_data) const {
- MetricData empty_metric_data;
+void AdditionalSamplersCollector::CollectAll(
+ OptionalMetricCallback on_all_collected_cb,
+ MetricData metric_data) const {
CollectAdditionalMetricData(
/*sampler_index=*/0, std::move(on_all_collected_cb),
- std::move(metric_data), std::move(empty_metric_data));
+ std::move(metric_data), /*new_metric_data=*/absl::nullopt);
}
void AdditionalSamplersCollector::CollectAdditionalMetricData(
uint64_t sampler_index,
- MetricCallback on_all_collected_cb,
+ OptionalMetricCallback on_all_collected_cb,
MetricData metric_data,
- MetricData new_metric_data) const {
+ absl::optional<MetricData> new_metric_data) const {
CHECK(base::SequencedTaskRunnerHandle::IsSet());
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- metric_data.CheckTypeAndMergeFrom(new_metric_data);
+ if (new_metric_data.has_value()) {
+ metric_data.CheckTypeAndMergeFrom(new_metric_data.value());
+ }
if (sampler_index == samplers_.size()) {
std::move(on_all_collected_cb).Run(std::move(metric_data));
return;
@@ -165,7 +178,7 @@ void AdditionalSamplersCollector::CollectAdditionalMetricData(
base::BindOnce(&AdditionalSamplersCollector::CollectAdditionalMetricData,
weak_ptr_factory_.GetWeakPtr(), sampler_index + 1,
std::move(on_all_collected_cb), std::move(metric_data));
- samplers_[sampler_index]->Collect(base::BindPostTask(
+ samplers_[sampler_index]->MaybeCollect(base::BindPostTask(
base::SequencedTaskRunnerHandle::Get(), std::move(on_collected_cb)));
}
@@ -195,13 +208,17 @@ PeriodicEventCollector::PeriodicEventCollector(
PeriodicEventCollector::~PeriodicEventCollector() = default;
-void PeriodicEventCollector::OnMetricDataCollected(MetricData metric_data) {
+void PeriodicEventCollector::OnMetricDataCollected(
+ absl::optional<MetricData> metric_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (!metric_data.has_value()) {
+ return;
+ }
- metric_data.set_timestamp_ms(base::Time::Now().ToJavaTime());
+ metric_data->set_timestamp_ms(base::Time::Now().ToJavaTime());
absl::optional<MetricEventType> event =
- event_detector_->DetectEvent(last_collected_data_, metric_data);
- last_collected_data_ = std::move(metric_data);
+ event_detector_->DetectEvent(last_collected_data_, metric_data.value());
+ last_collected_data_ = std::move(metric_data.value());
if (!event.has_value()) {
return;
}
@@ -214,9 +231,14 @@ void PeriodicEventCollector::OnMetricDataCollected(MetricData metric_data) {
}
void PeriodicEventCollector::OnAdditionalMetricDataCollected(
- MetricData metric_data) {
+ absl::optional<MetricData> metric_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (!metric_data.has_value()) {
+ NOTREACHED() << "Metric data is unexpectedly empty after additional metric "
+ << "collection.";
+ return;
+ }
- ReportMetricData(metric_data);
+ ReportMetricData(std::move(metric_data.value()));
}
} // namespace reporting
diff --git a/chromium/components/reporting/metrics/metric_data_collector.h b/chromium/components/reporting/metrics/metric_data_collector.h
index 7b7253fb382..1cbd49a38da 100644
--- a/chromium/components/reporting/metrics/metric_data_collector.h
+++ b/chromium/components/reporting/metrics/metric_data_collector.h
@@ -40,10 +40,11 @@ class CollectorBase {
protected:
virtual void Collect();
- virtual void OnMetricDataCollected(MetricData metric_data) = 0;
+ virtual void OnMetricDataCollected(
+ absl::optional<MetricData> metric_data) = 0;
virtual void ReportMetricData(
- const MetricData& metric_data,
+ MetricData metric_data,
base::OnceClosure on_data_reported = base::DoNothing());
SEQUENCE_CHECKER(sequence_checker_);
@@ -74,7 +75,7 @@ class OneShotCollector : public CollectorBase {
protected:
void Collect() override;
- void OnMetricDataCollected(MetricData metric_data) override;
+ void OnMetricDataCollected(absl::optional<MetricData> metric_data) override;
private:
std::unique_ptr<MetricReportingController> reporting_controller_;
@@ -103,7 +104,7 @@ class PeriodicCollector : public CollectorBase {
~PeriodicCollector() override;
protected:
- void OnMetricDataCollected(MetricData metric_data) override;
+ void OnMetricDataCollected(absl::optional<MetricData> metric_data) override;
private:
virtual void StartPeriodicCollection();
@@ -139,14 +140,15 @@ class AdditionalSamplersCollector {
~AdditionalSamplersCollector();
- void CollectAll(MetricCallback on_all_collected_cb,
+ void CollectAll(OptionalMetricCallback on_all_collected_cb,
MetricData metric_data) const;
private:
- void CollectAdditionalMetricData(uint64_t sampler_index,
- MetricCallback on_all_collected_cb,
- MetricData metric_data,
- MetricData new_metric_data) const;
+ void CollectAdditionalMetricData(
+ uint64_t sampler_index,
+ OptionalMetricCallback on_all_collected_cb,
+ MetricData metric_data,
+ absl::optional<MetricData> new_metric_data) const;
const std::vector<Sampler*> samplers_;
@@ -177,10 +179,10 @@ class PeriodicEventCollector : public PeriodicCollector {
~PeriodicEventCollector() override;
protected:
- void OnMetricDataCollected(MetricData metric_data) override;
+ void OnMetricDataCollected(absl::optional<MetricData> metric_data) override;
private:
- void OnAdditionalMetricDataCollected(MetricData metric_data);
+ void OnAdditionalMetricDataCollected(absl::optional<MetricData> metric_data);
const std::unique_ptr<EventDetector> event_detector_;
diff --git a/chromium/components/reporting/metrics/metric_data_collector_unittest.cc b/chromium/components/reporting/metrics/metric_data_collector_unittest.cc
index 9d1ab6f73de..e9cd451a65b 100644
--- a/chromium/components/reporting/metrics/metric_data_collector_unittest.cc
+++ b/chromium/components/reporting/metrics/metric_data_collector_unittest.cc
@@ -18,6 +18,7 @@
#include "components/reporting/metrics/fake_sampler.h"
#include "components/reporting/metrics/metric_report_queue.h"
#include "components/reporting/proto/synced/metric_data.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -36,7 +37,8 @@ class FakeEventDetector : public EventDetector {
absl::optional<MetricEventType> DetectEvent(
const MetricData& previous_metric_data,
const MetricData& current_metric_data) override {
- previous_metric_list_.emplace_back(previous_metric_data);
+ previous_metric_list_.emplace_back(
+ std::make_unique<const MetricData>(previous_metric_data));
if (!has_event_) {
return absl::nullopt;
}
@@ -45,14 +47,15 @@ class FakeEventDetector : public EventDetector {
void SetHasEvent(bool has_event) { has_event_ = has_event; }
- std::vector<MetricData> GetPreviousMetricList() {
+ const std::vector<std::unique_ptr<const MetricData>>& GetPreviousMetricList()
+ const {
return previous_metric_list_;
}
private:
bool has_event_ = false;
- std::vector<MetricData> previous_metric_list_;
+ std::vector<std::unique_ptr<const MetricData>> previous_metric_list_;
};
} // namespace test
@@ -110,12 +113,36 @@ TEST_F(MetricDataCollectorTest, OneShotCollector_InitiallyEnabled) {
EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
FlushTasks();
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
- ASSERT_EQ(metric_data_reported.size(), 1ul);
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(1));
EXPECT_TRUE(callback_called);
- EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[0].has_info_data());
+ EXPECT_TRUE(metric_data_reported[0]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[0]->has_info_data());
+}
+
+TEST_F(MetricDataCollectorTest, OneShotCollector_NoMetricData) {
+ settings_->SetBoolean(kEnableSettingPath, true);
+
+ sampler_->SetMetricData(absl::nullopt);
+ bool callback_called = false;
+
+ OneShotCollector collector(sampler_.get(), metric_report_queue_.get(),
+ settings_.get(), kEnableSettingPath,
+ /*setting_enabled_default_value=*/false,
+ base::BindLambdaForTesting([&callback_called]() {
+ callback_called = true;
+ }));
+
+ // Setting is initially enabled, data is being collected.
+ EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
+ FlushTasks();
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
+
+ ASSERT_TRUE(metric_data_reported.empty());
+ EXPECT_FALSE(callback_called);
}
TEST_F(MetricDataCollectorTest, OneShotCollector_InitiallyDisabled) {
@@ -145,11 +172,12 @@ TEST_F(MetricDataCollectorTest, OneShotCollector_InitiallyDisabled) {
EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
FlushTasks();
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
- ASSERT_EQ(metric_data_reported.size(), 1ul);
- EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[0].has_info_data());
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(1));
+ EXPECT_TRUE(metric_data_reported[0]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[0]->has_info_data());
}
TEST_F(MetricDataCollectorTest, OneShotCollector_DefaultEnabled) {
@@ -169,12 +197,13 @@ TEST_F(MetricDataCollectorTest, OneShotCollector_DefaultEnabled) {
EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
FlushTasks();
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
- ASSERT_EQ(metric_data_reported.size(), 1ul);
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(1));
EXPECT_TRUE(callback_called);
- EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[0].has_info_data());
+ EXPECT_TRUE(metric_data_reported[0]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[0]->has_info_data());
}
TEST_F(MetricDataCollectorTest, OneShotCollector_DefaultDisabled) {
@@ -253,20 +282,42 @@ TEST_F(MetricDataCollectorTest, PeriodicCollector_InitiallyEnabled) {
EXPECT_EQ(sampler_->GetNumCollectCalls(), expected_collect_calls);
FlushTasks();
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
- ASSERT_EQ(metric_data_reported.size(), 5ul);
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(5));
for (int i = 0; i < 5; ++i) {
- EXPECT_TRUE(metric_data_reported[i].has_timestamp_ms());
- EXPECT_EQ(metric_data_reported[i].has_telemetry_data(),
+ EXPECT_TRUE(metric_data_reported[i]->has_timestamp_ms());
+ EXPECT_EQ(metric_data_reported[i]->has_telemetry_data(),
metric_data[i].has_telemetry_data());
- EXPECT_EQ(metric_data_reported[i].has_info_data(),
+ EXPECT_EQ(metric_data_reported[i]->has_info_data(),
metric_data[i].has_info_data());
- EXPECT_EQ(metric_data_reported[i].has_event_data(),
+ EXPECT_EQ(metric_data_reported[i]->has_event_data(),
metric_data[i].has_event_data());
}
}
+TEST_F(MetricDataCollectorTest, PeriodicCollector_NoMetricData) {
+ constexpr int interval = 10000;
+ settings_->SetBoolean(kEnableSettingPath, true);
+ settings_->SetInteger(kRateSettingPath, interval);
+
+ sampler_->SetMetricData(absl::nullopt);
+
+ PeriodicCollector collector(
+ sampler_.get(), metric_report_queue_.get(), settings_.get(),
+ kEnableSettingPath, /*setting_enabled_default_value=*/false,
+ kRateSettingPath, base::Milliseconds(interval / 2));
+
+ // One initial collection at startup.
+ EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
+ FlushTasks();
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
+
+ ASSERT_TRUE(metric_data_reported.empty());
+}
+
TEST_F(MetricDataCollectorTest, PeriodicCollector_InitiallyDisabled) {
constexpr int interval = 10000;
settings_->SetBoolean(kEnableSettingPath, false);
@@ -296,13 +347,14 @@ TEST_F(MetricDataCollectorTest, PeriodicCollector_InitiallyDisabled) {
EXPECT_EQ(sampler_->GetNumCollectCalls(), 2);
FlushTasks();
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
-
- ASSERT_EQ(metric_data_reported.size(), 2ul);
- EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[0].has_telemetry_data());
- EXPECT_TRUE(metric_data_reported[1].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[1].has_telemetry_data());
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
+
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(2));
+ EXPECT_TRUE(metric_data_reported[0]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[0]->has_telemetry_data());
+ EXPECT_TRUE(metric_data_reported[1]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[1]->has_telemetry_data());
}
TEST_F(MetricDataCollectorTest, PeriodicCollector_DefaultEnabled) {
@@ -331,14 +383,15 @@ TEST_F(MetricDataCollectorTest, PeriodicCollector_DefaultEnabled) {
EXPECT_EQ(sampler_->GetNumCollectCalls(), 2);
FlushTasks();
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
-
- ASSERT_EQ(metric_data_reported.size(), 2ul);
- EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[0].has_telemetry_data());
- EXPECT_TRUE(metric_data_reported[1].has_timestamp_ms());
- EXPECT_FALSE(metric_data_reported[1].has_telemetry_data());
- EXPECT_TRUE(metric_data_reported[1].has_event_data());
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
+
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(2));
+ EXPECT_TRUE(metric_data_reported[0]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[0]->has_telemetry_data());
+ EXPECT_TRUE(metric_data_reported[1]->has_timestamp_ms());
+ EXPECT_FALSE(metric_data_reported[1]->has_telemetry_data());
+ EXPECT_TRUE(metric_data_reported[1]->has_event_data());
}
TEST_F(MetricDataCollectorTest, PeriodicCollector_DefaultDisabled) {
@@ -399,32 +452,34 @@ TEST_F(MetricDataCollectorTest, PeriodicEventCollector_NoAdditionalSamplers) {
// Data collected but not reported.
EXPECT_EQ(sampler_->GetNumCollectCalls(), 3);
FlushTasks();
- auto previous_metric_list = event_detector_ptr->GetPreviousMetricList();
-
- ASSERT_EQ(previous_metric_list.size(), 3ul);
-
- EXPECT_FALSE(previous_metric_list[0].has_timestamp_ms());
- EXPECT_FALSE(previous_metric_list[0].has_info_data());
- EXPECT_FALSE(previous_metric_list[0].has_telemetry_data());
- EXPECT_FALSE(previous_metric_list[0].has_event_data());
-
- EXPECT_TRUE(previous_metric_list[1].has_timestamp_ms());
- EXPECT_TRUE(previous_metric_list[1].has_info_data());
- EXPECT_FALSE(previous_metric_list[1].has_telemetry_data());
- EXPECT_FALSE(previous_metric_list[1].has_event_data());
-
- EXPECT_TRUE(previous_metric_list[2].has_timestamp_ms());
- EXPECT_FALSE(previous_metric_list[2].has_info_data());
- EXPECT_TRUE(previous_metric_list[2].has_telemetry_data());
- EXPECT_TRUE(previous_metric_list[2].has_event_data());
-
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
-
- ASSERT_EQ(metric_data_reported.size(), 1ul);
- EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
- EXPECT_FALSE(metric_data_reported[0].has_info_data());
- EXPECT_TRUE(metric_data_reported[0].has_telemetry_data());
- EXPECT_TRUE(metric_data_reported[0].has_event_data());
+ const auto& previous_metric_list =
+ event_detector_ptr->GetPreviousMetricList();
+
+ ASSERT_THAT(previous_metric_list, ::testing::SizeIs(3));
+
+ EXPECT_FALSE(previous_metric_list[0]->has_timestamp_ms());
+ EXPECT_FALSE(previous_metric_list[0]->has_info_data());
+ EXPECT_FALSE(previous_metric_list[0]->has_telemetry_data());
+ EXPECT_FALSE(previous_metric_list[0]->has_event_data());
+
+ EXPECT_TRUE(previous_metric_list[1]->has_timestamp_ms());
+ EXPECT_TRUE(previous_metric_list[1]->has_info_data());
+ EXPECT_FALSE(previous_metric_list[1]->has_telemetry_data());
+ EXPECT_FALSE(previous_metric_list[1]->has_event_data());
+
+ EXPECT_TRUE(previous_metric_list[2]->has_timestamp_ms());
+ EXPECT_FALSE(previous_metric_list[2]->has_info_data());
+ EXPECT_TRUE(previous_metric_list[2]->has_telemetry_data());
+ EXPECT_TRUE(previous_metric_list[2]->has_event_data());
+
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
+
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(1));
+ EXPECT_TRUE(metric_data_reported[0]->has_timestamp_ms());
+ EXPECT_FALSE(metric_data_reported[0]->has_info_data());
+ EXPECT_TRUE(metric_data_reported[0]->has_telemetry_data());
+ EXPECT_TRUE(metric_data_reported[0]->has_event_data());
}
TEST_F(MetricDataCollectorTest, PeriodicEventCollector_WithAdditionalSamplers) {
@@ -456,6 +511,8 @@ TEST_F(MetricDataCollectorTest, PeriodicEventCollector_WithAdditionalSamplers) {
additional_samplers[i].SetMetricData(additional_metric_data[i]);
additional_sampler_ptrs.emplace_back(additional_samplers + i);
}
+ test::FakeSampler empty_additional_sampler;
+ additional_sampler_ptrs.emplace_back(&empty_additional_sampler);
auto event_detector = std::make_unique<test::FakeEventDetector>();
@@ -472,21 +529,22 @@ TEST_F(MetricDataCollectorTest, PeriodicEventCollector_WithAdditionalSamplers) {
EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
task_environment_.RunUntilIdle();
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
- ASSERT_EQ(metric_data_reported.size(), 1ul);
- EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[0].has_event_data());
- ASSERT_TRUE(metric_data_reported[0].has_telemetry_data());
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(1));
+ EXPECT_TRUE(metric_data_reported[0]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[0]->has_event_data());
+ ASSERT_TRUE(metric_data_reported[0]->has_telemetry_data());
ASSERT_TRUE(
- metric_data_reported[0].telemetry_data().has_networks_telemetry());
+ metric_data_reported[0]->telemetry_data().has_networks_telemetry());
ASSERT_TRUE(metric_data_reported[0]
- .telemetry_data()
+ ->telemetry_data()
.networks_telemetry()
.has_https_latency_data());
auto https_latency_data = metric_data_reported[0]
- .telemetry_data()
+ ->telemetry_data()
.networks_telemetry()
.https_latency_data();
EXPECT_EQ(https_latency_data.verdict(), additional_metric_data[0]
diff --git a/chromium/components/reporting/metrics/metric_event_observer_manager.cc b/chromium/components/reporting/metrics/metric_event_observer_manager.cc
index 301b9675c27..de767125746 100644
--- a/chromium/components/reporting/metrics/metric_event_observer_manager.cc
+++ b/chromium/components/reporting/metrics/metric_event_observer_manager.cc
@@ -73,8 +73,13 @@ void MetricEventObserverManager::OnEventObserved(MetricData metric_data) {
std::move(metric_data));
}
-void MetricEventObserverManager::Report(MetricData metric_data) {
+void MetricEventObserverManager::Report(
+ absl::optional<MetricData> metric_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (!metric_data.has_value()) {
+ NOTREACHED() << "Reporting requested for empty metric data.";
+ return;
+ }
auto enqueue_cb = base::BindOnce([](Status status) {
if (!status.ok()) {
@@ -83,6 +88,8 @@ void MetricEventObserverManager::Report(MetricData metric_data) {
<< status;
}
});
- metric_report_queue_->Enqueue(metric_data, std::move(enqueue_cb));
+ metric_report_queue_->Enqueue(
+ std::make_unique<MetricData>(std::move(metric_data.value())),
+ std::move(enqueue_cb));
}
} // namespace reporting
diff --git a/chromium/components/reporting/metrics/metric_event_observer_manager.h b/chromium/components/reporting/metrics/metric_event_observer_manager.h
index e11b9a11254..161ffe6415a 100644
--- a/chromium/components/reporting/metrics/metric_event_observer_manager.h
+++ b/chromium/components/reporting/metrics/metric_event_observer_manager.h
@@ -13,6 +13,7 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace reporting {
@@ -46,7 +47,7 @@ class MetricEventObserverManager {
void OnEventObserved(MetricData metric_data);
- void Report(MetricData metric_data);
+ void Report(absl::optional<MetricData> metric_data);
const std::unique_ptr<MetricEventObserver> event_observer_;
diff --git a/chromium/components/reporting/metrics/metric_event_observer_manager_unittest.cc b/chromium/components/reporting/metrics/metric_event_observer_manager_unittest.cc
index c1e6eb55316..f1142547f23 100644
--- a/chromium/components/reporting/metrics/metric_event_observer_manager_unittest.cc
+++ b/chromium/components/reporting/metrics/metric_event_observer_manager_unittest.cc
@@ -17,7 +17,9 @@
#include "components/reporting/metrics/fake_sampler.h"
#include "components/reporting/metrics/metric_report_queue.h"
#include "components/reporting/proto/synced/metric_data.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace reporting {
namespace {
@@ -33,7 +35,7 @@ class MetricEventObserverManagerTest : public ::testing::Test {
protected:
base::test::SingleThreadTaskEnvironment task_environment_;
- const std::string kEnableSettingPath = "enable_path";
+ static constexpr char kEnableSettingPath[] = "enable_path";
std::unique_ptr<test::FakeReportingSettings> settings_;
std::unique_ptr<test::FakeMetricEventObserver> event_observer_;
@@ -56,10 +58,11 @@ TEST_F(MetricEventObserverManagerTest, InitiallyEnabled) {
for (size_t i = 0; i < reporting_count; ++i) {
event_observer_ptr->RunCallback(metric_data);
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
- ASSERT_EQ(metric_data_reported.size(), i + 1);
- EXPECT_TRUE(metric_data_reported[i].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[i].has_event_data());
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(i + 1));
+ EXPECT_TRUE(metric_data_reported[i]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[i]->has_event_data());
}
// Setting disabled, no more data should be reported even if the callback is
@@ -69,8 +72,8 @@ TEST_F(MetricEventObserverManagerTest, InitiallyEnabled) {
event_observer_ptr->RunCallback(metric_data);
ASSERT_FALSE(event_observer_ptr->GetReportingEnabled());
- EXPECT_EQ(metric_report_queue_->GetMetricDataReported().size(),
- reporting_count);
+ EXPECT_THAT(metric_report_queue_->GetMetricDataReported(),
+ ::testing::SizeIs(reporting_count));
}
TEST_F(MetricEventObserverManagerTest, InitiallyDisabled) {
@@ -94,9 +97,10 @@ TEST_F(MetricEventObserverManagerTest, InitiallyDisabled) {
event_observer_ptr->RunCallback(metric_data);
ASSERT_TRUE(event_observer_ptr->GetReportingEnabled());
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
- ASSERT_EQ(metric_data_reported.size(), 1ul);
- EXPECT_TRUE(metric_data_reported[0].has_event_data());
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(1));
+ EXPECT_TRUE(metric_data_reported[0]->has_event_data());
}
TEST_F(MetricEventObserverManagerTest, DefaultEnabled) {
@@ -112,10 +116,11 @@ TEST_F(MetricEventObserverManagerTest, DefaultEnabled) {
ASSERT_TRUE(event_observer_ptr->GetReportingEnabled());
event_observer_ptr->RunCallback(metric_data);
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
- ASSERT_EQ(metric_data_reported.size(), 1ul);
- EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[0].has_event_data());
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(1));
+ EXPECT_TRUE(metric_data_reported[0]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[0]->has_event_data());
}
TEST_F(MetricEventObserverManagerTest, DefaultDisabled) {
@@ -142,30 +147,32 @@ TEST_F(MetricEventObserverManagerTest, AdditionalSamplers) {
additional_metric_data.mutable_telemetry_data();
test::FakeSampler additional_sampler;
+ test::FakeSampler empty_additional_sampler;
MetricEventObserverManager event_manager(
std::move(event_observer_), metric_report_queue_.get(), settings_.get(),
kEnableSettingPath, /*setting_enabled_default_value=*/false,
- {&additional_sampler});
+ {&additional_sampler, &empty_additional_sampler});
MetricData metric_data;
metric_data.mutable_event_data();
ASSERT_TRUE(event_observer_ptr->GetReportingEnabled());
- for (size_t i = 0; i < 2; ++i) {
- base::RunLoop run_loop;
- additional_sampler.SetMetricData(additional_metric_data);
- event_observer_ptr->RunCallback(metric_data);
- base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- run_loop.QuitClosure());
- run_loop.Run();
-
- auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
- ASSERT_EQ(metric_data_reported.size(), i + 1);
- EXPECT_TRUE(metric_data_reported[i].has_timestamp_ms());
- EXPECT_TRUE(metric_data_reported[i].has_event_data());
- EXPECT_TRUE(metric_data_reported[i].has_telemetry_data());
- }
+
+ additional_sampler.SetMetricData(additional_metric_data);
+ empty_additional_sampler.SetMetricData(absl::nullopt);
+ event_observer_ptr->RunCallback(metric_data);
+ task_environment_.RunUntilIdle();
+
+ const auto& metric_data_reported =
+ metric_report_queue_->GetMetricDataReported();
+
+ ASSERT_EQ(additional_sampler.GetNumCollectCalls(), 1);
+ EXPECT_EQ(empty_additional_sampler.GetNumCollectCalls(), 1);
+ ASSERT_THAT(metric_data_reported, ::testing::SizeIs(1));
+ EXPECT_TRUE(metric_data_reported[0]->has_timestamp_ms());
+ EXPECT_TRUE(metric_data_reported[0]->has_event_data());
+ EXPECT_TRUE(metric_data_reported[0]->has_telemetry_data());
}
} // namespace
diff --git a/chromium/components/reporting/metrics/metric_report_queue.cc b/chromium/components/reporting/metrics/metric_report_queue.cc
index d571648f5fb..2cc535bf31d 100644
--- a/chromium/components/reporting/metrics/metric_report_queue.cc
+++ b/chromium/components/reporting/metrics/metric_report_queue.cc
@@ -34,9 +34,10 @@ MetricReportQueue::MetricReportQueue(
MetricReportQueue::~MetricReportQueue() = default;
-void MetricReportQueue::Enqueue(const MetricData& metric_data,
+void MetricReportQueue::Enqueue(std::unique_ptr<const MetricData> metric_data,
ReportQueue::EnqueueCallback callback) {
- report_queue_->Enqueue(&metric_data, priority_, std::move(callback));
+ report_queue_->Enqueue(std::move(metric_data), priority_,
+ std::move(callback));
}
void MetricReportQueue::Upload() {
diff --git a/chromium/components/reporting/metrics/metric_report_queue.h b/chromium/components/reporting/metrics/metric_report_queue.h
index ec4583319a4..b03055fefa6 100644
--- a/chromium/components/reporting/metrics/metric_report_queue.h
+++ b/chromium/components/reporting/metrics/metric_report_queue.h
@@ -43,7 +43,7 @@ class MetricReportQueue {
virtual ~MetricReportQueue();
// Enqueue the metric data.
- virtual void Enqueue(const MetricData& metric_data,
+ virtual void Enqueue(std::unique_ptr<const MetricData> metric_data,
ReportQueue::EnqueueCallback callback);
// Initiate manual upload of records with `priority_` and restart timer if
diff --git a/chromium/components/reporting/metrics/metric_report_queue_unittest.cc b/chromium/components/reporting/metrics/metric_report_queue_unittest.cc
index 7a9a94d3140..fe28437fc78 100644
--- a/chromium/components/reporting/metrics/metric_report_queue_unittest.cc
+++ b/chromium/components/reporting/metrics/metric_report_queue_unittest.cc
@@ -48,8 +48,8 @@ class MetricReportQueueTest : public ::testing::Test {
TEST_F(MetricReportQueueTest, ManualUpload) {
auto mock_queue =
- std::unique_ptr<::reporting::MockReportQueue, base::OnTaskRunnerDeleter>(
- new testing::StrictMock<::reporting::MockReportQueue>(),
+ std::unique_ptr<MockReportQueueStrict, base::OnTaskRunnerDeleter>(
+ new MockReportQueueStrict(),
base::OnTaskRunnerDeleter(
base::ThreadPool::CreateSequencedTaskRunner({})));
auto* mock_queue_ptr = mock_queue.get();
@@ -72,8 +72,9 @@ TEST_F(MetricReportQueueTest, ManualUpload) {
});
bool callback_called = false;
metric_report_queue.Enqueue(
- record, base::BindLambdaForTesting(
- [&callback_called](Status) { callback_called = true; }));
+ std::make_unique<MetricData>(record),
+ base::BindLambdaForTesting(
+ [&callback_called](Status) { callback_called = true; }));
EXPECT_TRUE(callback_called);
EXPECT_CALL(*mock_queue_ptr, Flush(priority_, _)).Times(1);
@@ -84,11 +85,10 @@ TEST_F(MetricReportQueueTest, ManualUploadWithTimer) {
settings_->SetInteger(kRateSettingPath, kRateMs);
int upload_count = 0;
- auto mock_queue =
- std::unique_ptr<::reporting::MockReportQueue, base::OnTaskRunnerDeleter>(
- new testing::NiceMock<::reporting::MockReportQueue>(),
- base::OnTaskRunnerDeleter(
- base::ThreadPool::CreateSequencedTaskRunner({})));
+ auto mock_queue = std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>(
+ new MockReportQueue(),
+ base::OnTaskRunnerDeleter(
+ base::ThreadPool::CreateSequencedTaskRunner({})));
auto* mock_queue_ptr = mock_queue.get();
MetricData record;
record.set_timestamp_ms(123456);
@@ -98,7 +98,7 @@ TEST_F(MetricReportQueueTest, ManualUploadWithTimer) {
kDefaultRate);
EXPECT_CALL(*mock_queue_ptr, AddRecord(_, _, _))
- .WillOnce([&record, this](base::StringPiece record_string,
+ .WillOnce([&record, this](std::string record_string,
Priority actual_priority,
ReportQueue::EnqueueCallback cb) {
std::move(cb).Run(Status());
@@ -111,8 +111,9 @@ TEST_F(MetricReportQueueTest, ManualUploadWithTimer) {
});
bool callback_called = false;
metric_report_queue.Enqueue(
- record, base::BindLambdaForTesting(
- [&callback_called](Status) { callback_called = true; }));
+ std::make_unique<MetricData>(record),
+ base::BindLambdaForTesting(
+ [&callback_called](Status) { callback_called = true; }));
EXPECT_TRUE(callback_called);
ON_CALL(*mock_queue_ptr, Flush(priority_, _)).WillByDefault([&]() {
@@ -135,8 +136,8 @@ TEST_F(MetricReportQueueTest, ManualUploadWithTimer) {
TEST_F(MetricReportQueueTest, RateControlledFlush_TimeNotElapsed) {
settings_->SetInteger(kRateSettingPath, kRateMs);
auto mock_queue =
- std::unique_ptr<::reporting::MockReportQueue, base::OnTaskRunnerDeleter>(
- new testing::StrictMock<::reporting::MockReportQueue>(),
+ std::unique_ptr<MockReportQueueStrict, base::OnTaskRunnerDeleter>(
+ new MockReportQueueStrict(),
base::OnTaskRunnerDeleter(
base::ThreadPool::CreateSequencedTaskRunner({})));
auto* mock_queue_ptr = mock_queue.get();
@@ -148,7 +149,7 @@ TEST_F(MetricReportQueueTest, RateControlledFlush_TimeNotElapsed) {
kDefaultRate);
EXPECT_CALL(*mock_queue_ptr, AddRecord(_, _, _))
- .WillOnce([&record, this](base::StringPiece record_string,
+ .WillOnce([&record, this](std::string record_string,
Priority actual_priority,
ReportQueue::EnqueueCallback cb) {
std::move(cb).Run(Status());
@@ -161,8 +162,9 @@ TEST_F(MetricReportQueueTest, RateControlledFlush_TimeNotElapsed) {
});
bool callback_called = false;
metric_report_queue.Enqueue(
- record, base::BindLambdaForTesting(
- [&callback_called](Status) { callback_called = true; }));
+ std::make_unique<MetricData>(record),
+ base::BindLambdaForTesting(
+ [&callback_called](Status) { callback_called = true; }));
EXPECT_TRUE(callback_called);
EXPECT_CALL(*mock_queue_ptr, Flush).Times(0);
@@ -172,8 +174,8 @@ TEST_F(MetricReportQueueTest, RateControlledFlush_TimeNotElapsed) {
TEST_F(MetricReportQueueTest, RateControlledFlush_TimeElapsed) {
settings_->SetInteger(kRateSettingPath, kRateMs);
auto mock_queue =
- std::unique_ptr<::reporting::MockReportQueue, base::OnTaskRunnerDeleter>(
- new testing::StrictMock<::reporting::MockReportQueue>(),
+ std::unique_ptr<MockReportQueueStrict, base::OnTaskRunnerDeleter>(
+ new MockReportQueueStrict(),
base::OnTaskRunnerDeleter(
base::ThreadPool::CreateSequencedTaskRunner({})));
auto* mock_queue_ptr = mock_queue.get();
@@ -185,7 +187,7 @@ TEST_F(MetricReportQueueTest, RateControlledFlush_TimeElapsed) {
kDefaultRate);
EXPECT_CALL(*mock_queue_ptr, AddRecord(_, _, _))
- .WillOnce([&record, this](base::StringPiece record_string,
+ .WillOnce([&record, this](std::string record_string,
Priority actual_priority,
ReportQueue::EnqueueCallback cb) {
std::move(cb).Run(Status());
@@ -198,8 +200,9 @@ TEST_F(MetricReportQueueTest, RateControlledFlush_TimeElapsed) {
});
bool callback_called = false;
metric_report_queue.Enqueue(
- record, base::BindLambdaForTesting(
- [&callback_called](Status) { callback_called = true; }));
+ std::make_unique<MetricData>(record),
+ base::BindLambdaForTesting(
+ [&callback_called](Status) { callback_called = true; }));
EXPECT_TRUE(callback_called);
EXPECT_CALL(*mock_queue_ptr, Flush(priority_, _)).Times(1);
diff --git a/chromium/components/reporting/metrics/sampler.h b/chromium/components/reporting/metrics/sampler.h
index fcd11c0d77f..6c1f200d1a4 100644
--- a/chromium/components/reporting/metrics/sampler.h
+++ b/chromium/components/reporting/metrics/sampler.h
@@ -7,9 +7,12 @@
#include "base/callback.h"
#include "components/reporting/proto/synced/metric_data.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace reporting {
+using OptionalMetricCallback =
+ base::OnceCallback<void(absl::optional<MetricData>)>;
using MetricCallback = base::OnceCallback<void(MetricData)>;
using MetricRepeatingCallback = base::RepeatingCallback<void(MetricData)>;
@@ -27,7 +30,7 @@ using MetricRepeatingCallback = base::RepeatingCallback<void(MetricData)>;
class Sampler {
public:
virtual ~Sampler() = default;
- virtual void Collect(MetricCallback callback) = 0;
+ virtual void MaybeCollect(OptionalMetricCallback callback) = 0;
};
// A `MetricEventObserver` object should observe events and report them using
diff --git a/chromium/components/reporting/proto/BUILD.gn b/chromium/components/reporting/proto/BUILD.gn
index 32e38c8e85b..0c149cbe1fe 100644
--- a/chromium/components/reporting/proto/BUILD.gn
+++ b/chromium/components/reporting/proto/BUILD.gn
@@ -24,7 +24,10 @@ proto_library("record_proto") {
sources = [ "synced/record.proto" ]
- deps = [ ":record_constants" ]
+ deps = [
+ ":health_proto",
+ ":record_constants",
+ ]
}
proto_library("interface_proto") {
@@ -33,7 +36,7 @@ proto_library("interface_proto") {
# Generate JS so it can be used by chrome extensions
generate_javascript = true
- sources = [ "interface.proto" ]
+ sources = [ "synced/interface.proto" ]
proto_deps = [
":record_constants",
@@ -42,6 +45,20 @@ proto_library("interface_proto") {
]
}
+proto_library("health_proto") {
+ proto_in_dir = "//"
+
+ # Generate JS so it can be used by chrome extensions
+ generate_javascript = true
+
+ sources = [ "synced/health.proto" ]
+
+ deps = [
+ ":record_constants",
+ "//components/reporting/util:status_proto",
+ ]
+}
+
proto_library("test_proto") {
proto_in_dir = "//"
sources = [ "test.proto" ]
diff --git a/chromium/components/reporting/proto/interface.proto b/chromium/components/reporting/proto/interface.proto
deleted file mode 100644
index cfdcab15b27..00000000000
--- a/chromium/components/reporting/proto/interface.proto
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2021 The Chromium OS Authors. All rights reserved. Use
-// of this source code is governed by a BSD-style license that can
-// be found in the LICENSE file.
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-
-package reporting;
-
-import "components/reporting/proto/synced/record_constants.proto";
-import "components/reporting/proto/synced/record.proto";
-import "components/reporting/util/status.proto";
-
-// ------------ CLIENT/USER REQUEST AND RESPONSES -----------------
-// EnqueueRecordRequest enqueues records for encryption,
-// storage, and upload.
-message EnqueueRecordRequest {
- // Record the user wants to enqueue.
- optional Record record = 1;
-
- // Priority from
- // //chrome/cros/reporting/api/proto/synced/record_constants.proto
- // indicates what priority queue the record should be included in.
- optional Priority priority = 2;
-}
-
-// EnqueueRecordResponse indicates the enqueue success or
-// failure.
-message EnqueueRecordResponse {
- // Indicates success or failure of EnqueueRecordRequest.
- // Specific error codes indicate if a retry is appropriate.
- // Expected errors:
- // FAILED_PRECONDITION:
- // "The daemon is unable to locate the public key for record encryption."
- // - Not Retryable
- // FAILED_PRECONDITION:
- // "The daemon has insufficient permissions to read/write from/to disk"
- // - Not Retryable
- // FAILED_PRECONDITION:
- // "Policy controlling daemon is either unset or off."
- // - Not Retryable
- // UNAVAILABLE:
- // "The daemon is still starting."
- // - Retryable
- // RESOURCE_EXHAUSTED:
- // "The daemon has no available threads for processing"
- // - Retryable
- optional StatusProto status = 1;
-}
-
-// FlushPriorityRequest requests that the indicated priority
-// queue is flushed (records are uploaded to the server).
-message FlushPriorityRequest {
- // Priority of the desired queue. Defaults to MANUAL_BATCH.
- optional Priority priority = 1 [default = MANUAL_BATCH];
-}
-
-// FlushPriorityResponse indicates success or failure of
-// processing FlushPriorityRequest.
-message FlushPriorityResponse {
- // Indicates success or failure of FlushPriorityRequest.
- // Specific error codes indicate if a retry is appropriate.
- // Expected errors:
- // FAILED_PRECONDITION:
- // "The daemon is unable to locate the public key for record encryption."
- // - Not Retryable
- // FAILED_PRECONDITION:
- // "The daemon has insufficient permissions to read/write from/to disk"
- // - Not Retryable
- // FAILED_PRECONDITION:
- // "Policy controlling daemon is either unset or off."
- // - Not Retryable
- // UNAVAILABLE:
- // "The daemon is still starting."
- // - Retryable
- optional StatusProto status = 1;
-}
-
-// --------------- CHROME CALLS AND RESPONSES -------------------
-// Daemon DBus call to Chrome for uploading records. The Daemon sends a
-// batch of records, and the Upload Service accepts as many (but at least
-// one) as it can for upload. Records not sent for upload can be sent again
-// at a later time.
-message UploadEncryptedRecordRequest {
- repeated EncryptedRecord encrypted_record = 1;
-
- // Must be either true or absent. If true, server should send the server's
- // public values in the next response. It corresponds to the
- // "attachEncryptionSettings" field in the payload.
- optional bool need_encryption_keys = 2;
-}
-
-message UploadEncryptedRecordResponse {
- // Status indicates if the records will be uploaded.
- // Expected Errors:
- // UNAVAILABLE:
- // "No internet connection. Unable to upload records at this time."
- // - Retryable
- // FAILED_PRECONDITION:
- // "Policy controlling reporting upload is unset or off."
- // - Not Retryable
- optional StatusProto status = 1;
-}
-
-// ConfirmRecordUploadRequest is expected to only come from
-// Chrome and indicates the record with the provided
-// SequenceInformation successfully uploaded. It is
-// only sent by Chrome after a successful upload.
-message ConfirmRecordUploadRequest {
- // SequenceInformation of the successfully uploaded record.
- // SequenceInformation is part of the UploadEncryptedRecordRequest
- // sent to Chrome.
- optional SequenceInformation sequence_information = 1;
-
- // If true, daemon should update to the provided sequence_information
- // regardless of its current state.
- optional bool force_confirm = 2;
-}
-
-// ConfirmRecordUploadResponse indicates that the request was
-// successfully resolved. In the event that the daemon is unable
-// to process the ConfirmRecordUploadeRequest it is safe to drop it.
-// On the next upload, the server will ignore all records with
-// SequenceInformation lower than the highest it has processed.
-// The next ConfirmRecordUploadRequest will ensure that they are
-// deleted from disk. Some data transmission will be repeated, but
-// data will not be lost.
-message ConfirmRecordUploadResponse {
- // Indicates success or failure of ConfirmRecordUploadRequest.
- // Specific error codes indicate if a retry is appropriate.
- // Expected errors:
- // FAILED_PRECONDITION:
- // "The daemon has insufficient permissions to read/write from/to disk"
- // - Not Retryable
- // FAILED_PRECONDITION:
- // "Policy controlling daemon is either unset or off."
- // - Not Retryable
- // UNAVAILABLE:
- // "The daemon is still starting."
- // - Retryable
- optional StatusProto status = 1;
-}
-
-// UpdateEncryptionKeyRequest sends a SignedEncryptionInfo to the daemon.
-message UpdateEncryptionKeyRequest {
- optional SignedEncryptionInfo signed_encryption_info = 1;
-}
-
-message UpdateEncryptionKeyResponse {
- // Indicates success or failure of UpdateEncryptionKeyRequest.
- // Specific error codes indicate if a retry is appropriate.
- // Expected errors:
- // FAILED_PRECONDITION:
- // "The daemon has insufficient permissions to read/write from/to disk"
- // - Not Retryable
- // FAILED_PRECONDITION:
- // "Policy controlling daemon is either unset or off."
- // - Not Retryable
- // UNAVAILABLE:
- // "The daemon is still starting."
- // - Retryable
- optional StatusProto status = 1;
-}
diff --git a/chromium/components/reporting/proto/synced/health.proto b/chromium/components/reporting/proto/synced/health.proto
new file mode 100644
index 00000000000..698d703f274
--- /dev/null
+++ b/chromium/components/reporting/proto/synced/health.proto
@@ -0,0 +1,75 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package reporting;
+
+import "components/reporting/proto/synced/record_constants.proto";
+import "components/reporting/util/status.proto";
+
+// Information about records being removed from a storage queue.
+message StorageDequeue {
+ optional int64 sequencing_id = 1;
+ optional int64 records_count = 2;
+}
+
+// Information about records being added to a storage queue.
+message StorageEnqueue {
+ optional int64 sequencing_id = 1;
+}
+
+// Data about a record being added or removed from a storage queue
+message StorageQueueAction {
+ oneof action {
+ StorageDequeue storage_dequeue = 1;
+ StorageEnqueue storage_enqueue = 2;
+ }
+
+ optional StatusProto status = 3;
+}
+
+// Data about the EnqueueRecord dBus call.
+message EnqueueRecordCall {
+ optional Priority priority = 1;
+ optional StatusProto status = 2;
+}
+
+// Data about the FlushPriority dBus call.
+message FlushPriorityCall {
+ optional Priority priority = 1;
+ optional StatusProto status = 2;
+}
+
+// Data about the UploadEncryptedRecord dBus call.
+message UploadEncryptedRecordCall {
+ optional int64 sequencing_id = 1;
+ optional bool encryption_key_requested = 2;
+ optional StatusProto status = 3;
+}
+
+// Data about the ConfirmRecordUpload dBus call.
+message ConfirmRecordUploadCall {
+ optional int64 sequencing_id = 1;
+ optional bool force_confirm = 2;
+}
+
+message HealthDataHistory {
+ oneof record {
+ EnqueueRecordCall enqueue_record_call = 1;
+ FlushPriorityCall flush_priority_call = 2;
+ UploadEncryptedRecordCall upload_encrypted_record_call = 3;
+ ConfirmRecordUploadCall confirm_record_upload_call = 4;
+ StorageQueueAction storage_queue_action = 5;
+ }
+
+ optional int64 timestamp_seconds = 6;
+}
+
+// Aggregate health data of the ERP.
+message ERPHealthData {
+ repeated HealthDataHistory history = 1;
+}
diff --git a/chromium/components/reporting/proto/synced/interface.proto b/chromium/components/reporting/proto/synced/interface.proto
index b08f61e844d..a81d30621a0 100644
--- a/chromium/components/reporting/proto/synced/interface.proto
+++ b/chromium/components/reporting/proto/synced/interface.proto
@@ -10,7 +10,7 @@ package reporting;
import "components/reporting/proto/synced/record_constants.proto";
import "components/reporting/proto/synced/record.proto";
-import "components/reporting/status/status.proto";
+import "components/reporting/util/status.proto";
// ------------ CLIENT/USER REQUEST AND RESPONSES -----------------
// EnqueueRecordRequest enqueues records for encryption,
@@ -89,6 +89,12 @@ message UploadEncryptedRecordRequest {
// public values in the next response. It corresponds to the
// "attachEncryptionSettings" field in the payload.
optional bool need_encryption_keys = 2;
+
+ // The remaining storage capacity in bytes.
+ optional uint64 remaining_storage_capacity = 3;
+
+ // The rate (bytes/sec) at which new events are added to the storage.
+ optional uint64 new_events_rate = 4;
}
message UploadEncryptedRecordResponse {
diff --git a/chromium/components/reporting/proto/synced/metric_data.proto b/chromium/components/reporting/proto/synced/metric_data.proto
index 439d19a543c..adb431748c6 100644
--- a/chromium/components/reporting/proto/synced/metric_data.proto
+++ b/chromium/components/reporting/proto/synced/metric_data.proto
@@ -110,6 +110,8 @@ message NetworkTelemetry {
optional int64 link_quality = 13;
// Wifi power management enabled
optional bool power_management_enabled = 14;
+ // Signal strength for wireless networks in dBm.
+ optional int32 signal_strength_dbm = 15;
}
// Configured networks telemetry data.
@@ -293,6 +295,19 @@ message BootPerformanceTelemetry {
optional string shutdown_reason = 5;
}
+// Data tracking user status information
+message UserStatusTelemetry {
+ enum DeviceActivityState {
+ DEVICE_ACTIVITY_STATE_UNKNOWN = 0;
+ ACTIVE = 1;
+ IDLE = 2;
+ LOCKED = 3;
+ }
+
+ // Device state.
+ optional DeviceActivityState device_activity_state = 1;
+}
+
// Data that can change over time, collected and reported every specific period
// of time or when an event occur.
message TelemetryData {
@@ -306,6 +321,8 @@ message TelemetryData {
optional PeripheralsTelemetry peripherals_telemetry = 3;
// Boot Performance telemetry data.
optional BootPerformanceTelemetry boot_performance_telemetry = 4;
+ // User status telemetry data.
+ optional UserStatusTelemetry user_status_telemetry = 5;
}
enum MetricEventType {
diff --git a/chromium/components/reporting/proto/synced/record.proto b/chromium/components/reporting/proto/synced/record.proto
index 1899e5a5f93..c64c75eef93 100644
--- a/chromium/components/reporting/proto/synced/record.proto
+++ b/chromium/components/reporting/proto/synced/record.proto
@@ -8,6 +8,7 @@ option optimize_for = LITE_RUNTIME;
package reporting;
+import "components/reporting/proto/synced/health.proto";
import "components/reporting/proto/synced/record_constants.proto";
// Record represents the data sent from the Reporting Client.
@@ -114,6 +115,8 @@ message EncryptedRecord {
optional CompressionInformation compression_information = 4;
optional SequenceInformation sequencing_information = 5 [deprecated = true];
+
+ optional ERPHealthData erp_health_data = 6;
}
// Tracking information for what compression is used.
diff --git a/chromium/components/reporting/resources/BUILD.gn b/chromium/components/reporting/resources/BUILD.gn
index 56960d449ba..7d788b9e7c6 100644
--- a/chromium/components/reporting/resources/BUILD.gn
+++ b/chromium/components/reporting/resources/BUILD.gn
@@ -5,7 +5,10 @@
import("//build/config/features.gni")
static_library("resource_interface") {
- visibility = [ "//components/reporting/*" ]
+ visibility = [
+ "//chrome/browser/*",
+ "//components/reporting/*",
+ ]
sources = [
"disk_resource_impl.cc",
"disk_resource_impl.h",
diff --git a/chromium/components/reporting/resources/disk_resource_impl.cc b/chromium/components/reporting/resources/disk_resource_impl.cc
index 07f162a4a28..03ab3336852 100644
--- a/chromium/components/reporting/resources/disk_resource_impl.cc
+++ b/chromium/components/reporting/resources/disk_resource_impl.cc
@@ -12,11 +12,7 @@
namespace reporting {
-// TODO(b/159361496): Set total disk allowance based on the platform
-// (or policy?).
-DiskResourceImpl::DiskResourceImpl()
- : total_(256u * 1024LLu * 1024LLu), // 256 MiB
- used_(0) {}
+DiskResourceImpl::DiskResourceImpl(uint64_t total_size) : total_(total_size) {}
DiskResourceImpl::~DiskResourceImpl() = default;
@@ -34,11 +30,11 @@ void DiskResourceImpl::Discard(uint64_t size) {
used_.fetch_sub(size);
}
-uint64_t DiskResourceImpl::GetTotal() {
+uint64_t DiskResourceImpl::GetTotal() const {
return total_;
}
-uint64_t DiskResourceImpl::GetUsed() {
+uint64_t DiskResourceImpl::GetUsed() const {
return used_.load();
}
@@ -46,9 +42,4 @@ void DiskResourceImpl::Test_SetTotal(uint64_t test_total) {
total_ = test_total;
}
-ResourceInterface* GetDiskResource() {
- static base::NoDestructor<DiskResourceImpl> disk;
- return disk.get();
-}
-
} // namespace reporting
diff --git a/chromium/components/reporting/resources/disk_resource_impl.h b/chromium/components/reporting/resources/disk_resource_impl.h
index 92f118fd636..dc354cf08b6 100644
--- a/chromium/components/reporting/resources/disk_resource_impl.h
+++ b/chromium/components/reporting/resources/disk_resource_impl.h
@@ -17,19 +17,20 @@ namespace reporting {
// All APIs are non-blocking.
class DiskResourceImpl : public ResourceInterface {
public:
- DiskResourceImpl();
- ~DiskResourceImpl() override;
+ explicit DiskResourceImpl(uint64_t total_size);
// Implementation of ResourceInterface methods.
bool Reserve(uint64_t size) override;
void Discard(uint64_t size) override;
- uint64_t GetTotal() override;
- uint64_t GetUsed() override;
+ uint64_t GetTotal() const override;
+ uint64_t GetUsed() const override;
void Test_SetTotal(uint64_t test_total) override;
private:
+ ~DiskResourceImpl() override;
+
uint64_t total_;
- std::atomic<uint64_t> used_;
+ std::atomic<uint64_t> used_{0};
};
} // namespace reporting
diff --git a/chromium/components/reporting/resources/memory_resource_impl.cc b/chromium/components/reporting/resources/memory_resource_impl.cc
index 79d3bce0f18..732a18ddbd9 100644
--- a/chromium/components/reporting/resources/memory_resource_impl.cc
+++ b/chromium/components/reporting/resources/memory_resource_impl.cc
@@ -12,11 +12,8 @@
namespace reporting {
-// TODO(b/159361496): Set total memory allowance based on the platform
-// (or policy?).
-MemoryResourceImpl::MemoryResourceImpl()
- : total_(16u * 1024LLu * 1024LLu), // 16 MiB
- used_(0) {}
+MemoryResourceImpl::MemoryResourceImpl(uint64_t total_size)
+ : total_(total_size) {}
MemoryResourceImpl::~MemoryResourceImpl() = default;
@@ -34,11 +31,11 @@ void MemoryResourceImpl::Discard(uint64_t size) {
used_.fetch_sub(size);
}
-uint64_t MemoryResourceImpl::GetTotal() {
+uint64_t MemoryResourceImpl::GetTotal() const {
return total_;
}
-uint64_t MemoryResourceImpl::GetUsed() {
+uint64_t MemoryResourceImpl::GetUsed() const {
return used_.load();
}
@@ -46,9 +43,4 @@ void MemoryResourceImpl::Test_SetTotal(uint64_t test_total) {
total_ = test_total;
}
-ResourceInterface* GetMemoryResource() {
- static base::NoDestructor<MemoryResourceImpl> memory;
- return memory.get();
-}
-
} // namespace reporting
diff --git a/chromium/components/reporting/resources/memory_resource_impl.h b/chromium/components/reporting/resources/memory_resource_impl.h
index 3fbb655afe4..a6106bb9137 100644
--- a/chromium/components/reporting/resources/memory_resource_impl.h
+++ b/chromium/components/reporting/resources/memory_resource_impl.h
@@ -17,19 +17,20 @@ namespace reporting {
// All APIs are non-blocking.
class MemoryResourceImpl : public ResourceInterface {
public:
- MemoryResourceImpl();
- ~MemoryResourceImpl() override;
+ explicit MemoryResourceImpl(uint64_t total_size);
// Implementation of ResourceInterface methods.
bool Reserve(uint64_t size) override;
void Discard(uint64_t size) override;
- uint64_t GetTotal() override;
- uint64_t GetUsed() override;
+ uint64_t GetTotal() const override;
+ uint64_t GetUsed() const override;
void Test_SetTotal(uint64_t test_total) override;
private:
+ ~MemoryResourceImpl() override;
+
uint64_t total_;
- std::atomic<uint64_t> used_;
+ std::atomic<uint64_t> used_{0};
};
} // namespace reporting
diff --git a/chromium/components/reporting/resources/resource_interface.cc b/chromium/components/reporting/resources/resource_interface.cc
index 2cf8a9e2dbf..727eb001f75 100644
--- a/chromium/components/reporting/resources/resource_interface.cc
+++ b/chromium/components/reporting/resources/resource_interface.cc
@@ -4,12 +4,19 @@
#include "components/reporting/resources/resource_interface.h"
+#include <utility>
+
#include <cstdint>
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
namespace reporting {
-ScopedReservation::ScopedReservation(uint64_t size,
- ResourceInterface* resource_interface)
+ScopedReservation::ScopedReservation(
+ uint64_t size,
+ scoped_refptr<ResourceInterface> resource_interface)
: resource_interface_(resource_interface) {
if (!resource_interface->Reserve(size)) {
return;
diff --git a/chromium/components/reporting/resources/resource_interface.h b/chromium/components/reporting/resources/resource_interface.h
index 1669df02d09..235f42f9470 100644
--- a/chromium/components/reporting/resources/resource_interface.h
+++ b/chromium/components/reporting/resources/resource_interface.h
@@ -7,7 +7,8 @@
#include <cstdint>
-#include "base/memory/raw_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace reporting {
@@ -15,10 +16,8 @@ namespace reporting {
// Interface to resources management by Storage module.
// Must be implemented by the caller base on the platform limitations.
// All APIs are non-blocking.
-class ResourceInterface {
+class ResourceInterface : public base::RefCountedThreadSafe<ResourceInterface> {
public:
- virtual ~ResourceInterface() = default;
-
// Needs to be called before attempting to allocate specified size.
// Returns true if requested amount can be allocated.
// After that the caller can actually allocate it or must call
@@ -30,23 +29,26 @@ class ResourceInterface {
virtual void Discard(uint64_t size) = 0;
// Returns total amount.
- virtual uint64_t GetTotal() = 0;
+ virtual uint64_t GetTotal() const = 0;
// Returns current used amount.
- virtual uint64_t GetUsed() = 0;
+ virtual uint64_t GetUsed() const = 0;
// Test only: Sets non-default usage limit.
virtual void Test_SetTotal(uint64_t test_total) = 0;
protected:
+ friend class base::RefCountedThreadSafe<ResourceInterface>;
+
ResourceInterface() = default;
+ virtual ~ResourceInterface() = default;
};
// Moveable RAII class used for scoped Reserve-Discard.
//
// Usage:
// {
-// ScopedReservation reservation(1024u, GetMemoryResource());
+// ScopedReservation reservation(1024u, options.memory_resource());
// if (!reservation.reserved()) {
// // Allocation failed.
// return;
@@ -58,7 +60,8 @@ class ResourceInterface {
// Can be handed over to another owner.
class ScopedReservation {
public:
- ScopedReservation(uint64_t size, ResourceInterface* resource_interface);
+ ScopedReservation(uint64_t size,
+ scoped_refptr<ResourceInterface> resource_interface);
ScopedReservation(ScopedReservation&& other);
ScopedReservation(const ScopedReservation& other) = delete;
ScopedReservation& operator=(const ScopedReservation& other) = delete;
@@ -68,13 +71,10 @@ class ScopedReservation {
bool Reduce(uint64_t new_size);
private:
- const raw_ptr<ResourceInterface> resource_interface_;
+ const scoped_refptr<ResourceInterface> resource_interface_;
absl::optional<uint64_t> size_;
};
-ResourceInterface* GetMemoryResource();
-ResourceInterface* GetDiskResource();
-
} // namespace reporting
#endif // COMPONENTS_REPORTING_RESOURCES_RESOURCE_INTERFACE_H_
diff --git a/chromium/components/reporting/resources/resource_interface_unittest.cc b/chromium/components/reporting/resources/resource_interface_unittest.cc
index 1ea2c8ff188..63bcbaee335 100644
--- a/chromium/components/reporting/resources/resource_interface_unittest.cc
+++ b/chromium/components/reporting/resources/resource_interface_unittest.cc
@@ -2,13 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <cstdint>
-
#include "components/reporting/resources/resource_interface.h"
+#include <cstdint>
+#include <utility>
+
+#include "base/memory/scoped_refptr.h"
#include "base/task/task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/task_environment.h"
+#include "components/reporting/resources/disk_resource_impl.h"
+#include "components/reporting/resources/memory_resource_impl.h"
#include "components/reporting/util/test_support_callbacks.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -19,9 +23,11 @@ namespace reporting {
namespace {
class ResourceInterfaceTest
- : public ::testing::TestWithParam<ResourceInterface*> {
+ : public ::testing::TestWithParam<scoped_refptr<ResourceInterface>> {
protected:
- ResourceInterface* resource_interface() const { return GetParam(); }
+ scoped_refptr<ResourceInterface> resource_interface() const {
+ return GetParam();
+ }
void TearDown() override {
EXPECT_THAT(resource_interface()->GetUsed(), Eq(0u));
@@ -54,7 +60,7 @@ TEST_P(ResourceInterfaceTest, SimultaneousReservationTest) {
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::BEST_EFFORT},
base::BindOnce(
- [](size_t size, ResourceInterface* resource_interface,
+ [](size_t size, scoped_refptr<ResourceInterface> resource_interface,
test::TestCallbackWaiter* waiter) {
EXPECT_TRUE(resource_interface->Reserve(size));
waiter->Signal();
@@ -70,7 +76,7 @@ TEST_P(ResourceInterfaceTest, SimultaneousReservationTest) {
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::BEST_EFFORT},
base::BindOnce(
- [](size_t size, ResourceInterface* resource_interface,
+ [](size_t size, scoped_refptr<ResourceInterface> resource_interface,
test::TestCallbackWaiter* waiter) {
resource_interface->Discard(size);
waiter->Signal();
@@ -89,7 +95,7 @@ TEST_P(ResourceInterfaceTest, SimultaneousScopedReservationTest) {
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::BEST_EFFORT},
base::BindOnce(
- [](size_t size, ResourceInterface* resource_interface,
+ [](size_t size, scoped_refptr<ResourceInterface> resource_interface,
test::TestCallbackWaiter* waiter) {
{ ScopedReservation(size, resource_interface); }
waiter->Signal();
@@ -150,10 +156,11 @@ TEST_P(ResourceInterfaceTest, ReservationOverMaxTest) {
resource_interface()->Discard(resource_interface()->GetTotal());
}
-INSTANTIATE_TEST_SUITE_P(VariousResources,
- ResourceInterfaceTest,
- testing::Values(GetMemoryResource(),
- GetDiskResource()));
-
+INSTANTIATE_TEST_SUITE_P(
+ VariousResources,
+ ResourceInterfaceTest,
+ testing::Values(
+ base::MakeRefCounted<DiskResourceImpl>(16u * 1024LLu * 1024LLu),
+ base::MakeRefCounted<MemoryResourceImpl>(4u * 1024LLu * 1024LLu)));
} // namespace
} // namespace reporting
diff --git a/chromium/components/reporting/storage/BUILD.gn b/chromium/components/reporting/storage/BUILD.gn
index 18c0ed1746b..871c18e47df 100644
--- a/chromium/components/reporting/storage/BUILD.gn
+++ b/chromium/components/reporting/storage/BUILD.gn
@@ -10,7 +10,10 @@ static_library("storage_configuration") {
"storage_configuration.h",
]
- deps = [ "//base" ]
+ deps = [
+ "//base",
+ "//components/reporting/resources:resource_interface",
+ ]
}
static_library("storage_uploader_interface") {
@@ -23,6 +26,7 @@ static_library("storage_uploader_interface") {
"//base",
"//components/reporting/proto:record_constants",
"//components/reporting/proto:record_proto",
+ "//components/reporting/resources:resource_interface",
"//components/reporting/util:status",
"//components/reporting/util:status_proto",
]
@@ -72,6 +76,7 @@ static_library("storage") {
"//components/reporting/encryption:verification",
"//components/reporting/proto:record_constants",
"//components/reporting/proto:record_proto",
+ "//components/reporting/resources:resource_interface",
"//components/reporting/util:file",
"//components/reporting/util:status",
"//components/reporting/util:status_macros",
diff --git a/chromium/components/reporting/storage/missive_storage_module.cc b/chromium/components/reporting/storage/missive_storage_module.cc
index d2e6768cdf3..f204532fe3f 100644
--- a/chromium/components/reporting/storage/missive_storage_module.cc
+++ b/chromium/components/reporting/storage/missive_storage_module.cc
@@ -36,15 +36,13 @@ scoped_refptr<MissiveStorageModule> MissiveStorageModule::Create(
return base::WrapRefCounted(new MissiveStorageModule(std::move(delegate)));
}
-void MissiveStorageModule::AddRecord(
- Priority priority,
- Record record,
- base::OnceCallback<void(Status)> callback) {
+void MissiveStorageModule::AddRecord(Priority priority,
+ Record record,
+ EnqueueCallback callback) {
delegate_->AddRecord(priority, std::move(record), std::move(callback));
}
-void MissiveStorageModule::Flush(Priority priority,
- base::OnceCallback<void(Status)> callback) {
+void MissiveStorageModule::Flush(Priority priority, FlushCallback callback) {
delegate_->Flush(priority, std::move(callback));
}
diff --git a/chromium/components/reporting/storage/missive_storage_module.h b/chromium/components/reporting/storage/missive_storage_module.h
index 67a1a32675c..52d3d1ee0e1 100644
--- a/chromium/components/reporting/storage/missive_storage_module.h
+++ b/chromium/components/reporting/storage/missive_storage_module.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_REPORTING_STORAGE_MISSIVE_STORAGE_MODULE_H_
#define COMPONENTS_REPORTING_STORAGE_MISSIVE_STORAGE_MODULE_H_
+#include <memory>
#include <utility>
#include "base/callback.h"
@@ -36,9 +37,8 @@ class MissiveStorageModule : public StorageModuleInterface {
virtual void AddRecord(const Priority priority,
Record record,
- base::OnceCallback<void(Status)> callback) = 0;
- virtual void Flush(Priority priority,
- base::OnceCallback<void(Status)> callback) = 0;
+ EnqueueCallback callback) = 0;
+ virtual void Flush(Priority priority, FlushCallback callback) = 0;
virtual void ReportSuccess(const SequenceInformation& sequence_information,
bool force) = 0;
virtual void UpdateEncryptionKey(
@@ -55,14 +55,13 @@ class MissiveStorageModule : public StorageModuleInterface {
// Calls |missive_delegate_->AddRecord| forwarding the arguments.
void AddRecord(Priority priority,
Record record,
- base::OnceCallback<void(Status)> callback) override;
+ EnqueueCallback callback) override;
// Calls |missive_delegate_->Flush| to initiate upload of collected records
// according to the priority. Called usually for a queue with an infinite or
// very large upload period. Multiple |Flush| calls can safely run in
// parallel. Returns error if cannot start upload.
- void Flush(Priority priority,
- base::OnceCallback<void(Status)> callback) override;
+ void Flush(Priority priority, FlushCallback callback) override;
// Once a record has been successfully uploaded, the sequence information
// can be passed back to the StorageModule here for record deletion.
diff --git a/chromium/components/reporting/storage/missive_storage_module_delegate_impl.cc b/chromium/components/reporting/storage/missive_storage_module_delegate_impl.cc
index f3a1640bd9d..ebf8a0a1816 100644
--- a/chromium/components/reporting/storage/missive_storage_module_delegate_impl.cc
+++ b/chromium/components/reporting/storage/missive_storage_module_delegate_impl.cc
@@ -3,6 +3,9 @@
// found in the LICENSE file.
#include "components/reporting/storage/missive_storage_module_delegate_impl.h"
+
+#include <utility>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
@@ -23,13 +26,13 @@ MissiveStorageModuleDelegateImpl::~MissiveStorageModuleDelegateImpl() = default;
void MissiveStorageModuleDelegateImpl::AddRecord(
Priority priority,
Record record,
- base::OnceCallback<void(Status)> callback) {
+ MissiveStorageModule::EnqueueCallback callback) {
add_record_.Run(priority, std::move(record), std::move(callback));
}
void MissiveStorageModuleDelegateImpl::Flush(
Priority priority,
- base::OnceCallback<void(Status)> callback) {
+ MissiveStorageModule::FlushCallback callback) {
flush_.Run(priority, std::move(callback));
}
diff --git a/chromium/components/reporting/storage/missive_storage_module_delegate_impl.h b/chromium/components/reporting/storage/missive_storage_module_delegate_impl.h
index 2031608690e..fa20b66f80a 100644
--- a/chromium/components/reporting/storage/missive_storage_module_delegate_impl.h
+++ b/chromium/components/reporting/storage/missive_storage_module_delegate_impl.h
@@ -18,9 +18,10 @@ class MissiveStorageModuleDelegateImpl
: public MissiveStorageModule::MissiveStorageModuleDelegateInterface {
public:
using AddRecordCallback = base::RepeatingCallback<
- void(Priority, Record, base::OnceCallback<void(Status)>)>;
+ void(Priority, Record, MissiveStorageModule::EnqueueCallback)>;
using FlushCallback =
- base::RepeatingCallback<void(Priority, base::OnceCallback<void(Status)>)>;
+ base::RepeatingCallback<void(Priority,
+ MissiveStorageModule::FlushCallback)>;
MissiveStorageModuleDelegateImpl(AddRecordCallback add_record,
FlushCallback flush);
@@ -28,10 +29,10 @@ class MissiveStorageModuleDelegateImpl
void AddRecord(Priority priority,
Record record,
- base::OnceCallback<void(Status)> callback) override;
+ MissiveStorageModule::EnqueueCallback callback) override;
void Flush(Priority priority,
- base::OnceCallback<void(Status)> callback) override;
+ MissiveStorageModule::FlushCallback callback) override;
void ReportSuccess(const SequenceInformation& sequence_information,
bool force) override;
diff --git a/chromium/components/reporting/storage/storage.cc b/chromium/components/reporting/storage/storage.cc
index 2f27e917f20..6856bfd77ef 100644
--- a/chromium/components/reporting/storage/storage.cc
+++ b/chromium/components/reporting/storage/storage.cc
@@ -18,6 +18,7 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/platform_file.h"
+#include "base/logging.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/task_runner.h"
@@ -30,6 +31,9 @@
#include "components/reporting/encryption/primitives.h"
#include "components/reporting/encryption/verification.h"
#include "components/reporting/proto/synced/record.pb.h"
+#include "components/reporting/resources/disk_resource_impl.h"
+#include "components/reporting/resources/memory_resource_impl.h"
+#include "components/reporting/resources/resource_interface.h"
#include "components/reporting/storage/storage_configuration.h"
#include "components/reporting/storage/storage_queue.h"
#include "components/reporting/storage/storage_uploader_interface.h"
@@ -83,8 +87,8 @@ constexpr base::TimeDelta kManualUploadPeriod = base::TimeDelta::Max();
constexpr base::FilePath::CharType kEncryptionKeyFilePrefix[] =
FILE_PATH_LITERAL("EncryptionKey.");
-const int32_t kEncryptionKeyMaxFileSize = 256;
-const uint64_t kQueueSize = 2 * 1024LL * 1024LL;
+constexpr int32_t kEncryptionKeyMaxFileSize = 256;
+constexpr uint64_t kQueueSize = 2UL * 1024UL * 1024UL;
// Failed upload retry delay: if an upload fails and there are no more incoming
// events, collected events will not get uploaded for an indefinite time (see
@@ -108,18 +112,16 @@ std::vector<std::pair<Priority, QueueOptions>> ExpectedQueues(
.set_file_prefix(kImmediateQueuePrefix)
.set_upload_retry_delay(kFailedUploadRetryDelay)
.set_max_single_file_size(kQueueSize)),
- std::make_pair(FAST_BATCH,
- QueueOptions(options)
- .set_subdirectory(kFastBatchQueueSubdir)
- .set_file_prefix(kFastBatchQueuePrefix)
- .set_upload_period(kFastBatchUploadPeriod)
- .set_max_single_file_size(kQueueSize)),
- std::make_pair(SLOW_BATCH,
- QueueOptions(options)
- .set_subdirectory(kSlowBatchQueueSubdir)
- .set_file_prefix(kSlowBatchQueuePrefix)
- .set_upload_period(kSlowBatchUploadPeriod)
- .set_max_single_file_size(kQueueSize)),
+ std::make_pair(FAST_BATCH, QueueOptions(options)
+ .set_subdirectory(kFastBatchQueueSubdir)
+ .set_file_prefix(kFastBatchQueuePrefix)
+ .set_upload_period(kFastBatchUploadPeriod)
+ .set_max_single_file_size(kQueueSize)),
+ std::make_pair(SLOW_BATCH, QueueOptions(options)
+ .set_subdirectory(kSlowBatchQueueSubdir)
+ .set_file_prefix(kSlowBatchQueuePrefix)
+ .set_upload_period(kSlowBatchUploadPeriod)
+ .set_max_single_file_size(kQueueSize)),
std::make_pair(BACKGROUND_BATCH,
QueueOptions(options)
.set_subdirectory(kBackgroundQueueSubdir)
@@ -135,7 +137,6 @@ std::vector<std::pair<Priority, QueueOptions>> ExpectedQueues(
.set_max_single_file_size(kQueueSize)),
};
}
-
} // namespace
// Uploader interface adaptor for individual queue.
@@ -161,12 +162,14 @@ class Storage::QueueUploaderInterface : public UploaderInterface {
}
void ProcessRecord(EncryptedRecord encrypted_record,
+ ScopedReservation scoped_reservation,
base::OnceCallback<void(bool)> processed_cb) override {
// Update sequence information: add Priority.
SequenceInformation* const sequence_info =
encrypted_record.mutable_sequence_information();
sequence_info->set_priority(priority_);
storage_interface_->ProcessRecord(std::move(encrypted_record),
+ std::move(scoped_reservation),
std::move(processed_cb));
}
@@ -452,8 +455,8 @@ class Storage::KeyInStorage {
directory_,
/*recursive=*/false, base::FileEnumerator::FILES,
base::StrCat({kEncryptionKeyFilePrefix, FILE_PATH_LITERAL("*")}));
- base::FilePath full_name;
- while (full_name = dir_enum.Next(), !full_name.empty()) {
+ for (auto full_name = dir_enum.Next(); !full_name.empty();
+ full_name = dir_enum.Next()) {
if (!all_key_files->emplace(full_name).second) {
// Duplicate file name. Should not happen.
continue;
diff --git a/chromium/components/reporting/storage/storage.h b/chromium/components/reporting/storage/storage.h
index 2d3046da41a..59d7bdf8f79 100644
--- a/chromium/components/reporting/storage/storage.h
+++ b/chromium/components/reporting/storage/storage.h
@@ -7,10 +7,12 @@
#include <map>
#include <memory>
+#include <string>
#include <utility>
#include "base/callback.h"
#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_piece.h"
@@ -68,6 +70,8 @@ class Storage : public base::RefCountedThreadSafe<Storage> {
// be paased here.
void UpdateEncryptionKey(SignedEncryptionInfo signed_encryption_key);
+ const StorageOptions& options() const { return options_; }
+
Storage(const Storage& other) = delete;
Storage& operator=(const Storage& other) = delete;
diff --git a/chromium/components/reporting/storage/storage_configuration.cc b/chromium/components/reporting/storage/storage_configuration.cc
index 89126edd53c..7919eb7fa4a 100644
--- a/chromium/components/reporting/storage/storage_configuration.cc
+++ b/chromium/components/reporting/storage/storage_configuration.cc
@@ -6,10 +6,13 @@
namespace reporting {
-StorageOptions::StorageOptions() = default;
+StorageOptions::StorageOptions()
+ : memory_resource_(base::MakeRefCounted<MemoryResourceImpl>(
+ 4u * 1024uLL * 1024uLL)), // 4 MiB by default
+ disk_space_resource_(base::MakeRefCounted<DiskResourceImpl>(
+ 64u * 1024uLL * 1024uLL)) // 64 MiB by default.
+{}
StorageOptions::StorageOptions(const StorageOptions& options) = default;
-StorageOptions& StorageOptions::operator=(const StorageOptions& options) =
- default;
StorageOptions::~StorageOptions() = default;
QueueOptions::QueueOptions(const StorageOptions& storage_options)
diff --git a/chromium/components/reporting/storage/storage_configuration.h b/chromium/components/reporting/storage/storage_configuration.h
index 789db0a7688..c52d1db7e4a 100644
--- a/chromium/components/reporting/storage/storage_configuration.h
+++ b/chromium/components/reporting/storage/storage_configuration.h
@@ -8,8 +8,12 @@
#include <string>
#include "base/files/file_path.h"
+#include "base/memory/scoped_refptr.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
+#include "components/reporting/resources/disk_resource_impl.h"
+#include "components/reporting/resources/memory_resource_impl.h"
+#include "components/reporting/resources/resource_interface.h"
namespace reporting {
@@ -24,7 +28,7 @@ class StorageOptions {
public:
StorageOptions();
StorageOptions(const StorageOptions& options);
- StorageOptions& operator=(const StorageOptions& options);
+ StorageOptions& operator=(const StorageOptions& options) = delete;
~StorageOptions();
StorageOptions& set_directory(const base::FilePath& directory) {
directory_ = directory;
@@ -41,11 +45,13 @@ class StorageOptions {
return *this;
}
StorageOptions& set_max_total_files_size(uint64_t max_total_files_size) {
- max_total_files_size_ = max_total_files_size;
+ disk_space_resource_ =
+ base::MakeRefCounted<DiskResourceImpl>(max_total_files_size);
return *this;
}
StorageOptions& set_max_total_memory_size(uint64_t max_total_memory_size) {
- max_total_memory_size_ = max_total_memory_size;
+ memory_resource_ =
+ base::MakeRefCounted<MemoryResourceImpl>(max_total_memory_size);
return *this;
}
const base::FilePath& directory() const { return directory_; }
@@ -53,8 +59,19 @@ class StorageOptions {
return signature_verification_public_key_;
}
size_t max_record_size() const { return max_record_size_; }
- uint64_t max_total_files_size() const { return max_total_files_size_; }
- uint64_t max_total_memory_size() const { return max_total_memory_size_; }
+ uint64_t max_total_files_size() const {
+ return disk_space_resource_->GetTotal();
+ }
+ uint64_t max_total_memory_size() const {
+ return memory_resource_->GetTotal();
+ }
+
+ scoped_refptr<ResourceInterface> disk_space_resource() const {
+ return disk_space_resource_.get();
+ }
+ scoped_refptr<ResourceInterface> memory_resource() const {
+ return memory_resource_;
+ }
private:
// Subdirectory of the location assigned for this Storage.
@@ -67,11 +84,9 @@ class StorageOptions {
// Maximum record size.
size_t max_record_size_ = 1 * 1024LL * 1024LL; // 1 MiB
- // Maximum total size of all files in all queues.
- uint64_t max_total_files_size_ = 64 * 1024LL * 1024LL; // 64 MiB
-
- // Maximum memory usage (reading buffers).
- uint64_t max_total_memory_size_ = 4 * 1024LL * 1024LL; // 4 MiB
+ // Resources managements.
+ scoped_refptr<ResourceInterface> memory_resource_;
+ scoped_refptr<ResourceInterface> disk_space_resource_;
};
// Single queue options class allowing to set parameters individually, e.g.:
@@ -84,7 +99,7 @@ class QueueOptions {
public:
explicit QueueOptions(const StorageOptions& storage_options);
QueueOptions(const QueueOptions& options);
- // QueueOptions& operator=(const QueueOptions& options) = default;
+ QueueOptions& operator=(const QueueOptions& options) = delete;
QueueOptions& set_subdirectory(
const base::FilePath::StringType& subdirectory) {
directory_ = storage_options_.directory().Append(subdirectory);
@@ -118,6 +133,12 @@ class QueueOptions {
uint64_t max_single_file_size() const { return max_single_file_size_; }
base::TimeDelta upload_period() const { return upload_period_; }
base::TimeDelta upload_retry_delay() const { return upload_retry_delay_; }
+ scoped_refptr<ResourceInterface> disk_space_resource() const {
+ return storage_options_.disk_space_resource();
+ }
+ scoped_refptr<ResourceInterface> memory_resource() const {
+ return storage_options_.memory_resource();
+ }
private:
// Whole storage options, which this queue options are based on.
diff --git a/chromium/components/reporting/storage/storage_module.cc b/chromium/components/reporting/storage/storage_module.cc
index 885a8f052c8..84707b1bfc0 100644
--- a/chromium/components/reporting/storage/storage_module.cc
+++ b/chromium/components/reporting/storage/storage_module.cc
@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/containers/span.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "components/reporting/compression/compression_module.h"
#include "components/reporting/encryption/encryption_module_interface.h"
@@ -30,7 +31,7 @@ StorageModule::~StorageModule() = default;
void StorageModule::AddRecord(Priority priority,
Record record,
- base::OnceCallback<void(Status)> callback) {
+ EnqueueCallback callback) {
storage_->Write(priority, std::move(record), std::move(callback));
}
@@ -45,8 +46,7 @@ void StorageModule::ReportSuccess(SequenceInformation sequence_information,
}));
}
-void StorageModule::Flush(Priority priority,
- base::OnceCallback<void(Status)> callback) {
+void StorageModule::Flush(Priority priority, FlushCallback callback) {
std::move(callback).Run(storage_->Flush(priority));
}
diff --git a/chromium/components/reporting/storage/storage_module.h b/chromium/components/reporting/storage/storage_module.h
index 9e9f6759615..f8f6542b43f 100644
--- a/chromium/components/reporting/storage/storage_module.h
+++ b/chromium/components/reporting/storage/storage_module.h
@@ -40,14 +40,13 @@ class StorageModule : public StorageModuleInterface {
// called.
void AddRecord(Priority priority,
Record record,
- base::OnceCallback<void(Status)> callback) override;
+ EnqueueCallback callback) override;
// Initiates upload of collected records according to the priority.
// Called usually for a queue with an infinite or very large upload period.
// Multiple |Flush| calls can safely run in parallel.
// Returns error if cannot start upload.
- void Flush(Priority priority,
- base::OnceCallback<void(Status)> callback) override;
+ void Flush(Priority priority, FlushCallback callback) override;
// Once a record has been successfully uploaded, the sequence information
// can be passed back to the StorageModule here for record deletion.
diff --git a/chromium/components/reporting/storage/storage_module_interface.h b/chromium/components/reporting/storage/storage_module_interface.h
index f597b0b5b4e..3728e525300 100644
--- a/chromium/components/reporting/storage/storage_module_interface.h
+++ b/chromium/components/reporting/storage/storage_module_interface.h
@@ -17,6 +17,9 @@ namespace reporting {
class StorageModuleInterface
: public base::RefCountedThreadSafe<StorageModuleInterface> {
public:
+ using EnqueueCallback = base::OnceCallback<void(Status)>;
+ using FlushCallback = base::OnceCallback<void(Status)>;
+
StorageModuleInterface(const StorageModuleInterface& other) = delete;
StorageModuleInterface& operator=(const StorageModuleInterface& other) =
delete;
@@ -26,14 +29,13 @@ class StorageModuleInterface
// completion, |callback| is called.
virtual void AddRecord(Priority priority,
Record record,
- base::OnceCallback<void(Status)> callback) = 0;
+ EnqueueCallback callback) = 0;
// Initiates upload of collected records according to the priority.
// Called usually for a queue with an infinite or very large upload period.
// Multiple |Flush| calls can safely run in parallel.
// Returns error if cannot start upload.
- virtual void Flush(Priority priority,
- base::OnceCallback<void(Status)> callback) = 0;
+ virtual void Flush(Priority priority, FlushCallback callback) = 0;
// Once a record has been successfully uploaded, the sequence information
// can be passed back to the StorageModuleInterface here for record deletion.
diff --git a/chromium/components/reporting/storage/storage_queue.cc b/chromium/components/reporting/storage/storage_queue.cc
index 495bf9aca78..d2e9af59adf 100644
--- a/chromium/components/reporting/storage/storage_queue.cc
+++ b/chromium/components/reporting/storage/storage_queue.cc
@@ -24,7 +24,9 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/hash/hash.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/rand_util.h"
#include "base/sequence_checker.h"
@@ -222,8 +224,12 @@ Status StorageQueue::Init() {
}
// Delete all files except used ones.
DeleteUnusedFiles(used_files_set);
- // Initiate periodic uploading, if needed.
- if (!options_.upload_period().is_zero()) {
+ // Initiate periodic uploading, if needed (IMMEDIATE, SECURITY and MANUAL
+ // priorities do not need it - they are created with 0, 0 and infinite period
+ // respectively).
+ //
+ if (!options_.upload_period().is_zero() &&
+ !options_.upload_period().is_max()) {
upload_timer_.Start(FROM_HERE, options_.upload_period(), this,
&StorageQueue::PeriodicUpload);
}
@@ -282,33 +288,22 @@ Status StorageQueue::SetGenerationId(const base::FilePath& full_name) {
StatusOr<int64_t> StorageQueue::AddDataFile(
const base::FilePath& full_name,
const base::FileEnumerator::FileInfo& file_info) {
- const auto extension = full_name.FinalExtension();
- if (extension.empty()) {
- return Status(error::INTERNAL,
- base::StrCat({"File has no extension: '",
- full_name.MaybeAsASCII(), "'"}));
- }
- int64_t file_sequencing_id = 0;
- const bool success =
- base::StringToInt64(extension.substr(1), &file_sequencing_id);
- if (!success) {
- return Status(error::INTERNAL,
- base::StrCat({"File extension does not parse: '",
- full_name.MaybeAsASCII(), "'"}));
- }
-
+ ASSIGN_OR_RETURN(int64_t file_sequence_id,
+ SingleFile::GetFileSequenceIdFromPath(full_name));
RETURN_IF_ERROR(SetGenerationId(full_name));
- auto file_or_status = SingleFile::Create(full_name, file_info.GetSize());
+ auto file_or_status = SingleFile::Create(full_name, file_info.GetSize(),
+ options_.memory_resource(),
+ options_.disk_space_resource());
if (!file_or_status.ok()) {
return file_or_status.status();
}
- if (!files_.emplace(file_sequencing_id, file_or_status.ValueOrDie()).second) {
+ if (!files_.emplace(file_sequence_id, file_or_status.ValueOrDie()).second) {
return Status(error::ALREADY_EXISTS,
base::StrCat({"Sequencing id duplicated: '",
full_name.MaybeAsASCII(), "'"}));
}
- return file_sequencing_id;
+ return file_sequence_id;
}
Status StorageQueue::EnumerateDataFiles(
@@ -322,8 +317,8 @@ Status StorageQueue::EnumerateDataFiles(
options_.directory(),
/*recursive=*/false, base::FileEnumerator::FILES,
base::StrCat({options_.file_prefix(), FILE_PATH_LITERAL(".*")}));
- base::FilePath full_name;
- while (full_name = dir_enum.Next(), !full_name.empty()) {
+ for (auto full_name = dir_enum.Next(); !full_name.empty();
+ full_name = dir_enum.Next()) {
const auto file_sequencing_id_result =
AddDataFile(full_name, dir_enum.GetInfo());
if (!file_sequencing_id_result.ok()) {
@@ -442,7 +437,8 @@ StatusOr<scoped_refptr<StorageQueue::SingleFile>> StorageQueue::AssignLastFile(
.Append(options_.file_prefix())
.AddExtensionASCII(base::NumberToString(generation_id_))
.AddExtensionASCII(base::NumberToString(next_sequencing_id_)),
- /*size=*/0));
+ /*size=*/0, options_.memory_resource(),
+ options_.disk_space_resource()));
next_sequencing_id_ = 0;
auto insert_result = files_.emplace(next_sequencing_id_, file);
DCHECK(insert_result.second);
@@ -472,7 +468,8 @@ StorageQueue::OpenNewWriteableFile() {
.Append(options_.file_prefix())
.AddExtensionASCII(base::NumberToString(generation_id_))
.AddExtensionASCII(base::NumberToString(next_sequencing_id_)),
- /*size=*/0));
+ /*size=*/0, options_.memory_resource(),
+ options_.disk_space_resource()));
RETURN_IF_ERROR(new_file->Open(/*read_only=*/false));
auto insert_result = files_.emplace(next_sequencing_id_, new_file);
if (!insert_result.second) {
@@ -517,7 +514,7 @@ Status StorageQueue::WriteHeaderAndBlock(
base::StrCat({"Cannot open file=", file->name(),
" status=", open_status.ToString()}));
}
- if (!GetDiskResource()->Reserve(total_size)) {
+ if (!options_.disk_space_resource()->Reserve(total_size)) {
return Status(
error::RESOURCE_EXHAUSTED,
base::StrCat({"Not enough disk space available to write into file=",
@@ -574,11 +571,12 @@ Status StorageQueue::WriteMetadata(base::StringPiece current_record_digest) {
options_.directory()
.Append(METADATA_NAME)
.AddExtensionASCII(base::NumberToString(next_sequencing_id_)),
- /*size=*/0));
+ /*size=*/0, options_.memory_resource(),
+ options_.disk_space_resource()));
RETURN_IF_ERROR(meta_file->Open(/*read_only=*/false));
// Account for the metadata file size.
- if (!GetDiskResource()->Reserve(sizeof(generation_id_) +
- current_record_digest.size())) {
+ if (!options_.disk_space_resource()->Reserve(sizeof(generation_id_) +
+ current_record_digest.size())) {
return Status(
error::RESOURCE_EXHAUSTED,
base::StrCat({"Not enough disk space available to write into file=",
@@ -625,8 +623,10 @@ Status StorageQueue::ReadMetadata(
size_t size,
int64_t sequencing_id,
base::flat_set<base::FilePath>* used_files_set) {
- ASSIGN_OR_RETURN(scoped_refptr<SingleFile> meta_file,
- SingleFile::Create(meta_file_path, size));
+ ASSIGN_OR_RETURN(
+ scoped_refptr<SingleFile> meta_file,
+ SingleFile::Create(meta_file_path, size, options_.memory_resource(),
+ options_.disk_space_resource()));
RETURN_IF_ERROR(meta_file->Open(/*read_only=*/true));
// Metadata file format is:
// - generation id (8 bytes)
@@ -693,21 +693,16 @@ Status StorageQueue::RestoreMetadata(
options_.directory(),
/*recursive=*/false, base::FileEnumerator::FILES,
base::StrCat({METADATA_NAME, FILE_PATH_LITERAL(".*")}));
- base::FilePath full_name;
- while (full_name = dir_enum.Next(), !full_name.empty()) {
- const auto extension = dir_enum.GetInfo().GetName().FinalExtension();
- if (extension.empty()) {
- continue;
- }
- int64_t sequencing_id = 0;
- bool success = base::StringToInt64(
- dir_enum.GetInfo().GetName().FinalExtension().substr(1),
- &sequencing_id);
- if (!success) {
+ for (auto full_name = dir_enum.Next(); !full_name.empty();
+ full_name = dir_enum.Next()) {
+ const auto file_sequence_id =
+ SingleFile::GetFileSequenceIdFromPath(dir_enum.GetInfo().GetName());
+ if (!file_sequence_id.ok()) {
continue;
}
+
// Record file name and size. Ignore the result.
- meta_files.emplace(sequencing_id,
+ meta_files.emplace(file_sequence_id.ValueOrDie(),
std::make_pair(full_name, dir_enum.GetInfo().GetSize()));
}
// See whether we have a match for next_sequencing_id_ - 1.
@@ -773,18 +768,13 @@ void StorageQueue::DeleteOutdatedMetadata(int64_t sequencing_id_to_keep) {
DeleteFilesWarnIfFailed(
dir_enum,
base::BindRepeating(
- [](int64_t sequencing_id_to_keep, const base::FilePath& full_name) {
- const auto extension = full_name.FinalExtension();
- if (extension.empty()) {
+ [](int64_t sequence_id_to_keep, const base::FilePath& full_name) {
+ const auto sequence_id =
+ SingleFile::GetFileSequenceIdFromPath(full_name);
+ if (!sequence_id.ok()) {
return false;
}
- int64_t sequencing_id = 0;
- bool success =
- base::StringToInt64(extension.substr(1), &sequencing_id);
- if (!success) {
- return false;
- }
- if (sequencing_id >= sequencing_id_to_keep) {
+ if (sequence_id.ValueOrDie() >= sequence_id_to_keep) {
return false;
}
return true;
@@ -819,7 +809,6 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
DCHECK_LT(
static_cast<uint32_t>(reason),
static_cast<uint32_t>(UploaderInterface::UploadReason::MAX_REASON));
- DETACH_FROM_SEQUENCE(read_sequence_checker_);
}
private:
@@ -827,7 +816,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
~ReadContext() override = default;
void OnStart() override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (!storage_queue_) {
Response(Status(error::UNAVAILABLE, "StorageQueue shut down"));
return;
@@ -842,7 +832,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
}
void PrepareDataFiles() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (!storage_queue_) {
Response(Status(error::UNAVAILABLE, "StorageQueue shut down"));
return;
@@ -876,6 +867,14 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
return;
}
+ // If expected sequencing id is at or beyond the last (empty) file,
+ // we have succeeded - there are no records to upload.
+ if (sequence_info_.sequencing_id() >=
+ storage_queue_->files_.rbegin()->first) {
+ Response(Status::StatusOK());
+ return;
+ }
+
// Collect and set aside the files in the set that might have data
// for the Upload.
files_ =
@@ -900,7 +899,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
}
void BeginUploading() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (!storage_queue_) {
Response(Status(error::UNAVAILABLE, "StorageQueue shut down"));
return;
@@ -924,7 +924,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
}
void StartUploading() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
// Read from it until the specified sequencing id is found.
for (int64_t sequencing_id = current_file_->first;
sequencing_id < sequence_info_.sequencing_id(); ++sequencing_id) {
@@ -960,7 +961,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
}
void UploadingCompleted(Status status) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
// If uploader was created, notify it about completion.
if (uploader_) {
uploader_->Completed(status);
@@ -978,7 +980,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
}
void OnCompletion() override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
// Unregister with storage_queue.
if (!files_.empty()) {
if (storage_queue_) {
@@ -990,18 +993,27 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
// Prepares the |blob| for uploading.
void CallCurrentRecord(base::StringPiece blob) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
google::protobuf::io::ArrayInputStream blob_stream( // Zero-copy stream.
blob.data(), blob.size());
EncryptedRecord encrypted_record;
+ ScopedReservation scoped_reservation(
+ blob.size(), storage_queue_->options().memory_resource());
+ if (!scoped_reservation.reserved()) {
+ Response(
+ Status(error::RESOURCE_EXHAUSTED, "Insufficient memory for upload"));
+ return;
+ }
if (!encrypted_record.ParseFromZeroCopyStream(&blob_stream)) {
LOG(ERROR) << "Failed to parse record, seq="
<< sequence_info_.sequencing_id();
- CallGapUpload(/*count=*/1);
+ CallGapUpload(/*count=*/1); // Do not reserve space for Gap record.
// Resume at ScheduleNextRecord.
return;
}
- CallRecordUpload(std::move(encrypted_record));
+ CallRecordUpload(std::move(encrypted_record),
+ std::move(scoped_reservation));
}
// Completes sequence information and makes a call to UploaderInterface
@@ -1010,8 +1022,10 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
// sequential thread runner of this StorageQueue. If |encrypted_record| is
// empty (has no |encrypted_wrapped_record| and/or |encryption_info|), it
// indicates a gap notification.
- void CallRecordUpload(EncryptedRecord encrypted_record) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ void CallRecordUpload(EncryptedRecord encrypted_record,
+ ScopedReservation scoped_reservation) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (encrypted_record.has_sequence_information()) {
LOG(ERROR) << "Sequence information already present, seq="
<< sequence_info_.sequencing_id();
@@ -1023,6 +1037,7 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
// Priority is attached by the Storage layer.
*encrypted_record.mutable_sequence_information() = sequence_info_;
uploader_->ProcessRecord(std::move(encrypted_record),
+ std::move(scoped_reservation),
base::BindOnce(&ReadContext::ScheduleNextRecord,
base::Unretained(this)));
// Move sequencing id forward (ScheduleNextRecord will see this).
@@ -1030,7 +1045,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
}
void CallGapUpload(uint64_t count) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (count == 0u) {
// No records skipped.
NextRecord(/*more_records=*/true);
@@ -1052,7 +1068,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
// sends for processing, or calls Response with error status. Otherwise, call
// Response(OK).
void NextRecord(bool more_records) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (!more_records) {
Response(Status::StatusOK()); // Requested to stop reading.
return;
@@ -1079,7 +1096,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
// If anything goes wrong (file is shorter than expected, or record hash does
// not match), returns error.
StatusOr<base::StringPiece> EnsureBlob(int64_t sequencing_id) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (!storage_queue_) {
return Status(error::UNAVAILABLE, "StorageQueue shut down");
}
@@ -1166,7 +1184,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
}
void CallRecordOrGap(int64_t sequencing_id) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (!storage_queue_) {
Response(Status(error::UNAVAILABLE, "StorageQueue shut down"));
return;
@@ -1224,7 +1243,8 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
void OnUploaderInstantiated(
base::OnceCallback<void()> continuation,
StatusOr<std::unique_ptr<UploaderInterface>> uploader_result) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (!uploader_result.ok()) {
Response(Status(error::FAILED_PRECONDITION,
base::StrCat({"Failed to provide the Uploader, status=",
@@ -1251,8 +1271,6 @@ class StorageQueue::ReadContext : public TaskRunnerContext<Status> {
const bool must_invoke_upload_;
std::unique_ptr<UploaderInterface> uploader_;
base::WeakPtr<StorageQueue> storage_queue_;
-
- SEQUENCE_CHECKER(read_sequence_checker_);
};
class StorageQueue::WriteContext : public TaskRunnerContext<Status> {
@@ -1266,13 +1284,13 @@ class StorageQueue::WriteContext : public TaskRunnerContext<Status> {
record_(std::move(record)),
in_contexts_queue_(storage_queue->write_contexts_queue_.end()) {
DCHECK(storage_queue.get());
- DETACH_FROM_SEQUENCE(write_sequence_checker_);
}
private:
// Context can only be deleted by calling Response method.
~WriteContext() override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(write_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
// If still in queue, remove it (something went wrong).
if (in_contexts_queue_ != storage_queue_->write_contexts_queue_.end()) {
@@ -1302,7 +1320,8 @@ class StorageQueue::WriteContext : public TaskRunnerContext<Status> {
}
void OnStart() override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(write_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
// Make sure the record is valid.
if (!record_.has_destination()) {
@@ -1355,8 +1374,9 @@ class StorageQueue::WriteContext : public TaskRunnerContext<Status> {
void ProcessWrappedRecord(WrappedRecord wrapped_record) {
// Serialize wrapped record into a string.
- ScopedReservation scoped_reservation(wrapped_record.ByteSizeLong(),
- GetMemoryResource());
+ ScopedReservation scoped_reservation(
+ wrapped_record.ByteSizeLong(),
+ storage_queue_->options().memory_resource());
if (!scoped_reservation.reserved()) {
Schedule(&ReadContext::Response, base::Unretained(this),
Status(error::RESOURCE_EXHAUSTED,
@@ -1380,6 +1400,7 @@ class StorageQueue::WriteContext : public TaskRunnerContext<Status> {
// Compress the string.
storage_queue_->compression_module_->CompressRecord(
std::move(serialized_record),
+ storage_queue_->options().memory_resource(),
base::BindOnce(&WriteContext::OnCompressedRecordReady,
base::Unretained(this), std::move(scoped_reservation)));
}
@@ -1419,7 +1440,7 @@ class StorageQueue::WriteContext : public TaskRunnerContext<Status> {
// Serialize encrypted record.
ScopedReservation scoped_reservation(
encrypted_record_result.ValueOrDie().ByteSizeLong(),
- GetMemoryResource());
+ storage_queue_->options().memory_resource());
if (!scoped_reservation.reserved()) {
Schedule(&ReadContext::Response, base::Unretained(this),
Status(error::RESOURCE_EXHAUSTED,
@@ -1441,14 +1462,16 @@ class StorageQueue::WriteContext : public TaskRunnerContext<Status> {
}
void WriteRecord(std::string buffer) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(write_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
buffer_.swap(buffer);
ResumeWriteRecord();
}
void ResumeWriteRecord() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(write_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
// If we are not at the head of the queue, delay write and expect to be
// reactivated later.
@@ -1504,8 +1527,6 @@ class StorageQueue::WriteContext : public TaskRunnerContext<Status> {
// Write buffer. When filled in (after encryption), |WriteRecord| can be
// executed. Empty until encryption is done.
std::string buffer_;
-
- SEQUENCE_CHECKER(write_sequence_checker_);
};
void StorageQueue::Write(Record record,
@@ -1562,7 +1583,6 @@ class StorageQueue::ConfirmContext : public TaskRunnerContext<Status> {
force_(force),
storage_queue_(storage_queue) {
DCHECK(storage_queue.get());
- DETACH_FROM_SEQUENCE(confirm_sequence_checker_);
}
private:
@@ -1570,7 +1590,8 @@ class StorageQueue::ConfirmContext : public TaskRunnerContext<Status> {
~ConfirmContext() override = default;
void OnStart() override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(confirm_sequence_checker_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(
+ storage_queue_->storage_queue_sequence_checker_);
if (force_) {
storage_queue_->first_unconfirmed_sequencing_id_ =
sequencing_id_.has_value() ? (sequencing_id_.value() + 1) : 0;
@@ -1588,8 +1609,6 @@ class StorageQueue::ConfirmContext : public TaskRunnerContext<Status> {
bool force_;
scoped_refptr<StorageQueue> storage_queue_;
-
- SEQUENCE_CHECKER(confirm_sequence_checker_);
};
void StorageQueue::Confirm(absl::optional<int64_t> sequencing_id,
@@ -1680,8 +1699,12 @@ void StorageQueue::TestInjectErrorsForOperation(
// SingleFile implementation
//
StatusOr<scoped_refptr<StorageQueue::SingleFile>>
-StorageQueue::SingleFile::Create(const base::FilePath& filename, int64_t size) {
- if (!GetDiskResource()->Reserve(size)) {
+StorageQueue::SingleFile::Create(
+ const base::FilePath& filename,
+ int64_t size,
+ scoped_refptr<ResourceInterface> memory_resource,
+ scoped_refptr<ResourceInterface> disk_space_resource) {
+ if (!disk_space_resource->Reserve(size)) {
LOG(WARNING) << "Disk space exceeded adding file "
<< filename.MaybeAsASCII();
return Status(
@@ -1691,15 +1714,41 @@ StorageQueue::SingleFile::Create(const base::FilePath& filename, int64_t size) {
}
// Cannot use base::MakeRefCounted, since the constructor is private.
return scoped_refptr<StorageQueue::SingleFile>(
- new SingleFile(filename, size));
+ new SingleFile(filename, size, memory_resource, disk_space_resource));
}
-StorageQueue::SingleFile::SingleFile(const base::FilePath& filename,
- int64_t size)
- : filename_(filename), size_(size) {}
+StatusOr<int64_t> StorageQueue::SingleFile::GetFileSequenceIdFromPath(
+ const base::FilePath& file_name) {
+ const auto extension = file_name.FinalExtension();
+ if (extension.empty() || extension == FILE_PATH_LITERAL(".")) {
+ return Status(error::INTERNAL,
+ base::StrCat({"File has no extension: '",
+ file_name.MaybeAsASCII(), "'"}));
+ }
+ int64_t file_sequence_id = 0;
+ const bool success =
+ base::StringToInt64(extension.substr(1), &file_sequence_id);
+ if (!success) {
+ return Status(error::INTERNAL,
+ base::StrCat({"File extension does not parse: '",
+ file_name.MaybeAsASCII(), "'"}));
+ }
+
+ return file_sequence_id;
+}
+
+StorageQueue::SingleFile::SingleFile(
+ const base::FilePath& filename,
+ int64_t size,
+ scoped_refptr<ResourceInterface> memory_resource,
+ scoped_refptr<ResourceInterface> disk_space_resource)
+ : filename_(filename),
+ size_(size),
+ memory_resource_(memory_resource),
+ disk_space_resource_(disk_space_resource) {}
StorageQueue::SingleFile::~SingleFile() {
- GetDiskResource()->Discard(size_);
+ disk_space_resource_->Discard(size_);
Close();
}
@@ -1740,13 +1789,13 @@ void StorageQueue::SingleFile::Close() {
is_readonly_ = absl::nullopt;
if (buffer_) {
buffer_.reset();
- GetMemoryResource()->Discard(buffer_size_);
+ memory_resource_->Discard(buffer_size_);
}
}
void StorageQueue::SingleFile::DeleteWarnIfFailed() {
DCHECK(!handle_);
- GetDiskResource()->Discard(size_);
+ disk_space_resource_->Discard(size_);
size_ = 0;
DeleteFileWarnIfFailed(filename_);
}
@@ -1778,7 +1827,7 @@ StatusOr<base::StringPiece> StorageQueue::SingleFile::Read(
// tracking the average + peak memory the Storage module is consuming.
if (!buffer_) {
// Register with resource management.
- if (!GetMemoryResource()->Reserve(buffer_size_)) {
+ if (!memory_resource_->Reserve(buffer_size_)) {
return Status(error::RESOURCE_EXHAUSTED,
"Not enough memory for the read buffer");
}
@@ -1865,5 +1914,4 @@ StatusOr<uint32_t> StorageQueue::SingleFile::Append(base::StringPiece data) {
}
return actual_size;
}
-
} // namespace reporting
diff --git a/chromium/components/reporting/storage/storage_queue.h b/chromium/components/reporting/storage/storage_queue.h
index 88619b38aff..9be2134f503 100644
--- a/chromium/components/reporting/storage/storage_queue.h
+++ b/chromium/components/reporting/storage/storage_queue.h
@@ -11,6 +11,7 @@
#include <string>
#include "base/callback.h"
+#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
@@ -146,7 +147,15 @@ class StorageQueue : public base::RefCountedDeleteOnSequence<StorageQueue> {
// space) returns status.
static StatusOr<scoped_refptr<SingleFile>> Create(
const base::FilePath& filename,
- int64_t size);
+ int64_t size,
+ scoped_refptr<ResourceInterface> memory_resource,
+ scoped_refptr<ResourceInterface> disk_space_resource);
+
+ // Returns the file sequence ID (the first sequence ID in the file) if the
+ // sequence ID can be extracted from the extension. Otherwise, returns an
+ // error status.
+ static StatusOr<int64_t> GetFileSequenceIdFromPath(
+ const base::FilePath& file_name);
Status Open(bool read_only); // No-op if already opened.
void Close(); // No-op if not opened.
@@ -185,7 +194,10 @@ class StorageQueue : public base::RefCountedDeleteOnSequence<StorageQueue> {
friend class base::RefCountedThreadSafe<SingleFile>;
// Private constructor, called by factory method only.
- SingleFile(const base::FilePath& filename, int64_t size);
+ SingleFile(const base::FilePath& filename,
+ int64_t size,
+ scoped_refptr<ResourceInterface> memory_resource,
+ scoped_refptr<ResourceInterface> disk_space_resource);
// Flag (valid for opened file only): true if file was opened for reading
// only, false otherwise.
@@ -196,6 +208,9 @@ class StorageQueue : public base::RefCountedDeleteOnSequence<StorageQueue> {
std::unique_ptr<base::File> handle_; // Set only when opened/created.
+ scoped_refptr<ResourceInterface> memory_resource_;
+ scoped_refptr<ResourceInterface> disk_space_resource_;
+
// When reading the file, this is the buffer and data positions.
// If the data is read sequentially, buffered portions are reused
// improving performance. When the sequential order is broken (e.g.
diff --git a/chromium/components/reporting/storage/storage_queue_stress_test.cc b/chromium/components/reporting/storage/storage_queue_stress_test.cc
index 15b1e5389d0..feed94f8117 100644
--- a/chromium/components/reporting/storage/storage_queue_stress_test.cc
+++ b/chromium/components/reporting/storage/storage_queue_stress_test.cc
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/memory/raw_ptr.h"
#include "components/reporting/storage/storage_queue.h"
#include <cstdint>
@@ -13,6 +12,7 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/memory/raw_ptr.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
@@ -64,6 +64,7 @@ class TestUploadClient : public UploaderInterface {
: last_record_digest_map_(last_record_digest_map) {}
void ProcessRecord(EncryptedRecord encrypted_record,
+ ScopedReservation scoped_reservation,
base::OnceCallback<void(bool)> processed_cb) override {
WrappedRecord wrapped_record;
ASSERT_TRUE(wrapped_record.ParseFromString(
@@ -136,10 +137,10 @@ class StorageQueueStressTest : public ::testing::TestWithParam<size_t> {
void TearDown() override {
ResetTestStorageQueue();
// Make sure all memory is deallocated.
- ASSERT_THAT(GetMemoryResource()->GetUsed(), Eq(0u));
+ ASSERT_THAT(options_.memory_resource()->GetUsed(), Eq(0u));
// Make sure all disk is not reserved (files remain, but Storage is not
// responsible for them anymore).
- ASSERT_THAT(GetDiskResource()->GetUsed(), Eq(0u));
+ ASSERT_THAT(options_.disk_space_resource()->GetUsed(), Eq(0u));
}
void CreateTestStorageQueueOrDie(const QueueOptions& options) {
@@ -231,7 +232,7 @@ TEST_P(StorageQueueStressTest,
test::TestCallbackWaiter write_waiter;
base::RepeatingCallback<void(Status)> cb = base::BindRepeating(
[](test::TestCallbackWaiter* waiter, Status status) {
- EXPECT_OK(status);
+ EXPECT_OK(status) << status;
waiter->Signal();
},
&write_waiter);
diff --git a/chromium/components/reporting/storage/storage_queue_unittest.cc b/chromium/components/reporting/storage/storage_queue_unittest.cc
index c21f45aba4a..d255a46c66b 100644
--- a/chromium/components/reporting/storage/storage_queue_unittest.cc
+++ b/chromium/components/reporting/storage/storage_queue_unittest.cc
@@ -20,7 +20,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
-#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/threading/sequence_bound.h"
#include "build/build_config.h"
@@ -109,10 +108,10 @@ class StorageQueueTest
void TearDown() override {
ResetTestStorageQueue();
// Make sure all memory is deallocated.
- ASSERT_THAT(GetMemoryResource()->GetUsed(), Eq(0u));
+ ASSERT_THAT(options_.memory_resource()->GetUsed(), Eq(0u));
// Make sure all disk is not reserved (files remain, but Storage is not
// responsible for them anymore).
- ASSERT_THAT(GetDiskResource()->GetUsed(), Eq(0u));
+ ASSERT_THAT(options_.disk_space_resource()->GetUsed(), Eq(0u));
// Log next uploader id for possible verification.
LOG(ERROR) << "Next uploader id=" << next_uploader_id.load();
}
@@ -213,6 +212,7 @@ class StorageQueueTest
std::move(processed_cb)
.Run(mock_upload_->UploadGap(uploader_id, sequencing_id, count));
}
+
void DoUploadComplete(int64_t uploader_id, Status status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(scoped_checker_);
upload_progress_.append("Complete: ")
@@ -267,6 +267,9 @@ class StorageQueueTest
.WillOnce(DoAll(
WithoutArgs(
Invoke(waiter_.get(), &test::TestCallbackWaiter::Signal)),
+ WithArg<1>(Invoke([](Status status) {
+ LOG(ERROR) << "Completion signaled with status=" << status;
+ })),
WithoutArgs(
Invoke([]() { LOG(ERROR) << "Completion signaled"; }))));
return std::move(uploader_);
@@ -363,6 +366,7 @@ class StorageQueueTest
}
void ProcessRecord(EncryptedRecord encrypted_record,
+ ScopedReservation scoped_reservation,
base::OnceCallback<void(bool)> processed_cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(test_uploader_checker_);
auto sequence_information = encrypted_record.sequence_information();
@@ -532,7 +536,8 @@ class StorageQueueTest
test::TestEvent<Status> key_update_event;
test_encryption_module_->UpdateAsymmetricKey("DUMMY KEY", 0,
key_update_event.cb());
- ASSERT_OK(key_update_event.result());
+ const auto status = key_update_event.result();
+ ASSERT_OK(status) << status;
}
// Tries to create a new storage queue by building the test encryption module
@@ -652,7 +657,6 @@ class StorageQueueTest
const scoped_refptr<base::SequencedTaskRunner> main_task_runner_{
base::SequencedTaskRunnerHandle::Get()};
- base::test::ScopedFeatureList scoped_feature_list_;
base::ScopedTempDir location_;
StorageOptions options_;
scoped_refptr<test::TestEncryptionModule> test_encryption_module_;
@@ -1024,8 +1028,7 @@ TEST_P(StorageQueueTest, WriteIntoNewStorageQueueReopenWriteMoreAndFlush) {
storage_queue_->Flush();
}
-// Test flaky: crbug.com/1312731
-TEST_P(StorageQueueTest, DISABLED_ValidateVariousRecordSizes) {
+TEST_P(StorageQueueTest, ValidateVariousRecordSizes) {
std::vector<std::string> data;
for (size_t i = 16; i < 16 + 16; ++i) {
data.emplace_back(i, 'R');
@@ -1795,10 +1798,10 @@ TEST_P(StorageQueueTest, WriteRecordWithInsufficientDiskSpace) {
// Update total disk space and reset after running the write operation so it
// does not affect other tests
- const auto original_disk_space = GetDiskResource()->GetTotal();
- GetDiskResource()->Test_SetTotal(0);
+ const auto original_disk_space = options_.disk_space_resource()->GetTotal();
+ options_.disk_space_resource()->Test_SetTotal(0);
Status write_result = WriteString(kData[0]);
- GetDiskResource()->Test_SetTotal(original_disk_space);
+ options_.disk_space_resource()->Test_SetTotal(original_disk_space);
EXPECT_FALSE(write_result.ok());
EXPECT_EQ(write_result.error_code(), error::RESOURCE_EXHAUSTED);
}
@@ -1808,14 +1811,48 @@ TEST_P(StorageQueueTest, WriteRecordWithInsufficientMemory) {
// Update total memory and reset after running the write operation so it does
// not affect other tests
- const auto original_total_memory = GetMemoryResource()->GetTotal();
- GetMemoryResource()->Test_SetTotal(0);
+ const auto original_total_memory = options_.memory_resource()->GetTotal();
+ options_.memory_resource()->Test_SetTotal(0);
Status write_result = WriteString(kData[0]);
- GetMemoryResource()->Test_SetTotal(original_total_memory);
+ options_.memory_resource()->Test_SetTotal(original_total_memory);
EXPECT_FALSE(write_result.ok());
EXPECT_EQ(write_result.error_code(), error::RESOURCE_EXHAUSTED);
}
+TEST_P(StorageQueueTest, UploadWithInsufficientMemory) {
+ CreateTestStorageQueueOrDie(BuildStorageQueueOptionsPeriodic());
+ WriteStringOrDie(kData[0]);
+
+ // Set uploader expectations.
+ test::TestCallbackAutoWaiter waiter;
+ EXPECT_CALL(set_mock_uploader_expectations_,
+ Call(Eq(UploaderInterface::UploadReason::PERIODIC)))
+ .WillOnce(Invoke([&waiter, this](UploaderInterface::UploadReason reason) {
+ return TestUploader::SetUp(&waiter, this)
+ .Complete(Status(error::RESOURCE_EXHAUSTED,
+ "Insufficient memory for upload"));
+ }))
+ .RetiresOnSaturation();
+ EXPECT_CALL(set_mock_uploader_expectations_,
+ Call(Eq(UploaderInterface::UploadReason::FAILURE_RETRY)))
+ .WillOnce(Invoke([&waiter, this](UploaderInterface::UploadReason reason) {
+ return TestUploader::SetUp(&waiter, this)
+ .Required(0, kData[0])
+ .Complete();
+ }))
+ .RetiresOnSaturation();
+
+ // Update total memory to a low amount.
+ const auto original_total_memory = options_.memory_resource()->GetTotal();
+ options_.memory_resource()->Test_SetTotal(100);
+ // Trigger upload.
+ task_environment_.FastForwardBy(base::Seconds(1));
+ // Reset after running upload so it does not affect other tests.
+ options_.memory_resource()->Test_SetTotal(original_total_memory);
+ // Trigger another upload.
+ task_environment_.FastForwardBy(base::Seconds(1));
+}
+
INSTANTIATE_TEST_SUITE_P(
VaryingFileSize,
StorageQueueTest,
diff --git a/chromium/components/reporting/storage/storage_unittest.cc b/chromium/components/reporting/storage/storage_unittest.cc
index b817988cd66..eebb5ee6b90 100644
--- a/chromium/components/reporting/storage/storage_unittest.cc
+++ b/chromium/components/reporting/storage/storage_unittest.cc
@@ -43,9 +43,11 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
using ::testing::_;
+using ::testing::AtLeast;
using ::testing::Between;
using ::testing::DoAll;
using ::testing::Eq;
+using ::testing::Gt;
using ::testing::HasSubstr;
using ::testing::Invoke;
using ::testing::Property;
@@ -220,11 +222,6 @@ class StorageTest
void TearDown() override {
ResetTestStorage();
- // Make sure all memory is deallocated.
- ASSERT_THAT(GetMemoryResource()->GetUsed(), Eq(0u));
- // Make sure all disk is not reserved (files remain, but Storage is not
- // responsible for them anymore).
- ASSERT_THAT(GetDiskResource()->GetUsed(), Eq(0u));
// Log next uploader id for possible verification.
LOG(ERROR) << "Next uploader id=" << next_uploader_id.load();
}
@@ -464,34 +461,6 @@ class StorageTest
const raw_ptr<test::TestCallbackWaiter> waiter_;
};
- // Helper class for setting up mock uploader expectations on empty queue.
- class SetEmpty {
- public:
- explicit SetEmpty(StorageTest* self)
- : uploader_(std::make_unique<TestUploader>(self)) {}
- SetEmpty(const SetEmpty& other) = delete;
- SetEmpty& operator=(const SetEmpty& other) = delete;
- ~SetEmpty() { CHECK(!uploader_) << "Missed 'Complete' call"; }
-
- std::unique_ptr<TestUploader> Complete() {
- CHECK(uploader_) << "'Complete' already called";
- EXPECT_CALL(*uploader_->mock_upload_,
- UploadRecord(Eq(uploader_->uploader_id_), _, _, _))
- .Times(0);
- EXPECT_CALL(*uploader_->mock_upload_,
- UploadRecordFailure(Eq(uploader_->uploader_id_), _, _, _))
- .Times(0);
- EXPECT_CALL(
- *uploader_->mock_upload_,
- UploadComplete(Eq(uploader_->uploader_id_), Eq(Status::StatusOK())))
- .Times(1);
- return std::move(uploader_);
- }
-
- private:
- std::unique_ptr<TestUploader> uploader_;
- };
-
// Helper class for setting up mock uploader expectations for key delivery.
class SetKeyDelivery {
public:
@@ -503,12 +472,13 @@ class StorageTest
std::unique_ptr<TestUploader> Complete() {
CHECK(uploader_) << "'Complete' already called";
+ // Log and ignore records and failures (usually there are none).
EXPECT_CALL(*uploader_->mock_upload_,
UploadRecord(Eq(uploader_->uploader_id_), _, _, _))
- .Times(0);
+ .WillRepeatedly(Return(true));
EXPECT_CALL(*uploader_->mock_upload_,
UploadRecordFailure(Eq(uploader_->uploader_id_), _, _, _))
- .Times(0);
+ .WillRepeatedly(Return(true));
EXPECT_CALL(
*uploader_->mock_upload_,
UploadComplete(Eq(uploader_->uploader_id_), Eq(Status::StatusOK())))
@@ -537,6 +507,7 @@ class StorageTest
}
void ProcessRecord(EncryptedRecord encrypted_record,
+ ScopedReservation scoped_reservation,
base::OnceCallback<void(bool)> processed_cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(test_uploader_checker_);
auto sequence_information = encrypted_record.sequence_information();
@@ -558,7 +529,7 @@ class StorageTest
base::OnceCallback<void(bool)> processed_cb,
scoped_refptr<base::SequencedTaskRunner> task_runner,
TestUploader* uploader, StatusOr<base::StringPiece> result) {
- ASSERT_OK(result.status());
+ ASSERT_OK(result.status()) << result.status();
WrappedRecord wrapped_record;
ASSERT_TRUE(wrapped_record.ParseFromArray(
result.ValueOrDie().data(), result.ValueOrDie().size()));
@@ -732,32 +703,47 @@ class StorageTest
scoped_refptr<EncryptionModuleInterface> encryption_module =
EncryptionModule::Create(
/*renew_encryption_key_period=*/base::Minutes(30))) {
- ASSERT_FALSE(storage_) << "TestStorage already assigned";
- StatusOr<scoped_refptr<Storage>> storage_result =
- CreateTestStorage(options, encryption_module);
- ASSERT_OK(storage_result)
- << "Failed to create TestStorage, error=" << storage_result.status();
- storage_ = std::move(storage_result.ValueOrDie());
-
if (expect_to_need_key_) {
// Set uploader expectations for any queue; expect no records and need
// key. Make sure no uploads happen, and key is requested.
EXPECT_CALL(set_mock_uploader_expectations_,
Call(UploaderInterface::UploadReason::KEY_DELIVERY))
- .WillOnce(Invoke([this](UploaderInterface::UploadReason) {
+ .Times(AtLeast(1))
+ .WillRepeatedly(Invoke([this](UploaderInterface::UploadReason) {
return TestUploader::SetKeyDelivery(this).Complete();
- }))
- .RetiresOnSaturation();
+ }));
+ } else {
+ // No attempts to deliver key.
+ EXPECT_CALL(set_mock_uploader_expectations_,
+ Call(UploaderInterface::UploadReason::KEY_DELIVERY))
+ .Times(0);
}
+
+ ASSERT_FALSE(storage_) << "TestStorage already assigned";
+ StatusOr<scoped_refptr<Storage>> storage_result =
+ CreateTestStorage(options, encryption_module);
+ ASSERT_OK(storage_result)
+ << "Failed to create TestStorage, error=" << storage_result.status();
+ storage_ = std::move(storage_result.ValueOrDie());
}
void ResetTestStorage() {
// Let asynchronous activity finish.
task_environment_.RunUntilIdle();
- storage_.reset();
- // StorageQueue is destructed on a thread,
- // so we need to wait for all queues to destruct.
- task_environment_.RunUntilIdle();
+ if (storage_) {
+ const auto memory_resource = storage_->options().memory_resource();
+ const auto disk_space_resource =
+ storage_->options().disk_space_resource();
+ storage_.reset();
+ // StorageQueue is destructed on a thread,
+ // so we need to wait for all queues to destruct.
+ task_environment_.RunUntilIdle();
+ // Make sure all memory is deallocated.
+ ASSERT_THAT(memory_resource->GetUsed(), Eq(0u));
+ // Make sure all disk is not reserved (files remain, but Storage is not
+ // responsible for them anymore).
+ ASSERT_THAT(disk_space_resource->GetUsed(), Eq(0u));
+ }
expect_to_need_key_ = false;
}
@@ -780,7 +766,8 @@ class StorageTest
}
StorageOptions BuildTestStorageOptions() const {
- auto options = StorageOptions().set_directory(location_.GetPath());
+ StorageOptions options;
+ options.set_directory(location_.GetPath());
if (is_encryption_enabled()) {
// Encryption enabled.
options.set_signature_verification_public_key(std::string(
@@ -899,7 +886,7 @@ class StorageTest
return signed_encryption_key;
}
- void DeliverKey() const {
+ void DeliverKey() {
ASSERT_TRUE(is_encryption_enabled())
<< "Key can be delivered only when encryption is enabled";
storage_->UpdateEncryptionKey(signed_encryption_key_);
@@ -998,7 +985,7 @@ TEST_P(StorageTest, WriteIntoNewStorageAndUploadWithKeyUpdate) {
return;
}
- static constexpr auto kKeyRenewalTime = base::Seconds(5);
+ static constexpr auto kKeyRenewalTime = base::Milliseconds(500);
CreateTestStorageOrDie(BuildTestStorageOptions(),
EncryptionModule::Create(kKeyRenewalTime));
WriteStringOrDie(MANUAL_BATCH, kData[0]);
@@ -1033,25 +1020,24 @@ TEST_P(StorageTest, WriteIntoNewStorageAndUploadWithKeyUpdate) {
WriteStringOrDie(MANUAL_BATCH, kMoreData[2]);
// Wait to trigger encryption key request on the next upload.
- task_environment_.FastForwardBy(kKeyRenewalTime + base::Seconds(1));
+ task_environment_.FastForwardBy(kKeyRenewalTime + base::Milliseconds(100));
- // Set uploader expectations with encryption key request.
+ // Set uploader expectations for MANUAL upload.
test::TestCallbackAutoWaiter waiter;
EXPECT_CALL(set_mock_uploader_expectations_,
Call(Eq(UploaderInterface::UploadReason::KEY_DELIVERY)))
.WillOnce(Invoke([&waiter, this](UploaderInterface::UploadReason reason) {
+ // Prevent more key delivery requests.
+ DeliverKey();
return TestUploader::SetUp(MANUAL_BATCH, &waiter, this)
.Required(3, kMoreData[0])
.Required(4, kMoreData[1])
.Required(5, kMoreData[2])
.Complete();
}))
- // Can be called later again, reject it.
- .WillRepeatedly(Invoke([](UploaderInterface::UploadReason reason) {
- return Status(error::CANCELLED, "Repeated key delivery rejected");
- }));
+ .RetiresOnSaturation();
- // Trigger upload with key update after a long wait.
+ // Trigger upload to make sure data is present.
EXPECT_OK(storage_->Flush(MANUAL_BATCH));
}
@@ -1745,10 +1731,10 @@ TEST_P(StorageTest, KeyDeliveryFailureOnNewStorage) {
key_delivery_failure_.store(false);
EXPECT_CALL(set_mock_uploader_expectations_,
Call(Eq(UploaderInterface::UploadReason::KEY_DELIVERY)))
- .WillOnce(Invoke([this](UploaderInterface::UploadReason) {
+ .Times(AtLeast(1))
+ .WillRepeatedly(Invoke([this](UploaderInterface::UploadReason) {
return TestUploader::SetKeyDelivery(this).Complete();
- }))
- .RetiresOnSaturation();
+ }));
// Forward time to trigger upload
task_environment_.FastForwardBy(base::Seconds(1));
diff --git a/chromium/components/reporting/storage/storage_uploader_interface.h b/chromium/components/reporting/storage/storage_uploader_interface.h
index a3d9711b628..46774f26acf 100644
--- a/chromium/components/reporting/storage/storage_uploader_interface.h
+++ b/chromium/components/reporting/storage/storage_uploader_interface.h
@@ -12,6 +12,7 @@
#include "base/strings/string_piece.h"
#include "components/reporting/proto/synced/record.pb.h"
#include "components/reporting/proto/synced/record_constants.pb.h"
+#include "components/reporting/resources/resource_interface.h"
#include "components/reporting/util/status.h"
#include "components/reporting/util/statusor.h"
@@ -62,6 +63,7 @@ class UploaderInterface {
// the record or error status has been processed, with true if next record
// needs to be delivered and false if the Uploader should stop.
virtual void ProcessRecord(EncryptedRecord record,
+ ScopedReservation scoped_reservation,
base::OnceCallback<void(bool)> processed_cb) = 0;
// Makes a note of a gap [start, start + count). Expects |processed_cb| to
diff --git a/chromium/components/reporting/storage/test_storage_module.cc b/chromium/components/reporting/storage/test_storage_module.cc
index 735cbcb9a56..7e837f731d9 100644
--- a/chromium/components/reporting/storage/test_storage_module.cc
+++ b/chromium/components/reporting/storage/test_storage_module.cc
@@ -21,17 +21,18 @@ namespace test {
TestStorageModuleStrict::TestStorageModuleStrict() {
ON_CALL(*this, AddRecord)
- .WillByDefault(Invoke(this, &TestStorageModule::AddRecordSuccessfully));
+ .WillByDefault(
+ Invoke(this, &TestStorageModuleStrict::AddRecordSuccessfully));
ON_CALL(*this, Flush)
.WillByDefault(
- WithArg<1>(Invoke([](base::OnceCallback<void(Status)> callback) {
+ WithArg<1>(Invoke([](StorageModuleInterface::FlushCallback callback) {
std::move(callback).Run(Status::StatusOK());
})));
}
TestStorageModuleStrict::~TestStorageModuleStrict() = default;
-Record TestStorageModuleStrict::record() const {
+const Record& TestStorageModuleStrict::record() const {
EXPECT_TRUE(record_.has_value());
return record_.value();
}
@@ -41,10 +42,9 @@ Priority TestStorageModuleStrict::priority() const {
return priority_.value();
}
-void TestStorageModuleStrict::AddRecordSuccessfully(
- Priority priority,
- Record record,
- base::OnceCallback<void(Status)> callback) {
+void TestStorageModuleStrict::AddRecordSuccessfully(Priority priority,
+ Record record,
+ EnqueueCallback callback) {
record_ = std::move(record);
priority_ = priority;
std::move(callback).Run(Status::StatusOK());
diff --git a/chromium/components/reporting/storage/test_storage_module.h b/chromium/components/reporting/storage/test_storage_module.h
index 7736b2f3196..a0ce0065bc8 100644
--- a/chromium/components/reporting/storage/test_storage_module.h
+++ b/chromium/components/reporting/storage/test_storage_module.h
@@ -24,14 +24,12 @@ class TestStorageModuleStrict : public StorageModuleInterface {
MOCK_METHOD(void,
AddRecord,
- (Priority priority,
- Record record,
- base::OnceCallback<void(Status)> callback),
+ (Priority priority, Record record, EnqueueCallback callback),
(override));
MOCK_METHOD(void,
Flush,
- (Priority priority, base::OnceCallback<void(Status)> callback),
+ (Priority priority, FlushCallback callback),
(override));
MOCK_METHOD(void,
@@ -44,7 +42,7 @@ class TestStorageModuleStrict : public StorageModuleInterface {
(SignedEncryptionInfo signed_encryption_key),
(override));
- Record record() const;
+ const Record& record() const;
Priority priority() const;
protected:
@@ -53,7 +51,7 @@ class TestStorageModuleStrict : public StorageModuleInterface {
private:
void AddRecordSuccessfully(Priority priority,
Record record,
- base::OnceCallback<void(Status)> callback);
+ EnqueueCallback callback);
absl::optional<Record> record_;
absl::optional<Priority> priority_;
diff --git a/chromium/components/reporting/storage_selector/storage_selector.cc b/chromium/components/reporting/storage_selector/storage_selector.cc
index 98867c6fa8a..1920560125d 100644
--- a/chromium/components/reporting/storage_selector/storage_selector.cc
+++ b/chromium/components/reporting/storage_selector/storage_selector.cc
@@ -23,20 +23,20 @@
#include "components/reporting/util/status_macros.h"
#include "components/reporting/util/statusor.h"
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/dbus/missive/missive_client.h"
#include "components/reporting/storage/missive_storage_module.h"
#include "components/reporting/storage/missive_storage_module_delegate_impl.h"
using ::chromeos::MissiveClient;
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
namespace reporting {
namespace {
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
// Features settings for storage and uploader.
// Use `missived` by all browsers.
const base::Feature kUseMissiveDaemonFeature{StorageSelector::kUseMissiveDaemon,
@@ -51,36 +51,36 @@ const base::Feature kProvideUploaderFeature {
base::FEATURE_DISABLED_BY_DEFAULT
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
};
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
} // namespace
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
// static
const char StorageSelector::kUseMissiveDaemon[] = "ConnectMissiveDaemon";
// static
const char StorageSelector::kProvideUploader[] = "ProvideUploader";
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
// static
bool StorageSelector::is_uploader_required() {
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
return base::FeatureList::IsEnabled(kProvideUploaderFeature);
#else // Not ChromeOS
return true; // Local storage must have an uploader.
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
}
// static
bool StorageSelector::is_use_missive() {
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
return base::FeatureList::IsEnabled(kUseMissiveDaemonFeature);
#else // Not ChromeOS
return false; // Use Local storage.
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
}
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
// static
void StorageSelector::CreateMissiveStorageModule(
base::OnceCallback<void(StatusOr<scoped_refptr<StorageModuleInterface>>)>
@@ -113,5 +113,5 @@ void StorageSelector::CreateMissiveStorageModule(
std::move(cb).Run(missive_storage_module);
return;
}
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
} // namespace reporting
diff --git a/chromium/components/reporting/storage_selector/storage_selector.h b/chromium/components/reporting/storage_selector/storage_selector.h
index e858a2f3484..e65cd2873ff 100644
--- a/chromium/components/reporting/storage_selector/storage_selector.h
+++ b/chromium/components/reporting/storage_selector/storage_selector.h
@@ -8,7 +8,6 @@
#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
#include "components/reporting/storage/storage_module_interface.h"
#include "components/reporting/storage/storage_uploader_interface.h"
#include "components/reporting/util/status.h"
@@ -24,22 +23,22 @@ namespace reporting {
// that case it always connects to Missive Daemon.
class StorageSelector {
public:
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
// Features to select specific backends.
// By default storage is local (as opposed to missive daemon use)
// and upload is enabled.
static const char kUseMissiveDaemon[];
static const char kProvideUploader[];
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
static bool is_use_missive();
static bool is_uploader_required();
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
static void CreateMissiveStorageModule(
base::OnceCallback<void(StatusOr<scoped_refptr<StorageModuleInterface>>)>
cb);
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
};
} // namespace reporting
diff --git a/chromium/components/reporting/util/BUILD.gn b/chromium/components/reporting/util/BUILD.gn
index 8aa7fe30758..24d087f13c5 100644
--- a/chromium/components/reporting/util/BUILD.gn
+++ b/chromium/components/reporting/util/BUILD.gn
@@ -20,11 +20,6 @@ static_library("file") {
"file.cc",
"file.h",
]
- deps = [ "//base" ]
-}
-
-source_set("shared_vector") {
- sources = [ "shared_vector.h" ]
deps = [
":status",
"//base",
@@ -32,6 +27,9 @@ source_set("shared_vector") {
}
proto_library("status_proto") {
+ # Generate JS so it can be used by chrome extensions
+ generate_javascript = true
+
proto_in_dir = "//"
sources = [ "status.proto" ]
}
@@ -96,7 +94,6 @@ source_set("unit_tests") {
"disconnectable_client_unittest.cc",
"file_unittest.cc",
"shared_queue_unittest.cc",
- "shared_vector_unittest.cc",
"status_macros_unittest.cc",
"status_unittest.cc",
"statusor_unittest.cc",
@@ -105,7 +102,6 @@ source_set("unit_tests") {
":disconnectable_client",
":file",
":shared_queue",
- ":shared_vector",
":status",
":status_macros",
":status_proto",
diff --git a/chromium/components/reporting/util/disconnectable_client.cc b/chromium/components/reporting/util/disconnectable_client.cc
index cd5fe81c70d..af106a3d74e 100644
--- a/chromium/components/reporting/util/disconnectable_client.cc
+++ b/chromium/components/reporting/util/disconnectable_client.cc
@@ -12,7 +12,6 @@
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
-#include "base/rand_util.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "components/reporting/util/status.h"
@@ -37,9 +36,8 @@ void DisconnectableClient::MaybeMakeCall(std::unique_ptr<Delegate> delegate) {
return;
}
// Add the delegate to the map.
- const auto id = base::RandUint64();
+ const auto id = ++last_id_;
auto res = outstanding_delegates_.emplace(id, std::move(delegate));
- DCHECK(res.second) << "Duplicate call id " << id;
// Make a call, resume on CallResponded, when response is received.
res.first->second->DoCall(base::BindOnce(&DisconnectableClient::CallResponded,
weak_ptr_factory_.GetWeakPtr(), id));
@@ -52,10 +50,10 @@ void DisconnectableClient::CallResponded(uint64_t id) {
// Callback has already been removed, no action needed.
return;
}
- // Respond through the |delegate|.
- auto delegate = std::move(it->second);
+ // Remove delegate from |outstanding_delegates_|.
+ const auto delegate = std::move(it->second);
outstanding_delegates_.erase(it);
- // Respond.
+ // Respond through the |delegate|.
delegate->Respond(Status::StatusOK());
}
@@ -66,13 +64,14 @@ void DisconnectableClient::SetAvailability(bool is_available) {
<< "available";
if (!is_available_) {
// Cancel all pending calls.
- for (auto& p : outstanding_delegates_) {
- p.second->Respond(
+ while (!outstanding_delegates_.empty()) {
+ // Remove the first delegate from |outstanding_delegates_|.
+ const auto delegate = std::move(outstanding_delegates_.begin()->second);
+ outstanding_delegates_.erase(outstanding_delegates_.begin());
+ // Respond through the |delegate|.
+ delegate->Respond(
Status(reporting::error::UNAVAILABLE, "Service is unavailable"));
- // Release the delegate sooner, don't wait until clear().
- p.second.reset();
}
- outstanding_delegates_.clear();
}
}
diff --git a/chromium/components/reporting/util/disconnectable_client.h b/chromium/components/reporting/util/disconnectable_client.h
index eb3af246885..26b9c525c3a 100644
--- a/chromium/components/reporting/util/disconnectable_client.h
+++ b/chromium/components/reporting/util/disconnectable_client.h
@@ -60,9 +60,11 @@ class DisconnectableClient {
bool is_available_ GUARDED_BY_CONTEXT(sequence_checker_){false};
// Map of delegates indexed by unique ids (all delegates will fail with error
- // Status if service disconnects).
+ // Status if service disconnects). last_id_ is used for generation of these
+ // unique ids.
base::flat_map<uint64_t, std::unique_ptr<Delegate>> outstanding_delegates_
GUARDED_BY_CONTEXT(sequence_checker_);
+ uint64_t last_id_ GUARDED_BY_CONTEXT(sequence_checker_) = 0;
// Weak pointer factory - must be last member of the class.
base::WeakPtrFactory<DisconnectableClient> weak_ptr_factory_{this};
diff --git a/chromium/components/reporting/util/file.cc b/chromium/components/reporting/util/file.cc
index 3243c280450..22340956c28 100644
--- a/chromium/components/reporting/util/file.cc
+++ b/chromium/components/reporting/util/file.cc
@@ -4,6 +4,7 @@
#include "components/reporting/util/file.h"
+#include <string>
#include <utility>
#include <vector>
@@ -11,6 +12,7 @@
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/logging.h"
+#include "base/strings/strcat.h"
namespace reporting {
@@ -40,4 +42,102 @@ bool DeleteFilesWarnIfFailed(
}
return success;
}
+
+StatusOr<std::string> MaybeReadFile(const base::FilePath& file_path,
+ int64_t offset) {
+ base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ if (!file.IsValid()) {
+ return Status(error::NOT_FOUND,
+ base::StrCat({"Could not open health data file ",
+ file_path.MaybeAsASCII()}));
+ }
+
+ base::File::Info file_info;
+ if (!file.GetInfo(&file_info) || file_info.size - offset < 0) {
+ return Status(error::DATA_LOSS,
+ base::StrCat({"Failed to read data file info ",
+ file_path.MaybeAsASCII()}));
+ }
+
+ std::string result;
+ result.resize(file_info.size - offset);
+ const int read_result =
+ file.Read(offset, result.data(), file_info.size - offset);
+ if (read_result != file_info.size - offset) {
+ return Status(error::DATA_LOSS, base::StrCat({"Failed to read data file ",
+ file_path.MaybeAsASCII()}));
+ }
+
+ return result;
+}
+
+Status AppendLine(const base::FilePath& file_path,
+ const base::StringPiece& data) {
+ base::File file(file_path,
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
+ if (!file.IsValid()) {
+ return Status(error::NOT_FOUND,
+ base::StrCat({"Could not open health data file ",
+ file_path.MaybeAsASCII()}));
+ }
+
+ const std::string line = base::StrCat({data, "\n"});
+ const int write_count = file.Write(0, line.data(), line.size());
+ if (write_count < 0 || static_cast<size_t>(write_count) < line.size()) {
+ return Status(error::DATA_LOSS,
+ base::StrCat({"Failed to write health data file ",
+ file_path.MaybeAsASCII(),
+ " write count=", std::to_string(write_count)}));
+ }
+ return Status::StatusOK();
+}
+
+StatusOr<uint32_t> RemoveAndTruncateLine(const base::FilePath& file_path,
+ uint32_t pos) {
+ StatusOr<std::string> status_or = MaybeReadFile(file_path, pos);
+ if (!status_or.ok()) {
+ return status_or.status();
+ }
+ std::string content = status_or.ValueOrDie();
+ uint32_t offset = 0;
+ // Search for next new line after pos.
+ while (offset < content.length()) {
+ if (content.at(offset++) == '\n') {
+ break;
+ }
+ }
+
+ // Check if the last line was removed.
+ if (offset >= content.length()) {
+ content = "";
+ } else {
+ content = content.substr(offset);
+ }
+
+ Status status = MaybeWriteFile(file_path, content);
+ if (!status.ok()) {
+ return status;
+ }
+ return pos + offset;
+}
+
+Status MaybeWriteFile(const base::FilePath& file_path,
+ const base::StringPiece& data) {
+ base::File file(file_path,
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ if (!file.IsValid()) {
+ return Status(error::NOT_FOUND, base::StrCat({"Could not open data file ",
+ file_path.MaybeAsASCII()}));
+ }
+
+ const int write_count = file.Write(0, data.data(), data.size());
+ if (write_count < 0 || static_cast<size_t>(write_count) < data.size()) {
+ return Status(
+ error::DATA_LOSS,
+ base::StrCat({"Failed to write data file ", file_path.MaybeAsASCII(),
+ " write count=", std::to_string(write_count)}));
+ }
+
+ return Status::StatusOK();
+}
} // namespace reporting
diff --git a/chromium/components/reporting/util/file.h b/chromium/components/reporting/util/file.h
index 02c213e26cc..e560ea1e54e 100644
--- a/chromium/components/reporting/util/file.h
+++ b/chromium/components/reporting/util/file.h
@@ -7,9 +7,14 @@
#ifndef COMPONENTS_REPORTING_UTIL_FILE_H_
#define COMPONENTS_REPORTING_UTIL_FILE_H_
+#include <string>
+
#include "base/callback.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
+#include "base/strings/strcat.h"
+#include "components/reporting/util/status.h"
+#include "components/reporting/util/statusor.h"
namespace reporting {
@@ -29,6 +34,23 @@ bool DeleteFilesWarnIfFailed(
base::RepeatingCallback<bool(const base::FilePath&)> pred =
base::BindRepeating([](const base::FilePath&) { return true; }));
+// Attempt to read entire file given from |file_path| starting from offset.
+// Returns a string of the data read, or an error if one was encountered.
+StatusOr<std::string> MaybeReadFile(const base::FilePath& file_path,
+ int64_t offset);
+
+// Appends |data| with a new line to |file_path|.
+Status AppendLine(const base::FilePath& file_path,
+ const base::StringPiece& data);
+
+// Overwrites or creates a new file at |file_path| with the contents |data|.
+Status MaybeWriteFile(const base::FilePath& file_path,
+ const base::StringPiece& data);
+
+// Removes the first |pos| bytes from a file at |file_path| and also removes
+// the rest of the line which the byte at position |pos| was on.
+StatusOr<uint32_t> RemoveAndTruncateLine(const base::FilePath& file_path,
+ uint32_t pos);
} // namespace reporting
#endif // COMPONENTS_REPORTING_UTIL_FILE_H_
diff --git a/chromium/components/reporting/util/file_unittest.cc b/chromium/components/reporting/util/file_unittest.cc
index b896967a448..0d9d89bc504 100644
--- a/chromium/components/reporting/util/file_unittest.cc
+++ b/chromium/components/reporting/util/file_unittest.cc
@@ -4,15 +4,40 @@
#include "components/reporting/util/file.h"
+#include <string>
+
#include "base/files/file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/test_file_util.h"
-
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using ::testing::StrEq;
+
namespace reporting {
namespace {
+constexpr char kNewFile[] = "to_create.txt";
+constexpr char kWriteDataOne[] = "hello world!";
+constexpr char kWriteDataTwo[] = "bye world!";
+constexpr char kMultiLineData[] = "12\n34\n56\n78\n";
+constexpr size_t kMultiLineDataLineLength = 3;
+constexpr size_t kMultiLineDataLines = 4;
+constexpr size_t kOverFlowPos = 256;
+
+void RemoveAndTruncateTest(const base::FilePath& file_path,
+ uint32_t pos,
+ int expected_lines_removed) {
+ const auto remove_status = RemoveAndTruncateLine(file_path, 0);
+ ASSERT_OK(remove_status) << remove_status.status();
+ const auto read_status = MaybeReadFile(file_path, 0);
+ ASSERT_OK(read_status) << read_status.status();
+ ASSERT_THAT(
+ read_status.ValueOrDie(),
+ StrEq(
+ &kMultiLineData[expected_lines_removed * kMultiLineDataLineLength]));
+}
+
TEST(FileTest, DeleteFileWarnIfFailed) {
// This test briefly tests DeleteFileWarnIfFailed, as it mostly calls
// DeleteFile(), which should be more extensively tested in base.
@@ -84,5 +109,93 @@ TEST(FileTest, DeleteFilesWarnIfFailed) {
<< " still exists.";
}
+TEST(FileTest, ReadWriteFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ const auto dir_path = temp_dir.GetPath();
+ ASSERT_TRUE(base::DirectoryExists(dir_path));
+
+ base::FilePath file_path;
+ ASSERT_TRUE(base::CreateTemporaryFileInDir(dir_path, &file_path));
+
+ auto write_status = MaybeWriteFile(file_path, kWriteDataOne);
+ ASSERT_OK(write_status) << write_status;
+
+ auto read_status = MaybeReadFile(file_path, /*offset=*/0);
+ ASSERT_OK(read_status) << read_status.status();
+ EXPECT_EQ(read_status.ValueOrDie(), kWriteDataOne);
+
+ // Overwrite file.
+ write_status = MaybeWriteFile(file_path, kWriteDataTwo);
+ ASSERT_OK(write_status) << write_status;
+
+ read_status = MaybeReadFile(file_path, /*offset=*/0);
+ ASSERT_OK(read_status) << read_status.status();
+ EXPECT_EQ(read_status.ValueOrDie(), kWriteDataTwo);
+
+ // Read file at an out of bounds index
+ read_status = MaybeReadFile(file_path, kOverFlowPos);
+ ASSERT_FALSE(read_status.ok());
+ EXPECT_EQ(read_status.status().error_code(), error::DATA_LOSS);
+}
+
+TEST(FileTest, AppendLine) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ const auto dir_path = temp_dir.GetPath();
+ ASSERT_TRUE(base::DirectoryExists(dir_path));
+
+ base::FilePath file_path;
+ ASSERT_TRUE(base::CreateTemporaryFileInDir(dir_path, &file_path));
+
+ // Create files.
+ auto status = AppendLine(dir_path.AppendASCII(kNewFile), kWriteDataOne);
+ ASSERT_OK(status) << status;
+
+ status = AppendLine(file_path, kWriteDataOne);
+ auto read_status = MaybeReadFile(file_path, /*offset=*/0);
+ ASSERT_OK(read_status) << read_status.status();
+ ASSERT_EQ(read_status.ValueOrDie(), base::StrCat({kWriteDataOne, "\n"}));
+
+ status = AppendLine(file_path, kWriteDataTwo);
+ read_status = MaybeReadFile(file_path, /*offset=*/0);
+ ASSERT_OK(read_status) << read_status.status();
+ ASSERT_EQ(read_status.ValueOrDie(),
+ base::StrCat({kWriteDataOne, "\n", kWriteDataTwo, "\n"}));
+}
+
+TEST(FileTest, RemoveAndTruncateLine) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ const auto dir_path = temp_dir.GetPath();
+ ASSERT_TRUE(base::DirectoryExists(dir_path));
+
+ base::FilePath file_path;
+ ASSERT_TRUE(base::CreateTemporaryFileInDir(dir_path, &file_path));
+
+ const auto write_status = MaybeWriteFile(file_path, kMultiLineData);
+ ASSERT_OK(write_status) << write_status;
+
+ // Load test data into string for substr method.
+ const std::string multi_line_ref(kMultiLineData);
+ int expected_lines_removed = 1;
+
+ // Remove at beginning of line
+ RemoveAndTruncateTest(file_path, 0, expected_lines_removed++);
+
+ // Remove at middle of line
+ RemoveAndTruncateTest(file_path, kMultiLineDataLineLength / 2,
+ expected_lines_removed++);
+
+ // Remove at end of line
+ RemoveAndTruncateTest(file_path, kMultiLineDataLineLength - 1,
+ expected_lines_removed++);
+
+ // Remove at end of file
+ const auto lines_left = kMultiLineDataLines - expected_lines_removed;
+ RemoveAndTruncateTest(file_path, kMultiLineDataLineLength * lines_left - 1,
+ expected_lines_removed);
+}
+
} // namespace
} // namespace reporting
diff --git a/chromium/components/reporting/util/shared_queue_unittest.cc b/chromium/components/reporting/util/shared_queue_unittest.cc
index d06ebbf3c87..bd37d6e5a65 100644
--- a/chromium/components/reporting/util/shared_queue_unittest.cc
+++ b/chromium/components/reporting/util/shared_queue_unittest.cc
@@ -4,6 +4,8 @@
#include "components/reporting/util/shared_queue.h"
+#include <vector>
+
#include "base/callback_helpers.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
diff --git a/chromium/components/reporting/util/shared_vector.h b/chromium/components/reporting/util/shared_vector.h
deleted file mode 100644
index 4bad192c76f..00000000000
--- a/chromium/components/reporting/util/shared_vector.h
+++ /dev/null
@@ -1,199 +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 COMPONENTS_REPORTING_UTIL_SHARED_VECTOR_H_
-#define COMPONENTS_REPORTING_UTIL_SHARED_VECTOR_H_
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/callback_helpers.h"
-#include "base/containers/queue.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "components/reporting/util/status.h"
-#include "components/reporting/util/statusor.h"
-
-namespace reporting {
-
-// SharedVector wraps a |std::vector| and ensures access happens on a
-// SequencedTaskRunner.
-template <typename VectorType>
-class SharedVector
- : public base::RefCountedThreadSafe<SharedVector<VectorType>> {
- public:
- static scoped_refptr<SharedVector<VectorType>> Create() {
- scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner{
- base::ThreadPool::CreateSequencedTaskRunner({})};
- return base::WrapRefCounted(
- new SharedVector<VectorType>(sequenced_task_runner));
- }
-
- void PushBack(VectorType item,
- base::OnceCallback<void()> push_back_complete_cb) {
- sequenced_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&SharedVector::OnPushBack, this, std::move(item),
- std::move(push_back_complete_cb)));
- }
-
- // Erase will call erase on all elements that return true for the
- // |predicate_cb|.
- void Erase(base::RepeatingCallback<bool(const VectorType&)> predicate_cb,
- base::OnceCallback<void(size_t)> remove_complete_cb) {
- sequenced_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&SharedVector::OnErase, this, std::move(predicate_cb),
- std::move(remove_complete_cb)));
- }
-
- // Provided as the nearest equivalent to std::vector::find. A regular find
- // operation may be invalid by the time a caller is notified of its existence.
- // |predicate_cb| is called on each element. If |predicate_cb| returns true
- // |found_cb| is called on the same element, ending the search.
- // |not_found_cb| is called if no elements return true.
- void ExecuteIfFound(
- base::RepeatingCallback<bool(const VectorType&)> predicate_cb,
- base::OnceCallback<void(VectorType&)> found_cb,
- base::OnceCallback<void()> not_found_cb) {
- sequenced_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&SharedVector::OnExecuteIfFound, this,
- std::move(predicate_cb), std::move(found_cb),
- std::move(not_found_cb)));
- }
-
- // Iterates over each element in |vector_|, and calls |predicate_cb|. If
- // |predicate_cb| returns true, |element_executor| will be called on the same
- // element and iteration will continue. At the end of iteration
- // |execute_complete_cb| will be called.
- // A default |predicate_cb| is provided that always returns true.
- void ExecuteOnEachElement(
- base::RepeatingCallback<void(VectorType&)> element_executor,
- base::OnceCallback<void()> execute_complete_cb,
- base::RepeatingCallback<bool(const VectorType&)> predicate_cb =
- base::BindRepeating([](const VectorType&) { return true; })) {
- sequenced_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&SharedVector::OnExecuteOnEachElement, this,
- std::move(element_executor),
- std::move(execute_complete_cb),
- std::move(predicate_cb)));
- }
-
- void IsEmpty(base::OnceCallback<void(bool)> get_empty_cb) {
- sequenced_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&SharedVector::OnIsEmpty, this,
- std::move(get_empty_cb)));
- }
-
- protected:
- virtual ~SharedVector() = default;
-
- private:
- friend class base::RefCountedThreadSafe<SharedVector<VectorType>>;
-
- explicit SharedVector(
- scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner)
- : sequenced_task_runner_(sequenced_task_runner) {
- DETACH_FROM_SEQUENCE(sequence_checker_);
- }
-
- void OnPushBack(VectorType item,
- base::OnceCallback<void()> push_back_complete_cb) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- vector_.push_back(std::move(item));
- base::ThreadPool::PostTask(
- FROM_HERE, {base::TaskPriority::BEST_EFFORT},
- base::BindOnce(
- [](base::OnceCallback<void()> push_back_complete_cb) {
- std::move(push_back_complete_cb).Run();
- },
- std::move(push_back_complete_cb)));
- }
-
- void OnErase(base::RepeatingCallback<bool(const VectorType&)> predicate_cb,
- base::OnceCallback<void(size_t)> remove_complete_cb) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- size_t number_erased = 0;
- for (auto it = vector_.begin(); it != vector_.end();) {
- if (predicate_cb.Run(*it)) {
- it = vector_.erase(it);
- number_erased++;
- } else {
- it++;
- }
- }
- base::ThreadPool::PostTask(
- FROM_HERE, {base::TaskPriority::BEST_EFFORT},
- base::BindOnce(
- [](base::OnceCallback<void(size_t)> remove_complete_cb,
- size_t number_erased) {
- std::move(remove_complete_cb).Run(number_erased);
- },
- std::move(remove_complete_cb), number_erased));
- }
-
- void OnExecuteIfFound(
- base::RepeatingCallback<bool(const VectorType&)> predicate_cb,
- base::OnceCallback<void(VectorType&)> found_cb,
- base::OnceCallback<void()> not_found_cb) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- for (VectorType& element : vector_) {
- if (predicate_cb.Run(element)) {
- std::move(found_cb).Run(element);
- return;
- }
- }
- base::ThreadPool::PostTask(FROM_HERE, {base::TaskPriority::BEST_EFFORT},
- base::BindOnce(
- [](base::OnceCallback<void()> not_found_cb) {
- std::move(not_found_cb).Run();
- },
- std::move(not_found_cb)));
- }
-
- void OnExecuteOnEachElement(
- base::RepeatingCallback<void(VectorType&)> element_executor,
- base::OnceCallback<void()> execute_complete_cb,
- base::RepeatingCallback<bool(const VectorType&)> predicate_cb) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- for (VectorType& element : vector_) {
- if (predicate_cb.Run(element)) {
- element_executor.Run(element);
- } else {
- break;
- }
- }
- base::ThreadPool::PostTask(
- FROM_HERE, {base::TaskPriority::BEST_EFFORT},
- base::BindOnce(
- [](base::OnceCallback<void()> execute_complete_cb) {
- std::move(execute_complete_cb).Run();
- },
- std::move(execute_complete_cb)));
- }
-
- void OnIsEmpty(base::OnceCallback<void(bool)> is_empty_cb) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- base::ThreadPool::PostTask(
- FROM_HERE, {base::TaskPriority::BEST_EFFORT},
- base::BindOnce(
- [](base::OnceCallback<void(bool)> is_empty_cb, bool is_empty) {
- std::move(is_empty_cb).Run(is_empty);
- },
- std::move(is_empty_cb), vector_.empty()));
- }
-
- std::vector<VectorType> vector_;
-
- SEQUENCE_CHECKER(sequence_checker_);
- scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
-};
-
-} // namespace reporting
-
-#endif // COMPONENTS_REPORTING_UTIL_SHARED_VECTOR_H_
diff --git a/chromium/components/reporting/util/shared_vector_unittest.cc b/chromium/components/reporting/util/shared_vector_unittest.cc
deleted file mode 100644
index f4779dd497f..00000000000
--- a/chromium/components/reporting/util/shared_vector_unittest.cc
+++ /dev/null
@@ -1,334 +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 "components/reporting/util/shared_vector.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/callback_helpers.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "base/test/task_environment.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace reporting {
-namespace {
-
-template <typename VectorType>
-class VectorTester {
- public:
- // FindType must be copyable.
- template <typename FindType>
- class Finder {
- public:
- explicit Finder(const FindType& item)
- : sought_item_(item), run_loop_(std::make_unique<base::RunLoop>()) {}
-
- bool Compare(const FindType& item) const { return sought_item_ == item; }
-
- void OnFound(FindType& item) {
- found_result_ = item;
- run_loop_->Quit();
- }
-
- void OnNotFound() { run_loop_->Quit(); }
-
- const FindType& sought_item() const { return sought_item_; }
-
- const absl::optional<FindType>& found_result() const {
- return found_result_;
- }
-
- void Wait() {
- run_loop_->Run();
- run_loop_ = std::make_unique<base::RunLoop>();
- }
-
- private:
- const FindType sought_item_;
- std::unique_ptr<base::RunLoop> run_loop_;
-
- absl::optional<FindType> found_result_;
- };
-
- template <typename ExecuteType>
- class Executor {
- public:
- explicit Executor(size_t expected_value_count)
- : expected_value_count_(expected_value_count),
- run_loop_(std::make_unique<base::RunLoop>()) {}
-
- void CountValue(ExecuteType& item) { found_count_++; }
-
- void Complete() { run_loop_->Quit(); }
-
- void Wait() {
- run_loop_->Run();
- run_loop_ = std::make_unique<base::RunLoop>();
- }
-
- size_t DifferenceInCount() const {
- return expected_value_count_ - found_count_;
- }
-
- size_t found_count() const { return found_count_; }
-
- private:
- const size_t expected_value_count_;
- std::unique_ptr<base::RunLoop> run_loop_;
- size_t found_count_{0};
- };
-
- VectorTester()
- : vector_(SharedVector<VectorType>::Create()),
- sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner({})),
- run_loop_(std::make_unique<base::RunLoop>()) {}
-
- ~VectorTester() = default;
-
- // Find only works on copyable items - so VectorType must be copyable.
- Finder<VectorType> GetFinder(VectorType sought_item) {
- return Finder<VectorType>(sought_item);
- }
-
- Executor<VectorType> GetExecutor(size_t expected_value_count) {
- return Executor<VectorType>(expected_value_count);
- }
-
- void PushBack(VectorType item) {
- vector_->PushBack(
- std::move(item),
- base::BindOnce(&VectorTester<VectorType>::OnPushBackComplete,
- base::Unretained(this)));
- }
-
- // Resets |insert_success| before returning its value.
- absl::optional<bool> GetPushBackSuccess() {
- absl::optional<bool> return_value;
- return_value.swap(insert_success_);
- return return_value;
- }
-
- void Erase(VectorType value) {
- auto predicate_cb = base::BindRepeating(
- [](const VectorType& expected_value, const VectorType& comparison_value)
- -> bool { return expected_value == comparison_value; },
- value);
- vector_->Erase(std::move(predicate_cb),
- base::BindOnce(&VectorTester<VectorType>::OnEraseComplete,
- base::Unretained(this)));
- }
-
- void Erase(base::RepeatingCallback<bool(const VectorType&)> predicate_cb) {
- vector_->Erase(std::move(predicate_cb),
- base::BindOnce(&VectorTester<VectorType>::OnEraseComplete,
- base::Unretained(this)));
- }
-
- absl::optional<uint64_t> GetEraseValue() {
- absl::optional<uint64_t> return_value;
- return_value.swap(number_deleted_);
- return return_value;
- }
-
- void ExecuteIfFound(Finder<VectorType>* finder) {
- vector_->ExecuteIfFound(
- base::BindRepeating(&Finder<VectorType>::Compare,
- base::Unretained(finder)),
- base::BindOnce(&Finder<VectorType>::OnFound, base::Unretained(finder)),
- base::BindOnce(&Finder<VectorType>::OnNotFound,
- base::Unretained(finder)));
- }
-
- void ExecuteOnEachElement(Executor<VectorType>* executor) {
- vector_->ExecuteOnEachElement(
- base::BindRepeating(&Executor<VectorType>::CountValue,
- base::Unretained(executor)),
- base::BindOnce(&Executor<VectorType>::Complete,
- base::Unretained(executor)));
- }
-
- void Wait() {
- run_loop_->Run();
- run_loop_ = std::make_unique<base::RunLoop>();
- }
-
- private:
- void OnPushBackComplete() {
- sequenced_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&VectorTester<VectorType>::VectorPushBackSuccess,
- base::Unretained(this)));
- }
-
- void VectorPushBackSuccess() {
- insert_success_ = true;
- Signal();
- }
-
- void OnEraseComplete(size_t number_deleted) {
- sequenced_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&VectorTester<VectorType>::VectorEraseValue,
- base::Unretained(this), number_deleted));
- }
-
- void VectorEraseValue(uint64_t number_deleted) {
- number_deleted_ = number_deleted;
- Signal();
- }
-
- void Signal() { run_loop_->Quit(); }
-
- scoped_refptr<SharedVector<VectorType>> vector_;
- scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
- std::unique_ptr<base::RunLoop> run_loop_;
-
- absl::optional<bool> insert_success_;
- absl::optional<uint64_t> number_deleted_;
-};
-
-// Ensures that the vector accept values, and will erase inserted values.
-TEST(SharedVectorTest, PushBackAndEraseWorkCorrectly) {
- base::test::TaskEnvironment task_envrionment;
-
- const std::vector<int> kValues = {1, 2, 3, 4, 5};
- const int kInsertLoopTimes = 10;
-
- VectorTester<int> vector_tester;
-
- // PushBack Values
- for (auto value : kValues) {
- vector_tester.PushBack(value);
- vector_tester.Wait();
- auto insert_success = vector_tester.GetPushBackSuccess();
- ASSERT_TRUE(insert_success.has_value());
- EXPECT_TRUE(insert_success.value());
- }
-
- // Attempt to erase inserted values - should find one each.
- for (auto value : kValues) {
- vector_tester.Erase(value);
- vector_tester.Wait();
- auto erase_success = vector_tester.GetEraseValue();
- ASSERT_TRUE(erase_success.has_value());
- EXPECT_EQ(erase_success.value(), uint64_t(1));
- }
-
- // Attempt to erase the values again - shouldn't find any.
- for (auto value : kValues) {
- vector_tester.Erase(value);
- vector_tester.Wait();
- auto erase_success = vector_tester.GetEraseValue();
- ASSERT_TRUE(erase_success.has_value());
- EXPECT_EQ(erase_success.value(), uint64_t(0));
- }
-
- // Attempt to insert the values multiple times - should succeed.
- for (int i = 0; i < kInsertLoopTimes; i++) {
- for (auto value : kValues) {
- vector_tester.PushBack(value);
- vector_tester.Wait();
- auto insert_success = vector_tester.GetPushBackSuccess();
- ASSERT_TRUE(insert_success.has_value());
- EXPECT_TRUE(insert_success.value());
- }
- }
-
- // Attempt to erase inserted values - should find kInsertLoopTimes each.
- for (auto value : kValues) {
- vector_tester.Erase(value);
- vector_tester.Wait();
- auto erase_success = vector_tester.GetEraseValue();
- ASSERT_TRUE(erase_success.has_value());
- EXPECT_EQ(erase_success.value(), uint64_t(kInsertLoopTimes));
- }
-}
-
-// Ensures that SharedVector::ExecuteIfFound works correctly
-TEST(SharedVectorTest, ExecuteIfFoundSucceeds) {
- base::test::TaskEnvironment task_envrionment;
-
- const int kExpectedValue = 1701;
- const int kUnexpectedValue = 42;
-
- VectorTester<int> vector_tester;
- vector_tester.PushBack(kExpectedValue);
- vector_tester.Wait();
-
- auto expected_finder = vector_tester.GetFinder(kExpectedValue);
- vector_tester.ExecuteIfFound(&expected_finder);
- expected_finder.Wait();
- auto found_result = expected_finder.found_result();
- ASSERT_TRUE(found_result.has_value());
- EXPECT_EQ(found_result.value(), kExpectedValue);
-
- auto unexpected_finder = vector_tester.GetFinder(kUnexpectedValue);
- vector_tester.ExecuteIfFound(&unexpected_finder);
- unexpected_finder.Wait();
- found_result = unexpected_finder.found_result();
- EXPECT_FALSE(found_result.has_value());
-}
-
-TEST(SharedVectorTest, ExecuteAllElements) {
- base::test::TaskEnvironment task_envrionment;
-
- const std::vector<int> kValues = {1, 2, 3, 4, 5};
-
- VectorTester<int> vector_tester;
-
- // PushBack Values
- for (auto value : kValues) {
- vector_tester.PushBack(value);
- vector_tester.Wait();
- auto insert_success = vector_tester.GetPushBackSuccess();
- ASSERT_TRUE(insert_success.has_value());
- EXPECT_TRUE(insert_success.value());
- }
-
- auto executor = vector_tester.GetExecutor(kValues.size());
- vector_tester.ExecuteOnEachElement(&executor);
- executor.Wait();
- EXPECT_EQ(executor.DifferenceInCount(), 0u);
-}
-
-// Ensures that execution can happen on elements that are moveable but not
-// copyable.
-TEST(SharedVectorTest, InsertAndExecuteAndEraseOnUniquePtr) {
- base::test::TaskEnvironment task_envrionment;
-
- const std::vector<int> kValues = {1, 2, 3, 4, 5};
-
- VectorTester<std::unique_ptr<int>> vector_tester;
-
- for (auto value : kValues) {
- vector_tester.PushBack(std::make_unique<int>(value));
- vector_tester.Wait();
- auto insert_success = vector_tester.GetPushBackSuccess();
- ASSERT_TRUE(insert_success.has_value());
- EXPECT_TRUE(insert_success.value());
- }
-
- auto executor = vector_tester.GetExecutor(kValues.size());
- vector_tester.ExecuteOnEachElement(&executor);
- executor.Wait();
- EXPECT_EQ(executor.DifferenceInCount(), 0u);
-
- for (auto value : kValues) {
- auto comparator_cb = base::BindRepeating(
- [](int expected_value, const std::unique_ptr<int>& comparison_value)
- -> bool { return expected_value == *comparison_value; },
- value);
- vector_tester.Erase(comparator_cb);
- vector_tester.Wait();
- auto erase_success = vector_tester.GetEraseValue();
- ASSERT_TRUE(erase_success.has_value());
- EXPECT_EQ(erase_success.value(), uint64_t(1));
- }
-}
-
-} // namespace
-} // namespace reporting
diff --git a/chromium/components/reporting/util/status.h b/chromium/components/reporting/util/status.h
index 30b182f5354..1f6a2cc3a88 100644
--- a/chromium/components/reporting/util/status.h
+++ b/chromium/components/reporting/util/status.h
@@ -43,7 +43,7 @@ enum Code : int32_t {
};
} // namespace error
-class Status {
+class [[nodiscard]] Status {
public:
// Creates a "successful" status.
Status();
diff --git a/chromium/components/reporting/util/status_macros.h b/chromium/components/reporting/util/status_macros.h
index 6830b883c6d..6d604bf004c 100644
--- a/chromium/components/reporting/util/status_macros.h
+++ b/chromium/components/reporting/util/status_macros.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_REPORTING_UTIL_STATUS_MACROS_H_
#define COMPONENTS_REPORTING_UTIL_STATUS_MACROS_H_
+#include <utility>
+
#include "components/reporting/util/status.h"
#include "components/reporting/util/statusor.h"
diff --git a/chromium/components/reporting/util/status_macros_unittest.cc b/chromium/components/reporting/util/status_macros_unittest.cc
index 7a6b3217df4..2bd9f3be5d4 100644
--- a/chromium/components/reporting/util/status_macros_unittest.cc
+++ b/chromium/components/reporting/util/status_macros_unittest.cc
@@ -5,6 +5,8 @@
#include "components/reporting/util/status_macros.h"
#include <stdio.h>
+#include <memory>
+#include <utility>
#include "base/bind.h"
#include "base/callback.h"
diff --git a/chromium/components/reporting/util/status_unittest.cc b/chromium/components/reporting/util/status_unittest.cc
index 2ba3b3d1a2c..0a357738396 100644
--- a/chromium/components/reporting/util/status_unittest.cc
+++ b/chromium/components/reporting/util/status_unittest.cc
@@ -5,6 +5,7 @@
#include "components/reporting/util/status.h"
#include <stdio.h>
+#include <utility>
#include "base/logging.h"
#include "components/reporting/util/status.pb.h"
diff --git a/chromium/components/reporting/util/statusor.h b/chromium/components/reporting/util/statusor.h
index 10035061b74..66ab50b3106 100644
--- a/chromium/components/reporting/util/statusor.h
+++ b/chromium/components/reporting/util/statusor.h
@@ -57,6 +57,7 @@
#define COMPONENTS_REPORTING_UTIL_STATUSOR_H_
#include <new>
+#include <string>
#include <type_traits>
#include <utility>
@@ -80,7 +81,7 @@ class StatusOrHelper {
} // namespace internal
template <typename T>
-class StatusOr {
+class [[nodiscard]] StatusOr {
template <typename U>
friend class StatusOr;
@@ -89,8 +90,8 @@ class StatusOr {
// is statically set to true, otherwise it is statically set to false.
template <class U, typename V>
struct is_implicitly_constructible
- : base::conjunction<std::is_constructible<U, V>,
- std::is_convertible<V, U>> {};
+ : std::conjunction<std::is_constructible<U, V>,
+ std::is_convertible<V, U>> {};
public:
// Constructs a new StatusOr with UNINITIALIZED status and no value.
diff --git a/chromium/components/reporting/util/task_runner_context.h b/chromium/components/reporting/util/task_runner_context.h
index ecf5b363d0f..9c06dd6f54a 100644
--- a/chromium/components/reporting/util/task_runner_context.h
+++ b/chromium/components/reporting/util/task_runner_context.h
@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
+#include "base/check.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
diff --git a/chromium/components/reporting/util/task_runner_context_unittest.cc b/chromium/components/reporting/util/task_runner_context_unittest.cc
index d085caace68..3494d47fbee 100644
--- a/chromium/components/reporting/util/task_runner_context_unittest.cc
+++ b/chromium/components/reporting/util/task_runner_context_unittest.cc
@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/check.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/waitable_event.h"
diff --git a/chromium/components/reporting/util/test_support_callbacks.h b/chromium/components/reporting/util/test_support_callbacks.h
index 5ed561999b1..160cc0cc8a1 100644
--- a/chromium/components/reporting/util/test_support_callbacks.h
+++ b/chromium/components/reporting/util/test_support_callbacks.h
@@ -6,10 +6,12 @@
#define COMPONENTS_REPORTING_UTIL_TEST_SUPPORT_CALLBACKS_H_
#include <tuple>
+#include <utility>
#include "base/atomic_ref_count.h"
#include "base/bind.h"
#include "base/callback_forward.h"
+#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/synchronization/lock.h"
diff --git a/chromium/components/resources/BUILD.gn b/chromium/components/resources/BUILD.gn
index db317cd4e32..59ff209aeb8 100644
--- a/chromium/components/resources/BUILD.gn
+++ b/chromium/components/resources/BUILD.gn
@@ -66,6 +66,11 @@ grit("dev_ui_components_resources") {
"grit/dev_ui_components_resources.h",
"dev_ui_components_resources.pak",
]
+
+ deps = [
+ "//components/local_state:build",
+ "//components/policy/resources/webui:web_components",
+ ]
output_dir = "$root_gen_dir/components"
}
diff --git a/chromium/components/resources/OWNERS b/chromium/components/resources/OWNERS
index d868fd667a6..755d244c54b 100644
--- a/chromium/components/resources/OWNERS
+++ b/chromium/components/resources/OWNERS
@@ -1,6 +1,7 @@
per-file about_ui_resources.grdp=file://ui/webui/PLATFORM_OWNERS
per-file android_system_error_page_resources.grdp=file://components/android_system_error_page/OWNERS
per-file autofill*=file://components/autofill/OWNERS
+per-file commerce*=file://components/commerce/OWNERS
per-file content_suggestions*=file://components/ntp_snippets/OWNERS
per-file crash_*=rsesek@chromium.org
per-file crash_*=thestig@chromium.org
diff --git a/chromium/components/resources/commerce_heuristics.grdp b/chromium/components/resources/commerce_resources.grdp
index 84fcb43c2fb..c4b34de2176 100644
--- a/chromium/components/resources/commerce_heuristics.grdp
+++ b/chromium/components/resources/commerce_resources.grdp
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
- <include name="IDR_CART_DOMAIN_CART_URL_REGEX_JSON" file="../commerce/core/heuristics/resources/cart_domain_cart_url_regex.json" type="BINDATA" />
- <include name="IDR_CHECKOUT_URL_REGEX_DOMAIN_MAPPING_JSON" file="../commerce/core/heuristics/resources/checkout_url_regex_domain_mapping.json" type="BINDATA"/>
+ <include name="IDR_CART_DOMAIN_CART_URL_REGEX_JSON" file="../commerce/core/heuristics/resources/cart_domain_cart_url_regex.json" compress="gzip" type="BINDATA" />
+ <include name="IDR_CHECKOUT_URL_REGEX_DOMAIN_MAPPING_JSON" file="../commerce/core/heuristics/resources/checkout_url_regex_domain_mapping.json" compress="gzip" type="BINDATA"/>
+
+ <include name="IDR_QUERY_SHOPPING_META_JS" file="../commerce/core/resources/query_shopping_meta.js" type="BINDATA" />
</grit-part>
diff --git a/chromium/components/resources/components_resources.grd b/chromium/components/resources/components_resources.grd
index 276e5d163fa..cca1ebdfcda 100644
--- a/chromium/components/resources/components_resources.grd
+++ b/chromium/components/resources/components_resources.grd
@@ -14,6 +14,7 @@ dev_ui_components_resources.grd.
<release seq="1">
<includes>
<part file="about_ui_resources.grdp" />
+ <part file="commerce_resources.grdp" />
<part file="dom_distiller_resources.grdp" />
<part file="flags_ui_resources.grdp" />
<part file="management_resources.grdp" />
@@ -30,9 +31,6 @@ dev_ui_components_resources.grd.
<if expr="is_android">
<part file="android_system_error_page_resources.grdp" />
</if>
- <if expr="not is_android">
- <part file="commerce_heuristics.grdp" />
- </if>
</includes>
</release>
</grit>
diff --git a/chromium/components/resources/default_100_percent/autofill/virtual_card_enroll.png b/chromium/components/resources/default_100_percent/autofill/virtual_card_enroll.png
index 660bb83abf9..993fbae1eb9 100644
--- a/chromium/components/resources/default_100_percent/autofill/virtual_card_enroll.png
+++ b/chromium/components/resources/default_100_percent/autofill/virtual_card_enroll.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/virtual_card_enroll_dark.png b/chromium/components/resources/default_100_percent/autofill/virtual_card_enroll_dark.png
index ad82791446c..25562bda7d6 100644
--- a/chromium/components/resources/default_100_percent/autofill/virtual_card_enroll_dark.png
+++ b/chromium/components/resources/default_100_percent/autofill/virtual_card_enroll_dark.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/virtual_card_enroll.png b/chromium/components/resources/default_200_percent/autofill/virtual_card_enroll.png
index b41324914e0..f7d249941c9 100644
--- a/chromium/components/resources/default_200_percent/autofill/virtual_card_enroll.png
+++ b/chromium/components/resources/default_200_percent/autofill/virtual_card_enroll.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/virtual_card_enroll_dark.png b/chromium/components/resources/default_200_percent/autofill/virtual_card_enroll_dark.png
index aa6fdb72d42..00972335236 100644
--- a/chromium/components/resources/default_200_percent/autofill/virtual_card_enroll_dark.png
+++ b/chromium/components/resources/default_200_percent/autofill/virtual_card_enroll_dark.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/virtual_card_enroll.png b/chromium/components/resources/default_300_percent/autofill/virtual_card_enroll.png
index 1a18b1e56a0..a9efe38bee1 100644
--- a/chromium/components/resources/default_300_percent/autofill/virtual_card_enroll.png
+++ b/chromium/components/resources/default_300_percent/autofill/virtual_card_enroll.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/virtual_card_enroll_dark.png b/chromium/components/resources/default_300_percent/autofill/virtual_card_enroll_dark.png
index d393ec1d20f..e062bfdc489 100644
--- a/chromium/components/resources/default_300_percent/autofill/virtual_card_enroll_dark.png
+++ b/chromium/components/resources/default_300_percent/autofill/virtual_card_enroll_dark.png
Binary files differ
diff --git a/chromium/components/resources/dev_ui_components_resources.grd b/chromium/components/resources/dev_ui_components_resources.grd
index 318653005b6..952fa5b6906 100644
--- a/chromium/components/resources/dev_ui_components_resources.grd
+++ b/chromium/components/resources/dev_ui_components_resources.grd
@@ -19,6 +19,7 @@ Feature Module (DevUI DFM) for Android Chrome. More info:
<part file="autofill_and_password_manager_internals_resources.grdp" />
<part file="crash_resources.grdp" />
<part file="gcm_driver_resources.grdp" />
+ <part file="local_state.grdp" />
<part file="net_log_resources.grdp" />
<part file="ntp_tiles_dev_ui_resources.grdp" />
<part file="policy_resources.grdp" />
diff --git a/chromium/components/resources/local_state.grdp b/chromium/components/resources/local_state.grdp
new file mode 100644
index 00000000000..364a4e84654
--- /dev/null
+++ b/chromium/components/resources/local_state.grdp
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+ <include name="IDR_LOCAL_STATE_HTML" file="../local_state/local_state.html" type="BINDATA" />
+ <include name="IDR_LOCAL_STATE_JS" file="${root_gen_dir}/components/local_state/local_state.js" use_base_dir="false" type="BINDATA" />
+</grit-part> \ No newline at end of file
diff --git a/chromium/components/resources/policy_resources.grdp b/chromium/components/resources/policy_resources.grdp
index 427d31ee78b..2670f27d3b1 100644
--- a/chromium/components/resources/policy_resources.grdp
+++ b/chromium/components/resources/policy_resources.grdp
@@ -4,4 +4,9 @@
<include name="IDR_POLICY_HTML" file="../policy/resources/webui/policy.html" type="BINDATA" />
<include name="IDR_POLICY_BASE_JS" file="../policy/resources/webui/policy_base.js" preprocess="true" type="BINDATA" />
<include name="IDR_POLICY_JS" file="../policy/resources/webui/policy.js" type="BINDATA" />
+ <include name="IDR_POLICY_POLICY_CONFLICT_JS" file="${root_gen_dir}/components/policy/resources/webui/policy_conflict.js" use_base_dir="false" type="BINDATA" />
+ <include name="IDR_POLICY_POLICY_ROW_JS" file="${root_gen_dir}/components/policy/resources/webui/policy_row.js" use_base_dir="false" type="BINDATA" />
+ <include name="IDR_POLICY_POLICY_PRECEDENCE_ROW_JS" file="${root_gen_dir}/components/policy/resources/webui/policy_precedence_row.js" use_base_dir="false" type="BINDATA" />
+ <include name="IDR_POLICY_POLICY_TABLE_JS" file="${root_gen_dir}/components/policy/resources/webui/policy_table.js" use_base_dir="false" type="BINDATA" />
+ <include name="IDR_POLICY_STATUS_BOX_JS" file="${root_gen_dir}/components/policy/resources/webui/status_box.js" use_base_dir="false" type="BINDATA" />
</grit-part>
diff --git a/chromium/components/safe_browsing/BUILD.gn b/chromium/components/safe_browsing/BUILD.gn
index 71f40a9d5da..f076662b8da 100644
--- a/chromium/components/safe_browsing/BUILD.gn
+++ b/chromium/components/safe_browsing/BUILD.gn
@@ -9,6 +9,19 @@ buildflag_header("buildflags") {
header = "buildflags.h"
flags = []
+
+ # FULL_SAFE_BROWSING means "are all Safe Browsing features available?"
+ # This is true only for desktop OSes.
+ #
+ # SAFE_BROWSING_AVAILABLE means "are any Safe Browsing features available?"
+ # This is true only for desktop OSes or Android.
+ #
+ # SAFE_BROWSING_DB_LOCAL means "are SB databases available locally?"
+ # This is true only for desktop OSes.
+ #
+ # SAFE_BROWSING_DB_REMOTE means "are SB databases available via GMS Core?"
+ # This is true only for Android.
+ #
if (safe_browsing_mode == 0) {
flags += [ "FULL_SAFE_BROWSING=0" ]
flags += [ "SAFE_BROWSING_AVAILABLE=0" ]
diff --git a/chromium/components/safe_browsing/DEPS b/chromium/components/safe_browsing/DEPS
index f4d1cf27330..e7a9e52494c 100644
--- a/chromium/components/safe_browsing/DEPS
+++ b/chromium/components/safe_browsing/DEPS
@@ -30,6 +30,7 @@ include_rules = [
"+third_party/tflite_support",
"+third_party/tflite",
"+third_party/protobuf",
+ "+third_party/blink/public/common/associated_interfaces",
"+ui/base/resource/resource_bundle.h",
"+ui/android/view_android.h",
diff --git a/chromium/components/safe_browsing/android/BUILD.gn b/chromium/components/safe_browsing/android/BUILD.gn
index 32b6836d9cf..7c783081624 100644
--- a/chromium/components/safe_browsing/android/BUILD.gn
+++ b/chromium/components/safe_browsing/android/BUILD.gn
@@ -8,6 +8,8 @@ import("//build/config/android/rules.gni")
android_library("safe_browsing_java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
@@ -66,25 +68,19 @@ static_library("safe_browsing_api_handler_util") {
source_set("safe_browsing_api_handler") {
sources = [
- "safe_browsing_api_handler.cc",
- "safe_browsing_api_handler.h",
+ "safe_browsing_api_handler_bridge.cc",
+ "safe_browsing_api_handler_bridge.h",
]
deps = [
+ ":jni_headers",
":safe_browsing_api_handler_util",
"//base",
+ "//components/safe_browsing:buildflags",
"//components/safe_browsing/core/browser/db:util",
"//components/safe_browsing/core/common",
"//content/public/browser:browser",
"//url",
]
-
- if (is_android) {
- deps += [ ":jni_headers" ]
- sources += [
- "safe_browsing_api_handler_bridge.cc",
- "safe_browsing_api_handler_bridge.h",
- ]
- }
}
source_set("unit_tests_mobile") {
diff --git a/chromium/components/safe_browsing/android/remote_database_manager.cc b/chromium/components/safe_browsing/android/remote_database_manager.cc
index c7e4c500abb..e86a3dd3cf7 100644
--- a/chromium/components/safe_browsing/android/remote_database_manager.cc
+++ b/chromium/components/safe_browsing/android/remote_database_manager.cc
@@ -14,7 +14,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/timer/elapsed_timer.h"
-#include "components/safe_browsing/android/safe_browsing_api_handler.h"
+#include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
#include "components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.h"
#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
#include "components/variations/variations_associated_data.h"
@@ -206,16 +206,11 @@ bool RemoteSafeBrowsingDatabaseManager::CheckBrowseUrl(
std::unique_ptr<ClientRequest> req(new ClientRequest(client, this, url));
DVLOG(1) << "Checking for client " << client << " and URL " << url;
- SafeBrowsingApiHandler* api_handler = SafeBrowsingApiHandler::GetInstance();
- // This shouldn't happen since SafeBrowsingResourceThrottle and
- // SubresourceFilterSafeBrowsingActivationThrottle check IsSupported()
- // earlier.
- DCHECK(api_handler) << "SafeBrowsingApiHandler was never constructed";
-
auto callback =
- std::make_unique<SafeBrowsingApiHandler::URLCheckCallbackMeta>(
+ std::make_unique<SafeBrowsingApiHandlerBridge::ResponseCallback>(
base::BindOnce(&ClientRequest::OnRequestDoneWeak, req->GetWeakPtr()));
- api_handler->StartURLCheck(std::move(callback), url, threat_types);
+ SafeBrowsingApiHandlerBridge::GetInstance().StartURLCheck(std::move(callback),
+ url, threat_types);
current_requests_.push_back(req.release());
@@ -253,8 +248,8 @@ RemoteSafeBrowsingDatabaseManager::CheckUrlForHighConfidenceAllowlist(
return AsyncMatch::NO_MATCH;
// TODO(crbug.com/1014202): Make this call async.
- SafeBrowsingApiHandler* api_handler = SafeBrowsingApiHandler::GetInstance();
- bool is_match = api_handler->StartHighConfidenceAllowlistCheck(url);
+ bool is_match = SafeBrowsingApiHandlerBridge::GetInstance()
+ .StartHighConfidenceAllowlistCheck(url);
return is_match ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH;
}
@@ -276,15 +271,10 @@ bool RemoteSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
std::unique_ptr<ClientRequest> req(new ClientRequest(client, this, url));
DVLOG(1) << "Checking for client " << client << " and URL " << url;
- SafeBrowsingApiHandler* api_handler = SafeBrowsingApiHandler::GetInstance();
- // This shouldn't happen since SafeBrowsingResourceThrottle and
- // SubresourceFilterSafeBrowsingActivationThrottle check IsSupported()
- // earlier.
- DCHECK(api_handler) << "SafeBrowsingApiHandler was never constructed";
auto callback =
- std::make_unique<SafeBrowsingApiHandler::URLCheckCallbackMeta>(
+ std::make_unique<SafeBrowsingApiHandlerBridge::ResponseCallback>(
base::BindOnce(&ClientRequest::OnRequestDoneWeak, req->GetWeakPtr()));
- api_handler->StartURLCheck(
+ SafeBrowsingApiHandlerBridge::GetInstance().StartURLCheck(
std::move(callback), url,
CreateSBThreatTypeSet(
{SB_THREAT_TYPE_SUBRESOURCE_FILTER, SB_THREAT_TYPE_URL_PHISHING}));
@@ -305,9 +295,9 @@ AsyncMatch RemoteSafeBrowsingDatabaseManager::CheckCsdAllowlistUrl(
return AsyncMatch::MATCH;
}
- // TODO(crbug.com/995926): Make this call async
- SafeBrowsingApiHandler* api_handler = SafeBrowsingApiHandler::GetInstance();
- bool is_match = api_handler->StartCSDAllowlistCheck(url);
+ // TODO(crbug.com/995926): Make this call async.
+ bool is_match =
+ SafeBrowsingApiHandlerBridge::GetInstance().StartCSDAllowlistCheck(url);
return is_match ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH;
}
@@ -332,10 +322,6 @@ bool RemoteSafeBrowsingDatabaseManager::IsDownloadProtectionEnabled() const {
return false;
}
-bool RemoteSafeBrowsingDatabaseManager::IsSupported() const {
- return SafeBrowsingApiHandler::GetInstance() != nullptr;
-}
-
void RemoteSafeBrowsingDatabaseManager::StartOnIOThread(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const V4ProtocolConfig& config) {
diff --git a/chromium/components/safe_browsing/android/remote_database_manager.h b/chromium/components/safe_browsing/android/remote_database_manager.h
index d8a559fdfee..af709615304 100644
--- a/chromium/components/safe_browsing/android/remote_database_manager.h
+++ b/chromium/components/safe_browsing/android/remote_database_manager.h
@@ -62,7 +62,6 @@ class RemoteSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
bool MatchMalwareIP(const std::string& ip_address) override;
safe_browsing::ThreatSource GetThreatSource() const override;
bool IsDownloadProtectionEnabled() const override;
- bool IsSupported() const override;
void StartOnIOThread(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const V4ProtocolConfig& config) override;
diff --git a/chromium/components/safe_browsing/android/remote_database_manager_unittest.cc b/chromium/components/safe_browsing/android/remote_database_manager_unittest.cc
index 1f54c5c3143..1f3e32496c7 100644
--- a/chromium/components/safe_browsing/android/remote_database_manager_unittest.cc
+++ b/chromium/components/safe_browsing/android/remote_database_manager_unittest.cc
@@ -10,7 +10,7 @@
#include "base/metrics/field_trial.h"
#include "base/run_loop.h"
#include "base/time/time.h"
-#include "components/safe_browsing/android/safe_browsing_api_handler.h"
+#include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/test/browser_task_environment.h"
#include "services/network/public/mojom/fetch_api.mojom.h"
@@ -20,15 +20,12 @@ namespace safe_browsing {
namespace {
-class TestSafeBrowsingApiHandler : public SafeBrowsingApiHandler {
+class BlackHoleInterceptor : public safe_browsing::UrlCheckInterceptor {
public:
- void StartURLCheck(std::unique_ptr<URLCheckCallbackMeta> callback,
- const GURL& url,
- const SBThreatTypeSet& threat_types) override {}
- bool StartCSDAllowlistCheck(const GURL& url) override { return false; }
- bool StartHighConfidenceAllowlistCheck(const GURL& url) override {
- return false;
- }
+ void Check(
+ std::unique_ptr<SafeBrowsingApiHandlerBridge::ResponseCallback> callback,
+ const GURL& url) const override{};
+ ~BlackHoleInterceptor() override{};
};
} // namespace
@@ -38,7 +35,8 @@ class RemoteDatabaseManagerTest : public testing::Test {
RemoteDatabaseManagerTest() {}
void SetUp() override {
- SafeBrowsingApiHandler::SetInstance(&api_handler_);
+ SafeBrowsingApiHandlerBridge::GetInstance().SetInterceptorForTesting(
+ &url_interceptor_);
db_ = new RemoteSafeBrowsingDatabaseManager();
}
@@ -66,17 +64,10 @@ class RemoteDatabaseManagerTest : public testing::Test {
}
content::BrowserTaskEnvironment task_environment_;
- TestSafeBrowsingApiHandler api_handler_;
+ BlackHoleInterceptor url_interceptor_;
scoped_refptr<RemoteSafeBrowsingDatabaseManager> db_;
};
-TEST_F(RemoteDatabaseManagerTest, DisabledViaNull) {
- EXPECT_TRUE(db_->IsSupported());
-
- SafeBrowsingApiHandler::SetInstance(nullptr);
- EXPECT_FALSE(db_->IsSupported());
-}
-
TEST_F(RemoteDatabaseManagerTest, DestinationsToCheckDefault) {
// Most are true, a few are false.
for (int t_int = 0;
diff --git a/chromium/components/safe_browsing/android/safe_browsing_api_handler.cc b/chromium/components/safe_browsing/android/safe_browsing_api_handler.cc
deleted file mode 100644
index b8d15323ffa..00000000000
--- a/chromium/components/safe_browsing/android/safe_browsing_api_handler.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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.
-
-#include "components/safe_browsing/android/safe_browsing_api_handler.h"
-#include "base/bind.h"
-
-namespace safe_browsing {
-
-SafeBrowsingApiHandler* SafeBrowsingApiHandler::instance_ = nullptr;
-
-// static
-void SafeBrowsingApiHandler::SetInstance(SafeBrowsingApiHandler* instance) {
- instance_ = instance;
-}
-
-// static
-SafeBrowsingApiHandler* SafeBrowsingApiHandler::GetInstance() {
- return instance_;
-}
-
-} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/android/safe_browsing_api_handler.h b/chromium/components/safe_browsing/android/safe_browsing_api_handler.h
deleted file mode 100644
index 51fcb763cd3..00000000000
--- a/chromium/components/safe_browsing/android/safe_browsing_api_handler.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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.
-//
-// Glue to pass Safe Browsing API requests between
-// RemoteSafeBrowsingDatabaseManager and Java-based API to check URLs.
-
-#ifndef COMPONENTS_SAFE_BROWSING_ANDROID_SAFE_BROWSING_API_HANDLER_H_
-#define COMPONENTS_SAFE_BROWSING_ANDROID_SAFE_BROWSING_API_HANDLER_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "components/safe_browsing/core/browser/db/util.h"
-#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
-#include "url/gurl.h"
-
-namespace safe_browsing {
-
-class SafeBrowsingApiHandler {
- public:
- // Singleton interface.
- static void SetInstance(SafeBrowsingApiHandler* instance);
- static SafeBrowsingApiHandler* GetInstance();
-
- typedef base::OnceCallback<void(SBThreatType sb_threat_type,
- const ThreatMetadata& metadata)>
- URLCheckCallbackMeta;
-
- // Makes Native->Java call and invokes callback when check is done.
- virtual void StartURLCheck(std::unique_ptr<URLCheckCallbackMeta> callback,
- const GURL& url,
- const SBThreatTypeSet& threat_types) = 0;
-
- virtual bool StartCSDAllowlistCheck(const GURL& url) = 0;
-
- virtual bool StartHighConfidenceAllowlistCheck(const GURL& url) = 0;
-
- virtual ~SafeBrowsingApiHandler() {}
-
- private:
- // Pointer not owned.
- static SafeBrowsingApiHandler* instance_;
-};
-
-} // namespace safe_browsing
-
-#endif // COMPONENTS_SAFE_BROWSING_ANDROID_SAFE_BROWSING_API_HANDLER_H_
diff --git a/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc b/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
index d7b3d9636f7..47ce6729fac 100644
--- a/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
+++ b/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
@@ -36,11 +36,9 @@ namespace safe_browsing {
namespace {
void RunCallbackOnIOThread(
- std::unique_ptr<SafeBrowsingApiHandler::URLCheckCallbackMeta> callback,
+ std::unique_ptr<SafeBrowsingApiHandlerBridge::ResponseCallback> callback,
SBThreatType threat_type,
const ThreatMetadata& metadata) {
- CHECK(callback); // Remove after fixing crbug.com/889972
- CHECK(!callback->is_null()); // Remove after fixing crbug.com/889972
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(*callback), threat_type, metadata));
}
@@ -91,7 +89,7 @@ ScopedJavaLocalRef<jintArray> SBThreatTypeSetToJavaArray(
// response.
typedef std::unordered_map<
jlong,
- std::unique_ptr<SafeBrowsingApiHandler::URLCheckCallbackMeta>>
+ std::unique_ptr<SafeBrowsingApiHandlerBridge::ResponseCallback>>
PendingCallbacksMap;
static PendingCallbacksMap* GetPendingCallbacksMapOnIOThread() {
@@ -104,14 +102,32 @@ static PendingCallbacksMap* GetPendingCallbacksMapOnIOThread() {
return &pending_callbacks;
}
+bool StartAllowlistCheck(const GURL& url, const SBThreatType& sb_threat_type) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ JNIEnv* env = AttachCurrentThread();
+ if (!Java_SafeBrowsingApiBridge_ensureCreated(env)) {
+ return false;
+ }
+
+ ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, url.spec());
+ int j_threat_type = SBThreatTypeToJavaThreatType(sb_threat_type);
+ return Java_SafeBrowsingApiBridge_startAllowlistLookup(env, j_url,
+ j_threat_type);
+}
+
} // namespace
+// static
+SafeBrowsingApiHandlerBridge& SafeBrowsingApiHandlerBridge::GetInstance() {
+ static base::NoDestructor<SafeBrowsingApiHandlerBridge> instance;
+ return *instance.get();
+}
// Respond to the URL reputation request by looking up the callback information
// stored in |pending_callbacks|.
-// |callback_id| is an int form of pointer to a URLCheckCallbackMeta
+// |callback_id| is an int form of pointer to a ::ResponseCallback
// that will be called and then deleted here.
-// |result_status| is one of those from SafeBrowsingApiHandler.java
+// |result_status| is one of those from SafeBrowsingApiHandlerBridge.java
// |metadata| is a JSON string classifying the threat if there is one.
void OnUrlCheckDoneOnIOThread(jlong callback_id,
jint result_status,
@@ -124,19 +140,14 @@ void OnUrlCheckDoneOnIOThread(jlong callback_id,
if (!found)
return;
- std::unique_ptr<SafeBrowsingApiHandler::URLCheckCallbackMeta> callback =
+ std::unique_ptr<SafeBrowsingApiHandlerBridge::ResponseCallback> callback =
std::move((*pending_callbacks)[callback_id]);
- CHECK(callback); // Remove after fixing crbug.com/889972
pending_callbacks->erase(callback_id);
if (result_status != RESULT_STATUS_SUCCESS) {
if (result_status == RESULT_STATUS_TIMEOUT) {
- CHECK(!callback->is_null()); // Remove after fixing crbug.com/889972
-
ReportUmaResult(UMA_STATUS_TIMEOUT);
} else {
- CHECK(!callback->is_null()); // Remove after fixing crbug.com/889972
-
DCHECK_EQ(result_status, RESULT_STATUS_INTERNAL_ERROR);
ReportUmaResult(UMA_STATUS_INTERNAL_ERROR);
}
@@ -146,13 +157,9 @@ void OnUrlCheckDoneOnIOThread(jlong callback_id,
// Shortcut for safe, so we don't have to parse JSON.
if (metadata == "{}") {
- CHECK(!callback->is_null()); // Remove after fixing crbug.com/889972
-
ReportUmaResult(UMA_STATUS_SAFE);
std::move(*callback).Run(SB_THREAT_TYPE_SAFE, ThreatMetadata());
} else {
- CHECK(!callback->is_null()); // Remove after fixing crbug.com/889972
-
// Unsafe, assuming we can parse the JSON.
SBThreatType worst_threat;
ThreatMetadata threat_metadata;
@@ -165,9 +172,9 @@ void OnUrlCheckDoneOnIOThread(jlong callback_id,
// Java->Native call, invoked when a check is done.
// |callback_id| is a key into the |pending_callbacks_| map, whose value is a
-// URLCheckCallbackMeta that will be called and then deleted on
+// ::ResponseCallback that will be called and then deleted on
// the IO thread.
-// |result_status| is one of those from SafeBrowsingApiHandler.java
+// |result_status| is a @SafeBrowsingResult from SafeBrowsingApiHandler.java
// |metadata| is a JSON string classifying the threat if there is one.
// |check_delta| is the number of microseconds it took to look up the URL
// reputation from GmsCore.
@@ -196,42 +203,20 @@ void JNI_SafeBrowsingApiBridge_OnUrlCheckDone(
//
// SafeBrowsingApiHandlerBridge
//
-SafeBrowsingApiHandlerBridge::SafeBrowsingApiHandlerBridge()
- : checked_api_support_(false) {}
-
SafeBrowsingApiHandlerBridge::~SafeBrowsingApiHandlerBridge() {}
-bool SafeBrowsingApiHandlerBridge::CheckApiIsSupported() {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- if (!checked_api_support_) {
- j_api_handler_ = base::android::ScopedJavaGlobalRef<jobject>(
- Java_SafeBrowsingApiBridge_create(AttachCurrentThread()));
- checked_api_support_ = true;
- }
- return j_api_handler_.obj() != nullptr;
-}
-
-bool SafeBrowsingApiHandlerBridge::StartAllowlistCheck(
- const GURL& url,
- const SBThreatType& sb_threat_type) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- if (!CheckApiIsSupported()) {
- return false;
- }
-
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, url.spec());
- int j_threat_type = SBThreatTypeToJavaThreatType(sb_threat_type);
- return Java_SafeBrowsingApiBridge_startAllowlistLookup(env, j_api_handler_,
- j_url, j_threat_type);
-}
-
void SafeBrowsingApiHandlerBridge::StartURLCheck(
- std::unique_ptr<SafeBrowsingApiHandler::URLCheckCallbackMeta> callback,
+ std::unique_ptr<ResponseCallback> callback,
const GURL& url,
const SBThreatTypeSet& threat_types) {
+ if (interceptor_for_testing_) {
+ // For testing, only check the interceptor.
+ interceptor_for_testing_->Check(std::move(callback), url);
+ return;
+ }
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- if (!CheckApiIsSupported()) {
+ JNIEnv* env = AttachCurrentThread();
+ if (!Java_SafeBrowsingApiBridge_ensureCreated(env)) {
// Mark all requests as safe. Only users who have an old, broken GMSCore or
// have sideloaded Chrome w/o PlayStore should land here.
RunCallbackOnIOThread(std::move(callback), SB_THREAT_TYPE_SAFE,
@@ -246,21 +231,24 @@ void SafeBrowsingApiHandlerBridge::StartURLCheck(
DCHECK(!threat_types.empty());
- JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, url.spec());
ScopedJavaLocalRef<jintArray> j_threat_types =
SBThreatTypeSetToJavaArray(env, threat_types);
- Java_SafeBrowsingApiBridge_startUriLookup(env, j_api_handler_, callback_id,
- j_url, j_threat_types);
+ Java_SafeBrowsingApiBridge_startUriLookup(env, callback_id, j_url,
+ j_threat_types);
}
bool SafeBrowsingApiHandlerBridge::StartCSDAllowlistCheck(const GURL& url) {
+ if (interceptor_for_testing_)
+ return false;
return StartAllowlistCheck(url, safe_browsing::SB_THREAT_TYPE_CSD_ALLOWLIST);
}
bool SafeBrowsingApiHandlerBridge::StartHighConfidenceAllowlistCheck(
const GURL& url) {
+ if (interceptor_for_testing_)
+ return false;
return StartAllowlistCheck(
url, safe_browsing::SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
}
diff --git a/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.h b/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
index ca63d2cb785..298617c7f9a 100644
--- a/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
+++ b/chromium/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Glue to pass Safe Browsing API requests between Chrome and GMSCore
+// Glue to pass Safe Browsing API requests between Chrome and GMSCore.
#ifndef COMPONENTS_SAFE_BROWSING_ANDROID_SAFE_BROWSING_API_HANDLER_BRIDGE_H_
#define COMPONENTS_SAFE_BROWSING_ANDROID_SAFE_BROWSING_API_HANDLER_BRIDGE_H_
@@ -10,48 +10,63 @@
#include <jni.h>
#include "base/android/jni_android.h"
-#include "components/safe_browsing/android/safe_browsing_api_handler.h"
+#include "base/callback.h"
#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
-#include "url/gurl.h"
+
+class GURL;
namespace safe_browsing {
-class SafeBrowsingApiHandlerBridge : public SafeBrowsingApiHandler {
+class UrlCheckInterceptor;
+struct ThreatMetadata;
+
+class SafeBrowsingApiHandlerBridge {
public:
- SafeBrowsingApiHandlerBridge();
+ using ResponseCallback =
+ base::OnceCallback<void(SBThreatType, const ThreatMetadata&)>;
+
+ SafeBrowsingApiHandlerBridge() = default;
+
+ ~SafeBrowsingApiHandlerBridge();
SafeBrowsingApiHandlerBridge(const SafeBrowsingApiHandlerBridge&) = delete;
SafeBrowsingApiHandlerBridge& operator=(const SafeBrowsingApiHandlerBridge&) =
delete;
- ~SafeBrowsingApiHandlerBridge() override;
+ // Returns a reference to the singleton.
+ static SafeBrowsingApiHandlerBridge& GetInstance();
- // Makes Native->Java call to check the URL against Safe Browsing lists.
- void StartURLCheck(std::unique_ptr<URLCheckCallbackMeta> callback,
+ // Makes Native-to-Java call to check the URL against Safe Browsing lists.
+ void StartURLCheck(std::unique_ptr<ResponseCallback> callback,
const GURL& url,
- const SBThreatTypeSet& threat_types) override;
-
- bool StartCSDAllowlistCheck(const GURL& url) override;
-
- bool StartHighConfidenceAllowlistCheck(const GURL& url) override;
-
- private:
- // Creates the |j_api_handler_| if it hasn't been already. If the API is not
- // supported, this will return false and j_api_handler_ will remain nullptr.
- bool CheckApiIsSupported();
+ const SBThreatTypeSet& threat_types);
- bool StartAllowlistCheck(const GURL& url, const SBThreatType& sb_threat_type);
+ bool StartCSDAllowlistCheck(const GURL& url);
- // The Java-side SafeBrowsingApiHandler. Must call CheckApiIsSupported first.
- base::android::ScopedJavaGlobalRef<jobject> j_api_handler_;
+ bool StartHighConfidenceAllowlistCheck(const GURL& url);
- // True if we've once tried to create the above object.
- bool checked_api_support_;
+ void SetInterceptorForTesting(UrlCheckInterceptor* interceptor) {
+ interceptor_for_testing_ = interceptor;
+ }
+ private:
// Used as a key to identify unique requests sent to Java to get Safe Browsing
// reputation from GmsCore.
jlong next_callback_id_ = 0;
+
+ UrlCheckInterceptor* interceptor_for_testing_ = nullptr;
+};
+
+// Interface allowing simplified interception of calls to
+// SafeBrowsingApiHandlerBridge. Intended for use only in tests.
+class UrlCheckInterceptor {
+ public:
+ virtual ~UrlCheckInterceptor(){};
+ virtual void Check(
+ std::unique_ptr<SafeBrowsingApiHandlerBridge::ResponseCallback> callback,
+ const GURL& url) const = 0;
};
} // namespace safe_browsing
+
#endif // COMPONENTS_SAFE_BROWSING_ANDROID_SAFE_BROWSING_API_HANDLER_BRIDGE_H_
diff --git a/chromium/components/safe_browsing/android/safe_browsing_api_handler_util.h b/chromium/components/safe_browsing/android/safe_browsing_api_handler_util.h
index 884c8eecab5..48eb41b9219 100644
--- a/chromium/components/safe_browsing/android/safe_browsing_api_handler_util.h
+++ b/chromium/components/safe_browsing/android/safe_browsing_api_handler_util.h
@@ -59,10 +59,6 @@ UmaRemoteCallResult ParseJsonFromGMSCore(const std::string& metadata_str,
SBThreatType* worst_threat,
ThreatMetadata* metadata);
-// DEPRECATED. Will be removed.
-UmaRemoteCallResult ParseJsonToThreatAndPB(const std::string& metadata_str,
- SBThreatType* worst_threat,
- std::string* metadata_pb_str);
} // namespace safe_browsing
#endif // COMPONENTS_SAFE_BROWSING_ANDROID_SAFE_BROWSING_API_HANDLER_UTIL_H_
diff --git a/chromium/components/safe_browsing/buildflags.gni b/chromium/components/safe_browsing/buildflags.gni
index 48e74db6c18..ebdc4d3e942 100644
--- a/chromium/components/safe_browsing/buildflags.gni
+++ b/chromium/components/safe_browsing/buildflags.gni
@@ -12,7 +12,7 @@ declare_args() {
# safe browsing feature. Safe browsing can be compiled in 3 different levels:
# 0 disables it, 1 enables it fully, and 2 enables mobile protection via an
# external API.
- if (is_ios || is_chromecast) {
+ if (is_ios || is_castos || is_cast_android) {
safe_browsing_mode = 0
} else if (is_android) {
safe_browsing_mode = 2
diff --git a/chromium/components/safe_browsing/content/browser/BUILD.gn b/chromium/components/safe_browsing/content/browser/BUILD.gn
index 0ff7a602b31..db86826f858 100644
--- a/chromium/components/safe_browsing/content/browser/BUILD.gn
+++ b/chromium/components/safe_browsing/content/browser/BUILD.gn
@@ -48,11 +48,11 @@ if (safe_browsing_mode > 0) {
"//components/no_state_prefetch/browser",
"//components/prefs",
"//components/safe_browsing/content/browser/triggers",
+ "//components/safe_browsing/content/browser/web_ui",
"//components/safe_browsing/core/browser",
"//components/safe_browsing/core/browser:safe_browsing_metrics_collector",
"//components/safe_browsing/core/common",
"//components/safe_browsing/core/common:safe_browsing_prefs",
- "//components/safe_browsing/core/common/proto:csd_proto",
"//components/security_interstitials/content:security_interstitial_page",
"//components/security_interstitials/core",
"//components/security_interstitials/core:unsafe_resource",
diff --git a/chromium/components/safe_browsing/content/browser/DEPS b/chromium/components/safe_browsing/content/browser/DEPS
index 5e1c18dd8dd..1a19a80b0b2 100644
--- a/chromium/components/safe_browsing/content/browser/DEPS
+++ b/chromium/components/safe_browsing/content/browser/DEPS
@@ -16,6 +16,7 @@ include_rules = [
"+content/public/test",
"+crypto/sha2.h",
"+ipc/ipc_message.h",
+ "+ipc/ipc_channel_proxy.h",
"+net/cookies",
"+net/extras",
"+net/http",
diff --git a/chromium/components/safe_browsing/content/browser/base_ui_manager.cc b/chromium/components/safe_browsing/content/browser/base_ui_manager.cc
index c81f75938d6..bee05840ec0 100644
--- a/chromium/components/safe_browsing/content/browser/base_ui_manager.cc
+++ b/chromium/components/safe_browsing/content/browser/base_ui_manager.cc
@@ -315,7 +315,7 @@ void BaseUIManager::DisplayBlockingPage(const UnsafeResource& resource) {
outermost_contents, unsafe_url, resource));
base::WeakPtr<content::NavigationHandle> error_page_navigation_handle =
outermost_contents->GetController().LoadPostCommitErrorPage(
- outermost_contents->GetMainFrame(), unsafe_url,
+ outermost_contents->GetPrimaryMainFrame(), unsafe_url,
blocking_page->GetHTMLContents(), net::ERR_BLOCKED_BY_CLIENT);
if (error_page_navigation_handle) {
blocking_page->CreatedPostCommitErrorPageNavigation(
@@ -357,9 +357,9 @@ void BaseUIManager::MaybeReportSafeBrowsingHit(
// If the user had opted-in to send ThreatDetails, this gets called
// when the report is ready.
-void BaseUIManager::SendSerializedThreatDetails(
+void BaseUIManager::SendThreatDetails(
content::BrowserContext* browser_context,
- const std::string& serialized) {
+ std::unique_ptr<ClientSafeBrowsingReportRequest> report) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return;
}
diff --git a/chromium/components/safe_browsing/content/browser/base_ui_manager.h b/chromium/components/safe_browsing/content/browser/base_ui_manager.h
index 0cc29218bb9..17b1f4a3070 100644
--- a/chromium/components/safe_browsing/content/browser/base_ui_manager.h
+++ b/chromium/components/safe_browsing/content/browser/base_ui_manager.h
@@ -11,6 +11,7 @@
#include "base/callback_helpers.h"
#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "components/security_interstitials/core/unsafe_resource.h"
class GURL;
@@ -49,11 +50,10 @@ class BaseUIManager : public base::RefCountedThreadSafe<BaseUIManager> {
virtual void DisplayBlockingPage(const UnsafeResource& resource);
// This is a no-op in the base class, but should be overridden to send threat
- // details. Called on the UI thread by the ThreatDetails with the serialized
- // protocol buffer.
- virtual void SendSerializedThreatDetails(
+ // details. Called on the UI thread by the ThreatDetails with the report.
+ virtual void SendThreatDetails(
content::BrowserContext* browser_context,
- const std::string& serialized);
+ std::unique_ptr<ClientSafeBrowsingReportRequest> report);
// Updates the allowlist URL set for |web_contents|. Called on the UI thread.
void AddToAllowlistUrlSet(const GURL& allowlist_url,
diff --git a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
index c3d058a9314..144f3c794f6 100644
--- a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
+++ b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
@@ -68,7 +68,6 @@ class BrowserURLLoaderThrottle::CheckerOnIO
std::move(delegate_getter_).Run();
skip_checks_ =
!url_checker_delegate ||
- !url_checker_delegate->GetDatabaseManager()->IsSupported() ||
url_checker_delegate->ShouldSkipRequestCheck(
url, frame_tree_node_id_,
content::ChildProcessHost::kInvalidUniqueID /* render_process_id */,
diff --git a/chromium/components/safe_browsing/content/browser/client_side_detection_host.cc b/chromium/components/safe_browsing/content/browser/client_side_detection_host.cc
index 68ea7b87504..8eaa56e25a0 100644
--- a/chromium/components/safe_browsing/content/browser/client_side_detection_host.cc
+++ b/chromium/components/safe_browsing/content/browser/client_side_detection_host.cc
@@ -45,6 +45,7 @@
#include "net/base/ip_endpoint.h"
#include "net/http/http_response_headers.h"
#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/mojom/loader/referrer.mojom.h"
#include "url/gurl.h"
@@ -384,27 +385,12 @@ ClientSideDetectionHost::ClientSideDetectionHost(
// be null if safe browsing service is not available in the embedder.
ui_manager_ = delegate_->GetSafeBrowsingUIManager();
database_manager_ = delegate_->GetSafeBrowsingDBManager();
-
- // We want to accurately track all active RenderFrameHosts, so make sure we
- // know about any pre-existings ones.
- web_contents()->ForEachRenderFrameHost(base::BindRepeating(
- [](ClientSideDetectionHost* csdh,
- const content::WebContents* web_contents,
- content::RenderFrameHost* rfh) {
- // Don't cross into inner WebContents since we wouldn't be notified of
- // its changes. See https://crbug.com/1308829
- if (content::WebContents::FromRenderFrameHost(rfh) != web_contents) {
- return content::RenderFrameHost::FrameIterationAction::kSkipChildren;
- }
- csdh->InitializePhishingDetector(rfh);
- return content::RenderFrameHost::FrameIterationAction::kContinue;
- },
- base::Unretained(this), web_contents()));
}
ClientSideDetectionHost::~ClientSideDetectionHost() {
- if (csd_service_)
- csd_service_->RemoveClientSideDetectionHost(this);
+ if (classification_request_.get()) {
+ classification_request_->Cancel();
+ }
}
void ClientSideDetectionHost::DidFinishNavigation(
@@ -414,6 +400,9 @@ void ClientSideDetectionHost::DidFinishNavigation(
return;
}
+ if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch))
+ return;
+
// TODO(noelutz): move this DCHECK to WebContents and fix all the unit tests
// that don't call this method on the UI thread.
// DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -452,71 +441,20 @@ void ClientSideDetectionHost::DidFinishNavigation(
classification_request_->Start();
}
-void ClientSideDetectionHost::SetPhishingModel(
- const mojo::Remote<mojom::PhishingDetector>& phishing_detector) {
- switch (csd_service_->GetModelType()) {
- case CSDModelType::kNone:
- case CSDModelType::kProtobuf:
- phishing_detector->SetPhishingModel(
- csd_service_->GetModelStr(),
- csd_service_->GetVisualTfLiteModel().Duplicate());
- return;
- case CSDModelType::kFlatbuffer:
- phishing_detector->SetPhishingFlatBufferModel(
- csd_service_->GetModelSharedMemoryRegion(),
- csd_service_->GetVisualTfLiteModel().Duplicate());
- return;
- }
-}
-
-void ClientSideDetectionHost::SendModelToRenderFrame() {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (!web_contents() || web_contents() != tab_ || !csd_service_)
- return;
-
- for (const auto& frame_and_remote : phishing_detectors_) {
- SetPhishingModel(frame_and_remote.second);
- }
-}
-
-void ClientSideDetectionHost::WebContentsDestroyed() {
- // Tell any pending classification request that it is being canceled.
- if (classification_request_.get()) {
- classification_request_->Cancel();
- }
- if (csd_service_)
- csd_service_->RemoveClientSideDetectionHost(this);
-}
-
-void ClientSideDetectionHost::RenderFrameCreated(
- content::RenderFrameHost* render_frame_host) {
- InitializePhishingDetector(render_frame_host);
-}
-
-void ClientSideDetectionHost::RenderFrameDeleted(
- content::RenderFrameHost* render_frame_host) {
- ClearPhishingDetector(render_frame_host->GetGlobalId());
-}
-
void ClientSideDetectionHost::OnPhishingPreClassificationDone(
bool should_classify) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (should_classify) {
- content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
- auto it = phishing_detectors_.find(rfh->GetGlobalId());
- bool remote_valid =
- (it != phishing_detectors_.end() && it->second.is_connected());
-
- base::UmaHistogramBoolean("SBClientPhishing.MainFrameRemoteExists",
- it != phishing_detectors_.end());
- if (it != phishing_detectors_.end()) {
- base::UmaHistogramBoolean("SBClientPhishing.MainFrameRemoteConnected",
- it->second.is_connected());
- }
+ content::RenderFrameHost* rfh = web_contents()->GetPrimaryMainFrame();
+
+ phishing_detector_.reset();
+ rfh->GetRemoteAssociatedInterfaces()->GetInterface(&phishing_detector_);
+ base::UmaHistogramBoolean("SBClientPhishing.MainFrameRemoteConnected",
+ phishing_detector_.is_bound());
- if (remote_valid) {
+ if (phishing_detector_.is_bound()) {
phishing_detection_start_time_ = tick_clock_->NowTicks();
- it->second->StartPhishingDetection(
+ phishing_detector_->StartPhishingDetection(
current_url_,
base::BindOnce(&ClientSideDetectionHost::PhishingDetectionDone,
weak_factory_.GetWeakPtr()));
@@ -533,6 +471,8 @@ void ClientSideDetectionHost::PhishingDetectionDone(
// if there isn't any service class in the browser.
DCHECK(csd_service_);
+ phishing_detector_.reset();
+
UmaHistogramMediumTimes(
"SBClientPhishing.PhishingDetectionDuration",
base::TimeTicks::Now() - phishing_detection_start_time_);
@@ -632,7 +572,7 @@ void ClientSideDetectionHost::MaybeShowPhishingWarning(bool is_from_cache,
DCHECK(web_contents());
if (ui_manager_.get()) {
const content::GlobalRenderFrameHostId primary_main_frame_id =
- web_contents()->GetMainFrame()->GetGlobalId();
+ web_contents()->GetPrimaryMainFrame()->GetGlobalId();
security_interstitials::UnsafeResource resource;
resource.url = phishing_url;
@@ -676,27 +616,6 @@ void ClientSideDetectionHost::OnGotAccessToken(
ClientSideDetectionHost::SendRequest(std::move(verdict), access_token);
}
-void ClientSideDetectionHost::InitializePhishingDetector(
- content::RenderFrameHost* render_frame_host) {
- if (render_frame_host->IsRenderFrameCreated()) {
- const content::GlobalRenderFrameHostId rfh_id =
- render_frame_host->GetGlobalId();
- mojo::Remote<mojom::PhishingDetector> new_detector;
- render_frame_host->GetRemoteInterfaces()->GetInterface(
- new_detector.BindNewPipeAndPassReceiver());
- new_detector.set_disconnect_handler(
- base::BindOnce(&ClientSideDetectionHost::ClearPhishingDetector,
- weak_factory_.GetWeakPtr(), rfh_id));
- phishing_detectors_[rfh_id] = std::move(new_detector);
- SetPhishingModel(phishing_detectors_[rfh_id]);
- }
-}
-
-void ClientSideDetectionHost::ClearPhishingDetector(
- content::GlobalRenderFrameHostId rfh_id) {
- phishing_detectors_.erase(rfh_id);
-}
-
bool ClientSideDetectionHost::CanGetAccessToken() {
if (is_off_the_record_)
return false;
diff --git a/chromium/components/safe_browsing/content/browser/client_side_detection_host.h b/chromium/components/safe_browsing/content/browser/client_side_detection_host.h
index 30c50c3b162..b91d1f3350c 100644
--- a/chromium/components/safe_browsing/content/browser/client_side_detection_host.h
+++ b/chromium/components/safe_browsing/content/browser/client_side_detection_host.h
@@ -23,6 +23,7 @@
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents_observer.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "url/gurl.h"
@@ -39,7 +40,6 @@ class ClientSideDetectionService;
// notifies the browser that a URL was classified as phishing. This
// class relays this information to the client-side detection service
// class which sends a ping to a server to validate the verdict.
-// TODO(noelutz): move all client-side detection IPCs to this class.
class ClientSideDetectionHost : public content::WebContentsObserver {
public:
// A callback via which the client of this component indicates whether the
@@ -82,8 +82,6 @@ class ClientSideDetectionHost : public content::WebContentsObserver {
ClientSideDetectionHost(const ClientSideDetectionHost&) = delete;
ClientSideDetectionHost& operator=(const ClientSideDetectionHost&) = delete;
- // The caller keeps ownership of the tab object and is responsible for
- // ensuring that it stays valid until WebContentsDestroyed is called.
~ClientSideDetectionHost() override;
// From content::WebContentsObserver. If we navigate away we cancel all
@@ -92,9 +90,6 @@ class ClientSideDetectionHost : public content::WebContentsObserver {
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
- // Send the model to all the render frame hosts in this WebContents.
- void SendModelToRenderFrame();
-
protected:
explicit ClientSideDetectionHost(
content::WebContents* tab,
@@ -104,11 +99,6 @@ class ClientSideDetectionHost : public content::WebContentsObserver {
bool is_off_the_record,
const PrimaryAccountSignedIn& account_signed_in_callback);
- // From content::WebContentsObserver.
- void WebContentsDestroyed() override;
- void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
- void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
-
// Used for testing.
void set_ui_manager(BaseUIManager* ui_manager);
void set_database_manager(SafeBrowsingDatabaseManager* database_manager);
@@ -172,10 +162,6 @@ class ClientSideDetectionHost : public content::WebContentsObserver {
// users, who are signed in and not in incognito mode.
bool CanGetAccessToken();
- // Set phishing model in PhishingDetector in renderers.
- void SetPhishingModel(
- const mojo::Remote<mojom::PhishingDetector>& phishing_detector);
-
// Send the client report to CSD server.
void SendRequest(std::unique_ptr<ClientPhishingRequest> verdict,
const std::string& access_token);
@@ -184,11 +170,6 @@ class ClientSideDetectionHost : public content::WebContentsObserver {
void OnGotAccessToken(std::unique_ptr<ClientPhishingRequest> verdict,
const std::string& access_token);
- // Setup a PhishingDetector Mojo connection for the given render frame.
- void InitializePhishingDetector(content::RenderFrameHost* render_frame_host);
-
- void ClearPhishingDetector(content::GlobalRenderFrameHostId rfh_id);
-
// This pointer may be nullptr if client-side phishing detection is
// disabled.
raw_ptr<ClientSideDetectionService> csd_service_;
@@ -199,17 +180,12 @@ class ClientSideDetectionHost : public content::WebContentsObserver {
scoped_refptr<BaseUIManager> ui_manager_;
// Keep a handle to the latest classification request so that we can cancel
// it if necessary.
+ // TODO(drubery): Make this a std::unique_ptr, for clearer lifetimes.
scoped_refptr<ShouldClassifyUrlRequest> classification_request_;
// The current URL
GURL current_url_;
// The current outermost main frame's id.
content::GlobalRenderFrameHostId current_outermost_main_frame_id_;
- // A map from the live RenderFrameHosts to their PhishingDetector. These
- // correspond to the `phishing_detector_receiver_` in the
- // PhishingClassifierDelegate.
- base::flat_map<content::GlobalRenderFrameHostId,
- mojo::Remote<mojom::PhishingDetector>>
- phishing_detectors_;
// Records the start time of when phishing detection started.
base::TimeTicks phishing_detection_start_time_;
@@ -231,6 +207,9 @@ class ClientSideDetectionHost : public content::WebContentsObserver {
// acces_token.
PrimaryAccountSignedIn account_signed_in_callback_;
+ // The remote for the currently active phishing classification.
+ mojo::AssociatedRemote<mojom::PhishingDetector> phishing_detector_;
+
base::WeakPtrFactory<ClientSideDetectionHost> weak_factory_{this};
};
diff --git a/chromium/components/safe_browsing/content/browser/client_side_detection_service.cc b/chromium/components/safe_browsing/content/browser/client_side_detection_service.cc
index 606feb3a25a..2ae8d3fd695 100644
--- a/chromium/components/safe_browsing/content/browser/client_side_detection_service.cc
+++ b/chromium/components/safe_browsing/content/browser/client_side_detection_service.cc
@@ -15,6 +15,7 @@
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
+#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -37,8 +38,9 @@
#include "content/public/browser/render_process_host.h"
#include "crypto/sha2.h"
#include "google_apis/google_api_keys.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
-#include "net/base/escape.h"
#include "net/base/ip_address.h"
#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
@@ -164,19 +166,6 @@ bool ClientSideDetectionService::IsLocalResource(
return !address.IsValid();
}
-void ClientSideDetectionService::AddClientSideDetectionHost(
- ClientSideDetectionHost* host) {
- csd_hosts_.push_back(host);
-}
-
-void ClientSideDetectionService::RemoveClientSideDetectionHost(
- ClientSideDetectionHost* host) {
- std::vector<ClientSideDetectionHost*>::iterator position =
- std::find(csd_hosts_.begin(), csd_hosts_.end(), host);
- if (position != csd_hosts_.end())
- csd_hosts_.erase(position);
-}
-
void ClientSideDetectionService::OnURLLoaderComplete(
network::SimpleURLLoader* url_loader,
base::Time start_time,
@@ -199,8 +188,10 @@ void ClientSideDetectionService::OnURLLoaderComplete(
}
void ClientSideDetectionService::SendModelToRenderers() {
- for (ClientSideDetectionHost* host : csd_hosts_) {
- host->SendModelToRenderFrame();
+ for (content::RenderProcessHost::iterator it(
+ content::RenderProcessHost::AllHostsIterator());
+ !it.IsAtEnd(); it.Advance()) {
+ SetPhishingModel(it.GetCurrentValue());
}
}
@@ -429,7 +420,7 @@ GURL ClientSideDetectionService::GetClientReportUrl(
GURL url(report_url);
std::string api_key = google_apis::GetAPIKey();
if (!api_key.empty())
- url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
+ url = url.Resolve("?key=" + base::EscapeQueryParamValue(api_key, true));
return url;
}
@@ -456,4 +447,29 @@ void ClientSideDetectionService::SetURLLoaderFactoryForTesting(
url_loader_factory_ = url_loader_factory;
}
+void ClientSideDetectionService::OnRenderProcessHostCreated(
+ content::RenderProcessHost* rph) {
+ SetPhishingModel(rph);
+}
+
+void ClientSideDetectionService::SetPhishingModel(
+ content::RenderProcessHost* rph) {
+ if (!rph->GetChannel())
+ return;
+ mojo::AssociatedRemote<mojom::PhishingModelSetter> model_setter;
+ rph->GetChannel()->GetRemoteAssociatedInterface(&model_setter);
+ switch (GetModelType()) {
+ case CSDModelType::kNone:
+ return;
+ case CSDModelType::kProtobuf:
+ model_setter->SetPhishingModel(GetModelStr(),
+ GetVisualTfLiteModel().Duplicate());
+ return;
+ case CSDModelType::kFlatbuffer:
+ model_setter->SetPhishingFlatBufferModel(
+ GetModelSharedMemoryRegion(), GetVisualTfLiteModel().Duplicate());
+ return;
+ }
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/browser/client_side_detection_service.h b/chromium/components/safe_browsing/content/browser/client_side_detection_service.h
index d08877f2f7b..3d7250ca3af 100644
--- a/chromium/components/safe_browsing/content/browser/client_side_detection_service.h
+++ b/chromium/components/safe_browsing/content/browser/client_side_detection_service.h
@@ -34,6 +34,7 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/render_process_host_creation_observer.h"
#include "net/base/ip_address.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/gurl.h"
@@ -49,7 +50,9 @@ class ClientSideDetectionHost;
// Main service which pushes models to the renderers, responds to classification
// requests. This owns two ModelLoader objects.
-class ClientSideDetectionService : public KeyedService {
+class ClientSideDetectionService
+ : public KeyedService,
+ public content::RenderProcessHostCreationObserver {
public:
// void(GURL phishing_url, bool is_phishing).
typedef base::OnceCallback<void(GURL, bool)>
@@ -86,9 +89,6 @@ class ClientSideDetectionService : public KeyedService {
return enabled_;
}
- void AddClientSideDetectionHost(ClientSideDetectionHost* host);
- void RemoveClientSideDetectionHost(ClientSideDetectionHost* host);
-
void OnURLLoaderComplete(network::SimpleURLLoader* url_loader,
base::Time start_time,
std::unique_ptr<std::string> response_body);
@@ -153,6 +153,9 @@ class ClientSideDetectionService : public KeyedService {
void SetURLLoaderFactoryForTesting(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+ // Sends a model to each renderer.
+ void SetPhishingModel(content::RenderProcessHost* rph);
+
private:
friend class ClientSideDetectionServiceTest;
FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionServiceTest,
@@ -219,6 +222,9 @@ class ClientSideDetectionService : public KeyedService {
// Returns the URL that will be used for phishing requests.
static GURL GetClientReportUrl(const std::string& report_url);
+ // content::RenderProcessHostCreationObserver:
+ void OnRenderProcessHostCreated(content::RenderProcessHost* rph) override;
+
// Whether the service is running or not. When the service is not running,
// it won't download the model nor report detected phishing URLs.
bool enabled_ = false;
@@ -252,8 +258,6 @@ class ClientSideDetectionService : public KeyedService {
// PrefChangeRegistrar used to track when the Safe Browsing pref changes.
PrefChangeRegistrar pref_change_registrar_;
- std::vector<ClientSideDetectionHost*> csd_hosts_;
-
std::unique_ptr<Delegate> delegate_;
base::CallbackListSubscription update_model_subscription_;
diff --git a/chromium/components/safe_browsing/content/browser/client_side_phishing_model.cc b/chromium/components/safe_browsing/content/browser/client_side_phishing_model.cc
index a911a32fedc..3a2e1b890f1 100644
--- a/chromium/components/safe_browsing/content/browser/client_side_phishing_model.cc
+++ b/chromium/components/safe_browsing/content/browser/client_side_phishing_model.cc
@@ -31,19 +31,46 @@ namespace {
// provided with an absolute path.
const char kOverrideCsdModelFlag[] = "csd-model-override-path";
-std::string ReadFileIntoString(base::FilePath path) {
- if (path.empty())
- return std::string();
+void ReturnModelOverrideFailure(
+ base::OnceCallback<void(std::pair<std::string, base::File>)> callback) {
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback),
+ std::make_pair(std::string(), base::File())));
+}
- base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
- if (!file.IsValid())
- return std::string();
+void ReadOverridenModel(
+ base::FilePath path,
+ base::OnceCallback<void(std::pair<std::string, base::File>)> callback) {
+ if (path.empty()) {
+ VLOG(2) << "Failed to override model. Path is empty.";
+ ReturnModelOverrideFailure(std::move(callback));
+ return;
+ }
- std::vector<char> model_data(file.GetLength());
- if (file.ReadAtCurrentPos(model_data.data(), model_data.size()) == -1)
- return std::string();
+ base::File model(path.AppendASCII("client_model.pb"),
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
+ base::File tflite_model(path.AppendASCII("visual_model.tflite"),
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
+ // `tflite_model` is allowed to be invalid, when testing a DOM-only model.
+ if (!model.IsValid()) {
+ VLOG(2) << "Failed to override model. Could not open: "
+ << path.AppendASCII("client_model.pb");
+ ReturnModelOverrideFailure(std::move(callback));
+ return;
+ }
+
+ std::vector<char> model_data(model.GetLength());
+ if (model.ReadAtCurrentPos(model_data.data(), model_data.size()) == -1) {
+ VLOG(2) << "Failed to override model. Could not read model data.";
+ ReturnModelOverrideFailure(std::move(callback));
+ return;
+ }
- return std::string(model_data.begin(), model_data.end());
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback),
+ std::make_pair(std::string(model_data.begin(),
+ model_data.end()),
+ std::move(tflite_model))));
}
} // namespace
@@ -205,28 +232,38 @@ void* ClientSidePhishingModel::GetFlatBufferMemoryAddressForTesting() {
return mapped_region_.mapping.memory();
}
+void ClientSidePhishingModel::NotifyCallbacksOfUpdateForTesting() {
+ // base::Unretained is safe because this is a singleton.
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&ClientSidePhishingModel::NotifyCallbacksOnUI,
+ base::Unretained(this)));
+}
+
void ClientSidePhishingModel::MaybeOverrideModel() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
kOverrideCsdModelFlag)) {
- base::FilePath overriden_model_path =
+ base::FilePath overriden_model_directory =
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
kOverrideCsdModelFlag);
CSDModelType model_type =
base::FeatureList::IsEnabled(kClientSideDetectionModelIsFlatBuffer)
? CSDModelType::kFlatbuffer
: CSDModelType::kProtobuf;
- base::ThreadPool::PostTaskAndReplyWithResult(
+ base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock()},
- base::BindOnce(&ReadFileIntoString, overriden_model_path),
- // base::Unretained is safe because this is a singleton.
- base::BindOnce(&ClientSidePhishingModel::OnGetOverridenModelData,
- base::Unretained(this), model_type));
+ base::BindOnce(
+ &ReadOverridenModel, overriden_model_directory,
+ // base::Unretained is safe because this is a singleton.
+ base::BindOnce(&ClientSidePhishingModel::OnGetOverridenModelData,
+ base::Unretained(this), model_type)));
}
}
void ClientSidePhishingModel::OnGetOverridenModelData(
CSDModelType model_type,
- const std::string& model_data) {
+ std::pair<std::string, base::File> model_and_tflite) {
+ const std::string& model_data = model_and_tflite.first;
+ base::File tflite_model = std::move(model_and_tflite.second);
if (model_data.empty()) {
VLOG(2) << "Overriden model data is empty";
return;
@@ -269,6 +306,10 @@ void ClientSidePhishingModel::OnGetOverridenModelData(
return;
}
+ if (tflite_model.IsValid()) {
+ visual_tflite_model_ = std::move(tflite_model);
+ }
+
VLOG(2) << "Model overriden successfully";
// Unretained is safe because this is a singleton.
diff --git a/chromium/components/safe_browsing/content/browser/client_side_phishing_model.h b/chromium/components/safe_browsing/content/browser/client_side_phishing_model.h
index cd38d5296cb..747a05f1f38 100644
--- a/chromium/components/safe_browsing/content/browser/client_side_phishing_model.h
+++ b/chromium/components/safe_browsing/content/browser/client_side_phishing_model.h
@@ -68,6 +68,8 @@ class ClientSidePhishingModel {
void ClearMappedRegionForTesting();
// Get flatbuffer memory address.
void* GetFlatBufferMemoryAddressForTesting();
+ // Notifies all the callbacks of a change in model.
+ void NotifyCallbacksOfUpdateForTesting();
// Called to check the command line and maybe override the current model.
void MaybeOverrideModel();
@@ -80,8 +82,9 @@ class ClientSidePhishingModel {
void NotifyCallbacksOnUI();
// Callback when the local file overriding the model has been read.
- void OnGetOverridenModelData(CSDModelType model_type,
- const std::string& model_data);
+ void OnGetOverridenModelData(
+ CSDModelType model_type,
+ std::pair<std::string, base::File> model_and_tflite);
// The list of callbacks to notify when a new model is ready. Protected by
// lock_. Will always be notified on the UI thread.
diff --git a/chromium/components/safe_browsing/content/browser/client_side_phishing_model_unittest.cc b/chromium/components/safe_browsing/content/browser/client_side_phishing_model_unittest.cc
index e91f3f7b991..783935c09ef 100644
--- a/chromium/components/safe_browsing/content/browser/client_side_phishing_model_unittest.cc
+++ b/chromium/components/safe_browsing/content/browser/client_side_phishing_model_unittest.cc
@@ -214,11 +214,10 @@ TEST(ClientSidePhishingModelTest, CanOverrideProtoWithFlag) {
/*disabled_features=*/{kClientSideDetectionModelIsFlatBuffer});
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- const base::FilePath file_path =
- temp_dir.GetPath().AppendASCII("overridden_model.proto");
- base::File file(file_path, base::File::FLAG_OPEN_ALWAYS |
- base::File::FLAG_READ |
- base::File::FLAG_WRITE);
+ const base::FilePath file_path = temp_dir.GetPath();
+ base::File file(file_path.AppendASCII("client_model.pb"),
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
+ base::File::FLAG_WRITE);
ClientSideModel model_proto;
model_proto.set_version(123);
model_proto.set_max_words_per_term(0); // Required field
@@ -260,11 +259,10 @@ TEST(ClientSidePhishingModelTest, CanOverrideFlatBufferWithFlag) {
/*disabled_features=*/{});
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- const base::FilePath file_path =
- temp_dir.GetPath().AppendASCII("overridden_model.fb");
- base::File file(file_path, base::File::FLAG_OPEN_ALWAYS |
- base::File::FLAG_READ |
- base::File::FLAG_WRITE);
+ const base::FilePath file_path = temp_dir.GetPath();
+ base::File file(file_path.AppendASCII("client_model.pb"),
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
+ base::File::FLAG_WRITE);
const std::string file_contents = CreateFlatBufferString();
file.WriteAtCurrentPos(file_contents.data(), file_contents.size());
diff --git a/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc b/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
index 488bef94b27..057a5c433c7 100644
--- a/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
+++ b/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
@@ -108,8 +108,7 @@ void MojoSafeBrowsingImpl::MaybeCreate(
scoped_refptr<UrlCheckerDelegate> delegate = delegate_getter.Run();
- if (!resource_context || !delegate ||
- !delegate->GetDatabaseManager()->IsSupported())
+ if (!resource_context || !delegate)
return;
std::unique_ptr<MojoSafeBrowsingImpl> impl(new MojoSafeBrowsingImpl(
diff --git a/chromium/components/safe_browsing/content/browser/password_protection/password_protection_request_content.cc b/chromium/components/safe_browsing/content/browser/password_protection/password_protection_request_content.cc
index a5faf91fe56..b5b35ad4bcc 100644
--- a/chromium/components/safe_browsing/content/browser/password_protection/password_protection_request_content.cc
+++ b/chromium/components/safe_browsing/content/browser/password_protection/password_protection_request_content.cc
@@ -127,7 +127,7 @@ bool PasswordProtectionRequestContent::IsVisualFeaturesEnabled() {
}
void PasswordProtectionRequestContent::GetDomFeatures() {
- content::RenderFrameHost* rfh = web_contents_->GetMainFrame();
+ content::RenderFrameHost* rfh = web_contents_->GetPrimaryMainFrame();
PasswordProtectionService* service =
static_cast<PasswordProtectionService*>(password_protection_service());
service->GetPhishingDetector(rfh->GetRemoteInterfaces(), &phishing_detector_);
diff --git a/chromium/components/safe_browsing/content/browser/password_protection/password_protection_service.cc b/chromium/components/safe_browsing/content/browser/password_protection/password_protection_service.cc
index d0fcc4f40b3..7e593038f7f 100644
--- a/chromium/components/safe_browsing/content/browser/password_protection/password_protection_service.cc
+++ b/chromium/components/safe_browsing/content/browser/password_protection/password_protection_service.cc
@@ -11,6 +11,7 @@
#include <string>
#include "base/metrics/histogram_macros.h"
+#include "base/strings/escape.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_reuse_detector.h"
#include "components/safe_browsing/content/browser/password_protection/password_protection_commit_deferring_condition.h"
@@ -21,7 +22,6 @@
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
#include "net/base/url_util.h"
#include "third_party/blink/public/common/page/page_zoom.h"
diff --git a/chromium/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc b/chromium/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
index d4ac1cde92d..42dac9e9949 100644
--- a/chromium/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
+++ b/chromium/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
@@ -136,11 +136,6 @@ class TestPhishingDetector : public mojom::PhishingDetector {
mojo::PendingReceiver<mojom::PhishingDetector>(std::move(handle)));
}
- void SetPhishingModel(const std::string& model, base::File file) override {}
-
- void SetPhishingFlatBufferModel(base::ReadOnlySharedMemoryRegion region,
- base::File file) override {}
-
void StartPhishingDetection(
const GURL& url,
StartPhishingDetectionCallback callback) override {
diff --git a/chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.cc b/chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.cc
index 1d4c5f0e1ec..de195ccda6a 100644
--- a/chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.cc
+++ b/chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.cc
@@ -53,8 +53,6 @@ SafeBrowsingBlockingPage::SafeBrowsingBlockingPage(
const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options,
bool should_trigger_reporting,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
SafeBrowsingNavigationObserverManager* navigation_observer_manager,
SafeBrowsingMetricsCollector* metrics_collector,
TriggerManager* trigger_manager,
@@ -68,7 +66,6 @@ SafeBrowsingBlockingPage::SafeBrowsingBlockingPage(
threat_details_in_progress_(false),
threat_source_(unsafe_resources[0].threat_source),
history_service_(history_service),
- get_user_population_callback_(get_user_population_callback),
navigation_observer_manager_(navigation_observer_manager),
metrics_collector_(metrics_collector),
trigger_manager_(trigger_manager) {
@@ -103,7 +100,7 @@ SafeBrowsingBlockingPage::SafeBrowsingBlockingPage(
trigger_manager_->StartCollectingThreatDetails(
TriggerType::SECURITY_INTERSTITIAL, web_contents,
unsafe_resources[0], url_loader_factory, history_service_,
- get_user_population_callback_, navigation_observer_manager_,
+ navigation_observer_manager_,
sb_error_ui()->get_error_display_options());
}
}
diff --git a/chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.h b/chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.h
index e9732302626..644b7bb79fb 100644
--- a/chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.h
+++ b/chromium/components/safe_browsing/content/browser/safe_browsing_blocking_page.h
@@ -34,7 +34,6 @@
#include "base/memory/raw_ptr.h"
#include "components/safe_browsing/content/browser/base_blocking_page.h"
#include "components/safe_browsing/content/browser/base_ui_manager.h"
-#include "components/safe_browsing/core/common/proto/csd.pb.h"
namespace history {
class HistoryService;
@@ -112,8 +111,6 @@ class SafeBrowsingBlockingPage : public BaseBlockingPage {
const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options,
bool should_trigger_reporting,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
SafeBrowsingNavigationObserverManager* navigation_observer_manager,
SafeBrowsingMetricsCollector* metrics_collector,
TriggerManager* trigger_manager,
@@ -140,7 +137,6 @@ class SafeBrowsingBlockingPage : public BaseBlockingPage {
private:
raw_ptr<history::HistoryService> history_service_ = nullptr;
- base::RepeatingCallback<ChromeUserPopulation()> get_user_population_callback_;
raw_ptr<SafeBrowsingNavigationObserverManager> navigation_observer_manager_ =
nullptr;
raw_ptr<SafeBrowsingMetricsCollector> metrics_collector_ = nullptr;
diff --git a/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer.cc b/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer.cc
index 422a2e7fe02..87dd58cc613 100644
--- a/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer.cc
+++ b/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer.cc
@@ -322,7 +322,7 @@ void SafeBrowsingNavigationObserver::SetNavigationSourceUrl(
// frame.
nav_event->source_url = SafeBrowsingNavigationObserverManager::ClearURLRef(
navigation_handle->GetWebContents()
- ->GetMainFrame()
+ ->GetPrimaryMainFrame()
->GetLastCommittedURL());
} else {
// If there was a URL previously committed in the current RenderFrameHost,
diff --git a/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.cc b/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.cc
index df4b069915d..88ed4b4a12c 100644
--- a/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.cc
+++ b/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.cc
@@ -687,9 +687,10 @@ void SafeBrowsingNavigationObserverManager::RecordNewWebContents(
nav_event->initiator_outermost_main_frame_id =
source_render_frame_host->GetOutermostMainFrame()->GetGlobalId();
}
- nav_event->outermost_main_frame_id = target_web_contents->GetMainFrame()
- ->GetOutermostMainFrame()
- ->GetGlobalId();
+ nav_event->outermost_main_frame_id =
+ target_web_contents->GetPrimaryMainFrame()
+ ->GetOutermostMainFrame()
+ ->GetGlobalId();
}
nav_event->source_tab_id =
diff --git a/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_unittest.cc b/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_unittest.cc
index ae9d150041d..04f0d36ee05 100644
--- a/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_unittest.cc
+++ b/chromium/components/safe_browsing/content/browser/safe_browsing_navigation_observer_unittest.cc
@@ -375,7 +375,7 @@ TEST_F(SBNavigationObserverTest, BasicNavigationAndCommit) {
TEST_F(SBNavigationObserverTest, ServerRedirect) {
auto navigation = content::NavigationSimulator::CreateRendererInitiated(
- GURL("http://foo/3"), web_contents()->GetMainFrame());
+ GURL("http://foo/3"), web_contents()->GetPrimaryMainFrame());
auto* nav_list = navigation_event_list();
SessionID tab_id = sessions::SessionTabHelper::IdForTab(web_contents());
@@ -446,8 +446,10 @@ TEST_F(SBNavigationObserverTest, TestCleanUpStaleNavigationEvents) {
base::Time::FromDoubleT(now.ToDoubleT() + 60.0 * 60.0); // Invalid
GURL url_0("http://foo/0");
GURL url_1("http://foo/1");
- content::MockNavigationHandle handle_0(url_0, web_contents()->GetMainFrame());
- content::MockNavigationHandle handle_1(url_1, web_contents()->GetMainFrame());
+ content::MockNavigationHandle handle_0(url_0,
+ web_contents()->GetPrimaryMainFrame());
+ content::MockNavigationHandle handle_1(url_1,
+ web_contents()->GetPrimaryMainFrame());
navigation_event_list()->RecordNavigationEvent(
CreateNavigationEventUniquePtr(url_0, in_an_hour));
navigation_event_list()->RecordNavigationEvent(
@@ -875,9 +877,12 @@ TEST_F(SBNavigationObserverTest, TestGetLatestPendingNavigationEvent) {
base::Time one_minute_ago = base::Time::FromDoubleT(now.ToDoubleT() - 60.0);
base::Time two_minute_ago = base::Time::FromDoubleT(now.ToDoubleT() - 120.0);
GURL url("http://foo/0");
- content::MockNavigationHandle handle_0(url, web_contents()->GetMainFrame());
- content::MockNavigationHandle handle_1(url, web_contents()->GetMainFrame());
- content::MockNavigationHandle handle_2(url, web_contents()->GetMainFrame());
+ content::MockNavigationHandle handle_0(url,
+ web_contents()->GetPrimaryMainFrame());
+ content::MockNavigationHandle handle_1(url,
+ web_contents()->GetPrimaryMainFrame());
+ content::MockNavigationHandle handle_2(url,
+ web_contents()->GetPrimaryMainFrame());
navigation_event_list()->RecordPendingNavigationEvent(
&handle_0, CreateNavigationEventUniquePtr(url, one_minute_ago));
navigation_event_list()->RecordPendingNavigationEvent(
diff --git a/chromium/components/safe_browsing/content/browser/safe_browsing_network_context.cc b/chromium/components/safe_browsing/content/browser/safe_browsing_network_context.cc
index 43daf77b1c4..d54a8f6f932 100644
--- a/chromium/components/safe_browsing/content/browser/safe_browsing_network_context.cc
+++ b/chromium/components/safe_browsing/content/browser/safe_browsing_network_context.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/files/file_util.h"
+#include "base/trace_event/trace_event.h"
#include "components/safe_browsing/core/common/safebrowsing_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_context_client_base.h"
@@ -21,6 +22,10 @@
#include "services/network/network_context.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/remove_stale_data.h"
+#endif
+
namespace safe_browsing {
class SafeBrowsingNetworkContext::SharedURLLoaderFactory
@@ -115,6 +120,8 @@ class SafeBrowsingNetworkContext::SharedURLLoaderFactory
~SharedURLLoaderFactory() override = default;
network::mojom::NetworkContextParamsPtr CreateNetworkContextParams() {
+ TRACE_EVENT0("startup",
+ "SafeBrowsingNetworkContext::CreateNetworkContextParams");
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
network::mojom::NetworkContextParamsPtr network_context_params =
network_context_params_factory_.Run();
@@ -132,6 +139,18 @@ class SafeBrowsingNetworkContext::SharedURLLoaderFactory
base::FilePath::StringType(kSafeBrowsingBaseFilename) + kCookiesFile);
network_context_params->enable_encrypted_cookies = false;
+#if BUILDFLAG(IS_ANDROID)
+ // On Android the `data_directory` was used by some wrong builds instead of
+ // `unsandboxed_data_path`. Cleaning it up. See crbug.com/1331809.
+ // The `cookie_manager` is set by WebView, where the mistaken migration did
+ // not happen.
+ DCHECK(!trigger_migration_);
+ if (!network_context_params->cookie_manager) {
+ base::android::RemoveStaleDataDirectory(
+ network_context_params->file_paths->data_directory.path());
+ }
+#endif // BUILDFLAG(IS_ANDROID)
+
return network_context_params;
}
diff --git a/chromium/components/safe_browsing/content/browser/safe_browsing_tab_observer.cc b/chromium/components/safe_browsing/content/browser/safe_browsing_tab_observer.cc
index 0cd12512dcb..13b36f0f943 100644
--- a/chromium/components/safe_browsing/content/browser/safe_browsing_tab_observer.cc
+++ b/chromium/components/safe_browsing/content/browser/safe_browsing_tab_observer.cc
@@ -47,8 +47,6 @@ SafeBrowsingTabObserver::SafeBrowsingTabObserver(
delegate_->DoesSafeBrowsingServiceExist() && csd_service) {
safebrowsing_detection_host_ =
delegate_->CreateClientSideDetectionHost(web_contents);
- csd_service->AddClientSideDetectionHost(
- safebrowsing_detection_host_.get());
}
}
#endif
@@ -70,8 +68,6 @@ void SafeBrowsingTabObserver::UpdateSafebrowsingDetectionHost() {
if (!safebrowsing_detection_host_.get()) {
safebrowsing_detection_host_ =
delegate_->CreateClientSideDetectionHost(&GetWebContents());
- csd_service->AddClientSideDetectionHost(
- safebrowsing_detection_host_.get());
}
} else {
safebrowsing_detection_host_.reset();
diff --git a/chromium/components/safe_browsing/content/browser/threat_details.cc b/chromium/components/safe_browsing/content/browser/threat_details.cc
index 2c5336108bf..0d18b38e54b 100644
--- a/chromium/components/safe_browsing/content/browser/threat_details.cc
+++ b/chromium/components/safe_browsing/content/browser/threat_details.cc
@@ -328,8 +328,6 @@ class ThreatDetailsFactoryImpl : public ThreatDetailsFactory {
const security_interstitials::UnsafeResource& unsafe_resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
bool trim_to_ad_tags,
ThreatDetailsDoneCallback done_callback) override {
@@ -338,8 +336,8 @@ class ThreatDetailsFactoryImpl : public ThreatDetailsFactory {
// used.
auto threat_details = base::WrapUnique(new ThreatDetails(
ui_manager, web_contents, unsafe_resource, url_loader_factory,
- history_service, get_user_population_callback, referrer_chain_provider,
- trim_to_ad_tags, std::move(done_callback)));
+ history_service, referrer_chain_provider, trim_to_ad_tags,
+ std::move(done_callback)));
threat_details->StartCollection();
return threat_details;
}
@@ -361,8 +359,6 @@ std::unique_ptr<ThreatDetails> ThreatDetails::NewThreatDetails(
const UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
bool trim_to_ad_tags,
ThreatDetailsDoneCallback done_callback) {
@@ -372,8 +368,7 @@ std::unique_ptr<ThreatDetails> ThreatDetails::NewThreatDetails(
factory_ = g_threat_details_factory_impl.Pointer();
return factory_->CreateThreatDetails(
ui_manager, web_contents, resource, url_loader_factory, history_service,
- get_user_population_callback, referrer_chain_provider, trim_to_ad_tags,
- std::move(done_callback));
+ referrer_chain_provider, trim_to_ad_tags, std::move(done_callback));
}
// Create a ThreatDetails for the given tab. Runs in the UI thread.
@@ -383,8 +378,6 @@ ThreatDetails::ThreatDetails(
const UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
bool trim_to_ad_tags,
ThreatDetailsDoneCallback done_callback)
@@ -393,7 +386,6 @@ ThreatDetails::ThreatDetails(
ui_manager_(ui_manager),
browser_context_(web_contents->GetBrowserContext()),
resource_(resource),
- get_user_population_callback_(get_user_population_callback),
referrer_chain_provider_(referrer_chain_provider),
cache_result_(false),
did_proceed_(false),
@@ -643,8 +635,9 @@ void ThreatDetails::StartCollection() {
// OnReceivedThreatDOMDetails will be called when the renderer replies.
// TODO(mattm): In theory, if the user proceeds through the warning DOM
// detail collection could be started once the page loads.
- web_contents_->GetMainFrame()->ForEachRenderFrameHost(base::BindRepeating(
- &ThreatDetails::RequestThreatDOMDetails, GetWeakPtr()));
+ web_contents_->GetPrimaryMainFrame()->ForEachRenderFrameHost(
+ base::BindRepeating(&ThreatDetails::RequestThreatDOMDetails,
+ GetWeakPtr()));
}
}
@@ -653,7 +646,7 @@ void ThreatDetails::RequestThreatDOMDetails(content::RenderFrameHost* frame) {
frame,
back_forward_cache::DisabledReason(
back_forward_cache::DisabledReasonId::kSafeBrowsingThreatDetails));
- if (!frame->IsRenderFrameCreated()) {
+ if (!frame->IsRenderFrameLive()) {
// A child frame may have been created browser-side but has not completed
// setting up the renderer for it yet. In particular, this occurs if the
// child frame was blocked and that's why we're showing a safe browsing page
@@ -851,28 +844,11 @@ void ThreatDetails::OnCacheCollectionReady() {
report_->mutable_client_properties()->set_url_api_type(
GetUrlApiTypeForThreatSource(resource_.threat_source));
- if (!get_user_population_callback_.is_null()) {
- *report_->mutable_population() = get_user_population_callback_.Run();
- }
-
// Fill the referrer chain if applicable.
MaybeFillReferrerChain();
// Send the report, using the SafeBrowsingService.
- std::string serialized;
- if (!report_->SerializeToString(&serialized)) {
- DLOG(ERROR) << "Unable to serialize the threat report.";
- AllDone();
- return;
- }
-
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE,
- base::BindOnce(&WebUIInfoSingleton::AddToCSBRRsSent,
- base::Unretained(WebUIInfoSingleton::GetInstance()),
- std::move(report_)));
-
- ui_manager_->SendSerializedThreatDetails(browser_context_, serialized);
+ ui_manager_->SendThreatDetails(browser_context_, std::move(report_));
AllDone();
}
@@ -890,7 +866,7 @@ void ThreatDetails::MaybeFillReferrerChain() {
// We would have cancelled a prerender if it was blocked, so we can use the
// primary main frame here.
referrer_chain_provider_->IdentifyReferrerChainByRenderFrameHost(
- web_contents_->GetMainFrame(), kThreatDetailsUserGestureLimit,
+ web_contents_->GetPrimaryMainFrame(), kThreatDetailsUserGestureLimit,
report_->mutable_referrer_chain());
}
diff --git a/chromium/components/safe_browsing/content/browser/threat_details.h b/chromium/components/safe_browsing/content/browser/threat_details.h
index cbfde1009d0..3104c345750 100644
--- a/chromium/components/safe_browsing/content/browser/threat_details.h
+++ b/chromium/components/safe_browsing/content/browser/threat_details.h
@@ -91,8 +91,6 @@ class ThreatDetails {
const UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
bool trim_to_ad_tags,
ThreatDetailsDoneCallback done_callback);
@@ -129,8 +127,6 @@ class ThreatDetails {
const UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
bool trim_to_ad_tags,
ThreatDetailsDoneCallback done_callback);
@@ -215,8 +211,6 @@ class ThreatDetails {
const UnsafeResource resource_;
- base::RepeatingCallback<ChromeUserPopulation()> get_user_population_callback_;
-
raw_ptr<ReferrerChainProvider> referrer_chain_provider_;
// For every Url we collect we create a Resource message. We keep
@@ -308,8 +302,6 @@ class ThreatDetailsFactory {
const security_interstitials::UnsafeResource& unsafe_resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
bool trim_to_ad_tags,
ThreatDetailsDoneCallback done_callback) = 0;
diff --git a/chromium/components/safe_browsing/content/browser/threat_details_cache.cc b/chromium/components/safe_browsing/content/browser/threat_details_cache.cc
index 1f0a5b14ba2..bd28ab4f449 100644
--- a/chromium/components/safe_browsing/content/browser/threat_details_cache.cc
+++ b/chromium/components/safe_browsing/content/browser/threat_details_cache.cc
@@ -191,7 +191,7 @@ void ThreatDetailsCacheCollector::ReadResponse(
std::string name, value;
while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
// Strip any Set-Cookie headers.
- if (base::LowerCaseEqualsASCII(name, "set-cookie"))
+ if (base::EqualsCaseInsensitiveASCII(name, "set-cookie"))
continue;
ClientSafeBrowsingReportRequest::HTTPHeader* pb_header =
pb_response->add_headers();
diff --git a/chromium/components/safe_browsing/content/browser/triggers/BUILD.gn b/chromium/components/safe_browsing/content/browser/triggers/BUILD.gn
index d1447798444..b7cef3ac77d 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/BUILD.gn
+++ b/chromium/components/safe_browsing/content/browser/triggers/BUILD.gn
@@ -63,7 +63,6 @@ source_set("ad_sampler_trigger") {
"//base:base",
"//components/safe_browsing/core/browser:referrer_chain_provider",
"//components/safe_browsing/core/common",
- "//components/safe_browsing/core/common/proto:csd_proto",
"//content/public/browser",
]
}
@@ -81,7 +80,6 @@ source_set("suspicious_site_trigger") {
"//components/prefs:prefs",
"//components/safe_browsing/core/browser:referrer_chain_provider",
"//components/safe_browsing/core/common",
- "//components/safe_browsing/core/common/proto:csd_proto",
"//content/public/browser",
"//net:net",
]
diff --git a/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.cc b/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.cc
index e7b6d9b6932..d328b5521a3 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.cc
+++ b/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.cc
@@ -85,8 +85,6 @@ AdSamplerTrigger::AdSamplerTrigger(
PrefService* prefs,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider)
: content::WebContentsObserver(web_contents),
content::WebContentsUserData<AdSamplerTrigger>(*web_contents),
@@ -99,7 +97,6 @@ AdSamplerTrigger::AdSamplerTrigger(
prefs_(prefs),
url_loader_factory_(url_loader_factory),
history_service_(history_service),
- get_user_population_callback_(get_user_population_callback),
referrer_chain_provider_(referrer_chain_provider),
task_runner_(content::GetUIThreadTaskRunner({})) {}
@@ -137,7 +134,7 @@ void AdSamplerTrigger::CreateAdSampleReport() {
TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
const content::GlobalRenderFrameHostId primary_main_frame_id =
- web_contents()->GetMainFrame()->GetGlobalId();
+ web_contents()->GetPrimaryMainFrame()->GetGlobalId();
security_interstitials::UnsafeResource resource;
resource.threat_type = SB_THREAT_TYPE_AD_SAMPLE;
resource.url = web_contents()->GetURL();
@@ -146,8 +143,7 @@ void AdSamplerTrigger::CreateAdSampleReport() {
if (!trigger_manager_->StartCollectingThreatDetails(
TriggerType::AD_SAMPLE, web_contents(), resource, url_loader_factory_,
- history_service_, get_user_population_callback_,
- referrer_chain_provider_, error_options)) {
+ history_service_, referrer_chain_provider_, error_options)) {
UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName,
NO_SAMPLE_COULD_NOT_START_REPORT, MAX_ACTIONS);
return;
diff --git a/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.h b/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.h
index 59af2aadc94..b6336dab7d7 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.h
+++ b/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger.h
@@ -8,7 +8,6 @@
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
@@ -83,8 +82,6 @@ class AdSamplerTrigger : public content::WebContentsObserver,
PrefService* prefs,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider);
// Called to create an ad sample report.
@@ -116,7 +113,6 @@ class AdSamplerTrigger : public content::WebContentsObserver,
raw_ptr<PrefService> prefs_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
raw_ptr<history::HistoryService> history_service_;
- base::RepeatingCallback<ChromeUserPopulation()> get_user_population_callback_;
raw_ptr<ReferrerChainProvider> referrer_chain_provider_;
// Task runner for posting delayed tasks. Normally set to the runner for the
diff --git a/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger_unittest.cc b/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger_unittest.cc
index 85c8017866a..54b90de5022 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger_unittest.cc
+++ b/chromium/components/safe_browsing/content/browser/triggers/ad_sampler_trigger_unittest.cc
@@ -50,8 +50,7 @@ class AdSamplerTriggerTest : public content::RenderViewHostTestHarness {
void CreateTriggerWithFrequency(const size_t denominator) {
safe_browsing::AdSamplerTrigger::CreateForWebContents(
- web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr,
- base::NullCallback(), nullptr);
+ web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr, nullptr);
safe_browsing::AdSamplerTrigger* ad_sampler =
safe_browsing::AdSamplerTrigger::FromWebContents(web_contents());
@@ -69,7 +68,7 @@ class AdSamplerTriggerTest : public content::RenderViewHostTestHarness {
// Returns the final RenderFrameHost after navigation commits.
RenderFrameHost* NavigateMainFrame(const std::string& url) {
- return NavigateFrame(url, web_contents()->GetMainFrame());
+ return NavigateFrame(url, web_contents()->GetPrimaryMainFrame());
}
// Returns the final RenderFrameHost after navigation commits.
@@ -101,7 +100,7 @@ TEST_F(AdSamplerTriggerTest, TriggerDisabledBySamplingFrequency) {
// zero, which disables the trigger.
CreateTriggerWithFrequency(kAdSamplerFrequencyDisabled);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetails(_, _, _, _, _, _, _, _))
+ StartCollectingThreatDetails(_, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
@@ -128,7 +127,7 @@ TEST_F(AdSamplerTriggerTest, DISABLED_PageWithNoAds) {
CreateTriggerWithFrequency(/*denominator=*/1);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetails(_, _, _, _, _, _, _, _))
+ StartCollectingThreatDetails(_, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
@@ -151,7 +150,7 @@ TEST_F(AdSamplerTriggerTest, PageWithMultipleAds) {
CreateTriggerWithFrequency(/*denominator=*/1);
EXPECT_CALL(*get_trigger_manager(),
StartCollectingThreatDetails(TriggerType::AD_SAMPLE,
- web_contents(), _, _, _, _, _, _))
+ web_contents(), _, _, _, _, _))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(*get_trigger_manager(),
@@ -184,7 +183,7 @@ TEST_F(AdSamplerTriggerTest, ReportRejectedByTriggerManager) {
CreateTriggerWithFrequency(/*denominator=*/1);
EXPECT_CALL(*get_trigger_manager(),
StartCollectingThreatDetails(TriggerType::AD_SAMPLE,
- web_contents(), _, _, _, _, _, _))
+ web_contents(), _, _, _, _, _))
.Times(1)
.WillOnce(Return(false));
EXPECT_CALL(*get_trigger_manager(),
@@ -216,7 +215,7 @@ TEST(AdSamplerTriggerTestFinch, FrequencyDenominatorFeature) {
// given.
content::BrowserTaskEnvironment task_environment;
AdSamplerTrigger trigger_default(nullptr, nullptr, nullptr, nullptr, nullptr,
- base::NullCallback(), nullptr);
+ nullptr);
EXPECT_EQ(kAdSamplerDefaultFrequency,
trigger_default.sampler_frequency_denominator_);
@@ -232,7 +231,7 @@ TEST(AdSamplerTriggerTestFinch, FrequencyDenominatorFeature) {
safe_browsing::kAdSamplerTriggerFeature, feature_params);
AdSamplerTrigger trigger_finch(nullptr, nullptr, nullptr, nullptr, nullptr,
- base::NullCallback(), nullptr);
+ nullptr);
EXPECT_EQ(kDenominatorInt, trigger_finch.sampler_frequency_denominator_);
}
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/browser/triggers/mock_trigger_manager.h b/chromium/components/safe_browsing/content/browser/triggers/mock_trigger_manager.h
index 13a05caab21..46833c4cee5 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/mock_trigger_manager.h
+++ b/chromium/components/safe_browsing/content/browser/triggers/mock_trigger_manager.h
@@ -21,26 +21,22 @@ class MockTriggerManager : public TriggerManager {
~MockTriggerManager() override;
- MOCK_METHOD8(
+ MOCK_METHOD7(
StartCollectingThreatDetails,
bool(TriggerType trigger_type,
content::WebContents* web_contents,
const security_interstitials::UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
const SBErrorOptions& error_display_options));
- MOCK_METHOD9(
+ MOCK_METHOD8(
StartCollectingThreatDetailsWithReason,
bool(TriggerType trigger_type,
content::WebContents* web_contents,
const security_interstitials::UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
const SBErrorOptions& error_display_options,
TriggerManagerReason* out_reason));
diff --git a/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.cc b/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.cc
index d4d092faeb6..eb0bc72d40f 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.cc
+++ b/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.cc
@@ -61,8 +61,6 @@ SuspiciousSiteTrigger::SuspiciousSiteTrigger(
PrefService* prefs,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
bool monitor_mode)
: content::WebContentsObserver(web_contents),
@@ -74,7 +72,6 @@ SuspiciousSiteTrigger::SuspiciousSiteTrigger(
prefs_(prefs),
url_loader_factory_(url_loader_factory),
history_service_(history_service),
- get_user_population_callback_(get_user_population_callback),
referrer_chain_provider_(referrer_chain_provider),
task_runner_(content::GetUIThreadTaskRunner({})) {}
@@ -102,8 +99,8 @@ bool SuspiciousSiteTrigger::MaybeStartReport() {
TriggerManagerReason reason;
if (!trigger_manager_->StartCollectingThreatDetailsWithReason(
TriggerType::SUSPICIOUS_SITE, web_contents(), resource,
- url_loader_factory_, history_service_, get_user_population_callback_,
- referrer_chain_provider_, error_options, &reason)) {
+ url_loader_factory_, history_service_, referrer_chain_provider_,
+ error_options, &reason)) {
UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
SuspiciousSiteTriggerEvent::REPORT_START_FAILED);
LOCAL_HISTOGRAM_ENUMERATION(
diff --git a/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.h b/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.h
index fc225e43e1c..88c9f90d30c 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.h
+++ b/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger.h
@@ -7,7 +7,6 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
@@ -132,8 +131,6 @@ class SuspiciousSiteTrigger
PrefService* prefs,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
bool monitor_mode);
@@ -174,7 +171,6 @@ class SuspiciousSiteTrigger
raw_ptr<PrefService> prefs_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
raw_ptr<history::HistoryService> history_service_;
- base::RepeatingCallback<ChromeUserPopulation()> get_user_population_callback_;
raw_ptr<ReferrerChainProvider> referrer_chain_provider_;
// Task runner for posting delayed tasks. Normally set to the runner for the
diff --git a/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger_unittest.cc b/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger_unittest.cc
index db7beb2a06b..5261da00dc0 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger_unittest.cc
+++ b/chromium/components/safe_browsing/content/browser/triggers/suspicious_site_trigger_unittest.cc
@@ -55,8 +55,8 @@ class SuspiciousSiteTriggerTest : public content::RenderViewHostTestHarness {
void CreateTrigger(bool monitor_mode) {
safe_browsing::SuspiciousSiteTrigger::CreateForWebContents(
- web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr,
- base::NullCallback(), nullptr, monitor_mode);
+ web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr, nullptr,
+ monitor_mode);
safe_browsing::SuspiciousSiteTrigger* trigger =
safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents());
// Give the trigger a test task runner that we can synchronize on.
@@ -78,7 +78,7 @@ class SuspiciousSiteTriggerTest : public content::RenderViewHostTestHarness {
// Returns the final RenderFrameHost after navigation commits.
RenderFrameHost* NavigateMainFrame(const std::string& url) {
- return NavigateFrame(url, web_contents()->GetMainFrame());
+ return NavigateFrame(url, web_contents()->GetPrimaryMainFrame());
}
// Returns the final RenderFrameHost after navigation commits.
@@ -172,7 +172,7 @@ TEST_F(SuspiciousSiteTriggerTest, RegularPageNonSuspicious) {
CreateTrigger(/*monitor_mode=*/false);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _, _))
+ StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
@@ -204,7 +204,7 @@ TEST_F(SuspiciousSiteTriggerTest, MAYBE_SuspiciousHitDuringLoad) {
CreateTrigger(/*monitor_mode=*/false);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _, _))
+ StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(*get_trigger_manager(),
@@ -242,7 +242,7 @@ TEST_F(SuspiciousSiteTriggerTest, SuspiciousHitAfterLoad) {
CreateTrigger(/*monitor_mode=*/false);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _, _))
+ StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(*get_trigger_manager(),
@@ -279,10 +279,10 @@ TEST_F(SuspiciousSiteTriggerTest, DISABLED_ReportRejectedByTriggerManager) {
CreateTrigger(/*monitor_mode=*/false);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _, _))
+ StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(
- DoAll(SetArgPointee<8>(TriggerManagerReason::DAILY_QUOTA_EXCEEDED),
+ DoAll(SetArgPointee<7>(TriggerManagerReason::DAILY_QUOTA_EXCEEDED),
Return(false)));
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
@@ -321,7 +321,7 @@ TEST_F(SuspiciousSiteTriggerTest, NewNavigationMidLoad_NotSuspicious) {
CreateTrigger(/*monitor_mode=*/false);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _, _))
+ StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
@@ -353,7 +353,7 @@ TEST_F(SuspiciousSiteTriggerTest, DISABLED_NewNavigationMidLoad_Suspicious) {
CreateTrigger(/*monitor_mode=*/false);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _, _))
+ StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
@@ -391,7 +391,7 @@ TEST_F(SuspiciousSiteTriggerTest, MonitorMode_NotSuspicious) {
CreateTrigger(/*monitor_mode=*/true);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _, _))
+ StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
@@ -418,7 +418,7 @@ TEST_F(SuspiciousSiteTriggerTest, MonitorMode_SuspiciousHitDuringLoad) {
CreateTrigger(/*monitor_mode=*/true);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _, _))
+ StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
@@ -454,7 +454,7 @@ TEST_F(SuspiciousSiteTriggerTest, VisibleURLChangeMidLoad_NotSuspicious) {
CreateTrigger(/*monitor_mode=*/false);
EXPECT_CALL(*get_trigger_manager(),
- StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _, _))
+ StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
@@ -491,7 +491,7 @@ TEST_F(SuspiciousSiteTriggerTest, VisibleURLChangeMidLoad_Suspicious) {
GURL suspicious_url(kSuspiciousUrl);
EXPECT_CALL(*get_trigger_manager(),
StartCollectingThreatDetailsWithReason(
- _, _, ResourceHasUrl(suspicious_url), _, _, _, _, _, _))
+ _, _, ResourceHasUrl(suspicious_url), _, _, _, _, _))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(*get_trigger_manager(),
diff --git a/chromium/components/safe_browsing/content/browser/triggers/trigger_manager.cc b/chromium/components/safe_browsing/content/browser/triggers/trigger_manager.cc
index b387d85270c..8d6f7b11851 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/trigger_manager.cc
+++ b/chromium/components/safe_browsing/content/browser/triggers/trigger_manager.cc
@@ -148,15 +148,12 @@ bool TriggerManager::StartCollectingThreatDetails(
const security_interstitials::UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
const SBErrorOptions& error_display_options) {
TriggerManagerReason unused_reason;
return StartCollectingThreatDetailsWithReason(
trigger_type, web_contents, resource, url_loader_factory, history_service,
- get_user_population_callback, referrer_chain_provider,
- error_display_options, &unused_reason);
+ referrer_chain_provider, error_display_options, &unused_reason);
}
bool TriggerManager::StartCollectingThreatDetailsWithReason(
@@ -165,8 +162,6 @@ bool TriggerManager::StartCollectingThreatDetailsWithReason(
const security_interstitials::UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
const SBErrorOptions& error_display_options,
TriggerManagerReason* reason) {
@@ -184,8 +179,7 @@ bool TriggerManager::StartCollectingThreatDetailsWithReason(
bool should_trim_threat_details = trigger_type == TriggerType::AD_SAMPLE;
collectors->threat_details = ThreatDetails::NewThreatDetails(
ui_manager_, web_contents, resource, url_loader_factory, history_service,
- get_user_population_callback, referrer_chain_provider,
- should_trim_threat_details,
+ referrer_chain_provider, should_trim_threat_details,
base::BindOnce(&TriggerManager::ThreatDetailsDone,
weak_factory_.GetWeakPtr()));
return true;
diff --git a/chromium/components/safe_browsing/content/browser/triggers/trigger_manager.h b/chromium/components/safe_browsing/content/browser/triggers/trigger_manager.h
index d191758b4d4..edb23b7b4d4 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/trigger_manager.h
+++ b/chromium/components/safe_browsing/content/browser/triggers/trigger_manager.h
@@ -132,8 +132,6 @@ class TriggerManager {
const security_interstitials::UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
const SBErrorOptions& error_display_options,
TriggerManagerReason* out_reason);
@@ -146,8 +144,6 @@ class TriggerManager {
const security_interstitials::UnsafeResource& resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
const SBErrorOptions& error_display_options);
diff --git a/chromium/components/safe_browsing/content/browser/triggers/trigger_manager_unittest.cc b/chromium/components/safe_browsing/content/browser/triggers/trigger_manager_unittest.cc
index a84455b3868..584e35d2f10 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/trigger_manager_unittest.cc
+++ b/chromium/components/safe_browsing/content/browser/triggers/trigger_manager_unittest.cc
@@ -47,8 +47,6 @@ class MockThreatDetailsFactory : public ThreatDetailsFactory {
const security_interstitials::UnsafeResource& unsafe_resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
- base::RepeatingCallback<ChromeUserPopulation()>
- get_user_population_callback,
ReferrerChainProvider* referrer_chain_provider,
bool trim_to_ad_tags,
ThreatDetailsDoneCallback done_callback) override {
@@ -123,7 +121,7 @@ class TriggerManagerTest : public ::testing::Test {
TriggerManager::GetSBErrorDisplayOptions(pref_service_, web_contents);
return trigger_manager_.StartCollectingThreatDetails(
trigger_type, web_contents, security_interstitials::UnsafeResource(),
- nullptr, nullptr, base::NullCallback(), nullptr, options);
+ nullptr, nullptr, nullptr, options);
}
bool FinishCollectingThreatDetails(const TriggerType trigger_type,
diff --git a/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.cc b/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.cc
index b203cee28a2..8f3553ee387 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.cc
+++ b/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.cc
@@ -18,7 +18,6 @@ namespace safe_browsing {
const size_t kAdSamplerTriggerDefaultQuota = 10;
const size_t kSuspiciousSiteTriggerDefaultQuota = 5;
const char kSuspiciousSiteTriggerQuotaParam[] = "suspicious_site_trigger_quota";
-const char kTriggerTypeAndQuotaParam[] = "trigger_type_and_quota_csv";
namespace {
const size_t kUnlimitedTriggerQuota = std::numeric_limits<size_t>::max();
@@ -49,44 +48,6 @@ void ParseTriggerTypeAndQuotaParam(
trigger_type_and_quota_list->push_back(
std::make_pair(TriggerType::SUSPICIOUS_SITE, suspicious_site_quota));
}
-
- // If the feature is disabled we just use the default list. Otherwise the list
- // from the Finch param will be the one used.
- if (!base::FeatureList::IsEnabled(kTriggerThrottlerDailyQuotaFeature)) {
- return;
- }
-
- const std::string& trigger_and_quota_csv_param =
- base::GetFieldTrialParamValueByFeature(kTriggerThrottlerDailyQuotaFeature,
- kTriggerTypeAndQuotaParam);
- if (trigger_and_quota_csv_param.empty()) {
- return;
- }
-
- std::vector<std::string> split =
- base::SplitString(trigger_and_quota_csv_param, ",", base::TRIM_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY);
- // If we don't have the right number of pairs in the csv then don't bother
- // parsing further.
- if (split.size() % 2 != 0) {
- return;
- }
- for (size_t i = 0; i < split.size(); i += 2) {
- // Make sure both the trigger type and quota are integers. Skip them if not.
- int trigger_type_int = -1;
- int quota_int = -1;
- if (!base::StringToInt(split[i], &trigger_type_int) ||
- !base::StringToInt(split[i + 1], &quota_int)) {
- continue;
- }
- trigger_type_and_quota_list->push_back(
- std::make_pair(static_cast<TriggerType>(trigger_type_int), quota_int));
- }
-
- std::sort(trigger_type_and_quota_list->begin(),
- trigger_type_and_quota_list->end(),
- [](const TriggerTypeAndQuotaItem& a,
- const TriggerTypeAndQuotaItem& b) { return a.first < b.first; });
}
// Looks in |trigger_quota_list| for |trigger_type|. If found, sets |out_quota|
@@ -241,7 +202,7 @@ void TriggerThrottler::WriteTriggerEventsToPref() {
size_t TriggerThrottler::GetDailyQuotaForTrigger(
const TriggerType trigger_type) const {
- size_t quota_from_finch = 0;
+ size_t quota = 0;
switch (trigger_type) {
case TriggerType::SECURITY_INTERSTITIAL:
case TriggerType::GAIA_PASSWORD_REUSE:
@@ -253,18 +214,18 @@ size_t TriggerThrottler::GetDailyQuotaForTrigger(
return 0;
case TriggerType::AD_SAMPLE:
- // Ad Samples have a non-zero default quota, but it can be overwritten
- // through Finch.
+ // Check for non-default quota (needed for unit tests).
if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
- &quota_from_finch)) {
- return quota_from_finch;
+ &quota)) {
+ return quota;
}
return kAdSamplerTriggerDefaultQuota;
+
case TriggerType::SUSPICIOUS_SITE:
// Suspicious Sites are disabled unless they are configured through Finch.
if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
- &quota_from_finch)) {
- return quota_from_finch;
+ &quota)) {
+ return quota;
}
break;
}
diff --git a/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.h b/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.h
index 33fa604fdb5..8ee231b0cd3 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.h
+++ b/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler.h
@@ -27,14 +27,6 @@ extern const size_t kSuspiciousSiteTriggerDefaultQuota;
// trigger.
extern const char kSuspiciousSiteTriggerQuotaParam[];
-// Param name of the finch param containing the comma-separated list of trigger
-// types and daily quotas.
-// TODO(crbug.com/744869): This param should be deprecated after ad sampler
-// launch in favour of having a unique quota feature and param per trigger.
-// Having a single shared feature makes it impossible to run multiple trigger
-// trials simultaneously.
-extern const char kTriggerTypeAndQuotaParam[];
-
enum class TriggerType {
SECURITY_INTERSTITIAL = 1,
AD_SAMPLE = 2,
diff --git a/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler_unittest.cc b/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler_unittest.cc
index b760b99f777..56c570004db 100644
--- a/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler_unittest.cc
+++ b/chromium/components/safe_browsing/content/browser/triggers/trigger_throttler_unittest.cc
@@ -229,11 +229,6 @@ class TriggerThrottlerTestFinch : public ::testing::Test {
const base::Feature** out_feature,
std::string* out_param) {
switch (trigger_type) {
- case TriggerType::AD_SAMPLE:
- *out_feature = &safe_browsing::kTriggerThrottlerDailyQuotaFeature;
- *out_param = safe_browsing::kTriggerTypeAndQuotaParam;
- break;
-
case TriggerType::SUSPICIOUS_SITE:
*out_feature = &safe_browsing::kSuspiciousSiteTriggerQuotaFeature;
*out_param = safe_browsing::kSuspiciousSiteTriggerQuotaParam;
@@ -254,42 +249,12 @@ class TriggerThrottlerTestFinch : public ::testing::Test {
}
};
-TEST_F(TriggerThrottlerTestFinch, ConfigureQuotaViaFinch) {
- base::test::ScopedFeatureList scoped_feature_list;
- SetupQuotaParams(TriggerType::AD_SAMPLE, "Group_ConfigureQuotaViaFinch", 3,
- &scoped_feature_list);
- // Make sure that setting the quota param via Finch params works as expected.
-
- // The throttler has been configured (above) to allow ad samples to fire three
- // times per day.
- TriggerThrottler throttler(nullptr);
-
- // First three triggers should work
- EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
- throttler.TriggerFired(TriggerType::AD_SAMPLE);
- EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
- throttler.TriggerFired(TriggerType::AD_SAMPLE);
- EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
- throttler.TriggerFired(TriggerType::AD_SAMPLE);
-
- // Fourth attempt will fail since we're out of quota.
- EXPECT_FALSE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
-}
-
TEST_F(TriggerThrottlerTestFinch, AdSamplerDefaultQuota) {
- // Make sure that the ad sampler gets its own default quota when no finch
- // config exists, but the quota can be overwritten through Finch.
+ // Make sure that the ad sampler gets its own default quota.
TriggerThrottler throttler_default(nullptr);
EXPECT_EQ(kAdSamplerTriggerDefaultQuota,
GetDailyQuotaForTrigger(throttler_default, TriggerType::AD_SAMPLE));
EXPECT_TRUE(throttler_default.TriggerCanFire(TriggerType::AD_SAMPLE));
-
- base::test::ScopedFeatureList scoped_feature_list;
- SetupQuotaParams(TriggerType::AD_SAMPLE, "Group_AdSamplerDefaultQuota", 4,
- &scoped_feature_list);
- TriggerThrottler throttler_finch(nullptr);
- EXPECT_EQ(4u,
- GetDailyQuotaForTrigger(throttler_finch, TriggerType::AD_SAMPLE));
}
TEST_F(TriggerThrottlerTestFinch, SuspiciousSiteTriggerDefaultQuota) {
diff --git a/chromium/components/safe_browsing/content/browser/ui_manager.cc b/chromium/components/safe_browsing/content/browser/ui_manager.cc
index ba29edb0b78..e7331e5729f 100644
--- a/chromium/components/safe_browsing/content/browser/ui_manager.cc
+++ b/chromium/components/safe_browsing/content/browser/ui_manager.cc
@@ -14,6 +14,7 @@
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/content/browser/safe_browsing_blocking_page.h"
#include "components/safe_browsing/content/browser/threat_details.h"
+#include "components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h"
#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
#include "components/safe_browsing/core/browser/ping_manager.h"
#include "components/safe_browsing/core/common/features.h"
@@ -203,6 +204,15 @@ void SafeBrowsingUIManager::MaybeReportSafeBrowsingHit(
<< hit_report.is_subresource << " " << hit_report.threat_type;
delegate_->GetPingManager(web_contents->GetBrowserContext())
->ReportSafeBrowsingHit(hit_report);
+
+ // The following is to log this HitReport on any open chrome://safe-browsing
+ // pages.
+ auto hit_report_copy = std::make_unique<HitReport>(hit_report);
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WebUIInfoSingleton::AddToHitReportsSent,
+ base::Unretained(WebUIInfoSingleton::GetInstance()),
+ std::move(hit_report_copy)));
}
// Static.
@@ -279,18 +289,17 @@ const GURL SafeBrowsingUIManager::default_safe_page() const {
// If the user had opted-in to send ThreatDetails, this gets called
// when the report is ready.
-void SafeBrowsingUIManager::SendSerializedThreatDetails(
+void SafeBrowsingUIManager::SendThreatDetails(
content::BrowserContext* browser_context,
- const std::string& serialized) {
+ std::unique_ptr<ClientSafeBrowsingReportRequest> report) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (shut_down_)
return;
- if (!serialized.empty()) {
- DVLOG(1) << "Sending serialized threat details.";
- delegate_->GetPingManager(browser_context)->ReportThreatDetails(serialized);
- }
+ DVLOG(1) << "Sending threat details.";
+ delegate_->GetPingManager(browser_context)
+ ->ReportThreatDetails(std::move(report));
}
void SafeBrowsingUIManager::OnBlockingPageDone(
diff --git a/chromium/components/safe_browsing/content/browser/ui_manager.h b/chromium/components/safe_browsing/content/browser/ui_manager.h
index bc37f984b99..d2607ce152d 100644
--- a/chromium/components/safe_browsing/content/browser/ui_manager.h
+++ b/chromium/components/safe_browsing/content/browser/ui_manager.h
@@ -140,10 +140,11 @@ class SafeBrowsingUIManager : public BaseUIManager {
// on UI thread. If shutdown is true, the manager is disabled permanently.
void Stop(bool shutdown);
- // Called on the IO thread by the ThreatDetails with the serialized
- // protocol buffer, so the service can send it over.
- void SendSerializedThreatDetails(content::BrowserContext* browser_context,
- const std::string& serialized) override;
+ // Called on the IO thread by the ThreatDetails with the report, so the
+ // service can send it over.
+ void SendThreatDetails(
+ content::BrowserContext* browser_context,
+ std::unique_ptr<ClientSafeBrowsingReportRequest> report) override;
// Calls |BaseUIManager::OnBlockingPageDone()| and triggers
// |OnSecurityInterstitialProceeded| event if |proceed| is true.
diff --git a/chromium/components/safe_browsing/content/browser/ui_manager_unittest.cc b/chromium/components/safe_browsing/content/browser/ui_manager_unittest.cc
index a4e16e329f4..5f9c2c7a763 100644
--- a/chromium/components/safe_browsing/content/browser/ui_manager_unittest.cc
+++ b/chromium/components/safe_browsing/content/browser/ui_manager_unittest.cc
@@ -122,7 +122,6 @@ class TestSafeBrowsingBlockingPage : public SafeBrowsingBlockingPage {
"cpn_safe_browsing"), // help_center_article_link
true, // should_trigger_reporting
/*history_service=*/nullptr,
- /*get_user_population_callback=*/base::NullCallback(),
/*navigation_observer_manager=*/nullptr,
/*metrics_collector=*/nullptr,
/*trigger_manager=*/nullptr) {
@@ -236,7 +235,7 @@ class SafeBrowsingUIManagerTest : public content::RenderViewHostTestHarness {
const char* url,
bool is_subresource) {
const content::GlobalRenderFrameHostId primary_main_frame_id =
- web_contents()->GetMainFrame()->GetGlobalId();
+ web_contents()->GetPrimaryMainFrame()->GetGlobalId();
security_interstitials::UnsafeResource resource;
resource.url = GURL(url);
resource.is_subresource = is_subresource;
diff --git a/chromium/components/safe_browsing/content/browser/web_api_handshake_checker.cc b/chromium/components/safe_browsing/content/browser/web_api_handshake_checker.cc
index 9cdd007217f..9c07406d1c3 100644
--- a/chromium/components/safe_browsing/content/browser/web_api_handshake_checker.cc
+++ b/chromium/components/safe_browsing/content/browser/web_api_handshake_checker.cc
@@ -45,7 +45,6 @@ class WebApiHandshakeChecker::CheckerOnIO
std::move(delegate_getter_).Run();
bool skip_checks =
!url_checker_delegate ||
- !url_checker_delegate->GetDatabaseManager()->IsSupported() ||
url_checker_delegate->ShouldSkipRequestCheck(
url, frame_tree_node_id_,
/*render_process_id=*/content::ChildProcessHost::kInvalidUniqueID,
diff --git a/chromium/components/safe_browsing/content/browser/web_ui/BUILD.gn b/chromium/components/safe_browsing/content/browser/web_ui/BUILD.gn
index 06bc3797497..26bdd3783db 100644
--- a/chromium/components/safe_browsing/content/browser/web_ui/BUILD.gn
+++ b/chromium/components/safe_browsing/content/browser/web_ui/BUILD.gn
@@ -21,6 +21,7 @@ static_library("web_ui") {
"//components/safe_browsing/core/browser",
"//components/safe_browsing/core/browser:download_check_result",
"//components/safe_browsing/core/browser:referrer_chain_provider",
+ "//components/safe_browsing/core/browser/db:hit_report",
"//components/safe_browsing/core/browser/db:v4_local_database_manager",
"//components/safe_browsing/core/common",
"//components/safe_browsing/core/common:safe_browsing_prefs",
diff --git a/chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.html b/chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.html
index 45423e018fa..685ee88b3b5 100644
--- a/chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.html
+++ b/chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.html
@@ -5,138 +5,137 @@
<title>Safe Browsing</title>
<link rel="stylesheet" href="safe_browsing.css">
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
- <link rel="stylesheet" href="chrome://resources/css/tabs.css">
</head>
<body>
<div id="header">
<h1 id="sb-title">Safe Browsing</h1>
</div>
- <tabbox id='tabbox'>
- <tabs>
- <tab id="preferences">Preferences</tab>
- <tab id="db-manager">Database Manager</tab>
- <tab id="hash-cache">Hash Cache</tab>
- <tab id="csbrr">ClientSafeBrowsingReportRequests</tab>
- <tab id="download-protection">Download Protection</tab>
- <tab id="cpr">Client Phishing Requests</tab>
- <tab id="password-protection">Password Protection</tab>
- <tab id="rt-lookup">RT Lookup</tab>
- <tab id="referrer-chain">Referrer Chain</tab>
- <tab id="log">Log Messages</tab>
- <tab id="reporting">Reporting Events</tab>
- <tab id="deep-scan">Deep Scans</tab>
- </tabs>
- <tabpanels>
- <tabpanel>
- <h2>Experiments</h2>
- <div class="content">
- <p id="experiments-list" class="result-container"></p>
- </div>
- <h2>Preferences</h2>
- <div class="content">
- <p id="preferences-list" class="result-container"></p>
- </div>
- <h2>Policies</h2>
- <div class="content">
- <p id="policies-list" class="result-container"></p>
- </div>
- <h2>Safe Browsing Cookie</h2>
- <div class="content">
- <p id="cookie-panel" class="result-container"></p>
- </div>
- </tabpanel>
- <tabpanel>
- <h2>Database Manager</h2>
- <div class="content">
- <p id="database-info-list" class="result-container"></p>
- </div>
- </tabpanel>
- <tabpanel>
- <h2>Full Hash Cache</h2>
- <div class="content">
- <p id="full-hash-cache-info"></p>
- </div>
- </tabpanel>
- <tabpanel>
- <h2>CSBRRs (ClientSafeBrowsingReportRequest) sent</h2>
- <div class="content">
- <p id="sent-csbrrs-list"></p>
- </div>
- </tabpanel>
- <tabpanel>
- <h2>Download URLs checked</h2>
- <div class="content">
- <p id="download-urls-checked-list"></p>
- </div>
- <h2>Download requests (ClientDownloadRequest) sent</h2>
- <div class="content">
- <p id="sent-client-download-requests-list"></p>
- </div>
- <h2>Download responses (ClientDownloadResponse) received</h2>
- <div class="content">
- <p id="received-client-download-response-list"></p>
- </div>
- </tabpanel>
- <tabpanel>
- <h2>Client Phishing requests sent</h2>
- <div class="content">
- <p id="sent-client-phishing-requests-list"></p>
- </div>
- <h2>Client Phishing responses sent</h2>
- <div class="content">
- <p id="received-client-phishing-response-list"></p>
- </div>
- </tabpanel>
- <tabpanel>
- <h2>Saved Password Hashes</h2>
- <div class="content">
- <p id="saved-passwords"></p>
- </div>
- <h2>Password Protection Events</h2>
- <div class="content">
- <p id="pg-event-log"></p>
- </div>
- <h2>Security Events</h2>
- <div class="content">
- <p id="security-event-log"></p>
- </div>
- <h2>Password Protection Pings</h2>
- <table id="pg-ping-list" class="request-response"></table>
- </tabpanel>
- <tabpanel>
- <h2>RT Lookup Pings</h2>
- <table id="rt-lookup-ping-list" class="request-response"></table>
- </tabpanel>
- <tabpanel>
- <h2>Referrer Chain</h2>
- <form id="get-referrer-chain-form">
- <input type="text" id="referrer-chain-url">
- <input type="submit" value="Get Chain">
- </form>
- <div class="content">
- <p id="referrer-chain-content"></p>
- </div>
- <h2>Most Recent Referring App Info (Android)</h2>
- <p id="referring-app-info" class="result-container"></p>
- </tabpanel>
- <tabpanel>
- <h2>Log Messages</h2>
- <div class="content">
- <p id="log-messages"></p>
- </div>
- </tabpanel>
- <tabpanel>
- <h2>Reporting Events</h2>
- <div class="content">
- <p id="reporting-events"></p>
- </div>
- </tabpanel>
- <tabpanel>
- <h2>Deep Scans</h2>
- <table id="deep-scan-list" class="request-response"></table>
- </tabpanel>
- </tabpanels>
- </tabbox>
+ <cr-tab-box id='tabbox'>
+ <div slot="tab" id="preferences">Preferences</div>
+ <div slot="tab" id="db-manager">Database Manager</div>
+ <div slot="tab" id="hash-cache">Hash Cache</div>
+ <div slot="tab" id="csbrr">ClientSafeBrowsingReportRequests</div>
+ <div slot="tab" id="download-protection">Download Protection</div>
+ <div slot="tab" id="cpr">Client Phishing Requests</div>
+ <div slot="tab" id="password-protection">Password Protection</div>
+ <div slot="tab" id="rt-lookup">RT Lookup</div>
+ <div slot="tab" id="referrer-chain">Referrer Chain</div>
+ <div slot="tab" id="log">Log Messages</div>
+ <div slot="tab" id="reporting">Reporting Events</div>
+ <div slot="tab" id="deep-scan">Deep Scans</div>
+ <div slot="panel">
+ <h2>Experiments</h2>
+ <div class="content">
+ <p id="experiments-list" class="result-container"></p>
+ </div>
+ <h2>Preferences</h2>
+ <div class="content">
+ <p id="preferences-list" class="result-container"></p>
+ </div>
+ <h2>Policies</h2>
+ <div class="content">
+ <p id="policies-list" class="result-container"></p>
+ </div>
+ <h2>Safe Browsing Cookie</h2>
+ <div class="content">
+ <p id="cookie-panel" class="result-container"></p>
+ </div>
+ </div>
+ <div slot="panel">
+ <h2>Database Manager</h2>
+ <div class="content">
+ <p id="database-info-list" class="result-container"></p>
+ </div>
+ </div>
+ <div slot="panel">
+ <h2>Full Hash Cache</h2>
+ <div class="content">
+ <p id="full-hash-cache-info"></p>
+ </div>
+ </div>
+ <div slot="panel">
+ <h2>CSBRRs (ClientSafeBrowsingReportRequest) sent</h2>
+ <div class="content">
+ <p id="sent-csbrrs-list" class="result-container"></p>
+ </div>
+ <h2>Hit Reports sent</h2>
+ <div class="content">
+ <p id="sent-hit-report-list" class="result-container"></p>
+ </div>
+ </div>
+ <div slot="panel">
+ <h2>Download URLs checked</h2>
+ <div class="content">
+ <p id="download-urls-checked-list"></p>
+ </div>
+ <h2>Download requests (ClientDownloadRequest) sent</h2>
+ <div class="content">
+ <p id="sent-client-download-requests-list"></p>
+ </div>
+ <h2>Download responses (ClientDownloadResponse) received</h2>
+ <div class="content">
+ <p id="received-client-download-response-list"></p>
+ </div>
+ </div>
+ <div slot="panel">
+ <h2>Client Phishing requests sent</h2>
+ <div class="content">
+ <p id="sent-client-phishing-requests-list"></p>
+ </div>
+ <h2>Client Phishing responses sent</h2>
+ <div class="content">
+ <p id="received-client-phishing-response-list"></p>
+ </div>
+ </div>
+ <div slot="panel">
+ <h2>Saved Password Hashes</h2>
+ <div class="content">
+ <p id="saved-passwords"></p>
+ </div>
+ <h2>Password Protection Events</h2>
+ <div class="content">
+ <p id="pg-event-log"></p>
+ </div>
+ <h2>Security Events</h2>
+ <div class="content">
+ <p id="security-event-log"></p>
+ </div>
+ <h2>Password Protection Pings</h2>
+ <table id="pg-ping-list" class="request-response"></table>
+ </div>
+ <div slot="panel">
+ <h2>RT Lookup Pings</h2>
+ <table id="rt-lookup-ping-list" class="request-response"></table>
+ </div>
+ <div slot="panel">
+ <h2>Referrer Chain</h2>
+ <form id="get-referrer-chain-form">
+ <input type="text" id="referrer-chain-url">
+ <input type="submit" value="Get Chain">
+ </form>
+ <div class="content">
+ <p id="referrer-chain-content"></p>
+ </div>
+ <h2>Most Recent Referring App Info (Android)</h2>
+ <p id="referring-app-info" class="result-container"></p>
+ </div>
+ <div slot="panel">
+ <h2>Log Messages</h2>
+ <div class="content">
+ <p id="log-messages"></p>
+ </div>
+ </div>
+ <div slot="panel">
+ <h2>Reporting Events</h2>
+ <div class="content">
+ <p id="reporting-events"></p>
+ </div>
+ </div>
+ <div slot="panel">
+ <h2>Deep Scans</h2>
+ <table id="deep-scan-list" class="request-response"></table>
+ </div>
+ </cr-tab-box>
<template id="result-template">
<div>
<span class="bold-span"></span>
diff --git a/chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.js b/chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.js
index f210463f835..6ad33bf7348 100644
--- a/chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.js
+++ b/chromium/components/safe_browsing/content/browser/web_ui/resources/safe_browsing.js
@@ -2,13 +2,11 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
+import 'chrome://resources/cr_elements/cr_tab_box/cr_tab_box.js';
+
import {addWebUIListener, sendWithPromise} from 'chrome://resources/js/cr.m.js';
-import {decorate} from 'chrome://resources/js/cr/ui.m.js';
-import {TabBox} from 'chrome://resources/js/cr/ui/tabs.js';
import {$} from 'chrome://resources/js/util.m.js';
-decorate('tabbox', TabBox);
-
/**
* Asks the C++ SafeBrowsingUIHandler to get the lists of Safe Browsing
* ongoing experiments and preferences.
@@ -89,6 +87,15 @@ function initialize() {
addSentCSBRRsInfo(result);
});
+ sendWithPromise('getSentHitReports', []).then((sentHitReports) => {
+ sentHitReports.forEach(function(hitReports) {
+ addSentHitReportsInfo(hitReports);
+ });
+ });
+ addWebUIListener('sent-hit-report-list', function(result) {
+ addSentHitReportsInfo(result);
+ });
+
sendWithPromise('getPGEvents', []).then((pgEvents) => {
pgEvents.forEach(function(pgEvent) {
addPGEvent(pgEvent);
@@ -184,10 +191,9 @@ function initialize() {
};
// When the tab updates, update the anchor
- $('tabbox').addEventListener('selectedChange', function() {
- const tabbox = $('tabbox');
- const tabs = tabbox.querySelector('tabs').children;
- const selectedTab = tabs[tabbox.selectedIndex];
+ $('tabbox').addEventListener('selected-index-change', e => {
+ const tabs = document.querySelectorAll('div[slot=\'tab\']');
+ const selectedTab = tabs[e.detail];
window.location.hash = 'tab-' + selectedTab.id;
}, true);
}
@@ -310,6 +316,11 @@ function addSentCSBRRsInfo(result) {
appendChildWithInnerText(logDiv, result);
}
+function addSentHitReportsInfo(result) {
+ const logDiv = $('sent-hit-report-list');
+ appendChildWithInnerText(logDiv, result);
+}
+
function addPGEvent(result) {
const logDiv = $('pg-event-log');
const eventFormatted = '[' + (new Date(result['time'])).toLocaleString() +
@@ -421,8 +432,11 @@ function addReferringAppInfo(info) {
}
function showTab(tabId) {
- if ($(tabId)) {
- $(tabId).selected = 'selected';
+ const tabs = document.querySelectorAll('div[slot=\'tab\']');
+ const index = Array.from(tabs).findIndex(t => t.id === tabId);
+ if (index !== -1) {
+ document.querySelector('cr-tab-box')
+ .setAttribute('selected-index', index.toString());
}
}
diff --git a/chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc b/chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
index 96394536699..052ea0f7636 100644
--- a/chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
+++ b/chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
@@ -14,12 +14,14 @@
#include "base/base64url.h"
#include "base/bind.h"
#include "base/callback.h"
+#include "base/callback_helpers.h"
#include "base/containers/cxx20_erase.h"
#include "base/i18n/number_formatting.h"
#include "base/i18n/time_formatting.h"
#include "base/json/json_string_value_serializer.h"
#include "base/memory/ref_counted.h"
#include "base/memory/singleton.h"
+#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
@@ -45,6 +47,7 @@
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
#include "components/safe_browsing/core/browser/db/v4_local_database_manager.h"
@@ -169,6 +172,9 @@ void WebUIInfoSingleton::AddToCSBRRsSent(
for (auto* webui_listener : webui_instances_)
webui_listener->NotifyCSBRRJsListener(csbrr.get());
csbrrs_sent_.push_back(std::move(csbrr));
+ if (on_csbrr_logged_for_testing_) {
+ std::move(on_csbrr_logged_for_testing_).Run();
+ }
}
void WebUIInfoSingleton::ClearCSBRRsSent() {
@@ -176,6 +182,25 @@ void WebUIInfoSingleton::ClearCSBRRsSent() {
csbrrs_sent_);
}
+void WebUIInfoSingleton::SetOnCSBRRLoggedCallbackForTesting(
+ base::OnceClosure on_done) {
+ on_csbrr_logged_for_testing_ = std::move(on_done);
+}
+
+void WebUIInfoSingleton::AddToHitReportsSent(
+ std::unique_ptr<HitReport> hit_report) {
+ if (!HasListener())
+ return;
+
+ for (auto* webui_listener : webui_instances_)
+ webui_listener->NotifyHitReportJsListener(hit_report.get());
+ hit_reports_sent_.push_back(std::move(hit_report));
+}
+
+void WebUIInfoSingleton::ClearHitReportsSent() {
+ std::vector<std::unique_ptr<HitReport>>().swap(hit_reports_sent_);
+}
+
void WebUIInfoSingleton::AddToPGEvents(
const sync_pb::UserEventSpecifics& event) {
if (!HasListener())
@@ -394,12 +419,14 @@ WebUIInfoSingleton::GetReferringAppInfo(content::WebContents* web_contents) {
void WebUIInfoSingleton::ClearListenerForTesting() {
has_test_listener_ = false;
+ on_csbrr_logged_for_testing_ = base::NullCallback();
MaybeClearData();
}
void WebUIInfoSingleton::MaybeClearData() {
if (!HasListener()) {
ClearCSBRRsSent();
+ ClearHitReportsSent();
ClearDownloadUrlsChecked();
ClearClientDownloadRequestsSent();
ClearClientDownloadResponsesReceived();
@@ -904,6 +931,8 @@ std::string SerializeClientDownloadRequest(const ClientDownloadRequest& cdr) {
dict_document_processing_info.SetStringKey(
"maldoca_error_message", processing_info.maldoca_error_message());
}
+ dict_document_processing_info.SetBoolKey(
+ "processing_successful", processing_info.processing_successful());
dict_document_summary.SetKey("processing_info",
std::move(dict_document_processing_info));
}
@@ -1000,6 +1029,8 @@ std::string SerializeClientPhishingRequest(
dict.SetBoolean("is_phishing", cpr.is_phishing());
if (cpr.has_model_version())
dict.SetInteger("model_version", cpr.model_version());
+ if (cpr.has_dom_model_version())
+ dict.SetInteger("dom_model_version", cpr.dom_model_version());
base::Value::ListStorage features;
for (const auto& feature : cpr.feature_map()) {
@@ -1068,8 +1099,125 @@ std::string SerializeClientPhishingResponse(const ClientPhishingResponse& cpr) {
return request_serialized;
}
+base::Value::Dict SerializeHTTPHeader(
+ const ClientSafeBrowsingReportRequest::HTTPHeader& header) {
+ base::Value::Dict header_dict;
+ header_dict.Set("name", header.name());
+ header_dict.Set("value", header.value());
+ return header_dict;
+}
+
+base::Value::Dict SerializeResource(
+ const ClientSafeBrowsingReportRequest::Resource& resource) {
+ base::Value::Dict resource_dict;
+ resource_dict.Set("id", resource.id());
+ resource_dict.Set("url", resource.url());
+ // HTTPRequest
+ if (resource.has_request()) {
+ base::Value::Dict request;
+ if (resource.request().has_firstline()) {
+ base::Value::Dict firstline;
+ firstline.Set("verb", resource.request().firstline().verb());
+ firstline.Set("uri", resource.request().firstline().uri());
+ firstline.Set("version", resource.request().firstline().version());
+ request.Set("firstline", std::move(firstline));
+ }
+ base::Value::List headers;
+ for (const ClientSafeBrowsingReportRequest::HTTPHeader& header :
+ resource.request().headers()) {
+ headers.Append(SerializeHTTPHeader(header));
+ }
+ request.Set("headers", std::move(headers));
+ resource_dict.Set("request", std::move(request));
+ }
+ // HTTPResponse
+ if (resource.has_response()) {
+ base::Value::Dict response;
+ if (resource.response().has_firstline()) {
+ base::Value::Dict firstline;
+ firstline.Set("code", resource.response().firstline().code());
+ firstline.Set("message", resource.response().firstline().message());
+ firstline.Set("version", resource.response().firstline().version());
+ response.Set("firstline", std::move(firstline));
+ }
+ base::Value::List headers;
+ for (const ClientSafeBrowsingReportRequest::HTTPHeader& header :
+ resource.response().headers()) {
+ headers.Append(SerializeHTTPHeader(header));
+ }
+ response.Set("headers", std::move(headers));
+ response.Set("body", resource.response().body());
+ response.Set("remote_ip", resource.response().remote_ip());
+ resource_dict.Set("response", std::move(response));
+ }
+ resource_dict.Set("parent_id", resource.parent_id());
+ base::Value::List child_id_list;
+ for (const int& child_id : resource.child_ids()) {
+ child_id_list.Append(child_id);
+ }
+ resource_dict.Set("child_ids", std::move(child_id_list));
+ resource_dict.Set("tag_name", resource.tag_name());
+ return resource_dict;
+}
+
+base::Value::Dict SerializeHTMLElement(const HTMLElement& element) {
+ base::Value::Dict element_dict;
+ element_dict.Set("id", element.id());
+ element_dict.Set("tag", element.tag());
+ base::Value::List child_id_lists;
+ for (const int& child_id : element.child_ids()) {
+ child_id_lists.Append(child_id);
+ }
+ element_dict.Set("child_ids", std::move(child_id_lists));
+ element_dict.Set("resource_id", element.resource_id());
+ base::Value::List attribute_list;
+ for (const HTMLElement::Attribute& attribute : element.attribute()) {
+ base::Value::Dict attribute_dict;
+ attribute_dict.Set("name", attribute.name());
+ attribute_dict.Set("value", attribute.value());
+ attribute_list.Append(std::move(attribute_dict));
+ }
+ element_dict.Set("attribute", std::move(attribute_list));
+ element_dict.Set("inner_html", element.inner_html());
+ return element_dict;
+}
+
+base::Value::Dict SerializeSafeBrowsingClientProperties(
+ const ClientSafeBrowsingReportRequest::SafeBrowsingClientProperties&
+ client_properties) {
+ base::Value::Dict client_properties_dict;
+ client_properties_dict.Set("client_version",
+ client_properties.client_version());
+ client_properties_dict.Set(
+ "google_play_services_version",
+ static_cast<int>(client_properties.google_play_services_version()));
+ client_properties_dict.Set("is_instant_apps",
+ client_properties.is_instant_apps());
+ std::string url_api_type;
+ switch (client_properties.url_api_type()) {
+ case ClientSafeBrowsingReportRequest::
+ SAFE_BROWSING_URL_API_TYPE_UNSPECIFIED:
+ url_api_type = "SAFE_BROWSING_URL_API_TYPE_UNSPECIFIED";
+ break;
+ case ClientSafeBrowsingReportRequest::PVER4_NATIVE:
+ url_api_type = "PVER4_NATIVE";
+ break;
+ case ClientSafeBrowsingReportRequest::ANDROID_SAFETYNET:
+ url_api_type = "ANDROID_SAFETYNET";
+ break;
+ case ClientSafeBrowsingReportRequest::REAL_TIME:
+ url_api_type = "REAL_TIME";
+ break;
+ default:
+ NOTREACHED();
+ url_api_type = "";
+ }
+ client_properties_dict.Set("url_api_type", url_api_type);
+ return client_properties_dict;
+}
+
std::string SerializeCSBRR(const ClientSafeBrowsingReportRequest& report) {
- base::DictionaryValue report_request;
+ base::Value::Dict report_request;
if (report.has_type()) {
std::string report_type;
switch (report.type()) {
@@ -1125,53 +1273,142 @@ std::string SerializeCSBRR(const ClientSafeBrowsingReportRequest& report) {
report_type = "BLOCKED_AD_POPUP";
break;
}
- report_request.SetString("type", report_type);
+ report_request.Set("type", report_type);
+ }
+ if (report.has_page_url()) {
+ report_request.Set("page_url", report.page_url());
+ }
+ if (report.has_referrer_url()) {
+ report_request.Set("referrer_url", report.referrer_url());
}
- if (report.has_page_url())
- report_request.SetString("page_url", report.page_url());
if (report.has_client_country()) {
- report_request.SetString("client_country", report.client_country());
+ report_request.Set("client_country", report.client_country());
}
if (report.has_repeat_visit()) {
- report_request.SetInteger("repeat_visit", report.repeat_visit());
+ report_request.Set("repeat_visit", report.repeat_visit());
}
if (report.has_did_proceed()) {
- report_request.SetInteger("did_proceed", report.did_proceed());
+ report_request.Set("did_proceed", report.did_proceed());
}
if (report.has_download_verdict()) {
- report_request.SetString(
+ report_request.Set(
"download_verdict",
ClientDownloadResponseVerdictToString(report.download_verdict()));
}
if (report.has_url()) {
- report_request.SetString("url", report.url());
+ report_request.Set("url", report.url());
}
if (report.has_token()) {
- report_request.SetString("token", report.token());
+ report_request.Set("token", report.token());
}
if (report.has_show_download_in_folder()) {
- report_request.SetBoolean("show_download_in_folder",
- report.show_download_in_folder());
+ report_request.Set("show_download_in_folder",
+ report.show_download_in_folder());
}
if (report.has_population()) {
- report_request.SetKey("population",
- SerializeChromeUserPopulation(report.population()));
+ report_request.Set("population",
+ SerializeChromeUserPopulation(report.population()));
+ }
+ base::Value::List resource_list;
+ for (const ClientSafeBrowsingReportRequest::Resource& resource :
+ report.resources()) {
+ resource_list.Append(SerializeResource(resource));
+ }
+ report_request.Set("resources", std::move(resource_list));
+ base::Value::List dom_list;
+ for (const HTMLElement& element : report.dom()) {
+ dom_list.Append(SerializeHTMLElement(element));
+ }
+ report_request.Set("dom", std::move(dom_list));
+ if (report.has_complete()) {
+ report_request.Set("complete", report.complete());
+ }
+ if (report.has_client_properties()) {
+ report_request.Set(
+ "client_properties",
+ SerializeSafeBrowsingClientProperties(report.client_properties()));
}
std::string serialized;
if (report.SerializeToString(&serialized)) {
std::string base64_encoded;
base::Base64Encode(serialized, &base64_encoded);
- report_request.SetString("csbrr(base64)", base64_encoded);
+ report_request.Set("csbrr(base64)", base64_encoded);
}
-
- base::Value* report_request_tree = &report_request;
std::string report_request_serialized;
JSONStringValueSerializer serializer(&report_request_serialized);
serializer.set_pretty_print(true);
- serializer.Serialize(*report_request_tree);
+ serializer.Serialize(report_request);
return report_request_serialized;
}
+std::string SerializeHitReport(const HitReport& hit_report) {
+ base::Value::Dict hit_report_dict;
+ hit_report_dict.Set("malicious_url", hit_report.malicious_url.spec());
+ hit_report_dict.Set("page_url", hit_report.page_url.spec());
+ hit_report_dict.Set("referrer_url", hit_report.referrer_url.spec());
+ hit_report_dict.Set("is_subresource", hit_report.is_subresource);
+ std::string threat_type;
+ switch (hit_report.threat_type) {
+ case SBThreatType::SB_THREAT_TYPE_URL_PHISHING:
+ threat_type = "SB_THREAT_TYPE_URL_PHISHING";
+ break;
+ case SBThreatType::SB_THREAT_TYPE_URL_MALWARE:
+ threat_type = "SB_THREAT_TYPE_URL_MALWARE";
+ break;
+ case SBThreatType::SB_THREAT_TYPE_URL_UNWANTED:
+ threat_type = "SB_THREAT_TYPE_URL_UNWANTED";
+ break;
+ case SBThreatType::SB_THREAT_TYPE_URL_BINARY_MALWARE:
+ threat_type = "SB_THREAT_TYPE_URL_BINARY_MALWARE";
+ break;
+ default:
+ threat_type = "OTHER";
+ }
+ hit_report_dict.Set("threat_type", threat_type);
+ std::string threat_source;
+ switch (hit_report.threat_source) {
+ case ThreatSource::LOCAL_PVER4:
+ threat_source = "LOCAL_PVER4";
+ break;
+ case ThreatSource::REMOTE:
+ threat_source = "REMOTE";
+ break;
+ case ThreatSource::CLIENT_SIDE_DETECTION:
+ threat_source = "CLIENT_SIDE_DETECTION";
+ break;
+ case ThreatSource::REAL_TIME_CHECK:
+ threat_source = "REAL_TIME_CHECK";
+ break;
+ case ThreatSource::UNKNOWN:
+ threat_source = "UNKNOWN";
+ break;
+ }
+ hit_report_dict.Set("threat_source", threat_source);
+ std::string extended_reporting_level;
+ switch (hit_report.extended_reporting_level) {
+ case ExtendedReportingLevel::SBER_LEVEL_OFF:
+ extended_reporting_level = "SBER_LEVEL_OFF";
+ break;
+ case ExtendedReportingLevel::SBER_LEVEL_LEGACY:
+ extended_reporting_level = "SBER_LEVEL_LEGACY";
+ break;
+ case ExtendedReportingLevel::SBER_LEVEL_SCOUT:
+ extended_reporting_level = "SBER_LEVEL_SCOUT";
+ break;
+ }
+ hit_report_dict.Set("extended_reporting_level", extended_reporting_level);
+ hit_report_dict.Set("is_enhanced_protection",
+ hit_report.is_enhanced_protection);
+ hit_report_dict.Set("is_metrics_reporting_active",
+ hit_report.is_metrics_reporting_active);
+ hit_report_dict.Set("post_data", hit_report.post_data);
+ std::string hit_report_serialized;
+ JSONStringValueSerializer serializer(&hit_report_serialized);
+ serializer.set_pretty_print(true);
+ serializer.Serialize(hit_report_dict);
+ return hit_report_serialized;
+}
+
base::Value SerializeReuseLookup(
const PasswordReuseLookup password_reuse_lookup) {
std::string lookup_result;
@@ -2013,6 +2250,11 @@ SafeBrowsingUI::SafeBrowsingUI(content::WebUI* web_ui)
html_source->AddResourcePath("safe_browsing.js", IDR_SAFE_BROWSING_JS);
html_source->SetDefaultResource(IDR_SAFE_BROWSING_HTML);
+ // Static types
+ html_source->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::TrustedTypes,
+ "trusted-types static-types;");
+
content::WebUIDataSource::Add(browser_context, html_source);
}
@@ -2317,6 +2559,22 @@ void SafeBrowsingUIHandler::GetSentCSBRRs(const base::Value::List& args) {
ResolveJavascriptCallback(base::Value(callback_id), sent_reports);
}
+void SafeBrowsingUIHandler::GetSentHitReports(const base::Value::List& args) {
+ const std::vector<std::unique_ptr<HitReport>>& reports =
+ WebUIInfoSingleton::GetInstance()->hit_reports_sent();
+
+ base::ListValue sent_reports;
+
+ for (const auto& report : reports) {
+ sent_reports.Append(base::Value(SerializeHitReport(*report)));
+ }
+
+ AllowJavascript();
+ DCHECK(!args.empty());
+ std::string callback_id = args[0].GetString();
+ ResolveJavascriptCallback(base::Value(callback_id), sent_reports);
+}
+
void SafeBrowsingUIHandler::GetPGEvents(const base::Value::List& args) {
const std::vector<sync_pb::UserEventSpecifics>& events =
WebUIInfoSingleton::GetInstance()->pg_event_log();
@@ -2573,6 +2831,12 @@ void SafeBrowsingUIHandler::NotifyCSBRRJsListener(
FireWebUIListener("sent-csbrr-update", base::Value(SerializeCSBRR(*csbrr)));
}
+void SafeBrowsingUIHandler::NotifyHitReportJsListener(HitReport* hit_report) {
+ AllowJavascript();
+ FireWebUIListener("sent-hit-report-list",
+ base::Value(SerializeHitReport(*hit_report)));
+}
+
void SafeBrowsingUIHandler::NotifyPGEventJsListener(
const sync_pb::UserEventSpecifics& event) {
AllowJavascript();
@@ -2702,6 +2966,10 @@ void SafeBrowsingUIHandler::RegisterMessages() {
base::BindRepeating(&SafeBrowsingUIHandler::GetSentCSBRRs,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "getSentHitReports",
+ base::BindRepeating(&SafeBrowsingUIHandler::GetSentHitReports,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"getPGEvents", base::BindRepeating(&SafeBrowsingUIHandler::GetPGEvents,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
diff --git a/chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h b/chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
index ac8c92ce535..d58119605f4 100644
--- a/chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
+++ b/chromium/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
@@ -12,7 +12,9 @@
#include "build/build_config.h"
#include "components/safe_browsing/buildflags.h"
#include "components/safe_browsing/content/browser/safe_browsing_service_interface.h"
+#include "components/safe_browsing/core/browser/db/hit_report.h"
#include "components/safe_browsing/core/browser/download_check_result.h"
+#include "components/safe_browsing/core/browser/ping_manager.h"
#include "components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "components/safe_browsing/core/common/proto/realtimeapi.pb.h"
@@ -140,6 +142,10 @@ class SafeBrowsingUIHandler : public content::WebUIMessageHandler {
// open chrome://safe-browsing tab was opened.
void GetSentCSBRRs(const base::Value::List& args);
+ // Get the HitReports that have been collected since the oldest currently
+ // open chrome://safe-browsing tab was opened.
+ void GetSentHitReports(const base::Value::List& args);
+
// Get the PhishGuard events that have been collected since the oldest
// currently open chrome://safe-browsing tab was opened.
void GetPGEvents(const base::Value::List& args);
@@ -221,6 +227,10 @@ class SafeBrowsingUIHandler : public content::WebUIMessageHandler {
// sent, while one or more WebUI tabs are opened.
void NotifyCSBRRJsListener(ClientSafeBrowsingReportRequest* csbrr);
+ // Get the new HitReport messages sent from PingManager when a ping is
+ // sent, while one or more WebUI tabs are opened.
+ void NotifyHitReportJsListener(HitReport* hit_report);
+
// Called when any new PhishGuard events are sent while one or more WebUI tabs
// are open.
void NotifyPGEventJsListener(const sync_pb::UserEventSpecifics& event);
@@ -292,8 +302,12 @@ class SafeBrowsingUI : public content::WebUIController {
~SafeBrowsingUI() override;
};
-class WebUIInfoSingleton : public SafeBrowsingUrlCheckerImpl::WebUIDelegate {
+class WebUIInfoSingleton : public SafeBrowsingUrlCheckerImpl::WebUIDelegate,
+ public PingManager::WebUIDelegate {
public:
+ WebUIInfoSingleton();
+ ~WebUIInfoSingleton() override;
+
static WebUIInfoSingleton* GetInstance();
WebUIInfoSingleton(const WebUIInfoSingleton&) = delete;
@@ -343,13 +357,24 @@ class WebUIInfoSingleton : public SafeBrowsingUrlCheckerImpl::WebUIDelegate {
// Clear the list of the received ClientPhishingResponse messages.
void ClearClientPhishingResponsesReceived();
+ // PingManager::WebUIDelegate:
// Add the new message in |csbrrs_sent_| and send it to all the open
// chrome://safe-browsing tabs.
- void AddToCSBRRsSent(std::unique_ptr<ClientSafeBrowsingReportRequest> csbrr);
+ void AddToCSBRRsSent(
+ std::unique_ptr<ClientSafeBrowsingReportRequest> csbrr) override;
// Clear the list of the sent ClientSafeBrowsingReportRequest messages.
void ClearCSBRRsSent();
+ void SetOnCSBRRLoggedCallbackForTesting(base::OnceClosure on_done);
+
+ // Add the new message in |hit_reports_sent_| and send it to all the open
+ // chrome://safe-browsing tabs.
+ void AddToHitReportsSent(std::unique_ptr<HitReport> hit_report);
+
+ // Clear the list of the sent HitReport messages.
+ void ClearHitReportsSent();
+
// Add the new message in |pg_event_log_| and send it to all the open
// chrome://safe-browsing tabs.
void AddToPGEvents(const sync_pb::UserEventSpecifics& event);
@@ -475,6 +500,12 @@ class WebUIInfoSingleton : public SafeBrowsingUrlCheckerImpl::WebUIDelegate {
return csbrrs_sent_;
}
+ // Get the list of the sent HitReports that have been collected since the
+ // oldest currently open chrome://safe-browsing tab was opened.
+ const std::vector<std::unique_ptr<HitReport>>& hit_reports_sent() const {
+ return hit_reports_sent_;
+ }
+
// Get the list of WebUI listener objects.
const std::vector<SafeBrowsingUIHandler*>& webui_instances() const {
return webui_instances_;
@@ -554,9 +585,6 @@ class WebUIInfoSingleton : public SafeBrowsingUrlCheckerImpl::WebUIDelegate {
void ClearListenerForTesting();
private:
- WebUIInfoSingleton();
- ~WebUIInfoSingleton() override;
-
void MaybeClearData();
friend struct base::DefaultSingletonTraits<WebUIInfoSingleton>;
@@ -599,6 +627,15 @@ class WebUIInfoSingleton : public SafeBrowsingUrlCheckerImpl::WebUIDelegate {
// functions that call AllowJavascript(), which is not marked const.
std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>> csbrrs_sent_;
+ // Gets fired at the end of the AddToCSBRRsSent function. Only used for tests.
+ base::OnceClosure on_csbrr_logged_for_testing_;
+
+ // List of HitReports sent since since the oldest currently open
+ // chrome://safe-browsing tab was opened.
+ // "HitReport" cannot be const, due to being used by
+ // functions that call AllowJavascript(), which is not marked const.
+ std::vector<std::unique_ptr<HitReport>> hit_reports_sent_;
+
// List of PhishGuard events sent since the oldest currently open
// chrome://safe-browsing tab was opened.
std::vector<sync_pb::UserEventSpecifics> pg_event_log_;
diff --git a/chromium/components/safe_browsing/content/common/file_type_policies_policy_util.cc b/chromium/components/safe_browsing/content/common/file_type_policies_policy_util.cc
index 8a787c6327a..15df3fae14a 100644
--- a/chromium/components/safe_browsing/content/common/file_type_policies_policy_util.cc
+++ b/chromium/components/safe_browsing/content/common/file_type_policies_policy_util.cc
@@ -64,7 +64,7 @@ bool IsInNotDangerousOverrideList(const std::string& extension,
if (!domains_for_extension.GetListDeprecated().empty()) {
url_matcher::URLMatcher matcher;
- url_matcher::URLMatcherConditionSet::ID id(0);
+ base::MatcherStringPattern::ID id(0);
url_matcher::util::AddFilters(&matcher, true, &id,
&domains_for_extension);
auto matching_set_size = matcher.MatchURL(normalized_url).size();
diff --git a/chromium/components/safe_browsing/content/common/safe_browsing.mojom b/chromium/components/safe_browsing/content/common/safe_browsing.mojom
index 1f9144fb17e..9b95cbf812b 100644
--- a/chromium/components/safe_browsing/content/common/safe_browsing.mojom
+++ b/chromium/components/safe_browsing/content/common/safe_browsing.mojom
@@ -121,8 +121,21 @@ enum PhishingDetectorResult {
};
[EnableIf=full_safe_browsing]
-// Interface for setting the CSD model and to start phishing classification.
+// Interface for starting phishing classification. This is scoped to a
+// particular RenderFrame.
interface PhishingDetector {
+ // Tells the renderer to begin phishing detection for the given toplevel URL
+ // which it has started loading. Returns the serialized request proto and a
+ // |result| enum to indicate failure. If the URL is phishing the request proto
+ // will have |is_phishing()| set to true.
+ StartPhishingDetection(url.mojom.Url url)
+ => (PhishingDetectorResult result, string request_proto);
+};
+
+[EnableIf=full_safe_browsing]
+// Interface for setting a phishing model. This is scoped to an entire
+// RenderProcess.
+interface PhishingModelSetter {
// A classification model for client-side phishing detection.
// The string is an encoded safe_browsing::ClientSideModel protocol buffer, or
// empty to disable client-side phishing detection for this renderer. The
@@ -140,10 +153,15 @@ interface PhishingDetector {
SetPhishingFlatBufferModel(mojo_base.mojom.ReadOnlySharedMemoryRegion region,
mojo_base.mojom.ReadOnlyFile? tflite_model);
- // Tells the renderer to begin phishing detection for the given toplevel URL
- // which it has started loading. Returns the serialized request proto and a
- // |result| enum to indicate failure. If the URL is phishing the request proto
- // will have |is_phishing()| set to true.
- StartPhishingDetection(url.mojom.Url url)
- => (PhishingDetectorResult result, string request_proto);
+ // This is used in tests to ensure that the model has been set before sending
+ // IPCs to start classification.
+ SetTestObserver(pending_remote<PhishingModelSetterTestObserver>? observer) =>
+ ();
+};
+
+// This is used in tests to ensure that the model has been set before sending
+// IPCs to start classification.
+interface PhishingModelSetterTestObserver {
+ // Called whenever the phishing model has changed.
+ PhishingModelUpdated();
};
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/BUILD.gn b/chromium/components/safe_browsing/content/renderer/phishing_classifier/BUILD.gn
index df0ebca7465..cba75f005b9 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/BUILD.gn
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/BUILD.gn
@@ -23,6 +23,8 @@ source_set("phishing_classifier") {
"phishing_classifier_delegate.h",
"phishing_dom_feature_extractor.cc",
"phishing_dom_feature_extractor.h",
+ "phishing_model_setter_impl.cc",
+ "phishing_model_setter_impl.h",
"phishing_term_feature_extractor.cc",
"phishing_term_feature_extractor.h",
"phishing_url_feature_extractor.cc",
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc b/chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc
index d93c10e1c9d..0e8954fdca9 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc
@@ -94,7 +94,7 @@ FlatBufferModelScorer::FlatBufferModelScorer() = default;
FlatBufferModelScorer::~FlatBufferModelScorer() = default;
/* static */
-FlatBufferModelScorer* FlatBufferModelScorer::Create(
+std::unique_ptr<FlatBufferModelScorer> FlatBufferModelScorer::Create(
base::ReadOnlySharedMemoryRegion region,
base::File visual_tflite_model) {
std::unique_ptr<FlatBufferModelScorer> scorer(new FlatBufferModelScorer());
@@ -142,7 +142,7 @@ FlatBufferModelScorer* FlatBufferModelScorer::Create(
RecordScorerCreationStatus(SCORER_SUCCESS);
scorer->flatbuffer_mapping_ = std::move(mapping);
- return scorer.release();
+ return scorer;
}
double FlatBufferModelScorer::ComputeRuleScore(
@@ -205,6 +205,10 @@ int FlatBufferModelScorer::model_version() const {
return flatbuffer_model_->version();
}
+int FlatBufferModelScorer::dom_model_version() const {
+ return flatbuffer_model_->dom_model_version();
+}
+
bool FlatBufferModelScorer::has_page_term(const std::string& str) const {
const flatbuffers::Vector<flatbuffers::Offset<flat::Hash>>* hashes =
flatbuffer_model_->hashes();
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h b/chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h
index b4622f91d56..a34974ad6ac 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h
@@ -43,8 +43,9 @@ class FlatBufferModelScorer : public Scorer {
// Factory method which creates a new Scorer object by parsing the given
// flatbuffer or tflite model. If parsing fails this method returns NULL.
// Use this only if region is valid.
- static FlatBufferModelScorer* Create(base::ReadOnlySharedMemoryRegion region,
- base::File visual_tflite_model);
+ static std::unique_ptr<FlatBufferModelScorer> Create(
+ base::ReadOnlySharedMemoryRegion region,
+ base::File visual_tflite_model);
double ComputeScore(const FeatureMap& features) const override;
@@ -55,6 +56,7 @@ class FlatBufferModelScorer : public Scorer {
#endif
int model_version() const override;
+ int dom_model_version() const override;
size_t max_words_per_term() const override;
uint32_t murmurhash3_seed() const override;
size_t max_shingles_per_page() const override;
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.cc b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.cc
index 9fcd0153493..c382b715109 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.cc
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.cc
@@ -76,7 +76,7 @@ const float PhishingClassifier::kInvalidScore = -1.0;
const float PhishingClassifier::kPhishyThreshold = 0.5;
PhishingClassifier::PhishingClassifier(content::RenderFrame* render_frame)
- : render_frame_(render_frame), scorer_(nullptr) {
+ : render_frame_(render_frame) {
Clear();
}
@@ -87,28 +87,8 @@ PhishingClassifier::~PhishingClassifier() {
DCHECK(!page_text_);
}
-void PhishingClassifier::set_phishing_scorer(const Scorer* scorer) {
- DCHECK(done_callback_.is_null());
- DCHECK(!page_text_);
- scorer_ = scorer;
- if (scorer_) {
- url_extractor_ = std::make_unique<PhishingUrlFeatureExtractor>();
- dom_extractor_ = std::make_unique<PhishingDOMFeatureExtractor>();
- term_extractor_ = std::make_unique<PhishingTermFeatureExtractor>(
- scorer_->find_page_term_callback(), scorer_->find_page_word_callback(),
- scorer_->max_words_per_term(), scorer_->murmurhash3_seed(),
- scorer_->max_shingles_per_page(), scorer_->shingle_size());
- } else {
- // We're disabling client-side phishing detection, so tear down all
- // of the relevant objects.
- url_extractor_.reset();
- dom_extractor_.reset();
- term_extractor_.reset();
- }
-}
-
bool PhishingClassifier::is_ready() const {
- return !!scorer_;
+ return !!ScorerStorage::GetInstance()->GetScorer();
}
void PhishingClassifier::BeginClassification(const std::u16string* page_text,
@@ -125,6 +105,13 @@ void PhishingClassifier::BeginClassification(const std::u16string* page_text,
// classification so that we can start in a known state.
CancelPendingClassification();
+ Scorer* scorer = ScorerStorage::GetInstance()->GetScorer();
+ url_extractor_ = std::make_unique<PhishingUrlFeatureExtractor>();
+ dom_extractor_ = std::make_unique<PhishingDOMFeatureExtractor>();
+ term_extractor_ = std::make_unique<PhishingTermFeatureExtractor>(
+ scorer->find_page_term_callback(), scorer->find_page_word_callback(),
+ scorer->max_words_per_term(), scorer->murmurhash3_seed(),
+ scorer->max_shingles_per_page(), scorer->shingle_size());
page_text_ = page_text;
done_callback_ = std::move(done_callback);
@@ -172,8 +159,8 @@ void PhishingClassifier::CancelPendingClassification() {
// Note that cancelling the feature extractors is simply a no-op if they
// were not running.
DCHECK(is_ready());
- dom_extractor_->CancelPendingExtraction();
- term_extractor_->CancelPendingExtraction();
+ dom_extractor_.reset();
+ term_extractor_.reset();
weak_factory_.InvalidateWeakPtrs();
Clear();
}
@@ -197,7 +184,7 @@ void PhishingClassifier::TermExtractionFinished(bool success) {
#if BUILDFLAG(FULL_SAFE_BROWSING)
ExtractVisualFeatures();
#else
- if (scorer_->HasVisualTfLiteModel()) {
+ if (ScorerStorage::GetInstance()->GetScorer()->HasVisualTfLiteModel()) {
ExtractVisualFeatures();
} else {
VisualExtractionFinished(true);
@@ -280,10 +267,12 @@ void PhishingClassifier::VisualExtractionFinished(bool success) {
// Hash all of the features so that they match the model, then compute
// the score.
+ Scorer* scorer = ScorerStorage::GetInstance()->GetScorer();
FeatureMap hashed_features;
std::unique_ptr<ClientPhishingRequest> verdict =
std::make_unique<ClientPhishingRequest>();
- verdict->set_model_version(scorer_->model_version());
+ verdict->set_model_version(scorer->model_version());
+ verdict->set_dom_model_version(scorer->dom_model_version());
verdict->set_url(main_frame->GetDocument().Url().GetString().Utf8());
for (const auto& it : features_->features()) {
bool result = hashed_features.AddRealFeature(
@@ -296,9 +285,9 @@ void PhishingClassifier::VisualExtractionFinished(bool success) {
for (const auto& it : *shingle_hashes_) {
verdict->add_shingle_hashes(it);
}
- float score = static_cast<float>(scorer_->ComputeScore(hashed_features));
+ float score = static_cast<float>(scorer->ComputeScore(hashed_features));
verdict->set_client_score(score);
- bool is_dom_match = (score >= scorer_->threshold_probability());
+ bool is_dom_match = (score >= scorer->threshold_probability());
verdict->set_is_phishing(is_dom_match);
verdict->set_is_dom_match(is_dom_match);
if (visual_features_) {
@@ -306,7 +295,7 @@ void PhishingClassifier::VisualExtractionFinished(bool success) {
}
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
- scorer_->ApplyVisualTfLiteModel(
+ ScorerStorage::GetInstance()->GetScorer()->ApplyVisualTfLiteModel(
*bitmap_, base::BindOnce(&PhishingClassifier::OnVisualTfLiteModelDone,
weak_factory_.GetWeakPtr(), std::move(verdict)));
#else
@@ -317,20 +306,21 @@ void PhishingClassifier::VisualExtractionFinished(bool success) {
void PhishingClassifier::OnVisualTfLiteModelDone(
std::unique_ptr<ClientPhishingRequest> verdict,
std::vector<double> result) {
- if (static_cast<int>(result.size()) > scorer_->tflite_thresholds().size()) {
+ Scorer* scorer = ScorerStorage::GetInstance()->GetScorer();
+ if (static_cast<int>(result.size()) > scorer->tflite_thresholds().size()) {
// Model is misconfigured, so bail out.
RunFailureCallback();
return;
}
- verdict->set_tflite_model_version(scorer_->tflite_model_version());
+ verdict->set_tflite_model_version(scorer->tflite_model_version());
for (size_t i = 0; i < result.size(); i++) {
ClientPhishingRequest::CategoryScore* category =
verdict->add_tflite_model_scores();
- category->set_label(scorer_->tflite_thresholds().at(i).label());
+ category->set_label(scorer->tflite_thresholds().at(i).label());
category->set_value(result[i]);
- if (result[i] >= scorer_->tflite_thresholds().at(i).threshold()) {
+ if (result[i] >= scorer->tflite_thresholds().at(i).threshold()) {
verdict->set_is_phishing(true);
verdict->set_is_tflite_match(true);
}
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.h b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.h
index add37ba47cb..5f44a8b5587 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.h
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier.h
@@ -28,6 +28,7 @@
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
+#include "components/safe_browsing/content/renderer/phishing_classifier/scorer.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace content {
@@ -66,12 +67,6 @@ class PhishingClassifier {
virtual ~PhishingClassifier();
- // Sets a scorer for the classifier to use in computing the phishiness score.
- // This must live at least as long as the PhishingClassifier. The caller is
- // expected to cancel any pending classification before setting a phishing
- // scorer.
- void set_phishing_scorer(const Scorer* scorer);
-
// Returns true if the classifier is ready to classify pages, i.e. it
// has had a scorer set via set_phishing_scorer().
bool is_ready() const;
@@ -152,7 +147,6 @@ class PhishingClassifier {
void Clear();
content::RenderFrame* render_frame_; // owns us
- const Scorer* scorer_; // owned by the caller
std::unique_ptr<PhishingUrlFeatureExtractor> url_extractor_;
std::unique_ptr<PhishingDOMFeatureExtractor> dom_extractor_;
std::unique_ptr<PhishingTermFeatureExtractor> term_extractor_;
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc
index 21e546c0a21..c894382c9c6 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc
@@ -24,6 +24,7 @@
#include "content/public/renderer/render_thread.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_local_frame.h"
@@ -41,14 +42,6 @@ GURL StripRef(const GURL& url) {
return url.ReplaceComponents(replacements);
}
-std::set<PhishingClassifierDelegate*>& PhishingClassifierDelegates() {
- static base::NoDestructor<std::set<PhishingClassifierDelegate*>> s;
- return *s;
-}
-
-base::LazyInstance<std::unique_ptr<const safe_browsing::Scorer>>::
- DestructorAtExit g_phishing_scorer = LAZY_INSTANCE_INITIALIZER;
-
} // namespace
PhishingClassifierDelegate::PhishingClassifierDelegate(
@@ -58,57 +51,21 @@ PhishingClassifierDelegate::PhishingClassifierDelegate(
last_main_frame_transition_(ui::PAGE_TRANSITION_LINK),
have_page_text_(false),
is_classifying_(false) {
- PhishingClassifierDelegates().insert(this);
if (!classifier) {
classifier = new PhishingClassifier(render_frame);
}
classifier_.reset(classifier);
- if (g_phishing_scorer.Get().get())
- SetPhishingScorer(g_phishing_scorer.Get().get());
-
- registry_.AddInterface(
+ render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
base::BindRepeating(&PhishingClassifierDelegate::PhishingDetectorReceiver,
base::Unretained(this)));
+
+ model_change_observation_.Observe(ScorerStorage::GetInstance());
}
PhishingClassifierDelegate::~PhishingClassifierDelegate() {
CancelPendingClassification(SHUTDOWN);
- PhishingClassifierDelegates().erase(this);
-}
-
-void PhishingClassifierDelegate::SetPhishingModel(
- const std::string& model,
- base::File tflite_visual_model) {
- safe_browsing::Scorer* scorer = nullptr;
- // An empty model string means we should disable client-side phishing
- // detection.
- if (!model.empty()) {
- scorer = safe_browsing::ProtobufModelScorer::Create(
- model, std::move(tflite_visual_model));
- if (!scorer)
- return;
- }
- for (auto* delegate : PhishingClassifierDelegates())
- delegate->SetPhishingScorer(scorer);
- g_phishing_scorer.Get().reset(scorer);
-}
-
-void PhishingClassifierDelegate::SetPhishingFlatBufferModel(
- base::ReadOnlySharedMemoryRegion flatbuffer_region,
- base::File tflite_visual_model) {
- safe_browsing::Scorer* scorer = nullptr;
- // An invalid region means we should disable client-side phishing detection.
- if (flatbuffer_region.IsValid()) {
- scorer = safe_browsing::FlatBufferModelScorer::Create(
- std::move(flatbuffer_region), std::move(tflite_visual_model));
- if (!scorer)
- return;
- }
- for (auto* delegate : PhishingClassifierDelegates())
- delegate->SetPhishingScorer(scorer);
- g_phishing_scorer.Get().reset(scorer);
}
// static
@@ -120,29 +77,10 @@ PhishingClassifierDelegate* PhishingClassifierDelegate::Create(
return new PhishingClassifierDelegate(render_frame, classifier);
}
-void PhishingClassifierDelegate::SetPhishingScorer(
- const safe_browsing::Scorer* scorer) {
- if (is_classifying_) {
- // If there is a classification going on right now it means we're
- // actually replacing an existing scorer with a new model. In
- // this case we simply cancel the current classification.
- // TODO(noelutz): if this happens too frequently we could also
- // replace the old scorer with the new one once classification is done
- // but this would complicate the code somewhat.
- CancelPendingClassification(NEW_PHISHING_SCORER);
- }
- classifier_->set_phishing_scorer(scorer);
-}
-
void PhishingClassifierDelegate::PhishingDetectorReceiver(
- mojo::PendingReceiver<mojom::PhishingDetector> receiver) {
- phishing_detector_receivers_.Add(this, std::move(receiver));
-}
-
-void PhishingClassifierDelegate::OnInterfaceRequestForFrame(
- const std::string& interface_name,
- mojo::ScopedMessagePipeHandle* interface_pipe) {
- registry_.TryBindInterface(interface_name, interface_pipe);
+ mojo::PendingAssociatedReceiver<mojom::PhishingDetector> receiver) {
+ phishing_detector_receiver_.reset();
+ phishing_detector_receiver_.Bind(std::move(receiver));
}
void PhishingClassifierDelegate::StartPhishingDetection(
@@ -179,6 +117,10 @@ void PhishingClassifierDelegate::DidFinishSameDocumentNavigation() {
CancelPendingClassification(NAVIGATE_WITHIN_PAGE);
}
+bool PhishingClassifierDelegate::is_ready() {
+ return classifier_->is_ready();
+}
+
void PhishingClassifierDelegate::PageCaptured(std::u16string* page_text,
bool preliminary_capture) {
RecordEvent(SBPhishingClassifierEvent::kPageTextCaptured);
@@ -307,4 +249,13 @@ void PhishingClassifierDelegate::OnDestruct() {
delete this;
}
+void PhishingClassifierDelegate::OnScorerChanged() {
+ if (is_classifying_) {
+ // If there is a classification going on right now it means we're
+ // actually replacing an existing scorer with a new model. In
+ // this case we simply cancel the current classification.
+ CancelPendingClassification(NEW_PHISHING_SCORER);
+ }
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.h b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.h
index 02a315a2ca2..a7a7fc6bd24 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.h
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.h
@@ -11,9 +11,12 @@
#include <string>
#include "base/memory/read_only_shared_memory_region.h"
+#include "base/scoped_observation.h"
#include "components/safe_browsing/content/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/content/renderer/phishing_classifier/scorer.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_thread_observer.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "services/service_manager/public/cpp/binder_registry.h"
@@ -39,7 +42,8 @@ enum class SBPhishingClassifierEvent {
};
class PhishingClassifierDelegate : public content::RenderFrameObserver,
- public mojom::PhishingDetector {
+ public mojom::PhishingDetector,
+ public ScorerStorage::Observer {
public:
// The RenderFrame owns us. This object takes ownership of the classifier.
// Note that if classifier is null, a default instance of PhishingClassifier
@@ -53,19 +57,6 @@ class PhishingClassifierDelegate : public content::RenderFrameObserver,
~PhishingClassifierDelegate() override;
- // mojom::PhishingDetector
- void SetPhishingModel(const std::string& model,
- base::File tflite_visual_model) override;
-
- // mojom::PhishingDetector
- void SetPhishingFlatBufferModel(
- base::ReadOnlySharedMemoryRegion flatbuffer_region,
- base::File tflite_visual_model) override;
-
- // Called by the RenderFrame once there is a phishing scorer available.
- // The scorer is passed on to the classifier.
- void SetPhishingScorer(const safe_browsing::Scorer* scorer);
-
// Called by the RenderFrame once a page has finished loading. Updates the
// last-loaded URL and page text, then starts classification if all other
// conditions are met (see MaybeStartClassification for details).
@@ -83,6 +74,8 @@ class PhishingClassifierDelegate : public content::RenderFrameObserver,
// committed. We continue running the current classification.
void DidFinishSameDocumentNavigation() override;
+ bool is_ready();
+
private:
friend class PhishingClassifierDelegateTest;
@@ -99,7 +92,7 @@ class PhishingClassifierDelegate : public content::RenderFrameObserver,
};
void PhishingDetectorReceiver(
- mojo::PendingReceiver<mojom::PhishingDetector> receiver);
+ mojo::PendingAssociatedReceiver<mojom::PhishingDetector> receiver);
// Cancels any pending classification and frees the page text.
void CancelPendingClassification(CancelClassificationReason reason);
@@ -109,10 +102,6 @@ class PhishingClassifierDelegate : public content::RenderFrameObserver,
void OnDestruct() override;
- void OnInterfaceRequestForFrame(
- const std::string& interface_name,
- mojo::ScopedMessagePipeHandle* interface_pipe) override;
-
// mojom::PhishingDetector
// Called by the RenderFrame when it receives a StartPhishingDetection IPC
// from the browser. This signals that it is ok to begin classification
@@ -128,6 +117,9 @@ class PhishingClassifierDelegate : public content::RenderFrameObserver,
// Shared code to begin classification if all conditions are met.
void MaybeStartClassification();
+ // ScorerStorage::Observer implementation:
+ void OnScorerChanged() override;
+
// The PhishingClassifier to use for the RenderFrame. This is created once
// a scorer is made available via SetPhishingScorer().
std::unique_ptr<PhishingClassifier> classifier_;
@@ -173,9 +165,11 @@ class PhishingClassifierDelegate : public content::RenderFrameObserver,
// The callback from the most recent call to StartPhishingDetection.
StartPhishingDetectionCallback callback_;
- mojo::ReceiverSet<mojom::PhishingDetector> phishing_detector_receivers_;
+ mojo::AssociatedReceiver<mojom::PhishingDetector> phishing_detector_receiver_{
+ this};
- service_manager::BinderRegistry registry_;
+ base::ScopedObservation<ScorerStorage, ScorerStorage::Observer>
+ model_change_observation_{this};
};
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_dom_feature_extractor.cc b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_dom_feature_extractor.cc
index b9ec8776ca4..50d0e92c7b8 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_dom_feature_extractor.cc
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_dom_feature_extractor.cc
@@ -109,11 +109,7 @@ PhishingDOMFeatureExtractor::PhishingDOMFeatureExtractor()
}
PhishingDOMFeatureExtractor::~PhishingDOMFeatureExtractor() {
- // The RenderView should have called CancelPendingExtraction() before
- // we are destroyed.
- DCHECK(done_callback_.is_null());
- DCHECK(!cur_frame_data_.get());
- DCHECK(cur_document_.IsNull());
+ CancelPendingExtraction();
}
void PhishingDOMFeatureExtractor::ExtractFeatures(blink::WebDocument document,
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_model_setter_impl.cc b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_model_setter_impl.cc
new file mode 100644
index 00000000000..5f1521fbb58
--- /dev/null
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_model_setter_impl.cc
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium 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/safe_browsing/content/renderer/phishing_classifier/phishing_model_setter_impl.h"
+
+#include "components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.h"
+#include "components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h"
+#include "components/safe_browsing/content/renderer/phishing_classifier/scorer.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+
+namespace safe_browsing {
+
+PhishingModelSetterImpl::PhishingModelSetterImpl() = default;
+PhishingModelSetterImpl::~PhishingModelSetterImpl() = default;
+
+void PhishingModelSetterImpl::RegisterMojoInterfaces(
+ blink::AssociatedInterfaceRegistry* associated_interfaces) {
+ associated_interfaces->AddInterface(
+ base::BindRepeating(&PhishingModelSetterImpl::OnRendererAssociatedRequest,
+ base::Unretained(this)));
+}
+
+void PhishingModelSetterImpl::UnregisterMojoInterfaces(
+ blink::AssociatedInterfaceRegistry* associated_interfaces) {
+ associated_interfaces->RemoveInterface(mojom::PhishingModelSetter::Name_);
+}
+
+void PhishingModelSetterImpl::SetPhishingModel(const std::string& model,
+ base::File tflite_visual_model) {
+ std::unique_ptr<Scorer> scorer;
+
+ // An empty model string means we should disable client-side phishing
+ // detection.
+ if (!model.empty()) {
+ scorer = safe_browsing::ProtobufModelScorer::Create(
+ model, std::move(tflite_visual_model));
+ if (!scorer)
+ return;
+ }
+ ScorerStorage::GetInstance()->SetScorer(std::move(scorer));
+
+ if (observer_for_testing_.is_bound()) {
+ observer_for_testing_->PhishingModelUpdated();
+ }
+}
+
+void PhishingModelSetterImpl::SetPhishingFlatBufferModel(
+ base::ReadOnlySharedMemoryRegion flatbuffer_region,
+ base::File tflite_visual_model) {
+ std::unique_ptr<Scorer> scorer;
+ // An invalid region means we should disable client-side phishing detection.
+ if (flatbuffer_region.IsValid()) {
+ scorer = safe_browsing::FlatBufferModelScorer::Create(
+ std::move(flatbuffer_region), std::move(tflite_visual_model));
+ if (!scorer)
+ return;
+ }
+ ScorerStorage::GetInstance()->SetScorer(std::move(scorer));
+
+ if (observer_for_testing_.is_bound()) {
+ observer_for_testing_->PhishingModelUpdated();
+ }
+}
+
+void PhishingModelSetterImpl::SetTestObserver(
+ mojo::PendingRemote<mojom::PhishingModelSetterTestObserver> observer,
+ SetTestObserverCallback callback) {
+ if (observer_for_testing_.is_bound())
+ observer_for_testing_.reset();
+ observer_for_testing_.Bind(std::move(observer));
+ std::move(callback).Run();
+}
+
+void PhishingModelSetterImpl::OnRendererAssociatedRequest(
+ mojo::PendingAssociatedReceiver<mojom::PhishingModelSetter> receiver) {
+ receiver_.reset();
+ receiver_.Bind(std::move(receiver));
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_model_setter_impl.h b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_model_setter_impl.h
new file mode 100644
index 00000000000..99edf2aafb0
--- /dev/null
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_model_setter_impl.h
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_PHISHING_CLASSIFIER_PHISHING_MODEL_SETTER_IMPL_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_PHISHING_CLASSIFIER_PHISHING_MODEL_SETTER_IMPL_H_
+
+#include "components/safe_browsing/content/common/safe_browsing.mojom.h"
+#include "content/public/renderer/render_thread_observer.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace safe_browsing {
+
+class PhishingModelSetterImpl : public mojom::PhishingModelSetter,
+ public content::RenderThreadObserver {
+ public:
+ PhishingModelSetterImpl();
+
+ PhishingModelSetterImpl(const PhishingModelSetterImpl&) = delete;
+ PhishingModelSetterImpl& operator=(const PhishingModelSetterImpl&) = delete;
+
+ ~PhishingModelSetterImpl() override;
+
+ private:
+ // content::RenderThreadObserver overrides:
+ void RegisterMojoInterfaces(
+ blink::AssociatedInterfaceRegistry* associated_interfaces) override;
+ void UnregisterMojoInterfaces(
+ blink::AssociatedInterfaceRegistry* associated_interfaces) override;
+
+ // mojom::PhishingModelSetter overrides:
+ void SetPhishingModel(const std::string& model,
+ base::File tflite_visual_model) override;
+ void SetPhishingFlatBufferModel(
+ base::ReadOnlySharedMemoryRegion flatbuffer_region,
+ base::File tflite_visual_model) override;
+ void SetTestObserver(
+ mojo::PendingRemote<mojom::PhishingModelSetterTestObserver> observer,
+ SetTestObserverCallback callback) override;
+
+ void OnRendererAssociatedRequest(
+ mojo::PendingAssociatedReceiver<mojom::PhishingModelSetter> receiver);
+
+ mojo::Remote<mojom::PhishingModelSetterTestObserver> observer_for_testing_;
+
+ mojo::AssociatedReceiver<mojom::PhishingModelSetter> receiver_{this};
+};
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_PHISHING_CLASSIFIER_PHISHING_MODEL_SETTER_IMPL_H_
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_term_feature_extractor.cc b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_term_feature_extractor.cc
index 18eb048a822..f96720515ef 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_term_feature_extractor.cc
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/phishing_term_feature_extractor.cc
@@ -96,10 +96,7 @@ PhishingTermFeatureExtractor::PhishingTermFeatureExtractor(
}
PhishingTermFeatureExtractor::~PhishingTermFeatureExtractor() {
- // The RenderView should have called CancelPendingExtraction() before
- // we are destroyed.
- DCHECK(done_callback_.is_null());
- DCHECK(!state_.get());
+ CancelPendingExtraction();
}
void PhishingTermFeatureExtractor::ExtractFeatures(
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc b/chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc
index cf6e038164a..1ca421ec550 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc
@@ -41,7 +41,7 @@ ProtobufModelScorer::ProtobufModelScorer() = default;
ProtobufModelScorer::~ProtobufModelScorer() = default;
/* static */
-ProtobufModelScorer* ProtobufModelScorer::Create(
+std::unique_ptr<ProtobufModelScorer> ProtobufModelScorer::Create(
const base::StringPiece& model_str,
base::File visual_tflite_model) {
std::unique_ptr<ProtobufModelScorer> scorer(new ProtobufModelScorer());
@@ -86,7 +86,7 @@ ProtobufModelScorer* ProtobufModelScorer::Create(
}
RecordScorerCreationStatus(SCORER_SUCCESS);
- return scorer.release();
+ return scorer;
}
double ProtobufModelScorer::ComputeScore(const FeatureMap& features) const {
@@ -127,6 +127,10 @@ int ProtobufModelScorer::model_version() const {
return model_.version();
}
+int ProtobufModelScorer::dom_model_version() const {
+ return model_.dom_model_version();
+}
+
bool Scorer::HasVisualTfLiteModel() const {
return visual_tflite_model_.IsValid();
}
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h b/chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h
index db968ac8dee..4243d3ee20f 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.h
@@ -41,8 +41,9 @@ class ProtobufModelScorer : public Scorer {
// Factory method which creates a new Scorer object by parsing the given
// model. If parsing fails this method returns NULL.
// Can use this if model_str is empty.
- static ProtobufModelScorer* Create(const base::StringPiece& model_str,
- base::File visual_tflite_model);
+ static std::unique_ptr<ProtobufModelScorer> Create(
+ const base::StringPiece& model_str,
+ base::File visual_tflite_model);
double ComputeScore(const FeatureMap& features) const override;
@@ -53,6 +54,7 @@ class ProtobufModelScorer : public Scorer {
#endif
int model_version() const override;
+ int dom_model_version() const override;
base::RepeatingCallback<bool(uint32_t)> find_page_word_callback()
const override;
base::RepeatingCallback<bool(const std::string&)> find_page_term_callback()
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.cc b/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.cc
index 3fd872456b9..3c6899f40b7 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.cc
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.cc
@@ -10,6 +10,7 @@
#include <unordered_map>
#include <unordered_set>
+#include "base/logging.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/metrics/histogram_functions.h"
@@ -82,8 +83,13 @@ std::unique_ptr<tflite::task::vision::ImageClassifier> CreateClassifier(
std::string model_data) {
TRACE_EVENT0("safe_browsing", "CreateTfLiteClassifier");
tflite::task::vision::ImageClassifierOptions options;
- options.mutable_model_file_with_metadata()->set_file_content(
- std::move(model_data));
+ tflite::task::core::BaseOptions* base_options =
+ options.mutable_base_options();
+ base_options->mutable_model_file()->set_file_content(std::move(model_data));
+ base_options->mutable_compute_settings()
+ ->mutable_tflite_settings()
+ ->mutable_cpu_settings()
+ ->set_num_threads(1);
auto statusor_classifier =
tflite::task::vision::ImageClassifier::CreateFromOptions(
options, CreateOpResolver());
@@ -189,17 +195,14 @@ void Scorer::ApplyVisualTfLiteModelHelper(
const SkBitmap& bitmap,
int input_width,
int input_height,
- const std::string& model_data,
+ std::string model_data,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
base::OnceCallback<void(std::vector<double>)> callback) {
TRACE_EVENT0("safe_browsing", "ApplyVisualTfLiteModel");
base::Time before_operation = base::Time::Now();
- std::string model_data_copy = model_data;
- base::UmaHistogramTimes("SBClientPhishing.ApplyTfliteTime.ModelCopy",
- base::Time::Now() - before_operation);
before_operation = base::Time::Now();
std::unique_ptr<tflite::task::vision::ImageClassifier> classifier =
- CreateClassifier(std::move(model_data_copy));
+ CreateClassifier(std::move(model_data));
base::UmaHistogramTimes("SBClientPhishing.ApplyTfliteTime.CreateClassifier",
base::Time::Now() - before_operation);
if (!classifier) {
@@ -231,4 +234,31 @@ double Scorer::LogOdds2Prob(double log_odds) {
Scorer::Scorer() = default;
Scorer::~Scorer() = default;
+// static
+ScorerStorage* ScorerStorage::GetInstance() {
+ static base::NoDestructor<ScorerStorage> instance;
+ return instance.get();
+}
+
+ScorerStorage::ScorerStorage() = default;
+ScorerStorage::~ScorerStorage() = default;
+
+void ScorerStorage::SetScorer(std::unique_ptr<Scorer> scorer) {
+ scorer_ = std::move(scorer);
+ for (Observer& obs : observers_)
+ obs.OnScorerChanged();
+}
+
+Scorer* ScorerStorage::GetScorer() const {
+ return scorer_.get();
+}
+
+void ScorerStorage::AddObserver(ScorerStorage::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void ScorerStorage::RemoveObserver(ScorerStorage::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.h b/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.h
index 0cbb1ca69f0..1461ed23192 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.h
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer.h
@@ -26,6 +26,7 @@
#include "base/files/memory_mapped_file.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
+#include "base/observer_list.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
@@ -80,6 +81,8 @@ class Scorer {
// Returns the version number of the loaded client model.
virtual int model_version() const = 0;
+ virtual int dom_model_version() const = 0;
+
bool HasVisualTfLiteModel() const;
// -- Accessors used by the page feature extractor ---------------------------
@@ -131,7 +134,7 @@ class Scorer {
const SkBitmap& bitmap,
int input_width,
int input_height,
- const std::string& model_data,
+ std::string model_data,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
base::OnceCallback<void(std::vector<double>)> callback);
@@ -142,6 +145,33 @@ class Scorer {
friend class PhishingScorerTest;
};
+// A small wrapper around a Scorer that allows callers to observe for changes in
+// the model.
+class ScorerStorage {
+ public:
+ static ScorerStorage* GetInstance();
+
+ class Observer : public base::CheckedObserver {
+ public:
+ virtual void OnScorerChanged() = 0;
+ };
+
+ ScorerStorage();
+ ~ScorerStorage();
+ ScorerStorage(const ScorerStorage&) = delete;
+ ScorerStorage& operator=(const ScorerStorage&) = delete;
+
+ void SetScorer(std::unique_ptr<Scorer> scorer);
+ Scorer* GetScorer() const;
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ private:
+ std::unique_ptr<Scorer> scorer_;
+ base::ObserverList<Observer> observers_;
+};
+
} // namespace safe_browsing
#endif // COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_PHISHING_CLASSIFIER_SCORER_H_
diff --git a/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer_unittest.cc b/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer_unittest.cc
index 559484d1493..7dcd9f30c3f 100644
--- a/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer_unittest.cc
+++ b/chromium/components/safe_browsing/content/renderer/phishing_classifier/scorer_unittest.cc
@@ -84,6 +84,7 @@ std::string GetFlatBufferString() {
csd_model_builder.add_max_shingles_per_page(10);
csd_model_builder.add_shingle_size(3);
csd_model_builder.add_tflite_metadata(tflite_metadata_flat);
+ csd_model_builder.add_dom_model_version(123);
builder.Finish(csd_model_builder.Finish());
return std::string(reinterpret_cast<char*>(builder.GetBufferPointer()),
@@ -139,6 +140,7 @@ class PhishingScorerTest : public ::testing::Test {
model_.set_murmur_hash_seed(12345U);
model_.set_max_shingles_per_page(10);
model_.set_shingle_size(3);
+ model_.set_dom_model_version(123);
}
void TearDown() override {
@@ -153,36 +155,36 @@ TEST_F(PhishingScorerTest, HasValidFlatBufferModel) {
std::string flatbuffer = GetFlatBufferString();
base::MappedReadOnlyRegion mapped_region =
GetMappedReadOnlyRegionWithData(flatbuffer);
- scorer.reset(FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
- base::File()));
+ scorer = FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
+ base::File());
EXPECT_TRUE(scorer.get() != nullptr);
// Invalid region.
- scorer.reset(FlatBufferModelScorer::Create(base::ReadOnlySharedMemoryRegion(),
- base::File()));
+ scorer = FlatBufferModelScorer::Create(base::ReadOnlySharedMemoryRegion(),
+ base::File());
EXPECT_FALSE(scorer.get());
// Invalid buffer in region.
mapped_region = GetMappedReadOnlyRegionWithData("bogus string");
- scorer.reset(FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
- base::File()));
+ scorer = FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
+ base::File());
EXPECT_FALSE(scorer.get());
}
TEST_F(PhishingScorerTest, HasValidModel) {
std::unique_ptr<Scorer> scorer;
- scorer.reset(
- ProtobufModelScorer::Create(model_.SerializeAsString(), base::File()));
+ scorer =
+ ProtobufModelScorer::Create(model_.SerializeAsString(), base::File());
EXPECT_TRUE(scorer.get() != nullptr);
// Invalid model string.
- scorer.reset(ProtobufModelScorer::Create("bogus string", base::File()));
+ scorer = ProtobufModelScorer::Create("bogus string", base::File());
EXPECT_FALSE(scorer.get());
// Mode is missing a required field.
model_.clear_max_words_per_term();
- scorer.reset(ProtobufModelScorer::Create(model_.SerializePartialAsString(),
- base::File()));
+ scorer = ProtobufModelScorer::Create(model_.SerializePartialAsString(),
+ base::File());
EXPECT_FALSE(scorer.get());
}
@@ -221,8 +223,8 @@ TEST_F(PhishingScorerTest, PageTermsFlat) {
std::string flatbuffer = GetFlatBufferString();
base::MappedReadOnlyRegion mapped_region =
GetMappedReadOnlyRegionWithData(flatbuffer);
- scorer.reset(FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
- base::File()));
+ scorer = FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
+ base::File());
ASSERT_TRUE(scorer.get());
base::RepeatingCallback<bool(const std::string&)> page_terms_callback(
scorer->find_page_term_callback());
@@ -271,8 +273,8 @@ TEST_F(PhishingScorerTest, PageWordsFlat) {
std::string flatbuffer = GetFlatBufferString();
base::MappedReadOnlyRegion mapped_region =
GetMappedReadOnlyRegionWithData(flatbuffer);
- scorer.reset(FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
- base::File()));
+ scorer = FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
+ base::File());
ASSERT_TRUE(scorer.get());
base::RepeatingCallback<bool(uint32_t)> page_words_callback(
scorer->find_page_word_callback());
@@ -321,8 +323,8 @@ TEST_F(PhishingScorerTest, ComputeScoreFlat) {
std::string flatbuffer = GetFlatBufferString();
base::MappedReadOnlyRegion mapped_region =
GetMappedReadOnlyRegionWithData(flatbuffer);
- scorer.reset(FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
- base::File()));
+ scorer = FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
+ base::File());
EXPECT_TRUE(scorer.get() != nullptr);
// An empty feature map should match the empty rule.
@@ -348,4 +350,23 @@ TEST_F(PhishingScorerTest, ComputeScoreFlat) {
EXPECT_DOUBLE_EQ(0.77729986117469119, scorer->ComputeScore(features));
}
+TEST_F(PhishingScorerTest, DomModelVersionProtobuffer) {
+ std::unique_ptr<Scorer> scorer;
+ scorer =
+ ProtobufModelScorer::Create(model_.SerializeAsString(), base::File());
+ ASSERT_TRUE(scorer.get() != nullptr);
+ EXPECT_EQ(scorer->dom_model_version(), 123);
+}
+
+TEST_F(PhishingScorerTest, DomModelVersionFlatbuffer) {
+ std::unique_ptr<Scorer> scorer;
+ std::string flatbuffer = GetFlatBufferString();
+ base::MappedReadOnlyRegion mapped_region =
+ GetMappedReadOnlyRegionWithData(flatbuffer);
+ scorer = FlatBufferModelScorer::Create(mapped_region.region.Duplicate(),
+ base::File());
+ ASSERT_TRUE(scorer.get() != nullptr);
+ EXPECT_EQ(scorer->dom_model_version(), 123);
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/content/resources/download_file_types.asciipb b/chromium/components/safe_browsing/content/resources/download_file_types.asciipb
index 05a2ba86311..8dbb6c66a63 100644
--- a/chromium/components/safe_browsing/content/resources/download_file_types.asciipb
+++ b/chromium/components/safe_browsing/content/resources/download_file_types.asciipb
@@ -8,7 +8,7 @@
##
## Top level settings
##
-version_id: 50
+version_id: 51
sampled_ping_probability: 0.01
max_archived_binaries_to_report: 10
default_file_type {
@@ -2863,6 +2863,39 @@ file_types {
auto_open_hint: ALLOW_AUTO_OPEN
}
}
+file_types {
+ # Windows troubleshooting component
+ extension: "diagcab"
+ uma_value: 399
+ ping_setting: FULL_PING
+ platform_settings {
+ platform: PLATFORM_WINDOWS
+ danger_level: ALLOW_ON_USER_GESTURE
+ auto_open_hint: DISALLOW_AUTO_OPEN
+ }
+}
+file_types {
+ # Windows troubleshooting
+ extension: "diagcfg"
+ uma_value: 400
+ ping_setting: FULL_PING
+ platform_settings {
+ platform: PLATFORM_WINDOWS
+ danger_level: ALLOW_ON_USER_GESTURE
+ auto_open_hint: DISALLOW_AUTO_OPEN
+ }
+}
+file_types {
+ # Windows troubleshooting component
+ extension: "diagpkg"
+ uma_value: 401
+ ping_setting: FULL_PING
+ platform_settings {
+ platform: PLATFORM_WINDOWS
+ danger_level: ALLOW_ON_USER_GESTURE
+ auto_open_hint: DISALLOW_AUTO_OPEN
+ }
+}
##
## MacOS-specific files
diff --git a/chromium/components/safe_browsing/content/resources/download_file_types_experiment.asciipb b/chromium/components/safe_browsing/content/resources/download_file_types_experiment.asciipb
index 615c1cf3e9a..08a5047187e 100644
--- a/chromium/components/safe_browsing/content/resources/download_file_types_experiment.asciipb
+++ b/chromium/components/safe_browsing/content/resources/download_file_types_experiment.asciipb
@@ -12,7 +12,7 @@
## version id is larger than the version id in download_file_types.asciipb. If
## there isn't an ongoing experiment, this version id is equal to the version id
## in download_file_types.asciipb.
-version_id: 50
+version_id: 51
sampled_ping_probability: 0.01
max_archived_binaries_to_report: 10
default_file_type {
@@ -2867,6 +2867,39 @@ file_types {
auto_open_hint: ALLOW_AUTO_OPEN
}
}
+file_types {
+ # Windows troubleshooting component
+ extension: "diagcab"
+ uma_value: 399
+ ping_setting: FULL_PING
+ platform_settings {
+ platform: PLATFORM_WINDOWS
+ danger_level: ALLOW_ON_USER_GESTURE
+ auto_open_hint: DISALLOW_AUTO_OPEN
+ }
+}
+file_types {
+ # Windows troubleshooting
+ extension: "diagcfg"
+ uma_value: 400
+ ping_setting: FULL_PING
+ platform_settings {
+ platform: PLATFORM_WINDOWS
+ danger_level: ALLOW_ON_USER_GESTURE
+ auto_open_hint: DISALLOW_AUTO_OPEN
+ }
+}
+file_types {
+ # Windows troubleshooting component
+ extension: "diagpkg"
+ uma_value: 401
+ ping_setting: FULL_PING
+ platform_settings {
+ platform: PLATFORM_WINDOWS
+ danger_level: ALLOW_ON_USER_GESTURE
+ auto_open_hint: DISALLOW_AUTO_OPEN
+ }
+}
##
## MacOS-specific files
diff --git a/chromium/components/safe_browsing/core/browser/BUILD.gn b/chromium/components/safe_browsing/core/browser/BUILD.gn
index d931494c494..f07e6908221 100644
--- a/chromium/components/safe_browsing/core/browser/BUILD.gn
+++ b/chromium/components/safe_browsing/core/browser/BUILD.gn
@@ -153,6 +153,20 @@ source_set("token_fetcher_unit_tests") {
]
}
+source_set("token_fetcher_testing_helper") {
+ testonly = true
+ sources = [
+ "test_safe_browsing_token_fetcher.cc",
+ "test_safe_browsing_token_fetcher.h",
+ ]
+
+ deps = [
+ ":token_fetcher",
+ "//base",
+ "//testing/gmock",
+ ]
+}
+
source_set("download_check_result") {
sources = [ "download_check_result.h" ]
}
diff --git a/chromium/components/safe_browsing/core/browser/db/database_manager.cc b/chromium/components/safe_browsing/core/browser/db/database_manager.cc
index b87902dd2cb..3ded7349599 100644
--- a/chromium/components/safe_browsing/core/browser/db/database_manager.cc
+++ b/chromium/components/safe_browsing/core/browser/db/database_manager.cc
@@ -150,6 +150,11 @@ void SafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished() {
update_complete_callback_list_.Notify();
}
+bool SafeBrowsingDatabaseManager::IsDatabaseReady() {
+ DCHECK(io_task_runner()->RunsTasksInCurrentSequence());
+ return enabled_;
+}
+
SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck(
const GURL& url,
Client* client)
diff --git a/chromium/components/safe_browsing/core/browser/db/database_manager.h b/chromium/components/safe_browsing/core/browser/db/database_manager.h
index 16e3ae675b6..71cb8c4ba9b 100644
--- a/chromium/components/safe_browsing/core/browser/db/database_manager.h
+++ b/chromium/components/safe_browsing/core/browser/db/database_manager.h
@@ -233,10 +233,6 @@ class SafeBrowsingDatabaseManager
// Returns whether download protection is enabled.
virtual bool IsDownloadProtectionEnabled() const = 0;
- // Returns true if URL-checking is supported on this build+device.
- // If false, calls to CheckBrowseUrl may dcheck-fail.
- virtual bool IsSupported() const = 0;
-
//
// Methods to indicate when to start or suspend the SafeBrowsing operations.
// These functions are always called on the IO thread.
@@ -267,6 +263,9 @@ class SafeBrowsingDatabaseManager
// method at the bottom of it.
virtual void StopOnIOThread(bool shutdown);
+ // Called to check if database is ready or not.
+ virtual bool IsDatabaseReady();
+
protected:
// Bundled client info for an API abuse hash prefix check.
class SafeBrowsingApiCheck {
diff --git a/chromium/components/safe_browsing/core/browser/db/fake_database_manager.cc b/chromium/components/safe_browsing/core/browser/db/fake_database_manager.cc
index 18265d4b36a..24fab78bfd8 100644
--- a/chromium/components/safe_browsing/core/browser/db/fake_database_manager.cc
+++ b/chromium/components/safe_browsing/core/browser/db/fake_database_manager.cc
@@ -94,10 +94,6 @@ safe_browsing::ThreatSource FakeSafeBrowsingDatabaseManager::GetThreatSource()
return safe_browsing::ThreatSource::LOCAL_PVER4;
}
-bool FakeSafeBrowsingDatabaseManager::IsSupported() const {
- return true;
-}
-
// static
void FakeSafeBrowsingDatabaseManager::CheckBrowseURLAsync(
GURL url,
diff --git a/chromium/components/safe_browsing/core/browser/db/fake_database_manager.h b/chromium/components/safe_browsing/core/browser/db/fake_database_manager.h
index 23d64c88d0b..cca570959d8 100644
--- a/chromium/components/safe_browsing/core/browser/db/fake_database_manager.h
+++ b/chromium/components/safe_browsing/core/browser/db/fake_database_manager.h
@@ -36,7 +36,6 @@ class FakeSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
Client* client) override;
bool CheckUrlForSubresourceFilter(const GURL& url, Client* client) override;
safe_browsing::ThreatSource GetThreatSource() const override;
- bool IsSupported() const override;
private:
~FakeSafeBrowsingDatabaseManager() override;
diff --git a/chromium/components/safe_browsing/core/browser/db/prefix_iterator.h b/chromium/components/safe_browsing/core/browser/db/prefix_iterator.h
index cd243154dde..0d065ac6514 100644
--- a/chromium/components/safe_browsing/core/browser/db/prefix_iterator.h
+++ b/chromium/components/safe_browsing/core/browser/db/prefix_iterator.h
@@ -16,12 +16,13 @@ namespace safe_browsing {
// The prefix iterator is used to binary search within a |HashPrefixes|. It is
// essentially a random access iterator that steps |PrefixSize| steps within the
// underlying buffer.
-class PrefixIterator
- : public std::iterator<std::random_access_iterator_tag, base::StringPiece> {
+class PrefixIterator {
public:
- using difference_type =
- typename std::iterator<std::random_access_iterator_tag,
- base::StringPiece>::difference_type;
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = base::StringPiece;
+ using difference_type = std::ptrdiff_t;
+ using pointer = base::StringPiece*;
+ using reference = base::StringPiece&;
PrefixIterator(base::StringPiece prefixes, size_t index, size_t size);
PrefixIterator(const PrefixIterator& rhs);
diff --git a/chromium/components/safe_browsing/core/browser/db/test_database_manager.cc b/chromium/components/safe_browsing/core/browser/db/test_database_manager.cc
index b4099616098..08610aa508b 100644
--- a/chromium/components/safe_browsing/core/browser/db/test_database_manager.cc
+++ b/chromium/components/safe_browsing/core/browser/db/test_database_manager.cc
@@ -117,11 +117,6 @@ bool TestSafeBrowsingDatabaseManager::IsDownloadProtectionEnabled() const {
return false;
}
-bool TestSafeBrowsingDatabaseManager::IsSupported() const {
- NOTIMPLEMENTED();
- return false;
-}
-
void TestSafeBrowsingDatabaseManager::StartOnIOThread(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const V4ProtocolConfig& config) {
diff --git a/chromium/components/safe_browsing/core/browser/db/test_database_manager.h b/chromium/components/safe_browsing/core/browser/db/test_database_manager.h
index ffa46a88a1a..a4d74b45cd7 100644
--- a/chromium/components/safe_browsing/core/browser/db/test_database_manager.h
+++ b/chromium/components/safe_browsing/core/browser/db/test_database_manager.h
@@ -48,7 +48,6 @@ class TestSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
bool MatchMalwareIP(const std::string& ip_address) override;
safe_browsing::ThreatSource GetThreatSource() const override;
bool IsDownloadProtectionEnabled() const override;
- bool IsSupported() const override;
void StartOnIOThread(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const V4ProtocolConfig& config) override;
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_database_unittest.cc b/chromium/components/safe_browsing/core/browser/db/v4_database_unittest.cc
index 900b9339a70..67ae5760ece 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_database_unittest.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_database_unittest.cc
@@ -32,7 +32,7 @@ class FakeV4Store : public V4Store {
return hash_prefix_should_match_ ? full_hash : HashPrefix();
}
- bool HasValidData() const override { return true; }
+ bool HasValidData() override { return true; }
void set_hash_prefix_matches(bool hash_prefix_matches) {
hash_prefix_should_match_ = hash_prefix_matches;
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.cc b/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.cc
index e7ffe69cda8..221cb5c7e8a 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.cc
@@ -11,6 +11,7 @@
#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_split.h"
#include "base/timer/timer.h"
@@ -38,6 +39,12 @@ void RecordGetHashResult(safe_browsing::V4OperationResult result) {
safe_browsing::V4OperationResult::OPERATION_RESULT_MAX);
}
+// Record a backoff error count
+void RecordBackoffErrorCountResult(size_t count) {
+ base::UmaHistogramCounts100(
+ "SafeBrowsing.V4GetHash.Result.BackoffErrorCount", count);
+}
+
// Enumerate parsing failures for histogramming purposes. DO NOT CHANGE
// THE ORDERING OF THESE VALUES.
enum ParseResultType {
@@ -124,6 +131,12 @@ void RecordV4GetHashCheckResult(V4GetHashCheckResultType result_type) {
GET_HASH_CHECK_RESULT_MAX);
}
+bool ErrorIsRetriable(int net_error, int http_error) {
+ return (net_error == net::ERR_INTERNET_DISCONNECTED ||
+ net_error == net::ERR_NETWORK_CHANGED) &&
+ http_error != net::HTTP_OK;
+}
+
const char kPermission[] = "permission";
const char kPhaPatternType[] = "pha_pattern_type";
const char kMalwareThreatType[] = "malware_threat_type";
@@ -297,6 +310,7 @@ void V4GetHashProtocolManager::GetFullHashes(
if (clock_->Now() <= next_gethash_time_) {
if (gethash_error_count_) {
RecordGetHashResult(V4OperationResult::BACKOFF_ERROR);
+ backoff_error_count_++;
} else {
RecordGetHashResult(V4OperationResult::MIN_WAIT_DURATION_ERROR);
}
@@ -708,6 +722,7 @@ void V4GetHashProtocolManager::ParseMetadata(const ThreatMatch& match,
void V4GetHashProtocolManager::ResetGetHashErrors() {
gethash_error_count_ = 0;
gethash_back_off_mult_ = 1;
+ backoff_error_count_ = 0;
next_gethash_time_ = base::Time();
}
@@ -802,11 +817,18 @@ void V4GetHashProtocolManager::OnURLLoaderCompleteInternal(
Time negative_cache_expire;
if (net_error == net::OK && response_code == net::HTTP_OK) {
RecordGetHashResult(V4OperationResult::STATUS_200);
+ if (gethash_error_count_) RecordBackoffErrorCountResult(backoff_error_count_);
ResetGetHashErrors();
if (!ParseHashResponse(data, &full_hash_infos, &negative_cache_expire)) {
full_hash_infos.clear();
RecordGetHashResult(V4OperationResult::PARSE_ERROR);
}
+ } else if (ErrorIsRetriable(net_error, response_code)) {
+ if (net_error != net::OK) {
+ RecordGetHashResult(V4OperationResult::RETRIABLE_NETWORK_ERROR);
+ } else {
+ RecordGetHashResult(V4OperationResult::RETRIABLE_HTTP_ERROR);
+ }
} else {
HandleGetHashError(clock_->Now());
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.h b/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.h
index b51211ef2a2..2a6eb81c89a 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.h
+++ b/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager.h
@@ -229,6 +229,7 @@ class V4GetHashProtocolManager {
TestGetHashErrorHandlingParallelRequests);
FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, GetCachedResults);
FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestUpdatesAreMerged);
+ FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestBackoffErrorHistogramCount);
friend class V4GetHashProtocolManagerTest;
friend class V4GetHashProtocolManagerFuzzer;
friend class V4GetHashProtocolManagerFactoryImpl;
@@ -329,6 +330,9 @@ class V4GetHashProtocolManager {
// response, used for request backoff timing.
size_t gethash_error_count_;
+ // The number of backoff errors since the last successful HTTP response.
+ size_t backoff_error_count_ = 0;
+
// Multiplier for the backoff error after the second.
size_t gethash_back_off_mult_;
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager_unittest.cc b/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager_unittest.cc
index 13ed4fd888d..c9e49a86309 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager_unittest.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_get_hash_protocol_manager_unittest.cc
@@ -10,6 +10,7 @@
#include "base/base64.h"
#include "base/bind.h"
#include "base/run_loop.h"
+#include "base/strings/escape.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
@@ -18,7 +19,6 @@
#include "components/safe_browsing/core/browser/db/safebrowsing.pb.h"
#include "components/safe_browsing/core/browser/db/util.h"
#include "components/safe_browsing/core/browser/db/v4_test_util.h"
-#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -232,6 +232,56 @@ TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingResponseCode) {
EXPECT_TRUE(callback_called());
}
+TEST_F(V4GetHashProtocolManagerTest, TestBackoffErrorHistogramCount) {
+ std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+ base::HistogramTester histogram_tester;
+
+ FullHashToStoreAndHashPrefixesMap matched_locally;
+ matched_locally[FullHash("AHashFull")].emplace_back(GetUrlSocEngId(),
+ HashPrefix("AHash"));
+
+ std::vector<FullHashInfo> expected_results;
+ pm->GetFullHashes(
+ matched_locally, {},
+ base::BindRepeating(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+ base::Unretained(this), expected_results));
+
+ FullHashToStoreAndHashPrefixesMap matched_locally2;
+ matched_locally2[FullHash("AHash2Full")].emplace_back(GetUrlSocEngId(),
+ HashPrefix("AHash2"));
+
+ pm->GetFullHashes(matched_locally2, {},
+ base::BindRepeating(
+ &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+ base::Unretained(this), expected_results));
+
+ // Failed request status should result in error.
+ SetupFullHashFetcherToReturnResponse(pm.get(), FullHash("AHashFull"),
+ net::ERR_CONNECTION_RESET, 200,
+ GetStockV4HashResponse());
+
+ EXPECT_EQ(1ul, pm->gethash_error_count_);
+ EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
+
+ EXPECT_TRUE(callback_called());
+
+ reset_callback_called();
+
+ pm->GetFullHashes(matched_locally2, {},
+ base::BindRepeating(
+ &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+ base::Unretained(this), expected_results));
+
+ EXPECT_EQ(1ul, pm->backoff_error_count_);
+
+ SetupFullHashFetcherToReturnResponse(pm.get(), FullHash("AHash2Full"),
+ net::OK, 200, GetStockV4HashResponse());
+
+ histogram_tester.ExpectTotalCount(
+ "SafeBrowsing.V4GetHash.Result.BackoffErrorCount",
+ 1);
+}
+
TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingParallelRequests) {
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
std::vector<FullHashInfo> empty_results;
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.cc b/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.cc
index 254468c76d3..0ee0f67720c 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.cc
@@ -639,10 +639,6 @@ bool V4LocalDatabaseManager::IsDownloadProtectionEnabled() const {
return true;
}
-bool V4LocalDatabaseManager::IsSupported() const {
- return true;
-}
-
void V4LocalDatabaseManager::StartOnIOThread(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const V4ProtocolConfig& config) {
@@ -990,15 +986,18 @@ void V4LocalDatabaseManager::PerformFullHashCheck(
std::unique_ptr<PendingCheck> check) {
DCHECK(io_task_runner()->RunsTasksInCurrentSequence());
- DCHECK(enabled_);
DCHECK(!check->full_hash_to_store_and_hash_prefixes.empty());
- FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes =
- check->full_hash_to_store_and_hash_prefixes;
- v4_get_hash_protocol_manager_->GetFullHashes(
- full_hash_to_store_and_hash_prefixes, list_client_states_,
- base::BindOnce(&V4LocalDatabaseManager::OnFullHashResponse,
- weak_factory_.GetWeakPtr(), std::move(check)));
+ // If we're not enabled, we're in the middle of shutdown, so silently drop the
+ // check.
+ if (enabled_) {
+ FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes =
+ check->full_hash_to_store_and_hash_prefixes;
+ v4_get_hash_protocol_manager_->GetFullHashes(
+ full_hash_to_store_and_hash_prefixes, list_client_states_,
+ base::BindOnce(&V4LocalDatabaseManager::OnFullHashResponse,
+ weak_factory_.GetWeakPtr(), std::move(check)));
+ }
}
void V4LocalDatabaseManager::ProcessQueuedChecks() {
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.h b/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.h
index c27d2dd53c9..511c12ac084 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.h
+++ b/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager.h
@@ -85,7 +85,6 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
bool MatchMalwareIP(const std::string& ip_address) override;
safe_browsing::ThreatSource GetThreatSource() const override;
bool IsDownloadProtectionEnabled() const override;
- bool IsSupported() const override;
void StartOnIOThread(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager_unittest.cc b/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager_unittest.cc
index 8961309d140..55ec5ab867e 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager_unittest.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_local_database_manager_unittest.cc
@@ -478,11 +478,6 @@ TEST_F(V4LocalDatabaseManagerTest, TestGetThreatSource) {
v4_local_database_manager_->GetThreatSource());
}
-TEST_F(V4LocalDatabaseManagerTest, TestIsSupported) {
- WaitForTasksOnTaskRunner();
- EXPECT_TRUE(v4_local_database_manager_->IsSupported());
-}
-
TEST_F(V4LocalDatabaseManagerTest, TestCanCheckUrl) {
WaitForTasksOnTaskRunner();
EXPECT_TRUE(
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.cc b/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.cc
index 4e9290e3f89..962d2b0e1bd 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.cc
@@ -9,6 +9,7 @@
#include "base/hash/sha1.h"
#include "base/metrics/histogram_functions.h"
#include "base/rand_util.h"
+#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
@@ -16,7 +17,6 @@
#include "components/version_info/version_info.h"
#include "crypto/sha2.h"
#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
#include "net/base/ip_address.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
@@ -47,7 +47,7 @@ std::string Unescape(const std::string& url) {
int loop_var = 0;
do {
old_size = unescaped_str.size();
- unescaped_str = net::UnescapeBinaryURLComponent(unescaped_str);
+ unescaped_str = base::UnescapeBinaryURLComponent(unescaped_str);
} while (old_size != unescaped_str.size() &&
++loop_var <= kMaxLoopIterations);
@@ -97,7 +97,7 @@ std::string GetReportUrl(const V4ProtocolConfig& config,
std::string api_key = google_apis::GetAPIKey();
if (!api_key.empty()) {
base::StringAppendF(&url, "&key=%s",
- net::EscapeQueryParamValue(api_key, true).c_str());
+ base::EscapeQueryParamValue(api_key, true).c_str());
}
if (reporting_level)
url.append(base::StringPrintf("&ext=%d", *reporting_level));
@@ -323,7 +323,7 @@ std::string V4ProtocolManagerUtil::ComposeUrl(const std::string& prefix,
method.c_str(), request_base64.c_str());
if (!key_param.empty()) {
base::StringAppendF(&url, "&key=%s",
- net::EscapeQueryParamValue(key_param, true).c_str());
+ base::EscapeQueryParamValue(key_param, true).c_str());
}
return url;
}
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.h b/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.h
index f304edb241e..0a350650d87 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.h
+++ b/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util.h
@@ -301,9 +301,16 @@ enum V4OperationResult {
// Identical operation already pending.
ALREADY_PENDING_ERROR = 6,
+ // A network error that can be retried without backoff (e.g.
+ // NETWORK_DISCONNECTED).
+ RETRIABLE_NETWORK_ERROR = 7,
+
+ // An HTTP error code that can be retried without backoff.
+ RETRIABLE_HTTP_ERROR = 8,
+
// Memory space for histograms is determined by the max. ALWAYS
// ADD NEW VALUES BEFORE THIS ONE.
- OPERATION_RESULT_MAX = 7
+ OPERATION_RESULT_MAX = 9
};
// A class that provides static methods related to the Pver4 protocol.
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util_unittest.cc b/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util_unittest.cc
index a36e312a201..63dd889d1df 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util_unittest.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_protocol_manager_util_unittest.cc
@@ -9,10 +9,10 @@
#include "base/base64.h"
#include "base/containers/contains.h"
#include "base/logging.h"
+#include "base/strings/escape.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "components/safe_browsing/core/browser/db/v4_test_util.h"
-#include "net/base/escape.h"
#include "net/http/http_request_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_store.cc b/chromium/components/safe_browsing/core/browser/db/v4_store.cc
index 9f76b1569b9..7c6f00fa918 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_store.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_store.cc
@@ -9,6 +9,7 @@
#include "base/base64.h"
#include "base/bind.h"
+#include "base/cpu_reduction_experiment.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
@@ -22,6 +23,7 @@
#include "components/safe_browsing/core/common/proto/webui.pb.h"
#include "crypto/secure_hash.h"
#include "crypto/sha2.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
using base::TimeTicks;
@@ -204,9 +206,14 @@ void V4Store::Initialize() {
RecordStoreReadResult(store_read_result);
}
-bool V4Store::HasValidData() const {
- RecordBooleanWithAndWithoutSuffix("SafeBrowsing.V4Store.IsStoreValid",
- has_valid_data_, store_path_);
+bool V4Store::HasValidData() {
+ // Record every 256th time (`record_has_valid_data_counter_` is 8-bit).
+ if (++record_has_valid_data_counter_ == 1 ||
+ // TODO(crbug.com/1295441): Remove the condition below.
+ !base::IsRunningCpuReductionExperiment()) {
+ RecordBooleanWithAndWithoutSuffix("SafeBrowsing.V4Store.IsStoreValid",
+ has_valid_data_, store_path_);
+ }
return has_valid_data_;
}
@@ -581,7 +588,8 @@ ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map,
// picked is not the same as merged. A picked element isn't merged if its
// index is on the raw_removals list.
int total_picked_from_old = 0;
- const int* removals_iter = raw_removals ? raw_removals->begin() : nullptr;
+ auto removals_iter =
+ raw_removals ? absl::make_optional(raw_removals->begin()) : absl::nullopt;
while (old_has_unmerged || additions_has_unmerged) {
// If the same hash prefix appears in the existing store and the additions
// list, something is clearly wrong. Discard the update.
@@ -605,8 +613,8 @@ ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map,
// prefix of size |next_smallest_prefix_size| from the old store.
old_iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size;
- if (!raw_removals || removals_iter == raw_removals->end() ||
- *removals_iter != total_picked_from_old) {
+ if (!raw_removals || *removals_iter == raw_removals->end() ||
+ **removals_iter != total_picked_from_old) {
// Append the smallest hash to the appropriate list.
hash_prefix_map_[next_smallest_prefix_size] += next_smallest_prefix_old;
@@ -616,7 +624,7 @@ ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map,
}
} else {
// Element not added to new map. Move the removals iterator forward.
- removals_iter++;
+ (*removals_iter)++;
}
total_picked_from_old++;
@@ -648,7 +656,7 @@ ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map,
}
}
- if (raw_removals && removals_iter != raw_removals->end()) {
+ if (raw_removals && *removals_iter != raw_removals->end()) {
return REMOVALS_INDEX_TOO_LARGE_FAILURE;
}
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_store.h b/chromium/components/safe_browsing/core/browser/db/v4_store.h
index 9b2cdb2957e..45a6056f917 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_store.h
+++ b/chromium/components/safe_browsing/core/browser/db/v4_store.h
@@ -188,7 +188,7 @@ class V4Store {
// True if this store has valid contents, either from a successful read
// from disk or a full update. This does not mean the checksum was verified.
- virtual bool HasValidData() const;
+ virtual bool HasValidData();
const std::string& state() const { return state_; }
@@ -432,6 +432,10 @@ class V4Store {
// The size of the file on disk for this store.
int64_t file_size_;
+ // A counter used to manage how frequently the value of `has_valid_data_`
+ // below is recorded.
+ uint8_t record_has_valid_data_counter_ = 0;
+
// True if the file was successfully read+parsed or was populated from
// a full update.
bool has_valid_data_;
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_test_util.cc b/chromium/components/safe_browsing/core/browser/db/v4_test_util.cc
index aa704c4b872..6ed5f4fd2f1 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_test_util.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_test_util.cc
@@ -46,7 +46,7 @@ TestV4Store::TestV4Store(
TestV4Store::~TestV4Store() = default;
-bool TestV4Store::HasValidData() const {
+bool TestV4Store::HasValidData() {
return true;
}
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_test_util.h b/chromium/components/safe_browsing/core/browser/db/v4_test_util.h
index cbfdb7f447e..accb167247b 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_test_util.h
+++ b/chromium/components/safe_browsing/core/browser/db/v4_test_util.h
@@ -31,7 +31,7 @@ class TestV4Store : public V4Store {
const base::FilePath& store_path);
~TestV4Store() override;
- bool HasValidData() const override;
+ bool HasValidData() override;
void MarkPrefixAsBad(HashPrefix prefix);
diff --git a/chromium/components/safe_browsing/core/browser/db/v4_update_protocol_manager_unittest.cc b/chromium/components/safe_browsing/core/browser/db/v4_update_protocol_manager_unittest.cc
index f077d7adb42..82b6bb1d827 100644
--- a/chromium/components/safe_browsing/core/browser/db/v4_update_protocol_manager_unittest.cc
+++ b/chromium/components/safe_browsing/core/browser/db/v4_update_protocol_manager_unittest.cc
@@ -10,6 +10,7 @@
#include "base/base64.h"
#include "base/bind.h"
+#include "base/strings/escape.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
@@ -18,7 +19,6 @@
#include "components/safe_browsing/core/browser/db/safebrowsing.pb.h"
#include "components/safe_browsing/core/browser/db/util.h"
#include "components/safe_browsing/core/browser/db/v4_test_util.h"
-#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/http/http_status_code.h"
diff --git a/chromium/components/safe_browsing/core/browser/password_protection/password_protection_request.cc b/chromium/components/safe_browsing/core/browser/password_protection/password_protection_request.cc
index 5a491a4036c..c0fe7698e6d 100644
--- a/chromium/components/safe_browsing/core/browser/password_protection/password_protection_request.cc
+++ b/chromium/components/safe_browsing/core/browser/password_protection/password_protection_request.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/metrics/histogram_functions.h"
+#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
@@ -18,7 +19,6 @@
#include "components/safe_browsing/core/common/safebrowsing_constants.h"
#include "components/safe_browsing/core/common/utils.h"
#include "components/url_formatter/url_formatter.h"
-#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
@@ -55,7 +55,7 @@ std::vector<std::string> GetMatchingDomains(
url_formatter::kFormatUrlOmitHTTPS |
url_formatter::kFormatUrlOmitTrivialSubdomains |
url_formatter::kFormatUrlTrimAfterHost,
- net::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
+ base::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
matching_domains.push_back(std::move(domain));
}
return base::flat_set<std::string>(std::move(matching_domains)).extract();
diff --git a/chromium/components/safe_browsing/core/browser/password_protection/password_protection_service_base.cc b/chromium/components/safe_browsing/core/browser/password_protection/password_protection_service_base.cc
index 0f73eeea82c..d93eb525666 100644
--- a/chromium/components/safe_browsing/core/browser/password_protection/password_protection_service_base.cc
+++ b/chromium/components/safe_browsing/core/browser/password_protection/password_protection_service_base.cc
@@ -12,6 +12,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/metrics/histogram_macros.h"
+#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -25,7 +26,6 @@
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/utils.h"
#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
#include "net/base/url_util.h"
using password_manager::metrics_util::PasswordType;
@@ -270,7 +270,7 @@ GURL PasswordProtectionServiceBase::GetPasswordProtectionRequestUrl() {
GURL url(kPasswordProtectionRequestUrl);
std::string api_key = google_apis::GetAPIKey();
DCHECK(!api_key.empty());
- return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
+ return url.Resolve("?key=" + base::EscapeQueryParamValue(api_key, true));
}
// static
diff --git a/chromium/components/safe_browsing/core/browser/ping_manager.cc b/chromium/components/safe_browsing/core/browser/ping_manager.cc
index 7db1aeb0016..456d3d157a4 100644
--- a/chromium/components/safe_browsing/core/browser/ping_manager.cc
+++ b/chromium/components/safe_browsing/core/browser/ping_manager.cc
@@ -11,12 +11,12 @@
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
+#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
#include "components/safe_browsing/core/common/utils.h"
#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
@@ -74,20 +74,32 @@ PingManager* PingManager::Create(
const V4ProtocolConfig& config,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher,
- base::RepeatingCallback<bool()> get_should_fetch_access_token) {
+ base::RepeatingCallback<bool()> get_should_fetch_access_token,
+ WebUIDelegate* webui_delegate,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ base::RepeatingCallback<ChromeUserPopulation()>
+ get_user_population_callback) {
return new PingManager(config, url_loader_factory, std::move(token_fetcher),
- get_should_fetch_access_token);
+ get_should_fetch_access_token, webui_delegate,
+ ui_task_runner, get_user_population_callback);
}
PingManager::PingManager(
const V4ProtocolConfig& config,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher,
- base::RepeatingCallback<bool()> get_should_fetch_access_token)
+ base::RepeatingCallback<bool()> get_should_fetch_access_token,
+ WebUIDelegate* webui_delegate,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ base::RepeatingCallback<ChromeUserPopulation()>
+ get_user_population_callback)
: config_(config),
url_loader_factory_(url_loader_factory),
token_fetcher_(std::move(token_fetcher)),
- get_should_fetch_access_token_(get_should_fetch_access_token) {}
+ get_should_fetch_access_token_(get_should_fetch_access_token),
+ webui_delegate_(webui_delegate),
+ ui_task_runner_(ui_task_runner),
+ get_user_population_callback_(get_user_population_callback) {}
PingManager::~PingManager() {}
@@ -139,19 +151,45 @@ void PingManager::ReportSafeBrowsingHit(
}
// Sends threat details for users who opt-in.
-void PingManager::ReportThreatDetails(const std::string& report) {
+PingManager::ReportThreatDetailsResult PingManager::ReportThreatDetails(
+ std::unique_ptr<ClientSafeBrowsingReportRequest> report) {
+ if (!get_user_population_callback_.is_null()) {
+ *report->mutable_population() = get_user_population_callback_.Run();
+ }
+
+ std::string serialized_report;
+ if (!report->SerializeToString(&serialized_report)) {
+ DLOG(ERROR) << "Unable to serialize the threat report.";
+ return ReportThreatDetailsResult::SERIALIZATION_ERROR;
+ }
+ if (serialized_report.empty()) {
+ DLOG(ERROR) << "The threat report is empty.";
+ return ReportThreatDetailsResult::EMPTY_REPORT;
+ }
+
if (get_should_fetch_access_token_.Run()) {
token_fetcher_->Start(
base::BindOnce(&PingManager::ReportThreatDetailsOnGotAccessToken,
- weak_factory_.GetWeakPtr(), report));
+ weak_factory_.GetWeakPtr(), serialized_report));
} else {
std::string empty_access_token;
- ReportThreatDetailsOnGotAccessToken(report, empty_access_token);
+ ReportThreatDetailsOnGotAccessToken(serialized_report, empty_access_token);
}
+
+ // The following is to log this ClientSafeBrowsingReportRequest on any open
+ // chrome://safe-browsing pages.
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WebUIDelegate::AddToCSBRRsSent,
+ // Unretained is okay because in practice, webui_delegate_
+ // is a singleton
+ base::Unretained(webui_delegate_), std::move(report)));
+
+ return ReportThreatDetailsResult::SUCCESS;
}
void PingManager::ReportThreatDetailsOnGotAccessToken(
- const std::string& report,
+ const std::string& serialized_report,
const std::string& access_token) {
GURL report_url = ThreatDetailsUrl();
@@ -171,7 +209,7 @@ void PingManager::ReportThreatDetailsOnGotAccessToken(
auto loader = network::SimpleURLLoader::Create(std::move(resource_request),
kTrafficAnnotation);
- loader->AttachStringForUpload(report, "application/octet-stream");
+ loader->AttachStringForUpload(serialized_report, "application/octet-stream");
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory_.get(),
@@ -240,7 +278,7 @@ GURL PingManager::SafeBrowsingHitUrl(
// Population_id should be URL-safe, but escape it and size-limit it
// anyway since it came from outside Chrome.
std::string up_str =
- net::EscapeQueryParamValue(hit_report.population_id, true);
+ base::EscapeQueryParamValue(hit_report.population_id, true);
if (up_str.size() > 512) {
DCHECK(false) << "population_id is too long: " << up_str;
up_str = "UP_STRING_TOO_LONG";
@@ -252,9 +290,10 @@ GURL PingManager::SafeBrowsingHitUrl(
return GURL(base::StringPrintf(
"%s&evts=%s&evtd=%s&evtr=%s&evhr=%s&evtb=%d&src=%s&m=%d%s", url.c_str(),
threat_list.c_str(),
- net::EscapeQueryParamValue(hit_report.malicious_url.spec(), true).c_str(),
- net::EscapeQueryParamValue(hit_report.page_url.spec(), true).c_str(),
- net::EscapeQueryParamValue(hit_report.referrer_url.spec(), true).c_str(),
+ base::EscapeQueryParamValue(hit_report.malicious_url.spec(), true)
+ .c_str(),
+ base::EscapeQueryParamValue(hit_report.page_url.spec(), true).c_str(),
+ base::EscapeQueryParamValue(hit_report.referrer_url.spec(), true).c_str(),
hit_report.is_subresource, threat_source.c_str(),
hit_report.is_metrics_reporting_active, user_population_comp.c_str()));
}
diff --git a/chromium/components/safe_browsing/core/browser/ping_manager.h b/chromium/components/safe_browsing/core/browser/ping_manager.h
index 558e420b986..312c51463f6 100644
--- a/chromium/components/safe_browsing/core/browser/ping_manager.h
+++ b/chromium/components/safe_browsing/core/browser/ping_manager.h
@@ -18,6 +18,7 @@
#include "components/safe_browsing/core/browser/db/hit_report.h"
#include "components/safe_browsing/core/browser/db/util.h"
#include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
+#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/gurl.h"
@@ -29,6 +30,25 @@ namespace safe_browsing {
class PingManager : public KeyedService {
public:
+ enum class ReportThreatDetailsResult {
+ SUCCESS = 0,
+ // There was a problem serializing the report to a string.
+ SERIALIZATION_ERROR = 1,
+ // The report is empty, so it is not sent.
+ EMPTY_REPORT = 2,
+ };
+
+ // Interface via which a client of this class can surface relevant events in
+ // WebUI. All methods must be called on the UI thread.
+ class WebUIDelegate {
+ public:
+ virtual ~WebUIDelegate() = default;
+
+ // Track a client safe browsing report being sent.
+ virtual void AddToCSBRRsSent(
+ std::unique_ptr<ClientSafeBrowsingReportRequest> csbrr) = 0;
+ };
+
PingManager(const PingManager&) = delete;
PingManager& operator=(const PingManager&) = delete;
@@ -39,7 +59,11 @@ class PingManager : public KeyedService {
const V4ProtocolConfig& config,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher,
- base::RepeatingCallback<bool()> get_should_fetch_access_token);
+ base::RepeatingCallback<bool()> get_should_fetch_access_token,
+ WebUIDelegate* webui_delegate,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ base::RepeatingCallback<ChromeUserPopulation()>
+ get_user_population_callback);
void OnURLLoaderComplete(network::SimpleURLLoader* source,
std::unique_ptr<std::string> response_body);
@@ -53,9 +77,11 @@ class PingManager : public KeyedService {
// SafeBrowsingtHitUrl.
void ReportSafeBrowsingHit(const safe_browsing::HitReport& hit_report);
- // Users can opt-in on the SafeBrowsing interstitial to send detailed
- // threat reports. |report| is the serialized report.
- void ReportThreatDetails(const std::string& report);
+ // Sends a detailed threat report after performing validation and adding extra
+ // details to the report. The returned object provides details on whether the
+ // report was successful.
+ ReportThreatDetailsResult ReportThreatDetails(
+ std::unique_ptr<ClientSafeBrowsingReportRequest> report);
// Only used for tests
void SetURLLoaderFactoryForTesting(
@@ -65,13 +91,15 @@ class PingManager : public KeyedService {
protected:
friend class PingManagerTest;
- // Constructs a PingManager with the given |config|, |url_loader_factory|, and
- // access token fetching information.
explicit PingManager(
const V4ProtocolConfig& config,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher,
- base::RepeatingCallback<bool()> get_should_fetch_access_token);
+ base::RepeatingCallback<bool()> get_should_fetch_access_token,
+ WebUIDelegate* webui_delegate,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ base::RepeatingCallback<ChromeUserPopulation()>
+ get_user_population_callback);
private:
FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestSafeBrowsingHitUrl);
@@ -92,7 +120,7 @@ class PingManager : public KeyedService {
// Once the user's access_token has been fetched by ReportThreatDetails (or
// intentionally not fetched), attaches the token and sends the report.
- void ReportThreatDetailsOnGotAccessToken(const std::string& report,
+ void ReportThreatDetailsOnGotAccessToken(const std::string& serialized_report,
const std::string& access_token);
// Track outstanding SafeBrowsing report fetchers for clean up.
@@ -109,6 +137,17 @@ class PingManager : public KeyedService {
// based on whether they're a signed-in ESB user.
base::RepeatingCallback<bool()> get_should_fetch_access_token_;
+ // WebUIInfoSingleton extends PingManager::WebUIDelegate to enable the
+ // workaround of calling AddToCSBRRsSent in WebUIInfoSingleton without /core
+ // having a dependency on /content.
+ raw_ptr<WebUIDelegate> webui_delegate_;
+
+ // The task runner for the UI thread.
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+
+ // Pulls the user population.
+ base::RepeatingCallback<ChromeUserPopulation()> get_user_population_callback_;
+
base::WeakPtrFactory<PingManager> weak_factory_{this};
};
diff --git a/chromium/components/safe_browsing/core/browser/ping_manager_unittest.cc b/chromium/components/safe_browsing/core/browser/ping_manager_unittest.cc
index d5a81d64059..8a063404620 100644
--- a/chromium/components/safe_browsing/core/browser/ping_manager_unittest.cc
+++ b/chromium/components/safe_browsing/core/browser/ping_manager_unittest.cc
@@ -5,13 +5,14 @@
#include "components/safe_browsing/core/browser/ping_manager.h"
#include "base/base64.h"
+#include "base/callback_helpers.h"
#include "base/run_loop.h"
+#include "base/strings/escape.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/safe_browsing/core/browser/db/v4_test_util.h"
#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::Time;
@@ -29,12 +30,13 @@ class PingManagerTest : public testing::Test {
std::string key = google_apis::GetAPIKey();
if (!key.empty()) {
key_param_ = base::StringPrintf(
- "&key=%s", net::EscapeQueryParamValue(key, true).c_str());
+ "&key=%s", base::EscapeQueryParamValue(key, true).c_str());
}
ping_manager_.reset(
new PingManager(safe_browsing::GetTestV4ProtocolConfig(), nullptr,
- nullptr, base::BindRepeating([]() { return false; })));
+ nullptr, base::BindRepeating([]() { return false; }),
+ nullptr, nullptr, base::NullCallback()));
}
PingManager* ping_manager() { return ping_manager_.get(); }
@@ -213,4 +215,12 @@ TEST_F(PingManagerTest, TestThreatDetailsUrl) {
ping_manager()->ThreatDetailsUrl().spec());
}
+TEST_F(PingManagerTest, TestReportThreatDetails_EmptyReport) {
+ std::unique_ptr<ClientSafeBrowsingReportRequest> report =
+ std::make_unique<ClientSafeBrowsingReportRequest>();
+ PingManager::ReportThreatDetailsResult result =
+ ping_manager()->ReportThreatDetails(std::move(report));
+ EXPECT_EQ(result, PingManager::ReportThreatDetailsResult::EMPTY_REPORT);
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/core/browser/realtime/BUILD.gn b/chromium/components/safe_browsing/core/browser/realtime/BUILD.gn
index 924a79816db..6c91a33316a 100644
--- a/chromium/components/safe_browsing/core/browser/realtime/BUILD.gn
+++ b/chromium/components/safe_browsing/core/browser/realtime/BUILD.gn
@@ -87,6 +87,7 @@ source_set("unit_tests") {
"//components/safe_browsing:buildflags",
"//components/safe_browsing/core/browser:referrer_chain_provider",
"//components/safe_browsing/core/browser:token_fetcher",
+ "//components/safe_browsing/core/browser:token_fetcher_testing_helper",
"//components/safe_browsing/core/browser:verdict_cache_manager",
"//components/safe_browsing/core/common",
"//components/safe_browsing/core/common:safe_browsing_prefs",
diff --git a/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc b/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
index fe620aad119..bb4dcd5dbd2 100644
--- a/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
+++ b/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
@@ -312,7 +312,7 @@ RealTimeUrlLookupServiceBase::GetCachedRealTimeUrlVerdict(const GURL& url) {
void RealTimeUrlLookupServiceBase::MayBeCacheRealTimeUrlVerdict(
const GURL& url,
RTLookupResponse response) {
- if (response.threat_info_size() > 0) {
+ if (cache_manager_ && response.threat_info_size() > 0) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&VerdictCacheManager::CacheRealTimeUrlVerdict,
cache_manager_->GetWeakPtr(), url, response,
@@ -406,10 +406,10 @@ void RealTimeUrlLookupServiceBase::SendRequest(
// NOTE: Pass |callback_task_runner| by copying it here as it's also needed
// just below.
- SendRequestInternal(std::move(resource_request), req_data, url,
- access_token_string, std::move(response_callback),
- callback_task_runner,
- request->population().user_population());
+ SendRequestInternal(
+ std::move(resource_request), req_data, url, access_token_string,
+ std::move(response_callback), callback_task_runner,
+ request->population().user_population(), is_sampled_report);
callback_task_runner->PostTask(
FROM_HERE,
@@ -425,7 +425,8 @@ void RealTimeUrlLookupServiceBase::SendRequestInternal(
absl::optional<std::string> access_token_string,
RTLookupResponseCallback response_callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
- ChromeUserPopulation::UserPopulation user_population) {
+ ChromeUserPopulation::UserPopulation user_population,
+ bool is_sampled_report) {
std::unique_ptr<network::SimpleURLLoader> owned_loader =
network::SimpleURLLoader::Create(std::move(resource_request),
GetTrafficAnnotationTag());
@@ -439,7 +440,7 @@ void RealTimeUrlLookupServiceBase::SendRequestInternal(
url_loader_factory_.get(),
base::BindOnce(&RealTimeUrlLookupServiceBase::OnURLLoaderComplete,
GetWeakPtr(), url, access_token_string, loader,
- user_population, base::TimeTicks::Now(),
+ user_population, base::TimeTicks::Now(), is_sampled_report,
std::move(callback_task_runner)));
pending_requests_[owned_loader.release()] = std::move(response_callback);
@@ -451,6 +452,7 @@ void RealTimeUrlLookupServiceBase::OnURLLoaderComplete(
network::SimpleURLLoader* url_loader,
ChromeUserPopulation::UserPopulation user_population,
base::TimeTicks request_start_time,
+ bool is_sampled_report,
scoped_refptr<base::SequencedTaskRunner> response_callback_task_runner,
std::unique_ptr<std::string> response_body) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -466,9 +468,14 @@ void RealTimeUrlLookupServiceBase::OnURLLoaderComplete(
int response_code = 0;
if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
response_code = url_loader->ResponseInfo()->headers->response_code();
+ std::string report_type_suffix =
+ is_sampled_report ? ".SampledPing" : ".NormalPing";
RecordNetworkResultWithAndWithoutSuffix("SafeBrowsing.RT.Network.Result",
GetMetricSuffix(), net_error,
response_code);
+ RecordHttpResponseOrErrorCode(
+ ("SafeBrowsing.RT.Network.Result" + report_type_suffix).c_str(),
+ net_error, response_code);
if (response_code == net::HTTP_UNAUTHORIZED &&
access_token_string.has_value()) {
@@ -481,6 +488,9 @@ void RealTimeUrlLookupServiceBase::OnURLLoaderComplete(
response->ParseFromString(*response_body);
RecordBooleanWithAndWithoutSuffix("SafeBrowsing.RT.IsLookupSuccessful",
GetMetricSuffix(), is_rt_lookup_successful);
+ base::UmaHistogramBoolean(
+ "SafeBrowsing.RT.IsLookupSuccessful" + report_type_suffix,
+ is_rt_lookup_successful);
is_rt_lookup_successful ? HandleLookupSuccess() : HandleLookupError();
MayBeCacheRealTimeUrlVerdict(url, *response);
diff --git a/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.h b/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.h
index 141c4fc2f8c..d9ecab77a37 100644
--- a/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.h
+++ b/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_base.h
@@ -229,7 +229,8 @@ class RealTimeUrlLookupServiceBase : public KeyedService {
absl::optional<std::string> access_token_string,
RTLookupResponseCallback response_callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
- ChromeUserPopulation::UserPopulation user_population);
+ ChromeUserPopulation::UserPopulation user_population,
+ bool is_sampled_report);
// Called when the response from the real-time lookup remote endpoint is
// received. |url_loader| is the unowned loader that was used to send the
@@ -243,6 +244,7 @@ class RealTimeUrlLookupServiceBase : public KeyedService {
network::SimpleURLLoader* url_loader,
ChromeUserPopulation::UserPopulation user_population,
base::TimeTicks request_start_time,
+ bool is_sampled_report,
scoped_refptr<base::SequencedTaskRunner> response_callback_task_runner,
std::unique_ptr<std::string> response_body);
diff --git a/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc b/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
index 323d521aa23..71dff161287 100644
--- a/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
+++ b/chromium/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
@@ -17,6 +17,7 @@
#include "components/safe_browsing/buildflags.h"
#include "components/safe_browsing/core/browser/referrer_chain_provider.h"
#include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
+#include "components/safe_browsing/core/browser/test_safe_browsing_token_fetcher.h"
#include "components/safe_browsing/core/browser/verdict_cache_manager.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
@@ -45,28 +46,6 @@ constexpr char kTestReferrerUrl[] = "http://example.referrer/";
constexpr char kTestSubframeUrl[] = "http://iframe.example.test/";
constexpr char kTestSubframeReferrerUrl[] = "http://iframe.example.referrer/";
-class TestSafeBrowsingTokenFetcher : public SafeBrowsingTokenFetcher {
- public:
- TestSafeBrowsingTokenFetcher() = default;
- ~TestSafeBrowsingTokenFetcher() override {
- // Like SafeBrowsingTokenFetchTracer, trigger the callback when destroyed.
- RunAccessTokenCallback("");
- }
-
- // SafeBrowsingTokenFetcher:
- void Start(Callback callback) override { callback_ = std::move(callback); }
-
- void RunAccessTokenCallback(std::string token) {
- if (callback_)
- std::move(callback_).Run(token);
- }
-
- MOCK_METHOD1(OnInvalidAccessToken, void(const std::string&));
-
- private:
- Callback callback_;
-};
-
class MockReferrerChainProvider : public ReferrerChainProvider {
public:
virtual ~MockReferrerChainProvider() = default;
@@ -1149,11 +1128,6 @@ TEST_F(RealTimeUrlLookupServiceTest, TestShutdown_CallbackNotPostedOnShutdown) {
TEST_F(RealTimeUrlLookupServiceTest, TestShutdown_CacheManagerReset) {
GURL url("https://a.example.test/path1/path2");
- // Post a task to cache_manager_ to cache the verdict.
- MayBeCacheRealTimeUrlVerdict(url, RTLookupResponse::ThreatInfo::DANGEROUS,
- RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING,
- 60, "a.example.test/path1/path2",
- RTLookupResponse::ThreatInfo::COVERING_MATCH);
// Shutdown and delete depending objects.
rt_service()->Shutdown();
@@ -1161,6 +1135,12 @@ TEST_F(RealTimeUrlLookupServiceTest, TestShutdown_CacheManagerReset) {
content_setting_map_->ShutdownOnUIThread();
content_setting_map_.reset();
+ // Post a task to cache_manager_ to cache the verdict.
+ MayBeCacheRealTimeUrlVerdict(url, RTLookupResponse::ThreatInfo::DANGEROUS,
+ RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING,
+ 60, "a.example.test/path1/path2",
+ RTLookupResponse::ThreatInfo::COVERING_MATCH);
+
// The task to cache_manager_ should be cancelled and not cause crash.
task_environment_.RunUntilIdle();
}
@@ -1226,8 +1206,6 @@ TEST_F(RealTimeUrlLookupServiceTest,
// Enable extended reporting.
EnableExtendedReporting();
rt_service()->set_bypass_probability_for_tests(true);
- // When feature is not enabled, a sampled ping should not be sent.
- EXPECT_FALSE(CanSendRTSampleRequest());
feature_list_.InitAndDisableFeature(
safe_browsing::kSendSampledPingsForProtegoAllowlistDomains);
// After enabling the feature, a sampled ping should be sent.
diff --git a/chromium/components/safe_browsing/core/browser/tailored_security_service/OWNERS b/chromium/components/safe_browsing/core/browser/tailored_security_service/OWNERS
index 3a311df9c96..4f674dd2204 100644
--- a/chromium/components/safe_browsing/core/browser/tailored_security_service/OWNERS
+++ b/chromium/components/safe_browsing/core/browser/tailored_security_service/OWNERS
@@ -1,2 +1 @@
-bdea@chromium.org
drubery@chromium.org
diff --git a/chromium/components/safe_browsing/core/browser/tailored_security_service/tailored_security_service.h b/chromium/components/safe_browsing/core/browser/tailored_security_service/tailored_security_service.h
index 3a7a7836579..2f85f3af1dd 100644
--- a/chromium/components/safe_browsing/core/browser/tailored_security_service/tailored_security_service.h
+++ b/chromium/components/safe_browsing/core/browser/tailored_security_service/tailored_security_service.h
@@ -183,7 +183,7 @@ class TailoredSecurityService : public KeyedService {
bool is_shut_down_ = false;
// The preferences for the given profile.
- PrefService* prefs_;
+ raw_ptr<PrefService> prefs_;
// This is used to observe when sync users update their Tailored Security
// setting.
diff --git a/chromium/components/safe_browsing/core/browser/test_safe_browsing_token_fetcher.cc b/chromium/components/safe_browsing/core/browser/test_safe_browsing_token_fetcher.cc
new file mode 100644
index 00000000000..5f07f5324ed
--- /dev/null
+++ b/chromium/components/safe_browsing/core/browser/test_safe_browsing_token_fetcher.cc
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium 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/safe_browsing/core/browser/test_safe_browsing_token_fetcher.h"
+
+namespace safe_browsing {
+
+TestSafeBrowsingTokenFetcher::TestSafeBrowsingTokenFetcher() = default;
+TestSafeBrowsingTokenFetcher::~TestSafeBrowsingTokenFetcher() {
+ // Like SafeBrowsingTokenFetchTracer, trigger the callback when destroyed.
+ RunAccessTokenCallback("");
+}
+void TestSafeBrowsingTokenFetcher::Start(Callback callback) {
+ callback_ = std::move(callback);
+ was_start_called_ = true;
+}
+void TestSafeBrowsingTokenFetcher::RunAccessTokenCallback(std::string token) {
+ if (callback_) {
+ std::move(callback_).Run(token);
+ }
+}
+bool TestSafeBrowsingTokenFetcher::WasStartCalled() {
+ return was_start_called_;
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/core/browser/test_safe_browsing_token_fetcher.h b/chromium/components/safe_browsing/core/browser/test_safe_browsing_token_fetcher.h
new file mode 100644
index 00000000000..78b7cf98eb0
--- /dev/null
+++ b/chromium/components/safe_browsing/core/browser/test_safe_browsing_token_fetcher.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SAFE_BROWSING_CORE_BROWSER_TEST_SAFE_BROWSING_TOKEN_FETCHER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_BROWSER_TEST_SAFE_BROWSING_TOKEN_FETCHER_H_
+
+#include "base/callback.h"
+#include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace safe_browsing {
+
+class TestSafeBrowsingTokenFetcher : public SafeBrowsingTokenFetcher {
+ public:
+ TestSafeBrowsingTokenFetcher();
+ ~TestSafeBrowsingTokenFetcher() override;
+
+ void Start(Callback callback) override;
+ void RunAccessTokenCallback(std::string token);
+ bool WasStartCalled();
+ MOCK_METHOD1(OnInvalidAccessToken, void(const std::string&));
+
+ private:
+ Callback callback_;
+ bool was_start_called_ = false;
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_CORE_BROWSER_TEST_SAFE_BROWSING_TOKEN_FETCHER_H_
diff --git a/chromium/components/safe_browsing/core/browser/verdict_cache_manager.cc b/chromium/components/safe_browsing/core/browser/verdict_cache_manager.cc
index 7ee2f1b7557..ab059db7f06 100644
--- a/chromium/components/safe_browsing/core/browser/verdict_cache_manager.cc
+++ b/chromium/components/safe_browsing/core/browser/verdict_cache_manager.cc
@@ -428,6 +428,11 @@ void VerdictCacheManager::Shutdown() {
history_service_observation_.Reset();
pref_change_registrar_.RemoveAll();
sync_observer_.reset();
+
+ // Clear references to other KeyedServices.
+ content_settings_ = nullptr;
+
+ is_shut_down_ = true;
weak_factory_.InvalidateWeakPtrs();
}
@@ -438,6 +443,9 @@ void VerdictCacheManager::CachePhishGuardVerdict(
ReusedPasswordAccountType password_type,
const LoginReputationClientResponse& verdict,
const base::Time& receive_time) {
+ if (is_shut_down_) {
+ return;
+ }
DCHECK(content_settings_);
DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
@@ -494,6 +502,9 @@ VerdictCacheManager::GetCachedPhishGuardVerdict(
LoginReputationClientResponse* out_response) {
DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+ if (is_shut_down_) {
+ return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+ }
std::string type_key =
GetKeyOfTypeFromTriggerType(trigger_type, password_type);
@@ -505,6 +516,9 @@ VerdictCacheManager::GetCachedPhishGuardVerdict(
size_t VerdictCacheManager::GetStoredPhishGuardVerdictCount(
LoginReputationClientRequest::TriggerType trigger_type) {
+ if (is_shut_down_) {
+ return 0;
+ }
DCHECK(content_settings_);
DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
@@ -535,6 +549,9 @@ size_t VerdictCacheManager::GetStoredPhishGuardVerdictCount(
}
size_t VerdictCacheManager::GetStoredRealTimeUrlCheckVerdictCount() {
+ if (is_shut_down_) {
+ return 0;
+ }
// If we have already computed this, return its value.
if (stored_verdict_count_real_time_url_check_.has_value())
return stored_verdict_count_real_time_url_check_.value();
@@ -558,6 +575,9 @@ void VerdictCacheManager::CacheRealTimeUrlVerdict(
const GURL& url,
const RTLookupResponse& verdict,
const base::Time& receive_time) {
+ if (is_shut_down_) {
+ return;
+ }
std::vector<std::string> visited_cache_expressions;
for (const auto& threat_info : verdict.threat_info()) {
// If |cache_expression_match_type| is unspecified, ignore this entry.
@@ -617,6 +637,9 @@ RTLookupResponse::ThreatInfo::VerdictType
VerdictCacheManager::GetCachedRealTimeUrlVerdict(
const GURL& url,
RTLookupResponse::ThreatInfo* out_threat_info) {
+ if (is_shut_down_) {
+ return RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED;
+ }
return GetMostMatchingCachedVerdictWithHostAndPathMatching<
RTLookupResponse::ThreatInfo>(
url, kRealTimeUrlCacheKey, content_settings_,
@@ -663,6 +686,9 @@ void VerdictCacheManager::ScheduleNextCleanUpAfterInterval(
}
void VerdictCacheManager::CleanUpExpiredVerdicts() {
+ if (is_shut_down_) {
+ return;
+ }
DCHECK(content_settings_);
SCOPED_UMA_HISTOGRAM_TIMER("SafeBrowsing.RT.CacheManager.CleanUpTime");
CleanUpExpiredPhishGuardVerdicts();
@@ -842,6 +868,9 @@ bool VerdictCacheManager::RemoveExpiredRealTimeUrlCheckVerdicts(
void VerdictCacheManager::RemoveContentSettingsOnURLsDeleted(
bool all_history,
const history::URLRows& deleted_rows) {
+ if (is_shut_down_) {
+ return;
+ }
DCHECK(content_settings_);
if (all_history) {
diff --git a/chromium/components/safe_browsing/core/browser/verdict_cache_manager.h b/chromium/components/safe_browsing/core/browser/verdict_cache_manager.h
index 1a7bda21a02..f46d70cc51d 100644
--- a/chromium/components/safe_browsing/core/browser/verdict_cache_manager.h
+++ b/chromium/components/safe_browsing/core/browser/verdict_cache_manager.h
@@ -203,6 +203,8 @@ class VerdictCacheManager : public history::HistoryServiceObserver,
std::unique_ptr<SafeBrowsingSyncObserver> sync_observer_;
+ bool is_shut_down_ = false;
+
base::WeakPtrFactory<VerdictCacheManager> weak_factory_{this};
static bool has_artificial_unsafe_url_;
diff --git a/chromium/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc b/chromium/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc
index b8e1b1f7421..4be882da08a 100644
--- a/chromium/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc
+++ b/chromium/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc
@@ -860,4 +860,27 @@ TEST_F(VerdictCacheManagerTest, TestClearTokenOnSyncStateChanged) {
ASSERT_FALSE(token.has_token_value());
}
+TEST_F(VerdictCacheManagerTest, TestShutdown) {
+ cache_manager_->Shutdown();
+ RTLookupResponse rt_response;
+ // Call to cache_manager after shutdown should not cause a crash.
+ cache_manager_->CacheRealTimeUrlVerdict(GURL("https://www.example.com/"),
+ rt_response, base::Time::Now());
+ RTLookupResponse::ThreatInfo out_rt_verdict;
+ cache_manager_->GetCachedRealTimeUrlVerdict(
+ GURL("https://www.example.com/path"), &out_rt_verdict);
+ LoginReputationClientResponse pg_response;
+ ReusedPasswordAccountType password_type;
+ cache_manager_->CachePhishGuardVerdict(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, password_type,
+ pg_response, base::Time::Now());
+ cache_manager_->GetStoredPhishGuardVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
+ LoginReputationClientResponse out_pg_verdict;
+ cache_manager_->GetCachedPhishGuardVerdict(
+ GURL("https://www.example.com/path"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, password_type,
+ &out_pg_verdict);
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/core/common/fbs/client_model.fbs b/chromium/components/safe_browsing/core/common/fbs/client_model.fbs
index 2cc099860a4..7bb1c227223 100644
--- a/chromium/components/safe_browsing/core/common/fbs/client_model.fbs
+++ b/chromium/components/safe_browsing/core/common/fbs/client_model.fbs
@@ -39,6 +39,7 @@ table ClientSideModel {
tflite_model_input_width: int (deprecated);
tflite_model_input_height: int (deprecated);
tflite_metadata:safe_browsing.flat.TfLiteModelMetadata;
+ dom_model_version:int;
}
root_type ClientSideModel;
diff --git a/chromium/components/safe_browsing/core/common/features.cc b/chromium/components/safe_browsing/core/common/features.cc
index 15ac35d23fa..f516af703a2 100644
--- a/chromium/components/safe_browsing/core/common/features.cc
+++ b/chromium/components/safe_browsing/core/common/features.cc
@@ -46,13 +46,25 @@ extern const base::Feature kClientSideDetectionModelTag{
const base::Feature kClientSideDetectionReferrerChain{
"ClientSideDetectionReferrerChain", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kClientSideDetectionKillswitch{
+ "ClientSideDetectionKillswitch",
+#if BUILDFLAG(IS_MAC)
+ base::FEATURE_ENABLED_BY_DEFAULT
+#else
+ base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
+
const base::Feature kConnectorsScanningAccessToken{
- "ConnectorsScanningAccessToken", base::FEATURE_DISABLED_BY_DEFAULT};
+ "ConnectorsScanningAccessToken", base::FEATURE_ENABLED_BY_DEFAULT};
-// TODO(b/197749390): Add tests for this feature being enabled when it's
-// finalized.
const base::Feature kConnectorsScanningReportOnlyUI{
- "ConnectorsScanningReportOnlyUI", base::FEATURE_DISABLED_BY_DEFAULT};
+ "ConnectorsScanningReportOnlyUI", base::FEATURE_ENABLED_BY_DEFAULT};
+
+#if BUILDFLAG(IS_ANDROID)
+const base::Feature kCreateSafebrowsingOnStartup{
+ "CreateSafebrowsingOnStartup", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
const base::Feature kDelayedWarnings{"SafeBrowsingDelayedWarnings",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -76,6 +88,10 @@ const base::Feature kEnhancedProtection {
#endif
};
+const base::Feature kEnhancedProtectionPhase2IOS{
+ "SafeBrowsingEnhancedProtectionPhase2IOS",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
const base::Feature kExtensionTelemetry{"SafeBrowsingExtensionTelemetry",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -86,6 +102,11 @@ const base::Feature kExtensionTelemetryPersistence{
const base::FeatureParam<int> kExtensionTelemetryUploadIntervalSeconds{
&kExtensionTelemetry, "UploadIntervalSeconds",
/*default_value=*/3600};
+
+const base::FeatureParam<int> kExtensionTelemetryWritesPerInterval{
+ &kExtensionTelemetry, "NumberOfWritesInInterval",
+ /*default_value=*/4};
+
const base::Feature kExtensionTelemetryTabsExecuteScriptSignal{
"SafeBrowsingExtensionTelemetryTabsExecuteScriptSignal",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -110,9 +131,6 @@ const base::Feature kOmitNonUserGesturesFromReferrerChain{
const base::Feature kSafeBrowsingCsbrrWithToken{
"SafeBrowsingCsbrrWithToken", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kSafeBrowsingCTDownloadWarning{
- "SafeBrowsingCTDownloadWarning", base::FEATURE_DISABLED_BY_DEFAULT};
-
const base::Feature kSafeBrowsingEnterpriseCsd{
"SafeBrowsingEnterpriseCsd", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -129,7 +147,7 @@ const base::Feature kSafeBrowsingRemoveCookiesInAuthRequests{
const base::Feature kSendSampledPingsForProtegoAllowlistDomains{
"SafeBrowsingSendSampledPingsForProtegoAllowlistDomains",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kSuspiciousSiteTriggerQuotaFeature{
"SafeBrowsingSuspiciousSiteTriggerQuota", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -137,10 +155,6 @@ const base::Feature kSuspiciousSiteTriggerQuotaFeature{
const base::Feature kThreatDomDetailsTagAndAttributeFeature{
"ThreatDomDetailsTagAttributes", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kTriggerThrottlerDailyQuotaFeature{
- "SafeBrowsingTriggerThrottlerDailyQuota",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
const base::Feature kUseNewDownloadWarnings{"UseNewDownloadWarnings",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -168,6 +182,7 @@ constexpr struct {
{&kDelayedWarnings, true},
{&kDownloadBubble, true},
{&kEnhancedProtection, true},
+ {&kEnhancedProtectionPhase2IOS, true},
{&kExtensionTelemetry, true},
{&kExtensionTelemetryReportContactedHosts, true},
{&kExtensionTelemetryPersistence, true},
@@ -179,7 +194,6 @@ constexpr struct {
{&kSendSampledPingsForProtegoAllowlistDomains, true},
{&kSuspiciousSiteTriggerQuotaFeature, true},
{&kThreatDomDetailsTagAndAttributeFeature, false},
- {&kTriggerThrottlerDailyQuotaFeature, false},
};
// Adds the name and the enabled/disabled status of a given feature.
diff --git a/chromium/components/safe_browsing/core/common/features.h b/chromium/components/safe_browsing/core/common/features.h
index 8c66f7be89f..ca50054e3a1 100644
--- a/chromium/components/safe_browsing/core/common/features.h
+++ b/chromium/components/safe_browsing/core/common/features.h
@@ -43,6 +43,13 @@ const char kClientSideDetectionTagParamName[] = "reporter_omaha_tag";
// Enables client side detection referrer chain.
extern const base::Feature kClientSideDetectionReferrerChain;
+// Killswitch for client side phishing detection. Since client side models are
+// run on a large fraction of navigations, crashes due to the model are very
+// impactful, even if only a small fraction of users have a bad version of the
+// model. This Finch flag allows us to remediate long-tail component versions
+// while we fix the root cause.
+extern const base::Feature kClientSideDetectionKillswitch;
+
// Controls whether an access token is attached to scanning requests triggered
// by enterprise Connectors.
extern const base::Feature kConnectorsScanningAccessToken;
@@ -53,6 +60,13 @@ extern const base::Feature kConnectorsScanningAccessToken;
// instead of just showing an "Open Now" button with the blocking UI.
extern const base::Feature kConnectorsScanningReportOnlyUI;
+// Controls whether to connect to the Safe Browsing service early on startup.
+// The alternative is to connect as soon as the first Safe Browsing check is
+// made associated with a URK request. Android only. On this platform getting
+// the notification about the success of establishing the connection can be
+// delayed by several seconds.
+extern const base::Feature kCreateSafebrowsingOnStartup;
+
// Controls whether the delayed warning experiment is enabled.
extern const base::Feature kDelayedWarnings;
// True if mouse clicks should undelay the warnings immediately when delayed
@@ -65,6 +79,9 @@ extern const base::Feature kDownloadBubble;
// Enables Enhanced Safe Browsing.
extern const base::Feature kEnhancedProtection;
+// Phase 2 of Enhanced Safe Browsing changes.
+extern const base::Feature kEnhancedProtectionPhase2IOS;
+
// Enables collection of signals related to extension activity and uploads
// of telemetry reports to SB servers.
extern const base::Feature kExtensionTelemetry;
@@ -75,6 +92,11 @@ extern const base::Feature kExtensionTelemetryPersistence;
// Specifies the upload interval for extension telemetry reports.
extern const base::FeatureParam<int> kExtensionTelemetryUploadIntervalSeconds;
+
+// Specifies the number of writes the telemetry service will perform during
+// a full upload interval.
+extern const base::FeatureParam<int> kExtensionTelemetryWritesPerInterval;
+
// Enables collection of telemetry signal whenever an extension invokes the
// tabs.executeScript API call.
extern const base::Feature kExtensionTelemetryTabsExecuteScriptSignal;
@@ -96,10 +118,6 @@ extern const base::Feature kOmitNonUserGesturesFromReferrerChain;
// for Enhanced Safe Browsing users
extern const base::Feature kSafeBrowsingCsbrrWithToken;
-// Controls whether users will see an account compromise specific warning
-// when Safe Browsing determines a file is associated with stealing cookies.
-extern const base::Feature kSafeBrowsingCTDownloadWarning;
-
// Controls whether we are performing enterprise download checks for users
// with the appropriate policies enabled.
extern const base::Feature kSafeBrowsingEnterpriseCsd;
@@ -138,16 +156,6 @@ extern const base::Feature kTailoredSecurityIntegration;
// be lower case.
extern const base::Feature kThreatDomDetailsTagAndAttributeFeature;
-// Controls the daily quota for data collection triggers. It's a single param
-// containing a comma-separated list of pairs. The format of the param is
-// "T1,Q1,T2,Q2,...Tn,Qn", where Tx is a TriggerType and Qx is how many reports
-// that trigger is allowed to send per day.
-// TODO(crbug.com/744869): This param should be deprecated after ad sampler
-// launch in favour of having a unique quota feature and param per trigger.
-// Having a single shared feature makes it impossible to run multiple trigger
-// trials simultaneously.
-extern const base::Feature kTriggerThrottlerDailyQuotaFeature;
-
// Controls whether Chrome uses new download warning UX.
extern const base::Feature kUseNewDownloadWarnings;
diff --git a/chromium/components/safe_browsing/core/common/proto/client_model.proto b/chromium/components/safe_browsing/core/common/proto/client_model.proto
index 3c74599ef8a..920711a5700 100644
--- a/chromium/components/safe_browsing/core/common/proto/client_model.proto
+++ b/chromium/components/safe_browsing/core/common/proto/client_model.proto
@@ -70,9 +70,11 @@ message ClientSideModel {
// Page terms in page_term contain at most this many page words.
required int32 max_words_per_term = 5;
- // Model version number. Every model that we train should have a different
- // version number and it should always be larger than the previous model
- // version.
+ optional int32 dom_model_version = 18;
+
+ // The overall client model version number. Every model update should have a
+ // different version number and it should always be larger than the previous
+ // model version.
optional int32 version = 6;
// List of known bad IP subnets.
@@ -107,7 +109,7 @@ message ClientSideModel {
optional TfLiteModelMetadata tflite_metadata = 17;
- // next available tag number: 18
+ // next available tag number: 19
}
message TfLiteModelMetadata {
diff --git a/chromium/components/safe_browsing/core/common/proto/csd.proto b/chromium/components/safe_browsing/core/common/proto/csd.proto
index 0ab63881eee..4112daa103f 100644
--- a/chromium/components/safe_browsing/core/common/proto/csd.proto
+++ b/chromium/components/safe_browsing/core/common/proto/csd.proto
@@ -109,6 +109,18 @@ message ChromeUserPopulation {
// Note: This field is set as repeated to support tokens from multiple
// sources.
repeated PageLoadToken page_load_tokens = 14;
+
+ // The current state of account-level enhanced safe browsing (A-ESB) as is
+ // known by the client. This is an optional field and represents the state of
+ // A-ESB as the client has observed it to be. This value will be set for sync
+ // users as well as signed-in users. The state on the server may be
+ // different from the value that the client has when setting this field.
+ // See: go/esb-mms-integration-dd.
+ optional bool is_aesb_enabled = 15;
+
+ // The time when the account-level enhanced safe browsing (A-ESB) bit state
+ // was last sent updated on the client. This is an optional field.
+ optional int64 aesb_last_update_time_windows_epoch_micros = 16;
}
message ClientPhishingRequest {
@@ -148,6 +160,9 @@ message ClientPhishingRequest {
// sent to the scorer and which resulted in client_score being computed.
repeated Feature feature_map = 5;
+ // The version of the DOM model used for classification
+ optional int32 dom_model_version = 27;
+
// The version number of the model that was used to compute the client-score.
// Copied from ClientSideModel.version().
optional int32 model_version = 6;
@@ -215,7 +230,7 @@ message ClientPhishingRequest {
// users.
optional VisualFeatures visual_features = 26;
- // next available tag number: 27.
+ // next available tag number: 28.
}
message ClientPhishingResponse {
diff --git a/chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc b/chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc
index f63da778f92..cf55188e9e5 100644
--- a/chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc
+++ b/chromium/components/safe_browsing/core/common/safe_browsing_prefs.cc
@@ -9,6 +9,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
@@ -111,7 +112,8 @@ const char kAccountTailoredSecurityShownNotification[] =
"safebrowsing.aesb_shown_notification";
const char kEnhancedProtectionEnabledViaTailoredSecurity[] =
"safebrowsing.esb_enabled_via_tailored_security";
-
+const char kExtensionTelemetryLastUploadTime[] =
+ "safebrowsing.extension_telemetry_last_upload_time";
} // namespace prefs
namespace safe_browsing {
@@ -231,6 +233,17 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
prefs::kAccountTailoredSecurityShownNotification, false);
registry->RegisterBooleanPref(
prefs::kEnhancedProtectionEnabledViaTailoredSecurity, false);
+ registry->RegisterTimePref(prefs::kExtensionTelemetryLastUploadTime,
+ base::Time::Now());
+}
+
+base::Time GetLastUploadTimeForExtensionTelemetry(PrefService& prefs) {
+ return (prefs.GetTime(prefs::kExtensionTelemetryLastUploadTime));
+}
+
+void SetLastUploadTimeForExtensionTelemetry(PrefService& prefs,
+ const base::Time& time) {
+ prefs.SetTime(prefs::kExtensionTelemetryLastUploadTime, time);
}
void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
diff --git a/chromium/components/safe_browsing/core/common/safe_browsing_prefs.h b/chromium/components/safe_browsing/core/common/safe_browsing_prefs.h
index 05136ba29e4..c32c617df1d 100644
--- a/chromium/components/safe_browsing/core/common/safe_browsing_prefs.h
+++ b/chromium/components/safe_browsing/core/common/safe_browsing_prefs.h
@@ -18,6 +18,10 @@ class PrefRegistrySimple;
class PrefService;
class GURL;
+namespace base {
+class Time;
+}
+
namespace prefs {
// A list of times at which CSD pings were sent.
extern const char kSafeBrowsingCsdPingTimestamps[];
@@ -123,6 +127,10 @@ extern const char kAccountTailoredSecurityShownNotification[];
// account tailored security.
extern const char kEnhancedProtectionEnabledViaTailoredSecurity[];
+// The last time the Extension Telemetry Service successfully
+// uploaded its data.
+extern const char kExtensionTelemetryLastUploadTime[];
+
} // namespace prefs
namespace safe_browsing {
@@ -251,6 +259,14 @@ void SetExtendedReportingPrefAndMetric(PrefService* prefs,
// This variant is used to simplify test code by omitting the location.
void SetExtendedReportingPrefForTests(PrefService* prefs, bool value);
+// Sets the last time the Extension Telemetry Service successfully uploaded
+// its data.
+void SetLastUploadTimeForExtensionTelemetry(PrefService& prefs,
+ const base::Time& time);
+
+// Returns the `kExtensionTelemetryLastUploadTime` user preference.
+base::Time GetLastUploadTimeForExtensionTelemetry(PrefService& prefs);
+
// Sets the currently active Safe Browsing Enhanced Protection to the specified
// value.
void SetEnhancedProtectionPrefForTests(PrefService* prefs, bool value);
diff --git a/chromium/components/safe_browsing/core/common/utils.cc b/chromium/components/safe_browsing/core/common/utils.cc
index e9f7c2984a5..b43b1db95cb 100644
--- a/chromium/components/safe_browsing/core/common/utils.cc
+++ b/chromium/components/safe_browsing/core/common/utils.cc
@@ -51,7 +51,7 @@ std::string ShortURLForReporting(const GURL& url) {
ChromeUserPopulation::ProfileManagementStatus GetProfileManagementStatus(
const policy::BrowserPolicyConnector* bpc) {
#if BUILDFLAG(IS_WIN)
- if (base::IsMachineExternallyManaged())
+ if (base::IsManagedDevice())
return ChromeUserPopulation::ENTERPRISE_MANAGED;
else
return ChromeUserPopulation::NOT_MANAGED;
diff --git a/chromium/components/safe_search_api/safe_search/safe_search_url_checker_client.cc b/chromium/components/safe_search_api/safe_search/safe_search_url_checker_client.cc
index bce77656f52..c53542bb3e5 100644
--- a/chromium/components/safe_search_api/safe_search/safe_search_url_checker_client.cc
+++ b/chromium/components/safe_search_api/safe_search/safe_search_url_checker_client.cc
@@ -9,13 +9,13 @@
#include "base/callback.h"
#include "base/json/json_reader.h"
#include "base/metrics/histogram_macros.h"
+#include "base/strings/escape.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/google/core/common/google_util.h"
-#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -34,7 +34,7 @@ const char kDataFormat[] = "key=%s&urls=%s";
// Builds the POST data for SafeSearch API requests.
std::string BuildRequestData(const std::string& api_key, const GURL& url) {
- std::string query = net::EscapeQueryParamValue(url.spec(), true);
+ std::string query = base::EscapeQueryParamValue(url.spec(), true);
return base::StringPrintf(kDataFormat, api_key.c_str(), query.c_str());
}
diff --git a/chromium/components/safe_search_api/safe_search/safe_search_url_checker_client_unittest.cc b/chromium/components/safe_search_api/safe_search/safe_search_url_checker_client_unittest.cc
index 22d06b3dd4f..8bdb464bb86 100644
--- a/chromium/components/safe_search_api/safe_search/safe_search_url_checker_client_unittest.cc
+++ b/chromium/components/safe_search_api/safe_search/safe_search_url_checker_client_unittest.cc
@@ -30,15 +30,13 @@ constexpr char kSafeSearchApiUrl[] =
"https://safesearch.googleapis.com/v1:classify";
std::string BuildResponse(bool is_porn) {
- base::Value dict(base::Value::Type::DICTIONARY);
- auto classification_dict =
- std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+ base::Value::Dict dict;
+ base::Value::Dict classification_dict;
if (is_porn)
- classification_dict->SetBoolKey("pornography", is_porn);
- auto classifications_list = std::make_unique<base::ListValue>();
- classifications_list->Append(std::move(classification_dict));
- dict.SetKey("classifications",
- base::Value::FromUniquePtrValue(std::move(classifications_list)));
+ classification_dict.Set("pornography", is_porn);
+ base::Value::List classifications_list;
+ classifications_list.Append(std::move(classification_dict));
+ dict.Set("classifications", std::move(classifications_list));
std::string result;
base::JSONWriter::Write(dict, &result);
return result;
diff --git a/chromium/components/safe_search_api/stub_url_checker.cc b/chromium/components/safe_search_api/stub_url_checker.cc
index 9ce25d60b9a..e71f00a7ea6 100644
--- a/chromium/components/safe_search_api/stub_url_checker.cc
+++ b/chromium/components/safe_search_api/stub_url_checker.cc
@@ -24,15 +24,13 @@ constexpr char kSafeSearchApiUrl[] =
"https://safesearch.googleapis.com/v1:classify";
std::string BuildResponse(bool is_porn) {
- base::Value dict(base::Value::Type::DICTIONARY);
- auto classification_dict =
- std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+ base::Value::Dict dict;
+ base::Value::Dict classification_dict;
if (is_porn)
- classification_dict->SetBoolKey("pornography", is_porn);
- auto classifications_list = std::make_unique<base::ListValue>();
- classifications_list->Append(std::move(classification_dict));
- dict.SetKey("classifications",
- base::Value::FromUniquePtrValue(std::move(classifications_list)));
+ classification_dict.Set("pornography", is_porn);
+ base::Value::List classifications_list;
+ classifications_list.Append(std::move(classification_dict));
+ dict.Set("classifications", std::move(classifications_list));
std::string result;
base::JSONWriter::Write(dict, &result);
return result;
diff --git a/chromium/components/scheduling_metrics/task_duration_metric_reporter.h b/chromium/components/scheduling_metrics/task_duration_metric_reporter.h
index be3fe49c653..f2c6935dbd2 100644
--- a/chromium/components/scheduling_metrics/task_duration_metric_reporter.h
+++ b/chromium/components/scheduling_metrics/task_duration_metric_reporter.h
@@ -9,6 +9,7 @@
#include "base/component_export.h"
#include "base/metrics/histogram.h"
+#include "base/numerics/clamped_math.h"
#include "base/time/time.h"
namespace base {
diff --git a/chromium/components/search/ntp_features.cc b/chromium/components/search/ntp_features.cc
index 167464a77aa..9f1d283724a 100644
--- a/chromium/components/search/ntp_features.cc
+++ b/chromium/components/search/ntp_features.cc
@@ -22,16 +22,18 @@ const base::Feature kConfirmSuggestionRemovals{
const base::Feature kCacheOneGoogleBar{"CacheOneGoogleBar",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables the removal of the NTP background scrim and forced dark foreground
+// colors for a specific subset of Chrome Web Store themes (see
+// crbug.com/1329552). This is enabled by default to allow finch to disable this
+// NTP treatment in the case of unexpected regressions.
+const base::Feature kCwsScrimRemoval{"CwsScrimRemoval",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
// If enabled, "middle slot" promos on the bottom of the NTP will show a dismiss
// UI that allows users to close them and not see them again.
const base::Feature kDismissPromos{"DismissNtpPromos",
base::FEATURE_DISABLED_BY_DEFAULT};
-// If enabled, queries that are frequently repeated by the user (and are
-// expected to be issued again) are shown as most visited tiles.
-const base::Feature kNtpRepeatableQueries{"NtpRepeatableQueries",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
// If enabled, the NTP "realbox" will be themed like the omnibox
// (same background/text/selected/hover colors).
const base::Feature kRealboxMatchOmniboxTheme{
@@ -70,12 +72,17 @@ const base::Feature kNtpShortcuts{"NtpShortcuts",
const base::Feature kNtpMiddleSlotPromo{"NtpMiddleSlotPromo",
base::FEATURE_ENABLED_BY_DEFAULT};
-// If enabled, modules will be shown.
-const base::Feature kModules{"NtpModules", base::FEATURE_DISABLED_BY_DEFAULT};
+// Dummy feature to set param "NtpModulesLoadTimeoutMillisecondsParam".
+const base::Feature kNtpModulesLoadTimeoutMilliseconds{
+ "NtpModulesLoadTimeoutMilliseconds", base::FEATURE_DISABLED_BY_DEFAULT};
-// If enabled, modules will be loaded even if kModules is disabled. This is
-// useful to determine if a user would have seen modules in order to
-// counterfactually log or trigger.
+// Dummy feature to set param "NtpModulesOrderParam".
+const base::Feature kNtpModulesOrder{"NtpModulesOrder",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+// If enabled, modules will be loaded but not shown. This is useful to determine
+// if a user would have seen modules in order to counterfactually log or
+// trigger.
const base::Feature kNtpModulesLoad{"NtpModulesLoad",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -122,6 +129,12 @@ const base::Feature kNtpPhotosModuleSoftOptOut(
"NtpPhotosModuleSoftOptOut",
base::FEATURE_DISABLED_BY_DEFAULT);
+// If enabled, the single svg image show in Photos opt-in screen will be
+// replaced by constituent images to support i18n.
+const base::Feature kNtpPhotosModuleSplitSvgOptInArtWork(
+ "NtpPhotosModuleSplitSvgOptInArtWork",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
// If enabled, SafeBrowsing module will be shown to a target user.
const base::Feature kNtpSafeBrowsingModule{"NtpSafeBrowsingModule",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -172,7 +185,8 @@ const char kRealboxMatchSearchboxThemeParam[] =
base::TimeDelta GetModulesLoadTimeout() {
std::string param_value = base::GetFieldTrialParamValueByFeature(
- kModules, kNtpModulesLoadTimeoutMillisecondsParam);
+ kNtpModulesLoadTimeoutMilliseconds,
+ kNtpModulesLoadTimeoutMillisecondsParam);
// If the field trial param is not found or cannot be parsed to an unsigned
// integer, return the default value.
unsigned int param_value_as_int = 0;
@@ -183,10 +197,10 @@ base::TimeDelta GetModulesLoadTimeout() {
}
std::vector<std::string> GetModulesOrder() {
- return base::SplitString(
- base::GetFieldTrialParamValueByFeature(kModules, kNtpModulesOrderParam),
- ",:;", base::WhitespaceHandling::TRIM_WHITESPACE,
- base::SplitResult::SPLIT_WANT_NONEMPTY);
+ return base::SplitString(base::GetFieldTrialParamValueByFeature(
+ kNtpModulesOrder, kNtpModulesOrderParam),
+ ",:;", base::WhitespaceHandling::TRIM_WHITESPACE,
+ base::SplitResult::SPLIT_WANT_NONEMPTY);
}
} // namespace ntp_features
diff --git a/chromium/components/search/ntp_features.h b/chromium/components/search/ntp_features.h
index bb77d51cc1d..cf3bcaaa7e8 100644
--- a/chromium/components/search/ntp_features.h
+++ b/chromium/components/search/ntp_features.h
@@ -22,9 +22,9 @@ namespace ntp_features {
extern const base::Feature kConfirmSuggestionRemovals;
extern const base::Feature kCacheOneGoogleBar;
+extern const base::Feature kCwsScrimRemoval;
extern const base::Feature kDismissPromos;
extern const base::Feature kIframeOneGoogleBar;
-extern const base::Feature kNtpRepeatableQueries;
extern const base::Feature kOneGoogleBarModalOverlays;
extern const base::Feature kRealboxMatchOmniboxTheme;
extern const base::Feature kRealboxMatchSearchboxTheme;
@@ -33,7 +33,8 @@ extern const base::Feature kNtpOneGoogleBar;
extern const base::Feature kNtpLogo;
extern const base::Feature kNtpShortcuts;
extern const base::Feature kNtpMiddleSlotPromo;
-extern const base::Feature kModules;
+extern const base::Feature kNtpModulesLoadTimeoutMilliseconds;
+extern const base::Feature kNtpModulesOrder;
extern const base::Feature kNtpModulesLoad;
extern const base::Feature kNtpRecipeTasksModule;
extern const base::Feature kNtpChromeCartModule;
@@ -48,6 +49,7 @@ extern const base::Feature kNtpPhotosModuleSoftOptOut;
extern const base::Feature kNtpPhotosModuleCustomizedOptInTitle;
extern const base::Feature kNtpPhotosModuleCustomizedOptInArtWork;
+extern const base::Feature kNtpPhotosModuleSplitSvgOptInArtWork;
extern const base::Feature kNtpSafeBrowsingModule;
extern const base::Feature kNtpModulesDragAndDrop;
extern const base::Feature kNtpModulesFirstRunExperience;
diff --git a/chromium/components/search_engines/BUILD.gn b/chromium/components/search_engines/BUILD.gn
index dbfbd7eeb27..df37adb5683 100644
--- a/chromium/components/search_engines/BUILD.gn
+++ b/chromium/components/search_engines/BUILD.gn
@@ -68,7 +68,6 @@ static_library("search_engines") {
"//base:i18n",
"//components/country_codes",
"//components/database_utils",
- "//components/history/core/browser",
"//components/infobars/core",
"//components/lens:lens",
"//components/omnibox/common",
diff --git a/chromium/components/search_engines/DEPS b/chromium/components/search_engines/DEPS
index aebf926f741..71fa36c890b 100644
--- a/chromium/components/search_engines/DEPS
+++ b/chromium/components/search_engines/DEPS
@@ -2,7 +2,6 @@ include_rules = [
"+components/country_codes",
"+components/database_utils",
"+components/google/core",
- "+components/history/core",
"+components/infobars/core",
"+components/keyed_service/core",
"+components/lens",
diff --git a/chromium/components/search_engines/android/BUILD.gn b/chromium/components/search_engines/android/BUILD.gn
index 810bbab5987..09d2ff0bcee 100644
--- a/chromium/components/search_engines/android/BUILD.gn
+++ b/chromium/components/search_engines/android/BUILD.gn
@@ -7,6 +7,8 @@ import("//build/config/android/rules.gni")
android_library("java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//url:gurl_java",
diff --git a/chromium/components/search_engines/default_search_manager.cc b/chromium/components/search_engines/default_search_manager.cc
index 77b9c311ede..aeb8330f603 100644
--- a/chromium/components/search_engines/default_search_manager.cc
+++ b/chromium/components/search_engines/default_search_manager.cc
@@ -80,6 +80,8 @@ const char DefaultSearchManager::kCreatedFromPlayAPI[] =
"created_from_play_api";
const char DefaultSearchManager::kPreconnectToSearchUrl[] =
"preconnect_to_search_url";
+const char DefaultSearchManager::kPrefetchLikelyNavigations[] =
+ "prefetch_likely_navigations";
const char DefaultSearchManager::kIsActive[] = "is_active";
const char DefaultSearchManager::kStarterPackId[] = "starter_pack_id";
diff --git a/chromium/components/search_engines/default_search_manager.h b/chromium/components/search_engines/default_search_manager.h
index c3d87556806..ecf0391d1eb 100644
--- a/chromium/components/search_engines/default_search_manager.h
+++ b/chromium/components/search_engines/default_search_manager.h
@@ -63,6 +63,7 @@ class DefaultSearchManager {
static const char kDisabledByPolicy[];
static const char kCreatedFromPlayAPI[];
static const char kPreconnectToSearchUrl[];
+ static const char kPrefetchLikelyNavigations[];
static const char kIsActive[];
static const char kStarterPackId[];
diff --git a/chromium/components/search_engines/default_search_manager_unittest.cc b/chromium/components/search_engines/default_search_manager_unittest.cc
index efcc18a6939..1ba1c871872 100644
--- a/chromium/components/search_engines/default_search_manager_unittest.cc
+++ b/chromium/components/search_engines/default_search_manager_unittest.cc
@@ -30,35 +30,35 @@ namespace {
// TODO(caitkp): TemplateURLData-ify this.
void SetOverrides(sync_preferences::TestingPrefServiceSyncable* prefs,
bool update) {
- prefs->SetUserPref(prefs::kSearchProviderOverridesVersion,
- std::make_unique<base::Value>(1));
- auto overrides = std::make_unique<base::ListValue>();
- auto entry = std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
-
- entry->SetStringKey("name", update ? "new_foo" : "foo");
- entry->SetStringKey("keyword", update ? "new_fook" : "fook");
- entry->SetStringKey("search_url", "http://foo.com/s?q={searchTerms}");
- entry->SetStringKey("favicon_url", "http://foi.com/favicon.ico");
- entry->SetStringKey("encoding", "UTF-8");
- entry->SetIntKey("id", 1001);
- entry->SetStringKey("suggest_url", "http://foo.com/suggest?q={searchTerms}");
- base::ListValue alternate_urls;
+ prefs->SetUserPref(prefs::kSearchProviderOverridesVersion, base::Value(1));
+ base::Value::List overrides;
+ base::Value::Dict entry;
+
+ entry.Set("name", update ? "new_foo" : "foo");
+ entry.Set("keyword", update ? "new_fook" : "fook");
+ entry.Set("search_url", "http://foo.com/s?q={searchTerms}");
+ entry.Set("favicon_url", "http://foi.com/favicon.ico");
+ entry.Set("encoding", "UTF-8");
+ entry.Set("id", 1001);
+ entry.Set("suggest_url", "http://foo.com/suggest?q={searchTerms}");
+ base::Value::List alternate_urls;
alternate_urls.Append("http://foo.com/alternate?q={searchTerms}");
- entry->SetKey("alternate_urls", std::move(alternate_urls));
- overrides->Append(std::move(entry));
-
- entry = std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
- entry->SetIntKey("id", 1002);
- entry->SetStringKey("name", update ? "new_bar" : "bar");
- entry->SetStringKey("keyword", update ? "new_bark" : "bark");
- entry->SetStringKey("encoding", std::string());
- overrides->Append(std::make_unique<base::Value>(entry->Clone()));
- entry->SetIntKey("id", 1003);
- entry->SetStringKey("name", "baz");
- entry->SetStringKey("keyword", "bazk");
- entry->SetStringKey("encoding", "UTF-8");
- overrides->Append(std::move(entry));
- prefs->SetUserPref(prefs::kSearchProviderOverrides, std::move(overrides));
+ entry.Set("alternate_urls", std::move(alternate_urls));
+ overrides.Append(std::move(entry));
+
+ entry = base::Value::Dict();
+ entry.Set("id", 1002);
+ entry.Set("name", update ? "new_bar" : "bar");
+ entry.Set("keyword", update ? "new_bark" : "bark");
+ entry.Set("encoding", std::string());
+ overrides.Append(entry.Clone());
+ entry.Set("id", 1003);
+ entry.Set("name", "baz");
+ entry.Set("keyword", "bazk");
+ entry.Set("encoding", "UTF-8");
+ overrides.Append(std::move(entry));
+ prefs->SetUserPref(prefs::kSearchProviderOverrides,
+ base::Value(std::move(overrides)));
}
void SetPolicy(sync_preferences::TestingPrefServiceSyncable* prefs,
diff --git a/chromium/components/search_engines/default_search_policy_handler.cc b/chromium/components/search_engines/default_search_policy_handler.cc
index 8c3dd4b23de..77afa2b4621 100644
--- a/chromium/components/search_engines/default_search_policy_handler.cc
+++ b/chromium/components/search_engines/default_search_policy_handler.cc
@@ -197,6 +197,7 @@ void DefaultSearchPolicyHandler::ApplyPolicySettings(const PolicyMap& policies,
dict->SetString(DefaultSearchManager::kID,
base::NumberToString(kInvalidTemplateURLID));
dict->SetInteger(DefaultSearchManager::kPrepopulateID, 0);
+ dict->SetInteger(DefaultSearchManager::kStarterPackId, 0);
dict->SetString(DefaultSearchManager::kSyncGUID, std::string());
dict->SetString(DefaultSearchManager::kOriginatingURL, std::string());
dict->SetBoolean(DefaultSearchManager::kSafeForAutoReplace, true);
diff --git a/chromium/components/search_engines/keyword_table.cc b/chromium/components/search_engines/keyword_table.cc
index 7711220220b..7878a641812 100644
--- a/chromium/components/search_engines/keyword_table.cc
+++ b/chromium/components/search_engines/keyword_table.cc
@@ -21,7 +21,6 @@
#include "base/time/time.h"
#include "base/values.h"
#include "components/database_utils/url_converter.h"
-#include "components/history/core/browser/url_database.h"
#include "components/search_engines/search_terms_data.h"
#include "components/search_engines/template_url.h"
#include "components/webdata/common/web_database.h"
diff --git a/chromium/components/search_engines/keyword_web_data_service.cc b/chromium/components/search_engines/keyword_web_data_service.cc
index 6ccf3e4d9ca..64f9f210397 100644
--- a/chromium/components/search_engines/keyword_web_data_service.cc
+++ b/chromium/components/search_engines/keyword_web_data_service.cc
@@ -32,6 +32,7 @@ std::unique_ptr<WDTypedResult> GetKeywordsImpl(WebDatabase* db) {
result.default_search_provider_id =
keyword_table->GetDefaultSearchProviderID();
result.builtin_keyword_version = keyword_table->GetBuiltinKeywordVersion();
+ result.starter_pack_version = keyword_table->GetStarterPackKeywordVersion();
return std::make_unique<WDResult<WDKeywordsResult>>(KEYWORDS_RESULT, result);
}
diff --git a/chromium/components/search_engines/keyword_web_data_service.h b/chromium/components/search_engines/keyword_web_data_service.h
index cb418bf7f6a..9066466cd88 100644
--- a/chromium/components/search_engines/keyword_web_data_service.h
+++ b/chromium/components/search_engines/keyword_web_data_service.h
@@ -32,8 +32,10 @@ struct WDKeywordsResult {
// Identifies the ID of the TemplateURL that is the default search. A value of
// 0 indicates there is no default search provider.
int64_t default_search_provider_id = 0;
- // Version of the built-in keywords. A value of 0 indicates a first run.
+ // Version of the built-in keywords and starter pack engines. A value of 0
+ // indicates a first run.
int builtin_keyword_version = 0;
+ int starter_pack_version = 0;
};
class WebDataServiceConsumer;
diff --git a/chromium/components/search_engines/prepopulated_engines.json b/chromium/components/search_engines/prepopulated_engines.json
index 13572d43554..765fe245ba1 100644
--- a/chromium/components/search_engines/prepopulated_engines.json
+++ b/chromium/components/search_engines/prepopulated_engines.json
@@ -28,7 +28,7 @@
// Increment this if you change the data in ways that mean users with
// existing data should get a new version. Otherwise, existing data may
// continue to be used and updates made here will not always appear.
- "kCurrentDataVersion": 128
+ "kCurrentDataVersion": 129
},
// The following engines are included in country lists and are added to the
@@ -131,6 +131,7 @@
],
"type": "SEARCH_ENGINE_GOOGLE",
"preconnect_to_search_url" : "ALLOWED",
+ "prefetch_likely_navigations" : "ALLOWED",
"id": 1
},
diff --git a/chromium/components/search_engines/prepopulated_engines_schema.json b/chromium/components/search_engines/prepopulated_engines_schema.json
index ba9ad4c654a..c1e6022448c 100644
--- a/chromium/components/search_engines/prepopulated_engines_schema.json
+++ b/chromium/components/search_engines/prepopulated_engines_schema.json
@@ -72,6 +72,10 @@
// the engine is set as the "default search engine". "ALLOWED" is the only
// value that will enable the pre-connections.
{ "field": "preconnect_to_search_url", "type": "string", "optional": true },
+ // Whether the client is allowed to prefetch Search queries that are likely
+ // (in addition to queries that are recommended via suggestion server). This
+ // is experimental.
+ { "field": "prefetch_likely_navigations", "type": "string", "optional": true },
// Unique id for this prepopulate engine (corresponds to
// TemplateURL::prepopulate_id). This ID must be greater than zero and must
// remain the same for a particular site regardless of how the url changes;
diff --git a/chromium/components/search_engines/search_engine_type.h b/chromium/components/search_engines/search_engine_type.h
index 7c972b57073..2d99e02049c 100644
--- a/chromium/components/search_engines/search_engine_type.h
+++ b/chromium/components/search_engines/search_engine_type.h
@@ -75,6 +75,9 @@ enum SearchEngineType {
SEARCH_ENGINE_PRIVACYWALL = 58,
SEARCH_ENGINE_ECOSIA = 59,
SEARCH_ENGINE_PETALSEARCH = 60,
+ SEARCH_ENGINE_STARTER_PACK_BOOKMARKS = 61,
+ SEARCH_ENGINE_STARTER_PACK_HISTORY = 62,
+ SEARCH_ENGINE_STARTER_PACK_TABS = 63,
SEARCH_ENGINE_MAX // Bounding value needed for UMA histogram macro.
};
diff --git a/chromium/components/search_engines/search_host_to_urls_map.cc b/chromium/components/search_engines/search_host_to_urls_map.cc
index 5c6e99b7041..2a4fe6a0914 100644
--- a/chromium/components/search_engines/search_host_to_urls_map.cc
+++ b/chromium/components/search_engines/search_host_to_urls_map.cc
@@ -4,6 +4,7 @@
#include "components/search_engines/search_host_to_urls_map.h"
+#include <algorithm>
#include <memory>
#include "components/search_engines/template_url.h"
@@ -59,7 +60,15 @@ TemplateURL* SearchHostToURLsMap::GetTemplateURLForHost(
HostToURLsMap::const_iterator iter = host_to_urls_map_.find(host);
if (iter == host_to_urls_map_.end() || iter->second.empty())
return nullptr;
- return *(iter->second.begin()); // Return the 1st element.
+
+ // Because we have to happily tolerate duplicates in TemplateURLService now,
+ /// return the best TemplateURL for `host`, just like
+ // `GetTemplateURLForKeyword` returns the best TemplateURL for a keyword.
+ return *std::min_element(
+ iter->second.begin(), iter->second.end(),
+ [](const auto& a, const auto& b) {
+ return a->IsBetterThanEngineWithConflictingKeyword(b);
+ });
}
SearchHostToURLsMap::TemplateURLSet* SearchHostToURLsMap::GetURLsForHost(
diff --git a/chromium/components/search_engines/search_host_to_urls_map.h b/chromium/components/search_engines/search_host_to_urls_map.h
index b8d030a3d60..a8801d28347 100644
--- a/chromium/components/search_engines/search_host_to_urls_map.h
+++ b/chromium/components/search_engines/search_host_to_urls_map.h
@@ -39,8 +39,8 @@ class SearchHostToURLsMap {
// Removes the TemplateURL from the lookup.
void Remove(const TemplateURL* template_url);
- // Returns the first TemplateURL found with a URL using the specified |host|,
- // or NULL if there are no such TemplateURLs
+ // Returns the best TemplateURL found with a URL using the specified |host|,
+ // or nullptr if there are no such TemplateURLs
TemplateURL* GetTemplateURLForHost(base::StringPiece host);
// Return the TemplateURLSet for the given the |host| or NULL if there are
diff --git a/chromium/components/search_engines/search_host_to_urls_map_unittest.cc b/chromium/components/search_engines/search_host_to_urls_map_unittest.cc
index 54d4215bb32..0268835fd51 100644
--- a/chromium/components/search_engines/search_host_to_urls_map_unittest.cc
+++ b/chromium/components/search_engines/search_host_to_urls_map_unittest.cc
@@ -36,8 +36,12 @@ void SearchHostToURLsMapTest::SetUp() {
host_ = "www.unittest.com";
TemplateURLData data;
data.SetURL("http://" + host_ + "/path1");
+ data.last_modified = base::Time() + base::Microseconds(15);
template_urls_.push_back(std::make_unique<TemplateURL>(data));
+
+ // The second one should be slightly newer.
data.SetURL("http://" + host_ + "/path2");
+ data.last_modified = base::Time() + base::Microseconds(25);
template_urls_.push_back(std::make_unique<TemplateURL>(data));
provider_map_ = std::make_unique<SearchHostToURLsMap>();
@@ -71,10 +75,22 @@ TEST_F(SearchHostToURLsMapTest, Remove) {
ASSERT_EQ(1, url_count);
}
-TEST_F(SearchHostToURLsMapTest, GetTemplateURLForKnownHost) {
+TEST_F(SearchHostToURLsMapTest, GetsBestTemplateURLForKnownHost) {
+ // The second one should be slightly newer.
const TemplateURL* found_url = provider_map_->GetTemplateURLForHost(host_);
- ASSERT_TRUE(found_url == template_urls_[0].get() ||
- found_url == template_urls_[1].get());
+ ASSERT_TRUE(found_url == template_urls_[1].get());
+
+ TemplateURLData data;
+ data.SetURL("http://" + host_ + "/path1");
+ // Make the new TemplateURL "better" by having it created by policy.
+ data.created_by_policy = true;
+
+ TemplateURL new_t_url(data);
+ provider_map_->Add(&new_t_url, SearchTermsData());
+
+ found_url = provider_map_->GetTemplateURLForHost(host_);
+ EXPECT_EQ(found_url, &new_t_url) << "We should have found the new better "
+ "TemplateURL that was created by policy.";
}
TEST_F(SearchHostToURLsMapTest, GetTemplateURLForUnknownHost) {
diff --git a/chromium/components/search_engines/template_url.cc b/chromium/components/search_engines/template_url.cc
index 001561251e6..dfdaf68d047 100644
--- a/chromium/components/search_engines/template_url.cc
+++ b/chromium/components/search_engines/template_url.cc
@@ -9,6 +9,7 @@
#include <vector>
#include "base/base64.h"
+#include "base/base64url.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/adapters.h"
@@ -21,6 +22,7 @@
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
+#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
@@ -36,7 +38,6 @@
#include "components/search_engines/search_terms_data.h"
#include "components/url_formatter/url_formatter.h"
#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
#include "net/base/mime_util.h"
#include "net/base/url_util.h"
#include "third_party/metrics_proto/omnibox_input_type.pb.h"
@@ -110,9 +111,9 @@ bool TryEncoding(const std::u16string& terms,
&encoded_terms)) {
return false;
}
- *escaped_terms = base::UTF8ToUTF16(is_in_query ?
- net::EscapeQueryParamValue(encoded_terms, true) :
- net::EscapePath(encoded_terms));
+ *escaped_terms = base::UTF8ToUTF16(
+ is_in_query ? base::EscapeQueryParamValue(encoded_terms, true)
+ : base::EscapePath(encoded_terms));
if (original_query.empty())
return true;
std::string encoded_original_query;
@@ -120,7 +121,7 @@ bool TryEncoding(const std::u16string& terms,
&encoded_original_query))
return false;
*escaped_original_query = base::UTF8ToUTF16(
- net::EscapeQueryParamValue(encoded_original_query, true));
+ base::EscapeQueryParamValue(encoded_original_query, true));
return true;
}
@@ -502,13 +503,13 @@ std::u16string TemplateURLRef::SearchTermToString16(
const std::vector<std::string>& encodings = owner_->input_encodings();
std::u16string result;
- net::UnescapeRule::Type unescape_rules =
- net::UnescapeRule::SPACES | net::UnescapeRule::PATH_SEPARATORS |
- net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS;
+ base::UnescapeRule::Type unescape_rules =
+ base::UnescapeRule::SPACES | base::UnescapeRule::PATH_SEPARATORS |
+ base::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS;
if (search_term_key_location_ != url::Parsed::PATH)
- unescape_rules |= net::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
+ unescape_rules |= base::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
- std::string unescaped = net::UnescapeURLComponent(term, unescape_rules);
+ std::string unescaped = base::UnescapeURLComponent(term, unescape_rules);
for (size_t i = 0; i < encodings.size(); ++i) {
if (base::CodepageToUTF16(unescaped, encodings[i].c_str(),
base::OnStringConversionError::FAIL, &result))
@@ -524,7 +525,7 @@ std::u16string TemplateURLRef::SearchTermToString16(
// encoding is. We need to substitute spaces for pluses ourselves since we're
// not sending it through an unescaper.
result = base::UTF8ToUTF16(term);
- if (unescape_rules & net::UnescapeRule::REPLACE_PLUS_WITH_SPACE)
+ if (unescape_rules & base::UnescapeRule::REPLACE_PLUS_WITH_SPACE)
std::replace(result.begin(), result.end(), '+', ' ');
return result;
}
@@ -748,7 +749,7 @@ bool TemplateURLRef::ParseParameter(size_t start,
#endif
} else if (parameter == "google:suggestAPIKeyParameter") {
url->insert(start,
- net::EscapeQueryParamValue(google_apis::GetAPIKey(), false));
+ base::EscapeQueryParamValue(google_apis::GetAPIKey(), false));
} else if (parameter == "google:suggestClient") {
replacements->push_back(Replacement(GOOGLE_SUGGEST_CLIENT, start));
} else if (parameter == "google:suggestRid") {
@@ -1098,8 +1099,9 @@ std::string TemplateURLRef::HandleReplacements(
&serialized_searchbox_stats);
if (!serialized_searchbox_stats.empty()) {
std::string encoded_searchbox_stats;
- base::Base64Encode(serialized_searchbox_stats,
- &encoded_searchbox_stats);
+ base::Base64UrlEncode(serialized_searchbox_stats,
+ base::Base64UrlEncodePolicy::OMIT_PADDING,
+ &encoded_searchbox_stats);
HandleReplacement("gs_lcrp", encoded_searchbox_stats, replacement,
&url);
base::UmaHistogramCounts1000(
@@ -1135,8 +1137,8 @@ std::string TemplateURLRef::HandleReplacements(
DCHECK(!replacement.is_post_param);
if (!search_terms_args.current_page_url.empty()) {
const std::string& escaped_current_page_url =
- net::EscapeQueryParamValue(search_terms_args.current_page_url,
- true);
+ base::EscapeQueryParamValue(search_terms_args.current_page_url,
+ true);
HandleReplacement("url", escaped_current_page_url, replacement, &url);
}
break;
@@ -1446,6 +1448,8 @@ bool TemplateURL::IsBetterThanEngineWithConflictingKeyword(
engine->created_from_play_api(),
// Favor prepopulated engines over other auto-generated engines.
engine->prepopulate_id() > 0,
+ // Favor starter pack engines over other auto-generated engines.
+ engine->starter_pack_id() > 0,
// Favor engines derived from OpenSearch descriptions over
// autogenerated engines heuristically generated from searchable forms.
engine->originating_url().is_valid(),
diff --git a/chromium/components/search_engines/template_url_data.cc b/chromium/components/search_engines/template_url_data.cc
index ecba073653f..f3c8a578f7b 100644
--- a/chromium/components/search_engines/template_url_data.cc
+++ b/chromium/components/search_engines/template_url_data.cc
@@ -16,18 +16,24 @@
namespace {
-// Returns a GUID used for sync, which is random except for prepopulated search
+// Returns a GUID used for sync, which is random except for built-in search
// engines. The latter benefit from using a deterministic GUID, to make sure
// sync doesn't incur in duplicates for prepopulated engines.
-std::string GenerateGUID(int prepopulate_id) {
+std::string GenerateGUID(int prepopulate_id, int starter_pack_id) {
+ // We compute a GUID deterministically given |prepopulate_id| or
+ // |starter_pack_id|, using an arbitrary base GUID.
+ std::string guid;
// IDs above 1000 are reserved for distribution custom engines.
- if (prepopulate_id <= 0 || prepopulate_id > 1000)
- return base::GenerateGUID();
+ if (prepopulate_id > 0 && prepopulate_id <= 1000) {
+ guid = base::StringPrintf("485bf7d3-0215-45af-87dc-538868%06d",
+ prepopulate_id);
+ } else if (starter_pack_id > 0) {
+ guid = base::StringPrintf("ec205736-edd7-4022-a9a3-b431fc%06d",
+ starter_pack_id);
+ } else {
+ guid = base::GenerateGUID();
+ }
- // We compute a GUID deterministically given |prepopulate_id|, using an
- // arbitrary base GUID.
- std::string guid =
- base::StringPrintf("485bf7d3-0215-45af-87dc-538868%06d", prepopulate_id);
DCHECK(base::IsValidGUID(guid));
return guid;
}
@@ -70,6 +76,7 @@ TemplateURLData::TemplateURLData(const std::u16string& name,
base::StringPiece encoding,
const base::Value& alternate_urls_list,
bool preconnect_to_search_url,
+ bool prefetch_likely_navigations,
int prepopulate_id)
: suggestions_url(suggest_url),
image_url(image_url),
@@ -90,8 +97,9 @@ TemplateURLData::TemplateURLData(const std::u16string& name,
created_from_play_api(false),
usage_count(0),
prepopulate_id(prepopulate_id),
- sync_guid(GenerateGUID(prepopulate_id)),
- preconnect_to_search_url(preconnect_to_search_url) {
+ sync_guid(GenerateGUID(prepopulate_id, 0)),
+ preconnect_to_search_url(preconnect_to_search_url),
+ prefetch_likely_navigations(prefetch_likely_navigations) {
SetShortName(name);
SetKeyword(keyword);
SetURL(std::string(search_url));
@@ -133,7 +141,7 @@ void TemplateURLData::SetURL(const std::string& url) {
}
void TemplateURLData::GenerateSyncGUID() {
- sync_guid = GenerateGUID(prepopulate_id);
+ sync_guid = GenerateGUID(prepopulate_id, starter_pack_id);
}
size_t TemplateURLData::EstimateMemoryUsage() const {
diff --git a/chromium/components/search_engines/template_url_data.h b/chromium/components/search_engines/template_url_data.h
index 72765df9181..069cfd85d1a 100644
--- a/chromium/components/search_engines/template_url_data.h
+++ b/chromium/components/search_engines/template_url_data.h
@@ -44,6 +44,7 @@ struct TemplateURLData {
base::StringPiece encoding,
const base::Value& alternate_urls_list,
bool preconnect_to_search_url,
+ bool prefetch_likely_navigations,
int prepopulate_id);
~TemplateURLData();
@@ -64,8 +65,9 @@ struct TemplateURLData {
const std::string& url() const { return url_; }
// Recomputes |sync_guid| using the same logic as in the constructor. This
- // means a random GUID is generated, except for prepopulated search engines,
- // which generate GUIDs deterministically based on |prepopulate_id|.
+ // means a random GUID is generated, except for built-in search engines,
+ // which generate GUIDs deterministically based on |prepopulate_id| or
+ // |starter_pack_id|.
void GenerateSyncGUID();
// Estimates dynamic memory usage.
@@ -160,6 +162,11 @@ struct TemplateURLData {
// set as the "default search engine".
bool preconnect_to_search_url = false;
+ // Whether the client is allowed to prefetch Search queries that are likely
+ // (in addition to queries that are recommended via suggestion server). This
+ // is experimental.
+ bool prefetch_likely_navigations = false;
+
enum class ActiveStatus {
kUnspecified = 0, // The default value when a search engine is auto-added.
kTrue, // Search engine is active.
diff --git a/chromium/components/search_engines/template_url_data_unittest.cc b/chromium/components/search_engines/template_url_data_unittest.cc
index 6300e698754..32e9c3f4ed3 100644
--- a/chromium/components/search_engines/template_url_data_unittest.cc
+++ b/chromium/components/search_engines/template_url_data_unittest.cc
@@ -16,7 +16,7 @@ TEST(TemplateURLDataTest, Trim) {
base::StringPiece(), base::StringPiece(), base::StringPiece(),
base::StringPiece(), base::StringPiece(), base::StringPiece(),
base::StringPiece(), base::StringPiece(), base::StringPiece(),
- base::ListValue(), false, 0);
+ base::ListValue(), false, false, 0);
EXPECT_EQ(u"shortname", data.short_name());
EXPECT_EQ(u"keyword", data.keyword());
diff --git a/chromium/components/search_engines/template_url_data_util.cc b/chromium/components/search_engines/template_url_data_util.cc
index 4fa76bf3845..f1c98f60e27 100644
--- a/chromium/components/search_engines/template_url_data_util.cc
+++ b/chromium/components/search_engines/template_url_data_util.cc
@@ -54,6 +54,9 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromDictionary(
result->SetShortName(base::UTF8ToUTF16(*short_name));
result->prepopulate_id = dict.FindIntKey(DefaultSearchManager::kPrepopulateID)
.value_or(result->prepopulate_id);
+ result->starter_pack_id =
+ dict.FindIntKey(DefaultSearchManager::kStarterPackId)
+ .value_or(result->starter_pack_id);
string_value = dict.FindStringKey(DefaultSearchManager::kSyncGUID);
if (string_value) {
result->sync_guid = *string_value;
@@ -179,6 +182,9 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromDictionary(
result->preconnect_to_search_url =
dict.FindBoolKey(DefaultSearchManager::kPreconnectToSearchUrl)
.value_or(result->preconnect_to_search_url);
+ result->prefetch_likely_navigations =
+ dict.FindBoolKey(DefaultSearchManager::kPrefetchLikelyNavigations)
+ .value_or(result->prefetch_likely_navigations);
result->is_active = static_cast<TemplateURLData::ActiveStatus>(
dict.FindIntKey(DefaultSearchManager::kIsActive)
.value_or(static_cast<int>(result->is_active)));
@@ -194,6 +200,8 @@ std::unique_ptr<base::DictionaryValue> TemplateURLDataToDictionary(
url_dict->SetStringKey(DefaultSearchManager::kKeyword, data.keyword());
url_dict->SetIntKey(DefaultSearchManager::kPrepopulateID,
data.prepopulate_id);
+ url_dict->SetIntKey(DefaultSearchManager::kStarterPackId,
+ data.starter_pack_id);
url_dict->SetStringKey(DefaultSearchManager::kSyncGUID, data.sync_guid);
url_dict->SetStringKey(DefaultSearchManager::kURL, data.url());
@@ -252,6 +260,8 @@ std::unique_ptr<base::DictionaryValue> TemplateURLDataToDictionary(
data.created_from_play_api);
url_dict->SetBoolKey(DefaultSearchManager::kPreconnectToSearchUrl,
data.preconnect_to_search_url);
+ url_dict->SetBoolKey(DefaultSearchManager::kPrefetchLikelyNavigations,
+ data.prefetch_likely_navigations);
url_dict->SetIntKey(DefaultSearchManager::kIsActive,
static_cast<int>(data.is_active));
return url_dict;
@@ -277,7 +287,9 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromPrepopulatedEngine(
ToStringPiece(engine.side_search_param),
ToStringPiece(engine.favicon_url), ToStringPiece(engine.encoding),
alternate_urls,
- ToStringPiece(engine.preconnect_to_search_url) == "ALLOWED", engine.id);
+ ToStringPiece(engine.preconnect_to_search_url) == "ALLOWED",
+ ToStringPiece(engine.prefetch_likely_navigations) == "ALLOWED",
+ engine.id);
}
std::unique_ptr<TemplateURLData> TemplateURLDataFromOverrideDictionary(
@@ -332,6 +344,7 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromOverrideDictionary(
std::string image_url_post_params;
std::string side_search_param;
std::string preconnect_to_search_url;
+ std::string prefetch_likely_navigations;
string_value = engine.FindStringKey("suggest_url");
if (string_value) {
@@ -377,13 +390,18 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromOverrideDictionary(
if (string_value) {
preconnect_to_search_url = *string_value;
}
+ string_value = engine.FindStringKey("prefetch_likely_navigations");
+ if (string_value) {
+ prefetch_likely_navigations = *string_value;
+ }
return std::make_unique<TemplateURLData>(
name, keyword, search_url, suggest_url, image_url, new_tab_url,
contextual_search_url, logo_url, doodle_url, search_url_post_params,
suggest_url_post_params, image_url_post_params, side_search_param,
favicon_url, encoding, *alternate_urls,
- preconnect_to_search_url.compare("ALLOWED") == 0, *id);
+ preconnect_to_search_url.compare("ALLOWED") == 0,
+ prefetch_likely_navigations.compare("ALLOWED") == 0, *id);
}
return nullptr;
}
@@ -396,6 +414,7 @@ std::unique_ptr<TemplateURLData> TemplateURLDataFromStarterPackEngine(
turl->SetURL(engine.search_url);
turl->favicon_url = GURL(ToStringPiece(engine.favicon_url));
turl->starter_pack_id = engine.id;
+ turl->GenerateSyncGUID();
turl->is_active = TemplateURLData::ActiveStatus::kTrue;
return turl;
diff --git a/chromium/components/search_engines/template_url_id.h b/chromium/components/search_engines/template_url_id.h
index f2737c2e305..3e6b0ee6894 100644
--- a/chromium/components/search_engines/template_url_id.h
+++ b/chromium/components/search_engines/template_url_id.h
@@ -5,9 +5,8 @@
#ifndef COMPONENTS_SEARCH_ENGINES_TEMPLATE_URL_ID_H_
#define COMPONENTS_SEARCH_ENGINES_TEMPLATE_URL_ID_H_
-#include "components/history/core/browser/keyword_id.h"
-
-typedef history::KeywordID TemplateURLID;
+// ID of a search provider.
+using TemplateURLID = int64_t;
const TemplateURLID kInvalidTemplateURLID = 0;
diff --git a/chromium/components/search_engines/template_url_parser.cc b/chromium/components/search_engines/template_url_parser.cc
index 8d38add1aa9..4ac097464ee 100644
--- a/chromium/components/search_engines/template_url_parser.cc
+++ b/chromium/components/search_engines/template_url_parser.cc
@@ -232,7 +232,7 @@ void SafeTemplateURLParser::ParseURLs(
data_decoder::GetXmlElementAttribute(*url_value, kURLTemplateAttribute);
std::string type =
data_decoder::GetXmlElementAttribute(*url_value, kURLTypeAttribute);
- bool is_post = base::LowerCaseEqualsASCII(
+ bool is_post = base::EqualsCaseInsensitiveASCII(
data_decoder::GetXmlElementAttribute(*url_value, kParamMethodAttribute),
"post");
bool is_html_url = (type == kHTMLType);
diff --git a/chromium/components/search_engines/template_url_service.cc b/chromium/components/search_engines/template_url_service.cc
index 4e22b44143b..a9536ea6a30 100644
--- a/chromium/components/search_engines/template_url_service.cc
+++ b/chromium/components/search_engines/template_url_service.cc
@@ -31,6 +31,7 @@
#include "components/search_engines/template_url_prepopulate_data.h"
#include "components/search_engines/template_url_service_client.h"
#include "components/search_engines/template_url_service_observer.h"
+#include "components/search_engines/template_url_starter_pack_data.h"
#include "components/search_engines/util.h"
#include "components/sync/model/sync_change.h"
#include "components/sync/model/sync_change_processor.h"
@@ -445,8 +446,10 @@ TemplateURL* TemplateURLService::GetTemplateURLForHost(
const TemplateURL* TemplateURLService::GetTemplateURLForHost(
const std::string& host) const {
- if (loaded_)
+ if (loaded_) {
+ // `provider_map_` takes care of finding the best TemplateURL for `host`.
return provider_map_->GetTemplateURLForHost(host);
+ }
TemplateURL* initial_dsp = initial_default_search_provider_.get();
return (initial_dsp &&
(initial_dsp->GenerateSearchURL(search_terms_data()).host_piece() ==
@@ -455,6 +458,13 @@ const TemplateURL* TemplateURLService::GetTemplateURLForHost(
: nullptr;
}
+size_t TemplateURLService::GetTemplateURLCountForHostForLogging(
+ const std::string& host) const {
+ DCHECK(loaded_);
+ auto* host_urls = provider_map_->GetURLsForHost(host);
+ return host_urls ? host_urls->size() : 0;
+}
+
TemplateURL* TemplateURLService::Add(
std::unique_ptr<TemplateURL> template_url) {
DCHECK(template_url);
@@ -828,7 +838,7 @@ void TemplateURLService::RepairPrepopulatedSearchEngines() {
std::vector<std::unique_ptr<TemplateURLData>> prepopulated_urls =
TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs_, nullptr);
DCHECK(!prepopulated_urls.empty());
- ActionsFromPrepopulateData actions(CreateActionsFromCurrentPrepopulateData(
+ ActionsFromCurrentData actions(CreateActionsFromCurrentPrepopulateData(
&prepopulated_urls, template_urls_, default_search_provider_));
// Remove items.
@@ -887,6 +897,37 @@ void TemplateURLService::RepairPrepopulatedSearchEngines() {
}
}
+void TemplateURLService::RepairStarterPackEngines() {
+ DCHECK(loaded());
+
+ Scoper scoper(this);
+
+ std::vector<std::unique_ptr<TemplateURLData>> starter_pack_engines =
+ TemplateURLStarterPackData::GetStarterPackEngines();
+ DCHECK(!starter_pack_engines.empty());
+ ActionsFromCurrentData actions(CreateActionsFromCurrentStarterPackData(
+ &starter_pack_engines, template_urls_));
+
+ // Remove items.
+ for (auto i = actions.removed_engines.begin();
+ i < actions.removed_engines.end(); ++i) {
+ Remove(*i);
+ }
+
+ // Edit items.
+ for (auto i(actions.edited_engines.begin()); i < actions.edited_engines.end();
+ ++i) {
+ Update(i->first, TemplateURL(i->second));
+ }
+
+ // Add items.
+ for (std::vector<TemplateURLData>::const_iterator i =
+ actions.added_engines.begin();
+ i < actions.added_engines.end(); ++i) {
+ Add(std::make_unique<TemplateURL>(*i));
+ }
+}
+
void TemplateURLService::AddObserver(TemplateURLServiceObserver* observer) {
model_observers_.AddObserver(observer);
}
@@ -930,13 +971,15 @@ void TemplateURLService::OnWebDataServiceRequestDone(
std::unique_ptr<OwnedTemplateURLVector> template_urls =
std::make_unique<OwnedTemplateURLVector>();
int new_resource_keyword_version = 0;
+ int new_resource_starter_pack_version = 0;
{
GetSearchProvidersUsingKeywordResult(
*result, web_data_service_.get(), prefs_, template_urls.get(),
(default_search_provider_source_ == DefaultSearchManager::FROM_USER)
? initial_default_search_provider_.get()
: nullptr,
- search_terms_data(), &new_resource_keyword_version, &pre_sync_deletes_);
+ search_terms_data(), &new_resource_keyword_version,
+ &new_resource_starter_pack_version, &pre_sync_deletes_);
}
Scoper scoper(this);
@@ -957,6 +1000,10 @@ void TemplateURLService::OnWebDataServiceRequestDone(
if (new_resource_keyword_version)
web_data_service_->SetBuiltinKeywordVersion(new_resource_keyword_version);
+
+ if (new_resource_starter_pack_version)
+ web_data_service_->SetStarterPackKeywordVersion(
+ new_resource_starter_pack_version);
}
if (default_search_provider_) {
@@ -1400,6 +1447,7 @@ syncer::SyncData TemplateURLService::CreateSyncDataFromTemplateURL(
for (size_t i = 0; i < turl.alternate_urls().size(); ++i)
se_specifics->add_alternate_urls(turl.alternate_urls()[i]);
se_specifics->set_is_active(ActiveStatusToSync(turl.is_active()));
+ se_specifics->set_starter_pack_id(turl.starter_pack_id());
return syncer::SyncData::CreateLocalData(se_specifics->sync_guid(),
se_specifics->keyword(),
@@ -1472,6 +1520,7 @@ TemplateURLService::CreateTemplateURLFromTemplateURLAndSyncData(
for (int i = 0; i < specifics.alternate_urls_size(); ++i)
data.alternate_urls.push_back(specifics.alternate_urls(i));
data.is_active = ActiveStatusFromSync(specifics.is_active());
+ data.starter_pack_id = specifics.starter_pack_id();
std::unique_ptr<TemplateURL> turl(new TemplateURL(data));
// If this TemplateURL matches a built-in prepopulated template URL, it's
@@ -1716,7 +1765,7 @@ void TemplateURLService::UpdateTemplateURLIfPrepopulated(
for (const auto& url : prepopulated_urls) {
if (url->prepopulate_id == prepopulate_id) {
- MergeIntoPrepopulatedEngineData(template_url, url.get());
+ MergeIntoEngineData(template_url, url.get());
template_url->CopyFrom(TemplateURL(*url));
}
}
@@ -2092,16 +2141,19 @@ void TemplateURLService::MergeInSyncTemplateURL(
local_data->erase(guid);
}
- // Try to take over a local prepopulated entry, assuming we haven't already
- // run into a keyword conflict.
- if (local_duplicates.empty() && sync_turl->prepopulate_id() != 0) {
+ // Try to take over a local built-in (prepopulated or starter pack) entry,
+ // assuming we haven't already run into a keyword conflict.
+ if (local_duplicates.empty() &&
+ (sync_turl->prepopulate_id() != 0 || sync_turl->starter_pack_id() != 0)) {
// Check for a turl with a conflicting prepopulate_id. This detects the case
// where the user changes a prepopulated engine's keyword on one client,
// then begins syncing on another client. We want to reflect this keyword
// change to that prepopulated URL on other clients instead of assuming that
// the modified TemplateURL is a new entity.
- TemplateURL* conflicting_prepopulated_turl =
- FindPrepopulatedTemplateURL(sync_turl->prepopulate_id());
+ TemplateURL* conflicting_built_in_turl =
+ (sync_turl->prepopulate_id() != 0)
+ ? FindPrepopulatedTemplateURL(sync_turl->prepopulate_id())
+ : FindStarterPackTemplateURL(sync_turl->starter_pack_id());
// If we found a conflict, and the sync entity is better, apply the remote
// changes locally. We consider |sync_turl| better if it's been modified
@@ -2113,12 +2165,12 @@ void TemplateURLService::MergeInSyncTemplateURL(
// be applied to other clients.
// If we can't safely replace the local entry with the synced one, or merge
// the relevant changes in, we give up and leave both intact.
- if (conflicting_prepopulated_turl &&
- !IsFromSync(conflicting_prepopulated_turl, sync_data) &&
+ if (conflicting_built_in_turl &&
+ !IsFromSync(conflicting_built_in_turl, sync_data) &&
sync_turl->IsBetterThanEngineWithConflictingKeyword(
- conflicting_prepopulated_turl)) {
- std::string guid = conflicting_prepopulated_turl->sync_guid();
- if (conflicting_prepopulated_turl == default_search_provider_) {
+ conflicting_built_in_turl)) {
+ std::string guid = conflicting_built_in_turl->sync_guid();
+ if (conflicting_built_in_turl == default_search_provider_) {
bool pref_matched =
prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID) ==
default_search_provider_->sync_guid();
@@ -2136,7 +2188,7 @@ void TemplateURLService::MergeInSyncTemplateURL(
should_add_sync_turl = false;
} else {
- Remove(conflicting_prepopulated_turl);
+ Remove(conflicting_built_in_turl);
}
// Remove the local data so it isn't written to sync.
local_data->erase(guid);
@@ -2242,6 +2294,16 @@ TemplateURL* TemplateURLService::FindPrepopulatedTemplateURL(
return nullptr;
}
+TemplateURL* TemplateURLService::FindStarterPackTemplateURL(
+ int starter_pack_id) {
+ DCHECK(starter_pack_id);
+ for (const auto& turl : template_urls_) {
+ if (turl->starter_pack_id() == starter_pack_id)
+ return turl.get();
+ }
+ return nullptr;
+}
+
TemplateURL* TemplateURLService::FindTemplateURLForExtension(
const std::string& extension_id,
TemplateURL::Type type) {
diff --git a/chromium/components/search_engines/template_url_service.h b/chromium/components/search_engines/template_url_service.h
index 56399fb9d7c..7f190a55447 100644
--- a/chromium/components/search_engines/template_url_service.h
+++ b/chromium/components/search_engines/template_url_service.h
@@ -171,10 +171,9 @@ class TemplateURLService : public WebDataServiceConsumer,
bool supports_replacement_only,
TURLsAndMeaningfulLengths* matches);
- // Looks up |keyword| and returns the element it maps to. Returns NULL if
- // the keyword was not found.
- // The caller should not try to delete the returned pointer; the data store
- // retains ownership of it.
+ // Looks up |keyword| and returns the best TemplateURL for it. Returns
+ // nullptr if the keyword was not found. The caller should not try to delete
+ // the returned pointer; the data store retains ownership of it.
TemplateURL* GetTemplateURLForKeyword(const std::u16string& keyword);
const TemplateURL* GetTemplateURLForKeyword(
const std::u16string& keyword) const;
@@ -185,11 +184,16 @@ class TemplateURLService : public WebDataServiceConsumer,
TemplateURL* GetTemplateURLForGUID(const std::string& sync_guid);
const TemplateURL* GetTemplateURLForGUID(const std::string& sync_guid) const;
- // Returns the first TemplateURL found with a URL using the specified |host|,
- // or NULL if there are no such TemplateURLs
+ // Returns the best TemplateURL found with a URL using the specified |host|,
+ // or nullptr if there are no such TemplateURLs.
TemplateURL* GetTemplateURLForHost(const std::string& host);
const TemplateURL* GetTemplateURLForHost(const std::string& host) const;
+ // Returns the number of TemplateURLs that match `host`. Used for logging.
+ // Caller must ensure TemplateURLService is loaded before calling this.
+ // TODO(crbug.com/1322216): Delete after bug is fixed.
+ size_t GetTemplateURLCountForHostForLogging(const std::string& host) const;
+
// Adds a new TemplateURL to this model.
//
// This function guarantees that on return the model will not have two non-
@@ -338,6 +342,15 @@ class TemplateURLService : public WebDataServiceConsumer,
// prepopulate data.
void RepairPrepopulatedSearchEngines();
+ // Performs the same actions that happen when the starter pack data version is
+ // revved: all existing starter pack entries are checked against the current
+ // starter pack data, any now-extraneous safe_for_autoreplace() entries are
+ // removed, any existing engines are reset to the provided data (except for
+ // user-edited names or keywords), and any new starter pack engines are
+ // added. Unlike `RepairPrepopulatedSearchEngines()`, this does not modify
+ // the default search engine entry.
+ void RepairStarterPackEngines();
+
// Observers used to listen for changes to the model.
// TemplateURLService does NOT delete the observers when deleted.
void AddObserver(TemplateURLServiceObserver* observer);
@@ -487,6 +500,7 @@ class TemplateURLService : public WebDataServiceConsumer,
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, LastVisitedTimeUpdate);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest,
RepairPrepopulatedSearchEngines);
+ FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, RepairStarterPackEngines);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceSyncTest, PreSyncDeletes);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceSyncTest, MergeInSyncTemplateURL);
FRIEND_TEST_ALL_PREFIXES(LocationBarModelTest, GoogleBaseURL);
@@ -684,6 +698,9 @@ class TemplateURLService : public WebDataServiceConsumer,
// Returns the TemplateURL corresponding to |prepopulated_id|, if any.
TemplateURL* FindPrepopulatedTemplateURL(int prepopulated_id);
+ // Returns the TemplateURL corresponding to |starter_pack_id|, if any.
+ TemplateURL* FindStarterPackTemplateURL(int starter_pack_id);
+
// Returns the TemplateURL associated with |extension_id|, if any.
TemplateURL* FindTemplateURLForExtension(const std::string& extension_id,
TemplateURL::Type type);
diff --git a/chromium/components/search_engines/template_url_service_util_unittest.cc b/chromium/components/search_engines/template_url_service_util_unittest.cc
index 445e69a21c0..deab1af8af8 100644
--- a/chromium/components/search_engines/template_url_service_util_unittest.cc
+++ b/chromium/components/search_engines/template_url_service_util_unittest.cc
@@ -28,7 +28,8 @@ std::unique_ptr<TemplateURLData> CreatePrepopulateTemplateURLData(
"" /* image_url_post_params */, "" /* side_search_param */,
"" /* favicon_url */, "UTF-8",
base::ListValue() /* alternate_urls_list */,
- false /* preconnect_to_search_url */, prepopulate_id);
+ false /* preconnect_to_search_url */,
+ false /* prefetch_likely_navigations */, prepopulate_id);
}
// Creates a TemplateURL with default values except for the prepopulate ID,
diff --git a/chromium/components/search_engines/template_url_starter_pack_data.cc b/chromium/components/search_engines/template_url_starter_pack_data.cc
index 6f3088f7f62..9f08cecc19a 100644
--- a/chromium/components/search_engines/template_url_starter_pack_data.cc
+++ b/chromium/components/search_engines/template_url_starter_pack_data.cc
@@ -3,6 +3,9 @@
// found in the LICENSE file.
#include "components/search_engines/template_url_starter_pack_data.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/search_engines/search_engine_type.h"
#include "components/search_engines/template_url_data.h"
#include "components/search_engines/template_url_data_util.h"
#include "components/strings/grit/components_strings.h"
@@ -10,37 +13,45 @@
namespace TemplateURLStarterPackData {
-const int kMaxStarterPackEngineID = 3;
-const int kCurrentDataVersion = 1;
+const int kCurrentDataVersion = 2;
const StarterPackEngine bookmarks = {
- IDS_SEARCH_ENGINES_STARTER_PACK_BOOKMARKS_NAME,
- IDS_SEARCH_ENGINES_STARTER_PACK_BOOKMARKS_KEYWORD,
- nullptr,
- "chrome://bookmarks/?q=%s",
- 1,
+ .name_message_id = IDS_SEARCH_ENGINES_STARTER_PACK_BOOKMARKS_NAME,
+ .keyword_message_id = IDS_SEARCH_ENGINES_STARTER_PACK_BOOKMARKS_KEYWORD,
+ .favicon_url = nullptr,
+ .search_url = "chrome://bookmarks/?q={searchTerms}",
+ .destination_url = "chrome://bookmarks",
+ .id = StarterPackID::kBookmarks,
+ .type = SEARCH_ENGINE_STARTER_PACK_BOOKMARKS,
};
const StarterPackEngine history = {
- IDS_SEARCH_ENGINES_STARTER_PACK_HISTORY_NAME,
- IDS_SEARCH_ENGINES_STARTER_PACK_HISTORY_KEYWORD,
- nullptr,
- "chrome://history/?q=%s",
- 2,
+ .name_message_id = IDS_SEARCH_ENGINES_STARTER_PACK_HISTORY_NAME,
+ .keyword_message_id = IDS_SEARCH_ENGINES_STARTER_PACK_HISTORY_KEYWORD,
+ .favicon_url = nullptr,
+ .search_url = "chrome://history/?q={searchTerms}",
+ .destination_url = "chrome://history",
+ .id = StarterPackID::kHistory,
+ .type = SEARCH_ENGINE_STARTER_PACK_HISTORY,
};
-const StarterPackEngine settings = {
- IDS_SEARCH_ENGINES_STARTER_PACK_SETTINGS_NAME,
- IDS_SEARCH_ENGINES_STARTER_PACK_SETTINGS_KEYWORD,
- nullptr,
- "chrome://settings/?q=%s",
- 3,
+const StarterPackEngine tabs = {
+ .name_message_id = IDS_SEARCH_ENGINES_STARTER_PACK_TABS_NAME,
+ .keyword_message_id = IDS_SEARCH_ENGINES_STARTER_PACK_TABS_KEYWORD,
+ .favicon_url = nullptr,
+ // This search_url and destination_url are placeholder URLs to make
+ // templateURL happy. chrome://tabs does not currently exist and the tab
+ // search engine will only provide suggestions from the OpenTabProvider.
+ .search_url = "chrome://tabs/?q={searchTerms}",
+ .destination_url = "chrome://tabs",
+ .id = StarterPackID::kTabs,
+ .type = SEARCH_ENGINE_STARTER_PACK_TABS,
};
const StarterPackEngine* engines[] = {
&bookmarks,
&history,
- &settings,
+ &tabs,
};
int GetDataVersion() {
@@ -56,4 +67,14 @@ std::vector<std::unique_ptr<TemplateURLData>> GetStarterPackEngines() {
return t_urls;
}
+std::u16string GetDestinationUrlForStarterPackID(int id) {
+ for (auto* engine : engines) {
+ if (engine->id == id) {
+ return base::UTF8ToUTF16(engine->destination_url);
+ }
+ }
+
+ return u"";
+}
+
} // namespace TemplateURLStarterPackData
diff --git a/chromium/components/search_engines/template_url_starter_pack_data.h b/chromium/components/search_engines/template_url_starter_pack_data.h
index 133dc13658a..16ca6a62a08 100644
--- a/chromium/components/search_engines/template_url_starter_pack_data.h
+++ b/chromium/components/search_engines/template_url_starter_pack_data.h
@@ -6,8 +6,11 @@
#define COMPONENTS_SEARCH_ENGINES_TEMPLATE_URL_STARTER_PACK_DATA_H_
#include <memory>
+#include <string>
#include <vector>
+#include "components/search_engines/search_engine_type.h"
+
struct TemplateURLData;
// The Starter Pack is a set of built-in search engines that allow the user to
@@ -18,17 +21,31 @@ struct TemplateURLData;
namespace TemplateURLStarterPackData {
+typedef enum {
+ kBookmarks = 1,
+ kHistory = 2,
+ kTabs = 3,
+
+ kMaxStarterPackID
+} StarterPackID;
+
struct StarterPackEngine {
int name_message_id;
int keyword_message_id;
const char* const favicon_url;
const char* const search_url;
- const int id;
+ const char* const destination_url;
+ const StarterPackID id;
+ const SearchEngineType type;
};
-extern const int kMaxStarterPackEngineID;
extern const int kCurrentDataVersion;
+/* Exposed for testing purposes */
+extern const StarterPackEngine bookmarks;
+extern const StarterPackEngine history;
+extern const StarterPackEngine tabs;
+
// Returns the current version of the starterpack data, so callers can know when
// they need to re-merge.
int GetDataVersion();
@@ -36,6 +53,10 @@ int GetDataVersion();
// Returns a vector of all starter pack engines, in TemplateURLData format.
std::vector<std::unique_ptr<TemplateURLData>> GetStarterPackEngines();
+// Returns the destination url for the starter pack engine associated with a
+// given starter pack id.
+std::u16string GetDestinationUrlForStarterPackID(int id);
+
} // namespace TemplateURLStarterPackData
#endif // COMPONENTS_SEARCH_ENGINES_TEMPLATE_URL_STARTER_PACK_DATA_H_
diff --git a/chromium/components/search_engines/template_url_unittest.cc b/chromium/components/search_engines/template_url_unittest.cc
index 071d17613e6..e8180efdde9 100644
--- a/chromium/components/search_engines/template_url_unittest.cc
+++ b/chromium/components/search_engines/template_url_unittest.cc
@@ -687,7 +687,7 @@ TEST_F(TemplateURLTest, ReplaceSearchboxStats) {
// HTTPS and non-empty gs_lcrp: replace gs_lcrp.
{u"foo", non_empty_searchbox_stats, "https://foo/",
"{google:baseURL}?q={searchTerms}&{google:searchboxStats}",
- "https://foo/?q=foo&gs_lcrp=EgZjaHJvbWWwAgE=&"},
+ "https://foo/?q=foo&gs_lcrp=EgZjaHJvbWWwAgE&"},
// HTTPS and non-empty gs_lcrp but no google:searchboxStats: no gs_lcrp.
{u"foo", non_empty_searchbox_stats, "https://foo/",
"{google:baseURL}?q={searchTerms}", "https://foo/?q=foo"},
@@ -702,7 +702,7 @@ TEST_F(TemplateURLTest, ReplaceSearchboxStats) {
// gs_lcrp.
{u"foo", non_empty_searchbox_stats, "https://foo/",
"https://foo/?{searchTerms}&{google:searchboxStats}",
- "https://foo/?foo&gs_lcrp=EgZjaHJvbWWwAgE=&"},
+ "https://foo/?foo&gs_lcrp=EgZjaHJvbWWwAgE&"},
// Non-Google search provider, HTTPS and non-empty gs_lcrp but no
// google:searchboxStats: no gs_lcrp.
{u"foo", non_empty_searchbox_stats, "https://foo/",
@@ -725,7 +725,7 @@ TEST_F(TemplateURLTest, ReplaceSearchboxStats) {
}
// Expect correct histograms to have been logged.
histogram_tester.ExpectTotalCount("Omnibox.SearchboxStats.Length", 2);
- histogram_tester.ExpectBucketCount("Omnibox.SearchboxStats.Length", 16, 2);
+ histogram_tester.ExpectBucketCount("Omnibox.SearchboxStats.Length", 15, 2);
}
// Tests replacing searchbox stats (gs_lcrp) and assisted query stats (AQS).
@@ -751,13 +751,13 @@ TEST_F(TemplateURLTest, ReplaceSearchboxStatsAndAssistedQueryStats) {
{u"foo", "chrome.0.0l6", non_empty_searchbox_stats, "https://foo/",
"{google:baseURL}?q={searchTerms}&{google:searchboxStats}{google:"
"assistedQueryStats}",
- "https://foo/?q=foo&gs_lcrp=EgZjaHJvbWWwAgE=&aqs=chrome.0.0l6&"},
+ "https://foo/?q=foo&gs_lcrp=EgZjaHJvbWWwAgE&aqs=chrome.0.0l6&"},
// Non-Google search provider, HTTPS and non-empty gs_lcrp and AQS:
// replace both.
{u"foo", "chrome.0.0l6", non_empty_searchbox_stats, "https://foo/",
"https://foo/"
"?{searchTerms}&{google:searchboxStats}{google:assistedQueryStats}",
- "https://foo/?foo&gs_lcrp=EgZjaHJvbWWwAgE=&aqs=chrome.0.0l6&"},
+ "https://foo/?foo&gs_lcrp=EgZjaHJvbWWwAgE&aqs=chrome.0.0l6&"},
};
TemplateURLData data;
data.input_encodings.push_back("UTF-8");
@@ -780,7 +780,7 @@ TEST_F(TemplateURLTest, ReplaceSearchboxStatsAndAssistedQueryStats) {
histogram_tester.ExpectBucketCount("Omnibox.AssistedQueryStats.Length", 12,
2);
histogram_tester.ExpectTotalCount("Omnibox.SearchboxStats.Length", 2);
- histogram_tester.ExpectBucketCount("Omnibox.SearchboxStats.Length", 16, 2);
+ histogram_tester.ExpectBucketCount("Omnibox.SearchboxStats.Length", 15, 2);
}
// Tests replacing cursor position.
diff --git a/chromium/components/search_engines/util.cc b/chromium/components/search_engines/util.cc
index 8269739eb9e..4731ee872d6 100644
--- a/chromium/components/search_engines/util.cc
+++ b/chromium/components/search_engines/util.cc
@@ -21,6 +21,7 @@
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_prepopulate_data.h"
#include "components/search_engines/template_url_service.h"
+#include "components/search_engines/template_url_starter_pack_data.h"
std::u16string GetDefaultSearchEngineName(TemplateURLService* service) {
DCHECK(service);
@@ -180,41 +181,41 @@ TemplateURL* FindURLByPrepopulateID(
return nullptr;
}
-void MergeIntoPrepopulatedEngineData(const TemplateURL* original_turl,
- TemplateURLData* prepopulated_url) {
+void MergeIntoEngineData(const TemplateURL* original_turl,
+ TemplateURLData* url_to_update) {
DCHECK(original_turl->prepopulate_id() == 0 ||
- original_turl->prepopulate_id() == prepopulated_url->prepopulate_id);
+ original_turl->prepopulate_id() == url_to_update->prepopulate_id);
+ DCHECK(original_turl->starter_pack_id() == 0 ||
+ original_turl->starter_pack_id() == url_to_update->starter_pack_id);
// When the user modified search engine's properties or search engine is
// imported from Play API data we need to preserve certain search engine
// properties from overriding with prepopulated data.
if (!original_turl->safe_for_autoreplace() ||
original_turl->created_from_play_api()) {
- prepopulated_url->safe_for_autoreplace =
- original_turl->safe_for_autoreplace();
- prepopulated_url->SetShortName(original_turl->short_name());
- prepopulated_url->SetKeyword(original_turl->keyword());
+ url_to_update->safe_for_autoreplace = original_turl->safe_for_autoreplace();
+ url_to_update->SetShortName(original_turl->short_name());
+ url_to_update->SetKeyword(original_turl->keyword());
if (original_turl->created_from_play_api()) {
// TODO(crbug/1002271): Search url from Play API might contain attribution
// info and therefore should be preserved through prepopulated data
// update. In the future we might decide to take different approach to
// pass attribution info to search providers.
- prepopulated_url->SetURL(original_turl->url());
+ url_to_update->SetURL(original_turl->url());
}
}
- prepopulated_url->id = original_turl->id();
- prepopulated_url->sync_guid = original_turl->sync_guid();
- prepopulated_url->date_created = original_turl->date_created();
- prepopulated_url->last_modified = original_turl->last_modified();
- prepopulated_url->created_from_play_api =
- original_turl->created_from_play_api();
+ url_to_update->id = original_turl->id();
+ url_to_update->sync_guid = original_turl->sync_guid();
+ url_to_update->date_created = original_turl->date_created();
+ url_to_update->last_modified = original_turl->last_modified();
+ url_to_update->created_from_play_api = original_turl->created_from_play_api();
}
-ActionsFromPrepopulateData::ActionsFromPrepopulateData() {}
+ActionsFromCurrentData::ActionsFromCurrentData() = default;
-ActionsFromPrepopulateData::ActionsFromPrepopulateData(
- const ActionsFromPrepopulateData& other) = default;
+ActionsFromCurrentData::ActionsFromCurrentData(
+ const ActionsFromCurrentData& other) = default;
-ActionsFromPrepopulateData::~ActionsFromPrepopulateData() {}
+ActionsFromCurrentData::~ActionsFromCurrentData() = default;
void MergeEnginesFromPrepopulateData(
KeywordWebDataService* service,
@@ -225,41 +226,14 @@ void MergeEnginesFromPrepopulateData(
DCHECK(prepopulated_urls);
DCHECK(template_urls);
- ActionsFromPrepopulateData actions(CreateActionsFromCurrentPrepopulateData(
+ ActionsFromCurrentData actions(CreateActionsFromCurrentPrepopulateData(
prepopulated_urls, *template_urls, default_search_provider));
- // Remove items.
- for (const auto* removed_engine : actions.removed_engines) {
- auto j = FindTemplateURL(template_urls, removed_engine);
- DCHECK(j != template_urls->end());
- DCHECK(!default_search_provider ||
- (*j)->prepopulate_id() != default_search_provider->prepopulate_id());
- std::unique_ptr<TemplateURL> template_url = std::move(*j);
- template_urls->erase(j);
- if (service) {
- service->RemoveKeyword(template_url->id());
- if (removed_keyword_guids)
- removed_keyword_guids->insert(template_url->sync_guid());
- }
- }
-
- // Edit items.
- for (const auto& edited_engine : actions.edited_engines) {
- const TemplateURLData& data = edited_engine.second;
- if (service)
- service->UpdateKeyword(data);
-
- // Replace the entry in |template_urls| with the updated one.
- auto j = FindTemplateURL(template_urls, edited_engine.first);
- *j = std::make_unique<TemplateURL>(data);
- }
-
- // Add items.
- for (const auto& added_engine : actions.added_engines)
- template_urls->push_back(std::make_unique<TemplateURL>(added_engine));
+ ApplyActionsFromCurrentData(actions, service, template_urls,
+ default_search_provider, removed_keyword_guids);
}
-ActionsFromPrepopulateData CreateActionsFromCurrentPrepopulateData(
+ActionsFromCurrentData CreateActionsFromCurrentPrepopulateData(
std::vector<std::unique_ptr<TemplateURLData>>* prepopulated_urls,
const TemplateURLService::OwnedTemplateURLVector& existing_urls,
const TemplateURL* default_search_provider) {
@@ -281,7 +255,7 @@ ActionsFromPrepopulateData CreateActionsFromCurrentPrepopulateData(
// a matching prepopulated URL. If so, update the passed-in URL to match the
// current data. (If the passed-in URL was user-edited, we persist the user's
// name and keyword.) If not, add the prepopulated URL.
- ActionsFromPrepopulateData actions;
+ ActionsFromCurrentData actions;
for (auto& prepopulated_url : *prepopulated_urls) {
const int prepopulated_id = prepopulated_url->prepopulate_id;
DCHECK_NE(0, prepopulated_id);
@@ -299,7 +273,7 @@ ActionsFromPrepopulateData CreateActionsFromCurrentPrepopulateData(
if (existing_url != nullptr) {
// Update the data store with the new prepopulated data. Preserve user
// edits to the name and keyword.
- MergeIntoPrepopulatedEngineData(existing_url, prepopulated_url.get());
+ MergeIntoEngineData(existing_url, prepopulated_url.get());
// Update last_modified to ensure that if this entry is later merged with
// entries from Sync, the conflict resolution logic knows that this was
// updated and propagates the new values to the server.
@@ -316,8 +290,8 @@ ActionsFromPrepopulateData CreateActionsFromCurrentPrepopulateData(
// We assume that this entry is equivalent to the DSE if its prepopulate ID
// and keyword both match. If the prepopulate ID _does_ match all properties
// will be replaced with those from |default_search_provider| anyway.
- for (auto i = id_to_turl.begin(); i != id_to_turl.end(); ++i) {
- TemplateURL* template_url = i->second;
+ for (auto& i : id_to_turl) {
+ TemplateURL* template_url = i.second;
if ((template_url->safe_for_autoreplace()) &&
(!default_search_provider ||
(template_url->prepopulate_id() !=
@@ -338,6 +312,117 @@ ActionsFromPrepopulateData CreateActionsFromCurrentPrepopulateData(
return actions;
}
+void MergeEnginesFromStarterPackData(
+ KeywordWebDataService* service,
+ TemplateURLService::OwnedTemplateURLVector* template_urls,
+ TemplateURL* default_search_provider,
+ std::set<std::string>* removed_keyword_guids) {
+ DCHECK(template_urls);
+
+ std::vector<std::unique_ptr<TemplateURLData>> starter_pack_urls =
+ TemplateURLStarterPackData::GetStarterPackEngines();
+
+ ActionsFromCurrentData actions(CreateActionsFromCurrentStarterPackData(
+ &starter_pack_urls, *template_urls));
+
+ ApplyActionsFromCurrentData(actions, service, template_urls,
+ default_search_provider, removed_keyword_guids);
+}
+
+ActionsFromCurrentData CreateActionsFromCurrentStarterPackData(
+ std::vector<std::unique_ptr<TemplateURLData>>* starter_pack_urls,
+ const TemplateURLService::OwnedTemplateURLVector& existing_urls) {
+ // Create a map to hold all provided |template_urls| that originally came from
+ // starter_pack data (i.e. have a non-zero starter_pack_id()).
+ std::map<int, TemplateURL*> id_to_turl;
+ for (auto& turl : existing_urls) {
+ int starter_pack_id = turl->starter_pack_id();
+ if (starter_pack_id > 0)
+ id_to_turl[starter_pack_id] = turl.get();
+ }
+
+ // For each current starter pack URL, check whether |template_urls| contained
+ // a matching starter pack URL. If so, update the passed-in URL to match the
+ // current data. (If the passed-in URL was user-edited, we persist the user's
+ // name and keyword.) If not, add the prepopulated URL.
+ ActionsFromCurrentData actions;
+ for (auto& url : *starter_pack_urls) {
+ const int starter_pack_id = url->starter_pack_id;
+ DCHECK_NE(0, starter_pack_id);
+
+ auto existing_url_iter = id_to_turl.find(starter_pack_id);
+ TemplateURL* existing_url = nullptr;
+ if (existing_url_iter != id_to_turl.end()) {
+ existing_url = existing_url_iter->second;
+ id_to_turl.erase(existing_url_iter);
+ }
+
+ if (existing_url != nullptr) {
+ // Update the data store with the new prepopulated data. Preserve user
+ // edits to the name and keyword.
+ MergeIntoEngineData(existing_url, url.get());
+ // Update last_modified to ensure that if this entry is later merged with
+ // entries from Sync, the conflict resolution logic knows that this was
+ // updated and propagates the new values to the server.
+ url->last_modified = base::Time::Now();
+ actions.edited_engines.push_back({existing_url, *url});
+ } else {
+ actions.added_engines.push_back(*url);
+ }
+ }
+
+ // The block above removed all the URLs from the |id_to_turl| map that were
+ // found in the prepopulate data. Any remaining URLs that haven't been
+ // user-edited can be removed from the data store.
+ for (auto& i : id_to_turl) {
+ TemplateURL* template_url = i.second;
+ if (template_url->safe_for_autoreplace()) {
+ actions.removed_engines.push_back(template_url);
+ }
+ }
+
+ return actions;
+}
+
+void ApplyActionsFromCurrentData(
+ ActionsFromCurrentData actions,
+ KeywordWebDataService* service,
+ TemplateURLService::OwnedTemplateURLVector* template_urls,
+ TemplateURL* default_search_provider,
+ std::set<std::string>* removed_keyword_guids) {
+ DCHECK(template_urls);
+
+ // Remove items.
+ for (const auto* removed_engine : actions.removed_engines) {
+ auto j = FindTemplateURL(template_urls, removed_engine);
+ DCHECK(j != template_urls->end());
+ DCHECK(!default_search_provider ||
+ (*j)->prepopulate_id() != default_search_provider->prepopulate_id());
+ std::unique_ptr<TemplateURL> template_url = std::move(*j);
+ template_urls->erase(j);
+ if (service) {
+ service->RemoveKeyword(template_url->id());
+ if (removed_keyword_guids)
+ removed_keyword_guids->insert(template_url->sync_guid());
+ }
+ }
+
+ // Edit items.
+ for (const auto& edited_engine : actions.edited_engines) {
+ const TemplateURLData& data = edited_engine.second;
+ if (service)
+ service->UpdateKeyword(data);
+
+ // Replace the entry in |template_urls| with the updated one.
+ auto j = FindTemplateURL(template_urls, edited_engine.first);
+ *j = std::make_unique<TemplateURL>(data);
+ }
+
+ // Add items.
+ for (const auto& added_engine : actions.added_engines)
+ template_urls->push_back(std::make_unique<TemplateURL>(added_engine));
+}
+
void GetSearchProvidersUsingKeywordResult(
const WDTypedResult& result,
KeywordWebDataService* service,
@@ -346,6 +431,7 @@ void GetSearchProvidersUsingKeywordResult(
TemplateURL* default_search_provider,
const SearchTermsData& search_terms_data,
int* new_resource_keyword_version,
+ int* new_resource_starter_pack_version,
std::set<std::string>* removed_keyword_guids) {
DCHECK(template_urls);
DCHECK(template_urls->empty());
@@ -371,11 +457,11 @@ void GetSearchProvidersUsingKeywordResult(
}
*new_resource_keyword_version = keyword_result.builtin_keyword_version;
- GetSearchProvidersUsingLoadedEngines(service, prefs, template_urls,
- default_search_provider,
- search_terms_data,
- new_resource_keyword_version,
- removed_keyword_guids);
+ *new_resource_starter_pack_version = keyword_result.starter_pack_version;
+ GetSearchProvidersUsingLoadedEngines(
+ service, prefs, template_urls, default_search_provider, search_terms_data,
+ new_resource_keyword_version, new_resource_starter_pack_version,
+ removed_keyword_guids);
}
void GetSearchProvidersUsingLoadedEngines(
@@ -385,6 +471,7 @@ void GetSearchProvidersUsingLoadedEngines(
TemplateURL* default_search_provider,
const SearchTermsData& search_terms_data,
int* resource_keyword_version,
+ int* resource_starter_pack_version,
std::set<std::string>* removed_keyword_guids) {
DCHECK(template_urls);
DCHECK(resource_keyword_version);
@@ -404,6 +491,16 @@ void GetSearchProvidersUsingLoadedEngines(
} else {
*resource_keyword_version = 0;
}
+
+ const int starter_pack_data_version =
+ TemplateURLStarterPackData::GetDataVersion();
+ if (*resource_starter_pack_version < starter_pack_data_version) {
+ MergeEnginesFromStarterPackData(
+ service, template_urls, default_search_provider, removed_keyword_guids);
+ *resource_starter_pack_version = starter_pack_data_version;
+ } else {
+ *resource_starter_pack_version = 0;
+ }
}
bool DeDupeEncodings(std::vector<std::string>* encodings) {
diff --git a/chromium/components/search_engines/util.h b/chromium/components/search_engines/util.h
index 64b0d152d68..5f7f0cbc4ca 100644
--- a/chromium/components/search_engines/util.h
+++ b/chromium/components/search_engines/util.h
@@ -33,34 +33,35 @@ TemplateURL* FindURLByPrepopulateID(
const TemplateURLService::TemplateURLVector& template_urls,
int prepopulate_id);
-// Modifies |prepopulated_url| so that it contains user-modified fields from
+// Modifies |url_to_update| so that it contains user-modified fields from
// |original_turl|. Both URLs must have the same prepopulate_id.
-void MergeIntoPrepopulatedEngineData(const TemplateURL* original_turl,
- TemplateURLData* prepopulated_url);
-
-// CreateActionsFromCurrentPrepopulateData() (see below) takes in the current
-// prepopulated URLs as well as the user's current URLs, and returns an instance
-// of the following struct representing the changes necessary to bring the
-// user's URLs in line with the prepopulated URLs.
+void MergeIntoEngineData(const TemplateURL* original_turl,
+ TemplateURLData* url_to_update);
+
+// CreateActionsFromCurrentPrepopulateData() and
+// CreateActionsFromStarterPackData() (see below) takes in the current built-in
+// (prepopulated or starter pack) URLs as well as the user's current URLs, and
+// returns an instance of the following struct representing the changes
+// necessary to bring the user's URLs in line with the built-in URLs.
//
// There are three types of changes:
-// (1) Previous prepopulated engines that no longer exist in the current set of
-// prepopulated engines and thus should be removed from the user's current
+// (1) Previous built-in engines that no longer exist in the current set of
+// built-in engines and thus should be removed from the user's current
// URLs.
-// (2) Previous prepopulated engines whose data has changed. The existing
+// (2) Previous built-in engines whose data has changed. The existing
// entries for these engines should be updated to reflect the new data,
// except for any user-set names and keywords, which can be preserved.
-// (3) New prepopulated engines not in the user's engine list, which should be
+// (3) New built-in engines not in the user's engine list, which should be
// added.
// The pair of current search engine and its new value.
typedef std::pair<TemplateURL*, TemplateURLData> EditedSearchEngine;
typedef std::vector<EditedSearchEngine> EditedEngines;
-struct ActionsFromPrepopulateData {
- ActionsFromPrepopulateData();
- ActionsFromPrepopulateData(const ActionsFromPrepopulateData& other);
- ~ActionsFromPrepopulateData();
+struct ActionsFromCurrentData {
+ ActionsFromCurrentData();
+ ActionsFromCurrentData(const ActionsFromCurrentData& other);
+ ~ActionsFromCurrentData();
TemplateURLService::TemplateURLVector removed_engines;
EditedEngines edited_engines;
@@ -70,7 +71,7 @@ struct ActionsFromPrepopulateData {
// MergeEnginesFromPrepopulateData merges search engines from
// |prepopulated_urls| into |template_urls|. Calls
// CreateActionsFromCurrentPrepopulateData() to collect actions and then applies
-// them on |tempate_urls|. MergeEnginesFromPrepopulateData is invoked when the
+// them on |template_urls|. MergeEnginesFromPrepopulateData is invoked when the
// version of the prepopulate data changes. If |removed_keyword_guids| is not
// nullptr, the Sync GUID of each item removed from the DB will be added to it.
// Note that this function will take ownership of |prepopulated_urls| and will
@@ -89,11 +90,42 @@ void MergeEnginesFromPrepopulateData(
// placing the current default provider on the "to be removed" list.
//
// NOTE: Takes ownership of, and clears, |prepopulated_urls|.
-ActionsFromPrepopulateData CreateActionsFromCurrentPrepopulateData(
+ActionsFromCurrentData CreateActionsFromCurrentPrepopulateData(
std::vector<std::unique_ptr<TemplateURLData>>* prepopulated_urls,
const TemplateURLService::OwnedTemplateURLVector& existing_urls,
const TemplateURL* default_search_provider);
+// MergeEnginesFromStarterPackData merges search engines from the built-in
+// TemplateURLStarterPackData class into |template_urls|. Calls
+// CreateActionsFromCurrentStarterPackData() to collect actions and then applies
+// them on |template_urls|. MergeEgninesFromStarterPackData is invoked when the
+// version of the starter pack data changes. If |removed_keyword_guids| is not
+// nullptr, the Sync GUID of each item removed from the DB will be added to it.
+void MergeEnginesFromStarterPackData(
+ KeywordWebDataService* service,
+ TemplateURLService::OwnedTemplateURLVector* template_urls,
+ TemplateURL* default_search_provider,
+ std::set<std::string>* removed_keyword_guids);
+
+// Given the user's current URLs and the current set of Starter Pack URLs,
+// produces the set of actions (see above) required to make the user's URLs
+// reflect the starter pack data.
+//
+// NOTE: Takes ownership of, and clears, |starter_pack_urls|.
+ActionsFromCurrentData CreateActionsFromCurrentStarterPackData(
+ std::vector<std::unique_ptr<TemplateURLData>>* starter_pack_urls,
+ const TemplateURLService::OwnedTemplateURLVector& existing_urls);
+
+// Takes in an ActionsFromCurrentData (see above) and applies the actions (add,
+// edit, or remove) to the user's current URLs. This is called by
+// MergeEnginesFromPrepopulateData() and MergeEnginesFromStarterPackData().
+void ApplyActionsFromCurrentData(
+ ActionsFromCurrentData actions,
+ KeywordWebDataService* service,
+ TemplateURLService::OwnedTemplateURLVector* template_urls,
+ TemplateURL* default_search_provider,
+ std::set<std::string>* removed_keyword_guids);
+
// Processes the results of KeywordWebDataService::GetKeywords, combining it
// with prepopulated search providers to result in:
// * a set of template_urls (search providers). The caller owns the
@@ -114,6 +146,7 @@ void GetSearchProvidersUsingKeywordResult(
TemplateURL* default_search_provider,
const SearchTermsData& search_terms_data,
int* new_resource_keyword_version,
+ int* new_resource_starter_pack_version,
std::set<std::string>* removed_keyword_guids);
// Like GetSearchProvidersUsingKeywordResult(), but allows the caller to pass in
@@ -130,6 +163,7 @@ void GetSearchProvidersUsingLoadedEngines(
TemplateURL* default_search_provider,
const SearchTermsData& search_terms_data,
int* resource_keyword_version,
+ int* resource_starter_pack_version,
std::set<std::string>* removed_keyword_guids);
// Due to a bug, the |input_encodings| field of TemplateURLData could have
diff --git a/chromium/components/search_provider_logos/logo_service_impl.cc b/chromium/components/search_provider_logos/logo_service_impl.cc
index a6bcb4b9b68..24a04e9d957 100644
--- a/chromium/components/search_provider_logos/logo_service_impl.cc
+++ b/chromium/components/search_provider_logos/logo_service_impl.cc
@@ -402,6 +402,7 @@ void LogoServiceImpl::OnCachedLogoRead(
cached_logo->encoded_image;
image_decoder_->DecodeImage(
encoded_image->data(), gfx::Size(), // No particular size desired.
+ /*data_decoder=*/nullptr,
ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
&LogoServiceImpl::OnLightCachedImageDecoded,
weak_ptr_factory_.GetWeakPtr(), std::move(cached_logo))));
@@ -441,6 +442,7 @@ void LogoServiceImpl::OnLightCachedImageDecoded(
image_decoder_->DecodeImage(
dark_encoded_image->data(), gfx::Size(), // No particular size desired.
+ /*data_decoder=*/nullptr,
ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
&LogoServiceImpl::OnCachedLogoAvailable,
weak_ptr_factory_.GetWeakPtr(), std::move(cached_logo), image)));
@@ -535,6 +537,7 @@ void LogoServiceImpl::OnFreshLogoParsed(bool* parsing_failed,
image_decoder_->DecodeImage(
encoded_image->data(), gfx::Size(), // No particular size desired.
+ /*data_decoder=*/nullptr,
ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
&LogoServiceImpl::OnLightFreshImageDecoded,
weak_ptr_factory_.GetWeakPtr(), std::move(logo),
@@ -562,6 +565,7 @@ void LogoServiceImpl::OnLightFreshImageDecoded(
image_decoder_->DecodeImage(
dark_encoded_image->data(), gfx::Size(), // No particular size desired.
+ /*data_decoder=*/nullptr,
ImageDecodedHandlerWithTimeout::Wrap(base::BindOnce(
&LogoServiceImpl::OnFreshLogoAvailable,
weak_ptr_factory_.GetWeakPtr(), std::move(logo), download_failed,
diff --git a/chromium/components/search_provider_logos/logo_service_impl_unittest.cc b/chromium/components/search_provider_logos/logo_service_impl_unittest.cc
index 3e079f6b023..cd41ecbf4e1 100644
--- a/chromium/components/search_provider_logos/logo_service_impl_unittest.cc
+++ b/chromium/components/search_provider_logos/logo_service_impl_unittest.cc
@@ -312,6 +312,7 @@ class FakeImageDecoder : public image_fetcher::ImageDecoder {
public:
void DecodeImage(const std::string& image_data,
const gfx::Size& desired_image_frame_size,
+ data_decoder::DataDecoder* data_decoder,
image_fetcher::ImageDecodedCallback callback) override {
gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(
reinterpret_cast<const uint8_t*>(image_data.data()), image_data.size());
diff --git a/chromium/components/security_interstitials/DEPS b/chromium/components/security_interstitials/DEPS
index af6cbb98c17..66cf58139c0 100644
--- a/chromium/components/security_interstitials/DEPS
+++ b/chromium/components/security_interstitials/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+components/content_settings/core/browser",
"+components/google/core",
"+components/history/core/browser",
"+components/metrics",
diff --git a/chromium/components/security_interstitials/content/android/BUILD.gn b/chromium/components/security_interstitials/content/android/BUILD.gn
index 99edd0d7952..08952036ddf 100644
--- a/chromium/components/security_interstitials/content/android/BUILD.gn
+++ b/chromium/components/security_interstitials/content/android/BUILD.gn
@@ -20,6 +20,8 @@ android_library("java") {
]
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
}
diff --git a/chromium/components/security_interstitials/content/captive_portal_blocking_page.cc b/chromium/components/security_interstitials/content/captive_portal_blocking_page.cc
index 4e737c574c4..6e968b8acdd 100644
--- a/chromium/components/security_interstitials/content/captive_portal_blocking_page.cc
+++ b/chromium/components/security_interstitials/content/captive_portal_blocking_page.cc
@@ -158,7 +158,7 @@ void CaptivePortalBlockingPage::PopulateInterstitialStrings(
} else {
paragraph = l10n_util::GetStringFUTF16(
IDS_CAPTIVE_PORTAL_PRIMARY_PARAGRAPH_NO_LOGIN_URL_WIFI_SSID,
- net::EscapeForHTML(base::UTF8ToUTF16(wifi_ssid)));
+ base::EscapeForHTML(base::UTF8ToUTF16(wifi_ssid)));
}
} else {
// Portal redirection was done with HTTP redirects, so show the login URL.
@@ -175,7 +175,7 @@ void CaptivePortalBlockingPage::PopulateInterstitialStrings(
} else {
paragraph = l10n_util::GetStringFUTF16(
IDS_CAPTIVE_PORTAL_PRIMARY_PARAGRAPH_WIFI_SSID,
- net::EscapeForHTML(base::UTF8ToUTF16(wifi_ssid)), login_host);
+ base::EscapeForHTML(base::UTF8ToUTF16(wifi_ssid)), login_host);
}
}
load_time_data->SetStringKey("primaryParagraph", paragraph);
diff --git a/chromium/components/security_interstitials/content/certificate_error_report_unittest.cc b/chromium/components/security_interstitials/content/certificate_error_report_unittest.cc
index 3da4dae3398..b54ad3d580e 100644
--- a/chromium/components/security_interstitials/content/certificate_error_report_unittest.cc
+++ b/chromium/components/security_interstitials/content/certificate_error_report_unittest.cc
@@ -235,7 +235,8 @@ TEST(ErrorReportTest, NetworkTimeQueryingFeatureInfo) {
std::unique_ptr<network_time::FieldTrialTest> field_trial_test(
new network_time::FieldTrialTest());
field_trial_test->SetFeatureParams(
- true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY);
+ true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY,
+ network_time::NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
scoped_refptr<network::TestSharedURLLoaderFactory> shared_url_loader_factory =
base::MakeRefCounted<network::TestSharedURLLoaderFactory>();
diff --git a/chromium/components/security_interstitials/content/insecure_form_navigation_throttle.cc b/chromium/components/security_interstitials/content/insecure_form_navigation_throttle.cc
index a1edb4dd859..190f5923627 100644
--- a/chromium/components/security_interstitials/content/insecure_form_navigation_throttle.cc
+++ b/chromium/components/security_interstitials/content/insecure_form_navigation_throttle.cc
@@ -65,8 +65,10 @@ InsecureFormNavigationThrottle::WillProcessResponse() {
// the IsProceeding flag.
InsecureFormTabStorage* tab_storage = InsecureFormTabStorage::FromWebContents(
navigation_handle()->GetWebContents());
- if (tab_storage)
+ if (tab_storage) {
tab_storage->SetIsProceeding(false);
+ tab_storage->SetInterstitialShown(false);
+ }
return content::NavigationThrottle::PROCEED;
}
@@ -90,9 +92,22 @@ content::NavigationThrottle::ThrottleCheckResult
InsecureFormNavigationThrottle::GetThrottleResultForMixedForm(
bool is_redirect) {
content::NavigationHandle* handle = navigation_handle();
- if (!handle->IsFormSubmission())
- return content::NavigationThrottle::PROCEED;
content::WebContents* contents = handle->GetWebContents();
+ InsecureFormTabStorage* tab_storage =
+ InsecureFormTabStorage::FromWebContents(contents);
+
+ // We only show insecure form interstitials for form submissions. However GET
+ // submissions are not marked as form submissions on reloads, so we check if
+ // this navigation is coming from another mixed form interstitial.
+ if (!handle->IsFormSubmission() &&
+ (!tab_storage || !tab_storage->InterstitialShown())) {
+ return content::NavigationThrottle::PROCEED;
+ }
+
+ // If user has just chosen to proceed on an interstitial, we don't show
+ // another one.
+ if (tab_storage && tab_storage->IsProceeding())
+ return content::NavigationThrottle::PROCEED;
// Do not set special error page HTML for insecure forms in subframes; those
// are already hard blocked.
@@ -107,13 +122,6 @@ InsecureFormNavigationThrottle::GetThrottleResultForMixedForm(
return content::NavigationThrottle::PROCEED;
}
- // If user has just chosen to proceed on an interstitial, we don't show
- // another one.
- InsecureFormTabStorage* tab_storage =
- InsecureFormTabStorage::GetOrCreate(contents);
- if (tab_storage->IsProceeding())
- return content::NavigationThrottle::PROCEED;
-
InterstitialTriggeredState log_state =
InterstitialTriggeredState::kMixedFormDirect;
bool should_proceed = false;
@@ -151,6 +159,9 @@ InsecureFormNavigationThrottle::GetThrottleResultForMixedForm(
std::string interstitial_html = blocking_page->GetHTMLContents();
SecurityInterstitialTabHelper::AssociateBlockingPage(
handle, std::move(blocking_page));
+ if (!tab_storage)
+ tab_storage = InsecureFormTabStorage::GetOrCreate(contents);
+ tab_storage->SetInterstitialShown(true);
return content::NavigationThrottle::ThrottleCheckResult(
CANCEL, net::ERR_BLOCKED_BY_CLIENT, interstitial_html);
}
diff --git a/chromium/components/security_interstitials/content/insecure_form_tab_storage.h b/chromium/components/security_interstitials/content/insecure_form_tab_storage.h
index 115d4d17699..82631ba9e61 100644
--- a/chromium/components/security_interstitials/content/insecure_form_tab_storage.h
+++ b/chromium/components/security_interstitials/content/insecure_form_tab_storage.h
@@ -13,8 +13,12 @@ class WebContents;
namespace security_interstitials {
-// A short-lived, per tab storage for mixed form interstitials, that stores a
-// flag while proceeding, so a new interstitial is not shown immediately.
+// A short-lived, per tab storage for mixed form interstitials, that:
+// -Stores a flag while proceeding, so a new interstitial is not shown
+// immediately.
+// -Stores a flag when an interstitial is currently being shown, to prevent
+// reloading the interstitial from causing it to be bypassed in certain cases
+// (e.g. for GET form submissions).
class InsecureFormTabStorage
: public content::WebContentsUserData<InsecureFormTabStorage> {
public:
@@ -31,6 +35,10 @@ class InsecureFormTabStorage
void SetIsProceeding(bool is_proceeding) { is_proceeding_ = is_proceeding; }
bool IsProceeding() const { return is_proceeding_; }
+ void SetInterstitialShown(bool interstitial_shown) {
+ interstitial_shown_ = interstitial_shown;
+ }
+ bool InterstitialShown() const { return interstitial_shown_; }
private:
explicit InsecureFormTabStorage(content::WebContents* contents);
@@ -39,6 +47,8 @@ class InsecureFormTabStorage
// Flag stores whether we are in the middle of a proceed action.
bool is_proceeding_ = false;
+ // Flag stores whether an interstitial was shown for the current navigation.
+ bool interstitial_shown_ = false;
};
} // namespace security_interstitials
diff --git a/chromium/components/security_interstitials/content/security_interstitial_tab_helper_unittest.cc b/chromium/components/security_interstitials/content/security_interstitial_tab_helper_unittest.cc
index 40b4a146597..8643b7ff236 100644
--- a/chromium/components/security_interstitials/content/security_interstitial_tab_helper_unittest.cc
+++ b/chromium/components/security_interstitials/content/security_interstitial_tab_helper_unittest.cc
@@ -260,9 +260,10 @@ TEST_F(SecurityInterstitialTabHelperFencedFrameTest,
->InitializeRenderFrameIfNeeded();
content::RenderFrameHost* fenced_frame_rfh = CreateFencedFrame(main_rfh());
std::unique_ptr<content::NavigationSimulator> navigation_simulator =
- content::NavigationSimulator::CreateForFencedFrame(fenced_frame_url,
- fenced_frame_rfh);
+ content::NavigationSimulator::CreateRendererInitiated(fenced_frame_url,
+ fenced_frame_rfh);
navigation_simulator->Commit();
+ fenced_frame_rfh = navigation_simulator->GetFinalRenderFrameHost();
EXPECT_TRUE(fenced_frame_rfh->IsFencedFrameRoot());
EXPECT_FALSE(blocking_page_destroyed);
EXPECT_TRUE(helper->IsDisplayingInterstitial());
diff --git a/chromium/components/security_interstitials/content/ssl_error_handler.cc b/chromium/components/security_interstitials/content/ssl_error_handler.cc
index ebb61d3b4ab..dd59a36daac 100644
--- a/chromium/components/security_interstitials/content/ssl_error_handler.cc
+++ b/chromium/components/security_interstitials/content/ssl_error_handler.cc
@@ -126,7 +126,7 @@ class CommonNameMismatchRedirectObserver
void NavigationEntryCommitted(
const content::LoadCommittedDetails& /* load_details */) override {
- GetWebContents().GetMainFrame()->AddMessageToConsole(
+ GetWebContents().GetPrimaryMainFrame()->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kInfo,
base::StringPrintf(
"Redirecting navigation %s -> %s because the server presented a "
diff --git a/chromium/components/security_interstitials/content/ssl_error_handler_unittest.cc b/chromium/components/security_interstitials/content/ssl_error_handler_unittest.cc
index 4e85285271d..a5675af2f7a 100644
--- a/chromium/components/security_interstitials/content/ssl_error_handler_unittest.cc
+++ b/chromium/components/security_interstitials/content/ssl_error_handler_unittest.cc
@@ -457,7 +457,7 @@ class SSLErrorAssistantProtoTest : public content::RenderViewHostTestHarness {
RunCaptivePortalTest();
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMECAST)
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
// On platforms where captive portal detection is enabled, timer should
// start for captive portal detection.
EXPECT_TRUE(error_handler()->IsTimerRunningForTesting());
@@ -476,9 +476,8 @@ class SSLErrorAssistantProtoTest : public content::RenderViewHostTestHarness {
EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
EXPECT_FALSE(delegate()->suggested_url_checked());
#else
- // On Android and Chromecast there is no custom captive portal detection
- // logic, so the timer should not start and an SSL interstitial should be
- // shown immediately.
+ // When there is no custom captive portal detection logic, the timer should
+ // not start and an SSL interstitial should be shown immediately.
EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
EXPECT_FALSE(delegate()->captive_portal_checked());
EXPECT_TRUE(delegate()->ssl_interstitial_shown());
@@ -666,7 +665,8 @@ class SSLErrorHandlerDateInvalidTest
field_trial_test()->SetFeatureParams(
false, 0.0,
- network_time::NetworkTimeTracker::FETCHES_IN_BACKGROUND_ONLY);
+ network_time::NetworkTimeTracker::FETCHES_IN_BACKGROUND_ONLY,
+ network_time::NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
}
SSLErrorHandlerDateInvalidTest(const SSLErrorHandlerDateInvalidTest&) =
@@ -1158,7 +1158,8 @@ TEST_F(SSLErrorHandlerDateInvalidTest, MAYBE_TimeQueryStarted) {
EXPECT_TRUE(test_server()->Start());
tracker()->SetTimeServerURLForTesting(test_server()->GetURL("/"));
field_trial_test()->SetFeatureParams(
- true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY);
+ true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY,
+ network_time::NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
error_handler()->StartHandlingError();
EXPECT_TRUE(error_handler()->IsTimerRunningForTesting());
@@ -1219,7 +1220,8 @@ TEST_F(SSLErrorHandlerDateInvalidTest, MAYBE_TimeQueryHangs) {
EXPECT_TRUE(test_server()->Start());
tracker()->SetTimeServerURLForTesting(test_server()->GetURL("/"));
field_trial_test()->SetFeatureParams(
- true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY);
+ true, 0.0, network_time::NetworkTimeTracker::FETCHES_ON_DEMAND_ONLY,
+ network_time::NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
error_handler()->StartHandlingError();
EXPECT_TRUE(error_handler()->IsTimerRunningForTesting());
wait_for_time_query_loop.Run();
diff --git a/chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc b/chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc
index efd772bc180..be2729fbffe 100644
--- a/chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc
+++ b/chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc
@@ -84,10 +84,6 @@ const char kSSLCertDecisionVersionKey[] = "version";
const int kDefaultSSLCertDecisionVersion = 1;
-// Key for the expiration time of a decision in the per-site HTTP allowlist
-// content settings dictionary.
-const char kHTTPAllowlistExpirationTimeKey[] = "decision_expiration_time";
-
// Records a new occurrence of |error|. The occurrence is stored in the
// recurrent interstitial pref, which keeps track of the most recent timestamps
// at which each error type occurred (up to the |threshold| most recent
@@ -207,10 +203,13 @@ StatefulSSLHostStateDelegate::StatefulSSLHostStateDelegate(
browser_context_(browser_context),
pref_service_(pref_service),
host_content_settings_map_(host_content_settings_map),
+ https_only_mode_allowlist_(
+ host_content_settings_map,
+ clock_.get(),
+ base::Seconds(kDeltaDefaultExpirationInSeconds)),
recurrent_interstitial_threshold_for_testing(-1),
recurrent_interstitial_mode_for_testing(NOT_SET),
- recurrent_interstitial_reset_time_for_testing(-1) {
-}
+ recurrent_interstitial_reset_time_for_testing(-1) {}
StatefulSSLHostStateDelegate::~StatefulSSLHostStateDelegate() = default;
@@ -227,7 +226,7 @@ void StatefulSSLHostStateDelegate::AllowCert(
DCHECK(web_contents);
content::StoragePartition* storage_partition =
browser_context_->GetStoragePartition(
- web_contents->GetMainFrame()->GetSiteInstance(),
+ web_contents->GetPrimaryMainFrame()->GetSiteInstance(),
false /* can_create */);
if (!storage_partition ||
storage_partition != browser_context_->GetDefaultStoragePartition()) {
@@ -280,9 +279,8 @@ void StatefulSSLHostStateDelegate::Clear(
host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
ContentSettingsType::SSL_CERT_DECISIONS, base::Time(), base::Time::Max(),
pattern_filter);
- host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
- ContentSettingsType::HTTP_ALLOWED, base::Time(), base::Time::Max(),
- pattern_filter);
+ https_only_mode_allowlist_.Clear(base::Time(), base::Time::Max(),
+ pattern_filter);
}
content::SSLHostStateDelegate::CertJudgment
@@ -294,7 +292,7 @@ StatefulSSLHostStateDelegate::QueryPolicy(const std::string& host,
content::StoragePartition* storage_partition =
browser_context_->GetStoragePartition(
- web_contents->GetMainFrame()->GetSiteInstance(),
+ web_contents->GetPrimaryMainFrame()->GetSiteInstance(),
false /* can_create */);
if (!storage_partition ||
storage_partition != browser_context_->GetDefaultStoragePartition()) {
@@ -375,27 +373,12 @@ void StatefulSSLHostStateDelegate::AllowHttpForHost(
content::StoragePartition* storage_partition =
browser_context_->GetStoragePartition(
- web_contents->GetMainFrame()->GetSiteInstance(),
+ web_contents->GetPrimaryMainFrame()->GetSiteInstance(),
/*can_create=*/false);
- if (!storage_partition ||
- storage_partition != browser_context_->GetDefaultStoragePartition()) {
- // Decisions for non-default storage partitions are stored in memory only.
- allowed_http_hosts_for_non_default_storage_partitions_.insert(host);
- return;
- }
-
- // Store when the HTTP allowlist entry for this host should expire. This value
- // must be stored inside a dictionary as content settings don't support
- // directly storing a string value.
- GURL url = GetSecureGURLForHost(host);
- base::Time expiration_time =
- clock_->Now() + base::Seconds(kDeltaDefaultExpirationInSeconds);
- auto dict = std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
- dict->SetKey(kHTTPAllowlistExpirationTimeKey,
- base::TimeToValue(expiration_time));
- host_content_settings_map_->SetWebsiteSettingDefaultScope(
- url, GURL(), ContentSettingsType::HTTP_ALLOWED,
- base::Value::FromUniquePtrValue(std::move(dict)));
+ bool is_nondefault_storage =
+ !storage_partition ||
+ storage_partition != browser_context_->GetDefaultStoragePartition();
+ https_only_mode_allowlist_.AllowHttpForHost(host, is_nondefault_storage);
}
bool StatefulSSLHostStateDelegate::IsHttpAllowedForHost(
@@ -403,48 +386,25 @@ bool StatefulSSLHostStateDelegate::IsHttpAllowedForHost(
content::WebContents* web_contents) {
content::StoragePartition* storage_partition =
browser_context_->GetStoragePartition(
- web_contents->GetMainFrame()->GetSiteInstance(),
- false /* can_create */);
- if (!storage_partition ||
- storage_partition != browser_context_->GetDefaultStoragePartition()) {
- return base::Contains(
- allowed_http_hosts_for_non_default_storage_partitions_, host);
- }
-
- GURL url = GetSecureGURLForHost(host);
- const ContentSettingsPattern pattern =
- ContentSettingsPattern::FromURLNoWildcard(url);
-
- const base::Value value = host_content_settings_map_->GetWebsiteSetting(
- url, url, ContentSettingsType::HTTP_ALLOWED, nullptr);
- if (!value.is_dict()) {
- return false;
- }
-
- auto* decision_expiration_value =
- value.FindKey(kHTTPAllowlistExpirationTimeKey);
- auto decision_expiration = base::ValueToTime(decision_expiration_value);
- if (decision_expiration <= clock_->Now()) {
- // Allowlist entry has expired.
- return false;
- }
-
- return true;
+ web_contents->GetPrimaryMainFrame()->GetSiteInstance(),
+ /*can_create=*/false);
+ bool is_nondefault_storage =
+ !storage_partition ||
+ storage_partition != browser_context_->GetDefaultStoragePartition();
+ return https_only_mode_allowlist_.IsHttpAllowedForHost(host,
+ is_nondefault_storage);
}
void StatefulSSLHostStateDelegate::RevokeUserAllowExceptions(
const std::string& host) {
GURL url = GetSecureGURLForHost(host);
-
host_content_settings_map_->SetWebsiteSettingDefaultScope(
url, GURL(), ContentSettingsType::SSL_CERT_DECISIONS, base::Value());
- host_content_settings_map_->SetWebsiteSettingDefaultScope(
- url, GURL(), ContentSettingsType::HTTP_ALLOWED, base::Value());
-
// Decisions for non-default storage partitions are stored separately in
// memory; delete those as well.
allowed_certs_for_non_default_storage_partitions_.erase(host);
- allowed_http_hosts_for_non_default_storage_partitions_.erase(host);
+
+ https_only_mode_allowlist_.RevokeUserAllowExceptions(host);
}
bool StatefulSSLHostStateDelegate::HasAllowException(
@@ -533,6 +493,7 @@ void StatefulSSLHostStateDelegate::ResetRecurrentErrorCountForTesting() {
void StatefulSSLHostStateDelegate::SetClockForTesting(
std::unique_ptr<base::Clock> clock) {
clock_ = std::move(clock);
+ https_only_mode_allowlist_.SetClockForTesting(clock_.get());
}
void StatefulSSLHostStateDelegate::SetRecurrentInterstitialThresholdForTesting(
@@ -580,7 +541,7 @@ bool StatefulSSLHostStateDelegate::HasCertAllowException(
content::WebContents* web_contents) {
content::StoragePartition* storage_partition =
browser_context_->GetStoragePartition(
- web_contents->GetMainFrame()->GetSiteInstance(),
+ web_contents->GetPrimaryMainFrame()->GetSiteInstance(),
false /* can_create */);
if (!storage_partition ||
storage_partition != browser_context_->GetDefaultStoragePartition()) {
diff --git a/chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.h b/chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.h
index a7e8ea7c841..db1dc5a8cda 100644
--- a/chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.h
+++ b/chromium/components/security_interstitials/content/stateful_ssl_host_state_delegate.h
@@ -11,6 +11,7 @@
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "components/keyed_service/core/keyed_service.h"
+#include "components/security_interstitials/core/https_only_mode_allowlist.h"
#include "content/public/browser/ssl_host_state_delegate.h"
class HostContentSettingsMap;
@@ -174,17 +175,9 @@ class StatefulSSLHostStateDelegate : public content::SSLHostStateDelegate,
std::map<int /* error code */, int /* count */> recurrent_errors_;
// Tracks sites that are allowed to load over HTTP when HTTPS-First Mode is
- // enabled, for non-default storage partitions. Allowed hosts are exact
- // hostname matches -- subdomains of a host on the allowlist must be
- // separately allowlisted.
- //
- // In most cases, HTTP interstitial decisions are stored in ContentSettings
- // and persisted to disk, like cert decisions. Similar to cert decisions, for
- // non-default StoragePartitions the decisions should be isolated from normal
- // browsing and don't need to be persisted to disk. For these cases, track
- // allowlist decisions purely in memory.
- std::set<std::string /* host */>
- allowed_http_hosts_for_non_default_storage_partitions_;
+ // enabled. Allowed hosts are exact hostname matches -- subdomains of a host
+ // on the allowlist must be separately allowlisted.
+ security_interstitials::HttpsOnlyModeAllowlist https_only_mode_allowlist_;
int recurrent_interstitial_threshold_for_testing;
enum RecurrentInterstitialMode recurrent_interstitial_mode_for_testing;
diff --git a/chromium/components/security_interstitials/core/BUILD.gn b/chromium/components/security_interstitials/core/BUILD.gn
index 0ef1fe858d7..2f073cfabcb 100644
--- a/chromium/components/security_interstitials/core/BUILD.gn
+++ b/chromium/components/security_interstitials/core/BUILD.gn
@@ -14,6 +14,8 @@ static_library("core") {
"common_string_util.h",
"controller_client.cc",
"controller_client.h",
+ "https_only_mode_allowlist.cc",
+ "https_only_mode_allowlist.h",
"https_only_mode_metrics.cc",
"https_only_mode_metrics.h",
"https_only_mode_ui_util.cc",
@@ -22,6 +24,8 @@ static_library("core") {
"metrics_helper.h",
"mitm_software_ui.cc",
"mitm_software_ui.h",
+ "omnibox_https_upgrade_metrics.cc",
+ "omnibox_https_upgrade_metrics.h",
"pref_names.cc",
"pref_names.h",
"safe_browsing_loud_error_ui.cc",
@@ -48,6 +52,7 @@ static_library("core") {
deps = [
"//base",
"//base:i18n",
+ "//components/content_settings/core/browser",
"//components/google/core/common",
"//components/history/core/browser",
"//components/metrics",
diff --git a/chromium/components/security_interstitials/core/blocked_interception_ui.cc b/chromium/components/security_interstitials/core/blocked_interception_ui.cc
index c90ce63c0ff..d9934685908 100644
--- a/chromium/components/security_interstitials/core/blocked_interception_ui.cc
+++ b/chromium/components/security_interstitials/core/blocked_interception_ui.cc
@@ -5,13 +5,13 @@
#include "components/security_interstitials/core/blocked_interception_ui.h"
#include "base/i18n/number_formatting.h"
+#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/security_interstitials/core/common_string_util.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "components/ssl_errors/error_info.h"
#include "components/strings/grit/components_strings.h"
-#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
namespace security_interstitials {
diff --git a/chromium/components/security_interstitials/core/browser/resources/images/blocked.svg b/chromium/components/security_interstitials/core/browser/resources/images/blocked.svg
index 03a289f59ff..5b3ff1b1e64 100644
--- a/chromium/components/security_interstitials/core/browser/resources/images/blocked.svg
+++ b/chromium/components/security_interstitials/core/browser/resources/images/blocked.svg
@@ -1 +1 @@
-<svg width="20" height="16" viewBox="0 0 20 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M-2-4h24v24H-2z"/><path d="M18 0c1 0 2 1 2 2v12c0 1-1 2-2 2H2c-1.1 0-2-.9-2-2V2c0-1 1-2 2-2h16zm-5.608 11.46l1.103-1.102-2.374-2.372L13.5 5.608l-1.103-1.102-2.38 2.378L7.632 4.5 6.529 5.602l2.386 2.384L6.5 10.398 7.603 11.5l2.414-2.413 2.375 2.373z" fill-opacity=".2" fill="#000"/></g></svg> \ No newline at end of file
+<svg width="20" height="16" viewBox="0 0 20 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M-2-4h24v24H-2z"/><path d="M18 0c1 0 2 1 2 2v12c0 1-1 2-2 2H2c-1.1 0-2-.9-2-2V2c0-1 1-2 2-2h16zm-5.608 11.46 1.103-1.102-2.374-2.372L13.5 5.608l-1.103-1.102-2.38 2.378L7.632 4.5 6.529 5.602l2.386 2.384L6.5 10.398 7.603 11.5l2.414-2.413 2.375 2.373z" fill-opacity=".2" fill="#000"/></g></svg> \ No newline at end of file
diff --git a/chromium/components/security_interstitials/core/browser/resources/images/heavy_ad.svg b/chromium/components/security_interstitials/core/browser/resources/images/heavy_ad.svg
index 7f15c3edc94..032064c45f2 100644
--- a/chromium/components/security_interstitials/core/browser/resources/images/heavy_ad.svg
+++ b/chromium/components/security_interstitials/core/browser/resources/images/heavy_ad.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path fill="none" d="M-618-440H782v3600H-618zM0 0h24v24H0z"/><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11z" fill-opacity=".2"/><path d="M14.392 16.82l1.103-1.102-2.374-2.372 2.379-2.378-1.103-1.102-2.38 2.378L9.632 9.86l-1.103 1.102 2.386 2.384L8.5 15.758l1.103 1.102 2.414-2.413z" fill-opacity=".2"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path fill="none" d="M-618-440H782v3600H-618zM0 0h24v24H0z"/><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11z" fill-opacity=".2"/><path d="m14.392 16.82 1.103-1.102-2.374-2.372 2.379-2.378-1.103-1.102-2.38 2.378L9.632 9.86l-1.103 1.102 2.386 2.384L8.5 15.758l1.103 1.102 2.414-2.413z" fill-opacity=".2"/></svg> \ No newline at end of file
diff --git a/chromium/components/security_interstitials/core/browser/resources/images/light_bulb_grey.svg b/chromium/components/security_interstitials/core/browser/resources/images/light_bulb_grey.svg
index 2cf0e30cb55..fc4f5367c68 100644
--- a/chromium/components/security_interstitials/core/browser/resources/images/light_bulb_grey.svg
+++ b/chromium/components/security_interstitials/core/browser/resources/images/light_bulb_grey.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#696969"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#696969"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 0 1 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"/></svg> \ No newline at end of file
diff --git a/chromium/components/security_interstitials/core/browser/resources/images/light_bulb_white.svg b/chromium/components/security_interstitials/core/browser/resources/images/light_bulb_white.svg
index e378483a1ae..7cc59e452e4 100644
--- a/chromium/components/security_interstitials/core/browser/resources/images/light_bulb_white.svg
+++ b/chromium/components/security_interstitials/core/browser/resources/images/light_bulb_white.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#FFF"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#FFF"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 0 1 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"/></svg> \ No newline at end of file
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_large.js b/chromium/components/security_interstitials/core/browser/resources/interstitial_large.js
index 7aab7c62184..257f4ab6151 100644
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_large.js
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_large.js
@@ -135,7 +135,7 @@ function setupEvents() {
break;
default:
- throw 'Invalid interstitial type';
+ throw new Error('Invalid interstitial type');
}
});
}
@@ -234,7 +234,7 @@ function setupEvents() {
}
if (lookalike) {
- console.log(
+ console.warn(
'Chrome has determined that ' +
loadTimeData.getString('lookalikeRequestHostname') +
' could be fake or fraudulent.\n\n' +
diff --git a/chromium/components/security_interstitials/core/https_only_mode_allowlist.cc b/chromium/components/security_interstitials/core/https_only_mode_allowlist.cc
new file mode 100644
index 00000000000..337cd81d972
--- /dev/null
+++ b/chromium/components/security_interstitials/core/https_only_mode_allowlist.cc
@@ -0,0 +1,120 @@
+// Copyright 2022 The Chromium 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/security_interstitials/core/https_only_mode_allowlist.h"
+
+#include "base/containers/contains.h"
+#include "base/json/values_util.h"
+#include "base/time/clock.h"
+
+namespace {
+
+// Key for the expiration time of a decision in the per-site HTTP allowlist
+// content settings dictionary.
+const char kHTTPAllowlistExpirationTimeKey[] = "decision_expiration_time";
+
+// All SSL decisions are per host (and are shared arcoss schemes), so this
+// canonicalizes all hosts into a secure scheme GURL to use with content
+// settings. The returned GURL will be the passed in host with an empty path and
+// https:// as the scheme.
+GURL GetSecureGURLForHost(const std::string& host) {
+ std::string url = "https://" + host;
+ return GURL(url);
+}
+
+} // namespace
+
+namespace security_interstitials {
+
+HttpsOnlyModeAllowlist::HttpsOnlyModeAllowlist(
+ HostContentSettingsMap* host_content_settings_map,
+ base::Clock* clock,
+ base::TimeDelta expiration_timeout)
+ : host_content_settings_map_(host_content_settings_map),
+ clock_(clock),
+ expiration_timeout_(expiration_timeout) {}
+
+HttpsOnlyModeAllowlist::~HttpsOnlyModeAllowlist() = default;
+
+void HttpsOnlyModeAllowlist::AllowHttpForHost(const std::string& host,
+ bool is_nondefault_storage) {
+ if (is_nondefault_storage) {
+ // Decisions for non-default storage partitions are stored in memory only.
+ allowed_http_hosts_for_non_default_storage_partitions_.insert(host);
+ return;
+ }
+
+ // Store when the HTTP allowlist entry for this host should expire. This value
+ // must be stored inside a dictionary as content settings don't support
+ // directly storing a string value.
+ GURL url = GetSecureGURLForHost(host);
+ base::Time expiration_time = clock_->Now() + expiration_timeout_;
+ auto dict = std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+ dict->SetKey(kHTTPAllowlistExpirationTimeKey,
+ base::TimeToValue(expiration_time));
+ host_content_settings_map_->SetWebsiteSettingDefaultScope(
+ url, GURL(), ContentSettingsType::HTTP_ALLOWED,
+ base::Value::FromUniquePtrValue(std::move(dict)));
+}
+
+bool HttpsOnlyModeAllowlist::IsHttpAllowedForHost(
+ const std::string& host,
+ bool is_nondefault_storage) const {
+ if (is_nondefault_storage) {
+ return base::Contains(
+ allowed_http_hosts_for_non_default_storage_partitions_, host);
+ }
+
+ GURL url = GetSecureGURLForHost(host);
+ const ContentSettingsPattern pattern =
+ ContentSettingsPattern::FromURLNoWildcard(url);
+
+ const base::Value value = host_content_settings_map_->GetWebsiteSetting(
+ url, url, ContentSettingsType::HTTP_ALLOWED, nullptr);
+ if (!value.is_dict()) {
+ return false;
+ }
+
+ auto* decision_expiration_value =
+ value.FindKey(kHTTPAllowlistExpirationTimeKey);
+ auto decision_expiration = base::ValueToTime(decision_expiration_value);
+ if (decision_expiration <= clock_->Now()) {
+ // Allowlist entry has expired.
+ return false;
+ }
+
+ return true;
+}
+
+void HttpsOnlyModeAllowlist::RevokeUserAllowExceptions(
+ const std::string& host) {
+ GURL url = GetSecureGURLForHost(host);
+ host_content_settings_map_->SetWebsiteSettingDefaultScope(
+ url, GURL(), ContentSettingsType::HTTP_ALLOWED, base::Value());
+ // Decisions for non-default storage partitions are stored separately in
+ // memory; delete those as well.
+ allowed_http_hosts_for_non_default_storage_partitions_.erase(host);
+}
+
+void HttpsOnlyModeAllowlist::Clear(
+ base::Time delete_begin,
+ base::Time delete_end,
+ const HostContentSettingsMap::PatternSourcePredicate& pattern_filter) {
+ host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
+ ContentSettingsType::HTTP_ALLOWED, delete_begin, delete_end,
+ pattern_filter);
+}
+
+void HttpsOnlyModeAllowlist::ClearAllowlist(base::Time delete_begin,
+ base::Time delete_end) {
+ Clear(delete_begin, delete_end,
+ HostContentSettingsMap::PatternSourcePredicate());
+ allowed_http_hosts_for_non_default_storage_partitions_.clear();
+}
+
+void HttpsOnlyModeAllowlist::SetClockForTesting(base::Clock* clock) {
+ clock_ = clock;
+}
+
+} // namespace security_interstitials
diff --git a/chromium/components/security_interstitials/core/https_only_mode_allowlist.h b/chromium/components/security_interstitials/core/https_only_mode_allowlist.h
new file mode 100644
index 00000000000..32250af4fb6
--- /dev/null
+++ b/chromium/components/security_interstitials/core/https_only_mode_allowlist.h
@@ -0,0 +1,84 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SECURITY_INTERSTITIALS_CORE_HTTPS_ONLY_MODE_ALLOWLIST_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CORE_HTTPS_ONLY_MODE_ALLOWLIST_H_
+
+#include <set>
+
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "url/gurl.h"
+
+namespace base {
+class Clock;
+}
+
+namespace security_interstitials {
+
+// Stores allowlist decisions for HTTPS-Only Mode.
+// A user can allowlist a site by clicking through its HTTPS-Only Mode
+// interstitial. For default storage partitions (e.g. non-incognito mode), the
+// decision is stored in content settings. Otherwise, it's stored in memory.
+class HttpsOnlyModeAllowlist {
+ public:
+ HttpsOnlyModeAllowlist(HostContentSettingsMap* host_content_settings_map,
+ base::Clock* clock,
+ base::TimeDelta expiration_timeout);
+
+ HttpsOnlyModeAllowlist(const HttpsOnlyModeAllowlist&) = delete;
+ HttpsOnlyModeAllowlist& operator=(const HttpsOnlyModeAllowlist&) = delete;
+
+ ~HttpsOnlyModeAllowlist();
+
+ // Adds host to the list of sites allowed to load over HTTP.
+ void AllowHttpForHost(const std::string& host, bool is_nondefault_storage);
+
+ // Returns true if host is allowed to be loaded over HTTP. If so, we don't
+ // attempt to upgrade it to HTTPS.
+ bool IsHttpAllowedForHost(const std::string& host,
+ bool is_nondefault_storage) const;
+
+ // Revokes all HTTP exceptions made by the user for host.
+ void RevokeUserAllowExceptions(const std::string& host);
+
+ // Clears allowlist for the given pattern filter. If the pattern filter is
+ // empty, clears allowlist for all hosts.
+ void Clear(
+ base::Time delete_begin,
+ base::Time delete_end,
+ const HostContentSettingsMap::PatternSourcePredicate& pattern_filter);
+
+ // Clears the persistent and in-memory allowlist entries. All of in-memory
+ // entries are removed, but only persistent entries between delete_begin and
+ // delete_end are removed.
+ void ClearAllowlist(base::Time delete_begin, base::Time delete_end);
+
+ // Sets the test clock.
+ void SetClockForTesting(base::Clock* clock);
+
+ private:
+ raw_ptr<HostContentSettingsMap> host_content_settings_map_;
+ raw_ptr<base::Clock> clock_;
+ base::TimeDelta expiration_timeout_;
+
+ // Tracks sites that are allowed to load over HTTP when HTTPS-First Mode is
+ // enabled, for non-default storage partitions. Allowed hosts are exact
+ // hostname matches -- subdomains of a host on the allowlist must be
+ // separately allowlisted.
+ //
+ // In most cases, HTTP interstitial decisions are stored in ContentSettings
+ // and persisted to disk, like cert decisions. Similar to cert decisions, for
+ // non-default StoragePartitions the decisions should be isolated from normal
+ // browsing and don't need to be persisted to disk. For these cases, track
+ // allowlist decisions purely in memory.
+ std::set<std::string /* host */>
+ allowed_http_hosts_for_non_default_storage_partitions_;
+};
+
+} // namespace security_interstitials
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CORE_HTTPS_ONLY_MODE_ALLOWLIST_H_
diff --git a/chromium/components/security_interstitials/core/mitm_software_ui.cc b/chromium/components/security_interstitials/core/mitm_software_ui.cc
index 1ddbb1deb72..6c12c861945 100644
--- a/chromium/components/security_interstitials/core/mitm_software_ui.cc
+++ b/chromium/components/security_interstitials/core/mitm_software_ui.cc
@@ -5,13 +5,13 @@
#include "components/security_interstitials/core/mitm_software_ui.h"
#include "base/i18n/time_formatting.h"
+#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/security_interstitials/core/common_string_util.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "components/ssl_errors/error_info.h"
#include "components/strings/grit/components_strings.h"
-#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
namespace security_interstitials {
@@ -114,12 +114,12 @@ void MITMSoftwareUI::PopulateEnterpriseUserStringsForHTML(
"primaryParagraph",
l10n_util::GetStringFUTF16(
IDS_MITM_SOFTWARE_PRIMARY_PARAGRAPH_ENTERPRISE,
- net::EscapeForHTML(base::UTF8ToUTF16(mitm_software_name_))));
+ base::EscapeForHTML(base::UTF8ToUTF16(mitm_software_name_))));
load_time_data->SetStringKey(
"explanationParagraph",
l10n_util::GetStringFUTF16(
IDS_MITM_SOFTWARE_EXPLANATION_ENTERPRISE,
- net::EscapeForHTML(base::UTF8ToUTF16(mitm_software_name_)),
+ base::EscapeForHTML(base::UTF8ToUTF16(mitm_software_name_)),
l10n_util::GetStringUTF16(IDS_MITM_SOFTWARE_EXPLANATION)));
}
@@ -129,12 +129,12 @@ void MITMSoftwareUI::PopulateAtHomeUserStringsForHTML(
"primaryParagraph",
l10n_util::GetStringFUTF16(
IDS_MITM_SOFTWARE_PRIMARY_PARAGRAPH_NONENTERPRISE,
- net::EscapeForHTML(base::UTF8ToUTF16(mitm_software_name_))));
+ base::EscapeForHTML(base::UTF8ToUTF16(mitm_software_name_))));
load_time_data->SetStringKey(
"explanationParagraph",
l10n_util::GetStringFUTF16(
IDS_MITM_SOFTWARE_EXPLANATION_NONENTERPRISE,
- net::EscapeForHTML(base::UTF8ToUTF16(mitm_software_name_)),
+ base::EscapeForHTML(base::UTF8ToUTF16(mitm_software_name_)),
l10n_util::GetStringUTF16(IDS_MITM_SOFTWARE_EXPLANATION)));
}
diff --git a/chromium/components/security_interstitials/core/omnibox_https_upgrade_metrics.cc b/chromium/components/security_interstitials/core/omnibox_https_upgrade_metrics.cc
new file mode 100644
index 00000000000..150d9c18163
--- /dev/null
+++ b/chromium/components/security_interstitials/core/omnibox_https_upgrade_metrics.cc
@@ -0,0 +1,11 @@
+// Copyright 2022 The Chromium 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/security_interstitials/core/omnibox_https_upgrade_metrics.h"
+
+namespace security_interstitials::omnibox_https_upgrades {
+
+const char kEventHistogram[] = "TypedNavigationUpgradeThrottle.Event";
+
+}
diff --git a/chromium/components/security_interstitials/core/omnibox_https_upgrade_metrics.h b/chromium/components/security_interstitials/core/omnibox_https_upgrade_metrics.h
new file mode 100644
index 00000000000..a37c29ce3f6
--- /dev/null
+++ b/chromium/components/security_interstitials/core/omnibox_https_upgrade_metrics.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SECURITY_INTERSTITIALS_CORE_OMNIBOX_HTTPS_UPGRADE_METRICS_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CORE_OMNIBOX_HTTPS_UPGRADE_METRICS_H_
+
+namespace security_interstitials::omnibox_https_upgrades {
+
+extern const char kEventHistogram[];
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class Event {
+ kNone = 0,
+ // Started the load of an upgraded HTTPS URL.
+ kHttpsLoadStarted,
+ // Successfully finished loading the upgraded HTTPS URL.
+ kHttpsLoadSucceeded,
+ // Failed to load the upgraded HTTPS URL because of a cert error, fell back
+ // to the HTTP URL.
+ kHttpsLoadFailedWithCertError,
+ // Failed to load the upgraded HTTPS URL because of a net error, fell back
+ // to the HTTP URL.
+ kHttpsLoadFailedWithNetError,
+ // Failed to load the upgraded HTTPS URL within the timeout window, fell
+ // back to the HTTP URL.
+ kHttpsLoadTimedOut,
+ // Received a redirect. This doesn't necessarily imply that the HTTPS load
+ // succeeded or failed.
+ kRedirected,
+ kMaxValue = kRedirected,
+};
+
+} // namespace security_interstitials::omnibox_https_upgrades
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CORE_OMNIBOX_HTTPS_UPGRADE_METRICS_H_
diff --git a/chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc b/chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
index eae06a31dca..34d5d9fa672 100644
--- a/chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
+++ b/chromium/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
@@ -6,6 +6,7 @@
#include "base/i18n/time_formatting.h"
#include "base/metrics/histogram_macros.h"
+#include "base/strings/escape.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
@@ -15,7 +16,6 @@
#include "components/security_interstitials/core/controller_client.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "components/strings/grit/components_strings.h"
-#include "net/base/escape.h"
#include "net/base/url_util.h"
#include "ui/base/l10n/l10n_util.h"
@@ -212,7 +212,7 @@ void SafeBrowsingLoudErrorUI::HandleCommand(
security_interstitials::MetricsHelper::SHOW_DIAGNOSTIC);
std::string diagnostic = base::StringPrintf(
kSbDiagnosticUrl,
- net::EscapeQueryParamValue(request_url().spec(), true).c_str());
+ base::EscapeQueryParamValue(request_url().spec(), true).c_str());
GURL diagnostic_url(diagnostic);
diagnostic_url =
google_util::AppendGoogleLocaleParam(diagnostic_url, app_locale());
@@ -224,7 +224,7 @@ void SafeBrowsingLoudErrorUI::HandleCommand(
security_interstitials::MetricsHelper::REPORT_PHISHING_ERROR);
std::string phishing_error = base::StringPrintf(
kReportPhishingErrorUrl,
- net::EscapeQueryParamValue(request_url().spec(), true).c_str());
+ base::EscapeQueryParamValue(request_url().spec(), true).c_str());
GURL phishing_error_url(phishing_error);
phishing_error_url = google_util::AppendGoogleLocaleParam(
phishing_error_url, app_locale());
diff --git a/chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc b/chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
index 91988e68efd..02c9b58da49 100644
--- a/chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
+++ b/chromium/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
@@ -6,13 +6,13 @@
#include "base/i18n/time_formatting.h"
#include "base/metrics/histogram_macros.h"
+#include "base/strings/escape.h"
#include "components/google/core/common/google_util.h"
#include "components/grit/components_resources.h"
#include "components/security_interstitials/core/common_string_util.h"
#include "components/security_interstitials/core/controller_client.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "components/strings/grit/components_strings.h"
-#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
namespace security_interstitials {
diff --git a/chromium/components/security_state/content/android/BUILD.gn b/chromium/components/security_state/content/android/BUILD.gn
index 787f71bf2c7..81ccc5ab73d 100644
--- a/chromium/components/security_state/content/android/BUILD.gn
+++ b/chromium/components/security_state/content/android/BUILD.gn
@@ -25,7 +25,8 @@ android_library("java") {
"java/src/org/chromium/components/security_state/SecurityStateModel.java",
]
deps = [
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/security_state/core:security_state_enums_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/components/security_state/core/BUILD.gn b/chromium/components/security_state/core/BUILD.gn
index fde5a1014ec..a226b75a7cf 100644
--- a/chromium/components/security_state/core/BUILD.gn
+++ b/chromium/components/security_state/core/BUILD.gn
@@ -14,7 +14,6 @@ static_library("core") {
]
public_deps = [
- ":features",
"//base",
"//net",
"//url",
@@ -40,21 +39,7 @@ source_set("unit_tests") {
deps = [
":core",
- ":features",
"//net:test_support",
"//testing/gtest",
]
}
-
-component("features") {
- output_name = "security_state_features"
-
- defines = [ "IS_SECURITY_STATE_FEATURES_IMPL" ]
-
- sources = [
- "features.cc",
- "features.h",
- ]
-
- deps = [ "//base" ]
-}
diff --git a/chromium/components/security_state/core/features.cc b/chromium/components/security_state/core/features.cc
deleted file mode 100644
index df385659b45..00000000000
--- a/chromium/components/security_state/core/features.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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 "components/security_state/core/features.h"
-
-namespace security_state {
-namespace features {
-
-const base::Feature kSafetyTipUI{"SafetyTip", base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kSafetyTipUIForSimplifiedDomainDisplay{
- "SafetyTipForSimplifiedDomainDisplay", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kSafetyTipUIOnDelayedWarning{
- "SafetyTipUIOnDelayedWarning", base::FEATURE_DISABLED_BY_DEFAULT};
-
-} // namespace features
-} // namespace security_state
diff --git a/chromium/components/security_state/core/features.h b/chromium/components/security_state/core/features.h
deleted file mode 100644
index 0388f38cade..00000000000
--- a/chromium/components/security_state/core/features.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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 COMPONENTS_SECURITY_STATE_CORE_FEATURES_H_
-#define COMPONENTS_SECURITY_STATE_CORE_FEATURES_H_
-
-#include "base/component_export.h"
-#include "base/feature_list.h"
-
-namespace security_state {
-namespace features {
-
-// This feature enables Safety Tip warnings on possibly-risky sites.
-COMPONENT_EXPORT(SECURITY_STATE_FEATURES)
-extern const base::Feature kSafetyTipUI;
-
-// This feature enables Safety Tip warnings on some types of lookalike sites,
-// for the purposes of measuring Simplified Domain Display
-// (https://crbug.com/1090393). It has similar behavior to kSafetyTipUI, but can
-// be enabled independently in a separate experiment.
-COMPONENT_EXPORT(SECURITY_STATE_FEATURES)
-extern const base::Feature kSafetyTipUIForSimplifiedDomainDisplay;
-
-// This feature enables Safety Tip warnings on pages where there is a delayed
-// Safe Browsing warning. Has no effect unless safe_browsing::kDelayedWarnings
-// is also enabled. Can be enabled independently of kSafetyTipUI.
-COMPONENT_EXPORT(SECURITY_STATE_FEATURES)
-extern const base::Feature kSafetyTipUIOnDelayedWarning;
-
-} // namespace features
-} // namespace security_state
-
-#endif // COMPONENTS_SECURITY_STATE_CORE_FEATURES_H_
diff --git a/chromium/components/security_state/core/security_state.cc b/chromium/components/security_state/core/security_state.cc
index db8be156f6d..25ae1a7a26b 100644
--- a/chromium/components/security_state/core/security_state.cc
+++ b/chromium/components/security_state/core/security_state.cc
@@ -12,7 +12,6 @@
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
-#include "components/security_state/core/features.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
@@ -67,10 +66,6 @@ std::string GetHistogramSuffixForSafetyTipStatus(
// Sets |level| to the right value if status should be set.
bool ShouldSetSecurityLevelFromSafetyTip(security_state::SafetyTipStatus status,
SecurityLevel* level) {
- if (!IsSafetyTipUIFeatureEnabled()) {
- return false;
- }
-
switch (status) {
case security_state::SafetyTipStatus::kBadReputation:
*level = security_state::NONE;
@@ -292,11 +287,4 @@ bool IsSHA1InChain(const VisibleSecurityState& visible_security_state) {
net::CERT_STATUS_SHA1_SIGNATURE_PRESENT);
}
-bool IsSafetyTipUIFeatureEnabled() {
- return base::FeatureList::IsEnabled(features::kSafetyTipUI) ||
- base::FeatureList::IsEnabled(
- features::kSafetyTipUIForSimplifiedDomainDisplay) ||
- base::FeatureList::IsEnabled(features::kSafetyTipUIOnDelayedWarning);
-}
-
} // namespace security_state
diff --git a/chromium/components/security_state/core/security_state.h b/chromium/components/security_state/core/security_state.h
index 50feb1fa7ba..74d81acd079 100644
--- a/chromium/components/security_state/core/security_state.h
+++ b/chromium/components/security_state/core/security_state.h
@@ -248,10 +248,6 @@ std::string GetSafetyTipHistogramName(const std::string& prefix,
bool IsSHA1InChain(const VisibleSecurityState& visible_security_state);
-// Returns true if Safety Tip UI should be shown because a relevant field trial
-// is enabled.
-bool IsSafetyTipUIFeatureEnabled();
-
} // namespace security_state
#endif // COMPONENTS_SECURITY_STATE_CORE_SECURITY_STATE_H_
diff --git a/chromium/components/security_state/core/security_state_unittest.cc b/chromium/components/security_state/core/security_state_unittest.cc
index f3c1215bfda..866542fb4fc 100644
--- a/chromium/components/security_state/core/security_state_unittest.cc
+++ b/chromium/components/security_state/core/security_state_unittest.cc
@@ -11,8 +11,6 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "components/security_state/core/features.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_connection_status_flags.h"
@@ -330,10 +328,6 @@ TEST(SecurityStateTest, SafetyTipSometimesRemovesSecure) {
{SafetyTipStatus::kBadKeyword, SECURE},
};
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- security_state::features::kSafetyTipUI);
-
for (auto testcase : kTestCases) {
TestSecurityStateHelper helper;
helper.set_cert_status(0);
diff --git a/chromium/components/segmentation_platform/components_unittests.filter b/chromium/components/segmentation_platform/components_unittests.filter
index d696a9dd37b..6a15249e184 100644
--- a/chromium/components/segmentation_platform/components_unittests.filter
+++ b/chromium/components/segmentation_platform/components_unittests.filter
@@ -2,6 +2,7 @@ CustomInputProcessorTest.*
DatabaseMaintenanceImplTest.*
DefaultModelManagerTest.*
DummySegmentationPlatformServiceTest.*
+FailedUkmDatabaseTest.*
FeatureAggregatorImplTest.*
FeatureListQueryProcessorTest.*
HistogramSignalHandlerTest.*
@@ -28,6 +29,7 @@ SignalFilterProcessorTest.*
SignalKeyInternalTest.*
SignalKeyTest.*
SignalStorageConfigTest.*
+SqlFeatureProcessorTest.*
StatsTest.*
TrainingDataCollectorImplTest.*
UkmConfigTest.*
diff --git a/chromium/components/segmentation_platform/content/BUILD.gn b/chromium/components/segmentation_platform/content/BUILD.gn
new file mode 100644
index 00000000000..e9d43e74a98
--- /dev/null
+++ b/chromium/components/segmentation_platform/content/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (is_android) {
+ import("//build/config/android/config.gni")
+ import("//build/config/android/rules.gni")
+}
+
+source_set("content") {
+ sources = [
+ "page_load_trigger_context.cc",
+ "page_load_trigger_context.h",
+ "segmentation_platform_tab_helper.cc",
+ "segmentation_platform_tab_helper.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/segmentation_platform/public",
+ "//content/public/browser",
+ ]
+
+ if (is_android) {
+ deps += [ ":jni_headers" ]
+ }
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ # IMPORTANT NOTE: When adding new tests, also remember to update the list of
+ # tests in //components/segmentation_platform/components_unittests.filter
+ sources = []
+
+ deps = [
+ ":content",
+ "//testing/gtest",
+ ]
+}
+
+if (is_android) {
+ android_library("content_java") {
+ visibility = [ "*" ]
+ sources = [ "android/java/src/org/chromium/components/segmentation_platform/PageLoadTriggerContext.java" ]
+
+ deps = [
+ "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
+ "//components/segmentation_platform/public:public_java",
+ "//content/public/android:content_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ "//ui/android:ui_no_recycler_view_java",
+ ]
+ annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+ }
+
+ generate_jni("jni_headers") {
+ visibility = [ ":*" ]
+
+ sources = [ "android/java/src/org/chromium/components/segmentation_platform/PageLoadTriggerContext.java" ]
+ }
+}
diff --git a/chromium/components/segmentation_platform/content/DEPS b/chromium/components/segmentation_platform/content/DEPS
new file mode 100644
index 00000000000..4bea8d18225
--- /dev/null
+++ b/chromium/components/segmentation_platform/content/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/android/java/src/org/chromium/content_public/browser/WebContents.java",
+ "+content/public/browser",
+]
diff --git a/chromium/components/segmentation_platform/content/page_load_trigger_context.cc b/chromium/components/segmentation_platform/content/page_load_trigger_context.cc
new file mode 100644
index 00000000000..bc84fd9edfc
--- /dev/null
+++ b/chromium/components/segmentation_platform/content/page_load_trigger_context.cc
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/content/page_load_trigger_context.h"
+
+#include "content/public/browser/web_contents.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#include "components/segmentation_platform/content/jni_headers/PageLoadTriggerContext_jni.h"
+#endif // BUILDFLAG(IS_ANDROID)
+
+namespace segmentation_platform {
+
+PageLoadTriggerContext::PageLoadTriggerContext(
+ content::WebContents* web_contents)
+ : web_contents_(web_contents->GetWeakPtr()) {}
+
+PageLoadTriggerContext::~PageLoadTriggerContext() = default;
+
+#if BUILDFLAG(IS_ANDROID)
+base::android::ScopedJavaLocalRef<jobject>
+PageLoadTriggerContext::CreateJavaObject() const {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobject> j_web_contents;
+ if (web_contents_ && !web_contents_->IsBeingDestroyed())
+ j_web_contents = web_contents_->GetJavaWebContents();
+ return Java_PageLoadTriggerContext_createPageLoadTriggerContext(
+ env, j_web_contents);
+}
+#endif // BUILDFLAG(IS_ANDROID)
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/content/page_load_trigger_context.h b/chromium/components/segmentation_platform/content/page_load_trigger_context.h
new file mode 100644
index 00000000000..34c5fe47055
--- /dev/null
+++ b/chromium/components/segmentation_platform/content/page_load_trigger_context.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_CONTENT_PAGE_LOAD_TRIGGER_CONTEXT_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_CONTENT_PAGE_LOAD_TRIGGER_CONTEXT_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/segmentation_platform/public/trigger_context.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#endif // BUILDFLAG(IS_ANDROID)
+
+namespace content {
+class WebContents;
+} // namespace content
+
+namespace segmentation_platform {
+
+// Contains contextual information for a page load trigger event.
+struct PageLoadTriggerContext : public TriggerContext {
+ public:
+ explicit PageLoadTriggerContext(content::WebContents* web_contents);
+ ~PageLoadTriggerContext() override;
+
+#if BUILDFLAG(IS_ANDROID)
+ base::android::ScopedJavaLocalRef<jobject> CreateJavaObject() const override;
+#endif // BUILDFLAG(IS_ANDROID)
+
+ private:
+ base::WeakPtr<content::WebContents> web_contents_;
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_CONTENT_PAGE_LOAD_TRIGGER_CONTEXT_H_
diff --git a/chromium/components/segmentation_platform/content/segmentation_platform_tab_helper.cc b/chromium/components/segmentation_platform/content/segmentation_platform_tab_helper.cc
new file mode 100644
index 00000000000..04a96cab0dc
--- /dev/null
+++ b/chromium/components/segmentation_platform/content/segmentation_platform_tab_helper.cc
@@ -0,0 +1,42 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/content/segmentation_platform_tab_helper.h"
+
+#include "components/segmentation_platform/content/page_load_trigger_context.h"
+#include "components/segmentation_platform/public/segmentation_platform_service.h"
+#include "content/public/browser/page.h"
+#include "content/public/browser/web_contents.h"
+
+namespace segmentation_platform {
+
+SegmentationPlatformTabHelper::SegmentationPlatformTabHelper(
+ content::WebContents* web_contents,
+ SegmentationPlatformService* segmentation_platform_service)
+ : content::WebContentsObserver(web_contents),
+ content::WebContentsUserData<SegmentationPlatformTabHelper>(
+ *web_contents),
+ segmentation_platform_service_(segmentation_platform_service) {}
+
+SegmentationPlatformTabHelper::~SegmentationPlatformTabHelper() = default;
+
+void SegmentationPlatformTabHelper::PrimaryPageChanged(content::Page& page) {
+ if (!segmentation_platform_service_)
+ return;
+
+ if (page.GetMainDocument().IsErrorDocument())
+ return;
+
+ // Only trigger for the visible tabs.
+ if (GetWebContents().GetVisibility() == content::Visibility::HIDDEN)
+ return;
+
+ PageLoadTriggerContext trigger_context(&GetWebContents());
+ segmentation_platform_service_->OnTrigger(TriggerType::kPageLoad,
+ trigger_context);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(SegmentationPlatformTabHelper);
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/content/segmentation_platform_tab_helper.h b/chromium/components/segmentation_platform/content/segmentation_platform_tab_helper.h
new file mode 100644
index 00000000000..593d2e76752
--- /dev/null
+++ b/chromium/components/segmentation_platform/content/segmentation_platform_tab_helper.h
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_CONTENT_SEGMENTATION_PLATFORM_TAB_HELPER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_CONTENT_SEGMENTATION_PLATFORM_TAB_HELPER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class Page;
+class WebContents;
+} // namespace content
+
+namespace segmentation_platform {
+class SegmentationPlatformService;
+
+// Observes navigation specific trigger events for a given tab.
+class SegmentationPlatformTabHelper
+ : public content::WebContentsObserver,
+ public content::WebContentsUserData<SegmentationPlatformTabHelper> {
+ public:
+ SegmentationPlatformTabHelper(
+ content::WebContents* web_contents,
+ SegmentationPlatformService* segmentation_platform_service);
+
+ SegmentationPlatformTabHelper(const SegmentationPlatformTabHelper&) = delete;
+ SegmentationPlatformTabHelper& operator=(
+ const SegmentationPlatformTabHelper&) = delete;
+
+ ~SegmentationPlatformTabHelper() override;
+
+ private:
+ friend class content::WebContentsUserData<SegmentationPlatformTabHelper>;
+
+ // content::WebContentsObserver implementation
+ void PrimaryPageChanged(content::Page& page) override;
+
+ raw_ptr<SegmentationPlatformService> segmentation_platform_service_;
+ base::WeakPtrFactory<SegmentationPlatformTabHelper> weak_ptr_factory_{this};
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_CONTENT_SEGMENTATION_PLATFORM_TAB_HELPER_H_
diff --git a/chromium/components/segmentation_platform/internal/BUILD.gn b/chromium/components/segmentation_platform/internal/BUILD.gn
index 7e5e725b92d..7df07e1f311 100644
--- a/chromium/components/segmentation_platform/internal/BUILD.gn
+++ b/chromium/components/segmentation_platform/internal/BUILD.gn
@@ -12,7 +12,9 @@ static_library("internal") {
visibility = [
":*",
"//chrome/browser",
+ "//chrome/browser/segmentation_platform:*",
"//chrome/test:*",
+ "//ios/chrome/browser/segmentation_platform:*",
]
sources = [
@@ -27,8 +29,6 @@ static_library("internal") {
"database/database_maintenance.h",
"database/database_maintenance_impl.cc",
"database/database_maintenance_impl.h",
- "database/metadata_utils.cc",
- "database/metadata_utils.h",
"database/segment_info_database.cc",
"database/segment_info_database.h",
"database/signal_database.h",
@@ -46,8 +46,11 @@ static_library("internal") {
"database/ukm_database.h",
"database/ukm_database_backend.cc",
"database/ukm_database_backend.h",
+ "database/ukm_database_impl.cc",
+ "database/ukm_database_impl.h",
"database/ukm_metrics_table.cc",
"database/ukm_metrics_table.h",
+ "database/ukm_types.cc",
"database/ukm_types.h",
"database/ukm_url_table.cc",
"database/ukm_url_table.h",
@@ -55,17 +58,10 @@ static_library("internal") {
"dummy_segmentation_platform_service.h",
"dummy_ukm_data_manager.cc",
"dummy_ukm_data_manager.h",
- "execution/custom_input_processor.cc",
- "execution/custom_input_processor.h",
"execution/default_model_manager.cc",
"execution/default_model_manager.h",
- "execution/feature_aggregator.h",
- "execution/feature_aggregator_impl.cc",
- "execution/feature_aggregator_impl.h",
- "execution/feature_list_query_processor.cc",
- "execution/feature_list_query_processor.h",
- "execution/feature_processor_state.cc",
- "execution/feature_processor_state.h",
+ "execution/execution_request.cc",
+ "execution/execution_request.h",
"execution/model_execution_manager.h",
"execution/model_execution_manager_impl.cc",
"execution/model_execution_manager_impl.h",
@@ -73,11 +69,32 @@ static_library("internal") {
"execution/model_executor.h",
"execution/model_executor_impl.cc",
"execution/model_executor_impl.h",
- "execution/query_processor.h",
- "execution/sql_feature_processor.cc",
- "execution/sql_feature_processor.h",
- "execution/uma_feature_processor.cc",
- "execution/uma_feature_processor.h",
+ "execution/processing/custom_input_processor.cc",
+ "execution/processing/custom_input_processor.h",
+ "execution/processing/feature_aggregator.h",
+ "execution/processing/feature_aggregator_impl.cc",
+ "execution/processing/feature_aggregator_impl.h",
+ "execution/processing/feature_list_query_processor.cc",
+ "execution/processing/feature_list_query_processor.h",
+ "execution/processing/feature_processor_state.cc",
+ "execution/processing/feature_processor_state.h",
+ "execution/processing/input_delegate.cc",
+ "execution/processing/input_delegate.h",
+ "execution/processing/query_processor.h",
+ "execution/processing/sql_feature_processor.cc",
+ "execution/processing/sql_feature_processor.h",
+ "execution/processing/uma_feature_processor.cc",
+ "execution/processing/uma_feature_processor.h",
+ "input_context.cc",
+ "input_context.h",
+ "local_state_helper_impl.cc",
+ "local_state_helper_impl.h",
+ "metadata/metadata_utils.cc",
+ "metadata/metadata_utils.h",
+ "metadata/metadata_writer.cc",
+ "metadata/metadata_writer.h",
+ "metric_filter_utils.cc",
+ "metric_filter_utils.h",
"platform_options.cc",
"platform_options.h",
"scheduler/execution_service.cc",
@@ -85,10 +102,14 @@ static_library("internal") {
"scheduler/model_execution_scheduler.h",
"scheduler/model_execution_scheduler_impl.cc",
"scheduler/model_execution_scheduler_impl.h",
+ "segment_id_convertor.cc",
+ "segment_id_convertor.h",
"segmentation_platform_service_impl.cc",
"segmentation_platform_service_impl.h",
"segmentation_ukm_helper.cc",
"segmentation_ukm_helper.h",
+ "selection/experimental_group_recorder.cc",
+ "selection/experimental_group_recorder.h",
"selection/segment_result_provider.cc",
"selection/segment_result_provider.h",
"selection/segment_score_provider.cc",
@@ -134,6 +155,7 @@ static_library("internal") {
"//components/prefs",
"//components/segmentation_platform/internal/proto",
"//components/segmentation_platform/public",
+ "//components/segmentation_platform/public/proto",
"//components/ukm:ukm_recorder",
"//services/metrics/public/cpp:metrics_cpp",
"//services/metrics/public/cpp:ukm_builders",
@@ -146,6 +168,8 @@ static_library("internal") {
if (is_android) {
sources += [
+ "android/segmentation_platform_conversion_bridge.cc",
+ "android/segmentation_platform_conversion_bridge.h",
"android/segmentation_platform_service_android.cc",
"android/segmentation_platform_service_android.h",
]
@@ -184,7 +208,6 @@ source_set("unit_tests") {
sources = [
"data_collection/training_data_collector_impl_unittest.cc",
"database/database_maintenance_impl_unittest.cc",
- "database/metadata_utils_unittest.cc",
"database/mock_signal_database.cc",
"database/mock_signal_database.h",
"database/mock_signal_storage_config.cc",
@@ -204,19 +227,19 @@ source_set("unit_tests") {
"database/ukm_metrics_table_unittest.cc",
"database/ukm_url_table_unittest.cc",
"dummy_segmentation_platform_service_unittest.cc",
- "execution/custom_input_processor_unittest.cc",
"execution/default_model_manager_unittest.cc",
- "execution/feature_aggregator_impl_unittest.cc",
- "execution/feature_list_query_processor_unittest.cc",
- "execution/mock_feature_aggregator.cc",
- "execution/mock_feature_aggregator.h",
- "execution/mock_feature_list_query_processor.cc",
- "execution/mock_feature_list_query_processor.h",
- "execution/mock_model_provider.cc",
- "execution/mock_model_provider.h",
"execution/model_execution_manager_impl_unittest.cc",
"execution/model_executor_impl_unittest.cc",
- "execution/query_processor.h",
+ "execution/processing/custom_input_processor_unittest.cc",
+ "execution/processing/feature_aggregator_impl_unittest.cc",
+ "execution/processing/feature_list_query_processor_unittest.cc",
+ "execution/processing/mock_feature_aggregator.cc",
+ "execution/processing/mock_feature_aggregator.h",
+ "execution/processing/mock_feature_list_query_processor.cc",
+ "execution/processing/mock_feature_list_query_processor.h",
+ "execution/processing/query_processor.h",
+ "execution/processing/sql_feature_processor_unittest.cc",
+ "metadata/metadata_utils_unittest.cc",
"mock_ukm_data_manager.cc",
"mock_ukm_data_manager.h",
"scheduler/model_execution_scheduler_unittest.cc",
@@ -244,6 +267,7 @@ source_set("unit_tests") {
deps = [
":internal",
+ ":test_support",
"//base",
"//base/test:test_support",
"//components/history/core/browser:browser",
@@ -279,6 +303,20 @@ source_set("unit_tests") {
}
}
+source_set("test_support") {
+ testonly = true
+ sources = [
+ "execution/mock_model_provider.cc",
+ "execution/mock_model_provider.h",
+ ]
+ deps = [
+ "//base",
+ "//components/segmentation_platform/public:public",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
+
bundle_data("unit_tests_bundle_data") {
visibility = [ ":unit_tests" ]
testonly = true
@@ -290,13 +328,17 @@ bundle_data("unit_tests_bundle_data") {
if (is_android) {
android_library("internal_java") {
visibility = [ "//chrome/android:chrome_all_java" ]
- sources = [ "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java" ]
+ sources = [
+ "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformConversionBridge.java",
+ "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java",
+ ]
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/optimization_guide/proto:optimization_guide_proto_java",
"//components/segmentation_platform/public:public_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
}
@@ -307,6 +349,9 @@ if (is_android) {
"//chrome/browser",
]
- sources = [ "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java" ]
+ sources = [
+ "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformConversionBridge.java",
+ "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformServiceImpl.java",
+ ]
}
}
diff --git a/chromium/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.cc b/chromium/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.cc
new file mode 100644
index 00000000000..029aaa5f5a1
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.cc
@@ -0,0 +1,36 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h"
+
+#include "components/segmentation_platform/internal/jni_headers/SegmentationPlatformConversionBridge_jni.h"
+#include "components/segmentation_platform/public/segment_selection_result.h"
+#include "components/segmentation_platform/public/trigger_context.h"
+
+namespace segmentation_platform {
+
+// static
+ScopedJavaLocalRef<jobject>
+SegmentationPlatformConversionBridge::CreateJavaSegmentSelectionResult(
+ JNIEnv* env,
+ const SegmentSelectionResult& result) {
+ int selected_segment = result.segment.has_value()
+ ? result.segment.value()
+ : proto::SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
+ return Java_SegmentationPlatformConversionBridge_createSegmentSelectionResult(
+ env, result.is_ready, selected_segment);
+}
+
+// static
+ScopedJavaLocalRef<jobject>
+SegmentationPlatformConversionBridge::CreateJavaOnDemandSegmentSelectionResult(
+ JNIEnv* env,
+ const SegmentSelectionResult& result,
+ const TriggerContext& trigger_context) {
+ return Java_SegmentationPlatformConversionBridge_createOnDemandSegmentSelectionResult(
+ env, CreateJavaSegmentSelectionResult(env, result),
+ trigger_context.CreateJavaObject());
+}
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h b/chromium/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h
new file mode 100644
index 00000000000..fcc653f1d7a
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_ANDROID_SEGMENTATION_PLATFORM_CONVERSION_BRIDGE_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_ANDROID_SEGMENTATION_PLATFORM_CONVERSION_BRIDGE_H_
+
+#include "base/android/jni_android.h"
+#include "base/memory/raw_ptr.h"
+#include "components/segmentation_platform/public/segment_selection_result.h"
+#include "components/segmentation_platform/public/trigger_context.h"
+
+using base::android::ScopedJavaLocalRef;
+
+namespace segmentation_platform {
+
+// A helper class for creating Java objects required by the segmentation
+// platform from their C++ counterparts.
+class SegmentationPlatformConversionBridge {
+ public:
+ static ScopedJavaLocalRef<jobject> CreateJavaSegmentSelectionResult(
+ JNIEnv* env,
+ const SegmentSelectionResult& result);
+ static ScopedJavaLocalRef<jobject> CreateJavaOnDemandSegmentSelectionResult(
+ JNIEnv* env,
+ const SegmentSelectionResult& result,
+ const TriggerContext& trigger_context);
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_ANDROID_SEGMENTATION_PLATFORM_CONVERSION_BRIDGE_H_
diff --git a/chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc b/chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc
index d580aa7d579..820d7ae43d9 100644
--- a/chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc
+++ b/chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.cc
@@ -9,9 +9,11 @@
#include "base/android/callback_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
+#include "components/segmentation_platform/internal/android/segmentation_platform_conversion_bridge.h"
#include "components/segmentation_platform/internal/jni_headers/SegmentationPlatformServiceImpl_jni.h"
#include "components/segmentation_platform/public/segment_selection_result.h"
#include "components/segmentation_platform/public/segmentation_platform_service.h"
+#include "components/segmentation_platform/public/trigger_context.h"
using base::android::AttachCurrentThread;
@@ -21,21 +23,25 @@ namespace {
const char kSegmentationPlatformServiceBridgeKey[] =
"segmentation_platform_service_bridge";
-ScopedJavaLocalRef<jobject> CreateJavaSegmentSelectionResult(
- JNIEnv* env,
- const SegmentSelectionResult& result) {
- int selected_segment = result.segment.has_value()
- ? result.segment.value()
- : OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
- return Java_SegmentationPlatformServiceImpl_createSegmentSelectionResult(
- env, result.is_ready, selected_segment);
-}
-
void RunGetSelectedSegmentCallback(const JavaRef<jobject>& j_callback,
const SegmentSelectionResult& result) {
JNIEnv* env = AttachCurrentThread();
- RunObjectCallbackAndroid(j_callback,
- CreateJavaSegmentSelectionResult(env, result));
+ RunObjectCallbackAndroid(
+ j_callback,
+ SegmentationPlatformConversionBridge::CreateJavaSegmentSelectionResult(
+ env, result));
+}
+
+void RunOnDemandSegmentSelectionCallback(
+ const JavaRef<jobject>& j_callback,
+ const SegmentSelectionResult& result,
+ const TriggerContext& trigger_context) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> j_on_demand_result =
+ SegmentationPlatformConversionBridge::
+ CreateJavaOnDemandSegmentSelectionResult(env, result,
+ trigger_context);
+ RunObjectCallbackAndroid(j_callback, j_on_demand_result);
}
} // namespace
@@ -89,11 +95,36 @@ SegmentationPlatformServiceAndroid::GetCachedSegmentResult(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jstring>& j_segmentation_key) {
- return CreateJavaSegmentSelectionResult(
+ return SegmentationPlatformConversionBridge::CreateJavaSegmentSelectionResult(
env, segmentation_platform_service_->GetCachedSegmentResult(
ConvertJavaStringToUTF8(env, j_segmentation_key)));
}
+int SegmentationPlatformServiceAndroid::
+ RegisterOnDemandSegmentSelectionCallback(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& jcaller,
+ const JavaParamRef<jstring>& j_segmentation_key,
+ const JavaParamRef<jobject>& jcallback) {
+ CallbackId callback_id =
+ segmentation_platform_service_->RegisterOnDemandSegmentSelectionCallback(
+ ConvertJavaStringToUTF8(env, j_segmentation_key),
+ base::BindRepeating(&RunOnDemandSegmentSelectionCallback,
+ ScopedJavaGlobalRef<jobject>(jcallback)));
+ return callback_id.value();
+}
+
+void SegmentationPlatformServiceAndroid::
+ UnregisterOnDemandSegmentSelectionCallback(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& jcaller,
+ const JavaParamRef<jstring>& j_segmentation_key,
+ jint j_callback_id) {
+ segmentation_platform_service_->UnregisterOnDemandSegmentSelectionCallback(
+ CallbackId::FromUnsafeValue(j_callback_id),
+ ConvertJavaStringToUTF8(env, j_segmentation_key));
+}
+
ScopedJavaLocalRef<jobject>
SegmentationPlatformServiceAndroid::GetJavaObject() {
return ScopedJavaLocalRef<jobject>(java_obj_);
diff --git a/chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.h b/chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.h
index 86ad117a3e0..7653f548796 100644
--- a/chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.h
+++ b/chromium/components/segmentation_platform/internal/android/segmentation_platform_service_android.h
@@ -35,6 +35,18 @@ class SegmentationPlatformServiceAndroid : public base::SupportsUserData::Data {
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jstring>& j_segmentation_key);
+ int RegisterOnDemandSegmentSelectionCallback(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& jcaller,
+ const JavaParamRef<jstring>& j_segmentation_key,
+ const JavaParamRef<jobject>& jcallback);
+
+ void UnregisterOnDemandSegmentSelectionCallback(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& jcaller,
+ const JavaParamRef<jstring>& j_segmentation_key,
+ jint j_callback_id);
+
ScopedJavaLocalRef<jobject> GetJavaObject();
private:
diff --git a/chromium/components/segmentation_platform/internal/constants.cc b/chromium/components/segmentation_platform/internal/constants.cc
index 3037f980e96..cab92fb9ffa 100644
--- a/chromium/components/segmentation_platform/internal/constants.cc
+++ b/chromium/components/segmentation_platform/internal/constants.cc
@@ -12,6 +12,9 @@ const char kSegmentationResultPref[] =
const char kSegmentationUkmMostRecentAllowedTimeKey[] =
"segmentation_platform.ukm_most_recent_allowed_time_key";
+const char kSegmentationLastCollectionTimePref[] =
+ "segmentation_platform.last_collection_time";
+
// The segmentation platform will ignore all the valid results from previous
// model executions, and re-run all the models and recompute segment selections.
// Used for testing the model execution locally.
diff --git a/chromium/components/segmentation_platform/internal/constants.h b/chromium/components/segmentation_platform/internal/constants.h
index acc685926c5..3cd4c3f9acc 100644
--- a/chromium/components/segmentation_platform/internal/constants.h
+++ b/chromium/components/segmentation_platform/internal/constants.h
@@ -13,6 +13,9 @@ extern const char kSegmentationResultPref[];
// The path to the pref storing when UKM are allowed recently.
extern const char kSegmentationUkmMostRecentAllowedTimeKey[];
+// Last metrics collection time for the segmentation platform.
+extern const char kSegmentationLastCollectionTimePref[];
+
extern const char kSegmentationPlatformRefreshResultsSwitch[];
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.cc b/chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.cc
index a4e7b8fbbdb..e55e9fc3639 100644
--- a/chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.cc
+++ b/chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.cc
@@ -14,4 +14,6 @@ void DummyTrainingDataCollector::OnModelMetadataUpdated() {}
void DummyTrainingDataCollector::OnServiceInitialized() {}
+void DummyTrainingDataCollector::ReportCollectedContinuousTrainingData() {}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.h b/chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.h
index 5acdeff02c1..b0b4a48c151 100644
--- a/chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.h
+++ b/chromium/components/segmentation_platform/internal/data_collection/dummy_training_data_collector.h
@@ -19,6 +19,7 @@ class DummyTrainingDataCollector : public TrainingDataCollector {
// TrainingDataCollector implementation.
void OnModelMetadataUpdated() override;
void OnServiceInitialized() override;
+ void ReportCollectedContinuousTrainingData() override;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector.cc b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector.cc
index 46f207e2ad0..9223fc20504 100644
--- a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector.cc
+++ b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector.cc
@@ -16,15 +16,17 @@ namespace segmentation_platform {
// static
std::unique_ptr<TrainingDataCollector> TrainingDataCollector::Create(
SegmentInfoDatabase* segment_info_database,
- FeatureListQueryProcessor* processor,
+ processing::FeatureListQueryProcessor* processor,
HistogramSignalHandler* histogram_signal_handler,
SignalStorageConfig* signal_storage_config,
+ std::vector<std::unique_ptr<Config>>* configs,
+ PrefService* profile_prefs,
base::Clock* clock) {
if (base::FeatureList::IsEnabled(
features::kSegmentationStructuredMetricsFeature)) {
return std::make_unique<TrainingDataCollectorImpl>(
segment_info_database, processor, histogram_signal_handler,
- signal_storage_config, clock);
+ signal_storage_config, configs, profile_prefs, clock);
}
return std::make_unique<DummyTrainingDataCollector>();
diff --git a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector.h b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector.h
index 62c076de9af..f996955fd27 100644
--- a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector.h
+++ b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector.h
@@ -9,13 +9,19 @@
#include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
+class PrefService;
+
namespace base {
class Clock;
} // namespace base
namespace segmentation_platform {
+namespace processing {
class FeatureListQueryProcessor;
+}
+
+struct Config;
class HistogramSignalHandler;
class SegmentInfoDatabase;
class SignalStorageConfig;
@@ -27,9 +33,11 @@ class TrainingDataCollector {
public:
static std::unique_ptr<TrainingDataCollector> Create(
SegmentInfoDatabase* segment_info_database,
- FeatureListQueryProcessor* processor,
+ processing::FeatureListQueryProcessor* processor,
HistogramSignalHandler* histogram_signal_handler,
SignalStorageConfig* signal_storage_config,
+ std::vector<std::unique_ptr<Config>>* configs,
+ PrefService* profile_prefs,
base::Clock* clock);
// Called when model metadata is updated. May result in training data
@@ -40,6 +48,11 @@ class TrainingDataCollector {
// to Ukm for |UMAOutput| in |SegmentationModelMetadata|.
virtual void OnServiceInitialized() = 0;
+ // Called by the DataCollectionScheduler to upload all the training data
+ // collected. This will only upload tensors that require continuous
+ // collection.
+ virtual void ReportCollectedContinuousTrainingData() = 0;
+
virtual ~TrainingDataCollector();
// Disallow copy/assign.
diff --git a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
index afc38e67b6d..46666bc6713 100644
--- a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
+++ b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
@@ -8,18 +8,31 @@
#include "base/metrics/metrics_hashes.h"
#include "base/notreached.h"
#include "base/time/clock.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/constants.h"
#include "components/segmentation_platform/internal/database/signal_storage_config.h"
-#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
-#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
+#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
#include "components/segmentation_platform/internal/stats.h"
-
-using optimization_guide::proto::OptimizationTarget;
+#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
namespace segmentation_platform {
namespace {
+using processing::FeatureListQueryProcessor;
+
+// Minimum intervals between collection.
+// TODO(qinmin): make this configurable through finch.
+static int kMinimumReportingIntervalInHours = 24;
+
+// Given the last report time, calculate the next report time.
+base::Time GetNextReportTime(base::Time last_report_time) {
+ // The next report time is determined by |kMinimumReportingIntervalInHours|
+ // hours after last report.
+ return last_report_time + base::Hours(kMinimumReportingIntervalInHours);
+}
// Parse outputs into a map of metric hash of the uma output and its index in
// the output list.
@@ -40,19 +53,38 @@ std::map<uint64_t, int> ParseUmaOutputs(
return hash_index_map;
}
+// Find the segmentation key from the configs that contains the segment ID.
+std::string GetSegmentationKey(std::vector<std::unique_ptr<Config>>* configs,
+ SegmentId segment_id) {
+ if (!configs)
+ return std::string();
+
+ for (const auto& config : *configs) {
+ if (std::find(config->segment_ids.begin(), config->segment_ids.end(),
+ segment_id) != config->segment_ids.end()) {
+ return config->segmentation_key;
+ }
+ }
+ return std::string();
+}
+
} // namespace
TrainingDataCollectorImpl::TrainingDataCollectorImpl(
SegmentInfoDatabase* segment_info_database,
- FeatureListQueryProcessor* processor,
+ processing::FeatureListQueryProcessor* processor,
HistogramSignalHandler* histogram_signal_handler,
SignalStorageConfig* signal_storage_config,
+ std::vector<std::unique_ptr<Config>>* configs,
+ PrefService* profile_prefs,
base::Clock* clock)
: segment_info_database_(segment_info_database),
feature_list_query_processor_(processor),
histogram_signal_handler_(histogram_signal_handler),
signal_storage_config_(signal_storage_config),
- clock_(clock) {}
+ configs_(configs),
+ clock_(clock),
+ result_prefs_(std::make_unique<SegmentationResultPrefs>(profile_prefs)) {}
TrainingDataCollectorImpl::~TrainingDataCollectorImpl() {
histogram_signal_handler_->RemoveObserver(this);
@@ -63,9 +95,11 @@ void TrainingDataCollectorImpl::OnModelMetadataUpdated() {
}
void TrainingDataCollectorImpl::OnServiceInitialized() {
- segment_info_database_->GetAllSegmentInfo(
- base::BindOnce(&TrainingDataCollectorImpl::OnGetSegmentsInfoList,
- weak_ptr_factory_.GetWeakPtr()));
+ if (!SegmentationUkmHelper::GetInstance()->allowed_segment_ids().empty()) {
+ segment_info_database_->GetAllSegmentInfo(
+ base::BindOnce(&TrainingDataCollectorImpl::OnGetSegmentsInfoList,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
}
void TrainingDataCollectorImpl::OnGetSegmentsInfoList(
@@ -73,7 +107,14 @@ void TrainingDataCollectorImpl::OnGetSegmentsInfoList(
histogram_signal_handler_->AddObserver(this);
DCHECK(segments);
+ const base::flat_set<SegmentId>& allowed_ids =
+ SegmentationUkmHelper::GetInstance()->allowed_segment_ids();
for (const auto& segment : *segments) {
+ // Skip the segment if it is not in allowed list.
+ if (!allowed_ids.contains(static_cast<int>(segment.first))) {
+ continue;
+ }
+
const proto::SegmentInfo& segment_info = segment.second;
// Validate segment info.
auto validation_result = metadata_utils::ValidateSegmentInfo(segment_info);
@@ -95,12 +136,16 @@ void TrainingDataCollectorImpl::OnGetSegmentsInfoList(
const auto& output =
segment_info.model_metadata().training_outputs().outputs(
hash_index.second);
- // Ignore continuous collection UMA.
- if (output.uma_output().has_duration())
+ // If tensor length is 0, the output is for immediate collection.
+ if (output.uma_output().uma_feature().tensor_length() != 0) {
+ continuous_collection_segments_.insert(segment.first);
continue;
+ }
immediate_collection_histograms_[hash_index.first].emplace(segment.first);
}
}
+
+ ReportCollectedContinuousTrainingData();
}
void TrainingDataCollectorImpl::OnHistogramSignalUpdated(
@@ -111,49 +156,67 @@ void TrainingDataCollectorImpl::OnHistogramSignalUpdated(
// Report training data for all models that are interested in
// |histogram_name| as output.
if (it != immediate_collection_histograms_.end()) {
- std::vector<OptimizationTarget> optimization_targets(it->second.begin(),
- it->second.end());
+ std::vector<SegmentId> segment_ids(it->second.begin(), it->second.end());
+ auto param = absl::make_optional<ImmediaCollectionParam>();
+ param->output_metric_hash = hash;
+ param->output_value = static_cast<float>(sample);
segment_info_database_->GetSegmentInfoForSegments(
- optimization_targets,
+ segment_ids,
base::BindOnce(&TrainingDataCollectorImpl::ReportForSegmentsInfoList,
- weak_ptr_factory_.GetWeakPtr(), hash, sample));
+ weak_ptr_factory_.GetWeakPtr(), std::move(param)));
}
}
void TrainingDataCollectorImpl::ReportForSegmentsInfoList(
- uint64_t output_metric_hash,
- base::HistogramBase::Sample output_metric_sample,
+ const absl::optional<ImmediaCollectionParam>& param,
std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segments) {
DCHECK(segments);
+ // Make a copy of the input param so it can be modified later.
+ absl::optional<ImmediaCollectionParam> immediate_collection_param = param;
+ bool include_outputs = !param.has_value();
for (const auto& segment : *segments) {
RecordTrainingDataCollectionEvent(
segment.first,
- stats::TrainingDataCollectionEvent::kImmediateCollectionStart);
+ immediate_collection_param.has_value()
+ ? stats::TrainingDataCollectionEvent::kImmediateCollectionStart
+ : stats::TrainingDataCollectionEvent::kContinousCollectionStart);
+
const proto::SegmentInfo& segment_info = segment.second;
// Figure out the output index.
- auto hash_index_map = ParseUmaOutputs(segment_info.model_metadata());
- if (hash_index_map.find(output_metric_hash) == hash_index_map.end())
- continue;
+ if (immediate_collection_param.has_value()) {
+ auto hash_index_map = ParseUmaOutputs(segment_info.model_metadata());
+ if (hash_index_map.find(immediate_collection_param->output_metric_hash) ==
+ hash_index_map.end()) {
+ continue;
+ }
+ immediate_collection_param->output_index =
+ hash_index_map[immediate_collection_param->output_metric_hash];
+ }
- if (!CanReportImmediateTrainingData(segment.second))
+ // For non-immediate collections, we need to validate all output
+ // tensors are allowed by UKM policies.
+ if (!CanReportTrainingData(segment.second, include_outputs)) {
continue;
+ }
// Generate training data input.
// TODO(ssid): Validate immediate output is not included in the input
// features and update the comment in model_metadata.proto.
feature_list_query_processor_->ProcessFeatureList(
- segment_info.model_metadata(), segment_info.segment_id(), clock_->Now(),
- base::BindOnce(&TrainingDataCollectorImpl::OnGetInputTensor,
+ segment_info.model_metadata(), /*input_context=*/nullptr,
+ segment_info.segment_id(), clock_->Now(),
+ include_outputs
+ ? FeatureListQueryProcessor::ProcessOption::kInputsAndOutputs
+ : FeatureListQueryProcessor::ProcessOption::kInputsOnly,
+ base::BindOnce(&TrainingDataCollectorImpl::OnGetTrainingTensors,
weak_ptr_factory_.GetWeakPtr(),
- static_cast<float>(output_metric_sample),
- hash_index_map[output_metric_hash],
- segment_info.segment_id(),
- segment_info.model_version()));
+ immediate_collection_param, segment_info));
}
}
-bool TrainingDataCollectorImpl::CanReportImmediateTrainingData(
- const proto::SegmentInfo& segment_info) {
+bool TrainingDataCollectorImpl::CanReportTrainingData(
+ const proto::SegmentInfo& segment_info,
+ bool include_output) {
if (!segment_info.has_model_version() ||
!segment_info.has_model_update_time_s() ||
segment_info.model_update_time_s() == 0) {
@@ -163,9 +226,26 @@ bool TrainingDataCollectorImpl::CanReportImmediateTrainingData(
return false;
}
+ const proto::SegmentationModelMetadata& model_metadata =
+ segment_info.model_metadata();
+
+ // If UKM is allowed recently, don't upload the metrics.
+ DCHECK_LE(model_metadata.min_signal_collection_length(),
+ model_metadata.signal_storage_length());
+ base::TimeDelta signal_storage_length =
+ model_metadata.signal_storage_length() *
+ metadata_utils::GetTimeUnit(model_metadata);
+ if (!SegmentationUkmHelper::AllowedToUploadData(signal_storage_length,
+ clock_)) {
+ RecordTrainingDataCollectionEvent(
+ segment_info.segment_id(),
+ stats::TrainingDataCollectionEvent::kPartialDataNotAllowed);
+ return false;
+ }
+
base::TimeDelta min_signal_collection_length =
- segment_info.model_metadata().min_signal_collection_length() *
- metadata_utils::GetTimeUnit(segment_info.model_metadata());
+ model_metadata.min_signal_collection_length() *
+ metadata_utils::GetTimeUnit(model_metadata);
base::Time model_update_time = base::Time::FromDeltaSinceWindowsEpoch(
base::Seconds(segment_info.model_update_time_s()));
@@ -182,7 +262,7 @@ bool TrainingDataCollectorImpl::CanReportImmediateTrainingData(
// Each input must be collected for enough time.
if (!signal_storage_config_->MeetsSignalCollectionRequirement(
- segment_info.model_metadata())) {
+ model_metadata, include_output)) {
RecordTrainingDataCollectionEvent(
segment_info.segment_id(),
stats::TrainingDataCollectionEvent::kNotEnoughCollectionTime);
@@ -192,29 +272,73 @@ bool TrainingDataCollectorImpl::CanReportImmediateTrainingData(
return true;
}
-void TrainingDataCollectorImpl::OnGetInputTensor(
- float output_value,
- int output_index,
- OptimizationTarget segment_id,
- int64_t model_version,
- bool success,
- const std::vector<float>& inputs) {
- if (!success) {
+void TrainingDataCollectorImpl::OnGetTrainingTensors(
+ const absl::optional<ImmediaCollectionParam>& param,
+ const proto::SegmentInfo& segment_info,
+ bool has_error,
+ const std::vector<float>& input_tensors,
+ const std::vector<float>& output_tensors) {
+ if (has_error) {
RecordTrainingDataCollectionEvent(
- segment_id, stats::TrainingDataCollectionEvent::kGetInputTensorsFailed);
+ segment_info.segment_id(),
+ stats::TrainingDataCollectionEvent::kGetInputTensorsFailed);
return;
}
+ // TODO(qinmin): update SegmentationUkmHelper::RecordTrainingData()
+ // and ukm file for description of the prediction result as it is
+ // the segment selection result, rather than model result.
+ std::string segmentation_key =
+ GetSegmentationKey(configs_, segment_info.segment_id());
+
+ std::vector<int> output_indexes;
+ if (param.has_value()) {
+ output_indexes = {param->output_index};
+ } else {
+ for (size_t i = 0; i < output_tensors.size(); ++i) {
+ output_indexes.emplace_back(i);
+ }
+ }
+
auto ukm_source_id = SegmentationUkmHelper::GetInstance()->RecordTrainingData(
- segment_id, model_version, inputs, {output_value}, {output_index});
+ segment_info.segment_id(), segment_info.model_version(), input_tensors,
+ param.has_value() ? std::vector<float>{param->output_value}
+ : output_tensors,
+ output_indexes, segment_info.prediction_result(),
+ result_prefs_->ReadSegmentationResultFromPref(segmentation_key));
if (ukm_source_id == ukm::kInvalidSourceId) {
- VLOG(1) << "Failed to collect training data for segment:" << segment_id;
- RecordTrainingDataCollectionEvent(
- segment_id, stats::TrainingDataCollectionEvent::kUkmReportingFailed);
- } else {
+ VLOG(1) << "Failed to collect training data for segment:"
+ << segment_info.segment_id();
RecordTrainingDataCollectionEvent(
- segment_id,
- stats::TrainingDataCollectionEvent::kImmediateCollectionSuccess);
+ segment_info.segment_id(),
+ stats::TrainingDataCollectionEvent::kUkmReportingFailed);
+ return;
+ }
+
+ RecordTrainingDataCollectionEvent(
+ segment_info.segment_id(),
+ param.has_value()
+ ? stats::TrainingDataCollectionEvent::kImmediateCollectionSuccess
+ : stats::TrainingDataCollectionEvent::kContinousCollectionSuccess);
+ if (!param.has_value()) {
+ LocalStateHelper::GetInstance().SetPrefTime(
+ kSegmentationLastCollectionTimePref, clock_->Now());
+ }
+}
+
+void TrainingDataCollectorImpl::ReportCollectedContinuousTrainingData() {
+ if (continuous_collection_segments_.empty())
+ return;
+
+ base::Time last_collection_time = LocalStateHelper::GetInstance().GetPrefTime(
+ kSegmentationLastCollectionTimePref);
+ base::Time next_collection_time = GetNextReportTime(last_collection_time);
+ if (clock_->Now() >= next_collection_time) {
+ segment_info_database_->GetSegmentInfoForSegments(
+ std::vector<SegmentId>(continuous_collection_segments_.begin(),
+ continuous_collection_segments_.end()),
+ base::BindOnce(&TrainingDataCollectorImpl::ReportForSegmentsInfoList,
+ weak_ptr_factory_.GetWeakPtr(), absl::nullopt));
}
}
diff --git a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
index 14a667b7847..d18c0d6f96b 100644
--- a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
+++ b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
@@ -5,69 +5,95 @@
#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATA_COLLECTION_TRAINING_DATA_COLLECTOR_IMPL_H_
#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATA_COLLECTION_TRAINING_DATA_COLLECTOR_IMPL_H_
-#include "components/segmentation_platform/internal/data_collection/training_data_collector.h"
+#include <set>
+#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_base.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/internal/data_collection/training_data_collector.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace segmentation_platform {
+using proto::SegmentId;
+
+struct Config;
+class SegmentationResultPrefs;
// Implementation of TrainingDataCollector.
class TrainingDataCollectorImpl : public TrainingDataCollector,
public HistogramSignalHandler::Observer {
public:
TrainingDataCollectorImpl(SegmentInfoDatabase* segment_info_database,
- FeatureListQueryProcessor* processor,
+ processing::FeatureListQueryProcessor* processor,
HistogramSignalHandler* histogram_signal_handler,
SignalStorageConfig* signal_storage_config,
+ std::vector<std::unique_ptr<Config>>* configs,
+ PrefService* profile_prefs,
base::Clock* clock);
~TrainingDataCollectorImpl() override;
// TrainingDataCollector implementation.
void OnModelMetadataUpdated() override;
void OnServiceInitialized() override;
+ void ReportCollectedContinuousTrainingData() override;
// HistogramSignalHandler::Observer implementation.
void OnHistogramSignalUpdated(const std::string& histogram_name,
base::HistogramBase::Sample sample) override;
private:
+ // Parameters used for reporting immediate output collections.
+ struct ImmediaCollectionParam {
+ uint64_t output_metric_hash; // Hash of the output metric name.
+ int output_index; // Index of the output metric in metadata.
+ float output_value; // Value of the output.
+ };
+
void OnGetSegmentsInfoList(
std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segments);
void ReportForSegmentsInfoList(
- uint64_t output_metric_hash,
- base::HistogramBase::Sample output_metric_sample,
+ const absl::optional<ImmediaCollectionParam>& param,
std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segments);
- void OnGetInputTensor(float output_value,
- int output_index,
- OptimizationTarget segment_id,
- int64_t model_version,
- bool success,
- const std::vector<float>& inputs);
+ void OnGetTrainingTensors(const absl::optional<ImmediaCollectionParam>& param,
+ const proto::SegmentInfo& segment_info,
+ bool has_error,
+ const std::vector<float>& input_tensors,
+ const std::vector<float>& output_tensors);
- bool CanReportImmediateTrainingData(const proto::SegmentInfo& segment_info);
+ // Returns whether training data can be reported through UKM. If
+ // |include_output| is false, only input data will be checked to see if they
+ // meet the collection requirement.
+ bool CanReportTrainingData(const proto::SegmentInfo& segment_info,
+ bool include_output);
raw_ptr<SegmentInfoDatabase> segment_info_database_;
- raw_ptr<FeatureListQueryProcessor> feature_list_query_processor_;
+ raw_ptr<processing::FeatureListQueryProcessor> feature_list_query_processor_;
raw_ptr<HistogramSignalHandler> histogram_signal_handler_;
raw_ptr<SignalStorageConfig> signal_storage_config_;
+ raw_ptr<std::vector<std::unique_ptr<Config>>> configs_;
raw_ptr<base::Clock> clock_;
+ // Helper class to read/write results to the prefs.
+ std::unique_ptr<SegmentationResultPrefs> result_prefs_;
+
// Hash of histograms for immediate training data collection. When any
// histogram hash contained in the map is recorded, a UKM message is reported
// right away.
- base::flat_map<uint64_t,
- base::flat_set<optimization_guide::proto::OptimizationTarget>>
+ base::flat_map<uint64_t, base::flat_set<proto::SegmentId>>
immediate_collection_histograms_;
+ // A list of segment IDs that needs to report metrics continuously.
+ std::set<SegmentId> continuous_collection_segments_;
+
base::WeakPtrFactory<TrainingDataCollectorImpl> weak_ptr_factory_{this};
};
diff --git a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
index 1efe6cba944..23cb7e58409 100644
--- a/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
@@ -11,20 +11,28 @@
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/segmentation_platform/internal/constants.h"
#include "components/segmentation_platform/internal/database/mock_signal_storage_config.h"
#include "components/segmentation_platform/internal/database/test_segment_info_database.h"
-#include "components/segmentation_platform/internal/execution/mock_feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
+#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
#include "components/segmentation_platform/internal/signals/mock_histogram_signal_handler.h"
#include "components/segmentation_platform/public/config.h"
#include "components/segmentation_platform/public/features.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
+#include "components/segmentation_platform/public/segmentation_platform_service.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace segmentation_platform {
+namespace {
+
using ::base::test::RunOnceCallback;
using ::testing::_;
using ::testing::NiceMock;
@@ -33,23 +41,31 @@ using Segmentation_ModelExecution =
::ukm::builders::Segmentation_ModelExecution;
constexpr auto kTestOptimizationTarget0 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
constexpr auto kTestOptimizationTarget1 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
constexpr char kHistogramName0[] = "histogram0";
constexpr char kHistogramName1[] = "histogram1";
+constexpr char kSegmentationKey[] = "test_key";
constexpr int64_t kModelVersion = 123;
constexpr int kSample = 1;
-namespace segmentation_platform {
-namespace {
-
class TrainingDataCollectorImplTest : public ::testing::Test {
public:
TrainingDataCollectorImplTest() = default;
~TrainingDataCollectorImplTest() override = default;
void SetUp() override {
+ SegmentationPlatformService::RegisterLocalStatePrefs(prefs_.registry());
+ SegmentationPlatformService::RegisterProfilePrefs(prefs_.registry());
+ LocalStateHelper::GetInstance().Initialize(&prefs_);
+ LocalStateHelper::GetInstance().SetPrefTime(
+ kSegmentationLastCollectionTimePref, base::Time::Now());
+ // Set UKM allowed 30 days ago
+ LocalStateHelper::GetInstance().SetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey,
+ base::Time::Now() - base::Days(30));
+ clock_.SetNow(base::Time::Now());
test_recorder_.Purge();
// Allow two models to collect training data.
@@ -60,15 +76,30 @@ class TrainingDataCollectorImplTest : public ::testing::Test {
// Setup behavior for |feature_list_processor_|.
std::vector<float> inputs({1.f});
- ON_CALL(feature_list_processor_, ProcessFeatureList(_, _, _, _))
- .WillByDefault(RunOnceCallback<3>(true, inputs));
- ON_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ ON_CALL(feature_list_processor_, ProcessFeatureList(_, _, _, _, _, _))
+ .WillByDefault(RunOnceCallback<5>(false, inputs, std::vector<float>()));
+ ON_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillByDefault(Return(true));
test_segment_info_db_ = std::make_unique<test::TestSegmentInfoDatabase>();
+
+ configs_.emplace_back(std::make_unique<Config>());
+ configs_[0]->segmentation_key = kSegmentationKey;
+ configs_[0]->segment_ids.push_back(
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ configs_[0]->segment_ids.push_back(
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+
+ SegmentationResultPrefs result_prefs(&prefs_);
+ SelectedSegment selected_segment(
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ selected_segment.selection_time = base::Time::Now() - base::Days(1);
+ result_prefs.SaveSegmentationResultToPref(kSegmentationKey,
+ selected_segment);
collector_ = std::make_unique<TrainingDataCollectorImpl>(
test_segment_info_db_.get(), &feature_list_processor_,
- &histogram_signal_handler_, &signal_storage_config_, &clock_);
+ &histogram_signal_handler_, &signal_storage_config_, &configs_, &prefs_,
+ &clock_);
}
protected:
@@ -81,28 +112,34 @@ class TrainingDataCollectorImplTest : public ::testing::Test {
MockSignalStorageConfig* signal_storage_config() {
return &signal_storage_config_;
}
+ processing::MockFeatureListQueryProcessor* feature_list_processor() {
+ return &feature_list_processor_;
+ }
proto::SegmentInfo* CreateSegmentInfo() {
test_segment_db()->AddUserActionFeature(kTestOptimizationTarget0, "action",
1, 1, proto::Aggregation::COUNT);
- // Segment 0 contains 1 immediate collection uma output for for
- // |kHistogramName0|, 1 continuous collection output for for
+ // Segment 0 contains 1 immediate collection uma output for
+ // |kHistogramName0|, 1 uma output collection with delay for
// |kHistogramName1|.
auto* segment_info = CreateSegment(kTestOptimizationTarget0);
AddOutput(segment_info, kHistogramName0);
proto::TrainingOutput* output1 = AddOutput(segment_info, kHistogramName1);
- output1->mutable_uma_output()->set_duration(1u);
+ output1->mutable_uma_output()->mutable_uma_feature()->set_tensor_length(1);
return segment_info;
}
- proto::SegmentInfo* CreateSegment(OptimizationTarget optimization_target) {
- auto* segment_info =
- test_segment_db()->FindOrCreateSegment(optimization_target);
- segment_info->mutable_model_metadata()->set_time_unit(proto::TimeUnit::DAY);
+ proto::SegmentInfo* CreateSegment(SegmentId segment_id) {
+ auto* segment_info = test_segment_db()->FindOrCreateSegment(segment_id);
+ auto* model_metadata = segment_info->mutable_model_metadata();
+ model_metadata->set_time_unit(proto::TimeUnit::DAY);
+ model_metadata->set_signal_storage_length(7);
segment_info->set_model_version(kModelVersion);
auto model_update_time = clock()->Now() - base::Days(365);
segment_info->set_model_update_time_s(
model_update_time.ToDeltaSinceWindowsEpoch().InSeconds());
+ auto* prediction_result = segment_info->mutable_prediction_result();
+ prediction_result->set_result(0.6);
return segment_info;
}
@@ -150,16 +187,28 @@ class TrainingDataCollectorImplTest : public ::testing::Test {
run_loop.Run();
}
+ void WaitForContinousCollection() {
+ base::RunLoop run_loop;
+ test_recorder_.SetOnAddEntryCallback(
+ Segmentation_ModelExecution::kEntryName, run_loop.QuitClosure());
+ collector_->ReportCollectedContinuousTrainingData();
+ run_loop.Run();
+ }
+
+ ukm::TestAutoSetUkmRecorder* test_recorder() { return &test_recorder_; }
+
private:
base::SimpleTestClock clock_;
base::test::TaskEnvironment task_environment_;
base::test::ScopedFeatureList feature_list_;
ukm::TestAutoSetUkmRecorder test_recorder_;
- NiceMock<MockFeatureListQueryProcessor> feature_list_processor_;
+ NiceMock<processing::MockFeatureListQueryProcessor> feature_list_processor_;
NiceMock<MockHistogramSignalHandler> histogram_signal_handler_;
NiceMock<MockSignalStorageConfig> signal_storage_config_;
std::unique_ptr<test::TestSegmentInfoDatabase> test_segment_info_db_;
std::unique_ptr<TrainingDataCollectorImpl> collector_;
+ TestingPrefServiceSimple prefs_;
+ std::vector<std::unique_ptr<Config>> configs_;
};
// No segment info in database. Do nothing.
@@ -212,7 +261,7 @@ TEST_F(TrainingDataCollectorImplTest,
// No UKM report due to minimum data collection time not met.
TEST_F(TrainingDataCollectorImplTest, SignalCollectionRequirementNotMet) {
- EXPECT_CALL(*signal_storage_config(), MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(*signal_storage_config(), MeetsSignalCollectionRequirement(_, _))
.WillOnce(Return(false));
CreateSegmentInfo();
@@ -228,7 +277,6 @@ TEST_F(TrainingDataCollectorImplTest, ModelUpdatedRecently) {
base::TimeDelta min_signal_collection_length =
segment_info->model_metadata().min_signal_collection_length() *
metadata_utils::GetTimeUnit(segment_info->model_metadata());
-
// Set the model update timestamp to be closer to Now().
segment_info->set_model_update_time_s(
(clock()->Now() - min_signal_collection_length + base::Seconds(30))
@@ -241,5 +289,105 @@ TEST_F(TrainingDataCollectorImplTest, ModelUpdatedRecently) {
ExpectUkmCount(0u);
}
+// No report if UKM is enabled recently.
+TEST_F(TrainingDataCollectorImplTest, PartialOutputNotAllowed) {
+ // Simulate that UKM is allowed 300 seconds ago.
+ LocalStateHelper::GetInstance().SetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey,
+ clock()->Now() - base::Seconds(300));
+ CreateSegmentInfo();
+ Init();
+ collector()->OnHistogramSignalUpdated(kHistogramName0, kSample);
+ task_environment()->RunUntilIdle();
+ ExpectUkmCount(0u);
+}
+
+// Tests that continuous collection happens on startup.
+TEST_F(TrainingDataCollectorImplTest, ContinousCollectionOnStartup) {
+ ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
+ .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
+ std::vector<float>{2.f, 3.f}));
+ CreateSegmentInfo();
+ clock()->Advance(base::Hours(24));
+ Init();
+ task_environment()->RunUntilIdle();
+ ExpectUkm({Segmentation_ModelExecution::kOptimizationTargetName,
+ Segmentation_ModelExecution::kModelVersionName,
+ Segmentation_ModelExecution::kInput0Name,
+ Segmentation_ModelExecution::kActualResultName,
+ Segmentation_ModelExecution::kActualResult2Name},
+ {kTestOptimizationTarget0, kModelVersion,
+ SegmentationUkmHelper::FloatToInt64(1.f),
+ SegmentationUkmHelper::FloatToInt64(2.f),
+ SegmentationUkmHelper::FloatToInt64(3.f)});
+}
+
+// Tests that ReportCollectedContinuousTrainingData() works well later if
+// no data is reported on start up.
+TEST_F(TrainingDataCollectorImplTest, ReportCollectedContinuousTrainingData) {
+ ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
+ .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
+ std::vector<float>{2.f, 3.f}));
+ CreateSegmentInfo();
+ Init();
+ clock()->Advance(base::Hours(24));
+ WaitForContinousCollection();
+ ExpectUkm(
+ {Segmentation_ModelExecution::kOptimizationTargetName,
+ Segmentation_ModelExecution::kModelVersionName,
+ Segmentation_ModelExecution::kInput0Name,
+ Segmentation_ModelExecution::kPredictionResultName,
+ Segmentation_ModelExecution::kSelectionResultName,
+ Segmentation_ModelExecution::kOutputDelaySecName,
+ Segmentation_ModelExecution::kActualResultName,
+ Segmentation_ModelExecution::kActualResult2Name},
+ {kTestOptimizationTarget0, kModelVersion,
+ SegmentationUkmHelper::FloatToInt64(1.f),
+ SegmentationUkmHelper::FloatToInt64(0.6f),
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+ base::Days(1).InSeconds(), SegmentationUkmHelper::FloatToInt64(2.f),
+ SegmentationUkmHelper::FloatToInt64(3.f)});
+}
+
+// Tests that after a data collection, another data collection won't happen
+// immediately afterwards.
+TEST_F(TrainingDataCollectorImplTest,
+ NoImmediateDataCollectionAfterLastCollection) {
+ ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
+ .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
+ std::vector<float>{2.f, 3.f}));
+ CreateSegmentInfo();
+ Init();
+ clock()->Advance(base::Hours(24));
+ WaitForContinousCollection();
+ test_recorder()->Purge();
+ ExpectUkmCount(0u);
+
+ // Nothing should be collected if collection just happen.
+ collector()->ReportCollectedContinuousTrainingData();
+ task_environment()->RunUntilIdle();
+ ExpectUkmCount(0u);
+
+ // Collect again after 24 hours and it should work.
+ clock()->Advance(base::Hours(24));
+ WaitForContinousCollection();
+ ExpectUkmCount(1u);
+}
+
+// Tests that if UKM allowed timestamp is not set in local state, data
+// collection won't happen.
+TEST_F(TrainingDataCollectorImplTest, NoDataCollectionIfUkmAllowedPrefNotSet) {
+ ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
+ .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
+ std::vector<float>{2.f, 3.f}));
+ LocalStateHelper::GetInstance().SetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey, base::Time());
+ CreateSegmentInfo();
+ Init();
+ collector()->ReportCollectedContinuousTrainingData();
+ task_environment()->RunUntilIdle();
+ ExpectUkmCount(0u);
+}
+
} // namespace
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/database_maintenance_impl.cc b/chromium/components/segmentation_platform/internal/database/database_maintenance_impl.cc
index c81318c48b9..52b2dfbe743 100644
--- a/chromium/components/segmentation_platform/internal/database/database_maintenance_impl.cc
+++ b/chromium/components/segmentation_platform/internal/database/database_maintenance_impl.cc
@@ -20,11 +20,11 @@
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/trace_event/typed_macros.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
#include "components/segmentation_platform/internal/database/signal_database.h"
#include "components/segmentation_platform/internal/database/signal_storage_config.h"
#include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/internal/stats.h"
#include "components/segmentation_platform/public/config.h"
@@ -90,7 +90,7 @@ struct DatabaseMaintenanceImpl::CleanupState {
};
DatabaseMaintenanceImpl::DatabaseMaintenanceImpl(
- const base::flat_set<OptimizationTarget>& segment_ids,
+ const base::flat_set<SegmentId>& segment_ids,
base::Clock* clock,
SegmentInfoDatabase* segment_info_database,
SignalDatabase* signal_database,
@@ -106,8 +106,7 @@ DatabaseMaintenanceImpl::DatabaseMaintenanceImpl(
DatabaseMaintenanceImpl::~DatabaseMaintenanceImpl() = default;
void DatabaseMaintenanceImpl::ExecuteMaintenanceTasks() {
- std::vector<OptimizationTarget> segment_ids(segment_ids_.begin(),
- segment_ids_.end());
+ std::vector<SegmentId> segment_ids(segment_ids_.begin(), segment_ids_.end());
default_model_manager_->GetAllSegmentInfoFromBothModels(
segment_ids, segment_info_database_,
base::BindOnce(&DatabaseMaintenanceImpl::OnSegmentInfoCallback,
diff --git a/chromium/components/segmentation_platform/internal/database/database_maintenance_impl.h b/chromium/components/segmentation_platform/internal/database/database_maintenance_impl.h
index bb6d2f77562..d8d67db5a95 100644
--- a/chromium/components/segmentation_platform/internal/database/database_maintenance_impl.h
+++ b/chromium/components/segmentation_platform/internal/database/database_maintenance_impl.h
@@ -15,19 +15,19 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/database_maintenance.h"
#include "components/segmentation_platform/internal/execution/default_model_manager.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace base {
class Clock;
class Time;
} // namespace base
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
+using proto::SegmentId;
+
class DefaultModelManager;
class SegmentInfoDatabase;
class SignalDatabase;
@@ -40,13 +40,12 @@ class DatabaseMaintenanceImpl : public DatabaseMaintenance {
using SignalIdentifier = std::pair<uint64_t, proto::SignalType>;
using CleanupItem = std::tuple<uint64_t, proto::SignalType, base::Time>;
- explicit DatabaseMaintenanceImpl(
- const base::flat_set<OptimizationTarget>& segment_ids,
- base::Clock* clock,
- SegmentInfoDatabase* segment_info_database,
- SignalDatabase* signal_database,
- SignalStorageConfig* signal_storage_config,
- DefaultModelManager* default_model_manager);
+ explicit DatabaseMaintenanceImpl(const base::flat_set<SegmentId>& segment_ids,
+ base::Clock* clock,
+ SegmentInfoDatabase* segment_info_database,
+ SignalDatabase* signal_database,
+ SignalStorageConfig* signal_storage_config,
+ DefaultModelManager* default_model_manager);
~DatabaseMaintenanceImpl() override;
// DatabaseMaintenance overrides.
@@ -88,7 +87,7 @@ class DatabaseMaintenanceImpl : public DatabaseMaintenance {
void CompactSamplesDone(base::OnceClosure next_action);
// Input.
- base::flat_set<OptimizationTarget> segment_ids_;
+ base::flat_set<SegmentId> segment_ids_;
raw_ptr<base::Clock> clock_;
// Databases.
diff --git a/chromium/components/segmentation_platform/internal/database/database_maintenance_impl_unittest.cc b/chromium/components/segmentation_platform/internal/database/database_maintenance_impl_unittest.cc
index 8c7bc74da23..ce73d34962d 100644
--- a/chromium/components/segmentation_platform/internal/database/database_maintenance_impl_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/database/database_maintenance_impl_unittest.cc
@@ -15,7 +15,6 @@
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/mock_signal_database.h"
#include "components/segmentation_platform/internal/database/mock_signal_storage_config.h"
#include "components/segmentation_platform/internal/database/signal_storage_config.h"
@@ -24,6 +23,7 @@
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,9 +31,9 @@ using ::base::test::RunOnceCallback;
using ::testing::_;
using ::testing::SetArgReferee;
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
+
+using SegmentId = proto::SegmentId;
using SignalType = proto::SignalType;
using Aggregation = proto::Aggregation;
using SignalIdentifier = std::pair<uint64_t, SignalType>;
@@ -46,7 +46,7 @@ constexpr uint64_t kEarliestCompactionDaysAgo = 60;
std::string kTestSegmentationKey = "some_key";
struct SignalData {
- OptimizationTarget target;
+ SegmentId target;
proto::SignalType signal_type;
std::string name;
uint64_t name_hash;
@@ -64,11 +64,11 @@ struct SignalData {
class TestDefaultModelManager : public DefaultModelManager {
public:
TestDefaultModelManager()
- : DefaultModelManager(nullptr, std::vector<OptimizationTarget>()) {}
+ : DefaultModelManager(nullptr, std::vector<SegmentId>()) {}
~TestDefaultModelManager() override = default;
void GetAllSegmentInfoFromDefaultModel(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback) override {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
@@ -76,7 +76,7 @@ class TestDefaultModelManager : public DefaultModelManager {
}
void GetAllSegmentInfoFromBothModels(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
SegmentInfoDatabase* segment_database,
MultipleSegmentInfoCallback callback) override {
segment_database->GetSegmentInfoForSegments(
@@ -107,9 +107,9 @@ class DatabaseMaintenanceImplTest : public testing::Test {
segment_info_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
signal_database_ = std::make_unique<MockSignalDatabase>();
signal_storage_config_ = std::make_unique<MockSignalStorageConfig>();
- base::flat_set<OptimizationTarget> segment_ids = {
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
+ base::flat_set<SegmentId> segment_ids = {
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
default_model_manager_ = std::make_unique<TestDefaultModelManager>();
database_maintenance_ = std::make_unique<DatabaseMaintenanceImpl>(
segment_ids, &clock_, segment_info_database_.get(),
@@ -181,13 +181,13 @@ std::vector<CleanupItem> GetCleanupItems(std::vector<SignalData> signal_datas) {
TEST_F(DatabaseMaintenanceImplTest, ExecuteMaintenanceTasks) {
std::vector<SignalData> signal_datas = {
- {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
SignalType::HISTOGRAM_VALUE, "Foo", base::HashMetricName("Foo"), 44, 1,
Aggregation::COUNT, clock_.Now() - base::Days(10), true},
- {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+ {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
SignalType::HISTOGRAM_ENUM, "Bar", base::HashMetricName("Bar"), 33, 1,
Aggregation::COUNT, clock_.Now() - base::Days(5), true},
- {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+ {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
SignalType::USER_ACTION, "Failed", base::HashMetricName("Failed"), 22, 1,
Aggregation::COUNT, clock_.Now() - base::Days(1), false},
};
diff --git a/chromium/components/segmentation_platform/internal/database/mock_signal_storage_config.h b/chromium/components/segmentation_platform/internal/database/mock_signal_storage_config.h
index fbada8cd500..5ab55b1078b 100644
--- a/chromium/components/segmentation_platform/internal/database/mock_signal_storage_config.h
+++ b/chromium/components/segmentation_platform/internal/database/mock_signal_storage_config.h
@@ -30,7 +30,7 @@ class MockSignalStorageConfig : public SignalStorageConfig {
MOCK_METHOD(bool,
MeetsSignalCollectionRequirement,
- (const proto::SegmentationModelMetadata& model_metadata),
+ (const proto::SegmentationModelMetadata& model_metadata, bool),
(override));
MOCK_METHOD(void,
diff --git a/chromium/components/segmentation_platform/internal/database/mock_ukm_database.cc b/chromium/components/segmentation_platform/internal/database/mock_ukm_database.cc
index f521fe9f880..381e912faee 100644
--- a/chromium/components/segmentation_platform/internal/database/mock_ukm_database.cc
+++ b/chromium/components/segmentation_platform/internal/database/mock_ukm_database.cc
@@ -8,7 +8,7 @@
namespace segmentation_platform {
-MockUkmDatabase::MockUkmDatabase() : UkmDatabase(base::FilePath()) {}
+MockUkmDatabase::MockUkmDatabase() = default;
MockUkmDatabase::~MockUkmDatabase() = default;
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/mock_ukm_database.h b/chromium/components/segmentation_platform/internal/database/mock_ukm_database.h
index dd02819feee..976a6bfefdb 100644
--- a/chromium/components/segmentation_platform/internal/database/mock_ukm_database.h
+++ b/chromium/components/segmentation_platform/internal/database/mock_ukm_database.h
@@ -16,6 +16,8 @@ class MockUkmDatabase : public UkmDatabase {
MockUkmDatabase();
~MockUkmDatabase() override;
+ MOCK_METHOD1(InitDatabase, void(SuccessCallback callback));
+
MOCK_METHOD1(StoreUkmEntry, void(ukm::mojom::UkmEntryPtr ukm_entry));
MOCK_METHOD3(UpdateUrlForUkmSource,
@@ -25,7 +27,12 @@ class MockUkmDatabase : public UkmDatabase {
MOCK_METHOD1(OnUrlValidated, void(const GURL& url));
- MOCK_METHOD1(RemoveUrls, void(const std::vector<GURL>& urls));
+ MOCK_METHOD2(RemoveUrls, void(const std::vector<GURL>& urls, bool));
+
+ MOCK_METHOD2(RunReadonlyQueries,
+ void(QueryList&& queries, QueryCallback callback));
+
+ MOCK_METHOD1(DeleteEntriesOlderThan, void(base::Time time));
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/segment_info_database.cc b/chromium/components/segmentation_platform/internal/database/segment_info_database.cc
index 12a3f46a83d..3cad35ac832 100644
--- a/chromium/components/segmentation_platform/internal/database/segment_info_database.cc
+++ b/chromium/components/segmentation_platform/internal/database/segment_info_database.cc
@@ -12,7 +12,7 @@ namespace segmentation_platform {
namespace {
-std::string ToString(OptimizationTarget segment_id) {
+std::string ToString(SegmentId segment_id) {
return base::NumberToString(static_cast<int>(segment_id));
}
@@ -53,10 +53,10 @@ void SegmentInfoDatabase::OnMultipleSegmentInfoLoaded(
}
void SegmentInfoDatabase::GetSegmentInfoForSegments(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback) {
std::vector<std::string> keys;
- for (OptimizationTarget target : segment_ids)
+ for (SegmentId target : segment_ids)
keys.emplace_back(ToString(target));
database_->LoadEntriesWithFilter(
@@ -69,7 +69,7 @@ void SegmentInfoDatabase::GetSegmentInfoForSegments(
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
-void SegmentInfoDatabase::GetSegmentInfo(OptimizationTarget segment_id,
+void SegmentInfoDatabase::GetSegmentInfo(SegmentId segment_id,
SegmentInfoCallback callback) {
database_->GetEntry(
ToString(segment_id),
@@ -86,7 +86,7 @@ void SegmentInfoDatabase::OnGetSegmentInfo(
}
void SegmentInfoDatabase::UpdateSegment(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
absl::optional<proto::SegmentInfo> segment_info,
SuccessCallback callback) {
auto entries_to_save = std::make_unique<
@@ -104,7 +104,7 @@ void SegmentInfoDatabase::UpdateSegment(
}
void SegmentInfoDatabase::SaveSegmentResult(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
absl::optional<proto::PredictionResult> result,
SuccessCallback callback) {
GetSegmentInfo(
diff --git a/chromium/components/segmentation_platform/internal/database/segment_info_database.h b/chromium/components/segmentation_platform/internal/database/segment_info_database.h
index 2c4ab127714..41dc38a6fa3 100644
--- a/chromium/components/segmentation_platform/internal/database/segment_info_database.h
+++ b/chromium/components/segmentation_platform/internal/database/segment_info_database.h
@@ -10,15 +10,15 @@
#include "base/callback.h"
#include "components/leveldb_proto/public/proto_database.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
+using proto::SegmentId;
+
namespace proto {
class SegmentInfo;
class PredictionResult;
@@ -29,8 +29,7 @@ class PredictionResult;
class SegmentInfoDatabase {
public:
using SuccessCallback = base::OnceCallback<void(bool)>;
- using SegmentInfoList =
- std::vector<std::pair<OptimizationTarget, proto::SegmentInfo>>;
+ using SegmentInfoList = std::vector<std::pair<SegmentId, proto::SegmentInfo>>;
using MultipleSegmentInfoCallback =
base::OnceCallback<void(std::unique_ptr<SegmentInfoList>)>;
using SegmentInfoCallback =
@@ -52,24 +51,24 @@ class SegmentInfoDatabase {
// Called to get metadata for a given list of segments.
virtual void GetSegmentInfoForSegments(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback);
// Called to get the metadata for a given segment.
- virtual void GetSegmentInfo(OptimizationTarget segment_id,
+ virtual void GetSegmentInfo(SegmentId segment_id,
SegmentInfoCallback callback);
// Called to save or update metadata for a segment. The previous data is
// overwritten. If |segment_info| is empty, the segment will be deleted.
// TODO(shaktisahu): How does the client know if a segment is to be deleted?
- virtual void UpdateSegment(OptimizationTarget segment_id,
+ virtual void UpdateSegment(SegmentId segment_id,
absl::optional<proto::SegmentInfo> segment_info,
SuccessCallback callback);
// Called to write the model execution results for a given segment. It will
// first read the currently stored result, and then overwrite it with
// |result|. If |result| is null, the existing result will be deleted.
- virtual void SaveSegmentResult(OptimizationTarget segment_id,
+ virtual void SaveSegmentResult(SegmentId segment_id,
absl::optional<proto::PredictionResult> result,
SuccessCallback callback);
diff --git a/chromium/components/segmentation_platform/internal/database/segment_info_database_unittest.cc b/chromium/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
index 9d49234546f..1dbe9e16a58 100644
--- a/chromium/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
@@ -18,16 +18,15 @@ namespace segmentation_platform {
namespace {
// Test Ids.
-const OptimizationTarget kSegmentId =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
-const OptimizationTarget kSegmentId2 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+const SegmentId kSegmentId =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+const SegmentId kSegmentId2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
-std::string ToString(OptimizationTarget segment_id) {
+std::string ToString(SegmentId segment_id) {
return base::NumberToString(static_cast<int>(segment_id));
}
-proto::SegmentInfo CreateSegment(OptimizationTarget segment_id,
+proto::SegmentInfo CreateSegment(SegmentId segment_id,
absl::optional<int> result = absl::nullopt) {
proto::SegmentInfo info;
info.set_segment_id(segment_id);
@@ -71,14 +70,13 @@ class SegmentInfoDatabaseTest : public testing::Test {
segment_db_.reset();
}
- void VerifyDb(std::vector<OptimizationTarget> expected_ids) {
+ void VerifyDb(std::vector<SegmentId> expected_ids) {
EXPECT_EQ(expected_ids.size(), db_entries_.size());
for (auto segment_id : expected_ids)
EXPECT_TRUE(db_entries_.find(ToString(segment_id)) != db_entries_.end());
}
- void WriteResult(OptimizationTarget segment_id,
- absl::optional<float> result) {
+ void WriteResult(SegmentId segment_id, absl::optional<float> result) {
proto::PredictionResult prediction_result;
if (result.has_value())
prediction_result.set_result(result.value());
@@ -92,8 +90,7 @@ class SegmentInfoDatabaseTest : public testing::Test {
db_->UpdateCallback(true);
}
- void VerifyResult(OptimizationTarget segment_id,
- absl::optional<float> result) {
+ void VerifyResult(SegmentId segment_id, absl::optional<float> result) {
segment_db_->GetSegmentInfo(
segment_id, base::BindOnce(&SegmentInfoDatabaseTest::OnGetSegment,
base::Unretained(this)));
diff --git a/chromium/components/segmentation_platform/internal/database/signal_database_impl.cc b/chromium/components/segmentation_platform/internal/database/signal_database_impl.cc
index 2d015187982..4e8f496595c 100644
--- a/chromium/components/segmentation_platform/internal/database/signal_database_impl.cc
+++ b/chromium/components/segmentation_platform/internal/database/signal_database_impl.cc
@@ -18,9 +18,9 @@
#include "base/time/time.h"
#include "base/trace_event/typed_macros.h"
#include "components/leveldb_proto/public/proto_database.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/signal_database.h"
#include "components/segmentation_platform/internal/database/signal_key.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/signal.pb.h"
#include "components/segmentation_platform/internal/stats.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chromium/components/segmentation_platform/internal/database/signal_key_internal.cc b/chromium/components/segmentation_platform/internal/database/signal_key_internal.cc
index 01341b69593..d2c84d01f10 100644
--- a/chromium/components/segmentation_platform/internal/database/signal_key_internal.cc
+++ b/chromium/components/segmentation_platform/internal/database/signal_key_internal.cc
@@ -12,6 +12,7 @@
#include "base/big_endian.h"
#include "base/check.h"
+#include "base/check_op.h"
#include "base/logging.h"
namespace segmentation_platform {
diff --git a/chromium/components/segmentation_platform/internal/database/signal_storage_config.cc b/chromium/components/segmentation_platform/internal/database/signal_storage_config.cc
index a3c5efaf2a1..e35d2bbd5bd 100644
--- a/chromium/components/segmentation_platform/internal/database/signal_storage_config.cc
+++ b/chromium/components/segmentation_platform/internal/database/signal_storage_config.cc
@@ -6,7 +6,7 @@
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
namespace segmentation_platform {
namespace {
@@ -63,11 +63,13 @@ void SignalStorageConfig::OnDataLoaded(
proto::SignalStorageConfig* SignalStorageConfig::FindSignal(
uint64_t signal_hash,
+ uint64_t event_hash,
proto::SignalType signal_type) {
// TODO(shaktisahu): May be have an internal map of signals.
for (int i = 0; i < config_.signals().size(); ++i) {
auto* signal_config = config_.mutable_signals(i);
if (signal_config->name_hash() == signal_hash &&
+ signal_config->event_hash() == event_hash &&
signal_config->signal_type() == signal_type) {
return signal_config;
}
@@ -75,16 +77,61 @@ proto::SignalStorageConfig* SignalStorageConfig::FindSignal(
return nullptr;
}
+bool SignalStorageConfig::UpdateConfigForSignal(int signal_storage_length,
+ uint64_t signal_hash,
+ uint64_t event_hash,
+ proto::SignalType signal_type) {
+ proto::SignalStorageConfig* config =
+ FindSignal(signal_hash, event_hash, signal_type);
+ if (config) {
+ if (config->storage_length_s() < signal_storage_length) {
+ // We found a model that has a longer storage length requirement. Update
+ // it to DB.
+ config->set_storage_length_s(signal_storage_length);
+ return true;
+ }
+ } else {
+ // This is the first time we have encountered this signal. Just create an
+ // entry in the DB, and set collection start time.
+ proto::SignalStorageConfig* signal_config = config_.add_signals();
+ signal_config->set_name_hash(signal_hash);
+ if (signal_type == proto::SignalType::UKM_EVENT)
+ signal_config->set_event_hash(event_hash);
+ signal_config->set_signal_type(signal_type);
+ signal_config->set_storage_length_s(signal_storage_length);
+ signal_config->set_collection_start_time_s(
+ clock_->Now().ToDeltaSinceWindowsEpoch().InSeconds());
+ return true;
+ }
+ return false;
+}
+
+bool SignalStorageConfig::MeetsSignalCollectionRequirementForSignal(
+ base::TimeDelta min_signal_collection_length,
+ uint64_t signal_hash,
+ uint64_t event_hash,
+ proto::SignalType signal_type) {
+ const proto::SignalStorageConfig* config =
+ FindSignal(signal_hash, event_hash, signal_type);
+ if (!config || config->collection_start_time_s() == 0)
+ return false;
+
+ base::Time collection_start_time = base::Time::FromDeltaSinceWindowsEpoch(
+ base::Seconds(config->collection_start_time_s()));
+ return clock_->Now() - collection_start_time >= min_signal_collection_length;
+}
+
bool SignalStorageConfig::MeetsSignalCollectionRequirement(
- const proto::SegmentationModelMetadata& model_metadata) {
+ const proto::SegmentationModelMetadata& model_metadata,
+ bool include_outputs) {
base::TimeDelta min_signal_collection_length =
model_metadata.min_signal_collection_length() *
metadata_utils::GetTimeUnit(model_metadata);
// Loop through all the signals specified in the model, and check if they have
// been collected long enough.
- auto features = metadata_utils::GetAllUmaFeatures(model_metadata,
- /*include_outputs=*/false);
+ auto features =
+ metadata_utils::GetAllUmaFeatures(model_metadata, include_outputs);
for (auto const& feature : features) {
// Skip the signals that has bucket_count set to 0. These ones are only for
// collection purposes and hence don't get used in model evaluation.
@@ -96,15 +143,35 @@ bool SignalStorageConfig::MeetsSignalCollectionRequirement(
continue;
}
- proto::SignalStorageConfig* config =
- FindSignal(feature.name_hash(), feature.type());
- if (!config || config->collection_start_time_s() == 0)
+ if (!MeetsSignalCollectionRequirementForSignal(min_signal_collection_length,
+ feature.name_hash(), 0,
+ feature.type())) {
return false;
+ };
+ }
- base::Time collection_start_time = base::Time::FromDeltaSinceWindowsEpoch(
- base::Seconds(config->collection_start_time_s()));
- if (clock_->Now() - collection_start_time < min_signal_collection_length)
- return false;
+ // Loop through sql features.
+ for (auto const& feature : model_metadata.input_features()) {
+ if (!feature.has_sql_feature())
+ continue;
+
+ if (metadata_utils::ValidateMetadataSqlFeature(feature.sql_feature()) !=
+ metadata_utils::ValidationResult::kValidationSuccess) {
+ continue;
+ }
+
+ const proto::SignalFilterConfig& sql_config =
+ feature.sql_feature().signal_filter();
+
+ for (auto const& event : sql_config.ukm_events()) {
+ for (auto const& metric_hash : event.metric_hash_filter()) {
+ if (!MeetsSignalCollectionRequirementForSignal(
+ min_signal_collection_length, metric_hash, event.event_hash(),
+ proto::SignalType::UKM_EVENT)) {
+ return false;
+ };
+ }
+ }
}
return true;
@@ -125,26 +192,33 @@ void SignalStorageConfig::OnSignalCollectionStarted(
metadata_utils::ValidationResult::kValidationSuccess) {
continue;
}
+ if (UpdateConfigForSignal(signal_storage_length, feature.name_hash(), 0,
+ feature.type())) {
+ is_dirty = true;
+ }
+ }
- proto::SignalStorageConfig* config =
- FindSignal(feature.name_hash(), feature.type());
- if (config) {
- if (config->storage_length_s() < signal_storage_length) {
- // We found a model that has a longer storage length requirement. Update
- // it to DB.
- config->set_storage_length_s(signal_storage_length);
- is_dirty = true;
+ // Add signals for sql features.
+ for (auto const& feature : model_metadata.input_features()) {
+ if (!feature.has_sql_feature())
+ continue;
+
+ if (metadata_utils::ValidateMetadataSqlFeature(feature.sql_feature()) !=
+ metadata_utils::ValidationResult::kValidationSuccess) {
+ continue;
+ }
+
+ const proto::SignalFilterConfig& sql_config =
+ feature.sql_feature().signal_filter();
+
+ for (auto const& event : sql_config.ukm_events()) {
+ for (auto const& metric_hash : event.metric_hash_filter()) {
+ if (UpdateConfigForSignal(signal_storage_length, metric_hash,
+ event.event_hash(),
+ proto::SignalType::UKM_EVENT)) {
+ is_dirty = true;
+ }
}
- } else {
- // This is the first time we have encountered this signal. Just create an
- // entry in the DB, and set collection start time.
- proto::SignalStorageConfig* signal_config = config_.add_signals();
- signal_config->set_name_hash(feature.name_hash());
- signal_config->set_signal_type(feature.type());
- signal_config->set_storage_length_s(signal_storage_length);
- signal_config->set_collection_start_time_s(
- clock_->Now().ToDeltaSinceWindowsEpoch().InSeconds());
- is_dirty = true;
}
}
@@ -157,6 +231,7 @@ void SignalStorageConfig::GetSignalsForCleanup(
std::vector<std::tuple<uint64_t, proto::SignalType, base::Time>>& result)
const {
// Collect the signals that have longer than required data.
+ // TODO(haileywang): Handle UKM signals.
for (int i = 0; i < config_.signals_size(); ++i) {
const auto& signal_config = config_.signals(i);
base::Time collection_start_time = base::Time::FromDeltaSinceWindowsEpoch(
@@ -201,7 +276,7 @@ void SignalStorageConfig::UpdateSignalsForCleanup(
base::Time timestamp = std::get<2>(tuple);
proto::SignalStorageConfig* signal_config =
- FindSignal(name_hash, signal_type);
+ FindSignal(name_hash, 0, signal_type);
if (!signal_config)
continue;
diff --git a/chromium/components/segmentation_platform/internal/database/signal_storage_config.h b/chromium/components/segmentation_platform/internal/database/signal_storage_config.h
index 45ee1c2f4bc..8d000015c77 100644
--- a/chromium/components/segmentation_platform/internal/database/signal_storage_config.h
+++ b/chromium/components/segmentation_platform/internal/database/signal_storage_config.h
@@ -46,7 +46,8 @@ class SignalStorageConfig {
// evaluation. The model should be skipped if it hasn't been captured long
// enough.
virtual bool MeetsSignalCollectionRequirement(
- const proto::SegmentationModelMetadata& model_metadata);
+ const proto::SegmentationModelMetadata& model_metadata,
+ bool include_output = false);
// Called whenever we find a model. Noop for existing models. Loops through
// all the signals and updates their storage requirements. Also sets their
@@ -84,8 +85,20 @@ class SignalStorageConfig {
std::unique_ptr<std::vector<proto::SignalStorageConfigs>> entries);
proto::SignalStorageConfig* FindSignal(uint64_t signal_hash,
+ uint64_t event_hash,
proto::SignalType signal_type);
+ bool UpdateConfigForSignal(int signal_storage_length,
+ uint64_t signal_hash,
+ uint64_t event_hash,
+ proto::SignalType signal_type);
+
+ bool MeetsSignalCollectionRequirementForSignal(
+ base::TimeDelta min_signal_collection_length,
+ uint64_t signal_hash,
+ uint64_t event_hash,
+ proto::SignalType signal_type);
+
// Helper method to flush the cached data to the DB. Called whenever the cache
// is dirty.
void WriteToDB();
diff --git a/chromium/components/segmentation_platform/internal/database/signal_storage_config_unittest.cc b/chromium/components/segmentation_platform/internal/database/signal_storage_config_unittest.cc
index 8c2015e3e9a..40a5f283bb1 100644
--- a/chromium/components/segmentation_platform/internal/database/signal_storage_config_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/database/signal_storage_config_unittest.cc
@@ -13,6 +13,8 @@
#include "components/leveldb_proto/public/proto_database.h"
#include "components/leveldb_proto/testing/fake_db.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/internal/proto/signal_storage_config.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -59,7 +61,9 @@ class SignalStorageConfigTest : public testing::Test {
TEST_F(SignalStorageConfigTest,
CheckMeetsSignalCollectionRequirementWithMultipleModels) {
- // Start with empty DB.
+ // TODO(haileywang): Separate UMA and UKM features tests. Make smaller helper
+ // functions that can be used by other tests to share repeated code. Start
+ // with empty DB.
SetUpDB();
base::MockCallback<SignalStorageConfig::SuccessCallback> init_callback;
EXPECT_CALL(init_callback, Run(true)).Times(1);
@@ -81,20 +85,41 @@ TEST_F(SignalStorageConfigTest,
metadata2.set_min_signal_collection_length(4);
// Add a user action feature to the both models.
- proto::UMAFeature* feature = metadata.add_features();
+ proto::InputFeature* input_feature = metadata.add_input_features();
+ proto::UMAFeature* feature = input_feature->mutable_uma_feature();
uint64_t name_hash = base::HashMetricName("some user action");
feature->set_type(proto::SignalType::USER_ACTION);
feature->set_name_hash(name_hash);
feature->set_bucket_count(1);
feature->set_tensor_length(1);
feature->set_aggregation(proto::Aggregation::COUNT);
- proto::UMAFeature* feature2 = metadata2.add_features();
+ proto::InputFeature* input_feature2 = metadata2.add_input_features();
+ proto::UMAFeature* feature2 = input_feature2->mutable_uma_feature();
feature2->set_type(proto::SignalType::USER_ACTION);
feature2->set_name_hash(name_hash);
feature2->set_bucket_count(1);
feature2->set_tensor_length(1);
feature2->set_aggregation(proto::Aggregation::COUNT);
+ // Add a sql feature to both models.
+ proto::InputFeature* input_feature3 = metadata.add_input_features();
+ proto::SqlFeature* feature3 = input_feature3->mutable_sql_feature();
+ proto::SignalFilterConfig::UkmEvent* ukm_event =
+ feature3->mutable_signal_filter()->add_ukm_events();
+ uint64_t event_hash = base::HashMetricName("some event");
+ uint64_t metric_hash = base::HashMetricName("some metric");
+ feature3->set_sql("sql");
+ ukm_event->set_event_hash(event_hash);
+ ukm_event->add_metric_hash_filter(metric_hash);
+
+ proto::InputFeature* input_feature4 = metadata2.add_input_features();
+ proto::SqlFeature* feature4 = input_feature4->mutable_sql_feature();
+ proto::SignalFilterConfig::UkmEvent* ukm_event2 =
+ feature4->mutable_signal_filter()->add_ukm_events();
+ feature4->set_sql("sql");
+ ukm_event2->set_event_hash(event_hash);
+ ukm_event2->add_metric_hash_filter(metric_hash);
+
// The DB should be empty before the model is added.
EXPECT_EQ(0u, db_entries_.size());
@@ -107,7 +132,7 @@ TEST_F(SignalStorageConfigTest,
// Verify that the DB has now a top level entry.
EXPECT_EQ(1u, db_entries_.size());
const auto& config = db_entries_[kDatabaseKey];
- EXPECT_EQ(1, config.signals_size());
+ EXPECT_EQ(2, config.signals_size());
// Verify that DB has a signal entry with correct storage and collection start
// time.
@@ -117,13 +142,20 @@ TEST_F(SignalStorageConfigTest,
EXPECT_EQ(base::Days(2).InSeconds(), signal_config.storage_length_s());
EXPECT_NE(0, signal_config.collection_start_time_s());
+ proto::SignalStorageConfig signal_config2 = config.signals(1);
+ EXPECT_EQ(metric_hash, signal_config2.name_hash());
+ EXPECT_EQ(event_hash, signal_config2.event_hash());
+ EXPECT_EQ(proto::SignalType::UKM_EVENT, signal_config2.signal_type());
+ EXPECT_EQ(base::Days(2).InSeconds(), signal_config2.storage_length_s());
+ EXPECT_NE(0, signal_config2.collection_start_time_s());
+
// Add the second model. It should do a overwrite of previous value.
signal_storage_config_->OnSignalCollectionStarted(metadata2);
db_->UpdateCallback(true);
// Verify DB size.
EXPECT_EQ(1u, db_entries_.size());
- EXPECT_EQ(1, config.signals_size());
+ EXPECT_EQ(2, config.signals_size());
// Verify that DB has a signal entry with correct storage and collection start
// time.
@@ -133,6 +165,13 @@ TEST_F(SignalStorageConfigTest,
EXPECT_EQ(base::Days(6).InSeconds(), signal_config.storage_length_s());
EXPECT_NE(0, signal_config.collection_start_time_s());
+ signal_config2 = config.signals(1);
+ EXPECT_EQ(metric_hash, signal_config2.name_hash());
+ EXPECT_EQ(event_hash, signal_config2.event_hash());
+ EXPECT_EQ(proto::SignalType::UKM_EVENT, signal_config2.signal_type());
+ EXPECT_EQ(base::Days(6).InSeconds(), signal_config2.storage_length_s());
+ EXPECT_NE(0, signal_config2.collection_start_time_s());
+
// Signal collection shouldn't satisfy.
EXPECT_FALSE(
signal_storage_config_->MeetsSignalCollectionRequirement(metadata));
@@ -172,6 +211,13 @@ TEST_F(SignalStorageConfigTest,
EXPECT_EQ(proto::SignalType::USER_ACTION, signal_config.signal_type());
EXPECT_EQ(base::Days(6).InSeconds(), signal_config.storage_length_s());
EXPECT_NE(0, signal_config.collection_start_time_s());
+
+ signal_config2 = config.signals(1);
+ EXPECT_EQ(metric_hash, signal_config2.name_hash());
+ EXPECT_EQ(event_hash, signal_config2.event_hash());
+ EXPECT_EQ(proto::SignalType::UKM_EVENT, signal_config2.signal_type());
+ EXPECT_EQ(base::Days(6).InSeconds(), signal_config2.storage_length_s());
+ EXPECT_NE(0, signal_config2.collection_start_time_s());
}
TEST_F(SignalStorageConfigTest, CleanupSignals) {
diff --git a/chromium/components/segmentation_platform/internal/database/storage_service.cc b/chromium/components/segmentation_platform/internal/database/storage_service.cc
index 63d68b297c7..90052cfa964 100644
--- a/chromium/components/segmentation_platform/internal/database/storage_service.cc
+++ b/chromium/components/segmentation_platform/internal/database/storage_service.cc
@@ -31,8 +31,7 @@ StorageService::StorageService(
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::Clock* clock,
UkmDataManager* ukm_data_manager,
- base::flat_set<optimization_guide::proto::OptimizationTarget>
- all_segment_ids,
+ base::flat_set<proto::SegmentId> all_segment_ids,
ModelProviderFactory* model_provider_factory)
: StorageService(
db_provider->GetDB<proto::SegmentInfo>(
@@ -60,13 +59,12 @@ StorageService::StorageService(
signal_storage_config_db,
base::Clock* clock,
UkmDataManager* ukm_data_manager,
- base::flat_set<optimization_guide::proto::OptimizationTarget>
- all_segment_ids,
+ base::flat_set<proto::SegmentId> all_segment_ids,
ModelProviderFactory* model_provider_factory)
: default_model_manager_(std::make_unique<DefaultModelManager>(
model_provider_factory,
- std::vector<OptimizationTarget>(all_segment_ids.begin(),
- all_segment_ids.end()))),
+ std::vector<SegmentId>(all_segment_ids.begin(),
+ all_segment_ids.end()))),
segment_info_database_(
std::make_unique<SegmentInfoDatabase>(std::move(segment_db))),
signal_database_(
@@ -85,6 +83,18 @@ StorageService::StorageService(
ukm_data_manager_->AddRef();
}
+StorageService::StorageService(
+ std::unique_ptr<SegmentInfoDatabase> segment_info_database,
+ std::unique_ptr<SignalDatabase> signal_database,
+ std::unique_ptr<SignalStorageConfig> signal_storage_config,
+ std::unique_ptr<DefaultModelManager> default_model_manager,
+ UkmDataManager* ukm_data_manager)
+ : default_model_manager_(std::move(default_model_manager)),
+ segment_info_database_(std::move(segment_info_database)),
+ signal_database_(std::move(signal_database)),
+ signal_storage_config_(std::move(signal_storage_config)),
+ ukm_data_manager_(ukm_data_manager) {}
+
StorageService::~StorageService() {
ukm_data_manager_->RemoveRef();
}
@@ -131,13 +141,6 @@ void StorageService::MaybeFinishInitialization() {
.Run(*segment_info_database_initialized_ &&
*signal_database_initialized_ &&
*signal_storage_config_initialized_);
-
- // Initiate database maintenance tasks with a small delay.
- base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&StorageService::OnExecuteDatabaseMaintenanceTasks,
- weak_ptr_factory_.GetWeakPtr()),
- kDatabaseMaintenanceDelay);
}
int StorageService::GetServiceStatus() const {
@@ -152,7 +155,19 @@ int StorageService::GetServiceStatus() const {
return status;
}
-void StorageService::OnExecuteDatabaseMaintenanceTasks() {
+void StorageService::ExecuteDatabaseMaintenanceTasks(bool is_startup) {
+ if (is_startup) {
+ // Initiate database maintenance tasks with a small delay at startup.
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&StorageService::ExecuteDatabaseMaintenanceTasks,
+ weak_ptr_factory_.GetWeakPtr(), false),
+ kDatabaseMaintenanceDelay);
+ return;
+ }
+
+ // This should be invoked at least after a short amount of time has passed
+ // since initialization happened.
database_maintenance_->ExecuteMaintenanceTasks();
}
diff --git a/chromium/components/segmentation_platform/internal/database/storage_service.h b/chromium/components/segmentation_platform/internal/database/storage_service.h
index ac76151d462..258bc0dc8c9 100644
--- a/chromium/components/segmentation_platform/internal/database/storage_service.h
+++ b/chromium/components/segmentation_platform/internal/database/storage_service.h
@@ -12,7 +12,7 @@
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "components/leveldb_proto/public/proto_database.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base {
@@ -65,10 +65,10 @@ class StorageService {
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::Clock* clock,
UkmDataManager* ukm_data_manager,
- base::flat_set<optimization_guide::proto::OptimizationTarget>
- all_segment_ids,
+ base::flat_set<proto::SegmentId> all_segment_ids,
ModelProviderFactory* model_provider_factory);
+ // For tests:
StorageService(
std::unique_ptr<leveldb_proto::ProtoDatabase<proto::SegmentInfo>>
segment_db,
@@ -78,10 +78,16 @@ class StorageService {
signal_storage_config_db,
base::Clock* clock,
UkmDataManager* ukm_data_manager,
- base::flat_set<optimization_guide::proto::OptimizationTarget>
- all_segment_ids,
+ base::flat_set<proto::SegmentId> all_segment_ids,
ModelProviderFactory* model_provider_factory);
+ // For tests:
+ StorageService(std::unique_ptr<SegmentInfoDatabase> segment_info_database,
+ std::unique_ptr<SignalDatabase> signal_database,
+ std::unique_ptr<SignalStorageConfig> signal_storage_config,
+ std::unique_ptr<DefaultModelManager> default_model_manager,
+ UkmDataManager* ukm_data_manager);
+
~StorageService();
StorageService(StorageService&) = delete;
@@ -96,6 +102,9 @@ class StorageService {
// bitmap values.
int GetServiceStatus() const;
+ // Executes all database maintenance tasks.
+ void ExecuteDatabaseMaintenanceTasks(bool is_startup);
+
DefaultModelManager* default_model_manager() {
DCHECK(default_model_manager_);
return default_model_manager_.get();
@@ -120,10 +129,6 @@ class StorageService {
bool IsInitializationFinished() const;
void MaybeFinishInitialization();
- // Executes all database maintenance tasks. This should be invoked after a
- // short amount of time has passed since initialization happened.
- void OnExecuteDatabaseMaintenanceTasks();
-
// Default models.
std::unique_ptr<DefaultModelManager> default_model_manager_;
diff --git a/chromium/components/segmentation_platform/internal/database/test_segment_info_database.cc b/chromium/components/segmentation_platform/internal/database/test_segment_info_database.cc
index 34af8ada538..1457adbe3ac 100644
--- a/chromium/components/segmentation_platform/internal/database/test_segment_info_database.cc
+++ b/chromium/components/segmentation_platform/internal/database/test_segment_info_database.cc
@@ -8,40 +8,15 @@
#include "base/containers/contains.h"
#include "base/metrics/metrics_hashes.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
-#include "components/segmentation_platform/internal/signals/ukm_config.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace segmentation_platform::test {
-namespace {
-void AddFeature(proto::SegmentInfo* segment_info,
- proto::SignalType signal_type,
- const std::string& name,
- uint64_t bucket_count,
- uint64_t tensor_length,
- proto::Aggregation aggregation,
- const std::vector<int32_t>& accepted_enum_ids) {
- proto::SegmentationModelMetadata* metadata =
- segment_info->mutable_model_metadata();
- proto::InputFeature* input = metadata->add_input_features();
- proto::UMAFeature* feature = input->mutable_uma_feature();
- feature->set_type(signal_type);
- feature->set_name(name);
- feature->set_name_hash(base::HashMetricName(name));
- feature->set_bucket_count(bucket_count);
- feature->set_tensor_length(tensor_length);
- feature->set_aggregation(aggregation);
-
- for (int32_t accepted_enum_id : accepted_enum_ids)
- feature->add_enum_ids(accepted_enum_id);
-}
-} // namespace
-
TestSegmentInfoDatabase::TestSegmentInfoDatabase()
: SegmentInfoDatabase(nullptr) {}
@@ -58,7 +33,7 @@ void TestSegmentInfoDatabase::GetAllSegmentInfo(
}
void TestSegmentInfoDatabase::GetSegmentInfoForSegments(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback) {
auto result = std::make_unique<SegmentInfoDatabase::SegmentInfoList>();
for (const auto& pair : segment_infos_) {
@@ -68,13 +43,13 @@ void TestSegmentInfoDatabase::GetSegmentInfoForSegments(
std::move(callback).Run(std::move(result));
}
-void TestSegmentInfoDatabase::GetSegmentInfo(OptimizationTarget segment_id,
+void TestSegmentInfoDatabase::GetSegmentInfo(SegmentId segment_id,
SegmentInfoCallback callback) {
- auto result = std::find_if(
- segment_infos_.begin(), segment_infos_.end(),
- [segment_id](std::pair<OptimizationTarget, proto::SegmentInfo> pair) {
- return pair.first == segment_id;
- });
+ auto result =
+ std::find_if(segment_infos_.begin(), segment_infos_.end(),
+ [segment_id](std::pair<SegmentId, proto::SegmentInfo> pair) {
+ return pair.first == segment_id;
+ });
std::move(callback).Run(result == segment_infos_.end()
? absl::nullopt
@@ -82,7 +57,7 @@ void TestSegmentInfoDatabase::GetSegmentInfo(OptimizationTarget segment_id,
}
void TestSegmentInfoDatabase::UpdateSegment(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
absl::optional<proto::SegmentInfo> segment_info,
SuccessCallback callback) {
if (segment_info.has_value()) {
@@ -92,8 +67,7 @@ void TestSegmentInfoDatabase::UpdateSegment(
// Delete the segment.
auto new_end = std::remove_if(
segment_infos_.begin(), segment_infos_.end(),
- [segment_id](
- const std::pair<OptimizationTarget, proto::SegmentInfo>& pair) {
+ [segment_id](const std::pair<SegmentId, proto::SegmentInfo>& pair) {
return pair.first == segment_id;
});
segment_infos_.erase(new_end, segment_infos_.end());
@@ -102,7 +76,7 @@ void TestSegmentInfoDatabase::UpdateSegment(
}
void TestSegmentInfoDatabase::SaveSegmentResult(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
absl::optional<proto::PredictionResult> result,
SuccessCallback callback) {
proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
@@ -117,58 +91,74 @@ void TestSegmentInfoDatabase::SaveSegmentResult(
}
void TestSegmentInfoDatabase::AddUserActionFeature(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
const std::string& name,
uint64_t bucket_count,
uint64_t tensor_length,
proto::Aggregation aggregation) {
proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
- AddFeature(info, proto::SignalType::USER_ACTION, name, bucket_count,
- tensor_length, aggregation, {});
+ MetadataWriter writer(info->mutable_model_metadata());
+ MetadataWriter::UMAFeature feature{
+ .signal_type = proto::SignalType::USER_ACTION,
+ .name = name.c_str(),
+ .bucket_count = bucket_count,
+ .tensor_length = tensor_length,
+ .aggregation = aggregation,
+ .accepted_enum_ids = nullptr};
+ MetadataWriter::UMAFeature features[] = {feature};
+ writer.AddUmaFeatures(features, 1);
}
void TestSegmentInfoDatabase::AddHistogramValueFeature(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
const std::string& name,
uint64_t bucket_count,
uint64_t tensor_length,
proto::Aggregation aggregation) {
proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
- AddFeature(info, proto::SignalType::HISTOGRAM_VALUE, name, bucket_count,
- tensor_length, aggregation, {});
+ MetadataWriter writer(info->mutable_model_metadata());
+ MetadataWriter::UMAFeature feature{
+ .signal_type = proto::SignalType::HISTOGRAM_VALUE,
+ .name = name.c_str(),
+ .bucket_count = bucket_count,
+ .tensor_length = tensor_length,
+ .aggregation = aggregation,
+ .accepted_enum_ids = nullptr};
+ MetadataWriter::UMAFeature features[] = {feature};
+ writer.AddUmaFeatures(features, 1);
}
void TestSegmentInfoDatabase::AddHistogramEnumFeature(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
const std::string& name,
uint64_t bucket_count,
uint64_t tensor_length,
proto::Aggregation aggregation,
const std::vector<int32_t>& accepted_enum_ids) {
proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
- AddFeature(info, proto::SignalType::HISTOGRAM_ENUM, name, bucket_count,
- tensor_length, aggregation, accepted_enum_ids);
+ MetadataWriter writer(info->mutable_model_metadata());
+ MetadataWriter::UMAFeature feature{
+ .signal_type = proto::SignalType::HISTOGRAM_ENUM,
+ .name = name.c_str(),
+ .bucket_count = bucket_count,
+ .tensor_length = tensor_length,
+ .aggregation = aggregation,
+ .enum_ids_size = accepted_enum_ids.size(),
+ .accepted_enum_ids = accepted_enum_ids.data()};
+ MetadataWriter::UMAFeature features[] = {feature};
+ writer.AddUmaFeatures(features, 1);
}
-void TestSegmentInfoDatabase::AddSqlFeature(OptimizationTarget segment_id,
- const std::string& sql,
- const UkmConfig& event_config) {
+void TestSegmentInfoDatabase::AddSqlFeature(
+ SegmentId segment_id,
+ const MetadataWriter::SqlFeature& feature) {
proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
- auto* metadata = info->mutable_model_metadata();
- proto::SqlFeature* feature =
- metadata->add_input_features()->mutable_sql_feature();
- feature->set_sql(sql);
- for (const auto& event_it : event_config.metrics_for_event_for_testing()) {
- auto* ukm_event = feature->mutable_signal_filter()->add_ukm_events();
- const UkmEventHash event_hash = event_it.first;
- ukm_event->set_event_hash(event_hash.GetUnsafeValue());
- const base::flat_set<UkmMetricHash>& metrics = event_it.second;
- for (const auto& metric : metrics)
- ukm_event->mutable_metric_hash_filter()->Add(metric.GetUnsafeValue());
- }
+ MetadataWriter writer(info->mutable_model_metadata());
+ MetadataWriter::SqlFeature features[] = {feature};
+ writer.AddSqlFeatures(features, 1);
}
-void TestSegmentInfoDatabase::AddPredictionResult(OptimizationTarget segment_id,
+void TestSegmentInfoDatabase::AddPredictionResult(SegmentId segment_id,
float score,
base::Time timestamp) {
proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
@@ -179,8 +169,8 @@ void TestSegmentInfoDatabase::AddPredictionResult(OptimizationTarget segment_id,
}
void TestSegmentInfoDatabase::AddDiscreteMapping(
- OptimizationTarget segment_id,
- float mappings[][2],
+ SegmentId segment_id,
+ const float mappings[][2],
int num_pairs,
const std::string& discrete_mapping_key) {
proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
@@ -195,7 +185,7 @@ void TestSegmentInfoDatabase::AddDiscreteMapping(
}
}
-void TestSegmentInfoDatabase::SetBucketDuration(OptimizationTarget segment_id,
+void TestSegmentInfoDatabase::SetBucketDuration(SegmentId segment_id,
uint64_t bucket_duration,
proto::TimeUnit time_unit) {
proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
@@ -204,7 +194,7 @@ void TestSegmentInfoDatabase::SetBucketDuration(OptimizationTarget segment_id,
}
proto::SegmentInfo* TestSegmentInfoDatabase::FindOrCreateSegment(
- OptimizationTarget segment_id) {
+ SegmentId segment_id) {
proto::SegmentInfo* info = nullptr;
for (auto& pair : segment_infos_) {
if (pair.first == segment_id) {
diff --git a/chromium/components/segmentation_platform/internal/database/test_segment_info_database.h b/chromium/components/segmentation_platform/internal/database/test_segment_info_database.h
index f407a6749cb..3b6d388e31a 100644
--- a/chromium/components/segmentation_platform/internal/database/test_segment_info_database.h
+++ b/chromium/components/segmentation_platform/internal/database/test_segment_info_database.h
@@ -11,15 +11,12 @@
#include "base/containers/flat_set.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
-namespace segmentation_platform {
-
-class UkmConfig;
-
-namespace test {
+namespace segmentation_platform::test {
// A fake database with sample entries that can be used for tests.
class TestSegmentInfoDatabase : public SegmentInfoDatabase {
@@ -30,58 +27,54 @@ class TestSegmentInfoDatabase : public SegmentInfoDatabase {
// SegmentInfoDatabase overrides.
void Initialize(SuccessCallback callback) override;
void GetAllSegmentInfo(MultipleSegmentInfoCallback callback) override;
- void GetSegmentInfoForSegments(
- const std::vector<OptimizationTarget>& segment_ids,
- MultipleSegmentInfoCallback callback) override;
- void GetSegmentInfo(OptimizationTarget segment_id,
+ void GetSegmentInfoForSegments(const std::vector<SegmentId>& segment_ids,
+ MultipleSegmentInfoCallback callback) override;
+ void GetSegmentInfo(SegmentId segment_id,
SegmentInfoCallback callback) override;
- void UpdateSegment(OptimizationTarget segment_id,
+ void UpdateSegment(SegmentId segment_id,
absl::optional<proto::SegmentInfo> segment_info,
SuccessCallback callback) override;
- void SaveSegmentResult(OptimizationTarget segment_id,
+ void SaveSegmentResult(SegmentId segment_id,
absl::optional<proto::PredictionResult> result,
SuccessCallback callback) override;
// Test helper methods.
- void AddUserActionFeature(OptimizationTarget segment_id,
+ void AddUserActionFeature(SegmentId segment_id,
const std::string& user_action,
uint64_t bucket_count,
uint64_t tensor_length,
proto::Aggregation aggregation);
- void AddHistogramValueFeature(OptimizationTarget segment_id,
+ void AddHistogramValueFeature(SegmentId segment_id,
const std::string& histogram,
uint64_t bucket_count,
uint64_t tensor_length,
proto::Aggregation aggregation);
- void AddHistogramEnumFeature(OptimizationTarget segment_id,
+ void AddHistogramEnumFeature(SegmentId segment_id,
const std::string& histogram_name,
uint64_t bucket_count,
uint64_t tensor_length,
proto::Aggregation aggregation,
const std::vector<int32_t>& accepted_enum_ids);
- void AddSqlFeature(OptimizationTarget segment_id,
- const std::string& sql,
- const UkmConfig& ukm_config);
- void AddPredictionResult(OptimizationTarget segment_id,
+ void AddSqlFeature(SegmentId segment_id,
+ const MetadataWriter::SqlFeature& feature);
+ void AddPredictionResult(SegmentId segment_id,
float score,
base::Time timestamp);
- void AddDiscreteMapping(OptimizationTarget segment_id,
- float mappings[][2],
+ void AddDiscreteMapping(SegmentId segment_id,
+ const float mappings[][2],
int num_pairs,
const std::string& discrete_mapping_key);
- void SetBucketDuration(OptimizationTarget segment_id,
+ void SetBucketDuration(SegmentId segment_id,
uint64_t bucket_duration,
proto::TimeUnit time_unit);
// Finds a segment with given |segment_id|. Creates one if it doesn't exist.
- proto::SegmentInfo* FindOrCreateSegment(OptimizationTarget segment_id);
+ proto::SegmentInfo* FindOrCreateSegment(SegmentId segment_id);
private:
- std::vector<std::pair<OptimizationTarget, proto::SegmentInfo>> segment_infos_;
+ std::vector<std::pair<SegmentId, proto::SegmentInfo>> segment_infos_;
};
-} // namespace test
-
-} // namespace segmentation_platform
+} // namespace segmentation_platform::test
#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_TEST_SEGMENT_INFO_DATABASE_H_
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_database.cc b/chromium/components/segmentation_platform/internal/database/ukm_database.cc
index 9222d56ea01..c2410ce302f 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_database.cc
+++ b/chromium/components/segmentation_platform/internal/database/ukm_database.cc
@@ -4,54 +4,17 @@
#include "components/segmentation_platform/internal/database/ukm_database.h"
-#include "base/rand_util.h"
-#include "base/task/thread_pool.h"
-#include "components/segmentation_platform/internal/database/ukm_database_backend.h"
-
namespace segmentation_platform {
-UkmDatabase::UkmDatabase(const base::FilePath& database_path)
- : task_runner_(base::SequencedTaskRunnerHandle::Get()),
- backend_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
- {base::MayBlock(), base::TaskPriority::USER_VISIBLE})),
- backend_(std::make_unique<UkmDatabaseBackend>(database_path,
- backend_task_runner_)) {}
-
-UkmDatabase::~UkmDatabase() {
- backend_task_runner_->DeleteSoon(FROM_HERE, std::move(backend_));
-}
-
-void UkmDatabase::InitDatabase(UkmDatabase::InitCallback callback) {
- backend_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&UkmDatabaseBackend::InitDatabase,
- backend_->GetWeakPtr(), std::move(callback)));
-}
-
-void UkmDatabase::StoreUkmEntry(ukm::mojom::UkmEntryPtr ukm_entry) {
- backend_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&UkmDatabaseBackend::StoreUkmEntry,
- backend_->GetWeakPtr(), std::move(ukm_entry)));
-}
+UkmDatabase::CustomSqlQuery::CustomSqlQuery() = default;
-void UkmDatabase::UpdateUrlForUkmSource(ukm::SourceId source_id,
- const GURL& url,
- bool is_validated) {
- backend_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&UkmDatabaseBackend::UpdateUrlForUkmSource,
- backend_->GetWeakPtr(), source_id, url, is_validated));
-}
+UkmDatabase::CustomSqlQuery::CustomSqlQuery(CustomSqlQuery&&) = default;
-void UkmDatabase::OnUrlValidated(const GURL& url) {
- backend_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&UkmDatabaseBackend::OnUrlValidated,
- backend_->GetWeakPtr(), url));
-}
+UkmDatabase::CustomSqlQuery::CustomSqlQuery(
+ const base::StringPiece& query,
+ const std::vector<processing::ProcessedValue>& bind_values)
+ : query(query), bind_values(bind_values) {}
-void UkmDatabase::RemoveUrls(const std::vector<GURL>& urls) {
- backend_task_runner_->PostTask(FROM_HERE,
- base::BindOnce(&UkmDatabaseBackend::RemoveUrls,
- backend_->GetWeakPtr(), urls));
-}
+UkmDatabase::CustomSqlQuery::~CustomSqlQuery() = default;
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_database.h b/chromium/components/segmentation_platform/internal/database/ukm_database.h
index cf4d469f02f..f14f24d8767 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_database.h
+++ b/chromium/components/segmentation_platform/internal/database/ukm_database.h
@@ -9,32 +9,35 @@
#include <memory>
#include <vector>
-#include "base/files/file_path.h"
+#include "base/callback.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/metrics/public/mojom/ukm_interface.mojom.h"
#include "url/gurl.h"
namespace segmentation_platform {
-class UkmDatabaseBackend;
-
// UKM database is a single instance for each browser process. This class will
// be used for the storage and query of UKM data, for all the segmentation
// platform service(s).
class UkmDatabase {
public:
- explicit UkmDatabase(const base::FilePath& database_path);
- virtual ~UkmDatabase();
+ UkmDatabase() = default;
+ virtual ~UkmDatabase() = default;
UkmDatabase(UkmDatabase&) = delete;
UkmDatabase& operator=(UkmDatabase&) = delete;
- using InitCallback = base::OnceCallback<void(bool)>;
- void InitDatabase(InitCallback callback);
+ using SuccessCallback = base::OnceCallback<void(bool)>;
+
+ // Initialize the database.
+ virtual void InitDatabase(SuccessCallback callback) = 0;
// Called once when an UKM event is added. All metrics from the event will be
// added to the metrics table.
- virtual void StoreUkmEntry(ukm::mojom::UkmEntryPtr ukm_entry);
+ virtual void StoreUkmEntry(ukm::mojom::UkmEntryPtr ukm_entry) = 0;
// Called when URL for a source ID is updated. Only validated URLs will be
// written to the URL table. The URL can be validated either by setting
@@ -44,24 +47,51 @@ class UkmDatabase {
// index metrics with the same |source_id| with the URL.
virtual void UpdateUrlForUkmSource(ukm::SourceId source_id,
const GURL& url,
- bool is_validated);
+ bool is_validated) = 0;
// Called to validate an URL, see also UpdateUrlForUkmSource(). Safe to call
// with unneeded URLs, since the database will only persist the URLs already
// pending for known |source_id|s. Note that this call will not automatically
// validate future URLs given by UpdateUrlForUkmSource(). They need to have
// |is_validated| set to be persisted.
- virtual void OnUrlValidated(const GURL& url);
+ virtual void OnUrlValidated(const GURL& url) = 0;
// Removes all the URLs from URL table and all the associated metrics in
// metrics table, on best effort. Any new metrics added with the URL will
- // still be stored in metrics table (without URLs).
- virtual void RemoveUrls(const std::vector<GURL>& urls);
+ // still be stored in metrics table (without URLs). If `all_urls` is true,
+ // then clears all the URLs without using `urls` list. It is an optimization
+ // to clear all the URLs quickly.
+ virtual void RemoveUrls(const std::vector<GURL>& urls, bool all_urls) = 0;
+
+ // Struct responsible for storing a sql query and its bind values.
+ struct CustomSqlQuery {
+ CustomSqlQuery();
+ CustomSqlQuery(CustomSqlQuery&&);
+ CustomSqlQuery(const base::StringPiece& query,
+ const std::vector<processing::ProcessedValue>& bind_values);
+ ~CustomSqlQuery();
+
+ bool operator==(const CustomSqlQuery& rhs) const {
+ return query == rhs.query && bind_values == rhs.bind_values;
+ }
+ CustomSqlQuery& operator=(CustomSqlQuery&&) = default;
+
+ std::string query;
+ std::vector<processing::ProcessedValue> bind_values;
+ };
+
+ using QueryList = base::flat_map<processing::FeatureIndex, CustomSqlQuery>;
+ using QueryCallback =
+ base::OnceCallback<void(bool success, processing::IndexedTensors)>;
+
+ // Called to query data from the ukm database. The result is returned in the
+ // |callback| as a mapping of indexed vectors of processing::ProcessedValue.
+ virtual void RunReadonlyQueries(QueryList&& queries,
+ QueryCallback callback) = 0;
- private:
- scoped_refptr<base::SequencedTaskRunner> task_runner_;
- scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
- std::unique_ptr<UkmDatabaseBackend> backend_;
+ // Removes metrics older than or equal to the given `time` from the database.
+ // URLs are removed when there are no references to the metrics.
+ virtual void DeleteEntriesOlderThan(base::Time time) = 0;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_database_backend.cc b/chromium/components/segmentation_platform/internal/database/ukm_database_backend.cc
index 16a10c453c5..2cde5ba41a9 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_database_backend.cc
+++ b/chromium/components/segmentation_platform/internal/database/ukm_database_backend.cc
@@ -4,10 +4,13 @@
#include "components/segmentation_platform/internal/database/ukm_database_backend.h"
+#include "base/files/file_util.h"
#include "base/rand_util.h"
#include "components/segmentation_platform/internal/database/ukm_metrics_table.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
#include "components/segmentation_platform/internal/database/ukm_url_table.h"
#include "sql/database.h"
+#include "sql/statement.h"
namespace segmentation_platform {
@@ -17,6 +20,55 @@ bool SanityCheckUrl(const GURL& url, UrlId url_id) {
return url.is_valid() && !url.is_empty() && !url_id.is_null();
}
+void BindValuesToStatement(
+ const std::vector<processing::ProcessedValue>& bind_values,
+ sql::Statement& statement) {
+ for (unsigned i = 0; i < bind_values.size(); ++i) {
+ const processing::ProcessedValue& value = bind_values[i];
+ switch (value.type) {
+ case processing::ProcessedValue::Type::BOOL:
+ statement.BindBool(i, value.bool_val);
+ break;
+ case processing::ProcessedValue::Type::INT:
+ statement.BindInt(i, value.int_val);
+ break;
+ case processing::ProcessedValue::Type::FLOAT:
+ statement.BindDouble(i, value.float_val);
+ break;
+ case processing::ProcessedValue::Type::DOUBLE:
+ statement.BindDouble(i, value.double_val);
+ break;
+ case processing::ProcessedValue::Type::STRING:
+ statement.BindString(i, value.str_val);
+ break;
+ case processing::ProcessedValue::Type::TIME:
+ statement.BindTime(i, value.time_val);
+ break;
+ case processing::ProcessedValue::Type::INT64:
+ statement.BindInt64(i, value.int64_val);
+ break;
+ case processing::ProcessedValue::Type::UNKNOWN:
+ NOTREACHED();
+ }
+ }
+}
+
+float GetSingleFloatOutput(sql::Statement& statement) {
+ sql::ColumnType output_type = statement.GetColumnType(0);
+ switch (output_type) {
+ case sql::ColumnType::kBlob:
+ case sql::ColumnType::kText:
+ NOTREACHED();
+ return 0;
+ case sql::ColumnType::kFloat:
+ return statement.ColumnDouble(0);
+ case sql::ColumnType::kInteger:
+ return statement.ColumnInt64(0);
+ case sql::ColumnType::kNull:
+ return 0;
+ }
+}
+
} // namespace
UkmDatabaseBackend::UkmDatabaseBackend(
@@ -32,21 +84,32 @@ UkmDatabaseBackend::UkmDatabaseBackend(
UkmDatabaseBackend::~UkmDatabaseBackend() = default;
-void UkmDatabaseBackend::InitDatabase(
- UkmDatabaseBackend::SuccessCallback callback) {
+void UkmDatabaseBackend::InitDatabase(SuccessCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!db_.Open(database_path_)) {
- callback_task_runner_->PostTask(FROM_HERE,
- base::BindOnce(std::move(callback), false));
- return;
+ base::File::Error error{};
+ bool result = true;
+ if (!base::CreateDirectoryAndGetError(database_path_.DirName(), &error) ||
+ !db_.Open(database_path_)) {
+ // TODO(ssid): On failure retry opening the database or delete backend or
+ // open in memory for session.
+ LOG(ERROR) << "Failed to open UKM database: " << error << " "
+ << db_.GetErrorMessage();
+ result = false;
+ }
+ if (result) {
+ result = metrics_table_.InitTable() && url_table_.InitTable();
}
- bool result = metrics_table_.InitTable() && url_table_.InitTable();
+ status_ = result ? Status::INIT_SUCCESS : Status::INIT_FAILED;
callback_task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), result));
}
void UkmDatabaseBackend::StoreUkmEntry(ukm::mojom::UkmEntryPtr entry) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (status_ != Status::INIT_SUCCESS) {
+ return;
+ }
+
MetricsRowEventId event_id =
MetricsRowEventId::FromUnsafeValue(base::RandUint64());
// If we have an URL ID for the entry, then use it, otherwise the URL ID will
@@ -73,18 +136,25 @@ void UkmDatabaseBackend::UpdateUrlForUkmSource(ukm::SourceId source_id,
const GURL& url,
bool is_validated) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (status_ != Status::INIT_SUCCESS) {
+ return;
+ }
+
UrlId url_id = UkmUrlTable::GenerateUrlId(url);
- if (!SanityCheckUrl(url, url_id))
+ if (!SanityCheckUrl(url, url_id)) {
return;
+ }
if (!url_table_.IsUrlInTable(url_id)) {
if (is_validated) {
- url_table_.WriteUrl(url, url_id);
+ url_table_.WriteUrl(url, url_id, base::Time::Now());
// Remove from list so we don't add the URL again to table later.
urls_not_validated_.erase(url_id);
} else {
urls_not_validated_.insert(url_id);
}
+ } else {
+ url_table_.UpdateUrlTimestamp(url_id, base::Time::Now());
}
// Keep track of source to URL ID mapping for future metrics.
source_to_url_[source_id] = url_id;
@@ -94,16 +164,30 @@ void UkmDatabaseBackend::UpdateUrlForUkmSource(ukm::SourceId source_id,
void UkmDatabaseBackend::OnUrlValidated(const GURL& url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (status_ != Status::INIT_SUCCESS) {
+ return;
+ }
+
UrlId url_id = UkmUrlTable::GenerateUrlId(url);
// Write URL to table only if it's needed and it's not already added.
if (urls_not_validated_.count(url_id) && SanityCheckUrl(url, url_id)) {
- url_table_.WriteUrl(url, url_id);
+ url_table_.WriteUrl(url, url_id, base::Time::Now());
urls_not_validated_.erase(url_id);
}
}
-void UkmDatabaseBackend::RemoveUrls(const std::vector<GURL>& urls) {
+void UkmDatabaseBackend::RemoveUrls(const std::vector<GURL>& urls,
+ bool all_urls) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (status_ != Status::INIT_SUCCESS) {
+ return;
+ }
+
+ if (all_urls) {
+ DeleteAllUrls();
+ return;
+ }
+
std::vector<UrlId> url_ids;
for (const GURL& url : urls) {
UrlId id = UkmUrlTable::GenerateUrlId(url);
@@ -117,4 +201,58 @@ void UkmDatabaseBackend::RemoveUrls(const std::vector<GURL>& urls) {
metrics_table_.DeleteEventsForUrls(url_ids);
}
+void UkmDatabaseBackend::RunReadonlyQueries(QueryList&& queries,
+ QueryCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (status_ != Status::INIT_SUCCESS) {
+ callback_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), false,
+ processing::IndexedTensors()));
+ return;
+ }
+
+ bool success = true;
+ processing::IndexedTensors result;
+ for (const auto& index_and_query : queries) {
+ const processing::FeatureIndex index = index_and_query.first;
+ const UkmDatabase::CustomSqlQuery& query = index_and_query.second;
+
+ sql::Statement statement(db_.GetReadonlyStatement(query.query.c_str()));
+ BindValuesToStatement(query.bind_values, statement);
+
+ if (!statement.is_valid() || !statement.Step()) {
+ success = false;
+ break;
+ }
+
+ float output = GetSingleFloatOutput(statement);
+ result[index].push_back(processing::ProcessedValue(output));
+ }
+ callback_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(callback), success, std::move(result)));
+}
+
+void UkmDatabaseBackend::DeleteEntriesOlderThan(base::Time time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ std::vector<UrlId> deleted_urls =
+ metrics_table_.DeleteEventsBeforeTimestamp(time);
+ url_table_.RemoveUrls(deleted_urls);
+ url_table_.DeleteUrlsBeforeTimestamp(time);
+}
+
+void UkmDatabaseBackend::DeleteAllUrls() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Remove all metrics associated with any URL, but retain the metrics that are
+ // not keyed on URL.
+ bool success = db_.Execute("DELETE FROM metrics WHERE url_id!=0");
+ // TODO(ssid): sqlite uses truncate optimization on DELETE statements without
+ // WHERE clause. Maybe replace the DROP and CREATE with DELETE if the
+ // performance is better.
+ success = success && db_.Execute("DROP TABLE urls");
+ success = success && url_table_.InitTable();
+ DCHECK(success);
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_database_backend.h b/chromium/components/segmentation_platform/internal/database/ukm_database_backend.h
index ee61d170cf0..686f46109a2 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_database_backend.h
+++ b/chromium/components/segmentation_platform/internal/database/ukm_database_backend.h
@@ -13,6 +13,7 @@
#include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
+#include "components/segmentation_platform/internal/database/ukm_database.h"
#include "components/segmentation_platform/internal/database/ukm_metrics_table.h"
#include "components/segmentation_platform/internal/database/ukm_types.h"
#include "components/segmentation_platform/internal/database/ukm_url_table.h"
@@ -25,27 +26,27 @@ namespace segmentation_platform {
// Database backend class that handles various tables in the SQL database. Runs
// in database task runner.
-class UkmDatabaseBackend {
+class UkmDatabaseBackend : public UkmDatabase {
public:
UkmDatabaseBackend(
const base::FilePath& database_path,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
- ~UkmDatabaseBackend();
+ ~UkmDatabaseBackend() override;
UkmDatabaseBackend(const UkmDatabaseBackend&) = delete;
UkmDatabaseBackend& operator=(const UkmDatabaseBackend&) = delete;
- using SuccessCallback = base::OnceCallback<void(bool)>;
-
// UkmDatabase implementation. All callbacks will be posted to
// |callback_task_runner|.
- void InitDatabase(SuccessCallback callback);
- void StoreUkmEntry(ukm::mojom::UkmEntryPtr ukm_entry);
+ void InitDatabase(SuccessCallback callback) override;
+ void StoreUkmEntry(ukm::mojom::UkmEntryPtr ukm_entry) override;
void UpdateUrlForUkmSource(ukm::SourceId source_id,
const GURL& url,
- bool is_validated);
- void OnUrlValidated(const GURL& url);
- void RemoveUrls(const std::vector<GURL>& urls);
+ bool is_validated) override;
+ void OnUrlValidated(const GURL& url) override;
+ void RemoveUrls(const std::vector<GURL>& urls, bool all_urls) override;
+ void RunReadonlyQueries(QueryList&& queries, QueryCallback callback) override;
+ void DeleteEntriesOlderThan(base::Time time) override;
sql::Database& db() { return db_; }
@@ -59,12 +60,17 @@ class UkmDatabaseBackend {
}
private:
+ // Helper to delete all URLs from database.
+ void DeleteAllUrls();
+
const base::FilePath database_path_;
scoped_refptr<base::SequencedTaskRunner> callback_task_runner_
GUARDED_BY_CONTEXT(sequence_checker_);
sql::Database db_ GUARDED_BY_CONTEXT(sequence_checker_);
UkmMetricsTable metrics_table_ GUARDED_BY_CONTEXT(sequence_checker_);
UkmUrlTable url_table_ GUARDED_BY_CONTEXT(sequence_checker_);
+ enum class Status { CREATED, INIT_FAILED, INIT_SUCCESS };
+ Status status_ = Status::CREATED;
// Map from source ID to URL. When URL updates are sent before metrics, this
// map is used to set URL ID to the metrics rows. This is an in-memory cache
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_database_backend_unittest.cc b/chromium/components/segmentation_platform/internal/database/ukm_database_backend_unittest.cc
index 3800924e2dc..6110870258d 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_database_backend_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/database/ukm_database_backend_unittest.cc
@@ -7,6 +7,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/task/thread_pool.h"
#include "base/test/task_environment.h"
+#include "build/build_config.h"
#include "components/segmentation_platform/internal/database/ukm_database_test_utils.h"
#include "components/segmentation_platform/internal/database/ukm_metrics_table.h"
#include "components/segmentation_platform/internal/database/ukm_url_table.h"
@@ -22,6 +23,14 @@ using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::UnorderedElementsAre;
+// Set path to protected file which fails to open the database.
+#if BUILDFLAG(IS_POSIX)
+constexpr base::FilePath::CharType kBadFilePath[] = FILE_PATH_LITERAL("/usr");
+#else
+constexpr base::FilePath::CharType kBadFilePath[] =
+ FILE_PATH_LITERAL("C:\\Windows");
+#endif
+
// Stats about the database tables.
struct DatabaseStats {
// Total number of metrics in metrics table.
@@ -96,6 +105,24 @@ class UkmDatabaseBackendTest : public testing::Test {
ASSERT_TRUE(temp_dir_.Delete());
}
+ void ExpectQueryResult(UkmDatabase::QueryList&& queries,
+ bool expect_success,
+ const processing::IndexedTensors& expected_values) {
+ base::RunLoop wait_for_query3;
+ backend_->RunReadonlyQueries(
+ std::move(queries),
+ base::BindOnce(
+ [](base::OnceClosure quit, bool expect_success,
+ const processing::IndexedTensors& expected_values, bool success,
+ processing::IndexedTensors tensors) {
+ EXPECT_EQ(expect_success, success);
+ EXPECT_EQ(expected_values, tensors);
+ std::move(quit).Run();
+ },
+ wait_for_query3.QuitClosure(), expect_success, expected_values));
+ wait_for_query3.Run();
+ }
+
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -276,8 +303,8 @@ TEST_F(UkmDatabaseBackendTest, RemoveUrls) {
EXPECT_EQ(stats1.metric_count_for_url_id.size(), 4u);
// Removing URLs that were not added does nothing.
- backend_->RemoveUrls({GURL()});
- backend_->RemoveUrls({GURL("https://www.other.com")});
+ backend_->RemoveUrls({GURL()}, /*all_urls=*/false);
+ backend_->RemoveUrls({GURL("https://www.other.com")}, /*all_urls=*/false);
DatabaseStats stats2 = GetDatabaseStats(backend_->db());
EXPECT_EQ(stats2.total_metrics, 12);
@@ -285,7 +312,7 @@ TEST_F(UkmDatabaseBackendTest, RemoveUrls) {
EXPECT_EQ(stats2.metric_count_for_url_id.size(), 4u);
// Removing non-validated URL removes from metrics table.
- backend_->RemoveUrls({kUrl3});
+ backend_->RemoveUrls({kUrl3}, /*all_urls=*/false);
DatabaseStats stats3 = GetDatabaseStats(backend_->db());
EXPECT_EQ(stats3.total_metrics, 9);
EXPECT_EQ(stats3.metric_count_for_event_id.size(), 3u);
@@ -298,7 +325,7 @@ TEST_F(UkmDatabaseBackendTest, RemoveUrls) {
UrlMatcher{.url_id = kUrlId2, .url = kUrl2}});
// Removing validated URL removes from url and metrics table.
- backend_->RemoveUrls({kUrl1, kUrl2});
+ backend_->RemoveUrls({kUrl1, kUrl2}, /*all_urls=*/false);
DatabaseStats stats4 = GetDatabaseStats(backend_->db());
EXPECT_EQ(stats4.total_metrics, 3);
EXPECT_EQ(stats4.metric_count_for_event_id.size(), 1u);
@@ -307,4 +334,230 @@ TEST_F(UkmDatabaseBackendTest, RemoveUrls) {
test_util::AssertUrlsInTable(backend_->db(), {});
}
+TEST_F(UkmDatabaseBackendTest, DeleteAllUrls) {
+ const GURL kUrl1("https://www.url1.com");
+ const GURL kUrl2("https://www.url2.com");
+ const GURL kUrl3("https://www.url3.com");
+ const UrlId kUrlId1 = UkmUrlTable::GenerateUrlId(kUrl1);
+ const UrlId kUrlId2 = UkmUrlTable::GenerateUrlId(kUrl2);
+ const ukm::SourceId kSourceId1 = 10;
+ const ukm::SourceId kSourceId2 = 20;
+ const ukm::SourceId kSourceId3 = 30;
+ const ukm::SourceId kSourceId4 = 40;
+
+ ukm::mojom::UkmEntryPtr entry1 = GetSampleUkmEntry(kSourceId1);
+ ukm::mojom::UkmEntryPtr entry2 = GetSampleUkmEntry(kSourceId2);
+ ukm::mojom::UkmEntryPtr entry3 = GetSampleUkmEntry(kSourceId3);
+ ukm::mojom::UkmEntryPtr entry4 = GetSampleUkmEntry(kSourceId4);
+
+ // Delete on empty database does not crash.
+ backend_->RemoveUrls({}, /*all_urls=*/true);
+
+ backend_->UpdateUrlForUkmSource(kSourceId1, kUrl1, true);
+ backend_->UpdateUrlForUkmSource(kSourceId2, kUrl2, true);
+ backend_->UpdateUrlForUkmSource(kSourceId3, kUrl3, false);
+ backend_->StoreUkmEntry(std::move(entry1));
+ backend_->StoreUkmEntry(std::move(entry2));
+ backend_->StoreUkmEntry(std::move(entry3));
+ backend_->StoreUkmEntry(std::move(entry4));
+
+ test_util::AssertUrlsInTable(backend_->db(),
+ {UrlMatcher{.url_id = kUrlId1, .url = kUrl1},
+ UrlMatcher{.url_id = kUrlId2, .url = kUrl2}});
+ DatabaseStats stats1 = GetDatabaseStats(backend_->db());
+ EXPECT_EQ(stats1.total_metrics, 12);
+ EXPECT_EQ(stats1.metric_count_for_event_id.size(), 4u);
+ EXPECT_EQ(stats1.metric_count_for_url_id.size(), 4u);
+
+ // Only one event with 3 metrics and without URL is left.
+ backend_->RemoveUrls({}, /*all_urls=*/true);
+ test_util::AssertUrlsInTable(backend_->db(), {});
+ DatabaseStats stats2 = GetDatabaseStats(backend_->db());
+ EXPECT_EQ(stats2.total_metrics, 3);
+ EXPECT_EQ(stats2.metric_count_for_event_id.size(), 1u);
+ const base::flat_map<UrlId, int> no_url_metrics({{UrlId(), 3}});
+ EXPECT_EQ(stats2.metric_count_for_url_id, no_url_metrics);
+
+ // Delete on table with all metrics without URL ID does nothing.
+ backend_->RemoveUrls({}, /*all_urls=*/true);
+ test_util::AssertUrlsInTable(backend_->db(), {});
+ DatabaseStats stats3 = GetDatabaseStats(backend_->db());
+ EXPECT_EQ(stats3.total_metrics, 3);
+ EXPECT_EQ(stats3.metric_count_for_event_id.size(), 1u);
+ EXPECT_EQ(stats2.metric_count_for_url_id, no_url_metrics);
+}
+
+TEST_F(UkmDatabaseBackendTest, DeleteOldEntries) {
+ const GURL kUrl1("https://www.url1.com");
+ const GURL kUrl2("https://www.url2.com");
+ const GURL kUrl3("https://www.url3.com");
+ const GURL kUrl4("https://www.url4.com");
+ const UrlId kUrlId1 = UkmUrlTable::GenerateUrlId(kUrl1);
+ const UrlId kUrlId2 = UkmUrlTable::GenerateUrlId(kUrl2);
+ const UrlId kUrlId3 = UkmUrlTable::GenerateUrlId(kUrl3);
+ const UrlId kUrlId4 = UkmUrlTable::GenerateUrlId(kUrl4);
+ const ukm::SourceId kSourceId1 = 10;
+ const ukm::SourceId kSourceId2 = 20;
+ const ukm::SourceId kSourceId3 = 30;
+ const ukm::SourceId kSourceId4 = 40;
+ const ukm::SourceId kSourceId5 = 50;
+
+ ukm::mojom::UkmEntryPtr entry1 = GetSampleUkmEntry(kSourceId1);
+ ukm::mojom::UkmEntryPtr entry2 = GetSampleUkmEntry(kSourceId2);
+ ukm::mojom::UkmEntryPtr entry3 = GetSampleUkmEntry(kSourceId3);
+ ukm::mojom::UkmEntryPtr entry4 = GetSampleUkmEntry(kSourceId4);
+
+ backend_->UpdateUrlForUkmSource(kSourceId1, kUrl1, true);
+ backend_->UpdateUrlForUkmSource(kSourceId2, kUrl2, true);
+ backend_->UpdateUrlForUkmSource(kSourceId3, kUrl3, true);
+ backend_->UpdateUrlForUkmSource(kSourceId4, kUrl1, true);
+ backend_->UpdateUrlForUkmSource(kSourceId5, kUrl4, true);
+ backend_->StoreUkmEntry(std::move(entry1));
+ backend_->StoreUkmEntry(std::move(entry2));
+ backend_->StoreUkmEntry(std::move(entry3));
+ backend_->StoreUkmEntry(std::move(entry4));
+
+ test_util::AssertUrlsInTable(backend_->db(),
+ {
+ UrlMatcher{.url_id = kUrlId1, .url = kUrl1},
+ UrlMatcher{.url_id = kUrlId2, .url = kUrl2},
+ UrlMatcher{.url_id = kUrlId3, .url = kUrl3},
+ UrlMatcher{.url_id = kUrlId4, .url = kUrl4},
+ });
+
+ backend_->DeleteEntriesOlderThan(base::Time::Max());
+ test_util::AssertUrlsInTable(backend_->db(), {});
+}
+
+TEST_F(UkmDatabaseBackendTest, ReadOnlyQueries) {
+ const GURL kUrl1("https://www.url1.com");
+ const GURL kUrl2("https://www.url2.com");
+ const GURL kUrl3("https://www.url3.com");
+ const UrlId kUrlId2 = UkmUrlTable::GenerateUrlId(kUrl2);
+ const ukm::SourceId kSourceId1 = 10;
+ const ukm::SourceId kSourceId2 = 20;
+ const ukm::SourceId kSourceId3 = 30;
+ const ukm::SourceId kSourceId4 = 40;
+
+ ukm::mojom::UkmEntryPtr entry1 = GetSampleUkmEntry(kSourceId1);
+ ukm::mojom::UkmEntryPtr entry2 = GetSampleUkmEntry(kSourceId2);
+ ukm::mojom::UkmEntryPtr entry3 = GetSampleUkmEntry(kSourceId3);
+ ukm::mojom::UkmEntryPtr entry4 = GetSampleUkmEntry(kSourceId4);
+
+ backend_->UpdateUrlForUkmSource(kSourceId1, kUrl1, true);
+ base::Time after1 = base::Time::Now();
+ backend_->UpdateUrlForUkmSource(kSourceId2, kUrl2, true);
+ backend_->UpdateUrlForUkmSource(kSourceId3, kUrl3, true);
+ backend_->UpdateUrlForUkmSource(kSourceId4, kUrl1, true);
+ backend_->StoreUkmEntry(std::move(entry1));
+ backend_->StoreUkmEntry(std::move(entry2));
+ backend_->StoreUkmEntry(std::move(entry3));
+ backend_->StoreUkmEntry(std::move(entry4));
+
+ UkmDatabase::QueryList queries;
+ queries.emplace(0, UkmDatabase::CustomSqlQuery(
+ "SELECT AVG(metric_value) FROM metrics",
+ std::vector<processing::ProcessedValue>()));
+ ExpectQueryResult(std::move(queries), true,
+ {{0, {processing::ProcessedValue(101.00f)}}});
+
+ constexpr char kBindValuesQuery[] =
+ // clang-format off
+ "SELECT CASE WHEN ? THEN SUM(metric_value) ELSE AVG(metric_value) END "
+ "FROM metrics m "
+ "LEFT JOIN urls u "
+ "ON m.url_id = u.url_id "
+ "WHERE "
+ "ukm_source_id/2=? "
+ "AND metric_hash=? "
+ "AND url=? "
+ "AND u.url_id=? "
+ "AND event_timestamp>=?";
+ // clang-format on
+ std::vector<processing::ProcessedValue> bind_values{
+ processing::ProcessedValue(true),
+ processing::ProcessedValue(10.00),
+ processing::ProcessedValue(std::string("1E")),
+ processing::ProcessedValue(std::string("https://www.url2.com/")),
+ processing::ProcessedValue(
+ static_cast<int64_t>(kUrlId2.GetUnsafeValue())),
+ processing::ProcessedValue(after1)};
+ UkmDatabase::QueryList queries2;
+ queries2.emplace(0, UkmDatabase::CustomSqlQuery(
+ "SELECT AVG(metric_value) FROM metrics",
+ std::vector<processing::ProcessedValue>()));
+ queries2.emplace(
+ 1, UkmDatabase::CustomSqlQuery(kBindValuesQuery, std::move(bind_values)));
+
+ ExpectQueryResult(std::move(queries2), true,
+ {{0, {processing::ProcessedValue(101.00f)}},
+ {1, {processing::ProcessedValue(100.00f)}}});
+
+ UkmDatabase::QueryList queries3;
+ queries3.emplace(0, UkmDatabase::CustomSqlQuery("SELECT bad query", {}));
+ ExpectQueryResult(std::move(queries3), false, {});
+
+ UkmDatabase::QueryList queries4;
+ queries4.emplace(
+ 0, UkmDatabase::CustomSqlQuery(
+ "SELECT metric_value FROM metrics WHERE metric_hash=?", {}));
+ ExpectQueryResult(std::move(queries4), false, {});
+
+ UkmDatabase::QueryList queries5;
+ queries5.emplace(0, UkmDatabase::CustomSqlQuery("DROP TABLE metrics", {}));
+ ExpectQueryResult(std::move(queries5), false, {});
+
+ // Database should not have changed.
+ DatabaseStats stats = GetDatabaseStats(backend_->db());
+ EXPECT_EQ(12, stats.total_metrics);
+ EXPECT_EQ(4u, stats.metric_count_for_event_id.size());
+
+ UkmDatabase::QueryList queries6;
+ queries6.emplace(
+ 0, UkmDatabase::CustomSqlQuery(
+ "INSERT INTO urls(url_id, url) VALUES(1,'not_added')", {}));
+ ExpectQueryResult(std::move(queries6), false, {});
+
+ // Database should not have changed.
+ DatabaseStats stats1 = GetDatabaseStats(backend_->db());
+ EXPECT_EQ(12, stats1.total_metrics);
+ EXPECT_EQ(4u, stats1.metric_count_for_event_id.size());
+}
+
+class FailedUkmDatabaseTest : public UkmDatabaseBackendTest {
+ public:
+ void SetUp() override {
+ task_runner_ = base::ThreadPool::CreateSequencedTaskRunner({});
+ backend_ = std::make_unique<UkmDatabaseBackend>(
+ base::FilePath(kBadFilePath), task_runner_);
+ base::RunLoop wait_for_init;
+ backend_->InitDatabase(base::BindOnce(
+ [](base::OnceClosure quit,
+ scoped_refptr<base::SequencedTaskRunner> task_runner, bool success) {
+ EXPECT_TRUE(task_runner->RunsTasksInCurrentSequence());
+ std::move(quit).Run();
+ ASSERT_FALSE(success);
+ },
+ wait_for_init.QuitClosure(), task_runner_));
+ wait_for_init.Run();
+ }
+ void TearDown() override {
+ backend_.reset();
+ task_runner_.reset();
+ }
+};
+
+TEST_F(FailedUkmDatabaseTest, QueriesAreNoop) {
+ const GURL kUrl1("https://www.url1.com");
+ backend_->OnUrlValidated(kUrl1);
+ backend_->StoreUkmEntry(GetSampleUkmEntry());
+ backend_->UpdateUrlForUkmSource(10, kUrl1, true);
+ backend_->RemoveUrls({kUrl1}, /*all_urls=*/false);
+ backend_->RemoveUrls({kUrl1}, /*all_urls=*/true);
+
+ UkmDatabase::QueryList queries;
+ queries.emplace(0, UkmDatabase::CustomSqlQuery("SELECT bad query", {}));
+ ExpectQueryResult(std::move(queries), false, {});
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_database_impl.cc b/chromium/components/segmentation_platform/internal/database/ukm_database_impl.cc
new file mode 100644
index 00000000000..3ba44ce81a3
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/database/ukm_database_impl.cc
@@ -0,0 +1,80 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/database/ukm_database_impl.h"
+
+#include "base/task/thread_pool.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/segmentation_platform/internal/database/ukm_database_backend.h"
+
+namespace segmentation_platform {
+
+UkmDatabaseImpl::UkmDatabaseImpl(const base::FilePath& database_path)
+ : task_runner_(base::SequencedTaskRunnerHandle::Get()),
+ backend_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+ {base::MayBlock(), base::TaskPriority::USER_VISIBLE})),
+ backend_(std::make_unique<UkmDatabaseBackend>(
+ database_path,
+ base::SequencedTaskRunnerHandle::Get())) {}
+
+UkmDatabaseImpl::~UkmDatabaseImpl() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ backend_task_runner_->DeleteSoon(FROM_HERE, std::move(backend_));
+}
+
+void UkmDatabaseImpl::InitDatabase(SuccessCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ backend_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&UkmDatabaseBackend::InitDatabase,
+ backend_->GetWeakPtr(), std::move(callback)));
+}
+
+void UkmDatabaseImpl::StoreUkmEntry(ukm::mojom::UkmEntryPtr ukm_entry) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ backend_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&UkmDatabaseBackend::StoreUkmEntry,
+ backend_->GetWeakPtr(), std::move(ukm_entry)));
+}
+
+void UkmDatabaseImpl::UpdateUrlForUkmSource(ukm::SourceId source_id,
+ const GURL& url,
+ bool is_validated) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ backend_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&UkmDatabaseBackend::UpdateUrlForUkmSource,
+ backend_->GetWeakPtr(), source_id, url, is_validated));
+}
+
+void UkmDatabaseImpl::OnUrlValidated(const GURL& url) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ backend_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&UkmDatabaseBackend::OnUrlValidated,
+ backend_->GetWeakPtr(), url));
+}
+
+void UkmDatabaseImpl::RemoveUrls(const std::vector<GURL>& urls, bool all_urls) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ backend_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&UkmDatabaseBackend::RemoveUrls,
+ backend_->GetWeakPtr(), urls, all_urls));
+}
+
+void UkmDatabaseImpl::RunReadonlyQueries(QueryList&& queries,
+ QueryCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ backend_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&UkmDatabaseBackend::RunReadonlyQueries,
+ backend_->GetWeakPtr(), std::move(queries),
+ std::move(callback)));
+}
+
+void UkmDatabaseImpl::DeleteEntriesOlderThan(base::Time time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ backend_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&UkmDatabaseBackend::DeleteEntriesOlderThan,
+ backend_->GetWeakPtr(), time));
+}
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_database_impl.h b/chromium/components/segmentation_platform/internal/database/ukm_database_impl.h
new file mode 100644
index 00000000000..7f33707dd86
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/database/ukm_database_impl.h
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_UKM_DATABASE_IMPL_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_UKM_DATABASE_IMPL_H_
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/time/time.h"
+#include "components/segmentation_platform/internal/database/ukm_database.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "services/metrics/public/mojom/ukm_interface.mojom.h"
+#include "url/gurl.h"
+
+namespace segmentation_platform {
+
+class UkmDatabaseBackend;
+
+class UkmDatabaseImpl : public UkmDatabase {
+ public:
+ explicit UkmDatabaseImpl(const base::FilePath& database_path);
+ ~UkmDatabaseImpl() override;
+
+ UkmDatabaseImpl(UkmDatabaseImpl&) = delete;
+ UkmDatabaseImpl& operator=(UkmDatabaseImpl&) = delete;
+
+ void InitDatabase(SuccessCallback callback) override;
+ void StoreUkmEntry(ukm::mojom::UkmEntryPtr ukm_entry) override;
+ void UpdateUrlForUkmSource(ukm::SourceId source_id,
+ const GURL& url,
+ bool is_validated) override;
+ void OnUrlValidated(const GURL& url) override;
+ void RemoveUrls(const std::vector<GURL>& urls, bool all_urls) override;
+ void RunReadonlyQueries(QueryList&& queries, QueryCallback callback) override;
+ void DeleteEntriesOlderThan(base::Time time) override;
+
+ private:
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
+ std::unique_ptr<UkmDatabaseBackend> backend_;
+ SEQUENCE_CHECKER(sequence_checker_);
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_UKM_DATABASE_IMPL_H_
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_metrics_table.cc b/chromium/components/segmentation_platform/internal/database/ukm_metrics_table.cc
index bbf1e84b7cf..fe8b038e96b 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_metrics_table.cc
+++ b/chromium/components/segmentation_platform/internal/database/ukm_metrics_table.cc
@@ -17,9 +17,8 @@
namespace segmentation_platform {
-namespace {} // namespace
-
UkmMetricsTable::UkmMetricsTable(sql::Database* db) : db_(db) {
+ DETACH_FROM_SEQUENCE(sequence_checker_);
DCHECK(db_);
}
@@ -146,14 +145,52 @@ bool UkmMetricsTable::DeleteEventsForUrls(const std::vector<UrlId>& urls) {
return success;
}
-bool UkmMetricsTable::DeleteEventsBeforeTimestamp(base::Time time) {
+std::vector<UrlId> UkmMetricsTable::DeleteEventsBeforeTimestamp(
+ base::Time time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ std::vector<UrlId> url_list;
+
+ // Get a list of URL IDs of the metrics that will be removed.
+ static constexpr char kGetOldEntries[] =
+ "SELECT DISTINCT url_id FROM metrics WHERE event_timestamp<=? ORDER BY "
+ "url_id";
+ sql::Statement find_statement(
+ db_->GetCachedStatement(SQL_FROM_HERE, kGetOldEntries));
+ find_statement.BindTime(0, time);
+ while (find_statement.Step()) {
+ url_list.push_back(UrlId::FromUnsafeValue(find_statement.ColumnInt64(0)));
+ }
+
+ // Delete the metrics.
static constexpr char kDeleteoldEntries[] =
- "DELETE FROM metrics WHERE event_timestamp <= ?";
+ "DELETE FROM metrics WHERE event_timestamp<=?";
sql::Statement statement(
db_->GetCachedStatement(SQL_FROM_HERE, kDeleteoldEntries));
statement.BindTime(0, time);
- return statement.Run();
+ if (!statement.Run()) {
+ return {};
+ }
+
+ // Find the list of URL IDs that are no longer needed in the URL table by
+ // checking if there are any other metrics referring to the removed URL IDs.
+ for (auto it = url_list.begin(); it != url_list.end();) {
+ if (HasEntriesWithUrl(*it)) {
+ it = url_list.erase(it);
+ } else {
+ it++;
+ }
+ }
+ return url_list;
+}
+
+bool UkmMetricsTable::HasEntriesWithUrl(UrlId url_id) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ static constexpr char kGetUrlQuery[] =
+ "SELECT 1 FROM metrics WHERE url_id=? LIMIT 1";
+ sql::Statement statement(
+ db_->GetCachedStatement(SQL_FROM_HERE, kGetUrlQuery));
+ statement.BindInt64(0, url_id.GetUnsafeValue());
+ return statement.Step();
}
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_metrics_table.h b/chromium/components/segmentation_platform/internal/database/ukm_metrics_table.h
index 061fa160a5b..107873c7b32 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_metrics_table.h
+++ b/chromium/components/segmentation_platform/internal/database/ukm_metrics_table.h
@@ -31,9 +31,6 @@ class UkmMetricsTable {
// Represents a row in the metrics table.
struct MetricsRow {
- MetricsRow() = default;
- ~MetricsRow() = default;
-
// Timestamp of the event, all the metrics in the event will have the same
// timestamp. The timestamp is approximate and generated by the database
// when getting notifications about UKM. Timestamps are required since its
@@ -76,10 +73,14 @@ class UkmMetricsTable {
// Deletes all rows associated with any of the ID from |urls|.
bool DeleteEventsForUrls(const std::vector<UrlId>& urls);
- // Deletes all entries that have an event timestamp earlier to |time|.
- bool DeleteEventsBeforeTimestamp(base::Time time);
+ // Deletes all entries that have an event timestamp earlier or equal to
+ // `time`. Returns a list of URL IDs that were removed by this task and no
+ // longer referred to by any other metrics.
+ std::vector<UrlId> DeleteEventsBeforeTimestamp(base::Time time);
private:
+ bool HasEntriesWithUrl(UrlId url_id);
+
SEQUENCE_CHECKER(sequence_checker_);
const raw_ptr<sql::Database> db_ GUARDED_BY_CONTEXT(sequence_checker_);
};
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_metrics_table_unittest.cc b/chromium/components/segmentation_platform/internal/database/ukm_metrics_table_unittest.cc
index 06047850851..5fce48d3ed9 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_metrics_table_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/database/ukm_metrics_table_unittest.cc
@@ -158,56 +158,84 @@ TEST_F(UkmMetricsTableTest, DeleteBeforeTimestamp) {
const base::Time kTimestamp3 = kTimestamp1 + base::Seconds(2);
const base::Time kTimestamp4 = kTimestamp1 + base::Seconds(3);
const base::Time kTimestamp5 = kTimestamp1 + base::Seconds(4);
+ const UrlId kUrl1 = UrlId::FromUnsafeValue(1);
+ const UrlId kUrl2 = UrlId::FromUnsafeValue(2);
+ const UrlId kUrl3 = UrlId::FromUnsafeValue(3);
+ const UrlId kUrl4 = UrlId::FromUnsafeValue(4);
ASSERT_TRUE(metrics_table().InitTable());
// Delete on empty table does nothing.
- EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(base::Time()));
+ std::vector<UrlId> empty_set;
+ EXPECT_EQ(empty_set,
+ metrics_table().DeleteEventsBeforeTimestamp(base::Time()));
test_util::AssertRowsInMetricsTable(db(), {});
- EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp1));
+ EXPECT_EQ(empty_set,
+ metrics_table().DeleteEventsBeforeTimestamp(kTimestamp1));
test_util::AssertRowsInMetricsTable(db(), {});
auto row1 = GetSampleMetricsRow();
row1.event_timestamp = kTimestamp1;
+ row1.url_id = kUrl1;
EXPECT_TRUE(metrics_table().AddUkmEvent(row1));
auto row2 = GetSampleMetricsRow();
row2.event_timestamp = kTimestamp2;
+ row2.url_id = kUrl2;
EXPECT_TRUE(metrics_table().AddUkmEvent(row2));
auto row3 = GetSampleMetricsRow();
row3.event_timestamp = kTimestamp3;
+ row3.url_id = kUrl3;
EXPECT_TRUE(metrics_table().AddUkmEvent(row3));
auto row4 = GetSampleMetricsRow();
row4.event_timestamp = kTimestamp4;
+ row4.url_id = kUrl4;
EXPECT_TRUE(metrics_table().AddUkmEvent(row4));
test_util::AssertRowsInMetricsTable(db(), {row1, row2, row3, row4});
// Delete with time before all rows does nothing.
- EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(base::Time()));
+ EXPECT_EQ(empty_set,
+ metrics_table().DeleteEventsBeforeTimestamp(base::Time()));
test_util::AssertRowsInMetricsTable(db(), {row1, row2, row3, row4});
- EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp1 -
- base::Seconds(1)));
+ EXPECT_EQ(empty_set, metrics_table().DeleteEventsBeforeTimestamp(
+ kTimestamp1 - base::Seconds(1)));
test_util::AssertRowsInMetricsTable(db(), {row1, row2, row3, row4});
// Remove single row.
- EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp1));
+ EXPECT_EQ(std::vector<UrlId>({kUrl1}),
+ metrics_table().DeleteEventsBeforeTimestamp(kTimestamp1));
test_util::AssertRowsInMetricsTable(db(), {row2, row3, row4});
+ // Add more rows with UrlId3.
auto row5 = GetSampleMetricsRow();
row5.event_timestamp = kTimestamp5;
+ row5.url_id = kUrl3;
EXPECT_TRUE(metrics_table().AddUkmEvent(row5));
- test_util::AssertRowsInMetricsTable(db(), {row2, row3, row4, row5});
+ auto row6 = GetSampleMetricsRow();
+ row6.event_timestamp = kTimestamp5;
+ row6.url_id = kUrl3;
+ EXPECT_TRUE(metrics_table().AddUkmEvent(row6));
+ test_util::AssertRowsInMetricsTable(db(), {row2, row3, row4, row5, row6});
- // Remove bunch of rows.
- EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp3));
- test_util::AssertRowsInMetricsTable(db(), {row4, row5});
+ // Remove bunch of rows. UrlId3 should not be part of removed list since 2
+ // other metrics reference it.
+ EXPECT_EQ(std::vector<UrlId>({kUrl2, kUrl4}),
+ metrics_table().DeleteEventsBeforeTimestamp(kTimestamp4));
+ test_util::AssertRowsInMetricsTable(db(), {row5, row6});
// Insert entry with an older timestamp out of order and remove old entries
// should still work.
EXPECT_TRUE(metrics_table().AddUkmEvent(row1));
- test_util::AssertRowsInMetricsTable(db(), {row4, row5, row1});
- EXPECT_TRUE(metrics_table().DeleteEventsBeforeTimestamp(kTimestamp3));
- test_util::AssertRowsInMetricsTable(db(), {row4, row5});
+ test_util::AssertRowsInMetricsTable(db(), {row5, row6, row1});
+ EXPECT_EQ(std::vector<UrlId>({kUrl1}),
+ metrics_table().DeleteEventsBeforeTimestamp(kTimestamp4));
+ test_util::AssertRowsInMetricsTable(db(), {row5, row6});
+
+ // Removing multiple entries with same timestamp and url should return the
+ // right url to be removed.
+ EXPECT_EQ(std::vector<UrlId>({kUrl3}),
+ metrics_table().DeleteEventsBeforeTimestamp(kTimestamp5));
+ test_util::AssertRowsInMetricsTable(db(), {});
}
TEST_F(UkmMetricsTableTest, MatchHashesTest) {
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_types.cc b/chromium/components/segmentation_platform/internal/database/ukm_types.cc
new file mode 100644
index 00000000000..8786b771361
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/database/ukm_types.cc
@@ -0,0 +1,48 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/database/ukm_types.h"
+
+namespace segmentation_platform::processing {
+
+ProcessedValue::ProcessedValue(bool val) : type(Type::BOOL), bool_val(val) {}
+ProcessedValue::ProcessedValue(int val) : type(Type::INT), int_val(val) {}
+ProcessedValue::ProcessedValue(float val) : type(Type::FLOAT), float_val(val) {}
+ProcessedValue::ProcessedValue(double val)
+ : type(Type::DOUBLE), double_val(val) {}
+ProcessedValue::ProcessedValue(const std::string& val)
+ : type(Type::STRING), str_val(val) {}
+ProcessedValue::ProcessedValue(base::Time val)
+ : type(Type::TIME), time_val(val) {}
+ProcessedValue::ProcessedValue(int64_t val)
+ : type(Type::INT64), int64_val(val) {}
+
+ProcessedValue::ProcessedValue(const ProcessedValue& other) = default;
+ProcessedValue& ProcessedValue::operator=(const ProcessedValue& other) =
+ default;
+
+bool ProcessedValue::operator==(const ProcessedValue& rhs) const {
+ if (type != rhs.type)
+ return false;
+ switch (type) {
+ case Type::UNKNOWN:
+ return false;
+ case Type::BOOL:
+ return bool_val == rhs.bool_val;
+ case Type::INT:
+ return int_val == rhs.int_val;
+ case Type::FLOAT:
+ return float_val == rhs.float_val;
+ case Type::DOUBLE:
+ return double_val == rhs.double_val;
+ case Type::STRING:
+ return str_val == rhs.str_val;
+ case Type::TIME:
+ return time_val == rhs.time_val;
+ case Type::INT64:
+ return int64_val == rhs.int64_val;
+ }
+}
+
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_types.h b/chromium/components/segmentation_platform/internal/database/ukm_types.h
index 3f66f43d9e9..5b94a590c76 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_types.h
+++ b/chromium/components/segmentation_platform/internal/database/ukm_types.h
@@ -7,6 +7,9 @@
#include <cstdint>
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
#include "base/time/time.h"
#include "base/types/id_type.h"
@@ -19,37 +22,27 @@ using UkmEventHash = base::IdTypeU64<class UkmEventHashTag>;
using UkmMetricHash = base::IdTypeU64<class UkmMetricHashTag>;
using UrlId = base::IdType64<class UrlIdTag>;
+using UkmEventsToMetricsMap =
+ base::flat_map<UkmEventHash, base::flat_set<UkmMetricHash>>;
+
+namespace processing {
+
// A struct that can accommodate multiple output types needed for Segmentation
// metadata's feature processing. It can only hold one value at a time with the
// corresponding type.
struct ProcessedValue {
- explicit ProcessedValue(bool val) : type(Type::BOOL), bool_val(val) {}
- explicit ProcessedValue(int val) : type(Type::INT), int_val(val) {}
- explicit ProcessedValue(float val) : type(Type::FLOAT), float_val(val) {}
- explicit ProcessedValue(double val) : type(Type::DOUBLE), double_val(val) {}
- explicit ProcessedValue(std::string val) : type(Type::STRING), str_val(val) {}
- explicit ProcessedValue(base::Time val) : type(Type::TIME), time_val(val) {}
-
- bool operator==(const ProcessedValue& rhs) const {
- if (type != rhs.type)
- return false;
- switch (type) {
- case Type::BOOL:
- return bool_val == rhs.bool_val;
- case Type::INT:
- return int_val == rhs.int_val;
- case Type::FLOAT:
- return float_val == rhs.float_val;
- case Type::DOUBLE:
- return double_val == rhs.double_val;
- case Type::STRING:
- return str_val == rhs.str_val;
- case Type::TIME:
- return time_val == rhs.time_val;
- default:
- return false;
- }
- }
+ explicit ProcessedValue(bool val);
+ explicit ProcessedValue(int val);
+ explicit ProcessedValue(float val);
+ explicit ProcessedValue(double val);
+ explicit ProcessedValue(const std::string& val);
+ explicit ProcessedValue(base::Time val);
+ explicit ProcessedValue(int64_t val);
+
+ ProcessedValue(const ProcessedValue& other);
+ ProcessedValue& operator=(const ProcessedValue& other);
+
+ bool operator==(const ProcessedValue& rhs) const;
enum Type {
UNKNOWN = 0,
@@ -59,6 +52,7 @@ struct ProcessedValue {
DOUBLE = 4,
STRING = 5,
TIME = 6,
+ INT64 = 7,
};
Type type{UNKNOWN};
bool bool_val{false};
@@ -67,8 +61,18 @@ struct ProcessedValue {
double double_val{0};
std::string str_val;
base::Time time_val;
+ int64_t int64_val{0};
};
+// Represents a set of values that can represent inputs or outputs for a model.
+using Tensor = std::vector<ProcessedValue>;
+
+// Intermediate representation of processed features from the metadata queries.
+using FeatureIndex = int;
+using IndexedTensors = base::flat_map<FeatureIndex, Tensor>;
+
+} // namespace processing
+
} // namespace segmentation_platform
#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_UKM_TYPES_H_
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_url_table.cc b/chromium/components/segmentation_platform/internal/database/ukm_url_table.cc
index c6a601c1fe7..a91ef06256f 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_url_table.cc
+++ b/chromium/components/segmentation_platform/internal/database/ukm_url_table.cc
@@ -16,6 +16,7 @@
namespace segmentation_platform {
UkmUrlTable::UkmUrlTable(sql::Database* db) : db_(db) {
+ DETACH_FROM_SEQUENCE(sequence_checker_);
DCHECK(db_);
}
@@ -43,6 +44,8 @@ bool UkmUrlTable::InitTable() {
"CREATE TABLE urls("
"url_id INTEGER PRIMARY KEY NOT NULL,"
"url TEXT NOT NULL,"
+ "last_timestamp INTEGER NOT NULL,"
+ "counter INTEGER,"
"title TEXT)";
// clang-format on
return db_->Execute(kCreateTableQuery);
@@ -50,26 +53,39 @@ bool UkmUrlTable::InitTable() {
bool UkmUrlTable::IsUrlInTable(UrlId url_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- static constexpr char kGetUrlQuery[] = "SELECT 1 FROM urls WHERE url_id = ?";
+ static constexpr char kGetUrlQuery[] = "SELECT 1 FROM urls WHERE url_id=?";
sql::Statement statement(
db_->GetCachedStatement(SQL_FROM_HERE, kGetUrlQuery));
statement.BindInt64(0, url_id.GetUnsafeValue());
return statement.Step();
}
-bool UkmUrlTable::WriteUrl(const GURL& url, UrlId url_id) {
+bool UkmUrlTable::WriteUrl(const GURL& url,
+ UrlId url_id,
+ base::Time timestamp) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
static constexpr char kWriteQuery[] =
- "INSERT INTO urls(url_id, url) VALUES(?,?)";
+ "INSERT INTO urls(url_id,url,last_timestamp) VALUES(?,?,?)";
sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kWriteQuery));
statement.BindInt64(0, url_id.GetUnsafeValue());
statement.BindString(1, database_utils::GurlToDatabaseUrl(url));
+ statement.BindTime(2, timestamp);
+ return statement.Run();
+}
+
+bool UkmUrlTable::UpdateUrlTimestamp(UrlId url_id, base::Time timestamp) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ static constexpr char kWriteQuery[] =
+ "UPDATE urls SET last_timestamp=? WHERE url_id=?";
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kWriteQuery));
+ statement.BindTime(0, timestamp);
+ statement.BindInt64(1, url_id.GetUnsafeValue());
return statement.Run();
}
bool UkmUrlTable::RemoveUrls(const std::vector<UrlId>& urls) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- static constexpr char kDeleteQuery[] = "DELETE FROM urls WHERE url_id = ?";
+ static constexpr char kDeleteQuery[] = "DELETE FROM urls WHERE url_id=?";
sql::Statement statement(
db_->GetCachedStatement(SQL_FROM_HERE, kDeleteQuery));
bool success = true;
@@ -82,4 +98,15 @@ bool UkmUrlTable::RemoveUrls(const std::vector<UrlId>& urls) {
return success;
}
+bool UkmUrlTable::DeleteUrlsBeforeTimestamp(base::Time time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // Delete the metrics.
+ static constexpr char kDeleteoldEntries[] =
+ "DELETE FROM urls WHERE last_timestamp<=?";
+ sql::Statement statement(
+ db_->GetCachedStatement(SQL_FROM_HERE, kDeleteoldEntries));
+ statement.BindTime(0, time);
+ return statement.Run();
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_url_table.h b/chromium/components/segmentation_platform/internal/database/ukm_url_table.h
index b8047eb1d4d..7e08977937b 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_url_table.h
+++ b/chromium/components/segmentation_platform/internal/database/ukm_url_table.h
@@ -38,11 +38,18 @@ class UkmUrlTable {
// Writes `url` to database with `url_id`. It is invalid to call this method
// when `url_id` exists in the database.
- bool WriteUrl(const GURL& url, UrlId url_id);
+ bool WriteUrl(const GURL& url, UrlId url_id, base::Time timestamp);
+
+ // Update the last used timestamp for the URL.
+ bool UpdateUrlTimestamp(UrlId url_id, base::Time timestamp);
// Removes all the URLs in `urls`.
bool RemoveUrls(const std::vector<UrlId>& urls);
+ // Delete URLs whose last used timestamps were earlier than or equal to
+ // `time`.
+ bool DeleteUrlsBeforeTimestamp(base::Time time);
+
private:
raw_ptr<sql::Database> db_ GUARDED_BY_CONTEXT(sequence_checker_);
SEQUENCE_CHECKER(sequence_checker_);
diff --git a/chromium/components/segmentation_platform/internal/database/ukm_url_table_unittest.cc b/chromium/components/segmentation_platform/internal/database/ukm_url_table_unittest.cc
index 7c4e36b0f46..6d90b64f415 100644
--- a/chromium/components/segmentation_platform/internal/database/ukm_url_table_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/database/ukm_url_table_unittest.cc
@@ -54,6 +54,7 @@ TEST_F(UkmUrlTableTest, CreateTable) {
TEST_F(UkmUrlTableTest, InsertUrl) {
const GURL kUrl("https://www.url1.com");
+ const base::Time kTimestamp1 = base::Time::Now();
auto url_id_generator = UrlId::Generator();
const UrlId kUrlId1 = url_id_generator.GenerateNextId();
const UrlId kUrlId2 = url_id_generator.GenerateNextId();
@@ -62,12 +63,12 @@ TEST_F(UkmUrlTableTest, InsertUrl) {
ASSERT_TRUE(url_table_->InitTable());
EXPECT_FALSE(url_table_->IsUrlInTable(kUrlId1));
- EXPECT_TRUE(url_table_->WriteUrl(kUrl, kUrlId1));
+ EXPECT_TRUE(url_table_->WriteUrl(kUrl, kUrlId1, kTimestamp1));
EXPECT_TRUE(url_table_->IsUrlInTable(kUrlId1));
{
sql::test::ScopedErrorExpecter error_expector;
error_expector.ExpectError(SQLITE_CONSTRAINT_PRIMARYKEY);
- EXPECT_FALSE(url_table_->WriteUrl(kUrl, kUrlId1));
+ EXPECT_FALSE(url_table_->WriteUrl(kUrl, kUrlId1, kTimestamp1));
ASSERT_TRUE(error_expector.SawExpectedErrors());
}
EXPECT_TRUE(url_table_->IsUrlInTable(kUrlId1));
@@ -75,8 +76,8 @@ TEST_F(UkmUrlTableTest, InsertUrl) {
test_util::AssertUrlsInTable(*db_, {UrlMatcher{kUrlId1, kUrl}});
EXPECT_FALSE(url_table_->IsUrlInTable(kUrlId2));
- EXPECT_TRUE(url_table_->WriteUrl(kUrl, kUrlId2));
- EXPECT_TRUE(url_table_->WriteUrl(kUrl, kUrlId3));
+ EXPECT_TRUE(url_table_->WriteUrl(kUrl, kUrlId2, kTimestamp1));
+ EXPECT_TRUE(url_table_->WriteUrl(kUrl, kUrlId3, kTimestamp1));
test_util::AssertUrlsInTable(
*db_, {UrlMatcher{kUrlId1, kUrl}, UrlMatcher{kUrlId2, kUrl},
@@ -102,6 +103,7 @@ TEST_F(UkmUrlTableTest, RemoveUrls) {
const GURL kUrl1("https://www.url1.com");
const GURL kUrl2("https://www.url2.com");
const GURL kUrl3("https://www.url3.com");
+ const base::Time kTimestamp1 = base::Time::Now();
const UrlId kUrlId1 = UkmUrlTable::GenerateUrlId(kUrl1);
const UrlId kUrlId2 = UkmUrlTable::GenerateUrlId(kUrl2);
const UrlId kUrlId3 = UkmUrlTable::GenerateUrlId(kUrl3);
@@ -109,9 +111,9 @@ TEST_F(UkmUrlTableTest, RemoveUrls) {
ASSERT_TRUE(url_table_->InitTable());
EXPECT_TRUE(url_table_->RemoveUrls({kUrlId1}));
- EXPECT_TRUE(url_table_->WriteUrl(kUrl1, kUrlId1));
- EXPECT_TRUE(url_table_->WriteUrl(kUrl2, kUrlId2));
- EXPECT_TRUE(url_table_->WriteUrl(kUrl3, kUrlId3));
+ EXPECT_TRUE(url_table_->WriteUrl(kUrl1, kUrlId1, kTimestamp1));
+ EXPECT_TRUE(url_table_->WriteUrl(kUrl2, kUrlId2, kTimestamp1));
+ EXPECT_TRUE(url_table_->WriteUrl(kUrl3, kUrlId3, kTimestamp1));
test_util::AssertUrlsInTable(
*db_, {UrlMatcher{kUrlId1, kUrl1}, UrlMatcher{kUrlId2, kUrl2},
@@ -125,4 +127,39 @@ TEST_F(UkmUrlTableTest, RemoveUrls) {
test_util::AssertUrlsInTable(*db_, {});
}
+TEST_F(UkmUrlTableTest, TimestampExpiration) {
+ const GURL kUrl1("https://www.url1.com");
+ const GURL kUrl2("https://www.url2.com");
+ const GURL kUrl3("https://www.url3.com");
+ const base::Time kTimestamp1 = base::Time::Now();
+ const base::Time kTimestamp2 = kTimestamp1 + base::Seconds(1);
+ const base::Time kTimestamp3 = kTimestamp1 + base::Seconds(2);
+ const UrlId kUrlId1 = UkmUrlTable::GenerateUrlId(kUrl1);
+ const UrlId kUrlId2 = UkmUrlTable::GenerateUrlId(kUrl2);
+ const UrlId kUrlId3 = UkmUrlTable::GenerateUrlId(kUrl3);
+
+ ASSERT_TRUE(url_table_->InitTable());
+ EXPECT_TRUE(url_table_->WriteUrl(kUrl1, kUrlId1, kTimestamp1));
+ EXPECT_TRUE(url_table_->WriteUrl(kUrl2, kUrlId2, kTimestamp2));
+ EXPECT_TRUE(url_table_->WriteUrl(kUrl3, kUrlId3, kTimestamp3));
+
+ test_util::AssertUrlsInTable(
+ *db_, {UrlMatcher{kUrlId1, kUrl1}, UrlMatcher{kUrlId2, kUrl2},
+ UrlMatcher{kUrlId3, kUrl3}});
+
+ // Remove one URL using timestamp equal to the earliest URL:
+ EXPECT_TRUE(url_table_->DeleteUrlsBeforeTimestamp(kTimestamp1));
+ test_util::AssertUrlsInTable(
+ *db_, {UrlMatcher{kUrlId2, kUrl2}, UrlMatcher{kUrlId3, kUrl3}});
+
+ // Update timestamps of the URLs and then expire, only the older one should be
+ // removed.
+ const base::Time kTimestamp4 = kTimestamp1 + base::Seconds(4);
+ EXPECT_TRUE(url_table_->UpdateUrlTimestamp(kUrlId2, kTimestamp4));
+ EXPECT_TRUE(url_table_->UpdateUrlTimestamp(kUrlId3, kTimestamp2));
+ EXPECT_TRUE(url_table_->DeleteUrlsBeforeTimestamp(kTimestamp3));
+
+ test_util::AssertUrlsInTable(*db_, {UrlMatcher{kUrlId2, kUrl2}});
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc b/chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
index 705edeb2746..1ea3bcd6595 100644
--- a/chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
+++ b/chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
@@ -9,6 +9,7 @@
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/segmentation_platform/internal/stats.h"
#include "components/segmentation_platform/public/segment_selection_result.h"
+#include "components/segmentation_platform/public/trigger_context.h"
namespace segmentation_platform {
@@ -31,6 +32,27 @@ SegmentSelectionResult DummySegmentationPlatformService::GetCachedSegmentResult(
return SegmentSelectionResult();
}
+CallbackId
+DummySegmentationPlatformService::RegisterOnDemandSegmentSelectionCallback(
+ const std::string& segmentation_key,
+ const OnDemandSegmentSelectionCallback& callback) {
+ return CallbackId::FromUnsafeValue(0);
+}
+
+void DummySegmentationPlatformService::
+ UnregisterOnDemandSegmentSelectionCallback(
+ CallbackId callback_id,
+ const std::string& segmentation_key) {}
+
+void DummySegmentationPlatformService::OnTrigger(
+ TriggerType trigger,
+ const TriggerContext& trigger_context) {}
+
void DummySegmentationPlatformService::EnableMetrics(
bool signal_collection_allowed) {}
+
+bool DummySegmentationPlatformService::IsPlatformInitialized() {
+ return false;
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.h b/chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.h
index 67df2fdcf59..8cf9fd80351 100644
--- a/chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.h
+++ b/chromium/components/segmentation_platform/internal/dummy_segmentation_platform_service.h
@@ -29,7 +29,16 @@ class DummySegmentationPlatformService : public SegmentationPlatformService {
SegmentSelectionCallback callback) override;
SegmentSelectionResult GetCachedSegmentResult(
const std::string& segmentation_key) override;
+ CallbackId RegisterOnDemandSegmentSelectionCallback(
+ const std::string& segmentation_key,
+ const OnDemandSegmentSelectionCallback& callback) override;
+ void UnregisterOnDemandSegmentSelectionCallback(
+ CallbackId callback_id,
+ const std::string& segmentation_key) override;
+ void OnTrigger(TriggerType trigger,
+ const TriggerContext& trigger_context) override;
void EnableMetrics(bool signal_collection_allowed) override;
+ bool IsPlatformInitialized() override;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.cc b/chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.cc
index 453b3b5a47d..9995c3e89ee 100644
--- a/chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.cc
+++ b/chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.cc
@@ -11,22 +11,17 @@ namespace segmentation_platform {
DummyUkmDataManager::DummyUkmDataManager() = default;
DummyUkmDataManager::~DummyUkmDataManager() = default;
-void DummyUkmDataManager::Initialize(const base::FilePath& database_path) {}
+void DummyUkmDataManager::Initialize(const base::FilePath& database_path,
+ UkmObserver* ukm_observer) {}
bool DummyUkmDataManager::IsUkmEngineEnabled() {
return false;
}
-void DummyUkmDataManager::NotifyCanObserveUkm(
- ukm::UkmRecorderImpl* ukm_recorder,
- PrefService* pref_service) {}
-
void DummyUkmDataManager::StartObservingUkm(const UkmConfig& config) {}
void DummyUkmDataManager::PauseOrResumeObservation(bool pause) {}
-void DummyUkmDataManager::StopObservingUkm() {}
-
UrlSignalHandler* DummyUkmDataManager::GetOrCreateUrlHandler() {
NOTREACHED();
return nullptr;
@@ -37,10 +32,13 @@ UkmDatabase* DummyUkmDataManager::GetUkmDatabase() {
return nullptr;
}
+void DummyUkmDataManager::OnEntryAdded(ukm::mojom::UkmEntryPtr entry) {}
+
+void DummyUkmDataManager::OnUkmSourceUpdated(ukm::SourceId source_id,
+ const std::vector<GURL>& urls) {}
+
void DummyUkmDataManager::AddRef() {}
void DummyUkmDataManager::RemoveRef() {}
-void DummyUkmDataManager::OnUkmAllowedStateChanged(bool allowed) {}
-
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.h b/chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.h
index bf8d87138dd..435bf6c049e 100644
--- a/chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.h
+++ b/chromium/components/segmentation_platform/internal/dummy_ukm_data_manager.h
@@ -20,18 +20,18 @@ class DummyUkmDataManager : public UkmDataManager {
DummyUkmDataManager& operator=(DummyUkmDataManager&) = delete;
// UkmDataManager implementation:
- void Initialize(const base::FilePath& database_path) override;
+ void Initialize(const base::FilePath& database_path,
+ UkmObserver* ukm_observer) override;
bool IsUkmEngineEnabled() override;
- void NotifyCanObserveUkm(ukm::UkmRecorderImpl* ukm_recorder,
- PrefService* pref_service) override;
void StartObservingUkm(const UkmConfig& config) override;
void PauseOrResumeObservation(bool pause) override;
- void StopObservingUkm() override;
UrlSignalHandler* GetOrCreateUrlHandler() override;
UkmDatabase* GetUkmDatabase() override;
+ void OnEntryAdded(ukm::mojom::UkmEntryPtr entry) override;
+ void OnUkmSourceUpdated(ukm::SourceId source_id,
+ const std::vector<GURL>& urls) override;
void AddRef() override;
void RemoveRef() override;
- void OnUkmAllowedStateChanged(bool allowed) override;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/execution/custom_input_processor.cc b/chromium/components/segmentation_platform/internal/execution/custom_input_processor.cc
deleted file mode 100644
index 329509749ed..00000000000
--- a/chromium/components/segmentation_platform/internal/execution/custom_input_processor.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/custom_input_processor.h"
-
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
-#include "components/segmentation_platform/internal/database/ukm_types.h"
-#include "components/segmentation_platform/internal/execution/feature_processor_state.h"
-
-namespace segmentation_platform {
-
-namespace {
-// Index not actually used for legacy code in FeatureQueryProcessor.
-const int kIndexNotUsed = 0;
-} // namespace
-
-CustomInputProcessor::CustomInputProcessor() = default;
-
-CustomInputProcessor::CustomInputProcessor(const base::Time prediction_time)
- : prediction_time_(prediction_time) {}
-
-CustomInputProcessor::CustomInputProcessor(
- base::flat_map<FeatureIndex, proto::CustomInput>&& custom_inputs,
- const base::Time prediction_time)
- : custom_inputs_(std::move(custom_inputs)),
- prediction_time_(prediction_time) {}
-
-CustomInputProcessor::~CustomInputProcessor() = default;
-
-void CustomInputProcessor::ProcessCustomInput(
- const proto::CustomInput& custom_input,
- std::unique_ptr<FeatureProcessorState> feature_processor_state,
- FeatureListQueryProcessorCallback callback) {
- DCHECK(custom_inputs_.empty());
- prediction_time_ = feature_processor_state->prediction_time();
- custom_inputs_[kIndexNotUsed] = custom_input;
- Process(std::move(feature_processor_state),
- base::BindOnce(&CustomInputProcessor::OnFinishProcessing,
- weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void CustomInputProcessor::OnFinishProcessing(
- FeatureListQueryProcessorCallback callback,
- std::unique_ptr<FeatureProcessorState> feature_processor_state,
- IndexedTensors result) {
- custom_inputs_.clear();
- feature_processor_state->AppendInputTensor(result[kIndexNotUsed]);
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback), std::move(feature_processor_state)));
-}
-
-void CustomInputProcessor::Process(
- std::unique_ptr<FeatureProcessorState> feature_processor_state,
- QueryProcessorCallback callback) {
- ProcessIndexType<FeatureIndex>(std::move(custom_inputs_),
- std::move(feature_processor_state),
- std::move(callback));
-}
-
-template <typename IndexType>
-void CustomInputProcessor::ProcessIndexType(
- base::flat_map<IndexType, proto::CustomInput> custom_inputs,
- std::unique_ptr<FeatureProcessorState> feature_processor_state,
- TemplateCallback<IndexType> callback) {
- base::flat_map<IndexType, Tensor> result;
- bool success = true;
- for (const auto& current : custom_inputs) {
- // Get the next feature in the list to process.
- const proto::CustomInput& custom_input = current.second;
-
- // Skip custom input with tensor length of 0.
- if (custom_input.tensor_length() == 0) {
- continue;
- }
- // Validate the proto::CustomInput metadata.
- if (metadata_utils::ValidateMetadataCustomInput(custom_input) !=
- metadata_utils::ValidationResult::kValidationSuccess) {
- success = false;
- } else {
- result[current.first] = ProcessSingleCustomInput(custom_input);
- }
- }
-
- // Processing of the feature list has completed.
- custom_inputs_.clear();
- if (!success) {
- custom_inputs_.clear();
- feature_processor_state->SetError();
- }
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback), std::move(feature_processor_state),
- std::move(result)));
-}
-
-template void CustomInputProcessor::ProcessIndexType(
- base::flat_map<std::pair<int, int>, proto::CustomInput> custom_inputs,
- std::unique_ptr<FeatureProcessorState> feature_processor_state,
- TemplateCallback<std::pair<int, int>> callback);
-
-QueryProcessor::Tensor CustomInputProcessor::ProcessSingleCustomInput(
- const proto::CustomInput& custom_input) {
- std::vector<ProcessedValue> tensor_result;
- if (custom_input.fill_policy() == proto::CustomInput::UNKNOWN_FILL_POLICY) {
- // When parsing a CustomInput object, if the fill policy is not
- // supported by the current version of the client, the fill policy field
- // will not be filled. When this happens, the custom input processor
- // will either use the default values to generate an input tensor or
- // fail the model execution.
- tensor_result =
- std::vector<ProcessedValue>(custom_input.default_value().begin(),
- custom_input.default_value().end());
- } else if (custom_input.fill_policy() ==
- proto::CustomInput::FILL_PREDICTION_TIME) {
- tensor_result.emplace_back(ProcessedValue(prediction_time_));
- }
- return tensor_result;
-}
-
-} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/execution/custom_input_processor_unittest.cc b/chromium/components/segmentation_platform/internal/execution/custom_input_processor_unittest.cc
deleted file mode 100644
index 34dea747eb0..00000000000
--- a/chromium/components/segmentation_platform/internal/execution/custom_input_processor_unittest.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/custom_input_processor.h"
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "base/test/simple_test_clock.h"
-#include "base/test/task_environment.h"
-#include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/database/ukm_types.h"
-#include "components/segmentation_platform/internal/execution/feature_processor_state.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace segmentation_platform {
-
-class CustomInputProcessorTest : public testing::Test {
- public:
- CustomInputProcessorTest() = default;
- ~CustomInputProcessorTest() override = default;
-
- void SetUp() override {
- clock_.SetNow(base::Time::Now());
- feature_processor_state_ = std::make_unique<FeatureProcessorState>(
- base::Time(), base::TimeDelta(),
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN, nullptr,
- base::BindOnce([](bool, const std::vector<float>&) {}));
- custom_input_processor_sql_ =
- std::make_unique<CustomInputProcessor>(clock_.Now());
- }
-
- void TearDown() override {
- feature_processor_state_.reset();
- custom_input_processor_sql_.reset();
- }
-
- proto::CustomInput CreateCustomInputQuery(
- size_t tensor_length,
- proto::CustomInput::FillPolicy fill_policy,
- const std::vector<float>& default_values) {
- proto::CustomInput custom_input;
- custom_input.set_fill_policy(fill_policy);
- custom_input.set_tensor_length(tensor_length);
- for (float default_value : default_values)
- custom_input.add_default_value(default_value);
- return custom_input;
- }
-
- template <typename IndexType>
- void ExpectProcessedCustomInputs(
- const base::flat_map<IndexType, proto::CustomInput>& data,
- bool expected_error,
- const base::flat_map<IndexType, QueryProcessor::Tensor>&
- expected_result) {
- base::RunLoop loop;
- custom_input_processor_sql_->ProcessIndexType<IndexType>(
- data, std::move(feature_processor_state_),
- base::BindOnce(
- &CustomInputProcessorTest::OnProcessingFinishedCallback<IndexType>,
- base::Unretained(this), loop.QuitClosure(), expected_error,
- expected_result));
- loop.Run();
- }
-
- template <typename IndexType>
- void OnProcessingFinishedCallback(
- base::RepeatingClosure closure,
- bool expected_error,
- const base::flat_map<IndexType, QueryProcessor::Tensor>& expected_result,
- std::unique_ptr<FeatureProcessorState> feature_processor_state,
- base::flat_map<IndexType, QueryProcessor::Tensor> result) {
- EXPECT_EQ(expected_error, feature_processor_state->error());
- EXPECT_EQ(expected_result, result);
- std::move(closure).Run();
- }
-
- protected:
- base::SimpleTestClock clock_;
- base::test::TaskEnvironment task_environment_;
- std::unique_ptr<FeatureProcessorState> feature_processor_state_;
- std::unique_ptr<CustomInputProcessor> custom_input_processor_sql_;
-};
-
-TEST_F(CustomInputProcessorTest, IntTypeIndex) {
- using IndexType = int;
- IndexType index = 0;
- base::flat_map<IndexType, proto::CustomInput> data;
- data[index] =
- CreateCustomInputQuery(1, proto::CustomInput::FILL_PREDICTION_TIME, {});
-
- base::flat_map<IndexType, QueryProcessor::Tensor> expected_result;
- expected_result[index] = {ProcessedValue(clock_.Now())};
- ExpectProcessedCustomInputs<IndexType>(data, /*expected_error=*/false,
- expected_result);
- task_environment_.RunUntilIdle();
-}
-
-TEST_F(CustomInputProcessorTest, IntPairTypeIndex) {
- using IndexType = std::pair<int, int>;
- IndexType index = std::make_pair(0, 0);
- base::flat_map<IndexType, proto::CustomInput> data;
- data[index] =
- CreateCustomInputQuery(1, proto::CustomInput::FILL_PREDICTION_TIME, {});
-
- base::flat_map<IndexType, QueryProcessor::Tensor> expected_result;
- expected_result[index] = {ProcessedValue(clock_.Now())};
- ExpectProcessedCustomInputs<IndexType>(data, /*expected_error=*/false,
- expected_result);
- task_environment_.RunUntilIdle();
-}
-
-} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/execution/default_model_manager.cc b/chromium/components/segmentation_platform/internal/execution/default_model_manager.cc
index 8c10c5debba..460668c68eb 100644
--- a/chromium/components/segmentation_platform/internal/execution/default_model_manager.cc
+++ b/chromium/components/segmentation_platform/internal/execution/default_model_manager.cc
@@ -14,11 +14,9 @@ DefaultModelManager::SegmentInfoWrapper::~SegmentInfoWrapper() = default;
DefaultModelManager::DefaultModelManager(
ModelProviderFactory* model_provider_factory,
- const std::vector<OptimizationTarget>& segment_ids)
+ const std::vector<SegmentId>& segment_ids)
: model_provider_factory_(model_provider_factory) {
- for (OptimizationTarget segment_id : segment_ids) {
- if (!model_provider_factory)
- continue;
+ for (SegmentId segment_id : segment_ids) {
std::unique_ptr<ModelProvider> provider =
model_provider_factory->CreateDefaultProvider(segment_id);
if (!provider)
@@ -30,8 +28,7 @@ DefaultModelManager::DefaultModelManager(
DefaultModelManager::~DefaultModelManager() = default;
-ModelProvider* DefaultModelManager::GetDefaultProvider(
- OptimizationTarget segment_id) {
+ModelProvider* DefaultModelManager::GetDefaultProvider(SegmentId segment_id) {
auto it = default_model_providers_.find(segment_id);
if (it != default_model_providers_.end())
return it->second.get();
@@ -39,21 +36,20 @@ ModelProvider* DefaultModelManager::GetDefaultProvider(
}
void DefaultModelManager::GetAllSegmentInfoFromDefaultModel(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback) {
auto result = std::make_unique<SegmentInfoList>();
- std::deque<OptimizationTarget> remaining_segment_ids(segment_ids.begin(),
- segment_ids.end());
+ std::deque<SegmentId> remaining_segment_ids(segment_ids.begin(),
+ segment_ids.end());
GetNextSegmentInfoFromDefaultModel(
std::move(result), std::move(remaining_segment_ids), std::move(callback));
}
void DefaultModelManager::GetNextSegmentInfoFromDefaultModel(
std::unique_ptr<SegmentInfoList> result,
- std::deque<OptimizationTarget> remaining_segment_ids,
+ std::deque<SegmentId> remaining_segment_ids,
MultipleSegmentInfoCallback callback) {
- OptimizationTarget segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+ SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
ModelProvider* default_provider = nullptr;
// Find the next available default provider.
@@ -80,9 +76,9 @@ void DefaultModelManager::GetNextSegmentInfoFromDefaultModel(
void DefaultModelManager::OnFetchDefaultModel(
std::unique_ptr<SegmentInfoList> result,
- std::deque<OptimizationTarget> remaining_segment_ids,
+ std::deque<SegmentId> remaining_segment_ids,
MultipleSegmentInfoCallback callback,
- OptimizationTarget segment_id,
+ SegmentId segment_id,
proto::SegmentationModelMetadata metadata,
int64_t model_version) {
auto info = std::make_unique<SegmentInfoWrapper>();
@@ -97,7 +93,7 @@ void DefaultModelManager::OnFetchDefaultModel(
}
void DefaultModelManager::GetAllSegmentInfoFromBothModels(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
SegmentInfoDatabase* segment_database,
MultipleSegmentInfoCallback callback) {
segment_database->GetSegmentInfoForSegments(
@@ -108,7 +104,7 @@ void DefaultModelManager::GetAllSegmentInfoFromBothModels(
}
void DefaultModelManager::OnGetAllSegmentInfoFromDatabase(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback,
std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segment_infos) {
GetAllSegmentInfoFromDefaultModel(
@@ -139,7 +135,7 @@ void DefaultModelManager::OnGetAllSegmentInfoFromDefaultModel(
}
void DefaultModelManager::SetDefaultProvidersForTesting(
- std::map<OptimizationTarget, std::unique_ptr<ModelProvider>>&& providers) {
+ std::map<SegmentId, std::unique_ptr<ModelProvider>>&& providers) {
default_model_providers_ = std::move(providers);
}
diff --git a/chromium/components/segmentation_platform/internal/execution/default_model_manager.h b/chromium/components/segmentation_platform/internal/execution/default_model_manager.h
index 911a31cc879..e0ca96b5d9b 100644
--- a/chromium/components/segmentation_platform/internal/execution/default_model_manager.h
+++ b/chromium/components/segmentation_platform/internal/execution/default_model_manager.h
@@ -20,9 +20,9 @@
#include "components/segmentation_platform/public/model_provider.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
+using proto::SegmentId;
+
class SegmentInfoDatabase;
// DefaultModelManager provides support to query all default models available.
@@ -31,7 +31,7 @@ class SegmentInfoDatabase;
class DefaultModelManager {
public:
DefaultModelManager(ModelProviderFactory* model_provider_factory,
- const std::vector<OptimizationTarget>& segment_ids);
+ const std::vector<SegmentId>& segment_ids);
virtual ~DefaultModelManager();
// Disallow copy/assign.
@@ -60,37 +60,37 @@ class DefaultModelManager {
// default model for a given set of segment IDs. The result can contain
// the same segment ID multiple times.
virtual void GetAllSegmentInfoFromBothModels(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
SegmentInfoDatabase* segment_database,
MultipleSegmentInfoCallback callback);
// Called to get the segment info from the default model for a given set of
// segment IDs.
virtual void GetAllSegmentInfoFromDefaultModel(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback);
// Returns the default provider or `nulllptr` when unavailable.
- ModelProvider* GetDefaultProvider(OptimizationTarget segment_id);
+ ModelProvider* GetDefaultProvider(SegmentId segment_id);
void SetDefaultProvidersForTesting(
- std::map<OptimizationTarget, std::unique_ptr<ModelProvider>>&& providers);
+ std::map<SegmentId, std::unique_ptr<ModelProvider>>&& providers);
private:
void GetNextSegmentInfoFromDefaultModel(
std::unique_ptr<SegmentInfoList> result,
- std::deque<OptimizationTarget> remaining_segment_ids,
+ std::deque<SegmentId> remaining_segment_ids,
MultipleSegmentInfoCallback callback);
void OnFetchDefaultModel(std::unique_ptr<SegmentInfoList> result,
- std::deque<OptimizationTarget> remaining_segment_ids,
+ std::deque<SegmentId> remaining_segment_ids,
MultipleSegmentInfoCallback callback,
- OptimizationTarget segment_id,
+ SegmentId segment_id,
proto::SegmentationModelMetadata metadata,
int64_t model_version);
void OnGetAllSegmentInfoFromDatabase(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback,
std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segment_infos);
@@ -101,8 +101,7 @@ class DefaultModelManager {
SegmentInfoList segment_infos_from_default_model);
// Default model providers.
- std::map<OptimizationTarget, std::unique_ptr<ModelProvider>>
- default_model_providers_;
+ std::map<SegmentId, std::unique_ptr<ModelProvider>> default_model_providers_;
const raw_ptr<ModelProviderFactory> model_provider_factory_;
base::WeakPtrFactory<DefaultModelManager> weak_ptr_factory_{this};
diff --git a/chromium/components/segmentation_platform/internal/execution/default_model_manager_unittest.cc b/chromium/components/segmentation_platform/internal/execution/default_model_manager_unittest.cc
index c7979cc4fba..32abfccb2a4 100644
--- a/chromium/components/segmentation_platform/internal/execution/default_model_manager_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/execution/default_model_manager_unittest.cc
@@ -18,18 +18,18 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
using base::test::RunOnceCallback;
-using optimization_guide::proto::OptimizationTarget;
using testing::_;
namespace segmentation_platform {
+using proto::SegmentId;
+
class DefaultModelManagerTest : public testing::Test {
public:
DefaultModelManagerTest() : model_provider_factory_(&model_provider_data_) {}
~DefaultModelManagerTest() override = default;
- MockModelProvider& FindHandler(
- optimization_guide::proto::OptimizationTarget segment_id) {
+ MockModelProvider& FindHandler(proto::SegmentId segment_id) {
return *(*model_provider_data_.default_model_providers.find(segment_id))
.second;
}
@@ -52,14 +52,11 @@ class DefaultModelManagerTest : public testing::Test {
};
TEST_F(DefaultModelManagerTest, BasicTest) {
- const auto segment_1 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
- const auto segment_2 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
- const auto segment_3 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
+ const auto segment_1 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ const auto segment_2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ const auto segment_3 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
const auto segment_4 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES;
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES;
// Set some model versions.
const int model_version_db = 4;
diff --git a/chromium/components/segmentation_platform/internal/execution/execution_request.cc b/chromium/components/segmentation_platform/internal/execution/execution_request.cc
new file mode 100644
index 00000000000..04513734e7a
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/execution_request.cc
@@ -0,0 +1,12 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/execution_request.h"
+
+namespace segmentation_platform {
+
+ExecutionRequest::ExecutionRequest() = default;
+ExecutionRequest::~ExecutionRequest() = default;
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/execution/execution_request.h b/chromium/components/segmentation_platform/internal/execution/execution_request.h
new file mode 100644
index 00000000000..e610475c55a
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/execution_request.h
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_EXECUTION_REQUEST_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_EXECUTION_REQUEST_H_
+
+#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
+#include "components/segmentation_platform/internal/execution/model_execution_status.h"
+#include "components/segmentation_platform/internal/input_context.h"
+
+namespace segmentation_platform {
+namespace proto {
+class SegmentInfo;
+}
+
+class ModelProvider;
+
+// Request for model execution.
+struct ExecutionRequest {
+ using ModelExecutionCallback =
+ base::OnceCallback<void(const std::pair<float, ModelExecutionStatus>&)>;
+
+ ExecutionRequest();
+ ~ExecutionRequest();
+
+ // Required: The segment info to use for model execution.
+ raw_ptr<const proto::SegmentInfo> segment_info = nullptr;
+
+ // The model provider used to execute the model.
+ raw_ptr<ModelProvider> model_provider = nullptr;
+
+ // Current context of the browser that is needed by feature processor for some
+ // of the models.
+ scoped_refptr<InputContext> input_context;
+
+ // Save result of execution to the database.
+ bool save_result_to_db = false;
+
+ // Record metrics for default model instead of optimization_guide based
+ // models.
+ bool record_metrics_for_default = false;
+
+ // returns result as by callback, to be used when `save_result_to_db` is
+ // false.
+ ModelExecutionCallback callback;
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_EXECUTION_REQUEST_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/feature_list_query_processor.cc b/chromium/components/segmentation_platform/internal/execution/feature_list_query_processor.cc
deleted file mode 100644
index cb9271ae6f6..00000000000
--- a/chromium/components/segmentation_platform/internal/execution/feature_list_query_processor.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/feature_list_query_processor.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/time/clock.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
-#include "components/segmentation_platform/internal/database/ukm_types.h"
-#include "components/segmentation_platform/internal/execution/custom_input_processor.h"
-#include "components/segmentation_platform/internal/execution/feature_processor_state.h"
-#include "components/segmentation_platform/internal/execution/sql_feature_processor.h"
-#include "components/segmentation_platform/internal/execution/uma_feature_processor.h"
-#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
-#include "components/segmentation_platform/internal/stats.h"
-
-namespace segmentation_platform {
-
-namespace {
-// Index not actually used for legacy code in FeatureQueryProcessor.
-const int kIndexNotUsed = 0;
-} // namespace
-
-FeatureListQueryProcessor::FeatureListQueryProcessor(
- SignalDatabase* signal_database,
- std::unique_ptr<FeatureAggregator> feature_aggregator)
- : signal_database_(signal_database),
- feature_aggregator_(std::move(feature_aggregator)) {}
-
-FeatureListQueryProcessor::~FeatureListQueryProcessor() = default;
-
-void FeatureListQueryProcessor::ProcessFeatureList(
- const proto::SegmentationModelMetadata& model_metadata,
- OptimizationTarget segment_id,
- base::Time prediction_time,
- FeatureProcessorCallback callback) {
- // The total bucket duration is defined by product of the bucket_duration
- // value and the length of related time_unit field, e.g. 28 * length(DAY).
- base::TimeDelta time_unit_len = metadata_utils::GetTimeUnit(model_metadata);
- base::TimeDelta bucket_duration =
- model_metadata.bucket_duration() * time_unit_len;
-
- // Grab the metadata for all the features, which will be processed one at a
- // time, before executing the model.
- auto input_features = std::make_unique<std::deque<proto::InputFeature>>();
- for (int i = 0; i < model_metadata.features_size(); ++i) {
- proto::InputFeature input_feature;
- input_feature.mutable_uma_feature()->CopyFrom(model_metadata.features(i));
- input_features->emplace_back(input_feature);
- }
- for (int i = 0; i < model_metadata.input_features_size(); ++i)
- input_features->emplace_back(model_metadata.input_features(i));
-
- // Capture all the relevant metadata information into a FeatureProcessorState.
- auto feature_processor_state = std::make_unique<FeatureProcessorState>(
- prediction_time, bucket_duration, segment_id, std::move(input_features),
- std::move(callback));
-
- ProcessNextInputFeature(std::move(feature_processor_state));
-}
-
-void FeatureListQueryProcessor::ProcessNextInputFeature(
- std::unique_ptr<FeatureProcessorState> feature_processor_state) {
- // Finished processing all input features or an error occurred.
- if (feature_processor_state->IsFeatureListEmpty() ||
- feature_processor_state->error()) {
- feature_processor_state->RunCallback();
- return;
- }
-
- // Get next input feature to process.
- proto::InputFeature input_feature =
- feature_processor_state->PopNextInputFeature();
- std::unique_ptr<QueryProcessor> processor;
-
- // Process all the features in-order, starting with the first feature.
- if (input_feature.has_uma_feature()) {
- base::flat_map<QueryProcessor::FeatureIndex, proto::UMAFeature> queries = {
- {kIndexNotUsed, input_feature.uma_feature()}};
- processor = std::make_unique<UmaFeatureProcessor>(
- std::move(queries), signal_database_, feature_aggregator_.get(),
- feature_processor_state->prediction_time(),
- feature_processor_state->bucket_duration(),
- feature_processor_state->segment_id());
- } else if (input_feature.has_custom_input()) {
- base::flat_map<QueryProcessor::FeatureIndex, proto::CustomInput> queries = {
- {kIndexNotUsed, input_feature.custom_input()}};
- processor = std::make_unique<CustomInputProcessor>(
- std::move(queries), feature_processor_state->prediction_time());
- } else if (input_feature.has_sql_feature()) {
- SqlFeatureProcessor::QueryList queries = {
- {kIndexNotUsed, input_feature.sql_feature()}};
- processor = std::make_unique<SqlFeatureProcessor>(
- std::move(queries), feature_processor_state->prediction_time());
- }
-
- auto* processor_ptr = processor.get();
- processor_ptr->Process(
- std::move(feature_processor_state),
- base::BindOnce(&FeatureListQueryProcessor::OnFeatureProcessed,
- weak_ptr_factory_.GetWeakPtr(), std::move(processor)));
-}
-
-void FeatureListQueryProcessor::OnFeatureProcessed(
- std::unique_ptr<QueryProcessor> feature_processor,
- std::unique_ptr<FeatureProcessorState> feature_processor_state,
- QueryProcessor::IndexedTensors result) {
- feature_processor_state->AppendInputTensor(result[kIndexNotUsed]);
- ProcessNextInputFeature(std::move(feature_processor_state));
-}
-
-} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/execution/feature_processor_state.cc b/chromium/components/segmentation_platform/internal/execution/feature_processor_state.cc
deleted file mode 100644
index e794fd63685..00000000000
--- a/chromium/components/segmentation_platform/internal/execution/feature_processor_state.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/feature_processor_state.h"
-
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "components/segmentation_platform/internal/database/ukm_types.h"
-
-namespace segmentation_platform {
-
-FeatureProcessorState::FeatureProcessorState(
- base::Time prediction_time,
- base::TimeDelta bucket_duration,
- OptimizationTarget segment_id,
- std::unique_ptr<std::deque<proto::InputFeature>> input_features,
- FeatureListQueryProcessor::FeatureProcessorCallback callback)
- : prediction_time_(prediction_time),
- bucket_duration_(bucket_duration),
- segment_id_(segment_id),
- input_features_(std::move(input_features)),
- callback_(std::move(callback)) {}
-
-FeatureProcessorState::~FeatureProcessorState() = default;
-
-void FeatureProcessorState::SetError() {
- error_ = true;
- input_tensor_.clear();
-}
-
-proto::InputFeature FeatureProcessorState::PopNextInputFeature() {
- proto::InputFeature input_feature = std::move(input_features_->front());
- input_features_->pop_front();
- return input_feature;
-}
-
-bool FeatureProcessorState::IsFeatureListEmpty() const {
- return input_features_->empty();
-}
-
-void FeatureProcessorState::RunCallback() {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback_), error_, input_tensor_));
-}
-
-void FeatureProcessorState::AppendInputTensor(const std::vector<float>& data) {
- input_tensor_.insert(input_tensor_.end(), data.begin(), data.end());
-}
-
-void FeatureProcessorState::AppendInputTensor(
- const std::vector<ProcessedValue>& data) {
- std::vector<float> tensor_result;
- for (auto& value : data) {
- if (value.type == ProcessedValue::Type::FLOAT) {
- tensor_result.push_back(value.float_val);
- } else {
- SetError();
- return;
- }
- }
-
- input_tensor_.insert(input_tensor_.end(), tensor_result.begin(),
- tensor_result.end());
-}
-
-} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/execution/mock_feature_list_query_processor.cc b/chromium/components/segmentation_platform/internal/execution/mock_feature_list_query_processor.cc
deleted file mode 100644
index 69673a88842..00000000000
--- a/chromium/components/segmentation_platform/internal/execution/mock_feature_list_query_processor.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/mock_feature_list_query_processor.h"
-
-namespace segmentation_platform {
-
-MockFeatureListQueryProcessor::MockFeatureListQueryProcessor()
- : FeatureListQueryProcessor(nullptr, nullptr) {}
-
-MockFeatureListQueryProcessor::~MockFeatureListQueryProcessor() = default;
-
-} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/execution/mock_feature_list_query_processor.h b/chromium/components/segmentation_platform/internal/execution/mock_feature_list_query_processor.h
deleted file mode 100644
index c969bcd59c1..00000000000
--- a/chromium/components/segmentation_platform/internal/execution/mock_feature_list_query_processor.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_FEATURE_LIST_QUERY_PROCESSOR_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_FEATURE_LIST_QUERY_PROCESSOR_H_
-
-#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
-
-#include "components/segmentation_platform/internal/execution/feature_aggregator.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace segmentation_platform {
-
-class MockFeatureListQueryProcessor : public FeatureListQueryProcessor {
- public:
- MockFeatureListQueryProcessor();
- ~MockFeatureListQueryProcessor() override;
- MOCK_METHOD(void,
- ProcessFeatureList,
- (const proto::SegmentationModelMetadata&,
- OptimizationTarget,
- base::Time,
- FeatureProcessorCallback),
- (override));
-};
-
-} // namespace segmentation_platform
-
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_FEATURE_LIST_QUERY_PROCESSOR_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/mock_model_provider.cc b/chromium/components/segmentation_platform/internal/execution/mock_model_provider.cc
index 7c16a169697..d9915c0567e 100644
--- a/chromium/components/segmentation_platform/internal/execution/mock_model_provider.cc
+++ b/chromium/components/segmentation_platform/internal/execution/mock_model_provider.cc
@@ -18,7 +18,7 @@ using ::testing::Invoke;
// Stores the client callbacks to |data|.
void StoreClientCallback(
- optimization_guide::proto::OptimizationTarget segment_id,
+ proto::SegmentId segment_id,
TestModelProviderFactory::Data* data,
const ModelProvider::ModelUpdatedCallback& model_updated_callback) {
data->model_providers_callbacks.emplace(
@@ -28,7 +28,7 @@ void StoreClientCallback(
} // namespace
MockModelProvider::MockModelProvider(
- optimization_guide::proto::OptimizationTarget segment_id,
+ proto::SegmentId segment_id,
base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)>
get_client_callback)
: ModelProvider(segment_id), get_client_callback_(get_client_callback) {
@@ -44,7 +44,7 @@ TestModelProviderFactory::Data::Data() = default;
TestModelProviderFactory::Data::~Data() = default;
std::unique_ptr<ModelProvider> TestModelProviderFactory::CreateProvider(
- optimization_guide::proto::OptimizationTarget segment_id) {
+ proto::SegmentId segment_id) {
auto provider = std::make_unique<MockModelProvider>(
segment_id, base::BindRepeating(&StoreClientCallback, segment_id, data_));
data_->model_providers.emplace(std::make_pair(segment_id, provider.get()));
@@ -52,7 +52,7 @@ std::unique_ptr<ModelProvider> TestModelProviderFactory::CreateProvider(
}
std::unique_ptr<ModelProvider> TestModelProviderFactory::CreateDefaultProvider(
- optimization_guide::proto::OptimizationTarget segment_id) {
+ proto::SegmentId segment_id) {
if (!base::Contains(data_->segments_supporting_default_model, segment_id))
return nullptr;
diff --git a/chromium/components/segmentation_platform/internal/execution/mock_model_provider.h b/chromium/components/segmentation_platform/internal/execution/mock_model_provider.h
index a1c679d4766..b8dc76199c2 100644
--- a/chromium/components/segmentation_platform/internal/execution/mock_model_provider.h
+++ b/chromium/components/segmentation_platform/internal/execution/mock_model_provider.h
@@ -14,15 +14,15 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
+using proto::SegmentId;
+
// Mock model provider for testing, to be used with TestModelProviderFactory.
class MockModelProvider : public ModelProvider {
public:
MockModelProvider(
- optimization_guide::proto::OptimizationTarget segment_id,
+ proto::SegmentId segment_id,
base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)>
get_client_callback);
~MockModelProvider() override;
@@ -56,21 +56,18 @@ class TestModelProviderFactory : public ModelProviderFactory {
// Map of targets to model providers, added when provider is created. The
// list is not cleared when providers are destroyed.
- std::map<optimization_guide::proto::OptimizationTarget, MockModelProvider*>
- model_providers;
+ std::map<proto::SegmentId, MockModelProvider*> model_providers;
// Map of targets to default model providers, added when provider is
// created. The list is not cleared when providers are destroyed.
- std::map<optimization_guide::proto::OptimizationTarget, MockModelProvider*>
- default_model_providers;
+ std::map<proto::SegmentId, MockModelProvider*> default_model_providers;
// Map from target to updated callback, recorded when InitAndFetchModel()
// was called on any provider.
- std::map<optimization_guide::proto::OptimizationTarget,
- ModelProvider::ModelUpdatedCallback>
+ std::map<proto::SegmentId, ModelProvider::ModelUpdatedCallback>
model_providers_callbacks;
- std::vector<OptimizationTarget> segments_supporting_default_model;
+ std::vector<SegmentId> segments_supporting_default_model;
};
// Records requests to `data`. `data` is not owned, and the caller must ensure
@@ -81,10 +78,10 @@ class TestModelProviderFactory : public ModelProviderFactory {
// ModelProviderFactory impl, that keeps track of the created provider and
// callbacks in |data_|.
std::unique_ptr<ModelProvider> CreateProvider(
- optimization_guide::proto::OptimizationTarget segment_id) override;
+ proto::SegmentId segment_id) override;
std::unique_ptr<ModelProvider> CreateDefaultProvider(
- optimization_guide::proto::OptimizationTarget) override;
+ proto::SegmentId) override;
private:
raw_ptr<Data> data_;
diff --git a/chromium/components/segmentation_platform/internal/execution/model_execution_manager.h b/chromium/components/segmentation_platform/internal/execution/model_execution_manager.h
index db967a4ec64..3e875701646 100644
--- a/chromium/components/segmentation_platform/internal/execution/model_execution_manager.h
+++ b/chromium/components/segmentation_platform/internal/execution/model_execution_manager.h
@@ -6,7 +6,7 @@
#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MODEL_EXECUTION_MANAGER_H_
#include "base/callback_forward.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace segmentation_platform {
namespace proto {
@@ -31,8 +31,7 @@ class ModelExecutionManager {
using SegmentationModelUpdatedCallback =
base::RepeatingCallback<void(proto::SegmentInfo)>;
- virtual ModelProvider* GetProvider(
- optimization_guide::proto::OptimizationTarget segment_id) = 0;
+ virtual ModelProvider* GetProvider(proto::SegmentId segment_id) = 0;
protected:
ModelExecutionManager() = default;
diff --git a/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc b/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
index 5f0bc8307a4..05a90a4825a 100644
--- a/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
+++ b/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
@@ -16,24 +16,23 @@
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/trace_event/typed_macros.h"
-#include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/execution/model_execution_manager.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/stats.h"
#include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace optimization_guide {
class OptimizationGuideModelProvider;
-using proto::OptimizationTarget;
} // namespace optimization_guide
namespace segmentation_platform {
ModelExecutionManagerImpl::ModelExecutionManagerImpl(
- const base::flat_set<OptimizationTarget>& segment_ids,
+ const base::flat_set<SegmentId>& segment_ids,
ModelProviderFactory* model_provider_factory,
base::Clock* clock,
SegmentInfoDatabase* segment_database,
@@ -41,7 +40,7 @@ ModelExecutionManagerImpl::ModelExecutionManagerImpl(
: clock_(clock),
segment_database_(segment_database),
model_updated_callback_(model_updated_callback) {
- for (OptimizationTarget segment_id : segment_ids) {
+ for (SegmentId segment_id : segment_ids) {
std::unique_ptr<ModelProvider> provider =
model_provider_factory->CreateProvider(segment_id);
provider->InitAndFetchModel(base::BindRepeating(
@@ -54,21 +53,20 @@ ModelExecutionManagerImpl::ModelExecutionManagerImpl(
ModelExecutionManagerImpl::~ModelExecutionManagerImpl() = default;
ModelProvider* ModelExecutionManagerImpl::GetProvider(
- optimization_guide::proto::OptimizationTarget segment_id) {
+ proto::SegmentId segment_id) {
auto it = model_providers_.find(segment_id);
DCHECK(it != model_providers_.end());
return it->second.get();
}
void ModelExecutionManagerImpl::OnSegmentationModelUpdated(
- optimization_guide::proto::OptimizationTarget segment_id,
+ proto::SegmentId segment_id,
proto::SegmentationModelMetadata metadata,
int64_t model_version) {
TRACE_EVENT("segmentation_platform",
"ModelExecutionManagerImpl::OnSegmentationModelUpdated");
stats::RecordModelDeliveryReceived(segment_id);
- if (segment_id == optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_UNKNOWN) {
+ if (segment_id == proto::SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
return;
}
@@ -91,7 +89,7 @@ void ModelExecutionManagerImpl::OnSegmentationModelUpdated(
}
void ModelExecutionManagerImpl::OnSegmentInfoFetchedForModelUpdate(
- optimization_guide::proto::OptimizationTarget segment_id,
+ proto::SegmentId segment_id,
proto::SegmentationModelMetadata metadata,
int64_t model_version,
absl::optional<proto::SegmentInfo> old_segment_info) {
@@ -131,11 +129,14 @@ void ModelExecutionManagerImpl::OnSegmentInfoFetchedForModelUpdate(
new_metadata->CopyFrom(metadata);
new_segment_info.set_model_version(model_version);
- if (!old_model_version.has_value() ||
- old_model_version.value() != model_version) {
- new_segment_info.set_model_update_time_s(
- clock_->Now().ToDeltaSinceWindowsEpoch().InSeconds());
+ int64_t new_model_update_time_s =
+ clock_->Now().ToDeltaSinceWindowsEpoch().InSeconds();
+ if (old_model_version.has_value() &&
+ old_model_version.value() == model_version &&
+ old_segment_info->has_model_update_time_s()) {
+ new_model_update_time_s = old_segment_info->model_update_time_s();
}
+ new_segment_info.set_model_update_time_s(new_model_update_time_s);
// We have a valid segment id, and the new metadata was valid, therefore the
// new metadata should be valid. We are not allowed to invoke the callback
diff --git a/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.h b/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
index 89696512251..e344583553e 100644
--- a/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
+++ b/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
@@ -13,9 +13,9 @@
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
#include "components/segmentation_platform/internal/execution/model_execution_manager.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base {
@@ -38,7 +38,7 @@ class SegmentInfo;
class ModelExecutionManagerImpl : public ModelExecutionManager {
public:
ModelExecutionManagerImpl(
- const base::flat_set<OptimizationTarget>& segment_ids,
+ const base::flat_set<SegmentId>& segment_ids,
ModelProviderFactory* model_provider_factory,
base::Clock* clock,
SegmentInfoDatabase* segment_database,
@@ -51,8 +51,7 @@ class ModelExecutionManagerImpl : public ModelExecutionManager {
delete;
// ModelExecutionManager override:
- ModelProvider* GetProvider(
- optimization_guide::proto::OptimizationTarget segment_id) override;
+ ModelProvider* GetProvider(proto::SegmentId segment_id) override;
private:
friend class SegmentationPlatformServiceImplTest;
@@ -61,10 +60,9 @@ class ModelExecutionManagerImpl : public ModelExecutionManager {
// Callback for whenever a SegmentationModelHandler is informed that the
// underlying ML model file has been updated. If there is an available
// model, this will be called at least once per session.
- void OnSegmentationModelUpdated(
- optimization_guide::proto::OptimizationTarget segment_id,
- proto::SegmentationModelMetadata metadata,
- int64_t model_version);
+ void OnSegmentationModelUpdated(proto::SegmentId segment_id,
+ proto::SegmentationModelMetadata metadata,
+ int64_t model_version);
// Callback after fetching the current SegmentInfo from the
// SegmentInfoDatabase. This is part of the flow for informing the
@@ -72,7 +70,7 @@ class ModelExecutionManagerImpl : public ModelExecutionManager {
// Merges the PredictionResult from the previously stored SegmentInfo with
// the newly updated one, and stores the new version in the DB.
void OnSegmentInfoFetchedForModelUpdate(
- optimization_guide::proto::OptimizationTarget segment_id,
+ proto::SegmentId segment_id,
proto::SegmentationModelMetadata metadata,
int64_t model_version,
absl::optional<proto::SegmentInfo> segment_info);
@@ -83,7 +81,7 @@ class ModelExecutionManagerImpl : public ModelExecutionManager {
bool success);
// All the relevant handlers for each of the segments.
- std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> model_providers_;
+ std::map<SegmentId, std::unique_ptr<ModelProvider>> model_providers_;
// Used to access the current time.
raw_ptr<base::Clock> clock_;
diff --git a/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc b/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
index 09ffd3d3ce9..b25c150f23b 100644
--- a/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
@@ -18,19 +18,19 @@
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/mock_signal_database.h"
#include "components/segmentation_platform/internal/database/signal_database.h"
#include "components/segmentation_platform/internal/database/test_segment_info_database.h"
-#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
#include "components/segmentation_platform/internal/execution/model_execution_manager.h"
#include "components/segmentation_platform/internal/execution/model_execution_status.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -57,22 +57,22 @@ class MockSegmentInfoDatabase : public test::TestSegmentInfoDatabase {
(override));
MOCK_METHOD(void,
GetSegmentInfoForSegments,
- (const std::vector<OptimizationTarget>& segment_ids,
+ (const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback),
(override));
MOCK_METHOD(void,
GetSegmentInfo,
- (OptimizationTarget segment_id, SegmentInfoCallback callback),
+ (SegmentId segment_id, SegmentInfoCallback callback),
(override));
MOCK_METHOD(void,
UpdateSegment,
- (OptimizationTarget segment_id,
+ (SegmentId segment_id,
absl::optional<proto::SegmentInfo> segment_info,
SuccessCallback callback),
(override));
MOCK_METHOD(void,
SaveSegmentResult,
- (OptimizationTarget segment_id,
+ (SegmentId segment_id,
absl::optional<proto::PredictionResult> result,
SuccessCallback callback),
(override));
@@ -100,7 +100,7 @@ class ModelExecutionManagerTest : public testing::Test {
}
void CreateModelExecutionManager(
- std::vector<OptimizationTarget> segment_ids,
+ std::vector<SegmentId> segment_ids,
const ModelExecutionManager::SegmentationModelUpdatedCallback& callback) {
model_execution_manager_ = std::make_unique<ModelExecutionManagerImpl>(
segment_ids, &model_provider_factory_, &clock_, segment_database_.get(),
@@ -109,8 +109,7 @@ class ModelExecutionManagerTest : public testing::Test {
void RunUntilIdle() { task_environment_.RunUntilIdle(); }
- MockModelProvider& FindHandler(
- optimization_guide::proto::OptimizationTarget segment_id) {
+ MockModelProvider& FindHandler(proto::SegmentId segment_id) {
return *(*model_provider_data_.model_providers.find(segment_id)).second;
}
@@ -137,8 +136,7 @@ TEST_F(ModelExecutionManagerTest, OnSegmentationModelUpdatedInvalidMetadata) {
// Construct the ModelExecutionManager.
base::MockCallback<ModelExecutionManager::SegmentationModelUpdatedCallback>
callback;
- auto segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ auto segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
CreateModelExecutionManager({segment_id}, callback.Get());
// Create invalid metadata, which should be ignored.
@@ -157,8 +155,7 @@ TEST_F(ModelExecutionManagerTest, OnSegmentationModelUpdatedInvalidMetadata) {
TEST_F(ModelExecutionManagerTest, OnSegmentationModelUpdatedNoOldMetadata) {
base::MockCallback<ModelExecutionManager::SegmentationModelUpdatedCallback>
callback;
- auto segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ auto segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
CreateModelExecutionManager({segment_id}, callback.Get());
proto::SegmentInfo segment_info;
@@ -195,8 +192,7 @@ TEST_F(ModelExecutionManagerTest,
OnSegmentationModelUpdatedWithPreviousMetadataAndPredictionResult) {
base::MockCallback<ModelExecutionManager::SegmentationModelUpdatedCallback>
callback;
- auto segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ auto segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
CreateModelExecutionManager({segment_id}, callback.Get());
// Fill in old data in the SegmentInfo database.
diff --git a/chromium/components/segmentation_platform/internal/execution/model_executor.h b/chromium/components/segmentation_platform/internal/execution/model_executor.h
index cd550f37c8d..3bc13a7a8d0 100644
--- a/chromium/components/segmentation_platform/internal/execution/model_executor.h
+++ b/chromium/components/segmentation_platform/internal/execution/model_executor.h
@@ -13,7 +13,7 @@
namespace segmentation_platform {
-class ModelProvider;
+struct ExecutionRequest;
// Class used to process features and execute the model.
class ModelExecutor {
@@ -32,10 +32,7 @@ class ModelExecutor {
// Computes input features using `segment_info` and executes the model using
// `model_provider`, and returns result.
- virtual void ExecuteModel(const proto::SegmentInfo& segment_info,
- ModelProvider* model_provider,
- bool record_metrics_for_default,
- ModelExecutionCallback callback) = 0;
+ virtual void ExecuteModel(std::unique_ptr<ExecutionRequest> request) = 0;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/execution/model_executor_impl.cc b/chromium/components/segmentation_platform/internal/execution/model_executor_impl.cc
index 2402cd31c5d..b752e72d5d3 100644
--- a/chromium/components/segmentation_platform/internal/execution/model_executor_impl.cc
+++ b/chromium/components/segmentation_platform/internal/execution/model_executor_impl.cc
@@ -9,18 +9,19 @@
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/trace_event/typed_macros.h"
-#include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/execution_request.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
#include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
#include "components/segmentation_platform/internal/stats.h"
#include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
namespace segmentation_platform {
namespace {
-using optimization_guide::proto::OptimizationTarget;
-
-}
+using processing::FeatureListQueryProcessor;
+using proto::SegmentId;
+} // namespace
struct ModelExecutorImpl::ModelExecutionTraceEvent {
ModelExecutionTraceEvent(const char* event_name,
@@ -54,7 +55,7 @@ struct ModelExecutorImpl::ExecutionState {
// https://crbug.com/1021571.
std::unique_ptr<ModelExecutionTraceEvent> trace_event;
- OptimizationTarget segment_id;
+ SegmentId segment_id;
int64_t model_version = 0;
raw_ptr<ModelProvider> model_provider = nullptr;
bool record_metrics_for_default = false;
@@ -62,6 +63,7 @@ struct ModelExecutorImpl::ExecutionState {
std::vector<float> input_tensor;
base::Time total_execution_start_time;
base::Time model_execution_start_time;
+ base::TimeDelta signal_storage_length;
};
ModelExecutorImpl::ModelExecutionTraceEvent::ModelExecutionTraceEvent(
@@ -79,33 +81,32 @@ ModelExecutorImpl::ModelExecutionTraceEvent::~ModelExecutionTraceEvent() {
ModelExecutorImpl::ModelExecutorImpl(
base::Clock* clock,
- FeatureListQueryProcessor* feature_list_query_processor)
+ processing::FeatureListQueryProcessor* feature_list_query_processor)
: clock_(clock),
feature_list_query_processor_(feature_list_query_processor) {}
ModelExecutorImpl::~ModelExecutorImpl() = default;
-void ModelExecutorImpl::ExecuteModel(const proto::SegmentInfo& segment_info,
- ModelProvider* model_provider,
- bool record_metrics_for_default,
- ModelExecutionCallback callback) {
- OptimizationTarget segment_id = segment_info.segment_id();
+void ModelExecutorImpl::ExecuteModel(
+ std::unique_ptr<ExecutionRequest> request) {
+ const proto::SegmentInfo& segment_info = *request->segment_info;
+ SegmentId segment_id = segment_info.segment_id();
// Create an ExecutionState that will stay with this request until it has been
// fully processed.
auto state = std::make_unique<ExecutionState>();
state->segment_id = segment_id;
- state->model_provider = model_provider;
- state->record_metrics_for_default = record_metrics_for_default;
+ state->model_provider = request->model_provider;
+ state->record_metrics_for_default = request->record_metrics_for_default;
- state->callback = std::move(callback);
+ state->callback = std::move(request->callback);
state->total_execution_start_time = clock_->Now();
ModelExecutionTraceEvent trace_event("ModelExecutorImpl::ExecuteModel",
*state);
- if (!state->model_provider->ModelAvailable()) {
+ if (!state->model_provider || !state->model_provider->ModelAvailable()) {
RunModelExecutionCallback(std::move(state), 0,
ModelExecutionStatus::kSkippedModelNotReady);
return;
@@ -120,8 +121,13 @@ void ModelExecutorImpl::ExecuteModel(const proto::SegmentInfo& segment_info,
}
state->model_version = segment_info.model_version();
+ const proto::SegmentationModelMetadata& model_metadata =
+ segment_info.model_metadata();
+ state->signal_storage_length = model_metadata.signal_storage_length() *
+ metadata_utils::GetTimeUnit(model_metadata);
feature_list_query_processor_->ProcessFeatureList(
- segment_info.model_metadata(), segment_id, clock_->Now(),
+ segment_info.model_metadata(), request->input_context, segment_id,
+ clock_->Now(), FeatureListQueryProcessor::ProcessOption::kInputsOnly,
base::BindOnce(&ModelExecutorImpl::OnProcessingFeatureListComplete,
weak_ptr_factory_.GetWeakPtr(), std::move(state)));
}
@@ -129,7 +135,8 @@ void ModelExecutorImpl::ExecuteModel(const proto::SegmentInfo& segment_info,
void ModelExecutorImpl::OnProcessingFeatureListComplete(
std::unique_ptr<ExecutionState> state,
bool error,
- const std::vector<float>& input_tensor) {
+ const std::vector<float>& input_tensor,
+ const std::vector<float>& output_tensor) {
if (error) {
// Validation error occurred on model's metadata.
RunModelExecutionCallback(std::move(state), 0,
@@ -150,9 +157,7 @@ void ModelExecutorImpl::ExecuteModel(std::unique_ptr<ExecutionState> state) {
for (unsigned i = 0; i < state->input_tensor.size(); ++i)
log_input << " feature " << i << ": " << state->input_tensor[i];
VLOG(1) << "Segmentation model input: " << log_input.str()
- << " for segment "
- << optimization_guide::proto::OptimizationTarget_Name(
- state->segment_id);
+ << " for segment " << proto::SegmentId_Name(state->segment_id);
}
const std::vector<float>& const_input_tensor = std::move(state->input_tensor);
stats::RecordModelExecutionZeroValuePercent(state->segment_id,
@@ -175,10 +180,10 @@ void ModelExecutorImpl::OnModelExecutionComplete(
clock_->Now() - state->model_execution_start_time);
if (result.has_value()) {
VLOG(1) << "Segmentation model result: " << *result << " for segment "
- << optimization_guide::proto::OptimizationTarget_Name(
- state->segment_id);
+ << proto::SegmentId_Name(state->segment_id);
stats::RecordModelExecutionResult(state->segment_id, result.value());
- if (state->model_version) {
+ if (state->model_version && SegmentationUkmHelper::AllowedToUploadData(
+ state->signal_storage_length, clock_)) {
SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
state->segment_id, state->model_version, state->input_tensor,
result.value());
@@ -187,8 +192,7 @@ void ModelExecutorImpl::OnModelExecutionComplete(
ModelExecutionStatus::kSuccess);
} else {
VLOG(1) << "Segmentation model returned no result for segment "
- << optimization_guide::proto::OptimizationTarget_Name(
- state->segment_id);
+ << proto::SegmentId_Name(state->segment_id);
RunModelExecutionCallback(std::move(state), 0,
ModelExecutionStatus::kExecutionError);
}
diff --git a/chromium/components/segmentation_platform/internal/execution/model_executor_impl.h b/chromium/components/segmentation_platform/internal/execution/model_executor_impl.h
index 283efc892fe..b5c15c0ff54 100644
--- a/chromium/components/segmentation_platform/internal/execution/model_executor_impl.h
+++ b/chromium/components/segmentation_platform/internal/execution/model_executor_impl.h
@@ -16,28 +16,29 @@
namespace segmentation_platform {
+namespace processing {
class FeatureListQueryProcessor;
-
-// Uses SignalDatabase (raw signals), and uses a FeatureListQueryProcessor for
-// each feature to go from metadata and raw signals to create an input tensor to
-// use when executing the ML model. It then uses this input tensor to execute
-// the model and returns the result through a callback. Uses a state within
-// callbacks for executing multiple models simultaneously, or the same model
-// multiple times without waiting for the requests to finish.
+}
+
+// Uses SignalDatabase (raw signals), and uses a
+// processing::FeatureListQueryProcessor for each feature to go from metadata
+// and raw signals to create an input tensor to use when executing the ML model.
+// It then uses this input tensor to execute the model and returns the result
+// through a callback. Uses a state within callbacks for executing multiple
+// models simultaneously, or the same model multiple times without waiting for
+// the requests to finish.
class ModelExecutorImpl : public ModelExecutor {
public:
- ModelExecutorImpl(base::Clock* clock,
- FeatureListQueryProcessor* feature_list_query_processor);
+ ModelExecutorImpl(
+ base::Clock* clock,
+ processing::FeatureListQueryProcessor* feature_list_query_processor);
~ModelExecutorImpl() override;
ModelExecutorImpl(ModelExecutorImpl&) = delete;
ModelExecutorImpl& operator=(ModelExecutorImpl&) = delete;
// ModelExecutionManager impl:.
- void ExecuteModel(const proto::SegmentInfo& segment_info,
- ModelProvider* model_provider,
- bool record_metrics_for_default,
- ModelExecutionCallback callback) override;
+ void ExecuteModel(std::unique_ptr<ExecutionRequest> request) override;
private:
struct ExecutionState;
@@ -48,7 +49,8 @@ class ModelExecutorImpl : public ModelExecutor {
// for executing the model.
void OnProcessingFeatureListComplete(std::unique_ptr<ExecutionState> state,
bool error,
- const std::vector<float>& input_tensor);
+ const std::vector<float>& input_tensor,
+ const std::vector<float>& output_tensor);
// ExecuteModel takes the current input tensor and passes it to the ML
// model for execution.
@@ -69,7 +71,8 @@ class ModelExecutorImpl : public ModelExecutor {
const raw_ptr<base::Clock> clock_;
// Feature list processor for processing a model metadata's feature list.
- const raw_ptr<FeatureListQueryProcessor> feature_list_query_processor_;
+ const raw_ptr<processing::FeatureListQueryProcessor>
+ feature_list_query_processor_;
base::WeakPtrFactory<ModelExecutorImpl> weak_ptr_factory_{this};
};
diff --git a/chromium/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc b/chromium/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
index e3d7dbfcbd0..a5ce7025c81 100644
--- a/chromium/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
@@ -18,23 +18,25 @@
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/mock_signal_database.h"
#include "components/segmentation_platform/internal/database/signal_database.h"
#include "components/segmentation_platform/internal/database/test_segment_info_database.h"
-#include "components/segmentation_platform/internal/execution/mock_feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/execution_request.h"
#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
#include "components/segmentation_platform/internal/execution/model_execution_status.h"
#include "components/segmentation_platform/internal/execution/model_executor.h"
+#include "components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::base::test::RunOnceCallback;
+using segmentation_platform::processing::FeatureListQueryProcessor;
using testing::_;
using testing::Invoke;
using testing::Return;
@@ -43,8 +45,8 @@ using testing::SetArgReferee;
namespace segmentation_platform {
-const OptimizationTarget kSegmentId =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+const SegmentId kSegmentId =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
class ModelExecutorTest : public testing::Test {
public:
@@ -65,7 +67,7 @@ class ModelExecutorTest : public testing::Test {
void CreateModelExecutor() {
feature_list_query_processor_ =
- std::make_unique<MockFeatureListQueryProcessor>();
+ std::make_unique<processing::MockFeatureListQueryProcessor>();
model_executor_ = std::make_unique<ModelExecutorImpl>(
&clock_, feature_list_query_processor_.get());
}
@@ -76,10 +78,14 @@ class ModelExecutorTest : public testing::Test {
ModelProvider* model,
const std::pair<float, ModelExecutionStatus>& expected) {
base::RunLoop loop;
- model_executor_->ExecuteModel(
- info, model, /*record_metrics_for_default=*/false,
+ auto request = std::make_unique<ExecutionRequest>();
+ request->segment_info = &info;
+ request->model_provider = model;
+ request->save_result_to_db = false;
+ request->callback =
base::BindOnce(&ModelExecutorTest::OnExecutionCallback,
- base::Unretained(this), loop.QuitClosure(), expected));
+ base::Unretained(this), loop.QuitClosure(), expected);
+ model_executor_->ExecuteModel(std::move(request));
loop.Run();
}
@@ -99,7 +105,8 @@ class ModelExecutorTest : public testing::Test {
base::SimpleTestClock clock_;
std::unique_ptr<MockSignalDatabase> signal_database_;
- std::unique_ptr<MockFeatureListQueryProcessor> feature_list_query_processor_;
+ std::unique_ptr<processing::MockFeatureListQueryProcessor>
+ feature_list_query_processor_;
std::unique_ptr<ModelExecutorImpl> model_executor_;
};
@@ -143,16 +150,18 @@ TEST_F(ModelExecutorTest, FailedFeatureProcessing) {
// Initialize with required metadata.
test::TestSegmentInfoDatabase metadata_writer;
- const OptimizationTarget segment_id = kSegmentId;
+ const SegmentId segment_id = kSegmentId;
metadata_writer.SetBucketDuration(segment_id, 3, proto::TimeUnit::HOUR);
std::string user_action_name = "some_user_action";
metadata_writer.AddUserActionFeature(segment_id, user_action_name, 3, 3,
proto::Aggregation::BUCKETED_COUNT);
EXPECT_CALL(*feature_list_query_processor_,
- ProcessFeatureList(_, segment_id, clock_.Now(), _))
- .WillOnce(
- RunOnceCallback<3>(/*error=*/true, std::vector<float>{1, 2, 3}));
+ ProcessFeatureList(
+ _, _, segment_id, clock_.Now(),
+ FeatureListQueryProcessor::ProcessOption::kInputsOnly, _))
+ .WillOnce(RunOnceCallback<5>(/*error=*/true, std::vector<float>{1, 2, 3},
+ std::vector<float>()));
// The input tensor should contain all values flattened to a single vector.
EXPECT_CALL(mock_model_, ModelAvailable()).WillRepeatedly(Return(true));
@@ -163,8 +172,11 @@ TEST_F(ModelExecutorTest, FailedFeatureProcessing) {
std::make_pair(0, ModelExecutionStatus::kSkippedInvalidMetadata));
EXPECT_CALL(*feature_list_query_processor_,
- ProcessFeatureList(_, segment_id, clock_.Now(), _))
- .WillOnce(RunOnceCallback<3>(/*error=*/true, std::vector<float>{}));
+ ProcessFeatureList(
+ _, _, segment_id, clock_.Now(),
+ FeatureListQueryProcessor::ProcessOption::kInputsOnly, _))
+ .WillOnce(RunOnceCallback<5>(/*error=*/true, std::vector<float>(),
+ std::vector<float>()));
ExecuteModel(
*metadata_writer.FindOrCreateSegment(segment_id), &mock_model_,
std::make_pair(0, ModelExecutionStatus::kSkippedInvalidMetadata));
@@ -181,9 +193,12 @@ TEST_F(ModelExecutorTest, ExecuteModelWithMultipleFeatures) {
proto::Aggregation::BUCKETED_COUNT);
EXPECT_CALL(*feature_list_query_processor_,
- ProcessFeatureList(_, kSegmentId, clock_.Now(), _))
- .WillOnce(RunOnceCallback<3>(/*error=*/false,
- std::vector<float>{1, 2, 3, 4, 5, 6, 7}));
+ ProcessFeatureList(
+ _, _, kSegmentId, clock_.Now(),
+ FeatureListQueryProcessor::ProcessOption::kInputsOnly, _))
+ .WillOnce(RunOnceCallback<5>(/*error=*/false,
+ std::vector<float>{1, 2, 3, 4, 5, 6, 7},
+ std::vector<float>()));
// The input tensor should contain all values flattened to a single vector.
EXPECT_CALL(mock_model_, ModelAvailable()).WillRepeatedly(Return(true));
diff --git a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
index 949d6a60bb8..44205cf7474 100644
--- a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
+++ b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
@@ -12,6 +12,7 @@
#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/internal/segment_id_convertor.h"
#include "components/segmentation_platform/internal/stats.h"
namespace segmentation_platform {
@@ -20,18 +21,19 @@ OptimizationGuideSegmentationModelHandler::
OptimizationGuideSegmentationModelHandler(
optimization_guide::OptimizationGuideModelProvider* model_provider,
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
- optimization_guide::proto::OptimizationTarget optimization_target,
+ optimization_guide::proto::OptimizationTarget segment_id,
const ModelUpdatedCallback& model_updated_callback,
absl::optional<optimization_guide::proto::Any>&& model_metadata)
: optimization_guide::ModelHandler<float, const std::vector<float>&>(
model_provider,
background_task_runner,
std::make_unique<SegmentationModelExecutor>(),
- optimization_target,
+ /*model_inference_timeout=*/absl::nullopt,
+ segment_id,
model_metadata),
model_updated_callback_(model_updated_callback) {
stats::RecordModelAvailability(
- optimization_target,
+ OptimizationTargetToSegmentId(segment_id),
stats::SegmentationModelAvailability::kModelHandlerCreated);
}
@@ -39,12 +41,11 @@ OptimizationGuideSegmentationModelHandler::
~OptimizationGuideSegmentationModelHandler() = default;
void OptimizationGuideSegmentationModelHandler::OnModelUpdated(
- optimization_guide::proto::OptimizationTarget optimization_target,
+ optimization_guide::proto::OptimizationTarget segment_id,
const optimization_guide::ModelInfo& model_info) {
// First invoke parent to update internal status.
optimization_guide::ModelHandler<
- float, const std::vector<float>&>::OnModelUpdated(optimization_target,
- model_info);
+ float, const std::vector<float>&>::OnModelUpdated(segment_id, model_info);
// The parent class should always set the model availability to true after
// having received an updated model.
DCHECK(ModelAvailable());
@@ -54,22 +55,23 @@ void OptimizationGuideSegmentationModelHandler::OnModelUpdated(
absl::optional<proto::SegmentationModelMetadata> segmentation_model_metadata =
ParsedSupportedFeaturesForLoadedModel<proto::SegmentationModelMetadata>();
stats::RecordModelDeliveryHasMetadata(
- optimization_target, segmentation_model_metadata.has_value());
+ OptimizationTargetToSegmentId(segment_id),
+ segmentation_model_metadata.has_value());
if (!segmentation_model_metadata.has_value()) {
// This is not expected to happen, since the optimization guide server is
// expected to pass this along. Either something failed horribly on the way,
// we failed to read the metadata, or the server side configuration is
// wrong.
stats::RecordModelAvailability(
- optimization_target,
+ OptimizationTargetToSegmentId(segment_id),
stats::SegmentationModelAvailability::kMetadataInvalid);
return;
}
stats::RecordModelAvailability(
- optimization_target,
+ OptimizationTargetToSegmentId(segment_id),
stats::SegmentationModelAvailability::kModelAvailable);
- model_updated_callback_.Run(optimization_target,
+ model_updated_callback_.Run(OptimizationTargetToSegmentId(segment_id),
std::move(*segmentation_model_metadata),
model_info.GetVersion());
}
diff --git a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
index 4eb60ff1b44..a619f535c01 100644
--- a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
+++ b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
@@ -10,6 +10,7 @@
#include "components/optimization_guide/core/model_handler.h"
#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace optimization_guide {
class OptimizationGuideModelProvider;
@@ -29,15 +30,13 @@ class OptimizationGuideSegmentationModelHandler
: public optimization_guide::ModelHandler<float,
const std::vector<float>&> {
public:
- using ModelUpdatedCallback = base::RepeatingCallback<void(
- optimization_guide::proto::OptimizationTarget,
- proto::SegmentationModelMetadata,
- int64_t)>;
+ using ModelUpdatedCallback = base::RepeatingCallback<
+ void(proto::SegmentId, proto::SegmentationModelMetadata, int64_t)>;
explicit OptimizationGuideSegmentationModelHandler(
optimization_guide::OptimizationGuideModelProvider* model_provider,
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
- optimization_guide::proto::OptimizationTarget optimization_target,
+ optimization_guide::proto::OptimizationTarget segment_id,
const ModelUpdatedCallback& model_updated_callback,
absl::optional<optimization_guide::proto::Any>&& model_metadata);
@@ -50,9 +49,8 @@ class OptimizationGuideSegmentationModelHandler
const OptimizationGuideSegmentationModelHandler&) = delete;
// optimization_guide::ModelHandler overrides.
- void OnModelUpdated(
- optimization_guide::proto::OptimizationTarget optimization_target,
- const optimization_guide::ModelInfo& model_info) override;
+ void OnModelUpdated(optimization_guide::proto::OptimizationTarget segment_id,
+ const optimization_guide::ModelInfo& model_info) override;
private:
// Callback to invoke whenever the model file has been updated. If there is
diff --git a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc
index be5e59dafc4..0658c30c104 100644
--- a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc
+++ b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc
@@ -10,11 +10,12 @@
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/optimization_guide/core/model_executor.h"
#include "components/optimization_guide/proto/common_types.pb.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h"
#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/internal/segment_id_convertor.h"
#include "components/segmentation_platform/internal/stats.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace segmentation_platform {
@@ -43,8 +44,8 @@ OptimizationGuideSegmentationModelProvider::
OptimizationGuideSegmentationModelProvider(
optimization_guide::OptimizationGuideModelProvider* model_provider,
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
- optimization_guide::proto::OptimizationTarget optimization_target)
- : ModelProvider(optimization_target),
+ proto::SegmentId segment_id)
+ : ModelProvider(segment_id),
model_provider_(model_provider),
background_task_runner_(background_task_runner) {}
@@ -54,9 +55,16 @@ OptimizationGuideSegmentationModelProvider::
void OptimizationGuideSegmentationModelProvider::InitAndFetchModel(
const ModelUpdatedCallback& model_updated_callback) {
DCHECK(!model_handler_);
+ absl::optional<optimization_guide::proto::OptimizationTarget> target =
+ SegmentIdToOptimizationTarget(segment_id_);
+ if (!target) {
+ // If the segment ID is not an OptimizationTarget then do not request a
+ // model.
+ return;
+ }
model_handler_ = std::make_unique<OptimizationGuideSegmentationModelHandler>(
- model_provider_, background_task_runner_, optimization_target_,
- model_updated_callback, GetModelFetchConfig());
+ model_provider_, background_task_runner_, *target, model_updated_callback,
+ GetModelFetchConfig());
}
void OptimizationGuideSegmentationModelProvider::ExecuteModelWithInput(
diff --git a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h
index 2e7bdeeebe2..c1bbe2af24b 100644
--- a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h
+++ b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h
@@ -9,8 +9,8 @@
#include <vector>
#include "components/optimization_guide/core/model_handler.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace optimization_guide {
class OptimizationGuideSegmentationModelProvider;
@@ -27,7 +27,7 @@ class OptimizationGuideSegmentationModelProvider : public ModelProvider {
OptimizationGuideSegmentationModelProvider(
optimization_guide::OptimizationGuideModelProvider* model_provider,
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
- optimization_guide::proto::OptimizationTarget optimization_target);
+ proto::SegmentId segment_id);
~OptimizationGuideSegmentationModelProvider() override;
diff --git a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc
index 55860815954..7e27f9618f6 100644
--- a/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc
@@ -4,8 +4,8 @@
#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h"
+#include "base/task/thread_pool.h"
#include "base/test/task_environment.h"
-#include "base/test/test_simple_task_runner.h"
#include "components/optimization_guide/core/optimization_guide_util.h"
#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
@@ -25,8 +25,7 @@ class ModelObserverTracker
registered_model_metadata_.insert_or_assign(target, model_metadata);
}
- bool DidRegisterForTarget(
- optimization_guide::proto::OptimizationTarget target) const {
+ bool DidRegisterForTarget(proto::SegmentId target) const {
auto it = registered_model_metadata_.find(target);
if (it == registered_model_metadata_.end())
return false;
@@ -57,70 +56,66 @@ class OptimizationGuideSegmentationModelProviderTest : public testing::Test {
~OptimizationGuideSegmentationModelProviderTest() override = default;
void SetUp() override {
- task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
model_observer_tracker_ = std::make_unique<ModelObserverTracker>();
}
void TearDown() override {
model_observer_tracker_.reset();
- task_runner_->RunPendingTasks();
+ RunUntilIdle();
}
+ void RunUntilIdle() { task_environment_.RunUntilIdle(); }
+
std::unique_ptr<OptimizationGuideSegmentationModelProvider>
- CreateModelProvider(optimization_guide::proto::OptimizationTarget target) {
+ CreateModelProvider(proto::SegmentId target) {
return std::make_unique<OptimizationGuideSegmentationModelProvider>(
- model_observer_tracker_.get(), task_runner_, target);
+ model_observer_tracker_.get(),
+ base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}),
+ target);
}
protected:
base::test::TaskEnvironment task_environment_;
- scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-
std::unique_ptr<ModelObserverTracker> model_observer_tracker_;
};
TEST_F(OptimizationGuideSegmentationModelProviderTest, InitAndFetchModel) {
std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider =
- CreateModelProvider(optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ CreateModelProvider(
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
// Not initialized yet.
EXPECT_FALSE(model_observer_tracker_->DidRegisterForTarget(
- optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
// Init should register observer.
provider->InitAndFetchModel(base::DoNothing());
EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
- optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
// Different target does not register yet.
EXPECT_FALSE(model_observer_tracker_->DidRegisterForTarget(
- optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
// Initialize voice provider.
std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider2 =
- CreateModelProvider(optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
+ CreateModelProvider(
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
provider2->InitAndFetchModel(base::DoNothing());
// 2 observers should be available:
EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
- optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
- optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
}
TEST_F(OptimizationGuideSegmentationModelProviderTest,
ExecuteModelWithoutFetch) {
std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider =
- CreateModelProvider(optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ CreateModelProvider(
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
base::RunLoop run_loop;
std::vector<float> input = {4, 5};
@@ -133,16 +128,16 @@ TEST_F(OptimizationGuideSegmentationModelProviderTest,
},
&run_loop));
run_loop.Run();
+ RunUntilIdle();
}
TEST_F(OptimizationGuideSegmentationModelProviderTest, ExecuteModelWithFetch) {
std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider =
- CreateModelProvider(optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ CreateModelProvider(
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
provider->InitAndFetchModel(base::DoNothing());
EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
- optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
base::RunLoop run_loop;
std::vector<float> input = {4, 5};
@@ -157,8 +152,8 @@ TEST_F(OptimizationGuideSegmentationModelProviderTest, ExecuteModelWithFetch) {
run_loop->Quit();
},
&run_loop));
- task_runner_->RunPendingTasks();
run_loop.Run();
+ RunUntilIdle();
}
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h b/chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
index 02cbdcdd19d..3077e9e4ff6 100644
--- a/chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
+++ b/chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
@@ -9,7 +9,7 @@
#include <vector>
#include "components/optimization_guide/core/base_model_executor.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
struct TfLiteTensor;
diff --git a/chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc b/chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
index 87b8409bda7..5d3561b8499 100644
--- a/chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
@@ -13,30 +13,32 @@
#include "base/check.h"
#include "base/path_service.h"
#include "base/run_loop.h"
+#include "base/task/thread_pool.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "components/optimization_guide/core/optimization_guide_model_provider.h"
#include "components/optimization_guide/core/test_model_info_builder.h"
#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
#include "components/optimization_guide/proto/common_types.pb.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h"
#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/internal/segment_id_convertor.h"
#include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+namespace segmentation_platform {
namespace {
-const auto kOptimizationTarget = optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+const auto kSegmentId =
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
const int64_t kModelVersion = 123;
} // namespace
-namespace segmentation_platform {
bool AreEqual(const proto::SegmentationModelMetadata& a,
const proto::SegmentationModelMetadata& b) {
// Serializing two protos and comparing them is unsafe, in particular if they
@@ -75,7 +77,8 @@ class SegmentationModelExecutorTest : public testing::Test {
opt_guide_model_provider_ =
std::make_unique<OptimizationGuideSegmentationModelProvider>(
optimization_guide_segmentation_model_provider_.get(),
- task_environment_.GetMainThreadTaskRunner(), kOptimizationTarget);
+ base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}),
+ kSegmentId);
opt_guide_model_provider_->InitAndFetchModel(callback);
}
@@ -110,8 +113,8 @@ class SegmentationModelExecutorTest : public testing::Test {
.SetModelFilePath(model_file_path_)
.SetVersion(kModelVersion)
.Build();
- opt_guide_model_handler().OnModelUpdated(kOptimizationTarget,
- *model_metadata);
+ opt_guide_model_handler().OnModelUpdated(
+ *SegmentIdToOptimizationTarget(kSegmentId), *model_metadata);
RunUntilIdle();
}
@@ -141,11 +144,11 @@ TEST_F(SegmentationModelExecutorTest, ExecuteWithLoadedModel) {
CreateModelExecutor(base::BindRepeating(
[](base::RunLoop* run_loop,
proto::SegmentationModelMetadata original_metadata,
- optimization_guide::proto::OptimizationTarget optimization_target,
+ proto::SegmentId segment_id,
proto::SegmentationModelMetadata actual_metadata,
int64_t model_version) {
// Verify that the callback is invoked with the correct data.
- EXPECT_EQ(kOptimizationTarget, optimization_target);
+ EXPECT_EQ(kSegmentId, segment_id);
EXPECT_TRUE(AreEqual(original_metadata, actual_metadata));
EXPECT_EQ(kModelVersion, model_version);
run_loop->Quit();
diff --git a/chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc b/chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc
new file mode 100644
index 00000000000..611540e7238
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc
@@ -0,0 +1,249 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/processing/custom_input_processor.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+#include "components/segmentation_platform/internal/execution/processing/input_delegate.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+
+namespace segmentation_platform::processing {
+
+namespace {
+// Index not actually used for legacy code in FeatureQueryProcessor.
+const int kIndexNotUsed = 0;
+
+absl::optional<int> GetArgAsInt(
+ const google::protobuf::Map<std::string, std::string>& args,
+ const std::string& key) {
+ int value;
+ auto iter = args.find(key);
+
+ // Did not find target key.
+ if (iter == args.end())
+ return absl::optional<int>();
+
+ // Perform string to int conversion, return empty value if the conversion
+ // failed.
+ if (!base::StringToInt(base::StringPiece(iter->second), &value))
+ return absl::optional<int>();
+
+ return absl::optional<int>(value);
+}
+
+} // namespace
+
+CustomInputProcessor::CustomInputProcessor(
+ const base::Time prediction_time,
+ InputDelegateHolder* input_delegate_holder)
+ : input_delegate_holder_(input_delegate_holder),
+ prediction_time_(prediction_time) {}
+
+CustomInputProcessor::CustomInputProcessor(
+ base::flat_map<FeatureIndex, proto::CustomInput>&& custom_inputs,
+ const base::Time prediction_time,
+ InputDelegateHolder* input_delegate_holder)
+ : input_delegate_holder_(input_delegate_holder),
+ custom_inputs_(std::move(custom_inputs)),
+ prediction_time_(prediction_time) {}
+
+CustomInputProcessor::~CustomInputProcessor() = default;
+
+void CustomInputProcessor::ProcessCustomInput(
+ const proto::CustomInput& custom_input,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ FeatureListQueryProcessorCallback callback) {
+ DCHECK(custom_inputs_.empty());
+ prediction_time_ = feature_processor_state->prediction_time();
+ custom_inputs_[kIndexNotUsed] = custom_input;
+ Process(std::move(feature_processor_state),
+ base::BindOnce(&CustomInputProcessor::OnFinishProcessing,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void CustomInputProcessor::OnFinishProcessing(
+ FeatureListQueryProcessorCallback callback,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ IndexedTensors result) {
+ custom_inputs_.clear();
+ feature_processor_state->AppendTensor(result[kIndexNotUsed],
+ true /*is_input*/);
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(callback), std::move(feature_processor_state)));
+}
+
+void CustomInputProcessor::Process(
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ QueryProcessorCallback callback) {
+ auto result = std::make_unique<base::flat_map<FeatureIndex, Tensor>>();
+ ProcessIndexType<FeatureIndex>(std::move(custom_inputs_),
+ std::move(feature_processor_state),
+ std::move(result), std::move(callback));
+}
+
+template <typename IndexType>
+void CustomInputProcessor::ProcessIndexType(
+ base::flat_map<IndexType, proto::CustomInput> custom_inputs,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ std::unique_ptr<base::flat_map<IndexType, Tensor>> result,
+ TemplateCallback<IndexType> callback) {
+ bool success = true;
+ auto it = custom_inputs.begin();
+ for (; it != custom_inputs.end(); it = custom_inputs.begin()) {
+ // Get the next feature in the list to process.
+ const proto::CustomInput custom_input(std::move(it->second));
+ const IndexType index = it->first;
+ custom_inputs.erase(it);
+
+ InputDelegate* input_delegate = nullptr;
+ if (input_delegate_holder_) {
+ input_delegate =
+ input_delegate_holder_->GetDelegate(custom_input.fill_policy());
+ }
+ if (input_delegate) {
+ // If a delegate is available then use it to process the input. All the
+ // state in this method is moved, so it is ok even if the client ran the
+ // callback without posting it.
+ const FeatureProcessorState& state = *feature_processor_state;
+ input_delegate->Process(
+ custom_input, state,
+ base::BindOnce(
+ &CustomInputProcessor::OnGotProcessedValue<IndexType>,
+ weak_ptr_factory_.GetWeakPtr(), std::move(custom_inputs),
+ std::move(feature_processor_state), std::move(result),
+ std::move(callback), index, custom_input.tensor_length()));
+ return;
+ }
+
+ // Skip custom input with tensor length of 0.
+ if (custom_input.tensor_length() == 0) {
+ continue;
+ }
+ // Validate the proto::CustomInput metadata.
+ if (metadata_utils::ValidateMetadataCustomInput(custom_input) !=
+ metadata_utils::ValidationResult::kValidationSuccess) {
+ success = false;
+ } else {
+ (*result)[index] =
+ ProcessSingleCustomInput(custom_input, feature_processor_state.get());
+ }
+ }
+
+ // Processing of the feature list has completed.
+ DCHECK(custom_inputs.empty());
+ if (!success || feature_processor_state->error()) {
+ result->clear();
+ feature_processor_state->SetError();
+ }
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(callback), std::move(feature_processor_state),
+ std::move(*result)));
+}
+
+template <typename IndexType>
+void CustomInputProcessor::OnGotProcessedValue(
+ base::flat_map<IndexType, proto::CustomInput> custom_inputs,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ std::unique_ptr<base::flat_map<IndexType, Tensor>> result,
+ TemplateCallback<IndexType> callback,
+ IndexType current_index,
+ size_t current_tensor_length,
+ bool error,
+ Tensor current_value) {
+ if (error) {
+ feature_processor_state->SetError();
+ } else {
+ DCHECK_EQ(current_tensor_length, current_value.size());
+ }
+ (*result)[current_index] = std::move(current_value);
+ ProcessIndexType<IndexType>(std::move(custom_inputs),
+ std::move(feature_processor_state),
+ std::move(result), std::move(callback));
+}
+
+using SqlCustomInputIndex = std::pair<int, int>;
+template void CustomInputProcessor::ProcessIndexType(
+ base::flat_map<SqlCustomInputIndex, proto::CustomInput> custom_inputs,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ std::unique_ptr<base::flat_map<SqlCustomInputIndex, Tensor>> result,
+ TemplateCallback<std::pair<int, int>> callback);
+
+template void CustomInputProcessor::OnGotProcessedValue(
+ base::flat_map<SqlCustomInputIndex, proto::CustomInput> custom_inputs,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ std::unique_ptr<base::flat_map<SqlCustomInputIndex, Tensor>> result,
+ TemplateCallback<SqlCustomInputIndex> callback,
+ SqlCustomInputIndex current_index,
+ size_t current_tensor_length,
+ bool success,
+ Tensor current_value);
+
+QueryProcessor::Tensor CustomInputProcessor::ProcessSingleCustomInput(
+ const proto::CustomInput& custom_input,
+ FeatureProcessorState* feature_processor_state) {
+ std::vector<ProcessedValue> tensor_result;
+ if (custom_input.fill_policy() == proto::CustomInput::UNKNOWN_FILL_POLICY) {
+ // When parsing a CustomInput object, if the fill policy is not
+ // supported by the current version of the client, the fill policy field
+ // will not be filled. When this happens, the custom input processor
+ // will either use the default values to generate an input tensor or
+ // fail the model execution.
+ tensor_result = std::vector<ProcessedValue>(
+ custom_input.default_value().begin(),
+ custom_input.default_value().begin() + custom_input.tensor_length());
+ } else if (custom_input.fill_policy() ==
+ proto::CustomInput::FILL_PREDICTION_TIME) {
+ if (!AddPredictionTime(custom_input, tensor_result))
+ feature_processor_state->SetError();
+ } else if (custom_input.fill_policy() ==
+ proto::CustomInput::TIME_RANGE_BEFORE_PREDICTION) {
+ if (!AddTimeRangeBeforePrediction(custom_input, tensor_result))
+ feature_processor_state->SetError();
+ } else if (custom_input.fill_policy() ==
+ proto::CustomInput::PRICE_TRACKING_HINTS) {
+ feature_processor_state->SetError();
+ NOTREACHED() << "InputDelegate is not found";
+ }
+ return tensor_result;
+}
+
+bool CustomInputProcessor::AddPredictionTime(
+ const proto::CustomInput& custom_input,
+ std::vector<ProcessedValue>& out_tensor) {
+ if (custom_input.tensor_length() != 1) {
+ return false;
+ }
+ out_tensor.emplace_back(prediction_time_);
+ return true;
+}
+
+bool CustomInputProcessor::AddTimeRangeBeforePrediction(
+ const proto::CustomInput& custom_input,
+ std::vector<ProcessedValue>& out_tensor) {
+ if (custom_input.tensor_length() != 2) {
+ return false;
+ }
+
+ constexpr char kBucketCountArg[] = "bucket_count";
+ absl::optional<int> bucket_count =
+ GetArgAsInt(custom_input.additional_args(), kBucketCountArg);
+
+ if (bucket_count.has_value()) {
+ out_tensor.emplace_back(prediction_time_ -
+ base::Days(bucket_count.value()));
+ out_tensor.emplace_back(prediction_time_);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/custom_input_processor.h b/chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor.h
index 5ba2a68476d..447c1aa60e3 100644
--- a/chromium/components/segmentation_platform/internal/execution/custom_input_processor.h
+++ b/chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor.h
@@ -2,29 +2,31 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_CUSTOM_INPUT_PROCESSOR_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_CUSTOM_INPUT_PROCESSOR_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_CUSTOM_INPUT_PROCESSOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_CUSTOM_INPUT_PROCESSOR_H_
#include <vector>
#include "base/containers/flat_map.h"
#include "base/time/time.h"
-#include "components/segmentation_platform/internal/execution/query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/query_processor.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
class FeatureProcessorState;
+class InputDelegateHolder;
// CustomInputProcessor adds support to a larger variety of data type
// (timestamps, strings mapped to enums, etc), transforming them into valid
// input tensor to use when executing the ML model.
class CustomInputProcessor : public QueryProcessor {
public:
- explicit CustomInputProcessor();
- explicit CustomInputProcessor(const base::Time prediction_time);
- explicit CustomInputProcessor(
+ CustomInputProcessor(const base::Time prediction_time,
+ InputDelegateHolder* input_delegate_holder);
+ CustomInputProcessor(
base::flat_map<FeatureIndex, proto::CustomInput>&& custom_inputs,
- const base::Time prediction_time);
+ const base::Time prediction_time,
+ InputDelegateHolder* input_delegate_holder);
~CustomInputProcessor() override;
using FeatureListQueryProcessorCallback =
@@ -57,18 +59,45 @@ class CustomInputProcessor : public QueryProcessor {
base::flat_map<IndexType, Tensor>)>;
// Process a data mapping with a customized index type and return the tensor
- // values in |callback|.
+ // values in |callback|. Appends the input to the provided `result` and
+ // returns it.
template <typename IndexType>
void ProcessIndexType(
base::flat_map<IndexType, proto::CustomInput> custom_inputs,
std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ std::unique_ptr<base::flat_map<IndexType, Tensor>> result,
TemplateCallback<IndexType> callback);
private:
- // Helper function for parsing a single custom input and return the result
- // along with the corresponding feature index.
+ // Helper method to handle async custom inputs for `ProcessIndexType()`
+ template <typename IndexType>
+ void OnGotProcessedValue(
+ base::flat_map<IndexType, proto::CustomInput> custom_inputs,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ std::unique_ptr<base::flat_map<IndexType, Tensor>> result,
+ TemplateCallback<IndexType> callback,
+ IndexType current_index,
+ size_t current_tensor_length,
+ bool error,
+ Tensor current_value);
+
+ // Helper function for parsing a single sync custom input and insert the
+ // result along with the corresponding feature index.
QueryProcessor::Tensor ProcessSingleCustomInput(
- const proto::CustomInput& custom_input);
+ const proto::CustomInput& custom_input,
+ FeatureProcessorState* feature_processor_state);
+
+ // Add a tensor value for CustomInput::FILL_PREDICTION_TIME type and return
+ // whether it succeeded.
+ bool AddPredictionTime(const proto::CustomInput& custom_input,
+ std::vector<ProcessedValue>& out_tensor);
+
+ // Add a tensor value for CustomInput::TIME_RANGE_BEFORE_PREDICTION type and
+ // return whether it succeeded.
+ bool AddTimeRangeBeforePrediction(const proto::CustomInput& custom_input,
+ std::vector<ProcessedValue>& out_tensor);
+
+ const raw_ptr<InputDelegateHolder> input_delegate_holder_;
// List of custom inputs to process into input tensors.
base::flat_map<FeatureIndex, proto::CustomInput> custom_inputs_;
@@ -79,6 +108,6 @@ class CustomInputProcessor : public QueryProcessor {
base::WeakPtrFactory<CustomInputProcessor> weak_ptr_factory_{this};
};
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_CUSTOM_INPUT_PROCESSOR_H_
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_CUSTOM_INPUT_PROCESSOR_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor_unittest.cc b/chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor_unittest.cc
new file mode 100644
index 00000000000..552b951cb1d
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/processing/custom_input_processor_unittest.cc
@@ -0,0 +1,294 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/processing/custom_input_processor.h"
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/simple_test_clock.h"
+#include "base/test/task_environment.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+#include "components/segmentation_platform/internal/execution/processing/input_delegate.h"
+#include "components/segmentation_platform/internal/execution/processing/query_processor.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace segmentation_platform::processing {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+
+class MockInputDelegate : public InputDelegate {
+ public:
+ MOCK_METHOD3(Process,
+ void(const proto::CustomInput& input,
+ const FeatureProcessorState& feature_processor_state,
+ ProcessedCallback callback));
+};
+
+} // namespace
+
+class CustomInputProcessorTest : public testing::Test {
+ public:
+ CustomInputProcessorTest() = default;
+ ~CustomInputProcessorTest() override = default;
+
+ void SetUp() override {
+ clock_.SetNow(base::Time::Now());
+ feature_processor_state_ = std::make_unique<FeatureProcessorState>();
+ custom_input_processor_sql_ = std::make_unique<CustomInputProcessor>(
+ clock_.Now(), &input_delegate_holder_);
+ }
+
+ void TearDown() override {
+ feature_processor_state_.reset();
+ custom_input_processor_sql_.reset();
+ }
+
+ proto::CustomInput CreateCustomInput(
+ int tensor_length,
+ proto::CustomInput::FillPolicy fill_policy,
+ const std::vector<float>& default_values,
+ const std::vector<std::pair<std::string, std::string>>& additional_args) {
+ proto::CustomInput custom_input;
+ custom_input.set_fill_policy(fill_policy);
+ custom_input.set_tensor_length(tensor_length);
+
+ // Set default values.
+ for (float default_value : default_values)
+ custom_input.add_default_value(default_value);
+
+ // Add additional arguments.
+ custom_input.mutable_additional_args()->insert(additional_args.begin(),
+ additional_args.end());
+
+ return custom_input;
+ }
+
+ void ExpectProcessedCustomInput(
+ base::flat_map<int, proto::CustomInput>&& data,
+ bool expected_error,
+ const base::flat_map<int, QueryProcessor::Tensor>& expected_result) {
+ std::unique_ptr<CustomInputProcessor> custom_input_processor =
+ std::make_unique<CustomInputProcessor>(std::move(data), clock_.Now(),
+ &input_delegate_holder_);
+ std::unique_ptr<FeatureProcessorState> feature_processor_state =
+ std::make_unique<FeatureProcessorState>();
+
+ base::RunLoop loop;
+ custom_input_processor->Process(
+ std::move(feature_processor_state),
+ base::BindOnce(
+ &CustomInputProcessorTest::OnProcessingFinishedCallback<int>,
+ base::Unretained(this), loop.QuitClosure(), expected_error,
+ expected_result));
+ loop.Run();
+ }
+
+ template <typename IndexType>
+ void ExpectProcessedCustomInputsForSql(
+ const base::flat_map<IndexType, proto::CustomInput>& data,
+ bool expected_error,
+ const base::flat_map<IndexType, QueryProcessor::Tensor>&
+ expected_result) {
+ base::RunLoop loop;
+ custom_input_processor_sql_->ProcessIndexType<IndexType>(
+ data, std::move(feature_processor_state_),
+ std::make_unique<base::flat_map<IndexType, Tensor>>(),
+ base::BindOnce(
+ &CustomInputProcessorTest::OnProcessingFinishedCallback<IndexType>,
+ base::Unretained(this), loop.QuitClosure(), expected_error,
+ expected_result));
+ loop.Run();
+ }
+
+ template <typename IndexType>
+ void OnProcessingFinishedCallback(
+ base::RepeatingClosure closure,
+ bool expected_error,
+ const base::flat_map<IndexType, QueryProcessor::Tensor>& expected_result,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ base::flat_map<IndexType, QueryProcessor::Tensor> result) {
+ EXPECT_EQ(expected_error, feature_processor_state->error());
+ EXPECT_EQ(expected_result, result);
+ std::move(closure).Run();
+ }
+
+ protected:
+ base::test::TaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ base::SimpleTestClock clock_;
+ InputDelegateHolder input_delegate_holder_;
+ std::unique_ptr<FeatureProcessorState> feature_processor_state_;
+ std::unique_ptr<CustomInputProcessor> custom_input_processor_sql_;
+};
+
+TEST_F(CustomInputProcessorTest, IntTypeIndex) {
+ using IndexType = int;
+ IndexType index = 0;
+ base::flat_map<IndexType, proto::CustomInput> data;
+ data[index] =
+ CreateCustomInput(1, proto::CustomInput::FILL_PREDICTION_TIME, {}, {});
+
+ base::flat_map<IndexType, QueryProcessor::Tensor> expected_result;
+ expected_result[index] = {ProcessedValue(clock_.Now())};
+ ExpectProcessedCustomInputsForSql<IndexType>(data, /*expected_error=*/false,
+ expected_result);
+}
+
+TEST_F(CustomInputProcessorTest, IntPairTypeIndex) {
+ using IndexType = std::pair<int, int>;
+ IndexType index = std::make_pair(0, 0);
+ base::flat_map<IndexType, proto::CustomInput> data;
+ data[index] =
+ CreateCustomInput(1, proto::CustomInput::FILL_PREDICTION_TIME, {}, {});
+
+ base::flat_map<IndexType, QueryProcessor::Tensor> expected_result;
+ expected_result[index] = {ProcessedValue(clock_.Now())};
+ ExpectProcessedCustomInputsForSql<IndexType>(data, /*expected_error=*/false,
+ expected_result);
+};
+
+TEST_F(CustomInputProcessorTest, DefaultValueCustomInput) {
+ // Create custom inputs data.
+ int index = 0;
+ base::flat_map<int, proto::CustomInput> data;
+ data[index] =
+ CreateCustomInput(2, proto::CustomInput::UNKNOWN_FILL_POLICY, {1, 2}, {});
+
+ // Set expected tensor result.
+ base::flat_map<int, QueryProcessor::Tensor> expected_result;
+ expected_result[index] = {ProcessedValue(static_cast<float>(1)),
+ ProcessedValue(static_cast<float>(2))};
+
+ // Process the custom inputs and verify using expected result.
+ ExpectProcessedCustomInput(std::move(data), /*expected_error=*/false,
+ expected_result);
+}
+
+TEST_F(CustomInputProcessorTest, PredictionTimeCustomInput) {
+ // Create custom inputs data.
+ int index = 0;
+ base::flat_map<int, proto::CustomInput> data;
+ data[index] =
+ CreateCustomInput(1, proto::CustomInput::FILL_PREDICTION_TIME, {}, {});
+
+ // Set expected tensor result.
+ base::flat_map<int, QueryProcessor::Tensor> expected_result;
+ expected_result[index] = {ProcessedValue(clock_.Now())};
+
+ // Process the custom inputs and verify using expected result.
+ ExpectProcessedCustomInput(std::move(data), /*expected_error=*/false,
+ expected_result);
+}
+
+TEST_F(CustomInputProcessorTest, TimeRangeBeforePredictionCustomInput) {
+ // Create custom inputs data.
+ int index = 0;
+ base::flat_map<int, proto::CustomInput> data;
+ data[index] =
+ CreateCustomInput(2, proto::CustomInput::TIME_RANGE_BEFORE_PREDICTION, {},
+ {{"bucket_count", "1"}});
+
+ // Set expected tensor result.
+ base::flat_map<int, QueryProcessor::Tensor> expected_result;
+ expected_result[index] = {ProcessedValue(clock_.Now() - base::Days(1)),
+ ProcessedValue(clock_.Now())};
+
+ // Process the custom inputs and verify using expected result.
+ ExpectProcessedCustomInput(std::move(data), /*expected_error=*/false,
+ expected_result);
+}
+
+TEST_F(CustomInputProcessorTest, InvalidTimeRangeBeforePredictionCustomInput) {
+ // Create custom inputs data.
+ int index = 0;
+ base::flat_map<int, proto::CustomInput> data;
+ data[index] = CreateCustomInput(
+ 2, proto::CustomInput::TIME_RANGE_BEFORE_PREDICTION, {}, {});
+
+ // Set expected tensor result.
+ base::flat_map<int, QueryProcessor::Tensor> expected_result;
+
+ // Process the custom inputs and verify using expected result.
+ ExpectProcessedCustomInput(std::move(data), /*expected_error=*/true,
+ expected_result);
+}
+
+TEST_F(CustomInputProcessorTest, InputDelegateFailure) {
+ auto moved_delegate = std::make_unique<MockInputDelegate>();
+ MockInputDelegate* delegate = moved_delegate.get();
+ input_delegate_holder_.SetDelegate(proto::CustomInput::PRICE_TRACKING_HINTS,
+ std::move(moved_delegate));
+
+ int index = 0;
+ base::flat_map<int, proto::CustomInput> data;
+ data[index] =
+ CreateCustomInput(2, proto::CustomInput::PRICE_TRACKING_HINTS, {}, {});
+
+ EXPECT_CALL(*delegate, Process(_, _, _))
+ .WillOnce(RunOnceCallback<2>(true, Tensor()));
+ base::flat_map<int, QueryProcessor::Tensor> expected_result;
+ ExpectProcessedCustomInput(std::move(data), /*expected_error=*/true,
+ expected_result);
+}
+
+TEST_F(CustomInputProcessorTest, InputDelegate) {
+ auto moved_delegate = std::make_unique<MockInputDelegate>();
+ MockInputDelegate* delegate = moved_delegate.get();
+ input_delegate_holder_.SetDelegate(proto::CustomInput::PRICE_TRACKING_HINTS,
+ std::move(moved_delegate));
+
+ int index = 0;
+ base::flat_map<int, proto::CustomInput> data;
+ data[index] =
+ CreateCustomInput(2, proto::CustomInput::PRICE_TRACKING_HINTS, {}, {});
+
+ Tensor result{ProcessedValue(1), ProcessedValue(2)};
+ EXPECT_CALL(*delegate, Process(_, _, _))
+ .WillOnce(RunOnceCallback<2>(false, result));
+ base::flat_map<int, QueryProcessor::Tensor> expected_result{{index, result}};
+ ExpectProcessedCustomInput(std::move(data), /*expected_error=*/false,
+ expected_result);
+}
+
+TEST_F(CustomInputProcessorTest, MultipleFillTypesCustomInputs) {
+ auto moved_delegate = std::make_unique<MockInputDelegate>();
+ MockInputDelegate* delegate = moved_delegate.get();
+ input_delegate_holder_.SetDelegate(proto::CustomInput::PRICE_TRACKING_HINTS,
+ std::move(moved_delegate));
+ EXPECT_CALL(*delegate, Process(_, _, _))
+ .WillOnce(RunOnceCallback<2>(false, Tensor{ProcessedValue(1)}));
+
+ // Create custom inputs data.
+ base::flat_map<int, proto::CustomInput> data;
+ data[0] =
+ CreateCustomInput(1, proto::CustomInput::FILL_PREDICTION_TIME, {}, {});
+ data[1] =
+ CreateCustomInput(2, proto::CustomInput::UNKNOWN_FILL_POLICY, {1, 2}, {});
+ data[2] =
+ CreateCustomInput(1, proto::CustomInput::UNKNOWN_FILL_POLICY, {3}, {});
+ data[3] =
+ CreateCustomInput(1, proto::CustomInput::PRICE_TRACKING_HINTS, {}, {});
+
+ // Set expected tensor result.
+ base::flat_map<int, QueryProcessor::Tensor> expected_result;
+ expected_result[0] = {ProcessedValue(clock_.Now())};
+ expected_result[1] = {ProcessedValue(static_cast<float>(1)),
+ ProcessedValue(static_cast<float>(2))};
+ expected_result[2] = {ProcessedValue(static_cast<float>(3))};
+ expected_result[3] = {ProcessedValue(1)};
+
+ // Process the custom inputs and verify using expected result.
+ ExpectProcessedCustomInput(std::move(data), /*expected_error=*/false,
+ expected_result);
+}
+
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/feature_aggregator.h b/chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator.h
index 0d0de0cd1ef..58de415f243 100644
--- a/chromium/components/segmentation_platform/internal/execution/feature_aggregator.h
+++ b/chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_AGGREGATOR_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_AGGREGATOR_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_AGGREGATOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_AGGREGATOR_H_
#include <cstdint>
#include <utility>
@@ -14,7 +14,7 @@
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
// The FeatureAggregator is able to convert metadata and a vector of samples
// into a vector of resulting floats that the ML model accepts.
@@ -40,6 +40,6 @@ class FeatureAggregator {
std::vector<SignalDatabase::Sample>& samples) const = 0;
};
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_AGGREGATOR_H_
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_AGGREGATOR_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/feature_aggregator_impl.cc b/chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.cc
index 177cc5a33bc..ffc3345afbc 100644
--- a/chromium/components/segmentation_platform/internal/execution/feature_aggregator_impl.cc
+++ b/chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/segmentation_platform/internal/execution/feature_aggregator_impl.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.h"
#include <algorithm>
#include <cstdint>
@@ -13,12 +13,12 @@
#include "base/numerics/safe_conversions.h"
#include "base/time/time.h"
#include "components/segmentation_platform/internal/database/signal_database.h"
-#include "components/segmentation_platform/internal/execution/feature_aggregator.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_aggregator.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
using Sample = SignalDatabase::Sample;
namespace {
@@ -283,4 +283,4 @@ void FeatureAggregatorImpl::FilterEnumSamples(
samples.erase(new_end, samples.end());
}
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/feature_aggregator_impl.h b/chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.h
index b6b0e4bf32c..88db8447f2b 100644
--- a/chromium/components/segmentation_platform/internal/execution/feature_aggregator_impl.h
+++ b/chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.h
@@ -2,20 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_AGGREGATOR_IMPL_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_AGGREGATOR_IMPL_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_AGGREGATOR_IMPL_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_AGGREGATOR_IMPL_H_
#include <cstdint>
#include <vector>
#include "base/time/time.h"
#include "components/segmentation_platform/internal/database/signal_database.h"
-#include "components/segmentation_platform/internal/execution/feature_aggregator.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_aggregator.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
// Core implementation of the FeatureAggregator.
class FeatureAggregatorImpl : public FeatureAggregator {
@@ -40,6 +40,6 @@ class FeatureAggregatorImpl : public FeatureAggregator {
std::vector<SignalDatabase::Sample>& samples) const override;
};
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_AGGREGATOR_IMPL_H_
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_AGGREGATOR_IMPL_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/feature_aggregator_impl_unittest.cc b/chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator_impl_unittest.cc
index 4aa30fd4a98..cecffb644c7 100644
--- a/chromium/components/segmentation_platform/internal/execution/feature_aggregator_impl_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/execution/processing/feature_aggregator_impl_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/segmentation_platform/internal/execution/feature_aggregator_impl.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.h"
#include <cstdint>
#include <memory>
@@ -12,13 +12,13 @@
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "components/segmentation_platform/internal/database/signal_database.h"
-#include "components/segmentation_platform/internal/execution/feature_aggregator.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_aggregator.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
using Sample = SignalDatabase::Sample;
using proto::Aggregation;
using proto::SignalType;
@@ -312,4 +312,4 @@ TEST_F(FeatureAggregatorImplTest, FilterEnumSamples) {
EXPECT_EQ(4, samples[1].second);
}
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc b/chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc
new file mode 100644
index 00000000000..b15a73235c2
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc
@@ -0,0 +1,167 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/clock.h"
+#include "components/segmentation_platform/internal/database/storage_service.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "components/segmentation_platform/internal/execution/processing/custom_input_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+#include "components/segmentation_platform/internal/execution/processing/input_delegate.h"
+#include "components/segmentation_platform/internal/execution/processing/sql_feature_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/uma_feature_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/internal/stats.h"
+#include "components/segmentation_platform/internal/ukm_data_manager.h"
+
+namespace segmentation_platform::processing {
+
+namespace {
+// Index not actually used for legacy code in FeatureQueryProcessor.
+const int kIndexNotUsed = 0;
+} // namespace
+
+FeatureListQueryProcessor::FeatureListQueryProcessor(
+ StorageService* storage_service,
+ std::unique_ptr<InputDelegateHolder> input_delegate_holder,
+ std::unique_ptr<FeatureAggregator> feature_aggregator)
+ : storage_service_(storage_service),
+ input_delegate_holder_(std::move(input_delegate_holder)),
+ feature_aggregator_(std::move(feature_aggregator)) {}
+
+FeatureListQueryProcessor::~FeatureListQueryProcessor() = default;
+
+void FeatureListQueryProcessor::ProcessFeatureList(
+ const proto::SegmentationModelMetadata& model_metadata,
+ scoped_refptr<InputContext> input_context,
+ SegmentId segment_id,
+ base::Time prediction_time,
+ ProcessOption process_option,
+ FeatureProcessorCallback callback) {
+ // The total bucket duration is defined by product of the bucket_duration
+ // value and the length of related time_unit field, e.g. 28 * length(DAY).
+ base::TimeDelta time_unit_len = metadata_utils::GetTimeUnit(model_metadata);
+ base::TimeDelta bucket_duration =
+ model_metadata.bucket_duration() * time_unit_len;
+
+ // Grab the metadata for all the features, which will be processed one at a
+ // time, before executing the model.
+ std::deque<FeatureProcessorState::Data> features;
+ if (process_option == ProcessOption::kInputsOnly ||
+ process_option == ProcessOption::kInputsAndOutputs) {
+ for (int i = 0; i < model_metadata.features_size(); ++i) {
+ proto::InputFeature input_feature;
+ input_feature.mutable_uma_feature()->CopyFrom(model_metadata.features(i));
+ features.emplace_back(std::move(input_feature));
+ }
+ for (int i = 0; i < model_metadata.input_features_size(); ++i)
+ features.emplace_back(model_metadata.input_features(i));
+ }
+
+ if (process_option == ProcessOption::kOutputsOnly ||
+ process_option == ProcessOption::kInputsAndOutputs) {
+ for (auto& output : model_metadata.training_outputs().outputs()) {
+ DCHECK(output.has_uma_output()) << "Currently only support UMA output.";
+ features.emplace_back(std::move(output));
+ }
+ }
+
+ // Capture all the relevant metadata information into a FeatureProcessorState.
+ auto feature_processor_state = std::make_unique<FeatureProcessorState>(
+ prediction_time, bucket_duration, segment_id, std::move(features),
+ input_context, std::move(callback));
+
+ ProcessNext(std::move(feature_processor_state));
+}
+
+void FeatureListQueryProcessor::ProcessNext(
+ std::unique_ptr<FeatureProcessorState> feature_processor_state) {
+ // Finished processing all input features or an error occurred.
+ if (feature_processor_state->IsFeatureListEmpty() ||
+ feature_processor_state->error()) {
+ feature_processor_state->RunCallback();
+ return;
+ }
+
+ // Get next input feature to process.
+ FeatureProcessorState::Data data = feature_processor_state->PopNextData();
+ std::unique_ptr<QueryProcessor> processor;
+
+ // Check either input or output has value.
+ DCHECK(data.input_feature.has_value() || data.output_feature.has_value());
+ DCHECK(!data.input_feature.has_value() || !data.output_feature.has_value());
+
+ // Process all the features in-order, starting with the first feature.
+ if (data.input_feature.has_value()) {
+ if (data.input_feature->has_uma_feature()) {
+ base::flat_map<QueryProcessor::FeatureIndex, proto::UMAFeature> queries =
+ {{kIndexNotUsed, data.input_feature->uma_feature()}};
+ processor = GetUmaFeatureProcessor(std::move(queries),
+ feature_processor_state.get());
+ } else if (data.input_feature->has_custom_input()) {
+ base::flat_map<QueryProcessor::FeatureIndex, proto::CustomInput> queries =
+ {{kIndexNotUsed, data.input_feature->custom_input()}};
+ processor = std::make_unique<CustomInputProcessor>(
+ std::move(queries), feature_processor_state->prediction_time(),
+ input_delegate_holder_.get());
+ } else if (data.input_feature->has_sql_feature()) {
+ auto* ukm_manager = storage_service_->ukm_data_manager();
+ if (!ukm_manager->IsUkmEngineEnabled()) {
+ // UKM engine is disabled, feature cannot be processed.
+ feature_processor_state->SetError();
+ feature_processor_state->RunCallback();
+ return;
+ }
+ SqlFeatureProcessor::QueryList queries = {
+ {kIndexNotUsed, data.input_feature->sql_feature()}};
+ processor = std::make_unique<SqlFeatureProcessor>(
+ std::move(queries), feature_processor_state->prediction_time(),
+ input_delegate_holder_.get(), ukm_manager->GetUkmDatabase());
+ }
+ } else {
+ // Process output features
+ if (data.output_feature->has_uma_output()) {
+ DCHECK(data.output_feature->uma_output().has_uma_feature());
+ base::flat_map<QueryProcessor::FeatureIndex, proto::UMAFeature> queries =
+ {{kIndexNotUsed, data.output_feature->uma_output().uma_feature()}};
+ processor = GetUmaFeatureProcessor(std::move(queries),
+ feature_processor_state.get());
+ }
+ }
+
+ auto* processor_ptr = processor.get();
+ processor_ptr->Process(
+ std::move(feature_processor_state),
+ base::BindOnce(&FeatureListQueryProcessor::OnFeatureProcessed,
+ weak_ptr_factory_.GetWeakPtr(), std::move(processor),
+ data.input_feature.has_value()));
+}
+
+void FeatureListQueryProcessor::OnFeatureProcessed(
+ std::unique_ptr<QueryProcessor> feature_processor,
+ bool is_input,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ QueryProcessor::IndexedTensors result) {
+ feature_processor_state->AppendTensor(result[kIndexNotUsed], is_input);
+ ProcessNext(std::move(feature_processor_state));
+}
+
+std::unique_ptr<UmaFeatureProcessor>
+FeatureListQueryProcessor::GetUmaFeatureProcessor(
+ base::flat_map<FeatureIndex, proto::UMAFeature>&& uma_features,
+ FeatureProcessorState* feature_processor_state) {
+ return std::make_unique<UmaFeatureProcessor>(
+ std::move(uma_features), storage_service_->signal_database(),
+ feature_aggregator_.get(), feature_processor_state->prediction_time(),
+ feature_processor_state->bucket_duration(),
+ feature_processor_state->segment_id());
+}
+
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/feature_list_query_processor.h b/chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h
index 62e6183ab32..6ade8ce558e 100644
--- a/chromium/components/segmentation_platform/internal/execution/feature_list_query_processor.h
+++ b/chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h
@@ -2,25 +2,32 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_LIST_QUERY_PROCESSOR_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_LIST_QUERY_PROCESSOR_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_LIST_QUERY_PROCESSOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_LIST_QUERY_PROCESSOR_H_
#include <deque>
#include <memory>
#include <vector>
#include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/execution/custom_input_processor.h"
-#include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
-#include "components/segmentation_platform/internal/execution/query_processor.h"
-#include "components/segmentation_platform/internal/execution/uma_feature_processor.h"
+#include "components/segmentation_platform/internal/database/ukm_database.h"
+#include "components/segmentation_platform/internal/execution/processing/custom_input_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/uma_feature_processor.h"
+#include "components/segmentation_platform/internal/input_context.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace segmentation_platform {
+class StorageService;
+
+namespace processing {
+
class FeatureAggregator;
class FeatureProcessorState;
-class SignalDatabase;
+class InputDelegateHolder;
+
+using proto::SegmentId;
// FeatureListQueryProcessor takes a segmentation model's metadata, processes
// each feature in the metadata's feature list in order and computes an input
@@ -28,7 +35,8 @@ class SignalDatabase;
class FeatureListQueryProcessor {
public:
FeatureListQueryProcessor(
- SignalDatabase* signal_database,
+ StorageService* storage_service,
+ std::unique_ptr<InputDelegateHolder> input_delegate_holder,
std::unique_ptr<FeatureAggregator> feature_aggregator);
virtual ~FeatureListQueryProcessor();
@@ -38,7 +46,12 @@ class FeatureListQueryProcessor {
delete;
using FeatureProcessorCallback =
- base::OnceCallback<void(bool, const std::vector<float>&)>;
+ base::OnceCallback<void(bool,
+ const std::vector<float>& /*inputs*/,
+ const std::vector<float>& /*outputs*/)>;
+
+ // Options for determining what should be included in the result.
+ enum class ProcessOption { kInputsOnly, kOutputsOnly, kInputsAndOutputs };
// Given a model's metadata, processes the feature list from the metadata and
// computes the input tensor for the ML model. Result is returned through a
@@ -48,37 +61,47 @@ class FeatureListQueryProcessor {
// time at which we predict the model execution should happen.
virtual void ProcessFeatureList(
const proto::SegmentationModelMetadata& model_metadata,
- OptimizationTarget segment_id,
+ scoped_refptr<InputContext> input_context,
+ SegmentId segment_id,
base::Time prediction_time,
+ ProcessOption process_option,
FeatureProcessorCallback callback);
private:
// Called by ProcessFeatureList to process the next input feature in the list.
// It then delegates the processing to the correct feature processor class
// until the feature list is empty.
- void ProcessNextInputFeature(
+ void ProcessNext(
std::unique_ptr<FeatureProcessorState> feature_processor_state);
// Callback called after a feature has been processed, indicating that we can
// safely discard the feature processor that handled the processing. Continue
// with the rest of the input features by calling ProcessNextInputFeature.
+ // `is_input' indicates whether the feature is for input tensors.
void OnFeatureProcessed(
std::unique_ptr<QueryProcessor> feature_processor,
+ bool is_input,
std::unique_ptr<FeatureProcessorState> feature_processor_state,
QueryProcessor::IndexedTensors result);
- // Signal database for uma features.
- const raw_ptr<SignalDatabase> signal_database_;
+ // Gets a UMA feature processor for a
+ std::unique_ptr<UmaFeatureProcessor> GetUmaFeatureProcessor(
+ base::flat_map<FeatureIndex, proto::UMAFeature>&& uma_features,
+ FeatureProcessorState* feature_processor_state);
+
+ // Storage service which provides signals to process.
+ const raw_ptr<StorageService> storage_service_;
+
+ // Holds InputDelegate for feature processing.
+ std::unique_ptr<InputDelegateHolder> input_delegate_holder_;
// Feature aggregator that aggregates data for uma features.
const std::unique_ptr<FeatureAggregator> feature_aggregator_;
- // Feature processor for uma type of input features.
- CustomInputProcessor custom_input_processor_;
-
base::WeakPtrFactory<FeatureListQueryProcessor> weak_ptr_factory_{this};
};
+} // namespace processing
} // namespace segmentation_platform
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_LIST_QUERY_PROCESSOR_H_
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_LIST_QUERY_PROCESSOR_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/feature_list_query_processor_unittest.cc b/chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc
index 39568021158..4380ae64aa3 100644
--- a/chromium/components/segmentation_platform/internal/execution/feature_list_query_processor_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
#include <memory>
@@ -13,7 +13,13 @@
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "components/segmentation_platform/internal/database/mock_signal_database.h"
-#include "components/segmentation_platform/internal/execution/mock_feature_aggregator.h"
+#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/database/storage_service.h"
+#include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/execution/processing/input_delegate.h"
+#include "components/segmentation_platform/internal/execution/processing/mock_feature_aggregator.h"
+#include "components/segmentation_platform/internal/mock_ukm_data_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::test::RunOnceCallback;
@@ -22,7 +28,7 @@ using testing::_;
using testing::Return;
using testing::SetArgReferee;
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
namespace {
constexpr base::TimeDelta kOneSecond = base::Seconds(1);
@@ -33,11 +39,14 @@ class FeatureListQueryProcessorTest : public testing::Test {
public:
FeatureListQueryProcessorTest() = default;
~FeatureListQueryProcessorTest() override = default;
-
void SetUp() override {
- signal_database_ = std::make_unique<MockSignalDatabase>();
+ auto moved_signal_db = std::make_unique<MockSignalDatabase>();
+ signal_database_ = moved_signal_db.get();
+ storage_service_ =
+ std::make_unique<StorageService>(nullptr, std::move(moved_signal_db),
+ nullptr, nullptr, &ukm_data_manager_);
clock_.SetNow(base::Time::Now());
- segment_id_ = OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ segment_id_ = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
}
void TearDown() override {
@@ -52,7 +61,9 @@ class FeatureListQueryProcessorTest : public testing::Test {
auto feature_aggregator = std::make_unique<MockFeatureAggregator>();
feature_aggregator_ = feature_aggregator.get();
feature_list_query_processor_ = std::make_unique<FeatureListQueryProcessor>(
- signal_database_.get(), std::move(feature_aggregator));
+ storage_service_.get(),
+ std::make_unique<processing::InputDelegateHolder>(),
+ std::move(feature_aggregator));
}
void SetBucketDuration(uint64_t bucket_duration, proto::TimeUnit time_unit) {
@@ -64,6 +75,26 @@ class FeatureListQueryProcessorTest : public testing::Test {
return clock_.Now() - bucket_duration * bucket_count;
}
+ proto::UMAFeature CreateUmaFeature(
+ proto::SignalType signal_type,
+ const std::string& name,
+ uint64_t bucket_count,
+ uint64_t tensor_length,
+ proto::Aggregation aggregation,
+ const std::vector<int32_t>& accepted_enum_ids) {
+ proto::UMAFeature uma_feature;
+ uma_feature.set_type(signal_type);
+ uma_feature.set_name(name);
+ uma_feature.set_name_hash(base::HashMetricName(name));
+ uma_feature.set_bucket_count(bucket_count);
+ uma_feature.set_tensor_length(tensor_length);
+ uma_feature.set_aggregation(aggregation);
+
+ for (int32_t accepted_enum_id : accepted_enum_ids)
+ uma_feature.add_enum_ids(accepted_enum_id);
+ return uma_feature;
+ }
+
void AddUmaFeature(proto::SignalType signal_type,
const std::string& name,
uint64_t bucket_count,
@@ -71,18 +102,26 @@ class FeatureListQueryProcessorTest : public testing::Test {
proto::Aggregation aggregation,
const std::vector<int32_t>& accepted_enum_ids) {
auto* input_feature = model_metadata.add_input_features();
- proto::UMAFeature* uma_feature = input_feature->mutable_uma_feature();
- uma_feature->set_type(signal_type);
- uma_feature->set_name(name);
- uma_feature->set_name_hash(base::HashMetricName(name));
- uma_feature->set_bucket_count(bucket_count);
- uma_feature->set_tensor_length(tensor_length);
- uma_feature->set_aggregation(aggregation);
-
- for (int32_t accepted_enum_id : accepted_enum_ids)
- uma_feature->add_enum_ids(accepted_enum_id);
+ auto* uma_feature = input_feature->mutable_uma_feature();
+ uma_feature->CopyFrom(CreateUmaFeature(signal_type, name, bucket_count,
+ tensor_length, aggregation,
+ accepted_enum_ids));
}
+ void AddOutputUmaFeature(proto::SignalType signal_type,
+ const std::string& name,
+ uint64_t bucket_count,
+ uint64_t tensor_length,
+ proto::Aggregation aggregation,
+ const std::vector<int32_t>& accepted_enum_ids) {
+ model_metadata.mutable_training_outputs()
+ ->add_outputs()
+ ->mutable_uma_output()
+ ->mutable_uma_feature()
+ ->CopyFrom(CreateUmaFeature(signal_type, name, bucket_count,
+ tensor_length, aggregation,
+ accepted_enum_ids));
+ }
void AddUserActionWithProcessingSetup(base::TimeDelta bucket_duration) {
// Set up a single user action feature.
std::string user_action_name = "some_action";
@@ -125,14 +164,18 @@ class FeatureListQueryProcessorTest : public testing::Test {
void ExpectProcessedFeatureList(
bool expected_error,
const std::vector<float>& expected_input_tensor,
- base::Time prediction_time) {
+ const std::vector<float>& expected_output_tensor,
+ base::Time prediction_time,
+ FeatureListQueryProcessor::ProcessOption process_option =
+ FeatureListQueryProcessor::ProcessOption::kInputsOnly) {
base::RunLoop loop;
feature_list_query_processor_->ProcessFeatureList(
- model_metadata, segment_id_, prediction_time,
+ model_metadata, /*input_context=*/nullptr, segment_id_, prediction_time,
+ process_option,
base::BindOnce(
&FeatureListQueryProcessorTest::OnProcessingFinishedCallback,
base::Unretained(this), loop.QuitClosure(), expected_error,
- expected_input_tensor));
+ expected_input_tensor, expected_output_tensor));
loop.Run();
}
@@ -140,25 +183,30 @@ class FeatureListQueryProcessorTest : public testing::Test {
bool expected_error,
const std::vector<float>& expected_input_tensor) {
ExpectProcessedFeatureList(expected_error, expected_input_tensor,
- clock_.Now());
+ std::vector<float>(), clock_.Now());
}
void OnProcessingFinishedCallback(
base::RepeatingClosure closure,
bool expected_error,
const std::vector<float>& expected_input_tensor,
+ const std::vector<float>& expected_output_tensor,
bool error,
- const std::vector<float>& input_tensor) {
+ const std::vector<float>& input_tensor,
+ const std::vector<float>& output_tensor) {
EXPECT_EQ(expected_error, error);
EXPECT_EQ(expected_input_tensor, input_tensor);
+ EXPECT_EQ(expected_output_tensor, output_tensor);
std::move(closure).Run();
}
base::SimpleTestClock clock_;
base::test::TaskEnvironment task_environment_;
- OptimizationTarget segment_id_;
+ SegmentId segment_id_;
proto::SegmentationModelMetadata model_metadata;
- std::unique_ptr<MockSignalDatabase> signal_database_;
+ MockUkmDataManager ukm_data_manager_;
+ std::unique_ptr<StorageService> storage_service_;
+ raw_ptr<MockSignalDatabase> signal_database_;
raw_ptr<MockFeatureAggregator> feature_aggregator_;
std::unique_ptr<FeatureListQueryProcessor> feature_list_query_processor_;
@@ -179,7 +227,7 @@ TEST_F(FeatureListQueryProcessorTest, InvalidMetadata) {
ExpectProcessedFeatureList(true, std::vector<float>{});
}
-TEST_F(FeatureListQueryProcessorTest, SingleCustomInput) {
+TEST_F(FeatureListQueryProcessorTest, PredictionTimeCustomInput) {
CreateFeatureListQueryProcessor();
// Initialize with required metadata.
@@ -277,7 +325,7 @@ TEST_F(FeatureListQueryProcessorTest, UmaFeaturesAndCustomInputsInvalid) {
ExpectProcessedFeatureList(true, std::vector<float>{});
}
-TEST_F(FeatureListQueryProcessorTest, MultipleUmaFeatures) {
+TEST_F(FeatureListQueryProcessorTest, MultipleUmaFeaturesWithOutputs) {
CreateFeatureListQueryProcessor();
// Initialize with required metadata.
@@ -295,6 +343,12 @@ TEST_F(FeatureListQueryProcessorTest, MultipleUmaFeatures) {
AddUmaFeature(proto::SignalType::HISTOGRAM_ENUM, histogram_enum_name, 4, 1,
proto::Aggregation::COUNT, {});
+ // Set up output feature.
+ std::string output_histogram_enum_name = "output_histogram_enum";
+ AddOutputUmaFeature(proto::SignalType::HISTOGRAM_ENUM,
+ output_histogram_enum_name, 5, 1,
+ proto::Aggregation::COUNT, {});
+
// First uma feature should be the user action.
std::vector<SignalDatabaseSample> user_action_samples{
{clock_.Now(), 0},
@@ -305,11 +359,13 @@ TEST_F(FeatureListQueryProcessorTest, MultipleUmaFeatures) {
GetSamples(proto::SignalType::USER_ACTION,
base::HashMetricName(user_action_name),
StartTime(bucket_duration, 2), clock_.Now(), _))
- .WillOnce(RunOnceCallback<4>(user_action_samples));
+ .Times(2)
+ .WillRepeatedly(RunOnceCallback<4>(user_action_samples));
EXPECT_CALL(*feature_aggregator_,
Process(proto::SignalType::USER_ACTION, proto::Aggregation::COUNT,
2, clock_.Now(), bucket_duration, user_action_samples))
- .WillOnce(Return(std::vector<float>{3}));
+ .Times(2)
+ .WillRepeatedly(Return(std::vector<float>{3}));
// Second uma feature should be the value histogram.
std::vector<SignalDatabaseSample> histogram_value_samples{
@@ -321,12 +377,14 @@ TEST_F(FeatureListQueryProcessorTest, MultipleUmaFeatures) {
GetSamples(proto::SignalType::HISTOGRAM_VALUE,
base::HashMetricName(histogram_value_name),
StartTime(bucket_duration, 3), clock_.Now(), _))
- .WillOnce(RunOnceCallback<4>(histogram_value_samples));
+ .Times(2)
+ .WillRepeatedly(RunOnceCallback<4>(histogram_value_samples));
EXPECT_CALL(
*feature_aggregator_,
Process(proto::SignalType::HISTOGRAM_VALUE, proto::Aggregation::SUM, 3,
clock_.Now(), bucket_duration, histogram_value_samples))
- .WillOnce(Return(std::vector<float>{6}));
+ .Times(2)
+ .WillRepeatedly(Return(std::vector<float>{6}));
// Third uma feature should be the enum histogram.
std::vector<SignalDatabaseSample> histogram_enum_samples{
@@ -339,15 +397,45 @@ TEST_F(FeatureListQueryProcessorTest, MultipleUmaFeatures) {
GetSamples(proto::SignalType::HISTOGRAM_ENUM,
base::HashMetricName(histogram_enum_name),
StartTime(bucket_duration, 4), clock_.Now(), _))
- .WillOnce(RunOnceCallback<4>(histogram_enum_samples));
+ .Times(2)
+ .WillRepeatedly(RunOnceCallback<4>(histogram_enum_samples));
EXPECT_CALL(
*feature_aggregator_,
Process(proto::SignalType::HISTOGRAM_ENUM, proto::Aggregation::COUNT, 4,
clock_.Now(), bucket_duration, histogram_enum_samples))
- .WillOnce(Return(std::vector<float>{4}));
+ .Times(2)
+ .WillRepeatedly(Return(std::vector<float>{4}));
// The input tensor should contain all three values: 3, 6, and 4.
ExpectProcessedFeatureList(false, std::vector<float>{3, 6, 4});
+
+ // Output is also enum histogram
+ std::vector<SignalDatabaseSample> output_histogram_enum_samples{
+ {clock_.Now(), 1}, {clock_.Now(), 2}, {clock_.Now(), 3},
+ {clock_.Now(), 4}, {clock_.Now(), 5},
+ };
+ EXPECT_CALL(*signal_database_,
+ GetSamples(proto::SignalType::HISTOGRAM_ENUM,
+ base::HashMetricName(output_histogram_enum_name),
+ StartTime(bucket_duration, 5), clock_.Now(), _))
+ .Times(2)
+ .WillRepeatedly(RunOnceCallback<4>(output_histogram_enum_samples));
+ EXPECT_CALL(
+ *feature_aggregator_,
+ Process(proto::SignalType::HISTOGRAM_ENUM, proto::Aggregation::COUNT, 5,
+ clock_.Now(), bucket_duration, output_histogram_enum_samples))
+ .Times(2)
+ .WillRepeatedly(Return(std::vector<float>{5}));
+ // The input tensor should contain all three values: {3, 6, 4}, output
+ // contains {5}
+ ExpectProcessedFeatureList(
+ false, std::vector<float>{3, 6, 4}, std::vector<float>{5}, clock_.Now(),
+ FeatureListQueryProcessor::ProcessOption::kInputsAndOutputs);
+
+ // Only return tensors for output features.
+ ExpectProcessedFeatureList(
+ false, std::vector<float>(), std::vector<float>{5}, clock_.Now(),
+ FeatureListQueryProcessor::ProcessOption::kOutputsOnly);
}
TEST_F(FeatureListQueryProcessorTest, SkipCollectionOnlyUmaFeatures) {
@@ -547,4 +635,4 @@ TEST_F(FeatureListQueryProcessorTest, MultipleUmaFeaturesWithMultipleBuckets) {
ExpectProcessedFeatureList(false, std::vector<float>{1, 2, 3, 4, 5, 6, 7});
}
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/processing/feature_processor_state.cc b/chromium/components/segmentation_platform/internal/execution/processing/feature_processor_state.cc
new file mode 100644
index 00000000000..a8ea6da417d
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/processing/feature_processor_state.cc
@@ -0,0 +1,96 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+
+namespace segmentation_platform::processing {
+
+FeatureProcessorState::Data::Data(proto::InputFeature input)
+ : input_feature(std::move(input)) {}
+
+FeatureProcessorState::Data::Data(proto::TrainingOutput output)
+ : output_feature(std::move(output)) {}
+
+FeatureProcessorState::Data::Data(Data&& other)
+ : input_feature(std::move(other.input_feature)),
+ output_feature(std::move(other.output_feature)) {}
+
+FeatureProcessorState::Data::~Data() = default;
+
+bool FeatureProcessorState::Data::IsInput() const {
+ DCHECK(!input_feature.has_value() || !output_feature.has_value());
+ DCHECK(input_feature.has_value() || output_feature.has_value());
+
+ return input_feature.has_value();
+}
+
+FeatureProcessorState::FeatureProcessorState()
+ : prediction_time_(base::Time::Now()),
+ bucket_duration_(base::TimeDelta()),
+ segment_id_(SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {}
+
+FeatureProcessorState::FeatureProcessorState(
+ base::Time prediction_time,
+ base::TimeDelta bucket_duration,
+ SegmentId segment_id,
+ std::deque<Data> data,
+ scoped_refptr<InputContext> input_context,
+ FeatureListQueryProcessor::FeatureProcessorCallback callback)
+ : prediction_time_(prediction_time),
+ bucket_duration_(bucket_duration),
+ segment_id_(segment_id),
+ data_(std::move(data)),
+ input_context_(std::move(input_context)),
+ callback_(std::move(callback)) {}
+
+FeatureProcessorState::~FeatureProcessorState() = default;
+
+void FeatureProcessorState::SetError() {
+ error_ = true;
+ input_tensor_.clear();
+}
+
+FeatureProcessorState::Data FeatureProcessorState::PopNextData() {
+ Data data = std::move(data_.front());
+ data_.pop_front();
+ return data;
+}
+
+bool FeatureProcessorState::IsFeatureListEmpty() const {
+ return data_.empty();
+}
+
+void FeatureProcessorState::RunCallback() {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback_), error_, input_tensor_,
+ output_tensor_));
+}
+
+void FeatureProcessorState::AppendTensor(
+ const std::vector<ProcessedValue>& data,
+ bool is_input) {
+ std::vector<float> tensor_result;
+ for (auto& value : data) {
+ if (value.type == ProcessedValue::Type::FLOAT) {
+ tensor_result.push_back(value.float_val);
+ } else {
+ SetError();
+ return;
+ }
+ }
+
+ if (is_input) {
+ input_tensor_.insert(input_tensor_.end(), tensor_result.begin(),
+ tensor_result.end());
+ } else {
+ output_tensor_.insert(output_tensor_.end(), tensor_result.begin(),
+ tensor_result.end());
+ }
+}
+
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/feature_processor_state.h b/chromium/components/segmentation_platform/internal/execution/processing/feature_processor_state.h
index 7755c9ac38d..befb0f4bd05 100644
--- a/chromium/components/segmentation_platform/internal/execution/feature_processor_state.h
+++ b/chromium/components/segmentation_platform/internal/execution/processing/feature_processor_state.h
@@ -2,30 +2,51 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_PROCESSOR_STATE_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_PROCESSOR_STATE_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_PROCESSOR_STATE_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_PROCESSOR_STATE_H_
#include <deque>
+#include <memory>
#include <vector>
#include "base/time/clock.h"
#include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/ukm_types.h"
-#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/input_context.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
+
+using proto::SegmentId;
// FeatureProcessorState is responsible for storing all necessary state during
// the processing of a model's metadata.
class FeatureProcessorState {
public:
+ // Wrapper class that either contains an input or output.
+ struct Data {
+ explicit Data(proto::InputFeature input);
+ explicit Data(proto::TrainingOutput output);
+ Data(Data&&);
+ Data(const Data&) = delete;
+ Data& operator=(const Data&) = delete;
+ ~Data();
+
+ bool IsInput() const;
+ absl::optional<proto::InputFeature> input_feature;
+ absl::optional<proto::TrainingOutput> output_feature;
+ };
+
+ FeatureProcessorState();
FeatureProcessorState(
base::Time prediction_time,
base::TimeDelta bucket_duration,
- OptimizationTarget segment_id,
- std::unique_ptr<std::deque<proto::InputFeature>> input_features,
+ SegmentId segment_id,
+ std::deque<Data> data,
+ scoped_refptr<InputContext> input_context,
FeatureListQueryProcessor::FeatureProcessorCallback callback);
virtual ~FeatureProcessorState();
@@ -38,12 +59,18 @@ class FeatureProcessorState {
base::Time prediction_time() const { return prediction_time_; }
- OptimizationTarget segment_id() const { return segment_id_; }
+ SegmentId segment_id() const { return segment_id_; }
bool error() const { return error_; }
- // Returns and pops the next input feature in the feature list.
- proto::InputFeature PopNextInputFeature();
+ const scoped_refptr<InputContext> input_context() const {
+ return input_context_;
+ }
+
+ // Returns and pops the next input feature or output feature, wrapped inside
+ // `Data` structure. Return an empty struct if no input and output are
+ // available.
+ Data PopNextData();
// Sets an error to the current feature processor state.
// TODO(haileywang): Pass in a reason for error enum here and record an enum
@@ -57,23 +84,24 @@ class FeatureProcessorState {
void RunCallback();
// Update the input tensor vector.
- void AppendInputTensor(const std::vector<float>& data);
- void AppendInputTensor(const std::vector<ProcessedValue>& data);
+ void AppendTensor(const std::vector<ProcessedValue>& data, bool is_input);
private:
const base::Time prediction_time_;
const base::TimeDelta bucket_duration_;
- const OptimizationTarget segment_id_;
- std::unique_ptr<std::deque<proto::InputFeature>> input_features_;
+ const SegmentId segment_id_;
+ std::deque<Data> data_;
+ scoped_refptr<InputContext> input_context_;
// Feature processing results.
std::vector<float> input_tensor_;
+ std::vector<float> output_tensor_;
bool error_{false};
// Callback to return feature processing results to model execution manager.
FeatureListQueryProcessor::FeatureProcessorCallback callback_;
};
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_FEATURE_PROCESSOR_STATE_H_
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_FEATURE_PROCESSOR_STATE_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/processing/input_delegate.cc b/chromium/components/segmentation_platform/internal/execution/processing/input_delegate.cc
new file mode 100644
index 00000000000..c4e4df84360
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/processing/input_delegate.cc
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/processing/input_delegate.h"
+
+namespace segmentation_platform::processing {
+
+InputDelegate::InputDelegate() = default;
+InputDelegate::~InputDelegate() = default;
+
+InputDelegateHolder::InputDelegateHolder() = default;
+InputDelegateHolder::~InputDelegateHolder() = default;
+
+InputDelegate* InputDelegateHolder::GetDelegate(
+ proto::CustomInput::FillPolicy policy) {
+ auto it = input_delegates_.find(policy);
+ if (it != input_delegates_.end()) {
+ return it->second.get();
+ }
+ return nullptr;
+}
+
+void InputDelegateHolder::SetDelegate(proto::CustomInput::FillPolicy policy,
+ std::unique_ptr<InputDelegate> delegate) {
+ DCHECK(!input_delegates_.count(policy));
+ input_delegates_[policy] = std::move(delegate);
+}
+
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/processing/input_delegate.h b/chromium/components/segmentation_platform/internal/execution/processing/input_delegate.h
new file mode 100644
index 00000000000..bf7defbc45a
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/processing/input_delegate.h
@@ -0,0 +1,60 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_INPUT_DELEGATE_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_INPUT_DELEGATE_H_
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+
+namespace segmentation_platform::processing {
+
+class FeatureProcessorState;
+
+// Delegate that provides inputs to the query processor that computes input and
+// output features.
+class InputDelegate {
+ public:
+ InputDelegate();
+ virtual ~InputDelegate();
+
+ InputDelegate(InputDelegate&) = delete;
+ InputDelegate& operator=(InputDelegate&) = delete;
+
+ // Processes the given `input`, and returns the result via `callback`. Should
+ // return an error if the processing failed. On success, the number of outputs
+ // in the Tensor should be equal to `input.tensor_length()`.
+ using ProcessedCallback = base::OnceCallback<void(/*error=*/bool, Tensor)>;
+ virtual void Process(const proto::CustomInput& input,
+ const FeatureProcessorState& feature_processor_state,
+ ProcessedCallback callback) = 0;
+};
+
+// A holder that stores the list of `InputDelegate`s used by the platform.
+class InputDelegateHolder {
+ public:
+ InputDelegateHolder();
+ ~InputDelegateHolder();
+
+ InputDelegateHolder(InputDelegateHolder&) = delete;
+ InputDelegateHolder& operator=(InputDelegateHolder&) = delete;
+
+ // Returns a delegate for the `policy` if available or nullptr otherwise.
+ InputDelegate* GetDelegate(proto::CustomInput::FillPolicy policy);
+
+ // Sets a delegate for the given `policy`. Overwrites any existing delegates
+ // for the same `policy`
+ void SetDelegate(proto::CustomInput::FillPolicy policy,
+ std::unique_ptr<InputDelegate> delegate);
+
+ private:
+ base::flat_map<proto::CustomInput::FillPolicy, std::unique_ptr<InputDelegate>>
+ input_delegates_;
+};
+
+} // namespace segmentation_platform::processing
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_INPUT_DELEGATE_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/mock_feature_aggregator.cc b/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_aggregator.cc
index 1aa1ab859ae..dba940709b7 100644
--- a/chromium/components/segmentation_platform/internal/execution/mock_feature_aggregator.cc
+++ b/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_aggregator.cc
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/segmentation_platform/internal/execution/mock_feature_aggregator.h"
+#include "components/segmentation_platform/internal/execution/processing/mock_feature_aggregator.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
MockFeatureAggregator::MockFeatureAggregator() = default;
MockFeatureAggregator::~MockFeatureAggregator() = default;
-} // namespace segmentation_platform \ No newline at end of file
+} // namespace segmentation_platform::processing \ No newline at end of file
diff --git a/chromium/components/segmentation_platform/internal/execution/mock_feature_aggregator.h b/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_aggregator.h
index 29bb25675ec..2d73d39a0f9 100644
--- a/chromium/components/segmentation_platform/internal/execution/mock_feature_aggregator.h
+++ b/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_aggregator.h
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_FEATURE_AGGREGATOR_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_FEATURE_AGGREGATOR_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_MOCK_FEATURE_AGGREGATOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_MOCK_FEATURE_AGGREGATOR_H_
#include <vector>
-#include "components/segmentation_platform/internal/execution/feature_aggregator.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_aggregator.h"
#include "testing/gmock/include/gmock/gmock.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
// Mock of feature aggregator class. Used for testing.
class MockFeatureAggregator : public FeatureAggregator {
@@ -33,6 +33,6 @@ class MockFeatureAggregator : public FeatureAggregator {
(const override));
};
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_FEATURE_AGGREGATOR_H_ \ No newline at end of file
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_MOCK_FEATURE_AGGREGATOR_H_ \ No newline at end of file
diff --git a/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.cc b/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.cc
new file mode 100644
index 00000000000..e493f812e35
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.cc
@@ -0,0 +1,16 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h"
+
+#include "components/segmentation_platform/internal/execution/processing/input_delegate.h"
+
+namespace segmentation_platform::processing {
+
+MockFeatureListQueryProcessor::MockFeatureListQueryProcessor()
+ : FeatureListQueryProcessor(nullptr, nullptr, nullptr) {}
+
+MockFeatureListQueryProcessor::~MockFeatureListQueryProcessor() = default;
+
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h b/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h
new file mode 100644
index 00000000000..990bac2c5e5
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_MOCK_FEATURE_LIST_QUERY_PROCESSOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_MOCK_FEATURE_LIST_QUERY_PROCESSOR_H_
+
+#include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
+
+#include "components/segmentation_platform/internal/execution/processing/feature_aggregator.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace segmentation_platform::processing {
+
+class MockFeatureListQueryProcessor : public FeatureListQueryProcessor {
+ public:
+ MockFeatureListQueryProcessor();
+ ~MockFeatureListQueryProcessor() override;
+ MOCK_METHOD(void,
+ ProcessFeatureList,
+ (const proto::SegmentationModelMetadata&,
+ scoped_refptr<InputContext> input_context,
+ proto::SegmentId,
+ base::Time,
+ FeatureListQueryProcessor::ProcessOption,
+ FeatureProcessorCallback),
+ (override));
+};
+
+} // namespace segmentation_platform::processing
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_MOCK_FEATURE_LIST_QUERY_PROCESSOR_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/query_processor.h b/chromium/components/segmentation_platform/internal/execution/processing/query_processor.h
index 346231feeb4..dc3366a0f14 100644
--- a/chromium/components/segmentation_platform/internal/execution/query_processor.h
+++ b/chromium/components/segmentation_platform/internal/execution/processing/query_processor.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_QUERY_PROCESSOR_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_QUERY_PROCESSOR_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_QUERY_PROCESSOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_QUERY_PROCESSOR_H_
#include <memory>
#include <vector>
@@ -12,7 +12,7 @@
#include "base/containers/flat_map.h"
#include "components/segmentation_platform/internal/database/ukm_types.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
class FeatureProcessorState;
// Interface that converts aribitrary data to a list of tensor in asynchronous
@@ -20,10 +20,13 @@ class FeatureProcessorState;
// inference data.
class QueryProcessor {
public:
- using FeatureIndex = int;
- using Tensor = std::vector<ProcessedValue>;
+ // TODO(ssid): Remove these indirections and replace all uses with the global
+ // types.
+ using Tensor = segmentation_platform::processing::Tensor;
+ using IndexedTensors = segmentation_platform::processing::IndexedTensors;
+ using FeatureIndex = segmentation_platform::processing::FeatureIndex;
+
// TODO(haileywang): Maybe use a unique_ptr<> here.
- using IndexedTensors = base::flat_map<FeatureIndex, Tensor>;
using QueryProcessorCallback =
base::OnceCallback<void(std::unique_ptr<FeatureProcessorState>,
IndexedTensors)>;
@@ -43,6 +46,6 @@ class QueryProcessor {
QueryProcessor() = default;
};
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_QUERY_PROCESSOR_H_
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_QUERY_PROCESSOR_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/sql_feature_processor.cc b/chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor.cc
index 75682ec768b..20f96f51f4d 100644
--- a/chromium/components/segmentation_platform/internal/execution/sql_feature_processor.cc
+++ b/chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor.cc
@@ -2,21 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/segmentation_platform/internal/execution/sql_feature_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/sql_feature_processor.h"
#include <utility>
+#include "base/bind.h"
#include "base/containers/flat_map.h"
#include "base/threading/sequenced_task_runner_handle.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
-#include "components/segmentation_platform/internal/execution/custom_input_processor.h"
-#include "components/segmentation_platform/internal/execution/feature_processor_state.h"
+#include "components/segmentation_platform/internal/execution/processing/custom_input_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
-namespace segmentation_platform {
-
-SqlFeatureProcessor::SqlFeatureProcessor(QueryList&& queries,
- base::Time prediction_time)
- : queries_(std::move(queries)), prediction_time_(prediction_time) {}
+namespace segmentation_platform::processing {
+
+SqlFeatureProcessor::SqlFeatureProcessor(
+ QueryList&& queries,
+ base::Time prediction_time,
+ InputDelegateHolder* input_delegate_holder,
+ UkmDatabase* ukm_database)
+ : queries_(std::move(queries)),
+ prediction_time_(prediction_time),
+ input_delegate_holder_(input_delegate_holder),
+ ukm_database_(ukm_database) {}
SqlFeatureProcessor::~SqlFeatureProcessor() = default;
void SqlFeatureProcessor::Process(
@@ -25,7 +32,6 @@ void SqlFeatureProcessor::Process(
DCHECK(!is_processed_);
is_processed_ = true;
callback_ = std::move(callback);
- feature_processor_state_ = std::move(feature_processor_state);
// Prepare the sql queries for indexed custom inputs processing.
base::flat_map<SqlFeatureAndBindValueIndices, proto::CustomInput> bind_values;
@@ -36,7 +42,11 @@ void SqlFeatureProcessor::Process(
// Validate the proto::SqlFeature metadata.
if (metadata_utils::ValidateMetadataSqlFeature(feature) !=
metadata_utils::ValidationResult::kValidationSuccess) {
- RunErrorCallback();
+ feature_processor_state->SetError();
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback_),
+ std::move(feature_processor_state),
+ std::move(result_)));
return;
}
@@ -52,24 +62,17 @@ void SqlFeatureProcessor::Process(
}
// Process the indexed custom inputs
- auto custom_input_processor =
- std::make_unique<CustomInputProcessor>(prediction_time_);
+ auto custom_input_processor = std::make_unique<CustomInputProcessor>(
+ prediction_time_, input_delegate_holder_);
auto* custom_input_processor_ptr = custom_input_processor.get();
custom_input_processor_ptr->ProcessIndexType<SqlFeatureAndBindValueIndices>(
- std::move(bind_values), std::move(feature_processor_state_),
+ std::move(bind_values), std::move(feature_processor_state),
+ std::make_unique<base::flat_map<std::pair<int, int>, Tensor>>(),
base::BindOnce(&SqlFeatureProcessor::OnCustomInputProcessed,
weak_ptr_factory_.GetWeakPtr(),
std::move(custom_input_processor)));
}
-// TODO(haileywang): Move this structure to ukm_types.
-struct SqlFeatureProcessor::CustomSqlQuery {
- CustomSqlQuery() = default;
- ~CustomSqlQuery() = default;
- std::string query;
- std::vector<ProcessedValue> bind_values;
-};
-
void SqlFeatureProcessor::OnCustomInputProcessed(
std::unique_ptr<CustomInputProcessor> custom_input_processor,
std::unique_ptr<FeatureProcessorState> feature_processor_state,
@@ -82,7 +85,11 @@ void SqlFeatureProcessor::OnCustomInputProcessed(
}
if (total_bind_values != result.size()) {
- RunErrorCallback();
+ feature_processor_state->SetError();
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(callback_), std::move(feature_processor_state),
+ std::move(result_)));
return;
}
@@ -90,6 +97,9 @@ void SqlFeatureProcessor::OnCustomInputProcessed(
for (const auto& query : queries_) {
const proto::SqlFeature& feature = query.second;
FeatureIndex sql_feature_index = query.first;
+ UkmDatabase::CustomSqlQuery& current =
+ processed_queries_[sql_feature_index];
+ current.query = feature.sql();
for (int i = 0; i < feature.bind_values_size(); ++i) {
int bind_value_index = i;
@@ -97,32 +107,42 @@ void SqlFeatureProcessor::OnCustomInputProcessed(
// Validate the result tensor.
if (result.count(std::make_pair(sql_feature_index, bind_value_index)) !=
1) {
- RunErrorCallback();
+ feature_processor_state->SetError();
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback_),
+ std::move(feature_processor_state),
+ std::move(result_)));
return;
}
- // Append query and query params to the list.
+ // Append query params to the list.
const auto& custom_input_tensors =
result[std::make_pair(sql_feature_index, bind_value_index)];
- CustomSqlQuery current;
- current.query = feature.sql();
current.bind_values.insert(current.bind_values.end(),
custom_input_tensors.begin(),
custom_input_tensors.end());
- processed_queries_[sql_feature_index] = std::move(current);
}
}
- // TODO(haileywang): Custom inputs have been processed and sql queries are
- // ready to be sent to the ukm database.
+ // Send the queries to the ukm database to process.
+ ukm_database_->RunReadonlyQueries(
+ std::move(processed_queries_),
+ base::BindOnce(&SqlFeatureProcessor::OnQueriesRun,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(feature_processor_state)));
}
-void SqlFeatureProcessor::RunErrorCallback() {
- feature_processor_state_->SetError();
+void SqlFeatureProcessor::OnQueriesRun(
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ bool success,
+ IndexedTensors result) {
+ if (!success) {
+ feature_processor_state->SetError();
+ }
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
- base::BindOnce(std::move(callback_), std::move(feature_processor_state_),
- std::move(result_)));
+ base::BindOnce(std::move(callback_), std::move(feature_processor_state),
+ std::move(result)));
}
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/sql_feature_processor.h b/chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor.h
index cae81e85f31..96c3ba1aa18 100644
--- a/chromium/components/segmentation_platform/internal/execution/sql_feature_processor.h
+++ b/chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor.h
@@ -2,21 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SQL_FEATURE_PROCESSOR_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SQL_FEATURE_PROCESSOR_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_SQL_FEATURE_PROCESSOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_SQL_FEATURE_PROCESSOR_H_
#include <memory>
#include <vector>
#include "base/callback_forward.h"
#include "base/containers/flat_map.h"
-#include "components/segmentation_platform/internal/database/ukm_types.h"
-#include "components/segmentation_platform/internal/execution/query_processor.h"
+#include "components/segmentation_platform/internal/database/ukm_database.h"
+#include "components/segmentation_platform/internal/execution/processing/query_processor.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
class CustomInputProcessor;
class FeatureProcessorState;
+class InputDelegateHolder;
// SqlFeatureProcessor takes a list of SqlFeature type of input, fetches samples
// from the UKMDatabase, and computes an input tensor to use when executing the
@@ -25,7 +26,10 @@ class SqlFeatureProcessor : public QueryProcessor {
public:
using QueryList = base::flat_map<FeatureIndex, proto::SqlFeature>;
- explicit SqlFeatureProcessor(QueryList&& queries, base::Time prediction_time);
+ SqlFeatureProcessor(QueryList&& queries,
+ base::Time prediction_time,
+ InputDelegateHolder* input_delegate_holder,
+ UkmDatabase* ukm_database);
~SqlFeatureProcessor() override;
// QueryProcessor implementation.
@@ -36,18 +40,18 @@ class SqlFeatureProcessor : public QueryProcessor {
using SqlFeatureAndBindValueIndices =
std::pair</*sql feature index*/ int, /*bind value index*/ int>;
- // Struct responsible for storing a sql query and its bind values.
- struct CustomSqlQuery;
-
// Callback method for when all relevant bind values have been processed.
void OnCustomInputProcessed(
std::unique_ptr<CustomInputProcessor> custom_input_processor,
std::unique_ptr<FeatureProcessorState> feature_processor_state,
base::flat_map<SqlFeatureAndBindValueIndices, Tensor> result);
- // Helper method for setting the error state and returning result to the
- // feature processor.
- void RunErrorCallback();
+ // Callback method for when all queries have been processed by the ukm
+ // database.
+ void OnQueriesRun(
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ bool success,
+ IndexedTensors result);
// List of sql features to process into input tensors.
QueryList queries_;
@@ -55,8 +59,10 @@ class SqlFeatureProcessor : public QueryProcessor {
// Time at which we expect the model execution to run.
const base::Time prediction_time_;
- // Temporary storage of the processing state object.
- std::unique_ptr<FeatureProcessorState> feature_processor_state_;
+ const raw_ptr<InputDelegateHolder> input_delegate_holder_;
+
+ // Main database for fetching data.
+ const raw_ptr<UkmDatabase> ukm_database_;
// Callback for sending the resulting indexed tensors to the feature list
// processor.
@@ -66,7 +72,7 @@ class SqlFeatureProcessor : public QueryProcessor {
// List of sql queries and bind values ready to be sent to the ukm database
// for processing.
- base::flat_map<FeatureIndex, CustomSqlQuery> processed_queries_;
+ base::flat_map<FeatureIndex, UkmDatabase::CustomSqlQuery> processed_queries_;
// List of resulting input tensors.
IndexedTensors result_;
@@ -74,6 +80,6 @@ class SqlFeatureProcessor : public QueryProcessor {
base::WeakPtrFactory<SqlFeatureProcessor> weak_ptr_factory_{this};
};
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SQL_FEATURE_PROCESSOR_H_
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_SQL_FEATURE_PROCESSOR_H_
diff --git a/chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor_unittest.cc b/chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor_unittest.cc
new file mode 100644
index 00000000000..6f07e35ee09
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/execution/processing/sql_feature_processor_unittest.cc
@@ -0,0 +1,193 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/execution/processing/sql_feature_processor.h"
+
+#include "base/bind.h"
+#include "base/containers/flat_map.h"
+#include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/simple_test_clock.h"
+#include "base/test/task_environment.h"
+#include "components/segmentation_platform/internal/database/mock_ukm_database.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+#include "components/segmentation_platform/internal/execution/processing/input_delegate.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::test::RunOnceCallback;
+using testing::_;
+
+namespace segmentation_platform::processing {
+
+class SqlFeatureProcessorTest : public testing::Test {
+ public:
+ using CustomSqlQuery = UkmDatabase::CustomSqlQuery;
+
+ void SetUp() override {
+ clock_.SetNow(base::Time::Now());
+ feature_processor_state_ = std::make_unique<FeatureProcessorState>();
+ ukm_database_ = std::make_unique<MockUkmDatabase>();
+ }
+
+ void TearDown() override {
+ feature_processor_state_.reset();
+ ukm_database_ = nullptr;
+ }
+
+ proto::CustomInput CreateCustomInput(
+ int tensor_length,
+ proto::CustomInput::FillPolicy fill_policy,
+ const std::vector<float>& default_values) {
+ proto::CustomInput custom_input;
+ custom_input.set_fill_policy(fill_policy);
+ custom_input.set_tensor_length(tensor_length);
+ for (float default_value : default_values)
+ custom_input.add_default_value(default_value);
+ return custom_input;
+ }
+
+ proto::SqlFeature CreateSqlFeature(
+ const std::string& sql,
+ const std::vector<proto::CustomInput>& custom_inputs) {
+ proto::SqlFeature sql_feature;
+ sql_feature.set_sql(sql);
+ for (const proto::CustomInput& custom_input : custom_inputs) {
+ auto* bind_value = sql_feature.add_bind_values();
+ auto* value = bind_value->mutable_value();
+ bind_value->set_param_type(proto::SqlFeature::BindValue::BOOL);
+ *value = custom_input;
+ }
+ return sql_feature;
+ }
+
+ void ExpectQueryResult(
+ SqlFeatureProcessor::QueryList&& data,
+ const base::flat_map<SqlFeatureProcessor::FeatureIndex, CustomSqlQuery>&
+ processed_queries,
+ const SqlFeatureProcessor::IndexedTensors& result) {
+ // Initialize the sql feature processor.
+ std::unique_ptr<SqlFeatureProcessor> processor =
+ std::make_unique<SqlFeatureProcessor>(std::move(data), clock_.Now(),
+ &input_delegate_holder_,
+ ukm_database_.get());
+
+ EXPECT_CALL(*ukm_database_, RunReadonlyQueries)
+ .WillOnce(testing::Invoke(
+ [&processed_queries, &result](
+ const base::flat_map<SqlFeatureProcessor::FeatureIndex,
+ CustomSqlQuery>& queries,
+ MockUkmDatabase::QueryCallback callback) {
+ EXPECT_EQ(processed_queries, queries);
+ std::move(callback).Run(true, result);
+ }));
+
+ // Process the sql query.
+ base::RunLoop loop;
+ processor->Process(
+ std::move(feature_processor_state_),
+ base::BindOnce(&SqlFeatureProcessorTest::OnProcessingFinishedCallback,
+ base::Unretained(this), loop.QuitClosure(), false,
+ result));
+ loop.Run();
+ }
+
+ void OnProcessingFinishedCallback(
+ base::RepeatingClosure closure,
+ bool expected_error,
+ const SqlFeatureProcessor::IndexedTensors& expected_result,
+ std::unique_ptr<FeatureProcessorState> feature_processor_state,
+ SqlFeatureProcessor::IndexedTensors result) {
+ EXPECT_EQ(expected_error, feature_processor_state->error());
+ EXPECT_EQ(expected_result, result);
+ std::move(closure).Run();
+ }
+
+ base::test::TaskEnvironment task_envåironment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+ base::SimpleTestClock clock_;
+ InputDelegateHolder input_delegate_holder_;
+ std::unique_ptr<FeatureProcessorState> feature_processor_state_;
+ std::unique_ptr<MockUkmDatabase> ukm_database_;
+};
+
+TEST_F(SqlFeatureProcessorTest, EmptyBindValues) {
+ // Set up a single empty sql feature.
+ SqlFeatureProcessor::QueryList data;
+ constexpr char kSqlQuery[] = "some sql query";
+ data[0] = CreateSqlFeature(kSqlQuery, {});
+
+ // Construct the expected processed bind values based on the given data.
+ base::flat_map<SqlFeatureProcessor::FeatureIndex, CustomSqlQuery>
+ processed_queries;
+ processed_queries[0] = CustomSqlQuery(kSqlQuery, {});
+
+ // Construct a result to be returned when the correct processed queries are
+ // sent.
+ SqlFeatureProcessor::IndexedTensors result;
+ result[0] = {ProcessedValue(1), ProcessedValue(2), ProcessedValue(3)};
+
+ ExpectQueryResult(std::move(data), processed_queries, result);
+}
+
+TEST_F(SqlFeatureProcessorTest, SingleSqlFeatureWithBindValues) {
+ // Set up a single empty sql feature.
+ SqlFeatureProcessor::QueryList data;
+ constexpr char kSqlQuery[] = "some sql query";
+ std::vector<float> custom_default_values = {1, 2, 3};
+ data[0] = CreateSqlFeature(
+ kSqlQuery,
+ {CreateCustomInput(1, proto::CustomInput::FILL_PREDICTION_TIME, {}),
+ CreateCustomInput(2, proto::CustomInput::UNKNOWN_FILL_POLICY,
+ custom_default_values)});
+
+ // Construct the expected processed bind values based on the given data.
+ base::flat_map<SqlFeatureProcessor::FeatureIndex, CustomSqlQuery>
+ processed_queries;
+ processed_queries[0] =
+ CustomSqlQuery(kSqlQuery, {ProcessedValue(clock_.Now()),
+ ProcessedValue(static_cast<float>(1)),
+ ProcessedValue(static_cast<float>(2))});
+
+ // Construct a result to be returned when the correct processed queries are
+ // sent.
+ SqlFeatureProcessor::IndexedTensors result;
+ result[0] = {ProcessedValue(1), ProcessedValue(2), ProcessedValue(3)};
+
+ ExpectQueryResult(std::move(data), processed_queries, result);
+}
+
+TEST_F(SqlFeatureProcessorTest, MultipleSqlFeatures) {
+ // Set up a single empty sql feature.
+ SqlFeatureProcessor::QueryList data;
+ constexpr char kSqlQuery[] = "some sql query";
+ std::vector<float> custom_default_values = {1, 2, 3};
+ data[0] = CreateSqlFeature(
+ kSqlQuery,
+ {CreateCustomInput(1, proto::CustomInput::FILL_PREDICTION_TIME, {})});
+ data[1] = CreateSqlFeature(kSqlQuery, {});
+ data[2] = CreateSqlFeature(
+ kSqlQuery,
+ {CreateCustomInput(1, proto::CustomInput::FILL_PREDICTION_TIME, {})});
+
+ // Construct the expected processed bind values based on the given data.
+ base::flat_map<SqlFeatureProcessor::FeatureIndex, CustomSqlQuery>
+ processed_queries;
+ processed_queries[0] =
+ CustomSqlQuery(kSqlQuery, {ProcessedValue(clock_.Now())});
+ processed_queries[1] = CustomSqlQuery(kSqlQuery, {});
+ processed_queries[2] =
+ CustomSqlQuery(kSqlQuery, {ProcessedValue(clock_.Now())});
+
+ // Construct a result to be returned when the correct processed queries are
+ // sent.
+ SqlFeatureProcessor::IndexedTensors result;
+ result[0] = {ProcessedValue(1), ProcessedValue(2), ProcessedValue(3)};
+
+ ExpectQueryResult(std::move(data), processed_queries, result);
+}
+
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/uma_feature_processor.cc b/chromium/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc
index 0162404d9f5..ad2ff4850c7 100644
--- a/chromium/components/segmentation_platform/internal/execution/uma_feature_processor.cc
+++ b/chromium/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc
@@ -2,20 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/segmentation_platform/internal/execution/uma_feature_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/uma_feature_processor.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/timer/elapsed_timer.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/ukm_types.h"
-#include "components/segmentation_platform/internal/execution/feature_processor_state.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/stats.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
UmaFeatureProcessor::UmaFeatureProcessor(
base::flat_map<FeatureIndex, proto::UMAFeature>&& uma_features,
@@ -23,7 +23,7 @@ UmaFeatureProcessor::UmaFeatureProcessor(
FeatureAggregator* feature_aggregator,
const base::Time prediction_time,
const base::TimeDelta bucket_duration,
- const OptimizationTarget segment_id)
+ const SegmentId segment_id)
: uma_features_(std::move(uma_features)),
signal_database_(signal_database),
feature_aggregator_(feature_aggregator),
@@ -148,4 +148,4 @@ void UmaFeatureProcessor::OnGetSamplesForUmaFeature(
ProcessNextUmaFeature();
}
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
diff --git a/chromium/components/segmentation_platform/internal/execution/uma_feature_processor.h b/chromium/components/segmentation_platform/internal/execution/processing/uma_feature_processor.h
index 0b4a75bfc23..664900b9d2d 100644
--- a/chromium/components/segmentation_platform/internal/execution/uma_feature_processor.h
+++ b/chromium/components/segmentation_platform/internal/execution/processing/uma_feature_processor.h
@@ -2,21 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_UMA_FEATURE_PROCESSOR_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_UMA_FEATURE_PROCESSOR_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_UMA_FEATURE_PROCESSOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_UMA_FEATURE_PROCESSOR_H_
#include <memory>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/signal_database.h"
-#include "components/segmentation_platform/internal/execution/feature_aggregator.h"
-#include "components/segmentation_platform/internal/execution/query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_aggregator.h"
+#include "components/segmentation_platform/internal/execution/processing/query_processor.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
-namespace segmentation_platform {
+namespace segmentation_platform::processing {
class FeatureProcessorState;
// A query processor that takes a list of UMAFeatures, fetches samples from the
@@ -30,7 +30,7 @@ class UmaFeatureProcessor : public QueryProcessor {
FeatureAggregator* feature_aggregator,
const base::Time prediction_time,
const base::TimeDelta bucket_duration,
- const optimization_guide::proto::OptimizationTarget segment_id);
+ const proto::SegmentId segment_id);
~UmaFeatureProcessor() override;
@@ -69,7 +69,7 @@ class UmaFeatureProcessor : public QueryProcessor {
// Data needed for the processing of uma features.
const base::Time prediction_time_;
const base::TimeDelta bucket_duration_;
- const optimization_guide::proto::OptimizationTarget segment_id_;
+ const proto::SegmentId segment_id_;
// Temporary storage of the processing state object.
// TODO(haileywang): Remove dependency to the state object once error check is
@@ -86,6 +86,6 @@ class UmaFeatureProcessor : public QueryProcessor {
base::WeakPtrFactory<UmaFeatureProcessor> weak_ptr_factory_{this};
};
-} // namespace segmentation_platform
+} // namespace segmentation_platform::processing
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_UMA_FEATURE_PROCESSOR_H_
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_PROCESSING_UMA_FEATURE_PROCESSOR_H_
diff --git a/chromium/components/segmentation_platform/internal/input_context.cc b/chromium/components/segmentation_platform/internal/input_context.cc
new file mode 100644
index 00000000000..ffa76327ff3
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/input_context.cc
@@ -0,0 +1,12 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/input_context.h"
+
+namespace segmentation_platform {
+
+InputContext::InputContext() = default;
+InputContext::~InputContext() = default;
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/input_context.h b/chromium/components/segmentation_platform/internal/input_context.h
new file mode 100644
index 00000000000..01d5988d432
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/input_context.h
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_INPUT_CONTEXT_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_INPUT_CONTEXT_H_
+
+#include "base/containers/flat_map.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+
+namespace segmentation_platform {
+
+class SegmentationTabHelper;
+
+// Experimental API, DO NOT USE.
+// Input provided for segment selection, based on the current state of the
+// browser.
+struct InputContext : base::RefCounted<InputContext> {
+ public:
+ InputContext();
+
+ InputContext(InputContext&) = delete;
+ InputContext& operator=(InputContext&) = delete;
+
+ // Input values that can be used to input to model directly if the type is
+ // `Type::INTEGER` or `Type::DOUBLE`. Inputs can be substituted to SQL queries
+ // if the type is not `Type::DICT` or `Type::LIST`.
+ base::flat_map<std::string, base::Value> metadata_args;
+
+ private:
+ friend class RefCounted<InputContext>;
+
+ ~InputContext();
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_INPUT_CONTEXT_H_
diff --git a/chromium/components/segmentation_platform/internal/local_state_helper_impl.cc b/chromium/components/segmentation_platform/internal/local_state_helper_impl.cc
new file mode 100644
index 00000000000..3e582e214e6
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/local_state_helper_impl.cc
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/local_state_helper_impl.h"
+
+#include "components/prefs/pref_service.h"
+
+namespace segmentation_platform {
+
+// static
+LocalStateHelper& LocalStateHelper::GetInstance() {
+ static base::NoDestructor<LocalStateHelperImpl> instance;
+ return *instance;
+}
+
+LocalStateHelperImpl::LocalStateHelperImpl() = default;
+
+LocalStateHelperImpl::~LocalStateHelperImpl() = default;
+
+void LocalStateHelperImpl::Initialize(PrefService* local_state) {
+ local_state_ = local_state;
+}
+
+void LocalStateHelperImpl::SetPrefTime(const char* pref_name, base::Time time) {
+ if (local_state_) {
+ local_state_->SetTime(pref_name, time);
+ }
+}
+
+base::Time LocalStateHelperImpl::GetPrefTime(const char* pref_name) const {
+ return local_state_ ? local_state_->GetTime(pref_name) : base::Time::Max();
+}
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/local_state_helper_impl.h b/chromium/components/segmentation_platform/internal/local_state_helper_impl.h
new file mode 100644
index 00000000000..0646fecf4c0
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/local_state_helper_impl.h
@@ -0,0 +1,38 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_LOCAL_STATE_HELPER_IMPL_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_LOCAL_STATE_HELPER_IMPL_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/no_destructor.h"
+#include "base/time/time.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
+
+class PrefService;
+
+namespace segmentation_platform {
+
+// Implementation of the LocalStateHelper class.
+class LocalStateHelperImpl : public LocalStateHelper {
+ public:
+ LocalStateHelperImpl(LocalStateHelperImpl&) = delete;
+ LocalStateHelperImpl& operator=(LocalStateHelperImpl&) = delete;
+
+ // LocalStateHelper implementation.
+ void Initialize(PrefService* local_state) override;
+ void SetPrefTime(const char* pref_name, base::Time time) override;
+ base::Time GetPrefTime(const char* pref_name) const override;
+
+ private:
+ friend base::NoDestructor<LocalStateHelperImpl>;
+ LocalStateHelperImpl();
+ ~LocalStateHelperImpl() override;
+
+ raw_ptr<PrefService> local_state_ = nullptr;
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_LOCAL_STATE_HELPER_IMPL_H_ \ No newline at end of file
diff --git a/chromium/components/segmentation_platform/internal/database/metadata_utils.cc b/chromium/components/segmentation_platform/internal/metadata/metadata_utils.cc
index 19148516108..1863b272e77 100644
--- a/chromium/components/segmentation_platform/internal/database/metadata_utils.cc
+++ b/chromium/components/segmentation_platform/internal/metadata/metadata_utils.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include <inttypes.h>
@@ -11,13 +11,13 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/signal_key.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/public/features.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace segmentation_platform {
@@ -76,8 +76,7 @@ std::string FeatureToString(const proto::UMAFeature& feature) {
} // namespace
ValidationResult ValidateSegmentInfo(const proto::SegmentInfo& segment_info) {
- if (segment_info.segment_id() ==
- optimization_guide::proto::OPTIMIZATION_TARGET_UNKNOWN) {
+ if (segment_info.segment_id() == proto::OPTIMIZATION_TARGET_UNKNOWN) {
return ValidationResult::kSegmentIDNotFound;
}
@@ -106,7 +105,8 @@ ValidationResult ValidateMetadata(
}
ValidationResult ValidateMetadataUmaFeature(const proto::UMAFeature& feature) {
- if (feature.type() == proto::SignalType::UNKNOWN_SIGNAL_TYPE)
+ if (feature.type() == proto::SignalType::UNKNOWN_SIGNAL_TYPE ||
+ feature.type() == proto::SignalType::UKM_EVENT)
return ValidationResult::kSignalTypeInvalid;
if ((feature.type() == proto::SignalType::HISTOGRAM_ENUM ||
@@ -157,12 +157,11 @@ ValidationResult ValidateMetadataCustomInput(
// provide enough input values as specified by tensor length.
if (custom_input.tensor_length() > custom_input.default_value_size())
return ValidationResult::kCustomInputInvalid;
- } else if (custom_input.fill_policy() ==
- proto::CustomInput::FILL_PREDICTION_TIME) {
- // Current time can only provide up to one input tensor value, so column
- // weight must not exceed 1.
- if (custom_input.tensor_length() > 1)
+ } else if (custom_input.default_value_size() != 0) {
+ // The default value should be longer than the tensor length.
+ if (custom_input.tensor_length() > custom_input.default_value_size()) {
return ValidationResult::kCustomInputInvalid;
+ }
}
return ValidationResult::kValidationSuccess;
}
@@ -203,7 +202,7 @@ ValidationResult ValidateMetadataAndFeatures(
}
ValidationResult ValidateIndexedTensors(
- const QueryProcessor::IndexedTensors& tensor,
+ const processing::IndexedTensors& tensor,
size_t expected_size) {
if (tensor.size() != expected_size)
return ValidationResult::kIndexedTensorsInvalid;
@@ -296,6 +295,7 @@ SignalKey::Kind SignalTypeToSignalKind(proto::SignalType signal_type) {
return SignalKey::Kind::HISTOGRAM_ENUM;
case proto::SignalType::HISTOGRAM_VALUE:
return SignalKey::Kind::HISTOGRAM_VALUE;
+ case proto::SignalType::UKM_EVENT:
case proto::SignalType::UNKNOWN_SIGNAL_TYPE:
return SignalKey::Kind::UNKNOWN;
}
diff --git a/chromium/components/segmentation_platform/internal/database/metadata_utils.h b/chromium/components/segmentation_platform/internal/metadata/metadata_utils.h
index 3aa1a71b6b1..ff221482b3e 100644
--- a/chromium/components/segmentation_platform/internal/database/metadata_utils.h
+++ b/chromium/components/segmentation_platform/internal/metadata/metadata_utils.h
@@ -2,21 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_METADATA_UTILS_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_METADATA_UTILS_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_UTILS_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_UTILS_H_
#include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/signal_key.h"
-#include "components/segmentation_platform/internal/execution/query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/query_processor.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
+using proto::SegmentId;
+
namespace metadata_utils {
// Keep up to date with SegmentationPlatformValidationResult in
@@ -71,7 +71,7 @@ ValidationResult ValidateMetadataAndFeatures(
// Whether the given indexed tensor is valid to be used for the current
// segmentation platform.
ValidationResult ValidateIndexedTensors(
- const QueryProcessor::IndexedTensors& tensor,
+ const processing::IndexedTensors& tensor,
size_t expected_size);
// Whether the given SegmentInfo, metadata and feature metadata is valid to be
@@ -119,4 +119,4 @@ std::vector<proto::UMAFeature> GetAllUmaFeatures(
} // namespace metadata_utils
} // namespace segmentation_platform
-#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_METADATA_UTILS_H_
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_UTILS_H_
diff --git a/chromium/components/segmentation_platform/internal/database/metadata_utils_unittest.cc b/chromium/components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc
index 9dcd1424c9f..336e7904ced 100644
--- a/chromium/components/segmentation_platform/internal/database/metadata_utils_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "base/metrics/metrics_hashes.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/ukm_types.h"
-#include "components/segmentation_platform/internal/execution/query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/query_processor.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace segmentation_platform {
@@ -41,8 +41,8 @@ TEST_F(MetadataUtilsTest, SegmentInfoValidation) {
EXPECT_EQ(metadata_utils::ValidationResult::kSegmentIDNotFound,
metadata_utils::ValidateSegmentInfo(segment_info));
- segment_info.set_segment_id(optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ segment_info.set_segment_id(
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
EXPECT_EQ(metadata_utils::ValidationResult::kMetadataNotFound,
metadata_utils::ValidateSegmentInfo(segment_info));
@@ -90,6 +90,10 @@ TEST_F(MetadataUtilsTest, MetadataUmaFeatureValidation) {
EXPECT_EQ(metadata_utils::ValidationResult::kSignalTypeInvalid,
metadata_utils::ValidateMetadataUmaFeature(feature));
+ feature.set_type(proto::SignalType::UKM_EVENT);
+ EXPECT_EQ(metadata_utils::ValidationResult::kSignalTypeInvalid,
+ metadata_utils::ValidateMetadataUmaFeature(feature));
+
// name not required for USER_ACTION.
feature.set_type(proto::SignalType::USER_ACTION);
EXPECT_EQ(metadata_utils::ValidationResult::kFeatureNameHashNotFound,
@@ -237,8 +241,8 @@ TEST_F(MetadataUtilsTest, MetadataCustomInputValidation) {
EXPECT_EQ(metadata_utils::ValidationResult::kValidationSuccess,
metadata_utils::ValidateMetadataCustomInput(custom_input));
- // When fill policy is current time, the custom input is invalid if the tensor
- // length is bigger than 1.
+ // When default value is filled, tensor length must be smaller or equal than
+ // the default value list size.
custom_input.set_fill_policy(proto::CustomInput::FILL_PREDICTION_TIME);
custom_input.set_tensor_length(2);
EXPECT_EQ(metadata_utils::ValidationResult::kCustomInputInvalid,
@@ -350,13 +354,13 @@ TEST_F(MetadataUtilsTest, ValidateMetadataAndInputFeatures) {
TEST_F(MetadataUtilsTest, MetadataIndexedTensorsValidation) {
// Empty indexed tensors are valid.
- QueryProcessor::IndexedTensors tensor;
+ processing::QueryProcessor::IndexedTensors tensor;
EXPECT_EQ(
metadata_utils::ValidationResult::kValidationSuccess,
metadata_utils::ValidateIndexedTensors(tensor, /* expected_size */ 0));
// Not continuously indexed tensors are invalid.
- const std::vector<ProcessedValue> value;
+ const std::vector<processing::ProcessedValue> value;
tensor[0] = value;
tensor[1] = value;
tensor[3] = value;
@@ -381,8 +385,8 @@ TEST_F(MetadataUtilsTest, ValidateSegementInfoMetadataAndFeatures) {
metadata_utils::ValidationResult::kSegmentIDNotFound,
metadata_utils::ValidateSegmentInfoMetadataAndFeatures(segment_info));
- segment_info.set_segment_id(optimization_guide::proto::OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ segment_info.set_segment_id(
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
EXPECT_EQ(
metadata_utils::ValidationResult::kMetadataNotFound,
metadata_utils::ValidateSegmentInfoMetadataAndFeatures(segment_info));
diff --git a/chromium/components/segmentation_platform/internal/metadata/metadata_writer.cc b/chromium/components/segmentation_platform/internal/metadata/metadata_writer.cc
new file mode 100644
index 00000000000..30464f29078
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/metadata/metadata_writer.cc
@@ -0,0 +1,88 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/metadata/metadata_writer.h"
+
+#include "base/metrics/metrics_hashes.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+
+namespace segmentation_platform {
+
+MetadataWriter::MetadataWriter(proto::SegmentationModelMetadata* metadata)
+ : metadata_(metadata) {}
+MetadataWriter::~MetadataWriter() = default;
+
+void MetadataWriter::AddUmaFeatures(const UMAFeature features[],
+ size_t features_size) {
+ for (size_t i = 0; i < features_size; i++) {
+ const auto& feature = features[i];
+ auto* input_feature = metadata_->add_input_features();
+ proto::UMAFeature* uma_feature = input_feature->mutable_uma_feature();
+ uma_feature->set_type(feature.signal_type);
+ uma_feature->set_name(feature.name);
+ uma_feature->set_name_hash(base::HashMetricName(feature.name));
+ uma_feature->set_bucket_count(feature.bucket_count);
+ uma_feature->set_tensor_length(feature.tensor_length);
+ uma_feature->set_aggregation(feature.aggregation);
+
+ for (size_t j = 0; j < feature.enum_ids_size; j++) {
+ uma_feature->add_enum_ids(feature.accepted_enum_ids[j]);
+ }
+ }
+}
+
+void MetadataWriter::AddSqlFeatures(const SqlFeature features[],
+ size_t features_size) {
+ proto::SqlFeature* feature =
+ metadata_->add_input_features()->mutable_sql_feature();
+ for (size_t i = 0; i < features_size; ++i) {
+ const auto& f = features[i];
+ feature->set_sql(f.sql);
+ for (size_t ev = 0; ev < f.events_size; ++ev) {
+ const auto& event = f.events[ev];
+ auto* ukm_event = feature->mutable_signal_filter()->add_ukm_events();
+ ukm_event->set_event_hash(event.event_hash.GetUnsafeValue());
+ for (size_t m = 0; m < event.metrics_size; ++m) {
+ ukm_event->mutable_metric_hash_filter()->Add(
+ event.metrics[m].GetUnsafeValue());
+ }
+ }
+ }
+}
+
+void MetadataWriter::AddCustomInput(const CustomInput& feature) {
+ proto::CustomInput* custom_input_feature =
+ metadata_->add_input_features()->mutable_custom_input();
+ custom_input_feature->set_tensor_length(feature.tensor_length);
+ custom_input_feature->set_fill_policy(feature.fill_policy);
+ custom_input_feature->add_default_value(feature.default_value);
+ custom_input_feature->set_name(feature.name);
+}
+
+void MetadataWriter::AddDiscreteMappingEntries(
+ const std::string& key,
+ const std::pair<float, int>* mappings,
+ size_t mappings_size) {
+ auto* discrete_mappings = metadata_->mutable_discrete_mappings();
+ for (size_t i = 0; i < mappings_size; i++) {
+ auto* discrete_mapping_entry = (*discrete_mappings)[key].add_entries();
+ discrete_mapping_entry->set_min_result(mappings[i].first);
+ discrete_mapping_entry->set_rank(mappings[i].second);
+ }
+}
+
+void MetadataWriter::SetSegmentationMetadataConfig(
+ proto::TimeUnit time_unit,
+ uint64_t bucket_duration,
+ int64_t signal_storage_length,
+ int64_t min_signal_collection_length,
+ int64_t result_time_to_live) {
+ metadata_->set_time_unit(time_unit);
+ metadata_->set_bucket_duration(bucket_duration);
+ metadata_->set_signal_storage_length(signal_storage_length);
+ metadata_->set_min_signal_collection_length(min_signal_collection_length);
+ metadata_->set_result_time_to_live(result_time_to_live);
+}
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/metadata/metadata_writer.h b/chromium/components/segmentation_platform/internal/metadata/metadata_writer.h
new file mode 100644
index 00000000000..d8b22dc7fbf
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/metadata/metadata_writer.h
@@ -0,0 +1,111 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_WRITER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_WRITER_H_
+
+#include <cinttypes>
+#include <cstddef>
+
+#include "base/memory/raw_ptr.h"
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+
+namespace segmentation_platform {
+
+// Utility to write metadata proto for default models.
+class MetadataWriter {
+ public:
+ explicit MetadataWriter(proto::SegmentationModelMetadata* metadata);
+ ~MetadataWriter();
+
+ MetadataWriter(MetadataWriter&) = delete;
+ MetadataWriter& operator=(MetadataWriter&) = delete;
+
+ // Defines a feature based on UMA metric.
+ struct UMAFeature {
+ const proto::SignalType signal_type{proto::SignalType::UNKNOWN_SIGNAL_TYPE};
+ const char* name{nullptr};
+ const uint64_t bucket_count{0};
+ const uint64_t tensor_length{0};
+ const proto::Aggregation aggregation{proto::Aggregation::UNKNOWN};
+ const size_t enum_ids_size{0};
+ const int32_t* const accepted_enum_ids = nullptr;
+
+ static constexpr UMAFeature FromUserAction(const char* name,
+ uint64_t bucket_count) {
+ return MetadataWriter::UMAFeature{
+ .signal_type = proto::SignalType::USER_ACTION,
+ .name = name,
+ .bucket_count = bucket_count,
+ .tensor_length = 1,
+ .aggregation = proto::Aggregation::COUNT,
+ .enum_ids_size = 0};
+ }
+ static constexpr UMAFeature FromEnumHistogram(const char* name,
+ uint64_t bucket_count,
+ const int32_t* const enum_ids,
+ size_t enum_ids_size) {
+ return MetadataWriter::UMAFeature{
+ .signal_type = proto::SignalType::HISTOGRAM_ENUM,
+ .name = name,
+ .bucket_count = bucket_count,
+ .tensor_length = 1,
+ .aggregation = proto::Aggregation::COUNT,
+ .enum_ids_size = enum_ids_size,
+ .accepted_enum_ids = enum_ids};
+ }
+ };
+
+ // Defines a feature based on a SQL query.
+ // TODO(ssid): Support custom inputs.
+ struct SqlFeature {
+ const char* const sql{nullptr};
+ struct EventAndMetrics {
+ const UkmEventHash event_hash;
+ const raw_ptr<const UkmMetricHash> metrics{nullptr};
+ const size_t metrics_size{0};
+ };
+ const EventAndMetrics* const events{nullptr};
+ const size_t events_size{0};
+ };
+
+ // Defines a feature based on a custom input.
+ struct CustomInput {
+ const uint64_t tensor_length{0};
+ const proto::CustomInput::FillPolicy fill_policy{
+ proto::CustomInput_FillPolicy_UNKNOWN_FILL_POLICY};
+ const float default_value{0};
+ const char* name{nullptr};
+ // TODO(shaktisahu): Support additional_args.
+ };
+
+ // Appends the list of UMA features in order.
+ void AddUmaFeatures(const UMAFeature features[], size_t features_size);
+
+ // Appends the list of SQL features in order.
+ void AddSqlFeatures(const SqlFeature features[], size_t features_size);
+
+ // Appends the list of SQL features in order.
+ void AddCustomInput(const CustomInput& feature);
+
+ // Appends a list of discrete mapping in order.
+ void AddDiscreteMappingEntries(const std::string& key,
+ const std::pair<float, int>* mappings,
+ size_t mappings_size);
+
+ // Writes the model metadata with the given parameters.
+ void SetSegmentationMetadataConfig(proto::TimeUnit time_unit,
+ uint64_t bucket_duration,
+ int64_t signal_storage_length,
+ int64_t min_signal_collection_length,
+ int64_t result_time_to_live);
+
+ private:
+ const raw_ptr<proto::SegmentationModelMetadata> metadata_;
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_WRITER_H_
diff --git a/chromium/components/segmentation_platform/internal/metric_filter_utils.cc b/chromium/components/segmentation_platform/internal/metric_filter_utils.cc
new file mode 100644
index 00000000000..b6c184958c1
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/metric_filter_utils.cc
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/metric_filter_utils.h"
+
+#include "base/strings/strcat.h"
+#include "components/segmentation_platform/internal/stats.h"
+
+namespace segmentation_platform::stats {
+namespace {
+using proto::SegmentId;
+
+} // namespace
+
+std::string OptimizationTargetToSegmentGroupName(SegmentId segment_id) {
+ return OptimizationTargetToHistogramVariant(segment_id);
+}
+
+std::string SegmentationKeyToTrialName(const std::string& segmentation_key) {
+ return base::StrCat(
+ {"Segmentation_", SegmentationKeyToUmaName(segmentation_key)});
+}
+
+std::string SegmentationKeyToSubsegmentTrialName(
+ const std::string& segmentation_key,
+ proto::SegmentId segment_id) {
+ return base::StrCat({"Segmentation_",
+ SegmentationKeyToUmaName(segmentation_key), "_",
+ OptimizationTargetToHistogramVariant(segment_id)});
+}
+
+} // namespace segmentation_platform::stats
diff --git a/chromium/components/segmentation_platform/internal/metric_filter_utils.h b/chromium/components/segmentation_platform/internal/metric_filter_utils.h
new file mode 100644
index 00000000000..f027cf6055d
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/metric_filter_utils.h
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METRIC_FILTER_UTILS_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METRIC_FILTER_UTILS_H_
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace segmentation_platform::stats {
+
+// Returns a name to be used in UMA dashboard as segment group for the given
+// `segment_id`.
+std::string OptimizationTargetToSegmentGroupName(proto::SegmentId segment_id);
+
+// Returns a name to be used in UMA dashboard as segmentation type for the given
+// `segmentation_key`.
+std::string SegmentationKeyToTrialName(const std::string& segmentation_key);
+
+// Returns a name to be used in UMA dashboard as segmentation subtype type for
+// the given `segmentation_key` and `segment_id`.
+std::string SegmentationKeyToSubsegmentTrialName(
+ const std::string& segmentation_key,
+ proto::SegmentId segment_id);
+
+} // namespace segmentation_platform::stats
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METRIC_FILTER_UTILS_H_
diff --git a/chromium/components/segmentation_platform/internal/mock_ukm_data_manager.h b/chromium/components/segmentation_platform/internal/mock_ukm_data_manager.h
index bef97ebdb8a..503d90b40ac 100644
--- a/chromium/components/segmentation_platform/internal/mock_ukm_data_manager.h
+++ b/chromium/components/segmentation_platform/internal/mock_ukm_data_manager.h
@@ -19,30 +19,28 @@ class MockUkmDataManager : public UkmDataManager {
MOCK_METHOD(void,
Initialize,
- (const base::FilePath& database_path),
+ (const base::FilePath& database_path, UkmObserver*),
(override));
MOCK_METHOD(bool, IsUkmEngineEnabled, (), (override));
- MOCK_METHOD(void,
- NotifyCanObserveUkm,
- (ukm::UkmRecorderImpl * ukm_recorder, PrefService* pref_service),
- (override));
-
MOCK_METHOD(void, StartObservingUkm, (const UkmConfig& config), (override));
MOCK_METHOD(void, PauseOrResumeObservation, (bool pause), (override));
- MOCK_METHOD(void, StopObservingUkm, (), (override));
-
MOCK_METHOD(UrlSignalHandler*, GetOrCreateUrlHandler, (), (override));
MOCK_METHOD(UkmDatabase*, GetUkmDatabase, (), (override));
+ MOCK_METHOD(void, OnEntryAdded, (ukm::mojom::UkmEntryPtr), (override));
+
+ MOCK_METHOD(void,
+ OnUkmSourceUpdated,
+ (ukm::SourceId, const std::vector<GURL>&),
+ (override));
+
MOCK_METHOD(void, AddRef, (), (override));
MOCK_METHOD(void, RemoveRef, (), (override));
-
- MOCK_METHOD(void, OnUkmAllowedStateChanged, (bool allowed), (override));
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/proto/BUILD.gn b/chromium/components/segmentation_platform/internal/proto/BUILD.gn
index 09c721d1b5a..2308e6cc0e9 100644
--- a/chromium/components/segmentation_platform/internal/proto/BUILD.gn
+++ b/chromium/components/segmentation_platform/internal/proto/BUILD.gn
@@ -15,6 +15,5 @@ proto_library("proto") {
"types.proto",
]
- link_deps =
- [ "//components/optimization_guide/proto:optimization_guide_proto" ]
+ link_deps = [ "//components/segmentation_platform/public/proto" ]
}
diff --git a/chromium/components/segmentation_platform/internal/proto/model_metadata.proto b/chromium/components/segmentation_platform/internal/proto/model_metadata.proto
index 9924dbb1348..8614a6d6e5a 100644
--- a/chromium/components/segmentation_platform/internal/proto/model_metadata.proto
+++ b/chromium/components/segmentation_platform/internal/proto/model_metadata.proto
@@ -15,10 +15,13 @@ import "components/segmentation_platform/internal/proto/types.proto";
// new feature added to metadata proto, and add a log of the new changes in the
// current version in this file.
// Version 0 supports UMA features and aggregation in |features| field.
-// Version 1 supports UMA features and custom inputs in |input_features| field.
+// Version 1 supports UMA features, custom inputs and sql features in
+// |input_features| field.
// Version 2 supports training data output collection in |training_outputs|
// field.
-enum CurrentVersion { METADATA_VERSION = 0; }
+enum CurrentVersion {
+ METADATA_VERSION = 1;
+}
// Version information for segmentation models.
message VersionInfo {
@@ -47,6 +50,8 @@ enum TimeUnit {
message UMAFeature {
// The type of signal this feature refers to.
+ // Note: SignalType::UKM_EVENT type is only used for SignalStorageConfig and
+ // should not be used as uma feature's signal type.
optional SignalType type = 1;
// The human readable name of the histogram or user action.
@@ -87,7 +92,19 @@ message CustomInput {
UNKNOWN_FILL_POLICY = 0;
// Output is the time at which model prediction is needed. Can be used to
// bind TIME type param to queries.
+ // Output type: Time
+ // Output length: 1
FILL_PREDICTION_TIME = 1;
+ // Output is two timestamps, the beginning and the end of last x days. Can
+ // be used to bind TIME type param to query within a time interval.
+ // Output type: Time
+ // Output length: 2
+ // Additional arg:
+ // `bucket_count`: Required. Number of buckets to include in the result.
+ TIME_RANGE_BEFORE_PREDICTION = 2;
+
+ // TODO(ssid): Add comments based on the final implementation of this type.
+ PRICE_TRACKING_HINTS = 3;
}
// The fill type of the custom input.
@@ -98,6 +115,12 @@ message CustomInput {
// will not run due to missing input. The number of entries should be equal to
// the |tensor_length|.
repeated float default_value = 3;
+
+ // If the fill type need additional arguments, use this value.
+ map<string, string> additional_args = 4;
+
+ // The human readable name of the custom input.
+ optional string name = 5;
}
// Configuration for storing signals in the SQL database.
@@ -106,11 +129,10 @@ message SignalFilterConfig {
message UkmEvent {
// Event hash of the UKM event.
optional uint64 event_hash = 1;
- // List of metric hashes for the event, to store in the database. If
- // empty, the database will store all the metrics for the UKM event. It is
- // is recommended to provide list of necessary metrics, unless it is clear
- // that the list of metrics of the event is easily trackable and doesn't
- // grow over time.
+ // List of metric hashes for the event, to store in the database. It is
+ // is required to provide list of necessary metrics.
+ // TODO: Support empty metric hash list, the database will store all the
+ // metrics for the UKM event.
repeated uint64 metric_hash_filter = 2;
}
// List of UKM events to store in the database.
@@ -152,6 +174,9 @@ message SqlFeature {
optional CustomInput value = 3;
}
repeated BindValue bind_values = 3;
+
+ // The human readable name of the ukm event and metric.
+ optional string name = 4;
}
// Contains a feature used as an input to the ML model.
diff --git a/chromium/components/segmentation_platform/internal/proto/model_prediction.proto b/chromium/components/segmentation_platform/internal/proto/model_prediction.proto
index c6ab5785b17..a8375a1f25b 100644
--- a/chromium/components/segmentation_platform/internal/proto/model_prediction.proto
+++ b/chromium/components/segmentation_platform/internal/proto/model_prediction.proto
@@ -8,7 +8,7 @@ option optimize_for = LITE_RUNTIME;
package segmentation_platform.proto;
import "components/segmentation_platform/internal/proto/model_metadata.proto";
-import "components/optimization_guide/proto/models.proto";
+import "components/segmentation_platform/public/proto/segmentation_platform.proto";
// Result from the model evaluation for a given segment.
message PredictionResult {
@@ -25,7 +25,7 @@ message PredictionResult {
// Next tag: 6
message SegmentInfo {
// Segment target.
- optional optimization_guide.proto.OptimizationTarget segment_id = 1;
+ optional SegmentId segment_id = 1;
// Cached copy of the segment metadata which is important in case the metadata
// is temporarily not available in the future. It also contains the relevant
diff --git a/chromium/components/segmentation_platform/internal/proto/signal_storage_config.proto b/chromium/components/segmentation_platform/internal/proto/signal_storage_config.proto
index f3b1fa6745c..c9b3525b91f 100644
--- a/chromium/components/segmentation_platform/internal/proto/signal_storage_config.proto
+++ b/chromium/components/segmentation_platform/internal/proto/signal_storage_config.proto
@@ -12,9 +12,13 @@ import "components/segmentation_platform/internal/proto/types.proto";
// Contains storage related information about a particular signal useful for
// determining eligibility for model evaluation and DB cleanups.
message SignalStorageConfig {
- // The name (hash) of the user action or histogram.
+ // The name hash of the user action/histogram or the metric hash of the ukm
+ // event.
optional uint64 name_hash = 1;
+ // The event hash of the ukm event. Only filled if signal type is UKM_EVENT.
+ optional uint64 event_hash = 5;
+
// The type of the signal.
optional SignalType signal_type = 2;
diff --git a/chromium/components/segmentation_platform/internal/proto/types.proto b/chromium/components/segmentation_platform/internal/proto/types.proto
index 9452ca3aefc..7d32b6c192f 100644
--- a/chromium/components/segmentation_platform/internal/proto/types.proto
+++ b/chromium/components/segmentation_platform/internal/proto/types.proto
@@ -13,4 +13,5 @@ enum SignalType {
USER_ACTION = 1;
HISTOGRAM_ENUM = 2;
HISTOGRAM_VALUE = 3;
+ UKM_EVENT = 4;
}
diff --git a/chromium/components/segmentation_platform/internal/scheduler/execution_service.cc b/chromium/components/segmentation_platform/internal/scheduler/execution_service.cc
index 67c5b31810a..631fe774966 100644
--- a/chromium/components/segmentation_platform/internal/scheduler/execution_service.cc
+++ b/chromium/components/segmentation_platform/internal/scheduler/execution_service.cc
@@ -4,67 +4,74 @@
#include "components/segmentation_platform/internal/scheduler/execution_service.h"
+#include "base/metrics/field_trial_params.h"
+#include "components/prefs/pref_service.h"
#include "components/segmentation_platform/internal/data_collection/training_data_collector.h"
-#include "components/segmentation_platform/internal/database/segment_info_database.h"
-#include "components/segmentation_platform/internal/database/signal_database.h"
-#include "components/segmentation_platform/internal/execution/feature_aggregator_impl.h"
-#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/database/storage_service.h"
+#include "components/segmentation_platform/internal/execution/default_model_manager.h"
#include "components/segmentation_platform/internal/execution/model_executor_impl.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/processing/input_delegate.h"
#include "components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h"
+#include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
#include "components/segmentation_platform/internal/signals/signal_handler.h"
+#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/features.h"
namespace segmentation_platform {
-ExecutionService::ExecutionRequest::ExecutionRequest() = default;
-ExecutionService::ExecutionRequest::~ExecutionRequest() = default;
-
ExecutionService::ExecutionService() = default;
ExecutionService::~ExecutionService() = default;
void ExecutionService::InitForTesting(
- std::unique_ptr<FeatureListQueryProcessor> feature_processor,
+ std::unique_ptr<processing::FeatureListQueryProcessor> feature_processor,
std::unique_ptr<ModelExecutor> executor,
- std::unique_ptr<ModelExecutionScheduler> scheduler) {
+ std::unique_ptr<ModelExecutionScheduler> scheduler,
+ std::unique_ptr<ModelExecutionManager> execution_manager) {
feature_list_query_processor_ = std::move(feature_processor);
model_executor_ = std::move(executor);
model_execution_scheduler_ = std::move(scheduler);
+ model_execution_manager_ = std::move(execution_manager);
}
void ExecutionService::Initialize(
- SignalDatabase* signal_database,
- SegmentInfoDatabase* segment_info_database,
- SignalStorageConfig* signal_storage_config,
+ StorageService* storage_service,
SignalHandler* signal_handler,
base::Clock* clock,
ModelExecutionManager::SegmentationModelUpdatedCallback callback,
scoped_refptr<base::SequencedTaskRunner> task_runner,
- const base::flat_set<OptimizationTarget>& all_segment_ids,
+ const base::flat_set<SegmentId>& all_segment_ids,
ModelProviderFactory* model_provider_factory,
std::vector<ModelExecutionScheduler::Observer*>&& observers,
- const PlatformOptions& platform_options) {
- feature_list_query_processor_ = std::make_unique<FeatureListQueryProcessor>(
- signal_database, std::make_unique<FeatureAggregatorImpl>());
+ const PlatformOptions& platform_options,
+ std::unique_ptr<processing::InputDelegateHolder> input_delegate_holder,
+ std::vector<std::unique_ptr<Config>>* configs,
+ PrefService* profile_prefs) {
+ storage_service_ = storage_service;
+
+ feature_list_query_processor_ =
+ std::make_unique<processing::FeatureListQueryProcessor>(
+ storage_service, std::move(input_delegate_holder),
+ std::make_unique<processing::FeatureAggregatorImpl>());
training_data_collector_ = TrainingDataCollector::Create(
- segment_info_database, feature_list_query_processor_.get(),
+ storage_service->segment_info_database(),
+ feature_list_query_processor_.get(),
signal_handler->deprecated_histogram_signal_handler(),
- signal_storage_config, clock);
- training_data_collector_->OnServiceInitialized();
+ storage_service->signal_storage_config(), configs, profile_prefs, clock);
model_executor_ = std::make_unique<ModelExecutorImpl>(
clock, feature_list_query_processor_.get());
model_execution_manager_ = std::make_unique<ModelExecutionManagerImpl>(
- all_segment_ids, model_provider_factory, clock, segment_info_database,
- callback);
+ all_segment_ids, model_provider_factory, clock,
+ storage_service->segment_info_database(), callback);
model_execution_scheduler_ = std::make_unique<ModelExecutionSchedulerImpl>(
- std::move(observers), segment_info_database, signal_storage_config,
- model_execution_manager_.get(), model_executor_.get(), all_segment_ids,
- clock, platform_options);
-
- model_execution_scheduler_->RequestModelExecutionForEligibleSegments(
- /*expired_only=*/true);
+ std::move(observers), storage_service->segment_info_database(),
+ storage_service->signal_storage_config(), model_execution_manager_.get(),
+ model_executor_.get(), all_segment_ids, clock, platform_options);
}
void ExecutionService::OnNewModelInfoReady(
@@ -72,33 +79,52 @@ void ExecutionService::OnNewModelInfoReady(
model_execution_scheduler_->OnNewModelInfoReady(segment_info);
}
+ModelProvider* ExecutionService::GetModelProvider(SegmentId segment_id) {
+ return model_execution_manager_->GetProvider(segment_id);
+}
+
void ExecutionService::RequestModelExecution(
std::unique_ptr<ExecutionRequest> request) {
DCHECK(request->segment_info);
- if (!request->callback.is_null() && request->model_provider) {
- DCHECK(!request->save_result_to_db)
- << "save_result_to_db + callback cannot be set together";
- model_executor_->ExecuteModel(
- *request->segment_info, request->model_provider,
- request->record_metrics_for_default, std::move(request->callback));
- } else if (request->save_result_to_db) {
+ if (request->save_result_to_db) {
DCHECK(!request->record_metrics_for_default)
<< "cannot record metics for default model from scheduler";
+ // TODO(ssid): Scheduler should use the `request` instead of fetching the
+ // model provider.
DCHECK(!request->model_provider)
<< "using custom model provider to save result is not supported";
DCHECK(request->callback.is_null())
<< "save_result_to_db + callback cannot be set together";
+ DCHECK(!request->input_context)
+ << "saving results keyed on input context is not supported";
model_execution_scheduler_->RequestModelExecution(*request->segment_info);
- } else {
- NOTREACHED()
- << "On demand xecution of non-default model is not yet supported";
+ return;
}
+
+ DCHECK(!request->callback.is_null());
+ model_executor_->ExecuteModel(std::move(request));
}
void ExecutionService::OverwriteModelExecutionResult(
- optimization_guide::proto::OptimizationTarget segment_id,
+ proto::SegmentId segment_id,
const std::pair<float, ModelExecutionStatus>& result) {
model_execution_scheduler_->OnModelExecutionCompleted(segment_id, result);
}
+void ExecutionService::RefreshModelResults() {
+ model_execution_scheduler_->RequestModelExecutionForEligibleSegments(
+ /*expired_only=*/true);
+}
+
+void ExecutionService::RunDailyTasks(bool is_startup) {
+ RefreshModelResults();
+
+ if (is_startup) {
+ // This will trigger data collection after initialization finishes.
+ training_data_collector_->OnServiceInitialized();
+ } else {
+ training_data_collector_->ReportCollectedContinuousTrainingData();
+ }
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/scheduler/execution_service.h b/chromium/components/segmentation_platform/internal/scheduler/execution_service.h
index 20f151b92dc..7bcf5fdbc8c 100644
--- a/chromium/components/segmentation_platform/internal/scheduler/execution_service.h
+++ b/chromium/components/segmentation_platform/internal/scheduler/execution_service.h
@@ -11,20 +11,26 @@
#include "base/containers/flat_set.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/clock.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/internal/execution/execution_request.h"
#include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
+#include "components/segmentation_platform/internal/input_context.h"
#include "components/segmentation_platform/internal/scheduler/model_execution_scheduler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+
+class PrefService;
namespace segmentation_platform {
+namespace processing {
+class FeatureListQueryProcessor;
+class InputDelegateHolder;
+}
+struct Config;
struct PlatformOptions;
-class FeatureListQueryProcessor;
class ModelExecutor;
class ModelProviderFactory;
-class SegmentInfoDatabase;
-class SignalDatabase;
class SignalHandler;
-class SignalStorageConfig;
+class StorageService;
class TrainingDataCollector;
// Handles feature processing and model execution.
@@ -37,65 +43,59 @@ class ExecutionService {
ExecutionService& operator=(ExecutionService&) = delete;
void InitForTesting(
- std::unique_ptr<FeatureListQueryProcessor> feature_processor,
+ std::unique_ptr<processing::FeatureListQueryProcessor> feature_processor,
std::unique_ptr<ModelExecutor> executor,
- std::unique_ptr<ModelExecutionScheduler> scheduler);
+ std::unique_ptr<ModelExecutionScheduler> scheduler,
+ std::unique_ptr<ModelExecutionManager> execution_manager);
void Initialize(
- SignalDatabase* signal_database,
- SegmentInfoDatabase* segment_info_database,
- SignalStorageConfig* signal_storage_config,
+ StorageService* storage_service,
SignalHandler* signal_handler,
base::Clock* clock,
ModelExecutionManager::SegmentationModelUpdatedCallback callback,
scoped_refptr<base::SequencedTaskRunner> task_runner,
- const base::flat_set<OptimizationTarget>& all_segment_ids,
+ const base::flat_set<SegmentId>& all_segment_ids,
ModelProviderFactory* model_provider_factory,
std::vector<ModelExecutionScheduler::Observer*>&& observers,
- const PlatformOptions& platform_options);
+ const PlatformOptions& platform_options,
+ std::unique_ptr<processing::InputDelegateHolder> input_delegate_holder,
+ std::vector<std::unique_ptr<Config>>* configs,
+ PrefService* profile_prefs);
// Called whenever a new or updated model is available. Must be a valid
// SegmentInfo with valid metadata and features.
void OnNewModelInfoReady(const proto::SegmentInfo& segment_info);
- using ModelExecutionCallback =
- base::OnceCallback<void(const std::pair<float, ModelExecutionStatus>&)>;
- struct ExecutionRequest {
- ExecutionRequest();
- ~ExecutionRequest();
-
- // Required: The segment info to use for model execution.
- const proto::SegmentInfo* segment_info = nullptr;
- // Required: The model provider used to execute the model.
- ModelProvider* model_provider = nullptr;
-
- // Save result of execution to the database.
- bool save_result_to_db = false;
- // Record metrics for default model instead of optimization_guide based
- // models.
- bool record_metrics_for_default = false;
- // returns result as by callback, to be used when `save_result_to_db` is
- // false.
- ModelExecutionCallback callback;
- };
+ // Gets the model provider for execution.
+ ModelProvider* GetModelProvider(SegmentId segment_id);
void RequestModelExecution(std::unique_ptr<ExecutionRequest> request);
void OverwriteModelExecutionResult(
- optimization_guide::proto::OptimizationTarget segment_id,
+ proto::SegmentId segment_id,
const std::pair<float, ModelExecutionStatus>& result);
+ // Refreshes model results for all eligible models.
+ void RefreshModelResults();
+
+ // Executes daily maintenance and collection tasks.
+ void RunDailyTasks(bool is_startup);
+
private:
+ raw_ptr<StorageService> storage_service_{};
+
// Training/inference input data generation.
- std::unique_ptr<FeatureListQueryProcessor> feature_list_query_processor_;
+ std::unique_ptr<processing::FeatureListQueryProcessor>
+ feature_list_query_processor_;
// Traing data collection logic.
std::unique_ptr<TrainingDataCollector> training_data_collector_;
+ // Utility to execute model and return result.
std::unique_ptr<ModelExecutor> model_executor_;
// Model execution.
- std::unique_ptr<ModelExecutionManagerImpl> model_execution_manager_;
+ std::unique_ptr<ModelExecutionManager> model_execution_manager_;
// Model execution scheduling logic.
std::unique_ptr<ModelExecutionScheduler> model_execution_scheduler_;
diff --git a/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler.h b/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler.h
index 641b9806c5b..a0ccfca3c20 100644
--- a/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler.h
+++ b/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler.h
@@ -5,16 +5,16 @@
#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SCHEDULER_MODEL_EXECUTION_SCHEDULER_H_
#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SCHEDULER_MODEL_EXECUTION_SCHEDULER_H_
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/execution/model_execution_status.h"
-
-using optimization_guide::proto::OptimizationTarget;
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace segmentation_platform {
namespace proto {
class SegmentInfo;
} // namespace proto
+using proto::SegmentId;
+
// Central class responsible for scheduling model execution. Determines which
// models are eligible for execution based on various criteria e.g. cached
// results, TTL etc. Invoked from multiple classes such as segment
@@ -25,7 +25,7 @@ class ModelExecutionScheduler {
class Observer {
public:
// Called whenever a model execution completes.
- virtual void OnModelExecutionCompleted(OptimizationTarget segment_id) = 0;
+ virtual void OnModelExecutionCompleted(SegmentId segment_id) = 0;
};
virtual ~ModelExecutionScheduler() = default;
@@ -54,7 +54,7 @@ class ModelExecutionScheduler {
// TODO(shaktisahu): Do we want to store that failure reason in the DB
// instead? We might treat different failures differently next time.
virtual void OnModelExecutionCompleted(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
const std::pair<float, ModelExecutionStatus>& result) = 0;
};
diff --git a/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc b/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
index f932d227225..790cb79ac69 100644
--- a/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
+++ b/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
@@ -8,10 +8,11 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/clock.h"
#include "base/time/time.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
#include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/execution/execution_request.h"
#include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/platform_options.h"
#include "components/segmentation_platform/internal/stats.h"
#include "components/segmentation_platform/public/model_provider.h"
@@ -25,7 +26,7 @@ ModelExecutionSchedulerImpl::ModelExecutionSchedulerImpl(
SignalStorageConfig* signal_storage_config,
ModelExecutionManager* model_execution_manager,
ModelExecutor* model_executor,
- base::flat_set<optimization_guide::proto::OptimizationTarget> segment_ids,
+ base::flat_set<proto::SegmentId> segment_ids,
base::Clock* clock,
const PlatformOptions& platform_options)
: observers_(observers),
@@ -58,8 +59,8 @@ void ModelExecutionSchedulerImpl::OnNewModelInfoReady(
void ModelExecutionSchedulerImpl::RequestModelExecutionForEligibleSegments(
bool expired_only) {
- std::vector<OptimizationTarget> segment_ids(all_segment_ids_.begin(),
- all_segment_ids_.end());
+ std::vector<SegmentId> segment_ids(all_segment_ids_.begin(),
+ all_segment_ids_.end());
segment_database_->GetSegmentInfoForSegments(
segment_ids,
base::BindOnce(&ModelExecutionSchedulerImpl::FilterEligibleSegments,
@@ -68,22 +69,24 @@ void ModelExecutionSchedulerImpl::RequestModelExecutionForEligibleSegments(
void ModelExecutionSchedulerImpl::RequestModelExecution(
const proto::SegmentInfo& segment_info) {
- OptimizationTarget segment_id = segment_info.segment_id();
+ SegmentId segment_id = segment_info.segment_id();
CancelOutstandingExecutionRequests(segment_id);
outstanding_requests_.insert(std::make_pair(
segment_id,
base::BindOnce(&ModelExecutionSchedulerImpl::OnModelExecutionCompleted,
weak_ptr_factory_.GetWeakPtr(), segment_id)));
- ModelProvider* model =
+ auto request = std::make_unique<ExecutionRequest>();
+ request->model_provider =
model_execution_manager_->GetProvider(segment_info.segment_id());
- DCHECK(model);
- model_executor_->ExecuteModel(segment_info, model,
- /*record_metrics_for_default=*/false,
- outstanding_requests_[segment_id].callback());
+ DCHECK(request->model_provider);
+ request->segment_info = &segment_info;
+ request->callback = outstanding_requests_[segment_id].callback();
+ request->record_metrics_for_default = false;
+ model_executor_->ExecuteModel(std::move(request));
}
void ModelExecutionSchedulerImpl::OnModelExecutionCompleted(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
const std::pair<float, ModelExecutionStatus>& result) {
// TODO(shaktisahu): Check ModelExecutionStatus and handle failure cases.
// Should we save it to DB?
@@ -107,11 +110,11 @@ void ModelExecutionSchedulerImpl::FilterEligibleSegments(
std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments) {
std::vector<const proto::SegmentInfo*> models_to_run;
for (const auto& pair : *all_segments) {
- OptimizationTarget segment_id = pair.first;
+ SegmentId segment_id = pair.first;
const proto::SegmentInfo& segment_info = pair.second;
if (!ShouldExecuteSegment(expired_only, segment_info)) {
VLOG(1) << "Segmentation scheduler: Skipped executed segment "
- << optimization_guide::proto::OptimizationTarget_Name(segment_id);
+ << proto::SegmentId_Name(segment_id);
continue;
}
@@ -165,7 +168,7 @@ bool ModelExecutionSchedulerImpl::ShouldExecuteSegment(
}
void ModelExecutionSchedulerImpl::CancelOutstandingExecutionRequests(
- OptimizationTarget segment_id) {
+ SegmentId segment_id) {
const auto& iter = outstanding_requests_.find(segment_id);
if (iter != outstanding_requests_.end()) {
iter->second.Cancel();
@@ -173,7 +176,7 @@ void ModelExecutionSchedulerImpl::CancelOutstandingExecutionRequests(
}
}
-void ModelExecutionSchedulerImpl::OnResultSaved(OptimizationTarget segment_id,
+void ModelExecutionSchedulerImpl::OnResultSaved(SegmentId segment_id,
bool success) {
stats::RecordModelExecutionSaveResult(segment_id, success);
if (!success) {
diff --git a/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h b/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h
index 1b8954e88de..cbe2b1a8fa0 100644
--- a/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h
+++ b/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h
@@ -11,11 +11,11 @@
#include "base/cancelable_callback.h"
#include "base/containers/flat_set.h"
#include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
#include "components/segmentation_platform/internal/execution/model_execution_status.h"
#include "components/segmentation_platform/internal/execution/model_executor.h"
#include "components/segmentation_platform/internal/platform_options.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace base {
class Clock;
@@ -32,15 +32,14 @@ class SignalStorageConfig;
class ModelExecutionSchedulerImpl : public ModelExecutionScheduler {
public:
- ModelExecutionSchedulerImpl(
- std::vector<Observer*>&& observers,
- SegmentInfoDatabase* segment_database,
- SignalStorageConfig* signal_storage_config,
- ModelExecutionManager* model_execution_manager,
- ModelExecutor* model_executor,
- base::flat_set<optimization_guide::proto::OptimizationTarget> segment_ids,
- base::Clock* clock,
- const PlatformOptions& platform_options);
+ ModelExecutionSchedulerImpl(std::vector<Observer*>&& observers,
+ SegmentInfoDatabase* segment_database,
+ SignalStorageConfig* signal_storage_config,
+ ModelExecutionManager* model_execution_manager,
+ ModelExecutor* model_executor,
+ base::flat_set<proto::SegmentId> segment_ids,
+ base::Clock* clock,
+ const PlatformOptions& platform_options);
~ModelExecutionSchedulerImpl() override;
// Disallow copy/assign.
@@ -53,7 +52,7 @@ class ModelExecutionSchedulerImpl : public ModelExecutionScheduler {
void RequestModelExecutionForEligibleSegments(bool expired_only) override;
void RequestModelExecution(const proto::SegmentInfo& segment_info) override;
void OnModelExecutionCompleted(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
const std::pair<float, ModelExecutionStatus>& score) override;
private:
@@ -62,9 +61,9 @@ class ModelExecutionSchedulerImpl : public ModelExecutionScheduler {
std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments);
bool ShouldExecuteSegment(bool expired_only,
const proto::SegmentInfo& segment_info);
- void CancelOutstandingExecutionRequests(OptimizationTarget segment_id);
+ void CancelOutstandingExecutionRequests(SegmentId segment_id);
- void OnResultSaved(OptimizationTarget segment_id, bool success);
+ void OnResultSaved(SegmentId segment_id, bool success);
// Observers listening to model exeuction events. Required by the segment
// selection pipeline.
@@ -81,8 +80,7 @@ class ModelExecutionSchedulerImpl : public ModelExecutionScheduler {
const raw_ptr<ModelExecutor> model_executor_;
// The set of all known segments.
- base::flat_set<optimization_guide::proto::OptimizationTarget>
- all_segment_ids_;
+ base::flat_set<proto::SegmentId> all_segment_ids_;
// The time provider.
raw_ptr<base::Clock> clock_;
@@ -91,7 +89,7 @@ class ModelExecutionSchedulerImpl : public ModelExecutionScheduler {
// In-flight model execution requests. Will be killed if we get a model
// update.
- std::map<OptimizationTarget,
+ std::map<SegmentId,
base::CancelableOnceCallback<
ModelExecutor::ModelExecutionCallback::RunType>>
outstanding_requests_;
diff --git a/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc b/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc
index 3108725f4ac..d9cd92fed38 100644
--- a/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc
@@ -12,6 +12,7 @@
#include "components/segmentation_platform/internal/database/segment_info_database.h"
#include "components/segmentation_platform/internal/database/signal_storage_config.h"
#include "components/segmentation_platform/internal/database/test_segment_info_database.h"
+#include "components/segmentation_platform/internal/execution/execution_request.h"
#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
#include "components/segmentation_platform/internal/execution/model_execution_manager.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
@@ -29,34 +30,27 @@ using SignalIdentifier = std::pair<uint64_t, SignalType>;
using CleanupItem = std::tuple<uint64_t, SignalType, base::Time>;
namespace {
-constexpr auto kTestOptimizationTarget =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+constexpr auto kTestSegmentId =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
constexpr auto kTestOptimizationTarget2 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY;
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY;
} // namespace
class MockModelExecutionObserver : public ModelExecutionScheduler::Observer {
public:
MockModelExecutionObserver() = default;
- MOCK_METHOD(void, OnModelExecutionCompleted, (OptimizationTarget));
+ MOCK_METHOD(void, OnModelExecutionCompleted, (SegmentId));
};
class MockModelExecutionManager : public ModelExecutionManager {
public:
- MOCK_METHOD(ModelProvider*,
- GetProvider,
- (optimization_guide::proto::OptimizationTarget segment_id));
+ MOCK_METHOD(ModelProvider*, GetProvider, (proto::SegmentId segment_id));
};
class MockModelExecutor : public ModelExecutor {
public:
MockModelExecutor() = default;
- MOCK_METHOD(void,
- ExecuteModel,
- (const proto::SegmentInfo&,
- ModelProvider*,
- bool,
- ModelExecutionCallback));
+ MOCK_METHOD(void, ExecuteModel, (std::unique_ptr<ExecutionRequest>));
};
class ModelExecutionSchedulerTest : public testing::Test {
@@ -69,8 +63,8 @@ class ModelExecutionSchedulerTest : public testing::Test {
std::vector<ModelExecutionScheduler::Observer*> observers = {&observer1_,
&observer2_};
segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
- base::flat_set<OptimizationTarget> segment_ids;
- segment_ids.insert(kTestOptimizationTarget);
+ base::flat_set<SegmentId> segment_ids;
+ segment_ids.insert(kTestSegmentId);
model_execution_scheduler_ = std::make_unique<ModelExecutionSchedulerImpl>(
std::move(observers), segment_database_.get(), &signal_storage_config_,
&model_execution_manager_, &model_executor_, segment_ids, &clock_,
@@ -89,34 +83,31 @@ class ModelExecutionSchedulerTest : public testing::Test {
};
MATCHER_P(IsForTarget, segment_id, "") {
- return arg.segment_id() == segment_id;
+ return arg->segment_info->segment_id() == segment_id;
}
TEST_F(ModelExecutionSchedulerTest, OnNewModelInfoReady) {
- auto* segment_info =
- segment_database_->FindOrCreateSegment(kTestOptimizationTarget);
- segment_info->set_segment_id(kTestOptimizationTarget);
+ auto* segment_info = segment_database_->FindOrCreateSegment(kTestSegmentId);
+ segment_info->set_segment_id(kTestSegmentId);
auto* metadata = segment_info->mutable_model_metadata();
metadata->set_result_time_to_live(1);
metadata->set_time_unit(proto::TimeUnit::DAY);
- MockModelProvider provider(kTestOptimizationTarget, base::DoNothing());
+ MockModelProvider provider(kTestSegmentId, base::DoNothing());
// If the metadata DOES NOT meet the signal requirement, we SHOULD NOT try to
// execute the model.
- EXPECT_CALL(model_executor_, ExecuteModel(_, _, _, _)).Times(0);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(model_executor_, ExecuteModel(_)).Times(0);
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillOnce(Return(false));
model_execution_scheduler_->OnNewModelInfoReady(*segment_info);
// If the metadata DOES meet the signal requirement, and we have no old,
// PredictionResult we SHOULD try to execute the model.
- EXPECT_CALL(model_execution_manager_, GetProvider(kTestOptimizationTarget))
+ EXPECT_CALL(model_execution_manager_, GetProvider(kTestSegmentId))
.WillOnce(Return(&provider));
- EXPECT_CALL(
- model_executor_,
- ExecuteModel(IsForTarget(kTestOptimizationTarget), &provider, false, _))
+ EXPECT_CALL(model_executor_, ExecuteModel(IsForTarget(kTestSegmentId)))
.Times(1);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillOnce(Return(true));
model_execution_scheduler_->OnNewModelInfoReady(*segment_info);
@@ -125,8 +116,8 @@ TEST_F(ModelExecutionSchedulerTest, OnNewModelInfoReady) {
prediction_result->set_result(0.9);
prediction_result->set_timestamp_us(
clock_.Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
- EXPECT_CALL(model_executor_, ExecuteModel(_, _, _, _)).Times(0);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(model_executor_, ExecuteModel(_)).Times(0);
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillRepeatedly(Return(true)); // Ensure this part has positive result.
model_execution_scheduler_->OnNewModelInfoReady(*segment_info);
@@ -137,7 +128,7 @@ TEST_F(ModelExecutionSchedulerTest, OnNewModelInfoReady) {
prediction_result->set_result(0.9);
prediction_result->set_timestamp_us(
not_expired_timestamp.ToDeltaSinceWindowsEpoch().InMicroseconds());
- EXPECT_CALL(model_executor_, ExecuteModel(_, _, _, _)).Times(0);
+ EXPECT_CALL(model_executor_, ExecuteModel(_)).Times(0);
model_execution_scheduler_->OnNewModelInfoReady(*segment_info);
// If we have an expired result, we SHOULD try to execute the model.
@@ -146,33 +137,29 @@ TEST_F(ModelExecutionSchedulerTest, OnNewModelInfoReady) {
prediction_result->set_result(0.9);
prediction_result->set_timestamp_us(
just_expired_timestamp.ToDeltaSinceWindowsEpoch().InMicroseconds());
- EXPECT_CALL(model_execution_manager_, GetProvider(kTestOptimizationTarget))
+ EXPECT_CALL(model_execution_manager_, GetProvider(kTestSegmentId))
.WillOnce(Return(&provider));
- EXPECT_CALL(
- model_executor_,
- ExecuteModel(IsForTarget(kTestOptimizationTarget), &provider, false, _))
+ EXPECT_CALL(model_executor_, ExecuteModel(IsForTarget(kTestSegmentId)))
.Times(1);
model_execution_scheduler_->OnNewModelInfoReady(*segment_info);
}
TEST_F(ModelExecutionSchedulerTest, RequestModelExecutionForEligibleSegments) {
- MockModelProvider provider(kTestOptimizationTarget, base::DoNothing());
- segment_database_->FindOrCreateSegment(kTestOptimizationTarget);
+ MockModelProvider provider(kTestSegmentId, base::DoNothing());
+ segment_database_->FindOrCreateSegment(kTestSegmentId);
segment_database_->FindOrCreateSegment(kTestOptimizationTarget2);
// TODO(shaktisahu): Add tests for expired segments, freshly computed segments
// etc.
- EXPECT_CALL(model_execution_manager_, GetProvider(kTestOptimizationTarget))
+ EXPECT_CALL(model_execution_manager_, GetProvider(kTestSegmentId))
.WillOnce(Return(&provider));
- EXPECT_CALL(
- model_executor_,
- ExecuteModel(IsForTarget(kTestOptimizationTarget), &provider, false, _))
+ EXPECT_CALL(model_executor_, ExecuteModel(IsForTarget(kTestSegmentId)))
.Times(1);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(model_executor_,
- ExecuteModel(IsForTarget(kTestOptimizationTarget2), _, _, _))
+ ExecuteModel(IsForTarget(kTestOptimizationTarget2)))
.Times(0);
// TODO(shaktisahu): Add test when the signal collection returns false.
@@ -181,21 +168,17 @@ TEST_F(ModelExecutionSchedulerTest, RequestModelExecutionForEligibleSegments) {
TEST_F(ModelExecutionSchedulerTest, OnModelExecutionCompleted) {
proto::SegmentInfo* segment_info =
- segment_database_->FindOrCreateSegment(kTestOptimizationTarget);
+ segment_database_->FindOrCreateSegment(kTestSegmentId);
// TODO(shaktisahu): Add tests for model failure.
- EXPECT_CALL(observer2_, OnModelExecutionCompleted(kTestOptimizationTarget))
- .Times(1);
- EXPECT_CALL(observer1_, OnModelExecutionCompleted(kTestOptimizationTarget))
- .Times(1);
+ EXPECT_CALL(observer2_, OnModelExecutionCompleted(kTestSegmentId)).Times(1);
+ EXPECT_CALL(observer1_, OnModelExecutionCompleted(kTestSegmentId)).Times(1);
float score = 0.4;
model_execution_scheduler_->OnModelExecutionCompleted(
- kTestOptimizationTarget,
- std::make_pair(score, ModelExecutionStatus::kSuccess));
+ kTestSegmentId, std::make_pair(score, ModelExecutionStatus::kSuccess));
// Verify that the results are written to the DB.
- segment_info =
- segment_database_->FindOrCreateSegment(kTestOptimizationTarget);
+ segment_info = segment_database_->FindOrCreateSegment(kTestSegmentId);
ASSERT_TRUE(segment_info->has_prediction_result());
ASSERT_EQ(score, segment_info->prediction_result().result());
}
diff --git a/chromium/components/segmentation_platform/internal/segment_id_convertor.cc b/chromium/components/segmentation_platform/internal/segment_id_convertor.cc
new file mode 100644
index 00000000000..7dd186641eb
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/segment_id_convertor.cc
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/segment_id_convertor.h"
+
+#include "base/check_op.h"
+
+namespace segmentation_platform {
+
+absl::optional<optimization_guide::proto::OptimizationTarget>
+SegmentIdToOptimizationTarget(proto::SegmentId segment_id) {
+ DCHECK_LT(static_cast<int>(optimization_guide::proto::OptimizationTarget_MAX),
+ static_cast<int>(proto::SegmentId::MAX_OPTIMIZATION_TARGET));
+ if (static_cast<int>(segment_id) >=
+ static_cast<int>(proto::SegmentId::MAX_OPTIMIZATION_TARGET)) {
+ return absl::nullopt;
+ }
+ return static_cast<optimization_guide::proto::OptimizationTarget>(segment_id);
+}
+
+proto::SegmentId OptimizationTargetToSegmentId(
+ optimization_guide::proto::OptimizationTarget segment_id) {
+ return static_cast<proto::SegmentId>(segment_id);
+}
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/segment_id_convertor.h b/chromium/components/segmentation_platform/internal/segment_id_convertor.h
new file mode 100644
index 00000000000..743b6b05054
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/segment_id_convertor.h
@@ -0,0 +1,24 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SEGMENT_ID_CONVERTOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SEGMENT_ID_CONVERTOR_H_
+
+#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace segmentation_platform {
+
+// Conversion functions between OptimizationTarget and SegmentId.
+absl::optional<optimization_guide::proto::OptimizationTarget>
+SegmentIdToOptimizationTarget(proto::SegmentId segment_id);
+
+// Conversion functions between OptimizationTarget and SegmentId.
+proto::SegmentId OptimizationTargetToSegmentId(
+ optimization_guide::proto::OptimizationTarget segment_id);
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SEGMENT_ID_CONVERTOR_H_
diff --git a/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index d4be8ec87b2..f37f983f18b 100644
--- a/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -10,15 +10,16 @@
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/system/sys_info.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/clock.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/database/signal_storage_config.h"
#include "components/segmentation_platform/internal/database/storage_service.h"
-#include "components/segmentation_platform/internal/execution/model_execution_manager.h"
+#include "components/segmentation_platform/internal/execution/processing/input_delegate.h"
#include "components/segmentation_platform/internal/platform_options.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h"
@@ -27,17 +28,18 @@
#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
#include "components/segmentation_platform/internal/stats.h"
#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/field_trial_register.h"
#include "components/segmentation_platform/public/model_provider.h"
-
-using optimization_guide::proto::OptimizationTarget;
+#include "components/segmentation_platform/public/trigger_context.h"
namespace segmentation_platform {
-
namespace {
-base::flat_set<OptimizationTarget> GetAllSegmentIds(
+using proto::SegmentId;
+
+base::flat_set<SegmentId> GetAllSegmentIds(
const std::vector<std::unique_ptr<Config>>& configs) {
- base::flat_set<OptimizationTarget> all_segment_ids;
+ base::flat_set<SegmentId> all_segment_ids;
for (const auto& config : configs) {
for (const auto& segment_id : config->segment_ids)
all_segment_ids.insert(segment_id);
@@ -47,63 +49,63 @@ base::flat_set<OptimizationTarget> GetAllSegmentIds(
} // namespace
-SegmentationPlatformServiceImpl::SegmentationPlatformServiceImpl(
- std::unique_ptr<ModelProviderFactory> model_provider,
- leveldb_proto::ProtoDatabaseProvider* db_provider,
- const base::FilePath& storage_dir,
- UkmDataManager* ukm_data_manager,
- PrefService* pref_service,
- history::HistoryService* history_service,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner,
- base::Clock* clock,
- std::vector<std::unique_ptr<Config>> configs)
- : SegmentationPlatformServiceImpl(
- std::make_unique<StorageService>(storage_dir,
- db_provider,
- task_runner,
- clock,
- ukm_data_manager,
- GetAllSegmentIds(configs),
- model_provider.get()),
- std::move(model_provider),
- pref_service,
- history_service,
- task_runner,
- clock,
- std::move(configs)) {}
+SegmentationPlatformServiceImpl::InitParams::InitParams() = default;
+SegmentationPlatformServiceImpl::InitParams::~InitParams() = default;
SegmentationPlatformServiceImpl::SegmentationPlatformServiceImpl(
- std::unique_ptr<StorageService> storage_service,
- std::unique_ptr<ModelProviderFactory> model_provider,
- PrefService* pref_service,
- history::HistoryService* history_service,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner,
- base::Clock* clock,
- std::vector<std::unique_ptr<Config>> configs)
- : model_provider_factory_(std::move(model_provider)),
- task_runner_(task_runner),
- clock_(clock),
+ std::unique_ptr<InitParams> init_params)
+ : model_provider_factory_(std::move(init_params->model_provider)),
+ task_runner_(init_params->task_runner),
+ clock_(init_params->clock.get()),
platform_options_(PlatformOptions::CreateDefault()),
- configs_(std::move(configs)),
- storage_service_(std::move(storage_service)) {
- all_segment_ids_ = GetAllSegmentIds(configs_);
- std::vector<OptimizationTarget> segment_id_vec(all_segment_ids_.begin(),
- all_segment_ids_.end());
+ input_delegate_holder_(std::move(init_params->input_delegate_holder)),
+ configs_(std::move(init_params->configs)),
+ all_segment_ids_(GetAllSegmentIds(configs_)),
+ field_trial_register_(std::move(init_params->field_trial_register)),
+ profile_prefs_(init_params->profile_prefs.get()),
+ creation_time_(clock_->Now()) {
+ base::UmaHistogramMediumTimes(
+ "SegmentationPlatform.Init.ProcessCreationToServiceCreationLatency",
+ base::SysInfo::Uptime());
+
+ DCHECK(task_runner_);
+ DCHECK(clock);
+ DCHECK(init_params->profile_prefs);
+
+ if (init_params->storage_service) {
+ // Test only:
+ storage_service_ = std::move(init_params->storage_service);
+ } else {
+ DCHECK(model_provider_factory_ && init_params->db_provider);
+ DCHECK(!init_params->storage_dir.empty() && init_params->ukm_data_manager);
+ storage_service_ = std::make_unique<StorageService>(
+ init_params->storage_dir, init_params->db_provider,
+ init_params->task_runner, init_params->clock,
+ init_params->ukm_data_manager, all_segment_ids_,
+ model_provider_factory_.get());
+ }
+
+ std::vector<SegmentId> segment_id_vec(all_segment_ids_.begin(),
+ all_segment_ids_.end());
// Construct signal processors.
signal_handler_.Initialize(
- storage_service_->signal_database(),
- storage_service_->segment_info_database(),
- storage_service_->ukm_data_manager(), history_service,
- storage_service_->default_model_manager(), segment_id_vec);
+ storage_service_.get(), init_params->history_service, segment_id_vec,
+ base::BindRepeating(
+ &SegmentationPlatformServiceImpl::OnModelRefreshNeeded,
+ weak_ptr_factory_.GetWeakPtr()));
for (const auto& config : configs_) {
segment_selectors_[config->segmentation_key] =
std::make_unique<SegmentSelectorImpl>(
storage_service_->segment_info_database(),
- storage_service_->signal_storage_config(), pref_service,
- config.get(), clock, platform_options_,
+ storage_service_->signal_storage_config(),
+ init_params->profile_prefs, config.get(),
+ field_trial_register_.get(), init_params->clock, platform_options_,
storage_service_->default_model_manager());
+ if (config->trigger != TriggerType::kNone) {
+ clients_for_trigger_[config->trigger].insert(config->segmentation_key);
+ }
}
proxy_ = std::make_unique<ServiceProxyImpl>(
@@ -139,6 +141,55 @@ SegmentSelectionResult SegmentationPlatformServiceImpl::GetCachedSegmentResult(
return selector->GetCachedSegmentResult();
}
+CallbackId
+SegmentationPlatformServiceImpl::RegisterOnDemandSegmentSelectionCallback(
+ const std::string& segmentation_key,
+ const OnDemandSegmentSelectionCallback& callback) {
+ static auto callback_id_generator = CallbackId::Generator();
+ const CallbackId callback_id = callback_id_generator.GenerateNextId();
+ callback_map_[callback_id] = callback;
+ segment_selection_callback_ids_[segmentation_key].insert(callback_id);
+ return callback_id;
+}
+
+void SegmentationPlatformServiceImpl::
+ UnregisterOnDemandSegmentSelectionCallback(
+ CallbackId callback_id,
+ const std::string& segmentation_key) {
+ segment_selection_callback_ids_[segmentation_key].erase(callback_id);
+ if (segment_selection_callback_ids_[segmentation_key].empty()) {
+ segment_selection_callback_ids_.erase(segmentation_key);
+ }
+}
+
+void SegmentationPlatformServiceImpl::OnTrigger(
+ TriggerType trigger,
+ const TriggerContext& trigger_context) {
+ if (clients_for_trigger_.find(trigger) == clients_for_trigger_.end())
+ return;
+ scoped_refptr<InputContext> input_context;
+ for (const auto& segmentation_key : clients_for_trigger_[trigger]) {
+ CHECK(segment_selectors_.find(segmentation_key) !=
+ segment_selectors_.end());
+ auto& selector = segment_selectors_.at(segmentation_key);
+ selector->GetSelectedSegmentOnDemand(
+ input_context,
+ base::BindOnce(
+ &SegmentationPlatformServiceImpl::OnSegmentSelectionForTrigger,
+ weak_ptr_factory_.GetWeakPtr(), segmentation_key, trigger_context));
+ }
+}
+
+void SegmentationPlatformServiceImpl::OnSegmentSelectionForTrigger(
+ const std::string& segmentation_key,
+ const TriggerContext& trigger_context,
+ const SegmentSelectionResult& selected_segment) {
+ for (auto callback_id : segment_selection_callback_ids_[segmentation_key]) {
+ const auto& callback = callback_map_[callback_id];
+ callback.Run(selected_segment, trigger_context);
+ }
+}
+
void SegmentationPlatformServiceImpl::EnableMetrics(
bool signal_collection_allowed) {
signal_handler_.EnableMetrics(signal_collection_allowed);
@@ -148,6 +199,10 @@ ServiceProxy* SegmentationPlatformServiceImpl::GetServiceProxy() {
return proxy_.get();
}
+bool SegmentationPlatformServiceImpl::IsPlatformInitialized() {
+ return storage_initialized_;
+}
+
void SegmentationPlatformServiceImpl::OnDatabaseInitialized(bool success) {
storage_initialized_ = true;
OnServiceStatusChanged();
@@ -168,21 +223,28 @@ void SegmentationPlatformServiceImpl::OnDatabaseInitialized(bool success) {
std::vector<ModelExecutionSchedulerImpl::Observer*> observers;
for (auto& key_and_selector : segment_selectors_)
observers.push_back(key_and_selector.second.get());
+ observers.push_back(proxy_.get());
execution_service_.Initialize(
- storage_service_->signal_database(),
- storage_service_->segment_info_database(),
- storage_service_->signal_storage_config(), &signal_handler_, clock_,
+ storage_service_.get(), &signal_handler_, clock_,
base::BindRepeating(
&SegmentationPlatformServiceImpl::OnSegmentationModelUpdated,
weak_ptr_factory_.GetWeakPtr()),
task_runner_, all_segment_ids_, model_provider_factory_.get(),
- std::move(observers), platform_options_);
+ std::move(observers), platform_options_,
+ std::move(input_delegate_holder_), &configs_, profile_prefs_);
proxy_->SetExecutionService(&execution_service_);
for (auto& selector : segment_selectors_) {
selector.second->OnPlatformInitialized(&execution_service_);
}
+
+ RunDailyTasks(/*is_startup=*/true);
+
+ init_time_ = clock_->Now();
+ base::UmaHistogramMediumTimes(
+ "SegmentationPlatform.Init.CreationToInitializationLatency",
+ init_time_ - creation_time_);
}
void SegmentationPlatformServiceImpl::OnSegmentationModelUpdated(
@@ -190,8 +252,6 @@ void SegmentationPlatformServiceImpl::OnSegmentationModelUpdated(
DCHECK(metadata_utils::ValidateSegmentInfoMetadataAndFeatures(segment_info) ==
metadata_utils::ValidationResult::kValidationSuccess);
- storage_service_->signal_storage_config()->OnSignalCollectionStarted(
- segment_info.model_metadata());
signal_handler_.OnSignalListUpdated();
execution_service_.OnNewModelInfoReady(segment_info);
@@ -203,21 +263,38 @@ void SegmentationPlatformServiceImpl::OnSegmentationModelUpdated(
weak_ptr_factory_.GetWeakPtr()));
}
+void SegmentationPlatformServiceImpl::OnModelRefreshNeeded() {
+ execution_service_.RefreshModelResults();
+}
+
void SegmentationPlatformServiceImpl::OnServiceStatusChanged() {
proxy_->OnServiceStatusChanged(storage_initialized_,
storage_service_->GetServiceStatus());
}
+void SegmentationPlatformServiceImpl::RunDailyTasks(bool is_startup) {
+ execution_service_.RunDailyTasks(is_startup);
+ storage_service_->ExecuteDatabaseMaintenanceTasks(is_startup);
+
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&SegmentationPlatformServiceImpl::RunDailyTasks,
+ weak_ptr_factory_.GetWeakPtr(), /*is_startup=*/false),
+ base::Days(1));
+}
+
// static
void SegmentationPlatformService::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kSegmentationResultPref);
}
+// static
void SegmentationPlatformService::RegisterLocalStatePrefs(
PrefRegistrySimple* registry) {
registry->RegisterTimePref(kSegmentationUkmMostRecentAllowedTimeKey,
base::Time());
+ registry->RegisterTimePref(kSegmentationLastCollectionTimePref, base::Time());
}
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.h b/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.h
index 8bb2cf2959b..399f5508b3b 100644
--- a/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.h
+++ b/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl.h
@@ -14,13 +14,13 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/storage_service.h"
#include "components/segmentation_platform/internal/execution/model_execution_manager.h"
#include "components/segmentation_platform/internal/platform_options.h"
#include "components/segmentation_platform/internal/scheduler/execution_service.h"
#include "components/segmentation_platform/internal/service_proxy_impl.h"
#include "components/segmentation_platform/internal/signals/signal_handler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "components/segmentation_platform/public/segmentation_platform_service.h"
namespace base {
@@ -41,7 +41,12 @@ class PrefService;
namespace segmentation_platform {
+namespace processing {
+class InputDelegateHolder;
+}
+
struct Config;
+class FieldTrialRegister;
class ModelProviderFactory;
class SegmentSelectorImpl;
class SegmentScoreProvider;
@@ -50,26 +55,36 @@ class UkmDataManager;
// The internal implementation of the SegmentationPlatformService.
class SegmentationPlatformServiceImpl : public SegmentationPlatformService {
public:
- SegmentationPlatformServiceImpl(
- std::unique_ptr<ModelProviderFactory> model_provider,
- leveldb_proto::ProtoDatabaseProvider* db_provider,
- const base::FilePath& storage_dir,
- UkmDataManager* ukm_data_manager,
- PrefService* pref_service,
- history::HistoryService* history_service,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner,
- base::Clock* clock,
- std::vector<std::unique_ptr<Config>> configs);
-
- // For testing only.
- SegmentationPlatformServiceImpl(
- std::unique_ptr<StorageService> storage_service,
- std::unique_ptr<ModelProviderFactory> model_provider,
- PrefService* pref_service,
- history::HistoryService* history_service,
- const scoped_refptr<base::SequencedTaskRunner>& task_runner,
- base::Clock* clock,
- std::vector<std::unique_ptr<Config>> configs);
+ struct InitParams {
+ InitParams();
+ ~InitParams();
+
+ bool IsValid();
+
+ // Profile data:
+ raw_ptr<leveldb_proto::ProtoDatabaseProvider> db_provider = nullptr;
+ raw_ptr<history::HistoryService> history_service = nullptr;
+ base::FilePath storage_dir;
+ raw_ptr<PrefService> profile_prefs = nullptr;
+
+ // Platform configuration:
+ std::unique_ptr<ModelProviderFactory> model_provider;
+ raw_ptr<UkmDataManager> ukm_data_manager = nullptr;
+ std::vector<std::unique_ptr<Config>> configs;
+ std::unique_ptr<FieldTrialRegister> field_trial_register;
+ std::unique_ptr<processing::InputDelegateHolder> input_delegate_holder;
+
+ scoped_refptr<base::SequencedTaskRunner> task_runner;
+ raw_ptr<base::Clock> clock = nullptr;
+
+ // Test only:
+ std::unique_ptr<StorageService> storage_service;
+ };
+
+ explicit SegmentationPlatformServiceImpl(
+ std::unique_ptr<InitParams> init_params);
+
+ SegmentationPlatformServiceImpl();
~SegmentationPlatformServiceImpl() override;
@@ -84,8 +99,17 @@ class SegmentationPlatformServiceImpl : public SegmentationPlatformService {
SegmentSelectionCallback callback) override;
SegmentSelectionResult GetCachedSegmentResult(
const std::string& segmentation_key) override;
+ CallbackId RegisterOnDemandSegmentSelectionCallback(
+ const std::string& segmentation_key,
+ const OnDemandSegmentSelectionCallback& callback) override;
+ void UnregisterOnDemandSegmentSelectionCallback(
+ CallbackId callback_id,
+ const std::string& segmentation_key) override;
+ void OnTrigger(TriggerType trigger,
+ const TriggerContext& trigger_context) override;
void EnableMetrics(bool signal_collection_allowed) override;
ServiceProxy* GetServiceProxy() override;
+ bool IsPlatformInitialized() override;
private:
friend class SegmentationPlatformServiceImplTest;
@@ -96,19 +120,35 @@ class SegmentationPlatformServiceImpl : public SegmentationPlatformService {
// Must only be invoked with a valid SegmentInfo.
void OnSegmentationModelUpdated(proto::SegmentInfo segment_info);
+ // Callback sent to child classes to notify when model results need to be
+ // refreshed. For example, when history is cleared.
+ void OnModelRefreshNeeded();
+
// Called when service status changes.
void OnServiceStatusChanged();
+ // Task that runs every day or at startup to keep the platform data updated.
+ void RunDailyTasks(bool is_startup);
+
+ // Callback to run after on-demand segment selection.
+ void OnSegmentSelectionForTrigger(
+ const std::string& segmentation_key,
+ const TriggerContext& trigger_context,
+ const SegmentSelectionResult& selected_segment);
+
std::unique_ptr<ModelProviderFactory> model_provider_factory_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
raw_ptr<base::Clock> clock_;
const PlatformOptions platform_options_;
+ // Temporarily stored till initialization and moved to `execution_service_`.
+ std::unique_ptr<processing::InputDelegateHolder> input_delegate_holder_;
+
// Config.
std::vector<std::unique_ptr<Config>> configs_;
- base::flat_set<optimization_guide::proto::OptimizationTarget>
- all_segment_ids_;
+ base::flat_set<proto::SegmentId> all_segment_ids_;
+ std::unique_ptr<FieldTrialRegister> field_trial_register_;
std::unique_ptr<StorageService> storage_service_;
bool storage_initialized_ = false;
@@ -122,6 +162,14 @@ class SegmentationPlatformServiceImpl : public SegmentationPlatformService {
base::flat_map<std::string, std::unique_ptr<SegmentSelectorImpl>>
segment_selectors_;
+ // On-demand segment selection.
+ base::flat_map<std::string, base::flat_set<CallbackId>>
+ segment_selection_callback_ids_;
+ base::flat_map<CallbackId, OnDemandSegmentSelectionCallback> callback_map_;
+
+ // Clients registered for trigger events.
+ base::flat_map<TriggerType, base::flat_set<std::string>> clients_for_trigger_;
+
// Segment results.
std::unique_ptr<SegmentScoreProvider> segment_score_provider_;
@@ -129,6 +177,13 @@ class SegmentationPlatformServiceImpl : public SegmentationPlatformService {
std::unique_ptr<ServiceProxyImpl> proxy_;
+ // PrefService from profile.
+ raw_ptr<PrefService> profile_prefs_;
+
+ // For metrics only:
+ const base::Time creation_time_;
+ base::Time init_time_;
+
base::WeakPtrFactory<SegmentationPlatformServiceImpl> weak_ptr_factory_{this};
};
diff --git a/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc b/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
index ed93b28deec..994b55e4fdd 100644
--- a/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
@@ -16,13 +16,17 @@
#include "base/test/task_environment.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/segmentation_platform/internal/constants.h"
+#include "components/segmentation_platform/internal/database/mock_ukm_database.h"
#include "components/segmentation_platform/internal/dummy_ukm_data_manager.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/segmentation_platform_service_test_base.h"
#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
+#include "components/segmentation_platform/internal/signals/ukm_observer.h"
#include "components/segmentation_platform/internal/ukm_data_manager_impl.h"
#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
#include "components/segmentation_platform/public/segment_selection_result.h"
+#include "components/ukm/test_ukm_recorder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -54,10 +58,20 @@ class SegmentationPlatformServiceImplTest
public SegmentationPlatformServiceTestBase {
public:
explicit SegmentationPlatformServiceImplTest(
- std::unique_ptr<UkmDataManager> ukm_data_manager = nullptr)
- : ukm_data_manager_(ukm_data_manager
- ? std::move(ukm_data_manager)
- : std::make_unique<UkmDataManagerImpl>()) {}
+ std::unique_ptr<UkmDataManager> ukm_data_manager = nullptr) {
+ if (ukm_data_manager) {
+ ukm_data_manager_ = std::move(ukm_data_manager);
+ return;
+ }
+ SegmentationPlatformService::RegisterLocalStatePrefs(prefs_.registry());
+ LocalStateHelper::GetInstance().Initialize(&prefs_);
+ ukm_data_manager_ = std::make_unique<UkmDataManagerImpl>();
+ ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
+ ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_.get());
+ auto ukm_database = std::make_unique<MockUkmDatabase>();
+ static_cast<UkmDataManagerImpl*>(ukm_data_manager_.get())
+ ->InitializeForTesting(std::move(ukm_database), ukm_observer_.get());
+ }
~SegmentationPlatformServiceImplTest() override = default;
@@ -84,11 +98,13 @@ class SegmentationPlatformServiceImplTest
std::move(closure).Run();
}
+ void OnOnDemandSegmentSelection(const SegmentSelectionResult& result,
+ const TriggerContext& trigger_context) {}
+
void AssertSelectedSegment(
const std::string& segmentation_key,
bool is_ready,
- OptimizationTarget expected =
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+ SegmentId expected = SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
SegmentSelectionResult result;
result.is_ready = is_ready;
if (is_ready)
@@ -105,8 +121,7 @@ class SegmentationPlatformServiceImplTest
void AssertCachedSegment(
const std::string& segmentation_key,
bool is_ready,
- OptimizationTarget expected =
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+ SegmentId expected = SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
SegmentSelectionResult result;
result.is_ready = is_ready;
if (is_ready)
@@ -152,19 +167,15 @@ class SegmentationPlatformServiceImplTest
// from the database, and then write the merged result of the old and new to
// the database.
ASSERT_TRUE(model_provider_data_.model_providers_callbacks.count(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
model_provider_data_
.model_providers_callbacks
- [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE]
- .Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
- metadata, kModelVersion);
+ [SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE]
+ .Run(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, metadata,
+ kModelVersion);
segment_db_->GetCallback(true);
segment_db_->UpdateCallback(true);
- // Since the updated config had a new feature, the SignalStorageConfigs DB
- // should have been updated.
- segment_storage_config_db_->UpdateCallback(true);
-
// The SignalFilterProcessor needs to read the segment information from the
// database before starting to listen to the updated signals.
segment_db_->LoadCallback(true);
@@ -175,14 +186,12 @@ class SegmentationPlatformServiceImplTest
histogram_tester.GetBucketCount(
"SegmentationPlatform.Signals.ListeningCount.HistogramValue", 1));
- AssertSelectedSegment(
- kTestSegmentationKey1, true,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ AssertSelectedSegment(kTestSegmentationKey1, true,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
AssertSelectedSegment(kTestSegmentationKey2, false);
AssertSelectedSegment(kTestSegmentationKey3, false);
- AssertCachedSegment(
- kTestSegmentationKey1, true,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ AssertCachedSegment(kTestSegmentationKey1, true,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
AssertCachedSegment(kTestSegmentationKey2, false);
AssertCachedSegment(kTestSegmentationKey3, false);
@@ -192,12 +201,12 @@ class SegmentationPlatformServiceImplTest
segment_db_->LoadCallback(true);
ASSERT_TRUE(model_provider_data_.model_providers_callbacks.count(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
model_provider_data_
.model_providers_callbacks
- [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE]
- .Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
- metadata, kModelVersion);
+ [SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE]
+ .Run(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE, metadata,
+ kModelVersion);
segment_db_->GetCallback(true);
segment_db_->UpdateCallback(true);
@@ -219,17 +228,15 @@ class SegmentationPlatformServiceImplTest
// Database maintenance tasks should try to cleanup the signals after a
// short delay, which starts with looking up data from the
// SegmentInfoDatabase.
- task_environment_.FastForwardUntilNoTasksRemain();
+ task_environment_.FastForwardBy(base::Hours(1));
segment_db_->LoadCallback(true);
- AssertSelectedSegment(
- kTestSegmentationKey1, true,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ AssertSelectedSegment(kTestSegmentationKey1, true,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
AssertSelectedSegment(kTestSegmentationKey2, false);
AssertSelectedSegment(kTestSegmentationKey3, false);
- AssertCachedSegment(
- kTestSegmentationKey1, true,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ AssertCachedSegment(kTestSegmentationKey1, true,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
AssertCachedSegment(kTestSegmentationKey2, false);
AssertCachedSegment(kTestSegmentationKey3, false);
}
@@ -238,6 +245,9 @@ class SegmentationPlatformServiceImplTest
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
MockServiceProxyObserver observer_;
std::unique_ptr<UkmDataManager> ukm_data_manager_;
+ std::unique_ptr<ukm::TestUkmRecorder> ukm_recorder_;
+ std::unique_ptr<UkmObserver> ukm_observer_;
+ TestingPrefServiceSimple prefs_;
};
TEST_F(SegmentationPlatformServiceImplTest, InitializationFlow) {
@@ -248,7 +258,7 @@ TEST_F(SegmentationPlatformServiceImplTest,
GetSelectedSegmentBeforeInitialization) {
SegmentSelectionResult expected;
expected.is_ready = true;
- expected.segment = OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ expected.segment = proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
base::RunLoop loop;
segmentation_platform_service_impl_->GetSelectedSegment(
kTestSegmentationKey1,
@@ -257,6 +267,38 @@ TEST_F(SegmentationPlatformServiceImplTest,
loop.Run();
}
+TEST_F(SegmentationPlatformServiceImplTest, RegisterAndUnregisterCallback) {
+ CallbackId callback_id1 =
+ segmentation_platform_service_impl_
+ ->RegisterOnDemandSegmentSelectionCallback(
+ kTestSegmentationKey1,
+ base::BindRepeating(&SegmentationPlatformServiceImplTest::
+ OnOnDemandSegmentSelection,
+ base::Unretained(this)));
+ CallbackId callback_id2 =
+ segmentation_platform_service_impl_
+ ->RegisterOnDemandSegmentSelectionCallback(
+ kTestSegmentationKey1,
+ base::BindRepeating(&SegmentationPlatformServiceImplTest::
+ OnOnDemandSegmentSelection,
+ base::Unretained(this)));
+ ASSERT_EQ(callback_id2.value(), callback_id1.value() + 1);
+
+ segmentation_platform_service_impl_
+ ->UnregisterOnDemandSegmentSelectionCallback(callback_id1,
+ kTestSegmentationKey1);
+ segmentation_platform_service_impl_
+ ->UnregisterOnDemandSegmentSelectionCallback(callback_id2,
+ kTestSegmentationKey1);
+
+ // Calling unregister multiple times have no effect.
+ segmentation_platform_service_impl_
+ ->UnregisterOnDemandSegmentSelectionCallback(callback_id2,
+ kTestSegmentationKey1);
+
+ // TODO(shaktisahu): Add test for OnTrigger that invokes the callback.
+}
+
class SegmentationPlatformServiceImplEmptyConfigTest
: public SegmentationPlatformServiceImplTest {
std::vector<std::unique_ptr<Config>> CreateConfigs() override {
@@ -286,14 +328,12 @@ class SegmentationPlatformServiceImplMultiClientTest
base::Value segmentation_result(base::Value::Type::DICTIONARY);
segmentation_result.SetIntKey(
- "segment_id",
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ "segment_id", SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
dictionary->SetKey(kTestSegmentationKey1, std::move(segmentation_result));
base::Value segmentation_result2(base::Value::Type::DICTIONARY);
segmentation_result2.SetIntKey(
- "segment_id",
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
+ "segment_id", SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
dictionary->SetKey(kTestSegmentationKey2, std::move(segmentation_result2));
}
};
@@ -311,19 +351,15 @@ TEST_F(SegmentationPlatformServiceImplMultiClientTest, InitializationFlow) {
// querying segment db.
segment_db_->LoadCallback(true);
- AssertSelectedSegment(
- kTestSegmentationKey1, true,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
- AssertSelectedSegment(
- kTestSegmentationKey2, true,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
+ AssertSelectedSegment(kTestSegmentationKey1, true,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ AssertSelectedSegment(kTestSegmentationKey2, true,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
AssertSelectedSegment(kTestSegmentationKey3, false);
- AssertCachedSegment(
- kTestSegmentationKey1, true,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
- AssertCachedSegment(
- kTestSegmentationKey2, true,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
+ AssertCachedSegment(kTestSegmentationKey1, true,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ AssertCachedSegment(kTestSegmentationKey2, true,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
AssertCachedSegment(kTestSegmentationKey3, false);
}
diff --git a/chromium/components/segmentation_platform/internal/segmentation_platform_service_test_base.cc b/chromium/components/segmentation_platform/internal/segmentation_platform_service_test_base.cc
index a80774f9970..8fa92fb3df5 100644
--- a/chromium/components/segmentation_platform/internal/segmentation_platform_service_test_base.cc
+++ b/chromium/components/segmentation_platform/internal/segmentation_platform_service_test_base.cc
@@ -14,37 +14,46 @@
#include "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
#include "components/segmentation_platform/internal/ukm_data_manager.h"
#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/field_trial_register.h"
namespace segmentation_platform {
namespace {
+class MockFieldTrialRegister : public FieldTrialRegister {
+ public:
+ MOCK_METHOD2(RegisterFieldTrial,
+ void(base::StringPiece trial_name,
+ base::StringPiece group_name));
+ MOCK_METHOD3(RegisterSubsegmentFieldTrialIfNeeded,
+ void(base::StringPiece trial_name,
+ proto::SegmentId segment_id,
+ int subsegment_rank));
+};
+
std::vector<std::unique_ptr<Config>> CreateTestConfigs() {
std::vector<std::unique_ptr<Config>> configs;
{
std::unique_ptr<Config> config = std::make_unique<Config>();
config->segmentation_key = kTestSegmentationKey1;
config->segment_selection_ttl = base::Days(28);
- config->segment_ids = {
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
+ config->segment_ids = {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
configs.push_back(std::move(config));
}
{
std::unique_ptr<Config> config = std::make_unique<Config>();
config->segmentation_key = kTestSegmentationKey2;
config->segment_selection_ttl = base::Days(10);
- config->segment_ids = {
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE};
+ config->segment_ids = {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE};
configs.push_back(std::move(config));
}
{
std::unique_ptr<Config> config = std::make_unique<Config>();
config->segmentation_key = kTestSegmentationKey3;
config->segment_selection_ttl = base::Days(14);
- config->segment_ids = {
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB};
+ config->segment_ids = {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB};
configs.push_back(std::move(config));
}
{
@@ -92,7 +101,7 @@ void SegmentationPlatformServiceTestBase::InitPlatform(
SetUpPrefs();
std::vector<std::unique_ptr<Config>> configs = CreateTestConfigs();
- base::flat_set<OptimizationTarget> all_segment_ids;
+ base::flat_set<SegmentId> all_segment_ids;
for (const auto& config : configs) {
for (const auto& segment_id : config->segment_ids)
all_segment_ids.insert(segment_id);
@@ -102,12 +111,18 @@ void SegmentationPlatformServiceTestBase::InitPlatform(
std::move(segment_storage_config_db), &test_clock_, ukm_data_manager,
all_segment_ids, model_provider_factory.get());
+ auto params = std::make_unique<SegmentationPlatformServiceImpl::InitParams>();
+ params->storage_service = std::move(storage_service);
+ params->model_provider =
+ std::make_unique<TestModelProviderFactory>(&model_provider_data_);
+ params->profile_prefs = &pref_service_;
+ params->history_service = history_service;
+ params->task_runner = task_runner_;
+ params->clock = &test_clock_;
+ params->configs = std::move(configs);
+ params->field_trial_register = std::make_unique<MockFieldTrialRegister>();
segmentation_platform_service_impl_ =
- std::make_unique<SegmentationPlatformServiceImpl>(
- std::move(storage_service),
- std::make_unique<TestModelProviderFactory>(&model_provider_data_),
- &pref_service_, history_service, task_runner_, &test_clock_,
- std::move(configs));
+ std::make_unique<SegmentationPlatformServiceImpl>(std::move(params));
}
void SegmentationPlatformServiceTestBase::DestroyPlatform() {
@@ -123,7 +138,7 @@ void SegmentationPlatformServiceTestBase::SetUpPrefs() {
base::Value segmentation_result(base::Value::Type::DICTIONARY);
segmentation_result.SetIntKey(
- "segment_id", OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ "segment_id", SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
dictionary->SetKey(kTestSegmentationKey1, std::move(segmentation_result));
}
diff --git a/chromium/components/segmentation_platform/internal/segmentation_ukm_helper.cc b/chromium/components/segmentation_platform/internal/segmentation_ukm_helper.cc
index 8a99d1b4319..ab518186d22 100644
--- a/chromium/components/segmentation_platform/internal/segmentation_ukm_helper.cc
+++ b/chromium/components/segmentation_platform/internal/segmentation_ukm_helper.cc
@@ -8,15 +8,20 @@
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
+#include "base/time/clock.h"
+#include "components/segmentation_platform/internal/constants.h"
+#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
#include "components/segmentation_platform/internal/stats.h"
#include "components/segmentation_platform/public/config.h"
#include "components/segmentation_platform/public/features.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#define CALL_MEMBER_FN(obj, func) ((obj).*(func))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x)[0])
+using segmentation_platform::proto::SegmentId;
using ukm::builders::Segmentation_ModelExecution;
namespace {
@@ -63,8 +68,7 @@ const UkmMemberFn kSegmentationUkmOutputMethods[] = {
&Segmentation_ModelExecution::SetActualResult5,
&Segmentation_ModelExecution::SetActualResult6};
-// Gets a set of segment IDs that are allowed to upload metrics.
-base::flat_set<int> GetSegmentIdsAllowedForReporting() {
+base::flat_set<SegmentId> GetSegmentIdsAllowedForReporting() {
std::vector<std::string> segment_ids = base::SplitString(
base::GetFieldTrialParamValueByFeature(
segmentation_platform::features::
@@ -72,11 +76,11 @@ base::flat_set<int> GetSegmentIdsAllowedForReporting() {
segmentation_platform::kSegmentIdsAllowedForReportingKey),
",;", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
- base::flat_set<int> result;
+ base::flat_set<SegmentId> result;
for (const auto& id : segment_ids) {
int segment_id;
if (base::StringToInt(id, &segment_id))
- result.emplace(segment_id);
+ result.emplace(static_cast<SegmentId>(segment_id));
}
return result;
}
@@ -102,7 +106,7 @@ SegmentationUkmHelper* SegmentationUkmHelper::GetInstance() {
}
ukm::SourceId SegmentationUkmHelper::RecordModelExecutionResult(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
int64_t model_version,
const std::vector<float>& input_tensor,
float result) {
@@ -121,11 +125,13 @@ ukm::SourceId SegmentationUkmHelper::RecordModelExecutionResult(
}
ukm::SourceId SegmentationUkmHelper::RecordTrainingData(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
int64_t model_version,
const std::vector<float>& input_tensor,
const std::vector<float>& outputs,
- const std::vector<int>& output_indexes) {
+ const std::vector<int>& output_indexes,
+ absl::optional<proto::PredictionResult> prediction_result,
+ absl::optional<SelectedSegment> selected_segment) {
ukm::SourceId source_id = ukm::NoURLSourceId();
ukm::builders::Segmentation_ModelExecution execution_result(source_id);
if (!AddInputsToUkm(&execution_result, segment_id, model_version,
@@ -137,13 +143,23 @@ ukm::SourceId SegmentationUkmHelper::RecordTrainingData(
return ukm::kInvalidSourceId;
}
+ if (prediction_result.has_value()) {
+ execution_result.SetPredictionResult(
+ FloatToInt64(prediction_result->result()));
+ }
+ if (selected_segment.has_value()) {
+ execution_result.SetSelectionResult(selected_segment->segment_id);
+ execution_result.SetOutputDelaySec(
+ (base::Time::Now() - selected_segment->selection_time).InSeconds());
+ }
+
execution_result.Record(ukm::UkmRecorder::Get());
return source_id;
}
bool SegmentationUkmHelper::AddInputsToUkm(
ukm::builders::Segmentation_ModelExecution* ukm_builder,
- OptimizationTarget segment_id,
+ SegmentId segment_id,
int64_t model_version,
const std::vector<float>& input_tensor) {
if (!allowed_segment_ids_.contains(static_cast<int>(segment_id)))
@@ -189,7 +205,21 @@ bool SegmentationUkmHelper::AddOutputsToUkm(
// static
int64_t SegmentationUkmHelper::FloatToInt64(float f) {
// Encode the float number in IEEE754 double precision.
- return bit_cast<int64_t>(static_cast<double>(f));
+ return base::bit_cast<int64_t>(static_cast<double>(f));
+}
+
+// static
+bool SegmentationUkmHelper::AllowedToUploadData(
+ base::TimeDelta signal_storage_length,
+ base::Clock* clock) {
+ base::Time most_recent_allowed = LocalStateHelper::GetInstance().GetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey);
+ // If the local state is never set, return false.
+ if (most_recent_allowed.is_null() ||
+ most_recent_allowed == base::Time::Max()) {
+ return false;
+ }
+ return most_recent_allowed + signal_storage_length < clock->Now();
}
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/segmentation_ukm_helper.h b/chromium/components/segmentation_platform/internal/segmentation_ukm_helper.h
index 8de5946c0bb..80b20210d08 100644
--- a/chromium/components/segmentation_platform/internal/segmentation_ukm_helper.h
+++ b/chromium/components/segmentation_platform/internal/segmentation_ukm_helper.h
@@ -7,10 +7,15 @@
#include "base/containers/flat_set.h"
#include "base/no_destructor.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "base/time/time.h"
+#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
-using optimization_guide::proto::OptimizationTarget;
+namespace base {
+class Clock;
+}
namespace ukm::builders {
class Segmentation_ModelExecution;
@@ -18,6 +23,9 @@ class Segmentation_ModelExecution;
namespace segmentation_platform {
+using proto::SegmentId;
+struct SelectedSegment;
+
// A helper class to record segmentation model execution results in UKM.
class SegmentationUkmHelper {
public:
@@ -28,29 +36,45 @@ class SegmentationUkmHelper {
// Record segmentation model information and input/output after the
// executing the model, and return the UKM source ID.
ukm::SourceId RecordModelExecutionResult(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
int64_t model_version,
const std::vector<float>& input_tensor,
float result);
// Record segmentation model training data as UKM message.
- // |input_tensors| contains the values for training inputs.
- // |outputs| contains the values for outputs.
- // |output_indexes| contains the indexes for outputs that needs to be included
+ // `input_tensors` contains the values for training inputs.
+ // `outputs` contains the values for outputs.
+ // `output_indexes` contains the indexes for outputs that needs to be included
// in the ukm message.
+ // `prediction_result` is the most recent model execution result.
+ // `selected_segment` is the recently selected segment for the feature that is
+ // tied to the ML model.
// Return the UKM source ID.
- ukm::SourceId RecordTrainingData(OptimizationTarget segment_id,
- int64_t model_version,
- const std::vector<float>& input_tensors,
- const std::vector<float>& outputs,
- const std::vector<int>& output_indexes);
+ ukm::SourceId RecordTrainingData(
+ SegmentId segment_id,
+ int64_t model_version,
+ const std::vector<float>& input_tensors,
+ const std::vector<float>& outputs,
+ const std::vector<int>& output_indexes,
+ absl::optional<proto::PredictionResult> prediction_result,
+ absl::optional<SelectedSegment> selected_segment);
// Helper method to encode a float number into int64.
static int64_t FloatToInt64(float f);
+ // Helper method to check if data is allowed to upload through ukm
+ // given a clock and the signal storage length.
+ static bool AllowedToUploadData(base::TimeDelta signal_storage_length,
+ base::Clock* clock);
+
+ // Gets a set of segment IDs that are allowed to upload metrics.
+ const base::flat_set<SegmentId>& allowed_segment_ids() {
+ return allowed_segment_ids_;
+ }
+
private:
bool AddInputsToUkm(ukm::builders::Segmentation_ModelExecution* ukm_builder,
- OptimizationTarget segment_id,
+ SegmentId segment_id,
int64_t model_version,
const std::vector<float>& input_tensor);
@@ -65,7 +89,7 @@ class SegmentationUkmHelper {
void Initialize();
- base::flat_set<int> allowed_segment_ids_;
+ base::flat_set<SegmentId> allowed_segment_ids_;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc b/chromium/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
index 923568a8378..faf59f9f21c 100644
--- a/chromium/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
@@ -9,22 +9,30 @@
#include "base/bit_cast.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/segmentation_platform/internal/constants.h"
+#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
#include "components/segmentation_platform/public/config.h"
#include "components/segmentation_platform/public/features.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
+#include "components/segmentation_platform/public/segmentation_platform_service.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
using Segmentation_ModelExecution = ukm::builders::Segmentation_ModelExecution;
+namespace segmentation_platform {
+
namespace {
// Round errors allowed during conversion.
static const double kRoundingError = 1E-5;
float Int64ToFloat(int64_t encoded) {
- return static_cast<float>(bit_cast<double>(encoded));
+ return static_cast<float>(base::bit_cast<double>(encoded));
}
void CompareEncodeDecodeDifference(float tensor) {
@@ -36,9 +44,13 @@ void CompareEncodeDecodeDifference(float tensor) {
kRoundingError);
}
-} // namespace
+absl::optional<proto::PredictionResult> GetPredictionResult() {
+ proto::PredictionResult result;
+ result.set_result(0.5);
+ return result;
+}
-namespace segmentation_platform {
+} // namespace
class SegmentationUkmHelperTest : public testing::Test {
public:
@@ -96,26 +108,24 @@ TEST_F(SegmentationUkmHelperTest, TestExecutionResultReporting) {
InitializeAllowedSegmentIds("4");
std::vector<float> input_tensors = {0.1, 0.7, 0.8, 0.5};
SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
- input_tensors, 0.6);
- ExpectUkmMetrics(
- Segmentation_ModelExecution::kEntryName,
- {Segmentation_ModelExecution::kOptimizationTargetName,
- Segmentation_ModelExecution::kModelVersionName,
- Segmentation_ModelExecution::kInput0Name,
- Segmentation_ModelExecution::kInput1Name,
- Segmentation_ModelExecution::kInput2Name,
- Segmentation_ModelExecution::kInput3Name,
- Segmentation_ModelExecution::kPredictionResultName},
- {
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- 101,
- SegmentationUkmHelper::FloatToInt64(0.1),
- SegmentationUkmHelper::FloatToInt64(0.7),
- SegmentationUkmHelper::FloatToInt64(0.8),
- SegmentationUkmHelper::FloatToInt64(0.5),
- SegmentationUkmHelper::FloatToInt64(0.6),
- });
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors, 0.6);
+ ExpectUkmMetrics(Segmentation_ModelExecution::kEntryName,
+ {Segmentation_ModelExecution::kOptimizationTargetName,
+ Segmentation_ModelExecution::kModelVersionName,
+ Segmentation_ModelExecution::kInput0Name,
+ Segmentation_ModelExecution::kInput1Name,
+ Segmentation_ModelExecution::kInput2Name,
+ Segmentation_ModelExecution::kInput3Name,
+ Segmentation_ModelExecution::kPredictionResultName},
+ {
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ 101,
+ SegmentationUkmHelper::FloatToInt64(0.1),
+ SegmentationUkmHelper::FloatToInt64(0.7),
+ SegmentationUkmHelper::FloatToInt64(0.8),
+ SegmentationUkmHelper::FloatToInt64(0.5),
+ SegmentationUkmHelper::FloatToInt64(0.6),
+ });
}
// Tests that the training data collection recording works properly.
@@ -126,23 +136,31 @@ TEST_F(SegmentationUkmHelperTest, TestTrainingDataCollectionReporting) {
std::vector<float> outputs = {1.0, 0.0};
std::vector<int> output_indexes = {2, 3};
+ SelectedSegment selected_segment(
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ selected_segment.selection_time = base::Time::Now() - base::Seconds(10);
SegmentationUkmHelper::GetInstance()->RecordTrainingData(
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
- input_tensors, outputs, output_indexes);
- ExpectUkmMetrics(
- Segmentation_ModelExecution::kEntryName,
- {Segmentation_ModelExecution::kOptimizationTargetName,
- Segmentation_ModelExecution::kModelVersionName,
- Segmentation_ModelExecution::kInput0Name,
- Segmentation_ModelExecution::kActualResult3Name,
- Segmentation_ModelExecution::kActualResult4Name},
- {
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- 101,
- SegmentationUkmHelper::FloatToInt64(0.1),
- SegmentationUkmHelper::FloatToInt64(1.0),
- SegmentationUkmHelper::FloatToInt64(0.0),
- });
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+ outputs, output_indexes, GetPredictionResult(), selected_segment);
+ ExpectUkmMetrics(Segmentation_ModelExecution::kEntryName,
+ {Segmentation_ModelExecution::kOptimizationTargetName,
+ Segmentation_ModelExecution::kModelVersionName,
+ Segmentation_ModelExecution::kInput0Name,
+ Segmentation_ModelExecution::kActualResult3Name,
+ Segmentation_ModelExecution::kActualResult4Name,
+ Segmentation_ModelExecution::kPredictionResultName,
+ Segmentation_ModelExecution::kSelectionResultName,
+ Segmentation_ModelExecution::kOutputDelaySecName},
+ {
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ 101,
+ SegmentationUkmHelper::FloatToInt64(0.1),
+ SegmentationUkmHelper::FloatToInt64(1.0),
+ SegmentationUkmHelper::FloatToInt64(0.0),
+ SegmentationUkmHelper::FloatToInt64(0.5),
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ 10,
+ });
}
// Tests that recording is disabled if kSegmentationStructuredMetricsFeature
@@ -151,8 +169,7 @@ TEST_F(SegmentationUkmHelperTest, TestDisabledStructuredMetrics) {
DisableStructureMetrics();
std::vector<float> input_tensors = {0.1, 0.7, 0.8, 0.5};
SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
- input_tensors, 0.6);
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors, 0.6);
ExpectEmptyUkmMetrics(Segmentation_ModelExecution::kEntryName);
}
@@ -162,8 +179,7 @@ TEST_F(SegmentationUkmHelperTest, TestNotAllowedSegmentId) {
InitializeAllowedSegmentIds("7, 8");
std::vector<float> input_tensors = {0.1, 0.7, 0.8, 0.5};
SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
- input_tensors, 0.6);
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors, 0.6);
ExpectEmptyUkmMetrics(Segmentation_ModelExecution::kEntryName);
}
@@ -198,8 +214,8 @@ TEST_F(SegmentationUkmHelperTest, TooManyInputTensors) {
std::vector<float> input_tensors(100, 0.1);
ukm::SourceId source_id =
SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- 101, input_tensors, 0.6);
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+ 0.6);
ASSERT_EQ(source_id, ukm::kInvalidSourceId);
tester.ExpectTotalCount(histogram_name, 1);
ASSERT_EQ(tester.GetTotalSum(histogram_name), 100);
@@ -216,23 +232,46 @@ TEST_F(SegmentationUkmHelperTest, OutputsValidation) {
ukm::SourceId source_id =
SegmentationUkmHelper::GetInstance()->RecordTrainingData(
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- 101, input_tensors, outputs, output_indexes);
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+ outputs, output_indexes, GetPredictionResult(), absl::nullopt);
ASSERT_EQ(source_id, ukm::kInvalidSourceId);
// output_indexes value too large.
output_indexes = {100, 1000};
source_id = SegmentationUkmHelper::GetInstance()->RecordTrainingData(
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
- input_tensors, outputs, output_indexes);
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+ outputs, output_indexes, GetPredictionResult(), absl::nullopt);
ASSERT_EQ(source_id, ukm::kInvalidSourceId);
// Valid outputs.
output_indexes = {3, 0};
source_id = SegmentationUkmHelper::GetInstance()->RecordTrainingData(
- optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
- input_tensors, outputs, output_indexes);
+ proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+ outputs, output_indexes, GetPredictionResult(), absl::nullopt);
ASSERT_NE(source_id, ukm::kInvalidSourceId);
}
+TEST_F(SegmentationUkmHelperTest, AllowedToUploadData) {
+ TestingPrefServiceSimple prefs;
+ SegmentationPlatformService::RegisterLocalStatePrefs(prefs.registry());
+ LocalStateHelper::GetInstance().Initialize(&prefs);
+ base::SimpleTestClock clock;
+ clock.SetNow(base::Time::Now());
+
+ // If pref is not initialized, AllowedToUploadData() always return false.
+ ASSERT_FALSE(
+ SegmentationUkmHelper::AllowedToUploadData(base::Seconds(1), &clock));
+
+ LocalStateHelper::GetInstance().SetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey, clock.Now());
+
+ ASSERT_FALSE(
+ SegmentationUkmHelper::AllowedToUploadData(base::Seconds(1), &clock));
+ clock.Advance(base::Seconds(10));
+ ASSERT_TRUE(
+ SegmentationUkmHelper::AllowedToUploadData(base::Seconds(1), &clock));
+ ASSERT_FALSE(
+ SegmentationUkmHelper::AllowedToUploadData(base::Seconds(11), &clock));
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/selection/experimental_group_recorder.cc b/chromium/components/segmentation_platform/internal/selection/experimental_group_recorder.cc
new file mode 100644
index 00000000000..f7078cf6fbd
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/selection/experimental_group_recorder.cc
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/internal/selection/experimental_group_recorder.h"
+
+#include "base/bind.h"
+#include "base/strings/strcat.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
+#include "components/segmentation_platform/internal/metric_filter_utils.h"
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/field_trial_register.h"
+
+#include "base/logging.h"
+
+namespace segmentation_platform {
+
+ExperimentalGroupRecorder::ExperimentalGroupRecorder(
+ SegmentResultProvider* result_provider,
+ FieldTrialRegister* field_trial_register,
+ const std::string& segmentation_key,
+ proto::SegmentId selected_segment)
+ : field_trial_register_(field_trial_register),
+ segmentation_key_(segmentation_key),
+ segment_id_(selected_segment) {
+ auto options = std::make_unique<SegmentResultProvider::GetResultOptions>();
+ options->segmentation_key =
+ base::StrCat({segmentation_key, kSubsegmentDiscreteMappingSuffix});
+ options->segment_id = selected_segment;
+ options->callback = base::BindOnce(&ExperimentalGroupRecorder::OnGetSegment,
+ weak_ptr_factory_.GetWeakPtr());
+ options->ignore_db_scores = false;
+ result_provider->GetSegmentResult(std::move(options));
+}
+
+ExperimentalGroupRecorder::~ExperimentalGroupRecorder() = default;
+
+void ExperimentalGroupRecorder::OnGetSegment(
+ std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
+ const std::string trial_name = stats::SegmentationKeyToSubsegmentTrialName(
+ segmentation_key_, segment_id_);
+ int rank = 0;
+ if (result && result->rank) {
+ rank = *result->rank;
+ }
+
+ // Can be nullptr in tests.
+ if (field_trial_register_) {
+ field_trial_register_->RegisterSubsegmentFieldTrialIfNeeded(
+ trial_name, segment_id_, rank);
+ }
+}
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/selection/experimental_group_recorder.h b/chromium/components/segmentation_platform/internal/selection/experimental_group_recorder.h
new file mode 100644
index 00000000000..f6c0f4d19d8
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/selection/experimental_group_recorder.h
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_EXPERIMENTAL_GROUP_RECORDER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_EXPERIMENTAL_GROUP_RECORDER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace segmentation_platform {
+
+class FieldTrialRegister;
+
+// Records experimental sub groups for the given optimization target.
+class ExperimentalGroupRecorder {
+ public:
+ // On construction, gets the model score from the database and records the
+ // subsegment based on the score. This class must be kept alive till the
+ // recording is complete, can be used only once.
+ ExperimentalGroupRecorder(SegmentResultProvider* result_provider,
+ FieldTrialRegister* field_trial_register,
+ const std::string& segmentation_key,
+ proto::SegmentId selected_segment);
+ ~ExperimentalGroupRecorder();
+
+ ExperimentalGroupRecorder(ExperimentalGroupRecorder&) = delete;
+ ExperimentalGroupRecorder& operator=(ExperimentalGroupRecorder&) = delete;
+
+ private:
+ void OnGetSegment(
+ std::unique_ptr<SegmentResultProvider::SegmentResult> result);
+
+ const raw_ptr<FieldTrialRegister> field_trial_register_;
+ const std::string segmentation_key_;
+ const proto::SegmentId segment_id_;
+
+ base::WeakPtrFactory<ExperimentalGroupRecorder> weak_ptr_factory_{this};
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_EXPERIMENTAL_GROUP_RECORDER_H_
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_result_provider.cc b/chromium/components/segmentation_platform/internal/selection/segment_result_provider.cc
index b66ad9bd3e3..5fad0dc313d 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_result_provider.cc
+++ b/chromium/components/segmentation_platform/internal/selection/segment_result_provider.cc
@@ -9,10 +9,11 @@
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/threading/sequenced_task_runner_handle.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
#include "components/segmentation_platform/internal/database/signal_storage_config.h"
#include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/execution/execution_request.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/scheduler/execution_service.h"
@@ -27,13 +28,26 @@ int ComputeDiscreteMapping(const std::string& segmentation_key,
segmentation_key, segment_info.prediction_result().result(),
segment_info.model_metadata());
VLOG(1) << __func__
- << ": segment=" << OptimizationTarget_Name(segment_info.segment_id())
+ << ": segment=" << SegmentId_Name(segment_info.segment_id())
<< ": result=" << segment_info.prediction_result().result()
<< ", rank=" << rank;
return rank;
}
+proto::SegmentInfo* FilterSegmentInfoBySource(
+ DefaultModelManager::SegmentInfoList& available_segments,
+ DefaultModelManager::SegmentSource needed_source) {
+ proto::SegmentInfo* segment_info = nullptr;
+ for (const auto& info : available_segments) {
+ if (info->segment_source == needed_source) {
+ segment_info = &info->segment_info;
+ break;
+ }
+ }
+ return segment_info;
+}
+
class SegmentResultProviderImpl : public SegmentResultProvider {
public:
SegmentResultProviderImpl(SegmentInfoDatabase* segment_database,
@@ -50,33 +64,45 @@ class SegmentResultProviderImpl : public SegmentResultProvider {
force_refresh_results_(force_refresh_results),
task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
- void GetSegmentResult(OptimizationTarget segment_id,
- const std::string& segmentation_key,
- SegmentResultCallback callback) override;
+ void GetSegmentResult(std::unique_ptr<GetResultOptions> options) override;
SegmentResultProviderImpl(SegmentResultProviderImpl&) = delete;
SegmentResultProviderImpl& operator=(SegmentResultProviderImpl&) = delete;
private:
struct RequestState {
- OptimizationTarget segment_id;
- SegmentResultCallback callback;
- raw_ptr<ModelProvider> default_provider;
- std::string segmentation_key;
+ std::unordered_map<DefaultModelManager::SegmentSource,
+ raw_ptr<ModelProvider>>
+ model_providers;
+ DefaultModelManager::SegmentInfoList available_segments;
+ std::unique_ptr<GetResultOptions> options;
};
void OnGetSegmentInfo(
- std::unique_ptr<RequestState> request_state,
+ std::unique_ptr<GetResultOptions> options,
DefaultModelManager::SegmentInfoList available_segments);
+ void OnGotDatabaseModelScore(std::unique_ptr<RequestState> request_state,
+ std::unique_ptr<SegmentResult> db_result);
+
void TryGetScoreFromDefaultModel(
std::unique_ptr<RequestState> request_state,
- SegmentResultProvider::ResultState existing_state,
- DefaultModelManager::SegmentInfoList available_segments);
- void OnDefaultModelExecuted(
- std::unique_ptr<RequestState> request_state,
- std::unique_ptr<proto::SegmentInfo> segment_info,
- const std::pair<float, ModelExecutionStatus>& result);
+ SegmentResultProvider::ResultState existing_state);
+
+ using ResultCallbackWithState =
+ base::OnceCallback<void(std::unique_ptr<RequestState>,
+ std::unique_ptr<SegmentResult>)>;
+
+ void GetCachedModelScore(std::unique_ptr<RequestState> request_state,
+ ResultCallbackWithState callback);
+ void ExecuteModelAndGetScore(std::unique_ptr<RequestState> request_state,
+ DefaultModelManager::SegmentSource source,
+ ResultCallbackWithState callback);
+
+ void OnModelExecuted(std::unique_ptr<RequestState> request_state,
+ DefaultModelManager::SegmentSource source,
+ ResultCallbackWithState callback,
+ const std::pair<float, ModelExecutionStatus>& result);
void PostResultCallback(std::unique_ptr<RequestState> request_state,
std::unique_ptr<SegmentResult> result);
@@ -93,160 +119,190 @@ class SegmentResultProviderImpl : public SegmentResultProvider {
};
void SegmentResultProviderImpl::GetSegmentResult(
- OptimizationTarget segment_id,
- const std::string& segmentation_key,
- SegmentResultCallback callback) {
+ std::unique_ptr<GetResultOptions> options) {
+ const SegmentId segment_id = options->segment_id;
+ default_model_manager_->GetAllSegmentInfoFromBothModels(
+ {segment_id}, segment_database_,
+ base::BindOnce(&SegmentResultProviderImpl::OnGetSegmentInfo,
+ weak_ptr_factory_.GetWeakPtr(), std::move(options)));
+}
+
+void SegmentResultProviderImpl::OnGetSegmentInfo(
+ std::unique_ptr<GetResultOptions> options,
+ DefaultModelManager::SegmentInfoList available_segments) {
+ const SegmentId segment_id = options->segment_id;
auto request_state = std::make_unique<RequestState>();
- request_state = std::make_unique<RequestState>();
- request_state->segment_id = segment_id;
- request_state->segmentation_key = segmentation_key;
- request_state->callback = std::move(callback);
- // Factory can be null in tests.
- request_state->default_provider =
+ request_state->options = std::move(options);
+ request_state->available_segments.swap(available_segments);
+ request_state->model_providers[DefaultModelManager::SegmentSource::DATABASE] =
+ execution_service_ ? execution_service_->GetModelProvider(segment_id)
+ : nullptr;
+ // Default manager can be null in tests.
+ request_state
+ ->model_providers[DefaultModelManager::SegmentSource::DEFAULT_MODEL] =
default_model_manager_
? default_model_manager_->GetDefaultProvider(segment_id)
: nullptr;
- default_model_manager_->GetAllSegmentInfoFromBothModels(
- {segment_id}, segment_database_,
- base::BindOnce(&SegmentResultProviderImpl::OnGetSegmentInfo,
- weak_ptr_factory_.GetWeakPtr(), std::move(request_state)));
+ auto db_score_callback =
+ base::BindOnce(&SegmentResultProviderImpl::OnGotDatabaseModelScore,
+ weak_ptr_factory_.GetWeakPtr());
+
+ if (request_state->options->ignore_db_scores) {
+ VLOG(1) << __func__ << ": segment="
+ << SegmentId_Name(request_state->options->segment_id)
+ << " executing model to get score";
+ ExecuteModelAndGetScore(std::move(request_state),
+ DefaultModelManager::SegmentSource::DATABASE,
+ std::move(db_score_callback));
+ return;
+ }
+
+ GetCachedModelScore(std::move(request_state), std::move(db_score_callback));
}
-void SegmentResultProviderImpl::OnGetSegmentInfo(
+void SegmentResultProviderImpl::OnGotDatabaseModelScore(
std::unique_ptr<RequestState> request_state,
- DefaultModelManager::SegmentInfoList available_segments) {
- const proto::SegmentInfo* db_segment_info = nullptr;
- for (const auto& info : available_segments) {
- DCHECK_EQ(request_state->segment_id, info->segment_info.segment_id());
- if (info->segment_source == DefaultModelManager::SegmentSource::DATABASE) {
- db_segment_info = &info->segment_info;
- break;
- }
+ std::unique_ptr<SegmentResult> db_result) {
+ if (db_result && db_result->rank.has_value()) {
+ PostResultCallback(std::move(request_state), std::move(db_result));
+ return;
}
+ VLOG(1) << __func__
+ << ": segment=" << SegmentId_Name(request_state->options->segment_id)
+ << " failed to get database model score, trying default model.";
+ TryGetScoreFromDefaultModel(std::move(request_state), db_result->state);
+}
- // Don't compute results if we don't have enough signals, or don't have
- // valid unexpired results for any of the segments.
- if (!db_segment_info) {
+void SegmentResultProviderImpl::TryGetScoreFromDefaultModel(
+ std::unique_ptr<RequestState> request_state,
+ SegmentResultProvider::ResultState existing_state) {
+ ModelProvider* default_model =
+ request_state
+ ->model_providers[DefaultModelManager::SegmentSource::DEFAULT_MODEL];
+ if (!default_model || !default_model->ModelAvailable()) {
VLOG(1) << __func__ << ": segment="
- << OptimizationTarget_Name(request_state->segment_id)
- << " does not have segment info.";
- TryGetScoreFromDefaultModel(std::move(request_state),
- ResultState::kSegmentNotAvailable,
- std::move(available_segments));
+ << SegmentId_Name(request_state->options->segment_id)
+ << " default provider not available";
+ // Make sure the metrics record state of database model failure when client
+ // did not provide a default model.
+ PostResultCallback(std::move(request_state),
+ std::make_unique<SegmentResult>(existing_state));
return;
}
+ ExecuteModelAndGetScore(
+ std::move(request_state),
+ DefaultModelManager::SegmentSource::DEFAULT_MODEL,
+ base::BindOnce(&SegmentResultProviderImpl::PostResultCallback,
+ weak_ptr_factory_.GetWeakPtr()));
+}
- // TODO(ssid): Remove this check since scheduler does this before executing
- // the model.
- if (!force_refresh_results_ &&
- !signal_storage_config_->MeetsSignalCollectionRequirement(
- db_segment_info->model_metadata())) {
+void SegmentResultProviderImpl::GetCachedModelScore(
+ std::unique_ptr<RequestState> request_state,
+ ResultCallbackWithState callback) {
+ const proto::SegmentInfo* db_segment_info =
+ FilterSegmentInfoBySource(request_state->available_segments,
+ DefaultModelManager::SegmentSource::DATABASE);
+
+ if (!db_segment_info) {
VLOG(1) << __func__ << ": segment="
- << OptimizationTarget_Name(db_segment_info->segment_id())
- << " does not meet signal collection requirements.";
- TryGetScoreFromDefaultModel(std::move(request_state),
- ResultState::kSignalsNotCollected,
- std::move(available_segments));
+ << SegmentId_Name(request_state->options->segment_id)
+ << " does not have a segment info.";
+ std::move(callback).Run(
+ std::move(request_state),
+ std::make_unique<SegmentResult>(ResultState::kSegmentNotAvailable));
return;
}
if (metadata_utils::HasExpiredOrUnavailableResult(*db_segment_info,
clock_->Now())) {
VLOG(1) << __func__ << ": segment="
- << OptimizationTarget_Name(db_segment_info->segment_id())
+ << SegmentId_Name(request_state->options->segment_id)
<< " has expired or unavailable result.";
- TryGetScoreFromDefaultModel(std::move(request_state),
- ResultState::kDatabaseScoreNotReady,
- std::move(available_segments));
+ std::move(callback).Run(
+ std::move(request_state),
+ std::make_unique<SegmentResult>(ResultState::kDatabaseScoreNotReady));
return;
}
- int rank =
- ComputeDiscreteMapping(request_state->segmentation_key, *db_segment_info);
- PostResultCallback(
+ int rank = ComputeDiscreteMapping(request_state->options->segmentation_key,
+ *db_segment_info);
+ std::move(callback).Run(
std::move(request_state),
std::make_unique<SegmentResult>(ResultState::kSuccessFromDatabase, rank));
}
-void SegmentResultProviderImpl::TryGetScoreFromDefaultModel(
+void SegmentResultProviderImpl::ExecuteModelAndGetScore(
std::unique_ptr<RequestState> request_state,
- SegmentResultProvider::ResultState existing_state,
- DefaultModelManager::SegmentInfoList available_segments) {
- if (!request_state->default_provider ||
- !request_state->default_provider->ModelAvailable()) {
- VLOG(1) << __func__ << ": segment="
- << OptimizationTarget_Name(request_state->segment_id)
- << " default provider not available";
- PostResultCallback(std::move(request_state),
- std::make_unique<SegmentResult>(existing_state));
- return;
- }
-
- std::unique_ptr<proto::SegmentInfo> default_segment_info;
- for (auto& info : available_segments) {
- DCHECK_EQ(request_state->segment_id, info->segment_info.segment_id());
- if (info->segment_source ==
- DefaultModelManager::SegmentSource::DEFAULT_MODEL) {
- default_segment_info = std::make_unique<proto::SegmentInfo>();
- default_segment_info->Swap(&info->segment_info);
- break;
- }
- }
-
- if (!default_segment_info) {
+ DefaultModelManager::SegmentSource source,
+ ResultCallbackWithState callback) {
+ const proto::SegmentInfo* segment_info =
+ FilterSegmentInfoBySource(request_state->available_segments, source);
+ if (!segment_info) {
VLOG(1) << __func__ << ": segment="
- << OptimizationTarget_Name(request_state->segment_id)
+ << SegmentId_Name(request_state->options->segment_id)
<< " default segment info not available";
- PostResultCallback(std::move(request_state),
- std::make_unique<SegmentResult>(
- ResultState::kDefaultModelMetadataMissing));
+ auto state = source == DefaultModelManager::SegmentSource::DATABASE
+ ? ResultState::kSegmentNotAvailable
+ : ResultState::kDefaultModelMetadataMissing;
+ std::move(callback).Run(std::move(request_state),
+ std::make_unique<SegmentResult>(state));
return;
}
- DCHECK_EQ(
- metadata_utils::ValidationResult::kValidationSuccess,
- metadata_utils::ValidateMetadata(default_segment_info->model_metadata()));
- if (!signal_storage_config_->MeetsSignalCollectionRequirement(
- default_segment_info->model_metadata())) {
+ DCHECK_EQ(metadata_utils::ValidationResult::kValidationSuccess,
+ metadata_utils::ValidateMetadata(segment_info->model_metadata()));
+ if (!force_refresh_results_ &&
+ !signal_storage_config_->MeetsSignalCollectionRequirement(
+ segment_info->model_metadata())) {
VLOG(1) << __func__ << ": segment="
- << OptimizationTarget_Name(request_state->segment_id)
+ << SegmentId_Name(request_state->options->segment_id)
<< " signal collection not met";
- PostResultCallback(std::move(request_state),
- std::make_unique<SegmentResult>(
- ResultState::kDefaultModelSignalNotCollected));
+ auto state = source == DefaultModelManager::SegmentSource::DATABASE
+ ? ResultState::kSignalsNotCollected
+ : ResultState::kDefaultModelSignalNotCollected;
+ std::move(callback).Run(std::move(request_state),
+ std::make_unique<SegmentResult>(state));
return;
}
- ModelProvider* default_provider = request_state->default_provider;
- DCHECK(default_provider);
- auto request = std::make_unique<ExecutionService::ExecutionRequest>();
- // The pointer is kept alive by the unique_ptr in the callback.
- request->segment_info = default_segment_info.get();
+ ModelProvider* provider = request_state->model_providers[source];
+ auto request = std::make_unique<ExecutionRequest>();
+ // The pointer is kept alive by the `request_state`.
+ request->segment_info = segment_info;
request->record_metrics_for_default = true;
request->callback =
- base::BindOnce(&SegmentResultProviderImpl::OnDefaultModelExecuted,
+ base::BindOnce(&SegmentResultProviderImpl::OnModelExecuted,
weak_ptr_factory_.GetWeakPtr(), std::move(request_state),
- std::move(default_segment_info));
- request->model_provider = default_provider;
+ source, std::move(callback));
+ request->model_provider = provider;
execution_service_->RequestModelExecution(std::move(request));
}
-void SegmentResultProviderImpl::OnDefaultModelExecuted(
+void SegmentResultProviderImpl::OnModelExecuted(
std::unique_ptr<RequestState> request_state,
- std::unique_ptr<proto::SegmentInfo> segment_info,
+ DefaultModelManager::SegmentSource source,
+ ResultCallbackWithState callback,
const std::pair<float, ModelExecutionStatus>& result) {
+ auto* segment_info =
+ FilterSegmentInfoBySource(request_state->available_segments, source);
if (result.second == ModelExecutionStatus::kSuccess) {
segment_info->mutable_prediction_result()->set_result(result.first);
- int rank =
- ComputeDiscreteMapping(request_state->segmentation_key, *segment_info);
- PostResultCallback(std::move(request_state),
- std::make_unique<SegmentResult>(
- ResultState::kDefaultModelScoreUsed, rank));
+ int rank = ComputeDiscreteMapping(request_state->options->segmentation_key,
+ *segment_info);
+ ResultState state =
+ source == DefaultModelManager::SegmentSource::DEFAULT_MODEL
+ ? ResultState::kDefaultModelScoreUsed
+ : ResultState::kTfliteModelScoreUsed;
+ std::move(callback).Run(std::move(request_state),
+ std::make_unique<SegmentResult>(state, rank));
} else {
- PostResultCallback(std::move(request_state),
- std::make_unique<SegmentResult>(
- ResultState::kDefaultModelExecutionFailed));
+ ResultState state =
+ source == DefaultModelManager::SegmentSource::DEFAULT_MODEL
+ ? ResultState::kDefaultModelExecutionFailed
+ : ResultState::kTfliteModelExecutionFailed;
+ std::move(callback).Run(std::move(request_state),
+ std::make_unique<SegmentResult>(state));
}
}
@@ -254,8 +310,8 @@ void SegmentResultProviderImpl::PostResultCallback(
std::unique_ptr<RequestState> request_state,
std::unique_ptr<SegmentResult> result) {
task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(request_state->callback), std::move(result)));
+ FROM_HERE, base::BindOnce(std::move(request_state->options->callback),
+ std::move(result)));
}
} // namespace
@@ -266,6 +322,9 @@ SegmentResultProvider::SegmentResult::SegmentResult(ResultState state, int rank)
: state(state), rank(rank) {}
SegmentResultProvider::SegmentResult::~SegmentResult() = default;
+SegmentResultProvider::GetResultOptions::GetResultOptions() = default;
+SegmentResultProvider::GetResultOptions::~GetResultOptions() = default;
+
// static
std::unique_ptr<SegmentResultProvider> SegmentResultProvider::Create(
SegmentInfoDatabase* segment_database,
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_result_provider.h b/chromium/components/segmentation_platform/internal/selection/segment_result_provider.h
index fa3bd4b6dfe..e2b931aa485 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_result_provider.h
+++ b/chromium/components/segmentation_platform/internal/selection/segment_result_provider.h
@@ -6,8 +6,10 @@
#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_RESULT_PROVIDER_H_
#include "base/callback.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "base/memory/scoped_refptr.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/input_context.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base {
@@ -20,6 +22,12 @@ class ExecutionService;
class SignalStorageConfig;
// Used for retrieving the result of a particular model.
+// Supports 3 use cases:
+// 1. Fetching cached and valid results from the segment database.
+// 2. Fallback to default model when cached results are missing. Executes the
+// default model and provides the result.
+// 3. Execute the TFLite model and provide the result when `ignore_db_scores`
+// is set.
class SegmentResultProvider {
public:
SegmentResultProvider() = default;
@@ -35,6 +43,8 @@ class SegmentResultProvider {
kDefaultModelMetadataMissing = 6,
kDefaultModelExecutionFailed = 7,
kDefaultModelScoreUsed = 8,
+ kTfliteModelExecutionFailed = 9,
+ kTfliteModelScoreUsed = 10,
};
struct SegmentResult {
explicit SegmentResult(ResultState state);
@@ -58,10 +68,36 @@ class SegmentResultProvider {
base::Clock* clock,
bool force_refresh_results);
+ // Options for `GetSegmentResult()`.
+ struct GetResultOptions {
+ GetResultOptions();
+ ~GetResultOptions();
+
+ // The segment ID to fetch result for.
+ SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
+
+ // The key is needed for computing segment from discrete mapping.
+ std::string segmentation_key;
+
+ // Ignores model results stored in database and executes them to fetch
+ // results. When set to false, the result could be from following:
+ // * Score cached in the database
+ // * Execution of default model when score is missing.
+ // When set to true, the result could be from following:
+ // * Execution of TFLite model.
+ // * TODO(ssid): Support fallback to default when model is missing.
+ bool ignore_db_scores = false;
+
+ // Callback to return the segment result.
+ SegmentResultCallback callback;
+
+ // Current context of the browser that is needed by feature processor for
+ // some of the models.
+ scoped_refptr<InputContext> input_context;
+ };
+
// Returns latest available score for the segment.
- virtual void GetSegmentResult(OptimizationTarget segment_id,
- const std::string& segmentation_key,
- SegmentResultCallback callback) = 0;
+ virtual void GetSegmentResult(std::unique_ptr<GetResultOptions> options) = 0;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc b/chromium/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
index 1bf1b07dcef..3987aa2bfcd 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
@@ -4,20 +4,24 @@
#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+#include "base/memory/raw_ptr.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
+#include "components/prefs/testing_pref_service.h"
#include "components/segmentation_platform/internal/database/mock_signal_database.h"
#include "components/segmentation_platform/internal/database/mock_signal_storage_config.h"
#include "components/segmentation_platform/internal/database/test_segment_info_database.h"
#include "components/segmentation_platform/internal/execution/default_model_manager.h"
-#include "components/segmentation_platform/internal/execution/mock_feature_list_query_processor.h"
#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
#include "components/segmentation_platform/internal/execution/model_executor_impl.h"
+#include "components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h"
#include "components/segmentation_platform/internal/platform_options.h"
#include "components/segmentation_platform/internal/scheduler/execution_service.h"
#include "components/segmentation_platform/internal/signals/signal_handler.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
#include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/segmentation_platform_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -29,38 +33,42 @@ using ::testing::_;
using ::testing::ByMove;
using ::testing::Return;
-const OptimizationTarget kTestSegment =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
-const OptimizationTarget kTestSegment2 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
+const SegmentId kTestSegment =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+const SegmentId kTestSegment2 =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
-constexpr float kDefaultScore = 0.1;
-constexpr float kModelScore = 0.6;
-constexpr int kDefaultRank = 0;
-constexpr int kModelRank = 1;
+constexpr float kTestScore = 0.1;
+constexpr float kDatabaseScore = 0.6;
+constexpr int kTestRank = 0;
+constexpr int kDatabaseRank = 1;
-class DefaultProvider : public ModelProvider {
+class TestModelProvider : public ModelProvider {
public:
static constexpr int64_t kVersion = 10;
- explicit DefaultProvider(OptimizationTarget segment)
- : ModelProvider(segment) {}
+ explicit TestModelProvider(SegmentId segment) : ModelProvider(segment) {}
void InitAndFetchModel(
const ModelUpdatedCallback& model_updated_callback) override {
proto::SegmentationModelMetadata metadata;
metadata.set_time_unit(proto::TimeUnit::DAY);
- model_updated_callback.Run(optimization_target_, metadata, kVersion);
+ model_updated_callback.Run(segment_id_, metadata, kVersion);
}
void ExecuteModelWithInput(const std::vector<float>& inputs,
ExecutionCallback callback) override {
- std::move(callback).Run(kDefaultScore);
+ std::move(callback).Run(kTestScore);
}
// Returns true if a model is available.
bool ModelAvailable() override { return true; }
};
+class MockModelExecutionManager : public ModelExecutionManager {
+ public:
+ MOCK_METHOD(ModelProvider*, GetProvider, (proto::SegmentId segment_id));
+};
+
} // namespace
class SegmentResultProviderTest : public testing::Test {
@@ -71,19 +79,25 @@ class SegmentResultProviderTest : public testing::Test {
void SetUp() override {
default_manager_ = std::make_unique<DefaultModelManager>(
&provider_factory_,
- std::vector<OptimizationTarget>({kTestSegment, kTestSegment2}));
+ std::vector<SegmentId>({kTestSegment, kTestSegment2}));
segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
execution_service_ = std::make_unique<ExecutionService>();
- auto query_processor = std::make_unique<MockFeatureListQueryProcessor>();
+ auto query_processor =
+ std::make_unique<processing::MockFeatureListQueryProcessor>();
mock_query_processor_ = query_processor.get();
+ auto moved_execution_manager =
+ std::make_unique<MockModelExecutionManager>();
+ mock_execution_manager_ = moved_execution_manager.get();
execution_service_->InitForTesting(
std::move(query_processor),
std::make_unique<ModelExecutorImpl>(&clock_, mock_query_processor_),
- nullptr);
+ nullptr, std::move(moved_execution_manager));
score_provider_ = SegmentResultProvider::Create(
segment_database_.get(), &signal_storage_config_,
default_manager_.get(), execution_service_.get(), &clock_,
/*force_refresh_results=*/false);
+ SegmentationPlatformService::RegisterLocalStatePrefs(prefs_.registry());
+ LocalStateHelper::GetInstance().Initialize(&prefs_);
}
void TearDown() override {
@@ -93,36 +107,40 @@ class SegmentResultProviderTest : public testing::Test {
}
void ExpectSegmentResultOnGet(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
+ bool ignore_db_scores,
SegmentResultProvider::ResultState expected_state,
absl::optional<int> expected_rank) {
base::RunLoop wait_for_result;
- score_provider_->GetSegmentResult(
- segment_id, "test_key",
- base::BindOnce(
- [](SegmentResultProvider::ResultState expected_state,
- absl::optional<int> expected_rank, base::OnceClosure quit,
- std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
- EXPECT_EQ(result->state, expected_state);
- if (expected_rank) {
- EXPECT_EQ(*expected_rank, result->rank);
- } else {
- EXPECT_FALSE(result->rank);
- }
- std::move(quit).Run();
- },
- expected_state, expected_rank, wait_for_result.QuitClosure()));
+ auto options = std::make_unique<SegmentResultProvider::GetResultOptions>();
+ options->segment_id = segment_id;
+ options->segmentation_key = "test_key";
+ options->ignore_db_scores = ignore_db_scores;
+ options->callback = base::BindOnce(
+ [](SegmentResultProvider::ResultState expected_state,
+ absl::optional<int> expected_rank, base::OnceClosure quit,
+ std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
+ EXPECT_EQ(result->state, expected_state);
+ if (expected_rank) {
+ EXPECT_EQ(*expected_rank, result->rank);
+ } else {
+ EXPECT_FALSE(result->rank);
+ }
+ std::move(quit).Run();
+ },
+ expected_state, expected_rank, wait_for_result.QuitClosure());
+ score_provider_->GetSegmentResult(std::move(options));
wait_for_result.Run();
}
- void SetSegmentResult(OptimizationTarget segment,
- absl::optional<float> score) {
+ void SetSegmentResult(SegmentId segment, absl::optional<float> score) {
absl::optional<proto::PredictionResult> result;
if (score) {
result = proto::PredictionResult();
result->set_result(*score);
}
base::RunLoop wait_for_save;
+ segment_database_->SetBucketDuration(segment, 1, proto::TimeUnit::DAY);
segment_database_->SaveSegmentResult(
segment, std::move(result),
base::BindOnce(
@@ -131,7 +149,7 @@ class SegmentResultProviderTest : public testing::Test {
wait_for_save.Run();
}
- void InitializeMetadata(OptimizationTarget segment_id) {
+ void InitializeMetadata(SegmentId segment_id) {
segment_database_->FindOrCreateSegment(segment_id)
->mutable_model_metadata()
->set_result_time_to_live(7);
@@ -139,8 +157,8 @@ class SegmentResultProviderTest : public testing::Test {
// Initialize metadata so that score from default model returns default rank
// and score from model score returns model rank.
- float mapping[][2] = {{kDefaultScore + 0.1, kDefaultRank},
- {kModelScore - 0.1, kModelRank}};
+ float mapping[][2] = {{kTestScore + 0.1, kTestRank},
+ {kDatabaseScore - 0.1, kDatabaseRank}};
segment_database_->AddDiscreteMapping(segment_id, mapping, 2, "test_key");
}
@@ -150,7 +168,9 @@ class SegmentResultProviderTest : public testing::Test {
TestModelProviderFactory::Data model_providers_;
TestModelProviderFactory provider_factory_;
MockSignalDatabase signal_database_;
- MockFeatureListQueryProcessor* mock_query_processor_ = nullptr;
+ raw_ptr<processing::MockFeatureListQueryProcessor> mock_query_processor_ =
+ nullptr;
+ raw_ptr<MockModelExecutionManager> mock_execution_manager_;
SignalHandler signal_handler_;
std::unique_ptr<DefaultModelManager> default_manager_;
std::unique_ptr<ExecutionService> execution_service_;
@@ -158,128 +178,189 @@ class SegmentResultProviderTest : public testing::Test {
std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_;
MockSignalStorageConfig signal_storage_config_;
std::unique_ptr<SegmentResultProvider> score_provider_;
+ TestingPrefServiceSimple prefs_;
};
TEST_F(SegmentResultProviderTest, GetScoreWithoutInfo) {
ExpectSegmentResultOnGet(
- kTestSegment, SegmentResultProvider::ResultState::kSegmentNotAvailable,
- absl::nullopt);
+ kTestSegment, /*ignore_db_scores=*/false,
+ SegmentResultProvider::ResultState::kSegmentNotAvailable, absl::nullopt);
}
TEST_F(SegmentResultProviderTest, GetScoreFromDbWithoutResult) {
SetSegmentResult(kTestSegment, absl::nullopt);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
- .WillOnce(Return(true));
ExpectSegmentResultOnGet(
- kTestSegment, SegmentResultProvider::ResultState::kDatabaseScoreNotReady,
+ kTestSegment, /*ignore_db_scores=*/false,
+ SegmentResultProvider::ResultState::kDatabaseScoreNotReady,
absl::nullopt);
}
-TEST_F(SegmentResultProviderTest, GetScoreNotEnoughSignals) {
- SetSegmentResult(kTestSegment, absl::nullopt);
+TEST_F(SegmentResultProviderTest, GetScoreFromDb) {
+ InitializeMetadata(kTestSegment);
+ SetSegmentResult(kTestSegment, kDatabaseScore);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ ExpectSegmentResultOnGet(
+ kTestSegment, /*ignore_db_scores=*/false,
+ SegmentResultProvider::ResultState::kSuccessFromDatabase, kDatabaseRank);
+}
+
+TEST_F(SegmentResultProviderTest, GetFromModelNotEnoughSignals) {
+ SetSegmentResult(kTestSegment, absl::nullopt);
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillOnce(Return(false));
ExpectSegmentResultOnGet(
- kTestSegment, SegmentResultProvider::ResultState::kSignalsNotCollected,
+ kTestSegment, /*ignore_db_scores=*/true,
+ SegmentResultProvider::ResultState::kSignalsNotCollected, absl::nullopt);
+}
+
+TEST_F(SegmentResultProviderTest, GetFromModelExecutionFailed) {
+ InitializeMetadata(kTestSegment);
+ SetSegmentResult(kTestSegment, kDatabaseScore);
+
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
+ .WillRepeatedly(Return(true));
+
+ // No model available to execute.
+ EXPECT_CALL(*mock_execution_manager_, GetProvider(kTestSegment))
+ .WillOnce(Return(nullptr));
+ ExpectSegmentResultOnGet(
+ kTestSegment, /*ignore_db_scores=*/true,
+ SegmentResultProvider::ResultState::kTfliteModelExecutionFailed,
+ absl::nullopt);
+
+ // Feature processing failed.
+ TestModelProvider provider(kTestSegment);
+ EXPECT_CALL(*mock_execution_manager_, GetProvider(kTestSegment))
+ .WillOnce(Return(&provider));
+ EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
+ .WillOnce(RunOnceCallback<5>(/*error=*/true, std::vector<float>{{1, 2}},
+ std::vector<float>()));
+ ExpectSegmentResultOnGet(
+ kTestSegment, /*ignore_db_scores=*/true,
+ SegmentResultProvider::ResultState::kTfliteModelExecutionFailed,
absl::nullopt);
}
-TEST_F(SegmentResultProviderTest, GetScoreFromDb) {
+TEST_F(SegmentResultProviderTest, GetFromModel) {
InitializeMetadata(kTestSegment);
- SetSegmentResult(kTestSegment, kModelScore);
+ SetSegmentResult(kTestSegment, kDatabaseScore);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillOnce(Return(true));
+
+ TestModelProvider provider(kTestSegment);
+ EXPECT_CALL(*mock_execution_manager_, GetProvider(kTestSegment))
+ .WillOnce(Return(&provider));
+ EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
+ .WillOnce(RunOnceCallback<5>(/*error=*/false, std::vector<float>{{1, 2}},
+ std::vector<float>()));
+
+ // Gets the rank from test model instead of database.
ExpectSegmentResultOnGet(
- kTestSegment, SegmentResultProvider::ResultState::kSuccessFromDatabase,
- kModelRank);
+ kTestSegment, /*ignore_db_scores=*/true,
+ SegmentResultProvider::ResultState::kTfliteModelScoreUsed, kTestRank);
}
-TEST_F(SegmentResultProviderTest, DefaultNeedsSignal) {
+TEST_F(SegmentResultProviderTest, DefaultNeedsSignalIgnoringDbScore) {
SetSegmentResult(kTestSegment, absl::nullopt);
- std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
- p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+ std::map<SegmentId, std::unique_ptr<ModelProvider>> p;
+ p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
default_manager_->SetDefaultProvidersForTesting(std::move(p));
// First call is to check opt guide model, and second is to check default
// model signals.
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillOnce(Return(true))
.WillOnce(Return(false));
ExpectSegmentResultOnGet(
kTestSegment,
+ /*ignore_db_scores=*/true,
SegmentResultProvider::ResultState::kDefaultModelSignalNotCollected,
absl::nullopt);
}
TEST_F(SegmentResultProviderTest, DefaultModelFailedExecution) {
SetSegmentResult(kTestSegment, absl::nullopt);
- std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
- p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+ std::map<SegmentId, std::unique_ptr<ModelProvider>> p;
+ p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
default_manager_->SetDefaultProvidersForTesting(std::move(p));
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
- .WillOnce(Return(true))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillOnce(Return(true));
// Set error while computing features.
- EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _))
- .WillOnce(RunOnceCallback<3>(/*error=*/true, std::vector<float>{{1, 2}}));
+ EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
+ .WillOnce(RunOnceCallback<5>(/*error=*/true, std::vector<float>{{1, 2}},
+ std::vector<float>()));
ExpectSegmentResultOnGet(
kTestSegment,
+ /*ignore_db_scores=*/false,
SegmentResultProvider::ResultState::kDefaultModelExecutionFailed,
absl::nullopt);
}
TEST_F(SegmentResultProviderTest, GetFromDefault) {
SetSegmentResult(kTestSegment, absl::nullopt);
- std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
- p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+ std::map<SegmentId, std::unique_ptr<ModelProvider>> p;
+ p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
default_manager_->SetDefaultProvidersForTesting(std::move(p));
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
+ .WillOnce(RunOnceCallback<5>(/*error=*/false, std::vector<float>{{1, 2}},
+ std::vector<float>()));
+ ExpectSegmentResultOnGet(
+ kTestSegment, /*ignore_db_scores=*/false,
+ SegmentResultProvider::ResultState::kDefaultModelScoreUsed, kTestRank);
+}
+
+TEST_F(SegmentResultProviderTest, GetFromDefaultIgnoringDb) {
+ SetSegmentResult(kTestSegment, absl::nullopt);
+ std::map<SegmentId, std::unique_ptr<ModelProvider>> p;
+ p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
+ default_manager_->SetDefaultProvidersForTesting(std::move(p));
+
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillOnce(Return(true))
.WillOnce(Return(true));
- EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _))
- .WillOnce(
- RunOnceCallback<3>(/*error=*/false, std::vector<float>{{1, 2}}));
+ EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
+ .WillOnce(RunOnceCallback<5>(/*error=*/false, std::vector<float>{{1, 2}},
+ std::vector<float>()));
ExpectSegmentResultOnGet(
- kTestSegment, SegmentResultProvider::ResultState::kDefaultModelScoreUsed,
- kDefaultRank);
+ kTestSegment, /*ignore_db_scores=*/true,
+ SegmentResultProvider::ResultState::kDefaultModelScoreUsed, kTestRank);
}
TEST_F(SegmentResultProviderTest, MultipleRequests) {
InitializeMetadata(kTestSegment);
SetSegmentResult(kTestSegment, absl::nullopt);
InitializeMetadata(kTestSegment2);
- SetSegmentResult(kTestSegment2, kModelScore);
+ SetSegmentResult(kTestSegment2, kDatabaseScore);
- std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
- p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
- p.emplace(kTestSegment2, std::make_unique<DefaultProvider>(kTestSegment2));
+ std::map<SegmentId, std::unique_ptr<ModelProvider>> p;
+ p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
+ p.emplace(kTestSegment2, std::make_unique<TestModelProvider>(kTestSegment2));
default_manager_->SetDefaultProvidersForTesting(std::move(p));
// For the first request, the database does not have valid result, and default
// provider fails execution.
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
- .WillOnce(Return(true))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillOnce(Return(true));
- EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _))
- .WillOnce(
- RunOnceCallback<3>(/*error=*/false, std::vector<float>{{1, 2}}));
+ EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
+ .WillOnce(RunOnceCallback<5>(/*error=*/false, std::vector<float>{{1, 2}},
+ std::vector<float>()));
ExpectSegmentResultOnGet(
- kTestSegment, SegmentResultProvider::ResultState::kDefaultModelScoreUsed,
- kDefaultRank);
+ kTestSegment, /*ignore_db_scores=*/false,
+ SegmentResultProvider::ResultState::kDefaultModelScoreUsed, kTestRank);
// For the second request the database has valid result.
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
- .WillOnce(Return(true));
- EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
+ .Times(0);
ExpectSegmentResultOnGet(
- kTestSegment2, SegmentResultProvider::ResultState::kSuccessFromDatabase,
- kModelRank);
+ kTestSegment2, /*ignore_db_scores=*/false,
+ SegmentResultProvider::ResultState::kSuccessFromDatabase, kDatabaseRank);
}
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_score_provider.cc b/chromium/components/segmentation_platform/internal/selection/segment_score_provider.cc
index eda9084bea0..df50bac4a9d 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_score_provider.cc
+++ b/chromium/components/segmentation_platform/internal/selection/segment_score_provider.cc
@@ -8,8 +8,8 @@
#include "base/memory/raw_ptr.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
@@ -30,7 +30,7 @@ class SegmentScoreProviderImpl : public SegmentScoreProvider {
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
- void GetSegmentScore(OptimizationTarget segment_id,
+ void GetSegmentScore(SegmentId segment_id,
SegmentScoreCallback callback) override {
DCHECK(initialized_);
@@ -49,7 +49,7 @@ class SegmentScoreProviderImpl : public SegmentScoreProvider {
std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments) {
// Read results from last session to memory.
for (const auto& pair : *all_segments) {
- OptimizationTarget id = pair.first;
+ SegmentId id = pair.first;
const proto::SegmentInfo& info = pair.second;
if (!info.has_prediction_result())
continue;
@@ -67,7 +67,7 @@ class SegmentScoreProviderImpl : public SegmentScoreProvider {
// Model scores that are read from db on startup and used for serving the
// clients in the current session.
- std::map<OptimizationTarget, float> scores_last_session_;
+ std::map<SegmentId, float> scores_last_session_;
// Whether the initialization is complete through an Initialize call.
bool initialized_{false};
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_score_provider.h b/chromium/components/segmentation_platform/internal/selection/segment_score_provider.h
index dc694122d6b..792809d4c12 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_score_provider.h
+++ b/chromium/components/segmentation_platform/internal/selection/segment_score_provider.h
@@ -6,13 +6,13 @@
#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_SCORE_PROVIDER_H_
#include "base/callback.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
+using proto::SegmentId;
+
class SegmentInfoDatabase;
// Result of a single segment.
@@ -50,7 +50,7 @@ class SegmentScoreProvider {
// from the last session.
// Note that there is no strong reason to keep this async, feel free to change
// this to sync if needed.
- virtual void GetSegmentScore(OptimizationTarget segment_id,
+ virtual void GetSegmentScore(SegmentId segment_id,
SegmentScoreCallback callback) = 0;
};
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_score_provider_unittest.cc b/chromium/components/segmentation_platform/internal/selection/segment_score_provider_unittest.cc
index 8fa007340b5..384ecc949ed 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_score_provider_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/selection/segment_score_provider_unittest.cc
@@ -29,7 +29,7 @@ class SegmentScoreProviderTest : public testing::Test {
SegmentScoreProvider::Create(segment_database_.get());
}
- void InitializeMetadataForSegment(OptimizationTarget segment_id,
+ void InitializeMetadataForSegment(SegmentId segment_id,
float mapping[][2],
int num_mapping_pairs) {
auto* metadata = segment_database_->FindOrCreateSegment(segment_id)
@@ -43,8 +43,7 @@ class SegmentScoreProviderTest : public testing::Test {
segment_id, mapping, num_mapping_pairs, default_mapping_key);
}
- void GetSegmentScore(OptimizationTarget segment_id,
- const SegmentScore& expected) {
+ void GetSegmentScore(SegmentId segment_id, const SegmentScore& expected) {
base::RunLoop loop;
single_segment_manager_->GetSegmentScore(
segment_id,
@@ -66,8 +65,7 @@ class SegmentScoreProviderTest : public testing::Test {
};
TEST_F(SegmentScoreProviderTest, GetSegmentScore) {
- OptimizationTarget segment_id1 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ SegmentId segment_id1 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
InitializeMetadataForSegment(segment_id1, mapping1, 3);
segment_database_->AddPredictionResult(segment_id1, 0.6, base::Time::Now());
@@ -86,7 +84,7 @@ TEST_F(SegmentScoreProviderTest, GetSegmentScore) {
GetSegmentScore(segment_id1, expected);
// Returns empty results when called on a segment with no scores.
- GetSegmentScore(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
+ GetSegmentScore(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
SegmentScore());
}
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_selector.h b/chromium/components/segmentation_platform/internal/selection/segment_selector.h
index eebbc04919b..a601823ad8a 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_selector.h
+++ b/chromium/components/segmentation_platform/internal/selection/segment_selector.h
@@ -6,14 +6,17 @@
#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_SELECTOR_H_
#include "base/callback.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "base/memory/scoped_refptr.h"
#include "components/segmentation_platform/internal/execution/model_execution_status.h"
#include "components/segmentation_platform/internal/scheduler/model_execution_scheduler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
+
+using proto::SegmentId;
+
+struct InputContext;
struct SegmentSelectionResult;
class ExecutionService;
@@ -36,6 +39,12 @@ class SegmentSelector : public ModelExecutionScheduler::Observer {
// asynchronously. If none, returns empty result.
virtual void GetSelectedSegment(SegmentSelectionCallback callback) = 0;
+ // Client API. Runs models and selects a segment on demand. Returns empty
+ // result on failure.
+ virtual void GetSelectedSegmentOnDemand(
+ scoped_refptr<InputContext> input_context,
+ SegmentSelectionCallback callback) = 0;
+
// Client API. Returns the cached selected segment from the last session
// synchronously.
virtual SegmentSelectionResult GetCachedSegmentResult() = 0;
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/chromium/components/segmentation_platform/internal/selection/segment_selector_impl.cc
index e52ffc4cd9b..8d9a850745a 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_selector_impl.cc
+++ b/chromium/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -8,20 +8,24 @@
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/logging.h"
+#include "base/strings/strcat.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
#include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
+#include "components/segmentation_platform/internal/metric_filter_utils.h"
#include "components/segmentation_platform/internal/platform_options.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/internal/selection/experimental_group_recorder.h"
#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
#include "components/segmentation_platform/internal/stats.h"
#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/field_trial_register.h"
#include "components/segmentation_platform/public/model_provider.h"
namespace segmentation_platform {
@@ -33,6 +37,7 @@ stats::SegmentationSelectionFailureReason GetFailureReason(
case SegmentResultProvider::ResultState::kUnknown:
case SegmentResultProvider::ResultState::kSuccessFromDatabase:
case SegmentResultProvider::ResultState::kDefaultModelScoreUsed:
+ case SegmentResultProvider::ResultState::kTfliteModelScoreUsed:
NOTREACHED();
return stats::SegmentationSelectionFailureReason::kMaxValue;
case SegmentResultProvider::ResultState::kDatabaseScoreNotReady:
@@ -53,18 +58,22 @@ stats::SegmentationSelectionFailureReason GetFailureReason(
case SegmentResultProvider::ResultState::kDefaultModelExecutionFailed:
return stats::SegmentationSelectionFailureReason::
kAtLeastOneSegmentDefaultExecFailed;
+ case SegmentResultProvider::ResultState::kTfliteModelExecutionFailed:
+ return stats::SegmentationSelectionFailureReason::
+ kAtLeastOneSegmentTfliteExecFailed;
}
}
} // namespace
-using optimization_guide::proto::OptimizationTarget_Name;
+using proto::SegmentId_Name;
SegmentSelectorImpl::SegmentSelectorImpl(
SegmentInfoDatabase* segment_database,
SignalStorageConfig* signal_storage_config,
PrefService* pref_service,
const Config* config,
+ FieldTrialRegister* field_trial_register,
base::Clock* clock,
const PlatformOptions& platform_options,
DefaultModelManager* default_model_manager)
@@ -73,6 +82,7 @@ SegmentSelectorImpl::SegmentSelectorImpl(
signal_storage_config,
std::make_unique<SegmentationResultPrefs>(pref_service),
config,
+ field_trial_register,
clock,
platform_options,
default_model_manager) {}
@@ -82,6 +92,7 @@ SegmentSelectorImpl::SegmentSelectorImpl(
SignalStorageConfig* signal_storage_config,
std::unique_ptr<SegmentationResultPrefs> prefs,
const Config* config,
+ FieldTrialRegister* field_trial_register,
base::Clock* clock,
const PlatformOptions& platform_options,
DefaultModelManager* default_model_manager)
@@ -90,21 +101,34 @@ SegmentSelectorImpl::SegmentSelectorImpl(
signal_storage_config_(signal_storage_config),
default_model_manager_(default_model_manager),
config_(config),
+ field_trial_register_(field_trial_register),
clock_(clock),
platform_options_(platform_options) {
// Read selected segment from prefs.
const auto& selected_segment =
result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key);
+ std::string trial_name =
+ stats::SegmentationKeyToTrialName(config_->segmentation_key);
+ std::string group_name;
if (selected_segment.has_value()) {
selected_segment_last_session_.segment = selected_segment->segment_id;
selected_segment_last_session_.is_ready = true;
stats::RecordSegmentSelectionFailure(
config_->segmentation_key,
stats::SegmentationSelectionFailureReason::kSelectionAvailableInPrefs);
+
+ group_name = stats::OptimizationTargetToSegmentGroupName(
+ selected_segment->segment_id);
} else {
stats::RecordSegmentSelectionFailure(
config_->segmentation_key, stats::SegmentationSelectionFailureReason::
kInvalidSelectionResultInPrefs);
+ group_name = "Unselected";
+ }
+
+ // Can be nullptr in tests.
+ if (field_trial_register_) {
+ field_trial_register_->RegisterFieldTrial(trial_name, group_name);
}
}
@@ -115,8 +139,21 @@ void SegmentSelectorImpl::OnPlatformInitialized(
segment_result_provider_ = SegmentResultProvider::Create(
segment_database_, signal_storage_config_, default_model_manager_,
execution_service, clock_, platform_options_.force_refresh_results);
- if (CanComputeSegmentSelection()) {
- GetRankForNextSegment(std::make_unique<SegmentRanks>());
+ if (IsPreviousSelectionInvalid()) {
+ SelectSegmentAndStoreToPrefs();
+ }
+
+ // If the segment selection is ready, also record the subsegment for all the
+ // segments.
+ // TODO(ssid): Store the scores in prefs so that this can be recorded earlier
+ // in startup.
+ if (selected_segment_last_session_.is_ready) {
+ for (const SegmentId segment_id : config_->segment_ids) {
+ experimental_group_recorder_.emplace_back(
+ std::make_unique<ExperimentalGroupRecorder>(
+ segment_result_provider_.get(), field_trial_register_,
+ config_->segmentation_key, segment_id));
+ }
}
}
@@ -131,27 +168,34 @@ SegmentSelectionResult SegmentSelectorImpl::GetCachedSegmentResult() {
return selected_segment_last_session_;
}
-void SegmentSelectorImpl::OnModelExecutionCompleted(
- OptimizationTarget segment_id) {
+void SegmentSelectorImpl::GetSelectedSegmentOnDemand(
+ scoped_refptr<InputContext> input_context,
+ SegmentSelectionCallback callback) {
+ DCHECK(config_->on_demand_execution);
+ GetRankForNextSegment(std::make_unique<SegmentRanks>(), input_context,
+ std::move(callback));
+}
+
+void SegmentSelectorImpl::OnModelExecutionCompleted(SegmentId segment_id) {
DCHECK(segment_result_provider_);
// If the |segment_id| is not in config, then skip any updates early.
if (!base::Contains(config_->segment_ids, segment_id))
return;
- if (!CanComputeSegmentSelection())
+ if (!IsPreviousSelectionInvalid())
return;
- GetRankForNextSegment(std::make_unique<SegmentRanks>());
+ SelectSegmentAndStoreToPrefs();
}
-bool SegmentSelectorImpl::CanComputeSegmentSelection() {
+bool SegmentSelectorImpl::IsPreviousSelectionInvalid() {
// Don't compute results if segment selection TTL hasn't expired.
const auto& previous_selection =
result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key);
if (previous_selection.has_value()) {
bool was_unknown_selected = previous_selection->segment_id ==
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+ SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
base::TimeDelta ttl_to_use = was_unknown_selected
? config_->unknown_selection_ttl
: config_->segment_selection_ttl;
@@ -161,7 +205,7 @@ bool SegmentSelectorImpl::CanComputeSegmentSelection() {
config_->segmentation_key,
stats::SegmentationSelectionFailureReason::kSelectionTtlNotExpired);
VLOG(1) << __func__ << ": previous selection of segment="
- << OptimizationTarget_Name(previous_selection->segment_id)
+ << SegmentId_Name(previous_selection->segment_id)
<< " has not yet expired.";
return false;
}
@@ -170,25 +214,55 @@ bool SegmentSelectorImpl::CanComputeSegmentSelection() {
return true;
}
+void SegmentSelectorImpl::SelectSegmentAndStoreToPrefs() {
+ if (config_->on_demand_execution) {
+ return;
+ }
+ GetRankForNextSegment(std::make_unique<SegmentRanks>(), nullptr,
+ SegmentSelectionCallback());
+}
+
void SegmentSelectorImpl::GetRankForNextSegment(
- std::unique_ptr<SegmentRanks> ranks) {
- for (OptimizationTarget needed_segment : config_->segment_ids) {
+ std::unique_ptr<SegmentRanks> ranks,
+ scoped_refptr<InputContext> input_context,
+ SegmentSelectionCallback callback) {
+ for (SegmentId needed_segment : config_->segment_ids) {
if (ranks->count(needed_segment) == 0) {
- segment_result_provider_->GetSegmentResult(
- needed_segment, config_->segmentation_key,
+ auto options =
+ std::make_unique<SegmentResultProvider::GetResultOptions>();
+ options->segment_id = needed_segment;
+ options->segmentation_key = config_->segmentation_key;
+ options->ignore_db_scores = config_->on_demand_execution;
+ options->callback =
base::BindOnce(&SegmentSelectorImpl::OnGetResultForSegmentSelection,
weak_ptr_factory_.GetWeakPtr(), std::move(ranks),
- needed_segment));
+ input_context, std::move(callback), needed_segment);
+
+ segment_result_provider_->GetSegmentResult(std::move(options));
return;
}
}
- OptimizationTarget selected_segment = FindBestSegment(*ranks);
- UpdateSelectedSegment(selected_segment);
+
+ // Finished fetching ranks for all segments.
+ SegmentId selected_segment = FindBestSegment(*ranks);
+ if (config_->on_demand_execution) {
+ DCHECK(!callback.is_null());
+ SegmentSelectionResult result;
+ result.is_ready = true;
+ result.segment = selected_segment;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), result));
+ } else {
+ DCHECK(callback.is_null());
+ UpdateSelectedSegment(selected_segment);
+ }
}
void SegmentSelectorImpl::OnGetResultForSegmentSelection(
std::unique_ptr<SegmentRanks> ranks,
- OptimizationTarget current_segment_id,
+ scoped_refptr<InputContext> input_context,
+ SegmentSelectionCallback callback,
+ SegmentId current_segment_id,
std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
if (!result->rank) {
stats::RecordSegmentSelectionFailure(config_->segmentation_key,
@@ -197,18 +271,17 @@ void SegmentSelectorImpl::OnGetResultForSegmentSelection(
}
ranks->insert(std::make_pair(current_segment_id, *result->rank));
- GetRankForNextSegment(std::move(ranks));
+ GetRankForNextSegment(std::move(ranks), input_context, std::move(callback));
}
-OptimizationTarget SegmentSelectorImpl::FindBestSegment(
+SegmentId SegmentSelectorImpl::FindBestSegment(
const SegmentRanks& segment_results) {
int max_rank = 0;
- OptimizationTarget max_rank_id =
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+ SegmentId max_rank_id = SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
// Loop through all the results. Convert them to discrete ranks. Select the
// one with highest discrete rank.
for (const auto& pair : segment_results) {
- OptimizationTarget id = pair.first;
+ SegmentId id = pair.first;
int rank = pair.second;
if (rank > max_rank) {
max_rank = rank;
@@ -221,10 +294,9 @@ OptimizationTarget SegmentSelectorImpl::FindBestSegment(
return max_rank_id;
}
-void SegmentSelectorImpl::UpdateSelectedSegment(
- OptimizationTarget new_selection) {
- VLOG(1) << __func__ << ": Updating selected segment="
- << OptimizationTarget_Name(new_selection);
+void SegmentSelectorImpl::UpdateSelectedSegment(SegmentId new_selection) {
+ VLOG(1) << __func__
+ << ": Updating selected segment=" << SegmentId_Name(new_selection);
const auto& previous_selection =
result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key);
@@ -237,7 +309,7 @@ void SegmentSelectorImpl::UpdateSelectedSegment(
skip_updating_prefs = new_selection == previous_selection->segment_id;
skip_updating_prefs |=
config_->unknown_selection_ttl == base::TimeDelta() &&
- new_selection == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+ new_selection == SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
// TODO(shaktisahu): Use segment selection inertia.
}
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_selector_impl.h b/chromium/components/segmentation_platform/internal/selection/segment_selector_impl.h
index 8aa96adc64c..f40d0201d21 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_selector_impl.h
+++ b/chromium/components/segmentation_platform/internal/selection/segment_selector_impl.h
@@ -9,6 +9,7 @@
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/input_context.h"
#include "components/segmentation_platform/internal/platform_options.h"
#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
#include "components/segmentation_platform/internal/selection/segment_selector.h"
@@ -23,8 +24,10 @@ class Clock;
namespace segmentation_platform {
struct Config;
-class ExecutionService;
class DefaultModelManager;
+class ExecutionService;
+class ExperimentalGroupRecorder;
+class FieldTrialRegister;
class SegmentationResultPrefs;
class SignalStorageConfig;
@@ -34,6 +37,7 @@ class SegmentSelectorImpl : public SegmentSelector {
SignalStorageConfig* signal_storage_config,
PrefService* pref_service,
const Config* config,
+ FieldTrialRegister* field_trial_register,
base::Clock* clock,
const PlatformOptions& platform_options,
DefaultModelManager* default_model_manager);
@@ -42,6 +46,7 @@ class SegmentSelectorImpl : public SegmentSelector {
SignalStorageConfig* signal_storage_config,
std::unique_ptr<SegmentationResultPrefs> prefs,
const Config* config,
+ FieldTrialRegister* field_trial_register,
base::Clock* clock,
const PlatformOptions& platform_options,
DefaultModelManager* default_model_manager);
@@ -52,40 +57,54 @@ class SegmentSelectorImpl : public SegmentSelector {
void OnPlatformInitialized(ExecutionService* execution_service) override;
void GetSelectedSegment(SegmentSelectionCallback callback) override;
SegmentSelectionResult GetCachedSegmentResult() override;
+ void GetSelectedSegmentOnDemand(scoped_refptr<InputContext> input_context,
+ SegmentSelectionCallback callback) override;
// Helper function to update the selected segment in the prefs. Auto-extends
// the selection if the new result is unknown.
- virtual void UpdateSelectedSegment(OptimizationTarget new_selection);
+ virtual void UpdateSelectedSegment(SegmentId new_selection);
// Called whenever a model eval completes. Runs segment selection to find the
// best segment, and writes it to the pref.
- void OnModelExecutionCompleted(OptimizationTarget segment_id) override;
+ void OnModelExecutionCompleted(SegmentId segment_id) override;
+
+ void set_segment_result_provider_for_testing(
+ std::unique_ptr<SegmentResultProvider> result_provider) {
+ segment_result_provider_ = std::move(result_provider);
+ }
private:
// For testing.
friend class SegmentSelectorTest;
- using SegmentRanks = base::flat_map<OptimizationTarget, int>;
+ using SegmentRanks = base::flat_map<SegmentId, int>;
- // Determines whether segment selection can be run based on whether all
- // segments have met signal collection requirement, have valid results, and
- // segment selection TTL has expired.
- bool CanComputeSegmentSelection();
+ // Determines whether segment selection can be run based on whether the
+ // segment selection TTL has expired, or selection is unavailable.
+ bool IsPreviousSelectionInvalid();
+
+ // Gets scores for all segments and recomputes selection and stores the result
+ // to prefs.
+ void SelectSegmentAndStoreToPrefs();
// Gets ranks for each segment from SegmentResultProvider, and then computes
// segment selection.
- void GetRankForNextSegment(std::unique_ptr<SegmentRanks> ranks);
+ void GetRankForNextSegment(std::unique_ptr<SegmentRanks> ranks,
+ scoped_refptr<InputContext> input_context,
+ SegmentSelectionCallback callback);
// Callback used to get result from SegmentResultProvider for each segment.
void OnGetResultForSegmentSelection(
std::unique_ptr<SegmentRanks> ranks,
- OptimizationTarget current_segment_id,
+ scoped_refptr<InputContext> input_context,
+ SegmentSelectionCallback callback,
+ SegmentId current_segment_id,
std::unique_ptr<SegmentResultProvider::SegmentResult> result);
// Loops through all segments, performs discrete mapping, honors finch
// supplied tie-breakers, TTL, inertia etc, and finds the highest rank.
// Ignores the segments that have no results.
- OptimizationTarget FindBestSegment(const SegmentRanks& segment_scores);
+ SegmentId FindBestSegment(const SegmentRanks& segment_scores);
std::unique_ptr<SegmentResultProvider> segment_result_provider_;
@@ -104,6 +123,14 @@ class SegmentSelectorImpl : public SegmentSelector {
// The config for providing configuration params.
const raw_ptr<const Config> config_;
+ // Delegate that records selected segments to metrics.
+ const raw_ptr<FieldTrialRegister> field_trial_register_;
+
+ // Helper that records segmentation subgroups to `field_trial_register_`. Once
+ // for each segment in the `config_`.
+ std::vector<std::unique_ptr<ExperimentalGroupRecorder>>
+ experimental_group_recorder_;
+
// The time provider.
const raw_ptr<base::Clock> clock_;
diff --git a/chromium/components/segmentation_platform/internal/selection/segment_selector_unittest.cc b/chromium/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
index fd7f5933a3a..5214571acbe 100644
--- a/chromium/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
@@ -8,18 +8,21 @@
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/mock_signal_storage_config.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
#include "components/segmentation_platform/internal/database/test_segment_info_database.h"
#include "components/segmentation_platform/internal/execution/default_model_manager.h"
#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
+#include "components/segmentation_platform/internal/metric_filter_utils.h"
#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/field_trial_register.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+using testing::Invoke;
using testing::Return;
using testing::SaveArg;
@@ -30,17 +33,34 @@ class SegmentInfo;
namespace {
+class MockFieldTrialRegister : public FieldTrialRegister {
+ public:
+ MOCK_METHOD2(RegisterFieldTrial,
+ void(base::StringPiece trial_name,
+ base::StringPiece group_name));
+
+ MOCK_METHOD3(RegisterSubsegmentFieldTrialIfNeeded,
+ void(base::StringPiece trial_name,
+ proto::SegmentId segment_id,
+ int subsegment_rank));
+};
+
Config CreateTestConfig() {
Config config;
config.segmentation_key = "test_key";
config.segment_selection_ttl = base::Days(28);
config.unknown_selection_ttl = base::Days(14);
- config.segment_ids = {
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
+ config.segment_ids = {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
return config;
}
+class MockResultProvider : public SegmentResultProvider {
+ public:
+ MOCK_METHOD1(GetSegmentResult,
+ void(std::unique_ptr<GetResultOptions> options));
+};
+
} // namespace
class TestSegmentationResultPrefs : public SegmentationResultPrefs {
@@ -76,7 +96,7 @@ class SegmentSelectorTest : public testing::Test {
prefs_ = prefs_moved.get();
segment_selector_ = std::make_unique<SegmentSelectorImpl>(
segment_database_.get(), &signal_storage_config_,
- std::move(prefs_moved), &config_, &clock_,
+ std::move(prefs_moved), &config_, &field_trial_register_, &clock_,
PlatformOptions::CreateDefault(), default_manager_.get());
segment_selector_->OnPlatformInitialized(nullptr);
}
@@ -96,10 +116,10 @@ class SegmentSelectorTest : public testing::Test {
std::move(closure).Run();
}
- void InitializeMetadataForSegment(OptimizationTarget segment_id,
+ void InitializeMetadataForSegment(SegmentId segment_id,
float mapping[][2],
int num_mapping_pairs) {
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillRepeatedly(Return(true));
segment_database_->FindOrCreateSegment(segment_id)
->mutable_model_metadata()
@@ -110,7 +130,7 @@ class SegmentSelectorTest : public testing::Test {
segment_id, mapping, num_mapping_pairs, config_.segmentation_key);
}
- void CompleteModelExecution(OptimizationTarget segment_id, float score) {
+ void CompleteModelExecution(SegmentId segment_id, float score) {
segment_database_->AddPredictionResult(segment_id, score, clock_.Now());
segment_selector_->OnModelExecutionCompleted(segment_id);
task_environment_.RunUntilIdle();
@@ -120,6 +140,7 @@ class SegmentSelectorTest : public testing::Test {
TestModelProviderFactory::Data model_providers_;
TestModelProviderFactory provider_factory_;
Config config_;
+ MockFieldTrialRegister field_trial_register_;
base::SimpleTestClock clock_;
std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_;
MockSignalStorageConfig signal_storage_config_;
@@ -130,16 +151,14 @@ class SegmentSelectorTest : public testing::Test {
TEST_F(SegmentSelectorTest, FindBestSegmentFlowWithTwoSegments) {
SetUpWithConfig(CreateTestConfig());
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillRepeatedly(Return(true));
- OptimizationTarget segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
float mapping[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
InitializeMetadataForSegment(segment_id, mapping, 3);
- OptimizationTarget segment_id2 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ SegmentId segment_id2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
InitializeMetadataForSegment(segment_id2, mapping2, 2);
@@ -153,23 +172,68 @@ TEST_F(SegmentSelectorTest, FindBestSegmentFlowWithTwoSegments) {
ASSERT_EQ(segment_id2, prefs_->selection->segment_id);
}
+TEST_F(SegmentSelectorTest, RunSelectionOnDemand) {
+ Config config = CreateTestConfig();
+ config.on_demand_execution = true;
+ SetUpWithConfig(config);
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
+ .WillRepeatedly(Return(true));
+
+ static constexpr SegmentId kSegmentId =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ float mapping[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
+ InitializeMetadataForSegment(kSegmentId, mapping, 3);
+
+ static constexpr SegmentId kSegmentId2 =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
+ InitializeMetadataForSegment(kSegmentId2, mapping2, 2);
+
+ auto result_provider = std::make_unique<MockResultProvider>();
+ EXPECT_CALL(*result_provider, GetSegmentResult(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(
+ [](std::unique_ptr<SegmentResultProvider::GetResultOptions> options) {
+ EXPECT_TRUE(options->ignore_db_scores);
+ int rank = options->segment_id == kSegmentId ? 3 : 4;
+ auto result =
+ std::make_unique<SegmentResultProvider::SegmentResult>(
+ SegmentResultProvider::ResultState::kTfliteModelScoreUsed,
+ rank);
+ std::move(options->callback).Run(std::move(result));
+ }));
+ segment_selector_->set_segment_result_provider_for_testing(
+ std::move(result_provider));
+
+ clock_.Advance(base::Days(1));
+ base::RunLoop wait_for_selection;
+ segment_selector_->GetSelectedSegmentOnDemand(
+ /*input_context=*/nullptr,
+ base::BindOnce(
+ [](base::OnceClosure quit, const SegmentSelectionResult& result) {
+ EXPECT_TRUE(result.is_ready);
+ EXPECT_EQ(kSegmentId2, *result.segment);
+ std::move(quit).Run();
+ },
+ wait_for_selection.QuitClosure()));
+ wait_for_selection.Run();
+}
+
TEST_F(SegmentSelectorTest, NewSegmentResultOverridesThePreviousBest) {
Config config = CreateTestConfig();
config.unknown_selection_ttl = base::TimeDelta();
SetUpWithConfig(config);
// Setup test with two models.
- OptimizationTarget segment_id1 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ SegmentId segment_id1 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}, {0.8, 5}};
InitializeMetadataForSegment(segment_id1, mapping1, 4);
- OptimizationTarget segment_id2 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ SegmentId segment_id2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
InitializeMetadataForSegment(segment_id2, mapping2, 2);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillRepeatedly(Return(true));
// Model 1 completes with a zero-ish score. We will wait for the other model
@@ -179,13 +243,13 @@ TEST_F(SegmentSelectorTest, NewSegmentResultOverridesThePreviousBest) {
CompleteModelExecution(segment_id2, 0.1);
ASSERT_TRUE(prefs_->selection.has_value());
- ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+ ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
prefs_->selection->segment_id);
// Model 1 completes with a good score. Model 2 results are expired.
clock_.Advance(config_.segment_selection_ttl * 1.2f);
CompleteModelExecution(segment_id1, 0.6);
- ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+ ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
prefs_->selection->segment_id);
// Model 2 gets fresh results. Now segment selection will update.
@@ -221,57 +285,53 @@ TEST_F(SegmentSelectorTest, NewSegmentResultOverridesThePreviousBest) {
TEST_F(SegmentSelectorTest, UnknownSegmentTtlExpiryForBooleanModel) {
Config config = CreateTestConfig();
config.segment_ids = {
- OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID};
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID};
SetUpWithConfig(config);
- OptimizationTarget segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID;
+ SegmentId segment_id =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID;
float mapping[][2] = {{0.7, 1}};
InitializeMetadataForSegment(segment_id, mapping, 1);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillRepeatedly(Return(true));
// Set a value less than 1 and result should be UNKNOWN.
CompleteModelExecution(segment_id, 0);
ASSERT_TRUE(prefs_->selection.has_value());
- ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+ ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
prefs_->selection->segment_id);
// Advance by less than UNKNOWN segment TTL and result should not change,
// UNKNOWN segment TTL is less than selection TTL.
clock_.Advance(config_.unknown_selection_ttl * 0.8f);
CompleteModelExecution(segment_id, 0.9);
- ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+ ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
prefs_->selection->segment_id);
// Advance clock so that the time is between UNKNOWN segment TTL and selection
// TTL.
clock_.Advance(config_.unknown_selection_ttl * 0.4f);
CompleteModelExecution(segment_id, 0.9);
- ASSERT_EQ(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
- prefs_->selection->segment_id);
+ ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
+ prefs_->selection->segment_id);
// Advance by more than UNKNOWN segment TTL and result should not change.
clock_.Advance(config_.unknown_selection_ttl * 1.2f);
CompleteModelExecution(segment_id, 0);
- ASSERT_EQ(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
- prefs_->selection->segment_id);
+ ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
+ prefs_->selection->segment_id);
// Advance by segment selection TTL and result should change.
clock_.Advance(config_.segment_selection_ttl * 1.2f);
CompleteModelExecution(segment_id, 0);
- ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+ ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
prefs_->selection->segment_id);
}
TEST_F(SegmentSelectorTest, DoesNotMeetSignalCollectionRequirement) {
SetUpWithConfig(CreateTestConfig());
- OptimizationTarget segment_id1 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ SegmentId segment_id1 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}, {0.8, 5}};
segment_database_->FindOrCreateSegment(segment_id1)
@@ -281,7 +341,7 @@ TEST_F(SegmentSelectorTest, DoesNotMeetSignalCollectionRequirement) {
segment_database_->AddDiscreteMapping(segment_id1, mapping1, 4,
config_.segmentation_key);
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillRepeatedly(Return(false));
CompleteModelExecution(segment_id1, 0.5);
@@ -291,12 +351,10 @@ TEST_F(SegmentSelectorTest, DoesNotMeetSignalCollectionRequirement) {
TEST_F(SegmentSelectorTest,
GetSelectedSegmentReturnsResultFromPreviousSession) {
SetUpWithConfig(CreateTestConfig());
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillRepeatedly(Return(true));
- OptimizationTarget segment_id0 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
- OptimizationTarget segment_id1 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ SegmentId segment_id0 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ SegmentId segment_id1 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
float mapping0[][2] = {{1.0, 0}};
float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
InitializeMetadataForSegment(segment_id0, mapping0, 1);
@@ -311,8 +369,8 @@ TEST_F(SegmentSelectorTest,
// Construct a segment selector. It should read result from last session.
segment_selector_ = std::make_unique<SegmentSelectorImpl>(
segment_database_.get(), &signal_storage_config_, std::move(prefs_moved),
- &config_, &clock_, PlatformOptions::CreateDefault(),
- default_manager_.get());
+ &config_, &field_trial_register_, &clock_,
+ PlatformOptions::CreateDefault(), default_manager_.get());
segment_selector_->OnPlatformInitialized(nullptr);
SegmentSelectionResult result;
@@ -339,16 +397,14 @@ TEST_F(SegmentSelectorTest,
// Tests that prefs are properly updated after calling UpdateSelectedSegment().
TEST_F(SegmentSelectorTest, UpdateSelectedSegment) {
SetUpWithConfig(CreateTestConfig());
- EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_))
+ EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
.WillRepeatedly(Return(true));
- OptimizationTarget segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
float mapping[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
InitializeMetadataForSegment(segment_id, mapping, 3);
- OptimizationTarget segment_id2 =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ SegmentId segment_id2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
InitializeMetadataForSegment(segment_id2, mapping2, 2);
@@ -367,4 +423,83 @@ TEST_F(SegmentSelectorTest, UpdateSelectedSegment) {
ASSERT_EQ(segment_id, prefs_->selection->segment_id);
}
+TEST_F(SegmentSelectorTest, SubsegmentRecording) {
+ const SegmentId kSubsegmentEnabledTarget =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER;
+
+ // Create config with Feed segment.
+ Config config = CreateTestConfig();
+ config.segment_ids.push_back(kSubsegmentEnabledTarget);
+ // Previous selection result is not available at this time, so it should
+ // record unselected.
+ EXPECT_CALL(field_trial_register_,
+ RegisterFieldTrial(base::StringPiece("Segmentation_TestKey"),
+ base::StringPiece("Unselected")));
+ SetUpWithConfig(config);
+
+ // Store model metadata, model scores and selection results.
+ SegmentId segment_id0 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ float mapping0[][2] = {{1.0, 0}};
+ InitializeMetadataForSegment(segment_id0, mapping0, 1);
+ segment_database_->AddPredictionResult(segment_id0, 0.7, clock_.Now());
+
+ float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
+ InitializeMetadataForSegment(kSubsegmentEnabledTarget, mapping1, 3);
+ constexpr float kMVTScore = 0.7;
+ segment_database_->AddPredictionResult(kSubsegmentEnabledTarget, kMVTScore,
+ clock_.Now());
+
+ // Additionally store subsegment mapping for Feed segment.
+ static constexpr std::array<float[2], 3> kFeedUserScoreToSubGroup = {{
+ {1.0, 2},
+ {kMVTScore, 3},
+ {0.0, 4},
+ }};
+ segment_database_->AddDiscreteMapping(
+ kSubsegmentEnabledTarget, kFeedUserScoreToSubGroup.data(),
+ kFeedUserScoreToSubGroup.size(),
+ config_.segmentation_key + kSubsegmentDiscreteMappingSuffix);
+
+ // Set up a selected segment in prefs.
+ SelectedSegment from_history(segment_id0);
+ auto prefs_moved = std::make_unique<TestSegmentationResultPrefs>();
+ prefs_ = prefs_moved.get();
+ prefs_->selection = from_history;
+
+ EXPECT_CALL(field_trial_register_,
+ RegisterFieldTrial(base::StringPiece("Segmentation_TestKey"),
+ base::StringPiece("Share")));
+
+ // Construct a segment selector. It should read result from last session.
+ segment_selector_ = std::make_unique<SegmentSelectorImpl>(
+ segment_database_.get(), &signal_storage_config_, std::move(prefs_moved),
+ &config_, &field_trial_register_, &clock_,
+ PlatformOptions::CreateDefault(), default_manager_.get());
+
+ // When segment result is missing, unknown subsegment is recorded.
+ EXPECT_CALL(
+ field_trial_register_,
+ RegisterSubsegmentFieldTrialIfNeeded(
+ base::StringPiece("Segmentation_TestKey_Share"), segment_id0, 0));
+ EXPECT_CALL(
+ field_trial_register_,
+ RegisterSubsegmentFieldTrialIfNeeded(
+ base::StringPiece("Segmentation_TestKey_NewTab"),
+ proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 0));
+
+ // The new selector will record subsegment metric groups based on the mapping.
+ base::RunLoop wait_for_subsegment;
+ EXPECT_CALL(field_trial_register_,
+ RegisterSubsegmentFieldTrialIfNeeded(
+ base::StringPiece("Segmentation_TestKey_FeedUserSegment"),
+ kSubsegmentEnabledTarget, 3))
+ .WillOnce(
+ Invoke([&wait_for_subsegment](base::StringPiece, SegmentId, int) {
+ wait_for_subsegment.QuitClosure().Run();
+ }));
+
+ segment_selector_->OnPlatformInitialized(nullptr);
+ wait_for_subsegment.Run();
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc b/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
index e5f87e2963c..91c669c5147 100644
--- a/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
+++ b/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
@@ -12,7 +12,7 @@
namespace segmentation_platform {
-SelectedSegment::SelectedSegment(OptimizationTarget segment_id)
+SelectedSegment::SelectedSegment(SegmentId segment_id)
: segment_id(segment_id), in_use(false) {}
SelectedSegment::~SelectedSegment() = default;
@@ -57,8 +57,7 @@ SegmentationResultPrefs::ReadSegmentationResultFromPref(
absl::optional<base::Time> selection_time =
base::ValueToTime(segmentation_result.FindPath("selection_time"));
- SelectedSegment selected_segment(
- static_cast<OptimizationTarget>(segment_id.value()));
+ SelectedSegment selected_segment(static_cast<SegmentId>(segment_id.value()));
if (in_use.has_value())
selected_segment.in_use = in_use.value();
if (selection_time.has_value())
diff --git a/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.h b/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.h
index e966dd79de4..a48ae879dff 100644
--- a/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.h
+++ b/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs.h
@@ -7,24 +7,24 @@
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-using optimization_guide::proto::OptimizationTarget;
-
class PrefService;
namespace segmentation_platform {
+using proto::SegmentId;
+
// Struct containing information about the selected segment. Convenient for
// reading and writing to prefs.
struct SelectedSegment {
public:
- explicit SelectedSegment(OptimizationTarget segment_id);
+ explicit SelectedSegment(SegmentId segment_id);
~SelectedSegment();
// The segment selection result.
- OptimizationTarget segment_id;
+ SegmentId segment_id;
// The time when the segment was selected.
base::Time selection_time;
diff --git a/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs_unittest.cc b/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs_unittest.cc
index c105671ef5e..386ee187afd 100644
--- a/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/selection/segmentation_result_prefs_unittest.cc
@@ -39,8 +39,7 @@ TEST_F(SegmentationResultPrefsTest, WriteResultAndRead) {
EXPECT_FALSE(current_result.has_value());
// Save a result. Verify by reading the result back.
- OptimizationTarget segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
SelectedSegment selected_segment(segment_id);
result_prefs_->SaveSegmentationResultToPref(result_key, selected_segment);
current_result = result_prefs_->ReadSegmentationResultFromPref(result_key);
@@ -51,7 +50,7 @@ TEST_F(SegmentationResultPrefsTest, WriteResultAndRead) {
// Overwrite the result with a new segment.
selected_segment.segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
selected_segment.in_use = true;
base::Time now = base::Time::Now();
selected_segment.selection_time = now;
@@ -66,7 +65,7 @@ TEST_F(SegmentationResultPrefsTest, WriteResultAndRead) {
// first key.
std::string result_key2 = "some_key2";
selected_segment.segment_id =
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
result_prefs_->SaveSegmentationResultToPref(result_key2, selected_segment);
current_result = result_prefs_->ReadSegmentationResultFromPref(result_key2);
EXPECT_TRUE(current_result.has_value());
@@ -74,7 +73,7 @@ TEST_F(SegmentationResultPrefsTest, WriteResultAndRead) {
current_result = result_prefs_->ReadSegmentationResultFromPref(result_key);
EXPECT_TRUE(current_result.has_value());
- EXPECT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+ EXPECT_EQ(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
current_result->segment_id);
// Save empty result. It should delete the current result.
@@ -85,7 +84,7 @@ TEST_F(SegmentationResultPrefsTest, WriteResultAndRead) {
TEST_F(SegmentationResultPrefsTest, CorruptedValue) {
std::string result_key = "some_key";
- SelectedSegment selected_segment(static_cast<OptimizationTarget>(100));
+ SelectedSegment selected_segment(static_cast<SegmentId>(100));
result_prefs_->SaveSegmentationResultToPref(result_key, selected_segment);
absl::optional<SelectedSegment> current_result =
result_prefs_->ReadSegmentationResultFromPref(result_key);
diff --git a/chromium/components/segmentation_platform/internal/service_proxy_impl.cc b/chromium/components/segmentation_platform/internal/service_proxy_impl.cc
index 66429fb627c..10f6e230198 100644
--- a/chromium/components/segmentation_platform/internal/service_proxy_impl.cc
+++ b/chromium/components/segmentation_platform/internal/service_proxy_impl.cc
@@ -11,8 +11,8 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/scheduler/execution_service.h"
#include "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
#include "components/segmentation_platform/internal/selection/segment_selector_impl.h"
@@ -81,8 +81,11 @@ void ServiceProxyImpl::OnServiceStatusChanged(bool is_initialized,
}
void ServiceProxyImpl::UpdateObservers(bool update_service_status) {
+ if (observers_.empty())
+ return;
+
if (update_service_status) {
- for (Observer& obs : observers_)
+ for (auto& obs : observers_)
obs.OnServiceStatusChanged(is_service_initialized_, service_status_flag_);
}
@@ -104,9 +107,9 @@ void ServiceProxyImpl::GetServiceStatus() {
UpdateObservers(true /* update_service_status */);
}
-void ServiceProxyImpl::ExecuteModel(OptimizationTarget segment_id) {
+void ServiceProxyImpl::ExecuteModel(SegmentId segment_id) {
if (!execution_service ||
- segment_id == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+ segment_id == SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
return;
}
segment_db_->GetSegmentInfo(
@@ -119,34 +122,33 @@ void ServiceProxyImpl::OnSegmentInfoFetchedForExecution(
absl::optional<proto::SegmentInfo> segment_info) {
if (!segment_info)
return;
- auto request = std::make_unique<ExecutionService::ExecutionRequest>();
+ auto request = std::make_unique<ExecutionRequest>();
request->record_metrics_for_default = false;
request->save_result_to_db = true;
request->segment_info = &segment_info.value();
execution_service->RequestModelExecution(std::move(request));
}
-void ServiceProxyImpl::OverwriteResult(OptimizationTarget segment_id,
- float result) {
+void ServiceProxyImpl::OverwriteResult(SegmentId segment_id, float result) {
if (!execution_service)
return;
if (result < 0 || result > 1)
return;
- if (segment_id != OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+ if (segment_id != SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
execution_service->OverwriteModelExecutionResult(
segment_id, std::make_pair(result, ModelExecutionStatus::kSuccess));
}
}
void ServiceProxyImpl::SetSelectedSegment(const std::string& segmentation_key,
- OptimizationTarget segment_id) {
+ SegmentId segment_id) {
if (!segment_selectors_ ||
segment_selectors_->find(segmentation_key) == segment_selectors_->end()) {
return;
}
- if (segment_id != OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+ if (segment_id != SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
auto& selector = segment_selectors_->at(segmentation_key);
selector->UpdateSelectedSegment(segment_id);
}
@@ -158,31 +160,30 @@ void ServiceProxyImpl::OnGetAllSegmentationInfo(
return;
// Convert the |segment_info| vector to a map for quick lookup.
- base::flat_map<OptimizationTarget, proto::SegmentInfo> optimization_targets;
+ base::flat_map<SegmentId, proto::SegmentInfo> segment_ids;
for (const auto& info : *segment_info) {
- optimization_targets[info.first] = info.second;
+ segment_ids[info.first] = info.second;
}
std::vector<ServiceProxy::ClientInfo> result;
for (const auto& config : *configs_) {
- OptimizationTarget selected =
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+ SegmentId selected = SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
if (segment_selectors_ &&
segment_selectors_->find(config->segmentation_key) !=
segment_selectors_->end()) {
- absl::optional<optimization_guide::proto::OptimizationTarget> target =
+ absl::optional<proto::SegmentId> target =
segment_selectors_->at(config->segmentation_key)
->GetCachedSegmentResult()
.segment;
if (target.has_value()) {
- selected = target.value();
+ selected = *target;
}
}
result.emplace_back(config->segmentation_key, selected);
for (const auto& segment_id : config->segment_ids) {
- if (!optimization_targets.contains(segment_id))
+ if (!segment_ids.contains(segment_id))
continue;
- const auto& info = optimization_targets[segment_id];
+ const auto& info = segment_ids[segment_id];
result.back().segment_status.emplace_back(
segment_id, SegmentMetadataToString(info),
PredictionResultToString(info),
@@ -193,8 +194,13 @@ void ServiceProxyImpl::OnGetAllSegmentationInfo(
}
}
- for (Observer& obs : observers_)
+ for (auto& obs : observers_)
obs.OnClientInfoAvailable(result);
}
+void ServiceProxyImpl::OnModelExecutionCompleted(SegmentId segment_id) {
+ // Update the observers with the new execution results.
+ UpdateObservers(false);
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/service_proxy_impl.h b/chromium/components/segmentation_platform/internal/service_proxy_impl.h
index 58bd3643d33..1d4ea7fc357 100644
--- a/chromium/components/segmentation_platform/internal/service_proxy_impl.h
+++ b/chromium/components/segmentation_platform/internal/service_proxy_impl.h
@@ -12,13 +12,14 @@
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "components/leveldb_proto/public/proto_database.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/scheduler/model_execution_scheduler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "components/segmentation_platform/public/service_proxy.h"
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
+using proto::SegmentId;
+
struct Config;
class SignalStorageConfig;
class ExecutionService;
@@ -26,7 +27,8 @@ class SegmentSelectorImpl;
// A helper class to expose internals of the segmentationss service to a logging
// component and/or debug UI.
-class ServiceProxyImpl : public ServiceProxy {
+class ServiceProxyImpl : public ServiceProxy,
+ public ModelExecutionScheduler::Observer {
public:
ServiceProxyImpl(
SegmentInfoDatabase* segment_db,
@@ -46,10 +48,10 @@ class ServiceProxyImpl : public ServiceProxy {
// ServiceProxy impl.
void GetServiceStatus() override;
- void ExecuteModel(OptimizationTarget segment_id) override;
- void OverwriteResult(OptimizationTarget segment_id, float result) override;
+ void ExecuteModel(SegmentId segment_id) override;
+ void OverwriteResult(SegmentId segment_id, float result) override;
void SetSelectedSegment(const std::string& segmentation_key,
- OptimizationTarget segment_id) override;
+ SegmentId segment_id) override;
// Called when segmentation service status changed.
void OnServiceStatusChanged(bool is_initialized, int status_flag);
@@ -67,6 +69,9 @@ class ServiceProxyImpl : public ServiceProxy {
void OnGetAllSegmentationInfo(
std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segment_info);
+ // ModelExecutionScheduler::Observer overrides.
+ void OnModelExecutionCompleted(SegmentId segment_id) override;
+
bool is_service_initialized_ = false;
int service_status_flag_ = 0;
raw_ptr<SegmentInfoDatabase> segment_db_;
diff --git a/chromium/components/segmentation_platform/internal/service_proxy_impl_unittest.cc b/chromium/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
index fd29b03d5dd..a2acb0253ef 100644
--- a/chromium/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
@@ -14,8 +14,8 @@
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
#include "components/segmentation_platform/internal/execution/model_executor.h"
+#include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
#include "components/segmentation_platform/internal/platform_options.h"
#include "components/segmentation_platform/internal/scheduler/execution_service.h"
#include "components/segmentation_platform/internal/scheduler/model_execution_scheduler.h"
@@ -38,7 +38,7 @@ constexpr char kTestSegmentationKey[] = "test_key";
proto::SegmentInfo AddSegmentInfo(
std::map<std::string, proto::SegmentInfo>* db_entries,
Config* config,
- OptimizationTarget segment_id) {
+ SegmentId segment_id) {
proto::SegmentInfo info;
info.set_segment_id(segment_id);
db_entries->insert(
@@ -55,8 +55,7 @@ class MockModelExecutionScheduler : public ModelExecutionScheduler {
MOCK_METHOD(void, RequestModelExecutionForEligibleSegments, (bool));
MOCK_METHOD(void,
OnModelExecutionCompleted,
- (OptimizationTarget,
- (const std::pair<float, ModelExecutionStatus>&)));
+ (SegmentId, (const std::pair<float, ModelExecutionStatus>&)));
};
} // namespace
@@ -70,18 +69,19 @@ class FakeSegmentSelectorImpl : public SegmentSelectorImpl {
pref_service,
config,
nullptr,
+ nullptr,
PlatformOptions::CreateDefault(),
nullptr) {}
~FakeSegmentSelectorImpl() override = default;
- void UpdateSelectedSegment(OptimizationTarget new_selection) override {
+ void UpdateSelectedSegment(SegmentId new_selection) override {
new_selection_ = new_selection;
}
- OptimizationTarget new_selection() const { return new_selection_; }
+ SegmentId new_selection() const { return new_selection_; }
private:
- OptimizationTarget new_selection_;
+ SegmentId new_selection_;
};
class ServiceProxyImplTest : public testing::Test,
@@ -168,9 +168,9 @@ TEST_F(ServiceProxyImplTest, GetServiceStatus) {
}
TEST_F(ServiceProxyImplTest, GetSegmentationInfoFromDB) {
- proto::SegmentInfo info = AddSegmentInfo(
- &db_entries_, configs_.at(0).get(),
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ proto::SegmentInfo info =
+ AddSegmentInfo(&db_entries_, configs_.at(0).get(),
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
SetUpProxy();
service_proxy_impl_->OnServiceStatusChanged(true, 7);
@@ -180,35 +180,36 @@ TEST_F(ServiceProxyImplTest, GetSegmentationInfoFromDB) {
ASSERT_EQ(client_info_.at(0).segment_status.size(), 1u);
ServiceProxy::SegmentStatus status = client_info_.at(0).segment_status.at(0);
ASSERT_EQ(status.segment_id,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
ASSERT_EQ(status.can_execute_segment, false);
ASSERT_TRUE(status.segment_metadata.empty());
ASSERT_TRUE(status.prediction_result.empty());
}
TEST_F(ServiceProxyImplTest, ExecuteModel) {
- proto::SegmentInfo info = AddSegmentInfo(
- &db_entries_, configs_.at(0).get(),
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ proto::SegmentInfo info =
+ AddSegmentInfo(&db_entries_, configs_.at(0).get(),
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
SetUpProxy();
service_proxy_impl_->OnServiceStatusChanged(true, 7);
db_->LoadCallback(true);
segment_db_->UpdateSegment(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, info,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, info,
base::DoNothing());
db_->UpdateCallback(true);
auto scheduler_moved = std::make_unique<MockModelExecutionScheduler>();
MockModelExecutionScheduler* scheduler = scheduler_moved.get();
ExecutionService execution;
- execution.InitForTesting(nullptr, nullptr, std::move(scheduler_moved));
+ execution.InitForTesting(nullptr, nullptr, std::move(scheduler_moved),
+ nullptr);
// Scheduler is not set, ExecuteModel() will do nothing.
EXPECT_CALL(*scheduler, RequestModelExecution(_)).Times(0);
service_proxy_impl_->ExecuteModel(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
service_proxy_impl_->SetExecutionService(&execution);
base::RunLoop wait_for_execution;
@@ -219,19 +220,18 @@ TEST_F(ServiceProxyImplTest, ExecuteModel) {
wait_for_execution.QuitClosure().Run();
}));
service_proxy_impl_->ExecuteModel(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
db_->GetCallback(true);
wait_for_execution.Run();
EXPECT_CALL(*scheduler, RequestModelExecution(_)).Times(0);
- service_proxy_impl_->ExecuteModel(
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN);
+ service_proxy_impl_->ExecuteModel(SegmentId::OPTIMIZATION_TARGET_UNKNOWN);
}
TEST_F(ServiceProxyImplTest, OverwriteResult) {
- proto::SegmentInfo info = AddSegmentInfo(
- &db_entries_, configs_.at(0).get(),
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ proto::SegmentInfo info =
+ AddSegmentInfo(&db_entries_, configs_.at(0).get(),
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
SetUpProxy();
service_proxy_impl_->OnServiceStatusChanged(true, 7);
@@ -240,38 +240,38 @@ TEST_F(ServiceProxyImplTest, OverwriteResult) {
auto scheduler_moved = std::make_unique<MockModelExecutionScheduler>();
MockModelExecutionScheduler* scheduler = scheduler_moved.get();
ExecutionService execution;
- execution.InitForTesting(nullptr, nullptr, std::move(scheduler_moved));
+ execution.InitForTesting(nullptr, nullptr, std::move(scheduler_moved),
+ nullptr);
// Scheduler is not set, OverwriteValue() will do nothing.
EXPECT_CALL(*scheduler, OnModelExecutionCompleted(_, _)).Times(0);
service_proxy_impl_->OverwriteResult(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 0.7);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 0.7);
// Test with invalid values.
service_proxy_impl_->SetExecutionService(&execution);
EXPECT_CALL(*scheduler, OnModelExecutionCompleted(_, _)).Times(0);
service_proxy_impl_->OverwriteResult(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 1.1);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 1.1);
service_proxy_impl_->OverwriteResult(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, -0.1);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, -0.1);
- EXPECT_CALL(
- *scheduler,
- OnModelExecutionCompleted(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, _))
+ EXPECT_CALL(*scheduler,
+ OnModelExecutionCompleted(
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, _))
.Times(1);
service_proxy_impl_->OverwriteResult(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 0.7);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 0.7);
EXPECT_CALL(*scheduler, OnModelExecutionCompleted(_, _)).Times(0);
- service_proxy_impl_->OverwriteResult(
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN, 0.7);
+ service_proxy_impl_->OverwriteResult(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
+ 0.7);
}
TEST_F(ServiceProxyImplTest, SetSelectSegment) {
- proto::SegmentInfo info = AddSegmentInfo(
- &db_entries_, configs_.at(0).get(),
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ proto::SegmentInfo info =
+ AddSegmentInfo(&db_entries_, configs_.at(0).get(),
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
SetUpProxy();
service_proxy_impl_->OnServiceStatusChanged(true, 7);
@@ -279,8 +279,8 @@ TEST_F(ServiceProxyImplTest, SetSelectSegment) {
service_proxy_impl_->SetSelectedSegment(
kTestSegmentationKey,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
- ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+ ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
static_cast<FakeSegmentSelectorImpl*>(
segment_selectors_[kTestSegmentationKey].get())
->new_selection());
diff --git a/chromium/components/segmentation_platform/internal/signals/history_delegate_impl.cc b/chromium/components/segmentation_platform/internal/signals/history_delegate_impl.cc
index ce8fff80762..61ae8ed91bf 100644
--- a/chromium/components/segmentation_platform/internal/signals/history_delegate_impl.cc
+++ b/chromium/components/segmentation_platform/internal/signals/history_delegate_impl.cc
@@ -7,16 +7,9 @@
#include "base/hash/hash.h"
#include "components/history/core/browser/history_service.h"
#include "components/segmentation_platform/internal/database/ukm_database.h"
+#include "components/segmentation_platform/internal/database/ukm_url_table.h"
namespace segmentation_platform {
-namespace {
-
-// TODO(ssid): This should be moved to URL table instead.
-UrlId GenerateUrlId(const GURL& url) {
- return UrlId::FromUnsafeValue(base::PersistentHash(url.spec()));
-}
-
-} // namespace
HistoryDelegateImpl::HistoryDelegateImpl(
history::HistoryService* history_service,
@@ -31,19 +24,19 @@ HistoryDelegateImpl::~HistoryDelegateImpl() {
void HistoryDelegateImpl::OnUrlAdded(const GURL& url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- cached_history_urls_.insert(GenerateUrlId(url));
+ cached_history_urls_.insert(UkmUrlTable::GenerateUrlId(url));
}
void HistoryDelegateImpl::OnUrlRemoved(const std::vector<GURL>& urls) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (const GURL& url : urls) {
- cached_history_urls_.erase(GenerateUrlId(url));
+ cached_history_urls_.erase(UkmUrlTable::GenerateUrlId(url));
}
}
bool HistoryDelegateImpl::FastCheckUrl(const GURL& url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return cached_history_urls_.count(GenerateUrlId(url));
+ return cached_history_urls_.count(UkmUrlTable::GenerateUrlId(url));
}
void HistoryDelegateImpl::FindUrlInHistory(
@@ -53,8 +46,8 @@ void HistoryDelegateImpl::FindUrlInHistory(
history_service_->QueryURL(
url, /* want_visits=*/false,
base::BindOnce(&HistoryDelegateImpl::OnHistoryQueryResult,
- weak_factory_.GetWeakPtr(), GenerateUrlId(url),
- std::move(callback)),
+ weak_factory_.GetWeakPtr(),
+ UkmUrlTable::GenerateUrlId(url), std::move(callback)),
&task_tracker_);
}
diff --git a/chromium/components/segmentation_platform/internal/signals/history_service_observer.cc b/chromium/components/segmentation_platform/internal/signals/history_service_observer.cc
index d00f5448745..4a61939852b 100644
--- a/chromium/components/segmentation_platform/internal/signals/history_service_observer.cc
+++ b/chromium/components/segmentation_platform/internal/signals/history_service_observer.cc
@@ -4,22 +4,33 @@
#include "components/segmentation_platform/internal/signals/history_service_observer.h"
+#include "base/metrics/user_metrics.h"
+#include "base/trace_event/trace_event.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h"
+#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/database/storage_service.h"
#include "components/segmentation_platform/internal/signals/history_delegate_impl.h"
#include "components/segmentation_platform/internal/signals/url_signal_handler.h"
+#include "components/segmentation_platform/internal/ukm_data_manager.h"
namespace segmentation_platform {
HistoryServiceObserver::HistoryServiceObserver(
history::HistoryService* history_service,
- UrlSignalHandler* url_signal_handler)
- : url_signal_handler_(url_signal_handler),
+ StorageService* storage_service,
+ base::RepeatingClosure models_refresh_callback)
+ : storage_service_(storage_service),
+ url_signal_handler_(
+ storage_service->ukm_data_manager()->GetOrCreateUrlHandler()),
+ models_refresh_callback_(models_refresh_callback),
history_delegate_(
std::make_unique<HistoryDelegateImpl>(history_service,
- url_signal_handler)) {
+ url_signal_handler_)) {
history_observation_.Observe(history_service);
}
+HistoryServiceObserver::HistoryServiceObserver()
+ : storage_service_(nullptr), url_signal_handler_(nullptr) {}
HistoryServiceObserver::~HistoryServiceObserver() = default;
@@ -27,7 +38,6 @@ void HistoryServiceObserver::OnURLVisited(
history::HistoryService* history_service,
ui::PageTransition transition,
const history::URLRow& row,
- const history::RedirectList& redirects,
base::Time visit_time) {
url_signal_handler_->OnHistoryVisit(row.url());
history_delegate_->OnUrlAdded(row.url());
@@ -36,11 +46,64 @@ void HistoryServiceObserver::OnURLVisited(
void HistoryServiceObserver::OnURLsDeleted(
history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) {
+ TRACE_EVENT0("segmentation_platform",
+ "HistoryServiceObserver::OnURLsDeleted");
+
+ // If the history deletion was not from expiration or if the whole history
+ // database was removed, delete the segment results computed based on URL
+ // data.
+ if (deletion_info.IsAllHistory() || !deletion_info.is_from_expiration()) {
+ base::RecordAction(
+ base::UserMetricsAction("SegmentationPurgeTriggeredByHistoryDelete"));
+ DeleteResultsForHistoryBasedSegments();
+ }
+
+ if (deletion_info.IsAllHistory()) {
+ url_signal_handler_->OnUrlsRemovedFromHistory({}, /*all_urls=*/true);
+ return;
+ }
std::vector<GURL> urls;
for (const auto& info : deletion_info.deleted_rows())
urls.push_back(info.url());
- url_signal_handler_->OnUrlsRemovedFromHistory(urls);
+ url_signal_handler_->OnUrlsRemovedFromHistory(urls, /*all_urls=*/false);
history_delegate_->OnUrlRemoved(urls);
}
+void HistoryServiceObserver::SetHistoryBasedSegments(
+ base::flat_set<proto::SegmentId>&& history_based_segments) {
+ history_based_segments_ = std::move(history_based_segments);
+ // If a delete is pending, clear the results now.
+ if (pending_deletion_based_on_history_based_segments_) {
+ DeleteResultsForHistoryBasedSegments();
+
+ // Only clear results once on first init. This method can be called multiple
+ // times during the session when model updates.
+ pending_deletion_based_on_history_based_segments_ = false;
+ }
+}
+
+void HistoryServiceObserver::DeleteResultsForHistoryBasedSegments() {
+ if (!history_based_segments_) {
+ // Set the delete flag to clear the history based results when
+ // SetHistoryBasedSegments() is called.
+ pending_deletion_based_on_history_based_segments_ = true;
+ return;
+ }
+ for (const auto segment_id : *history_based_segments_) {
+ storage_service_->segment_info_database()->SaveSegmentResult(
+ segment_id, absl::nullopt, base::DoNothing());
+ }
+
+ // If a model refresh was recently posted, then cancel the task and restart
+ // the 30 second timer. This is to avoid running models often when user clears
+ // multiple history entries at once.
+ if (posted_model_refresh_task_) {
+ posted_model_refresh_task_->Cancel();
+ }
+ posted_model_refresh_task_ = std::make_unique<base::CancelableOnceClosure>(
+ base::BindOnce(models_refresh_callback_));
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, posted_model_refresh_task_->callback(), base::Minutes(1));
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/signals/history_service_observer.h b/chromium/components/segmentation_platform/internal/signals/history_service_observer.h
index 0c66f28431b..75adbccdcd4 100644
--- a/chromium/components/segmentation_platform/internal/signals/history_service_observer.h
+++ b/chromium/components/segmentation_platform/internal/signals/history_service_observer.h
@@ -5,22 +5,30 @@
#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SIGNALS_HISTORY_SERVICE_OBSERVER_H_
#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SIGNALS_HISTORY_SERVICE_OBSERVER_H_
+#include "base/cancelable_callback.h"
+#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace segmentation_platform {
class HistoryDelegateImpl;
+class StorageService;
class UrlSignalHandler;
// Observes history service for visits.
class HistoryServiceObserver : public history::HistoryServiceObserver {
public:
HistoryServiceObserver(history::HistoryService* history_service,
- UrlSignalHandler* url_signal_handler);
+ StorageService* storage_service,
+ base::RepeatingClosure models_refresh_callback);
+ // For tests.
+ HistoryServiceObserver();
~HistoryServiceObserver() override;
HistoryServiceObserver(HistoryServiceObserver&) = delete;
@@ -30,13 +38,28 @@ class HistoryServiceObserver : public history::HistoryServiceObserver {
void OnURLVisited(history::HistoryService* history_service,
ui::PageTransition transition,
const history::URLRow& row,
- const history::RedirectList& redirects,
base::Time visit_time) override;
void OnURLsDeleted(history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) override;
+ // Sets the list of segment IDs that are based on history data.
+ virtual void SetHistoryBasedSegments(
+ base::flat_set<proto::SegmentId>&& history_based_segments);
+
private:
- raw_ptr<UrlSignalHandler> url_signal_handler_;
+ void DeleteResultsForHistoryBasedSegments();
+
+ const raw_ptr<StorageService> storage_service_;
+ const raw_ptr<UrlSignalHandler> url_signal_handler_;
+
+ // List of segment IDs that depend on history data, that will be cleared when
+ // history is deleted.
+ absl::optional<base::flat_set<proto::SegmentId>> history_based_segments_;
+ bool pending_deletion_based_on_history_based_segments_ = false;
+
+ base::RepeatingClosure models_refresh_callback_;
+ std::unique_ptr<base::CancelableOnceClosure> posted_model_refresh_task_;
+
std::unique_ptr<HistoryDelegateImpl> history_delegate_;
base::ScopedObservation<history::HistoryService,
history::HistoryServiceObserver>
diff --git a/chromium/components/segmentation_platform/internal/signals/signal_filter_processor.cc b/chromium/components/segmentation_platform/internal/signals/signal_filter_processor.cc
index 808f09e44aa..32262b01e5a 100644
--- a/chromium/components/segmentation_platform/internal/signals/signal_filter_processor.cc
+++ b/chromium/components/segmentation_platform/internal/signals/signal_filter_processor.cc
@@ -7,12 +7,16 @@
#include <set>
#include "base/logging.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/database/storage_service.h"
#include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
+#include "components/segmentation_platform/internal/signals/history_service_observer.h"
+#include "components/segmentation_platform/internal/signals/signal_handler.h"
#include "components/segmentation_platform/internal/signals/ukm_config.h"
#include "components/segmentation_platform/internal/signals/user_action_signal_handler.h"
#include "components/segmentation_platform/internal/stats.h"
@@ -29,13 +33,16 @@ class FilterExtractor {
const proto::SegmentInfo& segment_info = info->segment_info;
const auto& metadata = segment_info.model_metadata();
AddUmaFeatures(metadata);
- AddUkmFeatures(metadata);
+ if (AddUkmFeatures(metadata)) {
+ history_based_segments.insert(segment_info.segment_id());
+ }
}
}
std::set<uint64_t> user_actions;
std::set<std::pair<std::string, proto::SignalType>> histograms;
UkmConfig ukm_config;
+ base::flat_set<SegmentId> history_based_segments;
private:
void AddUmaFeatures(const proto::SegmentationModelMetadata& metadata) {
@@ -64,7 +71,8 @@ class FilterExtractor {
}
}
- void AddUkmFeatures(const proto::SegmentationModelMetadata& metadata) {
+ bool AddUkmFeatures(const proto::SegmentationModelMetadata& metadata) {
+ bool has_ukm_features = false;
for (const auto& feature : metadata.input_features()) {
for (const auto& ukm_event :
feature.sql_feature().signal_filter().ukm_events()) {
@@ -73,32 +81,32 @@ class FilterExtractor {
metrics.insert(UkmMetricHash::FromUnsafeValue(metric));
ukm_config.AddEvent(
UkmEventHash::FromUnsafeValue(ukm_event.event_hash()), metrics);
+ has_ukm_features = true;
}
}
+ return has_ukm_features;
}
};
} // namespace
SignalFilterProcessor::SignalFilterProcessor(
- SegmentInfoDatabase* segment_database,
+ StorageService* storage_service,
UserActionSignalHandler* user_action_signal_handler,
HistogramSignalHandler* histogram_signal_handler,
- UkmDataManager* ukm_data_manager,
- DefaultModelManager* default_model_manager,
- const std::vector<OptimizationTarget>& segment_ids)
- : segment_database_(segment_database),
+ HistoryServiceObserver* history_observer,
+ const std::vector<SegmentId>& segment_ids)
+ : storage_service_(storage_service),
user_action_signal_handler_(user_action_signal_handler),
histogram_signal_handler_(histogram_signal_handler),
- ukm_data_manager_(ukm_data_manager),
- default_model_manager_(default_model_manager),
+ history_observer_(history_observer),
segment_ids_(segment_ids) {}
SignalFilterProcessor::~SignalFilterProcessor() = default;
void SignalFilterProcessor::OnSignalListUpdated() {
- default_model_manager_->GetAllSegmentInfoFromBothModels(
- segment_ids_, segment_database_,
+ storage_service_->default_model_manager()->GetAllSegmentInfoFromBothModels(
+ segment_ids_, storage_service_->segment_info_database(),
base::BindOnce(&SignalFilterProcessor::FilterSignals,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -113,13 +121,23 @@ void SignalFilterProcessor::FilterSignals(
user_action_signal_handler_->SetRelevantUserActions(
std::move(extractor.user_actions));
histogram_signal_handler_->SetRelevantHistograms(extractor.histograms);
- ukm_data_manager_->StartObservingUkm(extractor.ukm_config);
+ storage_service_->ukm_data_manager()->StartObservingUkm(extractor.ukm_config);
+
+ if (history_observer_) {
+ history_observer_->SetHistoryBasedSegments(
+ std::move(extractor.history_based_segments));
+ }
+ for (const auto& segment_info : segment_infos) {
+ storage_service_->signal_storage_config()->OnSignalCollectionStarted(
+ segment_info->segment_info.model_metadata());
+ }
}
void SignalFilterProcessor::EnableMetrics(bool enable_metrics) {
user_action_signal_handler_->EnableMetrics(enable_metrics);
histogram_signal_handler_->EnableMetrics(enable_metrics);
- ukm_data_manager_->PauseOrResumeObservation(/*pause=*/!enable_metrics);
+ storage_service_->ukm_data_manager()->PauseOrResumeObservation(
+ /*pause=*/!enable_metrics);
}
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/signals/signal_filter_processor.h b/chromium/components/segmentation_platform/internal/signals/signal_filter_processor.h
index 1465966a973..ff39f4c2abd 100644
--- a/chromium/components/segmentation_platform/internal/signals/signal_filter_processor.h
+++ b/chromium/components/segmentation_platform/internal/signals/signal_filter_processor.h
@@ -7,29 +7,28 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/execution/default_model_manager.h"
-
-using optimization_guide::proto::OptimizationTarget;
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace segmentation_platform {
+using proto::SegmentId;
+
class HistogramSignalHandler;
-class SegmentInfoDatabase;
+class HistoryServiceObserver;
+class StorageService;
class UserActionSignalHandler;
-class UkmDataManager;
// Responsible for listening to the metadata updates for the models and
// registers various signal handlers for the relevant UMA signals specified in
// the metadata.
class SignalFilterProcessor {
public:
- SignalFilterProcessor(SegmentInfoDatabase* segment_database,
+ SignalFilterProcessor(StorageService* storage_service,
UserActionSignalHandler* user_action_signal_handler,
HistogramSignalHandler* histogram_signal_handler,
- UkmDataManager* ukm_data_manager,
- DefaultModelManager* default_model_manager,
- const std::vector<OptimizationTarget>& segment_ids);
+ HistoryServiceObserver* history_observer,
+ const std::vector<SegmentId>& segment_ids);
~SignalFilterProcessor();
// Disallow copy/assign.
@@ -50,12 +49,11 @@ class SignalFilterProcessor {
private:
void FilterSignals(DefaultModelManager::SegmentInfoList segment_infos);
- raw_ptr<SegmentInfoDatabase> segment_database_;
- raw_ptr<UserActionSignalHandler> user_action_signal_handler_;
- raw_ptr<HistogramSignalHandler> histogram_signal_handler_;
- raw_ptr<UkmDataManager> ukm_data_manager_;
- raw_ptr<DefaultModelManager> default_model_manager_;
- std::vector<OptimizationTarget> segment_ids_;
+ const raw_ptr<StorageService> storage_service_;
+ const raw_ptr<UserActionSignalHandler> user_action_signal_handler_;
+ const raw_ptr<HistogramSignalHandler> histogram_signal_handler_;
+ const raw_ptr<HistoryServiceObserver> history_observer_;
+ std::vector<SegmentId> segment_ids_;
base::WeakPtrFactory<SignalFilterProcessor> weak_ptr_factory_{this};
};
diff --git a/chromium/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc b/chromium/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc
index dfc35a90071..126c51c0586 100644
--- a/chromium/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc
@@ -8,7 +8,11 @@
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "components/segmentation_platform/internal/database/mock_signal_storage_config.h"
#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/database/signal_database.h"
+#include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/database/storage_service.h"
#include "components/segmentation_platform/internal/database/test_segment_info_database.h"
#include "components/segmentation_platform/internal/execution/default_model_manager.h"
#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
@@ -16,6 +20,7 @@
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
+#include "components/segmentation_platform/internal/signals/history_service_observer.h"
#include "components/segmentation_platform/internal/signals/mock_histogram_signal_handler.h"
#include "components/segmentation_platform/internal/signals/user_action_signal_handler.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -24,16 +29,17 @@
using testing::_;
using testing::Contains;
using testing::Invoke;
+using testing::IsEmpty;
using testing::SaveArg;
namespace segmentation_platform {
namespace {
-UkmEventHash TestEvent(uint64_t val) {
+constexpr UkmEventHash TestEvent(uint64_t val) {
return UkmEventHash::FromUnsafeValue(val);
}
-UkmMetricHash TestMetric(uint64_t val) {
+constexpr UkmMetricHash TestMetric(uint64_t val) {
return UkmMetricHash::FromUnsafeValue(val);
}
@@ -45,15 +51,21 @@ class MockUserActionSignalHandler : public UserActionSignalHandler {
MOCK_METHOD(void, EnableMetrics, (bool));
};
+class MockHistoryObserver : public HistoryServiceObserver {
+ public:
+ MOCK_METHOD1(SetHistoryBasedSegments,
+ void(base::flat_set<proto::SegmentId>&& history_based_segments));
+};
+
// Noop version. For database calls, just passes the calls to the DB.
class TestDefaultModelManager : public DefaultModelManager {
public:
TestDefaultModelManager()
- : DefaultModelManager(nullptr, std::vector<OptimizationTarget>()) {}
+ : DefaultModelManager(nullptr, std::vector<SegmentId>()) {}
~TestDefaultModelManager() override = default;
void GetAllSegmentInfoFromDefaultModel(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
MultipleSegmentInfoCallback callback) override {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
@@ -61,7 +73,7 @@ class TestDefaultModelManager : public DefaultModelManager {
}
void GetAllSegmentInfoFromBothModels(
- const std::vector<OptimizationTarget>& segment_ids,
+ const std::vector<SegmentId>& segment_ids,
SegmentInfoDatabase* segment_database,
MultipleSegmentInfoCallback callback) override {
segment_database->GetSegmentInfoForSegments(
@@ -92,44 +104,56 @@ class SignalFilterProcessorTest : public testing::Test {
base::SetRecordActionTaskRunner(
task_environment_.GetMainThreadTaskRunner());
- std::vector<OptimizationTarget> segment_ids(
- {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE});
- segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
+ std::vector<SegmentId> segment_ids(
+ {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE});
user_action_signal_handler_ =
std::make_unique<MockUserActionSignalHandler>();
histogram_signal_handler_ = std::make_unique<MockHistogramSignalHandler>();
+ history_observer_ = std::make_unique<MockHistoryObserver>();
+
+ auto moved_segment_database =
+ std::make_unique<test::TestSegmentInfoDatabase>();
+ segment_database_ = moved_segment_database.get();
+ auto moved_signal_config = std::make_unique<MockSignalStorageConfig>();
+ signal_storage_config_ = moved_signal_config.get();
ukm_data_manager_ = std::make_unique<MockUkmDataManager>();
- default_model_manager_ = std::make_unique<TestDefaultModelManager>();
+ storage_service_ = std::make_unique<StorageService>(
+ std::move(moved_segment_database), nullptr,
+ std::move(moved_signal_config),
+ std::make_unique<TestDefaultModelManager>(), ukm_data_manager_.get());
+
signal_filter_processor_ = std::make_unique<SignalFilterProcessor>(
- segment_database_.get(), user_action_signal_handler_.get(),
- histogram_signal_handler_.get(), ukm_data_manager_.get(),
- default_model_manager_.get(), segment_ids);
+ storage_service_.get(), user_action_signal_handler_.get(),
+ histogram_signal_handler_.get(), history_observer_.get(), segment_ids);
}
base::test::TaskEnvironment task_environment_;
- std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_;
std::unique_ptr<MockUserActionSignalHandler> user_action_signal_handler_;
std::unique_ptr<MockHistogramSignalHandler> histogram_signal_handler_;
- std::unique_ptr<TestDefaultModelManager> default_model_manager_;
std::unique_ptr<SignalFilterProcessor> signal_filter_processor_;
+ std::unique_ptr<MockHistoryObserver> history_observer_;
std::unique_ptr<MockUkmDataManager> ukm_data_manager_;
+ std::unique_ptr<StorageService> storage_service_;
+ raw_ptr<test::TestSegmentInfoDatabase> segment_database_;
+ raw_ptr<MockSignalStorageConfig> signal_storage_config_;
};
TEST_F(SignalFilterProcessorTest, UserActionRegistrationFlow) {
std::string kUserActionName1 = "some_action_1";
segment_database_->AddUserActionFeature(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- kUserActionName1, 0, 0, proto::Aggregation::COUNT);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, kUserActionName1, 0,
+ 0, proto::Aggregation::COUNT);
std::string kUserActionName2 = "some_action_2";
segment_database_->AddUserActionFeature(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
- kUserActionName2, 0, 0, proto::Aggregation::COUNT);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, kUserActionName2, 0, 0,
+ proto::Aggregation::COUNT);
std::set<uint64_t> actions;
EXPECT_CALL(*user_action_signal_handler_, SetRelevantUserActions(_))
.Times(1)
.WillOnce(SaveArg<0>(&actions));
+ EXPECT_CALL(*signal_storage_config_, OnSignalCollectionStarted(_)).Times(2);
signal_filter_processor_->OnSignalListUpdated();
ASSERT_THAT(actions, Contains(base::HashMetricName(kUserActionName1)));
@@ -139,21 +163,22 @@ TEST_F(SignalFilterProcessorTest, UserActionRegistrationFlow) {
TEST_F(SignalFilterProcessorTest, HistogramRegistrationFlow) {
std::string kHistogramName1 = "some_histogram_1";
segment_database_->AddHistogramValueFeature(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- kHistogramName1, 1, 1, proto::Aggregation::COUNT);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, kHistogramName1, 1,
+ 1, proto::Aggregation::COUNT);
std::string kHistogramName2 = "some_histogram_2";
segment_database_->AddHistogramValueFeature(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
- kHistogramName2, 1, 1, proto::Aggregation::COUNT);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, kHistogramName2, 1, 1,
+ proto::Aggregation::COUNT);
std::string kHistogramName3 = "some_histogram_3";
segment_database_->AddHistogramEnumFeature(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
- kHistogramName3, 1, 1, proto::Aggregation::COUNT, {3, 4});
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, kHistogramName3, 1, 1,
+ proto::Aggregation::COUNT, {3, 4});
std::set<std::pair<std::string, proto::SignalType>> histograms;
EXPECT_CALL(*histogram_signal_handler_, SetRelevantHistograms(_))
.Times(1)
.WillOnce(SaveArg<0>(&histograms));
+ EXPECT_CALL(*signal_storage_config_, OnSignalCollectionStarted(_)).Times(2);
signal_filter_processor_->OnSignalListUpdated();
ASSERT_THAT(histograms,
@@ -168,6 +193,35 @@ TEST_F(SignalFilterProcessorTest, HistogramRegistrationFlow) {
}
TEST_F(SignalFilterProcessorTest, UkmMetricsConfig) {
+ const SegmentId kSegmentId =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+ const UkmEventHash kEvent1 = TestEvent(10);
+ const UkmEventHash kEvent2 = TestEvent(11);
+ const std::array<UkmMetricHash, 3> kMetrics1_1{
+ TestMetric(100), TestMetric(101), TestMetric(102)};
+ const std::array<UkmMetricHash, 3> kMetrics1_2{
+ TestMetric(100), TestMetric(104), TestMetric(105)};
+ const std::array<UkmMetricHash, 3> kMetrics2{TestMetric(103), TestMetric(104),
+ TestMetric(105)};
+ const std::array<MetadataWriter::SqlFeature::EventAndMetrics, 2> kConfig1{
+ MetadataWriter::SqlFeature::EventAndMetrics{
+ .event_hash = kEvent1,
+ .metrics = kMetrics1_1.data(),
+ .metrics_size = kMetrics1_1.size()},
+ MetadataWriter::SqlFeature::EventAndMetrics{
+ .event_hash = kEvent2,
+ .metrics = kMetrics2.data(),
+ .metrics_size = kMetrics2.size()}};
+ const std::array<MetadataWriter::SqlFeature::EventAndMetrics, 1> kConfig2{
+ MetadataWriter::SqlFeature::EventAndMetrics{
+ .event_hash = kEvent1,
+ .metrics = kMetrics1_2.data(),
+ .metrics_size = kMetrics1_2.size()}};
+ MetadataWriter::SqlFeature feature1{
+ .sql = "", .events = kConfig1.data(), .events_size = kConfig1.size()};
+ MetadataWriter::SqlFeature feature2{
+ .sql = "", .events = kConfig2.data(), .events_size = kConfig2.size()};
+
EXPECT_CALL(*histogram_signal_handler_,
SetRelevantHistograms(
std::set<std::pair<std::string, proto::SignalType>>()))
@@ -181,31 +235,31 @@ TEST_F(SignalFilterProcessorTest, UkmMetricsConfig) {
.WillOnce(Invoke([](const UkmConfig& actual_config) {
EXPECT_EQ(actual_config, UkmConfig());
}));
+ EXPECT_CALL(*history_observer_, SetHistoryBasedSegments(IsEmpty()));
signal_filter_processor_->OnSignalListUpdated();
UkmConfig config1;
- config1.AddEvent(TestEvent(10),
- {TestMetric(100), TestMetric(101), TestMetric(102)});
- config1.AddEvent(TestEvent(11),
- {TestMetric(103), TestMetric(104), TestMetric(105)});
- segment_database_->AddSqlFeature(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, "",
- config1);
+ config1.AddEvent(kEvent1, base::flat_set<UkmMetricHash>(kMetrics1_1.begin(),
+ kMetrics1_1.end()));
+ config1.AddEvent(kEvent2, base::flat_set<UkmMetricHash>(kMetrics2.begin(),
+ kMetrics2.end()));
+ segment_database_->AddSqlFeature(kSegmentId, feature1);
+
UkmConfig config2;
- config2.AddEvent(TestEvent(10),
- {TestMetric(100), TestMetric(104), TestMetric(105)});
- segment_database_->AddSqlFeature(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, "",
- config2);
+ config2.AddEvent(kEvent1, base::flat_set<UkmMetricHash>(kMetrics1_2.begin(),
+ kMetrics1_2.end()));
+ segment_database_->AddSqlFeature(kSegmentId, feature2);
config2.Merge(config1);
- UkmConfig actual_config;
EXPECT_CALL(*ukm_data_manager_, StartObservingUkm(_))
.Times(1)
.WillOnce(Invoke([&config2](const UkmConfig& actual_config) {
EXPECT_EQ(actual_config, config2);
}));
+ EXPECT_CALL(*history_observer_,
+ SetHistoryBasedSegments(base::flat_set<SegmentId>({kSegmentId})));
+ EXPECT_CALL(*signal_storage_config_, OnSignalCollectionStarted(_));
signal_filter_processor_->OnSignalListUpdated();
}
diff --git a/chromium/components/segmentation_platform/internal/signals/signal_handler.cc b/chromium/components/segmentation_platform/internal/signals/signal_handler.cc
index 8190b48cc37..42e9a806fed 100644
--- a/chromium/components/segmentation_platform/internal/signals/signal_handler.cc
+++ b/chromium/components/segmentation_platform/internal/signals/signal_handler.cc
@@ -4,6 +4,7 @@
#include "components/segmentation_platform/internal/signals/signal_handler.h"
+#include "components/segmentation_platform/internal/database/storage_service.h"
#include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
#include "components/segmentation_platform/internal/signals/history_service_observer.h"
#include "components/segmentation_platform/internal/signals/signal_filter_processor.h"
@@ -15,29 +16,27 @@ namespace segmentation_platform {
SignalHandler::SignalHandler() = default;
SignalHandler::~SignalHandler() = default;
-void SignalHandler::Initialize(
- SignalDatabase* signal_database,
- SegmentInfoDatabase* segment_info_database,
- UkmDataManager* ukm_data_manager,
- history::HistoryService* history_service,
- DefaultModelManager* default_model_manager,
- const std::vector<optimization_guide::proto::OptimizationTarget>&
- segment_ids) {
- user_action_signal_handler_ =
- std::make_unique<UserActionSignalHandler>(signal_database);
- histogram_signal_handler_ =
- std::make_unique<HistogramSignalHandler>(signal_database);
- signal_filter_processor_ = std::make_unique<SignalFilterProcessor>(
- segment_info_database, user_action_signal_handler_.get(),
- histogram_signal_handler_.get(), ukm_data_manager, default_model_manager,
- segment_ids);
-
- if (ukm_data_manager->IsUkmEngineEnabled() && history_service) {
+void SignalHandler::Initialize(StorageService* storage_service,
+ history::HistoryService* history_service,
+ const std::vector<proto::SegmentId>& segment_ids,
+ base::RepeatingClosure models_refresh_callback) {
+ user_action_signal_handler_ = std::make_unique<UserActionSignalHandler>(
+ storage_service->signal_database());
+ histogram_signal_handler_ = std::make_unique<HistogramSignalHandler>(
+ storage_service->signal_database());
+
+ if (storage_service->ukm_data_manager()->IsUkmEngineEnabled() &&
+ history_service) {
// If UKM engine is enabled and history service is not available, then we
// would write metrics without URLs to the database, which is OK.
history_service_observer_ = std::make_unique<HistoryServiceObserver>(
- history_service, ukm_data_manager->GetOrCreateUrlHandler());
+ history_service, storage_service, models_refresh_callback);
}
+
+ signal_filter_processor_ = std::make_unique<SignalFilterProcessor>(
+ storage_service, user_action_signal_handler_.get(),
+ histogram_signal_handler_.get(), history_service_observer_.get(),
+ segment_ids);
}
void SignalHandler::TearDown() {
diff --git a/chromium/components/segmentation_platform/internal/signals/signal_handler.h b/chromium/components/segmentation_platform/internal/signals/signal_handler.h
index cf34f619e38..ac05648f1a7 100644
--- a/chromium/components/segmentation_platform/internal/signals/signal_handler.h
+++ b/chromium/components/segmentation_platform/internal/signals/signal_handler.h
@@ -7,7 +7,8 @@
#include <memory>
-#include "components/optimization_guide/proto/models.pb.h"
+#include "base/callback.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace history {
class HistoryService;
@@ -15,13 +16,10 @@ class HistoryService;
namespace segmentation_platform {
-class DefaultModelManager;
class HistogramSignalHandler;
class HistoryServiceObserver;
-class SegmentInfoDatabase;
-class SignalDatabase;
class SignalFilterProcessor;
-class UkmDataManager;
+class StorageService;
class UserActionSignalHandler;
// Finds and observes the right signals needed for the models, and stores to the
@@ -35,14 +33,10 @@ class SignalHandler {
SignalHandler(SignalHandler&) = delete;
SignalHandler& operator=(SignalHandler&) = delete;
- void Initialize(
- SignalDatabase* signal_database,
- SegmentInfoDatabase* segment_info_database,
- UkmDataManager* ukm_data_manager,
- history::HistoryService* history_service,
- DefaultModelManager* default_model_manager,
- const std::vector<optimization_guide::proto::OptimizationTarget>&
- segment_ids);
+ void Initialize(StorageService* storage_service,
+ history::HistoryService* history_service,
+ const std::vector<proto::SegmentId>& segment_ids,
+ base::RepeatingClosure model_refresh_callback);
void TearDown();
@@ -68,6 +62,7 @@ class SignalHandler {
std::unique_ptr<UserActionSignalHandler> user_action_signal_handler_;
std::unique_ptr<HistogramSignalHandler> histogram_signal_handler_;
std::unique_ptr<SignalFilterProcessor> signal_filter_processor_;
+ // Can be null when UKM engine is disabled.
std::unique_ptr<HistoryServiceObserver> history_service_observer_;
};
diff --git a/chromium/components/segmentation_platform/internal/signals/ukm_observer.cc b/chromium/components/segmentation_platform/internal/signals/ukm_observer.cc
index 356f9f92ef1..3d8eb1cb12b 100644
--- a/chromium/components/segmentation_platform/internal/signals/ukm_observer.cc
+++ b/chromium/components/segmentation_platform/internal/signals/ukm_observer.cc
@@ -6,31 +6,24 @@
#include <cstdint>
-#include "base/rand_util.h"
-#include "components/segmentation_platform/internal/database/ukm_database.h"
+#include "components/segmentation_platform/internal/constants.h"
#include "components/segmentation_platform/internal/signals/ukm_config.h"
-#include "components/segmentation_platform/internal/signals/url_signal_handler.h"
#include "components/segmentation_platform/internal/ukm_data_manager_impl.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
#include "components/ukm/ukm_recorder_impl.h"
#include "services/metrics/public/mojom/ukm_interface.mojom.h"
namespace segmentation_platform {
-UkmObserver::UkmObserver(ukm::UkmRecorderImpl* ukm_recorder,
- UkmDatabase* ukm_database,
- UrlSignalHandler* url_signal_handler,
- UkmDataManagerImpl* ukm_data_manager)
- : ukm_database_(ukm_database),
- url_signal_handler_(url_signal_handler),
- ukm_recorder_(ukm_recorder),
- ukm_data_manager_(ukm_data_manager) {
+UkmObserver::UkmObserver(ukm::UkmRecorderImpl* ukm_recorder)
+ : ukm_recorder_(ukm_recorder), ukm_data_manager_(nullptr) {
// Listen to |OnUkmAllowedStateChanged| event.
ukm_recorder_->AddUkmRecorderObserver(base::flat_set<uint64_t>(), this);
}
UkmObserver::~UkmObserver() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
- ukm_recorder_->RemoveUkmRecorderObserver(this);
+ StopObserving();
}
void UkmObserver::StartObserving(const UkmConfig& config) {
@@ -48,9 +41,14 @@ void UkmObserver::StartObserving(const UkmConfig& config) {
ukm_recorder_->AddUkmRecorderObserver(config_->GetRawObservedEvents(), this);
}
+void UkmObserver::StopObserving() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
+ ukm_recorder_->RemoveUkmRecorderObserver(this);
+}
+
void UkmObserver::OnEntryAdded(ukm::mojom::UkmEntryPtr entry) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
- if (paused_)
+ if (paused_ || !ukm_data_manager_)
return;
// Remove any metric from the entry that is not observed.
@@ -65,16 +63,16 @@ void UkmObserver::OnEntryAdded(ukm::mojom::UkmEntryPtr entry) {
entry->metrics.erase(metric_and_value);
}
}
- ukm_database_->StoreUkmEntry(std::move(entry));
+ ukm_data_manager_->OnEntryAdded(std::move(entry));
}
void UkmObserver::OnUpdateSourceURL(ukm::SourceId source_id,
const std::vector<GURL>& urls) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
- if (paused_)
+ if (paused_ || !ukm_data_manager_)
return;
- url_signal_handler_->OnUkmSourceUpdated(source_id, urls);
+ ukm_data_manager_->OnUkmSourceUpdated(source_id, urls);
}
void UkmObserver::PauseOrResumeObservation(bool pause) {
@@ -83,7 +81,21 @@ void UkmObserver::PauseOrResumeObservation(bool pause) {
}
void UkmObserver::OnUkmAllowedStateChanged(bool allowed) {
- ukm_data_manager_->OnUkmAllowedStateChanged(allowed);
+ base::Time most_recent_allowed = LocalStateHelper::GetInstance().GetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey);
+ if (!allowed) {
+ if (most_recent_allowed != base::Time::Max()) {
+ LocalStateHelper::GetInstance().SetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey, base::Time::Max());
+ }
+ return;
+ }
+ // Update the most recent allowed time if needed.
+ if (most_recent_allowed.is_null() ||
+ most_recent_allowed == base::Time::Max()) {
+ LocalStateHelper::GetInstance().SetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey, base::Time::Now());
+ }
}
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/signals/ukm_observer.h b/chromium/components/segmentation_platform/internal/signals/ukm_observer.h
index f52cad1493e..03e318c0ce8 100644
--- a/chromium/components/segmentation_platform/internal/signals/ukm_observer.h
+++ b/chromium/components/segmentation_platform/internal/signals/ukm_observer.h
@@ -9,6 +9,7 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
+#include "base/time/time.h"
#include "components/ukm/ukm_recorder_observer.h"
namespace ukm {
@@ -17,19 +18,14 @@ class UkmRecorderImpl;
namespace segmentation_platform {
-class UkmDatabase;
-class UrlSignalHandler;
class UkmConfig;
class UkmDataManagerImpl;
-// Observes UKM metrics and URL source and stores the relevant data in UKM
-// database.
+// Observes UKM metrics and URL source and calls UKMDataManager on new
+// entries or on source URL changes.
class UkmObserver : public ukm::UkmRecorderObserver {
public:
- UkmObserver(ukm::UkmRecorderImpl* ukm_recorder,
- UkmDatabase* ukm_database,
- UrlSignalHandler* url_signal_handler,
- UkmDataManagerImpl* ukm_data_manager);
+ explicit UkmObserver(ukm::UkmRecorderImpl* ukm_recorder);
~UkmObserver() override;
UkmObserver(UkmObserver&) = delete;
@@ -44,19 +40,23 @@ class UkmObserver : public ukm::UkmRecorderObserver {
// database when paused.
void PauseOrResumeObservation(bool pause);
+ // Stop observing |ukm_recorder_|.
+ void StopObserving();
+
// UkmRecorderObserver implementation:
void OnEntryAdded(ukm::mojom::UkmEntryPtr entry) override;
void OnUpdateSourceURL(ukm::SourceId source_id,
const std::vector<GURL>& urls) override;
void OnUkmAllowedStateChanged(bool allowed) override;
+ void set_ukm_data_manager(UkmDataManagerImpl* ukm_data_manager) {
+ ukm_data_manager_ = ukm_data_manager;
+ }
+
+ bool is_started_for_testing() const { return !!config_; }
+ bool is_paused_for_testing() const { return paused_; }
+
private:
- // Owned by UkmDataManagerImpl. The manager guarantees that database is
- // deleted after observer.
- raw_ptr<UkmDatabase> ukm_database_;
- // Owned by UkmDataManagerImpl. The manager guarantees that handler is deleted
- // after observer.
- raw_ptr<UrlSignalHandler> url_signal_handler_;
// UkmDataManagerImpl destroys this observer before the UKM service is
// destroyed.
raw_ptr<ukm::UkmRecorderImpl> const ukm_recorder_;
@@ -65,7 +65,7 @@ class UkmObserver : public ukm::UkmRecorderObserver {
std::unique_ptr<UkmConfig> config_;
bool paused_ = false;
- // Class that owns this object.
+ // Class that is notified on new entries or on source URL change.
raw_ptr<UkmDataManagerImpl> ukm_data_manager_;
SEQUENCE_CHECKER(sequence_check_);
diff --git a/chromium/components/segmentation_platform/internal/signals/ukm_observer_unittest.cc b/chromium/components/segmentation_platform/internal/signals/ukm_observer_unittest.cc
index 74f6d1c66c3..6172adbbc67 100644
--- a/chromium/components/segmentation_platform/internal/signals/ukm_observer_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/signals/ukm_observer_unittest.cc
@@ -7,10 +7,13 @@
#include <memory>
#include "base/test/task_environment.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/segmentation_platform/internal/constants.h"
#include "components/segmentation_platform/internal/database/mock_ukm_database.h"
#include "components/segmentation_platform/internal/signals/ukm_config.h"
#include "components/segmentation_platform/internal/signals/url_signal_handler.h"
#include "components/segmentation_platform/internal/ukm_data_manager_impl.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
#include "components/segmentation_platform/public/segmentation_platform_service.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
@@ -66,20 +69,15 @@ class UkmObserverTest : public testing::Test {
~UkmObserverTest() override = default;
void SetUp() override {
- ukm_database_ = std::make_unique<MockUkmDatabase>();
- url_signal_handler_ =
- std::make_unique<UrlSignalHandler>(ukm_database_.get());
+ SegmentationPlatformService::RegisterLocalStatePrefs(prefs_.registry());
+ LocalStateHelper::GetInstance().Initialize(&prefs_);
ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
- ukm_observer_ = std::make_unique<UkmObserver>(
- ukm_recorder_.get(), ukm_database_.get(), url_signal_handler_.get(),
- &ukm_data_manager_);
+ InitializeUkmObserver(true /*is_ukm_allowed*/);
}
void TearDown() override {
ukm_observer_.reset();
ukm_recorder_.reset();
- url_signal_handler_.reset();
- ukm_database_.reset();
}
void ExpectUkmEventFromRecorder(ukm::mojom::UkmEntryPtr entry) {
@@ -95,18 +93,29 @@ class UkmObserverTest : public testing::Test {
wait_for_record.Run();
}
+ void InitializeUkmObserver(bool is_ukm_allowed) {
+ ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_.get());
+ ukm_observer_->OnUkmAllowedStateChanged(is_ukm_allowed);
+ auto ukm_database = std::make_unique<MockUkmDatabase>();
+ ukm_database_ = ukm_database.get();
+ ukm_data_manager_ = std::make_unique<UkmDataManagerImpl>();
+ ukm_data_manager_->InitializeForTesting(std::move(ukm_database),
+ ukm_observer_.get());
+ }
+
UkmObserver& ukm_observer() { return *ukm_observer_; }
ukm::TestUkmRecorder& ukm_recorder() { return *ukm_recorder_; }
MockUkmDatabase& ukm_database() { return *ukm_database_; }
+ UkmDataManagerImpl& ukm_data_manager() { return *ukm_data_manager_; }
private:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
- std::unique_ptr<MockUkmDatabase> ukm_database_;
- std::unique_ptr<UrlSignalHandler> url_signal_handler_;
std::unique_ptr<ukm::TestUkmRecorder> ukm_recorder_;
std::unique_ptr<UkmObserver> ukm_observer_;
- UkmDataManagerImpl ukm_data_manager_;
+ base::raw_ptr<MockUkmDatabase> ukm_database_;
+ std::unique_ptr<UkmDataManagerImpl> ukm_data_manager_;
+ TestingPrefServiceSimple prefs_;
};
TEST_F(UkmObserverTest, EmptyConfig) {
@@ -214,7 +223,7 @@ TEST_F(UkmObserverTest, ObservationFromRecorder) {
config.AddEvent(TestEvent(PageLoad::kEntryNameHash),
{TestMetric(PageLoad::kCpuTimeNameHash),
TestMetric(PageLoad::kIsNewBookmarkNameHash)});
- observer.StartObserving(config);
+ ukm_data_manager().StartObservingUkm(config);
ExpectUkmEventFromRecorder(GetSamplePageLoadEntry());
@@ -239,4 +248,41 @@ TEST_F(UkmObserverTest, ObservationFromRecorder) {
ExpectUkmEventFromRecorder(GetSamplePaintPreviewEntry());
}
+// Tests that the most recent time for UKM allowed state is correctly set and
+// read.
+TEST_F(UkmObserverTest, GetUkmMostRecentAllowedTime) {
+ LocalStateHelper& local_state_helper = LocalStateHelper::GetInstance();
+ // Without pref entry, the |is_ukm_allowed| param passed to UkmObserver ctor
+ // will determine the value to be returned.
+ EXPECT_LE(
+ local_state_helper.GetPrefTime(kSegmentationUkmMostRecentAllowedTimeKey),
+ base::Time::Now());
+ InitializeUkmObserver(false /*is_ukm_allowed*/);
+ EXPECT_EQ(base::Time::Max(), local_state_helper.GetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey));
+
+ ukm_observer().OnUkmAllowedStateChanged(true);
+ EXPECT_LE(
+ local_state_helper.GetPrefTime(kSegmentationUkmMostRecentAllowedTimeKey),
+ base::Time::Now());
+
+ // Change the allowed state to false, the start time should now be set to
+ // Time::Max().
+ ukm_recorder().OnUkmAllowedStateChanged(false);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(base::Time::Max(), local_state_helper.GetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey));
+
+ // Change the allowed state to true, the new start time should be close to
+ // now.
+ base::Time now = base::Time::Now();
+ ukm_recorder().OnUkmAllowedStateChanged(true);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_LE(now, local_state_helper.GetPrefTime(
+ kSegmentationUkmMostRecentAllowedTimeKey));
+ EXPECT_LE(
+ local_state_helper.GetPrefTime(kSegmentationUkmMostRecentAllowedTimeKey),
+ base::Time::Now());
+}
+
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/signals/url_signal_handler.cc b/chromium/components/segmentation_platform/internal/signals/url_signal_handler.cc
index c83dda2cd73..12379854375 100644
--- a/chromium/components/segmentation_platform/internal/signals/url_signal_handler.cc
+++ b/chromium/components/segmentation_platform/internal/signals/url_signal_handler.cc
@@ -34,9 +34,10 @@ void UrlSignalHandler::OnHistoryVisit(const GURL& url) {
ukm_database_->OnUrlValidated(url);
}
-void UrlSignalHandler::OnUrlsRemovedFromHistory(const std::vector<GURL>& urls) {
+void UrlSignalHandler::OnUrlsRemovedFromHistory(const std::vector<GURL>& urls,
+ bool all_urls) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- ukm_database_->RemoveUrls(urls);
+ ukm_database_->RemoveUrls(urls, all_urls);
}
void UrlSignalHandler::AddHistoryDelegate(HistoryDelegate* history_delegate) {
diff --git a/chromium/components/segmentation_platform/internal/signals/url_signal_handler.h b/chromium/components/segmentation_platform/internal/signals/url_signal_handler.h
index 79abc1e3cec..f54bd4b5b1e 100644
--- a/chromium/components/segmentation_platform/internal/signals/url_signal_handler.h
+++ b/chromium/components/segmentation_platform/internal/signals/url_signal_handler.h
@@ -55,7 +55,7 @@ class UrlSignalHandler {
void OnHistoryVisit(const GURL& url);
// Called when |urls| are removed from the history database.
- void OnUrlsRemovedFromHistory(const std::vector<GURL>& urls);
+ void OnUrlsRemovedFromHistory(const std::vector<GURL>& urls, bool all_urls);
// Add/Remove history delegates.
void AddHistoryDelegate(HistoryDelegate* history_delegate);
diff --git a/chromium/components/segmentation_platform/internal/signals/url_signal_handler_unittest.cc b/chromium/components/segmentation_platform/internal/signals/url_signal_handler_unittest.cc
index 574b2529259..aca243d182e 100644
--- a/chromium/components/segmentation_platform/internal/signals/url_signal_handler_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/signals/url_signal_handler_unittest.cc
@@ -157,8 +157,8 @@ TEST_F(UrlSignalHandlerTest, Observation) {
EXPECT_CALL(ukm_database(), OnUrlValidated(kUrl2));
signal_handler().OnHistoryVisit(kUrl2);
std::vector<GURL> list({kUrl1, kUrl2});
- EXPECT_CALL(ukm_database(), RemoveUrls(list));
- signal_handler().OnUrlsRemovedFromHistory(list);
+ EXPECT_CALL(ukm_database(), RemoveUrls(list, false));
+ signal_handler().OnUrlsRemovedFromHistory(list, false);
}
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/signals/user_action_signal_handler.cc b/chromium/components/segmentation_platform/internal/signals/user_action_signal_handler.cc
index b97d58de335..d2d9170c4c7 100644
--- a/chromium/components/segmentation_platform/internal/signals/user_action_signal_handler.cc
+++ b/chromium/components/segmentation_platform/internal/signals/user_action_signal_handler.cc
@@ -19,12 +19,12 @@ UserActionSignalHandler::UserActionSignalHandler(
}
UserActionSignalHandler::~UserActionSignalHandler() {
- if (metrics_enabled_)
+ if (metrics_enabled_ && base::GetRecordActionTaskRunner())
base::RemoveActionCallback(action_callback_);
}
void UserActionSignalHandler::EnableMetrics(bool enable_metrics) {
- if (metrics_enabled_ == enable_metrics)
+ if (metrics_enabled_ == enable_metrics || !base::GetRecordActionTaskRunner())
return;
metrics_enabled_ = enable_metrics;
diff --git a/chromium/components/segmentation_platform/internal/stats.cc b/chromium/components/segmentation_platform/internal/stats.cc
index 93d52eae7b9..40333fdda75 100644
--- a/chromium/components/segmentation_platform/internal/stats.cc
+++ b/chromium/components/segmentation_platform/internal/stats.cc
@@ -9,38 +9,12 @@
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace segmentation_platform::stats {
namespace {
-// Should map to SegmentationModel variant in
-// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
-std::string OptimizationTargetToHistogramVariant(
- OptimizationTarget segment_id) {
- switch (segment_id) {
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
- return "NewTab";
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
- return "Share";
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
- return "Voice";
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
- return "Dummy";
- case OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
- return "ChromeStartAndroid";
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
- return "QueryTiles";
- case OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
- return "ChromeLowUserEngagement";
- default:
- NOTREACHED();
- return "Unknown";
- }
-}
// Keep in sync with AdaptiveToolbarButtonVariant in enums.xml.
enum class AdaptiveToolbarButtonVariant {
@@ -53,7 +27,7 @@ enum class AdaptiveToolbarButtonVariant {
};
// This is the segmentation subset of
-// optimization_guide::proto::OptimizationTarget.
+// proto::SegmentId.
// Keep in sync with SegmentationPlatformSegmenationModel in
// //tools/metrics/histograms/enums.xml.
// See also SegmentationModel variant in
@@ -67,19 +41,20 @@ enum class SegmentationModel {
kChromeStartAndroid = 11,
kQueryTiles = 12,
kChromeLowUserEngagement = 16,
- kMaxValue = kChromeLowUserEngagement,
+ kFeedUserSegment = 17,
+ kMaxValue = kFeedUserSegment,
};
AdaptiveToolbarButtonVariant OptimizationTargetToAdaptiveToolbarButtonVariant(
- OptimizationTarget segment_id) {
+ SegmentId segment_id) {
switch (segment_id) {
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarButtonVariant::kNewTab;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarButtonVariant::kShare;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarButtonVariant::kVoice;
- case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+ case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarButtonVariant::kNone;
default:
return AdaptiveToolbarButtonVariant::kUnknown;
@@ -91,73 +66,72 @@ bool IsBooleanSegment(const std::string& segmentation_key) {
// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
return segmentation_key == kChromeStartAndroidSegmentationKey ||
segmentation_key == kQueryTilesSegmentationKey ||
- segmentation_key == kChromeLowUserEngagementSegmentationKey;
+ segmentation_key == kChromeLowUserEngagementSegmentationKey ||
+ segmentation_key == kFeedUserSegmentationKey;
}
-BooleanSegmentSwitch GetBooleanSegmentSwitch(
- OptimizationTarget new_selection,
- OptimizationTarget previous_selection) {
- if (new_selection != OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN &&
- previous_selection == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+BooleanSegmentSwitch GetBooleanSegmentSwitch(SegmentId new_selection,
+ SegmentId previous_selection) {
+ if (new_selection != SegmentId::OPTIMIZATION_TARGET_UNKNOWN &&
+ previous_selection == SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
return BooleanSegmentSwitch::kNoneToEnabled;
- } else if (new_selection == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN &&
- previous_selection !=
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+ } else if (new_selection == SegmentId::OPTIMIZATION_TARGET_UNKNOWN &&
+ previous_selection != SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
return BooleanSegmentSwitch::kEnabledToNone;
}
return BooleanSegmentSwitch::kUnknown;
}
AdaptiveToolbarSegmentSwitch GetAdaptiveToolbarSegmentSwitch(
- OptimizationTarget new_selection,
- OptimizationTarget previous_selection) {
+ SegmentId new_selection,
+ SegmentId previous_selection) {
switch (previous_selection) {
- case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+ case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
switch (new_selection) {
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarSegmentSwitch::kNoneToNewTab;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarSegmentSwitch::kNoneToShare;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarSegmentSwitch::kNoneToVoice;
default:
NOTREACHED();
return AdaptiveToolbarSegmentSwitch::kUnknown;
}
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
switch (new_selection) {
- case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+ case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarSegmentSwitch::kNewTabToNone;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarSegmentSwitch::kNewTabToShare;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarSegmentSwitch::kNewTabToVoice;
default:
NOTREACHED();
return AdaptiveToolbarSegmentSwitch::kUnknown;
}
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
switch (new_selection) {
- case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+ case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarSegmentSwitch::kShareToNone;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarSegmentSwitch::kShareToNewTab;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarSegmentSwitch::kShareToVoice;
default:
NOTREACHED();
return AdaptiveToolbarSegmentSwitch::kUnknown;
}
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
switch (new_selection) {
- case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+ case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarSegmentSwitch::kVoiceToNone;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarSegmentSwitch::kVoiceToNewTab;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarSegmentSwitch::kVoiceToShare;
default:
NOTREACHED();
@@ -170,25 +144,24 @@ AdaptiveToolbarSegmentSwitch GetAdaptiveToolbarSegmentSwitch(
}
}
-SegmentationModel OptimizationTargetToSegmentationModel(
- OptimizationTarget segment_id) {
+SegmentationModel OptimizationTargetToSegmentationModel(SegmentId segment_id) {
switch (segment_id) {
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return SegmentationModel::kNewTab;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return SegmentationModel::kShare;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return SegmentationModel::kVoice;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
return SegmentationModel::kDummy;
- case OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
return SegmentationModel::kChromeStartAndroid;
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
return SegmentationModel::kQueryTiles;
- case OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
return SegmentationModel::kChromeLowUserEngagement;
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
+ return SegmentationModel::kFeedUserSegment;
default:
return SegmentationModel::kUnknown;
}
@@ -244,9 +217,40 @@ float ZeroValueFraction(const std::vector<float>& tensor) {
return static_cast<float>(zero_values) / static_cast<float>(tensor.size());
}
+} // namespace
+
+// Should map to SegmentationModel variant in
+// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
+// Should also update the field trials allowlist in
+// go/segmentation-field-trials-map.
+std::string OptimizationTargetToHistogramVariant(SegmentId segment_id) {
+ switch (segment_id) {
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+ return "NewTab";
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+ return "Share";
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+ return "Voice";
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
+ return "Dummy";
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
+ return "ChromeStartAndroid";
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
+ return "QueryTiles";
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
+ return "ChromeLowUserEngagement";
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
+ return "FeedUserSegment";
+ default:
+ return "Other";
+ }
+}
+
const char* SegmentationKeyToUmaName(const std::string& segmentation_key) {
// Please keep in sync with SegmentationKey variant in
// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
+ // Should also update the field trials allowlist in
+ // go/segmentation-field-trials-map.
if (segmentation_key == kAdaptiveToolbarSegmentationKey) {
return "AdaptiveToolbar";
} else if (segmentation_key == kDummySegmentationKey) {
@@ -257,6 +261,8 @@ const char* SegmentationKeyToUmaName(const std::string& segmentation_key) {
return "QueryTiles";
} else if (segmentation_key == kChromeLowUserEngagementSegmentationKey) {
return "ChromeLowUserEngagement";
+ } else if (segmentation_key == kFeedUserSegmentationKey) {
+ return "FeedUserSegment";
} else if (base::StartsWith(segmentation_key, "test_key")) {
return "TestKey";
}
@@ -264,15 +270,13 @@ const char* SegmentationKeyToUmaName(const std::string& segmentation_key) {
return "Unknown";
}
-} // namespace
-
-void RecordModelScore(OptimizationTarget segment_id, float score) {
+void RecordModelScore(SegmentId segment_id, float score) {
// Special case adaptive toolbar models since it already has histograms being
// recorded and updating names will affect current work.
switch (segment_id) {
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
base::UmaHistogramPercentage(
"SegmentationPlatform.AdaptiveToolbar.ModelScore." +
OptimizationTargetToHistogramVariant(segment_id),
@@ -283,15 +287,14 @@ void RecordModelScore(OptimizationTarget segment_id, float score) {
}
switch (segment_id) {
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
- case OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
- case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
- case OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
+ case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
// Assumes all models return score between 0 and 1. This is true for all
// the models we have currently.
base::UmaHistogramPercentage(
@@ -306,8 +309,8 @@ void RecordModelScore(OptimizationTarget segment_id, float score) {
void RecordSegmentSelectionComputed(
const std::string& segmentation_key,
- OptimizationTarget new_selection,
- absl::optional<OptimizationTarget> previous_selection) {
+ SegmentId new_selection,
+ absl::optional<SegmentId> previous_selection) {
// Special case adaptive toolbar since it already has histograms being
// recorded and updating names will affect current work.
if (segmentation_key == kAdaptiveToolbarSegmentationKey) {
@@ -321,10 +324,9 @@ void RecordSegmentSelectionComputed(
base::UmaHistogramEnumeration(
computed_hist, OptimizationTargetToSegmentationModel(new_selection));
- OptimizationTarget prev_segment =
- previous_selection.has_value()
- ? previous_selection.value()
- : OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+ SegmentId prev_segment = previous_selection.has_value()
+ ? previous_selection.value()
+ : SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
if (prev_segment == new_selection)
return;
@@ -362,15 +364,14 @@ void RecordMaintenanceSignalIdentifierCount(size_t count) {
"SegmentationPlatform.Maintenance.SignalIdentifierCount", count);
}
-void RecordModelDeliveryHasMetadata(OptimizationTarget segment_id,
- bool has_metadata) {
+void RecordModelDeliveryHasMetadata(SegmentId segment_id, bool has_metadata) {
base::UmaHistogramBoolean(
"SegmentationPlatform.ModelDelivery.HasMetadata." +
OptimizationTargetToHistogramVariant(segment_id),
has_metadata);
}
-void RecordModelDeliveryMetadataFeatureCount(OptimizationTarget segment_id,
+void RecordModelDeliveryMetadataFeatureCount(SegmentId segment_id,
size_t count) {
base::UmaHistogramCounts1000(
"SegmentationPlatform.ModelDelivery.Metadata.FeatureCount." +
@@ -379,7 +380,7 @@ void RecordModelDeliveryMetadataFeatureCount(OptimizationTarget segment_id,
}
void RecordModelDeliveryMetadataValidation(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
bool processed,
metadata_utils::ValidationResult validation_result) {
// Should map to ValidationPhase variant string in
@@ -392,37 +393,34 @@ void RecordModelDeliveryMetadataValidation(
validation_result);
}
-void RecordModelDeliveryReceived(OptimizationTarget segment_id) {
+void RecordModelDeliveryReceived(SegmentId segment_id) {
UMA_HISTOGRAM_ENUMERATION("SegmentationPlatform.ModelDelivery.Received",
OptimizationTargetToSegmentationModel(segment_id));
}
-void RecordModelDeliverySaveResult(OptimizationTarget segment_id,
- bool success) {
+void RecordModelDeliverySaveResult(SegmentId segment_id, bool success) {
base::UmaHistogramBoolean(
"SegmentationPlatform.ModelDelivery.SaveResult." +
OptimizationTargetToHistogramVariant(segment_id),
success);
}
-void RecordModelDeliverySegmentIdMatches(OptimizationTarget segment_id,
- bool matches) {
+void RecordModelDeliverySegmentIdMatches(SegmentId segment_id, bool matches) {
base::UmaHistogramBoolean(
"SegmentationPlatform.ModelDelivery.SegmentIdMatches." +
OptimizationTargetToHistogramVariant(segment_id),
matches);
}
-void RecordModelExecutionDurationFeatureProcessing(
- OptimizationTarget segment_id,
- base::TimeDelta duration) {
+void RecordModelExecutionDurationFeatureProcessing(SegmentId segment_id,
+ base::TimeDelta duration) {
base::UmaHistogramTimes(
"SegmentationPlatform.ModelExecution.Duration.FeatureProcessing." +
OptimizationTargetToHistogramVariant(segment_id),
duration);
}
-void RecordModelExecutionDurationModel(OptimizationTarget segment_id,
+void RecordModelExecutionDurationModel(SegmentId segment_id,
bool success,
base::TimeDelta duration) {
ModelExecutionStatus status = success ? ModelExecutionStatus::kSuccess
@@ -438,7 +436,7 @@ void RecordModelExecutionDurationModel(OptimizationTarget segment_id,
duration);
}
-void RecordModelExecutionDurationTotal(OptimizationTarget segment_id,
+void RecordModelExecutionDurationTotal(SegmentId segment_id,
ModelExecutionStatus status,
base::TimeDelta duration) {
absl::optional<base::StringPiece> status_variant =
@@ -452,22 +450,21 @@ void RecordModelExecutionDurationTotal(OptimizationTarget segment_id,
duration);
}
-void RecordModelExecutionResult(OptimizationTarget segment_id, float result) {
+void RecordModelExecutionResult(SegmentId segment_id, float result) {
base::UmaHistogramPercentage(
"SegmentationPlatform.ModelExecution.Result." +
OptimizationTargetToHistogramVariant(segment_id),
result * 100);
}
-void RecordModelExecutionSaveResult(OptimizationTarget segment_id,
- bool success) {
+void RecordModelExecutionSaveResult(SegmentId segment_id, bool success) {
base::UmaHistogramBoolean(
"SegmentationPlatform.ModelExecution.SaveResult." +
OptimizationTargetToHistogramVariant(segment_id),
success);
}
-void RecordModelExecutionStatus(OptimizationTarget segment_id,
+void RecordModelExecutionStatus(SegmentId segment_id,
bool default_provider,
ModelExecutionStatus status) {
if (!default_provider) {
@@ -483,7 +480,7 @@ void RecordModelExecutionStatus(OptimizationTarget segment_id,
}
}
-void RecordModelExecutionZeroValuePercent(OptimizationTarget segment_id,
+void RecordModelExecutionZeroValuePercent(SegmentId segment_id,
const std::vector<float>& tensor) {
base::UmaHistogramPercentage(
"SegmentationPlatform.ModelExecution.ZeroValuePercent." +
@@ -542,7 +539,7 @@ void RecordSegmentSelectionFailure(const std::string& segmentation_key,
reason);
}
-void RecordModelAvailability(OptimizationTarget segment_id,
+void RecordModelAvailability(SegmentId segment_id,
SegmentationModelAvailability availability) {
base::UmaHistogramEnumeration(
"SegmentationPlatform.ModelAvailability." +
@@ -556,7 +553,7 @@ void RecordTooManyInputTensors(int tensor_size) {
tensor_size);
}
-void RecordTrainingDataCollectionEvent(OptimizationTarget segment_id,
+void RecordTrainingDataCollectionEvent(SegmentId segment_id,
TrainingDataCollectionEvent event) {
base::UmaHistogramEnumeration(
"SegmentationPlatform.TrainingDataCollectionEvents." +
diff --git a/chromium/components/segmentation_platform/internal/stats.h b/chromium/components/segmentation_platform/internal/stats.h
index d1165f5ebf2..ac59117df1f 100644
--- a/chromium/components/segmentation_platform/internal/stats.h
+++ b/chromium/components/segmentation_platform/internal/stats.h
@@ -5,17 +5,17 @@
#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_STATS_H_
#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_STATS_H_
-#include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "components/segmentation_platform/internal/execution/model_execution_status.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "components/segmentation_platform/public/segment_selection_result.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform::stats {
+using proto::SegmentId;
+
// Keep in sync with AdaptiveToolbarSegmentSwitch in enums.xml.
// Visible for testing.
enum class AdaptiveToolbarSegmentSwitch {
@@ -44,15 +44,21 @@ enum class BooleanSegmentSwitch {
kMaxValue = kEnabledToNone,
};
+// Returns an UMA display string for the given segment_id.
+std::string OptimizationTargetToHistogramVariant(SegmentId segment_id);
+
+// Returns an UMA display string for the given `segmentation_key`.
+const char* SegmentationKeyToUmaName(const std::string& segmentation_key);
+
// Records the score computed for a given segment.
-void RecordModelScore(OptimizationTarget segment_id, float score);
+void RecordModelScore(SegmentId segment_id, float score);
// Records the result of segment selection whenever segment selection is
// computed.
void RecordSegmentSelectionComputed(
const std::string& segmentation_key,
- OptimizationTarget new_selection,
- absl::optional<OptimizationTarget> previous_selection);
+ SegmentId new_selection,
+ absl::optional<SegmentId> previous_selection);
// Database Maintenance metrics.
// Records the number of unique signal identifiers that were successfully
@@ -68,61 +74,57 @@ void RecordMaintenanceSignalIdentifierCount(size_t count);
// Model Delivery metrics.
// Records whether any incoming ML model had metadata attached that we were able
// to parse.
-void RecordModelDeliveryHasMetadata(OptimizationTarget segment_id,
- bool has_metadata);
+void RecordModelDeliveryHasMetadata(SegmentId segment_id, bool has_metadata);
// Records the number of tensor features an updated ML model has.
-void RecordModelDeliveryMetadataFeatureCount(OptimizationTarget segment_id,
+void RecordModelDeliveryMetadataFeatureCount(SegmentId segment_id,
size_t count);
// Records the result of validating the metadata of an incoming ML model.
// Recorded before and after it has been merged with the already stored
// metadata.
void RecordModelDeliveryMetadataValidation(
- OptimizationTarget segment_id,
+ SegmentId segment_id,
bool processed,
metadata_utils::ValidationResult validation_result);
// Record what type of model metadata we received.
-void RecordModelDeliveryReceived(OptimizationTarget segment_id);
+void RecordModelDeliveryReceived(SegmentId segment_id);
// Records the result of attempting to save an updated version of the model
// metadata.
-void RecordModelDeliverySaveResult(OptimizationTarget segment_id, bool success);
+void RecordModelDeliverySaveResult(SegmentId segment_id, bool success);
// Records whether the currently stored segment_id matches the incoming
// segment_id, as these are expected to match.
-void RecordModelDeliverySegmentIdMatches(OptimizationTarget segment_id,
- bool matches);
+void RecordModelDeliverySegmentIdMatches(SegmentId segment_id, bool matches);
// Model Execution metrics.
// Records the duration of processing a single ML feature. This only takes into
// account the time it takes to process (aggregate) a feature result, not
// fetching it from the database. It also takes into account filtering any
// enum histograms.
-void RecordModelExecutionDurationFeatureProcessing(
- OptimizationTarget segment_id,
- base::TimeDelta duration);
+void RecordModelExecutionDurationFeatureProcessing(SegmentId segment_id,
+ base::TimeDelta duration);
// Records the duration of executing an ML model. This only takes into account
// the time it takes to invoke and wait for a result from the underlying ML
// infrastructure from //components/optimization_guide, and not fetching the
// relevant data from the database.
-void RecordModelExecutionDurationModel(OptimizationTarget segment_id,
+void RecordModelExecutionDurationModel(SegmentId segment_id,
bool success,
base::TimeDelta duration);
// Records the duration of fetching data for, processing, and executing an ML
// model.
-void RecordModelExecutionDurationTotal(OptimizationTarget segment_id,
+void RecordModelExecutionDurationTotal(SegmentId segment_id,
ModelExecutionStatus status,
base::TimeDelta duration);
// Records the result value after successfully executing an ML model.
-void RecordModelExecutionResult(OptimizationTarget segment_id, float result);
+void RecordModelExecutionResult(SegmentId segment_id, float result);
// Records whether the result value of of executing an ML model was successfully
// saved.
-void RecordModelExecutionSaveResult(OptimizationTarget segment_id,
- bool success);
+void RecordModelExecutionSaveResult(SegmentId segment_id, bool success);
// Records the final execution status for any ML model execution.
-void RecordModelExecutionStatus(OptimizationTarget segment_id,
+void RecordModelExecutionStatus(SegmentId segment_id,
bool default_provider,
ModelExecutionStatus status);
// Records the percent of features in a tensor that are equal to 0 when the
// segmentation model is executed.
-void RecordModelExecutionZeroValuePercent(OptimizationTarget segment_id,
+void RecordModelExecutionZeroValuePercent(SegmentId segment_id,
const std::vector<float>& tensor);
// Signal Database metrics.
@@ -164,7 +166,8 @@ enum class SegmentationSelectionFailureReason {
kAtLeastOneSegmentDefaultSignalNotCollected = 12,
kAtLeastOneSegmentDefaultExecFailed = 13,
kAtLeastOneSegmentDefaultMissingMetadata = 14,
- kMaxValue = kAtLeastOneSegmentDefaultMissingMetadata
+ kAtLeastOneSegmentTfliteExecFailed = 15,
+ kMaxValue = kAtLeastOneSegmentTfliteExecFailed
};
// Records the reason for failure or success to compute a segment selection.
@@ -181,7 +184,7 @@ enum class SegmentationModelAvailability {
kMaxValue = kMetadataInvalid
};
// Records the availability of segmentation models for each target needed.
-void RecordModelAvailability(OptimizationTarget segment_id,
+void RecordModelAvailability(SegmentId segment_id,
SegmentationModelAvailability availability);
// Records the number of input tensor that's causing a failure to upload
@@ -198,11 +201,14 @@ enum class TrainingDataCollectionEvent {
kGetInputTensorsFailed = 4,
kNotEnoughCollectionTime = 5,
kUkmReportingFailed = 6,
- kMaxValue = kUkmReportingFailed,
+ kPartialDataNotAllowed = 7,
+ kContinousCollectionStart = 8,
+ kContinousCollectionSuccess = 9,
+ kMaxValue = kContinousCollectionSuccess,
};
// Records analytics for training data collection.
-void RecordTrainingDataCollectionEvent(OptimizationTarget segment_id,
+void RecordTrainingDataCollectionEvent(SegmentId segment_id,
TrainingDataCollectionEvent event);
} // namespace segmentation_platform::stats
diff --git a/chromium/components/segmentation_platform/internal/stats_unittest.cc b/chromium/components/segmentation_platform/internal/stats_unittest.cc
index 722aa165873..cc8d08500ba 100644
--- a/chromium/components/segmentation_platform/internal/stats_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/stats_unittest.cc
@@ -5,9 +5,9 @@
#include "components/segmentation_platform/internal/stats.h"
#include "base/test/metrics/histogram_tester.h"
-#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -25,44 +25,40 @@ TEST(StatsTest, ModelExecutionZeroValuePercent) {
std::vector<float> all_non_zero{1, 2, 3};
RecordModelExecutionZeroValuePercent(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, empty);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, empty);
EXPECT_EQ(
1, tester.GetBucketCount(
"SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 0));
RecordModelExecutionZeroValuePercent(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- single_zero);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, single_zero);
EXPECT_EQ(
1,
tester.GetBucketCount(
"SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 100));
RecordModelExecutionZeroValuePercent(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- single_non_zero);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, single_non_zero);
EXPECT_EQ(
2, tester.GetBucketCount(
"SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 0));
RecordModelExecutionZeroValuePercent(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, all_zeroes);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, all_zeroes);
EXPECT_EQ(
2,
tester.GetBucketCount(
"SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 100));
RecordModelExecutionZeroValuePercent(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- one_non_zero);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, one_non_zero);
EXPECT_EQ(
1,
tester.GetBucketCount(
"SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 66));
RecordModelExecutionZeroValuePercent(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- all_non_zero);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, all_non_zero);
EXPECT_EQ(
3, tester.GetBucketCount(
"SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 0));
@@ -75,20 +71,19 @@ TEST(StatsTest, AdaptiveToolbarSegmentSwitch) {
// Share -> New tab.
RecordSegmentSelectionComputed(
kAdaptiveToolbarSegmentationKey,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
// None -> Share.
RecordSegmentSelectionComputed(
kAdaptiveToolbarSegmentationKey,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
- absl::nullopt);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, absl::nullopt);
// Share -> Share.
RecordSegmentSelectionComputed(
kAdaptiveToolbarSegmentationKey,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
tester.ExpectTotalCount(histogram, 2);
EXPECT_THAT(
@@ -111,9 +106,8 @@ TEST(StatsTest, BooleanSegmentSwitch) {
// Start to none.
RecordSegmentSelectionComputed(
kChromeStartAndroidSegmentationKey,
- OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
- OptimizationTarget::
- OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID);
+ SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID);
tester.ExpectTotalCount(histogram, 1);
EXPECT_THAT(tester.GetAllSamples(histogram),
@@ -122,7 +116,7 @@ TEST(StatsTest, BooleanSegmentSwitch) {
// None to start.
RecordSegmentSelectionComputed(
kChromeStartAndroidSegmentationKey,
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
absl::nullopt);
tester.ExpectTotalCount(histogram, 2);
@@ -164,7 +158,7 @@ TEST(StatsTest, SignalsListeningCount) {
TEST(StatsTest, TrainingDataCollectionEvent) {
base::HistogramTester tester;
RecordTrainingDataCollectionEvent(
- OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
TrainingDataCollectionEvent::kImmediateCollectionStart);
EXPECT_EQ(1,
tester.GetBucketCount(
diff --git a/chromium/components/segmentation_platform/internal/tools/create_class.py b/chromium/components/segmentation_platform/internal/tools/create_class.py
new file mode 100644
index 00000000000..c9bb38327f2
--- /dev/null
+++ b/chromium/components/segmentation_platform/internal/tools/create_class.py
@@ -0,0 +1,181 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Script to generate header cc and unittest file for a class in chromium.
+
+Usage:
+ python3 components/segmentation_platform/internal/tools/create_class.py \
+ src/dir/class_name.h
+
+If any of these file already exists then prints a log and does not touch the
+file, and creates the remaining files.
+
+"""
+
+import argparse
+from datetime import date
+import logging
+import os
+import sys
+
+_HEADER_TEMPLATE = (
+"""// Copyright {year} The Chromium 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 {macro}
+#define {macro}
+
+namespace {namespace} {{
+
+class {clas} {{
+ public:
+ {clas}();
+ ~{clas}();
+
+ {clas}({clas}&) = delete;
+ {clas}& operator=({clas}&) = delete;
+
+ private:
+}};
+
+}}
+
+#endif // {macro}
+""")
+
+_CC_TEMPLATE = (
+"""// Copyright {year} The Chromium 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 "{file_path}"
+
+namespace {namespace} {{
+
+{clas}::{clas} () = default;
+{clas}::~{clas}() = default;
+
+}}
+""")
+
+_TEST_TEMPLATE = (
+"""// Copyright {year} The Chromium 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 "{file_path}"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {namespace} {{
+
+class {test_class} : public testing::Test {{
+ public:
+ {test_class}() = default;
+ ~{test_class}() override = default;
+
+ void SetUp() override {{
+ Test::SetUp();
+ }}
+
+ void TearDown() override {{
+ Test::TearDown();
+ }}
+
+ protected:
+}};
+
+TEST_F({test_class}, Test) {{
+}}
+
+}}
+""")
+
+
+def _GetLogger():
+ """Logger for the tool."""
+ logging.basicConfig(level=logging.INFO)
+ logger = logging.getLogger('create_class')
+ logger.setLevel(level=logging.INFO)
+ return logger
+
+
+def _WriteFile(path, type_str, contents):
+ """Writes a file with contents to the path, if not exists."""
+ if os.path.exists(path):
+ _GetLogger().error('%s already exists', type_str)
+ return
+
+ _GetLogger().info('Writing %s file %s', type_str, path)
+ with open(path, 'w') as f:
+ f.write(contents)
+
+
+def _GetClassNameFromFile(header):
+ """Gets a class name from the header file name."""
+ file_base = os.path.basename(header).replace('.h', '')
+ class_name = ''
+ for i in range(len(file_base)):
+ if i == 0 or file_base[i - 1] == '_':
+ class_name += file_base[i].upper()
+ elif file_base[i] == '_':
+ continue
+ else:
+ class_name += file_base[i]
+ return class_name
+
+
+def _CreateFilesForClass(args):
+ """Creates header cc and test files for the class."""
+ macro = args.header.replace('/', '_').replace('.', '_').upper() + '_'
+ file_cc = args.header.replace('.h', '.cc')
+ file_test = args.header.replace('.h', '_unittest.cc')
+ class_name = _GetClassNameFromFile(args.header)
+
+ contents = _HEADER_TEMPLATE.format(
+ macro=macro,
+ year=date.today().year,
+ clas=class_name,
+ namespace=args.namespace)
+ _WriteFile(args.header, 'Header', contents)
+
+ contents = _CC_TEMPLATE.format(
+ year=date.today().year,
+ clas=class_name,
+ file_path=args.header,
+ namespace=args.namespace)
+ _WriteFile(file_cc, 'CC', contents)
+
+ contents = _TEST_TEMPLATE.format(
+ year=date.today().year,
+ test_class=class_name + 'Test',
+ file_path=args.header,
+ namespace=args.namespace)
+ _WriteFile(file_test, 'Test', contents)
+
+
+def _CreateOptionParser():
+ """Options parser for the tool."""
+ parser = argparse.ArgumentParser(
+ description='Create header, cc and test file for Chromium')
+ parser.add_argument('header', help='Path to the header file from src/')
+ parser.add_argument(
+ '--namespace', dest='namespace', default='segmentation_platform')
+ return parser
+
+
+def main():
+ parser = _CreateOptionParser()
+ args = parser.parse_args()
+
+ if '.h' not in args.header:
+ raise ValueError('The first argument should be a path to header')
+
+ _GetLogger().info('Creating class for header %s', args.header)
+ _CreateFilesForClass(args)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
+
diff --git a/chromium/components/segmentation_platform/internal/ukm_data_manager.h b/chromium/components/segmentation_platform/internal/ukm_data_manager.h
index c9669ba161f..33780e6b9d0 100644
--- a/chromium/components/segmentation_platform/internal/ukm_data_manager.h
+++ b/chromium/components/segmentation_platform/internal/ukm_data_manager.h
@@ -5,21 +5,20 @@
#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_UKM_DATA_MANAGER_H_
#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_UKM_DATA_MANAGER_H_
-class PrefService;
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "services/metrics/public/mojom/ukm_interface.mojom.h"
+#include "url/gurl.h"
namespace base {
class FilePath;
}
-namespace ukm {
-class UkmRecorderImpl;
-}
-
namespace segmentation_platform {
class UkmDatabase;
class UrlSignalHandler;
class UkmConfig;
+class UkmObserver;
// Manages ownership and lifetime of all UKM related classes, like database and
// observer. There is only one manager per browser process. Created before
@@ -34,8 +33,9 @@ class UkmDataManager {
UkmDataManager(UkmDataManager&) = delete;
UkmDataManager& operator=(UkmDataManager&) = delete;
- // Initializes UKM database.
- virtual void Initialize(const base::FilePath& database_path) = 0;
+ // Initializes UKM database and the observer of all UKM events.
+ virtual void Initialize(const base::FilePath& database_path,
+ UkmObserver* ukm_observer) = 0;
// Returns true when UKM engine is usable. If false, then UKM based engine is
// disabled and this class is a no-op. UkmObserver, UrlSignalHandler and
@@ -43,23 +43,14 @@ class UkmDataManager {
// false.
virtual bool IsUkmEngineEnabled() = 0;
- // Must be called when UKM service is available to start observing metrics.
- virtual void NotifyCanObserveUkm(ukm::UkmRecorderImpl* ukm_recorder,
- PrefService* pref_service) = 0;
-
- // Can be called at any time, irrespective of UKM observer's lifetime. If
- // NotifyCanObserveUkm() was already called, then starts observing UKM with
- // the given config. Else, starts when NotifyCanObserveUkm() is called. If
- // called after StopObservingUkm(), does nothing.
+ // Can be called at any time, starts observing UKM with the given config. This
+ // must be called after the Initialize() call.
virtual void StartObservingUkm(const UkmConfig& config) = 0;
// Pauses or resumes observation of UKM, can be called any time, irrespective
// of UKM observer's lifetime, similar to StartObservingUkm().
virtual void PauseOrResumeObservation(bool pause) = 0;
- // Must be called before UKM service is destroyed, to remove observers.
- virtual void StopObservingUkm() = 0;
-
// Get URL signal handler. The signal handler is safe to use as long as data
// manager is alive, so until after all profiles are destroyed.
virtual UrlSignalHandler* GetOrCreateUrlHandler() = 0;
@@ -68,13 +59,17 @@ class UkmDataManager {
// alive, so until after all profiles are destroyed.
virtual UkmDatabase* GetUkmDatabase() = 0;
+ // Called when a new UKM entry is added.
+ virtual void OnEntryAdded(ukm::mojom::UkmEntryPtr entry) = 0;
+
+ // Called when UKM source URL is updated.
+ virtual void OnUkmSourceUpdated(ukm::SourceId source_id,
+ const std::vector<GURL>& urls) = 0;
+
// Keep track of all the segmentation services that hold reference to this
// object.
virtual void AddRef() = 0;
virtual void RemoveRef() = 0;
-
- // Called when UKM allowed state is changed.
- virtual void OnUkmAllowedStateChanged(bool allowed) = 0;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/ukm_data_manager_impl.cc b/chromium/components/segmentation_platform/internal/ukm_data_manager_impl.cc
index f86fbd8195e..3290279417a 100644
--- a/chromium/components/segmentation_platform/internal/ukm_data_manager_impl.cc
+++ b/chromium/components/segmentation_platform/internal/ukm_data_manager_impl.cc
@@ -5,38 +5,70 @@
#include "components/segmentation_platform/internal/ukm_data_manager_impl.h"
#include "base/check_op.h"
-#include "components/prefs/pref_service.h"
-#include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/database/ukm_database.h"
+#include "components/segmentation_platform/internal/database/ukm_database_impl.h"
#include "components/segmentation_platform/internal/signals/ukm_config.h"
#include "components/segmentation_platform/internal/signals/ukm_observer.h"
#include "components/segmentation_platform/internal/signals/url_signal_handler.h"
namespace segmentation_platform {
+namespace {
+
+// Delay for running clean up task from startup.
+const base::TimeDelta kDatabaseCleanupDelayStartup = base::Minutes(2);
+
+// Periodic interval between two cleanup tasks.
+const base::TimeDelta kDatabaseCleanupDelayNormal = base::Days(1);
+
+// Number of days to keep UKM metrics in database.
+constexpr base::TimeDelta kUkmEntriesTTL = base::Days(30);
+
+} // namespace
+
UkmDataManagerImpl::UkmDataManagerImpl() = default;
UkmDataManagerImpl::~UkmDataManagerImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
DCHECK_EQ(ref_count_, 0);
- // UKM observer should be destroyed earlier since it uses the database.
- DCHECK(!ukm_observer_);
url_signal_handler_.reset();
ukm_database_.reset();
}
void UkmDataManagerImpl::InitializeForTesting(
- std::unique_ptr<UkmDatabase> ukm_database) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
- DCHECK(!ukm_database_);
- ukm_database_ = std::move(ukm_database);
+ std::unique_ptr<UkmDatabase> ukm_database,
+ UkmObserver* ukm_observer) {
+ InitiailizeImpl(std::move(ukm_database), ukm_observer);
+}
+
+void UkmDataManagerImpl::Initialize(const base::FilePath& database_path,
+ UkmObserver* ukm_observer) {
+ InitiailizeImpl(std::make_unique<UkmDatabaseImpl>(database_path),
+ ukm_observer);
}
-void UkmDataManagerImpl::Initialize(const base::FilePath& database_path) {
+void UkmDataManagerImpl::InitiailizeImpl(
+ std::unique_ptr<UkmDatabase> ukm_database,
+ UkmObserver* ukm_observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
DCHECK(!ukm_database_);
- ukm_database_ = std::make_unique<UkmDatabase>(database_path);
+ DCHECK(!ukm_observer_);
+
+ ukm_observer_ = ukm_observer;
+ ukm_observer_->set_ukm_data_manager(this);
+
+ ukm_database_ = std::move(ukm_database);
+ // TODO(ssid): Move this call to constructor to make it clear any transaction
+ // is posted after initialization.
+ ukm_database_->InitDatabase(base::DoNothing());
+
+ GetOrCreateUrlHandler();
+
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&UkmDataManagerImpl::RunCleanupTask,
+ weak_factory_.GetWeakPtr()),
+ kDatabaseCleanupDelayStartup);
}
bool UkmDataManagerImpl::IsUkmEngineEnabled() {
@@ -54,31 +86,9 @@ UrlSignalHandler* UkmDataManagerImpl::GetOrCreateUrlHandler() {
return url_signal_handler_.get();
}
-void UkmDataManagerImpl::NotifyCanObserveUkm(ukm::UkmRecorderImpl* ukm_recorder,
- PrefService* pref_service) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
- DCHECK(ukm_database_);
- ukm_observer_ = std::make_unique<UkmObserver>(
- ukm_recorder, ukm_database_.get(), GetOrCreateUrlHandler(), this);
- if (pending_ukm_config_) {
- ukm_observer_->StartObserving(*pending_ukm_config_);
- pending_ukm_config_.reset();
- }
- prefs_ = pref_service;
- if (is_ukm_allowed_.has_value()) {
- OnUkmAllowedStateChanged(is_ukm_allowed_.value());
- }
-}
-
void UkmDataManagerImpl::StartObservingUkm(const UkmConfig& ukm_config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
- if (ukm_observer_) {
- ukm_observer_->StartObserving(ukm_config);
- } else {
- if (!pending_ukm_config_)
- pending_ukm_config_ = std::make_unique<UkmConfig>();
- pending_ukm_config_->Merge(ukm_config);
- }
+ ukm_observer_->StartObserving(ukm_config);
}
void UkmDataManagerImpl::PauseOrResumeObservation(bool pause) {
@@ -86,22 +96,22 @@ void UkmDataManagerImpl::PauseOrResumeObservation(bool pause) {
ukm_observer_->PauseOrResumeObservation(pause);
}
-void UkmDataManagerImpl::StopObservingUkm() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
- if (!ukm_observer_)
- return;
-
- DCHECK(ukm_database_);
- DCHECK(url_signal_handler_);
- ukm_observer_.reset();
-}
-
UkmDatabase* UkmDataManagerImpl::GetUkmDatabase() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
DCHECK(ukm_database_);
return ukm_database_.get();
}
+void UkmDataManagerImpl::OnEntryAdded(ukm::mojom::UkmEntryPtr entry) {
+ ukm_database_->StoreUkmEntry(std::move(entry));
+}
+
+void UkmDataManagerImpl::OnUkmSourceUpdated(ukm::SourceId source_id,
+ const std::vector<GURL>& urls) {
+ if (url_signal_handler_)
+ url_signal_handler_->OnUkmSourceUpdated(source_id, urls);
+}
+
void UkmDataManagerImpl::AddRef() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
ref_count_++;
@@ -113,30 +123,17 @@ void UkmDataManagerImpl::RemoveRef() {
ref_count_--;
}
-void UkmDataManagerImpl::OnUkmAllowedStateChanged(bool allowed) {
- if (!prefs_) {
- is_ukm_allowed_ = allowed;
- return;
- }
-
- if (!allowed) {
- prefs_->SetTime(kSegmentationUkmMostRecentAllowedTimeKey,
- base::Time::Max());
- return;
- }
- // Update the most recent allowed time if needed.
- base::Time most_recent_allowed = GetUkmMostRecentAllowedTime();
- if (most_recent_allowed.is_null() ||
- most_recent_allowed == base::Time::Max()) {
- prefs_->SetTime(kSegmentationUkmMostRecentAllowedTimeKey,
- base::Time::Now());
- }
-}
-
-base::Time UkmDataManagerImpl::GetUkmMostRecentAllowedTime() const {
- if (prefs_)
- return prefs_->GetTime(kSegmentationUkmMostRecentAllowedTimeKey);
- return base::Time::Max();
+void UkmDataManagerImpl::RunCleanupTask() {
+ DCHECK(ukm_database_);
+ ukm_database_->DeleteEntriesOlderThan(base::Time::Now() - kUkmEntriesTTL);
+
+ // Consider waiting for the above task to finish successfully before posting
+ // the next one.
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&UkmDataManagerImpl::RunCleanupTask,
+ weak_factory_.GetWeakPtr()),
+ kDatabaseCleanupDelayNormal);
}
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/ukm_data_manager_impl.h b/chromium/components/segmentation_platform/internal/ukm_data_manager_impl.h
index 9fb77e9a8ac..dbfdbc5922f 100644
--- a/chromium/components/segmentation_platform/internal/ukm_data_manager_impl.h
+++ b/chromium/components/segmentation_platform/internal/ukm_data_manager_impl.h
@@ -8,13 +8,11 @@
#include <memory>
#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
-#include "base/time/time.h"
#include "components/segmentation_platform/internal/ukm_data_manager.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-class PrefService;
-
namespace segmentation_platform {
class UkmDatabase;
@@ -29,36 +27,41 @@ class UkmDataManagerImpl : public UkmDataManager {
UkmDataManagerImpl(UkmDataManagerImpl&) = delete;
UkmDataManagerImpl& operator=(UkmDataManagerImpl&) = delete;
- void InitializeForTesting(std::unique_ptr<UkmDatabase> ukm_database);
-
- // Gets the most recent time when UKM is allowed.
- base::Time GetUkmMostRecentAllowedTime() const;
+ void InitializeForTesting(std::unique_ptr<UkmDatabase> ukm_database,
+ UkmObserver* ukm_observer);
// UkmDataManager implementation:
- void Initialize(const base::FilePath& database_path) override;
+ void Initialize(const base::FilePath& database_path,
+ UkmObserver* ukm_observer) override;
bool IsUkmEngineEnabled() override;
- void NotifyCanObserveUkm(ukm::UkmRecorderImpl* ukm_recorder,
- PrefService* pref_service) override;
void StartObservingUkm(const UkmConfig& config) override;
void PauseOrResumeObservation(bool pause) override;
- void StopObservingUkm() override;
UrlSignalHandler* GetOrCreateUrlHandler() override;
UkmDatabase* GetUkmDatabase() override;
+ void OnEntryAdded(ukm::mojom::UkmEntryPtr entry) override;
+ void OnUkmSourceUpdated(ukm::SourceId source_id,
+ const std::vector<GURL>& urls) override;
void AddRef() override;
void RemoveRef() override;
- void OnUkmAllowedStateChanged(bool allowed) override;
private:
+ // Helper method for initializing this object.
+ void InitiailizeImpl(std::unique_ptr<UkmDatabase> ukm_database,
+ UkmObserver* ukm_observer);
+
+ void RunCleanupTask();
+
int ref_count_ = 0;
+ raw_ptr<UkmObserver> ukm_observer_ = nullptr;
std::unique_ptr<UkmDatabase> ukm_database_;
std::unique_ptr<UrlSignalHandler> url_signal_handler_;
- std::unique_ptr<UkmObserver> ukm_observer_;
std::unique_ptr<UkmConfig> pending_ukm_config_;
absl::optional<bool> is_ukm_allowed_;
- raw_ptr<PrefService> prefs_ = nullptr;
SEQUENCE_CHECKER(sequence_check_);
+
+ base::WeakPtrFactory<UkmDataManagerImpl> weak_factory_{this};
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/internal/ukm_data_manager_impl_unittest.cc b/chromium/components/segmentation_platform/internal/ukm_data_manager_impl_unittest.cc
index 505b83bf25b..fb722780c8e 100644
--- a/chromium/components/segmentation_platform/internal/ukm_data_manager_impl_unittest.cc
+++ b/chromium/components/segmentation_platform/internal/ukm_data_manager_impl_unittest.cc
@@ -13,8 +13,11 @@
#include "components/segmentation_platform/internal/database/mock_ukm_database.h"
#include "components/segmentation_platform/internal/database/ukm_types.h"
#include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
+#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
#include "components/segmentation_platform/internal/segmentation_platform_service_test_base.h"
+#include "components/segmentation_platform/internal/signals/ukm_observer.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -110,11 +113,10 @@ class TestServicesForPlatform : public SegmentationPlatformServiceTestBase {
}
void AddModel(const proto::SegmentationModelMetadata& metadata) {
- auto& callback =
- model_provider_data_.model_providers_callbacks
- [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE];
- callback.Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
- metadata, 0);
+ auto& callback = model_provider_data_.model_providers_callbacks
+ [SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE];
+ callback.Run(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, metadata,
+ 0);
segment_db_->GetCallback(true);
segment_db_->UpdateCallback(true);
segment_db_->LoadCallback(true);
@@ -125,6 +127,28 @@ class TestServicesForPlatform : public SegmentationPlatformServiceTestBase {
return *segmentation_platform_service_impl_;
}
+ void SaveSegmentResult(SegmentId segment_id,
+ absl::optional<proto::PredictionResult> result) {
+ const std::string key = base::NumberToString(static_cast<int>(segment_id));
+ auto& segment_info = segment_db_entries_[key];
+ // Assume that test already created the segment info, this method only
+ // writes result.
+ ASSERT_EQ(segment_info.segment_id(), segment_id);
+ if (result) {
+ *segment_info.mutable_prediction_result() = std::move(*result);
+ } else {
+ segment_info.clear_prediction_result();
+ }
+ }
+
+ bool HasSegmentResult(SegmentId segment_id) {
+ const std::string key = base::NumberToString(static_cast<int>(segment_id));
+ const auto it = segment_db_entries_.find(key);
+ if (it == segment_db_entries_.end())
+ return false;
+ return it->second.has_prediction_result();
+ }
+
base::ScopedTempDir profile_dir;
std::unique_ptr<history::HistoryService> history_service;
};
@@ -136,15 +160,17 @@ class UkmDataManagerImplTest : public testing::Test {
void SetUp() override {
SegmentationPlatformService::RegisterLocalStatePrefs(prefs_.registry());
+ LocalStateHelper::GetInstance().Initialize(&prefs_);
data_manager_ = std::make_unique<UkmDataManagerImpl>();
ukm_recorder_ = std::make_unique<ukm::TestUkmRecorder>();
auto ukm_db = std::make_unique<MockUkmDatabase>();
ukm_database_ = ukm_db.get();
- data_manager_->InitializeForTesting(std::move(ukm_db));
+ ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_.get());
+ data_manager_->InitializeForTesting(std::move(ukm_db), ukm_observer_.get());
}
void TearDown() override {
- data_manager_->StopObservingUkm();
+ ukm_observer_.reset();
ukm_recorder_.reset();
ukm_database_ = nullptr;
data_manager_.reset();
@@ -175,6 +201,7 @@ class UkmDataManagerImplTest : public testing::Test {
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::SYSTEM_TIME};
+ std::unique_ptr<UkmObserver> ukm_observer_;
std::unique_ptr<ukm::TestUkmRecorder> ukm_recorder_;
raw_ptr<MockUkmDatabase> ukm_database_;
std::unique_ptr<UkmDataManagerImpl> data_manager_;
@@ -189,8 +216,16 @@ MATCHER_P(HasEventHash, event_hash, "") {
TEST_F(UkmDataManagerImplTest, HistoryNotification) {
const GURL kUrl1 = GURL("https://www.url1.com/");
+ const SegmentId kSegmentId =
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
TestServicesForPlatform& platform1 = CreatePlatform();
+ platform1.AddModel(PageLoadModelMetadata());
+ proto::PredictionResult prediction_result;
+ prediction_result.set_result(10);
+ prediction_result.set_timestamp_us(1000);
+ platform1.SaveSegmentResult(kSegmentId, prediction_result);
+ EXPECT_TRUE(platform1.HasSegmentResult(kSegmentId));
// Add a page to history and check that the notification is sent to
// UkmDatabase. All notifications should be sent.
@@ -205,19 +240,25 @@ TEST_F(UkmDataManagerImplTest, HistoryNotification) {
// Check that RemoveUrls() notification is sent to UkmDatabase.
base::RunLoop wait_for_remove1;
- EXPECT_CALL(*ukm_database_, RemoveUrls(std::vector({kUrl1})))
+ EXPECT_CALL(*ukm_database_, RemoveUrls(std::vector({kUrl1}), false))
.WillOnce(
[&wait_for_remove1]() { wait_for_remove1.QuitClosure().Run(); });
wait_for_remove1.Run();
+ // Run segment info callbacks that were posted to remove results.
+ platform1.segment_db().GetCallback(true);
+ platform1.segment_db().UpdateCallback(true);
+
+ // History based segment results should be removed.
+ EXPECT_FALSE(platform1.HasSegmentResult(
+ SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+
RemovePlatform(&platform1);
}
TEST_F(UkmDataManagerImplTest, UkmSourceObservation) {
const GURL kUrl1 = GURL("https://www.url1.com/");
- data_manager_->NotifyCanObserveUkm(ukm_recorder_.get(), &prefs_);
-
// Create a platform that observes PageLoad events.
TestServicesForPlatform& platform1 = CreatePlatform();
platform1.AddModel(PageLoadModelMetadata());
@@ -250,8 +291,6 @@ TEST_F(UkmDataManagerImplTest, UkmEntryObservation) {
// Not added since UkmDataManager is not notified for UKM observation.
ukm_recorder_->AddEntry(GetSamplePageLoadEntry());
- data_manager_->NotifyCanObserveUkm(ukm_recorder_.get(), &prefs_);
-
// Not added since it is not PageLoad event.
ukm_recorder_->AddEntry(GetSamplePaintPreviewEntry());
@@ -271,9 +310,6 @@ TEST_F(UkmDataManagerImplTest, UkmEntryObservation) {
TEST_F(UkmDataManagerImplTest, UkmServiceCreatedBeforePlatform) {
const GURL kUrl1 = GURL("https://www.url1.com/");
- // Observation is available before platforms are created.
- data_manager_->NotifyCanObserveUkm(ukm_recorder_.get(), &prefs_);
-
TestServicesForPlatform& platform1 = CreatePlatform();
platform1.AddModel(PageLoadModelMetadata());
@@ -300,7 +336,6 @@ TEST_F(UkmDataManagerImplTest, UkmServiceCreatedBeforePlatform) {
TEST_F(UkmDataManagerImplTest, UrlValidationWithHistory) {
const GURL kUrl1 = GURL("https://www.url1.com/");
- data_manager_->NotifyCanObserveUkm(ukm_recorder_.get(), &prefs_);
TestServicesForPlatform& platform1 = CreatePlatform();
platform1.AddModel(PageLoadModelMetadata());
@@ -330,8 +365,6 @@ TEST_F(UkmDataManagerImplTest, MultiplePlatforms) {
const GURL kUrl1 = GURL("https://www.url1.com/");
const GURL kUrl2 = GURL("https://www.url2.com/");
- data_manager_->NotifyCanObserveUkm(ukm_recorder_.get(), &prefs_);
-
// Create 2 platforms, and 1 of them observing UKM events.
TestServicesForPlatform& platform1 = CreatePlatform();
TestServicesForPlatform& platform3 = CreatePlatform();
@@ -398,29 +431,4 @@ TEST_F(UkmDataManagerImplTest, MultiplePlatforms) {
RemovePlatform(&platform3);
}
-// Tests that the most recent time for UKM allowed state is correctly set and
-// read.
-TEST_F(UkmDataManagerImplTest, GetUkmMostRecentAllowedTime) {
- // Without PrefService, base::Time::Max() will be returned.
- data_manager_->OnUkmAllowedStateChanged(true);
- EXPECT_EQ(data_manager_->GetUkmMostRecentAllowedTime(), base::Time::Max());
-
- data_manager_->NotifyCanObserveUkm(ukm_recorder_.get(), &prefs_);
- EXPECT_LE(data_manager_->GetUkmMostRecentAllowedTime(), base::Time::Now());
-
- // Change the allowed state to false, the start time should now be set to
- // Time::Max().
- ukm_recorder_->OnUkmAllowedStateChanged(false);
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(base::Time::Max(), data_manager_->GetUkmMostRecentAllowedTime());
-
- // Change the allowed state to true, the new start time should be close to
- // now.
- base::Time now = base::Time::Now();
- ukm_recorder_->OnUkmAllowedStateChanged(true);
- base::RunLoop().RunUntilIdle();
- EXPECT_LE(now, data_manager_->GetUkmMostRecentAllowedTime());
- EXPECT_LE(data_manager_->GetUkmMostRecentAllowedTime(), base::Time::Now());
-}
-
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/public/BUILD.gn b/chromium/components/segmentation_platform/public/BUILD.gn
index 0dd677eced5..859a5d687b7 100644
--- a/chromium/components/segmentation_platform/public/BUILD.gn
+++ b/chromium/components/segmentation_platform/public/BUILD.gn
@@ -13,6 +13,8 @@ source_set("public") {
"config.h",
"features.cc",
"features.h",
+ "field_trial_register.h",
+ "local_state_helper.h",
"model_provider.cc",
"model_provider.h",
"segment_selection_result.cc",
@@ -21,13 +23,21 @@ source_set("public") {
"segmentation_platform_service.h",
"service_proxy.cc",
"service_proxy.h",
+ "trigger.h",
+ "trigger_context.cc",
+ "trigger_context.h",
]
+ public_deps = [ "//components/segmentation_platform/public/proto" ]
+
deps = [
"//base",
"//components/keyed_service/core",
- "//components/optimization_guide/proto:optimization_guide_proto",
]
+
+ if (is_android) {
+ deps += [ ":jni_headers" ]
+ }
}
source_set("unit_tests") {
@@ -46,17 +56,26 @@ source_set("unit_tests") {
if (is_android) {
android_library("public_java") {
sources = [
+ "android/java/src/org/chromium/components/segmentation_platform/OnDemandSegmentSelectionResult.java",
"android/java/src/org/chromium/components/segmentation_platform/SegmentSelectionResult.java",
"android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformService.java",
+ "android/java/src/org/chromium/components/segmentation_platform/TriggerContext.java",
]
deps = [
"//base:base_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
+ "//base:jni_java",
+ "//build/android:build_java",
]
- public_deps = [
- "//components/optimization_guide/proto:optimization_guide_proto_java",
- ]
+ annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+
+ public_deps = [ "//components/segmentation_platform/public/proto:segmentation_platform_proto_java" ]
+ }
+
+ generate_jni("jni_headers") {
+ visibility = [ ":*" ]
+
+ sources = [ "android/java/src/org/chromium/components/segmentation_platform/TriggerContext.java" ]
}
}
diff --git a/chromium/components/segmentation_platform/public/config.h b/chromium/components/segmentation_platform/public/config.h
index 13a98f59fe2..ee2564b1d82 100644
--- a/chromium/components/segmentation_platform/public/config.h
+++ b/chromium/components/segmentation_platform/public/config.h
@@ -8,7 +8,8 @@
#include <string>
#include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+#include "components/segmentation_platform/public/trigger.h"
namespace segmentation_platform {
@@ -33,11 +34,19 @@ const char kQueryTilesSegmentationKey[] = "query_tiles";
const char kChromeLowUserEngagementSegmentationKey[] =
"chrome_low_user_engagement";
+// The key is used to decide whether the user likes to use Feed.
+const char kFeedUserSegmentationKey[] = "feed_user_segment";
+
+// The key is used to show a contextual page action.
+const char kContextualPageActionsKey[] = "contextual_page_actions";
+
// The key provide a list of segment IDs, separated by commas, whose ML model
// execution results are allowed to be uploaded through UKM.
const char kSegmentIdsAllowedForReportingKey[] =
"segment_ids_allowed_for_reporting";
+const char kSubsegmentDiscreteMappingSuffix[] = "_subsegment";
+
// Contains various finch configuration params used by the segmentation
// platform.
struct Config {
@@ -52,6 +61,10 @@ struct Config {
// discrete mapping and writing results to prefs.
std::string segmentation_key;
+ // The trigger event type that triggers segment selection. If trigger is
+ // non-none, |on_demand_execution| must be true.
+ TriggerType trigger = TriggerType::kNone;
+
// Time to live for a segment selection. Segment selection can't be changed
// before this duration.
base::TimeDelta segment_selection_ttl;
@@ -64,7 +77,12 @@ struct Config {
base::TimeDelta unknown_selection_ttl;
// List of segment ids that the current config requires to be available.
- std::vector<optimization_guide::proto::OptimizationTarget> segment_ids;
+ std::vector<proto::SegmentId> segment_ids;
+
+ // The selection only supports returning results from on-demand model
+ // executions instead of returning result from previous sessions. The
+ // selection TTLs are ignored in this config.
+ bool on_demand_execution = false;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/public/features.cc b/chromium/components/segmentation_platform/public/features.cc
index c7666db9fdf..e849983ba6c 100644
--- a/chromium/components/segmentation_platform/public/features.cc
+++ b/chromium/components/segmentation_platform/public/features.cc
@@ -6,17 +6,10 @@
#include "build/build_config.h"
-namespace segmentation_platform {
-namespace features {
+namespace segmentation_platform::features {
-const base::Feature kSegmentationPlatformFeature {
- "SegmentationPlatform",
-#if BUILDFLAG(IS_ANDROID)
- base::FEATURE_ENABLED_BY_DEFAULT
-#else
- base::FEATURE_DISABLED_BY_DEFAULT
-#endif
-};
+const base::Feature kSegmentationPlatformFeature{
+ "SegmentationPlatform", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kSegmentationPlatformDummyFeature{
"SegmentationPlatformDummyFeature", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -27,5 +20,24 @@ const base::Feature kSegmentationStructuredMetricsFeature{
const base::Feature kSegmentationPlatformUkmEngine{
"SegmentationPlatformUkmEngine", base::FEATURE_DISABLED_BY_DEFAULT};
-} // namespace features
-} // namespace segmentation_platform
+const base::Feature kSegmentationPlatformLowEngagementFeature{
+ "SegmentationPlatformLowEngagementFeature",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kSegmentationPlatformFeedSegmentFeature{
+ "SegmentationPlatformFeedSegmentFeature",
+#if BUILDFLAG(IS_ANDROID)
+ base::FEATURE_ENABLED_BY_DEFAULT
+};
+#else
+ base::FEATURE_DISABLED_BY_DEFAULT
+};
+#endif
+
+const base::Feature kContextualPageActions{"ContextualPageActions",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kContextualPageActionPriceTracking{
+ "ContextualPageActionPriceTracking", base::FEATURE_DISABLED_BY_DEFAULT};
+
+} // namespace segmentation_platform::features
diff --git a/chromium/components/segmentation_platform/public/features.h b/chromium/components/segmentation_platform/public/features.h
index 21cbe0a765c..3c65bc3c9d3 100644
--- a/chromium/components/segmentation_platform/public/features.h
+++ b/chromium/components/segmentation_platform/public/features.h
@@ -6,9 +6,9 @@
#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_FEATURES_H_
#include "base/feature_list.h"
+#include "base/strings/string_piece.h"
-namespace segmentation_platform {
-namespace features {
+namespace segmentation_platform::features {
// Core feature flag for segmentation platform.
extern const base::Feature kSegmentationPlatformFeature;
@@ -23,7 +23,19 @@ extern const base::Feature kSegmentationStructuredMetricsFeature;
// Feature flag for enabling UKM based engine.
extern const base::Feature kSegmentationPlatformUkmEngine;
-} // namespace features
-} // namespace segmentation_platform
+// Feature flag for enabling low engagement segmentation key.
+extern const base::Feature kSegmentationPlatformLowEngagementFeature;
+
+// Feature flag for enabling Feed user segments feature.
+extern const base::Feature kSegmentationPlatformFeedSegmentFeature;
+
+// Feature flag for enabling contextual page actions. Only effective when at
+// least one action is enabled.
+extern const base::Feature kContextualPageActions;
+
+// Feature flag for enabling price tracking action feature.
+extern const base::Feature kContextualPageActionPriceTracking;
+
+} // namespace segmentation_platform::features
#endif // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_FEATURES_H_
diff --git a/chromium/components/segmentation_platform/public/field_trial_register.h b/chromium/components/segmentation_platform/public/field_trial_register.h
new file mode 100644
index 00000000000..626c8844952
--- /dev/null
+++ b/chromium/components/segmentation_platform/public/field_trial_register.h
@@ -0,0 +1,42 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_FIELD_TRIAL_REGISTER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_FIELD_TRIAL_REGISTER_H_
+
+#include "base/strings/string_piece.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+
+namespace segmentation_platform {
+
+// A delegate class that handles recording of the selected segmentation groups
+// to metrics.
+class FieldTrialRegister {
+ public:
+ FieldTrialRegister() = default;
+ virtual ~FieldTrialRegister() = default;
+
+ FieldTrialRegister(FieldTrialRegister&) = delete;
+ FieldTrialRegister& operator=(FieldTrialRegister&) = delete;
+
+ // Records that the current session uses `trial_name` and `group_name` as
+ // segmentation groups. Calling multiple times with same `trial_name`
+ // will replace the existing group with the new one, but note that the
+ // previous logs already closed / staged for upload will not be changed.
+ virtual void RegisterFieldTrial(base::StringPiece trial_name,
+ base::StringPiece group_name) = 0;
+
+ // Registers subsegments based on the `subsegment_rank` of the segment when
+ // the subsegment mapping was provided by the segment. The `subsegment_rank`
+ // should be computed based on the subsegment discrete mapping in the model
+ // metadata.
+ virtual void RegisterSubsegmentFieldTrialIfNeeded(
+ base::StringPiece trial_name,
+ proto::SegmentId segment_id,
+ int subsegment_rank) = 0;
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_FIELD_TRIAL_REGISTER_H_
diff --git a/chromium/components/segmentation_platform/public/local_state_helper.h b/chromium/components/segmentation_platform/public/local_state_helper.h
new file mode 100644
index 00000000000..14d8e0608af
--- /dev/null
+++ b/chromium/components/segmentation_platform/public/local_state_helper.h
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_LOCAL_STATE_HELPER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_LOCAL_STATE_HELPER_H_
+
+#include "base/time/time.h"
+
+class PrefService;
+
+namespace segmentation_platform {
+
+// A helper class for keeping track of pref service entries from browser local
+// state.
+class LocalStateHelper {
+ public:
+ static LocalStateHelper& GetInstance();
+
+ // Initializes the PrefService that is used by this object.
+ // Needs to be called before calling all the get and set
+ // methods.
+ virtual void Initialize(PrefService* local_state) = 0;
+
+ // Sets and gets time in local state for the given pref name.
+ virtual void SetPrefTime(const char* pref_name, base::Time time) = 0;
+ virtual base::Time GetPrefTime(const char* pref_name) const = 0;
+
+ virtual ~LocalStateHelper() = default;
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_LOCAL_STATE_HELPER_H_ \ No newline at end of file
diff --git a/chromium/components/segmentation_platform/public/model_provider.cc b/chromium/components/segmentation_platform/public/model_provider.cc
index 1eede716181..3f957a25faa 100644
--- a/chromium/components/segmentation_platform/public/model_provider.cc
+++ b/chromium/components/segmentation_platform/public/model_provider.cc
@@ -6,9 +6,8 @@
namespace segmentation_platform {
-ModelProvider::ModelProvider(
- optimization_guide::proto::OptimizationTarget optimization_target)
- : optimization_target_(optimization_target) {}
+ModelProvider::ModelProvider(proto::SegmentId segment_id)
+ : segment_id_(segment_id) {}
ModelProvider::~ModelProvider() = default;
diff --git a/chromium/components/segmentation_platform/public/model_provider.h b/chromium/components/segmentation_platform/public/model_provider.h
index fe41711110e..cfbc9b5ff1b 100644
--- a/chromium/components/segmentation_platform/public/model_provider.h
+++ b/chromium/components/segmentation_platform/public/model_provider.h
@@ -7,7 +7,7 @@
#include "base/callback.h"
#include "base/task/sequenced_task_runner.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace segmentation_platform {
@@ -19,15 +19,12 @@ class SegmentationModelMetadata;
// single optimization target.
class ModelProvider {
public:
- using ModelUpdatedCallback = base::RepeatingCallback<void(
- optimization_guide::proto::OptimizationTarget,
- proto::SegmentationModelMetadata,
- int64_t)>;
+ using ModelUpdatedCallback = base::RepeatingCallback<
+ void(proto::SegmentId, proto::SegmentationModelMetadata, int64_t)>;
using ExecutionCallback =
base::OnceCallback<void(const absl::optional<float>&)>;
- explicit ModelProvider(
- optimization_guide::proto::OptimizationTarget optimization_target);
+ explicit ModelProvider(proto::SegmentId segment_id);
virtual ~ModelProvider();
ModelProvider(ModelProvider&) = delete;
@@ -57,7 +54,7 @@ class ModelProvider {
virtual bool ModelAvailable() = 0;
protected:
- const optimization_guide::proto::OptimizationTarget optimization_target_;
+ const proto::SegmentId segment_id_;
};
// Interface used by segmentation platform to create ModelProvider(s).
@@ -65,15 +62,14 @@ class ModelProviderFactory {
public:
virtual ~ModelProviderFactory();
- // Creates a model provider for the given `optimization_target`.
- virtual std::unique_ptr<ModelProvider> CreateProvider(
- optimization_guide::proto::OptimizationTarget) = 0;
+ // Creates a model provider for the given `segment_id`.
+ virtual std::unique_ptr<ModelProvider> CreateProvider(proto::SegmentId) = 0;
// Creates a default model provider to be used when the original provider did
// not provide a model. Returns `nullptr` when a default provider is not
// available.
virtual std::unique_ptr<ModelProvider> CreateDefaultProvider(
- optimization_guide::proto::OptimizationTarget) = 0;
+ proto::SegmentId) = 0;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/public/proto/BUILD.gn b/chromium/components/segmentation_platform/public/proto/BUILD.gn
new file mode 100644
index 00000000000..2ae31b387ee
--- /dev/null
+++ b/chromium/components/segmentation_platform/public/proto/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2022 The Chromium 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("//third_party/protobuf/proto_library.gni")
+
+if (is_android) {
+ import("//build/config/android/rules.gni")
+}
+
+proto_library("proto") {
+ proto_in_dir = "//"
+ sources = [ "segmentation_platform.proto" ]
+}
+
+if (is_android) {
+ proto_java_library("segmentation_platform_proto_java") {
+ proto_path = "//"
+ sources = [ "segmentation_platform.proto" ]
+ }
+}
diff --git a/chromium/components/segmentation_platform/public/proto/segmentation_platform.proto b/chromium/components/segmentation_platform/public/proto/segmentation_platform.proto
new file mode 100644
index 00000000000..99c9b17f41a
--- /dev/null
+++ b/chromium/components/segmentation_platform/public/proto/segmentation_platform.proto
@@ -0,0 +1,61 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+option java_package = "org.chromium.components.segmentation_platform.proto";
+option java_outer_classname = "SegmentationProto";
+
+package segmentation_platform.proto;
+
+// List of user segment types.
+// Any segment that needs a server provided model should have an entry in
+// OptimizationTarget. If the segment only uses default model, then the segment
+// should have a value higher than `MAX_OPTIMIZATION_TARGET`.
+enum SegmentId {
+ OPTIMIZATION_TARGET_UNKNOWN = 0;
+ // Should only be applied when the page load is predicted to be painful.
+ OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD = 1;
+ // Target for supplying the language detection model via the model downloader.
+ OPTIMIZATION_TARGET_LANGUAGE_DETECTION = 2;
+ // Target for determining topics present on a page.
+ OPTIMIZATION_TARGET_PAGE_TOPICS = 3;
+ // Target for segmentation: New tab page user.
+ OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB = 4;
+ // Target for segmentation: Share user.
+ OPTIMIZATION_TARGET_SEGMENTATION_SHARE = 5;
+ // Target for segmentation: Voice user.
+ OPTIMIZATION_TARGET_SEGMENTATION_VOICE = 6;
+ // Target for model validation.
+ OPTIMIZATION_TARGET_MODEL_VALIDATION = 7;
+ // Target for determining entities present on a page.
+ OPTIMIZATION_TARGET_PAGE_ENTITIES = 8;
+ // Target for Chrome Permissions Suggestions Service: Notification permission.
+ OPTIMIZATION_TARGET_NOTIFICATION_PERMISSION_PREDICTIONS = 9;
+ // Target that enables data collection on client side for various experiments.
+ OPTIMIZATION_TARGET_SEGMENTATION_DUMMY = 10;
+ // Target for segmentation: Chrome Android Start user.
+ OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID = 11;
+ // Target for segmentation: Query Tiles user.
+ OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES = 12;
+ // Target for determining the UI visibility of a page.
+ OPTIMIZATION_TARGET_PAGE_VISIBILITY = 13;
+ // Target for supplying the Autofill Assistant annotate DOM model via the
+ // model downloader.
+ OPTIMIZATION_TARGET_AUTOFILL_ASSISTANT = 14;
+ // Target for determining topics present on a page.
+ // TODO(crbug/1266504): Remove PAGE_TOPICS in favor of this target.
+ OPTIMIZATION_TARGET_PAGE_TOPICS_V2 = 15;
+ // Target for segmentation: Determine users with low engagement with chrome.
+ OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT = 16;
+ // Target for segmentation: Determine users who prefer to use Feed.
+ OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER = 17;
+ // Target for price tracking action when shown as a contextual page action.
+ OPTIMIZATION_TARGET_CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING = 18;
+ // Add new entries to OptimizationTarget proto.
+
+ // New entries should start from a 1000 if OptimizationTarget does not have a
+ // corresponding type.
+ MAX_OPTIMIZATION_TARGET = 999;
+};
diff --git a/chromium/components/segmentation_platform/public/segment_selection_result.h b/chromium/components/segmentation_platform/public/segment_selection_result.h
index 917ba61ca78..70e223f76a8 100644
--- a/chromium/components/segmentation_platform/public/segment_selection_result.h
+++ b/chromium/components/segmentation_platform/public/segment_selection_result.h
@@ -5,11 +5,9 @@
#ifndef COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_SEGMENT_SELECTION_RESULT_H_
#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_SEGMENT_SELECTION_RESULT_H_
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-using optimization_guide::proto::OptimizationTarget;
-
namespace segmentation_platform {
// The result of segmentation and related metadata.
@@ -26,7 +24,7 @@ struct SegmentSelectionResult {
// The result of segmentation. Can be empty if the the backend couldn't select
// a segment with confidence.
- absl::optional<OptimizationTarget> segment;
+ absl::optional<proto::SegmentId> segment;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/public/segmentation_platform_service.h b/chromium/components/segmentation_platform/public/segmentation_platform_service.h
index fcdf346199c..dac91f3c120 100644
--- a/chromium/components/segmentation_platform/public/segmentation_platform_service.h
+++ b/chromium/components/segmentation_platform/public/segmentation_platform_service.h
@@ -10,8 +10,10 @@
#include "base/callback.h"
#include "base/observer_list_types.h"
#include "base/supports_user_data.h"
+#include "base/types/id_type.h"
#include "build/build_config.h"
#include "components/keyed_service/core/keyed_service.h"
+#include "components/segmentation_platform/public/trigger.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/jni_android.h"
@@ -22,6 +24,9 @@ class PrefRegistrySimple;
namespace segmentation_platform {
class ServiceProxy;
struct SegmentSelectionResult;
+struct TriggerContext;
+
+using CallbackId = base::IdType32<class OnDemandSegmentSelectionCallbackTag>;
// The core class of segmentation platform that integrates all the required
// pieces on the client side.
@@ -64,12 +69,35 @@ class SegmentationPlatformService : public KeyedService,
virtual SegmentSelectionResult GetCachedSegmentResult(
const std::string& segmentation_key) = 0;
+ // Called to register a callback that will be invoked on segment selection
+ // on-demand. Returns a callback ID that can be used for unregister.
+ using OnDemandSegmentSelectionCallback =
+ base::RepeatingCallback<void(const SegmentSelectionResult&,
+ const TriggerContext&)>;
+ virtual CallbackId RegisterOnDemandSegmentSelectionCallback(
+ const std::string& segmentation_key,
+ const OnDemandSegmentSelectionCallback& callback) = 0;
+
+ // Called to unregister the callback with the given callback_id.
+ virtual void UnregisterOnDemandSegmentSelectionCallback(
+ CallbackId callback_id,
+ const std::string& segmentation_key) = 0;
+
+ // Called when a trigger event happens.
+ virtual void OnTrigger(TriggerType trigger,
+ const TriggerContext& trigger_context) = 0;
+
// Called to enable or disable metrics collection. Must be explicitly called
// on startup.
virtual void EnableMetrics(bool signal_collection_allowed) = 0;
// Called to get the proxy that is used for debugging purpose.
virtual ServiceProxy* GetServiceProxy();
+
+ // Returns true when platform finished initializing, and can execute models.
+ // The `GetSelectedSegment()` calls work without full platform initialization
+ // since they load results from previous sessions.
+ virtual bool IsPlatformInitialized() = 0;
};
} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/public/service_proxy.cc b/chromium/components/segmentation_platform/public/service_proxy.cc
index 616fbe3cfc1..f4ffc6ebbc9 100644
--- a/chromium/components/segmentation_platform/public/service_proxy.cc
+++ b/chromium/components/segmentation_platform/public/service_proxy.cc
@@ -6,7 +6,7 @@
namespace segmentation_platform {
-ServiceProxy::SegmentStatus::SegmentStatus(OptimizationTarget segment_id,
+ServiceProxy::SegmentStatus::SegmentStatus(SegmentId segment_id,
const std::string& segment_metadata,
const std::string& prediction_result,
bool can_execute_segment)
@@ -16,7 +16,7 @@ ServiceProxy::SegmentStatus::SegmentStatus(OptimizationTarget segment_id,
can_execute_segment(can_execute_segment) {}
ServiceProxy::ClientInfo::ClientInfo(const std::string& segmentation_key,
- OptimizationTarget selected_segment)
+ SegmentId selected_segment)
: segmentation_key(segmentation_key), selected_segment(selected_segment) {}
ServiceProxy::ClientInfo::~ClientInfo() = default;
diff --git a/chromium/components/segmentation_platform/public/service_proxy.h b/chromium/components/segmentation_platform/public/service_proxy.h
index 10f96e4e33c..90303eeaa87 100644
--- a/chromium/components/segmentation_platform/public/service_proxy.h
+++ b/chromium/components/segmentation_platform/public/service_proxy.h
@@ -9,23 +9,23 @@
#include <vector>
#include "base/observer_list_types.h"
-#include "components/optimization_guide/proto/models.pb.h"
-
-using optimization_guide::proto::OptimizationTarget;
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
namespace segmentation_platform {
+using proto::SegmentId;
+
// A helper class to expose internals of the segmentationss service to a logging
// component and/or debug UI.
class ServiceProxy {
public:
// Status about a segment.
struct SegmentStatus {
- SegmentStatus(OptimizationTarget segment_id,
+ SegmentStatus(SegmentId segment_id,
const std::string& segment_metadata,
const std::string& prediction_result,
bool can_execute_segment);
- OptimizationTarget segment_id;
+ SegmentId segment_id;
std::string segment_metadata;
std::string prediction_result;
bool can_execute_segment;
@@ -33,13 +33,12 @@ class ServiceProxy {
// Information about a client to the segmentation platform.
struct ClientInfo {
- ClientInfo(const std::string& segmentation_key,
- OptimizationTarget selected_segment);
+ ClientInfo(const std::string& segmentation_key, SegmentId selected_segment);
~ClientInfo();
ClientInfo(const ClientInfo& other);
std::string segmentation_key;
- OptimizationTarget selected_segment;
+ SegmentId selected_segment;
std::vector<SegmentStatus> segment_status;
};
@@ -63,16 +62,16 @@ class ServiceProxy {
virtual void GetServiceStatus() = 0;
// Executes the given segment identified by |segment_id|.
- virtual void ExecuteModel(OptimizationTarget segment_id) = 0;
+ virtual void ExecuteModel(SegmentId segment_id) = 0;
// Overwrites the result for the given segment identified by |segment_id|.
// This will trigger a new round of segment selection and update the existing
// result in Prefs.
- virtual void OverwriteResult(OptimizationTarget segment_id, float result) = 0;
+ virtual void OverwriteResult(SegmentId segment_id, float result) = 0;
// Sets the selected segment for the config identified by |segment_id|.
virtual void SetSelectedSegment(const std::string& segmentation_key,
- OptimizationTarget segment_id) = 0;
+ SegmentId segment_id) = 0;
protected:
ServiceProxy() = default;
diff --git a/chromium/components/segmentation_platform/public/trigger.h b/chromium/components/segmentation_platform/public/trigger.h
new file mode 100644
index 00000000000..2fdfebb0e74
--- /dev/null
+++ b/chromium/components/segmentation_platform/public/trigger.h
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_H_
+
+namespace segmentation_platform {
+
+// Various trigger events that drive on-demand model execution.
+enum class TriggerType {
+ kNone = 0,
+ kPageLoad = 1,
+ kMaxValue = kPageLoad,
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_H_
diff --git a/chromium/components/segmentation_platform/public/trigger_context.cc b/chromium/components/segmentation_platform/public/trigger_context.cc
new file mode 100644
index 00000000000..59c97358fb3
--- /dev/null
+++ b/chromium/components/segmentation_platform/public/trigger_context.cc
@@ -0,0 +1,28 @@
+// Copyright 2022 The Chromium 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/segmentation_platform/public/trigger_context.h"
+
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#include "components/segmentation_platform/public/jni_headers/TriggerContext_jni.h"
+#endif // BUILDFLAG(IS_ANDROID)
+
+namespace segmentation_platform {
+
+TriggerContext::TriggerContext() = default;
+
+TriggerContext::~TriggerContext() = default;
+
+#if BUILDFLAG(IS_ANDROID)
+base::android::ScopedJavaLocalRef<jobject> TriggerContext::CreateJavaObject()
+ const {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ return Java_TriggerContext_createTriggerContext(env);
+}
+#endif // BUILDFLAG(IS_ANDROID)
+
+} // namespace segmentation_platform
diff --git a/chromium/components/segmentation_platform/public/trigger_context.h b/chromium/components/segmentation_platform/public/trigger_context.h
new file mode 100644
index 00000000000..c45343b71c1
--- /dev/null
+++ b/chromium/components/segmentation_platform/public/trigger_context.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_CONTEXT_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_CONTEXT_H_
+
+#include "base/memory/raw_ptr.h"
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/jni_android.h"
+#endif // BUILDFLAG(IS_ANDROID)
+
+namespace segmentation_platform {
+
+// Contains contextual information for a trigger event.
+struct TriggerContext {
+ public:
+ TriggerContext();
+ virtual ~TriggerContext();
+
+#if BUILDFLAG(IS_ANDROID)
+ // Returns a Java object representing the TriggerContext.
+ virtual base::android::ScopedJavaLocalRef<jobject> CreateJavaObject() const;
+#endif // BUILDFLAG(IS_ANDROID)
+};
+
+} // namespace segmentation_platform
+
+#endif // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_TRIGGER_CONTEXT_H_
diff --git a/chromium/components/send_tab_to_self/BUILD.gn b/chromium/components/send_tab_to_self/BUILD.gn
index 6fba4c0f8d9..7dbf9d306dd 100644
--- a/chromium/components/send_tab_to_self/BUILD.gn
+++ b/chromium/components/send_tab_to_self/BUILD.gn
@@ -2,9 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+if (is_android) {
+ import("//build/config/android/rules.gni")
+}
+
static_library("send_tab_to_self") {
sources = [
- "features.cc",
+ "entry_point_display_reason.cc",
+ "entry_point_display_reason.h",
"features.h",
"metrics_util.cc",
"metrics_util.h",
@@ -28,7 +33,10 @@ static_library("send_tab_to_self") {
"//base",
"//components/history/core/browser",
"//components/keyed_service/core",
+ "//components/prefs",
"//components/send_tab_to_self/proto:send_tab_to_self_proto",
+ "//components/signin/public/base",
+ "//components/signin/public/identity_manager",
"//components/strings",
"//components/sync",
"//components/sync_device_info",
@@ -50,6 +58,21 @@ static_library("send_tab_to_self") {
}
}
+if (is_android) {
+ java_cpp_enum("java_enum_srcjar") {
+ visibility = [ ":*" ]
+ sources = [ "entry_point_display_reason.h" ]
+ }
+
+ android_library("send_tab_to_self_java") {
+ srcjar_deps = [ ":java_enum_srcjar" ]
+
+ # Important: the generated enum uses the @IntDef annotation provided by
+ # this dependency.
+ deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
+ }
+}
+
source_set("test_support") {
testonly = true
sources = [
@@ -65,16 +88,18 @@ source_set("test_support") {
source_set("unit_tests") {
testonly = true
sources = [
- "features_unittest.cc",
+ "entry_point_display_reason_unittest.cc",
"send_tab_to_self_bridge_unittest.cc",
"send_tab_to_self_entry_unittest.cc",
"target_device_info_unittest.cc",
]
deps = [
":send_tab_to_self",
+ ":test_support",
"//base",
"//base/test:test_support",
"//components/history/core/browser",
+ "//components/prefs:test_support",
"//components/send_tab_to_self/proto:send_tab_to_self_proto",
"//components/sync:test_support",
"//components/sync_device_info:test_support",
diff --git a/chromium/components/send_tab_to_self/DEPS b/chromium/components/send_tab_to_self/DEPS
index 1c57b57d209..67bfcc80dfd 100644
--- a/chromium/components/send_tab_to_self/DEPS
+++ b/chromium/components/send_tab_to_self/DEPS
@@ -2,6 +2,9 @@ include_rules = [
"+components/history",
"+components/infobars",
"+components/keyed_service/core",
+ "+components/prefs",
+ "+components/signin/public/base",
+ "+components/signin/public/identity_manager",
"+components/strings",
"+components/sync",
"+components/sync_device_info",
diff --git a/chromium/components/send_tab_to_self/entry_point_display_reason.cc b/chromium/components/send_tab_to_self/entry_point_display_reason.cc
new file mode 100644
index 00000000000..41366304120
--- /dev/null
+++ b/chromium/components/send_tab_to_self/entry_point_display_reason.cc
@@ -0,0 +1,62 @@
+// Copyright 2022 The Chromium 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/send_tab_to_self/entry_point_display_reason.h"
+
+#include "components/prefs/pref_service.h"
+#include "components/send_tab_to_self/features.h"
+#include "components/send_tab_to_self/send_tab_to_self_model.h"
+#include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
+#include "components/signin/public/base/signin_pref_names.h"
+#include "components/signin/public/identity_manager/account_info.h"
+#include "components/sync/driver/sync_service.h"
+#include "url/gurl.h"
+
+namespace send_tab_to_self {
+
+namespace {
+
+bool ShouldOfferSignin(syncer::SyncService* sync_service,
+ PrefService* pref_service) {
+ return pref_service->GetBoolean(prefs::kSigninAllowed) && sync_service &&
+ sync_service->GetAccountInfo().IsEmpty() &&
+ !sync_service->IsLocalSyncEnabled();
+}
+
+} // namespace
+
+absl::optional<EntryPointDisplayReason> GetEntryPointDisplayReason(
+ const GURL& url_to_share,
+ syncer::SyncService* sync_service,
+ SendTabToSelfSyncService* send_tab_to_self_sync_service,
+ PrefService* pref_service) {
+ if (!url_to_share.SchemeIsHTTPOrHTTPS())
+ return absl::nullopt;
+
+ if (!send_tab_to_self_sync_service) {
+ // Can happen in incognito or guest profile.
+ return absl::nullopt;
+ }
+
+ if (ShouldOfferSignin(sync_service, pref_service) &&
+ base::FeatureList::IsEnabled(kSendTabToSelfSigninPromo)) {
+ return EntryPointDisplayReason::kOfferSignIn;
+ }
+
+ SendTabToSelfModel* model =
+ send_tab_to_self_sync_service->GetSendTabToSelfModel();
+ if (!model->IsReady())
+ return absl::nullopt;
+
+ if (!model->HasValidTargetDevice()) {
+ return base::FeatureList::IsEnabled(kSendTabToSelfSigninPromo)
+ ? absl::make_optional(
+ EntryPointDisplayReason::kInformNoTargetDevice)
+ : absl::nullopt;
+ }
+
+ return EntryPointDisplayReason::kOfferFeature;
+}
+
+} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/entry_point_display_reason.h b/chromium/components/send_tab_to_self/entry_point_display_reason.h
new file mode 100644
index 00000000000..d2ab8ecf369
--- /dev/null
+++ b/chromium/components/send_tab_to_self/entry_point_display_reason.h
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SEND_TAB_TO_SELF_ENTRY_POINT_DISPLAY_REASON_H_
+#define COMPONENTS_SEND_TAB_TO_SELF_ENTRY_POINT_DISPLAY_REASON_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class GURL;
+class PrefService;
+
+namespace syncer {
+class SyncService;
+} // namespace syncer
+
+namespace send_tab_to_self {
+
+class SendTabToSelfSyncService;
+
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: (
+// org.chromium.chrome.browser.share.send_tab_to_self)
+enum class EntryPointDisplayReason {
+ // The send-tab-to-self entry point should be shown because all the conditions
+ // are met and the feature is ready to be used.
+ kOfferFeature,
+ // The user might be able to use send-tab-to-self if they sign in, so offer
+ // that. "Might" because the list of target devices can't be known yet, it
+ // could be empty (see below).
+ kOfferSignIn,
+ // All the conditions for send-tab-to-self are met, but there is no valid
+ // target device. In that case the entry point should inform the user they
+ // can enjoy the feature by signing in on other devices.
+ kInformNoTargetDevice,
+};
+
+// |sync_service| and |send_tab_to_self_sync_service| can be null.
+absl::optional<EntryPointDisplayReason> GetEntryPointDisplayReason(
+ const GURL& url_to_share,
+ syncer::SyncService* sync_service,
+ SendTabToSelfSyncService* send_tab_to_self_sync_service,
+ PrefService* pref_service);
+
+} // namespace send_tab_to_self
+
+#endif // COMPONENTS_SEND_TAB_TO_SELF_ENTRY_POINT_DISPLAY_REASON_H_
diff --git a/chromium/components/send_tab_to_self/entry_point_display_reason_unittest.cc b/chromium/components/send_tab_to_self/entry_point_display_reason_unittest.cc
new file mode 100644
index 00000000000..e0e755d2e62
--- /dev/null
+++ b/chromium/components/send_tab_to_self/entry_point_display_reason_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright 2022 The Chromium 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/send_tab_to_self/entry_point_display_reason.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/send_tab_to_self/features.h"
+#include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
+#include "components/send_tab_to_self/test_send_tab_to_self_model.h"
+#include "components/signin/public/base/signin_pref_names.h"
+#include "components/sync/driver/test_sync_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace send_tab_to_self {
+
+namespace {
+
+const char kHttpsUrl[] = "https://www.foo.com";
+const char kHttpUrl[] = "http://www.foo.com";
+
+class FakeSendTabToSelfModel : public TestSendTabToSelfModel {
+ public:
+ FakeSendTabToSelfModel() = default;
+ ~FakeSendTabToSelfModel() override = default;
+
+ void SetIsReady(bool is_ready) { is_ready_ = is_ready; }
+ void SetHasValidTargetDevice(bool has_valid_target_device) {
+ if (has_valid_target_device) {
+ DCHECK(is_ready_) << "Target devices are only known if the model's ready";
+ }
+ has_valid_target_device_ = has_valid_target_device;
+ }
+
+ bool IsReady() override { return is_ready_; }
+ bool HasValidTargetDevice() override { return has_valid_target_device_; }
+
+ private:
+ bool is_ready_ = false;
+ bool has_valid_target_device_ = false;
+};
+
+class FakeSendTabToSelfSyncService : public SendTabToSelfSyncService {
+ public:
+ FakeSendTabToSelfSyncService() = default;
+ ~FakeSendTabToSelfSyncService() override = default;
+
+ FakeSendTabToSelfModel* GetSendTabToSelfModel() override { return &model_; }
+
+ private:
+ FakeSendTabToSelfModel model_;
+};
+
+class EntryPointDisplayReasonTest : public ::testing::Test {
+ public:
+ EntryPointDisplayReasonTest() {
+ pref_service_.registry()->RegisterBooleanPref(prefs::kSigninAllowed, true);
+ }
+
+ syncer::TestSyncService* sync_service() { return &sync_service_; }
+ FakeSendTabToSelfSyncService* send_tab_to_self_sync_service() {
+ return &send_tab_to_self_sync_service_;
+ }
+ TestingPrefServiceSimple* pref_service() { return &pref_service_; }
+
+ void SignIn() {
+ CoreAccountInfo account;
+ account.gaia = "gaia_id";
+ account.email = "email@test.com";
+ account.account_id = CoreAccountId::FromGaiaId(account.gaia);
+ sync_service_.SetAccountInfo(account);
+ }
+
+ private:
+ syncer::TestSyncService sync_service_;
+ FakeSendTabToSelfSyncService send_tab_to_self_sync_service_;
+ TestingPrefServiceSimple pref_service_;
+};
+
+TEST_F(EntryPointDisplayReasonTest,
+ ShouldHideEntryPointIfSignedOutAndPromoFeatureDisabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndDisableFeature(kSendTabToSelfSigninPromo);
+
+ EXPECT_FALSE(GetEntryPointDisplayReason(GURL(kHttpsUrl), sync_service(),
+ send_tab_to_self_sync_service(),
+ pref_service()));
+}
+
+TEST_F(EntryPointDisplayReasonTest,
+ ShouldShowPromoIfSignedOutAndPromoFeatureEnabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kSendTabToSelfSigninPromo);
+
+ EXPECT_EQ(EntryPointDisplayReason::kOfferSignIn,
+ GetEntryPointDisplayReason(GURL(kHttpsUrl), sync_service(),
+ send_tab_to_self_sync_service(),
+ pref_service()));
+}
+
+TEST_F(EntryPointDisplayReasonTest, ShouldHideEntryPointIfModelNotReady) {
+ SignIn();
+ send_tab_to_self_sync_service()->GetSendTabToSelfModel()->SetIsReady(false);
+ send_tab_to_self_sync_service()
+ ->GetSendTabToSelfModel()
+ ->SetHasValidTargetDevice(false);
+
+ EXPECT_FALSE(GetEntryPointDisplayReason(GURL(kHttpsUrl), sync_service(),
+ send_tab_to_self_sync_service(),
+ pref_service()));
+}
+
+TEST_F(EntryPointDisplayReasonTest,
+ ShouldHideEntryPointIfHasNoValidTargetDeviceAndPromoFeatureDisabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndDisableFeature(kSendTabToSelfSigninPromo);
+
+ SignIn();
+ send_tab_to_self_sync_service()->GetSendTabToSelfModel()->SetIsReady(true);
+ send_tab_to_self_sync_service()
+ ->GetSendTabToSelfModel()
+ ->SetHasValidTargetDevice(false);
+
+ EXPECT_FALSE(GetEntryPointDisplayReason(GURL(kHttpsUrl), sync_service(),
+ send_tab_to_self_sync_service(),
+ pref_service()));
+}
+
+TEST_F(EntryPointDisplayReasonTest,
+ ShouldShowPromoIfHasNoValidTargetDeviceAndPromoFeatureEnabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kSendTabToSelfSigninPromo);
+
+ SignIn();
+ send_tab_to_self_sync_service()->GetSendTabToSelfModel()->SetIsReady(true);
+ send_tab_to_self_sync_service()
+ ->GetSendTabToSelfModel()
+ ->SetHasValidTargetDevice(false);
+
+ EXPECT_EQ(EntryPointDisplayReason::kInformNoTargetDevice,
+ GetEntryPointDisplayReason(GURL(kHttpsUrl), sync_service(),
+ send_tab_to_self_sync_service(),
+ pref_service()));
+}
+
+TEST_F(EntryPointDisplayReasonTest, ShouldOnlyOfferFeatureIfHttpOrHttps) {
+ SignIn();
+ send_tab_to_self_sync_service()->GetSendTabToSelfModel()->SetIsReady(true);
+ send_tab_to_self_sync_service()
+ ->GetSendTabToSelfModel()
+ ->SetHasValidTargetDevice(true);
+
+ EXPECT_EQ(EntryPointDisplayReason::kOfferFeature,
+ GetEntryPointDisplayReason(GURL(kHttpsUrl), sync_service(),
+ send_tab_to_self_sync_service(),
+ pref_service()));
+
+ EXPECT_EQ(EntryPointDisplayReason::kOfferFeature,
+ GetEntryPointDisplayReason(GURL(kHttpUrl), sync_service(),
+ send_tab_to_self_sync_service(),
+ pref_service()));
+
+ EXPECT_FALSE(GetEntryPointDisplayReason(GURL("192.168.0.0"), sync_service(),
+ send_tab_to_self_sync_service(),
+ pref_service()));
+
+ EXPECT_FALSE(GetEntryPointDisplayReason(
+ GURL("chrome-untrusted://url"), sync_service(),
+ send_tab_to_self_sync_service(), pref_service()));
+
+ EXPECT_FALSE(GetEntryPointDisplayReason(
+ GURL("chrome://flags"), sync_service(), send_tab_to_self_sync_service(),
+ pref_service()));
+
+ EXPECT_FALSE(GetEntryPointDisplayReason(
+ GURL("tel:07399999999"), sync_service(), send_tab_to_self_sync_service(),
+ pref_service()));
+}
+
+TEST_F(EntryPointDisplayReasonTest, ShouldHideEntryPointInIncognitoMode) {
+ // Note: if changing this, audit profile-finding logic in the feature.
+ // For example, NotificationManager.java in the Android code assumes
+ // incognito is not supported.
+ EXPECT_FALSE(GetEntryPointDisplayReason(GURL(kHttpsUrl), nullptr, nullptr,
+ pref_service()));
+}
+
+} // namespace
+
+} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/features.cc b/chromium/components/send_tab_to_self/features.cc
deleted file mode 100644
index 2bc791e45d7..00000000000
--- a/chromium/components/send_tab_to_self/features.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/send_tab_to_self/features.h"
-
-#include "base/feature_list.h"
-#include "build/build_config.h"
-#include "build/buildflag.h"
-#include "components/sync/base/sync_prefs.h"
-#include "components/sync/base/user_selectable_type.h"
-
-namespace send_tab_to_self {
-
-const base::Feature kSendTabToSelfSigninPromo{
- "SendTabToSelfSigninPromo", base::FEATURE_DISABLED_BY_DEFAULT};
-
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-const base::Feature kSendTabToSelfV2{"SendTabToSelfV2",
- base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
-bool IsReceivingEnabledByUserOnThisDevice(PrefService* prefs) {
- // TODO(crbug.com/1015322): SyncPrefs is used directly instead of methods in
- // SyncService due to a dependency of SyncServiceImpl on
- // DeviceInfoSyncService. IsReceivingEnabledByUserOnThisDevice is ultimately
- // used by DeviceInfoSyncClient which is owned by DeviceInfoSyncService.
- syncer::SyncPrefs sync_prefs(prefs);
-
- // IsFirstSetupComplete() false means sync-the-feature is disabled or the
- // consent is missing (e.g. sync setup in progress). The method can return
- // true without actually checking whether the user is signed-in: if they are
- // not, sync-the-transport won't run and receiving tabs would be impossible
- // anyway.
- if (!sync_prefs.IsFirstSetupComplete())
- return true;
-
- // Sync-the-feature was fully setup. The user-configurable bits should be
- // respected in this state.
- return sync_prefs.IsSyncRequested() &&
- sync_prefs.GetSelectedTypes().Has(syncer::UserSelectableType::kTabs);
-}
-
-} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/features.h b/chromium/components/send_tab_to_self/features.h
index 2b10474de42..228b116f8bf 100644
--- a/chromium/components/send_tab_to_self/features.h
+++ b/chromium/components/send_tab_to_self/features.h
@@ -7,14 +7,14 @@
#include "base/feature_list.h"
#include "build/build_config.h"
-
-class PrefService;
+#include "build/buildflag.h"
namespace send_tab_to_self {
// If this feature is enabled and a signed-out user attempts to share a tab,
// they will see a promo to sign-in.
-extern const base::Feature kSendTabToSelfSigninPromo;
+inline constexpr base::Feature kSendTabToSelfSigninPromo{
+ "SendTabToSelfSigninPromo", base::FEATURE_DISABLED_BY_DEFAULT};
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
// If this feature is enabled, show received tabs in a new UI next to the
@@ -22,14 +22,10 @@ extern const base::Feature kSendTabToSelfSigninPromo;
//
// V2 is the default on desktop and the V1 code path has been deleted there, so
// this base::Feature no longer exists on desktop platforms.
-extern const base::Feature kSendTabToSelfV2;
+inline constexpr base::Feature kSendTabToSelfV2{
+ "SendTabToSelfV2", base::FEATURE_DISABLED_BY_DEFAULT};
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-// Returns whether the receiving components of the feature is enabled on this
-// device. This doesn't rely on the SendTabToSelfSyncService to be actively up
-// and ready.
-bool IsReceivingEnabledByUserOnThisDevice(PrefService* prefs);
-
} // namespace send_tab_to_self
#endif // COMPONENTS_SEND_TAB_TO_SELF_FEATURES_H_
diff --git a/chromium/components/send_tab_to_self/features_unittest.cc b/chromium/components/send_tab_to_self/features_unittest.cc
deleted file mode 100644
index c59003af2e5..00000000000
--- a/chromium/components/send_tab_to_self/features_unittest.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/send_tab_to_self/features.h"
-
-#include <memory>
-#include "base/test/task_environment.h"
-
-#include "components/sync/base/sync_prefs.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace send_tab_to_self {
-
-namespace {
-
-class SendTabToSelfFeaturesTest : public testing::Test {
- public:
- SendTabToSelfFeaturesTest() {
- syncer::SyncPrefs::RegisterProfilePrefs(prefs_.registry());
- sync_prefs_ = std::make_unique<syncer::SyncPrefs>(&prefs_);
- }
-
- protected:
- sync_preferences::TestingPrefServiceSyncable prefs_;
- std::unique_ptr<syncer::SyncPrefs> sync_prefs_;
- base::test::TaskEnvironment task_environment_;
-};
-
-TEST_F(SendTabToSelfFeaturesTest, ReceivingEnabledIfSyncTheFeatureEnabled) {
- sync_prefs_->SetSyncRequested(true);
- sync_prefs_->SetFirstSetupComplete();
- sync_prefs_->SetSelectedTypes(
- /*keep_everything_synced=*/false,
- /*registered_types=*/syncer::UserSelectableTypeSet::All(),
- /*selected_types=*/{syncer::UserSelectableType::kTabs});
-
- EXPECT_TRUE(IsReceivingEnabledByUserOnThisDevice(&prefs_));
-}
-
-TEST_F(SendTabToSelfFeaturesTest,
- ReceivingDisabledIfSyncTheFeatureEnabledButStopped) {
- sync_prefs_->SetSyncRequested(false);
- sync_prefs_->SetFirstSetupComplete();
- sync_prefs_->SetSelectedTypes(
- /*keep_everything_synced=*/false,
- /*registered_types=*/syncer::UserSelectableTypeSet::All(),
- /*selected_types=*/{syncer::UserSelectableType::kTabs});
-
- EXPECT_FALSE(IsReceivingEnabledByUserOnThisDevice(&prefs_));
-}
-
-TEST_F(SendTabToSelfFeaturesTest,
- ReceivingDisabledIfSyncTheFeatureEnabledButTabsNotSelected) {
- sync_prefs_->SetSyncRequested(true);
- sync_prefs_->SetFirstSetupComplete();
- sync_prefs_->SetSelectedTypes(
- /*keep_everything_synced=*/false,
- /*registered_types=*/syncer::UserSelectableTypeSet::All(),
- /*selected_types=*/{});
-
- EXPECT_FALSE(IsReceivingEnabledByUserOnThisDevice(&prefs_));
-}
-
-TEST_F(SendTabToSelfFeaturesTest,
- ReceivingEnabledIfSyncSetupIncomplete_SendTabToSelfWhenSignedInEnabled) {
- sync_prefs_->SetSyncRequested(true);
- // Skip setting FirstSetupComplete.
- sync_prefs_->SetSelectedTypes(
- /*keep_everything_synced=*/false,
- /*registered_types=*/syncer::UserSelectableTypeSet::All(),
- /*selected_types=*/{syncer::UserSelectableType::kTabs});
-
- // While the setup isn't complete, the client is still treated as having
- // sync-the-feature disabled.
- EXPECT_TRUE(IsReceivingEnabledByUserOnThisDevice(&prefs_));
-}
-
-TEST_F(
- SendTabToSelfFeaturesTest,
- ReceivingEnabledIfSyncTheFeatureDisabled_SendTabToSelfWhenSignedInEnabled) {
- sync_prefs_->SetSelectedTypes(
- /*keep_everything_synced=*/false,
- /*registered_types=*/syncer::UserSelectableTypeSet::All(),
- /*selected_types=*/{});
-
- EXPECT_TRUE(IsReceivingEnabledByUserOnThisDevice(&prefs_));
-}
-
-} // namespace
-
-} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_bridge.cc b/chromium/components/send_tab_to_self/send_tab_to_self_bridge.cc
index cdd052734d9..8d9ec885e76 100644
--- a/chromium/components/send_tab_to_self/send_tab_to_self_bridge.cc
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_bridge.cc
@@ -263,27 +263,6 @@ std::vector<std::string> SendTabToSelfBridge::GetAllGuids() const {
return keys;
}
-void SendTabToSelfBridge::DeleteAllEntries() {
- if (!change_processor()->IsTrackingMetadata()) {
- DCHECK_EQ(0ul, entries_.size());
- return;
- }
-
- std::unique_ptr<ModelTypeStore::WriteBatch> batch =
- store_->CreateWriteBatch();
-
- std::vector<std::string> all_guids = GetAllGuids();
-
- for (const auto& guid : all_guids) {
- change_processor()->Delete(guid, batch->GetMetadataChangeList());
- batch->DeleteData(guid);
- }
- entries_.clear();
- mru_entry_ = nullptr;
-
- NotifyRemoteSendTabToSelfEntryDeleted(all_guids);
-}
-
const SendTabToSelfEntry* SendTabToSelfBridge::GetEntryByGUID(
const std::string& guid) const {
auto it = entries_.find(guid);
@@ -296,7 +275,6 @@ const SendTabToSelfEntry* SendTabToSelfBridge::GetEntryByGUID(
const SendTabToSelfEntry* SendTabToSelfBridge::AddEntry(
const GURL& url,
const std::string& title,
- base::Time navigation_time,
const std::string& target_device_cache_guid) {
if (!change_processor()->IsTrackingMetadata()) {
// TODO(crbug.com/940512) handle failure case.
@@ -312,7 +290,6 @@ const SendTabToSelfEntry* SendTabToSelfBridge::AddEntry(
// has the first sent tab in progress, and so we will not attempt to resend.
base::Time shared_time = clock_->Now();
if (mru_entry_ && url == mru_entry_->GetURL() &&
- navigation_time == mru_entry_->GetOriginalNavigationTime() &&
shared_time - mru_entry_->GetSharedTime() < kDedupeTime) {
send_tab_to_self::RecordNotificationThrottled();
return mru_entry_;
@@ -330,8 +307,8 @@ const SendTabToSelfEntry* SendTabToSelfBridge::AddEntry(
}
auto entry = std::make_unique<SendTabToSelfEntry>(
- guid, url, trimmed_title, shared_time, navigation_time,
- local_device_name_, target_device_cache_guid);
+ guid, url, trimmed_title, shared_time, local_device_name_,
+ target_device_cache_guid);
std::unique_ptr<ModelTypeStore::WriteBatch> batch =
store_->CreateWriteBatch();
@@ -722,7 +699,8 @@ void SendTabToSelfBridge::DeleteEntries(const std::vector<GURL>& urls) {
for (const GURL& url : urls) {
auto entry = entries_.begin();
while (entry != entries_.end()) {
- bool to_delete = (url == entry->second->GetURL());
+ bool to_delete =
+ (entry->second == nullptr || url == entry->second->GetURL());
std::string guid = entry->first;
entry++;
@@ -739,4 +717,25 @@ void SendTabToSelfBridge::DeleteEntries(const std::vector<GURL>& urls) {
NotifyRemoteSendTabToSelfEntryDeleted(removed_guids);
}
+void SendTabToSelfBridge::DeleteAllEntries() {
+ if (!change_processor()->IsTrackingMetadata()) {
+ DCHECK_EQ(0ul, entries_.size());
+ return;
+ }
+
+ std::unique_ptr<ModelTypeStore::WriteBatch> batch =
+ store_->CreateWriteBatch();
+
+ std::vector<std::string> all_guids = GetAllGuids();
+
+ for (const auto& guid : all_guids) {
+ change_processor()->Delete(guid, batch->GetMetadataChangeList());
+ batch->DeleteData(guid);
+ }
+ entries_.clear();
+ mru_entry_ = nullptr;
+
+ NotifyRemoteSendTabToSelfEntryDeleted(all_guids);
+}
+
} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_bridge.h b/chromium/components/send_tab_to_self/send_tab_to_self_bridge.h
index fecea74332a..65c8b4f213b 100644
--- a/chromium/components/send_tab_to_self/send_tab_to_self_bridge.h
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_bridge.h
@@ -73,13 +73,11 @@ class SendTabToSelfBridge : public syncer::ModelTypeSyncBridge,
// SendTabToSelfModel overrides.
std::vector<std::string> GetAllGuids() const override;
- void DeleteAllEntries() override;
const SendTabToSelfEntry* GetEntryByGUID(
const std::string& guid) const override;
const SendTabToSelfEntry* AddEntry(
const GURL& url,
const std::string& title,
- base::Time navigation_time,
const std::string& target_device_cache_guid) override;
void DeleteEntry(const std::string& guid) override;
void DismissEntry(const std::string& guid) override;
@@ -152,6 +150,8 @@ class SendTabToSelfBridge : public syncer::ModelTypeSyncBridge,
// Delete all of the entries that match the URLs provided.
void DeleteEntries(const std::vector<GURL>& urls);
+ void DeleteAllEntries();
+
// |entries_| is keyed by GUIDs.
SendTabToSelfEntries entries_;
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc b/chromium/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
index e4b84ef3033..9074c81c415 100644
--- a/chromium/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
@@ -60,8 +60,7 @@ ACTION_TEMPLATE(SaveArgPointeeMove,
sync_pb::SendTabToSelfSpecifics CreateSpecifics(
int suffix,
- base::Time shared_time = base::Time::Now(),
- base::Time navigation_time = base::Time::Now()) {
+ base::Time shared_time = base::Time::Now()) {
sync_pb::SendTabToSelfSpecifics specifics;
specifics.set_guid(base::StringPrintf(kGuidFormat, suffix));
specifics.set_url(base::StringPrintf(kURLFormat, suffix));
@@ -70,8 +69,6 @@ sync_pb::SendTabToSelfSpecifics CreateSpecifics(
specifics.set_target_device_sync_cache_guid(kLocalDeviceCacheGuid);
specifics.set_shared_time_usec(
shared_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
- specifics.set_navigation_time_usec(
- navigation_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
return specifics;
}
@@ -199,14 +196,10 @@ class SendTabToSelfBridgeTest : public testing::Test {
// For Model Tests.
void AddSampleEntries() {
// Adds timer to avoid having two entries with the same shared timestamp.
- bridge_->AddEntry(GURL("http://a.com"), "a", AdvanceAndGetTime(),
- kLocalDeviceCacheGuid);
- bridge_->AddEntry(GURL("http://b.com"), "b", AdvanceAndGetTime(),
- kLocalDeviceCacheGuid);
- bridge_->AddEntry(GURL("http://c.com"), "c", AdvanceAndGetTime(),
- kLocalDeviceCacheGuid);
- bridge_->AddEntry(GURL("http://d.com"), "d", AdvanceAndGetTime(),
- kLocalDeviceCacheGuid);
+ bridge_->AddEntry(GURL("http://a.com"), "a", kLocalDeviceCacheGuid);
+ bridge_->AddEntry(GURL("http://b.com"), "b", kLocalDeviceCacheGuid);
+ bridge_->AddEntry(GURL("http://c.com"), "c", kLocalDeviceCacheGuid);
+ bridge_->AddEntry(GURL("http://d.com"), "d", kLocalDeviceCacheGuid);
}
void SetLocalDeviceCacheGuid(const std::string& cache_guid) {
@@ -266,7 +259,7 @@ TEST_F(SendTabToSelfBridgeTest, SyncAddOneEntry) {
syncer::EntityChangeList remote_input;
SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
+ AdvanceAndGetTime(), "device",
kLocalDeviceCacheGuid);
remote_input.push_back(
@@ -301,7 +294,7 @@ TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesOneAdd) {
InitializeBridge();
SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
+ AdvanceAndGetTime(), "device",
kLocalDeviceCacheGuid);
syncer::EntityChangeList add_changes;
@@ -322,7 +315,7 @@ TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesOneDeletion) {
InitializeBridge();
SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
+ AdvanceAndGetTime(), "device",
kLocalDeviceCacheGuid);
syncer::EntityChangeList add_changes;
@@ -347,15 +340,15 @@ TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesOneDeletion) {
TEST_F(SendTabToSelfBridgeTest, LocalHistoryDeletion) {
InitializeBridge();
SendTabToSelfEntry entry1("guid1", GURL("http://www.example.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
+ AdvanceAndGetTime(), "device",
kLocalDeviceCacheGuid);
SendTabToSelfEntry entry2("guid2", GURL("http://www.example2.com/"), "title2",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device2",
+ AdvanceAndGetTime(), "device2",
kLocalDeviceCacheGuid);
SendTabToSelfEntry entry3("guid3", GURL("http://www.example3.com/"), "title3",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device3",
+ AdvanceAndGetTime(), "device3",
kLocalDeviceCacheGuid);
syncer::EntityChangeList add_changes;
@@ -471,8 +464,7 @@ TEST_F(SendTabToSelfBridgeTest, MarkEntryOpenedInformsServer) {
InitializeBridge();
SendTabToSelfEntry entry("guid", GURL("http://g.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "remote",
- "remote");
+ AdvanceAndGetTime(), "remote", "remote");
syncer::EntityChangeList remote_data;
remote_data.push_back(
syncer::EntityChange::CreateAdd("guid", MakeEntityData(entry)));
@@ -491,8 +483,7 @@ TEST_F(SendTabToSelfBridgeTest, DismissEntryInformsServer) {
InitializeBridge();
SendTabToSelfEntry entry("guid", GURL("http://g.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "remote",
- "remote");
+ AdvanceAndGetTime(), "remote", "remote");
syncer::EntityChangeList remote_data;
remote_data.push_back(
syncer::EntityChange::CreateAdd("guid", MakeEntityData(entry)));
@@ -536,12 +527,12 @@ TEST_F(SendTabToSelfBridgeTest, ExpireEntryDuringInit) {
InitializeBridge();
const sync_pb::SendTabToSelfSpecifics expired_specifics =
- CreateSpecifics(1, AdvanceAndGetTime(), AdvanceAndGetTime());
+ CreateSpecifics(1, AdvanceAndGetTime());
AdvanceAndGetTime(kExpiryTime / 2.0);
const sync_pb::SendTabToSelfSpecifics not_expired_specifics =
- CreateSpecifics(2, AdvanceAndGetTime(), AdvanceAndGetTime());
+ CreateSpecifics(2, AdvanceAndGetTime());
sync_pb::ModelTypeState state = StateWithEncryption("ekn");
std::unique_ptr<syncer::MetadataChangeList> metadata_changes =
@@ -577,12 +568,12 @@ TEST_F(SendTabToSelfBridgeTest, AddExpiredEntry) {
metadata_changes->UpdateModelTypeState(state);
const sync_pb::SendTabToSelfSpecifics expired_specifics =
- CreateSpecifics(1, AdvanceAndGetTime(), AdvanceAndGetTime());
+ CreateSpecifics(1, AdvanceAndGetTime());
AdvanceAndGetTime(kExpiryTime);
const sync_pb::SendTabToSelfSpecifics not_expired_specifics =
- CreateSpecifics(2, AdvanceAndGetTime(), AdvanceAndGetTime());
+ CreateSpecifics(2, AdvanceAndGetTime());
EXPECT_CALL(*processor(), Delete(_, _));
@@ -604,26 +595,16 @@ TEST_F(SendTabToSelfBridgeTest, AddInvalidEntries) {
// Add Entry should succeed in this case.
EXPECT_CALL(*processor(), Put(_, _, _));
- EXPECT_NE(nullptr,
- bridge()->AddEntry(GURL("http://www.example.com/"), "d",
- AdvanceAndGetTime(), kLocalDeviceCacheGuid));
+ EXPECT_NE(nullptr, bridge()->AddEntry(GURL("http://www.example.com/"), "d",
+ kLocalDeviceCacheGuid));
// Add Entry should fail on invalid URLs.
EXPECT_CALL(*processor(), Put(_, _, _)).Times(0);
- EXPECT_EQ(nullptr, bridge()->AddEntry(GURL(), "d", AdvanceAndGetTime(),
+ EXPECT_EQ(nullptr, bridge()->AddEntry(GURL(), "d", kLocalDeviceCacheGuid));
+ EXPECT_EQ(nullptr, bridge()->AddEntry(GURL("http://?k=v"), "d",
+ kLocalDeviceCacheGuid));
+ EXPECT_EQ(nullptr, bridge()->AddEntry(GURL("http//google.com"), "d",
kLocalDeviceCacheGuid));
- EXPECT_EQ(nullptr,
- bridge()->AddEntry(GURL("http://?k=v"), "d", AdvanceAndGetTime(),
- kLocalDeviceCacheGuid));
- EXPECT_EQ(nullptr,
- bridge()->AddEntry(GURL("http//google.com"), "d",
- AdvanceAndGetTime(), kLocalDeviceCacheGuid));
-
- // Add Entry should succeed on an invalid navigation_time, since that is the
- // case for sending links.
- EXPECT_CALL(*processor(), Put(_, _, _));
- EXPECT_NE(nullptr, bridge()->AddEntry(GURL("http://www.example.com/"), "d",
- base::Time(), kLocalDeviceCacheGuid));
}
TEST_F(SendTabToSelfBridgeTest, IsBridgeReady) {
@@ -639,21 +620,19 @@ TEST_F(SendTabToSelfBridgeTest, AddDuplicateEntries) {
EXPECT_CALL(*mock_observer(), EntriesAddedRemotely(_)).Times(0);
- base::Time navigation_time = AdvanceAndGetTime();
// The de-duplication code does not use the title as a comparator.
// So they are intentionally different here.
EXPECT_CALL(*processor(), Put(_, _, _)).Times(1);
- bridge()->AddEntry(GURL("http://a.com"), "a", navigation_time,
- kLocalDeviceCacheGuid);
- bridge()->AddEntry(GURL("http://a.com"), "b", navigation_time,
- kLocalDeviceCacheGuid);
+ bridge()->AddEntry(GURL("http://a.com"), "a", kLocalDeviceCacheGuid);
+ bridge()->AddEntry(GURL("http://a.com"), "b", kLocalDeviceCacheGuid);
EXPECT_EQ(1ul, bridge()->GetAllGuids().size());
+ // Wait for more than the current dedupe time (5 seconds).
+ AdvanceAndGetTime(base::Seconds(6));
+
EXPECT_CALL(*processor(), Put(_, _, _)).Times(2);
- bridge()->AddEntry(GURL("http://a.com"), "a", AdvanceAndGetTime(),
- kLocalDeviceCacheGuid);
- bridge()->AddEntry(GURL("http://b.com"), "b", AdvanceAndGetTime(),
- kLocalDeviceCacheGuid);
+ bridge()->AddEntry(GURL("http://a.com"), "a", kLocalDeviceCacheGuid);
+ bridge()->AddEntry(GURL("http://b.com"), "b", kLocalDeviceCacheGuid);
EXPECT_EQ(3ul, bridge()->GetAllGuids().size());
}
@@ -664,11 +643,10 @@ TEST_F(SendTabToSelfBridgeTest, NotifyRemoteSendTabToSelfEntryAdded) {
// Add on entry targeting this device and another targeting another device.
syncer::EntityChangeList remote_input;
SendTabToSelfEntry entry1("guid1", GURL("http://www.example.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
+ AdvanceAndGetTime(), "device",
kLocalDeviceCacheGuid);
SendTabToSelfEntry entry2("guid2", GURL("http://www.example.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
- kRemoteGuid);
+ AdvanceAndGetTime(), "device", kRemoteGuid);
remote_input.push_back(
syncer::EntityChange::CreateAdd("guid1", MakeEntityData(entry1)));
remote_input.push_back(
@@ -905,11 +883,9 @@ TEST_F(SendTabToSelfBridgeTest, NotifyRemoteSendTabToSelfEntryOpened) {
// Add on entry targeting this device and another targeting another device.
syncer::EntityChangeList remote_input;
SendTabToSelfEntry entry1("guid1", GURL("http://www.example.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
- "Device1");
+ AdvanceAndGetTime(), "device", "Device1");
SendTabToSelfEntry entry2("guid2", GURL("http://www.example.com/"), "title",
- AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
- "Device2");
+ AdvanceAndGetTime(), "device", "Device2");
remote_input.push_back(
syncer::EntityChange::CreateAdd("guid1", MakeEntityData(entry1)));
remote_input.push_back(
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_entry.cc b/chromium/components/send_tab_to_self/send_tab_to_self_entry.cc
index 94b3b9838ca..14803eb1350 100644
--- a/chromium/components/send_tab_to_self/send_tab_to_self_entry.cc
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_entry.cc
@@ -35,7 +35,6 @@ SendTabToSelfEntry::SendTabToSelfEntry(
const GURL& url,
const std::string& title,
base::Time shared_time,
- base::Time original_navigation_time,
const std::string& device_name,
const std::string& target_device_sync_cache_guid)
: guid_(guid),
@@ -44,7 +43,6 @@ SendTabToSelfEntry::SendTabToSelfEntry(
device_name_(device_name),
target_device_sync_cache_guid_(target_device_sync_cache_guid),
shared_time_(shared_time),
- original_navigation_time_(original_navigation_time),
notification_dismissed_(false),
opened_(false) {
DCHECK(!guid_.empty());
@@ -71,10 +69,6 @@ base::Time SendTabToSelfEntry::GetSharedTime() const {
return shared_time_;
}
-base::Time SendTabToSelfEntry::GetOriginalNavigationTime() const {
- return original_navigation_time_;
-}
-
const std::string& SendTabToSelfEntry::GetDeviceName() const {
return device_name_;
}
@@ -107,8 +101,6 @@ SendTabToSelfLocal SendTabToSelfEntry::AsLocalProto() const {
pb_entry->set_title(GetTitle());
pb_entry->set_url(GetURL().spec());
pb_entry->set_shared_time_usec(TimeToProtoTime(GetSharedTime()));
- pb_entry->set_navigation_time_usec(
- TimeToProtoTime(GetOriginalNavigationTime()));
pb_entry->set_device_name(GetDeviceName());
pb_entry->set_target_device_sync_cache_guid(GetTargetDeviceSyncCacheGuid());
pb_entry->set_opened(IsOpened());
@@ -136,15 +128,10 @@ std::unique_ptr<SendTabToSelfEntry> SendTabToSelfEntry::FromProto(
shared_time = now;
}
- base::Time navigation_time;
- if (pb_entry.has_navigation_time_usec()) {
- navigation_time = ProtoTimeToTime(pb_entry.navigation_time_usec());
- }
-
// Protobuf parsing enforces utf8 encoding for all strings.
auto entry = std::make_unique<SendTabToSelfEntry>(
- guid, url, pb_entry.title(), shared_time, navigation_time,
- pb_entry.device_name(), pb_entry.target_device_sync_cache_guid());
+ guid, url, pb_entry.title(), shared_time, pb_entry.device_name(),
+ pb_entry.target_device_sync_cache_guid());
if (pb_entry.opened()) {
entry->MarkOpened();
@@ -176,8 +163,7 @@ std::unique_ptr<SendTabToSelfEntry> SendTabToSelfEntry::FromRequiredFields(
if (guid.empty() || !url.is_valid()) {
return nullptr;
}
- return std::make_unique<SendTabToSelfEntry>(guid, url, "", base::Time(),
- base::Time(), "",
+ return std::make_unique<SendTabToSelfEntry>(guid, url, "", base::Time(), "",
target_device_sync_cache_guid);
}
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_entry.h b/chromium/components/send_tab_to_self/send_tab_to_self_entry.h
index 9701840559e..4f70d4cee5d 100644
--- a/chromium/components/send_tab_to_self/send_tab_to_self_entry.h
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_entry.h
@@ -34,7 +34,6 @@ class SendTabToSelfEntry {
const GURL& url,
const std::string& title,
base::Time shared_time,
- base::Time original_navigation_time,
const std::string& device_name,
const std::string& target_device_sync_cache_guid);
@@ -51,8 +50,6 @@ class SendTabToSelfEntry {
const std::string& GetTitle() const;
// The time that the tab was shared.
base::Time GetSharedTime() const;
- // The time that the tab was navigated to.
- base::Time GetOriginalNavigationTime() const;
// The name of the device that originated the sent tab.
const std::string& GetDeviceName() const;
// The cache guid of of the device that this tab is shared with.
@@ -86,6 +83,8 @@ class SendTabToSelfEntry {
bool IsExpired(base::Time current_time) const;
// Creates a SendTabToSelfEntry consisting of only the required fields.
+ // This entry will have an expired SharedTime and therefor this function
+ // should only be used for testing.
static std::unique_ptr<SendTabToSelfEntry> FromRequiredFields(
const std::string& guid,
const GURL& url,
@@ -98,7 +97,6 @@ class SendTabToSelfEntry {
std::string device_name_;
std::string target_device_sync_cache_guid_;
base::Time shared_time_;
- base::Time original_navigation_time_;
bool notification_dismissed_;
bool opened_;
};
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc b/chromium/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc
index 109db063b33..af5a52ff775 100644
--- a/chromium/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc
@@ -23,8 +23,7 @@ bool IsEqualForTesting(const SendTabToSelfEntry& a,
a.GetTitle() == b.GetTitle() &&
a.GetDeviceName() == b.GetDeviceName() &&
a.GetTargetDeviceSyncCacheGuid() == b.GetTargetDeviceSyncCacheGuid() &&
- a.GetSharedTime() == b.GetSharedTime() &&
- a.GetOriginalNavigationTime() == b.GetOriginalNavigationTime();
+ a.GetSharedTime() == b.GetSharedTime();
}
bool IsEqualForTesting(const SendTabToSelfEntry& entry,
@@ -37,23 +36,17 @@ bool IsEqualForTesting(const SendTabToSelfEntry& entry,
entry.GetTargetDeviceSyncCacheGuid() ==
specifics.target_device_sync_cache_guid() &&
specifics.shared_time_usec() ==
- entry.GetSharedTime().ToDeltaSinceWindowsEpoch().InMicroseconds() &&
- specifics.navigation_time_usec() == entry.GetOriginalNavigationTime()
- .ToDeltaSinceWindowsEpoch()
- .InMicroseconds());
+ entry.GetSharedTime().ToDeltaSinceWindowsEpoch().InMicroseconds());
}
TEST(SendTabToSelfEntry, CompareEntries) {
const SendTabToSelfEntry e1("1", GURL("http://example.com"), "bar",
- base::Time::FromTimeT(10),
base::Time::FromTimeT(10), "device1", "device2");
const SendTabToSelfEntry e2("1", GURL("http://example.com"), "bar",
- base::Time::FromTimeT(10),
base::Time::FromTimeT(10), "device1", "device2");
EXPECT_TRUE(IsEqualForTesting(e1, e2));
const SendTabToSelfEntry e3("2", GURL("http://example.org"), "bar",
- base::Time::FromTimeT(10),
base::Time::FromTimeT(10), "device1", "device2");
EXPECT_FALSE(IsEqualForTesting(e1, e3));
@@ -61,8 +54,7 @@ TEST(SendTabToSelfEntry, CompareEntries) {
TEST(SendTabToSelfEntry, SharedTime) {
SendTabToSelfEntry e("1", GURL("http://example.com"), "bar",
- base::Time::FromTimeT(10), base::Time::FromTimeT(10),
- "device", "device2");
+ base::Time::FromTimeT(10), "device", "device2");
EXPECT_EQ("bar", e.GetTitle());
// Getters return Base::Time values.
EXPECT_EQ(e.GetSharedTime(), base::Time::FromTimeT(10));
@@ -72,8 +64,7 @@ TEST(SendTabToSelfEntry, SharedTime) {
// sync_pb::SendTabToSelfSpecifics.
TEST(SendTabToSelfEntry, AsProto) {
SendTabToSelfEntry entry("1", GURL("http://example.com"), "bar",
- base::Time::FromTimeT(10), base::Time::FromTimeT(10),
- "device", "device2");
+ base::Time::FromTimeT(10), "device", "device2");
SendTabToSelfLocal pb_entry(entry.AsLocalProto());
EXPECT_TRUE(IsEqualForTesting(entry, pb_entry.specifics()));
}
@@ -82,7 +73,7 @@ TEST(SendTabToSelfEntry, AsProto) {
// fields
TEST(SendTabToSelfEntry, FromRequiredFields) {
SendTabToSelfEntry expected("1", GURL("http://example.com"), "", base::Time(),
- base::Time(), "", "target_device");
+ "", "target_device");
std::unique_ptr<SendTabToSelfEntry> actual =
SendTabToSelfEntry::FromRequiredFields("1", GURL("http://example.com"),
"target_device");
@@ -100,7 +91,6 @@ TEST(SendTabToSelfEntry, FromProto) {
pb_entry->set_device_name("device");
pb_entry->set_target_device_sync_cache_guid("device");
pb_entry->set_shared_time_usec(1);
- pb_entry->set_navigation_time_usec(1);
std::unique_ptr<SendTabToSelfEntry> entry(
SendTabToSelfEntry::FromProto(*pb_entry, base::Time::FromTimeT(10)));
@@ -111,8 +101,7 @@ TEST(SendTabToSelfEntry, FromProto) {
// Tests that the send tab to self entry expiry works as expected
TEST(SendTabToSelfEntry, IsExpired) {
SendTabToSelfEntry entry("1", GURL("http://example.com"), "bar",
- base::Time::FromTimeT(10), base::Time::FromTimeT(10),
- "device1", "device1");
+ base::Time::FromTimeT(10), "device1", "device1");
EXPECT_TRUE(entry.IsExpired(base::Time::FromTimeT(11) + base::Days(10)));
EXPECT_FALSE(entry.IsExpired(base::Time::FromTimeT(11)));
@@ -125,26 +114,24 @@ TEST(SendTabToSelfEntry, InvalidStrings) {
base::UTF16ToUTF8(&term[0], 1, &invalid_utf8);
SendTabToSelfEntry invalid1("1", GURL("http://example.com"), invalid_utf8,
- base::Time::FromTimeT(10),
base::Time::FromTimeT(10), "device", "device");
EXPECT_EQ("1", invalid1.GetGUID());
SendTabToSelfEntry invalid2(invalid_utf8, GURL("http://example.com"), "title",
- base::Time::FromTimeT(10),
base::Time::FromTimeT(10), "device", "device");
EXPECT_EQ(invalid_utf8, invalid2.GetGUID());
- SendTabToSelfEntry invalid3(
- "1", GURL("http://example.com"), "title", base::Time::FromTimeT(10),
- base::Time::FromTimeT(10), invalid_utf8, "device");
+ SendTabToSelfEntry invalid3("1", GURL("http://example.com"), "title",
+ base::Time::FromTimeT(10), invalid_utf8,
+ "device");
EXPECT_EQ("1", invalid3.GetGUID());
- SendTabToSelfEntry invalid4(
- "1", GURL("http://example.com"), "title", base::Time::FromTimeT(10),
- base::Time::FromTimeT(10), "device", invalid_utf8);
+ SendTabToSelfEntry invalid4("1", GURL("http://example.com"), "title",
+ base::Time::FromTimeT(10), "device",
+ invalid_utf8);
EXPECT_EQ("1", invalid4.GetGUID());
@@ -155,9 +142,7 @@ TEST(SendTabToSelfEntry, InvalidStrings) {
pb_entry->set_title(invalid_utf8);
pb_entry->set_device_name(invalid_utf8);
pb_entry->set_target_device_sync_cache_guid("device");
- ;
pb_entry->set_shared_time_usec(1);
- pb_entry->set_navigation_time_usec(1);
std::unique_ptr<SendTabToSelfEntry> invalid_entry(
SendTabToSelfEntry::FromProto(*pb_entry, base::Time::FromTimeT(10)));
@@ -169,8 +154,7 @@ TEST(SendTabToSelfEntry, InvalidStrings) {
// sync_pb::SendTabToSelfSpecifics.
TEST(SendTabToSelfEntry, MarkAsOpened) {
SendTabToSelfEntry entry("1", GURL("http://example.com"), "bar",
- base::Time::FromTimeT(10), base::Time::FromTimeT(10),
- "device", "device2");
+ base::Time::FromTimeT(10), "device", "device2");
EXPECT_FALSE(entry.IsOpened());
entry.MarkOpened();
EXPECT_TRUE(entry.IsOpened());
@@ -183,7 +167,6 @@ TEST(SendTabToSelfEntry, MarkAsOpened) {
pb_entry->set_device_name("device");
pb_entry->set_target_device_sync_cache_guid("device");
pb_entry->set_shared_time_usec(1);
- pb_entry->set_navigation_time_usec(1);
pb_entry->set_opened(true);
std::unique_ptr<SendTabToSelfEntry> entry2(
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_model.h b/chromium/components/send_tab_to_self/send_tab_to_self_model.h
index 7069a39a2ca..ab1bf789435 100644
--- a/chromium/components/send_tab_to_self/send_tab_to_self_model.h
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_model.h
@@ -9,7 +9,6 @@
#include <vector>
#include "base/observer_list.h"
-#include "base/time/time.h"
#include "components/send_tab_to_self/send_tab_to_self_entry.h"
#include "components/send_tab_to_self/send_tab_to_self_model_observer.h"
#include "url/gurl.h"
@@ -33,9 +32,6 @@ class SendTabToSelfModel {
// Returns a vector of entry IDs in the model.
virtual std::vector<std::string> GetAllGuids() const = 0;
- // Delete all entries.
- virtual void DeleteAllEntries() = 0;
-
// Returns a specific entry. Returns null if the entry does not exist.
virtual const SendTabToSelfEntry* GetEntryByGUID(
const std::string& guid) const = 0;
@@ -47,7 +43,6 @@ class SendTabToSelfModel {
virtual const SendTabToSelfEntry* AddEntry(
const GURL& url,
const std::string& title,
- base::Time navigation_time,
const std::string& target_device_cache_guid) = 0;
// Remove entry with |guid| from entries. Allows clients to modify the state
diff --git a/chromium/components/send_tab_to_self/test_send_tab_to_self_model.cc b/chromium/components/send_tab_to_self/test_send_tab_to_self_model.cc
index 182c21638d7..6c2c2a84ead 100644
--- a/chromium/components/send_tab_to_self/test_send_tab_to_self_model.cc
+++ b/chromium/components/send_tab_to_self/test_send_tab_to_self_model.cc
@@ -10,8 +10,6 @@ std::vector<std::string> TestSendTabToSelfModel::GetAllGuids() const {
return {};
}
-void TestSendTabToSelfModel::DeleteAllEntries() {}
-
const SendTabToSelfEntry* TestSendTabToSelfModel::GetEntryByGUID(
const std::string& guid) const {
return nullptr;
@@ -20,7 +18,6 @@ const SendTabToSelfEntry* TestSendTabToSelfModel::GetEntryByGUID(
const SendTabToSelfEntry* TestSendTabToSelfModel::AddEntry(
const GURL& url,
const std::string& title,
- base::Time navigation_time,
const std::string& device_id) {
return nullptr;
}
diff --git a/chromium/components/send_tab_to_self/test_send_tab_to_self_model.h b/chromium/components/send_tab_to_self/test_send_tab_to_self_model.h
index 0e8c69a52ad..828771ab338 100644
--- a/chromium/components/send_tab_to_self/test_send_tab_to_self_model.h
+++ b/chromium/components/send_tab_to_self/test_send_tab_to_self_model.h
@@ -8,7 +8,6 @@
#include <string>
#include <vector>
-#include "base/time/time.h"
#include "components/send_tab_to_self/send_tab_to_self_model.h"
#include "components/send_tab_to_self/target_device_info.h"
@@ -23,12 +22,10 @@ class TestSendTabToSelfModel : public SendTabToSelfModel {
// SendTabToSelfModel:
std::vector<std::string> GetAllGuids() const override;
- void DeleteAllEntries() override;
const SendTabToSelfEntry* GetEntryByGUID(
const std::string& guid) const override;
const SendTabToSelfEntry* AddEntry(const GURL& url,
const std::string& title,
- base::Time navigation_time,
const std::string& device_id) override;
void DeleteEntry(const std::string& guid) override;
void DismissEntry(const std::string& guid) override;
diff --git a/chromium/components/services/app_service/BUILD.gn b/chromium/components/services/app_service/BUILD.gn
index 5e460cc8fde..9428e1bd57f 100644
--- a/chromium/components/services/app_service/BUILD.gn
+++ b/chromium/components/services/app_service/BUILD.gn
@@ -14,7 +14,9 @@ source_set("lib") {
]
public_deps = [
+ "//components/services/app_service/public/cpp:app_types",
"//components/services/app_service/public/cpp:intents",
+ "//components/services/app_service/public/cpp:preferred_app",
"//components/services/app_service/public/cpp:preferred_apps",
"//components/services/app_service/public/mojom",
]
@@ -32,6 +34,7 @@ source_set("unit_tests") {
"//components/services/app_service/public/cpp:app_update",
"//components/services/app_service/public/cpp:icon_types",
"//components/services/app_service/public/cpp:intents",
+ "//components/services/app_service/public/cpp:preferred_app",
"//components/services/app_service/public/cpp:preferred_apps",
"//components/services/app_service/public/cpp:publisher",
"//components/services/app_service/public/cpp:run_on_os_login",
diff --git a/chromium/components/services/app_service/app_service_mojom_impl.cc b/chromium/components/services/app_service/app_service_mojom_impl.cc
index b684783c0e0..4bb1f86b866 100644
--- a/chromium/components/services/app_service/app_service_mojom_impl.cc
+++ b/chromium/components/services/app_service/app_service_mojom_impl.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/threading/scoped_blocking_call.h"
+#include "components/services/app_service/public/cpp/features.h"
#include "components/services/app_service/public/cpp/intent_filter_util.h"
#include "components/services/app_service/public/cpp/preferred_apps_list.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
@@ -30,11 +31,13 @@ namespace apps {
AppServiceMojomImpl::AppServiceMojomImpl(
const base::FilePath& profile_dir,
base::OnceClosure read_completed_for_testing,
- base::OnceClosure write_completed_for_testing)
- : preferred_apps_(this,
- profile_dir,
- std::move(read_completed_for_testing),
- std::move(write_completed_for_testing)) {}
+ base::OnceClosure write_completed_for_testing) {
+ if (!base::FeatureList::IsEnabled(kAppServicePreferredAppsWithoutMojom)) {
+ preferred_apps_impl_ = std::make_unique<PreferredAppsImpl>(
+ this, profile_dir, std::move(read_completed_for_testing),
+ std::move(write_completed_for_testing));
+ }
+}
AppServiceMojomImpl::~AppServiceMojomImpl() = default;
@@ -81,9 +84,11 @@ void AppServiceMojomImpl::RegisterSubscriber(
// TODO: store the opts somewhere.
// Initialise the Preferred Apps in the Subscribers on register.
- if (preferred_apps_.preferred_apps_list_.IsInitialized()) {
+ if (preferred_apps_impl_ &&
+ preferred_apps_impl_->preferred_apps_list_.IsInitialized()) {
subscriber->InitializePreferredApps(
- preferred_apps_.preferred_apps_list_.GetValue());
+ ConvertPreferredAppsToMojomPreferredApps(
+ preferred_apps_impl_->preferred_apps_list_.GetValue()));
}
// Add the new subscriber to the set.
@@ -248,35 +253,39 @@ void AppServiceMojomImpl::AddPreferredApp(
apps::mojom::IntentFilterPtr intent_filter,
apps::mojom::IntentPtr intent,
bool from_publisher) {
- preferred_apps_.AddPreferredApp(app_type, app_id, std::move(intent_filter),
- std::move(intent), from_publisher);
+ if (preferred_apps_impl_) {
+ preferred_apps_impl_->AddPreferredApp(
+ ConvertMojomAppTypToAppType(app_type), app_id,
+ ConvertMojomIntentFilterToIntentFilter(intent_filter),
+ ConvertMojomIntentToIntent(intent), from_publisher);
+ }
}
void AppServiceMojomImpl::RemovePreferredApp(apps::mojom::AppType app_type,
const std::string& app_id) {
- preferred_apps_.RemovePreferredApp(app_type, app_id);
-}
-
-void AppServiceMojomImpl::RemovePreferredAppForFilter(
- apps::mojom::AppType app_type,
- const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter) {
- preferred_apps_.RemovePreferredAppForFilter(app_type, app_id,
- std::move(intent_filter));
+ if (preferred_apps_impl_) {
+ preferred_apps_impl_->RemovePreferredApp(app_id);
+ }
}
void AppServiceMojomImpl::SetSupportedLinksPreference(
apps::mojom::AppType app_type,
const std::string& app_id,
std::vector<apps::mojom::IntentFilterPtr> all_link_filters) {
- preferred_apps_.SetSupportedLinksPreference(app_type, app_id,
- std::move(all_link_filters));
+ if (preferred_apps_impl_) {
+ preferred_apps_impl_->SetSupportedLinksPreference(
+ ConvertMojomAppTypToAppType(app_type), app_id,
+ ConvertMojomIntentFiltersToIntentFilters(all_link_filters));
+ }
}
void AppServiceMojomImpl::RemoveSupportedLinksPreference(
apps::mojom::AppType app_type,
const std::string& app_id) {
- preferred_apps_.RemoveSupportedLinksPreference(app_type, app_id);
+ if (preferred_apps_impl_) {
+ preferred_apps_impl_->RemoveSupportedLinksPreference(
+ ConvertMojomAppTypToAppType(app_type), app_id);
+ }
}
void AppServiceMojomImpl::SetResizeLocked(apps::mojom::AppType app_type,
@@ -311,28 +320,36 @@ void AppServiceMojomImpl::SetRunOnOsLoginMode(
}
void AppServiceMojomImpl::InitializePreferredAppsForAllSubscribers() {
+ if (!preferred_apps_impl_) {
+ return;
+ }
+
for (auto& subscriber : subscribers_) {
subscriber->InitializePreferredApps(
- preferred_apps_.preferred_apps_list_.GetValue());
+ ConvertPreferredAppsToMojomPreferredApps(
+ preferred_apps_impl_->preferred_apps_list_.GetValue()));
}
}
void AppServiceMojomImpl::OnPreferredAppsChanged(
- apps::mojom::PreferredAppChangesPtr changes) {
+ PreferredAppChangesPtr changes) {
for (auto& subscriber : subscribers_) {
- subscriber->OnPreferredAppsChanged(changes->Clone());
+ subscriber->OnPreferredAppsChanged(
+ ConvertPreferredAppChangesToMojomPreferredAppChanges(changes));
}
}
void AppServiceMojomImpl::OnPreferredAppSet(
const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter,
- apps::mojom::IntentPtr intent,
- apps::mojom::ReplacedAppPreferencesPtr replaced_app_preferences) {
+ IntentFilterPtr intent_filter,
+ IntentPtr intent,
+ ReplacedAppPreferences replaced_app_preferences) {
for (const auto& iter : publishers_) {
- iter.second->OnPreferredAppSet(app_id, intent_filter->Clone(),
- intent->Clone(),
- replaced_app_preferences->Clone());
+ iter.second->OnPreferredAppSet(
+ app_id, ConvertIntentFilterToMojomIntentFilter(intent_filter),
+ ConvertIntentToMojomIntent(intent),
+ ConvertReplacedAppPreferencesToMojomReplacedAppPreferences(
+ replaced_app_preferences));
}
}
@@ -344,17 +361,20 @@ void AppServiceMojomImpl::OnSupportedLinksPreferenceChanged(
}
}
-apps::mojom::Publisher* AppServiceMojomImpl::GetPublisher(
- apps::mojom::AppType app_type) {
- auto iter = publishers_.find(app_type);
- if (iter == publishers_.end()) {
- return nullptr;
- }
- return iter->second.get();
+void AppServiceMojomImpl::OnSupportedLinksPreferenceChanged(
+ AppType app_type,
+ const std::string& app_id,
+ bool open_in_app) {
+ publishers_[ConvertAppTypeToMojomAppType(app_type)]
+ ->OnSupportedLinksPreferenceChanged(app_id, open_in_app);
+}
+
+bool AppServiceMojomImpl::HasPublisher(AppType app_type) {
+ return base::Contains(publishers_, ConvertAppTypeToMojomAppType(app_type));
}
PreferredAppsList& AppServiceMojomImpl::GetPreferredAppsListForTesting() {
- return preferred_apps_.preferred_apps_list_;
+ return preferred_apps_impl_->preferred_apps_list_;
}
void AppServiceMojomImpl::OnPublisherDisconnected(
diff --git a/chromium/components/services/app_service/app_service_mojom_impl.h b/chromium/components/services/app_service/app_service_mojom_impl.h
index 6e1b40fc0ef..162bef0d390 100644
--- a/chromium/components/services/app_service/app_service_mojom_impl.h
+++ b/chromium/components/services/app_service/app_service_mojom_impl.h
@@ -6,9 +6,14 @@
#define COMPONENTS_SERVICES_APP_SERVICE_APP_SERVICE_MOJOM_IMPL_H_
#include <map>
+#include <memory>
#include "base/files/file_path.h"
-#include "components/services/app_service/public/cpp/preferred_apps.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/intent.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
+#include "components/services/app_service/public/cpp/preferred_app.h"
+#include "components/services/app_service/public/cpp/preferred_apps_impl.h"
#include "components/services/app_service/public/mojom/app_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@@ -24,7 +29,7 @@ class PreferredAppsList;
//
// See components/services/app_service/README.md.
class AppServiceMojomImpl : public apps::mojom::AppService,
- public PreferredApps::Host {
+ public PreferredAppsImpl::Host {
public:
AppServiceMojomImpl(
const base::FilePath& profile_dir,
@@ -104,10 +109,6 @@ class AppServiceMojomImpl : public apps::mojom::AppService,
bool from_publisher) override;
void RemovePreferredApp(apps::mojom::AppType app_type,
const std::string& app_id) override;
- void RemovePreferredAppForFilter(
- apps::mojom::AppType app_type,
- const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter) override;
void SetSupportedLinksPreference(
apps::mojom::AppType app_type,
const std::string& app_id,
@@ -128,21 +129,24 @@ class AppServiceMojomImpl : public apps::mojom::AppService,
// PreferredApps::Host overrides.
void InitializePreferredAppsForAllSubscribers() override;
- void OnPreferredAppsChanged(
- apps::mojom::PreferredAppChangesPtr changes) override;
+ void OnPreferredAppsChanged(PreferredAppChangesPtr changes) override;
void OnPreferredAppSet(
const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter,
- apps::mojom::IntentPtr intent,
- apps::mojom::ReplacedAppPreferencesPtr replaced_app_preferences) override;
+ IntentFilterPtr intent_filter,
+ IntentPtr intent,
+ ReplacedAppPreferences replaced_app_preferences) override;
void OnSupportedLinksPreferenceChanged(const std::string& app_id,
bool open_in_app) override;
- // Returns publisher for `app_type`, or nullptr if there is no publisher for
- // `app_type`.
- apps::mojom::Publisher* GetPublisher(apps::mojom::AppType app_type) override;
+ void OnSupportedLinksPreferenceChanged(AppType app_type,
+ const std::string& app_id,
+ bool open_in_app) override;
+
+ // Returns true if there is a publisher for `app_type`. Otherwise, returns
+ // false.
+ bool HasPublisher(AppType app_type) override;
// Retern the preferred_apps_list_ for testing.
PreferredAppsList& GetPreferredAppsListForTesting();
@@ -160,7 +164,7 @@ class AppServiceMojomImpl : public apps::mojom::AppService,
// destroyed first, closing the connection to avoid dangling callbacks.
mojo::ReceiverSet<apps::mojom::AppService> receivers_;
- PreferredApps preferred_apps_;
+ std::unique_ptr<PreferredAppsImpl> preferred_apps_impl_;
};
} // namespace apps
diff --git a/chromium/components/services/app_service/app_service_mojom_impl_unittest.cc b/chromium/components/services/app_service/app_service_mojom_impl_unittest.cc
index ff4c3e696c6..d0f33085639 100644
--- a/chromium/components/services/app_service/app_service_mojom_impl_unittest.cc
+++ b/chromium/components/services/app_service/app_service_mojom_impl_unittest.cc
@@ -11,11 +11,15 @@
#include "base/callback.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
#include "components/services/app_service/app_service_mojom_impl.h"
#include "components/services/app_service/public/cpp/app_capability_access_cache.h"
+#include "components/services/app_service/public/cpp/features.h"
+#include "components/services/app_service/public/cpp/intent.h"
#include "components/services/app_service/public/cpp/intent_filter_util.h"
#include "components/services/app_service/public/cpp/intent_test_util.h"
#include "components/services/app_service/public/cpp/intent_util.h"
+#include "components/services/app_service/public/cpp/preferred_app.h"
#include "components/services/app_service/public/cpp/preferred_apps_list.h"
#include "components/services/app_service/public/cpp/publisher_base.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
@@ -226,12 +230,14 @@ class FakeSubscriber : public apps::mojom::Subscriber {
void OnPreferredAppsChanged(
apps::mojom::PreferredAppChangesPtr changes) override {
- preferred_apps_.ApplyBulkUpdate(std::move(changes));
+ preferred_apps_.ApplyBulkUpdate(
+ ConvertMojomPreferredAppChangesToPreferredAppChanges(changes));
}
void InitializePreferredApps(
- PreferredAppsList::PreferredApps preferred_apps) override {
- preferred_apps_.Init(preferred_apps);
+ std::vector<apps::mojom::PreferredAppPtr> mojom_preferred_apps) override {
+ preferred_apps_.Init(
+ ConvertMojomPreferredAppsToPreferredApps(mojom_preferred_apps));
}
mojo::ReceiverSet<apps::mojom::Subscriber> receivers_;
@@ -242,8 +248,14 @@ class FakeSubscriber : public apps::mojom::Subscriber {
class AppServiceMojomImplTest : public testing::Test {
protected:
+ AppServiceMojomImplTest() {
+ scoped_feature_list_.InitAndDisableFeature(
+ kAppServicePreferredAppsWithoutMojom);
+ }
+
content::BrowserTaskEnvironment task_environment_;
base::ScopedTempDir temp_dir_;
+ base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(AppServiceMojomImplTest, PubSub) {
@@ -389,7 +401,8 @@ TEST_F(AppServiceMojomImplTest, PreferredApps) {
GURL filter_url = GURL("https://www.google.com/abc");
auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
- impl.GetPreferredAppsListForTesting().AddPreferredApp(kAppId1, intent_filter);
+ impl.GetPreferredAppsListForTesting().AddPreferredApp(
+ kAppId1, ConvertMojomIntentFilterToIntentFilter(intent_filter));
// Add one subscriber.
FakeSubscriber sub0(&impl);
@@ -466,19 +479,6 @@ TEST_F(AppServiceMojomImplTest, PreferredApps) {
sub0.PreferredApps().FindPreferredAppForUrl(another_filter_url));
EXPECT_EQ(kAppId2,
sub1.PreferredApps().FindPreferredAppForUrl(another_filter_url));
-
- // Test that remove setting for one filter.
- impl.RemovePreferredAppForFilter(apps::mojom::AppType::kUnknown, kAppId2,
- intent_filter->Clone());
- task_environment_.RunUntilIdle();
- EXPECT_EQ(absl::nullopt,
- sub0.PreferredApps().FindPreferredAppForUrl(filter_url));
- EXPECT_EQ(absl::nullopt,
- sub1.PreferredApps().FindPreferredAppForUrl(filter_url));
- EXPECT_EQ(kAppId2,
- sub0.PreferredApps().FindPreferredAppForUrl(another_filter_url));
- EXPECT_EQ(kAppId2,
- sub1.PreferredApps().FindPreferredAppForUrl(another_filter_url));
}
// Tests that writing a preferred app value before the PreferredAppsList is
@@ -507,11 +507,12 @@ TEST_F(AppServiceMojomImplTest, PreferredAppsWriteBeforeInit) {
run_loop_read.Run();
// Both changes to the PreferredAppsList should have been applied.
- ASSERT_EQ(
- kAppId1,
- impl.GetPreferredAppsListForTesting().FindPreferredAppForIntent(
- apps_util::CreateShareIntentFromFiles(
- {GURL("filesystem:chrome://foo/image.png")}, {"image/png"})));
+ std::vector<GURL> filesystem_urls(
+ {GURL("filesystem:chrome://foo/image.png")});
+ std::vector<std::string> mime_types({"image/png"});
+ ASSERT_EQ(kAppId1,
+ impl.GetPreferredAppsListForTesting().FindPreferredAppForIntent(
+ apps_util::MakeShareIntent(filesystem_urls, mime_types)));
ASSERT_EQ(
kAppId2,
impl.GetPreferredAppsListForTesting().FindPreferredAppForUrl(filter_url));
@@ -704,19 +705,6 @@ TEST_F(AppServiceMojomImplTest, PreferredAppsOverlap) {
EXPECT_EQ(kAppId2, sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3));
EXPECT_EQ(1U, impl.GetPreferredAppsListForTesting().GetEntrySize());
EXPECT_EQ(1U, sub0.PreferredApps().GetEntrySize());
-
- // Test that can remove entry with overlapped filter.
- impl.RemovePreferredAppForFilter(apps::mojom::AppType::kArc, kAppId2,
- intent_filter_1->Clone());
- task_environment_.RunUntilIdle();
- EXPECT_EQ(absl::nullopt,
- sub0.PreferredApps().FindPreferredAppForUrl(filter_url_1));
- EXPECT_EQ(absl::nullopt,
- sub0.PreferredApps().FindPreferredAppForUrl(filter_url_2));
- EXPECT_EQ(absl::nullopt,
- sub0.PreferredApps().FindPreferredAppForUrl(filter_url_3));
- EXPECT_EQ(0U, impl.GetPreferredAppsListForTesting().GetEntrySize());
- EXPECT_EQ(0U, sub0.PreferredApps().GetEntrySize());
}
// Test that app with overlapped supported links works properly.
diff --git a/chromium/components/services/app_service/public/cpp/BUILD.gn b/chromium/components/services/app_service/public/cpp/BUILD.gn
index 0d662f04b3e..b51028289cc 100644
--- a/chromium/components/services/app_service/public/cpp/BUILD.gn
+++ b/chromium/components/services/app_service/public/cpp/BUILD.gn
@@ -49,6 +49,8 @@ component("app_types") {
sources = [
"app_types.cc",
"app_types.h",
+ "features.cc",
+ "features.h",
"intent_filter.cc",
"intent_filter.h",
"macros.h",
@@ -83,8 +85,6 @@ component("app_update") {
"app_update.h",
"capability_access_update.cc",
"capability_access_update.h",
- "features.cc",
- "features.h",
"macros.h",
]
@@ -228,12 +228,24 @@ source_set("protocol_handling") {
deps = [ "//url" ]
}
+source_set("preferred_app") {
+ sources = [
+ "preferred_app.cc",
+ "preferred_app.h",
+ ]
+
+ deps = [
+ ":app_types",
+ "//components/services/app_service/public/mojom",
+ ]
+}
+
source_set("preferred_apps") {
sources = [
- "preferred_apps.cc",
- "preferred_apps.h",
"preferred_apps_converter.cc",
"preferred_apps_converter.h",
+ "preferred_apps_impl.cc",
+ "preferred_apps_impl.h",
"preferred_apps_list.cc",
"preferred_apps_list.h",
"preferred_apps_list_handle.cc",
@@ -243,6 +255,7 @@ source_set("preferred_apps") {
deps = [
":app_types",
":intents",
+ ":preferred_app",
"//base",
"//components/services/app_service/public/mojom",
"//content/public/browser",
@@ -326,6 +339,7 @@ source_set("unit_tests") {
":icon_loader",
":icon_types",
":intents",
+ ":preferred_app",
":preferred_apps",
":publisher",
":run_on_os_login",
diff --git a/chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc b/chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc
index b5f080afc86..4798bfc1957 100644
--- a/chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/app_registry_cache_unittest.cc
@@ -10,6 +10,7 @@
#include <vector>
#include "base/containers/contains.h"
+#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/features.h"
@@ -93,7 +94,7 @@ class RemoveObserver : public apps::AppRegistryCache::Observer {
private:
std::vector<std::string> updated_ids_;
std::vector<apps::Readiness> readinesses_;
- apps::AppRegistryCache* cache_ = nullptr;
+ raw_ptr<apps::AppRegistryCache> cache_ = nullptr;
};
// Responds to a cache's OnAppUpdate to call back into the cache, checking that
@@ -303,7 +304,7 @@ class InitializedObserver : public apps::AppRegistryCache::Observer {
std::set<apps::AppType> app_types_;
int initialized_app_type_count_ = 0;
int app_count_at_initialization_ = 0;
- apps::AppRegistryCache* cache_ = nullptr;
+ raw_ptr<apps::AppRegistryCache> cache_ = nullptr;
};
} // namespace
diff --git a/chromium/components/services/app_service/public/cpp/app_types.cc b/chromium/components/services/app_service/public/cpp/app_types.cc
index 403310bba88..fc4fbc1e8ad 100644
--- a/chromium/components/services/app_service/public/cpp/app_types.cc
+++ b/chromium/components/services/app_service/public/cpp/app_types.cc
@@ -99,6 +99,9 @@ AppPtr App::Clone() const {
app->shortcuts = CloneShortcuts(shortcuts);
+ app->app_size_in_bytes = app_size_in_bytes;
+ app->data_size_in_bytes = data_size_in_bytes;
+
return app;
}
diff --git a/chromium/components/services/app_service/public/cpp/app_types.h b/chromium/components/services/app_service/public/cpp/app_types.h
index 63ce597dfda..5bd3b34a586 100644
--- a/chromium/components/services/app_service/public/cpp/app_types.h
+++ b/chromium/components/services/app_service/public/cpp/app_types.h
@@ -200,6 +200,10 @@ struct COMPONENT_EXPORT(APP_TYPES) App {
// There is no guarantee that this is sorted by any criteria.
Shortcuts shortcuts;
+ // Storage space size for app and associated data.
+ absl::optional<uint64_t> app_size_in_bytes;
+ absl::optional<uint64_t> data_size_in_bytes;
+
// When adding new fields to the App type, the `Clone` function and the
// `AppUpdate` class should also be updated.
};
@@ -212,7 +216,7 @@ COMPONENT_EXPORT(APP_TYPES)
AppType ConvertMojomAppTypToAppType(apps::mojom::AppType mojom_app_type);
COMPONENT_EXPORT(APP_TYPES)
-mojom::AppType ConvertAppTypeToMojomAppType(AppType mojom_app_type);
+mojom::AppType ConvertAppTypeToMojomAppType(AppType app_type);
COMPONENT_EXPORT(APP_TYPES)
Readiness ConvertMojomReadinessToReadiness(
diff --git a/chromium/components/services/app_service/public/cpp/app_update.cc b/chromium/components/services/app_service/public/cpp/app_update.cc
index 0c5c8e8df14..29cacae793e 100644
--- a/chromium/components/services/app_service/public/cpp/app_update.cc
+++ b/chromium/components/services/app_service/public/cpp/app_update.cc
@@ -39,16 +39,6 @@ void CloneStrings(const std::vector<std::string>& clone_from,
}
}
-std::vector<apps::IntentFilterPtr> ConvertMojomIntentFiltersToIntentFilters(
- const std::vector<apps::mojom::IntentFilterPtr>& mojom_intent_filters) {
- std::vector<apps::IntentFilterPtr> intent_filters;
- for (const auto& mojom_intent_filter : mojom_intent_filters) {
- intent_filters.push_back(
- apps::ConvertMojomIntentFilterToIntentFilter(mojom_intent_filter));
- }
- return intent_filters;
-}
-
void CloneMojomIntentFilters(
const std::vector<apps::mojom::IntentFilterPtr>& clone_from,
std::vector<apps::mojom::IntentFilterPtr>* clone_to) {
@@ -255,6 +245,9 @@ void AppUpdate::Merge(App* state, const App* delta) {
state->shortcuts = CloneShortcuts(delta->shortcuts);
}
+ SET_OPTIONAL_VALUE(app_size_in_bytes);
+ SET_OPTIONAL_VALUE(data_size_in_bytes);
+
// When adding new fields to the App type, this function should also be
// updated.
}
@@ -882,10 +875,10 @@ apps::IntentFilters AppUpdate::IntentFilters() const {
}
if (mojom_delta_ && !mojom_delta_->intent_filters.empty()) {
- return ::ConvertMojomIntentFiltersToIntentFilters(
+ return ConvertMojomIntentFiltersToIntentFilters(
mojom_delta_->intent_filters);
} else if (mojom_state_ && !mojom_state_->intent_filters.empty()) {
- return ::ConvertMojomIntentFiltersToIntentFilters(
+ return ConvertMojomIntentFiltersToIntentFilters(
mojom_state_->intent_filters);
}
return std::vector<IntentFilterPtr>{};
@@ -1003,6 +996,34 @@ const ::AccountId& AppUpdate::AccountId() const {
return account_id_;
}
+absl::optional<uint64_t> AppUpdate::AppSizeInBytes() const {
+ if (ShouldUseNonMojom()) {
+ GET_VALUE_WITH_FALLBACK(app_size_in_bytes, absl::nullopt);
+ }
+
+ return absl::nullopt;
+}
+
+bool AppUpdate::AppSizeInBytesChanged() const {
+ MAYBE_RETURN_OPTIONAL_VALUE_CHANGED(app_size_in_bytes);
+
+ return false;
+}
+
+absl::optional<uint64_t> AppUpdate::DataSizeInBytes() const {
+ if (ShouldUseNonMojom()) {
+ GET_VALUE_WITH_FALLBACK(data_size_in_bytes, absl::nullopt);
+ }
+
+ return absl::nullopt;
+}
+
+bool AppUpdate::DataSizeInBytesChanged() const {
+ MAYBE_RETURN_OPTIONAL_VALUE_CHANGED(data_size_in_bytes);
+
+ return false;
+}
+
bool AppUpdate::ShouldUseNonMojom() const {
// `state_` or `delta_` being non-null means exclusively non-mojom updates are
// being sent.
@@ -1070,6 +1091,9 @@ std::ostream& operator<<(std::ostream& out, const AppUpdate& app) {
out << shortcut->ToString() << std::endl;
}
+ out << "App Size: " << app.AppSizeInBytes().value_or(-1) << std::endl;
+ out << "Data Size: " << app.DataSizeInBytes().value_or(-1) << std::endl;
+
return out;
}
diff --git a/chromium/components/services/app_service/public/cpp/app_update.h b/chromium/components/services/app_service/public/cpp/app_update.h
index 971421c1db7..3bdb0442a55 100644
--- a/chromium/components/services/app_service/public/cpp/app_update.h
+++ b/chromium/components/services/app_service/public/cpp/app_update.h
@@ -181,6 +181,12 @@ class COMPONENT_EXPORT(APP_UPDATE) AppUpdate {
const ::AccountId& AccountId() const;
+ absl::optional<uint64_t> AppSizeInBytes() const;
+ bool AppSizeInBytesChanged() const;
+
+ absl::optional<uint64_t> DataSizeInBytes() const;
+ bool DataSizeInBytesChanged() const;
+
private:
friend class AppRegistryCacheTest;
diff --git a/chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc b/chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
index 978ee6c66c6..cba1250d0b5 100644
--- a/chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
@@ -122,8 +122,7 @@ class AppUpdateMojomTest : public testing::Test {
apps::mojom::TriState value) {
apps::mojom::PermissionPtr permission = apps::mojom::Permission::New();
permission->permission_type = permission_type;
- permission->value = apps::mojom::PermissionValue::New();
- permission->value->set_tristate_value(value);
+ permission->value = apps::mojom::PermissionValue::NewTristateValue(value);
return permission;
}
@@ -1072,8 +1071,7 @@ TEST_F(AppUpdateMojomTest, AppConvert) {
auto permission = apps::mojom::Permission::New();
permission->permission_type = apps::mojom::PermissionType::kCamera;
- permission->value = apps::mojom::PermissionValue::New();
- permission->value->set_bool_value(true);
+ permission->value = apps::mojom::PermissionValue::NewBoolValue(true);
permission->is_managed = true;
input->permissions.push_back(std::move(permission));
diff --git a/chromium/components/services/app_service/public/cpp/app_update_unittest.cc b/chromium/components/services/app_service/public/cpp/app_update_unittest.cc
index fd0aa0e11ef..92be8d27cd8 100644
--- a/chromium/components/services/app_service/public/cpp/app_update_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/app_update_unittest.cc
@@ -139,6 +139,12 @@ class AppUpdateTest : public testing::Test {
AccountId account_id_ = AccountId::FromUserEmail("test@gmail.com");
+ absl::optional<uint64_t> expect_app_size_in_bytes_;
+ bool expect_app_size_in_bytes_changed_;
+
+ absl::optional<uint64_t> expect_data_size_in_bytes_;
+ bool expect_data_size_in_bytes_changed_;
+
void ExpectNoChange() {
expect_readiness_changed_ = false;
expect_name_changed_ = false;
@@ -170,6 +176,8 @@ class AppUpdateTest : public testing::Test {
expect_window_mode_changed_ = false;
expect_run_on_os_login_changed_ = false;
expect_shortcuts_changed_ = false;
+ expect_app_size_in_bytes_changed_ = false;
+ expect_data_size_in_bytes_changed_ = false;
}
void CheckExpects(const AppUpdate& u) {
@@ -266,6 +274,12 @@ class AppUpdateTest : public testing::Test {
EXPECT_EQ(expect_shortcuts_changed_, u.ShortcutsChanged());
EXPECT_EQ(account_id_, u.AccountId());
+
+ EXPECT_EQ(expect_app_size_in_bytes_, u.AppSizeInBytes());
+ EXPECT_EQ(expect_app_size_in_bytes_changed_, u.AppSizeInBytesChanged());
+
+ EXPECT_EQ(expect_data_size_in_bytes_, u.DataSizeInBytes());
+ EXPECT_EQ(expect_data_size_in_bytes_changed_, u.DataSizeInBytesChanged());
}
void TestAppUpdate(App* state, App* delta) {
@@ -303,6 +317,8 @@ class AppUpdateTest : public testing::Test {
expect_resize_locked_ = absl::nullopt;
expect_window_mode_ = WindowMode::kUnknown;
expect_run_on_os_login_ = absl::nullopt;
+ expect_app_size_in_bytes_ = absl::nullopt;
+ expect_data_size_in_bytes_ = absl::nullopt;
expect_shortcuts_.clear();
ExpectNoChange();
CheckExpects(u);
@@ -1072,6 +1088,52 @@ class AppUpdateTest : public testing::Test {
ExpectNoChange();
CheckExpects(u);
}
+
+ // App size in bytes tests.
+
+ if (state) {
+ state->app_size_in_bytes = 17;
+ expect_app_size_in_bytes_ = 17;
+ expect_app_size_in_bytes_changed_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->app_size_in_bytes = 42;
+ expect_app_size_in_bytes_ = 42;
+ expect_app_size_in_bytes_changed_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_app_size_in_bytes_, state->app_size_in_bytes);
+ ExpectNoChange();
+ CheckExpects(u);
+ }
+
+ // Data size in bytes tests.
+
+ if (state) {
+ state->data_size_in_bytes = 17;
+ expect_data_size_in_bytes_ = 17;
+ expect_data_size_in_bytes_changed_ = false;
+ CheckExpects(u);
+ }
+
+ if (delta) {
+ delta->data_size_in_bytes = 42;
+ expect_data_size_in_bytes_ = 42;
+ expect_data_size_in_bytes_changed_ = true;
+ CheckExpects(u);
+ }
+
+ if (state) {
+ apps::AppUpdate::Merge(state, delta);
+ EXPECT_EQ(expect_data_size_in_bytes_, state->data_size_in_bytes);
+ ExpectNoChange();
+ CheckExpects(u);
+ }
}
private:
diff --git a/chromium/components/services/app_service/public/cpp/features.cc b/chromium/components/services/app_service/public/cpp/features.cc
index 56515963223..38788c32154 100644
--- a/chromium/components/services/app_service/public/cpp/features.cc
+++ b/chromium/components/services/app_service/public/cpp/features.cc
@@ -13,7 +13,10 @@ const base::Feature kAppServiceOnAppTypeInitializedWithoutMojom{
const base::Feature kAppServiceOnAppUpdateWithoutMojom{
"AppServiceOnAppUpdateWithoutMojom", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature AppServiceCrosApiOnAppsWithoutMojom{
+const base::Feature kAppServiceCrosApiOnAppsWithoutMojom{
"AppServiceCrosApiOnAppsWithoutMojom", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kAppServicePreferredAppsWithoutMojom{
+ "AppServicePreferredAppsWithoutMojom", base::FEATURE_DISABLED_BY_DEFAULT};
+
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/features.h b/chromium/components/services/app_service/public/cpp/features.h
index 76aff34bf02..612d2a8f5c4 100644
--- a/chromium/components/services/app_service/public/cpp/features.h
+++ b/chromium/components/services/app_service/public/cpp/features.h
@@ -10,12 +10,14 @@
namespace apps {
-COMPONENT_EXPORT(APP_UPDATE)
+COMPONENT_EXPORT(APP_TYPES)
extern const base::Feature kAppServiceOnAppTypeInitializedWithoutMojom;
-COMPONENT_EXPORT(APP_UPDATE)
+COMPONENT_EXPORT(APP_TYPES)
extern const base::Feature kAppServiceOnAppUpdateWithoutMojom;
-COMPONENT_EXPORT(APP_UPDATE)
-extern const base::Feature AppServiceCrosApiOnAppsWithoutMojom;
+COMPONENT_EXPORT(APP_TYPES)
+extern const base::Feature kAppServiceCrosApiOnAppsWithoutMojom;
+COMPONENT_EXPORT(APP_TYPES)
+extern const base::Feature kAppServicePreferredAppsWithoutMojom;
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/instance_registry.h b/chromium/components/services/app_service/public/cpp/instance_registry.h
index d0f78ea11d3..c367c9270a4 100644
--- a/chromium/components/services/app_service/public/cpp/instance_registry.h
+++ b/chromium/components/services/app_service/public/cpp/instance_registry.h
@@ -66,6 +66,8 @@ class InstanceRegistry {
// Observe(nullptr)).
virtual void OnInstanceRegistryWillBeDestroyed(InstanceRegistry* cache) = 0;
+ InstanceRegistry* instance_registry() const { return instance_registry_; }
+
protected:
// Use this constructor when the observer |this| is tied to a single
// InstanceRegistry for its entire lifetime, or until the observee (the
diff --git a/chromium/components/services/app_service/public/cpp/instance_registry_unittest.cc b/chromium/components/services/app_service/public/cpp/instance_registry_unittest.cc
index 190cb16b2cb..cf519dc9bbf 100644
--- a/chromium/components/services/app_service/public/cpp/instance_registry_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/instance_registry_unittest.cc
@@ -87,9 +87,7 @@ class InstanceRegistryTest : public testing::Test,
class InstanceRecursiveObserver : public apps::InstanceRegistry::Observer {
public:
explicit InstanceRecursiveObserver(apps::InstanceRegistry* instance_registry)
- : instance_registry_(instance_registry) {
- Observe(instance_registry);
- }
+ : apps::InstanceRegistry::Observer(instance_registry) {}
~InstanceRecursiveObserver() override = default;
@@ -124,7 +122,7 @@ class InstanceRecursiveObserver : public apps::InstanceRegistry::Observer {
// apps::InstanceRegistry::Observer overrides.
void OnInstanceUpdate(const apps::InstanceUpdate& outer) override {
int num_instance = 0;
- instance_registry_->ForEachInstance(
+ instance_registry()->ForEachInstance(
[&outer, &num_instance](const apps::InstanceUpdate& inner) {
if (outer.InstanceId() == inner.InstanceId()) {
ExpectEq(outer, inner);
@@ -132,12 +130,12 @@ class InstanceRecursiveObserver : public apps::InstanceRegistry::Observer {
num_instance++;
});
- EXPECT_TRUE(instance_registry_->ForOneInstance(
+ EXPECT_TRUE(instance_registry()->ForOneInstance(
outer.InstanceId(), [&outer](const apps::InstanceUpdate& inner) {
ExpectEq(outer, inner);
}));
- EXPECT_TRUE(instance_registry_->ForInstancesWithWindow(
+ EXPECT_TRUE(instance_registry()->ForInstancesWithWindow(
outer.Window(), [&outer](const apps::InstanceUpdate& inner) {
if (outer.InstanceId() == inner.InstanceId()) {
ExpectEq(outer, inner);
@@ -156,7 +154,7 @@ class InstanceRecursiveObserver : public apps::InstanceRegistry::Observer {
super_recursive_instance_params_.pop_back();
break;
}
- instance_registry_->CreateOrUpdateInstance(std::move(*params));
+ instance_registry()->CreateOrUpdateInstance(std::move(*params));
super_recursive_instance_params_.pop_back();
}
@@ -168,7 +166,7 @@ class InstanceRecursiveObserver : public apps::InstanceRegistry::Observer {
super_recursive_instances_.pop_back();
break;
}
- instance_registry_->OnInstance(std::move(instance));
+ instance_registry()->OnInstance(std::move(instance));
super_recursive_instances_.pop_back();
}
@@ -191,7 +189,6 @@ class InstanceRecursiveObserver : public apps::InstanceRegistry::Observer {
EXPECT_EQ(outer.BrowserContext(), inner.BrowserContext());
}
- apps::InstanceRegistry* instance_registry_ = nullptr;
int expected_num_instances_ = -1;
int num_instances_seen_on_instance_update_ = 0;
diff --git a/chromium/components/services/app_service/public/cpp/intent.cc b/chromium/components/services/app_service/public/cpp/intent.cc
index 5189c2b8463..5fdef63e5ca 100644
--- a/chromium/components/services/app_service/public/cpp/intent.cc
+++ b/chromium/components/services/app_service/public/cpp/intent.cc
@@ -14,6 +14,19 @@ IntentFile::IntentFile(const GURL& url) : url(url) {}
IntentFile::~IntentFile() = default;
+std::unique_ptr<IntentFile> IntentFile::Clone() const {
+ auto intent_file = std::make_unique<IntentFile>(url);
+ if (mime_type.has_value()) {
+ intent_file->mime_type = mime_type.value();
+ }
+ if (file_name.has_value()) {
+ intent_file->file_name = file_name.value();
+ }
+ intent_file->file_size = file_size;
+ intent_file->is_directory = is_directory;
+ return intent_file;
+}
+
bool IntentFile::MatchConditionValue(const ConditionValuePtr& condition_value) {
switch (condition_value->match_type) {
case PatternMatchType::kNone:
@@ -51,50 +64,54 @@ bool IntentFile::MatchAnyConditionValue(
Intent::Intent(const std::string& action) : action(action) {}
-Intent::Intent(const GURL& url)
- : action(apps_util::kIntentActionView), url(url) {}
-
-Intent::Intent(const std::vector<GURL>& filesystem_urls,
- const std::vector<std::string>& mime_types)
- : action(filesystem_urls.size() == 1
- ? apps_util::kIntentActionSend
- : apps_util::kIntentActionSendMultiple),
- mime_type(apps_util::CalculateCommonMimeType(mime_types)) {
- DCHECK_EQ(filesystem_urls.size(), mime_types.size());
- for (size_t i = 0; i < filesystem_urls.size(); i++) {
- auto file = std::make_unique<IntentFile>(filesystem_urls[i]);
- file->mime_type = mime_types.at(i);
- files.push_back(std::move(file));
- }
-}
+Intent::Intent(const std::string& action, const GURL& url)
+ : action(action), url(url) {}
+
+Intent::Intent(const std::string& action, std::vector<IntentFilePtr> files)
+ : action(action), files(std::move(files)) {}
+
+Intent::~Intent() = default;
-Intent::Intent(std::vector<IntentFilePtr> files)
- : action(apps_util::kIntentActionView), files(std::move(files)) {}
+std::unique_ptr<Intent> Intent::Clone() const {
+ auto intent = std::make_unique<Intent>(action);
-Intent::Intent(const std::vector<GURL>& filesystem_urls,
- const std::vector<std::string>& mime_types,
- const std::string& text,
- const std::string& title)
- : Intent(filesystem_urls, mime_types) {
- if (!text.empty()) {
- share_text = text;
+ if (url.has_value()) {
+ intent->url = url.value();
}
- if (!title.empty()) {
- share_title = title;
+ if (mime_type.has_value()) {
+ intent->mime_type = mime_type.value();
}
-}
-
-Intent::Intent(const std::string& text, const std::string& title)
- : Intent(apps_util::kIntentActionSend) {
- mime_type = "text/plain";
- share_text = text;
- if (!title.empty()) {
- share_title = title;
+ for (const auto& file : files) {
+ intent->files.push_back(file->Clone());
}
+ if (activity_name.has_value()) {
+ intent->activity_name = activity_name.value();
+ }
+ if (drive_share_url.has_value()) {
+ intent->drive_share_url = drive_share_url.value();
+ }
+ if (share_text.has_value()) {
+ intent->share_text = share_text.value();
+ }
+ if (share_title.has_value()) {
+ intent->share_title = share_title.value();
+ }
+ if (start_type.has_value()) {
+ intent->start_type = start_type.value();
+ }
+ for (const auto& category : categories) {
+ intent->categories.push_back(category);
+ }
+ if (data.has_value()) {
+ intent->data = data.value();
+ }
+ intent->ui_bypassed = ui_bypassed;
+ for (const auto& extra : extras) {
+ intent->extras[extra.first] = extra.second;
+ }
+ return intent;
}
-Intent::~Intent() = default;
-
absl::optional<std::string> Intent::GetIntentConditionValueByType(
ConditionType condition_type) {
switch (condition_type) {
diff --git a/chromium/components/services/app_service/public/cpp/intent.h b/chromium/components/services/app_service/public/cpp/intent.h
index a067e0c7fe2..78f67232467 100644
--- a/chromium/components/services/app_service/public/cpp/intent.h
+++ b/chromium/components/services/app_service/public/cpp/intent.h
@@ -25,6 +25,8 @@ struct IntentFile {
IntentFile& operator=(const IntentFile&) = delete;
~IntentFile();
+ std::unique_ptr<IntentFile> Clone() const;
+
// Returns true if matches `condition_value`, otherwise, returns false.
bool MatchConditionValue(const ConditionValuePtr& condition_value);
@@ -58,31 +60,18 @@ using IntentFilePtr = std::unique_ptr<IntentFile>;
// ConvertIntentToValue and ConvertValueToIntent in
// components/services/app_service/public/cpp/intent_util.*
struct Intent {
+ // Factory methods for more complicated Intents are available in
+ // intent_util.h.
explicit Intent(const std::string& action);
- explicit Intent(const GURL& url);
-
- // Creates an intent for sharing `filesystem_urls`. `filesystem_urls` must be
- // co-indexed with `mime_types`.
- Intent(const std::vector<GURL>& filesystem_urls,
- const std::vector<std::string>& mime_types);
-
- // Creates an intent with the list of `files`.
- explicit Intent(std::vector<IntentFilePtr> files);
-
- // Creates an intent for sharing `filesystem_urls`, along with `text` content
- // and `title`. `filesystem_urls` must be co-indexed with mime_types.
- Intent(const std::vector<GURL>& filesystem_urls,
- const std::vector<std::string>& mime_types,
- const std::string& text,
- const std::string& title);
-
- // Creates an intent for sharing `text`, with `title`.
- Intent(const std::string& text, const std::string& title);
+ explicit Intent(const std::string& action, const GURL& url);
+ explicit Intent(const std::string& action, std::vector<IntentFilePtr> files);
Intent(const Intent&) = delete;
Intent& operator=(const Intent&) = delete;
~Intent();
+ std::unique_ptr<Intent> Clone() const;
+
// Gets the field that need to be checked/matched based on `condition_type`.
absl::optional<std::string> GetIntentConditionValueByType(
ConditionType condition_type);
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter.cc b/chromium/components/services/app_service/public/cpp/intent_filter.cc
index 76f9c482792..907585ef74f 100644
--- a/chromium/components/services/app_service/public/cpp/intent_filter.cc
+++ b/chromium/components/services/app_service/public/cpp/intent_filter.cc
@@ -177,6 +177,79 @@ void IntentFilter::GetMimeTypesAndExtensions(
}
}
+std::set<std::string> IntentFilter::GetSupportedLinksForAppManagement() {
+ std::set<std::string> hosts;
+ std::set<std::string> paths;
+ bool is_http_or_https = false;
+
+ for (auto& condition : conditions) {
+ // For scheme conditions we check if it's http or https and set a Boolean
+ // if this intent filter is for one of those schemes.
+ if (condition->condition_type == ConditionType::kScheme) {
+ for (auto& condition_value : condition->condition_values) {
+ // We only care about http and https schemes.
+ if (condition_value->value == url::kHttpScheme ||
+ condition_value->value == url::kHttpsScheme) {
+ is_http_or_https = true;
+ break;
+ }
+ }
+
+ // There should only be one condition of type |kScheme| so if there
+ // aren't any http or https scheme values this indicates that no http or
+ // https scheme exists in the intent filter and thus we will have to
+ // return an empty list.
+ if (!is_http_or_https) {
+ break;
+ }
+ }
+
+ // For host conditions we add each value to the |hosts| set.
+ if (condition->condition_type == ConditionType::kHost) {
+ for (auto& condition_value : condition->condition_values) {
+ // Prepend the wildcard to indicate any subdomain in the hosts
+ std::string host = condition_value->value;
+ if (condition_value->match_type == PatternMatchType::kSuffix) {
+ host = "*" + host;
+ }
+ hosts.insert(host);
+ }
+ }
+
+ // For path conditions we add each value to the |paths| set.
+ if (condition->condition_type == ConditionType::kPattern) {
+ for (auto& condition_value : condition->condition_values) {
+ std::string value = condition_value->value;
+ // Glob and literal patterns can be printed exactly, but prefix
+ // patterns must have be appended with "*" to indicate that
+ // anything with that prefix can be matched.
+ if (condition_value->match_type == PatternMatchType::kPrefix) {
+ value.append("*");
+ }
+ paths.insert(value);
+ }
+ }
+ }
+
+ // We only care about http and https schemes.
+ if (!is_http_or_https) {
+ return std::set<std::string>();
+ }
+
+ std::set<std::string> supported_links;
+ for (auto& host : hosts) {
+ for (auto& path : paths) {
+ if (path.front() == '/') {
+ supported_links.insert(host + path);
+ } else {
+ supported_links.insert(host + "/" + path);
+ }
+ }
+ }
+
+ return supported_links;
+}
+
bool IntentFilter::IsBrowserFilter() {
if (GetFilterMatchLevel() !=
static_cast<int>(IntentFilterMatchLevel::kScheme)) {
@@ -215,6 +288,15 @@ bool IntentFilter::IsFileExtensionsFilter() {
return true;
}
+bool IntentFilter::FilterNeedsUpgrade() {
+ for (const auto& condition : conditions) {
+ if (condition->condition_type == ConditionType::kAction) {
+ return false;
+ }
+ }
+ return true;
+}
+
std::string IntentFilter::ToString() const {
std::stringstream out;
if (activity_name.has_value()) {
@@ -240,6 +322,15 @@ IntentFilters CloneIntentFilters(const IntentFilters& intent_filters) {
return ret;
}
+base::flat_map<std::string, IntentFilters> CloneIntentFiltersMap(
+ const base::flat_map<std::string, IntentFilters>& intent_filters_map) {
+ base::flat_map<std::string, IntentFilters> ret;
+ for (const auto& it : intent_filters_map) {
+ ret.insert(std::make_pair(it.first, CloneIntentFilters(it.second)));
+ }
+ return ret;
+}
+
bool IsEqual(const IntentFilters& source, const IntentFilters& target) {
if (source.size() != target.size()) {
return false;
@@ -253,6 +344,16 @@ bool IsEqual(const IntentFilters& source, const IntentFilters& target) {
return true;
}
+bool Contains(const IntentFilters& intent_filters,
+ const IntentFilterPtr& intent_filter) {
+ for (const auto& filter : intent_filters) {
+ if (*filter == *intent_filter) {
+ return true;
+ }
+ }
+ return false;
+}
+
ConditionType ConvertMojomConditionTypeToConditionType(
const apps::mojom::ConditionType& mojom_condition_type) {
switch (mojom_condition_type) {
@@ -437,38 +538,26 @@ apps::mojom::IntentFilterPtr ConvertIntentFilterToMojomIntentFilter(
return mojom_intent_filter;
}
-base::flat_map<std::string, std::vector<apps::mojom::IntentFilterPtr>>
-ConvertIntentFiltersToMojomIntentFilters(
- const base::flat_map<std::string, apps::IntentFilters>& intent_filter) {
- base::flat_map<std::string, std::vector<apps::mojom::IntentFilterPtr>> ret;
- for (const auto& it : intent_filter) {
- std::vector<apps::mojom::IntentFilterPtr> mojom_filters;
- for (const auto& filter_it : it.second) {
- if (filter_it) {
- mojom_filters.push_back(
- ConvertIntentFilterToMojomIntentFilter(filter_it));
- }
- }
- ret[it.first] = std::move(mojom_filters);
+IntentFilters ConvertMojomIntentFiltersToIntentFilters(
+ const std::vector<apps::mojom::IntentFilterPtr>& mojom_intent_filters) {
+ IntentFilters intent_filters;
+ intent_filters.reserve(mojom_intent_filters.size());
+ for (const auto& mojom_intent_filter : mojom_intent_filters) {
+ intent_filters.push_back(
+ ConvertMojomIntentFilterToIntentFilter(mojom_intent_filter));
}
- return ret;
+ return intent_filters;
}
-base::flat_map<std::string, apps::IntentFilters>
-ConvertMojomIntentFiltersToIntentFilters(
- const base::flat_map<std::string,
- std::vector<apps::mojom::IntentFilterPtr>>&
- mojom_intent_filter) {
- base::flat_map<std::string, apps::IntentFilters> ret;
- for (const auto& it : mojom_intent_filter) {
- apps::IntentFilters filters;
- for (const auto& filter_it : it.second) {
- if (filter_it)
- filters.push_back(ConvertMojomIntentFilterToIntentFilter(filter_it));
- }
- ret[it.first] = std::move(filters);
+std::vector<apps::mojom::IntentFilterPtr>
+ConvertIntentFiltersToMojomIntentFilters(const IntentFilters& intent_filters) {
+ std::vector<apps::mojom::IntentFilterPtr> mojom_intent_filters;
+ mojom_intent_filters.reserve(intent_filters.size());
+ for (const auto& intent_filter : intent_filters) {
+ mojom_intent_filters.push_back(
+ ConvertIntentFilterToMojomIntentFilter(intent_filter));
}
- return ret;
+ return mojom_intent_filters;
}
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter.h b/chromium/components/services/app_service/public/cpp/intent_filter.h
index d01cc22dfea..337650e4b0a 100644
--- a/chromium/components/services/app_service/public/cpp/intent_filter.h
+++ b/chromium/components/services/app_service/public/cpp/intent_filter.h
@@ -33,6 +33,8 @@ enum class IntentFilterMatchLevel {
// The type of a condition in an IntentFilter, which determines what Intent
// field will be matched against.
+// Values are persisted to disk by preferred_apps_converter.h, so should not be
+// changed or removed without migrating existing data.
ENUM(ConditionType,
// Matches the URL scheme (e.g. https, tel).
kScheme,
@@ -52,6 +54,8 @@ ENUM(ConditionType,
kFile)
// Describes what pattern matching rules are applied to a ConditionValue.
+// Values are persisted to disk by preferred_apps_converter.h, so should not be
+// changed or removed without migrating existing data.
enum class PatternMatchType {
kNone = 0,
// The ConditionValue is a literal string which must match the value in the
@@ -161,6 +165,10 @@ struct COMPONENT_EXPORT(APP_TYPES) IntentFilter {
void GetMimeTypesAndExtensions(std::set<std::string>& mime_types,
std::set<std::string>& file_extensions);
+ // Returns all of the links that this intent filter would accept, to be used
+ // in listing all of the supported links for a given app.
+ std::set<std::string> GetSupportedLinksForAppManagement();
+
// Returns true if the filter is a browser filter, i.e. can handle all https
// or http scheme.
bool IsBrowserFilter();
@@ -168,6 +176,9 @@ struct COMPONENT_EXPORT(APP_TYPES) IntentFilter {
// Returns true if the filter only contains file extension pattern matches.
bool IsFileExtensionsFilter();
+ // Checks if the filter is the older version that doesn't contain action.
+ bool FilterNeedsUpgrade();
+
std::string ToString() const;
// A list of Conditions which Intents must match.
@@ -189,9 +200,19 @@ using IntentFilters = std::vector<IntentFilterPtr>;
COMPONENT_EXPORT(APP_TYPES)
IntentFilters CloneIntentFilters(const IntentFilters& intent_filters);
+// Creates a deep copy of `intent_filters` map.
+COMPONENT_EXPORT(APP_TYPES)
+base::flat_map<std::string, IntentFilters> CloneIntentFiltersMap(
+ const base::flat_map<std::string, IntentFilters>& intent_filters_map);
+
COMPONENT_EXPORT(APP_TYPES)
bool IsEqual(const IntentFilters& source, const IntentFilters& target);
+// Returns true if `intent_filters` contains `intent_filter`.
+COMPONENT_EXPORT(APP_TYPES)
+bool Contains(const IntentFilters& intent_filters,
+ const IntentFilterPtr& intent_filter);
+
// TODO(crbug.com/1253250): Remove these functions after migrating to non-mojo
// AppService.
COMPONENT_EXPORT(APP_TYPES)
@@ -235,16 +256,12 @@ apps::mojom::IntentFilterPtr ConvertIntentFilterToMojomIntentFilter(
const IntentFilterPtr& intent_filter);
COMPONENT_EXPORT(APP_TYPES)
-base::flat_map<std::string, std::vector<apps::mojom::IntentFilterPtr>>
-ConvertIntentFiltersToMojomIntentFilters(
- const base::flat_map<std::string, apps::IntentFilters>& intent_filter);
+IntentFilters ConvertMojomIntentFiltersToIntentFilters(
+ const std::vector<apps::mojom::IntentFilterPtr>& mojom_intent_filters);
COMPONENT_EXPORT(APP_TYPES)
-base::flat_map<std::string, apps::IntentFilters>
-ConvertMojomIntentFiltersToIntentFilters(
- const base::flat_map<std::string,
- std::vector<apps::mojom::IntentFilterPtr>>&
- mojom_intent_filter);
+std::vector<apps::mojom::IntentFilterPtr>
+ConvertIntentFiltersToMojomIntentFilters(const IntentFilters& intent_filters);
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter_util.cc b/chromium/components/services/app_service/public/cpp/intent_filter_util.cc
index b433edfb54f..cf5a6031575 100644
--- a/chromium/components/services/app_service/public/cpp/intent_filter_util.cc
+++ b/chromium/components/services/app_service/public/cpp/intent_filter_util.cc
@@ -14,6 +14,44 @@ namespace {
// Returns true if |value1| has overlapping values with |value2|. This method
// should be called twice, with |value1| and |value2| swapped.
+bool ConditionValuesHaveOverlap(const apps::ConditionValuePtr& value1,
+ const apps::ConditionValuePtr& value2) {
+ if (*value1 == *value2) {
+ return true;
+ }
+
+ if (value1->match_type == apps::PatternMatchType::kSuffix &&
+ (value2->match_type == apps::PatternMatchType::kNone ||
+ value2->match_type == apps::PatternMatchType::kLiteral ||
+ value2->match_type == apps::PatternMatchType::kSuffix)) {
+ return base::EndsWith(/*str=*/value2->value,
+ /*search_for=*/value1->value);
+ }
+
+ else if (value1->match_type == apps::PatternMatchType::kLiteral) {
+ if (value2->match_type == apps::PatternMatchType::kPrefix) {
+ return base::StartsWith(/*str=*/value1->value,
+ /*search_for=*/value2->value);
+ } else if (value2->match_type == apps::PatternMatchType::kGlob) {
+ return apps_util::MatchGlob(/*value=*/value1->value,
+ /*pattern=*/value2->value);
+ }
+ }
+
+ else if (value1->match_type == apps::PatternMatchType::kPrefix &&
+ value2->match_type == apps::PatternMatchType::kPrefix) {
+ return base::StartsWith(/*str=*/value1->value,
+ /*search_for=*/value2->value) ||
+ base::StartsWith(/*str=*/value2->value,
+ /*search_for=*/value1->value);
+ }
+
+ return false;
+}
+
+// Returns true if |value1| has overlapping values with |value2|. This method
+// should be called twice, with |value1| and |value2| swapped.
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
bool ConditionValuesHaveOverlap(const apps::mojom::ConditionValuePtr& value1,
const apps::mojom::ConditionValuePtr& value2) {
if (value1 == value2) {
@@ -49,6 +87,25 @@ bool ConditionValuesHaveOverlap(const apps::mojom::ConditionValuePtr& value1,
return false;
}
+bool ConditionsHaveOverlap(const apps::ConditionPtr& condition1,
+ const apps::ConditionPtr& condition2) {
+ if (condition1->condition_type != condition2->condition_type) {
+ return false;
+ }
+ // If there are same |condition_value| exist in the both |condition|s, there
+ // is an overlap.
+ for (auto& value1 : condition1->condition_values) {
+ for (auto& value2 : condition2->condition_values) {
+ if (ConditionValuesHaveOverlap(value1, value2) ||
+ ConditionValuesHaveOverlap(value2, value1)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
bool ConditionsHaveOverlap(const apps::mojom::ConditionPtr& condition1,
const apps::mojom::ConditionPtr& condition2) {
if (condition1->condition_type != condition2->condition_type) {
@@ -171,6 +228,24 @@ int GetFilterMatchLevel(const apps::mojom::IntentFilterPtr& intent_filter) {
return match_level;
}
+bool FiltersHaveOverlap(const apps::IntentFilterPtr& filter1,
+ const apps::IntentFilterPtr& filter2) {
+ if (filter1->conditions.size() != filter2->conditions.size()) {
+ return false;
+ }
+ if (filter1->GetFilterMatchLevel() != filter2->GetFilterMatchLevel()) {
+ return false;
+ }
+ for (size_t i = 0; i < filter1->conditions.size(); i++) {
+ auto& condition1 = filter1->conditions[i];
+ auto& condition2 = filter2->conditions[i];
+ if (!ConditionsHaveOverlap(condition1, condition2)) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool FiltersHaveOverlap(const apps::mojom::IntentFilterPtr& filter1,
const apps::mojom::IntentFilterPtr& filter2) {
if (filter1->conditions.size() != filter2->conditions.size()) {
@@ -198,6 +273,15 @@ bool FilterNeedsUpgrade(const apps::mojom::IntentFilterPtr& filter) {
return true;
}
+void UpgradeFilter(apps::IntentFilterPtr& filter) {
+ std::vector<apps::ConditionValuePtr> condition_values;
+ condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ apps_util::kIntentActionView, apps::PatternMatchType::kNone));
+ auto condition = std::make_unique<apps::Condition>(
+ apps::ConditionType::kAction, std::move(condition_values));
+ filter->conditions.insert(filter->conditions.begin(), std::move(condition));
+}
+
void UpgradeFilter(apps::mojom::IntentFilterPtr& filter) {
std::vector<apps::mojom::ConditionValuePtr> condition_values;
condition_values.push_back(apps_util::MakeConditionValue(
@@ -227,84 +311,6 @@ bool IsBrowserFilter(const apps::mojom::IntentFilterPtr& filter) {
return false;
}
-// This function returns all of the links that the given intent filter would
-// accept, to be used in listing all of the supported links for a given app.
-std::set<std::string> AppManagementGetSupportedLinks(
- const apps::mojom::IntentFilterPtr& intent_filter) {
- std::set<std::string> hosts;
- std::set<std::string> paths;
- bool is_http_or_https = false;
-
- for (auto& condition : intent_filter->conditions) {
- // For scheme conditions we check if it's http or https and set a Boolean
- // if this intent filter is for one of those schemes.
- if (condition->condition_type == apps::mojom::ConditionType::kScheme) {
- for (auto& condition_value : condition->condition_values) {
- // We only care about http and https schemes.
- if (condition_value->value == url::kHttpScheme ||
- condition_value->value == url::kHttpsScheme) {
- is_http_or_https = true;
- break;
- }
- }
-
- // There should only be one condition of type |kScheme| so if there
- // aren't any http or https scheme values this indicates that no http or
- // https scheme exists in the intent filter and thus we will have to
- // return an empty list.
- if (!is_http_or_https) {
- break;
- }
- }
-
- // For host conditions we add each value to the |hosts| set.
- if (condition->condition_type == apps::mojom::ConditionType::kHost) {
- for (auto& condition_value : condition->condition_values) {
- // Prepend the wildcard to indicate any subdomain in the hosts
- std::string host = condition_value->value;
- if (condition_value->match_type ==
- apps::mojom::PatternMatchType::kSuffix) {
- host = "*" + host;
- }
- hosts.insert(host);
- }
- }
-
- // For path conditions we add each value to the |paths| set.
- if (condition->condition_type == apps::mojom::ConditionType::kPattern) {
- for (auto& condition_value : condition->condition_values) {
- std::string value = condition_value->value;
- // Glob and literal patterns can be printed exactly, but prefix
- // patterns must have be appended with "*" to indicate that
- // anything with that prefix can be matched.
- if (condition_value->match_type ==
- apps::mojom::PatternMatchType::kPrefix) {
- value.append("*");
- }
- paths.insert(value);
- }
- }
- }
-
- // We only care about http and https schemes.
- if (!is_http_or_https) {
- return std::set<std::string>();
- }
-
- std::set<std::string> supported_links;
- for (auto& host : hosts) {
- for (auto& path : paths) {
- if (path.front() == '/') {
- supported_links.insert(host + path);
- } else {
- supported_links.insert(host + "/" + path);
- }
- }
- }
-
- return supported_links;
-}
-
bool IsSupportedLinkForApp(const std::string& app_id,
const apps::IntentFilterPtr& intent_filter) {
// Filters associated with kUseBrowserForLink are a special case. These
@@ -407,7 +413,7 @@ bool IsSupportedLinkForApp(const std::string& app_id,
size_t IntentFilterUrlMatchLength(const apps::IntentFilterPtr& intent_filter,
const GURL& url) {
- apps::Intent intent(url);
+ apps::Intent intent(kIntentActionView, url);
if (!intent.MatchFilter(intent_filter)) {
return 0;
}
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter_util.h b/chromium/components/services/app_service/public/cpp/intent_filter_util.h
index c7c3dd64021..631940c516c 100644
--- a/chromium/components/services/app_service/public/cpp/intent_filter_util.h
+++ b/chromium/components/services/app_service/public/cpp/intent_filter_util.h
@@ -55,27 +55,33 @@ apps::mojom::IntentFilterPtr CreateIntentFilterForUrlScope(const GURL& url);
// match compare with filter with only scheme. Each condition type has a
// matching level value, and this function will return the sum of the matching
// level values of all existing condition types.
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
int GetFilterMatchLevel(const apps::mojom::IntentFilterPtr& intent_filter);
// Check if the two intent filters have overlap. i.e. they can handle same
// intent with same match level.
+bool FiltersHaveOverlap(const apps::IntentFilterPtr& filter1,
+ const apps::IntentFilterPtr& filter2);
+
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
bool FiltersHaveOverlap(const apps::mojom::IntentFilterPtr& filter1,
const apps::mojom::IntentFilterPtr& filter2);
// Check if the filter is the older version that doesn't contain action.
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
bool FilterNeedsUpgrade(const apps::mojom::IntentFilterPtr& filter);
// Upgrade the filter to contain action view.
+void UpgradeFilter(apps::IntentFilterPtr& filter);
+
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
void UpgradeFilter(apps::mojom::IntentFilterPtr& filter);
// Check if the filter is a browser filter, i.e. can handle all https
// or http scheme.
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
bool IsBrowserFilter(const apps::mojom::IntentFilterPtr& filter);
-// Convert an intent filter to a list of its supported links.
-std::set<std::string> AppManagementGetSupportedLinks(
- const apps::mojom::IntentFilterPtr& intent_filter);
-
// Checks if the `intent_filter` is a supported link for `app_id`, i.e. it has
// the "view" action, a http or https scheme, and at least one host and pattern.
bool IsSupportedLinkForApp(const std::string& app_id,
diff --git a/chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc b/chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
index a5357262447..cc616e08143 100644
--- a/chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
@@ -75,6 +75,15 @@ class IntentFilterUtilTest : public testing::Test {
return intent_filter;
}
+ apps::IntentFilterPtr MakeHostOnlyFilter(std::string host,
+ apps::PatternMatchType pattern) {
+ auto intent_filter = std::make_unique<apps::IntentFilter>();
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, host,
+ pattern);
+
+ return intent_filter;
+ }
+
apps::mojom::IntentFilterPtr MakeHostOnlyFilter(
std::string host,
apps::mojom::PatternMatchType pattern) {
@@ -88,52 +97,50 @@ class IntentFilterUtilTest : public testing::Test {
};
TEST_F(IntentFilterUtilTest, EmptyConditionList) {
- auto intent_filter = apps::mojom::IntentFilter::New();
+ auto intent_filter = std::make_unique<apps::IntentFilter>();
- EXPECT_EQ(apps_util::AppManagementGetSupportedLinks(intent_filter).size(),
- 0u);
+ EXPECT_EQ(intent_filter->GetSupportedLinksForAppManagement().size(), 0u);
}
TEST_F(IntentFilterUtilTest, SingleHostAndManyPaths) {
- auto intent_filter = apps::mojom::IntentFilter::New();
+ auto intent_filter = std::make_unique<apps::IntentFilter>();
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kScheme, url::kHttpScheme,
- apps::mojom::PatternMatchType::kNone, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme,
+ url::kHttpScheme,
+ apps::PatternMatchType::kNone);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kHost, kHostUrlGoogle,
- apps::mojom::PatternMatchType::kNone, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kHost,
+ kHostUrlGoogle,
+ apps::PatternMatchType::kNone);
std::set<std::string> links =
- apps_util::AppManagementGetSupportedLinks(intent_filter);
+ intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 0u);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kPattern, kPathLiteral,
- apps::mojom::PatternMatchType::kLiteral, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern,
+ kPathLiteral,
+ apps::PatternMatchType::kLiteral);
- links = apps_util::AppManagementGetSupportedLinks(intent_filter);
+ links = intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 1u);
EXPECT_EQ(links.count(kUrlGoogleLiteral), 1u);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kPattern, kPathPrefix,
- apps::mojom::PatternMatchType::kPrefix, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern,
+ kPathPrefix,
+ apps::PatternMatchType::kPrefix);
- links = apps_util::AppManagementGetSupportedLinks(intent_filter);
+ links = intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 2u);
EXPECT_EQ(links.count(kUrlGoogleLiteral), 1u);
EXPECT_EQ(links.count(kUrlGooglePrefix), 1u);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kPattern, kPathGlob,
- apps::mojom::PatternMatchType::kGlob, intent_filter);
+ intent_filter->AddSingleValueCondition(
+ apps::ConditionType::kPattern, kPathGlob, apps::PatternMatchType::kGlob);
- links = apps_util::AppManagementGetSupportedLinks(intent_filter);
+ links = intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 3u);
EXPECT_EQ(links.count(kUrlGoogleLiteral), 1u);
@@ -143,38 +150,38 @@ TEST_F(IntentFilterUtilTest, SingleHostAndManyPaths) {
TEST_F(IntentFilterUtilTest, InvalidScheme) {
auto intent_filter = MakeFilter(url::kTelScheme, kHostUrlGoogle, kPathLiteral,
- apps::mojom::PatternMatchType::kLiteral);
+ apps::PatternMatchType::kLiteral);
std::set<std::string> links =
- apps_util::AppManagementGetSupportedLinks(intent_filter);
+ intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 0u);
}
TEST_F(IntentFilterUtilTest, ManyHostsAndOnePath) {
- auto intent_filter = apps::mojom::IntentFilter::New();
+ auto intent_filter = std::make_unique<apps::IntentFilter>();
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kScheme, url::kHttpScheme,
- apps::mojom::PatternMatchType::kNone, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme,
+ url::kHttpScheme,
+ apps::PatternMatchType::kNone);
- std::vector<apps::mojom::ConditionValuePtr> condition_values;
+ std::vector<apps::ConditionValuePtr> condition_values;
- condition_values.push_back(apps_util::MakeConditionValue(
- kHostUrlGoogle, apps::mojom::PatternMatchType::kNone));
+ condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ kHostUrlGoogle, apps::PatternMatchType::kNone));
- condition_values.push_back(apps_util::MakeConditionValue(
- kHostUrlGmail, apps::mojom::PatternMatchType::kNone));
+ condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ kHostUrlGmail, apps::PatternMatchType::kNone));
- intent_filter->conditions.push_back(apps_util::MakeCondition(
- apps::mojom::ConditionType::kHost, std::move(condition_values)));
+ intent_filter->conditions.push_back(std::make_unique<apps::Condition>(
+ apps::ConditionType::kHost, std::move(condition_values)));
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kPattern, kPathLiteral,
- apps::mojom::PatternMatchType::kLiteral, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern,
+ kPathLiteral,
+ apps::PatternMatchType::kLiteral);
std::set<std::string> links =
- apps_util::AppManagementGetSupportedLinks(intent_filter);
+ intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 2u);
EXPECT_EQ(links.count(kUrlGoogleLiteral), 1u);
@@ -182,36 +189,36 @@ TEST_F(IntentFilterUtilTest, ManyHostsAndOnePath) {
}
TEST_F(IntentFilterUtilTest, ManyHostsAndManyPaths) {
- auto intent_filter = apps::mojom::IntentFilter::New();
+ auto intent_filter = std::make_unique<apps::IntentFilter>();
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kScheme, url::kHttpScheme,
- apps::mojom::PatternMatchType::kNone, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme,
+ url::kHttpScheme,
+ apps::PatternMatchType::kNone);
- std::vector<apps::mojom::ConditionValuePtr> host_condition_values;
+ std::vector<apps::ConditionValuePtr> host_condition_values;
- host_condition_values.push_back(apps_util::MakeConditionValue(
- kHostUrlGoogle, apps::mojom::PatternMatchType::kNone));
- host_condition_values.push_back(apps_util::MakeConditionValue(
- kHostUrlGmail, apps::mojom::PatternMatchType::kNone));
+ host_condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ kHostUrlGoogle, apps::PatternMatchType::kNone));
+ host_condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ kHostUrlGmail, apps::PatternMatchType::kNone));
- intent_filter->conditions.push_back(apps_util::MakeCondition(
- apps::mojom::ConditionType::kHost, std::move(host_condition_values)));
+ intent_filter->conditions.push_back(std::make_unique<apps::Condition>(
+ apps::ConditionType::kHost, std::move(host_condition_values)));
- std::vector<apps::mojom::ConditionValuePtr> path_condition_values;
+ std::vector<apps::ConditionValuePtr> path_condition_values;
- path_condition_values.push_back(apps_util::MakeConditionValue(
- kPathLiteral, apps::mojom::PatternMatchType::kLiteral));
- path_condition_values.push_back(apps_util::MakeConditionValue(
- kPathPrefix, apps::mojom::PatternMatchType::kPrefix));
- path_condition_values.push_back(apps_util::MakeConditionValue(
- kPathGlob, apps::mojom::PatternMatchType::kGlob));
+ path_condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ kPathLiteral, apps::PatternMatchType::kLiteral));
+ path_condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ kPathPrefix, apps::PatternMatchType::kPrefix));
+ path_condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ kPathGlob, apps::PatternMatchType::kGlob));
- intent_filter->conditions.push_back(apps_util::MakeCondition(
- apps::mojom::ConditionType::kPattern, std::move(path_condition_values)));
+ intent_filter->conditions.push_back(std::make_unique<apps::Condition>(
+ apps::ConditionType::kPattern, std::move(path_condition_values)));
std::set<std::string> links =
- apps_util::AppManagementGetSupportedLinks(intent_filter);
+ intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 6u);
EXPECT_EQ(links.count(kUrlGoogleLiteral), 1u);
@@ -224,88 +231,86 @@ TEST_F(IntentFilterUtilTest, ManyHostsAndManyPaths) {
TEST_F(IntentFilterUtilTest, WildcardHost) {
std::string host = ".google.com";
- auto intent_filter = apps::mojom::IntentFilter::New();
+ auto intent_filter = std::make_unique<apps::IntentFilter>();
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kScheme, url::kHttpScheme,
- apps::mojom::PatternMatchType::kNone, intent_filter);
- apps_util::AddSingleValueCondition(apps::mojom::ConditionType::kHost, host,
- apps::mojom::PatternMatchType::kSuffix,
- intent_filter);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kPattern, kPathLiteral,
- apps::mojom::PatternMatchType::kLiteral, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme,
+ url::kHttpScheme,
+ apps::PatternMatchType::kNone);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, host,
+ apps::PatternMatchType::kSuffix);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern,
+ kPathLiteral,
+ apps::PatternMatchType::kLiteral);
std::set<std::string> links =
- apps_util::AppManagementGetSupportedLinks(intent_filter);
+ intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 1u);
EXPECT_EQ(links.count("*.google.com/a"), 1u);
}
TEST_F(IntentFilterUtilTest, HttpsScheme) {
- std::set<std::string> links = apps_util::AppManagementGetSupportedLinks(
+ auto intent_filter =
MakeFilter(url::kHttpsScheme, kHostUrlGoogle, kPathLiteral,
- apps::mojom::PatternMatchType::kLiteral));
+ apps::PatternMatchType::kLiteral);
+ std::set<std::string> links =
+ intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 1u);
EXPECT_EQ(links.count(kUrlGoogleLiteral), 1u);
}
TEST_F(IntentFilterUtilTest, HttpAndHttpsSchemes) {
- auto intent_filter = apps::mojom::IntentFilter::New();
+ auto intent_filter = std::make_unique<apps::IntentFilter>();
- std::vector<apps::mojom::ConditionValuePtr> condition_values;
+ std::vector<apps::ConditionValuePtr> condition_values;
- condition_values.push_back(apps_util::MakeConditionValue(
- url::kHttpScheme, apps::mojom::PatternMatchType::kNone));
+ condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ url::kHttpScheme, apps::PatternMatchType::kNone));
- condition_values.push_back(apps_util::MakeConditionValue(
- url::kHttpsScheme, apps::mojom::PatternMatchType::kNone));
+ condition_values.push_back(std::make_unique<apps::ConditionValue>(
+ url::kHttpsScheme, apps::PatternMatchType::kNone));
- intent_filter->conditions.push_back(apps_util::MakeCondition(
- apps::mojom::ConditionType::kScheme, std::move(condition_values)));
+ intent_filter->conditions.push_back(std::make_unique<apps::Condition>(
+ apps::ConditionType::kScheme, std::move(condition_values)));
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kHost, kHostUrlGoogle,
- apps::mojom::PatternMatchType::kNone, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kHost,
+ kHostUrlGoogle,
+ apps::PatternMatchType::kNone);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kPattern, kPathLiteral,
- apps::mojom::PatternMatchType::kLiteral, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern,
+ kPathLiteral,
+ apps::PatternMatchType::kLiteral);
std::set<std::string> links =
- apps_util::AppManagementGetSupportedLinks(intent_filter);
+ intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 1u);
EXPECT_EQ(links.count(kUrlGoogleLiteral), 1u);
}
TEST_F(IntentFilterUtilTest, PathsWithNoSlash) {
- auto intent_filter = apps::mojom::IntentFilter::New();
+ auto intent_filter = std::make_unique<apps::IntentFilter>();
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kScheme, url::kHttpScheme,
- apps::mojom::PatternMatchType::kNone, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme,
+ url::kHttpScheme,
+ apps::PatternMatchType::kNone);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kHost, "m.youtube.com",
- apps::mojom::PatternMatchType::kNone, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kHost,
+ "m.youtube.com",
+ apps::PatternMatchType::kNone);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kPattern, ".*",
- apps::mojom::PatternMatchType::kGlob, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern, ".*",
+ apps::PatternMatchType::kGlob);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kPattern, ".*/foo",
- apps::mojom::PatternMatchType::kGlob, intent_filter);
+ intent_filter->AddSingleValueCondition(
+ apps::ConditionType::kPattern, ".*/foo", apps::PatternMatchType::kGlob);
- apps_util::AddSingleValueCondition(
- apps::mojom::ConditionType::kPattern, "",
- apps::mojom::PatternMatchType::kPrefix, intent_filter);
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern, "",
+ apps::PatternMatchType::kPrefix);
std::set<std::string> links =
- apps_util::AppManagementGetSupportedLinks(intent_filter);
+ intent_filter->GetSupportedLinksForAppManagement();
EXPECT_EQ(links.size(), 3u);
EXPECT_EQ(links.count("m.youtube.com/*"), 1u);
@@ -385,7 +390,8 @@ TEST_F(IntentFilterUtilTest, NotSupportedLink) {
ASSERT_FALSE(apps_util::IsSupportedLinkForApp(kAppId, browser_filter));
}
-TEST_F(IntentFilterUtilTest, HostMatchOverlapLiteralAndNone) {
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+TEST_F(IntentFilterUtilTest, HostMatchOverlapLiteralAndNoneMojom) {
auto google_domain_filter = MakeFilter(
"https", "www.google.com", "/", apps::mojom::PatternMatchType::kLiteral);
@@ -403,7 +409,26 @@ TEST_F(IntentFilterUtilTest, HostMatchOverlapLiteralAndNone) {
apps_util::FiltersHaveOverlap(maps_domain_filter, google_domain_filter));
}
-TEST_F(IntentFilterUtilTest, HostMatchOverlapSuffix) {
+TEST_F(IntentFilterUtilTest, HostMatchOverlapLiteralAndNone) {
+ auto google_domain_filter = MakeFilter("https", "www.google.com", "/",
+ apps::PatternMatchType::kLiteral);
+
+ auto maps_domain_filter = MakeFilter("https", "maps.google.com", "/",
+ apps::PatternMatchType::kLiteral);
+
+ ASSERT_FALSE(
+ apps_util::FiltersHaveOverlap(maps_domain_filter, google_domain_filter));
+
+ apps_util::AddConditionValue(apps::ConditionType::kHost, "www.google.com",
+ apps::PatternMatchType::kNone,
+ maps_domain_filter);
+
+ ASSERT_TRUE(
+ apps_util::FiltersHaveOverlap(maps_domain_filter, google_domain_filter));
+}
+
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+TEST_F(IntentFilterUtilTest, HostMatchOverlapSuffixMojom) {
// Wildcard host filter
auto wikipedia_wildcard_filter = MakeHostOnlyFilter(
".wikipedia.org", apps::mojom::PatternMatchType::kSuffix);
@@ -443,7 +468,48 @@ TEST_F(IntentFilterUtilTest, HostMatchOverlapSuffix) {
wikipedia_subsubdomain_filter));
}
-TEST_F(IntentFilterUtilTest, PatternMatchOverlap) {
+TEST_F(IntentFilterUtilTest, HostMatchOverlapSuffix) {
+ // Wildcard host filter
+ auto wikipedia_wildcard_filter =
+ MakeHostOnlyFilter(".wikipedia.org", apps::PatternMatchType::kSuffix);
+
+ // Filters that shouldn't overlap
+ auto wikipedia_com_filter =
+ MakeHostOnlyFilter(".wikipedia.com", apps::PatternMatchType::kNone);
+ auto wikipedia_no_subdomain_filter =
+ MakeHostOnlyFilter("wikipedia.org", apps::PatternMatchType::kNone);
+
+ ASSERT_FALSE(apps_util::FiltersHaveOverlap(wikipedia_wildcard_filter,
+ wikipedia_com_filter));
+ ASSERT_FALSE(apps_util::FiltersHaveOverlap(wikipedia_wildcard_filter,
+ wikipedia_no_subdomain_filter));
+
+ // Filters that should overlap
+ auto wikipedia_subdomain_filter =
+ MakeHostOnlyFilter("es.wikipedia.org", apps::PatternMatchType::kNone);
+ auto wikipedia_empty_subdomain_filter =
+ MakeHostOnlyFilter(".wikipedia.org", apps::PatternMatchType::kNone);
+ auto wikipedia_literal_filter =
+ MakeHostOnlyFilter("fr.wikipedia.org", apps::PatternMatchType::kLiteral);
+ auto wikipedia_other_wildcard_filter =
+ MakeHostOnlyFilter(".wikipedia.org", apps::PatternMatchType::kSuffix);
+ auto wikipedia_subsubdomain_filter =
+ MakeHostOnlyFilter(".es.wikipedia.org", apps::PatternMatchType::kSuffix);
+
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(wikipedia_wildcard_filter,
+ wikipedia_subdomain_filter));
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(wikipedia_wildcard_filter,
+ wikipedia_empty_subdomain_filter));
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(wikipedia_wildcard_filter,
+ wikipedia_literal_filter));
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(wikipedia_wildcard_filter,
+ wikipedia_other_wildcard_filter));
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(wikipedia_wildcard_filter,
+ wikipedia_subsubdomain_filter));
+}
+
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+TEST_F(IntentFilterUtilTest, PatternMatchOverlapMojom) {
auto literal_pattern_filter1 = MakeFilter(
"https", "www.example.com", "/", apps::mojom::PatternMatchType::kLiteral);
apps_util::AddConditionValue(apps::mojom::ConditionType::kPattern, "/foo",
@@ -482,7 +548,46 @@ TEST_F(IntentFilterUtilTest, PatternMatchOverlap) {
apps_util::FiltersHaveOverlap(foo_prefix_filter, bar_prefix_filter));
}
-TEST_F(IntentFilterUtilTest, PatternGlobAndLiteralOverlap) {
+TEST_F(IntentFilterUtilTest, PatternMatchOverlap) {
+ auto literal_pattern_filter1 = MakeFilter("https", "www.example.com", "/",
+ apps::PatternMatchType::kLiteral);
+ apps_util::AddConditionValue(apps::ConditionType::kPattern, "/foo",
+ apps::PatternMatchType::kLiteral,
+ literal_pattern_filter1);
+
+ auto literal_pattern_filter2 = MakeFilter(
+ "https", "www.example.com", "/foo/bar", apps::PatternMatchType::kLiteral);
+ apps_util::AddConditionValue(apps::ConditionType::kPattern, "/bar",
+ apps::PatternMatchType::kLiteral,
+ literal_pattern_filter2);
+
+ ASSERT_FALSE(apps_util::FiltersHaveOverlap(literal_pattern_filter1,
+ literal_pattern_filter2));
+
+ auto root_prefix_filter = MakeFilter("https", "www.example.com", "/",
+ apps::PatternMatchType::kPrefix);
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(root_prefix_filter,
+ literal_pattern_filter1));
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(root_prefix_filter,
+ literal_pattern_filter2));
+
+ auto bar_prefix_filter = MakeFilter("https", "www.example.com", "/bar",
+ apps::PatternMatchType::kPrefix);
+ ASSERT_FALSE(apps_util::FiltersHaveOverlap(bar_prefix_filter,
+ literal_pattern_filter1));
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(bar_prefix_filter,
+ literal_pattern_filter2));
+ ASSERT_TRUE(
+ apps_util::FiltersHaveOverlap(bar_prefix_filter, root_prefix_filter));
+
+ auto foo_prefix_filter = MakeFilter("https", "www.example.com", "/foo",
+ apps::PatternMatchType::kPrefix);
+ ASSERT_FALSE(
+ apps_util::FiltersHaveOverlap(foo_prefix_filter, bar_prefix_filter));
+}
+
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+TEST_F(IntentFilterUtilTest, PatternGlobAndLiteralOverlapMojom) {
auto literal_pattern_filter1 =
MakeFilter("https", "maps.google.com", "/u/0/maps",
apps::mojom::PatternMatchType::kLiteral);
@@ -503,102 +608,23 @@ TEST_F(IntentFilterUtilTest, PatternGlobAndLiteralOverlap) {
glob_pattern_filter));
}
-TEST_F(IntentFilterUtilTest, IntentFiltersConvert) {
- base::flat_map<std::string, std::vector<apps::IntentFilterPtr>> filters;
+TEST_F(IntentFilterUtilTest, PatternGlobAndLiteralOverlap) {
+ auto literal_pattern_filter1 =
+ MakeFilter("https", "maps.google.com", "/u/0/maps",
+ apps::PatternMatchType::kLiteral);
+ auto literal_pattern_filter2 = MakeFilter("https", "maps.google.com", "/maps",
+ apps::PatternMatchType::kLiteral);
- auto intent_filter1 = std::make_unique<apps::IntentFilter>();
- intent_filter1->AddSingleValueCondition(apps::ConditionType::kScheme, "1",
- apps::PatternMatchType::kNone);
- filters["1"].push_back(std::move(intent_filter1));
-
- auto intent_filter2 = std::make_unique<apps::IntentFilter>();
- intent_filter2->AddSingleValueCondition(apps::ConditionType::kHost, "2",
- apps::PatternMatchType::kLiteral);
- intent_filter2->AddSingleValueCondition(apps::ConditionType::kPattern, "3",
- apps::PatternMatchType::kPrefix);
- filters["1"].push_back(std::move(intent_filter2));
-
- apps::IntentFilters intent_filters2;
- auto intent_filter3 = std::make_unique<apps::IntentFilter>();
- intent_filter3->AddSingleValueCondition(apps::ConditionType::kAction, "4",
- apps::PatternMatchType::kGlob);
- intent_filter3->AddSingleValueCondition(apps::ConditionType::kMimeType, "5",
- apps::PatternMatchType::kMimeType);
- filters["2"].push_back(std::move(intent_filter3));
-
- auto intent_filter4 = std::make_unique<apps::IntentFilter>();
- intent_filter4->AddSingleValueCondition(apps::ConditionType::kFile, "6",
- apps::PatternMatchType::kMimeType);
- intent_filter4->AddSingleValueCondition(
- apps::ConditionType::kFile, "7", apps::PatternMatchType::kFileExtension);
- filters["2"].push_back(std::move(intent_filter4));
-
- auto output = apps::ConvertMojomIntentFiltersToIntentFilters(
- apps::ConvertIntentFiltersToMojomIntentFilters(filters));
-
- ASSERT_EQ(output.size(), 2U);
- EXPECT_EQ(*filters["1"][0], *output["1"][0]);
- EXPECT_EQ(*filters["1"][1], *output["1"][1]);
-
- EXPECT_EQ(*filters["2"][0], *output["2"][0]);
- EXPECT_EQ(*filters["2"][1], *output["2"][1]);
-
- {
- auto& condition = output["1"][0]->conditions[0];
- EXPECT_EQ(condition->condition_type, apps::ConditionType::kScheme);
- ASSERT_EQ(condition->condition_values.size(), 1U);
- EXPECT_EQ(condition->condition_values[0]->match_type,
- apps::PatternMatchType::kNone);
- EXPECT_EQ(condition->condition_values[0]->value, "1");
- }
- {
- auto& condition = output["1"][1]->conditions[0];
- EXPECT_EQ(condition->condition_type, apps::ConditionType::kHost);
- ASSERT_EQ(condition->condition_values.size(), 1U);
- EXPECT_EQ(condition->condition_values[0]->match_type,
- apps::PatternMatchType::kLiteral);
- EXPECT_EQ(condition->condition_values[0]->value, "2");
- }
- {
- auto& condition = output["1"][1]->conditions[1];
- EXPECT_EQ(condition->condition_type, apps::ConditionType::kPattern);
- ASSERT_EQ(condition->condition_values.size(), 1U);
- EXPECT_EQ(condition->condition_values[0]->match_type,
- apps::PatternMatchType::kPrefix);
- EXPECT_EQ(condition->condition_values[0]->value, "3");
- }
- {
- auto& condition = output["2"][0]->conditions[0];
- EXPECT_EQ(condition->condition_type, apps::ConditionType::kAction);
- ASSERT_EQ(condition->condition_values.size(), 1U);
- EXPECT_EQ(condition->condition_values[0]->match_type,
- apps::PatternMatchType::kGlob);
- EXPECT_EQ(condition->condition_values[0]->value, "4");
- }
- {
- auto& condition = output["2"][0]->conditions[1];
- EXPECT_EQ(condition->condition_type, apps::ConditionType::kMimeType);
- ASSERT_EQ(condition->condition_values.size(), 1U);
- EXPECT_EQ(condition->condition_values[0]->match_type,
- apps::PatternMatchType::kMimeType);
- EXPECT_EQ(condition->condition_values[0]->value, "5");
- }
- {
- auto& condition = output["2"][1]->conditions[0];
- EXPECT_EQ(condition->condition_type, apps::ConditionType::kFile);
- ASSERT_EQ(condition->condition_values.size(), 1U);
- EXPECT_EQ(condition->condition_values[0]->match_type,
- apps::PatternMatchType::kMimeType);
- EXPECT_EQ(condition->condition_values[0]->value, "6");
- }
- {
- auto& condition = output["2"][1]->conditions[1];
- EXPECT_EQ(condition->condition_type, apps::ConditionType::kFile);
- ASSERT_EQ(condition->condition_values.size(), 1U);
- EXPECT_EQ(condition->condition_values[0]->match_type,
- apps::PatternMatchType::kFileExtension);
- EXPECT_EQ(condition->condition_values[0]->value, "7");
- }
+ auto glob_pattern_filter = MakeFilter(
+ "https", "maps.google.com", "/u/.*/maps", apps::PatternMatchType::kGlob);
+
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(literal_pattern_filter1,
+ glob_pattern_filter));
+ ASSERT_TRUE(apps_util::FiltersHaveOverlap(glob_pattern_filter,
+ literal_pattern_filter1));
+
+ ASSERT_FALSE(apps_util::FiltersHaveOverlap(literal_pattern_filter2,
+ glob_pattern_filter));
}
TEST_F(IntentFilterUtilTest, TestIntentFilterUrlMatchLength) {
diff --git a/chromium/components/services/app_service/public/cpp/intent_util.cc b/chromium/components/services/app_service/public/cpp/intent_util.cc
index 7cf26942140..cf523695642 100644
--- a/chromium/components/services/app_service/public/cpp/intent_util.cc
+++ b/chromium/components/services/app_service/public/cpp/intent_util.cc
@@ -95,6 +95,59 @@ const char kIntentActionEdit[] = "edit";
const char kUseBrowserForLink[] = "use_browser";
+apps::IntentPtr MakeShareIntent(const std::vector<GURL>& filesystem_urls,
+ const std::vector<std::string>& mime_types) {
+ auto intent = std::make_unique<apps::Intent>(filesystem_urls.size() == 1
+ ? kIntentActionSend
+ : kIntentActionSendMultiple);
+ intent->mime_type = CalculateCommonMimeType(mime_types);
+
+ DCHECK_EQ(filesystem_urls.size(), mime_types.size());
+ for (size_t i = 0; i < filesystem_urls.size(); i++) {
+ auto file = std::make_unique<apps::IntentFile>(filesystem_urls[i]);
+ file->mime_type = mime_types.at(i);
+ intent->files.push_back(std::move(file));
+ }
+
+ return intent;
+}
+
+apps::IntentPtr MakeShareIntent(const std::vector<GURL>& filesystem_urls,
+ const std::vector<std::string>& mime_types,
+ const std::string& text,
+ const std::string& title) {
+ auto intent = MakeShareIntent(filesystem_urls, mime_types);
+ if (!text.empty()) {
+ intent->share_text = text;
+ }
+ if (!title.empty()) {
+ intent->share_title = title;
+ }
+ return intent;
+}
+
+apps::IntentPtr MakeShareIntent(const std::string& text,
+ const std::string& title) {
+ auto intent = std::make_unique<apps::Intent>(kIntentActionSend);
+ intent->mime_type = "text/plain";
+ intent->share_text = text;
+ if (!title.empty()) {
+ intent->share_title = title;
+ }
+ return intent;
+}
+
+apps::IntentPtr MakeEditIntent(const GURL& filesystem_url,
+ const std::string& mime_type) {
+ auto intent = std::make_unique<apps::Intent>(kIntentActionEdit);
+ intent->mime_type = mime_type;
+
+ auto file = std::make_unique<apps::IntentFile>(filesystem_url);
+ file->mime_type = mime_type;
+ intent->files.push_back(std::move(file));
+ return intent;
+}
+
apps::mojom::IntentPtr CreateIntentFromUrl(const GURL& url) {
auto intent = apps::mojom::Intent::New();
intent->action = kIntentActionView;
diff --git a/chromium/components/services/app_service/public/cpp/intent_util.h b/chromium/components/services/app_service/public/cpp/intent_util.h
index 92adcba5a9d..07a830571d7 100644
--- a/chromium/components/services/app_service/public/cpp/intent_util.h
+++ b/chromium/components/services/app_service/public/cpp/intent_util.h
@@ -39,6 +39,30 @@ struct SharedText {
GURL url;
};
+// Creates an intent for sharing |filesystem_urls|. |filesystem_urls| must be
+// co-indexed with |mime_types|.
+apps::IntentPtr MakeShareIntent(const std::vector<GURL>& filesystem_urls,
+ const std::vector<std::string>& mime_types);
+
+// Creates an intent for sharing |filesystem_urls|, along with |text| and a
+// |title|. |filesystem_urls| must be co-indexed with |mime_types|.
+apps::IntentPtr MakeShareIntent(const std::vector<GURL>& filesystem_urls,
+ const std::vector<std::string>& mime_types,
+ const std::string& text,
+ const std::string& title);
+
+// Creates an intent for sharing |text|, with |title|.
+apps::IntentPtr MakeShareIntent(const std::string& text,
+ const std::string& title);
+
+// Create an edit intent for the file with a given |filesystem_url| and
+// |mime_type|.
+apps::IntentPtr MakeEditIntent(const GURL& filesystem_url,
+ const std::string& mime_type);
+
+// TODO(crbug.com/1253250): Remove below functions after migrating to non-mojo
+// AppService.
+
// Create an intent struct from URL.
apps::mojom::IntentPtr CreateIntentFromUrl(const GURL& url);
diff --git a/chromium/components/services/app_service/public/cpp/intent_util_unittest.cc b/chromium/components/services/app_service/public/cpp/intent_util_unittest.cc
index de8514538eb..55b9debfb49 100644
--- a/chromium/components/services/app_service/public/cpp/intent_util_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/intent_util_unittest.cc
@@ -65,7 +65,37 @@ class IntentUtilTest : public testing::Test {
return files;
}
- apps::mojom::IntentPtr CreateIntent(
+ apps::IntentPtr CreateIntent(
+ const std::string& action,
+ const GURL& url,
+ const std::string& mime_type,
+ std::vector<apps::IntentFilePtr> files,
+ const std::string& activity_name,
+ const GURL& drive_share_url,
+ const std::string& share_text,
+ const std::string& share_title,
+ const std::string& start_type,
+ std::vector<std::string> categories,
+ const std::string& data,
+ bool ui_bypassed,
+ const base::flat_map<std::string, std::string>& extras) {
+ auto intent = std::make_unique<apps::Intent>(action);
+ intent->url = url;
+ intent->mime_type = mime_type;
+ intent->files = std::move(files);
+ intent->activity_name = activity_name;
+ intent->drive_share_url = drive_share_url;
+ intent->share_text = share_text;
+ intent->share_title = share_title;
+ intent->start_type = start_type;
+ intent->categories = categories;
+ intent->data = data;
+ intent->ui_bypassed = ui_bypassed;
+ intent->extras = extras;
+ return intent;
+ }
+
+ apps::mojom::IntentPtr CreateMojomIntent(
const std::string& action,
const GURL& url,
const std::string& mime_type,
@@ -109,7 +139,8 @@ TEST_F(IntentUtilTest, AllConditionMatchesMojom) {
TEST_F(IntentUtilTest, AllConditionMatches) {
GURL test_url("https://www.google.com/");
- auto intent = std::make_unique<apps::Intent>(test_url);
+ auto intent =
+ std::make_unique<apps::Intent>(apps_util::kIntentActionView, test_url);
auto intent_filter = apps_util::MakeIntentFilterForUrlScope(GURL(kFilterUrl));
EXPECT_TRUE(intent->MatchFilter(intent_filter));
@@ -127,7 +158,8 @@ TEST_F(IntentUtilTest, OneConditionDoesNotMatchMojom) {
TEST_F(IntentUtilTest, OneConditionDoesNotMatch) {
GURL test_url("https://www.abc.com/");
- auto intent = std::make_unique<apps::Intent>(test_url);
+ auto intent =
+ std::make_unique<apps::Intent>(apps_util::kIntentActionView, test_url);
auto intent_filter = apps_util::MakeIntentFilterForUrlScope(GURL(kFilterUrl));
EXPECT_FALSE(intent->MatchFilter(intent_filter));
@@ -145,7 +177,8 @@ TEST_F(IntentUtilTest, IntentDoesNotHaveValueToMatchMojom) {
TEST_F(IntentUtilTest, IntentDoesNotHaveValueToMatch) {
GURL test_url("www.abc.com/");
- auto intent = std::make_unique<apps::Intent>(test_url);
+ auto intent =
+ std::make_unique<apps::Intent>(apps_util::kIntentActionView, test_url);
auto intent_filter = apps_util::MakeIntentFilterForUrlScope(GURL(kFilterUrl));
EXPECT_FALSE(intent->MatchFilter(intent_filter));
@@ -164,7 +197,8 @@ TEST_F(IntentUtilTest, OneMojomConditionValueMatch) {
TEST_F(IntentUtilTest, OneConditionValueMatch) {
auto condition = CreateMultiConditionValuesCondition();
GURL test_url("https://www.google.com/");
- auto intent = std::make_unique<apps::Intent>(test_url);
+ auto intent =
+ std::make_unique<apps::Intent>(apps_util::kIntentActionView, test_url);
EXPECT_TRUE(intent->MatchCondition(condition));
}
@@ -179,7 +213,8 @@ TEST_F(IntentUtilTest, NoneMojomConditionValueMatch) {
TEST_F(IntentUtilTest, NoneConditionValueMatch) {
auto condition = CreateMultiConditionValuesCondition();
GURL test_url("tel://www.google.com/");
- auto intent = std::make_unique<apps::Intent>(test_url);
+ auto intent =
+ std::make_unique<apps::Intent>(apps_util::kIntentActionView, test_url);
EXPECT_FALSE(intent->MatchCondition(condition));
}
@@ -464,11 +499,13 @@ TEST_F(IntentUtilTest, ActionMatchMojom) {
TEST_F(IntentUtilTest, ActionMatch) {
GURL test_url("https://www.google.com/");
- auto intent = std::make_unique<apps::Intent>(test_url);
+ auto intent =
+ std::make_unique<apps::Intent>(apps_util::kIntentActionView, test_url);
auto intent_filter = apps_util::MakeIntentFilterForUrlScope(GURL(kFilterUrl));
EXPECT_TRUE(intent->MatchFilter(intent_filter));
- auto send_intent = std::make_unique<apps::Intent>(test_url);
+ auto send_intent =
+ std::make_unique<apps::Intent>(apps_util::kIntentActionView, test_url);
send_intent->action = apps_util::kIntentActionSend;
EXPECT_FALSE(send_intent->MatchFilter(intent_filter));
@@ -750,7 +787,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatch) {
// Test match with text/plain type.
mime_types.push_back(mime_type1);
- auto intent = std::make_unique<apps::Intent>(urls, mime_types);
+ auto intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_TRUE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter2));
EXPECT_TRUE(intent->MatchFilter(filter1_and_2));
@@ -761,7 +798,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatch) {
// Test match with image/jpeg type.
mime_types.clear();
mime_types.push_back(mime_type2);
- intent = std::make_unique<apps::Intent>(urls, mime_types);
+ intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_FALSE(intent->MatchFilter(filter1));
EXPECT_TRUE(intent->MatchFilter(filter2));
EXPECT_TRUE(intent->MatchFilter(filter1_and_2));
@@ -772,7 +809,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatch) {
// Test match with text/html type.
mime_types.clear();
mime_types.push_back(mime_type3);
- intent = std::make_unique<apps::Intent>(urls, mime_types);
+ intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_FALSE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter2));
EXPECT_FALSE(intent->MatchFilter(filter1_and_2));
@@ -783,7 +820,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatch) {
// Test match with text/* types.
mime_types.clear();
mime_types.push_back(mime_type_sub_wildcard);
- intent = std::make_unique<apps::Intent>(urls, mime_types);
+ intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_FALSE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter2));
EXPECT_FALSE(intent->MatchFilter(filter1_and_2));
@@ -794,7 +831,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatch) {
// Test match with */* type.
mime_types.clear();
mime_types.push_back(mime_type_all_wildcard);
- intent = std::make_unique<apps::Intent>(urls, mime_types);
+ intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_FALSE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter2));
EXPECT_FALSE(intent->MatchFilter(filter1_and_2));
@@ -906,7 +943,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatchMultiple) {
// Test match with same mime types.
mime_types.push_back(mime_type1);
mime_types.push_back(mime_type1);
- auto intent = std::make_unique<apps::Intent>(urls, mime_types);
+ auto intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_TRUE(intent->MatchFilter(filter1));
EXPECT_TRUE(intent->MatchFilter(filter1_and_2));
EXPECT_FALSE(intent->MatchFilter(filter3));
@@ -915,7 +952,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatchMultiple) {
mime_types.clear();
mime_types.push_back(mime_type1);
mime_types.push_back(mime_type3);
- intent = std::make_unique<apps::Intent>(urls, mime_types);
+ intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_FALSE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter1_and_2));
EXPECT_FALSE(intent->MatchFilter(filter3));
@@ -925,7 +962,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatchMultiple) {
mime_types.clear();
mime_types.push_back(mime_type1);
mime_types.push_back(mime_type_sub_wildcard);
- intent = std::make_unique<apps::Intent>(urls, mime_types);
+ intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_FALSE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter1_and_2));
EXPECT_TRUE(intent->MatchFilter(filter_sub_wildcard));
@@ -934,7 +971,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatchMultiple) {
mime_types.clear();
mime_types.push_back(mime_type1);
mime_types.push_back(mime_type2);
- intent = std::make_unique<apps::Intent>(urls, mime_types);
+ intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_FALSE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter2));
EXPECT_FALSE(intent->MatchFilter(filter_sub_wildcard));
@@ -945,7 +982,7 @@ TEST_F(IntentUtilTest, CommonMimeTypeMatchMultiple) {
mime_types.clear();
mime_types.push_back(mime_type1);
mime_types.push_back(mime_type_all_wildcard);
- intent = std::make_unique<apps::Intent>(urls, mime_types);
+ intent = apps_util::MakeShareIntent(urls, mime_types);
EXPECT_FALSE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter1_and_2));
EXPECT_FALSE(intent->MatchFilter(filter_sub_wildcard));
@@ -1041,21 +1078,25 @@ TEST_F(IntentUtilTest, FileExtensionMatch) {
// Test match with the same mime type and the same file extension.
auto intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(test_url("abc.mp3"), mime_type_mp3, false));
EXPECT_TRUE(intent->MatchFilter(file_filter));
// Test match with different mime types and the same file extension.
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(test_url("abc.mp3"), mime_type_mp3, false));
EXPECT_TRUE(intent->MatchFilter(file_filter));
// Test match with the same mime type and a different file extension.
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(test_url("abc.png"), mime_type_mp3, false));
EXPECT_TRUE(intent->MatchFilter(file_filter));
// Test match with different mime types and a different file extension.
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(test_url("abc.png"), mime_type_mpeg, false));
EXPECT_FALSE(intent->MatchFilter(file_filter));
@@ -1065,12 +1106,14 @@ TEST_F(IntentUtilTest, FileExtensionMatch) {
// The whole extension must match, not just the end.
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(test_url("abc.extramp3"), mime_type_mpeg, false));
EXPECT_FALSE(intent->MatchFilter(file_filter));
EXPECT_FALSE(intent->MatchFilter(file_filter_dot));
// Check that the filter behaves the same with and without a leading ".".
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(test_url("abc.mp3"), mime_type_mpeg, false));
EXPECT_TRUE(intent->MatchFilter(file_filter_dot));
}
@@ -1137,16 +1180,19 @@ TEST_F(IntentUtilTest, FileURLMatch) {
// Test match with mp3 file extension.
auto intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(ext_test_url("abc.mp3"), "", false));
EXPECT_TRUE(intent->MatchFilter(url_filter));
// Test non-match with mp4 file extension.
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(ext_test_url("abc.mp4"), "", false));
EXPECT_FALSE(intent->MatchFilter(url_filter));
// Test non-match with just the end of a file extension.
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(ext_test_url("abc.testmp3"), "", false));
EXPECT_FALSE(intent->MatchFilter(url_filter));
@@ -1156,11 +1202,13 @@ TEST_F(IntentUtilTest, FileURLMatch) {
// Test that mp3 matches with *
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(ext_test_url("abc.mp3"), "", false));
EXPECT_TRUE(intent->MatchFilter(wild_filter));
// Test that no file extension matches with *
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(ext_test_url("abc"), "", false));
EXPECT_TRUE(intent->MatchFilter(wild_filter));
@@ -1171,11 +1219,13 @@ TEST_F(IntentUtilTest, FileURLMatch) {
// Test that mp3 matches with *.*
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(ext_test_url("abc.mp3"), "", false));
EXPECT_TRUE(intent->MatchFilter(ext_wild_filter));
// Test that no file extension does not match with *.*
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(ext_test_url("abc"), "", false));
EXPECT_FALSE(intent->MatchFilter(ext_wild_filter));
}
@@ -1187,16 +1237,19 @@ TEST_F(IntentUtilTest, FileSystemWebAppURLMatch) {
// Test match with mp3 file extension.
auto intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(system_web_app_test_url("abc.mp3"), "", false));
EXPECT_TRUE(intent->MatchFilter(url_filter));
// Test non-match with mp4 file extension.
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(system_web_app_test_url("abc.mp4"), "", false));
EXPECT_FALSE(intent->MatchFilter(url_filter));
// Test non-match with just the end of a file extension.
intent = std::make_unique<apps::Intent>(
+ apps_util::kIntentActionView,
CreateIntentFiles(system_web_app_test_url("abc.testmp3"), "", false));
EXPECT_FALSE(intent->MatchFilter(url_filter));
}
@@ -1242,27 +1295,26 @@ TEST_F(IntentUtilTest, FileWithTitleText) {
const std::vector<GURL> urls{GURL("abc")};
const std::vector<std::string> mime_types{mime_type};
- auto intent =
- std::make_unique<apps::Intent>(urls, mime_types, "text", "title");
+ auto intent = apps_util::MakeShareIntent(urls, mime_types, "text", "title");
EXPECT_TRUE(intent->share_text.has_value());
EXPECT_EQ(intent->share_text.value(), "text");
EXPECT_TRUE(intent->share_title.has_value());
EXPECT_EQ(intent->share_title.value(), "title");
EXPECT_TRUE(intent->MatchFilter(filter));
- intent = std::make_unique<apps::Intent>(urls, mime_types, "text", "");
+ intent = apps_util::MakeShareIntent(urls, mime_types, "text", "");
EXPECT_TRUE(intent->share_text.has_value());
EXPECT_EQ(intent->share_text.value(), "text");
EXPECT_FALSE(intent->share_title.has_value());
EXPECT_TRUE(intent->MatchFilter(filter));
- intent = std::make_unique<apps::Intent>(urls, mime_types, "", "title");
+ intent = apps_util::MakeShareIntent(urls, mime_types, "", "title");
EXPECT_FALSE(intent->share_text.has_value());
EXPECT_TRUE(intent->share_title.has_value());
EXPECT_EQ(intent->share_title.value(), "title");
EXPECT_TRUE(intent->MatchFilter(filter));
- intent = std::make_unique<apps::Intent>(urls, mime_types, "", "");
+ intent = apps_util::MakeShareIntent(urls, mime_types, "", "");
EXPECT_FALSE(intent->share_text.has_value());
EXPECT_FALSE(intent->share_title.has_value());
EXPECT_TRUE(intent->MatchFilter(filter));
@@ -1294,15 +1346,15 @@ TEST_F(IntentUtilTest, TextMatch) {
auto filter1 = apps_util::MakeIntentFilterForMimeType(mime_type1);
auto filter2 = apps_util::MakeIntentFilterForMimeType(mime_type2);
- auto intent = std::make_unique<apps::Intent>("text", "");
+ auto intent = apps_util::MakeShareIntent("text", "");
EXPECT_TRUE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter2));
- intent = std::make_unique<apps::Intent>("", "title");
+ intent = apps_util::MakeShareIntent("", "title");
EXPECT_TRUE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter2));
- intent = std::make_unique<apps::Intent>("text", "title");
+ intent = apps_util::MakeShareIntent("text", "title");
EXPECT_TRUE(intent->MatchFilter(filter1));
EXPECT_FALSE(intent->MatchFilter(filter2));
}
@@ -1333,9 +1385,9 @@ TEST_F(IntentUtilTest, Convert) {
files.push_back(std::move(file2));
auto src_intent =
- CreateIntent(action, test_url1, mime_type, std::move(files),
- activity_name, test_url3, share_text, share_title,
- start_type, {category1}, data, ui_bypassed, extras);
+ CreateMojomIntent(action, test_url1, mime_type, std::move(files),
+ activity_name, test_url3, share_text, share_title,
+ start_type, {category1}, data, ui_bypassed, extras);
base::Value value = apps_util::ConvertIntentToValue(src_intent);
auto dst_intent = apps_util::ConvertValueToIntent(std::move(value));
@@ -1538,9 +1590,12 @@ TEST_F(IntentUtilTest, IsGenericFileHandler) {
foo_dir->is_directory = true;
intent_files3.push_back(std::move(foo_dir));
- IntentPtr intent = std::make_unique<Intent>(std::move(intent_files));
- IntentPtr intent2 = std::make_unique<Intent>(std::move(intent_files2));
- IntentPtr intent3 = std::make_unique<Intent>(std::move(intent_files3));
+ IntentPtr intent = std::make_unique<Intent>(apps_util::kIntentActionView,
+ std::move(intent_files));
+ IntentPtr intent2 = std::make_unique<Intent>(apps_util::kIntentActionView,
+ std::move(intent_files2));
+ IntentPtr intent3 = std::make_unique<Intent>(apps_util::kIntentActionView,
+ std::move(intent_files3));
const std::string kLabel = "";
@@ -1618,6 +1673,57 @@ TEST_F(IntentUtilTest, IsGenericFileHandler) {
EXPECT_FALSE(filter12->IsFileExtensionsFilter());
}
+TEST_F(IntentUtilTest, CloneIntent) {
+ const std::string action = apps_util::kIntentActionSend;
+ auto src_intent = std::make_unique<apps::Intent>(action);
+ auto dst_intent = src_intent->Clone();
+ EXPECT_EQ(action, dst_intent->action);
+ EXPECT_FALSE(dst_intent->ui_bypassed.has_value());
+
+ GURL test_url1 = GURL("https://www.google.com/");
+ GURL test_url2 = GURL("https://www.abc.com/");
+ GURL test_url3 = GURL("https://www.foo.com/");
+ const std::string mime_type = "image/jpeg";
+ const std::string activity_name = "test";
+ const std::string share_text = "share text";
+ const std::string share_title = "share title";
+ const std::string start_type = "start type";
+ const std::string category1 = "category1";
+ const std::string data = "data";
+ const bool ui_bypassed = true;
+ base::flat_map<std::string, std::string> extras = {
+ {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}};
+
+ auto file1 = std::make_unique<apps::IntentFile>(test_url1);
+ auto file2 = std::make_unique<apps::IntentFile>(test_url2);
+ std::vector<apps::IntentFilePtr> files;
+ files.push_back(std::move(file1));
+ files.push_back(std::move(file2));
+
+ src_intent = CreateIntent(action, test_url1, mime_type, std::move(files),
+ activity_name, test_url3, share_text, share_title,
+ start_type, {category1}, data, ui_bypassed, extras);
+ dst_intent = src_intent->Clone();
+
+ EXPECT_EQ(action, dst_intent->action);
+ EXPECT_EQ(test_url1, dst_intent->url.value());
+ EXPECT_EQ(mime_type, dst_intent->mime_type.value());
+ EXPECT_EQ(2u, dst_intent->files.size());
+ EXPECT_EQ(test_url1, dst_intent->files[0]->url);
+ EXPECT_EQ(test_url2, dst_intent->files[1]->url);
+ EXPECT_EQ(activity_name, dst_intent->activity_name.value());
+ EXPECT_EQ(test_url3, dst_intent->drive_share_url.value());
+ EXPECT_EQ(share_text, dst_intent->share_text.value());
+ EXPECT_EQ(share_title, dst_intent->share_title.value());
+ EXPECT_EQ(start_type, dst_intent->start_type.value());
+ EXPECT_EQ(1u, dst_intent->categories.size());
+ EXPECT_EQ(category1, dst_intent->categories[0]);
+ EXPECT_EQ(data, dst_intent->data.value());
+ EXPECT_EQ(ui_bypassed, dst_intent->ui_bypassed);
+ EXPECT_EQ(3u, dst_intent->extras.size());
+ EXPECT_EQ(extras, dst_intent->extras);
+}
+
// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
TEST_F(IntentUtilTest, MojomConvert) {
const std::string action = apps_util::kIntentActionSend;
@@ -1645,9 +1751,9 @@ TEST_F(IntentUtilTest, MojomConvert) {
files.push_back(std::move(file2));
auto src_intent =
- CreateIntent(action, test_url1, mime_type, std::move(files),
- activity_name, test_url3, share_text, share_title,
- start_type, {category1}, data, ui_bypassed, extras);
+ CreateMojomIntent(action, test_url1, mime_type, std::move(files),
+ activity_name, test_url3, share_text, share_title,
+ start_type, {category1}, data, ui_bypassed, extras);
auto dst_intent = apps::ConvertIntentToMojomIntent(
apps::ConvertMojomIntentToIntent(src_intent));
diff --git a/chromium/components/services/app_service/public/cpp/permission.cc b/chromium/components/services/app_service/public/cpp/permission.cc
index c987e7db79e..fd22820a27d 100644
--- a/chromium/components/services/app_service/public/cpp/permission.cc
+++ b/chromium/components/services/app_service/public/cpp/permission.cc
@@ -208,20 +208,22 @@ PermissionValuePtr ConvertMojomPermissionValueToPermissionValue(
apps::mojom::PermissionValuePtr ConvertPermissionValueToMojomPermissionValue(
const PermissionValuePtr& permission_value) {
- auto mojom_permission_value = apps::mojom::PermissionValue::New();
if (!permission_value) {
- return mojom_permission_value;
+ return nullptr;
}
if (permission_value->bool_value.has_value()) {
- mojom_permission_value->set_bool_value(
+ return apps::mojom::PermissionValue::NewBoolValue(
permission_value->bool_value.value());
}
if (permission_value->tristate_value.has_value()) {
- mojom_permission_value->set_tristate_value(ConvertTriStateToMojomTriState(
- permission_value->tristate_value.value()));
+ return apps::mojom::PermissionValue::NewTristateValue(
+ ConvertTriStateToMojomTriState(
+ permission_value->tristate_value.value()));
}
- return mojom_permission_value;
+
+ NOTREACHED();
+ return nullptr;
}
PermissionPtr ConvertMojomPermissionToPermission(
diff --git a/chromium/components/services/app_service/public/cpp/preferred_app.cc b/chromium/components/services/app_service/public/cpp/preferred_app.cc
new file mode 100644
index 00000000000..c3f22b87e28
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/preferred_app.cc
@@ -0,0 +1,189 @@
+// Copyright 2022 The Chromium 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/services/app_service/public/cpp/preferred_app.h"
+
+namespace apps {
+
+PreferredApp::PreferredApp(IntentFilterPtr intent_filter,
+ const std::string& app_id)
+ : intent_filter(std::move(intent_filter)), app_id(app_id) {}
+
+PreferredApp::~PreferredApp() = default;
+
+bool PreferredApp::operator==(const PreferredApp& other) const {
+ return *intent_filter == *other.intent_filter && app_id == other.app_id;
+}
+
+bool PreferredApp::operator!=(const PreferredApp& other) const {
+ return !(*this == other);
+}
+
+std::unique_ptr<PreferredApp> PreferredApp::Clone() const {
+ return std::make_unique<PreferredApp>(intent_filter->Clone(), app_id);
+}
+
+PreferredAppChanges::PreferredAppChanges() = default;
+
+PreferredAppChanges::~PreferredAppChanges() = default;
+
+PreferredAppChangesPtr PreferredAppChanges::Clone() const {
+ auto preferred_app_changes = std::make_unique<PreferredAppChanges>();
+ for (const auto& added_filter : added_filters) {
+ apps::IntentFilters filters;
+ for (auto& filter : added_filter.second) {
+ filters.push_back(filter->Clone());
+ }
+ preferred_app_changes->added_filters[added_filter.first] =
+ std::move(filters);
+ }
+ for (const auto& removed_filter : removed_filters) {
+ apps::IntentFilters filters;
+ for (auto& filter : removed_filter.second) {
+ filters.push_back(filter->Clone());
+ }
+ preferred_app_changes->removed_filters[removed_filter.first] =
+ std::move(filters);
+ }
+ return preferred_app_changes;
+}
+
+PreferredApps ClonePreferredApps(const PreferredApps& preferred_apps) {
+ PreferredApps ret;
+ ret.reserve(preferred_apps.size());
+ for (const auto& preferred_app : preferred_apps) {
+ ret.push_back(preferred_app->Clone());
+ }
+ return ret;
+}
+
+bool IsEqual(const PreferredApps& source, const PreferredApps& target) {
+ if (source.size() != target.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < static_cast<int>(source.size()); i++) {
+ if (*source[i] != *target[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+PreferredAppPtr ConvertMojomPreferredAppToPreferredApp(
+ const apps::mojom::PreferredAppPtr& mojom_preferred_app) {
+ if (!mojom_preferred_app) {
+ return nullptr;
+ }
+
+ PreferredAppPtr preferred_app =
+ std::make_unique<PreferredApp>(ConvertMojomIntentFilterToIntentFilter(
+ mojom_preferred_app->intent_filter),
+ mojom_preferred_app->app_id);
+ return preferred_app;
+}
+
+apps::mojom::PreferredAppPtr ConvertPreferredAppToMojomPreferredApp(
+ const PreferredAppPtr& preferred_app) {
+ auto mojom_preferred_app = apps::mojom::PreferredApp::New();
+ if (!preferred_app) {
+ return mojom_preferred_app;
+ }
+
+ mojom_preferred_app->intent_filter =
+ ConvertIntentFilterToMojomIntentFilter(preferred_app->intent_filter);
+ mojom_preferred_app->app_id = preferred_app->app_id;
+ return mojom_preferred_app;
+}
+
+PreferredAppChangesPtr ConvertMojomPreferredAppChangesToPreferredAppChanges(
+ const apps::mojom::PreferredAppChangesPtr& mojom_preferred_app_changes) {
+ if (!mojom_preferred_app_changes) {
+ return nullptr;
+ }
+
+ PreferredAppChangesPtr preferred_app_changes =
+ std::make_unique<PreferredAppChanges>();
+ for (const auto& added_filters : mojom_preferred_app_changes->added_filters) {
+ apps::IntentFilters filters;
+ for (auto& filter : added_filters.second) {
+ filters.push_back(ConvertMojomIntentFilterToIntentFilter(filter));
+ }
+ preferred_app_changes->added_filters[added_filters.first] =
+ std::move(filters);
+ }
+ for (const auto& removed_filters :
+ mojom_preferred_app_changes->removed_filters) {
+ apps::IntentFilters filters;
+ for (auto& filter : removed_filters.second) {
+ filters.push_back(ConvertMojomIntentFilterToIntentFilter(filter));
+ }
+ preferred_app_changes->removed_filters[removed_filters.first] =
+ std::move(filters);
+ }
+ return preferred_app_changes;
+}
+
+apps::mojom::PreferredAppChangesPtr
+ConvertPreferredAppChangesToMojomPreferredAppChanges(
+ const PreferredAppChangesPtr& preferred_app_changes) {
+ auto mojom_preferred_app_changes = apps::mojom::PreferredAppChanges::New();
+ if (!preferred_app_changes) {
+ return mojom_preferred_app_changes;
+ }
+
+ for (const auto& added_filters : preferred_app_changes->added_filters) {
+ std::vector<apps::mojom::IntentFilterPtr> mojom_filters;
+ for (auto& filter : added_filters.second) {
+ mojom_filters.push_back(ConvertIntentFilterToMojomIntentFilter(filter));
+ }
+ mojom_preferred_app_changes->added_filters[added_filters.first] =
+ std::move(mojom_filters);
+ }
+ for (const auto& removed_filters : preferred_app_changes->removed_filters) {
+ std::vector<apps::mojom::IntentFilterPtr> mojom_filters;
+ for (auto& filter : removed_filters.second) {
+ mojom_filters.push_back(ConvertIntentFilterToMojomIntentFilter(filter));
+ }
+ mojom_preferred_app_changes->removed_filters[removed_filters.first] =
+ std::move(mojom_filters);
+ }
+ return mojom_preferred_app_changes;
+}
+
+PreferredApps ConvertMojomPreferredAppsToPreferredApps(
+ const std::vector<apps::mojom::PreferredAppPtr>& mojom_preferred_apps) {
+ PreferredApps ret;
+ ret.reserve(mojom_preferred_apps.size());
+ for (const auto& mojom_preferred_app : mojom_preferred_apps) {
+ ret.push_back(ConvertMojomPreferredAppToPreferredApp(mojom_preferred_app));
+ }
+ return ret;
+}
+
+std::vector<apps::mojom::PreferredAppPtr>
+ConvertPreferredAppsToMojomPreferredApps(const PreferredApps& preferred_apps) {
+ std::vector<apps::mojom::PreferredAppPtr> ret;
+ ret.reserve(preferred_apps.size());
+ for (const auto& preferred_app : preferred_apps) {
+ ret.push_back(ConvertPreferredAppToMojomPreferredApp(preferred_app));
+ }
+ return ret;
+}
+
+apps::mojom::ReplacedAppPreferencesPtr
+ConvertReplacedAppPreferencesToMojomReplacedAppPreferences(
+ const ReplacedAppPreferences& replace_preferences) {
+ auto replaced_app_preferences = apps::mojom::ReplacedAppPreferences::New();
+ auto& replaced_preference_map = replaced_app_preferences->replaced_preference;
+ for (const auto& it : replace_preferences) {
+ for (const auto& filter : it.second) {
+ replaced_preference_map[it.first].push_back(
+ ConvertIntentFilterToMojomIntentFilter(filter));
+ }
+ }
+ return replaced_app_preferences;
+}
+
+} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/preferred_app.h b/chromium/components/services/app_service/public/cpp/preferred_app.h
new file mode 100644
index 00000000000..7009f9877d8
--- /dev/null
+++ b/chromium/components/services/app_service/public/cpp/preferred_app.h
@@ -0,0 +1,86 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APP_H_
+#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APP_H_
+
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
+
+namespace apps {
+
+// The preferred app represents by `app_id` for `intent_filter`.
+struct PreferredApp {
+ PreferredApp(IntentFilterPtr intent_filter, const std::string& app_id);
+ PreferredApp(const PreferredApp&) = delete;
+ PreferredApp& operator=(const PreferredApp&) = delete;
+ ~PreferredApp();
+
+ bool operator==(const PreferredApp& other) const;
+ bool operator!=(const PreferredApp& other) const;
+
+ std::unique_ptr<PreferredApp> Clone() const;
+
+ IntentFilterPtr intent_filter;
+ std::string app_id;
+};
+
+using PreferredAppPtr = std::unique_ptr<PreferredApp>;
+using PreferredApps = std::vector<PreferredAppPtr>;
+
+// Represents changes which have been made to the preferred apps list, both
+// adding new filters and removing existing filters.
+struct PreferredAppChanges {
+ PreferredAppChanges();
+ PreferredAppChanges(const PreferredAppChanges&) = delete;
+ PreferredAppChanges& operator=(const PreferredAppChanges&) = delete;
+ ~PreferredAppChanges();
+
+ std::unique_ptr<PreferredAppChanges> Clone() const;
+
+ base::flat_map<std::string, IntentFilters> added_filters;
+ base::flat_map<std::string, IntentFilters> removed_filters;
+};
+
+using PreferredAppChangesPtr = std::unique_ptr<PreferredAppChanges>;
+
+// Represents a group of `app_ids` that is no longer preferred app of their
+// corresponding `intent_filters`.
+using ReplacedAppPreferences = base::flat_map<std::string, IntentFilters>;
+
+// Creates a deep copy of `preferred_apps`.
+PreferredApps ClonePreferredApps(const PreferredApps& preferred_apps);
+
+bool IsEqual(const PreferredApps& source, const PreferredApps& target);
+
+// TODO(crbug.com/1253250): Remove these functions after migrating to non-mojo
+// AppService.
+PreferredAppPtr ConvertMojomPreferredAppToPreferredApp(
+ const apps::mojom::PreferredAppPtr& mojom_preferred_app);
+
+apps::mojom::PreferredAppPtr ConvertPreferredAppToMojomPreferredApp(
+ const PreferredAppPtr& preferred_app);
+
+PreferredAppChangesPtr ConvertMojomPreferredAppChangesToPreferredAppChanges(
+ const apps::mojom::PreferredAppChangesPtr& mojom_preferred_app_changes);
+
+apps::mojom::PreferredAppChangesPtr
+ConvertPreferredAppChangesToMojomPreferredAppChanges(
+ const PreferredAppChangesPtr& preferred_app_changes);
+
+PreferredApps ConvertMojomPreferredAppsToPreferredApps(
+ const std::vector<apps::mojom::PreferredAppPtr>& mojom_preferred_apps);
+
+std::vector<apps::mojom::PreferredAppPtr>
+ConvertPreferredAppsToMojomPreferredApps(const PreferredApps& preferred_apps);
+
+apps::mojom::ReplacedAppPreferencesPtr
+ConvertReplacedAppPreferencesToMojomReplacedAppPreferences(
+ const ReplacedAppPreferences& replace_preferences);
+
+} // namespace apps
+
+#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APP_H_
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc b/chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc
index e83d2165923..2a35100964b 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_converter.cc
@@ -5,9 +5,9 @@
#include <memory>
#include <utility>
+#include "components/services/app_service/public/cpp/intent_filter.h"
#include "components/services/app_service/public/cpp/intent_filter_util.h"
#include "components/services/app_service/public/cpp/preferred_apps_converter.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
namespace {
@@ -15,7 +15,7 @@ constexpr int kVersionInitial = 0;
constexpr int kVersionSupportsSharing = 1;
base::Value ConvertConditionValueToValue(
- const apps::mojom::ConditionValuePtr& condition_value) {
+ const apps::ConditionValuePtr& condition_value) {
base::Value condition_value_dict(base::Value::Type::DICTIONARY);
condition_value_dict.SetStringKey(apps::kValueKey, condition_value->value);
condition_value_dict.SetIntKey(apps::kMatchTypeKey,
@@ -23,8 +23,7 @@ base::Value ConvertConditionValueToValue(
return condition_value_dict;
}
-base::Value ConvertConditionToValue(
- const apps::mojom::ConditionPtr& condition) {
+base::Value ConvertConditionToValue(const apps::ConditionPtr& condition) {
base::Value condition_dict(base::Value::Type::DICTIONARY);
condition_dict.SetIntKey(apps::kConditionTypeKey,
static_cast<int>(condition->condition_type));
@@ -38,7 +37,7 @@ base::Value ConvertConditionToValue(
}
base::Value ConvertIntentFilterToValue(
- const apps::mojom::IntentFilterPtr& intent_filter) {
+ const apps::IntentFilterPtr& intent_filter) {
base::Value intent_filter_value(base::Value::Type::LIST);
for (auto& condition : intent_filter->conditions) {
intent_filter_value.Append(ConvertConditionToValue(condition));
@@ -46,62 +45,60 @@ base::Value ConvertIntentFilterToValue(
return intent_filter_value;
}
-apps::mojom::ConditionValuePtr ParseValueToConditionValue(
- const base::Value& value) {
+apps::ConditionValuePtr ParseValueToConditionValue(const base::Value& value) {
auto* value_string = value.FindStringKey(apps::kValueKey);
if (!value_string) {
DVLOG(0) << "Fail to parse condition value. Cannot find \""
<< apps::kValueKey << "\" key with string value.";
return nullptr;
}
- auto condition_value = apps::mojom::ConditionValue::New();
- condition_value->value = *value_string;
+
auto match_type = value.FindIntKey(apps::kMatchTypeKey);
if (!match_type.has_value()) {
DVLOG(0) << "Fail to parse condition value. Cannot find \""
<< apps::kMatchTypeKey << "\" key with int value.";
return nullptr;
}
- condition_value->match_type =
- static_cast<apps::mojom::PatternMatchType>(match_type.value());
- return condition_value;
+
+ return std::make_unique<apps::ConditionValue>(
+ *value_string, static_cast<apps::PatternMatchType>(match_type.value()));
}
-apps::mojom::ConditionPtr ParseValueToCondition(const base::Value& value) {
+apps::ConditionPtr ParseValueToCondition(const base::Value& value) {
auto condition_type = value.FindIntKey(apps::kConditionTypeKey);
if (!condition_type.has_value()) {
DVLOG(0) << "Fail to parse condition. Cannot find \""
<< apps::kConditionTypeKey << "\" key with int value.";
return nullptr;
}
- auto condition = apps::mojom::Condition::New();
- condition->condition_type =
- static_cast<apps::mojom::ConditionType>(condition_type.value());
- auto* condition_values = value.FindKey(apps::kConditionValuesKey);
- if (!condition_values || !condition_values->is_list()) {
+ apps::ConditionValues condition_values;
+ auto* values = value.FindKey(apps::kConditionValuesKey);
+ if (!values || !values->is_list()) {
DVLOG(0) << "Fail to parse condition. Cannot find \""
<< apps::kConditionValuesKey << "\" key with list value.";
return nullptr;
}
- for (auto& condition_value : condition_values->GetListDeprecated()) {
+ for (auto& condition_value : values->GetListDeprecated()) {
auto parsed_condition_value = ParseValueToConditionValue(condition_value);
if (!parsed_condition_value) {
DVLOG(0) << "Fail to parse condition. Cannot parse condition values";
return nullptr;
}
- condition->condition_values.push_back(std::move(parsed_condition_value));
+ condition_values.push_back(std::move(parsed_condition_value));
}
- return condition;
+
+ return std::make_unique<apps::Condition>(
+ static_cast<apps::ConditionType>(condition_type.value()),
+ std::move(condition_values));
}
-apps::mojom::IntentFilterPtr ParseValueToIntentFilter(
- const base::Value* value) {
+apps::IntentFilterPtr ParseValueToIntentFilter(const base::Value* value) {
if (!value || !value->is_list()) {
DVLOG(0) << "Fail to parse intent filter. Cannot find the conditions list.";
return nullptr;
}
- auto intent_filter = apps::mojom::IntentFilter::New();
+ auto intent_filter = std::make_unique<apps::IntentFilter>();
for (auto& condition : value->GetListDeprecated()) {
auto parsed_condition = ParseValueToCondition(condition);
if (!parsed_condition) {
@@ -126,8 +123,7 @@ const char kIntentFilterKey[] = "intent_filter";
const char kPreferredAppsKey[] = "preferred_apps";
const char kVersionKey[] = "version";
-base::Value ConvertPreferredAppsToValue(
- const PreferredAppsList::PreferredApps& preferred_apps) {
+base::Value ConvertPreferredAppsToValue(const PreferredApps& preferred_apps) {
base::Value preferred_apps_value(base::Value::Type::DICTIONARY);
int version = kVersionSupportsSharing;
preferred_apps_value.SetIntKey(kVersionKey, version);
@@ -145,7 +141,7 @@ base::Value ConvertPreferredAppsToValue(
return preferred_apps_value;
}
-PreferredAppsList::PreferredApps ParseValueToPreferredApps(
+PreferredApps ParseValueToPreferredApps(
const base::Value& preferred_apps_value) {
const base::Value* preferred_apps_list = nullptr;
if (preferred_apps_value.is_list()) {
@@ -156,31 +152,31 @@ PreferredAppsList::PreferredApps ParseValueToPreferredApps(
if (!preferred_apps_list || !preferred_apps_list->is_list()) {
DVLOG(0)
<< "Fail to parse preferred apps. Cannot find the preferred app list.";
- return PreferredAppsList::PreferredApps();
+ return PreferredApps();
}
- PreferredAppsList::PreferredApps preferred_apps;
+ PreferredApps preferred_apps;
for (auto& entry : preferred_apps_list->GetListDeprecated()) {
auto* app_id = entry.FindStringKey(kAppIdKey);
if (!app_id) {
DVLOG(0) << "Fail to parse condition value. Cannot find \""
<< apps::kAppIdKey << "\" key with string value.";
- return PreferredAppsList::PreferredApps();
+ return PreferredApps();
}
auto parsed_intent_filter =
ParseValueToIntentFilter(entry.FindKey(kIntentFilterKey));
if (!parsed_intent_filter) {
DVLOG(0) << "Fail to parse condition value. Cannot parse intent filter.";
- return PreferredAppsList::PreferredApps();
+ return PreferredApps();
}
// Do not show other browser apps when the user is already using this
// browser (matches Android behaviour).
- if (apps_util::IsBrowserFilter(parsed_intent_filter)) {
+ if (parsed_intent_filter->IsBrowserFilter()) {
continue;
}
- auto new_preferred_app = apps::mojom::PreferredApp::New(
+ auto new_preferred_app = std::make_unique<PreferredApp>(
std::move(parsed_intent_filter), *app_id);
preferred_apps.push_back(std::move(new_preferred_app));
}
@@ -188,9 +184,9 @@ PreferredAppsList::PreferredApps ParseValueToPreferredApps(
return preferred_apps;
}
-void UpgradePreferredApps(PreferredAppsList::PreferredApps& preferred_apps) {
+void UpgradePreferredApps(PreferredApps& preferred_apps) {
for (auto& preferred_app : preferred_apps) {
- if (apps_util::FilterNeedsUpgrade(preferred_app->intent_filter)) {
+ if (preferred_app->intent_filter->FilterNeedsUpgrade()) {
apps_util::UpgradeFilter(preferred_app->intent_filter);
}
}
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps_converter.h b/chromium/components/services/app_service/public/cpp/preferred_apps_converter.h
index 534fd7fd34f..0aca4089674 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps_converter.h
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_converter.h
@@ -6,7 +6,7 @@
#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APPS_CONVERTER_H_
#include "base/values.h"
-#include "components/services/app_service/public/cpp/preferred_apps_list.h"
+#include "components/services/app_service/public/cpp/preferred_app.h"
namespace apps {
@@ -45,15 +45,14 @@ extern const char kVersionKey[];
// } ]
// } ],
// "version": 0}
-base::Value ConvertPreferredAppsToValue(
- const PreferredAppsList::PreferredApps& preferred_apps);
+base::Value ConvertPreferredAppsToValue(const PreferredApps& preferred_apps);
// Parse the base::Value read from JSON file back to preferred apps struct.
-PreferredAppsList::PreferredApps ParseValueToPreferredApps(
+PreferredApps ParseValueToPreferredApps(
const base::Value& preferred_apps_value);
// Upgrade the preferred apps struct to contain action in the filters.
-void UpgradePreferredApps(PreferredAppsList::PreferredApps& preferred_apps);
+void UpgradePreferredApps(PreferredApps& preferred_apps);
// Check if the preferred apps file already upgraded for supporting sharing.
bool IsUpgradedForSharing(const base::Value& preferred_apps_value);
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc b/chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc
index b40d83862db..4287c959249 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc
@@ -8,6 +8,7 @@
#include "components/services/app_service/public/cpp/intent_filter_util.h"
#include "components/services/app_service/public/cpp/intent_test_util.h"
#include "components/services/app_service/public/cpp/intent_util.h"
+#include "components/services/app_service/public/cpp/preferred_app.h"
#include "components/services/app_service/public/cpp/preferred_apps_list.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -22,7 +23,7 @@ class PreferredAppsConverterTest : public testing::Test {};
// Test one simple entry with simple filter.
TEST_F(PreferredAppsConverterTest, ConvertSimpleEntry) {
GURL filter_url = GURL("https://www.google.com/abc");
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
apps::PreferredAppsList preferred_apps;
preferred_apps.Init();
@@ -58,10 +59,9 @@ TEST_F(PreferredAppsConverterTest, ConvertSimpleEntry) {
converted_condition_values[0].FindIntKey(apps::kMatchTypeKey));
}
- auto preferred_apps_list = apps::ParseValueToPreferredApps(converted_value);
preferred_apps.Init();
EXPECT_EQ(absl::nullopt, preferred_apps.FindPreferredAppForUrl(filter_url));
- preferred_apps.Init(preferred_apps_list);
+ preferred_apps.Init(apps::ParseValueToPreferredApps(converted_value));
EXPECT_EQ(kAppId1, preferred_apps.FindPreferredAppForUrl(filter_url));
GURL url_wrong_host = GURL("https://www.hahaha.com/");
EXPECT_EQ(absl::nullopt,
@@ -71,7 +71,7 @@ TEST_F(PreferredAppsConverterTest, ConvertSimpleEntry) {
// Test one upgraded simple entry with json string.
TEST_F(PreferredAppsConverterTest, ConvertUpgradedSimpleEntryJson) {
GURL filter_url = GURL("https://www.google.com/abc");
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
apps::PreferredAppsList preferred_apps;
preferred_apps.Init();
@@ -144,14 +144,14 @@ TEST_F(PreferredAppsConverterTest, ParseSimpleEntryJson) {
EXPECT_FALSE(apps::IsUpgradedForSharing(test_value.value()));
GURL filter_url = GURL("https://www.google.com/abc");
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
intent_filter->conditions.erase(intent_filter->conditions.begin());
apps::PreferredAppsList preferred_apps;
preferred_apps.Init();
preferred_apps.AddPreferredApp(kAppId1, intent_filter);
auto& expected_entry = preferred_apps.GetReference();
- EXPECT_EQ(expected_entry, parsed_entry);
+ EXPECT_TRUE(IsEqual(expected_entry, parsed_entry));
}
// Test parse simple entry from json string (upgraded for sharing).
@@ -191,14 +191,14 @@ TEST_F(PreferredAppsConverterTest, ParseUpgradedSimpleEntryJson) {
EXPECT_TRUE(apps::IsUpgradedForSharing(test_value.value()));
GURL filter_url = GURL("https://www.google.com/abc");
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
apps::PreferredAppsList preferred_apps;
preferred_apps.Init();
preferred_apps.AddPreferredApp(kAppId1, intent_filter);
auto& expected_entry = preferred_apps.GetReference();
- EXPECT_EQ(expected_entry, parsed_entry);
+ EXPECT_TRUE(IsEqual(expected_entry, parsed_entry));
}
TEST_F(PreferredAppsConverterTest, ParseJsonWithInvalidAppId) {
@@ -531,13 +531,13 @@ TEST_F(PreferredAppsConverterTest, ParseJsonWithInvalidValue) {
TEST_F(PreferredAppsConverterTest, UpgradePreferredApp) {
// Create preferred app with old filter.
GURL filter_url = GURL("https://www.google.com/abc");
- auto old_intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto old_intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
apps::PreferredAppsList old_preferred_apps;
old_preferred_apps.Init();
old_preferred_apps.AddPreferredApp(kAppId1, old_intent_filter);
- auto new_intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto new_intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
apps::PreferredAppsList new_preferred_apps;
new_preferred_apps.Init();
@@ -545,5 +545,6 @@ TEST_F(PreferredAppsConverterTest, UpgradePreferredApp) {
auto old_preferred_apps_value = old_preferred_apps.GetValue();
apps::UpgradePreferredApps(old_preferred_apps_value);
- EXPECT_EQ(old_preferred_apps_value, new_preferred_apps.GetReference());
+ EXPECT_TRUE(
+ IsEqual(old_preferred_apps_value, new_preferred_apps.GetReference()));
}
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps.cc b/chromium/components/services/app_service/public/cpp/preferred_apps_impl.cc
index 0ba48bc1c94..4f4ee3bb712 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps.cc
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/services/app_service/public/cpp/preferred_apps.h"
+#include "components/services/app_service/public/cpp/preferred_apps_impl.h"
#include <iterator>
#include <utility>
@@ -77,10 +77,11 @@ std::string ReadDataBlocking(const base::FilePath& preferred_apps_file) {
namespace apps {
-PreferredApps::PreferredApps(Host* host,
- const base::FilePath& profile_dir,
- base::OnceClosure read_completed_for_testing,
- base::OnceClosure write_completed_for_testing)
+PreferredAppsImpl::PreferredAppsImpl(
+ Host* host,
+ const base::FilePath& profile_dir,
+ base::OnceClosure read_completed_for_testing,
+ base::OnceClosure write_completed_for_testing)
: host_(host),
profile_dir_(profile_dir),
task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
@@ -92,60 +93,50 @@ PreferredApps::PreferredApps(Host* host,
InitializePreferredApps();
}
-PreferredApps::~PreferredApps() = default;
+PreferredAppsImpl::~PreferredAppsImpl() = default;
-void PreferredApps::AddPreferredApp(apps::mojom::AppType app_type,
- const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter,
- apps::mojom::IntentPtr intent,
- bool from_publisher) {
+void PreferredAppsImpl::AddPreferredApp(AppType app_type,
+ const std::string& app_id,
+ IntentFilterPtr intent_filter,
+ IntentPtr intent,
+ bool from_publisher) {
RunAfterPreferredAppsReady(base::BindOnce(
- &PreferredApps::AddPreferredAppImpl, weak_ptr_factory_.GetWeakPtr(),
+ &PreferredAppsImpl::AddPreferredAppImpl, weak_ptr_factory_.GetWeakPtr(),
app_type, app_id, std::move(intent_filter), std::move(intent),
from_publisher));
}
-void PreferredApps::RemovePreferredApp(apps::mojom::AppType app_type,
- const std::string& app_id) {
+void PreferredAppsImpl::RemovePreferredApp(const std::string& app_id) {
RunAfterPreferredAppsReady(
- base::BindOnce(&PreferredApps::RemovePreferredAppImpl,
- weak_ptr_factory_.GetWeakPtr(), app_type, app_id));
+ base::BindOnce(&PreferredAppsImpl::RemovePreferredAppImpl,
+ weak_ptr_factory_.GetWeakPtr(), app_id));
}
-void PreferredApps::RemovePreferredAppForFilter(
- apps::mojom::AppType app_type,
+void PreferredAppsImpl::SetSupportedLinksPreference(
+ AppType app_type,
const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter) {
+ IntentFilters all_link_filters) {
RunAfterPreferredAppsReady(
- base::BindOnce(&PreferredApps::RemovePreferredAppForFilterImpl,
- weak_ptr_factory_.GetWeakPtr(), app_type, app_id,
- std::move(intent_filter)));
-}
-
-void PreferredApps::SetSupportedLinksPreference(
- apps::mojom::AppType app_type,
- const std::string& app_id,
- std::vector<apps::mojom::IntentFilterPtr> all_link_filters) {
- RunAfterPreferredAppsReady(
- base::BindOnce(&PreferredApps::SetSupportedLinksPreferenceImpl,
+ base::BindOnce(&PreferredAppsImpl::SetSupportedLinksPreferenceImpl,
weak_ptr_factory_.GetWeakPtr(), app_type, app_id,
std::move(all_link_filters)));
}
-void PreferredApps::RemoveSupportedLinksPreference(
- apps::mojom::AppType app_type,
+void PreferredAppsImpl::RemoveSupportedLinksPreference(
+ AppType app_type,
const std::string& app_id) {
RunAfterPreferredAppsReady(
- base::BindOnce(&PreferredApps::RemoveSupportedLinksPreferenceImpl,
+ base::BindOnce(&PreferredAppsImpl::RemoveSupportedLinksPreferenceImpl,
weak_ptr_factory_.GetWeakPtr(), app_type, app_id));
}
-void PreferredApps::InitializePreferredApps() {
+void PreferredAppsImpl::InitializePreferredApps() {
ReadFromJSON(profile_dir_);
}
-void PreferredApps::WriteToJSON(const base::FilePath& profile_dir,
- const apps::PreferredAppsList& preferred_apps) {
+void PreferredAppsImpl::WriteToJSON(
+ const base::FilePath& profile_dir,
+ const apps::PreferredAppsList& preferred_apps) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// If currently is writing preferred apps to file, set a flag to write after
// the current write completed.
@@ -167,11 +158,11 @@ void PreferredApps::WriteToJSON(const base::FilePath& profile_dir,
FROM_HERE,
base::BindOnce(&WriteDataBlocking,
profile_dir.Append(kPreferredAppsDirname), json_string),
- base::BindOnce(&PreferredApps::WriteCompleted,
+ base::BindOnce(&PreferredAppsImpl::WriteCompleted,
weak_ptr_factory_.GetWeakPtr()));
}
-void PreferredApps::WriteCompleted() {
+void PreferredAppsImpl::WriteCompleted() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
writing_preferred_apps_ = false;
if (!should_write_preferred_apps_to_file_) {
@@ -187,17 +178,17 @@ void PreferredApps::WriteCompleted() {
WriteToJSON(profile_dir_, preferred_apps_list_);
}
-void PreferredApps::ReadFromJSON(const base::FilePath& profile_dir) {
+void PreferredAppsImpl::ReadFromJSON(const base::FilePath& profile_dir) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&ReadDataBlocking,
profile_dir.Append(kPreferredAppsDirname)),
- base::BindOnce(&PreferredApps::ReadCompleted,
+ base::BindOnce(&PreferredAppsImpl::ReadCompleted,
weak_ptr_factory_.GetWeakPtr()));
}
-void PreferredApps::ReadCompleted(std::string preferred_apps_string) {
+void PreferredAppsImpl::ReadCompleted(std::string preferred_apps_string) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
bool preferred_apps_upgraded = false;
if (preferred_apps_string.empty()) {
@@ -221,7 +212,7 @@ void PreferredApps::ReadCompleted(std::string preferred_apps_string) {
if (!preferred_apps_upgraded) {
UpgradePreferredApps(preferred_apps);
}
- preferred_apps_list_.Init(preferred_apps);
+ preferred_apps_list_.Init(std::move(preferred_apps));
}
}
if (!preferred_apps_upgraded) {
@@ -242,7 +233,7 @@ void PreferredApps::ReadCompleted(std::string preferred_apps_string) {
}
}
-void PreferredApps::RunAfterPreferredAppsReady(base::OnceClosure task) {
+void PreferredAppsImpl::RunAfterPreferredAppsReady(base::OnceClosure task) {
if (preferred_apps_list_.IsInitialized()) {
std::move(task).Run();
} else {
@@ -250,12 +241,11 @@ void PreferredApps::RunAfterPreferredAppsReady(base::OnceClosure task) {
}
}
-void PreferredApps::AddPreferredAppImpl(
- apps::mojom::AppType app_type,
- const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter,
- apps::mojom::IntentPtr intent,
- bool from_publisher) {
+void PreferredAppsImpl::AddPreferredAppImpl(AppType app_type,
+ const std::string& app_id,
+ IntentFilterPtr intent_filter,
+ IntentPtr intent,
+ bool from_publisher) {
// TODO(https://crbug.com/853604): Remove this and convert to a DCHECK
// after finding out the root cause.
if (app_id.empty()) {
@@ -263,15 +253,14 @@ void PreferredApps::AddPreferredAppImpl(
return;
}
- apps::mojom::ReplacedAppPreferencesPtr replaced_apps =
+ auto replaced_apps =
preferred_apps_list_.AddPreferredApp(app_id, intent_filter);
WriteToJSON(profile_dir_, preferred_apps_list_);
- auto changes = apps::mojom::PreferredAppChanges::New();
-
+ auto changes = std::make_unique<PreferredAppChanges>();
changes->added_filters[app_id].push_back(intent_filter->Clone());
- changes->removed_filters = Clone(replaced_apps->replaced_preference);
+ changes->removed_filters = CloneIntentFiltersMap(replaced_apps);
host_->OnPreferredAppsChanged(std::move(changes));
if (from_publisher || !intent) {
@@ -281,53 +270,34 @@ void PreferredApps::AddPreferredAppImpl(
// Sync the change to publishers. Because |replaced_app_preference| can
// be any app type, we should run this for all publishers. Currently
// only implemented in ARC publisher.
- // TODO(crbug.com/853604): The |replaced_app_preference| can be really big,
+ // TODO(crbug.com/1322000): The |replaced_app_preference| can be really big,
// update this logic to only call the relevant publisher for each app after
// updating the storage structure.
host_->OnPreferredAppSet(app_id, std::move(intent_filter), std::move(intent),
std::move(replaced_apps));
}
-void PreferredApps::RemovePreferredAppImpl(apps::mojom::AppType app_type,
- const std::string& app_id) {
- std::vector<apps::mojom::IntentFilterPtr> removed_filters =
- preferred_apps_list_.DeleteAppId(app_id);
- if (!removed_filters.empty()) {
- WriteToJSON(profile_dir_, preferred_apps_list_);
-
- auto changes = apps::mojom::PreferredAppChanges::New();
- changes->removed_filters.emplace(app_id, std::move(removed_filters));
- host_->OnPreferredAppsChanged(std::move(changes));
- }
-}
-
-void PreferredApps::RemovePreferredAppForFilterImpl(
- apps::mojom::AppType app_type,
- const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter) {
- std::vector<apps::mojom::IntentFilterPtr> removed_filters =
- preferred_apps_list_.DeletePreferredApp(app_id, intent_filter);
-
+void PreferredAppsImpl::RemovePreferredAppImpl(const std::string& app_id) {
+ IntentFilters removed_filters = preferred_apps_list_.DeleteAppId(app_id);
if (!removed_filters.empty()) {
WriteToJSON(profile_dir_, preferred_apps_list_);
- auto changes = apps::mojom::PreferredAppChanges::New();
- changes->removed_filters.emplace(app_id, std::move(removed_filters));
+ auto changes = std::make_unique<PreferredAppChanges>();
+ changes->removed_filters[app_id] = std::move(removed_filters);
host_->OnPreferredAppsChanged(std::move(changes));
}
}
-void PreferredApps::SetSupportedLinksPreferenceImpl(
- apps::mojom::AppType app_type,
+void PreferredAppsImpl::SetSupportedLinksPreferenceImpl(
+ AppType app_type,
const std::string& app_id,
- std::vector<apps::mojom::IntentFilterPtr> all_link_filters) {
- auto changes = apps::mojom::PreferredAppChanges::New();
+ IntentFilters all_link_filters) {
+ auto changes = std::make_unique<PreferredAppChanges>();
auto& added = changes->added_filters;
auto& removed = changes->removed_filters;
for (auto& filter : all_link_filters) {
- apps::mojom::ReplacedAppPreferencesPtr replaced_apps =
- preferred_apps_list_.AddPreferredApp(app_id, filter);
+ auto replaced_apps = preferred_apps_list_.AddPreferredApp(app_id, filter);
added[app_id].push_back(std::move(filter));
// If we removed overlapping supported links when adding the new app, those
@@ -335,7 +305,7 @@ void PreferredApps::SetSupportedLinksPreferenceImpl(
// need to have all their other Supported Links filters removed.
// Additionally, track all removals in the |removed| map so that subscribers
// can be notified correctly.
- for (auto& replaced_app_and_filters : replaced_apps->replaced_preference) {
+ for (auto& replaced_app_and_filters : replaced_apps) {
const std::string& removed_app_id = replaced_app_and_filters.first;
bool first_removal_for_app = !base::Contains(removed, app_id);
bool did_replace_supported_link = base::ranges::any_of(
@@ -344,8 +314,7 @@ void PreferredApps::SetSupportedLinksPreferenceImpl(
return apps_util::IsSupportedLinkForApp(removed_app_id, filter);
});
- std::vector<apps::mojom::IntentFilterPtr>& removed_filters_for_app =
- removed[removed_app_id];
+ IntentFilters& removed_filters_for_app = removed[removed_app_id];
removed_filters_for_app.insert(
removed_filters_for_app.end(),
std::make_move_iterator(replaced_app_and_filters.second.begin()),
@@ -353,7 +322,7 @@ void PreferredApps::SetSupportedLinksPreferenceImpl(
// We only need to remove other supported links once per app.
if (first_removal_for_app && did_replace_supported_link) {
- std::vector<apps::mojom::IntentFilterPtr> removed_filters =
+ IntentFilters removed_filters =
preferred_apps_list_.DeleteSupportedLinks(removed_app_id);
removed_filters_for_app.insert(
removed_filters_for_app.end(),
@@ -369,39 +338,38 @@ void PreferredApps::SetSupportedLinksPreferenceImpl(
// Notify publishers: The new app has been set to open links, and all removed
// apps no longer handle links.
- auto* publisher = host_->GetPublisher(app_type);
- if (publisher) {
- publisher->OnSupportedLinksPreferenceChanged(app_id,
- /*open_in_app=*/true);
+ if (host_->HasPublisher(app_type)) {
+ host_->OnSupportedLinksPreferenceChanged(app_type, app_id,
+ /*open_in_app=*/true);
}
for (const auto& removed_app_and_filters : removed) {
// We don't know what app type the app is, so we have to notify all
// publishers.
+ // TODO(crbug.com/1322000): Only notify the relevant publishers.
host_->OnSupportedLinksPreferenceChanged(removed_app_and_filters.first,
/*open_in_app=*/false);
}
}
-void PreferredApps::RemoveSupportedLinksPreferenceImpl(
- apps::mojom::AppType app_type,
+void PreferredAppsImpl::RemoveSupportedLinksPreferenceImpl(
+ AppType app_type,
const std::string& app_id) {
- auto* publisher = host_->GetPublisher(app_type);
- if (!publisher) {
+ if (!host_->HasPublisher(app_type)) {
return;
}
- std::vector<apps::mojom::IntentFilterPtr> removed_filters =
+ IntentFilters removed_filters =
preferred_apps_list_.DeleteSupportedLinks(app_id);
if (!removed_filters.empty()) {
WriteToJSON(profile_dir_, preferred_apps_list_);
- auto changes = apps::mojom::PreferredAppChanges::New();
- changes->removed_filters.emplace(app_id, std::move(removed_filters));
+ auto changes = std::make_unique<PreferredAppChanges>();
+ changes->removed_filters[app_id] = std::move(removed_filters);
host_->OnPreferredAppsChanged(std::move(changes));
}
- publisher->OnSupportedLinksPreferenceChanged(app_id,
- /*open_in_app=*/false);
+ host_->OnSupportedLinksPreferenceChanged(app_type, app_id,
+ /*open_in_app=*/false);
}
} // namespace apps
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps.h b/chromium/components/services/app_service/public/cpp/preferred_apps_impl.h
index 6b59de817f3..9ff41649f15 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps.h
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_impl.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APPS_H_
-#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APPS_H_
+#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APPS_IMPL_H_
+#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APPS_IMPL_H_
#include <map>
@@ -14,16 +14,18 @@
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/intent.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
+#include "components/services/app_service/public/cpp/preferred_app.h"
#include "components/services/app_service/public/cpp/preferred_apps_list.h"
-#include "components/services/app_service/public/mojom/app_service.mojom.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
namespace apps {
class AppServiceMojomImpl;
// The implementation of the preferred apps to manage the PreferredAppsList.
-class PreferredApps {
+class PreferredAppsImpl {
public:
class Host {
public:
@@ -34,52 +36,53 @@ class PreferredApps {
virtual void InitializePreferredAppsForAllSubscribers() = 0;
- virtual void OnPreferredAppsChanged(
- apps::mojom::PreferredAppChangesPtr changes) = 0;
+ virtual void OnPreferredAppsChanged(PreferredAppChangesPtr changes) = 0;
virtual void OnPreferredAppSet(
const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter,
- apps::mojom::IntentPtr intent,
- apps::mojom::ReplacedAppPreferencesPtr replaced_app_preferences) = 0;
+ IntentFilterPtr intent_filter,
+ IntentPtr intent,
+ ReplacedAppPreferences replaced_app_preferences) = 0;
virtual void OnSupportedLinksPreferenceChanged(const std::string& app_id,
bool open_in_app) = 0;
- // Returns publisher for `app_type`, or nullptr if there is no publisher for
- // `app_type`.
- virtual apps::mojom::Publisher* GetPublisher(
- apps::mojom::AppType app_type) = 0;
+ virtual void OnSupportedLinksPreferenceChanged(AppType app_type,
+ const std::string& app_id,
+ bool open_in_app) = 0;
+
+ // Returns true if there is a publisher for `app_type`. Otherwise, returns
+ // false.
+ virtual bool HasPublisher(AppType app_type) = 0;
};
- PreferredApps(
+ PreferredAppsImpl(
Host* host,
const base::FilePath& profile_dir,
base::OnceClosure read_completed_for_testing = base::OnceClosure(),
base::OnceClosure write_completed_for_testing = base::OnceClosure());
- PreferredApps(const PreferredApps&) = delete;
- PreferredApps& operator=(const PreferredApps&) = delete;
+ PreferredAppsImpl(const PreferredAppsImpl&) = delete;
+ PreferredAppsImpl& operator=(const PreferredAppsImpl&) = delete;
- ~PreferredApps();
+ ~PreferredAppsImpl();
- void AddPreferredApp(apps::mojom::AppType app_type,
+ void AddPreferredApp(AppType app_type,
const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter,
- apps::mojom::IntentPtr intent,
+ IntentFilterPtr intent_filter,
+ IntentPtr intent,
bool from_publisher);
- void RemovePreferredApp(apps::mojom::AppType app_type,
- const std::string& app_id);
- void RemovePreferredAppForFilter(apps::mojom::AppType app_type,
+ void RemovePreferredApp(const std::string& app_id);
+ void SetSupportedLinksPreference(AppType app_type,
const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter);
- void SetSupportedLinksPreference(
- apps::mojom::AppType app_type,
- const std::string& app_id,
- std::vector<apps::mojom::IntentFilterPtr> all_link_filters);
- void RemoveSupportedLinksPreference(apps::mojom::AppType app_type,
+ IntentFilters all_link_filters);
+ void RemoveSupportedLinksPreference(AppType app_type,
const std::string& app_id);
+ const PreferredAppsList& preferred_apps_list() const {
+ return preferred_apps_list_;
+ }
+
private:
friend AppServiceMojomImpl;
@@ -100,22 +103,16 @@ class PreferredApps {
// be run immediately if preferred apps are already initialized.
void RunAfterPreferredAppsReady(base::OnceClosure task);
- void AddPreferredAppImpl(apps::mojom::AppType app_type,
+ void AddPreferredAppImpl(AppType app_type,
const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter,
- apps::mojom::IntentPtr intent,
+ IntentFilterPtr intent_filter,
+ IntentPtr intent,
bool from_publisher);
- void RemovePreferredAppImpl(apps::mojom::AppType app_type,
- const std::string& app_id);
- void RemovePreferredAppForFilterImpl(
- apps::mojom::AppType app_type,
- const std::string& app_id,
- apps::mojom::IntentFilterPtr intent_filter);
- void SetSupportedLinksPreferenceImpl(
- apps::mojom::AppType app_type,
- const std::string& app_id,
- std::vector<apps::mojom::IntentFilterPtr> all_link_filters);
- void RemoveSupportedLinksPreferenceImpl(apps::mojom::AppType app_type,
+ void RemovePreferredAppImpl(const std::string& app_id);
+ void SetSupportedLinksPreferenceImpl(AppType app_type,
+ const std::string& app_id,
+ IntentFilters all_link_filters);
+ void RemoveSupportedLinksPreferenceImpl(AppType app_type,
const std::string& app_id);
// `host_` owns `this`.
@@ -142,9 +139,9 @@ class PreferredApps {
base::queue<base::OnceClosure> pending_preferred_apps_tasks_;
- base::WeakPtrFactory<PreferredApps> weak_ptr_factory_{this};
+ base::WeakPtrFactory<PreferredAppsImpl> weak_ptr_factory_{this};
};
} // namespace apps
-#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APPS_H_
+#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PREFERRED_APPS_IMPL_H_
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps_list.cc b/chromium/components/services/app_service/public/cpp/preferred_apps_list.cc
index 0ced6cbf37f..dac2cc96dff 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps_list.cc
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_list.cc
@@ -10,23 +10,10 @@
#include "base/observer_list.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
-#include "components/services/app_service/public/cpp/intent_filter.h"
#include "components/services/app_service/public/cpp/intent_filter_util.h"
#include "components/services/app_service/public/cpp/intent_util.h"
#include "url/gurl.h"
-namespace {
-
-void Clone(const apps::PreferredAppsList::PreferredApps& source,
- apps::PreferredAppsList::PreferredApps* destination) {
- destination->clear();
- for (auto& preferred_app : source) {
- destination->push_back(preferred_app->Clone());
- }
-}
-
-} // namespace
-
namespace apps {
PreferredAppsList::PreferredAppsList() = default;
@@ -37,8 +24,8 @@ void PreferredAppsList::Init() {
initialized_ = true;
}
-void PreferredAppsList::Init(PreferredApps& preferred_apps) {
- Clone(preferred_apps, &preferred_apps_);
+void PreferredAppsList::Init(PreferredApps preferred_apps) {
+ preferred_apps_ = std::move(preferred_apps);
auto iter = preferred_apps_.begin();
while (iter != preferred_apps_.end()) {
if (apps_util::IsSupportedLinkForApp((*iter)->app_id,
@@ -52,17 +39,16 @@ void PreferredAppsList::Init(PreferredApps& preferred_apps) {
initialized_ = true;
}
-apps::mojom::ReplacedAppPreferencesPtr PreferredAppsList::AddPreferredApp(
+ReplacedAppPreferences PreferredAppsList::AddPreferredApp(
const std::string& app_id,
- const apps::mojom::IntentFilterPtr& intent_filter) {
- auto replaced_app_preferences = apps::mojom::ReplacedAppPreferences::New();
+ const IntentFilterPtr& intent_filter) {
+ ReplacedAppPreferences replaced_app_preferences;
if (EntryExists(app_id, intent_filter)) {
return replaced_app_preferences;
}
auto iter = preferred_apps_.begin();
- auto& replaced_preference_map = replaced_app_preferences->replaced_preference;
// Go through the list and see if there are overlapped intent filters in the
// list. If there is, add this into the replaced_app_preferences and remove it
@@ -72,21 +58,20 @@ apps::mojom::ReplacedAppPreferencesPtr PreferredAppsList::AddPreferredApp(
if ((*iter)->app_id != app_id &&
apps_util::FiltersHaveOverlap((*iter)->intent_filter, intent_filter)) {
// Add the to be removed preferred app into a map, key by app_id.
- replaced_preference_map[(*iter)->app_id].push_back(
+ replaced_app_preferences[(*iter)->app_id].push_back(
std::move((*iter)->intent_filter));
iter = preferred_apps_.erase(iter);
} else {
iter++;
}
}
- auto new_preferred_app =
- apps::mojom::PreferredApp::New(intent_filter->Clone(), app_id);
- preferred_apps_.push_back(std::move(new_preferred_app));
+ preferred_apps_.push_back(
+ std::make_unique<PreferredApp>(intent_filter->Clone(), app_id));
if (apps_util::IsSupportedLinkForApp(app_id, intent_filter)) {
for (auto& obs : observers_) {
obs.OnPreferredAppChanged(app_id, true);
- for (auto& app : replaced_preference_map) {
+ for (auto& app : replaced_app_preferences) {
obs.OnPreferredAppChanged(app.first, false);
}
}
@@ -94,12 +79,12 @@ apps::mojom::ReplacedAppPreferencesPtr PreferredAppsList::AddPreferredApp(
return replaced_app_preferences;
}
-std::vector<apps::mojom::IntentFilterPtr> PreferredAppsList::DeletePreferredApp(
+IntentFilters PreferredAppsList::DeletePreferredApp(
const std::string& app_id,
- const apps::mojom::IntentFilterPtr& intent_filter) {
+ const IntentFilterPtr& intent_filter) {
// Go through the list and see if there are overlapped intent filters with the
// same app id in the list. If there are, delete the entry.
- std::vector<apps::mojom::IntentFilterPtr> out;
+ IntentFilters out;
auto iter = preferred_apps_.begin();
while (iter != preferred_apps_.end()) {
if ((*iter)->app_id == app_id &&
@@ -120,9 +105,8 @@ std::vector<apps::mojom::IntentFilterPtr> PreferredAppsList::DeletePreferredApp(
return out;
}
-std::vector<apps::mojom::IntentFilterPtr> PreferredAppsList::DeleteAppId(
- const std::string& app_id) {
- std::vector<apps::mojom::IntentFilterPtr> out;
+IntentFilters PreferredAppsList::DeleteAppId(const std::string& app_id) {
+ IntentFilters out;
auto iter = preferred_apps_.begin();
// Go through the list and delete the entry with requested app_id.
@@ -142,9 +126,9 @@ std::vector<apps::mojom::IntentFilterPtr> PreferredAppsList::DeleteAppId(
return out;
}
-std::vector<apps::mojom::IntentFilterPtr>
-PreferredAppsList::DeleteSupportedLinks(const std::string& app_id) {
- std::vector<apps::mojom::IntentFilterPtr> out;
+IntentFilters PreferredAppsList::DeleteSupportedLinks(
+ const std::string& app_id) {
+ IntentFilters out;
auto iter = preferred_apps_.begin();
while (iter != preferred_apps_.end()) {
@@ -166,8 +150,7 @@ PreferredAppsList::DeleteSupportedLinks(const std::string& app_id) {
return out;
}
-void PreferredAppsList::ApplyBulkUpdate(
- apps::mojom::PreferredAppChangesPtr changes) {
+void PreferredAppsList::ApplyBulkUpdate(apps::PreferredAppChangesPtr changes) {
// Process removed filters first. There's no difference in behavior whether we
// handle removals or additions first, but doing removals first means there
// are fewer items in the list to search through when finding matches.
@@ -181,7 +164,7 @@ void PreferredAppsList::ApplyBulkUpdate(
auto iter = preferred_apps_.begin();
while (iter != preferred_apps_.end()) {
if ((*iter)->app_id == app_id &&
- base::Contains(filters, (*iter)->intent_filter)) {
+ Contains(filters, (*iter)->intent_filter)) {
iter = preferred_apps_.erase(iter);
} else {
iter++;
@@ -213,7 +196,8 @@ void PreferredAppsList::ApplyBulkUpdate(
}
has_supported_link = has_supported_link ||
apps_util::IsSupportedLinkForApp(app_id, filter);
- preferred_apps_.emplace_back(base::in_place, std::move(filter), app_id);
+ preferred_apps_.push_back(
+ std::make_unique<PreferredApp>(std::move(filter), app_id));
}
// Notify observers if any of the added filters added were supported links.
@@ -233,14 +217,11 @@ size_t PreferredAppsList::GetEntrySize() const {
return preferred_apps_.size();
}
-PreferredAppsList::PreferredApps PreferredAppsList::GetValue() const {
- PreferredAppsList::PreferredApps preferred_apps_copy;
- Clone(preferred_apps_, &preferred_apps_copy);
- return preferred_apps_copy;
+PreferredApps PreferredAppsList::GetValue() const {
+ return ClonePreferredApps(preferred_apps_);
}
-const PreferredAppsList::PreferredApps& PreferredAppsList::GetReference()
- const {
+const PreferredApps& PreferredAppsList::GetReference() const {
return preferred_apps_;
}
@@ -259,18 +240,18 @@ bool PreferredAppsList::IsPreferredAppForSupportedLinks(
absl::optional<std::string> PreferredAppsList::FindPreferredAppForUrl(
const GURL& url) const {
- auto intent = apps_util::CreateIntentFromUrl(url);
- return FindPreferredAppForIntent(intent);
+ return FindPreferredAppForIntent(
+ std::make_unique<Intent>(apps_util::kIntentActionView, url));
}
absl::optional<std::string> PreferredAppsList::FindPreferredAppForIntent(
- const apps::mojom::IntentPtr& intent) const {
+ const IntentPtr& intent) const {
absl::optional<std::string> best_match_app_id = absl::nullopt;
int best_match_level = static_cast<int>(IntentFilterMatchLevel::kNone);
+ DCHECK(intent);
for (auto& preferred_app : preferred_apps_) {
- if (apps_util::IntentMatchesFilter(intent, preferred_app->intent_filter)) {
- int match_level =
- apps_util::GetFilterMatchLevel(preferred_app->intent_filter);
+ if (intent->MatchFilter(preferred_app->intent_filter)) {
+ int match_level = preferred_app->intent_filter->GetFilterMatchLevel();
if (match_level < best_match_level) {
continue;
}
@@ -282,7 +263,7 @@ absl::optional<std::string> PreferredAppsList::FindPreferredAppForIntent(
}
base::flat_set<std::string> PreferredAppsList::FindPreferredAppsForFilters(
- const std::vector<apps::mojom::IntentFilterPtr>& intent_filters) const {
+ const IntentFilters& intent_filters) const {
base::flat_set<std::string> app_ids;
for (auto& intent_filter : intent_filters) {
@@ -297,11 +278,10 @@ base::flat_set<std::string> PreferredAppsList::FindPreferredAppsForFilters(
return app_ids;
}
-bool PreferredAppsList::EntryExists(
- const std::string& app_id,
- const apps::mojom::IntentFilterPtr& intent_filter) {
+bool PreferredAppsList::EntryExists(const std::string& app_id,
+ const IntentFilterPtr& intent_filter) {
for (auto& entry : preferred_apps_) {
- if (app_id == entry->app_id && intent_filter == entry->intent_filter) {
+ if (app_id == entry->app_id && *intent_filter == *entry->intent_filter) {
return true;
}
}
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps_list.h b/chromium/components/services/app_service/public/cpp/preferred_apps_list.h
index df3baf926ec..614a0d14f12 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps_list.h
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_list.h
@@ -10,8 +10,10 @@
#include <vector>
#include "base/containers/flat_set.h"
+#include "components/services/app_service/public/cpp/intent.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
+#include "components/services/app_service/public/cpp/preferred_app.h"
#include "components/services/app_service/public/cpp/preferred_apps_list_handle.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
class GURL;
@@ -28,33 +30,27 @@ class PreferredAppsList : public PreferredAppsListHandle {
PreferredAppsList(const PreferredAppsList&) = delete;
PreferredAppsList& operator=(const PreferredAppsList&) = delete;
- using PreferredApps = std::vector<apps::mojom::PreferredAppPtr>;
-
// Initialize the preferred app with empty list or existing |preferred_apps|;
void Init();
- void Init(PreferredApps& preferred_apps);
+ void Init(PreferredApps preferred_apps);
// Add a preferred app for an |intent_filter|, and returns a group of
// |app_ids| that is no longer preferred app of their corresponding
// |intent_filters|.
- apps::mojom::ReplacedAppPreferencesPtr AddPreferredApp(
- const std::string& app_id,
- const apps::mojom::IntentFilterPtr& intent_filter);
+ ReplacedAppPreferences AddPreferredApp(const std::string& app_id,
+ const IntentFilterPtr& intent_filter);
// Delete a preferred app for an |intent_filter| with the same |app_id|.
// Returns the deleted filters, if any.
- std::vector<apps::mojom::IntentFilterPtr> DeletePreferredApp(
- const std::string& app_id,
- const apps::mojom::IntentFilterPtr& intent_filter);
+ IntentFilters DeletePreferredApp(const std::string& app_id,
+ const IntentFilterPtr& intent_filter);
// Delete all settings for an |app_id|. Returns the deleted filters, if any.
- std::vector<apps::mojom::IntentFilterPtr> DeleteAppId(
- const std::string& app_id);
+ IntentFilters DeleteAppId(const std::string& app_id);
// Deletes all stored supported link preferences for an |app_id|.
// Returns the deleted filters, if any.
- std::vector<apps::mojom::IntentFilterPtr> DeleteSupportedLinks(
- const std::string& app_id);
+ IntentFilters DeleteSupportedLinks(const std::string& app_id);
// Applies all of the |changes| in a single bulk update. This method is
// intended to only be called from |OnPreferredAppsChanged| App Service
@@ -62,7 +58,7 @@ class PreferredAppsList : public PreferredAppsListHandle {
// Note that removed filters are processed before new filters are added. If
// the same filter appears in both |changes->added_filters| and
// |changes->removed_filters|, it be removed and then immediately added back.
- void ApplyBulkUpdate(apps::mojom::PreferredAppChangesPtr changes);
+ void ApplyBulkUpdate(apps::PreferredAppChangesPtr changes);
// PreferredAppsListHandler overrides:
bool IsInitialized() const override;
@@ -74,15 +70,14 @@ class PreferredAppsList : public PreferredAppsListHandle {
absl::optional<std::string> FindPreferredAppForUrl(
const GURL& url) const override;
absl::optional<std::string> FindPreferredAppForIntent(
- const apps::mojom::IntentPtr& intent) const override;
+ const IntentPtr& intent) const override;
base::flat_set<std::string> FindPreferredAppsForFilters(
- const std::vector<apps::mojom::IntentFilterPtr>& intent_filters)
- const override;
+ const IntentFilters& intent_filters) const override;
private:
// Check if the entry already exists in the preferred app list.
bool EntryExists(const std::string& app_id,
- const apps::mojom::IntentFilterPtr& intent_filter);
+ const IntentFilterPtr& intent_filter);
PreferredApps preferred_apps_;
bool initialized_ = false;
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps_list_handle.h b/chromium/components/services/app_service/public/cpp/preferred_apps_list_handle.h
index ab845da22b3..11f2d142d84 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps_list_handle.h
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_list_handle.h
@@ -13,6 +13,8 @@
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
+#include "components/services/app_service/public/cpp/intent.h"
+#include "components/services/app_service/public/cpp/preferred_app.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -31,8 +33,6 @@ class PreferredAppsListHandle {
PreferredAppsListHandle(const PreferredAppsListHandle&) = delete;
PreferredAppsListHandle& operator=(const PreferredAppsListHandle&) = delete;
- using PreferredApps = std::vector<apps::mojom::PreferredAppPtr>;
-
virtual bool IsInitialized() const = 0;
// Get the entry size of the preferred app list.
virtual size_t GetEntrySize() const = 0;
@@ -49,13 +49,12 @@ class PreferredAppsListHandle {
// Find preferred app id for an |intent|.
virtual absl::optional<std::string> FindPreferredAppForIntent(
- const apps::mojom::IntentPtr& intent) const = 0;
+ const IntentPtr& intent) const = 0;
// Returns a list of app IDs that are set as preferred app to an intent
// filter in the |intent_filters| list.
virtual base::flat_set<std::string> FindPreferredAppsForFilters(
- const std::vector<apps::mojom::IntentFilterPtr>& intent_filters)
- const = 0;
+ const IntentFilters& intent_filters) const = 0;
class Observer : public base::CheckedObserver {
public:
diff --git a/chromium/components/services/app_service/public/cpp/preferred_apps_list_unittest.cc b/chromium/components/services/app_service/public/cpp/preferred_apps_list_unittest.cc
index 97164ec4952..fe8907e4539 100644
--- a/chromium/components/services/app_service/public/cpp/preferred_apps_list_unittest.cc
+++ b/chromium/components/services/app_service/public/cpp/preferred_apps_list_unittest.cc
@@ -5,9 +5,11 @@
#include "components/services/app_service/public/cpp/preferred_apps_list.h"
#include "base/containers/contains.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
#include "components/services/app_service/public/cpp/intent_filter_util.h"
#include "components/services/app_service/public/cpp/intent_test_util.h"
#include "components/services/app_service/public/cpp/intent_util.h"
+#include "components/services/app_service/public/cpp/preferred_app.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -21,6 +23,15 @@ const char kAppId3[] = "hahahahaha";
class PreferredAppListTest : public testing::Test {
protected:
+ apps::IntentFilterPtr MakePatternFilter(const std::string& pattern,
+ apps::PatternMatchType match_type) {
+ auto intent_filter =
+ apps_util::MakeSchemeAndHostOnlyFilter("https", "www.google.com");
+ intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern,
+ pattern, match_type);
+ return intent_filter;
+ }
+
apps::mojom::IntentFilterPtr CreatePatternFilter(
const std::string& pattern,
apps::mojom::PatternMatchType match_type) {
@@ -39,7 +50,7 @@ class PreferredAppListTest : public testing::Test {
// URLs.
TEST_F(PreferredAppListTest, AddPreferredAppForURL) {
GURL filter_url = GURL("https://www.google.com/abc");
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url));
@@ -63,7 +74,7 @@ TEST_F(PreferredAppListTest, AddPreferredAppForURL) {
// Test for preferred app with filter that does not have all condition
// types. E.g. add preferred app with intent filter that only have scheme.
TEST_F(PreferredAppListTest, TopLayerFilters) {
- auto intent_filter = apps_util::CreateSchemeOnlyFilter("tel");
+ auto intent_filter = apps_util::MakeSchemeOnlyFilter("tel");
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
GURL url_in_scope = GURL("tel://1234556/");
@@ -77,15 +88,15 @@ TEST_F(PreferredAppListTest, TopLayerFilters) {
// Test for multiple preferred app setting with different number of condition
// types.
TEST_F(PreferredAppListTest, MixLayerFilters) {
- auto intent_filter_scheme = apps_util::CreateSchemeOnlyFilter("tel");
+ auto intent_filter_scheme = apps_util::MakeSchemeOnlyFilter("tel");
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_scheme);
auto intent_filter_scheme_host =
- apps_util::CreateSchemeAndHostOnlyFilter("http", "www.abc.com");
+ apps_util::MakeSchemeAndHostOnlyFilter("http", "www.abc.com");
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_scheme_host);
auto intent_filter_url =
- apps_util::CreateIntentFilterForUrlScope(GURL("https://www.google.com/"));
+ apps_util::MakeIntentFilterForUrlScope(GURL("https://www.google.com/"));
preferred_apps_.AddPreferredApp(kAppId3, intent_filter_url);
GURL url_1 = GURL("tel://1234556/");
@@ -105,19 +116,19 @@ TEST_F(PreferredAppListTest, MixLayerFilters) {
TEST_F(PreferredAppListTest, MultiplePreferredApps) {
GURL url = GURL("https://www.google.com/");
- auto intent_filter_scheme = apps_util::CreateSchemeOnlyFilter("https");
+ auto intent_filter_scheme = apps_util::MakeSchemeOnlyFilter("https");
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_scheme);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(url));
auto intent_filter_scheme_host =
- apps_util::CreateSchemeAndHostOnlyFilter("https", "www.google.com");
+ apps_util::MakeSchemeAndHostOnlyFilter("https", "www.google.com");
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_scheme_host);
EXPECT_EQ(kAppId2, preferred_apps_.FindPreferredAppForUrl(url));
auto intent_filter_url =
- apps_util::CreateIntentFilterForUrlScope(GURL("https://www.google.com/"));
+ apps_util::MakeIntentFilterForUrlScope(GURL("https://www.google.com/"));
preferred_apps_.AddPreferredApp(kAppId3, intent_filter_url);
EXPECT_EQ(kAppId3, preferred_apps_.FindPreferredAppForUrl(url));
@@ -127,10 +138,9 @@ TEST_F(PreferredAppListTest, MultiplePreferredApps) {
// condition values for a condition type.
TEST_F(PreferredAppListTest, MultipleConditionValues) {
auto intent_filter =
- apps_util::CreateIntentFilterForUrlScope(GURL("https://www.google.com/"));
- apps_util::AddConditionValue(apps::mojom::ConditionType::kScheme, "http",
- apps::mojom::PatternMatchType::kNone,
- intent_filter);
+ apps_util::MakeIntentFilterForUrlScope(GURL("https://www.google.com/"));
+ apps_util::AddConditionValue(apps::ConditionType::kScheme, "http",
+ apps::PatternMatchType::kNone, intent_filter);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
@@ -150,11 +160,11 @@ TEST_F(PreferredAppListTest, MultipleConditionValues) {
// Test for more than one pattern available, we can find the correct match.
TEST_F(PreferredAppListTest, DifferentPatterns) {
auto intent_filter_literal =
- CreatePatternFilter("/bc", apps::mojom::PatternMatchType::kLiteral);
+ MakePatternFilter("/bc", apps::PatternMatchType::kLiteral);
auto intent_filter_prefix =
- CreatePatternFilter("/a", apps::mojom::PatternMatchType::kPrefix);
+ MakePatternFilter("/a", apps::PatternMatchType::kPrefix);
auto intent_filter_glob =
- CreatePatternFilter("/c.*d", apps::mojom::PatternMatchType::kGlob);
+ MakePatternFilter("/c.*d", apps::PatternMatchType::kGlob);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_literal);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_prefix);
@@ -175,7 +185,7 @@ TEST_F(PreferredAppListTest, DifferentPatterns) {
// Test that for same intent filter, the app id will overwrite the old setting.
TEST_F(PreferredAppListTest, OverwritePreferredApp) {
GURL filter_url = GURL("https://www.google.com/abc");
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url));
@@ -189,25 +199,23 @@ TEST_F(PreferredAppListTest, OverwritePreferredApp) {
TEST_F(PreferredAppListTest, OverlapPreferredApp) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
GURL filter_url_2 = GURL("http://www.google.com.au/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_2.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_2.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_2.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_1);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_2.host(),
+ apps::PatternMatchType::kNone, intent_filter_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_1));
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
GURL filter_url_3 = GURL("https://www.abc.com/abc");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_2.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_2);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_2.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_2.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_2);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_2.host(),
+ apps::PatternMatchType::kNone, intent_filter_2);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_2);
EXPECT_EQ(absl::nullopt,
preferred_apps_.FindPreferredAppForUrl(filter_url_1));
@@ -219,114 +227,109 @@ TEST_F(PreferredAppListTest, OverlapPreferredApp) {
TEST_F(PreferredAppListTest, ReplacedAppPreference) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
GURL filter_url_2 = GURL("http://www.google.com.au/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_2.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_2.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_2.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_1);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_2.host(),
+ apps::PatternMatchType::kNone, intent_filter_1);
auto replaced_app_preferences =
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
- EXPECT_EQ(0u, replaced_app_preferences->replaced_preference.size());
+ EXPECT_EQ(0u, replaced_app_preferences.size());
GURL filter_url_3 = GURL("https://www.abc.com/abc");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_2.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_2);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_2.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_2.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_2);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_2.host(),
+ apps::PatternMatchType::kNone, intent_filter_2);
replaced_app_preferences =
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_2);
- EXPECT_EQ(1u, replaced_app_preferences->replaced_preference.size());
- EXPECT_TRUE(replaced_app_preferences->replaced_preference.find(kAppId1) !=
- replaced_app_preferences->replaced_preference.end());
+ EXPECT_EQ(1u, replaced_app_preferences.size());
+ EXPECT_TRUE(replaced_app_preferences.find(kAppId1) !=
+ replaced_app_preferences.end());
GURL filter_url_4 = GURL("http://www.example.com/abc");
- auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_4.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_3);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_4.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_3);
+ auto intent_filter_3 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_4.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_3);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_4.host(),
+ apps::PatternMatchType::kNone, intent_filter_3);
// Test when replacing multiple preferred app entries with same app id.
replaced_app_preferences =
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
- EXPECT_EQ(1u, replaced_app_preferences->replaced_preference.size());
- EXPECT_TRUE(replaced_app_preferences->replaced_preference.find(kAppId2) !=
- replaced_app_preferences->replaced_preference.end());
+ EXPECT_EQ(1u, replaced_app_preferences.size());
+ EXPECT_TRUE(replaced_app_preferences.find(kAppId2) !=
+ replaced_app_preferences.end());
replaced_app_preferences =
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_3);
- EXPECT_EQ(0u, replaced_app_preferences->replaced_preference.size());
+ EXPECT_EQ(0u, replaced_app_preferences.size());
replaced_app_preferences =
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_2);
- EXPECT_EQ(1u, replaced_app_preferences->replaced_preference.size());
- auto entry = replaced_app_preferences->replaced_preference.find(kAppId1);
- EXPECT_TRUE(entry != replaced_app_preferences->replaced_preference.end());
+ EXPECT_EQ(1u, replaced_app_preferences.size());
+ auto entry = replaced_app_preferences.find(kAppId1);
+ EXPECT_TRUE(entry != replaced_app_preferences.end());
EXPECT_EQ(2u, entry->second.size());
// Test when replacing multiple preferred app entries with different app id.
replaced_app_preferences =
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
- EXPECT_EQ(1u, replaced_app_preferences->replaced_preference.size());
- EXPECT_TRUE(replaced_app_preferences->replaced_preference.find(kAppId2) !=
- replaced_app_preferences->replaced_preference.end());
+ EXPECT_EQ(1u, replaced_app_preferences.size());
+ EXPECT_TRUE(replaced_app_preferences.find(kAppId2) !=
+ replaced_app_preferences.end());
replaced_app_preferences =
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_3);
- EXPECT_EQ(0u, replaced_app_preferences->replaced_preference.size());
+ EXPECT_EQ(0u, replaced_app_preferences.size());
replaced_app_preferences =
preferred_apps_.AddPreferredApp(kAppId3, intent_filter_2);
- EXPECT_EQ(2u, replaced_app_preferences->replaced_preference.size());
- entry = replaced_app_preferences->replaced_preference.find(kAppId1);
- EXPECT_TRUE(entry != replaced_app_preferences->replaced_preference.end());
+ EXPECT_EQ(2u, replaced_app_preferences.size());
+ entry = replaced_app_preferences.find(kAppId1);
+ EXPECT_TRUE(entry != replaced_app_preferences.end());
EXPECT_EQ(1u, entry->second.size());
- entry = replaced_app_preferences->replaced_preference.find(kAppId2);
- EXPECT_TRUE(entry != replaced_app_preferences->replaced_preference.end());
+ entry = replaced_app_preferences.find(kAppId2);
+ EXPECT_TRUE(entry != replaced_app_preferences.end());
EXPECT_EQ(1u, entry->second.size());
}
TEST_F(PreferredAppListTest, ReplacedAppPreferencesSameApp) {
GURL filter_url = GURL("https://www.google.com/abc");
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
auto replaced_app_preferences =
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
- EXPECT_EQ(0u, replaced_app_preferences->replaced_preference.size());
+ EXPECT_EQ(0u, replaced_app_preferences.size());
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url));
}
TEST_F(PreferredAppListTest, OverlapPreferencesSameApp) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
GURL filter_url_2 = GURL("http://www.google.com.au/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_2.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_2.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_2.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_1);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_2.host(),
+ apps::PatternMatchType::kNone, intent_filter_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_1));
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
GURL filter_url_3 = GURL("https://www.abc.com/abc");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_2.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_2);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_2.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_2.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_2);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_2.host(),
+ apps::PatternMatchType::kNone, intent_filter_2);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_2);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_1));
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
@@ -336,13 +339,12 @@ TEST_F(PreferredAppListTest, OverlapPreferencesSameApp) {
TEST_F(PreferredAppListTest, AddSameEntry) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
GURL filter_url_2 = GURL("http://www.google.com.au/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_2.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_2.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_2.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_1);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_2.host(),
+ apps::PatternMatchType::kNone, intent_filter_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_1));
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
@@ -358,7 +360,7 @@ TEST_F(PreferredAppListTest, AddSameEntry) {
// the preferred app id.
TEST_F(PreferredAppListTest, DeletePreferredAppForURL) {
GURL filter_url = GURL("https://www.google.com/abc");
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url));
@@ -374,7 +376,7 @@ TEST_F(PreferredAppListTest, DeletePreferredAppForURL) {
// Test for preferred app with filter that does not have all condition
// types. E.g. delete preferred app with intent filter that only have scheme.
TEST_F(PreferredAppListTest, DeleteForTopLayerFilters) {
- auto intent_filter = apps_util::CreateSchemeOnlyFilter("tel");
+ auto intent_filter = apps_util::MakeSchemeOnlyFilter("tel");
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
GURL url_in_scope = GURL("tel://1234556/");
@@ -389,10 +391,9 @@ TEST_F(PreferredAppListTest, DeleteForTopLayerFilters) {
// condition values for a condition type.
TEST_F(PreferredAppListTest, DeleteMultipleConditionValues) {
auto intent_filter =
- apps_util::CreateIntentFilterForUrlScope(GURL("https://www.google.com/"));
- apps_util::AddConditionValue(apps::mojom::ConditionType::kScheme, "http",
- apps::mojom::PatternMatchType::kNone,
- intent_filter);
+ apps_util::MakeIntentFilterForUrlScope(GURL("https://www.google.com/"));
+ apps_util::AddConditionValue(apps::ConditionType::kScheme, "http",
+ apps::PatternMatchType::kNone, intent_filter);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
GURL url_https = GURL("https://www.google.com/");
@@ -408,11 +409,11 @@ TEST_F(PreferredAppListTest, DeleteMultipleConditionValues) {
// Test for more than one pattern available, we can delete the filter.
TEST_F(PreferredAppListTest, DeleteDifferentPatterns) {
auto intent_filter_literal =
- CreatePatternFilter("/bc", apps::mojom::PatternMatchType::kLiteral);
+ MakePatternFilter("/bc", apps::PatternMatchType::kLiteral);
auto intent_filter_prefix =
- CreatePatternFilter("/a", apps::mojom::PatternMatchType::kPrefix);
+ MakePatternFilter("/a", apps::PatternMatchType::kPrefix);
auto intent_filter_glob =
- CreatePatternFilter("/c.*d", apps::mojom::PatternMatchType::kGlob);
+ MakePatternFilter("/c.*d", apps::PatternMatchType::kGlob);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_literal);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_prefix);
@@ -441,12 +442,12 @@ TEST_F(PreferredAppListTest, DeleteDifferentPatterns) {
// to delete has more condition values compare with filter that was set.
TEST_F(PreferredAppListTest, DeleteForNotCompletedFilter) {
auto intent_filter_set =
- apps_util::CreateIntentFilterForUrlScope(GURL("https://www.google.com/"));
+ apps_util::MakeIntentFilterForUrlScope(GURL("https://www.google.com/"));
auto intent_filter_to_delete =
- apps_util::CreateIntentFilterForUrlScope(GURL("http://www.google.com/"));
- apps_util::AddConditionValue(apps::mojom::ConditionType::kScheme, "https",
- apps::mojom::PatternMatchType::kNone,
+ apps_util::MakeIntentFilterForUrlScope(GURL("http://www.google.com/"));
+ apps_util::AddConditionValue(apps::ConditionType::kScheme, "https",
+ apps::PatternMatchType::kNone,
intent_filter_to_delete);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_set);
@@ -468,31 +469,28 @@ TEST_F(PreferredAppListTest, DeleteOverlapFilters) {
GURL filter_url_4 = GURL("http://www.example.com/abc");
// Filter 1 handles url 1 and 2.
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_2.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_2.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_2.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_1);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_2.host(),
+ apps::PatternMatchType::kNone, intent_filter_1);
// Filter 2 handles url 2 and 3.
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_2.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_2);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_2.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_2.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_2);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_2.host(),
+ apps::PatternMatchType::kNone, intent_filter_2);
// Filter 3 handles url 3 and 4.
- auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kScheme, filter_url_4.scheme(),
- apps::mojom::PatternMatchType::kNone, intent_filter_3);
- apps_util::AddConditionValue(
- apps::mojom::ConditionType::kHost, filter_url_4.host(),
- apps::mojom::PatternMatchType::kNone, intent_filter_3);
+ auto intent_filter_3 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
+ apps_util::AddConditionValue(apps::ConditionType::kScheme,
+ filter_url_4.scheme(),
+ apps::PatternMatchType::kNone, intent_filter_3);
+ apps_util::AddConditionValue(apps::ConditionType::kHost, filter_url_4.host(),
+ apps::PatternMatchType::kNone, intent_filter_3);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_3);
@@ -517,7 +515,7 @@ TEST_F(PreferredAppListTest, DeleteOverlapFilters) {
// Test that DeleteAppId() can delete the setting for one filter.
TEST_F(PreferredAppListTest, DeleteAppIdForOneFilter) {
GURL filter_url = GURL("https://www.google.com/abc");
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
+ auto intent_filter = apps_util::MakeIntentFilterForUrlScope(filter_url);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url));
@@ -531,25 +529,24 @@ TEST_F(PreferredAppListTest, DeleteAppIdForOneFilter) {
// delete all of them.
TEST_F(PreferredAppListTest, DeleteAppIdForMultipleFilters) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_1));
GURL filter_url_2 = GURL("https://www.abc.com/google");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_2);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_2);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
GURL filter_url_3 = GURL("tel://12345678/");
- auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
+ auto intent_filter_3 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_3);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_3));
- std::vector<apps::mojom::IntentFilterPtr> removed_filters =
- preferred_apps_.DeleteAppId(kAppId1);
+ apps::IntentFilters removed_filters = preferred_apps_.DeleteAppId(kAppId1);
EXPECT_EQ(absl::nullopt,
preferred_apps_.FindPreferredAppForUrl(filter_url_1));
@@ -559,19 +556,18 @@ TEST_F(PreferredAppListTest, DeleteAppIdForMultipleFilters) {
preferred_apps_.FindPreferredAppForUrl(filter_url_3));
EXPECT_EQ(3u, removed_filters.size());
- EXPECT_TRUE(base::Contains(removed_filters, intent_filter_1));
- EXPECT_TRUE(base::Contains(removed_filters, intent_filter_2));
- EXPECT_TRUE(base::Contains(removed_filters, intent_filter_3));
+ EXPECT_TRUE(apps::Contains(removed_filters, intent_filter_1));
+ EXPECT_TRUE(apps::Contains(removed_filters, intent_filter_2));
+ EXPECT_TRUE(apps::Contains(removed_filters, intent_filter_3));
}
// Test that for filter with multiple condition values, DeleteAppId() can
// delete them all.
TEST_F(PreferredAppListTest, DeleteAppIdForMultipleConditionValues) {
auto intent_filter =
- apps_util::CreateIntentFilterForUrlScope(GURL("https://www.google.com/"));
- apps_util::AddConditionValue(apps::mojom::ConditionType::kScheme, "http",
- apps::mojom::PatternMatchType::kNone,
- intent_filter);
+ apps_util::MakeIntentFilterForUrlScope(GURL("https://www.google.com/"));
+ apps_util::AddConditionValue(apps::ConditionType::kScheme, "http",
+ apps::PatternMatchType::kNone, intent_filter);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
@@ -589,37 +585,37 @@ TEST_F(PreferredAppListTest, DeleteAppIdForMultipleConditionValues) {
// deletes the correct app id.
TEST_F(PreferredAppListTest, DeleteAppIdForMultipleAppIds) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_1));
GURL filter_url_2 = GURL("https://www.abc.com/google");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_2);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_2);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
GURL filter_url_3 = GURL("tel://12345678/");
- auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
+ auto intent_filter_3 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_3);
EXPECT_EQ(kAppId2, preferred_apps_.FindPreferredAppForUrl(filter_url_3));
GURL filter_url_4 = GURL("https://www.google.com.au/");
- auto intent_filter_4 = apps_util::CreateIntentFilterForUrlScope(filter_url_4);
+ auto intent_filter_4 = apps_util::MakeIntentFilterForUrlScope(filter_url_4);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_4);
EXPECT_EQ(kAppId2, preferred_apps_.FindPreferredAppForUrl(filter_url_4));
GURL filter_url_5 = GURL("https://www.example.com/google");
- auto intent_filter_5 = apps_util::CreateIntentFilterForUrlScope(filter_url_5);
+ auto intent_filter_5 = apps_util::MakeIntentFilterForUrlScope(filter_url_5);
preferred_apps_.AddPreferredApp(kAppId3, intent_filter_5);
EXPECT_EQ(kAppId3, preferred_apps_.FindPreferredAppForUrl(filter_url_5));
GURL filter_url_6 = GURL("tel://98765432/");
- auto intent_filter_6 = apps_util::CreateIntentFilterForUrlScope(filter_url_6);
+ auto intent_filter_6 = apps_util::MakeIntentFilterForUrlScope(filter_url_6);
preferred_apps_.AddPreferredApp(kAppId3, intent_filter_6);
EXPECT_EQ(kAppId3, preferred_apps_.FindPreferredAppForUrl(filter_url_6));
@@ -649,15 +645,15 @@ TEST_F(PreferredAppListTest, DeleteAppIdForMultipleAppIds) {
TEST_F(PreferredAppListTest, DeleteSupportedLinks) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
GURL filter_url_2 = GURL("tel://12345678/");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_2);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_2);
GURL filter_url_3 = GURL("https://www.google.com.au/");
- auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
+ auto intent_filter_3 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_3);
auto deleted = preferred_apps_.DeleteSupportedLinks(kAppId1);
@@ -667,23 +663,22 @@ TEST_F(PreferredAppListTest, DeleteSupportedLinks) {
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
EXPECT_EQ(kAppId2, preferred_apps_.FindPreferredAppForUrl(filter_url_3));
EXPECT_EQ(1u, deleted.size());
- EXPECT_EQ(intent_filter_1, deleted[0]);
+ EXPECT_EQ(*intent_filter_1, *deleted[0]);
deleted = preferred_apps_.DeleteSupportedLinks(kAppId2);
EXPECT_EQ(absl::nullopt,
preferred_apps_.FindPreferredAppForUrl(filter_url_3));
EXPECT_EQ(1u, deleted.size());
- EXPECT_EQ(intent_filter_3, deleted[0]);
+ EXPECT_EQ(*intent_filter_3, *deleted[0]);
}
// Test that DeleteSupportedLinks removes the entire preference, including
// condition values other than http/https links.
TEST_F(PreferredAppListTest, DeleteSupportedLinksForMultipleConditionValues) {
- auto intent_filter = apps_util::CreateIntentFilterForUrlScope(
- GURL("https://www.example.com/"));
- apps_util::AddConditionValue(apps::mojom::ConditionType::kScheme, "ftp",
- apps::mojom::PatternMatchType::kNone,
- intent_filter);
+ auto intent_filter =
+ apps_util::MakeIntentFilterForUrlScope(GURL("https://www.example.com/"));
+ apps_util::AddConditionValue(apps::ConditionType::kScheme, "ftp",
+ apps::PatternMatchType::kNone, intent_filter);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter);
preferred_apps_.DeleteSupportedLinks(kAppId1);
@@ -694,13 +689,13 @@ TEST_F(PreferredAppListTest, DeleteSupportedLinksForMultipleConditionValues) {
TEST_F(PreferredAppListTest, ApplyBulkUpdateAdditions) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
GURL filter_url_2 = GURL("https://www.google.com/def");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_2);
GURL filter_url_3 = GURL("https://www.google.com/hij");
- auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
+ auto intent_filter_3 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
- auto changes = apps::mojom::PreferredAppChanges::New();
+ auto changes = std::make_unique<apps::PreferredAppChanges>();
changes->added_filters[kAppId1].push_back(intent_filter_1->Clone());
changes->added_filters[kAppId1].push_back(intent_filter_2->Clone());
changes->added_filters[kAppId2].push_back(intent_filter_3->Clone());
@@ -714,13 +709,13 @@ TEST_F(PreferredAppListTest, ApplyBulkUpdateAdditions) {
TEST_F(PreferredAppListTest, ApplyBulkUpdateDuplicateAdditions) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
GURL filter_url_2 = GURL("https://www.google.com/def");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_2);
GURL filter_url_3 = GURL("https://www.google.com/hij");
- auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
+ auto intent_filter_3 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
- auto changes = apps::mojom::PreferredAppChanges::New();
+ auto changes = std::make_unique<apps::PreferredAppChanges>();
changes->added_filters[kAppId1].push_back(intent_filter_1->Clone());
changes->added_filters[kAppId1].push_back(intent_filter_2->Clone());
changes->added_filters[kAppId2].push_back(intent_filter_3->Clone());
@@ -747,14 +742,14 @@ TEST_F(PreferredAppListTest, ApplyBulkUpdateDuplicateAdditions) {
TEST_F(PreferredAppListTest, ApplyBulkUpdateAddAndRemove) {
GURL filter_url_base = GURL("https://www.google.com/foo");
auto intent_filter_base =
- apps_util::CreateIntentFilterForUrlScope(filter_url_base);
+ apps_util::MakeIntentFilterForUrlScope(filter_url_base);
GURL filter_url_ext = GURL("https://www.google.com/foo/bar");
auto intent_filter_ext =
- apps_util::CreateIntentFilterForUrlScope(filter_url_ext);
+ apps_util::MakeIntentFilterForUrlScope(filter_url_ext);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_base);
- auto changes = apps::mojom::PreferredAppChanges::New();
+ auto changes = std::make_unique<apps::PreferredAppChanges>();
changes->added_filters[kAppId1].push_back(intent_filter_ext->Clone());
changes->removed_filters[kAppId1].push_back(intent_filter_base->Clone());
@@ -770,20 +765,20 @@ TEST_F(PreferredAppListTest, ApplyBulkUpdateAddAndRemove) {
TEST_F(PreferredAppListTest, ApplyBulkUpdateRemoveMatchesExactly) {
GURL filter_url_base = GURL("https://www.google.com/foo");
auto intent_filter_base =
- apps_util::CreateIntentFilterForUrlScope(filter_url_base);
+ apps_util::MakeIntentFilterForUrlScope(filter_url_base);
GURL filter_url_ext = GURL("https://www.google.com/foo/bar");
auto intent_filter_ext =
- apps_util::CreateIntentFilterForUrlScope(filter_url_ext);
+ apps_util::MakeIntentFilterForUrlScope(filter_url_ext);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_ext);
- auto changes = apps::mojom::PreferredAppChanges::New();
+ auto changes = std::make_unique<apps::PreferredAppChanges>();
changes->removed_filters[kAppId1].push_back(intent_filter_base->Clone());
preferred_apps_.ApplyBulkUpdate(std::move(changes));
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_ext));
- changes = apps::mojom::PreferredAppChanges::New();
+ changes = std::make_unique<apps::PreferredAppChanges>();
changes->removed_filters[kAppId1].push_back(intent_filter_ext->Clone());
preferred_apps_.ApplyBulkUpdate(std::move(changes));
@@ -795,20 +790,20 @@ TEST_F(PreferredAppListTest, ApplyBulkUpdateRemoveMatchesExactly) {
// are no matches.
TEST_F(PreferredAppListTest, FindNoPreferredApps) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_1));
GURL filter_url_2 = GURL("https://www.google.com.au/");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_2);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_2);
EXPECT_EQ(kAppId2, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
GURL test_url = GURL("https://www.example.com/google");
- auto test_intent_filter = apps_util::CreateIntentFilterForUrlScope(test_url);
+ auto test_intent_filter = apps_util::MakeIntentFilterForUrlScope(test_url);
- std::vector<apps::mojom::IntentFilterPtr> intent_filters;
+ apps::IntentFilters intent_filters;
intent_filters.push_back(std::move(test_intent_filter));
auto preferred_apps =
@@ -821,20 +816,20 @@ TEST_F(PreferredAppListTest, FindNoPreferredApps) {
// found.
TEST_F(PreferredAppListTest, FindOnePreferredApps) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_1));
GURL filter_url_2 = GURL("https://www.google.com.au/");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_2);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_2);
EXPECT_EQ(kAppId2, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
GURL test_url = GURL("https://www.example.com/google");
- auto test_intent_filter = apps_util::CreateIntentFilterForUrlScope(test_url);
+ auto test_intent_filter = apps_util::MakeIntentFilterForUrlScope(test_url);
- std::vector<apps::mojom::IntentFilterPtr> intent_filters;
+ apps::IntentFilters intent_filters;
intent_filters.push_back(std::move(intent_filter_2));
intent_filters.push_back(std::move(test_intent_filter));
@@ -849,36 +844,36 @@ TEST_F(PreferredAppListTest, FindOnePreferredApps) {
// are made.
TEST_F(PreferredAppListTest, FindMultiplePreferredApps) {
GURL filter_url_1 = GURL("https://www.google.com/abc");
- auto intent_filter_1 = apps_util::CreateIntentFilterForUrlScope(filter_url_1);
+ auto intent_filter_1 = apps_util::MakeIntentFilterForUrlScope(filter_url_1);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_1);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_1));
GURL filter_url_2 = GURL("https://www.abc.com/google");
- auto intent_filter_2 = apps_util::CreateIntentFilterForUrlScope(filter_url_2);
+ auto intent_filter_2 = apps_util::MakeIntentFilterForUrlScope(filter_url_2);
preferred_apps_.AddPreferredApp(kAppId1, intent_filter_2);
EXPECT_EQ(kAppId1, preferred_apps_.FindPreferredAppForUrl(filter_url_2));
GURL filter_url_3 = GURL("tel://12345678/");
- auto intent_filter_3 = apps_util::CreateIntentFilterForUrlScope(filter_url_3);
+ auto intent_filter_3 = apps_util::MakeIntentFilterForUrlScope(filter_url_3);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_3);
EXPECT_EQ(kAppId2, preferred_apps_.FindPreferredAppForUrl(filter_url_3));
GURL filter_url_4 = GURL("https://www.google.com.au/");
- auto intent_filter_4 = apps_util::CreateIntentFilterForUrlScope(filter_url_4);
+ auto intent_filter_4 = apps_util::MakeIntentFilterForUrlScope(filter_url_4);
preferred_apps_.AddPreferredApp(kAppId2, intent_filter_4);
EXPECT_EQ(kAppId2, preferred_apps_.FindPreferredAppForUrl(filter_url_4));
GURL filter_url_5 = GURL("https://www.example.com/google");
- auto intent_filter_5 = apps_util::CreateIntentFilterForUrlScope(filter_url_5);
+ auto intent_filter_5 = apps_util::MakeIntentFilterForUrlScope(filter_url_5);
preferred_apps_.AddPreferredApp(kAppId3, intent_filter_5);
EXPECT_EQ(kAppId3, preferred_apps_.FindPreferredAppForUrl(filter_url_5));
- std::vector<apps::mojom::IntentFilterPtr> intent_filters;
+ apps::IntentFilters intent_filters;
intent_filters.push_back(std::move(intent_filter_1));
intent_filters.push_back(std::move(intent_filter_2));
intent_filters.push_back(std::move(intent_filter_3));
diff --git a/chromium/components/services/app_service/public/mojom/app_service.mojom b/chromium/components/services/app_service/public/mojom/app_service.mojom
index 199129ed973..c29f9e434ef 100644
--- a/chromium/components/services/app_service/public/mojom/app_service.mojom
+++ b/chromium/components/services/app_service/public/mojom/app_service.mojom
@@ -133,12 +133,6 @@ interface AppService {
// Removes all preferred app setting for an |app_id|.
RemovePreferredApp(AppType app_type, string app_id);
- // Resets app identified by |app_id| as preferred app for |intent_filter|.
- RemovePreferredAppForFilter(
- AppType app_type,
- string app_id,
- IntentFilter intent_filter);
-
// Set |app_id| as preferred app for all its supported link filters. Supported
// link filters, which have the http/https scheme and at least one host, are
// always enabled/disabled as a group. |all_link_filters| should contain all
diff --git a/chromium/components/services/filesystem/file_impl_unittest.cc b/chromium/components/services/filesystem/file_impl_unittest.cc
index 519d857e838..170b3713baa 100644
--- a/chromium/components/services/filesystem/file_impl_unittest.cc
+++ b/chromium/components/services/filesystem/file_impl_unittest.cc
@@ -361,6 +361,12 @@ TEST_F(FileImplTest, OpenInTruncateMode) {
// Note: Ignore nanoseconds, since it may not always be supported. We expect at
// least second-resolution support though.
+// TODO(https://crbug.com/702990): Remove this test once last_access_time has
+// been removed after PPAPI has been deprecated. Fuchsia does not support touch,
+// which breaks this test that relies on it. Since PPAPI is being deprecated,
+// this test is excluded from the Fuchsia build.
+// See https://crbug.com/1077456 for details.
+#if !BUILDFLAG(IS_FUCHSIA)
TEST_F(FileImplTest, StatTouch) {
mojo::Remote<mojom::Directory> directory = CreateTempDir();
base::File::Error error;
@@ -430,6 +436,7 @@ TEST_F(FileImplTest, StatTouch) {
// TODO(vtl): Also test Touch() "now" options.
// TODO(vtl): Also test touching both atime and mtime.
}
+#endif // !BUILDFLAG(IS_FUCHSIA)
TEST_F(FileImplTest, TellSeek) {
mojo::Remote<mojom::Directory> directory = CreateTempDir();
diff --git a/chromium/components/services/quarantine/BUILD.gn b/chromium/components/services/quarantine/BUILD.gn
index d8d69e696ba..b9a2d6a7c15 100644
--- a/chromium/components/services/quarantine/BUILD.gn
+++ b/chromium/components/services/quarantine/BUILD.gn
@@ -116,7 +116,7 @@ source_set("unit_tests") {
# enabled, the devices use tmpfs which restricts the extended attributes that
# can be set such that quarantining still would not work. (The platform
# specific tests include a runtime guard to skip tests that need xattr.)
- if (!is_chromecast) {
+ if (!is_castos && !is_cast_android) {
sources += [ "quarantine_unittest.cc" ]
}
diff --git a/chromium/components/services/screen_ai/BUILD.gn b/chromium/components/services/screen_ai/BUILD.gn
index 1d12d228bb4..789decb4697 100644
--- a/chromium/components/services/screen_ai/BUILD.gn
+++ b/chromium/components/services/screen_ai/BUILD.gn
@@ -4,10 +4,6 @@
import("//third_party/protobuf/proto_library.gni")
-proto_library("screen_ai_proto") {
- sources = [ "proto/chrome_screen_ai.proto" ]
-}
-
source_set("screen_ai") {
sources = [
"screen_ai_service_impl.cc",
@@ -15,29 +11,42 @@ source_set("screen_ai") {
]
deps = [
- ":screen_ai_proto",
"//base",
+ "//components/services/screen_ai/proto:proto_convertors",
"//components/services/screen_ai/public/cpp:utilities",
- ]
-
- public_deps = [
"//components/services/screen_ai/public/mojom",
"//mojo/public/cpp/bindings",
+ "//ui/accessibility:ax_base",
]
}
-source_set("screen_ai_sandbox_hook") {
- sources = [
- "sandbox/screen_ai_sandbox_hook_linux.cc",
- "sandbox/screen_ai_sandbox_hook_linux.h",
- ]
+if (!is_mac) {
+ source_set("screen_ai_sandbox_hook") {
+ sources = [
+ "sandbox/screen_ai_sandbox_hook_linux.cc",
+ "sandbox/screen_ai_sandbox_hook_linux.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/component_updater",
+ "//components/services/screen_ai/public/cpp:utilities",
+ "//sandbox/linux:sandbox_services",
+ "//ui/accessibility:ax_base",
+ ]
+
+ public_deps = [ "//sandbox/policy" ]
+ }
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "proto/proto_convertor_unittest.cc" ]
deps = [
- "//base",
- "//components/component_updater",
- "//components/services/screen_ai/public/cpp:utilities",
- "//sandbox/linux:sandbox_services",
+ "//components/services/screen_ai/proto",
+ "//components/services/screen_ai/proto:proto_convertors",
+ "//testing/gtest",
+ "//ui/accessibility:test_support",
]
-
- public_deps = [ "//sandbox/policy" ]
}
diff --git a/chromium/components/services/screen_ai/DEPS b/chromium/components/services/screen_ai/DEPS
new file mode 100644
index 00000000000..30107b9dc5e
--- /dev/null
+++ b/chromium/components/services/screen_ai/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+ "+ui/accessibility/accessibility_features.h",
+ "+ui/accessibility/ax_enum_util.h",
+ "+ui/accessibility/ax_enums.mojom.h",
+ "+ui/accessibility/ax_node_data.h",
+ "+ui/accessibility/ax_role_properties.h",
+ "+ui/accessibility/ax_tree_update.h",
+ "+ui/gfx/geometry",
+]
diff --git a/chromium/components/services/screen_ai/buildflags/BUILD.gn b/chromium/components/services/screen_ai/buildflags/BUILD.gn
new file mode 100644
index 00000000000..fa9e8a92ce8
--- /dev/null
+++ b/chromium/components/services/screen_ai/buildflags/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2022 The Chromium 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/buildflag_header.gni")
+import("//components/services/screen_ai/buildflags/features.gni")
+
+buildflag_header("buildflags") {
+ header = "buildflags.h"
+ flags = [ "ENABLE_SCREEN_AI_SERVICE=$enable_screen_ai_service" ]
+}
diff --git a/chromium/components/services/screen_ai/buildflags/features.gni b/chromium/components/services/screen_ai/buildflags/features.gni
new file mode 100644
index 00000000000..4b48eb12c69
--- /dev/null
+++ b/chromium/components/services/screen_ai/buildflags/features.gni
@@ -0,0 +1,12 @@
+# Copyright 2022 The Chromium 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/chromecast_build.gni")
+import("//build/config/chromeos/ui_mode.gni")
+
+declare_args() {
+ # Screen AI service is still not supported on other platforms.
+ enable_screen_ai_service =
+ is_linux || is_mac || is_chromeos_ash || is_chromeos_lacros
+}
diff --git a/chromium/components/services/screen_ai/proto/BUILD.gn b/chromium/components/services/screen_ai/proto/BUILD.gn
new file mode 100644
index 00000000000..754f89b5e1e
--- /dev/null
+++ b/chromium/components/services/screen_ai/proto/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2022 The Chromium 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("//third_party/protobuf/proto_library.gni")
+
+proto_library("proto") {
+ sources = [
+ "chrome_screen_ai.proto",
+ "dimension.proto",
+ "view_hierarchy.proto",
+ ]
+}
+
+source_set("proto_convertors") {
+ sources = [
+ "proto_convertor.cc",
+ "proto_convertor.h",
+ ]
+
+ deps = [
+ ":proto",
+ "//base",
+ "//components/services/screen_ai/public/mojom",
+ "//ui/accessibility:ax_base",
+ ]
+}
diff --git a/chromium/components/services/screen_ai/proto/chrome_screen_ai.proto b/chromium/components/services/screen_ai/proto/chrome_screen_ai.proto
index 8962b4c80e6..4467ccb13b5 100644
--- a/chromium/components/services/screen_ai/proto/chrome_screen_ai.proto
+++ b/chromium/components/services/screen_ai/proto/chrome_screen_ai.proto
@@ -10,6 +10,8 @@ syntax = "proto3";
package chrome_screen_ai;
+// This option should be enabled for Chrome, but not in Google3.
+// TODO(https://crbug.com/1278249): Try to make this based on build commands.
option optimize_for = LITE_RUNTIME;
// A component of a given UI. This proto is filled based on
@@ -233,11 +235,14 @@ message UIComponent {
// The predicted type of this component.
message PredictedType {
- Type type = 1;
+ oneof type_of {
+ Type enum_type = 1;
+ string string_type = 2;
+ }
- // A score in the range of [0, 1], indicating how confident the model is of
- // the given type prediction. Closer to 1 means more confident.
- float score = 2;
+ // A confidence score in the range of [0, 1], indicating how confident the
+ // model is of the given type prediction. Closer to 1 means more confident.
+ float confidence = 3;
}
PredictedType predicted_type = 1;
@@ -253,8 +258,153 @@ message Rect {
int32 y = 2;
int32 width = 3;
int32 height = 4;
+
+ // Angle of rotation of (in degrees, clockwise is positive) of the box about
+ // the top-left corner.
+ float angle = 5;
+}
+
+enum Direction {
+ LEFT_TO_RIGHT = 0;
+ RIGHT_TO_LEFT = 1;
+ UNSPECIFIED = 2;
+ TOP_TO_BOTTOM = 3;
+}
+
+// Text line with associated bounding box.
+// This proto is copied from google3/ocr/photo/proto/image.proto.
+message LineBox {
+ // Words in the text line with their bounding boxes.
+ repeated WordBox words = 1;
+
+ // Line bounding box relative to the original image.
+ Rect bounding_box = 2;
+
+ // Text line in UTF8 format.
+ string utf8_string = 3;
+
+ // Confidence as computed by the OCR engine.
+ float confidence = 4;
+
+ // Language guess for the line. The format is the ISO 639-1 two-letter
+ // language code if that is defined (e.g. "en"), or else the ISO 639-2
+ // three-letter code if that is defined, or else a Google-specific code.
+ string language = 5;
+
+ // ID of the text block that this line belongs to.
+ int32 block_id = 6;
+
+ // Index within the block that this line belongs to.
+ int32 order_within_block = 7;
+
+ // The direction of the script contained in the line. Directed by the enum
+ // Direction defined above.
+ int32 direction = 8;
+
+ // Content type for this line.
+ ContentType content_type = 9;
+}
+
+// Word with associated bounding box.
+// This proto is copied from google3/ocr/photo/proto/image.proto.
+message WordBox {
+ // Symbols in the word with their bounding boxes (relative to the word
+ // ImagePatch).
+ repeated SymbolBox symbols = 1;
+
+ // Word bounding box relative to the original image.
+ Rect bounding_box = 2;
+
+ // Word string in UTF8 format.
+ string utf8_string = 3;
+
+ // Confidence as computed by the OCR engine.
+ float confidence = 4;
+
+ // True if the word passes the internal beamsearch dictionary check.
+ bool dictionary_word = 5;
+
+ // Language guess for the word. The format is the ISO 639-1 two-letter
+ // language code if that is defined (e.g. "en"), or else the ISO 639-2
+ // three-letter code if that is defined, or else a Google-specific code.
+ string language = 6;
+
+ // This word is separated from next word by space.
+ bool has_space_after = 7;
+
+ // If true, foreground and background colors successfully detected.
+ bool estimate_color_success = 8;
+
+ // Estimated grayscale value of foreground.
+ int32 foreground_gray_value = 9;
+
+ // Estimated grayscale value of background.
+ int32 background_gray_value = 10;
+
+ // Estimated RGB of foreground. Extracting RGB channels from this
+ // integer is best done using the leptonica helper extractRGBValues().
+ int32 foreground_rgb_value = 11;
+
+ // Estimated RGB of background. Extracting RGB channels from this
+ // integer is best done using the leptonica helper extractRGBValues().
+ int32 background_rgb_value = 12;
+
+ // The direction of the script contained in the word. Directed by the enum
+ // Direction defined above.
+ int32 direction = 13;
+
+ // Content type for this word.
+ ContentType content_type = 14;
+
+ // If content_type == HANDWRITTEN, confidence on this word being handwritten.
+ float handwriting_confidence = 15;
+
+ // Detected orientation of the text.
+ Orientation orientation = 16;
+
+ // Confidence of langid language identification.
+ float language_confidence = 17;
+}
+
+// Symbol with associated bounding box.
+// This proto is copied from google3/ocr/photo/proto/image.proto.
+message SymbolBox {
+ // Bounding box of the symbol relative to the original image.
+ Rect bounding_box = 1;
+
+ // Symbol in UTF8 format.
+ string utf8_string = 2;
+}
+
+// Copied from google3/ocr/photo/proto/image.proto.
+enum Orientation {
+ ORIENTATION_DEFAULT = 0;
+ ORIENTATION_HORIZONTAL = 1;
+ ORIENTATION_VERTICAL = 2;
+ ORIENTATION_ROTATED_HORIZONTAL = 3;
+ ORIENTATION_ROTATED_VERTICAL = 4;
+}
+
+// Copied from google3/ocr/goodoc/page-layout.proto
+enum ContentType {
+ CONTENT_TYPE_PRINTED_TEXT = 0;
+ CONTENT_TYPE_HANDWRITTEN_TEXT = 1;
+ CONTENT_TYPE_IMAGE = 2;
+ CONTENT_TYPE_LINE_DRAWING = 3;
+ // E.g. a dividing line.
+ CONTENT_TYPE_SEPARATOR = 4;
+ CONTENT_TYPE_UNREADABLE_TEXT = 5;
+ // Formula: a mathematical or chemical formula.
+ CONTENT_TYPE_FORMULA = 6;
+ // Same as FORMULA, but handwritten.
+ CONTENT_TYPE_HANDWRITTEN_FORMULA = 7;
+ // Signature or intitals.
+ CONTENT_TYPE_SIGNATURE = 8;
+
+ CONTENT_TYPE_UNKNOWN = 100;
}
message VisualAnnotation {
repeated UIComponent ui_component = 1;
+ repeated LineBox lines = 2;
}
diff --git a/chromium/components/services/screen_ai/proto/dimension.proto b/chromium/components/services/screen_ai/proto/dimension.proto
new file mode 100644
index 00000000000..e436f243d3a
--- /dev/null
+++ b/chromium/components/services/screen_ai/proto/dimension.proto
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This proto is copied from the following proto, do not change.
+// google3/knowledge/cerebra/sense/im2query/screenai/proto/dimensions.proto
+syntax = "proto2";
+
+package screenai;
+
+option optimize_for = LITE_RUNTIME;
+
+// Normalized coordinates [0.0, 1.0] specifying a region of the screen.
+// The coordinate [0.0, 0.0] corresponds to the top left pixel on the screen.
+message BoundingBox {
+ optional float top = 1;
+ optional float left = 2;
+ optional float bottom = 3;
+ optional float right = 4;
+}
+
+// Bounding box specifying a region of the screen in pixel coordinates.
+// The coordinate [0, 0] corresponds to the top left pixel on the screen.
+message BoundingBoxPixels {
+ optional int32 top = 1;
+ optional int32 left = 2;
+ optional int32 bottom = 3;
+ optional int32 right = 4;
+}
diff --git a/chromium/components/services/screen_ai/proto/proto_convertor.cc b/chromium/components/services/screen_ai/proto/proto_convertor.cc
new file mode 100644
index 00000000000..ba27b51dd2c
--- /dev/null
+++ b/chromium/components/services/screen_ai/proto/proto_convertor.cc
@@ -0,0 +1,528 @@
+// Copyright 2022 The Chromium 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/services/screen_ai/proto/proto_convertor.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <map>
+#include <memory>
+#include <numeric>
+#include <utility>
+#include <vector>
+
+#include "base/check_op.h"
+#include "base/notreached.h"
+#include "components/services/screen_ai/proto/chrome_screen_ai.pb.h"
+#include "components/services/screen_ai/proto/dimension.pb.h"
+#include "components/services/screen_ai/proto/view_hierarchy.pb.h"
+#include "components/services/screen_ai/public/mojom/screen_ai_service.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/accessibility/ax_enum_util.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_role_properties.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/transform.h"
+
+namespace {
+
+// The minimum confidence level that a Screen AI annotation should have to be
+// accepted.
+// TODO(https://crbug.com/1278249): Add experiment or heuristics to better
+// adjust this threshold.
+constexpr float kScreenAIMinConfidenceThreshold = 0.1f;
+
+// Returns the next valid ID that can be used for identifying `AXNode`s in the
+// accessibility tree.
+ui::AXNodeID GetNextNodeID() {
+ static ui::AXNodeID next_node_id{1};
+ return next_node_id++;
+}
+
+// Returns whether the provided `predicted_type` is:
+// A) set, and
+// B) has a confidence that is above our acceptance threshold.
+bool SerializePredictedType(
+ const chrome_screen_ai::UIComponent::PredictedType& predicted_type,
+ ui::AXNodeData& out_data) {
+ DCHECK_EQ(out_data.role, ax::mojom::Role::kUnknown);
+ if (predicted_type.confidence() < 0.0f ||
+ predicted_type.confidence() > 1.0f) {
+ NOTREACHED()
+ << "Unrecognized chrome_screen_ai::PredictedType::confidence value: "
+ << predicted_type.confidence();
+ return false; // Confidence is out of bounds.
+ }
+ if (predicted_type.confidence() < kScreenAIMinConfidenceThreshold)
+ return false;
+ switch (predicted_type.type_of_case()) {
+ case chrome_screen_ai::UIComponent::PredictedType::kEnumType:
+ // TODO(https://crbug.com/1278249): We do not actually need an enum. All
+ // predicted types could be strings. We could easily map from a string to
+ // an `ax::mojom::Role`. Then, we won't need to keep the enums synced.
+ out_data.role = static_cast<ax::mojom::Role>(predicted_type.enum_type());
+ break;
+ case chrome_screen_ai::UIComponent::PredictedType::kStringType:
+ out_data.role = ax::mojom::Role::kGenericContainer;
+ out_data.AddStringAttribute(ax::mojom::StringAttribute::kRoleDescription,
+ predicted_type.string_type());
+ break;
+ case chrome_screen_ai::UIComponent::PredictedType::TYPE_OF_NOT_SET:
+ NOTREACHED() << "Malformed proto message: Required field "
+ "`chrome_screen_ai::UIComponent::PredictedType` not set.";
+ return false;
+ }
+
+ return true;
+}
+
+void SerializeBoundingBox(const chrome_screen_ai::Rect& bounding_box,
+ const ui::AXNodeID& container_id,
+ ui::AXNodeData& out_data) {
+ out_data.relative_bounds.bounds =
+ gfx::RectF(bounding_box.x(), bounding_box.y(), bounding_box.width(),
+ bounding_box.height());
+ // A negative width or height will result in an empty rect.
+ if (out_data.relative_bounds.bounds.IsEmpty())
+ return;
+ if (container_id != ui::kInvalidAXNodeID)
+ out_data.relative_bounds.offset_container_id = container_id;
+ if (bounding_box.angle()) {
+ out_data.relative_bounds.transform = std::make_unique<gfx::Transform>();
+ out_data.relative_bounds.transform->Rotate(bounding_box.angle());
+ }
+}
+
+void SerializeDirection(const chrome_screen_ai::Direction& direction,
+ ui::AXNodeData& out_data) {
+ if (!chrome_screen_ai::Direction_IsValid(direction)) {
+ NOTREACHED() << "Unrecognized chrome_screen_ai::Direction value: "
+ << direction;
+ return;
+ }
+ switch (direction) {
+ case chrome_screen_ai::Direction::UNSPECIFIED:
+ // We assume that LEFT_TO_RIGHT is the default direction.
+ case chrome_screen_ai::Direction::LEFT_TO_RIGHT:
+ out_data.AddIntAttribute(
+ ax::mojom::IntAttribute::kTextDirection,
+ static_cast<int32_t>(ax::mojom::WritingDirection::kLtr));
+ break;
+ case chrome_screen_ai::Direction::RIGHT_TO_LEFT:
+ out_data.AddIntAttribute(
+ ax::mojom::IntAttribute::kTextDirection,
+ static_cast<int32_t>(ax::mojom::WritingDirection::kRtl));
+ break;
+ case chrome_screen_ai::Direction::TOP_TO_BOTTOM:
+ out_data.AddIntAttribute(
+ ax::mojom::IntAttribute::kTextDirection,
+ static_cast<int32_t>(ax::mojom::WritingDirection::kTtb));
+ break;
+ case google::protobuf::kint32min:
+ case google::protobuf::kint32max:
+ // Ordinarily, a default case should have been added to permit future
+ // additions to `chrome_screen_ai::Direction`. However, in this
+ // case, both the screen_ai library and this code should always be in
+ // sync.
+ NOTREACHED() << "Unrecognized chrome_screen_ai::Direction value: "
+ << direction;
+ break;
+ }
+}
+
+void SerializeContentType(const chrome_screen_ai::ContentType& content_type,
+ ui::AXNodeData& out_data) {
+ if (!chrome_screen_ai::ContentType_IsValid(content_type)) {
+ NOTREACHED() << "Unrecognized chrome_screen_ai::ContentType value: "
+ << content_type;
+ return;
+ }
+ switch (content_type) {
+ case chrome_screen_ai::CONTENT_TYPE_PRINTED_TEXT:
+ case chrome_screen_ai::CONTENT_TYPE_HANDWRITTEN_TEXT:
+ out_data.role = ax::mojom::Role::kStaticText;
+ break;
+ case chrome_screen_ai::CONTENT_TYPE_IMAGE:
+ out_data.role = ax::mojom::Role::kImage;
+ break;
+ case chrome_screen_ai::CONTENT_TYPE_LINE_DRAWING:
+ out_data.role = ax::mojom::Role::kGraphicsObject;
+ break;
+ case chrome_screen_ai::CONTENT_TYPE_SEPARATOR:
+ out_data.role = ax::mojom::Role::kSplitter;
+ break;
+ case chrome_screen_ai::CONTENT_TYPE_UNREADABLE_TEXT:
+ out_data.role = ax::mojom::Role::kGraphicsObject;
+ break;
+ case chrome_screen_ai::CONTENT_TYPE_FORMULA:
+ case chrome_screen_ai::CONTENT_TYPE_HANDWRITTEN_FORMULA:
+ // Note that `Role::kMath` indicates that the formula is not represented
+ // as a subtree of MathML elements in the accessibility tree, but as a raw
+ // string which may optionally be written in MathML, but could also be
+ // written in plain text.
+ out_data.role = ax::mojom::Role::kMath;
+ break;
+ case chrome_screen_ai::CONTENT_TYPE_SIGNATURE:
+ // Signatures may be readable, but even when they are not we could still
+ // try our best.
+ // TODO(accessibility): Explore adding a description attribute informing
+ // the user that this is a signature, e.g. via ARIA Annotations.
+ out_data.role = ax::mojom::Role::kStaticText;
+ break;
+ case chrome_screen_ai::CONTENT_TYPE_UNKNOWN:
+ // This should be "Role::kPresentational" but it has been erroniously
+ // removed from the codebase.
+ // TODO(nektar): Add presentational role back to avoid confusion with the
+ // meaning of kNone vs. kUnknown.
+ out_data.role = ax::mojom::Role::kNone; // Presentational.
+ break;
+ case google::protobuf::kint32min:
+ case google::protobuf::kint32max:
+ // Ordinarily, a default case should have been added to permit future
+ // additions to `chrome_screen_ai::ContentType`. However, in this
+ // case, both the screen_ai library and this code should always be in
+ // sync.
+ NOTREACHED() << "Unrecognized chrome_screen_ai::ContentType value: "
+ << content_type;
+ break;
+ }
+}
+
+void SerializeWordBox(const chrome_screen_ai::WordBox& word_box,
+ const size_t index,
+ ui::AXNodeData& parent_node,
+ std::vector<ui::AXNodeData>& node_data) {
+ DCHECK_LT(index, node_data.size());
+ DCHECK_NE(parent_node.id, ui::kInvalidAXNodeID);
+ ui::AXNodeData& word_box_node = node_data[index];
+ DCHECK_EQ(word_box_node.role, ax::mojom::Role::kUnknown);
+ if (word_box.confidence() < 0.0f || word_box.confidence() > 1.0f) {
+ NOTREACHED() << "Unrecognized chrome_screen_ai::WordBox::confidence value: "
+ << word_box.confidence();
+ return; // Confidence is out of bounds.
+ }
+ if (word_box.confidence() < kScreenAIMinConfidenceThreshold)
+ return;
+ word_box_node.role = ax::mojom::Role::kInlineTextBox;
+ word_box_node.id = GetNextNodeID();
+ SerializeBoundingBox(word_box.bounding_box(), parent_node.id, word_box_node);
+ // Since the role is `kInlineTextBox`, NameFrom would automatically and
+ // correctly be set to `ax::mojom::NameFrom::kContents`.
+ if (word_box.has_space_after()) {
+ word_box_node.SetName(word_box.utf8_string() + " ");
+ } else {
+ word_box_node.SetName(word_box.utf8_string());
+ }
+ // TODO(nektar): DCHECK that line box's text is equal to the concatenation of
+ // the text found in all contained word boxes.
+ // TODO(nektar): Set character bounding box information.
+ if (word_box.estimate_color_success()) {
+ word_box_node.AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor,
+ word_box.background_rgb_value());
+ word_box_node.AddIntAttribute(ax::mojom::IntAttribute::kColor,
+ word_box.foreground_rgb_value());
+ }
+ SerializeDirection(
+ static_cast<chrome_screen_ai::Direction>(word_box.direction()),
+ word_box_node);
+ parent_node.child_ids.push_back(word_box_node.id);
+}
+
+void SerializeUIComponent(const chrome_screen_ai::UIComponent& ui_component,
+ const size_t index,
+ ui::AXNodeData& parent_node,
+ std::vector<ui::AXNodeData>& node_data) {
+ DCHECK_LT(index, node_data.size());
+ DCHECK_NE(parent_node.id, ui::kInvalidAXNodeID);
+ ui::AXNodeData& current_node = node_data[index];
+ if (!SerializePredictedType(ui_component.predicted_type(), current_node))
+ return;
+ current_node.id = GetNextNodeID();
+ SerializeBoundingBox(ui_component.bounding_box(), parent_node.id,
+ current_node);
+ parent_node.child_ids.push_back(current_node.id);
+}
+
+void SerializeLineBox(const chrome_screen_ai::LineBox& line_box,
+ const size_t index,
+ ui::AXNodeData& parent_node,
+ std::vector<ui::AXNodeData>& node_data) {
+ DCHECK_LT(index, node_data.size());
+ DCHECK_NE(parent_node.id, ui::kInvalidAXNodeID);
+ ui::AXNodeData& line_box_node = node_data[index];
+ DCHECK_EQ(line_box_node.role, ax::mojom::Role::kUnknown);
+ if (line_box.confidence() < 0.0f || line_box.confidence() > 1.0f) {
+ NOTREACHED() << "Unrecognized chrome_screen_ai::LineBox::confidence value: "
+ << line_box.confidence();
+ return; // Confidence is out of bounds.
+ }
+ if (line_box.confidence() < kScreenAIMinConfidenceThreshold)
+ return;
+ SerializeContentType(line_box.content_type(), line_box_node);
+ line_box_node.id = GetNextNodeID();
+ if (ui::IsText(line_box_node.role)) {
+ size_t word_node_index = index + 1u;
+ for (const auto& word : line_box.words())
+ SerializeWordBox(word, word_node_index++, line_box_node, node_data);
+ }
+ SerializeBoundingBox(line_box.bounding_box(), parent_node.id, line_box_node);
+ // Since the role is `kStaticText`, NameFrom would automatically and correctly
+ // be set to `ax::mojom::NameFrom::kContents`.
+ line_box_node.SetName(line_box.utf8_string());
+ if (!line_box.language().empty()) {
+ // TODO(nektar): Only set language if different from parent node to
+ // minimize memory usage.
+ line_box_node.AddStringAttribute(ax::mojom::StringAttribute::kLanguage,
+ line_box.language());
+ }
+ SerializeDirection(
+ static_cast<chrome_screen_ai::Direction>(line_box.direction()),
+ line_box_node);
+ parent_node.child_ids.push_back(line_box_node.id);
+}
+
+// Adds the subtree of |nodes[node_index_to_add]| to |nodes_order| with
+// pre-order traversal.
+// The comment at the beginning of |Screen2xSnapshotToViewHierarchy| explains
+// more.
+void AddSubTree(const std::vector<ui::AXNodeData>& nodes,
+ std::map<int, int>& id_to_position,
+ std::vector<int>& nodes_order,
+ const int node_index_to_add) {
+ nodes_order.push_back(node_index_to_add);
+ const ui::AXNodeData& node = nodes[node_index_to_add];
+ for (const ui::AXNodeID& child_id : node.child_ids)
+ AddSubTree(nodes, id_to_position, nodes_order, id_to_position[child_id]);
+}
+
+} // namespace
+
+namespace screen_ai {
+
+ui::AXTreeUpdate ScreenAIVisualAnnotationToAXTreeUpdate(
+ const std::string& serialized_proto,
+ const gfx::Rect& image_rect) {
+ ui::AXTreeUpdate update;
+
+ chrome_screen_ai::VisualAnnotation visual_annotation;
+ if (!visual_annotation.ParseFromString(serialized_proto)) {
+ NOTREACHED() << "Could not parse Screen AI library output.";
+ return update;
+ }
+
+ // TODO(https://crbug.com/1278249): Create an AXTreeSource and create the
+ // update using AXTreeSerializer.
+
+ // Each `UIComponent` and `LineBox` will take up one node in the accessibility
+ // tree, resulting in hundreds of nodes, making it inefficient to push_back
+ // one node at a time. We pre-allocate the needed nodes making node creation
+ // an O(n) operation.
+ const size_t word_count = std::accumulate(
+ std::begin(visual_annotation.lines()),
+ std::end(visual_annotation.lines()), 0u,
+ [](const size_t& count, const chrome_screen_ai::LineBox& line_box) {
+ return count + line_box.words().size();
+ });
+
+ // Each unique `chrome_screen_ai::LineBox::block_id` creates a new
+ // paragraph, each paragraph is placed in its correct reading order,
+ // and each paragraph has a sorted set of line boxes. Line boxes are sorted
+ // using their `chrome_screen_ai::LineBox::order_within_block` member and they
+ // are identified by their index in the container of line boxes. Use std::map
+ // to sort both paragraphs and lines, both operations having an O(n * log(n))
+ // complexity.
+ // TODO(accessibility): Determine reading order based on visual positioning of
+ // paragraphs, not on their block IDs.
+ std::map<int32_t, std::map<int32_t, int>> blocks_to_lines_map;
+ for (int i = 0; i < visual_annotation.lines_size(); ++i) {
+ const chrome_screen_ai::LineBox& line = visual_annotation.lines(i);
+ blocks_to_lines_map[line.block_id()].emplace(
+ std::make_pair(line.order_within_block(), i));
+ }
+
+ size_t rootnodes_count = 0u;
+ if (!visual_annotation.ui_component().empty())
+ ++rootnodes_count;
+ if (!visual_annotation.lines().empty())
+ ++rootnodes_count;
+
+ std::vector<ui::AXNodeData> nodes(
+ rootnodes_count + visual_annotation.ui_component().size() +
+ blocks_to_lines_map.size() + visual_annotation.lines().size() +
+ word_count);
+ size_t index = 0u;
+
+ if (!visual_annotation.ui_component().empty()) {
+ ui::AXNodeData& rootnode = nodes[index++];
+ rootnode.role = ax::mojom::Role::kDialog;
+ rootnode.id = GetNextNodeID();
+ rootnode.relative_bounds.bounds = gfx::RectF(image_rect);
+ for (const auto& ui_component : visual_annotation.ui_component())
+ SerializeUIComponent(ui_component, index++, rootnode, nodes);
+ }
+
+ if (!visual_annotation.lines().empty()) {
+ // We assume that OCR is performed on a page-by-page basis.
+ ui::AXNodeData& page_node = nodes[index++];
+ page_node.role = ax::mojom::Role::kRegion;
+ page_node.id = GetNextNodeID();
+ page_node.AddBoolAttribute(ax::mojom::BoolAttribute::kIsPageBreakingObject,
+ true);
+ page_node.relative_bounds.bounds = gfx::RectF(image_rect);
+ for (const auto& block_to_lines_pair : blocks_to_lines_map) {
+ for (const auto& line_sequence_number_to_index_pair :
+ block_to_lines_pair.second) {
+ const chrome_screen_ai::LineBox& line_box =
+ visual_annotation.lines(line_sequence_number_to_index_pair.second);
+ SerializeLineBox(line_box, index++, page_node, nodes);
+ index += line_box.words().size();
+ }
+ }
+ }
+
+ // Filter out invalid / unrecognized / unused nodes from the update.
+ update.nodes.resize(nodes.size());
+ auto end_node_iter =
+ std::copy_if(std::begin(nodes), std::end(nodes), std::begin(update.nodes),
+ [](const ui::AXNodeData& node_data) {
+ return node_data.role != ax::mojom::Role::kUnknown &&
+ node_data.id != ui::kInvalidAXNodeID;
+ });
+ update.nodes.resize(std::distance(std::begin(update.nodes), end_node_iter));
+
+ // TODO(https://crbug.com/1278249): Add UMA metrics to record the number of
+ // annotations, item types, confidence levels, etc.
+
+ return update;
+}
+
+std::string Screen2xSnapshotToViewHierarchy(const ui::AXTreeUpdate& snapshot) {
+ screenai::ViewHierarchy view_hierarchy;
+
+ // Screen2x requires the nodes to come in PRE-ORDER, and have only positive
+ // ids. |nodes_order| will specify the new order of the nodes, i.e.
+ // nodes_order[X] will tell which index in |snapshot.nodes| will be the new
+ // Xth node in the proto that is sent to Screen2x. Screen2x also requires that
+ // the node at position X would have id X.
+ std::vector<int> nodes_order;
+
+ // A map for fast access from AXNode.id to position in |snapshot.nodex|.
+ std::map<int, int> id_to_position;
+
+ // A map for fast access from AXNode.id of a child node to its parent node.
+ std::map<int, int> child_id_to_parent_id;
+
+ // The new id for each node id in |snapshot.nodes|.
+ std::map<int, int> new_id;
+
+ int snapshot_width = -1;
+ int snapshot_height = -1;
+ int root_index = -1;
+
+ for (size_t i = 0; i < snapshot.nodes.size(); i++) {
+ const ui::AXNodeData& node = snapshot.nodes[i];
+
+ id_to_position[static_cast<int>(node.id)] = static_cast<int>(i);
+ for (const ui::AXNodeID& child_id : node.child_ids)
+ child_id_to_parent_id[child_id] = static_cast<int>(node.id);
+
+ // Set root as the first node and take its size as snapshot size.
+ if (node.id == snapshot.root_id) {
+ root_index = i;
+ snapshot_width = node.relative_bounds.bounds.width();
+ snapshot_height = node.relative_bounds.bounds.height();
+ }
+ }
+
+ DCHECK_NE(root_index, -1) << "Root not found.";
+ AddSubTree(snapshot.nodes, id_to_position, nodes_order, root_index);
+
+ for (int i = 0; i < static_cast<int>(nodes_order.size()); i++)
+ new_id[snapshot.nodes[nodes_order[i]].id] = i;
+
+ for (int node_index : nodes_order) {
+ const ui::AXNodeData& node = snapshot.nodes[node_index];
+ const ui::AXNodeID& ax_node_id = node.id;
+ screenai::UiElement* uie = view_hierarchy.add_ui_elements();
+ screenai::UiElementAttribute* attrib = nullptr;
+
+ // ID.
+ uie->set_id(new_id[ax_node_id]);
+
+ // Text.
+ attrib = uie->add_attributes();
+ attrib->set_name("text");
+ attrib->set_string_value(
+ node.GetStringAttribute(ax::mojom::StringAttribute::kName));
+
+ // Class Name.
+ // This is a fixed constant for Chrome requests to Screen2x.
+ attrib = uie->add_attributes();
+ attrib->set_name("class_name");
+ attrib->set_string_value("chrome.unicorn");
+
+ // Role.
+ attrib = uie->add_attributes();
+ attrib->set_name("chrome_role");
+ attrib->set_string_value(ui::ToString(node.role));
+
+ // AXNode ID.
+ attrib = uie->add_attributes();
+ attrib->set_name("/axnode/node_id");
+ attrib->set_int_value(ax_node_id);
+
+ // Child IDs.
+ for (const ui::AXNodeID& id : node.child_ids) {
+ attrib = uie->add_attributes();
+ attrib->set_name("/axnode/child_ids");
+ attrib->set_int_value(id);
+ uie->add_child_ids(new_id[id]);
+ }
+
+ // Type and parent.
+ if (node.id == snapshot.root_id) {
+ uie->set_type(screenai::UiElementType::ROOT);
+ uie->set_parent_id(-1);
+ } else {
+ uie->set_type(screenai::UiElementType::VIEW);
+ uie->set_parent_id(new_id[child_id_to_parent_id[ax_node_id]]);
+ }
+
+ // TODO(https://crbug.com/1278249): Bounding box and Bounding Box Pixels
+ // do not consider offset container, ransforms, device scaling, clipping,
+ // offscreen state, etc. This should be fixed the same way the data is
+ // created for training Screen2x models.
+
+ // Bounding Box.
+ uie->mutable_bounding_box()->set_top(node.relative_bounds.bounds.y() /
+ snapshot_height);
+ uie->mutable_bounding_box()->set_left(node.relative_bounds.bounds.x() /
+ snapshot_width);
+ uie->mutable_bounding_box()->set_bottom(
+ node.relative_bounds.bounds.bottom() / snapshot_height);
+ uie->mutable_bounding_box()->set_right(node.relative_bounds.bounds.right() /
+ snapshot_width);
+
+ // Bounding Box Pixels.
+ uie->mutable_bounding_box_pixels()->set_top(
+ node.relative_bounds.bounds.y());
+ uie->mutable_bounding_box_pixels()->set_left(
+ node.relative_bounds.bounds.x());
+ uie->mutable_bounding_box_pixels()->set_bottom(
+ node.relative_bounds.bounds.bottom());
+ uie->mutable_bounding_box_pixels()->set_right(
+ node.relative_bounds.bounds.right());
+
+ // TODO(https://crbug.com/1278249): Add non-essential values.
+ }
+
+ return view_hierarchy.SerializeAsString();
+}
+
+} // namespace screen_ai
diff --git a/chromium/components/services/screen_ai/proto/proto_convertor.h b/chromium/components/services/screen_ai/proto/proto_convertor.h
new file mode 100644
index 00000000000..873bb2545f9
--- /dev/null
+++ b/chromium/components/services/screen_ai/proto/proto_convertor.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SERVICES_SCREEN_AI_PROTO_PROTO_CONVERTOR_H_
+#define COMPONENTS_SERVICES_SCREEN_AI_PROTO_PROTO_CONVERTOR_H_
+
+#include <string>
+
+#include "ui/accessibility/ax_tree_update.h"
+
+namespace gfx {
+class Rect;
+} // namespace gfx
+
+namespace screen_ai {
+
+// Converts serialized VisualAnnotation proto from ScreenAI to AXTreeUpdate. The
+// argument `image_rect` is the bounding box of the image from which the visual
+// annotation was created.
+ui::AXTreeUpdate ScreenAIVisualAnnotationToAXTreeUpdate(
+ const std::string& serialized_proto,
+ const gfx::Rect& image_rect);
+
+// Converts an AXTreeUpdate snapshot to serialized ViewHierarchy proto for
+// Screen2X.
+std::string Screen2xSnapshotToViewHierarchy(const ui::AXTreeUpdate& snapshot);
+
+} // namespace screen_ai
+
+#endif // COMPONENTS_SERVICES_SCREEN_AI_PROTO_PROTO_CONVERTOR_H_
diff --git a/chromium/components/services/screen_ai/proto/proto_convertor_unittest.cc b/chromium/components/services/screen_ai/proto/proto_convertor_unittest.cc
new file mode 100644
index 00000000000..d9eba124359
--- /dev/null
+++ b/chromium/components/services/screen_ai/proto/proto_convertor_unittest.cc
@@ -0,0 +1,256 @@
+// Copyright 2022 The Chromium 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/services/screen_ai/proto/proto_convertor.h"
+
+#include <string>
+
+#include "components/services/screen_ai/proto/chrome_screen_ai.pb.h"
+#include "components/services/screen_ai/proto/view_hierarchy.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_tree_update.h"
+
+namespace {
+
+constexpr int kMaxChildInTemplate = 3;
+
+// A dummy tree node definition.
+struct NodeTemplate {
+ ui::AXNodeID node_id;
+ int child_count;
+ ui::AXNodeID child_ids[kMaxChildInTemplate];
+};
+
+ui::AXTreeUpdate CreateAXTreeUpdateFromTemplate(int root_id,
+ NodeTemplate* nodes_template,
+ int nodes_count) {
+ ui::AXTreeUpdate update;
+ update.root_id = root_id;
+
+ for (int i = 0; i < nodes_count; i++) {
+ ui::AXNodeData node;
+ node.id = nodes_template[i].node_id;
+ for (int j = 0; j < nodes_template[i].child_count; j++)
+ node.child_ids.push_back(nodes_template[i].child_ids[j]);
+ update.nodes.push_back(node);
+ }
+ return update;
+}
+
+int GetAxNodeID(const ::screenai::UiElement& ui_element) {
+ for (const auto& attribute : ui_element.attributes()) {
+ if (attribute.name() == "/axnode/node_id")
+ return attribute.int_value();
+ }
+ return static_cast<int>(ui::kInvalidAXNodeID);
+}
+
+} // namespace
+
+namespace screen_ai {
+
+using ProtoConvertorTest = testing::Test;
+
+TEST_F(ProtoConvertorTest, ScreenAIVisualAnnotationToAXTreeUpdate) {
+ chrome_screen_ai::VisualAnnotation annotation;
+ gfx::Rect snapshot_bounds(800, 900);
+
+ {
+ chrome_screen_ai::UIComponent* component_0 = annotation.add_ui_component();
+ chrome_screen_ai::UIComponent::PredictedType* type_0 =
+ component_0->mutable_predicted_type();
+ type_0->set_enum_type(chrome_screen_ai::UIComponent::BUTTON);
+ type_0->set_confidence(0.8f);
+ chrome_screen_ai::Rect* box_0 = component_0->mutable_bounding_box();
+ box_0->set_x(0);
+ box_0->set_y(1);
+ box_0->set_width(2);
+ box_0->set_height(3);
+ box_0->set_angle(90.0f);
+
+ chrome_screen_ai::UIComponent* component_1 = annotation.add_ui_component();
+ chrome_screen_ai::UIComponent::PredictedType* type_1 =
+ component_1->mutable_predicted_type();
+ type_1->set_string_type("Presentational");
+ // If the confidence is low, this component together with all its fields
+ // should be ignored.
+ type_1->set_confidence(0.05f);
+ chrome_screen_ai::Rect* box_1 = component_1->mutable_bounding_box();
+ box_1->set_x(0);
+ box_1->set_y(0);
+ box_1->set_width(5);
+ box_1->set_height(5);
+
+ chrome_screen_ai::UIComponent* component_2 = annotation.add_ui_component();
+ chrome_screen_ai::UIComponent::PredictedType* type_2 =
+ component_2->mutable_predicted_type();
+ type_2->set_string_type("Signature");
+ type_2->set_confidence(0.6f);
+ chrome_screen_ai::Rect* box_2 = component_2->mutable_bounding_box();
+ // `x`, `y`, and `angle` should be defaulted to 0 since they are singular
+ // proto3 fields, not proto2.
+ box_2->set_width(5);
+ box_2->set_height(5);
+ }
+
+ {
+ std::string serialized_annotation;
+ ASSERT_TRUE(annotation.SerializeToString(&serialized_annotation));
+ const ui::AXTreeUpdate update = ScreenAIVisualAnnotationToAXTreeUpdate(
+ serialized_annotation, snapshot_bounds);
+
+ const std::string expected_update(
+ "id=1 dialog (0, 0)-(800, 900) child_ids=2,3\n"
+ " id=2 button offset_container_id=1 (0, 1)-(2, 3) transform=[ +0.0000 "
+ "-1.0000 +0.0000 +0.0000 \n"
+ " +1.0000 +0.0000 +0.0000 +0.0000 \n"
+ " +0.0000 +0.0000 +1.0000 +0.0000 \n"
+ " +0.0000 +0.0000 +0.0000 +1.0000 ]\n"
+ "\n"
+ " id=3 genericContainer offset_container_id=1 (0, 0)-(5, 5) "
+ "role_description=Signature\n");
+ EXPECT_EQ(expected_update, update.ToString());
+ }
+
+ {
+ chrome_screen_ai::LineBox* line_0 = annotation.add_lines();
+ line_0->set_direction(chrome_screen_ai::Direction::RIGHT_TO_LEFT);
+
+ chrome_screen_ai::WordBox* word_0_0 = line_0->add_words();
+ chrome_screen_ai::Rect* box_0_0 = word_0_0->mutable_bounding_box();
+ box_0_0->set_x(100);
+ box_0_0->set_y(100);
+ box_0_0->set_width(250);
+ box_0_0->set_height(20);
+ word_0_0->set_utf8_string("Hello");
+ word_0_0->set_has_space_after(true);
+ word_0_0->set_confidence(0.9f);
+ word_0_0->set_estimate_color_success(true);
+ word_0_0->set_background_rgb_value(50000);
+ word_0_0->set_foreground_rgb_value(25000);
+ word_0_0->set_direction(chrome_screen_ai::Direction::RIGHT_TO_LEFT);
+
+ chrome_screen_ai::WordBox* word_0_1 = line_0->add_words();
+ chrome_screen_ai::Rect* box_0_1 = word_0_1->mutable_bounding_box();
+ box_0_1->set_x(350);
+ box_0_1->set_y(100);
+ box_0_1->set_width(250);
+ box_0_1->set_height(20);
+ word_0_1->set_utf8_string("world");
+ // `word_0_1.has_space_after()` should be defaulted to false.
+ word_0_1->set_confidence(0.9f);
+ word_0_1->set_estimate_color_success(true);
+ word_0_1->set_background_rgb_value(50000);
+ word_0_1->set_foreground_rgb_value(25000);
+ word_0_1->set_direction(chrome_screen_ai::Direction::RIGHT_TO_LEFT);
+
+ chrome_screen_ai::Rect* box_0 = line_0->mutable_bounding_box();
+ box_0->set_x(100);
+ box_0->set_y(100);
+ box_0->set_width(500);
+ box_0->set_height(20);
+ line_0->set_utf8_string("Hello world");
+ line_0->set_confidence(0.9f);
+ line_0->set_language("en");
+ line_0->set_block_id(2);
+ line_0->set_order_within_block(1);
+
+ chrome_screen_ai::LineBox* line_1 = annotation.add_lines();
+ line_1->set_confidence(0.0f);
+ // Language, and the line as a whole, should be ignored since the
+ // confidence is zero.
+ line_1->set_language("en");
+ line_1->set_block_id(1);
+ line_1->set_order_within_block(0);
+
+ chrome_screen_ai::LineBox* line_2 = annotation.add_lines();
+ line_2->set_confidence(0.7f);
+ chrome_screen_ai::Rect* box_2 = line_2->mutable_bounding_box();
+ // No bounding box should be created in the AXTree because the height is -5.
+ box_2->set_width(5);
+ box_2->set_height(-5);
+ line_2->set_block_id(2);
+ line_2->set_order_within_block(0);
+ line_2->set_direction(chrome_screen_ai::Direction::UNSPECIFIED);
+ }
+
+ {
+ std::string serialized_annotation;
+ ASSERT_TRUE(annotation.SerializeToString(&serialized_annotation));
+ const ui::AXTreeUpdate update = ScreenAIVisualAnnotationToAXTreeUpdate(
+ serialized_annotation, snapshot_bounds);
+
+ const std::string expected_update(
+ "id=4 dialog (0, 0)-(800, 900) child_ids=5,6\n"
+ " id=5 button offset_container_id=4 (0, 1)-(2, 3) transform=[ +0.0000 "
+ "-1.0000 +0.0000 +0.0000 \n"
+ " +1.0000 +0.0000 +0.0000 +0.0000 \n"
+ " +0.0000 +0.0000 +1.0000 +0.0000 \n"
+ " +0.0000 +0.0000 +0.0000 +1.0000 ]\n"
+ "\n"
+ " id=6 genericContainer offset_container_id=4 (0, 0)-(5, 5) "
+ "role_description=Signature\n"
+ "id=7 region (0, 0)-(800, 900) is_page_breaking_object=true "
+ "child_ids=8,9\n"
+ " id=8 staticText (0, 0)-(5, 0) name_from=contents text_direction=ltr "
+ "name=\n"
+ " id=9 staticText offset_container_id=7 (100, 100)-(500, 20) "
+ "name_from=contents text_direction=rtl name=Hello world language=en "
+ "child_ids=10,11\n"
+ " id=10 inlineTextBox offset_container_id=9 (100, 100)-(250, 20) "
+ "name_from=contents background_color=&C350 color=&61A8 "
+ "text_direction=rtl name=Hello \n"
+ " id=11 inlineTextBox offset_container_id=9 (350, 100)-(250, 20) "
+ "name_from=contents background_color=&C350 color=&61A8 "
+ "text_direction=rtl name=world\n");
+ EXPECT_EQ(expected_update, update.ToString());
+ }
+}
+
+// Tests if the given tree is properly traversed and new ids are assigned.
+TEST_F(ProtoConvertorTest, PreOrderTreeGeneration) {
+ // Input Tree:
+ // +-- 1
+ // +-- 2
+ // +-- 7
+ // +-- 8
+ // +-- 3
+ // +-- 4
+ // +-- 5
+ // +-- 6
+ // +-- 9
+ // +-- -20
+
+ // Input tree is added in shuffled order to avoid order assumption.
+ NodeTemplate input_tree[] = {
+ {4, 3, {5, 6, 9}}, {1, 3, {2, 4, -20}}, {7, 0, {}}, {8, 1, {3}},
+ {6, 0, {}}, {5, 0, {}}, {3, 0, {}}, {2, 2, {7, 8}},
+ {9, 0, {}}, {-20, 0, {}}};
+ const int nodes_count = sizeof(input_tree) / sizeof(NodeTemplate);
+
+ // Expected order of nodes in the output.
+ int expected_order[] = {1, 2, 7, 8, 3, 4, 5, 6, 9, -20};
+
+ // Create the tree, convert it, and decode from proto.
+ ui::AXTreeUpdate tree_update =
+ CreateAXTreeUpdateFromTemplate(1, input_tree, nodes_count);
+ std::string serialized_proto = Screen2xSnapshotToViewHierarchy(tree_update);
+ screenai::ViewHierarchy view_hierarchy;
+ ASSERT_TRUE(view_hierarchy.ParseFromString(serialized_proto));
+
+ // Verify.
+ EXPECT_EQ(view_hierarchy.ui_elements().size(), nodes_count);
+ for (int i = 0; i < nodes_count; i++) {
+ const screenai::UiElement& ui_element = view_hierarchy.ui_elements(i);
+
+ // Expect node to be correctly re-ordered.
+ EXPECT_EQ(expected_order[i], GetAxNodeID(ui_element));
+
+ // Expect node at index 'i' has id 'i'
+ EXPECT_EQ(ui_element.id(), i);
+ }
+}
+
+} // namespace screen_ai
diff --git a/chromium/components/services/screen_ai/proto/view_hierarchy.proto b/chromium/components/services/screen_ai/proto/view_hierarchy.proto
new file mode 100644
index 00000000000..acf2ccd896c
--- /dev/null
+++ b/chromium/components/services/screen_ai/proto/view_hierarchy.proto
@@ -0,0 +1,109 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This proto is copied from the following proto, do not change.
+// google3/knowledge/cerebra/sense/im2query/screenai/proto/view_hierarchy.proto
+syntax = "proto2";
+
+package screenai;
+
+import "dimension.proto";
+
+option optimize_for = LITE_RUNTIME;
+
+// Represents a hierarchical data (e.g. tree structure) in general, in which
+// UiElement represents a node.
+// Next ID: 3.
+message ViewHierarchy {
+ reserved 1;
+ repeated UiElement ui_elements = 2;
+}
+
+// A single UI element. An element is relatively general and includes containers
+// (e.g. LinearLayout) or individual visible elements (e.g. button, text label).
+// Next ID: 10.
+message UiElement {
+ // Unique positive number in the entire view hierarchy.
+ optional int32 id = 1 [default = -1];
+
+ optional UiElementType type = 2;
+
+ optional int32 parent_id = 3 [default = -1];
+
+ // All child elements of this element.
+ // The order of child ids is preserved from original view hierarchy tree.
+ repeated int32 child_ids = 4 [packed = true];
+
+ // Bounding box in screenshot, normalized by the image dimensions. This
+ // dimension is generally the captured screenshot dimension or the dimension
+ // of the root element if it covers the whole visible area.
+ // For example: The viewport of the screen is: 1440x1080. We capture a long
+ // screen that contains scrolled content, which has size 1440x8500.
+ // Then a container with coordinates:
+ // left: 50, right 1390, top 1300, bottom 2000
+ // would be normalized to:
+ // left: 50 / 1440, right 1390 / 1440, top 1300 / 8500, bottom 2000 / 8500.
+ optional BoundingBox bounding_box = 6;
+
+ // Bounding box in screenshot. Bounding box in pixels (non-normalized).
+ optional BoundingBoxPixels bounding_box_pixels = 8;
+
+ // A list of attributes for this element. Includes common attributes such as
+ // 'enabled', 'text'.
+ repeated UiElementAttribute attributes = 7;
+
+ // Define the application-specific information below.
+
+ // Information about data extracted from a Chrome-based application.
+ optional ChromeInfo chrome_info = 9;
+}
+
+enum UiElementType {
+ UNKNOWN = 0;
+ ROOT = 1;
+ WINDOW = 2;
+ VIEW = 3;
+}
+
+// An UiElement attribute.
+// Next ID: 9.
+message UiElementAttribute {
+ // The name of the attribute, for example 'text', 'focusable',
+ // 'contentDescription'.
+ optional string name = 1;
+
+ // The value of the attribute.
+ // This format only supports simple types. Some types for example dimension
+ // '123 px' should be stored as string and the application should handle
+ // parsing it. For other types for example reference a custom attribute
+ // message should be defined.
+ oneof value {
+ bool bool_value = 2;
+ int32 int_value = 3;
+ string string_value = 4;
+ float float_value = 5;
+ IntList int_list_value = 6;
+ StringList string_list_value = 7;
+ FloatList float_list_value = 8;
+ }
+}
+
+message IntList {
+ repeated int32 value = 1 [packed = true];
+}
+
+message StringList {
+ repeated string value = 1;
+}
+
+message FloatList {
+ repeated float value = 1;
+}
+
+// Chrome specific information.
+message ChromeInfo {
+ // Backend DOM Node ID from which the UiElement is extracted.
+ // Sample usage: to trace back the original DOM Node of an UiElement.
+ optional int32 dom_node_id = 1 [default = -1];
+}
diff --git a/chromium/components/services/screen_ai/public/cpp/BUILD.gn b/chromium/components/services/screen_ai/public/cpp/BUILD.gn
index fa29f65293b..7295a13a688 100644
--- a/chromium/components/services/screen_ai/public/cpp/BUILD.gn
+++ b/chromium/components/services/screen_ai/public/cpp/BUILD.gn
@@ -2,6 +2,22 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+source_set("screen_ai_service_router_factory") {
+ sources = [
+ "screen_ai_service_router.cc",
+ "screen_ai_service_router.h",
+ "screen_ai_service_router_factory.cc",
+ "screen_ai_service_router_factory.h",
+ ]
+
+ deps = [
+ "//components/keyed_service/content",
+ "//components/keyed_service/core",
+ "//components/services/screen_ai/public/mojom",
+ "//content/public/browser",
+ ]
+}
+
source_set("utilities") {
sources = [
"pref_names.cc",
diff --git a/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router.cc b/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router.cc
new file mode 100644
index 00000000000..8f36d144e6e
--- /dev/null
+++ b/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router.cc
@@ -0,0 +1,42 @@
+// Copyright 2022 The Chromium 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/services/screen_ai/public/cpp/screen_ai_service_router.h"
+
+#include "content/public/browser/service_process_host.h"
+
+namespace screen_ai {
+
+ScreenAIServiceRouter::ScreenAIServiceRouter() = default;
+ScreenAIServiceRouter::~ScreenAIServiceRouter() = default;
+
+void ScreenAIServiceRouter::BindScreenAIAnnotator(
+ mojo::PendingReceiver<screen_ai::mojom::ScreenAIAnnotator> receiver) {
+ LaunchIfNotRunning();
+
+ if (screen_ai_service_.is_bound())
+ screen_ai_service_->BindAnnotator(std::move(receiver));
+}
+
+void ScreenAIServiceRouter::BindMainContentExtractor(
+ mojo::PendingReceiver<screen_ai::mojom::Screen2xMainContentExtractor>
+ receiver) {
+ LaunchIfNotRunning();
+
+ if (screen_ai_service_.is_bound())
+ screen_ai_service_->BindMainContentExtractor(std::move(receiver));
+}
+
+void ScreenAIServiceRouter::LaunchIfNotRunning() {
+ if (screen_ai_service_.is_bound())
+ return;
+
+ content::ServiceProcessHost::Launch(
+ screen_ai_service_.BindNewPipeAndPassReceiver(),
+ content::ServiceProcessHost::Options()
+ .WithDisplayName("Screen AI Service")
+ .Pass());
+}
+
+} // namespace screen_ai
diff --git a/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router.h b/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router.h
new file mode 100644
index 00000000000..2d85ace3098
--- /dev/null
+++ b/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_SCREEN_AI_SERVICE_ROUTER_H_
+#define COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_SCREEN_AI_SERVICE_ROUTER_H_
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/services/screen_ai/public/mojom/screen_ai_service.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace screen_ai {
+
+class ScreenAIServiceRouter : public KeyedService {
+ public:
+ ScreenAIServiceRouter();
+ ScreenAIServiceRouter(const ScreenAIServiceRouter&) = delete;
+ ScreenAIServiceRouter& operator=(const ScreenAIServiceRouter&) = delete;
+ ~ScreenAIServiceRouter() override;
+
+ void BindScreenAIAnnotator(
+ mojo::PendingReceiver<screen_ai::mojom::ScreenAIAnnotator> receiver);
+
+ void BindMainContentExtractor(
+ mojo::PendingReceiver<screen_ai::mojom::Screen2xMainContentExtractor>
+ receiver);
+
+ void LaunchIfNotRunning();
+
+ private:
+ mojo::Remote<mojom::ScreenAIService> screen_ai_service_;
+};
+
+} // namespace screen_ai
+
+#endif // COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_SCREEN_AI_SERVICE_ROUTER_H_
diff --git a/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router_factory.cc b/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router_factory.cc
new file mode 100644
index 00000000000..6dc4a7c968e
--- /dev/null
+++ b/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router_factory.cc
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium 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/services/screen_ai/public/cpp/screen_ai_service_router_factory.h"
+
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/services/screen_ai/public/cpp/screen_ai_service_router.h"
+#include "content/public/browser/browser_context.h"
+
+// static
+screen_ai::ScreenAIServiceRouter*
+ScreenAIServiceRouterFactory::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return static_cast<screen_ai::ScreenAIServiceRouter*>(
+ GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+// static
+ScreenAIServiceRouterFactory* ScreenAIServiceRouterFactory::GetInstance() {
+ static base::NoDestructor<ScreenAIServiceRouterFactory> instance;
+ return instance.get();
+}
+
+ScreenAIServiceRouterFactory::ScreenAIServiceRouterFactory()
+ : BrowserContextKeyedServiceFactory(
+ "ScreenAIService",
+ BrowserContextDependencyManager::GetInstance()) {}
+
+ScreenAIServiceRouterFactory::~ScreenAIServiceRouterFactory() = default;
+
+KeyedService* ScreenAIServiceRouterFactory::BuildServiceInstanceFor(
+ content::BrowserContext* /*context*/) const {
+ return new screen_ai::ScreenAIServiceRouter();
+}
+
+// Incognito profiles should use their own instance.
+content::BrowserContext* ScreenAIServiceRouterFactory::GetBrowserContextToUse(
+ content::BrowserContext* context) const {
+ return context;
+} \ No newline at end of file
diff --git a/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router_factory.h b/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router_factory.h
new file mode 100644
index 00000000000..1834c88578f
--- /dev/null
+++ b/chromium/components/services/screen_ai/public/cpp/screen_ai_service_router_factory.h
@@ -0,0 +1,39 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_SCREEN_AI_SERVICE_ROUTER_FACTORY_H_
+#define COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_SCREEN_AI_SERVICE_ROUTER_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace screen_ai {
+class ScreenAIServiceRouter;
+}
+
+// Factory to get or create an instance of ScreenAIServiceRouter for a Profile.
+class ScreenAIServiceRouterFactory : public BrowserContextKeyedServiceFactory {
+ public:
+ static screen_ai::ScreenAIServiceRouter* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ private:
+ friend class base::NoDestructor<ScreenAIServiceRouterFactory>;
+ static ScreenAIServiceRouterFactory* GetInstance();
+
+ ScreenAIServiceRouterFactory();
+ ~ScreenAIServiceRouterFactory() override;
+
+ // BrowserContextKeyedServiceFactory:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const override;
+ content::BrowserContext* GetBrowserContextToUse(
+ content::BrowserContext* context) const override;
+};
+
+#endif // COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_SCREEN_AI_SERVICE_ROUTER_FACTORY_H_ \ No newline at end of file
diff --git a/chromium/components/services/screen_ai/public/cpp/utilities.cc b/chromium/components/services/screen_ai/public/cpp/utilities.cc
index 13a3162c39f..5062f7dfe16 100644
--- a/chromium/components/services/screen_ai/public/cpp/utilities.cc
+++ b/chromium/components/services/screen_ai/public/cpp/utilities.cc
@@ -17,17 +17,25 @@ const base::FilePath::CharType kScreenAISubDirName[] =
const base::FilePath::CharType kScreenAILibraryFileName[] =
FILE_PATH_LITERAL("libchrome_screen_ai.so");
+
+enum {
+ PATH_START = 13000,
+
+ DIR_SCREEN_AI_LIBRARY, // Path from which ScreenAI library is preloaded.
+
+ PATH_END
+};
+
} // namespace
-const base::FilePath GetRelativeInstallDir() {
+base::FilePath GetRelativeInstallDir() {
return base::FilePath(kScreenAISubDirName);
}
-const base::FilePath GetLibraryFilePath() {
+base::FilePath GetLatestLibraryFilePath() {
base::FilePath components_dir;
base::PathService::Get(component_updater::DIR_COMPONENT_USER,
&components_dir);
-
if (components_dir.empty())
return base::FilePath();
@@ -50,4 +58,14 @@ const base::FilePath GetLibraryFilePath() {
return library_path;
}
+void SetPreloadedLibraryFilePath(const base::FilePath& path) {
+ base::PathService::Override(DIR_SCREEN_AI_LIBRARY, path);
+}
+
+base::FilePath GetPreloadedLibraryFilePath() {
+ base::FilePath path;
+ base::PathService::Get(DIR_SCREEN_AI_LIBRARY, &path);
+ return path;
+}
+
} // namespace screen_ai \ No newline at end of file
diff --git a/chromium/components/services/screen_ai/public/cpp/utilities.h b/chromium/components/services/screen_ai/public/cpp/utilities.h
index 45d8c70ae65..b1e13f9afe8 100644
--- a/chromium/components/services/screen_ai/public/cpp/utilities.h
+++ b/chromium/components/services/screen_ai/public/cpp/utilities.h
@@ -10,10 +10,17 @@
namespace screen_ai {
// Get the absolute path of the ScreenAI library.
-const base::FilePath GetLibraryFilePath();
+base::FilePath GetLatestLibraryFilePath();
// Returns the install directory relative to components folder.
-const base::FilePath GetRelativeInstallDir();
+base::FilePath GetRelativeInstallDir();
+
+// Stores the path to the preloaded library. This value is set when the library
+// is loaded prior to sandboxing in every session.
+void SetPreloadedLibraryFilePath(const base::FilePath& path);
+
+// Returns the preloaded path for the library.
+base::FilePath GetPreloadedLibraryFilePath();
} // namespace screen_ai
#endif // COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_UTILITIES_H_
diff --git a/chromium/components/services/screen_ai/public/mojom/BUILD.gn b/chromium/components/services/screen_ai/public/mojom/BUILD.gn
index cd7c0872627..697bec9f19e 100644
--- a/chromium/components/services/screen_ai/public/mojom/BUILD.gn
+++ b/chromium/components/services/screen_ai/public/mojom/BUILD.gn
@@ -10,6 +10,6 @@ mojom("mojom") {
public_deps = [
"//media/mojo/mojom",
"//sandbox/policy/mojom",
- "//ui/accessibility:ax_enums_mojo",
+ "//ui/accessibility/mojom",
]
}
diff --git a/chromium/components/services/screen_ai/public/mojom/screen_ai_service.mojom b/chromium/components/services/screen_ai/public/mojom/screen_ai_service.mojom
index 029332a7761..f2a5679e0ac 100644
--- a/chromium/components/services/screen_ai/public/mojom/screen_ai_service.mojom
+++ b/chromium/components/services/screen_ai/public/mojom/screen_ai_service.mojom
@@ -6,51 +6,45 @@ module screen_ai.mojom;
import "sandbox/policy/mojom/sandbox.mojom";
import "skia/public/mojom/bitmap.mojom";
-import "ui/gfx/geometry/mojom/geometry.mojom";
-import "ui/accessibility/ax_enums.mojom";
+import "ui/accessibility/mojom/ax_tree_update.mojom";
-// An object detected by the Screen AI library.
-struct Node {
- // Rectangle covering the detected object, with respect to the top-left of
- // the image.
- gfx.mojom.Rect rect;
-
- // Detected type of the object.
- ax.mojom.Role role;
-
- // Confidence level of detection, [0..1].
- float confidence;
-};
-
-// Indicates the result of image processing service.
-enum ErrorType {
- // No error.
- kOK = 0,
-
- // Could not find the library.
- kFailedLibraryNotFound = 1,
-
- // Library could not process image.
- kFailedProcessingImage = 2,
-};
-
-// Main interface a client uses for Screen AI services. Each browser mainframe
-// has its own ScreenAIAnnotator and all ScreenAIAnnotators of one profile use
-// one ScreenAIService.
-// Requests are sent from browsers in RenderFrameHostImpl class.
+// Main interface a client uses for Visual Annotation function of Screen AI
+// service. Each browser can have one AXScreenAIAnnotator which contains an
+// ScreenAIAnnotator.
+// All annotators of one browser profile use one ScreenAIService.
interface ScreenAIAnnotator {
- // Receives a snapshot, schedules image processing, and returns the error
- // type (if any) and an array of detected nodes.
+ // Receives a snapshot, schedules image processing, and returns the detected
+ // items as an accessibility tree update.
Annotate(skia.mojom.BitmapN32 image) =>
- (ErrorType error, array<Node> nodes);
+ (ax.mojom.AXTreeUpdate updates);
+};
+
+// Main interface a client uses for Main Content Extraction function of Screen
+// AI service. Each RenderFrameImpl can have one AXTreeDistiller which contains
+// an Screen2xMainContentExtractor.
+// All interfaces of one browser profile use one ScreenAIService.
+interface Screen2xMainContentExtractor {
+ // Receives the accessibility tree, schedules processing, and returns the main
+ // content of the given tree.
+ // TODO(https://crbug.com/1278249): Add an interface for sending back the
+ // result.
+ ExtractMainContent(ax.mojom.AXTreeUpdate snapshot) =>
+ (array<int32> content_node_ids);
};
// The service runs in a sandboxed process to run Screen AI service library. The
-// library provides an image processing module to analyze snapshots of the
-// browser and add more details to the accessibility tree.
+// library provides two AI modules:
+// 1) An image processing module to analyze snapshots of the browser and add
+// more details to the accessibility tree.
+// 2) A text processing module that receives the accessibility tree and
+// returns the main content of the tree.
[ServiceSandbox=sandbox.mojom.Sandbox.kScreenAI]
interface ScreenAIService {
// Binds a new annotator to the service.
BindAnnotator(pending_receiver<ScreenAIAnnotator> annotator);
+
+ // Binds a new main content extractor to the service.
+ BindMainContentExtractor(pending_receiver<Screen2xMainContentExtractor>
+ main_content_extractor);
};
diff --git a/chromium/components/services/screen_ai/sandbox/screen_ai_sandbox_hook_linux.cc b/chromium/components/services/screen_ai/sandbox/screen_ai_sandbox_hook_linux.cc
index 64eff90642a..7ae9b0b0d61 100644
--- a/chromium/components/services/screen_ai/sandbox/screen_ai_sandbox_hook_linux.cc
+++ b/chromium/components/services/screen_ai/sandbox/screen_ai_sandbox_hook_linux.cc
@@ -10,6 +10,7 @@
#include "components/services/screen_ai/public/cpp/utilities.h"
#include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+#include "ui/accessibility/accessibility_features.h"
using sandbox::syscall_broker::BrokerFilePermission;
using sandbox::syscall_broker::MakeBrokerCommandSet;
@@ -17,10 +18,7 @@ using sandbox::syscall_broker::MakeBrokerCommandSet;
namespace screen_ai {
bool ScreenAIPreSandboxHook(sandbox::policy::SandboxLinux::Options options) {
- // TODO(https://crbug.com/1278249): Ensure this is the same version of the
- // library that is used in ScreenAIService and component updater has not added
- // a newer version between the two steps.
- const base::FilePath library_path = screen_ai::GetLibraryFilePath();
+ base::FilePath library_path = screen_ai::GetLatestLibraryFilePath();
if (library_path.empty()) {
VLOG(1) << "Screen AI library not found.";
} else {
@@ -29,11 +27,14 @@ bool ScreenAIPreSandboxHook(sandbox::policy::SandboxLinux::Options options) {
// The library is delivered by the component updater. If it is not available
// we cannot do anything about it here. The requests to the service will
// fail later as the library does not exist.
- if (!screen_ai_library)
+ if (!screen_ai_library) {
VLOG(1) << dlerror();
- else
+ library_path.clear();
+ } else {
VLOG(2) << "Screen AI library loaded pre-sandboxing:" << library_path;
+ }
}
+ screen_ai::SetPreloadedLibraryFilePath(library_path);
auto* instance = sandbox::policy::SandboxLinux::GetInstance();
@@ -41,6 +42,18 @@ bool ScreenAIPreSandboxHook(sandbox::policy::SandboxLinux::Options options) {
BrokerFilePermission::ReadOnly("/dev/urandom"),
BrokerFilePermission::ReadOnly("/proc/meminfo")};
+ // The models are in the same folder as the library, and the library requires
+ // read access for them.
+ if (!library_path.empty()) {
+ permissions.push_back(BrokerFilePermission::ReadOnlyRecursive(
+ library_path.DirName().MaybeAsASCII() + base::FilePath::kSeparators));
+ }
+
+ if (features::IsScreenAIDebugModeEnabled()) {
+ permissions.push_back(
+ BrokerFilePermission::ReadWriteCreateRecursive("/tmp/"));
+ }
+
instance->StartBrokerProcess(
MakeBrokerCommandSet({sandbox::syscall_broker::COMMAND_ACCESS,
sandbox::syscall_broker::COMMAND_OPEN}),
diff --git a/chromium/components/services/screen_ai/screen_ai_service_impl.cc b/chromium/components/services/screen_ai/screen_ai_service_impl.cc
index 72e2b5c9fb0..b5833f72526 100644
--- a/chromium/components/services/screen_ai/screen_ai_service_impl.cc
+++ b/chromium/components/services/screen_ai/screen_ai_service_impl.cc
@@ -4,33 +4,34 @@
#include "components/services/screen_ai/screen_ai_service_impl.h"
-#include "components/services/screen_ai/proto/chrome_screen_ai.pb.h"
+#include "base/process/process.h"
+#include "components/services/screen_ai/proto/proto_convertor.h"
#include "components/services/screen_ai/public/cpp/utilities.h"
-#include "components/services/screen_ai/public/mojom/screen_ai_service.mojom.h"
-
-namespace {
-
-// The minimum confidence level that a Screen AI annotation should have to be
-// accepted.
-// TODO(https://crbug.com/1278249): Add experiment or heuristics to better
-// adjust this threshold.
-const float kScreenAIMinConfidenceThreshold = 0.1;
-
-} // namespace
+#include "ui/accessibility/accessibility_features.h"
+#include "ui/gfx/geometry/rect_f.h"
namespace screen_ai {
ScreenAIService::ScreenAIService(
mojo::PendingReceiver<mojom::ScreenAIService> receiver)
- : library_(screen_ai::GetLibraryFilePath()),
- init_function_(reinterpret_cast<ScreenAIInitFunction>(
- library_.GetFunctionPointer("Init"))),
- annotator_function_(reinterpret_cast<ScreenAIAnnotateFunction>(
+ : library_(GetPreloadedLibraryFilePath()),
+ init_function_(
+ reinterpret_cast<InitFunction>(library_.GetFunctionPointer("Init"))),
+ annotate_function_(reinterpret_cast<AnnotateFunction>(
library_.GetFunctionPointer("Annotate"))),
+ extract_main_content_function_(
+ reinterpret_cast<ExtractMainContentFunction>(
+ library_.GetFunctionPointer("ExtractMainContent"))),
receiver_(this, std::move(receiver)) {
- if (!init_function_ || !init_function_()) {
- VLOG(1) << "Screen AI library initialization failed.";
- annotator_function_ = nullptr;
+ DCHECK(init_function_ && annotate_function_ &&
+ extract_main_content_function_);
+ if (!init_function_(features::IsScreenAIVisualAnnotationsEnabled(),
+ features::IsReadAnythingWithScreen2xEnabled(),
+ features::IsScreenAIDebugModeEnabled(),
+ GetPreloadedLibraryFilePath().DirName().MaybeAsASCII())) {
+ // TODO(https://crbug.com/1278249): Add UMA metrics to monitor failures.
+ VLOG(0) << "Screen AI library initialization failed.";
+ base::Process::TerminateCurrentProcessImmediately(-1);
}
}
@@ -41,72 +42,47 @@ void ScreenAIService::BindAnnotator(
screen_ai_annotators_.Add(this, std::move(annotator));
}
+void ScreenAIService::BindMainContentExtractor(
+ mojo::PendingReceiver<mojom::Screen2xMainContentExtractor>
+ main_content_extractor) {
+ screen_2x_main_content_extractors_.Add(this,
+ std::move(main_content_extractor));
+}
+
void ScreenAIService::Annotate(const SkBitmap& image,
AnnotationCallback callback) {
- std::vector<mojom::Node> annotations;
- mojom::ErrorType error = mojom::ErrorType::kOK;
-
- if (annotator_function_) {
- VLOG(2) << "Screen AI library starting to process " << image.width() << "x"
- << image.height() << " snapshot.";
-
- std::string annotation_text;
- // TODO(https://crbug.com/1278249): Consider adding a signature that
- // verifies the data integrity and source.
- // TODO(https://crbug.com/1278249): Consider replacing the input with a data
- // item that includes data size.
- if (annotator_function_(
- static_cast<const unsigned char*>(image.getPixels()), image.width(),
- image.height(), annotation_text)) {
- annotations = DecodeProto(annotation_text);
- } else {
- VLOG(1) << "Screen AI library could not process snapshot.";
- error = mojom::ErrorType::kFailedProcessingImage;
- }
+ ui::AXTreeUpdate update;
+
+ VLOG(2) << "Screen AI library starting to process " << image.width() << "x"
+ << image.height() << " snapshot.";
+
+ std::string annotation_text;
+ // TODO(https://crbug.com/1278249): Consider adding a signature that
+ // verifies the data integrity and source.
+ if (annotate_function_(image, annotation_text)) {
+ gfx::Rect image_rect(image.width(), image.height());
+ update =
+ ScreenAIVisualAnnotationToAXTreeUpdate(annotation_text, image_rect);
} else {
- error = mojom::ErrorType::kFailedLibraryNotFound;
+ VLOG(1) << "Screen AI library could not process snapshot.";
}
- // TODO(https://crbug.com/1278249): Convert |annotations| array to an
- // AxTreeSource and return it.
- VLOG(2) << "Screen AI library has " << annotations.size() << " annotations.";
-
- std::move(callback).Run(error, std::vector<mojom::NodePtr>());
+ std::move(callback).Run(update);
}
-std::vector<mojom::Node> ScreenAIService::DecodeProto(
- const std::string& serialized_proto) {
- // TODO(https://crbug.com/1278249): Consider using AxNodeData here.
- std::vector<mojom::Node> annotations;
+void ScreenAIService::ExtractMainContent(const ui::AXTreeUpdate& snapshot,
+ ContentExtractionCallback callback) {
+ std::string serialized_snapshot = Screen2xSnapshotToViewHierarchy(snapshot);
+ std::vector<int32_t> content_node_ids;
- // TODO(https://crbug.com/1278249): Consider adding version checking.
- chrome_screen_ai::VisualAnnotation results;
- if (!results.ParseFromString(serialized_proto)) {
- VLOG(1) << "Could not parse Screen AI library output.";
- return annotations;
- }
-
- for (const auto& uic : results.ui_component()) {
- float score = uic.predicted_type().score();
- if (score < kScreenAIMinConfidenceThreshold)
- continue;
-
- chrome_screen_ai::UIComponent::Type original_type =
- uic.predicted_type().type();
- ::gfx::Rect rect(uic.bounding_box().x(), uic.bounding_box().y(),
- uic.bounding_box().width(), uic.bounding_box().height());
-
- // TODO(https://crbug.com/1278249): Add tests to ensure these two types
- // match.
- ax::mojom::Role role = static_cast<ax::mojom::Role>(original_type);
-
- annotations.emplace_back(screen_ai::mojom::Node(rect, role, score));
+ if (!extract_main_content_function_(serialized_snapshot, content_node_ids)) {
+ VLOG(1) << "Screen2x did not return main content.";
+ } else {
+ VLOG(2) << "Screen2x returned " << content_node_ids.size() << " node ids:";
+ for (int32_t i : content_node_ids)
+ VLOG(2) << i;
}
-
- // TODO(https://crbug.com/1278249): Add UMA metrics to record the number of
- // annotations, item types, confidence levels, etc.
-
- return annotations;
+ std::move(callback).Run(content_node_ids);
}
} // namespace screen_ai
diff --git a/chromium/components/services/screen_ai/screen_ai_service_impl.h b/chromium/components/services/screen_ai/screen_ai_service_impl.h
index c9cfa2d933c..ba21a8fcabe 100644
--- a/chromium/components/services/screen_ai/screen_ai_service_impl.h
+++ b/chromium/components/services/screen_ai/screen_ai_service_impl.h
@@ -14,15 +14,17 @@
namespace screen_ai {
-using AnnotationCallback =
- base::OnceCallback<void(mojom::ErrorType, std::vector<mojom::NodePtr>)>;
+using AnnotationCallback = base::OnceCallback<void(const ui::AXTreeUpdate&)>;
+using ContentExtractionCallback =
+ base::OnceCallback<void(const std::vector<int32_t>&)>;
// Sends the snapshot to a local machine learning library to get annotations
// that can help in updating the accessibility tree. See more in:
// google3/chrome/chromeos/accessibility/machine_intelligence/
// chrome_screen_ai/README.md
class ScreenAIService : public mojom::ScreenAIService,
- public mojom::ScreenAIAnnotator {
+ public mojom::ScreenAIAnnotator,
+ public mojom::Screen2xMainContentExtractor {
public:
explicit ScreenAIService(
mojo::PendingReceiver<mojom::ScreenAIService> receiver);
@@ -36,27 +38,42 @@ class ScreenAIService : public mojom::ScreenAIService,
// mojom::ScreenAIAnnotator
void Annotate(const SkBitmap& image, AnnotationCallback callback) override;
+ // mojom::Screen2xMainContentExtractor
+ void ExtractMainContent(const ui::AXTreeUpdate& snapshot,
+ ContentExtractionCallback callback) override;
+
// mojom::ScreenAIService
void BindAnnotator(
mojo::PendingReceiver<mojom::ScreenAIAnnotator> annotator) override;
- // Converts the serialized proto from Screen AI library to a vector of
- // screen_ai::mojom::Node.
- std::vector<mojom::Node> DecodeProto(const std::string& serialized_proto);
+ // mojom::ScreenAIService
+ void BindMainContentExtractor(
+ mojo::PendingReceiver<mojom::Screen2xMainContentExtractor>
+ main_content_extractor) override;
+
+ typedef bool (*InitFunction)(bool /*init_visual_annotations*/,
+ bool /*init_main_content_extraction*/,
+ bool /*debug_mode*/,
+ const std::string& models_path);
+ InitFunction init_function_;
- typedef bool (*ScreenAIInitFunction)();
- ScreenAIInitFunction init_function_;
+ typedef bool (*AnnotateFunction)(const SkBitmap& /*image*/,
+ std::string& /*annotation_text*/);
+ AnnotateFunction annotate_function_;
- typedef bool (*ScreenAIAnnotateFunction)(const unsigned char* /*png_pixels*/,
- int /*image_width*/,
- int /*image_height*/,
- std::string& /*annotation_text*/);
- ScreenAIAnnotateFunction annotator_function_;
+ typedef bool (*ExtractMainContentFunction)(
+ const std::string& /*serialized_snapshot*/,
+ std::vector<int32_t>& /*content_node_ids*/);
+ ExtractMainContentFunction extract_main_content_function_;
mojo::Receiver<mojom::ScreenAIService> receiver_;
- // The set of receivers used to receive messages from the renderer clients.
+ // The set of receivers used to receive messages from annotators.
mojo::ReceiverSet<mojom::ScreenAIAnnotator> screen_ai_annotators_;
+
+ // The set of receivers used to receive messages from main content extractors.
+ mojo::ReceiverSet<mojom::Screen2xMainContentExtractor>
+ screen_2x_main_content_extractors_;
};
} // namespace screen_ai
diff --git a/chromium/components/services/storage/BUILD.gn b/chromium/components/services/storage/BUILD.gn
index 67d64e597fd..53a59c8af49 100644
--- a/chromium/components/services/storage/BUILD.gn
+++ b/chromium/components/services/storage/BUILD.gn
@@ -88,6 +88,7 @@ source_set("storage") {
"//base",
"//components/services/storage/dom_storage:local_storage_proto",
"//components/services/storage/indexed_db/scopes:scopes_metadata_proto",
+ "//components/services/storage/privileged/mojom",
"//components/services/storage/public/mojom",
"//components/services/storage/service_worker:service_worker_proto",
"//components/services/storage/shared_storage/public/mojom",
@@ -164,6 +165,7 @@ bundle_data("tests_bundle_data") {
"//components/test/data/storage/shared_storage.v0.init_too_old.sql",
"//components/test/data/storage/shared_storage.v1.init_too_new.sql",
"//components/test/data/storage/shared_storage.v1.iterator.sql",
+ "//components/test/data/storage/shared_storage.v1.no_budget_table.sql",
"//components/test/data/storage/shared_storage.v1.sql",
]
outputs = [ "{{bundle_resources_dir}}/" +
diff --git a/chromium/components/services/storage/dom_storage/local_storage_impl.cc b/chromium/components/services/storage/dom_storage/local_storage_impl.cc
index bf608371d7e..d2569bde3af 100644
--- a/chromium/components/services/storage/dom_storage/local_storage_impl.cc
+++ b/chromium/components/services/storage/dom_storage/local_storage_impl.cc
@@ -18,7 +18,6 @@
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
-#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
@@ -76,16 +75,6 @@ const int64_t kCurrentLocalStorageSchemaVersion = 1;
// database.
const int kCommitErrorThreshold = 8;
-// Limits on the cache size and number of areas in memory, over which the areas
-// are purged.
-#if BUILDFLAG(IS_ANDROID)
-const unsigned kMaxLocalStorageAreaCount = 10;
-const size_t kMaxLocalStorageCacheSize = 2 * 1024 * 1024;
-#else
-const unsigned kMaxLocalStorageAreaCount = 50;
-const size_t kMaxLocalStorageCacheSize = 20 * 1024 * 1024;
-#endif
-
DomStorageDatabase::Key CreateMetaDataKey(
const blink::StorageKey& storage_key) {
std::string storage_key_str = storage_key.SerializeForLocalStorage();
@@ -151,45 +140,6 @@ void DeleteStorageKeys(AsyncDomStorageDatabase* database,
std::move(callback));
}
-enum class CachePurgeReason {
- NotNeeded,
- SizeLimitExceeded,
- AreaCountLimitExceeded,
- InactiveOnLowEndDevice,
- AggressivePurgeTriggered
-};
-
-void RecordCachePurgedHistogram(CachePurgeReason reason,
- size_t purged_size_kib) {
- UMA_HISTOGRAM_COUNTS_100000("LocalStorageContext.CachePurgedInKB",
- purged_size_kib);
- switch (reason) {
- case CachePurgeReason::SizeLimitExceeded:
- UMA_HISTOGRAM_COUNTS_100000(
- "LocalStorageContext.CachePurgedInKB.SizeLimitExceeded",
- purged_size_kib);
- break;
- case CachePurgeReason::AreaCountLimitExceeded:
- UMA_HISTOGRAM_COUNTS_100000(
- "LocalStorageContext.CachePurgedInKB.AreaCountLimitExceeded",
- purged_size_kib);
- break;
- case CachePurgeReason::InactiveOnLowEndDevice:
- UMA_HISTOGRAM_COUNTS_100000(
- "LocalStorageContext.CachePurgedInKB.InactiveOnLowEndDevice",
- purged_size_kib);
- break;
- case CachePurgeReason::AggressivePurgeTriggered:
- UMA_HISTOGRAM_COUNTS_100000(
- "LocalStorageContext.CachePurgedInKB.AggressivePurgeTriggered",
- purged_size_kib);
- break;
- case CachePurgeReason::NotNeeded:
- NOTREACHED();
- break;
- }
-}
-
} // namespace
class LocalStorageImpl::StorageAreaHolder final
@@ -265,21 +215,8 @@ class LocalStorageImpl::StorageAreaHolder final
}
void DidCommit(leveldb::Status status) override {
- UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.CommitResult",
- leveldb_env::GetLevelDBStatusUMAValue(status),
- leveldb_env::LEVELDB_STATUS_MAX);
-
context_->OnCommitResult(status);
}
-
- void OnMapLoaded(leveldb::Status status) override {
- if (!status.ok()) {
- UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.MapLoadError",
- leveldb_env::GetLevelDBStatusUMAValue(status),
- leveldb_env::LEVELDB_STATUS_MAX);
- }
- }
-
void Bind(mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
has_bindings_ = true;
storage_area()->Bind(std::move(receiver));
@@ -455,9 +392,6 @@ void LocalStorageImpl::ShutDown(base::OnceClosure callback) {
}
void LocalStorageImpl::PurgeMemory() {
- size_t total_cache_size, unused_area_count;
- GetStatistics(&total_cache_size, &unused_area_count);
-
for (auto it = areas_.begin(); it != areas_.end();) {
if (it->second->has_bindings()) {
it->second->storage_area()->PurgeMemory();
@@ -466,13 +400,6 @@ void LocalStorageImpl::PurgeMemory() {
it = areas_.erase(it);
}
}
-
- // Track the size of cache purged.
- size_t final_total_cache_size;
- GetStatistics(&final_total_cache_size, &unused_area_count);
- size_t purged_size_kib = (total_cache_size - final_total_cache_size) / 1024;
- RecordCachePurgedHistogram(CachePurgeReason::AggressivePurgeTriggered,
- purged_size_kib);
}
void LocalStorageImpl::ApplyPolicyUpdates(
@@ -496,30 +423,12 @@ void LocalStorageImpl::PurgeUnusedAreasIfNeeded() {
if (!unused_area_count)
return;
- CachePurgeReason purge_reason = CachePurgeReason::NotNeeded;
-
- if (total_cache_size > kMaxLocalStorageCacheSize)
- purge_reason = CachePurgeReason::SizeLimitExceeded;
- else if (areas_.size() > kMaxLocalStorageAreaCount)
- purge_reason = CachePurgeReason::AreaCountLimitExceeded;
- else if (is_low_end_device_)
- purge_reason = CachePurgeReason::InactiveOnLowEndDevice;
-
- if (purge_reason == CachePurgeReason::NotNeeded)
- return;
-
for (auto it = areas_.begin(); it != areas_.end();) {
if (it->second->has_bindings())
++it;
else
it = areas_.erase(it);
}
-
- // Track the size of cache purged.
- size_t final_total_cache_size;
- GetStatistics(&final_total_cache_size, &unused_area_count);
- size_t purged_size_kib = (total_cache_size - final_total_cache_size) / 1024;
- RecordCachePurgedHistogram(purge_reason, purged_size_kib);
}
void LocalStorageImpl::ForceKeepSessionState() {
@@ -638,22 +547,9 @@ void LocalStorageImpl::InitiateConnection(bool in_memory_only) {
void LocalStorageImpl::OnDatabaseOpened(leveldb::Status status) {
if (!status.ok()) {
- UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.DatabaseOpenError",
- leveldb_env::GetLevelDBStatusUMAValue(status),
- leveldb_env::LEVELDB_STATUS_MAX);
- if (in_memory_) {
- UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.DatabaseOpenError.Memory",
- leveldb_env::GetLevelDBStatusUMAValue(status),
- leveldb_env::LEVELDB_STATUS_MAX);
- } else {
- UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.DatabaseOpenError.Disk",
- leveldb_env::GetLevelDBStatusUMAValue(status),
- leveldb_env::LEVELDB_STATUS_MAX);
- }
- LogDatabaseOpenResult(OpenResult::DATABASE_OPEN_FAILED);
// If we failed to open the database, try to delete and recreate the
// database, or ultimately fallback to an in-memory database.
- DeleteAndRecreateDatabase("LocalStorageContext.OpenResultAfterOpenFailed");
+ DeleteAndRecreateDatabase();
return;
}
@@ -681,21 +577,14 @@ void LocalStorageImpl::OnGotDatabaseVersion(leveldb::Status status,
&db_version) ||
db_version < kMinSchemaVersion ||
db_version > kCurrentLocalStorageSchemaVersion) {
- LogDatabaseOpenResult(OpenResult::INVALID_VERSION);
- DeleteAndRecreateDatabase(
- "LocalStorageContext.OpenResultAfterInvalidVersion");
+ DeleteAndRecreateDatabase();
return;
}
database_initialized_ = true;
} else {
// Other read error. Possibly database corruption.
- UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.ReadVersionError",
- leveldb_env::GetLevelDBStatusUMAValue(status),
- leveldb_env::LEVELDB_STATUS_MAX);
- LogDatabaseOpenResult(OpenResult::VERSION_READ_ERROR);
- DeleteAndRecreateDatabase(
- "LocalStorageContext.OpenResultAfterReadVersionError");
+ DeleteAndRecreateDatabase();
return;
}
@@ -712,9 +601,6 @@ void LocalStorageImpl::OnConnectionFinished() {
if (database_)
tried_to_recreate_during_open_ = false;
- LogDatabaseOpenResult(OpenResult::SUCCESS);
- open_result_histogram_ = nullptr;
-
// |database_| should be known to either be valid or invalid by now. Run our
// delayed bindings.
connection_state_ = CONNECTION_FINISHED;
@@ -723,7 +609,7 @@ void LocalStorageImpl::OnConnectionFinished() {
on_database_opened_callbacks_.clear();
}
-void LocalStorageImpl::DeleteAndRecreateDatabase(const char* histogram_name) {
+void LocalStorageImpl::DeleteAndRecreateDatabase() {
if (connection_state_ == CONNECTION_SHUTDOWN)
return;
@@ -736,7 +622,6 @@ void LocalStorageImpl::DeleteAndRecreateDatabase(const char* histogram_name) {
connection_state_ = CONNECTION_IN_PROGRESS;
commit_error_count_ = 0;
database_.reset();
- open_result_histogram_ = histogram_name;
bool recreate_in_memory = false;
@@ -767,9 +652,6 @@ void LocalStorageImpl::DeleteAndRecreateDatabase(const char* histogram_name) {
void LocalStorageImpl::OnDBDestroyed(bool recreate_in_memory,
leveldb::Status status) {
- UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.DestroyDBResult",
- leveldb_env::GetLevelDBStatusUMAValue(status),
- leveldb_env::LEVELDB_STATUS_MAX);
// We're essentially ignoring the status here. Even if destroying failed we
// still want to go ahead and try to recreate.
InitiateConnection(recreate_in_memory);
@@ -783,13 +665,6 @@ LocalStorageImpl::StorageAreaHolder* LocalStorageImpl::GetOrCreateStorageArea(
return found->second.get();
}
- size_t total_cache_size, unused_area_count;
- GetStatistics(&total_cache_size, &unused_area_count);
-
- // Track the total localStorage cache size.
- UMA_HISTOGRAM_COUNTS_100000("LocalStorageContext.CacheSizeInKB",
- total_cache_size / 1024);
-
PurgeUnusedAreasIfNeeded();
auto holder = std::make_unique<StorageAreaHolder>(this, storage_key);
@@ -935,19 +810,7 @@ void LocalStorageImpl::OnCommitResult(leveldb::Status status) {
// Deleting StorageAreas in here could cause more commits (and commit
// errors), but those commits won't reach OnCommitResult because the area
// will have been deleted before the commit finishes.
- DeleteAndRecreateDatabase(
- "LocalStorageContext.OpenResultAfterCommitErrors");
- }
-}
-
-void LocalStorageImpl::LogDatabaseOpenResult(OpenResult result) {
- if (result != OpenResult::SUCCESS) {
- UMA_HISTOGRAM_ENUMERATION("LocalStorageContext.OpenError", result,
- OpenResult::MAX);
- }
- if (open_result_histogram_) {
- base::UmaHistogramEnumeration(open_result_histogram_, result,
- OpenResult::MAX);
+ DeleteAndRecreateDatabase();
}
}
diff --git a/chromium/components/services/storage/dom_storage/local_storage_impl.h b/chromium/components/services/storage/dom_storage/local_storage_impl.h
index fff97e683d3..9ad107b58c6 100644
--- a/chromium/components/services/storage/dom_storage/local_storage_impl.h
+++ b/chromium/components/services/storage/dom_storage/local_storage_impl.h
@@ -118,7 +118,7 @@ class LocalStorageImpl : public base::trace_event::MemoryDumpProvider,
void OnGotDatabaseVersion(leveldb::Status status,
const std::vector<uint8_t>& value);
void OnConnectionFinished();
- void DeleteAndRecreateDatabase(const char* histogram_name);
+ void DeleteAndRecreateDatabase();
void OnDBDestroyed(bool recreate_in_memory, leveldb::Status status);
StorageAreaHolder* GetOrCreateStorageArea(
@@ -138,19 +138,6 @@ class LocalStorageImpl : public base::trace_event::MemoryDumpProvider,
void GetStatistics(size_t* total_cache_size, size_t* unused_area_count);
void OnCommitResult(leveldb::Status status);
- // These values are written to logs. New enum values can be added, but
- // existing enums must never be renumbered or deleted and reused.
- enum class OpenResult {
- DIRECTORY_OPEN_FAILED = 0,
- DATABASE_OPEN_FAILED = 1,
- INVALID_VERSION = 2,
- VERSION_READ_ERROR = 3,
- SUCCESS = 4,
- MAX
- };
-
- void LogDatabaseOpenResult(OpenResult result);
-
const base::FilePath directory_;
enum ConnectionState {
@@ -185,9 +172,6 @@ class LocalStorageImpl : public base::trace_event::MemoryDumpProvider,
// The set of StorageKeys whose storage should be cleared on shutdown.
std::set<blink::StorageKey> storage_keys_to_purge_on_shutdown_;
- // Name of an extra histogram to log open results to, if not null.
- const char* open_result_histogram_ = nullptr;
-
mojo::Receiver<mojom::LocalStorageControl> control_receiver_{this};
base::OnceClosure shutdown_complete_callback_;
diff --git a/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.cc b/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.cc
index 61c9b55f8b4..faa6c5c29f2 100644
--- a/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.cc
+++ b/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.cc
@@ -105,6 +105,36 @@ bool DisjointRangeLockManager::AcquireLocks(
return true;
}
+DisjointRangeLockManager::TestLockResult DisjointRangeLockManager::TestLock(
+ LeveledLockRequest request) {
+ if (request.level < 0 || static_cast<size_t>(request.level) >= locks_.size())
+ return TestLockResult::kInvalid;
+ if (request.range.begin >= request.range.end)
+ return TestLockResult::kInvalid;
+
+ auto& level_locks = locks_[request.level];
+
+ // TODO(dmurph): Remove this weird juggling we do here to support disjoint
+ // ranges once we remove range support. https://crbug.com/1322109
+ auto it = level_locks.find(request.range);
+ if (it == level_locks.end()) {
+ it = level_locks
+ .emplace(std::piecewise_construct,
+ std::forward_as_tuple(request.range),
+ std::forward_as_tuple())
+ .first;
+ }
+
+ if (!IsRangeDisjointFromNeighbors(level_locks, request.range)) {
+ level_locks.erase(it);
+ return TestLockResult::kInvalid;
+ }
+
+ Lock& lock = it->second;
+ return lock.CanBeAcquired(request.type) ? TestLockResult::kFree
+ : TestLockResult::kLocked;
+}
+
void DisjointRangeLockManager::RemoveLockRange(int level,
const LeveledLockRange& range) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -129,11 +159,13 @@ bool DisjointRangeLockManager::AcquireLock(
auto& level_locks = locks_[request.level];
+ // TODO(dmurph): Remove this weird juggling we do here to support disjoint
+ // ranges once we remove range support. https://crbug.com/1322109
auto it = level_locks.find(request.range);
if (it == level_locks.end()) {
it = level_locks
.emplace(std::piecewise_construct,
- std::forward_as_tuple(std::move(request.range)),
+ std::forward_as_tuple(request.range),
std::forward_as_tuple())
.first;
}
diff --git a/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.h b/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.h
index 23bf8d0bd24..b680cd4df65 100644
--- a/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.h
+++ b/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager.h
@@ -58,6 +58,10 @@ class COMPONENT_EXPORT(LOCK_MANAGER) DisjointRangeLockManager
base::WeakPtr<LeveledLockHolder> locks_holder,
LocksAcquiredCallback callback) override;
+ enum class TestLockResult { kInvalid, kLocked, kFree };
+ // Tests to see if the given lock request can be acquired.
+ TestLockResult TestLock(LeveledLockRequest lock_requests);
+
// Remove the given lock range at the given level. The lock range must not be
// in use. Use this if the lock will never be used again.
void RemoveLockRange(int level, const LeveledLockRange& range);
diff --git a/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager_unittest.cc b/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager_unittest.cc
index 78276b453e3..c564ba23754 100644
--- a/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager_unittest.cc
+++ b/chromium/components/services/storage/indexed_db/locks/disjoint_range_lock_manager_unittest.cc
@@ -138,9 +138,15 @@ TEST_F(DisjointRangeLockManagerTest, Shared) {
base::RunLoop loop;
{
BarrierBuilder barrier(loop.QuitClosure());
+ EXPECT_EQ(DisjointRangeLockManager::TestLockResult::kFree,
+ lock_manager.TestLock(
+ {0, range, LeveledLockManager::LockType::kShared}));
EXPECT_TRUE(lock_manager.AcquireLocks(
{{0, range, LeveledLockManager::LockType::kShared}},
locks_holder1.AsWeakPtr(), barrier.AddClosure()));
+ EXPECT_EQ(DisjointRangeLockManager::TestLockResult::kFree,
+ lock_manager.TestLock(
+ {0, range, LeveledLockManager::LockType::kShared}));
EXPECT_TRUE(lock_manager.AcquireLocks(
{{0, range, LeveledLockManager::LockType::kShared}},
locks_holder2.AsWeakPtr(), barrier.AddClosure()));
@@ -180,6 +186,14 @@ TEST_F(DisjointRangeLockManagerTest, SharedAndExclusiveQueuing) {
EXPECT_EQ(2ll, lock_manager.LocksHeldForTesting());
EXPECT_EQ(0ll, lock_manager.RequestsWaitingForTesting());
+ // Exclusive request is blocked, shared is free.
+ EXPECT_EQ(DisjointRangeLockManager::TestLockResult::kLocked,
+ lock_manager.TestLock(
+ {0, range, LeveledLockManager::LockType::kExclusive}));
+ EXPECT_EQ(
+ DisjointRangeLockManager::TestLockResult::kFree,
+ lock_manager.TestLock({0, range, LeveledLockManager::LockType::kShared}));
+
// Both of the following locks should be queued - the exclusive is next in
// line, then the shared lock will come after it.
EXPECT_TRUE(lock_manager.AcquireLocks(
@@ -217,6 +231,14 @@ TEST_F(DisjointRangeLockManagerTest, SharedAndExclusiveQueuing) {
EXPECT_EQ(1ll, lock_manager.LocksHeldForTesting());
EXPECT_EQ(1ll, lock_manager.RequestsWaitingForTesting());
+ // Both exclusive and shared requests are blocked.
+ EXPECT_EQ(DisjointRangeLockManager::TestLockResult::kLocked,
+ lock_manager.TestLock(
+ {0, range, LeveledLockManager::LockType::kExclusive}));
+ EXPECT_EQ(
+ DisjointRangeLockManager::TestLockResult::kLocked,
+ lock_manager.TestLock({0, range, LeveledLockManager::LockType::kShared}));
+
exclusive_lock3_holder.locks.clear();
// Flush the task queue to propagate the lock releases and grant the exclusive
@@ -240,9 +262,15 @@ TEST_F(DisjointRangeLockManagerTest, LevelsOperateSeparately) {
{
BarrierBuilder barrier(loop.QuitClosure());
LeveledLockRange range = {IntegerKey(0), IntegerKey(1)};
+ EXPECT_EQ(DisjointRangeLockManager::TestLockResult::kFree,
+ lock_manager.TestLock(
+ {0, range, LeveledLockManager::LockType::kExclusive}));
EXPECT_TRUE(lock_manager.AcquireLocks(
{{0, range, LeveledLockManager::LockType::kExclusive}},
l0_lock_holder.AsWeakPtr(), barrier.AddClosure()));
+ EXPECT_EQ(DisjointRangeLockManager::TestLockResult::kFree,
+ lock_manager.TestLock(
+ {1, range, LeveledLockManager::LockType::kExclusive}));
EXPECT_TRUE(lock_manager.AcquireLocks(
{{1, range, LeveledLockManager::LockType::kExclusive}},
l1_lock_holder.AsWeakPtr(), barrier.AddClosure()));
@@ -276,6 +304,9 @@ TEST_F(DisjointRangeLockManagerTest, InvalidRequests) {
EXPECT_FALSE(lock_manager.AcquireLocks(
{{-1, range1, LeveledLockManager::LockType::kShared}},
locks_holder.AsWeakPtr(), base::DoNothing()));
+ EXPECT_EQ(DisjointRangeLockManager::TestLockResult::kInvalid,
+ lock_manager.TestLock(
+ {-1, range1, LeveledLockManager::LockType::kShared}));
EXPECT_TRUE(locks_holder.locks.empty());
EXPECT_EQ(0ll, lock_manager.LocksHeldForTesting());
EXPECT_EQ(0ll, lock_manager.RequestsWaitingForTesting());
@@ -284,6 +315,9 @@ TEST_F(DisjointRangeLockManagerTest, InvalidRequests) {
EXPECT_FALSE(lock_manager.AcquireLocks(
{{4, range1, LeveledLockManager::LockType::kShared}},
locks_holder.AsWeakPtr(), base::DoNothing()));
+ EXPECT_EQ(DisjointRangeLockManager::TestLockResult::kInvalid,
+ lock_manager.TestLock(
+ {4, range1, LeveledLockManager::LockType::kShared}));
EXPECT_TRUE(locks_holder.locks.empty());
EXPECT_EQ(0ll, lock_manager.LocksHeldForTesting());
EXPECT_EQ(0ll, lock_manager.RequestsWaitingForTesting());
@@ -293,6 +327,9 @@ TEST_F(DisjointRangeLockManagerTest, InvalidRequests) {
EXPECT_FALSE(lock_manager.AcquireLocks(
{{0, range3, LeveledLockManager::LockType::kShared}},
locks_holder.AsWeakPtr(), base::DoNothing()));
+ EXPECT_EQ(DisjointRangeLockManager::TestLockResult::kInvalid,
+ lock_manager.TestLock(
+ {0, range3, LeveledLockManager::LockType::kShared}));
EXPECT_TRUE(locks_holder.locks.empty());
EXPECT_EQ(0ll, lock_manager.LocksHeldForTesting());
EXPECT_EQ(0ll, lock_manager.RequestsWaitingForTesting());
diff --git a/chromium/components/services/storage/indexed_db/locks/leveled_lock_range.h b/chromium/components/services/storage/indexed_db/locks/leveled_lock_range.h
index b09cdb76fae..444a7c92b3a 100644
--- a/chromium/components/services/storage/indexed_db/locks/leveled_lock_range.h
+++ b/chromium/components/services/storage/indexed_db/locks/leveled_lock_range.h
@@ -15,8 +15,6 @@ namespace content {
// The range is [begin, end). Bytewise comparison is used to determine
// overlapping ranges.
struct COMPONENT_EXPORT(LOCK_MANAGER) LeveledLockRange {
- LeveledLockRange() = default;
- ~LeveledLockRange() = default;
std::string begin;
std::string end;
diff --git a/chromium/components/services/storage/privileged/mojom/BUILD.gn b/chromium/components/services/storage/privileged/mojom/BUILD.gn
new file mode 100644
index 00000000000..181c478fd44
--- /dev/null
+++ b/chromium/components/services/storage/privileged/mojom/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2022 The Chromium 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("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+ sources = [
+ "indexed_db_control.mojom",
+ "indexed_db_control_test.mojom",
+ ]
+
+ public_deps = [
+ "//components/services/storage/public/mojom",
+ "//third_party/blink/public/mojom:mojom_core",
+ ]
+
+ overridden_deps = [ "//third_party/blink/public/mojom:mojom_core" ]
+
+ component_deps = [ "//third_party/blink/public/common" ]
+}
diff --git a/chromium/components/services/storage/privileged/mojom/OWNERS b/chromium/components/services/storage/privileged/mojom/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/components/services/storage/privileged/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/services/storage/public/mojom/indexed_db_control.mojom b/chromium/components/services/storage/privileged/mojom/indexed_db_control.mojom
index 16542dcd8f3..4e1593df4f0 100644
--- a/chromium/components/services/storage/public/mojom/indexed_db_control.mojom
+++ b/chromium/components/services/storage/privileged/mojom/indexed_db_control.mojom
@@ -4,7 +4,8 @@
module storage.mojom;
-import "components/services/storage/public/mojom/indexed_db_control_test.mojom";
+import "components/services/storage/privileged/mojom/indexed_db_control_test.mojom";
+import "components/services/storage/public/mojom/buckets/bucket_locator.mojom";
import "components/services/storage/public/mojom/storage_policy_update.mojom";
import "components/services/storage/public/mojom/storage_usage_info.mojom";
import "mojo/public/mojom/base/file_path.mojom";
@@ -25,15 +26,17 @@ enum ForceCloseReason {
// enums.xml.
};
-// Communicates with IndexedDB clients about changes in IndexedDB.
+// Communicates with IndexedDB clients about changes in IndexedDB. These clients
+// should live in the browser process as the notifications are dispatched by the
+// storage service without filtering by frame.
interface IndexedDBObserver {
// This function is called when the size of the usage for a particular
- // `storage_key` changes (both in memory and on disk).
- OnIndexedDBListChanged(blink.mojom.StorageKey storage_key);
+ // `bucket_locator` changes (both in memory and on disk).
+ OnIndexedDBListChanged(storage.mojom.BucketLocator bucket_locator);
// This function is called when the content of a particular object store
- // for a particular `storage_key` has been modified.
- OnIndexedDBContentChanged(blink.mojom.StorageKey storage_key,
+ // for a particular `bucket_locator` has been modified.
+ OnIndexedDBContentChanged(storage.mojom.BucketLocator bucket_locator,
mojo_base.mojom.String16 database_name,
mojo_base.mojom.String16 object_store_name);
};
@@ -46,21 +49,26 @@ interface IndexedDBObserver {
// out-of-process.
interface IndexedDBControl {
// Binds an IDBFactory to the given `storage_key`.
+ // TODO(crbug.com/1315371): Allow custom bucket names.
BindIndexedDB(blink.mojom.StorageKey storage_key,
pending_receiver<blink.mojom.IDBFactory> receiver);
// Retrieves some basic usage information about the IndexedDB state. The
// returned array has one StorageUsageInfo per `storage_key` in the
// partition that has IndexedDB usage data.
+ // TODO(crbug.com/1315371): Allow custom bucket names.
GetUsage() => (array<StorageUsageInfo> info);
// Deletes all indexed db files for the given `storage_key`.
- DeleteForStorageKey(blink.mojom.StorageKey storage_key) => (bool success);
+ // TODO(crbug.com/1315371): Allow custom bucket names.
+ DeleteForBucket(blink.mojom.StorageKey storage_key) => (bool success);
// Forcibly closes all connections to all databases within the `storage_key`.
+ // TODO(crbug.com/1315371): Allow custom bucket names.
ForceClose(blink.mojom.StorageKey storage_key, ForceCloseReason reason) => ();
// Returns the current number of connections.
+ // TODO(crbug.com/1315371): Allow custom bucket names.
GetConnectionCount(blink.mojom.StorageKey storage_key) =>
(uint64 connection_count);
@@ -68,14 +76,15 @@ interface IndexedDBControl {
// data for a particular `storage_key`. This creates a zip file at
// `zip_path` using the temporary directory `temp_path` which needs to be
// cleaned up after the user downloads the file.
- DownloadStorageKeyData(blink.mojom.StorageKey storage_key) => (
+ // TODO(crbug.com/1315371): Allow custom bucket names.
+ DownloadBucketData(blink.mojom.StorageKey storage_key) => (
bool success,
mojo_base.mojom.FilePath temp_path,
mojo_base.mojom.FilePath zip_path);
// Called by chrome://indexeddb-internals to populate its page details.
- GetAllStorageKeysDetails() => (bool incognito,
- mojo_base.mojom.ListValue details);
+ // TODO(crbug.com/1315371): Allow custom bucket names.
+ GetAllBucketsDetails() => (bool incognito, mojo_base.mojom.ListValue details);
// Disables the exit-time deletion of session-only data, so that internal
// restarts can preserve that data.
@@ -86,6 +95,7 @@ interface IndexedDBControl {
// Applies changes to data retention policy which are relevant at shutdown.
// See StoragePolicyUpdate.
+ // TODO(crbug.com/1315371): Allow custom bucket names.
ApplyPolicyUpdates(array<StoragePolicyUpdate> policy_updates);
// Binds the testing interface for extra functionality only available in
diff --git a/chromium/components/services/storage/public/mojom/indexed_db_control_test.mojom b/chromium/components/services/storage/privileged/mojom/indexed_db_control_test.mojom
index 6637a743dcd..b07a71f0d6a 100644
--- a/chromium/components/services/storage/public/mojom/indexed_db_control_test.mojom
+++ b/chromium/components/services/storage/privileged/mojom/indexed_db_control_test.mojom
@@ -4,10 +4,10 @@
module storage.mojom;
+import "components/services/storage/public/mojom/buckets/bucket_locator.mojom";
import "mojo/public/mojom/base/file_path.mojom";
import "mojo/public/mojom/base/values.mojom";
import "mojo/public/mojom/base/time.mojom";
-import "third_party/blink/public/mojom/storage_key/storage_key.mojom";
enum V2SchemaCorruptionStatus {
CORRUPTION_UNKNOWN,
@@ -44,40 +44,41 @@ interface IndexedDBControlTest {
// Gets the root data path for storage.
GetBaseDataPathForTesting() => (mojo_base.mojom.FilePath path);
- // Gets the file name of the local storage file for the given `storage_key`.
- GetFilePathForTesting(blink.mojom.StorageKey storage_key) =>
+ // Gets the name of the local storage file for the given `bucket_locator`.
+ GetFilePathForTesting(storage.mojom.BucketLocator bucket_locator) =>
(mojo_base.mojom.FilePath path);
// Forgets the storage keys and sizes read from disk.
ResetCachesForTesting() => ();
// Downgrades databases to V2.
- ForceSchemaDowngradeForTesting(blink.mojom.StorageKey storage_key) =>
+ ForceSchemaDowngradeForTesting(storage.mojom.BucketLocator bucket_locator) =>
(bool downgraded);
// Returns the corruption status for a given database.
- HasV2SchemaCorruptionForTesting(blink.mojom.StorageKey storage_key) =>
+ HasV2SchemaCorruptionForTesting(storage.mojom.BucketLocator bucket_locator) =>
(V2SchemaCorruptionStatus status);
// Does a direct write to a database with a given key/value pair.
- WriteToIndexedDBForTesting(blink.mojom.StorageKey storage_key, string key,
- string value) => ();
+ WriteToIndexedDBForTesting(storage.mojom.BucketLocator bucket_locator,
+ string key, string value) => ();
- // Returns the number of blobs for a given `storage_key`.
- GetBlobCountForTesting(blink.mojom.StorageKey storage_key) =>
+ // Returns the number of blobs for a given `bucket_locator`.
+ GetBlobCountForTesting(storage.mojom.BucketLocator bucket_locator) =>
(int64 num_blobs);
- // Returns the next blob id for a given `storage_key`.
- GetNextBlobNumberForTesting(blink.mojom.StorageKey storage_key,
+ // Returns the next blob id for a given `bucket_locator`.
+ GetNextBlobNumberForTesting(storage.mojom.BucketLocator bucket_locator,
int64 database_id) => (int64 next_blob_number);
// Returns the path for a given key/database/blob.
- GetPathForBlobForTesting(blink.mojom.StorageKey storage_key,
+ GetPathForBlobForTesting(storage.mojom.BucketLocator bucket_locator,
int64 database_id, int64 blob_number) =>
(mojo_base.mojom.FilePath path);
- // Compacts the backing store for a given `storage_key`.
- CompactBackingStoreForTesting(blink.mojom.StorageKey storage_key) => ();
+ // Compacts the backing store for a given `bucket_locator`.
+ CompactBackingStoreForTesting(storage.mojom.BucketLocator bucket_locator) =>
+ ();
// Overrides IndexedDB with a mock class factory that can inject failures
// at specific points. For now, because this is static, there can only
@@ -92,4 +93,7 @@ interface IndexedDBControlTest {
// Return keys used in WriteToIndexedDBForTesting.
GetDatabaseKeysForTesting() =>
(string schema_version_key, string data_version_key);
+
+ // Forces disk files to be re-read and added to active set if changed.
+ ForceInitializeFromFilesForTesting() => ();
};
diff --git a/chromium/components/services/storage/public/cpp/buckets/BUILD.gn b/chromium/components/services/storage/public/cpp/buckets/BUILD.gn
index 73c93545a09..50029a52657 100644
--- a/chromium/components/services/storage/public/cpp/buckets/BUILD.gn
+++ b/chromium/components/services/storage/public/cpp/buckets/BUILD.gn
@@ -1,15 +1,21 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
component("buckets") {
output_name = "storage_service_buckets_support"
public = [
"bucket_id.h",
"bucket_info.h",
+ "bucket_init_params.h",
"bucket_locator.h",
"constants.h",
]
sources = [
"bucket_info.cc",
+ "bucket_init_params.cc",
"bucket_locator.cc",
"constants.cc",
]
diff --git a/chromium/components/services/storage/public/cpp/buckets/bucket_info.cc b/chromium/components/services/storage/public/cpp/buckets/bucket_info.cc
index 161a883a219..3a409459589 100644
--- a/chromium/components/services/storage/public/cpp/buckets/bucket_info.cc
+++ b/chromium/components/services/storage/public/cpp/buckets/bucket_info.cc
@@ -11,15 +11,18 @@ BucketInfo::BucketInfo(BucketId bucket_id,
blink::mojom::StorageType type,
std::string name,
base::Time expiration,
- int64_t quota)
+ int64_t quota,
+ bool persistent,
+ blink::mojom::BucketDurability durability)
: id(std::move(bucket_id)),
storage_key(std::move(storage_key)),
type(type),
name(std::move(name)),
expiration(std::move(expiration)),
- quota(quota) {}
+ quota(quota),
+ persistent(persistent),
+ durability(durability) {}
-BucketInfo::BucketInfo() = default;
BucketInfo::~BucketInfo() = default;
BucketInfo::BucketInfo(const BucketInfo&) = default;
diff --git a/chromium/components/services/storage/public/cpp/buckets/bucket_info.h b/chromium/components/services/storage/public/cpp/buckets/bucket_info.h
index 84a2a6eb862..e80b947052c 100644
--- a/chromium/components/services/storage/public/cpp/buckets/bucket_info.h
+++ b/chromium/components/services/storage/public/cpp/buckets/bucket_info.h
@@ -10,6 +10,7 @@
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "components/services/storage/public/cpp/buckets/constants.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/buckets/bucket_manager_host.mojom.h"
#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h"
#include "url/origin.h"
@@ -21,14 +22,16 @@ namespace storage {
// `expiration` and `quota`, may get out of sync with the database. The
// database is the source of truth.
struct COMPONENT_EXPORT(STORAGE_SERVICE_BUCKETS_SUPPORT) BucketInfo {
- BucketInfo();
BucketInfo(BucketId bucket_id,
blink::StorageKey storage_key,
blink::mojom::StorageType type,
std::string name,
base::Time expiration,
- int64_t quota);
+ int64_t quota,
+ bool persistent,
+ blink::mojom::BucketDurability durability);
+ BucketInfo() = delete;
~BucketInfo();
BucketInfo(const BucketInfo&);
@@ -53,10 +56,12 @@ struct COMPONENT_EXPORT(STORAGE_SERVICE_BUCKETS_SUPPORT) BucketInfo {
BucketId id;
blink::StorageKey storage_key;
- blink::mojom::StorageType type = blink::mojom::StorageType::kUnknown;
+ blink::mojom::StorageType type;
std::string name;
base::Time expiration;
- int64_t quota = 0;
+ int64_t quota;
+ bool persistent;
+ blink::mojom::BucketDurability durability;
};
} // namespace storage
diff --git a/chromium/components/services/storage/public/cpp/buckets/bucket_init_params.cc b/chromium/components/services/storage/public/cpp/buckets/bucket_init_params.cc
new file mode 100644
index 00000000000..6028ee96179
--- /dev/null
+++ b/chromium/components/services/storage/public/cpp/buckets/bucket_init_params.cc
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium 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/services/storage/public/cpp/buckets/bucket_init_params.h"
+
+#include "components/services/storage/public/cpp/buckets/constants.h"
+
+namespace storage {
+
+BucketInitParams BucketInitParams::ForDefaultBucket(
+ const blink::StorageKey& storage_key) {
+ BucketInitParams params(storage_key, kDefaultBucketName);
+ params.durability = blink::mojom::BucketDurability::kStrict;
+ return params;
+}
+
+BucketInitParams::BucketInitParams(blink::StorageKey storage_key,
+ const std::string& name)
+ : storage_key(std::move(storage_key)), name(name) {}
+
+BucketInitParams::~BucketInitParams() = default;
+
+BucketInitParams::BucketInitParams(const BucketInitParams&) = default;
+BucketInitParams::BucketInitParams(BucketInitParams&&) noexcept = default;
+BucketInitParams& BucketInitParams::operator=(const BucketInitParams&) =
+ default;
+BucketInitParams& BucketInitParams::operator=(BucketInitParams&&) noexcept =
+ default;
+
+} // namespace storage
diff --git a/chromium/components/services/storage/public/cpp/buckets/bucket_init_params.h b/chromium/components/services/storage/public/cpp/buckets/bucket_init_params.h
new file mode 100644
index 00000000000..06f5b7e99b1
--- /dev/null
+++ b/chromium/components/services/storage/public/cpp/buckets/bucket_init_params.h
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_BUCKETS_BUCKET_INIT_PARAMS_H_
+#define COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_BUCKETS_BUCKET_INIT_PARAMS_H_
+
+#include "base/time/time.h"
+#include "components/services/storage/public/cpp/buckets/bucket_id.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/buckets/bucket_manager_host.mojom.h"
+
+namespace storage {
+
+// A collection of attributes to describe a bucket.
+//
+// These attributes are used for creating the bucket in the database.
+struct COMPONENT_EXPORT(STORAGE_SERVICE_BUCKETS_SUPPORT) BucketInitParams {
+ public:
+ // Creates an object that represents the `default` bucket for the given
+ // storage key.
+ static BucketInitParams ForDefaultBucket(
+ const blink::StorageKey& storage_key);
+
+ BucketInitParams(blink::StorageKey storage_key, const std::string& name);
+ ~BucketInitParams();
+
+ BucketInitParams(const BucketInitParams&);
+ BucketInitParams(BucketInitParams&&) noexcept;
+ BucketInitParams& operator=(const BucketInitParams&);
+ BucketInitParams& operator=(BucketInitParams&&) noexcept;
+
+ blink::StorageKey storage_key;
+ std::string name;
+
+ // `is_null()` when not specified.
+ base::Time expiration = base::Time();
+
+ // 0 when not specified.
+ int64_t quota = 0;
+
+ // nullopt when not specified.
+ absl::optional<bool> persistent;
+ absl::optional<blink::mojom::BucketDurability> durability;
+};
+
+} // namespace storage
+
+#endif // COMPONENTS_SERVICES_STORAGE_PUBLIC_CPP_BUCKETS_BUCKET_INIT_PARAMS_H_
diff --git a/chromium/components/services/storage/public/cpp/buckets/bucket_locator.h b/chromium/components/services/storage/public/cpp/buckets/bucket_locator.h
index a391010ea8f..9f0b587e6c4 100644
--- a/chromium/components/services/storage/public/cpp/buckets/bucket_locator.h
+++ b/chromium/components/services/storage/public/cpp/buckets/bucket_locator.h
@@ -39,8 +39,8 @@ struct COMPONENT_EXPORT(STORAGE_SERVICE_BUCKETS_SUPPORT) BucketLocator {
COMPONENT_EXPORT(STORAGE_SERVICE_BUCKETS_SUPPORT)
friend bool operator<(const BucketLocator& lhs, const BucketLocator& rhs);
- BucketId id;
- blink::StorageKey storage_key;
+ BucketId id = BucketId::FromUnsafeValue(0);
+ blink::StorageKey storage_key = blink::StorageKey();
blink::mojom::StorageType type = blink::mojom::StorageType::kUnknown;
bool is_default = false;
};
diff --git a/chromium/components/services/storage/public/cpp/quota_error_or.h b/chromium/components/services/storage/public/cpp/quota_error_or.h
index 3aab3462766..cb7ba06749b 100644
--- a/chromium/components/services/storage/public/cpp/quota_error_or.h
+++ b/chromium/components/services/storage/public/cpp/quota_error_or.h
@@ -15,6 +15,8 @@ enum class QuotaError {
kDatabaseError,
kNotFound,
kEntryExistsError,
+ kFileOperationError,
+ kIllegalOperation,
};
// Helper for methods which perform database operations which may fail. Objects
diff --git a/chromium/components/services/storage/public/mojom/BUILD.gn b/chromium/components/services/storage/public/mojom/BUILD.gn
index fd86f60efd5..8e9899c6637 100644
--- a/chromium/components/services/storage/public/mojom/BUILD.gn
+++ b/chromium/components/services/storage/public/mojom/BUILD.gn
@@ -9,8 +9,6 @@ mojom("mojom") {
"blob_storage_context.mojom",
"cache_storage_control.mojom",
"file_system_access_context.mojom",
- "indexed_db_control.mojom",
- "indexed_db_control_test.mojom",
"local_storage_control.mojom",
"origin_context.mojom",
"partition.mojom",
diff --git a/chromium/components/services/storage/public/mojom/service_worker_database.mojom b/chromium/components/services/storage/public/mojom/service_worker_database.mojom
index 52da6a3dfac..242c53774d8 100644
--- a/chromium/components/services/storage/public/mojom/service_worker_database.mojom
+++ b/chromium/components/services/storage/public/mojom/service_worker_database.mojom
@@ -11,7 +11,7 @@ import "third_party/blink/public/mojom/service_worker/navigation_preload_state.m
import "third_party/blink/public/mojom/service_worker/service_worker_database.mojom";
import "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom";
import "third_party/blink/public/mojom/storage_key/storage_key.mojom";
-import "third_party/blink/public/mojom/web_feature/web_feature.mojom";
+import "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom";
import "url/mojom/url.mojom";
// Status code of service worker database operations.
diff --git a/chromium/components/services/storage/service_worker/service_worker_database.h b/chromium/components/services/storage/service_worker/service_worker_database.h
index fd2686765ee..0fb57346837 100644
--- a/chromium/components/services/storage/service_worker/service_worker_database.h
+++ b/chromium/components/services/storage/service_worker/service_worker_database.h
@@ -24,7 +24,7 @@
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/service_worker/navigation_preload_state.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h"
-#include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
+#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
diff --git a/chromium/components/services/storage/shared_storage/async_shared_storage_database.h b/chromium/components/services/storage/shared_storage/async_shared_storage_database.h
index 57ea5a7b074..fe12d1cfd19 100644
--- a/chromium/components/services/storage/shared_storage/async_shared_storage_database.h
+++ b/chromium/components/services/storage/shared_storage/async_shared_storage_database.h
@@ -39,6 +39,7 @@ class AsyncSharedStorageDatabase {
using SetBehavior = SharedStorageDatabase::SetBehavior;
using OperationResult = SharedStorageDatabase::OperationResult;
using GetResult = SharedStorageDatabase::GetResult;
+ using BudgetResult = SharedStorageDatabase::BudgetResult;
// A callback type to check if a given origin matches a storage policy.
// Can be passed empty/null where used, which means the origin will always
@@ -58,11 +59,12 @@ class AsyncSharedStorageDatabase {
// `TrimMemory()`, `Get()`, `Set()`, `Append()`, `Delete()`, `Clear()`,
// `Length()`, `Keys()`, `Entries()`, `PurgeMatchingOrigins()`,
- // `PurgeStaleOrigins()`, and `FetchOrigins()` are all async versions of the
- // corresponding methods in `storage::SharedStorageDatabase`, with the
- // modification that `Set()` and `Append()` take a boolean callback to
- // indicate that a value was set or appended, rather than a long integer
- // callback with the row number for the next available row.
+ // `PurgeStaleOrigins()`, `FetchOrigins()`, `MakeBudgetWithdrawal()`, and
+ // `GetRemainingBudget()` are all async versions of the corresponding methods
+ // in `storage::SharedStorageDatabase`, with the modification that `Set()` and
+ // `Append()` take a boolean callback to indicate that a value was set or
+ // appended, rather than a long integer callback with the row number for the
+ // next available row.
//
// It is OK to call these async methods even if the database has failed to
// initialize, as there is an alternate code path to handle this case that
@@ -182,7 +184,13 @@ class AsyncSharedStorageDatabase {
bool perform_storage_cleanup = false) = 0;
// Clear all entries for all origins whose `last_read_time` falls before
- // `base::Time::Now() - window_to_be_deemed_active`.
+ // `SharedStorageDatabase::clock_->Now() - window_to_be_deemed_active`. Also
+ // purges, for all origins, all privacy budget withdrawals that have
+ // `time_stamps` older than `SharedStorageDatabase::clock_->Now() -
+ // SharedStorageDatabase::budget_interval_`.
+ //
+ // TODO(crbug.com/1317487): Remove the `window_to_be_deemed_active` parameter
+ // by sending via `SharedStorageDatabaseOptions` in the constructor.
virtual void PurgeStaleOrigins(
base::TimeDelta window_to_be_deemed_active,
base::OnceCallback<void(OperationResult)> callback) = 0;
@@ -193,6 +201,22 @@ class AsyncSharedStorageDatabase {
virtual void FetchOrigins(
base::OnceCallback<void(std::vector<mojom::StorageUsageInfoPtr>)>
callback) = 0;
+
+ // Makes a withdrawal of `bits_debit` stamped with the current time from the
+ // privacy budget of `context_origin`.
+ virtual void MakeBudgetWithdrawal(
+ url::Origin context_origin,
+ double bits_debit,
+ base::OnceCallback<void(OperationResult)> callback) = 0;
+
+ // Determines the number of bits remaining in the privacy budget of
+ // `context_origin`, where only withdrawals within the most recent
+ // `budget_interval_` are counted as still valid, and calls `callback` with
+ // this information bundled with an `OperationResult` value to indicate
+ // whether the database retrieval was successful.
+ virtual void GetRemainingBudget(
+ url::Origin context_origin,
+ base::OnceCallback<void(BudgetResult)> callback) = 0;
};
} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.cc b/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.cc
index e16b5749d21..bddd6703060 100644
--- a/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.cc
+++ b/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.cc
@@ -47,12 +47,14 @@ AsyncSharedStorageDatabaseImpl::~AsyncSharedStorageDatabaseImpl() = default;
void AsyncSharedStorageDatabaseImpl::Destroy(
base::OnceCallback<void(bool)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::Destroy)
.Then(std::move(callback));
}
void AsyncSharedStorageDatabaseImpl::TrimMemory(base::OnceClosure callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::TrimMemory)
.Then(std::move(callback));
@@ -62,6 +64,7 @@ void AsyncSharedStorageDatabaseImpl::Get(
url::Origin context_origin,
std::u16string key,
base::OnceCallback<void(GetResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::Get)
.WithArgs(std::move(context_origin), std::move(key))
@@ -74,6 +77,7 @@ void AsyncSharedStorageDatabaseImpl::Set(
std::u16string value,
base::OnceCallback<void(OperationResult)> callback,
SetBehavior behavior) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::Set)
.WithArgs(std::move(context_origin), std::move(key), std::move(value),
@@ -86,6 +90,7 @@ void AsyncSharedStorageDatabaseImpl::Append(
std::u16string key,
std::u16string value,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::Append)
.WithArgs(std::move(context_origin), std::move(key), std::move(value))
@@ -96,6 +101,7 @@ void AsyncSharedStorageDatabaseImpl::Delete(
url::Origin context_origin,
std::u16string key,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::Delete)
.WithArgs(std::move(context_origin), std::move(key))
@@ -105,6 +111,7 @@ void AsyncSharedStorageDatabaseImpl::Delete(
void AsyncSharedStorageDatabaseImpl::Clear(
url::Origin context_origin,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::Clear)
.WithArgs(std::move(context_origin))
@@ -114,6 +121,7 @@ void AsyncSharedStorageDatabaseImpl::Clear(
void AsyncSharedStorageDatabaseImpl::Length(
url::Origin context_origin,
base::OnceCallback<void(int)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::Length)
.WithArgs(std::move(context_origin))
@@ -126,6 +134,7 @@ void AsyncSharedStorageDatabaseImpl::Keys(
shared_storage_worklet::mojom::SharedStorageEntriesListener>
pending_listener,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::Keys)
.WithArgs(std::move(context_origin), std::move(pending_listener))
@@ -138,6 +147,7 @@ void AsyncSharedStorageDatabaseImpl::Entries(
shared_storage_worklet::mojom::SharedStorageEntriesListener>
pending_listener,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::Entries)
.WithArgs(std::move(context_origin), std::move(pending_listener))
@@ -150,6 +160,7 @@ void AsyncSharedStorageDatabaseImpl::PurgeMatchingOrigins(
base::Time end,
base::OnceCallback<void(OperationResult)> callback,
bool perform_storage_cleanup) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::PurgeMatchingOrigins)
.WithArgs(std::move(origin_matcher), begin, end, perform_storage_cleanup)
@@ -159,6 +170,7 @@ void AsyncSharedStorageDatabaseImpl::PurgeMatchingOrigins(
void AsyncSharedStorageDatabaseImpl::PurgeStaleOrigins(
base::TimeDelta window_to_be_deemed_active,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::PurgeStaleOrigins)
.WithArgs(window_to_be_deemed_active)
@@ -168,11 +180,33 @@ void AsyncSharedStorageDatabaseImpl::PurgeStaleOrigins(
void AsyncSharedStorageDatabaseImpl::FetchOrigins(
base::OnceCallback<void(std::vector<mojom::StorageUsageInfoPtr>)>
callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::FetchOrigins)
.Then(std::move(callback));
}
+void AsyncSharedStorageDatabaseImpl::MakeBudgetWithdrawal(
+ url::Origin context_origin,
+ double bits_debit,
+ base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::MakeBudgetWithdrawal)
+ .WithArgs(std::move(context_origin), bits_debit)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabaseImpl::GetRemainingBudget(
+ url::Origin context_origin,
+ base::OnceCallback<void(BudgetResult)> callback) {
+ DCHECK(callback);
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::GetRemainingBudget)
+ .WithArgs(std::move(context_origin))
+ .Then(std::move(callback));
+}
+
base::SequenceBound<SharedStorageDatabase>*
AsyncSharedStorageDatabaseImpl::GetSequenceBoundDatabaseForTesting() {
return database_ ? &database_ : nullptr;
@@ -180,6 +214,7 @@ AsyncSharedStorageDatabaseImpl::GetSequenceBoundDatabaseForTesting() {
void AsyncSharedStorageDatabaseImpl::IsOpenForTesting(
base::OnceCallback<void(bool)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::IsOpenForTesting)
.Then(std::move(callback));
@@ -187,6 +222,7 @@ void AsyncSharedStorageDatabaseImpl::IsOpenForTesting(
void AsyncSharedStorageDatabaseImpl::DBStatusForTesting(
base::OnceCallback<void(InitStatus)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::DBStatusForTesting)
.Then(std::move(callback));
@@ -196,6 +232,7 @@ void AsyncSharedStorageDatabaseImpl::OverrideLastUsedTimeForTesting(
url::Origin context_origin,
base::Time new_last_used_time,
base::OnceCallback<void(bool)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_.AsyncCall(&SharedStorageDatabase::OverrideLastUsedTimeForTesting)
.WithArgs(std::move(context_origin), new_last_used_time)
@@ -210,4 +247,32 @@ void AsyncSharedStorageDatabaseImpl::OverrideSpecialStoragePolicyForTesting(
.WithArgs(std::move(special_storage_policy));
}
+void AsyncSharedStorageDatabaseImpl::OverrideClockForTesting(
+ base::Clock* clock,
+ base::OnceClosure callback) {
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::OverrideClockForTesting)
+ .WithArgs(clock)
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabaseImpl::GetNumBudgetEntriesForTesting(
+ url::Origin context_origin,
+ base::OnceCallback<void(int)> callback) {
+ DCHECK(callback);
+ DCHECK(database_);
+ database_.AsyncCall(&SharedStorageDatabase::GetNumBudgetEntriesForTesting)
+ .WithArgs(std::move(context_origin))
+ .Then(std::move(callback));
+}
+
+void AsyncSharedStorageDatabaseImpl::GetTotalNumBudgetEntriesForTesting(
+ base::OnceCallback<void(int)> callback) {
+ DCHECK(callback);
+ DCHECK(database_);
+ database_
+ .AsyncCall(&SharedStorageDatabase::GetTotalNumBudgetEntriesForTesting)
+ .Then(std::move(callback));
+}
+
} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.h b/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.h
index e2afabe7f1f..cfaf4ee040d 100644
--- a/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.h
+++ b/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl.h
@@ -16,6 +16,7 @@
#include "base/threading/sequence_bound.h"
#include "components/services/storage/shared_storage/async_shared_storage_database.h"
#include "components/services/storage/shared_storage/shared_storage_database.h"
+#include "url/origin.h"
namespace base {
class FilePath;
@@ -39,6 +40,7 @@ class AsyncSharedStorageDatabaseImpl : public AsyncSharedStorageDatabase {
using SetBehavior = SharedStorageDatabase::SetBehavior;
using OperationResult = SharedStorageDatabase::OperationResult;
using GetResult = SharedStorageDatabase::GetResult;
+ using BudgetResult = SharedStorageDatabase::BudgetResult;
// A callback type to check if a given origin matches a storage policy.
// Can be passed empty/null where used, which means the origin will always
@@ -114,6 +116,13 @@ class AsyncSharedStorageDatabaseImpl : public AsyncSharedStorageDatabase {
void FetchOrigins(
base::OnceCallback<void(std::vector<mojom::StorageUsageInfoPtr>)>
callback) override;
+ void MakeBudgetWithdrawal(
+ url::Origin context_origin,
+ double bits_debit,
+ base::OnceCallback<void(OperationResult)> callback) override;
+ void GetRemainingBudget(
+ url::Origin context_origin,
+ base::OnceCallback<void(BudgetResult)> callback) override;
// Gets the underlying database for tests.
base::SequenceBound<SharedStorageDatabase>*
@@ -134,6 +143,21 @@ class AsyncSharedStorageDatabaseImpl : public AsyncSharedStorageDatabase {
void OverrideSpecialStoragePolicyForTesting(
scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy);
+ // Overrides the clock used to check the time.
+ void OverrideClockForTesting(base::Clock* clock, base::OnceClosure callback);
+
+ // Calls `callback` with the number of entries (including stale entries) in
+ // the table `budget_mapping` for `context_origin`, or with -1 in case of
+ // database initialization failure or SQL error.
+ void GetNumBudgetEntriesForTesting(url::Origin context_origin,
+ base::OnceCallback<void(int)> callback);
+
+ // Calls `callback` with the total number of entries in the table for all
+ // origins, or with -1 in case of database initialization failure or SQL
+ // error.
+ void GetTotalNumBudgetEntriesForTesting(
+ base::OnceCallback<void(int)> callback);
+
private:
// Instances should be obtained from the `Create()` factory method.
AsyncSharedStorageDatabaseImpl(
diff --git a/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc b/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc
index d424fc097de..18ae0ad03a0 100644
--- a/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc
+++ b/chromium/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc
@@ -22,6 +22,7 @@
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/threading/sequence_bound.h"
@@ -49,10 +50,15 @@ using InitStatus = SharedStorageDatabase::InitStatus;
using SetBehavior = SharedStorageDatabase::SetBehavior;
using OperationResult = SharedStorageDatabase::OperationResult;
using GetResult = SharedStorageDatabase::GetResult;
+using BudgetResult = SharedStorageDatabase::BudgetResult;
using DBOperation = TestDatabaseOperationReceiver::DBOperation;
using Type = DBOperation::Type;
using DBType = SharedStorageTestDBType;
+const int kBudgetIntervalHours = 24;
+const int kOriginStalenessThresholdHours = 1;
+const int kOriginStalenessThresholdDays = 1;
+const int kBitBudget = 8;
const int kMaxEntriesPerOrigin = 5;
const int kMaxStringLength = 100;
@@ -90,7 +96,10 @@ class AsyncSharedStorageDatabaseImplTest : public testing::Test {
virtual void InitSharedStorageFeature() {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
{blink::features::kSharedStorageAPI},
- {{"MaxSharedStorageInitTries", "1"}});
+ {{"MaxSharedStorageInitTries", "1"},
+ {"SharedStorageBitBudget", base::NumberToString(kBitBudget)},
+ {"SharedStorageBudgetInterval",
+ TimeDeltaToString(base::Hours(kBudgetIntervalHours))}});
}
virtual DBType GetType() { return DBType::kInMemory; }
@@ -159,17 +168,10 @@ class AsyncSharedStorageDatabaseImplTest : public testing::Test {
receiver_->WaitForOperations();
}
- void VerifySharedStorageTablesAndColumnsSync() {
+ void RunTask(base::OnceCallback<bool(SharedStorageDatabase*)> task) {
DCHECK(async_database_);
DCHECK(GetImpl()->GetSequenceBoundDatabaseForTesting());
- auto task = base::BindOnce([](SharedStorageDatabase* db) -> bool {
- auto* sql_db = db->db();
- EXPECT_TRUE(sql_db);
- VerifySharedStorageTablesAndColumns(*sql_db);
- return true;
- });
-
base::test::TestFuture<bool> future;
auto wrapped_task = base::BindOnce(
@@ -190,6 +192,17 @@ class AsyncSharedStorageDatabaseImplTest : public testing::Test {
EXPECT_TRUE(future.Get());
}
+ void VerifySharedStorageTablesAndColumnsSync() {
+ auto task = base::BindOnce([](SharedStorageDatabase* db) -> bool {
+ auto* sql_db = db->db();
+ EXPECT_TRUE(sql_db);
+ VerifySharedStorageTablesAndColumns(*sql_db);
+ return true;
+ });
+
+ RunTask(std::move(task));
+ }
+
void IsOpen(bool* out_boolean) {
DCHECK(out_boolean);
DCHECK(async_database_);
@@ -226,15 +239,6 @@ class AsyncSharedStorageDatabaseImplTest : public testing::Test {
return future.Get();
}
- void TrimMemory() {
- DCHECK(async_database_);
- DCHECK(receiver_);
-
- auto callback =
- receiver_->MakeOnceClosure(DBOperation(Type::DB_TRIM_MEMORY));
- async_database_->TrimMemory(std::move(callback));
- }
-
void OverrideLastUsedTime(url::Origin context_origin,
base::Time new_last_used_time,
bool* out_success) {
@@ -251,6 +255,75 @@ class AsyncSharedStorageDatabaseImplTest : public testing::Test {
std::move(context_origin), new_last_used_time, std::move(callback));
}
+ void OverrideClockSync(base::Clock* clock) {
+ DCHECK(async_database_);
+
+ base::RunLoop loop;
+ GetImpl()->OverrideClockForTesting(clock, loop.QuitClosure());
+ loop.Run();
+ }
+
+ void AdvanceClockAsync(base::TimeDelta delta) {
+ auto task = base::BindOnce(
+ [](base::SimpleTestClock* clock, base::TimeDelta delta,
+ SharedStorageDatabase* db) -> bool {
+ clock->Advance(delta);
+ return true;
+ },
+ &clock_, delta);
+
+ RunTask(std::move(task));
+ }
+
+ void GetNumBudgetEntries(url::Origin context_origin, int* out_result) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ *out_result = -1;
+ auto callback = receiver_->MakeIntCallback(
+ DBOperation(Type::DB_GET_NUM_BUDGET, context_origin), out_result);
+ GetImpl()->GetNumBudgetEntriesForTesting(std::move(context_origin),
+ std::move(callback));
+ }
+
+ int GetNumBudgetEntriesSync(url::Origin context_origin) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<int> future;
+ GetImpl()->GetNumBudgetEntriesForTesting(std::move(context_origin),
+ future.GetCallback());
+ return future.Get();
+ }
+
+ void GetTotalNumBudgetEntries(int* out_result) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ *out_result = -1;
+ auto callback = receiver_->MakeIntCallback(
+ DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET), out_result);
+ GetImpl()->GetTotalNumBudgetEntriesForTesting(std::move(callback));
+ }
+
+ int GetTotalNumBudgetEntriesSync() {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<int> future;
+ GetImpl()->GetTotalNumBudgetEntriesForTesting(future.GetCallback());
+ return future.Get();
+ }
+
+ void TrimMemory() {
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ auto callback =
+ receiver_->MakeOnceClosure(DBOperation(Type::DB_TRIM_MEMORY));
+ async_database_->TrimMemory(std::move(callback));
+ }
+
void Get(url::Origin context_origin,
std::u16string key,
GetResult* out_value) {
@@ -270,7 +343,7 @@ class AsyncSharedStorageDatabaseImplTest : public testing::Test {
base::test::TestFuture<GetResult> future;
async_database_->Get(std::move(context_origin), std::move(key),
future.GetCallback());
- return future.Get();
+ return future.Take();
}
void Set(url::Origin context_origin,
@@ -513,6 +586,62 @@ class AsyncSharedStorageDatabaseImplTest : public testing::Test {
std::move(callback));
}
+ OperationResult PurgeStaleOriginsSync(
+ base::TimeDelta window_to_be_deemed_active) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<OperationResult> future;
+ async_database_->PurgeStaleOrigins(window_to_be_deemed_active,
+ future.GetCallback());
+ return future.Get();
+ }
+
+ void MakeBudgetWithdrawal(url::Origin context_origin,
+ double bits_debit,
+ OperationResult* out_result) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ auto callback = receiver_->MakeOperationResultCallback(
+ DBOperation(Type::DB_MAKE_BUDGET_WITHDRAWAL, context_origin,
+ {base::NumberToString16(bits_debit)}),
+ out_result);
+ async_database_->MakeBudgetWithdrawal(std::move(context_origin), bits_debit,
+ std::move(callback));
+ }
+
+ OperationResult MakeBudgetWithdrawalSync(const url::Origin& context_origin,
+ double bits_debit) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<OperationResult> future;
+ async_database_->MakeBudgetWithdrawal(std::move(context_origin), bits_debit,
+ future.GetCallback());
+ return future.Get();
+ }
+
+ void GetRemainingBudget(url::Origin context_origin,
+ BudgetResult* out_result) {
+ DCHECK(out_result);
+ DCHECK(async_database_);
+ DCHECK(receiver_);
+
+ auto callback = receiver_->MakeBudgetResultCallback(
+ DBOperation(Type::DB_GET_REMAINING_BUDGET, context_origin), out_result);
+ async_database_->GetRemainingBudget(std::move(context_origin),
+ std::move(callback));
+ }
+
+ BudgetResult GetRemainingBudgetSync(const url::Origin& context_origin) {
+ DCHECK(async_database_);
+
+ base::test::TestFuture<BudgetResult> future;
+ async_database_->GetRemainingBudget(std::move(context_origin),
+ future.GetCallback());
+ return future.Take();
+ }
+
protected:
base::test::ScopedFeatureList scoped_feature_list_;
base::test::TaskEnvironment task_environment_;
@@ -522,6 +651,7 @@ class AsyncSharedStorageDatabaseImplTest : public testing::Test {
std::unique_ptr<TestDatabaseOperationReceiver> receiver_;
base::ScopedTempDir temp_dir_;
base::FilePath file_name_;
+ base::SimpleTestClock clock_;
};
class AsyncSharedStorageDatabaseImplFromFileV1Test
@@ -536,6 +666,12 @@ class AsyncSharedStorageDatabaseImplFromFileV1Test
TEST_F(AsyncSharedStorageDatabaseImplFromFileV1Test, Version1_LoadFromFile) {
ASSERT_TRUE(async_database_);
+ // Override the clock and set to the last time in the file that is used to
+ // make a budget withdrawal.
+ OverrideClockSync(&clock_);
+ clock_.SetNow(base::Time::FromDeltaSinceWindowsEpoch(
+ base::Microseconds(13269546593856733)));
+
url::Origin google_com = url::Origin::Create(GURL("http://google.com/"));
EXPECT_EQ(GetSync(google_com, u"key1").data, u"value1");
EXPECT_EQ(GetSync(google_com, u"key2").data, u"value2");
@@ -544,19 +680,63 @@ TEST_F(AsyncSharedStorageDatabaseImplFromFileV1Test, Version1_LoadFromFile) {
// columns until after the first call to `GetSync()`.
VerifySharedStorageTablesAndColumnsSync();
+ url::Origin abc_xyz = url::Origin::Create(GURL("http://abc.xyz"));
+
std::vector<url::Origin> origins;
for (const auto& info : FetchOriginsSync())
origins.push_back(info->origin);
EXPECT_THAT(
origins,
- ElementsAre(url::Origin::Create(GURL("http://abc.xyz")),
- url::Origin::Create(GURL("http://chromium.org")), google_com,
- url::Origin::Create(GURL("http://google.org")),
+ ElementsAre(abc_xyz, url::Origin::Create(GURL("http://chromium.org")),
+ google_com, url::Origin::Create(GURL("http://google.org")),
url::Origin::Create(GURL("http://growwithgoogle.com")),
url::Origin::Create(GURL("http://gv.com")),
url::Origin::Create(GURL("http://waymo.com")),
url::Origin::Create(GURL("http://withgoogle.com")),
url::Origin::Create(GURL("http://youtube.com"))));
+
+ EXPECT_DOUBLE_EQ(kBitBudget - 5.3, GetRemainingBudgetSync(abc_xyz).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(google_com).bits);
+}
+
+class AsyncSharedStorageDatabaseImplFromFileV1NoBudgetTableTest
+ : public AsyncSharedStorageDatabaseImplFromFileV1Test {
+ public:
+ const char* GetRelativeFilePath() override {
+ return "shared_storage.v1.no_budget_table.sql";
+ }
+};
+
+// Test loading version 1 database without budget table.
+TEST_F(AsyncSharedStorageDatabaseImplFromFileV1NoBudgetTableTest,
+ Version1_LoadFromFileNoBudgetTable) {
+ ASSERT_TRUE(async_database_);
+
+ url::Origin google_com = url::Origin::Create(GURL("http://google.com/"));
+ EXPECT_EQ(GetSync(google_com, u"key1").data, u"value1");
+ EXPECT_EQ(GetSync(google_com, u"key2").data, u"value2");
+
+ // Because the SQL database is lazy-initialized, wait to verify tables and
+ // columns until after the first call to `GetSync()`.
+ VerifySharedStorageTablesAndColumnsSync();
+
+ url::Origin abc_xyz = url::Origin::Create(GURL("http://abc.xyz"));
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : FetchOriginsSync())
+ origins.push_back(info->origin);
+ EXPECT_THAT(
+ origins,
+ ElementsAre(abc_xyz, url::Origin::Create(GURL("http://chromium.org")),
+ google_com, url::Origin::Create(GURL("http://google.org")),
+ url::Origin::Create(GURL("http://growwithgoogle.com")),
+ url::Origin::Create(GURL("http://gv.com")),
+ url::Origin::Create(GURL("http://waymo.com")),
+ url::Origin::Create(GURL("http://withgoogle.com")),
+ url::Origin::Create(GURL("http://youtube.com"))));
+
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(abc_xyz).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(google_com).bits);
}
struct InitFailureTestCase {
@@ -638,7 +818,10 @@ class AsyncSharedStorageDatabaseImplParamTest
{{"MaxSharedStorageEntriesPerOrigin",
base::NumberToString(kMaxEntriesPerOrigin)},
{"MaxSharedStorageStringLength",
- base::NumberToString(kMaxStringLength)}});
+ base::NumberToString(kMaxStringLength)},
+ {"SharedStorageBitBudget", base::NumberToString(kBitBudget)},
+ {"SharedStorageBudgetInterval",
+ TimeDeltaToString(base::Hours(kBudgetIntervalHours))}});
}
};
@@ -661,7 +844,7 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest, SyncOperations) {
EXPECT_EQ(2, LengthSync(kOrigin1));
EXPECT_EQ(OperationResult::kSuccess, DeleteSync(kOrigin1, u"key1"));
- EXPECT_FALSE(GetSync(kOrigin1, u"key1").data);
+ EXPECT_EQ(OperationResult::kKeyNotFound, GetSync(kOrigin1, u"key1").result);
EXPECT_EQ(1, LengthSync(kOrigin1));
EXPECT_EQ(OperationResult::kSet, AppendSync(kOrigin1, u"key1", u"value1"));
@@ -700,6 +883,113 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest, SyncOperations) {
EXPECT_EQ(0, LengthSync(kOrigin1));
}
+// Synchronously tests budget operations.
+TEST_P(AsyncSharedStorageDatabaseImplParamTest, SyncMakeBudgetWithdrawal) {
+ OverrideClockSync(&clock_);
+ clock_.SetNow(base::Time::Now());
+
+ // There should be no entries in the budget table.
+ EXPECT_EQ(0, GetTotalNumBudgetEntriesSync());
+
+ // SQL database hasn't yet been lazy-initialized. Nevertheless, remaining
+ // budgets should be returned as the max possible.
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin1).bits);
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+
+ // A withdrawal for `kOrigin1` doesn't affect `kOrigin2`.
+ EXPECT_EQ(OperationResult::kSuccess,
+ MakeBudgetWithdrawalSync(kOrigin1, 1.75));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75, GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(1, GetTotalNumBudgetEntriesSync());
+
+ // An additional withdrawal for `kOrigin1` at or near the same time as the
+ // previous one is debited appropriately.
+ EXPECT_EQ(OperationResult::kSuccess, MakeBudgetWithdrawalSync(kOrigin1, 2.5));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5,
+ GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(2, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(2, GetTotalNumBudgetEntriesSync());
+
+ // A withdrawal for `kOrigin2` doesn't affect `kOrigin1`.
+ EXPECT_EQ(OperationResult::kSuccess, MakeBudgetWithdrawalSync(kOrigin2, 3.4));
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5,
+ GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_EQ(2, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin2));
+ EXPECT_EQ(3, GetTotalNumBudgetEntriesSync());
+
+ // Advance halfway through the lookback window.
+ clock_.Advance(base::Hours(kBudgetIntervalHours) / 2);
+
+ // Remaining budgets continue to take into account the withdrawals above, as
+ // they are still within the lookback window.
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5,
+ GetRemainingBudgetSync(kOrigin1).bits);
+
+ // An additional withdrawal for `kOrigin1` at a later time from previous ones
+ // is debited appropriately.
+ EXPECT_EQ(OperationResult::kSuccess, MakeBudgetWithdrawalSync(kOrigin1, 1.0));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5 - 1.0,
+ GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(3, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin2));
+ EXPECT_EQ(4, GetTotalNumBudgetEntriesSync());
+
+ // Advance to the end of the initial lookback window, plus an additional
+ // microsecond to move past that window.
+ clock_.Advance(base::Hours(kBudgetIntervalHours) / 2 + base::Microseconds(1));
+
+ // Now only the single debit made within the current lookback window is
+ // counted, although the entries are still in the table because we haven't
+ // called `PurgeStaleOriginsSync()`.
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.0, GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(3, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin2));
+ EXPECT_EQ(4, GetTotalNumBudgetEntriesSync());
+
+ // After `PurgeStaleOriginsSync()` runs, there will only be the most
+ // recent debit left in the budget table.
+ //
+ // Note: The parameter for PurgeStaleOriginsSync() isn't relevant here.
+ EXPECT_EQ(OperationResult::kSuccess,
+ PurgeStaleOriginsSync(base::Days(kOriginStalenessThresholdDays)));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.0, GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(0, GetNumBudgetEntriesSync(kOrigin2));
+ EXPECT_EQ(1, GetTotalNumBudgetEntriesSync());
+
+ // Advance to where the last debit should no longer be in the lookback window.
+ clock_.Advance(base::Hours(kBudgetIntervalHours) / 2);
+
+ // Remaining budgets should be back at the max, although there is still an
+ // entry in the table.
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(1, GetTotalNumBudgetEntriesSync());
+
+ // After `PurgeStaleOriginsSync()` runs, the budget table will be empty.
+ //
+ // Note: The parameter for PurgeStaleOriginsSync() isn't relevant here.
+ EXPECT_EQ(OperationResult::kSuccess,
+ PurgeStaleOriginsSync(base::Days(kOriginStalenessThresholdDays)));
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(0, GetTotalNumBudgetEntriesSync());
+}
+
// Verifies that the async operations are executed in order and without races.
TEST_P(AsyncSharedStorageDatabaseImplParamTest, AsyncOperations) {
url::Origin kOrigin1 = url::Origin::Create(GURL("http://www.example1.test"));
@@ -829,6 +1119,298 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest, AsyncOperations) {
EXPECT_EQ(0, length5);
}
+TEST_P(AsyncSharedStorageDatabaseImplParamTest, AsyncMakeBudgetWithdrawal) {
+ OverrideClockSync(&clock_);
+ clock_.SetNow(base::Time::Now());
+
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+
+ // need to fix operations
+ std::queue<DBOperation> operation_list;
+ operation_list.push(DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+
+ operation_list.push(DBOperation(Type::DB_MAKE_BUDGET_WITHDRAWAL, kOrigin1,
+ {base::NumberToString16(1.75)}));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET));
+
+ operation_list.push(DBOperation(Type::DB_MAKE_BUDGET_WITHDRAWAL, kOrigin1,
+ {base::NumberToString16(2.5)}));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET));
+
+ operation_list.push(DBOperation(Type::DB_MAKE_BUDGET_WITHDRAWAL, kOrigin2,
+ {base::NumberToString16(3.4)}));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET));
+
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+
+ operation_list.push(DBOperation(Type::DB_MAKE_BUDGET_WITHDRAWAL, kOrigin1,
+ {base::NumberToString16(1.0)}));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET));
+
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET));
+
+ operation_list.push(DBOperation(
+ Type::DB_PURGE_STALE, {TestDatabaseOperationReceiver::SerializeTimeDelta(
+ base::Days(kOriginStalenessThresholdDays))}));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET));
+
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_NUM_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET));
+
+ operation_list.push(DBOperation(
+ Type::DB_PURGE_STALE, {TestDatabaseOperationReceiver::SerializeTimeDelta(
+ base::Days(kOriginStalenessThresholdDays))}));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin1));
+ operation_list.push(DBOperation(Type::DB_GET_REMAINING_BUDGET, kOrigin2));
+ operation_list.push(DBOperation(Type::DB_GET_TOTAL_NUM_BUDGET));
+
+ SetExpectedOperationList(std::move(operation_list));
+
+ // There should be no entries in the budget table.
+ int total_entries1 = -1;
+ GetTotalNumBudgetEntries(&total_entries1);
+
+ // SQL database hasn't yet been lazy-initialized. Nevertheless, remaining
+ // budgets should be returned as the max possible.
+ BudgetResult budget_result1 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result1);
+ BudgetResult budget_result2 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result2);
+
+ // A withdrawal for `kOrigin1` doesn't affect `kOrigin2`.
+ OperationResult operation_result1 = OperationResult::kSqlError;
+ MakeBudgetWithdrawal(kOrigin1, 1.75, &operation_result1);
+ BudgetResult budget_result3 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result3);
+ BudgetResult budget_result4 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result4);
+ int num_entries1 = -1;
+ GetNumBudgetEntries(kOrigin1, &num_entries1);
+ int total_entries2 = -1;
+ GetTotalNumBudgetEntries(&total_entries2);
+
+ // An additional withdrawal for `kOrigin1` at or near the same time as the
+ // previous one is debited appropriately.
+ OperationResult operation_result2 = OperationResult::kSqlError;
+ MakeBudgetWithdrawal(kOrigin1, 2.5, &operation_result2);
+ BudgetResult budget_result5 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result5);
+ BudgetResult budget_result6 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result6);
+ int num_entries2 = -1;
+ GetNumBudgetEntries(kOrigin1, &num_entries2);
+ int total_entries3 = -1;
+ GetTotalNumBudgetEntries(&total_entries3);
+
+ // A withdrawal for `kOrigin2` doesn't affect `kOrigin1`.
+ OperationResult operation_result3 = OperationResult::kSqlError;
+ MakeBudgetWithdrawal(kOrigin2, 3.4, &operation_result3);
+ BudgetResult budget_result7 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result7);
+ BudgetResult budget_result8 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result8);
+ int num_entries3 = -1;
+ GetNumBudgetEntries(kOrigin1, &num_entries3);
+ int num_entries4 = -1;
+ GetNumBudgetEntries(kOrigin2, &num_entries4);
+ int total_entries4 = -1;
+ GetTotalNumBudgetEntries(&total_entries4);
+
+ // Advance halfway through the lookback window.
+ AdvanceClockAsync(base::Hours(kBudgetIntervalHours / 2));
+
+ // Remaining budgets continue to take into account the withdrawals above, as
+ // they are still within the lookback window.
+ BudgetResult budget_result9 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result9);
+ BudgetResult budget_result10 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result10);
+
+ // An additional withdrawal for `kOrigin1` at a later time from previous ones
+ // is debited appropriately.
+ OperationResult operation_result4 = OperationResult::kSqlError;
+ MakeBudgetWithdrawal(kOrigin1, 1.0, &operation_result4);
+ BudgetResult budget_result11 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result11);
+ BudgetResult budget_result12 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result12);
+ int num_entries5 = -1;
+ GetNumBudgetEntries(kOrigin1, &num_entries5);
+ int num_entries6 = -1;
+ GetNumBudgetEntries(kOrigin2, &num_entries6);
+ int total_entries5 = -1;
+ GetTotalNumBudgetEntries(&total_entries5);
+
+ // Advance to the end of the initial lookback window, plus an additional
+ // microsecond to move past that window.
+ AdvanceClockAsync(base::Hours(kBudgetIntervalHours / 2) +
+ base::Microseconds(1));
+
+ // Now only the single debit made within the current lookback window is
+ // counted, although the entries are still in the table because we haven't
+ // called `PurgeStaleOrigins()`.
+ BudgetResult budget_result13 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result13);
+ BudgetResult budget_result14 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result14);
+ int num_entries7 = -1;
+ GetNumBudgetEntries(kOrigin1, &num_entries7);
+ int num_entries8 = -1;
+ GetNumBudgetEntries(kOrigin2, &num_entries8);
+ int total_entries6 = -1;
+ GetTotalNumBudgetEntries(&total_entries6);
+
+ // After `PurgeStaleOrigins()` runs, there will only be the most
+ // recent debit left in the budget table.
+ OperationResult operation_result5 = OperationResult::kSqlError;
+ PurgeStaleOrigins(base::Days(kOriginStalenessThresholdDays),
+ &operation_result5);
+ BudgetResult budget_result15 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result15);
+ BudgetResult budget_result16 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result16);
+ int num_entries9 = -1;
+ GetNumBudgetEntries(kOrigin1, &num_entries9);
+ int num_entries10 = -1;
+ GetNumBudgetEntries(kOrigin2, &num_entries10);
+ int total_entries7 = -1;
+ GetTotalNumBudgetEntries(&total_entries7);
+
+ // Advance to where the last debit should no longer be in the lookback window.
+ AdvanceClockAsync(base::Hours(kBudgetIntervalHours / 2));
+
+ // Remaining budgets should be back at the max, although there is still an
+ // entry in the table.
+ BudgetResult budget_result17 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result17);
+ BudgetResult budget_result18 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result18);
+ int num_entries11 = -1;
+ GetNumBudgetEntries(kOrigin1, &num_entries11);
+ int total_entries8 = -1;
+ GetTotalNumBudgetEntries(&total_entries8);
+
+ // After `PurgeStaleOrigins()` runs, the budget table will be empty.
+ OperationResult operation_result6 = OperationResult::kSqlError;
+ PurgeStaleOrigins(base::Days(kOriginStalenessThresholdDays),
+ &operation_result6);
+ BudgetResult budget_result19 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin1, &budget_result19);
+ BudgetResult budget_result20 = MakeBudgetResultForSqlError();
+ GetRemainingBudget(kOrigin2, &budget_result20);
+ int total_entries9 = -1;
+ GetTotalNumBudgetEntries(&total_entries9);
+
+ WaitForOperations();
+ EXPECT_TRUE(is_finished());
+
+ // There should be no entries in the budget table.
+ EXPECT_EQ(0, total_entries1);
+
+ // SQL database hasn't yet been lazy-initialized. Nevertheless, remaining
+ // budgets should be returned as the max possible.
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result1.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result2.bits);
+
+ // A withdrawal for `kOrigin1` doesn't affect `kOrigin2`.
+ EXPECT_EQ(OperationResult::kSuccess, operation_result1);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75, budget_result3.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result4.bits);
+ EXPECT_EQ(1, num_entries1);
+ EXPECT_EQ(1, total_entries2);
+
+ // An additional withdrawal for `kOrigin1` at or near the same time as the
+ // previous one is debited appropriately.
+ EXPECT_EQ(OperationResult::kSuccess, operation_result2);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5, budget_result5.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result6.bits);
+ EXPECT_EQ(2, num_entries2);
+ EXPECT_EQ(2, total_entries3);
+
+ // A withdrawal for `kOrigin2` doesn't affect `kOrigin1`.
+ EXPECT_EQ(OperationResult::kSuccess, operation_result3);
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, budget_result7.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5, budget_result8.bits);
+ EXPECT_EQ(2, num_entries3);
+ EXPECT_EQ(1, num_entries4);
+ EXPECT_EQ(3, total_entries4);
+
+ // Remaining budgets continue to take into account the withdrawals above, as
+ // they are still within the lookback window.
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, budget_result9.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5, budget_result10.bits);
+
+ // An additional withdrawal for `kOrigin1` at a later time from previous ones
+ // is debited appropriately.
+ EXPECT_EQ(OperationResult::kSuccess, operation_result4);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5 - 1.0, budget_result11.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, budget_result12.bits);
+ EXPECT_EQ(3, num_entries5);
+ EXPECT_EQ(1, num_entries6);
+ EXPECT_EQ(4, total_entries5);
+
+ // Now only the single debit made within the current lookback window is
+ // counted, although the entries are still in the table because we haven't
+ // called `PurgeStaleOrigins()`.
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.0, budget_result13.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result14.bits);
+ EXPECT_EQ(3, num_entries7);
+ EXPECT_EQ(1, num_entries8);
+ EXPECT_EQ(4, total_entries6);
+
+ // After `PurgeStaleOrigins()` runs, there will only be the most
+ // recent debit left in the budget table.
+ EXPECT_EQ(OperationResult::kSuccess, operation_result5);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.0, budget_result15.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result16.bits);
+ EXPECT_EQ(1, num_entries9);
+ EXPECT_EQ(0, num_entries10);
+ EXPECT_EQ(1, total_entries7);
+
+ // Remaining budgets should be back at the max, although there is still an
+ // entry in the table.
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result17.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result18.bits);
+ EXPECT_EQ(1, num_entries11);
+ EXPECT_EQ(1, total_entries8);
+
+ // After `PurgeStaleOrigins()` runs, the budget table will be empty.
+ EXPECT_EQ(OperationResult::kSuccess, operation_result6);
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result19.bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, budget_result20.bits);
+ EXPECT_EQ(0, total_entries9);
+}
+
TEST_P(AsyncSharedStorageDatabaseImplParamTest,
LazyInit_IgnoreForGet_CreateForSet) {
url::Origin kOrigin1 = url::Origin::Create(GURL("http://www.example1.test"));
@@ -884,8 +1466,8 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest,
EXPECT_FALSE(open1);
EXPECT_EQ(InitStatus::kUnattempted, status1);
- EXPECT_FALSE(value1.data);
- EXPECT_EQ(OperationResult::kSuccess, value1.result);
+ EXPECT_TRUE(value1.data.empty());
+ EXPECT_EQ(OperationResult::kKeyNotFound, value1.result);
EXPECT_EQ(!GetParam().in_memory_only, open2);
EXPECT_EQ(InitStatus::kUnattempted, status2);
@@ -1035,8 +1617,8 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest, PurgeStaleOrigins) {
operation_list.push(DBOperation(Type::DB_STATUS));
operation_list.push(DBOperation(
- Type::DB_PURGE_STALE,
- {TestDatabaseOperationReceiver::SerializeTimeDelta(base::Days(1))}));
+ Type::DB_PURGE_STALE, {TestDatabaseOperationReceiver::SerializeTimeDelta(
+ base::Days(kOriginStalenessThresholdDays))}));
operation_list.push(
DBOperation(Type::DB_SET, kOrigin1,
@@ -1106,8 +1688,8 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest, PurgeStaleOrigins) {
Type::DB_OVERRIDE_TIME, kOrigin1,
{TestDatabaseOperationReceiver::SerializeTime(override_time1)}));
operation_list.push(DBOperation(
- Type::DB_PURGE_STALE,
- {TestDatabaseOperationReceiver::SerializeTimeDelta(base::Days(1))}));
+ Type::DB_PURGE_STALE, {TestDatabaseOperationReceiver::SerializeTimeDelta(
+ base::Days(kOriginStalenessThresholdDays))}));
operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin1));
operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin2));
@@ -1120,8 +1702,8 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest, PurgeStaleOrigins) {
Type::DB_OVERRIDE_TIME, kOrigin3,
{TestDatabaseOperationReceiver::SerializeTime(override_time2)}));
operation_list.push(DBOperation(
- Type::DB_PURGE_STALE,
- {TestDatabaseOperationReceiver::SerializeTimeDelta(base::Hours(1))}));
+ Type::DB_PURGE_STALE, {TestDatabaseOperationReceiver::SerializeTimeDelta(
+ base::Hours(kOriginStalenessThresholdHours))}));
operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin1));
operation_list.push(DBOperation(Type::DB_LENGTH, kOrigin2));
@@ -1155,7 +1737,7 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest, PurgeStaleOrigins) {
DBStatus(&status1);
OperationResult result1 = OperationResult::kSqlError;
- PurgeStaleOrigins(base::Days(1), &result1);
+ PurgeStaleOrigins(base::Days(kOriginStalenessThresholdDays), &result1);
OperationResult result2 = OperationResult::kSqlError;
Set(kOrigin1, u"key1", u"value1", &result2);
@@ -1202,7 +1784,7 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest, PurgeStaleOrigins) {
OverrideLastUsedTime(kOrigin1, override_time1, &success1);
OperationResult result12 = OperationResult::kSqlError;
- PurgeStaleOrigins(base::Days(1), &result12);
+ PurgeStaleOrigins(base::Days(kOriginStalenessThresholdDays), &result12);
int length5 = -1;
Length(kOrigin1, &length5);
@@ -1220,7 +1802,7 @@ TEST_P(AsyncSharedStorageDatabaseImplParamTest, PurgeStaleOrigins) {
OverrideLastUsedTime(kOrigin3, override_time2, &success2);
OperationResult result13 = OperationResult::kSqlError;
- PurgeStaleOrigins(base::Hours(1), &result13);
+ PurgeStaleOrigins(base::Hours(kOriginStalenessThresholdHours), &result13);
int length9 = -1;
Length(kOrigin1, &length9);
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_database.cc b/chromium/components/services/storage/shared_storage/shared_storage_database.cc
index 7e3f8ffd929..704a3a853ff 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_database.cc
+++ b/chromium/components/services/storage/shared_storage/shared_storage_database.cc
@@ -62,12 +62,27 @@ const int kCurrentVersionNumber = 1;
if (!db.Execute(kPerOriginMappingSql))
return false;
+ static constexpr char kBudgetMappingSql[] =
+ "CREATE TABLE IF NOT EXISTS budget_mapping("
+ "id INTEGER NOT NULL PRIMARY KEY,"
+ "context_origin TEXT NOT NULL,"
+ "time_stamp INTEGER NOT NULL,"
+ "bits_debit REAL NOT NULL)";
+ if (!db.Execute(kBudgetMappingSql))
+ return false;
+
static constexpr char kLastUsedTimeIndexSql[] =
"CREATE INDEX IF NOT EXISTS per_origin_mapping_last_used_time_idx "
"ON per_origin_mapping(last_used_time)";
if (!db.Execute(kLastUsedTimeIndexSql))
return false;
+ static constexpr char kOriginTimeIndexSql[] =
+ "CREATE INDEX IF NOT EXISTS budget_mapping_origin_time_stamp_idx "
+ "ON budget_mapping(context_origin,time_stamp)";
+ if (!db.Execute(kOriginTimeIndexSql))
+ return false;
+
return true;
}
@@ -75,18 +90,24 @@ const int kCurrentVersionNumber = 1;
SharedStorageDatabase::GetResult::GetResult() = default;
-SharedStorageDatabase::GetResult::GetResult(const GetResult&) = default;
-
SharedStorageDatabase::GetResult::GetResult(GetResult&&) = default;
SharedStorageDatabase::GetResult::~GetResult() = default;
SharedStorageDatabase::GetResult& SharedStorageDatabase::GetResult::operator=(
- const GetResult&) = default;
-
-SharedStorageDatabase::GetResult& SharedStorageDatabase::GetResult::operator=(
GetResult&&) = default;
+SharedStorageDatabase::BudgetResult::BudgetResult(BudgetResult&&) = default;
+
+SharedStorageDatabase::BudgetResult::BudgetResult(double bits,
+ OperationResult result)
+ : bits(bits), result(result) {}
+
+SharedStorageDatabase::BudgetResult::~BudgetResult() = default;
+
+SharedStorageDatabase::BudgetResult&
+SharedStorageDatabase::BudgetResult::operator=(BudgetResult&&) = default;
+
SharedStorageDatabase::SharedStorageDatabase(
base::FilePath db_path,
scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
@@ -95,21 +116,23 @@ SharedStorageDatabase::SharedStorageDatabase(
// accessing the database while we're running, and this will give
// somewhat improved perf.
.exclusive_locking = true,
+ // We DCHECK that the page size is valid in the constructor for
+ // `SharedStorageOptions`.
.page_size = options->max_page_size,
.cache_size = options->max_cache_size}),
db_path_(std::move(db_path)),
special_storage_policy_(std::move(special_storage_policy)),
+ // We DCHECK that these `options` fields are all positive in the
+ // constructor for `SharedStorageOptions`.
max_entries_per_origin_(int64_t{options->max_entries_per_origin}),
+ max_string_length_(static_cast<size_t>(options->max_string_length)),
+ max_init_tries_(static_cast<size_t>(options->max_init_tries)),
+ max_iterator_batch_size_(
+ static_cast<size_t>(options->max_iterator_batch_size)),
+ bit_budget_(static_cast<double>(options->bit_budget)),
+ budget_interval_(options->budget_interval),
clock_(base::DefaultClock::GetInstance()) {
DCHECK(db_path_.empty() || db_path_.IsAbsolute());
- DCHECK_GT(max_entries_per_origin_, 0);
- DCHECK_GT(options->max_init_tries, 0);
- DCHECK_GT(options->max_string_length, 0);
- DCHECK_GT(options->max_iterator_batch_size, 0);
- max_string_length_ = static_cast<size_t>(options->max_string_length);
- max_init_tries_ = static_cast<size_t>(options->max_init_tries);
- max_iterator_batch_size_ =
- static_cast<size_t>(options->max_iterator_batch_size);
db_file_status_ = db_path_.empty() ? DBFileStatus::kNoPreexistingFile
: DBFileStatus::kNotChecked;
}
@@ -132,7 +155,6 @@ bool SharedStorageDatabase::Destroy() {
void SharedStorageDatabase::TrimMemory() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(InitStatus::kSuccess, db_status_);
db_.TrimMemory();
}
@@ -144,10 +166,10 @@ SharedStorageDatabase::GetResult SharedStorageDatabase::Get(
GetResult result;
if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
- // We do not return an error if the database doesn't exist, but only if it
- // pre-exists on disk and yet fails to initialize.
+ // We do not return `OperationResult::kInitFailure` if the database doesn't
+ // exist, but only if it pre-exists on disk and yet fails to initialize.
if (db_status_ == InitStatus::kUnattempted)
- result.result = OperationResult::kSuccess;
+ result.result = OperationResult::kKeyNotFound;
else
result.result = OperationResult::kInitFailure;
@@ -167,13 +189,18 @@ SharedStorageDatabase::GetResult SharedStorageDatabase::Get(
statement.BindString(0, origin_str);
statement.BindString16(1, key);
- if (statement.Step())
+ bool key_found = false;
+ if (statement.Step()) {
result.data = statement.ColumnString16(0);
- if (!statement.Succeeded())
+ key_found = true;
+ } else if (!statement.Succeeded()) {
return result;
+ }
- if (UpdateLastUsedTime(origin_str))
- result.result = OperationResult::kSuccess;
+ if (UpdateLastUsedTime(origin_str)) {
+ result.result =
+ key_found ? OperationResult::kSuccess : OperationResult::kKeyNotFound;
+ }
return result;
}
@@ -243,14 +270,16 @@ SharedStorageDatabase::OperationResult SharedStorageDatabase::Append(
return OperationResult::kSqlError;
GetResult get_result = Get(context_origin, key);
- if (get_result.result != OperationResult::kSuccess)
+ if (get_result.result != OperationResult::kSuccess &&
+ get_result.result != OperationResult::kKeyNotFound) {
return OperationResult::kSqlError;
+ }
std::u16string new_value;
std::string origin_str(SerializeOrigin(context_origin));
- if (get_result.data) {
- new_value = std::move(*get_result.data);
+ if (get_result.result == OperationResult::kSuccess) {
+ new_value = std::move(get_result.data);
new_value.append(tail_value);
if (new_value.size() > max_string_length_)
@@ -605,26 +634,22 @@ SharedStorageDatabase::OperationResult SharedStorageDatabase::PurgeStaleOrigins(
return OperationResult::kInitFailure;
}
- base::Time threshold = clock_->Now() - window_to_be_deemed_active;
-
static constexpr char kSelectSql[] =
"SELECT context_origin FROM per_origin_mapping "
"WHERE last_used_time<? "
"ORDER BY last_used_time";
- sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
- statement.BindTime(0, threshold);
+ sql::Statement select_statement(
+ db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ select_statement.BindTime(0, clock_->Now() - window_to_be_deemed_active);
std::vector<std::string> stale_origins;
- while (statement.Step())
- stale_origins.push_back(statement.ColumnString(0));
+ while (select_statement.Step())
+ stale_origins.push_back(select_statement.ColumnString(0));
- if (!statement.Succeeded())
+ if (!select_statement.Succeeded())
return OperationResult::kSqlError;
- if (stale_origins.empty())
- return OperationResult::kSuccess;
-
sql::Transaction transaction(&db_);
if (!transaction.Begin())
return OperationResult::kSqlError;
@@ -634,6 +659,16 @@ SharedStorageDatabase::OperationResult SharedStorageDatabase::PurgeStaleOrigins(
return OperationResult::kSqlError;
}
+ static constexpr char kDeleteSql[] =
+ "DELETE FROM budget_mapping WHERE time_stamp<?";
+
+ sql::Statement delete_statement(
+ db_.GetCachedStatement(SQL_FROM_HERE, kDeleteSql));
+ delete_statement.BindTime(0, clock_->Now() - budget_interval_);
+
+ if (!delete_statement.Run())
+ return OperationResult::kSqlError;
+
if (!transaction.Commit())
return OperationResult::kSqlError;
return OperationResult::kSuccess;
@@ -666,6 +701,60 @@ std::vector<mojom::StorageUsageInfoPtr> SharedStorageDatabase::FetchOrigins() {
return fetched_origin_infos;
}
+SharedStorageDatabase::OperationResult
+SharedStorageDatabase::MakeBudgetWithdrawal(url::Origin context_origin,
+ double bits_debit) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_GT(bits_debit, 0.0);
+
+ if (LazyInit(DBCreationPolicy::kCreateIfAbsent) != InitStatus::kSuccess)
+ return OperationResult::kInitFailure;
+
+ static constexpr char kInsertSql[] =
+ "INSERT INTO budget_mapping(context_origin,time_stamp,bits_debit)"
+ "VALUES(?,?,?)";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kInsertSql));
+ statement.BindString(0, SerializeOrigin(context_origin));
+ statement.BindTime(1, clock_->Now());
+ statement.BindDouble(2, bits_debit);
+
+ if (!statement.Run())
+ return OperationResult::kSqlError;
+ return OperationResult::kSuccess;
+}
+
+SharedStorageDatabase::BudgetResult SharedStorageDatabase::GetRemainingBudget(
+ url::Origin context_origin) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return an error if the database doesn't exist, but only if it
+ // pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ return BudgetResult(bit_budget_, OperationResult::kSuccess);
+ else
+ return BudgetResult(0.0, OperationResult::kInitFailure);
+ }
+
+ static constexpr char kSelectSql[] =
+ "SELECT SUM(bits_debit) FROM budget_mapping "
+ "WHERE context_origin=? AND time_stamp>=?";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ statement.BindString(0, SerializeOrigin(context_origin));
+ statement.BindTime(1, clock_->Now() - budget_interval_);
+
+ double total_debits = 0.0;
+ if (statement.Step())
+ total_debits = statement.ColumnDouble(0);
+
+ if (!statement.Succeeded())
+ return BudgetResult(0.0, OperationResult::kSqlError);
+
+ return BudgetResult(bit_budget_ - total_debits, OperationResult::kSuccess);
+}
+
bool SharedStorageDatabase::IsOpenForTesting() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return db_.is_open();
@@ -700,6 +789,54 @@ void SharedStorageDatabase::OverrideSpecialStoragePolicyForTesting(
special_storage_policy_ = std::move(special_storage_policy);
}
+int64_t SharedStorageDatabase::GetNumBudgetEntriesForTesting(
+ url::Origin context_origin) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return an error if the database doesn't exist, but only if it
+ // pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ return 0;
+ else
+ return -1;
+ }
+
+ static constexpr char kSelectSql[] =
+ "SELECT COUNT(*) FROM budget_mapping "
+ "WHERE context_origin=?";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+ statement.BindString(0, SerializeOrigin(context_origin));
+
+ if (statement.Step())
+ return statement.ColumnInt64(0);
+
+ return -1;
+}
+
+int64_t SharedStorageDatabase::GetTotalNumBudgetEntriesForTesting() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (LazyInit(DBCreationPolicy::kIgnoreIfAbsent) != InitStatus::kSuccess) {
+ // We do not return an error if the database doesn't exist, but only if it
+ // pre-exists on disk and yet fails to initialize.
+ if (db_status_ == InitStatus::kUnattempted)
+ return 0;
+ else
+ return -1;
+ }
+
+ static constexpr char kSelectSql[] = "SELECT COUNT(*) FROM budget_mapping";
+
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
+
+ if (statement.Step())
+ return statement.ColumnInt64(0);
+
+ return -1;
+}
+
bool SharedStorageDatabase::PopulateDatabaseForTesting(url::Origin origin1,
url::Origin origin2,
url::Origin origin3) {
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_database.h b/chromium/components/services/storage/shared_storage/shared_storage_database.h
index 9ebe6d83522..6155a18818d 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_database.h
+++ b/chromium/components/services/storage/shared_storage/shared_storage_database.h
@@ -33,7 +33,7 @@ class TimeDelta;
namespace sql {
class Statement;
-}
+} // namespace sql
namespace url {
class Origin;
@@ -100,21 +100,37 @@ class SharedStorageDatabase {
// requesting origin.
kInvalidAppend = 6, // Result if the length of the value after appending
// would exceed the maximum allowed length.
+ kKeyNotFound = 7, // Result if a key could not be retrieved via `Get()`
+ // because it doesn't exist in the database.
};
// Bundles a retrieved string from the database along with a field indicating
// whether the transaction was free of SQL errors.
struct GetResult {
- absl::optional<std::u16string> data;
+ std::u16string data;
OperationResult result = OperationResult::kSqlError;
GetResult();
- GetResult(const GetResult&);
+ GetResult(const GetResult&) = delete;
GetResult(GetResult&&);
~GetResult();
- GetResult& operator=(const GetResult&);
+ GetResult& operator=(const GetResult&) = delete;
GetResult& operator=(GetResult&&);
};
+ // Bundles a double `bits` representing the available bits remaining for the
+ // queried origin along with a field indicating whether the database retrieval
+ // was free of SQL errors.
+ struct BudgetResult {
+ double bits = 0.0;
+ OperationResult result = OperationResult::kSqlError;
+ BudgetResult(const BudgetResult&) = delete;
+ BudgetResult(BudgetResult&&);
+ BudgetResult(double bits, OperationResult result);
+ ~BudgetResult();
+ BudgetResult& operator=(const BudgetResult&) = delete;
+ BudgetResult& operator=(BudgetResult&&);
+ };
+
// When `db_path` is empty, the database will be opened in memory only.
SharedStorageDatabase(
base::FilePath db_path,
@@ -237,8 +253,17 @@ class SharedStorageDatabase {
bool perform_storage_cleanup = false);
// Clear all entries for all origins whose `last_read_time` falls before
- // `base::Time::Now() - window_to_be_deemed_active`. Returns whether the
- // transaction was successful.
+ // `clock_->Now() - window_to_be_deemed_active`. Also purges, for all origins,
+ // all privacy budget withdrawals that have `time_stamps` older than
+ // `clock_->Now() - budget_interval_`. Returns whether the transaction was
+ // successful.
+ //
+ // Note that `budget_interval_` may be different from
+ // `window_to_be_deemed_active`, and the latter only applies to the staleness
+ // of origins in `values_mapping`.
+ //
+ // TODO(crbug.com/1317487): Remove the `window_to_be_deemed_active` parameter
+ // by sending via `SharedStorageDatabaseOptions` in the constructor.
[[nodiscard]] OperationResult PurgeStaleOrigins(
base::TimeDelta window_to_be_deemed_active);
@@ -247,6 +272,18 @@ class SharedStorageDatabase {
// in this profile.
[[nodiscard]] std::vector<mojom::StorageUsageInfoPtr> FetchOrigins();
+ // Makes a withdrawal of `bits_debit` stamped with the current time from the
+ // privacy budget of `context_origin`.
+ [[nodiscard]] OperationResult MakeBudgetWithdrawal(url::Origin context_origin,
+ double bits_debit);
+
+ // Determines the number of bits remaining in the privacy budget of
+ // `context_origin`, where only withdrawals within the most recent
+ // `budget_interval_` are counted as still valid, and returns this information
+ // bundled with an `OperationResult` value to indicate whether the database
+ // retrieval was successful.
+ [[nodiscard]] BudgetResult GetRemainingBudget(url::Origin context_origin);
+
// Returns whether the SQLite database is open.
[[nodiscard]] bool IsOpenForTesting() const;
@@ -265,6 +302,16 @@ class SharedStorageDatabase {
void OverrideSpecialStoragePolicyForTesting(
scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy);
+ // Gets the number of entries (including stale entries) in the table
+ // `budget_mapping` for `context_origin`. Returns -1 in case of database
+ // initialization failure or SQL error.
+ [[nodiscard]] int64_t GetNumBudgetEntriesForTesting(
+ url::Origin context_origin);
+
+ // Returns the total number of entries in the table for all origins, or -1 in
+ // case of database initialization failure or SQL error.
+ [[nodiscard]] int64_t GetTotalNumBudgetEntriesForTesting();
+
// Populates the database in order to test integration with
// `content::StoragePartitionImpl` while keeping in this file the parts of
// those tests that depend on implementation details of
@@ -338,12 +385,12 @@ class SharedStorageDatabase {
base::Time new_last_used_time)
VALID_CONTEXT_REQUIRED(sequence_checker_);
- // Updates `last_used_time` to `base::Time::Now()` for `context_origin`.
+ // Updates `last_used_time` to `clock_->Now()` for `context_origin`.
[[nodiscard]] bool UpdateLastUsedTime(const std::string& context_origin)
VALID_CONTEXT_REQUIRED(sequence_checker_);
// Updates `length` by `delta` for `context_origin`. If `should_update_time`
- // is true, also updates `last_used_time` to `base::Time::Now()`.
+ // is true, also updates `last_used_time` to `clock_->Now()`.
[[nodiscard]] bool UpdateLength(const std::string& context_origin,
int64_t delta,
bool should_update_time = true)
@@ -373,7 +420,7 @@ class SharedStorageDatabase {
[[nodiscard]] bool HasCapacity(const std::string& context_origin)
VALID_CONTEXT_REQUIRED(sequence_checker_);
- // The database containing the actual data.
+ // Database containing the actual data.
sql::Database db_ GUARDED_BY_CONTEXT(sequence_checker_);
// Contains the version information.
@@ -386,17 +433,17 @@ class SharedStorageDatabase {
// Only set to true if `DBExists()
DBFileStatus db_file_status_ GUARDED_BY_CONTEXT(sequence_checker_);
- // The path to the database, if file-backed.
+ // Path to the database, if file-backed.
base::FilePath db_path_ GUARDED_BY_CONTEXT(sequence_checker_);
- // The owning partition's storage policy.
+ // Owning partition's storage policy.
scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_
GUARDED_BY_CONTEXT(sequence_checker_);
- // The maximum allowed number of entries per origin.
+ // Maximum allowed number of entries per origin.
const int64_t max_entries_per_origin_ GUARDED_BY_CONTEXT(sequence_checker_);
- // The maximum size of a string input from any origin's script. Applies
+ // Maximum size of a string input from any origin's script. Applies
// separately to both script keys and script values.
size_t max_string_length_ GUARDED_BY_CONTEXT(sequence_checker_);
@@ -407,6 +454,13 @@ class SharedStorageDatabase {
// async `Keys()` and `Entries()` iterators, respectively.
size_t max_iterator_batch_size_ GUARDED_BY_CONTEXT(sequence_checker_);
+ // Maximum number of bits of entropy allowed per origin to output via the
+ // Shared Storage API.
+ const double bit_budget_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // Interval over which `bit_budget_` is defined.
+ const base::TimeDelta budget_interval_ GUARDED_BY_CONTEXT(sequence_checker_);
+
// Clock used to determine current time. Can be overridden in tests.
raw_ptr<base::Clock> clock_ GUARDED_BY_CONTEXT(sequence_checker_);
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_database_unittest.cc b/chromium/components/services/storage/shared_storage/shared_storage_database_unittest.cc
index fd8295df579..61e3a2a5a40 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_database_unittest.cc
+++ b/chromium/components/services/storage/shared_storage/shared_storage_database_unittest.cc
@@ -43,6 +43,8 @@ using SetBehavior = SharedStorageDatabase::SetBehavior;
using OperationResult = SharedStorageDatabase::OperationResult;
using GetResult = SharedStorageDatabase::GetResult;
+const int kBudgetIntervalHours = 24;
+const int kBitBudget = 8;
const int kMaxEntriesPerOrigin = 5;
const int kMaxEntriesPerOriginForIteratorTest = 1000;
const int kMaxStringLength = 100;
@@ -91,7 +93,10 @@ class SharedStorageDatabaseTest : public testing::Test {
virtual void InitSharedStorageFeature() {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
{blink::features::kSharedStorageAPI},
- {{"MaxSharedStorageInitTries", "1"}});
+ {{"MaxSharedStorageInitTries", "1"},
+ {"SharedStorageBitBudget", base::NumberToString(kBitBudget)},
+ {"SharedStorageBudgetInterval",
+ TimeDeltaToString(base::Hours(kBudgetIntervalHours))}});
}
protected:
@@ -109,6 +114,12 @@ TEST_F(SharedStorageDatabaseTest, Version1_LoadFromFile) {
db_ = LoadFromFile("shared_storage.v1.sql");
ASSERT_TRUE(db_);
+ // Override the clock and set to the last time in the file that is used to
+ // make a budget withdrawal.
+ db_->OverrideClockForTesting(&clock_);
+ clock_.SetNow(base::Time::FromDeltaSinceWindowsEpoch(
+ base::Microseconds(13269546593856733)));
+
url::Origin google_com = url::Origin::Create(GURL("http://google.com/"));
EXPECT_EQ(db_->Get(google_com, u"key1").data, u"value1");
EXPECT_EQ(db_->Get(google_com, u"key2").data, u"value2");
@@ -174,17 +185,131 @@ TEST_F(SharedStorageDatabaseTest, Version1_LoadFromFile) {
.data,
u"k");
+ url::Origin abc_xyz = url::Origin::Create(GURL("http://abc.xyz"));
+ url::Origin grow_with_google_com =
+ url::Origin::Create(GURL("http://growwithgoogle.com"));
+ url::Origin gv_com = url::Origin::Create(GURL("http://gv.com"));
+ url::Origin waymo_com = url::Origin::Create(GURL("http://waymo.com"));
+ url::Origin withgoogle_com =
+ url::Origin::Create(GURL("http://withgoogle.com"));
+
std::vector<url::Origin> origins;
for (const auto& info : db_->FetchOrigins())
origins.push_back(info->origin);
- EXPECT_THAT(
- origins,
- ElementsAre(
- url::Origin::Create(GURL("http://abc.xyz")), chromium_org, google_com,
- google_org, url::Origin::Create(GURL("http://growwithgoogle.com")),
- url::Origin::Create(GURL("http://gv.com")),
- url::Origin::Create(GURL("http://waymo.com")),
- url::Origin::Create(GURL("http://withgoogle.com")), youtube_com));
+ EXPECT_THAT(origins, ElementsAre(abc_xyz, chromium_org, google_com,
+ google_org, grow_with_google_com, gv_com,
+ waymo_com, withgoogle_com, youtube_com));
+
+ EXPECT_DOUBLE_EQ(kBitBudget - 5.3, db_->GetRemainingBudget(abc_xyz).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(chromium_org).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(google_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 4.0, db_->GetRemainingBudget(google_org).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.2,
+ db_->GetRemainingBudget(grow_with_google_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(gv_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 4.2, db_->GetRemainingBudget(waymo_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.0,
+ db_->GetRemainingBudget(withgoogle_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(youtube_com).bits);
+
+ EXPECT_TRUE(db_->Destroy());
+}
+
+// Test loading version 1 database with no budget tables.
+TEST_F(SharedStorageDatabaseTest, Version1_LoadFromFileNoBudgetTables) {
+ db_ = LoadFromFile("shared_storage.v1.no_budget_table.sql");
+ ASSERT_TRUE(db_);
+
+ url::Origin google_com = url::Origin::Create(GURL("http://google.com/"));
+ EXPECT_EQ(db_->Get(google_com, u"key1").data, u"value1");
+ EXPECT_EQ(db_->Get(google_com, u"key2").data, u"value2");
+
+ // Because the SQL database is lazy-initialized, wait to verify tables and
+ // columns until after the first call to `Get()`.
+ ASSERT_TRUE(SqlDB());
+ VerifySharedStorageTablesAndColumns(*SqlDB());
+
+ url::Origin youtube_com = url::Origin::Create(GURL("http://youtube.com/"));
+ EXPECT_EQ(1L, db_->Length(youtube_com));
+
+ url::Origin chromium_org = url::Origin::Create(GURL("http://chromium.org/"));
+ EXPECT_EQ(db_->Get(chromium_org, u"a").data, u"");
+
+ TestSharedStorageEntriesListener listener(
+ task_environment_.GetMainThreadTaskRunner());
+ EXPECT_EQ(OperationResult::kSuccess,
+ db_->Keys(chromium_org, listener.BindNewPipeAndPassRemote()));
+ listener.Flush();
+ EXPECT_THAT(listener.TakeKeys(), ElementsAre(u"a", u"b", u"c"));
+ EXPECT_EQ("", listener.error_message());
+ EXPECT_EQ(1U, listener.BatchCount());
+ listener.VerifyNoError();
+
+ url::Origin google_org = url::Origin::Create(GURL("http://google.org/"));
+ EXPECT_EQ(
+ db_->Get(google_org, u"1").data,
+ u"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffff");
+ EXPECT_EQ(db_->Get(google_org,
+ u"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
+ .data,
+ u"k");
+
+ url::Origin abc_xyz = url::Origin::Create(GURL("http://abc.xyz"));
+ url::Origin grow_with_google_com =
+ url::Origin::Create(GURL("http://growwithgoogle.com"));
+ url::Origin gv_com = url::Origin::Create(GURL("http://gv.com"));
+ url::Origin waymo_com = url::Origin::Create(GURL("http://waymo.com"));
+ url::Origin withgoogle_com =
+ url::Origin::Create(GURL("http://withgoogle.com"));
+
+ std::vector<url::Origin> origins;
+ for (const auto& info : db_->FetchOrigins())
+ origins.push_back(info->origin);
+ EXPECT_THAT(origins, ElementsAre(abc_xyz, chromium_org, google_com,
+ google_org, grow_with_google_com, gv_com,
+ waymo_com, withgoogle_com, youtube_com));
+
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(abc_xyz).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(chromium_org).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(google_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(google_org).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget,
+ db_->GetRemainingBudget(grow_with_google_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(gv_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(waymo_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(withgoogle_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(youtube_com).bits);
EXPECT_TRUE(db_->Destroy());
}
@@ -260,7 +385,10 @@ class SharedStorageDatabaseParamTest
{{"MaxSharedStorageEntriesPerOrigin",
base::NumberToString(kMaxEntriesPerOrigin)},
{"MaxSharedStorageStringLength",
- base::NumberToString(kMaxStringLength)}});
+ base::NumberToString(kMaxStringLength)},
+ {"SharedStorageBitBudget", base::NumberToString(kBitBudget)},
+ {"SharedStorageBudgetInterval",
+ TimeDeltaToString(base::Hours(kBudgetIntervalHours))}});
}
};
@@ -279,13 +407,14 @@ TEST_P(SharedStorageDatabaseParamTest, BasicOperations) {
EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value2");
EXPECT_EQ(OperationResult::kSuccess, db_->Delete(kOrigin1, u"key1"));
- EXPECT_FALSE(db_->Get(kOrigin1, u"key1").data);
+ EXPECT_EQ(OperationResult::kKeyNotFound, db_->Get(kOrigin1, u"key1").result);
- // Check that trying to retrieve the empty key doesn't give an error, even
- // though the input is invalid and no value is found.
+ // Check that trying to retrieve the empty key returns
+ // `OperationResult::kKeyNotFound` rather than `OperationResult::kSqlError`,
+ // even though the input is considered invalid.
GetResult result = db_->Get(kOrigin1, u"");
- EXPECT_EQ(OperationResult::kSuccess, result.result);
- EXPECT_FALSE(result.data);
+ EXPECT_EQ(OperationResult::kKeyNotFound, result.result);
+ EXPECT_TRUE(result.data.empty());
// Check that trying to delete the empty key doesn't give an error, even
// though the input is invalid and no value is found to delete.
@@ -564,6 +693,112 @@ TEST_P(SharedStorageDatabaseParamTest, FetchOrigins) {
EXPECT_THAT(origins, ElementsAre(kOrigin3, kOrigin4));
}
+TEST_P(SharedStorageDatabaseParamTest, MakeBudgetWithdrawal) {
+ clock_.SetNow(base::Time::Now());
+
+ // There should be no entries in the budget table.
+ EXPECT_EQ(0L, db_->GetTotalNumBudgetEntriesForTesting());
+
+ // SQL database hasn't yet been lazy-initialized. Nevertheless, remaining
+ // budgets should be returned as the max possible.
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin1).bits);
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin2).bits);
+
+ // A withdrawal for `kOrigin1` doesn't affect `kOrigin2`.
+ EXPECT_EQ(OperationResult::kSuccess,
+ db_->MakeBudgetWithdrawal(kOrigin1, 1.75));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75, db_->GetRemainingBudget(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin2).bits);
+ EXPECT_EQ(1L, db_->GetNumBudgetEntriesForTesting(kOrigin1));
+ EXPECT_EQ(1L, db_->GetTotalNumBudgetEntriesForTesting());
+
+ // An additional withdrawal for `kOrigin1` at or near the same time as the
+ // previous one is debited appropriately.
+ EXPECT_EQ(OperationResult::kSuccess,
+ db_->MakeBudgetWithdrawal(kOrigin1, 2.5));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5,
+ db_->GetRemainingBudget(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin2).bits);
+ EXPECT_EQ(2L, db_->GetNumBudgetEntriesForTesting(kOrigin1));
+ EXPECT_EQ(2L, db_->GetTotalNumBudgetEntriesForTesting());
+
+ // A withdrawal for `kOrigin2` doesn't affect `kOrigin1`.
+ EXPECT_EQ(OperationResult::kSuccess,
+ db_->MakeBudgetWithdrawal(kOrigin2, 3.4));
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, db_->GetRemainingBudget(kOrigin2).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5,
+ db_->GetRemainingBudget(kOrigin1).bits);
+ EXPECT_EQ(2L, db_->GetNumBudgetEntriesForTesting(kOrigin1));
+ EXPECT_EQ(1L, db_->GetNumBudgetEntriesForTesting(kOrigin2));
+ EXPECT_EQ(3L, db_->GetTotalNumBudgetEntriesForTesting());
+
+ // Advance halfway through the lookback window.
+ clock_.Advance(base::Hours(kBudgetIntervalHours) / 2);
+
+ // Remaining budgets continue to take into account the withdrawals above, as
+ // they are still within the lookback window.
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, db_->GetRemainingBudget(kOrigin2).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5,
+ db_->GetRemainingBudget(kOrigin1).bits);
+
+ // An additional withdrawal for `kOrigin1` at a later time from previous ones
+ // is debited appropriately.
+ EXPECT_EQ(OperationResult::kSuccess,
+ db_->MakeBudgetWithdrawal(kOrigin1, 1.0));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5 - 1.0,
+ db_->GetRemainingBudget(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, db_->GetRemainingBudget(kOrigin2).bits);
+ EXPECT_EQ(3L, db_->GetNumBudgetEntriesForTesting(kOrigin1));
+ EXPECT_EQ(1L, db_->GetNumBudgetEntriesForTesting(kOrigin2));
+ EXPECT_EQ(4L, db_->GetTotalNumBudgetEntriesForTesting());
+
+ // Advance to the end of the initial lookback window, plus an additional
+ // microsecond to move past that window.
+ clock_.Advance(base::Hours(kBudgetIntervalHours) / 2 + base::Microseconds(1));
+
+ // Now only the single debit made within the current lookback window is
+ // counted, although the entries are still in the table because we haven't
+ // called `PurgeStaleWithdrawals()`.
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.0, db_->GetRemainingBudget(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin2).bits);
+ EXPECT_EQ(3L, db_->GetNumBudgetEntriesForTesting(kOrigin1));
+ EXPECT_EQ(1L, db_->GetNumBudgetEntriesForTesting(kOrigin2));
+ EXPECT_EQ(4L, db_->GetTotalNumBudgetEntriesForTesting());
+
+ // After `PurgeStaleOrigins()` runs, there will only be the most recent
+ // debit left in the budget table.
+ //
+ // Note: The parameter for PurgeStaleOrigins() isn't relevant here.
+ EXPECT_EQ(OperationResult::kSuccess, db_->PurgeStaleOrigins(base::Days(30)));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.0, db_->GetRemainingBudget(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin2).bits);
+ EXPECT_EQ(1L, db_->GetNumBudgetEntriesForTesting(kOrigin1));
+ EXPECT_EQ(0L, db_->GetNumBudgetEntriesForTesting(kOrigin2));
+ EXPECT_EQ(1L, db_->GetTotalNumBudgetEntriesForTesting());
+
+ // Advance to where the last debit should no longer be in the lookback window.
+ clock_.Advance(base::Hours(kBudgetIntervalHours) / 2);
+
+ // Remaining budgets should be back at the max, although there is still an
+ // entry in the table.
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin2).bits);
+ EXPECT_EQ(1L, db_->GetNumBudgetEntriesForTesting(kOrigin1));
+ EXPECT_EQ(1L, db_->GetTotalNumBudgetEntriesForTesting());
+
+ // After `PurgeStaleOrigins()` runs, the budget table will be empty.
+ //
+ // Note: The parameter for PurgeStaleOrigins() isn't relevant here.
+ EXPECT_EQ(OperationResult::kSuccess, db_->PurgeStaleOrigins(base::Days(30)));
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(kOrigin2).bits);
+ EXPECT_EQ(0L, db_->GetTotalNumBudgetEntriesForTesting());
+}
+
class SharedStorageDatabasePurgeMatchingOriginsParamTest
: public SharedStorageDatabaseTest,
public testing::WithParamInterface<PurgeMatchingOriginsParams> {
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_manager.cc b/chromium/components/services/storage/shared_storage/shared_storage_manager.cc
index f7f2e60b3c6..dd8b5e50c96 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_manager.cc
+++ b/chromium/components/services/storage/shared_storage/shared_storage_manager.cc
@@ -60,6 +60,7 @@ SharedStorageManager::~SharedStorageManager() {
void SharedStorageManager::OnMemoryPressure(
base::OnceCallback<void()> callback,
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
+ DCHECK(callback);
DCHECK(database_);
// TODO(cammie): Check if MEMORY_PRESSURE_LEVEL_MODERATE should also be
@@ -97,13 +98,14 @@ void SharedStorageManager::OnOperationResult(OperationResult result) {
void SharedStorageManager::Get(url::Origin context_origin,
std::u16string key,
base::OnceCallback<void(GetResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
auto new_callback = base::BindOnce(
[](base::WeakPtr<SharedStorageManager> manager,
base::OnceCallback<void(GetResult)> callback, GetResult result) {
if (manager)
manager->OnOperationResult(result.result);
- std::move(callback).Run(result);
+ std::move(callback).Run(std::move(result));
},
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
@@ -117,6 +119,7 @@ void SharedStorageManager::Set(
std::u16string value,
base::OnceCallback<void(OperationResult)> callback,
SharedStorageDatabase::SetBehavior behavior) {
+ DCHECK(callback);
DCHECK(database_);
database_->Set(std::move(context_origin), std::move(key), std::move(value),
GetOperationResultCallback(std::move(callback)), behavior);
@@ -127,6 +130,7 @@ void SharedStorageManager::Append(
std::u16string key,
std::u16string value,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_->Append(std::move(context_origin), std::move(key), std::move(value),
GetOperationResultCallback(std::move(callback)));
@@ -136,6 +140,7 @@ void SharedStorageManager::Delete(
url::Origin context_origin,
std::u16string key,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_->Delete(std::move(context_origin), std::move(key),
GetOperationResultCallback(std::move(callback)));
@@ -143,6 +148,7 @@ void SharedStorageManager::Delete(
void SharedStorageManager::Length(url::Origin context_origin,
base::OnceCallback<void(int)> callback) {
+ DCHECK(callback);
DCHECK(database_);
auto new_callback = base::BindOnce(
[](base::WeakPtr<SharedStorageManager> manager,
@@ -164,6 +170,7 @@ void SharedStorageManager::Keys(
shared_storage_worklet::mojom::SharedStorageEntriesListener>
pending_listener,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_->Keys(std::move(context_origin), std::move(pending_listener),
GetOperationResultCallback(std::move(callback)));
@@ -175,6 +182,7 @@ void SharedStorageManager::Entries(
shared_storage_worklet::mojom::SharedStorageEntriesListener>
pending_listener,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_->Entries(std::move(context_origin), std::move(pending_listener),
GetOperationResultCallback(std::move(callback)));
@@ -183,6 +191,7 @@ void SharedStorageManager::Entries(
void SharedStorageManager::Clear(
url::Origin context_origin,
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
DCHECK(database_);
database_->Clear(std::move(context_origin),
GetOperationResultCallback(std::move(callback)));
@@ -194,6 +203,7 @@ void SharedStorageManager::PurgeMatchingOrigins(
base::Time end,
base::OnceCallback<void(OperationResult)> callback,
bool perform_storage_cleanup) {
+ DCHECK(callback);
DCHECK(database_);
database_->PurgeMatchingOrigins(
std::move(origin_matcher), begin, end,
@@ -207,8 +217,38 @@ void SharedStorageManager::FetchOrigins(
database_->FetchOrigins(std::move(callback));
}
+void SharedStorageManager::MakeBudgetWithdrawal(
+ url::Origin context_origin,
+ double bits_debit,
+ base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
+ DCHECK(database_);
+ database_->MakeBudgetWithdrawal(
+ std::move(context_origin), bits_debit,
+ GetOperationResultCallback(std::move(callback)));
+}
+
+void SharedStorageManager::GetRemainingBudget(
+ url::Origin context_origin,
+ base::OnceCallback<void(BudgetResult)> callback) {
+ DCHECK(callback);
+ DCHECK(database_);
+ auto new_callback = base::BindOnce(
+ [](base::WeakPtr<SharedStorageManager> manager,
+ base::OnceCallback<void(BudgetResult)> callback, BudgetResult result) {
+ if (manager)
+ manager->OnOperationResult(result.result);
+ std::move(callback).Run(std::move(result));
+ },
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+
+ database_->GetRemainingBudget(std::move(context_origin),
+ std::move(new_callback));
+}
+
void SharedStorageManager::SetOnDBDestroyedCallbackForTesting(
base::OnceCallback<void(bool)> callback) {
+ DCHECK(callback);
on_db_destroyed_callback_for_testing_ = std::move(callback);
}
@@ -216,6 +256,7 @@ void SharedStorageManager::OverrideLastUsedTimeForTesting(
url::Origin context_origin,
base::Time new_last_used_time,
base::OnceCallback<void(bool)> callback) {
+ DCHECK(callback);
DCHECK(database_);
static_cast<AsyncSharedStorageDatabaseImpl*>(database_.get())
->OverrideLastUsedTimeForTesting( // IN-TEST
@@ -236,6 +277,49 @@ void SharedStorageManager::OverrideDatabaseForTesting(
database_ = std::move(override_async_database);
}
+void SharedStorageManager::GetNumBudgetEntriesForTesting(
+ url::Origin context_origin,
+ base::OnceCallback<void(int)> callback) {
+ DCHECK(callback);
+ DCHECK(database_);
+ auto new_callback = base::BindOnce(
+ [](base::WeakPtr<SharedStorageManager> manager,
+ base::OnceCallback<void(int)> callback, int num_entries) {
+ OperationResult result = (num_entries != -1)
+ ? OperationResult::kSuccess
+ : OperationResult::kSqlError;
+ if (manager)
+ manager->OnOperationResult(result);
+ std::move(callback).Run(num_entries);
+ },
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+
+ static_cast<AsyncSharedStorageDatabaseImpl*>(database_.get())
+ ->GetNumBudgetEntriesForTesting( // IN-TEST
+ std::move(context_origin), std::move(new_callback));
+}
+
+void SharedStorageManager::GetTotalNumBudgetEntriesForTesting(
+ base::OnceCallback<void(int)> callback) {
+ DCHECK(callback);
+ DCHECK(database_);
+ auto new_callback = base::BindOnce(
+ [](base::WeakPtr<SharedStorageManager> manager,
+ base::OnceCallback<void(int)> callback, int num_entries) {
+ OperationResult result = (num_entries != -1)
+ ? OperationResult::kSuccess
+ : OperationResult::kSqlError;
+ if (manager)
+ manager->OnOperationResult(result);
+ std::move(callback).Run(num_entries);
+ },
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+
+ static_cast<AsyncSharedStorageDatabaseImpl*>(database_.get())
+ ->GetTotalNumBudgetEntriesForTesting( // IN-TEST
+ std::move(new_callback));
+}
+
void SharedStorageManager::DestroyAndRecreateDatabase() {
bool recreate_in_memory = in_memory_;
@@ -278,6 +362,7 @@ void SharedStorageManager::OnDatabaseDestroyed(bool recreate_in_memory,
base::OnceCallback<void(SharedStorageManager::OperationResult)>
SharedStorageManager::GetOperationResultCallback(
base::OnceCallback<void(OperationResult)> callback) {
+ DCHECK(callback);
return base::BindOnce(
[](base::WeakPtr<SharedStorageManager> manager,
base::OnceCallback<void(OperationResult)> callback,
@@ -290,6 +375,10 @@ SharedStorageManager::GetOperationResultCallback(
}
void SharedStorageManager::PurgeStaleOrigins() {
+ // TODO(crbug.com/1317487): Move this DCHECK for
+ // `options_->origin_staleness_threshold` to `SharedStorageOptions` after
+ // removing the parameter from `SharedStorageDatabase::PurgeStaleOrigins()`
+ // and passing into the database constructor instead.
DCHECK(!options_->origin_staleness_threshold.is_zero());
DCHECK(database_);
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_manager.h b/chromium/components/services/storage/shared_storage/shared_storage_manager.h
index 663a6112351..e1979369507 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_manager.h
+++ b/chromium/components/services/storage/shared_storage/shared_storage_manager.h
@@ -40,6 +40,7 @@ class SharedStorageManager {
using SetBehavior = SharedStorageDatabase::SetBehavior;
using OperationResult = SharedStorageDatabase::OperationResult;
using GetResult = SharedStorageDatabase::GetResult;
+ using BudgetResult = SharedStorageDatabase::BudgetResult;
// A callback type to check if a given origin matches a storage policy.
// Can be passed empty/null where used, which means the origin will always
@@ -213,6 +214,20 @@ class SharedStorageManager {
base::OnceCallback<void(std::vector<mojom::StorageUsageInfoPtr>)>
callback);
+ // Makes a withdrawal of `bits_debit` stamped with the current time from the
+ // privacy budget of `context_origin`.
+ void MakeBudgetWithdrawal(url::Origin context_origin,
+ double bits_debit,
+ base::OnceCallback<void(OperationResult)> callback);
+
+ // Determines the number of bits remaining in the privacy budget of
+ // `context_origin`, where only withdrawals within the most recent
+ // `budget_interval_` are counted as still valid, and calls `callback` with
+ // this information bundled with an `OperationResult` value to indicate
+ // whether the database retrieval was successful.
+ void GetRemainingBudget(url::Origin context_origin,
+ base::OnceCallback<void(BudgetResult)> callback);
+
void SetOnDBDestroyedCallbackForTesting(
base::OnceCallback<void(bool)> callback);
@@ -226,6 +241,18 @@ class SharedStorageManager {
void OverrideDatabaseForTesting(
std::unique_ptr<AsyncSharedStorageDatabase> override_async_database);
+ // Calls `callback` with the number of entries (including stale entries) in
+ // the table `budget_mapping` for `context_origin`, or with -1 in case of
+ // database initialization failure or SQL error.
+ void GetNumBudgetEntriesForTesting(url::Origin context_origin,
+ base::OnceCallback<void(int)> callback);
+
+ // Calls `callback` with the total number of entries in the table for all
+ // origins, or with -1 in case of database initialization failure or SQL
+ // error.
+ void GetTotalNumBudgetEntriesForTesting(
+ base::OnceCallback<void(int)> callback);
+
private:
void DestroyAndRecreateDatabase();
void OnDatabaseDestroyed(bool recreate_in_memory, bool success);
@@ -235,9 +262,15 @@ class SharedStorageManager {
base::OnceCallback<void(OperationResult)> callback);
// Purges the data for any origins that haven't been written to or read from
- // for more than the `origin_staleness_threshold_`.
+ // for more than the `origin_staleness_threshold_`. Also purges, for all
+ // origins, all privacy budget withdrawals that have `time_stamps` older than
+ // the current time minus `SharedStorageDatabase::budget_interval_`.
void PurgeStaleOrigins();
+ // Processes the result of purging stale origins, then purges all privacy
+ // budget withdrawals that have `time_stamps` older than the current time
+ // minus `SharedStorageDatabase::budget_interval_`
+
// Starts the `timer_` for the next call to `PurgeStaleOrigins()`.
void OnStaleOriginsPurged(OperationResult result);
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_manager_unittest.cc b/chromium/components/services/storage/shared_storage/shared_storage_manager_unittest.cc
index 1e81f8ba8e2..7c798ee8e5f 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_manager_unittest.cc
+++ b/chromium/components/services/storage/shared_storage/shared_storage_manager_unittest.cc
@@ -47,15 +47,17 @@ using InitStatus = SharedStorageDatabase::InitStatus;
using SetBehavior = SharedStorageDatabase::SetBehavior;
using OperationResult = SharedStorageDatabase::OperationResult;
using GetResult = SharedStorageDatabase::GetResult;
+using BudgetResult = SharedStorageDatabase::BudgetResult;
using OriginMatcherFunction = SharedStorageDatabase::OriginMatcherFunction;
using DBOperation = TestDatabaseOperationReceiver::DBOperation;
using Type = DBOperation::Type;
using DBType = SharedStorageTestDBType;
using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
-std::string TimeDeltaToString(base::TimeDelta delta) {
- return base::StrCat({base::NumberToString(delta.InMilliseconds()), "ms"});
-}
+const int kBitBudget = 8;
+const int kInitialPurgeIntervalHours = 4;
+const int kRecurringPurgeIntervalHours = 2;
+const int kThresholdHours = 7;
class MockResultQueue {
public:
@@ -84,6 +86,14 @@ class MockResultQueue {
return next_result;
}
+ BudgetResult NextBudgetResult() {
+ DCHECK(!result_queue_.empty());
+ BudgetResult next_result = MakeBudgetResultForSqlError();
+ next_result.result = result_queue_.front();
+ result_queue_.pop();
+ return next_result;
+ }
+
bool NextBool() {
DCHECK(!result_queue_.empty());
bool next_success = (static_cast<int>(result_queue_.front()) < 3);
@@ -191,6 +201,18 @@ class MockAsyncSharedStorageDatabase : public AsyncSharedStorageDatabase {
callback) override {
Run(std::move(callback));
}
+ void MakeBudgetWithdrawal(
+ url::Origin context_origin,
+ double bits_debit,
+ base::OnceCallback<void(OperationResult)> callback) override {
+ Run(std::move(callback));
+ }
+ void GetRemainingBudget(
+ url::Origin context_origin,
+ base::OnceCallback<void(BudgetResult)> callback) override {
+ Run(std::move(callback));
+ }
+
void SetResultsForTesting(std::queue<OperationResult> result_queue,
base::OnceClosure callback) {
mock_result_queue_.AsyncCall(&MockResultQueue::SetResultQueue)
@@ -234,6 +256,12 @@ class MockAsyncSharedStorageDatabase : public AsyncSharedStorageDatabase {
.Then(std::move(callback));
}
+ void Run(base::OnceCallback<void(BudgetResult)> callback) {
+ DCHECK(callback);
+ mock_result_queue_.AsyncCall(&MockResultQueue::NextBudgetResult)
+ .Then(std::move(callback));
+ }
+
void Run(base::OnceCallback<void(int)> callback) {
DCHECK(callback);
mock_result_queue_.AsyncCall(&MockResultQueue::NextInt)
@@ -312,11 +340,14 @@ class SharedStorageManagerTest : public testing::Test {
// Set these intervals to be long enough not to interfere with the
// basic tests.
{{"SharedStorageStaleOriginPurgeInitialInterval",
- TimeDeltaToString(base::Hours(1))},
+ TimeDeltaToString(base::Hours(kInitialPurgeIntervalHours))},
{"SharedStorageStaleOriginPurgeRecurringInterval",
- TimeDeltaToString(base::Hours(2))},
+ TimeDeltaToString(base::Hours(kRecurringPurgeIntervalHours))},
{"SharedStorageOriginStalenessThreshold",
- TimeDeltaToString(base::Hours(4))}});
+ TimeDeltaToString(base::Hours(kThresholdHours))},
+ {"SharedStorageBitBudget", base::NumberToString(kBitBudget)},
+ {"SharedStorageBudgetInterval",
+ TimeDeltaToString(base::Hours(kBudgetIntervalHours_))}});
}
// Return the relative file path in the "storage/" subdirectory of test data
@@ -411,7 +442,7 @@ class SharedStorageManagerTest : public testing::Test {
base::test::TestFuture<GetResult> future;
GetManager()->Get(std::move(context_origin), std::move(key),
future.GetCallback());
- return future.Get();
+ return future.Take();
}
void Set(url::Origin context_origin,
@@ -643,6 +674,25 @@ class SharedStorageManagerTest : public testing::Test {
return future.Get();
}
+ OperationResult MakeBudgetWithdrawalSync(const url::Origin& context_origin,
+ double bits_debit) {
+ DCHECK(GetManager());
+
+ base::test::TestFuture<OperationResult> future;
+ GetManager()->MakeBudgetWithdrawal(std::move(context_origin), bits_debit,
+ future.GetCallback());
+ return future.Get();
+ }
+
+ BudgetResult GetRemainingBudgetSync(const url::Origin& context_origin) {
+ DCHECK(GetManager());
+
+ base::test::TestFuture<BudgetResult> future;
+ GetManager()->GetRemainingBudget(std::move(context_origin),
+ future.GetCallback());
+ return future.Take();
+ }
+
void OverrideLastUsedTime(url::Origin context_origin,
base::Time new_last_used_time,
bool* out_success) {
@@ -659,7 +709,27 @@ class SharedStorageManagerTest : public testing::Test {
std::move(context_origin), new_last_used_time, std::move(callback));
}
+ int GetNumBudgetEntriesSync(url::Origin context_origin) {
+ DCHECK(GetManager());
+
+ base::test::TestFuture<int> future;
+ GetManager()->GetNumBudgetEntriesForTesting(std::move(context_origin),
+ future.GetCallback());
+ return future.Get();
+ }
+
+ int GetTotalNumBudgetEntriesSync() {
+ DCHECK(GetManager());
+
+ base::test::TestFuture<int> future;
+ GetManager()->GetTotalNumBudgetEntriesForTesting(future.GetCallback());
+ return future.Get();
+ }
+
protected:
+ static constexpr int kBudgetIntervalHours_ =
+ kInitialPurgeIntervalHours + 2 * kRecurringPurgeIntervalHours;
+
base::test::ScopedFeatureList scoped_feature_list_;
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -763,7 +833,7 @@ TEST_F(SharedStorageManagerFromFileV1Test, Version1_LoadFromFile) {
origins.push_back(info->origin);
EXPECT_THAT(
origins,
- testing::UnorderedElementsAre(
+ ElementsAre(
url::Origin::Create(GURL("http://abc.xyz")), chromium_org, google_com,
google_org, url::Origin::Create(GURL("http://growwithgoogle.com")),
url::Origin::Create(GURL("http://gv.com")),
@@ -771,6 +841,110 @@ TEST_F(SharedStorageManagerFromFileV1Test, Version1_LoadFromFile) {
url::Origin::Create(GURL("http://withgoogle.com")), youtube_com));
}
+class SharedStorageManagerFromFileV1NoBudgetTableTest
+ : public SharedStorageManagerFromFileV1Test {
+ public:
+ const char* GetRelativeFilePath() override {
+ return "shared_storage.v1.no_budget_table.sql";
+ }
+};
+
+// Test loading version 1 database.
+TEST_F(SharedStorageManagerFromFileV1NoBudgetTableTest,
+ Version1_LoadFromFileNoBudgetTable) {
+ url::Origin google_com = url::Origin::Create(GURL("http://google.com/"));
+ EXPECT_EQ(GetSync(google_com, u"key1").data, u"value1");
+ EXPECT_EQ(GetSync(google_com, u"key2").data, u"value2");
+
+ url::Origin youtube_com = url::Origin::Create(GURL("http://youtube.com/"));
+ EXPECT_EQ(1L, LengthSync(youtube_com));
+
+ url::Origin chromium_org = url::Origin::Create(GURL("http://chromium.org/"));
+ EXPECT_EQ(GetSync(chromium_org, u"a").data, u"");
+
+ TestSharedStorageEntriesListenerUtility listener_utility(
+ task_environment_.GetMainThreadTaskRunner());
+ size_t id1 = listener_utility.RegisterListener();
+ EXPECT_EQ(OperationResult::kSuccess,
+ KeysSync(chromium_org,
+ listener_utility.BindNewPipeAndPassRemoteForId(id1)));
+ listener_utility.FlushForId(id1);
+ EXPECT_THAT(listener_utility.TakeKeysForId(id1),
+ ElementsAre(u"a", u"b", u"c"));
+ EXPECT_EQ(1U, listener_utility.BatchCountForId(id1));
+ listener_utility.VerifyNoErrorForId(id1);
+
+ size_t id2 = listener_utility.RegisterListener();
+ EXPECT_EQ(OperationResult::kSuccess,
+ EntriesSync(chromium_org,
+ listener_utility.BindNewPipeAndPassRemoteForId(id2)));
+ listener_utility.FlushForId(id2);
+ EXPECT_THAT(
+ listener_utility.TakeEntriesForId(id2),
+ ElementsAre(std::make_pair(u"a", u""), std::make_pair(u"b", u"hello"),
+ std::make_pair(u"c", u"goodbye")));
+ EXPECT_EQ(1U, listener_utility.BatchCountForId(id2));
+ listener_utility.VerifyNoErrorForId(id2);
+
+ url::Origin google_org = url::Origin::Create(GURL("http://google.org/"));
+ EXPECT_EQ(
+ GetSync(google_org, u"1").data,
+ u"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffff");
+ EXPECT_EQ(GetSync(google_org,
+ u"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
+ .data,
+ u"k");
+
+ std::vector<mojom::StorageUsageInfoPtr> infos = FetchOriginsSync();
+ std::vector<url::Origin> origins;
+ for (const auto& info : infos)
+ origins.push_back(info->origin);
+ EXPECT_THAT(
+ origins,
+ ElementsAre(
+ url::Origin::Create(GURL("http://abc.xyz")), chromium_org, google_com,
+ google_org, url::Origin::Create(GURL("http://growwithgoogle.com")),
+ url::Origin::Create(GURL("http://gv.com")),
+ url::Origin::Create(GURL("http://waymo.com")),
+ url::Origin::Create(GURL("http://withgoogle.com")), youtube_com));
+
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(chromium_org).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(google_com).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(google_org).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(youtube_com).bits);
+}
+
class SharedStorageManagerParamTest
: public SharedStorageManagerTest,
public testing::WithParamInterface<SharedStorageWrappedBool> {
@@ -795,7 +969,7 @@ TEST_P(SharedStorageManagerParamTest, BasicOperations) {
EXPECT_EQ(GetSync(kOrigin1, u"key1").data, u"value2");
EXPECT_EQ(OperationResult::kSuccess, DeleteSync(kOrigin1, u"key1"));
- EXPECT_FALSE(GetSync(kOrigin1, u"key1").data);
+ EXPECT_EQ(OperationResult::kKeyNotFound, GetSync(kOrigin1, u"key1").result);
}
TEST_P(SharedStorageManagerParamTest, IgnoreIfPresent) {
@@ -1047,20 +1221,100 @@ TEST_P(SharedStorageManagerParamTest, AdvanceTime_StaleOriginsPurged) {
EXPECT_EQ(OperationResult::kSet, SetSync(kOrigin1, u"key1", u"value1"));
EXPECT_FALSE(FetchOriginsSync().empty());
- // Initial interval for checking origin staleness is 1 hour for this test.
- task_environment_.FastForwardBy(base::Hours(1));
+ // Initial interval for checking origin staleness is
+ // `kInitialPurgeIntervalHours` hours for this test.
+ task_environment_.FastForwardBy(base::Hours(kInitialPurgeIntervalHours));
EXPECT_FALSE(FetchOriginsSync().empty());
- // Subsequent intervals are 2 hours each.
- task_environment_.FastForwardBy(base::Hours(2));
+ // Subsequent intervals are `kRecurringPurgeIntervalHours` hours each.
+ task_environment_.FastForwardBy(base::Hours(kRecurringPurgeIntervalHours));
EXPECT_FALSE(FetchOriginsSync().empty());
- // We have set the staleness threshold to 4 hours for this test. So `kOrigin1`
- // should now be cleared.
- task_environment_.FastForwardBy(base::Hours(2));
+ // We have set the staleness threshold to `kThresholdHours` hours for this
+ // test. So `kOrigin1` should now be cleared.
+ task_environment_.FastForwardBy(base::Hours(kThresholdHours));
EXPECT_TRUE(FetchOriginsSync().empty());
}
+// Synchronously tests budget operations.
+TEST_P(SharedStorageManagerParamTest, SyncMakeBudgetWithdrawal) {
+ // There should be no entries in the budget table.
+ EXPECT_EQ(0, GetTotalNumBudgetEntriesSync());
+
+ // SQL database hasn't yet been lazy-initialized. Nevertheless, remaining
+ // budgets should be returned as the max possible.
+ const url::Origin kOrigin1 =
+ url::Origin::Create(GURL("http://www.example1.test"));
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin1).bits);
+ const url::Origin kOrigin2 =
+ url::Origin::Create(GURL("http://www.example2.test"));
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+
+ // A withdrawal for `kOrigin1` doesn't affect `kOrigin2`.
+ EXPECT_EQ(OperationResult::kSuccess,
+ MakeBudgetWithdrawalSync(kOrigin1, 1.75));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75, GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(1, GetTotalNumBudgetEntriesSync());
+
+ // An additional withdrawal for `kOrigin1` at or near the same time as the
+ // previous one is debited appropriately.
+ EXPECT_EQ(OperationResult::kSuccess, MakeBudgetWithdrawalSync(kOrigin1, 2.5));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5,
+ GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(2, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(2, GetTotalNumBudgetEntriesSync());
+
+ // A withdrawal for `kOrigin2` doesn't affect `kOrigin1`.
+ EXPECT_EQ(OperationResult::kSuccess, MakeBudgetWithdrawalSync(kOrigin2, 3.4));
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5,
+ GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_EQ(2, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin2));
+ EXPECT_EQ(3, GetTotalNumBudgetEntriesSync());
+
+ // Advance partway through the lookback window, to the point where the first
+ // call to `PurgeStaleOrigins()` happens.
+ task_environment_.FastForwardBy(base::Hours(kInitialPurgeIntervalHours));
+
+ // Remaining budgets continue to take into account the withdrawals above, as
+ // they are still within the lookback window.
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5,
+ GetRemainingBudgetSync(kOrigin1).bits);
+
+ // An additional withdrawal for `kOrigin1` at a later time from previous ones
+ // is debited appropriately.
+ EXPECT_EQ(OperationResult::kSuccess, MakeBudgetWithdrawalSync(kOrigin1, 1.0));
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.75 - 2.5 - 1.0,
+ GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget - 3.4, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(3, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin2));
+ EXPECT_EQ(4, GetTotalNumBudgetEntriesSync());
+
+ // Advance further through the lookback window, to the point where the second
+ // call to `PurgeStaleOrigins()` happens.
+ task_environment_.FastForwardBy(base::Hours(kRecurringPurgeIntervalHours));
+ // Advance further through the lookback window, to the point where the third
+ // call to `PurgeStaleOrigins()` happens.
+ task_environment_.FastForwardBy(base::Hours(kRecurringPurgeIntervalHours));
+ // Advance further through the lookback window, to the point where the fourth
+ // call to `PurgeStaleOrigins()` happens.
+ task_environment_.FastForwardBy(base::Hours(kRecurringPurgeIntervalHours));
+
+ // After `PurgeStaleOrigins()` runs via the timer, there will only be the most
+ // recent debit left in the budget table.
+ EXPECT_DOUBLE_EQ(kBitBudget - 1.0, GetRemainingBudgetSync(kOrigin1).bits);
+ EXPECT_DOUBLE_EQ(kBitBudget, GetRemainingBudgetSync(kOrigin2).bits);
+ EXPECT_EQ(1, GetNumBudgetEntriesSync(kOrigin1));
+ EXPECT_EQ(0, GetNumBudgetEntriesSync(kOrigin2));
+ EXPECT_EQ(1, GetTotalNumBudgetEntriesSync());
+}
+
class SharedStorageManagerErrorParamTest
: public SharedStorageManagerTest,
public testing::WithParamInterface<SharedStorageWrappedBool> {
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_options.cc b/chromium/components/services/storage/shared_storage/shared_storage_options.cc
index e4c6e919f04..3f8596b2d00 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_options.cc
+++ b/chromium/components/services/storage/shared_storage/shared_storage_options.cc
@@ -28,6 +28,8 @@ std::unique_ptr<SharedStorageOptions> SharedStorageOptions::Create() {
blink::features::kMaxSharedStorageStringLength.Get(),
blink::features::kMaxSharedStorageInitTries.Get(),
blink::features::kMaxSharedStorageIteratorBatchSize.Get(),
+ blink::features::kSharedStorageBitBudget.Get(),
+ blink::features::kSharedStorageBudgetInterval.Get(),
blink::features::kSharedStorageStaleOriginPurgeInitialInterval.Get(),
blink::features::kSharedStorageStaleOriginPurgeRecurringInterval.Get(),
blink::features::kSharedStorageOriginStalenessThreshold.Get());
@@ -40,6 +42,8 @@ SharedStorageOptions::SharedStorageOptions(
int max_string_length,
int max_init_tries,
int max_iterator_batch_size,
+ int bit_budget,
+ base::TimeDelta budget_interval,
base::TimeDelta stale_origin_purge_initial_interval,
base::TimeDelta stale_origin_purge_recurring_interval,
base::TimeDelta origin_staleness_threshold)
@@ -49,18 +53,29 @@ SharedStorageOptions::SharedStorageOptions(
max_string_length(max_string_length),
max_init_tries(max_init_tries),
max_iterator_batch_size(max_iterator_batch_size),
+ bit_budget(bit_budget),
+ budget_interval(budget_interval),
stale_origin_purge_initial_interval(stale_origin_purge_initial_interval),
stale_origin_purge_recurring_interval(
stale_origin_purge_recurring_interval),
origin_staleness_threshold(origin_staleness_threshold) {
DCHECK(IsValidPageSize(max_page_size));
+ DCHECK_GT(max_entries_per_origin, 0);
+ DCHECK_GT(max_string_length, 0);
+ DCHECK_GT(max_init_tries, 0);
+ DCHECK_GT(max_iterator_batch_size, 0);
+ DCHECK_GT(bit_budget, 0);
+ DCHECK(budget_interval.is_positive());
+ DCHECK(stale_origin_purge_initial_interval.is_positive());
+ DCHECK(stale_origin_purge_recurring_interval.is_positive());
+ DCHECK(origin_staleness_threshold.is_positive());
}
std::unique_ptr<SharedStorageDatabaseOptions>
SharedStorageOptions::GetDatabaseOptions() {
return std::make_unique<SharedStorageDatabaseOptions>(
max_page_size, max_cache_size, max_entries_per_origin, max_string_length,
- max_init_tries, max_iterator_batch_size);
+ max_init_tries, max_iterator_batch_size, bit_budget, budget_interval);
}
SharedStorageDatabaseOptions::SharedStorageDatabaseOptions(
@@ -69,14 +84,24 @@ SharedStorageDatabaseOptions::SharedStorageDatabaseOptions(
int max_entries_per_origin,
int max_string_length,
int max_init_tries,
- int max_iterator_batch_size)
+ int max_iterator_batch_size,
+ int bit_budget,
+ base::TimeDelta budget_interval)
: max_page_size(max_page_size),
max_cache_size(max_cache_size),
max_entries_per_origin(max_entries_per_origin),
max_string_length(max_string_length),
max_init_tries(max_init_tries),
- max_iterator_batch_size(max_iterator_batch_size) {
+ max_iterator_batch_size(max_iterator_batch_size),
+ bit_budget(bit_budget),
+ budget_interval(budget_interval) {
DCHECK(IsValidPageSize(max_page_size));
+ DCHECK_GT(max_entries_per_origin, 0);
+ DCHECK_GT(max_string_length, 0);
+ DCHECK_GT(max_init_tries, 0);
+ DCHECK_GT(max_iterator_batch_size, 0);
+ DCHECK_GT(bit_budget, 0);
+ DCHECK(budget_interval.is_positive());
}
} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_options.h b/chromium/components/services/storage/shared_storage/shared_storage_options.h
index 8c34f09c706..c04484b758b 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_options.h
+++ b/chromium/components/services/storage/shared_storage/shared_storage_options.h
@@ -16,8 +16,8 @@ struct SharedStorageDatabaseOptions;
// Bundles Finch-configurable constants for the `SharedStorageManager`,
// `AsyncSharedStorageDatabase`, and `SharedStorageDatabase` classes.
struct SharedStorageOptions {
- // The static `Create()` method accesses field trial params to populate one or
- // more attributes, and so must be called on the main thread.
+ // Accesses field trial params to populate one or more attributes, and so must
+ // be called on the main thread.
static std::unique_ptr<SharedStorageOptions> Create();
SharedStorageOptions(int max_page_size,
@@ -26,6 +26,8 @@ struct SharedStorageOptions {
int max_string_length,
int max_init_tries,
int max_iterator_batch_size,
+ int bit_budget,
+ base::TimeDelta budget_interval,
base::TimeDelta stale_origin_purge_initial_interval,
base::TimeDelta stale_origin_purge_recurring_interval,
base::TimeDelta origin_staleness_threshold);
@@ -34,20 +36,20 @@ struct SharedStorageOptions {
// be forwarded to `AsyncSharedStorageDatabase` and `SharedStorageDatabase`.
std::unique_ptr<SharedStorageDatabaseOptions> GetDatabaseOptions();
- // The max size of a database page, in bytes. Must be a power of 2 between
+ // Maximum size of a database page, in bytes. Must be a power of 2 between
// 512 and 65536 inclusive.
const int max_page_size;
- // The max size of the database cache, in pages.
+ // Maximum size of the database cache, in pages.
const int max_cache_size;
- // The maximum number of entries allowed per origin.
+ // Maximum number of entries allowed per origin.
const int max_entries_per_origin;
- // The maximum allowed string length for each script key or script value.
+ // Maximum allowed string length for each script key or script value.
const int max_string_length;
- // The maximum number of times that `SharedStorageDatabase` will try to
+ // Maximum number of times that `SharedStorageDatabase` will try to
// initialize the SQL database.
const int max_init_tries;
@@ -55,14 +57,21 @@ struct SharedStorageOptions {
// async `Keys()` and `Entries()` iterators, respectively.
const int max_iterator_batch_size;
- // The initial interval at which stale origins are purged.
+ // Maximum number of bits of entropy allowed per origin to output via the
+ // Shared Storage API.
+ const int bit_budget;
+
+ // Interval over which `bit_budget` is defined.
+ const base::TimeDelta budget_interval;
+
+ // Initial interval at which stale origins are purged.
const base::TimeDelta stale_origin_purge_initial_interval;
- // The recurring interval at which stale origins are purged. May differ from
+ // Recurring interval at which stale origins are purged. May differ from
// the initial interval.
const base::TimeDelta stale_origin_purge_recurring_interval;
- // The amount of time that an origin needs to be inactive in order for it to
+ // Amount of time that an origin needs to be inactive in order for it to
// be deemed stale.
const base::TimeDelta origin_staleness_threshold;
};
@@ -80,28 +89,37 @@ struct SharedStorageDatabaseOptions {
int max_entries_per_origin,
int max_string_length,
int max_init_tries,
- int max_iterator_batch_size);
+ int max_iterator_batch_size,
+ int bit_budget,
+ base::TimeDelta budget_interval);
- // The max size of a database page, in bytes. Must be a power of 2 between
+ // Maximum size of a database page, in bytes. Must be a power of 2 between
// 512 and 65536 inclusive.
const int max_page_size;
- // The max size of the database cache, in pages.
+ // Maximum size of the database cache, in pages.
const int max_cache_size;
- // The maximum number of entries allowed per origin.
+ // Maximum number of entries allowed per origin.
const int max_entries_per_origin;
- // The maximum allowed string length for each script key or script value.
+ // Maximum allowed string length for each script key or script value.
const int max_string_length;
- // The maximum number of times that `SharedStorageDatabase` will try to
+ // Maximum number of times that `SharedStorageDatabase` will try to
// initialize the SQL database.
const int max_init_tries;
// Maximum number of keys or key-value pairs returned per batch by the
// async `Keys()` and `Entries()` iterators, respectively.
const int max_iterator_batch_size;
+
+ // Maximum number of bits of entropy allowed per origin to output via the
+ // Shared Storage API.
+ const int bit_budget;
+
+ // Interval over which `bit_budget` is defined.
+ const base::TimeDelta budget_interval;
};
} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_test_utils.cc b/chromium/components/services/storage/shared_storage/shared_storage_test_utils.cc
index bc761fcf6ea..e3a1dd6d46e 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_test_utils.cc
+++ b/chromium/components/services/storage/shared_storage/shared_storage_test_utils.cc
@@ -16,7 +16,6 @@
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
#include "sql/database.h"
@@ -30,13 +29,16 @@ TestDatabaseOperationReceiver::DBOperation::DBOperation(Type type)
: type(type) {
DCHECK(type == Type::DB_IS_OPEN || type == Type::DB_STATUS ||
type == Type::DB_DESTROY || type == Type::DB_TRIM_MEMORY ||
- type == Type::DB_FETCH_ORIGINS || type == Type::DB_PURGE_STALE);
+ type == Type::DB_FETCH_ORIGINS ||
+ type == Type::DB_GET_TOTAL_NUM_BUDGET);
}
TestDatabaseOperationReceiver::DBOperation::DBOperation(Type type,
url::Origin origin)
: type(type), origin(std::move(origin)) {
- DCHECK(type == Type::DB_LENGTH || type == Type::DB_CLEAR);
+ DCHECK(type == Type::DB_LENGTH || type == Type::DB_CLEAR ||
+ type == Type::DB_GET_REMAINING_BUDGET ||
+ type == Type::DB_GET_NUM_BUDGET);
}
TestDatabaseOperationReceiver::DBOperation::DBOperation(
@@ -47,6 +49,7 @@ TestDatabaseOperationReceiver::DBOperation::DBOperation(
DCHECK(type == Type::DB_GET || type == Type::DB_SET ||
type == Type::DB_APPEND || type == Type::DB_DELETE ||
type == Type::DB_KEYS || type == Type::DB_ENTRIES ||
+ type == Type::DB_MAKE_BUDGET_WITHDRAWAL ||
type == Type::DB_OVERRIDE_TIME);
}
@@ -159,6 +162,26 @@ TestDatabaseOperationReceiver::MakeGetResultCallback(
base::Unretained(this), current_operation, out_result);
}
+void TestDatabaseOperationReceiver::BudgetResultCallbackBase(
+ const DBOperation& current_operation,
+ BudgetResult* out_result,
+ BudgetResult result) {
+ DCHECK(out_result);
+ *out_result = std::move(result);
+
+ if (ExpectationsMet(current_operation) && loop_.running())
+ Finish();
+}
+
+base::OnceCallback<void(BudgetResult)>
+TestDatabaseOperationReceiver::MakeBudgetResultCallback(
+ const DBOperation& current_operation,
+ BudgetResult* out_result) {
+ return base::BindOnce(
+ &TestDatabaseOperationReceiver::BudgetResultCallbackBase,
+ base::Unretained(this), current_operation, out_result);
+}
+
void TestDatabaseOperationReceiver::OperationResultCallbackBase(
const DBOperation& current_operation,
OperationResult* out_result,
@@ -481,11 +504,12 @@ std::string PrintToString(const PurgeMatchingOriginsParams& p) {
}
void VerifySharedStorageTablesAndColumns(sql::Database& db) {
- // `meta`, `values_mapping`, and `per_origin_mapping`.
- EXPECT_EQ(3u, sql::test::CountSQLTables(&db));
+ // `meta`, `values_mapping`, `per_origin_mapping`, and budget_mapping.
+ EXPECT_EQ(4u, sql::test::CountSQLTables(&db));
- // Implicit index on `meta` and `per_origin_mapping_last_used_time_idx`.
- EXPECT_EQ(2u, sql::test::CountSQLIndices(&db));
+ // Implicit index on `meta`, `per_origin_mapping_last_used_time_idx`,
+ // and budget_mapping_origin_time_stamp_idx.
+ EXPECT_EQ(3u, sql::test::CountSQLIndices(&db));
// `key` and `value`.
EXPECT_EQ(2u, sql::test::CountTableColumns(&db, "meta"));
@@ -515,4 +539,12 @@ bool CreateDatabaseFromSQL(const base::FilePath& db_path,
return sql::test::CreateDatabaseFromSQL(db_path, dir.AppendASCII(ascii_path));
}
+std::string TimeDeltaToString(base::TimeDelta delta) {
+ return base::StrCat({base::NumberToString(delta.InMilliseconds()), "ms"});
+}
+
+BudgetResult MakeBudgetResultForSqlError() {
+ return BudgetResult(0.0, OperationResult::kSqlError);
+}
+
} // namespace storage
diff --git a/chromium/components/services/storage/shared_storage/shared_storage_test_utils.h b/chromium/components/services/storage/shared_storage/shared_storage_test_utils.h
index 04cc52a801d..1f62f097826 100644
--- a/chromium/components/services/storage/shared_storage/shared_storage_test_utils.h
+++ b/chromium/components/services/storage/shared_storage/shared_storage_test_utils.h
@@ -35,6 +35,7 @@ using InitStatus = SharedStorageDatabase::InitStatus;
using SetBehavior = SharedStorageDatabase::SetBehavior;
using OperationResult = SharedStorageDatabase::OperationResult;
using GetResult = SharedStorageDatabase::GetResult;
+using BudgetResult = SharedStorageDatabase::BudgetResult;
using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
// For categorizing test databases.
@@ -64,9 +65,13 @@ class TestDatabaseOperationReceiver {
DB_PURGE_MATCHING = 11,
DB_PURGE_STALE = 12,
DB_FETCH_ORIGINS = 13,
- DB_IS_OPEN = 14,
- DB_STATUS = 15,
- DB_OVERRIDE_TIME = 16,
+ DB_MAKE_BUDGET_WITHDRAWAL = 14,
+ DB_GET_REMAINING_BUDGET = 15,
+ DB_IS_OPEN = 16,
+ DB_STATUS = 17,
+ DB_OVERRIDE_TIME = 18,
+ DB_GET_NUM_BUDGET = 19,
+ DB_GET_TOTAL_NUM_BUDGET = 20,
} type;
url::Origin origin;
std::vector<std::u16string> params;
@@ -110,6 +115,13 @@ class TestDatabaseOperationReceiver {
const DBOperation& current_operation,
GetResult* out_result);
+ void BudgetResultCallbackBase(const DBOperation& current_operation,
+ BudgetResult* out_result,
+ BudgetResult result);
+ base::OnceCallback<void(BudgetResult)> MakeBudgetResultCallback(
+ const DBOperation& current_operation,
+ BudgetResult* out_result);
+
void OperationResultCallbackBase(const DBOperation& current_operation,
OperationResult* out_result,
OperationResult result);
@@ -305,6 +317,10 @@ void VerifySharedStorageTablesAndColumns(sql::Database& db);
[[nodiscard]] bool CreateDatabaseFromSQL(const base::FilePath& db_path,
const char* ascii_path);
+[[nodiscard]] std::string TimeDeltaToString(base::TimeDelta delta);
+
+[[nodiscard]] BudgetResult MakeBudgetResultForSqlError();
+
} // namespace storage
#endif // COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_TEST_UTILS_H_
diff --git a/chromium/components/services/unzip/BUILD.gn b/chromium/components/services/unzip/BUILD.gn
index a21e47cabef..101ad663dc1 100644
--- a/chromium/components/services/unzip/BUILD.gn
+++ b/chromium/components/services/unzip/BUILD.gn
@@ -46,6 +46,7 @@ bundle_data("unit_tests_bundle_data") {
visibility = [ ":unit_tests" ]
testonly = true
sources = [
+ "//components/test/data/unzip_service/Duplicate Filenames.zip",
"//components/test/data/unzip_service/SJIS 00.zip",
"//components/test/data/unzip_service/SJIS 01.zip",
"//components/test/data/unzip_service/SJIS 02.zip",
@@ -61,7 +62,10 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/unzip_service/SJIS 12.zip",
"//components/test/data/unzip_service/SJIS 13.zip",
"//components/test/data/unzip_service/UTF8 (Bug 903664).zip",
+ "//components/test/data/unzip_service/Wrong CRC.zip",
"//components/test/data/unzip_service/bad_archive.zip",
+ "//components/test/data/unzip_service/bug953599.zip",
+ "//components/test/data/unzip_service/encrypted_archive.zip",
"//components/test/data/unzip_service/good_archive.zip",
]
outputs = [ "{{bundle_resources_dir}}/" +
diff --git a/chromium/components/services/unzip/public/cpp/unzip.cc b/chromium/components/services/unzip/public/cpp/unzip.cc
index ff33ccea989..90e753613fe 100644
--- a/chromium/components/services/unzip/public/cpp/unzip.cc
+++ b/chromium/components/services/unzip/public/cpp/unzip.cc
@@ -36,7 +36,8 @@ std::string Redact(const base::FilePath& path) {
}
class UnzipParams : public base::RefCounted<UnzipParams>,
- public unzip::mojom::UnzipFilter {
+ public unzip::mojom::UnzipFilter,
+ public unzip::mojom::UnzipListener {
public:
UnzipParams(mojo::PendingRemote<mojom::Unzipper> unzipper,
UnzipCallback callback)
@@ -59,6 +60,15 @@ class UnzipParams : public base::RefCounted<UnzipParams>,
filter_callback_ = std::move(filter_callback);
}
+ void SetListener(UnzipListenerCallback listener_callback) {
+ DCHECK(listener_callback);
+ listener_callback_ = std::move(listener_callback);
+ }
+
+ mojo::Receiver<unzip::mojom::UnzipListener>* GetListenerReceiver() {
+ return &listener_receiver_;
+ }
+
private:
friend class base::RefCounted<UnzipParams>;
@@ -71,11 +81,19 @@ class UnzipParams : public base::RefCounted<UnzipParams>,
std::move(callback).Run(filter_callback_.Run(path));
}
- // The Remote and UnzipFilter are stored so they do not get deleted before the
- // callback runs.
+ // unzip::mojom::UnzipListener implementation:
+ void OnProgress(uint64_t bytes) override {
+ DCHECK(listener_callback_);
+ listener_callback_.Run(bytes);
+ }
+
+ // The Remote, UnzipFilter and UnzipListener are stored so they do not
+ // get deleted before the callback runs.
mojo::Remote<mojom::Unzipper> unzipper_;
mojo::Receiver<unzip::mojom::UnzipFilter> filter_receiver_{this};
UnzipFilterCallback filter_callback_;
+ mojo::Receiver<unzip::mojom::UnzipListener> listener_receiver_{this};
+ UnzipListenerCallback listener_callback_;
UnzipCallback callback_;
};
@@ -112,10 +130,38 @@ class DetectEncodingParams : public base::RefCounted<DetectEncodingParams> {
const base::TimeTicks start_time_ = base::TimeTicks::Now();
};
+class GetExtractedInfoParams : public base::RefCounted<GetExtractedInfoParams> {
+ public:
+ GetExtractedInfoParams(mojo::PendingRemote<mojom::Unzipper> unzipper,
+ GetExtractedInfoCallback callback)
+ : unzipper_(std::move(unzipper)), callback_(std::move(callback)) {}
+
+ mojo::Remote<mojom::Unzipper>& unzipper() { return unzipper_; }
+
+ void InvokeCallback(mojom::InfoPtr info) {
+ if (callback_) {
+ // TODO(crbug.com/953256) Add UMA timing.
+ std::move(callback_).Run(std::move(info));
+ }
+
+ unzipper_.reset();
+ }
+
+ private:
+ friend class base::RefCounted<GetExtractedInfoParams>;
+
+ ~GetExtractedInfoParams() = default;
+
+ // The Remote is stored so it does not get deleted before the callback runs.
+ mojo::Remote<mojom::Unzipper> unzipper_;
+ GetExtractedInfoCallback callback_;
+};
+
void DoUnzip(mojo::PendingRemote<mojom::Unzipper> unzipper,
const base::FilePath& zip_path,
const base::FilePath& output_dir,
UnzipFilterCallback filter_callback,
+ UnzipListenerCallback listener_callback,
mojom::UnzipOptionsPtr options,
UnzipCallback result_callback) {
base::File zip_file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
@@ -146,9 +192,17 @@ void DoUnzip(mojo::PendingRemote<mojom::Unzipper> unzipper,
std::move(filter_callback));
}
+ mojo::PendingRemote<unzip::mojom::UnzipListener> listener_remote;
+ if (listener_callback) {
+ mojo::Receiver<unzip::mojom::UnzipListener>* listener =
+ unzip_params->GetListenerReceiver();
+ unzip_params->SetListener(std::move(listener_callback));
+ listener_remote = listener->BindNewPipeAndPassRemote();
+ }
+
unzip_params->unzipper()->Unzip(
std::move(zip_file), std::move(directory_remote), std::move(options),
- std::move(filter_remote),
+ std::move(filter_remote), std::move(listener_remote),
base::BindOnce(&UnzipParams::InvokeCallback, unzip_params));
}
@@ -177,6 +231,32 @@ void DoDetectEncoding(mojo::PendingRemote<mojom::Unzipper> unzipper,
base::BindOnce(&DetectEncodingParams::InvokeCallback, params));
}
+void DoGetExtractedInfo(mojo::PendingRemote<mojom::Unzipper> unzipper,
+ const base::FilePath& zip_path,
+ GetExtractedInfoCallback result_callback) {
+ base::File zip_file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ unzip::mojom::InfoPtr info = unzip::mojom::Info::New(false, 0, false);
+ if (!zip_file.IsValid()) {
+ LOG(ERROR) << "Cannot open ZIP archive " << Redact(zip_path) << ": "
+ << base::File::ErrorToString(zip_file.error_details());
+ std::move(result_callback).Run(std::move(info));
+ return;
+ }
+
+ // |result_callback| is shared between the connection error handler and the
+ // GetExtractedInfo call using a refcounted GetExtractedInfoParams object that
+ // owns |result_callback|.
+ auto params = base::MakeRefCounted<GetExtractedInfoParams>(
+ std::move(unzipper), std::move(result_callback));
+
+ params->unzipper().set_disconnect_handler(base::BindOnce(
+ &GetExtractedInfoParams::InvokeCallback, params, std::move(info)));
+
+ params->unzipper()->GetExtractedInfo(
+ std::move(zip_file),
+ base::BindOnce(&GetExtractedInfoParams::InvokeCallback, params));
+}
+
} // namespace
void Unzip(mojo::PendingRemote<mojom::Unzipper> unzipper,
@@ -191,6 +271,7 @@ void Unzip(mojo::PendingRemote<mojom::Unzipper> unzipper,
const base::FilePath& zip_file,
const base::FilePath& output_dir,
mojom::UnzipOptionsPtr options,
+ UnzipListenerCallback listener_callback,
UnzipCallback result_callback) {
DCHECK(!result_callback.is_null());
@@ -201,7 +282,10 @@ void Unzip(mojo::PendingRemote<mojom::Unzipper> unzipper,
runner->PostTask(
FROM_HERE,
base::BindOnce(&DoUnzip, std::move(unzipper), zip_file, output_dir,
- UnzipFilterCallback(), std::move(options),
+ UnzipFilterCallback(),
+ base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
+ std::move(listener_callback)),
+ std::move(options),
base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
std::move(result_callback))));
}
@@ -220,7 +304,8 @@ void UnzipWithFilter(mojo::PendingRemote<mojom::Unzipper> unzipper,
runner->PostTask(
FROM_HERE,
base::BindOnce(&DoUnzip, std::move(unzipper), zip_path, output_dir,
- filter_callback, unzip::mojom::UnzipOptions::New(),
+ filter_callback, UnzipListenerCallback(),
+ unzip::mojom::UnzipOptions::New(),
base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
std::move(result_callback))));
}
@@ -239,4 +324,18 @@ void DetectEncoding(mojo::PendingRemote<mojom::Unzipper> unzipper,
std::move(result_callback))));
}
+void GetExtractedInfo(mojo::PendingRemote<mojom::Unzipper> unzipper,
+ const base::FilePath& zip_path,
+ GetExtractedInfoCallback result_callback) {
+ const scoped_refptr<base::SequencedTaskRunner> runner =
+ base::ThreadPool::CreateSequencedTaskRunner(
+ {base::TaskPriority::USER_VISIBLE, base::MayBlock(),
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+ runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(&DoGetExtractedInfo, std::move(unzipper), zip_path,
+ base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
+ std::move(result_callback))));
+}
+
} // namespace unzip
diff --git a/chromium/components/services/unzip/public/cpp/unzip.h b/chromium/components/services/unzip/public/cpp/unzip.h
index 53fdc422813..ead7290a051 100644
--- a/chromium/components/services/unzip/public/cpp/unzip.h
+++ b/chromium/components/services/unzip/public/cpp/unzip.h
@@ -33,10 +33,12 @@ void UnzipWithFilter(mojo::PendingRemote<mojom::Unzipper> unzipper,
UnzipFilterCallback filter_callback,
UnzipCallback result_callback);
+using UnzipListenerCallback = base::RepeatingCallback<void(uint64_t bytes)>;
void Unzip(mojo::PendingRemote<mojom::Unzipper> unzipper,
const base::FilePath& zip_file,
const base::FilePath& output_dir,
mojom::UnzipOptionsPtr options,
+ UnzipListenerCallback listener_callback,
UnzipCallback result_callback);
using DetectEncodingCallback = base::OnceCallback<void(Encoding)>;
@@ -44,6 +46,11 @@ void DetectEncoding(mojo::PendingRemote<mojom::Unzipper> unzipper,
const base::FilePath& zip_file,
DetectEncodingCallback result_callback);
+using GetExtractedInfoCallback = base::OnceCallback<void(mojom::InfoPtr)>;
+void GetExtractedInfo(mojo::PendingRemote<mojom::Unzipper> unzipper,
+ const base::FilePath& zip_file,
+ GetExtractedInfoCallback result_callback);
+
} // namespace unzip
#endif // COMPONENTS_SERVICES_UNZIP_PUBLIC_CPP_UNZIP_H_
diff --git a/chromium/components/services/unzip/public/cpp/unzip_unittest.cc b/chromium/components/services/unzip/public/cpp/unzip_unittest.cc
index 15a09254dd5..db4db7d2158 100644
--- a/chromium/components/services/unzip/public/cpp/unzip_unittest.cc
+++ b/chromium/components/services/unzip/public/cpp/unzip_unittest.cc
@@ -87,21 +87,24 @@ class UnzipTest : public testing::Test {
// successful. |options| hosts parameters for the unpack.
bool DoUnzipWithOptions(const base::FilePath& zip_file,
const base::FilePath& output_dir,
- mojom::UnzipOptions options = {}) {
+ mojom::UnzipOptionsPtr options) {
mojo::PendingRemote<mojom::Unzipper> unzipper;
receivers_.Add(&unzipper_, unzipper.InitWithNewPipeAndPassReceiver());
base::RunLoop run_loop;
bool result = false;
+ UnzipListenerCallback progress_callback = base::BindLambdaForTesting(
+ [&](uint64_t written_bytes) { base::DoNothing(); });
+
UnzipCallback result_callback =
base::BindLambdaForTesting([&](const bool success) {
result = success;
run_loop.QuitClosure().Run();
});
- Unzip(std::move(unzipper), zip_file, output_dir,
- std::move(result_callback));
+ Unzip(std::move(unzipper), zip_file, output_dir, std::move(options),
+ std::move(progress_callback), std::move(result_callback));
run_loop.Run();
return result;
@@ -125,6 +128,50 @@ class UnzipTest : public testing::Test {
return result;
}
+ mojom::Info DoGetExtractedInfo(const base::FilePath& zip_file) {
+ mojo::PendingRemote<mojom::Unzipper> unzipper;
+ receivers_.Add(&unzipper_, unzipper.InitWithNewPipeAndPassReceiver());
+
+ base::RunLoop run_loop;
+ mojom::Info result;
+
+ GetExtractedInfoCallback result_callback =
+ base::BindLambdaForTesting([&](mojom::InfoPtr info) {
+ result = *info;
+ run_loop.QuitClosure().Run();
+ });
+
+ GetExtractedInfo(std::move(unzipper), zip_file, std::move(result_callback));
+ run_loop.Run();
+ return result;
+ }
+
+ uint64_t DoGetProgressSize(const base::FilePath& zip_file,
+ const base::FilePath& output_dir) {
+ mojo::PendingRemote<mojom::Unzipper> unzipper;
+ receivers_.Add(&unzipper_, unzipper.InitWithNewPipeAndPassReceiver());
+
+ base::RunLoop run_loop;
+ uint64_t bytes = 0;
+ mojom::UnzipOptionsPtr options =
+ unzip::mojom::UnzipOptions::New("auto", "");
+
+ UnzipListenerCallback progress_callback =
+ base::BindLambdaForTesting([&](uint64_t written_bytes) {
+ bytes = written_bytes;
+ run_loop.QuitClosure().Run();
+ });
+
+ UnzipCallback result_callback = base::BindLambdaForTesting(
+ [&](const bool success) { run_loop.QuitClosure().Run(); });
+
+ Unzip(std::move(unzipper), zip_file, output_dir, std::move(options),
+ std::move(progress_callback), std::move(result_callback));
+
+ run_loop.Run();
+ return bytes;
+ }
+
protected:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -183,6 +230,20 @@ TEST_F(UnzipTest, UnzipWithFilter) {
EXPECT_FALSE(some_files_empty);
}
+// Checks that the Unzipper service does not overwrite an existing file.
+TEST_F(UnzipTest, DuplicatedNames) {
+ EXPECT_FALSE(DoUnzip(GetArchivePath("Duplicate Filenames.zip"), unzip_dir_));
+
+ // Check that the first file was correctly extracted.
+ std::string content;
+ EXPECT_TRUE(
+ base::ReadFileToString(unzip_dir_.AppendASCII("Simple.txt"), &content));
+ EXPECT_EQ("Simple 1\n", content);
+
+ // Check that no other file was extracted.
+ EXPECT_EQ(1, CountFiles(unzip_dir_));
+}
+
TEST_F(UnzipTest, DetectEncodingAbsentArchive) {
EXPECT_EQ(UNKNOWN_ENCODING,
DoDetectEncoding(GetArchivePath("absent_archive.zip")));
@@ -227,14 +288,49 @@ TEST_F(UnzipTest, DetectEncodingSjis) {
}
}
+TEST_F(UnzipTest, GetExtractedSize) {
+ mojom::Info result = DoGetExtractedInfo(GetArchivePath("good_archive.zip"));
+ EXPECT_TRUE(result.size_is_valid);
+ EXPECT_EQ(137, static_cast<int64_t>(result.size));
+}
+
+TEST_F(UnzipTest, GetExtractedSizeBrokenArchive) {
+ mojom::Info result = DoGetExtractedInfo(GetArchivePath("bad_archive.zip"));
+ EXPECT_FALSE(result.size_is_valid);
+}
+
TEST_F(UnzipTest, UnzipWithOptions) {
- EXPECT_TRUE(
- DoUnzipWithOptions(GetArchivePath("good_archive.zip"), unzip_dir_));
+ unzip::mojom::UnzipOptionsPtr options =
+ unzip::mojom::UnzipOptions::New("auto", "");
+ EXPECT_TRUE(DoUnzipWithOptions(GetArchivePath("good_archive.zip"), unzip_dir_,
+ std::move(options)));
// 8 files should have been extracted.
bool some_files_empty = false;
EXPECT_EQ(8, CountFiles(unzip_dir_, &some_files_empty));
}
+TEST_F(UnzipTest, GetExtractedProgressSize) {
+ uint64_t result =
+ DoGetProgressSize(GetArchivePath("good_archive.zip"), unzip_dir_);
+ // Check: first file extracted is 23 bytes long.
+ EXPECT_EQ(23ul, result);
+}
+
+TEST_F(UnzipTest, ExtractEncrypted) {
+ mojom::Info result =
+ DoGetExtractedInfo(GetArchivePath("encrypted_archive.zip"));
+ EXPECT_TRUE(result.is_encrypted);
+
+ unzip::mojom::UnzipOptionsPtr options =
+ unzip::mojom::UnzipOptions::New("auto", "fake_password");
+ EXPECT_TRUE(DoUnzipWithOptions(GetArchivePath("encrypted_archive.zip"),
+ unzip_dir_, std::move(options)));
+
+ // Check: 5 files should have been extracted.
+ bool some_files_empty = false;
+ EXPECT_EQ(5, CountFiles(unzip_dir_, &some_files_empty));
+}
+
} // namespace
} // namespace unzip
diff --git a/chromium/components/services/unzip/public/mojom/unzipper.mojom b/chromium/components/services/unzip/public/mojom/unzipper.mojom
index 3bdb959793d..1ed647f1365 100644
--- a/chromium/components/services/unzip/public/mojom/unzipper.mojom
+++ b/chromium/components/services/unzip/public/mojom/unzipper.mojom
@@ -13,6 +13,17 @@ struct UnzipOptions {
// Explicit encoding or set to "auto" to auto-detect from the ZIP file.
// Default unpack encoding uses UTF-8.
string encoding;
+ // Password used to unpack encrypted ZIP file.
+ string password;
+};
+
+struct Info {
+ // Boolean set to false if broken sizes are stored in the ZIP archive.
+ bool size_is_valid;
+ // Predicted size as read from the ZIP archive (not trustable).
+ uint64 size;
+ // Boolean set to true if the ZIP contains encrypted content.
+ bool is_encrypted;
};
// UnzipFilter is used to call back into the caller to check if
@@ -22,6 +33,13 @@ interface UnzipFilter {
ShouldUnzipFile(mojo_base.mojom.FilePath path) => (bool result);
};
+// Listener for a ZIP extraction operation.
+interface UnzipListener {
+ // Regularly called during ZIP extraction operation to report progress,
+ // with the total number of |bytes| processed since the last call.
+ OnProgress(uint64 bytes);
+};
+
// Interface to the out-of-process unzipper. The unzipper unzips a ZIP
// archive, often for the purpose of unpacking software updates.
[ServiceSandbox=sandbox.mojom.Sandbox.kUtility]
@@ -32,15 +50,20 @@ interface Unzipper {
// If provided, |filter| is called for each entry of the archive (which incurs
// one IPC for each entry) and only the entries for which it returns true are
// extracted.
+ // If provided, |listener| is called repeatedly with the bytes extracted.
Unzip(
mojo_base.mojom.ReadOnlyFile zip_file,
pending_remote<filesystem.mojom.Directory> output_dir,
UnzipOptions options,
- pending_remote<UnzipFilter>? filter) => (bool result);
+ pending_remote<UnzipFilter>? filter,
+ pending_remote<UnzipListener>? listener) => (bool result);
// Detects the encoding of filenames stored in the ZIP archive.
// Returns an Encoding as defined in
// third_party/ced/src/util/encodings/encodings.pb.h
// or UNKNOWN_ENCODING in case of error.
DetectEncoding(mojo_base.mojom.ReadOnlyFile zip_file) => (int32 encoding);
+
+ // Returns the size in bytes and metadata for the extracted archive.
+ GetExtractedInfo(mojo_base.mojom.ReadOnlyFile zip_file) => (Info info);
};
diff --git a/chromium/components/services/unzip/unzipper_impl.cc b/chromium/components/services/unzip/unzipper_impl.cc
index ee59725a424..a0f5ba9fbb5 100644
--- a/chromium/components/services/unzip/unzipper_impl.cc
+++ b/chromium/components/services/unzip/unzipper_impl.cc
@@ -11,10 +11,10 @@
#include "base/compiler_specific.h"
#include "base/files/file.h"
#include "base/logging.h"
+#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/services/filesystem/public/mojom/directory.mojom.h"
-#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/ced/src/compact_enc_det/compact_enc_det.h"
#include "third_party/zlib/google/redact.h"
#include "third_party/zlib/google/zip.h"
@@ -81,7 +81,7 @@ class Writer : public zip::FileWriterDelegate {
private:
const mojo::Remote<filesystem::mojom::Directory> owned_output_dir_;
- filesystem::mojom::Directory* const output_dir_;
+ const raw_ptr<filesystem::mojom::Directory> output_dir_;
const base::FilePath path_;
};
@@ -181,11 +181,33 @@ Encoding GetEncoding(const base::File& zip_file) {
return encoding;
}
+void UnzipperImpl::Listener(const mojo::Remote<mojom::UnzipListener>& listener,
+ uint64_t bytes) {
+ listener->OnProgress(bytes);
+}
+
+bool DoUnzip(base::File zip_file,
+ mojo::Remote<filesystem::mojom::Directory> output_dir,
+ std::string encoding_name,
+ std::string password,
+ zip::FilterCallback filter_cb,
+ zip::UnzipProgressCallback progress_cb) {
+ return zip::Unzip(
+ zip_file.GetPlatformFile(),
+ base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
+ base::BindRepeating(&CreateDirectory, output_dir.get()),
+ {.encoding = std::move(encoding_name),
+ .filter = std::move(filter_cb),
+ .progress = std::move(progress_cb),
+ .password = std::move(password)});
+}
+
void UnzipperImpl::Unzip(
base::File zip_file,
mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
mojom::UnzipOptionsPtr set_options,
mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
+ mojo::PendingRemote<mojom::UnzipListener> listener_remote,
UnzipCallback callback) {
DCHECK(zip_file.IsValid());
@@ -198,6 +220,13 @@ void UnzipperImpl::Unzip(
&Filter, mojo::Remote<mojom::UnzipFilter>(std::move(filter_remote)));
}
+ zip::UnzipProgressCallback progress_cb;
+ if (listener_remote) {
+ mojo::Remote<mojom::UnzipListener> listener(std::move(listener_remote));
+ progress_cb =
+ base::BindRepeating(&UnzipperImpl::Listener, std::move(listener));
+ }
+
std::string encoding_name;
if (set_options->encoding == "auto") {
Encoding encoding = GetEncoding(zip_file);
@@ -207,11 +236,13 @@ void UnzipperImpl::Unzip(
} else {
encoding_name = set_options->encoding;
}
- std::move(callback).Run(zip::Unzip(
- zip_file.GetPlatformFile(),
- base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
- base::BindRepeating(&CreateDirectory, output_dir.get()),
- {.encoding = std::move(encoding_name), .filter = std::move(filter_cb)}));
+
+ base::SequencedTaskRunnerHandle::Get()->PostTaskAndReplyWithResult(
+ FROM_HERE,
+ base::BindOnce(&DoUnzip, std::move(zip_file), std::move(output_dir),
+ std::move(encoding_name), std::move(set_options->password),
+ std::move(filter_cb), std::move(progress_cb)),
+ base::BindOnce(std::move(callback)));
}
void UnzipperImpl::DetectEncoding(base::File zip_file,
@@ -222,4 +253,45 @@ void UnzipperImpl::DetectEncoding(base::File zip_file,
std::move(callback).Run(encoding);
}
+void UnzipperImpl::GetExtractedInfo(base::File zip_file,
+ GetExtractedInfoCallback callback) {
+ DCHECK(zip_file.IsValid());
+
+ // Open ZIP archive for reading.
+ zip::ZipReader reader;
+ if (!reader.OpenFromPlatformFile(zip_file.GetPlatformFile())) {
+ LOG(ERROR) << "Cannot decode ZIP archive from file handle "
+ << zip_file.GetPlatformFile();
+ unzip::mojom::InfoPtr info = unzip::mojom::Info::New(false, 0, false);
+ std::move(callback).Run(std::move(info));
+ return;
+ }
+
+ int64_t size = 0;
+ bool valid = true;
+ bool has_encrypted_content = false;
+
+ // Iterate over file entries of the ZIP archive.
+ while (const zip::ZipReader::Entry* const entry = reader.Next()) {
+ // Check for (invalid) size stored.
+ if (entry->original_size < 0 ||
+ entry->original_size > std::numeric_limits<int64_t>::max() - size) {
+ LOG(ERROR) << "ZIP bad size info from file handle "
+ << zip_file.GetPlatformFile();
+ valid = false;
+ break;
+ }
+ // Accumulate size (since original_size is signed, ignore invalid sizes).
+ if (entry->original_size > 0) {
+ size += entry->original_size;
+ }
+ if (entry->is_encrypted) {
+ has_encrypted_content = true;
+ }
+ }
+ unzip::mojom::InfoPtr info =
+ unzip::mojom::Info::New(valid, size, has_encrypted_content);
+ std::move(callback).Run(std::move(info));
+}
+
} // namespace unzip
diff --git a/chromium/components/services/unzip/unzipper_impl.h b/chromium/components/services/unzip/unzipper_impl.h
index 650dbcf9d5b..89f132addbf 100644
--- a/chromium/components/services/unzip/unzipper_impl.h
+++ b/chromium/components/services/unzip/unzipper_impl.h
@@ -10,6 +10,7 @@
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
namespace unzip {
@@ -34,11 +35,18 @@ class UnzipperImpl : public mojom::Unzipper {
mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
mojom::UnzipOptionsPtr options,
mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
+ mojo::PendingRemote<mojom::UnzipListener> listener_remote,
UnzipCallback callback) override;
void DetectEncoding(base::File zip_file,
DetectEncodingCallback callback) override;
+ void GetExtractedInfo(base::File zip_file,
+ GetExtractedInfoCallback callback) override;
+
+ static void Listener(const mojo::Remote<mojom::UnzipListener>& listener,
+ uint64_t bytes);
+
mojo::Receiver<mojom::Unzipper> receiver_{this};
};
diff --git a/chromium/components/sessions/core/base_session_service_commands.cc b/chromium/components/sessions/core/base_session_service_commands.cc
index 8a956cfe60e..48241019c4f 100644
--- a/chromium/components/sessions/core/base_session_service_commands.cc
+++ b/chromium/components/sessions/core/base_session_service_commands.cc
@@ -147,6 +147,24 @@ std::unique_ptr<SessionCommand> CreateSetWindowUserTitleCommand(
return std::make_unique<SessionCommand>(command_id, pickle);
}
+std::unique_ptr<SessionCommand> CreateAddExtraDataCommand(
+ SessionCommand::id_type command,
+ const SessionID& session_id,
+ const std::string& key,
+ const std::string& data) {
+ base::Pickle pickle;
+ pickle.WriteInt(session_id.id());
+
+ // Enforce a max for ids. They should never be anywhere near this size.
+ static const SessionCommand::size_type max_id_size =
+ std::numeric_limits<SessionCommand::size_type>::max() - 1024;
+ int total_bytes_written = 0;
+ WriteStringToPickle(pickle, &total_bytes_written, max_id_size, key);
+ WriteStringToPickle(pickle, &total_bytes_written, max_id_size, data);
+
+ return std::make_unique<SessionCommand>(command, pickle);
+}
+
bool RestoreUpdateTabNavigationCommand(
const SessionCommand& command,
sessions::SerializedNavigationEntry* navigation,
@@ -238,4 +256,17 @@ bool RestoreSetWindowUserTitleCommand(const SessionCommand& command,
iterator.ReadString(user_title);
}
+bool RestoreAddExtraDataCommand(const SessionCommand& command,
+ SessionID* session_id,
+ std::string* key,
+ std::string* data) {
+ std::unique_ptr<base::Pickle> pickle(command.PayloadAsPickle());
+ if (!pickle)
+ return false;
+
+ base::PickleIterator it(*pickle);
+ return ReadSessionIdFromPickle(&it, session_id) && it.ReadString(key) &&
+ it.ReadString(data);
+}
+
} // namespace sessions
diff --git a/chromium/components/sessions/core/base_session_service_commands.h b/chromium/components/sessions/core/base_session_service_commands.h
index d9b23cc5149..05ad2e059ff 100644
--- a/chromium/components/sessions/core/base_session_service_commands.h
+++ b/chromium/components/sessions/core/base_session_service_commands.h
@@ -52,6 +52,13 @@ std::unique_ptr<SessionCommand> CreateSetWindowUserTitleCommand(
SessionID window_id,
const std::string& app_name);
+// Creates a SessionCommand storing a tab extra data.
+std::unique_ptr<SessionCommand> CreateAddExtraDataCommand(
+ SessionCommand::id_type command,
+ const SessionID& session_id,
+ const std::string& key,
+ const std::string& data);
+
// Converts a SessionCommand previously created by
// CreateUpdateTabNavigationCommand into a
// SerializedNavigationEntry. Returns true on success. If
@@ -95,6 +102,13 @@ bool RestoreSetWindowUserTitleCommand(const SessionCommand& command,
SessionID* window_id,
std::string* user_title);
+// Extracts a SessionCommand as previously created by
+// CreateAddExtraDataCommand into the tab/window id, key and data.
+bool RestoreAddExtraDataCommand(const SessionCommand& command,
+ SessionID* session_id,
+ std::string* key,
+ std::string* data);
+
} // namespace sessions
#endif // COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_COMMANDS_H_
diff --git a/chromium/components/sessions/core/live_tab_context.h b/chromium/components/sessions/core/live_tab_context.h
index fff5e758563..db697a87b9c 100644
--- a/chromium/components/sessions/core/live_tab_context.h
+++ b/chromium/components/sessions/core/live_tab_context.h
@@ -36,6 +36,7 @@ class SESSIONS_EXPORT LiveTabContext {
// TODO(blundell): Rename.
virtual void ShowBrowserWindow() = 0;
virtual SessionID GetSessionID() const = 0;
+ virtual SessionWindow::WindowType GetWindowType() const = 0;
virtual int GetTabCount() const = 0;
virtual int GetSelectedIndex() const = 0;
virtual std::string GetAppName() const = 0;
diff --git a/chromium/components/sessions/core/session_id_generator.cc b/chromium/components/sessions/core/session_id_generator.cc
index b74b809cac6..6624a1ae506 100644
--- a/chromium/components/sessions/core/session_id_generator.cc
+++ b/chromium/components/sessions/core/session_id_generator.cc
@@ -100,4 +100,8 @@ void SessionIdGenerator::IncrementValueBy(int increment) {
last_value_ += increment;
}
+bool SessionIdGenerator::IsInitializedForTest() const {
+ return local_state_ != nullptr;
+}
+
} // namespace sessions
diff --git a/chromium/components/sessions/core/session_id_generator.h b/chromium/components/sessions/core/session_id_generator.h
index 80a427447c5..19b5ecb992c 100644
--- a/chromium/components/sessions/core/session_id_generator.h
+++ b/chromium/components/sessions/core/session_id_generator.h
@@ -44,6 +44,9 @@ class SESSIONS_EXPORT SessionIdGenerator {
using RandomGenerator = base::RepeatingCallback<SessionID::id_type()>;
void SetRandomGeneratorForTest(const RandomGenerator& rand_generator);
+ // Used for test only, verify the SessionIdGenerator is initialized.
+ bool IsInitializedForTest() const;
+
private:
friend struct base::DefaultSingletonTraits<SessionIdGenerator>;
diff --git a/chromium/components/sessions/core/session_service_commands.cc b/chromium/components/sessions/core/session_service_commands.cc
index b3c28cb682f..a22dc77ad8c 100644
--- a/chromium/components/sessions/core/session_service_commands.cc
+++ b/chromium/components/sessions/core/session_service_commands.cc
@@ -22,19 +22,6 @@
#include "components/sessions/core/base_session_service_commands.h"
#include "components/tab_groups/tab_group_color.h"
-namespace {
-
-bool ReadSessionIdFromPickle(base::PickleIterator* iterator, SessionID* id) {
- SessionID::id_type value;
- if (!iterator->ReadInt(&value)) {
- return false;
- }
- *id = SessionID::FromSerializedValue(value);
- return true;
-}
-
-} // namespace
-
namespace sessions {
// Identifier for commands written to file.
@@ -857,14 +844,10 @@ bool CreateTabsAndWindows(
}
case kCommandAddTabExtraData: {
- std::unique_ptr<base::Pickle> pickle(command->PayloadAsPickle());
- base::PickleIterator it(*pickle);
-
SessionID tab_id = SessionID::InvalidValue();
std::string key;
std::string data;
- if (!ReadSessionIdFromPickle(&it, &tab_id) || !it.ReadString(&key) ||
- !it.ReadString(&data)) {
+ if (!RestoreAddExtraDataCommand(*command, &tab_id, &key, &data)) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
@@ -874,14 +857,10 @@ bool CreateTabsAndWindows(
}
case kCommandAddWindowExtraData: {
- std::unique_ptr<base::Pickle> pickle(command->PayloadAsPickle());
- base::PickleIterator it(*pickle);
-
SessionID window_id = SessionID::InvalidValue();
std::string key;
std::string data;
- if (!ReadSessionIdFromPickle(&it, &window_id) || !it.ReadString(&key) ||
- !it.ReadString(&data)) {
+ if (!RestoreAddExtraDataCommand(*command, &window_id, &key, &data)) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
@@ -1147,19 +1126,6 @@ std::unique_ptr<SessionCommand> CreateSetTabDataCommand(
return std::make_unique<SessionCommand>(kCommandSetTabData, pickle);
}
-std::unique_ptr<SessionCommand> CreateAddExtraDataCommand(
- SessionCommand::id_type command,
- const SessionID& session_id,
- const std::string& key,
- const std::string& data) {
- base::Pickle pickle;
- pickle.WriteInt(session_id.id());
- pickle.WriteString(key);
- pickle.WriteString(data);
-
- return std::make_unique<SessionCommand>(command, pickle);
-}
-
std::unique_ptr<SessionCommand> CreateAddTabExtraDataCommand(
const SessionID& tab_id,
const std::string& key,
diff --git a/chromium/components/sessions/core/tab_restore_service.h b/chromium/components/sessions/core/tab_restore_service.h
index 80a2722ebe2..5e7ecb56c69 100644
--- a/chromium/components/sessions/core/tab_restore_service.h
+++ b/chromium/components/sessions/core/tab_restore_service.h
@@ -146,6 +146,9 @@ class SESSIONS_EXPORT TabRestoreService : public KeyedService {
// Entry:
size_t EstimateMemoryUsage() const override;
+ // Type of window.
+ SessionWindow::WindowType type;
+
// The tabs that comprised the window, in order.
std::vector<std::unique_ptr<Tab>> tabs;
diff --git a/chromium/components/sessions/core/tab_restore_service_client.h b/chromium/components/sessions/core/tab_restore_service_client.h
index 9d9c10c8899..029b9ca0a69 100644
--- a/chromium/components/sessions/core/tab_restore_service_client.h
+++ b/chromium/components/sessions/core/tab_restore_service_client.h
@@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/files/file_path.h"
#include "components/sessions/core/session_id.h"
+#include "components/sessions/core/session_types.h"
#include "components/sessions/core/sessions_export.h"
#include "ui/base/ui_base_types.h"
@@ -28,7 +29,6 @@ namespace sessions {
class LiveTab;
class LiveTabContext;
-struct SessionWindow;
// Callback from TabRestoreServiceClient::GetLastSession.
// The second parameter is the id of the window that was last active.
@@ -47,6 +47,7 @@ class SESSIONS_EXPORT TabRestoreServiceClient {
// functionality).
virtual LiveTabContext* CreateLiveTabContext(
LiveTabContext* existing_context,
+ SessionWindow::WindowType type,
const std::string& app_name,
const gfx::Rect& bounds,
ui::WindowShowState show_state,
diff --git a/chromium/components/sessions/core/tab_restore_service_helper.cc b/chromium/components/sessions/core/tab_restore_service_helper.cc
index d946a538b9a..aa7c9c3cbb6 100644
--- a/chromium/components/sessions/core/tab_restore_service_helper.cc
+++ b/chromium/components/sessions/core/tab_restore_service_helper.cc
@@ -193,6 +193,7 @@ void TabRestoreServiceHelper::BrowserClosing(LiveTabContext* context) {
closing_contexts_.insert(context);
auto window = std::make_unique<Window>();
+ window->type = context->GetWindowType();
window->selected_tab_index = context->GetSelectedIndex();
window->timestamp = TimeNow();
window->app_name = context->GetAppName();
@@ -489,8 +490,9 @@ std::vector<LiveTab*> TabRestoreServiceHelper::RestoreEntryById(
// restored.
if (entry_id_matches_restore_id || !window.app_name.empty()) {
context = client_->CreateLiveTabContext(
- context, window.app_name, window.bounds, window.show_state,
- window.workspace, window.user_title, window.extra_data);
+ context, window.type, window.app_name, window.bounds,
+ window.show_state, window.workspace, window.user_title,
+ window.extra_data);
base::flat_map<tab_groups::TabGroupId, tab_groups::TabGroupId>
new_group_ids;
@@ -878,8 +880,9 @@ LiveTabContext* TabRestoreServiceHelper::RestoreTab(
tab_index = tab.tabstrip_index;
} else {
context = client_->CreateLiveTabContext(
- context, std::string(), gfx::Rect(), ui::SHOW_STATE_NORMAL,
- std::string(), std::string(), std::map<std::string, std::string>());
+ context, SessionWindow::TYPE_NORMAL, std::string(), gfx::Rect(),
+ ui::SHOW_STATE_NORMAL, std::string(), std::string(),
+ std::map<std::string, std::string>());
if (tab.browser_id)
UpdateTabBrowserIDs(tab.browser_id, context->GetSessionID());
}
diff --git a/chromium/components/sessions/core/tab_restore_service_impl.cc b/chromium/components/sessions/core/tab_restore_service_impl.cc
index 66298641f50..35f2ba5ea0c 100644
--- a/chromium/components/sessions/core/tab_restore_service_impl.cc
+++ b/chromium/components/sessions/core/tab_restore_service_impl.cc
@@ -119,6 +119,7 @@ const SessionCommand::id_type kCommandSetTabGroupData = 10;
const SessionCommand::id_type kCommandSetTabUserAgentOverride2 = 11;
const SessionCommand::id_type kCommandSetWindowUserTitle = 12;
const SessionCommand::id_type kCommandCreateGroup = 13;
+const SessionCommand::id_type kCommandAddTabExtraData = 14;
// Number of entries (not commands) before we clobber the file and write
// everything.
@@ -237,6 +238,24 @@ bool DeserializeWindowShowState(int show_state_int,
return false;
}
+// Converts an int to a window type. Returns true on success, false otherwise.
+bool DeserializeWindowType(int type_int,
+ sessions::SessionWindow::WindowType* type) {
+ switch (static_cast<sessions::SessionWindow::WindowType>(type_int)) {
+ case sessions::SessionWindow::TYPE_NORMAL:
+ case sessions::SessionWindow::TYPE_POPUP:
+ case sessions::SessionWindow::TYPE_APP:
+ case sessions::SessionWindow::TYPE_DEVTOOLS:
+ case sessions::SessionWindow::TYPE_APP_POPUP:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ case sessions::SessionWindow::TYPE_CUSTOM_TAB:
+#endif
+ *type = static_cast<sessions::SessionWindow::WindowType>(type_int);
+ return true;
+ }
+ return false;
+}
+
// Superset of WindowPayloadObsolete/WindowPayloadObsolete2 and the other fields
// that can appear in the Pickle version of a Window command. This is used as a
// convenient destination for parsing the various fields in a WindowCommand.
@@ -258,6 +277,8 @@ struct WindowCommandFields {
int window_height = 0;
int window_show_state = 0;
std::string workspace;
+
+ int type = 0;
};
std::unique_ptr<sessions::TabRestoreService::Window>
@@ -266,6 +287,7 @@ CreateWindowEntryFromCommand(const SessionCommand* command,
int32_t* num_tabs) {
WindowCommandFields fields;
ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
+ auto type = sessions::SessionWindow::TYPE_NORMAL;
if (command->id() == kCommandWindow) {
std::unique_ptr<base::Pickle> pickle(command->PayloadAsPickle());
@@ -290,6 +312,14 @@ CreateWindowEntryFromCommand(const SessionCommand* command,
return nullptr;
}
+ // New field in M104, use default if it fails to read.
+ // TODO(crbug.com/1332968): After some time (say M114), this code can be
+ // added into parsing above which fails when ReadInt() fails.
+ if (!it.ReadInt(&parsed_fields.type)) {
+ parsed_fields.type =
+ static_cast<int>(sessions::SessionWindow::TYPE_NORMAL);
+ }
+
// Validate the parameters. If the entire pickles parses but any of the
// validation fails assume corruption.
if (parsed_fields.window_width < 0 || parsed_fields.window_height < 0)
@@ -301,6 +331,10 @@ CreateWindowEntryFromCommand(const SessionCommand* command,
return nullptr;
}
+ // Validate window type.
+ if (!DeserializeWindowType(parsed_fields.type, &type)) {
+ return nullptr;
+ }
// New fields added to the pickle in later versions would be parsed and
// validated here.
@@ -345,6 +379,7 @@ CreateWindowEntryFromCommand(const SessionCommand* command,
// Create the Window entry.
std::unique_ptr<sessions::TabRestoreService::Window> window =
std::make_unique<sessions::TabRestoreService::Window>();
+ window->type = type;
window->selected_tab_index = fields.selected_tab_index;
window->timestamp = base::Time::FromDeltaSinceWindowsEpoch(
base::Microseconds(fields.timestamp));
@@ -488,6 +523,7 @@ class TabRestoreServiceImpl::PersistenceDelegate
// Creates a window close command.
static std::unique_ptr<SessionCommand> CreateWindowCommand(
SessionID window_id,
+ SessionWindow::WindowType type,
int selected_tab_index,
int num_tabs,
const gfx::Rect& bounds,
@@ -763,7 +799,7 @@ void TabRestoreServiceImpl::PersistenceDelegate::ScheduleCommandsForWindow(
return; // No tabs to persist.
command_storage_manager_->ScheduleCommand(CreateWindowCommand(
- window.id, std::min(real_selected_tab, valid_tab_count - 1),
+ window.id, window.type, std::min(real_selected_tab, valid_tab_count - 1),
valid_tab_count, window.bounds, window.show_state, window.workspace,
window.timestamp));
@@ -862,12 +898,18 @@ void TabRestoreServiceImpl::PersistenceDelegate::ScheduleCommandsForTab(
navigations[i]));
}
}
+
+ for (const auto& data : tab.extra_data) {
+ command_storage_manager_->ScheduleCommand(CreateAddExtraDataCommand(
+ kCommandAddTabExtraData, tab.id, data.first, data.second));
+ }
}
// static
std::unique_ptr<SessionCommand>
TabRestoreServiceImpl::PersistenceDelegate::CreateWindowCommand(
SessionID window_id,
+ SessionWindow::WindowType type,
int selected_tab_index,
int num_tabs,
const gfx::Rect& bounds,
@@ -897,6 +939,8 @@ TabRestoreServiceImpl::PersistenceDelegate::CreateWindowCommand(
else
pickle.WriteString(std::string());
+ pickle.WriteInt(type);
+
std::unique_ptr<SessionCommand> command(
new SessionCommand(kCommandWindow, pickle));
return command;
@@ -1272,6 +1316,21 @@ void TabRestoreServiceImpl::PersistenceDelegate::CreateEntriesFromCommands(
break;
}
+ case kCommandAddTabExtraData: {
+ if (!current_tab) {
+ // Should be in a tab when we get this.
+ return;
+ }
+ SessionID tab_id = SessionID::InvalidValue();
+ std::string key;
+ std::string data;
+ if (!RestoreAddExtraDataCommand(command, &tab_id, &key, &data))
+ return;
+
+ current_tab->extra_data[key] = std::move(data);
+ break;
+ }
+
default:
// Unknown type, usually indicates corruption of file. Ignore it.
return;
@@ -1314,6 +1373,8 @@ void TabRestoreServiceImpl::PersistenceDelegate::OnGotPreviousSession(
bool TabRestoreServiceImpl::PersistenceDelegate::ConvertSessionWindowToWindow(
SessionWindow* session_window,
Window* window) {
+ window->type = session_window->type;
+
// The group visual datas must be stored in both |window| and each
// grouped tab.
std::map<tab_groups::TabGroupId, tab_groups::TabGroupVisualData>
diff --git a/chromium/components/shared_highlighting/OWNERS b/chromium/components/shared_highlighting/OWNERS
index a535e7086dc..3190126fdfa 100644
--- a/chromium/components/shared_highlighting/OWNERS
+++ b/chromium/components/shared_highlighting/OWNERS
@@ -1,4 +1,5 @@
gayane@chromium.org
+jeffreycohen@chromium.org
sebsg@chromium.org
seblalancette@chromium.org
tmartino@chromium.org
diff --git a/chromium/components/shared_highlighting/core/common/BUILD.gn b/chromium/components/shared_highlighting/core/common/BUILD.gn
index 364cb547817..2434d6ab56f 100644
--- a/chromium/components/shared_highlighting/core/common/BUILD.gn
+++ b/chromium/components/shared_highlighting/core/common/BUILD.gn
@@ -55,6 +55,7 @@ source_set("data_driven_testing") {
sources = [
"shared_highlighting_data_driven_test.cc",
"shared_highlighting_data_driven_test.h",
+ "shared_highlighting_data_driven_test_results.h",
]
data = [
"//components/test/data/shared_highlighting/",
diff --git a/chromium/components/shared_highlighting/core/common/disabled_sites.cc b/chromium/components/shared_highlighting/core/common/disabled_sites.cc
index 9e4d0b5133d..5c641a95a25 100644
--- a/chromium/components/shared_highlighting/core/common/disabled_sites.cc
+++ b/chromium/components/shared_highlighting/core/common/disabled_sites.cc
@@ -13,24 +13,42 @@
#include "third_party/re2/src/re2/re2.h"
namespace shared_highlighting {
+static auto CreateBlocklist() {
+ if (base::FeatureList::IsEnabled(kSharedHighlightingRefinedBlocklist)) {
+ return base::MakeFixedFlatMap<base::StringPiece, base::StringPiece>(
+ {{"facebook.com", "(?!.*(about)).*"},
+ // TODO(crbug.com/1157981): special case this to cover other Google
+ // TLDs
+ {"google.com", "^\\/amp\\/.*"},
+ {"instagram.com", "(?!.*(/p/)).*"},
+ {"mail.google.com", ".*"},
+ {"outlook.live.com", ".*"},
+ {"reddit.com", "(?!.*(comments)).*"},
+ {"twitter.com", "(?!.*(status)).*"},
+ {"web.whatsapp.com", ".*"},
+ {"youtube.com", "?!.*(about|community)).*"}});
+ } else {
+ return base::MakeFixedFlatMap<base::StringPiece, base::StringPiece>(
+ {{"facebook.com", ".*"},
+ // TODO(crbug.com/1157981): special case this to cover other Google
+ // TLDs
+ {"google.com", "^\\/amp\\/.*"},
+ {"instagram.com", ".*"},
+ {"mail.google.com", ".*"},
+ {"outlook.live.com", ".*"},
+ {"reddit.com", ".*"},
+ {"twitter.com", ".*"},
+ {"web.whatsapp.com", ".*"},
+ {"youtube.com", ".*"}});
+ }
+}
bool ShouldOfferLinkToText(const GURL& url) {
// If a URL's host matches a key in this map, then the path will be tested
// against the RE stored in the value. For example, {"foo.com", ".*"} means
// any page on the foo.com domain.
- static constexpr auto kBlocklist =
- base::MakeFixedFlatMap<base::StringPiece, base::StringPiece>(
- {{"facebook.com", ".*"},
- // TODO(crbug.com/1157981): special case this to cover other Google
- // TLDs
- {"google.com", "^\\/amp\\/.*"},
- {"instagram.com", ".*"},
- {"mail.google.com", ".*"},
- {"outlook.live.com", ".*"},
- {"reddit.com", ".*"},
- {"twitter.com", ".*"},
- {"web.whatsapp.com", ".*"},
- {"youtube.com", ".*"}});
+
+ static auto kBlocklist = CreateBlocklist();
std::string domain = url.host();
if (domain.compare(0, 4, "www.") == 0) {
diff --git a/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.cc b/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.cc
index f263cedc04e..43f6c0ac027 100644
--- a/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.cc
+++ b/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.cc
@@ -43,6 +43,9 @@ const char kEndTextOffsetField[] = "endTextOffset";
const char kSelectedTextField[] = "selectedText";
const char kHighlightTextField[] = "highlightText";
+const std::string kTestSuccess = "Success";
+const std::string kTestFailure = "Failure";
+
// Reads |file| into |content|, and converts Windows line-endings to Unix ones.
// Returns true on success.
bool ReadFile(const base::FilePath& file, std::string* content) {
@@ -134,9 +137,15 @@ void SharedHighlightingDataDrivenTest::GenerateResults(const std::string& input,
std::string html_content;
ReadFile(GetHtmlDir().AppendASCII(html_file_name), &html_content);
- GenerateAndNavigate(html_content, start_parent_id, start_offset_in_parent,
- start_text_offset, end_parent_id, end_offset_in_parent,
- end_text_offset, selected_text, highlight_text);
+ auto results = GenerateAndNavigate(
+ html_content, start_parent_id, start_offset_in_parent, start_text_offset,
+ end_parent_id, end_offset_in_parent, end_text_offset, selected_text,
+ highlight_text);
+
+ *output = "GENERATION: " +
+ (results.generation_success ? kTestSuccess : kTestFailure) + "\n" +
+ "HIGHLIGHTING: " +
+ (results.highlighting_success ? kTestSuccess : kTestFailure) + "\n";
}
} // namespace shared_highlighting
diff --git a/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.h b/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.h
index 368d20d25f7..7f589466169 100644
--- a/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.h
+++ b/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test.h
@@ -11,6 +11,7 @@
#include "base/files/file_path.h"
#include "base/values.h"
+#include "components/shared_highlighting/core/common/shared_highlighting_data_driven_test_results.h"
#include "testing/data_driven_testing/data_driven_test.h"
namespace shared_highlighting {
@@ -28,15 +29,16 @@ class SharedHighlightingDataDrivenTest : public testing::DataDrivenTest {
// DataDrivenTest:
void GenerateResults(const std::string& input, std::string* output) override;
- virtual void GenerateAndNavigate(std::string html_content,
- std::string* start_parent_id,
- int start_offset_in_parent,
- absl::optional<int> start_text_offset,
- std::string* end_parent_id,
- int end_offset_in_parent,
- absl::optional<int> end_text_offset,
- std::string selected_text,
- std::string* highlight_text) = 0;
+ virtual SharedHighlightingDataDrivenTestResults GenerateAndNavigate(
+ std::string html_content,
+ std::string* start_parent_id,
+ int start_offset_in_parent,
+ absl::optional<int> start_text_offset,
+ std::string* end_parent_id,
+ int end_offset_in_parent,
+ absl::optional<int> end_text_offset,
+ std::string selected_text,
+ std::string* highlight_text) = 0;
};
} // namespace shared_highlighting
diff --git a/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test_results.h b/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test_results.h
new file mode 100644
index 00000000000..2ff2d2d6ab0
--- /dev/null
+++ b/chromium/components/shared_highlighting/core/common/shared_highlighting_data_driven_test_results.h
@@ -0,0 +1,23 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SHARED_HIGHLIGHTING_CORE_COMMON_SHARED_HIGHLIGHTING_DATA_DRIVEN_TEST_RESULTS_H_
+#define COMPONENTS_SHARED_HIGHLIGHTING_CORE_COMMON_SHARED_HIGHLIGHTING_DATA_DRIVEN_TEST_RESULTS_H_
+
+namespace shared_highlighting {
+
+// Struct for holding the results of the data-driven tests of Shared
+// Highlighting.
+struct SharedHighlightingDataDrivenTestResults {
+ // Indicates that a link was generated for a data-driven test case.
+ bool generation_success = false;
+ // Indicates that the expected text was highlighted for a data-driven test
+ // case.
+ bool highlighting_success = false;
+ // TODO(crbug.com/1318483): Add highlighted text to results for easier failure
+ // diagosis
+};
+} // namespace shared_highlighting
+
+#endif // COMPONENTS_SHARED_HIGHLIGHTING_CORE_COMMON_SHARED_HIGHLIGHTING_DATA_DRIVEN_TEST_RESULTS_H_ \ No newline at end of file
diff --git a/chromium/components/shared_highlighting/core/common/shared_highlighting_features.cc b/chromium/components/shared_highlighting/core/common/shared_highlighting_features.cc
index e5d4c364418..9006f4c4048 100644
--- a/chromium/components/shared_highlighting/core/common/shared_highlighting_features.cc
+++ b/chromium/components/shared_highlighting/core/common/shared_highlighting_features.cc
@@ -14,9 +14,6 @@ const base::Feature kPreemptiveLinkToTextGeneration{
constexpr base::FeatureParam<int> kPreemptiveLinkGenTimeoutLengthMs{
&kPreemptiveLinkToTextGeneration, "TimeoutLengthMs", 500};
-const base::Feature kSharedHighlightingV2{"SharedHighlightingV2",
- base::FEATURE_ENABLED_BY_DEFAULT};
-
const base::Feature kSharedHighlightingAmp {
"SharedHighlightingAmp",
#if BUILDFLAG(IS_IOS)
@@ -29,6 +26,23 @@ const base::Feature kSharedHighlightingAmp {
const base::Feature kIOSSharedHighlightingV2{"IOSSharedHighlightingV2",
base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kSharedHighlightingRefinedBlocklist{
+ "SharedHighlightingRefinedBlocklist", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kSharedHighlightingRefinedMaxContextWords{
+ "SharedHighlightingRefinedMaxContextWords",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kSharedHighlightingManager{
+ "SharedHighlightingManager", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const char kSharedHighlightingRefinedMaxContextWordsName[] =
+ "SharedHighlightingRefinedMaxContextWords";
+
+const base::FeatureParam<int> kSharedHighlightingMaxContextWords{
+ &kSharedHighlightingRefinedMaxContextWords,
+ kSharedHighlightingRefinedMaxContextWordsName, 10};
+
int GetPreemptiveLinkGenTimeoutLengthMs() {
return kPreemptiveLinkGenTimeoutLengthMs.Get();
}
diff --git a/chromium/components/shared_highlighting/core/common/shared_highlighting_features.h b/chromium/components/shared_highlighting/core/common/shared_highlighting_features.h
index 144dfcf54ab..5f2c42eee81 100644
--- a/chromium/components/shared_highlighting/core/common/shared_highlighting_features.h
+++ b/chromium/components/shared_highlighting/core/common/shared_highlighting_features.h
@@ -18,15 +18,24 @@ extern const base::Feature kPreemptiveLinkToTextGeneration;
// Sets the timeout length for pre-emptive link generation.
extern const base::FeatureParam<int> kPreemptiveLinkGenTimeoutLengthMs;
-// Enables the new UI features for highlighted text.
-extern const base::Feature kSharedHighlightingV2;
-
// Enables shared highlighting for AMP viewers pages.
extern const base::Feature kSharedHighlightingAmp;
+// Enables the new SharedHighlightingManager refactoring.
+extern const base::Feature kSharedHighlightingManager;
+
// Feature flag that enable Shared Highlighting V2 in iOS.
extern const base::Feature kIOSSharedHighlightingV2;
+// Feature flag that enables a narrower blocklist.
+extern const base::Feature kSharedHighlightingRefinedBlocklist;
+
+// Feature flag that allows to experiment with different Max Context Words.
+extern const base::Feature kSharedHighlightingRefinedMaxContextWords;
+// Feature name and parameter to capture the different maxContextWords values.
+extern const char kSharedHighlightingRefinedMaxContextWordsName[];
+extern const base::FeatureParam<int> kSharedHighlightingMaxContextWords;
+
// Returns the pre-emptive link generation timeout length.
int GetPreemptiveLinkGenTimeoutLengthMs();
diff --git a/chromium/components/shared_highlighting/core/common/text_fragment.cc b/chromium/components/shared_highlighting/core/common/text_fragment.cc
index 24b09b8a5a7..ecd515c73a1 100644
--- a/chromium/components/shared_highlighting/core/common/text_fragment.cc
+++ b/chromium/components/shared_highlighting/core/common/text_fragment.cc
@@ -6,16 +6,16 @@
#include <sstream>
+#include "base/strings/escape.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "components/shared_highlighting/core/common/fragment_directives_constants.h"
-#include "net/base/escape.h"
namespace {
// Escapes any special character such that the fragment can be added to a URL.
std::string Escape(const std::string& str) {
- std::string escaped = net::EscapeQueryParamValue(str, /*usePlus=*/false);
+ std::string escaped = base::EscapeQueryParamValue(str, /*usePlus=*/false);
// Hyphens must also be escaped since they are used to indicate prefix/suffix
// components.
diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn
index efe9ed3b30f..df0893c6b90 100644
--- a/chromium/components/signin/core/browser/BUILD.gn
+++ b/chromium/components/signin/core/browser/BUILD.gn
@@ -42,12 +42,8 @@ static_library("browser") {
"signin_internals_util.h",
"signin_investigator.cc",
"signin_investigator.h",
- "signin_status_metrics_provider.cc",
- "signin_status_metrics_provider.h",
- "signin_status_metrics_provider_base.cc",
- "signin_status_metrics_provider_base.h",
- "signin_status_metrics_provider_delegate.cc",
- "signin_status_metrics_provider_delegate.h",
+ "signin_status_metrics_provider_helpers.cc",
+ "signin_status_metrics_provider_helpers.h",
]
public_deps = [
@@ -87,10 +83,6 @@ static_library("browser") {
"//ash/components/tpm",
"//chromeos/crosapi/mojom",
]
- sources -= [
- "signin_status_metrics_provider.cc",
- "signin_status_metrics_provider_delegate.cc",
- ]
}
if (is_chromeos_lacros) {
@@ -122,11 +114,10 @@ source_set("unit_tests") {
"account_investigator_unittest.cc",
"account_reconcilor_delegate_unittest.cc",
"account_reconcilor_unittest.cc",
- "dice_account_reconcilor_delegate_unittest.cc",
"signin_error_controller_unittest.cc",
"signin_header_helper_unittest.cc",
"signin_investigator_unittest.cc",
- "signin_status_metrics_provider_unittest.cc",
+ "signin_status_metrics_provider_helpers_unittest.cc",
]
deps = [
@@ -158,7 +149,7 @@ source_set("unit_tests") {
]
sources -= [
"account_investigator_unittest.cc",
- "signin_status_metrics_provider_unittest.cc",
+ "signin_status_metrics_provider_helpers_unittest.cc",
]
}
@@ -173,10 +164,6 @@ source_set("unit_tests") {
"mirror_landing_account_reconcilor_delegate_unittest.cc",
]
}
-
- if (!enable_dice_support) {
- sources -= [ "dice_account_reconcilor_delegate_unittest.cc" ]
- }
}
if (is_android) {
diff --git a/chromium/components/signin/core/browser/account_reconcilor.cc b/chromium/components/signin/core/browser/account_reconcilor.cc
index 66e07043701..562d86f0f4b 100644
--- a/chromium/components/signin/core/browser/account_reconcilor.cc
+++ b/chromium/components/signin/core/browser/account_reconcilor.cc
@@ -60,49 +60,6 @@ std::vector<gaia::ListedAccount> FilterUnverifiedAccounts(
return verified_gaia_accounts;
}
-// Revokes tokens for all accounts in chrome_accounts but the primary account.
-// Returns true if tokens were revoked, and false if the function did nothing.
-bool RevokeAllSecondaryTokens(
- signin::IdentityManager* identity_manager,
- signin::AccountReconcilorDelegate::RevokeTokenOption revoke_option,
- const CoreAccountId& primary_account,
- signin_metrics::SourceForRefreshTokenOperation source) {
- bool token_revoked = false;
- if (revoke_option ==
- AccountReconcilorDelegate::RevokeTokenOption::kDoNotRevoke)
- return false;
- for (const CoreAccountInfo& account_info :
- identity_manager->GetAccountsWithRefreshTokens()) {
- CoreAccountId account = account_info.account_id;
- if (account == primary_account)
- continue;
- bool should_revoke = false;
- switch (revoke_option) {
- case AccountReconcilorDelegate::RevokeTokenOption::kRevokeIfInError:
- if (identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
- account)) {
- VLOG(1) << "Revoke token for " << account;
- should_revoke = true;
- }
- break;
- case AccountReconcilorDelegate::RevokeTokenOption::kRevoke:
- VLOG(1) << "Revoke token for " << account;
- should_revoke = true;
- break;
- case AccountReconcilorDelegate::RevokeTokenOption::kDoNotRevoke:
- NOTREACHED();
- break;
- }
- if (should_revoke) {
- token_revoked = true;
- VLOG(1) << "Revoke token for " << account;
- auto* accounts_mutator = identity_manager->GetAccountsMutator();
- accounts_mutator->RemoveAccount(account, source);
- }
- }
- return token_revoked;
-}
-
// Pick the account will become first after this reconcile is finished.
CoreAccountId PickFirstGaiaAccount(
const signin::MultiloginParameters& parameters,
@@ -116,20 +73,6 @@ CoreAccountId PickFirstGaiaAccount(
: parameters.accounts_to_send[0];
}
-// Returns true if gaia_accounts contains an invalid account that is unknown to
-// the identity manager.
-bool HasUnknownInvalidAccountInCookie(
- signin::IdentityManager* identity_manager,
- const std::vector<gaia::ListedAccount>& gaia_accounts) {
- for (const gaia::ListedAccount& account : gaia_accounts) {
- if (!account.valid &&
- !identity_manager->HasAccountWithRefreshToken(account.id)) {
- return true;
- }
- }
- return false;
-}
-
} // namespace
// static
@@ -461,30 +404,23 @@ void AccountReconcilor::FinishReconcileWithMultiloginEndpoint(
DCHECK(!log_out_in_progress_);
DCHECK_EQ(AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING, state_);
- bool primary_has_error =
- identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
- primary_account);
-
const signin::MultiloginParameters kLogoutParameters(
gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
std::vector<CoreAccountId>());
- const bool should_revoke_tokens =
- delegate_->ShouldRevokeTokensBeforeMultilogin(
- chrome_accounts, primary_account, gaia_accounts, first_execution_,
- primary_has_error);
+ const bool tokens_revoked =
+ delegate_->RevokeSecondaryTokensBeforeMultiloginIfNeeded(
+ chrome_accounts, gaia_accounts, first_execution_);
DCHECK(is_reconcile_started_);
signin::MultiloginParameters parameters_for_multilogin;
- if (should_revoke_tokens) {
+ if (tokens_revoked) {
// Set parameters for logout for deleting cookies.
parameters_for_multilogin = kLogoutParameters;
- RevokeAllSecondaryTokens(
- identity_manager_,
- AccountReconcilorDelegate::RevokeTokenOption::kRevoke, primary_account,
- signin_metrics::SourceForRefreshTokenOperation::
- kAccountReconcilor_Reconcile);
} else {
+ bool primary_has_error =
+ identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
+ primary_account);
parameters_for_multilogin = delegate_->CalculateParametersForMultilogin(
chrome_accounts, primary_account, gaia_accounts, first_execution_,
primary_has_error);
@@ -593,13 +529,7 @@ void AccountReconcilor::OnAccountsInCookieUpdated(
// completely remove them from Chrome.
// Revoking the token for the primary account is not supported (it should be
// signed out or put to auth error state instead).
- // TODO(https://crbug.com/1122551): Move to |DiceAccountReconcilorDelegate|.
- AccountReconcilorDelegate::RevokeTokenOption revoke_option =
- delegate_->ShouldRevokeSecondaryTokensBeforeReconcile(
- verified_gaia_accounts);
- RevokeAllSecondaryTokens(identity_manager_, revoke_option, primary_account,
- signin_metrics::SourceForRefreshTokenOperation::
- kAccountReconcilor_GaiaCookiesUpdated);
+ delegate_->RevokeSecondaryTokensBeforeReconcileIfNeeded();
std::vector<CoreAccountId> chrome_accounts =
LoadValidAccountsFromTokenService();
@@ -619,33 +549,8 @@ void AccountReconcilor::OnAccountsInCookieUpdated(
}
void AccountReconcilor::OnAccountsCookieDeletedByUserAction() {
- if (!delegate_->ShouldRevokeTokensOnCookieDeleted())
- return;
-
- // This code is only used with DiceAccountReconcilorDelegate and should thus
- // use sync account.
- // TODO(https://crbug.com/1122551): Move to |DiceAccountReconcilorDelegate|.
- DCHECK_EQ(delegate_->GetConsentLevelForPrimaryAccount(), ConsentLevel::kSync);
-
- CoreAccountId primary_account =
- identity_manager_->GetPrimaryAccountId(ConsentLevel::kSync);
- // Revoke secondary tokens.
- RevokeAllSecondaryTokens(
- identity_manager_, AccountReconcilorDelegate::RevokeTokenOption::kRevoke,
- primary_account,
- signin_metrics::SourceForRefreshTokenOperation::
- kAccountReconcilor_GaiaCookiesDeletedByUser);
- if (primary_account.empty())
- return;
- if (identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
- primary_account) ||
- synced_data_deletion_in_progress_count_ == 0) {
- // Invalidate the primary token, but do not revoke it.
- auto* accounts_mutator = identity_manager_->GetAccountsMutator();
- accounts_mutator->InvalidateRefreshTokenForPrimaryAccount(
- signin_metrics::SourceForRefreshTokenOperation::
- kAccountReconcilor_GaiaCookiesDeletedByUser);
- }
+ delegate_->OnAccountsCookieDeletedByUserAction(
+ synced_data_deletion_in_progress_count_ != 0);
}
std::vector<CoreAccountId>
@@ -867,17 +772,6 @@ void AccountReconcilor::HandleReconcileTimeout() {
bool AccountReconcilor::CookieNeedsUpdate(
const signin::MultiloginParameters& parameters,
const std::vector<gaia::ListedAccount>& existing_accounts) {
- bool should_remove_unknown_account =
- !delegate_->IsUnknownInvalidAccountInCookieAllowed() &&
- HasUnknownInvalidAccountInCookie(identity_manager_, existing_accounts);
- if (should_remove_unknown_account) {
- // Removing unknown accounts in the cookie is only supported for UPDATE
- // mode.
- DCHECK_EQ(parameters.mode,
- gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER);
- return true;
- }
-
if (parameters.mode ==
gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER &&
!existing_accounts.empty() && !parameters.accounts_to_send.empty() &&
diff --git a/chromium/components/signin/core/browser/account_reconcilor.h b/chromium/components/signin/core/browser/account_reconcilor.h
index 754c29d9b97..92d03784d4d 100644
--- a/chromium/components/signin/core/browser/account_reconcilor.h
+++ b/chromium/components/signin/core/browser/account_reconcilor.h
@@ -191,7 +191,7 @@ class AccountReconcilor : public KeyedService,
HandleSigninDuringReconcile);
FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest,
DiceReconcileReuseGaiaFirstAccount);
- FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DiceDeleteCookie);
+ FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest, DeleteCookie);
FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, TokensNotLoaded);
FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
StartReconcileCookiesDisabled);
diff --git a/chromium/components/signin/core/browser/account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/account_reconcilor_delegate.cc
index 4aa86fd499f..40b068e45bf 100644
--- a/chromium/components/signin/core/browser/account_reconcilor_delegate.cc
+++ b/chromium/components/signin/core/browser/account_reconcilor_delegate.cc
@@ -34,15 +34,6 @@ ConsentLevel AccountReconcilorDelegate::GetConsentLevelForPrimaryAccount()
return ConsentLevel::kSync;
}
-CoreAccountId AccountReconcilorDelegate::GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const {
- return CoreAccountId();
-}
-
MultiloginParameters
AccountReconcilorDelegate::CalculateParametersForMultilogin(
const std::vector<CoreAccountId>& chrome_accounts,
@@ -60,12 +51,10 @@ AccountReconcilorDelegate::CalculateParametersForMultilogin(
return {mode, accounts_to_send};
}
-bool AccountReconcilorDelegate::ShouldRevokeTokensBeforeMultilogin(
+bool AccountReconcilorDelegate::RevokeSecondaryTokensBeforeMultiloginIfNeeded(
const std::vector<CoreAccountId>& chrome_accounts,
- const CoreAccountId& primary_account,
const std::vector<gaia::ListedAccount>& gaia_accounts,
- bool first_execution,
- bool primary_has_error) const {
+ bool first_execution) {
return false;
}
@@ -186,15 +175,11 @@ AccountReconcilorDelegate::GetChromeAccountsForReconcile(
return std::vector<CoreAccountId>();
}
-AccountReconcilorDelegate::RevokeTokenOption
-AccountReconcilorDelegate::ShouldRevokeSecondaryTokensBeforeReconcile(
- const std::vector<gaia::ListedAccount>& gaia_accounts) {
- return RevokeTokenOption::kDoNotRevoke;
+void AccountReconcilorDelegate::RevokeSecondaryTokensBeforeReconcileIfNeeded() {
}
-bool AccountReconcilorDelegate::ShouldRevokeTokensOnCookieDeleted() {
- return false;
-}
+void AccountReconcilorDelegate::OnAccountsCookieDeletedByUserAction(
+ bool synced_data_deletion_in_progress) {}
bool AccountReconcilorDelegate::ShouldRevokeTokensIfNoPrimaryAccount() const {
return true;
@@ -207,8 +192,4 @@ base::TimeDelta AccountReconcilorDelegate::GetReconcileTimeout() const {
void AccountReconcilorDelegate::OnReconcileError(
const GoogleServiceAuthError& error) {}
-bool AccountReconcilorDelegate::IsUnknownInvalidAccountInCookieAllowed() const {
- return true;
-}
-
} // namespace signin
diff --git a/chromium/components/signin/core/browser/account_reconcilor_delegate.h b/chromium/components/signin/core/browser/account_reconcilor_delegate.h
index 9c49aa6c657..781ddd9519d 100644
--- a/chromium/components/signin/core/browser/account_reconcilor_delegate.h
+++ b/chromium/components/signin/core/browser/account_reconcilor_delegate.h
@@ -22,17 +22,6 @@ namespace signin {
// Base class for AccountReconcilorDelegate.
class AccountReconcilorDelegate {
public:
- // Options for revoking refresh tokens.
- enum class RevokeTokenOption {
- // Do not revoke the token.
- kDoNotRevoke,
- // Revoke the token if it is in auth error state.
- kRevokeIfInError,
- // Revoke the token.
- // TODO(droger): remove this when Dice is launched.
- kRevoke
- };
-
AccountReconcilorDelegate();
virtual ~AccountReconcilorDelegate();
@@ -51,20 +40,6 @@ class AccountReconcilorDelegate {
// account. Defaults to ConsentLevel::kSync.
virtual ConsentLevel GetConsentLevelForPrimaryAccount() const;
- // Returns the first account to add in the Gaia cookie.
- // If this returns an empty string, the user must be logged out of all
- // accounts.
- // |first_execution| is true for the first reconciliation after startup.
- // |will_logout| is true if the reconcilor will perform a logout no matter
- // what is returned by this function.
- // Only used with MergeSession.
- virtual CoreAccountId GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const;
-
// Returns a pair of mode and accounts to send to Mutilogin endpoint.
MultiloginParameters CalculateParametersForMultilogin(
const std::vector<CoreAccountId>& chrome_accounts,
@@ -73,28 +48,23 @@ class AccountReconcilorDelegate {
bool first_execution,
bool primary_has_error) const;
- // Returns whether secondary accounts should be revoked for doing full logout.
- // Used only for the Multilogin codepath.
- virtual bool ShouldRevokeTokensBeforeMultilogin(
+ // Revokes secondary tokens if needed based on the platform.
+ // Returns whether tokens has been revoked.
+ virtual bool RevokeSecondaryTokensBeforeMultiloginIfNeeded(
const std::vector<CoreAccountId>& chrome_accounts,
- const CoreAccountId& primary_account,
const std::vector<gaia::ListedAccount>& gaia_accounts,
- bool first_execution,
- bool primary_has_error) const;
+ bool first_execution);
- // Returns whether secondary accounts should be revoked at the beginning of
- // the reconcile.
- virtual RevokeTokenOption ShouldRevokeSecondaryTokensBeforeReconcile(
- const std::vector<gaia::ListedAccount>& gaia_accounts);
+ // Revokes secondary accounts if needed.
+ virtual void RevokeSecondaryTokensBeforeReconcileIfNeeded();
- // Returns whether tokens should be revoked when the Gaia cookie has been
- // explicitly deleted by the user.
- // If this returns false, tokens will not be revoked. If this returns true,
- // secondary tokens will be deleted ; and the primary token will be
- // invalidated unless it has to be kept for critical Sync operations.
- virtual bool ShouldRevokeTokensOnCookieDeleted();
+ // Called when cookies are deleted by user action.
+ // This might be a no-op or signout the profile or lead to a sync paused state
+ // based on different platforms conditions.
+ virtual void OnAccountsCookieDeletedByUserAction(
+ bool synced_data_deletion_in_progress);
- // Returns whether tokens should be revoked when the primary account is empty
+ // Returns whether tokens should be revoked when the primary account is empty.
virtual bool ShouldRevokeTokensIfNoPrimaryAccount() const;
// Called when reconcile is finished.
@@ -115,12 +85,6 @@ class AccountReconcilorDelegate {
// |OnReconcileError| is called before |OnReconcileFinished|.
virtual void OnReconcileError(const GoogleServiceAuthError& error);
- // If this returns false, the reconcilor ensures that all accounts unknown to
- // Chrome are always removed from the cookies (even if their session is
- // expired). Returning false is only supported in with multilogin UPDATE mode.
- // Defaults to true.
- virtual bool IsUnknownInvalidAccountInCookieAllowed() const;
-
void set_reconcilor(AccountReconcilor* reconcilor) {
reconcilor_ = reconcilor;
}
diff --git a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc
index 81299e3dd4e..3af02870985 100644
--- a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -52,6 +52,11 @@
#include "components/signin/core/browser/active_directory_account_reconcilor_delegate.h"
#endif
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "components/signin/core/browser/mirror_landing_account_reconcilor_delegate.h"
+#include "components/signin/public/base/signin_switches.h"
+#endif
+
using signin_metrics::AccountReconcilorState;
using testing::_;
@@ -71,15 +76,6 @@ class SpyReconcilorDelegate : public signin::AccountReconcilorDelegate {
bool ShouldAbortReconcileIfPrimaryHasError() const override { return true; }
- CoreAccountId GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const override {
- return primary_account;
- }
-
std::vector<CoreAccountId> GetChromeAccountsForReconcile(
const std::vector<CoreAccountId>& chrome_accounts,
const CoreAccountId& primary_account,
@@ -114,11 +110,11 @@ class DummyAccountReconcilorWithDelegate : public AccountReconcilor {
signin::IdentityManager* identity_manager,
SigninClient* client,
signin::AccountConsistencyMethod account_consistency)
- : AccountReconcilor(
- identity_manager,
- client,
- CreateAccountReconcilorDelegate(identity_manager,
- account_consistency)) {
+ : AccountReconcilor(identity_manager,
+ client,
+ CreateAccountReconcilorDelegate(identity_manager,
+ account_consistency,
+ client)) {
Initialize(false /* start_reconcile_if_tokens_available */);
}
@@ -138,16 +134,26 @@ class DummyAccountReconcilorWithDelegate : public AccountReconcilor {
static std::unique_ptr<signin::AccountReconcilorDelegate>
CreateAccountReconcilorDelegate(
signin::IdentityManager* identity_manager,
- signin::AccountConsistencyMethod account_consistency) {
+ signin::AccountConsistencyMethod account_consistency,
+ SigninClient* client) {
switch (account_consistency) {
case signin::AccountConsistencyMethod::kMirror:
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ if (base::FeatureList::IsEnabled(switches::kLacrosNonSyncingProfiles)) {
+ bool is_main_profile = client->GetInitialPrimaryAccount().has_value();
+ return std::make_unique<
+ signin::MirrorLandingAccountReconcilorDelegate>(identity_manager,
+ is_main_profile);
+ }
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
return std::make_unique<signin::MirrorAccountReconcilorDelegate>(
identity_manager);
case signin::AccountConsistencyMethod::kDisabled:
return std::make_unique<signin::AccountReconcilorDelegate>();
case signin::AccountConsistencyMethod::kDice:
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- return std::make_unique<signin::DiceAccountReconcilorDelegate>();
+ return std::make_unique<signin::DiceAccountReconcilorDelegate>(
+ identity_manager);
#else
NOTREACHED();
return nullptr;
@@ -615,8 +621,9 @@ class AccountReconcilorTestTable
}
};
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-
+// On Lacros, the reconcilor is always registered as reconcile is always
+// enabled.
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
TEST_F(AccountReconcilorMirrorTest, IdentityManagerRegistration) {
AccountReconcilor* reconcilor = GetMockReconcilor();
ASSERT_TRUE(reconcilor);
@@ -650,7 +657,7 @@ TEST_F(AccountReconcilorMirrorTest, Reauth) {
ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager());
}
-#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
TEST_F(AccountReconcilorMirrorTest, ProfileAlreadyConnected) {
ConnectProfileToAccount("user@gmail.com");
@@ -1208,9 +1215,7 @@ TEST_F(AccountReconcilorDiceTest, UnverifiedAccountMerge) {
ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
}
-TEST_F(AccountReconcilorTest, DiceDeleteCookie) {
- SetAccountConsistency(signin::AccountConsistencyMethod::kDice);
-
+TEST_F(AccountReconcilorDiceTest, DeleteCookie) {
const CoreAccountId primary_account_id =
identity_test_env()
->MakePrimaryAccountAvailable("user@gmail.com",
@@ -1817,6 +1822,7 @@ TEST_F(AccountReconcilorMirrorTest,
ASSERT_TRUE(reconcilor->is_reconcile_started_);
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
// This test is needed until chrome changes to use gaia obfuscated id.
// The primary account manager and token service use the gaia "email" property,
// which preserves dots in usernames and preserves case.
@@ -1826,10 +1832,9 @@ TEST_F(AccountReconcilorMirrorTest,
// "Dot.S@hmail.com", as seen by the token service, will be considered the same
// as "dots@gmail.com" as returned by gaia::ParseListAccountsData().
TEST_F(AccountReconcilorMirrorTest, StartReconcileNoopWithDots) {
- if (identity_test_env()->identity_manager()->GetAccountIdMigrationState() !=
- signin::IdentityManager::AccountIdMigrationState::MIGRATION_NOT_STARTED) {
- return;
- }
+ ASSERT_EQ(
+ identity_test_env()->identity_manager()->GetAccountIdMigrationState(),
+ signin::IdentityManager::AccountIdMigrationState::MIGRATION_DONE);
AccountInfo account_info = ConnectProfileToAccount("Dot.S@gmail.com");
signin::SetListAccountsResponseOneAccount(
@@ -1841,6 +1846,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileNoopWithDots) {
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(reconcilor->is_reconcile_started_);
}
+#endif
TEST_F(AccountReconcilorMirrorTest, StartReconcileNoopMultiple) {
AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
@@ -1938,7 +1944,14 @@ TEST_F(AccountReconcilorTest, AuthErrorTriggersListAccount) {
ASSERT_FALSE(reconcilor->is_reconcile_started_);
signin::SetListAccountsResponseOneAccount(
account_info.email, account_info.gaia, &test_url_loader_factory_);
- if (account_consistency == signin::AccountConsistencyMethod::kDice) {
+
+ bool expect_logout =
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ base::FeatureList::IsEnabled(switches::kLacrosNonSyncingProfiles);
+#else
+ account_consistency == signin::AccountConsistencyMethod::kDice;
+#endif
+ if (expect_logout) {
EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
.Times(1);
}
@@ -2025,21 +2038,34 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileRemoveFromCookie) {
ASSERT_FALSE(reconcilor->is_reconcile_started_);
}
-// Check that reconcile is aborted if there is token error on primary account.
+// Check that token error on primary account results in a logout to all accounts
+// on Lacros. For other mirror platforms, reconcile is aborted.
TEST_F(AccountReconcilorMirrorTest, TokenErrorOnPrimary) {
AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
signin::UpdatePersistentErrorOfRefreshTokenForAccount(
identity_test_env()->identity_manager(), account_info.account_id,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ if (base::FeatureList::IsEnabled(switches::kLacrosNonSyncingProfiles)) {
+ EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
+ }
+#endif
+ AccountReconcilor* reconcilor = GetMockReconcilor();
signin::SetListAccountsResponseTwoAccounts(
account_info.email, account_info.gaia, "other@gmail.com", "67890",
&test_url_loader_factory_);
-
- AccountReconcilor* reconcilor = GetMockReconcilor();
reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange);
-
base::RunLoop().RunUntilIdle();
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ if (base::FeatureList::IsEnabled(switches::kLacrosNonSyncingProfiles)) {
+ ASSERT_TRUE(reconcilor->is_reconcile_started_);
+ SimulateLogOutFromCookieCompleted(reconcilor,
+ GoogleServiceAuthError::AuthErrorNone());
+ testing::Mock::VerifyAndClearExpectations(GetMockReconcilor());
+ base::RunLoop().RunUntilIdle();
+ }
+#endif
ASSERT_FALSE(reconcilor->is_reconcile_started_);
}
diff --git a/chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.cc
index 1e720841a60..54629b0767c 100644
--- a/chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.cc
+++ b/chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.cc
@@ -27,16 +27,6 @@ bool ActiveDirectoryAccountReconcilorDelegate::IsReconcileEnabled() const {
return true;
}
-CoreAccountId
-ActiveDirectoryAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const {
- return GetFirstAccount(chrome_accounts, gaia_accounts);
-}
-
std::vector<CoreAccountId>
ActiveDirectoryAccountReconcilorDelegate::GetChromeAccountsForReconcile(
const std::vector<CoreAccountId>& chrome_accounts,
diff --git a/chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.h
index b49f63c1dad..a95eb8c25c1 100644
--- a/chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.h
+++ b/chromium/components/signin/core/browser/active_directory_account_reconcilor_delegate.h
@@ -27,12 +27,6 @@ class ActiveDirectoryAccountReconcilorDelegate
// AccountReconcilorDelegate:
gaia::GaiaSource GetGaiaApiSource() const override;
bool IsReconcileEnabled() const override;
- CoreAccountId GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const override;
std::vector<CoreAccountId> GetChromeAccountsForReconcile(
const std::vector<CoreAccountId>& chrome_accounts,
diff --git a/chromium/components/signin/core/browser/chrome_connected_header_helper.cc b/chromium/components/signin/core/browser/chrome_connected_header_helper.cc
index 78876a115e0..15fa8016f24 100644
--- a/chromium/components/signin/core/browser/chrome_connected_header_helper.cc
+++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.cc
@@ -182,7 +182,7 @@ bool ChromeConnectedHeaderHelper::IsUrlEligibleForRequestHeader(
google_util::IsYoutubeDomainUrl(
url, google_util::ALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS) ||
- gaia::IsGaiaSignonRealm(url.DeprecatedGetOriginAsURL());
+ gaia::HasGaiaSchemeHostPort(url);
}
}
}
@@ -207,13 +207,13 @@ std::string ChromeConnectedHeaderHelper::BuildRequestHeader(
// Sessions and Active Directory logins. Guest Sessions have already been
// filtered upstream and we want to enforce account consistency in Public
// Sessions and Active Directory logins.
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
force_account_consistency = true;
#endif
if (!force_account_consistency && gaia_id.empty()) {
#if BUILDFLAG(IS_ANDROID)
- if (gaia::IsGaiaSignonRealm(url.DeprecatedGetOriginAsURL())) {
+ if (gaia::HasGaiaSchemeHostPort(url)) {
parts.push_back(
base::StringPrintf("%s=%s", kEligibleForConsistency, "true"));
return base::JoinString(parts, is_header_request ? "," : ":");
diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc
index 6a3fc9416ed..d878170552e 100644
--- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc
+++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc
@@ -13,10 +13,45 @@
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/signin_client.h"
#include "components/signin/public/base/signin_pref_names.h"
+#include "components/signin/public/identity_manager/accounts_mutator.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
namespace signin {
-DiceAccountReconcilorDelegate::DiceAccountReconcilorDelegate() = default;
+// Revokes tokens for all accounts in chrome_accounts but the primary account.
+void RevokeAllSecondaryTokens(
+ IdentityManager* identity_manager,
+ signin_metrics::SourceForRefreshTokenOperation source,
+ bool revoke_only_if_in_error) {
+ // The sync account should not be removed but put in a paused state, therefore
+ // this function excludes only the primary account with sync consent.
+ CoreAccountId primary_account =
+ identity_manager->GetPrimaryAccountId(ConsentLevel::kSync);
+
+ auto* accounts_mutator = identity_manager->GetAccountsMutator();
+ for (const CoreAccountInfo& account_info :
+ identity_manager->GetAccountsWithRefreshTokens()) {
+ CoreAccountId account = account_info.account_id;
+
+ bool is_primary_account =
+ !primary_account.empty() && account == primary_account;
+
+ if (is_primary_account)
+ continue;
+
+ bool should_revoke =
+ !revoke_only_if_in_error ||
+ identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
+ account);
+
+ if (should_revoke)
+ accounts_mutator->RemoveAccount(account, source);
+ }
+}
+
+DiceAccountReconcilorDelegate::DiceAccountReconcilorDelegate(
+ IdentityManager* identity_manager)
+ : identity_manager_(identity_manager) {}
DiceAccountReconcilorDelegate::~DiceAccountReconcilorDelegate() = default;
bool DiceAccountReconcilorDelegate::IsReconcileEnabled() const {
@@ -84,81 +119,6 @@ gaia::GaiaSource DiceAccountReconcilorDelegate::GetGaiaApiSource() const {
return gaia::GaiaSource::kAccountReconcilorDice;
}
-// - On first execution, the candidates are examined in this order:
-// 1. The primary account
-// 2. The current first Gaia account
-// 3. The last known first Gaia account
-// 4. The first account in the token service
-// - On subsequent executions, the order is:
-// 1. The current first Gaia account
-// 2. The primary account
-// 3. The last known first Gaia account
-// 4. The first account in the token service
-CoreAccountId DiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const {
- bool primary_account_has_token =
- !primary_account.empty() &&
- base::Contains(chrome_accounts, primary_account);
-
- if (gaia_accounts.empty()) {
- if (primary_account_has_token)
- return primary_account;
-
- // Try the last known account. This happens when the cookies are cleared
- // while Sync is disabled.
- if (base::Contains(chrome_accounts, last_known_first_account_))
- return last_known_first_account_;
-
- // As a last resort, use the first Chrome account.
- return chrome_accounts.empty() ? CoreAccountId() : chrome_accounts[0];
- }
-
- const CoreAccountId& first_gaia_account = gaia_accounts[0].id;
- bool first_gaia_account_has_token =
- base::Contains(chrome_accounts, first_gaia_account);
-
- if (!first_gaia_account_has_token &&
- (primary_account == first_gaia_account) && gaia_accounts[0].valid) {
- // The primary account is also the first Gaia account, and has no token.
- // Logout everything.
- return CoreAccountId();
- }
-
- // If the primary Chrome account and the default Gaia account are both in
- // error, then the first gaia account can be kept, to avoid logging the user
- // out of their other accounts.
- // It's only possible when the reconcilor will not perform a logout, because
- // that account cannot be rebuilt.
- if (!first_gaia_account_has_token && !gaia_accounts[0].valid && !will_logout)
- return first_gaia_account;
-
- if (first_execution) {
- // On first execution, try the primary account, and then the first Gaia
- // account.
- if (primary_account_has_token)
- return primary_account;
- if (first_gaia_account_has_token)
- return first_gaia_account;
- // As a last resort, use the first Chrome account.
- return chrome_accounts.empty() ? CoreAccountId() : chrome_accounts[0];
- }
-
- // While Chrome is running, try the first Gaia account, and then the
- // primary account.
- if (first_gaia_account_has_token)
- return first_gaia_account;
- if (primary_account_has_token)
- return primary_account;
-
- // Changing the first Gaia account while Chrome is running would be
- // confusing for the user. Logout everything.
- return CoreAccountId();
-}
-
bool DiceAccountReconcilorDelegate::ShouldDeleteAccountsFromGaia(
const std::vector<CoreAccountId>& chrome_accounts,
const std::vector<gaia::ListedAccount>& gaia_accounts) const {
@@ -185,12 +145,34 @@ bool DiceAccountReconcilorDelegate::IsPreserveModePossible(
first_account == gaia_accounts[0].id;
}
+bool DiceAccountReconcilorDelegate::
+ RevokeSecondaryTokensBeforeMultiloginIfNeeded(
+ const std::vector<CoreAccountId>& chrome_accounts,
+ const std::vector<gaia::ListedAccount>& gaia_accounts,
+ bool first_execution) {
+ if (!ShouldRevokeTokensBeforeMultilogin(chrome_accounts, gaia_accounts,
+ first_execution)) {
+ return false;
+ }
+
+ RevokeAllSecondaryTokens(identity_manager_,
+ signin_metrics::SourceForRefreshTokenOperation::
+ kAccountReconcilor_Reconcile,
+ /*revoke_only_if_in_error=*/false);
+ return true;
+}
+
bool DiceAccountReconcilorDelegate::ShouldRevokeTokensBeforeMultilogin(
const std::vector<CoreAccountId>& chrome_accounts,
- const CoreAccountId& primary_account,
const std::vector<gaia::ListedAccount>& gaia_accounts,
- bool first_execution,
- bool primary_has_error) const {
+ bool first_execution) const {
+ CoreAccountId primary_account =
+ identity_manager_->GetPrimaryAccountId(ConsentLevel::kSync);
+
+ bool primary_has_error =
+ identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
+ primary_account);
+
// If Gaia accounts are empty, any combination of accounts can be set and
// logout is not needed.
if (gaia_accounts.empty())
@@ -280,14 +262,42 @@ gaia::MultiloginMode DiceAccountReconcilorDelegate::CalculateModeForReconcile(
: gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER;
}
-AccountReconcilorDelegate::RevokeTokenOption
-DiceAccountReconcilorDelegate::ShouldRevokeSecondaryTokensBeforeReconcile(
- const std::vector<gaia::ListedAccount>& gaia_accounts) {
- return RevokeTokenOption::kRevokeIfInError;
+void DiceAccountReconcilorDelegate::
+ RevokeSecondaryTokensBeforeReconcileIfNeeded() {
+ RevokeAllSecondaryTokens(identity_manager_,
+ signin_metrics::SourceForRefreshTokenOperation::
+ kAccountReconcilor_GaiaCookiesUpdated,
+ /*revoke_only_if_in_error=*/true);
}
-bool DiceAccountReconcilorDelegate::ShouldRevokeTokensOnCookieDeleted() {
- return true;
+void DiceAccountReconcilorDelegate::OnAccountsCookieDeletedByUserAction(
+ bool synced_data_deletion_in_progress) {
+ // Revoke secondary tokens to avoid reconcilor rebuilding cookies.
+ RevokeAllSecondaryTokens(identity_manager_,
+ signin_metrics::SourceForRefreshTokenOperation::
+ kAccountReconcilor_GaiaCookiesDeletedByUser,
+ /*revoke_only_if_in_error=*/false);
+
+ if (!identity_manager_->HasPrimaryAccount(ConsentLevel::kSync))
+ return;
+
+ DCHECK_EQ(GetConsentLevelForPrimaryAccount(), ConsentLevel::kSync);
+ CoreAccountId primary_sync_account =
+ identity_manager_->GetPrimaryAccountId(ConsentLevel::kSync);
+
+ // Sync account should be paused if the account cookie is deleted by user
+ // action. If sync data deletion in progress, avoid invalidating the sync
+ // account unless it is already in a persistent error state. This is needed to
+ // ensure the data gets deleted from the google account.
+ if (!synced_data_deletion_in_progress ||
+ identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
+ primary_sync_account)) {
+ // Invalidate the primary token, but do not revoke it.
+ auto* accounts_mutator = identity_manager_->GetAccountsMutator();
+ accounts_mutator->InvalidateRefreshTokenForPrimaryAccount(
+ signin_metrics::SourceForRefreshTokenOperation::
+ kAccountReconcilor_GaiaCookiesDeletedByUser);
+ }
}
void DiceAccountReconcilorDelegate::OnReconcileFinished(
diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h
index 581ed5be32f..552286790c1 100644
--- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h
+++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h
@@ -10,10 +10,12 @@
namespace signin {
+class IdentityManager;
+
// AccountReconcilorDelegate specialized for Dice.
class DiceAccountReconcilorDelegate : public AccountReconcilorDelegate {
public:
- DiceAccountReconcilorDelegate();
+ explicit DiceAccountReconcilorDelegate(IdentityManager* identity_manager);
DiceAccountReconcilorDelegate(const DiceAccountReconcilorDelegate&) = delete;
DiceAccountReconcilorDelegate& operator=(
@@ -24,22 +26,14 @@ class DiceAccountReconcilorDelegate : public AccountReconcilorDelegate {
// AccountReconcilorDelegate:
bool IsReconcileEnabled() const override;
gaia::GaiaSource GetGaiaApiSource() const override;
- CoreAccountId GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const override;
- RevokeTokenOption ShouldRevokeSecondaryTokensBeforeReconcile(
- const std::vector<gaia::ListedAccount>& gaia_accounts) override;
+ void RevokeSecondaryTokensBeforeReconcileIfNeeded() override;
void OnReconcileFinished(const CoreAccountId& first_account) override;
- bool ShouldRevokeTokensOnCookieDeleted() override;
- bool ShouldRevokeTokensBeforeMultilogin(
+ void OnAccountsCookieDeletedByUserAction(
+ bool synced_data_deletion_in_progress) override;
+ bool RevokeSecondaryTokensBeforeMultiloginIfNeeded(
const std::vector<CoreAccountId>& chrome_accounts,
- const CoreAccountId& primary_account,
const std::vector<gaia::ListedAccount>& gaia_accounts,
- bool first_execution,
- bool primary_has_error) const override;
+ bool first_execution) override;
private:
// Possible inconsistency reasons between tokens and gaia cookies.
@@ -108,6 +102,15 @@ class DiceAccountReconcilorDelegate : public AccountReconcilorDelegate {
bool first_execution,
bool primary_has_error) const;
+ // Returns whether secondary accounts should be revoked for doing full logout.
+ // Used only for the Multilogin codepath.
+ bool ShouldRevokeTokensBeforeMultilogin(
+ const std::vector<CoreAccountId>& chrome_accounts,
+ const std::vector<gaia::ListedAccount>& gaia_accounts,
+ bool first_execution) const;
+
+ const raw_ptr<IdentityManager> identity_manager_;
+
// Last known "first account". Used when cookies are lost as a best guess.
CoreAccountId last_known_first_account_;
};
diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc
deleted file mode 100644
index 0ed6807c101..00000000000
--- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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 "components/signin/core/browser/dice_account_reconcilor_delegate.h"
-
-#include <vector>
-
-#include "components/prefs/pref_registry_simple.h"
-#include "components/signin/public/base/account_consistency_method.h"
-#include "components/signin/public/base/signin_pref_names.h"
-#include "components/signin/public/base/test_signin_client.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "google_apis/gaia/gaia_auth_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace signin {
-
-TEST(DiceAccountReconcilorDelegateTest, RevokeTokens) {
- gaia::ListedAccount gaia_account;
- gaia_account.id = CoreAccountId("other");
- DiceAccountReconcilorDelegate delegate;
- EXPECT_EQ(
- signin::AccountReconcilorDelegate::RevokeTokenOption::kRevokeIfInError,
- delegate.ShouldRevokeSecondaryTokensBeforeReconcile(
- std::vector<gaia::ListedAccount>()));
-}
-
-} // namespace signin
diff --git a/chromium/components/signin/core/browser/dice_header_helper.cc b/chromium/components/signin/core/browser/dice_header_helper.cc
index aa1cacf4880..04825859968 100644
--- a/chromium/components/signin/core/browser/dice_header_helper.cc
+++ b/chromium/components/signin/core/browser/dice_header_helper.cc
@@ -204,7 +204,7 @@ bool DiceHeaderHelper::IsUrlEligibleForRequestHeader(const GURL& url) {
if (account_consistency_ != AccountConsistencyMethod::kDice)
return false;
- return gaia::IsGaiaSignonRealm(url.DeprecatedGetOriginAsURL());
+ return gaia::HasGaiaSchemeHostPort(url);
}
std::string DiceHeaderHelper::BuildRequestHeader(
diff --git a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
index b83cc8b7b86..943d43b471c 100644
--- a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
+++ b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
@@ -52,18 +52,6 @@ ConsentLevel MirrorAccountReconcilorDelegate::GetConsentLevelForPrimaryAccount()
#endif
}
-CoreAccountId MirrorAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const {
- // Mirror only uses the primary account, and it is never empty.
- DCHECK(!primary_account.empty());
- DCHECK(base::Contains(chrome_accounts, primary_account));
- return primary_account;
-}
-
std::vector<CoreAccountId>
MirrorAccountReconcilorDelegate::GetChromeAccountsForReconcile(
const std::vector<CoreAccountId>& chrome_accounts,
diff --git a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h
index 6e0de8ba33c..1b3af51a09d 100644
--- a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h
+++ b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h
@@ -39,12 +39,6 @@ class MirrorAccountReconcilorDelegate : public AccountReconcilorDelegate,
gaia::GaiaSource GetGaiaApiSource() const override;
bool ShouldAbortReconcileIfPrimaryHasError() const override;
ConsentLevel GetConsentLevelForPrimaryAccount() const override;
- CoreAccountId GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const override;
std::vector<CoreAccountId> GetChromeAccountsForReconcile(
const std::vector<CoreAccountId>& chrome_accounts,
const CoreAccountId& primary_account,
diff --git a/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.cc
index b62569359f5..6a5bfae1211 100644
--- a/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.cc
+++ b/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.cc
@@ -6,14 +6,19 @@
#include "base/containers/contains.h"
#include "base/logging.h"
+#include "components/signin/public/base/signin_metrics.h"
+#include "components/signin/public/identity_manager/accounts_mutator.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
#include "google_apis/gaia/core_account_id.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/gaia_auth_util.h"
namespace signin {
-MirrorLandingAccountReconcilorDelegate::
- MirrorLandingAccountReconcilorDelegate() = default;
+MirrorLandingAccountReconcilorDelegate::MirrorLandingAccountReconcilorDelegate(
+ IdentityManager* identity_manager,
+ bool is_main_profile)
+ : identity_manager_(identity_manager), is_main_profile_(is_main_profile) {}
MirrorLandingAccountReconcilorDelegate::
~MirrorLandingAccountReconcilorDelegate() = default;
@@ -27,9 +32,25 @@ gaia::GaiaSource MirrorLandingAccountReconcilorDelegate::GetGaiaApiSource()
return gaia::GaiaSource::kAccountReconcilorMirror;
}
+bool MirrorLandingAccountReconcilorDelegate::ShouldRevokeTokensOnCookieDeleted()
+ const {
+ return !is_main_profile_ &&
+ !identity_manager_->HasPrimaryAccount(ConsentLevel::kSync);
+}
+
+void MirrorLandingAccountReconcilorDelegate::
+ OnAccountsCookieDeletedByUserAction(bool synced_data_deletion_in_progress) {
+ if (!ShouldRevokeTokensOnCookieDeleted())
+ return;
+
+ identity_manager_->GetAccountsMutator()->RemoveAllAccounts(
+ signin_metrics::SourceForRefreshTokenOperation::
+ kAccountReconcilor_GaiaCookiesDeletedByUser);
+}
+
bool MirrorLandingAccountReconcilorDelegate::
ShouldAbortReconcileIfPrimaryHasError() const {
- return true;
+ return false;
}
ConsentLevel
@@ -38,24 +59,6 @@ MirrorLandingAccountReconcilorDelegate::GetConsentLevelForPrimaryAccount()
return ConsentLevel::kSignin;
}
-CoreAccountId
-MirrorLandingAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const {
- if (!primary_account.empty()) {
- // `ShouldAbortReconcileIfPrimaryHasError()` returns true.
- DCHECK(base::Contains(chrome_accounts, primary_account));
- return primary_account;
- }
-
- // If there is no primary account, there should be no account at all.
- DCHECK(chrome_accounts.empty());
- return CoreAccountId();
-}
-
std::vector<CoreAccountId>
MirrorLandingAccountReconcilorDelegate::GetChromeAccountsForReconcile(
const std::vector<CoreAccountId>& chrome_accounts,
@@ -64,9 +67,10 @@ MirrorLandingAccountReconcilorDelegate::GetChromeAccountsForReconcile(
bool first_execution,
bool primary_has_error,
const gaia::MultiloginMode mode) const {
- DCHECK(!primary_has_error);
DCHECK_EQ(mode,
gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER);
+ if (primary_has_error)
+ return {}; // Log out all accounts.
return ReorderChromeAccountsForReconcile(chrome_accounts, primary_account,
gaia_accounts);
}
diff --git a/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.h
index ae252c016af..b020696f37f 100644
--- a/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.h
+++ b/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate.h
@@ -9,12 +9,15 @@
namespace signin {
+class IdentityManager;
+
// AccountReconcilorDelegate specialized for Mirror, using the "Mirror landing"
// variant. Mirror is always enabled, even when there is no primary account.
class MirrorLandingAccountReconcilorDelegate
: public AccountReconcilorDelegate {
public:
- MirrorLandingAccountReconcilorDelegate();
+ MirrorLandingAccountReconcilorDelegate(IdentityManager* identity_manager,
+ bool is_main_profile);
~MirrorLandingAccountReconcilorDelegate() override;
MirrorLandingAccountReconcilorDelegate(
@@ -27,12 +30,8 @@ class MirrorLandingAccountReconcilorDelegate
gaia::GaiaSource GetGaiaApiSource() const override;
bool ShouldAbortReconcileIfPrimaryHasError() const override;
ConsentLevel GetConsentLevelForPrimaryAccount() const override;
- CoreAccountId GetFirstGaiaAccountForReconcile(
- const std::vector<CoreAccountId>& chrome_accounts,
- const std::vector<gaia::ListedAccount>& gaia_accounts,
- const CoreAccountId& primary_account,
- bool first_execution,
- bool will_logout) const override;
+ void OnAccountsCookieDeletedByUserAction(
+ bool synced_data_deletion_in_progress) override;
std::vector<CoreAccountId> GetChromeAccountsForReconcile(
const std::vector<CoreAccountId>& chrome_accounts,
const CoreAccountId& primary_account,
@@ -40,6 +39,11 @@ class MirrorLandingAccountReconcilorDelegate
bool first_execution,
bool primary_has_error,
const gaia::MultiloginMode mode) const override;
+
+ private:
+ bool ShouldRevokeTokensOnCookieDeleted() const;
+ const raw_ptr<IdentityManager> identity_manager_;
+ const bool is_main_profile_;
};
} // namespace signin
diff --git a/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate_unittest.cc b/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate_unittest.cc
index 94cc99bdc1d..92f794d3788 100644
--- a/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate_unittest.cc
+++ b/chromium/components/signin/core/browser/mirror_landing_account_reconcilor_delegate_unittest.cc
@@ -6,6 +6,8 @@
#include <string>
+#include "base/test/task_environment.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "google_apis/gaia/core_account_id.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -14,6 +16,9 @@ namespace signin {
namespace {
+const char* kPrimaryAccount = "primary@gmail.com";
+const char* kSecondaryAccount = "secondary@gmail.com";
+
gaia::ListedAccount BuildListedAccount(const std::string& gaia_id) {
CoreAccountId account_id = CoreAccountId::FromGaiaId(gaia_id);
gaia::ListedAccount gaia_account;
@@ -24,35 +29,58 @@ gaia::ListedAccount BuildListedAccount(const std::string& gaia_id) {
return gaia_account;
}
-} // namespace
+void AddPrimaryAndSecondaryAccounts(IdentityTestEnvironment* env,
+ ConsentLevel level) {
+ const CoreAccountId primary_account_id =
+ env->MakePrimaryAccountAvailable(kPrimaryAccount, level).account_id;
-TEST(MirrorLandingAccountReconcilorDelegateTest,
- GetFirstGaiaAccountForReconcile) {
- gaia::ListedAccount gaia_account = BuildListedAccount("gaia");
- CoreAccountId kPrimaryAccountId = CoreAccountId::FromGaiaId("primary");
- CoreAccountId kOtherAccountId = CoreAccountId::FromGaiaId("other");
- MirrorLandingAccountReconcilorDelegate delegate;
- // No primary account.
- EXPECT_TRUE(delegate
- .GetFirstGaiaAccountForReconcile(
- /*chrome_accounts=*/{},
- /*gaia_accounts=*/{gaia_account},
- /*primary_account=*/CoreAccountId(),
- /*first_execution=*/true,
- /*will_logout=*/false)
- .empty());
- // With primary account.
- EXPECT_EQ(delegate.GetFirstGaiaAccountForReconcile(
- /*chrome_accounts=*/{{kOtherAccountId, kPrimaryAccountId}},
- /*gaia_accounts=*/{gaia_account},
- /*primary_account=*/kPrimaryAccountId,
- /*first_execution=*/true,
- /*will_logout=*/false),
- kPrimaryAccountId);
+ // Add secondary account.
+ env->MakeAccountAvailable(kSecondaryAccount);
+
+ auto* identity_manager = env->identity_manager();
+ EXPECT_EQ(2U, identity_manager->GetAccountsWithRefreshTokens().size());
+ EXPECT_FALSE(
+ identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
+ primary_account_id));
}
-TEST(MirrorLandingAccountReconcilorDelegateTest,
- GetChromeAccountsForReconcile) {
+} // namespace
+
+class MirrorLandingAccountReconcilorDelegateTest : public ::testing::Test {
+ public:
+ MirrorLandingAccountReconcilorDelegateTest(
+ const MirrorLandingAccountReconcilorDelegateTest&) = delete;
+ MirrorLandingAccountReconcilorDelegateTest& operator=(
+ const MirrorLandingAccountReconcilorDelegateTest&) = delete;
+
+ protected:
+ MirrorLandingAccountReconcilorDelegateTest();
+ ~MirrorLandingAccountReconcilorDelegateTest() override;
+
+ IdentityTestEnvironment* identity_test_env() { return &identity_test_env_; }
+
+ IdentityManager* identity_manager() {
+ return identity_test_env()->identity_manager();
+ }
+
+ std::unique_ptr<MirrorLandingAccountReconcilorDelegate>
+ CreateMirrorLandingAccountReconcilorDelegate(bool is_main_profile) {
+ return std::make_unique<MirrorLandingAccountReconcilorDelegate>(
+ identity_manager(), is_main_profile);
+ }
+
+ private:
+ base::test::SingleThreadTaskEnvironment task_environment_;
+ signin::IdentityTestEnvironment identity_test_env_;
+};
+
+MirrorLandingAccountReconcilorDelegateTest::
+ MirrorLandingAccountReconcilorDelegateTest() = default;
+MirrorLandingAccountReconcilorDelegateTest::
+ ~MirrorLandingAccountReconcilorDelegateTest() = default;
+
+TEST_F(MirrorLandingAccountReconcilorDelegateTest,
+ GetChromeAccountsForReconcile) {
CoreAccountId kPrimaryAccountId = CoreAccountId::FromGaiaId("primary");
CoreAccountId kOtherAccountId1 = CoreAccountId::FromGaiaId("1");
CoreAccountId kOtherAccountId2 = CoreAccountId::FromGaiaId("2");
@@ -60,11 +88,14 @@ TEST(MirrorLandingAccountReconcilorDelegateTest,
gaia::ListedAccount gaia_account_1 = BuildListedAccount("1");
gaia::ListedAccount gaia_account_2 = BuildListedAccount("2");
gaia::ListedAccount gaia_account_3 = BuildListedAccount("3");
- MirrorLandingAccountReconcilorDelegate delegate;
+
+ std::unique_ptr<MirrorLandingAccountReconcilorDelegate> delegate =
+ CreateMirrorLandingAccountReconcilorDelegate(/*is_main_profile=*/false);
+
// No primary account. Gaia accounts are removed.
EXPECT_TRUE(
delegate
- .GetChromeAccountsForReconcile(
+ ->GetChromeAccountsForReconcile(
/*chrome_accounts=*/{},
/*primary_account=*/CoreAccountId(),
/*gaia_accounts=*/{gaia_account_1, gaia_account_2},
@@ -74,7 +105,7 @@ TEST(MirrorLandingAccountReconcilorDelegateTest,
.empty());
// With primary account. Primary is moved in front, account 1 is kept in the
// same slot, account 2 is added, account 3 is removed.
- EXPECT_EQ(delegate.GetChromeAccountsForReconcile(
+ EXPECT_EQ(delegate->GetChromeAccountsForReconcile(
/*chrome_accounts=*/{kOtherAccountId1, kOtherAccountId2,
kPrimaryAccountId},
/*primary_account=*/kPrimaryAccountId,
@@ -85,6 +116,57 @@ TEST(MirrorLandingAccountReconcilorDelegateTest,
gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER),
(std::vector<CoreAccountId>{kPrimaryAccountId, kOtherAccountId2,
kOtherAccountId1}));
+ // Primary account error causes a logout.
+ EXPECT_TRUE(
+ delegate
+ ->GetChromeAccountsForReconcile(
+ /*chrome_accounts=*/{kPrimaryAccountId, kOtherAccountId1},
+ /*primary_account=*/kPrimaryAccountId,
+ /*gaia_accounts=*/{gaia_account_primary, gaia_account_1},
+ /*first_execution=*/true,
+ /*primary_has_error=*/true,
+ gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER)
+ .empty());
+}
+
+TEST_F(MirrorLandingAccountReconcilorDelegateTest,
+ DeleteCookieSecondaryNonSyncingProfile) {
+ AddPrimaryAndSecondaryAccounts(identity_test_env(), ConsentLevel::kSignin);
+
+ CreateMirrorLandingAccountReconcilorDelegate(/*is_main_profile=*/false)
+ ->OnAccountsCookieDeletedByUserAction(
+ /*synced_data_deletion_in_progress=*/false);
+ EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty());
+}
+
+// Tests that delete cookies in syncing profile does nothing.
+TEST_F(MirrorLandingAccountReconcilorDelegateTest, DeleteCookieSyncingProfile) {
+ AddPrimaryAndSecondaryAccounts(identity_test_env(), ConsentLevel::kSync);
+
+ CreateMirrorLandingAccountReconcilorDelegate(/*is_main_profile=*/false)
+ ->OnAccountsCookieDeletedByUserAction(
+ /*synced_data_deletion_in_progress=*/false);
+
+ // No account has been removed.
+ EXPECT_EQ(2U, identity_manager()->GetAccountsWithRefreshTokens().size());
+ EXPECT_FALSE(
+ identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState(
+ CoreAccountId::FromGaiaId("primary")));
+}
+
+// Tests that delete cookies in main profile does nothing
+TEST_F(MirrorLandingAccountReconcilorDelegateTest, DeleteCookieMainProfile) {
+ AddPrimaryAndSecondaryAccounts(identity_test_env(), ConsentLevel::kSignin);
+
+ CreateMirrorLandingAccountReconcilorDelegate(/*is_main_profile=*/true)
+ ->OnAccountsCookieDeletedByUserAction(
+ /*synced_data_deletion_in_progress=*/false);
+
+ // No account has been removed.
+ EXPECT_EQ(2U, identity_manager()->GetAccountsWithRefreshTokens().size());
+ EXPECT_FALSE(
+ identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState(
+ CoreAccountId::FromGaiaId("primary")));
}
} // namespace signin
diff --git a/chromium/components/signin/core/browser/signin_header_helper.cc b/chromium/components/signin/core/browser/signin_header_helper.cc
index 6cfc6255e81..477fc6780ee 100644
--- a/chromium/components/signin/core/browser/signin_header_helper.cc
+++ b/chromium/components/signin/core/browser/signin_header_helper.cc
@@ -9,12 +9,12 @@
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
+#include "base/strings/escape.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "components/google/core/common/google_util.h"
#include "components/signin/core/browser/chrome_connected_header_helper.h"
#include "google_apis/gaia/gaia_auth_util.h"
-#include "net/base/escape.h"
#include "net/http/http_request_headers.h"
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -152,12 +152,12 @@ SigninHeaderHelper::ParseAccountConsistencyResponseHeader(
DLOG(WARNING) << "Unexpected Gaia header field '" << field << "'.";
continue;
}
- dictionary.insert(
- {std::string(field.substr(0, delim)),
- net::UnescapeURLComponent(
- field.substr(delim + 1),
- net::UnescapeRule::PATH_SEPARATORS |
- net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)});
+ dictionary.insert({std::string(field.substr(0, delim)),
+ base::UnescapeURLComponent(
+ field.substr(delim + 1),
+ base::UnescapeRule::PATH_SEPARATORS |
+ base::UnescapeRule::
+ URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)});
}
return dictionary;
}
diff --git a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
index 3884e498914..0953cdca7f2 100644
--- a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
+++ b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
@@ -182,7 +182,7 @@ class SigninHeaderHelperTest : public testing::Test {
scoped_refptr<content_settings::CookieSettings> cookie_settings_;
};
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
// Tests that Mirror request is returned on Chrome OS for Public Sessions (no
// account id).
TEST_F(SigninHeaderHelperTest, TestMirrorRequestNoAccountIdChromeOS) {
@@ -198,7 +198,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestNoAccountIdChromeOS) {
"consistency_enabled_by_default=" +
consistency_enabled_by_default_value());
}
-#else // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#else // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_ANDROID)
// Tests that eligible_for_consistency request is returned on Android
// when reaching to Gaia origin and there's no primary account.
diff --git a/chromium/components/signin/core/browser/signin_internals_util.h b/chromium/components/signin/core/browser/signin_internals_util.h
index 09e80e87532..9851505e41a 100644
--- a/chromium/components/signin/core/browser/signin_internals_util.h
+++ b/chromium/components/signin/core/browser/signin_internals_util.h
@@ -18,11 +18,9 @@ namespace signin_internals_util {
extern const char kSigninPrefPrefix[];
extern const char kTokenPrefPrefix[];
-// Helper enums to access fields from SigninStatus (declared below).
-enum {
- SIGNIN_FIELDS_BEGIN = 0,
- UNTIMED_FIELDS_BEGIN_UNTYPED = SIGNIN_FIELDS_BEGIN
-};
+// Helper constants to access fields from SigninStatus (declared below).
+constexpr int SIGNIN_FIELDS_BEGIN = 0;
+constexpr int UNTIMED_FIELDS_BEGIN_UNTYPED = SIGNIN_FIELDS_BEGIN;
enum UntimedSigninStatusField {
UNTIMED_FIELDS_BEGIN = UNTIMED_FIELDS_BEGIN_UNTYPED,
@@ -32,10 +30,8 @@ enum UntimedSigninStatusField {
UNTIMED_FIELDS_END
};
-enum {
- UNTIMED_FIELDS_COUNT = UNTIMED_FIELDS_END - UNTIMED_FIELDS_BEGIN,
- TIMED_FIELDS_BEGIN_UNTYPED = UNTIMED_FIELDS_END
-};
+constexpr int UNTIMED_FIELDS_COUNT = UNTIMED_FIELDS_END - UNTIMED_FIELDS_BEGIN;
+constexpr int TIMED_FIELDS_BEGIN_UNTYPED = UNTIMED_FIELDS_END;
enum TimedSigninStatusField {
TIMED_FIELDS_BEGIN = TIMED_FIELDS_BEGIN_UNTYPED,
@@ -44,11 +40,9 @@ enum TimedSigninStatusField {
TIMED_FIELDS_END
};
-enum {
- TIMED_FIELDS_COUNT = TIMED_FIELDS_END - TIMED_FIELDS_BEGIN,
- SIGNIN_FIELDS_END = TIMED_FIELDS_END,
- SIGNIN_FIELDS_COUNT = SIGNIN_FIELDS_END - SIGNIN_FIELDS_BEGIN
-};
+constexpr int TIMED_FIELDS_COUNT = TIMED_FIELDS_END - TIMED_FIELDS_BEGIN;
+constexpr int SIGNIN_FIELDS_END = TIMED_FIELDS_END;
+constexpr int SIGNIN_FIELDS_COUNT = SIGNIN_FIELDS_END - SIGNIN_FIELDS_BEGIN;
// Returns the name of a SigninStatus field.
std::string SigninStatusFieldToString(UntimedSigninStatusField field);
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider.cc b/chromium/components/signin/core/browser/signin_status_metrics_provider.cc
deleted file mode 100644
index 062d35a8640..00000000000
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider.cc
+++ /dev/null
@@ -1,161 +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.
-
-#include "components/signin/core/browser/signin_status_metrics_provider.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
-#include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-
-SigninStatusMetricsProvider::SigninStatusMetricsProvider(
- std::unique_ptr<SigninStatusMetricsProviderDelegate> delegate,
- bool is_test)
- : delegate_(std::move(delegate)),
- is_test_(is_test) {
- DCHECK(delegate_ || is_test_);
- if (is_test_)
- return;
-
- delegate_->SetOwner(this);
-
- // Postpone the initialization until all threads are created.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&SigninStatusMetricsProvider::Initialize,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-SigninStatusMetricsProvider::~SigninStatusMetricsProvider() {}
-
-void SigninStatusMetricsProvider::ProvideCurrentSessionData(
- metrics::ChromeUserMetricsExtension* uma_proto) {
- RecordSigninStatusHistogram(signin_status());
- // After a histogram value is recorded, a new UMA session will be started, so
- // we need to re-check the current sign-in status regardless of the previous
- // recorded |signin_status_| value.
- ResetSigninStatus();
- ComputeCurrentSigninStatus();
-}
-
-// static
-std::unique_ptr<SigninStatusMetricsProvider>
-SigninStatusMetricsProvider::CreateInstance(
- std::unique_ptr<SigninStatusMetricsProviderDelegate> delegate) {
- return base::WrapUnique(
- new SigninStatusMetricsProvider(std::move(delegate), false));
-}
-
-void SigninStatusMetricsProvider::OnIdentityManagerCreated(
- signin::IdentityManager* identity_manager) {
- // Whenever a new profile is created, a new IdentityManager will be created
- // for it. This ensures that all sign-in or sign-out actions of all opened
- // profiles are being monitored.
- scoped_observations_.AddObservation(identity_manager);
-
- // If the status is unknown, it means this is the first created
- // IdentityManager and the corresponding profile should be the only opened
- // profile.
- if (signin_status() == UNKNOWN_SIGNIN_STATUS) {
- size_t signed_in_count =
- identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync) ? 1
- : 0;
- UpdateInitialSigninStatus(1, signed_in_count);
- }
-}
-
-void SigninStatusMetricsProvider::OnIdentityManagerShutdown(
- signin::IdentityManager* identity_manager) {
- if (scoped_observations_.IsObservingSource(identity_manager))
- scoped_observations_.RemoveObservation(identity_manager);
-}
-
-void SigninStatusMetricsProvider::OnPrimaryAccountChanged(
- const signin::PrimaryAccountChangeEvent& event) {
- if (event.GetEventTypeFor(signin::ConsentLevel::kSync) ==
- signin::PrimaryAccountChangeEvent::Type::kNone) {
- return;
- }
-
- SigninStatus recorded_signin_status = signin_status();
- switch (event.GetEventTypeFor(signin::ConsentLevel::kSync)) {
- case signin::PrimaryAccountChangeEvent::Type::kSet:
- if (recorded_signin_status == ALL_PROFILES_NOT_SIGNED_IN)
- UpdateSigninStatus(MIXED_SIGNIN_STATUS);
- break;
- case signin::PrimaryAccountChangeEvent::Type::kCleared:
- if (recorded_signin_status == ALL_PROFILES_SIGNED_IN)
- UpdateSigninStatus(MIXED_SIGNIN_STATUS);
- break;
- case signin::PrimaryAccountChangeEvent::Type::kNone:
- NOTREACHED() << "See return statement above";
- break;
- }
- if (recorded_signin_status == UNKNOWN_SIGNIN_STATUS) {
- // There should have at least one browser opened if the user can sign in or
- // out, so signin_status_ value should not be unknown.
- UpdateSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
- }
-}
-
-void SigninStatusMetricsProvider::Initialize() {
- delegate_->Initialize();
-
- // Start observing all already-created IdentityManagers.
- for (signin::IdentityManager* manager :
- delegate_->GetIdentityManagersForAllAccounts()) {
- DCHECK(!scoped_observations_.IsObservingSource(manager));
- scoped_observations_.AddObservation(manager);
- }
-
- // It is possible that when this object is created, no IdentityManager is
- // created yet, for example, when Chrome is opened for the first time after
- // installation on desktop, or when Chrome on Android is loaded into memory.
- if (delegate_->GetStatusOfAllAccounts().num_accounts == 0) {
- UpdateSigninStatus(UNKNOWN_SIGNIN_STATUS);
- } else {
- ComputeCurrentSigninStatus();
- }
-}
-
-void SigninStatusMetricsProvider::UpdateInitialSigninStatus(
- size_t total_count,
- size_t signed_in_profiles_count) {
- // total_count is known to be bigger than 0.
- if (signed_in_profiles_count == 0) {
- UpdateSigninStatus(ALL_PROFILES_NOT_SIGNED_IN);
- } else if (total_count == signed_in_profiles_count) {
- UpdateSigninStatus(ALL_PROFILES_SIGNED_IN);
- } else {
- UpdateSigninStatus(MIXED_SIGNIN_STATUS);
- }
-}
-
-void SigninStatusMetricsProvider::ComputeCurrentSigninStatus() {
- AccountsStatus accounts_status = delegate_->GetStatusOfAllAccounts();
- if (accounts_status.num_accounts == 0) {
- UpdateSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
- } else if (accounts_status.num_opened_accounts == 0) {
- // The code indicates that Chrome is running in the background but no
- // browser window is opened.
- UpdateSigninStatus(UNKNOWN_SIGNIN_STATUS);
- } else {
- UpdateInitialSigninStatus(accounts_status.num_opened_accounts,
- accounts_status.num_signed_in_accounts);
- }
-}
-
-void SigninStatusMetricsProvider::UpdateInitialSigninStatusForTesting(
- size_t total_count,
- size_t signed_in_profiles_count) {
- UpdateInitialSigninStatus(total_count, signed_in_profiles_count);
-}
-
-SigninStatusMetricsProvider::SigninStatus
-SigninStatusMetricsProvider::GetSigninStatusForTesting() {
- return signin_status();
-}
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider.h b/chromium/components/signin/core/browser/signin_status_metrics_provider.h
deleted file mode 100644
index 4cb56338c08..00000000000
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider.h
+++ /dev/null
@@ -1,104 +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.
-
-#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_H_
-#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_H_
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/gtest_prod_util.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_multi_source_observation.h"
-#include "build/build_config.h"
-#include "components/signin/core/browser/signin_status_metrics_provider_base.h"
-#include "components/signin/core/browser/signin_status_metrics_provider_delegate.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-
-namespace metrics {
-class ChromeUserMetricsExtension;
-}
-
-class SigninStatusMetricsProviderDelegate;
-
-// Responsible for sign-in status metrics on Windows, Mac, Linux, Android, and
-// iOS. See SigninStatusMetricsProviderChromeOS for ChromeOS-specific support.
-class SigninStatusMetricsProvider : public SigninStatusMetricsProviderBase,
- public signin::IdentityManager::Observer {
- public:
- SigninStatusMetricsProvider(const SigninStatusMetricsProvider&) = delete;
- SigninStatusMetricsProvider& operator=(const SigninStatusMetricsProvider&) =
- delete;
-
- ~SigninStatusMetricsProvider() override;
-
- // SigninStatusMetricsProviderBase:
- void ProvideCurrentSessionData(
- metrics::ChromeUserMetricsExtension* uma_proto) override;
-
- // Factory method, creates a new instance of this class.
- static std::unique_ptr<SigninStatusMetricsProvider> CreateInstance(
- std::unique_ptr<SigninStatusMetricsProviderDelegate> delegate);
-
- // Update the sign-in status when a IdentityManager is created.
- void OnIdentityManagerCreated(signin::IdentityManager* identity_manager);
-
- // Updates the initial sign-in status. For testing purpose only.
- void UpdateInitialSigninStatusForTesting(size_t total_count,
- size_t signed_in_count);
-
- // Get the current recorded sign-in status. For testing purpose only.
- SigninStatus GetSigninStatusForTesting();
-
- private:
- FRIEND_TEST_ALL_PREFIXES(SigninStatusMetricsProviderTest,
- UpdateInitialSigninStatus);
- FRIEND_TEST_ALL_PREFIXES(SigninStatusMetricsProviderTest,
- OnPrimaryAccountSet);
- FRIEND_TEST_ALL_PREFIXES(SigninStatusMetricsProviderTest,
- OnPrimaryAccountCleared);
-
- // The boolean |is_test| indicates whether or not this is an instance for
- // testing purpose. If so, skip the initialization. Except for testing
- // purpose, this class's instance should be created through the static
- // CreateInstance() method.
- SigninStatusMetricsProvider(
- std::unique_ptr<SigninStatusMetricsProviderDelegate> delegate,
- bool is_test);
-
- // IdentityManager::Observer:
- void OnPrimaryAccountChanged(
- const signin::PrimaryAccountChangeEvent& event) override;
- void OnIdentityManagerShutdown(
- signin::IdentityManager* identity_manager) override;
-
- // Obtain sign-in status and add observers.
- void Initialize();
-
- // Update the sign-in status based on all currently opened profiles. Called by
- // ComputeCurrentSigninStatus at class construction and right after each UMA
- // log upload. |total_count| is the number of opened profiles and
- // |signed_in_count| represents the number of signed-in profiles among those
- // |total_count| profiles.
- void UpdateInitialSigninStatus(size_t total_count, size_t signed_in_count);
-
- // Compute current sign-in status of all opened profiles.
- void ComputeCurrentSigninStatus();
-
- std::unique_ptr<SigninStatusMetricsProviderDelegate> delegate_;
-
- // Used to track the IdentityManagers that this instance is observing so that
- // this instance can be removed as an observer on its destruction.
- base::ScopedMultiSourceObservation<signin::IdentityManager,
- signin::IdentityManager::Observer>
- scoped_observations_{this};
-
- // Whether the instance is for testing or not.
- bool is_test_;
-
- base::WeakPtrFactory<SigninStatusMetricsProvider> weak_ptr_factory_{this};
-};
-
-#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_H_
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_base.cc b/chromium/components/signin/core/browser/signin_status_metrics_provider_base.cc
deleted file mode 100644
index 3ef29c9c0f3..00000000000
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider_base.cc
+++ /dev/null
@@ -1,31 +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.
-
-#include "components/signin/core/browser/signin_status_metrics_provider_base.h"
-
-#include "base/metrics/histogram_macros.h"
-
-SigninStatusMetricsProviderBase::SigninStatusMetricsProviderBase()
- : signin_status_(UNKNOWN_SIGNIN_STATUS) {}
-
-SigninStatusMetricsProviderBase::~SigninStatusMetricsProviderBase() {}
-
-void SigninStatusMetricsProviderBase::RecordSigninStatusHistogram(
- SigninStatus signin_status) {
- UMA_HISTOGRAM_ENUMERATION("UMA.ProfileSignInStatus", signin_status,
- SIGNIN_STATUS_MAX);
-}
-
-void SigninStatusMetricsProviderBase::UpdateSigninStatus(
- SigninStatus new_status) {
- // The recorded sign-in status value can't be changed once it's recorded as
- // error until the next UMA upload.
- if (signin_status_ == ERROR_GETTING_SIGNIN_STATUS)
- return;
- signin_status_ = new_status;
-}
-
-void SigninStatusMetricsProviderBase::ResetSigninStatus() {
- signin_status_ = UNKNOWN_SIGNIN_STATUS;
-}
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_base.h b/chromium/components/signin/core/browser/signin_status_metrics_provider_base.h
deleted file mode 100644
index e56d5f5c1b7..00000000000
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider_base.h
+++ /dev/null
@@ -1,60 +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.
-
-#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_BASE_H_
-#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_BASE_H_
-
-#include "components/metrics/metrics_provider.h"
-
-// The base class for collecting the sign-in status of all opened profiles
-// during one UMA session and recording the status in a histogram before the UMA
-// log is uploaded.
-class SigninStatusMetricsProviderBase : public metrics::MetricsProvider {
- public:
- SigninStatusMetricsProviderBase();
-
- SigninStatusMetricsProviderBase(const SigninStatusMetricsProviderBase&) =
- delete;
- SigninStatusMetricsProviderBase& operator=(
- const SigninStatusMetricsProviderBase&) = delete;
-
- ~SigninStatusMetricsProviderBase() override;
-
- // Possible sign-in status of all opened profiles during one UMA session. For
- // MIXED_SIGNIN_STATUS, at least one signed-in profile and at least one
- // unsigned-in profile were opened between two UMA log uploads. Some statuses
- // are not applicable to all platforms.
- //
- // These values are persisted to logs. Entries should not be renumbered and
- // numeric values should never be reused.
- enum SigninStatus {
- ALL_PROFILES_SIGNED_IN,
- ALL_PROFILES_NOT_SIGNED_IN,
- MIXED_SIGNIN_STATUS,
- UNKNOWN_SIGNIN_STATUS,
- ERROR_GETTING_SIGNIN_STATUS,
- SIGNIN_STATUS_MAX,
- };
-
- // Sets the value of |signin_status_|. It ensures that |signin_status_| will
- // not be changed if its value is already ERROR_GETTING_SIGNIN_STATUS.
- void UpdateSigninStatus(SigninStatus new_status);
-
- SigninStatus signin_status() const { return signin_status_; }
-
- protected:
- // Record the sign in status into the proper histogram bucket. This should be
- // called exactly once for each UMA session.
- void RecordSigninStatusHistogram(SigninStatus signin_status);
-
- // Resets the value of |signin_status_| to be UNKNOWN_SIGNIN_STATUS regardless
- // of its current value;
- void ResetSigninStatus();
-
- private:
- // Sign-in status of all profiles seen so far.
- SigninStatus signin_status_;
-};
-
-#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_BASE_H_
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.cc b/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.cc
deleted file mode 100644
index cf31124f2ef..00000000000
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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.
-
-#include "components/signin/core/browser/signin_status_metrics_provider_delegate.h"
-
-#include "base/check.h"
-#include "components/signin/core/browser/signin_status_metrics_provider.h"
-
-AccountsStatus::AccountsStatus()
- : num_accounts(0), num_opened_accounts(0), num_signed_in_accounts(0) {}
-
-SigninStatusMetricsProviderDelegate::SigninStatusMetricsProviderDelegate()
- : owner_(nullptr) {}
-
-SigninStatusMetricsProviderDelegate::~SigninStatusMetricsProviderDelegate() {}
-
-void SigninStatusMetricsProviderDelegate::SetOwner(
- SigninStatusMetricsProvider* owner) {
- DCHECK(owner);
- DCHECK(!owner_);
- owner_ = owner;
-}
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.h b/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.h
deleted file mode 100644
index 198fb8f0853..00000000000
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_DELEGATE_H_
-#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_DELEGATE_H_
-
-#include <stddef.h>
-
-#include <vector>
-
-#include "base/memory/raw_ptr.h"
-#include "build/build_config.h"
-
-class SigninStatusMetricsProvider;
-
-namespace signin {
-class IdentityManager;
-}
-
-// Provides information relating to the status of accounts in the embedder: how
-// many there are, how many are open, and how many are signed in. Note that
-// "open" is an embedder-defined concept; in some embedders, all accounts are
-// open.
-struct AccountsStatus {
- AccountsStatus();
-
- size_t num_accounts;
- size_t num_opened_accounts;
- size_t num_signed_in_accounts;
-};
-
-// Delegate for SigninStatusMetricsProvider to abstract dependencies on
-// embedder.
-class SigninStatusMetricsProviderDelegate {
- public:
- SigninStatusMetricsProviderDelegate();
-
- SigninStatusMetricsProviderDelegate(
- const SigninStatusMetricsProviderDelegate&) = delete;
- SigninStatusMetricsProviderDelegate& operator=(
- const SigninStatusMetricsProviderDelegate&) = delete;
-
- virtual ~SigninStatusMetricsProviderDelegate();
-
- // Set the |owner_| field to the owning SigninStatusMetricsProvider.
- void SetOwner(SigninStatusMetricsProvider* owner);
-
- // Initializes the instance. SetOwner() must have been called before this
- // method.
- virtual void Initialize() = 0;
-
- // Returns the status of all accounts.
- virtual AccountsStatus GetStatusOfAllAccounts() = 0;
-
- // Returns the IdentityManager instance (if any) associated with each account.
- virtual std::vector<signin::IdentityManager*>
- GetIdentityManagersForAllAccounts() = 0;
-
- protected:
- SigninStatusMetricsProvider* owner() { return owner_; }
-
- private:
- raw_ptr<SigninStatusMetricsProvider> owner_;
-};
-
-#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_DELEGATE_H_
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_helpers.cc b/chromium/components/signin/core/browser/signin_status_metrics_provider_helpers.cc
new file mode 100644
index 00000000000..3b2f64e6d87
--- /dev/null
+++ b/chromium/components/signin/core/browser/signin_status_metrics_provider_helpers.cc
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium 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/signin/core/browser/signin_status_metrics_provider_helpers.h"
+
+#include "base/metrics/histogram_functions.h"
+
+namespace {
+
+using signin_metrics::SigninOrSyncStatus;
+
+// Returns the appropriate status if there are |opened_profiles| total,
+// of which |profiles_in_state| are in the state.
+SigninOrSyncStatus GetStatus(size_t opened_profiles, size_t profiles_in_state) {
+ if (opened_profiles == 0)
+ return SigninOrSyncStatus::kUnknown;
+ if (opened_profiles == profiles_in_state)
+ return SigninOrSyncStatus::kAllProfiles;
+ if (profiles_in_state == 0)
+ return SigninOrSyncStatus::kNoProfiles;
+ return SigninOrSyncStatus::kMixedProfiles;
+}
+
+} // anonymous namespace
+
+namespace signin_metrics {
+
+void EmitHistograms(const ProfilesStatus& profiles_status) {
+ base::UmaHistogramEnumeration(
+ "UMA.ProfileSignInStatusV2",
+ GetStatus(profiles_status.num_opened_profiles,
+ profiles_status.num_signed_in_profiles));
+ base::UmaHistogramEnumeration(
+ "UMA.ProfileSyncStatusV2",
+ GetStatus(profiles_status.num_opened_profiles,
+ profiles_status.num_syncing_profiles));
+}
+
+void UpdateProfilesStatusBasedOnSignInAndSyncStatus(
+ ProfilesStatus& profiles_status,
+ bool signed_in,
+ bool syncing) {
+ profiles_status.num_opened_profiles++;
+ if (signed_in)
+ profiles_status.num_signed_in_profiles++;
+ if (syncing)
+ profiles_status.num_syncing_profiles++;
+}
+
+} // namespace signin_metrics
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_helpers.h b/chromium/components/signin/core/browser/signin_status_metrics_provider_helpers.h
new file mode 100644
index 00000000000..7c2513148b9
--- /dev/null
+++ b/chromium/components/signin/core/browser/signin_status_metrics_provider_helpers.h
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_HELPERS_H_
+#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_HELPERS_H_
+
+#include <cstddef>
+
+// Structures and common code used to regularly emit metrics about sign-in and
+// sync status of all opened profiles.
+namespace signin_metrics {
+
+// Possible sign-in or sync status of all profiles open during one snapshot.
+// For kMixedProfiles, at least one signed-in profile and at least one
+// signed-out profile were open. Some statuses are not applicable to all
+// platforms.
+//
+// This is only made visible for testing.
+//
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class SigninOrSyncStatus {
+ kAllProfiles = 0,
+ kNoProfiles = 1,
+ kMixedProfiles = 2,
+ kUnknown = 3,
+ kMaxValue = kUnknown,
+};
+
+// Provides information relating to the status of profiles in the embedder:
+// how many are open, how many are signed in, and how many are syncing.
+struct ProfilesStatus {
+ size_t num_opened_profiles = 0;
+ size_t num_signed_in_profiles = 0;
+ size_t num_syncing_profiles = 0;
+};
+
+// Uses |profiles_status| to identify whether all, some, or no profiles are
+// signed-in and emits that to the appropriate histogram. Does the same with
+// sync status to the sync histogram.
+void EmitHistograms(const ProfilesStatus& profiles_status);
+
+// Updates |profiles_status| to incorporate another opened profiles that is
+// |signed_in| and |syncing|.
+void UpdateProfilesStatusBasedOnSignInAndSyncStatus(
+ ProfilesStatus& profiles_status,
+ bool signed_in,
+ bool syncing);
+
+} // namespace signin_metrics
+
+#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_STATUS_METRICS_PROVIDER_HELPERS_H_
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_helpers_unittest.cc b/chromium/components/signin/core/browser/signin_status_metrics_provider_helpers_unittest.cc
new file mode 100644
index 00000000000..edd45a4875f
--- /dev/null
+++ b/chromium/components/signin/core/browser/signin_status_metrics_provider_helpers_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2022 The Chromium 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/signin/core/browser/signin_status_metrics_provider_helpers.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+signin_metrics::ProfilesStatus MakeProfilesStatus(size_t num_opened_profiles,
+ size_t num_signed_in_profiles,
+ size_t num_syncing_profiles) {
+ return {.num_opened_profiles = num_opened_profiles,
+ .num_signed_in_profiles = num_signed_in_profiles,
+ .num_syncing_profiles = num_syncing_profiles};
+}
+
+} // anonymous namespace
+
+using signin_metrics::SigninOrSyncStatus;
+
+struct TestItem {
+ size_t num_opened_profiles;
+ size_t num_signed_in_profiles;
+ size_t num_syncing_profiles;
+ SigninOrSyncStatus expected_signin_status_bucket;
+ SigninOrSyncStatus expected_sync_status_bucket;
+};
+
+class SigninAndSyncStatusMetricsProviderHelpersTest
+ : public ::testing::TestWithParam<TestItem> {};
+
+TEST_P(SigninAndSyncStatusMetricsProviderHelpersTest, HistogramEmit) {
+ auto item = GetParam();
+ auto profiles_status =
+ MakeProfilesStatus(item.num_opened_profiles, item.num_signed_in_profiles,
+ item.num_syncing_profiles);
+ base::HistogramTester histogram_tester;
+ signin_metrics::EmitHistograms(profiles_status);
+ histogram_tester.ExpectUniqueSample("UMA.ProfileSignInStatusV2",
+ item.expected_signin_status_bucket, 1);
+ histogram_tester.ExpectUniqueSample("UMA.ProfileSyncStatusV2",
+ item.expected_sync_status_bucket, 1);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ SigninAndSyncStatusMetricsProviderHelpersTest_HistogramEmit,
+ SigninAndSyncStatusMetricsProviderHelpersTest,
+ ::testing::Values(TestItem{0, 0, 0, SigninOrSyncStatus::kUnknown,
+ SigninOrSyncStatus::kUnknown},
+ TestItem{1, 0, 0, SigninOrSyncStatus::kNoProfiles,
+ SigninOrSyncStatus::kNoProfiles},
+ TestItem{1, 1, 0, SigninOrSyncStatus::kAllProfiles,
+ SigninOrSyncStatus::kNoProfiles},
+ TestItem{1, 0, 1, SigninOrSyncStatus::kNoProfiles,
+ SigninOrSyncStatus::kAllProfiles},
+ TestItem{1, 1, 1, SigninOrSyncStatus::kAllProfiles,
+ SigninOrSyncStatus::kAllProfiles},
+ TestItem{2, 0, 0, SigninOrSyncStatus::kNoProfiles,
+ SigninOrSyncStatus::kNoProfiles},
+ TestItem{2, 0, 1, SigninOrSyncStatus::kNoProfiles,
+ SigninOrSyncStatus::kMixedProfiles},
+ TestItem{2, 0, 2, SigninOrSyncStatus::kNoProfiles,
+ SigninOrSyncStatus::kAllProfiles},
+ TestItem{2, 1, 0, SigninOrSyncStatus::kMixedProfiles,
+ SigninOrSyncStatus::kNoProfiles},
+ TestItem{2, 1, 1, SigninOrSyncStatus::kMixedProfiles,
+ SigninOrSyncStatus::kMixedProfiles},
+ TestItem{2, 1, 2, SigninOrSyncStatus::kMixedProfiles,
+ SigninOrSyncStatus::kAllProfiles},
+ TestItem{2, 2, 0, SigninOrSyncStatus::kAllProfiles,
+ SigninOrSyncStatus::kNoProfiles},
+ TestItem{2, 2, 1, SigninOrSyncStatus::kAllProfiles,
+ SigninOrSyncStatus::kMixedProfiles},
+ TestItem{2, 2, 2, SigninOrSyncStatus::kAllProfiles,
+ SigninOrSyncStatus::kAllProfiles}));
diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_unittest.cc b/chromium/components/signin/core/browser/signin_status_metrics_provider_unittest.cc
deleted file mode 100644
index 832813dabfa..00000000000
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider_unittest.cc
+++ /dev/null
@@ -1,80 +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.
-
-#include "components/signin/core/browser/signin_status_metrics_provider.h"
-#include "components/signin/public/identity_manager/identity_test_utils.h"
-
-#include <string>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-using signin::PrimaryAccountChangeEvent;
-
-CoreAccountInfo TestAccount() {
- CoreAccountInfo account;
- account.email = "test@gmail.com";
- account.gaia = signin::GetTestGaiaIdForEmail(account.email);
- account.account_id = CoreAccountId::FromEmail(account.email);
- return account;
-}
-
-TEST(SigninStatusMetricsProviderTest, UpdateInitialSigninStatus) {
- SigninStatusMetricsProvider metrics_provider(nullptr, true);
-
- metrics_provider.UpdateInitialSigninStatus(2, 2);
- EXPECT_EQ(SigninStatusMetricsProviderBase::ALL_PROFILES_SIGNED_IN,
- metrics_provider.GetSigninStatusForTesting());
- metrics_provider.UpdateInitialSigninStatus(2, 0);
- EXPECT_EQ(SigninStatusMetricsProviderBase::ALL_PROFILES_NOT_SIGNED_IN,
- metrics_provider.GetSigninStatusForTesting());
- metrics_provider.UpdateInitialSigninStatus(2, 1);
- EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
- metrics_provider.GetSigninStatusForTesting());
-}
-
-TEST(SigninStatusMetricsProviderTest, OnPrimaryAccountSet) {
- SigninStatusMetricsProvider metrics_provider(nullptr, true);
- CoreAccountInfo account_info = TestAccount();
-
- // Initial status is all signed out and then one of the profiles is signed in.
- metrics_provider.UpdateInitialSigninStatus(2, 0);
- metrics_provider.OnPrimaryAccountChanged(PrimaryAccountChangeEvent(
- PrimaryAccountChangeEvent::State(),
- PrimaryAccountChangeEvent::State(account_info,
- signin::ConsentLevel::kSync)));
- EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
- metrics_provider.GetSigninStatusForTesting());
-
- // Initial status is mixed and then one of the profiles is signed in.
- metrics_provider.UpdateInitialSigninStatus(2, 1);
- metrics_provider.OnPrimaryAccountChanged(PrimaryAccountChangeEvent(
- PrimaryAccountChangeEvent::State(),
- PrimaryAccountChangeEvent::State(account_info,
- signin::ConsentLevel::kSync)));
- EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
- metrics_provider.GetSigninStatusForTesting());
-}
-
-TEST(SigninStatusMetricsProviderTest, OnPrimaryAccountCleared) {
- SigninStatusMetricsProvider metrics_provider(nullptr, true);
- CoreAccountInfo account_info = TestAccount();
-
- // Initial status is all signed in and then one of the profiles is signed out.
- metrics_provider.UpdateInitialSigninStatus(2, 2);
- metrics_provider.OnPrimaryAccountChanged(
- PrimaryAccountChangeEvent(PrimaryAccountChangeEvent::State(
- account_info, signin::ConsentLevel::kSync),
- PrimaryAccountChangeEvent::State()));
- EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
- metrics_provider.GetSigninStatusForTesting());
-
- // Initial status is mixed and then one of the profiles is signed out.
- metrics_provider.UpdateInitialSigninStatus(2, 1);
- metrics_provider.OnPrimaryAccountChanged(
- PrimaryAccountChangeEvent(PrimaryAccountChangeEvent::State(
- account_info, signin::ConsentLevel::kSync),
- PrimaryAccountChangeEvent::State()));
- EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
- metrics_provider.GetSigninStatusForTesting());
-}
diff --git a/chromium/components/signin/internal/identity_manager/BUILD.gn b/chromium/components/signin/internal/identity_manager/BUILD.gn
index 5a0f4c49dcc..011ec1fa83c 100644
--- a/chromium/components/signin/internal/identity_manager/BUILD.gn
+++ b/chromium/components/signin/internal/identity_manager/BUILD.gn
@@ -108,7 +108,6 @@ source_set("identity_manager") {
if (is_chromeos_ash) {
deps += [ "//ash/constants" ]
- public_deps += [ "//ash/components/account_manager" ]
}
if (!is_android && !is_ios) {
@@ -189,14 +188,14 @@ source_set("unit_tests") {
]
}
- if (is_chromeos_ash) {
+ if (is_chromeos) {
sources += [ "profile_oauth2_token_service_delegate_chromeos_unittest.cc" ]
- deps += [
- "//ash/components/account_manager",
- "//ash/constants",
- "//components/account_manager_core:test_support",
- ]
+ deps += [ "//components/account_manager_core:test_support" ]
+ }
+
+ if (is_chromeos_ash) {
+ deps += [ "//ash/constants" ]
}
if (is_ios) {
diff --git a/chromium/components/signin/internal/identity_manager/account_capabilities_list.h b/chromium/components/signin/internal/identity_manager/account_capabilities_list.h
index 9f243408a2a..ff3f8115535 100644
--- a/chromium/components/signin/internal/identity_manager/account_capabilities_list.h
+++ b/chromium/components/signin/internal/identity_manager/account_capabilities_list.h
@@ -31,3 +31,7 @@ ACCOUNT_CAPABILITY(kCanRunChromePrivacySandboxTrialsCapabilityName,
ACCOUNT_CAPABILITY(kCanStopParentalSupervisionCapabilityName,
CAN_STOP_PARENTAL_SUPERVISION_CAPABILITY_NAME,
"accountcapabilities/guzdslldmfya")
+
+ACCOUNT_CAPABILITY(kCanToggleAutoUpdatesName,
+ CAN_TOGGLE_AUTO_UPDATES_NAME,
+ "accountcapabilities/gu4dmlldmfya")
diff --git a/chromium/components/signin/internal/identity_manager/account_tracker_service.cc b/chromium/components/signin/internal/identity_manager/account_tracker_service.cc
index 49a1dbad395..36708d97860 100644
--- a/chromium/components/signin/internal/identity_manager/account_tracker_service.cc
+++ b/chromium/components/signin/internal/identity_manager/account_tracker_service.cc
@@ -26,6 +26,7 @@
#include "base/threading/scoped_blocking_call.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
+#include "build/buildflag.h"
#include "build/chromeos_buildflags.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/scoped_user_pref_update.h"
@@ -179,8 +180,10 @@ AccountTrackerService::~AccountTrackerService() {
// static
void AccountTrackerService::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterListPref(prefs::kAccountInfo);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
registry->RegisterIntegerPref(prefs::kAccountIdMigrationState,
AccountTrackerService::MIGRATION_NOT_STARTED);
+#endif
}
void AccountTrackerService::Initialize(PrefService* pref_service,
@@ -244,6 +247,7 @@ AccountInfo AccountTrackerService::FindAccountInfoByEmail(
return AccountInfo();
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
AccountTrackerService::AccountIdMigrationState
AccountTrackerService::GetMigrationState() const {
return GetMigrationState(pref_service_);
@@ -252,6 +256,7 @@ AccountTrackerService::GetMigrationState() const {
void AccountTrackerService::SetMigrationDone() {
SetMigrationState(MIGRATION_DONE);
}
+#endif
void AccountTrackerService::NotifyAccountUpdated(
const AccountInfo& account_info) {
@@ -393,6 +398,7 @@ void AccountTrackerService::ResetForTesting() {
Initialize(prefs, base::FilePath());
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
void AccountTrackerService::MigrateToGaiaId() {
DCHECK_EQ(GetMigrationState(), MIGRATION_IN_PROGRESS);
@@ -437,6 +443,7 @@ void AccountTrackerService::MigrateToGaiaId() {
accounts_.erase(account_id);
}
}
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
bool AccountTrackerService::AreAllAccountsMigrated() const {
for (const auto& pair : accounts_) {
@@ -447,6 +454,7 @@ bool AccountTrackerService::AreAllAccountsMigrated() const {
return true;
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
AccountTrackerService::AccountIdMigrationState
AccountTrackerService::ComputeNewMigrationState() const {
if (accounts_.empty()) {
@@ -456,14 +464,12 @@ AccountTrackerService::ComputeNewMigrationState() const {
return MIGRATION_DONE;
}
-#if BUILDFLAG(IS_CHROMEOS_ASH)
// Migration on ChromeOS is not started by default due to the following risks:
// * a lot more data than on desktop is keyed by the account id
// * bugs in the migration flow can lead to user not being able to sign in
// to their device which makes the device unusable.
if (!base::FeatureList::IsEnabled(switches::kAccountIdMigration))
return MIGRATION_NOT_STARTED;
-#endif
bool migration_required = false;
for (const auto& pair : accounts_) {
@@ -490,6 +496,7 @@ AccountTrackerService::GetMigrationState(const PrefService* pref_service) {
return static_cast<AccountTrackerService::AccountIdMigrationState>(
pref_service->GetInteger(prefs::kAccountIdMigrationState));
}
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
base::FilePath AccountTrackerService::GetImagePathFor(
const CoreAccountId& account_id) {
@@ -675,6 +682,7 @@ void AccountTrackerService::LoadFromPrefs() {
RemoveAccountImageFromDisk(account_id);
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
if (GetMigrationState() != MIGRATION_DONE) {
const AccountIdMigrationState new_state = ComputeNewMigrationState();
SetMigrationState(new_state);
@@ -683,10 +691,13 @@ void AccountTrackerService::LoadFromPrefs() {
MigrateToGaiaId();
}
}
-
DCHECK(GetMigrationState() != MIGRATION_DONE || AreAllAccountsMigrated());
+
UMA_HISTOGRAM_ENUMERATION("Signin.AccountTracker.GaiaIdMigrationState",
GetMigrationState(), NUM_MIGRATION_STATES);
+#else
+ DCHECK(AreAllAccountsMigrated());
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
UMA_HISTOGRAM_COUNTS_100("Signin.AccountTracker.CountOfLoadedAccounts",
accounts_.size());
@@ -757,27 +768,23 @@ void AccountTrackerService::RemoveFromPrefs(const AccountInfo& account_info) {
CoreAccountId AccountTrackerService::PickAccountIdForAccount(
const std::string& gaia,
const std::string& email) const {
- return PickAccountIdForAccount(pref_service_, gaia, email);
-}
-
-// static
-CoreAccountId AccountTrackerService::PickAccountIdForAccount(
- const PrefService* pref_service,
- const std::string& gaia,
- const std::string& email) {
- DCHECK(!gaia.empty() ||
- GetMigrationState(pref_service) == MIGRATION_NOT_STARTED);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
DCHECK(!email.empty());
- switch (GetMigrationState(pref_service)) {
+ switch (GetMigrationState(pref_service_)) {
case MIGRATION_NOT_STARTED:
return CoreAccountId::FromEmail(gaia::CanonicalizeEmail(email));
case MIGRATION_IN_PROGRESS:
case MIGRATION_DONE:
+ DCHECK(!gaia.empty());
return CoreAccountId::FromGaiaId(gaia);
default:
NOTREACHED();
return CoreAccountId::FromString(email);
}
+#else
+ DCHECK(!gaia.empty());
+ return CoreAccountId::FromGaiaId(gaia);
+#endif
}
CoreAccountId AccountTrackerService::SeedAccountInfo(const std::string& gaia,
diff --git a/chromium/components/signin/internal/identity_manager/account_tracker_service.h b/chromium/components/signin/internal/identity_manager/account_tracker_service.h
index bf4650caa40..58fe9fc614d 100644
--- a/chromium/components/signin/internal/identity_manager/account_tracker_service.h
+++ b/chromium/components/signin/internal/identity_manager/account_tracker_service.h
@@ -60,6 +60,7 @@ class AccountTrackerService {
typedef base::RepeatingCallback<void(const AccountInfo& info)>
AccountInfoCallback;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
// Possible values for the kAccountIdMigrationState preference.
// Keep in sync with OAuth2LoginAccountRevokedMigrationState histogram enum.
// These values are persisted to logs. Entries should not be renumbered and
@@ -70,6 +71,7 @@ class AccountTrackerService {
MIGRATION_DONE = 2,
NUM_MIGRATION_STATES
};
+#endif
AccountTrackerService();
@@ -97,9 +99,6 @@ class AccountTrackerService {
// migration state.
CoreAccountId PickAccountIdForAccount(const std::string& gaia,
const std::string& email) const;
- static CoreAccountId PickAccountIdForAccount(const PrefService* pref_service,
- const std::string& gaia,
- const std::string& email);
// Seeds the account whose account_id is given by PickAccountIdForAccount()
// with its corresponding gaia id and email address. Returns the same
@@ -122,8 +121,10 @@ class AccountTrackerService {
void RemoveAccount(const CoreAccountId& account_id);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
AccountIdMigrationState GetMigrationState() const;
void SetMigrationDone();
+#endif
#if BUILDFLAG(IS_ANDROID)
// Returns a reference to the corresponding Java AccountTrackerService object.
@@ -208,14 +209,15 @@ class AccountTrackerService {
bool success);
void RemoveAccountImageFromDisk(const CoreAccountId& account_id);
- // Migrate accounts to be keyed by gaia id instead of normalized email.
- // Requires that the migration state is set to MIGRATION_IN_PROGRESS.
- void MigrateToGaiaId();
-
// Returns whether the accounts are all keyed by gaia id. This should
// be the case when the migration state is set to MIGRATION_DONE.
bool AreAllAccountsMigrated() const;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // Migrate accounts to be keyed by gaia id instead of normalized email.
+ // Requires that the migration state is set to MIGRATION_IN_PROGRESS.
+ void MigrateToGaiaId();
+
// Computes the new migration state. The state is saved to preference
// before performing the migration in order to support resuming the
// migration if necessary during the next load.
@@ -227,6 +229,7 @@ class AccountTrackerService {
// Returns the saved migration state in the preferences.
static AccountIdMigrationState GetMigrationState(
const PrefService* pref_service);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
raw_ptr<PrefService> pref_service_ = nullptr; // Not owned.
std::map<CoreAccountId, AccountInfo> accounts_;
diff --git a/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc b/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
index f3e41ad21a6..4a4226c5785 100644
--- a/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
+++ b/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
@@ -9,6 +9,7 @@
#include "base/auto_reset.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/memory/raw_ptr.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
@@ -211,7 +212,8 @@ class AccountTrackerServiceTest : public testing::Test {
void SetUp() override {
testing::Test::SetUp();
CreateAccountTracker(base::FilePath(), /*network_enabled=*/true);
- fake_oauth2_token_service_.LoadCredentials(CoreAccountId());
+ fake_oauth2_token_service_.LoadCredentials(CoreAccountId(),
+ /*is_syncing=*/false);
}
void TearDown() override {
@@ -250,9 +252,7 @@ class AccountTrackerServiceTest : public testing::Test {
if (force_account_id_to_email_for_legacy_tests_)
return CoreAccountId(AccountKeyToEmail(account_key));
- return AccountTrackerService::PickAccountIdForAccount(
- &pref_service_, AccountKeyToGaiaId(account_key),
- AccountKeyToEmail(account_key));
+ return CoreAccountId::FromGaiaId(AccountKeyToGaiaId(account_key));
}
void CheckAccountDetails(AccountKey account_key, const AccountInfo& info) {
@@ -373,8 +373,10 @@ class AccountTrackerServiceTest : public testing::Test {
DCHECK(!account_tracker_);
DCHECK(!account_fetcher_);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
pref_service_.SetInteger(prefs::kAccountIdMigrationState,
AccountTrackerService::MIGRATION_NOT_STARTED);
+#endif
account_tracker_ = std::make_unique<AccountTrackerService>();
account_fetcher_ = std::make_unique<AccountFetcherService>();
@@ -413,7 +415,7 @@ class AccountTrackerServiceTest : public testing::Test {
FakeProfileOAuth2TokenService fake_oauth2_token_service_;
std::unique_ptr<AccountFetcherService> account_fetcher_;
std::unique_ptr<AccountTrackerService> account_tracker_;
- FakeAccountCapabilitiesFetcherFactory*
+ raw_ptr<FakeAccountCapabilitiesFetcherFactory>
fake_account_capabilities_fetcher_factory_ = nullptr;
std::vector<TrackingEvent> account_tracker_events_;
bool force_account_id_to_email_for_legacy_tests_ = false;
@@ -1201,6 +1203,7 @@ TEST_F(AccountTrackerServiceTest, TimerRefresh) {
EXPECT_FALSE(account_fetcher()->AreAllAccountCapabilitiesFetched());
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(AccountTrackerServiceTest, LegacyDottedAccountIds) {
// Force legacy of non-normalized email as account_id.
base::AutoReset<bool> force_account_id_to_email_for_legacy_test(
@@ -1237,10 +1240,8 @@ TEST_F(AccountTrackerServiceTest, LegacyDottedAccountIds) {
}
TEST_F(AccountTrackerServiceTest, MigrateAccountIdToGaiaId) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(switches::kAccountIdMigration);
-#endif
const std::string email_alpha = AccountKeyToEmail(kAccountKeyAlpha);
const std::string gaia_alpha = AccountKeyToGaiaId(kAccountKeyAlpha);
@@ -1286,10 +1287,8 @@ TEST_F(AccountTrackerServiceTest, MigrateAccountIdToGaiaId) {
}
TEST_F(AccountTrackerServiceTest, CanNotMigrateAccountIdToGaiaId) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(switches::kAccountIdMigration);
-#endif
const std::string email_alpha = AccountKeyToEmail(kAccountKeyAlpha);
const std::string gaia_alpha = AccountKeyToGaiaId(kAccountKeyAlpha);
@@ -1334,10 +1333,8 @@ TEST_F(AccountTrackerServiceTest, CanNotMigrateAccountIdToGaiaId) {
}
TEST_F(AccountTrackerServiceTest, GaiaIdMigrationCrashInTheMiddle) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(switches::kAccountIdMigration);
-#endif
const std::string email_alpha = AccountKeyToEmail(kAccountKeyAlpha);
const std::string gaia_alpha = AccountKeyToGaiaId(kAccountKeyAlpha);
@@ -1409,6 +1406,7 @@ TEST_F(AccountTrackerServiceTest, GaiaIdMigrationCrashInTheMiddle) {
accounts = account_tracker()->GetAccounts();
EXPECT_EQ(2u, accounts.size());
}
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(AccountTrackerServiceTest, ChildAccountBasic) {
SimulateTokenAvailable(kAccountKeyChild);
@@ -1675,7 +1673,6 @@ TEST_F(AccountTrackerServiceTest, AdvancedProtectionAccountBasic) {
SimulateTokenRevoked(kAccountKeyAdvancedProtection);
}
-
#endif
TEST_F(AccountTrackerServiceTest, CountOfLoadedAccounts_NoAccount) {
@@ -1688,6 +1685,40 @@ TEST_F(AccountTrackerServiceTest, CountOfLoadedAccounts_NoAccount) {
}
TEST_F(AccountTrackerServiceTest, CountOfLoadedAccounts_TwoAccounts) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ prefs()->SetInteger(prefs::kAccountIdMigrationState,
+ AccountTrackerService::MIGRATION_DONE);
+#endif
+
+ const std::string email_alpha = AccountKeyToEmail(kAccountKeyAlpha);
+ const std::string gaia_alpha = AccountKeyToGaiaId(kAccountKeyAlpha);
+ const std::string email_beta = AccountKeyToEmail(kAccountKeyBeta);
+ const std::string gaia_beta = AccountKeyToGaiaId(kAccountKeyBeta);
+
+ ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetStringKey("account_id", gaia_alpha);
+ dict.SetStringKey("email", email_alpha);
+ dict.SetStringKey("gaia", gaia_alpha);
+ update->Append(std::move(dict));
+
+ dict = base::Value(base::Value::Type::DICTIONARY);
+ dict.SetStringKey("account_id", gaia_beta);
+ dict.SetStringKey("email", email_beta);
+ dict.SetStringKey("gaia", gaia_beta);
+ update->Append(std::move(dict));
+
+ base::HistogramTester tester;
+ ResetAccountTracker();
+
+ EXPECT_THAT(
+ tester.GetAllSamples("Signin.AccountTracker.CountOfLoadedAccounts"),
+ testing::ElementsAre(base::Bucket(2, 1)));
+}
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+TEST_F(AccountTrackerServiceTest, Migrate_CountOfLoadedAccounts_TwoAccounts) {
const std::string email_alpha = AccountKeyToEmail(kAccountKeyAlpha);
const std::string gaia_alpha = AccountKeyToGaiaId(kAccountKeyAlpha);
const std::string email_beta = AccountKeyToEmail(kAccountKeyBeta);
@@ -1715,7 +1746,8 @@ TEST_F(AccountTrackerServiceTest, CountOfLoadedAccounts_TwoAccounts) {
testing::ElementsAre(base::Bucket(2, 1)));
}
-TEST_F(AccountTrackerServiceTest, CountOfLoadedAccounts_TwoAccountsOneInvalid) {
+TEST_F(AccountTrackerServiceTest,
+ Migrate_CountOfLoadedAccounts_TwoAccountsOneInvalid) {
const std::string email_alpha = AccountKeyToEmail(kAccountKeyAlpha);
const std::string gaia_alpha = AccountKeyToGaiaId(kAccountKeyAlpha);
const std::string email_foobar = AccountKeyToEmail(kAccountKeyFooDotBar);
@@ -1744,6 +1776,7 @@ TEST_F(AccountTrackerServiceTest, CountOfLoadedAccounts_TwoAccountsOneInvalid) {
tester.GetAllSamples("Signin.AccountTracker.CountOfLoadedAccounts"),
testing::ElementsAre(base::Bucket(1, 1)));
}
+#endif
TEST_F(AccountTrackerServiceTest, CapabilityPrefNameMigration) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.cc b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.cc
index 0d9458f9db8..c54f007bd63 100644
--- a/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.cc
+++ b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.cc
@@ -103,7 +103,8 @@ void FakeProfileOAuth2TokenServiceDelegate::RevokeAllCredentials() {
}
void FakeProfileOAuth2TokenServiceDelegate::LoadCredentials(
- const CoreAccountId& primary_account_id) {
+ const CoreAccountId& primary_account_id,
+ bool is_syncing) {
set_load_credentials_state(
signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS);
FireRefreshTokensLoaded();
diff --git a/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h
index 8cc9a884060..8aa89396dcb 100644
--- a/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h
+++ b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h
@@ -45,7 +45,8 @@ class FakeProfileOAuth2TokenServiceDelegate
const GoogleServiceAuthError& error) override;
std::vector<CoreAccountId> GetAccounts() const override;
void RevokeAllCredentials() override;
- void LoadCredentials(const CoreAccountId& primary_account_id) override;
+ void LoadCredentials(const CoreAccountId& primary_account_id,
+ bool is_syncing) override;
void UpdateCredentials(const CoreAccountId& account_id,
const std::string& refresh_token) override;
void RevokeCredentials(const CoreAccountId& account_id) override;
diff --git a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
index 5e1934169ac..8d3b3b3ab36 100644
--- a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
+++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
@@ -441,9 +441,11 @@ void GaiaCookieManagerService::ExternalCcResultFetcher::
}
GaiaCookieManagerService::GaiaCookieManagerService(
+ AccountTrackerService* account_tracker_service,
ProfileOAuth2TokenService* token_service,
SigninClient* signin_client)
- : token_service_(token_service),
+ : account_tracker_service_(account_tracker_service),
+ token_service_(token_service),
signin_client_(signin_client),
external_cc_result_fetcher_(this),
fetcher_backoff_(&kBackoffPolicy),
@@ -1008,8 +1010,8 @@ GaiaCookieManagerService::GetCookieManagerForPartition() {
void GaiaCookieManagerService::InitializeListedAccountsIds() {
for (gaia::ListedAccount& account : listed_accounts_) {
DCHECK(account.id.empty());
- account.id = AccountTrackerService::PickAccountIdForAccount(
- signin_client_->GetPrefs(), account.gaia_id, account.email);
+ account.id = account_tracker_service_->PickAccountIdForAccount(
+ account.gaia_id, account.email);
}
}
diff --git a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h
index 53e240123e2..978cecd91e5 100644
--- a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h
+++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h
@@ -18,6 +18,7 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/prefs/pref_registry_simple.h"
+#include "components/signin/internal/identity_manager/account_tracker_service.h"
#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h"
#include "components/signin/public/base/signin_client.h"
#include "components/signin/public/identity_manager/accounts_cookie_mutator.h"
@@ -219,7 +220,8 @@ class GaiaCookieManagerService
base::OnceClosure callback_;
};
- GaiaCookieManagerService(ProfileOAuth2TokenService* token_service,
+ GaiaCookieManagerService(AccountTrackerService* account_tracker_service_,
+ ProfileOAuth2TokenService* token_service,
SigninClient* signin_client);
GaiaCookieManagerService(const GaiaCookieManagerService&) = delete;
@@ -392,6 +394,7 @@ class GaiaCookieManagerService
// Start the next request, if needed.
void HandleNextRequest();
+ const raw_ptr<AccountTrackerService> account_tracker_service_ = nullptr;
raw_ptr<ProfileOAuth2TokenService> token_service_;
raw_ptr<SigninClient> signin_client_;
diff --git a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc
index 41b99117093..69ceef8e855 100644
--- a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc
+++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc
@@ -104,9 +104,13 @@ MATCHER_P(ListedAccountMatchesGaiaId, gaia_id, "") {
class InstrumentedGaiaCookieManagerService : public GaiaCookieManagerService {
public:
- InstrumentedGaiaCookieManagerService(ProfileOAuth2TokenService* token_service,
- SigninClient* signin_client)
- : GaiaCookieManagerService(token_service, signin_client) {
+ InstrumentedGaiaCookieManagerService(
+ AccountTrackerService* account_tracker_service,
+ ProfileOAuth2TokenService* token_service,
+ SigninClient* signin_client)
+ : GaiaCookieManagerService(account_tracker_service,
+ token_service,
+ signin_client) {
total++;
}
@@ -136,10 +140,15 @@ class GaiaCookieManagerServiceTest : public testing::Test {
AccountTrackerService::RegisterPrefs(pref_service_.registry());
GaiaCookieManagerService::RegisterPrefs(pref_service_.registry());
signin_client_ = std::make_unique<TestSigninClient>(&pref_service_);
+ account_tracker_service_ = std::make_unique<AccountTrackerService>();
+ account_tracker_service_->Initialize(&pref_service_, base::FilePath());
token_service_ =
std::make_unique<FakeProfileOAuth2TokenService>(&pref_service_);
}
+ AccountTrackerService* account_tracker_service() {
+ return account_tracker_service_.get();
+ }
ProfileOAuth2TokenService* token_service() { return token_service_.get(); }
TestSigninClient* signin_client() { return signin_client_.get(); }
@@ -247,6 +256,7 @@ class GaiaCookieManagerServiceTest : public testing::Test {
GoogleServiceAuthError canceled_;
TestingPrefServiceSimple pref_service_;
std::unique_ptr<TestSigninClient> signin_client_;
+ std::unique_ptr<AccountTrackerService> account_tracker_service_;
std::unique_ptr<FakeProfileOAuth2TokenService> token_service_;
};
@@ -256,7 +266,8 @@ using ::testing::_;
using ::testing::ElementsAre;
TEST_F(GaiaCookieManagerServiceTest, Success) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken());
@@ -270,7 +281,8 @@ TEST_F(GaiaCookieManagerServiceTest, Success) {
}
TEST_F(GaiaCookieManagerServiceTest, FailedMergeSession) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
base::HistogramTester histograms;
@@ -289,7 +301,8 @@ TEST_F(GaiaCookieManagerServiceTest, FailedMergeSession) {
}
TEST_F(GaiaCookieManagerServiceTest, AddAccountCookiesDisabled) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
signin_client()->set_are_signin_cookies_allowed(false);
@@ -301,7 +314,8 @@ TEST_F(GaiaCookieManagerServiceTest, AddAccountCookiesDisabled) {
}
TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetried) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
@@ -324,7 +338,8 @@ TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetried) {
}
TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetriedTwice) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
base::HistogramTester histograms;
@@ -353,7 +368,8 @@ TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetriedTwice) {
}
TEST_F(GaiaCookieManagerServiceTest, FailedUbertoken) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken());
@@ -367,7 +383,8 @@ TEST_F(GaiaCookieManagerServiceTest, FailedUbertoken) {
}
TEST_F(GaiaCookieManagerServiceTest, ContinueAfterSuccess) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2);
@@ -386,7 +403,8 @@ TEST_F(GaiaCookieManagerServiceTest, ContinueAfterSuccess) {
}
TEST_F(GaiaCookieManagerServiceTest, ContinueAfterFailure1) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2);
@@ -405,7 +423,8 @@ TEST_F(GaiaCookieManagerServiceTest, ContinueAfterFailure1) {
}
TEST_F(GaiaCookieManagerServiceTest, ContinueAfterFailure2) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2);
@@ -424,7 +443,8 @@ TEST_F(GaiaCookieManagerServiceTest, ContinueAfterFailure2) {
}
TEST_F(GaiaCookieManagerServiceTest, AllRequestsInMultipleGoes) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken()).Times(4);
@@ -452,7 +472,8 @@ TEST_F(GaiaCookieManagerServiceTest, AllRequestsInMultipleGoes) {
}
TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsNoQueue) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken());
@@ -475,7 +496,8 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsNoQueue) {
}
TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsFails) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken());
@@ -498,7 +520,8 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsFails) {
}
TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterOneAddInQueue) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken());
@@ -519,7 +542,8 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterOneAddInQueue) {
}
TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterTwoAddsInQueue) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken());
@@ -545,7 +569,8 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterTwoAddsInQueue) {
}
TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsTwice) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken());
@@ -572,7 +597,8 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsTwice) {
}
TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeAdd) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2);
@@ -601,7 +627,8 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeAdd) {
}
TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeLogoutAndAdd) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2);
@@ -635,7 +662,8 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeLogoutAndAdd) {
}
TEST_F(GaiaCookieManagerServiceTest, PendingSigninThenSignout) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
// From the first Signin.
@@ -667,7 +695,8 @@ TEST_F(GaiaCookieManagerServiceTest, PendingSigninThenSignout) {
}
TEST_F(GaiaCookieManagerServiceTest, CancelSignIn) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingUbertoken());
@@ -692,7 +721,8 @@ TEST_F(GaiaCookieManagerServiceTest, CancelSignIn) {
}
TEST_F(GaiaCookieManagerServiceTest, ListAccountsFirstReturnsEmpty) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
std::vector<gaia::ListedAccount> list_accounts;
@@ -710,7 +740,8 @@ TEST_F(GaiaCookieManagerServiceTest, ListAccountsFirstReturnsEmpty) {
}
TEST_F(GaiaCookieManagerServiceTest, ListAccountsFindsOneAccount) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
std::vector<gaia::ListedAccount> list_accounts;
@@ -741,7 +772,8 @@ TEST_F(GaiaCookieManagerServiceTest, ListAccountsFindsOneAccount) {
}
TEST_F(GaiaCookieManagerServiceTest, ListAccountsFindsSignedOutAccounts) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
std::vector<gaia::ListedAccount> list_accounts;
@@ -781,7 +813,8 @@ TEST_F(GaiaCookieManagerServiceTest, ListAccountsFindsSignedOutAccounts) {
}
TEST_F(GaiaCookieManagerServiceTest, ListAccountsAcceptsNull) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
ASSERT_FALSE(helper.ListAccounts(nullptr, nullptr));
@@ -807,7 +840,8 @@ TEST_F(GaiaCookieManagerServiceTest, ListAccountsAcceptsNull) {
}
TEST_F(GaiaCookieManagerServiceTest, ListAccountsAfterOnCookieChange) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
MockObserver observer(&helper);
std::vector<gaia::ListedAccount> list_accounts;
@@ -891,8 +925,8 @@ TEST_F(GaiaCookieManagerServiceTest, GaiaCookieLastListAccountsDataSaved) {
std::vector<gaia::ListedAccount> signed_out_accounts;
{
- InstrumentedGaiaCookieManagerService helper(token_service(),
- signin_client());
+ InstrumentedGaiaCookieManagerService helper(
+ account_tracker_service(), token_service(), signin_client());
MockObserver observer(&helper);
EXPECT_CALL(helper, StartFetchingListAccounts());
@@ -918,8 +952,8 @@ TEST_F(GaiaCookieManagerServiceTest, GaiaCookieLastListAccountsDataSaved) {
// starting a new Gaia Service Manager gives synchronous answers to list
// accounts.
{
- InstrumentedGaiaCookieManagerService helper(token_service(),
- signin_client());
+ InstrumentedGaiaCookieManagerService helper(
+ account_tracker_service(), token_service(), signin_client());
MockObserver observer(&helper);
auto test_task_runner =
base::MakeRefCounted<base::TestMockTimeTaskRunner>();
@@ -985,7 +1019,8 @@ TEST_F(GaiaCookieManagerServiceTest, GaiaCookieLastListAccountsDataSaved) {
}
TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcher) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
GaiaCookieManagerService::ExternalCcResultFetcher result_fetcher(&helper);
EXPECT_CALL(helper, StartFetchingMergeSession());
result_fetcher.Start(base::BindOnce(
@@ -1012,7 +1047,8 @@ TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcher) {
}
TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherTimeout) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
GaiaCookieManagerService::ExternalCcResultFetcher result_fetcher(&helper);
EXPECT_CALL(helper, StartFetchingMergeSession());
result_fetcher.Start(base::BindOnce(
@@ -1043,7 +1079,8 @@ TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherTimeout) {
}
TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherTruncate) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
GaiaCookieManagerService::ExternalCcResultFetcher result_fetcher(&helper);
EXPECT_CALL(helper, StartFetchingMergeSession());
result_fetcher.Start(base::BindOnce(
@@ -1066,7 +1103,8 @@ TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherTruncate) {
}
TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherWithCommas) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
GaiaCookieManagerService::ExternalCcResultFetcher result_fetcher(&helper);
EXPECT_CALL(helper, StartFetchingMergeSession());
result_fetcher.Start(base::BindOnce(
@@ -1089,7 +1127,8 @@ TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherWithCommas) {
}
TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCC) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
EXPECT_CALL(helper, StartFetchingUbertoken());
helper.AddAccountToCookie(
@@ -1111,7 +1150,8 @@ TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCC) {
}
TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCCOnce) {
- InstrumentedGaiaCookieManagerService helper(token_service(), signin_client());
+ InstrumentedGaiaCookieManagerService helper(account_tracker_service(),
+ token_service(), signin_client());
helper.external_cc_result_fetcher_for_testing()->Start(base::BindOnce(
&InstrumentedGaiaCookieManagerService::StartFetchingMergeSession,
@@ -1132,7 +1172,7 @@ TEST_F(GaiaCookieManagerServiceTest, RemoveLoggedOutAccountByGaiaId) {
const std::string kTestGaiaId2 = "9";
::testing::NiceMock<InstrumentedGaiaCookieManagerService> helper(
- token_service(), signin_client());
+ account_tracker_service(), token_service(), signin_client());
::testing::NiceMock<MockObserver> observer(&helper);
std::vector<gaia::ListedAccount> signed_in_accounts;
@@ -1177,7 +1217,7 @@ TEST_F(GaiaCookieManagerServiceTest,
const std::string kTestGaiaId1 = "8";
::testing::NiceMock<InstrumentedGaiaCookieManagerService> helper(
- token_service(), signin_client());
+ account_tracker_service(), token_service(), signin_client());
::testing::NiceMock<MockObserver> observer(&helper);
std::vector<gaia::ListedAccount> signed_in_accounts;
@@ -1220,7 +1260,7 @@ TEST_F(GaiaCookieManagerServiceTest,
const std::string kNonListedAccount = "9";
::testing::NiceMock<InstrumentedGaiaCookieManagerService> helper(
- token_service(), signin_client());
+ account_tracker_service(), token_service(), signin_client());
::testing::NiceMock<MockObserver> observer(&helper);
std::vector<gaia::ListedAccount> signed_in_accounts;
diff --git a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc
index 791a211fec8..0e2e638171b 100644
--- a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc
+++ b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc
@@ -31,7 +31,6 @@
namespace {
const char kAccountIdPrefix[] = "AccountId-";
-const size_t kAccountIdPrefixLength = 10;
// Enum for the Signin.LoadTokenFromDB histogram.
// Do not modify, or add or delete other than directly before
@@ -39,11 +38,15 @@ const size_t kAccountIdPrefixLength = 10;
enum class LoadTokenFromDBStatus {
// Token was loaded.
TOKEN_LOADED = 0,
+
+ // DEPRECATED
// Token was revoked as part of Dice migration.
- TOKEN_REVOKED_DICE_MIGRATION = 1,
+ // TOKEN_REVOKED_DICE_MIGRATION = 1,
+
// Token was revoked because it is a secondary account and account consistency
// is disabled.
TOKEN_REVOKED_SECONDARY_ACCOUNT = 2,
+
// Token was revoked on load due to cookie settings.
TOKEN_REVOKED_ON_LOAD = 3,
@@ -54,17 +57,15 @@ std::string ApplyAccountIdPrefix(const std::string& account_id) {
return kAccountIdPrefix + account_id;
}
-bool IsLegacyRefreshTokenId(const std::string& service_id) {
- return service_id == GaiaConstants::kGaiaOAuth2LoginRefreshToken;
-}
-
-bool IsLegacyServiceId(const std::string& account_id) {
- return account_id.compare(0u, kAccountIdPrefixLength, kAccountIdPrefix) != 0;
-}
-
+// Checks that |prefixed_account_id| starts with the expected prefix
+// (|prefixed_account_id|) and returns the non-prefixed account id or an empty
+// account id if |prefixed_account_id| is not correctly prefixed.
CoreAccountId RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
+ if (!base::StartsWith(prefixed_account_id, kAccountIdPrefix))
+ return CoreAccountId();
+
return CoreAccountId::FromString(
- prefixed_account_id.substr(kAccountIdPrefixLength));
+ prefixed_account_id.substr(/*pos=*/strlen(kAccountIdPrefix)));
}
signin::LoadCredentialsState LoadCredentialsStateFromTokenResult(
@@ -86,17 +87,6 @@ signin::LoadCredentialsState LoadCredentialsStateFromTokenResult(
LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS;
}
-// Returns whether the token service should be migrated to Dice.
-// Migration can happen if the following conditions are met:
-// - Token service Dice migration is not already done,
-// - Account consistency is DiceMigration or greater.
-// TODO(droger): Remove this code once Dice is fully enabled.
-bool ShouldMigrateToDice(signin::AccountConsistencyMethod account_consistency,
- PrefService* prefs) {
- return account_consistency == signin::AccountConsistencyMethod::kDice &&
- !prefs->GetBoolean(prefs::kTokenServiceDiceCompatible);
-}
-
} // namespace
// This class sends a request to GAIA to revoke the given refresh token from
@@ -240,12 +230,6 @@ MutableProfileOAuth2TokenServiceDelegate::
network_connection_tracker_->RemoveNetworkConnectionObserver(this);
}
-// static
-void MutableProfileOAuth2TokenServiceDelegate::RegisterProfilePrefs(
- PrefRegistrySimple* registry) {
- registry->RegisterBooleanPref(prefs::kTokenServiceDiceCompatible, false);
-}
-
std::unique_ptr<OAuth2AccessTokenFetcher>
MutableProfileOAuth2TokenServiceDelegate::CreateAccessTokenFetcher(
const CoreAccountId& account_id,
@@ -367,7 +351,8 @@ void MutableProfileOAuth2TokenServiceDelegate::InvalidateTokenForMultilogin(
}
void MutableProfileOAuth2TokenServiceDelegate::LoadCredentials(
- const CoreAccountId& primary_account_id) {
+ const CoreAccountId& primary_account_id,
+ bool is_syncing) {
if (load_credentials_state() ==
signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS) {
VLOG(1) << "Load credentials operation already in progress";
@@ -390,21 +375,11 @@ void MutableProfileOAuth2TokenServiceDelegate::LoadCredentials(
set_load_credentials_state(
signin::LoadCredentialsState::
LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS);
- MaybeDeletePreDiceTokens();
FinishLoadingCredentials();
return;
}
- // If |account_id| is an email address, then canonicalize it. This is needed
- // to support legacy account IDs, and will not be needed after switching to
- // gaia IDs.
- if (primary_account_id.ToString().find('@') != std::string::npos) {
- loading_primary_account_id_ = CoreAccountId::FromEmail(
- gaia::CanonicalizeEmail(primary_account_id.ToString()));
- } else {
- loading_primary_account_id_ = primary_account_id;
- }
-
+ loading_primary_account_id_ = primary_account_id;
web_data_service_request_ = token_web_data_->GetAllTokens(this);
}
@@ -429,7 +404,6 @@ void MutableProfileOAuth2TokenServiceDelegate::OnWebDataServiceRequestDone(
set_load_credentials_state(
signin::LoadCredentialsState::
LOAD_CREDENTIALS_FINISHED_WITH_DB_CANNOT_BE_OPENED);
- MaybeDeletePreDiceTokens();
}
// Make sure that we have an entry for |loading_primary_account_id_| in the
@@ -464,147 +438,60 @@ void MutableProfileOAuth2TokenServiceDelegate::OnWebDataServiceRequestDone(
void MutableProfileOAuth2TokenServiceDelegate::LoadAllCredentialsIntoMemory(
const std::map<std::string, std::string>& db_tokens) {
- std::string old_login_token;
- bool migrate_to_dice =
- ShouldMigrateToDice(account_consistency_, client_->GetPrefs());
+ VLOG(1) << "MutablePO2TS::LoadAllCredentialsIntoMemory; " << db_tokens.size()
+ << " redential(s).";
- {
- ScopedBatchChange batch(this);
+ ScopedBatchChange batch(this);
+ for (const auto& db_token : db_tokens) {
+ std::string prefixed_account_id = db_token.first;
+ std::string refresh_token = db_token.second;
+
+ CoreAccountId account_id = RemoveAccountIdPrefix(prefixed_account_id);
+ if (account_id.empty()) {
+ if (token_web_data_) {
+ VLOG(1) << "MutablePO2TS remove refresh token for invalid account id ["
+ << prefixed_account_id << "]";
+ token_web_data_->RemoveTokenForService(prefixed_account_id);
+ }
+ continue;
+ }
- VLOG(1) << "MutablePO2TS::LoadAllCredentialsIntoMemory; "
- << db_tokens.size() << " Credential(s).";
- AccountTrackerService::AccountIdMigrationState migration_state =
- account_tracker_service_->GetMigrationState();
- for (auto iter = db_tokens.begin(); iter != db_tokens.end(); ++iter) {
- std::string prefixed_account_id = iter->first;
- std::string refresh_token = iter->second;
-
- if (IsLegacyRefreshTokenId(prefixed_account_id) && !refresh_token.empty())
- old_login_token = refresh_token;
-
- if (IsLegacyServiceId(prefixed_account_id)) {
- if (token_web_data_) {
- VLOG(1) << "MutablePO2TS remove legacy refresh token for account id "
- << prefixed_account_id;
- token_web_data_->RemoveTokenForService(prefixed_account_id);
- }
+ DCHECK(!account_id.IsEmail())
+ << "Acount id should be a Gaia id [account_id = " << account_id << "]";
+ DCHECK(!refresh_token.empty());
+
+ // Only load secondary accounts when account consistency is enabled.
+ bool load_account =
+ account_id == loading_primary_account_id_ ||
+ account_consistency_ == signin::AccountConsistencyMethod::kDice;
+ LoadTokenFromDBStatus load_token_status =
+ load_account ? LoadTokenFromDBStatus::TOKEN_LOADED
+ : LoadTokenFromDBStatus::TOKEN_REVOKED_SECONDARY_ACCOUNT;
+
+ if (load_account && revoke_all_tokens_on_load_) {
+ if (account_id == loading_primary_account_id_) {
+ RevokeCredentialsOnServer(refresh_token);
+ refresh_token = GaiaConstants::kInvalidRefreshToken;
+ PersistCredentials(account_id, refresh_token);
} else {
- DCHECK(!refresh_token.empty());
- CoreAccountId account_id = RemoveAccountIdPrefix(prefixed_account_id);
-
- switch (migration_state) {
- case AccountTrackerService::MIGRATION_IN_PROGRESS: {
- // Migrate to gaia-ids.
- AccountInfo account_info =
- account_tracker_service_->FindAccountInfoByEmail(
- account_id.ToString());
- // |account_info| can be empty if |account_id| was already migrated.
- // This could happen if the chrome was closed in the middle of the
- // account id migration.
- if (!account_info.IsEmpty()) {
- ClearPersistedCredentials(account_id);
- account_id = account_info.account_id;
- PersistCredentials(account_id, refresh_token);
- }
-
- // Skip duplicate accounts, this could happen if migration was
- // crashed in the middle.
- if (refresh_tokens_.count(account_id) != 0)
- continue;
- break;
- }
- case AccountTrackerService::MIGRATION_NOT_STARTED:
- // If the account_id is an email address, then canonicalize it. This
- // is to support legacy account_ids, and will not be needed after
- // switching to gaia-ids.
- if (account_id.ToString().find('@') != std::string::npos) {
- // If the canonical account id is not the same as the loaded
- // account id, make sure not to overwrite a refresh token from
- // a canonical version. If no canonical version was loaded, then
- // re-persist this refresh token with the canonical account id.
- CoreAccountId canon_account_id = CoreAccountId::FromEmail(
- gaia::CanonicalizeEmail(account_id.ToString()));
- if (canon_account_id != account_id) {
- ClearPersistedCredentials(account_id);
- if (db_tokens.count(
- ApplyAccountIdPrefix(canon_account_id.ToString())) == 0)
- PersistCredentials(canon_account_id, refresh_token);
- }
- account_id = canon_account_id;
- }
- break;
- case AccountTrackerService::MIGRATION_DONE:
- DCHECK_EQ(std::string::npos, account_id.ToString().find('@'));
- break;
- case AccountTrackerService::NUM_MIGRATION_STATES:
- NOTREACHED();
- break;
- }
-
- // Only load secondary accounts when account consistency is enabled.
- bool load_account =
- account_id == loading_primary_account_id_ ||
- account_consistency_ == signin::AccountConsistencyMethod::kDice;
- LoadTokenFromDBStatus load_token_status =
- load_account
- ? LoadTokenFromDBStatus::TOKEN_LOADED
- : LoadTokenFromDBStatus::TOKEN_REVOKED_SECONDARY_ACCOUNT;
-
- if (migrate_to_dice) {
- // Revoke old hosted domain accounts as part of Dice migration.
- AccountInfo account_info =
- account_tracker_service_->GetAccountInfo(account_id);
- bool is_hosted_domain = false;
- if (account_info.hosted_domain.empty()) {
- // The AccountInfo is incomplete. Use a conservative approximation.
- is_hosted_domain =
- !client_->IsNonEnterpriseUser(account_info.email);
- } else {
- is_hosted_domain =
- (account_info.hosted_domain != kNoHostedDomainFound);
- }
- if (is_hosted_domain) {
- load_account = false;
- load_token_status =
- LoadTokenFromDBStatus::TOKEN_REVOKED_DICE_MIGRATION;
- }
- }
-
- if (load_account && revoke_all_tokens_on_load_) {
- if (account_id == loading_primary_account_id_) {
- RevokeCredentialsOnServer(refresh_token);
- refresh_token = GaiaConstants::kInvalidRefreshToken;
- PersistCredentials(account_id, refresh_token);
- } else {
- load_account = false;
- }
- load_token_status = LoadTokenFromDBStatus::TOKEN_REVOKED_ON_LOAD;
- }
-
- UMA_HISTOGRAM_ENUMERATION(
- "Signin.LoadTokenFromDB", load_token_status,
- LoadTokenFromDBStatus::NUM_LOAD_TOKEN_FROM_DB_STATUS);
-
- if (load_account) {
- UpdateCredentialsInMemory(account_id, refresh_token);
- FireRefreshTokenAvailable(account_id);
- } else {
- RevokeCredentialsOnServer(refresh_token);
- ClearPersistedCredentials(account_id);
- FireRefreshTokenRevoked(account_id);
- }
+ load_account = false;
}
+ load_token_status = LoadTokenFromDBStatus::TOKEN_REVOKED_ON_LOAD;
}
- if (!old_login_token.empty()) {
- DCHECK(!loading_primary_account_id_.empty());
- if (refresh_tokens_.count(loading_primary_account_id_) == 0)
- UpdateCredentials(loading_primary_account_id_, old_login_token);
+ UMA_HISTOGRAM_ENUMERATION(
+ "Signin.LoadTokenFromDB", load_token_status,
+ LoadTokenFromDBStatus::NUM_LOAD_TOKEN_FROM_DB_STATUS);
+
+ if (load_account) {
+ UpdateCredentialsInMemory(account_id, refresh_token);
+ FireRefreshTokenAvailable(account_id);
+ } else {
+ RevokeCredentialsOnServer(refresh_token);
+ ClearPersistedCredentials(account_id);
+ FireRefreshTokenRevoked(account_id);
}
}
-
- if (migrate_to_dice)
- client_->GetPrefs()->SetBoolean(prefs::kTokenServiceDiceCompatible, true);
}
void MutableProfileOAuth2TokenServiceDelegate::UpdateCredentials(
@@ -795,8 +682,6 @@ void MutableProfileOAuth2TokenServiceDelegate::AddAccountStatus(
}
void MutableProfileOAuth2TokenServiceDelegate::FinishLoadingCredentials() {
- if (account_consistency_ == signin::AccountConsistencyMethod::kDice)
- DCHECK(client_->GetPrefs()->GetBoolean(prefs::kTokenServiceDiceCompatible));
FireRefreshTokensLoaded();
}
@@ -817,18 +702,3 @@ void MutableProfileOAuth2TokenServiceDelegate::RevokeCredentialsImpl(
FireRefreshTokenRevoked(account_id);
}
}
-
-void MutableProfileOAuth2TokenServiceDelegate::MaybeDeletePreDiceTokens() {
- DCHECK(load_credentials_state() ==
- signin::LoadCredentialsState::
- LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS ||
- load_credentials_state() ==
- signin::LoadCredentialsState::
- LOAD_CREDENTIALS_FINISHED_WITH_DB_CANNOT_BE_OPENED);
-
- if (account_consistency_ == signin::AccountConsistencyMethod::kDice &&
- !client_->GetPrefs()->GetBoolean(prefs::kTokenServiceDiceCompatible)) {
- RevokeAllCredentials();
- client_->GetPrefs()->SetBoolean(prefs::kTokenServiceDiceCompatible, true);
- }
-}
diff --git a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h
index ef9951b7517..82a113efdd0 100644
--- a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h
+++ b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h
@@ -23,7 +23,6 @@
#include "net/base/backoff_entry.h"
#include "services/network/public/cpp/network_connection_tracker.h"
-class PrefRegistrySimple;
class SigninClient;
class TokenWebData;
@@ -50,8 +49,6 @@ class MutableProfileOAuth2TokenServiceDelegate
~MutableProfileOAuth2TokenServiceDelegate() override;
- static void RegisterProfilePrefs(PrefRegistrySimple* registry);
-
// Overridden from ProfileOAuth2TokenServiceDelegate.
std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher(
const CoreAccountId& account_id,
@@ -71,7 +68,8 @@ class MutableProfileOAuth2TokenServiceDelegate
std::vector<CoreAccountId> GetAccounts() const override;
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
const override;
- void LoadCredentials(const CoreAccountId& primary_account_id) override;
+ void LoadCredentials(const CoreAccountId& primary_account_id,
+ bool is_syncing) override;
void UpdateCredentials(const CoreAccountId& account_id,
const std::string& refresh_token) override;
void RevokeAllCredentials() override;
@@ -198,11 +196,6 @@ class MutableProfileOAuth2TokenServiceDelegate
void RevokeCredentialsImpl(const CoreAccountId& account_id,
bool revoke_on_server);
- // If the Dice migration happened before the tokens could be migrated, delete
- // all the tokens. This is only called if the tokens could not be loaded
- // successfully.
- void MaybeDeletePreDiceTokens();
-
// Maps the |account_id| of accounts known to ProfileOAuth2TokenService
// to information about the account.
typedef std::map<CoreAccountId, AccountStatus> AccountStatusMap;
diff --git a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc
index 794ebcc79ae..97c523d76e3 100644
--- a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc
+++ b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -46,33 +46,8 @@
// Defining constant here to handle backward compatiblity tests, but this
// constant is no longer used in current versions of chrome.
-static const char kLSOService[] = "lso";
static const char kEmail[] = "user@gmail.com";
-namespace {
-
-// Create test account info.
-AccountInfo CreateTestAccountInfo(const std::string& name,
- bool is_hosted_domain,
- bool is_valid) {
- AccountInfo account_info;
- account_info.account_id = CoreAccountId(name);
- account_info.gaia = name;
- account_info.email = name + "@email.com";
- account_info.full_name = "name";
- account_info.given_name = "name";
- if (is_valid) {
- account_info.hosted_domain =
- is_hosted_domain ? "example.com" : kNoHostedDomainFound;
- }
- account_info.locale = "en";
- account_info.picture_url = "https://example.com";
- EXPECT_EQ(is_valid, account_info.IsValid());
- return account_info;
-}
-
-} // namespace
-
class MutableProfileOAuth2TokenServiceDelegateTest
: public testing::Test,
public OAuth2AccessTokenConsumer,
@@ -95,9 +70,6 @@ class MutableProfileOAuth2TokenServiceDelegateTest
void SetUp() override {
OSCryptMocker::SetUp();
-
- MutableProfileOAuth2TokenServiceDelegate::RegisterProfilePrefs(
- pref_service_.registry());
AccountTrackerService::RegisterPrefs(pref_service_.registry());
PrimaryAccountManager::RegisterProfilePrefs(pref_service_.registry());
client_ = std::make_unique<TestSigninClient>(&pref_service_);
@@ -270,69 +242,30 @@ class MutableProfileOAuth2TokenServiceDelegateTest
TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceDBUpgrade) {
InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice);
- CoreAccountId main_account_id("account_id");
- std::string main_refresh_token("old_refresh_token");
+ CoreAccountId primary_account_id("primaryAccount");
- // Populate DB with legacy tokens.
- AddAuthTokenManually(GaiaConstants::kSyncService, "syncServiceToken");
- AddAuthTokenManually(kLSOService, "lsoToken");
- AddAuthTokenManually(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
- main_refresh_token);
+ // Populate DB with legacy service tokens (all expected to be discarded).
+ AddAuthTokenManually("chromiumsync", "syncServiceToken");
+ AddAuthTokenManually("lso", "lsoToken");
+ AddAuthTokenManually("kObfuscatedGaiaId", "primaryLegacyRefreshToken");
// Force LoadCredentials.
- oauth2_service_delegate_->LoadCredentials(main_account_id);
+ oauth2_service_delegate_->LoadCredentials(primary_account_id,
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
- // Legacy tokens get discarded, but the old refresh token is kept.
+ // 1. Legacy tokens get all discarded.
+ // 2. Token for primary account is set to invalid as it cannot be found.
+ // 3. Token for secondary account is loaded.
EXPECT_EQ(1, tokens_loaded_count_);
EXPECT_EQ(1, token_available_count_);
EXPECT_EQ(1, end_batch_changes_);
- EXPECT_TRUE(
- oauth2_service_delegate_->RefreshTokenIsAvailable(main_account_id));
EXPECT_EQ(1U, oauth2_service_delegate_->refresh_tokens_.size());
- EXPECT_EQ(
- main_refresh_token,
- oauth2_service_delegate_->refresh_tokens_[main_account_id].refresh_token);
-
- // Add an old legacy token to the DB, to ensure it will not overwrite existing
- // credentials for main account.
- AddAuthTokenManually(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
- "secondOldRefreshToken");
- // Add some other legacy token. (Expected to get discarded).
- AddAuthTokenManually(kLSOService, "lsoToken");
- // Also add a token using PO2TS.UpdateCredentials and make sure upgrade does
- // not wipe it.
- CoreAccountId other_account_id("other_account_id");
- std::string other_refresh_token("other_refresh_token");
- oauth2_service_delegate_->UpdateCredentials(other_account_id,
- other_refresh_token);
- ResetObserverCounts();
-
- // Force LoadCredentials.
- oauth2_service_delegate_->LoadCredentials(main_account_id);
- base::RunLoop().RunUntilIdle();
-
- // Again legacy tokens get discarded, but since the main porfile account
- // token is present it is not overwritten.
- EXPECT_EQ(2, token_available_count_);
- EXPECT_EQ(1, tokens_loaded_count_);
- EXPECT_EQ(1, end_batch_changes_);
- EXPECT_EQ(main_refresh_token,
- oauth2_service_delegate_->GetRefreshToken(main_account_id));
EXPECT_TRUE(
- oauth2_service_delegate_->RefreshTokenIsAvailable(main_account_id));
- // TODO(fgorski): cover both using RefreshTokenIsAvailable() and then get the
- // tokens using GetRefreshToken()
- EXPECT_EQ(2U, oauth2_service_delegate_->refresh_tokens_.size());
- EXPECT_EQ(
- main_refresh_token,
- oauth2_service_delegate_->refresh_tokens_[main_account_id].refresh_token);
- EXPECT_EQ(other_refresh_token,
- oauth2_service_delegate_->refresh_tokens_[other_account_id]
+ oauth2_service_delegate_->RefreshTokenIsAvailable(primary_account_id));
+ EXPECT_EQ(GaiaConstants::kInvalidRefreshToken,
+ oauth2_service_delegate_->refresh_tokens_[primary_account_id]
.refresh_token);
-
- oauth2_service_delegate_->RevokeAllCredentials();
- EXPECT_EQ(2, end_batch_changes_);
}
TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
@@ -376,7 +309,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED,
oauth2_service_delegate_->load_credentials_state());
- oauth2_service_delegate_->LoadCredentials(CoreAccountId());
+ oauth2_service_delegate_->LoadCredentials(CoreAccountId(),
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(
signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS,
@@ -417,7 +351,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
AddAuthTokenManually("AccountId-" + account1.ToString(), "refresh_token");
AddAuthTokenManually("AccountId-" + account2.ToString(), "refresh_token");
- oauth2_service_delegate_->LoadCredentials(CoreAccountId());
+ oauth2_service_delegate_->LoadCredentials(CoreAccountId(),
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, tokens_loaded_count_);
@@ -444,7 +379,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
// Perform a load from an empty DB.
EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED,
oauth2_service_delegate_->load_credentials_state());
- oauth2_service_delegate_->LoadCredentials(account_id);
+ oauth2_service_delegate_->LoadCredentials(account_id, /*is_syncing=*/false);
EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS,
oauth2_service_delegate_->load_credentials_state());
base::RunLoop().RunUntilIdle();
@@ -482,7 +417,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
EXPECT_EQ(2, auth_error_changed_count_);
ResetObserverCounts();
- oauth2_service_delegate_->LoadCredentials(account_id);
+ oauth2_service_delegate_->LoadCredentials(account_id, /*is_syncing=*/false);
EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS,
oauth2_service_delegate_->load_credentials_state());
base::RunLoop().RunUntilIdle();
@@ -522,7 +457,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
// Perform a load from an empty DB.
EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED,
oauth2_service_delegate_->load_credentials_state());
- oauth2_service_delegate_->LoadCredentials(CoreAccountId());
+ oauth2_service_delegate_->LoadCredentials(CoreAccountId(),
+ /*is_syncing=*/false);
EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS,
oauth2_service_delegate_->load_credentials_state());
base::RunLoop().RunUntilIdle();
@@ -545,7 +481,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
EXPECT_EQ(2, auth_error_changed_count_);
ResetObserverCounts();
- oauth2_service_delegate_->LoadCredentials(CoreAccountId());
+ oauth2_service_delegate_->LoadCredentials(CoreAccountId(),
+ /*is_syncing=*/false);
EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS,
oauth2_service_delegate_->load_credentials_state());
base::RunLoop().RunUntilIdle();
@@ -571,52 +508,6 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
ResetObserverCounts();
}
-// Checks that tokens are loaded and prefs::kTokenServiceDiceCompatible is set
-// to true if the tokens are loaded after the Dice migration.
-TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, LoadAfterDiceMigration) {
- InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice);
- ASSERT_FALSE(pref_service_.GetBoolean(prefs::kTokenServiceDiceCompatible));
-
- // Add account info to the account tracker.
- AccountInfo primary_account = CreateTestAccountInfo(
- "primary_account", false /* is_hosted_domain*/, true /* is_valid*/);
- account_tracker_service_.SeedAccountInfo(primary_account);
- AddAuthTokenManually("AccountId-" + primary_account.account_id.ToString(),
- "refresh_token");
-
- oauth2_service_delegate_->LoadCredentials(CoreAccountId());
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(
- primary_account.account_id));
- EXPECT_EQ(
- signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS,
- oauth2_service_delegate_->load_credentials_state());
-
- ASSERT_TRUE(pref_service_.GetBoolean(prefs::kTokenServiceDiceCompatible));
-}
-
-// Checks that prefs::kTokenServiceDiceCompatible is set to true if the tokens
-// are loaded after the Dice migration, even if there was a database read error.
-TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
- LoadAfterDiceMigrationWithError) {
- InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice);
- ASSERT_FALSE(pref_service_.GetBoolean(prefs::kTokenServiceDiceCompatible));
-
- // Shutdown the database to trigger a database read error.
- token_web_data_->ShutdownDatabase();
-
- oauth2_service_delegate_->LoadCredentials(CoreAccountId());
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(0u, oauth2_service_delegate_->GetAccounts().size());
- EXPECT_EQ(signin::LoadCredentialsState::
- LOAD_CREDENTIALS_FINISHED_WITH_DB_CANNOT_BE_OPENED,
- oauth2_service_delegate_->load_credentials_state());
-
- ASSERT_TRUE(pref_service_.GetBoolean(prefs::kTokenServiceDiceCompatible));
-}
-
TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
LoadCredentialsClearsTokenDBWhenNoPrimaryAccount_DiceDisabled) {
// Populate DB with 2 valid tokens.
@@ -625,7 +516,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
oauth2_service_delegate_->LoadCredentials(
- /*primary_account_id=*/CoreAccountId());
+ /*primary_account_id=*/CoreAccountId(), /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
// No tokens were loaded.
@@ -830,7 +721,6 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
}
TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, LoadInvalidToken) {
- pref_service_.SetBoolean(prefs::kTokenServiceDiceCompatible, true);
InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice);
std::map<std::string, std::string> tokens;
const CoreAccountId account_id("account_id");
@@ -1027,6 +917,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ResetBackoff) {
EXPECT_EQ(1, access_token_failure_count_);
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, CanonicalizeAccountId) {
pref_service_.SetInteger(prefs::kAccountIdMigrationState,
AccountTrackerService::MIGRATION_NOT_STARTED);
@@ -1080,7 +971,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ShutdownService) {
EXPECT_EQ(2u, accounts.size());
EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id1));
EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id2));
- oauth2_service_delegate_->LoadCredentials(account_id1);
+ oauth2_service_delegate_->LoadCredentials(account_id1, /*is_syncing=*/false);
oauth2_service_delegate_->UpdateCredentials(account_id1, "refresh_token3");
oauth2_service_delegate_->Shutdown();
EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
@@ -1110,7 +1001,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GaiaIdMigration) {
account_tracker_service_.ResetForTesting();
AddAuthTokenManually("AccountId-" + email, "refresh_token");
- oauth2_service_delegate_->LoadCredentials(acc_id_gaia_id);
+ oauth2_service_delegate_->LoadCredentials(acc_id_gaia_id,
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, tokens_loaded_count_);
@@ -1130,7 +1022,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GaiaIdMigration) {
oauth2_service_delegate_->Shutdown();
ResetObserverCounts();
- oauth2_service_delegate_->LoadCredentials(acc_id_gaia_id);
+ oauth2_service_delegate_->LoadCredentials(acc_id_gaia_id,
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, tokens_loaded_count_);
@@ -1180,7 +1073,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
AddAuthTokenManually("AccountId-" + email1, "refresh_token");
AddAuthTokenManually("AccountId-" + email2, "refresh_token");
AddAuthTokenManually("AccountId-" + gaia_id1, "refresh_token");
- oauth2_service_delegate_->LoadCredentials(acc_gaia1);
+ oauth2_service_delegate_->LoadCredentials(acc_gaia1, /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, tokens_loaded_count_);
@@ -1200,7 +1093,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
oauth2_service_delegate_->Shutdown();
ResetObserverCounts();
- oauth2_service_delegate_->LoadCredentials(acc_gaia1);
+ oauth2_service_delegate_->LoadCredentials(acc_gaia1, /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, tokens_loaded_count_);
@@ -1215,6 +1108,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
EXPECT_EQ(2u, accounts.size());
}
}
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
LoadPrimaryAccountOnlyWhenAccountConsistencyDisabled) {
@@ -1228,7 +1122,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
"refresh_token");
AddAuthTokenManually("AccountId-" + secondary_account.ToString(),
"refresh_token");
- oauth2_service_delegate_->LoadCredentials(primary_account);
+ oauth2_service_delegate_->LoadCredentials(primary_account,
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, tokens_loaded_count_);
@@ -1387,7 +1282,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ClearTokensOnStartup) {
"refresh_token");
AddAuthTokenManually("AccountId-" + secondary_account.ToString(),
"refresh_token");
- oauth2_service_delegate_->LoadCredentials(primary_account);
+ oauth2_service_delegate_->LoadCredentials(primary_account,
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, tokens_loaded_count_);
@@ -1415,7 +1311,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ClearTokensOnStartup) {
// Check that the changes have been persisted in the database: tokens are not
// revoked again on the server.
client_->SetNetworkCallsDelayed(true);
- oauth2_service_delegate_->LoadCredentials(primary_account);
+ oauth2_service_delegate_->LoadCredentials(primary_account,
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
oauth2_service_delegate_->RefreshTokenIsAvailable(primary_account));
@@ -1451,7 +1348,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
{
base::HistogramTester h_tester;
AddAuthTokenManually("account_id", "refresh_token");
- token_service.LoadCredentials(account_id);
+ token_service.LoadCredentials(account_id, /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ("TokenService::LoadCredentials",
@@ -1512,7 +1409,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest,
TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ExtractCredentials) {
InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice);
- oauth2_service_delegate_->LoadCredentials(CoreAccountId());
+ oauth2_service_delegate_->LoadCredentials(CoreAccountId(),
+ /*is_syncing=*/false);
const CoreAccountId account_id("account_id");
// Create another token service
@@ -1522,7 +1420,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ExtractCredentials) {
std::make_unique<FakeProfileOAuth2TokenServiceDelegate>();
FakeProfileOAuth2TokenServiceDelegate* other_delegate = delegate.get();
ProfileOAuth2TokenService other_token_service(&prefs, std::move(delegate));
- other_token_service.LoadCredentials(CoreAccountId());
+ other_token_service.LoadCredentials(CoreAccountId(), /*is_syncing=*/false);
// Add credentials to the first token service delegate.
oauth2_service_delegate_->UpdateCredentials(account_id, "token");
diff --git a/chromium/components/signin/internal/identity_manager/primary_account_manager.cc b/chromium/components/signin/internal/identity_manager/primary_account_manager.cc
index 9d0a7c78946..0f5f38b24f3 100644
--- a/chromium/components/signin/internal/identity_manager/primary_account_manager.cc
+++ b/chromium/components/signin/internal/identity_manager/primary_account_manager.cc
@@ -87,6 +87,7 @@ void PrimaryAccountManager::Initialize(PrefService* local_state) {
!pref_account_id.empty());
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
if (!pref_account_id.empty()) {
if (account_tracker_service_->GetMigrationState() ==
AccountTrackerService::MIGRATION_IN_PROGRESS) {
@@ -100,6 +101,7 @@ void PrimaryAccountManager::Initialize(PrefService* local_state) {
}
}
}
+#endif
bool consented =
client_->GetPrefs()->GetBoolean(prefs::kGoogleServicesConsentedToSync);
@@ -120,7 +122,8 @@ void PrimaryAccountManager::Initialize(PrefService* local_state) {
// token service.
token_service_->AddObserver(this);
token_service_->LoadCredentials(
- GetPrimaryAccountId(signin::ConsentLevel::kSignin));
+ GetPrimaryAccountId(signin::ConsentLevel::kSignin),
+ HasPrimaryAccount(signin::ConsentLevel::kSync));
}
bool PrimaryAccountManager::IsInitialized() const {
@@ -364,10 +367,12 @@ void PrimaryAccountManager::FirePrimaryAccountChanged(
void PrimaryAccountManager::OnRefreshTokensLoaded() {
token_service_->RemoveObserver(this);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
if (account_tracker_service_->GetMigrationState() ==
AccountTrackerService::MIGRATION_IN_PROGRESS) {
account_tracker_service_->SetMigrationDone();
}
+#endif
// Remove account information from the account tracker service if needed.
if (token_service_->HasLoadCredentialsFinishedWithNoErrors()) {
@@ -384,26 +389,4 @@ void PrimaryAccountManager::OnRefreshTokensLoaded() {
}
}
}
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
- // On non-ChromeOS platforms, account ID migration started in 2015. Data is
- // most probably corrupted for profiles that were not migrated. Clear all
- // accounts to fix this state.
-
- // TODO(crbug.com/1224899): This code should be removed once migration
- // finishes.
- if (account_tracker_service_->GetMigrationState() ==
- AccountTrackerService::MIGRATION_NOT_STARTED) {
- // Clear the primary account if any.
- ClearPrimaryAccount(signin_metrics::ACCOUNT_ID_MIGRATION,
- signin_metrics::SignoutDelete::kIgnoreMetric);
- // Clean all remaining account information from the account tracker.
- for (const auto& account : account_tracker_service_->GetAccounts())
- account_tracker_service_->RemoveAccount(account.account_id);
- account_tracker_service_->SetMigrationDone();
- client_->GetPrefs()->CommitPendingWrite();
- }
- DCHECK_EQ(AccountTrackerService::MIGRATION_DONE,
- account_tracker_service_->GetMigrationState());
-#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
}
diff --git a/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc b/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
index fda15ab8f60..6641ccc473f 100644
--- a/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
+++ b/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
@@ -293,11 +293,10 @@ TEST_F(PrimaryAccountManagerTest,
EXPECT_EQ(account_id, manager_->GetPrimaryAccountId(ConsentLevel::kSync));
}
-TEST_F(PrimaryAccountManagerTest, GaiaIdMigration) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
+TEST_F(PrimaryAccountManagerTest, GaiaIdMigration) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(switches::kAccountIdMigration);
-#endif
ASSERT_EQ(AccountTrackerService::MIGRATION_DONE,
account_tracker()->GetMigrationState());
@@ -327,10 +326,8 @@ TEST_F(PrimaryAccountManagerTest, GaiaIdMigration) {
}
TEST_F(PrimaryAccountManagerTest, GaiaIdMigrationCrashInTheMiddle) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(switches::kAccountIdMigration);
-#endif
ASSERT_EQ(AccountTrackerService::MIGRATION_DONE,
account_tracker()->GetMigrationState());
@@ -361,38 +358,6 @@ TEST_F(PrimaryAccountManagerTest, GaiaIdMigrationCrashInTheMiddle) {
EXPECT_EQ(AccountTrackerService::MIGRATION_DONE,
account_tracker()->GetMigrationState());
}
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-// Test that force migrating the account id to Gaia ID is finished.
-TEST_F(PrimaryAccountManagerTest, GaiaIdMigration_ForceAllAccounts) {
- ASSERT_EQ(AccountTrackerService::MIGRATION_DONE,
- account_tracker()->GetMigrationState());
- std::string email = "user@gmail.com";
- std::string gaia_id = "account_gaia_id";
-
- PrefService* client_prefs = signin_client()->GetPrefs();
- client_prefs->SetInteger(prefs::kAccountIdMigrationState,
- AccountTrackerService::MIGRATION_NOT_STARTED);
- ListPrefUpdate update(client_prefs, prefs::kAccountInfo);
- update->ClearList();
- base::Value dict(base::Value::Type::DICTIONARY);
- dict.SetStringKey("account_id", email);
- dict.SetStringKey("email", email);
- update->Append(std::move(dict));
-
- account_tracker()->ResetForTesting();
-
- client_prefs->SetString(prefs::kGoogleServicesAccountId, email);
-
- CreatePrimaryAccountManager();
- base::RunLoop().RunUntilIdle();
-
- EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSignin));
- EXPECT_EQ("", user_prefs_.GetString(prefs::kGoogleServicesAccountId));
- EXPECT_TRUE(account_tracker()->GetAccounts().empty());
- EXPECT_EQ(AccountTrackerService::MIGRATION_DONE,
- account_tracker()->GetMigrationState());
-}
#endif
TEST_F(PrimaryAccountManagerTest, RestoreFromPrefsConsented) {
diff --git a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc
index 59ea1009115..40b3b6d9648 100644
--- a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc
+++ b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc
@@ -96,27 +96,9 @@ bool PrimaryAccountMutatorImpl::CanTransitionFromSyncToSigninConsentLevel()
const {
switch (account_consistency_) {
case AccountConsistencyMethod::kDice:
- // If DICE is enabled, then adding and removing accounts is handled from
- // the Google web services. This means that the user needs to be signed
- // in to the their Google account on the web in order to be able to sign
- // out of that accounts. As in most cases, the Google auth cookies are
- // are derived from the refresh token, which means that the user is signed
- // out of their Google account on the web when the primary account is in
- // an auth error. It is therefore important to clear all accounts when
- // the user revokes their sync consent for a primary account that is in
- // an auth error as otherwise the user will not be able to remove it from
- // Chrome.
- //
- // TODO(msarda): The logic in this function is platform specific and we
- // should consider moving it to |SigninManager|.
- return !token_service_->RefreshTokenHasError(
- primary_account_manager_->GetPrimaryAccountId(ConsentLevel::kSync));
+ return true;
case AccountConsistencyMethod::kMirror:
#if BUILDFLAG(IS_CHROMEOS_LACROS)
- // TODO(crbug.com/1217645): Consider making this return false only for the
- // main profile and return true, otherwise. This requires implementing
- // ProfileOAuth2TokenServiceDelegateChromeOS::Revoke* and it's not clear
- // what these functions should do.
return true;
#elif BUILDFLAG(IS_ANDROID)
// Android supports users being signed in with sync disabled, with the
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.cc
index a98fecb0302..44dea913b79 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.cc
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.cc
@@ -255,12 +255,13 @@ void ProfileOAuth2TokenService::SetRefreshTokenRevokedFromSourceCallback(
}
void ProfileOAuth2TokenService::LoadCredentials(
- const CoreAccountId& primary_account_id) {
+ const CoreAccountId& primary_account_id,
+ bool is_syncing) {
DCHECK_EQ(SourceForRefreshTokenOperation::kUnknown,
update_refresh_token_source_);
update_refresh_token_source_ =
SourceForRefreshTokenOperation::kTokenService_LoadCredentials;
- GetDelegate()->LoadCredentials(primary_account_id);
+ GetDelegate()->LoadCredentials(primary_account_id, is_syncing);
}
void ProfileOAuth2TokenService::UpdateCredentials(
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.h
index 2177cc9a9d8..a9bbef2cdd6 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.h
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.h
@@ -187,7 +187,9 @@ class ProfileOAuth2TokenService : public OAuth2AccessTokenManager::Delegate,
// For a regular profile, the primary account id comes from
// PrimaryAccountManager.
// For a supervised user, the id comes from SupervisedUserService.
- void LoadCredentials(const CoreAccountId& primary_account_id);
+ // |is_syncing| whether the primary account has sync consent.
+ void LoadCredentials(const CoreAccountId& primary_account_id,
+ bool is_syncing);
// Returns true if LoadCredentials finished with no errors.
bool HasLoadCredentialsFinishedWithNoErrors();
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc
index 9d9220a7cdb..79f98e6c43d 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc
@@ -25,7 +25,7 @@
#include "components/signin/public/webdata/token_web_data.h"
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h"
#endif
@@ -53,16 +53,23 @@ std::unique_ptr<ProfileOAuth2TokenServiceIOSDelegate> CreateIOSOAuthDelegate(
signin_client, std::move(device_accounts_provider),
account_tracker_service);
}
-#elif BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_CHROMEOS)
std::unique_ptr<ProfileOAuth2TokenServiceDelegate> CreateCrOsOAuthDelegate(
SigninClient* signin_client,
AccountTrackerService* account_tracker_service,
network::NetworkConnectionTracker* network_connection_tracker,
account_manager::AccountManagerFacade* account_manager_facade,
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ bool delete_signin_cookies_on_exit,
+#endif
bool is_regular_profile) {
return std::make_unique<signin::ProfileOAuth2TokenServiceDelegateChromeOS>(
signin_client, account_tracker_service, network_connection_tracker,
- account_manager_facade, is_regular_profile);
+ account_manager_facade,
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ delete_signin_cookies_on_exit,
+#endif
+ is_regular_profile);
}
#elif BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -101,12 +108,14 @@ CreateOAuth2TokenServiceDelegate(
AccountTrackerService* account_tracker_service,
signin::AccountConsistencyMethod account_consistency,
SigninClient* signin_client,
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade* account_manager_facade,
bool is_regular_profile,
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+#endif // BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
bool delete_signin_cookies_on_exit,
+#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
scoped_refptr<TokenWebData> token_web_data,
#endif
#if BUILDFLAG(IS_IOS)
@@ -123,10 +132,14 @@ CreateOAuth2TokenServiceDelegate(
return CreateIOSOAuthDelegate(signin_client,
std::move(device_accounts_provider),
account_tracker_service);
-#elif BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_CHROMEOS)
return CreateCrOsOAuthDelegate(signin_client, account_tracker_service,
network_connection_tracker,
- account_manager_facade, is_regular_profile);
+ account_manager_facade,
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ delete_signin_cookies_on_exit,
+#endif
+ is_regular_profile);
#elif BUILDFLAG(ENABLE_DICE_SUPPORT)
// Fall back to |MutableProfileOAuth2TokenServiceDelegate| on all platforms
// other than Android, iOS, and Chrome OS (Ash and Lacros).
@@ -150,14 +163,16 @@ std::unique_ptr<ProfileOAuth2TokenService> BuildProfileOAuth2TokenService(
AccountTrackerService* account_tracker_service,
network::NetworkConnectionTracker* network_connection_tracker,
signin::AccountConsistencyMethod account_consistency,
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade* account_manager_facade,
bool is_regular_profile,
+#endif // BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+ bool delete_signin_cookies_on_exit,
#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- bool delete_signin_cookies_on_exit,
scoped_refptr<TokenWebData> token_web_data,
-#endif
+#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
#if BUILDFLAG(IS_IOS)
std::unique_ptr<DeviceAccountsProvider> device_accounts_provider,
#endif
@@ -179,11 +194,14 @@ std::unique_ptr<ProfileOAuth2TokenService> BuildProfileOAuth2TokenService(
pref_service,
CreateOAuth2TokenServiceDelegate(
account_tracker_service, account_consistency, signin_client,
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager_facade, is_regular_profile,
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+ delete_signin_cookies_on_exit,
+#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- delete_signin_cookies_on_exit, token_web_data,
+ token_web_data,
#endif
#if BUILDFLAG(IS_IOS)
std::move(device_accounts_provider),
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h
index ffbb90f818b..85978f99c43 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h
@@ -41,25 +41,27 @@ class NetworkConnectionTracker;
class TokenWebData;
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
namespace account_manager {
class AccountManagerFacade;
}
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
std::unique_ptr<ProfileOAuth2TokenService> BuildProfileOAuth2TokenService(
PrefService* pref_service,
AccountTrackerService* account_tracker_service,
network::NetworkConnectionTracker* network_connection_tracker,
signin::AccountConsistencyMethod account_consistency,
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade* account_manager_facade,
bool is_regular_profile,
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+#endif // BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
bool delete_signin_cookies_on_exit,
+#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
scoped_refptr<TokenWebData> token_web_data,
-#endif
+#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
#if BUILDFLAG(IS_IOS)
std::unique_ptr<DeviceAccountsProvider> device_accounts_provider,
#endif
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc
index d73c7e16286..925866b9d79 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc
@@ -123,7 +123,8 @@ const net::BackoffEntry* ProfileOAuth2TokenServiceDelegate::BackoffEntry()
}
void ProfileOAuth2TokenServiceDelegate::LoadCredentials(
- const CoreAccountId& primary_account_id) {
+ const CoreAccountId& primary_account_id,
+ bool is_syncing) {
NOTREACHED()
<< "ProfileOAuth2TokenServiceDelegate does not load credentials. "
"Subclasses that need to load credentials must provide "
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h
index 7cbe1702118..fb6cfe1dcc9 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h
@@ -116,7 +116,8 @@ class ProfileOAuth2TokenServiceDelegate {
// is initialized. Default implementation is NOTREACHED - subsclasses that
// are used by the ProfileOAuth2TokenService must provide an implementation
// for this method.
- virtual void LoadCredentials(const CoreAccountId& primary_account_id);
+ virtual void LoadCredentials(const CoreAccountId& primary_account_id,
+ bool is_syncing);
// Returns the state of the load credentials operation.
signin::LoadCredentialsState load_credentials_state() const {
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc
index ef5e58dcf30..010b083f5a6 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc
@@ -160,20 +160,6 @@ ProfileOAuth2TokenServiceDelegateAndroid::
env, reinterpret_cast<intptr_t>(this),
account_tracker_service_->GetJavaObject());
java_ref_.Reset(env, local_java_ref.obj());
-
- if (account_tracker_service_->GetMigrationState() ==
- AccountTrackerService::MIGRATION_IN_PROGRESS) {
- std::vector<CoreAccountId> accounts = GetAccounts();
- std::vector<CoreAccountId> accounts_id;
- for (auto account_name : accounts) {
- std::string email = account_name.ToString();
- AccountInfo account_info =
- account_tracker_service_->FindAccountInfoByEmail(email);
- DCHECK(!account_info.gaia.empty());
- accounts_id.push_back(CoreAccountId::FromGaiaId(account_info.gaia));
- }
- SetAccounts(accounts_id);
- }
}
ProfileOAuth2TokenServiceDelegateAndroid::
@@ -365,14 +351,6 @@ void ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList(
if (!base::Contains(curr_ids, info.account_id))
account_tracker_service_->RemoveAccount(info.account_id);
}
-
- // No need to wait for PrimaryAccountManager to finish migration if not signed
- // in.
- if (account_tracker_service_->GetMigrationState() ==
- AccountTrackerService::MIGRATION_IN_PROGRESS &&
- !signed_in_account_id.has_value()) {
- account_tracker_service_->SetMigrationDone();
- }
}
bool ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList(
@@ -451,7 +429,8 @@ void ProfileOAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() {
}
void ProfileOAuth2TokenServiceDelegateAndroid::LoadCredentials(
- const CoreAccountId& primary_account_id) {
+ const CoreAccountId& primary_account_id,
+ bool is_syncing) {
DCHECK_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED,
load_credentials_state());
set_load_credentials_state(
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h
index 4fddcb3c27c..e452292110c 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h
@@ -54,7 +54,8 @@ class ProfileOAuth2TokenServiceDelegateAndroid
// POA2TService aware accounts.
void RevokeAllCredentials() override;
- void LoadCredentials(const CoreAccountId& primary_account_id) override;
+ void LoadCredentials(const CoreAccountId& primary_account_id,
+ bool is_syncing) override;
void ReloadAllAccountsFromSystemWithPrimaryAccount(
const absl::optional<CoreAccountId>& primary_account_id) override;
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc
index 4718b96e9dc..9b5045e9779 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc
@@ -143,6 +143,9 @@ ProfileOAuth2TokenServiceDelegateChromeOS::
AccountTrackerService* account_tracker_service,
network::NetworkConnectionTracker* network_connection_tracker,
account_manager::AccountManagerFacade* account_manager_facade,
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ bool delete_signin_cookies_on_exit,
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
bool is_regular_profile)
: signin_client_(signin_client),
account_tracker_service_(account_tracker_service),
@@ -150,6 +153,9 @@ ProfileOAuth2TokenServiceDelegateChromeOS::
account_manager_facade_(account_manager_facade),
backoff_entry_(&kBackoffPolicy),
backoff_error_(GoogleServiceAuthError::NONE),
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ delete_signin_cookies_on_exit_(delete_signin_cookies_on_exit),
+#endif
is_regular_profile_(is_regular_profile),
weak_factory_(this) {
network_connection_tracker_->AddNetworkConnectionObserver(this);
@@ -285,7 +291,8 @@ ProfileOAuth2TokenServiceDelegateChromeOS::GetAccounts() const {
}
void ProfileOAuth2TokenServiceDelegateChromeOS::LoadCredentials(
- const CoreAccountId& primary_account_id) {
+ const CoreAccountId& primary_account_id,
+ bool is_syncing) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (load_credentials_state() !=
@@ -311,6 +318,20 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::LoadCredentials(
DCHECK(account_manager_facade_);
account_manager_facade_->AddObserver(this);
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ // On Lacros, signin cookies can only be cleared for non-syncing
+ // secondary profiles, because:
+ // - the main profile cannot be signed out,
+ // - clearing cookie does not turn sync off
+ // Additionally, there is no way for Chrome to "invalidate" a token. In
+ // particular, the "sync paused" state does not exist.
+ bool revoke_all_tokens =
+ delete_signin_cookies_on_exit_ && !is_syncing &&
+ !signin_client_->GetInitialPrimaryAccount().has_value();
+ if (revoke_all_tokens)
+ RevokeAllCredentials();
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+
account_manager_facade_->GetAccounts(
base::BindOnce(&ProfileOAuth2TokenServiceDelegateChromeOS::OnGetAccounts,
weak_factory_.GetWeakPtr()));
@@ -515,8 +536,17 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::OnAccountRemoved(
void ProfileOAuth2TokenServiceDelegateChromeOS::RevokeCredentials(
const CoreAccountId& account_id) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ ScopedBatchChange batch(this);
+ AccountInfo account_info =
+ account_tracker_service_->GetAccountInfo(account_id);
+ DCHECK(!account_info.IsEmpty());
+ signin_client_->RemoveAccount(account_manager::AccountKey{
+ account_info.gaia, account_manager::AccountType::kGaia});
+#else
// Signing out of Chrome is not possible on Chrome OS Ash / Lacros.
NOTREACHED();
+#endif
}
void ProfileOAuth2TokenServiceDelegateChromeOS::RevokeAllCredentials() {
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h
index 76d3e6b9b86..eb6487ec1de 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h
@@ -36,6 +36,11 @@ class ProfileOAuth2TokenServiceDelegateChromeOS
AccountTrackerService* account_tracker_service,
network::NetworkConnectionTracker* network_connection_tracker,
account_manager::AccountManagerFacade* account_manager_facade,
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ // |delete_signin_cookies_on_exit| is used on startup, in case the
+ // cookies were not properly cleared on last exit.
+ bool delete_signin_cookies_on_exit,
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
bool is_regular_profile);
ProfileOAuth2TokenServiceDelegateChromeOS(
@@ -59,7 +64,8 @@ class ProfileOAuth2TokenServiceDelegateChromeOS
GoogleServiceAuthError GetAuthError(
const CoreAccountId& account_id) const override;
std::vector<CoreAccountId> GetAccounts() const override;
- void LoadCredentials(const CoreAccountId& primary_account_id) override;
+ void LoadCredentials(const CoreAccountId& primary_account_id,
+ bool is_syncing) override;
void UpdateCredentials(const CoreAccountId& account_id,
const std::string& refresh_token) override;
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
@@ -123,6 +129,10 @@ class ProfileOAuth2TokenServiceDelegateChromeOS
net::BackoffEntry backoff_entry_;
GoogleServiceAuthError backoff_error_;
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ const bool delete_signin_cookies_on_exit_;
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+
// Is |this| attached to a regular (non-Signin && non-LockScreen) Profile.
const bool is_regular_profile_;
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc
index 66bfecfb657..e4c68b45b08 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc
@@ -187,6 +187,16 @@ class MockProfileOAuth2TokenServiceObserver
} // namespace
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+class MockTestSigninClient : public testing::StrictMock<TestSigninClient> {
+ public:
+ MockTestSigninClient(PrefService* pref_service)
+ : testing::StrictMock<TestSigninClient>(pref_service) {}
+
+ MOCK_METHOD(void, RemoveAllAccounts, (), (override));
+};
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+
class ProfileOAuth2TokenServiceDelegateChromeOSTest : public testing::Test {
public:
ProfileOAuth2TokenServiceDelegateChromeOSTest() {}
@@ -204,7 +214,12 @@ class ProfileOAuth2TokenServiceDelegateChromeOSTest : public testing::Test {
AccountTrackerService::RegisterPrefs(pref_service_.registry());
AccountManager::RegisterPrefs(pref_service_.registry());
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ client_ = std::make_unique<MockTestSigninClient>(&pref_service_);
+#else
client_ = std::make_unique<TestSigninClient>(&pref_service_);
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+
account_manager_.Initialize(tmp_dir_.GetPath(),
client_->GetURLLoaderFactory(),
immediate_callback_runner_);
@@ -220,13 +235,25 @@ class ProfileOAuth2TokenServiceDelegateChromeOSTest : public testing::Test {
account_info_ = CreateAccountInfoTestFixture(kGaiaId, kUserEmail);
account_tracker_service_.SeedAccountInfo(account_info_);
+ ResetProfileOAuth2TokenServiceDelegateChromeOS(
+ /*delete_signin_cookies_on_exit=*/false, /*is_syncing=*/false);
+ }
+
+ void ResetProfileOAuth2TokenServiceDelegateChromeOS(
+ bool delete_signin_cookies_on_exit,
+ bool is_syncing) {
+ delegate_.reset();
delegate_ = std::make_unique<ProfileOAuth2TokenServiceDelegateChromeOS>(
client_.get(), &account_tracker_service_,
network::TestNetworkConnectionTracker::GetInstance(),
- account_manager_facade_.get(), true /* is_regular_profile */);
+ account_manager_facade_.get(),
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ delete_signin_cookies_on_exit,
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+ /*is_regular_profile=*/true);
LoadCredentialsAndWaitForCompletion(
- account_info_.account_id /* primary_account_id */);
+ /*primary_account_id=*/account_info_.account_id, is_syncing);
}
account_manager::AccountKey gaia_account_key() const {
@@ -265,12 +292,13 @@ class ProfileOAuth2TokenServiceDelegateChromeOSTest : public testing::Test {
}
void LoadCredentialsAndWaitForCompletion(
- const CoreAccountId& primary_account_id) {
+ const CoreAccountId& primary_account_id,
+ bool is_syncing) {
MockProfileOAuth2TokenServiceObserver observer(delegate_.get());
base::RunLoop run_loop;
EXPECT_CALL(observer, OnRefreshTokensLoaded())
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- delegate_->LoadCredentials(primary_account_id);
+ delegate_->LoadCredentials(primary_account_id, is_syncing);
run_loop.Run();
}
@@ -363,9 +391,61 @@ class ProfileOAuth2TokenServiceDelegateChromeOSTest : public testing::Test {
base::BindRepeating(
[](base::OnceClosure closure) -> void { std::move(closure).Run(); });
sync_preferences::TestingPrefServiceSyncable pref_service_;
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ std::unique_ptr<MockTestSigninClient> client_;
+#else
std::unique_ptr<TestSigninClient> client_;
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
};
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+// Tests |RemoveAllAccounts| is not called on startup if
+// |delete_signin_cookies_on_exit| is false.
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+ CookieNotClearedOnStartup) {
+ EXPECT_FALSE(client_->GetInitialPrimaryAccount().has_value());
+ EXPECT_CALL(*(client_.get()), RemoveAllAccounts()).Times(0);
+ ResetProfileOAuth2TokenServiceDelegateChromeOS(
+ /*delete_signin_cookies_on_exit=*/false, /*is_syncing=*/false);
+}
+
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+ CookieClearedOnStartupSecondaryNonSyncingProfile) {
+ EXPECT_FALSE(client_->GetInitialPrimaryAccount().has_value());
+ EXPECT_CALL(*(client_.get()), RemoveAllAccounts()).Times(1);
+ ResetProfileOAuth2TokenServiceDelegateChromeOS(
+ /*delete_signin_cookies_on_exit=*/true, /*is_syncing=*/false);
+}
+
+// Test that |delete_signin_cookies_on_exit| does nothing if the profile
+// is syncing.
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+ CookieNotClearedOnStartupSyncingProfile) {
+ EXPECT_FALSE(client_->GetInitialPrimaryAccount().has_value());
+ EXPECT_CALL(*(client_.get()), RemoveAllAccounts()).Times(0);
+ ResetProfileOAuth2TokenServiceDelegateChromeOS(
+ /*delete_signin_cookies_on_exit=*/true, /*is_syncing=*/true);
+}
+
+// Test that |delete_signin_cookies_on_exit| does nothing for the main profile.
+TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
+ CookieNotClearedOnStartupMainProfile) {
+ // The delegate figures out that the profile is the main profile by checking
+ // if the |SigninClient| has an initial primary account set.
+ client_->SetInitialPrimaryAccountForTests(
+ account_manager::Account{
+ account_manager::AccountKey{kGaiaId,
+ account_manager::AccountType::kGaia},
+ kUserEmail},
+ /*is_child=*/false);
+ EXPECT_TRUE(client_->GetInitialPrimaryAccount().has_value());
+
+ EXPECT_CALL(*(client_.get()), RemoveAllAccounts()).Times(0);
+ ResetProfileOAuth2TokenServiceDelegateChromeOS(
+ /*delete_signin_cookies_on_exit=*/true, /*is_syncing=*/false);
+}
+#endif
+
// Refresh tokens should load successfully for non-regular (Signin and Lock
// Screen) Profiles.
TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
@@ -378,12 +458,17 @@ TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
auto delegate = std::make_unique<ProfileOAuth2TokenServiceDelegateChromeOS>(
client_.get(), &account_tracker_service_,
network::TestNetworkConnectionTracker::GetInstance(),
- account_manager_facade_.get(), false /* is_regular_profile */);
+ account_manager_facade_.get(),
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ /*delete_signin_cookies_on_exit=*/false,
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+ /*is_regular_profile=*/false);
TestOAuth2TokenServiceObserver observer(delegate.get());
// Test that LoadCredentials works as expected.
EXPECT_FALSE(observer.refresh_tokens_loaded_);
- delegate->LoadCredentials(CoreAccountId() /* primary_account_id */);
+ delegate->LoadCredentials(CoreAccountId() /* primary_account_id */,
+ /*is_syncing=*/false);
EXPECT_TRUE(observer.refresh_tokens_loaded_);
EXPECT_EQ(LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS,
delegate->load_credentials_state());
@@ -601,8 +686,13 @@ TEST_F(ProfileOAuth2TokenServiceDelegateChromeOSTest,
auto delegate = std::make_unique<ProfileOAuth2TokenServiceDelegateChromeOS>(
client_.get(), &account_tracker_service_,
network::TestNetworkConnectionTracker::GetInstance(),
- account_manager_facade.get(), true /* is_regular_profile */);
- delegate->LoadCredentials(account1.account_id /* primary_account_id */);
+ account_manager_facade.get(),
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ /*delete_signin_cookies_on_exit=*/false,
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS
+ /*is_regular_profile=*/true);
+ delegate->LoadCredentials(account1.account_id /* primary_account_id */,
+ /*is_syncing=*/false);
TestOAuth2TokenServiceObserver observer(delegate.get());
// Wait until AccountManager is fully initialized.
task_environment_.RunUntilIdle();
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h
index f33ec8a335a..1750fde31e2 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h
@@ -46,7 +46,8 @@ class ProfileOAuth2TokenServiceIOSDelegate
void UpdateAuthError(const CoreAccountId& account_id,
const GoogleServiceAuthError& error) override;
- void LoadCredentials(const CoreAccountId& primary_account_id) override;
+ void LoadCredentials(const CoreAccountId& primary_account_id,
+ bool is_syncing) override;
std::vector<CoreAccountId> GetAccounts() const override;
// This method should not be called when using shared authentication.
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm
index 83622a0a224..3b4d9bf18bb 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm
@@ -178,7 +178,8 @@ void ProfileOAuth2TokenServiceIOSDelegate::Shutdown() {
}
void ProfileOAuth2TokenServiceIOSDelegate::LoadCredentials(
- const CoreAccountId& primary_account_id) {
+ const CoreAccountId& primary_account_id,
+ bool is_syncing) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED,
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm
index 016d758b912..dc5428c3842 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm
@@ -117,7 +117,8 @@ class ProfileOAuth2TokenServiceIOSDelegateTest
TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest,
LoadRevokeCredentialsOneAccount) {
ProviderAccount account = fake_provider_->AddAccount("gaia_1", "email_1@x");
- oauth2_delegate_->LoadCredentials(GetAccountId(account));
+ oauth2_delegate_->LoadCredentials(GetAccountId(account),
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, token_available_count_);
EXPECT_EQ(1, tokens_loaded_count_);
@@ -140,7 +141,8 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest,
ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
ProviderAccount account2 = fake_provider_->AddAccount("gaia_2", "email_2@x");
ProviderAccount account3 = fake_provider_->AddAccount("gaia_3", "email_3@x");
- oauth2_delegate_->LoadCredentials(GetAccountId(account1));
+ oauth2_delegate_->LoadCredentials(GetAccountId(account1),
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3, token_available_count_);
EXPECT_EQ(1, tokens_loaded_count_);
@@ -171,7 +173,7 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest,
LoadCredentialsPrimaryAccountMissing) {
CoreAccountId primary_account =
account_tracker_.SeedAccountInfo("gaia_1", "email_1@x");
- oauth2_delegate_->LoadCredentials(primary_account);
+ oauth2_delegate_->LoadCredentials(primary_account, /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, token_available_count_);
EXPECT_EQ(1, tokens_loaded_count_);
@@ -202,7 +204,8 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, ReloadAllAccountsFromSystem) {
ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
ProviderAccount account2 = fake_provider_->AddAccount("gaia_2", "email_2@x");
ProviderAccount account3 = fake_provider_->AddAccount("gaia_3", "email_3@x");
- oauth2_delegate_->LoadCredentials(GetAccountId(account1));
+ oauth2_delegate_->LoadCredentials(GetAccountId(account1),
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
// Change the accounts.
@@ -246,7 +249,8 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest,
TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, StartRequestSuccess) {
ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
- oauth2_delegate_->LoadCredentials(GetAccountId(account1));
+ oauth2_delegate_->LoadCredentials(GetAccountId(account1),
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
// Fetch access tokens.
@@ -270,7 +274,8 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, StartRequestSuccess) {
TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, StartRequestFailure) {
ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
- oauth2_delegate_->LoadCredentials(GetAccountId(account1));
+ oauth2_delegate_->LoadCredentials(GetAccountId(account1),
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
// Fetch access tokens.
@@ -297,7 +302,8 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, StartRequestFailure) {
TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest,
UpdateAuthErrorAfterRevokeCredentials) {
ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
- oauth2_delegate_->LoadCredentials(GetAccountId(account1));
+ oauth2_delegate_->LoadCredentials(GetAccountId(account1),
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
ResetObserverCounts();
@@ -315,7 +321,8 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest,
TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, GetAuthError) {
// Accounts have no error by default.
ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
- oauth2_delegate_->LoadCredentials(GetAccountId(account1));
+ oauth2_delegate_->LoadCredentials(GetAccountId(account1),
+ /*is_syncing=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
oauth2_delegate_->GetAuthError(GetAccountId(account1)));
diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_unittest.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_unittest.cc
index 91cb7d16172..4bb13098293 100644
--- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_unittest.cc
+++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_unittest.cc
@@ -155,7 +155,8 @@ TEST_F(ProfileOAuth2TokenServiceTest, GetAccounts) {
EXPECT_TRUE(accounts.empty());
// Load tokens from disk.
- oauth2_service_->GetDelegate()->LoadCredentials(CoreAccountId());
+ oauth2_service_->GetDelegate()->LoadCredentials(CoreAccountId(),
+ /*is_syncing=*/false);
// |account_id_| should now be visible in the accounts.
accounts = oauth2_service_->GetAccounts();
diff --git a/chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.cc b/chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.cc
index 74dd41c91e9..2742c8f5217 100644
--- a/chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.cc
+++ b/chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.cc
@@ -76,7 +76,8 @@ TestProfileOAuth2TokenServiceDelegateChromeOS::GetAccounts() const {
}
void TestProfileOAuth2TokenServiceDelegateChromeOS::LoadCredentials(
- const CoreAccountId& primary_account_id) {
+ const CoreAccountId& primary_account_id,
+ bool is_syncing) {
// In tests |LoadCredentials| may be called twice, in this case we call
// |FireRefreshTokensLoaded| again to notify that credentials are loaded.
if (load_credentials_state() ==
@@ -92,7 +93,7 @@ void TestProfileOAuth2TokenServiceDelegateChromeOS::LoadCredentials(
set_load_credentials_state(
signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS);
- delegate_->LoadCredentials(primary_account_id);
+ delegate_->LoadCredentials(primary_account_id, is_syncing);
}
void TestProfileOAuth2TokenServiceDelegateChromeOS::UpdateCredentials(
diff --git a/chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.h b/chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.h
index 6a990dbaf44..a37f65f6162 100644
--- a/chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.h
+++ b/chromium/components/signin/internal/identity_manager/test_profile_oauth2_token_service_delegate_chromeos.h
@@ -50,7 +50,8 @@ class TestProfileOAuth2TokenServiceDelegateChromeOS
GoogleServiceAuthError GetAuthError(
const CoreAccountId& account_id) const override;
std::vector<CoreAccountId> GetAccounts() const override;
- void LoadCredentials(const CoreAccountId& primary_account_id) override;
+ void LoadCredentials(const CoreAccountId& primary_account_id,
+ bool is_syncing) override;
void UpdateCredentials(const CoreAccountId& account_id,
const std::string& refresh_token) override;
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
diff --git a/chromium/components/signin/ios/browser/account_consistency_service.mm b/chromium/components/signin/ios/browser/account_consistency_service.mm
index 03de401f8cb..9eb8f3a7595 100644
--- a/chromium/components/signin/ios/browser/account_consistency_service.mm
+++ b/chromium/components/signin/ios/browser/account_consistency_service.mm
@@ -52,7 +52,7 @@ const char* kGaiaDomain = "accounts.google.com";
// Returns the registered, organization-identifying host, but no subdomains,
// from the given GURL. Returns an empty string if the GURL is invalid.
static std::string GetDomainFromUrl(const GURL& url) {
- if (gaia::IsGaiaSignonRealm(url.DeprecatedGetOriginAsURL())) {
+ if (gaia::HasGaiaSchemeHostPort(url)) {
return kGaiaDomain;
}
return net::registry_controlled_domains::GetDomainAndRegistry(
@@ -202,7 +202,7 @@ void AccountConsistencyService::AccountConsistencyHandler::ShouldAllowResponse(
{url, GURL(kGoogleUrl)});
}
- if (!gaia::IsGaiaSignonRealm(url.DeprecatedGetOriginAsURL())) {
+ if (!gaia::HasGaiaSchemeHostPort(url)) {
std::move(callback).Run(PolicyDecision::Allow());
return;
}
@@ -309,7 +309,7 @@ void AccountConsistencyService::AccountConsistencyHandler::PageLoaded(
}
if (delegate_ && show_consistency_web_signin_ &&
- gaia::IsGaiaSignonRealm(url.DeprecatedGetOriginAsURL())) {
+ gaia::HasGaiaSchemeHostPort(url)) {
delegate_->OnShowConsistencyPromo(url, web_state);
}
show_consistency_web_signin_ = false;
diff --git a/chromium/components/signin/public/android/BUILD.gn b/chromium/components/signin/public/android/BUILD.gn
index f6cd06216cb..17bfae08775 100644
--- a/chromium/components/signin/public/android/BUILD.gn
+++ b/chromium/components/signin/public/android/BUILD.gn
@@ -5,10 +5,11 @@ android_library("java") {
"$google_play_services_package:google_play_services_auth_base_java",
"$google_play_services_package:google_play_services_base_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/externalauth/android:java",
"//net/android:net_java",
"//third_party/android_deps:chromium_play_services_availability_java",
- "//third_party/android_deps:guava_android_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
@@ -106,6 +107,7 @@ android_library("signin_java_test_support") {
"//third_party/mockito:mockito_java",
]
sources = [
+ "java/src/org/chromium/components/signin/test/util/AccountCapabilitiesBuilder.java",
"java/src/org/chromium/components/signin/test/util/AccountCapabilitiesFetcherTestUtil.java",
"java/src/org/chromium/components/signin/test/util/AccountHolder.java",
"java/src/org/chromium/components/signin/test/util/AccountManagerFacadeUtil.java",
@@ -135,7 +137,6 @@ android_library("javatests") {
"//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/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
diff --git a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java
index d6190ad2755..8472a2770b7 100644
--- a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java
+++ b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java
@@ -279,44 +279,6 @@ public class AccountManagerFacadeImplTest {
}
@Test
- public void testCanOfferExtendedSyncPromosForUnknownAccount() {
- final Account account = AccountUtils.createAccountFromName("test@gmail.com");
-
- Assert.assertFalse(mFacade.canOfferExtendedSyncPromos(account).isPresent());
- }
-
- @Test
- public void testAccountCanNotOfferExtendedSyncPromos() {
- final AccountHolder accountHolder = AccountHolder.createFromEmail("test@gmail.com");
- mDelegate.addAccount(accountHolder);
-
- Assert.assertFalse(mFacade.canOfferExtendedSyncPromos(accountHolder.getAccount()).get());
- }
-
- @Test
- public void testAccountCanNotOfferExtendedSyncPromosWhenExceptionCodeReturns() {
- final AccountHolder accountHolder = AccountHolder.createFromEmail("test@gmail.com");
- doReturn(CapabilityResponse.EXCEPTION)
- .when(mDelegate)
- .hasCapability(eq(accountHolder.getAccount()), any());
- mDelegate.addAccount(accountHolder);
-
- Assert.assertFalse(mFacade.canOfferExtendedSyncPromos(accountHolder.getAccount()).get());
- }
-
- @Test
- public void testAccountCanOfferExtendedSyncPromos() {
- final AccountHolder accountHolder1 = AccountHolder.createFromEmail("test1@gmail.com");
- mDelegate.addAccount(accountHolder1);
- final AccountHolder accountHolder2 = AccountHolder.createFromEmailAndFeatures(
- "test2@gmail.com", AccountManagerFacadeImpl.CAN_OFFER_EXTENDED_CHROME_SYNC_PROMOS);
- mDelegate.addAccount(accountHolder2);
-
- Assert.assertFalse(mFacade.canOfferExtendedSyncPromos(accountHolder1.getAccount()).get());
- Assert.assertTrue(mFacade.canOfferExtendedSyncPromos(accountHolder2.getAccount()).get());
- }
-
- @Test
public void testGetAccessToken() throws AuthException {
final Account account = AccountUtils.createAccountFromName("test@gmail.com");
final AccessTokenData originalToken =
diff --git a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java
index 00918a621ec..adb4f3006e4 100644
--- a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java
+++ b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java
@@ -52,6 +52,8 @@ public final class AccountCapabilitiesTest {
return capabilities.isSubjectToParentalControls();
case AccountCapabilitiesConstants.CAN_STOP_PARENTAL_SUPERVISION_CAPABILITY_NAME:
return capabilities.canStopParentalSupervision();
+ case AccountCapabilitiesConstants.CAN_TOGGLE_AUTO_UPDATES_NAME:
+ return capabilities.canToggleAutoUpdates();
}
assert false : "Capability name is not known.";
return -1;
@@ -88,7 +90,10 @@ public final class AccountCapabilitiesTest {
new ParameterSet()
.name("CanOfferExtendedChromeSyncPromos")
.value(AccountCapabilitiesConstants
- .CAN_OFFER_EXTENDED_CHROME_SYNC_PROMOS_CAPABILITY_NAME));
+ .CAN_OFFER_EXTENDED_CHROME_SYNC_PROMOS_CAPABILITY_NAME),
+ new ParameterSet()
+ .name("CanToggleAutoUpdates")
+ .value(AccountCapabilitiesConstants.CAN_TOGGLE_AUTO_UPDATES_NAME));
// Returns String value added from Capabilities ParameterSet.
static String getCapabilityName(ParameterSet parameterSet) {
diff --git a/chromium/components/signin/public/base/signin_client.cc b/chromium/components/signin/public/base/signin_client.cc
index 1ddf8d9913e..93c2f4f1cb6 100644
--- a/chromium/components/signin/public/base/signin_client.cc
+++ b/chromium/components/signin/public/base/signin_client.cc
@@ -10,7 +10,3 @@ void SigninClient::PreSignOut(
// Allow sign out to continue.
std::move(on_signout_decision_reached).Run(SignoutDecision::ALLOW_SIGNOUT);
}
-
-bool SigninClient::IsNonEnterpriseUser(const std::string& username) {
- return false;
-}
diff --git a/chromium/components/signin/public/base/signin_client.h b/chromium/components/signin/public/base/signin_client.h
index ebeae99e5dc..17e7e49c0a0 100644
--- a/chromium/components/signin/public/base/signin_client.h
+++ b/chromium/components/signin/public/base/signin_client.h
@@ -14,6 +14,7 @@
#include "components/keyed_service/core/keyed_service.h"
#include "components/signin/public/base/account_consistency_method.h"
#include "components/signin/public/base/signin_metrics.h"
+#include "google_apis/gaia/core_account_id.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "url/gurl.h"
@@ -90,11 +91,6 @@ class SigninClient : public KeyedService {
GaiaAuthConsumer* consumer,
gaia::GaiaSource source) = 0;
- // Checks whether a user is known to be non-enterprise. Domains such as
- // gmail.com and googlemail.com are known to not be managed. Also returns
- // false if the username is empty.
- virtual bool IsNonEnterpriseUser(const std::string& username);
-
#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Returns an account used to sign into Chrome OS session if available.
virtual absl::optional<account_manager::Account>
@@ -104,6 +100,10 @@ class SigninClient : public KeyedService {
// Returns nullopt for secondary / non-main profiles in LaCrOS.
virtual absl::optional<bool> IsInitialPrimaryAccountChild() const = 0;
+ // Remove account.
+ virtual void RemoveAccount(
+ const account_manager::AccountKey& account_key) = 0;
+
// Removes all accounts.
virtual void RemoveAllAccounts() = 0;
#endif
diff --git a/chromium/components/signin/public/base/signin_metrics.cc b/chromium/components/signin/public/base/signin_metrics.cc
index e086368f682..a0831b455a9 100644
--- a/chromium/components/signin/public/base/signin_metrics.cc
+++ b/chromium/components/signin/public/base/signin_metrics.cc
@@ -104,14 +104,6 @@ void RecordSigninUserActionForAccessPoint(AccessPoint access_point) {
base::RecordAction(
base::UserMetricsAction("Signin_Signin_FromTabSwitcher"));
break;
- case AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE:
- base::RecordAction(
- base::UserMetricsAction("Signin_Signin_FromSaveCardBubble"));
- break;
- case AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE:
- base::RecordAction(
- base::UserMetricsAction("Signin_Signin_FromManageCardsBubble"));
- break;
case AccessPoint::ACCESS_POINT_MACHINE_LOGON:
base::RecordAction(
base::UserMetricsAction("Signin_Signin_FromMachineLogon"));
@@ -128,6 +120,10 @@ void RecordSigninUserActionForAccessPoint(AccessPoint access_point) {
base::RecordAction(base::UserMetricsAction(
"Signin_Signin_FromSigninInterceptFirstRunExperience"));
break;
+ case AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromNTPFeedTopPromo"));
+ break;
case AccessPoint::ACCESS_POINT_KALEIDOSCOPE:
NOTREACHED() << "Access point " << static_cast<int>(access_point)
<< " is only used to trigger non-sync sign-in and this"
@@ -138,6 +134,7 @@ void RecordSigninUserActionForAccessPoint(AccessPoint access_point) {
case AccessPoint::ACCESS_POINT_FORCED_SIGNIN:
case AccessPoint::ACCESS_POINT_ACCOUNT_RENAMED:
case AccessPoint::ACCESS_POINT_WEB_SIGNIN:
+ case AccessPoint::ACCESS_POINT_SETTINGS_SYNC_OFF_ROW:
NOTREACHED() << "Access point " << static_cast<int>(access_point)
<< " is not supposed to log signin user actions.";
break;
@@ -145,6 +142,10 @@ void RecordSigninUserActionForAccessPoint(AccessPoint access_point) {
VLOG(1) << "Signin_Signin_From* user action is not recorded "
<< "for access point " << static_cast<int>(access_point);
break;
+ case AccessPoint::ACCESS_POINT_SEND_TAB_TO_SELF_PROMO:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromSendTabToSelfPromo"));
+ break;
case AccessPoint::ACCESS_POINT_MAX:
NOTREACHED();
break;
@@ -190,13 +191,9 @@ void RecordSigninWithDefaultUserActionForAccessPoint(
base::RecordAction(base::UserMetricsAction(
"Signin_SigninWithDefault_FromNTPContentSuggestions"));
break;
- case AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE:
- base::RecordAction(base::UserMetricsAction(
- "Signin_SigninWithDefault_FromSaveCardBubble"));
- break;
- case AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE:
+ case AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninWithDefault_FromManageCardsBubble"));
+ "Signin_SigninWithDefault_FromNTPFeedTopPromo"));
break;
case AccessPoint::ACCESS_POINT_ENTERPRISE_SIGNOUT_COORDINATOR:
case AccessPoint::ACCESS_POINT_START_PAGE:
@@ -221,6 +218,8 @@ void RecordSigninWithDefaultUserActionForAccessPoint(
case AccessPoint::ACCESS_POINT_SAFETY_CHECK:
case AccessPoint::ACCESS_POINT_KALEIDOSCOPE:
case AccessPoint::ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE:
+ case AccessPoint::ACCESS_POINT_SEND_TAB_TO_SELF_PROMO:
+ case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS_SYNC_OFF_ROW:
NOTREACHED() << "Signin_SigninWithDefault_From* user actions"
<< " are not recorded for access_point "
<< static_cast<int>(access_point)
@@ -271,13 +270,9 @@ void RecordSigninNotDefaultUserActionForAccessPoint(
base::RecordAction(base::UserMetricsAction(
"Signin_SigninNotDefault_FromNTPContentSuggestions"));
break;
- case AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE:
+ case AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNotDefault_FromSaveCardBubble"));
- break;
- case AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE:
- base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNotDefault_FromManageCardsBubble"));
+ "Signin_SigninNotDefault_FromNTPFeedTopPromo"));
break;
case AccessPoint::ACCESS_POINT_ENTERPRISE_SIGNOUT_COORDINATOR:
case AccessPoint::ACCESS_POINT_START_PAGE:
@@ -302,6 +297,8 @@ void RecordSigninNotDefaultUserActionForAccessPoint(
case AccessPoint::ACCESS_POINT_SAFETY_CHECK:
case AccessPoint::ACCESS_POINT_KALEIDOSCOPE:
case AccessPoint::ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE:
+ case AccessPoint::ACCESS_POINT_SEND_TAB_TO_SELF_PROMO:
+ case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS_SYNC_OFF_ROW:
NOTREACHED() << "Signin_SigninNotDefault_From* user actions"
<< " are not recorded for access point "
<< static_cast<int>(access_point)
@@ -356,13 +353,9 @@ void RecordSigninNewAccountNoExistingAccountUserActionForAccessPoint(
"Signin_SigninNewAccountNoExistingAccount_FromNTPContentSuggestions")); // NOLINT(whitespace/line_length)
// clang-format on
break;
- case AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE:
+ case AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccountNoExistingAccount_FromSaveCardBubble"));
- break;
- case AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE:
- base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccountNoExistingAccount_FromManageCardsBubble"));
+ "Signin_SigninNewAccountNoExistingAccount_FromNTPFeedTopPromo"));
break;
case AccessPoint::ACCESS_POINT_ENTERPRISE_SIGNOUT_COORDINATOR:
case AccessPoint::ACCESS_POINT_START_PAGE:
@@ -387,6 +380,8 @@ void RecordSigninNewAccountNoExistingAccountUserActionForAccessPoint(
case AccessPoint::ACCESS_POINT_SAFETY_CHECK:
case AccessPoint::ACCESS_POINT_KALEIDOSCOPE:
case AccessPoint::ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE:
+ case AccessPoint::ACCESS_POINT_SEND_TAB_TO_SELF_PROMO:
+ case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS_SYNC_OFF_ROW:
// These access points do not support personalized sign-in promos, so
// |Signin_SigninNewAccountNoExistingAccount_From*| user actions should
// not be recorded for them. Note: To avoid bloating the sign-in APIs, the
@@ -444,13 +439,9 @@ void RecordSigninNewAccountExistingAccountUserActionForAccessPoint(
base::RecordAction(base::UserMetricsAction(
"Signin_SigninNewAccountExistingAccount_FromNTPContentSuggestions"));
break;
- case AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE:
+ case AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccountExistingAccount_FromSaveCardBubble"));
- break;
- case AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE:
- base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccountExistingAccount_FromManageCardsBubble"));
+ "Signin_SigninNewAccountExistingAccount_FromNTPFeedTopPromo"));
break;
case AccessPoint::ACCESS_POINT_ENTERPRISE_SIGNOUT_COORDINATOR:
case AccessPoint::ACCESS_POINT_START_PAGE:
@@ -475,6 +466,8 @@ void RecordSigninNewAccountExistingAccountUserActionForAccessPoint(
case AccessPoint::ACCESS_POINT_SAFETY_CHECK:
case AccessPoint::ACCESS_POINT_KALEIDOSCOPE:
case AccessPoint::ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE:
+ case AccessPoint::ACCESS_POINT_SEND_TAB_TO_SELF_PROMO:
+ case AccessPoint::ACCESS_POINT_SETTINGS_SYNC_OFF_ROW:
// These access points do not support personalized sign-in promos, so
// |Signin_SigninNewAccountExistingAccount_From*| user actions should not
// be recorded for them. Note: To avoid bloating the sign-in APIs, the
@@ -857,14 +850,6 @@ void RecordSigninImpressionUserActionForAccessPoint(AccessPoint access_point) {
base::RecordAction(
base::UserMetricsAction("Signin_Impression_FromTabSwitcher"));
break;
- case AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE:
- base::RecordAction(
- base::UserMetricsAction("Signin_Impression_FromSaveCardBubble"));
- break;
- case AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE:
- base::RecordAction(
- base::UserMetricsAction("Signin_Impression_FromManageCardsBubble"));
- break;
case AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS:
base::RecordAction(base::UserMetricsAction(
"Signin_Impression_FromGoogleServicesSettings"));
@@ -877,6 +862,14 @@ void RecordSigninImpressionUserActionForAccessPoint(AccessPoint access_point) {
base::RecordAction(
base::UserMetricsAction("Signin_Impression_FromUserManager"));
break;
+ case AccessPoint::ACCESS_POINT_SEND_TAB_TO_SELF_PROMO:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Impression_FromSendTabToSelfPromo"));
+ break;
+ case AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Impression_FromNTPFeedTopPromo"));
+ break;
case AccessPoint::ACCESS_POINT_ENTERPRISE_SIGNOUT_COORDINATOR:
case AccessPoint::ACCESS_POINT_CONTENT_AREA:
case AccessPoint::ACCESS_POINT_EXTENSIONS:
@@ -889,6 +882,7 @@ void RecordSigninImpressionUserActionForAccessPoint(AccessPoint access_point) {
case AccessPoint::ACCESS_POINT_WEB_SIGNIN:
case AccessPoint::ACCESS_POINT_SAFETY_CHECK:
case AccessPoint::ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE:
+ case AccessPoint::ACCESS_POINT_SETTINGS_SYNC_OFF_ROW:
NOTREACHED() << "Signin_Impression_From* user actions"
<< " are not recorded for access point "
<< static_cast<int>(access_point);
@@ -899,143 +893,6 @@ void RecordSigninImpressionUserActionForAccessPoint(AccessPoint access_point) {
}
}
-void RecordSigninImpressionWithAccountUserActionForAccessPoint(
- AccessPoint access_point,
- bool with_account) {
- switch (access_point) {
- case AccessPoint::ACCESS_POINT_SETTINGS:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromSettings"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromSettings"));
- }
- break;
- case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromExtensionInstallBubble"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromExtensionInstallBubble"));
- }
- break;
- case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromBookmarkBubble"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromBookmarkBubble"));
- }
- break;
- case AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromBookmarkManager"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromBookmarkManager"));
- }
- break;
- case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromAvatarBubbleSignin"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromAvatarBubbleSignin"));
- }
- break;
- case AccessPoint::ACCESS_POINT_RECENT_TABS:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromRecentTabs"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromRecentTabs"));
- }
- break;
- case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromPasswordBubble"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromPasswordBubble"));
- }
- break;
- case AccessPoint::ACCESS_POINT_TAB_SWITCHER:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromTabSwitcher"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromTabSwitcher"));
- }
- break;
- case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromNTPContentSuggestions"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromNTPContentSuggestions"));
- }
- break;
- case AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromSaveCardBubble"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromSaveCardBubble"));
- }
- break;
- case AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE:
- if (with_account) {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithAccount_FromManageCardsBubble"));
- } else {
- base::RecordAction(base::UserMetricsAction(
- "Signin_ImpressionWithNoAccount_FromManageCardsBubble"));
- }
- break;
- case AccessPoint::ACCESS_POINT_ENTERPRISE_SIGNOUT_COORDINATOR:
- case AccessPoint::ACCESS_POINT_START_PAGE:
- case AccessPoint::ACCESS_POINT_NTP_LINK:
- case AccessPoint::ACCESS_POINT_MENU:
- case AccessPoint::ACCESS_POINT_SUPERVISED_USER:
- case AccessPoint::ACCESS_POINT_EXTENSIONS:
- case AccessPoint::ACCESS_POINT_USER_MANAGER:
- case AccessPoint::ACCESS_POINT_DEVICES_PAGE:
- case AccessPoint::ACCESS_POINT_CLOUD_PRINT:
- case AccessPoint::ACCESS_POINT_CONTENT_AREA:
- case AccessPoint::ACCESS_POINT_SIGNIN_PROMO:
- case AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN:
- case AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR:
- case AccessPoint::ACCESS_POINT_UNKNOWN:
- case AccessPoint::ACCESS_POINT_MACHINE_LOGON:
- case AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS:
- case AccessPoint::ACCESS_POINT_SYNC_ERROR_CARD:
- case AccessPoint::ACCESS_POINT_FORCED_SIGNIN:
- case AccessPoint::ACCESS_POINT_ACCOUNT_RENAMED:
- case AccessPoint::ACCESS_POINT_WEB_SIGNIN:
- case AccessPoint::ACCESS_POINT_SAFETY_CHECK:
- case AccessPoint::ACCESS_POINT_KALEIDOSCOPE:
- case AccessPoint::ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE:
- NOTREACHED() << "Signin_Impression{With|WithNo}Account_From* user actions"
- << " are not recorded for access point "
- << static_cast<int>(access_point)
- << " as it does not support a personalized sign-in promo.";
- break;
- case AccessPoint::ACCESS_POINT_MAX:
- NOTREACHED();
- break;
- }
-}
-
#if BUILDFLAG(IS_IOS)
void RecordConsistencyPromoUserAction(AccountConsistencyPromoAction action) {
UMA_HISTOGRAM_ENUMERATION(
diff --git a/chromium/components/signin/public/base/signin_metrics.h b/chromium/components/signin/public/base/signin_metrics.h
index 810bddfa4eb..7bcc60e3423 100644
--- a/chromium/components/signin/public/base/signin_metrics.h
+++ b/chromium/components/signin/public/base/signin_metrics.h
@@ -63,6 +63,20 @@ enum ProfileSignout : int {
// User clicked to 'Turn off sync' from the settings page.
// Currently only available for Android Unicorn users.
USER_CLICKED_REVOKE_SYNC_CONSENT_SETTINGS = 16,
+ // User clicked to signout from the settings page.
+ USER_CLICKED_SIGNOUT_PROFILE_MENU = 17,
+ // User retriggered signin from the Android web sign-in bottomsheet.
+ SIGNIN_RETRIGGERD_FROM_WEB_SIGNIN = 18,
+ // User clicked on sign-out from the notification dialog for User Policy. The
+ // notification informs the user that from now on user policies may be
+ // effective on their browser if they Sync with their managed account. The
+ // user has the option to sign out to avoid user policies.
+ USER_CLICKED_SIGNOUT_FROM_USER_POLICY_NOTIFICATION_DIALOG = 19,
+ // The email address of the primary account on the device was updated,
+ // triggering an automatic signout followed by signin.
+ ACCOUNT_EMAIL_UPDATED = 20,
+ // User clicked on sign-out from the clear browsing data page.
+ USER_CLICKED_SIGNOUT_FROM_CLEAR_BROWSING_DATA_PAGE = 21,
// Keep this as the last enum.
NUM_PROFILE_SIGNOUT_METRICS,
};
@@ -159,9 +173,9 @@ enum class AccessPoint : int {
ACCESS_POINT_NTP_CONTENT_SUGGESTIONS = 20,
ACCESS_POINT_RESIGNIN_INFOBAR = 21,
ACCESS_POINT_TAB_SWITCHER = 22,
- // ACCESS_POINT_FORCE_SIGNIN_WARNING is no longer used.
- ACCESS_POINT_SAVE_CARD_BUBBLE = 24,
- ACCESS_POINT_MANAGE_CARDS_BUBBLE = 25,
+ // ACCESS_POINT_FORCE_SIGNIN_WARNING = 23, no longer used.
+ // ACCESS_POINT_SAVE_CARD_BUBBLE = 24, no longer used
+ // ACCESS_POINT_MANAGE_CARDS_BUBBLE = 25, no longer used
ACCESS_POINT_MACHINE_LOGON = 26,
ACCESS_POINT_GOOGLE_SERVICES_SETTINGS = 27,
ACCESS_POINT_SYNC_ERROR_CARD = 28,
@@ -172,6 +186,9 @@ enum class AccessPoint : int {
ACCESS_POINT_KALEIDOSCOPE = 33,
ACCESS_POINT_ENTERPRISE_SIGNOUT_COORDINATOR = 34,
ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE = 35,
+ ACCESS_POINT_SEND_TAB_TO_SELF_PROMO = 36,
+ ACCESS_POINT_NTP_FEED_TOP_PROMO = 37,
+ ACCESS_POINT_SETTINGS_SYNC_OFF_ROW = 38,
// Add values above this line with a corresponding label to the
// "SigninAccessPoint" enum in tools/metrics/histograms/enums.xml
ACCESS_POINT_MAX, // This must be last.
@@ -543,14 +560,9 @@ void RecordSigninAccountType(signin::ConsentLevel consent_level,
void RecordSigninUserActionForAccessPoint(AccessPoint access_point,
PromoAction promo_action);
-// Records |Signin_ImpressionWithAccount_From*| user action.
+// Records |Signin_Impression_From*| user action.
void RecordSigninImpressionUserActionForAccessPoint(AccessPoint access_point);
-// Records |Signin_Impression{With|No}Account_From*| user action.
-void RecordSigninImpressionWithAccountUserActionForAccessPoint(
- AccessPoint access_point,
- bool with_account);
-
#if BUILDFLAG(IS_IOS)
// Records |Signin.AccountConsistencyPromoAction| histogram.
void RecordConsistencyPromoUserAction(AccountConsistencyPromoAction action);
diff --git a/chromium/components/signin/public/base/signin_metrics_unittest.cc b/chromium/components/signin/public/base/signin_metrics_unittest.cc
index 58581394b77..dac68c1350f 100644
--- a/chromium/components/signin/public/base/signin_metrics_unittest.cc
+++ b/chromium/components/signin/public/base/signin_metrics_unittest.cc
@@ -37,10 +37,9 @@ const AccessPoint kAccessPointsThatSupportUserAction[] = {
AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS,
AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR,
AccessPoint::ACCESS_POINT_TAB_SWITCHER,
- AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE,
- AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE,
AccessPoint::ACCESS_POINT_MACHINE_LOGON,
- AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS};
+ AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS,
+ AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO};
const AccessPoint kAccessPointsThatSupportImpression[] = {
AccessPoint::ACCESS_POINT_START_PAGE,
@@ -60,8 +59,7 @@ const AccessPoint kAccessPointsThatSupportImpression[] = {
AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS,
AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR,
AccessPoint::ACCESS_POINT_TAB_SWITCHER,
- AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE,
- AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE};
+ AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO};
const AccessPoint kAccessPointsThatSupportPersonalizedPromos[] = {
AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER,
@@ -73,8 +71,7 @@ const AccessPoint kAccessPointsThatSupportPersonalizedPromos[] = {
AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE,
AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE,
AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS,
- AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE,
- AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE};
+ AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO};
class SigninMetricsTest : public ::testing::Test {
public:
@@ -124,10 +121,6 @@ class SigninMetricsTest : public ::testing::Test {
return "ReSigninInfobar";
case AccessPoint::ACCESS_POINT_TAB_SWITCHER:
return "TabSwitcher";
- case AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE:
- return "SaveCardBubble";
- case AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE:
- return "ManageCardsBubble";
case AccessPoint::ACCESS_POINT_MACHINE_LOGON:
return "MachineLogon";
case AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS:
@@ -148,6 +141,12 @@ class SigninMetricsTest : public ::testing::Test {
return "EnterpriseSignoutResignSheet";
case AccessPoint::ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE:
return "SigninInterceptFirstRunExperience";
+ case AccessPoint::ACCESS_POINT_SEND_TAB_TO_SELF_PROMO:
+ return "SendTabToSelfPromo";
+ case AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO:
+ return "NTPFeedTopPromo";
+ case AccessPoint::ACCESS_POINT_SETTINGS_SYNC_OFF_ROW:
+ return "SettingsSyncOffRow";
case AccessPoint::ACCESS_POINT_MAX:
NOTREACHED();
return "";
@@ -241,25 +240,5 @@ TEST_F(SigninMetricsTest, RecordSigninImpressionUserAction) {
}
}
-TEST_F(SigninMetricsTest, RecordSigninImpressionWithAccountUserAction) {
- for (const AccessPoint& ap : kAccessPointsThatSupportPersonalizedPromos) {
- base::UserActionTester user_action_tester;
- RecordSigninImpressionWithAccountUserActionForAccessPoint(ap, true);
- EXPECT_EQ(1, user_action_tester.GetActionCount(
- "Signin_ImpressionWithAccount_From" +
- GetAccessPointDescription(ap)));
- }
-}
-
-TEST_F(SigninMetricsTest, RecordSigninImpressionWithNoAccountUserAction) {
- for (const AccessPoint& ap : kAccessPointsThatSupportPersonalizedPromos) {
- base::UserActionTester user_action_tester;
- RecordSigninImpressionWithAccountUserActionForAccessPoint(ap, false);
- EXPECT_EQ(1, user_action_tester.GetActionCount(
- "Signin_ImpressionWithNoAccount_From" +
- GetAccessPointDescription(ap)));
- }
-}
-
} // namespace
} // namespace signin_metrics
diff --git a/chromium/components/signin/public/base/signin_pref_names.cc b/chromium/components/signin/public/base/signin_pref_names.cc
index a4edd51b46a..1817c6505d5 100644
--- a/chromium/components/signin/public/base/signin_pref_names.cc
+++ b/chromium/components/signin/public/base/signin_pref_names.cc
@@ -13,12 +13,12 @@ namespace prefs {
// automatically. Default value is false.
const char kForceLogoutUnauthenticatedUserEnabled[] =
"profile.force_logout_unauthenticated_user_enabled";
-#endif
// An integer property indicating the state of account id migration from
// email to gaia id for the the profile. See account_tracker_service.h
// for possible values.
const char kAccountIdMigrationState[] = "account_id_migration_state";
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
// Name of the preference property that persists the account information
// tracked by this signin.
@@ -85,9 +85,6 @@ const char kSignedInWithCredentialProvider[] =
// Boolean which stores if the user is allowed to signin to chrome.
const char kSigninAllowed[] = "signin.allowed";
-// True if the token service has been prepared for Dice migration.
-const char kTokenServiceDiceCompatible[] = "token_service.dice_compatible";
-
// Contains last |ListAccounts| data which corresponds to Gaia cookies.
const char kGaiaCookieLastListAccountsData[] =
"gaia_cookie.last_list_accounts_data";
diff --git a/chromium/components/signin/public/base/signin_pref_names.h b/chromium/components/signin/public/base/signin_pref_names.h
index 7020950ec70..42b6295b430 100644
--- a/chromium/components/signin/public/base/signin_pref_names.h
+++ b/chromium/components/signin/public/base/signin_pref_names.h
@@ -5,14 +5,16 @@
#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_PREF_NAMES_H_
#define COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_PREF_NAMES_H_
+#include "build/build_config.h"
+#include "build/buildflag.h"
#include "build/chromeos_buildflags.h"
namespace prefs {
#if BUILDFLAG(IS_CHROMEOS_ASH)
extern const char kForceLogoutUnauthenticatedUserEnabled[];
-#endif
extern const char kAccountIdMigrationState[];
+#endif
extern const char kAccountInfo[];
extern const char kAutologinEnabled[];
extern const char kGaiaCookieHash[];
@@ -28,7 +30,6 @@ extern const char kRestrictAccountsToPatterns[];
extern const char kReverseAutologinRejectedEmailList[];
extern const char kSignedInWithCredentialProvider[];
extern const char kSigninAllowed[];
-extern const char kTokenServiceDiceCompatible[];
extern const char kGaiaCookieLastListAccountsData[];
} // namespace prefs
diff --git a/chromium/components/signin/public/base/signin_switches.cc b/chromium/components/signin/public/base/signin_switches.cc
index 1113d6ad79d..24139003d84 100644
--- a/chromium/components/signin/public/base/signin_switches.cc
+++ b/chromium/components/signin/public/base/signin_switches.cc
@@ -45,15 +45,24 @@ const base::Feature kForceDisableExtendedSyncPromos{
"ForceDisableExtendedSyncPromos", base::FEATURE_DISABLED_BY_DEFAULT};
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+// Decouples signing out from clearing browsing data on Android. Users are
+// no longer signed-out when they clear browsing data. Instead they may
+// choose to sign out separately by pressing another button.
+const base::Feature kEnableCbdSignOut{"EnableCbdSignOut",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Features to trigger the startup sign-in promo at boot.
const base::Feature kForceStartupSigninPromo{"ForceStartupSigninPromo",
base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kTangibleSync{"TangibleSync",
+ base::FEATURE_DISABLED_BY_DEFAULT};
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Allows local (not signed-in) profiles on lacros.
-const base::Feature kLacrosNonSyncingProfiles{
- "LacrosNonSyncingProfiles", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kLacrosNonSyncingProfiles{"LacrosNonSyncingProfiles",
+ base::FEATURE_ENABLED_BY_DEFAULT};
#endif
} // namespace switches
diff --git a/chromium/components/signin/public/base/signin_switches.h b/chromium/components/signin/public/base/signin_switches.h
index e8ccef399a2..37c8689f95c 100644
--- a/chromium/components/signin/public/base/signin_switches.h
+++ b/chromium/components/signin/public/base/signin_switches.h
@@ -36,7 +36,9 @@ extern const base::Feature kEnableFetchingAccountCapabilities;
extern const base::Feature kForceDisableExtendedSyncPromos;
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+extern const base::Feature kEnableCbdSignOut;
extern const base::Feature kForceStartupSigninPromo;
+extern const base::Feature kTangibleSync;
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chromium/components/signin/public/base/test_signin_client.cc b/chromium/components/signin/public/base/test_signin_client.cc
index 972b6f477c2..fd3cb4b3bb1 100644
--- a/chromium/components/signin/public/base/test_signin_client.cc
+++ b/chromium/components/signin/public/base/test_signin_client.cc
@@ -112,10 +112,6 @@ std::unique_ptr<GaiaAuthFetcher> TestSigninClient::CreateGaiaAuthFetcher(
GetURLLoaderFactory());
}
-bool TestSigninClient::IsNonEnterpriseUser(const std::string& email) {
- return gaia::ExtractDomainName(email) == "gmail.com";
-}
-
#if BUILDFLAG(IS_CHROMEOS_LACROS)
absl::optional<account_manager::Account>
TestSigninClient::GetInitialPrimaryAccount() {
@@ -133,6 +129,8 @@ void TestSigninClient::SetInitialPrimaryAccountForTests(
is_initial_primary_account_child_ = is_child;
}
+void TestSigninClient::RemoveAccount(
+ const account_manager::AccountKey& account_key) {}
void TestSigninClient::RemoveAllAccounts() {}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chromium/components/signin/public/base/test_signin_client.h b/chromium/components/signin/public/base/test_signin_client.h
index bf40e25fcc9..d091d39ab40 100644
--- a/chromium/components/signin/public/base/test_signin_client.h
+++ b/chromium/components/signin/public/base/test_signin_client.h
@@ -96,7 +96,6 @@ class TestSigninClient : public SigninClient {
std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
GaiaAuthConsumer* consumer,
gaia::GaiaSource source) override;
- bool IsNonEnterpriseUser(const std::string& email) override;
#if BUILDFLAG(IS_CHROMEOS_LACROS)
absl::optional<account_manager::Account> GetInitialPrimaryAccount() override;
@@ -104,6 +103,8 @@ class TestSigninClient : public SigninClient {
void SetInitialPrimaryAccountForTests(const account_manager::Account& account,
const absl::optional<bool>& is_child);
+ void RemoveAccount(const account_manager::AccountKey& account_key) override;
+
void RemoveAllAccounts() override;
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chromium/components/signin/public/identity_manager/BUILD.gn b/chromium/components/signin/public/identity_manager/BUILD.gn
index e726761f763..fac2ebe4be2 100644
--- a/chromium/components/signin/public/identity_manager/BUILD.gn
+++ b/chromium/components/signin/public/identity_manager/BUILD.gn
@@ -174,6 +174,10 @@ source_set("test_support") {
"//testing/gtest",
]
+ if (is_chromeos_ash) {
+ deps += [ "//ash/components/account_manager" ]
+ }
+
if (is_chromeos_lacros) {
deps += [ "//components/account_manager_core" ]
}
diff --git a/chromium/components/signin/public/identity_manager/account_capabilities.cc b/chromium/components/signin/public/identity_manager/account_capabilities.cc
index 9a224dff02c..b6d109499d1 100644
--- a/chromium/components/signin/public/identity_manager/account_capabilities.cc
+++ b/chromium/components/signin/public/identity_manager/account_capabilities.cc
@@ -77,6 +77,10 @@ signin::Tribool AccountCapabilities::is_subject_to_parental_controls() const {
return GetCapabilityByName(kIsSubjectToParentalControlsCapabilityName);
}
+signin::Tribool AccountCapabilities::can_toggle_auto_updates() const {
+ return GetCapabilityByName(kCanToggleAutoUpdatesName);
+}
+
bool AccountCapabilities::UpdateWith(const AccountCapabilities& other) {
bool modified = false;
diff --git a/chromium/components/signin/public/identity_manager/account_capabilities.h b/chromium/components/signin/public/identity_manager/account_capabilities.h
index 9bc27f627dd..3e0b7dd3cc3 100644
--- a/chromium/components/signin/public/identity_manager/account_capabilities.h
+++ b/chromium/components/signin/public/identity_manager/account_capabilities.h
@@ -55,6 +55,9 @@ class AccountCapabilities {
// Chrome applies parental controls to accounts with this capability.
signin::Tribool is_subject_to_parental_controls() const;
+ // Chrome can toggle auto updates with this capability.
+ signin::Tribool can_toggle_auto_updates() const;
+
// Whether none of the capabilities has `signin::Tribool::kUnknown`.
bool AreAllCapabilitiesKnown() const;
diff --git a/chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.cc b/chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
index 40a4a7db791..956131f604d 100644
--- a/chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
+++ b/chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
@@ -42,6 +42,10 @@ void AccountCapabilitiesTestMutator::set_is_subject_to_parental_controls(
value;
}
+void AccountCapabilitiesTestMutator::set_can_toggle_auto_updates(bool value) {
+ capabilities_->capabilities_map_[kCanToggleAutoUpdatesName] = value;
+}
+
void AccountCapabilitiesTestMutator::SetAllSupportedCapabilities(bool value) {
for (const std::string& name :
AccountCapabilities::GetSupportedAccountCapabilityNames()) {
diff --git a/chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.h b/chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.h
index 2c40cd011a9..08a2ac41a35 100644
--- a/chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.h
+++ b/chromium/components/signin/public/identity_manager/account_capabilities_test_mutator.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNT_CAPABILITIES_TEST_MUTATOR_H_
#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNT_CAPABILITIES_TEST_MUTATOR_H_
+#include "base/memory/raw_ptr.h"
#include "components/signin/public/identity_manager/account_capabilities.h"
// Support class that allows callers to modify internal capability state
@@ -21,12 +22,13 @@ class AccountCapabilitiesTestMutator {
void set_can_run_chrome_privacy_sandbox_trials(bool value);
void set_can_stop_parental_supervision(bool value);
void set_is_subject_to_parental_controls(bool value);
+ void set_can_toggle_auto_updates(bool value);
// Modifies all supported capabilities at once.
void SetAllSupportedCapabilities(bool value);
private:
- AccountCapabilities* capabilities_;
+ raw_ptr<AccountCapabilities> capabilities_;
};
#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNT_CAPABILITIES_TEST_MUTATOR_H_
diff --git a/chromium/components/signin/public/identity_manager/account_capabilities_unittest.cc b/chromium/components/signin/public/identity_manager/account_capabilities_unittest.cc
index bfde01b5409..31698927e83 100644
--- a/chromium/components/signin/public/identity_manager/account_capabilities_unittest.cc
+++ b/chromium/components/signin/public/identity_manager/account_capabilities_unittest.cc
@@ -73,6 +73,18 @@ TEST_F(AccountCapabilitiesTest, IsSubjectToParentalControls) {
signin::Tribool::kFalse);
}
+TEST_F(AccountCapabilitiesTest, CanToggleAutoUpdates) {
+ AccountCapabilities capabilities;
+ EXPECT_EQ(capabilities.can_toggle_auto_updates(), signin::Tribool::kUnknown);
+
+ AccountCapabilitiesTestMutator mutator(&capabilities);
+ mutator.set_can_toggle_auto_updates(true);
+ EXPECT_EQ(capabilities.can_toggle_auto_updates(), signin::Tribool::kTrue);
+
+ mutator.set_can_toggle_auto_updates(false);
+ EXPECT_EQ(capabilities.can_toggle_auto_updates(), signin::Tribool::kFalse);
+}
+
TEST_F(AccountCapabilitiesTest, AreAllCapabilitiesKnown_Empty) {
AccountCapabilities capabilities;
EXPECT_FALSE(capabilities.AreAllCapabilitiesKnown());
diff --git a/chromium/components/signin/public/identity_manager/identity_manager.cc b/chromium/components/signin/public/identity_manager/identity_manager.cc
index 2d8d85cd4da..223b74bf7dd 100644
--- a/chromium/components/signin/public/identity_manager/identity_manager.cc
+++ b/chromium/components/signin/public/identity_manager/identity_manager.cc
@@ -121,7 +121,7 @@ IdentityManager::IdentityManager(IdentityManager::InitParameters&& parameters)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
signin_client_(parameters.signin_client),
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager_facade_(parameters.account_manager_facade),
#endif
identity_mutator_(std::move(parameters.primary_account_mutator),
@@ -424,11 +424,13 @@ void IdentityManager::OnNetworkInitialized() {
account_fetcher_service_->OnNetworkInitialized();
}
+#if BUILDFLAG(IS_CHROMEOS_ASH)
IdentityManager::AccountIdMigrationState
IdentityManager::GetAccountIdMigrationState() const {
return static_cast<IdentityManager::AccountIdMigrationState>(
account_tracker_service_->GetMigrationState());
}
+#endif
CoreAccountId IdentityManager::PickAccountIdForAccount(
const std::string& gaia,
@@ -448,9 +450,6 @@ void IdentityManager::RegisterProfilePrefs(PrefRegistrySimple* registry) {
AccountFetcherService::RegisterPrefs(registry);
AccountTrackerService::RegisterPrefs(registry);
GaiaCookieManagerService::RegisterPrefs(registry);
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- MutableProfileOAuth2TokenServiceDelegate::RegisterProfilePrefs(registry);
-#endif
}
DiagnosticsProvider* IdentityManager::GetDiagnosticsProvider() {
@@ -559,7 +558,7 @@ GaiaCookieManagerService* IdentityManager::GetGaiaCookieManagerService() const {
return gaia_cookie_manager_service_.get();
}
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade*
IdentityManager::GetAccountManagerFacade() const {
return account_manager_facade_;
diff --git a/chromium/components/signin/public/identity_manager/identity_manager.h b/chromium/components/signin/public/identity_manager/identity_manager.h
index 4cce79d3bf3..912c2b7c453 100644
--- a/chromium/components/signin/public/identity_manager/identity_manager.h
+++ b/chromium/components/signin/public/identity_manager/identity_manager.h
@@ -33,7 +33,7 @@
#include "base/time/time.h"
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
namespace account_manager {
class AccountManagerFacade;
}
@@ -371,7 +371,7 @@ class IdentityManager : public KeyedService,
#if BUILDFLAG(IS_CHROMEOS_LACROS)
SigninClient* signin_client = nullptr;
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade* account_manager_facade = nullptr;
#endif
@@ -397,6 +397,7 @@ class IdentityManager : public KeyedService,
// initialized.
void OnNetworkInitialized();
+#if BUILDFLAG(IS_CHROMEOS_ASH)
// Methods related to migration of account IDs from email to Gaia ID.
// TODO(https://crbug.com/883272): Remove these once all platforms have
// migrated to the new account_id based on gaia (currently, only ChromeOS
@@ -413,6 +414,7 @@ class IdentityManager : public KeyedService,
// Returns the currently saved state of the migration of account IDs.
AccountIdMigrationState GetAccountIdMigrationState() const;
+#endif
// Picks the correct account_id for the specified account depending on the
// migration state.
@@ -550,7 +552,7 @@ class IdentityManager : public KeyedService,
const std::string& locale,
const std::string& picture_url);
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
friend account_manager::AccountManagerFacade* GetAccountManagerFacade(
IdentityManager* identity_manager);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -626,7 +628,7 @@ class IdentityManager : public KeyedService,
AccountTrackerService* GetAccountTrackerService() const;
AccountFetcherService* GetAccountFetcherService() const;
GaiaCookieManagerService* GetGaiaCookieManagerService() const;
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade* GetAccountManagerFacade() const;
#endif
@@ -686,7 +688,7 @@ class IdentityManager : public KeyedService,
#if BUILDFLAG(IS_CHROMEOS_LACROS)
SigninClient* const signin_client_;
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade* const account_manager_facade_;
#endif
diff --git a/chromium/components/signin/public/identity_manager/identity_manager_builder.cc b/chromium/components/signin/public/identity_manager/identity_manager_builder.cc
index 904912c9ff1..330c7c3e7ff 100644
--- a/chromium/components/signin/public/identity_manager/identity_manager_builder.cc
+++ b/chromium/components/signin/public/identity_manager/identity_manager_builder.cc
@@ -112,12 +112,15 @@ IdentityManager::InitParameters BuildIdentityManagerInitParameters(
BuildProfileOAuth2TokenService(
params->pref_service, account_tracker_service.get(),
params->network_connection_tracker, params->account_consistency,
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
params->account_manager_facade, params->is_regular_profile,
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+ params->delete_signin_cookies_on_exit,
+#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- params->delete_signin_cookies_on_exit, params->token_web_data,
-#endif
+ params->token_web_data,
+#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
#if BUILDFLAG(IS_IOS)
std::move(params->device_accounts_provider),
#endif
@@ -127,7 +130,8 @@ IdentityManager::InitParameters BuildIdentityManagerInitParameters(
params->signin_client);
auto gaia_cookie_manager_service = std::make_unique<GaiaCookieManagerService>(
- token_service.get(), params->signin_client);
+ account_tracker_service.get(), token_service.get(),
+ params->signin_client);
std::unique_ptr<PrimaryAccountManager> primary_account_manager =
BuildPrimaryAccountManager(params->signin_client,
@@ -185,7 +189,7 @@ IdentityManager::InitParameters BuildIdentityManagerInitParameters(
#if BUILDFLAG(IS_CHROMEOS_LACROS)
init_params.signin_client = params->signin_client;
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
init_params.account_manager_facade = params->account_manager_facade;
#endif
diff --git a/chromium/components/signin/public/identity_manager/identity_manager_builder.h b/chromium/components/signin/public/identity_manager/identity_manager_builder.h
index 3f76111603d..0cdab6f70ff 100644
--- a/chromium/components/signin/public/identity_manager/identity_manager_builder.h
+++ b/chromium/components/signin/public/identity_manager/identity_manager_builder.h
@@ -10,7 +10,6 @@
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/signin/public/identity_manager/identity_manager.h"
@@ -41,7 +40,7 @@ namespace network {
class NetworkConnectionTracker;
}
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
namespace account_manager {
class AccountManagerFacade;
}
@@ -63,12 +62,15 @@ struct IdentityManagerBuildParams {
base::FilePath profile_path;
raw_ptr<SigninClient> signin_client = nullptr;
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
bool delete_signin_cookies_on_exit = false;
+#endif
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
scoped_refptr<TokenWebData> token_web_data;
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade* account_manager_facade = nullptr;
bool is_regular_profile = false;
#endif
diff --git a/chromium/components/signin/public/identity_manager/identity_manager_builder_unittest.cc b/chromium/components/signin/public/identity_manager/identity_manager_builder_unittest.cc
index 8d57ea54e6e..bf5b607cf0a 100644
--- a/chromium/components/signin/public/identity_manager/identity_manager_builder_unittest.cc
+++ b/chromium/components/signin/public/identity_manager/identity_manager_builder_unittest.cc
@@ -9,7 +9,6 @@
#include "base/files/scoped_temp_dir.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
#include "components/image_fetcher/core/fake_image_decoder.h"
#include "components/signin/internal/identity_manager/account_fetcher_service.h"
#include "components/signin/public/base/test_signin_client.h"
@@ -23,7 +22,7 @@
#include "components/signin/public/identity_manager/identity_test_utils.h"
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
#include "components/account_manager_core/mock_account_manager_facade.h"
#endif
@@ -86,7 +85,7 @@ TEST_F(IdentityManagerBuilderTest, BuildIdentityManagerInitParameters) {
std::make_unique<FakeDeviceAccountsProvider>();
#endif
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
auto account_manager_facade =
std::make_unique<account_manager::MockAccountManagerFacade>();
params.account_manager_facade = account_manager_facade.get();
@@ -111,9 +110,8 @@ TEST_F(IdentityManagerBuilderTest, BuildIdentityManagerInitParameters) {
EXPECT_EQ(init_params.device_accounts_synchronizer, nullptr);
EXPECT_NE(init_params.accounts_mutator, nullptr);
#endif
-#if defined(IS_CHROMEOS)
- EXPECT_NE(init_params.ash_account_manager_facade, nullptr);
- EXPECT_TRUE(init_params.is_regular_profile);
+#if BUILDFLAG(IS_CHROMEOS)
+ EXPECT_NE(init_params.account_manager_facade, nullptr);
#endif
}
diff --git a/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc b/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc
index a71f5a10746..a7599181e47 100644
--- a/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc
+++ b/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc
@@ -391,8 +391,9 @@ class IdentityManagerTest : public testing::Test {
#endif
auto gaia_cookie_manager_service =
- std::make_unique<GaiaCookieManagerService>(token_service.get(),
- &signin_client_);
+ std::make_unique<GaiaCookieManagerService>(
+ account_tracker_service.get(), token_service.get(),
+ &signin_client_);
auto account_fetcher_service = std::make_unique<AccountFetcherService>();
account_fetcher_service->Initialize(
@@ -1725,7 +1726,7 @@ TEST_F(IdentityManagerTest, IdentityManagerGetsTokensLoadedEvent) {
// we fake the credentials loaded state and force another load in
// order to be able to capture the TokensLoaded event.
token_service()->set_all_credentials_loaded_for_testing(false);
- token_service()->LoadCredentials(CoreAccountId());
+ token_service()->LoadCredentials(CoreAccountId(), /*is_syncing=*/false);
run_loop.Run();
}
@@ -2118,8 +2119,8 @@ TEST_F(IdentityManagerTest, CallbackSentOnAccountsCookieDeletedByUserAction) {
run_loop.QuitClosure());
auto cookie = net::CanonicalCookie::CreateUnsafeCookieForTesting(
"SAPISID", std::string(), ".google.com", "/", base::Time(), base::Time(),
- base::Time(), /*secure=*/true, false, net::CookieSameSite::NO_RESTRICTION,
- net::COOKIE_PRIORITY_DEFAULT, false);
+ base::Time(), base::Time(), /*secure=*/true, false,
+ net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT, false);
SimulateCookieDeletedByUser(identity_manager()->GetGaiaCookieManagerService(),
*cookie);
run_loop.Run();
@@ -2150,8 +2151,8 @@ TEST_F(IdentityManagerTest, OnNetworkInitialized) {
// through any mojo pipe.
auto cookie = net::CanonicalCookie::CreateUnsafeCookieForTesting(
"SAPISID", std::string(), ".google.com", "/", base::Time(), base::Time(),
- base::Time(), /*secure=*/true, false, net::CookieSameSite::NO_RESTRICTION,
- net::COOKIE_PRIORITY_DEFAULT, false);
+ base::Time(), base::Time(), /*secure=*/true, false,
+ net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT, false);
test_cookie_manager_ptr->DispatchCookieChange(net::CookieChangeInfo(
*cookie, net::CookieAccessResult(), net::CookieChangeCause::EXPLICIT));
run_loop.Run();
@@ -2352,7 +2353,7 @@ TEST_F(IdentityManagerTest, AreRefreshTokensLoaded) {
// order to test AreRefreshTokensLoaded.
token_service()->set_all_credentials_loaded_for_testing(false);
EXPECT_FALSE(identity_manager()->AreRefreshTokensLoaded());
- token_service()->LoadCredentials(CoreAccountId());
+ token_service()->LoadCredentials(CoreAccountId(), /*is_syncing=*/false);
run_loop.Run();
EXPECT_TRUE(identity_manager()->AreRefreshTokensLoaded());
}
@@ -2405,10 +2406,12 @@ TEST_F(IdentityManagerTest, SetPrimaryAccountClearsExistingPrimaryAccount) {
}
#endif
+#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(IdentityManagerTest, AccountIdMigration_DoneOnInitialization) {
EXPECT_EQ(IdentityManager::AccountIdMigrationState::MIGRATION_DONE,
identity_manager()->GetAccountIdMigrationState());
}
+#endif
// Checks that IdentityManager::Observer gets OnAccountUpdated when account info
// is updated.
@@ -2455,6 +2458,7 @@ TEST_F(IdentityManagerTest, TestOnAccountRemovedWithInfoCallback) {
TEST_F(IdentityManagerTest, TestPickAccountIdForAccount) {
const CoreAccountId account_id =
identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
const bool account_id_migration_done =
identity_manager()->GetAccountIdMigrationState() ==
IdentityManager::AccountIdMigrationState::MIGRATION_DONE;
@@ -2463,6 +2467,9 @@ TEST_F(IdentityManagerTest, TestPickAccountIdForAccount) {
} else {
EXPECT_TRUE(gaia::AreEmailsSame(kTestEmail, account_id.ToString()));
}
+#else
+ EXPECT_TRUE(gaia::AreEmailsSame(kTestGaiaId, account_id.ToString()));
+#endif
}
#if BUILDFLAG(IS_ANDROID)
diff --git a/chromium/components/signin/public/identity_manager/identity_test_environment.cc b/chromium/components/signin/public/identity_manager/identity_test_environment.cc
index 314ac25ed12..b0947a422a2 100644
--- a/chromium/components/signin/public/identity_manager/identity_test_environment.cc
+++ b/chromium/components/signin/public/identity_manager/identity_test_environment.cc
@@ -344,8 +344,8 @@ IdentityTestEnvironment::FinishBuildIdentityManagerForTests(
primary_account_manager->Initialize(pref_service);
std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service =
- std::make_unique<GaiaCookieManagerService>(token_service.get(),
- signin_client);
+ std::make_unique<GaiaCookieManagerService>(
+ account_tracker_service.get(), token_service.get(), signin_client);
IdentityManager::InitParameters init_params;
init_params.primary_account_mutator =
std::make_unique<PrimaryAccountMutatorImpl>(
@@ -672,7 +672,7 @@ void IdentityTestEnvironment::ResetToAccountsNotYetLoadedFromDiskState() {
}
void IdentityTestEnvironment::ReloadAccountsFromDisk() {
- fake_token_service()->LoadCredentials(CoreAccountId());
+ fake_token_service()->LoadCredentials(CoreAccountId(), /*is_syncing=*/false);
}
bool IdentityTestEnvironment::IsAccessTokenRequestPending() {
diff --git a/chromium/components/signin/public/identity_manager/identity_test_utils.cc b/chromium/components/signin/public/identity_manager/identity_test_utils.cc
index a583e60bbe1..fdaddfbcbb4 100644
--- a/chromium/components/signin/public/identity_manager/identity_test_utils.cc
+++ b/chromium/components/signin/public/identity_manager/identity_test_utils.cc
@@ -9,6 +9,7 @@
#include "base/guid.h"
#include "base/run_loop.h"
#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
#include "components/signin/internal/identity_manager/account_fetcher_service.h"
#include "components/signin/internal/identity_manager/account_tracker_service.h"
#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h"
@@ -22,7 +23,7 @@
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_constants.h"
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
#include "components/account_manager_core/account.h"
#include "components/account_manager_core/account_manager_facade.h"
#endif
@@ -83,7 +84,7 @@ void UpdateRefreshTokenForAccount(
run_loop.QuitClosure());
// TODO(crbug.com/1226041): simplify this when all Lacros Profiles use Mirror.
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
if (ShouldUseAccountManagerFacade(identity_manager)) {
const AccountInfo& account_info =
account_tracker_service->GetAccountInfo(account_id);
@@ -94,7 +95,7 @@ void UpdateRefreshTokenForAccount(
GetAccountManagerFacade(identity_manager)
->UpsertAccountForTesting(account, new_token);
} else
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
{
token_service->UpdateCredentials(account_id, new_token);
}
@@ -273,6 +274,25 @@ void ClearPrimaryAccount(IdentityManager* identity_manager) {
#endif
}
+void WaitForPrimaryAccount(IdentityManager* identity_manager,
+ ConsentLevel consent_level,
+ const CoreAccountId& account_id) {
+ if (identity_manager->GetPrimaryAccountId(consent_level) == account_id)
+ return;
+
+ base::RunLoop run_loop;
+ TestIdentityManagerObserver primary_account_observer(identity_manager);
+ primary_account_observer.SetOnPrimaryAccountChangedCallback(base::BindOnce(
+ [](IdentityManager* identity_manager, ConsentLevel consent_level,
+ const CoreAccountId& account_id, base::RunLoop* run_loop,
+ PrimaryAccountChangeEvent event) {
+ if (identity_manager->GetPrimaryAccountId(consent_level) == account_id)
+ run_loop->Quit();
+ },
+ identity_manager, consent_level, account_id, &run_loop));
+ run_loop.Run();
+}
+
AccountInfo MakeAccountAvailable(IdentityManager* identity_manager,
const std::string& email) {
AccountTrackerService* account_tracker_service =
@@ -358,7 +378,7 @@ void RemoveRefreshTokenForAccount(IdentityManager* identity_manager,
run_loop.QuitClosure());
// TODO(crbug.com/1226041): simplify this when all Lacros Profiles use Mirror.
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
if (ShouldUseAccountManagerFacade(identity_manager)) {
const AccountInfo& account_info =
identity_manager->GetAccountTrackerService()->GetAccountInfo(
@@ -367,7 +387,7 @@ void RemoveRefreshTokenForAccount(IdentityManager* identity_manager,
->RemoveAccountForTesting(account_manager::AccountKey{
account_info.gaia, account_manager::AccountType::kGaia});
} else
-#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif // BUILDFLAG(IS_CHROMEOS)
{
identity_manager->GetTokenService()->RevokeCredentials(account_id);
}
@@ -514,7 +534,7 @@ void SimulateSuccessfulFetchOfAccountInfo(IdentityManager* identity_manager,
account_tracker_service->SetAccountInfoFromUserInfo(account_id, &user_info);
}
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade* GetAccountManagerFacade(
IdentityManager* identity_manager) {
return identity_manager->GetAccountManagerFacade();
diff --git a/chromium/components/signin/public/identity_manager/identity_test_utils.h b/chromium/components/signin/public/identity_manager/identity_test_utils.h
index 499bb6ed890..fb972ec3f3a 100644
--- a/chromium/components/signin/public/identity_manager/identity_test_utils.h
+++ b/chromium/components/signin/public/identity_manager/identity_test_utils.h
@@ -9,7 +9,6 @@
#include "base/bind.h"
#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/account_info.h"
@@ -17,7 +16,7 @@ namespace network {
class TestURLLoaderFactory;
}
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
namespace account_manager {
class AccountManagerFacade;
}
@@ -96,6 +95,16 @@ void RevokeSyncConsent(IdentityManager* identity_manager);
// NOTE: See disclaimer at top of file re: direct usage.
void ClearPrimaryAccount(IdentityManager* identity_manager);
+// Waits until the primary account id at consent_level to be equal to
+// |account_id|.
+//
+// Note: Passing an empty |account_id| will make this function wait until
+// the primary account id is cleared at the |consent_level| (calling
+// identity_manager->HasPrimaryAccount(consent_level) will return false)
+void WaitForPrimaryAccount(IdentityManager* identity_manager,
+ ConsentLevel consent_level,
+ const CoreAccountId& account_id);
+
// Makes an account available for the given email address, generating a GAIA ID
// and refresh token that correspond uniquely to that email address. Blocks
// until the account is available. Returns the AccountInfo of the
@@ -206,7 +215,7 @@ void SimulateSuccessfulFetchOfAccountInfo(IdentityManager* identity_manager,
const std::string& locale,
const std::string& picture_url);
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
account_manager::AccountManagerFacade* GetAccountManagerFacade(
IdentityManager* identity_manager);
#endif
diff --git a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc
index d0fcf8a380f..b5deee8a055 100644
--- a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc
+++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc
@@ -18,16 +18,38 @@ PrimaryAccountAccessTokenFetcher::PrimaryAccountAccessTokenFetcher(
const std::string& oauth_consumer_name,
IdentityManager* identity_manager,
const ScopeSet& scopes,
- AccessTokenFetcher::TokenCallback callback,
Mode mode,
ConsentLevel consent)
: oauth_consumer_name_(oauth_consumer_name),
identity_manager_(identity_manager),
scopes_(scopes),
- callback_(std::move(callback)),
mode_(mode),
consent_(consent) {
identity_manager_observation_.Observe(identity_manager_.get());
+}
+
+PrimaryAccountAccessTokenFetcher::PrimaryAccountAccessTokenFetcher(
+ const std::string& oauth_consumer_name,
+ IdentityManager* identity_manager,
+ const ScopeSet& scopes,
+ AccessTokenFetcher::TokenCallback callback,
+ Mode mode,
+ ConsentLevel consent)
+ : PrimaryAccountAccessTokenFetcher(oauth_consumer_name,
+ identity_manager,
+ scopes,
+ mode,
+ consent) {
+ Start(std::move(callback));
+}
+
+PrimaryAccountAccessTokenFetcher::~PrimaryAccountAccessTokenFetcher() = default;
+
+void PrimaryAccountAccessTokenFetcher::Start(
+ AccessTokenFetcher::TokenCallback callback) {
+ DCHECK(callback);
+ DCHECK(!callback_);
+ callback_ = std::move(callback);
if (mode_ == Mode::kImmediate || AreCredentialsAvailable()) {
StartAccessTokenRequest();
return;
@@ -35,8 +57,6 @@ PrimaryAccountAccessTokenFetcher::PrimaryAccountAccessTokenFetcher(
waiting_for_account_available_ = true;
}
-PrimaryAccountAccessTokenFetcher::~PrimaryAccountAccessTokenFetcher() = default;
-
CoreAccountId PrimaryAccountAccessTokenFetcher::GetAccountId() const {
return identity_manager_->GetPrimaryAccountId(consent_);
}
diff --git a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h
index d4a4802b8e6..5d4f8b4d49c 100644
--- a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h
+++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h
@@ -67,28 +67,29 @@ struct AccessTokenInfo;
// // create access token requests could and should just create
// // PrimaryAccountAccessTokenFetchers directly themselves rather than
// // introducing wrapper API surfaces.
+//
// MyClass::StartAccessTokenRequestForPrimaryAccount() {
// // Choose scopes to obtain for the access token.
// ScopeSet scopes;
// scopes.insert(GaiaConstants::kMyFirstScope);
// scopes.insert(GaiaConstants::kMySecondScope);
-
+//
// // Choose the mode in which to fetch the access token:
// // see AccessTokenFetcher::Mode below for definitions.
// auto mode =
// PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable;
-
+//
// // Create the fetcher.
// access_token_fetcher_ =
// std::make_unique<PrimaryAccountAccessTokenFetcher>(
// /*consumer_name=*/"MyClass",
// identity_manager_,
// scopes,
+// mode,
// base::BindOnce(&MyClass::OnAccessTokenRequestCompleted,
// // It is safe to use base::Unretained as
// // |this| owns |access_token_fetcher_|.
-// base::Unretained(this)),
-// mode);
+// base::Unretained(this)));
//
// }
// void MyClass::OnAccessTokenRequestCompleted(
@@ -111,7 +112,7 @@ struct AccessTokenInfo;
// }
// }
//
-// Concrete test example:
+// Concrete test example:
// TEST(MyClassTest, SuccessfulAccessTokenFetchForPrimaryAccount) {
// IdentityTestEnvironment identity_test_env;
//
@@ -120,7 +121,8 @@ struct AccessTokenInfo;
//
// // Make the primary account available, which should result in an access
// // token fetch being made on behalf of |my_class|.
-// identity_test_env.MakePrimaryAccountAvailable("test_email");
+// identity_test_env.
+// MakePrimaryAccountAvailable("test_email", ConsentLevel::kSync);
//
// identity_test_env.
// WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
@@ -131,6 +133,38 @@ struct AccessTokenInfo;
// // the test can now perform any desired validation of expected actions
// // |MyClass| took in response.
// }
+//
+// It is also possible to create a PrimaryAccountAccessTokenFetcher that does
+// not immediately start its access token request, as follows:
+//
+// auto access_token_fetcher =
+// std::make_unique<PrimaryAccountAccessTokenFetcher>(
+// /*consumer_name=*/"MyClass",
+// identity_manager_,
+// scopes,
+// mode);
+//
+// This allows an ownership model wherein MyClass does not store a
+// std::unique_ptr<PrimaryAccountAccessTokenFetcher> instance variable; instead,
+// the fetcher is bound to the |TokenCallback| itself and destroyed
+// automatically when the callback is called or destroyed. Continuing the
+// example from above:
+//
+// auto* access_token_fetcher_ptr = access_token_fetcher.get();
+// access_token_fetcher_ptr.Start(
+// base::BindOnce(&MyClass::OnAccessTokenRequestCompleted,
+// // |this| no longer owns |access_token_fetcher|, so
+// // a |WeakPtr| is required.
+// my_class_weak_factory_.GetWeakPtr()),
+// std::move(access_token_fetcher));
+//
+// void MyClass::OnAccessTokenRequestCompleted(
+// std::unique_ptr<PrimaryAccountAccessTokenFetcher> fetcher,
+// GoogleServiceAuthError error,
+// AccessTokenInfo access_token_info) {
+// ...
+// // |fetcher| is destroyed automatically when this callback finishes.
+// }
class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer {
public:
// Specifies how this instance should behave:
@@ -146,13 +180,17 @@ class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer {
// if the user is not signed in and doesn't sign in.
enum class Mode { kImmediate, kWaitUntilAvailable };
- // Instantiates a fetcher and immediately starts the process of obtaining an
- // OAuth2 access token for the given |scopes|. The |callback| is called once
- // the request completes (successful or not). If the
- // PrimaryAccountAccessTokenFetcher is destroyed before the process completes,
- // the callback is not called.
- // |consent| defaults to kSync because historically having an "authenticated"
- // account was tied to browser sync. See ./README.md.
+ // Instantiates an OAuth2 access token fetcher for the given |scopes|. Once
+ // the fetcher is created, the caller initiates its token request using
+ // |Start()|. |consent| defaults to |kSync| because historically having an
+ // "authenticated" account was tied to browser sync. See ./README.md.
+ PrimaryAccountAccessTokenFetcher(const std::string& oauth_consumer_name,
+ IdentityManager* identity_manager,
+ const ScopeSet& scopes,
+ Mode mode,
+ ConsentLevel consent = ConsentLevel::kSync);
+
+ // Convenience constructor that immediately issues the access token request.
PrimaryAccountAccessTokenFetcher(const std::string& oauth_consumer_name,
IdentityManager* identity_manager,
const ScopeSet& scopes,
@@ -167,6 +205,12 @@ class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer {
~PrimaryAccountAccessTokenFetcher() override;
+ // Starts the process of obtaining an OAuth2 access token for the fetcher's
+ // |scopes_|. |callback| is called once the request completes (successfully
+ // or not). If the fetcher is destroyed before the process completes, the
+ // callback is not called.
+ void Start(AccessTokenFetcher::TokenCallback callback);
+
// Exposed for tests.
bool access_token_request_retried() { return access_token_retried_; }
diff --git a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc
index a4d347a6789..ce483752431 100644
--- a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc
+++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc
@@ -75,6 +75,17 @@ class PrimaryAccountAccessTokenFetcherTest
return CreateFetcher(std::move(callback), mode, consent);
}
+ std::unique_ptr<PrimaryAccountAccessTokenFetcher> CreateDelayedStartFetcher(
+ PrimaryAccountAccessTokenFetcher::Mode mode) {
+ // API scope that does not require consent.
+ std::set<std::string> scopes = {
+ GaiaConstants::kChromeSafeBrowsingOAuth2Scope};
+ ConsentLevel consent = GetParam();
+ return std::make_unique<PrimaryAccountAccessTokenFetcher>(
+ "test_consumer", identity_test_env_->identity_manager(), scopes, mode,
+ consent);
+ }
+
IdentityTestEnvironment* identity_test_env() {
return identity_test_env_.get();
}
@@ -121,6 +132,27 @@ TEST_P(PrimaryAccountAccessTokenFetcherTest, OneShotShouldReturnAccessToken) {
}
TEST_P(PrimaryAccountAccessTokenFetcherTest,
+ DelayedOneShotShouldReturnAccessToken) {
+ TestTokenCallback callback;
+
+ CoreAccountId account_id = SignIn();
+
+ // Signed in and refresh token already exists, so this should result in a
+ // request for an access token.
+ auto fetcher = CreateDelayedStartFetcher(
+ PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
+
+ // Once the access token request is fulfilled, we should get called back with
+ // the access token.
+ EXPECT_CALL(callback, Run(GoogleServiceAuthError::AuthErrorNone(),
+ access_token_info()));
+ fetcher->Start(callback.Get());
+ identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ access_token_info().token, access_token_info().expiration_time,
+ access_token_info().id_token);
+}
+
+TEST_P(PrimaryAccountAccessTokenFetcherTest,
WaitAndRetryShouldReturnAccessToken) {
TestTokenCallback callback;
@@ -141,6 +173,27 @@ TEST_P(PrimaryAccountAccessTokenFetcherTest,
access_token_info().id_token);
}
+TEST_P(PrimaryAccountAccessTokenFetcherTest,
+ DelayedWaitAndRetryShouldReturnAccessToken) {
+ TestTokenCallback callback;
+
+ CoreAccountId account_id = SignIn();
+
+ // Signed in and refresh token already exists, so this should result in a
+ // request for an access token.
+ auto fetcher = CreateDelayedStartFetcher(
+ PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
+
+ // Once the access token request is fulfilled, we should get called back with
+ // the access token.
+ EXPECT_CALL(callback, Run(GoogleServiceAuthError::AuthErrorNone(),
+ access_token_info()));
+ fetcher->Start(callback.Get());
+ identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ access_token_info().token, access_token_info().expiration_time,
+ access_token_info().id_token);
+}
+
TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldNotReplyIfDestroyed) {
TestTokenCallback callback;
@@ -151,6 +204,8 @@ TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldNotReplyIfDestroyed) {
auto fetcher = CreateFetcher(
callback.Get(), PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
+ EXPECT_CALL(callback, Run).Times(0);
+
// Destroy the fetcher before the access token request is fulfilled.
fetcher.reset();
@@ -160,6 +215,20 @@ TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldNotReplyIfDestroyed) {
access_token_info().id_token);
}
+TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldNotReplyIfNotStarted) {
+ TestTokenCallback callback;
+
+ CoreAccountId account_id = SignIn();
+
+ // Signed in and refresh token already exists, so this would result in a
+ // request for an access token if the fetcher were started.
+ auto fetcher = CreateDelayedStartFetcher(
+ PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
+
+ // No token request generated because the fetcher has not been started.
+ EXPECT_FALSE(identity_test_env()->IsAccessTokenRequestPending());
+}
+
TEST_P(PrimaryAccountAccessTokenFetcherTest, OneShotCallsBackWhenSignedOut) {
base::RunLoop run_loop;
@@ -201,6 +270,8 @@ TEST_P(PrimaryAccountAccessTokenFetcherTest,
auto fetcher = CreateFetcher(
callback.Get(),
PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
+
+ EXPECT_CALL(callback, Run).Times(0);
}
TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldWaitForSignIn) {
diff --git a/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc b/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc
index aa0962935e2..25c7303fec4 100644
--- a/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc
+++ b/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc
@@ -518,14 +518,6 @@ TEST_F(PrimaryAccountMutatorTest, RevokeSyncConsent_DiceConsistency) {
RemoveAccountExpectation::kKeepAll);
}
-// Test that revoking the sync consent when DICE account consistency is
-// enabled clears the primary account if it uin auth error state.
-TEST_F(PrimaryAccountMutatorTest, RevokeSyncConsent_DiceConsistency_AuthError) {
- RunRevokeSyncConsentTest(signin::AccountConsistencyMethod::kDice,
- RemoveAccountExpectation::kRemoveAll,
- AuthExpectation::kAuthError);
-}
-
#else //! BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(PrimaryAccountMutatorTest, CROS_ASH_RevokeSyncConsent) {
diff --git a/chromium/components/site_engagement/content/android/BUILD.gn b/chromium/components/site_engagement/content/android/BUILD.gn
index 851994a29ca..612ae40bfef 100644
--- a/chromium/components/site_engagement/content/android/BUILD.gn
+++ b/chromium/components/site_engagement/content/android/BUILD.gn
@@ -8,6 +8,8 @@ android_library("java") {
sources = [ "java/src/org/chromium/components/site_engagement/SiteEngagementService.java" ]
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_full_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/chromium/components/site_engagement/content/site_engagement_metrics.cc b/chromium/components/site_engagement/content/site_engagement_metrics.cc
index bb4d5adae9c..92729b706ce 100644
--- a/chromium/components/site_engagement/content/site_engagement_metrics.cc
+++ b/chromium/components/site_engagement/content/site_engagement_metrics.cc
@@ -40,9 +40,6 @@ const char SiteEngagementMetrics::kEngagementScoreHistogram[] =
const char SiteEngagementMetrics::kOriginsWithMaxEngagementHistogram[] =
"SiteEngagementService.OriginsWithMaxEngagement";
-const char SiteEngagementMetrics::kOriginsWithMaxDailyEngagementHistogram[] =
- "SiteEngagementService.OriginsWithMaxDailyEngagement";
-
const char SiteEngagementMetrics::kEngagementTypeHistogram[] =
"SiteEngagementService.EngagementType";
@@ -103,12 +100,6 @@ void SiteEngagementMetrics::RecordOriginsWithMaxEngagement(int total_origins) {
UMA_HISTOGRAM_COUNTS_100(kOriginsWithMaxEngagementHistogram, total_origins);
}
-void SiteEngagementMetrics::RecordOriginsWithMaxDailyEngagement(
- int total_origins) {
- UMA_HISTOGRAM_COUNTS_100(kOriginsWithMaxDailyEngagementHistogram,
- total_origins);
-}
-
void SiteEngagementMetrics::RecordEngagement(EngagementType type) {
UMA_HISTOGRAM_ENUMERATION(kEngagementTypeHistogram, type,
EngagementType::kLast);
diff --git a/chromium/components/site_engagement/content/site_engagement_metrics.h b/chromium/components/site_engagement/content/site_engagement_metrics.h
index 988fd0e07f0..cfa31da02f7 100644
--- a/chromium/components/site_engagement/content/site_engagement_metrics.h
+++ b/chromium/components/site_engagement/content/site_engagement_metrics.h
@@ -44,7 +44,6 @@ class SiteEngagementMetrics {
static const char kMedianEngagementHistogram[];
static const char kEngagementScoreHistogram[];
static const char kOriginsWithMaxEngagementHistogram[];
- static const char kOriginsWithMaxDailyEngagementHistogram[];
static const char kEngagementTypeHistogram[];
static const char kEngagementBucketHistogramBase[];
static const char kDaysSinceLastShortcutLaunchHistogram[];
diff --git a/chromium/components/site_engagement/content/site_engagement_score.h b/chromium/components/site_engagement/content/site_engagement_score.h
index 24307cb998b..94dfebcf0e2 100644
--- a/chromium/components/site_engagement/content/site_engagement_score.h
+++ b/chromium/components/site_engagement/content/site_engagement_score.h
@@ -11,6 +11,7 @@
#include <utility>
#include "base/gtest_prod_util.h"
+#include "base/memory/raw_ptr_exclusion.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/site_engagement/core/mojom/site_engagement_details.mojom-forward.h"
@@ -216,7 +217,7 @@ class SiteEngagementScore {
// the SiteEngagementService.
// `clock_` is not a raw_ptr<...> for performance reasons (based on analysis
// of sampling profiler data).
- base::Clock* clock_;
+ RAW_PTR_EXCLUSION base::Clock* clock_;
// |raw_score_| is the score before any decay is applied.
double raw_score_;
@@ -243,7 +244,7 @@ class SiteEngagementScore {
// The settings to write this score to when Commit() is called.
// `settings_map_` is not a raw_ptr<...> for performance reasons (based on
// analysis of sampling profiler data).
- HostContentSettingsMap* settings_map_;
+ RAW_PTR_EXCLUSION HostContentSettingsMap* settings_map_;
};
} // namespace site_engagement
diff --git a/chromium/components/site_engagement/content/site_engagement_service.cc b/chromium/components/site_engagement/content/site_engagement_service.cc
index 47a987d2d39..7efc8347fab 100644
--- a/chromium/components/site_engagement/content/site_engagement_service.cc
+++ b/chromium/components/site_engagement/content/site_engagement_service.cc
@@ -558,8 +558,6 @@ void SiteEngagementService::RecordMetrics(
GetMedianEngagementFromSortedDetails(details));
SiteEngagementMetrics::RecordEngagementScores(details);
- SiteEngagementMetrics::RecordOriginsWithMaxDailyEngagement(
- OriginsWithMaxDailyEngagement());
SiteEngagementMetrics::RecordOriginsWithMaxEngagement(
origins_with_max_engagement);
}
@@ -688,22 +686,4 @@ SiteEngagementScore SiteEngagementService::CreateEngagementScore(
permissions::PermissionsClient::Get()->GetSettingsMap(browser_context_));
}
-int SiteEngagementService::OriginsWithMaxDailyEngagement() const {
- int total_origins = 0;
-
- // We cannot call GetScoreMap as we need the score objects, not raw scores.
- for (const auto& site : GetContentSettingsFromBrowserContext(
- browser_context_, ContentSettingsType::SITE_ENGAGEMENT)) {
- GURL origin(site.primary_pattern.ToString());
-
- if (!origin.is_valid())
- continue;
-
- if (CreateEngagementScore(origin).MaxPointsPerDayAdded())
- ++total_origins;
- }
-
- return total_origins;
-}
-
} // namespace site_engagement
diff --git a/chromium/components/site_engagement/content/site_engagement_service.h b/chromium/components/site_engagement/content/site_engagement_service.h
index 9aa0ed19ceb..7aadbb3df9a 100644
--- a/chromium/components/site_engagement/content/site_engagement_service.h
+++ b/chromium/components/site_engagement/content/site_engagement_service.h
@@ -298,10 +298,6 @@ class SiteEngagementService : public KeyedService,
// browser for an extended period of time do not have their engagement decay.
bool IsLastEngagementStale() const;
- // Returns the number of origins with maximum daily and total engagement
- // respectively.
- int OriginsWithMaxDailyEngagement() const;
-
// Add and remove observers of this service.
void AddObserver(SiteEngagementObserver* observer);
void RemoveObserver(SiteEngagementObserver* observer);
diff --git a/chromium/components/site_isolation/site_isolation_policy_unittest.cc b/chromium/components/site_isolation/site_isolation_policy_unittest.cc
index c9cf70c407b..9ed07c030dd 100644
--- a/chromium/components/site_isolation/site_isolation_policy_unittest.cc
+++ b/chromium/components/site_isolation/site_isolation_policy_unittest.cc
@@ -5,6 +5,7 @@
#include "components/site_isolation/site_isolation_policy.h"
#include "base/base_switches.h"
+#include "base/command_line.h"
#include "base/json/values_util.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
diff --git a/chromium/components/site_settings_strings.grdp b/chromium/components/site_settings_strings.grdp
index d88234f2fcd..30559a5d36a 100644
--- a/chromium/components/site_settings_strings.grdp
+++ b/chromium/components/site_settings_strings.grdp
@@ -148,6 +148,12 @@
<!-- Site Settings desktop-only -->
<if expr="not is_android">
+ <message name="IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API" desc="The label used for the third-party sign-in site settings controls.">
+ Third-party sign-in
+ </message>
+ <message name="IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API_MID_SENTENCE" desc="The label used for the third-party sign-in site settings controls when used mid-sentence.">
+ third-party sign-in
+ </message>
<message name="IDS_SITE_SETTINGS_TYPE_FILE_SYSTEM_ACCESS_WRITE" desc="The label used for the File System Access write site settings controls. The write permission determines whether sites are allowed to save to the original file that was selected by the user through the File System Access API.">
File editing
</message>
diff --git a/chromium/components/site_settings_strings_grdp/IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API.png.sha1 b/chromium/components/site_settings_strings_grdp/IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API.png.sha1
new file mode 100644
index 00000000000..47984e4ad1a
--- /dev/null
+++ b/chromium/components/site_settings_strings_grdp/IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API.png.sha1
@@ -0,0 +1 @@
+4e0f7428b0978106d7f4e4be3b9339f5d65d7b2c \ No newline at end of file
diff --git a/chromium/components/site_settings_strings_grdp/IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API_MID_SENTENCE.png.sha1 b/chromium/components/site_settings_strings_grdp/IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API_MID_SENTENCE.png.sha1
new file mode 100644
index 00000000000..02e3f22b9c0
--- /dev/null
+++ b/chromium/components/site_settings_strings_grdp/IDS_SITE_SETTINGS_TYPE_FEDERATED_IDENTITY_API_MID_SENTENCE.png.sha1
@@ -0,0 +1 @@
+0a3a30e094fc5bdd013e3982decfbf85fa03c861 \ No newline at end of file
diff --git a/chromium/components/site_settings_strings_grdp/OWNERS b/chromium/components/site_settings_strings_grdp/OWNERS
new file mode 100644
index 00000000000..87f9a0af35e
--- /dev/null
+++ b/chromium/components/site_settings_strings_grdp/OWNERS
@@ -0,0 +1,2 @@
+file://components/browser_ui/site_settings/OWNERS
+file://chrome/browser/resources/settings/site_settings/OWNERS
diff --git a/chromium/components/spellcheck/browser/android/BUILD.gn b/chromium/components/spellcheck/browser/android/BUILD.gn
index 6c847d7d689..2a95e85e037 100644
--- a/chromium/components/spellcheck/browser/android/BUILD.gn
+++ b/chromium/components/spellcheck/browser/android/BUILD.gn
@@ -9,7 +9,11 @@ generate_jni("jni_headers") {
}
android_library("java") {
- deps = [ "//base:base_java" ]
+ deps = [
+ "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
+ ]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
sources = [ "java/src/org/chromium/components/spellcheck/SpellCheckerSessionBridge.java" ]
diff --git a/chromium/components/spellcheck/browser/spellcheck_platform_mac.mm b/chromium/components/spellcheck/browser/spellcheck_platform_mac.mm
index 63512e1ae34..a14986166fe 100644
--- a/chromium/components/spellcheck/browser/spellcheck_platform_mac.mm
+++ b/chromium/components/spellcheck/browser/spellcheck_platform_mac.mm
@@ -128,13 +128,11 @@ std::string GetSpellCheckerLanguage() {
}
bool SpellCheckerAvailable() {
- // If this file was compiled, then we know that we are on OS X 10.5 at least
- // and can safely return true here.
return true;
}
bool SpellCheckerProvidesPanel() {
- // OS X has a Spelling Panel, so we can return true here.
+ // macOS has a Spelling Panel, so we can return true here.
return true;
}
@@ -207,10 +205,12 @@ bool CheckSpelling(const std::u16string& word_to_check, int tag) {
// Convert the word to an NSString.
NSString* NS_word_to_check = base::SysUTF16ToNSString(word_to_check);
// Check the spelling, starting at the beginning of the word.
- spell_range = [SharedSpellChecker()
- checkSpellingOfString:NS_word_to_check startingAt:0
- language:nil wrap:NO inSpellDocumentWithTag:tag
- wordCount:NULL];
+ spell_range = [SharedSpellChecker() checkSpellingOfString:NS_word_to_check
+ startingAt:0
+ language:nil
+ wrap:NO
+ inSpellDocumentWithTag:tag
+ wordCount:nullptr];
// If the length of the misspelled word == 0,
// then there is no misspelled word.
@@ -291,10 +291,9 @@ void RequestTextCheck(PlatformSpellChecker* spell_checker_instance,
// In this use case, the spell checker should never
// return anything but a single range per result.
- check_results.push_back(SpellCheckResult(
- SpellCheckResult::SPELLING,
- [result range].location,
- [result range].length));
+ check_results.emplace_back(SpellCheckResult::SPELLING,
+ [result range].location,
+ [result range].length);
}
// TODO(groby): Verify we don't need to post from here.
std::move(callback).Run(check_results);
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider.cc b/chromium/components/spellcheck/renderer/spellcheck_provider.cc
index 2e6b48af364..77ebad0a7ba 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider.cc
@@ -439,7 +439,7 @@ bool SpellCheckProvider::SatisfyRequestFromCache(
if (start <= text_length && end <= text_length)
++result_size;
}
- blink::WebVector<blink::WebTextCheckingResult> results(last_results_.Data(),
+ blink::WebVector<blink::WebTextCheckingResult> results(last_results_.data(),
result_size);
completion->DidFinishCheckingText(results);
return true;
diff --git a/chromium/components/ssl_errors/error_info.cc b/chromium/components/ssl_errors/error_info.cc
index a1c66510c29..994656e5e72 100644
--- a/chromium/components/ssl_errors/error_info.cc
+++ b/chromium/components/ssl_errors/error_info.cc
@@ -8,9 +8,9 @@
#include "base/i18n/message_formatter.h"
#include "base/notreached.h"
+#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "components/strings/grit/components_strings.h"
-#include "net/base/escape.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_status_flags.h"
#include "net/ssl/ssl_info.h"
@@ -58,7 +58,7 @@ ErrorInfo ErrorInfo::CreateError(ErrorType error_type,
details = l10n_util::GetStringFUTF16(
IDS_CERT_ERROR_COMMON_NAME_INVALID_DETAILS,
UTF8ToUTF16(request_url.host()),
- net::EscapeForHTML(UTF8ToUTF16(dns_names[i])));
+ base::EscapeForHTML(UTF8ToUTF16(dns_names[i])));
}
short_description = l10n_util::GetStringUTF16(
diff --git a/chromium/components/storage_monitor/BUILD.gn b/chromium/components/storage_monitor/BUILD.gn
index 28c2ecbf6d6..529def5f24a 100644
--- a/chromium/components/storage_monitor/BUILD.gn
+++ b/chromium/components/storage_monitor/BUILD.gn
@@ -164,8 +164,8 @@ source_set("unit_tests") {
]
}
- if (use_dbus) {
- if (is_chromeos_ash) {
+ if (is_chromeos_ash) {
+ if (use_dbus) {
deps += [
"//services/device/public/mojom",
"//testing/gmock",
@@ -174,7 +174,9 @@ source_set("unit_tests") {
"mtp_manager_client_chromeos_unittest.cc",
"storage_monitor_chromeos_unittest.cc",
]
- } else if (is_linux || is_chromeos_lacros) {
+ }
+ } else if (is_linux || is_chromeos_lacros) {
+ if (use_udev) {
sources += [ "storage_monitor_linux_unittest.cc" ]
}
}
diff --git a/chromium/components/storage_monitor/image_capture_device_manager.mm b/chromium/components/storage_monitor/image_capture_device_manager.mm
index 9712e51f3ae..1da420f1e6f 100644
--- a/chromium/components/storage_monitor/image_capture_device_manager.mm
+++ b/chromium/components/storage_monitor/image_capture_device_manager.mm
@@ -55,9 +55,10 @@ storage_monitor::ImageCaptureDeviceManager* g_image_capture_device_manager =
_deviceBrowser.reset([[ICDeviceBrowser alloc] init]);
[_deviceBrowser setDelegate:self];
- [_deviceBrowser setBrowsedDeviceTypeMask:
- static_cast<ICDeviceTypeMask>(
- ICDeviceTypeMaskCamera | ICDeviceLocationTypeMaskLocal)];
+ [_deviceBrowser
+ setBrowsedDeviceTypeMask:ICDeviceTypeMask{
+ ICDeviceTypeMaskCamera |
+ UInt{ICDeviceLocationTypeMaskLocal}}];
[_deviceBrowser start];
}
return self;
diff --git a/chromium/components/strings/components_strings_af.xtb b/chromium/components/strings/components_strings_af.xtb
index 963bce1e930..c92f92abffe 100644
--- a/chromium/components/strings/components_strings_af.xtb
+++ b/chromium/components/strings/components_strings_af.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Toelae, beurse en finansiële hulp</translation>
<translation id="1048785276086539861">Hierdie dokument sal na enkelbladsyaansig toe terugkeer wanneer jy aantekeninge redigeer</translation>
<translation id="1050038467049342496">Maak ander programme toe</translation>
+<translation id="1053959602163383901">Jy het gekies om met ’n stawingstoestel te verifieer op webwerwe wat <ph name="PROVIDER_ORIGIN" /> gebruik. Hierdie verskaffer het dalk inligting oor jou betaalmetode geberg, wat jy kan <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">Ontdoen byvoeging</translation>
<translation id="1056663316309890343">Fotosagteware</translation>
<translation id="1056898198331236512">Waarskuwing</translation>
@@ -119,6 +120,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="1270502636509132238">Oplaaimetode</translation>
<translation id="1281476433249504884">Stapelaar 1</translation>
<translation id="1285320974508926690">Moet nooit hierdie werf vertaal nie</translation>
+<translation id="1288548991597756084">Stoor kaart veilig</translation>
<translation id="1292571435393770077">Laai 16</translation>
<translation id="1292701964462482250">"Sagteware op jou rekenaar verhoed dat Chrome veilig aan die web koppel" (Net Windows-rekenaars)</translation>
<translation id="1294154142200295408">Bevellynvariasies</translation>
@@ -223,6 +225,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
&lt;p&gt;Klik &lt;strong&gt;Koppel&lt;/strong&gt; op die bladsy wat jy probeer oopmaak om die fout reg te stel.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landskapontwerp</translation>
<translation id="1513706915089223971">Lys geskiedenisinskrywings</translation>
+<translation id="1516097932025103760">Dit sal geënkripteer word, veilig gestoor word en die CVC word nooit geberg nie.</translation>
<translation id="1517433312004943670">Foonnommer vereis</translation>
<translation id="1519264250979466059">Boudatum</translation>
<translation id="1521159554480556801">Vesel- en tekstielkuns</translation>
@@ -288,6 +291,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="1662550410081243962">Stoor en vul betaalmetodes in</translation>
<translation id="1663943134801823270">Kaarte en adresse kom uit Chrome. Jy kan hulle in <ph name="BEGIN_LINK" />Instellings<ph name="END_LINK" /> bestuur.</translation>
<translation id="1671391448414634642">Bladsye in <ph name="SOURCE_LANGUAGE" /> sal van nou af in <ph name="TARGET_LANGUAGE" /> vertaal word.</translation>
+<translation id="1673886523110456987">Betaal met <ph name="CARD_DETAIL" /> om aanbieding te gebruik</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> na <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Kort kant eerste</translation>
<translation id="168693727862418163">Hierdie beleidwaarde kon nie teen die skema bekragtig word nie en sal geïgnoreer word.</translation>
@@ -306,6 +310,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="1717218214683051432">Bewegingsensors</translation>
<translation id="1717494416764505390">Posbus 3</translation>
<translation id="1718029547804390981">Dokument is te groot om aantekeninge by te maak</translation>
+<translation id="1720941539803966190">Maak tutoriaal toe</translation>
<translation id="1721424275792716183">* Veld word vereis</translation>
<translation id="1727613060316725209">Sertifikaat is geldig</translation>
<translation id="1727741090716970331">Voeg geldige kaartnommer by</translation>
@@ -422,8 +427,8 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="205212645995975601">Braai en rooster</translation>
<translation id="2053111141626950936">Bladsye in <ph name="LANGUAGE" /> sal nie vertaal word nie.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Wanneer hierdie kontrole aan is en die status aktief is, bepaal Chrome met watter groot groep mense, of "kohort", jou onlangse blaai-aktiwiteit die meeste ooreenstem. Adverteerders kan advertensies vir die groep kies en jou blaai-aktiwiteit word privaat op jou toestel gehou. Jou groep word elke dag opgedateer.}=1{Wanneer hierdie kontrole aan is en die status aktief is, bepaal Chrome met watter groot groep mense, of "kohort", jou onlangse blaai-aktiwiteit die meeste ooreenstem. Adverteerders kan advertensies vir die groep kies en jou blaai-aktiwiteit word privaat op jou toestel gehou. Jou groep word elke dag opgedateer.}other{Wanneer hierdie kontrole aan is en die status aktief is, bepaal Chrome met watter groot groep mense, of "kohort", jou onlangse blaai-aktiwiteit die meeste ooreenstem. Adverteerders kan advertensies vir die groep kies en jou blaai-aktiwiteit word privaat op jou toestel gehou. Jou groep word elke {NUM_DAYS} dae opgedateer.}}</translation>
-<translation id="2053553514270667976">Poskode</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 voorstel}other{# voorstelle}}</translation>
+<translation id="2066915425250589881">versoek om uit te vee</translation>
<translation id="2068528718802935086">Babas en kleuters</translation>
<translation id="2071156619270205202">Hierdie kaart kwalifiseer nie vir virtuele kaartnommer nie.</translation>
<translation id="2071692954027939183">Kennisgewings is outomaties geblokkeer omdat jy hulle gewoonlik nie toelaat nie</translation>
@@ -435,7 +440,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="2088086323192747268">Bestuur Sinkronisering-knoppie, druk Enter om te bestuur watter inligting jy in Chrome-instellings sinkroniseer</translation>
<translation id="2091887806945687916">Klank</translation>
<translation id="2094505752054353250">Domein stem nie ooreen nie</translation>
-<translation id="2096368010154057602">Departement</translation>
<translation id="2099652385553570808">Tripelkram links</translation>
<translation id="2101225219012730419">Weergawe:</translation>
<translation id="2102134110707549001">Stel sterk wagwoord voor …</translation>
@@ -472,7 +476,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="2185836064961771414">Amerikaanse voetbal</translation>
<translation id="2187317261103489799">Bespeur (verstek)</translation>
<translation id="2188375229972301266">Veelvuldige pons onder</translation>
-<translation id="2188852899391513400">Die wagwoord wat jy gebruik het, is in 'n dataskending gekry. Google Wagwoordbestuurder beveel aan dat jy dit nou verander en jou gestoorde wagwoorde dan nagaan om jou rekeninge te beveilig.</translation>
<translation id="219906046732893612">Tuisverbetering</translation>
<translation id="2202020181578195191">Voer 'n geldige vervaljaar in</translation>
<translation id="22081806969704220">Laai 3</translation>
@@ -483,6 +486,8 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="2215727959747642672">Lêerwysiging</translation>
<translation id="2215963164070968490">Honde</translation>
<translation id="2218879909401188352">Aanvallers wat tans op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> is, kan dalk gevaarlike programme installeer wat jou toestel kan beskadig, verskuilde koste by jou toestelrekening kan voeg of jou persoonlike inligting kan steel. <ph name="BEGIN_LEARN_MORE_LINK" />Kom meer te wete<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Herbegin tutoriaal</translation>
+<translation id="2219735899272417925">Toestelterugstelling word vereis</translation>
<translation id="2224337661447660594">Geen internet nie</translation>
<translation id="2230458221926704099">Maak jou verbinding reg deur die <ph name="BEGIN_LINK" />diagnostiekprogram<ph name="END_LINK" /> te gebruik</translation>
<translation id="2239100178324503013">Stuur nou</translation>
@@ -580,11 +585,13 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="2512101340618156538">Nie toegelaat nie (verstek)</translation>
<translation id="2512413427717747692">Stel Chrome as verstekblaaierknoppie; druk Enter om Chrome in iOS-instellings as die stelsel se verstekblaaier te stel</translation>
<translation id="2515629240566999685">Die sein in jou omgewing nagaan</translation>
+<translation id="2515761554693942801">Jy het gekies om met Raak-ID te verifieer op webwerwe wat <ph name="PROVIDER_ORIGIN" /> gebruik. Hierdie verskaffer het dalk inligting oor jou betaalmetode geberg, wat jy kan <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Kram regs onder</translation>
<translation id="2521736961081452453">Skep vorm</translation>
<translation id="2523886232349826891">Net op hierdie toestel gestoor</translation>
<translation id="2524461107774643265">Voeg meer inligting by</translation>
<translation id="2529899080962247600">Hierdie veld moenie meer as <ph name="MAX_ITEMS_LIMIT" /> inskrywings hê nie. Alle verdere inskrywings sal geïgnoreer word.</translation>
+<translation id="253493526287553278">Sien promosiekodebesonderhede</translation>
<translation id="2535585790302968248">Maak 'n nuwe Incognito-oortjie oop om privaat te blaai</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{en nog 1}other{en nog #}}</translation>
<translation id="2536110899380797252">Voeg adres by</translation>
@@ -620,7 +627,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="259821504105826686">Fotografie en digitale kuns</translation>
<translation id="2601150049980261779">Romanseflieks</translation>
<translation id="2604589665489080024">Popmusiek</translation>
-<translation id="2609632851001447353">Variasies</translation>
<translation id="2610561535971892504">Klik om te kopieer</translation>
<translation id="2617988307566202237">Chrome sal nie die volgende inligting <ph name="BEGIN_EMPHASIS" />stoor nie<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Verjaarsdae en naamdae</translation>
<translation id="2677748264148917807">Gaan uit</translation>
+<translation id="2679714844901977852">Stoor jou kaart- en faktureringinligting in jou Google-rekening <ph name="USER_EMAIL" /> om veilig en vinniger te betaal</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Beste passing</translation>
<translation id="2688969097326701645">Ja, gaan voort</translation>
@@ -701,7 +708,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="2854764410992194509">Internetdiensverskaffers</translation>
<translation id="2856444702002559011">Aanvallers probeer dalk om jou inligting (byvoorbeeld: wagwoorde, boodskappe of kredietkaartinligting) op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> te steel. <ph name="BEGIN_LEARN_MORE_LINK" />Kom meer te wete<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Hierdie werf wys indringerige of misleidende advertensies.</translation>
-<translation id="286512204874376891">'n Virtuele kaart verberg jou werklike kaart om te help om jou teen potensiële bedrog te beskerm. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Vriendelik</translation>
<translation id="28761159517501904">Flieks</translation>
<translation id="2876489322757410363">Verlaat tans Incognitomodus om deur 'n eksterne program te betaal. Gaan voort?</translation>
@@ -802,7 +808,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="3158539265159265653">Skyf</translation>
<translation id="3162559335345991374">Die Wi-Fi wat jy gebruik, kan vereis dat jy sy aanmeldbladsy besoek.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Eiland</translation>
<translation id="3176929007561373547">Gaan jou instaanbedienerinstellings na of kontak jou netwerkadministrateur om
seker te maak dat jou instaanbediener werk. As jy dink jy moenie
'n instaanbediener gebruik nie:
@@ -881,9 +886,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="3369192424181595722">Horlosiefout</translation>
<translation id="3369459162151165748">Voertuigonderdele en -bykomstighede</translation>
<translation id="3371064404604898522">Stel Chrome as verstekblaaier</translation>
-<translation id="3371076217486966826"><ph name="URL" /> wil:
- • 'n 3D-kaart van jou omgewing skep en kameraposisie naspoor
- • Jou kamera gebruik</translation>
<translation id="337363190475750230">Onvoorsien</translation>
<translation id="3375754925484257129">Doen Chrome-veiligheidskontrole</translation>
<translation id="3377144306166885718">Die bediener gebruik 'n verouderde weergawe van TLS.</translation>
@@ -900,6 +902,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="3399952811970034796">Afleweringadres</translation>
<translation id="3402261774528610252">Die verbinding wat gebruik word om hierdie werf te laai, gebruik TLS 1.0 of TLS 1.1, wat opgeskort is en in die toekoms gedeaktiveer sal word. Nadat dit gedeaktiveer is, sal gebruikers verhinder word om hierdie werf te laai. Die bediener moet TLS 1.2 of nuwer aktiveer.</translation>
<translation id="3405664148539009465">Pasmaak lettertipes</translation>
+<translation id="3407789382767355356">derdeparty-aanmelding</translation>
<translation id="3409896703495473338">Bestuur sekuriteitinstellings</translation>
<translation id="3414952576877147120">Grootte:</translation>
<translation id="3417660076059365994">Lêers wat jy oplaai of aanheg, word na Google Cloud of derde partye gestuur om ontleed te word. Hulle kan byvoorbeeld vir sensitiewe data of wanware geskandeer word.</translation>
@@ -932,6 +935,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="3477679029130949506">Flieklysinskrywings en teatervertoontye</translation>
<translation id="3479552764303398839">Nie nou nie</translation>
<translation id="3484560055331845446">Jy kan toegang tot jou Google-rekening verloor. Chrome beveel aan dat jy jou wagwoord nou verander. Jy sal gevra word om aan te meld.</translation>
+<translation id="3484861421501147767">Onthounota: gestoorde promosiekode is beskikbaar</translation>
<translation id="3487845404393360112">Laai 4</translation>
<translation id="3495081129428749620">Vind in bladsy
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="3810973564298564668">Bestuur</translation>
<translation id="3816482573645936981">Waarde (vervang)</translation>
<translation id="382518646247711829">As jy 'n instaanbediener gebruik …</translation>
+<translation id="3826050100957962900">Derdeparty-aanmelding</translation>
<translation id="3827112369919217609">Absoluut</translation>
<translation id="3827666161959873541">Gesinsflieks</translation>
<translation id="3828924085048779000">Leë wagfrase word nie toegelaat nie.</translation>
@@ -1068,9 +1073,9 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="3858027520442213535">Dateer datum en tyd op</translation>
<translation id="3858860766373142691">Naam</translation>
<translation id="3872834068356954457">Wetenskap</translation>
+<translation id="3875783148670536197">Wys my hoe</translation>
<translation id="3881478300875776315">Wys minder reëls</translation>
<translation id="3884278016824448484">Teenstrydige toestelidentifiseerder</translation>
-<translation id="3885155851504623709">Gemeente</translation>
<translation id="388632593194507180">Monitering is bespeur</translation>
<translation id="3886948180919384617">Stapelaar 3</translation>
<translation id="3890664840433101773">Voeg e-posadres by</translation>
@@ -1100,9 +1105,11 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="3973234410852337861"><ph name="HOST_NAME" /> is geblokkeer</translation>
<translation id="3978338123949022456">Soekmodus; tik ’n navraag in en druk Enter om met <ph name="KEYWORD_SUFFIX" /> te soek</translation>
<translation id="398470910934384994">Voëls</translation>
+<translation id="3985750352229496475">Bestuur adresse …</translation>
<translation id="3986705137476756801">Skakel Intydse Onderskrifte af vir nou</translation>
<translation id="3987940399970879459">Minder as 1 MB</translation>
<translation id="3990250421422698716">Gelykskudafwyking</translation>
+<translation id="3992684624889376114">Meer oor hierdie bladsy</translation>
<translation id="3996311196211510766">Die werf <ph name="ORIGIN" /> het versoek dat 'n oorsprongbeleid
op alle versoeke daaraan toegepas word, maar hierdie beleid kan nie op die oomblik toegepas word nie.</translation>
<translation id="4006465311664329701">Betaalmetodes, aanbiedings en adresse wat Google Pay gebruik</translation>
@@ -1227,6 +1234,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="4305666528087210886">Kon nie toegang tot jou lêer kry nie</translation>
<translation id="4306529830550717874">Stoor adres?</translation>
<translation id="4306812610847412719">knipbord</translation>
+<translation id="4310070645992025887">Deursoek jou Reise</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokkeer (verstek)</translation>
<translation id="4314815835985389558">Bestuur sinkronisering</translation>
@@ -1277,6 +1285,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Die gebruik van 'n instaanbediener is gedeaktiveer, maar 'n eksplisiete instaanbedieneropstelling word gespesifiseer.</translation>
<translation id="4441832193888514600">Geïgnoreer omdat die beleid slegs as ’n wolkgebruikerbeleid gestel kan word.</translation>
+<translation id="4442470707340296952">Chrome-oortjies</translation>
<translation id="4450893287417543264">Moenie weer wys nie</translation>
<translation id="4451135742916150903">Kan vra om aan HID-toestelle te koppel</translation>
<translation id="4452328064229197696">Die wagwoord wat jy gebruik het, is in 'n dataskending gekry. Google Wagwoordbestuurder beveel aan dat jy jou gestoorde wagwoorde nagaan om jou rekeninge te beveilig.</translation>
@@ -1415,6 +1424,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="483241715238664915">Skakel waarskuwings aan</translation>
<translation id="4834250788637067901">Betaalmetodes, aanbiedings en adresse wat Google Pay gebruik</translation>
<translation id="4838327282952368871">Dromerig</translation>
+<translation id="4839087176073128681">Betaal volgende keer vinniger en beskerm jou kaart met Google se voorste bedryfsekuriteit.</translation>
<translation id="4840250757394056958">Bekyk jou Chrome-geskiedenis</translation>
<translation id="484462545196658690">Outo</translation>
<translation id="484671803914931257">Kry afslag op <ph name="MERCHANT_NAME" /> en meer</translation>
@@ -1477,6 +1487,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="5011561501798487822">Bespeurde taal</translation>
<translation id="5015510746216210676">Masjiennaam:</translation>
<translation id="5017554619425969104">Teks wat jy gekopieer het</translation>
+<translation id="5017828934289857214">Herinner my later</translation>
<translation id="5018422839182700155">Kan nie hierdie bladsy oopmaak nie</translation>
<translation id="5019198164206649151">Agtergrondstoor in swak toestand</translation>
<translation id="5020776957610079374">Wêreldmusiek</translation>
@@ -1496,6 +1507,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="5051305769747448211">Regstreekse komedie</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Maak spasie (<ph name="DISK_SPACE_SIZE" />) op jou toestel beskikbaar as jy hierdie lêer met Nabydeling wil stuur}other{Maak spasie (<ph name="DISK_SPACE_SIZE" />) op jou toestel beskikbaar as jy hierdie lêers met Nabydeling wil stuur}}</translation>
<translation id="5056549851600133418">Artikels vir jou</translation>
+<translation id="5060483733937416656">Jy het gekies om met Windows Hello te verifieer op webwerwe wat <ph name="PROVIDER_ORIGIN" /> gebruik. Hierdie verskaffer het dalk inligting oor jou betaalmetode geberg, wat jy kan <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Het jy bedoel <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Drukgeskiedenis</translation>
<translation id="5068234115460527047">Skansfondse</translation>
@@ -1509,10 +1521,8 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="5087286274860437796">Bediener se sertifikaat is nie op die oomblik geldig nie.</translation>
<translation id="5087580092889165836">Voeg kaart by</translation>
<translation id="5088142053160410913">Boodskap aan operateur</translation>
-<translation id="5089810972385038852">Deelstaat</translation>
<translation id="5093232627742069661">Z-vou</translation>
<translation id="5094747076828555589">Hierdie bediener kon nie bewys dat dit <ph name="DOMAIN" /> is nie; sy sekuriteitsertifikaat word nie deur Chromium vertrou nie. Dit kan veroorsaak word deur 'n wanopstelling of 'n aanvaller wat jou verbinding onderskep.</translation>
-<translation id="5095208057601539847">Provinsie</translation>
<translation id="5097099694988056070">Toestelstatistieke, soos CPU- of RAM-gebruik</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Werf is nie veilig nie</translation>
@@ -1550,6 +1560,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="5171045022955879922">Soek of tik URL in</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Masjien</translation>
+<translation id="5177076414499237632">Kom meer te wete oor hierdie bladsy se bron en onderwerp</translation>
<translation id="5179510805599951267">Nie in <ph name="ORIGINAL_LANGUAGE" /> nie? Meld hierdie fout aan</translation>
<translation id="518639307526414276">Troeteldierkos en troeteldiersorgbenodigdhede</translation>
<translation id="5190835502935405962">Boekmerkebalk</translation>
@@ -1710,6 +1721,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="5624120631404540903">Bestuur wagwoorde</translation>
<translation id="5629630648637658800">Kon nie beleidinstellings laai nie</translation>
<translation id="5631439013527180824">Ongeldige toestelbestuurtoken</translation>
+<translation id="5632485077360054581">Wys my hoe</translation>
<translation id="5633066919399395251">Aanvallers wat tans op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> is, kan dalk probeer om gevaarlike programme op jou rekenaar te installeer wat jou inligting (byvoorbeeld: foto's, wagwoorde, boodskappe en kredietkaartinligting) steel of uitvee. <ph name="BEGIN_LEARN_MORE_LINK" />Kom meer te wete<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Misleidende inhoud is geblokkeer.</translation>
<translation id="5633259641094592098">Kultus- en indieflieks</translation>
@@ -1827,6 +1839,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="5989320800837274978">Nie vaste instaanbedieners of 'n .pac-skrip-URL is gespesifiseer nie.</translation>
<translation id="5992691462791905444">Ingenieur-Z-vou</translation>
<translation id="5995727681868049093">Bestuur jou inligting, privaatheid en sekuriteit in jou Google-rekening</translation>
+<translation id="5997247540087773573">Die wagwoord wat jy gebruik het, is in 'n dataskending gekry. Google Wagwoordbestuurder beveel aan dat jy dit nou verander en jou gestoorde wagwoorde nagaan om jou rekeninge te beveilig.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultate vir "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Klassiek</translation>
<translation id="6008122969617370890">Volgorde: N-tot-1</translation>
@@ -1922,7 +1935,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="627746635834430766">Stoor jou kaart en faktureringadres in jou Google-rekening om volgende keer vinniger te betaal.</translation>
<translation id="6279183038361895380">Druk |<ph name="ACCELERATOR" />| om jou merker te wys</translation>
<translation id="6280223929691119688">Kan nie by hierdie adres aflewer nie. Kies 'n ander adres.</translation>
-<translation id="6282194474023008486">Poskode</translation>
<translation id="6285507000506177184">Knoppie om aflaaie in Chrome te bestuur; druk Enter om lêers wat jy in Chrome afgelaai het, te bestuur</translation>
<translation id="6289939620939689042">Bladsykleur</translation>
<translation id="6290238015253830360">Jou voorgestelde artikels verskyn hier</translation>
@@ -1944,6 +1956,7 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="6337133576188860026">Maak minder as <ph name="SIZE" /> beskikbaar. Sommige werwe sal dalk op jou volgende besoek stadiger laai.</translation>
<translation id="6337534724793800597">Filtreer beleide volgens naam</translation>
<translation id="6340739886198108203">Administrateursbeleid beveel nie aan dat skermskote geneem of opnames gemaak word wanneer vertroulike inhoud sigbaar is nie:</translation>
+<translation id="6348220984832452017">Aktiewe variasies</translation>
<translation id="6349101878882523185">Installeer <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Geen}=1{1 wagwoord (vir <ph name="DOMAIN_LIST" />, gesinkroniseer)}=2{2 wagwoorde (vir <ph name="DOMAIN_LIST" />, gesinkroniseer)}other{# wagwoorde (vir <ph name="DOMAIN_LIST" /> gesinkroniseer)}}</translation>
<translation id="6355392890578844978">Hierdie blaaier word nie deur 'n maatskappy of ander organisasie bestuur nie. Aktiwiteit op hierdie toestel kan buite Chromium bestuur word. <ph name="BEGIN_LINK" />Kom meer te wete<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="643051589346665201">Verander Google-wagwoord</translation>
<translation id="6433490469411711332">Wysig kontakinligting</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> het geweier om te koppel.</translation>
-<translation id="6438025220577812695">Verander dit self</translation>
<translation id="6440503408713884761">Geïgnoreer</translation>
<translation id="6443406338865242315">Watter uitbreidings en inproppe jy geïnstalleer het</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2105,9 +2117,9 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Vertaal</translation>
<translation id="6833752742582340615">Stoor jou kaart- en faktureringinligting in jou Google-rekening om volgende keer veilig en vinniger te betaal.</translation>
-<translation id="6839929833149231406">Area</translation>
<translation id="6846340164947227603">Gebruik 'n virtuele kaartnommer …</translation>
<translation id="6852204201400771460">Herlaai program?</translation>
+<translation id="6857776781123259569">Bestuur wagwoorde …</translation>
<translation id="686485648936420384">Vebruikerhulpbronne</translation>
<translation id="6865412394715372076">Hierdie kaart kan nie op die oomblik geverifieer word nie</translation>
<translation id="6869334554832814367">Persoonlike lenings</translation>
@@ -2156,7 +2168,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="6965978654500191972">Toestel</translation>
<translation id="696703987787944103">Perseptueel</translation>
<translation id="6968269510885595029">Gebruik jou sekuriteitsleutel</translation>
-<translation id="6970216967273061347">Distrik</translation>
<translation id="6971439137020188025">Skep vinnig 'n nuwe Google-aanbieding in Skyfies</translation>
<translation id="6972629891077993081">HID-toestelle</translation>
<translation id="6973656660372572881">Sowel vaste instaanbedieners as 'n .pac-skrip is gespesifiseer.</translation>
@@ -2195,7 +2206,6 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<translation id="7081308185095828845">Hierdie kenmerk is nie op jou toestel beskikbaar nie</translation>
<translation id="7083258188081898530">Laai 9</translation>
<translation id="7086090958708083563">Oplaai deur gebruiker versoek</translation>
-<translation id="7087282848513945231">Land</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, druk Tab en dan Enter om toestemmings en data wat op werwe geberg is in Chrome-instellings te bestuur</translation>
<translation id="7096937462164235847">Die identiteit van hierdie webwerf is nie geverifieer nie.</translation>
<translation id="7101893872976785596">Rillerflieks</translation>
@@ -2214,10 +2224,10 @@ Dit sal andersins deur jou privaatheidinstellings geblokkeer word. Dit sal die i
<ph name="LIST_ITEM" />Inligting wat op vorms ingevoer is<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Kan nie na hierdie adres versend nie. Kies 'n ander adres.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Jou admin het verbied dat hierdie data gekopieer word.</translation>
<translation id="7135130955892390533">Wys status</translation>
<translation id="7138472120740807366">Afleweringmetode</translation>
-<translation id="7139724024395191329">Emiraat</translation>
<translation id="7139892792842608322">Primêre laai</translation>
<translation id="714064300541049402">Skuif prent langs X-as op kant 2</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2434,6 +2444,7 @@ om uit te vind hoe om die sagteware permanent van jou rekenaar te verwyder&lt;/o
<translation id="7669271284792375604">Aanvallers op hierdie werf kan probeer om jou te mislei om programme te installeer wat jou blaai-ervaring sal skaad (byvoorbeeld deur jou tuisblad te verander of ekstra advertensies te wys op werwe wat jy besoek).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Handelinge uitgevoer met data wat as vertroulik gevlag is (1 handeling sedert aanmelding). <ph name="BEGIN_LINK" />Kom meer te wete<ph name="END_LINK" />}other{Handelinge uitgevoer met data wat as vertroulik gevlag is (# handelinge sedert aanmelding). <ph name="BEGIN_LINK" />Kom meer te wete<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Posbus 6</translation>
+<translation id="7675325315208090829">Bestuur betaalmetodes …</translation>
<translation id="7676643023259824263">Soek vir knipbordteks, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Bekyk en bestuur jou blaaigeskiedenis in Chrome-instellings</translation>
<translation id="7679947978757153706">Bofbal</translation>
@@ -2476,7 +2487,6 @@ om uit te vind hoe om die sagteware permanent van jou rekenaar te verwyder&lt;/o
<translation id="7766518757692125295">Buitelyn</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Dieselfde volgorde, voorkant na bo</translation>
-<translation id="777702478322588152">Prefektuur</translation>
<translation id="7791011319128895129">Onuitgereik</translation>
<translation id="7791196057686275387">Baal</translation>
<translation id="7791543448312431591">Voeg by</translation>
@@ -2567,12 +2577,12 @@ om uit te vind hoe om die sagteware permanent van jou rekenaar te verwyder&lt;/o
<translation id="8055534648776115597">Beroeps- en voortgesette opleiding</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" is nie reg opgestel nie."<ph name="SOFTWARE_NAME" />" stel gewoonlik die probleem reg. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Voedselproduksie</translation>
-<translation id="8066955247577885446">Jammer, iets is fout.</translation>
<translation id="8067872629359326442">Jy het sopas jou wagwoord op 'n misleidende werf ingevoer. Chromium kan help. Klik Beskerm Rekening om jou wagwoord te verander en stel Google in kennis dat jou rekening dalk in gevaar is.</translation>
<translation id="8070439594494267500">Programikoon</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Skep vinnig 'n nuwe Google Blad</translation>
<translation id="8075898834294118863">Bestuur werfinstellings</translation>
+<translation id="8076492880354921740">Oortjies</translation>
<translation id="8078141288243656252">Kan nie aantekeninge maak wanneer gedraai nie</translation>
<translation id="8079031581361219619">Herlaai werf?</translation>
<translation id="8081087320434522107">Sedans</translation>
@@ -2695,6 +2705,7 @@ Bykomende besonderhede:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Instellings</translation>
+<translation id="8428634594422941299">Het dit</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, druk Tab en dan Enter om jou webkoekievoorkeuure in Chrome-instellings te bestuur</translation>
<translation id="8433057134996913067">Dit sal jou by die meeste webwerwe afmeld.</translation>
<translation id="8434840396568290395">Troeteldiere</translation>
@@ -2792,6 +2803,7 @@ Bykomende besonderhede:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> is jou kode vir <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Boekmerk hierdie oortjie</translation>
<translation id="8751426954251315517">Probeer volgende keer weer</translation>
+<translation id="8757526089434340176">Google Pay-aanbieding beskikbaar</translation>
<translation id="8758885506338294482">Mededingende videospeletjies</translation>
<translation id="8759274551635299824">Hierdie kaart het verval</translation>
<translation id="87601671197631245">Hierdie werf gebruik 'n verouderde sekuriteitopstelling, wat jou inligting (byvoorbeeld, wagwoorde, boodskappe of kredietkaarte) kan blootstel wanneer dit na hierdie werf toe gestuur word.</translation>
@@ -2799,6 +2811,7 @@ Bykomende besonderhede:
<translation id="8763927697961133303">USB-toestel</translation>
<translation id="8763986294015493060">Maak alle Incognito-vensters wat tans oop is, toe</translation>
<translation id="8766943070169463815">Veilige betaling se eiebewysstawingblad is oop</translation>
+<translation id="8767765348545497220">Maak hulpborrel toe</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorfietse</translation>
<translation id="8790007591277257123">Herdoen uitvee</translation>
@@ -2811,6 +2824,7 @@ Bykomende besonderhede:
<translation id="8806285662264631610">Bad- en lyfprodukte</translation>
<translation id="8807160976559152894">Knip ná elke bladsy</translation>
<translation id="8808828119384186784">Chrome-instellings</translation>
+<translation id="8813277370772331957">Herinner my later</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, druk Tab en dan Enter om Chrome vanuit jou Chrome-instellings op te dateer</translation>
<translation id="8820817407110198400">Boekmerke</translation>
<translation id="882338992931677877">Handgleuf</translation>
@@ -2990,6 +3004,7 @@ Bykomende besonderhede:
<translation id="988159990683914416">Ontwikkelaarbou</translation>
<translation id="989988560359834682">Wysig adres</translation>
<translation id="991413375315957741">beweging- of ligsensors</translation>
+<translation id="992110854164447044">’n Virtuele kaart verberg jou werklike kaart om te help om jou teen potensiële bedrog te beskerm. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Pienk</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" is nie behoorlik op jou rekenaar of netwerk geïnstalleer nie:
diff --git a/chromium/components/strings/components_strings_am.xtb b/chromium/components/strings/components_strings_am.xtb
index 2a5f7b18e75..1b02ee0baee 100644
--- a/chromium/components/strings/components_strings_am.xtb
+++ b/chromium/components/strings/components_strings_am.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ድጎማዎችᣠስኮላርሺá–ች እና የገንዘብ ድጋá</translation>
<translation id="1048785276086539861">ማብራሪያዎችን ሲያርትዑ ይህ ሰáŠá‹µ ወደ የáŠáŒ áˆ‹ ገጽ እይታ ይመለሳáˆ</translation>
<translation id="1050038467049342496">ሌሎች መተáŒá‰ áˆªá‹«á‹Žá‰½áŠ• á‹­á‹áŒ‰</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" />ን በሚጠቀሙ ድር ጣቢያዎች ላይ በአረጋጋጭ መሣሪያ ማረጋገጥን መርጠዋáˆá¢ ይህ አቅራቢ ስለእርስዎ የመክáˆá‹« ዘዴ መረጃ አከማችቶ ሊሆን ይችላáˆá¢ እርስዎ ይህንን <ph name="LINK_TEXT" /> ይችላሉá¢</translation>
<translation id="1055184225775184556">&amp;አክáˆáŠ• ቀáˆá‰¥áˆµ</translation>
<translation id="1056663316309890343">የáŽá‰¶ ሶáትዌር</translation>
<translation id="1056898198331236512">ማስጠንቀቂያ</translation>
@@ -53,7 +54,7 @@
<translation id="1110994991967754504">ለ<ph name="PERMISSION_NAME" /> áˆá‰ƒá‹µ አቀናብር</translation>
<translation id="1112828774174131240">የቆዩ እና የሰብሳቢ ዕቃዎች</translation>
<translation id="1113869188872983271">&amp;እንደገና ደርድርን ቀáˆá‰¥áˆµ</translation>
-<translation id="1123753900084781868">የቀጥታ መáŒáˆˆáŒ« ጽሑá አáˆáŠ• ላይ አይገáŠáˆ</translation>
+<translation id="1123753900084781868">የቀጥታ መáŒáˆˆáŒ« ጽáˆá አáˆáŠ• ላይ አይገáŠáˆ</translation>
<translation id="1125573121925420732">ማስጠንቀቂያዎች ድርጣቢያዎች የእáŠáˆ­áˆ±áŠ• ደህንáŠá‰µ በሚያዘáˆáŠ‘በት ጊዜ የተለመዱ ሊሆኑ ይችላሉᢠይህ በቅርቡ መሻሻሠአለበትá¢</translation>
<translation id="112840717907525620">የመáˆáˆªá‹« መሸጎጫ እሺ</translation>
<translation id="1130564665089811311">የገጽ ተርጉሠአá‹áˆ«áˆ­á£ ይህን ገጽ በGoogle ትርጉሠለመተርጎሠአስገባን ይጫኑá¢</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">የመá‹áˆ°áŒƒ ስáˆá‰µ</translation>
<translation id="1281476433249504884">á‰áˆáˆ 1</translation>
<translation id="1285320974508926690">ይህን ጣቢያ በጭራሽ አትተርጉáˆ</translation>
+<translation id="1288548991597756084">ካርድ ደህንáŠá‰± በተጠበቀ áˆáŠ”ታ ያስቀáˆáŒ¡</translation>
<translation id="1292571435393770077">መሳቢያ 16</translation>
<translation id="1292701964462482250">«በእርስዎ ኮáˆá’á‹á‰°áˆ­ ላይ ያለ ሶáትዌር Chrome ደህንáŠá‰± በተጠበቀ áˆáŠ”ታ ከድር ጋር እንዳይገናአእያስቆመዠáŠá‹Â» (የWindows ኮáˆá’á‹á‰°áˆ®á‰½ ብቻ)</translation>
<translation id="1294154142200295408">የትእዛዠመስመር áˆá‹©áŠá‰¶á‰½</translation>
@@ -192,7 +194,7 @@
<translation id="1442987760062738829">ብሳ</translation>
<translation id="1446396933673057385">የትክክለáŠáŠá‰µ áተሻ</translation>
<translation id="1447067628680007684">(x86_64)</translation>
-<translation id="1453974140256777690">እርስዎ የሚለጥá‰á‰µ ወይሠዓባሪ የሚያያይዙት ጽሑá ወደ Google ደመና ወይሠሦስተኛ ወገኖች ለትንታኔ ይላካáˆá¢ ለáˆáˆ³áˆŒá£ አደጋን ሊያስከትሠለሚችሠá‹áˆ‚ብ ሊቃኙ ይችላሉá¢</translation>
+<translation id="1453974140256777690">እርስዎ የሚለጥá‰á‰µ ወይሠዓባሪ የሚያያይዙት ጽáˆá ወደ Google ደመና ወይሠሦስተኛ ወገኖች ለትንታኔ ይላካáˆá¢ ለáˆáˆ³áˆŒá£ አደጋን ሊያስከትሠለሚችሠá‹áˆ‚ብ ሊቃኙ ይችላሉá¢</translation>
<translation id="1455413310270022028">ማጥáŠá‹«</translation>
<translation id="1459693405370120464">የአየር áˆáŠ”ታ</translation>
<translation id="1462245070427461050">JIS B9</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;ይህን ስህተት ለመáታት ለመክáˆá‰µ እየሞከሩ ባለዠገጽ ላይ &lt;strong&gt;ተገናáŠ&lt;/strong&gt;ን ጠቅ ያድርጉá¢&lt;/p&gt;</translation>
<translation id="1507780850870535225">የመሬት ገጽታ ንድá</translation>
<translation id="1513706915089223971">የታሪክ áŒá‰¤á‰¶á‰½ á‹áˆ­á‹áˆ­</translation>
+<translation id="1516097932025103760">የተመሰጠረ ይሆናáˆá£ ደህንáŠá‰± በተጠበቀ áˆáŠ”ታ ይቀመጣሠእና የካርድ ማረጋገጫ ኮዱ በáጹሠአይከማችáˆá¢</translation>
<translation id="1517433312004943670">ስáˆáŠ­ á‰áŒ¥áˆ­ ያስáˆáˆáŒ‹áˆ</translation>
<translation id="1519264250979466059">የáŒáŠ•á‰¥ ቀን</translation>
<translation id="1521159554480556801">የá‹á‹­á‰ áˆ­ እና ጨርቃጨርቅ ጥበባት</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">የመክáˆá‹« ዘዴዎችን አስቀáˆáŒ¥ እና ሙላ</translation>
<translation id="1663943134801823270">ካርዶች እና አድራሻዎች ከChrome የመጡ ናቸá‹á¢ በ<ph name="BEGIN_LINK" />ቅንብሮች<ph name="END_LINK" /> á‹áˆµáŒ¥ ሊያስተዳድሯቸዠይችላሉá¢</translation>
<translation id="1671391448414634642">ከአáˆáŠ• በኋላ በ<ph name="SOURCE_LANGUAGE" /> ያሉ ገጾች ወደ <ph name="TARGET_LANGUAGE" /> ይተረጎማሉá¢</translation>
+<translation id="1673886523110456987">ቅናሽን ለመጠቀሠበ<ph name="CARD_DETAIL" /> áŒá‹¢ ያጠናቅá‰</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> ወደ <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">አጭር ጠርዠመጀመሪያ</translation>
<translation id="168693727862418163">ይህ የመመሪያ እሴት ከዕቅዱ ጋር ሲተያይ ማረጋገጥ አáˆá‰»áˆˆáˆá£ እና ችላ ይባላáˆá¢</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">የእንቅስቃሴ ዳሳሾች</translation>
<translation id="1717494416764505390">የመáˆá‹•áŠ­á‰µ ሳጥን 3</translation>
<translation id="1718029547804390981">ሰáŠá‹µ ለመብራራት ከáˆáŠ­ በላይ ትáˆá‰… áŠá‹</translation>
+<translation id="1720941539803966190">አጋዥ ስáˆáŒ áŠ“á‹áŠ• á‹áŒ‹</translation>
<translation id="1721424275792716183">* መስክ ያስáˆáˆáŒ‹áˆ</translation>
<translation id="1727613060316725209">የዕá‹á‰…ና ማረጋገጫ áˆáŠ­ የሆአáŠá‹</translation>
<translation id="1727741090716970331">የሚሰራ የካርድ á‰áŒ¥áˆ­ ያክሉ</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">ባርበኩ እና ጥብስ</translation>
<translation id="2053111141626950936">በ<ph name="LANGUAGE" /> ያሉ ገጾች አይተረጎሙáˆá¢</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ይህ መቆጣጠሪያ ሲበራ እና áˆáŠ”ታዠንበሲሆን Chrome የቅርብ ጊዜ የአሰሳ እንቅስቃሴዎ ከየትኛዠየብዙ ሰዎች ስብስብ ወይሠ«የተመሳሳይ ሰዎች ስብስቦች» ጋር በጣሠእንደሚቀራረብ ይወስናáˆá¢ ማስታወቂያ ሰሪዎች ለቡድኑ ማስታወቂያዎችን መáˆáˆ¨áŒ¥ ይችላሉᣠእንዲáˆáˆ የአሰሳ እንቅስቃሴዎ በመሣሪያዎ ላይ በáŒáˆ ሆኖ ይቀመጣáˆá¢ የእርስዎ ቡድን በየቀኑ ይዘáˆáŠ“áˆá¢}=1{ይህ መቆጣጠሪያ ሲበራ እና áˆáŠ”ታዠንበሲሆን Chrome የቅርብ ጊዜ የአሰሳ እንቅስቃሴዎ ከየትኛዠየብዙ ሰዎች ስብስብ ወይሠ«የተመሳሳይ ሰዎች ስብስቦች» ጋር በጣሠእንደሚቀራረብ ይወስናáˆá¢ ማስታወቂያ ሰሪዎች ለቡድኑ ማስታወቂያዎችን መáˆáˆ¨áŒ¥ ይችላሉᣠእንዲáˆáˆ የአሰሳ እንቅስቃሴዎ በመሣሪያዎ ላይ በáŒáˆ ሆኖ ይቀመጣáˆá¢ የእርስዎ ቡድን በየቀኑ ይዘáˆáŠ“áˆá¢}one{ይህ መቆጣጠሪያ ሲበራ እና áˆáŠ”ታዠንበሲሆን Chrome የቅርብ ጊዜ የአሰሳ እንቅስቃሴዎ ከየትኛዠየብዙ ሰዎች ስብስብ ወይሠ«የተመሳሳይ ሰዎች ስብስቦች» ጋር በጣሠእንደሚቀራረብ ይወስናáˆá¢ ማስታወቂያ ሰሪዎች ለቡድኑ ማስታወቂያዎችን መáˆáˆ¨áŒ¥ ይችላሉᣠእንዲáˆáˆ የአሰሳ እንቅስቃሴዎ በመሣሪያዎ ላይ በáŒáˆ ሆኖ ይቀመጣáˆá¢ የእርስዎ ቡድን በየ{NUM_DAYS} ቀኖች ይዘመናáˆá¢}other{ይህ መቆጣጠሪያ ሲበራ እና áˆáŠ”ታዠንበሲሆን Chrome የቅርብ ጊዜ የአሰሳ እንቅስቃሴዎ ከየትኛዠየብዙ ሰዎች ስብስብ ወይሠ«የተመሳሳይ ሰዎች ስብስቦች» ጋር በጣሠእንደሚቀራረብ ይወስናáˆá¢ ማስታወቂያ ሰሪዎች ለቡድኑ ማስታወቂያዎችን መáˆáˆ¨áŒ¥ ይችላሉᣠእንዲáˆáˆ የአሰሳ እንቅስቃሴዎ በመሣሪያዎ ላይ በáŒáˆ ሆኖ ይቀመጣáˆá¢ የእርስዎ ቡድን በየ{NUM_DAYS} ቀኖች ይዘመናáˆá¢}}</translation>
-<translation id="2053553514270667976">ዚᕠኮድ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 የአስተያየት ጥቆማ}one{# የአስተያየት ጥቆማዎች}other{# የአስተያየት ጥቆማዎች}}</translation>
+<translation id="2066915425250589881">ለመሰረዠመጠየቅ</translation>
<translation id="2068528718802935086">ጨቅላዎች እና ህጻናት</translation>
<translation id="2071156619270205202">ይህ ካርድ ለáˆáŠ“ባዊ ካርድ á‰áŒ¥áˆ­ ብበአይደለáˆá¢</translation>
<translation id="2071692954027939183">ማሳወቂያዎች ብዙá‹áŠ• ጊዜ ስለማይáˆá‰…ዱáˆá‰¸á‹ በራስ-ሰር ታáŒá‹°á‹‹áˆ</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">የአስáˆáˆ­ አá‹áˆ«áˆ­áŠ• ያቀናብሩᣠበChrome ቅንብሮች á‹áˆµáŒ¥ áˆáŠ• መረጃ እንደሚያሰáˆáˆ© ለማቀናበር አስገባን ይጫኑ</translation>
<translation id="2091887806945687916">ድáˆá…</translation>
<translation id="2094505752054353250">የጎራ አለመዛመድ</translation>
-<translation id="2096368010154057602">መመሪያ</translation>
<translation id="2099652385553570808">ሦስቴ ስቴá•áˆˆáˆ­ በáŒáˆ« በኩሠáˆá‰³</translation>
<translation id="2101225219012730419">ስሪትá¦</translation>
<translation id="2102134110707549001">ጠንካራ የይለá ቃሠጠá‰áˆ...</translation>
@@ -455,7 +459,7 @@
<translation id="2149968176347646218">áŒáŠ•áŠ™áŠá‰µá‹Ž ደህንáŠá‰± የተጠበቀ አይደለáˆ</translation>
<translation id="2154054054215849342">ስáˆáˆ¨á‰µ ለእርስዎ ጎራ አይገáŠáˆ</translation>
<translation id="2154484045852737596">ካርትን ያርትዑ</translation>
-<translation id="2161656808144014275">ጽሑá</translation>
+<translation id="2161656808144014275">ጽáˆá</translation>
<translation id="2162510787844374618">ባንክዎን በማáŒáŠ˜á‰µ ላይ…</translation>
<translation id="2164510882479075877">በ<ph name="HOST_NAME" /> á‹áˆµáŒ¥ የáŠá‹°áˆ áŒá‹µáˆá‰µ ካለ á‹­áˆá‰µáˆ¹á¢</translation>
<translation id="2166049586286450108">ሙሉ የአስተዳደር መድረሻ</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">የአሜሪካ እáŒáˆ­ ኳስ</translation>
<translation id="2187317261103489799">አáŒáŠ (áŠá‰£áˆª)</translation>
<translation id="2188375229972301266">በርካታ ብስ áŒáˆ­áŒŒ</translation>
-<translation id="2188852899391513400">አáˆáŠ• የተጠቀሙበት የይለá ቃሠበá‹áˆ‚ብ ጥሰት á‹áˆµáŒ¥ ተገáŠá‰·áˆá¢ የእርስዎን መለያዎች ደህንáŠá‰µ ለመጠበቅᣠGoogle የይለá ቃሠአስተዳዳሪ አáˆáŠ‘ኑ እንዲቀይሩት እና ከዚያ የተቀመጡ የይለá ቃላትዎን እንዲáˆá‰µáˆ¹ ይመክራáˆá¢</translation>
<translation id="219906046732893612">የቤት ማሻሻáˆ</translation>
<translation id="2202020181578195191">ትክክለኛ የአገáˆáŒáˆŽá‰µ ማብቂያ ዓመት ያስገቡ</translation>
<translation id="22081806969704220">መሳቢያ 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">የá‹á‹­áˆ አርትዖት አደራረáŒ</translation>
<translation id="2215963164070968490">á‹áˆ»á‹Žá‰½</translation>
<translation id="2218879909401188352">በ<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ላይ አáˆáŠ• ያሉ አጥቂዎች የእርስዎን መሣሪያ የሚያበላሹ አደገኛ መተáŒá‰ áˆªá‹«á‹Žá‰½áŠ• ሊጭኑᣠበእርስዎ ሞባይሠክáá‹« መጠየቂያ ላይ የተደበበክáá‹« መጠየቂያዎችን ሊያክሉ ወይሠየእርስዎን የáŒáˆ መረጃ ሊሰርበይችላሉᢠ<ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">አጋዥ ሥáˆáŒ áŠ“ን እንደገና ጀáˆáˆ­</translation>
+<translation id="2219735899272417925">የመሣሪያ ዳáŒáˆ ማስጀመር ያስáˆáˆáŒ‹áˆ</translation>
<translation id="2224337661447660594">áˆáŠ•áˆ በይáŠáˆ˜áˆ¨á‰¥ የለáˆ</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />የመመርመሪያ መተáŒá‰ áˆªá‹«á‹áŠ•<ph name="END_LINK" /> በመጠቀሠáŒáŠ•áŠ™áŠá‰µá‹ŽáŠ• ያስተካክሉት</translation>
<translation id="2239100178324503013">አáˆáŠ• ላክ</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">አáˆá‰°áˆá‰€á‹°áˆ (áŠá‰£áˆª)</translation>
<translation id="2512413427717747692">Chromeን እንደ áŠá‰£áˆª አሳሽ አá‹áˆ«áˆ­ ያዋቅሩᣠChromeን በiOS ቅንብሮች á‹áˆµáŒ¥ የስርዓቱ áŠá‰£áˆª አሳሽ ለማዋቀር አስገባን ይጫኑ</translation>
<translation id="2515629240566999685">በእርስዎ አካባቢ ያለá‹áŠ• ሲáŒáŠ“ሠመáˆá‰°áˆ½</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" />ን በሚጠቀሙ ድር ጣቢያዎች ላይ በTouch ID ማረጋገጥን መርጠዋáˆá¢ ይህ አቅራቢ ስለእርስዎ የመክáˆá‹« ዘዴ መረጃ አከማችቶ ሊሆን ይችላáˆá¢ እርስዎ ይህንን <ph name="LINK_TEXT" /> ይችላሉá¢</translation>
<translation id="2521385132275182522">ከáŒáˆ«áŒŒ በቀአበኩሠስቴá•áˆˆáˆ­ áˆá‰³</translation>
<translation id="2521736961081452453">ቅጽ áጠር</translation>
<translation id="2523886232349826891">በዚህ መሣሪያ ላይ ብቻ ይቀመጣáˆ</translation>
<translation id="2524461107774643265">ተጨማሪ መረጃ ያክሉ</translation>
<translation id="2529899080962247600">ይህ መስክ ከ<ph name="MAX_ITEMS_LIMIT" /> áŒá‰¤á‰¶á‰½ በላይ ሊኖሩት አይገባáˆá¢ áˆáˆ‰áˆ ተጨማሪ áŒá‰¤á‰¶á‰½ ችላ ይባላሉá¢</translation>
+<translation id="253493526287553278">የማስተዋወቂያ ኮድ á‹áˆ­á‹áˆ®á‰½áŠ• ይመáˆáŠ¨á‰±</translation>
<translation id="2535585790302968248">በáŒáˆ ለማሰስ አዲስ ማንáŠá‰µáŠ• የማያሳá‹á‰… ትርን ክáˆá‰µ</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{እና 1 ተጨማሪ}one{እና # ተጨማሪ}other{እና # ተጨማሪ}}</translation>
<translation id="2536110899380797252">አድራሻ ያክሉ</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">የáŽá‰¶áŒáˆ«á እና ዲጂታሠስአጥበባት</translation>
<translation id="2601150049980261779">የáቅር áŠáˆáˆžá‰½</translation>
<translation id="2604589665489080024">á–ᕠሙዚቃ</translation>
-<translation id="2609632851001447353">áˆá‹©áŠá‰¶á‰½</translation>
<translation id="2610561535971892504">ለመቅዳት ጠቅ ያድርጉ</translation>
<translation id="2617988307566202237">Chrome የሚከተለá‹áŠ• መረጃ <ph name="BEGIN_EMPHASIS" />አያስቀáˆáŒ¥áˆ<ph name="END_EMPHASIS" />á¦
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">ROC 8K</translation>
<translation id="2677696497921480781">የáˆá‹°á‰µ ቀናት እና የስሠቀናት</translation>
<translation id="2677748264148917807">ለቅቀህ á‹áŒ£</translation>
+<translation id="2679714844901977852">ለደህንáŠá‰³á‰¸á‹ የተጠበቀ እና áˆáŒ£áŠ• ክáá‹« ማጠናቀቆች የካርድዎን እና የሂሳብ አከá‹áˆáˆá‹ŽáŠ• መረጃ በእርስዎ Google መለያ <ph name="USER_EMAIL" /> ላይ ያስቀáˆáŒ¡</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">áˆáˆ­áŒ¥ መገጣጠáˆ</translation>
<translation id="2688969097326701645">አዎᣠቀጥáˆ</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">የበይáŠáˆ˜áˆ¨á‰¥ አገáˆáŒáˆŽá‰µ አቅራቢዎች (አይኤስá’ዎች)</translation>
<translation id="2856444702002559011">አጥቂዎች የእርስዎን መረጃ (ለáˆáˆ³áˆŒá¦ የይለá ቃላትንᣠመáˆá‹•áŠ­á‰¶á‰½áŠ•á£ ወይሠየክሬዲት ካርዶችን የመሳሰሉ) ከ<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ለመስረቅ እየሞከሩ ሊሆኑ ይችላሉᢠ<ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ይህ ጣቢያ ረባሽ ወይሠአሳሳች ማስታወቂያዎችን ያሳያáˆá¢</translation>
-<translation id="286512204874376891">áˆáŠ“ባዊ ካርድ እርስዎን ሊሆን ከሚችሠማጭበርበር ለመጠበቅ እንዲረዳዎት ትክክለኛá‹áŠ• ካርድዎን አስመስሎ ይደብቀዋáˆá¢ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">የወዳጅáŠá‰µ</translation>
<translation id="28761159517501904">áŠáˆáˆžá‰½</translation>
<translation id="2876489322757410363">በá‹áŒ«á‹Š ማከማቻ በኩሠለማጫወት ማንáŠá‰µ ከማያሳá‹á‰… áˆáŠá‰³ በመá‹áŒ£á‰µ ላይᢠይቀጥáˆ?</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">ዲስክ</translation>
<translation id="3162559335345991374">እየተጠቀሙ ያሉት Wi-Fi በመለያ መáŒá‰¢á‹« ገጹን እንዲጎበኙ ሊጠይቅዎት ይችላáˆá¢</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">ደሴት</translation>
<translation id="3176929007561373547">ተኪ አገáˆáŒ‹á‹© በአáŒá‰£á‰¡ እየሰራ መሆኑን ለማረጋገጥ የተኪ ቅንብሮችዎን á‹­áˆá‰µáˆ¹ ወይሠየአá‹á‰³áˆ¨
መረብዎ አስተዳዳሪን á‹«áŒáŠ™á¢ ተኪ አገáˆáŒ‹á‹­ መጠቀሠእንደሌለብዎት የሚያáˆáŠ‘ ከሆኑá¦
<ph name="PLATFORM_TEXT" /></translation>
@@ -876,9 +881,6 @@
<translation id="3369192424181595722">የሰዓት ስህተት</translation>
<translation id="3369459162151165748">የተሽከርካሪ ክáሎች እና መለዋወጫዎች</translation>
<translation id="3371064404604898522">Chromeን እንደ áŠá‰£áˆª አሳሽ ያስቀáˆáŒ¡á‰µ</translation>
-<translation id="3371076217486966826"><ph name="URL" /> የሚከተሉትን ማድረጠይáˆáˆáŒ‹áˆá¦
- • የዙሪያዎን የ3ሠካርታ መáጠር እና የካሜራ ቦታን መከታተáˆ
- • የእርስዎን ካሜራ መጠቀáˆ</translation>
<translation id="337363190475750230">አቅርቦት ተቋርጧáˆ</translation>
<translation id="3375754925484257129">የChrome ደህንáŠá‰µ áተሻን ያሂዱ</translation>
<translation id="3377144306166885718">አገáˆáŒ‹á‹© ጊዜ ያለáˆá‰ á‰µ የTLS ስሪት ተጠቅመዋáˆá¢</translation>
@@ -895,6 +897,7 @@
<translation id="3399952811970034796">የመላኪያ አድራሻ</translation>
<translation id="3402261774528610252">ይህን ጣቢያ ለመጫን ስራ ላይ የዋለዠáŒáŠ•áŠ™áŠá‰µ TLS 1.0 ወይሠTLS 1.1 áŠá‹ የተጠቀመá‹á£ እáŠá‹šáˆ…ሠየተቋረጡ እና ለወደáŠá‰± የሚሰናከሉ ናቸá‹á¢ አንዴ ከተሰናከለ በኋላ ተጠቃሚዎች ይህን ጣቢያ እንዳይጭኑት ይከለከላሉᢠአገáˆáŒ‹á‹© TLS 1.2 ወይሠከዚያ በኋላ ማንቃት አለበትá¢</translation>
<translation id="3405664148539009465">ቅርጸ-á‰áˆáŠá‹Žá‰½áŠ• አብጅ</translation>
+<translation id="3407789382767355356">የሶስተኛ ወገን መáŒá‰¢á‹«</translation>
<translation id="3409896703495473338">የደህንáŠá‰µ ቅንብሮችን ያቀናብሩ</translation>
<translation id="3414952576877147120">መጠንá¦</translation>
<translation id="3417660076059365994">እርስዎ የሚሰቅáˆá‰¸á‹ ወይሠዓባሪ የሚያይዟቸዠá‹á‹­áˆŽá‰½ ወደ Google ደመና ወይሠሦስተኛ ወገኖች ለትንታኔ ይላካሉᢠለáˆáˆ³áˆŒá£ አደጋን ሊያስከትሠለሚችሠá‹áˆ‚ብ ወይሠተንኮáˆ-አዘሠዌር ሊቃኙ ይችላሉá¢</translation>
@@ -927,6 +930,7 @@
<translation id="3477679029130949506">የáŠáˆáˆ á‹áˆ­á‹áˆ®á‰½ እና የትያትር ማሳያ ሰዓቶች</translation>
<translation id="3479552764303398839">አáˆáŠ• አይደለáˆ</translation>
<translation id="3484560055331845446">የGoogle መለያዎን መዳረሻ ሊያጡ ይችላሉᢠChrome የይለá ቃáˆá‹ŽáŠ• አáˆáŠ‘ኑ እንዲቀይሩት ይመክራáˆá¢ በመለያ እንዲገቡ ይጠየቃሉá¢</translation>
+<translation id="3484861421501147767">ማስታወሻᦠየተቀመጠ የማስተዋወቂያ ኮድ አለ</translation>
<translation id="3487845404393360112">መሳቢያ 4</translation>
<translation id="3495081129428749620">በገጽ á‹áˆµáŒ¥ á‹«áŒáŠ™
<ph name="PAGE_TITLE" /></translation>
@@ -1001,7 +1005,7 @@
<translation id="3704162925118123524">እየተጠቀሙ ያሉት አá‹á‰³áˆ¨ መረብ በመለያ መáŒá‰¢á‹« ገጹን እንዲጎበኙ ሊጠይቅዎት ይችላáˆá¢</translation>
<translation id="3705189812819839667"><ph name="RESULT_OWNER" /> - <ph name="RESULT_PRODUCT_SOURCE" /></translation>
<translation id="370665806235115550">በመጫን ላይ…</translation>
-<translation id="3709599264800900598">እርስዎ የቀዱት ጽሑá</translation>
+<translation id="3709599264800900598">እርስዎ የቀዱት ጽáˆá</translation>
<translation id="370972442370243704">ጉዞዎችን ያብሩ</translation>
<translation id="3709866969787468031">የኦዲዮ እና ሙዚቃ ሶáትዌር</translation>
<translation id="3711895659073496551">አንጠáˆáŒ¥áˆ</translation>
@@ -1051,6 +1055,7 @@
<translation id="3810973564298564668">አደራጅ</translation>
<translation id="3816482573645936981">እሴት (የተተካ)</translation>
<translation id="382518646247711829">ተኪ አገáˆáŒ‹á‹­ የሚጠቀሙ ከሆኑ...</translation>
+<translation id="3826050100957962900">የሶስተኛ ወገን መáŒá‰¢á‹«</translation>
<translation id="3827112369919217609">áጹáˆ</translation>
<translation id="3827666161959873541">የቤተሰብ áŠáˆáˆžá‰½</translation>
<translation id="3828924085048779000">ባዶ የይለá áˆáˆ¨áŒ አይáˆá‰€á‹µáˆá¢</translation>
@@ -1063,9 +1068,9 @@
<translation id="3858027520442213535">ቀን እና ሰዓትን አዘáˆáŠ•</translation>
<translation id="3858860766373142691">ስáˆ</translation>
<translation id="3872834068356954457">ሳይንስ</translation>
+<translation id="3875783148670536197">እንዴት እንደሆአአሳየáŠ</translation>
<translation id="3881478300875776315">á‹«áŠáˆ± መስመሮችን አሳይ</translation>
<translation id="3884278016824448484">የሚጋጭ የመሣሪያ ለዪ</translation>
-<translation id="3885155851504623709">á“ሪሽ</translation>
<translation id="388632593194507180">ክትትሠእንዳለ ተደርሶበታáˆ</translation>
<translation id="3886948180919384617">á‰áˆáˆ 3</translation>
<translation id="3890664840433101773">ኢሜይሠያክሉ</translation>
@@ -1095,9 +1100,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> ታáŒá‹·áˆ</translation>
<translation id="3978338123949022456">የáለጋ áˆáŠá‰³á£ መጠይቅን ይተይቡ እና በ<ph name="KEYWORD_SUFFIX" /> ለመáˆáˆˆáŒ አስገባን ይጫኑ</translation>
<translation id="398470910934384994">አዕዋá</translation>
+<translation id="3985750352229496475">አድራሻዎችን ያቀናብሩ...</translation>
<translation id="3986705137476756801">ለአáˆáŠ• የቀጥታ መáŒáˆˆáŒ« ጽሑáን አጥá‹</translation>
<translation id="3987940399970879459">ከ1 ሜባ á‹«áŠáˆ°</translation>
<translation id="3990250421422698716">ሕትመት አጠናቅ</translation>
+<translation id="3992684624889376114">ስለዚህ ገጽ</translation>
<translation id="3996311196211510766">የ<ph name="ORIGIN" /> ጣቢያ የመጀመሪያ መመሪያ
በáˆáˆ‰áˆ ወደ እሱ በተላኩ ጥያቄዎች ላይ ተáˆáŒ»áˆš እንዲሆን ጠይቋáˆá£ áŠáŒˆáˆ­ áŒáŠ• ይህ መመሪያ በአáˆáŠ‘ ጊዜ ሊተገበር አይችáˆáˆá¢</translation>
<translation id="4006465311664329701">Google Payን የሚጠቀሙ የመክáˆá‹« ዘዴዎችᣠቅናሾች እና አድራሻዎች</translation>
@@ -1126,7 +1133,7 @@
<translation id="4098354747657067197">አሳሳች ጣቢያ ከáŠá‰µ አለ</translation>
<translation id="4099048595830172239">ሚስጥራዊ ይዘት በሚታይበት ጊዜ የአስተዳዳሪ መመሪያ ማያ ገጽዎን ለ<ph name="APPLICATION_TITLE" /> ማጋራትን አይመከርáˆá¦</translation>
<translation id="4099391883283080991"><ph name="CUSTOMIZE_CHROME_FONTS_FOCUSED_FRIENDLY_MATCH_TEXT" />ᣠከዚያ በChrome á‹áˆµáŒ¥ የቅርጸ-á‰áˆáŠ መጠኖችን እና መáˆáŠ¨ á‰áˆáŠá‹Žá‰½áŠ• ለማበጀት ትርን ከዚያ አስገባን ይጫኑ</translation>
-<translation id="4101413244023615925">ጽሑá እና áŒáˆ«áŠáŠ­</translation>
+<translation id="4101413244023615925">ጽáˆá እና áŒáˆ«áŠáŠ­</translation>
<translation id="410148943680000050">የዴስክቶᕠህትመት</translation>
<translation id="4103249731201008433">የመሣሪያ መለያ á‰áŒ¥áˆ­ áˆáŠ­ á‹«áˆáˆ†áŠ áŠá‹</translation>
<translation id="4106133539597032659">የጣቢያ አá‹áˆ«áˆ­áŠ• á‹­áጠሩᣠበGoogle ጣቢያዎች á‹áˆµáŒ¥ አዲስ ጣቢያ በáጥáŠá‰µ ለመáጠር አስገባን ይጫኑ</translation>
@@ -1219,6 +1226,7 @@
<translation id="4305666528087210886">የእርስዎ á‹á‹­áˆ ሊደርስበት አáˆá‰°á‰»áˆˆáˆ</translation>
<translation id="4306529830550717874">አድራሻ ይቀመጥ?</translation>
<translation id="4306812610847412719">ቅንጥብ ሰሌዳ</translation>
+<translation id="4310070645992025887">ጉዞዎችዎን á‹­áˆáˆáŒ‰</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">አáŒá‹µ (áŠá‰£áˆª)</translation>
<translation id="4314815835985389558">ስáˆáˆ¨á‰µáŠ• ያቀናብሩ</translation>
@@ -1252,7 +1260,7 @@
<translation id="4386413576162606861">የሚና መጫወት ጨዋታዎች</translation>
<translation id="4390472908992056574">ከáˆá</translation>
<translation id="4393632246160856858">የጥáር እንክብካቤ áˆáˆ­á‰¶á‰½</translation>
-<translation id="4406883609789734330">የቀጥታ ስርጭት መáŒáˆˆáŒ« ጽሑá</translation>
+<translation id="4406883609789734330">የቀጥታ ስርጭት መáŒáˆˆáŒ« ጽáˆá</translation>
<translation id="4406896451731180161">የáለጋ á‹áŒ¤á‰¶á‰½</translation>
<translation id="4407755609041463909">እሳት</translation>
<translation id="4408413947728134509">ኩኪዎች <ph name="NUM_COOKIES" /></translation>
@@ -1269,6 +1277,7 @@
<translation id="4435702339979719576">የá–ስታ ካርድ)</translation>
<translation id="443673843213245140">የተኪ መጠቀሠተሰናክáˆáˆ áŒáŠ• áŒáˆá… የሆአየተኪ á‹á‰…ር ተገáˆáŒ¿áˆá¢</translation>
<translation id="4441832193888514600">መመሪያዠሊዋቀር የሚችለዠእንደ ደመና ተጠቃሚ መመሪያ ብቻ ስለሆአችላ ተብáˆáˆá¢</translation>
+<translation id="4442470707340296952">የChrome ትሮች</translation>
<translation id="4450893287417543264">ዳáŒáˆ አታሳይ</translation>
<translation id="4451135742916150903">ከHID መሣሪያዎች ጋር ለመገናኘት መጠየቅ ይችላáˆ</translation>
<translation id="4452328064229197696">አáˆáŠ• የተጠቀሙበት የይለá ቃሠበá‹áˆ‚ብ ጥሰት á‹áˆµáŒ¥ ተገáŠá‰·áˆá¢ የእርስዎን መለያዎች ደህንáŠá‰µ ለመጠበቅᣠGoogle የይለá ቃሠአስተዳዳሪ የተቀመጡ የይለá ቃላትዎን እንዲáˆá‰µáˆ¹ ይመክራáˆá¢</translation>
@@ -1352,7 +1361,7 @@
<translation id="4692623383562244444">የáለጋ á•áˆ®áŒáˆ«áˆžá‰½</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4702504834785592287">ጎን</translation>
-<translation id="4702656508969495934">የቀጥታ መáŒáˆˆáŒ« ጽሑá ይታያáˆá£ ለትኩረት መስኮት መቀየሪያን ይጠቀሙ</translation>
+<translation id="4702656508969495934">የቀጥታ መáŒáˆˆáŒ« ጽáˆá ይታያáˆá£ ለትኩረት መስኮት መቀየሪያን ይጠቀሙ</translation>
<translation id="470284880436071933">ወንጀሠእና áትህ</translation>
<translation id="4704732901923281920">የስአህይወት ሳይንሶች</translation>
<translation id="4708268264240856090">የእርስዎ áŒáŠ•áŠ™áŠá‰µ ተቋርጧáˆ</translation>
@@ -1407,6 +1416,7 @@
<translation id="483241715238664915">ማስጠንቀቂያዎችን አብራ</translation>
<translation id="4834250788637067901">Google Payን የሚጠቀሙ የመክáˆá‹« ዘዴዎችᣠቅናሾች እና አድራሻዎች</translation>
<translation id="4838327282952368871">ህáˆáˆ˜áŠ›</translation>
+<translation id="4839087176073128681">በGoogle ኢንዱስትሪ-መሪ ደህንáŠá‰µ በሚቀጥለዠጊዜ በáጥáŠá‰µ ይክáˆáˆ‰ እና ካርድዎን ይጠብá‰á¢</translation>
<translation id="4840250757394056958">የChrome ታሪክዎን ይመáˆáŠ¨á‰±</translation>
<translation id="484462545196658690">ራስ-ሰር</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> ላይ ተጨማሪ ቅናሽ á‹«áŒáŠ™ እና ሌሎች</translation>
@@ -1468,7 +1478,8 @@
<translation id="5002932099480077015">የáŠá‰ƒ እንደሆአChrome ለበለጠ áˆáŒ£áŠ• ቅጽ አሞላሠሲባሠበዚህ መሳሪያ ላይ ያለዠየካርድዎን ቅጂ ያከማቻáˆá¢</translation>
<translation id="5011561501798487822">የተገኘ ቋንቋ</translation>
<translation id="5015510746216210676">የማሽን ስáˆá¦</translation>
-<translation id="5017554619425969104">እርስዎ የቀዱት ጽሑá</translation>
+<translation id="5017554619425969104">እርስዎ የቀዱት ጽáˆá</translation>
+<translation id="5017828934289857214">በኋላ አስታá‹áˆ°áŠ</translation>
<translation id="5018422839182700155">ይህን ገጽ መክáˆá‰µ አáˆá‰°á‰»áˆˆáˆ</translation>
<translation id="5019198164206649151">የመጠባበቂያ ማከማቻ በመጥᎠáˆáŠ”ታ ላይ</translation>
<translation id="5020776957610079374">የዓለሠሙዚቃ</translation>
@@ -1488,6 +1499,7 @@
<translation id="5051305769747448211">የቀጥታ ስርጭት አስቂáŠ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{አቅራቢያ አጋራን በመጠቀሠይህንን á‹á‹­áˆ ለመላክ በመሣሪያዎ ላይ ቦታ (<ph name="DISK_SPACE_SIZE" />) ያስለቅá‰}one{አቅራቢያ አጋራን በመጠቀሠእáŠá‹šáˆ…ን á‹á‹­áˆŽá‰½ ለመላክ በመሣሪያዎ ላይ ቦታ (<ph name="DISK_SPACE_SIZE" />)ን ያስለቅá‰}other{አቅራቢያ አጋራን በመጠቀሠእáŠá‹šáˆ…ን á‹á‹­áˆŽá‰½ ለመላክ በመሣሪያዎ ላይ ቦታ (<ph name="DISK_SPACE_SIZE" />)ን ያስለቅá‰}}</translation>
<translation id="5056549851600133418">ለእርስዎ የሚሆኑ ጽሑáŽá‰½</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" />ን በሚጠቀሙ ድር ጣቢያዎች ላይ በWindows Hello ለማረጋገጥ መርጠዋáˆá¢ ይህ አቅራቢ ስለእርስዎ የመክáˆá‹« ዘዴ መረጃ አከማችቶ ሊሆን ይችላáˆá¢ እርስዎ ይህንን <ph name="LINK_TEXT" /> ይችላሉá¢</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" /> ለማለት áˆáˆáŒˆá‹ áŠá‹?</translation>
<translation id="5066056036849835175">የማተሠታሪክ</translation>
<translation id="5068234115460527047">ሄጅ áˆáŠ•á‹µ</translation>
@@ -1501,10 +1513,8 @@
<translation id="5087286274860437796">የአገáˆáŒ‹á‹­ የዕá‹á‰…ና ማረጋገጫ በዚህ ጊዜ ላይ የሚሰራ አይደለáˆá¢</translation>
<translation id="5087580092889165836">ካርድ አክáˆ</translation>
<translation id="5088142053160410913">ለከዋአመáˆá‹•áŠ­á‰µ ይላኩ</translation>
-<translation id="5089810972385038852">áŒá‹›á‰µ</translation>
<translation id="5093232627742069661">Z-እጥá‹á‰µ</translation>
<translation id="5094747076828555589">ይህ አገáˆáŒ‹á‹­ <ph name="DOMAIN" /> መሆኑን ሊያረጋáŒáŒ¥ አáˆá‰»áˆˆáˆá¤ የደህንáŠá‰µ እá‹á‰…ና ማረጋገጫዠበChromium የሚታመን አይደለáˆá¢ ይሄ በተሳሳተ አወቃቀር ወይሠአንድ አጥቂ áŒáŠ•áŠ™áŠá‰µá‹ŽáŠ• በመጥለበየተከሰተ ሊሆን ይችላáˆá¢</translation>
-<translation id="5095208057601539847">ጠቅላይ áŒá‹›á‰µ</translation>
<translation id="5097099694988056070">እንደ የሲá’á‹©/ራሠአጠቃቀሠያለ የመሣሪያ ስታቲስቲክስ</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">የጣቢያ ደህንáŠá‰µ የተጠበቀ አይደለáˆ</translation>
@@ -1536,12 +1546,13 @@
<translation id="5159010409087891077">ገጹን ማንáŠá‰µ በማያሳá‹á‰… አዲስ መስኮት á‹áˆµáŒ¥ ይክáˆá‰± (⇧⌘N)</translation>
<translation id="5161334686036120870">ርዕሰ ጉዳይá¦</translation>
<translation id="5161506081086828129">á‰áˆáˆ 9</translation>
-<translation id="5164798890604758545">ጽሑá ገብቷáˆ</translation>
+<translation id="5164798890604758545">ጽáˆá ገብቷáˆ</translation>
<translation id="516920405563544094">የ<ph name="CREDIT_CARD" /> CVC ያስገቡᢠካረጋገጡ በኋላ የGoogle መለያዎ ካርድ á‹áˆ­á‹áˆ®á‰½ ለዚህ ጣቢያ ይጋራሉá¢</translation>
<translation id="5169827969064885044">የድርጅት መለያዎን መዳረሻ ሊያጡ ወይሠየማንáŠá‰µ ስርቆት ሊያጋጥመዎት ይችላሉᢠChrome የይለá ቃáˆá‹ŽáŠ• አáˆáŠ• እንዲቀይሩ ይመክራáˆá¢</translation>
<translation id="5171045022955879922">á‹­áˆáˆáŒ‰ ወይሠዩአርኤሠይጻá‰</translation>
<translation id="5171689220826475070">á‹áŠ•áŽáˆá‹µ-አá‹áˆ®á“</translation>
<translation id="5172758083709347301">ማሽን</translation>
+<translation id="5177076414499237632">ስለዚህ ገጽ áˆáŠ•áŒ­ እና ርዕስ ይወá‰</translation>
<translation id="5179510805599951267">በ<ph name="ORIGINAL_LANGUAGE" /> አይደለáˆ? ይህን ስህተት ሪá–ርት ያድርጉ</translation>
<translation id="518639307526414276">የቤት እንስሳት áˆáŒá‰¥ እና የቤት እንስሳት እንክብካቤ አቅርቦቶች</translation>
<translation id="5190835502935405962">የዕáˆá‰£á‰¶á‰½ አሞሌ</translation>
@@ -1575,7 +1586,7 @@
<translation id="5269999699920406580">በመላዠስርዓት ላይ የሚተገበሩ ባህሪያት በባለቤቱ ብቻ áŠá‹ ሊዋቀሩ የሚችሉትᦠ<ph name="OWNER_EMAIL" /></translation>
<translation id="5273658854610202413">ማስጠንቀቂያᦠይህ መመሪያ ሊዋሃዱ የሚችሉ የመá‹áŒˆá‰ -ቃላት መመሪያዎች አካሠስላáˆáˆ†áŠ በPolicyDictionaryMultipleSourceMergeList መመሪያ ላይ በተገለጸዠመሠረትት አáˆá‰°á‹‹áˆƒá‹°áˆá¢</translation>
<translation id="5273881944177595304">የድር መተáŒá‰ áˆªá‹«á‹Žá‰½ እና መስመር ላይ መሣሪያዎች</translation>
-<translation id="5274025349362408263">መጽáˆáት እና ስአጽሑá</translation>
+<translation id="5274025349362408263">መጽáˆáት እና ስአጽáˆá</translation>
<translation id="5279286380302340275">á‹áˆ­á‹¶á‰½áŠ• ያቀናብሩ</translation>
<translation id="5283044957620376778">B1</translation>
<translation id="5284295735376057059">የሰáŠá‹µ ባሕሪያት</translation>
@@ -1669,7 +1680,7 @@
<translation id="5535133333442455806">የአሰሳ á‹áˆ‚ብ አá‹áˆ«áˆ­áŠ• ያጽዱᣠበChrome ቅንብሮች á‹áˆµáŒ¥ የእርስዎን አሰሳ ታሪክᣠኩኪዎችᣠመሸጎጫዎች እና ተጨማሪ áŠáŒˆáˆ®á‰½ ለማጽዳት አስገባን ይጫኑ</translation>
<translation id="5536214594743852365">የ«<ph name="SECTION" />» ክáሉን አሳይ</translation>
<translation id="5539243836947087108">ታንኳ</translation>
-<translation id="5540224163453853">የተጠየቀá‹áŠ• ጽሑá ማáŒáŠ˜á‰µ አáˆá‰°á‰»áˆˆáˆá¢</translation>
+<translation id="5540224163453853">የተጠየቀá‹áŠ• ጽáˆá ማáŒáŠ˜á‰µ አáˆá‰°á‰»áˆˆáˆá¢</translation>
<translation id="5540969246441091044">የáˆáˆ¨áˆµ ስá–ርት</translation>
<translation id="5541086400771735334">የመáˆá‹•áŠ­á‰µ ሳጥን 7</translation>
<translation id="5541546772353173584">ኢሜይሠያክሉ</translation>
@@ -1702,6 +1713,7 @@
<translation id="5624120631404540903">የይለá ቃሎችን አስተዳድር</translation>
<translation id="5629630648637658800">የመáˆáˆªá‹« ቅንብሮችን መጫን አáˆá‰°áˆ³áŠ«áˆ</translation>
<translation id="5631439013527180824">áˆáŠ­ á‹«áˆáˆ†áŠ የመሣሪያ አስተዳደር ማስመሰያ</translation>
+<translation id="5632485077360054581">እንዴት እንደሆአአሳየáŠ</translation>
<translation id="5633066919399395251">በአáˆáŠ‘ ጊዜ በ<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ላይ የሚገኙ አጥቂዎች የእርስዎን መረጃ (ለáˆáˆ³áˆŒá¦ áŽá‰¶á‹Žá‰½á£ የይለá ቃላትᣠመáˆá‹•áŠ­á‰¶á‰½ እና ክሬዲት ካርዶች) የሚሰርበወይሠየሚሰርዙ አደገኛ á•áˆ®áŒáˆ«áˆžá‰½áŠ• በእርስዎ ኮáˆá’á‹á‰°áˆ­ ላይ ለመጫን እየሞከሩ ሊሆኑ ይችላሉᢠ<ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">አታላይ ይዘት ታáŒá‹·áˆá¢</translation>
<translation id="5633259641094592098">የአáˆáˆáŠ® እና ኢንዲ áŠáˆáˆžá‰½</translation>
@@ -1819,6 +1831,7 @@
<translation id="5989320800837274978">ቋሚ ተኪ አገáˆáŒ‹á‹®á‰½áˆ ሆኑ የ.pac ስክሪá•á‰µ ዩአርኤሠአáˆá‰°áŒˆáˆˆáŒ¹áˆá¢</translation>
<translation id="5992691462791905444">Z-እጥá‹á‰µáŠ• በመቀየስ ላይ</translation>
<translation id="5995727681868049093">በGoogle መለያዎ á‹áˆµáŒ¥ የእርስዎን መረጃᣠáŒáˆ‹á‹ŠáŠá‰µ እና ደህንáŠá‰µ ያስተዳድሩ</translation>
+<translation id="5997247540087773573">አáˆáŠ• የተጠቀሙበት የይለá ቃሠበá‹áˆ‚ብ ጥሰት á‹áˆµáŒ¥ ተገáŠá‰·áˆá¢ የእርስዎን መለያዎች ደህንáŠá‰µ ለመጠበቅ Google የይለá ቃሠአስተዳዳሪ አáˆáŠ‘ኑ እንዲቀይሩት እና የተቀመጡ የይለá ቃላትዎን እንዲáˆá‰µáˆ¹ ይመክራáˆá¢</translation>
<translation id="6000758707621254961">ለ«<ph name="SEARCH_TEXT" />» <ph name="RESULT_COUNT" /> á‹áŒ¤á‰¶á‰½áŠ• አሳይ</translation>
<translation id="6006484371116297560">የታወቀ ገጽታ</translation>
<translation id="6008122969617370890">N-ለ1 ቅደáˆ-ተከተáˆ</translation>
@@ -1866,7 +1879,7 @@
<translation id="6106989379647458772">በ<ph name="PAGE" /> ላይ ያለዠድረ-áŒˆá… áˆˆáŒŠá‹œá‹ á‰°á‰ áˆ‹áˆ½á‰¶ ሊሆን ይችላሠወይሠበቋሚáŠá‰µ ወደ አዲስ የድር አድራሻ ተንቀሳቅሶ ሊሆን ይችላáˆá¢</translation>
<translation id="6107012941649240045">ለእዚህ ቀርቧáˆ</translation>
<translation id="610911394827799129">የእርስዎ Google መለያ <ph name="BEGIN_LINK" />myactivity.google.com<ph name="END_LINK" /> ላይ ሌሎች የአሰሳ ታሪክ á‹“á‹­áŠá‰¶á‰½ ሊኖረዠይችላáˆá¢</translation>
-<translation id="6116338172782435947">ወደ ቅንጥብ ሰሌዳዠየተቀዱ ጽሑá እና áˆáˆµáˆŽá‰½áŠ• ይመáˆáŠ¨á‰±</translation>
+<translation id="6116338172782435947">ወደ ቅንጥብ ሰሌዳዠየተቀዱ ጽáˆá እና áˆáˆµáˆŽá‰½áŠ• ይመáˆáŠ¨á‰±</translation>
<translation id="6120179357481664955">የእርስዎን UPI መታወቂያ ያስታá‹áˆ³áˆ‰?</translation>
<translation id="6124432979022149706">Chrome Enterprise አያያዦች</translation>
<translation id="6127379762771434464">ንጥሠተወáŒá‹·áˆ</translation>
@@ -1914,7 +1927,6 @@
<translation id="627746635834430766">በሚቀጥለዠጊዜ በበለጠ áጥáŠá‰µ ለመክáˆáˆ ካርድዎን እና የማስከáˆá‹« አድራሻዎን በGoogle መለያዎ ላይ ያስቀáˆáŒ¡á¢</translation>
<translation id="6279183038361895380">የእርስዎን ጠቋሚ ለማሳየት |<ph name="ACCELERATOR" />| ይጫኑ</translation>
<translation id="6280223929691119688">ወደዚህ አድራሻ ማድረስ አይቻáˆáˆá¢ የተለየ አድራሻ á‹­áˆáˆ¨áŒ¡á¢</translation>
-<translation id="6282194474023008486">የá–ስታ ኮድ</translation>
<translation id="6285507000506177184">በChrome አá‹áˆ«áˆ­ á‹áˆµáŒ¥ á‹áˆ­á‹¶á‰½áŠ• ያቀናብሩᣠበChrome á‹áˆµáŒ¥ ያወረዷቸá‹áŠ• á‹á‹­áˆŽá‰½ ለማቀናበር አስገባን ይጫኑ</translation>
<translation id="6289939620939689042">ገጽ ቀለáˆ</translation>
<translation id="6290238015253830360">የእርስዎ የተጠቆሙ ዘገባዎች እዚህ ይመጣሉ</translation>
@@ -1936,6 +1948,7 @@
<translation id="6337133576188860026">ከ<ph name="SIZE" /> á‹«áŠáˆ° ቦታ ያስለቅቃáˆá¢ አንዳንድ ጣቢያዎች በሚቀጥለዠጉብáŠá‰µá‹Ž ላይ ይበáˆáŒ¥ በá‹áŒá‰³ ሊጫኑ ይችላሉá¢</translation>
<translation id="6337534724793800597">መáˆáˆªá‹«á‹Žá‰½áŠ• በስሠአጣራ</translation>
<translation id="6340739886198108203">ሚስጥራዊ ይዘት በሚታይበት ጊዜ የአስተዳዳሪ መመሪያ ቅጽበታዊ ገጽ እይታዎችን ወይሠቅጂዎች መá‹áˆ°á‹µáŠ• አይመክርáˆá¡-</translation>
+<translation id="6348220984832452017">ንበተለዋዋጮች</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" />ን ይጫኑ</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{áˆáŠ•áˆ}=1{1 ይለá ቃሠ(ለ<ph name="DOMAIN_LIST" />ᣠሰáˆáˆ¯áˆ)}=2{2 ይለá ቃላት (ለ<ph name="DOMAIN_LIST" />ᣠሰáˆáˆ¯áˆ)}one{# ይለá ቃላት (ለ<ph name="DOMAIN_LIST" />ᣠሰáˆáˆ¯áˆ)}other{# ይለá ቃላት (ለ<ph name="DOMAIN_LIST" />ᣠሰáˆáˆ¯áˆ)}}</translation>
<translation id="6355392890578844978">ይህ አሳሽ በኩባንያ ወይሠበሌላ ድርጅት አይተዳደርáˆá¢ በዚህ መሣሪያ ላይ ያለ እንቅስቃሴ ከChromium á‹áŒ­ ሊተዳደር ይችላáˆá¢ <ph name="BEGIN_LINK" />የበለጠ ለመረዳት<ph name="END_LINK" /></translation>
@@ -1967,7 +1980,6 @@
<translation id="643051589346665201">የGoogle የይለá ቃሠቀይር</translation>
<translation id="6433490469411711332">የዕá‹á‰‚á‹« መረጃን ያርትዑ</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ማገናኘት አሻáˆáˆ¨áŠ ብáˆáˆá¢</translation>
-<translation id="6438025220577812695">እራሴ እቀይረዋለáˆ</translation>
<translation id="6440503408713884761">የተተወ</translation>
<translation id="6443406338865242315">የትኛዎቹ ቅጥያዎች እና ተሰኪዎች ናቸዠእርስዎ የጫኑት</translation>
<translation id="6446163441502663861">ካሠ(የደብዳቤ á–ስታ)</translation>
@@ -2097,9 +2109,9 @@
<translation id="6828866289116430505">ጀáŠá‰²áŠ­áˆµ</translation>
<translation id="6831043979455480757">መተርጎáˆ</translation>
<translation id="6833752742582340615">ደህንáŠá‰³á‰¸á‹ ለተጠበቀ እና ለáˆáŒ£áŠ• ክáá‹« ማጠናቀቆች የካርድዎን እና የሂሳብ አከá‹áˆáˆ መረጃን በGoogle መለያዎ ላይ ያስቀáˆáŒ¡</translation>
-<translation id="6839929833149231406">አካባቢ</translation>
<translation id="6846340164947227603">áˆáŠ“ባዊ የካርድ á‰áŒ¥áˆ­áŠ• ይጠቀሙ...</translation>
<translation id="6852204201400771460">መተáŒá‰ áˆªá‹« ዳáŒáˆ ይጫን?</translation>
+<translation id="6857776781123259569">የይለá ቃላትን ያቀናብሩ...</translation>
<translation id="686485648936420384">የሸማቾች መርጃዎች</translation>
<translation id="6865412394715372076">ይህ ካርድ አáˆáŠ• ላይ ሊረጋገጥ አይችáˆáˆ</translation>
<translation id="6869334554832814367">የáŒáˆ ብድሮች</translation>
@@ -2148,7 +2160,6 @@
<translation id="6965978654500191972">መሣሪያ</translation>
<translation id="696703987787944103">እንደሚታየá‹</translation>
<translation id="6968269510885595029">የደህንáŠá‰µ á‰áˆáዎን ይጠቀሙ</translation>
-<translation id="6970216967273061347">ወረዳ</translation>
<translation id="6971439137020188025">በስላይዶች á‹áˆµáŒ¥ አዲስ የGoogle የá‹áŒáŒ…ት አቀራረብን áጠር</translation>
<translation id="6972629891077993081">HID መሣሪያዎች</translation>
<translation id="6973656660372572881">áˆáˆˆá‰±áˆ ቋሚ ተኪ አገáˆáŒ‹á‹®á‰½ እና የ.pac ስክሪá•á‰µ ዩአርኤሠተገáˆáŒ¸á‹‹áˆá¢</translation>
@@ -2187,7 +2198,6 @@
<translation id="7081308185095828845">ይህ ባህሪ በእርስዎ መሣሪያ ላይ አይገáŠáˆ</translation>
<translation id="7083258188081898530">መሳቢያ 9</translation>
<translation id="7086090958708083563">ሰቀላ በተጠቃሚ ተጠይቋáˆ</translation>
-<translation id="7087282848513945231">ክáለ ሀገር</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />ᣠበChrome ቅንብሮች á‹áˆµáŒ¥ áˆá‰ƒá‹¶á‰½áŠ• እና በመላ ጣቢያዎች ላይ የተከማቸ á‹áˆ‚ብን ለማቀናበር ትርን ከዚያ አስገባን ይጫኑ</translation>
<translation id="7096937462164235847">የዚህ ድር ጣቢያ ማንáŠá‰µ አáˆá‰°áˆ¨áŒ‹áŒˆáŒ áˆá¢</translation>
<translation id="7101893872976785596">አስáˆáˆª áŠáˆáˆžá‰½</translation>
@@ -2206,10 +2216,10 @@
<ph name="LIST_ITEM" />በቅጾች á‹áˆµáŒ¥ የገባ መረጃ<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">ወደዚህ አድራሻ መላክ አይቻáˆáˆá¢ የተለየ አድራሻ á‹­áˆáˆ¨áŒ¡á¢</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">የእርስዎ አስተዳዳሪ ይህ á‹áˆ‚ብ እንዳይቀዳ ከáˆáŠ­áˆáˆá¢</translation>
<translation id="7135130955892390533">áˆáŠ”ታን አሳይ</translation>
<translation id="7138472120740807366">የማድረሻ ስáˆá‰µ</translation>
-<translation id="7139724024395191329">ኤሚሬት</translation>
<translation id="7139892792842608322">ዋና መሳቢያ</translation>
<translation id="714064300541049402">ጎን 2 áˆáˆµáˆ X ሽáŒáˆ½áŒ</translation>
<translation id="7152423860607593928">á‰áŒ¥áˆ­-14 (የደብዳቤ á–ስታ)</translation>
@@ -2426,7 +2436,8 @@
<translation id="7669271284792375604">በዚህ ጣቢያ ላይ ያሉ አጥቂዎች እርስዎ የአሰሳ ተሞክሮዎን ሊጎዱ (ለáˆáˆ³áˆŒá¦ መáŠáˆ» ገጽዎን በመቀየር ወይሠበሚጎበኟቸዠጣቢያዎች ላይ ተጨማሪ ማስታወቂያዎችን በማሳየት) የሚችሉ á•áˆ®áŒáˆ«áˆžá‰½áŠ• እንዲጭኑ ለማታለሠሊሞክሩ ይችላሉá¢</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ሚስጥራዊ ተብሎ በተጠቆመ á‹áˆ‚ብ ላይ የተወሰዱ እርáˆáŒƒá‹Žá‰½ (ከገቡ ጊዜ ጀáˆáˆ® 1 እርáˆáŒƒ)ᢠ<ph name="BEGIN_LINK" />የበለጠ ለመረዳት<ph name="END_LINK" />}one{ሚስጥራዊ ተብሎ በተጠቆመ á‹áˆ‚ብ ላይ የተወሰዱ እርáˆáŒƒá‹Žá‰½ (ከገቡ ጊዜ ጀáˆáˆ® # እርáˆáŒƒá‹Žá‰½)ᢠ<ph name="BEGIN_LINK" />የበለጠ ለመረዳት<ph name="END_LINK" />}other{ሚስጥራዊ ተብሎ በተጠቆመ á‹áˆ‚ብ ላይ የተወሰዱ እርáˆáŒƒá‹Žá‰½ (ከገቡ ጊዜ ጀáˆáˆ® # እርáˆáŒƒá‹Žá‰½)ᢠ<ph name="BEGIN_LINK" />የበለጠ ለመረዳት<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">የመáˆá‹•áŠ­á‰µ ሳጥን 6</translation>
-<translation id="7676643023259824263">የቅንጥብ ሰሌዳ ጽሑá <ph name="TEXT" /> á‹­áˆáˆáŒ‰</translation>
+<translation id="7675325315208090829">የክáá‹« ዘዴዎችን ያስተዳድሩ...</translation>
+<translation id="7676643023259824263">የቅንጥብ ሰሌዳ ጽáˆá <ph name="TEXT" /> á‹­áˆáˆáŒ‰</translation>
<translation id="7679367271685653708">በChrome ቅንብሮች á‹áˆµáŒ¥ የአሰሳ ታሪክዎን ይመáˆáŠ¨á‰± እና ያቀናብሩ</translation>
<translation id="7679947978757153706">ቤá‹á‰¦áˆ</translation>
<translation id="7681273392938116652">áˆáŠ“ባዊ ስá–ርቶች</translation>
@@ -2468,7 +2479,6 @@
<translation id="7766518757692125295">ቀሚስ</translation>
<translation id="7770259615151589601">የተሰየመ-ረጅáˆ</translation>
<translation id="7773005668374414287">ተመሳሳይ ቅደáˆ-ተከተሠáŠá‰± ወደ ላይ</translation>
-<translation id="777702478322588152">መስተዳድር</translation>
<translation id="7791011319128895129">á‹«áˆá‰°áˆˆá‰€á‰</translation>
<translation id="7791196057686275387">ቤáˆ</translation>
<translation id="7791543448312431591">ያክሉ</translation>
@@ -2559,12 +2569,12 @@
<translation id="8055534648776115597">የሙያ እና ቀጣይ ትáˆáˆ…ርት</translation>
<translation id="8057711352706143257">«<ph name="SOFTWARE_NAME" />» በአáŒá‰£á‰¡ አáˆá‰°á‹‹á‰€áˆ¨áˆá¢ «<ph name="SOFTWARE_NAME" />»ን ማራገá አብዛኛዠጊዜ ችáŒáˆ©áŠ• á‹­áˆá‰³á‹‹áˆá¢ <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">የáˆáŒá‰¥ ማáˆáˆ¨á‰µ</translation>
-<translation id="8066955247577885446">ይቅርታᣠየሆአችáŒáˆ­ ተáˆáŒ¥áˆ¯áˆá¢</translation>
<translation id="8067872629359326442">አáˆáŠ• የይለá ቃáˆá‹ŽáŠ• በአንድ አታላይ ጣቢያ ላይ አስገብተዋáˆá¢ Chromium ሊያáŒá‹ ይችላáˆá¢ የእርስዎን የይለá ቃሠለመለወጥ እና የእርስዎ መለያ ስጋት á‹áˆµáŒ¥ እንዳለ ለGoogle ለማሳወቅᣠመለያን ከጥቃት ተከላከሠየሚለዠላይ ጠቅ ያድርጉá¢</translation>
<translation id="8070439594494267500">የመተáŒá‰ áˆªá‹« አዶ</translation>
<translation id="8074253406171541171">10x13 (የደብዳቤ á–ስታ)</translation>
<translation id="8075736640322370409">አዲስ Google ሉህ በáጥáŠá‰µ áጠር</translation>
<translation id="8075898834294118863">የጣቢያ ቅንብሮችን ያቀናብሩ</translation>
+<translation id="8076492880354921740">ትሮች</translation>
<translation id="8078141288243656252">በሚሸከርከርበት ጊዜ ማብራራት አይችáˆáˆ</translation>
<translation id="8079031581361219619">ጣቢያ ዳáŒáˆ ይጫን?</translation>
<translation id="8081087320434522107">ሴዳኖች</translation>
@@ -2687,6 +2697,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ቅንብሮች</translation>
+<translation id="8428634594422941299">ገባáŠ</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />ᣠበChrome ቅንብሮች á‹áˆµáŒ¥ የኩኪ áˆáˆ­áŒ«á‹Žá‰½á‹ŽáŠ• ለማቀናበር ትርን ከዚያ አስገባን ይጫኑ</translation>
<translation id="8433057134996913067">ይህ ከአብዛኛዎቹ የድር ጣቢያዎች ዘáŒá‰¶ ያስወጣዎታáˆá¢</translation>
<translation id="8434840396568290395">የቤት እንስሳት</translation>
@@ -2768,7 +2779,7 @@
<translation id="8710842507289500830">የቅርጸ-á‰áˆáŠ ቅጥ</translation>
<translation id="8712637175834984815">ገባáŠ</translation>
<translation id="8713438021996895321">ስአáŒáŒ¥áˆ</translation>
-<translation id="8715502133575042727">የáˆáŒ†á‰½ ስአጽሑá</translation>
+<translation id="8715502133575042727">የáˆáŒ†á‰½ ስአጽáˆá</translation>
<translation id="8718314106902482036">ክáá‹« አáˆá‰°áŒ áŠ“ቀቀáˆ</translation>
<translation id="8719263113926255150"><ph name="ENTITY" />ᣠ<ph name="DESCRIPTION" />ᣠየáለጋ ጥቆማ áˆáˆ³á‰¥</translation>
<translation id="8719528812645237045">በርካታ ብስ ከላይ</translation>
@@ -2785,6 +2796,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> የእርስዎ የ <ph name="ORIGIN" /> ኮድ áŠá‹</translation>
<translation id="874918643257405732">ይህን ትር á‹•áˆá‰£á‰µ ያድርጉት</translation>
<translation id="8751426954251315517">እባክዎ በሚቀጥለዠጊዜ እንደገና ይሞክሩ</translation>
+<translation id="8757526089434340176">የGoogle Pay ቅናሽ ይገኛáˆ</translation>
<translation id="8758885506338294482">ተáŽáŠ«áŠ«áˆª የቪዲዮ ጨዋታ</translation>
<translation id="8759274551635299824">ይህ ካርድ የአገáˆáŒáˆŽá‰µ ጊዜዠአብቅቷáˆ</translation>
<translation id="87601671197631245">ይህ ጣቢያ ጊዜ ያለáˆá‰ á‰µ የደህንáŠá‰µ á‹á‰…ረትን ይጠቀማáˆá£ ይህ ወደዚህ ጣቢያ ሲላክ መረጃዎን (ለáˆáˆ³áˆŒá¦ የይለá ቃላትᣠመáˆá‹•áŠ­á‰¶á‰½ ወይሠክሬዲት ካርዶች) ሊያጋáˆáŒ¥ ይችላáˆá¢</translation>
@@ -2792,6 +2804,7 @@
<translation id="8763927697961133303">የዩኤስቢ መሣሪያ</translation>
<translation id="8763986294015493060">በአáˆáŠ‘ ጊዜ ክáት የሆኑ áˆáˆ‰áŠ•áˆ ማንáŠá‰µáŠ• የማያሳá‹á‰ መስኮቶች á‹­á‹áŒ‰</translation>
<translation id="8766943070169463815">ደህንáŠá‰± የተጠበቀ የክáá‹« ማስረጃ ማረጋገጫ ሉህ ተከáቷáˆ</translation>
+<translation id="8767765348545497220">የእርዳታ አረá‹áŠ• á‹áŒ‹</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">ሞተርሳይክሎች</translation>
<translation id="8790007591277257123">&amp;ሰርá‹áŠ• ድገáˆ</translation>
@@ -2804,6 +2817,7 @@
<translation id="8806285662264631610">የመታጠቢያ እና የሰá‹áŠá‰µ áˆáˆ­á‰¶á‰½</translation>
<translation id="8807160976559152894">ከእያንዳንዱ ገጽ በኋላ ከርክáˆ</translation>
<translation id="8808828119384186784">የChrome ቅንብሮች</translation>
+<translation id="8813277370772331957">ቆይተህ አስታá‹áˆ°áŠ</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />ᣠትርን ይጫኑ እና ከእርስዎ Chrome ቅንብሮች Chromeን ለማዘመን አስገባን ይጫኑ</translation>
<translation id="8820817407110198400">á‹•áˆá‰£á‰¶á‰½</translation>
<translation id="882338992931677877">ራስዎ የሚያስገቡበት ክáተት</translation>
@@ -2950,7 +2964,7 @@
<translation id="9199905725844810519">ማተሠታáŒá‹·áˆ</translation>
<translation id="9205078245616868884">የእርስዎ á‹áˆ‚ብ በእርስዎ የስáˆáˆ¨á‰µ የይለá ቃሠተመስጥሯáˆá¢ ስáˆáˆ¨á‰µáŠ• ለመጀመር ያስገቡትá¢</translation>
<translation id="920511547311754821">ኮድዎን ማáŒáŠ˜á‰µ አáˆá‰»áˆ‰áˆ? <ph name="IDS_AUTOFILL_CARD_UNMASK_OTP_INPUT_DIALOG_NEW_CODE_MESSAGE" /></translation>
-<translation id="9207861905230894330">ጽሑá ማከሠአáˆá‰°á‰»áˆˆáˆá¢</translation>
+<translation id="9207861905230894330">ጽáˆá ማከሠአáˆá‰°á‰»áˆˆáˆá¢</translation>
<translation id="9213433120051936369">ገጽታን ያብáŒ</translation>
<translation id="9215416866750762878">አንድ መተáŒá‰ áˆªá‹« Chrome ከዚህ ጣቢያ ጋር ደህንáŠá‰± በተጠበቀ áˆáŠ”ታ እንዳይገናአእያቆመዠáŠá‹</translation>
<translation id="9219103736887031265">áˆáˆµáˆŽá‰½</translation>
@@ -2983,6 +2997,7 @@
<translation id="988159990683914416">የገንቢዎች áŒáŠ•á‰£á‰³</translation>
<translation id="989988560359834682">አድራሻ ያርትዑ</translation>
<translation id="991413375315957741">የእንቅስቃሴ ወይሠየብርሃን ዳሳሾች</translation>
+<translation id="992110854164447044">áˆáŠ“ባዊ ካርድ እርስዎን ሊሆን ከሚችሠማጭበርበር ለመጠበቅ እንዲረዳዎ ትክክለኛá‹áŠ• የእርስዎ ካርድ ይደብቀዋáˆá¢ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ሮá‹</translation>
<translation id="992432478773561401">«<ph name="SOFTWARE_NAME" />» በእርስዎ ኮáˆá’á‹á‰°áˆ­ ወይሠበአá‹á‰³áˆ¨ መረቡ ላይ በአáŒá‰£á‰¡ አáˆá‰°áŒ«áŠáˆá¦
diff --git a/chromium/components/strings/components_strings_ar.xtb b/chromium/components/strings/components_strings_ar.xtb
index 1fe4b2bf4b3..270d6b15fce 100644
--- a/chromium/components/strings/components_strings_ar.xtb
+++ b/chromium/components/strings/components_strings_ar.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">منح دراسية ومنح تعليمية ودعم مالي</translation>
<translation id="1048785276086539861">عند تعديل التعليقات التوضيحية، سيعود هذا المستند إلى عرض صÙحة واحدة.</translation>
<translation id="1050038467049342496">إغلاق التطبيقات الأخرى</translation>
+<translation id="1053959602163383901">لقد اخترت إثبات هويتك باستخدام جهاز مصادقة على المواقع الإلكترونية التي تستخدم مقدّÙÙ… الخدمة <ph name="PROVIDER_ORIGIN" />. قد يكون مقدّÙÙ… الخدمة احتÙظ بمعلومات حول طريقة الدÙع التي تم استخدامها، ويمكنك <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">تراجع عن الإ&amp;ضاÙØ©</translation>
<translation id="1056663316309890343">برامج للصور</translation>
<translation id="1056898198331236512">تحذير</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">طريقة الاستلام</translation>
<translation id="1281476433249504884">المكدّÙس 1</translation>
<translation id="1285320974508926690">عدم ترجمة هذا الموقع مطلقًا</translation>
+<translation id="1288548991597756084">Ø­Ùظ البطاقة بأمان</translation>
<translation id="1292571435393770077">الدÙرج 16</translation>
<translation id="1292701964462482250">â€"هناك برامج على جهاز الكمبيوتر تمنع اتصال Chrome بالويب بأمان" (أجهزة الكمبيوتر المزوّدة بنظام التشغيل Windows Ùقط)</translation>
<translation id="1294154142200295408">صيغ سطر الأوامر</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;لإصلاح الخطأ، انقر على &lt;strong&gt;"اتصال"&lt;/strong&gt; ÙÙŠ الصÙحة التي تحاول Ùتحها.&lt;/p&gt;</translation>
<translation id="1507780850870535225">تصميم مناظر طبيعية</translation>
<translation id="1513706915089223971">قائمة إدخالات السجل</translation>
+<translation id="1516097932025103760">â€Ø³ØªÙƒÙˆÙ† البطاقة مشÙّرة، وسيتم Ø­Ùظها بأمان ولن يتم تخزين رمز التحقق من البطاقة (CVC) مطلقًا.</translation>
<translation id="1517433312004943670">رقم الهات٠مطلوب</translation>
<translation id="1519264250979466059">تاريخ الإصدار</translation>
<translation id="1521159554480556801">أعمال Ùنية بالغزل والنسيج</translation>
@@ -272,7 +275,7 @@
<translation id="1634828734222219955">الإجمالي</translation>
<translation id="163669211644121865">إعداد الضرائب والتخطيط الضريبي</translation>
<translation id="1638780421120290329">يتعذّر Ø­Ùظ البطاقة</translation>
-<translation id="1639239467298939599">جار٠التحميل.</translation>
+<translation id="1639239467298939599">جار٠التحميل</translation>
<translation id="1640180200866533862">سياسات المستخدم</translation>
<translation id="1640244768702815859">جرّب <ph name="BEGIN_LINK" />الانتقال إلى الصÙحة الرئيسية للموقع<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">تأخير الإخراج حتى</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Ø­Ùظ طرق الدÙع وملؤها</translation>
<translation id="1663943134801823270">â€ÙŠØªÙ… أخذ البطاقات والعناوين من ChromeØŒ ويمكنك إدارتها ÙÙŠ <ph name="BEGIN_LINK" />الإعدادات<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">ستتم ترجمة الصÙحات باللغة <ph name="SOURCE_LANGUAGE" /> إلى اللغة <ph name="TARGET_LANGUAGE" /> من الآن Ùصاعدًا.</translation>
+<translation id="1673886523110456987">الدÙع باستخدام <ph name="CARD_DETAIL" /> للاستÙادة من العرض</translation>
<translation id="1674504678466460478">من <ph name="SOURCE_LANGUAGE" /> إلى <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">حاÙØ© قصيرة أولًا</translation>
<translation id="168693727862418163">تعذّر التحقّÙÙ‚ من قيمة السياسة هذه ÙˆÙقًا لمخططها وسيتم تجاهلها.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">مستشعرات الحركة</translation>
<translation id="1717494416764505390">صندوق البريد الإلكتروني 3</translation>
<translation id="1718029547804390981">حجم المستند كبير جدًا بحيث تتعذّر إضاÙØ© تعليق توضيحي إليه</translation>
+<translation id="1720941539803966190">إغلاق البرنامج التعليمي</translation>
<translation id="1721424275792716183">* هناك حقل مطلوب</translation>
<translation id="1727613060316725209">الشهادة صالحة</translation>
<translation id="1727741090716970331">إضاÙØ© رقم بطاقة صالح</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">شواء</translation>
<translation id="2053111141626950936">لن تتم ترجمة الصÙحات باللغة <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{â€Ø¹Ù†Ø¯Ù…ا يكون عنصر التحكم هذا Ù…Ùعّلاً ونشطًا، يحدّد Chrome "المجموعة النموذجية" التي تضم عددًا كبيرًا من الأشخاص الذين تتشابه أنشطة تصÙّحهم مع أنشطة تصÙّحك الأخيرة. ويمكن للمعلÙنين اختيار الإعلانات التي يريدون عرضها للمجموعة ويتم الحÙاظ على خصوصية سجلّ التصÙّح على جهازك، مع العÙلم أنّ مجموعتك يتم تعديلها كل يوم.}=1{â€Ø¹Ù†Ø¯Ù…ا يكون عنصر التحكم هذا Ù…Ùعّلاً ونشطًا، يحدّد Chrome "المجموعة النموذجية" التي تضم عددًا كبيرًا من الأشخاص الذين تتشابه أنشطة تصÙّحهم مع أنشطة تصÙّحك الأخيرة. ويمكن للمعلÙنين اختيار الإعلانات التي يريدون عرضها للمجموعة ويتم الحÙاظ على خصوصية سجلّ التصÙّح على جهازك، مع العÙلم أنّ مجموعتك يتم تعديلها كل يوم.}two{â€Ø¹Ù†Ø¯Ù…ا يكون عنصر التحكم هذا Ù…Ùعّلاً ونشطًا، يحدّد Chrome "المجموعة النموذجية" التي تضم عددًا كبيرًا من الأشخاص الذين تتشابه أنشطة تصÙّحهم مع أنشطة تصÙّحك الأخيرة. ويمكن للمعلÙنين اختيار الإعلانات التي يريدون عرضها للمجموعة ويتم الحÙاظ على خصوصية سجلّ التصÙّح على جهازك، مع العÙلم أنّ مجموعتك يتم تعديلها كل يومَين ({NUM_DAYS}).}few{â€Ø¹Ù†Ø¯Ù…ا يكون عنصر التحكم هذا Ù…Ùعّلاً ونشطًا، يحدّد Chrome "المجموعة النموذجية" التي تضم عددًا كبيرًا من الأشخاص الذين تتشابه أنشطة تصÙّحهم مع أنشطة تصÙّحك الأخيرة. ويمكن للمعلÙنين اختيار الإعلانات التي يريدون عرضها للمجموعة ويتم الحÙاظ على خصوصية سجلّ التصÙّح على جهازك، مع العÙلم أنّ مجموعتك يتم تعديلها كل {NUM_DAYS} أيام.}many{â€Ø¹Ù†Ø¯Ù…ا يكون عنصر التحكم هذا Ù…Ùعّلاً ونشطًا، يحدّد Chrome "المجموعة النموذجية" التي تضم عددًا كبيرًا من الأشخاص الذين تتشابه أنشطة تصÙّحهم مع أنشطة تصÙّحك الأخيرة. ويمكن للمعلÙنين اختيار الإعلانات التي يريدون عرضها للمجموعة ويتم الحÙاظ على خصوصية سجلّ التصÙّح على جهازك، مع العÙلم أنّ مجموعتك يتم تعديلها كل {NUM_DAYS} يومًا.}other{â€Ø¹Ù†Ø¯Ù…ا يكون عنصر التحكم هذا Ù…Ùعّلاً ونشطًا، يحدّد Chrome "المجموعة النموذجية" التي تضم عددًا كبيرًا من الأشخاص الذين تتشابه أنشطة تصÙّحهم مع أنشطة تصÙّحك الأخيرة. ويمكن للمعلÙنين اختيار الإعلانات التي يريدون عرضها للمجموعة ويتم الحÙاظ على خصوصية سجلّ التصÙّح على جهازك، مع العÙلم أنّ مجموعتك يتم تعديلها كل {NUM_DAYS} يوم.}}</translation>
-<translation id="2053553514270667976">الرمز البريدي</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{اقتراح واحد}zero{# اقتراح}two{اقتراحان (#)}few{# اقتراحات}many{# اقتراحًا}other{# اقتراح}}</translation>
+<translation id="2066915425250589881">طلب حذ٠هذه المعلومات</translation>
<translation id="2068528718802935086">رÙضّع وأطÙال صغار</translation>
<translation id="2071156619270205202">رقم هذه البطاقة غير مؤهَّل للاستخدام كرقم بطاقة اÙتراضية.</translation>
<translation id="2071692954027939183">تم حظر الإشعارات تلقائيًا لأنّك عادةً لا تسمح بظهورها.</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">â€Ø²Ø± "إدارة المزامنة"ØŒ اضغط على Ù…Ùتاح Enter لإدارة نوع المعلومات التي تريد مزامنتها من خلال إعدادات Chrome.</translation>
<translation id="2091887806945687916">الصوت</translation>
<translation id="2094505752054353250">النطاق غير متطابق</translation>
-<translation id="2096368010154057602">القسم</translation>
<translation id="2099652385553570808">وضع ثلاثة دبابيس يسارًا</translation>
<translation id="2101225219012730419">الإصدار:</translation>
<translation id="2102134110707549001">اقتراح كلمة مرور قوية…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">كرة القدم الأمريكية</translation>
<translation id="2187317261103489799">اكتشا٠(تلقائي)</translation>
<translation id="2188375229972301266">عمل عدة ثقوب ÙÙŠ الأسÙÙ„</translation>
-<translation id="2188852899391513400">â€ÙƒÙ„مة المرور التي استخدمتها للتو تم رصدها ضمن عملية اختراق للبيانات. ولتأمين حساباتك، ينصح "مدير كلمات المرور" من Google بتغييرها الآن ثم التحقّق من كلمات المرور المحÙوظة.</translation>
<translation id="219906046732893612">تجديد المنازل</translation>
<translation id="2202020181578195191">أدخÙÙ„ سنة تاريخ انتهاء صلاحية صحيحة</translation>
<translation id="22081806969704220">الدÙرج 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">تعديل الملÙ</translation>
<translation id="2215963164070968490">كلاب</translation>
<translation id="2218879909401188352">يمكن للمهاجمين الموجودين حاليًا على <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> تثبيت تطبيقات خطيرة تضرّ جهازك أو تضي٠رسومًا إلى Ùاتورة الجوّال أو تسرق معلوماتك الشخصية. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">إعادة تشغيل البرنامج التعليمي</translation>
+<translation id="2219735899272417925">مطلوب إعادة ضبط الجهاز</translation>
<translation id="2224337661447660594">الاتصال بالإنترنت مقطوع</translation>
<translation id="2230458221926704099">إصلاح الاتصال باستخدام <ph name="BEGIN_LINK" />تطبيق بيانات التشخيص<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">إرسال الآن</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">غير مسموح به (الإعداد التلقائي)</translation>
<translation id="2512413427717747692">â€Ø²Ø± ضبط Chrome كمتصÙÙ‘ÙØ­ تلقائي، انقر على Ù…Ùتاح Enter لضبط Chrome كمتصÙÙ‘ÙØ­ تلقائي للنظام ÙÙŠ إعدادات iOS</translation>
<translation id="2515629240566999685">التحقّق من اتصال الإنترنت ÙÙŠ منطقتك</translation>
+<translation id="2515761554693942801">â€Ù„قد اخترت إثبات هويتك باستخدام ميزة Touch ID على المواقع الإلكترونية التي تستخدم مقدّÙÙ… الخدمة <ph name="PROVIDER_ORIGIN" />. قد يكون مقدّÙÙ… الخدمة احتÙظ بمعلومات حول طريقة الدÙع التي تم استخدامها، ويمكنك <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">وضع دبوس أسÙÙ„ اليمين</translation>
<translation id="2521736961081452453">إنشاء نموذج</translation>
<translation id="2523886232349826891">سيتم Ø­Ùظ البطاقة على هذا الجهاز Ùقط</translation>
<translation id="2524461107774643265">إضاÙØ© مزيد من المعلومات</translation>
<translation id="2529899080962247600">يجب ألا يحتوي هذا الحقل على أكثر من <ph name="MAX_ITEMS_LIMIT" /> إدخال. وسيتم تجاهل جميع الإدخالات الأخرى.</translation>
+<translation id="253493526287553278">الاطّلاع على تÙاصيل الرمز الترويجي</translation>
<translation id="2535585790302968248">Ùتح علامة تبويب جديدة ÙÙŠ وضع التصÙّح المتخÙÙŠ للتصÙÙ‘ÙØ­ بخصوصية تامة</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{وكلمة مرور واحدة أخرى}zero{و# كلمة مرور أخرى}two{وكلمتا مرور أخريان}few{و# كلمات مرور أخرى}many{و# كلمة مرور أخرى}other{و# كلمة مرور أخرى}}</translation>
<translation id="2536110899380797252">إضاÙØ© عنوان</translation>
@@ -621,7 +628,6 @@
<translation id="259821504105826686">Ùنون رقمية وتصوير ÙوتوغراÙÙŠ</translation>
<translation id="2601150049980261779">Ø£Ùلام رومانسية</translation>
<translation id="2604589665489080024">موسيقى "بوب"</translation>
-<translation id="2609632851001447353">الاختلاÙات</translation>
<translation id="2610561535971892504">النقر لنسخ النص</translation>
<translation id="2617988307566202237">â€<ph name="BEGIN_EMPHASIS" />لن يحÙظ<ph name="END_EMPHASIS" /> Chrome المعلومات التالية:
<ph name="BEGIN_LIST" />
@@ -654,6 +660,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">أعياد الميلاد وأعياد القديسين</translation>
<translation id="2677748264148917807">الخروج</translation>
+<translation id="2679714844901977852">â€ÙŠÙ…كنك Ø­Ùظ بيانات البطاقات ومعلومات الÙوترة ÙÙŠ حسابك على Google <ph name="USER_EMAIL" /> لإتمام عمليات الدÙع بسرعة أكبر وبشكل آمن.</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Ø£Ùضل حجم</translation>
<translation id="2688969097326701645">نعم، متابعة</translation>
@@ -702,7 +709,6 @@
<translation id="2854764410992194509">â€Ù…زوّدو خدمة الإنترنت (ISP)</translation>
<translation id="2856444702002559011">قد يحاول المهاجمون سرقة معلوماتك من <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (مثل كلمات المرور أو الرسائل أو بطاقات الائتمان). <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">يعرض هذا الموقع الإلكتروني إعلانات مضلّÙلة أو غير مرغوب Ùيها.</translation>
-<translation id="286512204874376891">تÙØ®ÙÙŠ البطاقة الاÙتراضية بطاقتك الÙعلية للمساعدة على حمايتك من عمليات الاحتيال المحتمَلة. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">سهل الاستخدام</translation>
<translation id="28761159517501904">Ø£Ùلام</translation>
<translation id="2876489322757410363">ستتم مغادرة وضع التصÙÙ‘ÙØ­ المتخÙÙŠ للدÙع عبر تطبيق خارجي. هل تريد المتابعة؟</translation>
@@ -803,7 +809,6 @@
<translation id="3158539265159265653">القرص</translation>
<translation id="3162559335345991374">â€Ù‚د يتطلب Wi-Fi الذي تستخدمه زيارة صÙحة تسجيل الدخول.</translation>
<translation id="3169472444629675720">اقتراحات</translation>
-<translation id="3174168572213147020">جزيرة</translation>
<translation id="3176929007561373547">تحقق من إعدادات الخادم الوكيل أو اتصل بمشر٠الشبكة
للتأكد من عمل الخادم الوكيل. Ùإذا كنت لا تعتقد أنه يجب عليك استخدام
خادم وكيل:
@@ -882,9 +887,6 @@
<translation id="3369192424181595722">خطأ ÙÙŠ الساعة</translation>
<translation id="3369459162151165748">إكسسوارات وقطع غيار مركبات</translation>
<translation id="3371064404604898522">â€Ø¶Ø¨Ø· Chrome كمتصÙÙ‘ÙØ­ تلقائي</translation>
-<translation id="3371076217486966826">يريد <ph name="URL" /> إجراء ما يلي:
- • إنشاء خريطة ثلاثية الأبعاد للبيئة المحيطة بك وتتبّÙع موضع الكاميرا
- • استخدام الكاميرا</translation>
<translation id="337363190475750230">تم إلغاء التوÙير</translation>
<translation id="3375754925484257129">â€ØªØ´ØºÙŠÙ„ ميزة "تأكيد السلامة" ÙÙŠ Chrome</translation>
<translation id="3377144306166885718">â€ÙŠØ³ØªØ®Ø¯Ù… الخادم إصدارًا قديمًا لطبقة النقل الآمنة (TLS).</translation>
@@ -901,6 +903,7 @@
<translation id="3399952811970034796">عنوان التسليم</translation>
<translation id="3402261774528610252">â€Ø¥Ù† الاتصال المستخدَم لتحميل هذا الموقع الإلكتروني يعتمد على إصدارات متوقÙØ©ØŒ مثل TLS 1.0 أو 1.1 TLS. وسيتم إيقا٠هذه الإصدارات كليًا ÙÙŠ المستقبل. وعندما يتم إيقاÙها كليًا، سيتم منع المستخدمين من تحميل هذا الموقع الإلكتروني. على الخادم تÙعيل TLS 1.2 أو إصدار أحدث.</translation>
<translation id="3405664148539009465">تخصيص الخطوط</translation>
+<translation id="3407789382767355356">تسجيل الدخول ÙÙŠ الخدمات التابعة لجهات خارجية</translation>
<translation id="3409896703495473338">إدارة إعدادات الأمان</translation>
<translation id="3414952576877147120">الحجم:</translation>
<translation id="3417660076059365994">â€ÙŠØªÙ… إرسال الملÙات التي تلصقها أو ترÙقها إلى Google Cloud أو جهات خارجية للتحليل. على سبيل المثال، قد يتم Ùحص هذه الملÙات بحثًا عن بيانات حسّاسة أو برامج ضارة.</translation>
@@ -933,6 +936,7 @@
<translation id="3477679029130949506">قوائم Ø£Ùلام ومواعيد عروض مسرحية</translation>
<translation id="3479552764303398839">ليس الآن</translation>
<translation id="3484560055331845446">â€Ù‚د لا تتمكّن من الوصول إلى حسابك على Google. لذا ينصح Chrome بتغيير كلمة مرورك الآن. وسيÙطلَب منك تسجيل الدخول.</translation>
+<translation id="3484861421501147767">هذا تذكير بتوÙّر رمز ترويجي تم Ø­Ùظه</translation>
<translation id="3487845404393360112">الدÙرج 4</translation>
<translation id="3495081129428749620">البحث ÙÙŠ الصÙحة
<ph name="PAGE_TITLE" /></translation>
@@ -1058,6 +1062,7 @@
<translation id="3810973564298564668">إدارة</translation>
<translation id="3816482573645936981">القيمة (التي تم استبدالها)</translation>
<translation id="382518646247711829">إذا كنت تستخدم خادمًا وكيلاً...</translation>
+<translation id="3826050100957962900">تسجيل الدخول ÙÙŠ الخدمات التابعة لجهات خارجية</translation>
<translation id="3827112369919217609">صيغة مطلقة</translation>
<translation id="3827666161959873541">Ø£Ùلام عائلية</translation>
<translation id="3828924085048779000">غير مسموح باستخدام عبارة مرور Ùارغة.</translation>
@@ -1070,9 +1075,9 @@
<translation id="3858027520442213535">تحديث التاريخ والوقت</translation>
<translation id="3858860766373142691">الاسم</translation>
<translation id="3872834068356954457">علوم</translation>
+<translation id="3875783148670536197">الاطّلاع على الإرشادات</translation>
<translation id="3881478300875776315">عرض أسطر أقل</translation>
<translation id="3884278016824448484">معر٠جهاز متضارب</translation>
-<translation id="3885155851504623709">الأبرشية</translation>
<translation id="388632593194507180">تم رصد المراقبة</translation>
<translation id="3886948180919384617">المكدّÙس 3</translation>
<translation id="3890664840433101773">إضاÙØ© بريد إلكتروني</translation>
@@ -1102,9 +1107,11 @@
<translation id="3973234410852337861">تم حظر <ph name="HOST_NAME" /></translation>
<translation id="3978338123949022456">â€Ø§Ù†ØªÙ‚ÙÙ„ إلى وضع البحث وأدخÙÙ„ طلب البحث ثم اضغط على Ù…Ùتاح Enter للبحث باستخدام <ph name="KEYWORD_SUFFIX" />.</translation>
<translation id="398470910934384994">طيور</translation>
+<translation id="3985750352229496475">إدارة العناوين...</translation>
<translation id="3986705137476756801">إيقا٠ميزة "النسخ النصي التلقائي" مؤقتًا</translation>
<translation id="3987940399970879459">أقل من ميغابايت واحد</translation>
<translation id="3990250421422698716">ترتيب النسخ مع الÙصل بينها</translation>
+<translation id="3992684624889376114">لمحة حول هذه الصÙحة</translation>
<translation id="3996311196211510766">لقد طلب الموقع الإلكتروني <ph name="ORIGIN" /> تطبيق سياسة المصدر
على جميع الطلبات المقدَّمة إليه، ولكن يتعذّر تطبيق هذه السياسة حاليًا.</translation>
<translation id="4006465311664329701">â€Ø·Ø±Ù‚ الدÙع والعروض الترويجية والعناوين التي تستخدم Google Pay</translation>
@@ -1229,6 +1236,7 @@
<translation id="4305666528087210886">تعذّر الوصول لملÙÙƒ</translation>
<translation id="4306529830550717874">هل تريد Ø­Ùظ العنوان؟</translation>
<translation id="4306812610847412719">الحاÙظة</translation>
+<translation id="4310070645992025887">البحث ÙÙŠ رحلات البحث</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">حظر (تلقائي)</translation>
<translation id="4314815835985389558">إدارة المزامنة</translation>
@@ -1279,6 +1287,7 @@
<translation id="4435702339979719576">بطاقة بريدية)</translation>
<translation id="443673843213245140">تم إيقا٠استخدام الخادم الوكيل ولكن تم تحديد إعداد صريح للخادم الوكيل.</translation>
<translation id="4441832193888514600">تم التجاهل لأنّ هذه السياسة لا يمكن ضبطها كسياسة مستخدم على السحابة الإلكترونية.</translation>
+<translation id="4442470707340296952">â€Ø¹Ù„امات التبويب ÙÙŠ متصÙÙ‘ÙØ­ Chrome</translation>
<translation id="4450893287417543264">عدم الإظهار مرة أخرى</translation>
<translation id="4451135742916150903">â€Ø§Ù„سماح للموقع الإلكتروني بطلب الاتصال بأجهزة HID</translation>
<translation id="4452328064229197696">â€ÙƒÙ„مة المرور التي استخدمتها للتو تم رصدها ضمن عملية اختراق للبيانات. ولتأمين حساباتك، ينصح "مدير كلمات المرور" من Google بالتحقّق من كلمات المرور المحÙوظة.</translation>
@@ -1418,6 +1427,7 @@
<translation id="483241715238664915">تÙعيل التحذيرات</translation>
<translation id="4834250788637067901">â€Ø·Ø±Ù‚ الدÙع والعروض الترويجية والعناوين التي تستخدم Google Pay</translation>
<translation id="4838327282952368871">جذّاب</translation>
+<translation id="4839087176073128681">â€ÙŠÙ…كنك الدÙع بشكل أسرع ÙÙŠ المرة القادمة وحماية بطاقتك باستخدام ميزات الأمان الرائدة من Google.</translation>
<translation id="4840250757394056958">â€Ø¹Ø±Ø¶ سجلّ Chrome</translation>
<translation id="484462545196658690">تلقائي</translation>
<translation id="484671803914931257">الحصول على خصم على منتجات <ph name="MERCHANT_NAME" /> والمزيد</translation>
@@ -1480,6 +1490,7 @@
<translation id="5011561501798487822">لغة تم التعرّ٠عليها</translation>
<translation id="5015510746216210676">اسم الجهاز:</translation>
<translation id="5017554619425969104">النص الذي نسخته</translation>
+<translation id="5017828934289857214">تذكيري لاحقًا</translation>
<translation id="5018422839182700155">يتعذّر Ùتح هذه الصÙحة</translation>
<translation id="5019198164206649151">التخزين المساعد ÙÙŠ حالة سيئة</translation>
<translation id="5020776957610079374">موسيقى عالمية</translation>
@@ -1499,6 +1510,7 @@
<translation id="5051305769747448211">عروض كوميدية مباشرة</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{لإرسال هذا المل٠باستخدام ميزة "المشاركة عن قرب"ØŒ عليك إخلاء مساحة قدرها (<ph name="DISK_SPACE_SIZE" />) على جهازك.}zero{لإرسال هذه الملÙات باستخدام ميزة "المشاركة عن قرب"ØŒ عليك إخلاء مساحة قدرها (<ph name="DISK_SPACE_SIZE" />) على جهازك.}two{لإرسال هذين الملÙَين باستخدام ميزة "المشاركة عن قرب"ØŒ عليك إخلاء مساحة قدرها (<ph name="DISK_SPACE_SIZE" />) على جهازك.}few{لإرسال هذه الملÙات باستخدام ميزة "المشاركة عن قرب"ØŒ عليك إخلاء مساحة قدرها (<ph name="DISK_SPACE_SIZE" />) على جهازك.}many{لإرسال هذه الملÙات باستخدام ميزة "المشاركة عن قرب"ØŒ عليك إخلاء مساحة قدرها (<ph name="DISK_SPACE_SIZE" />) على جهازك.}other{لإرسال هذه الملÙات باستخدام ميزة "المشاركة عن قرب"ØŒ عليك إخلاء مساحة قدرها (<ph name="DISK_SPACE_SIZE" />) على جهازك.}}</translation>
<translation id="5056549851600133418">مقالات لك</translation>
+<translation id="5060483733937416656">â€Ù„قد اخترت إثبات هويتك باستخدام Windows Hello على المواقع الإلكترونية التي تستخدم مقدّÙÙ… الخدمة <ph name="PROVIDER_ORIGIN" />. قد يكون مقدّÙÙ… الخدمة احتÙظ بمعلومات حول طريقة الدÙع التي تم استخدامها، ويمكنك <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">هل كنت تقصد <ph name="LOOKALIKE_DOMAIN" />؟</translation>
<translation id="5066056036849835175">سجلّ الطباعة</translation>
<translation id="5068234115460527047">صناديق تحوّط</translation>
@@ -1512,10 +1524,8 @@
<translation id="5087286274860437796">شهادة الخادم ليست صالحة حاليًا.</translation>
<translation id="5087580092889165836">إضاÙØ© بطاقة</translation>
<translation id="5088142053160410913">رسالة إلى عامل التشغيل</translation>
-<translation id="5089810972385038852">الولاية</translation>
<translation id="5093232627742069661">â€Ø§Ù„طي على شكل حر٠Z</translation>
<translation id="5094747076828555589">â€Ù‡Ø°Ø§ الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />Ø› بل إنه شهادة أمان غير موثوقة من قبل Chromium. وربما يكون السبب ÙÙŠ ذلك خطأ ÙÙŠ التكوين أو مهاجمًا يعترض الاتصال.</translation>
-<translation id="5095208057601539847">المقاطعة</translation>
<translation id="5097099694988056070">â€Ø¥Ø­ØµØ§Ø¡Ø§Øª الأجهزة، مثل استخدام وحدة المعالجة المركزية (CPU)/ذاكرة الوصول العشوائي (RAM)</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">الموقع الإلكتروني غير آمن</translation>
@@ -1553,6 +1563,7 @@
<translation id="5171045022955879922">â€Ø§Ù„بحث أو إدخال عنوان URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">الجهاز</translation>
+<translation id="5177076414499237632">الاطّلاع على مصدر هذه الصÙحة وموضوعها</translation>
<translation id="5179510805599951267">هل الصÙحة ليست باللغة <ph name="ORIGINAL_LANGUAGE" />ØŸ الإبلاغ عن هذا الخطأ</translation>
<translation id="518639307526414276">طعام حيوانات أليÙØ© ومستلزمات رعايتها</translation>
<translation id="5190835502935405962">شريط الإشارات</translation>
@@ -1713,6 +1724,7 @@
<translation id="5624120631404540903">إدارة كلمات المرور</translation>
<translation id="5629630648637658800">تعذّر تحميل إعدادات السياسة</translation>
<translation id="5631439013527180824">الرمز المميز لإدارة الجهاز غير صالح</translation>
+<translation id="5632485077360054581">الاطّلاع على الإرشادات</translation>
<translation id="5633066919399395251">يمكن حاليًا للمهاجمين على <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> محاولة تثبيت برامج خطيرة على الكمبيوتر تسرق أو تحذ٠معلوماتك (على سبيل المثال، الصور، وكلمات المرور، والرسائل، وبطاقات الائتمان). <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">تم حظر المحتوى المÙضلّل.</translation>
<translation id="5633259641094592098">Ø£Ùلام مستقلة وأÙلام شعبية</translation>
@@ -1830,6 +1842,7 @@
<translation id="5989320800837274978">â€Ù„Ù… يتم تحديد أي من الخوادم الوكيلة الثابتة ولا عنوان URL للنص البرمجي pac.</translation>
<translation id="5992691462791905444">â€ØªØµÙ…يم الطي على شكل حر٠Z</translation>
<translation id="5995727681868049093">â€Ø¥Ø¯Ø§Ø±Ø© المعلومات والخصوصية والأمان ÙÙŠ حسابك على Google</translation>
+<translation id="5997247540087773573">â€Ø§Ø³ØªÙخدمت كلمة المرور التي أدخلتها للتو ÙÙŠ عملية اختراق للبيانات. لتأمين حساباتك، ينصح "مدير كلمات المرور" ÙÙŠ Google بتغيير كلمة المرور الآن ثم التحقّق من كلمات المرور المحÙوظة.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> من نتائج البحث عن "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">كلاسيكي</translation>
<translation id="6008122969617370890">طباعة الصÙحات بالترتيب المعكوس</translation>
@@ -1925,7 +1938,6 @@
<translation id="627746635834430766">â€Ù„لدÙع بشكل٠أسرع ÙÙŠ المرة القادمة، يجب Ø­Ùظ البطاقة وعنوان إرسال الÙواتير ÙÙŠ حسابك على Google.</translation>
<translation id="6279183038361895380">اضغط على |<ph name="ACCELERATOR" />| لعرض المؤشر</translation>
<translation id="6280223929691119688">لا يمكن التسليم على هذا العنوان. اختَر عنوانًا آخر.</translation>
-<translation id="6282194474023008486">الرمز البريدي</translation>
<translation id="6285507000506177184">â€Ø²Ø± إدارة عمليات التنزيل ÙÙŠ Chrome: اضغط على Ù…Ùتاح Enter لإدارة الملÙات التي تم تنزيلها ÙÙŠ Chrome</translation>
<translation id="6289939620939689042">لون الصÙحة</translation>
<translation id="6290238015253830360">ستظهر المقالات المقترحة هنا</translation>
@@ -1947,6 +1959,7 @@
<translation id="6337133576188860026">إخلاء أقل من <ph name="SIZE" />. وقد يتم تحميل بعض المواقع الإلكترونية بشكل أبطأ عند زيارتها ÙÙŠ المرة القادمة.</translation>
<translation id="6337534724793800597">تصÙية السياسات بحسب الاسم</translation>
<translation id="6340739886198108203">حسب سياسة المشرÙØŒ لا ÙŠÙنصح بأخذ لقطات شاشة أو تسجيل Ùيديوهات لمحتوى سرّي معروض على الشاشة:</translation>
+<translation id="6348220984832452017">الأنماط المتنوّعة المÙعَّلة</translation>
<translation id="6349101878882523185">تثبيت <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ما من كلمات مرور}=1{كلمة مرور واحدة (للقائمة <ph name="DOMAIN_LIST" />، تمت المزامنة)}=2{كلمتا مرور (للقائمة <ph name="DOMAIN_LIST" />، تمت المزامنة)}few{# كلمات مرور (للقائمة <ph name="DOMAIN_LIST" />، تمت المزامنة)}many{# كلمة مرور (للقائمة <ph name="DOMAIN_LIST" />، تمت المزامنة)}other{# كلمة مرور (للقائمة <ph name="DOMAIN_LIST" />، تمت المزامنة)}}</translation>
<translation id="6355392890578844978">â€Ù„ا تتم إدارة هذا المتصÙÙ‘ÙØ­ من خلال شركة أو مؤسسة أخرى. وقد تتم إدارة النشاط على هذا الجهاز خارج Chromium. <ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" /></translation>
@@ -1978,7 +1991,6 @@
<translation id="643051589346665201">â€ØªØºÙŠÙŠØ± كلمة مرور Google</translation>
<translation id="6433490469411711332">تعديل معلومات الاتصال</translation>
<translation id="6433595998831338502">رÙض <ph name="HOST_NAME" /> الاتصال.</translation>
-<translation id="6438025220577812695">تغيير كلمة المرور بنÙسي</translation>
<translation id="6440503408713884761">تم تجاهله</translation>
<translation id="6443406338865242315">الإضاÙات والمكوّنات الإضاÙية التي تثبّتها</translation>
<translation id="6446163441502663861">â€Kahu (مغلÙ)</translation>
@@ -2108,9 +2120,9 @@
<translation id="6828866289116430505">علم الوراثة</translation>
<translation id="6831043979455480757">ترجمة</translation>
<translation id="6833752742582340615">â€ÙŠÙ…كنك Ø­Ùظ معلومات البطاقة والÙوترة ÙÙŠ حسابك على Google لإتمام عمليات الدÙع بسرعة أكبر وبشكل آمن.</translation>
-<translation id="6839929833149231406">المنطقة</translation>
<translation id="6846340164947227603">استخدام رقم بطاقة اÙتراضية...</translation>
<translation id="6852204201400771460">هل تريد تحديث التطبيق؟</translation>
+<translation id="6857776781123259569">إدارة كلمات المرور...</translation>
<translation id="686485648936420384">مراجع للمستهلك</translation>
<translation id="6865412394715372076">يتعذَّر إثبات ملكية هذه البطاقة الآن</translation>
<translation id="6869334554832814367">قروض شخصية</translation>
@@ -2159,7 +2171,6 @@
<translation id="6965978654500191972">جهاز</translation>
<translation id="696703987787944103">Ø­Ùسّي</translation>
<translation id="6968269510885595029">استخدام Ù…Ùتاح الأمان</translation>
-<translation id="6970216967273061347">حي</translation>
<translation id="6971439137020188025">â€Ø¥Ù†Ø´Ø§Ø¡ عرض تقديمي جديد من Google ÙÙŠ "العروض التقديمية من Google" بسرعة</translation>
<translation id="6972629891077993081">â€Ø£Ø¬Ù‡Ø²Ø© HID</translation>
<translation id="6973656660372572881">â€ØªÙ… تحديد كل من الخوادم الوكيلة الثابتة وعنوان URL للنص البرمجي pac.</translation>
@@ -2198,7 +2209,6 @@
<translation id="7081308185095828845">لا تتوÙّر هذه الميزة على جهازك.</translation>
<translation id="7083258188081898530">الدÙرج 9</translation>
<translation id="7086090958708083563">طلب التحميل مقدّم من المستخدم</translation>
-<translation id="7087282848513945231">مقاطعة</translation>
<translation id="7095139009144195559">â€<ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />ØŒ اضغط على Ù…Ùتاح التبويب (Tab) ثم Ù…Ùتاح Enter لإدارة الأذونات والبيانات المÙخزّنة ÙÙŠ المواقع الإلكترونية من خلال إعدادات متصÙّح Chrome</translation>
<translation id="7096937462164235847">لم يتم التحقّق من هذا الموقع الإلكتروني.</translation>
<translation id="7101893872976785596">Ø£Ùلام رعب</translation>
@@ -2217,10 +2227,10 @@
<ph name="LIST_ITEM" />المعلومات التي تمّ إدخالها ÙÙŠ النماذج<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">لا يمكن الشحن على هذا العنوان. اختَر عنوانًا آخر.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">حظر المشر٠نسخ هذه البيانات.</translation>
<translation id="7135130955892390533">عرض الحالة</translation>
<translation id="7138472120740807366">طريقة التسليم</translation>
-<translation id="7139724024395191329">إمارة</translation>
<translation id="7139892792842608322">الدÙرج الأساسي</translation>
<translation id="714064300541049402">â€Ø·Ø¨Ø§Ø¹Ø© الجانب الثاني image X shift</translation>
<translation id="7152423860607593928">â€Number-14 (مغلÙ)</translation>
@@ -2437,6 +2447,7 @@
<translation id="7669271284792375604">قد يحاول المهاجمون ÙÙŠ هذا الموقع خداعك من خلال تثبيت برامج تضر بتجربة التصÙØ­ (على سبيل المثال، من خلال تغيير صÙحتك الرئيسية أو عرض إعلانات إضاÙية على المواقع التي تزورها).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{الإجراءات المÙتَّخَذة بخصوص البيانات التي يتم وضع علامة عليها على أنّها سريّة (تم رصد إجراء واحد منذ تسجيل الدخول). <ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" />}zero{الإجراءات المÙتَّخَذة بخصوص البيانات التي يتم وضع علامة عليها على أنّها سريّة (تم رصد # إجراء منذ تسجيل الدخول). <ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" />}two{الإجراءات المÙتَّخَذة بخصوص البيانات التي يتم وضع علامة عليها على أنّها سريّة (تم رصد إجراءين منذ تسجيل الدخول). <ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" />}few{الإجراءات المÙتَّخَذة بخصوص البيانات التي يتم وضع علامة عليها على أنّها سريّة (تم رصد # إجراءات منذ تسجيل الدخول). <ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" />}many{الإجراءات المÙتَّخَذة بخصوص البيانات التي يتم وضع علامة عليها على أنّها سريّة (تم رصد # إجراءً منذ تسجيل الدخول). <ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" />}other{الإجراءات المÙتَّخَذة بخصوص البيانات التي يتم وضع علامة عليها على أنّها سريّة (تم رصد # إجراء منذ تسجيل الدخول). <ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">صندوق البريد الإلكتروني 6</translation>
+<translation id="7675325315208090829">إدارة Ø·Ùرق الدÙع...</translation>
<translation id="7676643023259824263">البحث عن نص الحاÙظة،<ph name="TEXT" /></translation>
<translation id="7679367271685653708">â€Ø¹Ø±Ø¶ سجلّ التصÙÙ‘ÙØ­ وإدارته ÙÙŠ إعدادات Chrome</translation>
<translation id="7679947978757153706">بيسبول</translation>
@@ -2479,7 +2490,6 @@
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Ù†Ùس الترتيب والوجه للأعلى</translation>
-<translation id="777702478322588152">المديرية</translation>
<translation id="7791011319128895129">لم يتم إصدارها</translation>
<translation id="7791196057686275387">حزمة</translation>
<translation id="7791543448312431591">إضاÙØ©</translation>
@@ -2570,12 +2580,12 @@
<translation id="8055534648776115597">تعليم مهني وتعليم مستمر</translation>
<translation id="8057711352706143257">لم يتم إعداد "<ph name="SOFTWARE_NAME" />" بشكل صحيح. يؤدي عادةً إلغاء تثبيت "<ph name="SOFTWARE_NAME" />" إلى إصلاح المشكلة. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">إنتاج غذائي</translation>
-<translation id="8066955247577885446">عذرًا، حدث خطأ.</translation>
<translation id="8067872629359326442">â€Ù„قد أدخلت للتو كلمة مرورك ÙÙŠ موقع إلكتروني مريب. يمكن لـ Chromium مساعدتك. لتغيير كلمة مرورك وإشعار Google أن حسابك قد يكون معرّضًا للخطر، انقر على "حماية الحساب".</translation>
<translation id="8070439594494267500">رمز التطبيق</translation>
<translation id="8074253406171541171">â€10x13 (مغلÙ)</translation>
<translation id="8075736640322370409">â€Ø¥Ù†Ø´Ø§Ø¡ "جدول بيانات Google" جديد بسرعة</translation>
<translation id="8075898834294118863">إدارة إعدادات المواقع الإلكترونية</translation>
+<translation id="8076492880354921740">علامات التبويب</translation>
<translation id="8078141288243656252">لا يمكن إضاÙØ© تعليق توضيحي عند تدوير المستند</translation>
<translation id="8079031581361219619">هل تريد تحديث الموقع؟</translation>
<translation id="8081087320434522107">سيارات سيدان</translation>
@@ -2700,6 +2710,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">الإعدادات</translation>
+<translation id="8428634594422941299">حسنًا</translation>
<translation id="8431194080598727332">â€<ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />ØŒ اضغط على Ù…Ùتاح التبويب (Tab) ثم Ù…Ùتاح Enter لإدارة الإعدادات المÙضّلة لملÙات تعري٠الارتباط ÙÙŠ إعدادات متصÙّح Chrome.</translation>
<translation id="8433057134996913067">سيؤدي هذا إلى خروجك من معظم المواقع الإلكترونية.</translation>
<translation id="8434840396568290395">حيوانات أليÙØ©</translation>
@@ -2786,7 +2797,7 @@
<translation id="8719263113926255150"><ph name="ENTITY" />، <ph name="DESCRIPTION" />، اقتراح البحث</translation>
<translation id="8719528812645237045">عمل عدة ثقوب بالأعلى</translation>
<translation id="8723535127346307411">إدخال رمز التحقق</translation>
-<translation id="8725066075913043281">أعد المحاولة</translation>
+<translation id="8725066075913043281">إعادة المحاولة</translation>
<translation id="8726549941689275341">حجم الصÙحة:</translation>
<translation id="8730621377337864115">تم</translation>
<translation id="8731544501227493793">â€Ø²Ø± "إدارة كلمات المرور"ØŒ اضغط على Ù…Ùتاح Enter لعرض كلمات المرور ÙÙŠ إعدادات متصÙÙ‘ÙØ­ Chrome وإدارتها.</translation>
@@ -2798,6 +2809,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> هو الرمز للنطاق <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">وضع إشارة مرجعية لعلامة التبويب هذه</translation>
<translation id="8751426954251315517">ÙŠÙرجى إعادة المحاولة ÙÙŠ المرة القادمة.</translation>
+<translation id="8757526089434340176">â€ÙŠØªÙˆÙّر عرض Google Pay.</translation>
<translation id="8758885506338294482">ألعاب الÙيديو التناÙسية</translation>
<translation id="8759274551635299824">هذه البطاقة منتهية الصلاحية</translation>
<translation id="87601671197631245">يستخدم هذا الموقع الإلكتروني إعداد أمان غير محدَّث، وقد يكش٠ذلك عن معلوماتك (مثل كلمات المرور أو الرسائل أو بطاقات الائتمان) عند إرسالها إلى هذا الموقع الإلكتروني.</translation>
@@ -2805,6 +2817,7 @@
<translation id="8763927697961133303">â€Ø¬Ù‡Ø§Ø² USB</translation>
<translation id="8763986294015493060">إغلاق جميع النواÙØ° المÙتوحة حاليًا ÙÙŠ وضع التصÙÙ‘ÙØ­ المتخÙÙŠ</translation>
<translation id="8766943070169463815">تم Ùتح صÙحة مصادقة بيانات اعتماد الدÙع الآمن.</translation>
+<translation id="8767765348545497220">إغلاق Ùقاعة المساعدة</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">دراجات نارية</translation>
<translation id="8790007591277257123">إعادة الح&amp;Ø°Ù</translation>
@@ -2817,6 +2830,7 @@
<translation id="8806285662264631610">منتجات الاستحمام والعناية بالجسم</translation>
<translation id="8807160976559152894">القطع بعد كل صÙحة</translation>
<translation id="8808828119384186784">â€Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª Chrome</translation>
+<translation id="8813277370772331957">تذكيري لاحقًا</translation>
<translation id="8816395686387277279">â€<ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />ØŒ اضغط على Ù…Ùتاح التبويب (Tab) ثم Ù…Ùتاح Enter لتحديث Chrome من إعدادات Chrome.</translation>
<translation id="8820817407110198400">الإشارات المرجعية</translation>
<translation id="882338992931677877">الÙتحة اليدوية</translation>
@@ -2996,6 +3010,7 @@
<translation id="988159990683914416">بنية المطوّÙر</translation>
<translation id="989988560359834682">تعديل العنوان</translation>
<translation id="991413375315957741">أجهزة استشعار الإضاءة أو الحركة</translation>
+<translation id="992110854164447044">تÙØ®ÙÙŠ البطاقة الاÙتراضية بطاقتك الÙعلية للمساعدة على حمايتك من عمليات الاحتيال المحتمَلة. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">وردي</translation>
<translation id="992432478773561401">â€Ù„Ù… يتم تثبيت "<ph name="SOFTWARE_NAME" />" بطريقة صحيحة على جهاز الكمبيوتر أو الشبكة:
diff --git a/chromium/components/strings/components_strings_as.xtb b/chromium/components/strings/components_strings_as.xtb
index 698ede8ac07..a1ec5d0757f 100644
--- a/chromium/components/strings/components_strings_as.xtb
+++ b/chromium/components/strings/components_strings_as.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">অনà§à¦¦à¦¾à¦¨, জলপানি আৰৠবিতà§à¦¤à§€à§Ÿ সহায়</translation>
<translation id="1048785276086539861">আপà§à¦¨à¦¿ à¦à¦¨â€™à¦Ÿà§‡à¦¶à§à¦¬à¦¨ সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ কৰিলে, à¦à¦‡ নথিখন à¦à¦•à¦• পৃষà§à¦ à¦¾à§° ভিউলৈ সলনি হ’ব</translation>
<translation id="1050038467049342496">অনà§à¦¯ à¦à¦ªà§â€Œà¦¬à§‹à§° বাছনি কৰক</translation>
+<translation id="1053959602163383901">আপà§à¦¨à¦¿ <ph name="PROVIDER_ORIGIN" /> বà§à¦¯à§±à¦¹à¦¾à§° কৰা ৱেবছাইটসমূহত বিশà§à¦¬à¦¾à¦¸à¦¯à§‹à¦—à§à¦¯à¦¤à¦¾ পà§à§°à¦®à¦¾à¦£à§€à¦•à§°à¦£ ডিভাইচৰ জৰিয়তে সতà§à¦¯à¦¾à¦ªà¦¨ কৰিবলৈ বাছনি কৰিছে। à¦à¦‡ পà§à§°à¦¦à¦¾à¦¨à¦•à¦¾à§°à§€à¦¯à¦¼à§‡ হয়তো আপোনাৰ পৰিশোধ পদà§à¦§à¦¤à¦¿à§° বিষয়ে তথà§à¦¯ ষà§à¦Ÿâ€™à§° কৰি ৰাখিব পাৰে, যিটো আপà§à¦¨à¦¿ <ph name="LINK_TEXT" /> পাৰে।</translation>
<translation id="1055184225775184556">যোগ কৰা কারà§à¦¯à¦Ÿà§‹ &amp;আনডৠকৰক</translation>
<translation id="1056663316309890343">ফট’ৰ ছফà§à¦Ÿà§±à§‡à§°</translation>
<translation id="1056898198331236512">সতরà§à¦•à¦¬à¦¾à¦°à§à¦¤à¦¾</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">পিকআপৰ পদà§à¦§à¦¤à¦¿</translation>
<translation id="1281476433249504884">ষà§à¦Ÿà§‡à¦•à¦¾à§° ১</translation>
<translation id="1285320974508926690">à¦à¦‡ ছাইটটো কেতিয়াও অনà§à¦¬à¦¾à¦¦ নকৰিব</translation>
+<translation id="1288548991597756084">কাৰà§à¦¡ সà§à§°à¦•à§à¦·à¦¿à¦¤à¦­à¦¾à§±à§‡ ছেভ কৰক</translation>
<translation id="1292571435393770077">টà§à§°à§‡â€™ ১৬</translation>
<translation id="1292701964462482250">"আপোনাৰ কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à§°à§° ছফà§â€Œà¦Ÿà§±à§‡à§°à§°à§‡ Chromeক ৱেবৰ সৈতে সà§à§°à¦•à§à¦·à¦¿à¦¤ ভাৱে সংযà§à¦•à§à¦¤ হোৱাত বাধা পà§à§°à¦¦à¦¾à¦¨ কৰি আছে (কেৱল Windows কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à§°)</translation>
<translation id="1294154142200295408">কামাণà§à¦¡ লাইনৰ ভিনà§à¦¨à¦¤à¦¾</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;à¦à¦‡ সমসà§à¦¯à¦¾à¦Ÿà§‹ সমাধান কৰিবলৈ, আপà§à¦¨à¦¿ খà§à¦²à¦¿à¦¬à¦²à§ˆ চেষà§à¦Ÿà¦¾ কৰি থকা পৃষà§à¦ à¦¾à¦Ÿà§‹à¦¤ &lt;শকà§à¦¤à¦¿à¦¶à¦¾à¦²à§€&gt;সংযোগ কৰক&lt;/শকà§à¦¤à¦¿à¦¶à¦¾à¦²à§€&gt;-ত কà§à¦²à¦¿à¦• কৰক।&lt;/p&gt;</translation>
<translation id="1507780850870535225">লেণà§à¦¡à¦¸à§à¦•à§‡à¦‡à¦ª ডিজাইন</translation>
<translation id="1513706915089223971">ইতিহাস পà§à§°à¦¬à¦¿à¦·à§à¦Ÿà¦¿à¦¸à¦®à§‚হৰ তালিকা</translation>
+<translation id="1516097932025103760">à¦à¦‡à¦Ÿà§‹ à¦à¦¨à¦•à§à§°à¦¿à¦ªà§à¦Ÿ কৰা হ’ব, সà§à§°à¦•à§à¦·à¦¿à¦¤à¦­à¦¾à§±à§‡ ছেভ কৰা হ’ব আৰৠCVC কেতিয়াও ছেভ কৰা নহয়।</translation>
<translation id="1517433312004943670">ফ’ন নমà§à¦¬à§°à§° আৱশà§à¦¯à¦•</translation>
<translation id="1519264250979466059">বনোৱা তাৰিখ</translation>
<translation id="1521159554480556801">ফাইবাৰ আৰৠটেকà§à¦¸à¦Ÿà¦¾à¦‡à¦² আৰà§à¦Ÿ</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">পৰিশোধ পদà§à¦§à¦¤à¦¿ ছেভ কৰক আৰৠপূৰ কৰক</translation>
<translation id="1663943134801823270">কাৰà§à¦¡ আৰৠঠিকনাসমূহ Chromeৰ পৰা লোৱা হয়। আপà§à¦¨à¦¿ সেয়া <ph name="BEGIN_LINK" />ছেটিংসমূহ<ph name="END_LINK" />-ত পৰিচালনা কৰিব পাৰে।</translation>
<translation id="1671391448414634642">à¦à¦¤à¦¿à¦¯à¦¼à¦¾à§° পৰা <ph name="SOURCE_LANGUAGE" />ত থকা পৃষà§à¦ à¦¾à¦¸à¦®à§‚হ <ph name="TARGET_LANGUAGE" />লৈ অনà§à¦¬à¦¾à¦¦ কৰা হ’ব।</translation>
+<translation id="1673886523110456987">অফাৰ বà§à¦¯à§±à¦¹à¦¾à§° কৰিবলৈ <ph name="CARD_DETAIL" /> চেক আউট কৰক</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" />ৰ পৰা <ph name="TARGET_LANGUAGE" />লৈ</translation>
<translation id="1682696192498422849">চà§à¦Ÿà¦¿ কাষটো পà§à§°à¦¥à¦®à§‡</translation>
<translation id="168693727862418163">à¦à¦‡ নীতিৰ মানটোৱে ইয়াৰ সà§à¦•à§€à¦®à¦¾à§° বিৰà§à¦¦à§à¦§à§‡ সতà§à¦¯à¦¾à¦ªà¦¨ কৰিব নোৱাৰিলে আৰৠইয়াক উপেকà§à¦·à¦¾ কৰা হ’ব।</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">ম'শà§à¦¬à¦¨ ছেনà§à¦¸à§°à¦¸à¦®à§‚হ</translation>
<translation id="1717494416764505390">মেইলবকà§à¦¸ ৩</translation>
<translation id="1718029547804390981">নথিটো ইমানেই ডাঙৰ যে সেইটো à¦à¦¨â€™à¦Ÿà§‡à¦Ÿ কৰিব নোৱাৰি</translation>
+<translation id="1720941539803966190">টিউট’ৰিয়েল বনà§à¦§ কৰক</translation>
<translation id="1721424275792716183">* কà§à¦·à§‡à¦¤à§à§°à¦Ÿà§‹ আৱশà§à¦¯à¦•à§€à¦¯à¦¼</translation>
<translation id="1727613060316725209">পà§à§°à¦®à¦¾à¦£à¦ªà¦¤à§à§°à¦–ন মানà§à¦¯</translation>
<translation id="1727741090716970331">মানà§à¦¯ কাৰà§à¦¡à§° নমà§à¦¬à§° যোগ কৰক</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">বিবিকিউ আৰৠগà§à§°à¦¿à¦²à¦¿à¦‚</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> ভাষাৰ পৃষà§à¦ à¦¾à¦¸à¦®à§‚হ অনà§à¦¬à¦¾à¦¦ কৰা নহয়।</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{à¦à¦‡ নিয়নà§à¦¤à§à§°à¦£à¦Ÿà§‹ অন কৰা অৱসà§à¦¥à¦¾à¦¤ আৰৠসà§à¦¥à¦¿à¦¤à¦¿à¦Ÿà§‹ সকà§à§°à¦¿à§Ÿ হৈ থাকিলে, Chromeঠআপোনাৰ শেহতীয়া বà§à§°à¦¾à¦‰à¦œà¦¿à¦™à§° কাৰà§à¦¯à¦•à¦²à¦¾à¦ªà§° সৈতে সকলোতকৈ বেছি মিলা বহà§à¦¤à§‹ লোকৰ গোট অথবা “à¦à¦•à§‡à¦§à§°à¦£à§° বৈশিষà§à¦Ÿà§à¦¯ শà§à¦¬à§‡à§Ÿà¦¾à§° কৰা গোট†নিৰà§à¦§à¦¾à§°à¦£ কৰে। বিজà§à¦žà¦¾à¦ªà¦¨à¦¦à¦¾à¦¤à¦¾à¦‡ গোটটোৰ বাবে বিজà§à¦žà¦¾à¦ªà¦¨ বাছনি কৰিব পাৰে আৰৠআপোনাৰ ডিভাইচত আপোনাৰ বà§à§°à¦¾à¦‰à¦œà¦¿à¦™à§° কাৰà§à¦¯à¦•à¦²à¦¾à¦ª বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত কৰি ৰখা হয়। আপোনাৰ গোটটো পà§à§°à¦¤à¦¿à¦¦à¦¿à¦¨à§‡ আপডে’ট কৰা হয়।}=1{à¦à¦‡ নিয়নà§à¦¤à§à§°à¦£à¦Ÿà§‹ অন কৰা অৱসà§à¦¥à¦¾à¦¤ আৰৠসà§à¦¥à¦¿à¦¤à¦¿à¦Ÿà§‹ সকà§à§°à¦¿à§Ÿ হৈ থাকিলে, Chromeঠআপোনাৰ শেহতীয়া বà§à§°à¦¾à¦‰à¦œà¦¿à¦™à§° কাৰà§à¦¯à¦•à¦²à¦¾à¦ªà§° সৈতে সকলোতকৈ বেছি মিলা বহà§à¦¤à§‹ লোকৰ গোট অথবা “à¦à¦•à§‡à¦§à§°à¦£à§° বৈশিষà§à¦Ÿà§à¦¯ শà§à¦¬à§‡à§Ÿà¦¾à§° কৰা গোট†নিৰà§à¦§à¦¾à§°à¦£ কৰে। বিজà§à¦žà¦¾à¦ªà¦¨à¦¦à¦¾à¦¤à¦¾à¦‡ গোটটোৰ বাবে বিজà§à¦žà¦¾à¦ªà¦¨ বাছনি কৰিব পাৰে আৰৠআপোনাৰ ডিভাইচত আপোনাৰ বà§à§°à¦¾à¦‰à¦œà¦¿à¦™à§° কাৰà§à¦¯à¦•à¦²à¦¾à¦ª বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত কৰি ৰখা হয়। আপোনাৰ গোটটো পà§à§°à¦¤à¦¿à¦¦à¦¿à¦¨à§‡ আপডে’ট কৰা হয়।}one{à¦à¦‡ নিয়নà§à¦¤à§à§°à¦£à¦Ÿà§‹ অন কৰা অৱসà§à¦¥à¦¾à¦¤ আৰৠসà§à¦¥à¦¿à¦¤à¦¿à¦Ÿà§‹ সকà§à§°à¦¿à§Ÿ হৈ থাকিলে, Chromeঠআপোনাৰ শেহতীয়া বà§à§°à¦¾à¦‰à¦œà¦¿à¦™à§° কাৰà§à¦¯à¦•à¦²à¦¾à¦ªà§° সৈতে সকলোতকৈ বেছি মিলা বহà§à¦¤à§‹ লোকৰ গোট অথবা “à¦à¦•à§‡à¦§à§°à¦£à§° বৈশিষà§à¦Ÿà§à¦¯ শà§à¦¬à§‡à§Ÿà¦¾à§° কৰা গোট†নিৰà§à¦§à¦¾à§°à¦£ কৰে। বিজà§à¦žà¦¾à¦ªà¦¨à¦¦à¦¾à¦¤à¦¾à¦‡ গোটটোৰ বাবে বিজà§à¦žà¦¾à¦ªà¦¨ বাছনি কৰিব পাৰে আৰৠআপোনাৰ ডিভাইচত আপোনাৰ বà§à§°à¦¾à¦‰à¦œà¦¿à¦™à§° কাৰà§à¦¯à¦•à¦²à¦¾à¦ª বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত কৰি ৰখা হয়। আপোনাৰ গোটটো পà§à§°à¦¤à¦¿ {NUM_DAYS} দিনত আপডে’ট কৰা হয়।}other{à¦à¦‡ নিয়নà§à¦¤à§à§°à¦£à¦Ÿà§‹ অন কৰা অৱসà§à¦¥à¦¾à¦¤ আৰৠসà§à¦¥à¦¿à¦¤à¦¿à¦Ÿà§‹ সকà§à§°à¦¿à§Ÿ হৈ থাকিলে, Chromeঠআপোনাৰ শেহতীয়া বà§à§°à¦¾à¦‰à¦œà¦¿à¦™à§° কাৰà§à¦¯à¦•à¦²à¦¾à¦ªà§° সৈতে সকলোতকৈ বেছি মিলা বহà§à¦¤à§‹ লোকৰ গোট অথবা “à¦à¦•à§‡à¦§à§°à¦£à§° বৈশিষà§à¦Ÿà§à¦¯ শà§à¦¬à§‡à§Ÿà¦¾à§° কৰা গোট†নিৰà§à¦§à¦¾à§°à¦£ কৰে। বিজà§à¦žà¦¾à¦ªà¦¨à¦¦à¦¾à¦¤à¦¾à¦‡ গোটটোৰ বাবে বিজà§à¦žà¦¾à¦ªà¦¨ বাছনি কৰিব পাৰে আৰৠআপোনাৰ ডিভাইচত আপোনাৰ বà§à§°à¦¾à¦‰à¦œà¦¿à¦™à§° কাৰà§à¦¯à¦•à¦²à¦¾à¦ª বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত কৰি ৰখা হয়। আপোনাৰ গোটটো পà§à§°à¦¤à¦¿ {NUM_DAYS} দিনত আপডে’ট কৰা হয়।}}</translation>
-<translation id="2053553514270667976">পিন ক’ড</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{১টা পৰামৰà§à¦¶}one{#টা পৰামৰà§à¦¶}other{#টা পৰামৰà§à¦¶}}</translation>
+<translation id="2066915425250589881">মচাৰ বাবে অনà§à§°à§‹à¦§ জনাব</translation>
<translation id="2068528718802935086">কেà¦à¦šà§à§±à¦¾ আৰৠখোজ কাà§à¦¿à¦¬à¦²à§ˆ শিকা শিশà§</translation>
<translation id="2071156619270205202">ভাৰà§à¦šà§à§±à§‡à¦² কাৰà§à¦¡ নমà§à¦¬à§°à§° বাবে à¦à¦‡ কাৰà§à¦¡à¦–ন যোগà§à¦¯ নহয়।</translation>
<translation id="2071692954027939183">জাননীসমূহ সà§à¦¬à¦¯à¦¼à¦‚কà§à§°à¦¿à¦¯à¦¼à¦­à¦¾à§±à§‡ অৱৰোধ কৰা হৈছে কাৰণ আপà§à¦¨à¦¿ সাধাৰণতে সেইসমূহক অনà§à¦®à¦¤à¦¿ নিদিয়ে</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">ছিংক কৰা বà§à¦Ÿà¦¾à¦® পৰিচালনা কৰক, আপà§à¦¨à¦¿ Chromeৰ ছেটিঙত কি তথà§à¦¯ ছিংক কৰিব সেয়া পৰিচালনা কৰিবলৈ à¦à¦£à§à¦Ÿà¦¾à§° টিপক</translation>
<translation id="2091887806945687916">শবà§à¦¦</translation>
<translation id="2094505752054353250">ড‘মেইন অমিল</translation>
-<translation id="2096368010154057602">বিভাগ</translation>
<translation id="2099652385553570808">বাওà¦à¦«à¦¾à¦²à§‡ তিনিবাৰ ষà§à¦Ÿà§‡'পল কৰক</translation>
<translation id="2101225219012730419">সংসà§à¦•à§°à¦£:</translation>
<translation id="2102134110707549001">শকà§à¦¤à¦¿à¦¶à¦¾à¦²à§€ পাছৱৰà§à¦¡à§° পà§à§°à¦¸à§à¦¤à¦¾à§± দিয়ক…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">আমেৰিকান ফà§à¦Ÿà¦¬à¦²</translation>
<translation id="2187317261103489799">চিনাকà§à¦¤à¦•à§°à¦• (ডিফ’লà§à¦Ÿ)</translation>
<translation id="2188375229972301266">তলৰ অংশত à¦à¦•à¦¾à¦§à¦¿à¦• পাঞà§à¦š কৰক</translation>
-<translation id="2188852899391513400">আপà§à¦¨à¦¿ à¦à¦‡à¦®à¦¾à¦¤à§à§° বà§à¦¯à§±à¦¹à¦¾à§° কৰা পাছৱৰà§à¦¡à¦Ÿà§‹ à¦à¦• ডেটা উলংঘনত বিচাৰি পোৱা গৈছিল। আপোনাৰ à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿà¦¸à¦®à§‚হ সà§à§°à¦•à§à¦·à¦¿à¦¤ কৰিবলৈ Google পাছৱৰà§à¦¡ পৰিচালকে à¦à¦¤à¦¿à§Ÿà¦¾à¦‡ à¦à¦‡à¦Ÿà§‹ সলনি কৰিবলৈ আৰৠতাৰ পাছত আপোনাৰ ছেভ হৈ থকা পাছৱৰà§à¦¡à¦¸à¦®à§‚হ পৰীকà§à¦·à¦¾ কৰিবলৈ চà§à¦ªà¦¾à§°à¦¿à¦› কৰে।</translation>
<translation id="219906046732893612">ঘৰৰ উনà§à¦¨à¦¤à¦¿ সাধন</translation>
<translation id="2202020181578195191">à¦à¦Ÿà¦¾ মানà§à¦¯ মà§à¦¯à¦¾à¦¦ উকলা বছৰ দিয়ক</translation>
<translation id="22081806969704220">টà§à§°à§‡â€™ ৩</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">ফাইল সমà§à¦ªà¦¾à¦¦à¦¨à¦¾</translation>
<translation id="2215963164070968490">কà§à¦•à§à§°</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ত থকা আকà§à§°à¦®à¦£à¦•à¦¾à§°à§€à¦¸à¦•à¦²à§‡ à¦à¦¤à¦¿à§Ÿà¦¾à¦“ কà§à¦·à¦¤à¦¿à¦•à¦¾à§°à¦• à¦à¦ªà§ ইনষà§à¦Ÿà¦² কৰিব পাৰে যিবিলাকে আপোনাৰ ডিভাইচটোৰ কà§à¦·à¦¤à¦¿ কৰিব পাৰে, আপোনাৰ ম’বাইল ফ‘নৰ বিলত আপà§à¦¨à¦¿ গম নোপোৱাকৈ মাচà§à¦² যোগ কৰিব পাৰে বা আপোনাৰ বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত তথà§à¦¯ চà§à§°à¦¿ কৰিব পাৰে। <ph name="BEGIN_LEARN_MORE_LINK" />অধিক জানক<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">টিউট’ৰিয়েল ৰিষà§à¦Ÿà¦¾à§°à§à¦Ÿ কৰক</translation>
+<translation id="2219735899272417925">ডিভাইচ ৰিছেট কৰাৰ আৱশà§à¦¯à¦•</translation>
<translation id="2224337661447660594">ইণà§à¦Ÿà¦¾à§°à¦¨à§‡à¦Ÿ নাই</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />ডায়গন’ষà§à¦Ÿà¦¿à¦•à§à¦¸ à¦à¦ªà§<ph name="END_LINK" /> বà§à¦¯à§±à¦¹à¦¾à§° কৰি আপোনাৰ সংযোগ সমà§à¦ªà¦°à§à¦•à§€à§Ÿ সমসà§à¦¯à¦¾ সমাধান কৰক</translation>
<translation id="2239100178324503013">à¦à¦¤à¦¿à§Ÿà¦¾à¦‡ পঠিয়াওক</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">অনà§à¦®à¦¤à¦¿ নাই (ডিফ’লà§à¦Ÿ)</translation>
<translation id="2512413427717747692">Chromeক ডিফ’লà§à¦Ÿ বà§à§°à¦¾à¦‰à¦œà¦¾à§° বà§à¦Ÿà¦¾à¦® হিচাপে ছেট কৰক, iOS ছেটিঙত Chromeক ছিষà§à¦Ÿà§‡à¦®à§° ডিফ’লà§à¦Ÿ বà§à§°à¦¾à¦‰à¦œà¦¾à§° হিচাপে ছেট কৰিবলৈ à¦à¦£à§à¦Ÿà¦¾à§° টিপক</translation>
<translation id="2515629240566999685">আপোনাৰ অঞà§à¦šà¦²à§° ছিংগনেল পৰীকà§à¦·à¦¾ কৰি থকা হৈছে</translation>
+<translation id="2515761554693942801">আপà§à¦¨à¦¿ <ph name="PROVIDER_ORIGIN" /> বà§à¦¯à§±à¦¹à¦¾à§° কৰা ৱেবছাইটসমূহত সà§à¦ªà§°à§à¦¶ আইডিৰ জৰিয়তে সতà§à¦¯à¦¾à¦ªà¦¨ কৰিবলৈ বাছনি কৰিছে। à¦à¦‡ পà§à§°à¦¦à¦¾à¦¨à¦•à¦¾à§°à§€à¦¯à¦¼à§‡ হয়তো আপোনাৰ পৰিশোধ পদà§à¦§à¦¤à¦¿à§° বিষয়ে তথà§à¦¯ ষà§à¦Ÿâ€™à§° কৰি ৰাখিব পাৰে, যিটো আপà§à¦¨à¦¿ <ph name="LINK_TEXT" /> পাৰে।</translation>
<translation id="2521385132275182522">তলৰ অংশৰ সোà¦à¦«à¦¾à¦²à§‡ ষà§à¦Ÿà§‡'পল কৰক</translation>
<translation id="2521736961081452453">ফ’ৰà§à¦® সৃষà§à¦Ÿà¦¿ কৰক</translation>
<translation id="2523886232349826891">কেৱল à¦à¦‡ ডিভাইচটোত ছেভ কৰা হয়</translation>
<translation id="2524461107774643265">অধিক তথà§à¦¯ যোগ কৰক</translation>
<translation id="2529899080962247600">à¦à¦‡ কà§à¦·à§‡à¦¤à§à§°à¦–নত <ph name="MAX_ITEMS_LIMIT" /> টাতকৈ অধিক পà§à§°à¦¬à¦¿à¦·à§à¦Ÿà¦¿ থাকিব নালাগে। পৰৱৰà§à¦¤à§€ আটাইবোৰ পà§à§°à¦¬à¦¿à¦·à§à¦Ÿà¦¿ অৱজà§à¦žà¦¾ কৰা হ’ব।</translation>
+<translation id="253493526287553278">পà§à§°â€™à¦®â€™ ক’ডৰ সবিশেষ চাওক</translation>
<translation id="2535585790302968248">বà§à¦¯à¦•à§à¦¤à¦¿à¦—তভাৱে বà§à§°à¦¾à¦‰à¦œ কৰিবলৈ à¦à¦Ÿà¦¾ নতà§à¦¨ ইনক’গনিট’ টেব খোলক</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{আৰৠ১টা}one{আৰৠঅনà§à¦¯ #টা}other{আৰৠঅনà§à¦¯ #টা}}</translation>
<translation id="2536110899380797252">ঠিকনা যোগ কৰক</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">ফট’গà§à§°à¦¾à¦«à¦¿à¦• আৰৠডিজটেল আৰà§à¦Ÿ</translation>
<translation id="2601150049980261779">ৰমà§à¦¯ চলচà§à¦šà¦¿à¦¤à§à§°</translation>
<translation id="2604589665489080024">পপ সংগীত</translation>
-<translation id="2609632851001447353">পà§à§°à¦•à¦¾à§°à¦­à§‡à¦¦</translation>
<translation id="2610561535971892504">পà§à§°à¦¤à¦¿à¦²à¦¿à¦ªà¦¿ কৰিবলৈ কà§à¦²à¦¿à¦• কৰক</translation>
<translation id="2617988307566202237">Chromeঠতলত দিয়া তথà§à¦¯à¦¸à¦®à§‚হ <ph name="BEGIN_EMPHASIS" />ছেভ নকৰিব<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">জনà§à¦®à¦¦à¦¿à¦¨ আৰৠনাম দিয়াৰ দিন</translation>
<translation id="2677748264148917807">তà§à¦¯à¦¾à¦— কৰক</translation>
+<translation id="2679714844901977852">সà§à§°à¦•à§à¦·à¦¿à¦¤ আৰৠকà§à¦·à¦¿à¦ªà§à§°à¦­à¦¾à§±à§‡ চেক আউট কৰিবলৈ আপোনাৰ কাৰà§à¦¡ আৰৠবিলিঙৰ ঠিকনা আপোনাৰ Google à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿ <ph name="USER_EMAIL" />ত ছেভ কৰক</translation>
<translation id="2684561033061424857">১১x১২</translation>
<translation id="2687555958734450033">উতà§à¦¤à¦® মিল</translation>
<translation id="2688969097326701645">হয়, অবà§à¦¯à¦¾à¦¹à¦¤ ৰাখক</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">ইণà§à¦Ÿà¦¾à§°à¦¨à§‡à¦Ÿ সেৱা পà§à§°à¦¦à¦¾à¦¨à¦•à¦¾à§°à§€ (ISPs)</translation>
<translation id="2856444702002559011">আকà§à§°à¦®à¦£à¦•à¦¾à§°à§€à§Ÿà§‡ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ৰ পৰা আপোনাৰ তথà§à¦¯ চà§à§°à¦¿ কৰিবলৈ চেষà§à¦Ÿà¦¾ কৰি থাকিব পাৰে (উদাহৰণসà§à¦¬à§°à§‚পে, পাছৱরà§à¦¡, বারà§à¦¤à¦¾ বা কà§à§°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡)। <ph name="BEGIN_LEARN_MORE_LINK" />অধিক জানক<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">à¦à¦‡ ছাইটোৱে বিনা অনà§à¦®à¦¤à¦¿à¦¤ বা কোনো বিভà§à§°à¦¾à¦¨à§à¦¤à¦¿à¦•à§° বিজà§à¦žà¦¾à¦ªà¦¨ দেখà§à§±à¦¾à§Ÿà¥¤</translation>
-<translation id="286512204874376891">à¦à¦–ন ভাৰà§à¦›à§à§±à§‡à¦² কাৰà§à¦¡à§‡ আপোনাক সমà§à¦­à¦¾à¦¬à§à¦¯ পà§à§°à¦¤à¦¾à§°à¦£à¦¾à§° পৰা সà§à§°à¦•à§à¦·à¦¿à¦¤ কৰাত সহায় কৰিবলৈ আপোনাৰ পà§à§°à¦•à§ƒà¦¤ কাৰà§à¦¡à§° পৰিচয় লà§à¦•à§à§±à¦¾à§Ÿà¥¤ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">বনà§à¦§à§à¦¤à§à¦¬à¦¸à§à¦²à¦­</translation>
<translation id="28761159517501904">চলচà§à¦šà¦¿à¦¤à§à§°</translation>
<translation id="2876489322757410363">বাহà§à¦¯à¦¿à¦• কোনো à¦à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à§à¦¬à¦¨à§‡à§°à§‡ পৰিশোধ কৰিবলৈ ইনক’গনিট’ ম’ড à¦à§°à¦¿ আছে। অবà§à¦¯à¦¾à¦¹à¦¤ ৰাখিবনে?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ডিসà§à¦•</translation>
<translation id="3162559335345991374">আপà§à¦¨à¦¿ বà§à¦¯à§±à¦¹à¦¾à§° কৰি থকা ৱাই-ফাইটোৰ বাবে আপà§à¦¨à¦¿ à¦à¦‡à¦Ÿà§‹à§° লগ ইন পৃষà§à¦ à¦¾à¦²à§ˆ যাব লগা হ'ব পাৰে।</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">দà§à¦¬à§€à¦ª</translation>
<translation id="3176929007561373547">আপোনাৰ পà§à§°â€™à¦•à§à¦¸à¦¿ ছেটিংসমূহ পৰীকà§à¦·à¦¾ কৰক বা পà§à§°â€™à¦•à§à¦¸à¦¿ ছারà§à¦­à¦¾à§°à§‡ কাম কৰি থকাটো নিশà§à¦šà¦¿à¦¤ কৰিবলৈ নিজৰ নেটৱরà§à¦• পà§à§°à¦¶à¦¾à¦¸à¦•à§° সৈতে যোগাযোগ কৰক। যদি আপà§à¦¨à¦¿ বিশà§à¦¬à¦¾à¦¸ কৰা নাই তেনà§à¦¤à§‡ আপà§à¦¨à¦¿ কোনো পà§à§°â€™à¦•à§à¦¸à¦¿ ছারà§à¦­à¦¾à§° বà§à¦¯à§±à¦¹à¦¾à§° কৰি আছে:
<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">আপà§à¦¨à¦¿ à¦à¦‡ ডিভাইচটো সকà§à§°à¦¿à§Ÿà¦­à¦¾à§±à§‡ বà§à¦¯à§±à¦¹à¦¾à§° কৰি থকাৰ বিষয়ে জনা</translation>
@@ -879,9 +884,6 @@
<translation id="3369192424181595722">ঘড়ীৰ আসোà¦à§±à¦¾à¦¹</translation>
<translation id="3369459162151165748">বাহনৰ অংশ আৰৠআনà§à¦·à¦‚গিক সামগà§à§°à§€</translation>
<translation id="3371064404604898522">Chromeক ডিফ’লà§à¦Ÿ বà§à§°à¦¾à¦‰à¦œà¦¾à§° হিচাপে ছেট কৰক</translation>
-<translation id="3371076217486966826"><ph name="URL" />à¦:
- • আপোনাৰ ওচৰ-পাজৰৰ ঠাইসমূহৰ à¦à¦–ন 3D মেপ সৃষà§à¦Ÿà¦¿ কৰিবলৈ আৰৠকেমেৰাৰ দিশ টà§à§°à§‡à¦• কৰিবলৈ বিচাৰে
- • আপোনাৰ কেমেৰা বà§à¦¯à§±à¦¹à¦¾à§° কৰিবলৈ বিচাৰে</translation>
<translation id="337363190475750230">পà§à¦°à§±à¦¨à§à¦§à¦¨ বনà§à¦§ কৰা হৈছে</translation>
<translation id="3375754925484257129">Chromeৰ সà§à§°à¦•à§à¦·à¦¾ পৰীকà§à¦·à¦¾ চলাওক</translation>
<translation id="3377144306166885718">ছারà§à¦­à¦¾à§°à¦Ÿà§‹à§±à§‡ TLSৰ à¦à¦Ÿà¦¾ অপà§à§°à¦šà¦²à¦¿à¦¤ সংসà§à¦•à§°à¦£ বà§à¦¯à§±à¦¹à¦¾à§° কৰিছে।</translation>
@@ -898,6 +900,7 @@
<translation id="3399952811970034796">ডেলিভেৰীৰ ঠিকনা</translation>
<translation id="3402261774528610252">à¦à¦‡ ছাইটটো ল’ড কৰিবলৈ বà§à¦¯à§±à¦¹à¦¾à§° কৰা সংযোগটোৱে TLS 1.0 অথবা TLS 1.1 বà§à¦¯à§±à¦¹à¦¾à§° কৰিছে, যিবোৰ অপà§à§°à¦šà¦²à¦¿à¦¤ আৰৠভৱিষà§à¦¯à¦¤à§‡ সেইবোৰক অকà§à¦·à¦® কৰা হ’ব। অকà§à¦·à¦® কৰাৰ পাছত, বà§à¦¯à§±à¦¹à¦¾à§°à¦•à¦¾à§°à§€à¦¸à¦•à¦²à¦• à¦à¦‡ ছাইটটো ল’ড কৰিবলৈ দিয়া নহ’ব। ছারà§à¦­à¦¾à§°à¦Ÿà§‹à§±à§‡ TLS 1.2 অথবা তাতকৈ পাছৰ সংসà§à¦•à§°à¦£ সকà§à¦·à¦® কৰিব লাগে।</translation>
<translation id="3405664148539009465">ফ’ণà§à¦Ÿ কাষà§à¦Ÿà¦®à¦¾à¦‡à¦œ কৰক</translation>
+<translation id="3407789382767355356">তৃতীয় পকà§à¦·à§° ছাইন-ইন</translation>
<translation id="3409896703495473338">সà§à§°à¦•à§à¦·à¦¾à§° ছেটিং পৰিচালনা কৰক</translation>
<translation id="3414952576877147120">আকাৰ:</translation>
<translation id="3417660076059365994">আপà§à¦¨à¦¿ আপল’ড অথবা সংলগà§à¦¨ কৰা ফাইলসমূহ বিশà§à¦²à§‡à¦·à¦£à§° বাবে Google Cloud অথবা তৃতীয় পকà§à¦·à¦²à§ˆ পঠিওৱা হয়। উদাহৰণসà§à¦¬à§°à§‚পে, সেইসমূহ সংবেদনশীল ডেটা অথবা মালৱেৰৰ বাবে সà§à¦•à§‡à¦¨ কৰা হ’ব পাৰে।</translation>
@@ -930,6 +933,7 @@
<translation id="3477679029130949506">চলচà§à¦šà¦¿à¦¤à§à§°à§° সূচী আৰৠথিয়েটাৰৰ শà§à¦¬â€™à§° সময়সূচী</translation>
<translation id="3479552764303398839">à¦à¦¤à¦¿à§Ÿà¦¾ নহয়</translation>
<translation id="3484560055331845446">আপà§à¦¨à¦¿ নিজৰ Google à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿà¦²à§ˆ à¦à¦•à§à¦¸à§‡à¦› হেৰà§à§±à¦¾à¦¬ পাৰে। Chromeঠআপোনাৰ পাছৱৰà§à¦¡à¦Ÿà§‹ à¦à¦¤à¦¿à§Ÿà¦¾à¦‡ সলনি কৰাটো চà§à¦ªà¦¾à§°à¦¿à¦› কৰিছে। আপোনাক ছাইন ইন কৰিবলৈ কোৱা হ’ব।</translation>
+<translation id="3484861421501147767">ৰিমাইণà§à¦¡à¦¾à§°: ছেভ কৰি থোৱা পà§à§°à¦šà¦¾à§°à§° ক’ড উপলবà§à¦§</translation>
<translation id="3487845404393360112">টà§à§°à§‡â€™ ৪</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" />ত বিচাৰক</translation>
<translation id="350069200438440499">ফাইলৰ নাম:</translation>
@@ -1053,6 +1057,7 @@
<translation id="3810973564298564668">পৰিচালনা কৰক</translation>
<translation id="3816482573645936981">মান (অপসাৰণ কৰা হৈছে)</translation>
<translation id="382518646247711829">যদি আপà§à¦¨à¦¿ কোনো পà§à§°â€™à¦•à§à¦¸à§€ ছাৰà§à¦­à¦¾à§° বà§à¦¯à§±à¦¹à¦¾à§° কৰে...</translation>
+<translation id="3826050100957962900">তৃতীয় পকà§à¦·à§° ছাইন ইন</translation>
<translation id="3827112369919217609">চূডা়নà§à¦¤</translation>
<translation id="3827666161959873541">পাৰিবাৰিক চলচà§à¦šà¦¿à¦¤à§à§°</translation>
<translation id="3828924085048779000">খালী পাছফà§à§°à§‡à¦œà§° অনà§à¦®à¦¤à¦¿ নাই।</translation>
@@ -1065,9 +1070,9 @@
<translation id="3858027520442213535">তাৰিখ আৰৠসময় আপডে’ট কৰক</translation>
<translation id="3858860766373142691">নাম</translation>
<translation id="3872834068356954457">বিজà§à¦žà¦¾à¦¨</translation>
+<translation id="3875783148670536197">কেনেকৈ কৰে মোক দেখà§à§±à¦¾à¦“ক</translation>
<translation id="3881478300875776315">শাৰী কমকৈ দেখà§à§±à¦¾à¦“ক</translation>
<translation id="3884278016824448484">বিৰোধাতà§à¦®à¦• ডিভাইচ চিনাকà§à¦¤à¦•à¦¾à§°à§€</translation>
-<translation id="3885155851504623709">গাà¦à¦“</translation>
<translation id="388632593194507180">নিৰীকà§à¦·à¦£ কৰি থকা বà§à¦²à¦¿ চিনাকà§à¦¤ কৰা হৈছে</translation>
<translation id="3886948180919384617">ষà§à¦Ÿà§‡à¦•à¦¾à§° ৩</translation>
<translation id="3890664840433101773">ইমেইল যোগ কৰক</translation>
@@ -1097,9 +1102,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" />ক অৱৰোধ কৰা হৈছে</translation>
<translation id="3978338123949022456">সনà§à¦§à¦¾à¦¨ কৰক ম’ড, <ph name="KEYWORD_SUFFIX" />ৰ জৰিয়তে সনà§à¦§à¦¾à¦¨ কৰিবলৈ à¦à¦Ÿà¦¾ পà§à§°à¦¶à§à¦¨ টাইপ কৰক আৰৠà¦à¦£à§à¦Ÿà¦¾à§° টিপক</translation>
<translation id="398470910934384994">চৰাই</translation>
+<translation id="3985750352229496475">ঠিকনাসমূহ পৰিচালনা কৰক...</translation>
<translation id="3986705137476756801">à¦à¦¤à¦¿à§Ÿà¦¾à§° বাবে লাইভ কেপশà§à¦¬à¦¨ অফ কৰক</translation>
<translation id="3987940399970879459">১ à¦à¦®. বি.তকৈ কম</translation>
<translation id="3990250421422698716">লাহেকৈ অফছেটক হেà¦à¦šà¦•</translation>
+<translation id="3992684624889376114">à¦à¦‡ পৃষà§à¦ à¦¾à¦–নৰ বিষয়ে</translation>
<translation id="3996311196211510766">à¦à¦‡ <ph name="ORIGIN" /> ছাইটটোৱে অনà§à§°à§‹à¦§ কৰিছে যে à¦à¦Ÿà¦¾ মূল নীতি
ইয়াৰ সকলো অনà§à§°à§‹à¦§à¦¤à§‡ পà§à§°à¦¯à§‹à¦œà§à¦¯ কৰা হওক, কিনà§à¦¤à§ à¦à¦‡ নীতিটো বরà§à¦¤à¦®à¦¾à¦¨ পà§à§°à§Ÿà§‹à¦— কৰিব নোৱাৰি।</translation>
<translation id="4006465311664329701">Google Pay বà§à¦¯à§±à¦¹à¦¾à§° কৰা পৰিশোধ পদà§à¦§à¦¤à¦¿, অফাৰ আৰৠঠিকনা</translation>
@@ -1223,6 +1230,7 @@
<translation id="4305666528087210886">আপোনাৰ ফাইলটো à¦à¦•à§à¦¸à§‡à¦› কৰিব পৰা নগ’ল</translation>
<translation id="4306529830550717874">ঠিকনাটো চেভ কৰিবনে?</translation>
<translation id="4306812610847412719">কà§à¦²à¦¿à¦ªà¦¬'ৰà§à¦¡</translation>
+<translation id="4310070645992025887">আপোনাৰ জাৰà§à¦¨à§€ সনà§à¦§à¦¾à¦¨ কৰক</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">অৱৰোধ কৰক (ডিফ’লà§à¦Ÿ)</translation>
<translation id="4314815835985389558">ছিংক পৰিচালনা কৰক</translation>
@@ -1273,6 +1281,7 @@
<translation id="4435702339979719576">প’ষà§à¦Ÿà¦•à¦¾à¦°à§à¦¡)</translation>
<translation id="443673843213245140">à¦à¦Ÿà¦¾ পà§à§°à¦•à§à¦¸à¦¿à§° বà§à¦¯à§±à¦¹à¦¾à§° অকà§à¦·à¦® কৰা হৈছে কিনà§à¦¤à§ à¦à¦Ÿà¦¾ মà§à¦–à§à¦¯ পà§à§°à¦•à§à¦¸à¦¿à§° কনফিগাৰেশà§à¦¬à¦¨ নিৰà§à¦¦à¦¿à¦·à§à¦Ÿ কৰা হৈছে।</translation>
<translation id="4441832193888514600">নীতিটো কেৱল কà§à¦²à¦¾à¦‰à¦¡ বà§à¦¯à§±à¦¹à¦¾à§°à¦•à¦¾à§°à§€à§° নীতি হিচাপে ছেট কৰিব পাৰে বাবে উপেকà§à¦·à¦¾ কৰা হৈছে।</translation>
+<translation id="4442470707340296952">Chromeৰ টেব</translation>
<translation id="4450893287417543264">পà§à¦¨à§° নেদেখà§à§±à¦¾à¦¬</translation>
<translation id="4451135742916150903">HID ডিভাইচৰ সৈতে সংযোগ কৰিবলৈ বিচাৰিব পাৰে</translation>
<translation id="4452328064229197696">আপà§à¦¨à¦¿ à¦à¦‡à¦®à¦¾à¦¤à§à§° বà§à¦¯à§±à¦¹à¦¾à§° কৰা পাছৱৰà§à¦¡à¦Ÿà§‹ à¦à¦• ডেটা উলংঘনত বিচাৰি পোৱা গৈছিল। আপোনাৰ à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿà¦¸à¦®à§‚হ সà§à§°à¦•à§à¦·à¦¿à¦¤ কৰিবলৈ Google পাছৱৰà§à¦¡ পৰিচালকে আপোনাৰ ছেভ হৈ থকা পাছৱৰà§à¦¡à¦¸à¦®à§‚হ পৰীকà§à¦·à¦¾ কৰিবলৈ চà§à¦ªà¦¾à§°à¦¿à¦› কৰে।</translation>
@@ -1411,6 +1420,7 @@
<translation id="483241715238664915">সকীয়নি অন কৰক</translation>
<translation id="4834250788637067901">Google Pay বà§à¦¯à§±à¦¹à¦¾à§° কৰা পৰিশোধ পদà§à¦§à¦¤à¦¿, অফাৰ আৰৠঠিকনা</translation>
<translation id="4838327282952368871">সà§à¦¬à¦ªà§à¦¨à¦®à¦¯à¦¼</translation>
+<translation id="4839087176073128681">পৰৱৰà§à¦¤à§€ বাৰ দà§à§°à§à¦¤à¦­à¦¾à§±à§‡ পৰিশোধ কৰক আৰৠGoogleঠআগবà§à§‹à§±à¦¾ উদà§à¦¯à§‹à¦—ৰ নেতৃসà§à¦¥à¦¾à¦¨à§€à§Ÿ সà§à§°à¦•à§à¦·à¦¾à§° জৰিয়তে আপোনাৰ কাৰà§à¦¡ সà§à§°à¦•à§à¦·à¦¿à¦¤ কৰক।</translation>
<translation id="4840250757394056958">আপোনাৰ Chromeৰ ইতিহাস চাওক</translation>
<translation id="484462545196658690">সà§à¦¬à§Ÿà¦‚কà§à§°à¦¿à§Ÿ</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> আৰৠবহà§à¦¤à¦¤ ৰেহাই পাওক</translation>
@@ -1473,6 +1483,7 @@
<translation id="5011561501798487822">চিনাকà§à¦¤ কৰা ভাষা</translation>
<translation id="5015510746216210676">মেছিনৰ নাম:</translation>
<translation id="5017554619425969104">আপà§à¦¨à¦¿ পà§à§°à¦¤à¦¿à¦²à¦¿à¦ªà¦¿ কৰা পাঠ</translation>
+<translation id="5017828934289857214">মোক পাছত মনত পেলাই দিয়ক</translation>
<translation id="5018422839182700155">à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà§‹ খà§à¦²à¦¿à¦¬ নোৱাৰি</translation>
<translation id="5019198164206649151">বেকিং ষà§à¦Ÿ'ৰ বেয়া অৱসà§à¦¥à¦¾à¦¤ আছে</translation>
<translation id="5020776957610079374">বিশà§à¦¬ সংগীত</translation>
@@ -1492,6 +1503,7 @@
<translation id="5051305769747448211">লাইভ কমেডী</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Nearby Share বà§à¦¯à§±à¦¹à¦¾à§° কৰি à¦à¦‡ ফাইলটো পঠিয়াবলৈ, আপোনাৰ ডিভাইচত ঠাই (<ph name="DISK_SPACE_SIZE" />) খালী কৰক}one{Nearby Share বà§à¦¯à§±à¦¹à¦¾à§° কৰি à¦à¦‡ ফাইলসমূহ পঠিয়াবলৈ, আপোনাৰ ডিভাইচত ঠাই (<ph name="DISK_SPACE_SIZE" />) খালী কৰক}other{Nearby Share বà§à¦¯à§±à¦¹à¦¾à§° কৰি à¦à¦‡ ফাইলসমূহ পঠিয়াবলৈ, আপোনাৰ ডিভাইচত ঠাই (<ph name="DISK_SPACE_SIZE" />) খালী কৰক}}</translation>
<translation id="5056549851600133418">আপোনাৰ বাবে লেখনি</translation>
+<translation id="5060483733937416656">আপà§à¦¨à¦¿ <ph name="PROVIDER_ORIGIN" /> বà§à¦¯à§±à¦¹à¦¾à§° কৰা ৱেবছাইটসমূহত Windows Helloৰ জৰিয়তে সতà§à¦¯à¦¾à¦ªà¦¨ কৰিবলৈ বাছনি কৰিছে। à¦à¦‡ পà§à§°à¦¦à¦¾à¦¨à¦•à¦¾à§°à§€à¦¯à¦¼à§‡ হয়তো আপোনাৰ পৰিশোধ পদà§à¦§à¦¤à¦¿à§° বিষয়ে তথà§à¦¯ ষà§à¦Ÿâ€™à§° কৰি ৰাখিব পাৰে, যিটো আপà§à¦¨à¦¿ <ph name="LINK_TEXT" /> পাৰে।</translation>
<translation id="5061227663725596739">আপà§à¦¨à¦¿ <ph name="LOOKALIKE_DOMAIN" />ৰ কথা বà§à¦œà¦¾à¦¬ বিচাৰিছিল নেকি?</translation>
<translation id="5066056036849835175">পà§à§°à¦¿à¦£à§à¦Ÿ কৰাৰ ইতিহাস</translation>
<translation id="5068234115460527047">হে’জৠপà§à¦à¦œà¦¿</translation>
@@ -1505,10 +1517,8 @@
<translation id="5087286274860437796">à¦à¦‡ মà§à¦¹à§‚ৰà§à¦¤à¦¤ ছাৰà§à¦­à¦¾à§°à§° পà§à§°à¦®à¦¾à¦£à¦ªà¦¤à§à§°à¦–ন অমানà§à¦¯à¥¤</translation>
<translation id="5087580092889165836">কাৰà§à¦¡ যোগ কৰক</translation>
<translation id="5088142053160410913">অপাৰেটৰলৈ বাৰà§à¦¤à¦¾</translation>
-<translation id="5089810972385038852">ৰাজà§à¦¯</translation>
<translation id="5093232627742069661">Z-ফ'লà§à¦¡</translation>
<translation id="5094747076828555589">à¦à¦‡ ছাৰà§à¦­à¦¾à§°à¦Ÿà§‹à§±à§‡ à¦à¦¯à¦¼à¦¾ <ph name="DOMAIN" /> বà§à¦²à¦¿ পà§à§°à¦®à¦¾à¦£ কৰিব নোৱাৰিলে; ইয়াৰ সà§à§°à¦•à§à¦·à¦¾ সমà§à¦ªà§°à§à¦•à§€à¦¯à¦¼ পà§à§°à¦®à¦¾à¦£à¦ªà¦¤à§à§° Chromiumঠবিশà§à¦¬à¦¾à¦¸ নকৰে। à¦à§Ÿà¦¾ কোনো ভà§à¦² কনফিগাৰেশà§à¦¬à¦¨à§° বাবে বা কোনো আকà§à§°à¦®à¦£à¦•à¦¾à§°à§€à§Ÿà§‡ আপোনাৰ সংযোগ অৱৰোধ কৰাৰ বাবে হ’ব পাৰে।</translation>
-<translation id="5095208057601539847">পà§à§°à¦¦à§‡à¦¶</translation>
<translation id="5097099694988056070">PU/RAMৰ বà§à¦¯à§±à¦¹à¦¾à§°à§° দৰে ডিভাইচৰ পৰিসংখà§à¦¯à¦¾</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">ছাইটটো সà§à§°à¦•à§à¦·à¦¿à¦¤ নহয়</translation>
@@ -1546,6 +1556,7 @@
<translation id="5171045022955879922">সনà§à¦§à¦¾à¦¨ কৰক বা URL টাইপ কৰক</translation>
<translation id="5171689220826475070">ফেনফ’লà§à¦¡-ইউৰোপীয়</translation>
<translation id="5172758083709347301">মেচিন</translation>
+<translation id="5177076414499237632">à¦à¦‡ পৃষà§à¦ à¦¾à¦–নৰ উৎস আৰৠবিষয়বসà§à¦¤à§à§° বিষয়ে জানক</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />ত নাই নেকি? à¦à¦‡ আসোà¦à§±à¦¾à¦¹à¦Ÿà§‹à§° অভিযোগ দিয়ক</translation>
<translation id="518639307526414276">পোহনীয়া জীৱ-জনà§à¦¤à§à§° খাদà§à¦¯ আৰৠসেইবোৰৰ যতà§à¦¨ লোৱাৰ বাবে আৱশà§à¦¯à¦•à§€à§Ÿ সামগà§à§°à§€</translation>
<translation id="5190835502935405962">বà§à¦•à¦®à¦¾à§°à§à¦•à§° বাৰ</translation>
@@ -1706,6 +1717,7 @@
<translation id="5624120631404540903">পাছৱৰà§à¦¡ সলনি কৰক</translation>
<translation id="5629630648637658800">নীতিৰ ছেটিংসমূহ ল’ড কৰিব পৰা নগ’ল</translation>
<translation id="5631439013527180824">অমানà§à¦¯ ডিভাইচ পৰিচালনাৰ ট’কেন</translation>
+<translation id="5632485077360054581">কেনেকৈ কৰে মোক দেখà§à§±à¦¾à¦“ক</translation>
<translation id="5633066919399395251">বরà§à¦¤à¦®à¦¾à¦¨ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ত থকা আকà§à§°à¦®à¦£à¦•à¦¾à§°à§€à§Ÿà§‡ আপোনাৰ কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à§°à¦¤ কিছà§à¦®à¦¾à¦¨ কà§à¦·à¦¤à¦¿à¦•à¦¾à§°à¦• পà§à¦°â€™à¦—à§à§°à§‡à¦® ইনষà§à¦Ÿà¦² কৰিবলৈ চেষà§à¦Ÿà¦¾ কৰিব পাৰে ,যিবোৰে আপোনাৰ তথà§à¦¯ (উদাহৰণ সà§à¦¬à§°à§‚পে ফট’, পাছৱৰà§à¦¡, বাৰà§à¦¤à¦¾ আৰৠকà§à§°à§‡à¦¡à¦¿à¦Ÿ কাৰà§à¦¡à¦¸à¦®à§‚হ) চà§à§°à¦¿ কৰিব বা মচিব পাৰে। <ph name="BEGIN_LEARN_MORE_LINK" />অধিক জানক<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">বিভà§à§°à¦¾à¦¨à§à¦¤à¦¿à¦•à§° সমল অৱৰোধ কৰা হৈছে।</translation>
<translation id="5633259641094592098">কালà§à¦Ÿ আৰৠইণà§à¦¡à§€ চলচà§à¦šà¦¿à¦¤à§à§°</translation>
@@ -1823,6 +1835,7 @@
<translation id="5989320800837274978">কোনো সà§à¦¥à¦¿à§° পà§à§°â€™à¦•à§à¦¸à§€ ছাৰà§à¦­à¦¾à§° নাইবা কোনো .pac সà§à¦•à§à§°à¦¿à¦ªà§à¦Ÿ URL নিৰà§à¦¦à¦¿à¦·à§à¦Ÿ কৰা নাই৷</translation>
<translation id="5992691462791905444">অভিযানà§à¦¤à§à¦°à¦¿à¦• Z-ফ'লà§à¦¡</translation>
<translation id="5995727681868049093">আপোনাৰ Google à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿà¦¤ আপোনাৰ তথà§à¦¯, গোপনীয়তা আৰৠসà§à§°à¦•à§à¦·à¦¾ পৰিচালনা কৰক</translation>
+<translation id="5997247540087773573">আপà§à¦¨à¦¿ à¦à¦‡à¦®à¦¾à¦¤à§à§° বà§à¦¯à§±à¦¹à¦¾à§° কৰা পাছৱৰà§à¦¡à¦Ÿà§‹ à¦à¦• ডেটা উলংঘনত বিচাৰি পোৱা গৈছে। আপোনাৰ à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿà¦¸à¦®à§‚হ সà§à§°à¦•à§à¦·à¦¿à¦¤ কৰিবলৈ Google পাছৱৰà§à¦¡ পৰিচালকে à¦à¦¤à¦¿à§Ÿà¦¾à¦‡ à¦à¦‡à¦Ÿà§‹ সলনি কৰি আপোনাৰ ছেভ হৈ থকা পাছৱৰà§à¦¡à¦¸à¦®à§‚হ পৰীকà§à¦·à¦¾ কৰিবলৈ চà§à¦ªà¦¾à§°à¦¿à¦› কৰে।</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />'ৰ বাবে <ph name="RESULT_COUNT" />টা ফলাফল</translation>
<translation id="6006484371116297560">ধà§à§°à§‚পদী</translation>
<translation id="6008122969617370890">বহà§à¦¤à§‹à§° পৰা à¦à¦Ÿà¦¾ কà§à§°à¦®</translation>
@@ -1917,7 +1930,6 @@
<translation id="627746635834430766">পৰৱৰà§à¦¤à§€ সময়ত দà§à§°à§à¦¤à¦­à¦¾à§±à§‡ পৰিশোধ কৰিবলৈ আপোনাৰ কাৰà§à¦¡ আৰৠবিলিঙৰ ঠিকনা নিজৰ Google à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿà¦¤ ছেভ কৰক।</translation>
<translation id="6279183038361895380">আপোনাৰ কাৰà§à¦›à§°à¦Ÿà§‹ দেখà§à§±à¦¾à¦¬à¦²à§ˆ |<ph name="ACCELERATOR" />| টিপক</translation>
<translation id="6280223929691119688">à¦à¦‡ ঠিকনাটোত পঠিয়াব নোৱাৰি৷ অনà§à¦¯ à¦à¦Ÿà¦¾ ঠিকনা বাছনি কৰক।</translation>
-<translation id="6282194474023008486">পিন ক'ড</translation>
<translation id="6285507000506177184">Chrome বà§à¦Ÿà¦¾à¦®à¦¤ ডাউনল’ড পৰিচালনা কৰক, আপà§à¦¨à¦¿ Chromeৰ জৰিয়তে ডাউনল’ড কৰা ফাইল পৰিচালনা কৰিবলৈ à¦à¦£à§à¦Ÿà¦¾à§° টিপক</translation>
<translation id="6289939620939689042">পৃষà§à¦ à¦¾à§° ৰং</translation>
<translation id="6290238015253830360">আপà§à¦¨à¦¿ পৰামৰà§à¦¶ দিয়া পà§à§°à¦¬à¦¨à§à¦§à¦¬à§‹à§° ইয়াত দেখা যাব</translation>
@@ -1939,6 +1951,7 @@
<translation id="6337133576188860026"><ph name="SIZE" />তকৈ কম খালী ঠাই উলিয়াওক। আপà§à¦¨à¦¿ পৰৱরà§à¦¤à§€ সময়ত কিছà§à¦®à¦¾à¦¨ ছাইট চাওতে লেহেমীয়াকৈ ল’ড হ’ব পাৰে।</translation>
<translation id="6337534724793800597">নাম অনà§à¦¸à§°à¦¿ নীতিসমূহ ফিলà§à¦Ÿà¦¾à§° কৰক</translation>
<translation id="6340739886198108203">পà§à§°à¦¶à¦¾à¦¸à¦•à§° নীতিয়ে গোপনীয় সমল দেখা পোৱা হৈ থকাৰ সময়ত সà§à¦•à§à§°à§€à¦¨à¦¶à§à¦¬à¦Ÿ লোৱা অথবা ৰেকৰà§à¦¡à¦¿à¦‚ কৰাটো চà§à¦ªà¦¾à§°à¦¿à¦› নকৰে:</translation>
+<translation id="6348220984832452017">ভিনà§à¦¨à¦¤à¦¾ সকà§à§°à¦¿à§Ÿ কৰক</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ইনষà§à¦Ÿà¦² কৰক</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{à¦à¦Ÿà¦¾à¦“ নাই}=1{১টা পাছৱৰà§à¦¡ (<ph name="DOMAIN_LIST" />ৰ বাবে, ছিংক কৰা আছে)}=2{২টা পাছৱৰà§à¦¡ (<ph name="DOMAIN_LIST" />ৰ বাবে, ছিংক কৰা আছে)}one{#টা পাছৱৰà§à¦¡ (<ph name="DOMAIN_LIST" />ৰ বাবে, ছিংক কৰা আছে)}other{#টা পাছৱৰà§à¦¡ (<ph name="DOMAIN_LIST" />ৰ বাবে, ছিংক কৰা আছে)}}</translation>
<translation id="6355392890578844978">à¦à¦‡ বà§à§°à¦¾à¦‰à¦œà¦¾à§°à¦Ÿà§‹ কোনো কোমà§à¦ªà¦¾à¦¨à§€ অথবা অনà§à¦¯ পà§à§°à¦¤à¦¿à¦·à§à¦ à¦¾à¦¨à§° দà§à¦¬à¦¾à§°à¦¾ পৰিচালিত। à¦à¦‡ ডিভাইচটোৰ কাৰà§à¦¯à¦•à¦²à¦¾à¦ª Chromiumৰ বাহিৰত পৰিচালনা কৰা হৈ থাকিব পাৰে। <ph name="BEGIN_LINK" />অধিক জানক<ph name="END_LINK" /></translation>
@@ -1970,7 +1983,6 @@
<translation id="643051589346665201">Googleৰ পাছৱৰà§à¦¡ সলনি কৰক</translation>
<translation id="6433490469411711332">সমà§à¦ªà¦°à§à¦•à§° তথà§à¦¯ সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ কৰক</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" />ঠসংযোগ কৰিবলৈ অসà§à¦¬à§€à¦•à¦¾à§° কৰিছে।</translation>
-<translation id="6438025220577812695">à¦à¦‡à¦Ÿà§‹ মই নিজেই ঠিক কৰিম</translation>
<translation id="6440503408713884761">উপেকà§à¦·à¦¿à¦¤ কৰা হৈছে</translation>
<translation id="6443406338865242315">আপà§à¦¨à¦¿ ইনষà§à¦Ÿà¦² কৰা à¦à¦•à§à¦¸à¦Ÿà§‡à¦¨à¦¶à§à¦¬à¦¨ আৰৠপà§à¦²à¦¾à¦—ইনসমূহ</translation>
<translation id="6446163441502663861">কাহৠ(লেফাফা)</translation>
@@ -2100,9 +2112,9 @@
<translation id="6828866289116430505">আনà§à¦¬à¦‚শিক বিজà§à¦žà¦¾à¦¨</translation>
<translation id="6831043979455480757">Translate</translation>
<translation id="6833752742582340615">সà§à§°à¦•à§à¦·à¦¿à¦¤ আৰৠকà§à¦·à¦¿à¦ªà§à§°à¦­à¦¾à§±à§‡ চেক আউট কৰিবলৈ আপোনাৰ কাৰà§à¦¡ আৰৠবিলিঙৰ ঠিকনা আপোনাৰ Google à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿà¦¤ ছেভ কৰক</translation>
-<translation id="6839929833149231406">কà§à¦·à§‡à¦¤à§à§°</translation>
<translation id="6846340164947227603">à¦à¦Ÿà¦¾ ভাৰà§à¦šà§à§±à§‡à¦² কাৰà§à¦¡ নমà§à¦¬à§° বà§à¦¯à§±à¦¹à¦¾à§° কৰক...</translation>
<translation id="6852204201400771460">à¦à¦ªà§ পà§à¦¨à§° ল’ড কৰিবনে?</translation>
+<translation id="6857776781123259569">পাছৱৰà§à¦¡à¦¸à¦®à§‚হ পৰিচালনা কৰক...</translation>
<translation id="686485648936420384">গà§à§°à¦¾à¦¹à¦•à§° উৎস</translation>
<translation id="6865412394715372076">à¦à¦‡ কারà§à¦¡à¦–ন বৰà§à¦¤à¦®à¦¾à¦¨ সতà§à¦¯à¦¾à¦ªà¦¨ কৰিব নোৱাৰি</translation>
<translation id="6869334554832814367">বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত ঋণ</translation>
@@ -2151,7 +2163,6 @@
<translation id="6965978654500191972">ডিভাইচ</translation>
<translation id="696703987787944103">পাৰà§à¦šà§‡à¦ªà¦¶à§à¦¬à§à§±à§‡à¦²</translation>
<translation id="6968269510885595029">আপোনাৰ সà§à§°à¦•à§à¦·à¦¾ সমà§à¦ªà§°à§à¦•à§€à§Ÿ চাবিটো বà§à¦¯à§±à¦¹à¦¾à§° কৰক</translation>
-<translation id="6970216967273061347">জিলা</translation>
<translation id="6971439137020188025">Slidesত কà§à¦·à¦¿à¦ªà§à§°à¦¤à¦¾à§°à§‡ à¦à¦Ÿà¦¾ নতà§à¦¨ Google উপসà§à¦¥à¦¾à¦ªà¦¨ সৃষà§à¦Ÿà¦¿ কৰক</translation>
<translation id="6972629891077993081">HID ডিভাইচসমূহ</translation>
<translation id="6973656660372572881">সà§à¦¥à¦¿à§° পà§à§°â€™à¦•à§à¦¸à¦¿ ছারà§à¦­à¦¾à§° আৰৠ.pac সà§à¦•à§à§°à¦¿à¦ªà§à¦Ÿà§° URL দà§à§Ÿà§‹à¦Ÿà¦¾à¦•à§‡ নিরà§à¦¦à¦¿à¦·à§à¦Ÿ কৰা হৈছে।</translation>
@@ -2190,7 +2201,6 @@
<translation id="7081308185095828845">à¦à¦‡ সà§à¦¬à¦¿à¦§à¦¾à¦Ÿà§‹ আপোনাৰ ডিভাইচটোত উপলবà§à¦§ নহয়</translation>
<translation id="7083258188081898530">টà§à§°à§‡â€™ ৯</translation>
<translation id="7086090958708083563">বà§à¦¯à§±à¦¹à¦¾à§°à¦•à¦¾à§°à§€à§Ÿà§‡ অনà§à§°à§‹à¦§ কৰা আপল'ড</translation>
-<translation id="7087282848513945231">জিলা</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chromeৰ ছেটিঙত সমগà§à§° ছাইটত ষà§à¦Ÿâ€™à§° কৰি ৰখা ডেটা আৰৠঅনà§à¦®à¦¤à¦¿ পৰিচালনা কৰিবলৈ à¦à¦£à§à¦Ÿà¦¾à§° টিপক</translation>
<translation id="7096937462164235847">à¦à¦‡ ৱেবছাইটটোৰ পৰিচয় সতà§à¦¯à¦¾à¦ªà¦¨ কৰা হোৱা নাই।</translation>
<translation id="7101893872976785596">ভৌতিক চলচà§à¦šà¦¿à¦¤à§à§°</translation>
@@ -2209,10 +2219,10 @@
<ph name="LIST_ITEM" />ফ’ৰà§à¦®à¦¤ দিয়া তথà§à¦¯<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">à¦à¦‡ ঠিকনালৈ পঠিয়াব নোৱাৰি। অনà§à¦¯ à¦à¦Ÿà¦¾ ঠিকনা বাছনি কৰক।</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">আপোনাৰ পà§à§°à¦¶à¦¾à¦¸à¦•à§‡ à¦à¦‡à¦–িনি ডেটা পà§à§°à¦¤à¦¿à¦²à¦¿à¦ªà¦¿ কৰাটো নিষিদà§à¦§ কৰি থৈছে।</translation>
<translation id="7135130955892390533">সà§à¦¥à¦¿à¦¤à¦¿ দেখà§à§±à¦¾à¦“ক</translation>
<translation id="7138472120740807366">ডেলিভাৰীৰ পদà§à¦§à¦¤à¦¿</translation>
-<translation id="7139724024395191329">à¦à¦®à¦¿à§°à§‡'ট</translation>
<translation id="7139892792842608322">পà§à§°à¦¾à¦¥à¦®à¦¿à¦• টà§à§°à§‡â€™</translation>
<translation id="714064300541049402">কাষৰ ২ পà§à§°à¦¤à¦¿à¦šà§à¦›à¦¬à¦¿ X শà§à¦¬à¦¿à¦«à§à¦Ÿ</translation>
<translation id="7152423860607593928">সংখà§à¦¯à¦¾-১৪ (লেফাফা)</translation>
@@ -2429,6 +2439,7 @@
<translation id="7669271284792375604">à¦à¦‡ ছাইটৰ আকà§à§°à¦®à¦£à¦•à¦¾à§°à§€à§Ÿà§‡ আপোনাক ছল-চাতà§à§°à¦¿à§°à§‡ কিছà§à¦®à¦¾à¦¨ পà§à§°â€™à¦—à§à§°à¦¾à¦® ইনষà§à¦Ÿà¦² কৰাবলৈ বাধà§à¦¯ কৰাব পাৰে, যিবোৰে আপোনাৰ বà§à§°à¦¾à¦‰à¦œà¦¿à¦‚ অভিজà§à¦žà¦¤à¦¾ কà§à¦·à¦¤à¦¿à¦—à§à§°à¦¸à§à¦¥ কৰিব পাৰে (উদাহৰণসà§à¦¬à§°à§‚পে, আপোনাৰ গৃহপৃষà§à¦ à¦¾ সলনি কৰি বা আপà§à¦¨à¦¿ চোৱা ছাইটত অধিক বিজà§à¦žà¦¾à¦ªà¦¨ পà§à§°à¦¦à¦°à§à¦¶à¦¨ কৰি)।</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{গোপনীয় হিচাপে ফà§à¦²à§‡à¦— কৰা ডেটা সমà§à¦ªà§°à§à¦•à§‡ কৰা কাৰà§à¦¯ (লগইন কৰাৰ পৰা ১ টা কাৰà§à¦¯ কৰা হৈছে)। <ph name="BEGIN_LINK" />অধিক জানক<ph name="END_LINK" />}one{গোপনীয় হিচাপে ফà§à¦²à§‡à¦— কৰা ডেটা সমà§à¦ªà§°à§à¦•à§‡ কৰা কাৰà§à¦¯ (লগইন কৰাৰ পৰা # টা কাৰà§à¦¯ কৰা হৈছে)। <ph name="BEGIN_LINK" />অধিক জানক<ph name="END_LINK" />}other{গোপনীয় হিচাপে ফà§à¦²à§‡à¦— কৰা ডেটা সমà§à¦ªà§°à§à¦•à§‡ কৰা কাৰà§à¦¯ (লগইন কৰাৰ পৰা # টা কাৰà§à¦¯ কৰা হৈছে)। <ph name="BEGIN_LINK" />অধিক জানক<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">মেইলবকà§à¦¸ ৬</translation>
+<translation id="7675325315208090829">পৰিশোধৰ পদà§à¦§à¦¤à¦¿à¦¸à¦®à§‚হ পৰিচালনা কৰক...</translation>
<translation id="7676643023259824263">কà§à¦²à¦¿à¦ªà¦¬â€™à§°à§à¦¡à§° পাঠ, <ph name="TEXT" /> সনà§à¦§à¦¾à¦¨ কৰক</translation>
<translation id="7679367271685653708">Chromeৰ ছেটিঙত আপোনাৰ বà§à§°à¦¾à¦‰à¦œà¦¿à¦™à§° ইতিহাস চাওক আৰৠপৰিচালনা কৰক</translation>
<translation id="7679947978757153706">বেছবল</translation>
@@ -2471,7 +2482,6 @@
<translation id="7766518757692125295">সà§à¦•à¦¾à§°à§à¦Ÿ</translation>
<translation id="7770259615151589601">নিৰà§à¦¦à¦¿à¦·à§à¦Ÿ-দৈৰà§à¦˜à§à¦¯</translation>
<translation id="7773005668374414287">ওপৰমà§à¦–ীয়াকৈ à¦à¦•à§‡à¦‡ কà§à§°à¦®</translation>
-<translation id="777702478322588152">জিলাৰ মà§à§°à¦¬à§à¦¬à§€</translation>
<translation id="7791011319128895129">ৰিলীজ নকৰা</translation>
<translation id="7791196057686275387">à¦à¦•à¦—োট কৰক</translation>
<translation id="7791543448312431591">যোগ কৰক</translation>
@@ -2562,12 +2572,12 @@
<translation id="8055534648776115597">বৃতà§à¦¤à¦¿ সমà§à¦ªà§°à§à¦•à§€à§Ÿ শিকà§à¦·à¦¾ আৰৠআধৰà§à§±à¦¾ শিকà§à¦·à¦¾ পà§à¦¨à§° আৰমà§à¦­ কৰা</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />"ক সঠিককৈ কনফিগাৰ কৰা হোৱা নাই। "<ph name="SOFTWARE_NAME" />" আনইনষà§à¦Ÿà¦² কৰিলে সাধাৰণতে সমসà§à¦¯à¦¾à¦Ÿà§‹ সমাধান কৰে। <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">খাদà§à¦¯ বসà§à¦¤à§ পà§à§°à¦¸à§à¦¤à§à¦¤à¦•à§°à¦£</translation>
-<translation id="8066955247577885446">দà§à¦ƒà¦–িত, কিবা ভà§à¦² হ'ল।</translation>
<translation id="8067872629359326442">আপà§à¦¨à¦¿ à¦à¦Ÿà¦¾ পà§à§°à¦¬à¦žà§à¦šà¦¨à¦¾à¦®à§‚লক ছাইটত à¦à¦‡à¦®à¦¾à¦¤à§à§° নিজৰ পাছৱৰà§à¦¡à¦Ÿà§‹ দিছে। Chromiumঠসহায় কৰিব পাৰে। আপোনাৰ পাছৱৰà§à¦¡à¦Ÿà§‹ সলনি কৰিবলৈ আৰৠআপোনাৰ à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿà¦Ÿà§‹ কà§à¦·à¦¤à¦¿ হোৱাৰ সমà§à¦­à¦¾à§±à¦¨à¦¾ থকা বà§à¦²à¦¿ Googleক অৱগত কৰিবলৈ à¦à¦•à¦¾à¦‰à¦£à§à¦Ÿà¦Ÿà§‹ সà§à§°à¦•à§à¦·à¦¿à¦¤ কৰকত কà§à¦²à¦¿à¦• কৰক।</translation>
<translation id="8070439594494267500">à¦à¦ªà§° চিহà§à¦¨</translation>
<translation id="8074253406171541171">১০x১৩ (লেফাফা)</translation>
<translation id="8075736640322370409">কà§à¦·à¦¿à¦ªà§à§°à¦¤à¦¾à§°à§‡ à¦à¦–ন নতà§à¦¨ Google Sheet সৃষà§à¦Ÿà¦¿ কৰক</translation>
<translation id="8075898834294118863">ছাইটৰ ছেটিং পৰিচালনা কৰক</translation>
+<translation id="8076492880354921740">টেব</translation>
<translation id="8078141288243656252">ঘূৰà§à¦£à§€à§Ÿà¦®à¦¾à¦¨ অৱসà§à¦¥à¦¾à¦¤ থাকিলে à¦à¦¨â€™à¦Ÿà§‡â€™à¦Ÿ কৰিব নোৱাৰে</translation>
<translation id="8079031581361219619">ছাইটটো পà§à¦¨à§° ল’ড কৰিবনে?</translation>
<translation id="8081087320434522107">ছিডেন</translation>
@@ -2690,6 +2700,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ছেটিংসমূহ</translation>
+<translation id="8428634594422941299">বà§à¦œà¦¿ পালোà¦</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chromeৰ ছেটিঙত আপোনাৰ কà§à¦•à¦¿à§° অগà§à§°à¦¾à¦§à¦¿à¦•à¦¾à§° পৰিচালনা কৰিবলৈ পà§à§°à¦¥à¦®à§‡ টেব আৰৠতাৰ পাছত à¦à¦£à§à¦Ÿà¦¾à§° টিপক</translation>
<translation id="8433057134996913067">à¦à¦‡à¦Ÿà§‹à§±à§‡ আপোনাক বেছিভাগ ৱেবছাইটৰ পৰাই ছাইন আউট কৰাব।</translation>
<translation id="8434840396568290395">পোহনীয়া জনà§à¦¤à§</translation>
@@ -2787,6 +2798,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> হৈছে <ph name="ORIGIN" />ৰ ক’ড</translation>
<translation id="874918643257405732">à¦à¦‡ টেবটো বà§à¦•à¦®à¦¾à¦°à§à¦• কৰক</translation>
<translation id="8751426954251315517">অনà§à¦—à§à§°à¦¹ কৰি পৰৱৰà§à¦¤à§€ সময়ত পà§à¦¨à§° চেষà§à¦Ÿà¦¾ কৰক</translation>
+<translation id="8757526089434340176">Google Payৰ অফাৰ উপলবà§à¦§</translation>
<translation id="8758885506338294482">পà§à§°à¦¤à¦¿à¦¯à§‹à¦—িতামূলক ভিডিঅ’ গে’মিং</translation>
<translation id="8759274551635299824">কারà§à¦¡à¦–নৰ মà§à¦¯à¦¾à¦¦ উকলিল</translation>
<translation id="87601671197631245">à¦à¦‡ ছাইটটোৱে সà§à§°à¦•à§à¦·à¦¾ সমà§à¦ªà§°à§à¦•à§€à§Ÿ à¦à¦Ÿà¦¾ পà§à§°à¦£à¦¿ কনফিগাৰেশà§à¦¬à¦¨ বà§à¦¯à§±à¦¹à¦¾à§° কৰে, যিটোৱে আপোনাৰ তথà§à¦¯ (উদাহৰণসà§à¦¬à§°à§‚পে, পাছৱৰà§à¦¡, বারà§à¦¤à¦¾ অথবা কà§à§°à§‡à¦¡à¦¿à¦Ÿ কাৰà§à¦¡à¦¸à¦®à§‚হ) à¦à¦‡ ছাইটটোলৈ পঠিওৱা হ’লে সেয়া ফাদিল কৰিব পাৰে।</translation>
@@ -2794,6 +2806,7 @@
<translation id="8763927697961133303">ইউà¦à¦›à¦¬à¦¿ ডিভাইচ</translation>
<translation id="8763986294015493060">বৰà§à¦¤à¦®à¦¾à¦¨ খোল খাই থকা আটাইবোৰ ইনক’গনিট’ ৱিণà§à¦¡â€™ বনà§à¦§ কৰক</translation>
<translation id="8766943070169463815">সà§à§°à¦•à§à¦·à¦¿à¦¤ পৰিশোধৰ কà§à§°à¦¿à¦¡à§‡à¦¨à¦¶à§à¦¬à¦¿à¦¯à¦¼à§‡à¦²à§° বিশà§à¦¬à¦¾à¦¸à¦¯à§‹à¦—à§à¦¯à¦¤à¦¾ পà§à§°à¦®à¦¾à¦£à§€à¦•à§°à¦£ বিষয়ক শà§à¦¬à§€à¦Ÿà¦–ন খোলা হৈছে</translation>
+<translation id="8767765348545497220">সহায়ৰ বাবল বনà§à¦§ কৰক</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">মটৰ চাইকেল</translation>
<translation id="8790007591277257123">মচা কারà§à¦¯ &amp;ৰিডৠকৰক</translation>
@@ -2806,6 +2819,7 @@
<translation id="8806285662264631610">গা ধোৱা আৰৠশৰীৰৰ যতà§à¦¨ লোৱাৰ বাবে বà§à¦¯à§±à¦¹à¦¾à§° কৰা পà§à§°â€™à¦¡à¦¾à¦•à§à¦Ÿ</translation>
<translation id="8807160976559152894">পà§à§°à¦¤à¦¿à¦Ÿà§‹ পৃষà§à¦ à¦¾à§° পাছত টà§à§°à¦¿à¦® কৰক</translation>
<translation id="8808828119384186784">Chromeৰ ছেটিং</translation>
+<translation id="8813277370772331957">মোক পাছত মনত পেলাই দিব</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chromeৰ ছেটিংসমূহত Chrome আপডে’ট কৰিবলৈ পà§à§°à¦¥à¦®à§‡ টেব আৰৠতাৰ পাছত à¦à¦£à§à¦Ÿà¦¾à§° টিপক</translation>
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="882338992931677877">মেনà§à§±à§‡à¦² শà§à¦²à¦Ÿ</translation>
@@ -2985,6 +2999,7 @@
<translation id="988159990683914416">বিকাশকৰà§à¦¤à¦¾à§° বিলà§à¦¡</translation>
<translation id="989988560359834682">ঠিকনা সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ কৰক</translation>
<translation id="991413375315957741">গতি অথবা পোহৰ ধৰা পেলাব পৰা ছেনà§à¦¸à§°</translation>
+<translation id="992110854164447044">à¦à¦–ন ভাৰà§à¦›à§à§±à§‡à¦² কাৰà§à¦¡à§‡ আপোনাক সমà§à¦­à¦¾à¦¬à§à¦¯ পà§à§°à¦¤à¦¾à§°à¦£à¦¾à§° পৰা সà§à§°à¦•à§à¦·à¦¿à¦¤ কৰাত সহায় কৰিবলৈ আপোনাৰ পà§à§°à¦•à§ƒà¦¤ কাৰà§à¦¡à§° পৰিচয় লà§à¦•à§à§±à¦¾à§Ÿà¥¤ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992256792861109788">গà§à¦²à¦ªà§€à¦¯à¦¼à¦¾</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" আপোনাৰ কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à§° বা নেটৱৰà§à¦•à¦¤ ভালকৈ ইনষà§à¦Ÿà¦² কৰা হোৱা নাছিল:
&lt;ul&gt;
diff --git a/chromium/components/strings/components_strings_az.xtb b/chromium/components/strings/components_strings_az.xtb
index 11524c597c1..116c3e431f8 100644
--- a/chromium/components/strings/components_strings_az.xtb
+++ b/chromium/components/strings/components_strings_az.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Qrantlar, təqaüdlər və maliyyə yardımı</translation>
<translation id="1048785276086539861">Annotasiyaları redaktə etdiyiniz zaman bu sənəd bir səhifəlik görünüşə qayıdacaq</translation>
<translation id="1050038467049342496">Digər tətbiqləri bağlayın</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> istifadə edən saytlarda doğrulayıcı cihaz ilə doğrulamağı seçmisiniz. Bu provayder ödəniş metodunuz haqqında məlumat saxlamış ola bilər, onu <ph name="LINK_TEXT" /> ünvanında tapa bilərsiniz.</translation>
<translation id="1055184225775184556">ÆlavÉ™ni geri qaytarın</translation>
<translation id="1056663316309890343">Foto proqramları</translation>
<translation id="1056898198331236512">Xəbərdarlıq</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">Götürmə Üsulu</translation>
<translation id="1281476433249504884">Yığıcı 1</translation>
<translation id="1285320974508926690">Bu saytı heç vaxt tərcümə etməyin</translation>
+<translation id="1288548991597756084">Kartı etibarlı şəkildə saxlayın</translation>
<translation id="1292571435393770077">Qab 16</translation>
<translation id="1292701964462482250">"Kompüterdəki proqram təminatı Chrome'un vebə təhlükəsiz şəkildə qoşulmağına mane olur" (yalnız Windows kompüterləri)</translation>
<translation id="1294154142200295408">Æmr xÉ™ttinin növlÉ™ri</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Bunu həlle etmək üçün açdığınız səhifədə &lt;strong&gt;Connect&lt;/strong&gt; üzərinə klikləyin.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landşaft dizaynı</translation>
<translation id="1513706915089223971">Tarixçə daxiletmələrinin siyahısı</translation>
+<translation id="1516097932025103760">O, şifrələnəcək, təhlükəsiz saxlanacaq və CVC heç vaxt saxlanmayacaq.</translation>
<translation id="1517433312004943670">Telefon nömrəsi tələb olunur</translation>
<translation id="1519264250979466059">Yaratma Tarixi</translation>
<translation id="1521159554480556801">Lif və tekstil sənəti</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Ödəniş üsullarını yadda saxlayın və doldurun</translation>
<translation id="1663943134801823270">Kart və ünvanlar Chrome'dandır. Onları <ph name="BEGIN_LINK" />Ayarlar<ph name="END_LINK" /> bölməsindən idarə edə bilərsiniz.</translation>
<translation id="1671391448414634642"><ph name="SOURCE_LANGUAGE" /> dilindəki səhifələr artıq <ph name="TARGET_LANGUAGE" /> dilinə tərcümə ediləcək.</translation>
+<translation id="1673886523110456987">Təklifi istifadə etmək üçün <ph name="CARD_DETAIL" /> məlumatına baxın</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> dilindən <ph name="TARGET_LANGUAGE" /> dilinə</translation>
<translation id="1682696192498422849">Qısa kənar əvvəldə</translation>
<translation id="168693727862418163">Bu siyasət dəyəri sxem əsasında doğrulanmadı və nəzərə alınmayacaq.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Hərəkət sensorları</translation>
<translation id="1717494416764505390">Poçt qutusu 3</translation>
<translation id="1718029547804390981">Sənəd annotasiya üçün çox böyükdür</translation>
+<translation id="1720941539803966190">Dərsliyi bağlayın</translation>
<translation id="1721424275792716183">* Sahə tələb olunur</translation>
<translation id="1727613060316725209">Sertifikat etibarlıdır</translation>
<translation id="1727741090716970331">Düzgün Kart NömrÉ™si ÆlavÉ™ edin</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">Kabab vÉ™ qrill</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> dilindəki səhifələr tərcümə edilməyəcək.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Bu kontrol aktiv olduqda və status "aktiv" olduğu zaman Chrome axtarış fəaliyyətinizin ən çox bənzədiyi çox iştirakçısı olan qrupu, yaxud "kohortu" müəyyən edir. Reklamçılar qrup üçün reklamlar seçə bilər və axtarış fəaliyyətiniz cihazınızda məxfi saxlanılır. Qrupunuz hər gün yenilənir.}=1{Bu kontrol aktiv olduqda və status "aktiv" olduğu zaman Chrome axtarış fəaliyyətinizin ən çox bənzədiyi çox iştirakçısı olan qrupu, yaxud "kohortu" müəyyən edir. Reklamçılar qrup üçün reklamlar seçə bilər və axtarış fəaliyyətiniz cihazınızda məxfi saxlanılır. Qrupunuz hər gün yenilənir.}other{Bu kontrol aktiv olduqda və status "aktiv" olduğu zaman Chrome axtarış fəaliyyətinizin ən çox bənzədiyi çox iştirakçısı olan qrupu, yaxud "kohortu" müəyyən edir. Reklamçılar qrup üçün reklamlar seçə bilər və axtarış fəaliyyətiniz cihazınızda məxfi saxlanılır. Qrupunuz {NUM_DAYS} gündən bir yenilənir.}}</translation>
-<translation id="2053553514270667976">Poçt indeksi</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 təklif}other{# təklif}}</translation>
+<translation id="2066915425250589881">silinməsini tələb edin</translation>
<translation id="2068528718802935086">Körpələr və yenicə ayaq açmış uşaqlar</translation>
<translation id="2071156619270205202">Bu kart virtual kart nömrəsi üçün uyğun deyil.</translation>
<translation id="2071692954027939183">Bildirişlərə adətən icazə vermədiyinizə görə onlar avtomatik olaraq bloklanıb</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">"Sinxronlaşdırmanı idarə edin" düyməsi, Chrome ayarlarında sinxronlaşdırdığınız məlumatları idarə etmək üçün Enter düyməsinə basın</translation>
<translation id="2091887806945687916">Səs</translation>
<translation id="2094505752054353250">Domen uyÄŸunsuzluÄŸu</translation>
-<translation id="2096368010154057602">Departament</translation>
<translation id="2099652385553570808">Soldan üçlü ştapel vurun</translation>
<translation id="2101225219012730419">Versiya:</translation>
<translation id="2102134110707549001">Güclü Parol Təklif Edin...</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">Amerikan futbolu</translation>
<translation id="2187317261103489799">Aşkarlayın (defolt)</translation>
<translation id="2188375229972301266">Aşağıdan çoxsaylı deşik açın</translation>
-<translation id="2188852899391513400">İndicə istifadə etdiyiniz parol bir data pozuntusunda tapılıb. Hesablarınızı qorumaq üçün Google Parol Meneceri onu indi dəyişməyi və sonra yadda saxladığınız parolları yoxlamağı tövsiyə edir.</translation>
<translation id="219906046732893612">Ev yeniləməsi</translation>
<translation id="2202020181578195191">Düzgün bitmə ili daxil edin</translation>
<translation id="22081806969704220">Qab 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">Fayl redaktəsi</translation>
<translation id="2215963164070968490">İtlər</translation>
<translation id="2218879909401188352">Hazırda <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> saytındakı hücumçular cihazı zÉ™dÉ™lÉ™yÉ™n tÉ™hlükÉ™li tÉ™tbiqlÉ™r quraÅŸdıra, mobil fakturaya gizli ödÉ™niÅŸ É™lavÉ™ edÉ™ vÉ™ ya ÅŸÉ™xsi mÉ™lumatı oÄŸurlaya bilÉ™rlÉ™r. <ph name="BEGIN_LEARN_MORE_LINK" />Ætraflı mÉ™lumat<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Dərsliyi yenidən başladın</translation>
+<translation id="2219735899272417925">Cihazın sıfırlanması tələb olunur</translation>
<translation id="2224337661447660594">Ä°nternet yoxdur</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />Diaqnostika tətbiqini<ph name="END_LINK" /> istifadə edərək bağlantınızı həll edin</translation>
<translation id="2239100178324503013">İndi göndərin</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">İcazə verilməyib (defolt)</translation>
<translation id="2512413427717747692">"Chrome'u defolt brauzer kimi ayarlayın" düyməsi, Enter düyməsinə basaraq iOS ayarlarında Chrome'u sistemin defolt brauzeri kimi ayarlayın</translation>
<translation id="2515629240566999685">ÆrazinizdÉ™ki siqnal yoxlanılır</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> istifadə edən saytlarda Touch ID ilə doğrulamağı seçmisiniz. Bu provayder ödəniş metodunuz haqqında məlumat saxlamış ola bilər, onu <ph name="LINK_TEXT" /> ünvanında tapa bilərsiniz.</translation>
<translation id="2521385132275182522">Aşağı sağdan ştapel vurun</translation>
<translation id="2521736961081452453">Forma yaradın</translation>
<translation id="2523886232349826891">Yalnız bu cihaz yadda saxlanıldı</translation>
<translation id="2524461107774643265">Ætraflı MÉ™lumat ÆlavÉ™ Edin</translation>
<translation id="2529899080962247600">Bu sahədə maksimum <ph name="MAX_ITEMS_LIMIT" /> daxiletmə olmalıdır. Bundan sonrakı daxiletmələr nəzərə alınmayacaq.</translation>
+<translation id="253493526287553278">Promo kodu detallarına baxın</translation>
<translation id="2535585790302968248">Gizli baxış üçün yeni Anonim tab açın</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{vÉ™ daha 1}other{and daha #}}</translation>
<translation id="2536110899380797252">Ãœnvan ÆlavÉ™ Edin</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">Foto və rəqəmsal incəsənət</translation>
<translation id="2601150049980261779">Romantik filmlər</translation>
<translation id="2604589665489080024">Pop musiqisi</translation>
-<translation id="2609632851001447353">Variasiyalar</translation>
<translation id="2610561535971892504">Kopyalamaq üçün klikləyin</translation>
<translation id="2617988307566202237">Chrome aşağıdakı məlumatları <ph name="BEGIN_EMPHASIS" />yadda saxlamayacaq<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Doğum günləri və ad günləri</translation>
<translation id="2677748264148917807">Tərk edin</translation>
+<translation id="2679714844901977852">Təhlükəsiz və sürətli ödəniş üçün kartınızı və faktura məlumatınızı Google Hesabınızda (<ph name="USER_EMAIL" />) saxlayın</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Æn uyÄŸun</translation>
<translation id="2688969097326701645">Bəli, davam edin</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">İnternet provayderləri (ISP)</translation>
<translation id="2856444702002559011">Hücumçular <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> saytından mÉ™lumat (mÉ™sÉ™lÉ™n, parol, mesaj vÉ™ ya kredit kartları) oÄŸurlamaÄŸa çalışa bilÉ™rlÉ™r. <ph name="BEGIN_LEARN_MORE_LINK" />Ætraflı mÉ™lumat<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Bu sayt inadçı və ya aldadıcı reklamlar göstərir.</translation>
-<translation id="286512204874376891">Virtual kart sizi potensial dələduzluqdan qorumaq üçün faktiki kartınızı gizlədir. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Dost</translation>
<translation id="28761159517501904">Film</translation>
<translation id="2876489322757410363">Xarici tətbiqlə ödəniş etmək üçün Anonim rejimdən çıxırsınız. Davam edilsin?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">İstifadə etdiyiniz Wi-Fi login səhifəsinə daxil olmağınızı tələb edə bilər.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ada</translation>
<translation id="3176929007561373547">Proksi serverin işləməsinə əmin olmaq üçün proksi ayarlarınızı yoxlayın və ya şəbəkə administratorunuzla əlaqə saxlayın. Proksi server işlətməli olduğunuza inanmırsınızsa:
<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">Bu cihazdan nə zaman aktiv şəkildə istifadə etdiyinizi bilmək</translation>
@@ -879,9 +884,6 @@
<translation id="3369192424181595722">Saat xətası</translation>
<translation id="3369459162151165748">Nəqliyyat hissələri və aksesuarlar</translation>
<translation id="3371064404604898522">Chrome'u defolt brauzer kimi ayarlayın</translation>
-<translation id="3371076217486966826"><ph name="URL" /> bunun üçün icazə istəyir:
- • Ætrafınızdakı sahÉ™lÉ™rin 3D xÉ™ritÉ™sini yaratmaq vÉ™ ya kamera mövqeyini izlÉ™mÉ™k
- • Kameranızı istifadə etmək</translation>
<translation id="337363190475750230">Nəzərdə tutulmayıb</translation>
<translation id="3375754925484257129">Chrome Təhlükəsizlik Yoxlanışı icra edin</translation>
<translation id="3377144306166885718">Server köhnə TLS versiyasından istifadə etdi.</translation>
@@ -898,6 +900,7 @@
<translation id="3399952811970034796">Çatdırılma Ünvanı</translation>
<translation id="3402261774528610252">Bu saytı yükləmək üçün istifadə edilən bağlantı köhnələn və gələcəkdə söndürüləcək TLS 1.0 və ya TLS 1.1 versiyasından istifadə etdi. Söndürüldükdən sonra istifadəçilərin bu saytı yükləməsinin qarşısı alınacaq. Server TLS 1.2 və ya daha sonrakı versiyanı aktivləşdirməlidir.</translation>
<translation id="3405664148539009465">Şriftləri fərdiləşdirin</translation>
+<translation id="3407789382767355356">üçüncü tərəfin girişi</translation>
<translation id="3409896703495473338">Təhlükəsizlik ayarlarını idarə edin</translation>
<translation id="3414952576877147120">Ölçü:</translation>
<translation id="3417660076059365994">Yüklədiyiniz və ya əlavə etdiyiniz fayllar təhlil üçün Google Cloud'a və ya üçüncü tərəflərə göndərilir. Məsələn, onlar həssas data və ya zərərli proqrama görə skanlana bilər.</translation>
@@ -930,6 +933,7 @@
<translation id="3477679029130949506">Film və teatr cədvəli</translation>
<translation id="3479552764303398839">Ä°ndi yox</translation>
<translation id="3484560055331845446">Google Hesabınıza girişi itirə bilərsiniz. Chrome parolu dəyişməyi məsləhət görür. Daxil olmağınız tələb olunacaq.</translation>
+<translation id="3484861421501147767">Xatırlatma: Saxlanmış promo kodu əlçatandır</translation>
<translation id="3487845404393360112">Qab 4</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" /> səhifəsində tapın</translation>
<translation id="350069200438440499">Fayl adı:</translation>
@@ -1052,6 +1056,7 @@
<translation id="3810973564298564668">Ä°darÉ™ edin</translation>
<translation id="3816482573645936981">Dəyər (əvəz edilib)</translation>
<translation id="382518646247711829">ÆgÉ™r proksi server istifadÉ™ edirsinizsÉ™...</translation>
+<translation id="3826050100957962900">Üçüncü tərəfin girişi</translation>
<translation id="3827112369919217609">Mütləq</translation>
<translation id="3827666161959873541">Ailə filmləri</translation>
<translation id="3828924085048779000">BoÅŸ parola icazÉ™ verilmir.</translation>
@@ -1064,9 +1069,9 @@
<translation id="3858027520442213535">Tarix və saatı güncəlləşdirin</translation>
<translation id="3858860766373142691">Ad</translation>
<translation id="3872834068356954457">Elm</translation>
+<translation id="3875783148670536197">İstifadə Qaydasını Göstərin</translation>
<translation id="3881478300875776315">Daha az sətir göstərin</translation>
<translation id="3884278016824448484">Ziddiyyətli cihaz identifikatoru</translation>
-<translation id="3885155851504623709">KilsÉ™</translation>
<translation id="388632593194507180">Nəzarət Aşkarlanıb</translation>
<translation id="3886948180919384617">Yığıcı 3</translation>
<translation id="3890664840433101773">E-poçt ünvanı əlavə edin</translation>
@@ -1096,9 +1101,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> blok edilib</translation>
<translation id="3978338123949022456">Axtarış rejimi, <ph name="KEYWORD_SUFFIX" /> ilə axtarmaq üçün sorğu yazın və Enter düyməsinə basın</translation>
<translation id="398470910934384994">QuÅŸlar</translation>
+<translation id="3985750352229496475">Ünvanları İdarə Edin...</translation>
<translation id="3986705137476756801">Canlı Subtitri hələlik deaktiv edin</translation>
<translation id="3987940399970879459">1 MB-dan az</translation>
<translation id="3990250421422698716">Kənarını bükün</translation>
+<translation id="3992684624889376114">Bu səhifə haqqında</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> saytı ona yönləndirilən
bütün sorğulara mənbə siyasətin tətbiq edilməsini tələb edib, lakin bu siyasət hazırda tətbiq edilə bilmir.</translation>
<translation id="4006465311664329701">Google Pay istifadə edən Ödəniş Metodları, Təkliflər və Ünvanlar</translation>
@@ -1222,6 +1229,7 @@
<translation id="4305666528087210886">Faylınıza giriş mümkün olmadı</translation>
<translation id="4306529830550717874">Ünvan saxlanılsın?</translation>
<translation id="4306812610847412719">mübadilə buferi</translation>
+<translation id="4310070645992025887">Səyahətlərinizi axtarın</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blok edin (defolt)</translation>
<translation id="4314815835985389558">Sinxronizasiyanı idarə edin</translation>
@@ -1272,6 +1280,7 @@
<translation id="4435702339979719576">Postkart)</translation>
<translation id="443673843213245140">Proksi istifadəsi deaktiv edilib, amma ətraflı proksi konfiqurasiyası müəyyən edilib.</translation>
<translation id="4441832193888514600">Siyasət yalnız bulud istifadəçisi siyasəti kimi təyin oluna bildiyi üçün nəzərə alınmadı.</translation>
+<translation id="4442470707340296952">Chrome Tabları</translation>
<translation id="4450893287417543264">Göstərilməsin</translation>
<translation id="4451135742916150903">Saytlar HID cihazlarına qoşulmaq üçün icazə istəyə bilər</translation>
<translation id="4452328064229197696">İndicə istifadə etdiyiniz parol bir data pozuntusunda tapılıb. Hesablarınızı qorumaq üçün Google Parol Meneceri yadda saxladığınız parolları yoxlamağı tövsiyə edir.</translation>
@@ -1410,6 +1419,7 @@
<translation id="483241715238664915">Xəbərdarlıqları aktiv edin</translation>
<translation id="4834250788637067901">Google Pay istifadə edən ödəniş metodları, təkliflər və ünvanlar</translation>
<translation id="4838327282952368871">Xəyali</translation>
+<translation id="4839087176073128681">Növbəti dəfə daha sürətli ödəyin və kartınızı Google'un qabaqcıl təhlükəsizliyi ilə qoruyun.</translation>
<translation id="4840250757394056958">Chrome tarixçənizə baxın</translation>
<translation id="484462545196658690">Avtomatik</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> və digərləri üçün endirim əldə edin</translation>
@@ -1472,6 +1482,7 @@
<translation id="5011561501798487822">Aşkarlanmış Dil</translation>
<translation id="5015510746216210676">Cihaz Adı:</translation>
<translation id="5017554619425969104">Kopyalanmış mətn</translation>
+<translation id="5017828934289857214">Sonra xatırladın</translation>
<translation id="5018422839182700155">Bu səhifəni açmaq olmur</translation>
<translation id="5019198164206649151">Yedəkləmə yaddaşı it günündədir</translation>
<translation id="5020776957610079374">Dünya musiqisi</translation>
@@ -1491,6 +1502,7 @@
<translation id="5051305769747448211">Canlı komediya</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Yaxınlıqda Paylaşım ilə bu faylı göndərmək üçün cihazınızda (<ph name="DISK_SPACE_SIZE" />) yer boşaldın}other{Yaxınlıqda Paylaşım ilə bu faylları göndərmək üçün cihazınızda (<ph name="DISK_SPACE_SIZE" />) yer boşaldın}}</translation>
<translation id="5056549851600133418">Sizin üçün məqalələr</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> istifadə edən saytlarda Windows Hello ilə doğrulamağı seçmisiniz. Bu provayder ödəniş metodunuz haqqında məlumat saxlamış ola bilər, onu <ph name="LINK_TEXT" /> ünvanında tapa bilərsiniz.</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" /> nəzərdə tuturdunuz?</translation>
<translation id="5066056036849835175">Çap tarixçəsi</translation>
<translation id="5068234115460527047">Hedge fondlar</translation>
@@ -1504,10 +1516,8 @@
<translation id="5087286274860437796">Server sertifikatı hazırda etibarlı deyil.</translation>
<translation id="5087580092889165836">Kart əlavə edin</translation>
<translation id="5088142053160410913">Operatora mesaj</translation>
-<translation id="5089810972385038852">Dövlət</translation>
<translation id="5093232627742069661">Z-qatlama</translation>
<translation id="5094747076828555589">Bu server <ph name="DOMAIN" /> domenini təsdiqləyə bilmədi; onun güvənlik sertifikatı Chromium tərəfindən doğrulanmayıb. Buna səbəb yanlış konfiqurasiya və ya hücumçü tərəfindən bağlantınızın ələ keçirilməsi ola bilər.</translation>
-<translation id="5095208057601539847">Vilayət</translation>
<translation id="5097099694988056070">CPU/RAM istifadəsi kimi cihaz statistikası</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Sayt güvənli deyil</translation>
@@ -1545,6 +1555,7 @@
<translation id="5171045022955879922">Linki axtarın və ya yazın</translation>
<translation id="5171689220826475070">Fanfold-Avropa</translation>
<translation id="5172758083709347301">Maşın</translation>
+<translation id="5177076414499237632">Bu səhifənin mənbəyi və mövzusu haqqında məlumat əldə edin</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> deyil? Bu xətanı xəbər verin</translation>
<translation id="518639307526414276">Heyvan yemi və ev heyvanlarına qulluq ləvazimatları</translation>
<translation id="5190835502935405962">ÆlfÉ™cinlÉ™r Paneli</translation>
@@ -1705,6 +1716,7 @@
<translation id="5624120631404540903">Parolları idarə edin</translation>
<translation id="5629630648637658800">Siyasət ayarlarını yükləmək uğursuz oldu</translation>
<translation id="5631439013527180824">Yanlış cihaz idarəetmə markeri</translation>
+<translation id="5632485077360054581">İstifadə qaydasını göstərin</translation>
<translation id="5633066919399395251">Hazırda <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> saytındakı hücumçular kompüterdÉ™ki mÉ™lumatları (mÉ™sÉ™lÉ™n, foto, parol, mesaj vÉ™ kredit kartları) oÄŸurlayan vÉ™ ya silÉ™n zÉ™rÉ™rli proqramlar quraÅŸdırmaÄŸa cÉ™hd edÉ™ bilÉ™r. <ph name="BEGIN_LEARN_MORE_LINK" />Ætraflı mÉ™lumat<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Aldadıcı kontent blok edildi.</translation>
<translation id="5633259641094592098">Kult və müstəqil filmlər</translation>
@@ -1822,6 +1834,7 @@
<translation id="5989320800837274978">Proksi serverlər və .pac skript URL-i göstərilməyib.</translation>
<translation id="5992691462791905444">Mühəndislikdə Z-qatlama</translation>
<translation id="5995727681868049093">Google Hesabınızda məlumat, məxfilik və təhlükəsizliyinizi idarə edin</translation>
+<translation id="5997247540087773573">İndicə istifadə etdiyiniz parol bir data pozuntusunda tapılıb. Hesablarınızı qorumaq üçün Google Parol Meneceri onu indi dəyişməyi və sonra yadda saxladığınız parolları yoxlamağı tövsiyə edir.</translation>
<translation id="6000758707621254961">"<ph name="SEARCH_TEXT" />" üçün <ph name="RESULT_COUNT" /> nəticə</translation>
<translation id="6006484371116297560">Klassik</translation>
<translation id="6008122969617370890">N-1 sırası</translation>
@@ -1917,7 +1930,6 @@
<translation id="627746635834430766">Növbəti dəfə daha sürətli ödəniş etmək üçün kartı və faktura ünvanını Google Hesabınızda yadda saxlayın.</translation>
<translation id="6279183038361895380">Kursorunuzu göstərmək üçün |<ph name="ACCELERATOR" />| basın</translation>
<translation id="6280223929691119688">Bu ünvana çatdırmaq mümkün deyil. Başqa ünvan seçin.</translation>
-<translation id="6282194474023008486">Poçt kodu</translation>
<translation id="6285507000506177184">"Chrome'da endirmələri idarə edin" düyməsi, Enter düyməsinə basaraq Chrome'da endirdiyiniz faylları idarə edin</translation>
<translation id="6289939620939689042">Səhifə Rəngi</translation>
<translation id="6290238015253830360">Təklif edilən məqalələriniz burada görünür</translation>
@@ -1939,6 +1951,7 @@
<translation id="6337133576188860026">Maksimum <ph name="SIZE" /> boşaldılır. Növbəti dəfə daxil olarkən bəzi saytlar daha yavaş yüklənə bilər.</translation>
<translation id="6337534724793800597">Siyasətləri adlara əsasən filtrləyin</translation>
<translation id="6340739886198108203">Administrator siyasəti məxfi kontent göründükdə skrinşot çəkməyi və ya nəsə qeydə almağı tövsiyə etmir:</translation>
+<translation id="6348220984832452017">Aktiv Variasiyalar</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> tətbiqini quraşdırın</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Heç biri}=1{1 parol (<ph name="DOMAIN_LIST" /> üçün sinxronizasiya edilib)}=2{2 parol (<ph name="DOMAIN_LIST" />üçün sinxronizasiya edilib)}other{# parol (<ph name="DOMAIN_LIST" />üçün sinxronizasiya edilib)}}</translation>
<translation id="6355392890578844978">Bu brauzer ÅŸirkÉ™t vÉ™ ya baÅŸqa təşkilat tÉ™rÉ™findÉ™n idarÉ™ edilmir. Bu cihazdakı fÉ™aliyyÉ™t Chromium'dan kÉ™narda idarÉ™ edilÉ™ bilÉ™r. <ph name="BEGIN_LINK" />Ætraflı mÉ™lumat<ph name="END_LINK" /></translation>
@@ -1970,7 +1983,6 @@
<translation id="643051589346665201">Google parolunu dəyişin</translation>
<translation id="6433490469411711332">Kontakt məlumatını redaktə edin</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> qoşulmaq istəmədi.</translation>
-<translation id="6438025220577812695">Özüm dəyişəcəyəm</translation>
<translation id="6440503408713884761">İqnor edilənlər</translation>
<translation id="6443406338865242315">Quraşdırdığınız artırmalar və qoşmalar</translation>
<translation id="6446163441502663861">Kahu (Zərf)</translation>
@@ -2100,9 +2112,9 @@
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Tərcümə et</translation>
<translation id="6833752742582340615">Təhlükəsiz və sürətli ödəniş üçün kartınızı və faktura məlumatınızı Google Hesabınızda saxlayın</translation>
-<translation id="6839929833149231406">SahÉ™</translation>
<translation id="6846340164947227603">Virtual kart nömrəsini istifadə edin:</translation>
<translation id="6852204201400771460">Tətbiq yenidən yüklənilsin?</translation>
+<translation id="6857776781123259569">Parolları idarə edin...</translation>
<translation id="686485648936420384">İstehlak resursları</translation>
<translation id="6865412394715372076">Bu kart hazırda doğrulana bilməz</translation>
<translation id="6869334554832814367">Şəxsi istiqrazlar</translation>
@@ -2151,7 +2163,6 @@
<translation id="6965978654500191972">Cihaz</translation>
<translation id="696703987787944103">Qavrayış</translation>
<translation id="6968269510885595029">Təhlükəsizlik Açarını istifadə edin</translation>
-<translation id="6970216967273061347">Rayon</translation>
<translation id="6971439137020188025">Slaydda cəld yeni Google təqdimatı yaradın</translation>
<translation id="6972629891077993081">HID cihazları</translation>
<translation id="6973656660372572881">Fiskə edilmiş proksi serverlər və .pac skript URL-lər göstərilib.</translation>
@@ -2190,7 +2201,6 @@
<translation id="7081308185095828845">Bu xüsusiyyət cihazınızda əlçatan deyil</translation>
<translation id="7083258188081898530">Qab 9</translation>
<translation id="7086090958708083563">Yükləmə istifadəçi tərəfindən tələb edildi</translation>
-<translation id="7087282848513945231">Ölkə</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome ayarlarında saytlarda saxlanılan icazələri və datanı idarə etmək üçün Tab, sonra Enter düyməsinə basın</translation>
<translation id="7096937462164235847">Bu veb saytın kimliyi doğrulanmayıb.</translation>
<translation id="7101893872976785596">Qorxu filmləri</translation>
@@ -2209,10 +2219,10 @@
<ph name="LIST_ITEM" />Formalara daxil edilmiş məlumatlar<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Bu ünvana göndərmək mümkün deyil. Başqa ünvan seçin.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Admininiz bu məlumatın kopyalanmasını qadağan edib.</translation>
<translation id="7135130955892390533">Statusu göstərin</translation>
<translation id="7138472120740807366">Çatdırılma üsulu</translation>
-<translation id="7139724024395191329">Æmirlik</translation>
<translation id="7139892792842608322">Æsas Qab</translation>
<translation id="714064300541049402">Şəklin 2-ci tərəfinin X oxu üzrə yerdəyişməsi</translation>
<translation id="7152423860607593928">Nömrə-14 (Zərf)</translation>
@@ -2429,6 +2439,7 @@
<translation id="7669271284792375604">Saytdakı hücumçular işinizə zərər gətirəcək proqramları quraşdırmaq üçün Sizi aldatmağa cəhd edə bilər (məsələn, əsas səhifəni dəyişməklə və ya daxil olduğunuz saytlarda əlavə reklamlar göstərməklə).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{MÉ™xfi olaraq iÅŸarÉ™lÉ™nmiÅŸ mÉ™lumatlarla aparılan É™mÉ™liyyatlar (giriÅŸdÉ™n sonra 1 É™mÉ™liyyat). <ph name="BEGIN_LINK" />Ætraflı mÉ™lumat<ph name="END_LINK" />}other{MÉ™xfi olaraq iÅŸarÉ™lÉ™nmiÅŸ mÉ™lumatlarla aparılan É™mÉ™liyyatlar (giriÅŸdÉ™n sonra # É™mÉ™liyyat). <ph name="BEGIN_LINK" />Ætraflı mÉ™lumat<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Poçt qutusu 6</translation>
+<translation id="7675325315208090829">Ödəniş Metodlarını İdarə Edin...</translation>
<translation id="7676643023259824263">Mübadilə buferində mətni axtarın, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Chrome ayarlarında brauzer tarixçənizə baxın və idarə edin</translation>
<translation id="7679947978757153706">Beysbol</translation>
@@ -2471,7 +2482,6 @@
<translation id="7766518757692125295">ÆtÉ™k</translation>
<translation id="7770259615151589601">Təyin Edilmiş Uzunluq</translation>
<translation id="7773005668374414287">Eyni sıra ilə üzü yuxarı</translation>
-<translation id="777702478322588152">Prefektura</translation>
<translation id="7791011319128895129">Buraxılmayıb</translation>
<translation id="7791196057686275387">Balanslaşdırın</translation>
<translation id="7791543448312431591">ÆlavÉ™ etmÉ™k</translation>
@@ -2562,12 +2572,12 @@
<translation id="8055534648776115597">Peşə təhsili və davamlı təhsil</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" düzgün konfiqurasiya edilməyib. "<ph name="SOFTWARE_NAME" />" proqramının sistemdən silinməsi ilə adətən problem həll olunur. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Qida istehsalı</translation>
-<translation id="8066955247577885446">Xəta baş verdi.</translation>
<translation id="8067872629359326442">İndicə parolunuzu aldadıcı saytda daxil etdiniz. Chromium yardım edə bilər. Parolunuzu dəyişmək və Google'a hesabınızın təhlükədə ola biləcəyini bildirmək üçün "Hesabı Qoruyun" seçiminə toxunun.</translation>
<translation id="8070439594494267500">Tətbiq ikonası</translation>
<translation id="8074253406171541171">10x13 (Zərf)</translation>
<translation id="8075736640322370409">Cəld yeni Google Cədvəl yaradın</translation>
<translation id="8075898834294118863">Sayt ayarlarını idarə edin</translation>
+<translation id="8076492880354921740">Tablar</translation>
<translation id="8078141288243656252">Döndərilidikdə annotasiya mümkün deyil</translation>
<translation id="8079031581361219619">Sayt yenidən yüklənsin?</translation>
<translation id="8081087320434522107">Sedan</translation>
@@ -2690,6 +2700,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ayarlar</translation>
+<translation id="8428634594422941299">Anladım</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome ayarlarında kuki tərcihlərinizi idarə etmək üçün Tab, sonra Enter düyməsinə basın</translation>
<translation id="8433057134996913067">Bu Sizi bir çox veb saytdan çıxaracaq.</translation>
<translation id="8434840396568290395">Ev heyvanları</translation>
@@ -2787,6 +2798,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> üçün kodunuz: <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">Bu tabı əlfəcinlərə əlavə edin</translation>
<translation id="8751426954251315517">Sonra yenidən cəhd edin</translation>
+<translation id="8757526089434340176">Google Pay təklifi əlçatandır</translation>
<translation id="8758885506338294482">Yarış video oyunları</translation>
<translation id="8759274551635299824">Bu kartın vaxtı bitib</translation>
<translation id="87601671197631245">Bu sayt vaxtı keçmiş təhlükəsizlik konfiqurasiyasından istifadə edir və bu da sayta göndərilən məlumatınızın (məsələn, parollar, mesajlar və ya kredit kartları) paylaşılmasına səbəb ola bilər.</translation>
@@ -2794,6 +2806,7 @@
<translation id="8763927697961133303">USB cihazı</translation>
<translation id="8763986294015493060">Hazırda açıq olan bütün Anonim pəncərələri bağlayın</translation>
<translation id="8766943070169463815">Güvənli ödəniş məlumatları üzrə doğrulama vərəqəsi açılıb</translation>
+<translation id="8767765348545497220">Yardım balonunu bağlayın</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motosikllar</translation>
<translation id="8790007591277257123">Silinməni yenidən edin</translation>
@@ -2806,6 +2819,7 @@
<translation id="8806285662264631610">Hamam və bədənə qulluq məhsulları</translation>
<translation id="8807160976559152894">Hər səhifədən sonra kəsin</translation>
<translation id="8808828119384186784">Chrome Ayarları</translation>
+<translation id="8813277370772331957">Sonra xatırladın</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Tab düyməsi, sonra Enter düyməsinə basaraq Chrome ayarlarında Chrome'u güncəlləyin</translation>
<translation id="8820817407110198400">ÆlfÉ™cinlÉ™r</translation>
<translation id="882338992931677877">Manual Yuva</translation>
@@ -2985,6 +2999,7 @@
<translation id="988159990683914416">Developer Build</translation>
<translation id="989988560359834682">Ünvana düzəliş edin</translation>
<translation id="991413375315957741">hərəkət və işıq sensorları</translation>
+<translation id="992110854164447044">Virtual kart sizi potensial dələduzluqdan qorumaq üçün faktiki kartınızı gizlədir. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Çəhrayı</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" kompüter və ya şəbəkənizdə düzgün quraşdırılmayıb:
diff --git a/chromium/components/strings/components_strings_be.xtb b/chromium/components/strings/components_strings_be.xtb
index 1c8a73f5695..90f251c026d 100644
--- a/chromium/components/strings/components_strings_be.xtb
+++ b/chromium/components/strings/components_strings_be.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Гранты, Ñтыпендыі Ñ– фінанÑÐ°Ð²Ð°Ñ Ð¿Ð°Ð´Ñ‚Ñ€Ñ‹Ð¼ÐºÐ°</translation>
<translation id="1048785276086539861">Калі вы будзеце змÑнÑць анатацыі, гÑÑ‚Ñ‹ дакумент вернецца Ñž аднаÑтаронкавы выглÑд</translation>
<translation id="1050038467049342496">Закрыйце Ñ–Ð½ÑˆÑ‹Ñ Ð¿Ñ€Ð°Ð³Ñ€Ð°Ð¼Ñ‹</translation>
+<translation id="1053959602163383901">Ð’Ñ‹ выбралі прыладу аўтÑнтыфікацыі Ð´Ð»Ñ ÑÐ¿Ñ€Ð°ÑžÐ´Ð¶Ð°Ð½Ð½Ñ ÐºÑƒÐ¿Ð»ÑÑž на Ñайтах, ÑÐºÑ–Ñ ÐºÐ°Ñ€Ñ‹Ñтаюцца паÑлугамі паÑтаўшчыка <ph name="PROVIDER_ORIGIN" />. ГÑÑ‚Ñ‹ паÑтаўшчык мог захаваць інфармацыю пра ваш ÑпоÑаб аплаты. Ð’Ñ‹ можаце <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Ðдрабіць дадаванне</translation>
<translation id="1056663316309890343">Праграмнае забеÑпÑчÑнне Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ з фота</translation>
<translation id="1056898198331236512">ПапÑÑ€Ñджанне</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">СпоÑаб прынÑццÑ</translation>
<translation id="1281476433249504884">Укладчык 1</translation>
<translation id="1285320974508926690">Ðіколі не перакладаць гÑÑ‚Ñ‹ Ñайт</translation>
+<translation id="1288548991597756084">Захоўвайце Ð´Ð°Ð½Ñ‹Ñ ÐºÐ°Ñ€Ñ‚ÐºÑ– Ñž бÑÑпецы</translation>
<translation id="1292571435393770077">Латок 16</translation>
<translation id="1292701964462482250">"Праграмнае забеÑпÑчÑнне на камп'ютары не дае браўзеру Chrome уÑтанавіць бÑÑпечнае падключÑнне да інтÑрнÑту" (толькі камп'ютары пад кіраваннем Windows)</translation>
<translation id="1294154142200295408">ВарыÑнты Ð´Ð»Ñ ÐºÐ°Ð¼Ð°Ð½Ð´Ð½Ð°Ð³Ð° радка</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Каб выправіць памылку, націÑніце &lt;strong&gt;Падключыцца&lt;/strong&gt; на Ñтаронцы, Ñкую вы Ñпрабуеце адкрыць.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Ландшафтны дызайн</translation>
<translation id="1513706915089223971">Ð¡Ð¿Ñ–Ñ Ð·Ð°Ð¿Ñ–Ñаў гіÑторыі</translation>
+<translation id="1516097932025103760">ПаÑÐ»Ñ ÑˆÑ‹Ñ„Ñ€Ð°Ð²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ñ‹Ñ… Ñны будуць захоўвацца Ñž бÑÑпецы. Код CVC не захоўваецца ніколі.</translation>
<translation id="1517433312004943670">Патрабуецца нумар Ñ‚Ñлефона</translation>
<translation id="1519264250979466059">Дата зборкі</translation>
<translation id="1521159554480556801">ТÑкÑтыльнае маÑтацтва</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Захоўваць Ñ– запаўнÑць Ð´Ð°Ð½Ñ‹Ñ ÑпоÑабаў аплаты</translation>
<translation id="1663943134801823270">Ð”Ð°Ð½Ñ‹Ñ ÐºÑ€Ñдытных картак Ñ– адраÑÑ‹ паходзÑць з браўзера Chrome. Кіраваць імі можна Ñž <ph name="BEGIN_LINK" />Ðаладах<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">ЦÑпер Ñтаронкі на мове <ph name="SOURCE_LANGUAGE" /> будуць перакладацца на мову <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Каб выкарыÑтаць прапанову, аплаціце, выкарыÑтаўшы картку <ph name="CARD_DETAIL" /></translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> – <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Спачатку кароткі край</translation>
<translation id="168693727862418163">Ðе ўдалоÑÑ Ð¿Ð°Ñ†Ð²ÐµÑ€Ð´Ð·Ñ–Ñ†ÑŒ адпаведнаÑць значÑÐ½Ð½Ñ Ð¿Ð°Ð»Ñ–Ñ‚Ñ‹ÐºÑ– Ñхеме, таму Ñно будзе ігнаравацца.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Датчыкі руху</translation>
<translation id="1717494416764505390">ÐŸÐ°ÑˆÑ‚Ð¾Ð²Ð°Ñ Ñкрынка 3</translation>
<translation id="1718029547804390981">Занадта вÑлікі дакумент Ð´Ð»Ñ Ð°Ð½Ð°Ñ‚Ð°Ñ†Ñ‹Ð¹</translation>
+<translation id="1720941539803966190">Закрыць дапаможнік</translation>
<translation id="1721424275792716183">* ÐбавÑзковае поле</translation>
<translation id="1727613060316725209">Сертыфікат з’ÑўлÑецца Ñапраўдным</translation>
<translation id="1727741090716970331">Дадайце нумар дзеючай карткі</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">Барбекю і грыль</translation>
<translation id="2053111141626950936">Старонкі на мове <ph name="LANGUAGE" /> не будуць перакладацца.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Калі гÑÑ‚Ñ‹ Ñлемент ÐºÑ–Ñ€Ð°Ð²Ð°Ð½Ð½Ñ ÑžÐºÐ»ÑŽÑ‡Ð°Ð½Ñ‹ Ñ– Ñ‚ÑÑ…Ð½Ð°Ð»Ð¾Ð³Ñ–Ñ FLoC актывавана, Chrome выÑўлÑе, да дзеÑннÑÑž Ñкой вÑлікай групы людзей ("кагорты") больш за ÑžÑÑ‘ Ð¿Ð°Ð´Ð¾Ð±Ð½Ñ‹Ñ Ð²Ð°ÑˆÑ‹ нÑÐ´Ð°ÑžÐ½Ñ–Ñ Ð´Ð·ÐµÑнні Ñž браўзеры. РÑкламадаўцы могуць падбіраць Ñ€Ñкламу Ð´Ð»Ñ Ð³Ñтай групы, а звеÑткі пра дзеÑнні Ñž браўзеры пры гÑтым будуць прыватна захоўвацца на вашай прыладзе. Параметры вашай групы абнаўлÑюцца кожны дзень.}=1{Калі гÑÑ‚Ñ‹ Ñлемент ÐºÑ–Ñ€Ð°Ð²Ð°Ð½Ð½Ñ ÑžÐºÐ»ÑŽÑ‡Ð°Ð½Ñ‹ Ñ– Ñ‚ÑÑ…Ð½Ð°Ð»Ð¾Ð³Ñ–Ñ FLoC актывавана, Chrome выÑўлÑе, да дзеÑннÑÑž Ñкой вÑлікай групы людзей ("кагорты") больш за ÑžÑÑ‘ Ð¿Ð°Ð´Ð¾Ð±Ð½Ñ‹Ñ Ð²Ð°ÑˆÑ‹ нÑÐ´Ð°ÑžÐ½Ñ–Ñ Ð´Ð·ÐµÑнні Ñž браўзеры. РÑкламадаўцы могуць падбіраць Ñ€Ñкламу Ð´Ð»Ñ Ð³Ñтай групы, а звеÑткі пра дзеÑнні Ñž браўзеры пры гÑтым будуць прыватна захоўвацца на вашай прыладзе. Параметры вашай групы абнаўлÑюцца кожны дзень.}one{Калі гÑÑ‚Ñ‹ Ñлемент ÐºÑ–Ñ€Ð°Ð²Ð°Ð½Ð½Ñ ÑžÐºÐ»ÑŽÑ‡Ð°Ð½Ñ‹ Ñ– Ñ‚ÑÑ…Ð½Ð°Ð»Ð¾Ð³Ñ–Ñ FLoC актывавана, Chrome выÑўлÑе, да дзеÑннÑÑž Ñкой вÑлікай групы людзей ("кагорты") больш за ÑžÑÑ‘ Ð¿Ð°Ð´Ð¾Ð±Ð½Ñ‹Ñ Ð²Ð°ÑˆÑ‹ нÑÐ´Ð°ÑžÐ½Ñ–Ñ Ð´Ð·ÐµÑнні Ñž браўзеры. РÑкламадаўцы могуць падбіраць Ñ€Ñкламу Ð´Ð»Ñ Ð³Ñтай групы, а звеÑткі пра дзеÑнні Ñž браўзеры пры гÑтым будуць прыватна захоўвацца на вашай прыладзе. Параметры вашай групы абнаўлÑюцца кожны {NUM_DAYS} дзень.}few{Калі гÑÑ‚Ñ‹ Ñлемент ÐºÑ–Ñ€Ð°Ð²Ð°Ð½Ð½Ñ ÑžÐºÐ»ÑŽÑ‡Ð°Ð½Ñ‹ Ñ– Ñ‚ÑÑ…Ð½Ð°Ð»Ð¾Ð³Ñ–Ñ FLoC актывавана, Chrome выÑўлÑе, да дзеÑннÑÑž Ñкой вÑлікай групы людзей ("кагорты") больш за ÑžÑÑ‘ Ð¿Ð°Ð´Ð¾Ð±Ð½Ñ‹Ñ Ð²Ð°ÑˆÑ‹ нÑÐ´Ð°ÑžÐ½Ñ–Ñ Ð´Ð·ÐµÑнні Ñž браўзеры. РÑкламадаўцы могуць падбіраць Ñ€Ñкламу Ð´Ð»Ñ Ð³Ñтай групы, а звеÑткі пра дзеÑнні Ñž браўзеры пры гÑтым будуць прыватна захоўвацца на вашай прыладзе. Параметры вашай групы абнаўлÑюцца ÐºÐ¾Ð¶Ð½Ñ‹Ñ {NUM_DAYS} дні.}many{Калі гÑÑ‚Ñ‹ Ñлемент ÐºÑ–Ñ€Ð°Ð²Ð°Ð½Ð½Ñ ÑžÐºÐ»ÑŽÑ‡Ð°Ð½Ñ‹ Ñ– Ñ‚ÑÑ…Ð½Ð°Ð»Ð¾Ð³Ñ–Ñ FLoC актывавана, Chrome выÑўлÑе, да дзеÑннÑÑž Ñкой вÑлікай групы людзей ("кагорты") больш за ÑžÑÑ‘ Ð¿Ð°Ð´Ð¾Ð±Ð½Ñ‹Ñ Ð²Ð°ÑˆÑ‹ нÑÐ´Ð°ÑžÐ½Ñ–Ñ Ð´Ð·ÐµÑнні Ñž браўзеры. РÑкламадаўцы могуць падбіраць Ñ€Ñкламу Ð´Ð»Ñ Ð³Ñтай групы, а звеÑткі пра дзеÑнні Ñž браўзеры пры гÑтым будуць прыватна захоўвацца на вашай прыладзе. Параметры вашай групы абнаўлÑюцца ÐºÐ¾Ð¶Ð½Ñ‹Ñ {NUM_DAYS} дзён.}other{Калі гÑÑ‚Ñ‹ Ñлемент ÐºÑ–Ñ€Ð°Ð²Ð°Ð½Ð½Ñ ÑžÐºÐ»ÑŽÑ‡Ð°Ð½Ñ‹ Ñ– Ñ‚ÑÑ…Ð½Ð°Ð»Ð¾Ð³Ñ–Ñ FLoC актывавана, Chrome выÑўлÑе, да дзеÑннÑÑž Ñкой вÑлікай групы людзей ("кагорты") больш за ÑžÑÑ‘ Ð¿Ð°Ð´Ð¾Ð±Ð½Ñ‹Ñ Ð²Ð°ÑˆÑ‹ нÑÐ´Ð°ÑžÐ½Ñ–Ñ Ð´Ð·ÐµÑнні Ñž браўзеры. РÑкламадаўцы могуць падбіраць Ñ€Ñкламу Ð´Ð»Ñ Ð³Ñтай групы, а звеÑткі пра дзеÑнні Ñž браўзеры пры гÑтым будуць прыватна захоўвацца на вашай прыладзе. Параметры вашай групы абнаўлÑюцца ÐºÐ¾Ð¶Ð½Ñ‹Ñ {NUM_DAYS} днÑ.}}</translation>
-<translation id="2053553514270667976">Паштовы індÑкÑ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 прапанова}one{# прапанова}few{# прапановы}many{# прапаноў}other{# прапановы}}</translation>
+<translation id="2066915425250589881">запытаць Ñе выдаленне</translation>
<translation id="2068528718802935086">ÐемаўлÑÑ‚Ñ‹ Ñ– Ð¼Ð°Ð»Ñ‹Ñ Ð´Ð·ÐµÑ†Ñ–</translation>
<translation id="2071156619270205202">ГÑтай картцы нельга прыÑвоіць нумар віртуальнай карткі.</translation>
<translation id="2071692954027939183">ÐпавÑшчÑнні былі заблакіраваны аўтаматычна, бо звычайна вы не даÑце дазволу на Ñ–Ñ… паказ</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">Кнопка "Кіраваць ÑінхранізацыÑй". Каб задаць у наладах Chrome, ÑÐºÐ°Ñ Ñ–Ð¼ÐµÐ½Ð½Ð° Ñ–Ð½Ñ„Ð°Ñ€Ð¼Ð°Ñ†Ñ‹Ñ Ð±ÑƒÐ´Ð·Ðµ Ñінхранізавацца, націÑніце Enter</translation>
<translation id="2091887806945687916">Гук</translation>
<translation id="2094505752054353250">ÐеÑупадзенне дамена</translation>
-<translation id="2096368010154057602">Ðддзел</translation>
<translation id="2099652385553570808">Тры Ñкабы злева</translation>
<translation id="2101225219012730419">ВерÑÑ–Ñ:</translation>
<translation id="2102134110707549001">Прапанаваць надзейны пароль…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">ÐмерыканÑкі футбол</translation>
<translation id="2187317261103489799">Ð’Ñ‹ÑўлÑць (Ñтандартна)</translation>
<translation id="2188375229972301266">Ðекалькі дзірак знізу</translation>
-<translation id="2188852899391513400">Пароль, Ñкі вы толькі што выкарыÑталі, быў раÑкрыты пры ўцечцы даных. Каб абараніць Ñвае ÑžÐ»Ñ–ÐºÐ¾Ð²Ñ‹Ñ Ð·Ð°Ð¿Ñ–ÑÑ‹, зараз жа змÑніце Ñго, а затым праверце Ð·Ð°Ñ…Ð°Ð²Ð°Ð½Ñ‹Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ– Ñž Менеджары паролÑÑž Google.</translation>
<translation id="219906046732893612">Добраўпарадкаванне дома</translation>
<translation id="2202020181578195191">УвÑдзіце Ñапраўдны год заканчÑÐ½Ð½Ñ Ñ‚Ñрміну дзеÑннÑ</translation>
<translation id="22081806969704220">Латок 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">ЗмÑненне файлаў</translation>
<translation id="2215963164070968490">Сабакі</translation>
<translation id="2218879909401188352">ЗламыÑнікі на Ñайце <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> могуць уÑталÑваць праграмы, ÑÐºÑ–Ñ Ð¼Ð¾Ð³ÑƒÑ†ÑŒ Ñтаць прычынай Ð¿Ð°ÑˆÐºÐ¾Ð´Ð¶Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ°Ð¹ прылады, ÑÐ¿Ð°Ð³Ð½Ð°Ð½Ð½Ñ Ð´Ð°Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð°Ð¹ Ñхаванай аплаты за паÑлугі мабільнай ÑувÑзі або крадзÑжу вашай аÑабіÑтай інфармацыі. <ph name="BEGIN_LEARN_MORE_LINK" />Даведацца больш<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ПеразапуÑціць дапаможнік</translation>
+<translation id="2219735899272417925">Патрабуецца Ñкід прылады</translation>
<translation id="2224337661447660594">ÐÑма падключÑÐ½Ð½Ñ Ð´Ð° інтÑрнÑту</translation>
<translation id="2230458221926704099">Выпраўце падключÑнне з дапамогай <ph name="BEGIN_LINK" />праграмы дыÑгноÑтыкі<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Ðдправіць</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">Забаронена (Ñтандартна)</translation>
<translation id="2512413427717747692">Кнопка "Зрабіць Chrome Ñтандартным браўзерам". Каб зрабіць Chrome Ñтандартным браўзерам ÑÑ–ÑÑ‚Ñмы Ñž наладах iOS, націÑніце Увод</translation>
<translation id="2515629240566999685">Праверыць Ñігнал там, дзе вы знаходзіцеÑÑ.</translation>
+<translation id="2515761554693942801">Ð’Ñ‹ выбралі Touch ID Ð´Ð»Ñ ÑÐ¿Ñ€Ð°ÑžÐ´Ð¶Ð°Ð½Ð½Ñ ÐºÑƒÐ¿Ð»ÑÑž на Ñайтах, ÑÐºÑ–Ñ ÐºÐ°Ñ€Ñ‹Ñтаюцца паÑлугамі паÑтаўшчыка <ph name="PROVIDER_ORIGIN" />. ГÑÑ‚Ñ‹ паÑтаўшчык мог захаваць інфармацыю пра ваш ÑпоÑаб аплаты. Ð’Ñ‹ можаце <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Скаба знізу Ñправа</translation>
<translation id="2521736961081452453">Стварыць форму</translation>
<translation id="2523886232349826891">Захавана толькі на гÑтай прыладзе</translation>
<translation id="2524461107774643265">Дадаць болей звеÑтак</translation>
<translation id="2529899080962247600">МакÑÑ–Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ ÐºÐ¾Ð»ÑŒÐºÐ°Ñць запіÑаў у гÑтым полі: <ph name="MAX_ITEMS_LIMIT" />. ЗапіÑÑ‹, ÑÐºÑ–Ñ Ð²Ñ‹Ð¹Ð´ÑƒÑ†ÑŒ за абмежаванне, будуць праігнараваны.</translation>
+<translation id="253493526287553278">ПаглÑдзець падрабÑÐ·Ð½Ñ‹Ñ Ð·Ð²ÐµÑткі пра прапановы па прома-кодзе</translation>
<translation id="2535585790302968248">Ðдкрыць укладку Ñž Ñ€Ñжыме інкогніта Ð´Ð»Ñ Ð¿Ñ€Ñ‹Ð²Ð°Ñ‚Ð½Ð°Ð³Ð° праглÑду вÑб-Ñтаронак</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{Ñ– ÑÑˆÑ‡Ñ 1}one{Ñ– ÑÑˆÑ‡Ñ #}few{Ñ– ÑÑˆÑ‡Ñ #}many{Ñ– ÑÑˆÑ‡Ñ #}other{Ñ– ÑÑˆÑ‡Ñ #}}</translation>
<translation id="2536110899380797252">Дадаць адраÑ</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">Фатаграфічнае Ñ– лічбавае маÑтацтва</translation>
<translation id="2601150049980261779">Фільмы пра каханне</translation>
<translation id="2604589665489080024">Поп-музыка</translation>
-<translation id="2609632851001447353">ВарыÑнты</translation>
<translation id="2610561535971892504">Капіруецца адным націÑканнем</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />не будзе захоўваць<ph name="END_EMPHASIS" /> наÑтупную інфармацыю:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Дні нараджÑÐ½Ð½Ñ Ñ– імÑніны</translation>
<translation id="2677748264148917807">ВыйÑці</translation>
+<translation id="2679714844901977852">Каб бÑÑпечна Ñ– хутка афармлÑць заказы, захавайце картку Ñ– плацежную інфармацыю Ñž Ñваім Уліковым запіÑе Google <ph name="USER_EMAIL" />.</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Ðптымальны памер</translation>
<translation id="2688969097326701645">Так, працÑгнуць</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">ІнтÑрнÑÑ‚-правайдары</translation>
<translation id="2856444702002559011">ЗламыÑнікі могуць Ñпрабаваць украÑці вашы Ð´Ð°Ð½Ñ‹Ñ Ð· Ñайта <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (напрыклад, паролі, паведамленні або Ð´Ð°Ð½Ñ‹Ñ ÐºÑ€Ñдытных картак). <ph name="BEGIN_LEARN_MORE_LINK" />Даведацца больш<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ГÑÑ‚Ñ‹ Ñайт паказвае назойлівую Ñ€Ñкламу або Ñ€Ñкламу, ÑÐºÐ°Ñ ÑžÐ²Ð¾Ð´Ð·Ñ–Ñ†ÑŒ у зман.</translation>
-<translation id="286512204874376891">Каб абараніць Ð²Ð°Ñ Ð°Ð´ патÑнцыÑльнага махлÑÑ€Ñтва, замеÑÑ‚ захаванай вамі карткі будзе паказвацца віртуальнаÑ. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ТаварыÑкі</translation>
<translation id="28761159517501904">Фільмы</translation>
<translation id="2876489322757410363">Ðдбудзецца выхад з Ñ€Ñжыму інкогніта Ð´Ð»Ñ Ð¿Ð»Ð°Ñ†Ñжу праз знешнюю праграму. ПрацÑгнуць?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ДыÑк</translation>
<translation id="3162559335345991374">Сетка Wi-Fi, Ñкую вы выкарыÑтоўваеце, можа запатрабаваць ад Ð²Ð°Ñ Ð½Ð°Ð²ÐµÐ´Ð°Ñ†ÑŒ Ñе Ñтаронку ўваходу.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">ВоÑтраў</translation>
<translation id="3176929007561373547">Праверце налады прокÑÑ– або звÑрніцеÑÑ Ð´Ð° адмініÑтратара Ñеткі, каб
упÑўніцца, што прокÑÑ–-Ñервер працуе. Калі вы не згодны, што Ñ‚Ñ€Ñба
выкарыÑтоўваць прокÑÑ–-Ñервер:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">Памылка гадзінніка</translation>
<translation id="3369459162151165748">ÐўтазапчаÑткі Ñ– акÑеÑуары</translation>
<translation id="3371064404604898522">Зрабіць Chrome Ñтандартным браўзерам</translation>
-<translation id="3371076217486966826">Сайт "<ph name="URL" />" запытвае дазвол на наÑÑ‚ÑƒÐ¿Ð½Ñ‹Ñ Ð´Ð·ÐµÑнні:
- • Ñтварыць 3D-карту вашага аÑÑÑ€Ð¾Ð´Ð´Ð·Ñ Ñ– адÑочваць Ñтановішча камеры;
- • выкарыÑтоўваць камеру.</translation>
<translation id="337363190475750230">ІніцыÑÐ»Ñ–Ð·Ð°Ñ†Ñ‹Ñ ÑкаÑавана</translation>
<translation id="3375754925484257129">ЗапуÑціць праверку бÑÑпекі Chrome</translation>
<translation id="3377144306166885718">Сервер выкарыÑтоўвае ÑžÑтарÑлую верÑÑ–ÑŽ пратакола TLS.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">ÐÐ´Ñ€Ð°Ñ Ð´Ð°Ñтаўкі</translation>
<translation id="3402261774528610252">Ð”Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÑ– гÑтага Ñайта выкарыÑтоўвалаÑÑ Ð¿Ð°Ð´ÐºÐ»ÑŽÑ‡Ñнне праз ÑаÑтарÑлы пратакол TLS 1.0 або TLS 1.1, Ñкі Ñž далейшым будзе адключаны. ПаÑÐ»Ñ Ñго адключÑÐ½Ð½Ñ ÐºÐ°Ñ€Ñ‹Ñтальнікі не змогуць загружаць гÑÑ‚Ñ‹ Ñайт. Ðа Ñерверы Ñ‚Ñ€Ñба ўключыць пратакол TLS верÑÑ–Ñ– 1.2 або навейшай.</translation>
<translation id="3405664148539009465">Ðаладзіць шрыфты</translation>
+<translation id="3407789382767355356">уваход праз ÑÑ‚Ð°Ñ€Ð¾Ð½Ð½Ñ–Ñ ÑÑрвіÑÑ‹</translation>
<translation id="3409896703495473338">Кіраваць наладамі бÑÑпекі</translation>
<translation id="3414952576877147120">Памер:</translation>
<translation id="3417660076059365994">Ð—Ð°Ð¿Ð°Ð¼Ð¿Ð°Ð²Ð°Ð½Ñ‹Ñ Ð°Ð±Ð¾ Ð´Ð°Ð»ÑƒÑ‡Ð°Ð½Ñ‹Ñ Ñ„Ð°Ð¹Ð»Ñ‹ пераÑылаюцца Ð´Ð»Ñ Ð°Ð½Ð°Ð»Ñ–Ð·Ñƒ Ñž Google Cloud або Ñ‚Ñ€Ñцім бакам: напрыклад, Ð´Ð»Ñ Ð¿Ñ€Ð°Ð²ÐµÑ€ÐºÑ– на наÑўнаÑць канфідÑнцыÑльных даных або шкодных праграм.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">РаÑклад паказаў у Ñ‚Ñатрах Ñ– кінатÑатрах</translation>
<translation id="3479552764303398839">Ðе зараз</translation>
<translation id="3484560055331845446">Ð’Ñ‹ можаце Ñтраціць доÑтуп да Ñвайго Уліковага запіÑу Google. Chrome Ñ€Ñкамендуе змÑніць пароль. Ð’Ð°Ñ Ð¿Ð°Ð¿Ñ€Ð¾ÑÑць увайÑці ва ўліковы запіÑ.</translation>
+<translation id="3484861421501147767">Ðапамін: захаваны прома-код даÑтупны</translation>
<translation id="3487845404393360112">Латок 4</translation>
<translation id="3495081129428749620">ЗнайÑці на Ñтаронцы "<ph name="PAGE_TITLE" />"</translation>
<translation id="350069200438440499">Ðазва файла:</translation>
@@ -1055,6 +1059,7 @@
<translation id="3810973564298564668">Кіраваць</translation>
<translation id="3816482573645936981">ЗначÑнне (замененае)</translation>
<translation id="382518646247711829">Калі вы выкарыÑтоўваеце прокÑÑ–-Ñервер...</translation>
+<translation id="3826050100957962900">Уваход праз ÑÑ‚Ð°Ñ€Ð¾Ð½Ð½Ñ–Ñ ÑÑрвіÑÑ‹</translation>
<translation id="3827112369919217609">ÐбÑалютны</translation>
<translation id="3827666161959873541">СÑÐ¼ÐµÐ¹Ð½Ñ‹Ñ Ñ„Ñ–Ð»ÑŒÐ¼Ñ‹</translation>
<translation id="3828924085048779000">Ðеабходна задаць фразу-пароль</translation>
@@ -1067,9 +1072,9 @@
<translation id="3858027520442213535">Ðбнавіць дату Ñ– чаÑ</translation>
<translation id="3858860766373142691">Ðазва</translation>
<translation id="3872834068356954457">Ðавука</translation>
+<translation id="3875783148670536197">Паказаць</translation>
<translation id="3881478300875776315">Паказваць менш радкоў</translation>
<translation id="3884278016824448484">Канфлікт ідÑнтыфікатара прылады</translation>
-<translation id="3885155851504623709">Прыход</translation>
<translation id="388632593194507180">Ð’Ñ‹Ñўлена ажыццÑўленне маніторынгу</translation>
<translation id="3886948180919384617">Укладчык 3</translation>
<translation id="3890664840433101773">Дадаць Ð°Ð´Ñ€Ð°Ñ Ñлектроннай пошты</translation>
@@ -1099,9 +1104,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> заблакіраваны</translation>
<translation id="3978338123949022456">РÑжым пошуку. Каб выканаць пошук праз <ph name="KEYWORD_SUFFIX" />, увÑдзіце запыт Ñ– націÑніце Увод</translation>
<translation id="398470910934384994">Птушкі</translation>
+<translation id="3985750352229496475">Кіраваць адраÑамі...</translation>
<translation id="3986705137476756801">Выключыць Ð†Ð¼Ð³Ð½ÐµÐ½Ð½Ñ‹Ñ Ñубцітры</translation>
<translation id="3987940399970879459">Менш за 1 МБ</translation>
<translation id="3990250421422698716">Ð’ÐµÐ»Ñ–Ñ‡Ñ‹Ð½Ñ Ð·Ñ€ÑƒÑ…Ñƒ</translation>
+<translation id="3992684624889376114">Пра гÑту Ñтаронку</translation>
<translation id="3996311196211510766">Сайт "<ph name="ORIGIN" />" патрабуе, каб на ÑžÑе запыты да Ñго раÑпаўÑюджвалаÑÑ
палітыка крыніцы, але гÑту палітыку зараз прымÑніць нельга.</translation>
<translation id="4006465311664329701">СпоÑабы аплаты, прапановы Ñ– адраÑÑ‹, ÑÐºÑ–Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўваюць Google Pay</translation>
@@ -1226,6 +1233,7 @@
<translation id="4305666528087210886">Ðе ўдалоÑÑ Ð°Ñ‚Ñ€Ñ‹Ð¼Ð°Ñ†ÑŒ доÑтуп да файла</translation>
<translation id="4306529830550717874">Захаваць адраÑ?</translation>
<translation id="4306812610847412719">буфер абмену</translation>
+<translation id="4310070645992025887">Пошук па шлÑхах</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Блакіраваць (Ñтандартна)</translation>
<translation id="4314815835985389558">Кіраванне ÑінхранізацыÑй</translation>
@@ -1276,6 +1284,7 @@
<translation id="4435702339979719576">паштоўка)</translation>
<translation id="443673843213245140">ВыкарыÑтанне прокÑÑ–-Ñервера выключана, але ÑÑžÐ½Ð°Ñ ÐºÐ°Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ‹Ñ Ð¿Ñ€Ð¾ÐºÑÑ– вызначана.</translation>
<translation id="4441832193888514600">Ігнаруецца, бо гÑÑ‚Ñ‹Ñ Ð¿Ñ€Ð°Ð²Ñ–Ð»Ñ‹ могуць задавацца толькі Ñк палітыка Ð´Ð»Ñ ÐºÐ°Ñ€Ñ‹Ñтальнікаў на ўзроўні воблака.</translation>
+<translation id="4442470707340296952">Укладкі Chrome</translation>
<translation id="4450893287417543264">Больш не паказваць</translation>
<translation id="4451135742916150903">Сайт можа запытваць дазвол на падключÑнне да прылад з HID</translation>
<translation id="4452328064229197696">Пароль, Ñкі вы толькі што выкарыÑталі, быў раÑкрыты пры ўцечцы даных. Каб абараніць Ñвае ÑžÐ»Ñ–ÐºÐ¾Ð²Ñ‹Ñ Ð·Ð°Ð¿Ñ–ÑÑ‹, праверце Ð·Ð°Ñ…Ð°Ð²Ð°Ð½Ñ‹Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ– Ñž Менеджары паролÑÑž Google.</translation>
@@ -1414,6 +1423,7 @@
<translation id="483241715238664915">Уключыць папÑÑ€Ñджанні</translation>
<translation id="4834250788637067901">СпоÑабы аплаты, прапановы Ñ– адраÑÑ‹, ÑÐºÑ–Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўваюць Google Pay</translation>
<translation id="4838327282952368871">Летуценны</translation>
+<translation id="4839087176073128681">Плаціце карткай хутчÑй Ñ– абаранÑйце Ñе Ð´Ð°Ð½Ñ‹Ñ Ð· дапамогай перадавых Ñ‚Ñхналогій бÑÑпекі Google.</translation>
<translation id="4840250757394056958">ПраглÑдзець гіÑторыю Chrome</translation>
<translation id="484462545196658690">Ðўтаматычна</translation>
<translation id="484671803914931257">Ðтрымайце Ñкідку ад прадаўца "<ph name="MERCHANT_NAME" />" Ñ– не толькі</translation>
@@ -1476,6 +1486,7 @@
<translation id="5011561501798487822">Ð’Ñ‹ÑÑžÐ»ÐµÐ½Ð°Ñ Ð¼Ð¾Ð²Ð°</translation>
<translation id="5015510746216210676">Ðазва прылады:</translation>
<translation id="5017554619425969104">Скапіраваны вамі Ñ‚ÑкÑÑ‚</translation>
+<translation id="5017828934289857214">Ðагадаць пазней</translation>
<translation id="5018422839182700155">Ðе ўдалоÑÑ Ð°Ð´ÐºÑ€Ñ‹Ñ†ÑŒ Ñтаронку</translation>
<translation id="5019198164206649151">РÑзервовае Ñховішча Ñž кепÑкім Ñтане</translation>
<translation id="5020776957610079374">Музыка з уÑÑго Ñвету</translation>
@@ -1495,6 +1506,7 @@
<translation id="5051305769747448211">Ð†Ð¼Ð¿Ñ€Ð°Ð²Ñ–Ð·Ð°Ñ†Ñ‹Ð¹Ð½Ð°Ñ ÐºÐ°Ð¼ÐµÐ´Ñ‹Ñ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Каб адправіць гÑÑ‚Ñ‹ файл з дапамогай функцыі "Ðбагульванне паблізу", вызваліце меÑца на прыладзе (<ph name="DISK_SPACE_SIZE" />)}one{Каб адправіць гÑÑ‚Ñ‹Ñ Ñ„Ð°Ð¹Ð»Ñ‹ з дапамогай функцыі "Ðбагульванне паблізу", вызваліце меÑца на прыладзе (<ph name="DISK_SPACE_SIZE" />)}few{Каб адправіць гÑÑ‚Ñ‹Ñ Ñ„Ð°Ð¹Ð»Ñ‹ з дапамогай функцыі "Ðбагульванне паблізу", вызваліце меÑца на прыладзе (<ph name="DISK_SPACE_SIZE" />)}many{Каб адправіць гÑÑ‚Ñ‹Ñ Ñ„Ð°Ð¹Ð»Ñ‹ з дапамогай функцыі "Ðбагульванне паблізу", вызваліце меÑца на прыладзе (<ph name="DISK_SPACE_SIZE" />)}other{Каб адправіць гÑÑ‚Ñ‹Ñ Ñ„Ð°Ð¹Ð»Ñ‹ з дапамогай функцыі "Ðбагульванне паблізу", вызваліце меÑца на прыладзе (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">Ðртыкулы Ð´Ð»Ñ Ð²Ð°Ñ</translation>
+<translation id="5060483733937416656">Ð’Ñ‹ выбралі Windows Hello Ð´Ð»Ñ ÑÐ¿Ñ€Ð°ÑžÐ´Ð¶Ð°Ð½Ð½Ñ ÐºÑƒÐ¿Ð»ÑÑž на Ñайтах, ÑÐºÑ–Ñ ÐºÐ°Ñ€Ñ‹Ñтаюцца паÑлугамі паÑтаўшчыка <ph name="PROVIDER_ORIGIN" />. ГÑÑ‚Ñ‹ паÑтаўшчык мог захаваць інфармацыю пра ваш ÑпоÑаб аплаты. Ð’Ñ‹ можаце <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Вы мелі на ўвазе "<ph name="LOOKALIKE_DOMAIN" />"?</translation>
<translation id="5066056036849835175">ГіÑÑ‚Ð¾Ñ€Ñ‹Ñ Ð´Ñ€ÑƒÐºÑƒ</translation>
<translation id="5068234115460527047">Хедж-фонды</translation>
@@ -1508,10 +1520,8 @@
<translation id="5087286274860437796">Ðа гÑÑ‚Ñ‹ момант Ñертыфікат Ñервера неÑапраўдны.</translation>
<translation id="5087580092889165836">Дадаць картку</translation>
<translation id="5088142053160410913">Паведамленне аператару</translation>
-<translation id="5089810972385038852">Штат</translation>
<translation id="5093232627742069661">Z-падобны згіб</translation>
<translation id="5094747076828555589">Серверу не ўдалоÑÑ Ð´Ð°ÐºÐ°Ð·Ð°Ñ†ÑŒ, што гÑта <ph name="DOMAIN" />; Chromium не давÑрае Ñго Ñертыфікату бÑÑпекі. Прычынай могуць быць нÑÐ¿Ñ€Ð°Ð²Ñ–Ð»ÑŒÐ½Ñ‹Ñ Ð½Ð°Ð»Ð°Ð´Ñ‹ або зламыÑнік, Ñкі Ñпрабуе перахапіць падключÑнне.</translation>
-<translation id="5095208057601539847">ПравінцыÑ</translation>
<translation id="5097099694988056070">СтатыÑтыка па абÑталÑванні, напрыклад загружанаÑць працÑÑара Ñ– выкарыÑтанне аператыўнай памÑці</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Сайт не з'ÑўлÑецца бÑÑпечным</translation>
@@ -1549,6 +1559,7 @@
<translation id="5171045022955879922">Знайдзіце або ўвÑдзіце URL-адраÑ</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Машына</translation>
+<translation id="5177076414499237632">Даведацца пра крыніцу Ñ– Ñ‚Ñму гÑтай Ñтаронкі</translation>
<translation id="5179510805599951267">Мова Ñтаронкі – не <ph name="ORIGINAL_LANGUAGE" />? Паведаміце пра памылку</translation>
<translation id="518639307526414276">Тавары Ñ– корм Ð´Ð»Ñ Ñ…Ð°Ñ‚Ð½Ñ–Ñ… жывёл</translation>
<translation id="5190835502935405962">ПанÑль закладак</translation>
@@ -1709,6 +1720,7 @@
<translation id="5624120631404540903">Кіраваць паролÑмі</translation>
<translation id="5629630648637658800">Ðе ўдалоÑÑ Ð·Ð°Ð³Ñ€ÑƒÐ·Ñ–Ñ†ÑŒ налады палітыкі</translation>
<translation id="5631439013527180824">ÐеÑапраўдны маркер Ð´Ð»Ñ ÐºÑ–Ñ€Ð°Ð²Ð°Ð½Ð½Ñ Ð¿Ñ€Ñ‹Ð»Ð°Ð´Ð°Ð¹</translation>
+<translation id="5632485077360054581">Паказаць</translation>
<translation id="5633066919399395251">ЗламыÑнікі на Ñайце <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> могуць імкнуцца ÑžÑталÑваць небÑÑÐ¿ÐµÑ‡Ð½Ñ‹Ñ Ð¿Ñ€Ð°Ð³Ñ€Ð°Ð¼Ñ‹ на вашым камп'ютары, ÑÐºÑ–Ñ ÐºÑ€Ð°Ð´ÑƒÑ†ÑŒ або выдалÑюць вашу інфармацыю (напрыклад, фота, паролі, паведамленні, Ð´Ð°Ð½Ñ‹Ñ ÐºÑ€Ñдытных картак). <ph name="BEGIN_LEARN_MORE_LINK" />Даведацца больш<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Падманлівае змеÑціва заблакіравана.</translation>
<translation id="5633259641094592098">ÐšÑƒÐ»ÑŒÑ‚Ð°Ð²Ñ‹Ñ Ñ– аўтарÑÐºÑ–Ñ Ñ„Ñ–Ð»ÑŒÐ¼Ñ‹</translation>
@@ -1826,6 +1838,7 @@
<translation id="5989320800837274978">Ðе ўказаны ні фікÑÐ°Ð²Ð°Ð½Ñ‹Ñ Ð¿Ñ€Ð¾ÐºÑÑ–-Ñерверы, ні URL-Ð°Ð´Ñ€Ð°Ñ ÑцÑÐ½Ð°Ñ€Ñ‹Ñ .pac.</translation>
<translation id="5992691462791905444">Z-падобны згіб (Ñ‚ÑÑ…Ð½Ñ–Ñ‡Ð½Ð°Ñ Ð´Ð°ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ‹Ñ)</translation>
<translation id="5995727681868049093">Кіраваць параметрамі даных, прыватнаÑці Ñ– бÑÑпекі ва Уліковым запіÑе Google</translation>
+<translation id="5997247540087773573">ВыкарыÑтаны вамі пароль трапіў у ÑÐ¿Ñ–Ñ ÑƒÐ·Ð»Ð°Ð¼Ð°Ð½Ñ‹Ñ… даных. Каб абараніць Ñвае ÑžÐ»Ñ–ÐºÐ¾Ð²Ñ‹Ñ Ð·Ð°Ð¿Ñ–ÑÑ‹, зараз жа змÑніце Ñго Ñ– праверце Ð·Ð°Ñ…Ð°Ð²Ð°Ð½Ñ‹Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ– Ñž Менеджары паролÑÑž Google.</translation>
<translation id="6000758707621254961">Вынікаў па запыце "<ph name="SEARCH_TEXT" />": <ph name="RESULT_COUNT" /></translation>
<translation id="6006484371116297560">КлаÑічнаÑ</translation>
<translation id="6008122969617370890">Парадак "ад N да 1"</translation>
@@ -1921,7 +1934,6 @@
<translation id="627746635834430766">Каб наÑтупны раз плаціць хутчÑй, захавайце картку Ñ– Ð°Ð´Ñ€Ð°Ñ Ð´Ð»Ñ Ð²Ñ‹ÑÑ‚Ð°ÑžÐ»ÐµÐ½Ð½Ñ Ñ€Ð°Ñ…ÑƒÐ½ÐºÐ°Ñž ва Уліковым запіÑе Google.</translation>
<translation id="6279183038361895380">ÐаціÑніце "<ph name="ACCELERATOR" />", каб убачыць курÑор</translation>
<translation id="6280223929691119688">Ðемагчыма адправіць на гÑÑ‚Ñ‹ адраÑ. Выберыце іншы адраÑ.</translation>
-<translation id="6282194474023008486">Паштовы індÑкÑ</translation>
<translation id="6285507000506177184">Кнопка "Кіраваць Ñпампоўкамі Ñž Chrome". Каб кіраваць файламі, ÑÐºÑ–Ñ Ð²Ñ‹ Ñпампавалі Ñž браўзеры Chrome, націÑніце Enter</translation>
<translation id="6289939620939689042">Колер Ñтаронкі</translation>
<translation id="6290238015253830360">РÑÐºÐ°Ð¼ÐµÐ½Ð´Ð°Ð²Ð°Ð½Ñ‹Ñ Ð²Ð°Ð¼ артыкулы з'ÑвÑцца тут</translation>
@@ -1943,6 +1955,7 @@
<translation id="6337133576188860026">Вызваліцца да <ph name="SIZE" />. ÐÐµÐºÐ°Ñ‚Ð¾Ñ€Ñ‹Ñ Ñайты могуць загружацца павальней пры наÑтупным наведванні.</translation>
<translation id="6337534724793800597">Фільтр палітык па назве</translation>
<translation id="6340739886198108203">Згодна з зададзенай адмініÑтратарам палітыкай, не Ñ€Ñкамендуецца рабіць здымкі або відÑазапіÑÑ‹ Ñкрана, калі паказваецца канфідÑнцыÑльнае змеÑціва:</translation>
+<translation id="6348220984832452017">Ð”Ð·ÐµÑŽÑ‡Ñ‹Ñ Ð²Ð°Ñ€Ñ‹Ñнты</translation>
<translation id="6349101878882523185">УÑталÑваць праграму "<ph name="APP_NAME" />"</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ÐÑма}=1{1 пароль (Ð´Ð»Ñ Ð´Ð°Ð¼ÐµÐ½Ð°Ñž <ph name="DOMAIN_LIST" />, Ñінхранізаваны)}=2{2 паролі (Ð´Ð»Ñ Ð´Ð°Ð¼ÐµÐ½Ð°Ñž <ph name="DOMAIN_LIST" />, Ñінхранізаваны)}one{# пароль (Ð´Ð»Ñ Ð´Ð°Ð¼ÐµÐ½Ð°Ñž <ph name="DOMAIN_LIST" />, Ñінхранізаваны)}few{# паролі (Ð´Ð»Ñ Ð´Ð°Ð¼ÐµÐ½Ð°Ñž <ph name="DOMAIN_LIST" />, Ñінхранізаваны)}many{# паролÑÑž (Ð´Ð»Ñ Ð´Ð°Ð¼ÐµÐ½Ð°Ñž <ph name="DOMAIN_LIST" />, Ñінхранізаваны)}other{#Â Ð¿Ð°Ñ€Ð¾Ð»Ñ (Ð´Ð»Ñ Ð´Ð°Ð¼ÐµÐ½Ð°Ñž <ph name="DOMAIN_LIST" />, Ñінхранізаваны)}}</translation>
<translation id="6355392890578844978">ГÑÑ‚Ñ‹ браўзер не знаходзіцца пад кіраваннем кампаніі або іншай арганізацыі. Ðднак Ñама прылада можа знаходзіцца пад знешнім кіраваннем. <ph name="BEGIN_LINK" />Даведацца больш<ph name="END_LINK" /></translation>
@@ -1974,7 +1987,6 @@
<translation id="643051589346665201">ЗмÑніць пароль Google</translation>
<translation id="6433490469411711332">Змена кантактных звеÑтак</translation>
<translation id="6433595998831338502">ХоÑÑ‚ <ph name="HOST_NAME" /> адмовіўÑÑ Ð°Ð´ падключÑннÑ.</translation>
-<translation id="6438025220577812695">Я змÑню Ñго ÑамаÑтойна</translation>
<translation id="6440503408713884761">Ігнаруецца</translation>
<translation id="6443406338865242315">Ð¯ÐºÑ–Ñ Ð¿Ð°ÑˆÑ‹Ñ€Ñнні Ñ– ўбудовы вы ÑžÑталÑвалі.</translation>
<translation id="6446163441502663861">Kahu (канверт)</translation>
@@ -2104,9 +2116,9 @@
<translation id="6828866289116430505">Генетыка</translation>
<translation id="6831043979455480757">ПераклаÑці</translation>
<translation id="6833752742582340615">Каб бÑÑпечна Ñ– хутка афармлÑць заказы, захавайце картку Ñ– плацежную інфармацыю Ñž Ñваім Уліковым запіÑе Google.</translation>
-<translation id="6839929833149231406">ВоблаÑць</translation>
<translation id="6846340164947227603">ВыкарыÑтаць нумар віртуальнай карткі...</translation>
<translation id="6852204201400771460">Перазагрузіць праграму?</translation>
+<translation id="6857776781123259569">Кіраваць паролÑмі...</translation>
<translation id="686485648936420384">Ð¡Ð¿Ð°Ð¶Ñ‹Ð²ÐµÑ†ÐºÑ–Ñ Ñ€ÑÑурÑÑ‹</translation>
<translation id="6865412394715372076">ГÑту картку Ñпраўдзіць пакуль што не ўдаецца</translation>
<translation id="6869334554832814367">КрÑдыты фізічным аÑобам</translation>
@@ -2155,7 +2167,6 @@
<translation id="6965978654500191972">Прылада</translation>
<translation id="696703987787944103">ПерцÑпцыйны</translation>
<translation id="6968269510885595029">ВыкарыÑтайце Ñвой ключ бÑÑпекі</translation>
-<translation id="6970216967273061347">Раён</translation>
<translation id="6971439137020188025">Хутка Ñтварыць новую прÑзентацыю Google у праграме "ПрÑзентацыі"</translation>
<translation id="6972629891077993081">Прылады HID</translation>
<translation id="6973656660372572881">Вызначаюцца Ñ– фікÑÐ°Ð²Ð°Ð½Ñ‹Ñ Ð¿Ñ€Ð¾ÐºÑÑ–-Ñерверы, Ñ– URL-Ð°Ð´Ñ€Ð°Ñ ÑцÑÐ½Ð°Ñ€Ñ‹Ñ .pac.</translation>
@@ -2194,7 +2205,6 @@
<translation id="7081308185095828845">Ðа жаль, гÑта Ñ„ÑƒÐ½ÐºÑ†Ñ‹Ñ Ð½Ð° прыладзе недаÑтупнаÑ</translation>
<translation id="7083258188081898530">Латок 9</translation>
<translation id="7086090958708083563">Запампоўка запытана карыÑтальнікам</translation>
-<translation id="7087282848513945231">Ðкруга</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. Каб кіраваць дазволамі Ñ– данымі, ÑÐºÑ–Ñ Ð·Ð°Ñ…Ð¾ÑžÐ²Ð°ÑŽÑ†Ñ†Ð° на Ñайтах, праз налады Chrome, націÑніце Tab, затым Enter</translation>
<translation id="7096937462164235847">СапраўднаÑць гÑтага вÑб-Ñайта не Ñпраўджана.</translation>
<translation id="7101893872976785596">Фільмы жахаў</translation>
@@ -2213,10 +2223,10 @@
<ph name="LIST_ITEM" />звеÑткі, ÑÐºÑ–Ñ Ð±Ñ‹Ð»Ñ– ўведзены Ñž формах.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Ðдпраўка на гÑÑ‚Ñ‹ Ð°Ð´Ñ€Ð°Ñ Ð½ÐµÐ¼Ð°Ð³Ñ‡Ñ‹Ð¼Ð°Ñ. Выберыце іншы адраÑ.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Ваш адмініÑтратар забараніў капіраванне гÑÑ‚Ñ‹Ñ… даных.</translation>
<translation id="7135130955892390533">Паказаць Ñтан</translation>
<translation id="7138472120740807366">СпоÑаб даÑтаўкі</translation>
-<translation id="7139724024395191329">Эмірат</translation>
<translation id="7139892792842608322">ÐÑноўны латок</translation>
<translation id="714064300541049402">Зрух відарыÑа па воÑÑ– X, бок 2</translation>
<translation id="7152423860607593928">Number-14 (канверт)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">ЗламыÑнікі на гÑтым Ñайце могуць падмануць Ð²Ð°Ñ Ñ– прымуÑіць уÑталÑваць праграмы, ÑÐºÑ–Ñ Ð¿Ð°ÑˆÐºÐ¾Ð´Ð·Ñць вашай працы Ñž інтÑрнÑце (напрыклад, зменай галоўнай Ñтаронкі або паказам дадатковай Ñ€Ñкламы на Ñайтах, ÑÐºÑ–Ñ Ð²Ñ‹ наведваеце).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ДзеÑнні з данымі, пазначанымі Ñк канфідÑнцыÑÐ»ÑŒÐ½Ñ‹Ñ (1 дзеÑнне з моманту ўваходу). <ph name="BEGIN_LINK" />Даведацца больш<ph name="END_LINK" />}one{ДзеÑнні з данымі, пазначанымі Ñк канфідÑнцыÑÐ»ÑŒÐ½Ñ‹Ñ (# дзеÑнне з моманту ўваходу). <ph name="BEGIN_LINK" />Даведацца больш<ph name="END_LINK" />}few{ДзеÑнні з данымі, пазначанымі Ñк канфідÑнцыÑÐ»ÑŒÐ½Ñ‹Ñ (# дзеÑнні з моманту ўваходу). <ph name="BEGIN_LINK" />Даведацца больш<ph name="END_LINK" />}many{ДзеÑнні з данымі, пазначанымі Ñк канфідÑнцыÑÐ»ÑŒÐ½Ñ‹Ñ (# дзеÑннÑÑž з моманту ўваходу). <ph name="BEGIN_LINK" />Даведацца больш<ph name="END_LINK" />}other{ДзеÑнні з данымі, пазначанымі Ñк канфідÑнцыÑÐ»ÑŒÐ½Ñ‹Ñ (# дзеÑÐ½Ð½Ñ Ð· моманту ўваходу). <ph name="BEGIN_LINK" />Даведацца больш<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ÐŸÐ°ÑˆÑ‚Ð¾Ð²Ð°Ñ Ñкрынка 6</translation>
+<translation id="7675325315208090829">Ðаладзіць ÑпоÑабы аплаты...</translation>
<translation id="7676643023259824263">Пошук Ñ‚ÑкÑту з буфера абмену: "<ph name="TEXT" />"</translation>
<translation id="7679367271685653708">Ðзнаёміцца з гіÑторыÑй праглÑду Ñайтаў або кіраваць ёй у наладах Chrome</translation>
<translation id="7679947978757153706">БейÑбол</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">Край</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">У тым жа парадку, рабочым бокам уверх</translation>
-<translation id="777702478322588152">ПрÑфектура</translation>
<translation id="7791011319128895129">Ðе апублікавана</translation>
<translation id="7791196057686275387">СтоÑ</translation>
<translation id="7791543448312431591">Дадаць</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">ПрафеÑÑ–Ð¹Ð½Ð°Ñ Ñ– беÑÐ¿ÐµÑ€Ð°Ð¿Ñ‹Ð½Ð½Ð°Ñ Ð°Ð´ÑƒÐºÐ°Ñ†Ñ‹Ñ</translation>
<translation id="8057711352706143257">Праграма "<ph name="SOFTWARE_NAME" />" наладжана нÑправільна. Праблема звычайна вырашаецца выдаленнем праграмы "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ВытворчаÑць харчовых прадуктаў</translation>
-<translation id="8066955247577885446">Ðа жаль, нешта пайшло не так.</translation>
<translation id="8067872629359326442">Ð’Ñ‹ толькі што ўвÑлі Ñвой пароль на Ñайце, вÑдомым падманнымі паводзінамі. Chromium можа дапамагчы. ÐаціÑніце "Ðбараніць уліковы запіÑ", каб змÑніць пароль Ñ– апавÑÑціць Google аб пагрозе вашаму ўліковаму запіÑу.</translation>
<translation id="8070439594494267500">Значок праграмы</translation>
<translation id="8074253406171541171">10x13 (канверт)</translation>
<translation id="8075736640322370409">Хутка Ñтварыць новую табліцу Google</translation>
<translation id="8075898834294118863">Кіраваць наладамі Ñайтаў</translation>
+<translation id="8076492880354921740">Укладкі</translation>
<translation id="8078141288243656252">Ðельга дадаваць анатацыі, калі дакумент павернуты</translation>
<translation id="8079031581361219619">Перазагрузіць Ñайт?</translation>
<translation id="8081087320434522107">Седаны</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ðалады</translation>
+<translation id="8428634594422941299">Зразумела</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. Каб кіраваць параметрамі файлаў cookie праз налады Chrome, націÑніце Tab, затым Enter</translation>
<translation id="8433057134996913067">Будзе выкананы выхад з большаÑці вÑб-Ñайтаў.</translation>
<translation id="8434840396568290395">Ð¥Ð°Ñ‚Ð½Ñ–Ñ Ð¶Ñ‹Ð²Ñ‘Ð»Ñ‹</translation>
@@ -2792,6 +2803,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> – ваш код Ð´Ð»Ñ <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Дадаць гÑту ўкладку Ñž закладкі</translation>
<translation id="8751426954251315517">Паўтарыце Ñпробу пазней</translation>
+<translation id="8757526089434340176">Прапанова Google Pay даÑтупнаÑ</translation>
<translation id="8758885506338294482">КіберÑÐ¿Ð°Ñ€Ñ‚Ñ‹ÑžÐ½Ñ‹Ñ Ñпаборніцтвы</translation>
<translation id="8759274551635299824">ТÑрмін дзеÑÐ½Ð½Ñ Ð³Ñтай карткі ÑкончыўÑÑ</translation>
<translation id="87601671197631245">ГÑÑ‚Ñ‹ Ñайт выкарыÑтоўвае ÑžÑтарÑлую канфігурацыю бÑÑпекі, таму звеÑткі пра Ð²Ð°Ñ (напрыклад, паролі, паведамленні або нумары крÑдытных картак) пры Ñ–Ñ… адпраўцы на гÑÑ‚Ñ‹ Ñайт могуць быць раÑкрыты.</translation>
@@ -2799,6 +2811,7 @@
<translation id="8763927697961133303">Прылада USB</translation>
<translation id="8763986294015493060">Закрыць уÑе Ð°Ð´ÐºÑ€Ñ‹Ñ‚Ñ‹Ñ Ð²Ð¾ÐºÐ½Ñ‹ Ñž Ñ€Ñжыме інкогніта</translation>
<translation id="8766943070169463815">Ðркуш аўтÑнтыфікацыі ўліковых даных Ð´Ð»Ñ Ð±ÑÑпечных плацÑжоў адкрыты</translation>
+<translation id="8767765348545497220">Закрыць уÑплывальнае апавÑшчÑнне даведкі</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Матацыклы</translation>
<translation id="8790007591277257123">&amp;Узнавіць выдаленне</translation>
@@ -2811,6 +2824,7 @@
<translation id="8806285662264631610">Тавары Ð´Ð»Ñ Ð²Ð°Ð½Ð½Ñ‹ Ñ– цела</translation>
<translation id="8807160976559152894">ÐбрÑзка паÑÐ»Ñ ÐºÐ¾Ð¶Ð½Ð°Ð¹ Ñтаронкі</translation>
<translation id="8808828119384186784">Ðалады Chrome</translation>
+<translation id="8813277370772331957">Ðагадаць пазней</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Каб абнавіць браўзер Chrome праз Ñго налады, націÑніце Tab, затым Enter</translation>
<translation id="8820817407110198400">Закладкі</translation>
<translation id="882338992931677877">Слот ручной падачы</translation>
@@ -2990,6 +3004,7 @@
<translation id="988159990683914416">Зборка Ð´Ð»Ñ Ñ€Ð°Ñпрацоўшчыкаў</translation>
<translation id="989988560359834682">РÑдагаваць адраÑ</translation>
<translation id="991413375315957741">датчыкі руху або ÑвÑтла</translation>
+<translation id="992110854164447044">Каб абараніць Ð²Ð°Ñ Ð°Ð´ патÑнцыÑльнага махлÑÑ€Ñтва, замеÑÑ‚ захаванай вамі карткі будзе паказвацца віртуальнаÑ. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992256792861109788">Ружовы</translation>
<translation id="992432478773561401">Праграма "<ph name="SOFTWARE_NAME" />" нÑправільна ÑžÑталÑвана на камп'ютар або Ñž Ñетцы:
&lt;ul&gt;
diff --git a/chromium/components/strings/components_strings_bg.xtb b/chromium/components/strings/components_strings_bg.xtb
index ced656fcafe..ce0b3411f03 100644
--- a/chromium/components/strings/components_strings_bg.xtb
+++ b/chromium/components/strings/components_strings_bg.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Безвъзмездна и финанÑова помощ и Ñтипендии</translation>
<translation id="1048785276086539861">Когато редактирате поÑÑнениÑта, изгледът на документа ще бъде на една Ñтраница</translation>
<translation id="1050038467049342496">Затворете другите приложениÑ.</translation>
+<translation id="1053959602163383901">Избрахте опциÑта за потвърждаване чрез уÑтройÑтво за удоÑтоверÑване в уебÑайтовете, които използват <ph name="PROVIDER_ORIGIN" />. Възможно е този доÑтавчик да е Ñъхранил Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° начина ви на плащане. Можете обаче да <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;ОтмÑна на добавÑнето</translation>
<translation id="1056663316309890343">ФотографÑки Ñофтуер</translation>
<translation id="1056898198331236512">Предупреждение</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">Ðачин на вземане</translation>
<translation id="1281476433249504884">Стакер 1</translation>
<translation id="1285320974508926690">Този Ñайт да не Ñе превежда никога</translation>
+<translation id="1288548991597756084">Запазване на картата надеждно</translation>
<translation id="1292571435393770077">Тава 16</translation>
<translation id="1292701964462482250">„Софтуер на компютъра ви пречи на Chrome да Ñе Ñвърже безопаÑно Ñ Ð¼Ñ€ÐµÐ¶Ð°Ñ‚Ð°â€œ (Ñамо на компютри под Windows)</translation>
<translation id="1294154142200295408">Варианти във формат за ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¸Ñ Ñ€ÐµÐ´</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;За да отÑтраните грешката, кликнете върху &lt;strong&gt;Свързване&lt;/strong&gt; на Ñтраницата, коÑто опитвате да отворите.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Ландшафтен дизайн</translation>
<translation id="1513706915089223971">СпиÑък на запиÑите в иÑториÑта</translation>
+<translation id="1516097932025103760">Ще бъде шифрована и запазена надеждно, а кодът за проверка никога не Ñе ÑъхранÑва.</translation>
<translation id="1517433312004943670">ТелефонниÑÑ‚ номер е задължителен</translation>
<translation id="1519264250979466059">Дата на верÑиÑта</translation>
<translation id="1521159554480556801">Влакна и текÑтилни изкуÑтва</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Запазване и попълване на начини на плащане</translation>
<translation id="1663943134801823270">Картите и адреÑите Ñа от Chrome. Можете да ги управлÑвате от <ph name="BEGIN_LINK" />наÑтройките<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">От Ñега нататък Ñтраниците на <ph name="SOURCE_LANGUAGE" /> ще Ñе превеждат на <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Платете Ñ <ph name="CARD_DETAIL" />, за да използвате офертата</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> на <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">КъÑата Ñтрана напред</translation>
<translation id="168693727862418163">СтойноÑтта на това правило не бе потвърдена ÑпрÑмо Ñъответната Ñхема и ще бъде пренебрегната.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Сензори за движение</translation>
<translation id="1717494416764505390">ПощенÑка ÐºÑƒÑ‚Ð¸Ñ 3</translation>
<translation id="1718029547804390981">Документът е твърде голÑм за добавÑне на поÑÑнениÑ</translation>
+<translation id="1720941539803966190">ЗатварÑне на урока</translation>
<translation id="1721424275792716183">* Полето е задължително</translation>
<translation id="1727613060316725209">Сертификатът е валиден</translation>
<translation id="1727741090716970331">ДобавÑне на валиден номер на карта</translation>
@@ -420,8 +425,8 @@
<translation id="205212645995975601">Готвене на барбекю и грил</translation>
<translation id="2053111141626950936">Страниците на <ph name="LANGUAGE" /> нÑма да Ñе превеждат.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Когато тази контрола е включена и ÑÑŠÑтоÑнието е активно, Chrome Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ñ Ð´Ð¾ ÐºÐ¾Ñ Ð³Ð¾Ð»Ñма група от хора, или кохорта, е най-Ñходна Ñкорошната ви активноÑÑ‚ при Ñърфиране. Рекламодателите могат да избират реклами за групата, а активноÑтта ви при Ñърфиране оÑтава чаÑтна на уÑтройÑтвото ви. Групата ви Ñе актуализира вÑеки ден.}=1{Когато тази контрола е включена и ÑÑŠÑтоÑнието е активно, Chrome Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ñ Ð´Ð¾ ÐºÐ¾Ñ Ð³Ð¾Ð»Ñма група от хора, или кохорта, е най-Ñходна Ñкорошната ви активноÑÑ‚ при Ñърфиране. Рекламодателите могат да избират реклами за групата, а активноÑтта ви при Ñърфиране оÑтава чаÑтна на уÑтройÑтвото ви. Групата ви Ñе актуализира вÑеки ден.}other{Когато тази контрола е включена и ÑÑŠÑтоÑнието е активно, Chrome Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ñ Ð´Ð¾ ÐºÐ¾Ñ Ð³Ð¾Ð»Ñма група от хора, или кохорта, е най-Ñходна Ñкорошната ви активноÑÑ‚ при Ñърфиране. Рекламодателите могат да избират реклами за групата, а активноÑтта ви при Ñърфиране оÑтава чаÑтна на уÑтройÑтвото ви. Групата ви Ñе актуализира на вÑеки {NUM_DAYS} дни.}}</translation>
-<translation id="2053553514270667976">ПощенÑки код</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 предложение}other{# предложениÑ}}</translation>
+<translation id="2066915425250589881">заÑвите изтриването Ñ</translation>
<translation id="2068528718802935086">Бебета и прохождащи деца</translation>
<translation id="2071156619270205202">Тази карта не Ð¾Ñ‚Ð³Ð¾Ð²Ð°Ñ€Ñ Ð½Ð° уÑловиÑта за номер на виртуална карта.</translation>
<translation id="2071692954027939183">ИзвеÑтиÑта бÑха блокирани автоматично, защото обикновено не ги разрешавате</translation>
@@ -433,7 +438,6 @@
<translation id="2088086323192747268">Бутон „Управление на Ñинхронизирането“. ÐатиÑнете Enter, за да управлÑвате данните, които да Ñе Ñинхронизират, от наÑтройките на Chrome</translation>
<translation id="2091887806945687916">Звук</translation>
<translation id="2094505752054353250">ÐеÑъответÑтвие в домейна</translation>
-<translation id="2096368010154057602">Департамент</translation>
<translation id="2099652385553570808">Триточково телбодиране отлÑво</translation>
<translation id="2101225219012730419">ВерÑиÑ:</translation>
<translation id="2102134110707549001">Предложение за надеждна парола…</translation>
@@ -470,7 +474,6 @@
<translation id="2185836064961771414">ÐмериканÑки футбол</translation>
<translation id="2187317261103489799">Откриване (по подразбиране)</translation>
<translation id="2188375229972301266">ÐÑколко перфорации в долната чаÑÑ‚</translation>
-<translation id="2188852899391513400">Паролата, коÑто току-що използвахте, е разкрита при нарушение на ÑигурноÑтта на данните. За да защитите профилите Ñи, мениджърът на паролите в Google ви препоръчва да Ñ Ð¿Ñ€Ð¾Ð¼ÐµÐ½Ð¸Ñ‚Ðµ Ñега и Ñлед това да проверите запазените Ñи пароли.</translation>
<translation id="219906046732893612">Подобрение на дома</translation>
<translation id="2202020181578195191">Въведете валидна година на изтичане</translation>
<translation id="22081806969704220">Тава 3</translation>
@@ -481,6 +484,8 @@
<translation id="2215727959747642672">Редактиране на файлове</translation>
<translation id="2215963164070968490">Кучета</translation>
<translation id="2218879909401188352">ПонаÑтоÑщем извършители на атака Ñрещу <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> могат да инÑталират опаÑни приложениÑ, които да повредÑÑ‚ уÑтройÑтвото ви, да добавÑÑ‚ Ñкрити такÑи към Ñметката ви за мобилни уÑлуги или да откраднат личната ви информациÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">РеÑтартиране на урока</translation>
+<translation id="2219735899272417925">ИзиÑква Ñе нулиране на уÑтройÑтвото</translation>
<translation id="2224337661447660594">ÐÑма доÑтъп до интернет</translation>
<translation id="2230458221926704099">Поправете връзката Ñи поÑредÑтвом <ph name="BEGIN_LINK" />приложението за диагноÑтика<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Изпращане Ñега</translation>
@@ -578,11 +583,13 @@
<translation id="2512101340618156538">Ðе е разрешено (по подразбиране)</translation>
<translation id="2512413427717747692">Бутон за задаване на Chrome като браузър по подразбиране. ÐатиÑнете Enter за задаване на Chrome като браузъра по подразбиране за ÑиÑтемата в наÑтройките на iOS</translation>
<translation id="2515629240566999685">Проверете Ñигнала в района.</translation>
+<translation id="2515761554693942801">Избрахте опциÑта за потвърждаване чрез Touch ID в уебÑайтовете, които използват <ph name="PROVIDER_ORIGIN" />. Възможно е този доÑтавчик да е Ñъхранил Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° начина ви на плащане. Можете обаче да <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Телбодиране долу вдÑÑно</translation>
<translation id="2521736961081452453">Създаване на формулÑÑ€</translation>
<translation id="2523886232349826891">Запазено Ñамо на това уÑтройÑтво</translation>
<translation id="2524461107774643265">ДобавÑне на още информациÑ</translation>
<translation id="2529899080962247600">Това поле не Ñ‚Ñ€Ñбва да Ñъдържа повече от <ph name="MAX_ITEMS_LIMIT" /> запиÑа. Следващите Ñе пренебрегват.</translation>
+<translation id="253493526287553278">Преглед на подробноÑтите за Ð¿Ñ€Ð¾Ð¼Ð¾Ñ†Ð¸Ð¾Ð½Ð°Ð»Ð½Ð¸Ñ ÐºÐ¾Ð´</translation>
<translation id="2535585790302968248">Отворете нов раздел в режим „инкогнито“, за да Ñърфирате чаÑтно</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{и още 1}other{и още #}}</translation>
<translation id="2536110899380797252">ДобавÑне на адреÑ</translation>
@@ -618,7 +625,6 @@
<translation id="259821504105826686">ФотографÑки и цифрови изкуÑтва</translation>
<translation id="2601150049980261779">Романтични филми</translation>
<translation id="2604589665489080024">Поп музика</translation>
-<translation id="2609632851001447353">Вариации</translation>
<translation id="2610561535971892504">Кликнете, за да копирате</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />нÑма да запазва<ph name="END_EMPHASIS" /> Ñледната информациÑ:
<ph name="BEGIN_LIST" />
@@ -651,6 +657,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Рождени и имени дни</translation>
<translation id="2677748264148917807">Излизане</translation>
+<translation id="2679714844901977852">Запазете картата и данните Ñи за такÑуване в профила Ñи в Google <ph name="USER_EMAIL" />, за да плащате по-бързо и Ñигурно</translation>
<translation id="2684561033061424857">11 x 12</translation>
<translation id="2687555958734450033">ÐапаÑване</translation>
<translation id="2688969097326701645">Да, продължаване</translation>
@@ -699,7 +706,6 @@
<translation id="2854764410992194509">ДоÑтавчици на интернет уÑлуги</translation>
<translation id="2856444702002559011">Възможно е извършители на атака да опитват да откраднат информациÑта ви от <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (например пароли, ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ номера на кредитни карти). <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Ðа този Ñайт Ñе показват натрапчиви или подвеждащи реклами.</translation>
-<translation id="286512204874376891">Виртуалната карта прикрива дейÑтвителната ви, за да ви защити от потенциална измама. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ПриÑтелÑко</translation>
<translation id="28761159517501904">Филми</translation>
<translation id="2876489322757410363">Ще напуÑнете режим „инкогнито“, за да платите във външно приложение. ИÑкате ли да продължите?</translation>
@@ -800,7 +806,6 @@
<translation id="3158539265159265653">ДиÑк</translation>
<translation id="3162559335345991374">Използваната от Ð²Ð°Ñ Wi-Fi мрежа може да изиÑква да поÑетите Ñтраницата й за вход.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">ОÑтров</translation>
<translation id="3176929007561373547">За да Ñе уверите, че прокÑи Ñървърът работи,
проверете наÑтройките му или Ñе Ñвържете ÑÑŠÑ ÑиÑÑ‚ÐµÐ¼Ð½Ð¸Ñ Ñи админиÑтратор. Ðко ÑмÑтате, че не Ñ‚Ñ€Ñбва
да използвате прокÑи Ñървър:
@@ -879,9 +884,6 @@
<translation id="3369192424181595722">Грешка в чаÑовника</translation>
<translation id="3369459162151165748">ЧаÑти и акÑеÑоари за превозни ÑредÑтва</translation>
<translation id="3371064404604898522">Задаване на Chrome като браузър по подразбиране</translation>
-<translation id="3371076217486966826"><ph name="URL" /> иÑка да:
- • Ñъздаде триизмерна карта на заобикалÑщата ви Ñреда и да Ñледи позициÑта на камерата;
- • използва камерата ви.</translation>
<translation id="337363190475750230">Обезпечаването е отменено</translation>
<translation id="3375754925484257129">Стартиране на проверка на безопаÑноÑтта в Chrome</translation>
<translation id="3377144306166885718">Сървърът използва оÑтарÑла верÑÐ¸Ñ Ð½Ð° TLS.</translation>
@@ -898,6 +900,7 @@
<translation id="3399952811970034796">ÐÐ´Ñ€ÐµÑ Ð·Ð° бърза доÑтавка</translation>
<translation id="3402261774528610252">Връзката, чрез коÑто Ñе зарежда този Ñайт, използва TLS 1.0 или TLS 1.1. Тези верÑии Ñа оттеглени и ще бъдат деактивирани в бъдеще. Когато това Ñтане, потребителите нÑма да могат да зареждат Ñайта. Сървърът Ñ‚Ñ€Ñбва да активира TLS 1.2 или по-нова верÑиÑ.</translation>
<translation id="3405664148539009465">ПерÑонализиране на шрифтовете</translation>
+<translation id="3407789382767355356">подкани за вход от трети Ñтрани</translation>
<translation id="3409896703495473338">Управление на наÑтройките за ÑигурноÑÑ‚</translation>
<translation id="3414952576877147120">Размер:</translation>
<translation id="3417660076059365994">Качените или прикачените от Ð²Ð°Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ðµ Ñе изпращат до Google Cloud или до трети Ñтрани за анализ. Възможно е например да бъдат Ñканирани за чувÑтвителни данни или злонамерен Ñофтуер.</translation>
@@ -930,6 +933,7 @@
<translation id="3477679029130949506">Филми и чаÑове на кинопрожекции</translation>
<translation id="3479552764303398839">Ðе Ñега</translation>
<translation id="3484560055331845446">Възможно е да загубите доÑтъп до профила Ñи в Google. Chrome препоръчва да промените паролата Ñи Ñега. Ще получите подкана за влизане в профила Ñи.</translation>
+<translation id="3484861421501147767">ÐапомнÑне: Имате запазен промоционален код</translation>
<translation id="3487845404393360112">Тава 4</translation>
<translation id="3495081129428749620">ТърÑене в Ñтраницата
<ph name="PAGE_TITLE" /></translation>
@@ -1054,6 +1058,7 @@
<translation id="3810973564298564668">Управление</translation>
<translation id="3816482573645936981">СтойноÑÑ‚ (заменена)</translation>
<translation id="382518646247711829">Ðко използвате прокÑи Ñървър...</translation>
+<translation id="3826050100957962900">Подкани за вход от трети Ñтрани</translation>
<translation id="3827112369919217609">ÐбÑолютно</translation>
<translation id="3827666161959873541">Семейни филми</translation>
<translation id="3828924085048779000">Ðе може пропуÑкът да не Ñе попълни.</translation>
@@ -1066,9 +1071,9 @@
<translation id="3858027520442213535">Ðктуализиране на датата и чаÑа</translation>
<translation id="3858860766373142691">Име</translation>
<translation id="3872834068356954457">Ðаука</translation>
+<translation id="3875783148670536197">Покажете ми как</translation>
<translation id="3881478300875776315">Показване на по-малко редове</translation>
<translation id="3884278016824448484">ИдентификационниÑÑ‚ номер на уÑтройÑтвото е неÑъвмеÑтим</translation>
-<translation id="3885155851504623709">ЕнориÑ</translation>
<translation id="388632593194507180">УÑтановено е наблюдение</translation>
<translation id="3886948180919384617">Стакер 3</translation>
<translation id="3890664840433101773">ДобавÑне на имейл адреÑ</translation>
@@ -1098,9 +1103,11 @@
<translation id="3973234410852337861">ХоÑÑ‚ÑŠÑ‚ <ph name="HOST_NAME" /> е блокиран</translation>
<translation id="3978338123949022456">Режим на Ñ‚ÑŠÑ€Ñене. Въведете заÑвка и натиÑнете Enter, за да Ñ‚ÑŠÑ€Ñите ÑÑŠÑ <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Птици</translation>
+<translation id="3985750352229496475">Управление на адреÑите…</translation>
<translation id="3986705137476756801">Изключване на „ÐадпиÑи на живо“ заÑега</translation>
<translation id="3987940399970879459">По-малко от 1 МБ</translation>
<translation id="3990250421422698716">Разделно офÑетово отпечатване</translation>
+<translation id="3992684624889376114">Ð’Ñичко за тази Ñтраница</translation>
<translation id="3996311196211510766">Сайтът <ph name="ORIGIN" /> изиÑква за вÑички заÑвки към него
да Ñе прилага правило за източник, но понаÑтоÑщем то не може да бъде приложено.</translation>
<translation id="4006465311664329701">Ðачини на плащане, оферти и адреÑи поÑредÑтвом Google Pay</translation>
@@ -1225,6 +1232,7 @@
<translation id="4305666528087210886">ÐÑма доÑтъп до файла</translation>
<translation id="4306529830550717874">Да Ñе запази ли адреÑÑŠÑ‚?</translation>
<translation id="4306812610847412719">буферна памет</translation>
+<translation id="4310070645992025887">ТърÑете в пътуваниÑта Ñи</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Блокиране (по подразбиране)</translation>
<translation id="4314815835985389558">Управление на Ñинхронизирането</translation>
@@ -1275,6 +1283,7 @@
<translation id="4435702339979719576">пощенÑка картичка)</translation>
<translation id="443673843213245140">Използването на прокÑи Ñървър е деактивирано, но е поÑочена изрична негова конфигурациÑ.</translation>
<translation id="4441832193888514600">Пренебрегнато, защото правилото може да бъде зададено Ñамо като правило за потребители в облака.</translation>
+<translation id="4442470707340296952">Раздели в Chrome</translation>
<translation id="4450893287417543264">Да не Ñе показва отново</translation>
<translation id="4451135742916150903">Може да поиÑка разрешение да Ñе Ñвързва Ñ HID уÑтройÑтва</translation>
<translation id="4452328064229197696">Паролата, коÑто току-що използвахте, е разкрита при нарушение на ÑигурноÑтта на данните. За да защитите профилите Ñи, мениджърът на паролите в Google ви препоръчва да проверите запазените Ñи пароли.</translation>
@@ -1413,6 +1422,7 @@
<translation id="483241715238664915">Включване на предупреждениÑта</translation>
<translation id="4834250788637067901">Ðачини на плащане, оферти и адреÑи поÑредÑтвом Google Pay</translation>
<translation id="4838327282952368871">Замечтано</translation>
+<translation id="4839087176073128681">Платете по-бързо ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ Ð¿ÑŠÑ‚ и защитете картата Ñи чрез водещите в отраÑъла функции за ÑигурноÑÑ‚ от Google.</translation>
<translation id="4840250757394056958">Преглед на иÑториÑта ви в Chrome</translation>
<translation id="484462545196658690">Ðвтоматично</translation>
<translation id="484671803914931257">Получаване на отÑтъпки за <ph name="MERCHANT_NAME" /> и др.</translation>
@@ -1475,6 +1485,7 @@
<translation id="5011561501798487822">УÑтановен език</translation>
<translation id="5015510746216210676">Име на компютъра:</translation>
<translation id="5017554619425969104">Копиран от Ð²Ð°Ñ Ñ‚ÐµÐºÑÑ‚</translation>
+<translation id="5017828934289857214">ÐапомнÑне по-къÑно</translation>
<translation id="5018422839182700155">Тази Ñтраница не може да Ñе отвори</translation>
<translation id="5019198164206649151">Допълнителното хранилище е в лошо ÑÑŠÑтоÑние</translation>
<translation id="5020776957610079374">Световна музика</translation>
@@ -1494,6 +1505,7 @@
<translation id="5051305769747448211">ÐšÐ¾Ð¼ÐµÐ´Ð¸Ñ Ð½Ð° живо</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{За да изпратите този файл чрез „СподелÑне наблизо“, оÑвободете мÑÑто (<ph name="DISK_SPACE_SIZE" />) на уÑтройÑтвото Ñи}other{За да изпратите тези файлове чрез „СподелÑне наблизо“, оÑвободете мÑÑто (<ph name="DISK_SPACE_SIZE" />) на уÑтройÑтвото Ñи}}</translation>
<translation id="5056549851600133418">Статии за ваÑ</translation>
+<translation id="5060483733937416656">Избрахте опциÑта за потвърждаване чрез Windows Hello в уебÑайтовете, които използват <ph name="PROVIDER_ORIGIN" />. Възможно е този доÑтавчик да е Ñъхранил Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° начина ви на плащане. Можете обаче да <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Може би имахте предвид <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð½Ð° отпечатаното</translation>
<translation id="5068234115460527047">Хедж фондове</translation>
@@ -1507,10 +1519,8 @@
<translation id="5087286274860437796">ПонаÑтоÑщем Ñертификатът на Ñървъра не е валиден.</translation>
<translation id="5087580092889165836">ДобавÑне на карта</translation>
<translation id="5088142053160410913">Съобщение до оператора</translation>
-<translation id="5089810972385038852">Щат</translation>
<translation id="5093232627742069661">Z-образно Ñгъване</translation>
<translation id="5094747076828555589">Сървърът не можа да докаже, че е <ph name="DOMAIN" />; Chromium нÑма доверие на Ñертификата му за ÑигурноÑÑ‚. Това може да Ñе дължи на неправилно конфигуриране или на прихващане на връзката ви от атакуващ.</translation>
-<translation id="5095208057601539847">ПровинциÑ</translation>
<translation id="5097099694988056070">СтатиÑтичеÑки данни за уÑтройÑтвото, например за използването на процеÑора/RAM паметта</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Сайтът не е защитен</translation>
@@ -1548,6 +1558,7 @@
<translation id="5171045022955879922">ТърÑете или въведете URL адреÑ</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Машината</translation>
+<translation id="5177076414499237632">Ðаучете за източника и темата на тази Ñтраница</translation>
<translation id="5179510805599951267">Ðе е на <ph name="ORIGINAL_LANGUAGE" />? Подайте Ñигнал за тази грешка</translation>
<translation id="518639307526414276">Храни и Ñтоки за домашни любимци</translation>
<translation id="5190835502935405962">Лента на отметките</translation>
@@ -1708,6 +1719,7 @@
<translation id="5624120631404540903">Управление на паролите</translation>
<translation id="5629630648637658800">Зареждането на наÑтройките за правилото не бе уÑпешно</translation>
<translation id="5631439013527180824">Ðевалидно означение за управление на уÑтройÑтвото</translation>
+<translation id="5632485077360054581">Покажете ми как</translation>
<translation id="5633066919399395251">Извършители на атака, понаÑтоÑщем използващи <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, може да опитат да инÑталират опаÑни програми на компютъра ви, които крадат или изтриват информациÑта ви (например Ñнимки, пароли, ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ номера на кредитни карти). <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Блокирахме измамно Ñъдържание.</translation>
<translation id="5633259641094592098">Култови и незавиÑими филми</translation>
@@ -1825,6 +1837,7 @@
<translation id="5989320800837274978">Ðе Ñа поÑочени нито фикÑирани прокÑи Ñървъри, нито URL Ð°Ð´Ñ€ÐµÑ Ð½Ð° Ñкрипт във формат .pac.</translation>
<translation id="5992691462791905444">Z-образно Ñгъване на A3 до размер A4</translation>
<translation id="5995727681868049093">Управление на информациÑта ви и наÑтройките за поверителноÑÑ‚ и ÑигурноÑÑ‚ в профила ви в Google</translation>
+<translation id="5997247540087773573">Паролата, коÑто току-що използвахте, е разкрита при нарушение на ÑигурноÑтта на данните. За да защитите профилите Ñи, Google Мениджър на пароли ви препоръчва да Ñ Ð¿Ñ€Ð¾Ð¼ÐµÐ½Ð¸Ñ‚Ðµ Ñега и да проверите запазените Ñи пароли.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> резултата за „<ph name="SEARCH_TEXT" />“</translation>
<translation id="6006484371116297560">КлаÑичеÑка тема</translation>
<translation id="6008122969617370890">Подредба от N до 1</translation>
@@ -1920,7 +1933,6 @@
<translation id="627746635834430766">За да платите по-бързо ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ Ð¿ÑŠÑ‚, запазете картата и адреÑа Ñи за фактуриране в профила Ñи в Google.</translation>
<translation id="6279183038361895380">ÐатиÑнете |<ph name="ACCELERATOR" />|, за да Ñе покаже курÑорът</translation>
<translation id="6280223929691119688">Този Ð°Ð´Ñ€ÐµÑ Ð·Ð° бърза доÑтавка не Ñе поддържа. Изберете друг.</translation>
-<translation id="6282194474023008486">ПощенÑки код</translation>
<translation id="6285507000506177184">Бутон „Управление на изтеглÑниÑта Ñ Chrome“. ÐатиÑнете Enter, за да управлÑвате файловете, които Ñте изтеглили Ñ Chrome</translation>
<translation id="6289939620939689042">ЦвÑÑ‚ на Ñтраницата</translation>
<translation id="6290238015253830360">Предложените ви Ñтатии ще Ñе показват тук</translation>
@@ -1942,6 +1954,7 @@
<translation id="6337133576188860026">Ще оÑвободите по-малко от <ph name="SIZE" />. ÐÑкои Ñайтове може да Ñе заредÑÑ‚ по-бавно при Ñледващото ви поÑещение.</translation>
<translation id="6337534724793800597">Филтриране на правилата по име</translation>
<translation id="6340739886198108203">Според админиÑтраторÑко правило не Ñе препоръчва правенето на екранни Ñнимки или запиÑи, когато Ñе вижда поверително Ñъдържание:</translation>
+<translation id="6348220984832452017">Ðктивни варианти</translation>
<translation id="6349101878882523185">ИнÑталиране на <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ÐÑма}=1{1 парола (за <ph name="DOMAIN_LIST" />, Ñинхронизирана)}=2{2 пароли (за <ph name="DOMAIN_LIST" />, Ñинхронизирани)}other{# пароли (за <ph name="DOMAIN_LIST" />, Ñинхронизирани)}}</translation>
<translation id="6355392890578844978">Този браузър не Ñе управлÑва от дружеÑтво или друга организациÑ. Възможно е активноÑтта на уÑтройÑтвото да Ñе управлÑва извън Chromium. <ph name="BEGIN_LINK" />Ðаучете повече<ph name="END_LINK" /></translation>
@@ -1973,7 +1986,6 @@
<translation id="643051589346665201">ПромÑна на паролата за Google</translation>
<translation id="6433490469411711332">Редактиране на информациÑта за връзка</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> отказа да уÑтанови връзка.</translation>
-<translation id="6438025220577812695">Ðз ще Ñ Ð¿Ñ€Ð¾Ð¼ÐµÐ½Ñ</translation>
<translation id="6440503408713884761">Пренебрегнато</translation>
<translation id="6443406338865242315">Кои Ñ€Ð°Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ Ð¸ приÑтавки Ñте инÑталирали</translation>
<translation id="6446163441502663861">Kahu (плик)</translation>
@@ -2103,9 +2115,9 @@
<translation id="6828866289116430505">Генетика</translation>
<translation id="6831043979455480757">Превод</translation>
<translation id="6833752742582340615">Запазете картата и данните Ñи за такÑуване в профила Ñи в Google, за да плащате по-бързо и Ñигурно</translation>
-<translation id="6839929833149231406">Район</translation>
<translation id="6846340164947227603">Използване на номер на виртуална карта...</translation>
<translation id="6852204201400771460">ИÑкате ли да презаредите приложението?</translation>
+<translation id="6857776781123259569">Управление на паролите…</translation>
<translation id="686485648936420384">ПотребителÑки реÑурÑи</translation>
<translation id="6865412394715372076">Тази карта не може да бъде потвърдена в момента</translation>
<translation id="6869334554832814367">ПотребителÑки заеми</translation>
@@ -2154,7 +2166,6 @@
<translation id="6965978654500191972">УÑтройÑтво</translation>
<translation id="696703987787944103">ЕÑтеÑтвено</translation>
<translation id="6968269510885595029">Използване на ключа ви за ÑигурноÑÑ‚</translation>
-<translation id="6970216967273061347">Окръг</translation>
<translation id="6971439137020188025">Бързо Ñъздаване на Ð¿Ñ€ÐµÐ·ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Ð² Google Презентации</translation>
<translation id="6972629891077993081">HID уÑтройÑтва</translation>
<translation id="6973656660372572881">ПоÑочени Ñа както фикÑирани прокÑи Ñървъри, така и URL Ð°Ð´Ñ€ÐµÑ Ð½Ð° Ñкрипт във формат .pac.</translation>
@@ -2193,7 +2204,6 @@
<translation id="7081308185095828845">Тази Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð½Ðµ е налице на уÑтройÑтвото ви</translation>
<translation id="7083258188081898530">Тава 9</translation>
<translation id="7086090958708083563">Качване, заÑвено от потребител</translation>
-<translation id="7087282848513945231">ОблаÑÑ‚</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. ÐатиÑнете Tab и Ñлед това Enter, за да управлÑвате разрешениÑта и ÑъхранÑваните данни от Ñайтовете от наÑтройките на Chrome</translation>
<translation id="7096937462164235847">ИдентичноÑтта на този уебÑайт е потвърдена.</translation>
<translation id="7101893872976785596">Филми на ужаÑите</translation>
@@ -2212,10 +2222,10 @@
<ph name="LIST_ITEM" />информациÑта, въведена във формулÑри.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Този Ð°Ð´Ñ€ÐµÑ Ð·Ð° доÑтавка не Ñе поддържа. Изберете друг.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ÐдминиÑтраторът ви е забранил копирането на тези данни.</translation>
<translation id="7135130955892390533">Показване на ÑÑŠÑтоÑнието</translation>
<translation id="7138472120740807366">Ðачин на бърза доÑтавка</translation>
-<translation id="7139724024395191329">ЕмирÑтво</translation>
<translation id="7139892792842608322">ОÑновна тава</translation>
<translation id="714064300541049402">ИзмеÑтване на изображението от Ñтрана 2 по оÑта X</translation>
<translation id="7152423860607593928">Number-14 (плик)</translation>
@@ -2432,6 +2442,7 @@
<translation id="7669271284792375604">Извършителите на атаки, използващи този Ñайт, може да опитат да ви подведат да инÑталирате програми, които вредÑÑ‚ на Ñърфирането ви (например, като променÑÑ‚ началната ви Ñтраница или показват допълнителни реклами в поÑещаваните от Ð²Ð°Ñ Ñайтове).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ДейÑтвиÑта Ñ Ð´Ð°Ð½Ð½Ð¸, означени като поверителни (1 дейÑтвие Ñлед влизането в профила). <ph name="BEGIN_LINK" />Ðаучете повече<ph name="END_LINK" />}other{ДейÑтвиÑта Ñ Ð´Ð°Ð½Ð½Ð¸, означени като поверителни (# дейÑÑ‚Ð²Ð¸Ñ Ñлед влизането в профила). <ph name="BEGIN_LINK" />Ðаучете повече<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ПощенÑка ÐºÑƒÑ‚Ð¸Ñ 6</translation>
+<translation id="7675325315208090829">Управление на начините на плащане…</translation>
<translation id="7676643023259824263">ТърÑене на текÑта от буферната памет, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Преглед и управление на иÑториÑта ви на Ñърфиране в наÑтройките на Chrome</translation>
<translation id="7679947978757153706">Бейзбол</translation>
@@ -2474,7 +2485,6 @@
<translation id="7766518757692125295">Пола</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Ð’ ÑÑŠÑ‰Ð¸Ñ Ñ€ÐµÐ´ Ñ Ð¾Ñ‚Ð¿ÐµÑ‡Ð°Ñ‚Ð°Ð½Ð°Ñ‚Ð° Ñтрана нагоре</translation>
-<translation id="777702478322588152">Префектура</translation>
<translation id="7791011319128895129">Ðепубликувано</translation>
<translation id="7791196057686275387">Пакетиране</translation>
<translation id="7791543448312431591">ДобавÑне</translation>
@@ -2565,12 +2575,12 @@
<translation id="8055534648776115597">ПрофеÑионално обучение и допълнителна квалификациÑ</translation>
<translation id="8057711352706143257">Софтуерът <ph name="SOFTWARE_NAME" /> не е конфигуриран правилно. Обикновено проблемът Ñе отÑтранÑва Ñ Ð´ÐµÐ¸Ð½Ñталиране на <ph name="SOFTWARE_NAME" />. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Хранително-вкуÑова промишленоÑÑ‚</translation>
-<translation id="8066955247577885446">За Ñъжаление, нещо Ñе обърка.</translation>
<translation id="8067872629359326442">Току-що въведохте паролата Ñи в измамничеÑки Ñайт. Chromium може да помогне. За да промените паролата Ñи и да уведомите Google за това, че профилът ви може да е изложен на риÑк, кликнете върху „Защита на профила“.</translation>
<translation id="8070439594494267500">Икона на приложението</translation>
<translation id="8074253406171541171">10 x 13 (плик)</translation>
<translation id="8075736640322370409">Бързо Ñъздаване на таблица в Google Таблици</translation>
<translation id="8075898834294118863">Управление на наÑтройките за Ñайтовете</translation>
+<translation id="8076492880354921740">Раздели</translation>
<translation id="8078141288243656252">ДобавÑнето на поÑÑÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ е възможно, когато документът е завъртÑн</translation>
<translation id="8079031581361219619">ИÑкате ли да презаредите Ñайта?</translation>
<translation id="8081087320434522107">Седани</translation>
@@ -2693,6 +2703,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" />, <ph name="SECOND_LABEL" />, <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ÐаÑтройки</translation>
+<translation id="8428634594422941299">Разбрах</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. ÐатиÑнете Tab и Ñлед това Enter, за да управлÑвате предпочитаниÑта Ñи за „биÑквитките“ от наÑтройките на Chrome</translation>
<translation id="8433057134996913067">Ще излезете от повечето уебÑайтове.</translation>
<translation id="8434840396568290395">Домашни любимци</translation>
@@ -2790,6 +2801,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> е кодът ви за <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Запазване на отметка към този раздел</translation>
<translation id="8751426954251315517">МолÑ, опитайте отново ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ Ð¿ÑŠÑ‚</translation>
+<translation id="8757526089434340176">Има налична оферта от Google Pay</translation>
<translation id="8758885506338294482">СъÑÑ‚ÐµÐ·Ð°Ð½Ð¸Ñ Ð¿Ð¾ видеоигри</translation>
<translation id="8759274551635299824">Тази карта е изтекла</translation>
<translation id="87601671197631245">Този Ñайт използва оÑтарÑла ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð·Ð° ÑигурноÑÑ‚, коÑто може да разкрие информациÑта ви (например пароли, ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ кредитни карти) при изпращането Ñ Ð´Ð¾ него.</translation>
@@ -2797,6 +2809,7 @@
<translation id="8763927697961133303">USB уÑтройÑтво</translation>
<translation id="8763986294015493060">ЗатварÑне на вÑички отворени прозорци в режим „инкогнито“</translation>
<translation id="8766943070169463815">ЛиÑÑ‚ÑŠÑ‚ за удоÑтоверÑване на идентификационни данни за Ñигурни Ð¿Ð»Ð°Ñ‰Ð°Ð½Ð¸Ñ Ðµ отворен</translation>
+<translation id="8767765348545497220">ЗатварÑне на балончето за помощ</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Мотоциклети</translation>
<translation id="8790007591277257123">&amp;ВъзÑтановÑване на изтриването</translation>
@@ -2809,6 +2822,7 @@
<translation id="8806285662264631610">Продукти за Ð±Ð°Ð½Ñ Ð¸ за Ñ‚Ñлото</translation>
<translation id="8807160976559152894">ОтрÑзване Ñлед вÑÑка Ñтраница</translation>
<translation id="8808828119384186784">ÐаÑтройки на Chrome</translation>
+<translation id="8813277370772331957">ÐапомнÑне по-къÑно</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. ÐатиÑнете Tab и Ñлед това – Enter, за да актуализирате Chrome от наÑтройките</translation>
<translation id="8820817407110198400">Отметки</translation>
<translation id="882338992931677877">Слот за ръчно подаване</translation>
@@ -2988,6 +3002,7 @@
<translation id="988159990683914416">Компилирана програма за програмиÑти</translation>
<translation id="989988560359834682">Редактиране на адреÑа</translation>
<translation id="991413375315957741">Ñензори за движение или Ñветлина</translation>
+<translation id="992110854164447044">Виртуалната карта Ñкрива дейÑтвителната ви, за да ви защити от потенциална измама. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">розово</translation>
<translation id="992432478773561401"><ph name="SOFTWARE_NAME" /> не Ñе инÑталира правилно на компютъра ви или в мрежата:
diff --git a/chromium/components/strings/components_strings_bn.xtb b/chromium/components/strings/components_strings_bn.xtb
index 1eccf003df9..02bf6b50c7f 100644
--- a/chromium/components/strings/components_strings_bn.xtb
+++ b/chromium/components/strings/components_strings_bn.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">অনà§à¦¦à¦¾à¦¨, বৃতà§à¦¤à¦¿ à¦à¦¬à¦‚ আরà§à¦¥à¦¿à¦• সাহাযà§à¦¯</translation>
<translation id="1048785276086539861">অà§à¦¯à¦¾à¦¨à§‹à¦Ÿà§‡à¦¶à¦¨ à¦à¦¡à¦¿à¦Ÿ করার সময় à¦à¦‡ ডকà§à¦®à§‡à¦¨à§à¦Ÿ আবার 'সিঙà§à¦—েল পেজ ভিউ' মোডে দেখা যাবে</translation>
<translation id="1050038467049342496">অনà§à¦¯à¦¾à¦¨à§à¦¯ অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¾à¦¨à¦—à§à¦²à¦¿ বনà§à¦§ করà§à¦¨</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> বà§à¦¯à¦¬à¦¹à¦¾à¦° করে à¦à¦®à¦¨ ওয়েবসাইটে যাচাইকরণ করার ডিভাইস বà§à¦¯à¦¬à¦¹à¦¾à¦° করে যাচাই করার বিকলà§à¦ª বেছে নিয়েছেন। à¦à¦‡ পরিষেবা পà§à¦°à¦¦à¦¾à¦¨à¦•à¦¾à¦°à§€ আপনার পেমেনà§à¦Ÿ পদà§à¦§à¦¤à¦¿ সমà§à¦ªà¦°à§à¦•à§‡ তথà§à¦¯ সà§à¦Ÿà§‹à¦° করে থাকতে পারে যা আপনি <ph name="LINK_TEXT" /> পারেন।</translation>
<translation id="1055184225775184556">&amp;যোগ করাকে পূরà§à¦¬à¦¾à¦¬à¦¸à§à¦¥à¦¾à¦¯à¦¼ ফেরান</translation>
<translation id="1056663316309890343">ফটোতে কাজে লাগে à¦à¦®à¦¨ সফà§à¦Ÿà¦“য়à§à¦¯à¦¾à¦°</translation>
<translation id="1056898198331236512">সতরà§à¦•à¦¤à¦¾</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">পিক-আপের পদà§à¦§à¦¤à¦¿</translation>
<translation id="1281476433249504884">সà§à¦Ÿà§à¦¯à¦¾à¦•à¦¾à¦° ১</translation>
<translation id="1285320974508926690">কখনই à¦à¦‡ সাইটটিকে অনà§à¦¬à¦¾à¦¦ করবেন না</translation>
+<translation id="1288548991597756084">নিরাপদে কারà§à¦¡ সেভ করà§à¦¨</translation>
<translation id="1292571435393770077">টà§à¦°à§‡ ১৬</translation>
<translation id="1292701964462482250">"আপনার কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡à¦° সফà§à¦Ÿà¦“য়à§à¦¯à¦¾à¦° Chrome-কে নিরাপদে ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿà§‡ কানেকà§à¦Ÿ করতে বাধা দিচà§à¦›à§‡" (শà§à¦§à§à¦®à¦¾à¦¤à§à¦° Windows কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡à¦° জনà§à¦¯)</translation>
<translation id="1294154142200295408">কমà§à¦¯à¦¾à¦¨à§à¦¡-লাইন ভেরিয়েশন</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;à¦à¦‡ সমসà§à¦¯à¦¾à¦° সমাধান করতে, যে পৃষà§à¦ à¦¾à¦Ÿà¦¿ খà§à¦²à¦¤à§‡ চান সেটিতে &lt;strong&gt;কানেকà§à¦Ÿ করà§à¦¨&lt;/strong&gt; বোতামে কà§à¦²à¦¿à¦• করà§à¦¨à¥¤&lt;/p&gt;</translation>
<translation id="1507780850870535225">লà§à¦¯à¦¾à¦¨à§à¦¡à¦¸à§à¦•à§‡à¦ª ডিজাইন</translation>
<translation id="1513706915089223971">ইতিহাসে à¦à¦¨à§à¦Ÿà§à¦°à¦¿à¦° তালিকা</translation>
+<translation id="1516097932025103760">à¦à¦Ÿà¦¿ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ করা হবে, নিরাপদে সেভ করা হয়েছে à¦à¦¬à¦‚ সিভিসি কখনও সà§à¦Ÿà§‹à¦° করা হয়না।</translation>
<translation id="1517433312004943670">ফোন নমà§à¦¬à¦° আবশà§à¦¯à¦•</translation>
<translation id="1519264250979466059">নিরà§à¦®à¦¾à¦£à§‡à¦° তারিখ</translation>
<translation id="1521159554480556801">ফাইবার ও টেকà§à¦¸à¦Ÿà¦¾à¦‡à¦² আরà§à¦Ÿ</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">পেমেনà§à¦Ÿà§‡à¦° পদà§à¦§à¦¤à¦¿à¦—à§à¦²à¦¿ পূরণ করে সেভ করà§à¦¨</translation>
<translation id="1663943134801823270">Chrome থেকে কারà§à¦¡ à¦à¦¬à¦‚ ঠিকানাগà§à¦²à¦¿ à¦à¦¸à§‡à¦›à§‡à¥¤ আপনি <ph name="BEGIN_LINK" />সেটিংস<ph name="END_LINK" /> ঠà¦à¦—à§à¦²à¦¿ পরিচালনা করতে পারবেন।</translation>
<translation id="1671391448414634642">à¦à¦–ন থেকে <ph name="SOURCE_LANGUAGE" /> ভাষার পৃষà§à¦ à¦¾ <ph name="TARGET_LANGUAGE" /> ভাষায় অনà§à¦¬à¦¾à¦¦ করা হবে।</translation>
+<translation id="1673886523110456987">উপলভà§à¦¯ অফার বà§à¦¯à¦¬à¦¹à¦¾à¦° করতে <ph name="CARD_DETAIL" />-à¦à¦° মাধà§à¦¯à¦®à§‡ করে চেক করà§à¦¨</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> থেকে <ph name="TARGET_LANGUAGE" />-à¦</translation>
<translation id="1682696192498422849">আগে ছোট পà§à¦°à¦¾à¦¨à§à¦¤</translation>
<translation id="168693727862418163">à¦à¦‡ নীতির মান সেটির সà§à¦•à¦¿à¦®à¦¾à¦° সাথে যাচাই করা যায়নি, তাই সেটি উপেকà§à¦·à¦¾ করা হবে।</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">মোশন সেনà§à¦¸à¦°</translation>
<translation id="1717494416764505390">মেলবকà§à¦¸ ৩</translation>
<translation id="1718029547804390981">বà§à¦¯à¦¾à¦–à§à¦¯à¦¾à¦° জনà§à¦¯ ডকà§à¦®à§‡à¦¨à§à¦Ÿà§‡à¦° সাইজ বেশি বড়</translation>
+<translation id="1720941539803966190">টিউটোরিয়াল বনà§à¦§ করà§à¦¨</translation>
<translation id="1721424275792716183">* à¦à¦‡ ফিলà§à¦¡à§‡ কিছৠলেখা পà§à¦°à§Ÿà§‹à¦œà¦¨</translation>
<translation id="1727613060316725209">সারà§à¦Ÿà¦¿à¦«à¦¿à¦•à§‡à¦Ÿà¦Ÿà¦¿ সঠিক</translation>
<translation id="1727741090716970331">সঠিক কারà§à¦¡ নমà§à¦¬à¦° যোগ করà§à¦¨</translation>
@@ -423,8 +428,8 @@
<translation id="205212645995975601">বারà§à¦¬à¦¿à¦•à¦¿à¦‰ ও গà§à¦°à¦¿à¦²à¦¿à¦‚</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> ভাষার পৃষà§à¦ à¦¾ অনà§à¦¬à¦¾à¦¦ করা হবে না।</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{à¦à¦‡ কনà§à¦Ÿà§à¦°à§‹à¦² চালৠà¦à¦¬à¦‚ সà§à¦Ÿà§à¦¯à¦¾à¦Ÿà¦¾à¦¸ অà§à¦¯à¦¾à¦•à§à¦Ÿà¦¿à¦­ থাকলে আপনার সামà§à¦ªà§à¦°à¦¤à¦¿à¦• বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚-à¦à¦° ইতিহাস, আরও বড় কোন গà§à¦°à§à¦ª বা 'দল'-à¦à¦° সাথে সবচেয়ে বেশি মেলে, 'Chrome' তা নিরà§à¦§à¦¾à¦°à¦£ করে। বিজà§à¦žà¦¾à¦ªà¦¨à¦¦à¦¾à¦¤à¦¾à¦°à¦¾ গà§à¦°à§à¦ªà§‡à¦° জনà§à¦¯ বিজà§à¦žà¦¾à¦ªà¦¨ বেছে নিতে পারেন à¦à¦¬à¦‚ আপনার বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚-à¦à¦° ইতিহাস ডিভাইসে বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত রাখা হয়। আপনার গà§à¦°à§à¦ª পà§à¦°à¦¤à§à¦¯à§‡à¦•à¦¦à¦¿à¦¨ আপডেট করা হয়।}=1{à¦à¦‡ কনà§à¦Ÿà§à¦°à§‹à¦² চালৠà¦à¦¬à¦‚ সà§à¦Ÿà§à¦¯à¦¾à¦Ÿà¦¾à¦¸ অà§à¦¯à¦¾à¦•à§à¦Ÿà¦¿à¦­ থাকলে আপনার সামà§à¦ªà§à¦°à¦¤à¦¿à¦• বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚-à¦à¦° ইতিহাস, আরও বড় কোন গà§à¦°à§à¦ª বা 'দল'-à¦à¦° সাথে সবচেয়ে বেশি মেলে, 'Chrome' তা নিরà§à¦§à¦¾à¦°à¦£ করে। বিজà§à¦žà¦¾à¦ªà¦¨à¦¦à¦¾à¦¤à¦¾à¦°à¦¾ গà§à¦°à§à¦ªà§‡à¦° জনà§à¦¯ বিজà§à¦žà¦¾à¦ªà¦¨ বেছে নিতে পারেন à¦à¦¬à¦‚ আপনার বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚-à¦à¦° ইতিহাস ডিভাইসে বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত রাখা হয়। আপনার গà§à¦°à§à¦ª পà§à¦°à¦¤à§à¦¯à§‡à¦•à¦¦à¦¿à¦¨ আপডেট করা হয়।}one{à¦à¦‡ কনà§à¦Ÿà§à¦°à§‹à¦² চালৠà¦à¦¬à¦‚ সà§à¦Ÿà§à¦¯à¦¾à¦Ÿà¦¾à¦¸ অà§à¦¯à¦¾à¦•à§à¦Ÿà¦¿à¦­ থাকলে আপনার সামà§à¦ªà§à¦°à¦¤à¦¿à¦• বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚-à¦à¦° ইতিহাস, আরও বড় কোন গà§à¦°à§à¦ª বা 'দল'-à¦à¦° সাথে সবচেয়ে বেশি মেলে, 'Chrome' তা নিরà§à¦§à¦¾à¦°à¦£ করে। বিজà§à¦žà¦¾à¦ªà¦¨à¦¦à¦¾à¦¤à¦¾à¦°à¦¾ গà§à¦°à§à¦ªà§‡à¦° জনà§à¦¯ বিজà§à¦žà¦¾à¦ªà¦¨ বেছে নিতে পারেন à¦à¦¬à¦‚ আপনার বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚-à¦à¦° ইতিহাস ডিভাইসে বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত রাখা হয়। আপনার গà§à¦°à§à¦ª পà§à¦°à¦¤à¦¿ {NUM_DAYS} দিনে আপডেট করা হয়।}other{à¦à¦‡ কনà§à¦Ÿà§à¦°à§‹à¦² চালৠà¦à¦¬à¦‚ সà§à¦Ÿà§à¦¯à¦¾à¦Ÿà¦¾à¦¸ অà§à¦¯à¦¾à¦•à§à¦Ÿà¦¿à¦­ থাকলে আপনার সামà§à¦ªà§à¦°à¦¤à¦¿à¦• বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚-à¦à¦° ইতিহাস, আরও বড় কোন গà§à¦°à§à¦ª বা 'দল'-à¦à¦° সাথে সবচেয়ে বেশি মেলে, 'Chrome' তা নিরà§à¦§à¦¾à¦°à¦£ করে। বিজà§à¦žà¦¾à¦ªà¦¨à¦¦à¦¾à¦¤à¦¾à¦°à¦¾ গà§à¦°à§à¦ªà§‡à¦° জনà§à¦¯ বিজà§à¦žà¦¾à¦ªà¦¨ বেছে নিতে পারেন à¦à¦¬à¦‚ আপনার বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚-à¦à¦° ইতিহাস ডিভাইসে বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত রাখা হয়। আপনার গà§à¦°à§à¦ª পà§à¦°à¦¤à¦¿ {NUM_DAYS} দিনে আপডেট করা হয়।}}</translation>
-<translation id="2053553514270667976">পিন কোড</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{১টি পà§à¦°à¦¸à§à¦¤à¦¾à¦¬}one{#টি পà§à¦°à¦¸à§à¦¤à¦¾à¦¬}other{#টি পà§à¦°à¦¸à§à¦¤à¦¾à¦¬}}</translation>
+<translation id="2066915425250589881">মà§à¦›à§‡ ফেলার অনà§à¦°à§‹à¦§</translation>
<translation id="2068528718802935086">শিশৠও ছোট শিশà§</translation>
<translation id="2071156619270205202">ভারà§à¦šà§à§Ÿà¦¾à¦² কারà§à¦¡ নমà§à¦¬à¦°à§‡à¦° জনà§à¦¯ à¦à¦‡ কারà§à¦¡à¦Ÿà¦¿ যোগà§à¦¯ নয়।</translation>
<translation id="2071692954027939183">আপনি সাধারণত বিজà§à¦žà¦ªà§à¦¤à¦¿ অনà§à¦®à§‹à¦¦à¦¨ না করার জনà§à¦¯ অটোমেটিক বà§à¦²à¦• হয়ে গেছে</translation>
@@ -436,7 +441,6 @@
<translation id="2088086323192747268">সিঙà§à¦• করার বোতাম মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨, Chrome সেটিংসে আপনি কী ধরনের তথà§à¦¯ সিঙà§à¦• করবেন, তা মà§à¦¯à¦¾à¦¨à§‡à¦œ করতে Enter পà§à¦°à§‡à¦¸ করà§à¦¨</translation>
<translation id="2091887806945687916">আওয়াজ</translation>
<translation id="2094505752054353250">ডোমেন মেলেনি</translation>
-<translation id="2096368010154057602">বিভাগ</translation>
<translation id="2099652385553570808">বাà¦à¦¦à¦¿à¦•à§‡ টà§à¦°à¦¿à¦ªà¦² সà§à¦Ÿà§‡à¦ªà¦²</translation>
<translation id="2101225219012730419">ভারà§à¦¸à¦¨:</translation>
<translation id="2102134110707549001">শকà§à¦¤à¦¿à¦¶à¦¾à¦²à§€ পাসওয়ারà§à¦¡ সাজেসà§à¦Ÿ করà§à¦¨â€¦</translation>
@@ -473,7 +477,6 @@
<translation id="2185836064961771414">আমেরিকান ফà§à¦Ÿà¦¬à¦²</translation>
<translation id="2187317261103489799">শনাকà§à¦¤ করà§à¦¨ (ডিফলà§à¦Ÿ)</translation>
<translation id="2188375229972301266">নিচে মালà§à¦Ÿà¦¿à¦ªà¦² পাঞà§à¦š</translation>
-<translation id="2188852899391513400">আপনি à¦à¦‡à¦®à¦¾à¦¤à§à¦° যে পাসওয়ারà§à¦¡ বà§à¦¯à¦¬à¦¹à¦¾à¦° করলেন, সেটি হà§à¦¯à¦¾à¦• হওয়া কোনও ডেটাবেসে পাওয়া গেছে। আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ সà§à¦°à¦•à§à¦·à¦¿à¦¤ করতে, Google পাসওয়ারà§à¦¡ মà§à¦¯à¦¾à¦¨à§‡à¦œà¦¾à¦° à¦à¦–নই à¦à¦Ÿà¦¿ পরিবরà§à¦¤à¦¨ করতে à¦à¦¬à¦‚ তারপর সেভ করা পাসওয়ারà§à¦¡ চেক করার সাজেশন দিচà§à¦›à§‡à¥¤</translation>
<translation id="219906046732893612">হোম উনà§à¦¨à¦¤à¦¿</translation>
<translation id="2202020181578195191">মেয়াদ শেষ হওয়ার বছরের সঠিক মান লিখà§à¦¨</translation>
<translation id="22081806969704220">টà§à¦°à§‡ ৩</translation>
@@ -484,6 +487,8 @@
<translation id="2215727959747642672">ফাইল à¦à¦¡à¦¿à¦Ÿ করা</translation>
<translation id="2215963164070968490">কà§à¦•à§à¦°</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />-ঠযে আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ à¦à¦‡ মà§à¦¹à§‚রà§à¦¤à§‡ সকà§à¦°à¦¿à§Ÿ আছে, তারা à¦à¦®à¦¨ বিপজà§à¦œà¦¨à¦• অà§à¦¯à¦¾à¦ª ইনসà§à¦Ÿà¦² করে দিতে পারে যেগà§à¦²à¦¿ আপনার ডিভাইসের কà§à¦·à¦¤à¦¿ করতে, আপনার মোবাইলের বিলে লà§à¦•à¦¾à¦¨à§‹ চারà§à¦œ যোগ করতে বা আপনার বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত তথà§à¦¯ চà§à¦°à¦¿ করতে পারে৷ <ph name="BEGIN_LEARN_MORE_LINK" />আরও জানà§à¦¨<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">আবার টিউটোরিয়াল শà§à¦°à§ করà§à¦¨</translation>
+<translation id="2219735899272417925">ডিভাইস রিসেট করতে হবে</translation>
<translation id="2224337661447660594">ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ কানেকশন নেই</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />ডায়াগনসà§à¦Ÿà¦¿à¦• অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¾à¦¨<ph name="END_LINK" /> বà§à¦¯à¦¬à¦¹à¦¾à¦° করে আপনার সংযোগ ঠিক করà§à¦¨</translation>
<translation id="2239100178324503013">à¦à¦–নই পাঠান</translation>
@@ -581,11 +586,13 @@
<translation id="2512101340618156538">অনà§à¦®à¦¤à¦¿ নেই (ডিফলà§à¦Ÿ)</translation>
<translation id="2512413427717747692">Chrome-কে ডিফলà§à¦Ÿ বà§à¦°à¦¾à¦‰à¦œà¦¾à¦° বোতাম হিসেবে সেট করà§à¦¨, iOS সেটিংসে Chrome-কে সিসà§à¦Ÿà§‡à¦®à§‡à¦° ডিফলà§à¦Ÿ বà§à¦°à¦¾à¦‰à¦œà¦¾à¦° হিসেবে সেট করতে Enter কী পà§à¦°à§‡à¦¸ করà§à¦¨</translation>
<translation id="2515629240566999685">আপনার à¦à¦²à¦¾à¦•à¦¾à¦¯à¦¼ সংকেত পরীকà§à¦·à¦¾ করে দেখà§à¦¨</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> বà§à¦¯à¦¬à¦¹à¦¾à¦° করে à¦à¦®à¦¨ ওয়েবসাইটে টাচ আইডি বà§à¦¯à¦¬à¦¹à¦¾à¦° করে যাচাই করার বিকলà§à¦ª বেছে নিয়েছেন। à¦à¦‡ পরিষেবা পà§à¦°à¦¦à¦¾à¦¨à¦•à¦¾à¦°à§€ আপনার পেমেনà§à¦Ÿ পদà§à¦§à¦¤à¦¿ সমà§à¦ªà¦°à§à¦•à§‡ তথà§à¦¯ সà§à¦Ÿà§‹à¦° করে থাকতে পারে যা আপনি <ph name="LINK_TEXT" /> পারেন।</translation>
<translation id="2521385132275182522">নিচে ডানদিকে সà§à¦Ÿà§‡à¦ªà¦² করà§à¦¨</translation>
<translation id="2521736961081452453">ফরà§à¦® তৈরি করà§à¦¨</translation>
<translation id="2523886232349826891">শà§à¦§à§à¦®à¦¾à¦¤à§à¦° à¦à¦‡ ডিভাইসে সেভ করা যাবে</translation>
<translation id="2524461107774643265">আরও তথà§à¦¯ যোগ করà§à¦¨</translation>
<translation id="2529899080962247600">à¦à¦‡ ফিলà§à¦¡à§‡ <ph name="MAX_ITEMS_LIMIT" />টির বেশি à¦à¦¨à§à¦Ÿà§à¦°à¦¿ লেখা যাবে না। অনà§à¦¯ সব à¦à¦¨à§à¦Ÿà§à¦°à¦¿ বাতিল করা হবে।</translation>
+<translation id="253493526287553278">পà§à¦°à§‹à¦®à§‹ কোড সংকà§à¦°à¦¾à¦¨à§à¦¤ বিবরণ দেখà§à¦¨</translation>
<translation id="2535585790302968248">গোপনে বà§à¦°à¦¾à¦‰à¦œ করতে ছদà§à¦®à¦¬à§‡à¦¶à§€ টà§à¦¯à¦¾à¦¬ খà§à¦²à§à¦¨</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{à¦à¦¬à¦‚ আরও ১টি}one{à¦à¦¬à¦‚ আরও #টি}other{à¦à¦¬à¦‚ আরও #টি}}</translation>
<translation id="2536110899380797252">ঠিকানা যোগ করà§à¦¨</translation>
@@ -621,7 +628,6 @@
<translation id="259821504105826686">ফটোগà§à¦°à¦¾à¦«à¦¿à¦• ও ডিজিটাল আরà§à¦Ÿ</translation>
<translation id="2601150049980261779">ভালবাসার সিনেমা</translation>
<translation id="2604589665489080024">পপ মিউজিক</translation>
-<translation id="2609632851001447353">বৈচিতà§à¦°à¦¤à¦¾</translation>
<translation id="2610561535971892504">কপি করতে কà§à¦²à¦¿à¦• করà§à¦¨</translation>
<translation id="2617988307566202237">à¦à¦•à§à¦·à§‡à¦¤à§à¦°à§‡ নিচে দেওয়া তথà§à¦¯ Chrome <ph name="BEGIN_EMPHASIS" />সেভ করবে না<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -654,6 +660,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">জনà§à¦®à¦¦à¦¿à¦¨ à¦à¦¬à¦‚ নাম দিবস</translation>
<translation id="2677748264148917807">ছেড়ে চলে যান</translation>
+<translation id="2679714844901977852">নিরাপদ à¦à¦¬à¦‚ দà§à¦°à§à¦¤ চেক-আউটের জনà§à¦¯ আপনার <ph name="USER_EMAIL" /> Google অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡ কারà§à¦¡ à¦à¦¬à¦‚ বিলিংয়ের তথà§à¦¯ সেভ করà§à¦¨</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">সবচেয়ে মানানসই</translation>
<translation id="2688969097326701645">হà§à¦¯à¦¾à¦, চালিয়ে যান</translation>
@@ -702,7 +709,6 @@
<translation id="2854764410992194509">ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ সারà§à¦­à¦¿à¦¸ পà§à¦°à§‹à¦­à¦¾à¦‡à¦¡à¦¾à¦° (ISPs)</translation>
<translation id="2856444702002559011">আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ হয়ত <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> থেকে আপনার তথà§à¦¯ (যেমন পাসওয়ারà§à¦¡, মেসেজ বা কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) চà§à¦°à¦¿ করার চেষà§à¦Ÿà¦¾ করছে। <ph name="BEGIN_LEARN_MORE_LINK" />আরও জানà§à¦¨<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">à¦à¦‡ সাইট বà§à¦¯à¦¾à¦˜à¦¾à¦¤ সৃষà§à¦Ÿà¦¿à¦•à¦¾à¦°à§€ বা বিভà§à¦°à¦¾à¦¨à§à¦¤à¦¿à¦•à¦° বিজà§à¦žà¦¾à¦ªà¦¨ দেখায়।</translation>
-<translation id="286512204874376891">কোনও ভারà§à¦šà§à§Ÿà¦¾à¦² কারà§à¦¡ আপনাকে সমà§à¦­à¦¾à¦¬à§à¦¯ জালিয়াতির হাত থেকে রকà§à¦·à¦¾ করতে আপনার আসল কারà§à¦¡à§‡à¦° ছদà§à¦®à¦¬à§‡à¦¶ নিতে পারে। <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">বনà§à¦§à§à¦¤à§à¦¬à¦ªà§‚রà§à¦£</translation>
<translation id="28761159517501904">সিনেমা</translation>
<translation id="2876489322757410363">à¦à¦•à§à¦¸à¦Ÿà¦¾à¦°à§à¦¨à¦¾à¦² অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¨à§‡à¦° মাধà§à¦¯à¦®à§‡ পেমেনà§à¦Ÿ করার জনà§à¦¯ ছদà§à¦®à¦¬à§‡à¦¶à§€ মোড ছেড়ে বেরিয়ে আসা হচà§à¦›à§‡à¥¤ চালিয়ে যেতে চান?</translation>
@@ -803,7 +809,6 @@
<translation id="3158539265159265653">ডিসà§à¦•</translation>
<translation id="3162559335345991374">আপনি যে ওয়াই-ফাইটি বà§à¦¯à¦¬à¦¹à¦¾à¦° করছেন সেটির জনà§à¦¯ অপনাকে à¦à¦Ÿà¦¿à¦° লগ-ইন পৃষà§à¦ à¦¾à¦¤à§‡ যেতে হতে পরে৷</translation>
<translation id="3169472444629675720">আবিষà§à¦•à¦¾à¦° করà§à¦¨</translation>
-<translation id="3174168572213147020">আইলà§à¦¯à¦¾à¦£à§à¦¡</translation>
<translation id="3176929007561373547">পà§à¦°à¦•à§à¦¸à§€ সারà§à¦­à¦¾à¦° কাজ করছে কি না, তা নিশà§à¦šà¦¿à¦¤ করতে আপনার পà§à¦°à¦•à§à¦¸à§€ সেটিংস পরীকà§à¦·à¦¾ করà§à¦¨
বা আপনার নেটওয়ারà§à¦• পà§à¦°à¦¶à¦¾à¦¸à¦•à§‡à¦° সাথে যোগাযোগ করà§à¦¨à§· আপনি কোনো পà§à¦°à¦•à§à¦¸à§€ সারà§à¦­à¦¾à¦°
বà§à¦¯à¦¬à¦¹à¦¾à¦° করবেন না বলে মনে করলে:
@@ -882,9 +887,6 @@
<translation id="3369192424181595722">ঘড়ির তà§à¦°à§à¦Ÿà¦¿</translation>
<translation id="3369459162151165748">যানবাহনের যনà§à¦¤à§à¦°à¦¾à¦‚শ ও অà§à¦¯à¦¾à¦•à§à¦¸à§‡à¦¸à¦°à¦¿</translation>
<translation id="3371064404604898522">Chrome-কে ডিফলà§à¦Ÿ বà§à¦°à¦¾à¦‰à¦œà¦¾à¦° হিসেবে সেট করà§à¦¨</translation>
-<translation id="3371076217486966826"><ph name="URL" /> à¦à¦‡ কাজগà§à¦²à¦¿ করতে চায়:
- • আপনার আশেপাশের জায়গার à¦à¦•à¦Ÿà¦¿ 3D মà§à¦¯à¦¾à¦ª তৈরি করা ও কà§à¦¯à¦¾à¦®à§‡à¦°à¦¾à¦° অবসà§à¦¥à¦¾à¦¨ টà§à¦°à§à¦¯à¦¾à¦• করা
- • আপনার কà§à¦¯à¦¾à¦®à§‡à¦°à¦¾ বà§à¦¯à¦¬à¦¹à¦¾à¦° করা</translation>
<translation id="337363190475750230">পà§à¦°à¦¦à¦¾à¦¨ করবে না</translation>
<translation id="3375754925484257129">'Chrome নিরাপতà§à¦¤à¦¾ সংকà§à¦°à¦¾à¦¨à§à¦¤ পরীকà§à¦·à¦¾' চালান</translation>
<translation id="3377144306166885718">সারà§à¦­à¦¾à¦° TLS-à¦à¦° à¦à¦®à¦¨ à¦à¦•à¦Ÿà¦¿ ভারà§à¦¸à¦¨ বà§à¦¯à¦¬à¦¹à¦¾à¦° করছে যা à¦à¦–ন আর বà§à¦¯à¦¬à¦¹à¦¾à¦°à§‡ নেই।</translation>
@@ -901,6 +903,7 @@
<translation id="3399952811970034796">ডেলিভারির ঠিকানা</translation>
<translation id="3402261774528610252">à¦à¦‡ সাইট লোড করার জনà§à¦¯ যে কানেকশন বà§à¦¯à¦¬à¦¹à¦¾à¦° করা হয়েছে সেটি TLS 1.0 বা TLS 1.1 বà§à¦¯à¦¬à¦¹à¦¾à¦° করেছে যা শীঘà§à¦°à¦‡ বনà§à¦§ করা হবে à¦à¦¬à¦‚ ভবিষà§à¦¯à¦¤à§‡ আর পাওয়া যাবে না। à¦à¦•à¦¬à¦¾à¦° বনà§à¦§ হয়ে যাওয়ার পরে, বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€à¦°à¦¾ আর à¦à¦‡ সাইটটি লোড করতে পারবেন না। সারà§à¦­à¦¾à¦°à¦Ÿà¦¿à¦¤à§‡ TLS 1.2 বা à¦à¦° পরবরà§à¦¤à§€ যেকোনও ভারà§à¦¸à¦¨ চালৠকরতে হবে।</translation>
<translation id="3405664148539009465">হরফগà§à¦²à¦¿ কাসà§à¦Ÿà¦®à¦¾à¦‡à¦œ করà§à¦¨</translation>
+<translation id="3407789382767355356">থারà§à¦¡-পারà§à¦Ÿà¦¿à¦° সাইন-ইন</translation>
<translation id="3409896703495473338">আপনার নিরাপতà§à¦¤à¦¾ সেটিংস মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨</translation>
<translation id="3414952576877147120">মাপ:</translation>
<translation id="3417660076059365994">আপনার আপলোড বা অà§à¦¯à¦¾à¦Ÿà¦¾à¦š করা ফাইল Google Cloud-ঠবা থারà§à¦¡-পারà§à¦Ÿà¦¿à¦° কাছে বিশà§à¦²à§‡à¦·à¦£ করার জনà§à¦¯ পাঠানো হয়েছে। যেমন, কোনও সংবেদনশীল ডেটা বা মà§à¦¯à¦¾à¦²à¦“য়à§à¦¯à¦¾à¦° আছে কিনা জানার জনà§à¦¯ ফাইলগà§à¦²à¦¿ হয়ত সà§à¦•à§à¦¯à¦¾à¦¨ করা হতে পারে।</translation>
@@ -933,6 +936,7 @@
<translation id="3477679029130949506">সিনেমার তালিকা ও সিনেমাহলের সময়সূচি</translation>
<translation id="3479552764303398839">à¦à¦–নই নয়</translation>
<translation id="3484560055331845446">আপনি আপনার Google অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡ অà§à¦¯à¦¾à¦•à§à¦¸à§‡à¦¸ হারাতে পারেন। Chrome-à¦à¦° পà§à¦°à¦¸à§à¦¤à¦¾à¦¬ হল যে আপনি আপনার পাসওয়ারà§à¦¡ à¦à¦–নই পরিবরà§à¦¤à¦¨ করà§à¦¨à¥¤ আপনাকে আবার সাইন-ইন করতে বলা হবে।</translation>
+<translation id="3484861421501147767">রিমাইনà§à¦¡à¦¾à¦°: সেভ করা পà§à¦°à§‹à¦®à§‹ কোড উপলভà§à¦¯ আছে</translation>
<translation id="3487845404393360112">টà§à¦°à§‡ ৪</translation>
<translation id="3495081129428749620">à¦à¦‡ পৃষà§à¦ à¦¾à¦¤à§‡ খà§à¦à¦œà§à¦¨
<ph name="PAGE_TITLE" /></translation>
@@ -1057,6 +1061,7 @@
<translation id="3810973564298564668">পরিচালনা করà§à¦¨</translation>
<translation id="3816482573645936981">মান (পà§à¦°à¦¨à§‹ মানের জায়গাতে নতà§à¦¨ মান পà§à¦°à§Ÿà§‹à¦— করা হয়েছে)</translation>
<translation id="382518646247711829">যদি আপনি à¦à¦•à¦Ÿà¦¿ পà§à¦°à¦•à§à¦¸à¦¿ সারà§à¦­à¦¾à¦° বà§à¦¯à¦¬à¦¹à¦¾à¦° করেন...</translation>
+<translation id="3826050100957962900">থারà§à¦¡-পারà§à¦Ÿà¦¿à¦° সাইন-ইন</translation>
<translation id="3827112369919217609">অà§à¦¯à¦¾à¦¬à¦¸à§‹à¦²à¦¿à¦‰à¦Ÿ</translation>
<translation id="3827666161959873541">পরিবারিক সিনেমা</translation>
<translation id="3828924085048779000">ফাà¦à¦•à¦¾ পাসফà§à¦°à§‡à¦œà§‡à¦° অনà§à¦®à¦¤à¦¿ নেই৷</translation>
@@ -1069,9 +1074,9 @@
<translation id="3858027520442213535">তারিখ à¦à¦¬à¦‚ সময় আপডেট করà§à¦¨</translation>
<translation id="3858860766373142691">নাম</translation>
<translation id="3872834068356954457">বিজà§à¦žà¦¾à¦¨</translation>
+<translation id="3875783148670536197">কীভাবে করতে হবে তা দেখান</translation>
<translation id="3881478300875776315">কম লাইন দেখà§à¦¨</translation>
<translation id="3884278016824448484">পরসà§à¦ªà¦° বিরোধী ডিভাইস সনাকà§à¦¤à¦•à¦¾à¦°à§€</translation>
-<translation id="3885155851504623709">পà§à¦¯à¦¾à¦°à¦¿à¦¶</translation>
<translation id="388632593194507180">মনিটর করা হচà§à¦›à§‡, সেটি শনাকà§à¦¤ করা গেছে</translation>
<translation id="3886948180919384617">সà§à¦Ÿà§à¦¯à¦¾à¦•à¦¾à¦° ৩</translation>
<translation id="3890664840433101773">ইমেল আইডি যোগ করà§à¦¨</translation>
@@ -1101,9 +1106,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> অবরà§à¦¦à§à¦§ হয়ে রয়েছে</translation>
<translation id="3978338123949022456">সারà§à¦š মোড, <ph name="KEYWORD_SUFFIX" /> বà§à¦¯à¦¬à¦¹à¦¾à¦° করে সারà§à¦š করতে à¦à¦•à¦Ÿà¦¿ কোয়েরি টাইপ করে Enter কী পà§à¦°à§‡à¦¸ করà§à¦¨</translation>
<translation id="398470910934384994">পাখি</translation>
+<translation id="3985750352229496475">ঠিকানাগà§à¦²à¦¿ মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨...</translation>
<translation id="3986705137476756801">à¦à¦–নকার মতো লাইভ কà§à¦¯à¦¾à¦ªà¦¶à¦¨ বনà§à¦§ করে দিন</translation>
<translation id="3987940399970879459">১ à¦à¦®à¦¬à¦¿à¦° কম</translation>
<translation id="3990250421422698716">জগ অফসেট</translation>
+<translation id="3992684624889376114">à¦à¦‡ পৃষà§à¦ à¦¾ সমà§à¦ªà¦°à§à¦•à§‡</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> সাইট থেকে অনà§à¦°à§‹à¦§ করা হয়েছে যে à¦à¦¤à§‡ করা সমসà§à¦¤ অনà§à¦°à§‹à¦§à§‡à¦° কà§à¦·à§‡à¦¤à§à¦°à§‡
à¦à¦•à¦Ÿà¦¿ 'অরিজিন নীতি' পà§à¦°à§Ÿà§‹à¦— করা হোক, কিনà§à¦¤à§ বরà§à¦¤à¦®à¦¾à¦¨à§‡ à¦à¦‡ নীতি কারà§à¦¯à¦•à¦° করা যাচà§à¦›à§‡ না।</translation>
<translation id="4006465311664329701">Google Pay-à¦à¦° সাথে যà§à¦•à§à¦¤ 'পেমেনà§à¦Ÿ পদà§à¦§à¦¤à¦¿', 'অফার' ও 'ঠিকানা'</translation>
@@ -1228,6 +1235,7 @@
<translation id="4305666528087210886">আপনার ফাইল অà§à¦¯à¦¾à¦•à§à¦¸à§‡à¦¸ করা যায়নি</translation>
<translation id="4306529830550717874">ঠিকানা সেভ করবেন?</translation>
<translation id="4306812610847412719">কà§à¦²à¦¿à¦ªà¦¬à§‹à¦°à§à¦¡</translation>
+<translation id="4310070645992025887">আপনার অà§à¦¯à¦¾à¦•à§à¦Ÿà¦¿à¦­à¦¿à¦Ÿà¦¿à¦° সিরিজ সারà§à¦š করà§à¦¨</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">অবরà§à¦¦à§à¦§ করà§à¦¨ (ডিফলà§à¦Ÿ)</translation>
<translation id="4314815835985389558">সিঙà§à¦• মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨</translation>
@@ -1278,6 +1286,7 @@
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">পà§à¦°à¦•à§à¦¸à¦¿à¦° বà§à¦¯à¦¬à¦¹à¦¾à¦° অকà§à¦·à¦® করা হয়েছে কিনà§à¦¤à§ কোনো সà§à¦ªà¦·à§à¦Ÿ পà§à¦°à¦•à§à¦¸à¦¿ কনফিগারেশান নিরà§à¦¦à¦¿à¦·à§à¦Ÿ করা হয়েছে৷</translation>
<translation id="4441832193888514600">উপেকà§à¦·à¦¾ করা হয়েছে কারণ নীতিটি শà§à¦§à§à¦®à¦¾à¦¤à§à¦° কà§à¦²à¦¾à¦‰à¦¡ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ নীতি হিসেবে সেট করা যেতে পারে।</translation>
+<translation id="4442470707340296952">Chrome টà§à¦¯à¦¾à¦¬</translation>
<translation id="4450893287417543264">আর দেখতে চাই না</translation>
<translation id="4451135742916150903">HID ডিভাইসের সাথে কানেকà§à¦Ÿ করার অনà§à¦®à¦¤à¦¿ চাইতে পারে</translation>
<translation id="4452328064229197696">আপনি à¦à¦‡à¦®à¦¾à¦¤à§à¦° যে পাসওয়ারà§à¦¡ বà§à¦¯à¦¬à¦¹à¦¾à¦° করলেন, সেটি হà§à¦¯à¦¾à¦• হওয়া কোনও ডেটাবেসে পাওয়া গেছে। আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ সà§à¦°à¦•à§à¦·à¦¿à¦¤ করতে, Google পাসওয়ারà§à¦¡ মà§à¦¯à¦¾à¦¨à§‡à¦œà¦¾à¦° আপনার সেভ করা পাসওয়ারà§à¦¡ চেক করার সাজেশন দিচà§à¦›à§‡à¥¤</translation>
@@ -1416,6 +1425,7 @@
<translation id="483241715238664915">সতরà§à¦•à¦¤à¦¾ চালৠকরà§à¦¨</translation>
<translation id="4834250788637067901">Google Pay-à¦à¦° সাথে যà§à¦•à§à¦¤ পেমেনà§à¦Ÿ পদà§à¦§à¦¤à¦¿, অফার ও ঠিকানা</translation>
<translation id="4838327282952368871">সà§à¦¬à¦ªà§à¦¨à¦¾à¦²à§</translation>
+<translation id="4839087176073128681">পরেরবার দà§à¦°à§à¦¤ পেমেনà§à¦Ÿ করà§à¦¨ à¦à¦¬à¦‚ মারà§à¦•à§‡à¦Ÿà§‡ উপলভà§à¦¯ Google-à¦à¦° সেরা নিরাপতà§à¦¤à¦¾ দিয়ে আপনার কারà§à¦¡à¦•à§‡ সà§à¦°à¦•à§à¦·à¦¿à¦¤ করà§à¦¨à¥¤</translation>
<translation id="4840250757394056958">আপনার Chrome ইতিহাস দেখà§à¦¨</translation>
<translation id="484462545196658690">অটো</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" />-ঠডিসকাউনà§à¦Ÿ পান à¦à¦¬à¦‚ আরও অনেক কিছà§</translation>
@@ -1478,6 +1488,7 @@
<translation id="5011561501798487822">শনাকà§à¦¤ করা ভাষা</translation>
<translation id="5015510746216210676">মেশিনের নাম:</translation>
<translation id="5017554619425969104">আপনার কপি করা টেকà§à¦¸à¦Ÿ</translation>
+<translation id="5017828934289857214">আমাকে পরে মনে করিয়ে দিও</translation>
<translation id="5018422839182700155">à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ খোলা যাচà§à¦›à§‡ না</translation>
<translation id="5019198164206649151">বà§à¦¯à¦¾à¦•à¦¿à¦‚ সà§à¦Ÿà§‹à¦°à¦Ÿà¦¿ তà§à¦°à§à¦Ÿà¦¿à¦ªà§‚রà§à¦£ অবসà§à¦¥à¦¾à§Ÿ আছে</translation>
<translation id="5020776957610079374">ওয়ারà§à¦²à§à¦¡ মিউজিক</translation>
@@ -1497,6 +1508,7 @@
<translation id="5051305769747448211">লাইভ কমেডি</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{নিয়ারবাই শেয়ার বà§à¦¯à¦¬à¦¹à¦¾à¦° করে à¦à¦‡ ফাইল পাঠাতে চাইলে, আপনার ডিভাইসে (<ph name="DISK_SPACE_SIZE" />) জায়গা খালি করà§à¦¨}one{নিয়ারবাই শেয়ার বà§à¦¯à¦¬à¦¹à¦¾à¦° করে à¦à¦‡ ফাইলগà§à¦²à¦¿ পাঠাতে চাইলে, আপনার ডিভাইসে (<ph name="DISK_SPACE_SIZE" />) জায়গা খালি করà§à¦¨}other{নিয়ারবাই শেয়ার বà§à¦¯à¦¬à¦¹à¦¾à¦° করে à¦à¦‡ ফাইলগà§à¦²à¦¿ পাঠাতে চাইলে, আপনার ডিভাইসে (<ph name="DISK_SPACE_SIZE" />) জায়গা খালি করà§à¦¨}}</translation>
<translation id="5056549851600133418">আপনার জনà§à¦¯ নিবনà§à¦§à¦—à§à¦²à¦¿</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> বà§à¦¯à¦¬à¦¹à¦¾à¦° করে à¦à¦®à¦¨ ওয়েবসাইটে Windows Hello বà§à¦¯à¦¬à¦¹à¦¾à¦° করে যাচাই করার বিকলà§à¦ª বেছে নিয়েছেন। à¦à¦‡ পরিষেবা পà§à¦°à¦¦à¦¾à¦¨à¦•à¦¾à¦°à§€ আপনার পেমেনà§à¦Ÿ পদà§à¦§à¦¤à¦¿ সমà§à¦ªà¦°à§à¦•à§‡ তথà§à¦¯ সà§à¦Ÿà§‹à¦° করে থাকতে পারে যা আপনি <ph name="LINK_TEXT" /> পারেন।</translation>
<translation id="5061227663725596739">আপনি কি <ph name="LOOKALIKE_DOMAIN" /> ডোমেনের কথা বলছেন?</translation>
<translation id="5066056036849835175">পà§à¦°à¦¿à¦¨à§à¦Ÿ করার ইতিহাস</translation>
<translation id="5068234115460527047">হেজ তহবিল</translation>
@@ -1510,10 +1522,8 @@
<translation id="5087286274860437796">সারà§à¦­à¦¾à¦°à§‡à¦° সারà§à¦Ÿà¦¿à¦«à¦¿à¦•à§‡à¦Ÿ à¦à¦‡ সময়ে বৈধ নয়৷</translation>
<translation id="5087580092889165836">কারà§à¦¡ জà§à¦¡à¦¼à§à¦¨</translation>
<translation id="5088142053160410913">অপারেটরকে মেসেজ করà§à¦¨</translation>
-<translation id="5089810972385038852">রাজà§à¦¯</translation>
<translation id="5093232627742069661">জেড ফোলà§à¦¡</translation>
<translation id="5094747076828555589">à¦à¦‡ সারà§à¦­à¦¾à¦° পà§à¦°à¦®à¦¾à¦£ করতে পারেনি যে à¦à¦Ÿà¦¿ <ph name="DOMAIN" />; à¦à¦° নিরাপতà§à¦¤à¦¾ সারà§à¦Ÿà¦¿à¦«à¦¿à¦•à§‡à¦Ÿà¦Ÿà¦¿ Chromium à¦à¦° নিকট বিশà§à¦¬à¦¾à¦¸à¦¯à§‹à¦—à§à¦¯ নয়। কোনো ভà§à¦² কনফিগারেশনের কারণে অথবা কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ আপনার সংযোগ মাà¦à¦ªà¦¥à§‡ আটকে দিচà§à¦›à§‡ বলে à¦à¦®à¦¨à¦Ÿà¦¾ হতে পারে।</translation>
-<translation id="5095208057601539847">পà§à¦°à¦¦à§‡à¦¶</translation>
<translation id="5097099694988056070">CPU/RAM বà§à¦¯à¦¬à¦¹à¦¾à¦°à§‡à¦° মতো ডিভাইসের পরিসংখà§à¦¯à¦¾à¦¨</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">সাইটটি সà§à¦°à¦•à§à¦·à¦¿à¦¤ নয়</translation>
@@ -1551,6 +1561,7 @@
<translation id="5171045022955879922">খà§à¦à¦œà§à¦¨ বা URL লিখà§à¦¨</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">যনà§à¦¤à§à¦°</translation>
+<translation id="5177076414499237632">পৃষà§à¦ à¦¾à¦° সোরà§à¦¸ ও বিষয় সমà§à¦ªà¦°à§à¦•à§‡ আরও জানà§à¦¨</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />-ঠনেই? à¦à¦‡ তà§à¦°à§à¦Ÿà¦¿ রিপোরà§à¦Ÿ করà§à¦¨</translation>
<translation id="518639307526414276">পোষা খাদà§à¦¯ à¦à¦¬à¦‚ পোষা যতà§à¦¨ সরবরাহ</translation>
<translation id="5190835502935405962">বà§à¦•à¦®à¦¾à¦°à§à¦• বার</translation>
@@ -1711,6 +1722,7 @@
<translation id="5624120631404540903">পাসওয়ারà§à¦¡à¦—à§à¦²à¦¿ পরিচালনা করà§à¦¨</translation>
<translation id="5629630648637658800">নীতি সেটিংস লোড করতে বà§à¦¯à¦°à§à¦¥ হয়েছে</translation>
<translation id="5631439013527180824">ভà§à¦² ডিভাইস পরিচালনা টোকেন</translation>
+<translation id="5632485077360054581">কীভাবে করতে হবে তা দেখান</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />-ঠযে আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ à¦à¦‡ মà§à¦¹à§‚রà§à¦¤à§‡ সকà§à¦°à¦¿à§Ÿ আছে, তারা আপনার কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡ à¦à¦®à¦¨ বিপজà§à¦œà¦¨à¦• পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® ইনসà§à¦Ÿà¦² করে দিতে পারে যেগà§à¦²à¦¿ আপনার তথà§à¦¯à§‡à¦° (যেমন ফটো, পাসওয়ারà§à¦¡, মেসেজ à¦à¦¬à¦‚ কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) কà§à¦·à¦¤à¦¿ করতে বা সেগà§à¦²à¦¿ চà§à¦°à¦¿ করতে পারে। <ph name="BEGIN_LEARN_MORE_LINK" />আরও জানà§à¦¨<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">পà§à¦°à¦¤à¦¾à¦°à¦£à¦¾à¦®à§‚লক কনà§à¦Ÿà§‡à¦¨à§à¦Ÿ বà§à¦²à¦• করা হয়েছে।</translation>
<translation id="5633259641094592098">কালà§à¦Ÿ à¦à¦¬à¦‚ ইনà§à¦¡à¦¿ সিনেমা</translation>
@@ -1828,6 +1840,7 @@
<translation id="5989320800837274978">কোনো নিরà§à¦§à¦¾à¦°à¦¿à¦¤ পà§à¦°à¦•à§à¦¸à¦¿ সারà§à¦­à¦¾à¦° অথবা à¦à¦•à¦Ÿà¦¿.pac সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ UR সà§à¦°à§à¦¨à¦¿à¦¦à¦¿à¦·à§à¦Ÿà¦­à¦¾à¦¬à§‡ উলà§à¦²à§‡à¦– করা হয়নি৷</translation>
<translation id="5992691462791905444">ইঞà§à¦œà¦¿à¦¨à¦¿à§Ÿà¦¾à¦°à¦¿à¦‚ জেড-ফোলà§à¦¡</translation>
<translation id="5995727681868049093">আপনার Google অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡ নিজের সমà§à¦ªà¦°à§à¦•à§‡ তথà§à¦¯, গোপনীয়তা à¦à¦¬à¦‚ সà§à¦°à¦•à§à¦·à¦¾ মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨</translation>
+<translation id="5997247540087773573">আপনি à¦à¦‡à¦®à¦¾à¦¤à§à¦° যে পাসওয়ারà§à¦¡ বà§à¦¯à¦¬à¦¹à¦¾à¦° করলেন, সেটি হà§à¦¯à¦¾à¦• হওয়া কোনও ডেটাবেসে পাওয়া গেছে। আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ সà§à¦°à¦•à§à¦·à¦¿à¦¤ করতে, Google পাসওয়ারà§à¦¡ মà§à¦¯à¦¾à¦¨à§‡à¦œà¦¾à¦° à¦à¦–নই à¦à¦Ÿà¦¿ পরিবরà§à¦¤à¦¨ করতে ও সেভ করা পাসওয়ারà§à¦¡ চেক করার সাজেশন দিচà§à¦›à§‡à¥¤</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />'-à¦à¦° জনà§à¦¯ <ph name="RESULT_COUNT" />টি ফলাফল</translation>
<translation id="6006484371116297560">কà§à¦²à¦¾à¦¸à¦¿à¦•</translation>
<translation id="6008122969617370890">N-to-1 অরà§à¦¡à¦¾à¦°</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">পরের বার আরও দà§à¦°à§à¦¤ পেমেনà§à¦Ÿ করা জনà§à¦¯ আপনার à¦à¦‡ কারà§à¦¡ à¦à¦¬à¦‚ বিলিং ঠিকানাটি Google অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡ সেভ করে রাখà§à¦¨à¥¤</translation>
<translation id="6279183038361895380">আপনার কারà§à¦¸à¦¾à¦° দেখাতে |<ph name="ACCELERATOR" />| চাপà§à¦¨</translation>
<translation id="6280223929691119688">à¦à¦‡ ঠিকানায় ডেলিভারি করা যাবে না। অনà§à¦¯ ঠিকানা বেছে নিন।</translation>
-<translation id="6282194474023008486">পোসà§à¦Ÿà¦¾à¦² কোড</translation>
<translation id="6285507000506177184">Chrome বোতামে ডাউনলোড মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨, Chrome-ঠডাউনলোড করা ফাইল মà§à¦¯à¦¾à¦¨à§‡à¦œ করতে Enter পà§à¦°à§‡à¦¸ করà§à¦¨</translation>
<translation id="6289939620939689042">পৃষà§à¦ à¦¾à¦° রঙ</translation>
<translation id="6290238015253830360">আপনার পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¿à¦¤ নিবনà§à¦§à¦—à§à¦²à¦¿ à¦à¦–ানে দেখা যাবে</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026"><ph name="SIZE" />-à¦à¦° চেয়ে কম জায়গা খালি করে। পরের বার যখন দেখবেন তখন কিছৠসাইট লোড হতে দেরি হতে পারে।</translation>
<translation id="6337534724793800597">নাম অনà§à¦¸à¦¾à¦°à§‡ ফিলà§à¦Ÿà¦¾à¦°à¦—à§à¦²à¦¿ বাছাই করà§à¦¨</translation>
<translation id="6340739886198108203">গোপনীয় কনà§à¦Ÿà§‡à¦¨à§à¦Ÿ দেখা গেলে অà§à¦¯à¦¾à¦¡à¦®à¦¿à¦¨à¦¿à¦¸à§à¦Ÿà§à¦°à§‡à¦Ÿà¦°à§‡à¦° নীতি সà§à¦•à§à¦°à¦¿à¦¨à¦¶à¦Ÿ নেওয়া অথবা রেকরà§à¦¡à¦¿à¦‚ করার সাজেশন দেয় না:</translation>
+<translation id="6348220984832452017">অà§à¦¯à¦¾à¦•à§à¦Ÿà¦¿à¦­ ভà§à¦¯à¦¾à¦°à¦¿à§Ÿà§‡à¦¶à¦¨</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ইনসà§à¦Ÿà¦² করà§à¦¨</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{à¦à¦•à¦Ÿà¦¿à¦“ নয়}=1{১টি পাসওয়ারà§à¦¡ (<ph name="DOMAIN_LIST" />-à¦à¦° জনà§à¦¯, সিঙà§à¦• করা হয়েছে)}=2{২টি পাসওয়ারà§à¦¡ (<ph name="DOMAIN_LIST" />-à¦à¦° জনà§à¦¯, সিঙà§à¦• করা হয়েছে)}one{#টি পাসওয়ারà§à¦¡ (<ph name="DOMAIN_LIST" />-à¦à¦° জনà§à¦¯, সিঙà§à¦• করা হয়েছে)}other{#টি পাসওয়ারà§à¦¡ (<ph name="DOMAIN_LIST" />-à¦à¦° জনà§à¦¯, সিঙà§à¦• করা হয়েছে)}}</translation>
<translation id="6355392890578844978">কোনও কোমà§à¦ªà¦¾à¦¨à¦¿ বা অনà§à¦¯ কোনও সংসà§à¦¥à¦¾ à¦à¦‡ বà§à¦°à¦¾à¦‰à¦œà¦¾à¦° মà§à¦¯à¦¾à¦¨à§‡à¦œ করে না। à¦à¦‡ ডিভাইসের অà§à¦¯à¦¾à¦•à§à¦Ÿà¦¿à¦­à¦¿à¦Ÿà¦¿ Chromium-à¦à¦° বাইরে থেকে মà§à¦¯à¦¾à¦¨à§‡à¦œ করা যেতে পারে। <ph name="BEGIN_LINK" />আরও জানà§à¦¨<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">Google পাসওয়ারà§à¦¡ পরিবরà§à¦¤à¦¨ করà§à¦¨</translation>
<translation id="6433490469411711332">পরিচিতি তথà§à¦¯ সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ করà§à¦¨</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> সংযোগ করতে পà§à¦°à¦¤à§à¦¯à¦¾à¦–à§à¦¯à¦¾à¦¨ করেছে।</translation>
-<translation id="6438025220577812695">নিজে পরিবরà§à¦¤à¦¨ করব</translation>
<translation id="6440503408713884761">à¦à§œà¦¾à¦¨à§‹ হয়েছে</translation>
<translation id="6443406338865242315">কোন কোন à¦à¦•à§à¦¸à¦Ÿà§‡à¦¨à¦¶à¦¨ ও পà§à¦²à¦¾à¦—-ইন আপনি ইনসà§à¦Ÿà¦² করেছেন</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">জীনততà§à¦¤à§à¦¬</translation>
<translation id="6831043979455480757">অনà§à¦¬à¦¾à¦¦</translation>
<translation id="6833752742582340615">নিরাপদ à¦à¦¬à¦‚ দà§à¦°à§à¦¤ চেকআউটের জনà§à¦¯ আপনার Google অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡ কারà§à¦¡ à¦à¦¬à¦‚ বিলিংয়ের তথà§à¦¯ সেভ করà§à¦¨</translation>
-<translation id="6839929833149231406">à¦à¦²à¦¾à¦•à¦¾</translation>
<translation id="6846340164947227603">কোনও à¦à¦•à¦Ÿà¦¿ ভারà§à¦šà§à§Ÿà¦¾à¦² কারà§à¦¡ নমà§à¦¬à¦° বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨...</translation>
<translation id="6852204201400771460">আবার অà§à¦¯à¦¾à¦ª লোড করতে চান?</translation>
+<translation id="6857776781123259569">পাসওয়ারà§à¦¡ মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨â€¦</translation>
<translation id="686485648936420384">কনজিউমার রিসোরà§à¦¸</translation>
<translation id="6865412394715372076">à¦à¦‡ কারà§à¦¡à¦Ÿà¦¿ à¦à¦–নই যাচাই করা যাবে না</translation>
<translation id="6869334554832814367">পারà§à¦¸à§‹à¦¨à¦¾à¦² লোন</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">ডিভাইস</translation>
<translation id="696703987787944103">পারসেপচà§à§Ÿà¦¾à¦²</translation>
<translation id="6968269510885595029">আপনার 'নিরাপতà§à¦¤à¦¾ কী' বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨</translation>
-<translation id="6970216967273061347">জেলা</translation>
<translation id="6971439137020188025">Slides-ঠচটপট নতà§à¦¨ Google পà§à¦°à§‡à¦œà§‡à¦¨à§à¦Ÿà§‡à¦¶à¦¨ তৈরি করà§à¦¨</translation>
<translation id="6972629891077993081">HID ডিভাইস</translation>
<translation id="6973656660372572881">সà§à¦¥à¦¿à¦° পà§à¦°à¦•à§à¦¸à¦¿ সারà§à¦­à¦¾à¦° à¦à¦¬à¦‚ .pac সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ URL-à¦à¦° উভয়ই নিরà§à¦¦à¦¿à¦·à§à¦Ÿ আছে৷</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">à¦à¦‡ ফিচারটি আপনার ডিভাইসে পাওয়া যাবে না</translation>
<translation id="7083258188081898530">টà§à¦°à§‡ ৯</translation>
<translation id="7086090958708083563">বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ আপলোড করার অনà§à¦°à§‹à¦§ জানিয়েছেন</translation>
-<translation id="7087282848513945231">দেশ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome সেটিংসে বিভিনà§à¦¨ সাইটের সà§à¦Ÿà§‹à¦° করা ডেটা ও অনà§à¦®à¦¤à¦¿ মà§à¦¯à¦¾à¦¨à§‡à¦œ করতে পà§à¦°à¦¥à¦®à§‡ Tab, তারপরে Enter পà§à¦°à§‡à¦¸ করà§à¦¨</translation>
<translation id="7096937462164235847">à¦à¦‡ ওয়েবসাইটের পরিচয় যাচাই করা যায়নি।</translation>
<translation id="7101893872976785596">ভূতের সিনেমা</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />ফরà§à¦®à§‡ তথà§à¦¯ দেওয়া হয়েছে<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">à¦à¦‡ ঠিকানায় শিপিং করা যাবে না। অনà§à¦¯ ঠিকানা বেছে নিন।</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">আপনার অà§à¦¯à¦¾à¦¡à¦®à¦¿à¦¨ à¦à¦‡ ডেটা কপি করার থেকে সীমাবদà§à¦§ করেছেন।</translation>
<translation id="7135130955892390533">সà§à¦Ÿà§à¦¯à¦¾à¦Ÿà¦¾à¦¸ দেখà§à¦¨</translation>
<translation id="7138472120740807366">ডেলিভারির পদà§à¦§à¦¤à¦¿</translation>
-<translation id="7139724024395191329">Emirate</translation>
<translation id="7139892792842608322">পà§à¦°à¦¾à¦‡à¦®à¦¾à¦°à¦¿ টà§à¦°à§‡</translation>
<translation id="714064300541049402">সাইড 2 ছবি X শিফà§à¦Ÿ</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">Attackers on this site might try to trick you into installing programs that harm your browsing experience (for example, by changing your homepage or showing extra ads on sites you visit).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{গোপন হিসেবে চিহà§à¦¨à¦¿à¦¤ ডেটা বà§à¦¯à¦¬à¦¹à¦¾à¦° করে যে অà§à¦¯à¦¾à¦•à¦¶à¦¨ নেওয়া হয়েছে (লগ-ইন করার পরে ১টি অà§à¦¯à¦¾à¦•à¦¶à¦¨)। <ph name="BEGIN_LINK" />আরও জানà§à¦¨<ph name="END_LINK" />}one{গোপন হিসেবে চিহà§à¦¨à¦¿à¦¤ ডেটা বà§à¦¯à¦¬à¦¹à¦¾à¦° করে যে অà§à¦¯à¦¾à¦•à¦¶à¦¨ নেওয়া হয়েছে (লগ-ইন করার পরে #টি অà§à¦¯à¦¾à¦•à¦¶à¦¨)। <ph name="BEGIN_LINK" />আরও জানà§à¦¨<ph name="END_LINK" />}other{গোপন হিসেবে চিহà§à¦¨à¦¿à¦¤ ডেটা বà§à¦¯à¦¬à¦¹à¦¾à¦° করে যে অà§à¦¯à¦¾à¦•à¦¶à¦¨ নেওয়া হয়েছে (লগ-ইন করার পরে #টি অà§à¦¯à¦¾à¦•à¦¶à¦¨)। <ph name="BEGIN_LINK" />আরও জানà§à¦¨<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">মেলবকà§à¦¸ ৬</translation>
+<translation id="7675325315208090829">পেমেনà§à¦Ÿ পদà§à¦§à¦¤à¦¿à¦—à§à¦²à¦¿ মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨...</translation>
<translation id="7676643023259824263">কà§à¦²à¦¿à¦ªà¦¬à§‹à¦°à§à¦¡ টেকà§à¦¸à¦Ÿ <ph name="TEXT" /> সারà§à¦š করà§à¦¨</translation>
<translation id="7679367271685653708">Chrome সেটিংস থেকে আপনার বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚য়ের ইতিহাস দেখà§à¦¨ à¦à¦¬à¦‚ মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨</translation>
<translation id="7679947978757153706">বেসবল</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">সà§à¦•à¦¾à¦°à§à¦Ÿ</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">à¦à¦•à¦‡ অরà§à¦¡à¦¾à¦°, সামনের দিক উপরে</translation>
-<translation id="777702478322588152">জেলা</translation>
<translation id="7791011319128895129">à¦à¦–নও পà§à¦°à¦•à¦¾à¦¶ করা হয়নি</translation>
<translation id="7791196057686275387">বেল</translation>
<translation id="7791543448312431591">জà§à¦¡à¦¼à§à¦¨</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">বৃতà§à¦¤à¦¿à¦®à§‚লক ও অবà§à¦¯à¦¾à¦¹à¦¤ শিকà§à¦·à¦¾</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" সঠিকভাবে কনফিগার হয়নি। সাধারণত "<ph name="SOFTWARE_NAME" />" আন-ইনসà§à¦Ÿà¦² করা হলে সমসà§à¦¯à¦¾à¦° সমাধান হয়ে যায়। <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">খাদà§à¦¯ উৎপাদন</translation>
-<translation id="8066955247577885446">কিছৠসমসà§à¦¯à¦¾ হয়েছে।</translation>
<translation id="8067872629359326442">আপনি à¦à¦–নই পà§à¦°à¦¤à¦¾à¦°à¦£à¦¾à¦®à§‚লক à¦à¦•à¦Ÿà¦¿ সাইটে আপনার পাসওয়ারà§à¦¡ লিখেছেন। Chromium সাহাযà§à¦¯ করবে। আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡à¦° নিরাপতà§à¦¤à¦¾ লঙà§à¦˜à¦¨à§‡à¦° সমà§à¦­à¦¾à¦¬à¦¨à¦¾ à¦à¦¡à¦¼à¦¾à¦¨à§‡à¦¾à¦° জনà§à¦¯ পাসওয়ারà§à¦¡ পরিবরà§à¦¤à¦¨ করতে à¦à¦¬à¦‚ Google-কে বিজà§à¦žà¦ªà§à¦¤à¦¿ দিয়ে জানাতে 'অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ সà§à¦°à¦•à§à¦·à¦¿à¦¤ রাখà§à¦¨' বিকলà§à¦ªà§‡ কà§à¦²à¦¿à¦• করà§à¦¨à¥¤</translation>
<translation id="8070439594494267500">অà§à¦¯à¦¾à¦ª আইকন</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">চটপট Google Sheet তৈরি করà§à¦¨</translation>
<translation id="8075898834294118863">'সাইট সেটিংস' বোতাম মà§à¦¯à¦¾à¦¨à§‡à¦œ করà§à¦¨</translation>
+<translation id="8076492880354921740">টà§à¦¯à¦¾à¦¬à¦—à§à¦²à¦¿</translation>
<translation id="8078141288243656252">ঘূরà§à¦£à¦¨à§‡à¦° সময় বà§à¦¯à¦¾à¦–à§à¦¯à¦¾ করা যাবে না</translation>
<translation id="8079031581361219619">সাইটটি রিলোড করবেন?</translation>
<translation id="8081087320434522107">সেডান</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">সেটিংস</translation>
+<translation id="8428634594422941299">বà§à¦à§‡à¦›à¦¿</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome সেটিংসে আপনার কà§à¦•à¦¿à¦° অগà§à¦°à¦¾à¦§à¦¿à¦•à¦¾à¦° মà§à¦¯à¦¾à¦¨à§‡à¦œ করতে পà§à¦°à¦¥à¦®à§‡ Tab, তারপরে Enter পà§à¦°à§‡à¦¸ করà§à¦¨</translation>
<translation id="8433057134996913067">à¦à¦Ÿà¦¿ বেশিরভাগ ওয়েবসাইট থেকে আপনাকে পà§à¦°à¦¸à§à¦¥à¦¾à¦¨ করà§à¦¨ করবে।</translation>
<translation id="8434840396568290395">পোষà§à¦¯</translation>
@@ -2792,6 +2803,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> হল আপনার <ph name="ORIGIN" />-à¦à¦° কোড</translation>
<translation id="874918643257405732">à¦à¦‡ টà§à¦¯à¦¾à¦¬ বà§à¦•à¦®à¦¾à¦°à§à¦• করà§à¦¨</translation>
<translation id="8751426954251315517">পরে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨</translation>
+<translation id="8757526089434340176">Google Pay অফার উপলভà§à¦¯</translation>
<translation id="8758885506338294482">পà§à¦°à¦¤à¦¿à¦¯à§‹à¦—িতামূলক ভিডিও গেমিং</translation>
<translation id="8759274551635299824">à¦à¦‡ কারà§à¦¡à¦Ÿà¦¿à¦° মেয়াদ শেষ হয়েছে</translation>
<translation id="87601671197631245">à¦à¦‡ সাইটটি পà§à¦°à¦¨à§‹ সিকিউরিটি কনফিগারেশন বà§à¦¯à¦¬à¦¹à¦¾à¦° করে তাই আপনার তথà§à¦¯ (যেমন, পাসওয়ারà§à¦¡, মেসেজ বা কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡à§‡à¦° নমà§à¦¬à¦°) à¦à¦‡ সাইটে পাঠানোর সময় à¦à¦‡à¦¸à¦¬ তথà§à¦¯à§‡à¦° নিরাপতà§à¦¤à¦¾ বিঘà§à¦¨à¦¿à¦¤ হতে পারে।</translation>
@@ -2799,6 +2811,7 @@
<translation id="8763927697961133303">USB ডিভাইস</translation>
<translation id="8763986294015493060">বরà§à¦¤à¦®à¦¾à¦¨à§‡ খোলা আছে à¦à¦®à¦¨ সব 'ছদà§à¦®à¦¬à§‡à¦¶à§€' উইনà§à¦¡à§‹ বনà§à¦§ করà§à¦¨</translation>
<translation id="8766943070169463815">নিরাপদে পেমেনà§à¦Ÿ কà§à¦°à§‡à¦¡à§‡à¦¨à¦¶à¦¿à§Ÿà¦¾à¦² যাচাই করার শিট খোলা হয়েছে</translation>
+<translation id="8767765348545497220">সহায়তা বাবল বনà§à¦§ করà§à¦¨</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">মোটর সাইকেল</translation>
<translation id="8790007591277257123">&amp;মà§à¦›à§‡ ফেলাকে আবার করà§à¦¨</translation>
@@ -2811,6 +2824,7 @@
<translation id="8806285662264631610">সà§à¦¨à¦¾à¦¨ à¦à¦¬à¦‚ বডিতে বà§à¦¯à¦¬à¦¹à¦¾à¦° করার পà§à¦°à§‹à¦¡à¦¾à¦•à§à¦Ÿ</translation>
<translation id="8807160976559152894">পà§à¦°à¦¤à¦¿à¦Ÿà¦¿ পৃষà§à¦ à¦¾à¦° পর টà§à¦°à¦¿à¦® করà§à¦¨</translation>
<translation id="8808828119384186784">Chrome সেটিংস</translation>
+<translation id="8813277370772331957">আমাকে পরে মনে করানো হোক</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, আপনার Chrome সেটিংসে Chrome আপডেট করতে, পà§à¦°à¦¥à¦®à§‡ Tab আর তারপরে Enter পà§à¦°à§‡à¦¸ করà§à¦¨</translation>
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="882338992931677877">মà§à¦¯à¦¾à¦¨à§à§Ÿà¦¾à¦² সà§à¦²à¦Ÿ</translation>
@@ -2990,6 +3004,7 @@
<translation id="988159990683914416">ডেভেলপার বিলà§à¦¡</translation>
<translation id="989988560359834682">ঠিকানা সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ করà§à¦¨</translation>
<translation id="991413375315957741">মোশন বা লাইট সেনà§à¦¸à¦°</translation>
+<translation id="992110854164447044">আপনাকে সমà§à¦­à¦¾à¦¬à§à¦¯ জালিয়াতির হাত থেকে রকà§à¦·à¦¾ করতে, ভারà§à¦šà§à§Ÿà¦¾à¦² কারà§à¦¡ আসল কারà§à¦¡à¦Ÿà¦¿ লà§à¦•à¦¾à§Ÿà¥¤ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">গোলাপী</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" আপনার কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦° বা নেটওয়ারà§à¦•à§‡ সঠিকভাবে ইনসà§à¦Ÿà¦² করা হয়নি:
diff --git a/chromium/components/strings/components_strings_bs.xtb b/chromium/components/strings/components_strings_bs.xtb
index 9faa55d99f8..266341511d8 100644
--- a/chromium/components/strings/components_strings_bs.xtb
+++ b/chromium/components/strings/components_strings_bs.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Grantovi, stipendije i finansijska pomoć</translation>
<translation id="1048785276086539861">Kada uredite bilješke, ovaj dokument će se vratiti na prikaz na jednoj stranici</translation>
<translation id="1050038467049342496">Zatvorite druge aplikacije</translation>
+<translation id="1053959602163383901">Možete odabrati potvrdu pomoću ureÄ‘aja za autentifikaciju na web lokacijama koje koriste pružatelja usluga <ph name="PROVIDER_ORIGIN" />. Ovaj pružalac usluga je možda pohranio informacije o vaÅ¡em naÄinu plaćanja, za koji možete <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Poništi dodavanje</translation>
<translation id="1056663316309890343">Softver za fotografije</translation>
<translation id="1056898198331236512">Upozorenje</translation>
@@ -38,7 +39,7 @@
<translation id="1068672505746868501">Nemoj nikada prevoditi stranice na <ph name="SOURCE_LANGUAGE" /> jezik</translation>
<translation id="1070853536588271387">Hibridna i alternativna vozila</translation>
<translation id="1070901266639972381">Noć</translation>
-<translation id="1072594122896439679">Muzika i audio zapisi</translation>
+<translation id="1072594122896439679">Muzika i audiozapisi</translation>
<translation id="1074497978438210769">Nije sigurno</translation>
<translation id="1075079914415273530">Poljoprivreda i Å¡umarstvo</translation>
<translation id="1080116354587839789">Uklopi po Å¡irini</translation>
@@ -119,6 +120,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="1270502636509132238">NaÄin preuzimanja</translation>
<translation id="1281476433249504884">SlagaÄ 1</translation>
<translation id="1285320974508926690">Nikada ne prevodi ovu web lokaciju</translation>
+<translation id="1288548991597756084">Bezbjedno saÄuvajte karticu</translation>
<translation id="1292571435393770077">Ladica 16</translation>
<translation id="1292701964462482250">"Softver na raÄunaru spreÄava Chrome da se sigurno poveže na web lokaciju" (samo na Windows raÄunarima)</translation>
<translation id="1294154142200295408">Varijacije komandne linije</translation>
@@ -223,6 +225,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
&lt;p&gt;Za ispravljanje greške, kliknite na &lt;strong&gt;Poveži se&lt;/strong&gt; na stranici koju pokušavate otvoriti.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Pejzažno uređenje</translation>
<translation id="1513706915089223971">Lista unosa u historiji</translation>
+<translation id="1516097932025103760">Bit će Å¡ifriran, sigurno saÄuvan i CVC se nikada ne pohranjuje.</translation>
<translation id="1517433312004943670">Broj telefona je obavezan</translation>
<translation id="1519264250979466059">Datum verzije</translation>
<translation id="1521159554480556801">Umjetnost rada s vlaknima i tekstilom</translation>
@@ -288,6 +291,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="1662550410081243962">SaÄuvajte i popunite naÄine plaćanja</translation>
<translation id="1663943134801823270">Kartice i adrese su iz Chromea. Njima možete upravljati u <ph name="BEGIN_LINK" />Postavkama<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Stranice na jeziku <ph name="SOURCE_LANGUAGE" /> od sada će se prevoditi na jezik <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Nastavite na plaćanje pomoću kartice <ph name="CARD_DETAIL" /> da iskoristite ponudu</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> na <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Najprije kratka strana</translation>
<translation id="168693727862418163">Vrijednost ovog pravila u odnosu na njegovu šemu nije potvrđena pa će biti zanemareno.</translation>
@@ -306,6 +310,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="1717218214683051432">Senzori pokreta</translation>
<translation id="1717494416764505390">PoÅ¡tansko sanduÄe 3</translation>
<translation id="1718029547804390981">Dokument je prevelik da bi se obilježio</translation>
+<translation id="1720941539803966190">Zatvaranje vodiÄa</translation>
<translation id="1721424275792716183">* Obavezno polje</translation>
<translation id="1727613060316725209">Certifikat je važeći</translation>
<translation id="1727741090716970331">Dodajte važeći broj kartice</translation>
@@ -422,8 +427,8 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="205212645995975601">Roštilj i grilovanje</translation>
<translation id="2053111141626950936">Stranice Äiji jezik je <ph name="LANGUAGE" />, neće se prevoditi.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Kada je ova kontrola ukljuÄena i status aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, ili "kohorti", vaÅ¡a nedavna aktivnost pregledanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a grupa se ažurira svaki dan.}=1{Kada je ova kontrola ukljuÄena i status aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, ili "kohorti", vaÅ¡a nedavna aktivnost pregledanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a grupa se ažurira svaki dan.}one{Kada je ova kontrola ukljuÄena i status aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, ili "kohorti", vaÅ¡a nedavna aktivnost pregledanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a grupa se ažurira svakih {NUM_DAYS} dan.}few{Kada je ova kontrola ukljuÄena i status aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, ili "kohorti", vaÅ¡a nedavna aktivnost pregledanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a grupa se ažurira svaka {NUM_DAYS} dana.}other{Kada je ova kontrola ukljuÄena i status aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, ili "kohorti", vaÅ¡a nedavna aktivnost pregledanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a grupa se ažurira svakih {NUM_DAYS} dana.}}</translation>
-<translation id="2053553514270667976">Poštanski broj</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{Jedan prijedlog}one{# prijedlog}few{# prijedloga}other{# prijedloga}}</translation>
+<translation id="2066915425250589881">zatražiti da se izbriše</translation>
<translation id="2068528718802935086">Bebe i mala djeca</translation>
<translation id="2071156619270205202">Ova kartica ne ispunjava uslove za broj virtuelne kartice.</translation>
<translation id="2071692954027939183">ObavjeÅ¡tenja su automatski blokirana jer ih obiÄno ne dozvoljavate</translation>
@@ -435,7 +440,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="2088086323192747268">Dugme Upravljajte sinhronizacijom, pritisnite Enter da upravljate time koje će se informacije sinhronizirati u postavkama Chromea</translation>
<translation id="2091887806945687916">Zvuk</translation>
<translation id="2094505752054353250">Nepodudaranje domene</translation>
-<translation id="2096368010154057602">Departman</translation>
<translation id="2099652385553570808">Trostruko spajanje na lijevoj strani</translation>
<translation id="2101225219012730419">Verzija:</translation>
<translation id="2102134110707549001">Predloži jaku lozinku…</translation>
@@ -472,7 +476,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="2185836064961771414">AmeriÄki fudbal</translation>
<translation id="2187317261103489799">Otkrij (zadano)</translation>
<translation id="2188375229972301266">Višestruko bušenje na donjoj strani</translation>
-<translation id="2188852899391513400">Lozinka koju ste upravo koristili pronaÄ‘ena je prilikom naruÅ¡avanja podataka. Da osigura vaÅ¡e raÄune, Googleov Upravitelj lozinki preporuÄuje da odmah promijenite lozinku i provjerite saÄuvane lozinke.</translation>
<translation id="219906046732893612">Uređenje doma</translation>
<translation id="2202020181578195191">Unesite važeću godinu isteka</translation>
<translation id="22081806969704220">Ladica 3</translation>
@@ -483,6 +486,8 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="2215727959747642672">Uređivanje fajla</translation>
<translation id="2215963164070968490">Psi</translation>
<translation id="2218879909401188352">NapadaÄi koji se trenutno nalaze na web lokaciji <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogu instalirati opasne aplikacije koje izazivaju oÅ¡tećenja vaÅ¡eg ureÄ‘aja, prouzrokuju skrivene troÅ¡kove na vaÅ¡em raÄunu za mobilnu mrežu ili kradu vaÅ¡e liÄne podatke. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Ponovo pokreni vodiÄ</translation>
+<translation id="2219735899272417925">Potrebno je vratiti uređaj na zadane postavke</translation>
<translation id="2224337661447660594">Nema internetske veze</translation>
<translation id="2230458221926704099">Riješite svoju grešku prilikom povezivanja pomoću <ph name="BEGIN_LINK" />aplikacije za dijagnostiku<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Pošalji sad</translation>
@@ -580,11 +585,13 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="2512101340618156538">Nije dozvoljeno (zadano)</translation>
<translation id="2512413427717747692">Dugme za postavljanje Chromea kao zadanog preglednika, pritisnite Enter da postavite Chrome kao zadani preglednik sistema u postavkama iOS-a</translation>
<translation id="2515629240566999685">Provjeriti signal u svojoj oblasti</translation>
+<translation id="2515761554693942801">Možete odabrati potvrdu pomoću Touch ID-a na web lokacijama koje koriste pružatelja usluga <ph name="PROVIDER_ORIGIN" />. Ovaj pružalac usluga je možda pohranio informacije o vaÅ¡em naÄinu plaćanja, za koji možete <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Spajanje u donjem desnom uglu</translation>
<translation id="2521736961081452453">Kreiraj obrazac</translation>
-<translation id="2523886232349826891">SaÄuvano iskljuÄivo na ovom ureÄ‘aju</translation>
+<translation id="2523886232349826891">Pohranjuje se samo na ovom uređaju</translation>
<translation id="2524461107774643265">Dodajte više informacija</translation>
<translation id="2529899080962247600">Ovo polje ne smije sadržavati više od sljedećeg broja unosa: <ph name="MAX_ITEMS_LIMIT" />. Svi daljnji unosi će se zanemariti.</translation>
+<translation id="253493526287553278">Pogledajte detalje o promotivnim kôdovima</translation>
<translation id="2535585790302968248">Otvorite novu anonimnu karticu da privatno pregledate</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{i još 1}one{i još #}few{i još #}other{i još #}}</translation>
<translation id="2536110899380797252">Dodaj adresu</translation>
@@ -620,7 +627,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="259821504105826686">Fotografska i digitalna umjetnost</translation>
<translation id="2601150049980261779">RomantiÄni filmovi</translation>
<translation id="2604589665489080024">Pop muzika</translation>
-<translation id="2609632851001447353">Varijacije</translation>
<translation id="2610561535971892504">Kopiranje klikom</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />neće saÄuvati<ph name="END_EMPHASIS" /> sljedeće informacije:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Rođendani i imendani</translation>
<translation id="2677748264148917807">Napusti</translation>
+<translation id="2679714844901977852">SaÄuvajte podatke o kartici i naplati na svoj Google raÄun <ph name="USER_EMAIL" /> za siguran i brži nastavak na plaćanje</translation>
<translation id="2684561033061424857">11 x 12</translation>
<translation id="2687555958734450033">Najpogodniji format</translation>
<translation id="2688969097326701645">Da, nastavi</translation>
@@ -701,7 +708,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="2854764410992194509">Pružaoci internet usluga (ISP)</translation>
<translation id="2856444702002559011">Moguće je da napadaÄi pokuÅ¡avaju ukrasti vaÅ¡e podatke s web lokacije <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (naprimjer, lozinke, poruke ili kreditne kartice). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Ova web lokacija prikazuje nametljive ili obmanjujuće oglase.</translation>
-<translation id="286512204874376891">Virtuelna kartica kamuflira vašu stvarnu karticu radi zaštite od potencijalne prevare. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Prijateljski</translation>
<translation id="28761159517501904">Filmovi</translation>
<translation id="2876489322757410363">NapuÅ¡tate anonimni naÄin rada radi plaćanja putem vanjske aplikacije. Nastaviti?</translation>
@@ -802,7 +808,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">WiFi koji koristite može tražiti da posjetite njegovu stranicu za prijavu.</translation>
<translation id="3169472444629675720">Otkrijte</translation>
-<translation id="3174168572213147020">Ostrvo</translation>
<translation id="3176929007561373547">Provjerite postavke proxyja ili se obratite mrežnom administratoru da
biste provjerili je li proxy poslužitelj u funkciji. Ako mislite da ne
biste trebali upotrebljavati proxy poslužitelj:
@@ -881,9 +886,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="3369192424181595722">Greška sa satom</translation>
<translation id="3369459162151165748">Dijelovi i oprema za vozila</translation>
<translation id="3371064404604898522">Postavi Chrome kao zadani preglednik</translation>
-<translation id="3371076217486966826"><ph name="URL" /> želi:
- • Kreirati 3D mapu vašeg okruženja i pratiti položaj kamere
- • Koristiti vašu kameru</translation>
<translation id="337363190475750230">Poništeno dodjeljivanje</translation>
<translation id="3375754925484257129">Pokrenite sigurnosnu provjeru Chromea</translation>
<translation id="3377144306166885718">Server je koristio zastarjelu verziju TLS-a.</translation>
@@ -900,6 +902,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="3399952811970034796">Adresa za isporuku</translation>
<translation id="3402261774528610252">Veza koriÅ¡tena za uÄitavanje ove web lokacije je koristila verzije TLS 1.0 ili TLS 1.1, koje su zastarjele i u budućnosti će se onemogućiti. Kada se onemoguće, korisnici neće moću uÄitati ovu web lokaciju. Server treba omogućiti verziju TLS 1.2 ili noviju.</translation>
<translation id="3405664148539009465">Prilagodi fontove</translation>
+<translation id="3407789382767355356">prijava treće strane</translation>
<translation id="3409896703495473338">Upravljajte sigurnosnim postavkama</translation>
<translation id="3414952576877147120">VeliÄina:</translation>
<translation id="3417660076059365994">Fajlovi koje otpremite ili priložite se šalju u Google oblak ili trećim stranama na analizu. Naprimjer, mogu se skenirati radi otkrivanja postojanja osjetljivih podataka ili zlonamjernog softvera.</translation>
@@ -932,6 +935,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="3477679029130949506">Repertoar filmova i pozorišnih predstava</translation>
<translation id="3479552764303398839">Ne sada</translation>
<translation id="3484560055331845446">Možete izgubiti pristup svom Google raÄunu. Chrome preporuÄuje da odmah promijenite lozinku. Od vas će se tražiti da se prijavite.</translation>
+<translation id="3484861421501147767">Podsjetnik: saÄuvani promotivni kôd je dostupan</translation>
<translation id="3487845404393360112">Ladica 4</translation>
<translation id="3495081129428749620">Pronađi na stranici
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="3810973564298564668">Upravljaj</translation>
<translation id="3816482573645936981">Vrijednost (zamijenjeno)</translation>
<translation id="382518646247711829">Ako koristite proksi server...</translation>
+<translation id="3826050100957962900">Prijava treće strane</translation>
<translation id="3827112369919217609">Apsolutno</translation>
<translation id="3827666161959873541">PorodiÄni filmovi</translation>
<translation id="3828924085048779000">Polje za pristupni izraz ne može biti prazno.</translation>
@@ -1068,9 +1073,9 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="3858027520442213535">Ažuriraj datum i vrijeme</translation>
<translation id="3858860766373142691">Naziv</translation>
<translation id="3872834068356954457">Nauka</translation>
+<translation id="3875783148670536197">Pokaži mi kako</translation>
<translation id="3881478300875776315">Prikaži manje redova</translation>
<translation id="3884278016824448484">Identifikator uređaja koji je u konfliktu</translation>
-<translation id="3885155851504623709">Parohija</translation>
<translation id="388632593194507180">Otkriveno je praćenje</translation>
<translation id="3886948180919384617">SlagaÄ 3</translation>
<translation id="3890664840433101773">Dodajte adresu e-pošte</translation>
@@ -1100,9 +1105,11 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="3973234410852337861">Host <ph name="HOST_NAME" /> je blokiran</translation>
<translation id="3978338123949022456">NaÄin rada pretraživanja, unesite upit i pritisnite Enter da pretražujete pomoću kljuÄne rijeÄi: <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Ptice</translation>
+<translation id="3985750352229496475">Upravljajte adresama...</translation>
<translation id="3986705137476756801">IskljuÄite Automatske titlove za sada</translation>
<translation id="3987940399970879459">Manje od 1 MB</translation>
<translation id="3990250421422698716">Odvajanje dokumenata</translation>
+<translation id="3992684624889376114">O ovoj stranici</translation>
<translation id="3996311196211510766">Web lokacija <ph name="ORIGIN" /> je zatražila da se izvorno pravilo
primijeni na sve zahtjeve upućenje njoj, ali ovo se pravilo trenutno ne može primijeniti.</translation>
<translation id="4006465311664329701">NaÄini plaćanja, ponude i adrese iz Google Paya</translation>
@@ -1227,6 +1234,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="4305666528087210886">Pristupanje vašem fajlu nije uspjelo</translation>
<translation id="4306529830550717874">SaÄuvati adresu?</translation>
<translation id="4306812610847412719">međumemorija</translation>
+<translation id="4310070645992025887">Pretražite Putovanja</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokiraj (zadano)</translation>
<translation id="4314815835985389558">Upravljanje sinhronizacijom</translation>
@@ -1277,6 +1285,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="4435702339979719576">Razglednica)</translation>
<translation id="443673843213245140">Korištenje proksi servera je onemogućeno ali je određena eksplicitna konfiguracija proksi servera.</translation>
<translation id="4441832193888514600">Zanemareno jer se pravilo može postaviti samo kao korisniÄko pravilo oblaka.</translation>
+<translation id="4442470707340296952">Chromeove kartice</translation>
<translation id="4450893287417543264">Ne prikazuj ponovo</translation>
<translation id="4451135742916150903">Može tražiti da se poveže na HID uređaje</translation>
<translation id="4452328064229197696">Lozinka koju ste upravo koristili pronaÄ‘ena je prilikom naruÅ¡avanja podataka. Da osigura vaÅ¡e raÄune, Googleov Upravitelj lozinki preporuÄuje da provjerite saÄuvane lozinke.</translation>
@@ -1415,6 +1424,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="483241715238664915">UkljuÄi upozorenja</translation>
<translation id="4834250788637067901">NaÄini plaćanja, ponude i adrese iz Google Paya</translation>
<translation id="4838327282952368871">Sanjivo</translation>
+<translation id="4839087176073128681">Plaćajte brže sljedeći put i zaštitite svoju karticu pomoću Googleove sigurnosti koja je vodeća u industriji.</translation>
<translation id="4840250757394056958">Pregledajte historiju Chromea</translation>
<translation id="484462545196658690">Automatski</translation>
<translation id="484671803914931257">Ostvarite popust u trgovini <ph name="MERCHANT_NAME" /> i još u više njih</translation>
@@ -1477,6 +1487,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="5011561501798487822">Otkriveni jezik</translation>
<translation id="5015510746216210676">Naziv mašine:</translation>
<translation id="5017554619425969104">Kopirani tekst</translation>
+<translation id="5017828934289857214">Podsjeti me kasnije</translation>
<translation id="5018422839182700155">Nije moguće otvoriti ovu stranicu</translation>
<translation id="5019198164206649151">Pohrana za sigurnosnu kopiju je u lošem stanju</translation>
<translation id="5020776957610079374">Svjetska muzika</translation>
@@ -1496,6 +1507,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="5051305769747448211">Komedija uživo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Da pošaljete ovaj fajl pomoću Dijeljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na uređaju}one{Da pošaljete ove fajlove pomoću Dijeljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na uređaju}few{Da pošaljete ove fajlove pomoću Dijeljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na uređaju}other{Da pošaljete ove fajlove pomoću Dijeljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na uređaju}}</translation>
<translation id="5056549851600133418">ÄŒlanci za vas</translation>
+<translation id="5060483733937416656">Odabrali ste potvrdu pomoću funkcije Windows Hello na web lokacijama koje koriste pružaoca usluga <ph name="PROVIDER_ORIGIN" />. Ovaj pružalac usluga je možda pohranio informacije o vaÅ¡em naÄinu plaćanja, za koji možete <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Jeste li mislili <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Historija Å¡tampanja</translation>
<translation id="5068234115460527047">Hedž fondovi</translation>
@@ -1509,10 +1521,8 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="5087286274860437796">Potvrda servera trenutno nije važeća.</translation>
<translation id="5087580092889165836">Dodaj karticu</translation>
<translation id="5088142053160410913">Poruka operateru</translation>
-<translation id="5089810972385038852">Država</translation>
<translation id="5093232627742069661">Z-presavijanje</translation>
<translation id="5094747076828555589">Ovaj server ne može dokazati da pripada domeni <ph name="DOMAIN" />; Chromium ne vjeruje njegovoj sigurnosnoj potvrdi. Uzrok tome može biti pogreÅ¡na konfiguracija ili napadaÄ koji je prekinuo vaÅ¡u vezu.</translation>
-<translation id="5095208057601539847">Pokrajina</translation>
<translation id="5097099694988056070">Statistika uređaja kao što je iskorištenost CPU-a/RAM-a</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Web lokacija nije sigurna</translation>
@@ -1550,6 +1560,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="5171045022955879922">Pretražite ili upišite URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Mašina</translation>
+<translation id="5177076414499237632">Saznajte više o izvoru i temi ove stranice</translation>
<translation id="5179510805599951267">Jezik nije <ph name="ORIGINAL_LANGUAGE" />? Prijavite ovu grešku</translation>
<translation id="518639307526414276">Hrana za kućne ljubimce i potrepštine za njegu kućnih ljubimaca</translation>
<translation id="5190835502935405962">Traka oznaka</translation>
@@ -1710,6 +1721,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="5624120631404540903">Upravljajte lozinkama</translation>
<translation id="5629630648637658800">UÄitavanje postavki pravila nije uspjelo</translation>
<translation id="5631439013527180824">Nevažeći token za upravljanje uređajem</translation>
+<translation id="5632485077360054581">Pokaži mi kako</translation>
<translation id="5633066919399395251">NapadaÄi koji su trenutno na web lokaciji <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogu pokuÅ¡ati instalirati opasne programe na vaÅ¡ raÄunar koji kradu ili briÅ¡u informacije (npr, fotografije, lozinke, poruke i podatke s kreditnih kartica). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Blokiran je obmanjujući sadržaj.</translation>
<translation id="5633259641094592098">Kultni i indie filmovi</translation>
@@ -1827,6 +1839,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="5989320800837274978">Nije naveden fiksni proksi server niti URL pac skripte.</translation>
<translation id="5992691462791905444">Inženjersko Z-presavijanje</translation>
<translation id="5995727681868049093">Upravljajte podacima, privatnošću i sigurnošću na Google raÄunu</translation>
+<translation id="5997247540087773573">Lozinka koju ste upravo koristili pronaÄ‘ena je prilikom naruÅ¡avanja podataka. Da osigura vaÅ¡e raÄune, Googleov Upravitelj lozinki preporuÄuje da odmah promijenite lozinku i provjerite saÄuvane lozinke.</translation>
<translation id="6000758707621254961">Broj rezultata <ph name="RESULT_COUNT" /> za "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">KlasiÄno</translation>
<translation id="6008122969617370890">Redoslijed od N do 1</translation>
@@ -1922,7 +1935,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="627746635834430766">Da sljedeći put brže izvrÅ¡ite plaćanje, saÄuvajte adresu kartice i adresu za naplatu na svoj Google raÄun.</translation>
<translation id="6279183038361895380">Pritisnite |<ph name="ACCELERATOR" />| da prikažete kursor</translation>
<translation id="6280223929691119688">Isporuka na ovu adresu nije moguća. Odaberite drugu adresu.</translation>
-<translation id="6282194474023008486">Poštanski broj</translation>
<translation id="6285507000506177184">Dugme za upravljanje preuzimanjima u Chromeu, pritisnite Enter da upravljate fajlovima koje ste preuzeli u Chromeu</translation>
<translation id="6289939620939689042">Boja stranice</translation>
<translation id="6290238015253830360">Predloženi Älanci će se pojaviti ovdje</translation>
@@ -1944,6 +1956,7 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="6337133576188860026">Oslobodit će se manje od <ph name="SIZE" />. Neke web lokacije će se prilikom sljedeće posjete možda sporije uÄitavati.</translation>
<translation id="6337534724793800597">Filtriraj pravila po nazivu</translation>
<translation id="6340739886198108203">Pravila administratora ne preporuÄuju pravljenje snimaka ekrana niti snimanje kada je vidljiv povjerljiv sadržaj:</translation>
+<translation id="6348220984832452017">Aktivne varijacije</translation>
<translation id="6349101878882523185">Instalirajte <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ništa}=1{1 lozinka (za <ph name="DOMAIN_LIST" />, sinhronizirano)}=2{2 lozinke (za <ph name="DOMAIN_LIST" />, sinhronizirano)}one{# lozinka (za <ph name="DOMAIN_LIST" />, sinhronizirano)}few{# lozinke (za <ph name="DOMAIN_LIST" />, sinhronizirano)}other{# lozinki (za <ph name="DOMAIN_LIST" />, sinhronizirano)}}</translation>
<translation id="6355392890578844978">Ovim preglednikom ne upravlja kompanija ili neka druga organizacija. Aktivnostima na ovom uređaju se može upravljati van Chromiuma. <ph name="BEGIN_LINK" />Saznajte više<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="643051589346665201">Promijeni Google lozinku</translation>
<translation id="6433490469411711332">Uređivanje podataka za kontakt</translation>
<translation id="6433595998831338502">Host raÄunar <ph name="HOST_NAME" /> je odbio povezivanje.</translation>
-<translation id="6438025220577812695">Samostalno ću promijeniti</translation>
<translation id="6440503408713884761">Zanemareno</translation>
<translation id="6443406338865242315">Koje ekstenzije i dodatke ste instalirali</translation>
<translation id="6446163441502663861">Kahu (koverta)</translation>
@@ -2105,9 +2117,9 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Prevedi</translation>
<translation id="6833752742582340615">SaÄuvajte podatke o kartici i naplati na Google raÄun za siguran i brži nastavak na plaćanje</translation>
-<translation id="6839929833149231406">PodruÄje</translation>
<translation id="6846340164947227603">Koristi broj virtuelne kartice...</translation>
<translation id="6852204201400771460">Ponovo uÄitati aplikaciju?</translation>
+<translation id="6857776781123259569">Upravljajte lozinkama…</translation>
<translation id="686485648936420384">PotroÅ¡aÄki izvori</translation>
<translation id="6865412394715372076">Ovu karticu sada ne možemo potvrditi</translation>
<translation id="6869334554832814367">LiÄni zajmovi</translation>
@@ -2156,7 +2168,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="6965978654500191972">Uređaj</translation>
<translation id="696703987787944103">Perceptivno</translation>
<translation id="6968269510885595029">Koristite sigurnosni kljuÄ</translation>
-<translation id="6970216967273061347">Distrikt</translation>
<translation id="6971439137020188025">Brzo kreirate novu Google prezentaciju u Prezentacijama</translation>
<translation id="6972629891077993081">HID uređaji</translation>
<translation id="6973656660372572881">Određeni su fiksni proxy poslužitelji i URL .pac skripte.</translation>
@@ -2195,7 +2206,6 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<translation id="7081308185095828845">Ova funkcija nije dostupna na vašem uređaju</translation>
<translation id="7083258188081898530">Ladica 9</translation>
<translation id="7086090958708083563">Otpremanje je zatražio korisnik</translation>
-<translation id="7087282848513945231">Okrug</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite Tab, a zatim Enter da upravljate odobrenjima i podacima pohranjenim na svim web lokacijama u postavkama Chromea</translation>
<translation id="7096937462164235847">Potvrđivanje identiteta ove web lokacije nije uspjelo.</translation>
<translation id="7101893872976785596">Horor filmovi</translation>
@@ -2214,10 +2224,10 @@ To će u suprotnom biti blokirano prema vašim postavkama privatnosti. Ovo će o
<ph name="LIST_ITEM" />informacije unesene u obrasce<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Dostava na ovu adresu nije moguća. Odaberite drugu adresu.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Vaš administrator je zabranio kopiranje ovih podataka.</translation>
<translation id="7135130955892390533">Prikaži status</translation>
<translation id="7138472120740807366">NaÄin dostave</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Primarna ladica</translation>
<translation id="714064300541049402">Pomak slike X sa strane 2</translation>
<translation id="7152423860607593928">Broj-14 (koverta)</translation>
@@ -2434,6 +2444,7 @@ Dodatni detalji:
<translation id="7669271284792375604">NapadaÄi na ovoj web lokaciji mogu vas pokuÅ¡ati prevarom navesti da instalirate programe koji naruÅ¡avaju vaÅ¡e iskustvo pregledanja (naprimjer, promjena vaÅ¡e poÄetne stranice ili prikazivanje dodatnih oglasa na web lokacijama koje posjetite).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Radnje poduzete s podacima koji su oznaÄeni kao povjerljivi (1 radnja od prijave). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}one{Radnje poduzete s podacima koji su oznaÄeni kao povjerljivi (# radnja od prijave). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}few{Radnje poduzete s podacima koji su oznaÄeni kao povjerljivi (# radnje od prijave). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}other{Radnje poduzete s podacima koji su oznaÄeni kao povjerljivi (# radnji od prijave). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">PoÅ¡tansko sanduÄe 6</translation>
+<translation id="7675325315208090829">Upravljajte naÄinima plaćanja...</translation>
<translation id="7676643023259824263">Pretraživanje teksta iz međumemorije, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Pregledajte historiju pregledanja i upravljajte njome u postavkama Chromea</translation>
<translation id="7679947978757153706">Bejzbol</translation>
@@ -2476,7 +2487,6 @@ Dodatni detalji:
<translation id="7766518757692125295">Kontura</translation>
<translation id="7770259615151589601">Zadano duga</translation>
<translation id="7773005668374414287">Isti redoslijed s odštampanom stranom prema gore</translation>
-<translation id="777702478322588152">Prefektura</translation>
<translation id="7791011319128895129">Neobjavljeno</translation>
<translation id="7791196057686275387">Povezivanje</translation>
<translation id="7791543448312431591">Dodaj</translation>
@@ -2567,12 +2577,12 @@ Dodatni detalji:
<translation id="8055534648776115597">StruÄno i kontinuirano obrazovanje</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" nije ispravno konfiguriran. Problem se obiÄno rijeÅ¡i deinstalacijom softvera "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Proizvodnja hrane</translation>
-<translation id="8066955247577885446">Nažalost, nešto nije uredu.</translation>
<translation id="8067872629359326442">Upravo ste unijeli lozinku na obmanjujućoj web lokaciji. Chromium vam može pomoći. Da promijenite lozinku i obavijestite Google da vam je raÄun možda ugrožen, kliknite ZaÅ¡titi raÄun.</translation>
<translation id="8070439594494267500">Ikona aplikacije</translation>
<translation id="8074253406171541171">10x13 (koverta)</translation>
<translation id="8075736640322370409">Brzo kreirajte novu Google tabelu</translation>
<translation id="8075898834294118863">Upravljajte postavkama web lokacije</translation>
+<translation id="8076492880354921740">Kartice</translation>
<translation id="8078141288243656252">Ne mogu se praviti bilješke kada je dokument rotiran</translation>
<translation id="8079031581361219619">Ponovo uÄitati web lokaciju?</translation>
<translation id="8081087320434522107">Limuzine</translation>
@@ -2592,7 +2602,7 @@ Dodatni detalji:
<translation id="810875025413331850">Nije pronađen nijedan uređaj u blizini.</translation>
<translation id="8116925261070264013">IskljuÄen zvuk</translation>
<translation id="8118489163946903409">NaÄin plaćanja</translation>
-<translation id="8123046743443732598">Stolni raÄunari</translation>
+<translation id="8123046743443732598">RaÄunari</translation>
<translation id="8124639700796374294">Dugme za prilagođavanje Chromea, pritisnite Enter da prilagodite izgled preglednika</translation>
<translation id="8126056688005753476">AvanturistiÄka putovanja</translation>
<translation id="8127301229239896662">"<ph name="SOFTWARE_NAME" />" nije pravilno instaliran na vaÅ¡ raÄunar ili mrežu. Zamolite IT administratora da rijeÅ¡i taj problem.</translation>
@@ -2695,6 +2705,7 @@ Dodatni detalji:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Postavke</translation>
+<translation id="8428634594422941299">Shvaćam</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite Tab, a zatim Enter da upravljate preferencama za kolaÄiće u postavkama Chromea</translation>
<translation id="8433057134996913067">Time ćete se odjaviti s većine web-lokacija.</translation>
<translation id="8434840396568290395">Kućni ljubimci</translation>
@@ -2792,6 +2803,7 @@ Dodatni detalji:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> je vaš kôd za <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">OznaÄite ovu karticu</translation>
<translation id="8751426954251315517">Pokušajte ponovo kasnije</translation>
+<translation id="8757526089434340176">Dostupna je ponuda Google Paya</translation>
<translation id="8758885506338294482">TakmiÄarsko igranje video igara</translation>
<translation id="8759274551635299824">Ova kartica je istekla</translation>
<translation id="87601671197631245">Ova web lokacija koristi zastarjelu konfiguraciju sigurnosti, zbog Äega može doći do otkrivanja vaÅ¡ih informacija (naprimjer lozinki, poruka ili kreditnih kartica) kada se Å¡alju na ovu web lokaciju.</translation>
@@ -2799,6 +2811,7 @@ Dodatni detalji:
<translation id="8763927697961133303">USB uređaj</translation>
<translation id="8763986294015493060">Zatvorite sve anonimne prozore koji su trenutno otvoreni</translation>
<translation id="8766943070169463815">Tabela za autentifikaciju akreditiva sigurnog plaćanja je otvorena</translation>
+<translation id="8767765348545497220">Zatvaranje oblaÄića za pomoć</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocikli</translation>
<translation id="8790007591277257123">Poništite b&amp;risanje</translation>
@@ -2811,6 +2824,7 @@ Dodatni detalji:
<translation id="8806285662264631610">Proizvodi za kupanje i tijelo</translation>
<translation id="8807160976559152894">Skraćivanje nakon svake stranice</translation>
<translation id="8808828119384186784">Postavke za Chrome</translation>
+<translation id="8813277370772331957">Podsjeti me kasnije</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Pritisnite Tab, a zatim Enter da ažurirate Chrome iz postavki Chromea</translation>
<translation id="8820817407110198400">Oznake</translation>
<translation id="882338992931677877">RuÄni utor</translation>
@@ -2990,6 +3004,7 @@ Dodatni detalji:
<translation id="988159990683914416">Verzija za programere</translation>
<translation id="989988560359834682">Uređivanje adrese</translation>
<translation id="991413375315957741">Senzori kretanja ili svjetla</translation>
+<translation id="992110854164447044">Virtuelna kartica sakriva stvarnu karticu radi zaštite od potencijalne prevare. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">RužiÄasta</translation>
<translation id="992432478773561401">Softver "<ph name="SOFTWARE_NAME" />" nije ispravno instaliran na vaÅ¡em raÄunaru ili mreži:
diff --git a/chromium/components/strings/components_strings_ca.xtb b/chromium/components/strings/components_strings_ca.xtb
index 25f563299d9..d05bb2034d0 100644
--- a/chromium/components/strings/components_strings_ca.xtb
+++ b/chromium/components/strings/components_strings_ca.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Subvencions, beques i ajudes financeres</translation>
<translation id="1048785276086539861">Quan editis anotacions, aquest document tornarà a la visualització d'una sola pàgina</translation>
<translation id="1050038467049342496">Tanca altres aplicacions</translation>
+<translation id="1053959602163383901">Has decidit verificar amb un dispositiu d'autenticació els llocs web que utilitzen <ph name="PROVIDER_ORIGIN" />. És possible que aquest proveïdor hagi emmagatzemat informació sobre la teva forma de pagament, que pots <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Desfés l'addició</translation>
<translation id="1056663316309890343">Programari de fotografia</translation>
<translation id="1056898198331236512">Advertiment</translation>
@@ -119,6 +120,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="1270502636509132238">Mètode de recollida</translation>
<translation id="1281476433249504884">Apiladora 1</translation>
<translation id="1285320974508926690">No tradueixis mai aquest lloc</translation>
+<translation id="1288548991597756084">Desa la targeta de manera segura</translation>
<translation id="1292571435393770077">Safata 16</translation>
<translation id="1292701964462482250">"L'ordinador conté programari que impedeix que Chrome es connecti de manera segura al web" (només en ordinadors Windows)</translation>
<translation id="1294154142200295408">Variacions de la línia d'ordres</translation>
@@ -223,6 +225,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
&lt;p&gt;Per solucionar l'error, feu clic a &lt;strong&gt;Connecta&lt;/strong&gt; a la pàgina que proveu d'obrir.</translation>
<translation id="1507780850870535225">Paisatgisme</translation>
<translation id="1513706915089223971">Llista d'entrades de l'historial</translation>
+<translation id="1516097932025103760">Les dades estaran encriptades, es desaran de manera segura i el CVC mai no s'emmagatzemarà.</translation>
<translation id="1517433312004943670">El número de telèfon és obligatori</translation>
<translation id="1519264250979466059">Data de creació</translation>
<translation id="1521159554480556801">Fibres i arts tèxtils</translation>
@@ -288,6 +291,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="1662550410081243962">Desa i emplena les formes de pagament</translation>
<translation id="1663943134801823270">Les targetes i les adreces s'obtenen de Chrome. Pots gestionar-les des de <ph name="BEGIN_LINK" />Configuració<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">A partir d'ara, les pàgines en <ph name="SOURCE_LANGUAGE" /> es traduiran a <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Paga amb <ph name="CARD_DETAIL" /> per utilitzar l'oferta</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> a <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">La vora curta primer</translation>
<translation id="168693727862418163">No s'ha pogut validar el valor d'aquesta política comparant-lo amb l'esquema i, per tant, s'ignorarà.</translation>
@@ -306,6 +310,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="1717218214683051432">Sensors de moviment</translation>
<translation id="1717494416764505390">Bústia de correu 3</translation>
<translation id="1718029547804390981">No es poden afegir anotacions a aquest fitxer perquè és massa gran</translation>
+<translation id="1720941539803966190">Tanca el tutorial</translation>
<translation id="1721424275792716183">* El camp és obligatori</translation>
<translation id="1727613060316725209">El certificat és vàlid</translation>
<translation id="1727741090716970331">Afegeix un número de targeta vàlid</translation>
@@ -418,8 +423,8 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="205212645995975601">Barbacoa i cuina a la brasa</translation>
<translation id="2053111141626950936">Les pàgines en <ph name="LANGUAGE" /> no es traduiran.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Quan aquest control està activat i la prova està activa, Chrome determina en quin grup gran de persones, o cohort, hi ha una activitat de navegació recent més semblant a la teva. Els anunciants poden seleccionar anuncis per al grup i la teva activitat de navegació es manté privada al teu dispositiu. El teu grup s'actualitza cada dia.}=1{Quan aquest control està activat i la prova està activa, Chrome determina en quin grup gran de persones, o cohort, hi ha una activitat de navegació recent més semblant a la teva. Els anunciants poden seleccionar anuncis per al grup i la teva activitat de navegació es manté privada al teu dispositiu. El teu grup s'actualitza cada dia.}other{Quan aquest control està activat i la prova està activa, Chrome determina en quin grup gran de persones, o cohort, hi ha una activitat de navegació recent més semblant a la teva. Els anunciants poden seleccionar anuncis per al grup i la teva activitat de navegació es manté privada al teu dispositiu. El teu grup s'actualitza cada {NUM_DAYS} dies}}</translation>
-<translation id="2053553514270667976">Codi postal</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggeriment}other{# suggeriments}}</translation>
+<translation id="2066915425250589881">sol·licitar que se suprimeixi</translation>
<translation id="2068528718802935086">Nadons i nens petits</translation>
<translation id="2071156619270205202">Aquesta targeta no és apta per a un número de targeta virtual.</translation>
<translation id="2071692954027939183">Les notificacions s'han bloquejat automàticament perquè normalment no les permets</translation>
@@ -431,7 +436,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="2088086323192747268">Botó Gestiona la sincronització: prem Retorn per gestionar la informació que sincronitzes a la configuració de Chrome</translation>
<translation id="2091887806945687916">So</translation>
<translation id="2094505752054353250">Els dominis no coincideixen.</translation>
-<translation id="2096368010154057602">Departament</translation>
<translation id="2099652385553570808">Grapat triple a l'esquerra</translation>
<translation id="2101225219012730419">Versió:</translation>
<translation id="2102134110707549001">Suggereix una contrasenya segura…</translation>
@@ -468,7 +472,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="2185836064961771414">Futbol americà</translation>
<translation id="2187317261103489799">Detecta (opció predeterminada)</translation>
<translation id="2188375229972301266">Encunyació múltiple a la part inferior</translation>
-<translation id="2188852899391513400">La contrasenya que acabes d'utilitzar s'ha trobat en una violació de les dades. Per protegir els teus comptes, el gestor de contrasenyes de Google recomana que la canvïis ara i que després comprovis les contrasenyes desades.</translation>
<translation id="219906046732893612">Millores de la llar</translation>
<translation id="2202020181578195191">Introdueix un any de caducitat vàlid</translation>
<translation id="22081806969704220">Safata 3</translation>
@@ -479,6 +482,8 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="2215727959747642672">Edició de fitxers</translation>
<translation id="2215963164070968490">Gossos</translation>
<translation id="2218879909401188352">És possible que els atacants que es troben a <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> instal·lin aplicacions perilloses que malmetin el teu dispositiu, afegeixin càrrecs amagats a la teva factura telefònica o et robin informació personal. <ph name="BEGIN_LEARN_MORE_LINK" />Més informació<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Reinicia el tutorial</translation>
+<translation id="2219735899272417925">Cal restablir el dispositiu</translation>
<translation id="2224337661447660594">Sense connexió a Internet</translation>
<translation id="2230458221926704099">Repareu la connexió amb l'<ph name="BEGIN_LINK" />aplicació de diagnòstic<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Envia ara</translation>
@@ -576,11 +581,13 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="2512101340618156538">No permès (opció predeterminada)</translation>
<translation id="2512413427717747692">Botó per establir Chrome com a navegador predeterminat: prem Retorn per establir Chrome com a navegador predeterminat del sistema a la configuració d'iOS</translation>
<translation id="2515629240566999685">Comprova el senyal a la teva zona.</translation>
+<translation id="2515761554693942801">Has decidit verificar amb Touch ID els llocs web que utilitzen <ph name="PROVIDER_ORIGIN" />. És possible que aquest proveïdor hagi emmagatzemat informació sobre la teva forma de pagament, que pots <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Grapat a la part inferior dreta</translation>
<translation id="2521736961081452453">Crea un formulari</translation>
<translation id="2523886232349826891">Només es desarà en aquest dispositiu</translation>
<translation id="2524461107774643265">Afegeix més informació</translation>
<translation id="2529899080962247600">Aquest camp no pot tenir més de <ph name="MAX_ITEMS_LIMIT" /> entrades. Totes les altres entrades s'ignoraran.</translation>
+<translation id="253493526287553278">Mostra els detalls del codi promocional</translation>
<translation id="2535585790302968248">Obre una pestanya d'incògnit nova per navegar en privat</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{i 1 més}other{i # més}}</translation>
<translation id="2536110899380797252">Afegeix una adreça</translation>
@@ -616,7 +623,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="259821504105826686">Arts digitals i fotogràfiques</translation>
<translation id="2601150049980261779">Pel·lícules romàntiques</translation>
<translation id="2604589665489080024">Música pop</translation>
-<translation id="2609632851001447353">Variacions</translation>
<translation id="2610561535971892504">Fes clic per copiar</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />no desarà<ph name="END_EMPHASIS" /> la informació següent:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Aniversaris i efemèrides</translation>
<translation id="2677748264148917807">Surt</translation>
+<translation id="2679714844901977852">Desa la informació de facturació i de la targeta al teu Compte de Google <ph name="USER_EMAIL" /> per tramitar compres de manera segura i més ràpida</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Mida òptima</translation>
<translation id="2688969097326701645">Sí, continua</translation>
@@ -697,7 +704,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="2854764410992194509">Proveïdors d'Internet</translation>
<translation id="2856444702002559011">Pot ser que els atacants provin de robar la teva informació del lloc web <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (per exemple, contrasenyes, missatges o targetes de crèdit). <ph name="BEGIN_LEARN_MORE_LINK" />Més informació<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Aquest lloc web mostra anuncis intrusius o enganyosos.</translation>
-<translation id="286512204874376891">Una targeta virtual oculta la teva targeta real per contribuir a protegir-te de possibles fraus. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Amistosa</translation>
<translation id="28761159517501904">Pel·lícules</translation>
<translation id="2876489322757410363">Per pagar amb una aplicació externa, sortiràs del mode d'incògnit. Vols continuar?</translation>
@@ -798,7 +804,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="3158539265159265653">Disc</translation>
<translation id="3162559335345991374">És possible que la xarxa Wi-Fi que esteu fent servir requereixi que visiteu la seva pàgina d'inici de sessió.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Illa</translation>
<translation id="3176929007561373547">Comproveu la configuració del servidor intermediari o contacteu amb l'administrador de la xarxa per
assegurar-vos que el servidor intermediari funcioni correctament. Si creieu que no és necessari
utilitzar un servidor intermediari:
@@ -877,9 +882,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="3369192424181595722">Error de rellotge</translation>
<translation id="3369459162151165748">Peces i accessoris de vehicles</translation>
<translation id="3371064404604898522">Estableix Chrome com a navegador predeterminat</translation>
-<translation id="3371076217486966826"><ph name="URL" /> vol:
- • Crear un mapa 3D del teu entorn i fer un seguiment de la posició de la càmera.
- • Utilitzar la teva càmera.</translation>
<translation id="337363190475750230">No administrat</translation>
<translation id="3375754925484257129">Executa la comprovació de seguretat de Chrome</translation>
<translation id="3377144306166885718">El servidor ha utilitzat una versió obsoleta de TLS.</translation>
@@ -896,6 +898,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="3399952811970034796">Adreça d'entrega</translation>
<translation id="3402261774528610252">La connexió utilitzada per carregar aquest lloc web ha fet servir TLS 1.0 o 1.1, estàndards que estan obsolets i es desactivaran en el futur. Un cop desactivats, els usuaris no podran carregar aquest lloc web. El servidor ha d'activar TLS 1.2 o una versió posterior.</translation>
<translation id="3405664148539009465">Personalitza els tipus de lletra</translation>
+<translation id="3407789382767355356">inici de sessió de tercers</translation>
<translation id="3409896703495473338">Gestiona la configuració de seguretat</translation>
<translation id="3414952576877147120">Mida:</translation>
<translation id="3417660076059365994">Els fitxers que penges o adjuntes s'envien a Google Cloud o a tercers perquè s'analitzin. Per exemple, pot ser que s'analitzin per detectar-hi dades sensibles o programari maliciós.</translation>
@@ -928,6 +931,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="3477679029130949506">Cartelleres i horaris de cinemes</translation>
<translation id="3479552764303398839">Ara no</translation>
<translation id="3484560055331845446">Podries perdre l'accés al Compte de Google. Chrome et recomana que canviïs la contrasenya ara. Se't demanarà que iniciïs la sessió.</translation>
+<translation id="3484861421501147767">Recordatori: tens disponible un codi promocional desat</translation>
<translation id="3487845404393360112">Safata 4</translation>
<translation id="3495081129428749620">Cerca a la pàgina
<ph name="PAGE_TITLE" /></translation>
@@ -1052,6 +1056,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="3810973564298564668">Gestiona</translation>
<translation id="3816482573645936981">Valor (substituït)</translation>
<translation id="382518646247711829">Si feu servir un servidor intermediari...</translation>
+<translation id="3826050100957962900">Inici de sessió de tercers</translation>
<translation id="3827112369919217609">Absolut</translation>
<translation id="3827666161959873541">Pel·lícules familiars</translation>
<translation id="3828924085048779000">Les frases de contrasenya no poder estar buides.</translation>
@@ -1064,9 +1069,9 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="3858027520442213535">Actualitza la data i l'hora</translation>
<translation id="3858860766373142691">Nom</translation>
<translation id="3872834068356954457">Ciència</translation>
+<translation id="3875783148670536197">Mostra'm com</translation>
<translation id="3881478300875776315">Mostra menys línies</translation>
<translation id="3884278016824448484">L'identificador del dispositiu ja s'està utilitzant</translation>
-<translation id="3885155851504623709">Districte</translation>
<translation id="388632593194507180">S'ha detectat supervisió</translation>
<translation id="3886948180919384617">Apiladora 3</translation>
<translation id="3890664840433101773">Afegeix un correu electrònic</translation>
@@ -1096,9 +1101,11 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="3973234410852337861"><ph name="HOST_NAME" /> està bloquejat</translation>
<translation id="3978338123949022456">Mode de cerca: escriu una consulta i prem Retorn per cercar a <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Aus</translation>
+<translation id="3985750352229496475">Gestiona les adreces...</translation>
<translation id="3986705137476756801">Desactiva Subtítols instantanis per ara</translation>
<translation id="3987940399970879459">Menys d'1 MB</translation>
<translation id="3990250421422698716">Jog offset</translation>
+<translation id="3992684624889376114">Sobre aquesta pàgina</translation>
<translation id="3996311196211510766">El lloc web <ph name="ORIGIN" /> ha sol·licitat que s'apliqui una política d'origen a totes les sol·licituds que rebi, però en aquest moment aquesta política no es pot aplicar.</translation>
<translation id="4006465311664329701">Formes de pagament, ofertes i adreces que fan servir Google Pay</translation>
<translation id="4009243425692662128">El contingut de les pàgines que imprimeixes s'envia a Google Cloud o a tercers perquè l'analitzin. Per exemple, pot ser que s'analitzi per detectar-hi dades sensibles.</translation>
@@ -1218,6 +1225,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="4305666528087210886">No s'ha pogut accedir al fitxer</translation>
<translation id="4306529830550717874">Vols desar l'adreça?</translation>
<translation id="4306812610847412719">porta-retalls</translation>
+<translation id="4310070645992025887">Cerca els teus recorreguts</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloqueja (opció predeterminada)</translation>
<translation id="4314815835985389558">Gestiona la sincronització</translation>
@@ -1268,6 +1276,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="4435702339979719576">Postal)</translation>
<translation id="443673843213245140">L'ús d'un servidor intermediari no està activat, però s'ha especificat una configuració explícita d'un servidor intermediari.</translation>
<translation id="4441832193888514600">S'ha ignorat perquè la política només es pot establir com a política d'usuari basada en el núvol.</translation>
+<translation id="4442470707340296952">Pestanyes de Chrome</translation>
<translation id="4450893287417543264">No ho tornis a mostrar</translation>
<translation id="4451135742916150903">Pot demanar permís per connectar-se a dispositius HID</translation>
<translation id="4452328064229197696">La contrasenya que acabes d'utilitzar s'ha trobat en una violació de les dades. Per protegir els teus comptes, el gestor de contrasenyes de Google recomana que comprovis les contrasenyes desades.</translation>
@@ -1406,6 +1415,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="483241715238664915">Activa els advertiments</translation>
<translation id="4834250788637067901">Formes de pagament, ofertes i adreces que fan servir Google Pay</translation>
<translation id="4838327282952368871">Somiadora</translation>
+<translation id="4839087176073128681">Paga més ràpidament la propera vegada i protegeix la teva targeta amb la seguretat líder del sector de Google.</translation>
<translation id="4840250757394056958">Mostra l'historial de Chrome</translation>
<translation id="484462545196658690">Automàtic</translation>
<translation id="484671803914931257">Obtén un descompte a <ph name="MERCHANT_NAME" /> i més</translation>
@@ -1468,6 +1478,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="5011561501798487822">Idioma detectat</translation>
<translation id="5015510746216210676">Nom de l'ordinador:</translation>
<translation id="5017554619425969104">Text que has copiat</translation>
+<translation id="5017828934289857214">Recorda-m'ho més tard</translation>
<translation id="5018422839182700155">No es pot obrir aquesta pàgina</translation>
<translation id="5019198164206649151">L'emmagatzematge de la còpia de seguretat està en mal estat</translation>
<translation id="5020776957610079374">Música del món</translation>
@@ -1487,6 +1498,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="5051305769747448211">Comèdia en directe</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Per enviar aquest fitxer amb Compartició Nearby, allibera espai (<ph name="DISK_SPACE_SIZE" />) al dispositiu}other{Per enviar aquests fitxers amb Compartició Nearby, allibera espai (<ph name="DISK_SPACE_SIZE" />) al dispositiu}}</translation>
<translation id="5056549851600133418">Articles que et poden interessar</translation>
+<translation id="5060483733937416656">Has decidit verificar amb Windows Hello els llocs web que utilitzen <ph name="PROVIDER_ORIGIN" />. És possible que aquest proveïdor hagi emmagatzemat informació sobre la teva forma de pagament, que pots <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Volies dir <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Historial d'impressions</translation>
<translation id="5068234115460527047">Fons d'inversió lliure</translation>
@@ -1500,10 +1512,8 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="5087286274860437796">En aquest moment el certificat del servidor no és vàlid.</translation>
<translation id="5087580092889165836">Afegeix una targeta</translation>
<translation id="5088142053160410913">Missatge a l'operador</translation>
-<translation id="5089810972385038852">Estat</translation>
<translation id="5093232627742069661">Plegat en Z</translation>
<translation id="5094747076828555589">Aquest servidor no ha pogut comprovar que sigui <ph name="DOMAIN" /> perquè Chromium considera que el seu certificat de seguretat no és de confiança. Això pot ser a causa d'una configuració incorrecta o d'un atacant que intercepta la vostra connexió.</translation>
-<translation id="5095208057601539847">Província</translation>
<translation id="5097099694988056070">Estadístiques del dispositiu, com ara l'ús de CPU o RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">El lloc web no és segur</translation>
@@ -1541,6 +1551,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="5171045022955879922">Cerqueu o escriviu l'URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Automàtica</translation>
+<translation id="5177076414499237632">Informació sobre la font i el tema d'aquesta pàgina</translation>
<translation id="5179510805599951267">No està escrita en <ph name="ORIGINAL_LANGUAGE" />? Informa d'aquest error</translation>
<translation id="518639307526414276">Productes d'alimentació i de cura per a animals de companyia</translation>
<translation id="5190835502935405962">Barra d'adreces d'interès</translation>
@@ -1701,6 +1712,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="5624120631404540903">Gestiona les contrasenyes</translation>
<translation id="5629630648637658800">No s'ha pogut carregar la configuració de la política</translation>
<translation id="5631439013527180824">Testimoni de gestió del dispositiu no vàlid</translation>
+<translation id="5632485077360054581">Mostra'm com</translation>
<translation id="5633066919399395251">És possible que els atacants del lloc web <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> provin d'instal·lar programes perillosos a l'ordinador per robar o suprimir la teva informació (per exemple, les fotos, les contrasenyes, els missatges i les targetes de crèdit). <ph name="BEGIN_LEARN_MORE_LINK" />Més informació<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">S'ha bloquejat el contingut enganyós.</translation>
<translation id="5633259641094592098">Pel·lícules de culte i independents</translation>
@@ -1818,6 +1830,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="5989320800837274978">No s'especifiquen servidors intermediaris ni URL de script .pac.</translation>
<translation id="5992691462791905444">Plegat en Z per a enginyeria</translation>
<translation id="5995727681868049093">Gestiona la teva informació, privadesa i seguretat al Compte de Google</translation>
+<translation id="5997247540087773573">La contrasenya que acabes d'utilitzar s'ha trobat en una violació de les dades. Per protegir els teus comptes, el gestor de contrasenyes de Google recomana que la canviïs ara i que comprovis les contrasenyes desades.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultats per a "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Clàssic</translation>
<translation id="6008122969617370890">Ordre de N a 1</translation>
@@ -1913,7 +1926,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="627746635834430766">Perquè la propera vegada puguis pagar més ràpidament, desa la targeta i l'adreça de facturació al compte de Google.</translation>
<translation id="6279183038361895380">Premeu |<ph name="ACCELERATOR" />| per veure el cursor</translation>
<translation id="6280223929691119688">No es pot entregar a aquesta adreça. Selecciona'n una altra.</translation>
-<translation id="6282194474023008486">Codi postal</translation>
<translation id="6285507000506177184">Botó Gestiona les baixades a Chrome: prem Retorn per gestionar els fitxers que has baixat a Chrome</translation>
<translation id="6289939620939689042">Color de la pàgina</translation>
<translation id="6290238015253830360">Els articles suggerits es mostren aquí</translation>
@@ -1935,6 +1947,7 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="6337133576188860026">Allibera menys de <ph name="SIZE" />. És possible que alguns llocs web es carreguin més a poc a poc la propera vegada que els visitis.</translation>
<translation id="6337534724793800597">Filtra les polítiques pel nom</translation>
<translation id="6340739886198108203">La política de l'administrador no recomana fer captures de pantalla ni gravacions quan hi hagi contingut confidencial visible:</translation>
+<translation id="6348220984832452017">Variacions actives</translation>
<translation id="6349101878882523185">Instal·la <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Cap}=1{1 contrasenya per a <ph name="DOMAIN_LIST" /> (sincronitzats)}=2{2 contrasenyes per a <ph name="DOMAIN_LIST" /> (sincronitzats)}other{# contrasenyes per a <ph name="DOMAIN_LIST" /> (sincronitzats)}}</translation>
<translation id="6355392890578844978">Cap empresa ni cap altra organització no gestiona aquest navegador. És possible que l'activitat d'aquest dispositiu es gestioni fora de Chromium. <ph name="BEGIN_LINK" />Més informació<ph name="END_LINK" /></translation>
@@ -1966,7 +1979,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="643051589346665201">Canvia la contrasenya de Google</translation>
<translation id="6433490469411711332">Edita la informació de contacte</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> no ens ha permès establir la connexió.</translation>
-<translation id="6438025220577812695">La canviaré jo</translation>
<translation id="6440503408713884761">Ignorada</translation>
<translation id="6443406338865242315">Quines extensions i connectors has instal·lat</translation>
<translation id="6446163441502663861">Kahu (sobre)</translation>
@@ -2096,9 +2108,9 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="6828866289116430505">Genètica</translation>
<translation id="6831043979455480757">Tradueix</translation>
<translation id="6833752742582340615">Desa la informació de facturació i de la targeta al teu Compte de Google per tramitar compres de manera segura i més ràpida</translation>
-<translation id="6839929833149231406">Àrea</translation>
<translation id="6846340164947227603">Fes servir un número de targeta virtual...</translation>
<translation id="6852204201400771460">Vols tornar a carregar l'aplicació?</translation>
+<translation id="6857776781123259569">Gestiona les contrasenyes...</translation>
<translation id="686485648936420384">Recursos per a consumidors</translation>
<translation id="6865412394715372076">En aquests moments no es pot verificar la targeta</translation>
<translation id="6869334554832814367">Préstecs personals</translation>
@@ -2147,7 +2159,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="6965978654500191972">Dispositiu</translation>
<translation id="696703987787944103">Perceptiu</translation>
<translation id="6968269510885595029">Fes servir la clau de seguretat</translation>
-<translation id="6970216967273061347">Districte</translation>
<translation id="6971439137020188025">Crea una presentació ràpidament a Presentacions de Google</translation>
<translation id="6972629891077993081">Dispositius d'interfície humana</translation>
<translation id="6973656660372572881">S'especifiquen tant els servidors intermediaris fixos com un URL de script .pac.</translation>
@@ -2186,7 +2197,6 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<translation id="7081308185095828845">Aquesta funció no està disponible al teu dispositiu</translation>
<translation id="7083258188081898530">Safata 9</translation>
<translation id="7086090958708083563">Pujada sol·licitada per l'usuari</translation>
-<translation id="7087282848513945231">Comtat</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, prem Tab i després Retorn per gestionar els permisos i les dades emmagatzemades als llocs web des de la configuració de Chrome</translation>
<translation id="7096937462164235847">La identitat d'aquest lloc web no s'ha verificat.</translation>
<translation id="7101893872976785596">Pel·lícules de terror</translation>
@@ -2205,10 +2215,10 @@ En cas contrari, la configuració de privadesa el bloquejarà. Això permetrà q
<ph name="LIST_ITEM" />Informació introduïda en formularis<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">No es pot enviar a aquesta adreça. Selecciona'n una altra.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">L'administrador ha prohibit copiar aquestes dades.</translation>
<translation id="7135130955892390533">Mostra l'estat</translation>
<translation id="7138472120740807366">Mètode d'entrega</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Safata principal</translation>
<translation id="714064300541049402">Desplaçament a l'eix X del costat 2 de la imatge</translation>
<translation id="7152423860607593928">Number-14 (sobre)</translation>
@@ -2425,6 +2435,7 @@ Detalls addicionals:
<translation id="7669271284792375604">És possible que els atacants d'aquest lloc intentin enganyar-te perquè instal·lis programes que perjudiquen la teva navegació (per exemple, et poden canviar la pàgina d'inici o mostrar anuncis addicionals als llocs que visites).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Accions dutes a terme amb dades marcades com a confidencials (1 acció des de l'inici de sessió). <ph name="BEGIN_LINK" />Més informació<ph name="END_LINK" />}other{Accions dutes a terme amb dades marcades com a confidencials (# accions des de l'inici de sessió). <ph name="BEGIN_LINK" />Més informació<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Bústia de correu 6</translation>
+<translation id="7675325315208090829">Gestiona les formes de pagament...</translation>
<translation id="7676643023259824263">Cerca el text (<ph name="TEXT" />) del porta-retalls</translation>
<translation id="7679367271685653708">Consulta i gestiona l'historial de navegació a la configuració de Chrome</translation>
<translation id="7679947978757153706">Beisbol</translation>
@@ -2467,7 +2478,6 @@ Detalls addicionals:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Ordre idèntic de cara amunt</translation>
-<translation id="777702478322588152">Prefectura</translation>
<translation id="7791011319128895129">No publicada</translation>
<translation id="7791196057686275387">Embalat</translation>
<translation id="7791543448312431591">Afegeix</translation>
@@ -2558,12 +2568,12 @@ Detalls addicionals:
<translation id="8055534648776115597">Formació professional i continuada</translation>
<translation id="8057711352706143257"><ph name="SOFTWARE_NAME" /> no s'ha configurat correctament. Normalment el problema se soluciona desinstal·lant <ph name="SOFTWARE_NAME" />. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Producció alimentària</translation>
-<translation id="8066955247577885446">S'ha produït un error.</translation>
<translation id="8067872629359326442">Acabes d'introduir la contrasenya en un lloc web enganyós. Chromium pot ajudar-te. Per canviar la contrasenya i notificar a Google que el compte pot estar en perill, fes clic a Protegeix el compte.</translation>
<translation id="8070439594494267500">Icona de l'aplicació</translation>
<translation id="8074253406171541171">10x13 (sobre)</translation>
<translation id="8075736640322370409">Crea un full de càlcul de Google ràpidament</translation>
<translation id="8075898834294118863">Gestiona la configuració del lloc web</translation>
+<translation id="8076492880354921740">Pestanyes</translation>
<translation id="8078141288243656252">No es poden fer anotacions en un document girat</translation>
<translation id="8079031581361219619">Vols tornar a carregar el lloc web?</translation>
<translation id="8081087320434522107">Berlines</translation>
@@ -2686,6 +2696,7 @@ Detalls addicionals:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Configuració</translation>
+<translation id="8428634594422941299">D'acord</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, prem Tab i després Retorn per gestionar les preferències de galetes a la configuració de Chrome</translation>
<translation id="8433057134996913067">Amb aquesta acció es tancarà la sessió a la majoria de llocs web.</translation>
<translation id="8434840396568290395">Animals de companyia</translation>
@@ -2783,6 +2794,7 @@ Detalls addicionals:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> és el teu codi per a <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Afegeix aquesta pestanya a les adreces d'interès</translation>
<translation id="8751426954251315517">Torna-ho a provar la propera vegada</translation>
+<translation id="8757526089434340176">Hi ha una oferta de Google Pay disponible</translation>
<translation id="8758885506338294482">Videojocs competitius</translation>
<translation id="8759274551635299824">Aquesta targeta ha caducat</translation>
<translation id="87601671197631245">Aquest lloc web utilitza una configuració de seguretat obsoleta i, per tant, la informació que hi enviïs podria quedar exposada (per exemple, les contrasenyes, els missatges o les targetes de crèdit).</translation>
@@ -2790,6 +2802,7 @@ Detalls addicionals:
<translation id="8763927697961133303">Dispositiu USB</translation>
<translation id="8763986294015493060">Tanca totes les finestres d'incògnit que estan obertes en aquests moments</translation>
<translation id="8766943070169463815">El full d'autenticació de credencials de pagament segur està obert</translation>
+<translation id="8767765348545497220">Tanca el quadre d'ajuda</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocicletes</translation>
<translation id="8790007591277257123">&amp;Refés la supressió</translation>
@@ -2802,6 +2815,7 @@ Detalls addicionals:
<translation id="8806285662264631610">Productes corporals i per al bany</translation>
<translation id="8807160976559152894">Retalla després de cada pàgina</translation>
<translation id="8808828119384186784">Configuració de Chrome</translation>
+<translation id="8813277370772331957">Recorda-m'ho més tard</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />; prem Tab i després Retorn per actualitzar Chrome des de la configuració de Chrome</translation>
<translation id="8820817407110198400">Adreces d'interès</translation>
<translation id="882338992931677877">Alimentador manual</translation>
@@ -2981,6 +2995,7 @@ Detalls addicionals:
<translation id="988159990683914416">Muntatge del desenvolupador</translation>
<translation id="989988560359834682">Edita l'adreça</translation>
<translation id="991413375315957741">sensors de moviment o de llum</translation>
+<translation id="992110854164447044">Una targeta virtual amaga la teva targeta real per contribuir a protegir-te de possibles fraus. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401"><ph name="SOFTWARE_NAME" /> no s'ha instal·lat correctament a l'ordinador o a la xarxa:
diff --git a/chromium/components/strings/components_strings_cs.xtb b/chromium/components/strings/components_strings_cs.xtb
index 894b6806e5a..f2265e7de63 100644
--- a/chromium/components/strings/components_strings_cs.xtb
+++ b/chromium/components/strings/components_strings_cs.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Granty, stipendia a finanÄní pomoc</translation>
<translation id="1048785276086539861">Když poznámky upravíte, tento dokument se vrátí do zobrazení jedné stránky</translation>
<translation id="1050038467049342496">Zavřete ostatní aplikace</translation>
+<translation id="1053959602163383901">Rozhodli jste se provést ověření pomocí ověřovací aplikace na webech, které používají <ph name="PROVIDER_ORIGIN" />. Tento poskytovatel může mít uložené údaje o vaší platební metodě – můžete <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Vrátit přidání zpět</translation>
<translation id="1056663316309890343">Fotografický software</translation>
<translation id="1056898198331236512">Upozornění</translation>
@@ -119,6 +120,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="1270502636509132238">Způsob vyzvednutí</translation>
<translation id="1281476433249504884">StohovaÄ 1</translation>
<translation id="1285320974508926690">Tento web nikdy nepřekládat</translation>
+<translation id="1288548991597756084">BezpeÄnÄ› si uložte kartu</translation>
<translation id="1292571435393770077">Přihrádka 16</translation>
<translation id="1292701964462482250">Software na poÄítaÄi Chromu brání v bezpeÄném pÅ™ipojení k webu (pouze poÄítaÄe se systémem Windows)</translation>
<translation id="1294154142200295408">Varianty pro příkazový řádek</translation>
@@ -223,6 +225,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
&lt;p&gt;Chybu odstraníte tím, že na stránce, kterou se snažíte otevřít, kliknete na &lt;strong&gt;Připojení&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Krajinářství</translation>
<translation id="1513706915089223971">Seznam položek v historii</translation>
+<translation id="1516097932025103760">Karta bude zaÅ¡ifrována a bezpeÄnÄ› uložena (kód CVC uložen nebude).</translation>
<translation id="1517433312004943670">Je vyžadováno telefonní Äíslo</translation>
<translation id="1519264250979466059">Datum sestavení</translation>
<translation id="1521159554480556801">Umění využívající textil a vlákna</translation>
@@ -288,6 +291,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="1662550410081243962">Ukládat a vyplňovat platební metody</translation>
<translation id="1663943134801823270">Karty a adresy pocházejí z Chromu. Můžete je spravovat v <ph name="BEGIN_LINK" />Nastavení<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Stránky v jazyce <ph name="SOURCE_LANGUAGE" /> se od teÄ budou pÅ™ekládat do jazyka <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Pokud nabídku chcete využít, zaplaťte kartou <ph name="CARD_DETAIL" /></translation>
<translation id="1674504678466460478">Z: <ph name="SOURCE_LANGUAGE" /> do: <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Krátkou hranou napřed</translation>
<translation id="168693727862418163">Hodnotu zásady se nepodařilo ověřit proti schématu a bude ignorována.</translation>
@@ -306,6 +310,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="1717218214683051432">Pohybová Äidla</translation>
<translation id="1717494416764505390">Schránka 3</translation>
<translation id="1718029547804390981">Dokument je na pÅ™idání oznaÄení a poznámek příliÅ¡ velký</translation>
+<translation id="1720941539803966190">Zavřít výukový program</translation>
<translation id="1721424275792716183">* Pole je povinné</translation>
<translation id="1727613060316725209">Certifikát je platný</translation>
<translation id="1727741090716970331">PÅ™idání platného Äísla karty</translation>
@@ -418,8 +423,8 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="205212645995975601">Barbecue a grilování</translation>
<translation id="2053111141626950936">Stránky v jazyce <ph name="LANGUAGE" /> se nebudou překládat.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Když je tento ovládací prvek zapnutý a stav je aktivní, Chrome urÄuje, které skupinÄ› lidí (neboli kohortÄ›) se vaÅ¡e nedávná aktivita prohlížení nejvíce podobá. Inzerenti mohou vybírat reklamy podle skupin a vaÅ¡e historie prohlížení zůstává soukromá ve vaÅ¡em zařízení. VaÅ¡e skupina se každý den aktualizuje.}=1{Když je tento ovládací prvek zapnutý a stav je aktivní, Chrome urÄuje, které skupinÄ› lidí (neboli kohortÄ›) se vaÅ¡e nedávná aktivita prohlížení nejvíce podobá. Inzerenti mohou vybírat reklamy podle skupin a vaÅ¡e historie prohlížení zůstává soukromá ve vaÅ¡em zařízení. VaÅ¡e skupina se každý den aktualizuje.}few{Když je tento ovládací prvek zapnutý a stav je aktivní, Chrome urÄuje, které skupinÄ› lidí (neboli kohortÄ›) se vaÅ¡e nedávná aktivita prohlížení nejvíce podobá. Inzerenti mohou vybírat reklamy podle skupin a vaÅ¡e historie prohlížení zůstává soukromá ve vaÅ¡em zařízení. VaÅ¡e skupina se každé {NUM_DAYS} dny aktualizuje.}many{Když je tento ovládací prvek zapnutý a stav je aktivní, Chrome urÄuje, které skupinÄ› lidí (neboli kohortÄ›) se vaÅ¡e nedávná aktivita prohlížení nejvíce podobá. Inzerenti mohou vybírat reklamy podle skupin a vaÅ¡e historie prohlížení zůstává soukromá ve vaÅ¡em zařízení. VaÅ¡e skupina se každých {NUM_DAYS} dne aktualizuje.}other{Když je tento ovládací prvek zapnutý a stav je aktivní, Chrome urÄuje, které skupinÄ› lidí (neboli kohortÄ›) se vaÅ¡e nedávná aktivita prohlížení nejvíce podobá. Inzerenti mohou vybírat reklamy podle skupin a vaÅ¡e historie prohlížení zůstává soukromá ve vaÅ¡em zařízení. VaÅ¡e skupina se každých {NUM_DAYS} dní aktualizuje.}}</translation>
-<translation id="2053553514270667976">PSČ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 návrh}few{# návrhy}many{# návrhu}other{# návrhů}}</translation>
+<translation id="2066915425250589881">požádat o jejich smazání</translation>
<translation id="2068528718802935086">Děti a batolata</translation>
<translation id="2071156619270205202">Tato karta není pro Äíslo virtuální karty vhodná.</translation>
<translation id="2071692954027939183">Oznámení byla automaticky zablokována, protože je obvykle nepovolujete</translation>
@@ -431,7 +436,6 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="2088086323192747268">TlaÄítko Správa synchronizace, stisknutím klávesy Enter můžete v nastavení Chromu spravovat, jaké informace synchronizujete</translation>
<translation id="2091887806945687916">Zvuk</translation>
<translation id="2094505752054353250">Neshoda domén</translation>
-<translation id="2096368010154057602">Department</translation>
<translation id="2099652385553570808">Tři sponky vlevo</translation>
<translation id="2101225219012730419">Verze:</translation>
<translation id="2102134110707549001">Navrhnout silné heslo…</translation>
@@ -468,7 +472,6 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="2185836064961771414">Americký fotbal</translation>
<translation id="2187317261103489799">Rozpoznat (výchozí)</translation>
<translation id="2188375229972301266">Několik děr dole</translation>
-<translation id="2188852899391513400">PrávÄ› použité heslo bylo nalezeno na seznamu hesel uniklých pÅ™i incidentu poruÅ¡ení zabezpeÄení údajů. Kvůli zabezpeÄení vaÅ¡ich úÄtů Správce hesel Google doporuÄuje ihned itoto heslo zmÄ›nit a poté zkontrolovat uložená hesla.</translation>
<translation id="219906046732893612">Vylepšování domácnosti</translation>
<translation id="2202020181578195191">Zadejte platný rok vypršení platnosti</translation>
<translation id="22081806969704220">Přihrádka 3</translation>
@@ -479,6 +482,8 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="2215727959747642672">Úprava souborů</translation>
<translation id="2215963164070968490">Psi</translation>
<translation id="2218879909401188352">ÚtoÄníci, kteří na webu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> aktuálnÄ› působí, by vám do zařízení mohli nainstalovat nebezpeÄné aplikace, které jej poÅ¡kodí, pÅ™idat skryté poplatky na úÄet za mobilní služby nebo odcizit osobní údaje. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Spustit výukový program znovu</translation>
+<translation id="2219735899272417925">Je nutné resetovat zařízení</translation>
<translation id="2224337661447660594">Nejste připojeni k internetu</translation>
<translation id="2230458221926704099">Opravte připojení pomocí <ph name="BEGIN_LINK" />diagnostické aplikace<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Odeslat</translation>
@@ -576,11 +581,13 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="2512101340618156538">Nepovoleno (výchozí)</translation>
<translation id="2512413427717747692">TlaÄítko nastavení Chromu jako výchozího prohlížeÄe: Stisknutím klávesy Enter nastavíte Chrome jako výchozí prohlížeÄ v nastavení iOS</translation>
<translation id="2515629240566999685">Zkontrolovat, zda máte dostateÄnÄ› silný signál</translation>
+<translation id="2515761554693942801">Rozhodli jste se provést ověření pomocí Touch ID na webech, které používají <ph name="PROVIDER_ORIGIN" />. Tento poskytovatel může mít uložené údaje o vaší platební metodě – můžete <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Sponka vpravo dole</translation>
<translation id="2521736961081452453">Vytvořit formulář</translation>
<translation id="2523886232349826891">Uloženo pouze do tohoto zařízení</translation>
<translation id="2524461107774643265">Přidání dalších informací</translation>
<translation id="2529899080962247600">Toto pole nesmí mít více než <ph name="MAX_ITEMS_LIMIT" /> položek. Všechny další položky budou ignorovány.</translation>
+<translation id="253493526287553278">Zobrazit podrobnosti propagaÄního kódu</translation>
<translation id="2535585790302968248">Otevřete novou anonymní kartu a prohlížejte v soukromí</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{a 1 další}few{a # další}many{a # další}other{a # dalších}}</translation>
<translation id="2536110899380797252">Přidat adresu</translation>
@@ -616,7 +623,6 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="259821504105826686">Fotografie a digitální umění</translation>
<translation id="2601150049980261779">Romantické filmy</translation>
<translation id="2604589665489080024">Populární hudba</translation>
-<translation id="2609632851001447353">Varianty</translation>
<translation id="2610561535971892504">Kliknutím zkopírujte</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />nebude ukládat<ph name="END_EMPHASIS" /> následující informace:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Svátky a narozeniny</translation>
<translation id="2677748264148917807">Odejít</translation>
+<translation id="2679714844901977852">Uložte si kartu a fakturaÄní údaje do úÄtu Google <ph name="USER_EMAIL" />, abyste mohli platit bezpeÄnÄ›ji a rychleji</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Přizpůsobit</translation>
<translation id="2688969097326701645">Ano, pokraÄovat</translation>
@@ -697,7 +704,6 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="2854764410992194509">Poskytovatelé internetových služeb</translation>
<translation id="2856444702002559011">ÚtoÄníci se mohou pokusit odcizit vaÅ¡e údaje na webu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (například hesla, zprávy nebo informace o platebních kartách). <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Tento web zobrazuje rušivé nebo zavádějící reklamy.</translation>
-<translation id="286512204874376891">Virtuální karta maskuje vaÅ¡i skuteÄnou kartu a chrání vás pÅ™ed případným podvodem. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Přátelské</translation>
<translation id="28761159517501904">Filmy</translation>
<translation id="2876489322757410363">Chystáte se opustit anonymní režim, abyste mohli zaplatit v externí aplikaci. PokraÄovat?</translation>
@@ -798,7 +804,6 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">Síť Wi-Fi, kterou používáte, může vyžadovat, abyste navštívili její stránku přihlášení.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ostrov</translation>
<translation id="3176929007561373547">Zkontrolujte nastavení proxy serveru nebo se obraťte na správce sítě, aby ověřil, zda proxy server funguje. Pokud se domníváte, že by proxy server neměl být používán: <ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">Zjišťovat, kdy aktivně používáte toto zařízení</translation>
<translation id="3180358318770512945">RodiÄovství</translation>
@@ -874,9 +879,6 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="3369192424181595722">Chyba hodin</translation>
<translation id="3369459162151165748">Náhradní díly a autodoplňky</translation>
<translation id="3371064404604898522">Nastavení Chromu jako výchozího prohlížeÄe</translation>
-<translation id="3371076217486966826">Web <ph name="URL" /> chce:
- • vytvářet 3D mapu vašeho okolí a sledovat polohu kamery,
- • používat fotoaparát.</translation>
<translation id="337363190475750230">Vyřazeno</translation>
<translation id="3375754925484257129">Spustit bezpeÄnostní kontrolu Chromu</translation>
<translation id="3377144306166885718">Server použil zastaralou verzi protokolu TLS.</translation>
@@ -893,6 +895,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="3399952811970034796">Adresa doruÄení</translation>
<translation id="3402261774528610252">PÅ™ipojení použité k naÄtení tohoto webu použilo protokol TLS 1.0 nebo TLS 1.1, který již není podporován a v budoucnu bude deaktivován. Po deaktivaci již uživatelé nebudou moci tento web naÄíst. Na serveru je tÅ™eba aktivovat protokol TLS 1.2 nebo novÄ›jší.</translation>
<translation id="3405664148539009465">Personalizovat písma</translation>
+<translation id="3407789382767355356">přihlašování prostřednictvím třetích stran</translation>
<translation id="3409896703495473338">Spravovat nastavení zabezpeÄení</translation>
<translation id="3414952576877147120">Velikost:</translation>
<translation id="3417660076059365994">Soubory, které nahrajete nebo připojíte, jsou odesílány k analýze do služby Google Cloud nebo třetím stranám. Mohou například projít kontrolou přítomnosti citlivých údajů nebo malwaru.</translation>
@@ -925,6 +928,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="3477679029130949506">Katalogy filmů a programy kin</translation>
<translation id="3479552764303398839">TeÄ ne</translation>
<translation id="3484560055331845446">Mohli byste ztratit přístup k úÄtu Google. Chrome doporuÄuje okamžitÄ› zmÄ›nit heslo. Budete vyzváni k pÅ™ihlášení.</translation>
+<translation id="3484861421501147767">PÅ™ipomenutí: Je k dispozici uložený propagaÄní kód</translation>
<translation id="3487845404393360112">Přihrádka 4</translation>
<translation id="3495081129428749620">Najít na stránce
<ph name="PAGE_TITLE" /></translation>
@@ -1048,6 +1052,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="3810973564298564668">Spravovat</translation>
<translation id="3816482573645936981">Hodnota (nahrazena)</translation>
<translation id="382518646247711829">Pokud používáte proxy server...</translation>
+<translation id="3826050100957962900">Externí přihlašování</translation>
<translation id="3827112369919217609">Absolutní</translation>
<translation id="3827666161959873541">Rodinné filmy</translation>
<translation id="3828924085048779000">Prázdná heslová fráze není povolena.</translation>
@@ -1060,9 +1065,9 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="3858027520442213535">Aktualizovat datum a Äas</translation>
<translation id="3858860766373142691">Název</translation>
<translation id="3872834068356954457">Věda</translation>
+<translation id="3875783148670536197">Ukázat postup</translation>
<translation id="3881478300875776315">Zobrazit méně řádků</translation>
<translation id="3884278016824448484">Konfliktní identifikátor zařízení</translation>
-<translation id="3885155851504623709">Farnost</translation>
<translation id="388632593194507180">Bylo zjištěno sledování</translation>
<translation id="3886948180919384617">StohovaÄ 3</translation>
<translation id="3890664840433101773">Přidat e-mail</translation>
@@ -1092,9 +1097,11 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="3973234410852337861">Web <ph name="HOST_NAME" /> je blokován</translation>
<translation id="3978338123949022456">Režim vyhledávání: Pokud chcete vyhledávat s příponou <ph name="KEYWORD_SUFFIX" />, zadejte dotaz a stiskněte Enter</translation>
<translation id="398470910934384994">Ptáci</translation>
+<translation id="3985750352229496475">Spravovat adresy...</translation>
<translation id="3986705137476756801">Zatím živý přepis vypnout</translation>
<translation id="3987940399970879459">Méně než 1 MB</translation>
<translation id="3990250421422698716">Běžící odsazení</translation>
+<translation id="3992684624889376114">O této stránce</translation>
<translation id="3996311196211510766">Web <ph name="ORIGIN" /> požádal, aby se na všechny na něj odesílané požadavky vztahovaly zásady ohledně původu, ale tyto zásady momentálně nelze použít.</translation>
<translation id="4006465311664329701">Platební metody, nabídky a adresy z Google Pay</translation>
<translation id="4009243425692662128">Obsah tisknutých stránek se odesílá do služby Google Cloud nebo třetím stranám k analýze. Může například projít kontrolou přítomnosti citlivých údajů.</translation>
@@ -1214,6 +1221,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="4305666528087210886">K souboru nelze získat přístup</translation>
<translation id="4306529830550717874">Uložit adresu?</translation>
<translation id="4306812610847412719">schránka</translation>
+<translation id="4310070645992025887">Prohledat cesty</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokovat (výchozí)</translation>
<translation id="4314815835985389558">Správa synchronizace</translation>
@@ -1264,6 +1272,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Využití proxy serveru je zakázáno, je vÅ¡ak urÄena explicitní konfigurace proxy serveru.</translation>
<translation id="4441832193888514600">Ignorováno, protože zásadu lze nastavit pouze jako zásadu cloudového uživatele.</translation>
+<translation id="4442470707340296952">Karty v Chromu</translation>
<translation id="4450893287417543264">Tuto zprávu již nezobrazovat</translation>
<translation id="4451135742916150903">Může žádat o připojení k zařízením HID</translation>
<translation id="4452328064229197696">PrávÄ› použité heslo bylo nalezeno na seznamu hesel uniklých pÅ™i incidentu poruÅ¡ení zabezpeÄení údajů. K zabezpeÄení vaÅ¡ich úÄtů doporuÄuje Správce hesel Google zkontrolovat uložená hesla.</translation>
@@ -1402,6 +1411,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="483241715238664915">Zapnout upozornění</translation>
<translation id="4834250788637067901">Platební metody, nabídky a adresy z Google Pay</translation>
<translation id="4838327282952368871">Zasněné</translation>
+<translation id="4839087176073128681">PlaÅ¥te rychleji a chraňte svou kartu pomocí Å¡piÄkového zabezpeÄení od Googlu.</translation>
<translation id="4840250757394056958">Zobrazit historii Chromu</translation>
<translation id="484462545196658690">Automaticky</translation>
<translation id="484671803914931257">Získejte slevu u obchodníka <ph name="MERCHANT_NAME" /> a dalších</translation>
@@ -1464,6 +1474,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="5011561501798487822">Zjištěný jazyk</translation>
<translation id="5015510746216210676">Název poÄítaÄe:</translation>
<translation id="5017554619425969104">Zkopírovaný text</translation>
+<translation id="5017828934289857214">Připomenout později</translation>
<translation id="5018422839182700155">Tuto stránku nelze otevřít</translation>
<translation id="5019198164206649151">Záložní úložiště je ve špatném stavu</translation>
<translation id="5020776957610079374">Světová hudba</translation>
@@ -1483,6 +1494,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="5051305769747448211">Živé komedie</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Pokud tento soubor chcete odeslat pomocí funkce Sdílení nablízko, uvolněte v zařízení místo (<ph name="DISK_SPACE_SIZE" />)}few{Pokud tyto soubory chcete odeslat pomocí funkce Sdílení nablízko, uvolněte v zařízení místo (<ph name="DISK_SPACE_SIZE" />)}many{Pokud tyto soubory chcete odeslat pomocí funkce Sdílení nablízko, uvolněte v zařízení místo (<ph name="DISK_SPACE_SIZE" />)}other{Pokud tyto soubory chcete odeslat pomocí funkce Sdílení nablízko, uvolněte v zařízení místo (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">Články pro vás</translation>
+<translation id="5060483733937416656">Rozhodli jste se provést ověření pomocí Windows Hello na webech, které používají <ph name="PROVIDER_ORIGIN" />. Tento poskytovatel může mít uložené údaje o vaší platební metodě – můžete <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Měli jste na mysli <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Historie tisku</translation>
<translation id="5068234115460527047">Zajišťovací fondy</translation>
@@ -1496,10 +1508,8 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="5087286274860437796">Certifikát serveru v tuto chvíli není platný.</translation>
<translation id="5087580092889165836">Přidat kartu</translation>
<translation id="5088142053160410913">Zpráva operátorovi</translation>
-<translation id="5089810972385038852">Stát/kraj</translation>
<translation id="5093232627742069661">Přeložení do Z</translation>
<translation id="5094747076828555589">Server nedokázal prokázat, že patří doménÄ› <ph name="DOMAIN" />. Chromium jeho bezpeÄnostnímu certifikátu nedůvěřuje. Může to být způsobeno nesprávnou konfigurací nebo tím, že vaÅ¡e pÅ™ipojení zachytává útoÄník.</translation>
-<translation id="5095208057601539847">Provincie</translation>
<translation id="5097099694988056070">Statistiky zařízení, například využití CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Web není bezpeÄný</translation>
@@ -1537,6 +1547,7 @@ Jinak to vaše nastavení ochrany soukromí bude blokovat. Povolením umožníte
<translation id="5171045022955879922">Vyhledávejte Äi zadejte URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">PoÄítaÄ</translation>
+<translation id="5177076414499237632">Informace o zdroji a tématu této stránky</translation>
<translation id="5179510805599951267">Nejedná se o jazyk <ph name="ORIGINAL_LANGUAGE" />? Nahlaste tuto chybu.</translation>
<translation id="518639307526414276">Krmení a potÅ™eby pro péÄi o domácí zvířata</translation>
<translation id="5190835502935405962">Lišta záložek</translation>
@@ -1697,6 +1708,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="5624120631404540903">Spravovat hesla</translation>
<translation id="5629630648637658800">NaÄítání nastavení zásady se nezdaÅ™ilo</translation>
<translation id="5631439013527180824">Neplatný token správy zařízení</translation>
+<translation id="5632485077360054581">Ukázat postup</translation>
<translation id="5633066919399395251">ÚtoÄníci, kteří se aktuálnÄ› nacházejí na webu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, se mohou pokusit nainstalovat vám do poÄítaÄe nebezpeÄné programy, které mohou ukrást nebo smazat vaÅ¡e informace (například fotky, hesla, zprávy nebo platební karty). <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Byl zablokován klamavý obsah.</translation>
<translation id="5633259641094592098">Kultovní a nezávislé filmy</translation>
@@ -1814,6 +1826,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="5989320800837274978">Nejsou urÄeny pevnÄ› dané servery proxy ani adresa URL skriptu PAC.</translation>
<translation id="5992691462791905444">Technické přeložení do Z</translation>
<translation id="5995727681868049093">Správa údajů, ochrany soukromí a zabezpeÄení v úÄtu Google</translation>
+<translation id="5997247540087773573">PrávÄ› použité heslo bylo nalezeno na seznamu hesel uniklých pÅ™i incidentu poruÅ¡ení zabezpeÄení údajů. Kvůli zabezpeÄení vaÅ¡ich úÄtů Správce hesel Google doporuÄuje ihned toto heslo zmÄ›nit a zkontrolovat uložená hesla.</translation>
<translation id="6000758707621254961">Výsledky pro dotaz <ph name="SEARCH_TEXT" /> (<ph name="RESULT_COUNT" />)</translation>
<translation id="6006484371116297560">Klasické</translation>
<translation id="6008122969617370890">Pořadí N stránek na list</translation>
@@ -1908,7 +1921,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="627746635834430766">Abyste příštÄ› mohli zaplatit rychleji, uložte si kartu a fakturaÄní adresu do úÄtu Google.</translation>
<translation id="6279183038361895380">Kurzor zobrazíte stisknutím klávesy |<ph name="ACCELERATOR" />|</translation>
<translation id="6280223929691119688">DoruÄení na tuto adresu není možné. Vyberte jinou adresu.</translation>
-<translation id="6282194474023008486">PSČ</translation>
<translation id="6285507000506177184">TlaÄítko správy stažených souborů v Chromu. Stisknutím klávesy Enter můžete spravovat soubory, které jste v Chromu stáhli</translation>
<translation id="6289939620939689042">Barva stránky</translation>
<translation id="6290238015253830360">Zde se zobrazí navrhované Älánky</translation>
@@ -1930,6 +1942,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="6337133576188860026">Uvolní ménÄ› než <ph name="SIZE" />. Je možné, že se nÄ›které weby pÅ™i příští návÅ¡tÄ›vÄ› budou naÄítat pomaleji.</translation>
<translation id="6337534724793800597">Filtrovat zásady podle názvu</translation>
<translation id="6340739886198108203">Zásady správce nedoporuÄují poÅ™izování snímků nebo záznamů obrazovky, když je viditelný důvÄ›rný obsah:</translation>
+<translation id="6348220984832452017">Aktivní variace</translation>
<translation id="6349101878882523185">Nainstalovat aplikaci <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Žádné}=1{1 heslo (pro <ph name="DOMAIN_LIST" />, synchronizováno)}=2{2 hesla (pro <ph name="DOMAIN_LIST" />, synchronizováno)}few{# hesla (pro <ph name="DOMAIN_LIST" />, synchronizováno)}many{# hesla (pro <ph name="DOMAIN_LIST" />, synchronizováno)}other{# hesel (pro <ph name="DOMAIN_LIST" />, synchronizováno)}}</translation>
<translation id="6355392890578844978">Tento prohlížeÄ není spravován administrátorem ani jinou organizací. Aktivita na tomto zařízení může být spravována mimo Chromium. <ph name="BEGIN_LINK" />Další informace<ph name="END_LINK" /></translation>
@@ -1961,7 +1974,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="643051589346665201">Změnit heslo Google</translation>
<translation id="6433490469411711332">Upravit kontaktní údaje</translation>
<translation id="6433595998831338502">Web <ph name="HOST_NAME" /> odmítl připojení.</translation>
-<translation id="6438025220577812695">ZmÄ›ním ho ruÄnÄ›</translation>
<translation id="6440503408713884761">Ignorováno</translation>
<translation id="6443406338865242315">Která rozšíření a pluginy máte nainstalované</translation>
<translation id="6446163441502663861">Kahu (obálka)</translation>
@@ -2091,9 +2103,9 @@ Kontaktujte administrátora systému.</translation>
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Přeložit</translation>
<translation id="6833752742582340615">Uložte si kartu a fakturaÄní údaje do úÄtu Google, abyste mohli platit bezpeÄnÄ›ji a rychleji</translation>
-<translation id="6839929833149231406">Oblast</translation>
<translation id="6846340164947227603">Použít Äíslo virtuální karty...</translation>
<translation id="6852204201400771460">NaÄíst aplikaci znovu?</translation>
+<translation id="6857776781123259569">Spravovat hesla…</translation>
<translation id="686485648936420384">Spotřebitelské zdroje</translation>
<translation id="6865412394715372076">Tuto kartu teÄ nelze ověřit</translation>
<translation id="6869334554832814367">Osobní půjÄky</translation>
@@ -2142,7 +2154,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="6965978654500191972">Zařízení</translation>
<translation id="696703987787944103">PercepÄní</translation>
<translation id="6968269510885595029">Použijte bezpeÄnostní klíÄ</translation>
-<translation id="6970216967273061347">Okres</translation>
<translation id="6971439137020188025">Rychle vytvořit novou prezentaci Google v Prezentacích</translation>
<translation id="6972629891077993081">Zařízení HID</translation>
<translation id="6973656660372572881">UrÄeny jsou pevnÄ› dané servery proxy i adresa URL skriptu PAC.</translation>
@@ -2181,7 +2192,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="7081308185095828845">Tato funkce ve vašem zařízení není k dispozici</translation>
<translation id="7083258188081898530">Přihrádka 9</translation>
<translation id="7086090958708083563">Uživatel požádal o nahrání</translation>
-<translation id="7087282848513945231">Okres</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, stisknutím tabulátoru a poté klávesy Enter můžete spravovat oprávnění a uložená data webů v nastavení Chromu</translation>
<translation id="7096937462164235847">Identita tohoto webu není ověřena.</translation>
<translation id="7101893872976785596">Horory</translation>
@@ -2200,10 +2210,10 @@ Kontaktujte administrátora systému.</translation>
<ph name="LIST_ITEM" />údaje zadané do formulářů.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Dodání na tuto adresu není možné. Vyberte jinou adresu.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Váš správce kopírování těchto dat zakázal.</translation>
<translation id="7135130955892390533">Zobrazit stav</translation>
<translation id="7138472120740807366">Způsob doruÄení</translation>
-<translation id="7139724024395191329">Emirát</translation>
<translation id="7139892792842608322">Hlavní zásobník</translation>
<translation id="714064300541049402">Posun obrázku strany 2 na ose X</translation>
<translation id="7152423860607593928">Number-14 (obálka)</translation>
@@ -2419,6 +2429,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="7669271284792375604">ÚtoÄníci na tomto webu by se mohli pokusit pÅ™imÄ›t vás k instalaci programů, které nepříznivÄ› ovlivní procházení webu (například zmÄ›ní vaÅ¡i domovskou stránku nebo na navÅ¡tÄ›vovaných stránkách budou zobrazovat další reklamy).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Akce provedené s daty, která byla oznaÄena jako důvÄ›rná (od pÅ™ihlášení 1 akce). <ph name="BEGIN_LINK" />Další informace<ph name="END_LINK" />}few{Akce provedené s daty, která byla oznaÄena jako důvÄ›rná (od pÅ™ihlášení # akce). <ph name="BEGIN_LINK" />Další informace<ph name="END_LINK" />}many{Akce provedené s daty, která byla oznaÄena jako důvÄ›rná (od pÅ™ihlášení # akce). <ph name="BEGIN_LINK" />Další informace<ph name="END_LINK" />}other{Akce provedené s daty, která byla oznaÄena jako důvÄ›rná (od pÅ™ihlášení # akcí). <ph name="BEGIN_LINK" />Další informace<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Schránka 6</translation>
+<translation id="7675325315208090829">Spravovat platební metody…</translation>
<translation id="7676643023259824263">Vyhledat text ve schránce (<ph name="TEXT" />)</translation>
<translation id="7679367271685653708">Zobrazit a spravovat v nastavení Chromu historii prohlížení</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2461,7 +2472,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Stejné pořadí lícem nahoru</translation>
-<translation id="777702478322588152">Prefektura</translation>
<translation id="7791011319128895129">Nevydáno</translation>
<translation id="7791196057686275387">Balení</translation>
<translation id="7791543448312431591">Přidat</translation>
@@ -2552,12 +2562,12 @@ Kontaktujte administrátora systému.</translation>
<translation id="8055534648776115597">Odborné a celoživotní vzdělávání</translation>
<translation id="8057711352706143257">Software <ph name="SOFTWARE_NAME" /> není nakonfigurován správně. Tento problém lze obvykle vyřešit odinstalováním softwaru <ph name="SOFTWARE_NAME" />. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Potravinářská výroba</translation>
-<translation id="8066955247577885446">Lituji, něco se pokazilo.</translation>
<translation id="8067872629359326442">PrávÄ› jste své heslo zadali na klamavém webu. Chromium vám může pomoci. Chcete-li zmÄ›nit heslo a oznámit Googlu, že váš úÄet může být ohrožen, kliknÄ›te na Ochránit úÄet.</translation>
<translation id="8070439594494267500">Ikona aplikace</translation>
<translation id="8074253406171541171">10x13 (obálka)</translation>
<translation id="8075736640322370409">Rychle vytvořit novou Tabulku Google</translation>
<translation id="8075898834294118863">Spravovat nastavení webů</translation>
+<translation id="8076492880354921740">Karty</translation>
<translation id="8078141288243656252">Když je dokument otoÄený, nelze do nÄ›j pÅ™idávat znaÄky a poznámky</translation>
<translation id="8079031581361219619">NaÄíst web znovu?</translation>
<translation id="8081087320434522107">Sedany</translation>
@@ -2680,6 +2690,7 @@ Další podrobnosti:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Nastavení</translation>
+<translation id="8428634594422941299">Rozumím</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, stisknutím tabulátoru a poté klávesy Enter můžete spravovat nastavení souborů cookie v nastavení Chromu</translation>
<translation id="8433057134996913067">Budete odhlášeni z většiny webů.</translation>
<translation id="8434840396568290395">Domácí zvířata</translation>
@@ -2778,6 +2789,7 @@ Další podrobnosti:
<translation id="8742371904523228557">Váš kód pro <ph name="ORIGIN" /> je <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">Přidat tuto kartu do záložek</translation>
<translation id="8751426954251315517">Zkuste to znovu později</translation>
+<translation id="8757526089434340176">K dispozici je nabídka Google Pay</translation>
<translation id="8758885506338294482">Soutěžní hraní videoher</translation>
<translation id="8759274551635299824">Platnost této karty vypršela</translation>
<translation id="87601671197631245">Tento web používá zastaralou konfiguraci zabezpeÄení, která vaÅ¡e informace odesílané na tento web (například hesla, zprávy nebo kreditní karty) vystavuje riziku odhalení.</translation>
@@ -2785,6 +2797,7 @@ Další podrobnosti:
<translation id="8763927697961133303">Zařízení USB</translation>
<translation id="8763986294015493060">Zavřít všechna aktuálně otevřená anonymní okna</translation>
<translation id="8766943070169463815">List ověření identifikaÄních údajů pro zabezpeÄené platby je otevÅ™ený</translation>
+<translation id="8767765348545497220">Zavřít bublinu nápovědy</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorky</translation>
<translation id="8790007591277257123">&amp;Opakovat smazání</translation>
@@ -2797,6 +2810,7 @@ Další podrobnosti:
<translation id="8806285662264631610">Koupelnové přípravky a péÄe o tÄ›lo</translation>
<translation id="8807160976559152894">Oříznutí za každou stránkou</translation>
<translation id="8808828119384186784">Nastavení Chromu</translation>
+<translation id="8813277370772331957">Připomenout později</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, stisknutím klávesy Tab a poté Enter aktualizujete Chrome v nastavení Chromu</translation>
<translation id="8820817407110198400">Záložky</translation>
<translation id="882338992931677877">RuÄní slot</translation>
@@ -2976,6 +2990,7 @@ Další podrobnosti:
<translation id="988159990683914416">Vývojářské sestavení</translation>
<translation id="989988560359834682">Upravit adresu</translation>
<translation id="991413375315957741">senzory pohybu nebo světla</translation>
+<translation id="992110854164447044">Virtuální karta skryje vaÅ¡i skuteÄnou kartu a chrání vás pÅ™ed případným podvodem. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Růžová</translation>
<translation id="992432478773561401">Software <ph name="SOFTWARE_NAME" /> na poÄítaÄi nebo v síti nebyl nainstalován správnÄ›:
diff --git a/chromium/components/strings/components_strings_cy.xtb b/chromium/components/strings/components_strings_cy.xtb
index 1039c096410..d5a4779559d 100644
--- a/chromium/components/strings/components_strings_cy.xtb
+++ b/chromium/components/strings/components_strings_cy.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Grantiau, ysgoloriaethau a chymorth ariannol</translation>
<translation id="1048785276086539861">Pan fyddwch yn golygu anodiadau, bydd y ddogfen hon yn dychwelyd i'r wedd un dudalen</translation>
<translation id="1050038467049342496">Caewch apiau eraill</translation>
+<translation id="1053959602163383901">Gwnaethoch ddewis dilysu gyda dyfais ddilysu ar wefannau sy'n eu defnyddio <ph name="PROVIDER_ORIGIN" />. Mae'n bosib bod y darparwr hwn wedi storio gwybodaeth am eich dull talu, y gallwch <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Dadwneud Ychwanegu</translation>
<translation id="1056663316309890343">Meddalwedd lluniau</translation>
<translation id="1056898198331236512">Rhybudd</translation>
@@ -119,6 +120,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="1270502636509132238">Dull Casglu</translation>
<translation id="1281476433249504884">Pentyrrwr 1</translation>
<translation id="1285320974508926690">Peidio byth â chyfieithu'r wefan hon</translation>
+<translation id="1288548991597756084">Cadw cerdyn yn ddiogel</translation>
<translation id="1292571435393770077">Hambwrdd 16</translation>
<translation id="1292701964462482250">"Mae meddalwedd ar eich cyfrifiadur yn atal Chrome rhag cysylltu'n ddiogel â'r we" (cyfrifiaduron Windows yn unig)</translation>
<translation id="1294154142200295408">Amrywiadau llinell orchymyn</translation>
@@ -223,6 +225,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
&lt;p&gt;Er mwyn datrys y gwall, cliciwch &lt;strong&gt;Cysylltu&lt;/strong&gt; ar y dudalen rydych yn ceisio ei hagor.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Dylunio tirwedd</translation>
<translation id="1513706915089223971">Rhestr o gofnodion hanes</translation>
+<translation id="1516097932025103760">Bydd yn cael ei amgryptio, ei gadw'n ddiogel ac nid yw'r CVC byth yn cael ei storio.</translation>
<translation id="1517433312004943670">Angen rhif ffôn</translation>
<translation id="1519264250979466059">Dyddiad y datblygiad</translation>
<translation id="1521159554480556801">Celfyddydau ffibr a thecstilau</translation>
@@ -288,6 +291,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="1662550410081243962">Cadw a llenwi dulliau talu</translation>
<translation id="1663943134801823270">Daw cardiau a chyfeiriadau o Chrome. Gallwch eu rheoli yn y <ph name="BEGIN_LINK" />Gosodiadau<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Bydd tudalennau yn <ph name="SOURCE_LANGUAGE" /> yn cael eu cyfieithu i <ph name="TARGET_LANGUAGE" /> o hyn ymlaen.</translation>
+<translation id="1673886523110456987">Talwch gyda <ph name="CARD_DETAIL" /> i ddefnyddio'r cynnig</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> i <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Ymyl fer yn gyntaf</translation>
<translation id="168693727862418163">Gwnaeth y gwerth polisi hwn fethu â dilysu yn erbyn ei sgema a bydd yn cael ei anwybyddu.</translation>
@@ -306,6 +310,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="1717218214683051432">Synwyryddion symudiad</translation>
<translation id="1717494416764505390">Blwch negeseuon 3</translation>
<translation id="1718029547804390981">Mae'r ddogfen yn rhy fawr i'w chael ei hanodi</translation>
+<translation id="1720941539803966190">Cau'r tiwtorial</translation>
<translation id="1721424275792716183">* Mae'r maes yn ofynnol</translation>
<translation id="1727613060316725209">Mae'r dystysgrif yn ddilys</translation>
<translation id="1727741090716970331">Ychwanegu Rhif Cerdyn Dilys</translation>
@@ -422,8 +427,8 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="205212645995975601">Barbeciw a grilio</translation>
<translation id="2053111141626950936">Ni fydd tudalennau yn <ph name="LANGUAGE" /> yn cael eu cyfieithu.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Pan fydd y rheolaeth hon ymlaen a'r statws yn weithredol, mae Chrome yn penderfynu pa grŵp mawr o bobl, neu "garfan," y mae eich gweithgarwch pori diweddar yn fwyaf tebyg iddynt. Gall hysbysebwyr ddewis hysbysebion ar gyfer y grŵp a chedwir eich gweithgarwch pori yn breifat ar eich dyfais. Mae'ch grŵp yn cael ei ddiweddaru bob dydd.}=1{Pan fydd y rheolaeth hon ymlaen a'r statws yn weithredol, mae Chrome yn penderfynu pa grŵp mawr o bobl, neu "garfan," y mae eich gweithgarwch pori diweddar yn fwyaf tebyg iddynt. Gall hysbysebwyr ddewis hysbysebion ar gyfer y grŵp a chedwir eich gweithgarwch pori yn breifat ar eich dyfais. Mae'ch grŵp yn cael ei ddiweddaru bob dydd.}two{Pan fydd y rheolaeth hon ymlaen a'r statws yn weithredol, mae Chrome yn penderfynu pa grŵp mawr o bobl, neu "garfan," y mae eich gweithgarwch pori diweddar yn fwyaf tebyg iddynt. Gall hysbysebwyr ddewis hysbysebion ar gyfer y grŵp a chedwir eich gweithgarwch pori yn breifat ar eich dyfais. Mae'ch grŵp yn cael ei ddiweddaru bob {NUM_DAYS} ddiwrnod.}few{Pan fydd y rheolaeth hon ymlaen a'r statws yn weithredol, mae Chrome yn penderfynu pa grŵp mawr o bobl, neu "garfan," y mae eich gweithgarwch pori diweddar yn fwyaf tebyg iddynt. Gall hysbysebwyr ddewis hysbysebion ar gyfer y grŵp a chedwir eich gweithgarwch pori yn breifat ar eich dyfais. Mae'ch grŵp yn cael ei ddiweddaru bob {NUM_DAYS} diwrnod.}many{Pan fydd y rheolaeth hon ymlaen a'r statws yn weithredol, mae Chrome yn penderfynu pa grŵp mawr o bobl, neu "garfan," y mae eich gweithgarwch pori diweddar yn fwyaf tebyg iddynt. Gall hysbysebwyr ddewis hysbysebion ar gyfer y grŵp a chedwir eich gweithgarwch pori yn breifat ar eich dyfais. Mae'ch grŵp yn cael ei ddiweddaru bob {NUM_DAYS} diwrnod.}other{Pan fydd y rheolaeth hon ymlaen a'r statws yn weithredol, mae Chrome yn penderfynu pa grŵp mawr o bobl, neu "garfan," y mae eich gweithgarwch pori diweddar yn fwyaf tebyg iddynt. Gall hysbysebwyr ddewis hysbysebion ar gyfer y grŵp a chedwir eich gweithgarwch pori yn breifat ar eich dyfais. Mae'ch grŵp yn cael ei ddiweddaru bob {NUM_DAYS} diwrnod.}}</translation>
-<translation id="2053553514270667976">Cod ZIP</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 awgrym}zero{# awgrym}two{# awgrym}few{# awgrym}many{# awgrym}other{# awgrym}}</translation>
+<translation id="2066915425250589881">ofyn am gael ei dileu</translation>
<translation id="2068528718802935086">Babanod a phlant bach</translation>
<translation id="2071156619270205202">Nid yw'r cerdyn hwn yn gymwys i gael rhif cerdyn rhithwir.</translation>
<translation id="2071692954027939183">Cafodd hysbysiadau eu rhwystro'n awtomatig oherwydd nid ydych fel arfer yn eu caniatáu</translation>
@@ -435,7 +440,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="2088086323192747268">Botwm rheoli cysoni, pwyswch Enter i reoli pa wybodaeth rydych yn ei chysoni yng ngosodiadau Chrome</translation>
<translation id="2091887806945687916">Sain</translation>
<translation id="2094505752054353250">Camgymhariad parth</translation>
-<translation id="2096368010154057602">Adran</translation>
<translation id="2099652385553570808">Tair stapl ar y chwith</translation>
<translation id="2101225219012730419">Fersiwn:</translation>
<translation id="2102134110707549001">Awgrymu Cyfrinair Cryf…</translation>
@@ -472,7 +476,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="2185836064961771414">Pêl-droed Americanaidd</translation>
<translation id="2187317261103489799">Canfod (diofyn)</translation>
<translation id="2188375229972301266">Mwy nag un twll ar y gwaelod</translation>
-<translation id="2188852899391513400">Canfuwyd y cyfrinair rydych newydd ei ddefnyddio mewn achos o dor data. Er mwyn diogelu'ch cyfrifon, mae Rheolwr Cyfrineiriau Google yn argymell eu newid nawr ac yna gwirio'ch cyfrineiriau sydd wedi'u cadw.</translation>
<translation id="219906046732893612">Gwelliannau'r cartref</translation>
<translation id="2202020181578195191">Rhowch flwyddyn darfod ddilys</translation>
<translation id="22081806969704220">Hambwrdd 3</translation>
@@ -483,6 +486,8 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="2215727959747642672">Golygu ffeiliau</translation>
<translation id="2215963164070968490">Cŵn</translation>
<translation id="2218879909401188352">Gallai ymosodwyr ar <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ar hyn o bryd osod apiau peryglus sy'n niweidio'ch dyfais, ychwanegu taliadau cudd at eich bil symudol, neu ddwyn eich gwybodaeth bersonol. <ph name="BEGIN_LEARN_MORE_LINK" />Dysgu rhagor<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Ailddechrau'r tiwtorial</translation>
+<translation id="2219735899272417925">Mae angen oilosod y ddyfais</translation>
<translation id="2224337661447660594">Dim rhyngrwyd</translation>
<translation id="2230458221926704099">Trwsiwch eich cysylltiad gan ddefnyddio'r <ph name="BEGIN_LINK" />ap diagnosteg<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Anfon nawr</translation>
@@ -580,11 +585,13 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="2512101340618156538">Ni chaniateir (diofyn)</translation>
<translation id="2512413427717747692">Gosod Chrome fel botwm porwr diofyn, pwyso Enter i osod Chrome fel porwr diofyn y system yng ngosodiadau iOS</translation>
<translation id="2515629240566999685">Wrthi'n gwirio'r signal yn eich ardal</translation>
+<translation id="2515761554693942801">Gwnaethoch ddewis dilysu gyda Touch ID ar wefannau sy'n defnyddio <ph name="PROVIDER_ORIGIN" />. Mae'n bosib bod y darparwr hwn wedi storio gwybodaeth am eich dull talu, y gallwch <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Stapl ar y dde isaf</translation>
<translation id="2521736961081452453">Creu ffurflen</translation>
<translation id="2523886232349826891">Cedwir ar y ddyfais hon yn unig</translation>
<translation id="2524461107774643265">Ychwanegu Rhagor o Wybodaeth</translation>
<translation id="2529899080962247600">Ni ddylai'r maes hwn gynnwys mwy na <ph name="MAX_ITEMS_LIMIT" /> o gofnodion. Bydd pob cofnod arall yn cael ei anwybyddu.</translation>
+<translation id="253493526287553278">Gweld manylion y cod hyrwyddo</translation>
<translation id="2535585790302968248">Agor tab Anhysbys newydd i bori'n breifat</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{ac 1 arall}zero{a # arall}two{a # arall}few{a # arall}many{a # arall}other{a # arall}}</translation>
<translation id="2536110899380797252">Ychwanegu Cyfeiriad</translation>
@@ -620,7 +627,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="259821504105826686">Celfyddydau ffotograffig a digidol</translation>
<translation id="2601150049980261779">Ffilmiau rhamant</translation>
<translation id="2604589665489080024">Cerddoriaeth pop</translation>
-<translation id="2609632851001447353">Amrywiadau</translation>
<translation id="2610561535971892504">Clicio i gopïo</translation>
<translation id="2617988307566202237"><ph name="BEGIN_EMPHASIS" />Ni fydd<ph name="END_EMPHASIS" /> Chrome yn cadw'r wybodaeth ganlynol:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Penblwyddi a dyddiau enwau</translation>
<translation id="2677748264148917807">Gadael</translation>
+<translation id="2679714844901977852">Cadw eich cerdyn a gwybodaeth bilio i'ch Cyfrif Google <ph name="USER_EMAIL" /> er mwyn talu'n gyflymach ac yn ddiogel</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Ffit orau</translation>
<translation id="2688969097326701645">Iawn, parhau</translation>
@@ -701,7 +708,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="2854764410992194509">Darparwyr gwasanaethau rhyngrwyd (ISP)</translation>
<translation id="2856444702002559011">Mae'n bosib bod ymosodwyr yn ceisio dwyn eich gwybodaeth o <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (er enghraifft, cyfrineiriau, negeseuon, neu gardiau credyd). <ph name="BEGIN_LEARN_MORE_LINK" />Dysgu rhagor<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Mae'r wefan hon yn dangos hysbysebion ymwthiol neu gamarweiniol.</translation>
-<translation id="286512204874376891">Mae cerdyn rhithwir yn cuddio eich cerdyn go iawn i helpu eich amddiffyn rhag twyll posib. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Cyfeillgar</translation>
<translation id="28761159517501904">Ffilmiau</translation>
<translation id="2876489322757410363">Wrthi'n gadael y modd Anhysbys i dalu drwy ap allanol. Parhau?</translation>
@@ -802,7 +808,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="3158539265159265653">Disc</translation>
<translation id="3162559335345991374">Mae'n bosib y bydd y Wi-Fi rydych yn ei ddefnyddio yn gofyn i chi fynd i'w dudalen fewngofnodi.</translation>
<translation id="3169472444629675720">Darganfod</translation>
-<translation id="3174168572213147020">Ynys</translation>
<translation id="3176929007561373547">Gwiriwch eich gosodiadau dirprwyol neu cysylltwch â'ch gweinyddwr rhwydwaith i
sicrhau bod y dirprwy weinydd yn gweithio. Os nad ydych yn credu y dylech
fod yn defnyddio dirprwy weinydd:
@@ -881,9 +886,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="3369192424181595722">Gwall cloc</translation>
<translation id="3369459162151165748">Rhannau ac ategolion cerbydau</translation>
<translation id="3371064404604898522">Gosod Chrome fel porwr diofyn</translation>
-<translation id="3371076217486966826">Mae <ph name="URL" /> eisiau:
- • Creu map 3D o'ch amgylchoedd ac olrhain safle'r camera
- • Defnyddio'ch camera</translation>
<translation id="337363190475750230">Wedi'i ddad-ddarparu</translation>
<translation id="3375754925484257129">Rhedeg gwiriad diogelwch Chrome</translation>
<translation id="3377144306166885718">Gwnaeth y gweinydd ddefnyddio fersiwn o TLS sydd wedi darfod.</translation>
@@ -900,6 +902,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="3399952811970034796">Cyfeiriad Anfon</translation>
<translation id="3402261774528610252">Gwnaeth y cysylltiad a ddefnyddiwyd i lwytho'r wefan hon ddefnyddio TLS 1.0 neu TLS 1.1, sy'n hen a byddant yn cael eu diffodd yn y dyfodol. Ar ôl eu diffodd, bydd defnyddwyr yn cael eu rhwystro rhag llwytho'r wefan hon. Dylai'r gweinydd alluogi TLS 1.2 neu'n ddiweddarach</translation>
<translation id="3405664148539009465">Personoleiddio ffontiau</translation>
+<translation id="3407789382767355356">mewngofnodi trydydd parti</translation>
<translation id="3409896703495473338">Rheoli gosodiadau diogelwch</translation>
<translation id="3414952576877147120">Maint:</translation>
<translation id="3417660076059365994">Anfonir ffeiliau rydych yn eu huwchlwytho neu eu hatodi i Google Cloud neu drydydd partïon i'w dadansoddi. Er enghraifft, mae'n bosib y byddant yn cael eu sganio am ddata sensitif neu ddrwgwedd.</translation>
@@ -932,6 +935,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="3477679029130949506">Rhestrau ffilmiau ac amserau sioe theatr</translation>
<translation id="3479552764303398839">Nid nawr</translation>
<translation id="3484560055331845446">Mae'n bosib y byddwch yn colli mynediad at eich Cyfrif Google. Mae Chrome yn argymell newid eich cyfrinair nawr. Gofynnir i chi fewngofnodi.</translation>
+<translation id="3484861421501147767">Nodyn atgoffa: Cod hyrwyddo wedi'i gadw ar gael</translation>
<translation id="3487845404393360112">Hambwrdd 4</translation>
<translation id="3495081129428749620">Chwilio'r Dudalen
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="3810973564298564668">Rheoli</translation>
<translation id="3816482573645936981">Gwerth (wedi'i ddisodli)</translation>
<translation id="382518646247711829">Os ydych yn defnyddio dirprwy weinydd...</translation>
+<translation id="3826050100957962900">Mewngofnodi trydydd parti</translation>
<translation id="3827112369919217609">Absoliwt</translation>
<translation id="3827666161959873541">Ffilmiau i'r teulu</translation>
<translation id="3828924085048779000">Ni chaniateir cyfrinymadrodd gwag.</translation>
@@ -1068,9 +1073,9 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="3858027520442213535">Diweddaru'r dyddiad a'r amser</translation>
<translation id="3858860766373142691">Enw</translation>
<translation id="3872834068356954457">Gwyddoniaeth</translation>
+<translation id="3875783148670536197">Dangoswch i Fi Sut</translation>
<translation id="3881478300875776315">Dangos llai o linellau</translation>
<translation id="3884278016824448484">Dynodwr dyfais anghyson</translation>
-<translation id="3885155851504623709">Plwyf</translation>
<translation id="388632593194507180">Canfuwyd Monitro</translation>
<translation id="3886948180919384617">Pentyrrwr 3</translation>
<translation id="3890664840433101773">Ychwanegwch e-bost</translation>
@@ -1100,9 +1105,11 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="3973234410852337861">Mae <ph name="HOST_NAME" /> wedi'i rwystro</translation>
<translation id="3978338123949022456">Modd chwilio, teipiwch ymholiad a phwyswch Enter i chwilio gyda <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Adar</translation>
+<translation id="3985750352229496475">Rheoli Cyfeiriadau...</translation>
<translation id="3986705137476756801">Diffodd Capsiynau Byw am y tro</translation>
<translation id="3987940399970879459">Llai nag 1 MB</translation>
<translation id="3990250421422698716">Gwrthbwyso loncian</translation>
+<translation id="3992684624889376114">Ynghylch y dudalen hon</translation>
<translation id="3996311196211510766">Mae'r wefan <ph name="ORIGIN" /> wedi gofyn bod polisi tarddiad
yn berthnasol i bob cais iddo, ond ar hyn o bryd ni ellir cymhwyso'r polisi hwn.</translation>
<translation id="4006465311664329701">Dulliau Talu, Cynigion a Chyfeiriadau sy'n defnyddio Google Pay</translation>
@@ -1227,6 +1234,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="4305666528087210886">Ni ellid cyrchu'ch ffeil</translation>
<translation id="4306529830550717874">Cadw'r cyfeiriad?</translation>
<translation id="4306812610847412719">clipfwrdd</translation>
+<translation id="4310070645992025887">Chwilio eich Teithiau</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Rhwystro (diofyn)</translation>
<translation id="4314815835985389558">Rheoli'r cysoni</translation>
@@ -1277,6 +1285,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="4435702339979719576">Cerdyn post)</translation>
<translation id="443673843213245140">Mae'r defnydd o ddirprwy weinydd wedi'i ddiffodd, ond mae ffurfweddiad dirprwyol penodol wedi'i nodi.</translation>
<translation id="4441832193888514600">Anwybyddwyd oherwydd dim ond fel polisi defnyddiwr cwmwl y gellir gosod y polisi.</translation>
+<translation id="4442470707340296952">Tabiau Chrome</translation>
<translation id="4450893287417543264">Peidio â dangos eto</translation>
<translation id="4451135742916150903">Yn gallu gofyn am gysylltu â dyfeisiau HID</translation>
<translation id="4452328064229197696">Canfuwyd y cyfrinair rydych newydd ei ddefnyddio mewn achos o dor data. Er mwyn diogelu'ch cyfrifon, mae Rheolwr Cyfrineiriau Google yn argymell gwirio'ch cyfrineiriau sydd wedi'u cadw.</translation>
@@ -1415,6 +1424,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="483241715238664915">Troi rhybuddion ymlaen</translation>
<translation id="4834250788637067901">Dulliau talu, cynigion a chyfeiriadau sy'n defnyddio Google Pay</translation>
<translation id="4838327282952368871">Breuddwydiol</translation>
+<translation id="4839087176073128681">Gallwch dalu'n gyflymach y tro nesaf a diogelu'ch cerdyn gyda diogelwch sy'n arwain y diwydiant Google.</translation>
<translation id="4840250757394056958">Gweld eich hanes Chrome</translation>
<translation id="484462545196658690">Awtomatig</translation>
<translation id="484671803914931257">Cael gostyngiad ar <ph name="MERCHANT_NAME" /> a rhagor</translation>
@@ -1477,6 +1487,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="5011561501798487822">Iaith a Ganfyddir</translation>
<translation id="5015510746216210676">Enw'r Peiriant:</translation>
<translation id="5017554619425969104">Testun y gwnaethoch ei gopïo</translation>
+<translation id="5017828934289857214">Atgoffa Fi yn Nes Ymlaen</translation>
<translation id="5018422839182700155">Methu ag agor y dudalen hon</translation>
<translation id="5019198164206649151">Mae'r storfa wrth gefn mewn cyflwr gwael</translation>
<translation id="5020776957610079374">Cerddoriaeth y byd</translation>
@@ -1496,6 +1507,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="5051305769747448211">Comedi byw</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{I anfon y ffeil hon drwy ddefnyddio Rhannu Gerllaw, crëwch ragor o le (<ph name="DISK_SPACE_SIZE" />) ar eich dyfais}zero{I anfon y ffeiliau hyn drwy ddefnyddio Rhannu Gerllaw, crëwch ragor o le (<ph name="DISK_SPACE_SIZE" />) ar eich dyfais}two{I anfon y ffeiliau hyn drwy ddefnyddio Rhannu Gerllaw, crëwch ragor o le (<ph name="DISK_SPACE_SIZE" />) ar eich dyfais}few{I anfon y ffeiliau hyn drwy ddefnyddio Rhannu Gerllaw, crëwch ragor o le (<ph name="DISK_SPACE_SIZE" />) ar eich dyfais}many{I anfon y ffeiliau hyn drwy ddefnyddio Rhannu Gerllaw, crëwch ragor o le (<ph name="DISK_SPACE_SIZE" />) ar eich dyfais}other{I anfon y ffeiliau hyn drwy ddefnyddio Rhannu Gerllaw, crëwch ragor o le (<ph name="DISK_SPACE_SIZE" />) ar eich dyfais}}</translation>
<translation id="5056549851600133418">Erthyglau i chi</translation>
+<translation id="5060483733937416656">Gwnaethoch ddewis dilysu gyda Windows Hello ar wefannau sy'n defnyddio <ph name="PROVIDER_ORIGIN" />. Mae'n bosib bod y darparwr hwn wedi storio gwybodaeth am eich dull talu, y gallwch <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">A oeddech chi'n golygu <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Hanes argraffu</translation>
<translation id="5068234115460527047">Cronfeydd rhagfantoli</translation>
@@ -1509,10 +1521,8 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="5087286274860437796">Nid yw tystysgrif y gweinydd yn ddilys ar hyn o bryd.</translation>
<translation id="5087580092889165836">Ychwanegu cerdyn</translation>
<translation id="5088142053160410913">Neges i'r gweithredwr</translation>
-<translation id="5089810972385038852">Talaith</translation>
<translation id="5093232627742069661">Plyg Z</translation>
<translation id="5094747076828555589">Ni allai'r gweinydd hwn brofi ei fod yn <ph name="DOMAIN" />; nid yw Chromium yn ymddiried yn ei dystysgrif ddiogelwch. Gall hyn gael ei achosi gan gamffurfweddiad neu ymosodwr yn rhyng-gipio'ch cysylltiad.</translation>
-<translation id="5095208057601539847">Talaith</translation>
<translation id="5097099694988056070">Ystadegau'r ddyfais megis defnydd o CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Nid yw'r gwefan yn ddiogel</translation>
@@ -1550,6 +1560,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="5171045022955879922">Chwiliwch neu teipiwch URL</translation>
<translation id="5171689220826475070">Ffanffold-Ewropeaidd</translation>
<translation id="5172758083709347301">Peiriant</translation>
+<translation id="5177076414499237632">Dysgu am ffynhonnell a phwnc y dudalen hon</translation>
<translation id="5179510805599951267">Ddim yn <ph name="ORIGINAL_LANGUAGE" />? Rhoi gwybod am y gwall hwn</translation>
<translation id="518639307526414276">Bwyd anifeiliaid anwes a chyflenwadau gofal anifeiliaid anwes</translation>
<translation id="5190835502935405962">Bar Nodau Tudalen</translation>
@@ -1710,6 +1721,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="5624120631404540903">Rheoli cyfrineiriau</translation>
<translation id="5629630648637658800">Methwyd â llwytho gosodiadau polisi</translation>
<translation id="5631439013527180824">Tocyn rheoli dyfais annilys</translation>
+<translation id="5632485077360054581">Dangoswch i fi sut</translation>
<translation id="5633066919399395251">Mae'n bosib y bydd ymosodwyr sydd ar <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ar hyn o bryd yn ceisio gosod rhaglenni peryglus ar eich cyfrifiadur sy'n dwyn neu'n dileu eich gwybodaeth (er enghraifft, lluniau, cyfrineiriau, negeseuon a chardiau credyd). <ph name="BEGIN_LEARN_MORE_LINK" />Dysgu rhagor<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Rhwystrwyd cynnwys twyllodrus.</translation>
<translation id="5633259641094592098">Ffilmiau cwlt ac indie</translation>
@@ -1827,6 +1839,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="5989320800837274978">Heb nodi naill ai dirprwy weinyddwyr nac URL sgript .pac.</translation>
<translation id="5992691462791905444">Plyg-Z Peirianneg</translation>
<translation id="5995727681868049093">Rheoli eich gwybodaeth, preifatrwydd, a diogelwch yn eich Cyfrif Google</translation>
+<translation id="5997247540087773573">Canfuwyd y cyfrinair rydych newydd ei ddefnyddio mewn achos o dor data. Er mwyn diogelu'ch cyfrifon, mae Rheolwr Cyfrineiriau Google yn argymell eu newid nawr a gwirio'ch cyfrineiriau sydd wedi'u cadw.</translation>
<translation id="6000758707621254961">Mae <ph name="RESULT_COUNT" /> o ganlyniadau ar gyfer '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">Clasurol</translation>
<translation id="6008122969617370890">Trefn N-i-1</translation>
@@ -1922,7 +1935,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="627746635834430766">I dalu'n gyflymach y tro nesaf, cadwch eich cerdyn a'ch cyfeiriad bilio i'ch Cyfrif Google.</translation>
<translation id="6279183038361895380">Pwyswch |<ph name="ACCELERATOR" />| i ddangos eich cyrchwr</translation>
<translation id="6280223929691119688">Methu ag anfon i'r cyfeiriad hwn. Dewiswch gyfeiriad gwahanol.</translation>
-<translation id="6282194474023008486">Côd Post</translation>
<translation id="6285507000506177184">Botwm rheoli lawrlwythiadau yn Chrome, pwyswch Enter i reoli ffeiliau rydych wedi'u lawrlwytho yn Chrome</translation>
<translation id="6289939620939689042">Lliw'r Dudalen</translation>
<translation id="6290238015253830360">Mae'r erthyglau a awgrymir gennych yn ymddangos yma</translation>
@@ -1944,6 +1956,7 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="6337133576188860026">Yn creu llai na <ph name="SIZE" />. Mae'n bosib y bydd rhai gwefannau yn llwytho'n arafach ar eich ymweliad nesaf.</translation>
<translation id="6337534724793800597">Hidlo polisïau yn ôl enw</translation>
<translation id="6340739886198108203">Nid yw'r polisi gweinyddwr yn argymell cymryd sgrinluniau na recordiadau pan fydd cynnwys cyfrinachol yn weladwy:</translation>
+<translation id="6348220984832452017">Amrywiadau Gweithredol</translation>
<translation id="6349101878882523185">Gosod <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Dim}=1{1 cyfrinair (ar gyfer <ph name="DOMAIN_LIST" />, cysonwyd)}=2{2 gyfrinair (ar gyfer <ph name="DOMAIN_LIST" />, cysonwyd)}few{# chyfrinair (ar gyfer <ph name="DOMAIN_LIST" />, cysonwyd)}many{# chyfrinair (ar gyfer <ph name="DOMAIN_LIST" />, cysonwyd)}other{# cyfrinair (ar gyfer <ph name="DOMAIN_LIST" />, cysonwyd)}}</translation>
<translation id="6355392890578844978">Nid yw'r porwr hwn yn cael ei reoli gan gwmni neu sefydliad arall. Gellir rheoli gweithgarwch ar y ddyfais hon y tu allan i Chromium. <ph name="BEGIN_LINK" />Dysgu rhagor<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="643051589346665201">Newid cyfrinair Google</translation>
<translation id="6433490469411711332">Golygu manylion cyswllt</translation>
<translation id="6433595998831338502">Mae <ph name="HOST_NAME" /> wedi gwrthod cysylltu.</translation>
-<translation id="6438025220577812695">Newid ef fy hun</translation>
<translation id="6440503408713884761">Wedi anwybyddu</translation>
<translation id="6443406338865242315">Pa estyniadau ac ategion rydych wedi'u gosod</translation>
<translation id="6446163441502663861">Kahu (Amlen)</translation>
@@ -2105,9 +2117,9 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="6828866289116430505">Geneteg</translation>
<translation id="6831043979455480757">Cyfieithu</translation>
<translation id="6833752742582340615">Cadw eich cerdyn a gwybodaeth bilio i'ch Cyfrif Google er mwyn talu'n gyflymach ac yn ddiogel</translation>
-<translation id="6839929833149231406">Ardal</translation>
<translation id="6846340164947227603">Defnyddio rhif cerdyn rhithwir...</translation>
<translation id="6852204201400771460">Ail-lwytho'r ap?</translation>
+<translation id="6857776781123259569">Rheoli Cyfrineiriau...</translation>
<translation id="686485648936420384">Adnoddau defnyddwyr</translation>
<translation id="6865412394715372076">Ni ellir dilysu'r cerdyn hwn ar hyn o bryd</translation>
<translation id="6869334554832814367">Benthyciadau personol</translation>
@@ -2156,7 +2168,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="6965978654500191972">Dyfais</translation>
<translation id="696703987787944103">Canfyddiadol</translation>
<translation id="6968269510885595029">Defnyddio'ch Allwedd Ddiogelwch</translation>
-<translation id="6970216967273061347">Ardal</translation>
<translation id="6971439137020188025">Creu cyflwyniad Google newydd yn Slides yn gyflym</translation>
<translation id="6972629891077993081">Dyfeisiau HID</translation>
<translation id="6973656660372572881">Nodir gweinyddwyr dirprwy sefydlog ac URL sgript .pac.</translation>
@@ -2195,7 +2206,6 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<translation id="7081308185095828845">Nid yw'r nodwedd hon ar gael ar eich dyfais</translation>
<translation id="7083258188081898530">Hambwrdd 9</translation>
<translation id="7086090958708083563">Mae defnyddiwr wedi gofyn i uwchlwytho</translation>
-<translation id="7087282848513945231">Sir</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, pwyswch Tab yna Enter i reoli caniatadau a data sydd wedi'u storio ar draws gwefannau yng ngosodiadau Chrome</translation>
<translation id="7096937462164235847">Nid yw hunaniaeth y wefan hon wedi'i dilysu.</translation>
<translation id="7101893872976785596">Ffilmiau arswyd</translation>
@@ -2214,10 +2224,10 @@ Fel arall, bydd eich gosodiadau preifatrwydd yn rhwystro hyn. Bydd hyn yn caniat
<ph name="LIST_ITEM" />Gwybodaeth a roddir mewn ffurflenni<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Methu ag anfon i'r cyfeiriad hwn. Dewiswch gyfeiriad gwahanol.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Mae eich gweinyddwr wedi gwahardd y data hyn rhag cael eu copïo.</translation>
<translation id="7135130955892390533">Dangos statws</translation>
<translation id="7138472120740807366">Dull cyflwyno</translation>
-<translation id="7139724024395191329">Emiriaeth</translation>
<translation id="7139892792842608322">Prif Hambwrdd</translation>
<translation id="714064300541049402">Ochr 2 llun X sifft</translation>
<translation id="7152423860607593928">Rhif-14 (Amlen)</translation>
@@ -2434,6 +2444,7 @@ Manylion ychwanegol:
<translation id="7669271284792375604">Mae'n bosib y bydd ymosodwyr yn ceisio eich twyllo i osod rhaglenni sy'n niweidio'ch profiad pori (er enghraifft, drwy newid eich tudalen hafan neu ddangos hysbysebion ychwanegol ar wefannau rydych yn ymweld â nhw).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Camau gweithredu a gymerwyd â data sydd wedi'u fflagio'n gyfrinachol (1 cam gweithredu ers mewngofnodi). <ph name="BEGIN_LINK" />Dysgu rhagor<ph name="END_LINK" />}zero{Camau gweithredu a gymerwyd â data sydd wedi'u fflagio'n gyfrinachol (# cam gweithredu ers mewngofnodi). <ph name="BEGIN_LINK" />Dysgu rhagor<ph name="END_LINK" />}two{Camau gweithredu a gymerwyd â data sydd wedi'u fflagio'n gyfrinachol (# gam gweithredu ers mewngofnodi). <ph name="BEGIN_LINK" />Dysgu rhagor<ph name="END_LINK" />}few{Camau gweithredu a gymerwyd â data sydd wedi'u fflagio'n gyfrinachol (# cham gweithredu ers mewngofnodi). <ph name="BEGIN_LINK" />Dysgu rhagor<ph name="END_LINK" />}many{Camau gweithredu a gymerwyd â data sydd wedi'u fflagio'n gyfrinachol (# cham gweithredu ers mewngofnodi). <ph name="BEGIN_LINK" />Dysgu rhagor<ph name="END_LINK" />}other{Camau gweithredu a gymerwyd â data sydd wedi'u fflagio'n gyfrinachol (# cam gweithredu ers mewngofnodi). <ph name="BEGIN_LINK" />Dysgu rhagor<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Blwch negeseuon 6</translation>
+<translation id="7675325315208090829">Rheoli Dulliau Talu...</translation>
<translation id="7676643023259824263">Chwilio am destun clipfwrdd, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Gweld a rheoli eich hanes pori yng ngosodiadau Chrome</translation>
<translation id="7679947978757153706">Pêl fas</translation>
@@ -2476,7 +2487,6 @@ Manylion ychwanegol:
<translation id="7766518757692125295">Sgert</translation>
<translation id="7770259615151589601">Dynodedig-Hir</translation>
<translation id="7773005668374414287">Yr un drefn wyneb i fyny</translation>
-<translation id="777702478322588152">Rhaglawiaeth</translation>
<translation id="7791011319128895129">Heb ei ryddhau</translation>
<translation id="7791196057686275387">Bwndelu</translation>
<translation id="7791543448312431591">Ychwanegu</translation>
@@ -2567,12 +2577,12 @@ Manylion ychwanegol:
<translation id="8055534648776115597">Addysg alwedigaethol a pharhaus</translation>
<translation id="8057711352706143257">Nid yw "<ph name="SOFTWARE_NAME" />" wedi'i ffurfweddu'n gywir. Gallwch ddatrys y broblem fel arfer drwy ddadosod "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Cynhyrchu bwyd</translation>
-<translation id="8066955247577885446">Mae'n ddrwg gennym, aeth rhywbeth o'i le.</translation>
<translation id="8067872629359326442">Rydych newydd roi'ch cyfrinair ar wefan dwyllodrus. Gall Chromium helpu. I newid eich cyfrinair ac i hysbysu Google y gallai eich cyfrif fod mewn perygl, cliciwch ar Diogelu Cyfrif.</translation>
<translation id="8070439594494267500">Eicon ap</translation>
<translation id="8074253406171541171">10x13 (Amlen)</translation>
<translation id="8075736640322370409">Creu Google Sheet yn gyflym</translation>
<translation id="8075898834294118863">Rheoli gosodiadau gwefan</translation>
+<translation id="8076492880354921740">Tabiau</translation>
<translation id="8078141288243656252">Ni all ei anodi pan fydd yn cylchdroi</translation>
<translation id="8079031581361219619">Ail-lwytho'r dudalen?</translation>
<translation id="8081087320434522107">Ceir sedan</translation>
@@ -2695,6 +2705,7 @@ Manylion ychwanegol:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Gosodiadau</translation>
+<translation id="8428634594422941299">Deall</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, pwyswch Tab yna Enter i reoli eich dewisiadau cwcis yng ngosodiadau Chrome</translation>
<translation id="8433057134996913067">Bydd hyn yn eich allgofnodi o'r mwyafrif o wefannau.</translation>
<translation id="8434840396568290395">Anifeiliaid anwes</translation>
@@ -2792,6 +2803,7 @@ Manylion ychwanegol:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> yw eich cod ar gyfer <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Creu nod tudalen ar gyfer y tab hwn</translation>
<translation id="8751426954251315517">Rhowch gynnig arall arni y tro nesaf</translation>
+<translation id="8757526089434340176">Mae cynnig Google Pay ar gael</translation>
<translation id="8758885506338294482">Chwarae gemau fideo cystadleuol</translation>
<translation id="8759274551635299824">Mae'r cerdyn hwn wedi darfod</translation>
<translation id="87601671197631245">Mae'r wefan hon yn defnyddio hen ffurfweddiad diogelwch, a allai ddatgelu'ch gwybodaeth (er enghraifft, cyfrineiriau, negeseuon, neu gardiau credyd) pan fydd yn cael ei hanfon i'r wefan hon.</translation>
@@ -2799,6 +2811,7 @@ Manylion ychwanegol:
<translation id="8763927697961133303">Dyfais USB</translation>
<translation id="8763986294015493060">Cau pob ffenestr anhysbys sydd ar agor ar hyn o bryd</translation>
<translation id="8766943070169463815">Mae'r daflen ddilysu manylion talu ddiogel ar agor</translation>
+<translation id="8767765348545497220">Cau'r swigen help</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Beiciau modur</translation>
<translation id="8790007591277257123">&amp;Ailwneud dileu</translation>
@@ -2811,6 +2824,7 @@ Manylion ychwanegol:
<translation id="8806285662264631610">Cynhyrchion bath a chorff</translation>
<translation id="8807160976559152894">Tocio ar ôl pob tudalen</translation>
<translation id="8808828119384186784">Gosodiadau Chrome</translation>
+<translation id="8813277370772331957">Atgoffa fi yn nes ymlaen</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, pwyswch Tab yna Enter i ddiweddaru Chrome o'ch gosodiadau Chrome</translation>
<translation id="8820817407110198400">Nodau tudalen</translation>
<translation id="882338992931677877">Slot Pwrpasol</translation>
@@ -2990,6 +3004,7 @@ Manylion ychwanegol:
<translation id="988159990683914416">Fersiwn Datblygwyr</translation>
<translation id="989988560359834682">Golygu'r Cyfeiriad</translation>
<translation id="991413375315957741">synwyryddion symudiad neu oleuadau</translation>
+<translation id="992110854164447044">Mae cerdyn rhithwir yn cuddio eich cerdyn go iawn i helpu i'ch amddiffyn rhag twyll posib. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992256792861109788">Pinc</translation>
<translation id="992432478773561401">Ni osodwyd "<ph name="SOFTWARE_NAME" />" yn iawn ar eich cyfrifiadur na'r rhwydwaith:
&lt;ul&gt;
diff --git a/chromium/components/strings/components_strings_da.xtb b/chromium/components/strings/components_strings_da.xtb
index cad2336fb29..98c027f11fb 100644
--- a/chromium/components/strings/components_strings_da.xtb
+++ b/chromium/components/strings/components_strings_da.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Bevillinger, legater og finansiel bistand</translation>
<translation id="1048785276086539861">NÃ¥r du redigerer annoteringer, skifter dette dokument tilbage til enkeltsidevisning</translation>
<translation id="1050038467049342496">Luk andre apps</translation>
+<translation id="1053959602163383901">Du har valgt at bekræfte med en authenticator-enhed på websites, der anvender <ph name="PROVIDER_ORIGIN" />. Denne udbyder har muligvis gemt oplysninger om din betalingsmetode. Du kan <ph name="LINK_TEXT" /> af disse oplysninger.</translation>
<translation id="1055184225775184556">&amp;Fortryd tilføjelse</translation>
<translation id="1056663316309890343">Fotosoftware</translation>
<translation id="1056898198331236512">Advarsel</translation>
@@ -119,6 +120,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="1270502636509132238">Afhentningsmetode</translation>
<translation id="1281476433249504884">Stabler 1</translation>
<translation id="1285320974508926690">Oversæt aldrig dette website</translation>
+<translation id="1288548991597756084">Gem kort på en sikker måde</translation>
<translation id="1292571435393770077">Bakke 16</translation>
<translation id="1292701964462482250">"Der er software på computeren, som forhindrer, at Chrome kan oprette en sikker forbindelse til nettet" (kun Windows-computere)</translation>
<translation id="1294154142200295408">Variationer i kommandolinjer</translation>
@@ -223,6 +225,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
&lt;p&gt;Du kan rette fejlen ved at klikke på &lt;strong&gt;Opret forbindelse&lt;/strong&gt; på den side, du forsøger at åbne.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landskabsarkitektur</translation>
<translation id="1513706915089223971">Liste over historiske poster</translation>
+<translation id="1516097932025103760">Oplysningerne krypteres og gemmes på sikker vis. Kontrolkoden gemmes aldrig.</translation>
<translation id="1517433312004943670">Telefonnummer er påkrævet</translation>
<translation id="1519264250979466059">Versionsdato</translation>
<translation id="1521159554480556801">Fiber- og tekstilkunst</translation>
@@ -272,7 +275,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="1634828734222219955">Total</translation>
<translation id="163669211644121865">Skatteangivelse og -rådgivning</translation>
<translation id="1638780421120290329">Kortet kan ikke gemmes</translation>
-<translation id="1639239467298939599">Indlæser...</translation>
+<translation id="1639239467298939599">Indlæser</translation>
<translation id="1640180200866533862">Brugerpolitikker</translation>
<translation id="1640244768702815859">Prøv at <ph name="BEGIN_LINK" />gå til websitets startside<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">Forsink output indtil</translation>
@@ -288,6 +291,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="1662550410081243962">Gem og udfyld betalingsmetoder</translation>
<translation id="1663943134801823270">Kort og adresser stammer fra Chrome. Du kan administrere dem i <ph name="BEGIN_LINK" />Indstillinger<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Sider på <ph name="SOURCE_LANGUAGE" /> oversættes fremover til <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Betal med <ph name="CARD_DETAIL" /> for at benytte tilbuddet</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> til <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Kort kant først</translation>
<translation id="168693727862418163">Politikkens værdi blev ikke bekræftet i forhold til dens skema, og den ignoreres derfor.</translation>
@@ -306,6 +310,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="1717218214683051432">Bevægelsessensorer</translation>
<translation id="1717494416764505390">Postkasse 3</translation>
<translation id="1718029547804390981">Dokumenterne er for store til at blive annoteret</translation>
+<translation id="1720941539803966190">Luk selvstudiet</translation>
<translation id="1721424275792716183">* Feltet skal udfyldes</translation>
<translation id="1727613060316725209">Certifikatet er gyldigt</translation>
<translation id="1727741090716970331">Tilføj et gyldigt kortnummer</translation>
@@ -422,8 +427,8 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="205212645995975601">Grill</translation>
<translation id="2053111141626950936">Sider på <ph name="LANGUAGE" /> oversættes ikke.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Når denne indstilling er aktiveret og angives som aktiv, fastslår Chrome, hvilken stor gruppe eller "kohorte" din seneste browseraktivitet minder mest om. Annoncører kan vælge annoncer til gruppen, og din browserhistorik forbliver privat på din enhed. Din gruppe opdateres hver dag.}=1{Når denne indstilling er aktiveret og angives som aktiv, fastslår Chrome, hvilken stor gruppe eller "kohorte" din seneste browseraktivitet minder mest om. Annoncører kan vælge annoncer til gruppen, og din browserhistorik forbliver privat på din enhed. Din gruppe opdateres hver dag.}one{Når denne indstilling er aktiveret og angives som aktiv, fastslår Chrome, hvilken stor gruppe eller "kohorte" din seneste browseraktivitet minder mest om. Annoncører kan vælge annoncer til gruppen, og din browserhistorik forbliver privat på din enhed. Din gruppe opdateres med {NUM_DAYS} dags mellemrum.}other{Når denne indstilling er aktiveret og angives som aktiv, fastslår Chrome, hvilken stor gruppe eller "kohorte" din seneste browseraktivitet minder mest om. Annoncører kan vælge annoncer til gruppen, og din browserhistorik forbliver privat på din enhed. Din gruppe opdateres med {NUM_DAYS} dages mellemrum.}}</translation>
-<translation id="2053553514270667976">Postnummer</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 forslag}one{# forslag}other{# forslag}}</translation>
+<translation id="2066915425250589881">anmode om sletning</translation>
<translation id="2068528718802935086">Babyer og småbørn</translation>
<translation id="2071156619270205202">Dette kort er ikke kvalificeret til et virtuelt kortnummer</translation>
<translation id="2071692954027939183">Der blev automatisk blokeret notifikationer, da du som regel ikke tillader notifikationer</translation>
@@ -435,7 +440,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="2088086323192747268">Knappen Administrer synkronisering – tryk på Enter for at administrere, hvilke oplysninger der skal synkroniseres, i Chrome-indstillingerne</translation>
<translation id="2091887806945687916">Lyd</translation>
<translation id="2094505752054353250">Uoverensstemmelse mellem domæner</translation>
-<translation id="2096368010154057602">Afdeling</translation>
<translation id="2099652385553570808">Trehæftning i venstre side</translation>
<translation id="2101225219012730419">Version:</translation>
<translation id="2102134110707549001">Foreslå stærk adgangskode…</translation>
@@ -472,7 +476,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="2185836064961771414">Amerikansk fodbold</translation>
<translation id="2187317261103489799">Registrer (standardindstilling)</translation>
<translation id="2188375229972301266">Flere huller nederst</translation>
-<translation id="2188852899391513400">Den adgangskode, du lige har brugt, er blevet lækket i forbindelse med et brud på datasikkerheden. For at beskytte dine konti anbefaler Google Adgangskodeadministrator, at du ændrer det nu og derefter tjekker dine gemte adgangskoder.</translation>
<translation id="219906046732893612">Boligforbedringer</translation>
<translation id="2202020181578195191">Angiv et gyldigt udløbsår</translation>
<translation id="22081806969704220">Bakke 3</translation>
@@ -483,6 +486,8 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="2215727959747642672">Filredigering</translation>
<translation id="2215963164070968490">Hunde</translation>
<translation id="2218879909401188352">Hackere, der i øjeblikket er på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, kan installere farlige apps, som kan skade din enhed, føje skjulte gebyrer til din mobilregning eller stjæle dine personlige oplysninger. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Genstart selvstudiet</translation>
+<translation id="2219735899272417925">Enheden skal nulstilles</translation>
<translation id="2224337661447660594">Der er ingen internetforbindelse</translation>
<translation id="2230458221926704099">Ret problemerne med din forbindelse ved hjælp af <ph name="BEGIN_LINK" />diagnoseappen<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Send nu</translation>
@@ -580,11 +585,13 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="2512101340618156538">Ikke tilladt (standard)</translation>
<translation id="2512413427717747692">Knappen Angiv Chrome som standardbrowser, tryk på Enter for at angive Chrome som systemets standardbrowser i iOS-indstillingerne</translation>
<translation id="2515629240566999685">Tjekke signalet i dit område</translation>
+<translation id="2515761554693942801">Du har valgt at bekræfte med Touch ID på websites, der anvender <ph name="PROVIDER_ORIGIN" />. Denne udbyder har muligvis gemt oplysninger om din betalingsmetode. Du kan <ph name="LINK_TEXT" /> af disse oplysninger.</translation>
<translation id="2521385132275182522">Hæftning nederst til højre</translation>
<translation id="2521736961081452453">Opret formular</translation>
<translation id="2523886232349826891">Gemmes kun på denne enhed</translation>
<translation id="2524461107774643265">Tilføj flere oplysninger</translation>
<translation id="2529899080962247600">Dette felt må ikke have mere end <ph name="MAX_ITEMS_LIMIT" /> poster. Alle yderligere poster ignoreres.</translation>
+<translation id="253493526287553278">Se oplysninger om kampagnekode</translation>
<translation id="2535585790302968248">Ã…bn en ny inkognitofane for at browse privat</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{og 1 mere}one{og # mere}other{og # mere}}</translation>
<translation id="2536110899380797252">Tilføj adresse</translation>
@@ -620,7 +627,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="259821504105826686">Foto- og digitalkunst</translation>
<translation id="2601150049980261779">Romantiske film</translation>
<translation id="2604589665489080024">Popmusik</translation>
-<translation id="2609632851001447353">Varianter</translation>
<translation id="2610561535971892504">Klik for at kopiere</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />gemmer ikke<ph name="END_EMPHASIS" /> følgende oplysninger:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Fødselsdage og navnedage</translation>
<translation id="2677748264148917807">Forlad</translation>
+<translation id="2679714844901977852">Gem dit kort og dine faktureringsoplysninger på din Google-konto (<ph name="USER_EMAIL" />) for at betale hurtigere og mere sikkert</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Bedste format</translation>
<translation id="2688969097326701645">Ja, fortsæt</translation>
@@ -701,7 +708,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="2854764410992194509">Internetudbydere</translation>
<translation id="2856444702002559011">Brugere med ondsindede hensigter kan forsøge at stjæle dine oplysninger fra <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (f.eks. adgangskoder, beskeder eller kreditkort). <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Dette website viser påtrængende eller vildledende annoncer.</translation>
-<translation id="286512204874376891">Et virtuelt kort skjuler dit faktiske kort og beskytter dig på den måde mod potentiel svindel. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Afslappet</translation>
<translation id="28761159517501904">Film</translation>
<translation id="2876489322757410363">Du forlader inkognitotilstand for at betale via en ekstern app. Vil du fortsætte?</translation>
@@ -802,7 +808,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">Det Wi-Fi-netværk, du bruger, kan kræve, at du går til netværkets loginside.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ø</translation>
<translation id="3176929007561373547">Kontrollér dine proxyindstillinger, eller kontakt din netværksadministrator
for at sikre, at proxyserveren fungerer. Hvis du ikke mener,
at du skal bruge en proxyserver, skal du:
@@ -881,9 +886,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="3369192424181595722">Urfejl</translation>
<translation id="3369459162151165748">Dele og tilbehør til biler</translation>
<translation id="3371064404604898522">Angiv Chrome som standardbrowser</translation>
-<translation id="3371076217486966826"><ph name="URL" /> vil gerne:
- • Oprette et 3D-kort over dine omgivelser og registrere kamerapositionen
- • Anvende dit kamera</translation>
<translation id="337363190475750230">Deprovisioneret</translation>
<translation id="3375754925484257129">Kør Chrome-sikkerhedstjek</translation>
<translation id="3377144306166885718">Serveren benyttede en forældet version af TSL.</translation>
@@ -900,6 +902,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="3399952811970034796">Leveringsadresse</translation>
<translation id="3402261774528610252">Den forbindelse, der blev brugt til at indlæse dette website, benyttede TSL 1.0 eller TLS 1.1, som er blevet udfaset, og som bliver deaktiveret inden for den nærmeste fremtid. Når deaktiveringen har fundet sted, kan brugerne ikke indlæse dette website. Serveren bør aktivere TLS 1.2 eller nyere.</translation>
<translation id="3405664148539009465">Tilpas skrifttyper</translation>
+<translation id="3407789382767355356">login hos tredjepart</translation>
<translation id="3409896703495473338">Administrer sikkerhedsindstillinger</translation>
<translation id="3414952576877147120">Størrelse:</translation>
<translation id="3417660076059365994">De filer, du uploader eller vedhæfter, sendes til Google Cloud eller tredjeparter, hvor de analyseres. De kan f.eks. blive scannet for følsomme oplysninger eller malware.</translation>
@@ -932,6 +935,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="3477679029130949506">Filmoversigter og spilletider</translation>
<translation id="3479552764303398839">Ikke nu</translation>
<translation id="3484560055331845446">Du kan miste adgangen til din Google-konto. Chrome anbefaler, at du skifter din adgangskode nu. Du bliver bedt om at logge ind.</translation>
+<translation id="3484861421501147767">Påmindelse: En gemt kampagnekode er tilgængelig</translation>
<translation id="3487845404393360112">Bakke 4</translation>
<translation id="3495081129428749620">Find på side
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="3810973564298564668">Administrer</translation>
<translation id="3816482573645936981">Værdi (tilsidesat)</translation>
<translation id="382518646247711829">Hvis du bruger en proxyserver...</translation>
+<translation id="3826050100957962900">Third-party sign-in</translation>
<translation id="3827112369919217609">Absolut</translation>
<translation id="3827666161959873541">Familiefilm</translation>
<translation id="3828924085048779000">Tomme adgangssætninger er ikke tilladt.</translation>
@@ -1068,9 +1073,9 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="3858027520442213535">Opdater dato og tid</translation>
<translation id="3858860766373142691">Navn</translation>
<translation id="3872834068356954457">Videnskab</translation>
+<translation id="3875783148670536197">Vis mig hvordan</translation>
<translation id="3881478300875776315">Vis færre linjer</translation>
<translation id="3884278016824448484">Modstridende enheds-id</translation>
-<translation id="3885155851504623709">Amt</translation>
<translation id="388632593194507180">Overvågning blev registreret.</translation>
<translation id="3886948180919384617">Stabler 3</translation>
<translation id="3890664840433101773">Tilføj mailadresse</translation>
@@ -1100,9 +1105,11 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="3973234410852337861"><ph name="HOST_NAME" /> er blokeret</translation>
<translation id="3978338123949022456">Søgetilstand, skriv en forespørgsel, og tryk på Enter for at søge på <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Fugle</translation>
+<translation id="3985750352229496475">Administrer adresser...</translation>
<translation id="3986705137476756801">Deaktiver Livetekstning indtil videre</translation>
<translation id="3987940399970879459">Mindre end 1 MB</translation>
<translation id="3990250421422698716">Langsom offsetkørsel</translation>
+<translation id="3992684624889376114">Om denne side</translation>
<translation id="3996311196211510766">Websitet <ph name="ORIGIN" /> har anmodet om, at en politik for oprindelse
skal gælde for alle anmodninger til websitet, men i øjeblikket kan denne politik ikke anvendes.</translation>
<translation id="4006465311664329701">Betalingsmetoder, tilbud og adresser, der bruger Google Pay</translation>
@@ -1227,6 +1234,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="4305666528087210886">Din fil kunne ikke tilgås</translation>
<translation id="4306529830550717874">Vil du gemme adressen?</translation>
<translation id="4306812610847412719">udklipsholder</translation>
+<translation id="4310070645992025887">Søg i dine søgeforløb</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloker (standardindstilling)</translation>
<translation id="4314815835985389558">Administrer synkronisering</translation>
@@ -1277,6 +1285,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Brug af en proxy er deaktiveret, men en eksplicit proxykonfiguration er angivet.</translation>
<translation id="4441832193888514600">Ignoreret, fordi politikken kun kan angives som en skybrugerpolitik.</translation>
+<translation id="4442470707340296952">Chrome-faner</translation>
<translation id="4450893287417543264">Vis ikke igen</translation>
<translation id="4451135742916150903">Websitet kan anmode om tilladelse til at oprette forbindelse til HID-enheder</translation>
<translation id="4452328064229197696">Den adgangskode, du lige har brugt, er blevet lækket i forbindelse med et brud på datasikkerheden. For at beskytte dine konti anbefaler Google Adgangskodeadministrator, at du tjekker dine gemte adgangskoder.</translation>
@@ -1415,6 +1424,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="483241715238664915">Aktivér advarsler</translation>
<translation id="4834250788637067901">Betalingsmetoder, tilbud og adresser, der bruger Google Pay</translation>
<translation id="4838327282952368871">Drømmende</translation>
+<translation id="4839087176073128681">Betal hurtigere næste gang, og beskyt dit kort med Googles brancheførende sikkerhed.</translation>
<translation id="4840250757394056958">Se din Chrome-historik</translation>
<translation id="484462545196658690">Automatisk</translation>
<translation id="484671803914931257">Få rabat på <ph name="MERCHANT_NAME" /> og meget mere</translation>
@@ -1477,6 +1487,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="5011561501798487822">Registreret sprog</translation>
<translation id="5015510746216210676">Maskinnavn:</translation>
<translation id="5017554619425969104">Tekst, du har kopieret</translation>
+<translation id="5017828934289857214">PÃ¥mind mig senere</translation>
<translation id="5018422839182700155">Denne side kan ikke åbnes</translation>
<translation id="5019198164206649151">Sikkerhedskopien er fejlbehæftet</translation>
<translation id="5020776957610079374">Verdensmusik</translation>
@@ -1496,6 +1507,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="5051305769747448211">Livekomedier</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Hvis du vil sende denne fil ved hjælp af Deling tæt på, skal du frigøre plads (<ph name="DISK_SPACE_SIZE" />) på din enhed}one{Hvis du vil sende denne fil ved hjælp af Deling tæt på, skal du frigøre plads (<ph name="DISK_SPACE_SIZE" />) på din enhed}other{Hvis du vil sende disse filer ved hjælp af Deling tæt på, skal du frigøre plads (<ph name="DISK_SPACE_SIZE" />) på din enhed}}</translation>
<translation id="5056549851600133418">Artikler til dig</translation>
+<translation id="5060483733937416656">Du har valgt at bekræfte med Windows Hello på websites, der anvender <ph name="PROVIDER_ORIGIN" />. Denne udbyder har muligvis gemt oplysninger om din betalingsmetode. Du kan <ph name="LINK_TEXT" /> af disse oplysninger.</translation>
<translation id="5061227663725596739">Mente du <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Udskrivningshistorik</translation>
<translation id="5068234115460527047">Hedgefonde</translation>
@@ -1509,10 +1521,8 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="5087286274860437796">Serverens certifikatet er ikke gyldigt i øjeblikket.</translation>
<translation id="5087580092889165836">Tilføj kort</translation>
<translation id="5088142053160410913">Meddelelse til operator</translation>
-<translation id="5089810972385038852">Stat</translation>
<translation id="5093232627742069661">Z-fals</translation>
<translation id="5094747076828555589">Denne server kunne ikke bevise, at den er <ph name="DOMAIN" />, da Chromium ikke har tillid til sikkerhedscertifikatet. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse.</translation>
-<translation id="5095208057601539847">Provins</translation>
<translation id="5097099694988056070">Enhedsstatistik som f.eks. CPU/RAM-forbrug</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Websitet er ikke sikkert</translation>
@@ -1550,6 +1560,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="5171045022955879922">Søg, eller angiv webadresse</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Maskine</translation>
+<translation id="5177076414499237632">FÃ¥ flere oplysninger om denne sides kilde og emne</translation>
<translation id="5179510805599951267">Ikke på <ph name="ORIGINAL_LANGUAGE" />? Rapporter denne fejl</translation>
<translation id="518639307526414276">Foder og udstyr til kæledyr</translation>
<translation id="5190835502935405962">Bogmærkelinje</translation>
@@ -1710,6 +1721,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="5624120631404540903">Administrer adgangskoder</translation>
<translation id="5629630648637658800">Der kunne ikke indlæses indstillinger for politik</translation>
<translation id="5631439013527180824">Ugyldigt token for enhedsadministration</translation>
+<translation id="5632485077360054581">Vis mig hvordan</translation>
<translation id="5633066919399395251">Brugere med ondsindede hensigter, der i øjeblikket er på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, kan forsøge at installere farlige programmer på din computer, der stjæler eller sletter dine oplysninger (f.eks. fotos, adgangskoder, beskeder og kreditkort). <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Vildledende indhold er blokeret.</translation>
<translation id="5633259641094592098">Kult- og indiefilm</translation>
@@ -1827,6 +1839,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="5989320800837274978">Der er hverken angivet faste proxyservere eller en .pac-scriptwebadresse.</translation>
<translation id="5992691462791905444">Z-fals med kant foroven</translation>
<translation id="5995727681868049093">Administrer din konto, dit privatliv og beskyttelsen på din Google-konto</translation>
+<translation id="5997247540087773573">Den adgangskode, du lige har brugt, er blevet lækket i forbindelse med et brud på datasikkerheden. For at beskytte dine konti anbefaler Google Adgangskodeadministrator, at du ændrer det nu og tjekker dine gemte adgangskoder.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultater for "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Klassisk</translation>
<translation id="6008122969617370890">N-til-1-rækkefølge</translation>
@@ -1922,7 +1935,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="627746635834430766">Gem dit kort og din faktureringsadresse på din Google-konto for at betale hurtigere næste gang.</translation>
<translation id="6279183038361895380">Tryk på |<ph name="ACCELERATOR" />| at se markøren</translation>
<translation id="6280223929691119688">Der kan ikke leveres til denne adresse. Vælg en anden adresse.</translation>
-<translation id="6282194474023008486">Postnummer</translation>
<translation id="6285507000506177184">Knappen "Administrer downloads i Chrome" – tryk på Enter for at administrere de filer, du har downloadet i Chrome</translation>
<translation id="6289939620939689042">Farve på side</translation>
<translation id="6290238015253830360">Forslag til artikler til dig vises her</translation>
@@ -1944,6 +1956,7 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="6337133576188860026">Frigiver mindre end <ph name="SIZE" />. Nogle websites indlæses muligvis langsommere under dit næste besøg.</translation>
<translation id="6337534724793800597">Filtrér politikker efter navn</translation>
<translation id="6340739886198108203">Administratorpolitikken fraråder screenshots og optagelser, når der vises fortroligt indhold på skærmen:</translation>
+<translation id="6348220984832452017">Aktive variationer</translation>
<translation id="6349101878882523185">Installer <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ingen}=1{1 adgangskode (til <ph name="DOMAIN_LIST" /> – synkroniseret)}=2{2 adgangskoder (til <ph name="DOMAIN_LIST" /> – synkroniseret)}one{# adgangskode (til <ph name="DOMAIN_LIST" /> – synkroniseret)}other{# adgangskoder (til <ph name="DOMAIN_LIST" /> – synkroniseret)}}</translation>
<translation id="6355392890578844978">Denne browser administreres ikke af en virksomhed eller en anden organisation. Aktivitet på denne enhed administreres muligvis uden for Chromium. <ph name="BEGIN_LINK" />Få flere oplysninger<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="643051589346665201">Skift Google-adgangskode</translation>
<translation id="6433490469411711332">Rediger kontaktoplysninger</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> nægtede at oprette forbindelse.</translation>
-<translation id="6438025220577812695">Skift manuelt</translation>
<translation id="6440503408713884761">Ignoreret</translation>
<translation id="6443406338865242315">Hvilke udvidelser og plugins, du har installeret</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2105,9 +2117,9 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="6828866289116430505">Genetik</translation>
<translation id="6831043979455480757">Oversæt</translation>
<translation id="6833752742582340615">Gem dit kort og dine faktureringsoplysninger på din Google-konto for at betale hurtigere og mere sikkert</translation>
-<translation id="6839929833149231406">Område</translation>
<translation id="6846340164947227603">Brug et virtuelt kortnummer...</translation>
<translation id="6852204201400771460">Vil du genindlæse appen?</translation>
+<translation id="6857776781123259569">Administrer adgangskoder...</translation>
<translation id="686485648936420384">Forbrugerressourcer</translation>
<translation id="6865412394715372076">Dette kort kan ikke bekræftes lige nu</translation>
<translation id="6869334554832814367">Privatlån</translation>
@@ -2156,7 +2168,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="6965978654500191972">Enhed</translation>
<translation id="696703987787944103">Perceptuel</translation>
<translation id="6968269510885595029">Brug din sikkerhedsnøgle</translation>
-<translation id="6970216967273061347">Distrikt</translation>
<translation id="6971439137020188025">Opret hurtigt en ny Google-præsentation i Slides</translation>
<translation id="6972629891077993081">HID-enheder</translation>
<translation id="6973656660372572881">BÃ¥de faste proxyservere og en webadresse for .pac-script angives.</translation>
@@ -2195,7 +2206,6 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<translation id="7081308185095828845">Denne funktion er ikke tilgængelig på din enhed</translation>
<translation id="7083258188081898530">Bakke 9</translation>
<translation id="7086090958708083563">Brugeren anmodede om upload</translation>
-<translation id="7087282848513945231">Amt/region</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, tryk på Tab-tasten efterfulgt af Enter for at administrere tilladelser og data, der gemmes på websites, i Chrome-indstillingerne</translation>
<translation id="7096937462164235847">Dette websites identitet er ikke bekræftet.</translation>
<translation id="7101893872976785596">Gyserfilm</translation>
@@ -2214,10 +2224,10 @@ Ellers vil det blive blokeret af dine privatlivsindstillinger. Det giver det ind
<ph name="LIST_ITEM" />Oplysninger, der er angivet i formularer<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Der kan ikke sendes til denne adresse. Vælg en anden adresse.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Din administrator tillader ikke kopiering af disse data.</translation>
<translation id="7135130955892390533">Vis status</translation>
<translation id="7138472120740807366">Leveringsmetode</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Primær bakke</translation>
<translation id="714064300541049402">Billedskift X på side 2</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2434,6 +2444,7 @@ Yderligere oplysninger:
<translation id="7669271284792375604">Hackere på dette website kan forsøge at narre dig til at installere programmer, der skader din browseroplevelse (f.eks. ved at ændre din startside eller vise flere annoncer på de websites, du besøger).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Handlinger, som er foretaget i forbindelse med data, der er rapporteret som fortrolige (1 handling siden login). <ph name="BEGIN_LINK" />FÃ¥ flere oplysninger<ph name="END_LINK" />}one{Handlinger, som er foretaget i forbindelse med data, der er rapporteret som fortrolige (# handling siden login). <ph name="BEGIN_LINK" />FÃ¥ flere oplysninger<ph name="END_LINK" />}other{Handlinger, som er foretaget i forbindelse med data, der er rapporteret som fortrolige (# handlinger siden login). <ph name="BEGIN_LINK" />FÃ¥ flere oplysninger<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Postkasse 6</translation>
+<translation id="7675325315208090829">Administrer betalingsmetoder…</translation>
<translation id="7676643023259824263">Søg efter tekst i udklipsholderen, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Se og administrer din browserhistorik i Chrome-indstillingerne</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2476,7 +2487,6 @@ Yderligere oplysninger:
<translation id="7766518757692125295">Skørte</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Samme rækkefølge med forside opad</translation>
-<translation id="777702478322588152">Præfektur</translation>
<translation id="7791011319128895129">Ikke udgivet</translation>
<translation id="7791196057686275387">Bale</translation>
<translation id="7791543448312431591">Tilføj</translation>
@@ -2567,12 +2577,12 @@ Yderligere oplysninger:
<translation id="8055534648776115597">Erhvervsuddannelse og videregående uddannelse</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" er ikke konfigureret korrekt. Problemet kan normalt løses ved at afinstallere "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Levnedsmiddelproduktion</translation>
-<translation id="8066955247577885446">Noget gik galt.</translation>
<translation id="8067872629359326442">Du har lige angivet din adgangskode på et vildledende website. Chromium kan hjælpe. Klik på Beskyt konto for at ændre din adgangskode og underrette Google om, at din konto muligvis er kompromitteret.</translation>
<translation id="8070439594494267500">Appikon</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Opret hurtigt et nyt Google-regneark</translation>
<translation id="8075898834294118863">Administrer indstillinger for websites</translation>
+<translation id="8076492880354921740">Faner</translation>
<translation id="8078141288243656252">Der kan ikke annoteres ved rotering</translation>
<translation id="8079031581361219619">Vil du genindlæse websitet?</translation>
<translation id="8081087320434522107">Sedaner</translation>
@@ -2695,6 +2705,7 @@ Yderligere oplysninger:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Indstillinger</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, tryk på Tab-tasten efterfulgt af Enter for at administrere dine cookiepræferencer i Chrome-indstillingerne</translation>
<translation id="8433057134996913067">Dette logger dig ud af de fleste websites.</translation>
<translation id="8434840396568290395">Kæledyr</translation>
@@ -2793,6 +2804,7 @@ Yderligere oplysninger:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> er din kode til <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Tilføj denne fane som bogmærke</translation>
<translation id="8751426954251315517">Prøv igen næste gang</translation>
+<translation id="8757526089434340176">Der er et tilgængeligt Google Pay-tilbud</translation>
<translation id="8758885506338294482">Videospilkonkurrencer (e-sport)</translation>
<translation id="8759274551635299824">Kortet er udløbet</translation>
<translation id="87601671197631245">Dette website benytter en forældet sikkerhedskonfiguration, der muligvis afslører dine oplysninger (f.eks. adgangskoder, meddelelser eller betalingskort), når de sendes til dette website.</translation>
@@ -2800,6 +2812,7 @@ Yderligere oplysninger:
<translation id="8763927697961133303">USB-enhed</translation>
<translation id="8763986294015493060">Luk alle inkognitovinduer, der i øjeblikket er åbne</translation>
<translation id="8766943070169463815">Arket til godkendelse af loginoplysninger til sikker betaling er åbnet</translation>
+<translation id="8767765348545497220">Luk hjælp-boble</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorcykler</translation>
<translation id="8790007591277257123">&amp;Annuller fortryd sletning</translation>
@@ -2812,6 +2825,7 @@ Yderligere oplysninger:
<translation id="8806285662264631610">Bade- og hudplejeprodukter</translation>
<translation id="8807160976559152894">Beskær efter hver side</translation>
<translation id="8808828119384186784">Indstillinger for Chrome</translation>
+<translation id="8813277370772331957">PÃ¥mind mig senere</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" /> – tryk på Tab-tasten efterfulgt af Enter for at opdatere Chrome i dine Chrome-indstillinger</translation>
<translation id="8820817407110198400">Bogmærker</translation>
<translation id="882338992931677877">Manuel papirbakke</translation>
@@ -2991,6 +3005,7 @@ Yderligere oplysninger:
<translation id="988159990683914416">Udviklerversion</translation>
<translation id="989988560359834682">Rediger adresse</translation>
<translation id="991413375315957741">bevægelses- eller lyssensorer</translation>
+<translation id="992110854164447044">Et virtuelt kort skjuler dit faktiske kort og beskytter dig på den måde mod potentiel svindel. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Lyserød</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" blev ikke korrekt installeret på computeren eller netværket:
diff --git a/chromium/components/strings/components_strings_de.xtb b/chromium/components/strings/components_strings_de.xtb
index 0661ae3a17c..30de30f3bbf 100644
--- a/chromium/components/strings/components_strings_de.xtb
+++ b/chromium/components/strings/components_strings_de.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Ausbildungsförderung, Stipendien und Finanzhilfen</translation>
<translation id="1048785276086539861">Wenn du Anmerkungen bearbeitest, wechselt das Dokument zur Einzelseitenansicht zurück</translation>
<translation id="1050038467049342496">Andere Apps schließen</translation>
+<translation id="1053959602163383901">Du hast festgelegt, dass die Bestätigung auf Websites, die <ph name="PROVIDER_ORIGIN" /> verwenden, mit einem Authentifizierungsgerät durchgeführt werden soll. Dieser Anbieter hat möglicherweise Informationen zu deiner Zahlungsmethode gespeichert. Du kannst deren <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Hinzufügen rückgängig machen</translation>
<translation id="1056663316309890343">Fotosoftware</translation>
<translation id="1056898198331236512">Warnung</translation>
@@ -119,6 +120,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="1270502636509132238">Abholoption</translation>
<translation id="1281476433249504884">Stapelfach 1</translation>
<translation id="1285320974508926690">Diese Website nie übersetzen</translation>
+<translation id="1288548991597756084">Karte sicher speichern</translation>
<translation id="1292571435393770077">Fach 16</translation>
<translation id="1292701964462482250">"Software auf deinem Computer verhindert, dass Chrome eine sichere Internetverbindung herstellt" (nur Windows-Computer)</translation>
<translation id="1294154142200295408">Befehlszeilen-Varianten</translation>
@@ -223,6 +225,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
&lt;p&gt;Klicke auf der Seite, die du öffnen möchtest, auf &lt;strong&gt;Verbinden&lt;/strong&gt;, um den Fehler zu beheben.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landschaftsgestaltung</translation>
<translation id="1513706915089223971">Liste der Verlaufseinträge</translation>
+<translation id="1516097932025103760">Sie wird verschlüsselt und sicher gespeichert – der CVC wird jedoch nie gespeichert.</translation>
<translation id="1517433312004943670">Telefonnummer erforderlich</translation>
<translation id="1519264250979466059">Build-Datum</translation>
<translation id="1521159554480556801">Faser- und Textilkunst</translation>
@@ -272,7 +275,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="1634828734222219955">Gesamt</translation>
<translation id="163669211644121865">Steuerberatung</translation>
<translation id="1638780421120290329">Karte kann nicht gespeichert werden</translation>
-<translation id="1639239467298939599">Wird geladen...</translation>
+<translation id="1639239467298939599">Wird geladen</translation>
<translation id="1640180200866533862">Nutzerrichtlinien</translation>
<translation id="1640244768702815859">Versuche, <ph name="BEGIN_LINK" />die Startseite aufzurufen<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">Ausgabe verzögern bis</translation>
@@ -288,6 +291,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="1662550410081243962">Zahlungsmethoden speichern und ausfüllen</translation>
<translation id="1663943134801823270">Die Karten und Adressen stammen aus Chrome. Sie werden in den <ph name="BEGIN_LINK" />Einstellungen<ph name="END_LINK" /> verwaltet.</translation>
<translation id="1671391448414634642">Seiten auf <ph name="SOURCE_LANGUAGE" /> werden ab jetzt auf <ph name="TARGET_LANGUAGE" /> übersetzt.</translation>
+<translation id="1673886523110456987">Bezahle mit <ph name="CARD_DETAIL" />, um das Angebot zu nutzen</translation>
<translation id="1674504678466460478">Von <ph name="SOURCE_LANGUAGE" /> auf <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Kurze Seite zuerst</translation>
<translation id="168693727862418163">Die Richtlinienwertprüfung des Schemas ist fehlgeschlagen. Der Richtlinienwert wird ignoriert.</translation>
@@ -306,6 +310,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="1717218214683051432">Bewegungssensoren</translation>
<translation id="1717494416764505390">Ablage 3</translation>
<translation id="1718029547804390981">Das Dokument kann aufgrund seiner Größe nicht mit Anmerkungen versehen werden</translation>
+<translation id="1720941539803966190">Anleitung schließen</translation>
<translation id="1721424275792716183">* Pflichtfeld</translation>
<translation id="1727613060316725209">Zertifikat ist gültig</translation>
<translation id="1727741090716970331">Gültige Kartennummer hinzufügen</translation>
@@ -418,8 +423,8 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="205212645995975601">Barbecue und Grillen</translation>
<translation id="2053111141626950936">Seiten auf <ph name="LANGUAGE" /> werden nicht übersetzt.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Wenn dieses Steuerelement aktiviert und aktiv ist, bestimmt Chrome, welcher großen Personengruppe oder „Kohorte“ deine aktuellen Browseraktivitäten am ähnlichsten sind. Werbetreibende können Werbung für die einzelnen Gruppen auswählen und deine Browseraktivitäten werden sicher auf deinem Gerät gespeichert. Deine Gruppe wird täglich aktualisiert.}=1{Wenn dieses Steuerelement aktiviert und aktiv ist, bestimmt Chrome, welcher großen Personengruppe oder „Kohorte“ deine aktuellen Browseraktivitäten am ähnlichsten sind. Werbetreibende können Werbung für die einzelnen Gruppen auswählen und deine Browseraktivitäten werden sicher auf deinem Gerät gespeichert. Deine Gruppe wird täglich aktualisiert.}other{Wenn dieses Steuerelement aktiviert und aktiv ist, bestimmt Chrome, welcher großen Personengruppe oder „Kohorte“ deine aktuellen Browseraktivitäten am ähnlichsten sind. Werbetreibende können Werbung für die einzelnen Gruppen auswählen und deine Browseraktivitäten werden sicher auf deinem Gerät gespeichert. Deine Gruppe wird alle {NUM_DAYS} Tage aktualisiert.}}</translation>
-<translation id="2053553514270667976">Postleitzahl</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 Vorschlag}other{# Vorschläge}}</translation>
+<translation id="2066915425250589881">Löschung beantragen</translation>
<translation id="2068528718802935086">Babys und Kleinkinder</translation>
<translation id="2071156619270205202">Für diese Karte ist keine virtuelle Kartennummer verfügbar.</translation>
<translation id="2071692954027939183">Benachrichtigungen wurden automatisch blockiert, da du sie normalerweise nicht zulässt</translation>
@@ -431,7 +436,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="2088086323192747268">Schaltfläche zum Verwalten der Synchronisierung – drücke die Eingabetaste, um in den Chrome-Einstellungen zu verwalten, welche Informationen synchronisiert werden</translation>
<translation id="2091887806945687916">Ton</translation>
<translation id="2094505752054353250">Domains stimmen nicht überein.</translation>
-<translation id="2096368010154057602">Abteilung</translation>
<translation id="2099652385553570808">Drei Heftklammern links</translation>
<translation id="2101225219012730419">Version:</translation>
<translation id="2102134110707549001">Starkes Passwort vorschlagen…</translation>
@@ -468,7 +472,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="2185836064961771414">American Football</translation>
<translation id="2187317261103489799">Erkennen (Standardeinstellung)</translation>
<translation id="2188375229972301266">Mehrfache Lochung unten</translation>
-<translation id="2188852899391513400">Das Passwort, das du gerade verwendet hast, wurde in einer Datenpanne gefunden. Zum besseren Schutz deiner Konten empfiehlt der Passwortmanager von Google, das Passwort jetzt zu ändern und deine gespeicherten Passwörter zu prüfen.</translation>
<translation id="219906046732893612">Heimwerken</translation>
<translation id="2202020181578195191">Gib ein gültiges Ablaufjahr ein</translation>
<translation id="22081806969704220">Fach 3</translation>
@@ -479,6 +482,8 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="2215727959747642672">Dateien bearbeiten</translation>
<translation id="2215963164070968490">Hunde</translation>
<translation id="2218879909401188352">Angreifer auf der Website <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> könnten gefährliche Apps installieren, die dein Gerät beschädigen, deiner Mobilfunkrechnung versteckte Kosten hinzufügen oder deine personenbezogenen Daten stehlen könnten. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Anleitung neu starten</translation>
+<translation id="2219735899272417925">Gerät muss zurückgesetzt werden</translation>
<translation id="2224337661447660594">Kein Internet</translation>
<translation id="2230458221926704099">Behebe den Verbindungsfehler mithilfe der <ph name="BEGIN_LINK" />Diagnose-App<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Jetzt senden</translation>
@@ -576,11 +581,13 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="2512101340618156538">Nicht zugelassen (Standardeinstellung)</translation>
<translation id="2512413427717747692">Schaltfläche „Google Chrome als Standardbrowser festlegen“ – Drück die Eingabetaste, um Chrome in den iOS-Einstellungen als Standardbrowser für das System festzulegen</translation>
<translation id="2515629240566999685">Signal an deinem Standort prüfen</translation>
+<translation id="2515761554693942801">Du hast festgelegt, dass die Bestätigung auf Websites, die <ph name="PROVIDER_ORIGIN" /> verwenden, mit Touch ID durchgeführt werden soll. Dieser Anbieter hat möglicherweise Informationen zu deiner Zahlungsmethode gespeichert. Du kannst deren <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Heftklammer unten rechts</translation>
<translation id="2521736961081452453">Formular erstellen</translation>
<translation id="2523886232349826891">Nur auf diesem Gerät gespeichert</translation>
<translation id="2524461107774643265">Weitere Informationen hinzufügen</translation>
<translation id="2529899080962247600">Dieses Feld sollte nicht mehr als <ph name="MAX_ITEMS_LIMIT" /> Einträge enthalten. Alle weiteren Einträge werden ignoriert.</translation>
+<translation id="253493526287553278">Details zu Gutscheincode ansehen</translation>
<translation id="2535585790302968248">Einen neuen Inkognitotab öffnen, um privat zu surfen</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{und 1 weitere}other{und # weitere}}</translation>
<translation id="2536110899380797252">Adresse hinzufügen</translation>
@@ -616,7 +623,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="259821504105826686">Fotografie und digitale Kunst</translation>
<translation id="2601150049980261779">Liebesfilme</translation>
<translation id="2604589665489080024">Popmusik</translation>
-<translation id="2609632851001447353">Varianten</translation>
<translation id="2610561535971892504">Zum Kopieren klicken</translation>
<translation id="2617988307566202237">Chrome speichert folgende Daten <ph name="BEGIN_EMPHASIS" />nicht<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Geburts- und Namenstage</translation>
<translation id="2677748264148917807">Verlassen</translation>
+<translation id="2679714844901977852">Du kannst deine Karten- und Abrechnungsinformationen in deinem Google-Konto „<ph name="USER_EMAIL" />“ speichern, um schneller und sicherer zu bezahlen</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Beste Anordnung</translation>
<translation id="2688969097326701645">Ja, weiter</translation>
@@ -697,7 +704,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="2854764410992194509">Internetanbieter (ISPs)</translation>
<translation id="2856444702002559011">Hacker könnten versuchen, deine Daten von <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> zu stehlen, zum Beispiel Passwörter, Nachrichten oder Kreditkartendaten. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Diese Website zeigt aufdringliche oder irreführende Werbung an.</translation>
-<translation id="286512204874376891">Wenn du eine virtuelle Karte verwendest, werden deine tatsächlichen Kartendetails nicht preisgegeben – dadurch bist du besser vor Betrugsversuchen geschützt. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Freundlich</translation>
<translation id="28761159517501904">Filme</translation>
<translation id="2876489322757410363">Der Inkognitomodus wird deaktiviert, um über eine externe Anwendung zu zahlen. Möchtest du fortfahren?</translation>
@@ -798,7 +804,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="3158539265159265653">CD</translation>
<translation id="3162559335345991374">Unter Umständen musst du die Anmeldeseite des verwendeten WLAN-Netzwerken aufrufen.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Insel</translation>
<translation id="3176929007561373547">Vergewissere dich, dass der Proxyserver funktioniert. Überprüfe die
Proxyeinstellungen oder wende dich an deinen Netzwerkadministrator.
Falls du keinen Proxyserver verwenden möchtest, deaktiviere ihn wie
@@ -877,9 +882,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="3369192424181595722">Fehler bei der Uhrzeit</translation>
<translation id="3369459162151165748">Ersatz- und Zubehörteile für Kraftfahrzeuge</translation>
<translation id="3371064404604898522">Google Chrome als Standardbrowser festlegen</translation>
-<translation id="3371076217486966826"><ph name="URL" /> möchte:
- • eine dreidimensionale Karte deiner Umgebung erstellen und die Kameraposition verfolgen
- • deine Kamera verwenden</translation>
<translation id="337363190475750230">Bereitstellung aufgehoben</translation>
<translation id="3375754925484257129">Chrome-Sicherheitscheck ausführen</translation>
<translation id="3377144306166885718">Der Server hat eine veraltete TLS-Version verwendet.</translation>
@@ -896,6 +898,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="3399952811970034796">Lieferadresse</translation>
<translation id="3402261774528610252">Diese Website wurde unter Verwendung von TLS 1.0 oder TLS 1.1 geladen. Diese Versionen wurden eingestellt und werden zukünftig deaktiviert. Sobald sie deaktiviert wurden, können Nutzer diese Website nicht mehr aufrufen. Auf dem Server sollte TLS 1.2 oder höher laufen.</translation>
<translation id="3405664148539009465">Schriftarten anpassen</translation>
+<translation id="3407789382767355356">Drittanbieter-Anmeldung</translation>
<translation id="3409896703495473338">Sicherheitseinstellungen verwalten</translation>
<translation id="3414952576877147120">Größe:</translation>
<translation id="3417660076059365994">Hochgeladene oder angehängte Dateien werden zur Analyse an Google Cloud oder Dritte gesendet. Sie werden beispielsweise auf sensible Daten oder Malware geprüft.</translation>
@@ -928,6 +931,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="3477679029130949506">Kino- und Theaterprogramme</translation>
<translation id="3479552764303398839">Jetzt nicht</translation>
<translation id="3484560055331845446">Du könntest den Zugriff auf dein Google-Konto verlieren. Chrome empfiehlt dir, dein Passwort jetzt zu ändern. Du wirst dazu aufgefordert, dich anzumelden.</translation>
+<translation id="3484861421501147767">Erinnerung: gespeicherter Gutscheincode verfügbar</translation>
<translation id="3487845404393360112">Fach 4</translation>
<translation id="3495081129428749620">Auf Seite "<ph name="PAGE_TITLE" />" suchen</translation>
<translation id="350069200438440499">Dateiname:</translation>
@@ -1050,6 +1054,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="3810973564298564668">Verwalten</translation>
<translation id="3816482573645936981">Wert (ersetzt)</translation>
<translation id="382518646247711829">Falls du einen Proxyserver verwendest...</translation>
+<translation id="3826050100957962900">Drittanbieter-Anmeldung</translation>
<translation id="3827112369919217609">Absolut</translation>
<translation id="3827666161959873541">Filme für die ganze Familie</translation>
<translation id="3828924085048779000">Eine leere Passphrase ist nicht zulässig.</translation>
@@ -1062,9 +1067,9 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="3858027520442213535">Datum und Uhrzeit aktualisieren</translation>
<translation id="3858860766373142691">Name</translation>
<translation id="3872834068356954457">Wissenschaft</translation>
+<translation id="3875783148670536197">Zeigen</translation>
<translation id="3881478300875776315">Weniger Zeilen anzeigen</translation>
<translation id="3884278016824448484">In Konflikt stehende Gerätekennung</translation>
-<translation id="3885155851504623709">Gemeinde</translation>
<translation id="388632593194507180">Ãœberwachung erkannt</translation>
<translation id="3886948180919384617">Stapelfach 3</translation>
<translation id="3890664840433101773">E-Mail-Adresse hinzufügen</translation>
@@ -1094,9 +1099,11 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="3973234410852337861"><ph name="HOST_NAME" /> ist gesperrt</translation>
<translation id="3978338123949022456">Suchmodus – Tippe eine Anfrage ein und drück die Eingabetaste, um mit <ph name="KEYWORD_SUFFIX" /> zu suchen</translation>
<translation id="398470910934384994">Vögel</translation>
+<translation id="3985750352229496475">Adressen verwalten…</translation>
<translation id="3986705137476756801">Automatische Untertitel vorerst deaktivieren</translation>
<translation id="3987940399970879459">Weniger als 1 MB</translation>
<translation id="3990250421422698716">Jog-Versatz</translation>
+<translation id="3992684624889376114">Ãœber diese Seite</translation>
<translation id="3996311196211510766">Die Website <ph name="ORIGIN" /> verlangt, dass auf alle an sie gerichteten Anfragen eine Ursprungsrichtlinie angewendet wird. Diese Richtlinie kann jedoch momentan nicht angewandt werden.</translation>
<translation id="4006465311664329701">Zahlungsmethoden, Angebote und Adressen aus Google Pay</translation>
<translation id="4009243425692662128">Die Inhalt der gedruckten Seiten wird zur Analyse an Google Cloud oder Drittanbieter gesendet. Er wird beispielsweise auf sensible Daten geprüft.</translation>
@@ -1216,6 +1223,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="4305666528087210886">Zugriff auf die Datei nicht möglich</translation>
<translation id="4306529830550717874">Adresse speichern?</translation>
<translation id="4306812610847412719">Zwischenablage</translation>
+<translation id="4310070645992025887">In Onlinerecherchen stöbern</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blockieren (Standard)</translation>
<translation id="4314815835985389558">Synchronisierung verwalten</translation>
@@ -1266,6 +1274,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="4435702339979719576">Postkarte)</translation>
<translation id="443673843213245140">Die Proxy-Nutzung ist deaktiviert, es ist jedoch eine explizite Proxy-Konfiguration festgelegt.</translation>
<translation id="4441832193888514600">Ignoriert, da die Richtlinie nur als Cloud-Nutzerrichtlinie festgelegt werden kann.</translation>
+<translation id="4442470707340296952">Chrome-Tabs</translation>
<translation id="4450893287417543264">Nicht mehr anzeigen</translation>
<translation id="4451135742916150903">Darf nachfragen, wenn sie eine Verbindung mit HID-Geräten herstellen möchte</translation>
<translation id="4452328064229197696">Das Passwort, das du gerade verwendet hast, wurde in einer Datenpanne gefunden. Zum besseren Schutz deiner Konten empfiehlt der Passwortmanager von Google, deine gespeicherten Passwörter zu prüfen.</translation>
@@ -1273,7 +1282,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="4460315069258617173">Zugelassen, bis du Tabs dieser Website schließt</translation>
<translation id="4464826014807964867">Websites mit Informationen deiner Organisation</translation>
<translation id="447665707681730621"><ph name="BUBBLE_MESSAGE" />. <ph name="LEARN_MORE_TEXT" /></translation>
-<translation id="4476953670630786061">Dieses Formular ist nicht sicher. Die Funktion „Automatisches Ausfüllen“ wurde deaktiviert.</translation>
+<translation id="4476953670630786061">Dieses Formular ist nicht sicher. „Autofill“ wurde deaktiviert.</translation>
<translation id="4477350412780666475">Nächster Titel</translation>
<translation id="4477949251180341057">Netzwerksicherheit</translation>
<translation id="4481251927743463293">Was der Inkognitomodus bewirkt</translation>
@@ -1404,6 +1413,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="483241715238664915">Warnmeldungen aktivieren</translation>
<translation id="4834250788637067901">Zahlungsmethoden, Angebote und Adressen aus Google Pay</translation>
<translation id="4838327282952368871">Träumerisch</translation>
+<translation id="4839087176073128681">Bezahle beim nächsten Mal schneller und schütze deine Karte mit der branchenführenden Sicherheit von Google.</translation>
<translation id="4840250757394056958">Chrome-Verlauf ansehen</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">Erhalte Rabatte bei <ph name="MERCHANT_NAME" /> und weiteren Händlern</translation>
@@ -1466,6 +1476,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="5011561501798487822">Erkannte Sprache</translation>
<translation id="5015510746216210676">Computername:</translation>
<translation id="5017554619425969104">Von dir kopierter Text</translation>
+<translation id="5017828934289857214">Später erinnern</translation>
<translation id="5018422839182700155">Diese Seite kann nicht geöffnet werden</translation>
<translation id="5019198164206649151">Sicherungsspeicher ist fehlerhaft.</translation>
<translation id="5020776957610079374">Weltmusik</translation>
@@ -1485,6 +1496,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="5051305769747448211">Live-Comedy</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Damit du diese Datei mit Nearby Share senden kannst, musst du auf deinem Gerät Speicherplatz (<ph name="DISK_SPACE_SIZE" />) freigeben}other{Damit du diese Dateien mit Nearby Share senden kannst, musst du auf deinem Gerät Speicherplatz (<ph name="DISK_SPACE_SIZE" />) freigeben}}</translation>
<translation id="5056549851600133418">Artikel für mich</translation>
+<translation id="5060483733937416656">Du hast festgelegt, dass die Bestätigung auf Websites, die <ph name="PROVIDER_ORIGIN" /> verwenden, mit Windows Hello durchgeführt werden soll. Dieser Anbieter hat möglicherweise Informationen zu deiner Zahlungsmethode gespeichert. Du kannst deren <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Meintest du <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Druckverlauf</translation>
<translation id="5068234115460527047">Hedgefonds</translation>
@@ -1498,10 +1510,8 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="5087286274860437796">Das Serverzertifikat ist zurzeit ungültig.</translation>
<translation id="5087580092889165836">Karte hinzufügen</translation>
<translation id="5088142053160410913">Nachricht an den Bediener</translation>
-<translation id="5089810972385038852">Bundesstaat</translation>
<translation id="5093232627742069661">Z-Faltung</translation>
<translation id="5094747076828555589">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat wird von Chromium als nicht vertrauenswürdig eingestuft. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der deine Verbindung abfängt.</translation>
-<translation id="5095208057601539847">Provinz</translation>
<translation id="5097099694988056070">Gerätestatistiken, z. B. CPU/RAM-Nutzung</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Website ist nicht sicher</translation>
@@ -1539,6 +1549,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="5171045022955879922">Suchen oder URL eingeben</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Computer</translation>
+<translation id="5177076414499237632">Informationen zu Quelle und Thema dieser Seite</translation>
<translation id="5179510805599951267">Nicht auf <ph name="ORIGINAL_LANGUAGE" />? Diesen Fehler melden</translation>
<translation id="518639307526414276">Haustiernahrung und -pflegemittel</translation>
<translation id="5190835502935405962">Lesezeichenleiste</translation>
@@ -1699,6 +1710,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="5624120631404540903">Passwörter verwalten</translation>
<translation id="5629630648637658800">Fehler beim Laden der Richtlinieneinstellungen</translation>
<translation id="5631439013527180824">Ungültiges Management-Token für das Gerät</translation>
+<translation id="5632485077360054581">Zeigen</translation>
<translation id="5633066919399395251">Zurzeit auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> befindliche Hacker könnten versuchen, gefährliche Programme auf deinem Computer zu installieren, um Daten wie Fotos, Passwörter, Nachrichten und Kreditkartendaten zu stehlen oder zu löschen. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Betrügerische Inhalte blockiert.</translation>
<translation id="5633259641094592098">Kultklassiker und Indie-Filme</translation>
@@ -1816,6 +1828,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="5989320800837274978">Weder feste Proxyserver noch eine PAC-Skript-URL sind festgelegt.</translation>
<translation id="5992691462791905444">Technische Z-Faltung</translation>
<translation id="5995727681868049093">Daten, Datenschutz- und Sicherheits­einstellungen in deinem Google-Konto verwalten</translation>
+<translation id="5997247540087773573">Das eingegebene Passwort wurde in einer Datenpanne gefunden. Zum besseren Schutz deiner Konten empfiehlt der Google Passwortmanager, das Passwort jetzt zu ändern und deine gespeicherten Passwörter zu prüfen.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> Ergebnisse für "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Klassisch</translation>
<translation id="6008122969617370890">N-zu-1-Reihenfolge</translation>
@@ -1911,7 +1924,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="627746635834430766">Damit Zahlungen zukünftig schneller abgewickelt werden können, speichere deine Kreditkartendaten und deine Rechnungsadresse in deinem Google-Konto.</translation>
<translation id="6279183038361895380">Zum Einblenden des Cursors |<ph name="ACCELERATOR" />| drücken</translation>
<translation id="6280223929691119688">Die Lieferadresse wird nicht unterstützt. Bitte wähle eine andere Adresse aus.</translation>
-<translation id="6282194474023008486">Postleitzahl</translation>
<translation id="6285507000506177184">Schaltfläche zum Verwalten von Downloads in Chrome – drücke die Eingabetaste, um in Chrome heruntergeladene Dateien zu verwalten</translation>
<translation id="6289939620939689042">Seitenfarbe</translation>
<translation id="6290238015253830360">Hier werden deine vorgeschlagenen Artikel angezeigt</translation>
@@ -1933,6 +1945,7 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="6337133576188860026">Es werden weniger als <ph name="SIZE" /> Speicherplatz freigegeben. Manche Websites werden beim nächsten Öffnen eventuell langsamer geladen.</translation>
<translation id="6337534724793800597">Richtlinien nach Name filtern</translation>
<translation id="6340739886198108203">Gemäß der Administratorrichtlinie wird nicht empfohlen, Screenshots oder Aufnahmen zu machen, wenn vertrauliche Inhalte zu sehen sind:</translation>
+<translation id="6348220984832452017">Aktive Variationen</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> installieren</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Keins}=1{1 Passwort (für <ph name="DOMAIN_LIST" />, synchronisiert)}=2{2 Passwörter (für <ph name="DOMAIN_LIST" />, synchronisiert)}other{# Passwörter (für <ph name="DOMAIN_LIST" />, synchronisiert)}}</translation>
<translation id="6355392890578844978">Dieser Browser wird nicht von einem Unternehmen oder einer anderen Organisation verwaltet. Aktivitäten auf diesem Gerät können außerhalb von Chromium verwaltet werden. <ph name="BEGIN_LINK" />Weitere Informationen<ph name="END_LINK" /></translation>
@@ -1964,7 +1977,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="643051589346665201">Google-Passwort ändern</translation>
<translation id="6433490469411711332">Kontaktdaten bearbeiten</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> hat die Verbindung abgelehnt.</translation>
-<translation id="6438025220577812695">Selbst ändern</translation>
<translation id="6440503408713884761">Ignoriert</translation>
<translation id="6443406338865242315">Welche Erweiterungen und Plug-ins du installiert hast</translation>
<translation id="6446163441502663861">Kahu (Umschlag)</translation>
@@ -2094,9 +2106,9 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="6828866289116430505">Genetik</translation>
<translation id="6831043979455480757">Ãœbersetzen</translation>
<translation id="6833752742582340615">Du kannst deine Karten- und Abrechnungsinformationen in deinem Google-Konto speichern, um schnell und sicher zu bezahlen</translation>
-<translation id="6839929833149231406">Region</translation>
<translation id="6846340164947227603">Virtuelle Kartennummer verwenden...</translation>
<translation id="6852204201400771460">App neu laden?</translation>
+<translation id="6857776781123259569">Passwörter verwalten…</translation>
<translation id="686485648936420384">Verbraucherressourcen</translation>
<translation id="6865412394715372076">Diese Karte kann momentan nicht geprüft werden</translation>
<translation id="6869334554832814367">Privatkredite</translation>
@@ -2145,7 +2157,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="6965978654500191972">Gerät</translation>
<translation id="696703987787944103">Perzeptiv</translation>
<translation id="6968269510885595029">Sicherheitsschlüssel verwenden</translation>
-<translation id="6970216967273061347">Bezirk</translation>
<translation id="6971439137020188025">Schnell eine neue Präsentation in Google Präsentationen erstellen</translation>
<translation id="6972629891077993081">HID-Geräte</translation>
<translation id="6973656660372572881">Sowohl feste Proxyserver als auch eine PAC-Skript-URL sind festgelegt.</translation>
@@ -2184,7 +2195,6 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<translation id="7081308185095828845">Diese Funktion ist auf deinem Gerät nicht verfügbar</translation>
<translation id="7083258188081898530">Fach 9</translation>
<translation id="7086090958708083563">Upload vom Nutzer angefordert</translation>
-<translation id="7087282848513945231">Landkreis</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" /> – drücke die Tabulatortaste und dann die Eingabetaste, um Berechtigungen und gespeicherte Daten von Websites in den Chrome-Einstellungen zu verwalten</translation>
<translation id="7096937462164235847">Die Identität dieser Website konnte nicht überprüft werden.</translation>
<translation id="7101893872976785596">Horrorfilme</translation>
@@ -2203,10 +2213,10 @@ Du wirst sonst gemäß deinen Datenschutzeinstellungen blockiert. Wenn Cookies u
<ph name="LIST_ITEM" />In Formulare eingegebene Informationen<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Der Versand an diese Adresse ist nicht möglich. Bitte wähle eine andere Adresse aus.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Dein Administrator hat das Kopieren dieser Daten untersagt.</translation>
<translation id="7135130955892390533">Status anzeigen</translation>
<translation id="7138472120740807366">Lieferoption</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Hauptfach</translation>
<translation id="714064300541049402">Seite 2 – X-Verschiebung des Bilds</translation>
<translation id="7152423860607593928">Number-14 (Umschlag)</translation>
@@ -2423,6 +2433,7 @@ Weitere Details:
<translation id="7669271284792375604">Unbefugte Dritte auf dieser Website versuchen eventuell, dich zur Installation von Programmen zu bewegen, die sich nachteilig auf deine Browsernutzung auswirken. Dabei kann zum Beispiel deine Startseite geändert werden oder es erscheinen zusätzliche Anzeigen auf von dir besuchten Websites.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Maßnahmen, die für Daten ergriffen werden, die als vertraulich gekennzeichnet sind (eine Maßnahme seit Anmeldung). <ph name="BEGIN_LINK" />Weitere Informationen<ph name="END_LINK" />}other{Maßnahmen, die für Daten ergriffen werden, die als vertraulich gekennzeichnet sind (# Maßnahmen seit Anmeldung). <ph name="BEGIN_LINK" />Weitere Informationen<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Ablage 6</translation>
+<translation id="7675325315208090829">Zahlungsmethoden verwalten…</translation>
<translation id="7676643023259824263">Nach Text aus Zwischenablage suchen: <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Browserverlauf in den Chrome-Einstellungen anzeigen und verwalten</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2465,7 +2476,6 @@ Weitere Details:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Gleiche Reihenfolge Vorderseite nach oben</translation>
-<translation id="777702478322588152">Präfektur</translation>
<translation id="7791011319128895129">Unveröffentlicht</translation>
<translation id="7791196057686275387">Bündel</translation>
<translation id="7791543448312431591">Hinzufügen</translation>
@@ -2556,12 +2566,12 @@ Weitere Details:
<translation id="8055534648776115597">Aus- und Weiterbildung</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" ist nicht ordnungsgemäß konfiguriert. Durch die Deinstallation von "<ph name="SOFTWARE_NAME" />" sollte das Problem behoben werden. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Nahrungsmittelindustrie</translation>
-<translation id="8066955247577885446">Ein Fehler ist aufgetreten.</translation>
<translation id="8067872629359326442">Du hast dein Passwort gerade auf einer verdächtigen Website eingegeben. Chromium kann dir helfen. Wenn du dein Passwort ändern und Google darüber informieren möchtest, dass dein Konto gefährdet sein könnte, klicke auf "Konto schützen".</translation>
<translation id="8070439594494267500">App-Symbol</translation>
<translation id="8074253406171541171">10x13 (Umschlag)</translation>
<translation id="8075736640322370409">Schnell eine neue Google-Tabelle erstellen</translation>
<translation id="8075898834294118863">Website-Einstellungen verwalten</translation>
+<translation id="8076492880354921740">Tabs</translation>
<translation id="8078141288243656252">Anmerkungen bei gedrehtem Dokument nicht möglich</translation>
<translation id="8079031581361219619">Website neu laden?</translation>
<translation id="8081087320434522107">Limousinen</translation>
@@ -2684,6 +2694,7 @@ Weitere Details:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" />, <ph name="SECOND_LABEL" />, <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Einstellungen</translation>
+<translation id="8428634594422941299">Ok</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" /> – drücke die Tabulatortaste und dann die Eingabetaste, um deine Cookie-Einstellungen in den Chrome-Einstellungen zu verwalten</translation>
<translation id="8433057134996913067">Dadurch wirst du von den meisten Websites abgemeldet.</translation>
<translation id="8434840396568290395">Haustiere</translation>
@@ -2782,6 +2793,7 @@ Weitere Details:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> ist dein Code für <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Lesezeichen für diesen Tab erstellen</translation>
<translation id="8751426954251315517">Bitte versuche es später noch einmal</translation>
+<translation id="8757526089434340176">Google Pay-Angebot verfügbar</translation>
<translation id="8758885506338294482">E-Sport</translation>
<translation id="8759274551635299824">Diese Karte ist abgelaufen</translation>
<translation id="87601671197631245">Diese Website nutzt eine veraltete Sicherheitskonfiguration. Hierdurch können Informationen wie etwa Passwörter, Nachrichten oder Kreditkartendaten, die an diese Website gesendet werden, in die Hände Unbefugter gelangen.</translation>
@@ -2789,6 +2801,7 @@ Weitere Details:
<translation id="8763927697961133303">USB-Gerät</translation>
<translation id="8763986294015493060">Alle geöffneten Inkognitofenster schließen</translation>
<translation id="8766943070169463815">Ansicht zum Authentifizieren der sicheren Anmeldedaten für Zahlungen ist geöffnet</translation>
+<translation id="8767765348545497220">Infofeld schließen</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorräder</translation>
<translation id="8790007591277257123">&amp;Löschen wiederholen</translation>
@@ -2801,6 +2814,7 @@ Weitere Details:
<translation id="8806285662264631610">Bade- und Körperpflegeprodukte</translation>
<translation id="8807160976559152894">Nach jeder Seite zuschneiden</translation>
<translation id="8808828119384186784">Chrome-Einstellungen</translation>
+<translation id="8813277370772331957">Später erinnern</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" /> – drücke die Tabulatortaste und dann die Eingabetaste, um Chrome über die Chrome-Einstellungen zu aktualisieren</translation>
<translation id="8820817407110198400">Lesezeichen</translation>
<translation id="882338992931677877">Manueller Einzug</translation>
@@ -2981,6 +2995,7 @@ Weitere Details:
<translation id="988159990683914416">Entwickler-Build</translation>
<translation id="989988560359834682">Adresse bearbeiten</translation>
<translation id="991413375315957741">Bewegungs- oder Lichtsensoren</translation>
+<translation id="992110854164447044">Wenn du eine virtuelle Karte verwendest, werden deine tatsächlichen Kartendetails nicht preisgegeben – dadurch bist du besser vor Betrugsversuchen geschützt. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" war nicht ordnungsgemäß auf deinem Computer oder in deinem Netzwerk installiert:
diff --git a/chromium/components/strings/components_strings_el.xtb b/chromium/components/strings/components_strings_el.xtb
index 3e5746c8a0f..ece8818860b 100644
--- a/chromium/components/strings/components_strings_el.xtb
+++ b/chromium/components/strings/components_strings_el.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ΕπιχοÏηγήσεις, υποτÏοφίες και οικονομική βοήθεια</translation>
<translation id="1048785276086539861">Όταν επεξεÏγάζεστε σχολιασμοÏÏ‚, αυτό το έγγÏαφο θα επιστÏέφει στην Ï€Ïοβολή μίας σελίδας.</translation>
<translation id="1050038467049342496">Κλείστε τις άλλες εφαÏμογές</translation>
+<translation id="1053959602163383901">Επιλέξατε να γίνεται επαλήθευση με συσκευή Ï€ÏογÏάμματος ελέγχου ταυτότητας σε ιστοτόπους που χÏησιμοποιοÏν τον πάÏοχο <ph name="PROVIDER_ORIGIN" />. Αυτός ο πάÏοχος μποÏεί να έχει αποθηκεÏσει πληÏοφοÏίες σχετικά με τον Ï„Ïόπο πληÏωμής σας, τις οποίες μποÏείτε να <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;ΑναίÏεση Ï€Ïοσθήκης</translation>
<translation id="1056663316309890343">Λογισμικό φωτογÏαφίας</translation>
<translation id="1056898198331236512">ΠÏοειδοποίηση</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">ΤÏόπος παÏαλαβής</translation>
<translation id="1281476433249504884">Μονάδα στοίβαξης 1</translation>
<translation id="1285320974508926690">Îα μην γίνεται ποτέ μετάφÏαση Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… ιστότοπου</translation>
+<translation id="1288548991597756084">Ασφαλής αποθήκευση κάÏτας</translation>
<translation id="1292571435393770077">ΤÏοφοδότης χαÏÏ„Î¹Î¿Ï 16</translation>
<translation id="1292701964462482250">"Κάποιο λογισμικό στον υπολογιστή σας παÏεμποδίζει την ασφαλή σÏνδεση του Chrome στον ιστό" (μόνο για υπολογιστές με Windows)</translation>
<translation id="1294154142200295408">ΠαÏαλλαγές γÏαμμής εντολών</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Για να διοÏθώσετε το σφάλμα, κάντε κλικ στο κουμπί &lt;strong&gt;ΣÏνδεση&lt;/strong&gt; στη σελίδα που Ï€Ïοσπαθείτε να ανοίξετε.&lt;/p&gt;</translation>
<translation id="1507780850870535225">ΔιαμόÏφωση υπαίθÏιων χώÏων</translation>
<translation id="1513706915089223971">Λίστα καταχωÏίσεων ιστοÏικοÏ</translation>
+<translation id="1516097932025103760">Θα κÏυπτογÏαφηθεί και θα αποθηκευτεί με ασφάλεια, ενώ το CVC δεν αποθηκεÏεται ποτέ.</translation>
<translation id="1517433312004943670">Απαιτείται αÏιθμός τηλεφώνου</translation>
<translation id="1519264250979466059">ΗμεÏομηνία κατασκευής</translation>
<translation id="1521159554480556801">ΚλωστοϋφαντουÏγικές τέχνες</translation>
@@ -272,7 +275,7 @@
<translation id="1634828734222219955">ΣÏνολο</translation>
<translation id="163669211644121865">ΠÏοετοιμασία και Ï€ÏογÏαμματισμός φόÏου</translation>
<translation id="1638780421120290329">Δεν είναι δυνατή η αποθήκευση της κάÏτας</translation>
-<translation id="1639239467298939599">Γίνεται φόÏτωση</translation>
+<translation id="1639239467298939599">ΦόÏτωση</translation>
<translation id="1640180200866533862">Πολιτικές χÏηστών</translation>
<translation id="1640244768702815859">Δοκιμάστε να <ph name="BEGIN_LINK" />επισκεφτείτε την αÏχική σελίδα του ιστότοπου<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">ΚαθυστέÏηση εξόδου έως</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Αποθήκευση και συμπλήÏωση Ï„Ïόπων πληÏωμής</translation>
<translation id="1663943134801823270">Οι κάÏτες και οι διευθÏνσεις Ï€ÏοέÏχονται από το Chrome. ΜποÏείτε να τις διαχειÏιστείτε στις <ph name="BEGIN_LINK" />Ρυθμίσεις<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Από εδώ και στο εξής, οι σελίδες στα <ph name="SOURCE_LANGUAGE" /> θα μεταφÏάζονται στα <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">ΟλοκλήÏωση αγοÏών με την κάÏτα <ph name="CARD_DETAIL" /> για χÏήση της Ï€ÏοσφοÏάς</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> Ï€Ïος <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ΠÏώτα η μικÏή πλευÏά</translation>
<translation id="168693727862418163">Αυτή η τιμή πολιτικής δεν επικυÏώθηκε με επιτυχία έναντι του σχήματός της και θα παÏαβλεφθεί.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">ΑισθητήÏες κίνησης</translation>
<translation id="1717494416764505390">ΓÏαμματοκιβώτιο 3</translation>
<translation id="1718029547804390981">Το έγγÏαφο είναι πάÏα Ï€Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿ για σχολιασμό.</translation>
+<translation id="1720941539803966190">Κλείσιμο οδηγοÏ</translation>
<translation id="1721424275792716183">* Το πεδίο είναι υποχÏεωτικό</translation>
<translation id="1727613060316725209">Το πιστοποιητικό είναι έγκυÏο</translation>
<translation id="1727741090716970331">ΠÏοσθήκη έγκυÏου αÏÎ¹Î¸Î¼Î¿Ï ÎºÎ¬Ïτας</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">ΜπάÏμπεκιου και ψητά</translation>
<translation id="2053111141626950936">Οι σελίδες στα <ph name="LANGUAGE" /> δεν θα μεταφÏάζονται.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Όταν αυτό το στοιχείο ελέγχου είναι ενεÏγοποιημένο και η κατάσταση είναι ενεÏγή, το Chrome καθοÏίζει τη μεγάλη ομάδα ατόμων ή "κοόÏτη" με την οποία μοιάζει πεÏισσότεÏο η πιο Ï€Ïόσφατη δÏαστηÏιότητα πεÏιήγησής σας. Οι διαφημιζόμενοι μποÏοÏν να επιλέξουν διαφημίσεις για την ομάδα και η δÏαστηÏιότητα πεÏιήγησής σας διατηÏείται ιδιωτική στη συσκευή σας. Η ομάδα σας ενημεÏώνεται καθημεÏινά.}=1{Όταν αυτό το στοιχείο ελέγχου είναι ενεÏγοποιημένο και η κατάσταση είναι ενεÏγή, το Chrome καθοÏίζει τη μεγάλη ομάδα ατόμων ή "κοόÏτη" με την οποία μοιάζει πεÏισσότεÏο η πιο Ï€Ïόσφατη δÏαστηÏιότητα πεÏιήγησής σας. Οι διαφημιζόμενοι μποÏοÏν να επιλέξουν διαφημίσεις για την ομάδα και η δÏαστηÏιότητα πεÏιήγησής σας διατηÏείται ιδιωτική στη συσκευή σας. Η ομάδα σας ενημεÏώνεται καθημεÏινά.}other{Όταν αυτό το στοιχείο ελέγχου είναι ενεÏγοποιημένο και η κατάσταση είναι ενεÏγή, το Chrome καθοÏίζει τη μεγάλη ομάδα ατόμων ή "κοόÏτη" με την οποία μοιάζει πεÏισσότεÏο η πιο Ï€Ïόσφατη δÏαστηÏιότητα πεÏιήγησής σας. Οι διαφημιζόμενοι μποÏοÏν να επιλέξουν διαφημίσεις για την ομάδα και η δÏαστηÏιότητα πεÏιήγησής σας διατηÏείται ιδιωτική στη συσκευή σας. Η ομάδα σας ενημεÏώνεται κάθε {NUM_DAYS} ημέÏες.}}</translation>
-<translation id="2053553514270667976">ΤαχυδÏομικός κώδικας</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 Ï€Ïόταση}other{# Ï€Ïοτάσεις}}</translation>
+<translation id="2066915425250589881">ζητήσετε να διαγÏαφοÏν</translation>
<translation id="2068528718802935086">Î’Ïέφη και νήπια</translation>
<translation id="2071156619270205202">Αυτή η κάÏτα δεν είναι κατάλληλη για έκδοση αÏÎ¹Î¸Î¼Î¿Ï ÎµÎ¹ÎºÎ¿Î½Î¹ÎºÎ®Ï‚ κάÏτας.</translation>
<translation id="2071692954027939183">Οι ειδοποιήσεις αποκλείστηκαν αυτόματα επειδή συνήθως δεν τις επιτÏέπετε</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">Κουμπί διαχείÏισης συγχÏονισμοÏ, πατήστε Enter για να διαχειÏιστείτε τις πληÏοφοÏίες που συγχÏονίζετε από τις Ïυθμίσεις του Chrome</translation>
<translation id="2091887806945687916">Ήχος</translation>
<translation id="2094505752054353250">Αναντιστοιχία τομέα</translation>
-<translation id="2096368010154057602">ΔιαμέÏισμα</translation>
<translation id="2099652385553570808">ΤÏιπλή συÏÏαφή αÏιστεÏά</translation>
<translation id="2101225219012730419">Έκδοση:</translation>
<translation id="2102134110707549001">ΠÏόταση για ισχυÏÏŒ κωδικό Ï€Ïόσβασης…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">ΑμεÏικανικό ποδόσφαιÏο</translation>
<translation id="2187317261103489799">Εντοπισμός (Ï€Ïοεπιλογή)</translation>
<translation id="2188375229972301266">Πολλαπλό Ï„ÏÏπημα στο κάτω μέÏος</translation>
-<translation id="2188852899391513400">Ο κωδικός Ï€Ïόσβασης που μόλις χÏησιμοποιήσατε εντοπίστηκε σε μια παÏαβίαση δεδομένων. Για την ασφάλεια των λογαÏιασμών σας, ο ΔιαχειÏιστής κωδικών Ï€Ïόσβασης της Google συνιστά να αλλάξετε άμεσα τον κωδικό Ï€Ïόσβασης και έπειτα να ελέγξετε τους αποθηκευμένους κωδικοÏÏ‚ Ï€Ïόσβασής σας.</translation>
<translation id="219906046732893612">Ανακαίνιση σπιτιοÏ</translation>
<translation id="2202020181578195191">Εισαγάγετε ένα έγκυÏο έτος λήξης</translation>
<translation id="22081806969704220">Δίσκος 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">ΕπεξεÏγασία αÏχείου</translation>
<translation id="2215963164070968490">Σκυλιά</translation>
<translation id="2218879909401188352">Οι εισβολείς στον ιστότοπο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> θα μποÏοÏσαν να εγκαταστήσουν επικίνδυνες εφαÏμογές που Ï€ÏοκαλοÏν ζημιά στη συσκευή σας, να Ï€Ïοσθέσουν κÏυφές χÏεώσεις στον λογαÏιασμό του ÎºÎ¹Î½Î·Ï„Î¿Ï ÏƒÎ±Ï‚ ή να κλέψουν τα Ï€Ïοσωπικά στοιχεία σας. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Επανεκκίνηση οδηγοÏ</translation>
+<translation id="2219735899272417925">Απαιτείται επαναφοÏά της συσκευής</translation>
<translation id="2224337661447660594">ΧωÏίς σÏνδεση στο διαδίκτυο</translation>
<translation id="2230458221926704099">ΕπιδιοÏθώστε τη σÏνδεσή σας χÏησιμοποιώντας την <ph name="BEGIN_LINK" />εφαÏμογή διαγνωστικών ελέγχων<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Αποστολή Ï„ÏŽÏα</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">Δεν επιτÏέπεται (Ï€Ïοεπιλογή)</translation>
<translation id="2512413427717747692">Κουμπί για τον οÏισμό του Chrome ως Ï€Ïοεπιλεγμένου Ï€ÏογÏάμματος πεÏιήγησης, πατήστε Enter για να οÏίσετε το Chrome ως το Ï€Ïοεπιλεγμένο Ï€ÏόγÏαμμα πεÏιήγησης του συστήματος στις Ïυθμίσεις του iOS</translation>
<translation id="2515629240566999685">Ελέγξτε το σήμα στην πεÏιοχή σας.</translation>
+<translation id="2515761554693942801">Επιλέξατε να γίνεται επαλήθευση με Touch ID σε ιστοτόπους που χÏησιμοποιοÏν τον πάÏοχο <ph name="PROVIDER_ORIGIN" />. Αυτός ο πάÏοχος μποÏεί να έχει αποθηκεÏσει πληÏοφοÏίες σχετικά με τον Ï„Ïόπο πληÏωμής σας, τις οποίες μποÏείτε να <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">ΣυÏÏαφή κάτω δεξιά</translation>
<translation id="2521736961081452453">ΔημιουÏγία φόÏμας</translation>
<translation id="2523886232349826891">ΑποθηκεÏτηκε μόνο σε αυτήν τη συσκευή</translation>
<translation id="2524461107774643265">ΠÏοσθήκη πεÏισσότεÏων πληÏοφοÏιών</translation>
<translation id="2529899080962247600">Αυτό το πεδίο δεν θα Ï€Ïέπει να έχει πεÏισσότεÏες από <ph name="MAX_ITEMS_LIMIT" /> καταχωÏίσεις. Τυχόν επιπλέον καταχωÏίσεις θα παÏαβλέπονται.</translation>
+<translation id="253493526287553278">ΠÏοβολή λεπτομεÏειών ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€ÏοσφοÏάς</translation>
<translation id="2535585790302968248">Ανοίξτε μια νέα καÏτέλα ανώνυμης πεÏιήγησης για ιδιωτική πεÏιήγηση</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{και 1 ακόμα}other{και # ακόμα}}</translation>
<translation id="2536110899380797252">ΠÏοσθήκη διεÏθυνσης</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">ΦωτογÏαφικές και ψηφιακές τέχνες</translation>
<translation id="2601150049980261779">Ρομαντικές ταινίες</translation>
<translation id="2604589665489080024">Μουσική ποπ</translation>
-<translation id="2609632851001447353">ΠαÏαλλαγές</translation>
<translation id="2610561535971892504">Κλικ για αντιγÏαφή</translation>
<translation id="2617988307566202237">Το Chrome <ph name="BEGIN_EMPHASIS" />δεν θα αποθηκεÏει<ph name="END_EMPHASIS" /> τις παÏακάτω πληÏοφοÏίες:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Γενέθλια και ονομαστικές εοÏτές</translation>
<translation id="2677748264148917807">ΑποχώÏηση</translation>
+<translation id="2679714844901977852">ΑποθηκεÏστε τις πληÏοφοÏίες της κάÏτας και τις πληÏοφοÏίες χÏέωσης στον ΛογαÏιασμό σας Google <ph name="USER_EMAIL" /> για ασφαλείς και πιο γÏήγοÏες ολοκληÏώσεις αγοÏών.</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Βέλτιστη Ï€ÏοσαÏμογή</translation>
<translation id="2688969097326701645">Îαι, συνέχεια</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">ΠάÏοχοι υπηÏεσιών διαδικτÏου (ISP)</translation>
<translation id="2856444702002559011">Οι εισβολείς ενδέχεται να Ï€Ïοσπαθήσουν να υποκλέψουν τα στοιχεία σας από τον ιστότοπο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (για παÏάδειγμα, κωδικοÏÏ‚ Ï€Ïόσβασης, μηνÏματα ή πιστωτικές κάÏτες). <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Αυτός ο ιστότοπος εμφανίζει παÏεμβατικές ή παÏαπλανητικές διαφημίσεις.</translation>
-<translation id="286512204874376891">Μια εικονική κάÏτα κÏÏβει την Ï€Ïαγματική κάÏτα, Ï€Ïοκειμένου να σας Ï€ÏοστατεÏει από πιθανή απάτη. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Φιλικό</translation>
<translation id="28761159517501904">Ταινίες</translation>
<translation id="2876489322757410363">Έξοδος από την κατάσταση ανώνυμης πεÏιήγησης για πληÏωμή μέσω εξωτεÏικής εφαÏμογής. Θέλετε να συνεχίστε;</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">Δίσκος</translation>
<translation id="3162559335345991374">Το Wi-Fi που χÏησιμοποιείτε ενδέχεται να σας ζητήσει να επισκεφτείτε τη σελίδα σÏνδεσής του.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Îήσος</translation>
<translation id="3176929007561373547">Ελέγξτε τις Ïυθμίσεις του διακομιστή μεσολάβησης ή επικοινωνήστε με το διαχειÏιστή του δικτÏου σας, για
να βεβαιωθείτε ότι ο διακομιστής μεσολάβησης λειτουÏγεί. Εάν δεν πιστεÏετε ότι
απαιτείται η χÏήση διακομιστή μεσολάβησης:
@@ -882,9 +887,6 @@
<translation id="3369192424181595722">Σφάλμα ÏολογιοÏ</translation>
<translation id="3369459162151165748">Ανταλλακτικά και εξαÏτήματα οχημάτων</translation>
<translation id="3371064404604898522">ΟÏισμός Chrome ως Ï€Ïοεπιλεγμένου Ï€ÏογÏάμματος πεÏιήγησης</translation>
-<translation id="3371076217486966826">Ο ιστότοπος <ph name="URL" /> θέλει:
- • Îα δημιουÏγήσει έναν Ï„Ïισδιάστατο χάÏτη του πεÏιβάλλοντα χώÏου σας και να παÏακολουθεί τη θέση της κάμεÏας.
- • Îα χÏησιμοποιήσει την κάμεÏά σας.</translation>
<translation id="337363190475750230">ΚατάÏγηση παÏοχής</translation>
<translation id="3375754925484257129">Εκτέλεση ελέγχου ασφαλείας Chrome</translation>
<translation id="3377144306166885718">Ο διακομιστής έχει παÏωχημένη έκδοση TLS.</translation>
@@ -901,6 +903,7 @@
<translation id="3399952811970034796">ΔιεÏθυνση παÏάδοσης</translation>
<translation id="3402261774528610252">Η σÏνδεση που χÏησιμοποιήθηκε για τη φόÏτωση Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… ιστοτόπου χÏησιμοποίησε το TLS 1.0 ή το TLS 1.1, τα οποία έχουν καταÏγηθεί και θα απενεÏγοποιηθοÏν στο μέλλον. Μετά την απενεÏγοποίησή τους, οι χÏήστες δεν θα μποÏοÏν να φοÏτώσουν αυτόν τον ιστότοπο. Θα Ï€Ïέπει να ενεÏγοποιηθεί η έκδοση TLS 1.2 ή μεταγενέστεÏη στον διακομιστή.</translation>
<translation id="3405664148539009465">ΠÏοσαÏμογή γÏαμματοσειÏών</translation>
+<translation id="3407789382767355356">σÏνδεση Ï„Ïίτου μέÏους</translation>
<translation id="3409896703495473338">ΔιαχείÏιση Ïυθμίσεων ασφάλειας</translation>
<translation id="3414952576877147120">Μέγεθος:</translation>
<translation id="3417660076059365994">Τα αÏχεία που ανεβάζετε ή επισυνάπτετε αποστέλλονται στο Google Cloud ή σε Ï„Ïίτα μέÏη για ανάλυση. Για παÏάδειγμα, ενδέχεται να σαÏώνονται για ευαίσθητα δεδομένα ή κακόβουλο λογισμικό.</translation>
@@ -933,6 +936,7 @@
<translation id="3477679029130949506">ΚαταχωÏίσεις ταινιών και ÏŽÏες Ï€Ïοβολής αιθουσών</translation>
<translation id="3479552764303398839">Όχι Ï„ÏŽÏα</translation>
<translation id="3484560055331845446">ΜποÏεί να χάσετε την Ï€Ïόσβαση στον ΛογαÏιασμό σας Google. Το Chrome συνιστά να αλλάξετε τον κωδικό Ï€Ïόσβασής σας Ï„ÏŽÏα. Θα σας ζητηθεί να συνδεθείτε.</translation>
+<translation id="3484861421501147767">ΥπενθÏμιση: Διατίθεται αποθηκευμένος κωδικός Ï€ÏοσφοÏάς</translation>
<translation id="3487845404393360112">Δίσκος 4</translation>
<translation id="3495081129428749620">ΕÏÏεση στη σελίδα
<ph name="PAGE_TITLE" /></translation>
@@ -1057,6 +1061,7 @@
<translation id="3810973564298564668">ΔιαχείÏιση</translation>
<translation id="3816482573645936981">Τιμή (αντικαταστάθηκε)</translation>
<translation id="382518646247711829">Εάν χÏησιμοποιείτε διακομιστή μεσολάβησης…</translation>
+<translation id="3826050100957962900">ΣÏνδεση Ï„Ïίτων μεÏών</translation>
<translation id="3827112369919217609">Απόλυτη</translation>
<translation id="3827666161959873541">Οικογενειακές ταινίες</translation>
<translation id="3828924085048779000">Δεν επιτÏέπεται να είναι κενή η φÏάση Ï€Ïόσβασης.</translation>
@@ -1069,9 +1074,9 @@
<translation id="3858027520442213535">ΕνημέÏωση ημεÏομηνίας και ÏŽÏας</translation>
<translation id="3858860766373142691">Όνομα</translation>
<translation id="3872834068356954457">Επιστήμες</translation>
+<translation id="3875783148670536197">Οδηγίες</translation>
<translation id="3881478300875776315">Εμφάνιση λιγότεÏων γÏαμμών</translation>
<translation id="3884278016824448484">ΑναγνωÏιστικό συσκευής που Ï€Ïοκαλεί διένεξη</translation>
-<translation id="3885155851504623709">ΕνοÏία</translation>
<translation id="388632593194507180">Εντοπίστηκε παÏακολοÏθηση</translation>
<translation id="3886948180919384617">Μονάδα στοίβαξης 3</translation>
<translation id="3890664840433101773">ΠÏοσθήκη διεÏθυνσης ηλεκτÏÎ¿Î½Î¹ÎºÎ¿Ï Ï„Î±Ï‡Ï…Î´Ïομείου</translation>
@@ -1101,9 +1106,11 @@
<translation id="3973234410852337861">Ο κεντÏικός υπολογιστής <ph name="HOST_NAME" /> είναι αποκλεισμένος</translation>
<translation id="3978338123949022456">ΛειτουÏγία αναζήτησης, πληκτÏολογήστε ένα εÏώτημα και πατήστε Enter για να αναζητήσετε με τη λέξη-κλειδί <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Πουλιά</translation>
+<translation id="3985750352229496475">ΔιαχείÏιση διευθÏνσεων…</translation>
<translation id="3986705137476756801">ΑπενεÏγοποίηση Ζωντανών υπότιτλων Ï€Ïος το παÏόν</translation>
<translation id="3987940399970879459">ΛιγότεÏα από 1 MB</translation>
<translation id="3990250421422698716">Μετατόπιση στοίβας</translation>
+<translation id="3992684624889376114">Σχετικά με αυτήν τη σελίδα</translation>
<translation id="3996311196211510766">Ο ιστότοπος <ph name="ORIGIN" /> ζήτησε να εφαÏμοστεί μια πολιτική Ï€Ïοέλευσης
για όλα τα αιτήματά του, αλλά δεν είναι δυνατή η εφαÏμογή της πολιτικής αυτήν τη στιγμή.</translation>
<translation id="4006465311664329701">ΤÏόποι πληÏωμής, Ï€ÏοσφοÏές και διευθÏνσεις μέσω Google Pay</translation>
@@ -1229,6 +1236,7 @@
<translation id="4305666528087210886">Δεν ήταν δυνατή η Ï€Ïόσβαση στο αÏχείο σας</translation>
<translation id="4306529830550717874">Αποθήκευση διεÏθυνσης;</translation>
<translation id="4306812610847412719">Ï€ÏόχειÏο</translation>
+<translation id="4310070645992025887">Αναζήτηση διαδÏομών σας</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Αποκλεισμός (Ï€Ïοεπιλογή)</translation>
<translation id="4314815835985389558">ΔιαχείÏιση συγχÏονισμοÏ</translation>
@@ -1279,6 +1287,7 @@
<translation id="4435702339979719576">ΤαχυδÏομική κάÏτα)</translation>
<translation id="443673843213245140">Η χÏήση ενός διακομιστή μεσολάβησης είναι απενεÏγοποιημένη, αλλά έχει καθοÏιστεί μια Ïητή διαμόÏφωση διακομιστή μεσολάβησης.</translation>
<translation id="4441832193888514600">Έγινε παÏάβλεψη επειδή η πολιτική μποÏεί να οÏιστεί μόνο ως πολιτική χÏήστη cloud.</translation>
+<translation id="4442470707340296952">ΚαÏτέλες Chrome</translation>
<translation id="4450893287417543264">Îα μην εμφανιστεί ξανά</translation>
<translation id="4451135742916150903">ΜποÏεί να ζητά να συνδεθεί σε συσκευές HID.</translation>
<translation id="4452328064229197696">Ο κωδικός Ï€Ïόσβασης που μόλις χÏησιμοποιήσατε εντοπίστηκε σε μια παÏαβίαση δεδομένων. Για την ασφάλεια των λογαÏιασμών σας, ο ΔιαχειÏιστής κωδικών Ï€Ïόσβασης της Google συνιστά να ελέγξετε τους αποθηκευμένους κωδικοÏÏ‚ Ï€Ïόσβασής σας.</translation>
@@ -1417,6 +1426,7 @@
<translation id="483241715238664915">ΕνεÏγοποίηση ειδοποιήσεων</translation>
<translation id="4834250788637067901">ΤÏόποι πληÏωμής, Ï€ÏοσφοÏές και διευθÏνσεις μέσω Google Pay</translation>
<translation id="4838327282952368871">ΟνειÏικό</translation>
+<translation id="4839087176073128681">ΠληÏώστε ταχÏτεÏα την επόμενη φοÏά και Ï€Ïοστατέψτε την κάÏτα σας με την κοÏυφαία ασφάλεια της Google.</translation>
<translation id="4840250757394056958">ΠÏοβολή του ιστοÏÎ¹ÎºÎ¿Ï Chrome</translation>
<translation id="484462545196658690">Αυτόματα</translation>
<translation id="484671803914931257">Λάβετε έκπτωση στο κατάστημα <ph name="MERCHANT_NAME" /> και πεÏισσότεÏα</translation>
@@ -1480,6 +1490,7 @@
<translation id="5011561501798487822">Εντοπίστηκε γλώσσα</translation>
<translation id="5015510746216210676">Όνομα συσκευής:</translation>
<translation id="5017554619425969104">Κείμενο που αντιγÏάψατε</translation>
+<translation id="5017828934289857214">ΥπενθÏμιση αÏγότεÏα</translation>
<translation id="5018422839182700155">Δεν είναι δυνατό το άνοιγμα αυτής της σελίδας</translation>
<translation id="5019198164206649151">Η αποθήκευση αντιγÏάφων ασφαλείας είναι σε κακή κατάσταση</translation>
<translation id="5020776957610079374">Μουσική του κόσμου</translation>
@@ -1499,6 +1510,7 @@
<translation id="5051305769747448211">Ζωντανή κωμωδία</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Για να στείλετε αυτό το αÏχείο χÏησιμοποιώντας την Κοινοποίηση κοντά, ελευθεÏώστε χώÏο (<ph name="DISK_SPACE_SIZE" />) στη συσκευή σας}other{Για να στείλετε αυτά τα αÏχεία χÏησιμοποιώντας την Κοινοποίηση κοντά, ελευθεÏώστε χώÏο (<ph name="DISK_SPACE_SIZE" />) στη συσκευή σας}}</translation>
<translation id="5056549851600133418">ΆÏθÏα για εσάς</translation>
+<translation id="5060483733937416656">Επιλέξατε να γίνεται επαλήθευση με το Windows Hello σε ιστοτόπους που χÏησιμοποιοÏν τον πάÏοχο <ph name="PROVIDER_ORIGIN" />. Αυτός ο πάÏοχος μποÏεί να έχει αποθηκεÏσει πληÏοφοÏίες σχετικά με τον Ï„Ïόπο πληÏωμής σας, τις οποίες μποÏείτε να <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Μήπως εννοοÏσατε <ph name="LOOKALIKE_DOMAIN" />;</translation>
<translation id="5066056036849835175">ΙστοÏικό εκτÏπωσης</translation>
<translation id="5068234115460527047">Κεφάλαια αντιστάθμισης κινδÏνου</translation>
@@ -1512,10 +1524,8 @@
<translation id="5087286274860437796">Το πιστοποιητικό του διακομιστή δεν είναι έγκυÏο αυτήν τη στιγμή.</translation>
<translation id="5087580092889165836">ΠÏοσθήκη κάÏτας</translation>
<translation id="5088142053160410913">Μήνυμα Ï€Ïος χειÏιστή</translation>
-<translation id="5089810972385038852">Πολιτεία</translation>
<translation id="5093232627742069661">Δίπλωση Z</translation>
<translation id="5094747076828555589">Ο διακομιστής δεν μποÏεί να αποδείξει ότι είναι το <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του δεν θεωÏείται έμπιστο από το Chromium. Αυτό μποÏεί να οφείλεται σε λανθασμένη ÏÏθμιση ή σε κάποιον Ï„Ïίτο που επιτίθεται στη σÏνδεσή σας.</translation>
-<translation id="5095208057601539847">ΕπαÏχία</translation>
<translation id="5097099694988056070">Στατιστικά στοιχεία συσκευής, όπως χÏήση CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Ο ιστότοπος δεν είναι ασφαλής</translation>
@@ -1553,6 +1563,7 @@
<translation id="5171045022955879922">Αναζήτηση ή πληκτÏολόγηση διεÏθυνσης URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Υπολογιστής</translation>
+<translation id="5177076414499237632">Μάθετε σχετικά με αυτήν την πηγή και το θέμα</translation>
<translation id="5179510805599951267">Δεν είναι στα <ph name="ORIGINAL_LANGUAGE" />; ΑναφέÏετε αυτό το σφάλμα</translation>
<translation id="518639307526414276">ΤÏοφή και είδη πεÏιποίησης κατοικίδιων ζώων</translation>
<translation id="5190835502935405962">ΓÏαμμή σελιδοδεικτών</translation>
@@ -1714,6 +1725,7 @@
<translation id="5624120631404540903">ΔιαχείÏιση κωδικών Ï€Ïόσβασης</translation>
<translation id="5629630648637658800">Αποτυχία φόÏτωσης Ïυθμίσεων πολιτικής</translation>
<translation id="5631439013527180824">Μη έγκυÏο διακÏιτικό διαχείÏισης συσκευής</translation>
+<translation id="5632485077360054581">Οδηγίες</translation>
<translation id="5633066919399395251">Οι εισβολείς που βÏίσκονται αυτήν τη στιγμή στον ιστότοπο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ενδέχεται να επιχειÏήσουν να εγκαταστήσουν επικίνδυνα Ï€ÏογÏάμματα στον υπολογιστή σας, τα οποία μποÏοÏν να υποκλέψουν ή να διαγÏάψουν τα δεδομένα σας (για παÏάδειγμα, φωτογÏαφίες, κωδικοÏÏ‚ Ï€Ïόσβασης, μηνÏματα και στοιχεία πιστωτικών καÏτών). <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Το παÏαπλανητικό πεÏιεχόμενο αποκλείστηκε.</translation>
<translation id="5633259641094592098">Ταινίες cult και indie</translation>
@@ -1831,6 +1843,7 @@
<translation id="5989320800837274978">Δεν Ï€ÏοσδιοÏίζονται οÏτε οι σταθεÏοί διακομιστές μεσολάβησης οÏτε μια διεÏθυνση URL σεναÏίου .pac.</translation>
<translation id="5992691462791905444">Δίπλωση Ζ τεχνικών εγγÏάφων</translation>
<translation id="5995727681868049093">ΔιαχειÏιστείτε τις πληÏοφοÏίες, το απόÏÏητο και την ασφάλεια στον ΛογαÏιασμό σας Google</translation>
+<translation id="5997247540087773573">Ο κωδικός Ï€Ïόσβασης που μόλις χÏησιμοποιήσατε εντοπίστηκε σε μια παÏαβίαση δεδομένων. Για την ασφάλεια των λογαÏιασμών σας, ο ΔιαχειÏιστής κωδικών Ï€Ïόσβασης Google συνιστά να αλλάξετε άμεσα τον κωδικό Ï€Ïόσβασης και να ελέγξετε τους αποθηκευμένους κωδικοÏÏ‚ Ï€Ïόσβασης.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> αποτελέσματα για την αναζήτηση "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Κλασικό</translation>
<translation id="6008122969617370890">ΣειÏά N Ï€Ïος 1</translation>
@@ -1926,7 +1939,6 @@
<translation id="627746635834430766">Για πιο γÏήγοÏες πληÏωμές, αποθηκεÏστε τα στοιχεία της κάÏτας και τη διεÏθυνση χÏέωσης στον ΛογαÏιασμό σας Google.</translation>
<translation id="6279183038361895380">Πιέστε |<ph name="ACCELERATOR" />| για να εμφανιστεί ο δÏομέας</translation>
<translation id="6280223929691119688">Δεν είναι δυνατή η παÏάδοση σε αυτήν τη διεÏθυνση. Επιλέξτε μια άλλη διεÏθυνση.</translation>
-<translation id="6282194474023008486">ΤαχυδÏομικός κώδικας</translation>
<translation id="6285507000506177184">Κουμπί ΔιαχείÏιση λήψεων στο Chrome, πατήστε το πλήκτÏο Enter για να διαχειÏιστείτε τα αÏχεία που έχετε κατεβάσει στο Chrome</translation>
<translation id="6289939620939689042">ΧÏώμα σελίδας</translation>
<translation id="6290238015253830360">Τα Ï€Ïοτεινόμενα άÏθÏα σας εμφανίζονται εδώ</translation>
@@ -1948,6 +1960,7 @@
<translation id="6337133576188860026">ΑπελευθεÏώνει λιγότεÏο από <ph name="SIZE" />. ΟÏισμένοι ιστότοποι μποÏεί να φοÏτωθοÏν πιο αÏγά κατά την επόμενη επίσκεψή σας.</translation>
<translation id="6337534724793800597">ΦιλτÏάÏισμα πολιτικών με βάση το όνομα</translation>
<translation id="6340739886198108203">Η πολιτική διαχειÏιστή δεν συνιστά τη λήψη στιγμιότυπων οθόνης ή εγγÏαφών όταν εμπιστευτικό πεÏιεχόμενο είναι οÏατό:</translation>
+<translation id="6348220984832452017">ΕνεÏγές παÏαλλαγές</translation>
<translation id="6349101878882523185">Εγκατάσταση εφαÏμογής <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Κανένας}=1{1 κωδικός Ï€Ïόσβασης (για <ph name="DOMAIN_LIST" />, συγχÏονισμένος)}=2{2 κωδικοί Ï€Ïόσβασης (για <ph name="DOMAIN_LIST" />, συγχÏονισμένοι)}other{# κωδικοί Ï€Ïόσβασης (για <ph name="DOMAIN_LIST" />, συγχÏονισμένοι)}}</translation>
<translation id="6355392890578844978">Αυτό το Ï€ÏόγÏαμμα πεÏιήγησης δεν είναι διαχειÏιζόμενο από κάποια εταιÏεία ή άλλον οÏγανισμό. Η διαχείÏιση της δÏαστηÏιότητας σε αυτήν τη συσκευή μποÏεί να Ï€Ïαγματοποιηθεί εκτός Chromium. <ph name="BEGIN_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LINK" /></translation>
@@ -1979,7 +1992,6 @@
<translation id="643051589346665201">Αλλαγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης Google</translation>
<translation id="6433490469411711332">ΕπεξεÏγασία στοιχείων επαφής</translation>
<translation id="6433595998831338502">Ο κεντÏικός υπολογιστής <ph name="HOST_NAME" /> απέÏÏιψε τη σÏνδεση.</translation>
-<translation id="6438025220577812695">Μη αυτόματη αλλαγή</translation>
<translation id="6440503408713884761">Αγνοείται</translation>
<translation id="6443406338865242315">Ποιες επεκτάσεις και Ï€Ïοσθήκες έχετε εγκαταστήσει</translation>
<translation id="6446163441502663861">Kahu (Φάκελος)</translation>
@@ -2109,9 +2121,9 @@
<translation id="6828866289116430505">Γενετική</translation>
<translation id="6831043979455480757">ΜετάφÏαση</translation>
<translation id="6833752742582340615">ΑποθηκεÏστε τις πληÏοφοÏίες της κάÏτας και τις πληÏοφοÏίες χÏέωσης στον ΛογαÏιασμό σας Google για ασφαλείς και πιο γÏήγοÏες ολοκληÏώσεις αγοÏών.</translation>
-<translation id="6839929833149231406">ΠεÏιοχή</translation>
<translation id="6846340164947227603">ΧÏήση αÏÎ¹Î¸Î¼Î¿Ï ÎµÎ¹ÎºÎ¿Î½Î¹ÎºÎ®Ï‚ κάÏτας...</translation>
<translation id="6852204201400771460">Επανάληψη φόÏτωσης εφαÏμογής;</translation>
+<translation id="6857776781123259569">ΔιαχείÏιση κωδικών Ï€Ïόσβασης…</translation>
<translation id="686485648936420384">ΠόÏοι καταναλωτών</translation>
<translation id="6865412394715372076">Δεν είναι δυνατή η επαλήθευση της κάÏτας αυτήν τη στιγμή</translation>
<translation id="6869334554832814367">ΠÏοσωπικά δάνεια</translation>
@@ -2160,7 +2172,6 @@
<translation id="6965978654500191972">Συσκευή</translation>
<translation id="696703987787944103">Αντιληπτική</translation>
<translation id="6968269510885595029">ΧÏήση του ÎºÎ»ÎµÎ¹Î´Î¹Î¿Ï Î±ÏƒÏ†Î±Î»ÎµÎ¯Î±Ï‚ σας</translation>
-<translation id="6970216967273061347">ΠεÏιφέÏεια</translation>
<translation id="6971439137020188025">ΓÏήγοÏη δημιουÏγία νέας παÏουσίασης Google στις ΠαÏουσιάσεις</translation>
<translation id="6972629891077993081">Συσκευές HID</translation>
<translation id="6973656660372572881">ΚαθοÏίζονται τόσο οι σταθεÏοί διακομιστές μεσολάβησης όσο και μια διεÏθυνση URL σεναÏίου .pac.</translation>
@@ -2199,7 +2210,6 @@
<translation id="7081308185095828845">Αυτή η λειτουÏγία δεν είναι διαθέσιμη στη συσκευή σας</translation>
<translation id="7083258188081898530">Δίσκος 9</translation>
<translation id="7086090958708083563">Ζητήθηκε μεταφόÏτωση από έναν χÏήστη.</translation>
-<translation id="7087282848513945231">Κομητεία</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, πατήστε Tab και μετά Enter για να διαχειÏιστείτε τις άδειες και τα δεδομένα που έχουν αποθηκευτεί σε ιστοτόπους από τις Ïυθμίσεις του Chrome</translation>
<translation id="7096937462164235847">Η ταυτότητα Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… ιστοτόπου δεν έχει επαληθευτεί.</translation>
<translation id="7101893872976785596">Ταινίες Ï„Ïόμου</translation>
@@ -2218,10 +2228,10 @@
<ph name="LIST_ITEM" />ΠληÏοφοÏίες που εισάγετε σε φόÏμες<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Δεν είναι δυνατή η αποστολή σε αυτήν τη διεÏθυνση. Επιλέξτε μια άλλη διεÏθυνση.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Ο διαχειÏιστής απαγοÏεÏει την αντιγÏαφή αυτών των δεδομένων.</translation>
<translation id="7135130955892390533">Εμφάνιση κατάστασης</translation>
<translation id="7138472120740807366">Μέθοδος Ï€Ïοβολής</translation>
-<translation id="7139724024395191329">ΕμιÏάτο</translation>
<translation id="7139892792842608322">ΚÏÏιος Ï„Ïοφοδότης χαÏτιοÏ</translation>
<translation id="714064300541049402">ΠλευÏά 2 μετατόπιση εικόνας στον άξονα X</translation>
<translation id="7152423860607593928">Number-14 (Φάκελος)</translation>
@@ -2438,6 +2448,7 @@
<translation id="7669271284792375604">Οι εισβολείς σε αυτόν τον ιστότοπο μποÏεί να επιχειÏήσουν να σας ξεγελάσουν, έτσι ώστε να εγκαταστήσετε Ï€ÏογÏάμματα που βλάπτουν την εμπειÏία πεÏιήγησής σας (για παÏάδειγμα, αλλάζοντας την αÏχική σελίδα σας ή εμφανίζοντας επιπλέον διαφημίσεις στους ιστότοπους που επισκέπτεστε).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ΕνέÏγειες που εκτελέστηκαν σε δεδομένα που έχουν επισημανθεί ως εμπιστευτικά (1 ενέÏγεια από τη στιγμή της σÏνδεσης). <ph name="BEGIN_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LINK" />}other{ΕνέÏγειες που εκτελέστηκαν σε δεδομένα που έχουν επισημανθεί ως εμπιστευτικά (# ενέÏγειες από τη στιγμή της σÏνδεσης). <ph name="BEGIN_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ΓÏαμματοκιβώτιο 6</translation>
+<translation id="7675325315208090829">ΔιαχείÏιση Ï„Ïόπων πληÏωμής…</translation>
<translation id="7676643023259824263">Αναζήτηση κειμένου σε Ï€ÏόχειÏο, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">ΠÏοβολή και διαχείÏιση του ιστοÏÎ¹ÎºÎ¿Ï Ï€ÎµÏιήγησής σας στις Ïυθμίσεις του Chrome</translation>
<translation id="7679947978757153706">Μπέιζμπολ</translation>
@@ -2480,7 +2491,6 @@
<translation id="7766518757692125295">Πλαίσιο</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Ίδια σειÏά με την Ï€Ïόσοψη Ï€Ïος τα επάνω</translation>
-<translation id="777702478322588152">Îομός</translation>
<translation id="7791011319128895129">Δεν έχει κυκλοφοÏήσει</translation>
<translation id="7791196057686275387">Δέμα</translation>
<translation id="7791543448312431591">ΠÏοσθήκη</translation>
@@ -2571,12 +2581,12 @@
<translation id="8055534648776115597">Τεχνική και διά βίου εκπαίδευση</translation>
<translation id="8057711352706143257">Το λογισμικό "<ph name="SOFTWARE_NAME" />" δεν έχει διαμοÏφωθεί σωστά. Το Ï€Ïόβλημα διοÏθώνεται συνήθως με την απεγκατάσταση του Î»Î¿Î³Î¹ÏƒÎ¼Î¹ÎºÎ¿Ï "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ΠαÏαγωγή Ï„Ïοφίμων</translation>
-<translation id="8066955247577885446">Δυστυχώς, παÏουσιάστηκε κάποιο Ï€Ïόβλημα.</translation>
<translation id="8067872629359326442">Μόλις καταχωÏίσατε τον κωδικό Ï€Ïόσβασής σας σε έναν παÏαπλανητικό ιστότοπο. Το Chromium μποÏεί να βοηθήσει. Για να αλλάξετε τον κωδικό Ï€Ïόσβασής σας και να ενημεÏώσετε την Google ότι ο λογαÏιασμός σας μποÏεί να κινδυνεÏει, κάντε κλικ στην επιλογή ΠÏοστασία λογαÏιασμοÏ.</translation>
<translation id="8070439594494267500">Εικονίδιο εφαÏμογής</translation>
<translation id="8074253406171541171">10x13 (Φάκελος)</translation>
<translation id="8075736640322370409">ΓÏήγοÏη δημιουÏγία νέου ΦÏλλου Google</translation>
<translation id="8075898834294118863">ΔιαχείÏιση Ïυθμίσεων ιστοτόπων</translation>
+<translation id="8076492880354921740">ΚαÏτέλες</translation>
<translation id="8078141288243656252">Δεν είναι δυνατός ο σχολιασμός μετά από πεÏιστÏοφή</translation>
<translation id="8079031581361219619">Îα επαναληφθεί η φόÏτωση του ιστοτόπου;</translation>
<translation id="8081087320434522107">Αυτοκίνητα Ï„Ïπου σεντάν</translation>
@@ -2700,6 +2710,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ρυθμίσεις</translation>
+<translation id="8428634594422941299">Το κατάλαβα</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, πατήστε Tab και μετά Enter για να διαχειÏιστείτε τις Ï€Ïοτιμήσεις σας για τα cookie από τις Ïυθμίσεις του Chrome</translation>
<translation id="8433057134996913067">Αυτή η ενέÏγεια θα σας αποσυνδέσει από τους πεÏισσότεÏους ιστότοπους.</translation>
<translation id="8434840396568290395">Κατοικίδια</translation>
@@ -2799,6 +2810,7 @@
<translation id="8742371904523228557">Το <ph name="ONE_TIME_CODE" /> είναι ο κωδικός σας για το <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">ΠÏοσθήκη αυτής της καÏτέλας στους σελιδοδείκτες</translation>
<translation id="8751426954251315517">Δοκιμάστε ξανά την επόμενη φοÏά</translation>
+<translation id="8757526089434340176">Διαθέσιμη Ï€ÏοσφοÏά Google Pay</translation>
<translation id="8758885506338294482">Διαγωνισμοί βιντεοπαιχνιδιών</translation>
<translation id="8759274551635299824">Αυτή η κάÏτα έχει λήξει</translation>
<translation id="87601671197631245">Αυτός ο ιστότοπος χÏησιμοποιεί μια παÏωχημένη διαμόÏφωση ασφαλείας, η οποία μποÏεί να εκθέσει τις πληÏοφοÏίες σας (για παÏάδειγμα, τους κωδικοÏÏ‚ Ï€Ïόσβασης, τα μηνÏματα ή τα στοιχεία των πιστωτικών σας καÏτών) κατά την αποστολή τους σε αυτόν τον ιστότοπο.</translation>
@@ -2806,6 +2818,7 @@
<translation id="8763927697961133303">Συσκευή USB</translation>
<translation id="8763986294015493060">Κλείστε όλα τα παÏάθυÏα για ανώνυμη πεÏιήγηση που είναι αυτήν τη στιγμή ανοικτά</translation>
<translation id="8766943070169463815">Το φÏλλο ελέγχου ταυτότητας διαπιστευτηÏίων για ασφαλείς πληÏωμές είναι ανοιχτό</translation>
+<translation id="8767765348545497220">Κλείστε το συννεφάκι βοήθειας</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Μοτοσικλέτες</translation>
<translation id="8790007591277257123">&amp;Επανάληψη διαγÏαφής</translation>
@@ -2818,6 +2831,7 @@
<translation id="8806285662264631610">ΠÏοϊόντα μπάνιου και σώματος</translation>
<translation id="8807160976559152894">ΠεÏικοπή μετά από κάθε σελίδα</translation>
<translation id="8808828119384186784">Ρυθμίσεις του Chrome</translation>
+<translation id="8813277370772331957">ΥπενθÏμιση αÏγότεÏα</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, πατήστε Tab και έπειτα Enter για να ενημεÏώσετε το Chrome από τις Ïυθμίσεις του Chrome.</translation>
<translation id="8820817407110198400">Σελιδοδείκτες</translation>
<translation id="882338992931677877">ΧειÏοκίνητη υποδοχή</translation>
@@ -2997,6 +3011,7 @@
<translation id="988159990683914416">Έκδοση Ï€ÏογÏαμματιστή</translation>
<translation id="989988560359834682">ΕπεξεÏγασία διεÏθυνσης</translation>
<translation id="991413375315957741">αισθητήÏες κίνησης ή φωτός</translation>
+<translation id="992110854164447044">Μια εικονική κάÏτα κÏÏβει την Ï€Ïαγματική κάÏτα, Ï€Ïοκειμένου να σας Ï€ÏοστατεÏει από πιθανή απάτη. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Ροζ</translation>
<translation id="992432478773561401">Το λογισμικό "<ph name="SOFTWARE_NAME" />" δεν εγκαταστάθηκε σωστά στον υπολογιστή ή στο δίκτυό σας:
diff --git a/chromium/components/strings/components_strings_en-GB.xtb b/chromium/components/strings/components_strings_en-GB.xtb
index b15db9d648a..335d4599214 100644
--- a/chromium/components/strings/components_strings_en-GB.xtb
+++ b/chromium/components/strings/components_strings_en-GB.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Grants, scholarships and financial aid</translation>
<translation id="1048785276086539861">When you edit annotations, this document will return to single-page view</translation>
<translation id="1050038467049342496">Close other apps</translation>
+<translation id="1053959602163383901">You chose to verify with an authenticator device on websites that use <ph name="PROVIDER_ORIGIN" />. This provider may have stored information about your payment method, which you can <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Undo add</translation>
<translation id="1056663316309890343">Photo software</translation>
<translation id="1056898198331236512">Warning</translation>
@@ -119,6 +120,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="1270502636509132238">Pickup Method</translation>
<translation id="1281476433249504884">Stacker 1</translation>
<translation id="1285320974508926690">Never translate this site</translation>
+<translation id="1288548991597756084">Save card securely</translation>
<translation id="1292571435393770077">Tray 16</translation>
<translation id="1292701964462482250">'Software on your computer is stopping Chrome from safely connecting to the web' (Windows computers only)</translation>
<translation id="1294154142200295408">Command-line variations</translation>
@@ -223,6 +225,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
&lt;p&gt;To fix the error, click &lt;strong&gt;Connect&lt;/strong&gt; on the page that you're trying to open.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landscape design</translation>
<translation id="1513706915089223971">List of history entries</translation>
+<translation id="1516097932025103760">It’ll be encrypted, saved securely and the CVC is never stored.</translation>
<translation id="1517433312004943670">Phone number required</translation>
<translation id="1519264250979466059">Build Date</translation>
<translation id="1521159554480556801">Fibre and textile arts</translation>
@@ -288,6 +291,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="1662550410081243962">Save and fill payment methods</translation>
<translation id="1663943134801823270">Cards and addresses are from Chrome. You can manage them in <ph name="BEGIN_LINK" />Settings<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Pages in <ph name="SOURCE_LANGUAGE" /> will be translated to <ph name="TARGET_LANGUAGE" /> from now on.</translation>
+<translation id="1673886523110456987">Check out with <ph name="CARD_DETAIL" /> to use offer</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> to <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Short edge first</translation>
<translation id="168693727862418163">This policy value failed to validate against its schema and will be ignored.</translation>
@@ -306,6 +310,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="1717218214683051432">Motion sensors</translation>
<translation id="1717494416764505390">Mailbox 3</translation>
<translation id="1718029547804390981">Document is too large to be annotated</translation>
+<translation id="1720941539803966190">Close tutorial</translation>
<translation id="1721424275792716183">* Field is required</translation>
<translation id="1727613060316725209">Certificate is valid</translation>
<translation id="1727741090716970331">Add Valid Card Number</translation>
@@ -421,8 +426,8 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="205212645995975601">BBQ and grilling</translation>
<translation id="2053111141626950936">Pages in <ph name="LANGUAGE" /> will not be translated.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{When this control is on and the status is active, Chrome determines which large group of people, or 'cohort', your recent browsing activity is most similar to. Advertisers can select ads for the group, and your browsing activity is kept private on your device. Your group is updated every day.}=1{When this control is on and the status is active, Chrome determines which large group of people, or 'cohort', your recent browsing activity is most similar to. Advertisers can select ads for the group, and your browsing activity is kept private on your device. Your group is updated every day.}other{When this control is on and the status is active, Chrome determines which large group of people, or 'cohort', your recent browsing activity is most similar to. Advertisers can select ads for the group, and your browsing activity is kept private on your device. Your group is updated every {NUM_DAYS} days.}}</translation>
-<translation id="2053553514270667976">Postcode</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggestion}other{# suggestions}}</translation>
+<translation id="2066915425250589881">request to be deleted</translation>
<translation id="2068528718802935086">Babies and toddlers</translation>
<translation id="2071156619270205202">This card is not eligible for virtual card number.</translation>
<translation id="2071692954027939183">Notifications were automatically blocked because you usually don't allow them</translation>
@@ -434,7 +439,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="2088086323192747268">Manage sync button, press Enter to manage what info you sync in Chrome settings</translation>
<translation id="2091887806945687916">Sound</translation>
<translation id="2094505752054353250">Domain mismatch</translation>
-<translation id="2096368010154057602">Department</translation>
<translation id="2099652385553570808">Triple staple left</translation>
<translation id="2101225219012730419">Version:</translation>
<translation id="2102134110707549001">Suggest strong password…</translation>
@@ -471,7 +475,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="2185836064961771414">American football</translation>
<translation id="2187317261103489799">Detect (default)</translation>
<translation id="2188375229972301266">Multiple punch bottom</translation>
-<translation id="2188852899391513400">The password that you just used was found in a data breach. To secure your accounts, Google Password Manager recommends changing it now, and then checking your saved passwords.</translation>
<translation id="219906046732893612">Home improvement</translation>
<translation id="2202020181578195191">Enter a valid expiry year</translation>
<translation id="22081806969704220">Tray 3</translation>
@@ -482,6 +485,8 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="2215727959747642672">File editing</translation>
<translation id="2215963164070968490">Dogs</translation>
<translation id="2218879909401188352">Attackers currently on <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> could install dangerous apps that damage your device, add hidden charges to your mobile bill or steal your personal information. <ph name="BEGIN_LEARN_MORE_LINK" />Learn more<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Restart tutorial</translation>
+<translation id="2219735899272417925">Device reset required</translation>
<translation id="2224337661447660594">No Internet</translation>
<translation id="2230458221926704099">Fix your connection using the <ph name="BEGIN_LINK" />diagnostics app<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Send now</translation>
@@ -579,11 +584,13 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="2512101340618156538">Not allowed (default)</translation>
<translation id="2512413427717747692">Set Chrome as default browser button, press Enter to set Chrome as the system's default browser in iOS settings</translation>
<translation id="2515629240566999685">Checking the signal in your area</translation>
+<translation id="2515761554693942801">You chose to verify with Touch ID on websites that use <ph name="PROVIDER_ORIGIN" />. This provider may have stored information about your payment method, which you can <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Staple bottom right</translation>
<translation id="2521736961081452453">Create form</translation>
<translation id="2523886232349826891">Saved on this device only</translation>
<translation id="2524461107774643265">Add More Information</translation>
<translation id="2529899080962247600">This field should not have more than <ph name="MAX_ITEMS_LIMIT" /> entries. All further entries will be ignored.</translation>
+<translation id="253493526287553278">See promo code details</translation>
<translation id="2535585790302968248">Open a new incognito tab to browse privately</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{and 1 more}other{and # more}}</translation>
<translation id="2536110899380797252">Add Address</translation>
@@ -619,7 +626,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="259821504105826686">Photographic and digital arts</translation>
<translation id="2601150049980261779">Romance movies</translation>
<translation id="2604589665489080024">Pop music</translation>
-<translation id="2609632851001447353">Variations</translation>
<translation id="2610561535971892504">Click to copy</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />won’t save<ph name="END_EMPHASIS" /> the following information:
<ph name="BEGIN_LIST" />
@@ -652,6 +658,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Birthdays and name days</translation>
<translation id="2677748264148917807">Leave</translation>
+<translation id="2679714844901977852">Save your card and billing info to your Google Account <ph name="USER_EMAIL" /> for secure and faster checkouts</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Best fit</translation>
<translation id="2688969097326701645">Yes, continue</translation>
@@ -700,7 +707,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="2854764410992194509">Internet Service Providers (ISPs)</translation>
<translation id="2856444702002559011">Attackers might be trying to steal your information from <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (for example, passwords, messages or credit cards). <ph name="BEGIN_LEARN_MORE_LINK" />Learn more<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">This site shows intrusive or misleading ads.</translation>
-<translation id="286512204874376891">A virtual card disguises your actual card to help protect you from potential fraud. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Friendly</translation>
<translation id="28761159517501904">Films</translation>
<translation id="2876489322757410363">Leaving Incognito mode to pay via an external application. Continue?</translation>
@@ -801,7 +807,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="3158539265159265653">Disc</translation>
<translation id="3162559335345991374">The Wi-Fi that you are using may require you to visit its login page.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Island</translation>
<translation id="3176929007561373547">Check your proxy settings or contact your network administrator to
make sure that the proxy server is working. If you don't believe you should
be using a proxy server:
@@ -880,9 +885,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="3369192424181595722">Clock error</translation>
<translation id="3369459162151165748">Vehicle parts and accessories</translation>
<translation id="3371064404604898522">Set Chrome as the default browser</translation>
-<translation id="3371076217486966826"><ph name="URL" /> wants to:
- • Create a 3D map of your surroundings and track camera position
- • Use your camera</translation>
<translation id="337363190475750230">Deprovisioned</translation>
<translation id="3375754925484257129">Run Chrome safety check</translation>
<translation id="3377144306166885718">The server used an obsolete version of TLS.</translation>
@@ -899,6 +901,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="3399952811970034796">Delivery address</translation>
<translation id="3402261774528610252">The connection used to load this site used TLS 1.0 or TLS 1.1, which are deprecated and will be disabled in the future. Once disabled, users will be prevented from loading this site. The server should enable TLS 1.2 or later.</translation>
<translation id="3405664148539009465">Customise fonts</translation>
+<translation id="3407789382767355356">third-party sign-in</translation>
<translation id="3409896703495473338">Manage security settings</translation>
<translation id="3414952576877147120">Size:</translation>
<translation id="3417660076059365994">Files that you upload or attach are sent to Google Cloud or third parties for analysis. For example, they might be scanned for sensitive data or malware.</translation>
@@ -931,6 +934,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="3477679029130949506">Movie listings and theatre showtimes</translation>
<translation id="3479552764303398839">Not now</translation>
<translation id="3484560055331845446">You could lose access to your Google Account. Chrome recommends changing your password now. You'll be asked to sign in.</translation>
+<translation id="3484861421501147767">Reminder: Saved promo code available</translation>
<translation id="3487845404393360112">Tray 4</translation>
<translation id="3495081129428749620">Find in page
<ph name="PAGE_TITLE" /></translation>
@@ -1055,6 +1059,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="3810973564298564668">Manage</translation>
<translation id="3816482573645936981">Value (superseded)</translation>
<translation id="382518646247711829">If you use a proxy server...</translation>
+<translation id="3826050100957962900">Third-party sign-in</translation>
<translation id="3827112369919217609">Absolute</translation>
<translation id="3827666161959873541">Family movies</translation>
<translation id="3828924085048779000">Empty passphrase is not allowed.</translation>
@@ -1067,9 +1072,9 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="3858027520442213535">Update date and time</translation>
<translation id="3858860766373142691">Name</translation>
<translation id="3872834068356954457">Science</translation>
+<translation id="3875783148670536197">Show me how</translation>
<translation id="3881478300875776315">Show fewer lines</translation>
<translation id="3884278016824448484">Conflicting device identifier</translation>
-<translation id="3885155851504623709">Parish</translation>
<translation id="388632593194507180">Monitoring detected</translation>
<translation id="3886948180919384617">Stacker 3</translation>
<translation id="3890664840433101773">Add email</translation>
@@ -1099,9 +1104,11 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="3973234410852337861"><ph name="HOST_NAME" /> is blocked</translation>
<translation id="3978338123949022456">Search mode, type a query and press Enter to search with <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Birds</translation>
+<translation id="3985750352229496475">Manage Addresses…</translation>
<translation id="3986705137476756801">Turn off Live Caption for now</translation>
<translation id="3987940399970879459">Less than 1 MB</translation>
<translation id="3990250421422698716">Jog offset</translation>
+<translation id="3992684624889376114">About this page</translation>
<translation id="3996311196211510766">The site <ph name="ORIGIN" /> has requested that an origin policy will
apply to all requests to it, but this policy cannot presently be applied.</translation>
<translation id="4006465311664329701">Payment methods, offers and addresses using Google Pay</translation>
@@ -1226,6 +1233,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="4305666528087210886">Your file couldn’t be accessed</translation>
<translation id="4306529830550717874">Save address?</translation>
<translation id="4306812610847412719">clipboard</translation>
+<translation id="4310070645992025887">Search your journeys</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Block (default)</translation>
<translation id="4314815835985389558">Manage sync</translation>
@@ -1276,6 +1284,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Use of a proxy is disabled but an explicit proxy configuration is specified.</translation>
<translation id="4441832193888514600">Ignored because the policy can only be set as a cloud user policy.</translation>
+<translation id="4442470707340296952">Chrome tabs</translation>
<translation id="4450893287417543264">Don't show again</translation>
<translation id="4451135742916150903">Can ask to connect to HID devices</translation>
<translation id="4452328064229197696">The password that you just used was found in a data breach. To secure your accounts, Google Password Manager recommends checking your saved passwords.</translation>
@@ -1414,6 +1423,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="483241715238664915">Turn on warnings</translation>
<translation id="4834250788637067901">Payment methods, offers and addresses using Google Pay</translation>
<translation id="4838327282952368871">Dreamy</translation>
+<translation id="4839087176073128681">Pay faster next time and protect your card with Google’s industry-leading security.</translation>
<translation id="4840250757394056958">View your Chrome history</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">Get discount on <ph name="MERCHANT_NAME" /> and more</translation>
@@ -1476,6 +1486,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="5011561501798487822">Detected language</translation>
<translation id="5015510746216210676">Machine name:</translation>
<translation id="5017554619425969104">Text that you copied</translation>
+<translation id="5017828934289857214">Remind me later</translation>
<translation id="5018422839182700155">Can't open this page</translation>
<translation id="5019198164206649151">Backing store in bad state</translation>
<translation id="5020776957610079374">World music</translation>
@@ -1495,6 +1506,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="5051305769747448211">Live comedy</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{To send this file using Nearby Share, free up space (<ph name="DISK_SPACE_SIZE" />) on your device}other{To send these files using Nearby Share, free up space (<ph name="DISK_SPACE_SIZE" />) on your device}}</translation>
<translation id="5056549851600133418">Articles for you</translation>
+<translation id="5060483733937416656">You chose to verify with Windows Hello on websites that use <ph name="PROVIDER_ORIGIN" />. This provider may have stored information about your payment method, which you can <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Did you mean <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Printing history</translation>
<translation id="5068234115460527047">Hedge funds</translation>
@@ -1508,10 +1520,8 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="5087286274860437796">Server's certificate is not valid at this time.</translation>
<translation id="5087580092889165836">Add card</translation>
<translation id="5088142053160410913">Message to operator</translation>
-<translation id="5089810972385038852">State</translation>
<translation id="5093232627742069661">Z-fold</translation>
<translation id="5094747076828555589">This server could not prove that it is <ph name="DOMAIN" />; its security certificate is not trusted by Chromium. This may be caused by a misconfiguration or an attacker intercepting your connection.</translation>
-<translation id="5095208057601539847">Province</translation>
<translation id="5097099694988056070">Device statistics such as CPU/RAM usage</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Site is not secure</translation>
@@ -1549,6 +1559,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="5171045022955879922">Search or type URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Machine</translation>
+<translation id="5177076414499237632">Learn about this page's source and topic</translation>
<translation id="5179510805599951267">Not in <ph name="ORIGINAL_LANGUAGE" />? Report this error</translation>
<translation id="518639307526414276">Pet food and pet care supplies</translation>
<translation id="5190835502935405962">Bookmarks Bar</translation>
@@ -1709,6 +1720,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="5624120631404540903">Manage passwords</translation>
<translation id="5629630648637658800">Failed to load policy settings</translation>
<translation id="5631439013527180824">Invalid device management token</translation>
+<translation id="5632485077360054581">Show me how</translation>
<translation id="5633066919399395251">Attackers currently on <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> might attempt to install dangerous programs on your computer that steal or delete your information (for example, photos, passwords, messages and credit cards). <ph name="BEGIN_LEARN_MORE_LINK" />Learn more<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Deceptive content blocked.</translation>
<translation id="5633259641094592098">Cult and indie movies</translation>
@@ -1826,6 +1838,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="5989320800837274978">Neither fixed proxy servers nor a .pac script URL are specified.</translation>
<translation id="5992691462791905444">Engineering Z-fold</translation>
<translation id="5995727681868049093">Manage your info, privacy and security in your Google Account</translation>
+<translation id="5997247540087773573">The password that you just used was found in a data breach. To secure your accounts, Google Password Manager recommends changing it now and checking your saved passwords.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> results for '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">Classic</translation>
<translation id="6008122969617370890">N-to-1 order</translation>
@@ -1921,7 +1934,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="627746635834430766">To pay faster next time, save your card and billing address to your Google Account.</translation>
<translation id="6279183038361895380">Press |<ph name="ACCELERATOR" />| to show your cursor</translation>
<translation id="6280223929691119688">Can’t deliver to this address. Select a different address.</translation>
-<translation id="6282194474023008486">Postcode</translation>
<translation id="6285507000506177184">Manage downloads in Chrome button; press Enter to manage files that you have downloaded in Chrome</translation>
<translation id="6289939620939689042">Page colour</translation>
<translation id="6290238015253830360">Your suggested articles appear here</translation>
@@ -1943,6 +1955,7 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="6337133576188860026">Frees up less than <ph name="SIZE" />. Some sites may load more slowly on your next visit.</translation>
<translation id="6337534724793800597">Filter policies by name</translation>
<translation id="6340739886198108203">Administrator policy doesn’t recommend taking screenshots or recordings when confidential content is visible:</translation>
+<translation id="6348220984832452017">Active variations</translation>
<translation id="6349101878882523185">Install <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{None}=1{1 password (for <ph name="DOMAIN_LIST" />, synced)}=2{2 passwords (for <ph name="DOMAIN_LIST" />, synced)}other{# passwords (for <ph name="DOMAIN_LIST" />, synced)}}</translation>
<translation id="6355392890578844978">This browser is not managed by a company or other organisation. Activity on this device may be managed outside of Chromium. <ph name="BEGIN_LINK" />Learn more<ph name="END_LINK" /></translation>
@@ -1974,7 +1987,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="643051589346665201">Change Google password</translation>
<translation id="6433490469411711332">Edit contact info</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> refused to connect.</translation>
-<translation id="6438025220577812695">Change it myself</translation>
<translation id="6440503408713884761">Ignored</translation>
<translation id="6443406338865242315">Which extensions and plug-ins you have installed</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2104,9 +2116,9 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="6828866289116430505">Genetics</translation>
<translation id="6831043979455480757">Translate</translation>
<translation id="6833752742582340615">Save your card and billing info to your Google Account for secure and faster checkouts</translation>
-<translation id="6839929833149231406">Area</translation>
<translation id="6846340164947227603">Use a virtual card number…</translation>
<translation id="6852204201400771460">Reload app?</translation>
+<translation id="6857776781123259569">Manage Passwords…</translation>
<translation id="686485648936420384">Consumer resources</translation>
<translation id="6865412394715372076">This card can't be verified at the moment</translation>
<translation id="6869334554832814367">Personal loans</translation>
@@ -2155,7 +2167,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="6965978654500191972">Device</translation>
<translation id="696703987787944103">Perceptual</translation>
<translation id="6968269510885595029">Use your security key</translation>
-<translation id="6970216967273061347">District</translation>
<translation id="6971439137020188025">Create a new Google presentation in Slides quickly</translation>
<translation id="6972629891077993081">HID devices</translation>
<translation id="6973656660372572881">Both fixed proxy servers and a .pac script URL are specified.</translation>
@@ -2194,7 +2205,6 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<translation id="7081308185095828845">This feature is not available on your device</translation>
<translation id="7083258188081898530">Tray 9</translation>
<translation id="7086090958708083563">Upload requested by user</translation>
-<translation id="7087282848513945231">County</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, press Tab then Enter to manage permissions and data stored across sites in Chrome settings</translation>
<translation id="7096937462164235847">The identity of this website hasn't been verified.</translation>
<translation id="7101893872976785596">Horror movies</translation>
@@ -2213,10 +2223,10 @@ This will otherwise be blocked by your privacy settings. This will allow the con
<ph name="LIST_ITEM" />Information entered in forms<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Can’t deliver to this address. Select a different address.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Your admin has prohibited this data from being copied.</translation>
<translation id="7135130955892390533">Show status</translation>
<translation id="7138472120740807366">Delivery method</translation>
-<translation id="7139724024395191329">Emirate</translation>
<translation id="7139892792842608322">Primary tray</translation>
<translation id="714064300541049402">Side 2 image X shift</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2433,6 +2443,7 @@ Additional details:
<translation id="7669271284792375604">Attackers on this site might try to trick you into installing programs that harm your browsing experience (for example, by changing your homepage or showing extra ads on sites that you visit).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Actions taken with data flagged as confidential (1 action since login). <ph name="BEGIN_LINK" />Learn more<ph name="END_LINK" />}other{Actions taken with data flagged as confidential (# actions since login). <ph name="BEGIN_LINK" />Learn more<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Mailbox 6</translation>
+<translation id="7675325315208090829">Manage Payment Methods…</translation>
<translation id="7676643023259824263">Search for clipboard text, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">View and manage your browsing history in Chrome settings</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2475,7 +2486,6 @@ Additional details:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Same order face up</translation>
-<translation id="777702478322588152">Prefecture</translation>
<translation id="7791011319128895129">Unreleased</translation>
<translation id="7791196057686275387">Bale</translation>
<translation id="7791543448312431591">Add</translation>
@@ -2566,12 +2576,12 @@ Additional details:
<translation id="8055534648776115597">Vocational and continuing education</translation>
<translation id="8057711352706143257">'<ph name="SOFTWARE_NAME" />' isn’t configured correctly. Uninstalling '<ph name="SOFTWARE_NAME" />' usually fixes the problem. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Food production</translation>
-<translation id="8066955247577885446">Sorry, something went wrong.</translation>
<translation id="8067872629359326442">You just entered your password on a deceptive site. Chromium can help. To change your password and notify Google that your account may be at risk, click 'Protect account'.</translation>
<translation id="8070439594494267500">App icon</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Create a new Google Sheet quickly</translation>
<translation id="8075898834294118863">Manage site settings</translation>
+<translation id="8076492880354921740">Tabs</translation>
<translation id="8078141288243656252">Cannot annotate when rotated</translation>
<translation id="8079031581361219619">Reload site?</translation>
<translation id="8081087320434522107">Sedans</translation>
@@ -2694,6 +2704,7 @@ Additional details:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Settings</translation>
+<translation id="8428634594422941299">Got it</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, press Tab then Enter to manage your cookie preferences in Chrome settings</translation>
<translation id="8433057134996913067">This will sign you out of most websites.</translation>
<translation id="8434840396568290395">Pets</translation>
@@ -2791,6 +2802,7 @@ Additional details:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> is your code for <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Bookmark this tab</translation>
<translation id="8751426954251315517">Please try again next time</translation>
+<translation id="8757526089434340176">Google Pay offer available</translation>
<translation id="8758885506338294482">Competitive video gaming</translation>
<translation id="8759274551635299824">This card has expired</translation>
<translation id="87601671197631245">This site uses an outdated security configuration, which may expose your information (for example, passwords, messages or credit cards) when it is sent to this site.</translation>
@@ -2798,6 +2810,7 @@ Additional details:
<translation id="8763927697961133303">USB device</translation>
<translation id="8763986294015493060">Close all incognito windows that are currently open</translation>
<translation id="8766943070169463815">Secure payment credential authentication sheet is opened</translation>
+<translation id="8767765348545497220">Close help bubble</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorcycles</translation>
<translation id="8790007591277257123">&amp;Redo delete</translation>
@@ -2810,6 +2823,7 @@ Additional details:
<translation id="8806285662264631610">Bath and body products</translation>
<translation id="8807160976559152894">Trim after each page</translation>
<translation id="8808828119384186784">Chrome settings</translation>
+<translation id="8813277370772331957">Remind me later</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, press tab, then enter to update Chrome from your Chrome settings</translation>
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="882338992931677877">Manual slot</translation>
@@ -2989,6 +3003,7 @@ Additional details:
<translation id="988159990683914416">Developer Build</translation>
<translation id="989988560359834682">Edit Address</translation>
<translation id="991413375315957741">motion or light sensors</translation>
+<translation id="992110854164447044">A virtual card hides your actual card to help protect you from potential fraud. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Pink</translation>
<translation id="992432478773561401">'<ph name="SOFTWARE_NAME" />' wasn’t installed properly on your computer or the network:
diff --git a/chromium/components/strings/components_strings_es-419.xtb b/chromium/components/strings/components_strings_es-419.xtb
index a9fe104b72e..54de6050020 100644
--- a/chromium/components/strings/components_strings_es-419.xtb
+++ b/chromium/components/strings/components_strings_es-419.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Subsidios, becas y ayuda financiera</translation>
<translation id="1048785276086539861">Cuando edites anotaciones, se mostrará este documento en una vista de una sola página</translation>
<translation id="1050038467049342496">Cierra las demás apps.</translation>
+<translation id="1053959602163383901">Elegiste realizar la verificación con un dispositivo autenticador en sitios web que usan <ph name="PROVIDER_ORIGIN" />. Es posible que este proveedor haya almacenado información sobre tu forma de pago, la que puedes <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Deshacer Agregar</translation>
<translation id="1056663316309890343">Software de fotografía</translation>
<translation id="1056898198331236512">Advertencia</translation>
@@ -119,6 +120,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="1270502636509132238">Método de retiro</translation>
<translation id="1281476433249504884">Apilador 1</translation>
<translation id="1285320974508926690">Nunca traducir este sitio</translation>
+<translation id="1288548991597756084">Guarda tu tarjeta de forma segura</translation>
<translation id="1292571435393770077">Bandeja 16</translation>
<translation id="1292701964462482250">"Un software en tu computadora evita que Chrome se conecte de forma segura a la Web" (solo en computadoras con Windows)</translation>
<translation id="1294154142200295408">Variaciones en la línea de comandos</translation>
@@ -223,6 +225,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
&lt;p&gt;Para solucionar el error, haz clic en &lt;strong&gt;Conectar&lt;/strong&gt; en la página que deseas abrir.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Paisajismo</translation>
<translation id="1513706915089223971">Lista de entradas del historial</translation>
+<translation id="1516097932025103760">Se encriptará y guardará la tarjeta de forma segura, y no se almacenará el CVC.</translation>
<translation id="1517433312004943670">Se requiere el número de teléfono</translation>
<translation id="1519264250979466059">Fecha de compilación</translation>
<translation id="1521159554480556801">Arte textil y telas</translation>
@@ -288,6 +291,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="1662550410081243962">Guardar y completar formas de pago</translation>
<translation id="1663943134801823270">Las tarjetas y direcciones provienen de Chrome. Puedes administrarlas en <ph name="BEGIN_LINK" />Configuración<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">De ahora en más, las páginas en <ph name="SOURCE_LANGUAGE" /> se traducirán al <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Paga con <ph name="CARD_DETAIL" /> para usar la oferta</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> a <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Borde corto primero</translation>
<translation id="168693727862418163">No se pudo validar el valor de esta política con su esquema y, por lo tanto, se ignorará.</translation>
@@ -306,6 +310,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="1717218214683051432">Sensores de movimiento</translation>
<translation id="1717494416764505390">Buzón 3</translation>
<translation id="1718029547804390981">El documento es demasiado grande para contener anotaciones</translation>
+<translation id="1720941539803966190">Cerrar el instructivo</translation>
<translation id="1721424275792716183">* El campo es obligatorio</translation>
<translation id="1727613060316725209">El certificado es válido</translation>
<translation id="1727741090716970331">Agregar un número de tarjeta válido</translation>
@@ -418,8 +423,8 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="205212645995975601">Barbacoas y parrilladas</translation>
<translation id="2053111141626950936">No se traducirán las páginas en <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Si habilitas este control y su estado es "activado", Chrome determinará el grupo grande de personas, o "cohorte", al que más se asemeja tu actividad de navegación reciente. Los anunciantes podrán seleccionar los anuncios para el grupo, y tu actividad de navegación se mantendrá privada en tu dispositivo. Tu grupo se actualiza todos los días.}=1{Si habilitas este control y su estado es "activado", Chrome determinará el grupo grande de personas, o "cohorte", al que más se asemeja tu actividad de navegación reciente. Los anunciantes podrán seleccionar los anuncios para el grupo, y tu actividad de navegación se mantendrá privada en tu dispositivo. Tu grupo se actualiza todos los días.}other{Si habilitas este control y su estado es "activado", Chrome determinará el grupo grande de personas, o "cohorte", al que más se asemeja tu actividad de navegación reciente. Los anunciantes podrán seleccionar los anuncios para el grupo, y tu actividad de navegación se mantendrá privada en tu dispositivo. Tu grupo se actualiza cada {NUM_DAYS} días.}}</translation>
-<translation id="2053553514270667976">Código Postal</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 sugerencia}other{# sugerencias}}</translation>
+<translation id="2066915425250589881">solicitar que se borre</translation>
<translation id="2068528718802935086">Bebés y niños pequeños</translation>
<translation id="2071156619270205202">Esta tarjeta no admite un número de tarjeta virtual.</translation>
<translation id="2071692954027939183">Se bloquearon las notificaciones de forma automática porque no sueles permitirlas.</translation>
@@ -431,7 +436,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="2088086323192747268">Botón Administrar la sincronización: presiona Intro para administrar en la configuración de Chrome qué información sincronizar</translation>
<translation id="2091887806945687916">Sonido</translation>
<translation id="2094505752054353250">El dominio no coincide.</translation>
-<translation id="2096368010154057602">Departamento</translation>
<translation id="2099652385553570808">Grapas triples a la izquierda</translation>
<translation id="2101225219012730419">Versión:</translation>
<translation id="2102134110707549001">Sugerir contraseña segura…</translation>
@@ -468,7 +472,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="2185836064961771414">Fútbol americano</translation>
<translation id="2187317261103489799">Detectar (predeterminado)</translation>
<translation id="2188375229972301266">Perforaciones múltiples en la parte inferior</translation>
-<translation id="2188852899391513400">La contraseña que acabas de usar se encontró en una violación de la seguridad de los datos. A fin de asegurar tus cuentas, el Administrador de contraseñas de Google te recomienda cambiarla ahora y revisar las contraseñas guardadas más tarde.</translation>
<translation id="219906046732893612">Mejora del hogar</translation>
<translation id="2202020181578195191">Ingresa un año de vencimiento válido</translation>
<translation id="22081806969704220">Bandeja 3</translation>
@@ -479,6 +482,8 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="2215727959747642672">Edición de archivo</translation>
<translation id="2215963164070968490">Perros</translation>
<translation id="2218879909401188352">Es posible que los atacantes que se encuentran actualmente en <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> instalen apps peligrosas que dañen tu dispositivo, agreguen cargos ocultos en la factura de tu servicio móvil o roben tu información personal. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Reiniciar el tutorial</translation>
+<translation id="2219735899272417925">Se debe restablecer el dispositivo</translation>
<translation id="2224337661447660594">Sin Internet</translation>
<translation id="2230458221926704099">Corregir la conexión con la <ph name="BEGIN_LINK" />app de diagnóstico<ph name="END_LINK" />.</translation>
<translation id="2239100178324503013">Enviar ahora</translation>
@@ -576,11 +581,13 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="2512101340618156538">No se permite (predeterminado).</translation>
<translation id="2512413427717747692">Establece Chrome como el botón de navegador predeterminado, presiona Intro para establecer Chrome como el navegador predeterminado en la configuración de iOS</translation>
<translation id="2515629240566999685">Verificar la señal en tu área.</translation>
+<translation id="2515761554693942801">Elegiste realizar la verificación con Touch ID en sitios web que usan <ph name="PROVIDER_ORIGIN" />. Es posible que este proveedor haya almacenado información sobre tu forma de pago, la que puedes <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Grapa en la esquina inferior derecha</translation>
<translation id="2521736961081452453">Crear formulario</translation>
<translation id="2523886232349826891">Solo se guardará en este dispositivo</translation>
<translation id="2524461107774643265">Agregar más información</translation>
<translation id="2529899080962247600">Esta campo no debe tener más de <ph name="MAX_ITEMS_LIMIT" /> entradas. Se ignorarán todas las entradas posteriores.</translation>
+<translation id="253493526287553278">Ver los detalles del código promocional</translation>
<translation id="2535585790302968248">Abre una nueva pestaña de incógnito para navegar de forma privada</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{y 1 más}other{y # más}}</translation>
<translation id="2536110899380797252">Agregar dirección</translation>
@@ -616,7 +623,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="259821504105826686">Arte fotográfico y digital</translation>
<translation id="2601150049980261779">Películas románticas</translation>
<translation id="2604589665489080024">Música pop</translation>
-<translation id="2609632851001447353">Variaciones</translation>
<translation id="2610561535971892504">Haz clic para copiar.</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />no guardará<ph name="END_EMPHASIS" /> la siguiente información:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Cumpleaños y onomásticos</translation>
<translation id="2677748264148917807">Abandonar</translation>
+<translation id="2679714844901977852">Guarda tu tarjeta y tu información de facturación en tu Cuenta de Google <ph name="USER_EMAIL" /> para confirmar compras de manera segura y más rápida</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Ajustar tamaño</translation>
<translation id="2688969097326701645">Sí, continuar</translation>
@@ -697,7 +704,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="2854764410992194509">Proveedores de servicios de Internet (ISPs)</translation>
<translation id="2856444702002559011">Es posible que algunos atacantes intenten robar tu información de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (p. ej., contraseñas, mensajes o tarjetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Este sitio muestra anuncios intrusivos o engañosos.</translation>
-<translation id="286512204874376891">La tarjeta virtual oculta tu tarjeta real para protegerte contra posibles fraudes. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Friendly</translation>
<translation id="28761159517501904">Películas</translation>
<translation id="2876489322757410363">Saldrás del modo Incógnito para pagar mediante una aplicación externa. ¿Deseas continuar?</translation>
@@ -798,7 +804,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="3158539265159265653">Disco</translation>
<translation id="3162559335345991374">Es posible que la red Wi-Fi que estás usando requiera que visites la página de acceso.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Isla</translation>
<translation id="3176929007561373547">Comprueba la configuración del proxy o comunícate con tu
administrador de red para asegurarte de que el
servidor proxy esté funcionando. Si consideras
@@ -878,9 +883,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="3369192424181595722">Error de reloj</translation>
<translation id="3369459162151165748">Piezas y accesorios para vehículos</translation>
<translation id="3371064404604898522">Establecer Chrome como navegador predeterminado</translation>
-<translation id="3371076217486966826"><ph name="URL" /> quiere hacer lo siguiente:
- • Crear un mapa 3D de tu entorno y rastrear la posición de la cámara
- • Usar la cámara</translation>
<translation id="337363190475750230">Desaprovisionado</translation>
<translation id="3375754925484257129">Ejecutar la verificación de seguridad de Chrome</translation>
<translation id="3377144306166885718">El servidor usaba una versión obsoleta de TLS.</translation>
@@ -897,6 +899,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="3399952811970034796">Dirección de entrega</translation>
<translation id="3402261774528610252">La conexión que se usó para cargar el sitio implementa el protocolo TLS 1.0 o TLS 1.1, los cuales son obsoletos y se inhabilitarán en el futuro. Cuando esto ocurra, los usuarios no podrán cargar el sitio. El servidor debe habilitar el protocolo TLS 1.2 o versiones posteriores.</translation>
<translation id="3405664148539009465">Personalizar fuentes</translation>
+<translation id="3407789382767355356">acceso de terceros</translation>
<translation id="3409896703495473338">Administrar la configuración de seguridad</translation>
<translation id="3414952576877147120">Tamaño:</translation>
<translation id="3417660076059365994">Se envían los archivos que cargas o adjuntas a Google Cloud o a terceros para su análisis. Por ejemplo, es posible que se analicen para detectar datos sensibles o software malicioso.</translation>
@@ -929,6 +932,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="3477679029130949506">Carteleras y horarios de cine y teatro</translation>
<translation id="3479552764303398839">Ahora no</translation>
<translation id="3484560055331845446">Podrías perder el acceso a tu Cuenta de Google. Chrome te recomienda cambiar la contraseña ahora. Deberás acceder a la cuenta.</translation>
+<translation id="3484861421501147767">Recordatorio: Hay un código promocional guardado disponible</translation>
<translation id="3487845404393360112">Bandeja 4</translation>
<translation id="3495081129428749620">Buscar en la página
<ph name="PAGE_TITLE" /></translation>
@@ -1053,6 +1057,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="3810973564298564668">Administrar</translation>
<translation id="3816482573645936981">Valor (sustituido)</translation>
<translation id="382518646247711829">Si utilizas un servidor proxy...</translation>
+<translation id="3826050100957962900">Acceso de terceros</translation>
<translation id="3827112369919217609">Absoluto</translation>
<translation id="3827666161959873541">Películas familiares</translation>
<translation id="3828924085048779000">No se permite una frase de contraseña vacía.</translation>
@@ -1065,9 +1070,9 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="3858027520442213535">Actualizar fecha y hora</translation>
<translation id="3858860766373142691">Nombre</translation>
<translation id="3872834068356954457">Ciencia</translation>
+<translation id="3875783148670536197">Cómo hacerlo</translation>
<translation id="3881478300875776315">Mostrar menos líneas</translation>
<translation id="3884278016824448484">Hay un identificador de dispositivo en conflicto.</translation>
-<translation id="3885155851504623709">Distrito</translation>
<translation id="388632593194507180">Se detectó una supervisión</translation>
<translation id="3886948180919384617">Apilador 3</translation>
<translation id="3890664840433101773">Agregar correo electrónico</translation>
@@ -1097,9 +1102,11 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="3973234410852337861">Se bloqueó <ph name="HOST_NAME" /></translation>
<translation id="3978338123949022456">Modo de búsqueda, ingresa una búsqueda y presiona Intro para buscar con <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Aves</translation>
+<translation id="3985750352229496475">Administrar direcciones…</translation>
<translation id="3986705137476756801">Desactivar el Subtitulado instantáneo por el momento</translation>
<translation id="3987940399970879459">Menos de 1 MB</translation>
<translation id="3990250421422698716">Desplazamiento del borde</translation>
+<translation id="3992684624889376114">Acerca de esta página</translation>
<translation id="3996311196211510766">El sitio <ph name="ORIGIN" /> solicitó que se aplique una política de origen a todas las solicitudes. Sin embargo, no es posible en este momento.</translation>
<translation id="4006465311664329701">Formas de pago, ofertas y direcciones con Google Pay</translation>
<translation id="4009243425692662128">Se envía el contenido de las páginas que imprimes a Google Cloud o a terceros para su análisis. Por ejemplo, es posible que se analice para detectar datos sensibles.</translation>
@@ -1219,6 +1226,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="4305666528087210886">No se pudo acceder a tu archivo</translation>
<translation id="4306529830550717874">¿Quieres guardar la dirección?</translation>
<translation id="4306812610847412719">portapapeles</translation>
+<translation id="4310070645992025887">Buscar tus Exploraciones</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloquear (predeterminado)</translation>
<translation id="4314815835985389558">Administrar sincronización</translation>
@@ -1269,6 +1277,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Se inhabilitó el uso de un proxy, pero se especificó una configuración explícita de proxy.</translation>
<translation id="4441832193888514600">Se ignoró porque la política puede establecerse solo como política del usuario basada en la nube.</translation>
+<translation id="4442470707340296952">Pestañas de Chrome</translation>
<translation id="4450893287417543264">No volver a mostrar</translation>
<translation id="4451135742916150903">Puede solicitar permiso para conectarse a dispositivos HID</translation>
<translation id="4452328064229197696">La contraseña que acabas de usar se encontró en una violación de la seguridad de los datos. A fin de asegurar tu cuenta, el Administrador de contraseñas de Google te recomienda revisar las contraseñas guardadas.</translation>
@@ -1407,6 +1416,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="483241715238664915">Activar advertencias</translation>
<translation id="4834250788637067901">Formas de pago, ofertas y direcciones con Google Pay</translation>
<translation id="4838327282952368871">Dreamy</translation>
+<translation id="4839087176073128681">Paga más rápido la próxima vez y protege tu tarjeta con la seguridad líder de la industria de Google.</translation>
<translation id="4840250757394056958">Ver tu historial de Chrome</translation>
<translation id="484462545196658690">Automático</translation>
<translation id="484671803914931257">Obtén descuentos en <ph name="MERCHANT_NAME" /> y más</translation>
@@ -1469,6 +1479,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="5011561501798487822">Idioma detectado</translation>
<translation id="5015510746216210676">Nombre de la máquina:</translation>
<translation id="5017554619425969104">Texto que copiaste</translation>
+<translation id="5017828934289857214">Recordarme más tarde</translation>
<translation id="5018422839182700155">No se puede abrir esta página</translation>
<translation id="5019198164206649151">La memoria auxiliar se encuentra en mal estado.</translation>
<translation id="5020776957610079374">Música del mundo</translation>
@@ -1488,6 +1499,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="5051305769747448211">Comedia en vivo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Para enviar este archivo mediante Compartir con Nearby, libera espacio (<ph name="DISK_SPACE_SIZE" />) en el dispositivo.}other{Para enviar estos archivos mediante Compartir con Nearby, libera espacio (<ph name="DISK_SPACE_SIZE" />) en el dispositivo.}}</translation>
<translation id="5056549851600133418">Artículos para ti</translation>
+<translation id="5060483733937416656">Elegiste realizar la verificación con Windows Hello en sitios web que usan <ph name="PROVIDER_ORIGIN" />. Es posible que este proveedor haya almacenado información sobre tu forma de pago, la que puedes <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">¿Quisiste decir <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Historial de impresión</translation>
<translation id="5068234115460527047">Fondos de cobertura</translation>
@@ -1501,10 +1513,8 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="5087286274860437796">El certificado del servidor no es válido en este momento.</translation>
<translation id="5087580092889165836">Agregar tarjeta</translation>
<translation id="5088142053160410913">Mensaje para el operador</translation>
-<translation id="5089810972385038852">Estado</translation>
<translation id="5093232627742069661">Plegado en Z</translation>
<translation id="5094747076828555589">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; Chromium no confía en el certificado de seguridad. Es posible que esto se deba a una configuración incorrecta o a que un atacante interceptó la conexión.</translation>
-<translation id="5095208057601539847">Provincia</translation>
<translation id="5097099694988056070">Estadísticas del dispositivo, como el uso de CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">El sitio no es seguro</translation>
@@ -1542,6 +1552,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="5171045022955879922">Buscar o escribir URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Equipo</translation>
+<translation id="5177076414499237632">Obtén más información sobre la fuente y el tema de esta página</translation>
<translation id="5179510805599951267">¿No está en <ph name="ORIGINAL_LANGUAGE" />? Informa este error</translation>
<translation id="518639307526414276">Alimento y suministros para el cuidado de mascotas</translation>
<translation id="5190835502935405962">Barra de favoritos</translation>
@@ -1702,6 +1713,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="5624120631404540903">Administrar contraseñas</translation>
<translation id="5629630648637658800">Error al cargar la configuración de la política</translation>
<translation id="5631439013527180824">Token de administración de dispositivos no válido</translation>
+<translation id="5632485077360054581">Cómo hacerlo</translation>
<translation id="5633066919399395251">Es posible que los atacantes que actualmente se encuentran en <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> intenten instalar programas peligrosos en tu computadora para robar o borrar información (p. ej., fotos, contraseñas, mensajes y tarjetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Se bloqueó contenido engañoso.</translation>
<translation id="5633259641094592098">Películas independientes y de culto</translation>
@@ -1819,6 +1831,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="5989320800837274978">No se especifican servidores proxy fijos ni URL de secuencias de comandos .pac.</translation>
<translation id="5992691462791905444">Plegado en Z para ingeniería</translation>
<translation id="5995727681868049093">Administrar la información, privacidad y seguridad de tu Cuenta de Google</translation>
+<translation id="5997247540087773573">La contraseña que acabas de usar se encontró en una violación de la seguridad de los datos. A fin de proteger tus cuentas, el Administrador de contraseñas de Google te recomienda cambiarla ahora y revisar las contraseñas guardadas.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultados para "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Clásico</translation>
<translation id="6008122969617370890">Orden de N a 1</translation>
@@ -1914,7 +1927,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="627746635834430766">Para realizar pagos de forma más rápida la próxima vez, guarda tu tarjeta y dirección de facturación en tu Cuenta de Google.</translation>
<translation id="6279183038361895380">Presiona |<ph name="ACCELERATOR" />| para mostrar tu cursor</translation>
<translation id="6280223929691119688">La dirección de envío no es válida. Selecciona una dirección diferente.</translation>
-<translation id="6282194474023008486">Código postal</translation>
<translation id="6285507000506177184">Botón Administrar las descargas en Chrome: presiona Intro para administrar los archivos que has descargado en Chrome</translation>
<translation id="6289939620939689042">Color de la página</translation>
<translation id="6290238015253830360">Tus artículos sugeridos aparecen aquí</translation>
@@ -1936,6 +1948,7 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="6337133576188860026">Esta acción libera menos de <ph name="SIZE" />. Es posible que algunos sitios se carguen más lento en tu próxima visita.</translation>
<translation id="6337534724793800597">Filtrar políticas por nombre</translation>
<translation id="6340739886198108203">La política del administrador no recomienda grabar pantalla o tomar capturas cuando hay contenido confidencial visible:</translation>
+<translation id="6348220984832452017">Variaciones activas</translation>
<translation id="6349101878882523185">Instalar <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ninguna}=1{1 contraseña (para <ph name="DOMAIN_LIST" />, sincronizada)}=2{2 contraseñas (para <ph name="DOMAIN_LIST" />, sincronizadas)}other{# contraseñas (para <ph name="DOMAIN_LIST" />, sincronizadas)}}</translation>
<translation id="6355392890578844978">Este navegador no es administrado por una empresa ni por otra organización. Es posible que la actividad correspondiente a este dispositivo se administre fuera de Chromium. <ph name="BEGIN_LINK" />Más información<ph name="END_LINK" /></translation>
@@ -1967,7 +1980,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="643051589346665201">Cambiar la contraseña de Google</translation>
<translation id="6433490469411711332">Editar la información de contacto</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> rechazó la conexión.</translation>
-<translation id="6438025220577812695">Cambiarla por mi cuenta</translation>
<translation id="6440503408713884761">Ignorada</translation>
<translation id="6443406338865242315">Las extensiones y complementos que instalaste</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2097,9 +2109,9 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="6828866289116430505">Genética</translation>
<translation id="6831043979455480757">Traducir</translation>
<translation id="6833752742582340615">Guarda tu tarjeta y tu información de facturación en tu Cuenta de Google para confirmar compras de manera segura y más rápida</translation>
-<translation id="6839929833149231406">Ãrea</translation>
<translation id="6846340164947227603">Usar un número de tarjeta virtual…</translation>
<translation id="6852204201400771460">¿Deseas volver a cargar la app?</translation>
+<translation id="6857776781123259569">Administrar contraseñas…</translation>
<translation id="686485648936420384">Recursos del consumidor</translation>
<translation id="6865412394715372076">No se puede verificar la tarjeta en este momento</translation>
<translation id="6869334554832814367">Préstamos personales</translation>
@@ -2148,7 +2160,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="6965978654500191972">Dispositivo</translation>
<translation id="696703987787944103">Perceptual</translation>
<translation id="6968269510885595029">Usar tu llave de seguridad</translation>
-<translation id="6970216967273061347">Distrito</translation>
<translation id="6971439137020188025">Crea una presentación de Google nueva en Presentaciones rápidamente</translation>
<translation id="6972629891077993081">Dispositivos HID</translation>
<translation id="6973656660372572881">Se especifican servidores proxy fijos y URL de secuencias de comandos .pac.</translation>
@@ -2187,7 +2198,6 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<translation id="7081308185095828845">Esta función no está disponible en el dispositivo</translation>
<translation id="7083258188081898530">Bandeja 9</translation>
<translation id="7086090958708083563">Carga solicitada por el usuario</translation>
-<translation id="7087282848513945231">Condado</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />: presiona Tab y, luego, Intro para administrar en la configuración de Chrome los permisos y datos almacenados en los sitios</translation>
<translation id="7096937462164235847">No se verificó la identidad de este sitio web.</translation>
<translation id="7101893872976785596">Películas de terror</translation>
@@ -2206,10 +2216,10 @@ De lo contrario, la configuración de privacidad bloqueará esta acción. Esto p
<ph name="LIST_ITEM" />Información que ingreses en formularios<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">No se pueden realizar envíos a esa dirección. Selecciona una dirección diferente.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Tu administrador prohíbe que se copien estos datos.</translation>
<translation id="7135130955892390533">Mostrar estado</translation>
<translation id="7138472120740807366">Método de entrega</translation>
-<translation id="7139724024395191329">Emirato</translation>
<translation id="7139892792842608322">Bandeja principal</translation>
<translation id="714064300541049402">Cambio en el eje X del lado 2 de la imagen</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2426,6 +2436,7 @@ Detalles adicionales:
<translation id="7669271284792375604">Es posible que los atacantes de este sitio intenten engañarte para que instales programas que pueden afectar tu experiencia de navegación (p. ej., podrían cambiar la página principal o mostrar más anuncios en los sitios que visitas).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Las acciones que se realizan con los datos marcados como confidenciales (1 acción desde el acceso). <ph name="BEGIN_LINK" />Más información<ph name="END_LINK" />}other{Las acciones que se realizan con los datos marcados como confidenciales (# acciones desde el acceso). <ph name="BEGIN_LINK" />Más información<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Buzón 6</translation>
+<translation id="7675325315208090829">Administrar formas de pago…</translation>
<translation id="7676643023259824263">Buscar el texto <ph name="TEXT" /> en el portapapeles</translation>
<translation id="7679367271685653708">Ver y administrar tu historial de navegación en la configuración de Chrome</translation>
<translation id="7679947978757153706">Béisbol</translation>
@@ -2468,7 +2479,6 @@ Detalles adicionales:
<translation id="7766518757692125295">Contorno</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Mismo orden hacia arriba</translation>
-<translation id="777702478322588152">Prefectura</translation>
<translation id="7791011319128895129">No publicada</translation>
<translation id="7791196057686275387">Embalaje</translation>
<translation id="7791543448312431591">Agregar</translation>
@@ -2559,12 +2569,12 @@ Detalles adicionales:
<translation id="8055534648776115597">Enseñanza profesional y formación continua</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" no se configuró correctamente. Prueba desinstalar "<ph name="SOFTWARE_NAME" />" para corregir el problema. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Producción alimentaria</translation>
-<translation id="8066955247577885446">Se produjo un error.</translation>
<translation id="8067872629359326442">Ingresaste tu contraseña en un sitio engañoso. Chromium puede ayudarte. Para cambiar la contraseña y notificar a Google que tu cuenta podría estar en riesgo, haz clic en Proteger cuenta.</translation>
<translation id="8070439594494267500">Ãcono de la app</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Crea una hoja de cálculo de Google nueva rápidamente</translation>
<translation id="8075898834294118863">Administrar la configuración de sitios</translation>
+<translation id="8076492880354921740">Pestañas</translation>
<translation id="8078141288243656252">No se pueden realizar anotaciones cuando el documento está rotado</translation>
<translation id="8079031581361219619">¿Deseas volver a cargar el sitio?</translation>
<translation id="8081087320434522107">Vehículos tipo sedán</translation>
@@ -2687,6 +2697,7 @@ Detalles adicionales:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Configuración</translation>
+<translation id="8428634594422941299">Entendido</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />: presiona Tab y, luego, Intro para administrar tus preferencias de cookies en la configuración de Chrome</translation>
<translation id="8433057134996913067">Si realizas esta acción, saldrás de la mayoría de los sitios web.</translation>
<translation id="8434840396568290395">Mascotas</translation>
@@ -2785,6 +2796,7 @@ Detalles adicionales:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> es tu código para <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Agregar esta pestaña a favoritos</translation>
<translation id="8751426954251315517">Vuelve a intentarlo más tarde</translation>
+<translation id="8757526089434340176">Oferta disponible de Google Pay</translation>
<translation id="8758885506338294482">Videojuegos competitivos</translation>
<translation id="8759274551635299824">La tarjeta está vencida</translation>
<translation id="87601671197631245">Este sitio usa una configuración de seguridad obsoleta. Si envías información (p. ej., contraseñas, mensajes o tarjetas de crédito) a este sitio, es posible que quede expuesta.</translation>
@@ -2792,6 +2804,7 @@ Detalles adicionales:
<translation id="8763927697961133303">Dispositivo USB</translation>
<translation id="8763986294015493060">Cierra todas las ventanas de incógnito que estén abiertas</translation>
<translation id="8766943070169463815">Está abierta la hoja de autenticación de credenciales de pago seguro</translation>
+<translation id="8767765348545497220">Cerrar el cuadro de ayuda</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocicletas</translation>
<translation id="8790007591277257123">&amp;Rehacer Eliminar</translation>
@@ -2804,6 +2817,7 @@ Detalles adicionales:
<translation id="8806285662264631610">Productos para el baño y el cuerpo</translation>
<translation id="8807160976559152894">Cortar después de cada página</translation>
<translation id="8808828119384186784">Configuración de Chrome</translation>
+<translation id="8813277370772331957">Recordarme más tarde</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />: presiona Tab y, luego, Intro para actualizar Chrome desde la configuración del navegador</translation>
<translation id="8820817407110198400">Favoritos</translation>
<translation id="882338992931677877">Ranura manual</translation>
@@ -2983,6 +2997,7 @@ Detalles adicionales:
<translation id="988159990683914416">Build para desarrolladores</translation>
<translation id="989988560359834682">Modificar dirección</translation>
<translation id="991413375315957741">sensores de luz o movimiento</translation>
+<translation id="992110854164447044">La tarjeta virtual oculta tu tarjeta real para protegerte contra posibles fraudes. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" no se instaló correctamente en tu computadora o red:
diff --git a/chromium/components/strings/components_strings_es.xtb b/chromium/components/strings/components_strings_es.xtb
index d1c17e92616..b9dddd5f687 100644
--- a/chromium/components/strings/components_strings_es.xtb
+++ b/chromium/components/strings/components_strings_es.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Subvenciones, becas y ayudas económicas</translation>
<translation id="1048785276086539861">Cuando edites las anotaciones, este documento volverá a la vista de una única página</translation>
<translation id="1050038467049342496">Cierra otras aplicaciones</translation>
+<translation id="1053959602163383901">Has elegido realizar la verificación con un dispositivo de autenticación en sitios web que usan <ph name="PROVIDER_ORIGIN" />. Este proveedor puede haber almacenado información sobre tu método de pago, que puedes <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Deshacer acción de añadir</translation>
<translation id="1056663316309890343">Software para fotos</translation>
<translation id="1056898198331236512">Advertencia</translation>
@@ -119,6 +120,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="1270502636509132238">Método de recogida</translation>
<translation id="1281476433249504884">Apilador 1</translation>
<translation id="1285320974508926690">No traducir nunca este sitio</translation>
+<translation id="1288548991597756084">Guardar tarjeta de forma segura</translation>
<translation id="1292571435393770077">Bandeja 16</translation>
<translation id="1292701964462482250">"Hay software en tu ordenador que impide que Chrome se conecte a la Web de forma segura" (solo para ordenadores Windows)</translation>
<translation id="1294154142200295408">Variaciones de la línea de comandos</translation>
@@ -223,6 +225,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
&lt;p&gt;Para solucionar el problema, haz clic en &lt;strong&gt;Conectar&lt;/strong&gt; en la página que intentas abrir.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Paisajismo</translation>
<translation id="1513706915089223971">Lista de entradas del historial</translation>
+<translation id="1516097932025103760">Se cifrará y se guardará de forma segura, y el CVC nunca se almacenará.</translation>
<translation id="1517433312004943670">Número de teléfono requerido</translation>
<translation id="1519264250979466059">Fecha de compilación</translation>
<translation id="1521159554480556801">Arte textil y con fibras</translation>
@@ -288,6 +291,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="1662550410081243962">Guardar y autocompletar métodos de pago</translation>
<translation id="1663943134801823270">Las tarjetas y las direcciones proceden de Chrome. Puedes gestionarlas en <ph name="BEGIN_LINK" />Configuración<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">A partir de ahora, las páginas en <ph name="SOURCE_LANGUAGE" /> se traducirán al <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Paga con <ph name="CARD_DETAIL" /> para usar la oferta</translation>
<translation id="1674504678466460478">De <ph name="SOURCE_LANGUAGE" /> a <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Borde corto primero</translation>
<translation id="168693727862418163">No se ha podido validar el valor de esta política con su esquema y se ignorará.</translation>
@@ -306,6 +310,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="1717218214683051432">Sensores de movimiento</translation>
<translation id="1717494416764505390">Buzón de correo 3</translation>
<translation id="1718029547804390981">El documento es demasiado grande para poder añadir anotaciones</translation>
+<translation id="1720941539803966190">Cerrar tutorial</translation>
<translation id="1721424275792716183">* El campo es obligatorio</translation>
<translation id="1727613060316725209">El certificado es válido</translation>
<translation id="1727741090716970331">Añade un número de tarjeta válido</translation>
@@ -422,8 +427,8 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="205212645995975601">Barbacoas y parrilladas</translation>
<translation id="2053111141626950936">No se traducirán las páginas en <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Cuando el control está habilitado y en estado activo, Chrome determina qué grupo de personas o "cohorte" encaja mejor con tu actividad de navegación reciente. Los anunciantes pueden seleccionar anuncios para el grupo, y tu actividad de navegación se conservará en tu dispositivo y será privada. Tu grupo se actualiza todos los días.}=1{Cuando el control está habilitado y en estado activo, Chrome determina qué grupo de personas o "cohorte" encaja mejor con tu actividad de navegación reciente. Los anunciantes pueden seleccionar anuncios para el grupo, y tu actividad de navegación se conservará en tu dispositivo y será privada. Tu grupo se actualiza todos los días.}other{Cuando el control está habilitado y en estado activo, Chrome determina qué grupo de personas o "cohorte" encaja mejor con tu actividad de navegación reciente. Los anunciantes pueden seleccionar anuncios para el grupo, y tu actividad de navegación se conservará en tu dispositivo y será privada. Tu grupo se actualiza cada {NUM_DAYS} días.}}</translation>
-<translation id="2053553514270667976">Código postal</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{Una sugerencia}other{# sugerencias}}</translation>
+<translation id="2066915425250589881">solicitar que se elimine</translation>
<translation id="2068528718802935086">Bebés y niños pequeños</translation>
<translation id="2071156619270205202">Esta tarjeta no reúne los requisitos necesarios para un número de tarjeta virtual.</translation>
<translation id="2071692954027939183">Se han bloqueado automáticamente las notificaciones porque no las sueles permitir.</translation>
@@ -435,7 +440,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="2088086323192747268">Botón Gestionar sincronización, pulsa Intro para gestionar la información que sincronizas en la configuración de Chrome</translation>
<translation id="2091887806945687916">Sonido</translation>
<translation id="2094505752054353250">El dominio no coincide</translation>
-<translation id="2096368010154057602">Departamento</translation>
<translation id="2099652385553570808">Grapado triple en la parte izquierda</translation>
<translation id="2101225219012730419">Versión:</translation>
<translation id="2102134110707549001">Sugerir contraseña segura…</translation>
@@ -472,7 +476,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="2185836064961771414">Fútbol americano</translation>
<translation id="2187317261103489799">Detectar (predeterminado)</translation>
<translation id="2188375229972301266">Perforado múltiple en la parte inferior</translation>
-<translation id="2188852899391513400">La contraseña que acabas de usar se ha visto expuesta en una quiebra de seguridad de datos. Para proteger tus cuentas, el gestor de contraseñas de Google te recomienda que cambies esa contraseña ahora y que después compruebes las contraseñas que tengas guardadas.</translation>
<translation id="219906046732893612">Mejora del hogar</translation>
<translation id="2202020181578195191">Introduce un año de vencimiento válido</translation>
<translation id="22081806969704220">Bandeja 3</translation>
@@ -483,6 +486,8 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="2215727959747642672">Edición de archivos</translation>
<translation id="2215963164070968490">Perros</translation>
<translation id="2218879909401188352">Se ha detectado la presencia de atacantes en <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> que podrían instalar aplicaciones peligrosas que dañen tu dispositivo, añadir cargos ocultos a tu factura del móvil o robar tu información personal. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Reiniciar tutorial</translation>
+<translation id="2219735899272417925">Es necesario restablecer el dispositivo</translation>
<translation id="2224337661447660594">Sin conexión a Internet</translation>
<translation id="2230458221926704099">Soluciona los problemas de tu conexión con la <ph name="BEGIN_LINK" />aplicación de diagnóstico<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Enviar ahora</translation>
@@ -580,11 +585,13 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="2512101340618156538">No permitido (predeterminado)</translation>
<translation id="2512413427717747692">Botón para establecer Chrome como navegador predeterminado, pulsa Intro para establecer Chrome como navegador predeterminado del sistema en los ajustes de iOS</translation>
<translation id="2515629240566999685">Comprobar la señal en tu zona</translation>
+<translation id="2515761554693942801">Has elegido realizar la verificación con Touch ID en sitios web que usan <ph name="PROVIDER_ORIGIN" />. Este proveedor puede haber almacenado información sobre tu método de pago, que puedes <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Grapado en la parte inferior derecha</translation>
<translation id="2521736961081452453">Crear formulario</translation>
<translation id="2523886232349826891">Solo se guardará en este dispositivo</translation>
<translation id="2524461107774643265">Añade más información</translation>
<translation id="2529899080962247600">Este campo no debe tener más de <ph name="MAX_ITEMS_LIMIT" /> entradas. Se ignorarán las demás entradas.</translation>
+<translation id="253493526287553278">Ver detalles del código promocional</translation>
<translation id="2535585790302968248">Abre una pestaña de Incógnito nueva para navegar en privado</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{y 1 más}other{y # más}}</translation>
<translation id="2536110899380797252">Añadir dirección</translation>
@@ -620,7 +627,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="259821504105826686">Fotografía y arte digital</translation>
<translation id="2601150049980261779">Películas románticas</translation>
<translation id="2604589665489080024">Música pop</translation>
-<translation id="2609632851001447353">Variaciones</translation>
<translation id="2610561535971892504">Haz clic para copiar</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />no almacenará<ph name="END_EMPHASIS" /> la siguiente información:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Cumpleaños y santos</translation>
<translation id="2677748264148917807">Salir</translation>
+<translation id="2679714844901977852">Guarda tu tarjeta y tu información de facturación en tu cuenta de Google <ph name="USER_EMAIL" /> para tramitar las compras más rápido de forma segura</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Mejor ajuste</translation>
<translation id="2688969097326701645">Sí, continuar</translation>
@@ -701,7 +708,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="2854764410992194509">Proveedores de Internet</translation>
<translation id="2856444702002559011">Es posible que los atacantes estén intentando robar tu información de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (por ejemplo, contraseñas, mensajes o tarjetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Este sitio web muestra anuncios invasivos o engañosos.</translation>
-<translation id="286512204874376891">Una tarjeta virtual oculta tu tarjeta real para protegerte frente a posibles fraudes. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Amistosa</translation>
<translation id="28761159517501904">Películas</translation>
<translation id="2876489322757410363">Saldrás del modo Incógnito para pagar en una aplicación externa. ¿Quieres continuar?</translation>
@@ -802,7 +808,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="3158539265159265653">Disco</translation>
<translation id="3162559335345991374">La red Wi-Fi que estás utilizando puede requerir que accedas a su página de inicio de sesión.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Isla</translation>
<translation id="3176929007561373547">Comprueba la configuración del proxy o ponte en contacto con el administrador de red para
asegurarte de que el servidor proxy funcione correctamente. Si consideras que no necesitas utilizar
un servidor proxy, sigue estas instrucciones:
@@ -881,9 +886,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="3369192424181595722">Error del reloj</translation>
<translation id="3369459162151165748">Repuestos y accesorios para vehículos</translation>
<translation id="3371064404604898522">Establecer Chrome como navegador predeterminado</translation>
-<translation id="3371076217486966826"><ph name="URL" /> quiere hacer lo siguiente:
- • Crear un mapa 3D de tu entorno y hacer un seguimiento de la posición de la cámara.
- • Usar tu cámara.</translation>
<translation id="337363190475750230">Dado de baja</translation>
<translation id="3375754925484257129">Realizar comprobación de seguridad de Chrome</translation>
<translation id="3377144306166885718">El servidor utiliza una versión de TLS obsoleta.</translation>
@@ -900,6 +902,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="3399952811970034796">Dirección de entrega</translation>
<translation id="3402261774528610252">La conexión que se usa para cargar este sitio web utiliza TLS 1.0 o TLS 1.1, que están obsoletas y se inhabilitarán en el futuro. Cuando se inhabiliten, los usuarios no podrán cargar este sitio web. El servidor debería habilitar el uso de TLS 1.2 o versiones posteriores.</translation>
<translation id="3405664148539009465">Personalizar fuentes</translation>
+<translation id="3407789382767355356">inicio de sesión de terceros</translation>
<translation id="3409896703495473338">Gestionar ajustes de seguridad</translation>
<translation id="3414952576877147120">Tamaño:</translation>
<translation id="3417660076059365994">Los archivos que subas o adjuntes se enviarán a Google Cloud o a terceros para que se analicen. Por ejemplo, puede que se analicen para buscar datos sensibles o software malicioso.</translation>
@@ -932,6 +935,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="3477679029130949506">Cartelera y horarios de cines</translation>
<translation id="3479552764303398839">Ahora no</translation>
<translation id="3484560055331845446">Podrías perder el acceso a tu cuenta de Google. Chrome te recomienda que cambies la contraseña ahora. Se te pedirá que inicies sesión.</translation>
+<translation id="3484861421501147767">Recordatorio: código promocional guardado disponible</translation>
<translation id="3487845404393360112">Bandeja 4</translation>
<translation id="3495081129428749620">Buscar en la página
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="3810973564298564668">Gestionar</translation>
<translation id="3816482573645936981">Valor (sustituido)</translation>
<translation id="382518646247711829">Si utilizas un servidor proxy...</translation>
+<translation id="3826050100957962900">Inicio de sesión de terceros</translation>
<translation id="3827112369919217609">Absoluto</translation>
<translation id="3827666161959873541">Películas familiares</translation>
<translation id="3828924085048779000">La frase de contraseña no puede estar vacía.</translation>
@@ -1068,9 +1073,9 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="3858027520442213535">Actualizar fecha y hora</translation>
<translation id="3858860766373142691">Nombre</translation>
<translation id="3872834068356954457">Ciencia</translation>
+<translation id="3875783148670536197">Muéstrame cómo</translation>
<translation id="3881478300875776315">Mostrar menos líneas</translation>
<translation id="3884278016824448484">Identificador de dispositivo en conflicto</translation>
-<translation id="3885155851504623709">Municipio</translation>
<translation id="388632593194507180">Vigilancia detectada</translation>
<translation id="3886948180919384617">Apilador 3</translation>
<translation id="3890664840433101773">Añadir correo electrónico</translation>
@@ -1100,9 +1105,11 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="3973234410852337861">La página <ph name="HOST_NAME" /> está bloqueada</translation>
<translation id="3978338123949022456">Modo de búsqueda: escribe una consulta y pulsa Intro para buscar con <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Aves</translation>
+<translation id="3985750352229496475">Gestionar direcciones...</translation>
<translation id="3986705137476756801">Desactivar Subtítulos automáticos por el momento</translation>
<translation id="3987940399970879459">Menos de 1 MB</translation>
<translation id="3990250421422698716">Agrupar</translation>
+<translation id="3992684624889376114">Acerca de esta página</translation>
<translation id="3996311196211510766">El sitio web <ph name="ORIGIN" /> ha solicitado que se aplique una política de origen
a todas las solicitudes que reciba, pero esta política no se puede aplicar actualmente.</translation>
<translation id="4006465311664329701">Métodos de pago, ofertas y direcciones con Google Pay</translation>
@@ -1227,6 +1234,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="4305666528087210886">No se ha podido acceder al archivo</translation>
<translation id="4306529830550717874">¿Guardar dirección?</translation>
<translation id="4306812610847412719">portapapeles</translation>
+<translation id="4310070645992025887">Buscar tus recorridos</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloquear (predeterminado)</translation>
<translation id="4314815835985389558">Gestionar sincronización</translation>
@@ -1277,6 +1285,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Se ha inhabilitado el uso de un servidor proxy, pero se han especificado ajustes de proxy explícitos.</translation>
<translation id="4441832193888514600">Se ignora porque la política solo se puede establecer como una política de usuarios basada en la nube.</translation>
+<translation id="4442470707340296952">Pestañas de Chrome</translation>
<translation id="4450893287417543264">No volver a mostrar</translation>
<translation id="4451135742916150903">Puede solicitar permiso para conectarse a dispositivos HID</translation>
<translation id="4452328064229197696">La contraseña que acabas de usar se ha visto expuesta en una quiebra de seguridad de datos. Para proteger tus cuentas, el gestor de contraseñas de Google te recomienda que compruebes las contraseñas que tengas guardadas.</translation>
@@ -1415,6 +1424,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="483241715238664915">Activar advertencias</translation>
<translation id="4834250788637067901">Métodos de pago, ofertas y direcciones con Google Pay</translation>
<translation id="4838327282952368871">De ensueño</translation>
+<translation id="4839087176073128681">Paga más rápido la próxima vez y protege tu tarjeta con la seguridad de Google, uno de los servicios más utilizados del sector.</translation>
<translation id="4840250757394056958">Ver tu historial de Chrome</translation>
<translation id="484462545196658690">Automático</translation>
<translation id="484671803914931257">Obtén descuentos en <ph name="MERCHANT_NAME" /> y más</translation>
@@ -1477,6 +1487,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="5011561501798487822">Idioma detectado</translation>
<translation id="5015510746216210676">Nombre del equipo:</translation>
<translation id="5017554619425969104">Texto copiado</translation>
+<translation id="5017828934289857214">Recordar más tarde</translation>
<translation id="5018422839182700155">No se puede abrir esta página</translation>
<translation id="5019198164206649151">El almacén secundario está en mal estado.</translation>
<translation id="5020776957610079374">Música del mundo</translation>
@@ -1496,6 +1507,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="5051305769747448211">Comedia en directo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Para enviar este archivo usando Compartir con Nearby, libera espacio (<ph name="DISK_SPACE_SIZE" />) en tu dispositivo}other{Para enviar estos archivos usando Compartir con Nearby, libera espacio (<ph name="DISK_SPACE_SIZE" />) en tu dispositivo}}</translation>
<translation id="5056549851600133418">Artículos recomendados para ti</translation>
+<translation id="5060483733937416656">Has elegido realizar la verificación con Windows Hello en sitios web que usan <ph name="PROVIDER_ORIGIN" />. Este proveedor puede haber almacenado información sobre tu método de pago, que puedes <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">¿Querías decir <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Historial de impresión</translation>
<translation id="5068234115460527047">Fondos de cobertura</translation>
@@ -1509,10 +1521,8 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="5087286274860437796">El certificado del servidor no es válido en este momento.</translation>
<translation id="5087580092889165836">Añadir tarjeta</translation>
<translation id="5088142053160410913">Mensaje al operador</translation>
-<translation id="5089810972385038852">Estado/provincia</translation>
<translation id="5093232627742069661">Plegado en Z</translation>
<translation id="5094747076828555589">Este servidor no ha podido probar que su dominio es <ph name="DOMAIN" />, Chromium no confía en su certificado de seguridad. Este problema puede deberse a una configuración incorrecta o a que un atacante haya interceptado la conexión.</translation>
-<translation id="5095208057601539847">Provincia</translation>
<translation id="5097099694988056070">Estadísticas del dispositivo, como uso de CPU y RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">El sitio no es seguro</translation>
@@ -1550,6 +1560,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="5171045022955879922">Busca o escribe una URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Equipo</translation>
+<translation id="5177076414499237632">Consultar información sobre la fuente y el tema de esta página</translation>
<translation id="5179510805599951267">¿Esta página no está escrita en <ph name="ORIGINAL_LANGUAGE" />? Informa de este error.</translation>
<translation id="518639307526414276">Productos de alimentación y cuidado de mascotas</translation>
<translation id="5190835502935405962">Barra de marcadores</translation>
@@ -1710,6 +1721,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="5624120631404540903">Gestionar contraseñas</translation>
<translation id="5629630648637658800">Error al cargar la configuración de la política</translation>
<translation id="5631439013527180824">Token de administración de dispositivos no válido</translation>
+<translation id="5632485077360054581">Muéstrame cómo</translation>
<translation id="5633066919399395251">Es posible que los atacantes que se encuentren en <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> intenten instalar programas peligrosos en tu ordenador para robar o eliminar tu información (por ejemplo, fotos, contraseñas, mensajes y tarjetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Contenido engañoso bloqueado.</translation>
<translation id="5633259641094592098">Películas de culto e independientes</translation>
@@ -1827,6 +1839,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="5989320800837274978">No se han especificado servidores proxy fijos ni una URL de secuencia de comandos .pac.</translation>
<translation id="5992691462791905444">Plegado en Z asimétrico</translation>
<translation id="5995727681868049093">Gestiona tu información, privacidad y seguridad en tu cuenta de Google</translation>
+<translation id="5997247540087773573">La contraseña que acabas de usar se ha encontrado en una quiebra de seguridad de datos. Para proteger tus cuentas, el gestor de contraseñas de Google te recomienda que cambies esa contraseña ahora y que compruebes las contraseñas que tengas guardadas.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultados para "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Clásico</translation>
<translation id="6008122969617370890">Orden de N a 1</translation>
@@ -1922,7 +1935,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="627746635834430766">Para pagar más rápido la próxima vez, guarda tu tarjeta y tu dirección de facturación en tu cuenta de Google.</translation>
<translation id="6279183038361895380">Pulsa |<ph name="ACCELERATOR" />| para mostrar el cursor</translation>
<translation id="6280223929691119688">Los pedidos no se pueden entregar en esta dirección. Selecciona otra.</translation>
-<translation id="6282194474023008486">Código postal</translation>
<translation id="6285507000506177184">Botón Gestionar las descargas en Chrome: pulsa Intro para gestionar los archivos que has descargado en Chrome</translation>
<translation id="6289939620939689042">Color de la página</translation>
<translation id="6290238015253830360">Los artículos sugeridos aparecen aquí</translation>
@@ -1944,6 +1956,7 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="6337133576188860026">Libera menos de <ph name="SIZE" />. Algunos sitios pueden tardar más en cargarse la próxima vez que accedas a ellos.</translation>
<translation id="6337534724793800597">Filtrar políticas por nombre</translation>
<translation id="6340739886198108203">La política del administrador recomienda no hacer capturas de pantalla o grabaciones cuando se muestra contenido confidencial:</translation>
+<translation id="6348220984832452017">Variaciones activas</translation>
<translation id="6349101878882523185">Instalar <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ninguna}=1{1 contraseña de <ph name="DOMAIN_LIST" /> (sincronizada)}=2{2 contraseñas de <ph name="DOMAIN_LIST" /> (sincronizadas)}other{# contraseñas de <ph name="DOMAIN_LIST" /> (sincronizadas)}}</translation>
<translation id="6355392890578844978">Ninguna empresa u organización gestiona este navegador. Es posible que la actividad de este dispositivo se gestione fuera de Chromium. <ph name="BEGIN_LINK" />Más información<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="643051589346665201">Cambiar contraseña de Google</translation>
<translation id="6433490469411711332">Editar información de contacto</translation>
<translation id="6433595998831338502">La página <ph name="HOST_NAME" /> ha rechazado la conexión.</translation>
-<translation id="6438025220577812695">Cambiarla yo</translation>
<translation id="6440503408713884761">Ignorada</translation>
<translation id="6443406338865242315">Qué extensiones y complementos tienes instalados</translation>
<translation id="6446163441502663861">Kahu (sobre)</translation>
@@ -2105,9 +2117,9 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="6828866289116430505">Genética</translation>
<translation id="6831043979455480757">Traducir</translation>
<translation id="6833752742582340615">Guarda tu tarjeta y tu información de facturación en tu cuenta de Google para tramitar las compras más rápido</translation>
-<translation id="6839929833149231406">Ãrea</translation>
<translation id="6846340164947227603">Usar un número de tarjeta virtual...</translation>
<translation id="6852204201400771460">¿Quieres volver a cargar la aplicación?</translation>
+<translation id="6857776781123259569">Gestionar contraseñas...</translation>
<translation id="686485648936420384">Recursos para consumidores</translation>
<translation id="6865412394715372076">No se puede verificar la tarjeta en este momento</translation>
<translation id="6869334554832814367">Préstamos personales</translation>
@@ -2156,7 +2168,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="6965978654500191972">Dispositivo</translation>
<translation id="696703987787944103">Perceptual</translation>
<translation id="6968269510885595029">Usar tu llave de seguridad</translation>
-<translation id="6970216967273061347">Distrito</translation>
<translation id="6971439137020188025">Crea una nueva Presentación de Google rápidamente</translation>
<translation id="6972629891077993081">Dispositivos HID</translation>
<translation id="6973656660372572881">Se especifican tanto servidores proxy fijos como una URL de secuencia de comandos .pac.</translation>
@@ -2195,7 +2206,6 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<translation id="7081308185095828845">Esta función no está disponible en tu dispositivo</translation>
<translation id="7083258188081898530">Bandeja 9</translation>
<translation id="7086090958708083563">Subida solicitada por el usuario</translation>
-<translation id="7087282848513945231">Condado</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, pulsa Tabulador y luego Intro para gestionar permisos y datos almacenados en sitios en la configuración de Chrome</translation>
<translation id="7096937462164235847">La identidad de este sitio web no se ha verificado.</translation>
<translation id="7101893872976785596">Películas de terror</translation>
@@ -2214,10 +2224,10 @@ De lo contrario, lo impedirá tu configuración de privacidad. Permitirá que el
<ph name="LIST_ITEM" />Información introducida en formularios<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Los pedidos no se pueden enviar a esta dirección. Selecciona otra.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Tu administrador ha prohibido que se copien estos datos.</translation>
<translation id="7135130955892390533">Mostrar estado</translation>
<translation id="7138472120740807366">Método de entrega</translation>
-<translation id="7139724024395191329">Emirato</translation>
<translation id="7139892792842608322">Bandeja principal</translation>
<translation id="714064300541049402">Cara 2 del desplazamiento de la imagen en el eje X</translation>
<translation id="7152423860607593928">Number-14 (sobre)</translation>
@@ -2434,6 +2444,7 @@ Más información:
<translation id="7669271284792375604">Es posible que los atacantes que se encuentren en este sitio web intenten engañarte para que instales programas que empeoren tu experiencia de navegación (por ejemplo, que cambien tu página principal o muestren anuncios adicionales en los sitios a los que accedas).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Acciones realizadas con datos marcados como confidenciales (1 acción desde el inicio de sesión). <ph name="BEGIN_LINK" />Más información<ph name="END_LINK" />}other{Acciones realizadas con datos marcados como confidenciales (# acciones desde el inicio de sesión). <ph name="BEGIN_LINK" />Más información<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Buzón de correo 6</translation>
+<translation id="7675325315208090829">Gestionar métodos de pago...</translation>
<translation id="7676643023259824263">Buscar texto <ph name="TEXT" /> del portapapeles</translation>
<translation id="7679367271685653708">Consulta y gestiona tu historial de navegación desde la configuración de Chrome</translation>
<translation id="7679947978757153706">Béisbol</translation>
@@ -2476,7 +2487,6 @@ Más información:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Mismo orden boca arriba</translation>
-<translation id="777702478322588152">Prefectura</translation>
<translation id="7791011319128895129">Sin publicar</translation>
<translation id="7791196057686275387">Envolver</translation>
<translation id="7791543448312431591">Añadir</translation>
@@ -2567,12 +2577,12 @@ Más información:
<translation id="8055534648776115597">Formación profesional y continua</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" no se ha configurado correctamente. Normalmente, el problema se soluciona al desinstalar "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Producción alimentaria</translation>
-<translation id="8066955247577885446">Se ha producido un error.</translation>
<translation id="8067872629359326442">Acabas de introducir tu contraseña en un sitio web engañoso. Chromium puede ayudarte. Para cambiar tu contraseña y notificar a Google de que tu cuenta podría estar en peligro, haz clic en Proteger cuenta.</translation>
<translation id="8070439594494267500">Icono de la aplicación</translation>
<translation id="8074253406171541171">10x13 (sobre)</translation>
<translation id="8075736640322370409">Crea una nueva Hoja de cálculo de Google rápidamente</translation>
<translation id="8075898834294118863">Gestionar la configuración del sitio</translation>
+<translation id="8076492880354921740">Pestañas</translation>
<translation id="8078141288243656252">No se pueden añadir anotaciones a los documentos girados</translation>
<translation id="8079031581361219619">¿Quieres volver a cargar el sitio web?</translation>
<translation id="8081087320434522107">Sedanes</translation>
@@ -2695,6 +2705,7 @@ Más información:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Configuración</translation>
+<translation id="8428634594422941299">Entendido</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, pulsa Tabulador y luego Intro para gestionar tus preferencias de cookies en la configuración de Chrome</translation>
<translation id="8433057134996913067">Con esta opción, tu sesión se cerrará en la mayoría de sitios web.</translation>
<translation id="8434840396568290395">Mascotas</translation>
@@ -2792,6 +2803,7 @@ Más información:
<translation id="8742371904523228557">El código de <ph name="ORIGIN" /> es <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">Añadir esta pestaña a marcadores</translation>
<translation id="8751426954251315517">Vuelve a intentarlo más adelante</translation>
+<translation id="8757526089434340176">Oferta de Google Play disponible</translation>
<translation id="8758885506338294482">Videojuegos de competición</translation>
<translation id="8759274551635299824">La tarjeta ha caducado</translation>
<translation id="87601671197631245">Este sitio web usa una configuración de seguridad obsoleta y puede que exponga tu información (por ejemplo, las contraseñas, los mensajes o las tarjetas de crédito) cuando se envíe a este sitio web.</translation>
@@ -2799,6 +2811,7 @@ Más información:
<translation id="8763927697961133303">Dispositivo USB</translation>
<translation id="8763986294015493060">Cierra todas las ventanas de Incógnito que estén abiertas</translation>
<translation id="8766943070169463815">La hoja de autenticación de credenciales de pago seguras está abierta</translation>
+<translation id="8767765348545497220">Cerrar cuadro de ayuda</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocicletas</translation>
<translation id="8790007591277257123">&amp;Rehacer eliminación</translation>
@@ -2811,6 +2824,7 @@ Más información:
<translation id="8806285662264631610">Productos para el baño y el cuerpo</translation>
<translation id="8807160976559152894">Recortar después de cada página</translation>
<translation id="8808828119384186784">Configuración de Chrome</translation>
+<translation id="8813277370772331957">Recordar más tarde</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, pulsa Tabulador y, a continuación, Intro para actualizar Chrome desde la configuración de Chrome</translation>
<translation id="8820817407110198400">Marcadores</translation>
<translation id="882338992931677877">Ranura manual</translation>
@@ -2990,6 +3004,7 @@ Más información:
<translation id="988159990683914416">Build para desarrolladores</translation>
<translation id="989988560359834682">Editar dirección</translation>
<translation id="991413375315957741">sensores de luz o movimiento</translation>
+<translation id="992110854164447044">Una tarjeta virtual oculta tu tarjeta real para protegerte frente a posibles fraudes. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" no se ha instalado correctamente en tu ordenador o red:
diff --git a/chromium/components/strings/components_strings_et.xtb b/chromium/components/strings/components_strings_et.xtb
index fc9bb8767eb..abc2ce78315 100644
--- a/chromium/components/strings/components_strings_et.xtb
+++ b/chromium/components/strings/components_strings_et.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Uurimistoetused, stipendiumid ja rahaline abi</translation>
<translation id="1048785276086539861">Märkuste muutmisel naaseb see dokument ühe lehe vaatele</translation>
<translation id="1050038467049342496">Sulgege muud rakendused</translation>
+<translation id="1053959602163383901">Valisite autentimisseadmega kinnitamise veebisaitide puhul, mis kasutavad pakkujat <ph name="PROVIDER_ORIGIN" />. See pakkuja võis teie makseviisi teabe salvestada. Võite <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Võta lisamine tagasi</translation>
<translation id="1056663316309890343">Fototarkvara</translation>
<translation id="1056898198331236512">Hoiatus</translation>
@@ -119,6 +120,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="1270502636509132238">Kättesaamisviis</translation>
<translation id="1281476433249504884">Virnastaja 1</translation>
<translation id="1285320974508926690">Ära kunagi seda saiti tõlgi</translation>
+<translation id="1288548991597756084">Salvestage kaart turvaliselt</translation>
<translation id="1292571435393770077">Salv 16</translation>
<translation id="1292701964462482250">„Teie arvutis olev tarkvara ei luba Chrome'il veebiga turvaliselt ühendust luua†(ainult Windowsi arvutid)</translation>
<translation id="1294154142200295408">Käsurea variatsioonid</translation>
@@ -223,6 +225,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
&lt;p&gt;Vea parandamiseks klõpsake avataval lehel käsul &lt;strong&gt;Loo ühendus&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Maastikukujundus</translation>
<translation id="1513706915089223971">Ajalookannete loend</translation>
+<translation id="1516097932025103760">See krüpteeritakse, salvestatakse turvaliselt ja CVC-d ei talletata.</translation>
<translation id="1517433312004943670">Telefoninumber on nõutav</translation>
<translation id="1519264250979466059">Järgu kuupäev</translation>
<translation id="1521159554480556801">Kiud- ja tekstiilikunst</translation>
@@ -288,6 +291,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="1662550410081243962">Salvesta ja sisesta makseviisid</translation>
<translation id="1663943134801823270">Kaardid ja aadressid pärinevad Chrome'ist. Neid saate hallata menüüs <ph name="BEGIN_LINK" />Seaded<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Lehed keeles <ph name="SOURCE_LANGUAGE" /> tõlgitakse nüüd keelde <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Pakkumise kasutamiseks makske kaardiga <ph name="CARD_DETAIL" /></translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> &gt; <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Lühem serv enne</translation>
<translation id="168693727862418163">Seda reegli väärtust ei õnnestunud skeemiga võrdluses kinnitada ja seda eiratakse.</translation>
@@ -306,6 +310,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="1717218214683051432">Liikumisandurid</translation>
<translation id="1717494416764505390">Postkast 3</translation>
<translation id="1718029547804390981">Dokument on märkuste lisamiseks liiga suur</translation>
+<translation id="1720941539803966190">Sule õpetus</translation>
<translation id="1721424275792716183">* Kohustuslik väli</translation>
<translation id="1727613060316725209">Sertifikaat on kehtiv</translation>
<translation id="1727741090716970331">Kehtiva kaardinumbri lisamine</translation>
@@ -421,8 +426,8 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="205212645995975601">Grillimine</translation>
<translation id="2053111141626950936">Selles keeles lehti ei tõlgita: <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Kui see juhtelement on sisse lülitatud ja olek on aktiivne, määrab Chrome, millise suure grupi (ehk rühma) inimestega on teie hiljutine sirvimistegevus kõige sarnasem. Reklaamijad saavad grupi jaoks reklaame valida ja teie sirvimistegevus jääb teie seadmes privaatseks. Teie gruppi värskendatakse iga päev.}=1{Kui see juhtelement on sisse lülitatud ja olek on aktiivne, määrab Chrome, millise suure grupi (ehk rühma) inimestega on teie hiljutine sirvimistegevus kõige sarnasem. Reklaamijad saavad grupi jaoks reklaame valida ja teie sirvimistegevus jääb teie seadmes privaatseks. Teie gruppi värskendatakse iga päev.}other{Kui see juhtelement on sisse lülitatud ja olek on aktiivne, määrab Chrome, millise suure grupi (ehk rühma) inimestega on teie hiljutine sirvimistegevus kõige sarnasem. Reklaamijad saavad grupi jaoks reklaame valida ja teie sirvimistegevus jääb teie seadmes privaatseks. Teie gruppi värskendatakse iga {NUM_DAYS} päeva järel.}}</translation>
-<translation id="2053553514270667976">Sihtnumber</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 soovitus}other{# soovitust}}</translation>
+<translation id="2066915425250589881">taotleda selle kustutamist</translation>
<translation id="2068528718802935086">Imikud ja väikelapsed</translation>
<translation id="2071156619270205202">Seda kaarti ei saa virtuaalkaardi numbrina kasutada.</translation>
<translation id="2071692954027939183">Märguanded blokeeriti automaatselt, kuna tavaliselt ei luba te neid kuvada</translation>
@@ -434,7 +439,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="2088086323192747268">Nupp Sünkroonimise haldamine, vajutage Chrome'i seadetes sünkroonitava teabe haldamiseks sisestusklahvi</translation>
<translation id="2091887806945687916">Heli</translation>
<translation id="2094505752054353250">Domeeni vastuolu</translation>
-<translation id="2096368010154057602">Osakond</translation>
<translation id="2099652385553570808">Kolm kirjaklambrit vasakul</translation>
<translation id="2101225219012730419">Versioon:</translation>
<translation id="2102134110707549001">Soovita tugevat parooli …</translation>
@@ -471,7 +475,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="2185836064961771414">Ameerika jalgpall</translation>
<translation id="2187317261103489799">Tuvasta (vaikimisi)</translation>
<translation id="2188375229972301266">Mitu auku all</translation>
-<translation id="2188852899391513400">Äsja kasutatud parool leiti andmetega seotud rikkumisest. Teie kontode turvalisuse kaitsmiseks soovitab Google'i paroolihaldur teil seda parooli kohe muuta ja kontrollida siis oma salvestatud paroole.</translation>
<translation id="219906046732893612">Remonditööd</translation>
<translation id="2202020181578195191">Sisestage kehtiv aegumisaasta</translation>
<translation id="22081806969704220">Salv 3</translation>
@@ -482,6 +485,8 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="2215727959747642672">Failide muutmine</translation>
<translation id="2215963164070968490">Koerad</translation>
<translation id="2218879909401188352">Saidil <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> olevad ründajad võivad installida ohtlikke rakendusi, mis kahjustavad teie seadet, lisavad mobiiliarvele varjatud kulusid või varastavad teie isiklikke andmeid. <ph name="BEGIN_LEARN_MORE_LINK" />Lisateave<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Alusta õpetusi uuesti</translation>
+<translation id="2219735899272417925">Nõutav on seadme lähtestamine</translation>
<translation id="2224337661447660594">Interneti-ühendus puudub</translation>
<translation id="2230458221926704099">Parandage oma ühendus <ph name="BEGIN_LINK" />diagnostikarakenduse<ph name="END_LINK" /> abil</translation>
<translation id="2239100178324503013">Saada kohe</translation>
@@ -579,11 +584,13 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="2512101340618156538">Pole lubatud (vaikeseade)</translation>
<translation id="2512413427717747692">Chrome'i vaikebrauseriks määramise nupp. Vajutage sisestusklahvi, et määrata Chrome iOS-i seadetes süsteemi vaikebrauseriks</translation>
<translation id="2515629240566999685">Kontrollige oma piirkonna signaali</translation>
+<translation id="2515761554693942801">Valisite Touch ID-ga kinnitamise veebisaitide puhul, mis kasutavad pakkujat <ph name="PROVIDER_ORIGIN" />. See pakkuja võis teie makseviisi teabe salvestada. Võite <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Kirjaklamber paremal all</translation>
<translation id="2521736961081452453">Loo vorm</translation>
<translation id="2523886232349826891">Salvestatakse ainult sellesse seadmesse</translation>
<translation id="2524461107774643265">Lisateabe lisamine</translation>
<translation id="2529899080962247600">Sellel väljal ei tohi olla üle <ph name="MAX_ITEMS_LIMIT" /> kirje. Kõiki järgnevaid kirjeid eiratakse.</translation>
+<translation id="253493526287553278">Kuva sooduskoodi üksikasjad</translation>
<translation id="2535585790302968248">Privaatselt sirvimiseks avage uus inkognito vaheleht</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{ja veel 1}other{ja veel #}}</translation>
<translation id="2536110899380797252">Lisa aadress</translation>
@@ -619,7 +626,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="259821504105826686">Fotograafia ja digitaalkunst</translation>
<translation id="2601150049980261779">Romantika</translation>
<translation id="2604589665489080024">Popmuusika</translation>
-<translation id="2609632851001447353">Variatsioonid</translation>
<translation id="2610561535971892504">Klõpsake kopeerimiseks</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />ei salvesta<ph name="END_EMPHASIS" /> järgmist teavet.
<ph name="BEGIN_LIST" />
@@ -652,6 +658,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Sünnipäevad ja nimepäevad</translation>
<translation id="2677748264148917807">Lahku</translation>
+<translation id="2679714844901977852">Salvestage oma kaart ja arveldusteave oma Google'i kontole <ph name="USER_EMAIL" />, et turvaliselt ja kiirelt maksta</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Parim sobivus</translation>
<translation id="2688969097326701645">Jah, jätka</translation>
@@ -700,7 +707,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="2854764410992194509">Internetiteenuse pakkujad (ISP-d)</translation>
<translation id="2856444702002559011">Ründajad võivad üritada varastada teie teavet (nt paroole, sõnumeid või krediitkaarditeavet) saidilt <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />. <ph name="BEGIN_LEARN_MORE_LINK" />Lisateave<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Sait kuvab sekkuvaid või eksitavaid reklaame.</translation>
-<translation id="286512204874376891">Virtuaalkaart varjab teie päriskaarti, et teid võimalike pettuste eest kaitsta. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Sõbralik</translation>
<translation id="28761159517501904">Filmid</translation>
<translation id="2876489322757410363">Väljute inkognito režiimist, et välise rakenduse kaudu maksta. Kas soovite jätkata?</translation>
@@ -801,7 +807,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="3158539265159265653">Ketas</translation>
<translation id="3162559335345991374">WiFi-võrk, mida kasutate, võib nõuda sisselogimislehe külastamist.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Saar</translation>
<translation id="3176929007561373547">Kontrollige puhverserveri seadeid või võtke ühendust võrguadministraatoriga
ja veenduge, et puhverserver töötaks. Kui arvate, et teil ei ole vaja
puhverserverit kasutada:
@@ -880,9 +885,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="3369192424181595722">Kella viga</translation>
<translation id="3369459162151165748">Sõidukite osad ja tarvikud</translation>
<translation id="3371064404604898522">Määra Chrome vaikebrauseriks</translation>
-<translation id="3371076217486966826"><ph name="URL" /> soovib:
- • luua teid ümbritsevast 3D-kaardi ja jälgida kaamera asendit;
- • kasutada teie kaamerat.</translation>
<translation id="337363190475750230">Eraldatud</translation>
<translation id="3375754925484257129">Käita Chrome'i ohutuskontroll</translation>
<translation id="3377144306166885718">Server kasutas TLS-i aegunud versiooni.</translation>
@@ -899,6 +901,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="3399952811970034796">Kohaletoimetamisaadress</translation>
<translation id="3402261774528610252">Selle saidi laadimiseks kasutatud ühendus kasutas TLS 1.0 või TLS 1.1, mille tugi on katkestatud ja mis tulevikus keelatakse. Kui see keelatakse, siis ei saa kasutajad enam seda saiti laadida. Server peab lubama TLS 1.2 või uuema.</translation>
<translation id="3405664148539009465">Kohanda fonte</translation>
+<translation id="3407789382767355356">kolmanda osapoole sisselogimisviibad</translation>
<translation id="3409896703495473338">Turvaseadete haldamine</translation>
<translation id="3414952576877147120">Suurus:</translation>
<translation id="3417660076059365994">Teie üles laaditud või manustatud failid saadetakse Google Cloudi või kolmandatele osapooltele analüüsimiseks. Näiteks võidakse neid skannida tundlike andmete või pahavara tuvastamiseks.</translation>
@@ -931,6 +934,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="3477679029130949506">Filmiloetelud ja teatrietenduste ajad</translation>
<translation id="3479552764303398839">Mitte praegu</translation>
<translation id="3484560055331845446">Võite kaotada juurdepääsu oma Google'i kontole. Chrome soovitab teil kohe oma parooli muuta. Teil palutakse sisse logida.</translation>
+<translation id="3484861421501147767">Meeldetuletus: salvestatud sooduskood on saadaval</translation>
<translation id="3487845404393360112">Salv 4</translation>
<translation id="3495081129428749620">Lehelt otsimine
<ph name="PAGE_TITLE" /></translation>
@@ -1055,6 +1059,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="3810973564298564668">Halda</translation>
<translation id="3816482573645936981">Väärtus (asendatud)</translation>
<translation id="382518646247711829">Kui kasutate puhverserverit ...</translation>
+<translation id="3826050100957962900">Kolmanda osapoole sisselogimisviibad</translation>
<translation id="3827112369919217609">Absoluutne</translation>
<translation id="3827666161959873541">Koguperefilmid</translation>
<translation id="3828924085048779000">Tühi parool ei ole lubatud.</translation>
@@ -1067,9 +1072,9 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="3858027520442213535">Värskenda kuupäeva ja kellaaega</translation>
<translation id="3858860766373142691">Nimi</translation>
<translation id="3872834068356954457">Teadus</translation>
+<translation id="3875783148670536197">Kuva juhised</translation>
<translation id="3881478300875776315">Kuva vähem ridu</translation>
<translation id="3884278016824448484">Seadme identifikaator on konfliktne</translation>
-<translation id="3885155851504623709">Vald</translation>
<translation id="388632593194507180">Tuvastati jälgimine</translation>
<translation id="3886948180919384617">Virnastaja 3</translation>
<translation id="3890664840433101773">E-posti aadressi lisamine</translation>
@@ -1099,9 +1104,11 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="3973234410852337861">Host <ph name="HOST_NAME" /> on blokeeritud</translation>
<translation id="3978338123949022456">Otsingurežiim. Sisestage päring ja vajutage sisestusklahvi, et otsida teenuses <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Linnud</translation>
+<translation id="3985750352229496475">Aadresside haldamine …</translation>
<translation id="3986705137476756801">Reaalajas subtiitrite praeguseks väljalülitamine</translation>
<translation id="3987940399970879459">Alla 1 MB</translation>
<translation id="3990250421422698716">Astme nihe</translation>
+<translation id="3992684624889376114">Lisateave selle lehe kohta</translation>
<translation id="3996311196211510766">Sait <ph name="ORIGIN" /> nõuab, et lähtekohareeglit
kohaldataks kõigi taotluste puhul, kuid reeglit ei saa praegu rakendada.</translation>
<translation id="4006465311664329701">Makseviisid, pakkumised ja aadressid, mis kasutavad Google Payd</translation>
@@ -1226,6 +1233,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="4305666528087210886">Teie failile ei pääsetud juurde</translation>
<translation id="4306529830550717874">Kas salvestada aadress?</translation>
<translation id="4306812610847412719">lõikelaud</translation>
+<translation id="4310070645992025887">Otsige teekondadest</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokeeri (vaikimisi)</translation>
<translation id="4314815835985389558">Sünkroonimise haldamine</translation>
@@ -1276,6 +1284,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="4435702339979719576">Postkaart)</translation>
<translation id="443673843213245140">Puhverserveri kasutamine on keelatud, kuid määratud on ka konkreetne puhverserveri konfigureerimine.</translation>
<translation id="4441832193888514600">Eiratakse, kuna reegli saab seadistada ainult pilves olev kasutaja.</translation>
+<translation id="4442470707340296952">Chrome'i vahelehed</translation>
<translation id="4450893287417543264">Ära kuva uuesti</translation>
<translation id="4451135742916150903">Saab küsida luba HID-seadmetega ühenduse loomiseks</translation>
<translation id="4452328064229197696">Äsja kasutatud parool leiti andmetega seotud rikkumisest. Teie kontode turvalisuse kaitsmiseks soovitab Google'i paroolihaldur teil kontrollida oma salvestatud paroole.</translation>
@@ -1414,6 +1423,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="483241715238664915">Lülita hoiatused sisse</translation>
<translation id="4834250788637067901">Makseviisid, pakkumised ja aadressid, mis kasutavad Google Payd</translation>
<translation id="4838327282952368871">Unistav</translation>
+<translation id="4839087176073128681">Makske järgmisel korral kiiremini ja kaitske oma kaarti Google'i valdkonna parimate turvameetmetega.</translation>
<translation id="4840250757394056958">Chrome’i ajaloo vaatamine</translation>
<translation id="484462545196658690">Automaatne</translation>
<translation id="484671803914931257">Saage allahindlust kaupmehe <ph name="MERCHANT_NAME" /> juures ja mujalgi</translation>
@@ -1476,6 +1486,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="5011561501798487822">Tuvastatud keel</translation>
<translation id="5015510746216210676">Seadme nimi:</translation>
<translation id="5017554619425969104">Teie kopeeritud tekst</translation>
+<translation id="5017828934289857214">Tuleta hiljem meelde</translation>
<translation id="5018422839182700155">Seda lehte ei saa avada</translation>
<translation id="5019198164206649151">Varusalves esineb probleeme</translation>
<translation id="5020776957610079374">Maailmamuusika</translation>
@@ -1495,6 +1506,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="5051305769747448211">Komöödiaesitused</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Selle faili saatmiseks funktsiooni Läheduses jagamine kaudu vabastage seadmes ruumi (<ph name="DISK_SPACE_SIZE" />)}other{Nende failide saatmiseks funktsiooni Läheduses jagamine kaudu vabastage seadmes ruumi (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">Teile soovitatud artiklid</translation>
+<translation id="5060483733937416656">Valisite Windows Helloga kinnitamise veebisaitide puhul, mis kasutavad pakkujat <ph name="PROVIDER_ORIGIN" />. See pakkuja võis teie makseviisi teabe salvestada. Võite <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Kas mõtlesite domeeni <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Printimisajalugu</translation>
<translation id="5068234115460527047">Riskifondid</translation>
@@ -1508,10 +1520,8 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="5087286274860437796">Serveri sertifikaat pole praegu kehtiv.</translation>
<translation id="5087580092889165836">Lisa kaart</translation>
<translation id="5088142053160410913">Sõnum operaatorile</translation>
-<translation id="5089810972385038852">Osariik/Maakond</translation>
<translation id="5093232627742069661">Z-kujuliselt volditud</translation>
<translation id="5094747076828555589">Server ei suutnud tõestada, et see on domeen <ph name="DOMAIN" />, Chromium ei usalda selle turvasertifikaati. Selle põhjuseks võib olla vale seadistus või ründaja, kes on sekkunud teie ühendusse.</translation>
-<translation id="5095208057601539847">Provints</translation>
<translation id="5097099694988056070">Seadme statistika, näiteks protsessori/muutmälu kasutus</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Sait pole turvaline</translation>
@@ -1549,6 +1559,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="5171045022955879922">Otsige või sisestage URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Masin</translation>
+<translation id="5177076414499237632">Teave selle lehe allika ja teema kohta</translation>
<translation id="5179510805599951267">Tegu ei ole <ph name="ORIGINAL_LANGUAGE" /> keelega? Andke veast teada</translation>
<translation id="518639307526414276">Lemmikloomade toit ja hooldustarbed</translation>
<translation id="5190835502935405962">Järjehoidjariba</translation>
@@ -1709,6 +1720,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="5624120631404540903">Paroolide haldamine</translation>
<translation id="5629630648637658800">Reegli seadete laadimine ebaõnnestus</translation>
<translation id="5631439013527180824">Seadme halduse luba on kehtetu</translation>
+<translation id="5632485077360054581">Kuva juhised</translation>
<translation id="5633066919399395251">Saidil <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> olevad ründajad võivad proovida installida teie arvutisse ohtlikke programme, mis varastavad teie teavet või kustutavad selle (nt fotod, paroolid, sõnumid ja krediitkaarditeave). <ph name="BEGIN_LEARN_MORE_LINK" />Lisateave<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Petlik sisu blokeeriti.</translation>
<translation id="5633259641094592098">Kultus- ja indie-filmid</translation>
@@ -1826,6 +1838,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="5989320800837274978">Määratud ei ole fikseeritud puhverservereid ega pac-skriptiga URL-i.</translation>
<translation id="5992691462791905444">Z-kujuliselt volditud</translation>
<translation id="5995727681868049093">Google'i kontol teabe, privaatsuse ja turvalisuse haldamine</translation>
+<translation id="5997247540087773573">Äsja kasutatud parool leiti andmetega seotud rikkumisest. Teie kontode turvalisuse kaitsmiseks soovitab Google'i paroolihaldur teil seda parooli kohe muuta ja kontrollida oma salvestatud paroole.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> tulemust otsingule „<ph name="SEARCH_TEXT" />â€</translation>
<translation id="6006484371116297560">Klassikaline</translation>
<translation id="6008122969617370890">Järjestus n–1</translation>
@@ -1921,7 +1934,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="627746635834430766">Kui soovite järgmisel korral kiiremini maksta, salvestage kaart ja arveldusaadress oma Google'i kontole.</translation>
<translation id="6279183038361895380">Kursori kuvamiseks vajutage klahvi |<ph name="ACCELERATOR" />|</translation>
<translation id="6280223929691119688">Sellele aadressile ei saa kaupa kohale toimetada. Valige mõni teine aadress.</translation>
-<translation id="6282194474023008486">Sihtnumber</translation>
<translation id="6285507000506177184">Nupp Chrome'i allalaadimiste haldamine, Chrome'is allalaaditud failide haldamiseks vajutage sisestusklahvi</translation>
<translation id="6289939620939689042">Lehe värv</translation>
<translation id="6290238015253830360">Teie soovitatud artiklid kuvatakse siin</translation>
@@ -1943,6 +1955,7 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="6337133576188860026">Vabastab alla <ph name="SIZE" />. Mõne saidi laadimine võib järgmisel külastusel rohkem aega võtta.</translation>
<translation id="6337534724793800597">Reeglite filtreerimine nime järgi</translation>
<translation id="6340739886198108203">Administraatori reegel ei soovita konfidentsiaalse sisu kuvamise ajal jäädvustada ekraanipilte ega salvestisi:</translation>
+<translation id="6348220984832452017">Aktiivsed variatsioonid</translation>
<translation id="6349101878882523185">Installi <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Puudub}=1{1 parool (loendile <ph name="DOMAIN_LIST" />, sünkroonitud)}=2{2 parooli (loendile <ph name="DOMAIN_LIST" />, sünkroonitud)}other{# parooli (loendile <ph name="DOMAIN_LIST" />, sünkroonitud)}}</translation>
<translation id="6355392890578844978">Seda brauserit ei halda ettevõte ega muu organisatsioon. Selle seadme tegevusi võidakse hallata ka väljaspool Chromiumi. <ph name="BEGIN_LINK" />Lisateave<ph name="END_LINK" /></translation>
@@ -1974,7 +1987,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="643051589346665201">Muuda Google'i parooli</translation>
<translation id="6433490469411711332">Kontaktandmete muutmine</translation>
<translation id="6433595998831338502">Host <ph name="HOST_NAME" /> keeldus ühendamast.</translation>
-<translation id="6438025220577812695">Muudan seda ise</translation>
<translation id="6440503408713884761">Eiras</translation>
<translation id="6443406338865242315">Millised laiendused ja pistikprogrammid olete installinud</translation>
<translation id="6446163441502663861">Kahu (ümbrik)</translation>
@@ -2104,9 +2116,9 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="6828866289116430505">Geneetika</translation>
<translation id="6831043979455480757">Tõlgi</translation>
<translation id="6833752742582340615">Salvestage oma kaart ja arveldusteave oma Google'i kontole, et turvaliselt ja kiirelt maksta</translation>
-<translation id="6839929833149231406">Ala</translation>
<translation id="6846340164947227603">Kasuta virtuaalkaardi numbrit …</translation>
<translation id="6852204201400771460">Kas soovite rakenduse uuesti laadida?</translation>
+<translation id="6857776781123259569">Paroolide haldamine …</translation>
<translation id="686485648936420384">Tarbijatele mõeldud teabeallikad</translation>
<translation id="6865412394715372076">Seda kaarti ei saa praegu kinnitada</translation>
<translation id="6869334554832814367">Eraisikulaenud</translation>
@@ -2155,7 +2167,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="6965978654500191972">Seade</translation>
<translation id="696703987787944103">Kujuteldav</translation>
<translation id="6968269510885595029">Kasutage oma turvavõtit</translation>
-<translation id="6970216967273061347">Piirkond</translation>
<translation id="6971439137020188025">Rakenduses Esitlused kiirelt uue Google'i esitluse loomine</translation>
<translation id="6972629891077993081">HID-seadmed</translation>
<translation id="6973656660372572881">Määratud on nii fikseeritud puhverserverid kui ka pac-skriptiga URL.</translation>
@@ -2194,7 +2205,6 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<translation id="7081308185095828845">See funktsioon pole teie seadmes saadaval</translation>
<translation id="7083258188081898530">Salv 9</translation>
<translation id="7086090958708083563">Kasutaja taotles üleslaadimist</translation>
-<translation id="7087282848513945231">Maakond</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, vajutage Chrome'i seadetes lubade ja mitmel saidil talletatud andmete haldamiseks tabulaatorit ja seejärel sisestusklahvi</translation>
<translation id="7096937462164235847">Selle veebisaidi identiteet on kinnitamata.</translation>
<translation id="7101893872976785596">Õudusfilmid</translation>
@@ -2213,10 +2223,10 @@ Vastasel korral blokeeritakse see teie privaatsusseadetes. See võimaldab teie k
<ph name="LIST_ITEM" />Vormidesse sisestatud teave.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Sellele aadressile ei saa tarnida. Valige mõni teine aadress.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Teie administraator on keelanud nende andmete kopeerimise.</translation>
<translation id="7135130955892390533">Kuva olek</translation>
<translation id="7138472120740807366">Kohaletoimetamisviis</translation>
-<translation id="7139724024395191329">Emiraat</translation>
<translation id="7139892792842608322">Peamine salv</translation>
<translation id="714064300541049402">Pildi nihe X-teljel 2. poolel</translation>
<translation id="7152423860607593928">Number-14 (ümbrik)</translation>
@@ -2433,6 +2443,7 @@ Lisateave:
<translation id="7669271284792375604">Sellel saidil asuvad ründajad võivad proovida meelitada teid installima programme, mis kahjustavad sirvimiskogemust (nt muudavad avalehte või kuvavad külastatavatel saitidel lisareklaame).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Andmetega seotud tegevused märgitakse konfidentsiaalseks (alates sisselogimisest on toimunud 1 toiming). <ph name="BEGIN_LINK" />Lisateave<ph name="END_LINK" />}other{Andmetega seotud tegevused märgitakse konfidentsiaalseks (alates sisselogimisest on toimunud # toimingut). <ph name="BEGIN_LINK" />Lisateave<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Postkast 6</translation>
+<translation id="7675325315208090829">Makseviiside haldamine …</translation>
<translation id="7676643023259824263">Lõikelaua teksti otsimine, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Vaadake ja hallake Chrome'i seadetes oma sirvimisajalugu</translation>
<translation id="7679947978757153706">Pesapall</translation>
@@ -2475,7 +2486,6 @@ Lisateave:
<translation id="7766518757692125295">Ãœmbris</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Sama järjestus, esikülg ülespoole</translation>
-<translation id="777702478322588152">Prefektuur</translation>
<translation id="7791011319128895129">Avaldamata</translation>
<translation id="7791196057686275387">Virn</translation>
<translation id="7791543448312431591">Lisa</translation>
@@ -2566,12 +2576,12 @@ Lisateave:
<translation id="8055534648776115597">Kutseharidus ja täiendusõpe</translation>
<translation id="8057711352706143257">Tarkvara „<ph name="SOFTWARE_NAME" />†ei ole õigesti seadistatud. Tarkvara „<ph name="SOFTWARE_NAME" />†desinstallimine lahendab tavaliselt probleemi. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Toiduainetööstus</translation>
-<translation id="8066955247577885446">Kahjuks läks midagi valesti</translation>
<translation id="8067872629359326442">Sisestasite äsja oma parooli petturlikule saidile. Chromium saab teid aidata. Oma parooli muutmiseks ja Google'i teavitamiseks sellest, et teie konto võib olla ohus, klõpsake valikul Konto kaitsmine.</translation>
<translation id="8070439594494267500">Rakenduse ikoon</translation>
<translation id="8074253406171541171">10x13 (ümbrik)</translation>
<translation id="8075736640322370409">Kiirelt uue Google'i arvutustabeli loomine</translation>
<translation id="8075898834294118863">Saidi seadete haldamine</translation>
+<translation id="8076492880354921740">Vahelehed</translation>
<translation id="8078141288243656252">Pööramisel ei saa märkusi lisada</translation>
<translation id="8079031581361219619">Kas laadida sait uuesti?</translation>
<translation id="8081087320434522107">Sedaanid</translation>
@@ -2694,6 +2704,7 @@ Lisateave:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Seaded</translation>
+<translation id="8428634594422941299">Selge</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, vajutage Chrome'i seadetes küpsiste eelistuste haldamiseks tabulaatorit ja seejärel sisestusklahvi</translation>
<translation id="8433057134996913067">See logib teid välja enamikult veebisaitidelt.</translation>
<translation id="8434840396568290395">Lemmikloomad</translation>
@@ -2791,6 +2802,7 @@ Lisateave:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> on saidi <ph name="ORIGIN" /> puhul teie kood</translation>
<translation id="874918643257405732">Lisa vaheleht järjehoidjatesse</translation>
<translation id="8751426954251315517">Proovige järgmisel korral uuesti</translation>
+<translation id="8757526089434340176">Saadaval on Google Pay pakkumine</translation>
<translation id="8758885506338294482">Videomänguvõistlused</translation>
<translation id="8759274551635299824">See kaart on aegunud</translation>
<translation id="87601671197631245">See sait kasutab vananenud turvaseadistust, mis võib paljastada teie teabe (näiteks paroolid, sõnumid või krediitkaardid), kui see saadetakse sellele saidile.</translation>
@@ -2798,6 +2810,7 @@ Lisateave:
<translation id="8763927697961133303">USB-seade</translation>
<translation id="8763986294015493060">Suletakse kõik praegu avatud inkognito aknad</translation>
<translation id="8766943070169463815">Turvalise maksemandaadi autentimisleht on avatud</translation>
+<translation id="8767765348545497220">Sule abimull</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Mootorrattad</translation>
<translation id="8790007591277257123">&amp;Kustuta uuesti</translation>
@@ -2810,6 +2823,7 @@ Lisateave:
<translation id="8806285662264631610">Vanni- ja kehatooted</translation>
<translation id="8807160976559152894">Kärbi pärast iga lehte</translation>
<translation id="8808828119384186784">Chrome'i seaded</translation>
+<translation id="8813277370772331957">Tuleta mulle hiljem meelde</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, vajutage Chrome'i seadetes Chrome'i värskendamiseks tabulaatorit ja seejärel sisestusklahvi</translation>
<translation id="8820817407110198400">Järjehoidjad</translation>
<translation id="882338992931677877">Käsitsi sisestamise ava</translation>
@@ -2989,6 +3003,7 @@ Lisateave:
<translation id="988159990683914416">Arendaja järk</translation>
<translation id="989988560359834682">Aadressi muutmine</translation>
<translation id="991413375315957741">liikumis- või valgusandurid</translation>
+<translation id="992110854164447044">Virtuaalkaart peidab teie päriskaardi, et teid võimalike pettuste eest kaitsta. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Roosa</translation>
<translation id="992432478773561401">Tarkvara „<ph name="SOFTWARE_NAME" />†ei installitud teie arvutisse või võrku korralikult.
diff --git a/chromium/components/strings/components_strings_eu.xtb b/chromium/components/strings/components_strings_eu.xtb
index 7dd314ce7ab..6d2898183b4 100644
--- a/chromium/components/strings/components_strings_eu.xtb
+++ b/chromium/components/strings/components_strings_eu.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Bekak eta dirulaguntzak</translation>
<translation id="1048785276086539861">Oharpenak editatzen dituzunean, orri bakarreko ikuspegira itzuliko da dokumentua</translation>
<translation id="1050038467049342496">Itxi beste aplikazio batzuk</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> darabilten webguneetan gailu autentifikatzaile batekin egiaztatzea aukeratu duzu. Baliteke hornitzaile horrek zure ordainketa-metodoari buruzko informazioa gorde izatea, baina <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Desegin gehitzea</translation>
<translation id="1056663316309890343">Argazkietarako softwareak</translation>
<translation id="1056898198331236512">Abisua</translation>
@@ -119,6 +120,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="1270502636509132238">Jasotzeko modua</translation>
<translation id="1281476433249504884">1. pilatzailea</translation>
<translation id="1285320974508926690">Ez itzuli inoiz webgune hau</translation>
+<translation id="1288548991597756084">Gorde txartela modu seguruan</translation>
<translation id="1292571435393770077">16. erretilua</translation>
<translation id="1292701964462482250">"Ordenagailuan dagoen software batek ez dio uzten Chrome-ri sarera segurtasunez konektatzen" (Windows ordenagailuak soilik)</translation>
<translation id="1294154142200295408">Agindu-lerroaren aldaerak</translation>
@@ -223,6 +225,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
&lt;p&gt;Errorea konpontzeko, sakatu ireki nahi duzun orriko &lt;strong&gt;Konektatu&lt;/strong&gt; aukera.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Paisaien diseinua</translation>
<translation id="1513706915089223971">Historiako sarreren zerrenda</translation>
+<translation id="1516097932025103760">Enkriptatu eta segurtasunez gordeko da. CVCa ez da inoiz gordetzen.</translation>
<translation id="1517433312004943670">Telefono-zenbakia behar da</translation>
<translation id="1519264250979466059">Konpilazioaren data</translation>
<translation id="1521159554480556801">Zuntz- eta ehun-arteak</translation>
@@ -288,6 +291,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="1662550410081243962">Gorde eta bete ordainketa-metodoak</translation>
<translation id="1663943134801823270">Chrome-tik hartu dira txartelak eta helbideak. Kudeatu nahi badituzu, joan <ph name="BEGIN_LINK" />Ezarpenak<ph name="END_LINK" /> atalera.</translation>
<translation id="1671391448414634642"><ph name="SOURCE_LANGUAGE" /> darabilten orriak <ph name="TARGET_LANGUAGE" /> hizkuntzara itzuliko dira.</translation>
+<translation id="1673886523110456987">Eskaintza erabiltzeko, erosi <ph name="CARD_DETAIL" /> txartelarekin</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> - <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Ertz laburra lehenbizi</translation>
<translation id="168693727862418163">Ezin izan da baliozkotu gidalerroaren balioa haren eskemarekin, eta ez ikusi egingo zaio.</translation>
@@ -306,6 +310,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="1717218214683051432">Mugimendu-sentsoreak</translation>
<translation id="1717494416764505390">3. postontzia</translation>
<translation id="1718029547804390981">Dokumentua handiegia da oharpenak egin ahal izateko.</translation>
+<translation id="1720941539803966190">Itxi tutoriala</translation>
<translation id="1721424275792716183">* Nahitaez bete behar da eremua</translation>
<translation id="1727613060316725209">Ziurtagiriak balio du</translation>
<translation id="1727741090716970331">Zehaztu balio duen txartel-zenbaki bat</translation>
@@ -418,8 +423,8 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="205212645995975601">Barbakoak eta parrillak</translation>
<translation id="2053111141626950936">Ez dira itzuliko <ph name="LANGUAGE" /> darabilten orriak.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Kontrolatzeko aukera abian denean eta aktibo dagoenean, duela gutxi egin dituzun arakatze-jardueren antz handiena duen talde demografikoa zein den zehaztuko du Chrome-k. Iragarleek talde horretarako iragarkiak hauta ditzakete, eta arakatze-historia pribatu mantenduko da gailuan. Egunero eguneratuko da taldea.}=1{Kontrolatzeko aukera abian denean eta aktibo dagoenean, duela gutxi egin dituzun arakatze-jardueren antz handiena duen talde demografikoa zein den zehaztuko du Chrome-k. Iragarleek talde horretarako iragarkiak hauta ditzakete, eta arakatze-historia pribatu mantenduko da gailuan. Egunero eguneratuko da taldea.}other{Kontrolatzeko aukera abian denean eta aktibo dagoenean, duela gutxi egin dituzun arakatze-jardueren antz handiena duen talde demografikoa zein den zehaztuko du Chrome-k. Iragarleek talde horretarako iragarkiak hauta ditzakete, eta arakatze-historia pribatu mantenduko da gailuan. {NUM_DAYS} egunetik behin eguneratuko da taldea.}}</translation>
-<translation id="2053553514270667976">Posta-kodea</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 iradokizun}other{# iradokizun}}</translation>
+<translation id="2066915425250589881">hura ezabatzea eska dezakezu</translation>
<translation id="2068528718802935086">Haurtxoak eta ume txikiak</translation>
<translation id="2071156619270205202">Txartela ez da egokia txartel birtualaren zenbaki bat izateko.</translation>
<translation id="2071692954027939183">Automatikoki blokeatu dira jakinarazpenak, normalean ez dituzulako onartzen</translation>
@@ -431,7 +436,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="2088086323192747268">Sinkronizazioa kudeatzeko botoia, sakatu Sartu Chrome-ren ezarpenetara joan, eta sinkronizatzen den informazioa kudeatzeko</translation>
<translation id="2091887806945687916">Soinua</translation>
<translation id="2094505752054353250">Domeinuak ez datoz bat</translation>
-<translation id="2096368010154057602">Departamentua</translation>
<translation id="2099652385553570808">Hiru grapa ezkerrean</translation>
<translation id="2101225219012730419">Bertsioa:</translation>
<translation id="2102134110707549001">Iradoki pasahitz konplexu bat…</translation>
@@ -468,7 +472,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="2185836064961771414">Futbol amerikarra</translation>
<translation id="2187317261103489799">Hauteman (lehenetsia)</translation>
<translation id="2188375229972301266">Hainbat zulo behean</translation>
-<translation id="2188852899391513400">Erabili berri duzun pasahitza datuen isilpekotasunaren urratze batean aurkitu da. Kontuak babesteko, Google-ren Pasahitz-kudeatzailea zerbitzuak pasahitza orain aldatzea gomendatzen du, eta gero gordeta dauzkazun pasahitzak seguruak direla egiaztatzea.</translation>
<translation id="219906046732893612">Etxeko hobekuntzak</translation>
<translation id="2202020181578195191">Idatzi balio duen iraungitze-urte bat</translation>
<translation id="22081806969704220">3. erretilua</translation>
@@ -479,6 +482,8 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="2215727959747642672">Fitxategiak editatzeko aukera</translation>
<translation id="2215963164070968490">Txakurrak</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webguneko erasotzaileek aplikazio arriskutsuak instala ditzakete eta haiek gailua kalte dezakete, mugikorraren fakturan gastu ezkutuak gehi ditzakete edo informazio pertsonala ezkuta dezakete. <ph name="BEGIN_LEARN_MORE_LINK" />Lortu informazio gehiago<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Berrabiarazi tutoriala</translation>
+<translation id="2219735899272417925">Gailua berrabiarazi behar da</translation>
<translation id="2224337661447660594">Ez dago Interneteko konexiorik</translation>
<translation id="2230458221926704099">Egiaztatu konexioa <ph name="BEGIN_LINK" />diagnostikoen aplikazioa<ph name="END_LINK" /> erabilita</translation>
<translation id="2239100178324503013">Bidali</translation>
@@ -576,11 +581,13 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="2512101340618156538">Baimenik gabe (lehenetsia)</translation>
<translation id="2512413427717747692">Chrome arakatzaile lehenetsi gisa ezartzeko botoia: sakatu "Sartu" Chrome sistemaren arakatzaile lehenetsi gisa ezartzeko iOS-en ezarpenetan</translation>
<translation id="2515629240566999685">Seinalea egiaztatu.</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> darabilten webguneetan Touch ID-rekin egiaztatzea aukeratu duzu. Baliteke hornitzaile horrek zure ordainketa-metodoari buruzko informazioa gorde izatea, baina <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Grapa bat behean, eskuinetara</translation>
<translation id="2521736961081452453">Sortu inprimaki bat</translation>
<translation id="2523886232349826891">Gailu honetan soilik gordeko da</translation>
<translation id="2524461107774643265">Gehitu informazio gehiago</translation>
<translation id="2529899080962247600">Eremu honek ez lituzke <ph name="MAX_ITEMS_LIMIT" /> sarrera baino gehiago izan behar. Sarrera gehiagorik badago, ez ikusi egingo zaie.</translation>
+<translation id="253493526287553278">Ikusi promozio-kodearen xehetasunak</translation>
<translation id="2535585790302968248">Modu pribatuan arakatzeko, ireki ezkutuko moduko fitxa bat</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{eta beste bat}other{eta beste #}}</translation>
<translation id="2536110899380797252">Gehitu helbidea</translation>
@@ -616,7 +623,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="259821504105826686">Argazkigintza eta arte digitala</translation>
<translation id="2601150049980261779">Film erromantikoak</translation>
<translation id="2604589665489080024">Pop musika</translation>
-<translation id="2609632851001447353">Aldaerak</translation>
<translation id="2610561535971892504">Sakatu kopiatzeko</translation>
<translation id="2617988307566202237">Chrome-k <ph name="BEGIN_EMPHASIS" />ez du gordeko<ph name="END_EMPHASIS" /> informazio hau:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Urtebetetzeak eta santu-egunak</translation>
<translation id="2677748264148917807">Irten</translation>
+<translation id="2679714844901977852">Ordainketa-prozesuak seguruak eta bizkorragoak izan daitezen, gorde txartela eta fakturazio-datuak Google-ko kontuan (<ph name="USER_EMAIL" />)</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Doikuntzarik onena</translation>
<translation id="2688969097326701645">Bai, egin aurrera</translation>
@@ -697,7 +704,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="2854764410992194509">Interneteko zerbitzu-hornitzaileak</translation>
<translation id="2856444702002559011">Baliteke erasotzaile batzuk <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webgunean duzun informazioa lapurtzen saiatzen aritzea (adibidez, pasahitzak, mezuak edo kreditu-txartelen datuak). <ph name="BEGIN_LEARN_MORE_LINK" />Lortu informazio gehiago<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Webgune honek iragarki oztopatzaile edo iruzurrezkoak erakusten ditu.</translation>
-<translation id="286512204874376891">Txartel birtualek benetako txartelak mozorrotzen dituzte, iruzurren aurka babestuago egon daitezen. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Polita</translation>
<translation id="28761159517501904">Filmak</translation>
<translation id="2876489322757410363">Ezkutuko modutik irtengo zara kanpoko aplikazio baten bidez ordaintzeko. Aurrera egin nahi duzu?</translation>
@@ -798,7 +804,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="3158539265159265653">Diskoa</translation>
<translation id="3162559335345991374">Baliteke darabilzun Wi-Fi konexioaren saio-hasierako orrira joan behar izatea.</translation>
<translation id="3169472444629675720">Ezagutu</translation>
-<translation id="3174168572213147020">Uhartea</translation>
<translation id="3176929007561373547">Egiaztatu proxy-ezarpenak edo jarri sarearen administratzailearekin harremanetan proxy-zerbitzaria badabilela ziurtatzeko. Proxy-zerbitzaririk erabili beharko ez zenukeela uste baduzu: <ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">Gailua noiz erabiltzen ari zaren jakin.</translation>
<translation id="3180358318770512945">Gurasotasuna</translation>
@@ -874,9 +879,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="3369192424181595722">Erlojuaren errorea</translation>
<translation id="3369459162151165748">Ibilgailuen piezak eta osagarriak</translation>
<translation id="3371064404604898522">Ezarri Chrome arakatzaile lehenetsi gisa</translation>
-<translation id="3371076217486966826"><ph name="URL" /> webguneak hauek egin nahi ditu:
- • Inguruaren 3D-ko mapa bat sortu eta kameraren posizioaren jarraipena egin.
- • Kamera erabili.</translation>
<translation id="337363190475750230">Hornitu gabe</translation>
<translation id="3375754925484257129">Egin Chrome-ren segurtasun-egiaztapena</translation>
<translation id="3377144306166885718">Zerbitzariak TLSren bertsio zaharkitu bat erabili du.</translation>
@@ -893,6 +895,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="3399952811970034796">Entregatzeko helbidea</translation>
<translation id="3402261774528610252">Webgunea kargatzeko erabilitako konexioa TLS 1.0 edo TLS 1.1 da, baina hura zaharkituta dago eta desgaitu egingo da etorkizunean. Desgaitu ondoren, erabiltzaileek ezingo dute kargatu webgunea. Zerbitzariak TLS 1.2 bertsioa edo berriago bat gaitu behar du.</translation>
<translation id="3405664148539009465">Pertsonalizatu letrak</translation>
+<translation id="3407789382767355356">Hirugarrenen bidez saioa hasteko aukera</translation>
<translation id="3409896703495473338">Kudeatu segurtasun-ezarpenak</translation>
<translation id="3414952576877147120">Tamaina:</translation>
<translation id="3417660076059365994">Kargatzen edo eransten dituzun fitxategiak Google Cloud-i edo hirugarrenei bidaltzen zaizkie, azter ditzaten. Adibidez, baliteke fitxategiak eskaneatzea kontuzko datuak edo malwarea duten ikusteko.</translation>
@@ -925,6 +928,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="3477679029130949506">Filmen zerrendak eta antzokiko ikuskizunen ordutegia</translation>
<translation id="3479552764303398839">Orain ez</translation>
<translation id="3484560055331845446">Google-ko konturako sarbidea gal zenezake. Pasahitza aldatzea gomendatzen dizu Chrome-k. Horretarako, saioa hasi beharko duzu.</translation>
+<translation id="3484861421501147767">Abisua: gordetako promozio-kode bat duzu</translation>
<translation id="3487845404393360112">4. erretilua</translation>
<translation id="3495081129428749620">Bilatu <ph name="PAGE_TITLE" /> orrian</translation>
<translation id="350069200438440499">Fitxategiaren izena:</translation>
@@ -1048,6 +1052,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="3810973564298564668">Kudeatu</translation>
<translation id="3816482573645936981">Balioa (ordeztua)</translation>
<translation id="382518646247711829">Proxy-zerbitzari bat erabiltzen baduzu…</translation>
+<translation id="3826050100957962900">Hirugarrenen bidez saioa hastea</translation>
<translation id="3827112369919217609">Absolutua</translation>
<translation id="3827666161959873541">Familientzako filmak</translation>
<translation id="3828924085048779000">Pasaesaldia ezin da hutsik utzi.</translation>
@@ -1060,9 +1065,9 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="3858027520442213535">Eguneratu data eta ordua</translation>
<translation id="3858860766373142691">Izena</translation>
<translation id="3872834068356954457">Zientzia</translation>
+<translation id="3875783148670536197">Erakutsi nola</translation>
<translation id="3881478300875776315">Erakutsi lerro gutxiago</translation>
<translation id="3884278016824448484">Gailu-identifikatzaile gatazkatsua</translation>
-<translation id="3885155851504623709">Barrutia</translation>
<translation id="388632593194507180">Sarea kontrolatzen ari direla hauteman da</translation>
<translation id="3886948180919384617">3. pilatzailea</translation>
<translation id="3890664840433101773">Gehitu helbide elektronikoa</translation>
@@ -1092,9 +1097,11 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="3973234410852337861">Blokeatuta dago <ph name="HOST_NAME" /></translation>
<translation id="3978338123949022456">Bilaketa modua, idatzi kontsulta bat eta sakatu "Sartu" <ph name="KEYWORD_SUFFIX" /> gako-hitzarekin bilatzeko</translation>
<translation id="398470910934384994">Hegaztiak</translation>
+<translation id="3985750352229496475">Kudeatu helbideak…</translation>
<translation id="3986705137476756801">Desaktibatu Istanteko azpitituluak, momentuz</translation>
<translation id="3987940399970879459">1 MB baino gutxiago</translation>
<translation id="3990250421422698716">Tolestura-marjina</translation>
+<translation id="3992684624889376114">Orri honi buruz</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> webguneak hartarako eskaera guztiei jatorri-gidalerro bat aplikatzeko eskatu du, baina ezin da aplikatu gidalerroa une honetan.</translation>
<translation id="4006465311664329701">Google Pay-rekin erabiltzen dituzun ordainketa-metodoak, eskaintzak eta helbideak</translation>
<translation id="4009243425692662128">Inprimatzen dituzun orrietako edukia Google Cloud-i edo hirugarrenei bidaltzen zaie eduki hori azter dezaten; adibidez, baliteke orrian kontuzko daturik dagoen bilatzea.</translation>
@@ -1214,6 +1221,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="4305666528087210886">Ezin izan da atzitu fitxategia</translation>
<translation id="4306529830550717874">Helbidea gorde nahi duzu?</translation>
<translation id="4306812610847412719">arbela</translation>
+<translation id="4310070645992025887">Bilatu bilaketa-ibilbideetan</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokeatu (lehenetsia)</translation>
<translation id="4314815835985389558">Kudeatu sinkronizazioa</translation>
@@ -1264,6 +1272,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Proxya erabiltzeko aukera desgaitu da, baina proxy-konfigurazio esplizitua zehaztu da.</translation>
<translation id="4441832193888514600">Ez ikusi egin zaio hodeiko erabiltzaile-gidalerro gisa soilik ezar daitekeelako gidalerroa.</translation>
+<translation id="4442470707340296952">Chrome-ko fitxak</translation>
<translation id="4450893287417543264">Ez erakutsi berriro</translation>
<translation id="4451135742916150903">HID gailuetara konektatzea eska dezake</translation>
<translation id="4452328064229197696">Erabili berri duzun pasahitza datuen isilpekotasunaren urratze batean aurkitu da. Kontuak babesteko, Google-ren Pasahitz-kudeatzailea zerbitzuak gordeta dauzkazun pasahitzak seguruak direla egiaztatzea gomendatzen du.</translation>
@@ -1402,6 +1411,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="483241715238664915">Aktibatu abisuak</translation>
<translation id="4834250788637067901">Google Pay-rekin erabiltzen dituzun ordainketa-metodoak, eskaintzak eta helbideak</translation>
<translation id="4838327282952368871">Ametsezkoa</translation>
+<translation id="4839087176073128681">Ordaindu bizkorrago hurrengoan eta babestu txartela Google-ren puntako segurtasun-neurriekin.</translation>
<translation id="4840250757394056958">Ikusi Chrome-ko historia</translation>
<translation id="484462545196658690">Automatikoa</translation>
<translation id="484671803914931257">Eskuratu deskontuak <ph name="MERCHANT_NAME" /> eta beste saltzaile batzuen produktuetan</translation>
@@ -1464,6 +1474,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="5011561501798487822">Hautemandako hizkuntza</translation>
<translation id="5015510746216210676">Gailuaren izena:</translation>
<translation id="5017554619425969104">Kopiatu duzun testua</translation>
+<translation id="5017828934289857214">Gogorarazi geroago</translation>
<translation id="5018422839182700155">Ezin da ireki orri hau</translation>
<translation id="5019198164206649151">Ordezko memoria egoera txarrean dago</translation>
<translation id="5020776957610079374">Munduko musika</translation>
@@ -1483,6 +1494,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="5051305769747448211">Zuzeneko komedia</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Fitxategia Nearby Share bidez bidaltzeko, egin tokia (<ph name="DISK_SPACE_SIZE" />) gailuan}other{Fitxategiak Nearby Share bidez bidaltzeko, egin tokia (<ph name="DISK_SPACE_SIZE" />) gailuan}}</translation>
<translation id="5056549851600133418">Zuretzako artikuluak</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> darabilten webguneetan Windows Hello-rekin egiaztatzea aukeratu duzu. Baliteke hornitzaile horrek zure ordainketa-metodoari buruzko informazioa gorde izatea, baina <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" /> esan nahi al zenuen?</translation>
<translation id="5066056036849835175">Inprimaketa-historia</translation>
<translation id="5068234115460527047">Estaldura-funtsak</translation>
@@ -1496,10 +1508,8 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="5087286274860437796">Une honetan ez du balio zerbitzariaren ziurtagiriak.</translation>
<translation id="5087580092889165836">Gehitu txartel bat</translation>
<translation id="5088142053160410913">Operadorearentzako mezua</translation>
-<translation id="5089810972385038852">Estatua</translation>
<translation id="5093232627742069661">Z-erako tolestura</translation>
<translation id="5094747076828555589">Zerbitzari honek ezin izan du egiaztatu <ph name="DOMAIN" /> domeinua denik. Chromium ez da bere segurtasun-ziurtagiriaz fidatzen. Baliteke gaizki konfiguratuta dagoelako izatea edo erasotzaile batek zure konexioa atzeman duelako izatea.</translation>
-<translation id="5095208057601539847">Probintzia</translation>
<translation id="5097099694988056070">Gailuaren estatistikak, hala nola CPU/RAM erabilera</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Webgunea ez da segurua</translation>
@@ -1537,6 +1547,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="5171045022955879922">Bilatu edo idatzi URLa</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Gailua</translation>
+<translation id="5177076414499237632">Lortu orri honen iturburuari eta gaiari buruzko informazioa</translation>
<translation id="5179510805599951267">Hizkuntza ez al da <ph name="ORIGINAL_LANGUAGE" />? Eman errorearen berri</translation>
<translation id="518639307526414276">Maskotentzako janaria eta maskotak zaintzeko hornigaiak</translation>
<translation id="5190835502935405962">Laster-marken barra</translation>
@@ -1697,6 +1708,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="5624120631404540903">Kudeatu pasahitzak</translation>
<translation id="5629630648637658800">Ezin izan dira kargatu gidalerroen ezarpenak</translation>
<translation id="5631439013527180824">Gailu-kudeaketaren tokenak ez du balio</translation>
+<translation id="5632485077360054581">Erakutsi nola</translation>
<translation id="5633066919399395251">Baliteke une honetan <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webgunean dauden erasotzaileak ordenagailuan informazioa (besteak beste, argazkiak, pasahitzak, mezuak eta kreditu-txartelen datuak) lapurtzen edo ezabatzen duten programa arriskutsuak instalatzen saiatzea. <ph name="BEGIN_LEARN_MORE_LINK" />Lortu informazio gehiago<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Eduki iruzurtia blokeatu da.</translation>
<translation id="5633259641094592098">Kultuko filmak eta film independenteak</translation>
@@ -1814,6 +1826,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="5989320800837274978">Ez da zehaztu proxy-zerbitzari finkorik, ezta .pac scripteko URLrik ere.</translation>
<translation id="5992691462791905444">Z-erako tolestura laburra</translation>
<translation id="5995727681868049093">Kudeatu Google-ko kontuko informazioa, pribatutasuna eta segurtasuna</translation>
+<translation id="5997247540087773573">Erabili berri duzun pasahitza datuen isilpekotasunaren urratze batean aurkitu da. Kontuak babesteko, Google-ren Pasahitz-kudeatzailea zerbitzuak pasahitza orain aldatzea gomendatzen du, eta gordeta dauzkazun pasahitzak seguruak direla egiaztatzea.</translation>
<translation id="6000758707621254961">"<ph name="SEARCH_TEXT" />" bilaketak <ph name="RESULT_COUNT" /> emaitza ditu</translation>
<translation id="6006484371116297560">Klasikoa</translation>
<translation id="6008122969617370890">N-tik 1erako ordena</translation>
@@ -1908,7 +1921,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="627746635834430766">Hurrengoan bizkorrago ordaintzeko, gorde txartela eta fakturazio-helbidea Google-ko kontuan.</translation>
<translation id="6279183038361895380">Kurtsorea ikusteko, sakatu |<ph name="ACCELERATOR" />|</translation>
<translation id="6280223929691119688">Ezin da entregatu helbide horretan. Hautatu beste helbide bat.</translation>
-<translation id="6282194474023008486">Posta-kodea</translation>
<translation id="6285507000506177184">Chrome-ren bidez egindako deskargak kudeatzeko botoia: sakatu "Sartu" tekla Chrome-ren bidez deskargatu dituzun fitxategiak kudeatzeko</translation>
<translation id="6289939620939689042">Orriaren kolorea</translation>
<translation id="6290238015253830360">Iradokitako artikuluak agertuko zaizkizu hemen</translation>
@@ -1930,6 +1942,7 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="6337133576188860026"><ph name="SIZE" /> baino gutxiago utziko ditu libre. Webgune batzuk mantsoago kargatuko dira bisitatzen dituzun hurrengoan.</translation>
<translation id="6337534724793800597">Iragazi gidalerroak izenaren arabera</translation>
<translation id="6340739886198108203">Administratzailearen gidalerroek ez dute gomendatzen pantaila-argazkirik ateratzea edo grabaketarik egitea isilpeko edukia ikusgai dagoenean:</translation>
+<translation id="6348220984832452017">Aldaera aktiboak</translation>
<translation id="6349101878882523185">Instalatu <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Bat ere ez}=1{<ph name="DOMAIN_LIST" />: 1 pasahitz (sinkronizatuta)}=2{<ph name="DOMAIN_LIST" />: 2 pasahitz (sinkronizatuta)}other{<ph name="DOMAIN_LIST" />: # pasahitz (sinkronizatuta)}}</translation>
<translation id="6355392890578844978">Arakatzailea ez du enpresa edo erakunde batek kudeatzen. Baliteke gailu honetako jarduerak Chromium-etik kanpo kudeatzea. <ph name="BEGIN_LINK" />Lortu informazio gehiago<ph name="END_LINK" /></translation>
@@ -1961,7 +1974,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="643051589346665201">Aldatu Google-ko pasahitza</translation>
<translation id="6433490469411711332">Editatu harremanetarako informazioa</translation>
<translation id="6433595998831338502">Konexioa baztertu du <ph name="HOST_NAME" /> webguneak.</translation>
-<translation id="6438025220577812695">Neure kabuz aldatuko dut</translation>
<translation id="6440503408713884761">Ez ikusi egin zaio</translation>
<translation id="6443406338865242315">Instalatuta dauzkazun luzapenak eta pluginak.</translation>
<translation id="6446163441502663861">Kahu (gutun-azala)</translation>
@@ -2091,9 +2103,9 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Itzuli</translation>
<translation id="6833752742582340615">Ordainketa-prozesuak seguruak eta bizkorragoak izan daitezen, gorde txartela eta fakturazio-datuak Google-ko kontuan</translation>
-<translation id="6839929833149231406">Eskualdea</translation>
<translation id="6846340164947227603">Erabili txartel birtualaren zenbakia…</translation>
<translation id="6852204201400771460">Berriro kargatu nahi duzu aplikazioa?</translation>
+<translation id="6857776781123259569">Kudeatu pasahitzak…</translation>
<translation id="686485648936420384">Kontsumo-baliabideak</translation>
<translation id="6865412394715372076">Txartela ezin da egiaztatu une honetan</translation>
<translation id="6869334554832814367">Mailegu pertsonalak</translation>
@@ -2142,7 +2154,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="6965978654500191972">Gailua</translation>
<translation id="696703987787944103">Pertzepziozkoa</translation>
<translation id="6968269510885595029">Erabili segurtasun-giltza</translation>
-<translation id="6970216967273061347">Distritua</translation>
<translation id="6971439137020188025">Sortu bizkor Google-ko aurkezpen bat Aurkezpenak zerbitzuan</translation>
<translation id="6972629891077993081">HID gailuak</translation>
<translation id="6973656660372572881">Proxy-zerbitzari finkoak nahiz .pac URL scripta zehaztu dira.</translation>
@@ -2181,7 +2192,6 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<translation id="7081308185095828845">Eginbidea ez dago erabilgarri zure gailuan</translation>
<translation id="7083258188081898530">9. erretilua</translation>
<translation id="7086090958708083563">Erabiltzaileak eskatutako kargatzea</translation>
-<translation id="7087282848513945231">Konderria</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, sakatu tabuladorea eta, ondoren, sakatu Sartu Chrome-ren ezarpenetara joan, eta baimenak eta webguneetan gordetako datuak kudeatzeko</translation>
<translation id="7096937462164235847">Ez dago egiaztatuta webgune honen identitatea.</translation>
<translation id="7101893872976785596">Beldurrezko filmak</translation>
@@ -2200,10 +2210,10 @@ Bestela, pribatutasun-ezarpenek blokeatu egingo dute baimen hori. Baimen honekin
<ph name="LIST_ITEM" />inprimakietan idatzitako informazioa<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Ezin da entregatu helbide horretan. Hautatu beste helbide bat.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Administratzaileak debekatu egin du datu hauek kopiatzea.</translation>
<translation id="7135130955892390533">Erakutsi egoera</translation>
<translation id="7138472120740807366">Entrega-metodoa</translation>
-<translation id="7139724024395191329">Arabiar Emirerri Batuak</translation>
<translation id="7139892792842608322">Erretilu nagusia</translation>
<translation id="714064300541049402">2. aldeko irudia X ardatzaren arabera aldatuta</translation>
<translation id="7152423860607593928">Number-14 (gutun-azala)</translation>
@@ -2420,6 +2430,7 @@ Xehetasun gehiago:
<translation id="7669271284792375604">Webgune honetako erasotzaileak zu engainatzen saia litezke, sarea arakatzea oztopatuko dizuten programak instala ditzazun. Besteak beste, hasierako orria alda lezakete edo ikusten dituzun webguneetan iragarki gehiago erakuts litzakete programok.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Isilpeko gisa markatu diren datuekin gauzatutako ekintzak (ekintza bat saioa hasi denetik). <ph name="BEGIN_LINK" />Lortu informazio gehiago<ph name="END_LINK" />}other{Isilpeko gisa markatu diren datuekin gauzatutako ekintzak (# ekintza saioa hasi denetik). <ph name="BEGIN_LINK" />Lortu informazio gehiago<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">6. postontzia</translation>
+<translation id="7675325315208090829">Kudeatu ordainketa-metodoak…</translation>
<translation id="7676643023259824263">Bilatu arbeleko testua (<ph name="TEXT" />)</translation>
<translation id="7679367271685653708">Ikusi eta kudeatu arakatze-historia Chrome-ren ezarpenetan</translation>
<translation id="7679947978757153706">Beisbola</translation>
@@ -2462,7 +2473,6 @@ Xehetasun gehiago:
<translation id="7766518757692125295">Ingurunea</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Ordena berean, ahoz gora</translation>
-<translation id="777702478322588152">Administrazio-eremua</translation>
<translation id="7791011319128895129">Kaleratzeke</translation>
<translation id="7791196057686275387">Bildu</translation>
<translation id="7791543448312431591">Gehitu</translation>
@@ -2476,7 +2486,7 @@ Xehetasun gehiago:
<translation id="7813600968533626083">Inprimaki-iradokizuna Chrome-tik kendu nahi duzu?</translation>
<translation id="781440967107097262">Arbela partekatu nahi duzu?</translation>
<translation id="7815407501681723534">"<ph name="SEARCH_STRING" />" bilaketaren <ph name="NUMBER_OF_RESULTS" /> <ph name="SEARCH_RESULTS" /> aurkitu dira.</translation>
-<translation id="7822320754433038727">Ehiza eta tiroketak</translation>
+<translation id="7822320754433038727">Ehiza eta tiroa</translation>
<translation id="782886543891417279">Baliteke darabilzun Wi-Fi konexioaren (<ph name="WIFI_NAME" />) saio-hasierako orrira joan behar izatea.</translation>
<translation id="7836231406687464395">Postfix (gutun-azala)</translation>
<translation id="7844689747373518809">{COUNT,plural, =0{Bat ere ez}=1{1 aplikazio (<ph name="EXAMPLE_APP_1" />)}=2{2 aplikazio (<ph name="EXAMPLE_APP_1" /> eta <ph name="EXAMPLE_APP_2" />)}other{# aplikazio (<ph name="EXAMPLE_APP_1" />, <ph name="EXAMPLE_APP_2" /> eta<ph name="AND_MORE" />)}}</translation>
@@ -2553,12 +2563,12 @@ Xehetasun gehiago:
<translation id="8055534648776115597">Lanbide-heziketa eta etengabeko heziketa</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" ez dago behar bezala konfiguratuta. "<ph name="SOFTWARE_NAME" />" desinstalatuta konpondu ohi da arazo hori. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Elikagaien ekoizpena</translation>
-<translation id="8066955247577885446">Arazo bat izan da.</translation>
<translation id="8067872629359326442">Webgune engainagarri batean idatzi duzu pasahitza. Chromium-ek lagundu egin diezazuke. Pasahitza aldatzeko eta Google-ri agian kontua arriskuan dagoela jakinarazteko, egin klik Babestu kontua botoian.</translation>
<translation id="8070439594494267500">Aplikazioaren ikonoa</translation>
<translation id="8074253406171541171">10x13 (gutun-azala)</translation>
<translation id="8075736640322370409">Sortu bizkor Google-ko kalkulu-orri bat</translation>
<translation id="8075898834294118863">Kudeatu webguneen ezarpenak</translation>
+<translation id="8076492880354921740">Fitxak</translation>
<translation id="8078141288243656252">Ezin da oharpenik egin dokumentua biratuta dagoenean</translation>
<translation id="8079031581361219619">Webgunea berriz kargatu nahi duzu?</translation>
<translation id="8081087320434522107">Sedanak</translation>
@@ -2681,6 +2691,7 @@ Xehetasun gehiago:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" />, <ph name="SECOND_LABEL" />, <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ezarpenak</translation>
+<translation id="8428634594422941299">Ados</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, sakatu tabuladorea eta, ondoren, sakatu Sartu Chrome-ren ezarpenetara joan, eta cookieen hobespenak kudeatzeko</translation>
<translation id="8433057134996913067">Hori eginez gero, webgune gehienetako saioa amaituko duzu.</translation>
<translation id="8434840396568290395">Maskotak</translation>
@@ -2778,6 +2789,7 @@ Xehetasun gehiago:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> da <ph name="ORIGIN" /> webgunerako kodea</translation>
<translation id="874918643257405732">Egin fitxa honen laster-marka</translation>
<translation id="8751426954251315517">Saiatu berriro geroago</translation>
+<translation id="8757526089434340176">Google Pay-ren eskaintza bat dago erabilgarri</translation>
<translation id="8758885506338294482">Bideo-jokoen lehiaketak</translation>
<translation id="8759274551635299824">Iraungita dago txartela</translation>
<translation id="87601671197631245">Webgune honek segurtasun-konfigurazio zaharkitu bat erabiltzen du; ondorioz, baliteke zure informazioa (adibidez, pasahitzak, mezuak edo kreditu-txartelak) agerian uztea webgune honetara bidaltzen duzunean.</translation>
@@ -2785,6 +2797,7 @@ Xehetasun gehiago:
<translation id="8763927697961133303">USB bidezko gailua</translation>
<translation id="8763986294015493060">Itxi irekita dauden ezkutuko moduko leiho guztiak</translation>
<translation id="8766943070169463815">Ordainketa seguruetarako kredentzialak autentifikatzeko orria irekita dago</translation>
+<translation id="8767765348545497220">Itxi laguntza-burbuila</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motozikletak</translation>
<translation id="8790007591277257123">&amp;Berregin ezabatzea</translation>
@@ -2797,6 +2810,7 @@ Xehetasun gehiago:
<translation id="8806285662264631610">Bainugelarako eta gorputzerako produktuak</translation>
<translation id="8807160976559152894">Moztu orri bakoitzaren amaieran</translation>
<translation id="8808828119384186784">Chrome-ren ezarpenak</translation>
+<translation id="8813277370772331957">Gogorarazi geroago</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />: sakatu tabuladorea eta, ondoren, sakatu Sartu Chrome eguneratzeko Chrome-ren ezarpenetan</translation>
<translation id="8820817407110198400">Laster-markak</translation>
<translation id="882338992931677877">Eskuzko erretilua</translation>
@@ -2976,6 +2990,7 @@ Xehetasun gehiago:
<translation id="988159990683914416">Garatzailearen konpilazioa</translation>
<translation id="989988560359834682">Editatu helbidea</translation>
<translation id="991413375315957741">mugimenduaren eta argiaren sentsoreak</translation>
+<translation id="992110854164447044">Txartel birtualek benetako txartelak ezkutatzen dituzte, iruzurren aurka babestuago egon daitezen. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Arrosa</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" ez dago behar bezala instalatuta ordenagailuan edo sarean:
diff --git a/chromium/components/strings/components_strings_fa.xtb b/chromium/components/strings/components_strings_fa.xtb
index 59116f8cf5f..ba6fb341dca 100644
--- a/chromium/components/strings/components_strings_fa.xtb
+++ b/chromium/components/strings/components_strings_fa.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">کمک‌هزینه، بورس تحصیلی، و کمک مالی</translation>
<translation id="1048785276086539861">وقتی یادداشت‌ها را ویرایش می‌کنید، این سند به نمای تک‌صÙحه‌ای برمی‌گردد</translation>
<translation id="1050038467049342496">برنامه‌های دیگر را ببندید</translation>
+<translation id="1053959602163383901">قبلاً انتخاب کرده‌اید در وب‌سایت‌هایی Ú©Ù‡ از <ph name="PROVIDER_ORIGIN" /> استÙاده می‌کنند درستی‌سنجی با دستگاه اصالت‌سنجی انجام شود. این ارائه‌دهنده ممکن است اطلاعات روش پرداختتان را ذخیره کرده باشد، Ú©Ù‡ می‌توانید <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;واگرد اÙزودن</translation>
<translation id="1056663316309890343">نرم‌اÙزار عکس</translation>
<translation id="1056898198331236512">اخطار</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">روش تحویل گرÙتن</translation>
<translation id="1281476433249504884">پشته‌ساز ۱</translation>
<translation id="1285320974508926690">این سایت هرگز ترجمه نشود</translation>
+<translation id="1288548991597756084">ذخیره ایمن کارت</translation>
<translation id="1292571435393770077">سینی ۱۶</translation>
<translation id="1292701964462482250">â€Â«Ù†Ø±Ù…‌اÙزاری در رایانه شما مانع از اتصال ایمن Chrome به وب می‌شود» (Ùقط رایانه‌های Windows)</translation>
<translation id="1294154142200295408">انواع مختل٠خط Ùرمان</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;برای برطر٠کردن این خطا، در صÙحه‌ای Ú©Ù‡ می‌خواهید باز کنید روی &lt;strong&gt;اتصال&lt;/strong&gt; کلیک کنید.&lt;/p&gt;</translation>
<translation id="1507780850870535225">طراحی چشم‌انداز</translation>
<translation id="1513706915089223971">Ùهرست ورودی‌های سابقه</translation>
+<translation id="1516097932025103760">رمزگذاری خواهد شد، به‌طور ایمن ذخیره خواهد شد، و کد تأیید کارت هرگز ذخیره نمی‌شود.</translation>
<translation id="1517433312004943670">شماره تلÙÙ† ضروری است</translation>
<translation id="1519264250979466059">تاریخ ساخت</translation>
<translation id="1521159554480556801">هنر نساجی Ùˆ باÙت</translation>
@@ -272,7 +275,7 @@
<translation id="1634828734222219955">مجموع</translation>
<translation id="163669211644121865">آماده‌سازی و برنامه‌ریزی مالیاتی</translation>
<translation id="1638780421120290329">نمی‌توان کارت را ذخیره کرد</translation>
-<translation id="1639239467298939599">بارگیری</translation>
+<translation id="1639239467298939599">در حال بارگیری</translation>
<translation id="1640180200866533862">خط‌مشی‌های کاربر</translation>
<translation id="1640244768702815859">از<ph name="BEGIN_LINK" />صÙحه اصلی سایت دیدن کنید<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">تأخیر خروجی تا</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ذخیره و تکمیل روش‌های پرداخت</translation>
<translation id="1663943134801823270">â€Ú©Ø§Ø±Øªâ€ŒÙ‡Ø§ Ùˆ نشانی‌ها از Chrome‌ هستند. می‌توانید آن‌ها را در <ph name="BEGIN_LINK" />تنظیمات<ph name="END_LINK" /> مدیریت کنید.</translation>
<translation id="1671391448414634642">از این به بعد، صÙحه‌های <ph name="SOURCE_LANGUAGE" />ØŒ به <ph name="TARGET_LANGUAGE" /> ترجمه خواهند شد.</translation>
+<translation id="1673886523110456987">برای استÙاده از پیشنهاد، با <ph name="CARD_DETAIL" /> تماس بگیرید</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> به<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ابتدا لبه کوتاه</translation>
<translation id="168693727862418163">این مقدار خط‌مشی در رابطه با طرح آن تأیید نشده است Ùˆ نادیده گرÙته می‌شود.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">حسگرهای حرکتی</translation>
<translation id="1717494416764505390">صندوق پست ۳</translation>
<translation id="1718029547804390981">این سند بسیار بزرگ است و نمی‌تواند حاشیه‌نویسی شود</translation>
+<translation id="1720941539803966190">بستن آموزش گام‌به‌گام</translation>
<translation id="1721424275792716183">* این Ùیلد اجباری است</translation>
<translation id="1727613060316725209">گواهینامه معتبر است</translation>
<translation id="1727741090716970331">اÙزودن شماره کارت معتبر</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">کبابی و بریانی</translation>
<translation id="2053111141626950936">صÙحه‌های <ph name="LANGUAGE" /> ترجمه نخواهند شد.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{â€ÙˆÙ‚تی این کنترل روشن باشد Ùˆ وضعیت روی Ùعال تنظیم شده باشد، Chrome تعیین می‌کند Ùعالیت مرور اخیر شما بیشتر شبیه کدام «هم‌گروه» یا گروه بزرگ از اÙراد است. تبلیغ‌کنندگان می‌توانند برای آن گروه٠به‌خصوص Ø¢Ú¯Ù‡ÛŒ انتخاب کنند Ùˆ سابقه مرور شما به‌صورت خصوصی در دستگاهتان Ù†Ú¯Ù‡ داشته می‌شود. گروهتان هر روز به‌روزرسانی می‌شود.}=1{â€ÙˆÙ‚تی این کنترل روشن باشد Ùˆ وضعیت روی Ùعال تنظیم شده باشد، Chrome تعیین می‌کند Ùعالیت مرور اخیر شما بیشتر شبیه کدام «هم‌گروه» یا گروه بزرگ از اÙراد است. تبلیغ‌کنندگان می‌توانند برای آن گروه٠به‌خصوص Ø¢Ú¯Ù‡ÛŒ انتخاب کنند Ùˆ سابقه مرور شما به‌صورت خصوصی در دستگاهتان Ù†Ú¯Ù‡ داشته می‌شود. گروهتان هر روز به‌روزرسانی می‌شود.}one{â€ÙˆÙ‚تی این کنترل روشن باشد Ùˆ وضعیت روی Ùعال تنظیم شده باشد، Chrome تعیین می‌کند Ùعالیت مرور اخیر شما بیشتر شبیه کدام «هم‌گروه» یا گروه بزرگ از اÙراد است. تبلیغ‌کنندگان می‌توانند برای آن گروه٠به‌خصوص Ø¢Ú¯Ù‡ÛŒ انتخاب کنند Ùˆ سابقه مرور شما به‌صورت خصوصی در دستگاهتان Ù†Ú¯Ù‡ داشته می‌شود. گروهتان هر {NUM_DAYS} روز یکبار به‌روزرسانی می‌شود.}other{â€ÙˆÙ‚تی این کنترل روشن باشد Ùˆ وضعیت روی Ùعال تنظیم شده باشد، Chrome تعیین می‌کند Ùعالیت مرور اخیر شما بیشتر شبیه کدام «هم‌گروه» یا گروه بزرگ از اÙراد است. تبلیغ‌کنندگان می‌توانند برای آن گروه٠به‌خصوص Ø¢Ú¯Ù‡ÛŒ انتخاب کنند Ùˆ سابقه مرور شما به‌صورت خصوصی در دستگاهتان Ù†Ú¯Ù‡ داشته می‌شود. گروهتان هر {NUM_DAYS} روز یکبار به‌روزرسانی می‌شود.}}</translation>
-<translation id="2053553514270667976">کد پستی</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{۱ پیشنهاد}one{# پیشنهاد}other{# پیشنهاد}}</translation>
+<translation id="2066915425250589881">درخواست حذ٠ارائه کنید</translation>
<translation id="2068528718802935086">نوزادان و کودکان نوپا</translation>
<translation id="2071156619270205202">این کارت برای شماره کارت مجازی واجدشرایط نیست.</translation>
<translation id="2071692954027939183">اعلان‌ها به‌طور خودکار مسدود شدند چون شما معمولاً اجازه نمی‌دهید نمایش داده شوند</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">â€Ø¯Ú©Ù…Ù‡ مدیریت همگام‌سازی، کلید «ورود» را Ùشار دهید تا اطلاعاتی را Ú©Ù‡ همگام‌سازی می‌کنید در تنظیمات Chrome مدیریت کنید</translation>
<translation id="2091887806945687916">صدا</translation>
<translation id="2094505752054353250">عدم تطابق دامنه</translation>
-<translation id="2096368010154057602">اداره</translation>
<translation id="2099652385553570808">سه منگنه در چپ</translation>
<translation id="2101225219012730419">نسخه:</translation>
<translation id="2102134110707549001">پیشنهاد گذرواژه قوی…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">Ùوتبال آمریکایی</translation>
<translation id="2187317261103489799">تشخیص (پیش‌Ùرض)</translation>
<translation id="2188375229972301266">چندین سوراخ در پایین</translation>
-<translation id="2188852899391513400">â€Ú¯Ø°Ø±ÙˆØ§Ú˜Ù‡â€ŒØ§ÛŒ Ú©Ù‡ اکنون استÙاده کردید مورد سرقت قرار گرÙته است. برای Ø­Ùظ امنیت حساب‌هایتان، «مدیر گذرواژه Google» توصیه می‌کند گذرواژه‌تان را همین‌حالا تغییر دهید Ùˆ سپس گذرواژه‌های ذخیره‌شده‌تان را بررسی کنید.</translation>
<translation id="219906046732893612">نوسازی منزل</translation>
<translation id="2202020181578195191">سال انقضای معتبری وارد کنید</translation>
<translation id="22081806969704220">سینی ۳</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">ویرایش Ùایل</translation>
<translation id="2215963164070968490">سگ</translation>
<translation id="2218879909401188352">درحال‌حاضر مهاجم‌ها در <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> می‌توانند برنامه‌های خطرناکی نصب کنند Ú©Ù‡ به دستگاهتان آسیب بزند، هزینه‌های پنهانی به صورت‌حساب دستگاه همراهتان اضاÙÙ‡ کند یا اطلاعات شخصی‌تان را سرقت کند. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">بازراه‌اندازی آموزش گام‌به‌گام</translation>
+<translation id="2219735899272417925">بازنشانی دستگاه لازم است</translation>
<translation id="2224337661447660594">اتصال اینترنت وجود ندارد</translation>
<translation id="2230458221926704099">با استÙاده از <ph name="BEGIN_LINK" />برنامه عیب‌یابی<ph name="END_LINK" />ØŒ مشکل اتصالتان را برطر٠کنید</translation>
<translation id="2239100178324503013">اکنون ارسال شود</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">مجاز نیست (پیش‌Ùرض)</translation>
<translation id="2512413427717747692">â€Chrome را به‌عنوان دکمه مرورگر پیش‌Ùرض تنظیم کنید. برای تنظیم Chrome به‌عنوان مرورگر پیش‌Ùرض سیستم در تنظیمات iOSØŒ کلید ورود را Ùشار دهید</translation>
<translation id="2515629240566999685">بررسی سیگنال در منطقه‌تان</translation>
+<translation id="2515761554693942801">â€Ù‚بلاً انتخاب کرده‌اید در وب‌سایت‌هایی Ú©Ù‡ از <ph name="PROVIDER_ORIGIN" /> استÙاده می‌کنند درستی‌سنجی با Touch ID انجام شود. این ارائه‌دهنده ممکن است اطلاعات روش پرداختتان را ذخیره کرده باشد، Ú©Ù‡ می‌توانید <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">منگنه در پایین سمت راست</translation>
<translation id="2521736961081452453">ایجاد Ùرم</translation>
<translation id="2523886232349826891">Ùقط در این دستگاه ذخیره شد</translation>
<translation id="2524461107774643265">اÙزودن اطلاعات بیشتر</translation>
<translation id="2529899080962247600">این Ùیلد نباید بیشتر از <ph name="MAX_ITEMS_LIMIT" /> ورودی داشته باشد. همه ورودی‌های بعدی نادیده گرÙته خواهند شد.</translation>
+<translation id="253493526287553278">دیدن جزئیات کد تبلیغاتی</translation>
<translation id="2535585790302968248">برگه ناشناس جدیدی برای مرور خصوصی باز می‌شود</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{و ۱ مورد دیگر}one{و # مورد دیگر}other{و # مورد دیگر}}</translation>
<translation id="2536110899380797252">اÙزودن نشانی</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">عکاسی و هنرهای دیجیتال</translation>
<translation id="2601150049980261779">Ùیلم عاشقانه</translation>
<translation id="2604589665489080024">موسیقی پاپ</translation>
-<translation id="2609632851001447353">انواع مختلÙ</translation>
<translation id="2610561535971892504">برای کپی کردن، کلیک کنید</translation>
<translation id="2617988307566202237">â€Chrome اطلاعات زیر را <ph name="BEGIN_EMPHASIS" />ذخیره نخواهد کرد<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">تاریخ تولد و روز نام‌گذاری</translation>
<translation id="2677748264148917807">خروج</translation>
+<translation id="2679714844901977852">â€Ø¨Ø±Ø§ÛŒ تسویه‌حساب ایمن Ùˆ سریع‌تر، اطلاعات کارت Ùˆ صورت‌حساب را در «حساب Google» <ph name="USER_EMAIL" /> ذخیره کنید</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">بهترین اندازه</translation>
<translation id="2688969097326701645">بله، ادامه می‌دهم</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">â€Ø±Ø³Ø§Ù†Ù†Ø¯Ù‡ خدمات اینترنتی (ISP)</translation>
<translation id="2856444702002559011">شاید مهاجم‌ها در تلاش باشند اطلاعات شما (مانند گذرواژه‌ها، پیام‌ها یا کارت‌های اعتباری) را از <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> سرقت کنند. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">این سایتْ آگهی‌های مزاحم یا گمراه‌کننده نشان می‌دهد.</translation>
-<translation id="286512204874376891">کارت مجازی با پنهان کردن کارت واقعی‌تان از شما دربرابر کلاهبرداری‌های احتمالی محاÙظت می‌کند. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">دوستانه</translation>
<translation id="28761159517501904">Ùیلم</translation>
<translation id="2876489322757410363">برای پرداخت ازطریق یک برنامه خارجی، از «حالت ناشناس» خارج می‌شوید. ادامه می‌دهید؟</translation>
@@ -775,7 +781,7 @@
<translation id="306573536155379004">بازی شروع شد.</translation>
<translation id="3068991664510324412">رایانه و بازی ویدیویی</translation>
<translation id="3080254622891793721">ترسیمی</translation>
-<translation id="3082007635241601060">â€Ø¨Ù‡ Google اجازه دهید از سبدهای خریدتان استÙاده کند تا درصورت موجود بودن تخÙÛŒÙ‌های شخصی‌شده، آن‌ها را پیدا کند</translation>
+<translation id="3082007635241601060">â€Ø¨Ù‡ Google اجازه دهید از سبدهای خریدتان استÙاده کند تا درصورت موجود بودن تخÙÛŒÙ‌های شخصی‌â€Ø³Ø§Ø²ÛŒâ€Œâ€Ø´Ø¯Ù‡ØŒ آن‌ها را پیدا کند</translation>
<translation id="3086579638707268289">Ùعالیتتان در وب تحت‌نظارت است</translation>
<translation id="3087734570205094154">پایین</translation>
<translation id="3095940652251934233">Statement</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">دیسک</translation>
<translation id="3162559335345991374">â€Ø´Ø¨Ú©Ù‡ Wi-Fi مورد استÙاده‌تان احتمالاً نیاز دارد Ú©Ù‡ به یک صÙحه ورود به سیستم بروید.</translation>
<translation id="3169472444629675720">کش٠کردن</translation>
-<translation id="3174168572213147020">ایسلند</translation>
<translation id="3176929007561373547">تنظیمات پروکسی‌ را بررسی کنید یا با سرپرست شبکه‌‌تان تماس بگیرید تا
مطمئن شوید سرور پروکسی کار می‌کند. اگر مطمئن نیستید که باید از سرور
پروکسی استÙاده کنید:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">خطای ساعت</translation>
<translation id="3369459162151165748">قطعات و لوازم جانبی خودرو</translation>
<translation id="3371064404604898522">â€ØªÙ†Ø¸ÛŒÙ… Chrome به‌عنوان مرورگر پیش‌Ùرض</translation>
-<translation id="3371076217486966826"><ph name="URL" /> می‌خواهد کارهای زیر را انجام دهد:
- • ایجاد نقشه سه‌بعدی از محیط اطراÙتان Ùˆ ردیابی موقعیت دوربین
- • استÙاده از دوربین</translation>
<translation id="337363190475750230">لغو مجوز شد</translation>
<translation id="3375754925484257129">â€Ø§Ø¬Ø±Ø§ÛŒ «بررسی ایمنی» در Chrome</translation>
<translation id="3377144306166885718">سرور از نسخه منسوخ «امنیت لایه انتقال» استÙاده می‌کرد.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">نشانی تحویل کالا</translation>
<translation id="3402261774528610252">اتصال استÙاده‌شده برای بار کردن این سایت از «امنیت لایه انتقال» نسخه Û±.Û° یا Û±.Û± استÙاده می‌کرد Ú©Ù‡ منسوخ شده است Ùˆ در آینده غیرÙعال خواهد شد. بعد از غیرÙعال شدن، کاربران نمی‌توانند این سایت را بار کنند. سرور باید «امنیت لایه انتقال» نسخه Û±.Û² یا بالاتر را Ùعال کند.</translation>
<translation id="3405664148539009465">سÙارشی کردن قلم‌ها</translation>
+<translation id="3407789382767355356">ورود به سیستم شخص ثالث</translation>
<translation id="3409896703495473338">مدیریت تنظیمات امنیتی</translation>
<translation id="3414952576877147120">اندازه:</translation>
<translation id="3417660076059365994">â€Ùایل‌هایی Ú©Ù‡ بارگذاری یا پیوست می‌کنید برای تجریه‌وتحلیل به Google Cloud یا اشخاص ثالث ارسال می‌شود. مثلاً ممکن است ازنظر وجود داده‌های حساس یا بداÙزار اسکن شوند.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">Ùهرست Ùیلم Ùˆ ساعات کار سینما</translation>
<translation id="3479552764303398839">حالا نه</translation>
<translation id="3484560055331845446">â€Ù…Ù…Ú©Ù† است دسترسی به حساب Google را از دست بدهید. Chrome توصیه می‌کند اکنون گذرواژه‌تان را تغییر دهید. از شما خواسته می‌شود به سیستم وارد شوید.</translation>
+<translation id="3484861421501147767">یادآوری: کد تبلیغاتی ذخیره‌شده دردسترس است</translation>
<translation id="3487845404393360112">سینی ۴</translation>
<translation id="3495081129428749620">یاÙتن در صÙحه
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">مدیریت</translation>
<translation id="3816482573645936981">مقدار (جایگزین‌شده)</translation>
<translation id="382518646247711829">اگر از سرور پراکسی استÙاده می‌کنید...</translation>
+<translation id="3826050100957962900">ورود به سیستم شخص ثالث</translation>
<translation id="3827112369919217609">دقیق</translation>
<translation id="3827666161959873541">Ùیلم خانوادگی</translation>
<translation id="3828924085048779000">گذرعبارت خالی مجاز نیست.</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">به‌روزرسانی تاریخ و زمان</translation>
<translation id="3858860766373142691">نام</translation>
<translation id="3872834068356954457">علم</translation>
+<translation id="3875783148670536197">نمایش روش انجام کار</translation>
<translation id="3881478300875776315">نمایش خطوط کمتر</translation>
<translation id="3884278016824448484">شناسه دستگاه یکسان نیست</translation>
-<translation id="3885155851504623709">استان</translation>
<translation id="388632593194507180">نظارت تشخیص داده شد</translation>
<translation id="3886948180919384617">پشته‌ساز ۳</translation>
<translation id="3890664840433101773">اÙزودن ایمیل</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> مسدود شده است</translation>
<translation id="3978338123949022456">حالت جستجو، Ù¾Ùرسمانی تایپ کنید Ùˆ برای جستجو با <ph name="KEYWORD_SUFFIX" />ØŒ کلید ورود را Ùشار دهید</translation>
<translation id="398470910934384994">پرندگان</translation>
+<translation id="3985750352229496475">مدیریت نشانی‌ها…</translation>
<translation id="3986705137476756801">«زیرنویس ناشنوایان زنده» Ùعلاً خاموش شود</translation>
<translation id="3987940399970879459">کمتر از ۱ مگابایت</translation>
<translation id="3990250421422698716">â€Ø¢Ùست Z Ø´Ú©Ù„</translation>
+<translation id="3992684624889376114">درباره این صÙحه</translation>
<translation id="3996311196211510766">سایت <ph name="ORIGIN" /> درخواست کرده است خط‌مشی مبدأ
برای همه درخواست‌هایی به سمت آن می‌رود اعمال شود، اما این خط‌مشی درحال‌حاضر نمی‌تواند اعمال شود.</translation>
<translation id="4006465311664329701">â€Ø±ÙˆØ´â€ŒÙ‡Ø§ÛŒ پرداخت، پیشنهادها، Ùˆ نشانی‌های استÙاده‌شده در Google Pay</translation>
@@ -1227,6 +1234,7 @@
<translation id="4305666528087210886">دسترسی به Ùایل شما ممکن نبود</translation>
<translation id="4306529830550717874">نشانی ذخیره شود؟</translation>
<translation id="4306812610847412719">بریده‌دان</translation>
+<translation id="4310070645992025887">«سÙرهای جستجو» را جستجو کنید</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">مسدود کردن (پیش‌Ùرض)</translation>
<translation id="4314815835985389558">مدیریت همگام‌سازی</translation>
@@ -1277,6 +1285,7 @@
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">استÙاده از پروکسی غیرÙعال است اما یک پیکربندی خاص برای پروکسی تعیین شده است.</translation>
<translation id="4441832193888514600">نادیده گرÙته شد زیرا این خط‌مشی Ùقط می‌تواند به‌عنوان خط‌مشی کاربر ابری تنظیم شود.</translation>
+<translation id="4442470707340296952">â€Ø¨Ø±Ú¯Ù‡â€ŒÙ‡Ø§ÛŒ Chrome</translation>
<translation id="4450893287417543264">دیگر نشان داده نشود</translation>
<translation id="4451135742916150903">â€Ù…ی‌تواند برای اتصال به دستگاه‌های HID درخواست دهد</translation>
<translation id="4452328064229197696">â€Ú¯Ø°Ø±ÙˆØ§Ú˜Ù‡â€ŒØ§ÛŒ Ú©Ù‡ اکنون استÙاده کردید مورد سرقت قرار گرÙته است. برای Ø­Ùظ امنیت حساب‌هایتان، «مدیر گذرواژه Google» توصیه می‌کند گذرواژه‌های ذخیره‌شده‌تان را بررسی کنید.</translation>
@@ -1415,6 +1424,7 @@
<translation id="483241715238664915">روشن کردن هشدارها</translation>
<translation id="4834250788637067901">â€Ø±ÙˆØ´â€ŒÙ‡Ø§ÛŒ پرداخت، پیشنهادها، Ùˆ نشانی‌های استÙاده‌شده در Google Pay</translation>
<translation id="4838327282952368871">رؤیایی</translation>
+<translation id="4839087176073128681">â€Ø¯Ùعه بعد سریع‌تر پرداخت کنید Ùˆ با امنیت Google Ú©Ù‡ امنیتی پیشرو در این صنعت است از کارتتان محاÙظت کنید.</translation>
<translation id="4840250757394056958">â€Ù…شاهده سابقه Chrome</translation>
<translation id="484462545196658690">خودکار</translation>
<translation id="484671803914931257">دریاÙت تخÙی٠از <ph name="MERCHANT_NAME" /> Ùˆ موارد دیگر</translation>
@@ -1477,6 +1487,7 @@
<translation id="5011561501798487822">زبان شناسایی‌شده</translation>
<translation id="5015510746216210676">نام ماشین:</translation>
<translation id="5017554619425969104">نوشتاری که کپی کرده‌اید</translation>
+<translation id="5017828934289857214">بعداً به من یادآوری شود</translation>
<translation id="5018422839182700155">این صÙحه نمی‌تواند باز شود</translation>
<translation id="5019198164206649151">پشتیبان‌گیری ذخیره در وضعیت نادرست است</translation>
<translation id="5020776957610079374">موسیقی ملل</translation>
@@ -1496,6 +1507,7 @@
<translation id="5051305769747448211">کمدی زنده</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{برای ارسال این Ùایل ازطریق «هم‌رسانی با اطراÙ»، در دستگاهتان Ùضا (<ph name="DISK_SPACE_SIZE" />) آزاد کنید}one{برای ارسال این Ùایل ازطریق «هم‌رسانی با اطراÙ»، در دستگاهتان Ùضا (<ph name="DISK_SPACE_SIZE" />) آزاد کنید}other{برای ارسال این Ùایل‌ها ازطریق «هم‌رسانی با اطراÙ»، در دستگاهتان Ùضا (<ph name="DISK_SPACE_SIZE" />) آزاد کنید}}</translation>
<translation id="5056549851600133418">مقالاتی برای شما</translation>
+<translation id="5060483733937416656">â€Ù‚بلاً انتخاب کرده‌اید در وب‌سایت‌هایی Ú©Ù‡ از <ph name="PROVIDER_ORIGIN" /> استÙاده می‌کنند درستی‌سنجی با Windows Hello انجام شود. این ارائه‌دهنده ممکن است اطلاعات روش پرداختتان را ذخیره کرده باشد، Ú©Ù‡ می‌توانید <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">منظورتان <ph name="LOOKALIKE_DOMAIN" /> بود؟</translation>
<translation id="5066056036849835175">سابقه چاپ</translation>
<translation id="5068234115460527047">صندوق پوشش ریسک</translation>
@@ -1509,10 +1521,8 @@
<translation id="5087286274860437796">در حال حاضر گواهی سرور معتبر نیست.</translation>
<translation id="5087580092889165836">اÙزودن کارت</translation>
<translation id="5088142053160410913">پیام به اپراتور</translation>
-<translation id="5089810972385038852">ایالت</translation>
<translation id="5093232627742069661">â€ØªØ§Ø®ÙˆØ±Ø¯Ú¯ÛŒ Z Ø´Ú©Ù„</translation>
<translation id="5094747076828555589">â€Ø§ÛŒÙ† سرور نتوانست اثبات کند Ú©Ù‡ این <ph name="DOMAIN" /> است؛ گواهی امنیت آن مورداعتماد Chromium نیست. علت این موضوع می‌توان پیکربندی اشتباه باشد یا مهاجمی اتصال شما را قطع کرده است.</translation>
-<translation id="5095208057601539847">استان</translation>
<translation id="5097099694988056070">â€Ø¢Ù…ار دستگاه مانند استÙاده از CPU یا RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">سایت امن نیست</translation>
@@ -1550,6 +1560,7 @@
<translation id="5171045022955879922">â€Ø¬Ø³ØªØ¬Ùˆ یا تایپ URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">دستگاه</translation>
+<translation id="5177076414499237632">آشنایی با منبع Ùˆ موضوع این صÙحه</translation>
<translation id="5179510805599951267">به زبان <ph name="ORIGINAL_LANGUAGE" /> نیست؟ گزارش این خطا</translation>
<translation id="518639307526414276">غذای حیوانات خانگی و وسایل مراقبت از آن‌ها</translation>
<translation id="5190835502935405962">نوار نشانک‌ها</translation>
@@ -1710,6 +1721,7 @@
<translation id="5624120631404540903">مدیریت گذرواژه‌ها</translation>
<translation id="5629630648637658800">تنظیمات خط‌مشی بارگیری نشد</translation>
<translation id="5631439013527180824">نشانه مدیریت دستگاه نامعتبر است</translation>
+<translation id="5632485077360054581">نمایش روش انجام کار</translation>
<translation id="5633066919399395251">شاید درحال‌حاضر مهاجم‌ها در <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> در تلاش باشند برنامه‌های خطرناکی در رایانه‌تان نصب کنند که اطلاعات شما (مانند عکس‌ها، گذرواژه‌ها، پیام‌ها و کارت‌های اعتباری) را به سرقت می‌برند یا حذ٠می‌کنند. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">محتوای Ùریب‌دهنده مسدود شد.</translation>
<translation id="5633259641094592098">Ùیلم کالت Ùˆ مستقل</translation>
@@ -1827,6 +1839,7 @@
<translation id="5989320800837274978">â€Ø³Ø±ÙˆØ± پروکسی ثابت Ùˆ URL اسکریپت pac. تعیین نشده‌اند.</translation>
<translation id="5992691462791905444">â€ØªØ§Ø®ÙˆØ±Ø¯Ú¯ÛŒ Z Ø´Ú©Ù„ مهندسی</translation>
<translation id="5995727681868049093">â€Ù…دیریت اطلاعات، حریم خصوصی، Ùˆ امنیت در «حساب Google»</translation>
+<translation id="5997247540087773573">â€Ú¯Ø°Ø±ÙˆØ§Ú˜Ù‡â€ŒØ§ÛŒ Ú©Ù‡ اکنون استÙاده کردید در سرقت اطلاعات شبکه پیدا شده است. برای Ø­Ùظ امنیت حساب‌هایتان، «مدیر گذرواژه Google» توصیه می‌کند گذرواژه‌تان را همین‌حالا تغییر دهید Ùˆ گذرواژه‌های ذخیره‌شده‌تان را بررسی کنید.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> نتیجه برای «<ph name="SEARCH_TEXT" />»</translation>
<translation id="6006484371116297560">کلاسيک</translation>
<translation id="6008122969617370890">â€ØªØ±ØªÛŒØ¨ N تا Û±</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">â€Ø¨Ø±Ø§ÛŒ اینکه دÙعات بعد پرداخت سریع‌تری داشته باشید، اطلاعات کارت Ùˆ نشانی صورت‌حسابتان را در حساب Google خود ذخیره کنید.</translation>
<translation id="6279183038361895380">برای نمایش نشان‌گر |<ph name="ACCELERATOR" />| را Ùشار دهید</translation>
<translation id="6280223929691119688">تحویل به این نشانی ممکن نیست. نشانی دیگری را انتخاب کنید.</translation>
-<translation id="6282194474023008486">کد پستی</translation>
<translation id="6285507000506177184">â€Ø¯Ú©Ù…Ù‡ «مدیریت بارگیری‌ها در Chrome»؛ برای مدیریت Ùایل‌هایی Ú©Ù‡ در Chrome بارگیری کرده‌اید، کلید «ورود» را Ùشار دهید</translation>
<translation id="6289939620939689042">رنگ صÙحه</translation>
<translation id="6290238015253830360">مقاله‌های پیشنهادی شما در اینجا نشان داده می‌شوند</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026">کمتر از <ph name="SIZE" /> از Ùضا را آزاد می‌کند. ممکن است برخی از سایت‌ها در بازدیدهای بعدی کندتر بارگیری شوند.</translation>
<translation id="6337534724793800597">Ùیلتر کردن خط‌مشی‌ها براساس نام</translation>
<translation id="6340739886198108203">خط‌مشی سرپرست توصیه می‌کند وقتی محتوای محرمانه قابل‌مشاهده است نماگرÙت نگیرید یا ضبط نکنید:</translation>
+<translation id="6348220984832452017">گونه‌های Ùعال</translation>
<translation id="6349101878882523185">نصب <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{هیچ‌کدام}=1{۱ گذرواژه (برای <ph name="DOMAIN_LIST" />، همگام‌سازی‌شده)}=2{۲ گذرواژه (برای <ph name="DOMAIN_LIST" />، همگام‌سازی‌شده)}one{# گذرواژه (برای <ph name="DOMAIN_LIST" />، همگام‌سازی‌شده)}other{# گذرواژه (برای <ph name="DOMAIN_LIST" />، همگام‌سازی‌شده)}}</translation>
<translation id="6355392890578844978">â€Ø´Ø±Ú©Øª یا سازمان دیگری این مرورگر را مدیریت نمی‌کند. ممکن است Ùعالیت‌های انجام‌شده در این دستگاه خارج از Chromium مدیریت شود. <ph name="BEGIN_LINK" />بیشتر بدانید<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">â€ØªØºÛŒÛŒØ± گذرواژه Google</translation>
<translation id="6433490469411711332">ویرایش اطلاعات تماس</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> از اتصال خودداری کرد.</translation>
-<translation id="6438025220577812695">خودم تغییر می‌دهم</translation>
<translation id="6440503408713884761">نادیده گرÙته شد</translation>
<translation id="6443406338865242315">اÙزونه‌ها Ùˆ اÙزایه‌هایی Ú©Ù‡ نصب کرده‌اید</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">وراثت</translation>
<translation id="6831043979455480757">ترجمه</translation>
<translation id="6833752742582340615">â€Ø¨Ø±Ø§ÛŒ تسویه‌حساب ایمن Ùˆ سریع‌تر، اطلاعات کارت Ùˆ صورت‌حساب را در «حساب Google» خودتان ذخیره کنید</translation>
-<translation id="6839929833149231406">ناحیه</translation>
<translation id="6846340164947227603">استÙاده از شماره کارت مجازی...</translation>
<translation id="6852204201400771460">برنامه تازه‌سازی شود؟</translation>
+<translation id="6857776781123259569">مدیریت گذرواژه‌ها…</translation>
<translation id="686485648936420384">منابع مصرÙ‌کننده</translation>
<translation id="6865412394715372076">درحال‌حاضر نمی‌توان این کارت را به تأیید رساند</translation>
<translation id="6869334554832814367">وام شخصی</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">دستگاه</translation>
<translation id="696703987787944103">حسی</translation>
<translation id="6968269510885595029">استÙاده از کلید امنیتی</translation>
-<translation id="6970216967273061347">منطقه</translation>
<translation id="6971439137020188025">â€Ø§ÛŒØ¬Ø§Ø¯ سریع ارائه جدید در «اسلایدنگار Google»</translation>
<translation id="6972629891077993081">â€Ø¯Ø³ØªÚ¯Ø§Ù‡â€ŒÙ‡Ø§ÛŒ HID</translation>
<translation id="6973656660372572881">â€Ù‡Ù… سرورهای پروکسی ثابت Ùˆ هم آدرس اسکریپت pac. مشخص شده‌اند.</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">این ویژگی در دستگاهتان دردسترس نیست.</translation>
<translation id="7083258188081898530">سینی ۹</translation>
<translation id="7086090958708083563">بارگذاری درخواست‌شده توسط کاربر</translation>
-<translation id="7087282848513945231">بخش/شهرستان</translation>
<translation id="7095139009144195559">â€<ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />ØŒ کلید «جهش» Ùˆ سپس «ورود» را Ùشار دهید تا اجازه‌ها Ùˆ داده‌های ذخیره‌شده در سایت‌ها را در تنظیمات Chrome مدیریت کنید</translation>
<translation id="7096937462164235847">هویت این وب‌سایت به‌تأیید نرسیده است.</translation>
<translation id="7101893872976785596">Ùیلم ترسناک</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />اطلاعات واردشده در Ùرم‌ها<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">ارسال به این نشانی ممکن نیست. نشانی دیگری را انتخاب کنید.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /><ph name="DETAILS" /></translation>
<translation id="7132939140423847331">سرپرست کپی شدن این داده‌ها را ممنوع کرده است.</translation>
<translation id="7135130955892390533">نمایش وضعیت</translation>
<translation id="7138472120740807366">روش تحویل</translation>
-<translation id="7139724024395191329">امارات</translation>
<translation id="7139892792842608322">سینی اصلی</translation>
<translation id="714064300541049402">â€Ø±ÙˆÛŒ ۲، تغییر جهت تصویر حول محور X</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">مهاجمان در این سایت ممکن است تلاش کنند شما را با نصب برنامه‌هایی Ú©Ù‡ به تجربه مرور شما آسیب می‌رساند، Ùریب دهند (مثلاً با تغییر دادن صÙحه اصلی شما یا با نشان دادن آگهی‌های بیش از حد در سایت‌هایی Ú©Ù‡ بازدید می‌کنید).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{اقدامات انجام‌شده درخصوص داده‌های پرچم‌گذاری‌شده به‌عنوان محرمانه (یک اقدام از زمان ورود به سیستم). <ph name="BEGIN_LINK" />بیشتر بدانید<ph name="END_LINK" />}one{اقدامات انجام‌شده درخصوص داده‌های پرچم‌گذاری‌شده به‌عنوان محرمانه (# اقدام از زمان ورود به سیستم). <ph name="BEGIN_LINK" />بیشتر بدانید<ph name="END_LINK" />}other{اقدامات انجام‌شده درخصوص داده‌های پرچم‌گذاری‌شده به‌عنوان محرمانه (# اقدام از زمان ورود به سیستم). <ph name="BEGIN_LINK" />بیشتر بدانید<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">صندوق پست ۶</translation>
+<translation id="7675325315208090829">مدیریت روش‌های پرداخت…</translation>
<translation id="7676643023259824263">جستجوی نوشتار بریده‌دان، <ph name="TEXT" /></translation>
<translation id="7679367271685653708">â€Ù…شاهده Ùˆ مدیریت سابقه مرور در تنظیمات Chrome</translation>
<translation id="7679947978757153706">بیسبال</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">«اسکیرت»</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">ترتیب یکسان، روبه‌بالا</translation>
-<translation id="777702478322588152">اداره ریاست</translation>
<translation id="7791011319128895129">منتشرنشده</translation>
<translation id="7791196057686275387">بسته</translation>
<translation id="7791543448312431591">اÙزودن</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">آموزش شغلی و مداوم</translation>
<translation id="8057711352706143257">«<ph name="SOFTWARE_NAME" />» درست پیکربندی نمی‌شود. معمولاً حذÙ‌ نصب «<ph name="SOFTWARE_NAME" />» مشکل را برطر٠می‌کند. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">تولید موادغذایی</translation>
-<translation id="8066955247577885446">متأسÙیم، مشکلی پیش آمد</translation>
<translation id="8067872629359326442">â€Ø§Ø®ÛŒØ±Ø§Ù‹ گذرواژه‌تان را در سایتی Ùریب‌کار وارد کرده‌اید. Chromium می‌تواند Ú©Ù…Ú© کند. برای اینکه گذرواژه‌تان را تغییر دهید Ùˆ به Google اطلاع دهید Ú©Ù‡ شاید حسابتان درمعرض خطر باشد، روی «محاÙظت از حساب» کلیک کنید.</translation>
<translation id="8070439594494267500">نماد برنامه</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">â€Ø§ÛŒØ¬Ø§Ø¯ سریع «کاربرگ‌نگار Google» جدید</translation>
<translation id="8075898834294118863">مدیریت تنظیمات سایت</translation>
+<translation id="8076492880354921740">برگه‌ها</translation>
<translation id="8078141288243656252">در حالت چرخش نمی‌توان حاشیه‌نویسی کرد</translation>
<translation id="8079031581361219619">سایت تازه‌سازی شود؟</translation>
<translation id="8081087320434522107">سدان</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">تنظیمات</translation>
+<translation id="8428634594422941299">متوجه شدم</translation>
<translation id="8431194080598727332">â€<ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />ØŒ کلید «جهش» Ùˆ سپس «ورود» را Ùشار دهید تا اولویت‌های Ú©ÙˆÚ©ÛŒ را در تنظیمات Chrome مدیریت کنید</translation>
<translation id="8433057134996913067">با این کار از سیستم بیشتر وب‌سایت‌ها خارج می‌شوید.</translation>
<translation id="8434840396568290395">حیوان خانگی</translation>
@@ -2792,6 +2803,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> کد شما برای <ph name="ORIGIN" /> است</translation>
<translation id="874918643257405732">نشانک‌گذاری این برگه</translation>
<translation id="8751426954251315517">لطÙاً بعداً دوباره امتحان کنید</translation>
+<translation id="8757526089434340176">â€Ù¾ÛŒØ´Ù†Ù‡Ø§Ø¯ÛŒ از Google Pay دردسترس است</translation>
<translation id="8758885506338294482">بازی ویدیویی رقابتی</translation>
<translation id="8759274551635299824">کارت منقضی شده است</translation>
<translation id="87601671197631245">پیکربندی امنیتی این سایت قدیمی است و ممکن است اطلاعاتتان (برای مثال، گذرواژه‌ها، پیام‌ها، یا کارت‌های اعتباری) هنگام ارسال شدن به این سایت، درمعرض خطر قرار گیرد.</translation>
@@ -2799,6 +2811,7 @@
<translation id="8763927697961133303">â€Ø¯Ø³ØªÚ¯Ø§Ù‡ USB</translation>
<translation id="8763986294015493060">بستن همه پنجره‌های ناشناسی که درحال‌حاضر باز هستند</translation>
<translation id="8766943070169463815">برگ اصالت‌سنجی اطلاعات کاربری پرداخت امن باز شد</translation>
+<translation id="8767765348545497220">بستن حبابک راهنما</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">موتورسیکلت</translation>
<translation id="8790007591277257123">&amp;انجام مجدد حذÙ</translation>
@@ -2811,6 +2824,7 @@
<translation id="8806285662264631610">محصولات حمام و بدن</translation>
<translation id="8807160976559152894">برش دادن بعد از چاپ هر صÙحه</translation>
<translation id="8808828119384186784">â€ØªÙ†Ø¸ÛŒÙ…ات Chrome</translation>
+<translation id="8813277370772331957">بعداً به من یادآوری شود</translation>
<translation id="8816395686387277279">â€<ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />ØŒ برای به‌روزرسانی Chrome ازطریق تنظیمات ChromeØŒ کلید Tab (جهش) Ùˆ سپس Enter (ورود) را Ùشار دهید</translation>
<translation id="8820817407110198400">نشانک‌ها</translation>
<translation id="882338992931677877">شیار دستی</translation>
@@ -2990,6 +3004,7 @@
<translation id="988159990683914416">ساخت برنامه‌نویس</translation>
<translation id="989988560359834682">ویرایش آدرس</translation>
<translation id="991413375315957741">حسگرهای نوری یا حرکتی</translation>
+<translation id="992110854164447044">کارت مجازی با پنهان کردن کارت واقعی‌تان، از شما دربرابر کلاهبرداری‌های احتمالی محاÙظت می‌کند. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">صورتی</translation>
<translation id="992432478773561401">â€Â«<ph name="SOFTWARE_NAME" />» به‌درستی در رایانه یا شبکه‌تان پیکربندی نشد:
diff --git a/chromium/components/strings/components_strings_fi.xtb b/chromium/components/strings/components_strings_fi.xtb
index e17fc1a01e6..384fd90e4c0 100644
--- a/chromium/components/strings/components_strings_fi.xtb
+++ b/chromium/components/strings/components_strings_fi.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Apurahat, stipendit ja taloudellinen tuki</translation>
<translation id="1048785276086539861">Kun muokkaat merkintöjä, tämä dokumentti palaa yhden sivun näkymään</translation>
<translation id="1050038467049342496">Sulje muita sovelluksia.</translation>
+<translation id="1053959602163383901">Valitsit todennuslaitteella vahvistamisen verkkosivustoilla, jotka käyttävät <ph name="PROVIDER_ORIGIN" /> â€palvelua. Tämä palveluntarjoaja on saattanut tallentaa maksutapaasi koskevia tietoja, joille voit <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">K&amp;umoa lisäys</translation>
<translation id="1056663316309890343">Valokuvaohjelmistot</translation>
<translation id="1056898198331236512">Varoitus</translation>
@@ -119,6 +120,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="1270502636509132238">Noutotapa</translation>
<translation id="1281476433249504884">Pinoaja 1</translation>
<translation id="1285320974508926690">Älä käännä tätä sivustoa</translation>
+<translation id="1288548991597756084">Tallenna kortti turvallisesti</translation>
<translation id="1292571435393770077">Lokero 16</translation>
<translation id="1292701964462482250">Tietokoneelle asennettu ohjelmisto estää Chromea muodostamasta turvallista yhteyttä verkkoon (vain Windows-tietokoneilla)</translation>
<translation id="1294154142200295408">Komentorivin muunnelmat</translation>
@@ -223,6 +225,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
&lt;p&gt;Korjaa virhe valitsemalla &lt;strong&gt;Yhdistä&lt;/strong&gt; sivulla, jota yrität avata.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Maisemasuunnittelu</translation>
<translation id="1513706915089223971">Luettelo historiamerkinnöistä</translation>
+<translation id="1516097932025103760">Se salataan ja tallennetaan turvallisesti. CVC:tä ei koskaan tallenneta.</translation>
<translation id="1517433312004943670">Puhelinnumero vaaditaan</translation>
<translation id="1519264250979466059">Koontipäivä</translation>
<translation id="1521159554480556801">Tekstiilitaide</translation>
@@ -288,6 +291,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="1662550410081243962">Tallenna ja täytä maksutavat</translation>
<translation id="1663943134801823270">Kortit ja osoitteet ovat peräisin Chromesta. Voit hallinnoida niitä <ph name="BEGIN_LINK" />asetuksissa<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Kielellä <ph name="SOURCE_LANGUAGE" /> kirjoitetut sivut käännetään tästä lähtien kielelle <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Avaa <ph name="CARD_DETAIL" /> ja käytä tarjous</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> – <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Lyhyt reuna ensin</translation>
<translation id="168693727862418163">Tämän käytännön arvon ei onnistunut vahvistamaan kaavaa ja se ohitetaan.</translation>
@@ -306,6 +310,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="1717218214683051432">Liiketunnistimet</translation>
<translation id="1717494416764505390">Postilaatikko 3</translation>
<translation id="1718029547804390981">Tiedosto on liian suuri merkintöihin</translation>
+<translation id="1720941539803966190">Sulje ohje</translation>
<translation id="1721424275792716183">* Kenttä on pakollinen.</translation>
<translation id="1727613060316725209">Varmenne on voimassa</translation>
<translation id="1727741090716970331">Lisää kelvollinen kortin numero</translation>
@@ -422,8 +427,8 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="205212645995975601">Grillaus</translation>
<translation id="2053111141626950936">Kielellä <ph name="LANGUAGE" /> kirjoitettuja sivuja ei käännetä.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Kun tämä asetus on päällä ja tila on aktiivinen, Chrome päättelee, minkä ihmisryhmän eli "kohortin" toimintaa viimeaikainen selaustoimintasi eniten muistuttaa. Mainostajat voivat valita ryhmälle mainoksia, ja selaustoimintasi pysyy yksityisenä laitteellasi. Ryhmäsi päivitetään päivittäin.}=1{Kun tämä asetus on päällä ja tila on aktiivinen, Chrome päättelee, minkä ihmisryhmän eli "kohortin" toimintaa viimeaikainen selaustoimintasi eniten muistuttaa. Mainostajat voivat valita ryhmälle mainoksia, ja selaustoimintasi pysyy yksityisenä laitteellasi. Ryhmäsi päivitetään päivittäin.}other{Kun tämä asetus on päällä ja tila on aktiivinen, Chrome päättelee, minkä ihmisryhmän eli "kohortin" toimintaa viimeaikainen selaustoimintasi eniten muistuttaa. Mainostajat voivat valita ryhmälle mainoksia, ja selaustoimintasi pysyy yksityisenä laitteellasi. Ryhmäsi päivitetään {NUM_DAYS} päivän välein.}}</translation>
-<translation id="2053553514270667976">Postinumero</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 ehdotus}other{# ehdotusta}}</translation>
+<translation id="2066915425250589881">tehdä poistopyynnön</translation>
<translation id="2068528718802935086">Vauvat ja taaperot</translation>
<translation id="2071156619270205202">Tälle kortille ei ole saatavilla virtuaalista korttinumeroa.</translation>
<translation id="2071692954027939183">Ilmoitukset on estetty automaattisesti, koska et yleensä salli niitä</translation>
@@ -435,7 +440,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="2088086323192747268">Muuta synkronointivalintoja ‑painike, paina Enter, niin voit valita synkronoitavat tiedot Chromen asetuksista</translation>
<translation id="2091887806945687916">Ääni</translation>
<translation id="2094505752054353250">Verkkotunnukset eivät ole yhteensopivat</translation>
-<translation id="2096368010154057602">Osasto</translation>
<translation id="2099652385553570808">Kolme niittiä vasemmalla</translation>
<translation id="2101225219012730419">Versio:</translation>
<translation id="2102134110707549001">Ehdota vahvaa salasanaa…</translation>
@@ -472,7 +476,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="2185836064961771414">Amerikkalainen jalkapallo</translation>
<translation id="2187317261103489799">Tunnista (oletus)</translation>
<translation id="2188375229972301266">Useita reikiä alareunassa</translation>
-<translation id="2188852899391513400">Juuri käyttämäsi salasana löytyi tietosuojaloukkauksesta. Tiliesi suojaamiseksi Googlen Salasanojen ylläpito suosittelee, että vaihdat salasanan heti ja tarkistat tallennetut salasanasi.</translation>
<translation id="219906046732893612">Kodin remontointi</translation>
<translation id="2202020181578195191">Anna kelvollinen viimeinen voimassaolovuosi.</translation>
<translation id="22081806969704220">Lokero 3</translation>
@@ -483,6 +486,8 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="2215727959747642672">Tiedostojen muokkaus</translation>
<translation id="2215963164070968490">Koirat</translation>
<translation id="2218879909401188352">Sivustolla <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> tällä hetkellä olevat hyökkääjät voivat asentaa vaarallisia laitettasi vahingoittavia sovelluksia, lisätä piilomaksuja puhelinlaskuusi tai varastaa henkilökohtaisia tietojasi. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Katso ohje uudelleen</translation>
+<translation id="2219735899272417925">Laitteen nollaaminen vaaditaan</translation>
<translation id="2224337661447660594">Ei internetyhteyttä</translation>
<translation id="2230458221926704099">Korjaa yhteytesi käyttämällä <ph name="BEGIN_LINK" />diagnostiikkasovellusta<ph name="END_LINK" />.</translation>
<translation id="2239100178324503013">Lähetä nyt</translation>
@@ -580,11 +585,13 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="2512101340618156538">Ei sallittu (oletus)</translation>
<translation id="2512413427717747692">Valitse Chrome oletusselaimeksi ‑painike, painamalla Enter voit valita Chromen järjestelmän oletusselaimeksi iOS-asetuksista</translation>
<translation id="2515629240566999685">Tarkista alueesi mobiilisignaali.</translation>
+<translation id="2515761554693942801">Valitsit vahvistuksen Touch ID:llä verkkosivustoilla, joilla on käytössä <ph name="PROVIDER_ORIGIN" />. Tämä palveluntarjoaja on saattanut tallentaa maksutapaasi koskevia tietoja, joille voit <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Niitti oikeassa alareunassa</translation>
<translation id="2521736961081452453">Luo lomake</translation>
<translation id="2523886232349826891">Tallennetaan vain tälle laitteelle</translation>
<translation id="2524461107774643265">Lisää tietoja</translation>
<translation id="2529899080962247600">Kentässä voi olla korkeintaan <ph name="MAX_ITEMS_LIMIT" /> kohtaa. Tämän määrän ylittävät merkinnät ohitetaan.</translation>
+<translation id="253493526287553278">Katso tarjouskoodin tiedot</translation>
<translation id="2535585790302968248">Avaa uusi incognito-välilehti selataksesi yksityisesti</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{ja 1 muu}other{ja # muuta}}</translation>
<translation id="2536110899380797252">Lisää osoite</translation>
@@ -620,7 +627,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="259821504105826686">Valokuvaus ja digitaide</translation>
<translation id="2601150049980261779">Romanttiset elokuvat</translation>
<translation id="2604589665489080024">Popmusiikki</translation>
-<translation id="2609632851001447353">Muunnelmat</translation>
<translation id="2610561535971892504">Kopioi klikkaamalla</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />ei tallenna<ph name="END_EMPHASIS" /> näitä tietoja:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Syntymä- ja nimipäivät</translation>
<translation id="2677748264148917807">Poistu</translation>
+<translation id="2679714844901977852">Tallenna kortti- ja laskutustiedot Google-tilillesi (<ph name="USER_EMAIL" />), jotta voit maksaa jatkossa suojatusti ja nopeasti</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Sopivin koko</translation>
<translation id="2688969097326701645">Kyllä, jatka</translation>
@@ -701,7 +708,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="2854764410992194509">Internetpalveluntarjoajat</translation>
<translation id="2856444702002559011">Sivustolle <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hyökännyt taho voi yrittää varastaa tietojasi (esimerkiksi salasanoja, viestejä tai luottokorttitietoja). <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Tällä sivustolla on häiritseviä tai harhaanjohtavia mainoksia.</translation>
-<translation id="286512204874376891">Virtuaalinen kortti salaa käyttämäsi kortin. Näin saat paremman suojauksen mahdollisia petoksia vastaan. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Ystävällinen</translation>
<translation id="28761159517501904">Elokuvat</translation>
<translation id="2876489322757410363">Poistutaan incognito-tilasta, jotta ulkoisessa sovelluksessa maksaminen onnistuu. Haluatko jatkaa?</translation>
@@ -802,7 +808,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="3158539265159265653">Levy</translation>
<translation id="3162559335345991374">Käyttämäsi Wi-Fi saattaa edellyttää kirjautumista.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Saari</translation>
<translation id="3176929007561373547">Tarkista välityspalvelinasetukset tai ota yhteyttä verkon järjestelmänvalvojaan
varmistaaksesi, että välityspalvelin toimii. Jos välityspalvelimen ei pitäisi olla
käytössä:
@@ -882,9 +887,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="3369192424181595722">Väärä kellonaika</translation>
<translation id="3369459162151165748">Ajoneuvojen osat ja lisävarusteet</translation>
<translation id="3371064404604898522">Valitse Chrome oletusselaimeksi</translation>
-<translation id="3371076217486966826"><ph name="URL" /> haluaa
- • luoda 3D-kartan ympäristöstäsi ja seurata kameran asentoa
- • käyttää kameraasi</translation>
<translation id="337363190475750230">Poistettu käytöstä</translation>
<translation id="3375754925484257129">Tee Chromen turvatarkistus</translation>
<translation id="3377144306166885718">Palvelin käytti vanhentunutta TLS:n versiota.</translation>
@@ -901,6 +903,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="3399952811970034796">Toimitusosoite</translation>
<translation id="3402261774528610252">Tämä sivusto ladattiin TLS 1.0:lla tai TLS 1.1:llä. Ne ovat vanhentuneet ja poistetaan käytöstä tulevaisuudessa. Kun ne poistetaan käytöstä, käyttäjät eivät voi enää ladata tätä sivustoa. Palvelimen pitäisi ottaa käyttöön TLS 1.2 tai uudempi versio.</translation>
<translation id="3405664148539009465">Muokkaa kirjasimia</translation>
+<translation id="3407789382767355356">kolmannen osapuolen sisäänkirjautuminen</translation>
<translation id="3409896703495473338">Muuta tietoturva-asetuksia</translation>
<translation id="3414952576877147120">Koko:</translation>
<translation id="3417660076059365994">Lähettämäsi tai liittämäsi tiedostot lähetetään Google Cloudiin tai kolmansille osapuolille analysoitaviksi. Ne voidaan esimerkiksi skannata arkaluontoisten tietojen tai haittaohjelmien varalta.</translation>
@@ -933,6 +936,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="3477679029130949506">Elokuvateatterien tarjonta ja esitysajat</translation>
<translation id="3479552764303398839">Ei nyt</translation>
<translation id="3484560055331845446">Saatat menettää pääsyn Google-tilillesi. Chrome suosittelee vaihtamaan salasanan nyt. Sinua pyydetään kirjautumaan sisään.</translation>
+<translation id="3484861421501147767">Muistutus: Tallennettu tarjouskoodi käytettävissä</translation>
<translation id="3487845404393360112">Lokero 4</translation>
<translation id="3495081129428749620">Hae sivulta
<ph name="PAGE_TITLE" /></translation>
@@ -1057,6 +1061,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="3810973564298564668">Hallinnoi</translation>
<translation id="3816482573645936981">Arvo (korvattu)</translation>
<translation id="382518646247711829">Jos käytät välityspalvelinta…</translation>
+<translation id="3826050100957962900">Kolmannen osapuolen sisäänkirjautuminen</translation>
<translation id="3827112369919217609">Absoluuttinen</translation>
<translation id="3827666161959873541">Koko perheen elokuvat</translation>
<translation id="3828924085048779000">Tunnuslause ei voi olla tyhjä.</translation>
@@ -1069,9 +1074,9 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="3858027520442213535">Päivitä päivämäärä ja aika</translation>
<translation id="3858860766373142691">Nimi</translation>
<translation id="3872834068356954457">Tiede</translation>
+<translation id="3875783148670536197">Näytä miten</translation>
<translation id="3881478300875776315">Näytä vähemmän rivejä</translation>
<translation id="3884278016824448484">Ristiriitainen laitteen tunnus</translation>
-<translation id="3885155851504623709">Kunta</translation>
<translation id="388632593194507180">Valvonta havaittu</translation>
<translation id="3886948180919384617">Pinoaja 3</translation>
<translation id="3890664840433101773">Lisää sähköposti</translation>
@@ -1101,9 +1106,11 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="3973234410852337861"><ph name="HOST_NAME" /> on estetty</translation>
<translation id="3978338123949022456">Hakutila, kirjoita hakulauseke ja paina Enter, niin <ph name="KEYWORD_SUFFIX" /> tekee haun</translation>
<translation id="398470910934384994">Linnut</translation>
+<translation id="3985750352229496475">Muuta osoitteita…</translation>
<translation id="3986705137476756801">Laita Livetekstitys päälle toistaiseksi</translation>
<translation id="3987940399970879459">Alle 1 Mt</translation>
<translation id="3990250421422698716">Jog offset</translation>
+<translation id="3992684624889376114">Tietoja sivusta</translation>
<translation id="3996311196211510766">Sivusto <ph name="ORIGIN" /> pyytää alkuperäkäytännön
soveltamista kaikkiin sen pyyntöihin, mutta käytäntöä ei voi soveltaa tällä hetkellä.</translation>
<translation id="4006465311664329701">Maksutavat, tarjoukset ja osoitteet Google Playta käyttäen</translation>
@@ -1228,6 +1235,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="4305666528087210886">Tiedostoa ei voi käyttää</translation>
<translation id="4306529830550717874">Tallennetaanko osoite?</translation>
<translation id="4306812610847412719">leikepöytä</translation>
+<translation id="4310070645992025887">Hae selailutiedoista</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Estä (oletus)</translation>
<translation id="4314815835985389558">Synkronointiasetusten muokkaus</translation>
@@ -1278,6 +1286,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="4435702339979719576">Postikortti</translation>
<translation id="443673843213245140">Välityspalvelinta ei saa käyttää, mutta erilliset välityspalvelimen asetukset on määritetty.</translation>
<translation id="4441832193888514600">Ohitettu, koska käytäntö voidaan asettaa vain pilvikäyttäjäkäytäntönä.</translation>
+<translation id="4442470707340296952">Chrome-välilehdet</translation>
<translation id="4450893287417543264">Älä näytä uudelleen</translation>
<translation id="4451135742916150903">Saa pyytää lupaa yhdistää HID-laitteisiin</translation>
<translation id="4452328064229197696">Juuri käyttämäsi salasana löytyi tietosuojaloukkauksesta. Tiliesi suojaamiseksi Googlen Salasanojen ylläpito suosittelee, että tarkistat tallennetut salasanasi.</translation>
@@ -1416,6 +1425,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="483241715238664915">Laita varoitukset päälle</translation>
<translation id="4834250788637067901">Maksutavat, tarjoukset ja osoitteet Google Playta käyttäen</translation>
<translation id="4838327282952368871">Unenomainen</translation>
+<translation id="4839087176073128681">Maksa seuraavalla kerralla nopeammin ja suojaa korttisi Googlen alan johtavalla suojauksella.</translation>
<translation id="4840250757394056958">Tarkista Chrome-historiasi</translation>
<translation id="484462545196658690">Automaattinen</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> ja muut voivat tarjota alennuksia</translation>
@@ -1478,6 +1488,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="5011561501798487822">Havaittu kieli</translation>
<translation id="5015510746216210676">Laitteen nimi:</translation>
<translation id="5017554619425969104">Kopioimasi teksti</translation>
+<translation id="5017828934289857214">Muistuta myöhemmin</translation>
<translation id="5018422839182700155">Sivun avaaminen epäonnistui</translation>
<translation id="5019198164206649151">Tallennustila on virheellisessä tilassa</translation>
<translation id="5020776957610079374">Maailmanmusiikki</translation>
@@ -1497,6 +1508,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="5051305769747448211">Live-komedia</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Jos haluat lähettää tiedoston käyttämällä lähijakamista, vapauta tilaa (<ph name="DISK_SPACE_SIZE" />) laitteeltasi}other{Jos haluat lähettää tiedostot käyttämällä lähijakamista, vapauta tilaa (<ph name="DISK_SPACE_SIZE" />) laitteeltasi}}</translation>
<translation id="5056549851600133418">Sinulle valitut artikkelit</translation>
+<translation id="5060483733937416656">Valitsit vahvistuksen Windows Hellolla verkkosivustoilla, joilla on käytössä <ph name="PROVIDER_ORIGIN" />. Tämä palveluntarjoaja on saattanut tallentaa maksutapaasi koskevia tietoja, joille voit <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Tarkoititko: <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Tulostushistoria</translation>
<translation id="5068234115460527047">Hedgerahastot</translation>
@@ -1510,10 +1522,8 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="5087286274860437796">Palvelimen varmenne ei ole tällä hetkellä kelvollinen.</translation>
<translation id="5087580092889165836">Lisää kortti</translation>
<translation id="5088142053160410913">Viesti operaattorille</translation>
-<translation id="5089810972385038852">Osavaltio/alue</translation>
<translation id="5093232627742069661">Kolmoistaite</translation>
<translation id="5094747076828555589">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />; Chromium ei luota sen suojausvarmenteeseen. Tämä voi johtua määritysvirheestä tai verkkoyhteytesi siepanneesta hyökkääjästä.</translation>
-<translation id="5095208057601539847">Provinssi</translation>
<translation id="5097099694988056070">Laitetilastot esim. CPU:n/RAM-muistin käytöstä</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Sivusto ei ole turvallinen</translation>
@@ -1551,6 +1561,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="5171045022955879922">Kirjoita hakusanoja tai URL-osoite</translation>
<translation id="5171689220826475070">European Fanfold</translation>
<translation id="5172758083709347301">Kaikki tietokoneen käyttäjät</translation>
+<translation id="5177076414499237632">Lisätietoja sivun lähteestä ja aiheesta</translation>
<translation id="5179510805599951267">Eikö kieli ole <ph name="ORIGINAL_LANGUAGE" />? Ilmoita virheestä</translation>
<translation id="518639307526414276">Lemmikkien ruoka ja tarvikkeet</translation>
<translation id="5190835502935405962">Kirjanmerkkipalkki</translation>
@@ -1711,6 +1722,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="5624120631404540903">Hallitse salasanoja</translation>
<translation id="5629630648637658800">Käytännön asetuksien lataaminen epäonnistui</translation>
<translation id="5631439013527180824">Laitteenhallintatunnus on virheellinen</translation>
+<translation id="5632485077360054581">Näytä miten</translation>
<translation id="5633066919399395251">Sivustolle <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hyökännyt taho voi yrittää asentaa tietokoneellesi vaarallisia ohjelmia, jotka varastavat tai poistavat tietojasi, esimerkiksi kuvia, salasanoja, viestejä tai luottokorttitietoja. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Harhaanjohtava sisältö estetty</translation>
<translation id="5633259641094592098">Kultti- ja indie-elokuvat</translation>
@@ -1828,6 +1840,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="5989320800837274978">Kiinteitä välityspalvelimia tai .pac-URL-osoitetta ei ole määritetty.</translation>
<translation id="5992691462791905444">Kolmoistaite (engineering)</translation>
<translation id="5995727681868049093">Ylläpidä tietojasi, yksityisyyttäsi ja tietoturvaasi Google-tilillä</translation>
+<translation id="5997247540087773573">Juuri käyttämäsi salasana löytyi tietosuojaloukkauksesta. Tiliesi suojaamiseksi Googlen Salasanojen ylläpito suosittelee, että vaihdat salasanan heti ja tarkistat tallennetut salasanasi.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> tulosta: <ph name="SEARCH_TEXT" /></translation>
<translation id="6006484371116297560">Perinteinen</translation>
<translation id="6008122969617370890">N-to-1-järjestys</translation>
@@ -1923,7 +1936,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="627746635834430766">Jos haluat maksaa nopeammin ensi kerralla, tallenna kortti ja laskutusosoite Google-tilillesi.</translation>
<translation id="6279183038361895380">Näytä kursori painamalla |<ph name="ACCELERATOR" />|.</translation>
<translation id="6280223929691119688">Toimitus ei onnistu tähän osoitteeseen. Valitse eri osoite.</translation>
-<translation id="6282194474023008486">Postinumero</translation>
<translation id="6285507000506177184">Ylläpidä latauksia Chromessa ‑painike, ylläpidä Chromessa lataamiasi tiedostoja painamalla Enter</translation>
<translation id="6289939620939689042">Sivun väri</translation>
<translation id="6290238015253830360">Suositellut artikkelit näkyvät tässä.</translation>
@@ -1945,6 +1957,7 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="6337133576188860026">Vapauttaa alle <ph name="SIZE" />. Jotkin sivustot saattavat latautua hitaammin seuraavalla käynnillä.</translation>
<translation id="6337534724793800597">Suodata käytäntöjä nimen mukaan</translation>
<translation id="6340739886198108203">Järjestelmänvalvojakäytännössä ei suositella kuvakaappausten ottamista tai tallenteen kuvaamista, kun luottamuksellinen sisältö on näkyvissä:</translation>
+<translation id="6348220984832452017">Aktiiviset muunnelmat</translation>
<translation id="6349101878882523185">Asenna <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ei mitään}=1{1 salasana: (<ph name="DOMAIN_LIST" />, synkronoitu)}=2{2 salasanaa (<ph name="DOMAIN_LIST" />, synkronoitu)}other{# salasanaa (<ph name="DOMAIN_LIST" />, synkronoitu)}}</translation>
<translation id="6355392890578844978">Yritys tai muu organisaatio ei ylläpidä selainta. Laitteen toimintaa saatetaan ylläpitää Chromiumin ulkopuolelta. <ph name="BEGIN_LINK" />Lue lisää<ph name="END_LINK" /></translation>
@@ -1976,7 +1989,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="643051589346665201">Vaihda Google-salasana</translation>
<translation id="6433490469411711332">Muokkaa yhteystietoja</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> kieltäytyi muodostamasta yhteyttä.</translation>
-<translation id="6438025220577812695">Vaihdan sen itse</translation>
<translation id="6440503408713884761">Ohitettu</translation>
<translation id="6443406338865242315">Asentamasi laajennukset</translation>
<translation id="6446163441502663861">Kahu (kirjekuori)</translation>
@@ -2106,9 +2118,9 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="6828866289116430505">Genetiikka</translation>
<translation id="6831043979455480757">Käännä</translation>
<translation id="6833752742582340615">Tallenna kortti- ja laskutustiedot Google-tilillesi, jotta voit maksaa jatkossa suojatusti ja nopeammin</translation>
-<translation id="6839929833149231406">Alue</translation>
<translation id="6846340164947227603">Käytä virtuaalista korttinumeroa…</translation>
<translation id="6852204201400771460">Ladataanko sovellus uudelleen?</translation>
+<translation id="6857776781123259569">Muuta salasana-asetuksia…</translation>
<translation id="686485648936420384">Tiedot kuluttajille</translation>
<translation id="6865412394715372076">Korttia ei voi vahvistaa juuri nyt.</translation>
<translation id="6869334554832814367">Henkilökohtaiset lainat</translation>
@@ -2157,7 +2169,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="6965978654500191972">Laite</translation>
<translation id="696703987787944103">Havainnollinen</translation>
<translation id="6968269510885595029">Käytä suojausavainta</translation>
-<translation id="6970216967273061347">Alue</translation>
<translation id="6971439137020188025">Luo uusi Google-esitys nopeasti Slidesissa</translation>
<translation id="6972629891077993081">HID-laitteet</translation>
<translation id="6973656660372572881">Sekä kiinteät välityspalvelimet että .pac-URL-osoite on määritetty.</translation>
@@ -2196,7 +2207,6 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<translation id="7081308185095828845">Ominaisuus ei ole käytettävissä tällä laitteella</translation>
<translation id="7083258188081898530">Lokero 9</translation>
<translation id="7086090958708083563">Käyttäjä pyysi lataamista</translation>
-<translation id="7087282848513945231">Piirikunta</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, paina sarkainta ja Enter, niin voit ylläpitää sivustoille tallennettuja lupia ja dataa Chromen asetuksista</translation>
<translation id="7096937462164235847">Tämän sivuston identiteettiä ei ole vahvistettu.</translation>
<translation id="7101893872976785596">Kauhuelokuvat</translation>
@@ -2215,10 +2225,10 @@ Muussa tapauksessa tämä estetään tietosuoja-asetuksilla. Jos sallit tämän,
<ph name="LIST_ITEM" />Lomakkeille lisätyt tiedot<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Lähetys tähän osoitteeseen ei onnistu. Valitse eri osoite.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Järjestelmänvalvojasi on kieltänyt tämän datan kopioimisen.</translation>
<translation id="7135130955892390533">Näytä tila</translation>
<translation id="7138472120740807366">Toimitustapa</translation>
-<translation id="7139724024395191329">Emiirikunta</translation>
<translation id="7139892792842608322">Ensisijainen lokero</translation>
<translation id="714064300541049402">2. puolen kuvan X vaihto</translation>
<translation id="7152423860607593928">Nro 14 (kirjekuori)</translation>
@@ -2435,6 +2445,7 @@ Lisätietoja:
<translation id="7669271284792375604">Tämän sivuston hyökkääjät saattavat yrittää huijata sinua asentamaan ohjelmia, jotka ovat haitallisia selauskokemuksellesi (esimerkiksi vaihtamalla aloitussivusi tai näyttämällä ylimääräisiä mainoksia käymilläsi sivustoilla).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Luottamukselliseen dataan liittyvät tapahtumat (1 tapahtuma kirjautumisen jälkeen). <ph name="BEGIN_LINK" />Lue lisää<ph name="END_LINK" />}other{Luottamukselliseen dataan liittyvät tapahtumat (# tapahtumaa kirjautumisen jälkeen). <ph name="BEGIN_LINK" />Lue lisää<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Postilaatikko 6</translation>
+<translation id="7675325315208090829">Muuta maksutapoja…</translation>
<translation id="7676643023259824263">Hae leikepöydän tekstiä <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Katso ja ylläpidä selaushistoriaasi Chromen asetuksista</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2477,7 +2488,6 @@ Lisätietoja:
<translation id="7766518757692125295">Reunus</translation>
<translation id="7770259615151589601">Määritetty-pitkä</translation>
<translation id="7773005668374414287">Sama järjestys tulostuspuoli ylöspäin</translation>
-<translation id="777702478322588152">Prefektuuri</translation>
<translation id="7791011319128895129">Julkaisematon</translation>
<translation id="7791196057686275387">Paali</translation>
<translation id="7791543448312431591">Lisää</translation>
@@ -2568,12 +2578,12 @@ Lisätietoja:
<translation id="8055534648776115597">Ammatti- ja jatkokoulutus</translation>
<translation id="8057711352706143257"><ph name="SOFTWARE_NAME" /> on määritetty virheellisesti. Ongelma korjaantuu yleensä, jos <ph name="SOFTWARE_NAME" /> poistetaan. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Elintarviketuotanto</translation>
-<translation id="8066955247577885446">Jotain meni pieleen.</translation>
<translation id="8067872629359326442">Lisäsit juuri salasanasi petolliselle sivustolle. Chromium voi auttaa. Valitse Suojaa tili, niin voit vaihtaa salasanasi ja Googlelle ilmoitetaan tilin mahdollisesta vaarantumisesta.</translation>
<translation id="8070439594494267500">Sovelluskuvake</translation>
<translation id="8074253406171541171">10x13 (kirjekuori)</translation>
<translation id="8075736640322370409">Luo uusi Google-taulukko nopeasti</translation>
<translation id="8075898834294118863">Muuta sivustoasetuksia</translation>
+<translation id="8076492880354921740">Välilehdet</translation>
<translation id="8078141288243656252">Kierrettyyn asiakirjaan ei voi tehdä merkintöjä</translation>
<translation id="8079031581361219619">Ladataanko sivusto uudelleen?</translation>
<translation id="8081087320434522107">Sedan-autot</translation>
@@ -2696,6 +2706,7 @@ Lisätietoja:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Asetukset</translation>
+<translation id="8428634594422941299">Selvä</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, paina sarkainta ja Enter, niin voit muuttaa evästevalintoja Chromen asetuksista</translation>
<translation id="8433057134996913067">Sinut kirjataan ulos useimmilta verkkosivustoilta.</translation>
<translation id="8434840396568290395">Lemmikit</translation>
@@ -2794,6 +2805,7 @@ Lisätietoja:
<translation id="8742371904523228557">Koodisi on <ph name="ONE_TIME_CODE" /> (<ph name="ORIGIN" />)</translation>
<translation id="874918643257405732">Aseta tämä välilehti kirjanmerkiksi</translation>
<translation id="8751426954251315517">Yritä seuraavalla kerralla uudelleen</translation>
+<translation id="8757526089434340176">Google Pay ‑tarjous saatavilla</translation>
<translation id="8758885506338294482">Kilpailulliset videopelit</translation>
<translation id="8759274551635299824">Tämä kortti on vanhentunut.</translation>
<translation id="87601671197631245">Tämä sivusto käyttää vanhentunutta tietoturvamääritystä, joka voi paljastaa tietosi (esim. salasanat, viestit tai credit-kortit), kun ne lähetetään sivustolle.</translation>
@@ -2801,6 +2813,7 @@ Lisätietoja:
<translation id="8763927697961133303">USB-laite</translation>
<translation id="8763986294015493060">Sulje kaikki tällä hetkellä avoinna olevat incognito-ikkunat</translation>
<translation id="8766943070169463815">Suojatun maksun kirjautumistietojen todennuspaneeli on avattu</translation>
+<translation id="8767765348545497220">Sulje ohjekupla</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Moottoripyörät</translation>
<translation id="8790007591277257123">&amp;Toista poisto</translation>
@@ -2813,6 +2826,7 @@ Lisätietoja:
<translation id="8806285662264631610">Kylpy- ja vartalonhoitotuotteet</translation>
<translation id="8807160976559152894">Leikkaa jokaisen sivun jälkeen</translation>
<translation id="8808828119384186784">Chromen asetukset</translation>
+<translation id="8813277370772331957">Muistuta myöhemmin</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, päivitä Chrome sen asetuksista painamalla sarkainta ja sitten Enter</translation>
<translation id="8820817407110198400">Kirjanmerkit</translation>
<translation id="882338992931677877">Manuaalinen syöttöpaikka</translation>
@@ -2992,6 +3006,7 @@ Lisätietoja:
<translation id="988159990683914416">Kehittäjän koontiversio</translation>
<translation id="989988560359834682">Osoitteen muokkaus</translation>
<translation id="991413375315957741">liike- tai valotunnistimet</translation>
+<translation id="992110854164447044">Virtuaalinen kortti piilottaa käyttämäsi kortin ja saat paremman suojauksen mahdollisia petoksia vastaan. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Vaaleanpunainen</translation>
<translation id="992432478773561401"><ph name="SOFTWARE_NAME" /> on asennettu virheellisesti tietokoneellesi tai verkkoon:
diff --git a/chromium/components/strings/components_strings_fil.xtb b/chromium/components/strings/components_strings_fil.xtb
index ad9d70dcdc6..d68e49176c2 100644
--- a/chromium/components/strings/components_strings_fil.xtb
+++ b/chromium/components/strings/components_strings_fil.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Mga grant, scholarship, at tulong Pampinansyal</translation>
<translation id="1048785276086539861">Kapag nag-edit ka ng mga anotasyon, babalik ang dokumentong ito sa isang page na view</translation>
<translation id="1050038467049342496">Isara ang iba pang app</translation>
+<translation id="1053959602163383901">Puwede mong piliing mag-verify gamit ang isang authenticator device sa mga website na gumagamit ng <ph name="PROVIDER_ORIGIN" />. Ang provider na ito ay posibleng nag-store ng impormasyon tungkol sa iyong paraan ng pagbabayad, na puwede mong <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;I-undo ang Pagdagdag</translation>
<translation id="1056663316309890343">Software sa larawan</translation>
<translation id="1056898198331236512">Babala</translation>
@@ -119,6 +120,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="1270502636509132238">Paraan sa Pag-pick up</translation>
<translation id="1281476433249504884">Stacker 1</translation>
<translation id="1285320974508926690">Huwag isalin kailanman ang site na ito</translation>
+<translation id="1288548991597756084">Secure na i-save ang card</translation>
<translation id="1292571435393770077">Tray 16</translation>
<translation id="1292701964462482250">"Pinipigilan ng software sa iyong computer na makakonekta nang ligtas ang Chrome sa web" (mga Windows computer lang)</translation>
<translation id="1294154142200295408">Mga variation ng command-line</translation>
@@ -223,6 +225,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
&lt;p&gt;Para maayos ang error, i-click ang &lt;strong&gt;Kumonekta&lt;/strong&gt; sa page na sinusubukan mong buksan.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Disenyo ng landscape</translation>
<translation id="1513706915089223971">Listahan ng mga entry sa history</translation>
+<translation id="1516097932025103760">Naka-encrypt ito, secure na mase-save at hindi kailanman iso-store ang CVC.</translation>
<translation id="1517433312004943670">Kinakailangan ang numero ng telepono</translation>
<translation id="1519264250979466059">Petsa ng Build</translation>
<translation id="1521159554480556801">Mga sining sa himulmol at tela</translation>
@@ -288,6 +291,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="1662550410081243962">I-save at punan ang mga paraan ng pagbabayad</translation>
<translation id="1663943134801823270">Ang mga card at address ay mula sa Chrome. Maaari mong pamahalaan ang mga ito sa <ph name="BEGIN_LINK" />Mga Setting<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Simula ngayon ay ita-translate na sa <ph name="TARGET_LANGUAGE" /> ang mga page na nasa <ph name="SOURCE_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Mag-check out gamit ang <ph name="CARD_DETAIL" /> para gamitin ang alok</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> na isasalin sa <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Short edge muna</translation>
<translation id="168693727862418163">Hindi na-validate ang value ng patakarang ito sa schema nito at babalewalain ito.</translation>
@@ -306,6 +310,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="1717218214683051432">Mga sensor ng paggalaw</translation>
<translation id="1717494416764505390">Mailbox 3</translation>
<translation id="1718029547804390981">Masyadong malaki para i-annotate ang dokumento</translation>
+<translation id="1720941539803966190">Isara ang tutorial</translation>
<translation id="1721424275792716183">Kinakailangan ang field na may *</translation>
<translation id="1727613060316725209">Valid ang certificate na ito</translation>
<translation id="1727741090716970331">Magdagdag ng Wastong Numero ng Card</translation>
@@ -422,8 +427,8 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="205212645995975601">BBQ at pag-iihaw</translation>
<translation id="2053111141626950936">Hindi ita-translate ang mga page na nasa <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Kapag naka-on ang kontrol na ito at aktibo ang status, tinutukoy ng Chrome kung aling malaking grupo ng mga tao, o "cohort," ang pinakakatulad ng iyong kamakailang aktibidad sa pag-browse. Makakapili ang mga advertiser ng mga ad para sa grupo at pinapanatiling pribado sa iyong device ang aktibidad mo sa pag-browse. Ina-update ang iyong grupo araw-araw.}=1{Kapag naka-on ang kontrol na ito at aktibo ang status, tinutukoy ng Chrome kung aling malaking grupo ng mga tao, o "cohort," ang pinakakatulad ng iyong kamakailang aktibidad sa pag-browse. Makakapili ang mga advertiser ng mga ad para sa grupo at pinapanatiling pribado sa iyong device ang aktibidad mo sa pag-browse. Ina-update ang iyong grupo araw-araw.}one{Kapag naka-on ang kontrol na ito at aktibo ang status, tinutukoy ng Chrome kung aling malaking grupo ng mga tao, o "cohort," ang pinakakatulad ng iyong kamakailang aktibidad sa pag-browse. Makakapili ang mga advertiser ng mga ad para sa grupo at pinapanatiling pribado sa iyong device ang aktibidad mo sa pag-browse. Ina-update ang iyong grupo kada {NUM_DAYS} araw.}other{Kapag naka-on ang kontrol na ito at aktibo ang status, tinutukoy ng Chrome kung aling malaking grupo ng mga tao, o "cohort," ang pinakakatulad ng iyong kamakailang aktibidad sa pag-browse. Makakapili ang mga advertiser ng mga ad para sa grupo at pinapanatiling pribado sa iyong device ang aktibidad mo sa pag-browse. Ina-update ang iyong grupo kada {NUM_DAYS} na araw.}}</translation>
-<translation id="2053553514270667976">Zip code</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 suhestyon}one{# suhestyon}other{# na suhestyon}}</translation>
+<translation id="2066915425250589881">hilinging i-delete</translation>
<translation id="2068528718802935086">Mga sanggol at toddler</translation>
<translation id="2071156619270205202">Hindi kwalipikado ang card na ito para sa virtual card number.</translation>
<translation id="2071692954027939183">Awtomatikong na-block ang mga notification dahil karaniwang hindi mo pinapayagan ang mga ito</translation>
@@ -435,7 +440,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="2088086323192747268">Button na Pamahalaan ang pag-sync, pindutin ang Enter para pamahalaan kung anong impormasyon ang isi-sync mo sa mga setting ng Chrome</translation>
<translation id="2091887806945687916">Tunog</translation>
<translation id="2094505752054353250">Maling pagtutugma sa domain</translation>
-<translation id="2096368010154057602">Kagawaran</translation>
<translation id="2099652385553570808">Triple staple left</translation>
<translation id="2101225219012730419">Bersyon:</translation>
<translation id="2102134110707549001">Magmungkahi ng Malakas na Password...</translation>
@@ -472,7 +476,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="2185836064961771414">American football</translation>
<translation id="2187317261103489799">Tukuyin (default)</translation>
<translation id="2188375229972301266">Multiple punch bottom</translation>
-<translation id="2188852899391513400">Nakita sa isang paglabag sa data ang password na kakagamit mo lang. Para ma-secure ang iyong mga account, inirerekomenda ng Google Password Manager na palitan na ito ngayon at pagkatapos ay tingnan ang mga naka-save mong password.</translation>
<translation id="219906046732893612">Pagpapaganda ng bahay</translation>
<translation id="2202020181578195191">Maglagay ng wastong taon ng pag-expire</translation>
<translation id="22081806969704220">Tray 3</translation>
@@ -483,6 +486,8 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="2215727959747642672">Pag-edit ng file</translation>
<translation id="2215963164070968490">Mga aso</translation>
<translation id="2218879909401188352">Ang mga umaatake na kasalukuyang nasa <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ay maaaring mag-install ng mga mapanganib na app na makakapinsala sa iyong device, magdagdag ng mga hindi alam na singil sa mobile bill mo, o magnakaw ng iyong personal na impormasyon. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto pa<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">I-restart ang tutorial</translation>
+<translation id="2219735899272417925">Kailangang i-reset ang device</translation>
<translation id="2224337661447660594">Walang internet</translation>
<translation id="2230458221926704099">Ayusin ang iyong koneksyon gamit ang <ph name="BEGIN_LINK" />diagnostics app<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Ipadala ngayon</translation>
@@ -580,11 +585,13 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="2512101340618156538">Hindi pinapayagan (default)</translation>
<translation id="2512413427717747692">Itakda ang Chrome bilang default na button ng browser, pindutin ang Enter para itakda ang Chrome bilang default na browser ng system sa mga setting ng iOS</translation>
<translation id="2515629240566999685">Suriin ang signal sa iyong lugar</translation>
+<translation id="2515761554693942801">Puwede mong piliing mag-verify gamit ang Touch ID sa mga website na gumagamit ng <ph name="PROVIDER_ORIGIN" />. Ang provider na ito ay posibleng nag-store ng impormasyon tungkol sa iyong paraan ng pagbabayad, na puwede mong <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Staple bottom right</translation>
<translation id="2521736961081452453">Gumawa ng form</translation>
<translation id="2523886232349826891">Ise-save sa device lang na ito</translation>
<translation id="2524461107774643265">Magdagdag ng Higit Pang Impormasyon</translation>
<translation id="2529899080962247600">Hindi dapat magkaroon ng higit sa <ph name="MAX_ITEMS_LIMIT" /> (na) entry ang field na ito. Babalewalain ang lahat ng entry na lalampas dito.</translation>
+<translation id="253493526287553278">Tingnan ang mga detalye ng promo code</translation>
<translation id="2535585790302968248">Magbukas ng bagong tab na Incognito para mag-browse nang pribado</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{at 1 pa}one{at # pa}other{at # pa}}</translation>
<translation id="2536110899380797252">Magdagdag ng Address</translation>
@@ -620,7 +627,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="259821504105826686">Photographic at digital arts</translation>
<translation id="2601150049980261779">Mga pelikulang romance</translation>
<translation id="2604589665489080024">Pop music</translation>
-<translation id="2609632851001447353">Mga Pagkakaiba-iba</translation>
<translation id="2610561535971892504">I-click para kopyahin</translation>
<translation id="2617988307566202237"><ph name="BEGIN_EMPHASIS" />Hindi ise-save<ph name="END_EMPHASIS" /> ng Chrome ang sumusunod na impormasyon:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Mga kaarawan at pangalan ng araw</translation>
<translation id="2677748264148917807">Umalis</translation>
+<translation id="2679714844901977852">I-save sa Google Account mo na <ph name="USER_EMAIL" /> ang iyong impormasyon ng card at impormasyon sa pagsingil para sa secure at mas mabilis na pag-checkout</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Pinakakasya</translation>
<translation id="2688969097326701645">Oo, magpatuloy</translation>
@@ -701,7 +708,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="2854764410992194509">Mga internet service provider (mga ISP)</translation>
<translation id="2856444702002559011">Maaaring sinusubukang nakawin ng mga attacker ang iyong impormasyon mula sa <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (halimbawa, mga password, mensahe, o credit card). <ph name="BEGIN_LEARN_MORE_LINK" />Matuto pa<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Nagpapakita ang site na ito ng mga nakakasagabal o nakakapanlinlang na ad.</translation>
-<translation id="286512204874376891">Itinatago ng virtual card ang iyong aktwal na card para makatulong na protektahan ka laban sa potensyal na panloloko. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Friendly</translation>
<translation id="28761159517501904">Mga pelikula</translation>
<translation id="2876489322757410363">Aalis sa Incognito mode upang magbayad sa pamamagitan ng external na application. Magpatuloy?</translation>
@@ -802,7 +808,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="3158539265159265653">Disc</translation>
<translation id="3162559335345991374">Maaaring hilingin ng Wi-Fi na ginagamit mo na bisitahin mo ang page nito sa pag-login.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Island</translation>
<translation id="3176929007561373547">Tingnan ang mga setting ng iyong proxy o makipag-ugnayan sa iyong network
administrator upang matiyak na gumagana ang proxy server. Kung sa palagay mo
ay hindi ka dapat gumagamit ng proxy server:
@@ -881,9 +886,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="3369192424181595722">Error sa orasan</translation>
<translation id="3369459162151165748">Mga piyesa at accessory ng sasakyan</translation>
<translation id="3371064404604898522">Itakda ang Chrome bilang default na browser</translation>
-<translation id="3371076217486966826">Gusto ng <ph name="URL" /> na:
- • Gumawa ng 3D na mapa ng iyong kapaligiran at subaybayan ang posisyon ng camera
- • Gamitin ang iyong camera</translation>
<translation id="337363190475750230">Naalis sa pagkakaprobisyon</translation>
<translation id="3375754925484257129">Magpatakbo ng pag-check sa kaligtasan sa Chrome</translation>
<translation id="3377144306166885718">Gumamit ang server ng lumang bersyon ng TLS.</translation>
@@ -900,6 +902,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="3399952811970034796">Delivery Address</translation>
<translation id="3402261774528610252">Ang koneksyong ginamit para i-load ang site na ito ay gumamit ng TLS 1.0 o TLS 1.1, na hindi na ginagamit at idi-disable sa hinaharap. Kapag na-disable, pipigilan ang mga user na i-load ang site na ito. Dapat i-enable ng server ang TLS 1.2 o mas bago.</translation>
<translation id="3405664148539009465">I-customize ang mga font</translation>
+<translation id="3407789382767355356">third-party na pag-sign in</translation>
<translation id="3409896703495473338">Pamahalaan ang mga setting ng seguridad</translation>
<translation id="3414952576877147120">Laki:</translation>
<translation id="3417660076059365994">Ipinapadala sa Google Cloud o mga third party para sa pagsusuri ang mga file na na-upload o na-attach mo. Halimbawa, posibleng i-scan ang mga ito para sa sensitibong data o malware.</translation>
@@ -932,6 +935,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="3477679029130949506">Mga listahan ng pelikula at mga oras ng palabas sa sinehan</translation>
<translation id="3479552764303398839">Hindi ngayon</translation>
<translation id="3484560055331845446">Maaari kang mawalan ng access sa iyong Google Account. Inirerekomenda ng Chrome na palitan na ang iyong password. Hihilingin sa iyong mag-sign in.</translation>
+<translation id="3484861421501147767">Paalala: May available na naka-save na promo code</translation>
<translation id="3487845404393360112">Tray 4</translation>
<translation id="3495081129428749620">Hanapin sa page
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="3810973564298564668">Pamahalaan</translation>
<translation id="3816482573645936981">Value (nasasapawan)</translation>
<translation id="382518646247711829">Kung gumagamit ka ng proxy server...</translation>
+<translation id="3826050100957962900">Third-party na pag-sign in</translation>
<translation id="3827112369919217609">Absolute</translation>
<translation id="3827666161959873541">Mga pelikulang pampamilya</translation>
<translation id="3828924085048779000">Hindi pinapayagan ang walang laman na passphrase.</translation>
@@ -1068,9 +1073,9 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="3858027520442213535">I-update ang petsa at oras</translation>
<translation id="3858860766373142691">Pangalan</translation>
<translation id="3872834068356954457">Agham</translation>
+<translation id="3875783148670536197">Ipakita sa Akin Kung Paano</translation>
<translation id="3881478300875776315">Magpakita ng mas kaunting linya</translation>
<translation id="3884278016824448484">Sumasalungat na tagatukoy ng device</translation>
-<translation id="3885155851504623709">Parish</translation>
<translation id="388632593194507180">May Na-detect na Pagsubaybay</translation>
<translation id="3886948180919384617">Stacker 3</translation>
<translation id="3890664840433101773">Magdagdag ng email</translation>
@@ -1100,9 +1105,11 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="3973234410852337861">Naka-block ang <ph name="HOST_NAME" /></translation>
<translation id="3978338123949022456">Search mode, mag-type ng query, at pindutin ang Enter para maghanap gamit ang <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Mga ibon</translation>
+<translation id="3985750352229496475">Pamahalaan ang Mga Address...</translation>
<translation id="3986705137476756801">I-off muna sa ngayon ang Instant Caption</translation>
<translation id="3987940399970879459">Wala pang 1 MB</translation>
<translation id="3990250421422698716">Jog offset</translation>
+<translation id="3992684624889376114">Tungkol sa page na ito</translation>
<translation id="3996311196211510766">Hiniling ng site na <ph name="ORIGIN" /> na maglapat ng patakaran ng pinagmulan
sa lahat ng kahilingan dito, pero hindi pa mailalapat sa ngayon ang patakarang ito.</translation>
<translation id="4006465311664329701">Mga Paraan ng Pagbabayad, Alok, at Address na Gumagamit ng Google Pay</translation>
@@ -1227,6 +1234,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="4305666528087210886">Hindi ma-access ang iyong file</translation>
<translation id="4306529830550717874">I-save ang address?</translation>
<translation id="4306812610847412719">clipboard</translation>
+<translation id="4310070645992025887">Maghanap sa iyong Mga Journey</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">I-block (default)</translation>
<translation id="4314815835985389558">Pamahalaan ang pag-sync</translation>
@@ -1277,6 +1285,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Hindi pinagana ang paggamit ng isang proxy ngunit tinutukoy ang isang tahasang configuration ng proxy.</translation>
<translation id="4441832193888514600">Binalewala dahil ang patakaran ay puwede lang itakda bilang patakaran ng user ng cloud.</translation>
+<translation id="4442470707340296952">Mga Tab ng Chrome</translation>
<translation id="4450893287417543264">Huwag ipakitang muli</translation>
<translation id="4451135742916150903">Puwedeng hilinging kumonekta sa mga HID device</translation>
<translation id="4452328064229197696">Nakita sa isang paglabag sa data ang password na kakagamit mo lang. Para ma-secure ang iyong mga account, inirerekomenda ng Google Password Manager na tingnan mo ang iyong mga naka-save na password.</translation>
@@ -1415,6 +1424,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="483241715238664915">I-on ang mga babala</translation>
<translation id="4834250788637067901">Mga paraan ng pagbabayad, alok, at address na gumagamit ng Google Pay</translation>
<translation id="4838327282952368871">Dreamy</translation>
+<translation id="4839087176073128681">Mas mabilis na magbayad sa susunod at protektahan ang iyong card gamit ang nangunguna sa industriyang seguridad ng Google.</translation>
<translation id="4840250757394056958">Tingnan ang iyong history sa Chrome</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">Makakuha ng diskwento sa <ph name="MERCHANT_NAME" /> at higit pa</translation>
@@ -1477,6 +1487,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="5011561501798487822">Na-detect na Wika</translation>
<translation id="5015510746216210676">Pangalan ng Machine:</translation>
<translation id="5017554619425969104">Text na kinopya mo</translation>
+<translation id="5017828934289857214">Paalalahanan Ako Sa Ibang Pagkakataon</translation>
<translation id="5018422839182700155">Hindi mabuksan ang page na ito</translation>
<translation id="5019198164206649151">Hindi maganda ang katayuan ng backing store</translation>
<translation id="5020776957610079374">Musika ng mundo</translation>
@@ -1496,6 +1507,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="5051305769747448211">Live na komedya</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Para ipadala ang file na ito gamit ang Nearby Share, magbakante ng espasyo (<ph name="DISK_SPACE_SIZE" />) sa iyong device}one{Para ipadala ang mga file na ito gamit ang Nearby Share, magbakante ng espasyo (<ph name="DISK_SPACE_SIZE" />) sa iyong device}other{Para ipadala ang mga file na ito gamit ang Nearby Share, magbakante ng espasyo (<ph name="DISK_SPACE_SIZE" />) sa iyong device}}</translation>
<translation id="5056549851600133418">Mga artikulo para sa iyo</translation>
+<translation id="5060483733937416656">Pinili mong mag-verify gamit ang Windows Hello sa mga website na gumagamit ng <ph name="PROVIDER_ORIGIN" />. Ang provider na ito ay posibleng nag-store ng impormasyon tungkol sa iyong paraan ng pagbabayad, na puwede mong <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Ang ibig mo bang sabihin ay <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">History ng pag-print</translation>
<translation id="5068234115460527047">Mga hedge fund</translation>
@@ -1509,10 +1521,8 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="5087286274860437796">Hindi angkop ang certificate ng server sa oras na ito.</translation>
<translation id="5087580092889165836">Magdagdag ng card</translation>
<translation id="5088142053160410913">Mensahe sa operator</translation>
-<translation id="5089810972385038852">Estado</translation>
<translation id="5093232627742069661">Z-fold</translation>
<translation id="5094747076828555589">Hindi mapatunayan ng server na ito na ito ay <ph name="DOMAIN" />; hindi pinagkakatiwalaan ng Chromium ang certificate ng seguridad nito. Maaaring dulot ito ng maling configuration o isang umaatake na hinahadlangan ang iyong koneksyon.</translation>
-<translation id="5095208057601539847">Lalawigan</translation>
<translation id="5097099694988056070">Mga istatistika ng device gaya ng paggamit ng CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Hindi secure ang site</translation>
@@ -1550,6 +1560,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="5171045022955879922">Hanapin o i-type ang URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Computer</translation>
+<translation id="5177076414499237632">Matuto tungkol sa source at paksa ng page na ito</translation>
<translation id="5179510805599951267">Wala sa <ph name="ORIGINAL_LANGUAGE" />? Iulat ang error na ito</translation>
<translation id="518639307526414276">Pagkain at mga supply para sa alagang hayop</translation>
<translation id="5190835502935405962">Bar ng Mga Bookmark</translation>
@@ -1710,6 +1721,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="5624120631404540903">Pamahalaan ang mga password</translation>
<translation id="5629630648637658800">Nabigong i-load ang mga setting ng patakaran</translation>
<translation id="5631439013527180824">Di-wastong token sa pamamahala ng device</translation>
+<translation id="5632485077360054581">Ipakita sa akin kung paano</translation>
<translation id="5633066919399395251">Maaaring magtangka ang mga attacker na kasalukuyang nasa <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> na mag-install ng mga mapanganib na program sa iyong computer na magnanakaw o magde-delete ng impormasyon mo (halimbawa, mga larawan, password, mensahe, at credit card). <ph name="BEGIN_LEARN_MORE_LINK" />Matuto pa<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Na-block ang mapanlinlang na content.</translation>
<translation id="5633259641094592098">Mga pelikulang cult at indie</translation>
@@ -1827,6 +1839,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="5989320800837274978">Hindi tunukoy ang alinman sa mga hindi nababagong proxy server o isang .pac script URL.</translation>
<translation id="5992691462791905444">Engineering Z-fold</translation>
<translation id="5995727681868049093">Pamahalaan ang iyong impormasyon, privacy, at seguridad sa iyong Google Account</translation>
+<translation id="5997247540087773573">Nakita sa isang data breach ang password na kakagamit mo lang. Para ma-secure ang iyong mga account, inirerekomenda ng Google Password Manager na palitan na ito ngayon at tingnan ang mga naka-save mong password.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> (na) resulta para sa '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">Classic</translation>
<translation id="6008122969617370890">N-to-1 na pagkakasunod-sunod</translation>
@@ -1922,7 +1935,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="627746635834430766">Para mas mabilis na makapagbayad sa susunod, i-save ang iyong card at billing address sa Google Account mo.</translation>
<translation id="6279183038361895380">Pindutin ang |<ph name="ACCELERATOR" />| upang ipakita ang iyong cursor</translation>
<translation id="6280223929691119688">Hindi maaaring maghatid sa address na ito. Pumili ng ibang address.</translation>
-<translation id="6282194474023008486">Postal code</translation>
<translation id="6285507000506177184">Button na Pamahalaan ang mga download sa Chrome, pindutin ang Enter para pamahalaan ang mga file na na-download mo sa Chrome</translation>
<translation id="6289939620939689042">Kulay ng Page</translation>
<translation id="6290238015253830360">Lalabas dito ang mga iminungkahi mong artikulo</translation>
@@ -1944,6 +1956,7 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="6337133576188860026">Magbabakante ng wala pang <ph name="SIZE" />. Maaaring mag-load nang mas mabagal ang ilang site sa iyong susunod na pagbisita.</translation>
<translation id="6337534724793800597">I-filter ang mga patakaran ayon sa pangalan</translation>
<translation id="6340739886198108203">Hindi inirerekomenda ng patakaran ng administrator ang pagkuha ng mga screenshot o recording kapag may nakikitang kumpidensyal na content:</translation>
+<translation id="6348220984832452017">Mga Aktibong Variation</translation>
<translation id="6349101878882523185">I-install ang <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Wala}=1{1 password (para sa <ph name="DOMAIN_LIST" />, naka-sync)}=2{2 password (para sa <ph name="DOMAIN_LIST" />, naka-sync)}one{# password (para sa <ph name="DOMAIN_LIST" />, naka-sync)}other{# na password (para sa <ph name="DOMAIN_LIST" />, naka-sync)}}</translation>
<translation id="6355392890578844978">Hindi pinapamahalaan ng kumpanya o iba pang organisasyon ang browser na ito. Posibleng pinapamahalaan sa labas ng Chromium ang aktibidad sa device na ito. <ph name="BEGIN_LINK" />Matuto pa<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="643051589346665201">Palitan ang password sa Google</translation>
<translation id="6433490469411711332">I-edit ang impormasyon sa pakikipag-ugnayan</translation>
<translation id="6433595998831338502">Tumangging kumonekta ang <ph name="HOST_NAME" />.</translation>
-<translation id="6438025220577812695">Ako mismo ang magpapalit</translation>
<translation id="6440503408713884761">Binalewala</translation>
<translation id="6443406338865242315">Aling mga extension at plugin ang na-install mo</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2105,9 +2117,9 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="6828866289116430505">Genetics</translation>
<translation id="6831043979455480757">Isalin</translation>
<translation id="6833752742582340615">I-save sa Google Account mo ang iyong impormasyon ng card at impormasyon sa pagsingil para sa secure at mas mabilis na pag-checkout</translation>
-<translation id="6839929833149231406">Lugar</translation>
<translation id="6846340164947227603">Gumamit ng virtual na numero ng card...</translation>
<translation id="6852204201400771460">I-reload ang app?</translation>
+<translation id="6857776781123259569">Pamahalaan ang Mga Password...</translation>
<translation id="686485648936420384">Mga resource ng consumer</translation>
<translation id="6865412394715372076">Hindi ma-verify ang card na ito sa ngayon.</translation>
<translation id="6869334554832814367">Mga personal na loan</translation>
@@ -2156,7 +2168,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="6965978654500191972">Device</translation>
<translation id="696703987787944103">Perceptual</translation>
<translation id="6968269510885595029">Gamitin ang iyong Security Key</translation>
-<translation id="6970216967273061347">Distrito</translation>
<translation id="6971439137020188025">Gumawa ng bagong Google presentation sa Slides nang mabilis</translation>
<translation id="6972629891077993081">Mga HID device</translation>
<translation id="6973656660372572881">Tinukoy ang parehong mga hindi nababagong proxy server at isang .pac script URL.</translation>
@@ -2195,7 +2206,6 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<translation id="7081308185095828845">Hindi available ang feature na ito sa iyong device</translation>
<translation id="7083258188081898530">Tray 9</translation>
<translation id="7086090958708083563">Hiniling ng user ang pag-upload</translation>
-<translation id="7087282848513945231">Lalawigan</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, pindutin ang Tab pagkatapos ay ang Enter para pamahalaan ang mga pahintulot at data na naka-store sa lahat ng site sa mga setting ng Chrome</translation>
<translation id="7096937462164235847">Hindi na-verify ang pagkakakilanlan ng website na ito.</translation>
<translation id="7101893872976785596">Mga pelikulang katatakutan</translation>
@@ -2214,10 +2224,10 @@ Kung ayaw mo, iba-block ito ng iyong mga setting ng privacy. Papayagan nito ang
<ph name="LIST_ITEM" />Impormasyong inilagay sa mga form<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Hindi maaaring magpadala sa address na ito. Pumili ng ibang address.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Ipinagbawal ng iyong admin ang pagkopya sa data na ito.</translation>
<translation id="7135130955892390533">Ipakita ang status</translation>
<translation id="7138472120740807366">Pamamaraan ng paghahatid</translation>
-<translation id="7139724024395191329">Emirate</translation>
<translation id="7139892792842608322">Pangunahing Tray</translation>
<translation id="714064300541049402">Pag-shift ng side 2 larawan X</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2434,6 +2444,7 @@ Mga karagdagang detalye:
<translation id="7669271284792375604">Maaaring subukan ng mga attacker sa site na ito na linlangin ka upang mag-install ng mga program na makakasama sa iyong karanasan sa pag-browse (halimbawa, sa pamamagitan ng pagbabago ng iyong homepage o pagpapakita ng mga karagdagang ad sa mga site na binibisita mo).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Mga pagkilos na ginawa sa data na na-flag bilang kumpidensyal (1 pagkilos mula noong nag-log in). <ph name="BEGIN_LINK" />Matuto pa<ph name="END_LINK" />}one{Mga pagkilos na ginawa sa data na na-flag bilang kumpidensyal (# pagkilos mula noong nag-log in). <ph name="BEGIN_LINK" />Matuto pa<ph name="END_LINK" />}other{Mga pagkilos na ginawa sa data na na-flag bilang kumpidensyal (# na pagkilos mula noong nag-log in). <ph name="BEGIN_LINK" />Matuto pa<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Mailbox 6</translation>
+<translation id="7675325315208090829">Pamahalaan ang Mga Paraan ng Pagbabayad...</translation>
<translation id="7676643023259824263">Hanapin ang text sa clipboard, na <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Tingnan at pamahalaan ang iyong history ng pag-browse sa mga setting ng Chrome</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2476,7 +2487,6 @@ Mga karagdagang detalye:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Parehong pagkakasunod-sunod nang nakatihaya</translation>
-<translation id="777702478322588152">Prefecture</translation>
<translation id="7791011319128895129">Hindi pa na-release</translation>
<translation id="7791196057686275387">Bale</translation>
<translation id="7791543448312431591">Idagdag</translation>
@@ -2567,12 +2577,12 @@ Mga karagdagang detalye:
<translation id="8055534648776115597">Bokasyonal at ipinagpatuloy na edukasyon</translation>
<translation id="8057711352706143257">Hindi maayos na naka-configure ang "<ph name="SOFTWARE_NAME" />." Kadalasang naaayos ang problema kapag in-uninstall ang "<ph name="SOFTWARE_NAME" />." <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Paggawa ng pagkain</translation>
-<translation id="8066955247577885446">Paumanhin, nagkaproblema.</translation>
<translation id="8067872629359326442">Kakalagay mo lang ng iyong password sa isang mapanlinang na site. Makakatulong ang Chromium. Para palitan ang iyong password at abisuhan ang Google na posibleng nasa panganib ang iyong account, i-click ang Protektahan ang Account.</translation>
<translation id="8070439594494267500">Icon ng app</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Gumawa ng bagong Google Sheet nang mabilis</translation>
<translation id="8075898834294118863">Pamahalaan ang mga setting ng site</translation>
+<translation id="8076492880354921740">Mga Tab</translation>
<translation id="8078141288243656252">Hindi maaaring i-annotate kapag naka-rotate</translation>
<translation id="8079031581361219619">I-reload ang site?</translation>
<translation id="8081087320434522107">Mga sedan</translation>
@@ -2695,6 +2705,7 @@ Mga karagdagang detalye:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Mga Setting</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, pindutin ang Tab pagkatapos ay ang Enter para pamahalaan ang iyong mga kagustuhan sa cookie sa mga setting ng Chrome</translation>
<translation id="8433057134996913067">Masa-sign out ka sa karamihan ng mga website.</translation>
<translation id="8434840396568290395">Mga alagang hayop</translation>
@@ -2792,6 +2803,7 @@ Mga karagdagang detalye:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> ang iyong code para sa <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">I-bookmark ang tab na ito</translation>
<translation id="8751426954251315517">Pakisubukang muli sa susunod</translation>
+<translation id="8757526089434340176">Available ang alok ng Google Pay</translation>
<translation id="8758885506338294482">Competitive na video gaming</translation>
<translation id="8759274551635299824">Nag-expire na ang card na ito</translation>
<translation id="87601671197631245">Gumagamit ang site na ito ng lumang configuration sa seguridad, na posibleng maglantad ng iyong impormasyon (halimbawa, mga password, mensahe, o credit card) kapag ipinadala ito sa site na ito.</translation>
@@ -2799,6 +2811,7 @@ Mga karagdagang detalye:
<translation id="8763927697961133303">USB device</translation>
<translation id="8763986294015493060">Isara ang lahat ng Incognito window na kasalukuyang nakabukas</translation>
<translation id="8766943070169463815">Binuksan ang sheet para sa pag-authenticate ng kredensyal sa secure na pagbabayad</translation>
+<translation id="8767765348545497220">Isara ang bubble ng tulong</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Mga motorsiklo</translation>
<translation id="8790007591277257123">&amp;Gawing muli ang pagtanggal</translation>
@@ -2811,6 +2824,7 @@ Mga karagdagang detalye:
<translation id="8806285662264631610">Mga produktong panligo at pangkatawan</translation>
<translation id="8807160976559152894">Paikliin pagkatapos ng bawat page</translation>
<translation id="8808828119384186784">Mga Setting ng Chrome</translation>
+<translation id="8813277370772331957">Ipaalala sa akin sa ibang pagkakataon</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, pindutin ang Tab at pagkatapos ay ang Enter para i-update ang Chrome mula sa iyong mga setting ng Chrome</translation>
<translation id="8820817407110198400">Mga Bookmark</translation>
<translation id="882338992931677877">Manual na Slot</translation>
@@ -2990,6 +3004,7 @@ Mga karagdagang detalye:
<translation id="988159990683914416">Bumuo ang Developer</translation>
<translation id="989988560359834682">I-edit ang Address</translation>
<translation id="991413375315957741">mga sensor ng paggalaw o liwanag</translation>
+<translation id="992110854164447044">Itinatago ng virtual card ang iyong aktwal na card para makatulong na protektahan ka laban sa potensyal na panloloko. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Pink</translation>
<translation id="992432478773561401">Hindi na-install nang maayos ang "<ph name="SOFTWARE_NAME" />" sa iyong computer o sa network:
diff --git a/chromium/components/strings/components_strings_fr-CA.xtb b/chromium/components/strings/components_strings_fr-CA.xtb
index 70513526e42..6cb28808fca 100644
--- a/chromium/components/strings/components_strings_fr-CA.xtb
+++ b/chromium/components/strings/components_strings_fr-CA.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Subventions, bourses d'études et aide financière</translation>
<translation id="1048785276086539861">Lorsque vous modifiez des annotations, ce document s'affiche au format de vue par page</translation>
<translation id="1050038467049342496">Fermer les autres applications</translation>
+<translation id="1053959602163383901">Vous avez choisi de valider avec un appareil d'authentification sur des sites Web qui utilisent <ph name="PROVIDER_ORIGIN" />. Ce fournisseur peut avoir stocké de l'information sur votre mode de paiement, dont vous pouvez <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Annuler l'ajout</translation>
<translation id="1056663316309890343">Logiciels de photo</translation>
<translation id="1056898198331236512">Avertissement</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">Mode de ramassage</translation>
<translation id="1281476433249504884">Empileur 1</translation>
<translation id="1285320974508926690">Ne jamais traduire ce site</translation>
+<translation id="1288548991597756084">Enregistrez la carte de façon sécurisée</translation>
<translation id="1292571435393770077">Bac 16</translation>
<translation id="1292701964462482250">« Un logiciel installé sur votre ordinateur empêche Chrome de se connecter au Web de manière sécurisée » (sous Windows uniquement)</translation>
<translation id="1294154142200295408">Variantes de ligne de commande</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Pour corriger l'erreur, cliquez sur &lt;strong&gt;Connexion&lt;/strong&gt; sur la page que vous essayez d'ouvrir.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Aménagement paysager</translation>
<translation id="1513706915089223971">Liste des entrées d'historique</translation>
+<translation id="1516097932025103760">Elle sera chiffrée et enregistrée de façon sécurisée, et le code CVC ne sera jamais stocké.</translation>
<translation id="1517433312004943670">Un numéro de téléphone est requis</translation>
<translation id="1519264250979466059">Date de création</translation>
<translation id="1521159554480556801">Arts textiles et de la fibre</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Enregistrer et remplir les modes de paiement</translation>
<translation id="1663943134801823270">Les cartes et les adresses proviennent de Chrome. Vous pouvez les gérer dans <ph name="BEGIN_LINK" />Paramètres<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Les pages en <ph name="SOURCE_LANGUAGE" /> seront désormais traduites en <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Payez avec <ph name="CARD_DETAIL" /> pour utiliser l'offre</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> en <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Bord court en premier</translation>
<translation id="168693727862418163">La validation de la valeur de cette politique par rapport à son schéma a échoué. La politique sera ignorée.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Capteurs de mouvements</translation>
<translation id="1717494416764505390">Boîte aux lettres 3</translation>
<translation id="1718029547804390981">Ce document est trop volumineux pour être annoté</translation>
+<translation id="1720941539803966190">Fermer le tutoriel</translation>
<translation id="1721424275792716183">* Ce champ est obligatoire</translation>
<translation id="1727613060316725209">Le certificat est valide</translation>
<translation id="1727741090716970331">Ajouter un numéro de carte valide</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">Barbecue et grillades</translation>
<translation id="2053111141626950936">Les pages en <ph name="LANGUAGE" /> ne seront pas traduites.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Lorsque cette commande est activée et que son état est défini à actif, Chrome détermine à quel grand groupe de personnes, ou « cohorte », votre activité de navigation récente ressemble le plus. Les annonceurs peuvent sélectionner des annonces pour le groupe, et votre activité de navigation reste privée sur votre appareil. Votre groupe est mis à jour tous les jours.}=1{Lorsque cette commande est activée et que son état est défini à actif, Chrome détermine à quel grand groupe de personnes, ou « cohorte », votre activité de navigation récente ressemble le plus. Les annonceurs peuvent sélectionner des annonces pour le groupe, et votre activité de navigation reste privée sur votre appareil. Votre groupe est mis à jour tous les jours.}one{Lorsque cette commande est activée et que son état est défini à actif, Chrome détermine à quel grand groupe de personnes, ou « cohorte », votre activité de navigation récente ressemble le plus. Les annonceurs peuvent sélectionner des annonces pour le groupe, et votre activité de navigation reste privée sur votre appareil. Votre groupe est mis à jour chaque {NUM_DAYS} jour.}other{Lorsque cette commande est activée et que son état est défini à actif, Chrome détermine à quel grand groupe de personnes, ou « cohorte », votre activité de navigation récente ressemble le plus. Les annonceurs peuvent sélectionner des annonces pour le groupe, et votre activité de navigation reste privée sur votre appareil. Votre groupe est mis à jour tous les {NUM_DAYS} jours.}}</translation>
-<translation id="2053553514270667976">Code postal</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggestion}one{# suggestion}other{# suggestions}}</translation>
+<translation id="2066915425250589881">demander la suppression</translation>
<translation id="2068528718802935086">Bébés et tout-petits</translation>
<translation id="2071156619270205202">Cette carte n'est pas compatible avec un numéro de carte virtuelle.</translation>
<translation id="2071692954027939183">Les notifications ont été bloquées automatiquement parce que vous ne les autorisez généralement pas</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">Bouton Gérer la synchronisation, appuyez sur Entrée pour gérer les données que vous synchronisez dans les paramètres de Chrome</translation>
<translation id="2091887806945687916">Son</translation>
<translation id="2094505752054353250">Le domaine ne correspond pas</translation>
-<translation id="2096368010154057602">Département</translation>
<translation id="2099652385553570808">Triple agrafe à gauche</translation>
<translation id="2101225219012730419">Version :</translation>
<translation id="2102134110707549001">Suggérer un mot de passe fort…</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">Football américain</translation>
<translation id="2187317261103489799">Détecter (par défaut)</translation>
<translation id="2188375229972301266">Perforation multiple en bas</translation>
-<translation id="2188852899391513400">Le mot de passe que vous venez juste d'utiliser a été trouvé dans une violation de données. Pour sécuriser vos comptes, le Gestionnaire de mots de passe Google vous recommande de le modifier maintenant, puis de vérifier vos mots de passe enregistrés.</translation>
<translation id="219906046732893612">Rénovation domiciliaire</translation>
<translation id="2202020181578195191">Entrez une année d'expiration valide</translation>
<translation id="22081806969704220">Bac 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">Modification des fichiers</translation>
<translation id="2215963164070968490">Chiens</translation>
<translation id="2218879909401188352">Les cyberpirates actuellement à l'œuvre sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pourraient installer des applications dangereuses afin d'endommager votre appareil, d'ajouter des frais cachés à votre facture de téléphonie cellulaire ou de voler vos données personnelles. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Redémarrer le tutoriel</translation>
+<translation id="2219735899272417925">Réinitialisation de l'appareil requise</translation>
<translation id="2224337661447660594">Aucune connexion Internet</translation>
<translation id="2230458221926704099">Réparez votre connexion à l'aide de l'<ph name="BEGIN_LINK" />application de diagnostic<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Envoyer maintenant</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">Non autorisés (par défaut)</translation>
<translation id="2512413427717747692">Définir Chrome comme bouton de navigateur par défaut, appuyez sur Entrée pour définir Chrome comme navigateur par défaut du système dans les réglages iOS</translation>
<translation id="2515629240566999685">De vérifier le signal dans votre zone</translation>
+<translation id="2515761554693942801">Vous avez choisi de valider avec Touch ID sur des sites Web qui utilisent <ph name="PROVIDER_ORIGIN" />. Ce fournisseur peut avoir stocké de l'information sur votre mode de paiement, dont vous pouvez <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Agrafe en bas à droite</translation>
<translation id="2521736961081452453">Créer un formulaire</translation>
<translation id="2523886232349826891">Carte enregistrée sur cet appareil uniquement</translation>
<translation id="2524461107774643265">Ajouter plus de renseignements</translation>
<translation id="2529899080962247600">Ce champ ne peut pas contenir plus de <ph name="MAX_ITEMS_LIMIT" /> entrées. Toutes les entrées supplémentaires seront ignorées.</translation>
+<translation id="253493526287553278">Voir les détails du code promotionnel</translation>
<translation id="2535585790302968248">Ouvrir une nouvelle fenêtre en mode de navigation privée pour naviguer en mode privé</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{et 1 autre}one{et # autre}other{et # autres}}</translation>
<translation id="2536110899380797252">Ajouter une adresse</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">Arts photographiques et numériques</translation>
<translation id="2601150049980261779">Films de romance</translation>
<translation id="2604589665489080024">Musique pop</translation>
-<translation id="2609632851001447353">Variations</translation>
<translation id="2610561535971892504">Cliquer pour copier</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />n'enregistre pas<ph name="END_EMPHASIS" /> les informations suivantes :
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc 8K</translation>
<translation id="2677696497921480781">Fêtes et anniversaires</translation>
<translation id="2677748264148917807">Quitter</translation>
+<translation id="2679714844901977852">Enregistrez votre carte et vos données de facturation dans votre compte Google <ph name="USER_EMAIL" /> pour effectuer des paiements sécurisés et plus rapides</translation>
<translation id="2684561033061424857">11 po x 12 po</translation>
<translation id="2687555958734450033">Ajustement optimal</translation>
<translation id="2688969097326701645">Oui, continuer</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">Fournisseurs d'accès Internet (FAI)</translation>
<translation id="2856444702002559011">Les cyberpirates peuvent essayer de voler vos données à partir de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (par exemple, des mots de passe, des messages ou des numéros de carte de crédit). <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Ce site diffuse des annonces intrusives ou trompeuses.</translation>
-<translation id="286512204874376891">Une carte virtuelle masque votre carte réelle pour vous protéger de fraudes potentielles. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Amical</translation>
<translation id="28761159517501904">Films</translation>
<translation id="2876489322757410363">Vous devez désactiver le mode de navigation privée pour effectuer un paiement par l'intermédiaire d'une application externe. Continuer?</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">Disque</translation>
<translation id="3162559335345991374">Le réseau Wi-Fi que vous utilisez peut vous demander de visiter sa page de connexion.</translation>
<translation id="3169472444629675720">Découvrir</translation>
-<translation id="3174168572213147020">ÃŽle</translation>
<translation id="3176929007561373547">Vérifiez vos paramètres de mandataire ou communiquez avec votre administrateur
réseau pour vous assurer que le serveur mandataire fonctionne. Si vous
ne pensez pas devoir utiliser de serveur mandataire, procédez comme suit :
@@ -877,9 +882,6 @@
<translation id="3369192424181595722">Erreur d'horloge</translation>
<translation id="3369459162151165748">Pièces et accessoires pour véhicules</translation>
<translation id="3371064404604898522">Définir Chrome comme navigateur par défaut</translation>
-<translation id="3371076217486966826"><ph name="URL" /> souhaite effectuer les actions suivantes :
- • Créer une carte 3D de votre environnement ou faire le suivi de la position de l'appareil photo
- • Utiliser votre appareil photo</translation>
<translation id="337363190475750230">Révoqué</translation>
<translation id="3375754925484257129">Effectuer une vérification de sécurité de Chrome</translation>
<translation id="3377144306166885718">Le serveur a utilisé une version obsolète de TLS.</translation>
@@ -896,6 +898,7 @@
<translation id="3399952811970034796">Adresse de livraison</translation>
<translation id="3402261774528610252">La connexion utilisée pour charger ce site faisait appel à TLS 1.0 ou à TLS 1.1, qui sont des versions obsolètes et qui seront désactivées à l'avenir. Une fois qu'elles seront désactivées, les utilisateurs ne seront pas en mesure de charger ce site. Le serveur devrait activer TLS 1.2 ou une version ultérieure.</translation>
<translation id="3405664148539009465">Personnaliser les polices</translation>
+<translation id="3407789382767355356">connexion tierce</translation>
<translation id="3409896703495473338">Gérer les paramètres de sécurité</translation>
<translation id="3414952576877147120">Taille :</translation>
<translation id="3417660076059365994">Les fichiers que vous téléversez ou joignez sont envoyés à Google Cloud ou à des tiers afin d'être analysés. Par exemple, ils pourraient être analysés pour déterminer s'ils contiennent des données confidentielles ou des logiciels malveillants.</translation>
@@ -928,6 +931,7 @@
<translation id="3477679029130949506">Films à l'affiche et horaires de représentations</translation>
<translation id="3479552764303398839">Pas maintenant</translation>
<translation id="3484560055331845446">Vous risquez de perdre l'accès à votre compte Google. Chrome recommande de modifier votre mot de passe immédiatement. Vous serez invité à vous connecter.</translation>
+<translation id="3484861421501147767">Rappel : code promotionnel enregistré disponible</translation>
<translation id="3487845404393360112">Bac 4</translation>
<translation id="3495081129428749620">Rechercher dans la page
<ph name="PAGE_TITLE" /></translation>
@@ -1052,6 +1056,7 @@
<translation id="3810973564298564668">Gérer</translation>
<translation id="3816482573645936981">Valeur (remplacée)</translation>
<translation id="382518646247711829">Si vous utilisez un serveur mandataire...</translation>
+<translation id="3826050100957962900">Connexion tierce</translation>
<translation id="3827112369919217609">Absolu</translation>
<translation id="3827666161959873541">Films familiaux</translation>
<translation id="3828924085048779000">La phrase de passe est obligatoire.</translation>
@@ -1064,9 +1069,9 @@
<translation id="3858027520442213535">Mettre à jour la date et l'heure</translation>
<translation id="3858860766373142691">Nom</translation>
<translation id="3872834068356954457">Sciences</translation>
+<translation id="3875783148670536197">Afficher la marche à suivre</translation>
<translation id="3881478300875776315">Afficher moins de lignes</translation>
<translation id="3884278016824448484">Identifiant de périphérique en conflit</translation>
-<translation id="3885155851504623709">Paroisse</translation>
<translation id="388632593194507180">Surveillance détectée</translation>
<translation id="3886948180919384617">Empileur 3</translation>
<translation id="3890664840433101773">Ajouter une adresse de courriel</translation>
@@ -1096,9 +1101,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> est bloquée</translation>
<translation id="3978338123949022456">Mode Recherche, tapez une requête et appuyez sur Entrée pour rechercher avec <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Oiseaux</translation>
+<translation id="3985750352229496475">Gérer les adresses…</translation>
<translation id="3986705137476756801">Désactiver la transcription instantanée pour le moment</translation>
<translation id="3987940399970879459">Moins de 1 Mo</translation>
<translation id="3990250421422698716">Décalage</translation>
+<translation id="3992684624889376114">À propos de cette page</translation>
<translation id="3996311196211510766">Le site <ph name="ORIGIN" /> a exigé qu'une politique d'origine soit appliquée à toutes les demandes qu'il reçoit, mais cette politique ne peut pas être appliquée actuellement.</translation>
<translation id="4006465311664329701">Modes de paiement, offres et adresses utilisant Google Pay</translation>
<translation id="4009243425692662128">Le contenu des pages que vous imprimez est envoyé à Google Cloud ou à des tiers à des fins d'analyse. Par exemple, il pourrait être analysé pour déterminer s'il contient des données confidentielles.</translation>
@@ -1218,6 +1225,7 @@
<translation id="4305666528087210886">Impossible d'accéder à votre fichier</translation>
<translation id="4306529830550717874">Enregistrer l'adresse?</translation>
<translation id="4306812610847412719">Presse-papiers</translation>
+<translation id="4310070645992025887">Recherchez vos explorations</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloquer (par défaut)</translation>
<translation id="4314815835985389558">Gérer la synchronisation</translation>
@@ -1268,6 +1276,7 @@
<translation id="4435702339979719576">Carte postale</translation>
<translation id="443673843213245140">L'utilisation d'un mandataire est désactivée, mais une configuration de mandataire explicite est spécifiée.</translation>
<translation id="4441832193888514600">Configuration ignorée parce que la politique peut uniquement être définie en tant que politique utilisateur du nuage.</translation>
+<translation id="4442470707340296952">Onglets Chrome</translation>
<translation id="4450893287417543264">Ne plus afficher</translation>
<translation id="4451135742916150903">Les sites peuvent demander à se connecter à des appareils HID</translation>
<translation id="4452328064229197696">Le mot de passe que vous venez juste d'utiliser a été trouvé dans une violation de données. Pour sécuriser vos comptes, le Gestionnaire de mots de passe Google vous recommande de vérifier vos mots de passe enregistrés.</translation>
@@ -1406,6 +1415,7 @@
<translation id="483241715238664915">Activer les avertissements</translation>
<translation id="4834250788637067901">Modes de paiement, offres et adresses utilisant Google Pay</translation>
<translation id="4838327282952368871">Rêveur</translation>
+<translation id="4839087176073128681">Payez plus rapidement la prochaine fois et protégez votre carte grâce à la sécurité de pointe de Google.</translation>
<translation id="4840250757394056958">Afficher votre historique de Chrome</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">Bénéficiez d'un rabais sur <ph name="MERCHANT_NAME" /> et d'autres</translation>
@@ -1468,6 +1478,7 @@
<translation id="5011561501798487822">Langue détectée</translation>
<translation id="5015510746216210676">Nom de la machine :</translation>
<translation id="5017554619425969104">Texte que vous avez copié</translation>
+<translation id="5017828934289857214">Me le rappeler plus tard</translation>
<translation id="5018422839182700155">Impossible d'ouvrir cette page</translation>
<translation id="5019198164206649151">L'espace de stockage destiné à la sauvegarde est en mauvais état</translation>
<translation id="5020776957610079374">Musique du monde</translation>
@@ -1487,6 +1498,7 @@
<translation id="5051305769747448211">Spectacles d'humour</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Pour envoyer ce fichier au moyen du partage à proximité, libérez de l'espace de stockage (<ph name="DISK_SPACE_SIZE" />) sur votre appareil}one{Pour envoyer ce fichier au moyen du partage à proximité, libérez de l'espace de stockage (<ph name="DISK_SPACE_SIZE" />) sur votre appareil}other{Pour envoyer ces fichiers au moyen du partage à proximité, libérez de l'espace de stockage (<ph name="DISK_SPACE_SIZE" />) sur votre appareil}}</translation>
<translation id="5056549851600133418">Articles pour vous</translation>
+<translation id="5060483733937416656">Vous avez choisi de valider avec Windows Hello sur des sites Web qui utilisent <ph name="PROVIDER_ORIGIN" />. Ce fournisseur peut avoir stocké de l'information sur votre mode de paiement, dont vous pouvez <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Vouliez-vous dire <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Historique d'impression</translation>
<translation id="5068234115460527047">Fonds spéculatifs</translation>
@@ -1500,10 +1512,8 @@
<translation id="5087286274860437796">Le certificat du serveur n'est pas valide présentement.</translation>
<translation id="5087580092889165836">Ajouter une carte</translation>
<translation id="5088142053160410913">Message à l'opérateur</translation>
-<translation id="5089810972385038852">État/province</translation>
<translation id="5093232627742069661">Pli en Z</translation>
<translation id="5094747076828555589">Impossible de vérifier sur le serveur qu'il s'agit bien du domaine <ph name="DOMAIN" />, car son certificat de sécurité n'est pas considéré comme fiable par Chromium. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique.</translation>
-<translation id="5095208057601539847">Province</translation>
<translation id="5097099694988056070">Statistiques relatives à l'appareil, comme l'utilisation du processeur ou de la mémoire vive</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Ce site n'est pas sécurisé</translation>
@@ -1541,6 +1551,7 @@
<translation id="5171045022955879922">Effectuez une recherche ou entrez une adresse URL</translation>
<translation id="5171689220826475070">Pli en éventail, Europe</translation>
<translation id="5172758083709347301">Machine</translation>
+<translation id="5177076414499237632">En savoir plus sur la source et le sujet de cette page</translation>
<translation id="5179510805599951267">Cette page n'est pas rédigée en <ph name="ORIGINAL_LANGUAGE" />? Signaler l'erreur.</translation>
<translation id="518639307526414276">Aliments et accessoires pour animaux</translation>
<translation id="5190835502935405962">Barre de favoris</translation>
@@ -1701,6 +1712,7 @@
<translation id="5624120631404540903">Gérer les mots de passe</translation>
<translation id="5629630648637658800">Échec du chargement des paramètres de la règle</translation>
<translation id="5631439013527180824">Jeton de gestion de l'appareil non valide.</translation>
+<translation id="5632485077360054581">Afficher la marche à suivre</translation>
<translation id="5633066919399395251">Les cyberpirates actuellement à l'œuvre sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> peuvent essayer d'installer des programmes dangereux sur votre ordinateur pour voler ou supprimer vos renseignements (par exemple, des photos, des mots de passe, des messages et des numéros de carte de crédit). <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Contenu trompeur bloqué.</translation>
<translation id="5633259641094592098">Films cultes et indépendants</translation>
@@ -1818,6 +1830,7 @@
<translation id="5989320800837274978">Aucun serveur mandataire fixe ni URL de script .pac indiqué.</translation>
<translation id="5992691462791905444">Technique de pli en Z</translation>
<translation id="5995727681868049093">Gérer vos renseignements, la confidentialité et la sécurité de votre compte Google</translation>
+<translation id="5997247540087773573">Le mot de passe que vous venez d'utiliser a été trouvé dans un cas de violation de données. Pour sécuriser vos comptes, le gestionnaire de mots de passe de Google vous recommande de le changer maintenant et de vérifier vos mots de passe enregistrés.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> résultats trouvés pour « <ph name="SEARCH_TEXT" /> »</translation>
<translation id="6006484371116297560">Classique</translation>
<translation id="6008122969617370890">Ordre N sur 1</translation>
@@ -1913,7 +1926,6 @@
<translation id="627746635834430766">Pour accélérer le paiement la prochaine fois, enregistrez votre carte et votre adresse de facturation dans votre compte Google.</translation>
<translation id="6279183038361895380">Appuyez sur |<ph name="ACCELERATOR" />| pour afficher votre curseur</translation>
<translation id="6280223929691119688">Impossible d'effectuer une livraison à cette adresse. Sélectionnez une autre adresse.</translation>
-<translation id="6282194474023008486">Code postal</translation>
<translation id="6285507000506177184">Bouton Gérer les téléchargements dans Chrome, appuyez sur la touche Entrée pour gérer les fichiers que vous avez téléchargés dans Chrome</translation>
<translation id="6289939620939689042">Couleur de la page</translation>
<translation id="6290238015253830360">Les articles que vous avez suggérés s'afficheront ici</translation>
@@ -1935,6 +1947,7 @@
<translation id="6337133576188860026">Libère moins de <ph name="SIZE" />. Certains sites peuvent être plus longs à charger lors de votre prochaine visite.</translation>
<translation id="6337534724793800597">Filtrer les règles par nom</translation>
<translation id="6340739886198108203">La politique de l'administrateur ne recommande pas de faire des captures d'écran ni des enregistrements lorsque du contenu confidentiel est visible :</translation>
+<translation id="6348220984832452017">Variantes actives</translation>
<translation id="6349101878882523185">Installer <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Aucun}=1{1 mot de passe (pour <ph name="DOMAIN_LIST" />, synchronisé)}=2{2 mots de passe (pour <ph name="DOMAIN_LIST" />, synchronisés)}one{# mot de passe (pour <ph name="DOMAIN_LIST" />, synchronisé)}other{# mots de passe (pour <ph name="DOMAIN_LIST" />, synchronisés)}}</translation>
<translation id="6355392890578844978">Ce navigateur n'est pas géré par une entreprise ni une organisation. L'activité sur cet appareil peut être gérée à l'extérieur de Chromium. <ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" /></translation>
@@ -1966,7 +1979,6 @@
<translation id="643051589346665201">Modifier le mot de passe Google</translation>
<translation id="6433490469411711332">Modifier les coordonnées</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> a refusé la connexion.</translation>
-<translation id="6438025220577812695">Le modifier moi-même</translation>
<translation id="6440503408713884761">Ignorée</translation>
<translation id="6443406338865242315">Les extensions et les plugiciels que vous avez installés</translation>
<translation id="6446163441502663861">Enveloppe Kahu</translation>
@@ -2096,9 +2108,9 @@
<translation id="6828866289116430505">Génétique</translation>
<translation id="6831043979455480757">Traduire</translation>
<translation id="6833752742582340615">Enregistrez votre carte et vos données de facturation dans votre compte Google pour effectuer des paiements sécurisés et plus rapides</translation>
-<translation id="6839929833149231406">Région</translation>
<translation id="6846340164947227603">Utiliser un numéro de carte virtuelle…</translation>
<translation id="6852204201400771460">Actualiser l'application?</translation>
+<translation id="6857776781123259569">Gérer les mots de passe…</translation>
<translation id="686485648936420384">Ressources pour les consommateurs</translation>
<translation id="6865412394715372076">Cette carte ne peut pas être vérifiée pour le moment</translation>
<translation id="6869334554832814367">Prêts personnels</translation>
@@ -2147,7 +2159,6 @@
<translation id="6965978654500191972">Appareil</translation>
<translation id="696703987787944103">Perceptuel</translation>
<translation id="6968269510885595029">Utilisez votre clé de sécurité</translation>
-<translation id="6970216967273061347">District</translation>
<translation id="6971439137020188025">Créer rapidement une présentation Google dans Présentations</translation>
<translation id="6972629891077993081">Appareils HID</translation>
<translation id="6973656660372572881">Des serveurs mandataires fixes et une URL de script .pac sont spécifiés.</translation>
@@ -2186,7 +2197,6 @@
<translation id="7081308185095828845">Cette fonctionnalité n'est pas offerte pour votre appareil.</translation>
<translation id="7083258188081898530">Bac 9</translation>
<translation id="7086090958708083563">Téléversement demandé par l'utilisateur</translation>
-<translation id="7087282848513945231">Comté</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, appuyez sur Tabulation, puis sur Entrée afin de gérer les autorisations et les données stockées pour les sites dans les paramètres de Chrome</translation>
<translation id="7096937462164235847">L'identité de ce site Web n'est pas vérifiée.</translation>
<translation id="7101893872976785596">Films d'horreur</translation>
@@ -2205,10 +2215,10 @@
<ph name="LIST_ITEM" />Les informations saisies dans les formulaires<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Impossible d'effectuer une livraison à cette adresse. Sélectionnez une autre adresse.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Votre administrateur a interdit la copie de ces données.</translation>
<translation id="7135130955892390533">Afficher l’état</translation>
<translation id="7138472120740807366">Mode de livraison rapide</translation>
-<translation id="7139724024395191329">Émirat</translation>
<translation id="7139892792842608322">Plateau principal</translation>
<translation id="714064300541049402">Décalage X de l'image côté 2</translation>
<translation id="7152423860607593928">Enveloppe n° 14</translation>
@@ -2425,6 +2435,7 @@ Détails supplémentaires :
<translation id="7669271284792375604">Les cyberpirates sur ce site pourraient tenter de vous inciter à installer des programmes qui nuisent à votre expérience de navigation (par exemple en changeant votre page d'accueil ou en affichant des annonces supplémentaires sur les sites que vous consultez).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Actions effectuées au moyen de données signalées comme étant confidentielles (1 action depuis la connexion). <ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" />}one{Actions effectuées au moyen de données signalées comme étant confidentielles (# action depuis la connexion). <ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" />}other{Actions effectuées au moyen de données signalées comme étant confidentielles (# actions depuis la connexion). <ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Boîte aux lettres 6</translation>
+<translation id="7675325315208090829">Gérer vos modes de paiement…</translation>
<translation id="7676643023259824263">Recherche de texte du presse-papiers, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Affichez et gérez votre historique de navigation dans les paramètres de Chrome</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2467,7 +2478,6 @@ Détails supplémentaires :
<translation id="7766518757692125295">Jupe</translation>
<translation id="7770259615151589601">Désigné long</translation>
<translation id="7773005668374414287">Même ordre, face vers le haut</translation>
-<translation id="777702478322588152">Préfecture</translation>
<translation id="7791011319128895129">Non publiée</translation>
<translation id="7791196057686275387">Balle</translation>
<translation id="7791543448312431591">Ajouter</translation>
@@ -2558,12 +2568,12 @@ Détails supplémentaires :
<translation id="8055534648776115597">Formations professionnelles et continues</translation>
<translation id="8057711352706143257">« <ph name="SOFTWARE_NAME" /> » n'est pas correctement configuré. La désinstallation de « <ph name="SOFTWARE_NAME" /> » résout généralement le problème. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Production alimentaire</translation>
-<translation id="8066955247577885446">Désolés, une erreur s'est produite.</translation>
<translation id="8067872629359326442">Vous venez d'entrer votre mot de passe sur un site trompeur. Chromium peut vous aider. Pour modifier votre mot de passe et avertir Google que votre compte pourrait être en danger, cliquez sur Protéger le compte.</translation>
<translation id="8070439594494267500">Icône de l'application</translation>
<translation id="8074253406171541171">Enveloppe 10 po x 13 po</translation>
<translation id="8075736640322370409">Créer rapidement une feuille Google</translation>
<translation id="8075898834294118863">Gérer les paramètres de site</translation>
+<translation id="8076492880354921740">Onglets</translation>
<translation id="8078141288243656252">Impossible d'annoter lorsque le document est pivoté</translation>
<translation id="8079031581361219619">Actualiser le site?</translation>
<translation id="8081087320434522107">Berlines</translation>
@@ -2686,6 +2696,7 @@ Détails supplémentaires :
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Paramètres</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, appuyez sur Tabulation, puis sur Entrée pour gérer vos préférences relatives aux témoins dans les paramètres de Chrome</translation>
<translation id="8433057134996913067">Cette option vous déconnecte de la plupart des sites Web.</translation>
<translation id="8434840396568290395">Animaux de compagnie</translation>
@@ -2783,6 +2794,7 @@ Détails supplémentaires :
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> est votre code pour <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Ajouter cet onglet aux favoris</translation>
<translation id="8751426954251315517">Veuillez réessayer la prochaine fois</translation>
+<translation id="8757526089434340176">Offre Google Pay</translation>
<translation id="8758885506338294482">Compétitions de jeux vidéo</translation>
<translation id="8759274551635299824">Cette carte a expiré</translation>
<translation id="87601671197631245">Ce site utilise une configuration de sécurité obsolète qui pourrait exposer vos données personnelles (par exemple, vos mots de passe, vos messages ou vos numéros de carte de crédit) lorsqu'elles sont envoyées à ce site.</translation>
@@ -2790,6 +2802,7 @@ Détails supplémentaires :
<translation id="8763927697961133303">Appareil USB</translation>
<translation id="8763986294015493060">Fermer toutes les fenêtres de navigation privée qui sont actuellement ouvertes</translation>
<translation id="8766943070169463815">La fiche d'authentification de l'authentifiant de paiement sécurisé est ouverte</translation>
+<translation id="8767765348545497220">Fermer la bulle d'aide</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motos</translation>
<translation id="8790007591277257123">&amp;Rétablir la suppression</translation>
@@ -2802,6 +2815,7 @@ Détails supplémentaires :
<translation id="8806285662264631610">Produits pour le bain et le corps</translation>
<translation id="8807160976559152894">Couper après chaque page</translation>
<translation id="8808828119384186784">Paramètres Chrome</translation>
+<translation id="8813277370772331957">Me le rappeler plus tard</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, appuyez sur la touche Tabulation, puis sur la touche Entrée pour mettre à jour Chrome dans les paramètres de Chrome</translation>
<translation id="8820817407110198400">Favoris</translation>
<translation id="882338992931677877">Insertion manuelle</translation>
@@ -2981,6 +2995,7 @@ Détails supplémentaires :
<translation id="988159990683914416">Version de développeur</translation>
<translation id="989988560359834682">Modifier l'adresse</translation>
<translation id="991413375315957741">Capteurs de mouvement ou de luminosité</translation>
+<translation id="992110854164447044">Une carte virtuelle masque votre carte réelle pour vous protéger de fraudes potentielles. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rose</translation>
<translation id="992432478773561401">« <ph name="SOFTWARE_NAME" /> » n'a pas été correctement installé sur votre ordinateur ou votre réseau :
diff --git a/chromium/components/strings/components_strings_fr.xtb b/chromium/components/strings/components_strings_fr.xtb
index 7a53b3b39fa..411e7e96095 100644
--- a/chromium/components/strings/components_strings_fr.xtb
+++ b/chromium/components/strings/components_strings_fr.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Bourses d'études et aide financière</translation>
<translation id="1048785276086539861">Si vous modifiez des annotations, ce document s'affichera en mode "Vue par page"</translation>
<translation id="1050038467049342496">Fermez les autres applications</translation>
+<translation id="1053959602163383901">Vous avez choisi la validation avec un appareil d'authentification sur les sites Web utilisant <ph name="PROVIDER_ORIGIN" />. Il est possible que ce fournisseur ait stocké des informations sur votre mode de paiement. Vous pouvez <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Annuler l'ajout</translation>
<translation id="1056663316309890343">Logiciels de photo</translation>
<translation id="1056898198331236512">Avertissement</translation>
@@ -119,6 +120,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="1270502636509132238">Mode d'enlèvement</translation>
<translation id="1281476433249504884">Empileur 1</translation>
<translation id="1285320974508926690">Ne jamais traduire ce site</translation>
+<translation id="1288548991597756084">Enregistrez la carte de manière sécurisée</translation>
<translation id="1292571435393770077">Bac 16</translation>
<translation id="1292701964462482250">Un logiciel installé sur votre ordinateur empêche Chrome de se connecter au Web de manière sécurisée (sous Windows uniquement)</translation>
<translation id="1294154142200295408">Variations de ligne de commande</translation>
@@ -223,6 +225,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
&lt;p&gt;Pour corriger cette erreur, cliquez sur &lt;strong&gt;Se connecter&lt;/strong&gt; sur la page que vous essayez d'ouvrir.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Aménagement paysager</translation>
<translation id="1513706915089223971">Liste des entrées de l'historique</translation>
+<translation id="1516097932025103760">Elle sera chiffrée et enregistrée de manière sécurisée, et le code CVC ne sera jamais stocké.</translation>
<translation id="1517433312004943670">Veuillez saisir le numéro de téléphone.</translation>
<translation id="1519264250979466059">Date de création</translation>
<translation id="1521159554480556801">Arts textiles</translation>
@@ -272,7 +275,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="1634828734222219955">Total</translation>
<translation id="163669211644121865">Planification fiscale et établissement de déclarations fiscales</translation>
<translation id="1638780421120290329">Impossible d'enregistrer cette carte</translation>
-<translation id="1639239467298939599">Chargement en cours</translation>
+<translation id="1639239467298939599">Chargement en cours…</translation>
<translation id="1640180200866533862">Règles relatives aux utilisateurs</translation>
<translation id="1640244768702815859">Essayez de <ph name="BEGIN_LINK" />consulter la page d'accueil du site<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">Retarder la sortie jusqu'à</translation>
@@ -288,6 +291,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="1662550410081243962">Enregistrer et saisir les modes de paiement</translation>
<translation id="1663943134801823270">Les cartes et les adresses proviennent de Chrome. Vous pouvez les gérer dans les <ph name="BEGIN_LINK" />paramètres<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Les pages en <ph name="SOURCE_LANGUAGE" /> seront désormais traduites en <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Payez avec <ph name="CARD_DETAIL" /> pour bénéficier de l'offre</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> à <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Bord court en premier</translation>
<translation id="168693727862418163">La valeur de la règle n'a pas été validée par rapport à son schéma et sera ignorée.</translation>
@@ -306,6 +310,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="1717218214683051432">Capteurs de mouvement</translation>
<translation id="1717494416764505390">Boîte aux lettres 3</translation>
<translation id="1718029547804390981">Le document est trop volumineux pour être annoté</translation>
+<translation id="1720941539803966190">Fermer le tutoriel</translation>
<translation id="1721424275792716183">* Champ obligatoire</translation>
<translation id="1727613060316725209">Certificat valide</translation>
<translation id="1727741090716970331">Ajouter un numéro de carte valide</translation>
@@ -422,8 +427,8 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="205212645995975601">Barbecue et grillades</translation>
<translation id="2053111141626950936">Les pages en <ph name="LANGUAGE" /> ne seront pas traduites.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Lorsque cette option est activée et que son état est actif, Chrome détermine le groupe de personnes, ou "cohorte", le plus semblable à votre activité de navigation récente. Les annonceurs peuvent sélectionner des annonces pour ce groupe. Votre activité de navigation restera privée sur cet appareil. Votre groupe est mis à jour tous les jours.}=1{Lorsque cette option est activée et que son état est actif, Chrome détermine le groupe de personnes, ou "cohorte", le plus semblable à votre activité de navigation récente. Les annonceurs peuvent sélectionner des annonces pour ce groupe. Votre activité de navigation restera privée sur cet appareil. Votre groupe est mis à jour tous les jours.}one{Lorsque cette option est activée et que son état est actif, Chrome détermine le groupe de personnes, ou "cohorte", le plus semblable à votre activité de navigation récente. Les annonceurs peuvent sélectionner des annonces pour ce groupe. Votre activité de navigation restera privée sur cet appareil. Votre groupe est mis à jour {NUM_DAYS} fois par jour.}other{Lorsque cette option est activée et que son état est actif, Chrome détermine le groupe de personnes, ou "cohorte", le plus semblable à votre activité de navigation récente. Les annonceurs peuvent sélectionner des annonces pour ce groupe. Votre activité de navigation restera privée sur cet appareil. Votre groupe est mis à jour tous les {NUM_DAYS} jours.}}</translation>
-<translation id="2053553514270667976">Code postal</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggestion}one{# suggestion}other{# suggestions}}</translation>
+<translation id="2066915425250589881">en demander la suppression</translation>
<translation id="2068528718802935086">Bébés et tout-petits</translation>
<translation id="2071156619270205202">Cette carte n'est pas éligible pour le numéro de carte virtuelle.</translation>
<translation id="2071692954027939183">Notifications bloquées automatiquement, car vous les refusez d'habitude</translation>
@@ -435,7 +440,6 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="2088086323192747268">Bouton "Gérer la synchronisation" : appuyez sur Entrée pour gérer les infos que vous synchronisez dans les paramètres Chrome</translation>
<translation id="2091887806945687916">Son</translation>
<translation id="2094505752054353250">Le domaine ne correspond pas</translation>
-<translation id="2096368010154057602">Département</translation>
<translation id="2099652385553570808">Triple agrafe à gauche</translation>
<translation id="2101225219012730419">Version :</translation>
<translation id="2102134110707549001">Suggérer un mot de passe sécurisé…</translation>
@@ -472,7 +476,6 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="2185836064961771414">Football américain</translation>
<translation id="2187317261103489799">Détecter (par défaut)</translation>
<translation id="2188375229972301266">Multiple perforation en bas</translation>
-<translation id="2188852899391513400">Le mot de passe que vous venez d'utiliser a été détecté lors d'une violation de données. Pour sécuriser vos comptes, le Gestionnaire de mots de passe Google vous recommande de le modifier immédiatement, puis de vérifier vos mots de passe enregistrés.</translation>
<translation id="219906046732893612">Travaux d'intérieur</translation>
<translation id="2202020181578195191">Saisissez une année d'expiration valide</translation>
<translation id="22081806969704220">Bac 3</translation>
@@ -483,6 +486,8 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="2215727959747642672">Modification de fichier</translation>
<translation id="2215963164070968490">Chiens</translation>
<translation id="2218879909401188352">Les pirates informatiques qui contrôlent le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> peuvent installer des applications dangereuses qui endommagent votre appareil, ajoutent des frais cachés à votre facture de téléphonie mobile ou dérobent vos informations personnelles. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Relancer le tutoriel</translation>
+<translation id="2219735899272417925">Réinitialisation de l'appareil requise</translation>
<translation id="2224337661447660594">Aucun accès à Internet</translation>
<translation id="2230458221926704099">Vérifiez la connexion à l'aide de l'<ph name="BEGIN_LINK" />application de diagnostic<ph name="END_LINK" />.</translation>
<translation id="2239100178324503013">Envoyer maintenant</translation>
@@ -580,11 +585,13 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="2512101340618156538">Non autorisée (par défaut)</translation>
<translation id="2512413427717747692">Bouton pour choisir Chrome comme navigateur par défaut ; appuyez sur Entrée pour définir Chrome comme navigateur par défaut dans les réglages iOS</translation>
<translation id="2515629240566999685">Vérifiez le signal dans votre zone.</translation>
+<translation id="2515761554693942801">Vous avez choisi la validation avec Touch ID sur les sites Web utilisant <ph name="PROVIDER_ORIGIN" />. Il est possible que ce fournisseur ait stocké des informations sur votre mode de paiement. Vous pouvez <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Agrafe en bas à droite</translation>
<translation id="2521736961081452453">Créer un formulaire</translation>
<translation id="2523886232349826891">Enregistrée sur cet appareil uniquement</translation>
<translation id="2524461107774643265">Ajouter des informations</translation>
<translation id="2529899080962247600">Ce champ ne peut pas contenir plus de <ph name="MAX_ITEMS_LIMIT" /> entrées. Les entrées supplémentaires seront ignorées.</translation>
+<translation id="253493526287553278">Voir les détails du code promotionnel</translation>
<translation id="2535585790302968248">Ouvrez un nouvel onglet de navigation privée pour naviguer de manière anonyme</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{et 1 autre}one{et # autre}other{et # autres}}</translation>
<translation id="2536110899380797252">Ajouter une adresse</translation>
@@ -620,7 +627,6 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="259821504105826686">Photographie et arts numériques</translation>
<translation id="2601150049980261779">Comédies romantiques</translation>
<translation id="2604589665489080024">Musique pop</translation>
-<translation id="2609632851001447353">Variantes</translation>
<translation id="2610561535971892504">Cliquer pour copier</translation>
<translation id="2617988307566202237">Les informations suivantes <ph name="BEGIN_EMPHASIS" />ne seront pas enregistrées<ph name="END_EMPHASIS" /> dans Chrome :
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Fêtes et anniversaires</translation>
<translation id="2677748264148917807">Quitter</translation>
+<translation id="2679714844901977852">Enregistrez votre carte et vos infos de facturation dans votre compte Google <ph name="USER_EMAIL" /> pour régler plus vite et de façon sécurisée</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Taille optimale</translation>
<translation id="2688969097326701645">Oui, continuer</translation>
@@ -701,7 +708,6 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="2854764410992194509">Fournisseurs d'accès Internet (FAI)</translation>
<translation id="2856444702002559011">Des individus malveillants tentent peut-être de subtiliser vos informations personnelles sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (mots de passe, messages ou numéros de carte de crédit, par exemple). <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Ce site affiche des annonces intrusives ou trompeuses.</translation>
-<translation id="286512204874376891">Une carte virtuelle masque votre carte réelle pour vous protéger des fraudes potentielles. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Amical</translation>
<translation id="28761159517501904">Films</translation>
<translation id="2876489322757410363">En payant via une appli externe, vous allez quitter le mode navigation privée. Voulez-vous continuer ?</translation>
@@ -802,7 +808,6 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="3158539265159265653">Disque</translation>
<translation id="3162559335345991374">Pour utiliser ce réseau Wi-Fi, il est possible que vous deviez vous rendre sur la page de connexion correspondante.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">ÃŽle</translation>
<translation id="3176929007561373547">Vérifiez vos paramètres de proxy ou contactez votre administrateur réseau pour
vous assurer que le serveur proxy fonctionne. Si vous
ne pensez pas devoir utiliser de serveur proxy, procédez comme suit :
@@ -881,9 +886,6 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="3369192424181595722">Erreur de l'horloge</translation>
<translation id="3369459162151165748">Pièces et accessoires pour véhicule</translation>
<translation id="3371064404604898522">Définir Chrome comme navigateur par défaut</translation>
-<translation id="3371076217486966826"><ph name="URL" /> souhaite effectuer les opérations suivantes :
- • Créer un plan 3D de votre environnement et suivre la position de votre caméra
- • Utiliser votre caméra</translation>
<translation id="337363190475750230">Révoqué</translation>
<translation id="3375754925484257129">Effectuer un contrôle de sécurité de Chrome</translation>
<translation id="3377144306166885718">Le serveur utilisait une version obsolète de TLS.</translation>
@@ -900,6 +902,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="3399952811970034796">Adresse de livraison</translation>
<translation id="3402261774528610252">La connexion utilisée pour charger ce site l'a fait au moyen de TLS 1.0 ou TLS 1.1, qui sont obsolètes et seront désactivés à l'avenir. Une fois ces protocoles désactivés, les utilisateurs ne seront plus autorisés à charger ce site. Le serveur doit activer TLS 1.2 ou version ultérieure.</translation>
<translation id="3405664148539009465">Personnaliser les polices</translation>
+<translation id="3407789382767355356">connexion tierce</translation>
<translation id="3409896703495473338">Gérer les paramètres de sécurité</translation>
<translation id="3414952576877147120">Taille :</translation>
<translation id="3417660076059365994">Le fichier que vous importez ou joignez est envoyé dans Google Cloud ou à des tiers pour y être analysé (par exemple, pour vérifier qu'il ne contient pas de données sensibles ni de logiciels malveillants).</translation>
@@ -932,6 +935,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="3477679029130949506">Films à l'affiche et séances</translation>
<translation id="3479552764303398839">Pas maintenant</translation>
<translation id="3484560055331845446">Vous pourriez perdre l'accès à votre compte Google. L'équipe Chrome vous recommande de modifier votre mot de passe maintenant. Vous devrez vous connecter.</translation>
+<translation id="3484861421501147767">Rappel : vous avez un code promotionnel enregistré</translation>
<translation id="3487845404393360112">Bac 4</translation>
<translation id="3495081129428749620">Rechercher sur la page
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="3810973564298564668">Gérer</translation>
<translation id="3816482573645936981">Valeur (remplacée)</translation>
<translation id="382518646247711829">Si vous utilisez un serveur proxy…</translation>
+<translation id="3826050100957962900">Connexion tierce</translation>
<translation id="3827112369919217609">Valeur absolue</translation>
<translation id="3827666161959873541">Films tous publics</translation>
<translation id="3828924085048779000">La phrase secrète est obligatoire.</translation>
@@ -1068,9 +1073,9 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="3858027520442213535">Mettre à jour la date et l'heure</translation>
<translation id="3858860766373142691">Nom</translation>
<translation id="3872834068356954457">Sciences</translation>
+<translation id="3875783148670536197">Démonstration</translation>
<translation id="3881478300875776315">Afficher moins de lignes</translation>
<translation id="3884278016824448484">Identifiant de l'appareil en conflit.</translation>
-<translation id="3885155851504623709">Paroisse</translation>
<translation id="388632593194507180">Activité de surveillance détectée</translation>
<translation id="3886948180919384617">Empileur 3</translation>
<translation id="3890664840433101773">Ajouter une adresse e-mail</translation>
@@ -1100,9 +1105,11 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="3973234410852337861"><ph name="HOST_NAME" /> est bloqué</translation>
<translation id="3978338123949022456">Mode Recherche ; saisissez une requête et appuyez sur Entrée pour effectuer une recherche avec "<ph name="KEYWORD_SUFFIX" />"</translation>
<translation id="398470910934384994">Oiseaux</translation>
+<translation id="3985750352229496475">Gérer les adresses…</translation>
<translation id="3986705137476756801">Désactiver les sous-titres instantanés pour le moment</translation>
<translation id="3987940399970879459">Moins de 1 Mo</translation>
<translation id="3990250421422698716">Décalage</translation>
+<translation id="3992684624889376114">À propos de cette page</translation>
<translation id="3996311196211510766">Le site <ph name="ORIGIN" /> a exigé qu'une règle d'origine
soit appliquée à toutes les requêtes qu'il reçoit. Or, cette règle n'est pas applicable actuellement.</translation>
<translation id="4006465311664329701">Modes de paiement, offres et adresses utilisant Google Pay</translation>
@@ -1227,6 +1234,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="4305666528087210886">Impossible d'accéder à votre fichier</translation>
<translation id="4306529830550717874">Enregistrer l'adresse ?</translation>
<translation id="4306812610847412719">presse-papiers</translation>
+<translation id="4310070645992025887">Effectuer une recherche parmi vos parcours</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloquer (par défaut)</translation>
<translation id="4314815835985389558">Gérer la synchronisation</translation>
@@ -1277,6 +1285,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="4435702339979719576">Carte postale)</translation>
<translation id="443673843213245140">L'utilisation d'un proxy est désactivée, mais une configuration de proxy explicite est spécifiée.</translation>
<translation id="4441832193888514600">Règle ignorée, car elle ne peut être définie que comme règle relative aux utilisateurs du cloud.</translation>
+<translation id="4442470707340296952">Onglets Chrome</translation>
<translation id="4450893287417543264">Ne plus afficher</translation>
<translation id="4451135742916150903">Peut demander à se connecter à des périphériques HID</translation>
<translation id="4452328064229197696">Le mot de passe que vous venez d'utiliser a été détecté lors d'une violation de données. Pour sécuriser vos comptes, le Gestionnaire de mots de passe Google vous recommande de vérifier vos mots de passe enregistrés.</translation>
@@ -1415,6 +1424,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="483241715238664915">Activer les avertissements</translation>
<translation id="4834250788637067901">Modes de paiement, offres et adresses utilisant Google Pay</translation>
<translation id="4838327282952368871">Rêveur</translation>
+<translation id="4839087176073128681">Avec le système de sécurité hors pair de Google, payez plus vite la prochaine fois et protégez votre carte.</translation>
<translation id="4840250757394056958">Afficher votre historique Chrome</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">Obtenez des remises sur <ph name="MERCHANT_NAME" /> et plus</translation>
@@ -1477,6 +1487,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="5011561501798487822">Langue détectée</translation>
<translation id="5015510746216210676">Nom de la machine :</translation>
<translation id="5017554619425969104">Texte copié</translation>
+<translation id="5017828934289857214">Me le rappeler plus tard</translation>
<translation id="5018422839182700155">Impossible d'ouvrir cette page</translation>
<translation id="5019198164206649151">L'espace de stockage destiné à la sauvegarde est en mauvais état.</translation>
<translation id="5020776957610079374">Musiques du monde</translation>
@@ -1496,6 +1507,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="5051305769747448211">Spectacles comiques</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Pour partager ce fichier à proximité, libérez de l'espace (<ph name="DISK_SPACE_SIZE" />) sur votre appareil}one{Pour partager ce fichier à proximité, libérez de l'espace (<ph name="DISK_SPACE_SIZE" />) sur votre appareil}other{Pour partager ces fichiers à proximité, libérez de l'espace (<ph name="DISK_SPACE_SIZE" />) sur votre appareil}}</translation>
<translation id="5056549851600133418">Articles pour vous</translation>
+<translation id="5060483733937416656">Vous avez choisi la validation avec Windows Hello sur les sites Web utilisant <ph name="PROVIDER_ORIGIN" />. Il est possible que ce fournisseur ait stocké des informations sur votre mode de paiement. Vous pouvez <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Vous vouliez dire <ph name="LOOKALIKE_DOMAIN" /> ?</translation>
<translation id="5066056036849835175">Historique d'impression</translation>
<translation id="5068234115460527047">Fonds spéculatifs</translation>
@@ -1509,10 +1521,8 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="5087286274860437796">Le certificat actuel du serveur n'est pas valide.</translation>
<translation id="5087580092889165836">Ajouter une carte</translation>
<translation id="5088142053160410913">Message à l'opérateur</translation>
-<translation id="5089810972385038852">État</translation>
<translation id="5093232627742069661">Pli en Z</translation>
<translation id="5094747076828555589">Impossible de vérifier sur le serveur qu'il s'agit bien du domaine <ph name="DOMAIN" />, car son certificat de sécurité n'est pas considéré comme fiable par Chromium. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique.</translation>
-<translation id="5095208057601539847">Province</translation>
<translation id="5097099694988056070">Statistiques concernant l'appareil, telles que l'utilisation du processeur et de la RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Site non sécurisé</translation>
@@ -1550,6 +1560,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="5171045022955879922">Rechercher ou saisir une URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Ordinateur</translation>
+<translation id="5177076414499237632">En savoir plus sur la source et le thème de cette page</translation>
<translation id="5179510805599951267">Cette page n'est pas rédigée en <ph name="ORIGINAL_LANGUAGE" /> ? Signaler l'erreur</translation>
<translation id="518639307526414276">Aliments et accessoires pour animaux</translation>
<translation id="5190835502935405962">Barre de favoris</translation>
@@ -1710,6 +1721,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="5624120631404540903">Gérer les mots de passe</translation>
<translation id="5629630648637658800">Échec du chargement des paramètres de la règle.</translation>
<translation id="5631439013527180824">Jeton de gestion de l'appareil non valide.</translation>
+<translation id="5632485077360054581">Démonstration</translation>
<translation id="5633066919399395251">Des individus malveillants à l'œuvre sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pourraient tenter d'installer des programmes dangereux sur votre ordinateur afin de récupérer ou de supprimer certaines informations (photos, mots de passe, messages ou numéros de carte de crédit, par exemple). <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Contenu trompeur bloqué.</translation>
<translation id="5633259641094592098">Films cultes et indépendants</translation>
@@ -1827,6 +1839,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="5989320800837274978">Aucun serveur proxy déterminé ou URL de script .pac n'a été indiqué.</translation>
<translation id="5992691462791905444">Technique de pli en Z</translation>
<translation id="5995727681868049093">Gérez vos infos, la confidentialité de vos données et votre sécurité dans votre compte Google</translation>
+<translation id="5997247540087773573">Le mot de passe que vous venez d'utiliser a été détecté lors d'une violation de données. Pour sécuriser vos comptes, le Gestionnaire de mots de passe de Google vous recommande de le modifier immédiatement et de vérifier vos mots de passe enregistrés.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> résultats pour "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Classique</translation>
<translation id="6008122969617370890">Ordre N à 1</translation>
@@ -1922,7 +1935,6 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="627746635834430766">Pour régler vos achats plus rapidement la prochaine fois, enregistrez votre carte et votre adresse de facturation dans votre compte Google.</translation>
<translation id="6279183038361895380">Appuyez sur |<ph name="ACCELERATOR" />| pour afficher le curseur.</translation>
<translation id="6280223929691119688">Impossible de livrer à cette adresse. Sélectionnez-en une autre.</translation>
-<translation id="6282194474023008486">Code postal</translation>
<translation id="6285507000506177184">Bouton "Gérer les téléchargements dans Chrome", puis Entrée pour gérer les fichiers que vous avez téléchargés dans Chrome</translation>
<translation id="6289939620939689042">Couleur de la page</translation>
<translation id="6290238015253830360">Vos suggestions d'articles s'affichent ici</translation>
@@ -1944,6 +1956,7 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="6337133576188860026">Libère moins de <ph name="SIZE" />. Le chargement de certains sites risque d'être plus lent lors de votre prochaine visite.</translation>
<translation id="6337534724793800597">Filtrer les règles par nom</translation>
<translation id="6340739886198108203">Une règle de l'administrateur déconseille de capturer ou d'enregistrer l'écran quand du contenu confidentiel est visible :</translation>
+<translation id="6348220984832452017">Variantes actives</translation>
<translation id="6349101878882523185">Installer <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Aucun}=1{1 mot de passe (associé à <ph name="DOMAIN_LIST" /> ; synchronisé)}=2{2 mots de passe (associés à <ph name="DOMAIN_LIST" /> ; synchronisés)}one{# mot de passe (associé à <ph name="DOMAIN_LIST" /> ; synchronisé)}other{# mots de passe (associés à <ph name="DOMAIN_LIST" /> ; synchronisés)}}</translation>
<translation id="6355392890578844978">Ce navigateur n'est géré par aucune entreprise ni aucune autre organisation. Il se peut que l'activité sur cet appareil soit gérée en dehors de Chromium. <ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="643051589346665201">Changer de mot de passe Google</translation>
<translation id="6433490469411711332">Modifier les coordonnées</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> n'autorise pas la connexion.</translation>
-<translation id="6438025220577812695">Changer moi-même</translation>
<translation id="6440503408713884761">Ignoré</translation>
<translation id="6443406338865242315">Les extensions et les plug-ins que vous avez installés</translation>
<translation id="6446163441502663861">Kahu (enveloppe)</translation>
@@ -2105,9 +2117,9 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="6828866289116430505">Génétique</translation>
<translation id="6831043979455480757">Traduire</translation>
<translation id="6833752742582340615">Enregistrez votre carte et vos infos de facturation dans votre compte Google pour régler plus vite et de façon sécurisée</translation>
-<translation id="6839929833149231406">Zone</translation>
<translation id="6846340164947227603">Utiliser un numéro de carte virtuelle…</translation>
<translation id="6852204201400771460">Actualiser l'application ?</translation>
+<translation id="6857776781123259569">Gérer les mots de passe…</translation>
<translation id="686485648936420384">Ressources pour les consommateurs</translation>
<translation id="6865412394715372076">Impossible de valider cette carte pour le moment</translation>
<translation id="6869334554832814367">Prêts personnels</translation>
@@ -2153,10 +2165,9 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="6963574715554809750">Commerce de détail</translation>
<translation id="6964255747740675745">Échec de l'analyse de la configuration réseau (JSON non valide).</translation>
<translation id="6965382102122355670">OK</translation>
-<translation id="6965978654500191972">Périphérique</translation>
+<translation id="6965978654500191972">Appareil</translation>
<translation id="696703987787944103">Perceptuel</translation>
<translation id="6968269510885595029">Utiliser votre clé de sécurité</translation>
-<translation id="6970216967273061347">District</translation>
<translation id="6971439137020188025">Créez rapidement une présentation Google dans Slides</translation>
<translation id="6972629891077993081">Périphériques HID</translation>
<translation id="6973656660372572881">Les serveurs proxy déterminés et une URL de script .pac sont spécifiés tous les deux.</translation>
@@ -2195,7 +2206,6 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<translation id="7081308185095828845">Cette fonctionnalité n'est pas disponible sur votre appareil</translation>
<translation id="7083258188081898530">Bac 9</translation>
<translation id="7086090958708083563">Importation demandée par l'utilisateur</translation>
-<translation id="7087282848513945231">Comté</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" /> : appuyez sur Tabulation, puis sur Entrée pour gérer les autorisations et les données stockées sur les sites dans les paramètres Chrome</translation>
<translation id="7096937462164235847">L'identité de ce site Web n'a pas été validée.</translation>
<translation id="7101893872976785596">Films d'horreur</translation>
@@ -2214,10 +2224,10 @@ Par défaut, ce type d'accès est bloqué par vos paramètres de confidentialitÃ
<ph name="LIST_ITEM" />Informations saisies dans les formulaires<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Impossible d'expédier à cette adresse. Sélectionnez-en une autre.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Votre administrateur a interdit la copie de ces données.</translation>
<translation id="7135130955892390533">Afficher l'état</translation>
<translation id="7138472120740807366">Mode de livraison</translation>
-<translation id="7139724024395191329">Émirat</translation>
<translation id="7139892792842608322">Bac principal</translation>
<translation id="714064300541049402">Décalage X de l'image côté 2</translation>
<translation id="7152423860607593928">Number-14 (enveloppe)</translation>
@@ -2435,6 +2445,7 @@ Informations supplémentaires :
<translation id="7669271284792375604">Des individus malveillants à l'œuvre sur ce site pourraient vous inciter à installer des programmes qui nuisent à votre confort de navigation (par exemple, en changeant votre page d'accueil ou en affichant des annonces supplémentaires sur les sites que vous consultez.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Actions effectuées sur des données signalées comme confidentielles (1 action depuis la connexion). <ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" />}one{Actions effectuées sur des données signalées comme confidentielles (# action depuis la connexion). <ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" />}other{Actions effectuées sur des données signalées comme confidentielles (# actions depuis la connexion). <ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Boîte aux lettres 6</translation>
+<translation id="7675325315208090829">Gérer les modes de paiement…</translation>
<translation id="7676643023259824263">Rechercher le texte du presse-papier "<ph name="TEXT" />"</translation>
<translation id="7679367271685653708">Affichez et gérez votre historique de navigation dans les paramètres Chrome</translation>
<translation id="7679947978757153706">Base-ball</translation>
@@ -2477,7 +2488,6 @@ Informations supplémentaires :
<translation id="7766518757692125295">Jupe</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Même ordre, face vers le haut</translation>
-<translation id="777702478322588152">Préfecture</translation>
<translation id="7791011319128895129">Non publiée</translation>
<translation id="7791196057686275387">Mise en balle</translation>
<translation id="7791543448312431591">Ajouter</translation>
@@ -2568,12 +2578,12 @@ Informations supplémentaires :
<translation id="8055534648776115597">Formation professionnelle et continue</translation>
<translation id="8057711352706143257">Le logiciel "<ph name="SOFTWARE_NAME" />" n'est pas configuré correctement. En général, la désinstallation de "<ph name="SOFTWARE_NAME" />" permet de remédier à la situation. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Production alimentaire</translation>
-<translation id="8066955247577885446">Désolé, un problème est survenu.</translation>
<translation id="8067872629359326442">Vous venez de saisir votre mot de passe sur un site trompeur. Chromium peut vous aider. Pour modifier votre mot de passe et informer Google que votre compte a peut-être été piraté, cliquez sur "Protéger le compte".</translation>
<translation id="8070439594494267500">Icône de l'appli</translation>
<translation id="8074253406171541171">10x13 (enveloppe)</translation>
<translation id="8075736640322370409">Créez rapidement une feuille de calcul Google Sheets</translation>
<translation id="8075898834294118863">Gérer les paramètres des sites</translation>
+<translation id="8076492880354921740">Onglets</translation>
<translation id="8078141288243656252">Impossible d'ajouter des annotations si le document a pivoté</translation>
<translation id="8079031581361219619">Actualiser le site Web ?</translation>
<translation id="8081087320434522107">Berlines</translation>
@@ -2696,6 +2706,7 @@ Informations supplémentaires :
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Paramètres</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" /> : appuyez sur Tabulation, puis sur Entrée pour gérer vos préférences de cookies dans les paramètres Chrome</translation>
<translation id="8433057134996913067">Vous serez déconnecté de la plupart des sites.</translation>
<translation id="8434840396568290395">Animaux de compagnie</translation>
@@ -2793,6 +2804,7 @@ Informations supplémentaires :
<translation id="8742371904523228557">Votre code pour <ph name="ORIGIN" /> est <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">Ajouter cet onglet aux favoris</translation>
<translation id="8751426954251315517">Veuillez réessayer plus tard</translation>
+<translation id="8757526089434340176">Offre Google Pay disponible</translation>
<translation id="8758885506338294482">Compétitions de jeux vidéo</translation>
<translation id="8759274551635299824">Carte arrivée à expiration</translation>
<translation id="87601671197631245">La configuration de sécurité obsolète de ce site peut exposer vos informations, comme vos mots de passe, vos messages ou vos cartes de crédit, lorsqu'elles lui sont transmises.</translation>
@@ -2800,6 +2812,7 @@ Informations supplémentaires :
<translation id="8763927697961133303">Périphérique USB</translation>
<translation id="8763986294015493060">Fermez toutes les fenêtres de navigation privée actuellement ouvertes</translation>
<translation id="8766943070169463815">La fiche d'authentification concernant les infos de paiement sécurisé est ouverte</translation>
+<translation id="8767765348545497220">Fermer l'info-bulle d'aide</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motos</translation>
<translation id="8790007591277257123">&amp;Rétablir la suppression</translation>
@@ -2812,6 +2825,7 @@ Informations supplémentaires :
<translation id="8806285662264631610">Produits pour le bain et le corps</translation>
<translation id="8807160976559152894">Couper après chaque page</translation>
<translation id="8808828119384186784">Paramètres de Chrome</translation>
+<translation id="8813277370772331957">Plus tard</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" /> : appuyer sur Tabulation, puis Entrée pour mettre à jour Chrome depuis les paramètres du navigateur</translation>
<translation id="8820817407110198400">Favoris</translation>
<translation id="882338992931677877">Emplacement manuel</translation>
@@ -2991,6 +3005,7 @@ Informations supplémentaires :
<translation id="988159990683914416">Build de développement</translation>
<translation id="989988560359834682">Modifier l'adresse</translation>
<translation id="991413375315957741">capteurs de mouvement ou de lumière</translation>
+<translation id="992110854164447044">Une carte virtuelle masque votre carte réelle pour vous protéger des fraudes potentielles. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rose</translation>
<translation id="992432478773561401">Le logiciel "<ph name="SOFTWARE_NAME" />" n'a pas été installé correctement sur votre ordinateur ou sur le réseau :
diff --git a/chromium/components/strings/components_strings_gl.xtb b/chromium/components/strings/components_strings_gl.xtb
index 8815b41098f..ab1ddd1fa7d 100644
--- a/chromium/components/strings/components_strings_gl.xtb
+++ b/chromium/components/strings/components_strings_gl.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Bolsas de estudo, prácticas e axudas financeiras</translation>
<translation id="1048785276086539861">Ao editar as anotacións, este documento volverá á vista dunha soa páxina</translation>
<translation id="1050038467049342496">Pecha outras aplicacións</translation>
+<translation id="1053959602163383901">Escolliches verificar a túa identidade cun dispositivo de autenticación nos sitios web que usan <ph name="PROVIDER_ORIGIN" />. É posible que este provedor almacenase información sobre o teu método de pago. Podes <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Desfacer adición</translation>
<translation id="1056663316309890343">Software de fotografía</translation>
<translation id="1056898198331236512">Advertencia</translation>
@@ -119,6 +120,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="1270502636509132238">Método de recollida</translation>
<translation id="1281476433249504884">Amontoador 1</translation>
<translation id="1285320974508926690">Non traducir nunca este sitio</translation>
+<translation id="1288548991597756084">Garda a tarxeta de forma segura</translation>
<translation id="1292571435393770077">Bandexa 16</translation>
<translation id="1292701964462482250">"Hai software no teu ordenador que está evitando que Chrome se conecte de forma segura á web" (só para ordenadores con Windows)</translation>
<translation id="1294154142200295408">Variacións da liña de comandos</translation>
@@ -223,6 +225,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
&lt;p&gt;Para solucionar o erro, fai clic en &lt;strong&gt;Conectar&lt;/strong&gt; na páxina que tentas abrir.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Paisaxismo</translation>
<translation id="1513706915089223971">Lista de entradas do historial</translation>
+<translation id="1516097932025103760">Encriptarase e gardarase de forma segura. O CVC nunca se almacena.</translation>
<translation id="1517433312004943670">O número de teléfono é obrigatorio</translation>
<translation id="1519264250979466059">Data de compilación</translation>
<translation id="1521159554480556801">Arte téxtil e fibras</translation>
@@ -288,6 +291,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="1662550410081243962">Gardar e autocompletar métodos de pago</translation>
<translation id="1663943134801823270">As tarxetas e enderezos proceden de Chrome. Podes xestionalos en <ph name="BEGIN_LINK" />Configuración<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">As páxinas en <ph name="SOURCE_LANGUAGE" /> traduciranse ao <ph name="TARGET_LANGUAGE" /> a partir de agora.</translation>
+<translation id="1673886523110456987">Paga coa tarxeta <ph name="CARD_DETAIL" /> para usar a oferta</translation>
<translation id="1674504678466460478">De <ph name="SOURCE_LANGUAGE" /> a <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Co bordo curto primeiro</translation>
<translation id="168693727862418163">Non puido cotexarse o valor desta política co seu esquema para levar a cabo a súa validación, así que se ignorará.</translation>
@@ -306,6 +310,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="1717218214683051432">Sensores de movemento</translation>
<translation id="1717494416764505390">Caixa de correo 3</translation>
<translation id="1718029547804390981">O documento é demasiado grande para anotalo</translation>
+<translation id="1720941539803966190">Pechar titorial</translation>
<translation id="1721424275792716183">* O campo é obrigatorio</translation>
<translation id="1727613060316725209">O certificado é válido</translation>
<translation id="1727741090716970331">Engade un número de tarxeta válido</translation>
@@ -421,8 +426,8 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="205212645995975601">Grelladas</translation>
<translation id="2053111141626950936">Non se traducirán as páxinas en <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Cando este control está habilitado e co estado activo, Chrome determina con que grupo grande de persoas ou "cohorte" concorda máis a túa actividade de navegación recente. Os anunciantes poden seleccionar anuncios para o grupo, e a túa actividade de navegación mantense privada no dispositivo. O teu grupo actualízase a diario.}=1{Cando este control está habilitado e co estado activo, Chrome determina con que grupo grande de persoas ou "cohorte" concorda máis a túa actividade de navegación recente. Os anunciantes poden seleccionar anuncios para o grupo, e a túa actividade de navegación mantense privada no dispositivo. O teu grupo actualízase a diario.}other{Cando este control está habilitado e co estado activo, Chrome determina con que grupo grande de persoas ou "cohorte" concorda máis a túa actividade de navegación recente. Os anunciantes poden seleccionar anuncios para o grupo, e a túa actividade de navegación mantense privada no dispositivo. O teu grupo actualízase cada {NUM_DAYS} días.}}</translation>
-<translation id="2053553514270667976">Código postal</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 suxestión}other{# suxestións}}</translation>
+<translation id="2066915425250589881">solicitar que se elimine</translation>
<translation id="2068528718802935086">Bebés e nenos pequenos</translation>
<translation id="2071156619270205202">Esta tarxeta non é apta para asociarlle un número de tarxeta virtual.</translation>
<translation id="2071692954027939183">As notificacións bloqueáronse automaticamente porque non adoitas permitilas</translation>
@@ -434,7 +439,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="2088086323192747268">Botón Xestionar sincronización. Para xestionar na configuración de Chrome a información que se sincroniza, preme Introducir</translation>
<translation id="2091887806945687916">Son</translation>
<translation id="2094505752054353250">Os dominios non coinciden</translation>
-<translation id="2096368010154057602">Departamento</translation>
<translation id="2099652385553570808">Tres grampas na parte esquerda</translation>
<translation id="2101225219012730419">Versión:</translation>
<translation id="2102134110707549001">Suxerir contrasinal seguro…</translation>
@@ -471,7 +475,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="2185836064961771414">Fútbol americano</translation>
<translation id="2187317261103489799">Detectar (predeterminado)</translation>
<translation id="2188375229972301266">Varias perforacións na parte inferior</translation>
-<translation id="2188852899391513400">O contrasinal que acabas de utilizar viuse implicado nunha violación da seguranza dos datos. Para protexer as túas contas, o xestor de contrasinais de Google recomenda que o cambies de inmediato e que despois comprobes os contrasinais gardados.</translation>
<translation id="219906046732893612">Reformas de vivendas</translation>
<translation id="2202020181578195191">Introduce un ano de caducidade válido</translation>
<translation id="22081806969704220">Bandexa 3</translation>
@@ -482,6 +485,8 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="2215727959747642672">Modificación de ficheiros</translation>
<translation id="2215963164070968490">Cans</translation>
<translation id="2218879909401188352">Os piratas informáticos do sitio <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> poderían instalar aplicacións perigosas que danen o teu dispositivo, engadir cargos ocultos á túa factura de teléfono móbil ou roubarche información persoal. <ph name="BEGIN_LEARN_MORE_LINK" />Máis información<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Reiniciar titorial</translation>
+<translation id="2219735899272417925">É necesario restablecer o dispositivo</translation>
<translation id="2224337661447660594">Non hai conexión a Internet</translation>
<translation id="2230458221926704099">Repara a conexión a través da <ph name="BEGIN_LINK" />aplicación de diagnóstico<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Enviar agora</translation>
@@ -579,11 +584,13 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="2512101340618156538">Permiso non concedido (opción predeterminada)</translation>
<translation id="2512413427717747692">Botón para establecer Chrome como navegador predeterminado. Para establecer Chrome como navegador predeterminado do sistema na configuración de iOS, preme Introducir</translation>
<translation id="2515629240566999685">Comprobar o sinal da túa zona</translation>
+<translation id="2515761554693942801">Escolliches verificar a túa identidade con Touch ID nos sitios web que usan <ph name="PROVIDER_ORIGIN" />. É posible que este provedor almacenase información sobre o teu método de pago. Podes <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Grampa na parte inferior dereita</translation>
<translation id="2521736961081452453">Crear formulario</translation>
<translation id="2523886232349826891">Gardada só neste dispositivo</translation>
<translation id="2524461107774643265">Engadir máis información</translation>
<translation id="2529899080962247600">Este campo non debe ter máis de <ph name="MAX_ITEMS_LIMIT" /> entradas. Unha vez alcanzada esa cantidade, ignoraranse as demais entradas.</translation>
+<translation id="253493526287553278">Ver detalles do código promocional</translation>
<translation id="2535585790302968248">Abrir unha nova pestana do modo de incógnito para navegar de forma privada</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{e 1 máis}other{e # máis}}</translation>
<translation id="2536110899380797252">Engadir enderezo</translation>
@@ -619,7 +626,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="259821504105826686">Fotografía e arte dixital</translation>
<translation id="2601150049980261779">Películas románticas</translation>
<translation id="2604589665489080024">Música pop</translation>
-<translation id="2609632851001447353">Variacións</translation>
<translation id="2610561535971892504">Fai clic para copiar a información</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />non gardará<ph name="END_EMPHASIS" /> a seguinte información:
<ph name="BEGIN_LIST" />
@@ -652,6 +658,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Aniversarios e santos</translation>
<translation id="2677748264148917807">Saír</translation>
+<translation id="2679714844901977852">Garda a túa tarxeta e a información de facturación na túa Conta de Google (<ph name="USER_EMAIL" />) para tramitar as compras de xeito máis rápido e seguro</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Tamaño predeterminado</translation>
<translation id="2688969097326701645">Si, continuar</translation>
@@ -700,7 +707,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="2854764410992194509">Fornecedores de servizos de Internet (ISP)</translation>
<translation id="2856444702002559011">É posible que os piratas informáticos tenten roubar a túa información de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (por exemplo, contrasinais, mensaxes ou tarxetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Máis información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Este sitio mostra anuncios intrusivos ou enganosos.</translation>
-<translation id="286512204874376891">As tarxetas virtuais ocultan as tarxetas reais para protexerte de posibles fraudes. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Amigable</translation>
<translation id="28761159517501904">Películas</translation>
<translation id="2876489322757410363">Sairás do modo de incógnito para pagar a través dunha aplicación externa. Queres continuar?</translation>
@@ -801,7 +807,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="3158539265159265653">Disco</translation>
<translation id="3162559335345991374">É posible que a rede wifi que utilizas requira o acceso á súa páxina de inicio de sesión.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Illa</translation>
<translation id="3176929007561373547">Comproba a configuración do teu proxy ou ponte en contacto co administrador da túa rede para
asegurarte de que o servidor proxy funciona. Se non cres que deberías
estar utilizando un servidor proxy:
@@ -880,9 +885,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="3369192424181595722">Erro do reloxo</translation>
<translation id="3369459162151165748">Pezas e accesorios para vehículos</translation>
<translation id="3371064404604898522">Establecer Chrome como navegador predeterminado</translation>
-<translation id="3371076217486966826"><ph name="URL" /> quere facer o seguinte:
- • Crear un mapa 3D do que te rodea e facer un seguimento da posición da cámara
- • Usar a túa cámara</translation>
<translation id="337363190475750230">Desaprovisionado</translation>
<translation id="3375754925484257129">Executar comprobación de seguranza de Chrome</translation>
<translation id="3377144306166885718">O servidor utilizou unha versión obsoleta de TLS.</translation>
@@ -899,6 +901,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="3399952811970034796">Enderezo de entrega</translation>
<translation id="3402261774528610252">A conexión coa que se cargou este sitio utilizou TLS 1.0 ou TLS 1.1, as cales son versións obsoletas que se desactivarán no futuro. Cando se desactiven, evitarase que os usuarios carguen este sitio. O servidor debería activar TLS 1.2 ou unha versión superior.</translation>
<translation id="3405664148539009465">Personalizar tipos de letra</translation>
+<translation id="3407789382767355356">inicio de sesión de terceiros</translation>
<translation id="3409896703495473338">Xestionar configuración de seguranza</translation>
<translation id="3414952576877147120">Tamaño:</translation>
<translation id="3417660076059365994">Os ficheiros que cargas ou anexas envíanse a Google Cloud ou a terceiros para analizalos. Por exemplo, poderíase comprobar se conteñen datos confidenciais ou software malicioso.</translation>
@@ -931,6 +934,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="3477679029130949506">Carteleira e horarios de cines</translation>
<translation id="3479552764303398839">Agora non</translation>
<translation id="3484560055331845446">Poderías perder o acceso á túa Conta de Google. Chrome recoméndache que cambies de contrasinal agora. Deberás iniciar sesión.</translation>
+<translation id="3484861421501147767">Recordatorio: tes un código promocional gardado dispoñible</translation>
<translation id="3487845404393360112">Bandexa 4</translation>
<translation id="3495081129428749620">Buscar na páxina
<ph name="PAGE_TITLE" /></translation>
@@ -1055,6 +1059,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="3810973564298564668">Xestionar</translation>
<translation id="3816482573645936981">Valor (substituído)</translation>
<translation id="382518646247711829">Se utilizas un servidor proxy...</translation>
+<translation id="3826050100957962900">Inicio de sesión de terceiros</translation>
<translation id="3827112369919217609">Absoluto</translation>
<translation id="3827666161959873541">Películas para toda a familia</translation>
<translation id="3828924085048779000">A frase de acceso non pode estar baleira.</translation>
@@ -1067,9 +1072,9 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="3858027520442213535">Actualizar data e hora</translation>
<translation id="3858860766373142691">Nome</translation>
<translation id="3872834068356954457">Ciencia</translation>
+<translation id="3875783148670536197">Ver como facelo</translation>
<translation id="3881478300875776315">Mostrar menos liñas</translation>
<translation id="3884278016824448484">Identificador de dispositivos en conflito</translation>
-<translation id="3885155851504623709">Parroquia</translation>
<translation id="388632593194507180">Detectouse a supervisión</translation>
<translation id="3886948180919384617">Amontoador 3</translation>
<translation id="3890664840433101773">Engadir correo electrónico</translation>
@@ -1099,9 +1104,11 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="3973234410852337861"><ph name="HOST_NAME" /> está bloqueado</translation>
<translation id="3978338123949022456">Modo de busca. Escribe unha consulta e preme Introducir para facer a busca con <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Aves</translation>
+<translation id="3985750352229496475">Xestiona os enderezos…</translation>
<translation id="3986705137476756801">Desactivar polo momento a función Subtítulos instantáneos</translation>
<translation id="3987940399970879459">Menos de 1 MB</translation>
<translation id="3990250421422698716">Compensación mediante empurro</translation>
+<translation id="3992684624889376114">Acerca desta páxina</translation>
<translation id="3996311196211510766">O sitio <ph name="ORIGIN" /> solicitou que se aplicase unha política de orixe
a todas as solicitudes que reciba, pero actualmente non se pode aplicar esta política.</translation>
<translation id="4006465311664329701">Métodos de pago, ofertas e enderezos que usan Google Pay</translation>
@@ -1226,6 +1233,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="4305666528087210886">Non se puido acceder ao ficheiro</translation>
<translation id="4306529830550717874">Queres gardar o enderezo?</translation>
<translation id="4306812610847412719">portapapeis</translation>
+<translation id="4310070645992025887">Busca os teus percorridos</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloquear (predeterminado)</translation>
<translation id="4314815835985389558">Xestionar sincronización</translation>
@@ -1276,6 +1284,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="4435702339979719576">Postal)</translation>
<translation id="443673843213245140">O uso dun proxy está desactivado, pero especifícase unha configuración de proxy explícita.</translation>
<translation id="4441832193888514600">Ignorada porque a política só se pode configurar como unha política de usuario da nube.</translation>
+<translation id="4442470707340296952">Pestanas de Chrome</translation>
<translation id="4450893287417543264">Non mostrar outra vez</translation>
<translation id="4451135742916150903">Pode pedirche permiso para conectarse a dispositivos de interface humana</translation>
<translation id="4452328064229197696">O contrasinal que acabas de utilizar viuse implicado nunha violación da seguranza dos datos. Para protexer as túas contas, o xestor de contrasinais de Google recomenda que comprobes os contrasinais gardados.</translation>
@@ -1414,6 +1423,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="483241715238664915">Activar advertencias</translation>
<translation id="4834250788637067901">Métodos de pago, ofertas e enderezos que usan Google Pay</translation>
<translation id="4838327282952368871">Onírico</translation>
+<translation id="4839087176073128681">Paga máis rápido a próxima vez e protexe a tarxeta coas medidas de seguranza de Google líderes no sector.</translation>
<translation id="4840250757394056958">Ver historial de Chrome</translation>
<translation id="484462545196658690">Automático</translation>
<translation id="484671803914931257">Obtén un desconto en <ph name="MERCHANT_NAME" /> e noutros comerciantes</translation>
@@ -1476,6 +1486,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="5011561501798487822">Idioma detectado</translation>
<translation id="5015510746216210676">Nome do equipo:</translation>
<translation id="5017554619425969104">Texto que copiaches</translation>
+<translation id="5017828934289857214">Lembrarmo máis tarde</translation>
<translation id="5018422839182700155">Non se pode abrir esta páxina</translation>
<translation id="5019198164206649151">A memoria auxiliar está en mal estado</translation>
<translation id="5020776957610079374">Música do mundo</translation>
@@ -1495,6 +1506,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="5051305769747448211">Comedia en directo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Para enviar este ficheiro usando Compartir por Nearby, libera espazo (<ph name="DISK_SPACE_SIZE" />) no dispositivo}other{Para enviar estes ficheiros usando Compartir por Nearby, libera espazo (<ph name="DISK_SPACE_SIZE" />) no dispositivo}}</translation>
<translation id="5056549851600133418">Artigos para ti</translation>
+<translation id="5060483733937416656">Escolliches verificar a túa identidade con Windows Hello nos sitios web que usan <ph name="PROVIDER_ORIGIN" />. É posible que este provedor almacenase información sobre o teu método de pago. Podes <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Querías dicir "<ph name="LOOKALIKE_DOMAIN" />"?</translation>
<translation id="5066056036849835175">Historial de impresión</translation>
<translation id="5068234115460527047">Fondos de alto risco</translation>
@@ -1508,10 +1520,8 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="5087286274860437796">O certificado do servidor non é válido neste momento.</translation>
<translation id="5087580092889165836">Engadir tarxeta</translation>
<translation id="5088142053160410913">Mensaxe para o operador</translation>
-<translation id="5089810972385038852">Estado</translation>
<translation id="5093232627742069661">Dobrez en Z</translation>
<translation id="5094747076828555589">Este servidor non puido demostrar que é <ph name="DOMAIN" /> porque Chromium non confía no seu certificado de seguranza. É posible que isto se deba a un erro de configuración ou a que un atacante interceptase a túa conexión.</translation>
-<translation id="5095208057601539847">Provincia</translation>
<translation id="5097099694988056070">Estatísticas do hardware, como o uso de CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">O sitio non é seguro</translation>
@@ -1549,6 +1559,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="5171045022955879922">Buscar ou escribir o URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Equipo</translation>
+<translation id="5177076414499237632">Máis información sobre o tema e a fonte desta páxina</translation>
<translation id="5179510805599951267">Non está en <ph name="ORIGINAL_LANGUAGE" />? Informar deste erro</translation>
<translation id="518639307526414276">Produtos de alimentación e coidado de mascotas</translation>
<translation id="5190835502935405962">Barra de marcadores</translation>
@@ -1709,6 +1720,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="5624120631404540903">Xestionar contrasinais</translation>
<translation id="5629630648637658800">Erro ao cargar a configuración da política</translation>
<translation id="5631439013527180824">Token de xestión de dispositivo non válido</translation>
+<translation id="5632485077360054581">Ver como facelo</translation>
<translation id="5633066919399395251">Os piratas informáticos do sitio <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> poderían tentar instalar programas perigosos no ordenador que rouben ou eliminen a túa información (por exemplo, fotos, contrasinais, mensaxes e tarxetas de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Máis información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Bloqueouse contido enganoso.</translation>
<translation id="5633259641094592098">Cine independente e películas de culto</translation>
@@ -1826,6 +1838,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="5989320800837274978">Non se especificaron nin servidores proxy fixos nin un URL de script .pac.</translation>
<translation id="5992691462791905444">Dobrez en Z para enxeñaría</translation>
<translation id="5995727681868049093">Xestionar a información, a privacidade e a seguranza na túa Conta de Google</translation>
+<translation id="5997247540087773573">O contrasinal que acabas de utilizar viuse implicado nunha violación da seguranza dos datos. Para protexer as túas contas, o xestor de contrasinais de Google recomenda que o cambies de inmediato e que comprobes os contrasinais gardados.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultados para "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Clásico</translation>
<translation id="6008122969617370890">Orde de N a 1</translation>
@@ -1921,7 +1934,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="627746635834430766">Para pagar máis rápido a próxima vez, garda a túa tarxeta e o enderezo de facturación na conta de Google.</translation>
<translation id="6279183038361895380">Preme |<ph name="ACCELERATOR" />| para mostrar o cursor</translation>
<translation id="6280223929691119688">Non se pode realizar a entrega neste enderezo. Selecciona un diferente.</translation>
-<translation id="6282194474023008486">Código postal</translation>
<translation id="6285507000506177184">Botón para xestionar as descargas en Chrome. Se queres xestionar os ficheiros que descargaches en Chrome, preme Introducir</translation>
<translation id="6289939620939689042">Cor das páxinas</translation>
<translation id="6290238015253830360">Os teus artigos suxeridos aparecerán aquí</translation>
@@ -1943,6 +1955,7 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="6337133576188860026">Libera menos de <ph name="SIZE" />. É posible que algúns sitios carguen de forma máis lenta a próxima vez que os visites.</translation>
<translation id="6337534724793800597">Filtrar políticas por nome</translation>
<translation id="6340739886198108203">Na política do administrador recoméndase que non se fagan capturas nin gravacións se na pantalla se mostra contido confidencial:</translation>
+<translation id="6348220984832452017">Variacións activas</translation>
<translation id="6349101878882523185">Instalar a aplicación <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ningún}=1{1 contrasinal (para <ph name="DOMAIN_LIST" />, sincronizado)}=2{2 contrasinais (para <ph name="DOMAIN_LIST" />, sincronizados)}other{# contrasinais (para <ph name="DOMAIN_LIST" />, sincronizados)}}</translation>
<translation id="6355392890578844978">Ningunha compañía ou organización xestiona este navegador. A actividade deste dispositivo pódese xestionar fóra de Chromium. <ph name="BEGIN_LINK" />Máis información<ph name="END_LINK" /></translation>
@@ -1974,7 +1987,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="643051589346665201">Cambiar contrasinal de Google</translation>
<translation id="6433490469411711332">Editar información de contacto</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> rexeitou a conexión.</translation>
-<translation id="6438025220577812695">Cambiar manualmente</translation>
<translation id="6440503408713884761">Política ignorada</translation>
<translation id="6443406338865242315">As extensións e os complementos que teñas instalados</translation>
<translation id="6446163441502663861">Kahu (sobre)</translation>
@@ -2104,9 +2116,9 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="6828866289116430505">Xenética</translation>
<translation id="6831043979455480757">Traducir</translation>
<translation id="6833752742582340615">Garda a túa tarxeta e o enderezo de facturación na túa Conta de Google para pagar de xeito máis rápido e seguro</translation>
-<translation id="6839929833149231406">Ãrea</translation>
<translation id="6846340164947227603">Usar un número de conta virtual…</translation>
<translation id="6852204201400771460">Queres volver cargar a aplicación?</translation>
+<translation id="6857776781123259569">Xestiona os contrasinais…</translation>
<translation id="686485648936420384">Recursos para consumidores</translation>
<translation id="6865412394715372076">Esta tarxeta non puido verificarse neste momento</translation>
<translation id="6869334554832814367">Préstamos persoais</translation>
@@ -2155,7 +2167,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="6965978654500191972">Dispositivo</translation>
<translation id="696703987787944103">Percepción</translation>
<translation id="6968269510885595029">Usa a túa chave de seguranza</translation>
-<translation id="6970216967273061347">Distrito</translation>
<translation id="6971439137020188025">Crear rapidamente unha presentación nova en Presentacións</translation>
<translation id="6972629891077993081">Dispositivos de interface humana</translation>
<translation id="6973656660372572881">Especifícanse tanto servidores proxy fixos como un URL de script .pac.</translation>
@@ -2194,7 +2205,6 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<translation id="7081308185095828845">Esta función non está dispoñible no teu dispositivo</translation>
<translation id="7083258188081898530">Bandexa 9</translation>
<translation id="7086090958708083563">O usuario solicitou a carga</translation>
-<translation id="7087282848513945231">Condado</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. Para xestionar na configuración de Chrome os permisos e os datos almacenados dos sitios, preme Tabulador e, a continuación, Introducir</translation>
<translation id="7096937462164235847">Non se verificou a identidade deste sitio web.</translation>
<translation id="7101893872976785596">Películas de terror</translation>
@@ -2213,10 +2223,10 @@ En caso contrario, a configuración de privacidade impedirao. Se o permites, o c
<ph name="LIST_ITEM" />Os datos que puxeses en formularios<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Non se pode realizar o envío a este enderezo. Selecciona un diferente.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">O teu administrador prohibiu que copiases estes datos.</translation>
<translation id="7135130955892390533">Mostrar estado</translation>
<translation id="7138472120740807366">Método de entrega</translation>
-<translation id="7139724024395191329">Emirato</translation>
<translation id="7139892792842608322">Bandexa principal</translation>
<translation id="714064300541049402">Desprazamento do lado 2 da imaxe no eixe X</translation>
<translation id="7152423860607593928">Number-14 (sobre)</translation>
@@ -2433,6 +2443,7 @@ Detalles adicionais:
<translation id="7669271284792375604">Os atacantes deste sitio poden tentar enganarte para que instales programas que afectan negativamente á túa experiencia de navegación (por exemplo, cambiando a páxina de inicio ou mostrando anuncios adicionais nos sitios que visitas).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Accións realizadas con datos marcados como confidenciais (1 acción desde o inicio de sesión). <ph name="BEGIN_LINK" />Máis información<ph name="END_LINK" />}other{Accións realizadas con datos marcados como confidenciais (# accións desde o inicio de sesión). <ph name="BEGIN_LINK" />Máis información<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Caixa de correo 6</translation>
+<translation id="7675325315208090829">Xestiona os métodos de pago...</translation>
<translation id="7676643023259824263">Buscar texto do portapapeis, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Ver e xestionar o teu historial de navegación na configuración de Chrome</translation>
<translation id="7679947978757153706">Béisbol</translation>
@@ -2475,7 +2486,6 @@ Detalles adicionais:
<translation id="7766518757692125295">Saia</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">A mesma orde cara arriba</translation>
-<translation id="777702478322588152">Prefectura</translation>
<translation id="7791011319128895129">Sen publicar</translation>
<translation id="7791196057686275387">Bala</translation>
<translation id="7791543448312431591">Engadir</translation>
@@ -2566,12 +2576,12 @@ Detalles adicionais:
<translation id="8055534648776115597">Educación continua e orientación profesional</translation>
<translation id="8057711352706143257"><ph name="SOFTWARE_NAME" /> non está configurado correctamente. Ao desinstalar <ph name="SOFTWARE_NAME" /> adoita solucionarse o problema. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Produción alimentaria</translation>
-<translation id="8066955247577885446">Produciuse un erro.</translation>
<translation id="8067872629359326442">Acabas de escribir o teu contrasinal nun sitio enganoso. Chromium pode axudarche. Para cambiar o teu contrasinal e notificarlle a Google que a túa conta pode estar en risco, fai clic en Protexer conta.</translation>
<translation id="8070439594494267500">Icona da aplicación</translation>
<translation id="8074253406171541171">10x13 (sobre)</translation>
<translation id="8075736640322370409">Crear rapidamente unha nova folla de cálculo de Google</translation>
<translation id="8075898834294118863">Xestionar configuración dos sitios</translation>
+<translation id="8076492880354921740">Pestanas</translation>
<translation id="8078141288243656252">Non se poden realizar anotacións ao xirar o documento</translation>
<translation id="8079031581361219619">Queres volver cargar o sitio?</translation>
<translation id="8081087320434522107">Sedáns</translation>
@@ -2694,6 +2704,7 @@ Detalles adicionais:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Configuración</translation>
+<translation id="8428634594422941299">De acordo</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. Para xestionar na configuración de Chrome as túas preferencias relativas ás cookies, preme Tabulador e, a continuación, Introducir</translation>
<translation id="8433057134996913067">Ao realizar esta acción, pecharase sesión na maioría dos sitios web</translation>
<translation id="8434840396568290395">Mascotas</translation>
@@ -2791,6 +2802,7 @@ Detalles adicionais:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> é o teu código para <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Engadir esta pestana aos marcadores</translation>
<translation id="8751426954251315517">Téntao de novo a próxima vez</translation>
+<translation id="8757526089434340176">Hai unha oferta de Google Pay dispoñible</translation>
<translation id="8758885506338294482">Deportes electrónicos</translation>
<translation id="8759274551635299824">Esta tarxeta caducou</translation>
<translation id="87601671197631245">Este sitio utiliza unha configuración de seguranza obsoleta que pode poñer en risco a túa información (por exemplo, contrasinais, mensaxes ou números de tarxetas de crédito) cando se lle envía.</translation>
@@ -2798,6 +2810,7 @@ Detalles adicionais:
<translation id="8763927697961133303">Dispositivo USB</translation>
<translation id="8763986294015493060">Pechar todas as ventás do modo de incógnito que estean abertas</translation>
<translation id="8766943070169463815">A folla de autenticación da credencial de pagos seguros está aberta</translation>
+<translation id="8767765348545497220">Pechar globo de axuda</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocicletas</translation>
<translation id="8790007591277257123">&amp;Refacer eliminación</translation>
@@ -2810,6 +2823,7 @@ Detalles adicionais:
<translation id="8806285662264631610">Produtos para o baño e o corpo</translation>
<translation id="8807160976559152894">Recorte despois de cada páxina</translation>
<translation id="8808828119384186784">Configuración de Chrome</translation>
+<translation id="8813277370772331957">Lembrarmo máis tarde</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Preme Tab e, a continuación, Intro para actualizar Chrome desde a configuración do navegador</translation>
<translation id="8820817407110198400">Marcadores</translation>
<translation id="882338992931677877">Oco manual</translation>
@@ -2989,6 +3003,7 @@ Detalles adicionais:
<translation id="988159990683914416">Compilación para programadores</translation>
<translation id="989988560359834682">Editar enderezo</translation>
<translation id="991413375315957741">sensores de movemento ou de luz</translation>
+<translation id="992110854164447044">As tarxetas virtuais ocultan as tarxetas reais para protexerte de posibles fraudes. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401"><ph name="SOFTWARE_NAME" /> non se instalou correctamente no teu ordenador ou na rede:
diff --git a/chromium/components/strings/components_strings_gu.xtb b/chromium/components/strings/components_strings_gu.xtb
index 31b68438d8a..7ef3a7ffdf7 100644
--- a/chromium/components/strings/components_strings_gu.xtb
+++ b/chromium/components/strings/components_strings_gu.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">અનà«àª¦àª¾àª¨, શિષà«àª¯àªµà«ƒàª¤à«àª¤àª¿ અને નાણાકીય સહાય</translation>
<translation id="1048785276086539861">જà«àª¯àª¾àª°à«‡ તમે ટીકાટિપà«àªªàª£à«€àª®àª¾àª‚ ફેરફાર કરશો, તà«àª¯àª¾àª°à«‡ આ દસà«àª¤àª¾àªµà«‡àªœ 'àªàª• પેજના વà«àª¯à«‚' પર પાછો ફરશે</translation>
<translation id="1050038467049342496">અનà«àª¯ àªàªªà«àª²àª¿àª•à«‡àª¶àª¨à«‹ બંધ કરો</translation>
+<translation id="1053959602163383901">તમે <ph name="PROVIDER_ORIGIN" />નો ઉપયોગ કરતી વેબસાઇટ પર કોઈ પà«àª°àª®àª¾àª£àª•àª°à«àª¤àª¾ ડિવાઇસ વડે ચકાસણી કરવાનà«àª‚ પસંદ કરà«àª¯à«àª‚ છે. આ પà«àª°àª¦àª¾àª¤àª¾àª તમારી ચà«àª•àªµàª£à«€ પદà«àª§àª¤àª¿ વિશેની માહિતી કદાચ સà«àªŸà«‹àª° કરી હોઈ શકે છે, જેને તમે <ph name="LINK_TEXT" /> કરી શકો છો.</translation>
<translation id="1055184225775184556">&amp;ઉમેરવà«àª‚ પૂરà«àªµàªµàª¤à« કરો</translation>
<translation id="1056663316309890343">ફોટો સૉફà«àªŸàªµà«‡àª°</translation>
<translation id="1056898198331236512">ચેતવણી</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">પિકઅપ પદà«àª§àª¤àª¿</translation>
<translation id="1281476433249504884">સà«àªŸà«…કર 1</translation>
<translation id="1285320974508926690">આ સાઇટનો કà«àª¯àª¾àª°à«‡àª¯ અનà«àªµàª¾àª¦ કરશો નહીં</translation>
+<translation id="1288548991597756084">કારà«àª¡àª¨à«‡ સà«àª°àª•à«àª·àª¿àª¤ રીતે સાચવો</translation>
<translation id="1292571435393770077">ટà«àª°à«‡ 16</translation>
<translation id="1292701964462482250">"તમારા કમà«àªªà«àª¯à«àªŸàª°àª®àª¾àª‚નà«àª‚ સૉફà«àªŸàªµà«‡àª° Chromeને સà«àª°àª•à«àª·àª¿àª¤ રીતે વેબ સાથે કનેકà«àªŸ થવાથી અટકાવે છે" (માતà«àª° Windows કમà«àªªà«àª¯à«àªŸàª°)</translation>
<translation id="1294154142200295408">આદેશ વાકà«àª¯àª®àª¾àª‚ વિવિધતા</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;આ સમસà«àª¯àª¾ ઉકેલવા માટે, તમે ખોલવાનો પà«àª°àª¯àª¾àª¸ કરી રહà«àª¯àª¾ છો ઠપેજ પર &lt;strong&gt;કનેકà«àªŸ કરો&lt;/strong&gt; પર કà«àª²àª¿àª• કરો.&lt;/p&gt;</translation>
<translation id="1507780850870535225">લૅનà«àª¡àª¸à«àª•à«‡àªª ડિàªàª¾àª‡àª¨</translation>
<translation id="1513706915089223971">ઇતિહાસના àªàª¨à«àªŸà«àª°à«€àª¨à«€ સૂચિ</translation>
+<translation id="1516097932025103760">તેને àªàª¨à«àª•à«àª°àª¿àªªà«àªŸ કરવામાં આવશે, તેને સà«àª°àª•à«àª·àª¿àª¤ રીતે સાચવવામાં આવશે અને CVC કà«àª¯àª¾àª°à«‡àª¯ સà«àªŸà«‹àª° કરવામાં આવશે નહીં.</translation>
<translation id="1517433312004943670">ફોન નંબર આવશà«àª¯àª•</translation>
<translation id="1519264250979466059">નિરà«àª®àª¾àª£ તારીખ</translation>
<translation id="1521159554480556801">ફાઇબર અને વસà«àª¤à«àª°àª•àª³àª¾</translation>
@@ -272,7 +275,7 @@
<translation id="1634828734222219955">કà«àª²</translation>
<translation id="163669211644121865">ટેકà«àª¸àª¨à«€ ગણતરી અને તેનà«àª‚ પà«àª²àª¾àª¨àª¿àª‚ગ</translation>
<translation id="1638780421120290329">કારà«àª¡ સાચવી શકાતà«àª‚ નથી</translation>
-<translation id="1639239467298939599">લોડ કરી રહà«àª¯à«àª‚ છે</translation>
+<translation id="1639239467298939599">લોડ થઇ રહી છે</translation>
<translation id="1640180200866533862">વપરાશકરà«àª¤àª¾ પૉલિસીઓ</translation>
<translation id="1640244768702815859"><ph name="BEGIN_LINK" />સાઇટના હોમપેજની મà«àª²àª¾àª•àª¾àª¤ લેવાનો<ph name="END_LINK" /> પà«àª°àª¯àª¾àª¸ કરો.</translation>
<translation id="1641976391427233992">આટલા સમય સà«àª§à«€ આઉટપà«àªŸ વિલંબિત કરો</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ચà«àª•àªµàª£à«€ પદà«àª§àª¤àª¿àª“ સાચવો અને ભરો</translation>
<translation id="1663943134801823270">કારà«àª¡ અને સરનામા Chromeમાંથી છે. તમે તેને <ph name="BEGIN_LINK" />સેટિંગ<ph name="END_LINK" />માં મેનેજ કરી શકો છો.</translation>
<translation id="1671391448414634642">હવેથી <ph name="SOURCE_LANGUAGE" />માં છે તે પેજનો અનà«àªµàª¾àª¦ <ph name="TARGET_LANGUAGE" />માં થશે.</translation>
+<translation id="1673886523110456987">ઑફરનો ઉપયોગ કરવા માટે, <ph name="CARD_DETAIL" /> સાથે ચેક કરો</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" />થી<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">પહેલાં ટૂંકી કિનારી</translation>
<translation id="168693727862418163">આ પૉલિસીનà«àª‚ મૂલà«àª¯ તેના સà«àª•à«€àª®àª¾ સામે માનà«àª¯ કરવામાં નિષà«àª«àª³ ગયà«àª‚ છે અને તેને અવગણવામાં આવશે.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">મોશન સેનà«àª¸àª°</translation>
<translation id="1717494416764505390">મેઇલબૉકà«àª¸ 3</translation>
<translation id="1718029547804390981">àªàª¨à«‹àªŸà«‡àªŸ કરવા માટે દસà«àª¤àª¾àªµà«‡àªœ ઘણો મોટો છે</translation>
+<translation id="1720941539803966190">ટà«àª¯à«‚ટૉરિઅલ બંધ કરો</translation>
<translation id="1721424275792716183">* ફીલà«àª¡ આવશà«àª¯àª• છે</translation>
<translation id="1727613060316725209">પà«àª°àª®àª¾àª£àªªàª¤à«àª° માનà«àª¯ છે</translation>
<translation id="1727741090716970331">માનà«àª¯ કારà«àª¡ નંબર ઉમેરો</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">બારà«àª¬à«‡àª•à«àª¯à«‚ અને ગà«àª°àª¿àª²àª¿àª‚ગ</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" />માં લખાયેલાં પેજનો અનà«àªµàª¾àª¦ થશે નહીં.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{જà«àª¯àª¾àª°à«‡ આ નિયંતà«àª°àª£ ચાલૠહોય અને સà«àªŸà«‡àªŸàª¸ સકà«àª°àª¿àª¯ હોય, તà«àª¯àª¾àª°à«‡ Chrome ઠનકà«àª•à«€ કરે છે કે તમારી તાજેતરની બà«àª°àª¾àª‰àªàª¿àª‚ગ પà«àª°àªµà«ƒàª¤à«àª¤àª¿ લોકોના કયા વિશાળ ગà«àª°à«‚પ અથવા "ગà«àª°à«‚પ" સાથે સૌથી વધૠસમાનતા ધરાવે છે. જાહેરાતકરà«àª¤àª¾àª“ ગà«àª°à«‚પ માટે જાહેરાતો પસંદ કરી શકે છે અને તમારી બà«àª°àª¾àª‰àªàª¿àª‚ગ પà«àª°àªµà«ƒàª¤à«àª¤àª¿àª¨à«‡ તમારા ડિવાઇસ પર ખાનગી રાખવામાં આવે છે. તમારà«àª‚ ગà«àª°à«‚પ દરરોજ અપડેટ થાય છે.}=1{જà«àª¯àª¾àª°à«‡ આ નિયંતà«àª°àª£ ચાલૠહોય અને સà«àªŸà«‡àªŸàª¸ સકà«àª°àª¿àª¯ હોય, તà«àª¯àª¾àª°à«‡ Chrome ઠનકà«àª•à«€ કરે છે કે તમારી તાજેતરની બà«àª°àª¾àª‰àªàª¿àª‚ગ પà«àª°àªµà«ƒàª¤à«àª¤àª¿ લોકોના કયા વિશાળ ગà«àª°à«‚પ અથવા "ગà«àª°à«‚પ" સાથે સૌથી વધૠસમાનતા ધરાવે છે. જાહેરાતકરà«àª¤àª¾àª“ ગà«àª°à«‚પ માટે જાહેરાતો પસંદ કરી શકે છે અને તમારી બà«àª°àª¾àª‰àªàª¿àª‚ગ પà«àª°àªµà«ƒàª¤à«àª¤àª¿àª¨à«‡ તમારા ડિવાઇસ પર ખાનગી રાખવામાં આવે છે. તમારà«àª‚ ગà«àª°à«‚પ દરરોજ અપડેટ થાય છે.}one{જà«àª¯àª¾àª°à«‡ આ નિયંતà«àª°àª£ ચાલૠહોય અને સà«àªŸà«‡àªŸàª¸ સકà«àª°àª¿àª¯ હોય, તà«àª¯àª¾àª°à«‡ Chrome ઠનકà«àª•à«€ કરે છે કે તમારી તાજેતરની બà«àª°àª¾àª‰àªàª¿àª‚ગ પà«àª°àªµà«ƒàª¤à«àª¤àª¿ લોકોના કયા વિશાળ ગà«àª°à«‚પ અથવા "ગà«àª°à«‚પ" સાથે સૌથી વધૠસમાનતા ધરાવે છે. જાહેરાતકરà«àª¤àª¾àª“ ગà«àª°à«‚પ માટે જાહેરાતો પસંદ કરી શકે છે અને તમારી બà«àª°àª¾àª‰àªàª¿àª‚ગ પà«àª°àªµà«ƒàª¤à«àª¤àª¿àª¨à«‡ તમારા ડિવાઇસ પર ખાનગી રાખવામાં આવે છે. તમારà«àª‚ ગà«àª°à«‚પ દર {NUM_DAYS} દિવસે અપડેટ થાય છે.}other{જà«àª¯àª¾àª°à«‡ આ નિયંતà«àª°àª£ ચાલૠહોય અને સà«àªŸà«‡àªŸàª¸ સકà«àª°àª¿àª¯ હોય, તà«àª¯àª¾àª°à«‡ Chrome ઠનકà«àª•à«€ કરે છે કે તમારી તાજેતરની બà«àª°àª¾àª‰àªàª¿àª‚ગ પà«àª°àªµà«ƒàª¤à«àª¤àª¿ લોકોના કયા વિશાળ ગà«àª°à«‚પ અથવા "ગà«àª°à«‚પ" સાથે સૌથી વધૠસમાનતા ધરાવે છે. જાહેરાતકરà«àª¤àª¾àª“ ગà«àª°à«‚પ માટે જાહેરાતો પસંદ કરી શકે છે અને તમારી બà«àª°àª¾àª‰àªàª¿àª‚ગ પà«àª°àªµà«ƒàª¤à«àª¤àª¿àª¨à«‡ તમારા ડિવાઇસ પર ખાનગી રાખવામાં આવે છે. તમારà«àª‚ ગà«àª°à«‚પ દર {NUM_DAYS} દિવસે અપડેટ થાય છે.}}</translation>
-<translation id="2053553514270667976">પિન કોડ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 સૂચન}one{# સૂચન}other{# સૂચન}}</translation>
+<translation id="2066915425250589881">ડિલીટ કરવાની વિનંતી</translation>
<translation id="2068528718802935086">શિશà«àª“ અને બાળકો</translation>
<translation id="2071156619270205202">આ કારà«àª¡àª¨à«‹ નંબર, વરà«àªšà«àª¯à«àª…લ કારà«àª¡ નંબર તરીકે ઉપયોગ માટેની યોગà«àª¯àª¤àª¾ ધરાવતો નથી.</translation>
<translation id="2071692954027939183">નોટિફિકેશન ઑટોમૅટિક રીતે બà«àª²à«‰àª• કરવામાં આવà«àª¯àª¾ હતા કારણ કે તમે સામાનà«àª¯ રીતે તેમને મંજૂરી આપતા નથી</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">સિંક બટન મેનેજ કરો, તેમજ Chrome સેટિંગમાં તમે જે માહિતી સિંક કરવા ઇચà«àª›àª¤àª¾ હો, તે મેનેજ કરવા માટે Enter કી દબાવો</translation>
<translation id="2091887806945687916">ધà«àªµàª¨àª¿</translation>
<translation id="2094505752054353250">ડોમેન મેળ ખાતà«àª‚ નથી</translation>
-<translation id="2096368010154057602">વિભાગ</translation>
<translation id="2099652385553570808">ડાબી બાજà«àª તà«àª°àª£ સà«àªŸà«‡àªªàª² લગાવો</translation>
<translation id="2101225219012730419">વરà«àªàª¨:</translation>
<translation id="2102134110707549001">સશકà«àª¤ પાસવરà«àª¡ સૂચવો…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">અમેરિકન ફૂટબૉલ</translation>
<translation id="2187317261103489799">શોધો (ડિફૉલà«àªŸ)</translation>
<translation id="2188375229972301266">નીચેની બાજà«àª àªàª•àª¥à«€ વધૠકાણાં પાડો</translation>
-<translation id="2188852899391513400">તમે હાલમાં જ ઉપયોગમાં લીધેલો પાસવરà«àª¡ ડેટા ઉલà«àª²àª‚ઘનમાં જોવા મળà«àª¯à«‹ છે. તમારા àªàª•àª¾àª‰àª¨à«àªŸ સà«àª°àª•à«àª·àª¿àª¤ રાખવા માટે, Google પાસવરà«àª¡ મેનેજર હમણાં જ તમારો પાસવરà«àª¡ બદલવાનો અને પછી તમારા સાચવેલા પાસવરà«àª¡àª¨à«‡ ચેક કરવાનો સà«àªàª¾àªµ આપે છે.</translation>
<translation id="219906046732893612">ઘરનà«àª‚ સમારકામ</translation>
<translation id="2202020181578195191">àªàª• માનà«àª¯ સમાપà«àª¤àª¿ વરà«àª· દાખલ કરો</translation>
<translation id="22081806969704220">ટà«àª°à«‡ 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">ફાઇલમાં ફેરફાર કરવો</translation>
<translation id="2215963164070968490">શà«àªµàª¾àª¨à«‹</translation>
<translation id="2218879909401188352">હાલમાં હà«àª®àª²àª¾àª–ોરો <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> પર જોખમકારક àªàªªà«àª²àª¿àª•à«‡àª¶àª¨ ઇનà«àª¸à«àªŸà«‰àª² કરી શકે છે જે તમારા ઉપકરણને નà«àª•àª¸àª¾àª¨ પહોંચાડે છે, તમારા મોબાઇલ બિલમાં છà«àªªàª¾àª¯à«‡àª²àª¾ ચારà«àªœ ઉમેરી શકે છે અથવા તમારી વà«àª¯àª•à«àª¤àª¿àª—ત માહિતી ચોરી શકે છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ટà«àª¯à«‚ટૉરિઅલ ફરી શરૂ કરો</translation>
+<translation id="2219735899272417925">ડિવાઇસ રીસેટ કરવà«àª‚ જરૂરી છે</translation>
<translation id="2224337661447660594">ઇનà«àªŸàª°àª¨à«‡àªŸ àªàª•à«àª¸à«‡àª¸ નથી</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />ડાયગà«àª¨à«‹àª¸à«àªŸàª¿àª•à«àª¸ àªàªªà«àª²àª¿àª•à«‡àª¶àª¨<ph name="END_LINK" />નો ઉપયોગ કરીને તમારà«àª‚ કનેકà«àª¶àª¨ ઠીક કરો</translation>
<translation id="2239100178324503013">હમણાં મોકલો</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">કોઈ મંજૂરી નથી (ડિફૉલà«àªŸ)</translation>
<translation id="2512413427717747692">'Chromeને ડિફૉલà«àªŸ બà«àª°àª¾àª‰àªàª° તરીકે સેટ કરો' બટન, iOS સેટિંગમાં Chromeને સિસà«àªŸàª®àª¨àª¾ ડિફૉલà«àªŸ બà«àª°àª¾àª‰àªàª° તરીકે સેટ કરવા માટે Enter કી દબાવો</translation>
<translation id="2515629240566999685">તમારા વિસà«àª¤àª¾àª°àª®àª¾àª‚ સિગà«àª¨àª² તપાસીને</translation>
+<translation id="2515761554693942801">તમે <ph name="PROVIDER_ORIGIN" />નો ઉપયોગ કરતી વેબસાઇટ પર ટચ ID વડે ચકાસણી કરવાનà«àª‚ પસંદ કરà«àª¯à«àª‚ છે. આ પà«àª°àª¦àª¾àª¤àª¾àª તમારી ચà«àª•àªµàª£à«€ પદà«àª§àª¤àª¿ વિશેની માહિતી કદાચ સà«àªŸà«‹àª° કરી હોઈ શકે છે, જેને તમે <ph name="LINK_TEXT" /> કરી શકો છો.</translation>
<translation id="2521385132275182522">નીચે જમણી બાજà«àª સà«àªŸà«‡àªªàª² લાગાવો</translation>
<translation id="2521736961081452453">ફોરà«àª® બનાવો</translation>
<translation id="2523886232349826891">કારà«àª¡àª¨à«€ માહિતીને માતà«àª° આ ડિવાઇસ પર સાચવવામાં આવી છે</translation>
<translation id="2524461107774643265">વધૠમાહિતી ઉમેરો</translation>
<translation id="2529899080962247600">આ ફીલà«àª¡àª®àª¾àª‚ <ph name="MAX_ITEMS_LIMIT" /> કરતાં વધારે àªàª¨à«àªŸà«àª°à«€ હોવી જોઈઠનહી. વધારાની તમામ àªàª¨à«àªŸà«àª°à«€ અવગણવામાં આવશે.</translation>
+<translation id="253493526287553278">પà«àª°à«‹àª®à«‹ કોડની વિગતો જà«àª“</translation>
<translation id="2535585790302968248">ખાનગી રીતે બà«àª°àª¾àª‰àª કરવા માટે, નવી છૂપી ટૅબ ખોલો</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{અને અનà«àª¯ 1}one{અને વધૠ#}other{અને વધૠ#}}</translation>
<translation id="2536110899380797252">સરનામà«àª‚ ઉમેરો</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">ફોટોગà«àª°àª¾àª«àª¿àª• અને ડિજિટલ આરà«àªŸ</translation>
<translation id="2601150049980261779">રોમાનà«àªŸàª¿àª• મૂવી</translation>
<translation id="2604589665489080024">પૉપ મà«àª¯à«àªàª¿àª•</translation>
-<translation id="2609632851001447353">વૈવિધà«àª¯</translation>
<translation id="2610561535971892504">કૉપિ કરવા માટે કà«àª²àª¿àª•</translation>
<translation id="2617988307566202237">Chrome નીચે આપેલી માહિતી <ph name="BEGIN_EMPHASIS" />સાચવશે નહીં<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">રૉક-8K</translation>
<translation id="2677696497921480781">જનà«àª®àª¦àª¿àªµàª¸ અને સà«àª®à«ƒàª¤àª¿ દિવસ</translation>
<translation id="2677748264148917807">છોડો</translation>
+<translation id="2679714844901977852">વધૠસà«àª°àª•à«àª·àª¿àª¤ અને વધૠàªàª¡àªªà«€ ચેકઆઉટ માટે, તમારા Google àªàª•àª¾àª‰àª¨à«àªŸ <ph name="USER_EMAIL" />માં તમારા કારà«àª¡àª¨à«€ અને બિલિંગની માહિતી સાચવો</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">શà«àª°à«‡àª·à«àª  ફિટ</translation>
<translation id="2688969097326701645">હા, ચાલૠરાખો</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">ઇનà«àªŸàª°àª¨à«‡àªŸ સેવા આપનારી કંપનીઓ (ISPs)</translation>
<translation id="2856444702002559011">હà«àª®àª²àª¾àª–ોરો કદાચ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />માંથી તમારી માહિતી (ઉદાહરણ તરીકે, પાસવરà«àª¡, મેસેજ અથવા કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡) ચોરવાનો પà«àª°àª¯àª¾àª¸ કરી રહà«àª¯àª¾àª‚ હોઈ શકે છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">આ સાઇટ ઘૃણાસà«àªªàª¦ અથવા ભà«àª°àª¾àª®àª• જાહેરાતો બતાવે છે.</translation>
-<translation id="286512204874376891">સંભવિત કપટ સામે તમને સà«àª°àª•à«àª·àª¿àª¤ રાખી શકાય તે માટે, કોઈ વરà«àªšà«àª¯à«àª…લ કારà«àª¡ થકી તમારા વાસà«àª¤àªµàª¿àª• કારà«àª¡àª¨à«€ ઓળખ છà«àªªàª¾àªµàª¾àª¯ છે. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ફà«àª°à«‡àª¨à«àª¡àª²à«€</translation>
<translation id="28761159517501904">મૂવી</translation>
<translation id="2876489322757410363">બાહà«àª¯ àªàªªà«àª²àª¿àª•à«‡àª¶àª¨ મારફતે ચà«àª•àªµàª£à«€ કરવા માટે છૂપો મોડ છોડી રહà«àª¯àª¾àª‚ છીàª. ચાલૠરાખીàª?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ડિસà«àª•</translation>
<translation id="3162559335345991374">તમે ઉપયોગ કરી રહà«àª¯àª¾ છો તે વાઇ-ફાઇને તેના લોગિન પેજની મà«àª²àª¾àª•àª¾àª¤ લેવાની જરૂર હોઈ શકે છે.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">આઇલેનà«àª¡</translation>
<translation id="3176929007561373547">પà«àª°à«‰àª•à«àª¸à«€ સરà«àªµàª° કારà«àª¯ કરી રહà«àª¯à«àª‚ છે તેની ખાતરી કરવા માટે તમારà«àª‚ પà«àª°à«‰àª•à«àª¸à«€ સેટિંગ તપાસો
અથવા તમારા નેટવરà«àª• àªàª¡àª®àª¿àª¨àª¨à«‹ સંપરà«àª• કરો. જો તમે પà«àª°à«‰àª•à«àª¸à«€ સરà«àªµàª°àª¨à«‹ ઉપયોગ કરવો 
જોઈઠàªàªµà«àª‚ ન માનતા હો:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">ઘડિયાળ ભૂલ</translation>
<translation id="3369459162151165748">વાહનના ભાગો અને àªàª•à«àª¸à«‡àª¸àª°à«€</translation>
<translation id="3371064404604898522">Chromeને ડિફૉલà«àªŸ બà«àª°àª¾àª‰àªàª° તરીકે સેટ કરો</translation>
-<translation id="3371076217486966826"><ph name="URL" /> આ કરવા માગે છે:
- • તમારી આસપાસનો 3D નકશો બનાવવા અને તમારા કૅમેરાનà«àª‚ સà«àªŸà«‡àªŸàª¸ ટà«àª°à«…ક કરવા
- • તમારા કૅમેરાનો ઉપયોગ કરવા</translation>
<translation id="337363190475750230">જોગવાઈ દૂર કરી</translation>
<translation id="3375754925484257129">Chromeની સલામતી માટે તપાસ ચલાવો</translation>
<translation id="3377144306166885718">સરà«àªµàª° TLSના જૂના વરà«àªàª¨àª¨à«‹ ઉપયોગ કરે છે.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">વિતરણ માટેનà«àª‚ સરનામà«àª‚</translation>
<translation id="3402261774528610252">આ સાઇટને લોડ કરવા માટે ઉપયોગમાં લેવાયેલા કનેકà«àª¶àª¨àª®àª¾àª‚ TLS 1.0 અથવા TLS 1.1નો ઉપયોગ કરવામાં આવે છે, જે જૂના છે અને ભવિષà«àª¯àª®àª¾àª‚ બંધ કરવામાં આવશે. àªàª•àªµàª¾àª° બંધ કરી દીધા, પછી વપરાશકરà«àª¤àª¾àª“ને આ સાઇટ લોડ કરવાથી રોકવામાં આવશે. સરà«àªµàª°à«‡ TLS 1.2 અથવા તે પછીનà«àª‚ વરà«àªàª¨ ચાલૠકરવà«àª‚ જોઈàª.</translation>
<translation id="3405664148539009465">ફોનà«àªŸà«àª¸ કસà«àªŸàª®àª¾àª‡àª કરો </translation>
+<translation id="3407789382767355356">તà«àª°à«€àªœàª¾ પકà«àª·àª¨à«àª‚ સાઇન-ઇન</translation>
<translation id="3409896703495473338">સà«àª°àª•à«àª·àª¾ સેટિંગ મેનેજ કરો</translation>
<translation id="3414952576877147120">કદ:</translation>
<translation id="3417660076059365994">તમે જે ફાઇલો અપલોડ કરો અથવા જોડો તેને Google Cloud અથવા તà«àª°à«€àªœàª¾ પકà«àª·à«‹àª¨à«‡ વિશà«àª²à«‡àª·àª£ માટે મોકલવામાં આવે છે. ઉદાહરણ તરીકે, તેને સંવેદનશીલ વà«àª¯àª•à«àª¤àª¿àª—ત ડેટા અથવા માલવેર માટે સà«àª•à«…ન કરવામાં આવી શકે છે.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">મૂવીની સૂચિઓ અને થિયેટરના શોટાઇમ</translation>
<translation id="3479552764303398839">હમણાં નહીં</translation>
<translation id="3484560055331845446">તમે તમારા Google àªàª•àª¾àª‰àª¨à«àªŸàª¨à«‹ àªàª•à«àª¸à«‡àª¸ ગà«àª®àª¾àªµà«€ શકો છો. Chrome તમને હમણાં જ તમારો પાસવરà«àª¡ બદલવાનો સà«àªàª¾àªµ આપે છે. તમને સાઇન ઇન કરવા માટે કહેવામાં આવશે.</translation>
+<translation id="3484861421501147767">રિમાઇનà«àª¡àª°: સાચવેલો પà«àª°à«‹àª®à«‹ કોડ ઉપલબà«àª§ છે</translation>
<translation id="3487845404393360112">ટà«àª°à«‡ 4</translation>
<translation id="3495081129428749620">પેજ
<ph name="PAGE_TITLE" />માં શોધો</translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">મેનેજ કરો</translation>
<translation id="3816482573645936981">મૂલà«àª¯ (જગà«àª¯àª¾ અનà«àª¯ દà«àªµàª¾àª°àª¾ લેવામાં આવી છે)</translation>
<translation id="382518646247711829">જો તમે કોઈ પà«àª°à«‰àª•à«àª¸à«€ સરà«àªµàª°àª¨à«‹ ઉપયોગ કરો છો...</translation>
+<translation id="3826050100957962900">Third-party sign-in</translation>
<translation id="3827112369919217609">ચોકà«àª•àª¸</translation>
<translation id="3827666161959873541">પારિવારિક મૂવી</translation>
<translation id="3828924085048779000">ખાલી પાસફà«àª°à«‡àªàª¨à«‡ અનà«àª®àª¤àª¿ નથી. </translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">તારીખ અને સમય અપડેટ કરો</translation>
<translation id="3858860766373142691">નામ</translation>
<translation id="3872834068356954457">વિજà«àªžàª¾àª¨</translation>
+<translation id="3875783148670536197">મને બતાવો કે કેવી રીતે</translation>
<translation id="3881478300875776315">ઓછી પંકà«àª¤àª¿àª“ બતાવો</translation>
<translation id="3884278016824448484">વિરોધાભાસી ઉપકરણ ઓળખકરà«àª¤àª¾</translation>
-<translation id="3885155851504623709">પેરિશ</translation>
<translation id="388632593194507180">નિરીકà«àª·àª£ કરતà«àª‚ જણાયà«àª‚</translation>
<translation id="3886948180919384617">સà«àªŸà«…કર 3</translation>
<translation id="3890664840433101773">ઇમેઇલ ઉમેરો</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> અવરોધિત છે</translation>
<translation id="3978338123949022456">શોધ મોડ, કà«àªµà«‡àª°à«€ ટાઇપ કરો અને <ph name="KEYWORD_SUFFIX" /> વડે શોધવા માટે Enter કી દબાવો</translation>
<translation id="398470910934384994">પકà«àª·à«€àª“</translation>
+<translation id="3985750352229496475">સરનામા મેનેજ કરો…</translation>
<translation id="3986705137476756801">હાલ પૂરતà«àª‚, લાઇવ કૅપà«àª¶àª¨àª¨à«€ સà«àªµàª¿àª§àª¾ બંધ કરો</translation>
<translation id="3987940399970879459">1 MB કરતાં ઓછà«àª‚</translation>
<translation id="3990250421422698716">બહાર નીકળેલા ભાગને અંદર દબાવો</translation>
+<translation id="3992684624889376114">આ પેજ વિશે</translation>
<translation id="3996311196211510766">આ સાઇટ <ph name="ORIGIN" /> દà«àªµàª¾àª°àª¾ વિનંતી કરવામાં આવી છે કે ઑરિજિન પૉલિસી તેની
બધી વિનંતી પર લાગૠથાય છે, પણ હાલમાં આ પૉલિસી લાગૠકરી શકાતી નથી.</translation>
<translation id="4006465311664329701">Google Payનો ઉપયોગ કરતી ચà«àª•àªµàª£à«€ પદà«àª§àª¤àª¿àª“, ઑફરો અને સરનામાં</translation>
@@ -1227,6 +1234,7 @@
<translation id="4305666528087210886">તમારી ફાઇલ àªàª•à«àª¸à«‡àª¸ કરી શકાઈ નથી</translation>
<translation id="4306529830550717874">સરનામà«àª‚ સાચવીàª?</translation>
<translation id="4306812610847412719">કà«àª²àª¿àªªàª¬à«‹àª°à«àª¡</translation>
+<translation id="4310070645992025887">તમારા પà«àª°àªµàª¾àª¸ શોધો</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">અવરોધિત કરો (ડિફૉલà«àªŸ)</translation>
<translation id="4314815835985389558">સિંક મેનેજ કરો</translation>
@@ -1277,6 +1285,7 @@
<translation id="4435702339979719576">પોસà«àªŸàª•àª¾àª°à«àª¡)</translation>
<translation id="443673843213245140">પà«àª°à«‰àª•à«àª¸à«€àª¨à«‹ ઉપયોગ બંધ કરેલો છે પણ àªàª• સà«àªªàª·à«àªŸ પà«àª°à«‰àª•à«àª¸à«€ ગોઠવણીનો ઉલà«àª²à«‡àª– કરેલો છે.</translation>
<translation id="4441832193888514600">અવગણવામાં આવà«àª¯à«àª‚ કારણ કે પૉલિસી માતà«àª° કà«àª²àª¾àª‰àª¡ વપરાશકરà«àª¤àª¾àª¨à«€ પૉલિસી તરીકે જ સેટ કરવામાં આવી શકે છે.</translation>
+<translation id="4442470707340296952">Chrome ટૅબ</translation>
<translation id="4450893287417543264">ફરી બતાવશો નહીં</translation>
<translation id="4451135742916150903">HID ડિવાઇસ સાથે કનેકà«àªŸ કરવાનà«àª‚ પૂછી શકે છે</translation>
<translation id="4452328064229197696">તમે હાલમાં જ ઉપયોગમાં લીધેલો પાસવરà«àª¡ ડેટા ઉલà«àª²àª‚ઘનમાં જોવા મળà«àª¯à«‹ છે. તમારા àªàª•àª¾àª‰àª¨à«àªŸ સà«àª°àª•à«àª·àª¿àª¤ રાખવા માટે, Google પાસવરà«àª¡ મેનેજર તમારા સાચવેલા પાસવરà«àª¡àª¨à«‡ ચેક કરવાનો સà«àªàª¾àªµ આપે છે.</translation>
@@ -1415,6 +1424,7 @@
<translation id="483241715238664915">ચેતવણીઓ ચાલૠકરો</translation>
<translation id="4834250788637067901">Google Payનો ઉપયોગ કરતી ચà«àª•àªµàª£à«€ પદà«àª§àª¤àª¿àª“, ઑફરો અને સરનામાં</translation>
<translation id="4838327282952368871">સà«àªµàªªà«àª¨àª¶à«€àª²</translation>
+<translation id="4839087176073128681">Googleની ઉદà«àª¯à«‹àª— જગતની અગà«àª°àª£à«€ સà«àª°àª•à«àª·àª¾ સà«àªµàª¿àª§àª¾ વડે આગલી વખતે વધૠàªàª¡àªªàª¥à«€ ચà«àª•àªµàª£à«€ કરો અને તમારા કારà«àª¡àª¨à«‡ સà«àª°àª•à«àª·àª¿àª¤ રાખો.</translation>
<translation id="4840250757394056958">તમારો Chrome ઇતિહાસ જà«àª“</translation>
<translation id="484462545196658690">ઑટો</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> અને અનà«àª¯ ઘણા પર ડિસà«àª•àª¾àª‰àª¨à«àªŸ મેળવો</translation>
@@ -1477,6 +1487,7 @@
<translation id="5011561501798487822">ઓળખવામાં આવેલી ભાષા</translation>
<translation id="5015510746216210676">મશીનનà«àª‚ નામ:</translation>
<translation id="5017554619425969104">તમે કૉપિ કરેલી ટેકà«àª¸à«àªŸ</translation>
+<translation id="5017828934289857214">મને પછીથી યાદ કરાવો</translation>
<translation id="5018422839182700155">આ પેજ ખોલી શકતાં નથી</translation>
<translation id="5019198164206649151">બેકઅપ સà«àªŸà«‹àª° કરવà«àª‚ ખરાબ સà«àª¥àª¿àª¤àª¿àª®àª¾àª‚ છે</translation>
<translation id="5020776957610079374">વરà«àª²à«àª¡ મà«àª¯à«àªàª¿àª•</translation>
@@ -1496,6 +1507,7 @@
<translation id="5051305769747448211">લાઇવ કૉમેડી</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{નજીકના શેરની સà«àªµàª¿àª§àª¾àª¨à«‹ ઉપયોગ કરીને આ ફાઇલ મોકલવા માટે, તમારા ડિવાઇસ પર સà«àªªà«‡àª¸ (<ph name="DISK_SPACE_SIZE" />) ખાલી કરો}one{નજીકના શેરની સà«àªµàª¿àª§àª¾àª¨à«‹ ઉપયોગ કરીને આ ફાઇલ મોકલવા માટે, તમારા ડિવાઇસ પર સà«àªªà«‡àª¸ (<ph name="DISK_SPACE_SIZE" />) ખાલી કરો}other{નજીકના શેરની સà«àªµàª¿àª§àª¾àª¨à«‹ ઉપયોગ કરીને આ ફાઇલો મોકલવા માટે, તમારા ડિવાઇસ પર સà«àªªà«‡àª¸ (<ph name="DISK_SPACE_SIZE" />) ખાલી કરો}}</translation>
<translation id="5056549851600133418">તમારા માટે લેખ</translation>
+<translation id="5060483733937416656">તમે <ph name="PROVIDER_ORIGIN" />નો ઉપયોગ કરતી વેબસાઇટ પર Windows Hello વડે ચકાસણી કરવાનà«àª‚ પસંદ કરà«àª¯à«àª‚ છે. આ પà«àª°àª¦àª¾àª¤àª¾àª તમારી ચà«àª•àªµàª£à«€ પદà«àª§àª¤àª¿ વિશેની માહિતી કદાચ સà«àªŸà«‹àª° કરી હોઈ શકે છે, જેને તમે <ph name="LINK_TEXT" /> કરી શકો છો.</translation>
<translation id="5061227663725596739">શà«àª‚ તમારો અરà«àª¥ <ph name="LOOKALIKE_DOMAIN" /> હતો?</translation>
<translation id="5066056036849835175">પà«àª°àª¿àª¨à«àªŸàª¨à«‹ ઇતિહાસ</translation>
<translation id="5068234115460527047">હેજ ફંડ</translation>
@@ -1509,10 +1521,8 @@
<translation id="5087286274860437796">સરà«àªµàª°àª¨à«àª‚ પà«àª°àª®àª¾àª£àªªàª¤à«àª° આ સમયે માનà«àª¯ નથી.</translation>
<translation id="5087580092889165836">કારà«àª¡ ઉમેરો</translation>
<translation id="5088142053160410913">ઑપરેટરને સંદેશ</translation>
-<translation id="5089810972385038852">રાજà«àª¯</translation>
<translation id="5093232627742069661">Z-ફોલà«àª¡</translation>
<translation id="5094747076828555589">આ સરà«àªµàª° સાબિત કરી શકà«àª¯à«àª‚ નથી કે તે <ph name="DOMAIN" /> છે; તેનà«àª‚ સà«àª°àª•à«àª·àª¾ પà«àª°àª®àª¾àª£àªªàª¤à«àª° Chromium દà«àªµàª¾àª°àª¾ વિશà«àªµàª¸àª¨à«€àª¯ નથી. આ કોઈ ખોટી ગોઠવણીને કારણે થયà«àª‚ હશે અથવા કોઈ હà«àª®àª²àª¾àª–ોર તમારા કનેકà«àª¶àª¨àª¨à«‡ અટકાવી રહà«àª¯à«‹ છે.</translation>
-<translation id="5095208057601539847">પà«àª°àª¾àª‚ત</translation>
<translation id="5097099694988056070">CPU/RAM વપરાશ જેવા ડિવાઇસ આંકડા</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">સાઇટ સà«àª°àª•à«àª·àª¿àª¤ નથી</translation>
@@ -1550,6 +1560,7 @@
<translation id="5171045022955879922">URL શોધો અથવા લખો</translation>
<translation id="5171689220826475070">Fanfold-યà«àª°à«‹àªªàª¿àª¯àª¨</translation>
<translation id="5172758083709347301">મશીન</translation>
+<translation id="5177076414499237632">આ પેજના સૉરà«àª¸ અને વિષય વિશે જાણો</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> માં નથી? આ ભૂલની જાણ કરો </translation>
<translation id="518639307526414276">પાળેલાં પà«àª°àª¾àª£à«€àª“ અને તેમની સંભાળ સંબંધિત પà«àª°à«‹àª¡àª•à«àªŸ</translation>
<translation id="5190835502935405962">બà«àª•àª®àª¾àª°à«àª• બાર</translation>
@@ -1710,6 +1721,7 @@
<translation id="5624120631404540903">પાસવરà«àª¡à«àª¸àª¨à«àª‚ સંચાલન કરો</translation>
<translation id="5629630648637658800">પૉલિસી સેટિંગ લોડ કરવામાં નિષà«àª«àª³ થયાં</translation>
<translation id="5631439013527180824">અમાનà«àª¯ ડિવાઇસ સંચાલન ટોકન</translation>
+<translation id="5632485077360054581">મને બતાવો કે કેવી રીતે</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> પરના હà«àª®àª²àª¾àª–ોરો કદાચ હાલમાં તમારા કમà«àªªà«àª¯à«àªŸàª° પર જોખમી પà«àª°à«‹àª—à«àª°àª¾àª® ઇનà«àª¸à«àªŸà«‰àª² કરવાનો પà«àª°àª¯àª¾àª¸ કરે છે કે જે તમારી માહિતી (ઉદાહરણ તરીકે, ફોટા, પાસવરà«àª¡, મેસેજ અને કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡) ચોરી અથવા ડિલીટ કરી શકે છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">ભà«àª°àª¾àª®àª• કનà«àªŸà«‡àª¨à«àªŸ બà«àª²à«‰àª• કરી</translation>
<translation id="5633259641094592098">કલà«àªŸ અને ઇનà«àª¡à«€ મૂવી</translation>
@@ -1827,6 +1839,7 @@
<translation id="5989320800837274978">ફિકà«àª¸à«àª¡ પà«àª°à«‰àª•à«àª¸à«€ સરà«àªµàª° કે .pac સà«àª•à«àª°àª¿àªªà«àªŸ URL, બેમાંથી કોઈનો પણ ઉલà«àª²à«‡àª– કરેલો નથી.</translation>
<translation id="5992691462791905444">àªàª¨à«àªœàª¿àª¨àª¿àª¯àª°àª¿àª‚ગ Z-ફોલà«àª¡</translation>
<translation id="5995727681868049093">તમારા Google àªàª•àª¾àª‰àª¨à«àªŸàª®àª¾àª‚ તમારી માહિતી, પà«àª°àª¾àª‡àªµàª¸à«€ અને સà«àª°àª•à«àª·àª¾ મેનેજ કરો</translation>
+<translation id="5997247540087773573">તમે હાલમાં જ ઉપયોગમાં લીધેલો પાસવરà«àª¡ ડેટા ઉલà«àª²àª‚ઘનમાં જોવા મળà«àª¯à«‹ છે. તમારા àªàª•àª¾àª‰àª¨à«àªŸ સà«àª°àª•à«àª·àª¿àª¤ રાખવા માટે, Google Password Manager હમણાં જ તમારો પાસવરà«àª¡ બદલવાનો અને તમારા સાચવેલા પાસવરà«àª¡àª¨à«‡ ચેક કરવાનો સà«àªàª¾àªµ આપે છે.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' માટે <ph name="RESULT_COUNT" /> પરિણામ</translation>
<translation id="6006484371116297560">કà«àª²àª¾àª¸àª¿àª•</translation>
<translation id="6008122969617370890">N-થી-1 સà«àª§à«€àª¨à«‹ કà«àª°àª®</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">આગલી વખતે વધૠàªàª¡àªªàª¥à«€ ચà«àª•àªµàª£à«€ કરવા માટે, તમારા કારà«àª¡ અને બિલિંગ સરનામાંને તમારા Google àªàª•àª¾àª‰àª¨à«àªŸàª®àª¾àª‚ સાચવો.</translation>
<translation id="6279183038361895380">તમારા કરà«àª¸àª°àª¨à«‡ બતાવવા માટે |<ph name="ACCELERATOR" />| દબાવો</translation>
<translation id="6280223929691119688">આ સરનામે વિતરણ કરી શકતા નથી. કોઈ ભિનà«àª¨ સરનામà«àª‚ પસંદ કરો.</translation>
-<translation id="6282194474023008486">પોસà«àªŸàª² કોડ</translation>
<translation id="6285507000506177184">Chromeમાં ડાઉનલોડ મેનેજ કરવા માટેનà«àª‚ બટન, Chromeમાં તમે ડાઉનલોડ કરેલી ફાઇલો મેનેજ કરવા માટે Enter કી દબાવો</translation>
<translation id="6289939620939689042">પેજનો રંગ</translation>
<translation id="6290238015253830360">તમારા સૂચવેલા લેખ અહીં દેખાય છે</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> કરતાં ઓછી જગà«àª¯àª¾ ખાલી કરે છે. તમારી આગલી મà«àª²àª¾àª•àª¾àª¤ સમયે કેટલીક સાઇટ વધૠધીમે લોડ થઈ શકે છે.</translation>
<translation id="6337534724793800597">નામ દà«àªµàª¾àª°àª¾ નીતિઓને ફિલà«àªŸàª° કરો</translation>
<translation id="6340739886198108203">જà«àª¯àª¾àª°à«‡ ગોપનીય કનà«àªŸà«‡àª¨à«àªŸ દેખાતà«àª‚ હોય, તà«àª¯àª¾àª°à«‡ àªàª¡àª®àª¿àª¨àª¿àª¸à«àªŸà«àª°à«‡àªŸàª°àª¨à«€ પૉલિસી સà«àª•à«àª°à«€àª¨àª¶à«‰àªŸ અથવા રેકોરà«àª¡àª¿àª‚ગ લેવાનો સà«àªàª¾àªµ આપતી નથી:</translation>
+<translation id="6348220984832452017">સકà«àª°àª¿àª¯ વિવિધતાઓ</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ઇનà«àª¸à«àªŸà«‰àª² કરો</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{àªàª•àªªàª£ નહીં}=1{1 પાસવરà«àª¡ (<ph name="DOMAIN_LIST" /> માટે, સિંક કરેલા)}=2{2 પાસવરà«àª¡ (<ph name="DOMAIN_LIST" /> માટે, સિંક કરેલા)}one{# પાસવરà«àª¡ (<ph name="DOMAIN_LIST" /> માટે, સિંક કરેલા)}other{# પાસવરà«àª¡ (<ph name="DOMAIN_LIST" /> માટે, સિંક કરેલા)}}</translation>
<translation id="6355392890578844978">આ બà«àª°àª¾àª‰àªàª° કંપની દà«àªµàª¾àª°àª¾ અથવા અનà«àª¯ સંસà«àª¥àª¾ દà«àªµàª¾àª°àª¾ મેનેજ કરવામાં આવતà«àª‚ નથી. આ ડિવાઇસ પરની પà«àª°àªµà«ƒàª¤à«àª¤àª¿ Chromiumની બહારથી મેનેજ કરી શકાય છે. <ph name="BEGIN_LINK" />વધૠજાણો<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">Google પાસવરà«àª¡ બદલો</translation>
<translation id="6433490469411711332">સંપરà«àª• માહિતીમાં ફેરફાર કરો</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ઠકનેકà«àªŸ કરવાનો ઇનકાર કરà«àª¯à«‹.</translation>
-<translation id="6438025220577812695">હà«àª‚ તેને જાતે બદલીશ</translation>
<translation id="6440503408713884761">અવગણી</translation>
<translation id="6443406338865242315">તમે કયા àªàª•à«àª¸à«àªŸà«‡àª‚શન અને પà«àª²àª—-ઇન ઇનà«àª¸à«àªŸà«‰àª² કરà«àª¯àª¾ છે</translation>
<translation id="6446163441502663861">Kahu (àªàª¨à«àªµàª²àªª)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">જિનેટિકà«àª¸</translation>
<translation id="6831043979455480757">અનà«àªµàª¾àª¦ કરો</translation>
<translation id="6833752742582340615">વધૠસà«àª°àª•à«àª·àª¿àª¤ અને વધૠàªàª¡àªªà«€ ચેકઆઉટ માટે, તમારા Google àªàª•àª¾àª‰àª¨à«àªŸàª®àª¾àª‚ તમારા કારà«àª¡àª¨à«€ અને બિલિંગની માહિતી સાચવો</translation>
-<translation id="6839929833149231406">કà«àª·à«‡àª¤à«àª°</translation>
<translation id="6846340164947227603">વરà«àªšà«àª¯à«àª…લ કારà«àª¡ નંબરનો ઉપયોગ કરો…</translation>
<translation id="6852204201400771460">àªàªª ફરીથી લોડ કરીàª?</translation>
+<translation id="6857776781123259569">પાસવરà«àª¡ મેનેજ કરો…</translation>
<translation id="686485648936420384">ગà«àª°àª¾àª¹àª• સંસાધનો</translation>
<translation id="6865412394715372076">હાલમાં આ કારà«àª¡àª¨à«€ ચકાસણી કરી શકાતી નથી</translation>
<translation id="6869334554832814367">વà«àª¯àª•à«àª¤àª¿àª—ત લોન</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">ઉપકરણ</translation>
<translation id="696703987787944103">પરસેપચà«àª¯à«àª…લ</translation>
<translation id="6968269510885595029">તમારી સિકà«àª¯à«àª°àª¿àªŸà«€ કીનો ઉપયોગ કરો</translation>
-<translation id="6970216967273061347">જિલà«àª²à«‹</translation>
<translation id="6971439137020188025">Slidesમાં àªàª¡àªªàª¥à«€ કોઈ નવી Google પà«àª°àª¸à«àª¤à«àª¤àª¿ બનાવો</translation>
<translation id="6972629891077993081">HID ડિવાઇસ</translation>
<translation id="6973656660372572881">ફિકà«àª¸à«àª¡ પà«àª°à«‰àª•à«àª¸à«€ સરà«àªµàª° અને .pac script URL બનà«àª¨à«‡àª¨à«‹ ઉલà«àª²à«‡àª– કરેલો છે.</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">તમારા ડિવાઇસમાં આ સà«àªµàª¿àª§àª¾ ઉપલબà«àª§ નથી</translation>
<translation id="7083258188081898530">ટà«àª°à«‡ 9</translation>
<translation id="7086090958708083563">વપરાશકરà«àª¤àª¾àª અપલોડની વિનંતી કરી છે</translation>
-<translation id="7087282848513945231">કાઉનà«àªŸàª¿</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, ટૅબ દબાવો અને પછી Chrome સેટિંગમાં પરવાનગીઓ અને સમગà«àª° સાઇટનો સà«àªŸà«‹àª° કરેલો ડેટા મેનેજ કરવા માટે Enter કી દબાવો</translation>
<translation id="7096937462164235847">વેબસાઇટની ઓળખ ચકાસવામાં આવી નથી.</translation>
<translation id="7101893872976785596">હોરર મૂવી</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />ફોરà«àª®àª®àª¾àª‚ દાખલ કરેલી માહિતી<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">આ સરનામે વિતરણ કરી શકાતà«àª‚ નથી. કોઈ ભિનà«àª¨ સરનામà«àª‚ પસંદ કરો.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">તમારા àªàª¡àª®àª¿àª¨ દà«àªµàª¾àª°àª¾ આ ડેટાને કૉપિ કરવાથી પà«àª°àª¤àª¿àª¬àª‚ધિત કરવામાં આવà«àª¯à«‹ છે.</translation>
<translation id="7135130955892390533">સà«àªŸà«‡àªŸàª¸ બતાવો</translation>
<translation id="7138472120740807366">વિતરણ પદà«àª§àª¤àª¿</translation>
-<translation id="7139724024395191329">àªàª®àª¿àª°à«‡àªŸ</translation>
<translation id="7139892792842608322">પà«àª°àª¾àª¥àª®àª¿àª• ટà«àª°à«‡</translation>
<translation id="714064300541049402">બાજૠ2 છબીને X અકà«àª· પર ખસેડો</translation>
<translation id="7152423860607593928">નંબર-14 (àªàª¨à«àªµàª²àªª)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">આ સાઇટ પરના હà«àª®àª²àª¾àª–ોરો તમને તમારા બà«àª°àª¾àª‰àªàª¿àª‚ગ અનà«àª­àªµàª¨à«‡ નà«àª•àª¸àª¾àª¨ પહોંચાડે àªàªµàª¾ પà«àª°à«‹àª—à«àª°àª¾àª® ઇનà«àª¸à«àªŸà«‰àª² કરવા માટે છેતરવાનો પà«àª°àª¯àª¾àª¸ કરી શકે છે (ઉદાહરણ તરીકે, તમારà«àª‚ હોમપેજ બદલીને અથવા તમે મà«àª²àª¾àª•àª¾àª¤ લો છો તે સાઇટ પર વધૠપડતી જાહેરાતો બતાવીને).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ગોપનીય તરીકે ચિહà«àª¨àª¿àª¤ કરવામાં આવેલા ડેટા પર લીધેલા પગલાં (લૉગ ઇન કરà«àª¯àª¾ પછી 1 પગલાની જાણ થઈ). <ph name="BEGIN_LINK" />વધૠજાણો<ph name="END_LINK" />}one{ગોપનીય તરીકે ચિહà«àª¨àª¿àª¤ કરવામાં આવેલા ડેટા પર લીધેલા પગલાં (લૉગ ઇન કરà«àª¯àª¾ પછી # પગલાની જાણ થઈ). <ph name="BEGIN_LINK" />વધૠજાણો<ph name="END_LINK" />}other{ગોપનીય તરીકે ચિહà«àª¨àª¿àª¤ કરવામાં આવેલા ડેટા પર લીધેલા પગલાં (લૉગ ઇન કરà«àª¯àª¾ પછી # પગલાંની જાણ થઈ). <ph name="BEGIN_LINK" />વધૠજાણો<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">મેઇલબૉકà«àª¸ 6</translation>
+<translation id="7675325315208090829">ચà«àª•àªµàª£à«€ પદà«àª§àª¤àª¿àª“ મેનેજ કરો…</translation>
<translation id="7676643023259824263">કà«àª²àª¿àªªàª¬à«‹àª°à«àª¡ ટેકà«àª¸à«àªŸàª®àª¾àª‚થી શોધો, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Chrome સેટિંગમાં તમારો બà«àª°àª¾àª‰àªàª¿àª‚ગ ઇતિહાસ જà«àª“ અને મેનેજ કરો</translation>
<translation id="7679947978757153706">બેસબૉલ</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">સà«àª•àª°à«àªŸ</translation>
<translation id="7770259615151589601">નિરà«àª¦àª¿àª·à«àªŸ-લાંબà«àª‚</translation>
<translation id="7773005668374414287">ઉપર તરફ તે જ કà«àª°àª®àª®àª¾àª‚</translation>
-<translation id="777702478322588152">પà«àª°à«€àª«à«‡àªšàª°</translation>
<translation id="7791011319128895129">રિલીઠન થયેલી</translation>
<translation id="7791196057686275387">ગાંસળી બનાવો</translation>
<translation id="7791543448312431591">ઉમેરો</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">વà«àª¯àª¾àªµàª¸àª¾àª¯àª¿àª• અને તેની આગળનà«àª‚ શિકà«àª·àª£</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />"ની ગોઠવણી યોગà«àª¯ રીતે કરવામાં આવી નથી. સામાનà«àª¯ રીતે "<ph name="SOFTWARE_NAME" />"ને અનઇનà«àª¸à«àªŸà«‰àª² કરવાથી સમસà«àª¯àª¾ હલ થઈ જાય છે. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ખાદà«àª¯ ઉતà«àªªàª¾àª¦àª¨</translation>
-<translation id="8066955247577885446">માફ કરશો, કંઈક ખોટà«àª‚ થયà«àª‚.</translation>
<translation id="8067872629359326442">હમણાં જ કોઈ છેતરામણી સાઇટ પર તમે તમારો પાસવરà«àª¡ દાખલ કરà«àª¯à«‹. Chromium સહાય કરી શકે છે. તમારો પાસવરà«àª¡ બદલવા માટે અને Googleને ઠજાણ કરવા માટે કે તમારà«àª‚ àªàª•àª¾àª‰àª¨à«àªŸ જોખમમાં હોઈ શકે છે, àªàª•àª¾àª‰àª¨à«àªŸ સà«àª°àª•à«àª·àª¿àª¤ કરો પર કà«àª²àª¿àª• કરો.</translation>
<translation id="8070439594494267500">àªàªªàª¨à«àª‚ આઇકન</translation>
<translation id="8074253406171541171">10x13 (àªàª¨à«àªµàª²àªª)</translation>
<translation id="8075736640322370409">Google Sheetમાં àªàª¡àªªàª¥à«€ નવી શીટ બનાવો</translation>
<translation id="8075898834294118863">સાઇટ સેટિંગ મેનેજ કરો</translation>
+<translation id="8076492880354921740">ટૅબà«àª¸</translation>
<translation id="8078141288243656252">ફેરવેલ હોય તà«àª¯àª¾àª°à«‡ àªàª¨à«‹àªŸà«‡àªŸ કરી શકતા નથી</translation>
<translation id="8079031581361219619">સાઇટ ફરીથી લોડ કરી�</translation>
<translation id="8081087320434522107">સીડાન</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">સેટિંગ</translation>
+<translation id="8428634594422941299">સમજાઈ ગયà«àª‚</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, ટૅબ દબાવો અને પછી Chrome સેટિંગમાં કà«àª•à«€àª¨à«€ તમારી પસંદગીઓ મેનેજ કરવા માટે Enter કી દબાવો</translation>
<translation id="8433057134996913067">આ તમને મોટાભાગની વેબસાઇટà«àª¸àª®àª¾àª‚થી સાઇન આઉટ કરશે.</translation>
<translation id="8434840396568290395">પાળેલાં પà«àª°àª¾àª£à«€àª“</translation>
@@ -2792,6 +2803,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> ઠ<ph name="ORIGIN" /> માટેનો તમારો કોડ છે</translation>
<translation id="874918643257405732">આ ટૅબને બà«àª•àª®àª¾àª°à«àª• કરો</translation>
<translation id="8751426954251315517">કૃપા કરીને આગલી વખતે ફરી પà«àª°àª¯àª¾àª¸ કરો</translation>
+<translation id="8757526089434340176">Google Pay ઑફર ઉપલબà«àª§ છે</translation>
<translation id="8758885506338294482">સà«àªªàª°à«àª§àª¾àª¤à«àª®àª• વીડિયો ગેમ</translation>
<translation id="8759274551635299824">આ કારà«àª¡àª¨à«€ સમયસીમા સમાપà«àª¤ થઈ ગઈ છે</translation>
<translation id="87601671197631245">આ સાઇટ હજી પણ જૂના સà«àª°àª•à«àª·àª¾ કનà«àª«àª¿àª—à«àª¯à«àª°à«‡àª¶àª¨àª¨à«‹ ઉપયોગ કરે છે, જેને લીધે તમારી માહિતી (ઉદાહરણ તરીકે, પાસવરà«àª¡, મેસેજ અથવા કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡) જà«àª¯àª¾àª°à«‡ આ સાઇટને મોકલવામાં આવે, તà«àª¯àª¾àª°à«‡ તે જોખમમાં આવી શકે છે.</translation>
@@ -2799,6 +2811,7 @@
<translation id="8763927697961133303">USB ઉપકરણ</translation>
<translation id="8763986294015493060">હાલમાં ખà«àª²à«àª²à«€ બધી છૂપી વિનà«àª¡à«‹ બંધ કરો</translation>
<translation id="8766943070169463815">સà«àª°àª•à«àª·àª¿àª¤ ચà«àª•àªµàª£à«€ માટે લૉગ ઇન વિગતના પà«àª°àª®àª¾àª£à«€àª•àª°àª£àª¨à«€ શીટ ખà«àª²à«àª²à«€ છે</translation>
+<translation id="8767765348545497220">સહાયનà«àª‚ બબલ બંધ કરો</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">મોટરસાઇકલ</translation>
<translation id="8790007591277257123">&amp;ફરી ડિલીટ કરો</translation>
@@ -2811,6 +2824,7 @@
<translation id="8806285662264631610">સà«àª¨àª¾àª¨ અને શરીરની સંભાળ રાખતી પà«àª°à«‹àª¡àª•à«àªŸ</translation>
<translation id="8807160976559152894">પà«àª°àª¤à«àª¯à«‡àª• પેજ પછી ટà«àª°àª¿àª® કરો</translation>
<translation id="8808828119384186784">Chrome સેટિંગ</translation>
+<translation id="8813277370772331957">મને પછીથી યાદ કરાવો</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, તમારા Chrome સેટિંગમાંથી Chrome અપડેટ કરવા માટે Tab પછી Enter દબાવો</translation>
<translation id="8820817407110198400">બà«àª•àª®àª¾àª°à«àª•</translation>
<translation id="882338992931677877">મેનà«àª¯à«àª…લ સà«àª²à«‰àªŸ</translation>
@@ -2990,6 +3004,7 @@
<translation id="988159990683914416">વિકાસકરà«àª¤àª¾ બિલà«àª¡</translation>
<translation id="989988560359834682">àªàª¡à«àª°à«‡àª¸àª®àª¾àª‚ ફેરફાર કરો</translation>
<translation id="991413375315957741">મોશન અથવા લાઇટ સેનà«àª¸àª°</translation>
+<translation id="992110854164447044">વરà«àªšà«àª¯à«àª…લ કારà«àª¡ સંભવિત કપટથી તમારà«àª‚ સંરકà«àª·àª£ કરવા માટે, તમારા વાસà«àª¤àªµàª¿àª• કારà«àª¡àª¨à«€ ઓળખ છà«àªªàª¾àªµà«‡ છે. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ગà«àª²àª¾àª¬à«€</translation>
<translation id="992432478773561401">તમારા કમà«àªªà«àª¯à«àªŸàª°àª®àª¾àª‚ અથવા નેટવરà«àª• પર "<ph name="SOFTWARE_NAME" />" યોગà«àª¯ રીતે ઇનà«àª¸à«àªŸà«‰àª² થયà«àª‚ નથી:
diff --git a/chromium/components/strings/components_strings_hi.xtb b/chromium/components/strings/components_strings_hi.xtb
index f15309e76a8..d7c3e16ceab 100644
--- a/chromium/components/strings/components_strings_hi.xtb
+++ b/chromium/components/strings/components_strings_hi.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">अनà¥à¤¦à¤¾à¤¨, सà¥à¤•à¥‰à¤²à¤°à¤¶à¤¿à¤ª, और वितà¥à¤¤à¥€à¤¯ सहायता</translation>
<translation id="1048785276086539861">जब आप वीडियो के ऊपर टेकà¥à¤¸à¥à¤Ÿ, लिंक वगैरह में बदलाव करते हैं, तो दसà¥à¤¤à¤¾à¤µà¥‡à¥›, सिंगल पेज वà¥à¤¯à¥‚ पर वापस चला जाà¤à¤—ा</translation>
<translation id="1050038467049342496">दूूूूसरे à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ बंद करें</translation>
+<translation id="1053959602163383901">आपने <ph name="PROVIDER_ORIGIN" /> इसà¥à¤¤à¥‡à¤®à¤¾à¤² करने वाली वेबसाइटों पर, पà¥à¤·à¥à¤Ÿà¤¿ करने वाले à¤à¤• डिवाइस की मदद से पà¥à¤·à¥à¤Ÿà¤¿ करने का विकलà¥à¤ª चà¥à¤¨à¤¾ है. हो सकता है कि सेवा उपलबà¥à¤§ कराने वाली इस कंपनी ने आपके पैसे चà¥à¤•à¤¾à¤¨à¥‡ के तरीके से जà¥à¤¡à¤¼à¥€ जानकारी सेव की हो, जिसे <ph name="LINK_TEXT" /> किया जा सकता है.</translation>
<translation id="1055184225775184556">&amp;जोड़ना वापस लाà¤à¤‚</translation>
<translation id="1056663316309890343">फ़ोटो से जà¥à¤¡à¤¼à¤¾ सॉफ़à¥à¤Ÿà¤µà¥‡à¤¯à¤°</translation>
<translation id="1056898198331236512">चेतावनी</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">पिकअप का तरीका</translation>
<translation id="1281476433249504884">सà¥à¤Ÿà¥ˆà¤•à¤° 1</translation>
<translation id="1285320974508926690">कभी भी इस साइट का अनà¥à¤µà¤¾à¤¦ न करें</translation>
+<translation id="1288548991597756084">कारà¥à¤¡ को सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ तरीके से सेव करें</translation>
<translation id="1292571435393770077">टà¥à¤°à¥‡ 16</translation>
<translation id="1292701964462482250">"आपके कंपà¥à¤¯à¥‚टर पर मौजूद सॉफ़à¥à¤Ÿà¤µà¥‡à¤¯à¤° Chrome को सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रूप से वेब से कनेकà¥à¤Ÿ होने से रोक रहा है" (केवल Windows कंपà¥à¤¯à¥‚टर)</translation>
<translation id="1294154142200295408">अलग-अलग तरह की कमांड-लाइन</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;गड़बड़ी ठीक करने के लिà¤, उस पेज पर &lt;strong&gt;कनेकà¥à¤Ÿ करें&lt;/strong&gt; को कà¥à¤²à¤¿à¤• करें, जिसे आप खोलने की कोशिश कर रहे हैं.&lt;/p&gt;</translation>
<translation id="1507780850870535225">लैंडसà¥à¤•à¥‡à¤ª डिज़ाइन</translation>
<translation id="1513706915089223971">इतिहास à¤à¤‚टà¥à¤°à¥€ की सूची</translation>
+<translation id="1516097932025103760">कारà¥à¤¡ को à¤à¤¨à¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ (सà¥à¤°à¤•à¥à¤·à¤¿à¤¤) किया जाà¤à¤—ा और उसे सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ तरीके से सेव किया जाà¤à¤—ा. हालांकि, कारà¥à¤¡ वेरिफ़िकेशन कोड (सीवीसी) को कभी भी सेव नहीं किया जाता है.</translation>
<translation id="1517433312004943670">फ़ोन नंबर आवशà¥à¤¯à¤• है</translation>
<translation id="1519264250979466059">बिलà¥à¤¡ तारीख</translation>
<translation id="1521159554480556801">फ़ाइबर और टेकà¥à¤¸à¤Ÿà¤¾à¤‡à¤² आरà¥à¤Ÿ</translation>
@@ -272,7 +275,7 @@
<translation id="1634828734222219955">कà¥à¤²</translation>
<translation id="163669211644121865">टैकà¥à¤¸ भरने से जà¥à¤¡à¤¼à¥€ जानकारी तैयार करने और सलाह देने वाली सेवाà¤à¤‚</translation>
<translation id="1638780421120290329">कारà¥à¤¡ सेव नहीं किया जा सकता</translation>
-<translation id="1639239467298939599">लोड हो रहा है</translation>
+<translation id="1639239467298939599">लोड हो रही है</translation>
<translation id="1640180200866533862">उपयोगकरà¥à¤¤à¤¾ नीतियां</translation>
<translation id="1640244768702815859"><ph name="BEGIN_LINK" />साइट के मà¥à¤–पृषà¥à¤  पर विज़िट करके<ph name="END_LINK" /> देखें.</translation>
<translation id="1641976391427233992">यहां तक के आउटपà¥à¤Ÿ में देरी</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">भà¥à¤—तान के तरीके सेव करें और जानकारी भरें</translation>
<translation id="1663943134801823270">कारà¥à¤¡ और पते Chrome से मिलते हैं. आप उनà¥à¤¹à¥‡à¤‚ <ph name="BEGIN_LINK" />सेटिंग<ph name="END_LINK" /> में पà¥à¤°à¤¬à¤‚धित कर सकते हैं.</translation>
<translation id="1671391448414634642">अब से <ph name="SOURCE_LANGUAGE" /> भाषा के पेज का अनà¥à¤µà¤¾à¤¦ <ph name="TARGET_LANGUAGE" /> भाषा में किया जाà¤à¤—ा.</translation>
+<translation id="1673886523110456987">ऑफ़र इसà¥à¤¤à¥‡à¤®à¤¾à¤² करने के लिà¤, <ph name="CARD_DETAIL" /> से पैसे चà¥à¤•à¤¾à¤à¤‚</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> से <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">शॉरà¥à¤Ÿ à¤à¤œ फ़रà¥à¤¸à¥à¤Ÿ</translation>
<translation id="168693727862418163">इस नीति का मान अपनी सà¥à¤•à¥€à¤®à¤¾ के हिसाब से काम नहीं कर सका और इसे अनदेखा किया जाà¤à¤—ा.</translation>
@@ -307,6 +311,7 @@
<translation id="1717218214683051432">मोशन सेंसर</translation>
<translation id="1717494416764505390">मेलबॉकà¥à¤¸ 3</translation>
<translation id="1718029547804390981">à¤à¤¨à¥‹à¤Ÿà¥‡à¤Ÿ करने के लिठदसà¥à¤¤à¤¾à¤µà¥‡à¤œà¤¼ बहà¥à¤¤ बड़ा है</translation>
+<translation id="1720941539803966190">टà¥à¤¯à¥‚टोरियल बंद करें</translation>
<translation id="1721424275792716183">* फ़ीलà¥à¤¡ ज़रूरी है</translation>
<translation id="1727613060316725209">पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° मानà¥à¤¯ है</translation>
<translation id="1727741090716970331">मानà¥à¤¯ कारà¥à¤¡ नंबर जोड़ें</translation>
@@ -395,7 +400,7 @@
<translation id="1959001866257244765"><ph name="BEGIN_WHITEPAPER_LINK" />आप जिन पेजों पर जाते हैं उनमें से कà¥à¤› के यूआरà¤à¤², सिसà¥à¤Ÿà¤® की थोड़ी जानकारी, और पेज का कà¥à¤› कॉनà¥à¤Ÿà¥‡à¤‚ट<ph name="END_WHITEPAPER_LINK" /> Google को भेजकर, वेब पर सभी के लिठसà¥à¤°à¤•à¥à¤·à¤¾ को बेहतर बनाने में मदद करें. <ph name="BEGIN_PRIVACY_PAGE_LINK" />निजता नीति<ph name="END_PRIVACY_PAGE_LINK" /></translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="1973335181906896915">कà¥à¤°à¤®à¤¬à¤¦à¥à¤§ करने में गड़बड़ी</translation>
-<translation id="1974060860693918893">बेहतर</translation>
+<translation id="1974060860693918893">बेहतर सेटिंग</translation>
<translation id="1975457531113383421">इनपà¥à¤Ÿ टà¥à¤°à¥‡</translation>
<translation id="1975584088563498795">मेलबॉकà¥à¤¸ 10</translation>
<translation id="1978555033938440688">फ़रà¥à¤®à¤µà¥‡à¤¯à¤° वरà¥à¤¶à¤¨</translation>
@@ -423,8 +428,8 @@
<translation id="205212645995975601">बारबेकà¥à¤¯à¥ और गà¥à¤°à¤¿à¤²à¤¿à¤‚ग</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> भाषा के पेजों का अनà¥à¤µà¤¾à¤¦ नहीं किया जाà¤à¤—ा.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{यह कंटà¥à¤°à¥‹à¤² चालू होने और सà¥à¤Ÿà¥‡à¤Ÿà¤¸ à¤à¤•à¥à¤Ÿà¤¿à¤µ होने पर, Chrome यह देखता है कि आपकी हाल की बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग गतिविधि, किस बड़े गà¥à¤°à¥à¤ª या "समानता रखने वाले लोगों" से काफ़ी हद तक मिलती है. विजà¥à¤žà¤¾à¤ªà¤¨ देने वाले, गà¥à¤°à¥à¤ª के लिठविजà¥à¤žà¤¾à¤ªà¤¨à¥‹à¤‚ को चà¥à¤¨ सकते हैं. साथ ही, आपकी बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग गतिविधि आपके डिवाइस पर गोपनीय रखी जाती है. आपका गà¥à¤°à¥à¤ª हर दिन अपडेट किया जाता है.}=1{यह कंटà¥à¤°à¥‹à¤² चालू होने और सà¥à¤Ÿà¥‡à¤Ÿà¤¸ à¤à¤•à¥à¤Ÿà¤¿à¤µ होने पर, Chrome यह देखता है कि आपकी हाल की बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग गतिविधि, किस बड़े गà¥à¤°à¥à¤ª या "समानता रखने वाले लोगों" से काफ़ी हद तक मिलती है. विजà¥à¤žà¤¾à¤ªà¤¨ देने वाले, गà¥à¤°à¥à¤ª के लिठविजà¥à¤žà¤¾à¤ªà¤¨à¥‹à¤‚ को चà¥à¤¨ सकते हैं. साथ ही, आपकी बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग गतिविधि आपके डिवाइस पर गोपनीय रखी जाती है. आपका गà¥à¤°à¥à¤ª हर दिन अपडेट किया जाता है.}one{यह कंटà¥à¤°à¥‹à¤² चालू होने और सà¥à¤Ÿà¥‡à¤Ÿà¤¸ à¤à¤•à¥à¤Ÿà¤¿à¤µ होने पर, Chrome यह देखता है कि आपकी हाल की बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग गतिविधि, किस बड़े गà¥à¤°à¥à¤ª या "समानता रखने वाले लोगों" से काफ़ी हद तक मिलती है. विजà¥à¤žà¤¾à¤ªà¤¨ देने वाले, गà¥à¤°à¥à¤ª के लिठविजà¥à¤žà¤¾à¤ªà¤¨à¥‹à¤‚ को चà¥à¤¨ सकते हैं. साथ ही, आपकी बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग गतिविधि आपके डिवाइस पर गोपनीय रखी जाती है. आपका गà¥à¤°à¥à¤ª हर {NUM_DAYS} दिन में अपडेट किया जाता है.}other{यह कंटà¥à¤°à¥‹à¤² चालू होने और सà¥à¤Ÿà¥‡à¤Ÿà¤¸ à¤à¤•à¥à¤Ÿà¤¿à¤µ होने पर, Chrome यह देखता है कि आपकी हाल की बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग गतिविधि, किस बड़े गà¥à¤°à¥à¤ª या "समानता रखने वाले लोगों" से काफ़ी हद तक मिलती है. विजà¥à¤žà¤¾à¤ªà¤¨ देने वाले, गà¥à¤°à¥à¤ª के लिठविजà¥à¤žà¤¾à¤ªà¤¨à¥‹à¤‚ को चà¥à¤¨ सकते हैं. साथ ही, आपकी बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग गतिविधि आपके डिवाइस पर गोपनीय रखी जाती है. आपका गà¥à¤°à¥à¤ª हर {NUM_DAYS} दिन में अपडेट किया जाता है.}}</translation>
-<translation id="2053553514270667976">ज़िप कोड</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 सà¥à¤à¤¾à¤µ}one{# सà¥à¤à¤¾à¤µ}other{# सà¥à¤à¤¾à¤µ}}</translation>
+<translation id="2066915425250589881">मिटाने का अनà¥à¤°à¥‹à¤§</translation>
<translation id="2068528718802935086">शिशॠऔर छोटे बचà¥à¤šà¥‡</translation>
<translation id="2071156619270205202">इस कारà¥à¤¡ को वरà¥à¤šà¥à¤…ल कारà¥à¤¡ के तौर पर इसà¥à¤¤à¥‡à¤®à¤¾à¤² नहीं किया जा सकता.</translation>
<translation id="2071692954027939183">सूचनाओं को अपने-आप बà¥à¤²à¥‰à¤• कर दिया गया, कà¥à¤¯à¥‹à¤‚कि आम तौर पर आप उनà¥à¤¹à¥‡à¤‚ अनà¥à¤®à¤¤à¤¿ नहीं देते हैं</translation>
@@ -436,7 +441,6 @@
<translation id="2088086323192747268">सिंक करने का बटन मैनेज करें, Chrome की सेटिंग में आप जो जानकारी सिंक करते हैं उसे मैनेज करने के लिठEnter दबाà¤à¤‚</translation>
<translation id="2091887806945687916">आवाज़</translation>
<translation id="2094505752054353250">डोमेन का गलत-मिलान</translation>
-<translation id="2096368010154057602">विभाग</translation>
<translation id="2099652385553570808">बाईं ओर टà¥à¤°à¤¿à¤ªà¤² सà¥à¤Ÿà¥‡à¤ªà¤²</translation>
<translation id="2101225219012730419">वरà¥à¤¶à¤¨:</translation>
<translation id="2102134110707549001">मज़बूत पासवरà¥à¤¡ सà¥à¤à¤¾à¤à¤‚…</translation>
@@ -473,7 +477,6 @@
<translation id="2185836064961771414">अमेरिकन फ़à¥à¤Ÿà¤¬à¥‰à¤²</translation>
<translation id="2187317261103489799">पता लगाà¤à¤‚ (डिफ़ॉलà¥à¤Ÿ)</translation>
<translation id="2188375229972301266">नीचे की ओर à¤à¤• से ज़à¥à¤¯à¤¾à¤¦à¤¾ पंच</translation>
-<translation id="2188852899391513400">आपने अभी जो पासवरà¥à¤¡ इसà¥à¤¤à¥‡à¤®à¤¾à¤² किया है वह लीक हो चà¥à¤•à¤¾ है. Google के पासवरà¥à¤¡ मैनेजर का सà¥à¤à¤¾à¤µ है कि आप तà¥à¤°à¤‚त इस पासवरà¥à¤¡ को बदलें और फिर, सेव किठगठपासवरà¥à¤¡ की जांच करें. इस तरह आपके खाते सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रहेंगे.</translation>
<translation id="219906046732893612">घर की मरमà¥à¤®à¤¤</translation>
<translation id="2202020181578195191">खतà¥à¤® होने का मानà¥à¤¯ वरà¥à¤· डालें</translation>
<translation id="22081806969704220">टà¥à¤°à¥‡ 3</translation>
@@ -484,6 +487,8 @@
<translation id="2215727959747642672">फ़ाइल में बदलाव करना</translation>
<translation id="2215963164070968490">कà¥à¤¤à¥à¤¤à¥‡</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> पर इस समय मौजूद हमलावर à¤à¤¸à¥‡ खतरनाक à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ इंसà¥à¤Ÿà¥‰à¤² कर सकते हैं जो आपके डिवाइस को नà¥à¤•à¤¸à¤¾à¤¨ पहà¥à¤‚चा सकते हैं और आपके मोबाइल बिल में अनजाने खरà¥à¤šà¥‡ जोड़ सकते हैं या आपकी वà¥â€à¤¯à¤•à¥â€à¤¤à¤¿à¤—त जानकारी चà¥à¤°à¤¾ सकते हैं. <ph name="BEGIN_LEARN_MORE_LINK" />ज़à¥à¤¯à¤¾à¤¦à¤¾ जानें<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">टà¥à¤¯à¥‚टोरियल को रीसà¥à¤Ÿà¤¾à¤°à¥à¤Ÿ करें</translation>
+<translation id="2219735899272417925">डिवाइस को रीसेट करना ज़रूरी है</translation>
<translation id="2224337661447660594">इंटरनेट कनेकà¥à¤¶à¤¨ नहीं है</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />गड़बड़ी की पहचान करने वाले à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨<ph name="END_LINK" /> का उपयोग करके अपने कनेकà¥à¤¶à¤¨ को ठीक करें</translation>
<translation id="2239100178324503013">अभी भेजें</translation>
@@ -581,11 +586,13 @@
<translation id="2512101340618156538">अनà¥à¤®à¤¤à¤¿ नहीं है (डिफ़ॉलà¥à¤Ÿ रूप से)</translation>
<translation id="2512413427717747692">'Chrome को डिफ़ॉलà¥à¤Ÿ बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° के रूप में सेट करें' बटन, Chrome को सिसà¥à¤Ÿà¤® के डिफ़ॉलà¥à¤Ÿ बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° के रूप में सेट करने के लिठEnter दबाà¤à¤‚</translation>
<translation id="2515629240566999685">अपने इलाके में सिगà¥à¤¨à¤² की जाà¤à¤š करें</translation>
+<translation id="2515761554693942801">आपने <ph name="PROVIDER_ORIGIN" /> का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करने वाली वेबसाइटों पर, Touch ID की मदद से पà¥à¤·à¥à¤Ÿà¤¿ करने का विकलà¥à¤ª चà¥à¤¨à¤¾ है. हो सकता है कि सेवा उपलबà¥à¤§ कराने वाली इस कंपनी ने आपके पैसे चà¥à¤•à¤¾à¤¨à¥‡ के तरीके से जà¥à¤¡à¤¼à¥€ जानकारी सेव की हो, जिसे <ph name="LINK_TEXT" /> किया जा सकता है.</translation>
<translation id="2521385132275182522">नीचे दाईं ओर सà¥à¤Ÿà¥‡à¤ªà¤²</translation>
<translation id="2521736961081452453">फ़ॉरà¥à¤® बनाà¤à¤‚</translation>
<translation id="2523886232349826891">सिरà¥à¤«à¤¼ इस डिवाइस पर सेव किया गया</translation>
<translation id="2524461107774643265">ज़à¥à¤¯à¤¾à¤¦à¤¾ जानकारी जोड़ें</translation>
<translation id="2529899080962247600">इस फ़ीलà¥à¤¡ में <ph name="MAX_ITEMS_LIMIT" /> से ज़à¥à¤¯à¤¾à¤¦à¤¾ à¤à¤‚टà¥à¤°à¥€ नहीं हो सकती. इसके बाद हà¥à¤ˆ सभी à¤à¤‚टà¥à¤°à¥€ को नज़रअंदाज़ किया जाà¤à¤—ा.</translation>
+<translation id="253493526287553278">पà¥à¤°à¥‹à¤®à¥‹ कोड से जà¥à¤¡à¤¼à¥€ जानकारी देखें</translation>
<translation id="2535585790302968248">निजी तौर पर बà¥à¤°à¤¾à¤‰à¤œà¤¼ करने के लिà¤, à¤à¤• नया गà¥à¤ªà¥à¤¤ टैब खोलें</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{और 1 और वà¥à¤¯à¤•à¥à¤¤à¤¿}one{और # और लोग}other{और # और लोग}}</translation>
<translation id="2536110899380797252">पता जोड़ें</translation>
@@ -621,7 +628,6 @@
<translation id="259821504105826686">फ़ोटोगà¥à¤°à¤¾à¤«à¤¼à¤¿à¤• और डिजिटल आरà¥à¤Ÿ</translation>
<translation id="2601150049980261779">रोमैंटिक फ़िलà¥à¤®à¥‡à¤‚</translation>
<translation id="2604589665489080024">पॉप मà¥à¤¯à¥‚ज़िक</translation>
-<translation id="2609632851001447353">विविधताà¤à¤‚</translation>
<translation id="2610561535971892504">कà¥à¤²à¤¿à¤• टू कॉपी</translation>
<translation id="2617988307566202237">Chrome यहां बताई गई जानकारी को <ph name="BEGIN_EMPHASIS" />सेव नहीं करेगा<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -654,6 +660,7 @@
<translation id="2676271551327853224">रॉक-8के</translation>
<translation id="2677696497921480781">जनà¥à¤®à¤¦à¤¿à¤¨ और नामकरण का दिन</translation>
<translation id="2677748264148917807">छोड़ें</translation>
+<translation id="2679714844901977852">à¤à¤Ÿà¤ªà¤Ÿ और सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ तरीके से चेकआउट करने के लिà¤, अपने कारà¥à¤¡ और बिलिंग से जà¥à¥œà¥€ जानकारी को अपने Google खाते <ph name="USER_EMAIL" /> में सेव करें</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">सबसे बà¥à¤¿à¤¯à¤¾ फ़िटिंग</translation>
<translation id="2688969097326701645">हां, जारी रखें</translation>
@@ -702,7 +709,6 @@
<translation id="2854764410992194509">इंटरनेट सेवा देने वाली कंपनियां (आईà¤à¤¸à¤ªà¥€)</translation>
<translation id="2856444702002559011">हो सकता है कि हमलावर <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> से आपकी जानकारी (जैसे- पासवरà¥à¤¡, मैसेज या कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡ वगैरह) चà¥à¤°à¤¾à¤¨à¥‡ की कोशिश कर रहे हों. <ph name="BEGIN_LEARN_MORE_LINK" />ज़à¥à¤¯à¤¾à¤¦à¤¾ जानें<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">इस साइट में तंग करने वाले या गà¥à¤®à¤°à¤¾à¤¹ करने वाले विजà¥à¤žà¤¾à¤ªà¤¨ दिखाई देते हैं.</translation>
-<translation id="286512204874376891">वरà¥à¤šà¥à¤…ल कारà¥à¤¡ आपके असली कारà¥à¤¡ की पहचान ज़ाहिर नहीं होने देता है. इससे, आपको धोखाधड़ी से बचने में मदद मिलती है. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">दोसà¥à¤¤à¤¾à¤¨à¤¾</translation>
<translation id="28761159517501904">फ़िलà¥à¤®à¥‡à¤‚</translation>
<translation id="2876489322757410363">किसी बाहरी à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ के ज़रिठपैसे चà¥à¤•à¤¾à¤¨à¥‡ के लिठगà¥à¤ªà¥à¤¤ मोड छोड़ रहे हैं. कà¥à¤¯à¤¾ आप यह मोड बंद करना चाहते हैं?</translation>
@@ -803,7 +809,6 @@
<translation id="3158539265159265653">डिसà¥à¤•</translation>
<translation id="3162559335345991374">आप जिस वाई-फ़ाई का उपयोग कर रहे हैं, आपको उसके लॉगिन पेज पर जाने की ज़रूरत पड़ सकती है.</translation>
<translation id="3169472444629675720">तलाश करें</translation>
-<translation id="3174168572213147020">दà¥à¤µà¥€à¤ª</translation>
<translation id="3176929007561373547">यह पकà¥à¤•à¤¾ करने के लिठकि पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¤° काम कर रहा है,
अपनी पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सेटिंग जांचें या अपने नेटवरà¥à¤• à¤à¤¡à¤®à¤¿à¤¨ से संपरà¥à¤• करें. अगर आपको नहीं लगता कि आपको किसी पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¤° का उपयोग करना चाहिà¤:
<ph name="PLATFORM_TEXT" /></translation>
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">घड़ी गड़बड़ी</translation>
<translation id="3369459162151165748">वाहनों के पà¥à¤°à¥à¥›à¥‡ और à¤à¤•à¥à¤¸à¥‡à¤¸à¤°à¥€</translation>
<translation id="3371064404604898522">Chrome को डिफ़ॉलà¥à¤Ÿ बà¥à¤°à¤¾à¤‰à¥›à¤° के रूप में सेट करें</translation>
-<translation id="3371076217486966826"><ph name="URL" /> ये काम करना चाहता है:
- • आपके आस-पास की जगहों का 3D मैप बनाना और कैमरे की सà¥à¤¥à¤¿à¤¤à¤¿ टà¥à¤°à¥ˆà¤• करना
- • कैमरे का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करना</translation>
<translation id="337363190475750230">पà¥à¤°à¤¾à¤µà¤§à¤¾à¤¨ रदà¥à¤¦</translation>
<translation id="3375754925484257129">Chrome की सà¥à¤°à¤•à¥à¤·à¤¾ जांच करें</translation>
<translation id="3377144306166885718">सरà¥à¤µà¤°, TLS के पà¥à¤°à¤¾à¤¨à¥‡ वरà¥à¤¶à¤¨ का इसà¥à¤¤à¥‡à¤®à¤¾à¤² कर रहा है.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">डिलीवरी का पता</translation>
<translation id="3402261774528610252">इस साइट को लोड करने में इसà¥à¤¤à¥‡à¤®à¤¾à¤² हà¥à¤† कनेकà¥à¤¶à¤¨ TLS 1.0 या TLS 1.1 का इसà¥à¤¤à¥‡à¤®à¤¾à¤² कर रहा है. ये वरà¥à¤¶à¤¨ रोक दिठगठहैं और आने वाले समय में इनà¥à¤¹à¥‡à¤‚ बंद कर दिया जाà¤à¤—ा. बंद होने के बाद, उपयोगकरà¥à¤¤à¤¾ इस साइट को लोड नहीं कर पाà¤à¤‚गे. सरà¥à¤µà¤° पर TLS 1.2 या इसके बाद का वरà¥à¤¶à¤¨ चालू होना चाहिà¤.</translation>
<translation id="3405664148539009465">फ़ॉनà¥â€à¤Ÿ को पसंद के मà¥à¤¤à¤¾à¤¬à¤¿à¤• बनाà¤à¤‚</translation>
+<translation id="3407789382767355356">तीसरे पकà¥à¤· की सेवा के लिठसाइन इन</translation>
<translation id="3409896703495473338">सà¥à¤°à¤•à¥à¤·à¤¾ की सेटिंग मैनेज करें</translation>
<translation id="3414952576877147120">आकार:</translation>
<translation id="3417660076059365994">आप जो फ़ाइलें अपलोड करते हैं या अटैच करते हैं उनà¥à¤¹à¥‡à¤‚ विशà¥à¤²à¥‡à¤·à¤£ के लिठ'Google कà¥à¤²à¤¾à¤‰à¤¡' या तीसरे पकà¥à¤·à¥‹à¤‚ को भेजा जाता है. उदाहरण के लिà¤, इनà¥à¤¹à¥‡à¤‚ संवेदनशील डेटा या मैलवेयर की जांच के लिठसà¥à¤•à¥ˆà¤¨ किया जा सकता है.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">फ़िलà¥à¤®à¥‹à¤‚ की सूचियां और थिà¤à¤Ÿà¤° में शो का समय</translation>
<translation id="3479552764303398839">अभी नहीं</translation>
<translation id="3484560055331845446">आप अपने Google खाते का à¤à¤•à¥à¤¸à¥‡à¤¸ खो सकते हैं. Chrome आपको इसी समय अपना पासवरà¥à¤¡ बदलने का सà¥à¤à¤¾à¤µ देता है. आपको साइन इन करने को कहा जाà¤à¤—ा.</translation>
+<translation id="3484861421501147767">रिमाइंडर: सेव किया गया पà¥à¤°à¥‹à¤®à¥‹ कोड उपलबà¥à¤§ है</translation>
<translation id="3487845404393360112">टà¥à¤°à¥‡ 4</translation>
<translation id="3495081129428749620">पेज में ढूंढें
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">पà¥à¤°à¤¬à¤‚धित करें</translation>
<translation id="3816482573645936981">मान (इस मान की जगह लागू किया गया)</translation>
<translation id="382518646247711829">अगर आप पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¤° का उपयोग करते हैं...</translation>
+<translation id="3826050100957962900">तीसरे पकà¥à¤· की सेवा के लिà¤, साइन इन करना</translation>
<translation id="3827112369919217609">पूरा</translation>
<translation id="3827666161959873541">पारिवारिक फ़िलà¥à¤®à¥‡à¤‚</translation>
<translation id="3828924085048779000">खाली 'पासफ़à¥à¤°à¥‡à¤œà¤¼' की अनà¥à¤®à¤¤à¤¿ नहीं है.</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">तारीख और समय अपडेट करें</translation>
<translation id="3858860766373142691">नाम</translation>
<translation id="3872834068356954457">विजà¥à¤žà¤¾à¤¨</translation>
+<translation id="3875783148670536197">मà¥à¤à¥‡ इसका तरीका दिखाà¤à¤‚</translation>
<translation id="3881478300875776315">कम लाइनें दिखाà¤à¤‚</translation>
<translation id="3884278016824448484">विरोधाभासी डिवाइस पहचानकरà¥à¤¤à¤¾</translation>
-<translation id="3885155851504623709">पैरिश</translation>
<translation id="388632593194507180">यह पता चला है कि नज़र रखी जा रही है</translation>
<translation id="3886948180919384617">सà¥à¤Ÿà¥ˆà¤•à¤° 3</translation>
<translation id="3890664840433101773">ईमेल जोड़ें</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> को बà¥à¤²à¥‰à¤• किया गया है</translation>
<translation id="3978338123949022456">खोज मोड, <ph name="KEYWORD_SUFFIX" /> को खोजने के लिठकà¥à¤µà¥‡à¤°à¥€ लिखें और Enter दबाà¤à¤‚</translation>
<translation id="398470910934384994">पकà¥à¤·à¤¿à¤¯à¥‹à¤‚ से जà¥à¥œà¥€ सेवाà¤à¤‚</translation>
+<translation id="3985750352229496475">पते मैनेज करें...</translation>
<translation id="3986705137476756801">अभी के लिठलाइव कैपà¥à¤¶à¤¨ की सà¥à¤µà¤¿à¤§à¤¾ बंद करें</translation>
<translation id="3987940399970879459">à¤à¤• à¤à¤®à¤¬à¥€ से कम</translation>
<translation id="3990250421422698716">जोग ऑफ़सेट</translation>
+<translation id="3992684624889376114">इस पेज के बारे में</translation>
<translation id="3996311196211510766">साइट <ph name="ORIGIN" /> ने यह अनà¥à¤°à¥‹à¤§ किया है कि इसके सभी अनà¥à¤°à¥‹à¤§à¥‹à¤‚ पर
मूल नीति लागू होती है, लेकिन अभी यह नीति लागू नहीं की जा सकती.</translation>
<translation id="4006465311664329701">Google Pay का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करने वाले पैसे चà¥à¤•à¤¾à¤¨à¥‡ के तरीके, ऑफ़र, और पते</translation>
@@ -1227,6 +1234,7 @@
<translation id="4305666528087210886">आपकी फ़ाइल à¤à¤•à¥à¤¸à¥‡à¤¸ नहीं की जा सकी</translation>
<translation id="4306529830550717874">कà¥à¤¯à¤¾ आप पता सेव करना चाहते हैं?</translation>
<translation id="4306812610847412719">कà¥à¤²à¤¿à¤ªà¤¬à¥‹à¤°à¥à¤¡</translation>
+<translation id="4310070645992025887">'Chrome इतिहास' में अपनी गतिविधियां खोजें</translation>
<translation id="4312613361423056926">बी2</translation>
<translation id="4312866146174492540">बà¥à¤²à¥‰à¤• करें (डिफ़ॉलà¥à¤Ÿ)</translation>
<translation id="4314815835985389558">सिंक पà¥à¤°à¤¬à¤‚धित करें</translation>
@@ -1277,6 +1285,7 @@
<translation id="4435702339979719576">पोसà¥à¤Ÿà¤•à¤¾à¤°à¥à¤¡)</translation>
<translation id="443673843213245140">पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ का उपयोग अकà¥à¤·à¤® है लेकिन कोई सà¥â€à¤ªà¤·à¥à¤Ÿ पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ कॉनà¥à¥žà¤¿à¤—रेशन दरà¥à¤œ किया गया है.</translation>
<translation id="4441832193888514600">अनदेखा किया गया है, कà¥à¤¯à¥‹à¤‚कि इस नीति को सिरà¥à¥ž कà¥à¤²à¤¾à¤‰à¤¡ की उपयोगकरà¥à¤¤à¤¾ नीति के तौर पर सेट किया जा सकता है.</translation>
+<translation id="4442470707340296952">Chrome टैब</translation>
<translation id="4450893287417543264">फिर से न दिखाà¤à¤‚</translation>
<translation id="4451135742916150903">साइट, à¤à¤šà¤†à¤ˆà¤¡à¥€ डिवाइसों से कनेकà¥à¤Ÿ करने की अनà¥à¤®à¤¤à¤¿ मांग सकती है</translation>
<translation id="4452328064229197696">आपने अभी जो पासवरà¥à¤¡ इसà¥à¤¤à¥‡à¤®à¤¾à¤² किया है वह लीक हो चà¥à¤•à¤¾ है. Google के पासवरà¥à¤¡ मैनेजर का सà¥à¤à¤¾à¤µ है कि आप सेव किठगठपासवरà¥à¤¡ की जांच करें. इस तरह आपके खाते सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रहेंगे.</translation>
@@ -1415,6 +1424,7 @@
<translation id="483241715238664915">सà¥à¤°à¤•à¥à¤·à¤¾ चेतावनियां चालू करें</translation>
<translation id="4834250788637067901">Google Pay का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करने वाले पैसे चà¥à¤•à¤¾à¤¨à¥‡ के तरीके, ऑफ़र, और पते</translation>
<translation id="4838327282952368871">खà¥à¤µà¤¾à¤¬ जैसा</translation>
+<translation id="4839087176073128681">Google की बेहतरीन सà¥à¤°à¤•à¥à¤·à¤¾ सेवाओं की मदद से, अपने कारà¥à¤¡ को सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रखें और अगली बार तेज़ी से पैसे चà¥à¤•à¤¾à¤à¤‚.</translation>
<translation id="4840250757394056958">अपना 'Chrome इतिहास' देखें</translation>
<translation id="484462545196658690">ऑटो</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> और कई अनà¥à¤¯ पर छूट पाà¤à¤‚</translation>
@@ -1477,6 +1487,7 @@
<translation id="5011561501798487822">पहचानी गई भाषा</translation>
<translation id="5015510746216210676">मशीन का नाम:</translation>
<translation id="5017554619425969104">आपका कॉपी किया हà¥à¤† टेकà¥à¤¸à¥à¤Ÿ</translation>
+<translation id="5017828934289857214">बाद में याद दिलाà¤à¤‚</translation>
<translation id="5018422839182700155">यह पेज खà¥à¤² नहीं पा रहा है</translation>
<translation id="5019198164206649151">बैकिंग संगà¥à¤°à¤¹ खराब सà¥à¤¥à¤¿à¤¤à¤¿ में है</translation>
<translation id="5020776957610079374">वरà¥à¤²à¥à¤¡ मà¥à¤¯à¥‚ज़िक</translation>
@@ -1496,6 +1507,7 @@
<translation id="5051305769747448211">लाइव कॉमेडी</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{आस-पास शेयर करने की सà¥à¤µà¤¿à¤§à¤¾ का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करके इस फ़ाइल को भेजने के लिà¤, अपने डिवाइस में (<ph name="DISK_SPACE_SIZE" />) जगह खाली करें}one{आस-पास शेयर करने की सà¥à¤µà¤¿à¤§à¤¾ का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करके इस फ़ाइल को भेजने के लिà¤, अपने डिवाइस में (<ph name="DISK_SPACE_SIZE" />) जगह खाली करें}other{आस-पास शेयर करने की सà¥à¤µà¤¿à¤§à¤¾ का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करके इन फ़ाइलों को भेजने के लिà¤, अपने डिवाइस में (<ph name="DISK_SPACE_SIZE" />) जगह खाली करें}}</translation>
<translation id="5056549851600133418">आपके लिठलेख</translation>
+<translation id="5060483733937416656">आपने <ph name="PROVIDER_ORIGIN" /> इसà¥à¤¤à¥‡à¤®à¤¾à¤² करने वाली वेबसाइटों पर, Windows Hello की मदद से पà¥à¤·à¥à¤Ÿà¤¿ करने का विकलà¥à¤ª चà¥à¤¨à¤¾ है. हो सकता है कि सेवा उपलबà¥à¤§ कराने वाली इस कंपनी ने आपके पैसे चà¥à¤•à¤¾à¤¨à¥‡ के तरीके से जà¥à¤¡à¤¼à¥€ जानकारी सेव की हो, जिसे <ph name="LINK_TEXT" /> किया जा सकता है.</translation>
<translation id="5061227663725596739">कà¥à¤¯à¤¾ आपका मतलब <ph name="LOOKALIKE_DOMAIN" /> से है?</translation>
<translation id="5066056036849835175">पà¥à¤°à¤¿à¤‚ट करने का इतिहास</translation>
<translation id="5068234115460527047">हेज फ़ंड</translation>
@@ -1509,10 +1521,8 @@
<translation id="5087286274860437796">सरà¥à¤µà¤° का पà¥à¤°à¤®à¤¾à¤£ पतà¥à¤° इस समय मानà¥à¤¯ नहीं है.</translation>
<translation id="5087580092889165836">कारà¥à¤¡ जोड़ें</translation>
<translation id="5088142053160410913">ऑपरेटर के लिठमैसेज</translation>
-<translation id="5089810972385038852">राजà¥à¤¯</translation>
<translation id="5093232627742069661">ज़ी-फ़ोलà¥à¤¡</translation>
<translation id="5094747076828555589">यह सरà¥à¤µà¤° यह नहीं पà¥à¤°à¤®à¤¾à¤£à¤¿à¤¤ कर सका कि यह <ph name="DOMAIN" /> है; इसका सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° Chromium दà¥à¤µà¤¾à¤°à¤¾ विशà¥à¤µà¤¸à¤¨à¥€à¤¯ नहीं है. à¤à¤¸à¤¾ गलत कॉनà¥à¥žà¤¿à¤—रेशन या किसी आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¤¾ दà¥à¤µà¤¾à¤°à¤¾ आपके कनेकà¥à¤¶à¤¨ में अवरोध डालने के कारण हो सकता है.</translation>
-<translation id="5095208057601539847">पà¥à¤°à¤¾à¤‚त</translation>
<translation id="5097099694988056070">डिवाइस के आंकड़े जैसे कि सीपीयू (CPU)/RAM का इसà¥à¤¤à¥‡à¤®à¤¾à¤²</translation>
<translation id="5097501891273180634">à¤2</translation>
<translation id="5108881358339761672">साइट सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नहीं है</translation>
@@ -1550,6 +1560,7 @@
<translation id="5171045022955879922">यूआरà¤à¤² खोजें या टाइप करें</translation>
<translation id="5171689220826475070">फ़ैनफ़ोलà¥à¤¡-यूरोपियन</translation>
<translation id="5172758083709347301">मशीन</translation>
+<translation id="5177076414499237632">इस पेज के सोरà¥à¤¸ और विषय के बारे में जानें</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> में नहीं है? इस गड़बड़ी की रिपोरà¥à¤Ÿ करें</translation>
<translation id="518639307526414276">पालतू जानवरों का खाना और उनकी देखभाल से जà¥à¤¡à¤¼à¥‡ पà¥à¤°à¥‰à¤¡à¤•à¥à¤Ÿ</translation>
<translation id="5190835502935405962">बà¥à¤•à¤®à¤¾à¤°à¥à¤• बार</translation>
@@ -1710,6 +1721,7 @@
<translation id="5624120631404540903">पासवरà¥à¤¡ पà¥à¤°à¤¬à¤‚धित करें</translation>
<translation id="5629630648637658800">नीति सेटिंग लोड करने में विफल</translation>
<translation id="5631439013527180824">अमानà¥à¤¯ डिवाइस पà¥à¤°à¤¬à¤‚धन टोकन</translation>
+<translation id="5632485077360054581">मà¥à¤à¥‡ इसका तरीका दिखाà¤à¤‚</translation>
<translation id="5633066919399395251">इस समय <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> पर मौजूद हमलावर आपके कंपà¥à¤¯à¥‚टर पर à¤à¤¸à¥‡ खतरनाक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® इंसà¥à¤Ÿà¥‰à¤² करने की कोशिश कर सकते हैं जो आपकी जानकारी (उदाहरण के लिà¤, फ़ोटो, पासवरà¥à¤¡, संदेश और कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) चà¥à¤°à¤¾à¤¤à¥‡ हैं या उसे मिटा देते हैं. <ph name="BEGIN_LEARN_MORE_LINK" />ज़à¥à¤¯à¤¾à¤¦à¤¾ जानें<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">भà¥à¤°à¤¾à¤®à¤• सामगà¥à¤°à¥€ बà¥à¤²à¥‰à¤• की गई.</translation>
<translation id="5633259641094592098">कलà¥à¤Ÿ और इंडी फ़िलà¥à¤®à¥‡à¤‚</translation>
@@ -1827,6 +1839,7 @@
<translation id="5989320800837274978">न तो कोई फ़िकà¥â€à¤¸à¥â€à¤¡ पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ सरà¥à¤µà¤° और न ही कोई .pac सà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ URL साफ़ तौर पर बताया गया है.</translation>
<translation id="5992691462791905444">इंजीनियरिंग ज़ी-फ़ोलà¥à¤¡</translation>
<translation id="5995727681868049093">Google खाते में अपनी जानकारी, निजता, और सà¥à¤°à¤•à¥à¤·à¤¾ को मैनेज करें</translation>
+<translation id="5997247540087773573">आपने अभी जो पासवरà¥à¤¡ इसà¥à¤¤à¥‡à¤®à¤¾à¤² किया है वह डेटा के गलत इसà¥à¤¤à¥‡à¤®à¤¾à¤² की वजह से लीक हो चà¥à¤•à¤¾ है. Google Password Manager आपको यह सà¥à¤à¤¾à¤µ देता है कि आप तà¥à¤°à¤‚त इस पासवरà¥à¤¡ को बदलें और सेव किठगठपासवरà¥à¤¡ की जांच करें. à¤à¤¸à¤¾ करने से, आपके खाते सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रहेंगे.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' के लिठ<ph name="RESULT_COUNT" /> नतीजे मिले हैं</translation>
<translation id="6006484371116297560">कà¥à¤²à¤¾à¤¸à¤¿à¤•</translation>
<translation id="6008122969617370890">N-से-1 के कà¥à¤°à¤® में</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">अगली बार तेज़ी से भà¥à¤—तान करने के लिà¤, अपने कारà¥à¤¡ और बिलिंग पते को अपने Google खाते में सेव करें.</translation>
<translation id="6279183038361895380">अपना करà¥à¤¸à¤° दिखाने के लिठ|<ph name="ACCELERATOR" />| दबाà¤à¤‚</translation>
<translation id="6280223929691119688">इस पते पर वितरित नहीं किया जा सकता. कोई दूसरा पता चà¥à¤¨à¥‡à¤‚.</translation>
-<translation id="6282194474023008486">पोसà¥à¤Ÿà¤² कोड</translation>
<translation id="6285507000506177184">'Chrome में डाउनलोड मैनेज करें' बटन. आपने Chrome में जो फ़ाइलें डाउनलोड की हैं उनà¥à¤¹à¥‡à¤‚ मैनेज करने के लिà¤, Enter दबाà¤à¤‚</translation>
<translation id="6289939620939689042">पेज का रंग</translation>
<translation id="6290238015253830360">आपके सà¥à¤à¤¾à¤ हà¥à¤ लेख यहां दिखाई देते हैं</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> से कम जगह खाली करता है. आपकी अगली विज़िट पर कà¥à¤› साइटें और भी धीमे लोड हो सकती हैं.</translation>
<translation id="6337534724793800597">नाम के अनà¥à¤¸à¤¾à¤° नीतियां फ़िलà¥à¤Ÿà¤° करें</translation>
<translation id="6340739886198108203">जब गोपनीय कॉनà¥à¤Ÿà¥‡à¤‚ट दिख रहा हो, तो à¤à¤¡à¤®à¤¿à¤¨ नीति सà¥à¤•à¥à¤°à¥€à¤¨à¤¶à¥‰à¤Ÿ लेने या रिकॉरà¥à¤¡à¤¿à¤‚ग करने का सà¥à¤à¤¾à¤µ नहीं देती है:</translation>
+<translation id="6348220984832452017">इसà¥à¤¤à¥‡à¤®à¤¾à¤² किठजा सकने वाले वरà¥à¤¶à¤¨</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> इंसà¥à¤Ÿà¥‰à¤² करें</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{कोई पासवरà¥à¤¡ नहीं है}=1{(<ph name="DOMAIN_LIST" /> के लिà¤, सिंक किया गया) 1 पासवरà¥à¤¡}=2{(<ph name="DOMAIN_LIST" /> के लिà¤, सिंक किठगà¤) 2 पासवरà¥à¤¡}one{(<ph name="DOMAIN_LIST" /> के लिà¤, सिंक किठगà¤) # पासवरà¥à¤¡}other{(<ph name="DOMAIN_LIST" /> के लिà¤, सिंक किठगà¤) # पासवरà¥à¤¡}}</translation>
<translation id="6355392890578844978">यह बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤°, कोई कंपनी या दूसरा संगठन मैनेज नहीं करता. इस डिवाइस की गतिविधि को Chromium के बाहर मैनेज किया जा सकता है. <ph name="BEGIN_LINK" />ज़à¥à¤¯à¤¾à¤¦à¤¾ जानें<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">Google पासवरà¥à¤¡ बदलें</translation>
<translation id="6433490469411711332">संपरà¥à¤• जानकारी में बदलाव करें</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ने कनेकà¥à¤Ÿ करने से मना कर दिया है.</translation>
-<translation id="6438025220577812695">खà¥à¤¦ बदलें</translation>
<translation id="6440503408713884761">अनदेखा किया गया</translation>
<translation id="6443406338865242315">आपने कौनसे à¤à¤•à¥à¤¸à¤Ÿà¥‡à¤‚शन और पà¥à¤²à¤— इन इंसà¥à¤Ÿà¥‰à¤² किठहैं</translation>
<translation id="6446163441502663861">काहू (à¤à¤¨à¥à¤µà¥‡à¤²à¤ª)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">आनà¥à¤µà¤‚शिकी</translation>
<translation id="6831043979455480757">Translate</translation>
<translation id="6833752742582340615">à¤à¤Ÿà¤ªà¤Ÿ और सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ तरीके से चेकआउट करने के लिà¤, अपने कारà¥à¤¡ और बिलिंग से जà¥à¥œà¥€ जानकारी को Google खाते में सेव करें</translation>
-<translation id="6839929833149231406">कà¥à¤·à¥‡à¤¤à¥à¤°</translation>
<translation id="6846340164947227603">वरà¥à¤šà¥à¤…ल कारà¥à¤¡ संखà¥à¤¯à¤¾ का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करें...</translation>
<translation id="6852204201400771460">à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ फिर लोड करें?</translation>
+<translation id="6857776781123259569">पासवरà¥à¤¡ मैनेज करें...</translation>
<translation id="686485648936420384">उपभोकà¥à¤¤à¤¾ संसाधन</translation>
<translation id="6865412394715372076">इस कारà¥à¤¡ की पà¥à¤·à¥à¤Ÿà¤¿ अभी नहीं की जा सकती</translation>
<translation id="6869334554832814367">निजी क़रà¥à¤œà¤¼</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">डिवाइस</translation>
<translation id="696703987787944103">परà¥à¤¸à¥‡à¤ªà¥à¤šà¥à¤…ल</translation>
<translation id="6968269510885595029">अपनी सà¥à¤°à¤•à¥à¤·à¤¾ कà¥à¤‚जी का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करें</translation>
-<translation id="6970216967273061347">जिला</translation>
<translation id="6971439137020188025">Slides में जलà¥à¤¦à¥€ से नई Google पà¥à¤°à¤œà¤¼à¥‡à¤‚टेशन बनाà¤à¤‚</translation>
<translation id="6972629891077993081">à¤à¤šà¤†à¤ˆà¤¡à¥€ डिवाइस</translation>
<translation id="6973656660372572881">फ़िकà¥â€à¤¸à¥â€à¤¡ पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ सरà¥à¤µà¤° और .pac सà¥â€à¤•à¥à¤°à¤¿à¤ªà¥â€à¤Ÿ URL दोनों ही बताठगठहैं.</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">यह सà¥à¤µà¤¿à¤§à¤¾ आपके डिवाइस पर उपलबà¥à¤§ नहीं है</translation>
<translation id="7083258188081898530">टà¥à¤°à¥‡ 9</translation>
<translation id="7086090958708083563">उपयोगकरà¥à¤¤à¤¾ ने अपलोड का अनà¥à¤°à¥‹à¤§ किया</translation>
-<translation id="7087282848513945231">काउंटी</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome की सेटिंग में अलग-अलग साइटों पर सेव डेटा और अनà¥à¤®à¤¤à¤¿à¤¯à¤¾à¤‚ मैनेज करने के लिà¤, पहले Tab और फिर Enter दबाà¤à¤‚</translation>
<translation id="7096937462164235847">इस वेबसाइट की पहचान की पà¥à¤·à¥à¤Ÿà¤¿ नहीं हà¥à¤ˆ है.</translation>
<translation id="7101893872976785596">डरावनी फ़िलà¥à¤®à¥‡à¤‚</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />फ़ॉरà¥à¤® में डाली गई जानकारी<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">इस पते पर शिप नहीं किया जा सकता. कोई दूसरा पता चà¥à¤¨à¥‡à¤‚.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">आपके à¤à¤¡à¤®à¤¿à¤¨ ने इस डेटा को कॉपी किठजाने पर रोक लगाई है.</translation>
<translation id="7135130955892390533">सà¥à¤¥à¤¿à¤¤à¤¿ दिखाà¤à¤‚</translation>
<translation id="7138472120740807366">वितरण का तरीका</translation>
-<translation id="7139724024395191329">अमीरात</translation>
<translation id="7139892792842608322">पà¥à¤°à¤¾à¤‡à¤®à¤°à¥€ टà¥à¤°à¥‡</translation>
<translation id="714064300541049402">साइड 2 इमेज X शिफ़à¥à¤Ÿ</translation>
<translation id="7152423860607593928">संखà¥à¤¯à¤¾-14 (à¤à¤¨à¥à¤µà¥‡à¤²à¤ª)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">इस साइट पर मौजूद हमलावर धोखे से आपसे à¤à¤¸à¥‡ पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® इंसà¥à¤Ÿà¥‰à¤² करवाने की कोशिश कर सकते हैं, जिनसे आपके बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग अनà¥à¤­à¤µ को नà¥à¤•à¤¸à¤¾à¤¨ पहà¥à¤‚च सकता है (उदाहरण के लिà¤, आपका होमपेज बदलकर या आप जिन साइटों पर जाते हैं उन पर जà¥à¤¼à¤¯à¤¾à¤¦à¤¾ विजà¥à¤žà¤¾à¤ªà¤¨ दिखाकर).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{गोपनीय के तौर पर फ़à¥à¤²à¥ˆà¤— किठगठडेटा पर कारà¥à¤°à¤µà¤¾à¤ˆ की गई (लॉगिन के बाद 1 कारà¥à¤°à¤µà¤¾à¤ˆ की गई). <ph name="BEGIN_LINK" />ज़à¥à¤¯à¤¾à¤¦à¤¾ जानें<ph name="END_LINK" />}one{गोपनीय के तौर पर फ़à¥à¤²à¥ˆà¤— किठगठडेटा पर कारà¥à¤°à¤µà¤¾à¤ˆ की गई (लॉगिन के बाद # कारà¥à¤°à¤µà¤¾à¤ˆ की गई). <ph name="BEGIN_LINK" />ज़à¥à¤¯à¤¾à¤¦à¤¾ जानें<ph name="END_LINK" />}other{गोपनीय के तौर पर फ़à¥à¤²à¥ˆà¤— किठगठडेटा पर कारà¥à¤°à¤µà¤¾à¤‡à¤¯à¤¾à¤‚ की गईं (लॉगिन के बाद # कारà¥à¤°à¤µà¤¾à¤‡à¤¯à¤¾à¤‚ रिपोरà¥à¤Ÿ की गईं). <ph name="BEGIN_LINK" />ज़à¥à¤¯à¤¾à¤¦à¤¾ जानें<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">मेलबॉकà¥à¤¸ 6</translation>
+<translation id="7675325315208090829">पैसे चà¥à¤•à¤¾à¤¨à¥‡ के तरीके मैनेज करें...</translation>
<translation id="7676643023259824263">कà¥à¤²à¤¿à¤ªà¤¬à¥‹à¤°à¥à¤¡ टेकà¥à¤¸à¥à¤Ÿ, <ph name="TEXT" /> खोजें</translation>
<translation id="7679367271685653708">Chrome की सेटिंग में अपने बà¥à¤°à¤¾à¤‰à¥›à¤¿à¤‚ग इतिहास को देखें और मैनेज करें</translation>
<translation id="7679947978757153706">बेसबॉल</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">सà¥à¤•à¤°à¥à¤Ÿ</translation>
<translation id="7770259615151589601">लंबाई वाला फ़ॉरà¥à¤®à¥ˆà¤Ÿ</translation>
<translation id="7773005668374414287">इसी तरह सीधा करके रखें</translation>
-<translation id="777702478322588152">पà¥à¤°à¤¾à¤‚त</translation>
<translation id="7791011319128895129">अपà¥à¤°à¤•à¤¾à¤¶à¤¿à¤¤</translation>
<translation id="7791196057686275387">बेल</translation>
<translation id="7791543448312431591">जोड़ें</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">वà¥à¤¯à¤¾à¤µà¤¸à¤¾à¤¯à¤¿à¤• और पà¥à¤°à¥Œà¤¢à¤¼ शिकà¥à¤·à¤¾</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" सही तरीके से कॉनà¥à¤«à¤¼à¤¿à¤—र नहीं किया गया है. आमतौर पर "<ph name="SOFTWARE_NAME" />" को अनइंसà¥à¤Ÿà¥‰à¤² करने से समसà¥à¤¯à¤¾ ठीक हो जाती है. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">खाने की चीज़ों से जà¥à¤¡à¤¼à¥€ सेवाà¤à¤‚</translation>
-<translation id="8066955247577885446">माफ़ करें, कोई गड़बड़ी हà¥à¤ˆ.</translation>
<translation id="8067872629359326442">आपने अभी-अभी जिस साइट पर अपना पासवरà¥à¤¡ डाला है वह सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नहीं है. कà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® इसमें आपकी मदद कर सकता है. अपना पासवरà¥à¤¡ बदलने और Google को यह बताने के लिठकि आपका खाता सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नहीं है, 'खाता सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ करें' पर कà¥à¤²à¤¿à¤• करें.</translation>
<translation id="8070439594494267500">à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ आइकॉन</translation>
<translation id="8074253406171541171">10x13 (à¤à¤¨à¥à¤µà¥‡à¤²à¤ª)</translation>
<translation id="8075736640322370409">जलà¥à¤¦à¥€ से नई Google शीट बनाà¤à¤‚</translation>
<translation id="8075898834294118863">साइट की सेटिंग मैनेज करें</translation>
+<translation id="8076492880354921740">टैब</translation>
<translation id="8078141288243656252">घà¥à¤®à¤¾à¤¨à¥‡ पर à¤à¤¨à¥‹à¤Ÿà¥‡à¤Ÿ नहीं कर सकते</translation>
<translation id="8079031581361219619">साइट को फिर लोड करें?</translation>
<translation id="8081087320434522107">सिडान</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">सेटिंग</translation>
+<translation id="8428634594422941299">समठलिया</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome की सेटिंग में अपनी कà¥à¤•à¥€ सेटिंग मैनेज करने के लिà¤, पहले Tab और फिर Enter दबाà¤à¤‚</translation>
<translation id="8433057134996913067">इससे आप ज़à¥à¤¯à¤¾à¤¦à¤¾à¤¤à¤° वेबसाइट से पà¥à¤°à¤¸à¥à¤¥à¤¾à¤¨ कर जाà¤à¤‚गे.</translation>
<translation id="8434840396568290395">पालतू जानवर</translation>
@@ -2793,6 +2804,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> के लिठआपका कोड <ph name="ONE_TIME_CODE" /> है</translation>
<translation id="874918643257405732">इस टैब को बà¥à¤•à¤®à¤¾à¤°à¥à¤• करें</translation>
<translation id="8751426954251315517">कृपया बाद में कोशिश करें</translation>
+<translation id="8757526089434340176">Google Pay ऑफ़र उपलबà¥à¤§ है</translation>
<translation id="8758885506338294482">मà¥à¤•à¤¾à¤¬à¤²à¥‡ वाले वीडियो गेम</translation>
<translation id="8759274551635299824">इस कारà¥à¤¡ की अवधि खतà¥à¤® हो चà¥à¤•à¥€ है</translation>
<translation id="87601671197631245">यह साइट à¤à¤• पà¥à¤°à¤¾à¤¨à¥‡ सà¥à¤°à¤•à¥à¤·à¤¾ कॉनà¥à¤«à¤¼à¤¿à¤—रेशन का इसà¥à¤¤à¥‡à¤®à¤¾à¤² करती है. इस वजह से, साइट पर भेजी जाने वाली आपकी जानकारी (उदाहरण के लिà¤, पासवरà¥à¤¡, मैसेज या कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) को कोई और देख सकता है.</translation>
@@ -2800,6 +2812,7 @@
<translation id="8763927697961133303">USB डिवाइस</translation>
<translation id="8763986294015493060">सभी खà¥à¤²à¥€ हà¥à¤ˆ गà¥à¤ªà¥à¤¤ विंडो बंद करें</translation>
<translation id="8766943070169463815">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ तरीके से पेमेंट करने के लिà¤, कà¥à¤°à¥‡à¤¡à¥‡à¤‚शियल की पà¥à¤·à¥à¤Ÿà¤¿ करने की शीट खà¥à¤²à¥€ है</translation>
+<translation id="8767765348545497220">सहायता वाला बबल बंद करें</translation>
<translation id="877985182522063539">à¤4</translation>
<translation id="8785658048882205566">मोटरसाइकल</translation>
<translation id="8790007591277257123">मिटाना &amp;फिर से करें</translation>
@@ -2812,6 +2825,7 @@
<translation id="8806285662264631610">नहाने और शरीर की देखभाल से जà¥à¤¡à¤¼à¥‡ पà¥à¤°à¥‰à¤¡à¤•à¥à¤Ÿ</translation>
<translation id="8807160976559152894">हर à¤à¤• पेज के बाद टà¥à¤°à¤¿à¤® करें</translation>
<translation id="8808828119384186784">Chrome की सेटिंग</translation>
+<translation id="8813277370772331957">मà¥à¤à¥‡ बाद में याद दिलाà¤à¤‚</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome की सेटिंग से Chrome अपडेट करने के लिà¤, Tab के बाद Enter दबाà¤à¤‚</translation>
<translation id="8820817407110198400">बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="882338992931677877">मैनà¥à¤¯à¥à¤…ल सà¥à¤²à¥‰à¤Ÿ</translation>
@@ -2991,6 +3005,7 @@
<translation id="988159990683914416">डेवलपर बिलà¥à¤¡</translation>
<translation id="989988560359834682">पते में बदलाव करें</translation>
<translation id="991413375315957741">मोशन या लाइट सेंसर</translation>
+<translation id="992110854164447044">वरà¥à¤šà¥à¤…ल कारà¥à¤¡ आपके असली कारà¥à¤¡ की पहचान ज़ाहिर नहीं होने देता है. इससे, आपको धोखाधड़ी से बचने में मदद मिलती है. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">गà¥à¤²à¤¾à¤¬à¥€</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" आपके कंपà¥à¤¯à¥‚टर या नेटवरà¥à¤• पर ठीक से इंसà¥à¤Ÿà¥‰à¤² नहीं हà¥à¤† था:
diff --git a/chromium/components/strings/components_strings_hr.xtb b/chromium/components/strings/components_strings_hr.xtb
index 0bcc1a6cb91..36de73324e2 100644
--- a/chromium/components/strings/components_strings_hr.xtb
+++ b/chromium/components/strings/components_strings_hr.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Stipendije i financijska potpora</translation>
<translation id="1048785276086539861">Kad uredite napomene, dokument će se vratiti na prikaz pojedinaÄne stranice</translation>
<translation id="1050038467049342496">Zatvorite ostale aplikacije</translation>
+<translation id="1053959602163383901">Odabrali ste potvrdu pomoću ureÄ‘aja za autentifikaciju na web-lokacijama koje koriste <ph name="PROVIDER_ORIGIN" />. Taj je davatelj usluga možda pohranio podatke o vaÅ¡em naÄinu plaćanja za koje možete <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Poništi dodavanje</translation>
<translation id="1056663316309890343">Softver za upravljanje fotografijama</translation>
<translation id="1056898198331236512">Upozorenje</translation>
@@ -119,6 +120,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="1270502636509132238">NaÄin preuzimanja</translation>
<translation id="1281476433249504884">Spremnik za slaganje u snopove 1</translation>
<translation id="1285320974508926690">Nikad nemoj prevoditi ovu web-lokaciju</translation>
+<translation id="1288548991597756084">Sigurno spremi karticu</translation>
<translation id="1292571435393770077">Ladica 16</translation>
<translation id="1292701964462482250">"Softver na vaÅ¡em raÄunalu sprjeÄava sigurno povezivanje Chromea s webom" (samo na Windows raÄunalima)</translation>
<translation id="1294154142200295408">Varijacije naredbenog retka</translation>
@@ -223,6 +225,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
&lt;p&gt;Da biste ispravili tu pogrešku, kliknite &lt;strong&gt;Poveži se&lt;/strong&gt; na stranici koju pokušavate otvoriti.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Oblikovanje krajobraza</translation>
<translation id="1513706915089223971">Popis povijesnih unosa</translation>
+<translation id="1516097932025103760">Bit će šifrirana, sigurno spremljena i CVC se nikad ne pohranjuje.</translation>
<translation id="1517433312004943670">Telefonski je broj obavezan</translation>
<translation id="1519264250979466059">Datum međuverzije</translation>
<translation id="1521159554480556801">Umjetnost na vlaknu i tekstilu</translation>
@@ -288,6 +291,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="1662550410081243962">Spremi i popuni naÄine plaćanja</translation>
<translation id="1663943134801823270">Kartice i adrese dolaze iz Chromea. Njima možete upravljati u <ph name="BEGIN_LINK" />Postavkama<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Odsad će se <ph name="SOURCE_LANGUAGE" /> prevoditi na <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Platite karticom <ph name="CARD_DETAIL" /> da biste iskoristili ponudu</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> – <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Najprije kratki rub</translation>
<translation id="168693727862418163">Ova vrijednost pravila nije uspjela potvrditi valjanost sheme i zanemarit će se.</translation>
@@ -306,6 +310,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="1717218214683051432">Senzori kretanja</translation>
<translation id="1717494416764505390">Izlazni spremnik s odjeljcima 3</translation>
<translation id="1718029547804390981">Dokument je prevelik za dodavanje bilješki</translation>
+<translation id="1720941539803966190">Zatvori vodiÄ</translation>
<translation id="1721424275792716183">* Polje je obavezno</translation>
<translation id="1727613060316725209">Certifikat je važeći</translation>
<translation id="1727741090716970331">Dodajte važeći broj kartice</translation>
@@ -422,8 +427,8 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="205212645995975601">Roštilj</translation>
<translation id="2053111141626950936">Neće se prevoditi <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Kada je ta kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, odnosno skupini, vaÅ¡a nedavna aktivnost pregledavanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledavanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a se grupa ažurira svaki dan.}=1{Kada je ta kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, odnosno skupini, vaÅ¡a nedavna aktivnost pregledavanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledavanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a se grupa ažurira svaki dan.}one{Kada je ta kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, odnosno skupini, vaÅ¡a nedavna aktivnost pregledavanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledavanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a se grupa ažurira svaki {NUM_DAYS} dan.}few{Kada je ta kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, odnosno skupini, vaÅ¡a nedavna aktivnost pregledavanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledavanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a se grupa ažurira svaka {NUM_DAYS} dana.}other{Kada je ta kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje kojoj je velikoj grupi ljudi, odnosno skupini, vaÅ¡a nedavna aktivnost pregledavanja najsliÄnija. OglaÅ¡ivaÄi mogu odabrati oglase za tu grupu, a vaÅ¡a aktivnost pregledavanja ostaje privatna na vaÅ¡em ureÄ‘aju. VaÅ¡a se grupa ažurira svakih {NUM_DAYS} dana.}}</translation>
-<translation id="2053553514270667976">Poštanski broj</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 prijedlog}one{# prijedlog}few{# prijedloga}other{# prijedloga}}</translation>
+<translation id="2066915425250589881">zatražiti da se izbrišu</translation>
<translation id="2068528718802935086">NovoroÄ‘enÄad i mala djeca</translation>
<translation id="2071156619270205202">Ova kartica ne ispunjava kriterije za broj virtualne kartice.</translation>
<translation id="2071692954027939183">Obavijesti su automatski blokirane jer ih obiÄno ne dopuÅ¡tate</translation>
@@ -435,7 +440,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="2088086323192747268">Gumb za upravljanje sinkronizacijom, pritisnite Enter da biste u postavkama Chromea upravljali time koji će se podaci sinkronizirati</translation>
<translation id="2091887806945687916">Zvuk</translation>
<translation id="2094505752054353250">Domena se ne podudara</translation>
-<translation id="2096368010154057602">Departman</translation>
<translation id="2099652385553570808">Trostruko spajanje s lijeve strane</translation>
<translation id="2101225219012730419">Verzija:</translation>
<translation id="2102134110707549001">Predloži snažnu zaporku…</translation>
@@ -472,7 +476,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="2185836064961771414">AmeriÄki nogomet</translation>
<translation id="2187317261103489799">Otkrij (zadano)</translation>
<translation id="2188375229972301266">Višestruko bušenje pri dnu</translation>
-<translation id="2188852899391513400">Zaporka koju ste upravo upotrijebili otkrivena je u povredi podataka. Radi zaÅ¡tite vaÅ¡ih raÄuna Google upravitelj zaporki preporuÄuje da je odmah promijenite, a zatim provjerite svoje spremljene zaporke.</translation>
<translation id="219906046732893612">Uređenje doma</translation>
<translation id="2202020181578195191">Unesite važeću godinu isteka</translation>
<translation id="22081806969704220">Ladica 3</translation>
@@ -483,6 +486,8 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="2215727959747642672">Uređivanje datoteke</translation>
<translation id="2215963164070968490">Psi</translation>
<translation id="2218879909401188352">NapadaÄi koji su trenutaÄno na web-lokaciji <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogli bi instalirati opasne aplikacije koje će oÅ¡tetiti ureÄ‘aj, dodati skrivene troÅ¡kove na raÄun za mobilne usluge ili ukrasti vaÅ¡e osobne podatke. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Ponovo pokreni vodiÄ</translation>
+<translation id="2219735899272417925">Uređaj je potrebno vratiti na zadano</translation>
<translation id="2224337661447660594">Nema interneta</translation>
<translation id="2230458221926704099">RijeÅ¡ite problem s povezivanjem pomoću <ph name="BEGIN_LINK" />dijagnostiÄke aplikacije<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Pošalji sad</translation>
@@ -580,11 +585,13 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="2512101340618156538">Nije dopušteno (zadano)</translation>
<translation id="2512413427717747692">Gumb za postavljanje Chromea kao zadanog preglednika, pritisnite Enter da biste u postavkama iOS-a Chrome postavili kao zadani preglednik sustava</translation>
<translation id="2515629240566999685">provjerite jaÄinu signala na svom podruÄju</translation>
+<translation id="2515761554693942801">Odabrali ste potvrdu pomoću Touch ID-ja na web-lokacijama koje koriste <ph name="PROVIDER_ORIGIN" />. Taj je davatelj usluga možda pohranio podatke o vaÅ¡em naÄinu plaćanja za koje možete <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Spajanje pri dnu desno</translation>
<translation id="2521736961081452453">Izradite obrazac</translation>
<translation id="2523886232349826891">Sprema se samo na ovom uređaju</translation>
<translation id="2524461107774643265">Dodajte još podataka</translation>
<translation id="2529899080962247600">Broj unosa u ovom polju ne smije biti veći od <ph name="MAX_ITEMS_LIMIT" />. Svi će se daljnji unosi zanemariti.</translation>
+<translation id="253493526287553278">Pogledajte pojedinosti o promotivnom kodu</translation>
<translation id="2535585790302968248">Otvorite novu anonimnu karticu da biste pregledavali privatno</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{i još jedan sudionik}one{i još #}few{i još #}other{i još #}}</translation>
<translation id="2536110899380797252">Dodaj adresu</translation>
@@ -620,7 +627,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="259821504105826686">Fotografska i digitalna umjetnost</translation>
<translation id="2601150049980261779">Ljubavni filmovi</translation>
<translation id="2604589665489080024">Pop glazba</translation>
-<translation id="2609632851001447353">Varijacije</translation>
<translation id="2610561535971892504">Klik za kopiranje</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />neće spremati<ph name="END_EMPHASIS" /> sljedeće podatke:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Rođendani i imendani</translation>
<translation id="2677748264148917807">Napusti</translation>
+<translation id="2679714844901977852">Spremite podatke o kartici i naplati na Google raÄun <ph name="USER_EMAIL" /> za sigurne i brže naplate</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Najbolja veliÄina</translation>
<translation id="2688969097326701645">Da, nastavi</translation>
@@ -701,7 +708,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="2854764410992194509">Davatelji internetskih usluga (ISP-ovi)</translation>
<translation id="2856444702002559011">NapadaÄi možda pokuÅ¡avaju ukrasti vaÅ¡e podatke s web-lokacije <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (na primjer zaporke, poruke ili brojeve kreditnih kartica). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Ova web-lokacija prikazuje ometajuće ili obmanjujuće oglase.</translation>
-<translation id="286512204874376891">Virtualna kartica skriva vašu stvarnu karticu kako bi vas bolje zaštitila od potencijalne prijevare. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Prijateljski</translation>
<translation id="28761159517501904">Filmovi</translation>
<translation id="2876489322757410363">NapuÅ¡tate anonimni naÄin rada da biste platili putem vanjske aplikacije. Želite li nastaviti?</translation>
@@ -802,7 +808,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">Za Wi-Fi koji upotrebljavate možda ćete morati posjetiti stranicu za prijavu.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Otok</translation>
<translation id="3176929007561373547">Provjerite postavke proxyja ili se obratite mrežnom administratoru da
biste provjerili je li proxy poslužitelj u funkciji. Ako mislite da ne
biste trebali upotrebljavati proxy poslužitelj:
@@ -881,9 +886,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="3369192424181595722">Pogreška sata</translation>
<translation id="3369459162151165748">Automobilski dijelovi i oprema</translation>
<translation id="3371064404604898522">Postavite Chrome kao zadani preglednik</translation>
-<translation id="3371076217486966826"><ph name="URL" /> traži dopuštenje za sljedeće:
- • izradu 3D karte vašeg okruženja i praćenje položaja kamere
- • upotrebu kamere</translation>
<translation id="337363190475750230">Dodjela je uklonjena</translation>
<translation id="3375754925484257129">Pokreni Chromeovu sigurnosnu provjeru</translation>
<translation id="3377144306166885718">Poslužitelj je koristio zastarjelu verziju TLS-a.</translation>
@@ -900,6 +902,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="3399952811970034796">Adresa za dostavu</translation>
<translation id="3402261774528610252">Veza za uÄitavanje ove web-lokacije koristila je TLS 1.0 ili TLS 1.1, koji su obustavljeni i onemogućit će se. Nakon onemogućivanja korisnici neće moći uÄitati ovu web-lokaciju. Poslužitelj bi trebao omogućiti TLS 1.2 ili noviju verziju.</translation>
<translation id="3405664148539009465">Prilagodi fontove</translation>
+<translation id="3407789382767355356">prijava treće strane</translation>
<translation id="3409896703495473338">Upravljajte sigurnosnim postavkama</translation>
<translation id="3414952576877147120">VeliÄina:</translation>
<translation id="3417660076059365994">Datoteke koje prenesete ili priložite šalju se Google Cloudu ili trećim stranama na analizu. Na primjer, mogu se pregledavati radi otkrivanja osjetljivih podataka ili zlonamjernog softvera.</translation>
@@ -932,6 +935,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="3477679029130949506">Rasporedi prikazivanja za kina i kazališta</translation>
<translation id="3479552764303398839">Ne sada</translation>
<translation id="3484560055331845446">Mogli biste izgubiti pristup svojem Google raÄunu. Chrome preporuÄuje da odmah promijenite zaporku. Morat ćete se prijaviti.</translation>
+<translation id="3484861421501147767">Podsjetnik: dostupan je spremljeni promotivni kôd</translation>
<translation id="3487845404393360112">Ladica 4</translation>
<translation id="3495081129428749620">Traži na stranici
<ph name="PAGE_TITLE" /></translation>
@@ -1055,6 +1059,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="3810973564298564668">Upravljanje</translation>
<translation id="3816482573645936981">Vrijednost (naslijeđena)</translation>
<translation id="382518646247711829">Ako upotrebljavate proxy poslužitelj...</translation>
+<translation id="3826050100957962900">Prijava za treće strane</translation>
<translation id="3827112369919217609">Apsolutno</translation>
<translation id="3827666161959873541">Obiteljski filmovi</translation>
<translation id="3828924085048779000">Prazne zaporke nisu dopuštene.</translation>
@@ -1067,9 +1072,9 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="3858027520442213535">Ažuriraj datum i vrijeme</translation>
<translation id="3858860766373142691">Naziv</translation>
<translation id="3872834068356954457">Znanost</translation>
+<translation id="3875783148670536197">Pokaži mi kako</translation>
<translation id="3881478300875776315">Prikaži manje redaka</translation>
<translation id="3884278016824448484">Identifikator uređaja sukobljen je</translation>
-<translation id="3885155851504623709">Župa</translation>
<translation id="388632593194507180">Otkriveno je praćenje</translation>
<translation id="3886948180919384617">Spremnik za slaganje u snopove 3</translation>
<translation id="3890664840433101773">Dodajte e-adresu</translation>
@@ -1099,9 +1104,11 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="3973234410852337861">Host <ph name="HOST_NAME" /> je blokiran</translation>
<translation id="3978338123949022456">NaÄin pretraživanja, unesite upit i pritisnite Enter za pretraživanje pomoću sufiksa kljuÄne rijeÄi <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Ptice</translation>
+<translation id="3985750352229496475">Upravljanje adresama...</translation>
<translation id="3986705137476756801">Za sad iskljuÄi automatske titlove</translation>
<translation id="3987940399970879459">Manje od 1 MB</translation>
<translation id="3990250421422698716">Pomak u naÄinu rada Jog</translation>
+<translation id="3992684624889376114">Informacije o ovoj stranici</translation>
<translation id="3996311196211510766">Web-lokacija <ph name="ORIGIN" /> zatražila je da se izvorno pravilo
primjenjuje na sve zahtjeve za nju, no to se pravilo trenutaÄno ne može primijeniti.</translation>
<translation id="4006465311664329701">NaÄini plaćanja, ponude i adrese s Google Paya</translation>
@@ -1227,6 +1234,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="4305666528087210886">Pristup datoteci nije uspio</translation>
<translation id="4306529830550717874">Želite li spremiti adresu?</translation>
<translation id="4306812610847412719">međuspremnik</translation>
+<translation id="4310070645992025887">Pretražite svoja putovanja</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokiraj (zadano)</translation>
<translation id="4314815835985389558">Upravljanje sinkronizacijom</translation>
@@ -1277,6 +1285,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="4435702339979719576">Dopisnica)</translation>
<translation id="443673843213245140">Upotreba proxy poslužitelja onemogućena je, ali odreÄ‘ena je izriÄita konfiguracija proxy poslužitelja.</translation>
<translation id="4441832193888514600">Zanemareno jer se pravilo može postaviti samo kao korisniÄko pravilo oblaka.</translation>
+<translation id="4442470707340296952">Chromeove kartice</translation>
<translation id="4450893287417543264">Ne prikazuj ponovo</translation>
<translation id="4451135742916150903">Može tražiti dopuštenje za povezivanje s HID uređajima</translation>
<translation id="4452328064229197696">Zaporka koju ste upravo upotrijebili otkrivena je u povredi podataka. Radi zaÅ¡tite vaÅ¡ih raÄuna Google upravitelj zaporki preporuÄuje da provjerite svoje spremljene zaporke.</translation>
@@ -1415,6 +1424,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="483241715238664915">UkljuÄite upozorenja</translation>
<translation id="4834250788637067901">NaÄini plaćanja, ponude i adrese s Google Paya</translation>
<translation id="4838327282952368871">Sanjivo</translation>
+<translation id="4839087176073128681">Sljedeći put platite brže i zaštitite karticu pomoću Googleove najnaprednije sigurnosti.</translation>
<translation id="4840250757394056958">Pregledaj povijest na Chromeu</translation>
<translation id="484462545196658690">Automatski</translation>
<translation id="484671803914931257">Ostvarite popust na usluzi <ph name="MERCHANT_NAME" /> i drugim uslugama</translation>
@@ -1477,6 +1487,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="5011561501798487822">Otkriveni jezik</translation>
<translation id="5015510746216210676">Naziv uređaja:</translation>
<translation id="5017554619425969104">Tekst koji ste kopirali</translation>
+<translation id="5017828934289857214">Podsjeti me kasnije</translation>
<translation id="5018422839182700155">Stranica se ne može otvoriti</translation>
<translation id="5019198164206649151">Sigurnosno pohranjivanje u neispravnom je stanju</translation>
<translation id="5020776957610079374">Svjetska glazba</translation>
@@ -1496,6 +1507,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="5051305769747448211">Komedija uživo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Da biste poslali tu datoteku pomoću dijeljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na uređaju}one{Da biste poslali te datoteke pomoću dijeljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na uređaju}few{Da biste poslali te datoteke pomoću dijeljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na uređaju}other{Da biste poslali te datoteke pomoću dijeljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na uređaju}}</translation>
<translation id="5056549851600133418">PreporuÄeni Älanci</translation>
+<translation id="5060483733937416656">Odabrali ste potvrdu putem znaÄajke Windows Hello na web-lokacijama koje koriste <ph name="PROVIDER_ORIGIN" />. Taj je davatelj usluga možda pohranio podatke o vaÅ¡em naÄinu plaćanja za koje možete <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Jeste li mislili <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Povijest ispisa</translation>
<translation id="5068234115460527047">Hedge fondovi</translation>
@@ -1509,10 +1521,8 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="5087286274860437796">Certifikat poslužitelja trenutaÄno nije važeći.</translation>
<translation id="5087580092889165836">Dodaj karticu</translation>
<translation id="5088142053160410913">Poruka operateru</translation>
-<translation id="5089810972385038852">Savezna država</translation>
<translation id="5093232627742069661">Cik-cak presavijanje</translation>
<translation id="5094747076828555589">Poslužitelj nije mogao dokazati da je <ph name="DOMAIN" />; Chromium smatra da njegov sigurnosni certifikat nije pouzdan. To može biti uzrokovano pogrešnom konfiguracijom ili napadom na vašu vezu.</translation>
-<translation id="5095208057601539847">Pokrajina</translation>
<translation id="5097099694988056070">Statistika uređaja kao što je upotreba procesora/RAM-a</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Web-lokacija nije sigurna</translation>
@@ -1550,6 +1560,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="5171045022955879922">Pretražite ili upišite URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Strojno</translation>
+<translation id="5177076414499237632">Saznajte više o izvoru i temi ove stranice</translation>
<translation id="5179510805599951267">Nije <ph name="ORIGINAL_LANGUAGE" /> jezik? Prijavite tu pogrešku</translation>
<translation id="518639307526414276">Hrana za ljubimce i potrepštine za njegu ljubimaca</translation>
<translation id="5190835502935405962">Traka oznaka</translation>
@@ -1710,6 +1721,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="5624120631404540903">Upravljanje zaporkama</translation>
<translation id="5629630648637658800">UÄitavanje postavki pravila nije uspjelo</translation>
<translation id="5631439013527180824">Token za upravljanje uređajem nije važeći</translation>
+<translation id="5632485077360054581">Pokaži mi kako</translation>
<translation id="5633066919399395251">NapadaÄi koji su trenutaÄno na web-lokaciji <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogu pokuÅ¡ati instalirati opasne programe na vaÅ¡e raÄunalo radi kraÄ‘e ili brisanja vaÅ¡ih podataka (na primjer fotografija, zaporki, poruka i brojeva kreditnih kartica). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Blokiran je obmanjujući sadržaj.</translation>
<translation id="5633259641094592098">Kultni i nezavisni filmovi</translation>
@@ -1827,6 +1839,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="5989320800837274978">Nisu određeni fiksni proxy poslužitelji ni URL .pac skripte.</translation>
<translation id="5992691462791905444">Presavijanje u obliku harmonike s dva nabora</translation>
<translation id="5995727681868049093">Upravljajte podacima, privatnošću i sigurnošću na svojem Google raÄunu</translation>
+<translation id="5997247540087773573">Zaporka koju ste upravo upotrijebili otkrivena je u povredi podataka. Radi zaÅ¡tite vaÅ¡ih raÄuna Google upravitelj zaporki preporuÄuje da je odmah promijenite i provjerite svoje spremljene zaporke.</translation>
<translation id="6000758707621254961">Rezultata za upit "<ph name="SEARCH_TEXT" />" ima <ph name="RESULT_COUNT" /></translation>
<translation id="6006484371116297560">KlasiÄna</translation>
<translation id="6008122969617370890">Redoslijed N do 1</translation>
@@ -1922,7 +1935,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="627746635834430766">Da biste sljedeći put platili brže, karticu i adresu za naplatu spremite na svoj Google raÄun.</translation>
<translation id="6279183038361895380">Pritisnite |<ph name="ACCELERATOR" />| da bi se prikazao pokazivaÄ</translation>
<translation id="6280223929691119688">Dostava na tu adresu nije moguća. Odaberite drugu adresu.</translation>
-<translation id="6282194474023008486">Poštanski broj</translation>
<translation id="6285507000506177184">Gumb Upravljaj preuzimanjima u Chromeu, pritisnite Enter da biste upravljali datotekama koje ste preuzeli u Chromeu</translation>
<translation id="6289939620939689042">Boja stranice</translation>
<translation id="6290238015253830360">Ovdje će se prikazivati predloženi Älanci</translation>
@@ -1944,6 +1956,7 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="6337133576188860026">Oslobodit će se <ph name="SIZE" />. Neke bi se web-lokacije pri sljedećem otvaranju mogle sporije uÄitavati.</translation>
<translation id="6337534724793800597">Filtriranje pravila prema nazivu</translation>
<translation id="6340739886198108203">Prema pravilima administratora ne preporuÄuje se izrada snimki zaslona ili snimki kad je vidljiv povjerljiv sadržaj:</translation>
+<translation id="6348220984832452017">Aktivne varijacije</translation>
<translation id="6349101878882523185">Instalirajte aplikaciju <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Nijedna}=1{Jedna zaporka (za <ph name="DOMAIN_LIST" />, sinkronizirano)}=2{Dvije zaporke (za <ph name="DOMAIN_LIST" />, sinkronizirano)}one{# zaporka (za <ph name="DOMAIN_LIST" />, sinkronizirano)}few{# zaporke (za <ph name="DOMAIN_LIST" />, sinkronizirano)}other{# zaporki (za <ph name="DOMAIN_LIST" />, sinkronizirano)}}</translation>
<translation id="6355392890578844978">Preglednikom ne upravlja tvrtka ili neka druga organizacija. Aktivnostima na ovom uređaju možda se upravlja izvan Chromiuma. <ph name="BEGIN_LINK" />Saznajte više<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="643051589346665201">Promijenite zaporku za Google</translation>
<translation id="6433490469411711332">Uređivanje podataka za kontakt</translation>
<translation id="6433595998831338502">Host <ph name="HOST_NAME" /> odbio je povezivanje.</translation>
-<translation id="6438025220577812695">Promijenit ću sam/sama</translation>
<translation id="6440503408713884761">Zanemareno</translation>
<translation id="6443406338865242315">koja ste proširenja i dodatke instalirali</translation>
<translation id="6446163441502663861">Kahu (omotnica)</translation>
@@ -2105,9 +2117,9 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Prevedi</translation>
<translation id="6833752742582340615">Spremite podatke o kartici i naplati na Google raÄun za sigurne i brže naplate</translation>
-<translation id="6839929833149231406">PodruÄje</translation>
<translation id="6846340164947227603">Upotrijebite broj virtualne kartice...</translation>
<translation id="6852204201400771460">Ponovo uÄitati aplikaciju?</translation>
+<translation id="6857776781123259569">Upravljanje zaporkama...</translation>
<translation id="686485648936420384">Izvori informacija za potroÅ¡aÄe</translation>
<translation id="6865412394715372076">TrenutaÄno nije moguće potvrditi karticu</translation>
<translation id="6869334554832814367">Osobni krediti</translation>
@@ -2156,7 +2168,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="6965978654500191972">Uređaj</translation>
<translation id="696703987787944103">Percepcijski</translation>
<translation id="6968269510885595029">Upotrijebite sigurnosni kljuÄ</translation>
-<translation id="6970216967273061347">Distrikt</translation>
<translation id="6971439137020188025">Brzo izradite novu Google prezentaciju u Prezentacijama</translation>
<translation id="6972629891077993081">HID uređaji</translation>
<translation id="6973656660372572881">Određeni su fiksni proxy poslužitelji i URL .pac skripte.</translation>
@@ -2195,7 +2206,6 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<translation id="7081308185095828845">Ta znaÄajka nije dostupna na vaÅ¡em ureÄ‘aju</translation>
<translation id="7083258188081898530">Ladica 9</translation>
<translation id="7086090958708083563">Korisnik je zatražio prijenos</translation>
-<translation id="7087282848513945231">Država</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite Tab, a zatim Enter da biste u postavkama Chromea upravljali dopuštenjima i podacima koji se pohranjuju na web-lokacijama</translation>
<translation id="7096937462164235847">Identitet te web-lokacije nije potvrđen.</translation>
<translation id="7101893872976785596">Filmovi strave</translation>
@@ -2214,10 +2224,10 @@ To će u suprotnom biti onemogućeno na temelju vaših postavki privatnosti. To
<ph name="LIST_ITEM" />podaci uneseni u obrasce.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Dostava na tu adresu nije moguća. Odaberite drugu adresu.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Vaš je administrator zabranio kopiranje tih podataka.</translation>
<translation id="7135130955892390533">Prikaži status</translation>
<translation id="7138472120740807366">NaÄin isporuke</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Primarna ladica</translation>
<translation id="714064300541049402">X-pomak 2. strane slike</translation>
<translation id="7152423860607593928">Number-14 (omotnica)</translation>
@@ -2434,6 +2444,7 @@ Dodatne pojedinosti:
<translation id="7669271284792375604">NapadaÄi na ovoj web-lokaciji mogu vas pokuÅ¡ati navesti na instaliranje programa koji smanjuju kvalitetu pregledavanja interneta (npr. promjenom poÄetne stranice ili prikazivanjem dodatnih oglasa na web-lokacijama koje posjetite).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Radnje poduzete s podacima koji su oznaÄeni kao povjerljivi (jedna radnja od prijave). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}one{Radnje poduzete s podacima koji su oznaÄeni kao povjerljivi (# radnja od prijave). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}few{Radnje poduzete s podacima koji su oznaÄeni kao povjerljivi (# radnje od prijave). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}other{Radnje poduzete s podacima koji su oznaÄeni kao povjerljivi (# radnji od prijave). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Izlazni spremnik s odjeljcima 6</translation>
+<translation id="7675325315208090829">Upravljanje naÄinima plaćanja...</translation>
<translation id="7676643023259824263">Traženje teksta u međuspremniku, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Pregledajte svoju povijest pretraživanja u postavkama Chromea i upravljajte njima</translation>
<translation id="7679947978757153706">Bejzbol</translation>
@@ -2476,7 +2487,6 @@ Dodatne pojedinosti:
<translation id="7766518757692125295">Ivica</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Istim redoslijedom prema gore</translation>
-<translation id="777702478322588152">Prefektura</translation>
<translation id="7791011319128895129">Neobjavljeno</translation>
<translation id="7791196057686275387">Napusti</translation>
<translation id="7791543448312431591">Dodavanje</translation>
@@ -2567,12 +2577,12 @@ Dodatne pojedinosti:
<translation id="8055534648776115597">Strukovno i cjeloživotno obrazovanje</translation>
<translation id="8057711352706143257">Softver "<ph name="SOFTWARE_NAME" />" nije ispravno konfiguriran. Taj se problem obiÄno rjeÅ¡ava deinstaliranjem softvera "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Proizvodnja hrane</translation>
-<translation id="8066955247577885446">Nešto nije u redu.</translation>
<translation id="8067872629359326442">Upravo ste unijeli zaporku na obmanjujućoj web-lokaciji. Chromium može pomoći. Da biste promijenili zaporku i obavijestili Google da je vaÅ¡ raÄun možda ugrožen, kliknite ZaÅ¡titite raÄun.</translation>
<translation id="8070439594494267500">Ikona aplikacije</translation>
<translation id="8074253406171541171">10x13 (omotnica)</translation>
<translation id="8075736640322370409">Brzo izradite novu Google tablicu</translation>
<translation id="8075898834294118863">Upravljajte postavkama web-lokacija</translation>
+<translation id="8076492880354921740">Kartice</translation>
<translation id="8078141288243656252">Ne može dodati bilješku kada je dokument zakrenut</translation>
<translation id="8079031581361219619">Želite li ponovo uÄitati web-lokaciju?</translation>
<translation id="8081087320434522107">Limuzine</translation>
@@ -2695,6 +2705,7 @@ Dodatne pojedinosti:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Postavke</translation>
+<translation id="8428634594422941299">Shvaćam</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite Tab, a zatim Enter da biste u postavkama Chromea upravljali preferencijama za kolaÄiće</translation>
<translation id="8433057134996913067">Time ćete se odjaviti s većine web-lokacija.</translation>
<translation id="8434840396568290395">Kućni ljubimci</translation>
@@ -2793,6 +2804,7 @@ Dodatne pojedinosti:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> je vaš kôd za <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">OznaÄi ovu karticu</translation>
<translation id="8751426954251315517">Pokušajte ponovo drugi put</translation>
+<translation id="8757526089434340176">Dostupna je ponuda Google Paya</translation>
<translation id="8758885506338294482">Natjecateljske videoigre</translation>
<translation id="8759274551635299824">Ta je kartica istekla</translation>
<translation id="87601671197631245">Ova web-lokacija upotrebljava zastarjelu sigurnosnu konfiguraciju, pa bi vaši podaci (na primjer zaporke, poruke ili brojevi kreditnih kartica) mogli biti otkriveni kad se šalju na nju.</translation>
@@ -2800,6 +2812,7 @@ Dodatne pojedinosti:
<translation id="8763927697961133303">USB uređaj</translation>
<translation id="8763986294015493060">Zatvorite sve anonimne prozore koji su trenutaÄno otvoreni</translation>
<translation id="8766943070169463815">Otvoren je list s autentifikacijom za vjerodajnicu sigurnog plaćanja</translation>
+<translation id="8767765348545497220">Zatvori oblaÄić za pomoć</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocikli</translation>
<translation id="8790007591277257123">&amp;Ponovi brisanje</translation>
@@ -2812,6 +2825,7 @@ Dodatne pojedinosti:
<translation id="8806285662264631610">Proizvodi za kupanje i tijelo</translation>
<translation id="8807160976559152894">Obreži nakon svake stranice</translation>
<translation id="8808828119384186784">Postavke preglednika Chrome</translation>
+<translation id="8813277370772331957">Podsjeti me kasnije</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite Tab, a zatim Enter da biste ažurirali Chrome iz postavki Chromea</translation>
<translation id="8820817407110198400">Knjižne oznake</translation>
<translation id="882338992931677877">Utor za ruÄno umetanje</translation>
@@ -2992,6 +3006,7 @@ i netoÄne vjerodajnice. To može znaÄiti da se neki napadaÄ pokuÅ¡ava predsta
<translation id="988159990683914416">Sastavak razvojnog programera</translation>
<translation id="989988560359834682">Uređivanje adrese</translation>
<translation id="991413375315957741">senzori kretanja ili osvjetljenja</translation>
+<translation id="992110854164447044">Virtualna kartica skriva vašu stvarnu karticu kako bi vas bolje zaštitila od potencijalne prijevare. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">RužiÄasta</translation>
<translation id="992432478773561401">Softver "<ph name="SOFTWARE_NAME" />" nije ispravno instaliran na vaÅ¡em raÄunalu ili mreži:
diff --git a/chromium/components/strings/components_strings_hu.xtb b/chromium/components/strings/components_strings_hu.xtb
index f1ee28ddbb5..0a33718853c 100644
--- a/chromium/components/strings/components_strings_hu.xtb
+++ b/chromium/components/strings/components_strings_hu.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Ösztöndíjak és pénzügyi segítség</translation>
<translation id="1048785276086539861">A jelölések szerkesztésekor a dokumentum visszaáll egyoldalas nézetre.</translation>
<translation id="1050038467049342496">Zárja be a többi alkalmazást</translation>
+<translation id="1053959602163383901">Úgy döntött, hogy hitelesítő eszköz használatával igazolja magát a következőt használó webhelyeken: <ph name="PROVIDER_ORIGIN" />. Ez a szolgáltató eltárolhatott információkat az Ön fizetési módjáról. Ön <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Hozzáadás visszavonása</translation>
<translation id="1056663316309890343">Képszerkesztő szoftverek</translation>
<translation id="1056898198331236512">Figyelmeztetés</translation>
@@ -119,6 +120,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="1270502636509132238">Ãtvételi mód</translation>
<translation id="1281476433249504884">1. kötegelő</translation>
<translation id="1285320974508926690">Ezt a webhelyet soha ne fordítsa le</translation>
+<translation id="1288548991597756084">Kártya biztonságos mentése</translation>
<translation id="1292571435393770077">16. tálca</translation>
<translation id="1292701964462482250">„A számítógépen található valamelyik szoftver megakadályozza a Chrome-ot abban, hogy biztonságosan csatlakozzon az internetre†(csak Windows rendszerű számítógépeken)</translation>
<translation id="1294154142200295408">Parancssorváltozatok</translation>
@@ -223,6 +225,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
&lt;p&gt;A hiba kijavításához kattintson a &lt;strong&gt;Csatlakozás&lt;/strong&gt; lehetőségre a megnyitni próbált oldalon.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Tájépítészet</translation>
<translation id="1513706915089223971">Előzménybejegyzések listája</translation>
+<translation id="1516097932025103760">Titkosítja, biztonságos helyre menti, a CVC-t pedig nem tárolja a rendszer.</translation>
<translation id="1517433312004943670">Telefonszám szükséges</translation>
<translation id="1519264250979466059">Build dátuma</translation>
<translation id="1521159554480556801">Szövet- és textilművészet</translation>
@@ -288,6 +291,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="1662550410081243962">Fizetési módok mentése és kitöltése</translation>
<translation id="1663943134801823270">A kártyák és a címek a Chrome-ból származnak. A <ph name="BEGIN_LINK" />Beállításokban<ph name="END_LINK" /> kezelheti őket.</translation>
<translation id="1671391448414634642">A(z) <ph name="SOURCE_LANGUAGE" /> nyelvű oldalak mostantól le lesznek fordítva <ph name="TARGET_LANGUAGE" /> nyelvre.</translation>
+<translation id="1673886523110456987">Az ajánlat felhasználásához fizessen következő kártyával: <ph name="CARD_DETAIL" /></translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> nyelvről <ph name="TARGET_LANGUAGE" /> nyelvre</translation>
<translation id="1682696192498422849">Rövid él először</translation>
<translation id="168693727862418163">A házirend értéke nem egyezik a sémájával, ezért a rendszer figyelmen kívül hagyja.</translation>
@@ -306,6 +310,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="1717218214683051432">Mozgásérzékelők</translation>
<translation id="1717494416764505390">3. postaláda</translation>
<translation id="1718029547804390981">A dokumentum túl nagy a jegyzeteléshez</translation>
+<translation id="1720941539803966190">Útmutató bezárása</translation>
<translation id="1721424275792716183">* A mező kitöltése kötelező</translation>
<translation id="1727613060316725209">A tanúsítvány érvényes</translation>
<translation id="1727741090716970331">Érvényes kártyaszámot adjon meg</translation>
@@ -422,8 +427,8 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="205212645995975601">Barbecue és grillezés</translation>
<translation id="2053111141626950936">A(z) <ph name="LANGUAGE" /> nyelvű oldalak nem lesznek lefordítva.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Ha ez a vezérlő be van kapcsolva, és az állapot aktív, a Chrome meghatározza, hogy az Ön legutóbbi böngészési tevékenysége melyik nagy társadalmi csoporthoz, azaz „kohorszhoz†hasonlít leginkább. A hirdetők kiválaszthatják a hirdetéseket a csoporthoz, ám a böngészési előzményeit privát módon tárolja a rendszer az eszközén. Csoportja minden nap frissül.}=1{Ha ez a vezérlő be van kapcsolva, és az állapot aktív, a Chrome meghatározza, hogy az Ön legutóbbi böngészési tevékenysége melyik nagy társadalmi csoporthoz, azaz „kohorszhoz†hasonlít leginkább. A hirdetők kiválaszthatják a hirdetéseket a csoporthoz, ám a böngészési előzményeit privát módon tárolja a rendszer az eszközén. Csoportja minden nap frissül.}other{Ha ez a vezérlő be van kapcsolva, és az állapot aktív, a Chrome meghatározza, hogy az Ön legutóbbi böngészési tevékenysége melyik nagy társadalmi csoporthoz, azaz „kohorszhoz†hasonlít leginkább. A hirdetők kiválaszthatják a hirdetéseket a csoporthoz, ám a böngészési előzményeit privát módon tárolja a rendszer az eszközén. Csoportja {NUM_DAYS} naponta frissül.}}</translation>
-<translation id="2053553514270667976">Irányítószám</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 javaslat}other{# javaslat}}</translation>
+<translation id="2066915425250589881">kérelmezheti az adatok törlését</translation>
<translation id="2068528718802935086">Csecsemők és kisgyermekek</translation>
<translation id="2071156619270205202">Ez a kártya nem használható virtuális kártyaszámmal.</translation>
<translation id="2071692954027939183">A rendszer automatikusan letiltotta az értesítéseket, mert Ön általában nem engedélyezi őket.</translation>
@@ -435,7 +440,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="2088086323192747268">Szinkronizálás kezelése gomb. Nyomja le az Enter billentyűt az adatok szinkronizálásának Chrome-beállítások közötti kezeléséhez.</translation>
<translation id="2091887806945687916">Hang</translation>
<translation id="2094505752054353250">Domainkeveredés</translation>
-<translation id="2096368010154057602">Megye</translation>
<translation id="2099652385553570808">Három kapocs a bal oldalon</translation>
<translation id="2101225219012730419">Verzió:</translation>
<translation id="2102134110707549001">Erős jelszó ajánlása…</translation>
@@ -472,7 +476,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="2185836064961771414">Amerikai futball</translation>
<translation id="2187317261103489799">Észlelés (alapértelmezett)</translation>
<translation id="2188375229972301266">Több lyuk alul</translation>
-<translation id="2188852899391513400">Kiderült, hogy a most használt jelszava adatvédelmi incidensben volt érintett. Fiókjai biztonságának megőrzése érdekében a Google Jelszókezelő a jelszó haladéktalan módosítását, majd a mentett jelszavak ellenőrzését javasolja.</translation>
<translation id="219906046732893612">Lakásfelújítás</translation>
<translation id="2202020181578195191">Érvényes lejárati évet kell megadnia</translation>
<translation id="22081806969704220">3. tálca</translation>
@@ -483,6 +486,8 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="2215727959747642672">Fájlszerkesztés</translation>
<translation id="2215963164070968490">Kutyák</translation>
<translation id="2218879909401188352">A(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhelyen lévő támadók veszélyes alkalmazásokat telepíthetnek, amelyek károsíthatják eszközét, rejtett költségeket okozhatnak a mobiltelefon-számlán, vagy ellophatják személyes adatait. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2219658597883514593">Útmutató újraindítása</translation>
+<translation id="2219735899272417925">Az eszköz visszaállítására van szükség</translation>
<translation id="2224337661447660594">Nincs internet</translation>
<translation id="2230458221926704099">Javítsa meg kapcsolatát a <ph name="BEGIN_LINK" />diagnosztikai alkalmazás<ph name="END_LINK" /> segítségével</translation>
<translation id="2239100178324503013">Küldés most</translation>
@@ -580,11 +585,13 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="2512101340618156538">Nem engedélyezett (alapértelmezett)</translation>
<translation id="2512413427717747692">A Chrome beállítása alapértelmezett böngészőként gomb. Nyomja le az Entert, ha a Chrome-ot szeretné beállítani a rendszer alapértelmezett böngészőjeként az iOS beállításaiban</translation>
<translation id="2515629240566999685">A térerő ellenőrzése tartózkodási helyén</translation>
+<translation id="2515761554693942801">Úgy döntött, hogy a Touch ID használatával igazolja magát a következőt használó webhelyeken: <ph name="PROVIDER_ORIGIN" />. Ez a szolgáltató eltárolhatott információkat az Ön fizetési módjáról. Ön <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Kapocs jobbra lent</translation>
<translation id="2521736961081452453">Űrlap létrehozása</translation>
<translation id="2523886232349826891">Csak erre az eszközre mentve</translation>
<translation id="2524461107774643265">További adatok hozzáadása</translation>
<translation id="2529899080962247600">Ebben a mezőben legfeljebb <ph name="MAX_ITEMS_LIMIT" /> bejegyzés szerepelhet. A további bejegyzéseket figyelmen kívül hagyja a rendszer.</translation>
+<translation id="253493526287553278">A promóciós kód részleteinek megtekintése</translation>
<translation id="2535585790302968248">Privát böngészéshez nyisson új inkognitó lapot</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{és 1 további}other{és # további}}</translation>
<translation id="2536110899380797252">Cím hozzáadása</translation>
@@ -620,7 +627,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="259821504105826686">Fényképezés és digitális művészetek</translation>
<translation id="2601150049980261779">Romantikus filmek</translation>
<translation id="2604589665489080024">Popzene</translation>
-<translation id="2609632851001447353">Változatok</translation>
<translation id="2610561535971892504">Kattintson a másoláshoz</translation>
<translation id="2617988307566202237">A Chrome <ph name="BEGIN_EMPHASIS" />nem menti<ph name="END_EMPHASIS" /> a következő adatokat:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Születésnapok és névnapok</translation>
<translation id="2677748264148917807">Lap elhagyása</translation>
+<translation id="2679714844901977852">Kártya- és számlázási adatait Google-fiókjába (<ph name="USER_EMAIL" />) mentve biztonságosan és gyorsabban fizethet</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Legjobb illeszkedés</translation>
<translation id="2688969097326701645">Igen, folytatás</translation>
@@ -701,7 +708,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="2854764410992194509">Internetszolgáltatók</translation>
<translation id="2856444702002559011">A támadók megpróbálhatják ellopni a(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhelyen lévő adatait (például jelszavait, üzeneteit és hitelkártyaadatait). <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2859806420264540918">Ez a webhely tolakodó vagy félrevezető hirdetéseket jelenít meg.</translation>
-<translation id="286512204874376891">A virtuális kártya álcázza a tényleges kártyáját, így segíthet az esetleges csalások elleni védekezésben. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Barátságos</translation>
<translation id="28761159517501904">Filmek</translation>
<translation id="2876489322757410363">Az inkognitó mód elhagyása külső alkalmazással történő fizetéshez. Folytatja?</translation>
@@ -802,7 +808,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="3158539265159265653">Lemez</translation>
<translation id="3162559335345991374">Az Ön által használt Wi-Fi-hálózat megkövetelheti bejelentkezési oldalának felkeresését.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Sziget</translation>
<translation id="3176929007561373547">Ellenőrizze a proxybeállításokat, vagy kérdezze meg a rendszergazdájától, hogy a proxyszerver működik-e. Ha úgy gondolja, hogy nem használ proxyszervert:
<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">megtudni, hogy Ön mikor használja aktívan ezt az eszközt</translation>
@@ -879,9 +884,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="3369192424181595722">Órahiba</translation>
<translation id="3369459162151165748">Járműalkatrészek és kiegészítők</translation>
<translation id="3371064404604898522">A Chrome beállítása alapértelmezett böngészőként</translation>
-<translation id="3371076217486966826">A(z) <ph name="URL" /> a következőkre kér engedélyt:
- • 3D-s térkép létrehozása az Ön környezetéről és a kamera pozíciójának követése
- • A kamera használata</translation>
<translation id="337363190475750230">Deaktiválva</translation>
<translation id="3375754925484257129">A Chrome biztonsági ellenőrzésének futtatása</translation>
<translation id="3377144306166885718">A szerver elavult TLS-verziót használt.</translation>
@@ -898,6 +900,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="3399952811970034796">Szállítási cím</translation>
<translation id="3402261774528610252">A webhely betöltésére használt kapcsolat a TLS 1.0 vagy a TLS 1.1 protokollt használta, amelyek elavultak, és a jövőben le lesznek tiltva. A letiltásuk után a felhasználók nem fogják tudni betölteni ezt a webhelyet. A szerveren a TLS 1.2-es vagy újabb verzióját kell használni.</translation>
<translation id="3405664148539009465">Betűtípusok testreszabása</translation>
+<translation id="3407789382767355356">a harmadik fél szolgáltatásokkal való bejelentkezés</translation>
<translation id="3409896703495473338">Biztonsági beállítások kezelése</translation>
<translation id="3414952576877147120">Méret:</translation>
<translation id="3417660076059365994">A böngésző elemzés céljából a Google Cloudnak vagy harmadik feleknek továbbítja a feltöltött és a csatolt fájlokat. A Google Cloud vagy a harmadik fél például bizalmas adatokat vagy rosszindulatú programokat kereshet a fájlokban.</translation>
@@ -930,6 +933,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="3477679029130949506">Mozi- és színházműsorok</translation>
<translation id="3479552764303398839">Ne most</translation>
<translation id="3484560055331845446">Elveszítheti a hozzáférést Google-fiókjához. A Chrome azt javasolja, hogy azonnal módosítsa jelszavát. Be kell majd jelentkeznie.</translation>
+<translation id="3484861421501147767">Emlékeztető: Mentett promóciós kódot használhat fel</translation>
<translation id="3487845404393360112">4. tálca</translation>
<translation id="3495081129428749620">Keresés a következő oldalon:
<ph name="PAGE_TITLE" /></translation>
@@ -1054,6 +1058,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="3810973564298564668">Kezelés</translation>
<translation id="3816482573645936981">Érték (felülírva)</translation>
<translation id="382518646247711829">Ha proxyszervert használ...</translation>
+<translation id="3826050100957962900">Harmadik fél bejelentkezése</translation>
<translation id="3827112369919217609">Abszolút</translation>
<translation id="3827666161959873541">Családi filmek</translation>
<translation id="3828924085048779000">Az üres összetett jelszó nem engedélyezett.</translation>
@@ -1066,9 +1071,9 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="3858027520442213535">Dátum és idő frissítése</translation>
<translation id="3858860766373142691">Név</translation>
<translation id="3872834068356954457">Természettudományok</translation>
+<translation id="3875783148670536197">Bemutató</translation>
<translation id="3881478300875776315">Kevesebb sor megjelenítése</translation>
<translation id="3884278016824448484">Eszközazonosító-ütközés</translation>
-<translation id="3885155851504623709">Körzet</translation>
<translation id="388632593194507180">Figyelés észlelve</translation>
<translation id="3886948180919384617">3. kötegelő</translation>
<translation id="3890664840433101773">E-mail-cím hozzáadása</translation>
@@ -1098,9 +1103,11 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="3973234410852337861">A(z) <ph name="HOST_NAME" /> le van tiltva</translation>
<translation id="3978338123949022456">Keresési mód. Ãrja be a lekérdezést, majd nyomja le az Entert a következÅ‘vel való kereséshez: <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Madarak</translation>
+<translation id="3985750352229496475">Címek kezelése…</translation>
<translation id="3986705137476756801">Élő feliratozás átmeneti kikapcsolása</translation>
<translation id="3987940399970879459">Kevesebb mint 1 MB</translation>
<translation id="3990250421422698716">Példányok eltolása</translation>
+<translation id="3992684624889376114">Információ az oldalról</translation>
<translation id="3996311196211510766">A(z) <ph name="ORIGIN" /> webhely egy adott eredetházirend alkalmazását kérte
az összes kérésére, de ez a házirend jelenleg nem alkalmazható.</translation>
<translation id="4006465311664329701">A Google Pay szolgáltatásban használt fizetési módok, ajánlatok és címek</translation>
@@ -1225,6 +1232,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="4305666528087210886">A fájlhoz nem sikerült hozzáférni</translation>
<translation id="4306529830550717874">Menti a címet?</translation>
<translation id="4306812610847412719">vágólap</translation>
+<translation id="4310070645992025887">Utazások keresése</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Letiltás (alapértelmezett)</translation>
<translation id="4314815835985389558">Szinkronizálás kezelése</translation>
@@ -1275,6 +1283,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="4435702339979719576">képeslap)</translation>
<translation id="443673843213245140">A proxy használata le van tiltva, de kifejezett proxykonfiguráció van megadva.</translation>
<translation id="4441832193888514600">Figyelmen kívül hagyva, mert a szabályzatot csak felhőalapú felhasználói szabályzatként lehet beállítani.</translation>
+<translation id="4442470707340296952">Chrome-lapok</translation>
<translation id="4450893287417543264">Ne jelenjen meg többé</translation>
<translation id="4451135742916150903">Engedélyt kérhet a HID-eszközökhöz való csatlakozásra</translation>
<translation id="4452328064229197696">Kiderült, hogy a most használt jelszava adatvédelmi incidensben volt érintett. Fiókjai biztonságának megőrzése érdekében a Google Jelszókezelő a mentett jelszavak ellenőrzését javasolja.</translation>
@@ -1413,6 +1422,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="483241715238664915">Figyelmeztetések bekapcsolása</translation>
<translation id="4834250788637067901">A Google Pay szolgáltatásban használt fizetési módok, ajánlatok és címek</translation>
<translation id="4838327282952368871">Ãlmodozó</translation>
+<translation id="4839087176073128681">Fizessen gyorsabban legközelebb, és védje meg kártyáját a Google iparági szinten vezető biztonsági megoldásával.</translation>
<translation id="4840250757394056958">Chrome-előzmények megtekintése</translation>
<translation id="484462545196658690">Automatikus</translation>
<translation id="484671803914931257">Kedvezmények <ph name="MERCHANT_NAME" /> keresekedőnél és másoknál</translation>
@@ -1475,6 +1485,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="5011561501798487822">Észlelt nyelv</translation>
<translation id="5015510746216210676">Számítógépnév:</translation>
<translation id="5017554619425969104">Vágólapra másolt szöveg</translation>
+<translation id="5017828934289857214">Emlékeztessen később</translation>
<translation id="5018422839182700155">Nem lehet megnyitni az oldalt</translation>
<translation id="5019198164206649151">A háttértároló állapota nem megfelelő</translation>
<translation id="5020776957610079374">Világzene</translation>
@@ -1494,6 +1505,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="5051305769747448211">Élő kabaré</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Ha a Közeli megosztás funkcióval szeretné elküldeni a fájlt, szabadítson fel legalább <ph name="DISK_SPACE_SIZE" /> tárhelyet eszközén}other{Ha a Közeli megosztás funkcióval szeretné elküldeni a fájlokat, szabadítson fel legalább <ph name="DISK_SPACE_SIZE" /> tárhelyet eszközén}}</translation>
<translation id="5056549851600133418">Cikkek Önnek</translation>
+<translation id="5060483733937416656">Úgy döntött, hogy a Windows Hello használatával igazolja magát a következőt használó webhelyeken: <ph name="PROVIDER_ORIGIN" />. Ez a szolgáltató eltárolhatott információkat az Ön fizetési módjáról. Ön <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Erre gondolt: <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Nyomtatási előzmények</translation>
<translation id="5068234115460527047">Hedge Fund befektetési alapok</translation>
@@ -1507,10 +1519,8 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="5087286274860437796">A szerver tanúsítványa jelenleg nem érvényes.</translation>
<translation id="5087580092889165836">Kártya hozzáadása</translation>
<translation id="5088142053160410913">Üzenet az operátornak</translation>
-<translation id="5089810972385038852">Ãllam</translation>
<translation id="5093232627742069661">Z-hajtás</translation>
<translation id="5094747076828555589">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványa a Chromium szerint nem megbízható. Ennek oka lehet konfigurációs hiba, vagy hogy egy támadó eltérítette az Ön kapcsolódását.</translation>
-<translation id="5095208057601539847">Tartomány</translation>
<translation id="5097099694988056070">Eszközstatisztikák, például processzor- és memóriahasználat</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">A webhely nem biztonságos</translation>
@@ -1548,6 +1558,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="5171045022955879922">Keressen vagy írjon be egy URL-t</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Számítógép</translation>
+<translation id="5177076414499237632">További információ az oldal forrásáról és témájáról</translation>
<translation id="5179510805599951267">Nem <ph name="ORIGINAL_LANGUAGE" /> nyelven van? Hiba bejelentése</translation>
<translation id="518639307526414276">Kisállateledel és kisállatgondozási kellékek</translation>
<translation id="5190835502935405962">Könyvjelzősáv</translation>
@@ -1708,6 +1719,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="5624120631404540903">Jelszavak kezelése</translation>
<translation id="5629630648637658800">Az irányelv-beállítások betöltése sikertelen</translation>
<translation id="5631439013527180824">Érvénytelen eszközkezelési token</translation>
+<translation id="5632485077360054581">Bemutató</translation>
<translation id="5633066919399395251">Előfordulhat, hogy a támadók a(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhelyen veszélyes programokat kísérelnek meg telepíteni számítógépére, amelyek ellopják vagy törlik az Ön adatait (például fotóit, jelszavait, üzeneteit és bankkártyájának adatait). <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="563324245173044180">Megtévesztő tartalom letiltva.</translation>
<translation id="5633259641094592098">Kult- és független filmek</translation>
@@ -1825,6 +1837,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="5989320800837274978">Sem fix proxyszerver, sem pedig .pac típusú szkript URL-címe nincs megadva.</translation>
<translation id="5992691462791905444">Z-hajtás hellyel kötés, lyukasztás és kapcsok számára</translation>
<translation id="5995727681868049093">Kezelheti adatait, az adatvédelmet és a biztonságot a Google-fiókjában</translation>
+<translation id="5997247540087773573">Kiderült, hogy a most használt jelszava adatvédelmi incidensben volt érintett. Fiókjai biztonságának megőrzése érdekében a Google Jelszókezelő a jelszó haladéktalan módosítását, majd a mentett jelszavak ellenőrzését javasolja.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> találat a következőre: <ph name="SEARCH_TEXT" /></translation>
<translation id="6006484371116297560">Klasszikus</translation>
<translation id="6008122969617370890">„N az 1-hez†sorrend</translation>
@@ -1920,7 +1933,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="627746635834430766">A következő alkalommal gyorsabban fizethet, ha Google-fiókjába menti kártyáját és számlázási címét.</translation>
<translation id="6279183038361895380">Az egérmutató megjelenítéséhez nyomja meg a következő billentyűt: |<ph name="ACCELERATOR" />|</translation>
<translation id="6280223929691119688">Erre a címre nem lehetséges a kézbesítés. Válasszon másik címet.</translation>
-<translation id="6282194474023008486">Irányítószám</translation>
<translation id="6285507000506177184">Letöltések kezelése a Chrome-ban gomb. Nyomja le az Enter billentyűt a Chrome-ban letöltött fájlok kezeléséhez.</translation>
<translation id="6289939620939689042">Oldalszín</translation>
<translation id="6290238015253830360">A javasolt cikkek helye</translation>
@@ -1942,6 +1954,7 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="6337133576188860026"><ph name="SIZE" />-nál kevesebb hely szabadul fel. Előfordulhat, hogy egyes webhelyek lassabban töltődnek be, amikor legközelebb felkeresi őket.</translation>
<translation id="6337534724793800597">Házirendek szűrése név szerint</translation>
<translation id="6340739886198108203">A rendszergazda által beállított házirend nem javasolja képernyőképek vagy felvételek készítését, amikor a következő bizalmas tartalmak láthatók:</translation>
+<translation id="6348220984832452017">Aktív változatok</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> telepítése</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Nincs}=1{1 jelszó (a következő domainekhez: <ph name="DOMAIN_LIST" />, szinkronizálva)}=2{2 jelszó (a következő domainekhez: <ph name="DOMAIN_LIST" />, szinkronizálva)}other{# jelszó (a következő domainekhez: <ph name="DOMAIN_LIST" />, szinkronizálva)}}</translation>
<translation id="6355392890578844978">Ezt a böngészőt nem kezeli cég vagy más szervezet. Lehetséges, hogy az eszközön végzett tevékenységeket a Chromiumon kívülről felügyelik. <ph name="BEGIN_LINK" />További információ<ph name="END_LINK" /></translation>
@@ -1973,7 +1986,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="643051589346665201">Google-jelszó módosítása</translation>
<translation id="6433490469411711332">Kapcsolattartási adatok szerkesztése</translation>
<translation id="6433595998831338502">A(z) <ph name="HOST_NAME" /> visszautasította a csatlakozást.</translation>
-<translation id="6438025220577812695">Saját magam módosítom</translation>
<translation id="6440503408713884761">Figyelmen kívül hagyva</translation>
<translation id="6443406338865242315">Ön milyen bővítményeket és beépülő modulokat telepített.</translation>
<translation id="6446163441502663861">Kahu (boríték)</translation>
@@ -2103,9 +2115,9 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Fordítás</translation>
<translation id="6833752742582340615">Kártya- és számlázási adatait Google-fiókjába mentve biztonságosan és gyorsabban fizethet</translation>
-<translation id="6839929833149231406">Terület</translation>
<translation id="6846340164947227603">Virtuális kártyaszám használata…</translation>
<translation id="6852204201400771460">Újratölti az alkalmazást?</translation>
+<translation id="6857776781123259569">Jelszavak kezelése…</translation>
<translation id="686485648936420384">Fogyasztói segédletek</translation>
<translation id="6865412394715372076">Ez a kártya jelenleg nem ellenőrizhető</translation>
<translation id="6869334554832814367">Személyi kölcsönök</translation>
@@ -2154,7 +2166,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="6965978654500191972">Készülék</translation>
<translation id="696703987787944103">Ãllandó</translation>
<translation id="6968269510885595029">Biztonsági hardverkulcs használata</translation>
-<translation id="6970216967273061347">Körzet</translation>
<translation id="6971439137020188025">Új Google-prezentáció gyors létrehozása a Diák szolgáltatással</translation>
<translation id="6972629891077993081">HID-eszközök</translation>
<translation id="6973656660372572881">Mindkét fix proxyszerver és egy .Pac típusú szkript URL-címe meg van adva.</translation>
@@ -2193,7 +2204,6 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<translation id="7081308185095828845">Ez a funkció nem áll rendelkezésre az eszközén</translation>
<translation id="7083258188081898530">9. tálca</translation>
<translation id="7086090958708083563">A felhasználó kérte a feltöltést</translation>
-<translation id="7087282848513945231">Megye</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. Nyomja le a Tab, majd az Enter billentyűt a webhelyek engedélyeinek és tárolt adatainak Chrome-beállítások közötti kezeléséhez.</translation>
<translation id="7096937462164235847">Igazolatlan azonosságú webhely.</translation>
<translation id="7101893872976785596">Horrorfilmek</translation>
@@ -2212,10 +2222,10 @@ Ezt egyéb esetben letiltják az Ön adatvédelmi beállításai. Az engedélyez
<ph name="LIST_ITEM" />űrlapokon megadott információk.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Erre a címre nem lehetséges a szállítás. Válasszon másik címet.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">A rendszergazda nem engedélyezi az adatok másolását.</translation>
<translation id="7135130955892390533">Ãllapot megjelenítése</translation>
<translation id="7138472120740807366">Kézbesítési mód</translation>
-<translation id="7139724024395191329">Emírség</translation>
<translation id="7139892792842608322">Elsődleges tálca</translation>
<translation id="714064300541049402">2. oldali kép X-eltolása</translation>
<translation id="7152423860607593928">Number-14 (boríték)</translation>
@@ -2432,6 +2442,7 @@ További részletek:
<translation id="7669271284792375604">A webhelyen lévő támadók megpróbálhatják csellel rávenni Önt olyan programok telepítésére, amelyek károsak a böngészési élmény szempontjából (például módosítják a kezdőlapot, vagy plusz hirdetéseket jelenítenek meg a felkeresett webhelyeken).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Bizalmasként megjelölt adatokkal végzett műveletek (1 művelet a bejelentkezés óta). <ph name="BEGIN_LINK" />További információ<ph name="END_LINK" />.}other{Bizalmasként megjelölt adatokkal végzett műveletek (# művelet a bejelentkezés óta). <ph name="BEGIN_LINK" />További információ<ph name="END_LINK" />.}}</translation>
<translation id="7673278391011283842">6. postaláda</translation>
+<translation id="7675325315208090829">Fizetési módok kezelése…</translation>
<translation id="7676643023259824263">Vágólapon lévő szöveg keresése, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Megtekintheti és kezelheti böngészési előzményeit a Chrome beállításaiban.</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2474,7 +2485,6 @@ További részletek:
<translation id="7766518757692125295">Szoknya</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Azonos sorrend, felfelé fordítva</translation>
-<translation id="777702478322588152">Prefektúra</translation>
<translation id="7791011319128895129">Kiadatlan</translation>
<translation id="7791196057686275387">Köteg</translation>
<translation id="7791543448312431591">Hozzáadás</translation>
@@ -2565,12 +2575,12 @@ További részletek:
<translation id="8055534648776115597">Szakképzés és folyamatos képzés</translation>
<translation id="8057711352706143257">A(z) „<ph name="SOFTWARE_NAME" />†nincs megfelelően beállítva. A(z) „<ph name="SOFTWARE_NAME" />†eltávolítása általában megoldja a problémát. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Élelmiszergyártás</translation>
-<translation id="8066955247577885446">Sajnos hiba történt.</translation>
<translation id="8067872629359326442">Megtévesztő webhelyen adta meg jelszavát. A Chromium segíthet. Ha módosítani szeretné jelszavát, és értesíteni szeretné a Google-t arról, hogy fiókja veszélyben lehet, kattintson a Fiók védelme gombra.</translation>
<translation id="8070439594494267500">Alkalmazásikon</translation>
<translation id="8074253406171541171">10x13 (boríték)</translation>
<translation id="8075736640322370409">Új Google-táblázat létrehozása gyorsan</translation>
<translation id="8075898834294118863">Webhelybeállítások kezelése</translation>
+<translation id="8076492880354921740">Lapok</translation>
<translation id="8078141288243656252">Elforgatott állapotban nem lehetséges a jegyzetelés</translation>
<translation id="8079031581361219619">Újratölti a webhelyet?</translation>
<translation id="8081087320434522107">Szedánok</translation>
@@ -2693,6 +2703,7 @@ További részletek:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Beállítások</translation>
+<translation id="8428634594422941299">Értem</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. Nyomja le a Tab, majd az Enter billentyűt a cookie-beállítások Chrome-beállítások közötti kezeléséhez.</translation>
<translation id="8433057134996913067">Ezzel kijelentkezik a legtöbb webhelyről.</translation>
<translation id="8434840396568290395">Házi kedvencek</translation>
@@ -2791,6 +2802,7 @@ További részletek:
<translation id="8742371904523228557">A(z) <ph name="ORIGIN" /> webhelyhez tartozó kód a következő: <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">Lap hozzáadása a könyvjelzőkhöz</translation>
<translation id="8751426954251315517">Próbálja újra később</translation>
+<translation id="8757526089434340176">Van ajánlat a Google Paytől</translation>
<translation id="8758885506338294482">Videójáték-versenyzés</translation>
<translation id="8759274551635299824">A kártya lejárt</translation>
<translation id="87601671197631245">Ez a webhely elavult biztonsági konfigurációt használ, ezért előfordulhat, hogy mások látják az Ön adatait (például jelszavait, üzeneteit vagy hitelkártyaszámait), amikor elküldi őket a webhelynek.</translation>
@@ -2798,6 +2810,7 @@ További részletek:
<translation id="8763927697961133303">USB-eszköz</translation>
<translation id="8763986294015493060">Az összes jelenleg megnyitott inkognitó ablak bezárása</translation>
<translation id="8766943070169463815">Biztonságos fizetés hitelesítési adatainak hitelesítési űrlapja megnyitva.</translation>
+<translation id="8767765348545497220">Súgóbuborék bezárása</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorkerékpárok</translation>
<translation id="8790007591277257123">&amp;Törlés újra</translation>
@@ -2810,11 +2823,12 @@ További részletek:
<translation id="8806285662264631610">Fürdő- és testápolási termékek</translation>
<translation id="8807160976559152894">Vágás minden oldal után</translation>
<translation id="8808828119384186784">A Chrome beállításai</translation>
+<translation id="8813277370772331957">Emlékeztessen később</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, nyomja le a Tab, majd az Enter billentyűt a Chrome-nak a Chrome beállításaiban való frissítéséhez</translation>
<translation id="8820817407110198400">Könyvjelzők</translation>
<translation id="882338992931677877">Manuális nyílás</translation>
<translation id="8834380158646307944">Inkognitó ablakok bezárása gomb. Nyomja le az Enter billentyűt a jelenleg megnyitott összes inkognitó ablak bezárásához.</translation>
-<translation id="883848425547221593">Egyéb könyvjelzők</translation>
+<translation id="883848425547221593">További könyvjelzők</translation>
<translation id="884264119367021077">Szállítási cím</translation>
<translation id="884923133447025588">Nem található visszavonási mechanizmus.</translation>
<translation id="8849262850971482943">Használja virtuális kártyáját a plusz biztonság érdekében</translation>
@@ -2989,6 +3003,7 @@ További részletek:
<translation id="988159990683914416">Fejlesztői változat</translation>
<translation id="989988560359834682">Cím szerkesztése</translation>
<translation id="991413375315957741">mozgás- és fényérzékelők</translation>
+<translation id="992110854164447044">A virtuális kártya elrejti tényleges kártyáját, így segíthet az esetleges csalások elleni védekezésben. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rózsaszín</translation>
<translation id="992432478773561401">A(z) „<ph name="SOFTWARE_NAME" />†nem megfelelően lett telepítve a számítógépre vagy a hálózatra:
diff --git a/chromium/components/strings/components_strings_hy.xtb b/chromium/components/strings/components_strings_hy.xtb
index a3210464e99..cab11aaae77 100644
--- a/chromium/components/strings/components_strings_hy.xtb
+++ b/chromium/components/strings/components_strings_hy.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Ô´Ö€Õ¡Õ´Õ¡Õ·Õ¶Õ¸Ö€Õ°Õ¶Õ¥Ö€, Õ¯Ö€Õ©Õ¡Õ©Õ¸Õ·Õ¡Õ¯Õ¶Õ¥Ö€ Ö‡ Ö†Õ«Õ¶Õ¡Õ¶Õ½Õ¡Õ¯Õ¡Õ¶ Ö…Õ£Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
<translation id="1048785276086539861">ÔµÖ€Õ¢ Õ¤Õ¸Ö‚Ö„ Õ­Õ´Õ¢Õ¡Õ£Ö€Õ¥Ö„ Õ®Õ¡Õ¶Õ¸Õ©Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨, ÖƒÕ¡Õ½Õ¿Õ¡Õ©Õ¸Ö‚Õ²Õ©Õ¨ Õ¯ÖÕ¸Ö‚ÖÕ¡Õ¤Ö€Õ¾Õ« Õ´Õ¥Õ¯ Õ§Õ»Õ¸Õ¾</translation>
<translation id="1050038467049342496">Õ“Õ¡Õ¯Õ¥Ö„ Õ´ÕµÕ¸Ö‚Õ½ Õ°Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ¶Õ¥Ö€Õ¨</translation>
+<translation id="1053959602163383901">Ô´Õ¸Ö‚Ö„ Õ¨Õ¶Õ¿Ö€Õ¥Õ¬ Õ¥Ö„, Õ¸Ö€ Õ¯Õ¡ÕµÖ„Õ¥Ö€Õ¸Ö‚Õ´, Õ¸Ö€Õ¸Õ¶Ö„ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¸Ö‚Õ´ Õ¥Õ¶ <ph name="PROVIDER_ORIGIN" /> Õ¡Õ²Õ¢ÕµÕ¸Ö‚Ö€Õ¨, Õ°Õ¡Õ½Õ¿Õ¡Õ¿Õ¸Ö‚Õ´Õ¨ Õ¯Õ¡Õ¿Õ¡Ö€Õ¾Õ« Õ«Õ½Õ¯Õ¸Ö€Õ¸Õ·Õ´Õ¡Õ¶ Õ½Õ¡Ö€Ö„Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾Ö‰ Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ§Õ Õ¡ÕµÕ½ Õ´Õ¡Õ¿Õ¡Õ¯Õ¡Ö€Õ¡Ö€Õ¨ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ Õ§ ÕºÕ¡Õ°Õ¥Õ¬ Õ±Õ¥Ö€ Õ¾Õ³Õ¡Ö€Õ´Õ¡Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯Õ« Õ´Õ¡Õ½Õ«Õ¶Ö‰ Ô´Õ¸Ö‚Ö„ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Ö„ Õ¡ÕµÕ¤ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« <ph name="LINK_TEXT" />Ö‰</translation>
<translation id="1055184225775184556">&amp;Õ€Õ¥Õ¿Õ¡Ö€Õ¯Õ¥Õ¬ Õ°Õ¡Õ¾Õ¥Õ¬Õ¸Ö‚Õ´Õ¨</translation>
<translation id="1056663316309890343">Ô¼Õ¸Ö‚Õ½Õ¡Õ¶Õ¯Õ¡Ö€Õ¶Õ¥Ö€Õ« Õ´Õ·Õ¡Õ¯Õ´Õ¡Õ¶ Õ®Ö€Õ¡Õ£Õ«Ö€</translation>
<translation id="1056898198331236512">Ô¶Õ£Õ¸Ö‚Õ·Õ¡ÖÕ¸Ö‚Õ´</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">ÕÕ¿Õ¡ÖÕ´Õ¡Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯Õ¨</translation>
<translation id="1281476433249504884">Õ‡Õ¥Õ²Õ»Õ«Õ¹ 1</translation>
<translation id="1285320974508926690">ÔµÖ€Õ¢Õ¥Ö„ Õ¹Õ©Õ¡Ö€Õ£Õ´Õ¡Õ¶Õ¥Õ¬ Õ¡ÕµÕ½ Õ¯Õ¡ÕµÖ„Õ¨</translation>
+<translation id="1288548991597756084">Ô±ÕºÕ¡Õ°Õ¸Õ¾ ÕºÕ¡Õ°Õ¥Ö„ Ö„Õ¡Ö€Õ¿Õ« Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨</translation>
<translation id="1292571435393770077">Ô´Õ¡Ö€Õ¡Õ¯ 16</translation>
<translation id="1292701964462482250">«Համակարգչում Õ¿Õ¥Õ²Õ¡Õ¤Ö€Õ¾Õ¡Õ® Õ®Ö€Õ¡Õ£Õ«Ö€Õ¨ Chrome-Õ«Õ¶ Õ©Õ¸Ö‚ÕµÕ¬ Õ¹Õ« Õ¿Õ¡Õ¬Õ«Õ½ Õ¡Õ¶Õ¾Õ¿Õ¡Õ¶Õ£ Õ´Õ«Õ¡Õ¶Õ¡Õ¬ Õ°Õ¡Õ´Õ¡ÖÕ¡Õ¶Öին» (Õ´Õ«Õ¡ÕµÕ¶ Windows Õ°Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£Õ«Õ¹Õ¶Õ¥Ö€Õ¸Ö‚Õ´)</translation>
<translation id="1294154142200295408">Õ€Ö€Õ¡Õ´Õ¡Õ¶Õ¡Õ¿Õ¸Õ²Õ« Õ¿Õ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯Õ¶Õ¥Ö€</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;ÕÕ­Õ¡Õ¬Õ¨ Õ·Õ¿Õ¯Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ½Õ¥Õ²Õ´Õ¥Ö„ &lt;strong&gt;Õ„Õ«Õ¡Õ¶Õ¡Õ¬&lt;/strong&gt; Õ¡ÕµÕ¶ Õ§Õ»Õ¸Ö‚Õ´, Õ¸Ö€Õ¨ ÖƒÕ¸Ö€Õ±Õ¸Ö‚Õ´ Õ¥Ö„ Õ¢Õ¡ÖÕ¥Õ¬Ö‰&lt;/p&gt;</translation>
<translation id="1507780850870535225">Ô¼Õ¡Õ¶Õ¤Õ·Õ¡Ö†Õ¿Õ¡ÕµÕ«Õ¶ Õ¤Õ«Õ¦Õ¡ÕµÕ¶</translation>
<translation id="1513706915089223971">ÕŠÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ£Ö€Õ¡Õ¼Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ« ÖÕ¡Õ¶Õ¯</translation>
+<translation id="1516097932025103760">Õ”Õ¡Ö€Õ¿Õ« Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨ Õ¯Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ£Ö€Õ¾Õ¥Õ¶ Ö‡ Õ¡ÕºÕ¡Õ°Õ¸Õ¾ Õ¯ÕºÕ¡Õ°Õ¾Õ¥Õ¶, Õ«Õ½Õ¯ CVC Õ¯Õ¸Õ¤Õ¨ Õ¥Ö€Õ¢Õ¥Ö„ Õ¹Õ« ÕºÕ¡Õ°Õ¾Õ¸Ö‚Õ´Ö‰</translation>
<translation id="1517433312004943670">Ô±Õ¶Õ°Ö€Õ¡ÕªÕ¥Õ·Õ¿ Õ§ Õ´Õ¸Ö‚Õ¿Ö„Õ¡Õ£Ö€Õ¥Õ¬ Õ°Õ¥Õ¼Õ¡Õ­Õ¸Õ½Õ¡Õ°Õ¡Õ´Õ¡Ö€</translation>
<translation id="1519264250979466059">Ô¿Õ¡Õ¼Õ¸Ö‚ÕµÖÕ« Õ¡Õ´Õ½Õ¡Õ©Õ«Õ¾</translation>
<translation id="1521159554480556801">Ô¹Õ¥Õ¬Õ¥Ö€Õ« Ö‡ Õ£Õ¸Ö€Õ®Õ¾Õ¡Õ®Ö„Õ¶Õ¥Ö€Õ« Õ¡Ö€Õ¿Õ¡Õ¤Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ÕŠÕ¡Õ°Õ¥Õ¬ Õ¾Õ³Õ¡Ö€Õ´Õ¡Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯Õ¶Õ¥Ö€Õ¨ Ö‡ Õ¤Ö€Õ¡Õ¶Ö Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¸Õ¾ Õ«Õ¶Ö„Õ¶Õ¡Õ¬Ö€Õ¡ÖÕ¶Õ¥Õ¬ Õ±Ö‡Õ¥Ö€Õ¨</translation>
<translation id="1663943134801823270">Õ”Õ¡Ö€Õ¿Õ¥Ö€Õ¶ Õ¸Ö‚ Õ°Õ¡Õ½ÖÕ¥Õ¶Õ¥Ö€Õ¨ Õ¶Õ·Õ¾Õ¡Õ® Õ¥Õ¶ Chrome-Õ¸Ö‚Õ´: Ô´Ö€Õ¡Õ¶Ö„ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Ö„ Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬ <ph name="BEGIN_LINK" />Ô¿Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¸Ö‚Õ´<ph name="END_LINK" />:</translation>
<translation id="1671391448414634642">Ô±ÕµÕ½Õ¸Ö‚Õ°Õ¥Õ¿ <ph name="SOURCE_LANGUAGE" /> Õ¬Õ¥Õ¦Õ¾Õ¸Õ¾ Õ§Õ»Õ¥Ö€Õ¨ Õ¯Õ©Õ¡Ö€Õ£Õ´Õ¡Õ¶Õ¾Õ¥Õ¶ <ph name="TARGET_LANGUAGE" />:</translation>
+<translation id="1673886523110456987">Ô±Õ¼Õ¡Õ»Õ¡Ö€Õ¯Õ¶ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ¾Õ³Õ¡Ö€Õ¥Ö„ <ph name="CARD_DETAIL" /> Ö„Õ¡Ö€Õ¿Õ¸Õ¾</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" />Õ«Ö <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Ô¿Õ¡Ö€Õ³ Õ¥Õ¦Ö€Õ«Ö Õ½Õ¯Õ½Õ¡Õ®</translation>
<translation id="168693727862418163">Ô¿Õ¡Õ¶Õ¸Õ¶Õ¨ Õ¯Õ¡Õ¶Õ¿Õ¥Õ½Õ¾Õ«, Ö„Õ¡Õ¶Õ« Õ¸Ö€ Õ¤Ö€Õ¡ Õ¡Ö€ÕªÕ¥Ö„Õ¨ Õ¹Õ« Õ°Õ¡Õ´Õ¡ÕºÕ¡Õ¿Õ¡Õ½Õ­Õ¡Õ¶Õ¸Ö‚Õ´ Õ½Õ­Õ¥Õ´Õ¡ÕµÕ«Õ¶Ö‰</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Õ‡Õ¡Ö€ÕªÕ´Õ¡Õ¶ Õ¿Õ¾Õ«Õ¹Õ¶Õ¥Ö€</translation>
<translation id="1717494416764505390">Õ“Õ¸Õ½Õ¿Õ¡Ö€Õ¯Õ² 3</translation>
<translation id="1718029547804390981">Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ®Õ¡Õ¶Õ¸Õ©Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Õ¡Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬, Ö„Õ¡Õ¶Õ« Õ¸Ö€ ÖƒÕ¡Õ½Õ¿Õ¡Õ©Õ¸Ö‚Õ²Õ©Õ¨ Õ¹Õ¡ÖƒÕ¡Õ¦Õ¡Õ¶Ö Õ´Õ¥Õ® Õ§Ö‰</translation>
+<translation id="1720941539803966190">Õ“Õ¡Õ¯Õ¥Õ¬ Õ¸Ö‚Õ²Õ¥ÖÕ¸Ö‚ÕµÖÕ¨</translation>
<translation id="1721424275792716183">* Ô´Õ¡Õ·Õ¿Õ¨ ÕºÕ¡Ö€Õ¿Õ¡Õ¤Õ«Ö€ Õ§</translation>
<translation id="1727613060316725209">Õ€Õ¡Õ¾Õ¡Õ½Õ¿Õ¡Õ£Õ«Ö€Õ¨ Õ¾Õ¡Õ¾Õ¥Ö€ Õ§</translation>
<translation id="1727741090716970331">Ô±Õ¾Õ¥Õ¬Õ¡ÖÖ€Õ¥Ö„ Ö„Õ¡Ö€Õ¿Õ« Õ¾Õ¡Õ¾Õ¥Ö€ Õ°Õ¡Õ´Õ¡Ö€</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">Ô½Õ¸Ö€Õ¸Õ¾Õ¡Õ® Ö‡ Õ£Ö€Õ«Õ¬</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> Õ¬Õ¥Õ¦Õ¾Õ¸Õ¾ Õ§Õ»Õ¥Ö€Õ¨ Õ¹Õ¥Õ¶ Õ©Õ¡Ö€Õ£Õ´Õ¡Õ¶Õ¾Õ«:</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ÔµÖ€Õ¢ Õ¡ÕµÕ½ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¨ Õ´Õ«Õ¡ÖÕ¾Õ¡Õ® Õ§ Ö‡ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ«Õ³Õ¡Õ¯Õ¶ Õ¡Õ¯Õ¿Õ«Õ¾ Õ§, Chrome-Õ¨ Õ¸Ö€Õ¸Õ·Õ¸Ö‚Õ´ Õ§, Õ©Õ¥ Õ¸Ö€ Õ­Õ´Õ¢Õ«Õ¶ Õ¯Õ¡Õ´ «կոհորտին» Õ§ Õ¶Õ´Õ¡Õ¶ Õ±Õ¥Ö€ Õ¾Õ¥Ö€Õ»Õ«Õ¶ Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¤Õ«Õ¿Õ¡Ö€Õ¯Õ«Õ¹Õ¸Ö‚Õ´Ö‰ Ô³Õ¸Õ¾Õ¡Õ¦Õ¤Õ¡Õ¿Õ¸Ö‚Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ£Õ¸Õ¾Õ¡Õ¦Õ¤Õ¶Õ¥Ö€ Õ¨Õ¶Õ¿Ö€Õ¥Õ¬ Õ­Õ´Õ¢Õ« Õ°Õ¡Õ´Õ¡Ö€Ö‰ ÕÕ¥Ö€ Õ¡ÕµÖÕ¥Õ¬Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ£Õ¡Õ²Õ¿Õ« Õ§ ÕºÕ¡Õ°Õ¾Õ¸Ö‚Õ´ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´Ö‰ ÕÕ¥Ö€ Õ­Õ¸Ö‚Õ´Õ¢Õ¶ Õ¡Õ´Õ¥Õ¶ Ö…Ö€ Õ©Õ¡Ö€Õ´Õ¡ÖÕ¾Õ¸Ö‚Õ´ Õ§Ö‰}=1{ÔµÖ€Õ¢ Õ¡ÕµÕ½ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¨ Õ´Õ«Õ¡ÖÕ¾Õ¡Õ® Õ§ Ö‡ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ«Õ³Õ¡Õ¯Õ¶ Õ¡Õ¯Õ¿Õ«Õ¾ Õ§, Chrome-Õ¨ Õ¸Ö€Õ¸Õ·Õ¸Ö‚Õ´ Õ§, Õ©Õ¥ Õ¸Ö€ Õ­Õ´Õ¢Õ«Õ¶ Õ¯Õ¡Õ´ «կոհորտին» Õ§ Õ¶Õ´Õ¡Õ¶ Õ±Õ¥Ö€ Õ¾Õ¥Ö€Õ»Õ«Õ¶ Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¤Õ«Õ¿Õ¡Ö€Õ¯Õ«Õ¹Õ¸Ö‚Õ´Ö‰ Ô³Õ¸Õ¾Õ¡Õ¦Õ¤Õ¡Õ¿Õ¸Ö‚Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ£Õ¸Õ¾Õ¡Õ¦Õ¤Õ¶Õ¥Ö€ Õ¨Õ¶Õ¿Ö€Õ¥Õ¬ Õ­Õ´Õ¢Õ« Õ°Õ¡Õ´Õ¡Ö€Ö‰ ÕÕ¥Ö€ Õ¡ÕµÖÕ¥Õ¬Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ£Õ¡Õ²Õ¿Õ« Õ§ ÕºÕ¡Õ°Õ¾Õ¸Ö‚Õ´ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´Ö‰ ÕÕ¥Ö€ Õ­Õ¸Ö‚Õ´Õ¢Õ¶ Õ¡Õ´Õ¥Õ¶ Ö…Ö€ Õ©Õ¡Ö€Õ´Õ¡ÖÕ¾Õ¸Ö‚Õ´ Õ§Ö‰}one{ÔµÖ€Õ¢ Õ¡ÕµÕ½ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¨ Õ´Õ«Õ¡ÖÕ¾Õ¡Õ® Õ§ Ö‡ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ«Õ³Õ¡Õ¯Õ¶ Õ¡Õ¯Õ¿Õ«Õ¾ Õ§, Chrome-Õ¨ Õ¸Ö€Õ¸Õ·Õ¸Ö‚Õ´ Õ§, Õ©Õ¥ Õ¸Ö€ Õ­Õ´Õ¢Õ«Õ¶ Õ¯Õ¡Õ´ «կոհորտին» Õ§ Õ¶Õ´Õ¡Õ¶ Õ±Õ¥Ö€ Õ¾Õ¥Ö€Õ»Õ«Õ¶ Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¤Õ«Õ¿Õ¡Ö€Õ¯Õ«Õ¹Õ¸Ö‚Õ´Ö‰ Ô³Õ¸Õ¾Õ¡Õ¦Õ¤Õ¡Õ¿Õ¸Ö‚Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ£Õ¸Õ¾Õ¡Õ¦Õ¤Õ¶Õ¥Ö€ Õ¨Õ¶Õ¿Ö€Õ¥Õ¬ Õ­Õ´Õ¢Õ« Õ°Õ¡Õ´Õ¡Ö€Ö‰ ÕÕ¥Ö€ Õ¡ÕµÖÕ¥Õ¬Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ£Õ¡Õ²Õ¿Õ« Õ§ ÕºÕ¡Õ°Õ¾Õ¸Ö‚Õ´ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´Ö‰ ÕÕ¥Ö€ Õ­Õ¸Ö‚Õ´Õ¢Õ¨ {NUM_DAYS} Ö…Ö€Õ¨ Õ´Õ¥Õ¯ Õ©Õ¡Ö€Õ´Õ¡ÖÕ¾Õ¸Ö‚Õ´ Õ§Ö‰}other{ÔµÖ€Õ¢ Õ¡ÕµÕ½ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¨ Õ´Õ«Õ¡ÖÕ¾Õ¡Õ® Õ§ Ö‡ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ«Õ³Õ¡Õ¯Õ¶ Õ¡Õ¯Õ¿Õ«Õ¾ Õ§, Chrome-Õ¨ Õ¸Ö€Õ¸Õ·Õ¸Ö‚Õ´ Õ§, Õ©Õ¥ Õ¸Ö€ Õ­Õ´Õ¢Õ«Õ¶ Õ¯Õ¡Õ´ «կոհորտին» Õ§ Õ¶Õ´Õ¡Õ¶ Õ±Õ¥Ö€ Õ¾Õ¥Ö€Õ»Õ«Õ¶ Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¤Õ«Õ¿Õ¡Ö€Õ¯Õ«Õ¹Õ¸Ö‚Õ´Ö‰ Ô³Õ¸Õ¾Õ¡Õ¦Õ¤Õ¡Õ¿Õ¸Ö‚Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ£Õ¸Õ¾Õ¡Õ¦Õ¤Õ¶Õ¥Ö€ Õ¨Õ¶Õ¿Ö€Õ¥Õ¬ Õ­Õ´Õ¢Õ« Õ°Õ¡Õ´Õ¡Ö€Ö‰ ÕÕ¥Ö€ Õ¡ÕµÖÕ¥Õ¬Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ£Õ¡Õ²Õ¿Õ« Õ§ ÕºÕ¡Õ°Õ¾Õ¸Ö‚Õ´ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´Ö‰ ÕÕ¥Ö€ Õ­Õ¸Ö‚Õ´Õ¢Õ¨ {NUM_DAYS} Ö…Ö€Õ¨ Õ´Õ¥Õ¯ Õ©Õ¡Ö€Õ´Õ¡ÖÕ¾Õ¸Ö‚Õ´ Õ§Ö‰}}</translation>
-<translation id="2053553514270667976">Õ“Õ¸Õ½Õ¿Õ¡ÕµÕ«Õ¶ Õ¤Õ¡Õ½Õ«Õ¹</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 Õ¡Õ¼Õ¡Õ»Õ¡Ö€Õ¯}one{# Õ¡Õ¼Õ¡Õ»Õ¡Ö€Õ¯}other{# Õ¡Õ¼Õ¡Õ»Õ¡Ö€Õ¯}}</translation>
+<translation id="2066915425250589881">Õ»Õ¶Õ»Õ´Õ¡Õ¶ Õ°Õ¡ÕµÕ¿ Õ¸Ö‚Õ²Õ¡Ö€Õ¯Õ¥Õ¬</translation>
<translation id="2068528718802935086">Õ†Õ¸Ö€Õ¡Õ®Õ«Õ¶Õ¶Õ¥Ö€ Ö‡ Õ´Õ¡Õ¶Õ¯Õ¡Õ°Õ¡Õ½Õ¡Õ¯ Õ¥Ö€Õ¥Õ­Õ¡Õ¶Õ¥Ö€</translation>
<translation id="2071156619270205202">Õ†Õ·Õ¾Õ¡Õ® Õ°Õ¡Õ´Õ¡Ö€Õ¨ Õ¹Õ« Õ¯Õ¡Ö€Õ¸Õ² Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¾Õ¥Õ¬ Õ¾Õ«Ö€Õ¿Õ¸Ö‚Õ¡Õ¬ Ö„Õ¡Ö€Õ¿Õ« Õ°Õ¡Õ´Õ¡Ö€Ö‰</translation>
<translation id="2071692954027939183">Ô¾Õ¡Õ¶Õ¸Ö‚ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¶ Õ¡Õ¾Õ¿Õ¸Õ´Õ¡Õ¿ Õ¡Ö€Õ£Õ¥Õ¬Õ¡ÖƒÕ¡Õ¯Õ¾Õ¥Õ¬ Õ¥Õ¶, Ö„Õ¡Õ¶Õ« Õ¸Ö€ Õ¤Õ¸Ö‚Ö„ Õ¤Ö€Õ¡Õ¶Ö„ Õ½Õ¸Õ¾Õ¸Ö€Õ¡Õ¢Õ¡Ö€ Õ´Õ¥Ö€ÕªÕ¸Ö‚Õ´ Õ¥Ö„Ö‰</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">«Կառավարել Õ°Õ¡Õ´Õ¡ÕªÕ¡Õ´Õ¡Öումը» Õ¯Õ¸Õ³Õ¡Õ¯Ö‰ Õ€Õ¡Õ´Õ¡ÕªÕ¡Õ´Õ¡ÖÕ¾Õ¸Õ² Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨ Chrome-Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¸Ö‚Õ´ Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ½Õ¥Õ²Õ´Õ¥Ö„ EnterÖ‰</translation>
<translation id="2091887806945687916">ÕÕ¡ÕµÕ¶</translation>
<translation id="2094505752054353250">ÕÕ«Ö€Õ¸Ö‚ÕµÕ©Õ« Õ¡Õ¶Õ°Õ¡Õ´Õ¡ÕºÕ¡Õ¿Õ¡Õ½Õ­Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
-<translation id="2096368010154057602">Ô´Õ¥ÕºÕ¡Ö€Õ¿Õ¡Õ´Õ¥Õ¶Õ¿</translation>
<translation id="2099652385553570808">ÔµÖ€Õ¥Ö„ Õ¡Õ´Ö€Õ¡Õ¯ Õ±Õ¡Õ­ Õ¯Õ¸Õ²Õ´Õ¸Ö‚Õ´</translation>
<translation id="2101225219012730419">ÕÕ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯Õ</translation>
<translation id="2102134110707549001">ÕÕ¿Õ¥Õ²Õ®Õ¥Õ¬ Õ°Õ¸Ö‚Õ½Õ¡Õ¬Õ« գաղտնաբառ…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">Ô±Õ´Õ¥Ö€Õ«Õ¯ÕµÕ¡Õ¶ Ö†Õ¸Ö‚Õ¿Õ¢Õ¸Õ¬</translation>
<translation id="2187317261103489799">ÕˆÖ€Õ¸Õ·Õ¥Õ¬ (Õ¯Õ¡Õ¶Õ­Õ¡Õ¤Ö€Õ¾Õ¡Õ®)</translation>
<translation id="2188375229972301266">Õ„Õ« Ö„Õ¡Õ¶Õ« Õ¡Õ¶ÖÖ„ Õ¶Õ¥Ö€Ö„Ö‡Õ¸Ö‚Õ´</translation>
-<translation id="2188852899391513400">Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨, Õ¸Ö€ Õ°Õ¥Õ¶Ö Õ¶Õ¸Ö€ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥ÖÕ«Ö„, Õ¯Õ¸Õ¿Ö€Õ¾Õ¥Õ¬ Õ§ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ¡Ö€Õ¿Õ¡Õ°Õ¸Õ½Ö„Õ« ÕºÕ¡Õ¿Õ³Õ¡Õ¼Õ¸Õ¾Ö‰ ÕÕ¥Ö€ Õ°Õ¡Õ·Õ«Õ¾Õ¶Õ¥Ö€Õ¨ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Google Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ« Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ«Õ¹Õ¨ Õ­Õ¸Ö€Õ°Õ¸Ö‚Ö€Õ¤ Õ§ Õ¿Õ¡Õ¬Õ«Õ½ Õ¡Õ¶Õ°Õ¡ÕºÕ¡Õ² ÖƒÕ¸Õ­Õ¥Õ¬ Õ¡ÕµÕ¶ Ö‡ Õ½Õ¿Õ¸Ö‚Õ£Õ¥Õ¬ ÕºÕ¡Õ°Õ¾Õ¡Õ® Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ¨Ö‰</translation>
<translation id="219906046732893612">ÕÕ¡Õ¶ Õ¾Õ¥Ö€Õ¡Õ¶Õ¸Ö€Õ¸Õ£Õ¸Ö‚Õ´</translation>
<translation id="2202020181578195191">Õ„Õ¸Ö‚Õ¿Ö„Õ¡Õ£Ö€Õ¥Ö„ ÕªÕ¡Õ´Õ¯Õ¥Õ¿Õ« Õ½ÕºÕ¡Õ¼Õ´Õ¡Õ¶ Õ¾Õ¡Õ¾Õ¥Ö€ Õ¿Õ¡Ö€Õ¥Õ©Õ«Õ¾</translation>
<translation id="22081806969704220">Ô´Õ¡Ö€Õ¡Õ¯ 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">Õ–Õ¡ÕµÕ¬Õ« ÖƒÕ¸ÖƒÕ¸Õ­Õ¸Ö‚Õ´</translation>
<translation id="2215963164070968490">Õ‡Õ¶Õ¥Ö€</translation>
<translation id="2218879909401188352">Õ€Õ¡Ö€Õ±Õ¡Õ¯Õ¾Õ¸Õ²Õ¶Õ¥Ö€Õ¨, Õ¸Ö€Õ¸Õ¶Ö„ Õ¶Õ¥Ö€Õ¯Õ¡ÕµÕ¸Ö‚Õ´Õ½ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> Õ¯Õ¡ÕµÖ„Õ¸Ö‚Õ´ Õ¥Õ¶, Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ¿Õ¥Õ²Õ¡Õ¤Ö€Õ¥Õ¬ Õ±Õ¥Ö€ Õ½Õ¡Ö€Ö„Õ¨ Õ¾Õ¶Õ¡Õ½Õ¸Õ² Õ°Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ¶Õ¥Ö€, Õ¡Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ Õ¢Õ»Õ»Õ¡ÕµÕ«Õ¶ Õ¯Õ¡ÕºÕ« Õ®Õ¡Õ­Õ½Õ¥Ö€Õ¨ Õ¯Õ¡Õ´ Õ°Õ¡ÖƒÕ·Õ¿Õ¡Õ¯Õ¥Õ¬ Õ±Õ¥Ö€ Õ¡Õ¶Õ±Õ¶Õ¡Õ¯Õ¡Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨Ö‰ <ph name="BEGIN_LEARN_MORE_LINK" />Ô»Õ´Õ¡Õ¶Õ¡Õ¬ Õ¡Õ¾Õ¥Õ¬Õ«Õ¶<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Õ†Õ¸Ö€Õ«Ö Õ¢Õ¡ÖÕ¥Õ¬ Õ¸Ö‚Õ²Õ¥ÖÕ¸Ö‚ÕµÖÕ¨</translation>
+<translation id="2219735899272417925">ÕŠÕ¡Õ°Õ¡Õ¶Õ»Õ¾Õ¸Ö‚Õ´ Õ§ Õ¾Õ¥Ö€Õ¡Õ¯Õ¡ÕµÕ¥Õ¬ Õ½Õ¡Ö€Ö„Õ¨</translation>
<translation id="2224337661447660594">Ô»Õ¶Õ¿Õ¥Ö€Õ¶Õ¥Õ¿ Õ¯Õ¡Õº Õ¹Õ¯Õ¡</translation>
<translation id="2230458221926704099">ÕÕ¿Õ¸Ö‚Õ£Õ¥Ö„ Õ±Õ¥Ö€ Õ¯Õ¡ÕºÕ¨ <ph name="BEGIN_LINK" />Õ¡Õ­Õ¿Õ¸Ö€Õ¸Õ·Õ«Õ¹ Õ°Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ«<ph name="END_LINK" /> Ö…Õ£Õ¶Õ¸Ö‚Õ©ÕµÕ¡Õ´Õ¢</translation>
<translation id="2239100178324503013">ÕˆÖ‚Õ²Õ¡Ö€Õ¯Õ¥Õ¬</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">Ô±Ö€Õ£Õ¥Õ¬Õ¾Õ¡Õ® Õ§ (Õ¯Õ¡Õ¶Õ­Õ¡Õ¤Ö€Õ¾Õ¡Õ®)</translation>
<translation id="2512413427717747692">«Դարձնել Chrome-Õ¨ Õ¯Õ¡Õ¶Õ­Õ¡Õ¤Ö€Õ¾Õ¡Õ® դիտարկիչ» Õ¯Õ¸Õ³Õ¡Õ¯Ö‰ ÕÕ¥Õ²Õ´Õ¥Ö„ EnterÕ iOS-Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¸Ö‚Õ´ Chrome-Õ¨ Õ°Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£Õ« Õ¯Õ¡Õ¶Õ­Õ¡Õ¤Ö€Õ¾Õ¡Õ® Õ¤Õ«Õ¿Õ¡Ö€Õ¯Õ«Õ¹Õ¨ Õ¤Õ¡Ö€Õ±Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€Ö‰</translation>
<translation id="2515629240566999685">ÕÕ¿Õ¸Ö‚Õ£Õ¥Õ¬ Õ¡Õ¦Õ¤Õ¡Õ¶Õ·Õ¡Õ¶Õ¨ Õ±Õ¥Ö€ Õ¿Õ¡Ö€Õ¡Õ®Ö„Õ¸Ö‚Õ´</translation>
+<translation id="2515761554693942801">Ô´Õ¸Ö‚Ö„ Õ¨Õ¶Õ¿Ö€Õ¥Õ¬ Õ¥Ö„, Õ¸Ö€ Õ¯Õ¡ÕµÖ„Õ¥Ö€Õ¸Ö‚Õ´, Õ¸Ö€Õ¸Õ¶Ö„ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¸Ö‚Õ´ Õ¥Õ¶ <ph name="PROVIDER_ORIGIN" /> Õ¡Õ²Õ¢ÕµÕ¸Ö‚Ö€Õ¨, Õ°Õ¡Õ½Õ¿Õ¡Õ¿Õ¸Ö‚Õ´Õ¨ Õ¯Õ¡Õ¿Õ¡Ö€Õ¾Õ« Touch ID-Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾Ö‰ Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ§Õ Õ¡ÕµÕ½ Õ´Õ¡Õ¿Õ¡Õ¯Õ¡Ö€Õ¡Ö€Õ¨ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ Õ§ ÕºÕ¡Õ°Õ¥Õ¬ Õ±Õ¥Ö€ Õ¾Õ³Õ¡Ö€Õ´Õ¡Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯Õ« Õ´Õ¡Õ½Õ«Õ¶Ö‰ Ô´Õ¸Ö‚Ö„ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Ö„ Õ¡ÕµÕ¤ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« <ph name="LINK_TEXT" />Ö‰</translation>
<translation id="2521385132275182522">Ô±Õ´Ö€Õ¡Õ¯ Õ¶Õ¥Ö€Ö„Ö‡Õ« Õ¡Õ» Õ¯Õ¸Õ²Õ´Õ¸Ö‚Õ´</translation>
<translation id="2521736961081452453">ÕÕ¿Õ¥Õ²Õ®Õ¥Õ¬ Õ±Ö‡</translation>
<translation id="2523886232349826891">Ô¿ÕºÕ¡Õ°Õ¾Õ« Õ´Õ«Õ¡ÕµÕ¶ Õ¡ÕµÕ½ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´</translation>
<translation id="2524461107774643265">Ô±Õ¾Õ¥Õ¬Õ¡ÖÖ€Õ¥Ö„ Õ¬Ö€Õ¡ÖÕ¸Ö‚ÖÕ«Õ¹ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€</translation>
<translation id="2529899080962247600">Ô±ÕµÕ½ Õ¤Õ¡Õ·Õ¿Õ¸Ö‚Õ´ Õ£Ö€Õ¡Õ¼Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ« Õ¡Õ¼Õ¡Õ¾Õ¥Õ¬Õ¡Õ£Õ¸Ö‚ÕµÕ¶ Ö„Õ¡Õ¶Õ¡Õ¯Õ¨Õ <ph name="MAX_ITEMS_LIMIT" />: Ô±Õ¾Õ¥Õ¬ Õ£Ö€Õ¡Õ¼Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Õ¶Õ¿Õ¥Õ½Õ¾Õ¥Õ¶:</translation>
+<translation id="253493526287553278">ÕÕ¥Õ½Õ¶Õ¥Õ¬ ÕºÖ€Õ¸Õ´Õ¸Õ¯Õ¸Õ¤Õ« Õ´Õ¡Õ¶Ö€Õ¡Õ´Õ¡Õ½Õ¶Õ¥Ö€Õ¨</translation>
<translation id="2535585790302968248">Ô²Õ¡ÖÕ¥Ö„ Õ¶Õ¸Ö€ Õ«Õ¶Õ¯Õ¸Õ£Õ¶Õ«Õ¿Õ¸ Õ¶Õ¥Ö€Õ¤Õ«Ö€, Õ¸Ö€ÕºÕ¥Õ½Õ¦Õ« Õ¡ÕµÖÕ¥Õ¬Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¹ÕºÕ¡Õ°Õ¾Õ«</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{Õ¸Ö‚ Ö‡Õ½ 1 Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©}one{Õ¸Ö‚ Ö‡Õ½ # Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©}other{Õ¸Ö‚ Ö‡Õ½ # Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©}}</translation>
<translation id="2536110899380797252">Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ Õ°Õ¡Õ½ÖÕ¥</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">Ô¼Õ¸Ö‚Õ½Õ¡Õ¶Õ¯Õ¡Ö€Õ¹Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Ö‡ Õ©Õ¾Õ¡ÕµÕ«Õ¶ Õ¡Ö€Õ¾Õ¥Õ½Õ¿</translation>
<translation id="2601150049980261779">Ռոմանտիկ ֆիլմեր</translation>
<translation id="2604589665489080024">Õ“Õ¸Öƒ Õ¥Ö€Õ¡ÕªÕ·Õ¿Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
-<translation id="2609632851001447353">ÕÕ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯Õ¶Õ¥Ö€</translation>
<translation id="2610561535971892504">ÕÕ¥Õ²Õ´Õ¥Õ¬Õ ÕºÕ¡Õ¿Õ³Õ¥Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€</translation>
<translation id="2617988307566202237">Chrome-Õ¸Ö‚Õ´ <ph name="BEGIN_EMPHASIS" />Õ¹Õ¥Õ¶ ÕºÕ¡Õ°Õ¾Õ«<ph name="END_EMPHASIS" />Õ
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Ô¾Õ¶Õ¶Õ¤ÕµÕ¡Õ¶ Ö‡ Õ¡Õ¶Õ¾Õ¡Õ¶Õ¨ Õ¶Õ¾Õ«Ö€Õ¾Õ¡Õ® Ö…Ö€Õ¥Ö€</translation>
<translation id="2677748264148917807">Ô´Õ¸Ö‚Ö€Õ½ Õ£Õ¡Õ¬ Õ§Õ»Õ«Ö</translation>
+<translation id="2679714844901977852">ÕŠÕ¡Õ°Õ¥Ö„ Õ±Õ¥Ö€ Ö„Õ¡Ö€Õ¿Õ¨ Ö‡ Õ¾Õ³Õ¡Ö€Õ¡ÕµÕ«Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨ Õ±Õ¥Ö€ Google Õ°Õ¡Õ·Õ¾Õ¸Ö‚Õ´ (<ph name="USER_EMAIL" />)Õ Õ¾Õ³Õ¡Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¶ Õ¡Õ¾Õ¥Õ¬Õ« Õ¡ÕºÕ¡Õ°Õ¸Õ¾ Ö‡ Õ¡Ö€Õ¡Õ£ Õ¯Õ¡Õ¿Õ¡Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Ô¼Õ¡Õ¾Õ¡Õ£Õ¸Ö‚ÕµÕ¶ Õ¿Õ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯</translation>
<translation id="2688969097326701645">Ô±ÕµÕ¸, Õ·Õ¡Ö€Õ¸Ö‚Õ¶Õ¡Õ¯Õ¥Õ¬</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">Ô»Õ¶Õ¿Õ¥Ö€Õ¶Õ¥Õ¿ ÕºÖ€Õ¸Õ¾Õ¡ÕµÕ¤Õ¥Ö€Õ¶Õ¥Ö€ (Ô»Ô¾Õ„-Õ¶Õ¥Ö€)</translation>
<translation id="2856444702002559011">Õ€Õ¡Ö€Õ±Õ¡Õ¯Õ¾Õ¸Õ²Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ°Õ¡ÖƒÕ·Õ¿Õ¡Õ¯Õ¥Õ¬ Õ±Õ¥Ö€ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨ (Ö…Ö€Õ«Õ¶Õ¡Õ¯Õ Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ¨, Õ¶Õ¡Õ´Õ¡Õ¯Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Õ´ Õ¾Õ¡Ö€Õ¯Õ¡ÕµÕ«Õ¶ Ö„Õ¡Ö€Õ¿Õ¥Ö€Õ¨) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> Õ¯Õ¡ÕµÖ„Õ«ÖÖ‰ <ph name="BEGIN_LEARN_MORE_LINK" />Ô»Õ´Õ¡Õ¶Õ¡Õ¬ Õ¡Õ¾Õ¥Õ¬Õ«Õ¶<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Ô±ÕµÕ½ Õ¯Õ¡ÕµÖ„Õ¨ Õ°Õ¸Õ£Õ¶Õ¥ÖÕ¶Õ¸Õ² Õ¯Õ¡Õ´ Õ´Õ¸Õ¬Õ¸Ö€Õ¥ÖÕ¶Õ¸Õ² Õ£Õ¸Õ¾Õ¡Õ¦Õ¤ Õ§ ÖÕ¸Ö‚ÖÕ¡Õ¤Ö€Õ¸Ö‚Õ´Ö‰</translation>
-<translation id="286512204874376891">ÕŽÕ«Ö€Õ¿Õ¸Ö‚Õ¡Õ¬ Ö„Õ¡Ö€Õ¿Õ¨ Õ©Õ¡Ö„ÖÕ¶Õ¸Ö‚Õ´ Õ§ Õ±Õ¥Ö€ Õ«Ö€Õ¡Õ¯Õ¡Õ¶ Ö„Õ¡Ö€Õ¿Õ¨Õ Ö…Õ£Õ¶Õ¥Õ¬Õ¸Õ¾ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¥Õ¬ Õ±Õ¥Õ¦ Õ°Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ­Õ¡Ö€Õ¤Õ¡Õ­Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ«ÖÖ‰ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Ô¸Õ¶Õ¯Õ¥Ö€Õ¡Õ¯Õ¡Õ¶</translation>
<translation id="28761159517501904">Õ–Õ«Õ¬Õ´Õ¥Ö€</translation>
<translation id="2876489322757410363">Ô´Õ¸Ö‚Ö„ Õ¤Õ¸Ö‚Ö€Õ½ Õ¯Õ£Õ¡Ö„ Õ«Õ¶Õ¯Õ¸Õ£Õ¶Õ«Õ¿Õ¸ Õ¼Õ¥ÕªÕ«Õ´Õ«ÖÕ Õ¡Ö€Õ¿Õ¡Ö„Õ«Õ¶ Õ°Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ¸Ö‚Õ´ Õ¾Õ³Õ¡Ö€Õ¸Ö‚Õ´ Õ¯Õ¡Õ¿Õ¡Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€Ö‰ Õ‡Õ¡Ö€Õ¸Ö‚Õ¶Õ¡Õ¯Õ¥ÕžÕ¬Ö‰</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ÕÕ¯Õ¡Õ¾Õ¡Õ¼Õ¡Õ¯</translation>
<translation id="3162559335345991374">Ô±Õ¶Õ¬Õ¡Ö€ Õ¯Õ¡ÕºÕ¨, Õ¸Ö€Õ«Ö Ö…Õ£Õ¿Õ¾Õ¸Ö‚Õ´ Õ¥Ö„, Õ¯Õ¡Ö€Õ¸Õ² Õ§ ÕºÕ¡Õ°Õ¡Õ¶Õ»Õ¥Õ¬, Õ¸Ö€ÕºÕ¥Õ½Õ¦Õ« Õ¤Õ¸Ö‚Ö„ Õ¡ÕµÖÕ¥Õ¬Õ¥Ö„ Õ¶Ö€Õ¡ Õ´Õ¸Ö‚Õ¿Ö„Õ« Õ§Õ»Õ¨:</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ô¿Õ²Õ¦Õ«</translation>
<translation id="3176929007561373547">ÕÕ¿Õ¸Ö‚Õ£Õ¥Ö„ Õ±Õ¥Ö€ ÕºÖ€Õ¸Ö„Õ½Õ« Õ½Õ¥Ö€Õ¾Õ¥Ö€Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Õ´ Õ¯Õ¡ÕºÕ¾Õ¥Ö„
ÖÕ¡Õ¶ÖÕ« Õ¡Õ¤Õ´Õ«Õ¶Õ«Õ½Õ¿Ö€Õ¡Õ¿Õ¸Ö€Õ« Õ°Õ¥Õ¿, Õ¸Ö€ÕºÕ¥Õ½Õ¦Õ« Õ°Õ¡Õ´Õ¸Õ¦Õ¾Õ¥Ö„, Õ¸Ö€
ÕºÖ€Õ¸Ö„Õ½Õ« Õ½Õ¥Ö€Õ¾Õ¥Ö€Õ¶ Õ¡Õ·Õ­Õ¡Õ¿Õ¸Ö‚Õ´ Õ§: ÔµÕ©Õ¥ Õ¹Õ¥Ö„ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¸Ö‚Õ´ ÕºÖ€Õ¸Ö„Õ½Õ« Õ½Õ¥Ö€Õ¾Õ¥Ö€`
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">ÔºÕ¡Õ´Õ¡ÖÕ¸Ö‚ÕµÖÕ« Õ½Õ­Õ¡Õ¬</translation>
<translation id="3369459162151165748">Ô±Õ¾Õ¿Õ¸ÕºÕ¡Õ°Õ¥Õ½Õ¿Õ¡Õ´Õ¡Õ½Õ¥Ö€ Ö‡ Õ¬Ö€Õ¡Õ½Õ¡Ö€Ö„Õ¥Ö€</translation>
<translation id="3371064404604898522">Ô´Õ¡Ö€Õ±Õ¶Õ¥Õ¬ Chrome-Õ¨ Õ¯Õ¡Õ¶Õ­Õ¡Õ¤Ö€Õ¾Õ¡Õ® Õ¤Õ«Õ¿Õ¡Ö€Õ¯Õ«Õ¹</translation>
-<translation id="3371076217486966826"><ph name="URL" /> Õ¯Õ¡ÕµÖ„Õ¶ Õ¸Ö‚Õ¦Õ¸Ö‚Õ´ Õ§Õ
- • ÕÕ¿Õ¥Õ²Õ®Õ¥Õ¬ Õ·Ö€Õ»Õ¡Õ¯Õ¡ÕµÖ„Õ« Õ¥Õ¼Õ¡Õ¹Õ¡Öƒ Ö„Õ¡Ö€Õ¿Õ¥Õ¦Õ¨ Ö‡ Õ°Õ¥Õ¿Õ¡Õ£Õ®Õ¥Õ¬ Õ¿Õ¥Õ½Õ¡Õ­ÖÕ«Õ¯Õ« Õ¤Õ«Ö€Ö„Õ¨
- • Õ•Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥Õ¬ Õ±Õ¥Ö€ Õ¿Õ¥Õ½Õ¡Õ­ÖÕ«Õ¯Õ¨</translation>
<translation id="337363190475750230">Ô±Õ¶Õ»Õ¡Õ¿Õ¾Õ¡Õ® Õ§</translation>
<translation id="3375754925484257129">Ô±Õ¶ÖÕ¶Õ¥Õ¬ Chrome-Õ« Õ¡Õ¶Õ¾Õ¿Õ¡Õ¶Õ£Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ½Õ¿Õ¸Ö‚Õ£Õ¸Ö‚Õ´</translation>
<translation id="3377144306166885718">ÕÕ¥Ö€Õ¾Õ¥Ö€Õ¶ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¸Ö‚Õ´ Õ§ TLS-Õ« Õ°Õ¶Õ¡ÖÕ¡Õ® Õ¿Õ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯Ö‰</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">Ô±Õ¼Õ¡Ö„Õ´Õ¡Õ¶ Õ°Õ¡Õ½ÖÕ¥Õ¶</translation>
<translation id="3402261774528610252">Ô¿Õ¡ÕºÕ¨, Õ¸Ö€Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾ Õ¢Õ¥Õ¼Õ¶Õ¾Õ¥Õ¬ Õ§ Õ¡ÕµÕ½ Õ¯Õ¡ÕµÖ„Õ¨, Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¸Ö‚Õ´ Õ§ TLS-Õ« 1.0 Õ¯Õ¡Õ´ 1.1 Õ¿Õ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯Õ¶Õ¥Ö€Õ¨, Õ¸Ö€Õ¸Õ¶Ö„ Õ°Õ¶Õ¡ÖÕ¡Õ® Õ¥Õ¶ Ö‡ Õ¹Õ¥Õ¶ Õ¡Õ»Õ¡Õ¯ÖÕ¾Õ« Õ¡ÕºÕ¡Õ£Õ¡ÕµÕ¸Ö‚Õ´Ö‰ Ô±Õ¶Õ»Õ¡Õ¿Õ¾Õ¥Õ¬Õ¸Ö‚Ö Õ°Õ¥Õ¿Õ¸ Õ¡ÕµÖÕ¥Õ¬Õ¸Ö‚Õ¶Õ¥Ö€Õ¨ Õ¡ÕµÕ¬Ö‡Õ½ Õ¹Õ¥Õ¶ Õ¯Õ¡Ö€Õ¸Õ²Õ¡Õ¶Õ¡ Õ¤Õ«Õ¿Õ¥Õ¬ Õ¡ÕµÕ½ Õ¯Õ¡ÕµÖ„Õ¨Ö‰ ÕÕ¥Ö€Õ¾Õ¥Ö€Õ¨ ÕºÕ¥Õ¿Ö„ Õ§ Õ¡Õ¯Õ¿Õ«Õ¾Õ¡ÖÕ¶Õ« TLS-Õ« 1.2 Õ¯Õ¡Õ´ Õ¡Õ¾Õ¥Õ¬Õ« Õ¢Õ¡Ö€Õ±Ö€ Õ¿Õ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯Ö‰</translation>
<translation id="3405664148539009465">Õ€Õ¡Ö€Õ´Õ¡Ö€Õ¥ÖÕ¶Õ¥Õ¬ Õ¿Õ¡Õ¼Õ¡Õ¿Õ¥Õ½Õ¡Õ¯Õ¶Õ¥Ö€Õ¨</translation>
+<translation id="3407789382767355356">Õ´Õ¸Ö‚Õ¿Ö„ Õ¥Ö€Ö€Õ¸Ö€Õ¤ Õ¯Õ¸Õ²Õ´Õ« Õ®Õ¡Õ¼Õ¡ÕµÕ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾</translation>
<translation id="3409896703495473338">Ô¿Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬ Õ¡Õ¶Õ¾Õ¿Õ¡Õ¶Õ£Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨</translation>
<translation id="3414952576877147120">Õ‰Õ¡ÖƒÕ½Õ¨Õ</translation>
<translation id="3417660076059365994">ÕÕ¥Ö€ Õ¾Õ¥Ö€Õ¢Õ¥Õ¼Õ¶Õ¡Õ® Õ¯Õ¡Õ´ Õ¯ÖÕ¡Õ® Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ¨ Õ¸Ö‚Õ²Õ¡Ö€Õ¯Õ¾Õ¸Ö‚Õ´ Õ¥Õ¶ Google Cloud Õ¯Õ¡Õ´ Õ£Õ¸Ö€Õ®Õ¨Õ¶Õ¯Õ¥Ö€ Õ¯Õ¡Õ¦Õ´Õ¡Õ¯Õ¥Ö€ÕºÕ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ«Õ¶Õ Õ¾Õ¥Ö€Õ¬Õ¸Ö‚Õ®Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ¶ÕºÕ¡Õ¿Õ¡Õ¯Õ¸Õ¾Ö‰ Õ•Ö€Õ«Õ¶Õ¡Õ¯, Õ¤Ö€Õ¡Õ¶ÖÕ¸Ö‚Õ´ Õ¯Õ¡Ö€Õ¸Õ² Õ§ Õ½Õ¿Õ¸Ö‚Õ£Õ¾Õ¥Õ¬ Õ­Õ«Õ½Õ¿ Õ¡Õ¶Õ±Õ¶Õ¡Õ¯Õ¡Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ¯Õ¡Õ´ Õ¾Õ¶Õ¡Õ½Õ¡Õ£Ö€Õ¥Ö€Õ« Õ¡Õ¼Õ¯Õ¡ÕµÕ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨Ö‰</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">Ô¿Õ«Õ¶Õ¸Õ©Õ¡Õ¿Ö€Õ¸Õ¶Õ¶Õ¥Ö€Õ« Ö‡ Õ©Õ¡Õ¿Ö€Õ¸Õ¶Õ¶Õ¥Ö€Õ« Õ­Õ¡Õ²Õ¡ÖÕ¡Õ¶Õ¯Õ¥Ö€</translation>
<translation id="3479552764303398839">Õ€Õ«Õ´Õ¡ Õ¹Õ§</translation>
<translation id="3484560055331845446">Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ§Õ Õ¹Õ¯Õ¡Ö€Õ¸Õ²Õ¡Õ¶Õ¡Ö„ Õ´Õ¿Õ¶Õ¥Õ¬ Õ±Õ¥Ö€ Google Õ°Õ¡Õ·Õ«Õ¾Ö‰ Chrome-Õ¨ Õ­Õ¸Ö€Õ°Õ¸Ö‚Ö€Õ¤ Õ§ Õ¿Õ¡Õ¬Õ«Õ½ ÖƒÕ¸Õ­Õ¥Õ¬ Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨, Õ¸Ö€Õ«Ö Õ°Õ¥Õ¿Õ¸ Õ°Õ¡Ö€Õ¯Õ¡Õ¾Õ¸Ö€ Õ¯Õ¬Õ«Õ¶Õ« Õ¶Õ¸Ö€Õ«Ö Õ´Õ¸Ö‚Õ¿Ö„ Õ£Õ¸Ö€Õ®Õ¥Õ¬Ö‰</translation>
+<translation id="3484861421501147767">Õ€Õ«Õ·Õ¥Öում․ Õ¡Õ¼Õ¯Õ¡ Õ§ ÕºÕ¡Õ°Õ¾Õ¡Õ® ÕºÖ€Õ¸Õ´Õ¸Õ¯Õ¸Õ¤</translation>
<translation id="3487845404393360112">Ô´Õ¡Ö€Õ¡Õ¯ 4</translation>
<translation id="3495081129428749620">ÕˆÖ€Õ¸Õ¶Õ¸Ö‚Õ´
<ph name="PAGE_TITLE" /> Õ§Õ»Õ¸Ö‚Õ´</translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">Ô¿Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬</translation>
<translation id="3816482573645936981">Ô±Ö€ÕªÕ¥Ö„Õ¨ (ÖƒÕ¸Õ­Õ¡Ö€Õ«Õ¶Õ¾Õ¡Õ® Õ§)</translation>
<translation id="382518646247711829">Եթե օգտագործում եք պրոքսի սերվեր…</translation>
+<translation id="3826050100957962900">Õ„Õ¸Ö‚Õ¿Ö„ Õ¥Ö€Ö€Õ¸Ö€Õ¤ Õ¯Õ¸Õ²Õ´Õ« Õ®Õ¡Õ¼Õ¡ÕµÕ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾</translation>
<translation id="3827112369919217609">Ô²Õ¡ÖÕ¡Ö€Õ±Õ¡Õ¯</translation>
<translation id="3827666161959873541">Ô¸Õ¶Õ¿Õ¡Õ¶Õ¥Õ¯Õ¡Õ¶ Ö†Õ«Õ¬Õ´Õ¥Ö€</translation>
<translation id="3828924085048779000">Ô±Õ¶ÖÕ¡Õ¢Õ¡Õ¼Õ« Õ¤Õ¡Õ·Õ¿Õ¨ Õ¹Õ« Õ¯Õ¡Ö€Õ¸Õ² Õ¤Õ¡Õ¿Õ¡Ö€Õ¯ Õ¬Õ«Õ¶Õ¥Õ¬:</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">Ô¹Õ¡Ö€Õ´Õ¡ÖÕ¶Õ¥Õ¬ Õ¡Õ´Õ½Õ¡Õ©Õ«Õ¾Õ¨ Ö‡ ÕªÕ¡Õ´Õ¨</translation>
<translation id="3858860766373142691">Ô±Õ¶Õ¸Ö‚Õ¶</translation>
<translation id="3872834068356954457">Ô³Õ«Õ¿Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
+<translation id="3875783148670536197">Õ‘Õ¸Ö‚ÕµÖ Õ¿Õ¡Õ¬, Õ©Õ¥ Õ«Õ¶Õ¹ÕºÕ¥Õ½</translation>
<translation id="3881478300875776315">Õ‘Õ¸Ö‚ÕµÖ Õ¿Õ¡Õ¬ Õ¡Õ¾Õ¥Õ¬Õ« Ö„Õ«Õ¹ Õ¿Õ¸Õ²Õ¥Ö€</translation>
<translation id="3884278016824448484">ÕÕ¡Ö€Ö„Õ¥Ö€Õ« Õ¶Õ¸Ö‚ÕµÕ¶Õ¡ÖÕ¸Ö‚ÖÕ«Õ¹Õ¶Õ¥Ö€Õ« Õ¨Õ¶Õ¤Õ°Õ¡Ö€Õ¸Ö‚Õ´</translation>
-<translation id="3885155851504623709">Ô¾Õ¸Ö‚Õ­</translation>
<translation id="388632593194507180">Õ„Õ«Õ¡ÖÕ¸Ö‚Õ´Õ¨ Õ¾Õ¥Ö€Õ¡Õ°Õ½Õ¯Õ¾Õ¸Ö‚Õ´ Õ§</translation>
<translation id="3886948180919384617">Õ‡Õ¥Õ²Õ»Õ«Õ¹ 3</translation>
<translation id="3890664840433101773">Ô±Õ¾Õ¥Õ¬Õ¡ÖÖ€Õ¥Ö„ էլ․ Õ°Õ¡Õ½ÖÕ¥</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" />-Õ¶ Õ¡Ö€Õ£Õ¥Õ¬Õ¡ÖƒÕ¡Õ¯Õ¾Õ¡Õ® Õ§</translation>
<translation id="3978338123949022456">ÕˆÖ€Õ¸Õ¶Õ´Õ¡Õ¶ Õ¼Õ¥ÕªÕ«Õ´, Õ´Õ¸Ö‚Õ¿Ö„Õ¡Õ£Ö€Õ¥Ö„ Õ°Õ¡Ö€ÖÕ¸Ö‚Õ´Õ¨ Ö‡ Õ½Õ¥Õ²Õ´Õ¥Ö„ EnterÕ <ph name="KEYWORD_SUFFIX" />-Õ¸Ö‚Õ´ Õ¸Ö€Õ¸Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€</translation>
<translation id="398470910934384994">Ô¹Õ¼Õ¹Õ¸Ö‚Õ¶Õ¶Õ¥Ö€</translation>
+<translation id="3985750352229496475">Ô¿Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬ Õ°Õ¡Õ½Öեները…</translation>
<translation id="3986705137476756801">Ô±Õ¶Õ»Õ¡Õ¿Õ¥Õ¬ Ô¿Õ¥Õ¶Õ¤Õ¡Õ¶Õ« Õ¥Õ¶Õ©Õ¡Õ£Ö€Õ¥Ö€Õ¨</translation>
<translation id="3987940399970879459">1 Õ„Ô²-Õ«Ö Ö„Õ«Õ¹</translation>
<translation id="3990250421422698716">Ô±Õ½Õ¿Õ«Õ³Õ¡Õ¶Õ¡Õ¯Õ¡Õ¶ Õ¿Õ¥Õ²Õ¡Õ·Õ¡Ö€Õª</translation>
+<translation id="3992684624889376114">Ô±ÕµÕ½ Õ§Õ»Õ« Õ´Õ¡Õ½Õ«Õ¶</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> Õ¯Õ¡ÕµÖ„Õ¨ ÕºÕ¡Õ°Õ¡Õ¶Õ»Õ¸Ö‚Õ´ Õ§, Õ¸Ö€ Õ½Õ¯Õ¦Õ¢Õ¶Õ¡Õ¯Õ¡Õ¶ Õ¯Õ¡Õ¶Õ¸Õ¶Õ¨
Õ¯Õ«Ö€Õ¡Õ¼Õ¾Õ« Õ«Ö€ Õ¢Õ¸Õ¬Õ¸Ö€ Õ°Õ¡Ö€ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€Õ« Õ¶Õ¯Õ¡Õ¿Õ´Õ¡Õ´Õ¢, Õ¢Õ¡ÕµÖ Õ¡ÕµÕ½ Õ¯Õ¡Õ¶Õ¸Õ¶Õ¶ Õ¡ÕµÕªÕ´ Õ°Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ¯Õ«Ö€Õ¡Õ¼Õ¥Õ¬Ö‰</translation>
<translation id="4006465311664329701">ÕŽÕ³Õ¡Ö€Õ´Õ¡Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯Õ¶Õ¥Ö€, Õ¡Õ¼Õ¡Õ»Õ¡Ö€Õ¯Õ¶Õ¥Ö€ Õ¸Ö‚ Õ°Õ¡Õ½ÖÕ¥Õ¶Õ¥Ö€ Google Pay-Õ«Ö</translation>
@@ -1227,6 +1234,7 @@
<translation id="4305666528087210886">Õ‰Õ°Õ¡Õ»Õ¸Õ²Õ¾Õ¥Ö Õ¢Õ¡ÖÕ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¨</translation>
<translation id="4306529830550717874">ÕŠÕ¡Õ°Õ¥ÕžÕ¬ Õ°Õ¡Õ½ÖÕ¥Õ¶</translation>
<translation id="4306812610847412719">Õ½Õ¥Õ²Õ´Õ¡Õ¿Õ¡Õ­Õ¿Õ¡Õ¯</translation>
+<translation id="4310070645992025887">ÕˆÖ€Õ¸Õ¶Õ¥Ö„ Õ±Õ¥Ö€ Õ¡Õ·Õ­Õ¡Õ¿Õ¡Õ·Ö€Õ»Õ¡Õ¶Õ¶Õ¥Ö€Õ¸Ö‚Õ´</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Ô±Ö€Õ£Õ¥Õ¬Õ¡ÖƒÕ¡Õ¯Õ¥Õ¬ (Õ¯Õ¡Õ¶Õ­Õ¡Õ¤Ö€Õ¾Õ¡Õ®)</translation>
<translation id="4314815835985389558">Õ€Õ¡Õ´Õ¡ÕªÕ¡Õ´Õ¡ÖÕ´Õ¡Õ¶ Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¸Ö‚Õ´</translation>
@@ -1277,6 +1285,7 @@
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">ÕŠÖ€Õ¸Ö„Õ½Õ«-Õ½Õ¥Ö€Õ¾Õ¥Ö€Õ« Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¸Ö‚Õ´Õ¶ Õ¡Õ¶Õ»Õ¡Õ¿Õ¾Õ¡Õ® Õ§, Õ¢Õ¡ÕµÖ Õ°Õ¡Õ¿Õ¯Õ¸Ö€Õ¸Õ·Õ¾Õ¡Õ® Õ§ Õ¸Ö‚Õ²Õ²Õ¡Õ¯Õ« Õ¯Õ¡Õ¦Õ´Õ¡Õ±Ö‡Õ¸Ö‚Õ´:</translation>
<translation id="4441832193888514600">Ô±Õ¶Õ¿Õ¥Õ½Õ¾Õ¸Ö‚Õ´ Õ§, Ö„Õ¡Õ¶Õ« Õ¸Ö€ Õ¡ÕµÕ½ Õ¯Õ¡Õ¶Õ¸Õ¶Õ¨ Õ¯Õ¡Ö€Õ¥Õ¬Õ« Õ§ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¥Õ¬ Õ´Õ«Õ¡ÕµÕ¶ Õ¡Õ´ÕºÕ« Õ´Õ¡Õ¯Õ¡Ö€Õ¤Õ¡Õ¯Õ¸Ö‚Õ´Ö‰</translation>
+<translation id="4442470707340296952">Chrome-Õ« Õ¶Õ¥Ö€Õ¤Õ«Ö€Õ¶Õ¥Ö€</translation>
<translation id="4450893287417543264">Ô±ÕµÕ¬Ö‡Õ½ ÖÕ¸Ö‚ÕµÖ Õ¹Õ¿Õ¡Õ¬</translation>
<translation id="4451135742916150903">Ô¿Õ¡Ö€Õ¸Õ² Õ§ Õ°Õ¡ÕµÖÕ¥Õ¬ HID Õ½Õ¡Ö€Ö„Õ¥Ö€Õ«Õ¶ Õ´Õ«Õ¡Õ¶Õ¡Õ¬Õ¸Ö‚ Õ©Õ¸Ö‚ÕµÕ¬Õ¿Õ¾Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
<translation id="4452328064229197696">Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨, Õ¸Ö€ Õ°Õ¥Õ¶Ö Õ¶Õ¸Ö€ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥ÖÕ«Ö„, Õ¯Õ¸Õ¿Ö€Õ¾Õ¥Õ¬ Õ§ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ¡Ö€Õ¿Õ¡Õ°Õ¸Õ½Ö„Õ« ÕºÕ¡Õ¿Õ³Õ¡Õ¼Õ¸Õ¾Ö‰ ÕÕ¥Ö€ Õ°Õ¡Õ·Õ«Õ¾Õ¶Õ¥Ö€Õ¨ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Google Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ« Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ«Õ¹Õ¨ Õ­Õ¸Ö€Õ°Õ¸Ö‚Ö€Õ¤ Õ§ Õ¿Õ¡Õ¬Õ«Õ½ Õ½Õ¿Õ¸Ö‚Õ£Õ¥Õ¬ ÕºÕ¡Õ°Õ¾Õ¡Õ® Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ¨Ö‰</translation>
@@ -1415,6 +1424,7 @@
<translation id="483241715238664915">Õ„Õ«Õ¡ÖÕ¶Õ¥Õ¬ Õ¦Õ£Õ¸Ö‚Õ·Õ¡ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨</translation>
<translation id="4834250788637067901">ÕŽÕ³Õ¡Ö€Õ´Õ¡Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯Õ¶Õ¥Ö€, Õ¡Õ¼Õ¡Õ»Õ¡Ö€Õ¯Õ¶Õ¥Ö€ Õ¸Ö‚ Õ°Õ¡Õ½ÖÕ¥Õ¶Õ¥Ö€ Google Pay-Õ«Ö</translation>
<translation id="4838327282952368871">ÔµÖ€Õ¡Õ¦Õ¡ÕµÕ«Õ¶</translation>
+<translation id="4839087176073128681">Õ€Õ¡Õ»Õ¸Ö€Õ¤ Õ¡Õ¶Õ£Õ¡Õ´ Õ¡Õ¾Õ¥Õ¬Õ« Õ¡Ö€Õ¡Õ£ Õ¾Õ³Õ¡Ö€Õ¥Ö„ Ö‡ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¥Ö„ Õ±Õ¥Ö€ Ö„Õ¡Ö€Õ¿Õ« Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨ Google-Õ« Õ¶Õ¸Ö€Õ¡Õ£Õ¸Ö‚ÕµÕ¶ Õ¿Õ¥Õ­Õ¶Õ¸Õ¬Õ¸Õ£Õ«Õ¡Õ¶Õ¥Ö€Õ« Õ·Õ¶Õ¸Ö€Õ°Õ«Õ¾Ö‰</translation>
<translation id="4840250757394056958">Ô´Õ«Õ¿Õ¥Õ¬ Chrome-Õ« ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨</translation>
<translation id="484462545196658690">Ô±Õ¾Õ¿Õ¸Õ´Õ¡Õ¿</translation>
<translation id="484671803914931257">ÕÕ¿Õ¡ÖÕ¥Ö„ Õ¦Õ¥Õ²Õ¹ <ph name="MERCHANT_NAME" /> Ö‡ Õ¡ÕµÕ¬ Õ­Õ¡Õ¶Õ¸Ö‚Õ©Õ¶Õ¥Ö€Õ¸Ö‚Õ´</translation>
@@ -1478,6 +1488,7 @@
<translation id="5011561501798487822">Õ€Õ¡ÕµÕ¿Õ¶Õ¡Õ¢Õ¥Ö€Õ¾Õ¡Õ® Õ¬Õ¥Õ¦Õ¸Ö‚Õ¶</translation>
<translation id="5015510746216210676">ÕÕ¡Ö€Ö„Õ« Õ¡Õ¶Õ¸Ö‚Õ¶Õ¨Õ</translation>
<translation id="5017554619425969104">ÕÕ¥Ö€ ÕºÕ¡Õ¿Õ³Õ¥Õ¶Õ¡Õ® Õ¿Õ¥Ö„Õ½Õ¿Õ¨</translation>
+<translation id="5017828934289857214">Õ€Õ«Õ·Õ¥ÖÕ¶Õ¥Õ¬ Õ«Õ¶Õ± Õ¡Õ¾Õ¥Õ¬Õ« Õ¸Ö‚Õ·</translation>
<translation id="5018422839182700155">Ô·Õ»Õ¨ Õ¹Õ°Õ¡Õ»Õ¸Õ²Õ¾Õ¥Ö Õ¢Õ¡ÖÕ¥Õ¬</translation>
<translation id="5019198164206649151">ÕŠÕ¡Õ°Õ¥Õ½Õ¿Õ« Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨ Õ¾Õ¶Õ¡Õ½Õ¾Õ¡Õ® Õ¥Õ¶</translation>
<translation id="5020776957610079374">Ô±Õ·Õ­Õ¡Ö€Õ°Õ« ÕªÕ¸Õ²Õ¸Õ¾Õ¸Ö‚Ö€Õ¤Õ¶Õ¥Ö€Õ« Õ¥Ö€Õ¡ÕªÕ·Õ¿Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
@@ -1497,6 +1508,7 @@
<translation id="5051305769747448211">Ô¿Õ¥Õ¶Õ¤Õ¡Õ¶Õ« Õ¯Õ¡Õ¿Õ¡Õ¯Õ¥Ö€Õ£Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Ô±ÕµÕ½ Ö†Õ¡ÕµÕ¬Õ¨ «Փոխանակում Õ´Õ¸Õ¿Õ¡Õ¯Õ¡ Õ½Õ¡Ö€Ö„Õ¥Ö€Õ« հետ» Õ£Õ¸Ö€Õ®Õ¡Õ¼Õ¸Ö‚ÕµÕ©Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾ Õ¸Ö‚Õ²Õ¡Ö€Õ¯Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ±Õ¥Ö€ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´ Õ¿Õ¡Ö€Õ¡Õ®Ö„ Õ¡Õ¦Õ¡Õ¿Õ¥Ö„ (<ph name="DISK_SPACE_SIZE" />)}one{Ô±ÕµÕ½ Ö†Õ¡ÕµÕ¬Õ¨ «Փոխանակում Õ´Õ¸Õ¿Õ¡Õ¯Õ¡ Õ½Õ¡Ö€Ö„Õ¥Ö€Õ« հետ» Õ£Õ¸Ö€Õ®Õ¡Õ¼Õ¸Ö‚ÕµÕ©Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾ Õ¸Ö‚Õ²Õ¡Ö€Õ¯Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ±Õ¥Ö€ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´ Õ¿Õ¡Ö€Õ¡Õ®Ö„ Õ¡Õ¦Õ¡Õ¿Õ¥Ö„ (<ph name="DISK_SPACE_SIZE" />)}other{Ô±ÕµÕ½ Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ¨ «Փոխանակում Õ´Õ¸Õ¿Õ¡Õ¯Õ¡ Õ½Õ¡Ö€Ö„Õ¥Ö€Õ« հետ» Õ£Õ¸Ö€Õ®Õ¡Õ¼Õ¸Ö‚ÕµÕ©Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾ Õ¸Ö‚Õ²Õ¡Ö€Õ¯Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ±Õ¥Ö€ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´ Õ¿Õ¡Ö€Õ¡Õ®Ö„ Õ¡Õ¦Õ¡Õ¿Õ¥Ö„ (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">Õ€Õ¸Õ¤Õ¾Õ¡Õ®Õ¶Õ¥Ö€ Õ±Õ¥Õ¦ Õ°Õ¡Õ´Õ¡Ö€</translation>
+<translation id="5060483733937416656">Ô´Õ¸Ö‚Ö„ Õ¨Õ¶Õ¿Ö€Õ¥Õ¬ Õ¥Ö„, Õ¸Ö€ Õ¯Õ¡ÕµÖ„Õ¥Ö€Õ¸Ö‚Õ´, Õ¸Ö€Õ¸Õ¶Ö„ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¸Ö‚Õ´ Õ¥Õ¶ <ph name="PROVIDER_ORIGIN" /> Õ¡Õ²Õ¢ÕµÕ¸Ö‚Ö€Õ¨, Õ°Õ¡Õ½Õ¿Õ¡Õ¿Õ¸Ö‚Õ´Õ¨ Õ¯Õ¡Õ¿Õ¡Ö€Õ¾Õ« Windows Hello-Õ« Õ´Õ«Õ»Õ¸ÖÕ¸Õ¾Ö‰ Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ§Õ Õ¡ÕµÕ½ Õ´Õ¡Õ¿Õ¡Õ¯Õ¡Ö€Õ¡Ö€Õ¨ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ Õ§ ÕºÕ¡Õ°Õ¥Õ¬ Õ±Õ¥Ö€ Õ¾Õ³Õ¡Ö€Õ´Õ¡Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯Õ« Õ´Õ¡Õ½Õ«Õ¶Ö‰ Ô´Õ¸Ö‚Ö„ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Ö„ Õ¡ÕµÕ¤ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« <ph name="LINK_TEXT" />Ö‰</translation>
<translation id="5061227663725596739">Արդյո՞ք նկատի ունեիք «<ph name="LOOKALIKE_DOMAIN" />»</translation>
<translation id="5066056036849835175">ÕÕºÕ´Õ¡Õ¶ ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
<translation id="5068234115460527047">Õ€Õ¥Õ» Ö†Õ¸Õ¶Õ¤Õ¥Ö€</translation>
@@ -1510,10 +1522,8 @@
<translation id="5087286274860437796">ÕÕ¥Ö€Õ¾Õ¥Ö€Õ« Õ¾Õ¯Õ¡ÕµÕ¡Õ£Õ«Ö€Õ¶ Õ¡ÕµÕ½ Õ¡Õ¶Õ£Õ¡Õ´ Õ¾Õ¡Õ¾Õ¥Ö€ Õ¹Õ§:</translation>
<translation id="5087580092889165836">Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ Ö„Õ¡Ö€Õ¿</translation>
<translation id="5088142053160410913">Õ€Õ¡Õ²Õ¸Ö€Õ¤Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Ö…ÕºÕ¥Ö€Õ¡Õ¿Õ¸Ö€Õ«Õ¶</translation>
-<translation id="5089810972385038852">Õ†Õ¡Õ°Õ¡Õ¶Õ£</translation>
<translation id="5093232627742069661">ÔµÖ€Õ¯Õ®Õ¡Õ¬Ö„ Õ¦Õ«Õ£Õ¦Õ¡Õ£Õ¡Õ±Ö‡ Õ®Õ¡Õ¬Õ¸Ö‚Õ´</translation>
<translation id="5094747076828555589">Ô±ÕµÕ½ Õ½Õ¥Ö€Õ¾Õ¥Ö€Õ¨ Õ¹Õ« Õ¯Õ¡Ö€Õ¸Õ² Õ¡ÕºÕ¡ÖÕ¸Ö‚ÖÕ¥Õ¬, Õ¸Ö€ <ph name="DOMAIN" /> Õ§: Chromium-Õ¨ Õ¹Õ« Õ¾Õ½Õ¿Õ¡Õ°Õ¸Ö‚Õ´ Õ¤Ö€Õ¡ Õ¡Õ¶Õ¾Õ¿Õ¡Õ¶Õ£Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ¾Õ¯Õ¡ÕµÕ¡Õ¯Õ¡Õ¶Õ«Õ¶: ÕŠÕ¡Õ¿Õ³Õ¡Õ¼Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ§ Õ¬Õ«Õ¶Õ¥Õ¬ Õ½Õ­Õ¡Õ¬ Õ¯Õ¡Õ¦Õ´Õ¡Õ±Ö‡Õ¸Ö‚Õ´Õ¨ Õ¯Õ¡Õ´ Õ¯Õ¡ÕºÕ¡Õ¯ÖÕ´Õ¡Õ¶ Õ­Õ¡ÖƒÕ¡Õ¶Õ¸Ö‚Õ´Õ¨ Õ°Õ¡Ö€Õ±Õ¡Õ¯Õ¾Õ¸Õ²Õ« Õ¯Õ¸Õ²Õ´Õ«Ö:</translation>
-<translation id="5095208057601539847">Ô³Õ¡Õ¾Õ¡Õ¼</translation>
<translation id="5097099694988056070">ÕÕ¡Ö€Ö„Õ« Õ¾Õ«Õ³Õ¡Õ¯Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨, Ö…Ö€Õ«Õ¶Õ¡Õ¯Õ CPU-Õ«/RAM-Õ« Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¸Ö‚Õ´Õ¨</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Ô¿Õ¡ÕµÖ„Õ¨ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¾Õ¡Õ® Õ¹Õ§</translation>
@@ -1551,6 +1561,7 @@
<translation id="5171045022955879922">ÕˆÖ€Õ¸Õ¶Õ¥Ö„ Õ¯Õ¡Õ´ Õ´Õ¸Ö‚Õ¿Ö„Õ¡Õ£Ö€Õ¥Ö„ URL-Õ¨</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Õ€Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£Õ«Õ¹</translation>
+<translation id="5177076414499237632">Ô»Õ´Õ¡Õ¶Õ¡Õ¬ Õ¡Õ¾Õ¥Õ¬Õ«Õ¶ Õ¡ÕµÕ½ Õ§Õ»Õ« Õ¡Õ²Õ¢ÕµÕ¸Ö‚Ö€Õ« Ö‡ Õ©Õ¥Õ´Õ¡ÕµÕ« Õ´Õ¡Õ½Õ«Õ¶</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> Õ¹Õ§Õž: Õ€Õ¡Õ²Õ¸Ö€Õ¤Õ¥Ö„ Õ½Õ­Õ¡Õ¬Õ« Õ´Õ¡Õ½Õ«Õ¶</translation>
<translation id="518639307526414276">Ô¿Õ¥Õ¶Õ¤Õ¡Õ¶Õ«Õ¶Õ¥Ö€Õ« Õ¯Õ¥Ö€ Ö‡ Õ­Õ¶Õ¡Õ´Ö„Õ« Õ´Õ«Õ»Õ¸ÖÕ¶Õ¥Ö€</translation>
<translation id="5190835502935405962">Ô·Õ»Õ¡Õ¶Õ«Õ·Õ¶Õ¥Ö€Õ« Õ£Õ¸Õ¿Õ«</translation>
@@ -1711,6 +1722,7 @@
<translation id="5624120631404540903">Ô¿Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬ Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ¨</translation>
<translation id="5629630648637658800">Õ‰Õ°Õ¡Õ»Õ¸Õ²Õ¾Õ¥Ö Õ¢Õ¥Õ¼Õ¶Õ¥Õ¬ Õ¯Õ¡Õ¶Õ¸Õ¶Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨</translation>
<translation id="5631439013527180824">ÕÕ¡Ö€Ö„Õ« Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ´Õ¡Õ¶ Õ½Õ­Õ¡Õ¬ Õ°Õ¥Õ¿Ö„Õ¡Õ¶Õ·Õ«Õ¹</translation>
+<translation id="5632485077360054581">Õ‘Õ¸Ö‚ÕµÖ Õ¿Õ¡Õ¬, Õ©Õ¥ Õ«Õ¶Õ¹ÕºÕ¥Õ½</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> Õ¯Õ¡ÕµÖ„Õ¸Ö‚Õ´ Õ£Õ¿Õ¶Õ¾Õ¸Õ² Õ°Õ¡Ö€Õ±Õ¡Õ¯Õ¾Õ¸Õ²Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ¾Õ¿Õ¡Õ¶Õ£Õ¡Õ¾Õ¸Ö€ Õ®Ö€Õ¡Õ£Ö€Õ¥Ö€ Õ¿Õ¥Õ²Õ¡Õ¤Ö€Õ¥Õ¬ Õ±Õ¥Ö€ Õ°Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£Õ¹Õ« Õ¾Ö€Õ¡, Õ¸Ö€Õ¸Õ¶Ö„ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ°Õ¡ÖƒÕ·Õ¿Õ¡Õ¯Õ¥Õ¬ Õ¯Õ¡Õ´ Õ»Õ¶Õ»Õ¥Õ¬ Õ±Õ¥Ö€ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨ (Ö…Ö€Õ«Õ¶Õ¡Õ¯Õ Õ¬Õ¸Ö‚Õ½Õ¡Õ¶Õ¯Õ¡Ö€Õ¶Õ¥Ö€Õ¨, Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ¨, Õ¶Õ¡Õ´Õ¡Õ¯Õ¶Õ¥Ö€Õ¶ Õ¸Ö‚ Õ¾Õ¡Ö€Õ¯Õ¡ÕµÕ«Õ¶ Ö„Õ¡Ö€Õ¿Õ¥Ö€Õ¨): <ph name="BEGIN_LEARN_MORE_LINK" />Ô»Õ´Õ¡Õ¶Õ¡Õ¬ Õ¡Õ¾Õ¥Õ¬Õ«Õ¶<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Ô½Õ¡Ö€Õ¤Õ¡Õ­Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ ÕºÕ¡Ö€Õ¸Ö‚Õ¶Õ¡Õ¯Õ¸Õ² Õ¢Õ¸Õ¾Õ¡Õ¶Õ¤Õ¡Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶ Õ¡Ö€Õ£Õ¥Õ¬Õ¡ÖƒÕ¡Õ¯Õ¾Õ¡Õ® Õ§</translation>
<translation id="5633259641094592098">Ô¿Õ¸Ö‚Õ¬Õ¿Õ¡ÕµÕ«Õ¶ Ö‡ Õ°Õ¥Õ²Õ«Õ¶Õ¡Õ¯Õ¡ÕµÕ«Õ¶ Ö†Õ«Õ¬Õ´Õ¥Ö€</translation>
@@ -1828,6 +1840,7 @@
<translation id="5989320800837274978">Õ†Õ·Õ¾Õ¡Õ® Õ¹Õ¥Õ¶ Õ¸Õ¹ Ö†Õ«Ö„Õ½Õ¾Õ¡Õ® ÕºÖ€Õ¸Ö„Õ½Õ«-Õ½Õ¥Ö€Õ¾Õ¥Ö€Õ¶Õ¥Ö€Õ¨, Õ¸Õ¹ PAC Õ½Õ¯Ö€Õ«ÕºÕ¿Õ¶Õ¥Ö€Õ« URL-Õ¶Õ¥Ö€Õ¨</translation>
<translation id="5992691462791905444">Ô¶Õ«Õ£Õ¦Õ¡Õ£Õ¡Õ±Ö‡ Õ®Õ¡Õ¬Õ¸Ö‚Õ´</translation>
<translation id="5995727681868049093">Ô¿Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨, Õ£Õ¡Õ²Õ¿Õ¶Õ«Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶ Õ¸Ö‚ Õ¡Õ¶Õ¾Õ¿Õ¡Õ¶Õ£Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Google Õ°Õ¡Õ·Õ¾Õ¸Ö‚Õ´</translation>
+<translation id="5997247540087773573">Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨, Õ¸Ö€Õ¨ Õ°Õ¥Õ¶Ö Õ¶Õ¸Ö€ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥ÖÕ«Ö„, Õ¯Õ¸Õ¿Ö€Õ¾Õ¥Õ¬ Õ§ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ¡Ö€Õ¿Õ¡Õ°Õ¸Õ½Ö„Õ« ÕºÕ¡Õ¿Õ³Õ¡Õ¼Õ¸Õ¾Ö‰ ÕÕ¥Ö€ Õ°Õ¡Õ·Õ«Õ¾Õ¶Õ¥Ö€Õ¨ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Google Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ« Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ«Õ¹Õ¨ Õ­Õ¸Ö€Õ°Õ¸Ö‚Ö€Õ¤ Õ§ Õ¿Õ¡Õ¬Õ«Õ½ Õ¡Õ¶Õ°Õ¡ÕºÕ¡Õ² ÖƒÕ¸Õ­Õ¥Õ¬ Õ¡ÕµÕ¶ Ö‡ Õ½Õ¿Õ¸Ö‚Õ£Õ¥Õ¬ ÕºÕ¡Õ°Õ¾Õ¡Õ® Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ¨Ö‰</translation>
<translation id="6000758707621254961">«<ph name="SEARCH_TEXT" />»-ի որոնման <ph name="RESULT_COUNT" /> արդյունք</translation>
<translation id="6006484371116297560">Ô´Õ¡Õ½Õ¡Õ¯Õ¡Õ¶</translation>
<translation id="6008122969617370890">N-Õ«Ö 1 Õ°Õ¥Ö€Õ©Õ¡Õ¯Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¡Õ´Õ¢</translation>
@@ -1923,7 +1936,6 @@
<translation id="627746635834430766">Õ€Õ¡Õ»Õ¸Ö€Õ¤ Õ¡Õ¶Õ£Õ¡Õ´ Õ¡Õ¾Õ¥Õ¬Õ« Õ¡Ö€Õ¡Õ£ Õ¾Õ³Õ¡Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ ÕºÕ¡Õ°Õ¥Ö„ Õ±Õ¥Ö€ Ö„Õ¡Ö€Õ¿Õ¨ Ö‡ Õ¾Õ³Õ¡Ö€Õ¡ÕµÕ«Õ¶ Õ°Õ¡Õ½ÖÕ¥Õ¶ Google Õ°Õ¡Õ·Õ¾Õ¸Ö‚Õ´:</translation>
<translation id="6279183038361895380">Õ†Õ·Õ¸Ö€Õ¤Õ¨ ÖÕ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ½Õ¥Õ²Õ´Õ¥Ö„ |<ph name="ACCELERATOR" />|</translation>
<translation id="6280223929691119688">Ô±ÕµÕ½ Õ°Õ¡Õ½ÖÕ¥Õ¸Õ¾ Õ°Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ¡Õ¼Õ¡Ö„Õ¥Õ¬: Ô¸Õ¶Õ¿Ö€Õ¥Ö„ Õ¡ÕµÕ¬ Õ°Õ¡Õ½ÖÕ¥:</translation>
-<translation id="6282194474023008486">Õ“Õ¸Õ½Õ¿Õ¡ÕµÕ«Õ¶ Õ¤Õ¡Õ½Õ«Õ¹</translation>
<translation id="6285507000506177184">«Կառավարել Õ¶Õ¥Ö€Õ¢Õ¥Õ¼Õ¶Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨ Chrome-ում» Õ¯Õ¸Õ³Õ¡Õ¯Ö‰ ÕÕ¥Õ²Õ´Õ¥Ö„ EnterÕ Chrome-Õ¸Ö‚Õ´ Õ±Õ¥Ö€ Õ¶Õ¥Ö€Õ¢Õ¥Õ¼Õ¶Õ¡Õ® Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ¨ Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€Ö‰</translation>
<translation id="6289939620939689042">Ô·Õ»Õ« Õ£Õ¸Ö‚ÕµÕ¶Õ¨</translation>
<translation id="6290238015253830360">Ô±ÕµÕ½Õ¿Õ¥Õ² Õ¯ÖÕ¸Ö‚ÖÕ¡Õ¤Ö€Õ¾Õ¥Õ¶ Õ¡Õ¼Õ¡Õ»Õ¡Ö€Õ¯Õ¾Õ¸Õ² Õ°Õ¸Õ¤Õ¾Õ¡Õ®Õ¶Õ¥Ö€Õ¨</translation>
@@ -1945,6 +1957,7 @@
<translation id="6337133576188860026">Ô¿Õ¡Õ¦Õ¡Õ¿Õ¾Õ« Õ´Õ«Õ¶Õ¹Ö‡ <ph name="SIZE" /> Õ¿Õ¡Ö€Õ¡Õ®Ö„Ö‰ Ô´Ö€Õ¡Õ¶Õ«Ö Õ°Õ¥Õ¿Õ¸ Õ¸Ö€Õ¸Õ· Õ¯Õ¡ÕµÖ„Õ¥Ö€ Õ°Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ§Õ Õ¡Õ¾Õ¥Õ¬Õ« Õ¤Õ¡Õ¶Õ¤Õ¡Õ² Õ¢Õ¥Õ¼Õ¶Õ¾Õ¥Õ¶Ö‰</translation>
<translation id="6337534724793800597">Ô¶Õ¿Õ¥Õ¬ Õ¯Õ¡Õ¶Õ¸Õ¶Õ¶Õ¥Ö€Õ¶ Õ¨Õ½Õ¿ Õ¡Õ¶Õ¾Õ¡Õ¶</translation>
<translation id="6340739886198108203">Ô±Õ¤Õ´Õ«Õ¶Õ«Õ½Õ¿Ö€Õ¡Õ¿Õ¸Ö€Õ¨ Õ­Õ¸Ö€Õ°Õ¸Ö‚Ö€Õ¤ Õ¹Õ« Õ¿Õ¡Õ¬Õ«Õ½ Õ½Ö„Ö€Õ«Õ¶Õ·Õ¸Õ©Õ¶Õ¥Ö€ Õ¯Õ¡Õ´ Õ¿Õ¥Õ½Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ Õ¡Õ¶Õ¥Õ¬, Õ¥Ö€Õ¢ Õ§Õ¯Ö€Õ¡Õ¶Õ«Õ¶ Õ¯Õ¸Õ¶Ö†Õ«Õ¤Õ¥Õ¶ÖÕ«Õ¡Õ¬ Õ¢Õ¸Õ¾Õ¡Õ¶Õ¤Õ¡Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Õ§ Õ¥Ö€Ö‡Õ¸Ö‚Õ´Ö‰</translation>
+<translation id="6348220984832452017">Ô¸Õ¶Õ©Õ¡ÖÕ«Õ¯ Õ¿Õ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯Õ¶Õ¥Ö€</translation>
<translation id="6349101878882523185">ÕÕ¥Õ²Õ¡Õ¤Ö€Õ¥Õ¬ <ph name="APP_NAME" /> Õ°Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ¨</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Õ‰Õ¯Õ¡}=1{1 Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼ (Õ°Õ¥Õ¿Ö‡ÕµÕ¡Õ¬ Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©Õ¶Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€Õ <ph name="DOMAIN_LIST" />, Õ°Õ¡Õ´Õ¡ÕªÕ¡Õ´Õ¡ÖÕ¾Õ¡Õ® Õ§)}=2{2 Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼ (Õ°Õ¥Õ¿Ö‡ÕµÕ¡Õ¬ Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©Õ¶Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€Õ <ph name="DOMAIN_LIST" />, Õ°Õ¡Õ´Õ¡ÕªÕ¡Õ´Õ¡ÖÕ¾Õ¡Õ® Õ¥Õ¶)}one{# Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼ (Õ°Õ¥Õ¿Ö‡ÕµÕ¡Õ¬ Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©Õ¶Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€Õ <ph name="DOMAIN_LIST" />, Õ°Õ¡Õ´Õ¡ÕªÕ¡Õ´Õ¡ÖÕ¾Õ¡Õ® Õ¥Õ¶)}other{# Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼ (Õ°Õ¥Õ¿Ö‡ÕµÕ¡Õ¬ Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©Õ¶Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€Õ <ph name="DOMAIN_LIST" />, Õ°Õ¡Õ´Õ¡ÕªÕ¡Õ´Õ¡ÖÕ¾Õ¡Õ® Õ¥Õ¶)}}</translation>
<translation id="6355392890578844978">ÕÕ¥Ö€ Õ¤Õ«Õ¿Õ¡Ö€Õ¯Õ«Õ¹Õ¨ Õ¹Õ« Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¾Õ¸Ö‚Õ´ Õ¸Ö€Ö‡Õ§ Õ¨Õ¶Õ¯Õ¥Ö€Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ¯Õ¡Õ´ Õ¯Õ¡Õ¦Õ´Õ¡Õ¯Õ¥Ö€ÕºÕ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ¯Õ¸Õ²Õ´Õ«ÖÖ‰ ÕÕ¡Ö€Ö„Õ¸Ö‚Õ´ Õ¡Ö€Õ¾Õ¸Õ² Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¾Õ¥Õ¬ Chromium-Õ«Ö Õ¤Õ¸Ö‚Ö€Õ½Ö‰ <ph name="BEGIN_LINK" />Ô»Õ´Õ¡Õ¶Õ¡Õ¬ Õ¡Õ¾Õ¥Õ¬Õ«Õ¶<ph name="END_LINK" /></translation>
@@ -1976,7 +1989,6 @@
<translation id="643051589346665201">Õ“Õ¸Õ­Õ¥Õ¬ Google-Õ« Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨</translation>
<translation id="6433490469411711332">Õ“Õ¸ÖƒÕ¸Õ­Õ¥Ö„ Õ¯Õ¸Õ¶Õ¿Õ¡Õ¯Õ¿Õ¡ÕµÕ«Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" />-Õ¨ Õ´Õ¥Ö€ÕªÕ¥Ö Õ¯Õ¡ÕºÕ¡Õ¯ÖÕ¸Ö‚Õ´Õ¨:</translation>
-<translation id="6438025220577812695">Õ“Õ¸Õ­Õ¥Õ¬ Õ«Õ¶Ö„Õ¶Õ¸Ö‚Ö€Õ¸Ö‚ÕµÕ¶</translation>
<translation id="6440503408713884761">Ô±Õ¶Õ¿Õ¥Õ½Õ¾Õ¡Õ®</translation>
<translation id="6443406338865242315">Ô»Õ¶Õ¹ Õ¨Õ¶Õ¤Õ¬Õ¡ÕµÕ¶Õ¸Ö‚Õ´Õ¶Õ¥Ö€ Ö‡ ÖƒÕ¬Õ¡Õ£Õ«Õ¶Õ¶Õ¥Ö€ Õ¥Ö„ Õ¤Õ¸Ö‚Ö„ Õ¿Õ¥Õ²Õ¡Õ¤Ö€Õ¥Õ¬</translation>
<translation id="6446163441502663861">Kahu (Õ®Ö€Õ¡Ö€)</translation>
@@ -2106,9 +2118,9 @@
<translation id="6828866289116430505">Ô³Õ¥Õ¶Õ¥Õ¿Õ«Õ¯Õ¡</translation>
<translation id="6831043979455480757">Ô¹Õ¡Ö€Õ£Õ´Õ¡Õ¶Õ¥Õ¬</translation>
<translation id="6833752742582340615">ÕŠÕ¡Õ°Õ¥Ö„ Õ±Õ¥Ö€ Ö„Õ¡Ö€Õ¿Õ¨ Ö‡ Õ¾Õ³Õ¡Ö€Õ¡ÕµÕ«Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨ Google Õ°Õ¡Õ·Õ¾Õ¸Ö‚Õ´Õ Õ¾Õ³Õ¡Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¶ Õ¡Õ¾Õ¥Õ¬Õ« Õ¡ÕºÕ¡Õ°Õ¸Õ¾ Ö‡ Õ¡Ö€Õ¡Õ£ Õ¯Õ¡Õ¿Õ¡Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€</translation>
-<translation id="6839929833149231406">Õ‡Ö€Õ»Õ¡Õ¶</translation>
<translation id="6846340164947227603">Õ•Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥Õ¬ Õ¾Õ«Ö€Õ¿Õ¸Ö‚Õ¡Õ¬ Ö„Õ¡Ö€Õ¿Õ« Õ°Õ¡Õ´Õ¡Ö€Õ¨</translation>
<translation id="6852204201400771460">Ô¿Ö€Õ¯Õ«Õ¶ Õ¢Õ¥Õ¼Õ¶Õ¥ÕžÕ¬ Õ°Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ¨</translation>
+<translation id="6857776781123259569">Կառավարել գաղտնաբառերը…</translation>
<translation id="686485648936420384">ÕÕºÕ¡Õ¼Õ¸Õ²Õ¡Õ¯Õ¡Õ¶ Õ¼Õ¥Õ½Õ¸Ö‚Ö€Õ½Õ¶Õ¥Ö€</translation>
<translation id="6865412394715372076">Õ‰Õ°Õ¡Õ»Õ¸Õ²Õ¾Õ¥Ö Õ°Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Õ¬ Õ¡ÕµÕ½ Ö„Õ¡Ö€Õ¿Õ¨</translation>
<translation id="6869334554832814367">Ô±Õ¶Õ°Õ¡Õ¿Õ¡Õ¯Õ¡Õ¶ Õ¾Õ¡Ö€Õ¯Õ¥Ö€</translation>
@@ -2157,7 +2169,6 @@
<translation id="6965978654500191972">ÕÕ¡Ö€Ö„</translation>
<translation id="696703987787944103">Ô¸Õ¶Õ¯Õ¡Õ¬Õ«Õ¹</translation>
<translation id="6968269510885595029">Õ•Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥Ö„ Õ¡Õ¶Õ¾Õ¿Õ¡Õ¶Õ£Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ¢Õ¡Õ¶Õ¡Õ¬Õ«Õ¶</translation>
-<translation id="6970216967273061347">Õ‡Ö€Õ»Õ¡Õ¶</translation>
<translation id="6971439137020188025">Ô±Ö€Õ¡Õ£ Õ½Õ¿Õ¥Õ²Õ®Õ¥Õ¬ Google Õ¶Õ¥Ö€Õ¯Õ¡ÕµÕ¡ÖÕ¸Ö‚Õ´ ÕÕ¬Õ¡ÕµÕ¤Õ¶Õ¥Ö€Õ¸Ö‚Õ´</translation>
<translation id="6972629891077993081">HID Õ½Õ¡Ö€Ö„Õ¥Ö€</translation>
<translation id="6973656660372572881">Õ†Õ·Õ¾Õ¡Õ® Õ¥Õ¶ Ö‡ Ö†Õ«Ö„Õ½Õ¾Õ¡Õ® ÕºÖ€Õ¸Ö„Õ½Õ«-Õ½Õ¥Ö€Õ¾Õ¥Ö€Õ¶Õ¥Ö€Õ¨, Ö‡ PAC Õ½Õ¯Ö€Õ«ÕºÕ¿Õ¶Õ¥Ö€Õ« URL-Õ¨:</translation>
@@ -2196,7 +2207,6 @@
<translation id="7081308185095828845">Ô±ÕµÕ½ Õ£Õ¸Ö€Õ®Õ¡Õ¼Õ¸Ö‚ÕµÕ©Õ¨ Õ°Õ¡Õ½Õ¡Õ¶Õ¥Õ¬Õ« Õ¹Õ§ Õ±Õ¥Ö€ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´</translation>
<translation id="7083258188081898530">Ô´Õ¡Ö€Õ¡Õ¯ 9</translation>
<translation id="7086090958708083563">Õ•Õ£Õ¿Õ¡Õ¿Õ¥Ö€Õ¨ Õ¾Õ¥Ö€Õ¢Õ¥Õ¼Õ¶Õ´Õ¡Õ¶ Õ°Õ¡ÕµÕ¿ Õ§ Õ¸Ö‚Õ²Õ¡Ö€Õ¯Õ¥Õ¬</translation>
-<translation id="7087282848513945231">Ô¿Õ¸Õ´Õ½Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />Ö‰ Ô¹Õ¸Ö‚ÕµÕ¬Õ¿Õ¾Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨ Ö‡ Õ¯Õ¡ÕµÖ„Õ¥Ö€Õ¸Ö‚Õ´ ÕºÕ¡Õ°Õ¾Õ¡Õ® Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨ Chrome-Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¸Ö‚Õ´ Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ½Õ¥Õ²Õ´Õ¥Ö„ Tab, Õ¡ÕºÕ¡Õ EnterÖ‰</translation>
<translation id="7096937462164235847">Ô±ÕµÕ½ Õ¯Õ¡ÕµÖ„Õ« Õ«Õ½Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ½Õ¿Õ¸Ö‚Õ£Õ¾Õ¡Õ® Õ¹Õ§Ö‰</translation>
<translation id="7101893872976785596">ÕÕ¡Ö€Õ½Õ¡Öƒ Ö†Õ«Õ¬Õ´Õ¥Ö€</translation>
@@ -2215,10 +2225,10 @@
<ph name="LIST_ITEM" />Õ±Ö‡Õ¥Ö€Õ¸Ö‚Õ´ Õ¬Ö€Õ¡ÖÕ¾Õ¡Õ® Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨Ö‰<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Ô±ÕµÕ½ Õ°Õ¡Õ½ÖÕ¥Õ¸Õ¾ Õ°Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ¡Õ¼Õ¡Ö„Õ¥Õ¬: Ô¸Õ¶Õ¿Ö€Õ¥Ö„ Õ¡ÕµÕ¬ Õ°Õ¡Õ½ÖÕ¥:</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ÕÕ¥Ö€ Õ¡Õ¤Õ´Õ«Õ¶Õ«Õ½Õ¿Ö€Õ¡Õ¿Õ¸Ö€Õ¶ Õ¡Ö€Õ£Õ¥Õ¬Õ¥Õ¬ Õ§ Õ¡ÕµÕ½ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ³Õ¥Õ¶Õ¸Ö‚Õ´Õ¨Ö‰</translation>
<translation id="7135130955892390533">Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ«Õ³Õ¡Õ¯Õ¨</translation>
<translation id="7138472120740807366">Ô±Õ¼Õ¡Ö„Õ´Õ¡Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯</translation>
-<translation id="7139724024395191329">Ô·Õ´Õ«Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
<translation id="7139892792842608322">Õ€Õ«Õ´Õ¶Õ¡Õ¯Õ¡Õ¶ Õ¤Õ¡Ö€Õ¡Õ¯</translation>
<translation id="714064300541049402">ÕŠÕ¡Õ¿Õ¯Õ¥Ö€Õ¶Õ¥Ö€Õ« Õ¿Õ¥Õ²Õ¡Õ·Õ¡Ö€Õª Õ°Õ¡Õ¯Õ¡Õ¼Õ¡Õ¯ Õ¯Õ¸Õ²Õ´Õ«ÖÕ X Õ¡Õ¼Õ¡Õ¶ÖÖ„Õ¸Õ¾</translation>
<translation id="7152423860607593928">Number-14 (Õ®Ö€Õ¡Ö€)</translation>
@@ -2435,6 +2445,7 @@
<translation id="7669271284792375604">Ô±ÕµÕ½ Õ¯Õ¡ÕµÖ„Õ« Õ°Õ¡Ö€Õ±Õ¡Õ¯Õ¾Õ¸Õ²Õ¶Õ¥Ö€Õ¨ Õ±Õ¥Ö€ Õ½Õ¡Ö€Ö„Õ¸Ö‚Õ´ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ­Õ¡Õ¢Õ¥Õ¸Ö‚Õ©ÕµÕ¡Õ´Õ¢ Õ¿Õ¥Õ²Õ¡Õ¤Ö€Õ¥Õ¬ Õ¾Õ¶Õ¡Õ½Õ¡Õ¢Õ¥Ö€ Õ®Ö€Õ¡Õ£Ö€Õ¥Ö€, Õ¸Ö€Õ¸Õ¶Ö„ Õ¯Õ¡Õ¦Õ¤Õ¥Õ¶ Õ±Õ¥Ö€ Õ¤Õ«Õ¿Õ¡Ö€Õ¯Õ«Õ¹Õ« Õ¡Õ·Õ­Õ¡Õ¿Õ¡Õ¶Ö„Õ« Õ¾Ö€Õ¡ (Ö…Ö€Õ«Õ¶Õ¡Õ¯Õ Õ¯ÖƒÕ¸Õ­Õ¾Õ« Õ±Õ¥Ö€ Õ£Õ¬Õ­Õ¡Õ¾Õ¸Ö€ Õ§Õ»Õ¨ Õ¯Õ¡Õ´ Õ±Õ¥Ö€ Õ¡ÕµÖÕ¥Õ¬Õ¡Õ® Õ¯Õ¡ÕµÖ„Õ¥Ö€Õ¸Ö‚Õ´ Õ¡Õ¾Õ¥Õ¬Õ¸Ö€Õ¤ Õ£Õ¸Õ¾Õ¡Õ¦Õ¤Õ¶Õ¥Ö€ Õ¯ÖÕ¸Ö‚ÖÕ¡Õ¤Ö€Õ¾Õ¥Õ¶):</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Ô³Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ°Õ¥Õ¿, Õ¸Ö€Õ¸Õ¶Ö„ Õ¶Õ·Õ¾Õ¡Õ® Õ¥Õ¶ Õ¸Ö€ÕºÕ¥Õ½ Õ£Õ¡Õ²Õ¿Õ¶Õ« (Õ°Õ¡Õ·Õ«Õ¾ Õ´Õ¿Õ¶Õ¥Õ¬Õ¸Ö‚Ö Õ°Õ¥Õ¿Õ¸ 1 Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶)Ö‰ <ph name="BEGIN_LINK" />Ô»Õ´Õ¡Õ¶Õ¡Õ¬ Õ¡Õ¾Õ¥Õ¬Õ«Õ¶<ph name="END_LINK" />}one{Ô³Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ°Õ¥Õ¿, Õ¸Ö€Õ¸Õ¶Ö„ Õ¶Õ·Õ¾Õ¡Õ® Õ¥Õ¶ Õ¸Ö€ÕºÕ¥Õ½ Õ£Õ¡Õ²Õ¿Õ¶Õ« (Õ°Õ¡Õ·Õ«Õ¾ Õ´Õ¿Õ¶Õ¥Õ¬Õ¸Ö‚Ö Õ°Õ¥Õ¿Õ¸ # Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶)Ö‰ <ph name="BEGIN_LINK" />Ô»Õ´Õ¡Õ¶Õ¡Õ¬ Õ¡Õ¾Õ¥Õ¬Õ«Õ¶<ph name="END_LINK" />}other{Ô³Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ°Õ¥Õ¿, Õ¸Ö€Õ¸Õ¶Ö„ Õ¶Õ·Õ¾Õ¡Õ® Õ¥Õ¶ Õ¸Ö€ÕºÕ¥Õ½ Õ£Õ¡Õ²Õ¿Õ¶Õ« (Õ°Õ¡Õ·Õ«Õ¾ Õ´Õ¿Õ¶Õ¥Õ¬Õ¸Ö‚Ö Õ°Õ¥Õ¿Õ¸ # Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶)Ö‰ <ph name="BEGIN_LINK" />Ô»Õ´Õ¡Õ¶Õ¡Õ¬ Õ¡Õ¾Õ¥Õ¬Õ«Õ¶<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Õ“Õ¸Õ½Õ¿Õ¡Ö€Õ¯Õ² 6</translation>
+<translation id="7675325315208090829">Կառավարել վճարման եղանակները…</translation>
<translation id="7676643023259824263">ÕÕ¥Õ²Õ´Õ¡Õ¿Õ¡Õ­Õ¿Õ¡Õ¯Õ«Õ¶ ÕºÕ¡Õ°Õ¾Õ¡Õ® Õ¿Õ¥Ö„Õ½Õ¿Õ« Õ¸Ö€Õ¸Õ¶Õ¸Ö‚Õ´, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Ô´Õ«Õ¿Õ¥Õ¬ Ö‡ Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬ Õ¡ÕµÖÕ¥Õ¬Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Chrome-Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¸Ö‚Õ´</translation>
<translation id="7679947978757153706">Ô²Õ¥ÕµÕ½Õ¢Õ¸Õ¬</translation>
@@ -2477,7 +2488,6 @@
<translation id="7766518757692125295">Ô¿Õ«Õ½Õ¡Õ·Ö€Õ»Õ¡Õ¦Õ£Õ¥Õ½Õ¿</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Õ†Õ¸Ö‚ÕµÕ¶ Õ°Õ¥Ö€Õ©Õ¡Õ¯Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¡Õ´Õ¢Õ Õ¥Ö€Õ¥Õ½Õ« Õ¯Õ¸Õ²Õ´Õ¸Õ¾ Õ¾Õ¥Ö€Ö‡</translation>
-<translation id="777702478322588152">ÕŠÖ€Õ¥Ö†Õ¥Õ¯Õ¿Õ¸Ö‚Ö€Õ¡</translation>
<translation id="7791011319128895129">Õ‰Õ©Õ¸Õ²Õ¡Ö€Õ¯Õ¾Õ¡Õ®</translation>
<translation id="7791196057686275387">ÕÕ¥Õ²Õ´Õ¸Ö‚Õ´</translation>
<translation id="7791543448312431591">Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬</translation>
@@ -2568,12 +2578,12 @@
<translation id="8055534648776115597">Õ„Õ¡Õ½Õ¶Õ¡Õ£Õ«Õ¿Õ¡Õ¯Õ¡Õ¶ Ö‡ Õ·Õ¡Ö€Õ¸Ö‚Õ¶Õ¡Õ¯Õ¡Õ¯Õ¡Õ¶ Õ¯Ö€Õ©Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
<translation id="8057711352706143257">«<ph name="SOFTWARE_NAME" />» ծրագիրը սխալ է կազմաձևված: Ապատեղադրեք «<ph name="SOFTWARE_NAME" />» ծրագիրը: Դրանով սովորաբար խնդիրը լուծվում է: <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ÕÕ¶Õ¶Õ¤Õ« Õ¡Ö€Õ¿Õ¡Õ¤Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶</translation>
-<translation id="8066955247577885446">ÕÕ­Õ¡Õ¬ Õ¡Õ¼Õ¡Õ»Õ¡ÖÕ¡Õ¾:</translation>
<translation id="8067872629359326442">Ô´Õ¸Ö‚Ö„ Õ°Õ¥Õ¶Ö Õ¶Õ¸Ö€ Õ´Õ¸Ö‚Õ¿Ö„Õ¡Õ£Ö€Õ¥ÖÕ«Ö„ Õ±Õ¥Ö€ Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨ Õ¯Õ¡Õ½Õ¯Õ¡Õ®Õ¥Õ¬Õ« Õ¯Õ¡ÕµÖ„Õ¸Ö‚Õ´Ö‰ Chromium-Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ§ Ö…Õ£Õ¶Õ¥Õ¬Ö‰ ÕˆÖ€ÕºÕ¥Õ½Õ¦Õ« Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨ ÖƒÕ¸Õ­Õ¥Ö„ Ö‡ Google-Õ«Õ¶ Õ¿Õ¥Õ²Õ¥Õ¯Õ¡ÖÕ¶Õ¥Ö„, Õ¸Ö€ Õ±Õ¥Ö€ Õ°Õ¡Õ·Õ«Õ¾Õ¨ Õ¾Õ¿Õ¡Õ¶Õ£Õ¾Õ¡Õ® Õ§, Õ½Õ¥Õ²Õ´Õ¥Ö„ «Պաշտպանել հաշիվը»։</translation>
<translation id="8070439594494267500">Õ€Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ« ÕºÕ¡Õ¿Õ¯Õ¥Ö€Õ¡Õ¯</translation>
<translation id="8074253406171541171">10x13 (Õ®Ö€Õ¡Ö€)</translation>
<translation id="8075736640322370409">Ô±Ö€Õ¡Õ£ Õ½Õ¿Õ¥Õ²Õ®Õ¥Õ¬ Google Õ¡Õ²ÕµÕ¸Ö‚Õ½Õ¡Õ¯</translation>
<translation id="8075898834294118863">Ô¿Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬ Õ¯Õ¡ÕµÖ„Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨</translation>
+<translation id="8076492880354921740">Õ†Õ¥Ö€Õ¤Õ«Ö€Õ¶Õ¥Ö€</translation>
<translation id="8078141288243656252">Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ®Õ¡Õ¶Õ¸Õ©Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Õ¡Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬, Õ¥Ö€Õ¢ ÖƒÕ¡Õ½Õ¿Õ¡Õ©Õ¸Ö‚Õ²Õ©Õ¨ ÕºÕ¿Õ¿Õ¾Õ¡Õ® Õ§</translation>
<translation id="8079031581361219619">ÕŽÕ¥Ö€Õ¡Õ¢Õ¥Õ¼Õ¶Õ¥ÕžÕ¬ Õ¯Õ¡ÕµÖ„Õ¨</translation>
<translation id="8081087320434522107">ÕÕ¥Õ¤Õ¡Õ¶Õ¶Õ¥Ö€</translation>
@@ -2696,6 +2706,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ô¿Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€</translation>
+<translation id="8428634594422941299">ÕŠÕ¡Ö€Õ¦ Õ§</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />Ö‰ Chrome-Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¸Ö‚Õ´ Ö„Õ¸Ö‚Ö„Õ«Õ¶Õ¥Ö€Õ« ÕºÕ¡Ö€Õ¡Õ´Õ¥Õ¿Ö€Õ¥Ö€Õ¨ Õ¯Õ¡Õ¼Õ¡Õ¾Õ¡Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ½Õ¥Õ²Õ´Õ¥Ö„ Tab, Õ¡ÕºÕ¡Õ EnterÖ‰</translation>
<translation id="8433057134996913067">ÕÕ¡ Õ±Õ¥Õ¦ Õ¤Õ¸Ö‚Ö€Õ½ Õ¯Õ°Õ¡Õ¶Õ« Õ¯Õ¡ÕµÖ„Õ¥Ö€Õ«Ö Õ·Õ¡Õ¿Õ¥Ö€Õ«Ö:</translation>
<translation id="8434840396568290395">ÕÕ¶Õ¡ÕµÕ«Õ¶ Õ¯Õ¥Õ¶Õ¤Õ¡Õ¶Õ«Õ¶Õ¥Ö€</translation>
@@ -2793,6 +2804,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> Õ¯Õ¡ÕµÖ„Õ« Õ±Õ¥Ö€ Õ¯Õ¸Õ¤Õ¨Õ <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">Ô·Õ»Õ¡Õ¶Õ·Õ¥Õ¬ Õ¡ÕµÕ½ Õ¶Õ¥Ö€Õ¤Õ«Ö€Õ¨</translation>
<translation id="8751426954251315517">Õ“Õ¸Ö€Õ±Õ¥Ö„ Õ¡Õ¾Õ¥Õ¬Õ« Õ¸Ö‚Õ·</translation>
+<translation id="8757526089434340176">Õ€Õ¡Õ½Õ¡Õ¶Õ¥Õ¬Õ« Õ§ Google Pay-Õ« Õ¡Õ¼Õ¡Õ»Õ¡Ö€Õ¯</translation>
<translation id="8758885506338294482">Õ„Ö€ÖÕ¡Õ¯ÖÕ¡ÕµÕ«Õ¶ Õ¿Õ¥Õ½Õ¡Õ­Õ¡Õ²Õ¥Ö€</translation>
<translation id="8759274551635299824">Õ”Õ¡Ö€Õ¿Õ« ÕªÕ¡Õ´Õ¯Õ¥Õ¿Õ¨ Õ½ÕºÕ¡Õ¼Õ¾Õ¥Õ¬ Õ§</translation>
<translation id="87601671197631245">Ô±ÕµÕ½ Õ¯Õ¡ÕµÖ„Õ¶ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¸Ö‚Õ´ Õ§ Õ¡Õ¶Õ¾Õ¿Õ¡Õ¶Õ£Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ°Õ¶Õ¡ÖÕ¡Õ® Õ¯Õ¡Õ¦Õ´Õ¡Õ±Ö‡Õ¸Ö‚Õ´, Õ«Õ¶Õ¹Õ« Õ°Õ¥Õ¿Ö‡Õ¡Õ¶Ö„Õ¸Õ¾ Õ±Õ¥Ö€ Õ¿Õ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨ (Ö…Ö€.` Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¥Ö€Õ¨, Õ°Õ¡Õ²Õ¸Ö€Õ¤Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨ Õ¯Õ¡Õ´ Õ¾Õ¡Ö€Õ¯Õ¡ÕµÕ«Õ¶ Ö„Õ¡Ö€Õ¿Õ« Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨) Õ¡ÕµÕ½ Õ¯Õ¡ÕµÖ„Õ¸Ö‚Õ´ Õ¬Ö€Õ¡ÖÕ¶Õ¥Õ¬Õ«Õ½ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ¶ Õ¢Õ¡ÖÕ¡Õ°Õ¡ÕµÕ¿Õ¾Õ¥Õ¬Ö‰</translation>
@@ -2800,6 +2812,7 @@
<translation id="8763927697961133303">USB Õ½Õ¡Ö€Ö„</translation>
<translation id="8763986294015493060">Õ“Õ¡Õ¯Õ¥Õ¬ Õ¨Õ¶Õ©Õ¡ÖÕ«Õ¯ Õ¢Õ¡ÖÕ¾Õ¡Õ® Õ¢Õ¸Õ¬Õ¸Ö€ Õ«Õ¶Õ¯Õ¸Õ£Õ¶Õ«Õ¿Õ¸ ÕºÕ¡Õ¿Õ¸Ö‚Õ°Õ¡Õ¶Õ¶Õ¥Ö€Õ¨</translation>
<translation id="8766943070169463815">ÕŠÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¾Õ¡Õ® Õ¾Õ³Õ¡Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ« Õ´Õ¸Ö‚Õ¿Ö„Õ¡ÕµÕ«Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ¶Õ¸Ö‚ÕµÕ¶Õ¡Õ¯Õ¡Õ¶Õ¡ÖÕ´Õ¡Õ¶ Õ§Õ¯Ö€Õ¡Õ¶Õ¨ Õ¢Õ¡ÖÕ¾Õ¡Õ® Õ§</translation>
+<translation id="8767765348545497220">Õ“Õ¡Õ¯Õ¥Õ¬ Õ°Õ¸Ö‚Õ·Õ´Õ¡Õ¶ Õ¡Õ´ÕºÕ«Õ¯Õ¨</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Õ„Õ¸Õ¿Õ¸ÖÕ«Õ¯Õ¬Õ¥Õ¿Õ¶Õ¥Ö€</translation>
<translation id="8790007591277257123">&amp;ÕŽÕ¥Ö€Õ¡Ö€Õ¯Õ¥Õ¬ Õ»Õ¶Õ»Õ¸Ö‚Õ´Õ¨</translation>
@@ -2812,6 +2825,7 @@
<translation id="8806285662264631610">Ô¼Õ¸Õ£Õ¡Õ¶Ö„Õ« Ö‡ Õ´Õ¡Ö€Õ´Õ¶Õ« Õ­Õ¶Õ¡Õ´Ö„Õ« Õ¯Õ¸Õ½Õ´Õ¥Õ¿Õ«Õ¯ Õ´Õ«Õ»Õ¸ÖÕ¶Õ¥Ö€</translation>
<translation id="8807160976559152894">Ô¿Õ¿Ö€Õ¥Õ¬ ÕµÕ¸Ö‚Ö€Õ¡Ö„Õ¡Õ¶Õ¹ÕµÕ¸Ö‚Ö€ Õ§Õ»Õ«Ö Õ°Õ¥Õ¿Õ¸</translation>
<translation id="8808828119384186784">Chrome-Õ« Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€</translation>
+<translation id="8813277370772331957">Õ€Õ«Õ·Õ¥ÖÕ¶Õ¥Õ¬ Õ¡Õ¾Õ¥Õ¬Õ« Õ¸Ö‚Õ·</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />Ö‰ Chrome-Õ¨ Õ©Õ¡Ö€Õ´Õ¡ÖÕ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ½Õ¥Õ²Õ´Õ¥Ö„ Tab, Õ¡ÕºÕ¡Õ EnterÖ‰</translation>
<translation id="8820817407110198400">Ô·Õ»Õ¡Õ¶Õ«Õ·Õ¶Õ¥Ö€</translation>
<translation id="882338992931677877">ÕÕ¥Õ¼Ö„Õ¸Õ¾ Õ¦Õ¥Õ¿Õ¥Õ²Õ´Õ¡Õ¶ Õ¤Õ¡Ö€Õ¡Õ¯</translation>
@@ -2991,6 +3005,7 @@
<translation id="988159990683914416">Õ„Õ·Õ¡Õ¯Õ¸Õ²Õ¶Õ¥Ö€Õ« Õ¿Õ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯</translation>
<translation id="989988560359834682">Õ“Õ¸Õ­Õ¥Õ¬ Õ°Õ¡Õ½ÖÕ¥Õ¶</translation>
<translation id="991413375315957741">Õ·Õ¡Ö€ÕªÕ´Õ¡Õ¶ Ö‡ Õ¬Õ¸Ö‚Õ½Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ¿Õ¾Õ«Õ¹Õ¶Õ¥Ö€</translation>
+<translation id="992110854164447044">ÕŽÕ«Ö€Õ¿Õ¸Ö‚Õ¡Õ¬ Ö„Õ¡Ö€Õ¿Õ¨ Õ©Õ¡Ö„ÖÕ¶Õ¸Ö‚Õ´ Õ§ Õ±Õ¥Ö€ Õ«Ö€Õ¡Õ¯Õ¡Õ¶ Ö„Õ¡Ö€Õ¿Õ¨Õ Ö…Õ£Õ¶Õ¥Õ¬Õ¸Õ¾ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¥Õ¬ Õ±Õ¥Õ¦ Õ°Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ­Õ¡Ö€Õ¤Õ¡Õ­Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ«ÖÖ‰ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ÕŽÕ¡Ö€Õ¤Õ¡Õ£Õ¸Ö‚ÕµÕ¶</translation>
<translation id="992432478773561401">«<ph name="SOFTWARE_NAME" />» Õ®Ö€Õ¡Õ£Õ«Ö€Õ¨ Õ±Õ¥Ö€ Õ°Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£Õ¹Õ¸Ö‚Õ´ Õ¯Õ¡Õ´ ÖÕ¡Õ¶ÖÕ¸Ö‚Õ´ Õ³Õ«Õ·Õ¿ Õ¹Õ§ տեղադրվել․
diff --git a/chromium/components/strings/components_strings_id.xtb b/chromium/components/strings/components_strings_id.xtb
index c3de93ec995..9f17054a2b4 100644
--- a/chromium/components/strings/components_strings_id.xtb
+++ b/chromium/components/strings/components_strings_id.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Hibah, beasiswa &amp; bantuan keuangan</translation>
<translation id="1048785276086539861">Jika Anda mengedit anotasi, dokumen ini akan kembali ke tampilan satu halaman</translation>
<translation id="1050038467049342496">Tutup aplikasi lain</translation>
+<translation id="1053959602163383901">Anda memilih untuk memverifikasi dengan perangkat pengautentikasi di situs yang menggunakan <ph name="PROVIDER_ORIGIN" />. Penyedia ini mungkin telah menyimpan informasi tentang metode pembayaran Anda, yang dapat Anda <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Urungkan Penambahan</translation>
<translation id="1056663316309890343">Software foto</translation>
<translation id="1056898198331236512">Peringatan</translation>
@@ -119,6 +120,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="1270502636509132238">Metode Pengambilan</translation>
<translation id="1281476433249504884">Tempat kertas 1</translation>
<translation id="1285320974508926690">Jangan pernah terjemahkan situs ini</translation>
+<translation id="1288548991597756084">Simpan kartu dengan aman</translation>
<translation id="1292571435393770077">Baki 16</translation>
<translation id="1292701964462482250">"Software di komputer mencegah Chrome terhubung dengan aman ke web" (khusus komputer Windows)</translation>
<translation id="1294154142200295408">Variasi baris perintah</translation>
@@ -223,6 +225,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
&lt;p&gt;Untuk memperbaiki error, klik &lt;strong&gt;Sambungkan&lt;/strong&gt; di halaman yang ingin Anda buka.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Desain lanskap</translation>
<translation id="1513706915089223971">Daftar entri histori</translation>
+<translation id="1516097932025103760">Kartu akan dienkripsi dan disimpan dengan aman, serta CVC tidak akan disimpan.</translation>
<translation id="1517433312004943670">Perlu nomor telepon</translation>
<translation id="1519264250979466059">Tanggal Dibuat</translation>
<translation id="1521159554480556801">Seni serat &amp; tekstil</translation>
@@ -288,6 +291,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="1662550410081243962">Simpan dan isi metode pembayaran</translation>
<translation id="1663943134801823270">Kartu dan alamat berasal dari Chrome. Anda dapat mengelolanya di <ph name="BEGIN_LINK" />Setelan<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Mulai sekarang, halaman dalam bahasa <ph name="SOURCE_LANGUAGE" /> akan diterjemahkan ke dalam bahasa <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Check out dengan <ph name="CARD_DETAIL" /> untuk menggunakan penawaran</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> ke <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Tepi pendek lebih dulu</translation>
<translation id="168693727862418163">Nilai kebijakan ini gagal memvalidasi skemanya dan akan diabaikan.</translation>
@@ -306,6 +310,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="1717218214683051432">Sensor gerakan</translation>
<translation id="1717494416764505390">Kotak surat 3</translation>
<translation id="1718029547804390981">Dokumen terlalu besar untuk dianotasi</translation>
+<translation id="1720941539803966190">Tutup tutorial</translation>
<translation id="1721424275792716183">* Kolom wajib diisi</translation>
<translation id="1727613060316725209">Sertifikat valid</translation>
<translation id="1727741090716970331">Tambahkan Nomor Kartu yang Valid</translation>
@@ -418,8 +423,8 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="205212645995975601">BBQ &amp; pemanggangan</translation>
<translation id="2053111141626950936">Halaman dalam bahasa <ph name="LANGUAGE" /> tidak akan diterjemahkan.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Jika kontrol ini aktif dan statusnya masih berlaku, Chrome akan menentukan grup besar, atau "kohor", yang paling mirip dengan aktivitas penjelajahan terbaru Anda. Pengiklan dapat memilih iklan untuk grup tersebut dan aktivitas penjelajahan Anda tetap bersifat pribadi di perangkat Anda. Grup Anda diperbarui setiap hari.}=1{Jika kontrol ini aktif dan statusnya masih berlaku, Chrome akan menentukan grup besar, atau "kohor", yang paling mirip dengan aktivitas penjelajahan terbaru Anda. Pengiklan dapat memilih iklan untuk grup tersebut dan aktivitas penjelajahan Anda tetap bersifat pribadi di perangkat Anda. Grup Anda diperbarui setiap hari.}other{Jika kontrol ini aktif dan statusnya masih berlaku, Chrome akan menentukan grup besar, atau "kohor", yang paling mirip dengan aktivitas penjelajahan terbaru Anda. Pengiklan dapat memilih iklan untuk grup tersebut dan aktivitas penjelajahan Anda tetap bersifat pribadi di perangkat Anda. Grup Anda diperbarui setiap {NUM_DAYS} hari.}}</translation>
-<translation id="2053553514270667976">Kode pos</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 saran}other{# saran}}</translation>
+<translation id="2066915425250589881">minta untuk dihapus</translation>
<translation id="2068528718802935086">Bayi &amp; balita</translation>
<translation id="2071156619270205202">Kartu ini tidak memenuhi syarat untuk nomor kartu virtual.</translation>
<translation id="2071692954027939183">Notifikasi otomatis diblokir karena Anda biasanya tidak mengizinkannya</translation>
@@ -431,7 +436,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="2088086323192747268">Tombol Kelola sinkronisasi, tekan Enter untuk mengelola info apa saja yang Anda sinkronkan di setelan Chrome</translation>
<translation id="2091887806945687916">Suara</translation>
<translation id="2094505752054353250">Ketidakcocokan domain</translation>
-<translation id="2096368010154057602">Departemen</translation>
<translation id="2099652385553570808">Tiga jepretan di kiri</translation>
<translation id="2101225219012730419">Versi:</translation>
<translation id="2102134110707549001">Sarankan Sandi yang Kuat…</translation>
@@ -468,7 +472,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="2185836064961771414">Sepak bola Amerika</translation>
<translation id="2187317261103489799">Deteksi (default)</translation>
<translation id="2188375229972301266">Beberapa lubang di bawah</translation>
-<translation id="2188852899391513400">Sandi yang baru saja Anda gunakan terekspos dalam pelanggaran data. Untuk mengamankan akun Anda, Pengelola Sandi Google merekomendasikan untuk mengubah sandi tersebut sekarang lalu memeriksa sandi tersimpan Anda.</translation>
<translation id="219906046732893612">Renovasi rumah</translation>
<translation id="2202020181578195191">Masukkan tahun habis masa berlaku yang valid</translation>
<translation id="22081806969704220">Baki 3</translation>
@@ -479,6 +482,8 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="2215727959747642672">Pengeditan file</translation>
<translation id="2215963164070968490">Anjing</translation>
<translation id="2218879909401188352">Penyerang yang saat ini berada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin berusaha menginstal aplikasi berbahaya yang dapat merusak perangkat, menambahkan biaya tersembunyi ke tagihan seluler, atau mencuri informasi pribadi Anda. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Mulai ulang tutorial</translation>
+<translation id="2219735899272417925">Perangkat perlu direset</translation>
<translation id="2224337661447660594">Tidak ada internet</translation>
<translation id="2230458221926704099">Perbaiki sambungan menggunakan <ph name="BEGIN_LINK" />aplikasi diagnosis<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Kirim sekarang</translation>
@@ -576,11 +581,13 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="2512101340618156538">Tidak diizinkan (default)</translation>
<translation id="2512413427717747692">Tombol Setel Chrome sebagai browser default, tekan Enter untuk menyetel Chrome sebagai browser default sistem di setelan iOS</translation>
<translation id="2515629240566999685">Periksa sinyal di area Anda</translation>
+<translation id="2515761554693942801">Anda memilih untuk memverifikasi dengan Touch ID di situs yang menggunakan <ph name="PROVIDER_ORIGIN" />. Penyedia ini mungkin telah menyimpan informasi tentang metode pembayaran Anda, yang dapat Anda <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Jepretan di kanan bawah</translation>
<translation id="2521736961081452453">Buat formulir</translation>
<translation id="2523886232349826891">Hanya disimpan di perangkat ini</translation>
<translation id="2524461107774643265">Tambahkan Informasi Lainnya</translation>
<translation id="2529899080962247600">Kolom ini tidak boleh memiliki lebih dari <ph name="MAX_ITEMS_LIMIT" /> entri. Semua entri berikutnya akan diabaikan.</translation>
+<translation id="253493526287553278">Lihat detail kode promo</translation>
<translation id="2535585790302968248">Buka tab Samaran baru untuk menjelajah secara pribadi</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{dan 1 lainnya}other{dan # lainnya}}</translation>
<translation id="2536110899380797252">Tambahkan Alamat</translation>
@@ -616,7 +623,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="259821504105826686">Seni fotografi &amp; digital</translation>
<translation id="2601150049980261779">Film romantis</translation>
<translation id="2604589665489080024">Musik pop</translation>
-<translation id="2609632851001447353">Variasi</translation>
<translation id="2610561535971892504">Klik untuk menyalin</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />tidak akan menyimpan<ph name="END_EMPHASIS" /> informasi berikut:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Hari ulang tahun &amp; pemberian nama</translation>
<translation id="2677748264148917807">Keluar</translation>
+<translation id="2679714844901977852">Menyimpan info kartu dan tagihan ke Akun Google Anda <ph name="USER_EMAIL" /> untuk checkout yang lebih cepat dan aman</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Paling pas</translation>
<translation id="2688969097326701645">Ya, lanjutkan</translation>
@@ -697,7 +704,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="2854764410992194509">Internet service provider (ISP)</translation>
<translation id="2856444702002559011">Penyerang mungkin berusaha mencuri informasi Anda dari <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (misalnya, sandi, pesan, atau kartu kredit). <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Situs ini menampilkan iklan yang mengganggu atau menyesatkan.</translation>
-<translation id="286512204874376891">Kartu virtual menyamarkan kartu Anda yang sebenarnya untuk membantu melindungi Anda dari kemungkinan penipuan. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Bersahabat</translation>
<translation id="28761159517501904">Film</translation>
<translation id="2876489322757410363">Keluar dari mode Samaran untuk membayar melalui aplikasi eksternal. Lanjutkan?</translation>
@@ -798,7 +804,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">Wi-Fi yang digunakan mungkin mewajibkan Anda mengunjungi halaman masuknya.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Pulau</translation>
<translation id="3176929007561373547">Periksa setelan proxy atau hubungi administrator jaringan untuk
memastikan bahwa server proxy bekerja. Jika Anda tidak yakin harus
menggunakan server proxy:
@@ -877,9 +882,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="3369192424181595722">Kesalahan jam</translation>
<translation id="3369459162151165748">Suku cadang &amp; aksesori kendaraan</translation>
<translation id="3371064404604898522">Setel Chrome sebagai browser default</translation>
-<translation id="3371076217486966826"><ph name="URL" /> ingin:
- • Membuat peta 3D area di sekeliling Anda dan melacak posisi kamera
- • Menggunakan kamera Anda</translation>
<translation id="337363190475750230">Dicabut aksesnya</translation>
<translation id="3375754925484257129">Jalankan pemeriksaan keamanan Chrome</translation>
<translation id="3377144306166885718">Server menggunakan versi TLS yang sudah tidak digunakan lagi.</translation>
@@ -896,6 +898,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="3399952811970034796">Alamat Pengiriman</translation>
<translation id="3402261774528610252">Koneksi yang digunakan untuk memuat situs ini menggunakan TLS 1.0 atau TLS 1.1 yang tidak digunakan lagi dan akan dinonaktifkan pada waktu mendatang. Setelah dinonaktifkan, pengguna tidak akan dapat memuat situs ini. Sebaiknya server mengaktifkan TLS 1.2 atau yang lebih baru.</translation>
<translation id="3405664148539009465">Sesuaikan font</translation>
+<translation id="3407789382767355356">login pihak ketiga</translation>
<translation id="3409896703495473338">Kelola setelan keamanan</translation>
<translation id="3414952576877147120">Ukuran:</translation>
<translation id="3417660076059365994">File yang Anda upload atau lampirkan akan dikirimkan ke Google Cloud atau pihak ketiga untuk analisis. Misalnya, file mungkin dipindai untuk mendeteksi data sensitif atau malware.</translation>
@@ -928,6 +931,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="3477679029130949506">Daftar film &amp; jadwal bioskop</translation>
<translation id="3479552764303398839">Jangan sekarang</translation>
<translation id="3484560055331845446">Anda dapat kehilangan akses ke Akun Google Anda. Chrome merekomendasikan untuk mengubah sandi Anda sekarang. Anda akan diminta untuk login.</translation>
+<translation id="3484861421501147767">Pengingat: Kode promo yang disimpan tersedia</translation>
<translation id="3487845404393360112">Baki 4</translation>
<translation id="3495081129428749620">Cari pada halaman <ph name="PAGE_TITLE" /></translation>
<translation id="350069200438440499">Nama file:</translation>
@@ -1051,6 +1055,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="3810973564298564668">Kelola</translation>
<translation id="3816482573645936981">Nilai (digantikan)</translation>
<translation id="382518646247711829">Jika Anda menggunakan server proxy...</translation>
+<translation id="3826050100957962900">Login pihak ketiga</translation>
<translation id="3827112369919217609">Absolut</translation>
<translation id="3827666161959873541">Film keluarga</translation>
<translation id="3828924085048779000">Frasa sandi kosong tidak dibolehkan.</translation>
@@ -1063,9 +1068,9 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="3858027520442213535">Perbarui tanggal dan waktu</translation>
<translation id="3858860766373142691">Nama</translation>
<translation id="3872834068356954457">Sains</translation>
+<translation id="3875783148670536197">Lihat Caranya</translation>
<translation id="3881478300875776315">Tampilkan lebih sedikit baris</translation>
<translation id="3884278016824448484">Pengenal perangkat bertentangan</translation>
-<translation id="3885155851504623709">Paroki</translation>
<translation id="388632593194507180">Pemantauan Terdeteksi</translation>
<translation id="3886948180919384617">Tempat kertas 3</translation>
<translation id="3890664840433101773">Tambahkan email</translation>
@@ -1095,9 +1100,11 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="3973234410852337861"><ph name="HOST_NAME" /> diblokir.</translation>
<translation id="3978338123949022456">Mode penelusuran, ketik kueri lalu tekan Enter untuk menelusuri <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Burung</translation>
+<translation id="3985750352229496475">Kelola Alamat ...</translation>
<translation id="3986705137476756801">Nonaktifkan Teks Otomatis untuk saat ini</translation>
<translation id="3987940399970879459">Kurang dari 1 MB</translation>
<translation id="3990250421422698716">Jog offset</translation>
+<translation id="3992684624889376114">Tentang halaman ini</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> situs telah meminta kebijakan asal agar diterapkan ke semua permintaan yang ditujukan kepadanya, tetapi kebijakan ini tidak dapat diterapkan untuk saat ini.</translation>
<translation id="4006465311664329701">Metode Pembayaran, Penawaran, dan Alamat yang Menggunakan Google Pay</translation>
<translation id="4009243425692662128">Konten halaman yang Anda cetak akan dikirim ke Google Cloud atau pihak ketiga untuk dianalisis. Misalnya, teks mungkin dipindai untuk mendeteksi data sensitif.</translation>
@@ -1217,6 +1224,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="4305666528087210886">File Anda tidak dapat diakses</translation>
<translation id="4306529830550717874">Simpan alamat?</translation>
<translation id="4306812610847412719">papan klip</translation>
+<translation id="4310070645992025887">Telusuri Perjalanan Anda</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokir (default)</translation>
<translation id="4314815835985389558">Kelola sinkronisasi</translation>
@@ -1267,6 +1275,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Penggunaan proxy dinonaktifkan tetapi konfigurasi proxy yang eksplisit ditentukan.</translation>
<translation id="4441832193888514600">Diabaikan karena kebijakan ini hanya dapat disetel sebagai kebijakan pengguna cloud.</translation>
+<translation id="4442470707340296952">Tab Chrome</translation>
<translation id="4450893287417543264">Jangan tampilkan lagi</translation>
<translation id="4451135742916150903">Dapat meminta untuk terhubung ke perangkat HID</translation>
<translation id="4452328064229197696">Sandi yang baru saja Anda gunakan terekspos dalam pelanggaran data. Untuk mengamankan akun Anda, Pengelola Sandi Google merekomendasikan untuk memeriksa sandi tersimpan Anda.</translation>
@@ -1382,7 +1391,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="4761869838909035636">Jalankan Pemeriksaan Keamanan Chrome</translation>
<translation id="4764776831041365478">Halaman web di <ph name="URL" /> mungkin sedang tidak aktif untuk sementara atau dipindahkan secara permanen ke alamat web baru.</translation>
<translation id="4766713847338118463">Dua jepretan di bawah</translation>
-<translation id="4771973620359291008">Terjadi kesalahan yang tidak diketahui.</translation>
+<translation id="4771973620359291008">Terjadi error yang tidak diketahui.</translation>
<translation id="477945296921629067">{NUM_POPUPS,plural, =1{Pop-up diblokir}other{# pop-up diblokir}}</translation>
<translation id="4780366598804516005">Kotak surat 1</translation>
<translation id="4785376858512657294">Kelola Akun Google</translation>
@@ -1405,6 +1414,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="483241715238664915">Aktifkan peringatan</translation>
<translation id="4834250788637067901">Metode pembayaran, penawaran, dan alamat yang menggunakan Google Pay</translation>
<translation id="4838327282952368871">Sangat Indah</translation>
+<translation id="4839087176073128681">Bayar lebih cepat dan lindungi kartu Anda dengan keamanan terbaik di industri dari Google.</translation>
<translation id="4840250757394056958">Lihat histori Chrome Anda</translation>
<translation id="484462545196658690">Otomatis</translation>
<translation id="484671803914931257">Dapatkan diskon di <ph name="MERCHANT_NAME" /> dan lainnya</translation>
@@ -1467,6 +1477,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="5011561501798487822">Bahasa yang Terdeteksi</translation>
<translation id="5015510746216210676">Nama Mesin:</translation>
<translation id="5017554619425969104">Teks yang Anda salin</translation>
+<translation id="5017828934289857214">Ingatkan Saya Nanti</translation>
<translation id="5018422839182700155">Tidak dapat membuka halaman ini</translation>
<translation id="5019198164206649151">Penyimpanan cadangan dalam kondisi buruk</translation>
<translation id="5020776957610079374">Musik dunia</translation>
@@ -1486,6 +1497,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="5051305769747448211">Komedi live</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Untuk mengirim file ini menggunakan Berbagi Langsung, kosongkan ruang penyimpanan (<ph name="DISK_SPACE_SIZE" />) di perangkat Anda}other{Untuk mengirim file ini menggunakan Berbagi Langsung, kosongkan ruang penyimpanan (<ph name="DISK_SPACE_SIZE" />) di perangkat Anda}}</translation>
<translation id="5056549851600133418">Artikel untuk Anda</translation>
+<translation id="5060483733937416656">Anda memilih untuk memverifikasi dengan Windows Hello di situs yang menggunakan <ph name="PROVIDER_ORIGIN" />. Penyedia ini mungkin telah menyimpan informasi tentang metode pembayaran Anda, yang dapat Anda <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Mungkin maksud Anda <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Histori pencetakan</translation>
<translation id="5068234115460527047">Dana lindung nilai</translation>
@@ -1499,10 +1511,8 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="5087286274860437796">Sertifikat server saat ini tidak valid.</translation>
<translation id="5087580092889165836">Tambahkan kartu</translation>
<translation id="5088142053160410913">Pesan untuk operator</translation>
-<translation id="5089810972385038852">Negara Bagian</translation>
<translation id="5093232627742069661">Lipatan Z</translation>
<translation id="5094747076828555589">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya tidak dipercaya oleh Chromium. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau penyerang memotong sambungan Anda.</translation>
-<translation id="5095208057601539847">Provinsi</translation>
<translation id="5097099694988056070">Statistik perangkat seperti penggunaan CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Situs tidak aman</translation>
@@ -1540,6 +1550,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="5171045022955879922">Telusuri atau ketik URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Mesin</translation>
+<translation id="5177076414499237632">Pelajari sumber &amp; topik halaman ini</translation>
<translation id="5179510805599951267">Bukan <ph name="ORIGINAL_LANGUAGE" />? Laporkan kesalahan deteksi ini</translation>
<translation id="518639307526414276">Makanan &amp; perlengkapan perawatan hewan peliharaan</translation>
<translation id="5190835502935405962">Bilah Bookmark</translation>
@@ -1700,6 +1711,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="5624120631404540903">Kelola sandi</translation>
<translation id="5629630648637658800">Gagal memuat setelan kebijakan</translation>
<translation id="5631439013527180824">Token pengelolaan perangkat tidak valid</translation>
+<translation id="5632485077360054581">Lihat caranya</translation>
<translation id="5633066919399395251">Penyerang yang saat ini berada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin berusaha menginstal program berbahaya di komputer Anda untuk mencuri atau menghapus informasi Anda (misalnya, foto, sandi, pesan, dan kartu kredit). <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Konten penipuan diblokir.</translation>
<translation id="5633259641094592098">Film kultus &amp; indie</translation>
@@ -1748,7 +1760,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="5781136890105823427">Eksperimen diaktifkan</translation>
<translation id="578305955206182703">Oranye kekuningan</translation>
<translation id="57838592816432529">Bisukan</translation>
-<translation id="5784606427469807560">Terjadi masalah saat mengonfirmasi kartu. Periksa koneksi internet Anda dan coba lagi.</translation>
+<translation id="5784606427469807560">Terjadi error saat mengonfirmasi kartu. Periksa koneksi internet Anda dan coba lagi.</translation>
<translation id="5785756445106461925">Selain itu, halaman ini berisi sumber daya lainnya yang tidak aman. Sumber daya ini dapat dilihat oleh orang lain saat transit dan dapat dimodifikasi oleh penyerang untuk mengubah tampilan perangkat.</translation>
<translation id="5786044859038896871">Ingin mengisi informasi kartu?</translation>
<translation id="578633867165174378">Chrome menemukan sandi yang baru saja Anda gunakan dalam pelanggaran data. Sebaiknya ubah sandi ini sekarang.</translation>
@@ -1817,6 +1829,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="5989320800837274978">Baik proxy server tetap ataupun URL skrip .pac tidak ditentukan.</translation>
<translation id="5992691462791905444">Lipatan format engineering Z</translation>
<translation id="5995727681868049093">Kelola info, privasi, dan keamanan di Akun Google Anda</translation>
+<translation id="5997247540087773573">Sandi yang baru saja Anda gunakan terekspos dalam pelanggaran data. Untuk mengamankan akun Anda, Pengelola Sandi Google merekomendasikan untuk mengubah sandi tersebut sekarang dan memeriksa sandi tersimpan Anda.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> hasil untuk '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">Klasik</translation>
<translation id="6008122969617370890">Urutan N-ke-1</translation>
@@ -1912,7 +1925,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="627746635834430766">Untuk membayar lebih cepat di pembelian selanjutnya, simpan kartu dan alamat penagihan ke Akun Google Anda.</translation>
<translation id="6279183038361895380">Tekan |<ph name="ACCELERATOR" />| untuk menampilkan kursor</translation>
<translation id="6280223929691119688">Tidak dapat mengirim ke alamat ini. Pilih alamat lain.</translation>
-<translation id="6282194474023008486">Kode pos</translation>
<translation id="6285507000506177184">Tombol Kelola download di Chrome, tekan Enter untuk mengelola file yang telah Anda download di Chrome</translation>
<translation id="6289939620939689042">Warna Halaman</translation>
<translation id="6290238015253830360">Artikel yang disarankan ditampilkan di sini</translation>
@@ -1934,6 +1946,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="6337133576188860026">Sediakan ruang kurang dari <ph name="SIZE" />. Beberapa situs mungkin dimuat lebih lambat saat dibuka lagi.</translation>
<translation id="6337534724793800597">Filter kebijakan menurut nama</translation>
<translation id="6340739886198108203">Kebijakan administrator tidak merekomendasikan untuk mengambil screenshot atau merekam layar saat konten rahasia terlihat:</translation>
+<translation id="6348220984832452017">Variasi Aktif</translation>
<translation id="6349101878882523185">Instal <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Tidak ada}=1{1 sandi (untuk <ph name="DOMAIN_LIST" />, disinkronkan)}=2{2 sandi (untuk <ph name="DOMAIN_LIST" />, disinkronkan)}other{# sandi (untuk <ph name="DOMAIN_LIST" />, disinkronkan)}}</translation>
<translation id="6355392890578844978">Browser ini tidak dikelola oleh perusahaan atau organisasi lain. Aktivitas di perangkat ini mungkin dikelola di luar Chromium. <ph name="BEGIN_LINK" />Pelajari lebih lanjut<ph name="END_LINK" /></translation>
@@ -1965,7 +1978,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="643051589346665201">Ubah sandi Google</translation>
<translation id="6433490469411711332">Edit info kontak</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> menolak untuk terhubung.</translation>
-<translation id="6438025220577812695">Ubah sendiri</translation>
<translation id="6440503408713884761">Diabaikan</translation>
<translation id="6443406338865242315">Ekstensi dan plugin yang telah Anda instal</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2095,9 +2107,9 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Terjemahkan</translation>
<translation id="6833752742582340615">Simpan info kartu dan tagihan ke Akun Google Anda untuk checkout yang lebih cepat dan aman</translation>
-<translation id="6839929833149231406">Wilayah</translation>
<translation id="6846340164947227603">Gunakan nomor kartu virtual...</translation>
<translation id="6852204201400771460">Muat ulang aplikasi?</translation>
+<translation id="6857776781123259569">Kelola Sandi ...</translation>
<translation id="686485648936420384">Referensi konsumen</translation>
<translation id="6865412394715372076">Kartu ini tidak dapat diverifikasi sekarang</translation>
<translation id="6869334554832814367">Pinjaman pribadi</translation>
@@ -2146,7 +2158,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="6965978654500191972">Perangkat</translation>
<translation id="696703987787944103">Perseptual</translation>
<translation id="6968269510885595029">Gunakan Kunci Keamanan</translation>
-<translation id="6970216967273061347">Distrik</translation>
<translation id="6971439137020188025">Buat presentasi Google baru di Slide dengan cepat</translation>
<translation id="6972629891077993081">Perangkat HID</translation>
<translation id="6973656660372572881">Server proxy tetap dan URL skrip .pac telah ditentukan.</translation>
@@ -2185,7 +2196,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="7081308185095828845">Fitur ini tidak tersedia di perangkat Anda</translation>
<translation id="7083258188081898530">Baki 9</translation>
<translation id="7086090958708083563">Upload yang diminta oleh pengguna</translation>
-<translation id="7087282848513945231">County</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, tekan Tab lalu Enter untuk mengelola izin dan data yang disimpan di seluruh situs di setelan Chrome</translation>
<translation id="7096937462164235847">Identitas situs ini tidak terverifikasi.</translation>
<translation id="7101893872976785596">Film horor</translation>
@@ -2204,10 +2214,10 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<ph name="LIST_ITEM" />Informasi yang dimasukkan dalam formulir<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Tidak dapat mengirim ke alamat ini. Pilih alamat lain.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Admin Anda telah melarang data ini disalin.</translation>
<translation id="7135130955892390533">Tampilkan status</translation>
<translation id="7138472120740807366">Metode pengiriman</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Baki Utama</translation>
<translation id="714064300541049402">Perpindahan image X sisi 2</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2422,6 +2432,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="7669271284792375604">Penyerang di situs ini mungkin berusaha mengelabui Anda agar memasang program yang dapat membahayakan pengalaman menjelajah Anda (misalnya dengan mengubah beranda Anda atau menayangkan iklan ekstra pada situs yang dikunjungi).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Tindakan yang dilakukan dengan data yang ditandai sebagai rahasia (1 tindakan sejak login). <ph name="BEGIN_LINK" />Pelajari lebih lanjut<ph name="END_LINK" />}other{Tindakan yang dilakukan dengan data yang ditandai sebagai rahasia (# tindakan sejak login). <ph name="BEGIN_LINK" />Pelajari lebih lanjut<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Kotak surat 6</translation>
+<translation id="7675325315208090829">Kelola Metode Pembayaran ...</translation>
<translation id="7676643023259824263">Telusuri teks papan klip, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Lihat dan kelola histori penjelajahan Anda di setelan Chrome</translation>
<translation id="7679947978757153706">Bisbol</translation>
@@ -2464,7 +2475,6 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="7766518757692125295">Perbatasan</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Urutan yang sama menghadap ke atas</translation>
-<translation id="777702478322588152">Prefektur</translation>
<translation id="7791011319128895129">Belum dirilis</translation>
<translation id="7791196057686275387">Bale</translation>
<translation id="7791543448312431591">Tambahkan</translation>
@@ -2555,12 +2565,12 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="8055534648776115597">Pendidikan keterampilan &amp; berkelanjutan</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" tidak dikonfigurasi dengan benar. Biasanya masalah akan terselesaikan dengan meng-uninstal "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Produksi makanan</translation>
-<translation id="8066955247577885446">Maaf, ada masalah.</translation>
<translation id="8067872629359326442">Anda baru saja memasukkan sandi ke situs penipuan. Chromium dapat membantu. Untuk mengubah sandi dan memberi tahu Google bahwa akun Anda mungkin berisiko, klik Lindungi Akun.</translation>
<translation id="8070439594494267500">Ikon aplikasi</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Buat Spreadsheet Google baru dengan cepat</translation>
<translation id="8075898834294118863">Kelola setelan situs</translation>
+<translation id="8076492880354921740">Tab</translation>
<translation id="8078141288243656252">Tidak dapat menambahkan anotasi saat diputar</translation>
<translation id="8079031581361219619">Muat ulang situs?</translation>
<translation id="8081087320434522107">Sedan</translation>
@@ -2681,6 +2691,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="8416694386774425977">Konfigurasi jaringan tidak valid dan tidak dapat diimpor. Detail tambahan: <ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Setelan</translation>
+<translation id="8428634594422941299">Mengerti</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, tekan Tab lalu Enter untuk mengelola preferensi cookie di setelan Chrome</translation>
<translation id="8433057134996913067">Tindakan ini akan mengeluarkan Anda dari sebagian besar situs web.</translation>
<translation id="8434840396568290395">Hewan peliharaan</translation>
@@ -2778,6 +2789,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> adalah kode Anda untuk <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Bookmark tab ini</translation>
<translation id="8751426954251315517">Coba lagi lain waktu</translation>
+<translation id="8757526089434340176">Penawaran Google Pay tersedia</translation>
<translation id="8758885506338294482">Kompetisi video game</translation>
<translation id="8759274551635299824">Kartu sudah tidak aktif</translation>
<translation id="87601671197631245">Situs ini menggunakan konfigurasi keamanan yang telah habis masa berlakunya, yang dapat mengekspos informasi Anda (misalnya, sandi, pesan, atau kartu kredit) saat dikirimkan ke situs ini.</translation>
@@ -2785,6 +2797,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="8763927697961133303">Perangkat USB</translation>
<translation id="8763986294015493060">Tutup semua jendela Samaran yang saat ini terbuka</translation>
<translation id="8766943070169463815">Sheet autentikasi kredensial pembayaran aman dibuka</translation>
+<translation id="8767765348545497220">Tutup balon bantuan</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Sepeda motor</translation>
<translation id="8790007591277257123">&amp;Ulangi penghapusan</translation>
@@ -2797,6 +2810,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="8806285662264631610">Produk mandi &amp; perawatan tubuh</translation>
<translation id="8807160976559152894">Trim setelah setiap halaman tercetak</translation>
<translation id="8808828119384186784">Setelan Chrome</translation>
+<translation id="8813277370772331957">Ingatkan saya nanti</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, tekan Tab lalu Enter untuk mengupdate Chrome dari setelan Chrome</translation>
<translation id="8820817407110198400">Bookmark</translation>
<translation id="882338992931677877">Slot Manual</translation>
@@ -2976,6 +2990,7 @@ Jika tidak, ini akan diblokir oleh setelan privasi Anda. Ini akan memungkinkan k
<translation id="988159990683914416">Buatan Pengembang</translation>
<translation id="989988560359834682">Edit Alamat</translation>
<translation id="991413375315957741">sensor gerakan atau cahaya</translation>
+<translation id="992110854164447044">Kartu virtual menyembunyikan kartu Anda yang sebenarnya untuk membantu melindungi Anda dari kemungkinan penipuan. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Pink</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" tidak diinstal dengan benar di komputer atau jaringan Anda:
diff --git a/chromium/components/strings/components_strings_is.xtb b/chromium/components/strings/components_strings_is.xtb
index 9400cdc1998..3d2fd3b3354 100644
--- a/chromium/components/strings/components_strings_is.xtb
+++ b/chromium/components/strings/components_strings_is.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Styrkir, skólastyrkir og fjárhagsaðstoð</translation>
<translation id="1048785276086539861">Þegar þú breytir textaskýringum fer þetta skjal aftur í einnar síðu snið</translation>
<translation id="1050038467049342496">Lokaðu öðrum forritum</translation>
+<translation id="1053959602163383901">Þú valdir að staðfesta með sannvottunartæki á vefsvæðum sem nota <ph name="PROVIDER_ORIGIN" />. Þessi þjónustuaðili kann að hafa vistað upplýsingar um greiðslumátann þinn, sem þú getur <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">Aft&amp;urkalla nýtt bókamerki</translation>
<translation id="1056663316309890343">Myndahugbúnaður</translation>
<translation id="1056898198331236512">Viðvörun</translation>
@@ -119,6 +120,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="1270502636509132238">Afhendingarmáti</translation>
<translation id="1281476433249504884">Staflari 1</translation>
<translation id="1285320974508926690">Aldrei þýða þetta vefsvæði</translation>
+<translation id="1288548991597756084">Vistaðu kortið á öruggan hátt</translation>
<translation id="1292571435393770077">Bakki 16</translation>
<translation id="1292701964462482250">„Hugbúnaður í tölvunni þinni hindrar Chrome í að tengjast netinu á öruggan máta“ (einungis Windows-tölvur)</translation>
<translation id="1294154142200295408">Tilbrigði fyrir skipanalínu</translation>
@@ -223,6 +225,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
&lt;p&gt;Til að leysa vandann skaltu smella á &lt;strong&gt;Tengjast&lt;/strong&gt; á síðunni sem þú ert að reyna að opna.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landslagshönnun</translation>
<translation id="1513706915089223971">Listi yfir færslur í ferli</translation>
+<translation id="1516097932025103760">Það verður dulkóðað, vistað á öruggan hátt og CVC-númerið er aldrei geymt.</translation>
<translation id="1517433312004943670">Símanúmers er krafist</translation>
<translation id="1519264250979466059">Dagsetning smíðar</translation>
<translation id="1521159554480556801">Textíllist</translation>
@@ -288,6 +291,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="1662550410081243962">Vista og fylla út greiðslumáta</translation>
<translation id="1663943134801823270">Spjöld og heimilisföng eru úr Chrome. Þú getur haft umsjón með þeim í <ph name="BEGIN_LINK" />stillingunum<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Síður á tungumálinu „<ph name="SOURCE_LANGUAGE" />“ verða héðan í frá þýddar yfir á eftirfarandi tungumál: <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Gakktu frá kaupum með <ph name="CARD_DETAIL" /> til að nýta tilboð</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> &gt; <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Skammhlið fyrst</translation>
<translation id="168693727862418163">Ekki tókst að staðfesta gildi þessarar reglu gagnvart skema hennar og hún verður hunsuð.</translation>
@@ -306,6 +310,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="1717218214683051432">Hreyfiskynjarar</translation>
<translation id="1717494416764505390">Pósthólf 3</translation>
<translation id="1718029547804390981">Þetta skjal er of stórt til að hægt sé að skrifa skýringar í það</translation>
+<translation id="1720941539803966190">Loka leiðsögn</translation>
<translation id="1721424275792716183">* Reiturinn má ekki vera auður</translation>
<translation id="1727613060316725209">Vottorðið er gilt</translation>
<translation id="1727741090716970331">Bæta við gildu kortanúmeri</translation>
@@ -422,8 +427,8 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="205212645995975601">Grillmatur</translation>
<translation id="2053111141626950936">Síður á þessu tungumáli verða ekki þýddar: <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Þegar kveikt er á þessari stýringu og staða hennar er virk metur Chrome hvaða stóra flokki eða „hópi“ fólks nýleg vafranotkun þín líkist mest. Auglýsendur geta valið auglýsingar fyrir hópinn og nýlegri vafranotkun þinni er haldið lokaðri í tækinu. Hópurinn þinn er uppfærður daglega.}=1{Þegar kveikt er á þessari stýringu og staða hennar er virk metur Chrome hvaða stóra flokki eða „hópi“ fólks nýleg vafranotkun þín líkist mest. Auglýsendur geta valið auglýsingar fyrir hópinn og nýlegri vafranotkun þinni er haldið lokaðri í tækinu. Hópurinn þinn er uppfærður daglega.}one{Þegar kveikt er á þessari stýringu og staða hennar er virk metur Chrome hvaða stóra flokki eða „hópi“ fólks nýleg vafranotkun þín líkist mest. Auglýsendur geta valið auglýsingar fyrir hópinn og nýlegri vafranotkun þinni er haldið lokaðri í tækinu. Hópurinn þinn er uppfærður á {NUM_DAYS} dags fresti.}other{Þegar kveikt er á þessari stýringu og staða hennar er virk metur Chrome hvaða stóra flokki eða „hópi“ fólks nýleg vafranotkun þín líkist mest. Auglýsendur geta valið auglýsingar fyrir hópinn og nýlegri vafranotkun þinni er haldið lokaðri í tækinu. Hópurinn þinn er uppfærður á {NUM_DAYS} daga fresti.}}</translation>
-<translation id="2053553514270667976">Póstnúmer</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{Ein tillaga}one{# tillaga}other{# tillögur}}</translation>
+<translation id="2066915425250589881">óskað eftir að láta eyða</translation>
<translation id="2068528718802935086">Ungbörn og smábörn</translation>
<translation id="2071156619270205202">Þetta kort er ekki gjaldgengt fyrir sýndarkortsnúmer.</translation>
<translation id="2071692954027939183">Lokað var sjálfkrafa á tilkynningar því þú leyfir þær yfirleitt ekki</translation>
@@ -435,7 +440,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="2088086323192747268">Hnappurinn „Stjórna samstillingu“, ýttu á „Enter“ til að opna stillingar Chrome og stjórna því hvaða upplýsingar eru samstilltar</translation>
<translation id="2091887806945687916">Hljóð</translation>
<translation id="2094505752054353250">Misræmi í léni</translation>
-<translation id="2096368010154057602">Deild</translation>
<translation id="2099652385553570808">Þrjú hefti vinstra megin</translation>
<translation id="2101225219012730419">Útgáfa:</translation>
<translation id="2102134110707549001">Tillaga að traustu aðgangsorði…</translation>
@@ -472,7 +476,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="2185836064961771414">Amerískur fótbolti</translation>
<translation id="2187317261103489799">Greina (sjálfgefið)</translation>
<translation id="2188375229972301266">Mörg göt neðst</translation>
-<translation id="2188852899391513400">Aðgangsorðið sem þú varst að nota fannst nýverið í öryggisbroti. Aðgangsorðastjórnun Google mælir með að breyta aðgangsorðinu núna og athuga vistuð aðgangsorð til að tryggja öryggi reikninganna þinna.</translation>
<translation id="219906046732893612">Endurbætur á heimili</translation>
<translation id="2202020181578195191">Færðu inn gilt lokaár</translation>
<translation id="22081806969704220">Bakki 3</translation>
@@ -483,6 +486,8 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="2215727959747642672">Skráarvinnsla</translation>
<translation id="2215963164070968490">Hundar</translation>
<translation id="2218879909401188352">Tölvuþrjótar sem eru á <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> núna gætu sett upp hættuleg forrit sem skaða tækið þitt, setja faldar færslur á símreikninginn þinn eða stela persónuupplýsingunum þínum. <ph name="BEGIN_LEARN_MORE_LINK" />Frekari upplýsingar<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Endurræsa leiðsögn</translation>
+<translation id="2219735899272417925">Endurstilla þarf tækið</translation>
<translation id="2224337661447660594">Engin nettenging</translation>
<translation id="2230458221926704099">Lagaðu tenginguna með því að nota <ph name="BEGIN_LINK" />greiningarforritið<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Senda núna</translation>
@@ -580,11 +585,13 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="2512101340618156538">Ekki heimilað (sjálfgefið)</translation>
<translation id="2512413427717747692">Hnappur til að stilla Chrome sem sjálfgefinn vafra, ýttu á Enter til að stilla Chrome sem sjálfgefinn vafra kerfisins í stillingum iOS</translation>
<translation id="2515629240566999685">Athuga merkið á þínu svæði</translation>
+<translation id="2515761554693942801">Þú valdir að staðfesta með Touch ID á vefsvæðum sem nota <ph name="PROVIDER_ORIGIN" />. Þessi þjónustuaðili kann að hafa vistað upplýsingar um greiðslumátann þinn, sem þú getur <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Hefti neðst til hægri</translation>
<translation id="2521736961081452453">Búa til eyðublað</translation>
<translation id="2523886232349826891">Aðeins vistað í þessu tæki</translation>
<translation id="2524461107774643265">Bæta við fleiri upplýsingum</translation>
<translation id="2529899080962247600">Þessi reitur á ekki að innihalda fleiri en <ph name="MAX_ITEMS_LIMIT" /> færslur. Allar frekari færslur verða hunsaðar.</translation>
+<translation id="253493526287553278">Sjá upplýsingar um kynningarkóða</translation>
<translation id="2535585790302968248">Opnaðu nýjan huliðsflipa til að fara huldu höfði</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{og 1 í viðbót}one{og # í viðbót}other{og # í viðbót}}</translation>
<translation id="2536110899380797252">Bæta við heimilisfangi</translation>
@@ -620,7 +627,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="259821504105826686">Ljósmyndun og stafræn list</translation>
<translation id="2601150049980261779">Rómantískar myndir</translation>
<translation id="2604589665489080024">Popptónlist</translation>
-<translation id="2609632851001447353">Tilbrigði</translation>
<translation id="2610561535971892504">Smelltu til að afrita</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />vistar ekki<ph name="END_EMPHASIS" /> eftirfarandi upplýsingar:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Afmæli og nafnadagar</translation>
<translation id="2677748264148917807">Yfirgefa</translation>
+<translation id="2679714844901977852">Vistaðu korta- og innheimtuupplýsingarnar þínar á Google reikningnum þínum <ph name="USER_EMAIL" /> fyrir öruggari og hraðari greiðslur</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Kjörstærð</translation>
<translation id="2688969097326701645">Já, halda áfram</translation>
@@ -701,7 +708,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="2854764410992194509">Netþjónusta</translation>
<translation id="2856444702002559011">Tölvuþrjótar gætu verið að reyna að stela upplýsingum þínum frá <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (til dæmis aðgangsorðum, skilaboðum eða kreditkortum). <ph name="BEGIN_LEARN_MORE_LINK" />Frekari upplýsingar<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Þetta vefsvæði sýnir ágengar eða villandi auglýsingar.</translation>
-<translation id="286512204874376891">Sýndarkort dulbýr raunverulega kortið þitt og hjálpar þannig til við að koma í veg fyrir svik. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Vinalegt</translation>
<translation id="28761159517501904">Kvikmyndir</translation>
<translation id="2876489322757410363">Slekkur á huliðsstillingu til að greiða með öðru forriti. Halda áfram?</translation>
@@ -802,7 +808,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="3158539265159265653">Diskur</translation>
<translation id="3162559335345991374">Wi-Fi netið sem þú notar kann að fara fram á að þú farir á innskráningarsíðu þess.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Eyja</translation>
<translation id="3176929007561373547">Athugaðu stillingarnar þínar fyrir staðgengilsþjón eða hafðu samband við netstjórann þinn til að
ganga úr skugga um að staðgengilsþjónninn starfi rétt. Ef þú heldur að þú ættir
ekki að nota staðgengilsþjón:
@@ -881,9 +886,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="3369192424181595722">Klukkuvilla</translation>
<translation id="3369459162151165748">Varahlutir og aukabúnaður fyrir bíla</translation>
<translation id="3371064404604898522">Stilla Chrome sem sjálfgefinn vafra</translation>
-<translation id="3371076217486966826"><ph name="URL" /> vill:
- • Búa til þrívíddarkort af umhverfi þínu og rekja stöðu myndavélarinnar
- • Nota myndavélina þína</translation>
<translation id="337363190475750230">Úthlutun dregin til baka</translation>
<translation id="3375754925484257129">Keyra öryggisathugun í Chrome</translation>
<translation id="3377144306166885718">Þjónninn notaði úrelta útgáfu af TLS.</translation>
@@ -900,6 +902,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="3399952811970034796">Sendingarheimilisfang</translation>
<translation id="3402261774528610252">Tengingin sem er notuð til að hlaða þessu vefsvæði notaði TLS 1.0 eða TLS 1.1, úreltar útgáfur sem verða gerðar óvirkar í framtíðinni. Þegar þær hafa verið gerðar óvirkar geta notendur ekki hlaðið þessu vefsvæði. Virkja ætti TLS 1.2 eða nýrri útgáfu á þjóninum.</translation>
<translation id="3405664148539009465">Sérsníða letur</translation>
+<translation id="3407789382767355356">innskráning þriðja aðila</translation>
<translation id="3409896703495473338">Stjórna öryggisstillingum</translation>
<translation id="3414952576877147120">Stærð:</translation>
<translation id="3417660076059365994">Skrár sem þú hleður upp eða hengir við eru sendar til Google Cloud eða þriðju aðila til greiningar. Þær gætu til dæmis verið skannaðar í leit að viðkvæmum upplýsingum eða spilliforritum.</translation>
@@ -932,6 +935,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="3477679029130949506">Kvikmyndir og sýningatímar í bíó</translation>
<translation id="3479552764303398839">Ekki núna</translation>
<translation id="3484560055331845446">Þú gætir glatað aðganginum að Google reikningnum þínum. Chrome mælir með því að þú skiptir um aðgangsorð núna. Þú verður beðin(n) um að skrá þig inn.</translation>
+<translation id="3484861421501147767">Ãminning: Vistaður kynningarkóði tiltækur</translation>
<translation id="3487845404393360112">Bakki 4</translation>
<translation id="3495081129428749620">Finna á síðunni
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="3810973564298564668">Stjórna</translation>
<translation id="3816482573645936981">Gildi (leyst af hólmi)</translation>
<translation id="382518646247711829">Ef þú notar proxy-þjón...</translation>
+<translation id="3826050100957962900">Innskráning þriðja aðila</translation>
<translation id="3827112369919217609">Fast</translation>
<translation id="3827666161959873541">Fjölskyldumyndir</translation>
<translation id="3828924085048779000">Ekki er leyft að hafa autt aðgangsorð.</translation>
@@ -1068,9 +1073,9 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="3858027520442213535">Uppfæra dagsetningu og tíma</translation>
<translation id="3858860766373142691">Heiti</translation>
<translation id="3872834068356954457">Vísindi</translation>
+<translation id="3875783148670536197">Fá leiðbeiningar</translation>
<translation id="3881478300875776315">Sýna færri línur</translation>
<translation id="3884278016824448484">Ósamræmi í auðkenni tækis</translation>
-<translation id="3885155851504623709">Sókn</translation>
<translation id="388632593194507180">Eftirlit greindist</translation>
<translation id="3886948180919384617">Staflari 3</translation>
<translation id="3890664840433101773">Bæta við netfangi</translation>
@@ -1100,9 +1105,11 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="3973234410852337861">Lokað er fyrir <ph name="HOST_NAME" /></translation>
<translation id="3978338123949022456">Leitarstilling, sláðu inn fyrirspurn og ýttu svo á Enter til að leita með <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Fuglar</translation>
+<translation id="3985750352229496475">Stjórna heimilisföngum...</translation>
<translation id="3986705137476756801">Slökkva á skjátextum í rauntíma í bili</translation>
<translation id="3987940399970879459">Minna en 1 MB</translation>
<translation id="3990250421422698716">Jöfnuð offsetprentun</translation>
+<translation id="3992684624889376114">Um þessa síðu</translation>
<translation id="3996311196211510766">Vefsvæðið <ph name="ORIGIN" /> hefur beðið um að upprunaregla
gildi fyrir allar beiðnir þess, en ekki er hægt að nota þessa reglu eins og er.</translation>
<translation id="4006465311664329701">Greiðslumátar, tilboð og heimilisföng sem nota Google Pay</translation>
@@ -1227,6 +1234,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="4305666528087210886">Ekki var hægt að opna skrána þína</translation>
<translation id="4306529830550717874">Vista heimilisfang?</translation>
<translation id="4306812610847412719">klippiborð</translation>
+<translation id="4310070645992025887">Leita í ferlunum þínum</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Útiloka (sjálfgefið)</translation>
<translation id="4314815835985389558">Stjórna samstillingu</translation>
@@ -1277,6 +1285,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="4435702339979719576">póstkort)</translation>
<translation id="443673843213245140">Slökkt er á notkun proxy-þjóns en sérstök proxy-stilling er tilgreind.</translation>
<translation id="4441832193888514600">Hunsað vegna þess að aðeins er hægt að stilla regluna sem notendareglu í skýi.</translation>
+<translation id="4442470707340296952">Chrome flipar</translation>
<translation id="4450893287417543264">Ekki sýna þetta aftur</translation>
<translation id="4451135742916150903">Getur beðið um að tengjast HID-tækjum</translation>
<translation id="4452328064229197696">Aðgangsorðið sem þú varst að nota fannst nýverið í öryggisbroti. Aðgangsorðastjórnun Google mælir með að þú athugir vistuð aðgangsorð til að tryggja öryggi reikninganna þinna.</translation>
@@ -1415,6 +1424,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="483241715238664915">Kveikja á viðvörunum</translation>
<translation id="4834250788637067901">Greiðslumátar, tilboð og heimilisföng sem nota Google Pay</translation>
<translation id="4838327282952368871">Draumkennt</translation>
+<translation id="4839087176073128681">Greiddu hraðar næst og verndaðu kortið þitt með framúrskarandi öryggislausnum Google.</translation>
<translation id="4840250757394056958">Skoða Chrome ferilinn þinn</translation>
<translation id="484462545196658690">Sjálfvirkt</translation>
<translation id="484671803914931257">Fáðu afslátt hjá <ph name="MERCHANT_NAME" /> og fleirum</translation>
@@ -1477,6 +1487,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="5011561501798487822">Greint tungumál</translation>
<translation id="5015510746216210676">Vélarheiti:</translation>
<translation id="5017554619425969104">Texti sem þú afritaðir</translation>
+<translation id="5017828934289857214">Minna mig á seinna</translation>
<translation id="5018422839182700155">Ekki tókst að opna þessa síðu</translation>
<translation id="5019198164206649151">Geymsla bakenda er í slæmri stöðu</translation>
<translation id="5020776957610079374">Heimstónlist</translation>
@@ -1496,6 +1507,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="5051305769747448211">Uppistand</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Losaðu um pláss (<ph name="DISK_SPACE_SIZE" />) í tækinu þínu til að senda þessa skrá með nærdeilingu}one{Losaðu um pláss (<ph name="DISK_SPACE_SIZE" />) í tækinu þínu til að senda þessa skrá með nærdeilingu}other{Losaðu um pláss (<ph name="DISK_SPACE_SIZE" />) í tækinu þínu til að senda þessar skrár með nærdeilingu}}</translation>
<translation id="5056549851600133418">Greinar fyrir þig</translation>
+<translation id="5060483733937416656">Þú valdir að staðfesta með Windows Hello á vefsvæðum sem nota <ph name="PROVIDER_ORIGIN" />. Þessi þjónustuaðili kann að hafa vistað upplýsingar um greiðslumátann þinn, sem þú getur <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Ãttirðu við <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Prentferill</translation>
<translation id="5068234115460527047">Vogunarsjóðir</translation>
@@ -1509,10 +1521,8 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="5087286274860437796">Vottorð þjóns er ekki gilt núna.</translation>
<translation id="5087580092889165836">Bæta við korti</translation>
<translation id="5088142053160410913">Skilaboð til stjórnanda</translation>
-<translation id="5089810972385038852">Ríki</translation>
<translation id="5093232627742069661">Z-brot</translation>
<translation id="5094747076828555589">Þessi þjónn gat ekki sannað að hann væri <ph name="DOMAIN" />; Chromium treystir ekki öryggisvottorðinu hans. Þetta kann að orsakast af vanstillingu eða tölvuþrjóti sem komist hefur inn í tenginguna.</translation>
-<translation id="5095208057601539847">Hérað</translation>
<translation id="5097099694988056070">Talnagögn tækis eins og notkun örgjörva/vinnsluminnis</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Vefsvæðið er ekki öruggt</translation>
@@ -1550,6 +1560,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="5171045022955879922">Leitaðu eða sláðu inn vefslóð</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Vél</translation>
+<translation id="5177076414499237632">Nánar um heimildir og umfjöllunarefni þessarar síðu</translation>
<translation id="5179510805599951267">Ekki <ph name="ORIGINAL_LANGUAGE" />? Tilkynna þessa villu</translation>
<translation id="518639307526414276">Gæludýrafóður og gæludýravörur</translation>
<translation id="5190835502935405962">Bókamerkjastika</translation>
@@ -1710,6 +1721,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="5624120631404540903">Stjórna aðgangsorðum</translation>
<translation id="5629630648637658800">Mistókst að hlaða reglustillingar</translation>
<translation id="5631439013527180824">Ógildur kóði tækjastjórnunar</translation>
+<translation id="5632485077360054581">Fá leiðbeiningar</translation>
<translation id="5633066919399395251">Tölvuþrjótar sem halda nú til á <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kunna að reyna að setja hættuleg forrit upp í tölvunni þinni, sem ætlað er að stela eða eyða upplýsingunum þínum (t.d. myndum, aðgangsorðum, skilaboðum og kreditkortum). <ph name="BEGIN_LEARN_MORE_LINK" />Frekari upplýsingar<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Lokað á villandi efni.</translation>
<translation id="5633259641094592098">Költ- og indímyndir</translation>
@@ -1827,6 +1839,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="5989320800837274978">Hvorki fastir proxy-þjónar né vefslóð á .pac-skriftu er skilgreind.</translation>
<translation id="5992691462791905444">Z-brot</translation>
<translation id="5995727681868049093">Stjórnaðu upplýsingum, persónuvernd og öryggi á Google reikningnum þínum</translation>
+<translation id="5997247540087773573">Aðgangsorðið sem þú varst að nota fannst í öryggisbroti. Aðgangsorðastjórnun Google mælir með að breyta aðgangsorðinu núna og athuga vistuð aðgangsorð til að tryggja öryggi reikninganna þinna.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> niðurstöður fyrir „<ph name="SEARCH_TEXT" />“</translation>
<translation id="6006484371116297560">Hefðbundið</translation>
<translation id="6008122969617370890">Röðun N til 1</translation>
@@ -1922,7 +1935,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="627746635834430766">Til að greiða hraðar næst geturðu vistað kortið og heimilisfang greiðanda á Google reikningnum.</translation>
<translation id="6279183038361895380">Ãttu á |<ph name="ACCELERATOR" />| til að sýna bendilinn</translation>
<translation id="6280223929691119688">Ekki er hægt að senda á þetta heimilisfang. Veldu annað heimilisfang.</translation>
-<translation id="6282194474023008486">Póstnúmer</translation>
<translation id="6285507000506177184">Hnappur til að stjórna niðurhali í Chrome, ýttu á Enter til að stjórna skrám sem þú hefur sótt í Chrome</translation>
<translation id="6289939620939689042">Síðulitur</translation>
<translation id="6290238015253830360">Tillögur að greinum birtast hér</translation>
@@ -1944,6 +1956,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="6337133576188860026">Losar minna en <ph name="SIZE" />. Lengri tíma gæti tekið að sækja sum vefsvæði þegar þú heimsækir þau næst.</translation>
<translation id="6337534724793800597">Sía reglur eftir heiti</translation>
<translation id="6340739886198108203">Regla kerfisstjóra ræður gegn því að taka skjámyndir eða taka upp þegar trúnaðarupplýsingar eru sýnilegar:</translation>
+<translation id="6348220984832452017">Virk afbrigði</translation>
<translation id="6349101878882523185">Setja upp <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ekkert}=1{1 aðgangsorð (fyrir <ph name="DOMAIN_LIST" />, samstillt)}=2{2 aðgangsorð fyrir (<ph name="DOMAIN_LIST" />, samstillt)}one{# aðgangsorð (fyrir <ph name="DOMAIN_LIST" />, samstillt)}other{# aðgangsorð (fyrir <ph name="DOMAIN_LIST" />, samstillt)}}</translation>
<translation id="6355392890578844978">Þessi vafri er ekki í umsjón fyrirtækis eða stofnunar Hægt er að hafa umsjón með aðgerðum í þessu tæki utan Chromium. <ph name="BEGIN_LINK" />Nánar<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="643051589346665201">Breyta Google aðgangsorði</translation>
<translation id="6433490469411711332">Breyta samskiptaupplýsingum</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> neitaði að koma á tengingu.</translation>
-<translation id="6438025220577812695">Ég breyti</translation>
<translation id="6440503408713884761">Hunsað</translation>
<translation id="6443406338865242315">Hvaða viðbætur þú hefur sett upp</translation>
<translation id="6446163441502663861">Kahu (umslag)</translation>
@@ -2105,9 +2117,9 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="6828866289116430505">Erfðafræði</translation>
<translation id="6831043979455480757">Þýða</translation>
<translation id="6833752742582340615">Vistaðu korta- og innheimtuupplýsingarnar þínar á Google reikningnum þínum fyrir öruggari og hraðari greiðslur</translation>
-<translation id="6839929833149231406">Svæði</translation>
<translation id="6846340164947227603">Nota sýndarkortsnúmer...</translation>
<translation id="6852204201400771460">Viltu endurhlaða forritið?</translation>
+<translation id="6857776781123259569">Stjórna aðgangsorðum...</translation>
<translation id="686485648936420384">Neytendamál</translation>
<translation id="6865412394715372076">Ekki er hægt að staðfesta þetta kort í augnablikinu</translation>
<translation id="6869334554832814367">Persónuleg lán</translation>
@@ -2156,7 +2168,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="6965978654500191972">Tæki</translation>
<translation id="696703987787944103">Skynjað</translation>
<translation id="6968269510885595029">Notaðu öryggislykilinn þinn</translation>
-<translation id="6970216967273061347">Umdæmi</translation>
<translation id="6971439137020188025">Búa til nýja Google kynningu í Skyggnum á skjótan hátt</translation>
<translation id="6972629891077993081">HID-tæki</translation>
<translation id="6973656660372572881">Bæði fastir proxy-þjónar og vefslóð á .pac-skriftu eru skilgreind.</translation>
@@ -2195,7 +2206,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="7081308185095828845">Þessi eiginleiki er ekki í boði í tækinu þínu</translation>
<translation id="7083258188081898530">Bakki 9</translation>
<translation id="7086090958708083563">Notandi bað um sendingu</translation>
-<translation id="7087282848513945231">Sýsla</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, ýttu á dálkalykilinn (Tab) og svo „Enter“ til að opna stillingar Chrome og stjórna heimildum og gögnum sem eru geymd á vefsvæðum</translation>
<translation id="7096937462164235847">Auðkenni þessa vefsvæðis er óstaðfest.</translation>
<translation id="7101893872976785596">Hryllingsmyndir</translation>
@@ -2214,10 +2224,10 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<ph name="LIST_ITEM" />Upplýsingar sem eru færðar inn í eyðublöð<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Ekki er hægt að senda á þetta heimilisfang. Veldu annað heimilisfang.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Stjórnandi hefur bannað afritun þessara gagna.</translation>
<translation id="7135130955892390533">Sýna stöðu</translation>
<translation id="7138472120740807366">Afhendingarmáti</translation>
-<translation id="7139724024395191329">Furstadæmi</translation>
<translation id="7139892792842608322">Aðalbakki</translation>
<translation id="714064300541049402">X-færsla myndar á hlið 2</translation>
<translation id="7152423860607593928">Number-14 (umslag)</translation>
@@ -2434,6 +2444,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="7669271284792375604">Tölvuþrjótar á þessu vefsvæði gætu verið að reyna að ginna þig til að setja upp forrit sem gætu gert vefskoðun óþægilegri fyrir þig (til dæmis með því að breyta upphafssíðunni eða birta viðbótarauglýsingar á vefsvæðum sem þú heimsækir).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Aðgerðir í gögnum sem eru merkt sem trúnaðarmál (1 aðgerð frá innskráningu). <ph name="BEGIN_LINK" />Nánar<ph name="END_LINK" />}one{Aðgerðir í gögnum sem eru merkt sem trúnaðarmál (# aðgerðir frá innskráningu). <ph name="BEGIN_LINK" />Nánar<ph name="END_LINK" />}other{Aðgerðir í gögnum sem eru merkt sem trúnaðarmál (# aðgerðir frá innskráningu). <ph name="BEGIN_LINK" />Nánar<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Pósthólf 6</translation>
+<translation id="7675325315208090829">Stjórna greiðslumátum...</translation>
<translation id="7676643023259824263">Leita að texta á klippiborði, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Skoða og stjórna vafraferli í stillingum Chrome</translation>
<translation id="7679947978757153706">Hafnabolti</translation>
@@ -2476,7 +2487,6 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="7766518757692125295">Kantur</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Sama röð, snýr upp</translation>
-<translation id="777702478322588152">Hérað</translation>
<translation id="7791011319128895129">Óútgefið</translation>
<translation id="7791196057686275387">Rúlla</translation>
<translation id="7791543448312431591">Bæta við</translation>
@@ -2567,12 +2577,12 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="8055534648776115597">Starfsþjálfun og símenntun</translation>
<translation id="8057711352706143257">„<ph name="SOFTWARE_NAME" />“ hefur ekki verið stillt rétt. Yfirleitt er hægt að leysa vandann með því að fjarlægja „<ph name="SOFTWARE_NAME" />“. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Matvælaframleiðsla</translation>
-<translation id="8066955247577885446">Eitthvað fór úrskeiðis.</translation>
<translation id="8067872629359326442">Þú varst að slá aðgangsorðið þitt inn á villandi vefsvæði. Chromium getur aðstoðað. Smelltu á „Vernda reikning“ til að breyta lykilorðinu þínu og láta Google vita að reikningurinn þinn sé hugsanlega í hættu.</translation>
<translation id="8070439594494267500">Forritstákn</translation>
<translation id="8074253406171541171">10x13 (umslag)</translation>
<translation id="8075736640322370409">Búa til nýjan Google töflureikni í skyndi</translation>
<translation id="8075898834294118863">Stjórna stillingum vefsvæðis</translation>
+<translation id="8076492880354921740">Flipar</translation>
<translation id="8078141288243656252">Ekki er hægt að skrifa skýringu þegar skjalinu hefur verið snúið</translation>
<translation id="8079031581361219619">Viltu endurhlaða vefsvæðið?</translation>
<translation id="8081087320434522107">Fólksbifreiðar</translation>
@@ -2695,6 +2705,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Stillingar</translation>
+<translation id="8428634594422941299">Ég skil</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, ýttu á dálkalykilinn (Tab) og svo „Enter“ til að stjórna fótsporastillingum í stillingum Chrome</translation>
<translation id="8433057134996913067">Þetta skráir þig út af flestum vefsvæðum.</translation>
<translation id="8434840396568290395">Gæludýr</translation>
@@ -2792,6 +2803,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> er kóðinn þinn fyrir <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Setja þennan flipa í bókamerki</translation>
<translation id="8751426954251315517">Reyndu aftur síðar</translation>
+<translation id="8757526089434340176">Google Pay tilboð í boði</translation>
<translation id="8758885506338294482">Keppnisleikjaspilun</translation>
<translation id="8759274551635299824">Kortið er útrunnið</translation>
<translation id="87601671197631245">Þetta vefsvæði notar úrelta öryggisstillingu sem gæti sett upplýsingarnar þínar í hættu (til dæmis aðgangsorð, skilaboð eða kreditkort) þegar þær eru sendar til vefsvæðisins.</translation>
@@ -2799,6 +2811,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="8763927697961133303">USB-tæki</translation>
<translation id="8763986294015493060">Loka öllum huliðsgluggum sem eru opnir</translation>
<translation id="8766943070169463815">Sannvottunarsíða fyrir skilríki öruggrar greiðslu var opnuð</translation>
+<translation id="8767765348545497220">Loka hjálparblöðru</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Mótorhjól</translation>
<translation id="8790007591277257123">Endu&amp;rtaka eyðingu</translation>
@@ -2811,6 +2824,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="8806285662264631610">Bað- og líkamsvörur</translation>
<translation id="8807160976559152894">Klippa eftir hverja síðu</translation>
<translation id="8808828119384186784">Stillingar Chrome</translation>
+<translation id="8813277370772331957">Minna mig á seinna</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, ýttu á Tab og svo Enter til að uppfæra Chrome úr stillingum Chrome</translation>
<translation id="8820817407110198400">Bókamerki</translation>
<translation id="882338992931677877">Handvirkt hólf</translation>
@@ -2990,6 +3004,7 @@ Ef þú gerir það ekki loka persónuverndarstillingar þínar á þennan aðga
<translation id="988159990683914416">Forritarasmíði</translation>
<translation id="989988560359834682">Breyta heimilisfangi</translation>
<translation id="991413375315957741">hreyfi- eða birtuskynjara</translation>
+<translation id="992110854164447044">Sýndarkort felur raunverulega kortið þitt og hjálpar þannig til við að koma í veg fyrir svik. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Bleikur</translation>
<translation id="992432478773561401">„<ph name="SOFTWARE_NAME" />“ var ekki sett upp rétt í tölvunni þinni eða á netkerfinu:
diff --git a/chromium/components/strings/components_strings_it.xtb b/chromium/components/strings/components_strings_it.xtb
index 7b3b946f53c..ae4b62ea371 100644
--- a/chromium/components/strings/components_strings_it.xtb
+++ b/chromium/components/strings/components_strings_it.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Prestiti, borse di studio e aiuti finanziari</translation>
<translation id="1048785276086539861">Quando modifichi le annotazioni, questo documento torna alla visualizzazione a pagina singola</translation>
<translation id="1050038467049342496">Chiudi altre app</translation>
+<translation id="1053959602163383901">Hai scelto di eseguire la verifica con un dispositivo di autenticazione sui siti web che utilizzano <ph name="PROVIDER_ORIGIN" />. Questo fornitore potrebbe aver memorizzato informazioni sul tuo metodo di pagamento, che puoi <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Annulla aggiunta</translation>
<translation id="1056663316309890343">Software per fotografia</translation>
<translation id="1056898198331236512">Avviso</translation>
@@ -119,6 +120,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="1270502636509132238">Metodo di ritiro</translation>
<translation id="1281476433249504884">Fascicolatore 1</translation>
<translation id="1285320974508926690">Non tradurre mai questo sito</translation>
+<translation id="1288548991597756084">Salva la carta in modo sicuro</translation>
<translation id="1292571435393770077">Vassoio 16</translation>
<translation id="1292701964462482250">"Il software installato sul computer sta impedendo a Chrome di connettersi in sicurezza al Web" (solo su computer Windows)</translation>
<translation id="1294154142200295408">Variazioni nella riga di comando</translation>
@@ -223,6 +225,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
&lt;p&gt;Per risolvere il problema, fai clic su &lt;strong&gt;Connetti&lt;/strong&gt; nella pagina che stai cercando di aprire.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Architettura del paesaggio</translation>
<translation id="1513706915089223971">Elenco di voci della cronologia</translation>
+<translation id="1516097932025103760">La carta verrà criptata, salvata in modo sicuro e il CVC non verrà mai memorizzato.</translation>
<translation id="1517433312004943670">Numero di telefono obbligatorio</translation>
<translation id="1519264250979466059">Data build</translation>
<translation id="1521159554480556801">Arte tessile</translation>
@@ -288,6 +291,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="1662550410081243962">Salva e compila i metodi di pagamento</translation>
<translation id="1663943134801823270">Carte di credito e indirizzi provengono da Chrome. Puoi gestirli in <ph name="BEGIN_LINK" />Impostazioni<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">D'ora in poi, le pagine in <ph name="SOURCE_LANGUAGE" /> verranno tradotte in <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Effettua il pagamento con <ph name="CARD_DETAIL" /> per utilizzare l'offerta</translation>
<translation id="1674504678466460478">Da <ph name="SOURCE_LANGUAGE" /> a <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Prima il lato corto</translation>
<translation id="168693727862418163">Impossibile convalidare il valore del criterio in base al relativo schema. Il valore verrà ignorato.</translation>
@@ -306,6 +310,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="1717218214683051432">Sensori di movimento</translation>
<translation id="1717494416764505390">Mailbox 3</translation>
<translation id="1718029547804390981">Il documento è troppo voluminoso per potervi inserire annotazioni</translation>
+<translation id="1720941539803966190">Chiudi il tutorial</translation>
<translation id="1721424275792716183">* Campo obbligatorio</translation>
<translation id="1727613060316725209">Il certificato è valido</translation>
<translation id="1727741090716970331">Aggiungi un numero di carta valido</translation>
@@ -422,8 +427,8 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="205212645995975601">BBQ e grigliate</translation>
<translation id="2053111141626950936">Le pagine in <ph name="LANGUAGE" /> non verranno tradotte.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Quando questo controllo è attivato e lo stato è attivo, Chrome stabilisce a quale "coorte" (un gruppo numeroso di utenti) è più simile la tua attività di navigazione. Gli inserzionisti possono selezionare gli annunci per il gruppo e la tua attività di navigazione rimane privata sul tuo dispositivo. Il tuo gruppo viene aggiornato ogni giorno.}=1{Quando questo controllo è attivato e lo stato è attivo, Chrome stabilisce a quale "coorte" (un gruppo numeroso di utenti) è più simile la tua attività di navigazione. Gli inserzionisti possono selezionare gli annunci per il gruppo e la tua attività di navigazione rimane privata sul tuo dispositivo. Il tuo gruppo viene aggiornato ogni giorno.}other{Quando questo controllo è attivato e lo stato è attivo, Chrome stabilisce a quale "coorte" (un gruppo numeroso di utenti) è più simile la tua attività di navigazione. Gli inserzionisti possono selezionare gli annunci per il gruppo e la tua attività di navigazione rimane privata sul tuo dispositivo. Il tuo gruppo viene aggiornato ogni {NUM_DAYS} giorni}}</translation>
-<translation id="2053553514270667976">Codice postale</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggerimento}other{# suggerimenti}}</translation>
+<translation id="2066915425250589881">richiedere di eliminare</translation>
<translation id="2068528718802935086">Neonati e bambini piccoli</translation>
<translation id="2071156619270205202">Questa carta non è idonea per un numero di carta virtuale.</translation>
<translation id="2071692954027939183">Le notifiche sono state automaticamente bloccate perché solitamente non le consenti</translation>
@@ -435,7 +440,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="2088086323192747268">Pulsante Gestisci sincronizzazione: premi Invio per gestire le informazioni da sincronizzare nelle impostazioni di Chrome</translation>
<translation id="2091887806945687916">Audio</translation>
<translation id="2094505752054353250">Dominio non corrispondente</translation>
-<translation id="2096368010154057602">Dipartimento</translation>
<translation id="2099652385553570808">Tripla pinzatura a sinistra</translation>
<translation id="2101225219012730419">Versione:</translation>
<translation id="2102134110707549001">Suggerisci password efficace…</translation>
@@ -472,7 +476,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="2185836064961771414">Football americano</translation>
<translation id="2187317261103489799">Rileva (predefinita)</translation>
<translation id="2188375229972301266">Perforatura multipla in basso</translation>
-<translation id="2188852899391513400">La password appena usata è stata compromessa nell'ambito di una violazione dei dati. Per proteggere i tuoi account, Gestore delle password di Google consiglia di cambiarla subito e poi di controllare le password salvate.</translation>
<translation id="219906046732893612">Ristrutturazione della casa</translation>
<translation id="2202020181578195191">Inserisci un anno di scadenza valido</translation>
<translation id="22081806969704220">Vassoio 3</translation>
@@ -483,6 +486,8 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="2215727959747642672">Modifica del file</translation>
<translation id="2215963164070968490">Cani</translation>
<translation id="2218879909401188352">Gli utenti malintenzionati attualmente presenti sul sito <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> potrebbero installare app pericolose che danneggiano il tuo dispositivo, generano addebiti nascosti sul tuo credito telefonico o si impossessano dei tuoi dati personali. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Riavvia tutorial</translation>
+<translation id="2219735899272417925">È richiesta la reimpostazione del dispositivo</translation>
<translation id="2224337661447660594">Nessuna connessione a Internet</translation>
<translation id="2230458221926704099">Correggi la connessione con l'<ph name="BEGIN_LINK" />app diagnostica<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Invia ora</translation>
@@ -580,11 +585,13 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="2512101340618156538">Non consentita (opzione predefinita)</translation>
<translation id="2512413427717747692">Imposta Chrome come pulsante del browser predefinito, premi Invio per impostare Chrome come browser predefinito del sistema nelle impostazioni di iOS</translation>
<translation id="2515629240566999685">Controllare il segnale nella tua area</translation>
+<translation id="2515761554693942801">Hai scelto di eseguire la verifica con Touch ID sui siti web che utilizzano <ph name="PROVIDER_ORIGIN" />. Questo fornitore potrebbe aver memorizzato informazioni sul tuo metodo di pagamento, che puoi <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Pinzatura in basso a destra</translation>
<translation id="2521736961081452453">Crea modulo</translation>
<translation id="2523886232349826891">Salvataggio effettuato solo su questo dispositivo</translation>
<translation id="2524461107774643265">Aggiungi altre informazioni</translation>
<translation id="2529899080962247600">Questo campo non deve avere più di <ph name="MAX_ITEMS_LIMIT" /> voci. Tutte le voci in più verranno ignorate.</translation>
+<translation id="253493526287553278">Visualizza i dettagli del codice promozionale</translation>
<translation id="2535585790302968248">Apri una nuova scheda di navigazione in incognito per navigare in privato</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{e 1 altro}other{e altro #}}</translation>
<translation id="2536110899380797252">Aggiungi indirizzo</translation>
@@ -620,7 +627,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="259821504105826686">Fotografia e arti digitali</translation>
<translation id="2601150049980261779">Film romantici</translation>
<translation id="2604589665489080024">Musica pop</translation>
-<translation id="2609632851001447353">Varianti</translation>
<translation id="2610561535971892504">Fai clic per copiare</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />non salverà<ph name="END_EMPHASIS" /> le seguenti informazioni:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Compleanni e onomastici</translation>
<translation id="2677748264148917807">Esci</translation>
+<translation id="2679714844901977852">Salva i dati della carta e di fatturazione nel tuo Account Google <ph name="USER_EMAIL" /> per eseguire pagamenti in modo più veloce e sicuro</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Dimensioni predefinite</translation>
<translation id="2688969097326701645">Sì, continua</translation>
@@ -701,7 +708,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="2854764410992194509">Provider di servizi Internet (ISP)</translation>
<translation id="2856444702002559011">Gli utenti malintenzionati potrebbero provare a carpire le tue informazioni da <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (ad esempio, password, messaggi o carte di credito). <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Questo sito mostra annunci invasivi o fuorvianti.</translation>
-<translation id="286512204874376891">Una carta virtuale nasconde la tua carta effettiva per proteggerti meglio da potenziali attività fraudolente. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Amichevole</translation>
<translation id="28761159517501904">Film</translation>
<translation id="2876489322757410363">Per procedere al pagamento tramite un'applicazione esterna, uscirai dalla modalità di navigazione in incognito. Continuare?</translation>
@@ -802,7 +808,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="3158539265159265653">Disco</translation>
<translation id="3162559335345991374">La rete Wi-Fi in uso potrebbe richiedere la visita della relativa pagina di accesso.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Isola</translation>
<translation id="3176929007561373547">Controlla le impostazioni del proxy o contatta il tuo amministratore di rete per verificare che il server proxy funzioni. Se non ritieni di dover utilizzare un server proxy: <ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">Sapere quando usi attivamente questo dispositivo</translation>
<translation id="3180358318770512945">Educazione dei figli</translation>
@@ -878,9 +883,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="3369192424181595722">Errore dell'orologio</translation>
<translation id="3369459162151165748">Componenti e accessori per auto</translation>
<translation id="3371064404604898522">Imposta Chrome come browser predefinito</translation>
-<translation id="3371076217486966826"><ph name="URL" /> vuole:
- • Creare una mappa 3D dell'ambiente circostante e monitorare la posizione della fotocamera
- • Usare la fotocamera</translation>
<translation id="337363190475750230">Deprovisioning effettuato</translation>
<translation id="3375754925484257129">Esegui il controllo di sicurezza di Chrome</translation>
<translation id="3377144306166885718">Il server usava una versione obsoleta di TLS.</translation>
@@ -897,6 +899,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="3399952811970034796">Indirizzo di consegna</translation>
<translation id="3402261774528610252">La connessione utilizzata per caricare il sito usava TLS 1.0 o TLS 1.1, che ora sono obsoleti e in futuro verranno disattivati. In seguito alla disattivazione, gli utenti non potranno caricare questo sito. Il server dovrà abilitare TLS 1.2 o versione successiva.</translation>
<translation id="3405664148539009465">Personalizza caratteri</translation>
+<translation id="3407789382767355356">accesso di terze parti</translation>
<translation id="3409896703495473338">Gestisci le impostazioni di sicurezza</translation>
<translation id="3414952576877147120">Dimensioni:</translation>
<translation id="3417660076059365994">I file che carichi o alleghi vengono inviati a Google Cloud o a terze parti per l'analisi. Ad esempio, potrebbero essere sottoposti a scansione alla ricerca di dati sensibili o malware.</translation>
@@ -929,6 +932,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="3477679029130949506">Film in programmazione e orari dei cinema</translation>
<translation id="3479552764303398839">Non adesso</translation>
<translation id="3484560055331845446">Potresti non riuscire più ad accedere al tuo Account Google. Chrome consiglia di cambiare subito la password. Ti verrà chiesto di eseguire l'accesso.</translation>
+<translation id="3484861421501147767">Promemoria: codice promozionale salvato disponibile</translation>
<translation id="3487845404393360112">Vassoio 4</translation>
<translation id="3495081129428749620">Trova nella pagina
<ph name="PAGE_TITLE" /></translation>
@@ -1053,6 +1057,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="3810973564298564668">Gestisci</translation>
<translation id="3816482573645936981">Valore (sostituito)</translation>
<translation id="382518646247711829">Se utilizzi un server proxy...</translation>
+<translation id="3826050100957962900">Third-party sign-in</translation>
<translation id="3827112369919217609">Assoluto</translation>
<translation id="3827666161959873541">Film per la famiglia</translation>
<translation id="3828924085048779000">Non è consentita una passphrase vuota.</translation>
@@ -1065,9 +1070,9 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="3858027520442213535">Aggiorna data e ora</translation>
<translation id="3858860766373142691">Nome</translation>
<translation id="3872834068356954457">Scienze</translation>
+<translation id="3875783148670536197">Mostrami come fare</translation>
<translation id="3881478300875776315">Mostra meno righe</translation>
<translation id="3884278016824448484">Identificativo del dispositivo in conflitto</translation>
-<translation id="3885155851504623709">Comune</translation>
<translation id="388632593194507180">Monitoraggio rilevato</translation>
<translation id="3886948180919384617">Fascicolatore 3</translation>
<translation id="3890664840433101773">Aggiungi email</translation>
@@ -1097,9 +1102,11 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="3973234410852337861"><ph name="HOST_NAME" /> è bloccato</translation>
<translation id="3978338123949022456">Modalità di ricerca, digita una query e premi Invio per cercare con <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Uccelli</translation>
+<translation id="3985750352229496475">Gestisci indirizzi…</translation>
<translation id="3986705137476756801">Disattiva Sottotitoli in tempo reale per ora</translation>
<translation id="3987940399970879459">Meno di 1 MB</translation>
<translation id="3990250421422698716">Offset jog</translation>
+<translation id="3992684624889376114">Informazioni su questa pagina</translation>
<translation id="3996311196211510766">Il sito <ph name="ORIGIN" /> ha richiesto l'applicazione di un criterio di origine
a tutte le richieste indirizzate a esso, ma questo criterio al momento non può essere applicato.</translation>
<translation id="4006465311664329701">Metodi di pagamento, offerte e indirizzi che utilizzano Google Pay</translation>
@@ -1224,6 +1231,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="4305666528087210886">Impossibile accedere al file</translation>
<translation id="4306529830550717874">Vuoi salvare l'indirizzo?</translation>
<translation id="4306812610847412719">appunti</translation>
+<translation id="4310070645992025887">Cerca nei percorsi</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blocca (predefinita)</translation>
<translation id="4314815835985389558">Gestisci sincronizzazione</translation>
@@ -1274,6 +1282,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">L'utilizzo di un proxy è stato disattivato ma è stata specificata una configurazione proxy esplicita.</translation>
<translation id="4441832193888514600">Ignorato perché il criterio può essere impostato solo da un utente cloud.</translation>
+<translation id="4442470707340296952">Schede di Chrome</translation>
<translation id="4450893287417543264">Non mostrare più</translation>
<translation id="4451135742916150903">Può chiedere di connettersi ai dispositivi HID</translation>
<translation id="4452328064229197696">La password appena usata è stata compromessa nell'ambito di una violazione dei dati. Per proteggere i tuoi account, Gestore delle password di Google consiglia di controllare le password salvate.</translation>
@@ -1412,6 +1421,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="483241715238664915">Attiva gli avvisi</translation>
<translation id="4834250788637067901">Metodi di pagamento, offerte e indirizzi che utilizzano Google Pay</translation>
<translation id="4838327282952368871">Surreale</translation>
+<translation id="4839087176073128681">Esegui il pagamento più velocemente la prossima volta e proteggi la tua carta con la sicurezza leader del settore di Google.</translation>
<translation id="4840250757394056958">Visualizza la tua cronologia di Chrome</translation>
<translation id="484462545196658690">Automatico</translation>
<translation id="484671803914931257">Ricevi uno sconto su <ph name="MERCHANT_NAME" /> e altri negozi</translation>
@@ -1474,6 +1484,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="5011561501798487822">Lingua rilevata</translation>
<translation id="5015510746216210676">Nome macchina:</translation>
<translation id="5017554619425969104">Testo copiato</translation>
+<translation id="5017828934289857214">Ricordamelo dopo</translation>
<translation id="5018422839182700155">Impossibile aprire questa pagina</translation>
<translation id="5019198164206649151">Archivio di backup in stato non valido</translation>
<translation id="5020776957610079374">World Music</translation>
@@ -1493,6 +1504,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="5051305769747448211">Commedie dal vivo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Per inviare questo file tramite Condivisione nelle vicinanze, libera spazio (<ph name="DISK_SPACE_SIZE" />) sul tuo dispositivo}other{Per inviare questi file tramite Condivisione nelle vicinanze, libera spazio (<ph name="DISK_SPACE_SIZE" />) sul tuo dispositivo}}</translation>
<translation id="5056549851600133418">Articoli per te</translation>
+<translation id="5060483733937416656">Hai scelto di eseguire la verifica con Windows Hello sui siti web che utilizzano <ph name="PROVIDER_ORIGIN" />. Questo fornitore potrebbe aver memorizzato informazioni sul tuo metodo di pagamento, che puoi <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Forse cercavi: <ph name="LOOKALIKE_DOMAIN" /></translation>
<translation id="5066056036849835175">Cronologia di stampa</translation>
<translation id="5068234115460527047">Hedge fund</translation>
@@ -1506,10 +1518,8 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="5087286274860437796">Il certificato del server non è valido in questa fase.</translation>
<translation id="5087580092889165836">Aggiungi carta</translation>
<translation id="5088142053160410913">Messaggio all'operatore</translation>
-<translation id="5089810972385038852">Stato</translation>
<translation id="5093232627742069661">Piegatura a Z standard</translation>
<translation id="5094747076828555589">Questo server non è riuscito a dimostrare che si tratta di <ph name="DOMAIN" />; il relativo certificato di sicurezza non è considerato attendibile da Chromium. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione.</translation>
-<translation id="5095208057601539847">Provincia</translation>
<translation id="5097099694988056070">Statistiche del dispositivo quali l'utilizzo di CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Il sito non è sicuro</translation>
@@ -1547,6 +1557,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="5171045022955879922">Cerca o digita un URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Computer</translation>
+<translation id="5177076414499237632">Scopri di più sulla fonte e sull'argomento di questa pagina</translation>
<translation id="5179510805599951267">Non in <ph name="ORIGINAL_LANGUAGE" />? Segnala questo errore</translation>
<translation id="518639307526414276">Cibo e articoli per animali domestici</translation>
<translation id="5190835502935405962">Barra dei Preferiti</translation>
@@ -1707,6 +1718,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="5624120631404540903">Gestisci password</translation>
<translation id="5629630648637658800">Caricamento delle impostazioni criterio non riuscito</translation>
<translation id="5631439013527180824">Token di gestione del dispositivo non valido</translation>
+<translation id="5632485077360054581">Mostrami come fare</translation>
<translation id="5633066919399395251">Gli utenti malintenzionati attualmente presenti sul sito <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> potrebbero cercare di installare sul tuo computer programmi pericolosi che carpiscono o eliminano le tue informazioni (ad esempio, foto, password, messaggi e carte di credito). <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Contenuti ingannevoli bloccati.</translation>
<translation id="5633259641094592098">Film cult e indie</translation>
@@ -1824,6 +1836,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="5989320800837274978">Non sono stati specificati né server proxy fissi né un URL script .pac.</translation>
<translation id="5992691462791905444">Piegatura a Z</translation>
<translation id="5995727681868049093">Gestisci le tue informazioni, la privacy e la sicurezza nel tuo Account Google</translation>
+<translation id="5997247540087773573">La password appena usata è stata compromessa nell'ambito di una violazione dei dati. Per proteggere i tuoi account, Gestore delle password di Google consiglia di cambiarla subito e poi di controllare le password salvate.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> risultati per "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Classico</translation>
<translation id="6008122969617370890">Ordine da N a 1</translation>
@@ -1883,7 +1896,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="6153243098246946146"><ph name="WIDTH" /> × <ph name="HEIGHT" /> in (<ph name="ORIENTATION" />)</translation>
<translation id="6157754950574419155">Rimuovi tutto dalla cronologia</translation>
<translation id="6157877588268064908">Seleziona un indirizzo per conoscere i requisiti e i metodi di spedizione</translation>
-<translation id="6165508094623778733">Ulteriori informazioni</translation>
+<translation id="6165508094623778733">Scopri di più</translation>
<translation id="6167577165590485365">Ultimo tentativo di recupero:</translation>
<translation id="6169916984152623906">Ora puoi navigare in privato. Le altre persone che usano questo dispositivo non vedranno le tue attività, ma i download e i preferiti verranno salvati.</translation>
<translation id="6176716740821145453">Dedica un istante alla valutazione dell'accuratezza</translation>
@@ -1918,7 +1931,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="627746635834430766">Per pagare più velocemente la prossima volta, salva la carta e l'indirizzo di fatturazione sul tuo Account Google.</translation>
<translation id="6279183038361895380">Premi |<ph name="ACCELERATOR" />| per mostrare il puntatore</translation>
<translation id="6280223929691119688">Impossibile consegnare all'indirizzo specificato. Seleziona un indirizzo diverso.</translation>
-<translation id="6282194474023008486">Codice postale</translation>
<translation id="6285507000506177184">Pulsante Gestisci i download in Chrome, premi Invio per gestire i file che hai scaricato in Chrome</translation>
<translation id="6289939620939689042">Colore pagina</translation>
<translation id="6290238015253830360">Gli articoli suggeriti vengono visualizzati qui</translation>
@@ -1940,6 +1952,7 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="6337133576188860026">Consente di liberare meno di <ph name="SIZE" />. Alcuni siti potrebbero caricarsi più lentamente alla prossima visita.</translation>
<translation id="6337534724793800597">Filtra i criteri per nome</translation>
<translation id="6340739886198108203">Il criterio dell'amministratore sconsiglia l'acquisizione di screenshot o registrazioni quando sono visibili contenuti riservati:</translation>
+<translation id="6348220984832452017">Varianti attive</translation>
<translation id="6349101878882523185">Installa <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Nessuna}=1{1 password per <ph name="DOMAIN_LIST" /> (sincronizzata)}=2{2 password per <ph name="DOMAIN_LIST" /> (sincronizzate)}other{# password per (<ph name="DOMAIN_LIST" />, sincronizzate)}}</translation>
<translation id="6355392890578844978">Questo browser non è gestito da un'azienda o da un'altra organizzazione. L'attività svolta su questo dispositivo potrebbe essere gestita al di fuori di Chromium. <ph name="BEGIN_LINK" />Scopri di più<ph name="END_LINK" /></translation>
@@ -1971,7 +1984,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="643051589346665201">Cambia la password Google</translation>
<translation id="6433490469411711332">Modifica informazioni di contatto</translation>
<translation id="6433595998831338502">Connessione negata da <ph name="HOST_NAME" />.</translation>
-<translation id="6438025220577812695">La cambio io</translation>
<translation id="6440503408713884761">Ignorata</translation>
<translation id="6443406338865242315">Le estensioni e i plug-in che hai installato</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2101,9 +2113,9 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="6828866289116430505">Genetica</translation>
<translation id="6831043979455480757">Traduci</translation>
<translation id="6833752742582340615">Salva i dati della carta e di fatturazione nel tuo Account Google per pagare in modo più veloce e sicuro</translation>
-<translation id="6839929833149231406">Area</translation>
<translation id="6846340164947227603">Usa un numero di carta virtuale…</translation>
<translation id="6852204201400771460">Ricaricare l'app?</translation>
+<translation id="6857776781123259569">Gestisci password…</translation>
<translation id="686485648936420384">Risorse per i consumatori</translation>
<translation id="6865412394715372076">Al momento non è possibile verificare questa carta.</translation>
<translation id="6869334554832814367">Prestiti personali</translation>
@@ -2152,7 +2164,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="6965978654500191972">Dispositivo</translation>
<translation id="696703987787944103">Percettivo</translation>
<translation id="6968269510885595029">Utilizzo del tuo token di sicurezza</translation>
-<translation id="6970216967273061347">District</translation>
<translation id="6971439137020188025">Crea rapidamente una nuova presentazione Google in Presentazioni</translation>
<translation id="6972629891077993081">Dispositivi HID</translation>
<translation id="6973656660372572881">Sono stati specificati sia i server proxy fissi che un URL script .pac.</translation>
@@ -2191,7 +2202,6 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<translation id="7081308185095828845">Questa funzionalità non è disponibile sul tuo dispositivo</translation>
<translation id="7083258188081898530">Vassoio 9</translation>
<translation id="7086090958708083563">Caricamento richiesto dall'utente</translation>
-<translation id="7087282848513945231">Contea</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, premi Tab e poi Invio per gestire le autorizzazioni e i dati memorizzati sui siti nelle impostazioni di Chrome</translation>
<translation id="7096937462164235847">L'identità di questo sito web non è stata verificata.</translation>
<translation id="7101893872976785596">Film horror</translation>
@@ -2210,10 +2220,10 @@ In caso contrario l'uso sarà bloccato dalle impostazioni sulla privacy. I conte
<ph name="LIST_ITEM" />Informazioni inserite nei moduli<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Impossibile spedire all'indirizzo specificato. Seleziona un indirizzo diverso.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Il tuo amministratore ha impedito la copia di questi dati.</translation>
<translation id="7135130955892390533">Mostra stato</translation>
<translation id="7138472120740807366">Metodo di consegna</translation>
-<translation id="7139724024395191329">Emirato</translation>
<translation id="7139892792842608322">Vassoio principale</translation>
<translation id="714064300541049402">Spostamento X lato 2 immagine</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2430,6 +2440,7 @@ Ulteriori dettagli:
<translation id="7669271284792375604">I malintenzionati su questo sito potrebbero cercare di indurti con l'inganno a installare programmi che danneggiano la tua navigazione (ad esempio cambiando la tua pagina iniziale o mostrando annunci extra sui siti che visiti).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Azioni intraprese con dati segnalati come riservati (1 azione dall'accesso). <ph name="BEGIN_LINK" />Scopri di più<ph name="END_LINK" />}other{Azioni intraprese con dati segnalati come riservati (# azioni dall'accesso). <ph name="BEGIN_LINK" />Scopri di più<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Mailbox 6</translation>
+<translation id="7675325315208090829">Gestisci metodi di pagamento…</translation>
<translation id="7676643023259824263">Cerca testo negli appunti, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Visualizza e gestisci la tua cronologia di navigazione nelle impostazioni di Chrome</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2472,7 +2483,6 @@ Ulteriori dettagli:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Stesso ordine a faccia in su</translation>
-<translation id="777702478322588152">Prefettura</translation>
<translation id="7791011319128895129">Non pubblicata</translation>
<translation id="7791196057686275387">Rilegatura</translation>
<translation id="7791543448312431591">Aggiungi</translation>
@@ -2563,12 +2573,12 @@ Ulteriori dettagli:
<translation id="8055534648776115597">Corsi di formazione e aggiornamento</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" non è configurato correttamente. La disinstallazione di "<ph name="SOFTWARE_NAME" />" solitamente risolve il problema. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Industria alimentare</translation>
-<translation id="8066955247577885446">Si è verificato un problema.</translation>
<translation id="8067872629359326442">Hai appena inserito la tua password su un sito ingannevole. Chromium può aiutarti. Per cambiare la password e informare Google che il tuo account potrebbe essere a rischio, fai clic su Proteggi account.</translation>
<translation id="8070439594494267500">Icona dell'app</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Crea rapidamente un nuovo foglio Google</translation>
<translation id="8075898834294118863">Gestisci impostazioni dei siti</translation>
+<translation id="8076492880354921740">Schede</translation>
<translation id="8078141288243656252">Non è possibile inserire annotazioni se il documento è ruotato</translation>
<translation id="8079031581361219619">Vuoi ricaricare il sito?</translation>
<translation id="8081087320434522107">Berline</translation>
@@ -2691,6 +2701,7 @@ Ulteriori dettagli:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Impostazioni</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, premi Tab e poi Invio per gestire le preferenze relative ai cookie nelle impostazioni di Chrome</translation>
<translation id="8433057134996913067">In questo modo uscirai dalla maggior parte dei siti web.</translation>
<translation id="8434840396568290395">Animali da compagnia</translation>
@@ -2699,7 +2710,7 @@ Ulteriori dettagli:
<translation id="8438786541497918448">Usare videocamera e microfono?</translation>
<translation id="8443613539889492016">Rap e Hip-hop</translation>
<translation id="8444543005280733648">Reti di computer</translation>
-<translation id="8446884382197647889">Ulteriori informazioni</translation>
+<translation id="8446884382197647889">Scopri di più</translation>
<translation id="8449836157089738489">Apri tutto in un nuovo gruppo di schede</translation>
<translation id="8457125768502047971">Indeterminato</translation>
<translation id="8461694314515752532">Cripta i dati sincronizzati con la tua passphrase di sincronizzazione</translation>
@@ -2789,6 +2800,7 @@ Ulteriori dettagli:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> è il tuo codice per <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Aggiungi questa scheda ai preferiti</translation>
<translation id="8751426954251315517">Riprova la prossima volta</translation>
+<translation id="8757526089434340176">Offerta Google Pay disponibile</translation>
<translation id="8758885506338294482">Videogiochi competitivi</translation>
<translation id="8759274551635299824">La carta è scaduta</translation>
<translation id="87601671197631245">Le tue informazioni (ad esempio password, messaggi o numeri di carte di credito) inviate a questo sito potrebbero essere a rischio, poiché il sito utilizza una configurazione di sicurezza obsoleta.</translation>
@@ -2796,6 +2808,7 @@ Ulteriori dettagli:
<translation id="8763927697961133303">Dispositivo USB</translation>
<translation id="8763986294015493060">Chiudi tutte le finestre di navigazione in incognito aperte al momento</translation>
<translation id="8766943070169463815">Il foglio per l'autenticazione delle credenziali di pagamento sicure è aperto</translation>
+<translation id="8767765348545497220">Chiudi fumetto guida</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocicli</translation>
<translation id="8790007591277257123">&amp;Ripeti eliminazione</translation>
@@ -2808,6 +2821,7 @@ Ulteriori dettagli:
<translation id="8806285662264631610">Prodotti per il corpo e per il bagno</translation>
<translation id="8807160976559152894">Taglia dopo ciascuna pagina</translation>
<translation id="8808828119384186784">Impostazioni di Chrome</translation>
+<translation id="8813277370772331957">Ricordamelo dopo</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, premi Tab poi Invio per aggiornare Chrome dalle relative impostazioni</translation>
<translation id="8820817407110198400">Preferiti</translation>
<translation id="882338992931677877">Slot manuale</translation>
@@ -2987,6 +3001,7 @@ Ulteriori dettagli:
<translation id="988159990683914416">Build</translation>
<translation id="989988560359834682">Modifica indirizzo</translation>
<translation id="991413375315957741">sensori di movimento o della luce</translation>
+<translation id="992110854164447044">Una carta virtuale nasconde la tua carta effettiva per proteggerti meglio da potenziali attività fraudolente. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" non è stato installato correttamente sul computer o sulla rete:
diff --git a/chromium/components/strings/components_strings_iw.xtb b/chromium/components/strings/components_strings_iw.xtb
index 365e620f091..41b26b0483f 100644
--- a/chromium/components/strings/components_strings_iw.xtb
+++ b/chromium/components/strings/components_strings_iw.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">מענקי×, מלגות לימוד וסיוע כספי</translation>
<translation id="1048785276086539861">המסמך הזה יחזור לתצוגת דף יחיד בזמן עריכת ההערות</translation>
<translation id="1050038467049342496">סגירת ×פליקציות ×חרות</translation>
+<translation id="1053959602163383901">בחרת לבצע ×ימות ב×מצעות מכשיר ×ימות ב××ª×¨×™× ×©×ž×©×ª×ž×©×™× ×‘-<ph name="PROVIDER_ORIGIN" />. יכול להיות שהספק ×”×–×” ×חסן מידע על ×מצעי ×”×ª×©×œ×•× ×©×œ×š, ש×ותו ×פשר <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;ביטול הוספה</translation>
<translation id="1056663316309890343">תוכנות לתמונות</translation>
<translation id="1056898198331236512">×זהרה</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">שיטת ×יסוף</translation>
<translation id="1281476433249504884">×ž×¢×¨×™× 1</translation>
<translation id="1285320974508926690">××™× ×™ רוצה לקבל ×ª×¨×’×•× ×©×œ ×תר ×–×”</translation>
+<translation id="1288548991597756084">שמירה מ×ובטחת של הכרטיס</translation>
<translation id="1292571435393770077">מגש 16</translation>
<translation id="1292701964462482250">â€"יש תוכנה במחשב שלך שמונעת מ-Chrome להתחבר ב×ופן מ×ובטח ×ל ×”×ינטרנט" (מחשבי Windows בלבד)</translation>
<translation id="1294154142200295408">ורי×ציות של שורת פקודה</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;כדי לפתור ×ת השגי××”, צריך ללחוץ על &lt;strong&gt;התחברות&lt;/strong&gt; בדף ×©×ž× ×¡×™× ×œ×¤×ª×•×—.&lt;/p&gt;</translation>
<translation id="1507780850870535225">עיצוב נוף</translation>
<translation id="1513706915089223971">רשימה של רשומות היסטוריות</translation>
+<translation id="1516097932025103760">â€×”×¤×¨×˜×™× ×™×•×¦×¤× ×• ויישמרו בצורה מ×ובטחת, וקוד ×”×ימות (CVC) ××£ ×¤×¢× ×œ× ×™×™×©×ž×¨.</translation>
<translation id="1517433312004943670">יש צורך במספר טלפון</translation>
<translation id="1519264250979466059">â€×ª×ריך ×”-Build</translation>
<translation id="1521159554480556801">×ומנות טקסטיל ו×ריגי×</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">שמירה ומילוי של ×מצעי תשלו×</translation>
<translation id="1663943134801823270">â€×”×›×¨×˜×™×¡×™× ×•×”×›×ª×•×‘×•×ª × ×œ×§×—×™× ×ž-Chrome. ×פשר לנהל ××•×ª× ×‘<ph name="BEGIN_LINK" />הגדרות<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">×“×¤×™× ×‘<ph name="SOURCE_LANGUAGE" /> יתורגמו מעכשיו ל<ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">×œ×ª×©×œ×•× ×‘×מצעות <ph name="CARD_DETAIL" /> כדי לממש ×ת המבצע</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> ל<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">הקצה הקצר ר×שון</translation>
<translation id="168693727862418163">×ימות ערך המדיניות ×”×–×” נכשל בבדיקה מול הסכימה והמערכת ×ª×ª×¢×œ× ×ž×ž× ×•.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">חיישני תנועה</translation>
<translation id="1717494416764505390">תיבת דו×ר 3</translation>
<translation id="1718029547804390981">המסמך גדול מדי ×•×œ× × ×™×ª×Ÿ להוסיף לו הערות</translation>
+<translation id="1720941539803966190">סגירת המדריך</translation>
<translation id="1721424275792716183">* זהו שדה חובה</translation>
<translation id="1727613060316725209">×”×ישור תקין</translation>
<translation id="1727741090716970331">הוספת מספר כרטיס חוקי</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">ברביקיו וצלייה</translation>
<translation id="2053111141626950936">×“×¤×™× ×‘<ph name="LANGUAGE" /> ×œ× ×™×ª×•×¨×’×ž×•.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{â€×›×©×”פקד ×”×–×” פועל והסטטוס פעיל, Chrome בודק מהי הקבוצה הגדולה של ×נשי×, ×ו "הקבוצה בעלת המ××¤×™×™× ×™× ×”×ž×©×•×ª×¤×™×", שפעילות הגלישה ×”×חרונה שלך ×”×›×™ דומה לה. ×ž×¤×¨×¡×ž×™× ×™×›×•×œ×™× ×œ×‘×—×•×¨ מודעות שמת×ימות לקבוצה, ופעילות הגלישה שלך נשמרת ב×ופן פרטי במכשיר. הקבוצה מתעדכנת מדי יו×.}=1{â€×›×©×”פקד ×”×–×” פועל והסטטוס פעיל, Chrome בודק מהי הקבוצה הגדולה של ×נשי×, ×ו "הקבוצה בעלת המ××¤×™×™× ×™× ×”×ž×©×•×ª×¤×™×", שפעילות הגלישה ×”×חרונה שלך ×”×›×™ דומה לה. ×ž×¤×¨×¡×ž×™× ×™×›×•×œ×™× ×œ×‘×—×•×¨ מודעות שמת×ימות לקבוצה, ופעילות הגלישה שלך נשמרת ב×ופן פרטי במכשיר. הקבוצה מתעדכנת מדי יו×.}two{â€×›×©×”פקד ×”×–×” פועל והסטטוס פעיל, Chrome בודק מהי הקבוצה הגדולה של ×נשי×, ×ו "הקבוצה בעלת המ××¤×™×™× ×™× ×”×ž×©×•×ª×¤×™×", שפעילות הגלישה ×”×חרונה שלך ×”×›×™ דומה לה. ×ž×¤×¨×¡×ž×™× ×™×›×•×œ×™× ×œ×‘×—×•×¨ מודעות שמת×ימות לקבוצה, ופעילות הגלישה שלך נשמרת ב×ופן פרטי במכשיר. הקבוצה מתעדכנת מדי ×™×•×ž×™×™× ({NUM_DAYS}).}many{â€×›×©×”פקד ×”×–×” פועל והסטטוס פעיל, Chrome בודק מהי הקבוצה הגדולה של ×נשי×, ×ו "הקבוצה בעלת המ××¤×™×™× ×™× ×”×ž×©×•×ª×¤×™×", שפעילות הגלישה ×”×חרונה שלך ×”×›×™ דומה לה. ×ž×¤×¨×¡×ž×™× ×™×›×•×œ×™× ×œ×‘×—×•×¨ מודעות שמת×ימות לקבוצה, ופעילות הגלישה שלך נשמרת ב×ופן פרטי במכשיר. הקבוצה מתעדכנת מדי {NUM_DAYS} ימי×.}other{â€×›×©×”פקד ×”×–×” פועל והסטטוס פעיל, Chrome בודק מהי הקבוצה הגדולה של ×נשי×, ×ו "הקבוצה בעלת המ××¤×™×™× ×™× ×”×ž×©×•×ª×¤×™×", שפעילות הגלישה ×”×חרונה שלך ×”×›×™ דומה לה. ×ž×¤×¨×¡×ž×™× ×™×›×•×œ×™× ×œ×‘×—×•×¨ מודעות שמת×ימות לקבוצה, ופעילות הגלישה שלך נשמרת ב×ופן פרטי במכשיר. הקבוצה מתעדכנת מדי {NUM_DAYS} ימי×.}}</translation>
-<translation id="2053553514270667976">מיקוד</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{הצעה ×חת}two{שתי הצעות}many{# הצעות}other{# הצעות}}</translation>
+<translation id="2066915425250589881">לבקש למחוק</translation>
<translation id="2068528718802935086">תינוקות ופעוטות</translation>
<translation id="2071156619270205202">הכרטיס ×”×–×” ×œ× ×¢×•×ž×“ בדרישות לקבלת מספר כרטיס וירטו×לי.</translation>
<translation id="2071692954027939183">ההתר×ות נחסמו ב×ופן ×וטומטי ×›×™ ברוב ×”×ž×§×¨×™× ×œ× × ×ª×ª הרש××” להציג ×ותן</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">â€×”לחצן 'ניהול הסנכרון', יש להקיש על Enter כדי לקבוע ××™×–×” מידע יסונכרן בהגדרות Chrome</translation>
<translation id="2091887806945687916">צליל</translation>
<translation id="2094505752054353250">××™ הת×מה בדומייני×</translation>
-<translation id="2096368010154057602">מחלקה</translation>
<translation id="2099652385553570808">3 סיכות הידוק בצד שמ×ל</translation>
<translation id="2101225219012730419">גרסה:</translation>
<translation id="2102134110707549001">הצעת סיסמה חזקה…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">פוטבול ×מריק××™</translation>
<translation id="2187317261103489799">זהה (ברירת מחדל)</translation>
<translation id="2188375229972301266">× ×™×§×•×‘×™× ×ž×¨×•×‘×™× ×‘×—×œ×§ התחתון</translation>
-<translation id="2188852899391513400">â€×”סיסמה שבה השתמשת עכשיו ×ותרה בפרצה ב×בטחת מידע. כדי להגן על החשבונות שלך, לפי מנהל הסיסמ×ות של Google, מומלץ לשנות ×ותה עכשיו ו××– לבדוק ×ת הסיסמ×ות השמורות שלך.</translation>
<translation id="219906046732893612">שיפוץ הבית</translation>
<translation id="2202020181578195191">עליך להזין שנת תפוגה חוקית</translation>
<translation id="22081806969704220">מגש 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">עריכת קובץ</translation>
<translation id="2215963164070968490">כלבי×</translation>
<translation id="2218879909401188352">×ª×•×§×¤×™× ×©×ž×©×ª×ž×©×™× ×¢×›×©×™×• ב-<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ×¢×œ×•×œ×™× ×œ×”×ª×§×™×Ÿ ×פליקציות מסוכנות ×•×œ×’×¨×•× × ×–×§ למכשיר שלך, להגדיל ×ת ×”×—×™×•×‘×™× ×©×œ×š על החבילה לנייד ×ו לגנוב ×ת המידע ×”×ישי שלך. <ph name="BEGIN_LEARN_MORE_LINK" />מידע נוסף<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">חזרה על המדריך</translation>
+<translation id="2219735899272417925">יש ל×תחל ×ת המכשיר</translation>
<translation id="2224337661447660594">×ין ×ינטרנט</translation>
<translation id="2230458221926704099">×פשר לתקן ×ת החיבור ב×מצעות <ph name="BEGIN_LINK" />×פליקציית הבדיקה<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">שליחה</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">×ין הרש××” (ברירת מחדל)</translation>
<translation id="2512413427717747692">â€×œ×—צן להגדרת Chrome כדפדפן ברירת המחדל, יש ללחוץ על Enter כדי להגדיר ×ת Chrome כדפדפן ברירת המחדל של המערכת בהגדרות iOS</translation>
<translation id="2515629240566999685">לבדוק ×ת ×”×ות ב×זור שלך</translation>
+<translation id="2515761554693942801">â€×‘חרת לבצע ×ימות ב×מצעות Touch ID ב××ª×¨×™× ×©×ž×©×ª×ž×©×™× ×‘-<ph name="PROVIDER_ORIGIN" />. יכול להיות שהספק ×”×–×” ×חסן מידע על ×מצעי ×”×ª×©×œ×•× ×©×œ×š, ש×ותו ×פשר <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">סיכת הידוק בפינה הימנית התחתונה</translation>
<translation id="2521736961081452453">יצירת טופס</translation>
<translation id="2523886232349826891">הכרטיס יישמר רק במכשיר הזה</translation>
<translation id="2524461107774643265">הוספת עוד מידע</translation>
<translation id="2529899080962247600">השדה ×”×–×” יכול לכלול עד <ph name="MAX_ITEMS_LIMIT" /> רשומות. המערכת ×ª×ª×¢×œ× ×ž×¨×©×•×ž×•×ª נוספות.</translation>
+<translation id="253493526287553278">הצגת ×”×¤×¨×˜×™× ×©×œ קוד ההטבה</translation>
<translation id="2535585790302968248">כדי לגלוש ב×ופן פרטי, יש לפתוח כרטיסייה פרטית חדשה</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{ודומיין ×חד נוסף}two{ו-# ×“×•×ž×™×™× ×™× × ×•×¡×¤×™×}many{ו-# ×“×•×ž×™×™× ×™× × ×•×¡×¤×™×}other{ו-# ×“×•×ž×™×™× ×™× × ×•×¡×¤×™×}}</translation>
<translation id="2536110899380797252">הוספת כתובת</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">×ומנות ×”×¦×™×œ×•× ×•×ומנות דיגיטלית</translation>
<translation id="2601150049980261779">×¡×¨×˜×™× ×¨×•×ž× ×˜×™×™×</translation>
<translation id="2604589665489080024">מוזיקת פופ</translation>
-<translation id="2609632851001447353">ורי×ציות</translation>
<translation id="2610561535971892504">לחיצה להעתקה</translation>
<translation id="2617988307566202237">â€Chrome <ph name="BEGIN_EMPHASIS" />×œ× ×™×©×ž×•×¨<ph name="END_EMPHASIS" /> ×ת ×”× ×ª×•× ×™× ×”×‘××™×:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">ימי הולדת</translation>
<translation id="2677748264148917807">יצי××”</translation>
+<translation id="2679714844901977852">â€×œ×ª×©×œ×•× מהיר ומ×ובטח יותר בקופה, ×פשר לשמור ×ת פרטי החיוב והכרטיס בחשבון Google, <ph name="USER_EMAIL" /></translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">ההת×מה הטובה ביותר</translation>
<translation id="2688969097326701645">כן, ×× ×™ רוצה להמשיך</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">â€×¡×¤×§×™ ×ינטרנט (ISP)</translation>
<translation id="2856444702002559011">ייתכן ×©×ª×•×§×¤×™× ×ž× ×¡×™× ×œ×’× ×•×‘ ×ת ×”×¤×¨×˜×™× ×©×œ×š מה×תר <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (לדוגמה, סיסמ×ות, הודעות ×ו כרטיסי ×שר××™). <ph name="BEGIN_LEARN_MORE_LINK" />מידע נוסף<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ב×תר ×”×–×” מוצגות מודעות מפריעות ×ו מטעות.</translation>
-<translation id="286512204874376891">כרטיס וירטו×לי מסווה ×ת הכרטיס בפועל כדי לעזור להגן עליך מפני תרמיות פוטנצי×ליות. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ידידותי</translation>
<translation id="28761159517501904">סרטי×</translation>
<translation id="2876489322757410363">בחרת לצ×ת מהמצב ×”×נונימי כדי ×œ×©×œ× ×‘×מצעות ×פליקציה חיצונית. להמשיך?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">דיסק</translation>
<translation id="3162559335345991374">â€×™×™×ª×›×Ÿ שרשת ×”-Wi-Fi דורשת כניסה לדף ההתחברות שלה.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">××™</translation>
<translation id="3176929007561373547">â€×™×© לבדוק ×ת הגדרות שרת ×”-proxy ×ו לפנות למנהל הרשת
כדי ×œ×•×•×“× ×©×©×¨×ª ×”-proxy פועל. ×× ×œ× × ×¨××” לך שעליך
להשתמש בשרת proxy:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">שגי×ת שעון</translation>
<translation id="3369459162151165748">חלקי חילוף ו××‘×™×–×¨×™× ×œ×¨×›×‘</translation>
<translation id="3371064404604898522">â€×”גדרת Chrome כדפדפן ברירת המחדל</translation>
-<translation id="3371076217486966826"><ph name="URL" /> רוצה:
- • ליצור מפות תלת ממד של ×”×זור שמסביבך ולעקוב ×חרי ×”×ž×™×§×•× ×©×œ המצלמה.
- • להשתמש במצלמה שלך.</translation>
<translation id="337363190475750230">ניהול התצורה בוטל</translation>
<translation id="3375754925484257129">â€×”רצה של בדיקת ×בטחה ב-Chrome</translation>
<translation id="3377144306166885718">â€×‘שרת נעשה שימוש בגרסה מיושנת של TLS.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">כתובת למשלוח</translation>
<translation id="3402261774528610252">â€×”חיבור ששימש לצורך טעינת ×”×תר ×”×–×” ×”×™×” מבוסס על הפרוטוקול TLS 1.0 ×ו TLS 1.1, ×©×”×•×¦× ×ž×©×™×ž×•×© ויושבת בעתיד. ×חרי שהפרוטוקול יושבת, ×ž×©×ª×ž×©×™× ×œ× ×™×•×›×œ×• לטעון ×ת ×”×תר ×”×–×”. יש להפעיל בשרת ×ת הפרוטוקול TLS 1.2 ו×ילך.</translation>
<translation id="3405664148539009465">הת×מה ×ישית של גופני×</translation>
+<translation id="3407789382767355356">כניסה של צד שלישי</translation>
<translation id="3409896703495473338">ניהול הגדרות ×”×בטחה</translation>
<translation id="3414952576877147120">גודל:</translation>
<translation id="3417660076059365994">â€×§×‘×¦×™× ×©×ž×¢×œ×™× ×ו ×ž×¦×¨×¤×™× × ×©×œ×—×™× ×œ-Google Cloud ×ו ×œ×¦×“×“×™× ×©×œ×™×©×™×™× ×œ×¦×•×¨×š ניתוח. לדוגמה, ייתכן ×©×”×§×‘×¦×™× ×™×¢×‘×¨×• סריקה כדי ל×תר מידע ×ישי רגיש ×ו תוכנות זדוניות.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">רשימות ×¡×¨×˜×™× ×•×–×ž× ×™ הקרנה בקולנוע</translation>
<translation id="3479552764303398839">×œ× ×¢×›×©×™×•</translation>
<translation id="3484560055331845446">â€×™×™×ª×›×Ÿ שמישהו ×חר ישלול ×ת הגישה שלך לחשבון Google. לגלישה בטוחה ב-Chrome, מומלץ לשנות ×ת הסיסמה עכשיו.</translation>
+<translation id="3484861421501147767">תזכורת: יש קוד הטבה שמור</translation>
<translation id="3487845404393360112">מגש 4</translation>
<translation id="3495081129428749620">חיפוש בדף
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">ניהול</translation>
<translation id="3816482573645936981">ערך (הוחלף)</translation>
<translation id="382518646247711829">â€×× ×¢×©×™×ª שימוש בשרת Proxy...</translation>
+<translation id="3826050100957962900">כניסה של צד שלישי</translation>
<translation id="3827112369919217609">מוחלט</translation>
<translation id="3827666161959873541">סרטי משפחה</translation>
<translation id="3828924085048779000">×ין ×פשרות להשתמש בביטוי סיסמה ריק.</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">עדכון הת×ריך והשעה</translation>
<translation id="3858860766373142691">ש×</translation>
<translation id="3872834068356954457">מדע</translation>
+<translation id="3875783148670536197">×יך ×¢×•×©×™× ×–×ת?</translation>
<translation id="3881478300875776315">×× ×™ רוצה לר×ות פחות שורות</translation>
<translation id="3884278016824448484">מזהה מכשיר מתנגש</translation>
-<translation id="3885155851504623709">פ×ריש</translation>
<translation id="388632593194507180">זוהה מעקב</translation>
<translation id="3886948180919384617">×ž×¢×¨×™× 3</translation>
<translation id="3890664840433101773">הוספת כתובת ×ימייל</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> חסו×</translation>
<translation id="3978338123949022456">â€×ž×¦×‘ חיפוש, יש להזין ש×ילתה וללחוץ על Enter כדי לחפש ב×מצעות <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">ציפורי×</translation>
+<translation id="3985750352229496475">לניהול הכתובות…</translation>
<translation id="3986705137476756801">השבתת הכתוביות המיידיות נכון לעכשיו</translation>
<translation id="3987940399970879459">â€×¤×—ות מ-‎1 MB</translation>
<translation id="3990250421422698716">היסט הלשונית</translation>
+<translation id="3992684624889376114">מידע על הדף הזה</translation>
<translation id="3996311196211510766">×”×תר <ph name="ORIGIN" /> ביקש להחיל מדיניות מקור
על כל הבקשות הנשלחות ×ליו, ×בל ×œ× × ×™×ª×Ÿ כרגע להחיל ×ת המדיניות הזו.</translation>
<translation id="4006465311664329701">â€×מצעי תשלו×, ×ž×‘×¦×¢×™× ×•×›×ª×•×‘×•×ª שנשמרו ב‑Google Pay</translation>
@@ -1225,6 +1232,7 @@
<translation id="4305666528087210886">×œ× × ×™×ª×Ÿ לגשת לקובץ</translation>
<translation id="4306529830550717874">לשמור ×ת הכתובת?</translation>
<translation id="4306812610847412719">לוח</translation>
+<translation id="4310070645992025887">חיפוש בתהליכי×</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">חסומה (ברירת מחדל)</translation>
<translation id="4314815835985389558">ניהול הסנכרון</translation>
@@ -1275,6 +1283,7 @@
<translation id="4435702339979719576">Postcard)‎</translation>
<translation id="443673843213245140">â€×”שימוש בשרת Proxy הושבת, ×ך צוינה תצורת שרת Proxy מפורשת.</translation>
<translation id="4441832193888514600">המערכת מתעלמת מהמדיניות ×›×™ ניתן להגדיר ×ותה רק כמדיניות משתמש בענן.</translation>
+<translation id="4442470707340296952">â€×›×¨×˜×™×¡×™×•×ª Chrome</translation>
<translation id="4450893287417543264">×ין להציג שוב</translation>
<translation id="4451135742916150903">â€×”×תר יכול לבקש הרש××” להתחבר למכשירי HID</translation>
<translation id="4452328064229197696">â€×”סיסמה שבה השתמשת עכשיו ×ותרה בפרצה ב×בטחת מידע. כדי להגן על החשבונות שלך, לפי מנהל הסיסמ×ות של Google, מומלץ לבדוק ×ת הסיסמ×ות השמורות שלך.</translation>
@@ -1417,6 +1426,7 @@ Del</translation>
<translation id="483241715238664915">הפעלת ×”×זהרות</translation>
<translation id="4834250788637067901">â€×מצעי תשלו×, ×ž×‘×¦×¢×™× ×•×›×ª×•×‘×•×ª שנשמרו ב‑Google Pay</translation>
<translation id="4838327282952368871">חלומי</translation>
+<translation id="4839087176073128681">â€×”×ª×©×œ×•× ×”×‘× ×™×”×™×” מהיר יותר והכרטיס ×™×”×™×” מוגן ב×מצעי ×”×בטחה ×”×ž×•×‘×™×œ×™× ×©×œ Google.</translation>
<translation id="4840250757394056958">â€×”צגת ההיסטוריה ב-Chrome</translation>
<translation id="484462545196658690">×וטומטי</translation>
<translation id="484671803914931257">קבלת הנחות על ×ž×•×¦×¨×™× ×©×œ <ph name="MERCHANT_NAME" /> ועוד</translation>
@@ -1479,6 +1489,7 @@ Del</translation>
<translation id="5011561501798487822">זוהתה השפה</translation>
<translation id="5015510746216210676">×©× ×”×ž×—×©×‘:</translation>
<translation id="5017554619425969104">טקסט שהעתקת</translation>
+<translation id="5017828934289857214">×× ×™ רוצה תזכורת מ×וחר יותר</translation>
<translation id="5018422839182700155">×œ× × ×™×ª×Ÿ לפתוח ×ת הדף</translation>
<translation id="5019198164206649151">×”×חסון המשמש כגיבוי ×ינו תקין</translation>
<translation id="5020776957610079374">מוזיקת עול×</translation>
@@ -1498,11 +1509,12 @@ Del</translation>
<translation id="5051305769747448211">מופעי קומדיה</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{כדי לשלוח ×ת הקובץ ×”×–×” ב×מצעות 'שיתוף בקרבת מקו×', צריך לפנות שטח ×חסון (<ph name="DISK_SPACE_SIZE" />) במכשיר}two{כדי לשלוח ×ת ×”×§×‘×¦×™× ×”×לה ב×מצעות 'שיתוף בקרבת מקו×', צריך לפנות שטח ×חסון (<ph name="DISK_SPACE_SIZE" />) במכשיר}many{כדי לשלוח ×ת ×”×§×‘×¦×™× ×”×לה ב×מצעות 'שיתוף בקרבת מקו×', צריך לפנות שטח ×חסון (<ph name="DISK_SPACE_SIZE" />) במכשיר}other{כדי לשלוח ×ת ×”×§×‘×¦×™× ×”×לה ב×מצעות 'שיתוף בקרבת מקו×', צריך לפנות שטח ×חסון (<ph name="DISK_SPACE_SIZE" />) במכשיר}}</translation>
<translation id="5056549851600133418">מ××ž×¨×™× ×©×¢×©×•×™×™× ×œ×¢× ×™×™×Ÿ ×ותך</translation>
+<translation id="5060483733937416656">â€×‘חרת לבצע ×ימות ב×מצעות Windows Hello ב××ª×¨×™× ×©×ž×©×ª×ž×©×™× ×‘-<ph name="PROVIDER_ORIGIN" />. יכול להיות שהספק ×”×–×” ×חסן מידע על ×מצעי ×”×ª×©×œ×•× ×©×œ×š, ש×ותו ×פשר <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">×”×× ×”×ª×›×•×•× ×ª ל-<ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">היסטוריית הדפסות</translation>
<translation id="5068234115460527047">קרנות גידור</translation>
<translation id="5068524481479508725">A10</translation>
-<translation id="5068778127327928576">{NUM_COOKIES,plural, =1{(×חד × ×ž×¦× ×‘×©×™×ž×•×©)}two{(# in use)}many{(# in use)}other{(# נמצ××™× ×‘×©×™×ž×•×©)}}</translation>
+<translation id="5068778127327928576">{NUM_COOKIES,plural, =1{(×חד × ×ž×¦× ×‘×©×™×ž×•×©)}two{(# נמצ××™× ×‘×©×™×ž×•×©)}many{(# נמצ××™× ×‘×©×™×ž×•×©)}other{(# נמצ××™× ×‘×©×™×ž×•×©)}}</translation>
<translation id="5070335125961472645">â€<ph name="BEGIN_LINK" />לבדוק ×ת כתובת שרת ×”-Proxy<ph name="END_LINK" /></translation>
<translation id="5070838744279127212">גליל עשירי</translation>
<translation id="507130231501693183">תיבת דו×ר 4</translation>
@@ -1511,10 +1523,8 @@ Del</translation>
<translation id="5087286274860437796">×”×ישור של השרת ×ינו תקף כעת.</translation>
<translation id="5087580092889165836">הוספת כרטיס</translation>
<translation id="5088142053160410913">הודעה ל×ופרטור</translation>
-<translation id="5089810972385038852">מדינה</translation>
<translation id="5093232627742069661">â€×§×™×¤×•×œ Z</translation>
<translation id="5094747076828555589">â€×”שרת ×”×–×” ×œ× ×”×¦×œ×™×— להוכיח ×©×”×•× <ph name="DOMAIN" />. ×ישור ×”×בטחה שלו ×œ× × ×—×©×‘ כמהימן על ידי Chromium. ייתכן שהסיבה לכך ×”×™× ×ª×¦×•×¨×” שגויה ×ו תוקף המיירט ×ת החיבור שלך.</translation>
-<translation id="5095208057601539847">פרובינציה</translation>
<translation id="5097099694988056070">â€× ×ª×•× ×™× ×¡×˜×˜×™×¡×˜×™×™× ×©×œ המכשיר, כמו ניצול יחידת העיבוד המרכזית (CPU)/זיכרון RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">×”×תר ×œ× ×ž×ובטח</translation>
@@ -1552,6 +1562,7 @@ Del</translation>
<translation id="5171045022955879922">טקסט ×ו כתובת ×תר לחיפוש</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">מכונה</translation>
+<translation id="5177076414499237632">מידע על המקור ×•×”× ×•×©× ×©×œ הדף ×”×–×”</translation>
<translation id="5179510805599951267">×œ× ×‘<ph name="ORIGINAL_LANGUAGE" />? דיווח על שגי××” זו</translation>
<translation id="518639307526414276">מזון וציוד לחיות מחמד</translation>
<translation id="5190835502935405962">סרגל הסימניות</translation>
@@ -1713,6 +1724,7 @@ Del</translation>
<translation id="5624120631404540903">ניהול סיסמ×ות</translation>
<translation id="5629630648637658800">טעינת הגדרות המדיניות נכשלה</translation>
<translation id="5631439013527180824">×סימון ניהול המכשיר ×ינו חוקי</translation>
+<translation id="5632485077360054581">×יך ×¢×•×©×™× ×–×ת?</translation>
<translation id="5633066919399395251">×ª×•×§×¤×™× ×©× ×ž×¦××™× ×›×¨×’×¢ ב×תר <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ×¢×œ×•×œ×™× ×œ×”×ª×§×™×Ÿ במחשב שלך תוכנות מסוכנות שגונבות מידע ×ו מוחקות ×ותו (למשל, תמונות, סיסמ×ות, הודעות וכרטיסי ×שר××™). <ph name="BEGIN_LEARN_MORE_LINK" />מידע נוסף<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">תוכן מטעה נחס×</translation>
<translation id="5633259641094592098">סרטי ×ינדי וק×לט</translation>
@@ -1830,6 +1842,7 @@ Del</translation>
<translation id="5989320800837274978">â€×œ× צוינו שרתי Proxy ×§×‘×•×¢×™× ×•×œ× ×›×ª×•×‘×ª ×תר של סקריפט ‎.pac</translation>
<translation id="5992691462791905444">â€×§×™×¤×•×œ מסוג Engineering Z</translation>
<translation id="5995727681868049093">â€× ×™×”ול של המידע, הפרטיות וה×בטחה בחשבון Google</translation>
+<translation id="5997247540087773573">â€×”סיסמה שבה השתמשת עכשיו ×ותרה בפרצה ב×בטחת מידע. כדי להגן על החשבונות שלך, לפי מנהל הסיסמ×ות של Google, מומלץ לשנות ×ותה עכשיו ולבדוק ×ת הסיסמ×ות השמורות שלך.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> תוצ×ות בשביל '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">קל×סי</translation>
<translation id="6008122969617370890">â€×¡×™×“ור מ-N עד 1</translation>
@@ -1925,7 +1938,6 @@ Del</translation>
<translation id="627746635834430766">â€×›×“×™ ×œ×©×œ× ×ž×”×¨ יותר ×‘×¤×¢× ×”×‘××”, ×פשר לשמור בחשבון Google ×ת פרטי הכרטיס ו×ת הכתובת לחיוב.</translation>
<translation id="6279183038361895380">יש להקיש על |<ph name="ACCELERATOR" />| כדי להציג ×ת הסמן</translation>
<translation id="6280223929691119688">×œ× × ×™×ª×Ÿ לבצע מסירה בכתובת זו. עליך לבחור כתובת ×חרת.</translation>
-<translation id="6282194474023008486">מיקוד</translation>
<translation id="6285507000506177184">â€×”לחצן לניהול הורדות ב-Chrome, ×ž×§×™×©×™× ×¢×œ Enter לניהול ×”×§×‘×¦×™× ×©×”×•×¨×“×ª ב-Chrome</translation>
<translation id="6289939620939689042">צבע הדף</translation>
<translation id="6290238015253830360">הצעות של מ××ž×¨×™× ×¢×‘×•×¨×š מופיעות ×›×ן</translation>
@@ -1947,6 +1959,7 @@ Del</translation>
<translation id="6337133576188860026">פינוי של פחות מ-<ph name="SIZE" /> מהנפח. ייתכן שחלק מה××ª×¨×™× ×™×™×˜×¢× ×• ל×ט יותר בביקור ×”×‘× ×©×œ×š.</translation>
<translation id="6337534724793800597">סינון מדיניות לפי ש×</translation>
<translation id="6340739886198108203">בהת×× ×œ×ž×“×™× ×™×•×ª של מנהל המערכת, ×œ× ×ž×•×ž×œ×¥ ×œ×¦×œ× ×¦×™×œ×•×ž×™ מסך ×ו הקלטות כשתוכן סודי גלוי:</translation>
+<translation id="6348220984832452017">הגרס×ות הפעילות</translation>
<translation id="6349101878882523185">התקנת <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{×ין}=1{סיסמה ×חת (עבור <ph name="DOMAIN_LIST" />, מסונכרנת)}=2{שתי סיסמ×ות (עבור <ph name="DOMAIN_LIST" />, מסונכרנות)}many{# סיסמ×ות (עבור <ph name="DOMAIN_LIST" />, מסונכרנות)}other{# סיסמ×ות (עבור <ph name="DOMAIN_LIST" />, מסונכרנות)}}</translation>
<translation id="6355392890578844978">â€×”דפדפן ×”×–×” ×œ× ×ž× ×•×”×œ על ידי חברה ×ו ×רגון ×חר. ייתכן שהפעילות במכשיר ×”×–×” מנוהלת מחוץ ל-Chromium. <ph name="BEGIN_LINK" />למידע נוסף<ph name="END_LINK" /></translation>
@@ -1978,7 +1991,6 @@ Del</translation>
<translation id="643051589346665201">â€×©×™× ×•×™ הסיסמה ל-Google</translation>
<translation id="6433490469411711332">עריכת ×”×¤×¨×˜×™× ×œ×™×¦×™×¨×ª קשר</translation>
<translation id="6433595998831338502">×œ× × ×™×ª×Ÿ ×”×™×” להתחבר ×ל <ph name="HOST_NAME" />.</translation>
-<translation id="6438025220577812695">×שנה ×ותה בעצמי</translation>
<translation id="6440503408713884761">המערכת מתעלמת מהפריט</translation>
<translation id="6443406338865242315">×ילו תוספות ויישומי פל×גין התקנת.</translation>
<translation id="6446163441502663861">Kahu (Envelope)‎</translation>
@@ -2108,9 +2120,9 @@ Del</translation>
<translation id="6828866289116430505">גנטיקה</translation>
<translation id="6831043979455480757">תרגו×</translation>
<translation id="6833752742582340615">â€×œ×ª×©×œ×•× מהיר ומ×ובטח יותר בקופה, ×פשר לשמור ×ת פרטי החיוב והכרטיס בחשבון Google</translation>
-<translation id="6839929833149231406">×זור</translation>
<translation id="6846340164947227603">ניתן להזין מספר כרטיס וירטו×לי…</translation>
<translation id="6852204201400771460">לטעון מחדש ×ת ×”×פליקציה?</translation>
+<translation id="6857776781123259569">לניהול הסיסמ×ות…</translation>
<translation id="686485648936420384">מש××‘×™× ×œ×¦×¨×›× ×™×</translation>
<translation id="6865412394715372076">××™ ×פשר ל×מת כרגע ×ת הכרטיס</translation>
<translation id="6869334554832814367">הלוו×ות ×ישיות</translation>
@@ -2159,7 +2171,6 @@ Del</translation>
<translation id="6965978654500191972">התקן</translation>
<translation id="696703987787944103">בהת×× ×œ×ª×¤×™×©×”</translation>
<translation id="6968269510885595029">צריך להשתמש במפתח ×”×בטחה</translation>
-<translation id="6970216967273061347">מחוז</translation>
<translation id="6971439137020188025">â€×™×¦×™×¨×” מהירה של מצגת חדשה ב-Google Slides</translation>
<translation id="6972629891077993081">â€×”תקני HID</translation>
<translation id="6973656660372572881">â€×¦×•×™× ×• שרתי Proxy ×§×‘×•×¢×™× ×•×›×ª×•×‘×ª ×תר של הסקריפט מסוג ‎.Pac</translation>
@@ -2198,7 +2209,6 @@ Del</translation>
<translation id="7081308185095828845">התכונה הזו ×œ× ×–×ž×™× ×” במכשיר שלך</translation>
<translation id="7083258188081898530">מגש 9</translation>
<translation id="7086090958708083563">ההעל××” התבקשה על ידי המשתמש</translation>
-<translation id="7087282848513945231">מחוז</translation>
<translation id="7095139009144195559">â€<ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, יש להקיש על Tab ו××– על Enter כדי לנהל ×ת ההרש×ות ×•×”× ×ª×•× ×™× ×”×©×ž×•×¨×™× ×‘×›×œ ×”××ª×¨×™× ×‘×”×’×“×¨×•×ª Chrome</translation>
<translation id="7096937462164235847">הזהות של ×”×תר ×”×–×” ×œ× ×ומתה.</translation>
<translation id="7101893872976785596">סרטי ×ימה</translation>
@@ -2217,10 +2227,10 @@ Del</translation>
<ph name="LIST_ITEM" />מידע שהוזן בטפסי×<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">×œ× × ×™×ª×Ÿ לבצע משלוח לכתובת הזו. עליך לבחור כתובת ×חרת.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">מנהל המערכת שלך ×סר על העתקת ×”× ×ª×•× ×™× ×”×לה.</translation>
<translation id="7135130955892390533">הצגת סטטוס</translation>
<translation id="7138472120740807366">שיטת מסירה</translation>
-<translation id="7139724024395191329">×”×מירויות הערביות</translation>
<translation id="7139892792842608322">המגש הר×שי</translation>
<translation id="714064300541049402">â€×”×–×–×” של תמונה בצד 2 על ציר X</translation>
<translation id="7152423860607593928">Number-14 (Envelope)‎</translation>
@@ -2249,7 +2259,7 @@ Del</translation>
<translation id="7219179957768738017">החיבור משתמש ב-<ph name="SSL_VERSION" />.</translation>
<translation id="7220786058474068424">המערכת מעבדת ×ת התשלו×</translation>
<translation id="7221855153210829124">להציג הודעות</translation>
-<translation id="722454870747268814">כרטיסייה חדשה לגלישה בסתר</translation>
+<translation id="722454870747268814">כרטיסייה פרטית חדשה</translation>
<translation id="7233592378249864828">הדפסה של גיליון ×ישור</translation>
<translation id="7234638337680728591">מחירי דלק ותדלוק כלי רכב</translation>
<translation id="7237492777898608035">×œ× ×œ×”×¦×™×’ ×ת ההודעה הזו שוב ל×תר ×”×–×”</translation>
@@ -2437,6 +2447,7 @@ Del</translation>
<translation id="7669271284792375604">×ª×•×§×¤×™× ×‘×תר ×”×–×” ×¢×©×•×™×™× ×œ×’×¨×•× ×œ×š, בדרכי מרמה, להתקין תוכנות שיפגעו בחוויית הגלישה שלך (לדוגמה, על ידי שינוי דף הבית ×ו הצגת מודעות נוספות ב×תרי×).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{הפעולות שננקטו ×¢× × ×ª×•× ×™× ×©×¡×•×ž× ×• ×›×¡×•×“×™×™× (פעולה ×חת דווחה מ××– ההתחברות). <ph name="BEGIN_LINK" />מידע נוסף<ph name="END_LINK" />}two{הפעולות שננקטו ×¢× × ×ª×•× ×™× ×©×¡×•×ž× ×• ×›×¡×•×“×™×™× (# פעולות דווחו מ××– ההתחברות). <ph name="BEGIN_LINK" />מידע נוסף<ph name="END_LINK" />}many{הפעולות שננקטו ×¢× × ×ª×•× ×™× ×©×¡×•×ž× ×• ×›×¡×•×“×™×™× (# פעולות דווחו מ××– ההתחברות). <ph name="BEGIN_LINK" />מידע נוסף<ph name="END_LINK" />}other{הפעולות שננקטו ×¢× × ×ª×•× ×™× ×©×¡×•×ž× ×• ×›×¡×•×“×™×™× (# פעולות דווחו מ××– ההתחברות). <ph name="BEGIN_LINK" />מידע נוסף<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">תיבת דו×ר 6</translation>
+<translation id="7675325315208090829">לניהול ×מצעי התשלו×…</translation>
<translation id="7676643023259824263">חיפוש טקסט בלוח, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">â€×”צגה וניהול של היסטוריית הגלישה בהגדרות Chrome</translation>
<translation id="7679947978757153706">בייסבול</translation>
@@ -2479,7 +2490,6 @@ Del</translation>
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">ב×ותו סדר ×¢× ×”×¤× ×™× ×›×œ×¤×™ מעלה</translation>
-<translation id="777702478322588152">×ª×—×•× ×©×™×¤×•×˜</translation>
<translation id="7791011319128895129">לפני השקה</translation>
<translation id="7791196057686275387">×ריזה</translation>
<translation id="7791543448312431591">הוספה</translation>
@@ -2570,12 +2580,12 @@ Del</translation>
<translation id="8055534648776115597">חינוך מקצועי ולימודי המשך</translation>
<translation id="8057711352706143257">יש בעיה בהגדרה של "<ph name="SOFTWARE_NAME" />". בדרך כלל, הסרת ההתקנה של "<ph name="SOFTWARE_NAME" />" פותרת ×ת הבעיה. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ייצור מזון</translation>
-<translation id="8066955247577885446">מצטערי×, משהו השתבש.</translation>
<translation id="8067872629359326442">â€×”זנת כרגע ×ת הסיסמה שלך ב×תר מטעה. ×פשר לפתור ×ת הבעיה בעזרת Chromium. כדי לשנות ×ת הסיסמה ולהודיע ל-Google שייתכן ×›×™ החשבון בסיכון, יש ללחוץ על '×”×’× ×” על החשבון'.</translation>
<translation id="8070439594494267500">סמל ×”×פליקציה</translation>
<translation id="8074253406171541171">10x13 (Envelope)‎</translation>
<translation id="8075736640322370409">â€×™×¦×™×¨×” מהירה של גיליון ×לקטרוני חדש ב-Google Sheets</translation>
<translation id="8075898834294118863">ניהול ההגדרות ל×תרי×</translation>
+<translation id="8076492880354921740">כרטיסיות</translation>
<translation id="8078141288243656252">×œ× × ×™×ª×Ÿ להוסיף הערה במצב מסובב</translation>
<translation id="8079031581361219619">לטעון ×ת ×”×תר מחדש?</translation>
<translation id="8081087320434522107">מכוניות משפחתיות</translation>
@@ -2698,6 +2708,7 @@ Del</translation>
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">הגדרות</translation>
+<translation id="8428634594422941299">הבנתי</translation>
<translation id="8431194080598727332">â€<ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, יש להקיש על Tab ו××– על Enter כדי לנהל ×ת ההעדפות לגבי קובצי Cookie בהגדרות Chrome</translation>
<translation id="8433057134996913067">פעולה זו ×ª×•×¦×™× ×ותך מהחשבון ברוב ×”×תרי×.</translation>
<translation id="8434840396568290395">חיות מחמד</translation>
@@ -2795,6 +2806,7 @@ Del</translation>
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> ×”×•× ×”×§×•×“ שלך בשביל <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">הוספת הכרטיסייה לסימניות</translation>
<translation id="8751426954251315517">×פשר לנסות שוב מ×וחר יותר.</translation>
+<translation id="8757526089434340176">â€×™×© הטבה של Google Pay</translation>
<translation id="8758885506338294482">משחקי ויד×ו תחרותיי×</translation>
<translation id="8759274551635299824">פג תוקפו של הכרטיס</translation>
<translation id="87601671197631245">ב×תר ×”×–×” נעשה שימוש בתצורת ×”×’× ×” מיושנת, כך שמידע נשלח ×ליו עשוי להיחשף (למשל סיסמ×ות, הודעות ×ו כרטיסי ×שר××™).</translation>
@@ -2802,6 +2814,7 @@ Del</translation>
<translation id="8763927697961133303">â€×”תקן USB</translation>
<translation id="8763986294015493060">סגירת כל החלונות ×”×¤×¨×˜×™×™× ×©×¤×ª×•×—×™× ×›×¨×’×¢</translation>
<translation id="8766943070169463815">גיליון ×”×ימות של פרטי הכניסה ×œ×ª×©×œ×•× ×”×ž×ובטח פתוח</translation>
+<translation id="8767765348545497220">סגירה של בועת העזרה</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">×ופנועי×</translation>
<translation id="8790007591277257123">&amp;ביצוע מחדש של מחיקה</translation>
@@ -2814,6 +2827,7 @@ Del</translation>
<translation id="8806285662264631610">מוצרי רחצה וטיפוח הגוף</translation>
<translation id="8807160976559152894">חיתוך ×חרי כל עמוד</translation>
<translation id="8808828119384186784">â€×”הגדרות של Chrome</translation>
+<translation id="8813277370772331957">×× ×™ רוצה לקבל תזכורת ×חר כך</translation>
<translation id="8816395686387277279">â€<ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, יש להקיש על Tab ו××– על Enter כדי לעדכן ×ת דפדפן Chrome בהגדרות Chrome</translation>
<translation id="8820817407110198400">סימניות</translation>
<translation id="882338992931677877">חריץ ידני</translation>
@@ -2993,6 +3007,7 @@ Del</translation>
<translation id="988159990683914416">גרסת מפתחי×</translation>
<translation id="989988560359834682">עריכת כתובת</translation>
<translation id="991413375315957741">חיישני תנועה ×ו ×ור</translation>
+<translation id="992110854164447044">כרטיס וירטו×לי מסתיר ×ת הכרטיס בפועל כדי להגן עליך מפני תרמיות פוטנצי×ליות. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ורוד</translation>
<translation id="992432478773561401">â€×”ייתה בעיה בהתקנה של "<ph name="SOFTWARE_NAME" />" במחשב שלך ×ו ברשת. ×פשר לנסות ×חת מהפעולות הב×ות:
diff --git a/chromium/components/strings/components_strings_ja.xtb b/chromium/components/strings/components_strings_ja.xtb
index da78b163971..cbeced4d8d7 100644
--- a/chromium/components/strings/components_strings_ja.xtb
+++ b/chromium/components/strings/components_strings_ja.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">奨学金ã€è£œåŠ©é‡‘</translation>
<translation id="1048785276086539861">メモを編集ã™ã‚‹ã¨ã€ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã¯å˜ä¸€ãƒšãƒ¼ã‚¸è¡¨ç¤ºã«æˆ»ã‚Šã¾ã™</translation>
<translation id="1050038467049342496">ä»–ã®ã‚¢ãƒ—リを終了ã™ã‚‹</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> を使用ã™ã‚‹ã‚¦ã‚§ãƒ–サイトã§èªè¨¼ã‚·ã‚¹ãƒ†ãƒ  デãƒã‚¤ã‚¹ã«ã‚ˆã‚‹ç¢ºèªã‚’è¡Œã†ã‚ˆã†é¸æŠžã—ã¦ã„ã¾ã™ã€‚ã“ã®ãƒ—ロãƒã‚¤ãƒ€ã«ãŠæ”¯æ‰•ã„方法ã«é–¢ã™ã‚‹æƒ…å ±ãŒä¿å­˜ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ãã®å ´åˆã€<ph name="LINK_TEXT" /> ã‚’è¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚</translation>
<translation id="1055184225775184556">追加ã®å–り消ã—(&amp;U)</translation>
<translation id="1056663316309890343">写真編集ソフトウェア</translation>
<translation id="1056898198331236512">警告</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">å—ã‘å–り方法</translation>
<translation id="1281476433249504884">スタッカー 1</translation>
<translation id="1285320974508926690">ã“ã®ã‚µã‚¤ãƒˆã¯ç¿»è¨³ã—ãªã„</translation>
+<translation id="1288548991597756084">カードを安全ã«ä¿å­˜</translation>
<translation id="1292571435393770077">トレイ 16</translation>
<translation id="1292701964462482250">「パソコンã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„るソフトウェアãŒåŽŸå› ã§ã€Chrome ã‹ã‚‰ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆã«å®‰å…¨ã«æŽ¥ç¶šã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã€ï¼ˆWindows パソコンã®ã¿ï¼‰</translation>
<translation id="1294154142200295408">コマンドラインã®ãƒãƒªã‚¨ãƒ¼ã‚·ãƒ§ãƒ³</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;エラーを解決ã™ã‚‹ã«ã¯ã€é–‹ã“ã†ã¨ã—ã¦ã„るページ㧠[&lt;strong&gt;接続&lt;/strong&gt;] をクリックã—ã¾ã™ã€‚&lt;/p&gt;</translation>
<translation id="1507780850870535225">造園設計</translation>
<translation id="1513706915089223971">履歴項目ã®ãƒªã‚¹ãƒˆ</translation>
+<translation id="1516097932025103760">カード情報ã¯æš—å·åŒ–ã•ã‚Œã¦å®‰å…¨ã«ä¿å­˜ã•ã‚Œã¾ã™ã€‚ãŸã ã—ã€CVC ã¯ä¿å­˜ã•ã‚Œã¾ã›ã‚“。</translation>
<translation id="1517433312004943670">電話番å·ãŒå¿…è¦ã§ã™</translation>
<translation id="1519264250979466059">ビルド日</translation>
<translation id="1521159554480556801">手芸</translation>
@@ -272,7 +275,7 @@
<translation id="1634828734222219955">åˆè¨ˆ</translation>
<translation id="163669211644121865">確定申告ã€ç¨Žå‹™</translation>
<translation id="1638780421120290329">カードをä¿å­˜ã§ãã¾ã›ã‚“</translation>
-<translation id="1639239467298939599">読ã¿è¾¼ã¿ä¸­</translation>
+<translation id="1639239467298939599">読ã¿è¾¼ã‚“ã§ã„ã¾ã™</translation>
<translation id="1640180200866533862">ユーザー ãƒãƒªã‚·ãƒ¼</translation>
<translation id="1640244768702815859"><ph name="BEGIN_LINK" />サイトã®ãƒ›ãƒ¼ãƒ ãƒšãƒ¼ã‚¸ã«ã‚¢ã‚¯ã‚»ã‚¹<ph name="END_LINK" />ã—ã¦ã¿ã¦ãã ã•ã„。</translation>
<translation id="1641976391427233992">出力ã®ã‚¿ã‚¤ãƒŸãƒ³ã‚°</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ãŠæ”¯æ‰•ã„方法ã®ä¿å­˜ã¨å…¥åŠ›</translation>
<translation id="1663943134801823270">Chrome ã«ä¿å­˜ã•ã‚Œã¦ã„るクレジット カードã¨ä½æ‰€ã§ã™ã€‚[<ph name="BEGIN_LINK" />設定<ph name="END_LINK" />] ã§ç®¡ç†ã§ãã¾ã™ã€‚</translation>
<translation id="1671391448414634642">今後ã€<ph name="SOURCE_LANGUAGE" />ã®ãƒšãƒ¼ã‚¸ã¯<ph name="TARGET_LANGUAGE" />ã«ç¿»è¨³ã•ã‚Œã¾ã™ã€‚</translation>
+<translation id="1673886523110456987">特典を使用ã™ã‚‹ã«ã¯ã€<ph name="CARD_DETAIL" /> ã§æ±ºæ¸ˆã‚’è¡Œã£ã¦ãã ã•ã„</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" />ã‹ã‚‰<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">短辺ã‹ã‚‰</translation>
<translation id="168693727862418163">ã“ã®ãƒãƒªã‚·ãƒ¼ã®å€¤ã¯ã€ã‚¹ã‚­ãƒ¼ãƒžã®æ¤œè¨¼ã§ã‚¨ãƒ©ãƒ¼ã¨ãªã£ãŸãŸã‚無視ã•ã‚Œã¾ã™ã€‚</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">モーション センサー</translation>
<translation id="1717494416764505390">用紙å—ã‘ 3</translation>
<translation id="1718029547804390981">ドキュメントã®ã‚µã‚¤ã‚ºãŒå¤§ãã™ãŽã‚‹ãŸã‚注釈を追加ã§ãã¾ã›ã‚“</translation>
+<translation id="1720941539803966190">ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚’é–‰ã˜ã‚‹</translation>
<translation id="1721424275792716183">* 必須欄ã§ã™</translation>
<translation id="1727613060316725209">証明書ã¯æœ‰åŠ¹ã§ã™</translation>
<translation id="1727741090716970331">有効ãªã‚«ãƒ¼ãƒ‰ç•ªå·ã®è¿½åŠ </translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">ãƒãƒ¼ãƒ™ã‚­ãƒ¥ãƒ¼ã€ã‚°ãƒªãƒ«</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" />ã®ãƒšãƒ¼ã‚¸ã¯ç¿»è¨³ã•ã‚Œã¾ã›ã‚“。</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ã“ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã‚’オンã«ã—ã¦ã€ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒæœ‰åŠ¹ãªå ´åˆã€Chrome ã¯æœ€è¿‘ã®é–²è¦§ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティãŒæœ€ã‚‚é¡žä¼¼ã—ã¦ã„る「コホートã€ã¨ã„ã†å¤šäººæ•°ã®ã‚°ãƒ«ãƒ¼ãƒ—を特定ã—ã¾ã™ã€‚広告主ã¯ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«å¯¾ã—ã¦åºƒå‘Šã‚’表示ã™ã‚‹ã‚ˆã†ã«è¨­å®šã§ãる一方ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®é–²è¦§ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティã¯ãƒ‡ãƒã‚¤ã‚¹ã«ä¿å­˜ã•ã‚Œã¦ãƒ—ライãƒã‚·ãƒ¼ãŒä¿ãŸã‚Œã¾ã™ã€‚ãªãŠã€ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã¯æ¯Žæ—¥æ›´æ–°ã•ã‚Œã¾ã™ã€‚}=1{ã“ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã‚’オンã«ã—ã¦ã€ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒæœ‰åŠ¹ãªå ´åˆã€Chrome ã¯æœ€è¿‘ã®é–²è¦§ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティãŒæœ€ã‚‚é¡žä¼¼ã—ã¦ã„る「コホートã€ã¨ã„ã†å¤šäººæ•°ã®ã‚°ãƒ«ãƒ¼ãƒ—を特定ã—ã¾ã™ã€‚広告主ã¯ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«å¯¾ã—ã¦åºƒå‘Šã‚’表示ã™ã‚‹ã‚ˆã†ã«è¨­å®šã§ãる一方ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®é–²è¦§ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティã¯ãƒ‡ãƒã‚¤ã‚¹ã«ä¿å­˜ã•ã‚Œã¦ãƒ—ライãƒã‚·ãƒ¼ãŒä¿ãŸã‚Œã¾ã™ã€‚ãªãŠã€ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã¯æ¯Žæ—¥æ›´æ–°ã•ã‚Œã¾ã™ã€‚}other{ã“ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã‚’オンã«ã—ã¦ã€ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒæœ‰åŠ¹ãªå ´åˆã€Chrome ã¯æœ€è¿‘ã®é–²è¦§ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティãŒæœ€ã‚‚é¡žä¼¼ã—ã¦ã„る「コホートã€ã¨ã„ã†å¤šäººæ•°ã®ã‚°ãƒ«ãƒ¼ãƒ—を特定ã—ã¾ã™ã€‚広告主ã¯ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«å¯¾ã—ã¦åºƒå‘Šã‚’表示ã™ã‚‹ã‚ˆã†ã«è¨­å®šã§ãる一方ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®é–²è¦§ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティã¯ãƒ‡ãƒã‚¤ã‚¹ã«ä¿å­˜ã•ã‚Œã¦ãƒ—ライãƒã‚·ãƒ¼ãŒä¿ãŸã‚Œã¾ã™ã€‚ãªãŠã€ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—㯠{NUM_DAYS} æ—¥ã”ã¨ã«æ›´æ–°ã•ã‚Œã¾ã™ã€‚}}</translation>
-<translation id="2053553514270667976">郵便番å·</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 件ã®å€™è£œ}other{# 件ã®å€™è£œ}}</translation>
+<translation id="2066915425250589881">削除ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆ</translation>
<translation id="2068528718802935086">乳幼å…</translation>
<translation id="2071156619270205202">ã“ã®ã‚«ãƒ¼ãƒ‰ã§ã¯ä»®æƒ³ã‚«ãƒ¼ãƒ‰ç•ªå·ã¯ã”利用ã„ãŸã ã‘ã¾ã›ã‚“</translation>
<translation id="2071692954027939183">普段ã¯é€šçŸ¥ã‚’許å¯ã•ã‚Œãªã„よã†ã§ã™ã®ã§ã€é€šçŸ¥ã‚’自動的ã«ãƒ–ロックã—ã¾ã—ãŸ</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">åŒæœŸã‚’管ç†ã™ã‚‹ãƒœã‚¿ãƒ³ã§ã™ã€‚Enter キーを押ã—ã¦ã€Chrome ã®è¨­å®šã§åŒæœŸã™ã‚‹æƒ…報を管ç†ã—ã¾ã™</translation>
<translation id="2091887806945687916">音声</translation>
<translation id="2094505752054353250">ドメインãŒä¸€è‡´ã—ã¾ã›ã‚“</translation>
-<translation id="2096368010154057602">県</translation>
<translation id="2099652385553570808">3 ã‹æ‰€ã®ã‚¹ãƒ†ãƒ¼ãƒ—ル(左)</translation>
<translation id="2101225219012730419">ãƒãƒ¼ã‚¸ãƒ§ãƒ³:</translation>
<translation id="2102134110707549001">安全ãªãƒ‘スワードを自動生æˆâ€¦</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">アメリカン フットボール</translation>
<translation id="2187317261103489799">検出(デフォルト)</translation>
<translation id="2188375229972301266">多穴パンãƒï¼ˆä¸‹ï¼‰</translation>
-<translation id="2188852899391513400">ãŸã£ãŸä»Šä½¿ç”¨ã—ãŸãƒ‘スワードãŒãƒ‡ãƒ¼ã‚¿ä¾µå®³ã§æ¤œå‡ºã•ã‚Œã¾ã—ãŸã€‚Google パスワード マãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã§ã¯ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ä¿è­·ã™ã‚‹ãŸã‚ã«ä»Šã™ãパスワードを変更ã—ã¦ã€ä¿å­˜ã—ãŸãƒ‘スワードを確èªã™ã‚‹ã“ã¨ã‚’ãŠã™ã™ã‚ã—ã¾ã™ã€‚</translation>
<translation id="219906046732893612">リフォーム</translation>
<translation id="2202020181578195191">有効期é™ï¼ˆå¹´ï¼‰ã‚’æ­£ã—ã„å½¢å¼ã§å…¥åŠ›ã—ã¦ãã ã•ã„</translation>
<translation id="22081806969704220">トレイ 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">ファイルã®ç·¨é›†</translation>
<translation id="2215963164070968490">犬</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ã§ã¯ç¾åœ¨ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦å±é™ºãªã‚¢ãƒ—リ(デãƒã‚¤ã‚¹ã«å•é¡Œã‚’生ã˜ã•ã›ãŸã‚Šã€ãƒ¢ãƒã‚¤ãƒ«åˆ©ç”¨æ–™ã«ä¸æ˜Žçž­ãªè«‹æ±‚を加ãˆãŸã‚Šã€å€‹äººæƒ…報を抜ãå–ã£ãŸã‚Šã™ã‚‹ã‚¢ãƒ—リ)ãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚’ã‚‚ã†ä¸€åº¦é–‹å§‹</translation>
+<translation id="2219735899272417925">デãƒã‚¤ã‚¹ã®ãƒªã‚»ãƒƒãƒˆãŒå¿…è¦</translation>
<translation id="2224337661447660594">インターãƒãƒƒãƒˆã«æŽ¥ç¶šã•ã‚Œã¦ã„ã¾ã›ã‚“</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />診断アプリ<ph name="END_LINK" />を使用ã—ã¦æŽ¥ç¶šã‚’修正ã—ã¦ãã ã•ã„</translation>
<translation id="2239100178324503013">é€ä¿¡</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">ブロック(デフォルト)</translation>
<translation id="2512413427717747692">Chrome を既定ã®ãƒ–ラウザã«è¨­å®šã™ã‚‹ãƒœã‚¿ãƒ³ã§ã™ã€‚Enter キーを押ã™ã¨ iOS 設定㧠Chrome をシステムã®æ—¢å®šã®ãƒ–ラウザã«è¨­å®šã§ãã¾ã™</translation>
<translation id="2515629240566999685">電波状æ³ã‚’確èªã™ã‚‹</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> を使用ã™ã‚‹ã‚¦ã‚§ãƒ–サイト㧠Touch ID ã«ã‚ˆã‚‹ç¢ºèªã‚’è¡Œã†ã‚ˆã†é¸æŠžã—ã¦ã„ã¾ã™ã€‚ã“ã®ãƒ—ロãƒã‚¤ãƒ€ã«ãŠæ”¯æ‰•ã„方法ã«é–¢ã™ã‚‹æƒ…å ±ãŒä¿å­˜ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ãã®å ´åˆã€<ph name="LINK_TEXT" /> ã‚’è¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚</translation>
<translation id="2521385132275182522">ステープル(å³ä¸‹ï¼‰</translation>
<translation id="2521736961081452453">フォームを作æˆ</translation>
<translation id="2523886232349826891">カードã¯ã“ã®ãƒ‡ãƒã‚¤ã‚¹ã®ã¿ã«ä¿å­˜ã•ã‚Œã¾ã™</translation>
<translation id="2524461107774643265">ãã®ä»–ã®æƒ…å ±ã®è¿½åŠ </translation>
<translation id="2529899080962247600">ã“ã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã§æŒ‡å®šã§ãるエントリ㯠<ph name="MAX_ITEMS_LIMIT" /> 件ã¾ã§ã§ã™ã€‚ãれ以é™ã®ã‚¨ãƒ³ãƒˆãƒªã¯ã™ã¹ã¦ç„¡è¦–ã•ã‚Œã¾ã™ã€‚</translation>
+<translation id="253493526287553278">プロモーション コードã®è©³ç´°ã‚’確èª</translation>
<translation id="2535585790302968248">æ–°ã—ã„シークレット タブを開ã„ã¦ã‚·ãƒ¼ã‚¯ãƒ¬ãƒƒãƒˆ モードã§ãƒ–ラウジングã—ã¾ã™</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{ã€ä»– 1 件}other{ã€ä»– # 件}}</translation>
<translation id="2536110899380797252">ä½æ‰€ã‚’追加</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">芸術写真ã€ãƒ‡ã‚¸ã‚¿ãƒ«ã‚¢ãƒ¼ãƒˆ</translation>
<translation id="2601150049980261779">æ‹æ„›æ˜ ç”»</translation>
<translation id="2604589665489080024">ãƒãƒƒãƒ— ミュージック</translation>
-<translation id="2609632851001447353">ãƒãƒªã‚¨ãƒ¼ã‚·ãƒ§ãƒ³</translation>
<translation id="2610561535971892504">クリックã—ã¦ã‚³ãƒ”ー</translation>
<translation id="2617988307566202237">Chrome ã«ã¯ã€æ¬¡ã®æƒ…å ±ã¯<ph name="BEGIN_EMPHASIS" />ä¿å­˜ã•ã‚Œã¾ã›ã‚“<ph name="END_EMPHASIS" />。
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">誕生日</translation>
<translation id="2677748264148917807">ã“ã®ãƒšãƒ¼ã‚¸ã‚’離れる</translation>
+<translation id="2679714844901977852">Google アカウント(<ph name="USER_EMAIL" />)ã«ã‚«ãƒ¼ãƒ‰ã¨ãŠæ”¯æ‰•ã„情報をä¿å­˜ã™ã‚‹ã¨ã€ã™ã°ã‚„ã安全ã«ã”購入手続ãã‚’è¡Œãˆã¾ã™</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">最é©ã‚µã‚¤ã‚º</translation>
<translation id="2688969097326701645">ã¯ã„ã€ç¶šè¡Œã—ã¾ã™</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">インターãƒãƒƒãƒˆ サービス プロãƒã‚¤ãƒ€ï¼ˆISP)</translation>
<translation id="2856444702002559011"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ã§ã¯ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦ã€ãƒ‘スワードã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カードãªã©ã®æƒ…å ±ãŒç›—ã¾ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ã“ã®ã‚µã‚¤ãƒˆã§ã¯ç…©ã‚ã—ã„広告ã¾ãŸã¯èª¤è§£ã‚’æ‹›ã広告ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚</translation>
-<translation id="286512204874376891">仮想カードã¯ã€å®Ÿéš›ã®ã‚«ãƒ¼ãƒ‰ã®ä»£ã‚ã‚Šã¨ã—ã¦æ©Ÿèƒ½ã—ã€ä¸æ­£è¡Œç‚ºã‹ã‚‰ä¿è­·ã—ã¾ã™ã€‚<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">フレンドリー</translation>
<translation id="28761159517501904">映画</translation>
<translation id="2876489322757410363">外部アプリケーションを経由ã—ãŸãŠæ”¯æ‰•ã„ã®å‡¦ç†ã«é€²ã‚€ãŸã‚ã€ã‚·ãƒ¼ã‚¯ãƒ¬ãƒƒãƒˆ モードを解除ã—ã¾ã™ã€‚続行ã—ã¾ã™ã‹ï¼Ÿ</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">ディスク</translation>
<translation id="3162559335345991374">ã”利用㮠Wi-Fi ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ã¯ã€ãƒ­ã‚°ã‚¤ãƒ³ãƒšãƒ¼ã‚¸ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ãŒå¿…è¦ãªå¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">島</translation>
<translation id="3176929007561373547">プロキシã®è¨­å®šã‚’確èªã™ã‚‹ã‹ã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ã€ãƒ—ロキシ サーãƒãƒ¼ãŒæ­£å¸¸ã«
動作ã—ã¦ã„ã‚‹ã‹ã©ã†ã‹ã‚’確èªã—ã¦ãã ã•ã„。プロキシ サーãƒãƒ¼ã‚’使用ã—ã¦ã„ãªã„å ´åˆã¯
次ã®æ–¹æ³•ã‚’ãŠè©¦ã—ãã ã•ã„。
@@ -877,9 +882,6 @@
<translation id="3369192424181595722">時計ã®ã‚¨ãƒ©ãƒ¼</translation>
<translation id="3369459162151165748">自動車部å“ã€ã‚¢ã‚¯ã‚»ã‚µãƒª</translation>
<translation id="3371064404604898522">Chrome を既定ã®ãƒ–ラウザã«è¨­å®šã™ã‚‹</translation>
-<translation id="3371076217486966826"><ph name="URL" /> ãŒæ¬¡ã®è¨±å¯ã‚’求ã‚ã¦ã„ã¾ã™ã€‚
- • 周囲㮠3D マップã®ä½œæˆã¨ã‚«ãƒ¡ãƒ©ä½ç½®ã®è¿½è·¡
- • カメラã®ä½¿ç”¨</translation>
<translation id="337363190475750230">プロビジョニングãŒè§£é™¤ã•ã‚Œã¾ã—ãŸ</translation>
<translation id="3375754925484257129">Chrome ã®å®‰å…¨ç¢ºèªã‚’実行</translation>
<translation id="3377144306166885718">ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¯ TLS ã®å»ƒæ­¢ã•ã‚ŒãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’使用ã—ã¦ã„ã¾ã™ã€‚</translation>
@@ -896,6 +898,7 @@
<translation id="3399952811970034796">é…é”å…ˆä½æ‰€</translation>
<translation id="3402261774528610252">ã“ã®ã‚µã‚¤ãƒˆã®èª­ã¿è¾¼ã¿ã§ã€TLS 1.0 ã¾ãŸã¯ TLS 1.1 ã®æŽ¥ç¶šãŒä½¿ç”¨ã•ã‚Œã¾ã—ãŸã€‚ã“れら㮠TLS ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚µãƒãƒ¼ãƒˆã¯çµ‚了ã—ã€ä»Šå¾Œç„¡åŠ¹ã¨ãªã‚‹äºˆå®šã§ã™ã€‚無効ã«ãªã‚‹ã¨ã€ã“ã®ã‚µã‚¤ãƒˆã¯èª­ã¿è¾¼ã‚ãªããªã‚Šã¾ã™ã€‚サーãƒãƒ¼ã§ TLS 1.2 以é™ã‚’有効ã«ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="3405664148539009465">フォントをカスタマイズ</translation>
+<translation id="3407789382767355356">サードパーティã®ãƒ­ã‚°ã‚¤ãƒ³</translation>
<translation id="3409896703495473338">セキュリティ設定を管ç†</translation>
<translation id="3414952576877147120">サイズ:</translation>
<translation id="3417660076059365994">アップロードã¾ãŸã¯æ·»ä»˜ã—ãŸãƒ•ã‚¡ã‚¤ãƒ«ã¯åˆ†æžã®ãŸã‚ Google Cloud ã¾ãŸã¯ç¬¬ä¸‰è€…ã«é€ä¿¡ã•ã‚Œã¾ã™ã€‚ãŸã¨ãˆã°ã€æ©Ÿå¯†ãƒ‡ãƒ¼ã‚¿ã‚„マルウェアãŒãªã„ã‹ã‚¹ã‚­ãƒ£ãƒ³ã•ã‚Œã¾ã™ã€‚</translation>
@@ -928,6 +931,7 @@
<translation id="3477679029130949506">映画情報ã€åŠ‡å ´æƒ…å ±</translation>
<translation id="3479552764303398839">後ã§</translation>
<translation id="3484560055331845446">Google アカウントã«ã‚¢ã‚¯ã‚»ã‚¹ã§ããªããªã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚Chrome ã§ä»Šã™ãパスワードを変更ã™ã‚‹ã“ã¨ã‚’ãŠã™ã™ã‚ã—ã¾ã™ã€‚変更ã®éš›ã«ã¯ã€ãƒ­ã‚°ã‚¤ãƒ³ã™ã‚‹ã‚ˆã†æ±‚ã‚られã¾ã™ã€‚</translation>
+<translation id="3484861421501147767">リマインダー: ä¿å­˜æ¸ˆã¿ã®ãƒ—ロモーション コードを利用ã§ãã¾ã™</translation>
<translation id="3487845404393360112">トレイ 4</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" />
ページ内を検索</translation>
@@ -1052,6 +1056,7 @@
<translation id="3810973564298564668">管ç†</translation>
<translation id="3816482573645936981">値(優先)</translation>
<translation id="382518646247711829">プロキシ サーãƒãƒ¼ã‚’使用ã—ã¦ã„ã‚‹å ´åˆ...</translation>
+<translation id="3826050100957962900">サードパーティã®ãƒ­ã‚°ã‚¤ãƒ³</translation>
<translation id="3827112369919217609">絶対的</translation>
<translation id="3827666161959873541">ファミリー映画</translation>
<translation id="3828924085048779000">パスフレーズã¯å¿…ãšæŒ‡å®šã—ã¦ãã ã•ã„。</translation>
@@ -1064,9 +1069,9 @@
<translation id="3858027520442213535">日時を更新</translation>
<translation id="3858860766373142691">åå‰</translation>
<translation id="3872834068356954457">科学</translation>
+<translation id="3875783148670536197">手順を見る</translation>
<translation id="3881478300875776315">表示ã™ã‚‹è¡Œæ•°ã‚’減らã™</translation>
<translation id="3884278016824448484">競åˆã™ã‚‹ãƒ‡ãƒã‚¤ã‚¹è­˜åˆ¥å­ã§ã™</translation>
-<translation id="3885155851504623709">教区</translation>
<translation id="388632593194507180">監視ãŒæ¤œå‡ºã•ã‚Œã¾ã—ãŸ</translation>
<translation id="3886948180919384617">スタッカー 3</translation>
<translation id="3890664840433101773">メールを追加</translation>
@@ -1096,9 +1101,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> ã¯ãƒ–ロックã•ã‚Œã¦ã„ã¾ã™</translation>
<translation id="3978338123949022456">検索モードã§ã™ã€‚クエリを入力ã—㦠Enter キーを押ã™ã¨ã€<ph name="KEYWORD_SUFFIX" /> ã§æ¤œç´¢ã§ãã¾ã™</translation>
<translation id="398470910934384994">é³¥</translation>
+<translation id="3985750352229496475">ä½æ‰€ã‚’管ç†...</translation>
<translation id="3986705137476756801">今回ã®ã¿è‡ªå‹•å­—幕起ã“ã—をオフã«ã™ã‚‹</translation>
<translation id="3987940399970879459">1 MB 未満</translation>
<translation id="3990250421422698716">ジョグ オフセット</translation>
+<translation id="3992684624889376114">ã“ã®ãƒšãƒ¼ã‚¸ã«ã¤ã„ã¦</translation>
<translation id="3996311196211510766">サイト <ph name="ORIGIN" /> ã§ã¯ã€ã‚µã‚¤ãƒˆã¸ã®ã™ã¹ã¦ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ã‚ªãƒªã‚¸ãƒ³ ãƒãƒªã‚·ãƒ¼ã®é©ç”¨ã‚’求ã‚ã¦ã„ã¾ã™ãŒã€ã“ã®ãƒãƒªã‚·ãƒ¼ã¯ç¾åœ¨é©ç”¨ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。</translation>
<translation id="4006465311664329701">Google Pay を使用ã—ãŸãŠæ”¯æ‰•æ–¹æ³•ã€ã‚¯ãƒ¼ãƒãƒ³ã€ä½æ‰€</translation>
<translation id="4009243425692662128">å°åˆ·ã™ã‚‹ãƒšãƒ¼ã‚¸ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã¯åˆ†æžã®ãŸã‚ Google Cloud ã¾ãŸã¯ç¬¬ä¸‰è€…ã«é€ä¿¡ã•ã‚Œã¾ã™ã€‚ãŸã¨ãˆã°ã€æ©Ÿå¯†ãƒ‡ãƒ¼ã‚¿ãŒãªã„ã‹ã‚¹ã‚­ãƒ£ãƒ³ã•ã‚Œã¾ã™ã€‚</translation>
@@ -1218,6 +1225,7 @@
<translation id="4305666528087210886">ファイルã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“ã§ã—ãŸ</translation>
<translation id="4306529830550717874">ä½æ‰€ã‚’ä¿å­˜ã—ã¾ã™ã‹ï¼Ÿ</translation>
<translation id="4306812610847412719">クリップボード</translation>
+<translation id="4310070645992025887">ジャーニーを検索</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">ブロック(デフォルト)</translation>
<translation id="4314815835985389558">åŒæœŸã‚’管ç†</translation>
@@ -1268,6 +1276,7 @@
<translation id="4435702339979719576">ãƒã‚¹ãƒˆã‚«ãƒ¼ãƒ‰</translation>
<translation id="443673843213245140">プロキシã®ä½¿ç”¨ã¯ç„¡åŠ¹ã§ã™ãŒã€ãƒ—ロキシã®è¨­å®šãŒæ˜Žç¤ºçš„ã«æŒ‡å®šã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="4441832193888514600">ãƒãƒªã‚·ãƒ¼ã¯ã€ã‚¯ãƒ©ã‚¦ãƒ‰ ユーザー ãƒãƒªã‚·ãƒ¼ã¨ã—ã¦ã®ã¿è¨­å®šå¯èƒ½ãªãŸã‚無視ã•ã‚Œã¾ã—ãŸã€‚</translation>
+<translation id="4442470707340296952">Chrome ã®ã‚¿ãƒ–</translation>
<translation id="4450893287417543264">次回ã‹ã‚‰è¡¨ç¤ºã—ãªã„</translation>
<translation id="4451135742916150903">HID デãƒã‚¤ã‚¹ã¸ã®æŽ¥ç¶šã‚’è¦æ±‚ã§ãã‚‹</translation>
<translation id="4452328064229197696">ãŸã£ãŸä»Šä½¿ç”¨ã—ãŸãƒ‘スワードãŒãƒ‡ãƒ¼ã‚¿ä¾µå®³ã§æ¤œå‡ºã•ã‚Œã¾ã—ãŸã€‚Google パスワード マãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã§ã¯ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ä¿è­·ã™ã‚‹ãŸã‚ã«ä¿å­˜ã—ãŸãƒ‘スワードを確èªã™ã‚‹ã“ã¨ã‚’ãŠã™ã™ã‚ã—ã¾ã™ã€‚</translation>
@@ -1406,6 +1415,7 @@
<translation id="483241715238664915">警告をオンã«ã™ã‚‹</translation>
<translation id="4834250788637067901">Google Pay を使用ã—ãŸãŠæ”¯æ‰•æ–¹æ³•ã€ã‚¯ãƒ¼ãƒãƒ³ã€ä½æ‰€</translation>
<translation id="4838327282952368871">ドリーミー</translation>
+<translation id="4839087176073128681">次回ã‹ã‚‰æ”¯æ‰•ã„処ç†ã‚’ã™ã°ã‚„ãè¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚カード情報㯠Google ã®æ¥­ç•Œæœ€é«˜æ°´æº–ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ã§ä¿è­·ã•ã‚Œã¾ã™ã€‚</translation>
<translation id="4840250757394056958">Chrome 履歴を表示</translation>
<translation id="484462545196658690">自動</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> ãªã©ã®å‰²å¼•ã‚’å–å¾—</translation>
@@ -1468,6 +1478,7 @@
<translation id="5011561501798487822">検出ã•ã‚ŒãŸè¨€èªž</translation>
<translation id="5015510746216210676">マシンå:</translation>
<translation id="5017554619425969104">コピーã—ãŸãƒ†ã‚­ã‚¹ãƒˆ</translation>
+<translation id="5017828934289857214">後ã§é€šçŸ¥ã™ã‚‹</translation>
<translation id="5018422839182700155">ã“ã®ãƒšãƒ¼ã‚¸ã‚’é–‹ã‘ã¾ã›ã‚“</translation>
<translation id="5019198164206649151">代替ストアã®çŠ¶æ…‹ãŒä¸é©åˆ‡ã§ã™</translation>
<translation id="5020776957610079374">ワールド ミュージック</translation>
@@ -1487,6 +1498,7 @@
<translation id="5051305769747448211">ãŠç¬‘ã„ライブ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{ニアãƒã‚¤ã‚·ã‚§ã‚¢ã‚’使用ã—ã¦ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é€ä¿¡ã™ã‚‹ã«ã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®ç©ºã容é‡ã‚’増やã—ã¦ãã ã•ã„(<ph name="DISK_SPACE_SIZE" /> å¿…è¦ã§ã™ï¼‰}other{ニアãƒã‚¤ã‚·ã‚§ã‚¢ã‚’使用ã—ã¦ã“れらã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é€ä¿¡ã™ã‚‹ã«ã¯ã€ãƒ‡ãƒã‚¤ã‚¹ã®ç©ºã容é‡ã‚’増やã—ã¦ãã ã•ã„(<ph name="DISK_SPACE_SIZE" /> å¿…è¦ã§ã™ï¼‰}}</translation>
<translation id="5056549851600133418">ãŠã™ã™ã‚ã®è¨˜äº‹</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> を使用ã™ã‚‹ã‚¦ã‚§ãƒ–サイト㧠Windows Hello ã«ã‚ˆã‚‹ç¢ºèªã‚’è¡Œã†ã‚ˆã†é¸æŠžã—ã¦ã„ã¾ã™ã€‚ã“ã®ãƒ—ロãƒã‚¤ãƒ€ã«ãŠæ”¯æ‰•ã„方法ã«é–¢ã™ã‚‹æƒ…å ±ãŒä¿å­˜ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ãã®å ´åˆã€<ph name="LINK_TEXT" /> ã‚’è¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚</translation>
<translation id="5061227663725596739">ã‚‚ã—ã‹ã—ã¦: <ph name="LOOKALIKE_DOMAIN" /></translation>
<translation id="5066056036849835175">å°åˆ·å±¥æ­´</translation>
<translation id="5068234115460527047">ヘッジファンド</translation>
@@ -1500,10 +1512,8 @@
<translation id="5087286274860437796">サーãƒãƒ¼ã®è¨¼æ˜Žæ›¸ãŒç¾åœ¨æœ‰åŠ¹ã§ã¯ã‚ã‚Šã¾ã›ã‚“。</translation>
<translation id="5087580092889165836">カードを追加</translation>
<translation id="5088142053160410913">オペレーターã¸ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸</translation>
-<translation id="5089810972385038852">都é“府県 / å·ž</translation>
<translation id="5093232627742069661">Z 折り</translation>
<translation id="5094747076828555589">ã“ã®ã‚µãƒ¼ãƒãƒ¼ãŒ <ph name="DOMAIN" /> ã§ã‚ã‚‹ã“ã¨ã‚’確èªã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã“ã®ã‚µãƒ¼ãƒãƒ¼ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã¯ Chromium ã«ã‚ˆã£ã¦ä¿¡é ¼ã•ã‚Œã¦ã„ã‚‹ã‚‚ã®ã§ã¯ã‚ã‚Šã¾ã›ã‚“。原因ã¨ã—ã¦ã¯ã€ä¸é©åˆ‡ãªè¨­å®šã‚„ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã‚‹æŽ¥ç¶šå¦¨å®³ãŒè€ƒãˆã‚‰ã‚Œã¾ã™ã€‚</translation>
-<translation id="5095208057601539847">地方</translation>
<translation id="5097099694988056070">CPU ã‚„ RAM ã®ä½¿ç”¨çŽ‡ãªã©ã®ãƒ‡ãƒã‚¤ã‚¹ã®çµ±è¨ˆæƒ…å ±</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">サイトã¯å®‰å…¨ã§ã¯ã‚ã‚Šã¾ã›ã‚“</translation>
@@ -1541,6 +1551,7 @@
<translation id="5171045022955879922">検索ã¾ãŸã¯ URL を入力</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">マシン</translation>
+<translation id="5177076414499237632">ã“ã®ãƒšãƒ¼ã‚¸ã®ã‚½ãƒ¼ã‚¹ã¨ãƒˆãƒ”ックã«ã¤ã„ã¦</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />ã§ãªã„å ´åˆã¯ã“ã®ã‚¨ãƒ©ãƒ¼ã‚’報告ã™ã‚‹</translation>
<translation id="518639307526414276">ペットフードã€ãƒšãƒƒãƒˆã‚±ã‚¢ç”¨å“</translation>
<translation id="5190835502935405962">ブックマーク ãƒãƒ¼</translation>
@@ -1701,6 +1712,7 @@
<translation id="5624120631404540903">パスワードを管ç†</translation>
<translation id="5629630648637658800">ãƒãƒªã‚·ãƒ¼è¨­å®šã‚’読ã¿è¾¼ã‚ã¾ã›ã‚“ã§ã—ãŸ</translation>
<translation id="5631439013527180824">無効ãªãƒ‡ãƒã‚¤ã‚¹ç®¡ç†ãƒˆãƒ¼ã‚¯ãƒ³ã§ã™</translation>
+<translation id="5632485077360054581">手順を見る</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ã§ã¯ç¾åœ¨ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦ã€ãŠä½¿ã„ã®ãƒ‘ソコン上ã«å±é™ºãªãƒ—ログラム(写真ã€ãƒ‘スワードã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カードãªã©ã®æƒ…報を盗んã ã‚Šå‰Šé™¤ã—ãŸã‚Šã™ã‚‹ãƒ—ログラム)ãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">ä¸æ­£ã®å¯èƒ½æ€§ãŒã‚るコンテンツãŒãƒ–ロックã•ã‚Œã¾ã—ãŸã€‚</translation>
<translation id="5633259641094592098">カルト映画ã€ã‚¤ãƒ³ãƒ‡ã‚£ãƒ¼ã‚ºæ˜ ç”»</translation>
@@ -1818,6 +1830,7 @@
<translation id="5989320800837274978">固定プロキシ サーãƒãƒ¼ã¨ .pac スクリプト URL ã®ã©ã¡ã‚‰ã‚‚指定ã•ã‚Œã¦ã„ã¾ã›ã‚“。</translation>
<translation id="5992691462791905444">工学 Z 折り</translation>
<translation id="5995727681868049093">Google アカウントã§æƒ…å ±ã€ãƒ—ライãƒã‚·ãƒ¼ã€ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ã‚’管ç†ã—ã¾ã™</translation>
+<translation id="5997247540087773573">ãŸã£ãŸä»Šä½¿ç”¨ã—ãŸãƒ‘スワードãŒãƒ‡ãƒ¼ã‚¿ä¾µå®³ã§æ¤œå‡ºã•ã‚Œã¾ã—ãŸã€‚Google パスワード マãƒãƒ¼ã‚¸ãƒ£ãƒ¼ã§ã¯ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ä¿è­·ã™ã‚‹ãŸã‚ã«ä»Šã™ãパスワードを変更ã—ã¦ã€ä¿å­˜ã—ãŸãƒ‘スワードを確èªã™ã‚‹ã“ã¨ã‚’ãŠã™ã™ã‚ã—ã¾ã™ã€‚</translation>
<translation id="6000758707621254961">「<ph name="SEARCH_TEXT" />ã€ã«å¯¾ã™ã‚‹ <ph name="RESULT_COUNT" /> 件ã®æ¤œç´¢çµæžœ</translation>
<translation id="6006484371116297560">クラシック</translation>
<translation id="6008122969617370890">N~1 ã®é †</translation>
@@ -1913,7 +1926,6 @@
<translation id="627746635834430766">カードã¨è«‹æ±‚å…ˆä½æ‰€ã‚’ Google アカウントã«ä¿å­˜ã™ã‚‹ã¨ã€æ¬¡å›žã®ãŠæ”¯æ‰•ã„ãŒç°¡å˜ã«ãªã‚Šã¾ã™ã€‚</translation>
<translation id="6279183038361895380">カーソルを表示ã™ã‚‹ã«ã¯ |<ph name="ACCELERATOR" />| を押ã—ã¾ã™</translation>
<translation id="6280223929691119688">ã“ã®ä½æ‰€ã«ã¯é…é”ã§ãã¾ã›ã‚“。別ã®ä½æ‰€ã‚’é¸æŠžã—ã¦ãã ã•ã„。</translation>
-<translation id="6282194474023008486">郵便番å·</translation>
<translation id="6285507000506177184">[Chrome ã§ã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚’管ç†] ボタンã§ã™ã€‚Enter キーを押ã™ã¨ã€Chrome ã§ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã—ãŸãƒ•ã‚¡ã‚¤ãƒ«ã‚’管ç†ã§ãã¾ã™</translation>
<translation id="6289939620939689042">ページã®è‰²</translation>
<translation id="6290238015253830360">ãŠã™ã™ã‚ã®è¨˜äº‹ãŒã“ã“ã«è¡¨ç¤ºã•ã‚Œã¾ã™</translation>
@@ -1935,6 +1947,7 @@
<translation id="6337133576188860026">最大㧠<ph name="SIZE" /> を解放ã—ã¾ã™ã€‚サイトã«ã‚ˆã£ã¦ã¯ã€æ¬¡å›žã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹éš›ã«èª­ã¿è¾¼ã¿ã«æ™‚é–“ãŒã‹ã‹ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="6337534724793800597">ãƒãƒªã‚·ãƒ¼ã‚’åå‰ã§ãƒ•ã‚£ãƒ«ã‚¿</translation>
<translation id="6340739886198108203">管ç†è€…ã®ãƒãƒªã‚·ãƒ¼è¨­å®šã§ã¯ã€æ©Ÿå¯†ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒè¡¨ç¤ºã•ã‚Œã¦ã„ã‚‹ç”»é¢ã®æ’®å½±ã‚„録画ã¯æŽ¨å¥¨ã•ã‚Œã¦ã„ã¾ã›ã‚“。</translation>
+<translation id="6348220984832452017">ãƒãƒªã‚¨ãƒ¼ã‚·ãƒ§ãƒ³ã‚’有効ã«ã™ã‚‹</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> をインストールã—ã¾ã™</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ãªã—}=1{<ph name="DOMAIN_LIST" /> ã®ãƒ‘スワード 1 件(åŒæœŸï¼‰}=2{<ph name="DOMAIN_LIST" /> ã®ãƒ‘スワード 2 件(åŒæœŸï¼‰}other{<ph name="DOMAIN_LIST" /> ã®ãƒ‘スワード # 件(åŒæœŸï¼‰}}</translation>
<translation id="6355392890578844978">ã“ã®ãƒ–ラウザã¯ã€ä¼šç¤¾ã¾ãŸã¯ãã®ä»–ã®çµ„ç¹”ã«ã‚ˆã£ã¦ç®¡ç†ã•ã‚Œã¦ã„ã¾ã›ã‚“。ã“ã®ãƒ‡ãƒã‚¤ã‚¹ä¸Šã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティã¯ã€Chromium ã®å¤–部ã§ç®¡ç†ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚<ph name="BEGIN_LINK" />詳細<ph name="END_LINK" /></translation>
@@ -1966,7 +1979,6 @@
<translation id="643051589346665201">Google ã®ãƒ‘スワードを変更</translation>
<translation id="6433490469411711332">連絡先情報ã®ç·¨é›†</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ã§æŽ¥ç¶šãŒæ‹’å¦ã•ã‚Œã¾ã—ãŸã€‚</translation>
-<translation id="6438025220577812695">自分ã§å¤‰æ›´</translation>
<translation id="6440503408713884761">除外ã•ã‚Œã¾ã—ãŸ</translation>
<translation id="6443406338865242315">インストールã—ãŸæ‹¡å¼µæ©Ÿèƒ½ã¨ãƒ—ラグイン</translation>
<translation id="6446163441502663861">Kahu(å°ç­’)</translation>
@@ -2096,9 +2108,9 @@
<translation id="6828866289116430505">éºä¼å­¦</translation>
<translation id="6831043979455480757">翻訳</translation>
<translation id="6833752742582340615">Google アカウントã«ã‚«ãƒ¼ãƒ‰ã¨ãŠæ”¯æ‰•ã„情報をä¿å­˜ã™ã‚‹ã¨ã€ã™ã°ã‚„ã安全ã«ã”購入手続ãã‚’è¡Œãˆã¾ã™</translation>
-<translation id="6839929833149231406">地区</translation>
<translation id="6846340164947227603">仮想カード番å·ã‚’使用...</translation>
<translation id="6852204201400771460">アプリをå†èª­ã¿è¾¼ã¿ã—ã¾ã™ã‹ï¼Ÿ</translation>
+<translation id="6857776781123259569">パスワードを管ç†...</translation>
<translation id="686485648936420384">消費者資æº</translation>
<translation id="6865412394715372076">ç¾åœ¨ã€ã“ã®ã‚«ãƒ¼ãƒ‰ã‚’確èªã§ãã¾ã›ã‚“</translation>
<translation id="6869334554832814367">個人ローン</translation>
@@ -2147,7 +2159,6 @@
<translation id="6965978654500191972">デãƒã‚¤ã‚¹</translation>
<translation id="696703987787944103">知覚的</translation>
<translation id="6968269510885595029">セキュリティ キーを使用ã™ã‚‹</translation>
-<translation id="6970216967273061347">地区</translation>
<translation id="6971439137020188025">スライドã§æ–°ã—ã„ Google プレゼンテーションをã™ã°ã‚„ã作æˆã—ã¾ã™</translation>
<translation id="6972629891077993081">HID デãƒã‚¤ã‚¹</translation>
<translation id="6973656660372572881">固定プロキシ サーãƒãƒ¼ã¨ .pac スクリプト URL ã®ä¸¡æ–¹ãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
@@ -2186,7 +2197,6 @@
<translation id="7081308185095828845">ã“ã®æ©Ÿèƒ½ã¯ãŠä½¿ã„ã®ãƒ‡ãƒã‚¤ã‚¹ã§ã¯ã”利用ã„ãŸã ã‘ã¾ã›ã‚“</translation>
<translation id="7083258188081898530">トレイ 9</translation>
<translation id="7086090958708083563">ユーザーã‹ã‚‰ã‚¢ãƒƒãƒ—ロードãŒãƒªã‚¯ã‚¨ã‚¹ãƒˆã•ã‚Œã¾ã—ãŸ</translation>
-<translation id="7087282848513945231">郡</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" /> ã§ã™ã€‚Tab キーã€Enter キーã®é †ã«æŠ¼ã—ã¦ã€Chrome ã®è¨­å®šã§ã™ã¹ã¦ã®ã‚µã‚¤ãƒˆã«ä¿å­˜ã•ã‚Œã¦ã„る権é™ã¨ãƒ‡ãƒ¼ã‚¿ã‚’管ç†ã—ã¾ã™</translation>
<translation id="7096937462164235847">ã“ã®ã‚¦ã‚§ãƒ–サイトã®è­˜åˆ¥æƒ…å ±ã¯ç¢ºèªã•ã‚Œã¦ã„ã¾ã›ã‚“。</translation>
<translation id="7101893872976785596">ホラー映画</translation>
@@ -2205,10 +2215,10 @@
<ph name="LIST_ITEM" />フォームã«å…¥åŠ›ã•ã‚ŒãŸæƒ…å ±<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">ã“ã®ä½æ‰€ã«ã¯é…é€ã§ãã¾ã›ã‚“。別ã®ä½æ‰€ã‚’é¸æŠžã—ã¦ãã ã•ã„。</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ã“ã®ãƒ‡ãƒ¼ã‚¿ã®ã‚³ãƒ”ーã¯ç®¡ç†è€…ã«ã‚ˆã£ã¦ç¦æ­¢ã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="7135130955892390533">ステータスを表示</translation>
<translation id="7138472120740807366">é…é”方法</translation>
-<translation id="7139724024395191329">管轄区域</translation>
<translation id="7139892792842608322">メイントレイ</translation>
<translation id="714064300541049402">2 é¢ã®ç”»åƒã® X 軸移動</translation>
<translation id="7152423860607593928">Number-14(å°ç­’)</translation>
@@ -2425,6 +2435,7 @@
<translation id="7669271284792375604">ã“ã®ã‚µã‚¤ãƒˆã‚’利用ã™ã‚‹ã¨ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦ã€é–²è¦§æ™‚ã®ã‚¨ã‚¯ã‚¹ãƒšãƒªã‚¨ãƒ³ã‚¹ã‚’æãªã†ãƒ—ログラム(ホームページを改ã–ã‚“ã™ã‚‹ã€ã‚¢ã‚¯ã‚»ã‚¹å…ˆã®ã‚µã‚¤ãƒˆã«è¿½åŠ ã®åºƒå‘Šã‚’表示ã™ã‚‹ãªã©ã®ãƒ—ログラム)をインストールã™ã‚‹ã‚ˆã†èª˜å°Žã•ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{機密ã¨ã—ã¦æŒ‡å®šã•ã‚ŒãŸãƒ‡ãƒ¼ã‚¿ã«å¯¾ã™ã‚‹æ“ä½œï¼ˆãƒ­ã‚°ã‚¤ãƒ³ä»¥é™ 1 件)。<ph name="BEGIN_LINK" />詳細<ph name="END_LINK" />}other{機密ã¨ã—ã¦æŒ‡å®šã•ã‚ŒãŸãƒ‡ãƒ¼ã‚¿ã«å¯¾ã™ã‚‹æ“ä½œï¼ˆãƒ­ã‚°ã‚¤ãƒ³ä»¥é™ # 件)。<ph name="BEGIN_LINK" />詳細<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">用紙å—ã‘ 6</translation>
+<translation id="7675325315208090829">ãŠæ”¯æ‰•ã„方法を管ç†...</translation>
<translation id="7676643023259824263">クリップボードã«ã‚るテキスト<ph name="TEXT" />を検索ã—ã¾ã™</translation>
<translation id="7679367271685653708">Chrome ã®è¨­å®šã§é–²è¦§å±¥æ­´ã‚’表示ã€ç®¡ç†ã—ã¾ã™</translation>
<translation id="7679947978757153706">野çƒ</translation>
@@ -2467,7 +2478,6 @@
<translation id="7766518757692125295">スカート</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">åŒã˜é †åºï¼ˆä¸Šå‘ã)</translation>
-<translation id="777702478322588152">都é“府県</translation>
<translation id="7791011319128895129">未公開</translation>
<translation id="7791196057686275387">梱包</translation>
<translation id="7791543448312431591">追加</translation>
@@ -2558,12 +2568,12 @@
<translation id="8055534648776115597">è·æ¥­æ•™è‚²ã€ç”Ÿæ¶¯æ•™è‚²</translation>
<translation id="8057711352706143257">「<ph name="SOFTWARE_NAME" />ã€ãŒæ­£ã—ã設定ã•ã‚Œã¦ã„ã¾ã›ã‚“。通常ã€ã“ã®å•é¡Œã¯ã€Œ<ph name="SOFTWARE_NAME" />ã€ã‚’アンインストールã™ã‚‹ã“ã¨ã§è§£æ±ºã—ã¾ã™ã€‚<ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">食å“産業</translation>
-<translation id="8066955247577885446">エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚</translation>
<translation id="8067872629359326442">å½ã®ã‚µã‚¤ãƒˆã§ãƒ‘スワードを入力ã—ã¾ã—ãŸã€‚Chromium を使ã£ã¦ä¸æ­£åˆ©ç”¨ã¸ã®å¯¾ç­–ãŒã§ãã¾ã™ã€‚パスワードを変更ã—ã€Google ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®çŠ¶æ³ã‚’通知ã™ã‚‹ã«ã¯ã€[アカウントをä¿è­·] をクリックã—ã¦ãã ã•ã„。</translation>
<translation id="8070439594494267500">アプリã®ã‚¢ã‚¤ã‚³ãƒ³</translation>
<translation id="8074253406171541171">10x13(å°ç­’)</translation>
<translation id="8075736640322370409">æ–°ã—ã„ Google スプレッドシートをã™ã°ã‚„ã作æˆã—ã¾ã™</translation>
<translation id="8075898834294118863">サイトã®è¨­å®šã‚’管ç†</translation>
+<translation id="8076492880354921740">タブ</translation>
<translation id="8078141288243656252">回転時ã«æ³¨é‡ˆã‚’追加ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“</translation>
<translation id="8079031581361219619">ã“ã®ã‚µã‚¤ãƒˆã‚’å†èª­ã¿è¾¼ã¿ã—ã¾ã™ã‹ï¼Ÿ</translation>
<translation id="8081087320434522107">セダン</translation>
@@ -2686,6 +2696,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">設定</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" /> ã§ã™ã€‚Tab キーã€Enter キーã®é †ã«æŠ¼ã—ã¦ã€Chrome ã®è¨­å®šã§ Cookie ã®è¨­å®šã‚’管ç†ã—ã¾ã™</translation>
<translation id="8433057134996913067">ã»ã¨ã‚“ã©ã®ã‚¦ã‚§ãƒ–サイトã‹ã‚‰ãƒ­ã‚°ã‚¢ã‚¦ãƒˆã—ã¾ã™ã€‚</translation>
<translation id="8434840396568290395">ペット</translation>
@@ -2784,6 +2795,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> ã®ã‚³ãƒ¼ãƒ‰ã¯ã€Œ<ph name="ONE_TIME_CODE" />ã€ã§ã™</translation>
<translation id="874918643257405732">ã“ã®ã‚¿ãƒ–をブックマークã«è¿½åŠ ã—ã¾ã™</translation>
<translation id="8751426954251315517">後ã§ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。</translation>
+<translation id="8757526089434340176">Google Pay ã®ã‚¯ãƒ¼ãƒãƒ³ã‚’ã”利用ã„ãŸã ã‘ã¾ã™</translation>
<translation id="8758885506338294482">競技ビデオゲーム</translation>
<translation id="8759274551635299824">ã“ã®ã‚«ãƒ¼ãƒ‰ã¯æœ‰åŠ¹æœŸé™ãŒåˆ‡ã‚Œã¦ã„ã¾ã™</translation>
<translation id="87601671197631245">ã“ã®ã‚µã‚¤ãƒˆã§ã¯å¤ã„セキュリティ設定を使用ã—ã¦ã„ã¾ã™ã€‚ã“ã®ã‚µã‚¤ãƒˆã«ãƒ‘スワードã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カードãªã©ã®æƒ…報をé€ä¿¡ã™ã‚‹ã¨æµå‡ºã™ã‚‹æã‚ŒãŒã‚ã‚Šã¾ã™ã€‚</translation>
@@ -2791,6 +2803,7 @@
<translation id="8763927697961133303">USB デãƒã‚¤ã‚¹</translation>
<translation id="8763986294015493060">ç¾åœ¨é–‹ã„ã¦ã„ã‚‹ã™ã¹ã¦ã®ã‚·ãƒ¼ã‚¯ãƒ¬ãƒƒãƒˆ ウィンドウを閉ã˜ã¾ã™</translation>
<translation id="8766943070169463815">ä¿è­·ã•ã‚ŒãŸãŠæ”¯æ‰•ã„èªè¨¼æƒ…å ±ã®èªè¨¼ã‚·ãƒ¼ãƒˆãŒé–‹ã„ã¦ã„ã¾ã™</translation>
+<translation id="8767765348545497220">ヘルプãƒãƒ–ルを閉ã˜ã‚‹</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">オートãƒã‚¤</translation>
<translation id="8790007591277257123">削除ã®ã‚„ã‚Šç›´ã—(&amp;R)</translation>
@@ -2803,6 +2816,7 @@
<translation id="8806285662264631610">ãƒã‚¹ã€ãƒœãƒ‡ã‚£å•†å“</translation>
<translation id="8807160976559152894">トリミング(ページå˜ä½ï¼‰</translation>
<translation id="8808828119384186784">Chrome ã®è¨­å®š</translation>
+<translation id="8813277370772331957">後ã§é€šçŸ¥</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" /> ã§ã™ã€‚Tab キーã€Enter キーã®é †ã«æŠ¼ã™ã¨ã€Chrome ã®è¨­å®šã§ Chrome ã‚’æ›´æ–°ã—ã¾ã™</translation>
<translation id="8820817407110198400">ブックマーク</translation>
<translation id="882338992931677877">手動スロット</translation>
@@ -2982,6 +2996,7 @@
<translation id="988159990683914416">Developer Build</translation>
<translation id="989988560359834682">ä½æ‰€ã®ç·¨é›†</translation>
<translation id="991413375315957741">モーション センサーã¾ãŸã¯å…‰ã‚»ãƒ³ã‚µãƒ¼</translation>
+<translation id="992110854164447044">仮想カードを使用ã™ã‚‹ã¨ã€å®Ÿéš›ã®ã‚«ãƒ¼ãƒ‰ã‚’éžè¡¨ç¤ºã«ã—ã¦ã€ä¸æ­£è¡Œç‚ºã‹ã‚‰ä¿è­·ã§ãã¾ã™ã€‚<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ピンク</translation>
<translation id="992432478773561401">「<ph name="SOFTWARE_NAME" />ã€ãŒãƒ‘ソコンã¾ãŸã¯ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«æ­£ã—ãインストールã•ã‚Œã¦ã„ã¾ã›ã‚“。
diff --git a/chromium/components/strings/components_strings_ka.xtb b/chromium/components/strings/components_strings_ka.xtb
index 303c1789622..52d48083117 100644
--- a/chromium/components/strings/components_strings_ka.xtb
+++ b/chromium/components/strings/components_strings_ka.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">გრáƒáƒœáƒ¢áƒ”ბი, სტიპენდიები დრფინáƒáƒœáƒ¡áƒ£áƒ áƒ˜ დáƒáƒ®áƒ›áƒáƒ áƒ”ბáƒ</translation>
<translation id="1048785276086539861">áƒáƒœáƒáƒ¢áƒáƒªáƒ˜áƒ”ბის რედáƒáƒ¥áƒ¢áƒ˜áƒ áƒ”ბისáƒáƒ¡ ეს დáƒáƒ™áƒ£áƒ›áƒ”ნტი დáƒáƒ‘რუნდებრერთგვერდიáƒáƒœ ხედზე</translation>
<translation id="1050038467049342496">სხვრáƒáƒžáƒ”ბის დáƒáƒ®áƒ£áƒ áƒ•áƒ</translation>
+<translation id="1053959602163383901">თქვენ áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ áƒáƒ•áƒ¢áƒáƒ áƒ˜áƒ–áƒáƒªáƒ˜áƒ˜áƒ¡ მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ის მეშვეáƒáƒ‘ით დáƒáƒ“áƒáƒ¡áƒ¢áƒ£áƒ áƒ”ბრვებსáƒáƒ˜áƒ¢áƒ”ბზე, რáƒáƒ›áƒ”ლთრმიერáƒáƒª გáƒáƒ›áƒáƒ˜áƒ§áƒ”ნებრ<ph name="PROVIDER_ORIGIN" />. áƒáƒ› პრáƒáƒ•áƒáƒ˜áƒ“ერს შეიძლებáƒáƒ“რშეენáƒáƒ®áƒ ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ თქვენი გáƒáƒ“áƒáƒ®áƒ“ის მეთáƒáƒ“ის შესáƒáƒ®áƒ”ბ. სურვილისáƒáƒ›áƒ”ბრ, შეგიძლიáƒáƒ— <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">დრდáƒáƒ‘რუნებáƒ-დáƒáƒ›áƒáƒ¢áƒ”ბáƒ</translation>
<translation id="1056663316309890343">ფáƒáƒ¢áƒáƒ”ბის პრáƒáƒ’რáƒáƒ›áƒ£áƒšáƒ˜ უზრუნველყáƒáƒ¤áƒ</translation>
<translation id="1056898198331236512">გáƒáƒ¤áƒ áƒ—ხილებáƒ</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">წáƒáƒ¦áƒ”ბის მეთáƒáƒ“ი</translation>
<translation id="1281476433249504884">სტეკერი 1</translation>
<translation id="1285320974508926690">áƒáƒ áƒáƒ¡áƒ“რáƒáƒ¡ გáƒáƒ“áƒáƒ—áƒáƒ áƒ’მნრეს სáƒáƒ˜áƒ¢áƒ˜</translation>
+<translation id="1288548991597756084">უსáƒáƒ¤áƒ áƒ—ხáƒáƒ“ შეინáƒáƒ®áƒ”თ ბáƒáƒ áƒáƒ—ი</translation>
<translation id="1292571435393770077">ლáƒáƒœáƒ’áƒáƒ áƒ˜ 16</translation>
<translation id="1292701964462482250">„თქვენს კáƒáƒ›áƒžáƒ˜áƒ£áƒ¢áƒ”რზე დáƒáƒ˜áƒœáƒ¡áƒ¢áƒáƒšáƒ˜áƒ áƒ”ბული პრáƒáƒ’რáƒáƒ›áƒ£áƒšáƒ˜ უზრუნველყáƒáƒ¤áƒ ხელს უშლის Chrome-ს ვებზე უსáƒáƒ¤áƒ áƒ—ხáƒáƒ“ წვდáƒáƒ›áƒáƒ¨áƒ˜â€œ (მხáƒáƒšáƒáƒ“ Windows)</translation>
<translation id="1294154142200295408">ბრძáƒáƒœáƒ”ბáƒáƒ—რსტრიქáƒáƒœáƒ˜áƒ¡ ვáƒáƒ áƒ˜áƒáƒœáƒ¢áƒ”ბი</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;პრáƒáƒ‘ლემის მáƒáƒ¡áƒáƒ’ვáƒáƒ áƒ”ბლáƒáƒ“ დáƒáƒáƒ¬áƒ™áƒáƒžáƒ£áƒœáƒ”თ ღილáƒáƒ™áƒ–ე &lt;strong&gt;დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბáƒ&lt;/strong&gt; იმ გვერდზე, რáƒáƒ›áƒšáƒ˜áƒ¡ გáƒáƒ®áƒ¡áƒœáƒáƒ¡áƒáƒª ცდილáƒáƒ‘თ.&lt;/p&gt;</translation>
<translation id="1507780850870535225">ლáƒáƒœáƒ“შáƒáƒ¤áƒ¢áƒ˜áƒ¡ დიზáƒáƒ˜áƒœáƒ˜</translation>
<translation id="1513706915089223971">ისტáƒáƒ áƒ˜áƒ˜áƒ¡ ჩáƒáƒœáƒáƒ¬áƒ”რების სიáƒ</translation>
+<translation id="1516097932025103760">ის დáƒáƒ˜áƒ¨áƒ˜áƒ¤áƒ áƒ”ბრდრუსáƒáƒ¤áƒ áƒ—ხáƒáƒ“ შეინáƒáƒ®áƒ”ბáƒ. áƒáƒ›áƒáƒ¡áƒ—áƒáƒœ, CVC áƒáƒ áƒáƒ¡áƒáƒ“ეს შეინáƒáƒ®áƒ”ბáƒ.</translation>
<translation id="1517433312004943670">ტელეფáƒáƒœáƒ˜áƒ¡ ნáƒáƒ›áƒ áƒ˜áƒ¡ მითითებრáƒáƒ£áƒªáƒ˜áƒšáƒ”ბელიáƒ</translation>
<translation id="1519264250979466059">áƒáƒ¬áƒ§áƒáƒ‘ის თáƒáƒ áƒ˜áƒ¦áƒ˜</translation>
<translation id="1521159554480556801">ბáƒáƒ­áƒ™áƒáƒ•áƒáƒœáƒ˜ მáƒáƒ¡áƒáƒšáƒ”ბით დრტექსტილით შექმნილი ხელáƒáƒ•áƒœáƒ”ბáƒ</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">გáƒáƒ“áƒáƒ®áƒ“ის მეთáƒáƒ“ების შენáƒáƒ®áƒ•áƒ დრშევსებáƒ</translation>
<translation id="1663943134801823270">ბáƒáƒ áƒáƒ—ებისრდრმისáƒáƒ›áƒáƒ áƒ—ების შესáƒáƒ®áƒ”ბ ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ მიღებულირChrome-იდáƒáƒœ. მáƒáƒ—ი მáƒáƒ áƒ—ვრშეგიძლიáƒáƒ— <ph name="BEGIN_LINK" />პáƒáƒ áƒáƒ›áƒ”ტრებში<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642"><ph name="SOURCE_LANGUAGE" /> გვერდები áƒáƒ›áƒ˜áƒ”რიდáƒáƒœ ითáƒáƒ áƒ’მნებრშემდეგ ენáƒáƒ–ე: <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">შემáƒáƒ—áƒáƒ•áƒáƒ–ებით რáƒáƒ› ისáƒáƒ áƒ’ებლáƒáƒ—, გáƒáƒ›áƒáƒ˜áƒ§áƒ”ნეთ áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¡áƒ¬áƒáƒ áƒ”ბისთვის <ph name="CARD_DETAIL" /></translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> — <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ჯერ მáƒáƒ™áƒšáƒ” კიდე</translation>
<translation id="168693727862418163">წესების áƒáƒ› მნიშვნელáƒáƒ‘ის სქემის დáƒáƒ“áƒáƒ¡áƒ¢áƒ£áƒ áƒ”ბრვერ მáƒáƒ®áƒ”რხდáƒ, áƒáƒ›áƒ˜áƒ¢áƒáƒ› ის უგულებელყáƒáƒ¤áƒ˜áƒšáƒ˜ იქნებáƒ.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">მáƒáƒ«áƒ áƒáƒáƒ‘ის სენსáƒáƒ áƒ”ბი</translation>
<translation id="1717494416764505390">სáƒáƒ¤áƒáƒ¡áƒ¢áƒ ყუთი 3</translation>
<translation id="1718029547804390981">დáƒáƒ™áƒ£áƒ›áƒ”ნტი áƒáƒœáƒáƒ¢áƒ˜áƒ áƒ”ბისთვის მეტისმეტáƒáƒ“ დიდიáƒ</translation>
+<translation id="1720941539803966190">სáƒáƒ®áƒ”ლმძღვáƒáƒœáƒ”ლáƒáƒ¡ დáƒáƒ®áƒ£áƒ áƒ•áƒ</translation>
<translation id="1721424275792716183">* ველი áƒáƒ£áƒªáƒ˜áƒšáƒ”ბელიáƒ</translation>
<translation id="1727613060316725209">სერთიფიკáƒáƒ¢áƒ˜ ძáƒáƒšáƒáƒ¨áƒ˜áƒ</translation>
<translation id="1727741090716970331">მიუთითეთ ბáƒáƒ áƒáƒ—ის სწáƒáƒ áƒ˜ ნáƒáƒ›áƒ”რი</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">ბáƒáƒ áƒ‘ექიუ დრგრილი</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> გვერდები áƒáƒ  ითáƒáƒ áƒ’მნებáƒ.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{რáƒáƒªáƒ მáƒáƒ áƒ—ვის ეს სáƒáƒ¨áƒ£áƒáƒšáƒ”ბრჩáƒáƒ áƒ—ულირდრსტáƒáƒ¢áƒ£áƒ¡áƒ˜ áƒáƒ¥áƒ¢áƒ˜áƒ£áƒ áƒ˜áƒ, Chrome გáƒáƒœáƒ¡áƒáƒ–ღვრáƒáƒ•áƒ¡, áƒáƒ“áƒáƒ›áƒ˜áƒáƒœáƒ—რრáƒáƒ›áƒ”ლი ვრცელი ჯგუფის, áƒáƒœáƒ£ ე.წ. „კáƒáƒ°áƒáƒ áƒ¢áƒ˜áƒ¡â€œ, მსგáƒáƒ•áƒ¡áƒ˜áƒ თქვენი დáƒáƒ—ვáƒáƒšáƒ˜áƒ”რების ბáƒáƒšáƒáƒ“რáƒáƒ˜áƒœáƒ“ელი áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘áƒ. რეკლáƒáƒ›áƒ˜áƒ¡ გáƒáƒœáƒ›áƒ—áƒáƒ•áƒ¡áƒ”ბლებს შეეძლებáƒáƒ— ჯგუფისთვის რეკლáƒáƒ›áƒ˜áƒ¡ áƒáƒ áƒ©áƒ”ვáƒ, ხáƒáƒšáƒ თქვენი დáƒáƒ—ვáƒáƒšáƒ˜áƒ”რების áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘რკáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ“ შეინáƒáƒ®áƒ”ბრთქვენს მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ–ე. თქვენი ჯგუფი ყáƒáƒ•áƒ”ლდღიურáƒáƒ“ გáƒáƒœáƒáƒ®áƒšáƒ“ებáƒ.}=1{რáƒáƒªáƒ მáƒáƒ áƒ—ვის ეს სáƒáƒ¨áƒ£áƒáƒšáƒ”ბრჩáƒáƒ áƒ—ულირდრსტáƒáƒ¢áƒ£áƒ¡áƒ˜ áƒáƒ¥áƒ¢áƒ˜áƒ£áƒ áƒ˜áƒ, Chrome გáƒáƒœáƒ¡áƒáƒ–ღვრáƒáƒ•áƒ¡, áƒáƒ“áƒáƒ›áƒ˜áƒáƒœáƒ—რრáƒáƒ›áƒ”ლი ვრცელი ჯგუფის, áƒáƒœáƒ£ ე.წ. „კáƒáƒ°áƒáƒ áƒ¢áƒ˜áƒ¡â€œ, მსგáƒáƒ•áƒ¡áƒ˜áƒ თქვენი დáƒáƒ—ვáƒáƒšáƒ˜áƒ”რების ბáƒáƒšáƒáƒ“რáƒáƒ˜áƒœáƒ“ელი áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘áƒ. რეკლáƒáƒ›áƒ˜áƒ¡ გáƒáƒœáƒ›áƒ—áƒáƒ•áƒ¡áƒ”ბლებს შეეძლებáƒáƒ— ჯგუფისთვის რეკლáƒáƒ›áƒ˜áƒ¡ áƒáƒ áƒ©áƒ”ვáƒ, ხáƒáƒšáƒ თქვენი დáƒáƒ—ვáƒáƒšáƒ˜áƒ”რების áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘რკáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ“ შეინáƒáƒ®áƒ”ბრთქვენს მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ–ე. თქვენი ჯგუფი ყáƒáƒ•áƒ”ლდღიურáƒáƒ“ გáƒáƒœáƒáƒ®áƒšáƒ“ებáƒ.}other{რáƒáƒªáƒ მáƒáƒ áƒ—ვის ეს სáƒáƒ¨áƒ£áƒáƒšáƒ”ბრჩáƒáƒ áƒ—ულირდრსტáƒáƒ¢áƒ£áƒ¡áƒ˜ áƒáƒ¥áƒ¢áƒ˜áƒ£áƒ áƒ˜áƒ, Chrome გáƒáƒœáƒ¡áƒáƒ–ღვრáƒáƒ•áƒ¡, áƒáƒ“áƒáƒ›áƒ˜áƒáƒœáƒ—რრáƒáƒ›áƒ”ლი ვრცელი ჯგუფის, áƒáƒœáƒ£ ე.წ. „კáƒáƒ°áƒáƒ áƒ¢áƒ˜áƒ¡â€œ, მსგáƒáƒ•áƒ¡áƒ˜áƒ თქვენი დáƒáƒ—ვáƒáƒšáƒ˜áƒ”რების ბáƒáƒšáƒáƒ“რáƒáƒ˜áƒœáƒ“ელი áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘áƒ. რეკლáƒáƒ›áƒ˜áƒ¡ გáƒáƒœáƒ›áƒ—áƒáƒ•áƒ¡áƒ”ბლებს შეეძლებáƒáƒ— ჯგუფისთვის რეკლáƒáƒ›áƒ˜áƒ¡ áƒáƒ áƒ©áƒ”ვáƒ, ხáƒáƒšáƒ თქვენი დáƒáƒ—ვáƒáƒšáƒ˜áƒ”რების áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘რკáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ“ შეინáƒáƒ®áƒ”ბრთქვენს მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ–ე. თქვენი ჯგუფი {NUM_DAYS} დღეში ერთხელ გáƒáƒœáƒáƒ®áƒšáƒ“ებáƒ.}}</translation>
-<translation id="2053553514270667976">ZIP კáƒáƒ“ი</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 შეთáƒáƒ•áƒáƒ–ებáƒ}other{# შეთáƒáƒ•áƒáƒ–ებáƒ}}</translation>
+<translation id="2066915425250589881">მისი წáƒáƒ¨áƒšáƒ˜áƒ¡ მáƒáƒ—ხáƒáƒ•áƒœáƒ</translation>
<translation id="2068528718802935086">ჩვილები დრáƒáƒ®áƒáƒšáƒ¤áƒ”ხáƒáƒ“გმული ბáƒáƒ•áƒ¨áƒ•áƒ”ბი</translation>
<translation id="2071156619270205202">ეს ბáƒáƒ áƒáƒ—ი áƒáƒ  áƒáƒ™áƒ›áƒáƒ§áƒáƒ¤áƒ˜áƒšáƒ”ბს ვირტუáƒáƒšáƒ£áƒ áƒ˜ ბáƒáƒ áƒáƒ—ის ნáƒáƒ›áƒ áƒ˜áƒ¡ მიღების კრიტერიუმებს.</translation>
<translation id="2071692954027939183">შეტყáƒáƒ‘ინებები áƒáƒ•áƒ¢áƒáƒ›áƒáƒ¢áƒ£áƒ áƒáƒ“ დáƒáƒ˜áƒ‘ლáƒáƒ™áƒ, რáƒáƒ“გáƒáƒœ, რáƒáƒ’áƒáƒ áƒª წესი, მáƒáƒ— მიღებáƒáƒ¡ კრძáƒáƒšáƒáƒ•áƒ— ხáƒáƒšáƒ›áƒ”</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">სინქრáƒáƒœáƒ˜áƒ–áƒáƒªáƒ˜áƒ˜áƒ¡ მáƒáƒ áƒ—ვის ღილáƒáƒ™áƒ˜, თქვენ მიერ სინქრáƒáƒœáƒ˜áƒ–ებული ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ Chrome-ის პáƒáƒ áƒáƒ›áƒ”ტრებიდáƒáƒœ სáƒáƒ›áƒáƒ áƒ—áƒáƒ•áƒáƒ“ დáƒáƒáƒ­áƒ˜áƒ áƒ”თ კლáƒáƒ•áƒ˜áƒ¨áƒ¡ Enter</translation>
<translation id="2091887806945687916">ხმáƒ</translation>
<translation id="2094505752054353250">დáƒáƒ›áƒ”ნის შეუსáƒáƒ‘áƒáƒ›áƒáƒ‘áƒ</translation>
-<translation id="2096368010154057602">დეპáƒáƒ áƒ¢áƒáƒ›áƒ”ნტი</translation>
<translation id="2099652385553570808">სáƒáƒ›áƒ›áƒáƒ’áƒáƒ“ დáƒáƒ¡áƒ¢áƒ”პლერებრმáƒáƒ áƒªáƒ®áƒœáƒ˜áƒ•</translation>
<translation id="2101225219012730419">ვერსიáƒ:</translation>
<translation id="2102134110707549001">ძლიერი პáƒáƒ áƒáƒšáƒ˜áƒ¡ შემáƒáƒ—áƒáƒ•áƒáƒ–ებáƒâ€¦</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">áƒáƒ›áƒ”რიკული ფეხბურთი</translation>
<translation id="2187317261103489799">áƒáƒ›áƒáƒªáƒœáƒáƒ‘რ(ნáƒáƒ’ულისხმევი)</translation>
<translation id="2188375229972301266">მრáƒáƒ•áƒáƒšáƒáƒ“ გáƒáƒ®áƒ•áƒ áƒ”ტრქვემáƒáƒ—</translation>
-<translation id="2188852899391513400">თქვენ მიერ áƒáƒ®áƒšáƒáƒ®áƒáƒœ გáƒáƒ›áƒáƒ§áƒ”ნებული პáƒáƒ áƒáƒšáƒ˜ ნáƒáƒžáƒáƒ•áƒœáƒ˜áƒ გáƒáƒŸáƒáƒœáƒ˜áƒš მáƒáƒœáƒáƒªáƒ”მებში. თქვენი áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ˜áƒ¡ დáƒáƒªáƒ•áƒ˜áƒ¡ მიზნით, Google პáƒáƒ áƒáƒšáƒ”ბის მმáƒáƒ áƒ—ველი გირჩევთ, áƒáƒ®áƒšáƒáƒ•áƒ” შეცვáƒáƒšáƒáƒ— ის, შემდეგ კი შეáƒáƒ›áƒáƒ¬áƒ›áƒáƒ— თქვენი შენáƒáƒ®áƒ£áƒšáƒ˜ პáƒáƒ áƒáƒšáƒ”ბი.</translation>
<translation id="219906046732893612">რემáƒáƒœáƒ¢áƒ˜</translation>
<translation id="2202020181578195191">შეიყვáƒáƒœáƒ”თ მáƒáƒ¥áƒ›áƒ”დების ვáƒáƒ“ის გáƒáƒ¡áƒ•áƒšáƒ˜áƒ¡ სწáƒáƒ áƒ˜ წელი</translation>
<translation id="22081806969704220">ლáƒáƒœáƒ’áƒáƒ áƒ˜ 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">ფáƒáƒ˜áƒšáƒ”ბის რედáƒáƒ¥áƒ¢áƒ˜áƒ áƒ”ბáƒ</translation>
<translation id="2215963164070968490">ძáƒáƒ¦áƒšáƒ”ბი</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />-ზე შეტევის შედეგáƒáƒ“ თქვენს მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ–ე შეიძლებრდáƒáƒ˜áƒœáƒ¡áƒ¢áƒáƒšáƒ˜áƒ áƒ“ეს სáƒáƒ®áƒ˜áƒ¤áƒáƒ—რáƒáƒžáƒ”ბი, რáƒáƒ›áƒšáƒ”ბსáƒáƒª შეუძლირთქვენი მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ის დáƒáƒ–იáƒáƒœáƒ”ბáƒ, მáƒáƒ‘ილური სერვისის სáƒáƒ¤áƒáƒ¡áƒ£áƒ áƒ˜áƒ¡ ფáƒáƒ áƒ£áƒšáƒáƒ“ გáƒáƒ–რდრთუ პერსáƒáƒœáƒáƒšáƒ£áƒ áƒ˜ ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ მáƒáƒžáƒáƒ áƒ•áƒ. <ph name="BEGIN_LEARN_MORE_LINK" />შეიტყვეთ მეტი<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">სáƒáƒ®áƒ”ლმძღვáƒáƒœáƒ”ლáƒáƒ¡ თáƒáƒ•áƒ˜áƒ“áƒáƒœ გáƒáƒ¨áƒ•áƒ”ბáƒ</translation>
+<translation id="2219735899272417925">სáƒáƒ­áƒ˜áƒ áƒáƒ მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ის გáƒáƒ“áƒáƒ§áƒ”ნებáƒ</translation>
<translation id="2224337661447660594">ინტერნეტ-კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ áƒáƒ  áƒáƒ áƒ˜áƒ¡</translation>
<translation id="2230458221926704099">გáƒáƒáƒ¡áƒ¬áƒáƒ áƒ”თ თქვენი კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ <ph name="BEGIN_LINK" />დიáƒáƒ’ნáƒáƒ¡áƒ¢áƒ˜áƒ™áƒ£áƒ áƒ˜ áƒáƒžáƒ˜áƒ¡<ph name="END_LINK" /> მეშვეáƒáƒ‘ით</translation>
<translation id="2239100178324503013">áƒáƒ®áƒšáƒáƒ•áƒ” გáƒáƒ’ზáƒáƒ•áƒœáƒ</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">áƒáƒ™áƒ áƒ«áƒáƒšáƒ£áƒšáƒ˜áƒ (ნáƒáƒ’ულისხმევáƒáƒ“)</translation>
<translation id="2512413427717747692">სისტემის ნáƒáƒ’ულისხმევ ბრáƒáƒ£áƒ–ერáƒáƒ“ Chrome-ის დáƒáƒ§áƒ”ნების ღილáƒáƒ™áƒ˜, iOS პáƒáƒ áƒáƒ›áƒ”ტრებიდáƒáƒœ Chrome სისტემის ნáƒáƒ’ულისხმევ ბრáƒáƒ£áƒ–ერáƒáƒ“ რáƒáƒ› დáƒáƒáƒ§áƒ”ნáƒáƒ—, დáƒáƒáƒ­áƒ˜áƒ áƒ”თ კლáƒáƒ•áƒ˜áƒ¨áƒ¡ Enter</translation>
<translation id="2515629240566999685">თქვენს ზáƒáƒœáƒáƒ¨áƒ˜ სიგნáƒáƒšáƒ˜áƒ¡ შემáƒáƒ¬áƒ›áƒ”ბáƒ</translation>
+<translation id="2515761554693942801">თქვენ áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ Touch ID-ს მეშვეáƒáƒ‘ით დáƒáƒ“áƒáƒ¡áƒ¢áƒ£áƒ áƒ”ბრვებსáƒáƒ˜áƒ¢áƒ”ბზე, რáƒáƒ›áƒ”ლთრმიერáƒáƒª გáƒáƒ›áƒáƒ˜áƒ§áƒ”ნებრ<ph name="PROVIDER_ORIGIN" />. áƒáƒ› პრáƒáƒ•áƒáƒ˜áƒ“ერს შეიძლებáƒáƒ“რშეენáƒáƒ®áƒ ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ თქვენი გáƒáƒ“áƒáƒ®áƒ“ის მეთáƒáƒ“ის შესáƒáƒ®áƒ”ბ. სურვილისáƒáƒ›áƒ”ბრ, შეგიძლიáƒáƒ— <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">ქვედრმáƒáƒ áƒ¯áƒ•áƒ”ნრნáƒáƒ¬áƒ˜áƒšáƒ˜áƒ¡ დáƒáƒ¡áƒ¢áƒ”პლერებáƒ</translation>
<translation id="2521736961081452453">ფáƒáƒ áƒ›áƒ˜áƒ¡ შექმნáƒ</translation>
<translation id="2523886232349826891">შეინáƒáƒ®áƒ”ბრმხáƒáƒšáƒáƒ“ áƒáƒ› მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ–ე</translation>
<translation id="2524461107774643265">მიუთითეთ დáƒáƒ›áƒáƒ¢áƒ”ბითი ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ</translation>
<translation id="2529899080962247600">áƒáƒ› ველში <ph name="MAX_ITEMS_LIMIT" />-ზე მეტი ჩáƒáƒœáƒáƒ¬áƒ”რი áƒáƒ  უნდრიყáƒáƒ¡. შემდგáƒáƒ›áƒ˜ ჩáƒáƒœáƒáƒ¬áƒ”რები იგნáƒáƒ áƒ˜áƒ áƒ”ბული იქნებáƒ.</translation>
+<translation id="253493526287553278">პრáƒáƒ›áƒáƒ™áƒáƒ“ის დეტáƒáƒšáƒ”ბის ნáƒáƒ®áƒ•áƒ</translation>
<translation id="2535585790302968248">ვების კáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ“ დáƒáƒ¡áƒáƒ—ვáƒáƒšáƒ˜áƒ”რებლáƒáƒ“ გáƒáƒ®áƒ¡áƒ”ნით áƒáƒ®áƒáƒšáƒ˜ ინკáƒáƒ’ნიტრჩáƒáƒœáƒáƒ áƒ—ი</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{დრ1 სხვáƒ}other{დრ# სხვáƒ}}</translation>
<translation id="2536110899380797252">მისáƒáƒ›áƒáƒ áƒ—ის დáƒáƒ›áƒáƒ¢áƒ”ბáƒ</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">ფáƒáƒ¢áƒ დრციფრული ხელáƒáƒ•áƒœáƒ”ბáƒ</translation>
<translation id="2601150049980261779">რáƒáƒ›áƒáƒœáƒ¢áƒ˜áƒ™áƒ£áƒšáƒ˜ ფილმები</translation>
<translation id="2604589665489080024">პáƒáƒž-მუსიკáƒ</translation>
-<translation id="2609632851001447353">ვáƒáƒ áƒ˜áƒáƒªáƒ˜áƒ”ბი</translation>
<translation id="2610561535971892504">დáƒáƒáƒ¬áƒ™áƒáƒžáƒ£áƒœáƒ”თ დáƒáƒ¡áƒáƒ™áƒáƒžáƒ˜áƒ áƒ”ბლáƒáƒ“</translation>
<translation id="2617988307566202237">Chrome-ის მიერ <ph name="BEGIN_EMPHASIS" />áƒáƒ  შეინáƒáƒ®áƒ”ბáƒ<ph name="END_EMPHASIS" /> შემდეგი ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">დáƒáƒ‘áƒáƒ“ებისრდრსáƒáƒ®áƒ”ლების დღეები</translation>
<translation id="2677748264148917807">დáƒáƒ¢áƒáƒ•áƒ”ბáƒ</translation>
+<translation id="2679714844901977852">უსáƒáƒ¤áƒ áƒ—ხრდრსწრáƒáƒ¤áƒ˜ áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¡áƒ¬áƒáƒ áƒ”ბისთვის შეინáƒáƒ®áƒ”თ თქვენი ბáƒáƒ áƒáƒ—ისრდრბილინგის ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ თქვენს Google áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜ (<ph name="USER_EMAIL" />)</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">სáƒáƒ£áƒ™áƒ”თესáƒáƒ“ მáƒáƒ áƒ’ებáƒ</translation>
<translation id="2688969097326701645">გáƒáƒ’რძელებáƒ</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">ინტერნტერპრáƒáƒ•áƒáƒ˜áƒ“ერები</translation>
<translation id="2856444702002559011">თáƒáƒ•áƒ“áƒáƒ›áƒ¡áƒ®áƒ›áƒ”ლები შეიძლებრცáƒáƒ“áƒáƒœ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />-დáƒáƒœ თქვენი ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ მáƒáƒžáƒáƒ áƒ•áƒ (მáƒáƒ’áƒáƒšáƒ˜áƒ—áƒáƒ“, პáƒáƒ áƒáƒšáƒ”ბის, შეტყáƒáƒ‘ინებების áƒáƒœ სáƒáƒ™áƒ áƒ”დიტრბáƒáƒ áƒáƒ—ების მáƒáƒœáƒáƒªáƒ”მების). <ph name="BEGIN_LEARN_MORE_LINK" />შეიტყვეთ მეტი<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">áƒáƒ› სáƒáƒ˜áƒ¢áƒ–ე ნáƒáƒ©áƒ•áƒ”ნებირმáƒáƒ›áƒáƒ‘ეზრებელი áƒáƒœ შეცდáƒáƒ›áƒáƒ¨áƒ˜ შემყვáƒáƒœáƒ˜ რეკლáƒáƒ›áƒ.</translation>
-<translation id="286512204874376891">ვირტუáƒáƒšáƒ£áƒ áƒ˜ ბáƒáƒ áƒáƒ—ი შენიღბáƒáƒ•áƒ¡ თქვენს რეáƒáƒšáƒ£áƒ  ბáƒáƒ áƒáƒ—ს, რáƒáƒª დáƒáƒ’იცáƒáƒ•áƒ— თáƒáƒ¦áƒšáƒ˜áƒ—áƒáƒ‘ის პáƒáƒ¢áƒ”ნციური შემთხვევებისგáƒáƒœ. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">მეგáƒáƒ‘რული</translation>
<translation id="28761159517501904">ფილმები</translation>
<translation id="2876489322757410363">გáƒáƒ áƒ” áƒáƒžáƒšáƒ˜áƒ™áƒáƒªáƒ˜áƒ˜áƒ— გáƒáƒ“áƒáƒ®áƒ“ის შემთხვევáƒáƒ¨áƒ˜, ინკáƒáƒ’ნიტრრეჟიმიდáƒáƒœ გáƒáƒ®áƒ•áƒáƒšáƒ—. გსურთ გáƒáƒ’რძელებáƒ?</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">დისკი</translation>
<translation id="3162559335345991374">Wi-Fi-მ, რáƒáƒ›áƒ”ლსáƒáƒª თქვენ იყენებთ, შეიძლებრმáƒáƒ˜áƒ—ხáƒáƒ•áƒáƒ¡ თქვენი სტუმრáƒáƒ‘რმის áƒáƒ•áƒ¢áƒáƒ áƒ˜áƒ–áƒáƒªáƒ˜áƒ˜áƒ¡ გვერდზე.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">კუნძული</translation>
<translation id="3176929007561373547">შეáƒáƒ›áƒáƒ¬áƒ›áƒ”თ თქვენი პრáƒáƒ¥áƒ¡áƒ˜áƒ¡ პáƒáƒ áƒáƒ›áƒ”ტრები, áƒáƒœ დáƒáƒ£áƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ“ით ქსელის áƒáƒ“მინისტრáƒáƒ¢áƒáƒ áƒ¡, რáƒáƒ—áƒ
დáƒáƒ áƒ¬áƒ›áƒ£áƒœáƒ“ეთ, რáƒáƒ› პრáƒáƒ¥áƒ¡áƒ˜ სერვერი მუშáƒáƒáƒ‘ს. თუ áƒáƒ  ხáƒáƒ áƒ— დáƒáƒ áƒ¬áƒ›áƒ£áƒœáƒ”ბული,
რáƒáƒ› პრáƒáƒ¥áƒ¡áƒ˜ სერვერს იყენებთ:
@@ -877,9 +882,6 @@
<translation id="3369192424181595722">სáƒáƒáƒ—ის შეცდáƒáƒ›áƒ</translation>
<translation id="3369459162151165748">áƒáƒ•áƒ¢áƒáƒ›áƒáƒ‘ილების ნáƒáƒ¬áƒ˜áƒšáƒ”ბი დრáƒáƒ¥áƒ¡áƒ”სუáƒáƒ áƒ”ბი</translation>
<translation id="3371064404604898522">Chrome-ის დáƒáƒ§áƒ”ნებრნáƒáƒ’ულისხმევ ბრáƒáƒ£áƒ–ერáƒáƒ“</translation>
-<translation id="3371076217486966826"><ph name="URL" /> მáƒáƒ˜áƒ—ხáƒáƒ•áƒ¡:
- • თქვენი შემáƒáƒ’áƒáƒ áƒ”ნის 3-გáƒáƒœáƒ–áƒáƒ›áƒ˜áƒšáƒ”ბიáƒáƒœáƒ˜ რუკის შექმნáƒáƒ¡áƒ დრკáƒáƒ›áƒ”რის პáƒáƒ–იციისთვის თვáƒáƒšáƒ˜áƒ¡ მიდევნებáƒáƒ¡
- • თქვენი კáƒáƒ›áƒ”რის გáƒáƒ›áƒáƒ§áƒ”ნებáƒáƒ¡</translation>
<translation id="337363190475750230">წáƒáƒ¨áƒšáƒ˜áƒšáƒ˜</translation>
<translation id="3375754925484257129">Chrome-ის უსáƒáƒ¤áƒ áƒ—ხáƒáƒ”ბის შემáƒáƒ¬áƒ›áƒ”ბის გáƒáƒ¨áƒ•áƒ”ბáƒ</translation>
<translation id="3377144306166885718">სერვერმრგáƒáƒ›áƒáƒ˜áƒ§áƒ”ნრTLS-ის მáƒáƒ«áƒ•áƒ”ლებული ვერსიáƒ.</translation>
@@ -896,6 +898,7 @@
<translation id="3399952811970034796">მიწáƒáƒ“ების მისáƒáƒ›áƒáƒ áƒ—ი</translation>
<translation id="3402261774528610252">სáƒáƒ˜áƒ¢áƒ˜áƒ¡ ჩáƒáƒ¡áƒáƒ¢áƒ•áƒ˜áƒ áƒ—áƒáƒ“ გáƒáƒ›áƒáƒ˜áƒ§áƒ”ნებáƒáƒ“რTLS 1.0 áƒáƒœ TLS 1.1, რáƒáƒ›áƒ”ლიც მáƒáƒ«áƒ•áƒ”ლებულირდრმáƒáƒ›áƒáƒ•áƒáƒšáƒ¨áƒ˜ გáƒáƒ£áƒ¥áƒ›áƒ“ებáƒ. გáƒáƒ£áƒ¥áƒ›áƒ”ბის შემდეგ მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლები ვეღáƒáƒ  შეძლებენ áƒáƒ› სáƒáƒ˜áƒ¢áƒ˜áƒ¡ ჩáƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვáƒáƒ¡. სერვერზე უნდრგáƒáƒáƒ¥áƒ¢áƒ˜áƒ£áƒ áƒ“ეს TLS 1.2 áƒáƒœ უფრრáƒáƒ®áƒáƒšáƒ˜ ვერსიáƒ.</translation>
<translation id="3405664148539009465">შრიფტების მáƒáƒ áƒ’ებáƒ</translation>
+<translation id="3407789382767355356">სისტემáƒáƒ¨áƒ˜ შესვლრმესáƒáƒ›áƒ” მხáƒáƒ áƒ˜áƒ¡ მეშვეáƒáƒ‘ით</translation>
<translation id="3409896703495473338">უსáƒáƒ¤áƒ áƒ—ხáƒáƒ”ბის პáƒáƒ áƒáƒ›áƒ”ტრების მáƒáƒ áƒ—ვáƒ</translation>
<translation id="3414952576877147120">ზáƒáƒ›áƒ:</translation>
<translation id="3417660076059365994">თქვენ მიერ áƒáƒ¢áƒ•áƒ˜áƒ áƒ—ული áƒáƒœ დáƒáƒ áƒ—ული ფáƒáƒ˜áƒšáƒ”ბი გáƒáƒ¡áƒáƒáƒœáƒáƒšáƒ˜áƒ–ებლáƒáƒ“ გáƒáƒ”გზáƒáƒ•áƒœáƒ”ბრGoogle Cloud-ს áƒáƒœ მესáƒáƒ›áƒ” მხáƒáƒ áƒ”ს. მáƒáƒ’áƒáƒšáƒ˜áƒ—áƒáƒ“, შეიძლებრშესრულდეს მáƒáƒ—ი სკáƒáƒœáƒ˜áƒ áƒ”ბრმáƒáƒ—ში სენსიტიური მáƒáƒœáƒáƒªáƒ”მების áƒáƒœ მáƒáƒ•áƒœáƒ” კáƒáƒ“ის áƒáƒ¦áƒ›áƒáƒ¡áƒáƒ©áƒ”ნáƒáƒ“.</translation>
@@ -928,6 +931,7 @@
<translation id="3477679029130949506">კინáƒáƒ—ეáƒáƒ¢áƒ áƒ”ბისრდრთეáƒáƒ¢áƒ áƒ”ბის áƒáƒ¤áƒ˜áƒ¨áƒ”ბი</translation>
<translation id="3479552764303398839">áƒáƒ®áƒšáƒ áƒáƒ áƒ</translation>
<translation id="3484560055331845446">თქვენ შეიძლებრდáƒáƒ™áƒáƒ áƒ’áƒáƒ— წვდáƒáƒ›áƒ თქვენს Google áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ–ე. Chrome გირჩევთ, áƒáƒ®áƒšáƒáƒ•áƒ” შეცვáƒáƒšáƒáƒ— პáƒáƒ áƒáƒšáƒ˜. თქვენ მáƒáƒ’იწევთ სისტემáƒáƒ¨áƒ˜ შესვლáƒ.</translation>
+<translation id="3484861421501147767">შეხსენებáƒ: ხელმისáƒáƒ¬áƒ•áƒ“áƒáƒ›áƒ˜áƒ შენáƒáƒ®áƒ£áƒšáƒ˜ პრáƒáƒ›áƒáƒ™áƒáƒ“ი</translation>
<translation id="3487845404393360112">ლáƒáƒœáƒ’áƒáƒ áƒ˜ 4</translation>
<translation id="3495081129428749620">გვერდზე
„<ph name="PAGE_TITLE" />“ პáƒáƒ•áƒœáƒ</translation>
@@ -1052,6 +1056,7 @@
<translation id="3810973564298564668">მáƒáƒ áƒ—ვáƒ</translation>
<translation id="3816482573645936981">მნიშვნელáƒáƒ‘რ(ჩáƒáƒœáƒáƒªáƒ•áƒšáƒ”ბული)</translation>
<translation id="382518646247711829">თუ იყენებთ პრáƒáƒ¥áƒ¡áƒ˜áƒ¡ სერვერს…</translation>
+<translation id="3826050100957962900">სისტემáƒáƒ¨áƒ˜ შესვლრმესáƒáƒ›áƒ” მხáƒáƒ áƒ˜áƒ¡ მეშვეáƒáƒ‘ით</translation>
<translation id="3827112369919217609">áƒáƒ‘სáƒáƒšáƒ£áƒ¢áƒ£áƒ áƒ˜</translation>
<translation id="3827666161959873541">სáƒáƒáƒ¯áƒáƒ®áƒ ფილმები</translation>
<translation id="3828924085048779000">ცáƒáƒ áƒ˜áƒ”ლი პáƒáƒ áƒáƒšáƒ˜ ნებáƒáƒ“áƒáƒ áƒ—ული áƒáƒ  áƒáƒ áƒ˜áƒ¡.</translation>
@@ -1064,9 +1069,9 @@
<translation id="3858027520442213535">გáƒáƒœáƒáƒ®áƒšáƒ”ბის თáƒáƒ áƒ˜áƒ¦áƒ˜ დრდრáƒ</translation>
<translation id="3858860766373142691">სáƒáƒ®áƒ”ლი</translation>
<translation id="3872834068356954457">მეცნიერებáƒ</translation>
+<translation id="3875783148670536197">ინსტრუქციის ნáƒáƒ®áƒ•áƒ</translation>
<translation id="3881478300875776315">ნáƒáƒ™áƒšáƒ”ბი ხáƒáƒ–ის ჩვენებáƒ</translation>
<translation id="3884278016824448484">კáƒáƒœáƒ¤áƒšáƒ˜áƒ¥áƒ¢áƒ£áƒ áƒ˜ მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ის იდენტიფიკáƒáƒ¢áƒáƒ áƒ˜</translation>
-<translation id="3885155851504623709">სáƒáƒ›áƒáƒ¥áƒáƒšáƒáƒ¥áƒ áƒáƒšáƒ¥áƒ˜</translation>
<translation id="388632593194507180">áƒáƒ¦áƒ›áƒáƒ©áƒ”ნილირმáƒáƒœáƒ˜áƒ¢áƒáƒ áƒ˜áƒœáƒ’ი</translation>
<translation id="3886948180919384617">სტეკერი 3</translation>
<translation id="3890664840433101773">ელფáƒáƒ¡áƒ¢áƒ˜áƒ¡ დáƒáƒ›áƒáƒ¢áƒ”ბáƒ</translation>
@@ -1096,9 +1101,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> დáƒáƒ‘ლáƒáƒ™áƒ˜áƒšáƒ˜áƒ</translation>
<translation id="3978338123949022456">ძიების რეჟიმი, áƒáƒ™áƒ áƒ˜áƒ¤áƒ”თ სáƒáƒ«áƒ˜áƒ”ბრმáƒáƒ—ხáƒáƒ•áƒœáƒ დრდáƒáƒáƒ­áƒ˜áƒ áƒ”თ Enter-ს, <ph name="KEYWORD_SUFFIX" />-ით რáƒáƒ› მáƒáƒ˜áƒ«áƒ˜áƒáƒ—</translation>
<translation id="398470910934384994">ფრინველები</translation>
+<translation id="3985750352229496475">მისáƒáƒ›áƒáƒ áƒ—ების მáƒáƒ áƒ—ვáƒâ€¦</translation>
<translation id="3986705137476756801">პირდáƒáƒžáƒ˜áƒ áƒ˜ სუბტიტრების დრáƒáƒ”ბით გáƒáƒ›áƒáƒ áƒ—ვáƒ</translation>
<translation id="3987940399970879459">1 მბáƒáƒ˜áƒ¢áƒ–ე ნáƒáƒ™áƒšáƒ”ბი</translation>
<translation id="3990250421422698716">კიდის წáƒáƒœáƒáƒªáƒ•áƒšáƒ”ბáƒ</translation>
+<translation id="3992684624889376114">áƒáƒ› გვერდის შესáƒáƒ®áƒ”ბ</translation>
<translation id="3996311196211510766">სáƒáƒ˜áƒ¢áƒ›áƒ (<ph name="ORIGIN" />) მáƒáƒ˜áƒ—ხáƒáƒ•áƒ წყáƒáƒ áƒáƒ¡ წესების მისáƒáƒ“áƒáƒ’ებრმის მიმáƒáƒ áƒ— ყველრმáƒáƒ—ხáƒáƒ•áƒœáƒ˜áƒ¡áƒ—ვის, თუმცრáƒáƒ› წესების მისáƒáƒ“áƒáƒ’ებრáƒáƒ›áƒŸáƒáƒ›áƒáƒ“ ვერ ხერხდებáƒ.</translation>
<translation id="4006465311664329701">გáƒáƒ“áƒáƒ®áƒ“ის მეთáƒáƒ“ები, შემáƒáƒ—áƒáƒ•áƒáƒ–ებები დრმისáƒáƒ›áƒáƒ áƒ—ები Google Pay-დáƒáƒœ</translation>
<translation id="4009243425692662128">თქვენ მიერ áƒáƒ›áƒáƒ‘ეჭდილი გვერდების კáƒáƒœáƒ¢áƒ”ნტი, áƒáƒœáƒáƒšáƒ˜áƒ–ის მიზნით, იგზáƒáƒ•áƒœáƒ”ბრGoogle Cloud-ში áƒáƒœ მესáƒáƒ›áƒ” მხáƒáƒ áƒ”სთáƒáƒœ. მáƒáƒ’áƒáƒšáƒ˜áƒ—áƒáƒ“, შესáƒáƒ«áƒšáƒáƒ, შესრულდეს გვერდების სკáƒáƒœáƒ˜áƒ áƒ”ბრსენსიტიური მáƒáƒœáƒáƒªáƒ”მების áƒáƒ¦áƒ›áƒáƒ©áƒ”ნის მიზნით.</translation>
@@ -1218,6 +1225,7 @@
<translation id="4305666528087210886">თქვენს ფáƒáƒ˜áƒšáƒ–ე წვდáƒáƒ›áƒ ვერ მáƒáƒ®áƒ”რხდáƒ</translation>
<translation id="4306529830550717874">გსურთ, შეინáƒáƒ®áƒáƒ— მისáƒáƒ›áƒáƒ áƒ—ი?</translation>
<translation id="4306812610847412719">გáƒáƒªáƒ•áƒšáƒ˜áƒ¡ ბუფერი</translation>
+<translation id="4310070645992025887">მáƒáƒ˜áƒ«áƒ˜áƒ”თ თქვენს პრáƒáƒªáƒ”სებში</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">დáƒáƒ‘ლáƒáƒ™áƒ•áƒ (ნáƒáƒ’ულისხმევი)</translation>
<translation id="4314815835985389558">სინქრáƒáƒœáƒ˜áƒ–áƒáƒªáƒ˜áƒ˜áƒ¡ მáƒáƒ áƒ—ვáƒ</translation>
@@ -1268,6 +1276,7 @@
<translation id="4435702339979719576">ღირბáƒáƒ áƒáƒ—ი)</translation>
<translation id="443673843213245140">პრáƒáƒ¥áƒ¡áƒ˜áƒ¡ გáƒáƒ›áƒáƒ§áƒ”ნებრგáƒáƒ›áƒáƒ áƒ—ულიáƒ, მáƒáƒ’რáƒáƒ› მითითებულირპრáƒáƒ¥áƒ¡áƒ˜áƒ¡ დეტáƒáƒšáƒ£áƒ áƒ˜ კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ.</translation>
<translation id="4441832193888514600">იგნáƒáƒ áƒ˜áƒ áƒ”ბულიáƒ, რáƒáƒ“გáƒáƒœ წესების გáƒáƒœáƒ¡áƒáƒ–ღვრრშესáƒáƒ«áƒšáƒ”ბელირმხáƒáƒšáƒáƒ“ ღრუბლáƒáƒ•áƒáƒœ სáƒáƒ›áƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლრწესების სáƒáƒ®áƒ˜áƒ—.</translation>
+<translation id="4442470707340296952">Chrome-ის ჩáƒáƒœáƒáƒ áƒ—ები</translation>
<translation id="4450893287417543264">áƒáƒ¦áƒáƒ  გáƒáƒ›áƒáƒ©áƒœáƒ“ეს</translation>
<translation id="4451135742916150903">შეუძლირHID-მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ებთáƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბის თხáƒáƒ•áƒœáƒ</translation>
<translation id="4452328064229197696">თქვენ მიერ áƒáƒ®áƒšáƒáƒ®áƒáƒœ გáƒáƒ›áƒáƒ§áƒ”ნებული პáƒáƒ áƒáƒšáƒ˜ ნáƒáƒžáƒáƒ•áƒœáƒ˜áƒ გáƒáƒŸáƒáƒœáƒ˜áƒš მáƒáƒœáƒáƒªáƒ”მებში. თქვენი áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ˜áƒ¡ დáƒáƒªáƒ•áƒ˜áƒ¡ მიზნით, Google პáƒáƒ áƒáƒšáƒ”ბის მმáƒáƒ áƒ—ველი გირჩევთ, შეáƒáƒ›áƒáƒ¬áƒ›áƒáƒ— თქვენი შენáƒáƒ®áƒ£áƒšáƒ˜ პáƒáƒ áƒáƒšáƒ”ბი.</translation>
@@ -1406,6 +1415,7 @@
<translation id="483241715238664915">გáƒáƒ¤áƒ áƒ—ხილებების ჩáƒáƒ áƒ—ვáƒ</translation>
<translation id="4834250788637067901">გáƒáƒ“áƒáƒ®áƒ“ის მეთáƒáƒ“ები, შემáƒáƒ—áƒáƒ•áƒáƒ–ებები დრმისáƒáƒ›áƒáƒ áƒ—ები Google Pay-დáƒáƒœ</translation>
<translation id="4838327282952368871">áƒáƒ áƒáƒáƒ›áƒ¥áƒ•áƒ”ყნიური</translation>
+<translation id="4839087176073128681">გáƒáƒ“áƒáƒ˜áƒ®áƒáƒ“ეთ მáƒáƒ›áƒáƒ•áƒáƒšáƒ¨áƒ˜ უფრრუსáƒáƒ¤áƒ áƒ—ხáƒáƒ“ დრდáƒáƒ˜áƒªáƒáƒ•áƒ˜áƒ— თქვენი ბáƒáƒ áƒáƒ—ი უსáƒáƒ¤áƒ áƒ—ხáƒáƒ”ბის სფერáƒáƒ¨áƒ˜ Google-ის მáƒáƒ¬áƒ˜áƒœáƒáƒ•áƒ” ტექნáƒáƒšáƒáƒ’იების მეშვეáƒáƒ‘ით.</translation>
<translation id="4840250757394056958">თქვენი Chrome-ის ისტáƒáƒ áƒ˜áƒ˜áƒ¡ ნáƒáƒ®áƒ•áƒ</translation>
<translation id="484462545196658690">áƒáƒ•áƒ¢áƒáƒ›áƒáƒ¢áƒ£áƒ áƒ˜</translation>
<translation id="484671803914931257">მიიღეთ ფáƒáƒ¡áƒ“áƒáƒ™áƒšáƒ”ბრ<ph name="MERCHANT_NAME" />-სრდრსხვრáƒáƒ‘იექტებში</translation>
@@ -1468,6 +1478,7 @@
<translation id="5011561501798487822">áƒáƒ¦áƒ›áƒáƒ©áƒ”ნილირენáƒ</translation>
<translation id="5015510746216210676">მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ის სáƒáƒ®áƒ”ლი:</translation>
<translation id="5017554619425969104">თქვენ მიერ კáƒáƒžáƒ˜áƒ áƒ”ბული ტექსტი</translation>
+<translation id="5017828934289857214">მáƒáƒ’ვიáƒáƒœáƒ”ბით შეხსენებáƒ</translation>
<translation id="5018422839182700155">áƒáƒ› გვერდის გáƒáƒ®áƒ¡áƒœáƒ ვერ მáƒáƒ®áƒ”რხდáƒ</translation>
<translation id="5019198164206649151">სáƒáƒ áƒ”ზერვრსáƒáƒªáƒáƒ•áƒ˜ ცუდ მდგáƒáƒ›áƒáƒ áƒ”áƒáƒ‘áƒáƒ¨áƒ˜áƒ</translation>
<translation id="5020776957610079374">მსáƒáƒ¤áƒšáƒ˜áƒ მუსიკáƒ</translation>
@@ -1487,6 +1498,7 @@
<translation id="5051305769747448211">ლáƒáƒ˜áƒ•áƒ™áƒáƒ›áƒ”დიáƒ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{ეს ფáƒáƒ˜áƒšáƒ˜ მáƒáƒ®áƒšáƒáƒ‘ლáƒáƒ“ გáƒáƒ–იáƒáƒ áƒ”ბის მეშვეáƒáƒ‘ით რáƒáƒ› გáƒáƒ’ზáƒáƒ•áƒœáƒáƒ—, გáƒáƒáƒ—áƒáƒ•áƒ˜áƒ¡áƒ£áƒ¤áƒšáƒ”თ სივრცე (<ph name="DISK_SPACE_SIZE" />) თქვენს მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ–ე}other{ეს ფáƒáƒ˜áƒšáƒ”ბი მáƒáƒ®áƒšáƒáƒ‘ლáƒáƒ“ გáƒáƒ–იáƒáƒ áƒ”ბის მეშვეáƒáƒ‘ით რáƒáƒ› გáƒáƒ’ზáƒáƒ•áƒœáƒáƒ—, გáƒáƒáƒ—áƒáƒ•áƒ˜áƒ¡áƒ£áƒ¤áƒšáƒ”თ სივრცე (<ph name="DISK_SPACE_SIZE" />) თქვენს მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ–ე}}</translation>
<translation id="5056549851600133418">თქვენთვის გáƒáƒœáƒ™áƒ£áƒ—ვნილი სტáƒáƒ¢áƒ˜áƒ”ბი</translation>
+<translation id="5060483733937416656">თქვენ áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ Windows Hello-ს მეშვეáƒáƒ‘ით დáƒáƒ“áƒáƒ¡áƒ¢áƒ£áƒ áƒ”ბრვებსáƒáƒ˜áƒ¢áƒ”ბზე, რáƒáƒ›áƒ”ლთრმიერáƒáƒª გáƒáƒ›áƒáƒ˜áƒ§áƒ”ნებრ<ph name="PROVIDER_ORIGIN" />. áƒáƒ› პრáƒáƒ•áƒáƒ˜áƒ“ერს შეიძლებáƒáƒ“რშეენáƒáƒ®áƒ ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ თქვენი გáƒáƒ“áƒáƒ®áƒ“ის მეთáƒáƒ“ის შესáƒáƒ®áƒ”ბ. სურვილისáƒáƒ›áƒ”ბრ, შეგიძლიáƒáƒ— <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" /> ხáƒáƒ› áƒáƒ  იგულისხმეთ?</translation>
<translation id="5066056036849835175">ბეჭდვის ისტáƒáƒ áƒ˜áƒ</translation>
<translation id="5068234115460527047">ჰეჯირებისთვის გáƒáƒ›áƒ˜áƒ–ნული სáƒáƒ®áƒ¡áƒ áƒ”ბი</translation>
@@ -1500,10 +1512,8 @@
<translation id="5087286274860437796">სერვერის áƒáƒ›áƒŸáƒáƒ›áƒ˜áƒœáƒ“ელი სერტიფიკáƒáƒ¢áƒ˜ áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜áƒ.</translation>
<translation id="5087580092889165836">ბáƒáƒ áƒáƒ—ის დáƒáƒ›áƒáƒ¢áƒ”ბáƒ</translation>
<translation id="5088142053160410913">შეტყáƒáƒ‘ინებრáƒáƒžáƒ”რáƒáƒ¢áƒáƒ áƒ˜áƒ¡áƒ—ვის</translation>
-<translation id="5089810972385038852">შტáƒáƒ¢áƒ˜</translation>
<translation id="5093232627742069661">დáƒáƒ™áƒ”ცვრZ-ის ფáƒáƒ áƒ›áƒ˜áƒ—</translation>
<translation id="5094747076828555589">ეს სერვერი ვერ áƒáƒ›áƒ¢áƒ™áƒ˜áƒªáƒ”ბს, რáƒáƒ› ის áƒáƒ áƒ˜áƒ¡ <ph name="DOMAIN" />; მისი უსáƒáƒ¤áƒ áƒ—ხáƒáƒ”ბის სერთიფიკáƒáƒ¢áƒ˜ áƒáƒ  ენდáƒáƒ‘რChromium-ს. ეს შეიძლებრიყáƒáƒ¡ გáƒáƒ›áƒáƒ¬áƒ•áƒ”ული áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜ კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ˜áƒ— áƒáƒœ თáƒáƒ•áƒ“áƒáƒ›áƒ¡áƒ®áƒ›áƒ”ლის მიერ თქვენი კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ გáƒáƒ“áƒáƒ­áƒ áƒ˜áƒ—.</translation>
-<translation id="5095208057601539847">პრáƒáƒ•áƒ˜áƒœáƒªáƒ˜áƒ</translation>
<translation id="5097099694988056070">მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ის სტáƒáƒ¢áƒ˜áƒ¡áƒ¢áƒ˜áƒ™áƒ, რáƒáƒ’áƒáƒ áƒ˜áƒªáƒáƒ CPU/RAM-ის გáƒáƒ›áƒáƒ§áƒ”ნებáƒ</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">სáƒáƒ˜áƒ¢áƒ˜ áƒáƒ  áƒáƒ áƒ˜áƒ¡ დáƒáƒªáƒ£áƒšáƒ˜</translation>
@@ -1541,6 +1551,7 @@
<translation id="5171045022955879922">მáƒáƒ«áƒ”ბნეთ áƒáƒœ áƒáƒ™áƒ áƒ˜áƒ¤áƒ”თ URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">áƒáƒžáƒáƒ áƒáƒ¢áƒ˜</translation>
+<translation id="5177076414499237632">შეიტყვეთ áƒáƒ› გვერდის წყáƒáƒ áƒáƒ¡áƒ დრთემის შესáƒáƒ®áƒ”ბ</translation>
<translation id="5179510805599951267">áƒáƒ  áƒáƒ áƒ˜áƒ¡ <ph name="ORIGINAL_LANGUAGE" />-ში? მáƒáƒáƒ®áƒ¡áƒ”ნეთ áƒáƒ› შეცდáƒáƒ›áƒ˜áƒ¡ შესáƒáƒ®áƒ”ბ</translation>
<translation id="518639307526414276">შინáƒáƒ£áƒ áƒ˜ ცხáƒáƒ•áƒ”ლების სáƒáƒ™áƒ•áƒ”ბი დრმáƒáƒ•áƒšáƒ˜áƒ¡ სáƒáƒ¨áƒ£áƒáƒšáƒ”ბები</translation>
<translation id="5190835502935405962">სáƒáƒœáƒ˜áƒ¨áƒœáƒ”ების ზáƒáƒšáƒ˜</translation>
@@ -1701,6 +1712,7 @@
<translation id="5624120631404540903">პáƒáƒ áƒáƒšáƒ”ბის მáƒáƒ áƒ—ვáƒ</translation>
<translation id="5629630648637658800">წესის პáƒáƒ áƒáƒ›áƒ”ტრები ვერ ჩáƒáƒ˜áƒ¢áƒ•áƒ˜áƒ áƒ—áƒ</translation>
<translation id="5631439013527180824">მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ის áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜ მáƒáƒ áƒ—ვის ჟეტáƒáƒœáƒ˜</translation>
+<translation id="5632485077360054581">ინსტრუქციის ნáƒáƒ®áƒ•áƒ</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />-ზე áƒáƒ›áƒŸáƒáƒ›áƒáƒ“ მყáƒáƒ¤áƒ›áƒ თáƒáƒ•áƒ“áƒáƒ›áƒ¡áƒ®áƒ›áƒ”ლებმრშეიძლებრცáƒáƒ“áƒáƒœ თქვენს კáƒáƒ›áƒžáƒ˜áƒ£áƒ¢áƒ”რზე ისეთი სáƒáƒ®áƒ˜áƒ¤áƒáƒ—რპრáƒáƒ’რáƒáƒ›áƒ”ბის ინსტáƒáƒšáƒáƒªáƒ˜áƒ, რáƒáƒ›áƒšáƒ”ბსáƒáƒª თქვენი ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ (მáƒáƒ’áƒáƒšáƒ˜áƒ—áƒáƒ“, ფáƒáƒ¢áƒáƒ”ბის, პáƒáƒ áƒáƒšáƒ”ბის, შეტყáƒáƒ‘ინებების დრსáƒáƒ™áƒ áƒ”დიტრბáƒáƒ áƒáƒ—ების მáƒáƒœáƒáƒªáƒ”მების) მáƒáƒžáƒáƒ áƒ•áƒ áƒáƒœ წáƒáƒ¨áƒšáƒ შეუძლიáƒ. <ph name="BEGIN_LEARN_MORE_LINK" />შეიტყვეთ მეტი<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">შეცდáƒáƒ›áƒáƒ¨áƒ˜ შემყვáƒáƒœáƒ˜ კáƒáƒœáƒ¢áƒ”ნტი დáƒáƒ‘ლáƒáƒ™áƒ˜áƒšáƒ˜áƒ.</translation>
<translation id="5633259641094592098">სáƒáƒ™áƒ£áƒšáƒ¢áƒ დრინდი ფილმები</translation>
@@ -1818,6 +1830,7 @@
<translation id="5989320800837274978">áƒáƒ  áƒáƒ áƒ˜áƒ¡ მითითებული áƒáƒ áƒª ფიქსირებული პრáƒáƒ¥áƒ¡áƒ˜ სერვერი, áƒáƒ áƒª .pac სკრიპტის URL.</translation>
<translation id="5992691462791905444">სáƒáƒ˜áƒœáƒŸáƒ˜áƒœáƒ áƒ დáƒáƒ™áƒ”ცვრZ-ის ფáƒáƒ áƒ›áƒ˜áƒ—</translation>
<translation id="5995727681868049093">თქვენს Google áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜ თქვენი ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡, კáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ‘ისრდრუსáƒáƒ¤áƒ áƒ—ხáƒáƒ”ბის მáƒáƒ áƒ—ვáƒ</translation>
+<translation id="5997247540087773573">თქვენ მიერ áƒáƒ®áƒšáƒáƒ®áƒáƒœ გáƒáƒ›áƒáƒ§áƒ”ნებული პáƒáƒ áƒáƒšáƒ˜ ნáƒáƒžáƒáƒ•áƒœáƒ˜áƒ გáƒáƒŸáƒáƒœáƒ˜áƒš მáƒáƒœáƒáƒªáƒ”მებში. თქვენი áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ˜áƒ¡ დáƒáƒªáƒ•áƒ˜áƒ¡ მიზნით, Google პáƒáƒ áƒáƒšáƒ”ბის მმáƒáƒ áƒ—ველი გირჩევთ, áƒáƒ®áƒšáƒáƒ•áƒ” შეცვáƒáƒšáƒáƒ— ის დრშეáƒáƒ›áƒáƒ¬áƒ›áƒáƒ— თქვენი შენáƒáƒ®áƒ£áƒšáƒ˜ პáƒáƒ áƒáƒšáƒ”ბი.</translation>
<translation id="6000758707621254961">მáƒáƒ—ხáƒáƒ•áƒœáƒáƒ–ე „<ph name="SEARCH_TEXT" />“ მáƒáƒ˜áƒ«áƒ”ბნრ<ph name="RESULT_COUNT" /> შედეგი</translation>
<translation id="6006484371116297560">კლáƒáƒ¡áƒ˜áƒ™áƒ£áƒ áƒ˜</translation>
<translation id="6008122969617370890">მიმდევრáƒáƒ‘რN-დáƒáƒœ 1-მდე</translation>
@@ -1913,7 +1926,6 @@
<translation id="627746635834430766">შემდგáƒáƒ›áƒ˜ გáƒáƒ“áƒáƒ®áƒ“ების დáƒáƒ¡áƒáƒ©áƒ¥áƒáƒ áƒ”ბლáƒáƒ“ შეგიძლიáƒáƒ— შეინáƒáƒ®áƒáƒ— თქვენი ბáƒáƒ áƒáƒ—ის მáƒáƒœáƒáƒªáƒ”მები დრბილინგის მისáƒáƒ›áƒáƒ áƒ—ი თქვენს Google áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜.</translation>
<translation id="6279183038361895380">კურსáƒáƒ áƒ˜áƒ¡ სáƒáƒ©áƒ•áƒ”ნებლáƒáƒ“, დáƒáƒáƒ­áƒ˜áƒ áƒ”თ კლáƒáƒ•áƒ˜áƒ¨áƒ–ე |<ph name="ACCELERATOR" />|</translation>
<translation id="6280223929691119688">áƒáƒ› მისáƒáƒ›áƒáƒ áƒ—ზე მიწáƒáƒ“ებრვერ მáƒáƒ®áƒ”რხდებáƒ. áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ სხვრმისáƒáƒ›áƒáƒ áƒ—ი.</translation>
-<translation id="6282194474023008486">სáƒáƒ¤áƒáƒ¡áƒ¢áƒ კáƒáƒ“ი</translation>
<translation id="6285507000506177184">Chrome-ის მეშვეáƒáƒ‘ით ჩáƒáƒ›áƒáƒ¢áƒ•áƒ˜áƒ áƒ—ული ფáƒáƒ˜áƒšáƒ”ბის მáƒáƒ áƒ—ვის ღილáƒáƒ™áƒ˜, Chrome-ის მეშვეáƒáƒ‘ით ჩáƒáƒ›áƒáƒ¢áƒ•áƒ˜áƒ áƒ—ული ფáƒáƒ˜áƒšáƒ”ბის სáƒáƒ›áƒáƒ áƒ—áƒáƒ•áƒáƒ“ დáƒáƒáƒ­áƒ˜áƒ áƒ”თ კლáƒáƒ•áƒ˜áƒ¨áƒ¡ Enter</translation>
<translation id="6289939620939689042">გვერდის ფერი</translation>
<translation id="6290238015253830360">áƒáƒ¥ გáƒáƒ›áƒáƒ©áƒœáƒ“ებრთქვენთვის შემáƒáƒ—áƒáƒ•áƒáƒ–ებული სტáƒáƒ¢áƒ˜áƒ”ბი</translation>
@@ -1935,6 +1947,7 @@
<translation id="6337133576188860026">გáƒáƒ›áƒáƒ—áƒáƒ•áƒ˜áƒ¡áƒ£áƒ¤áƒšáƒ“ებრ<ph name="SIZE" /> áƒáƒœ ნáƒáƒ™áƒšáƒ”ბი. ზáƒáƒ’იერთი სáƒáƒ˜áƒ¢áƒ˜ შემდეგი მáƒáƒœáƒáƒ®áƒ£áƒšáƒ”ბისáƒáƒ¡ შეიძლებრუფრრნელრჩáƒáƒ˜áƒ¢áƒ•áƒ˜áƒ áƒ—áƒáƒ¡.</translation>
<translation id="6337534724793800597">ფილტრის წესები სáƒáƒ®áƒ”ლის მიხედვით</translation>
<translation id="6340739886198108203">áƒáƒ“მინისტრáƒáƒ¢áƒáƒ áƒ˜áƒ¡ წესების მიხედვით, ეკრáƒáƒœáƒ˜áƒ¡ áƒáƒœáƒáƒ‘ეჭდების áƒáƒœ ჩáƒáƒœáƒáƒ¬áƒ”რების გáƒáƒ™áƒ”თებრáƒáƒ  áƒáƒ áƒ˜áƒ¡ რეკáƒáƒ›áƒ”ნდებული, რáƒáƒªáƒ ხილულირკáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒ˜ კáƒáƒœáƒ¢áƒ”ნტი:</translation>
+<translation id="6348220984832452017">áƒáƒ¥áƒ¢áƒ˜áƒ£áƒ áƒ˜ ვáƒáƒ áƒ˜áƒáƒªáƒ˜áƒ”ბი</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" />-ის ინსტáƒáƒšáƒáƒªáƒ˜áƒ</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{áƒáƒ áƒªáƒ”რთი}=1{1 პáƒáƒ áƒáƒšáƒ˜ (<ph name="DOMAIN_LIST" />-ისთვის, სინქრáƒáƒœáƒ˜áƒ–ებული)}=2{2 პáƒáƒ áƒáƒšáƒ˜ (<ph name="DOMAIN_LIST" />-ისთვის, სინქრáƒáƒœáƒ˜áƒ–ებული)}other{# პáƒáƒ áƒáƒšáƒ˜ (<ph name="DOMAIN_LIST" />-ისთვის, სინქრáƒáƒœáƒ˜áƒ–ებული)}}</translation>
<translation id="6355392890578844978">áƒáƒ› ბრáƒáƒ£áƒ–ერს áƒáƒ  მáƒáƒ áƒ—áƒáƒ•áƒ¡ კáƒáƒ›áƒžáƒáƒœáƒ˜áƒ დრáƒáƒ áƒª სხვრáƒáƒ áƒ’áƒáƒœáƒ˜áƒ–áƒáƒªáƒ˜áƒ. áƒáƒ› მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ–ე áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘რშეიძლებრიმáƒáƒ áƒ—ებáƒáƒ“ეს Chromium-ს მიღმáƒ. <ph name="BEGIN_LINK" />შეიტყვეთ მეტი<ph name="END_LINK" /></translation>
@@ -1966,7 +1979,6 @@
<translation id="643051589346665201">Google პáƒáƒ áƒáƒšáƒ˜áƒ¡ შეცვლáƒ</translation>
<translation id="6433490469411711332">სáƒáƒ™áƒáƒœáƒ¢áƒáƒ¥áƒ¢áƒ ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ რედáƒáƒ¥áƒ¢áƒ˜áƒ áƒ”ბáƒ</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" />-მრუáƒáƒ áƒ§áƒ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბáƒ.</translation>
-<translation id="6438025220577812695">მე თვითáƒáƒœ შევცვლი</translation>
<translation id="6440503408713884761">იგნáƒáƒ áƒ˜áƒ áƒ”ბული</translation>
<translation id="6443406338865242315">თქვენ მიერ დáƒáƒ˜áƒœáƒ¡áƒ¢áƒáƒšáƒ˜áƒ áƒ”ბული გáƒáƒ¤áƒáƒ áƒ—áƒáƒ”ბები დრდáƒáƒœáƒáƒ›áƒáƒ¢áƒ”ბი</translation>
<translation id="6446163441502663861">Kahu (კáƒáƒœáƒ•áƒ”რტი)</translation>
@@ -2096,9 +2108,9 @@
<translation id="6828866289116430505">გენეტიკáƒ</translation>
<translation id="6831043979455480757">თáƒáƒ áƒ’მნáƒ</translation>
<translation id="6833752742582340615">უსáƒáƒ¤áƒ áƒ—ხრდრსწრáƒáƒ¤áƒ˜ áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¡áƒ¬áƒáƒ áƒ”ბისთვის შეინáƒáƒ®áƒ”თ თქვენი ბáƒáƒ áƒáƒ—ისრდრბილინგის ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ თქვენს Google áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜</translation>
-<translation id="6839929833149231406">რეგიáƒáƒœáƒ˜</translation>
<translation id="6846340164947227603">ვირტუáƒáƒšáƒ£áƒ áƒ˜ ბáƒáƒ áƒáƒ—ის ნáƒáƒ›áƒ áƒ˜áƒ¡ გáƒáƒ›áƒáƒ§áƒ”ნებáƒâ€¦</translation>
<translation id="6852204201400771460">გსურთ áƒáƒžáƒ˜áƒ¡ გáƒáƒ“áƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვáƒ?</translation>
+<translation id="6857776781123259569">პáƒáƒ áƒáƒšáƒ”ბის მáƒáƒ áƒ—ვáƒâ€¦</translation>
<translation id="686485648936420384">სáƒáƒ›áƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლრრესურსები</translation>
<translation id="6865412394715372076">áƒáƒ› ბáƒáƒ áƒáƒ—ის დáƒáƒ“áƒáƒ¡áƒ¢áƒ£áƒ áƒ”ბრáƒáƒ›áƒŸáƒáƒ›áƒáƒ“ ვერ მáƒáƒ®áƒ”რხდებáƒ</translation>
<translation id="6869334554832814367">პერსáƒáƒœáƒáƒšáƒ£áƒ áƒ˜ სესხები</translation>
@@ -2147,7 +2159,6 @@
<translation id="6965978654500191972">მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒ</translation>
<translation id="696703987787944103">პერცეფციული</translation>
<translation id="6968269510885595029">თქვენი უსáƒáƒ¤áƒ áƒ—ხáƒáƒ”ბის გáƒáƒ¡áƒáƒ¦áƒ”ბის გáƒáƒ›áƒáƒ§áƒ”ნებáƒ</translation>
-<translation id="6970216967273061347">áƒáƒšáƒ¥áƒ˜</translation>
<translation id="6971439137020188025">Slides-ში áƒáƒ®áƒáƒšáƒ˜ Google პრეზენტáƒáƒªáƒ˜áƒ˜áƒ¡ სწრáƒáƒ¤áƒáƒ“ შექმნáƒ</translation>
<translation id="6972629891077993081">HID მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ები</translation>
<translation id="6973656660372572881">ფიქსირებული პრáƒáƒ¥áƒ¡áƒ˜ სერვერები დრ.pac სკრიპტის URL მითითებულიáƒ.</translation>
@@ -2186,7 +2197,6 @@
<translation id="7081308185095828845">ეს ფუნქცირმიუწვდáƒáƒ›áƒ”ლირთქვენს მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒáƒ–ე</translation>
<translation id="7083258188081898530">ლáƒáƒœáƒ’áƒáƒ áƒ˜ 9</translation>
<translation id="7086090958708083563">áƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვრმáƒáƒ—ხáƒáƒ•áƒœáƒ˜áƒšáƒ˜áƒ მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლის მიერ</translation>
-<translation id="7087282848513945231">სáƒáƒ’რáƒáƒ¤áƒ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, ნებáƒáƒ áƒ—ვებისრდრსხვáƒáƒ“áƒáƒ¡áƒ®áƒ•áƒ სáƒáƒ˜áƒ¢áƒ–ე შენáƒáƒ®áƒ£áƒšáƒ˜ მáƒáƒœáƒáƒªáƒ”მების Chrome-ის პáƒáƒ áƒáƒ›áƒ”ტრებიდáƒáƒœ სáƒáƒ›áƒáƒ áƒ—áƒáƒ•áƒáƒ“ დáƒáƒáƒ­áƒ˜áƒ áƒ”თ კლáƒáƒ•áƒ˜áƒ¨áƒ¡ Tab, შემდეგ კი Enter-ს</translation>
<translation id="7096937462164235847">áƒáƒ› ვებსáƒáƒ˜áƒ¢áƒ˜áƒ¡ სáƒáƒ˜áƒ“ენტიფიკáƒáƒªáƒ˜áƒ მáƒáƒœáƒáƒªáƒ”მები შემáƒáƒ¬áƒ›áƒ”ბული áƒáƒ  áƒáƒ áƒ˜áƒ¡.</translation>
<translation id="7101893872976785596">სáƒáƒ¨áƒ˜áƒœáƒ”ლებáƒáƒ—რფილმები</translation>
@@ -2205,10 +2215,10 @@
<ph name="LIST_ITEM" />ფáƒáƒ áƒ›áƒ”ბში მითითებული ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">áƒáƒ› მისáƒáƒ›áƒáƒ áƒ—ზე გáƒáƒ’ზáƒáƒ•áƒœáƒ ვერ მáƒáƒ®áƒ”რხდებáƒ. áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ სხვრმისáƒáƒ›áƒáƒ áƒ—ი.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">თქვენი áƒáƒ“მინისტრáƒáƒ¢áƒáƒ áƒ˜ კრძáƒáƒšáƒáƒ•áƒ¡ áƒáƒ› მáƒáƒœáƒáƒªáƒ”მთრკáƒáƒžáƒ˜áƒ áƒ”ბáƒáƒ¡.</translation>
<translation id="7135130955892390533">სტáƒáƒ¢áƒ£áƒ¡áƒ˜áƒ¡ ჩვენებáƒ</translation>
<translation id="7138472120740807366">მიწáƒáƒ“ების მეთáƒáƒ“ი</translation>
-<translation id="7139724024395191329">ემირáƒáƒ¢áƒ˜</translation>
<translation id="7139892792842608322">ძირითáƒáƒ“ი ლáƒáƒœáƒ’áƒáƒ áƒ˜</translation>
<translation id="714064300541049402">მხáƒáƒ áƒ” 2 — სურáƒáƒ—ის წáƒáƒœáƒáƒªáƒ•áƒšáƒ”ბრX ღერძზე</translation>
<translation id="7152423860607593928">Number-14 (კáƒáƒœáƒ•áƒ”რტი)</translation>
@@ -2425,6 +2435,7 @@
<translation id="7669271284792375604">თáƒáƒ•áƒ“áƒáƒ›áƒ¡áƒ®áƒ›áƒ”ლებმრáƒáƒ› სáƒáƒ˜áƒ¢áƒ–ე შეიძლებრმáƒáƒ¢áƒ§áƒ£áƒ”ბით დáƒáƒ’áƒáƒ˜áƒœáƒ¡áƒ¢áƒáƒšáƒ˜áƒ áƒ”ბინáƒáƒœ პრáƒáƒ’რáƒáƒ›áƒ”ბი, რáƒáƒ›áƒšáƒ”ბიც უáƒáƒ áƒ§áƒáƒ¤áƒ˜áƒ—áƒáƒ“ იმáƒáƒ¥áƒ›áƒ”დებს ვების დáƒáƒ—ვáƒáƒšáƒ˜áƒ”რების პრáƒáƒªáƒ”სზე (მáƒáƒ’áƒáƒšáƒ˜áƒ—áƒáƒ“, შეიძლებრშეიცვáƒáƒšáƒáƒ¡ თქვენი მთáƒáƒ•áƒáƒ áƒ˜ გვერდი áƒáƒœ თქვენ მიერ მáƒáƒœáƒáƒ®áƒ£áƒšáƒ”ბულ სáƒáƒ˜áƒ¢áƒ”ბზე რეკლáƒáƒ›áƒ გáƒáƒ›áƒáƒ©áƒœáƒ“ეს).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{კáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ“ მáƒáƒœáƒ˜áƒ¨áƒœáƒ£áƒš მáƒáƒœáƒáƒªáƒ”მებთáƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბით გáƒáƒœáƒ®áƒáƒ áƒªáƒ˜áƒ”ლებული ქმედებები (შესვლის შემდეგ გáƒáƒœáƒ®áƒáƒ áƒªáƒ˜áƒ”ლდრ1 ქმედებáƒ). <ph name="BEGIN_LINK" />შეიტყვეთ მეტი<ph name="END_LINK" />}other{კáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ“ მáƒáƒœáƒ˜áƒ¨áƒœáƒ£áƒš მáƒáƒœáƒáƒªáƒ”მებთáƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბით გáƒáƒœáƒ®áƒáƒ áƒªáƒ˜áƒ”ლებული ქმედებები (შესვლის შემდეგ გáƒáƒœáƒ®áƒáƒ áƒªáƒ˜áƒ”ლდრ# ქმედებáƒ). <ph name="BEGIN_LINK" />შეიტყვეთ მეტი<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">სáƒáƒ¤áƒáƒ¡áƒ¢áƒ ყუთი 6</translation>
+<translation id="7675325315208090829">გáƒáƒ“áƒáƒ®áƒ“ის მეთáƒáƒ“ების მáƒáƒ áƒ—ვáƒâ€¦</translation>
<translation id="7676643023259824263">გáƒáƒªáƒ•áƒšáƒ˜áƒ¡ ბუფერში ტექსტის ძიებáƒ, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">თქვენი დáƒáƒ—ვáƒáƒšáƒ˜áƒ”რების ისტáƒáƒ áƒ˜áƒ˜áƒ¡ ნáƒáƒ®áƒ•áƒ დრმáƒáƒ áƒ—ვრChrome პáƒáƒ áƒáƒ›áƒ”ტრებიდáƒáƒœ</translation>
<translation id="7679947978757153706">ბეისბáƒáƒšáƒ˜</translation>
@@ -2467,7 +2478,6 @@
<translation id="7766518757692125295">სáƒáƒ¤áƒáƒ áƒ•áƒ”ლი</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">იგივე მიმდევრáƒáƒ‘რნáƒáƒ‘ეჭდი მხáƒáƒ áƒ˜áƒ— áƒáƒ¦áƒ›áƒ</translation>
-<translation id="777702478322588152">პრეფექტურáƒ</translation>
<translation id="7791011319128895129">გáƒáƒ›áƒáƒ£áƒªáƒ”მელი</translation>
<translation id="7791196057686275387">შეფუთვáƒ</translation>
<translation id="7791543448312431591">დáƒáƒ›áƒáƒ¢áƒ”ბáƒ</translation>
@@ -2558,12 +2568,12 @@
<translation id="8055534648776115597">პრáƒáƒ¤áƒ”სიული დრუწყვეტი გáƒáƒœáƒáƒ—ლებáƒ</translation>
<translation id="8057711352706143257">„<ph name="SOFTWARE_NAME" />“ áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒáƒ“ áƒáƒ áƒ˜áƒ¡ კáƒáƒœáƒ¤áƒ˜áƒ’ურირებული. რáƒáƒ’áƒáƒ áƒª წესი, პრáƒáƒ‘ლემის მáƒáƒ’ვáƒáƒ áƒ”ბáƒáƒ¨áƒ˜ უნდრდáƒáƒ’ეხმáƒáƒ áƒáƒ— „<ph name="SOFTWARE_NAME" />“-ის დეინსტáƒáƒšáƒáƒªáƒ˜áƒ. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">კვების მრეწველáƒáƒ‘áƒ</translation>
-<translation id="8066955247577885446">სáƒáƒ›áƒ¬áƒ£áƒ®áƒáƒ áƒáƒ“, წáƒáƒ áƒ›áƒáƒ˜áƒ¥áƒ›áƒœáƒ შეფერხებáƒ.</translation>
<translation id="8067872629359326442">თქვენ ეს-ესáƒáƒ შეიყვáƒáƒœáƒ”თ პáƒáƒ áƒáƒšáƒ˜ შეცდáƒáƒ›áƒáƒ¨áƒ˜ შემყვáƒáƒœ სáƒáƒ˜áƒ¢áƒ–ე. Chromium-ს შეუძლირთქვენი დáƒáƒ®áƒ›áƒáƒ áƒ”ბáƒ. პáƒáƒ áƒáƒšáƒ˜áƒ¡ შესáƒáƒªáƒ•áƒšáƒ”ლáƒáƒ“ დრთქვენი áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ˜áƒ¡ სáƒáƒ¤áƒ áƒ—ხეში ყáƒáƒ¤áƒœáƒ˜áƒ¡ შესáƒáƒ®áƒ”ბ Google-ის სáƒáƒ¥áƒ›áƒ˜áƒ¡ კურსში ჩáƒáƒ¡áƒáƒ§áƒ”ნებლáƒáƒ“, დáƒáƒ¬áƒ™áƒáƒžáƒ£áƒœáƒ”ბით áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ „áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ˜áƒ¡ დáƒáƒªáƒ•áƒâ€œ.</translation>
<translation id="8070439594494267500">áƒáƒžáƒ˜áƒ¡ ხáƒáƒ¢áƒ£áƒšáƒ</translation>
<translation id="8074253406171541171">10x13 (კáƒáƒœáƒ•áƒ”რტი)</translation>
<translation id="8075736640322370409">áƒáƒ®áƒáƒšáƒ˜ Google Sheet-ის სწრáƒáƒ¤áƒáƒ“ შექმნáƒ</translation>
<translation id="8075898834294118863">სáƒáƒ˜áƒ¢áƒ”ბის პáƒáƒ áƒáƒ›áƒ”ტრების მáƒáƒ áƒ—ვáƒ</translation>
+<translation id="8076492880354921740">ჩáƒáƒœáƒáƒ áƒ—ები</translation>
<translation id="8078141288243656252">შეტრიáƒáƒšáƒ”ბულ მდგáƒáƒ›áƒáƒ áƒ”áƒáƒ‘áƒáƒ¨áƒ˜ áƒáƒœáƒáƒ¢áƒ˜áƒ áƒ”ბრვერ მáƒáƒ®áƒ”რხდებáƒ</translation>
<translation id="8079031581361219619">გსურთ სáƒáƒ˜áƒ¢áƒ˜áƒ¡ ხელáƒáƒ®áƒšáƒ ჩáƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვáƒ?</translation>
<translation id="8081087320434522107">სედáƒáƒœáƒ”ბი</translation>
@@ -2686,6 +2696,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">პáƒáƒ áƒáƒ›áƒ”ტრები</translation>
+<translation id="8428634594422941299">გáƒáƒ¡áƒáƒ’ებიáƒ</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, ქუქი-ჩáƒáƒœáƒáƒ¬áƒ”რების პáƒáƒ áƒáƒ›áƒ”ტრების Chrome-ის პáƒáƒ áƒáƒ›áƒ”ტრებიდáƒáƒœ სáƒáƒ›áƒáƒ áƒ—áƒáƒ•áƒáƒ“ დáƒáƒáƒ­áƒ˜áƒ áƒ”თ კლáƒáƒ•áƒ˜áƒ¨áƒ¡ Tab, შემდეგ კი Enter-ს</translation>
<translation id="8433057134996913067">áƒáƒ› მáƒáƒ¥áƒ›áƒ”დებით ვებსáƒáƒ˜áƒ¢áƒ”ბის უმეტესáƒáƒ‘იდáƒáƒœ გáƒáƒ›áƒáƒ®áƒ•áƒáƒšáƒ—.</translation>
<translation id="8434840396568290395">შინáƒáƒ£áƒ áƒ˜ ცხáƒáƒ•áƒ”ლები</translation>
@@ -2783,6 +2794,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> áƒáƒ áƒ˜áƒ¡ თქვენი კáƒáƒ“ი <ph name="ORIGIN" />-ისთვის</translation>
<translation id="874918643257405732">áƒáƒ› ჩáƒáƒœáƒáƒ áƒ—ის სáƒáƒœáƒ˜áƒ¨áƒœáƒ”ებში დáƒáƒ›áƒáƒ¢áƒ”ბáƒ</translation>
<translation id="8751426954251315517">გთხáƒáƒ•áƒ—, ცáƒáƒ“áƒáƒ— სხვრდრáƒáƒ¡</translation>
+<translation id="8757526089434340176">Google Pay შემáƒáƒ—áƒáƒ•áƒáƒ–ებáƒ</translation>
<translation id="8758885506338294482">ვიდეáƒáƒ—áƒáƒ›áƒáƒ¨áƒ”ბი შეჯიბრების პრინციპით</translation>
<translation id="8759274551635299824">ეს ბáƒáƒ áƒáƒ—ი ვáƒáƒ“áƒáƒ’áƒáƒ¡áƒ£áƒšáƒ˜áƒ</translation>
<translation id="87601671197631245">ეს სáƒáƒ˜áƒ¢áƒ˜ იყენებს უსáƒáƒ¤áƒ áƒ—ხáƒáƒ”ბის მáƒáƒ«áƒ•áƒ”ლებულ კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒáƒ¡, რáƒáƒ›áƒ”ლმáƒáƒª შეიძლებრგáƒáƒáƒ›áƒŸáƒ¦áƒáƒ•áƒœáƒáƒ¡ მისთვის გáƒáƒ’ზáƒáƒ•áƒœáƒ˜áƒšáƒ˜ თქვენი ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ (მáƒáƒ’áƒáƒšáƒ˜áƒ—áƒáƒ“, პáƒáƒ áƒáƒšáƒ”ბი, შეტყáƒáƒ‘ინებები áƒáƒœ სáƒáƒ™áƒ áƒ”დიტრბáƒáƒ áƒáƒ—ების მáƒáƒœáƒáƒªáƒ”მები).</translation>
@@ -2790,6 +2802,7 @@
<translation id="8763927697961133303">USB მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘áƒ</translation>
<translation id="8763986294015493060">áƒáƒ›áƒŸáƒáƒ›áƒáƒ“ გáƒáƒ®áƒ¡áƒœáƒ˜áƒšáƒ˜ ყველრინკáƒáƒ’ნიტრფáƒáƒœáƒ¯áƒ áƒ˜áƒ¡ დáƒáƒ®áƒ£áƒ áƒ•áƒ</translation>
<translation id="8766943070169463815">გáƒáƒ˜áƒ®áƒ¡áƒœáƒ უსáƒáƒ¤áƒ áƒ—ხრგáƒáƒ“áƒáƒ®áƒ“ის áƒáƒ•áƒ¢áƒáƒ áƒ˜áƒ–áƒáƒªáƒ˜áƒ˜áƒ¡ ფურცელი</translation>
+<translation id="8767765348545497220">დáƒáƒ›áƒ®áƒ›áƒáƒ áƒ” ბუშტის დáƒáƒ®áƒ£áƒ áƒ•áƒ</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">მáƒáƒ¢áƒáƒªáƒ˜áƒ™áƒšáƒ”ბი</translation>
<translation id="8790007591277257123">წáƒáƒ¨áƒšáƒ˜áƒ¡ &amp;გáƒáƒ›áƒ”áƒáƒ áƒ”ბáƒ</translation>
@@ -2802,6 +2815,7 @@
<translation id="8806285662264631610">სáƒáƒáƒ‘áƒáƒ–áƒáƒœáƒáƒ¡ დრტáƒáƒœáƒ˜áƒ¡ პრáƒáƒ“უქციáƒ</translation>
<translation id="8807160976559152894">შემáƒáƒ­áƒ áƒ თითáƒáƒ”ული გვერდის შემდეგ</translation>
<translation id="8808828119384186784">Chrome-ის პáƒáƒ áƒáƒ›áƒ”ტრები</translation>
+<translation id="8813277370772331957">მáƒáƒ’ვიáƒáƒœáƒ”ბით შეხსენებáƒ</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome-ის პáƒáƒ áƒáƒ›áƒ”ტრებიდáƒáƒœ Chrome-ის გáƒáƒ¡áƒáƒáƒ®áƒšáƒ”ბლáƒáƒ“ დáƒáƒáƒ­áƒ˜áƒ áƒ”თ კლáƒáƒ•áƒ˜áƒ¨áƒ¡ Tab, შემდეგ კი Enter-ს</translation>
<translation id="8820817407110198400">სáƒáƒœáƒ˜áƒ¨áƒœáƒ”ები</translation>
<translation id="882338992931677877">ინდივიდუáƒáƒšáƒ£áƒ áƒ˜ სáƒáƒ—áƒáƒ•áƒ¡áƒ</translation>
@@ -2981,6 +2995,7 @@
<translation id="988159990683914416">დეველáƒáƒžáƒ”რის კáƒáƒœáƒ¡áƒ¢áƒ áƒ£áƒ¥áƒªáƒ˜áƒ</translation>
<translation id="989988560359834682">მისáƒáƒ›áƒáƒ áƒ—ის რედáƒáƒ¥áƒ¢áƒ˜áƒ áƒ”ბáƒ</translation>
<translation id="991413375315957741">მáƒáƒ«áƒ áƒáƒáƒ‘ის áƒáƒœ გáƒáƒœáƒáƒ—ების სენსáƒáƒ áƒ”ბი</translation>
+<translation id="992110854164447044">ვირტუáƒáƒšáƒ£áƒ áƒ˜ ბáƒáƒ áƒáƒ—ი დáƒáƒ›áƒáƒšáƒáƒ•áƒ¡ თქვენს რეáƒáƒšáƒ£áƒ  ბáƒáƒ áƒáƒ—ს, რáƒáƒª დáƒáƒ’იცáƒáƒ•áƒ— თáƒáƒ¦áƒšáƒ˜áƒ—áƒáƒ‘ის პáƒáƒ¢áƒ”ნციური შემთხვევებისგáƒáƒœ. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ვáƒáƒ áƒ“ისფერი</translation>
<translation id="992432478773561401">„<ph name="SOFTWARE_NAME" />“ áƒáƒ áƒáƒ¡áƒáƒ—áƒáƒœáƒáƒ“áƒáƒ“ დáƒáƒ˜áƒœáƒ¡áƒ¢áƒáƒšáƒ˜áƒ áƒ“რთქვენს კáƒáƒ›áƒžáƒ˜áƒ£áƒ¢áƒ”რზე áƒáƒœ ქსელში:
diff --git a/chromium/components/strings/components_strings_kk.xtb b/chromium/components/strings/components_strings_kk.xtb
index 533aed73218..83f215864c9 100644
--- a/chromium/components/strings/components_strings_kk.xtb
+++ b/chromium/components/strings/components_strings_kk.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Гранттар, шәкіртақылар және қаржылай көмек</translation>
<translation id="1048785276086539861">ÐннотациÑларды өзгерткен кезде, бұл құжат бір беттік көрініÑке ауыÑады.</translation>
<translation id="1050038467049342496">БаÑқа қолданбаларды жабу</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> қызметін пайдаланатын веб-Ñайттарда аутентификациÑлау құрылғыÑÑ‹ арқылы раÑтау нұÑқаÑын таңдадыңыз. Бұл провайдерде төлеу әдіÑіңіз туралы Ñақталған ақпарат болуы мүмкін, оны <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;ҚоÑуды болдырмау</translation>
<translation id="1056663316309890343">ФотоÑуретке арналған бағдарламалық құрал</translation>
<translation id="1056898198331236512">ЕÑкерту</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">Таңдау әдіÑÑ–</translation>
<translation id="1281476433249504884">1-жинаÑтырушы</translation>
<translation id="1285320974508926690">Бұл Ñайтты ешқашан аудармау</translation>
+<translation id="1288548991597756084">Картаңызды қауіпÑіз Ñақтаңыз</translation>
<translation id="1292571435393770077">16-науа</translation>
<translation id="1292701964462482250">"Компьютердегі бағдарламалық құрал Chrome жүйеÑінің интернетке қауіпÑіз қоÑылуына жол бермей тұр" (тек Windows компьютерлерінде)</translation>
<translation id="1294154142200295408">Пәрмендер жолының нұÑқалары</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Қатені түзету үшін ашқыңыз келетін бетте &lt;strong&gt;ҚоÑылу&lt;/strong&gt; түймеÑін баÑыңыз.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Ландшафт дизайны</translation>
<translation id="1513706915089223971">Тарихтағы жазбалар тізімі</translation>
+<translation id="1516097932025103760">Ол шифрланады, қауіпÑіз Ñақталады. CVC ешқашан Ñақталмайды.</translation>
<translation id="1517433312004943670">Телефон нөмірі қажет</translation>
<translation id="1519264250979466059">ЖаÑалған күні</translation>
<translation id="1521159554480556801">Тоқыма және текÑтиль өнері</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Төлеу әдіÑтерін Ñақтау және толтыру</translation>
<translation id="1663943134801823270">Карталар мен мекенжайлар Chrome браузерінен алынған. Оларды <ph name="BEGIN_LINK" />Параметрлер<ph name="END_LINK" /> бөлімінде баÑқара алаÑыз.</translation>
<translation id="1671391448414634642"><ph name="SOURCE_LANGUAGE" /> тіліндегі беттер <ph name="TARGET_LANGUAGE" /> тіліне аударылатын болады.</translation>
+<translation id="1673886523110456987">Ò°ÑыныÑÑ‚Ñ‹ пайдалану үшін <ph name="CARD_DETAIL" /> картаÑымен төлеңіз.</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> тілінен <ph name="TARGET_LANGUAGE" /> тіліне</translation>
<translation id="1682696192498422849">Ðлдымен қыÑқа беті</translation>
<translation id="168693727862418163">Бұл ÑаÑÑат мәні оÑÑ‹ Ñхемаға қатыÑÑ‚Ñ‹ текÑерілмеді және ол еленбейді.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">ÒšÐ¾Ð·Ò“Ð°Ð»Ñ‹Ñ Ð´Ð°Ñ‚Ñ‡Ð¸ÐºÑ‚ÐµÑ€Ñ–</translation>
<translation id="1717494416764505390">3-ші пошта жәшігі</translation>
<translation id="1718029547804390981">ÐÐ½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ Ò›Ð¾Ñуға құжат өлшемі өте үлкен.</translation>
+<translation id="1720941539803966190">Оқулықты жабу</translation>
<translation id="1721424275792716183">* Ó¨Ñ€Ñ–Ñ Ñ‚Ð¾Ð»Ñ‚Ñ‹Ñ€Ñ‹Ð»ÑƒÑ‹ қажет</translation>
<translation id="1727613060316725209">Сертификат жарамды</translation>
<translation id="1727741090716970331">Ð”Ò±Ñ€Ñ‹Ñ ÐºÐ°Ñ€Ñ‚Ð° нөмірін енгізу</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">Барбекю және гриль</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> тіліндегі беттер аударылмайды.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Бұл баÑқару Ñлементі қоÑулы болÑа, Chrome қызметі Ñоңғы браузерді қолдану мәліметіңіз барынша Ñай келетін адамдар тобын немеÑе "когортаны" анықтайды. Жарнама берушілер топ үшін жарнамаларды таңдай алады және браузерді қолдану мәліметі құрылғыда Ò›Ò±Ð¿Ð¸Ñ Ñақталады. Тобыңыз күн Ñайын жаңартылады.}=1{Бұл баÑқару Ñлементі қоÑулы болÑа, Chrome қызметі Ñоңғы браузерді қолдану мәліметіңіз барынша Ñай келетін адамдар тобын немеÑе "когортаны" анықтайды. Жарнама берушілер топ үшін жарнамаларды таңдай алады және браузерді қолдану мәліметі құрылғыда Ò›Ò±Ð¿Ð¸Ñ Ñақталады. Тобыңыз күн Ñайын жаңартылады.}other{Бұл баÑқару Ñлементі қоÑулы болÑа, Chrome қызметі Ñоңғы браузерді қолдану мәліметіңіз барынша Ñай келетін адамдар тобын немеÑе "когортаны" анықтайды. Жарнама берушілер топ үшін жарнамаларды таңдай алады және браузерді қолдану мәліметі құрылғыда Ò›Ò±Ð¿Ð¸Ñ Ñақталады. Тобыңыз әр {NUM_DAYS} күн Ñайын жаңартылады.}}</translation>
-<translation id="2053553514270667976">Пошта индекÑÑ–</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 Ò±ÑыныÑ}other{# Ò±ÑыныÑ}}</translation>
+<translation id="2066915425250589881">жоюды Ñұрауыңызға болады</translation>
<translation id="2068528718802935086">ÐәреÑтелер мен Ñәбилер</translation>
<translation id="2071156619270205202">Бұл нөмір виртуалдық карта нөмірі үшін жарамÑыз.</translation>
<translation id="2071692954027939183">Сіз әдетте хабарландыруларға Ñ€Ò±Ò›Ñат бермейтін болғандықтан, олар автоматты түрде бөгелді.</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">"Синхрондауды баÑқару" түймеÑÑ–. Chrome параметрлеріне кіріп, Ñинхрондалатын ақпаратты баÑқару үшін Enter пернеÑін баÑыңыз.</translation>
<translation id="2091887806945687916">ДыбыÑ</translation>
<translation id="2094505752054353250">Домен ÑәйкеÑÑіздігі</translation>
-<translation id="2096368010154057602">Департамент</translation>
<translation id="2099652385553570808">Сол жағын үш рет қапÑыру</translation>
<translation id="2101225219012730419">ÐÒ±Ñқа:</translation>
<translation id="2102134110707549001">Күрделі Ò›Ò±Ð¿Ð¸Ñ Ñөз жаÑау…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">Ðмерикалық футбол</translation>
<translation id="2187317261103489799">Ðнықтау (әдепкі)</translation>
<translation id="2188375229972301266">Төменгі жағын бірнеше рет теÑу</translation>
-<translation id="2188852899391513400">Сіз жаңа ғана қолданған Ò›Ò±Ð¿Ð¸Ñ Ñөз деректердің қолды болуы Ñалдарынан Ð¶Ð°Ñ€Ð¸Ñ ÐµÑ‚Ñ–Ð»Ð´Ñ–. Ðккаунттарыңызды қорғау үшін Google ÒšÒ±Ð¿Ð¸Ñ Ñөздер реттегіші оны дереу өзгертуді және Ñақталған Ò›Ò±Ð¿Ð¸Ñ Ñөздеріңізді текÑеруді Ò±Ñынады.</translation>
<translation id="219906046732893612">Тұрғын үйлерді жақÑарту</translation>
<translation id="2202020181578195191">Ð”Ò±Ñ€Ñ‹Ñ Ð¶Ð°Ñ€Ð°Ð¼Ð´Ñ‹Ð»Ñ‹Ò› мерзімі аÑқталатын жылды енгізіңіз</translation>
<translation id="22081806969704220">3-науа</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">Файлды өңдеу</translation>
<translation id="2215963164070968490">Иттер</translation>
<translation id="2218879909401188352">Ðғымдағы <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> Ñайтындағы шабуылдаушылар құрылғыңызға зақым келтіретін қауіпті қолданбаларды орнатуы, мобильді шотқа жаÑырын ақыларды қоÑуы немеÑе жеке ақпаратты ұрлауы мүмкін. <ph name="BEGIN_LEARN_MORE_LINK" />Толығырақ<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Оқулықты қайта баÑтау</translation>
+<translation id="2219735899272417925">Құрылғыны баÑтапқы күйге қайтару қажет.</translation>
<translation id="2224337661447660594">Интернет жоқ</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />ДиагноÑтика қолданбаÑÑ‹<ph name="END_LINK" /> көмегімен байланыÑÑ‚Ñ‹ жөндеңіз</translation>
<translation id="2239100178324503013">Қазір жіберу</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">РұқÑат етілмеген (әдепкі)</translation>
<translation id="2512413427717747692">Chrome-ды әдепкі браузер түймеÑÑ– етіп орнату: Chrome-ды iOS параметрлерінде жүйенің әдепкі браузері етіп орнату үшін Enter пернеÑін баÑыңыз.</translation>
<translation id="2515629240566999685">Ðймағыңыздағы Ñигналды текÑеру</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> қызметін пайдаланатын веб-Ñайттарда Touch ID арқылы раÑтау нұÑқаÑын таңдадыңыз. Бұл провайдерде төлеу әдіÑіңіз туралы Ñақталған ақпарат болуы мүмкін, оны <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Төменгі оң жағын қапÑыру</translation>
<translation id="2521736961081452453">Үлгі жаÑау</translation>
<translation id="2523886232349826891">Тек оÑÑ‹ құрылғыда Ñақталады</translation>
<translation id="2524461107774643265">ҚоÑымша ақпарат енгізу</translation>
<translation id="2529899080962247600">Бұл өріÑтегі жазбалардың макÑималды Ñаны: <ph name="MAX_ITEMS_LIMIT" />. Қалған жазбалар еленбейтін болады.</translation>
+<translation id="253493526287553278">Промокод туралы мәліметтерді көру</translation>
<translation id="2535585790302968248">ÒšÒ±Ð¿Ð¸Ñ ÑˆÐ°Ñ€Ð»Ð°Ñƒ үшін жаңа инкогнито қойындыÑын ашыңыз.</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{және тағы 1}other{және тағы #}}</translation>
<translation id="2536110899380797252">Мекенжай енгізу</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">ФотоÑурет және цифрлық кеÑкін</translation>
<translation id="2601150049980261779">Романтикалық фильмдер</translation>
<translation id="2604589665489080024">Поп-музыка</translation>
-<translation id="2609632851001447353">ÐÒ±Ñқалар</translation>
<translation id="2610561535971892504">БаÑып көшіру</translation>
<translation id="2617988307566202237">Chrome келеÑÑ– ақпаратты <ph name="BEGIN_EMPHASIS" />Ñақтамайды<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Туған күн және еÑім күні</translation>
<translation id="2677748264148917807">Шығу</translation>
+<translation id="2679714844901977852">ҚауіпÑіз әрі жылдамырақ төлеу үшін карта мен төлем туралы ақпаратты Google аккаунтыңызға (<ph name="USER_EMAIL" />) Ñақтаңыз.</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Ең оңтайлы</translation>
<translation id="2688969097326701645">Иә, жалғаÑтырамын</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">Интернет провайдерлері</translation>
<translation id="2856444702002559011"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> Ñайтындағы шабуылдаушылар ақпаратыңызды (мыÑалы, Ò›Ò±Ð¿Ð¸Ñ Ñөздер, хабарлар немеÑе неÑиелік карталар) ұрлауға әрекет етуі мүмкін.<ph name="BEGIN_LEARN_MORE_LINK" />Толығырақ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Бұл Ñайтта мазалайтын немеÑе жалған ақпаратты жарнамалар көрÑетіледі.</translation>
-<translation id="286512204874376891">Виртуалдық карта қолданыÑтағы картаны алаÑқтықтан қорғауға көмектеÑеді. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ÐÒ› ниетті</translation>
<translation id="28761159517501904">Фильмдер</translation>
<translation id="2876489322757410363">Сыртқы қолданба арқылы төлеу үшін инкогнито режимінен шығаÑыз. ЖалғаÑтыру керек пе?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ДиÑк</translation>
<translation id="3162559335345991374">Сіз пайдаланып жатқан Wi-Fi өзінің кіру бетіне өтуіңізді қажет етуі мүмкін.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ðрал</translation>
<translation id="3176929007561373547">ПрокÑи Ñерверінің Ð¶Ò±Ð¼Ñ‹Ñ Ñ–Ñтеп жатқанын текÑеру үшін прокÑи параметрлерін
текÑеріңіз немеÑе желі әкімшіÑіне хабарлаÑыңыз. ПрокÑи Ñервері қажет ÐµÐ¼ÐµÑ Ð´ÐµÐ¿ ойлаÑаңыз:
<ph name="PLATFORM_TEXT" /></translation>
@@ -880,9 +885,6 @@
<translation id="3369192424181595722">Сағат қатеÑÑ–</translation>
<translation id="3369459162151165748">Ðвтобөлшектер мен керек-жарақтар</translation>
<translation id="3371064404604898522">Chrome-ды әдепкі браузер етіп орнату</translation>
-<translation id="3371076217486966826"><ph name="URL" /> үшін келеÑÑ– Ñ€Ò±Ò›Ñаттар қажет:
- • айналаңыздың 3D картаÑын жаÑау және камера қалпын қадағалау;
- • камераны пайдалану.</translation>
<translation id="337363190475750230">Өшірілді</translation>
<translation id="3375754925484257129">Chrome қауіпÑіздік текÑеріÑін Ñ–Ñке қоÑу</translation>
<translation id="3377144306166885718">Сервер еÑкірген TLS нұÑқаÑын пайдаланған.</translation>
@@ -899,6 +901,7 @@
<translation id="3399952811970034796">Жеткізу мекенжайы</translation>
<translation id="3402261774528610252">Бұл Ñайтты жүктеуге қолданылған Ð±Ð°Ð¹Ð»Ð°Ð½Ñ‹Ñ Ò¯ÑˆÑ–Ð½ еÑкірген және алдағы уақытта өшірілетін TLS 1.0 немеÑе TLS 1.1 протоколы пайдаланылған. Ол өшірілгеннен кейін, пайдаланушылар бұл Ñайтқа кіре алмайды. Серверде TLS 1.2 протоколы немеÑе одан кейінгі нұÑқаÑÑ‹ қоÑылуы тиіÑ.</translation>
<translation id="3405664148539009465">Қаріптерді реттеу</translation>
+<translation id="3407789382767355356">Үшінші тарап арқылы кіру</translation>
<translation id="3409896703495473338">ҚауіпÑіздік параметрлерін баÑқару</translation>
<translation id="3414952576877147120">Көлемі:</translation>
<translation id="3417660076059365994">Сіз жүктеп Ñалған немеÑе тіркеген файлдар Google Cloud қызметіне немеÑе баÑқа қызметтерге жіберіледі және талданады. МыÑалы, онда Ò›Ò±Ð¿Ð¸Ñ Ð´ÐµÑ€ÐµÐºÑ‚ÐµÑ€Ð´Ñ–Ò£ немеÑе зиÑнды бағдарламалардың бар-жоғы текÑеріледі.</translation>
@@ -931,6 +934,7 @@
<translation id="3477679029130949506">Фильм тізімдері және кинотеатрдан көрÑету кеÑтеÑÑ–</translation>
<translation id="3479552764303398839">Қазір емеÑ</translation>
<translation id="3484560055331845446">Google аккаунтыңызға кіре алмай қалуыңыз мүмкін. Chrome браузері Ò›Ò±Ð¿Ð¸Ñ Ñөзді қазір өзгертуге ÐºÐµÒ£ÐµÑ Ð±ÐµÑ€ÐµÐ´Ñ–. Сонан Ñоң аккаунтқа кіру Ñұралады.</translation>
+<translation id="3484861421501147767">ЕÑке Ñалғыш: Ñақталған промокодыңыз бар</translation>
<translation id="3487845404393360112">4-науа</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" />
бетінен табу</translation>
@@ -1055,6 +1059,7 @@
<translation id="3810973564298564668">БаÑқару</translation>
<translation id="3816482573645936981">Мән (ауыÑтырылған)</translation>
<translation id="382518646247711829">ПрокÑи Ñерверді пайдаланÑаңыз…</translation>
+<translation id="3826050100957962900">Үшінші тараптың кіруі</translation>
<translation id="3827112369919217609">ÐбÑолютті</translation>
<translation id="3827666161959873541">ОтбаÑылық фильмдер</translation>
<translation id="3828924085048779000">ÒšÒ±Ð¿Ð¸Ñ Ñ„Ñ€Ð°Ð·Ð° Ð±Ð¾Ñ Ð±Ð¾Ð»Ð¼Ð°ÑƒÑ‹ керек.</translation>
@@ -1067,9 +1072,9 @@
<translation id="3858027520442213535">Күні мен уақытын жаңарту</translation>
<translation id="3858860766373142691">ÐÑ‚Ñ‹</translation>
<translation id="3872834068356954457">Ғылым</translation>
+<translation id="3875783148670536197">Орындалуын көру</translation>
<translation id="3881478300875776315">Жолдарды азырақ көрÑету</translation>
<translation id="3884278016824448484">Мұндай құрылғы идентификаторы бұрыннан бар</translation>
-<translation id="3885155851504623709">Округ</translation>
<translation id="388632593194507180">Бақыланып жатқаны анықталды</translation>
<translation id="3886948180919384617">3-жинаÑтырушы</translation>
<translation id="3890664840433101773">Электрондық пошта мекенжайын енгізу</translation>
@@ -1099,9 +1104,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> бөгелген</translation>
<translation id="3978338123949022456">Іздеу режимі: <ph name="KEYWORD_SUFFIX" /> Ñөзі арқылы іздеу үшін Ñұрауды жазып, Enter пернеÑін баÑыңыз.</translation>
<translation id="398470910934384994">ÒšÒ±Ñтар</translation>
+<translation id="3985750352229496475">Мекенжайларды баÑқару...</translation>
<translation id="3986705137476756801">Live Caption функциÑÑын қазір өшіру</translation>
<translation id="3987940399970879459">1 МБ-тан аз</translation>
<translation id="3990250421422698716">Бірқалыпты Ñ‹Ò“Ñ‹Ñу</translation>
+<translation id="3992684624889376114">ОÑÑ‹ бет туралы ақпарат</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> Ñайты өзінің барлық Ñұрауына түпнұÑқа ÑаÑÑатын
қолдануын талап етті, бірақ бұл ÑаÑÑатты дәл қазір қолдану мүмкін емеÑ.</translation>
<translation id="4006465311664329701">Google Pay қызметіндегі төлеу әдіÑтері, Ò±ÑыныÑтар және мекенжайлар</translation>
@@ -1226,6 +1233,7 @@
<translation id="4305666528087210886">Файлға кіру мүмкін болмады</translation>
<translation id="4306529830550717874">Мекенжайды Ñақтау керек пе?</translation>
<translation id="4306812610847412719">буфер</translation>
+<translation id="4310070645992025887">СаÑхаттарыңызды іздеңіз</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Бөгеу (әдепкі)</translation>
<translation id="4314815835985389558">Синхрондауды баÑқару</translation>
@@ -1276,6 +1284,7 @@
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">ПрокÑиді пайдалану өшірілген, бірақ айқын прокÑи конфигурациÑÑÑ‹ көрÑетілген.</translation>
<translation id="4441832193888514600">Еленбеді, Ñебебі ÑаÑÑат бұлт пайдаланушыÑÑ‹ ÑаÑÑаты ретінде ғана орнатылады.</translation>
+<translation id="4442470707340296952">Chrome қойындылары</translation>
<translation id="4450893287417543264">Қайта көрÑетілмеÑін</translation>
<translation id="4451135742916150903">HID құрылғыларына қоÑылуға Ñ€Ò±Ò›Ñат Ñұрайды</translation>
<translation id="4452328064229197696">Сіз жаңа ғана қолданған Ò›Ò±Ð¿Ð¸Ñ Ñөз деректердің қолды болуы Ñалдарынан Ð¶Ð°Ñ€Ð¸Ñ ÐµÑ‚Ñ–Ð»Ð´Ñ–. Ðккаунттарыңызды қорғау үшін Google ÒšÒ±Ð¿Ð¸Ñ Ñөздер реттегіші Ñақталған Ò›Ò±Ð¿Ð¸Ñ Ñөздеріңізді текÑеруді Ò±Ñынады.</translation>
@@ -1414,6 +1423,7 @@
<translation id="483241715238664915">Хабарландыруларды қоÑу</translation>
<translation id="4834250788637067901">Google Pay қызметіндегі төлеу әдіÑтері, Ò±ÑыныÑтар және мекенжайлар</translation>
<translation id="4838327282952368871">Ðрманшыл</translation>
+<translation id="4839087176073128681">Google-дың үздік қауіпÑіздік жүйеÑімен картаңызды қорғаңыз және жылдамырақ төлеңіз.</translation>
<translation id="4840250757394056958">Chrome тарихын көру</translation>
<translation id="484462545196658690">Ðвто</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> және т. б. дүкендердің жеңілдіктерін алыңыз</translation>
@@ -1476,6 +1486,7 @@
<translation id="5011561501798487822">Ðнықталған тіл</translation>
<translation id="5015510746216210676">Құрылғы атауы:</translation>
<translation id="5017554619425969104">Көшірілген мәтін</translation>
+<translation id="5017828934289857214">Кейінірек еÑке Ñалу</translation>
<translation id="5018422839182700155">Бұл бет ашылмайды</translation>
<translation id="5019198164206649151">Қоймадағы деректер бүлінген</translation>
<translation id="5020776957610079374">Әлемдік музыка</translation>
@@ -1495,6 +1506,7 @@
<translation id="5051305769747448211">Жанды комедиÑ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Бұл файлды Nearby Share арқылы жіберу үшін құрылғыңыздан орын (<ph name="DISK_SPACE_SIZE" />) боÑатыңыз.}other{Бұл файлдарды Nearby Share арқылы жіберу үшін құрылғыңыздан орын (<ph name="DISK_SPACE_SIZE" />) боÑатыңыз.}}</translation>
<translation id="5056549851600133418">Сізге арналған мақалалар</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> қызметін пайдаланатын веб-Ñайттарда Windows Hello арқылы раÑтау нұÑқаÑын таңдадыңыз. Бұл провайдерде төлеу әдіÑіңіз туралы Ñақталған ақпарат болуы мүмкін, оны <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" /> деп жазғыңыз келді ме?</translation>
<translation id="5066056036849835175">БаÑып шығару тарихы</translation>
<translation id="5068234115460527047">Хедж қорлары</translation>
@@ -1508,10 +1520,8 @@
<translation id="5087286274860437796">Сервердің Ñертификаты әзірше жарамайды.</translation>
<translation id="5087580092889165836">ÐеÑие картаÑын қоÑу</translation>
<translation id="5088142053160410913">Операторға жіберілетін хабарлама</translation>
-<translation id="5089810972385038852">Штат</translation>
<translation id="5093232627742069661">Z тәрізді бүктеу</translation>
<translation id="5094747076828555589">Бұл Ñервер өзінің <ph name="DOMAIN" /> екенін дәлелдей алмады; оның қауіпÑіздік Ñертификатына Chromium Ñенім артпайды. Бұған қате ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð½ÐµÐ¼ÐµÑе қаÑкүнемнің байланыÑÑ‚Ñ‹ тоқтатуы Ñебеп болуы мүмкін.</translation>
-<translation id="5095208057601539847">ПровинциÑ</translation>
<translation id="5097099694988056070">CPU/RAM пайдалануы ÑиÑқты құрылғы ÑтатиÑтикаÑÑ‹</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Сайт қауіпÑіз емеÑ</translation>
@@ -1549,6 +1559,7 @@
<translation id="5171045022955879922">Іздеңіз не URL теріңіз</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Машина</translation>
+<translation id="5177076414499237632">ОÑÑ‹ беттің дереккөзі мен тақырыбы туралы толығырақ ақпарат</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> тілінде ÐµÐ¼ÐµÑ Ð¿Ðµ? Бұл қате туралы хабарлау</translation>
<translation id="518639307526414276">Үй жануарларының азығы және оларды күту өнімдері</translation>
<translation id="5190835502935405962">Бетбелгілер жолағы</translation>
@@ -1709,6 +1720,7 @@
<translation id="5624120631404540903">ÒšÒ±Ð¿Ð¸Ñ Ñөздерді баÑқару</translation>
<translation id="5629630648637658800">СаÑÑат параметрлерін жүктеу ÑәтÑіз аÑқталды</translation>
<translation id="5631439013527180824">Құрылғының баÑқару таңбалауышы жарамÑыз</translation>
+<translation id="5632485077360054581">Орындалуын көру</translation>
<translation id="5633066919399395251">Қазір <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> Ñайтындағы шабуылдаушылар ақпаратыңызды (мыÑалы, фотоÑуреттер, Ò›Ò±Ð¿Ð¸Ñ Ñөздер, хабарлар және неÑиелік карталар) ұрлайтын не жоÑтын қауіпті бағдарламаларды компьютеріңізде орнатуға әрекет етуі мүмкін. <ph name="BEGIN_LEARN_MORE_LINK" />Толығырақ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Жалған мазмұн бөгелді.</translation>
<translation id="5633259641094592098">Культтік және инди фильмдер</translation>
@@ -1826,6 +1838,7 @@
<translation id="5989320800837274978">Бекітілген прокÑи Ñерверлері де, .pac Ñценарий URL мекенжайы да көрÑетілмеген.</translation>
<translation id="5992691462791905444">Z тәрізді етіп жиі бүктеу</translation>
<translation id="5995727681868049093">Google аккаунтыңыздағы ақпаратты, құпиÑлылықты және қауіпÑіздікті баÑқару</translation>
+<translation id="5997247540087773573">Сіз жаңа ғана қолданған Ò›Ò±Ð¿Ð¸Ñ Ñөз деректердің қолды болуы Ñалдарынан Ð¶Ð°Ñ€Ð¸Ñ ÐµÑ‚Ñ–Ð»Ð´Ñ–. Ðккаунттарыңызды қорғау үшін Google ÒšÒ±Ð¿Ð¸Ñ Ñөз менеджері оны дереу өзгертуді және Ñақталған Ò›Ò±Ð¿Ð¸Ñ Ñөздеріңізді текÑеруді Ò±Ñынады.</translation>
<translation id="6000758707621254961">"<ph name="SEARCH_TEXT" />" Ñұрауы бойынша <ph name="RESULT_COUNT" /> нәтиже шықты</translation>
<translation id="6006484371116297560">КлаÑÑикалық</translation>
<translation id="6008122969617370890">N баÑтап 1 дейінгі ретпен</translation>
@@ -1921,7 +1934,6 @@
<translation id="627746635834430766">КелеÑіде жылдамырақ төлеу үшін картаны және төлем мекенжайын Google аккаунтына Ñақтаңыз.</translation>
<translation id="6279183038361895380">КурÑорды көрÑету үшін |<ph name="ACCELERATOR" />| баÑыңыз</translation>
<translation id="6280223929691119688">Бұл мекенжайға жеткізілмейді. БаÑқа мекенжайды таңдаңыз.</translation>
-<translation id="6282194474023008486">Пошта индекÑÑ–</translation>
<translation id="6285507000506177184">"Chrome браузерінде жүктеп алынған файлдарды баÑқару" түймеÑÑ–. Chrome браузерінде жүктеп алған файлдарыңызды баÑқару үшін Enter пернеÑін баÑыңыз.</translation>
<translation id="6289939620939689042">Бет Ñ‚Ò¯ÑÑ–</translation>
<translation id="6290238015253830360">Сізге Ò±Ñынылған мақалалар оÑÑ‹ жерге шығады.</translation>
@@ -1943,6 +1955,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> аз орын боÑатады .Кейбір Ñайттар келеÑÑ– кіргенде баÑуырақ жүктелуі мүмкін.</translation>
<translation id="6337534724793800597">Ðтауы бойынша Ñүзгі ÑаÑÑаттары</translation>
<translation id="6340739886198108203">Әкімші ÑаÑÑаты Ò›Ò±Ð¿Ð¸Ñ Ð¼Ð°Ð·Ð¼Ò±Ð½ көрÑетілген кезде, Ñкриншот жаÑауды немеÑе жазуды Ò±Ñынбайды.</translation>
+<translation id="6348220984832452017">БелÑенді нұÑқалар</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> қолданбаÑын орнату</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Жоқ}=1{1 Ò›Ò±Ð¿Ð¸Ñ Ñөз (<ph name="DOMAIN_LIST" />, Ñинхрондалды)}=2{2 Ò›Ò±Ð¿Ð¸Ñ Ñөз (<ph name="DOMAIN_LIST" />, Ñинхрондалды)}other{# Ò›Ò±Ð¿Ð¸Ñ Ñөз (<ph name="DOMAIN_LIST" />, Ñинхрондалды)}}</translation>
<translation id="6355392890578844978">Бұл браузер ÐºÐ¾Ð¼Ð¿Ð°Ð½Ð¸Ñ Ð½ÐµÐ¼ÐµÑе баÑқа ұйым арқылы баÑқарылмайды. Құрылғыдағы әрекет Chromium браузерінен Ñ‚Ñ‹Ñ Ð±Ð°Ñқарылуы мүмкін. <ph name="BEGIN_LINK" />Толығырақ<ph name="END_LINK" /></translation>
@@ -1974,7 +1987,6 @@
<translation id="643051589346665201">Google Ò›Ò±Ð¿Ð¸Ñ Ñөзін өзгерту</translation>
<translation id="6433490469411711332">Ð‘Ð°Ð¹Ð»Ð°Ð½Ñ‹Ñ Ð°Ò›Ð¿Ð°Ñ€Ð°Ñ‚Ñ‹Ð½ өңдеу</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> байланыÑтан Ð±Ð°Ñ Ñ‚Ð°Ñ€Ñ‚Ñ‚Ñ‹.</translation>
-<translation id="6438025220577812695">Өзім өзгертемін</translation>
<translation id="6440503408713884761">Еленбейді</translation>
<translation id="6443406338865242315">қай кеңейтімдер мен плагиндерді орнатқаныңызды;</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2104,9 +2116,9 @@
<translation id="6828866289116430505">Генетика</translation>
<translation id="6831043979455480757">Ðудару</translation>
<translation id="6833752742582340615">ҚауіпÑіз әрі жылдамырақ төлеу үшін карта мен төлем туралы ақпаратты Google аккаунтына Ñақтаңыз</translation>
-<translation id="6839929833149231406">Ðудан</translation>
<translation id="6846340164947227603">Виртуалдық карта нөмірін пайдалану...</translation>
<translation id="6852204201400771460">Қолданба қайта жүктелÑін бе?</translation>
+<translation id="6857776781123259569">ÒšÒ±Ð¿Ð¸Ñ Ñөздерді баÑқару...</translation>
<translation id="686485648936420384">Тұтыну реÑурÑтары</translation>
<translation id="6865412394715372076">Бұл картаны дәл қазір раÑтау мүмкін емеÑ</translation>
<translation id="6869334554832814367">Тұтынушылық неÑиелер</translation>
@@ -2155,7 +2167,6 @@
<translation id="6965978654500191972">Құрылғы</translation>
<translation id="696703987787944103">ПерцепциÑлық</translation>
<translation id="6968269510885595029">ҚауіпÑіздік кілтін пайдаланыңыз</translation>
-<translation id="6970216967273061347">Ðудан</translation>
<translation id="6971439137020188025">Slides-да жаңа Google презентациÑÑын жылдам жаÑау</translation>
<translation id="6972629891077993081">HID құрылғылары</translation>
<translation id="6973656660372572881">Бекітілген прокÑи Ñерверлері мен .pac Ñценарий URL мекенжайы көрÑетілген.</translation>
@@ -2194,7 +2205,6 @@
<translation id="7081308185095828845">Бұл мүмкіндік құрылғыда жоқ.</translation>
<translation id="7083258188081898530">9-науа</translation>
<translation id="7086090958708083563">Пайдаланушы жүктеп Ñалуды Ñұрады.</translation>
-<translation id="7087282848513945231">Округ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. Chrome параметрлеріне кіріп, Ñайттарға қатыÑÑ‚Ñ‹ Ñақталған Ñ€Ò±Ò›Ñаттар мен деректерді баÑқару үшін Tab, ÑоÑын Enter пернелерін баÑыңыз.</translation>
<translation id="7096937462164235847">Бұл веб-Ñайттың идентификациÑÑÑ‹ раÑталмаған.</translation>
<translation id="7101893872976785596">Қорқынышты фильмдер</translation>
@@ -2213,10 +2223,10 @@
<ph name="LIST_ITEM" />үлгілерге енгізілген ақпарат.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Бұл мекенжайға жөнелтілмейді. БаÑқа мекенжайды таңдаңыз.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Әкімші бұл деректің көшірілуіне тыйым Ñалды.</translation>
<translation id="7135130955892390533">Күйін көрÑету</translation>
<translation id="7138472120740807366">Жеткізу әдіÑÑ–</translation>
-<translation id="7139724024395191329">Әмірлік</translation>
<translation id="7139892792842608322">Ðегізгі науа</translation>
<translation id="714064300541049402">КеÑкіннің Ð¥ оÑÑ– бойынша 2-жағында Ñ‹Ò“Ñ‹Ñуы</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2433,6 +2443,7 @@
<translation id="7669271284792375604">ОÑÑ‹ Ñайттағы шабуылдаушылар шолу әрекетіңізге зиÑн келтіретін бағдарламаларды айлакерлікпен (мыÑалы, баÑÑ‚Ñ‹ бетіңізді өзгерту немеÑе Ñіз кірген Ñайттарда қоÑымша жарнамалар көрÑету арқылы) орнатқызуы мүмкін.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ÒšÒ±Ð¿Ð¸Ñ Ð´ÐµÐ¿ белгіленген деректерге қатыÑÑ‚Ñ‹ қолданылған әрекеттер (кіргелі бері 1 әрекет). <ph name="BEGIN_LINK" />Толығырақ<ph name="END_LINK" />}other{ÒšÒ±Ð¿Ð¸Ñ Ð´ÐµÐ¿ белгіленген деректерге қатыÑÑ‚Ñ‹ қолданылған әрекеттер (кіргелі бері # әрекет). <ph name="BEGIN_LINK" />Толығырақ<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">6-шы пошта жәшігі</translation>
+<translation id="7675325315208090829">Төлеу әдіÑтерін баÑқару…</translation>
<translation id="7676643023259824263">Буфер мәтінін (<ph name="TEXT" />) іздеу</translation>
<translation id="7679367271685653708">Chrome параметрлерінен браузерді қолдану тарихын көру және баÑқару</translation>
<translation id="7679947978757153706">БейÑбол</translation>
@@ -2475,7 +2486,6 @@
<translation id="7766518757692125295">Шеті</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Бірдей ретпен беткі жағында</translation>
-<translation id="777702478322588152">Префектура</translation>
<translation id="7791011319128895129">Шығарылмаған</translation>
<translation id="7791196057686275387">Қабаттау</translation>
<translation id="7791543448312431591">ҚоÑу</translation>
@@ -2566,12 +2576,12 @@
<translation id="8055534648776115597">КәÑіби және үздікÑіз білім беру</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" Ð´Ò±Ñ€Ñ‹Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñланған. Әдетте "<ph name="SOFTWARE_NAME" />" жойылғанда, мәÑеле шешіледі. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Тамақ өндіру</translation>
-<translation id="8066955247577885446">Кешіріңіз, бірдеңе Ð´Ò±Ñ€Ñ‹Ñ Ð±Ð¾Ð»Ð¼Ð°Ð´Ñ‹.</translation>
<translation id="8067872629359326442">Жаңа ғана Ò›Ò±Ð¿Ð¸Ñ Ñөзіңізді алаÑқтық Ñайтқа енгіздіңіз. Chromium көмектеÑе алады. ÒšÒ±Ð¿Ð¸Ñ Ñөзді өзгертіп, Google-ға аккаунтқа төніп тұрған қауіп туралы хабарлау үшін "Ðккаунтты қорғау" түймеÑін баÑыңыз.</translation>
<translation id="8070439594494267500">Қолданба белгішеÑÑ–</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Жаңа Google парағын жылдам жаÑау</translation>
<translation id="8075898834294118863">Сайт параметрлерін баÑқару</translation>
+<translation id="8076492880354921740">Қойындылар</translation>
<translation id="8078141288243656252">Құжат бұрылып тұрғанда, Ð°Ð½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ Ò›Ð¾Ñу мүмкін емеÑ.</translation>
<translation id="8079031581361219619">Сайтты қайта жүктегіңіз келе ме?</translation>
<translation id="8081087320434522107">Седандар</translation>
@@ -2694,6 +2704,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Параметрлер</translation>
+<translation id="8428634594422941299">ТүÑінікті</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. Chrome параметрлеріне кіріп, cookie файлдарын баÑқару үшін Tab, ÑоÑын Enter пернелерін баÑыңыз.</translation>
<translation id="8433057134996913067">Бұл — көп веб-Ñайттардан шығарады.</translation>
<translation id="8434840396568290395">Үй жануарлары</translation>
@@ -2791,6 +2802,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> – <ph name="ORIGIN" /> Ñайтына арналған код</translation>
<translation id="874918643257405732">ОÑÑ‹ қойындыға бетбелгі қойыңыз</translation>
<translation id="8751426954251315517">КелеÑÑ– жолы қайталап көріңіз.</translation>
+<translation id="8757526089434340176">Google Pay Ò±ÑыныÑÑ‹ қолжетімді</translation>
<translation id="8758885506338294482">СайыÑатын бейне ойындар</translation>
<translation id="8759274551635299824">Картаның мерзімі біткен</translation>
<translation id="87601671197631245">Бұл Ñайттың қауіпÑіздік конфигурациÑÑÑ‹ еÑкірген. Бұл Ñайтқа жіберілетін жеке ақпаратыңыз (мыÑалы, Ò›Ò±Ð¿Ð¸Ñ Ñөздер, хабарлар не неÑиелік карта нөмірлері) Ð¶Ð°Ñ€Ð¸Ñ ÐµÑ‚Ñ–Ð»ÑƒÑ– мүмкін.</translation>
@@ -2798,6 +2810,7 @@
<translation id="8763927697961133303">USB құрылғыÑÑ‹</translation>
<translation id="8763986294015493060">Қазір ашық тұрған инкогнито терезелерінің барлығын жабу</translation>
<translation id="8766943070169463815">ҚауіпÑіз төлем деректерін аутентификациÑлау парағы ашылды.</translation>
+<translation id="8767765348545497220">Қалқымалы анықтаманы жабу</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Мотоциклдер</translation>
<translation id="8790007591277257123">&amp;Жоюды қайталау</translation>
@@ -2810,6 +2823,7 @@
<translation id="8806285662264631610">Ванна мен денеге арналған өнімдер</translation>
<translation id="8807160976559152894">Әр беттен кейін кеÑу</translation>
<translation id="8808828119384186784">Chrome параметрлері</translation>
+<translation id="8813277370772331957">Кейінірек еÑке Ñалу</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Chrome параметрлерінде Chrome-ды жаңарту үшін Tab, одан кейін Enter пернеÑін баÑыңыз.</translation>
<translation id="8820817407110198400">Бетбелгілер</translation>
<translation id="882338992931677877">Қолмен беру науаÑÑ‹</translation>
@@ -2989,6 +3003,7 @@
<translation id="988159990683914416">Әзірлеуші жаÑақтамаÑÑ‹</translation>
<translation id="989988560359834682">Мекенжайды өзгерту</translation>
<translation id="991413375315957741">Ò›Ð¾Ð·Ò“Ð°Ð»Ñ‹Ñ Ð½Ðµ жарық датчиктері</translation>
+<translation id="992110854164447044">Виртуалдық карта Ñізді ықтимал алаÑқтықтан қорғау үшін қолданыÑтағы картаңызды жаÑырады. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Қызғылт</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" компьютерде не желіде Ð´Ò±Ñ€Ñ‹Ñ Ð¾Ñ€Ð½Ð°Ñ‚Ñ‹Ð»Ð¼Ð°Ò“Ð°Ð½:
diff --git a/chromium/components/strings/components_strings_km.xtb b/chromium/components/strings/components_strings_km.xtb
index c0ef4e94728..0abbea7c346 100644
--- a/chromium/components/strings/components_strings_km.xtb
+++ b/chromium/components/strings/components_strings_km.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ជំនួយផ្នែក​ហិរញ្ញវážáŸ’ážáž» ជំនួយ​ឥážážŸáŸ†ážŽáž„ និង​អាហារូបករណáŸ</translation>
<translation id="1048785276086539861">នៅពáŸáž›â€‹áž¢áŸ’នក​កែចំណារ ឯកសារនáŸáŸ‡â€‹áž“ឹងážáŸ’រឡប់ទៅ​ការមើល​ទំពáŸážšâ€‹áž‘ោលវិញ</translation>
<translation id="1050038467049342496">បិទ​កម្មវិធី​ផ្សáŸáž„ទៀáž</translation>
+<translation id="1053959602163383901">អ្នក​បាន​ជ្រើសរើស​ផ្ទៀងផ្ទាážáŸ‹â€‹ážŠáŸ„យប្រើ​ឧបករណáŸâ€‹ážŠáŸ‚ល​បាន​ភ្ជាប់​នឹង​កម្មវិធី​ផ្ទៀងផ្ទាážáŸ‹â€‹áž“ៅលើ​គáŸáž áž‘ំពáŸážšâ€‹ážŠáŸ‚ល​ប្រើប្រាស់ <ph name="PROVIDER_ORIGIN" />។ ក្រុមហ៊ុន​ផ្ដល់​សáŸážœáž¶â€‹áž“áŸáŸ‡â€‹áž”្រហែលជា​បានរក្សាទុក​ពáŸážáŸŒáž˜áž¶áž“​អំពី​វិធីបង់ប្រាក់​របស់អ្នក ដែល​អ្នក​អាច​<ph name="LINK_TEXT" />។</translation>
<translation id="1055184225775184556">បកក្រោយការបន្ážáŸ‚ម</translation>
<translation id="1056663316309890343">កម្មវិធី​រូបážáž</translation>
<translation id="1056898198331236512">ការព្រមាន</translation>
@@ -120,6 +121,7 @@
<translation id="1270502636509132238">មធ្យោបាយ​ទៅយក</translation>
<translation id="1281476433249504884">ទម្រគំនរ​ទី 1</translation>
<translation id="1285320974508926690">មិនបកប្រែគáŸáž áž‘ំពáŸážšáž“áŸáŸ‡áž‘ៀážáž¡áž¾áž™</translation>
+<translation id="1288548991597756084">រក្សាទុកកាážáž”្រកប​ដោយសុវážáŸ’ážáž·áž—ាព</translation>
<translation id="1292571435393770077">ទម្រទី 16</translation>
<translation id="1292701964462482250">"កម្មវិធី​នៅលើ​កុំព្យូទáŸážšâ€‹ážšáž”ស់អ្នក​កំពុង​បញ្ឈប់ Chrome មិន​ឱ្យ​ភ្ជាប់​ទៅ​អ៊ីនធឺណិážâ€‹â€‹ážŠáŸ„យសុវážáŸ’ážáž·áž—ាព" (កុំព្យូទáŸážšâ€‹ Windows ážáŸ‚​ប៉ុណ្ណោះ)</translation>
<translation id="1294154142200295408">អážáŸážšâ€‹áž“ៃ​អážáŸ’ážáž”ទ​បញ្ជា</translation>
@@ -224,6 +226,7 @@
&lt;p&gt;ដើម្បី​ដោះ​ស្រាយ​បញ្ហានáŸáŸ‡ សូមចុច &lt;strong&gt;ភ្ជាប់&lt;/strong&gt; នៅលើ​ទំពáŸážšâ€‹ážŠáŸ‚ល​អ្នក​កំពុង​ព្យាយាម​បើក។&lt;/p&gt;</translation>
<translation id="1507780850870535225">ការរចនាទáŸážŸáž—ាព</translation>
<translation id="1513706915089223971">បញ្ជី​ធាážáž»áž”្រវážáŸ’ážáž·</translation>
+<translation id="1516097932025103760">វានឹងážáŸ’រូវបានអ៊ីនគ្រីប និង​រក្សាទុកប្រកប​ដោយសុវážáŸ’ážáž·áž—ាព ហើយ CVC មិនážáŸ’រូវបានរក្សាទុកទáŸáŸ”</translation>
<translation id="1517433312004943670">ážáž˜áŸ’រូវឲ្យមានលáŸážáž‘ូរសáŸáž–្ទ</translation>
<translation id="1519264250979466059">កាលបរិច្ឆáŸáž‘áž›áŸážáž€áŸ†ážŽáŸ‚</translation>
<translation id="1521159554480556801">សិល្បៈ​សរសៃអំបោះ និង​វាយនភណ្ឌ</translation>
@@ -289,6 +292,7 @@
<translation id="1662550410081243962">រក្សាទុក និងបំពáŸáž‰â€‹ážœáž·áž’ីបង់ប្រាក់</translation>
<translation id="1663943134801823270">បណ្ណ និងអាសយដ្ឋានគឺបានមកពី Chrome ។ អ្នកអាចគ្រប់គ្រងពួកវាបាននៅក្នុង <ph name="BEGIN_LINK" />ការកំណážáŸ‹<ph name="END_LINK" />។</translation>
<translation id="1671391448414634642">ទំពáŸážšáž‡áž¶ <ph name="SOURCE_LANGUAGE" /> នឹងážáŸ’រូវបាន​បកប្រែទៅជា <ph name="TARGET_LANGUAGE" /> ចាប់ពី​ពáŸáž›áž“áŸáŸ‡ážáž‘ៅ។</translation>
+<translation id="1673886523110456987">បង់ប្រាក់ចáŸáž‰â€‹ážŠáŸ„យប្រើ <ph name="CARD_DETAIL" /> ដើម្បីប្រើប្រាស់​ការផ្ដល់ជូន</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> ទៅ <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">គែមážáŸ’លី​មុនគáŸ</translation>
<translation id="168693727862418163">ážáž˜áŸ’លៃគោលការណáŸáž“áŸáŸ‡áž˜áž·áž“ážáŸ’រូវនឹងគំនូស​ážáž¶áž„របស់វាទ០ហើយនឹងមិនážáŸ’រូវបានអើពើទáŸáŸ”</translation>
@@ -307,6 +311,7 @@
<translation id="1717218214683051432">ឧបករណáŸâ€‹áž…ាប់ចលនា</translation>
<translation id="1717494416764505390">ប្រអប់​សំបុážáŸ’រទី 3</translation>
<translation id="1718029547804390981">ឯកសារធំពáŸáž€ មិន​អាចធ្វើចំណារបានទáŸ</translation>
+<translation id="1720941539803966190">បិទ​មáŸážšáŸ€áž“</translation>
<translation id="1721424275792716183">* ážáž˜áŸ’រូវឲ្យបំពáŸáž‰</translation>
<translation id="1727613060316725209">វិញ្ញាបនបážáŸ’រ​មានសុពលភាព</translation>
<translation id="1727741090716970331">បញ្ចូល​លáŸážáž”ណ្ណដែល​​ážáŸ’រឹមážáŸ’រូវ</translation>
@@ -424,8 +429,8 @@
<translation id="205212645995975601">BBQ និង​ការអាំងសាច់</translation>
<translation id="2053111141626950936">ទំពáŸážšáž‡áž¶ <ph name="LANGUAGE" /> នឹងមិនážáŸ’រូវ​បានបកប្រែទáŸáŸ”</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{នៅពáŸáž›áž”ើកការគ្រប់គ្រងនáŸáŸ‡ ហើយស្ážáž¶áž“ភាពកំពុងសកម្ម នោះ Chrome កំណážáŸ‹ážáž¶ážáž¾áž€áŸ’រុមមនុស្សមួយក្រុមធំ ឬ "ក្រុមមនុស្សដូចគ្នា" ណា ដែលសកម្មភាព​រុករក​ážáž¶áž˜áž¢áŸŠáž¸áž“ធឺណិážážáŸ’មីៗរបស់អ្នកស្រដៀងបំផុážáŸ” អ្នកផ្សាយ​ពាណិជ្ជកម្ម​អាចជ្រើសរើស​ការផ្សាយពាណិជ្ជកម្ម​សម្រាប់ក្រុម​នោះ ហើយសកម្មភាព​រុករក​ážáž¶áž˜áž¢áŸŠáž¸áž“ធឺណិážážšáž”ស់អ្នកážáŸ’រូវបានរក្សាជាលក្ážážŽáŸˆáž¯áž€áž‡áž“នៅលើឧបករណáŸážšáž”ស់អ្នក។ ក្រុមរបស់អ្នកážáŸ’រូវបានធ្វើបច្ចុប្បន្នភាពរៀងរាល់ážáŸ’ងៃ។}=1{នៅពáŸáž›áž”ើកការគ្រប់គ្រងនáŸáŸ‡ ហើយស្ážáž¶áž“ភាពកំពុងសកម្ម នោះ Chrome កំណážáŸ‹ážáž¶ážáž¾áž€áŸ’រុមមនុស្សមួយក្រុមធំ ឬ "ក្រុមមនុស្សដូចគ្នា" ណា ដែលសកម្មភាព​រុករក​ážáž¶áž˜áž¢áŸŠáž¸áž“ធឺណិážážáŸ’មីៗរបស់អ្នកស្រដៀងបំផុážáŸ” អ្នកផ្សាយ​ពាណិជ្ជកម្ម​អាចជ្រើសរើស​ការផ្សាយពាណិជ្ជកម្ម​សម្រាប់ក្រុម​នោះ ហើយសកម្មភាព​រុករក​ážáž¶áž˜áž¢áŸŠáž¸áž“ធឺណិážážšáž”ស់អ្នកážáŸ’រូវបានរក្សាជាលក្ážážŽáŸˆáž¯áž€áž‡áž“នៅលើឧបករណáŸážšáž”ស់អ្នក។ ក្រុមរបស់អ្នកážáŸ’រូវបានធ្វើបច្ចុប្បន្នភាពរៀងរាល់ážáŸ’ងៃ។}other{នៅពáŸáž›áž”ើកការគ្រប់គ្រងនáŸáŸ‡ ហើយស្ážáž¶áž“ភាពកំពុងសកម្ម នោះ Chrome កំណážáŸ‹ážáž¶ážáž¾áž€áŸ’រុមមនុស្សមួយក្រុមធំ ឬ "ក្រុមមនុស្សដូចគ្នា" ណា ដែលសកម្មភាព​រុករក​ážáž¶áž˜áž¢áŸŠáž¸áž“ធឺណិážážáŸ’មីៗរបស់អ្នកស្រដៀងបំផុážáŸ” អ្នកផ្សាយ​ពាណិជ្ជកម្ម​អាចជ្រើសរើស​ការផ្សាយពាណិជ្ជកម្ម​សម្រាប់ក្រុម​នោះ ហើយសកម្មភាព​រុករក​ážáž¶áž˜áž¢áŸŠáž¸áž“ធឺណិážážšáž”ស់អ្នកážáŸ’រូវបានរក្សាជាលក្ážážŽáŸˆáž¯áž€áž‡áž“នៅលើឧបករណáŸážšáž”ស់អ្នក។ ក្រុមរបស់អ្នកážáŸ’រូវបានធ្វើបច្ចុប្បន្នភាពរៀងរាល់ {NUM_DAYS} ážáŸ’ងៃ។}}</translation>
-<translation id="2053553514270667976">áž›áŸážáž€áž¼ážŠážáŸ†áž”ន់</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{ការផ្ážáž›áŸ‹áž™áŸ„បល់ 1}other{ការផ្ážáž›áŸ‹áž™áŸ„បល់ #}}</translation>
+<translation id="2066915425250589881">ស្នើសុំ​ឱ្យលុប</translation>
<translation id="2068528718802935086">ទារក និង​ក្មáŸáž„ážáž¼áž…​ទើបចáŸáŸ‡ážŠáž¾ážš</translation>
<translation id="2071156619270205202">កាážáž“áŸáŸ‡â€‹áž˜áž·áž“មានសិទ្ធិ​សម្រាប់លáŸážáž€áž¶ážâ€‹áž“ិម្មិážáž‘áŸáŸ”</translation>
<translation id="2071692954027939183">ការជូនដំណឹងážáŸ’រូវបានទប់ស្កាážáŸ‹ážŠáŸ„យស្វáŸáž™áž”្រវážáŸ’ážáž· ដោយសារជាធម្មážáž¶ អ្នកមិនអនុញ្ញាážáž€áž¶ážšáž‡áž¼áž“ដំណឹងទáŸ</translation>
@@ -437,7 +442,6 @@
<translation id="2088086323192747268">គ្រប់គ្រងប៊ូážáž»áž„សម​កាល​កម្ម, ចុច "Enter" ដើម្បីគ្រប់គ្រងážáž¶áž–áŸážáŸŒáž˜áž¶áž“អ្វីដែលអ្នកធ្វើសមកាលកម្មនៅក្នុងការកំណážáŸ‹ Chrome</translation>
<translation id="2091887806945687916">សំឡáŸáž„</translation>
<translation id="2094505752054353250">ដែនមិនážáŸ’រូវគ្នា</translation>
-<translation id="2096368010154057602">នាយកដ្ឋាន</translation>
<translation id="2099652385553570808">កិបបីគ្រាប់​ážáž¶áž„ឆ្វáŸáž„</translation>
<translation id="2101225219012730419">កំណែ៖</translation>
<translation id="2102134110707549001">ណែនាំ​ពាក្យសម្ងាážáŸ‹â€‹ážáŸ’លាំង…</translation>
@@ -474,7 +478,6 @@
<translation id="2185836064961771414">បាល់ឱប</translation>
<translation id="2187317261103489799">ស្វែងរក (លំនាំដើម)</translation>
<translation id="2188375229972301266">ចោះ​ច្រើនរន្ធ​ážáž¶áž„ក្រោម</translation>
-<translation id="2188852899391513400">ពាក្យសម្ងាážáŸ‹ážŠáŸ‚លអ្នកទើបážáŸ‚ប្រើážáŸ’រូវបានរកឃើញនៅក្នុងការបែកធ្លាយទិន្ននáŸáž™áŸ” ដើម្បីការពារសុវážáŸ’ážáž·áž—ាពគណនីរបស់អ្នក កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាážáŸ‹ Google ណែនាំឱ្យផ្លាស់ប្ដូរពាក្យសម្ងាážáŸ‹áž“áŸáŸ‡áž¥áž¡áž¼ážœáž“áŸáŸ‡ រួចពិនិážáŸ’យមើលពាក្យសម្ងាážáŸ‹ážŠáŸ‚លអ្នកបានរក្សាទុក។</translation>
<translation id="219906046732893612">ការកែលម្អផ្ទះ</translation>
<translation id="2202020181578195191">បញ្ចូលឆ្នាំផុážáž€áŸ†ážŽážáŸ‹áž²áŸ’យបានážáŸ’រឹមážáŸ’រូវ</translation>
<translation id="22081806969704220">ទម្រទី 3</translation>
@@ -485,6 +488,8 @@
<translation id="2215727959747642672">ការកែឯកសារ</translation>
<translation id="2215963164070968490">ឆ្កែ</translation>
<translation id="2218879909401188352">ážáŸ’មីៗនáŸáŸ‡â€‹áž¢áŸ’នក​វាយ​ប្រហារ​នៅលើ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> អាច​ដំឡើង​កម្មវិធី​ដែល​មាន​គ្រោះážáŸ’នាក់​ដែល​អាច​ប៉ះពាល់​ដល់ឧបករណáŸážšáž”ស់អ្នក បន្ážáŸ‚ម​ការចំណាយ​ទៅក្នុង​វិក្កយបážáŸ’រ​សáŸážœáž¶â€‹áž‘ូរសព្ទ​ចលáŸážâ€‹ážŠáŸ„យ​មិន​ឲ្យ​អ្នកដឹង ឬ​លួច​ពáŸážáŸŒáž˜áž¶áž“​ផ្ទាល់ážáŸ’លួន​របស់​អ្នក។ <ph name="BEGIN_LEARN_MORE_LINK" />ស្វែងយល់​បន្ážáŸ‚ម<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ចាប់ផ្ដើម​មáŸážšáŸ€áž“​ឡើងវិញ</translation>
+<translation id="2219735899272417925">ážáž˜áŸ’រូវ​ឱ្យ​កំណážáŸ‹â€‹áž§áž”ករណáŸâ€‹áž¡áž¾áž„វិញ</translation>
<translation id="2224337661447660594">គ្មាន​អ៊ីនធឺណិážáž‘áŸ</translation>
<translation id="2230458221926704099">សូមដោះស្រាយការážáž—្ជាប់របស់អ្នកដោយប្រើ<ph name="BEGIN_LINK" />កម្មវិធីវិនិច្ឆáŸáž™<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">ផ្ញើឥឡូវនáŸáŸ‡</translation>
@@ -583,11 +588,13 @@
<translation id="2512101340618156538">មិនអនុញ្ញាហ(លំនាំដើម)</translation>
<translation id="2512413427717747692">កំណážáŸ‹ Chrome ជាប៊ូážáž»áž„កម្មវិធីរុករកážáž¶áž˜áž¢áŸŠáž¸áž“ធឺណិážáž›áŸ†áž“ាំដើម ចុច "Enter" ដើម្បីកំណážáŸ‹ Chrome ជាកម្មវិធីរុករកážáž¶áž˜áž¢áŸŠáž¸áž“ធឺណិážáž›áŸ†áž“ាំដើមរបស់ប្រពáŸáž“្ធនៅក្នុងការកំណážáŸ‹ iOS</translation>
<translation id="2515629240566999685">áž–áž·áž“áž·ážáŸ’យសáŸážœáž¶áž“ៅក្នុងážáŸ†áž”ន់របស់អ្នក</translation>
+<translation id="2515761554693942801">អ្នក​បាន​ជ្រើសរើស​ផ្ទៀងផ្ទាážáŸ‹â€‹ážŠáŸ„យប្រើ Touch ID នៅលើ​គáŸáž áž‘ំពáŸážšâ€‹ážŠáŸ‚ល​ប្រើប្រាស់ <ph name="PROVIDER_ORIGIN" />។ ក្រុមហ៊ុន​ផ្ដល់​សáŸážœáž¶â€‹áž“áŸáŸ‡â€‹áž”្រហែលជា​បានរក្សាទុក​ពáŸážáŸŒáž˜áž¶áž“​អំពី​វិធីបង់ប្រាក់​របស់អ្នក ដែល​អ្នក​អាច​<ph name="LINK_TEXT" />។</translation>
<translation id="2521385132275182522">កិប​ážáž¶áž„ក្រោម​ផ្នែកážáž¶áž„​ស្ដាំ</translation>
<translation id="2521736961081452453">បង្កើážâ€‹áž‘ម្រង់បែបបទ</translation>
<translation id="2523886232349826891">បានរក្សាទុក​នៅលើážáŸ‚ឧបករណáŸáž“áŸáŸ‡â€‹áž”៉ុណ្ណោះ</translation>
<translation id="2524461107774643265">បញ្ចូល​ពáŸážáŸŒáž˜áž¶áž“​បន្ážáŸ‚ម</translation>
<translation id="2529899080962247600">កន្លែងបញ្ចូលនáŸáŸ‡â€‹áž˜áž·áž“​គួរមានច្រើនជាង <ph name="MAX_ITEMS_LIMIT" /> ធាážáž»â€‹áž‘áŸáŸ” ធាážáž»áž‘ាំងអស់​ដែលលើសពីនáŸáŸ‡â€‹áž“ឹងមិនážáŸ’រូវបាន​អើពើទáŸáŸ”</translation>
+<translation id="253493526287553278">មើល​ពáŸážáŸŒáž˜áž¶áž“​លម្អិážâ€‹áž¢áŸ†áž–ី​កូដផ្ដល់ជូន​ពិសáŸážŸ</translation>
<translation id="2535585790302968248">បើក​ផ្ទាំងឯកជនážáŸ’មី ដើម្បីរុករក​ជាលក្ážážŽáŸˆáž¯áž€áž‡áž“</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{áž“áž·áž„ 1 ​ទៀáž}other{áž“áž·áž„ # ទៀáž}}</translation>
<translation id="2536110899380797252">បញ្ចូល​អាសយដ្ឋាន</translation>
@@ -623,7 +630,6 @@
<translation id="259821504105826686">សិល្បៈ​ឌីជីážáž› និងការážážážšáž¼áž”</translation>
<translation id="2601150049980261779">ភាពយន្ážâ€‹áž˜áž“ោសញ្ចáŸážáž“ា</translation>
<translation id="2604589665489080024">ážáž“្ážáŸ’រី​ប៉ុប</translation>
-<translation id="2609632851001447353">បំរែបម្រួល</translation>
<translation id="2610561535971892504">ចុច​ដើម្បី​ចម្លង</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />នឹង​មិន​រក្សាទុក<ph name="END_EMPHASIS" />áž–áŸážáŸŒáž˜áž¶áž“​ážáž¶áž„ក្រោម​ទáŸáŸ–
<ph name="BEGIN_LIST" />
@@ -656,6 +662,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">ážáŸ’ងៃកំណើហនិង​ឈ្មោះážáŸ’ងៃ</translation>
<translation id="2677748264148917807">ចាកចáŸáž‰</translation>
+<translation id="2679714844901977852">រក្សាទុកពáŸážáŸŒáž˜áž¶áž“​អំពីការទូទាážáŸ‹ážœáž·áž€áŸ’កយបážáŸ’ážš និងកាážâ€‹ážšáž”ស់​អ្នក​ទៅក្នុងគណនី Google <ph name="USER_EMAIL" /> របស់អ្នក ដើម្បីឱ្យការបង់ប្រាក់ចáŸáž‰â€‹áž˜áž¶áž“សុវážáŸ’ážáž·áž—ាព និងកាន់ážáŸ‚ážšáž áŸážŸ</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">សáŸáž€áŸ’ážáž·ážŸáž˜â€‹áž”ំផុáž</translation>
<translation id="2688969097326701645">បាទ/ចាស បន្áž</translation>
@@ -704,7 +711,6 @@
<translation id="2854764410992194509">ក្រុមហ៊ុន​ផ្ដល់សáŸážœáž¶áž€áž˜áŸ’ម​អ៊ីនធឺណិហ(ISP)</translation>
<translation id="2856444702002559011">អ្នក​វាយ​ប្រហារ​ប្រហែល​ជាកំពុង​លួចយក​ពáŸážáŸŒáž˜áž¶áž“​របស់អ្នក​ពី <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (ឧទាហរណáŸáŸ– ពាក្យសម្ងាážáŸ‹ សារ ឬបណ្ណឥណទាន)។ <ph name="BEGIN_LEARN_MORE_LINK" />ស្វែងយល់បន្ážáŸ‚ម<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">áž‚áŸáž áž‘ំពáŸážšâ€‹áž“áŸáŸ‡áž”ង្ហាញ​ការផ្សាយពាណិជ្ជកម្ម​ដែលនាំឱ្យយល់ច្រឡំ ឬរំážáž¶áž“។</translation>
-<translation id="286512204874376891">កាážáž“ិម្មិážáž€áŸ’លែងធ្វើជា​កាážáž–áž·ážáž”្រាកដ​របស់អ្នក ដើម្បីជួយការពារអ្នក​ពីការគៃបន្លំ​ដែលអាចកើážáž˜áž¶áž“។ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">លក្ážážŽáŸˆâ€‹áž˜áž·ážáŸ’ážáž—ាព</translation>
<translation id="28761159517501904">ភាពយន្áž</translation>
<translation id="2876489322757410363">កំពុង​ចាកចáŸáž‰áž–ី​មុážáž„ារ​ឯកជន ដើម្បីបង់ប្រាក់​ážáž¶áž˜ážšáž™áŸˆâ€‹áž€áž˜áŸ’មវិធី​ážáž¶áž„ក្រៅ។ បន្ážáž¬áž‘áŸ?</translation>
@@ -805,7 +811,6 @@
<translation id="3158539265159265653">ážáž¶ážŸ</translation>
<translation id="3162559335345991374">Wi-Fi ដែលអ្នកកំពុងប្រើអាចážáž˜áŸ’រូវឲ្យអ្នកទៅកាន់ទំពáŸážšáž…ុះឈ្មោះរបស់វា។</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">កោះ</translation>
<translation id="3176929007561373547">áž–áž·áž“áž·ážáŸ’យការកំណážáŸ‹áž”្រូកស៊ីរបស់អ្នក ឬទំនាក់ទំនងអ្នកគ្រប់គ្រងបណ្ážáž¶áž‰ážšáž”ស់អ្នកដើម្បី
មáŸáž”្រូកស៊ីកំពុងដំណើរការ។ ប្រសិនបើអ្នកមិនជឿជាក់ážáž¶áž¢áŸ’នកគួរ
ប្រើម៉ាស៊ីនមáŸáž”្រូកស៊ីឬអážáŸ‹áŸ–
@@ -884,9 +889,6 @@
<translation id="3369192424181595722">កំហុសម៉ោង</translation>
<translation id="3369459162151165748">គ្រឿងបន្លាស់​យានជំនិះ និង​គ្រឿងបន្ážáŸ‚ម</translation>
<translation id="3371064404604898522">កំណážáŸ‹ Chrome ជាកម្មវិធីរុករកលំនាំដើម</translation>
-<translation id="3371076217486966826"><ph name="URL" /> ចង់៖
- • បង្កើážáž•áŸ‚នទី 3D នៃមជ្ឈដ្ឋានជុំវិញ​របស់អ្នក áž“áž·áž„ážáž¶áž˜ážŠáž¶áž“​ទីážáž¶áŸ†áž„កាមáŸážšáŸ‰áž¶
- • ប្រើកាមáŸážšáŸ‰áž¶ážšáž”ស់អ្នក</translation>
<translation id="337363190475750230">បានផ្ážáž¶áž…់ការផ្ážáž›áŸ‹áž‡áž¼áž“</translation>
<translation id="3375754925484257129">ដំណើរការ​ការពិនិážáŸ’យ​សុវážáŸ’ážáž·áž—ាព Chrome</translation>
<translation id="3377144306166885718">ម៉ាស៊ីនមáŸáž”ានប្រើ​កំណែ TLS ដែលលែងប្រើ។</translation>
@@ -903,6 +905,7 @@
<translation id="3399952811970034796">អាសយដ្ឋាន​ចែកចាយ</translation>
<translation id="3402261774528610252">បានប្រើការážáž—្ជាប់ ដើម្បីផ្ទុកគáŸáž áž‘ំពáŸážšáž“áŸáŸ‡ ដែលប្រើ TLS 1.0 ឬ TLS 1.1 ដែលបានបញ្ឈប់ ហើយនឹងážáŸ’រូវបិទក្នុងពáŸáž›áž¢áž“ាគážáŸ” បន្ទាប់ពីបិទហើយ អ្នកប្រើប្រាស់​នឹងážáŸ’រូវបានទប់ស្កាážáŸ‹â€‹áž˜áž·áž“ឱ្យផ្ទុកគáŸáž áž‘ំពáŸážšáž“áŸáŸ‡áŸ” ម៉ាស៊ីនមáŸáž“áŸáŸ‡áž‚ួរážáŸ‚បើក TLS 1.2 ឬážáŸ’ពស់ជាងនáŸáŸ‡áŸ”</translation>
<translation id="3405664148539009465">ប្ážáž¼ážšáž–ុម្ពអក្សរážáž¶áž˜áž”ំណង</translation>
+<translation id="3407789382767355356">ការចូលគណនី​ដោយភាគីទីបី</translation>
<translation id="3409896703495473338">គ្រប់គ្រង​ការកំណážáŸ‹â€‹ážŸáž»ážœážáŸ’ážáž·áž—ាព</translation>
<translation id="3414952576877147120">ទំហំ៖</translation>
<translation id="3417660076059365994">ឯកសារ​ដែលអ្នក​បង្ហោះ ឬ​ភ្ជាប់​ážáŸ’រូវបានផ្ញើ​ទៅ Google Cloud ឬ​ភាគីទីបី​ដើម្បីវិភាគ​។ ឧទាហរណ០ឯកសារទាំងនោះ​អាចážáŸ’រូវបានស្កáŸáž“ ដើម្បីរកមើល​ទិន្ននáŸáž™â€‹ážšážŸáž¾áž” ឬ​កម្មវិធីគ្រោះážáŸ’នាក់​។</translation>
@@ -935,6 +938,7 @@
<translation id="3477679029130949506">បញ្ជីភាពយន្ហនិង​ម៉ោងបញ្ចាំង​នៅរោងភាពយន្áž</translation>
<translation id="3479552764303398839">មិនមែនឥឡូវនáŸáŸ‡áž‘áŸ</translation>
<translation id="3484560055331845446">អ្នកអាច​បាážáŸ‹áž”ង់​សិទ្ធិចូល​ប្រើ​គណនី Google របស់អ្នក។ Chrome ណែនាំ​ឱ្យប្ដូរ​ពាក្យសម្ងាážáŸ‹â€‹ážšáž”ស់អ្នក​ឥឡូវនáŸáŸ‡áŸ” អ្នកនឹងážáŸ’រូវបាន​ស្នើ​ឱ្យ​ចូល​គណនី។</translation>
+<translation id="3484861421501147767">ការរំលឹក៖ មាន​កូដ​ផ្ážáž›áŸ‹áž‡áž¼áž“ពិសáŸážŸâ€‹ážŠáŸ‚លបានរក្សាទុក</translation>
<translation id="3487845404393360112">ទម្រទី 4</translation>
<translation id="3495081129428749620">ស្វែងរក​នៅក្នុងទំពáŸážš
<ph name="PAGE_TITLE" /></translation>
@@ -1059,6 +1063,7 @@
<translation id="3810973564298564668">គ្រប់គ្រង</translation>
<translation id="3816482573645936981">ážáž˜áŸ’លៃ (បានជំនួស)</translation>
<translation id="382518646247711829">ប្រសិនបើអ្នកប្រើម៉ាស៊ីនមáŸáž”្រូកស៊ី...</translation>
+<translation id="3826050100957962900">ការចូលគណនីភាគីទីបី</translation>
<translation id="3827112369919217609">ដាច់ážáž¶áž</translation>
<translation id="3827666161959873541">ភាពយន្ážâ€‹áž›áž€áŸ’ážážŽáŸˆâ€‹áž‚្រួសារ</translation>
<translation id="3828924085048779000">ឃ្លាសម្ងាážáŸ‹áž‘áž‘áŸáž˜áž·áž“ážáŸ’រូវបានអនុញ្ញាážáž‘áŸáŸ”</translation>
@@ -1071,9 +1076,9 @@
<translation id="3858027520442213535">ធ្វើបច្ចុប្បន្នភាពកាលបរិច្ឆáŸáž‘ និងម៉ោង</translation>
<translation id="3858860766373142691">ឈ្មោះ</translation>
<translation id="3872834068356954457">វិទ្យាសាស្ážáŸ’ážš</translation>
+<translation id="3875783148670536197">បង្ហាញážáŸ’ញុំ​អំពី​របៀប</translation>
<translation id="3881478300875776315">បង្ហាញបន្ទាážáŸ‹ážáž·áž…ជាងនáŸáŸ‡</translation>
<translation id="3884278016824448484">មានបញ្ហាជាមួយឧបករណáŸážŸáž˜áŸ’គាល់ឧបករណáŸ</translation>
-<translation id="3885155851504623709">ážáŸ†áž”ន់រដ្ឋបាល</translation>
<translation id="388632593194507180">បានរកឃើញ​ការឃ្លាំមើល</translation>
<translation id="3886948180919384617">ទម្រគំនរទី 3</translation>
<translation id="3890664840433101773">បញ្ចូល​អ៊ីមែល</translation>
@@ -1103,9 +1108,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> ážáŸ’រូវបានរារាំង</translation>
<translation id="3978338123949022456">មុážáž„ារស្វែងរក សូមវាយបញ្ចូលសំណួរ និងចុច "Enter" ដើម្បីស្វែងរកជាមួយ <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">បក្សី</translation>
+<translation id="3985750352229496475">គ្រប់គ្រង​អាសយដ្ឋាន...</translation>
<translation id="3986705137476756801">បិទអក្សររážáŸ‹â€‹áž€áŸ’នុងពáŸáž›áž‡áž¶áž€áŸ‹ážŸáŸ’ដែង​ឥឡូវនáŸáŸ‡</translation>
<translation id="3987940399970879459">ážáž·áž…ជាង 1 MB</translation>
<translation id="3990250421422698716">គម្លាážážšáž»áž‰</translation>
+<translation id="3992684624889376114">អំពី​ទំពáŸážšâ€‹áž“áŸáŸ‡</translation>
<translation id="3996311196211510766">áž‚áŸáž áž‘ំពáŸážš <ph name="ORIGIN" /> បានស្នើសុំឱ្យ​អនុវážáŸ’ážâ€‹áž‚ោលការណáŸâ€‹ážŠáž¾áž˜
ចំពោះ​គ្រប់សំណើ​មក​កាន់​វា ប៉ុន្ážáŸ‚​គោលការណáŸáž“áŸáŸ‡áž˜áž·áž“អាច​អនុវážáŸ’ážâ€‹áž”ាន​ទáŸáž€áŸ’នុងពáŸáž›áž”ច្ចុប្បន្ន។</translation>
<translation id="4006465311664329701">វិធី​បង់ប្រាក់ ការផ្ដល់ជូន និង​អាសយដ្ឋាន​ដែលប្រើ Google Pay</translation>
@@ -1230,6 +1237,7 @@
<translation id="4305666528087210886">មិនអាចចូលប្រើ​ឯកសារ​របស់អ្នកបានទáŸ</translation>
<translation id="4306529830550717874">រក្សាទុក​អាសយដ្ឋាន​ដែរទ�</translation>
<translation id="4306812610847412719">ឃ្លីបបáž</translation>
+<translation id="4310070645992025887">ស្វែងរកការធ្វើដំណើររបស់អ្នក</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">រារាំង (លំនាំដើម)</translation>
<translation id="4314815835985389558">គ្រប់គ្រង​សមកាលកម្ម</translation>
@@ -1281,6 +1289,7 @@
<translation id="4435702339979719576">បណ្ណប្រៃសណីយáŸ)</translation>
<translation id="443673843213245140">ការប្រើប្រូកស៊ីážáŸ’រូវបានបិទដំណើរការ ប៉ុន្ážáŸ‚ការកំណážáŸ‹áž”្រូកស៊ីដែលច្បាស់លាស់ážáŸ’រូវបានបញ្ជាក់។</translation>
<translation id="4441832193888514600">បានមិនអើពើ ដោយសារអាចកំណážáŸ‹â€‹áž‚ោលការណáŸâ€‹áž‡áž¶áž‚ោលការណáŸâ€‹áž¢áŸ’នកប្រើប្រាស់ពពក​ážáŸ‚ប៉ុណ្ណោះ។</translation>
+<translation id="4442470707340296952">ផ្ទាំង Chrome</translation>
<translation id="4450893287417543264">កុំបង្ហាញម្ដងទៀáž</translation>
<translation id="4451135742916150903">អាចស្នើសុំ​ភ្ជាប់ជាមួយ​ឧបករណ០HID</translation>
<translation id="4452328064229197696">ពាក្យសម្ងាážáŸ‹ážŠáŸ‚លអ្នកទើបážáŸ‚ប្រើážáŸ’រូវបានរកឃើញនៅក្នុងការបែកធ្លាយទិន្ននáŸáž™áŸ” ដើម្បីការពារសុវážáŸ’ážáž·áž—ាពគណនីរបស់អ្នក កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាážáŸ‹ Google ណែនាំឱ្យពិនិážáŸ’យមើលពាក្យសម្ងាážáŸ‹ážŠáŸ‚លអ្នកបានរក្សាទុក។</translation>
@@ -1419,6 +1428,7 @@
<translation id="483241715238664915">បើក​ការព្រមាន</translation>
<translation id="4834250788637067901">វិធី​បង់ប្រាក់ ការផ្ដល់ជូន និង​អាសយដ្ឋាន​ដែលប្រើ Google Pay</translation>
<translation id="4838327282952368871">ដូចក្ដី​សុបិន</translation>
+<translation id="4839087176073128681">បង់ប្រាក់បាន​កាន់​ážáŸ‚​រហáŸážŸâ€‹áž“ៅ​លើក​ក្រោយ ហើយការពារកាážážšáž”ស់អ្នកážáž¶áž˜â€‹ážšáž™áŸˆážŸáž»ážœážáŸ’ážáž·áž—ាពឈានមុនគáŸáž€áŸ’នុងវិសáŸáž™ážšáž”ស់ Google។</translation>
<translation id="4840250757394056958">មើលប្រវážáŸ’ážáž· Chrome របស់អ្នក</translation>
<translation id="484462545196658690">ស្វáŸáž™áž”្រវážáŸ’ážáž·</translation>
<translation id="484671803914931257">ទទួលបាន​ការបញ្ចុះážáž˜áŸ’លៃសម្រាប់ <ph name="MERCHANT_NAME" /> និង​ច្រើនទៀáž</translation>
@@ -1481,6 +1491,7 @@
<translation id="5011561501798487822">ភាសាដែលបានសម្គាល់</translation>
<translation id="5015510746216210676">ឈ្មោះ​ម៉ាស៊ីន​៖</translation>
<translation id="5017554619425969104">ពាក្យដែល​អ្នកបាន​ចម្លង</translation>
+<translation id="5017828934289857214">រំលឹក​ážáŸ’ញុំ​ពáŸáž›áž€áŸ’រោយ</translation>
<translation id="5018422839182700155">មិនអាចបើកទំពáŸážšáž“áŸáŸ‡áž”ានទáŸ</translation>
<translation id="5019198164206649151">កន្លែងផ្ទុកមូលដ្ឋាននៅក្នុងស្ážáž¶áž“ភាពមិនល្អ</translation>
<translation id="5020776957610079374">ážáž“្ážáŸ’រី​ពិភពលោក</translation>
@@ -1500,6 +1511,7 @@
<translation id="5051305769747448211">រឿងកំប្លែង​បន្ážáž•áŸ’ទាល់</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{ដើម្បីផ្ញើឯកសារនáŸáŸ‡ážŠáŸ„យប្រើការ​ចែករំលែកនៅ​ជិហសូមបង្កើនទំហំផ្ទុក (<ph name="DISK_SPACE_SIZE" />) នៅលើឧបករណáŸážšáž”ស់អ្នក}other{ដើម្បីផ្ញើឯកសារទាំងនáŸáŸ‡ážŠáŸ„យប្រើការ​ចែករំលែកនៅ​ជិហសូមបង្កើនទំហំផ្ទុក (<ph name="DISK_SPACE_SIZE" />) នៅលើឧបករណáŸážšáž”ស់អ្នក}}</translation>
<translation id="5056549851600133418">អážáŸ’ážáž”ទសម្រាប់អ្នក</translation>
+<translation id="5060483733937416656">អ្នក​បាន​ជ្រើសរើស​ផ្ទៀងផ្ទាážáŸ‹â€‹ážŠáŸ„យប្រើ Windows Hello នៅលើ​គáŸáž áž‘ំពáŸážšâ€‹ážŠáŸ‚ល​ប្រើប្រាស់ <ph name="PROVIDER_ORIGIN" />។ ក្រុមហ៊ុន​ផ្ដល់​សáŸážœáž¶â€‹áž“áŸáŸ‡â€‹áž”្រហែលជា​បានរក្សាទុក​ពáŸážáŸŒáž˜áž¶áž“​អំពី​វិធីបង់ប្រាក់​របស់អ្នក ដែល​អ្នក​អាច​<ph name="LINK_TEXT" />។</translation>
<translation id="5061227663725596739">ážáž¾â€‹áž¢áŸ’នក​ចង់​មាននáŸáž™â€‹ážáž¶ <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">ប្រវážáŸ’ážáž·â€‹áž”ោះពុម្ព</translation>
<translation id="5068234115460527047">មូលនិធិ​ការពារហានិភáŸáž™</translation>
@@ -1513,10 +1525,8 @@
<translation id="5087286274860437796">វិញ្ញាបនបážáŸ’រម៉ាស៊ីនមáŸáž˜áž·áž“មានសុពលភាពទáŸáž“ៅពáŸáž›áž“áŸáŸ‡áŸ”</translation>
<translation id="5087580092889165836">បន្ážáŸ‚មកាáž</translation>
<translation id="5088142053160410913">សារផ្ញើទៅ​ប្រážáž·áž”ážáŸ’ážáž·áž€ážš</translation>
-<translation id="5089810972385038852">រដ្ឋ</translation>
<translation id="5093232627742069661">áž”ážáŸ‹â€‹áž‡áž¶áž¢áž€áŸ’សរ Z</translation>
<translation id="5094747076828555589">ម៉ាស៊ីនមáŸáž“áŸáŸ‡áž˜áž·áž“អាចបង្ហាញážáž¶ážœáž¶áž‡áž¶ <ph name="DOMAIN" /> ទ០វិញ្ញាបនបáŸážáŸ’រសុវážáŸ’ážáž·áž—ាពរបស់វាមិនអាចážáŸ’រូវបានជឿជាក់ដោយ Chromium áž‘áŸáŸ” áž“áŸáŸ‡áž¢áž¶áž…បណ្ážáž¶áž›áž˜áž€áž–ីការកំណážáŸ‹áž˜áž·áž“ážáŸ’រឹមážáŸ’រូវ ឬមានការស្ទាក់ការភ្ជាប់របស់អ្នកពីអ្នកវាយប្រហារ។</translation>
-<translation id="5095208057601539847">ážáŸážáŸ’áž</translation>
<translation id="5097099694988056070">ស្ážáž·ážáž·áž§áž”ករណáŸâ€‹ážŠáž¼áž…ជាការប្រើប្រាស់ CPU/RAM ជាដើម</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">áž‚áŸáž áž‘ំពáŸážšáž“áŸáŸ‡â€‹áž˜áž·áž“មានសុវážáŸ’ážáž·áž—ាពទáŸ</translation>
@@ -1554,6 +1564,7 @@
<translation id="5171045022955879922">ស្វែងរក ឬវាយបញ្ចូល URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">ម៉ាស៊ីន</translation>
+<translation id="5177076414499237632">ស្វែងយល់​អំពី​ប្រភព និង​ប្រធានបទ​របស់ទំពáŸážšâ€‹áž“áŸáŸ‡</translation>
<translation id="5179510805599951267">មិនមែនជា <ph name="ORIGINAL_LANGUAGE" /> áž‘áŸ? រាយការណáŸáž€áŸ†áž áž»ážŸáž†áŸ’áž‚áž„áž“áŸáŸ‡</translation>
<translation id="518639307526414276">ផលិážáž•áž›â€‹ážáŸ‚ទាំ​សážáŸ’វចិញ្ចឹម និង​អាហារ​របស់សážáŸ’វចិញ្ចឹម</translation>
<translation id="5190835502935405962">របាចំណាំ</translation>
@@ -1714,6 +1725,7 @@
<translation id="5624120631404540903">គ្រប់គ្រងពាក្យសម្ងាážáŸ‹</translation>
<translation id="5629630648637658800">បានបរាជáŸáž™áž€áŸ’នុងដំណើរការការកំណážáŸ‹áž‚ោលការណáŸ</translation>
<translation id="5631439013527180824">សញ្ញាážáŸ†ážŽáž¶áž„ការគ្រប់គ្រងឧបករណáŸáž‚្មានសុពលភាព</translation>
+<translation id="5632485077360054581">បង្ហាញážáŸ’ញុំ​អំពី​របៀប</translation>
<translation id="5633066919399395251">អ្នក​វាយ​ប្រហារ​ដែល​បច្ចុប្បន្នធ្វើទៅលើ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> អាចនឹងព្យាយាមដំឡើងកម្មវិធីដែលមានគ្រោះážáŸ’នាក់នៅលើ​កុំព្យូទáŸážšâ€‹ážšáž”ស់អ្នក ដែលកម្មវិធីនោះ​អាច​លួច ឬលុប​ពáŸážáŸŒáž˜áž¶áž“​របស់អ្នក (ឧទាហរណáŸáŸ– រូបážáž ពាក្យសម្ងាážáŸ‹ សារ និងបណ្ណឥណទាន)។ <ph name="BEGIN_LEARN_MORE_LINK" />ស្វែងយល់បន្ážáŸ‚ម<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">បានទប់ស្កាážáŸ‹â€‹ážáŸ’លឹមសារបញ្ឆោážáŸ”</translation>
<translation id="5633259641094592098">ភាពយន្ážâ€‹áž¢áŸŠáž¸áž“ឌី និង​​វប្បធម៌</translation>
@@ -1831,6 +1843,7 @@
<translation id="5989320800837274978">ទាំងម៉ាស៊ីនមáŸáž”្រូកស៊ីដែលážáŸážš áž“áž·áž„ URL ស្គ្រីបផáŸáž€áž˜áž·áž“ážáŸ’រូវបានបញ្ជាក់ទáŸáŸ”</translation>
<translation id="5992691462791905444">áž”ážáŸ‹â€‹áž‡áž¶áž¢áž€áŸ’សរ Z</translation>
<translation id="5995727681868049093">គ្រប់គ្រង​ពáŸážáŸŒáž˜áž¶áž“ ឯកជនភាព និង​សុវážáŸ’ážáž·áž—ាព​របស់អ្នក​នៅក្នុង​គណនី Google របស់អ្នក</translation>
+<translation id="5997247540087773573">ពាក្យសម្ងាážáŸ‹ážŠáŸ‚លអ្នកទើបážáŸ‚ប្រើážáŸ’រូវបានរកឃើញនៅក្នុងការបែកធ្លាយទិន្ននáŸáž™áŸ” ដើម្បីការពារសុវážáŸ’ážáž·áž—ាពគណនីរបស់អ្នក កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាážáŸ‹ Google ណែនាំឱ្យផ្លាស់ប្ដូរពាក្យសម្ងាážáŸ‹áž“áŸáŸ‡áž¥áž¡áž¼ážœáž“áŸáŸ‡ រួចពិនិážáŸ’យមើលពាក្យសម្ងាážáŸ‹ážŠáŸ‚លអ្នកបានរក្សាទុក។</translation>
<translation id="6000758707621254961">លទ្ធផល <ph name="RESULT_COUNT" /> សម្រាប់ '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">ធម្មážáž¶</translation>
<translation id="6008122969617370890">លំដាប់លំដោយ N-to-1</translation>
@@ -1926,7 +1939,6 @@
<translation id="627746635834430766">ដើម្បី​បង់ប្រាក់​លឿន​ជាងនáŸáŸ‡â€‹áž“ៅពáŸáž›â€‹áž€áŸ’រោយ សូម​រក្សា​ទុកបណ្ណ និង​អាសយដ្ឋាន​ចáŸáž‰â€‹ážœáž·áž€áŸ’កយបážáŸ’រ​របស់អ្នក​ទៅក្នុង​គណនី Google របស់​អ្នក។</translation>
<translation id="6279183038361895380">ចុច |<ph name="ACCELERATOR" />| ដើម្បីបង្ហាញទស្សនáŸáž‘្រនិចរបស់អ្នក</translation>
<translation id="6280223929691119688">មិនអាចដឹកជញ្ជូនផ្ទាល់ទៅអាសយដ្ឋាននáŸáŸ‡áž”ានទáŸáŸ” សូមជ្រើសរើសអាសយដ្ឋានផ្សáŸáž„។</translation>
-<translation id="6282194474023008486">áž›áŸážáž€áž¼ážŠáž”្រៃសណីយáŸ</translation>
<translation id="6285507000506177184">ប៊ូážáž»áž„ "គ្រប់គ្រង​ការទាញយកនៅក្នុង Chrome" ចុច Enter ដើម្បីគ្រប់គ្រង​ឯកសារដែលអ្នក​បានទាញយកនៅក្នុង Chrome</translation>
<translation id="6289939620939689042">ពណ៌​ទំពáŸážš</translation>
<translation id="6290238015253830360">អážáŸ’ážáž”ទដែលបានផ្ážáž›áŸ‹áž™áŸ„បល់របស់អ្នកបង្ហាញនៅទីនáŸáŸ‡</translation>
@@ -1948,6 +1960,7 @@
<translation id="6337133576188860026">បង្កើន​ទំហំផ្ទុក​ážáž·áž…ជាង <ph name="SIZE" /> ។ ទំពáŸážšâ€‹áž˜áž½áž™áž…ំនួន​អាចផ្ទុក​យឺážáž‡áž¶áž„មុន នៅពáŸáž›ážŠáŸ‚លអ្នក​ចូល​លើកក្រោយ។</translation>
<translation id="6337534724793800597">ážáŸ’រងគោលការណáŸážáž¶áž˜ážˆáŸ’មោះ</translation>
<translation id="6340739886198108203">គោលការណáŸâ€‹áž¢áŸ’នកគ្រប់គ្រង​មិនណែនាំឱ្យ​ážážážœáž¸ážŠáŸáž¢áž¼ ឬážážážšáž¼áž”អáŸáž€áŸ’រង់ទ០នៅពáŸáž›áž¢áž¶áž…​មើលឃើញážáŸ’លឹមសារ​សម្ងាážáŸ‹áŸ–</translation>
+<translation id="6348220984832452017">បម្រែបម្រួលសកម្ម</translation>
<translation id="6349101878882523185">ដំឡើង <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{គ្មាន}=1{ពាក្យសម្ងាážáŸ‹ 1 (សម្រាប់ <ph name="DOMAIN_LIST" />, បាន​ធ្វើសមកាលកម្ម)}=2{ពាក្យសម្ងាážáŸ‹ 2 (សម្រាប់ <ph name="DOMAIN_LIST" />, បាន​ធ្វើសមកាលកម្ម)}other{ពាក្យសម្ងាážáŸ‹ # (សម្រាប់ <ph name="DOMAIN_LIST" />, បាន​ធ្វើសមកាលកម្ម)}}</translation>
<translation id="6355392890578844978">កម្មវិធីរុករកážáž¶áž˜áž¢áŸŠáž¸áž“ធឺណិážáž“áŸáŸ‡â€‹áž˜áž·áž“ážáŸ’រូវបានគ្រប់គ្រង​ដោយក្រុមហ៊ុន ឬស្ážáž¶áž”áŸáž“ផ្សáŸáž„áž‘áŸáŸ” សកម្មភាព​នៅលើឧបករណáŸáž“áŸáŸ‡â€‹áž¢áž¶áž…ážáŸ’រូវបាន​គ្រប់គ្រងក្រៅពី Chromium។ <ph name="BEGIN_LINK" />ស្វែងយល់បន្ážáŸ‚ម<ph name="END_LINK" /></translation>
@@ -1979,7 +1992,6 @@
<translation id="643051589346665201">ប្ដូរ​ពាក្យសម្ងាážáŸ‹ Google</translation>
<translation id="6433490469411711332">កែសម្រួលពáŸážáŸŒáž˜áž¶áž“ទំនាក់ទំនង</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> បានបដិសáŸáž’ក្នុងការážáž—្ជាប់</translation>
-<translation id="6438025220577812695">ប្ដូរពាក្យសម្ងាážáŸ‹â€‹ážŠáŸ„áž™ážáŸ’លួនឯង</translation>
<translation id="6440503408713884761">មិនážáŸ’រូវបាន​អើពើ</translation>
<translation id="6443406338865242315">ážáž¶ážáž¾â€‹áž€áž˜áŸ’មវិធីបន្ážáŸ‚ម និងកម្មវិធី​ជំនួយណាážáŸ’លះដែលអ្នកបានដំឡើង</translation>
<translation id="6446163441502663861">Kahu (ស្រោម​សំបុážáŸ’ážš)</translation>
@@ -2109,9 +2121,9 @@
<translation id="6828866289116430505">សáŸáž“áŸáž‘áž·áž…</translation>
<translation id="6831043979455480757">បកប្រែ</translation>
<translation id="6833752742582340615">រក្សាទុកពáŸážáŸŒáž˜áž¶áž“​អំពីការទូទាážáŸ‹ážœáž·áž€áŸ’កយបážáŸ’ážš និងកាážâ€‹áž‘ៅក្នុងគណនី Google របស់អ្នក ដើម្បីឱ្យការបង់ប្រាក់ចáŸáž‰â€‹áž˜áž¶áž“សុវážáŸ’ážáž·áž—ាព និងកាន់ážáŸ‚ážšáž áŸážŸ</translation>
-<translation id="6839929833149231406">ážáŸ†áž”ន់</translation>
<translation id="6846340164947227603">ប្រើ​លáŸážâ€‹áž”ណ្ណ​​និម្មិáž...</translation>
<translation id="6852204201400771460">ផ្ទុក​កម្មវិធី​ឡើងវិញ?</translation>
+<translation id="6857776781123259569">គ្រប់គ្រង​ពាក្យសម្ងាážáŸ‹...</translation>
<translation id="686485648936420384">ធនធាន​សម្រាប់​អ្នកប្រើប្រាស់</translation>
<translation id="6865412394715372076">មិនអាច​ផ្ទៀងផ្ទាážáŸ‹áž€áž¶ážáž“áŸáŸ‡â€‹â€‹â€‹áž¥áž¡áž¼ážœáž“áŸáŸ‡â€‹áž”ានទáŸ</translation>
<translation id="6869334554832814367">កម្ចី​ផ្ទាល់ážáŸ’លួន</translation>
@@ -2160,7 +2172,6 @@
<translation id="6965978654500191972">ឧបករណáŸ</translation>
<translation id="696703987787944103">ឆាប់សម្គាល់បាន</translation>
<translation id="6968269510885595029">ប្រើ​សោសុវážáŸ’ážáž·áž—ាព​របស់អ្នក</translation>
-<translation id="6970216967273061347">ស្រុក</translation>
<translation id="6971439137020188025">បង្កើážâ€‹áž”ទបង្ហាញ Google ážáŸ’មី​នៅក្នុង​កម្មវិធីបទបង្ហាញ​បានរហáŸážŸ</translation>
<translation id="6972629891077993081">ឧបករណ០HID</translation>
<translation id="6973656660372572881">ទាំងម៉ាស៊ីនមáŸáž”្រូកស៊ីážáŸážš áž“áž·áž„ URL ស្គ្រីបផáŸáž€ážáŸ’រូវបានបញ្ជាក់់។</translation>
@@ -2199,7 +2210,6 @@
<translation id="7081308185095828845">មិនអាច​ប្រើ​មុážáž„ារ​នáŸáŸ‡â€‹áž“ៅលើ​ឧបករណáŸâ€‹ážšáž”ស់អ្នក​បានទáŸ</translation>
<translation id="7083258188081898530">ទម្រទី 9</translation>
<translation id="7086090958708083563">ការបង្ហោះដែលបានស្នើសុំដោយអ្នកប្រើប្រាស់</translation>
-<translation id="7087282848513945231">ប្រទáŸážŸ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, ចុច "Tab" រួចចុច "Enter" ដើម្បីគ្រប់គ្រងការអនុញ្ញាហនិងទិន្ននáŸáž™ážŠáŸ‚លបានរក្សាទុកនៅលើគáŸáž áž‘ំពáŸážšáž“ានានៅក្នុងការកំណážáŸ‹ Chrome</translation>
<translation id="7096937462164235847">អážáŸ’ážážŸáž‰áŸ’ញាណ​របស់គáŸáž áž‘ំពáŸážšáž“áŸáŸ‡â€‹áž˜áž·áž“ážáŸ’រូវបាន​ផ្ទៀងផ្ទាážáŸ‹áž‘áŸáŸ”</translation>
<translation id="7101893872976785596">ភាពយន្ážâ€‹áž—áŸáž™ážšáž“្ធážáŸ‹</translation>
@@ -2218,10 +2228,10 @@
<ph name="LIST_ITEM" />áž–áŸážáŸŒáž˜áž¶áž“ដែលបានបញ្ចូលក្នុងទម្រង់បែបបទ<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">មិនអាចដឹកជញ្ជូនážáž¶áž˜áž”្រៃសណីយáŸáž‘ៅអាសយដ្ឋាននáŸáŸ‡áž”ានទáŸáŸ” សូមជ្រើសរើសអាសយដ្ឋានផ្សáŸáž„។</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">អ្នកគ្រប់គ្រង​របស់អ្នក​បានហាមឃាážáŸ‹â€‹áž˜áž·áž“ឱ្យ​ចម្លងទិន្ននáŸáž™áž“áŸáŸ‡áŸ”</translation>
<translation id="7135130955892390533">បង្ហាញ​ស្ážáž¶áž“ភាព</translation>
<translation id="7138472120740807366">មធ្យោបាយដឹកជញ្ជូនផ្ទាល់</translation>
-<translation id="7139724024395191329">អារ៉ាប់រួម</translation>
<translation id="7139892792842608322">ទម្រ​ចម្បង</translation>
<translation id="714064300541049402">ការប្ដូរ​រូបភាព X នៃចំហៀងទី 2</translation>
<translation id="7152423860607593928">Number-14 (ស្រោម​សំបុážáŸ’ážš)</translation>
@@ -2439,6 +2449,7 @@
<translation id="7669271284792375604">អ្នកវាយប្រហារនៅលើទំពáŸážšáž“áŸáŸ‡ អាចព្យាយាមបញ្ជោážáž¢áŸ’នកឲ្យដំឡើងកម្មវិធីដែលបង្កគ្រោះážáŸ’នាក់ដល់ការរុករករបស់អ្នក (ឧទាហរណáŸáŸ– ដោយផ្លាស់ប្ážáž¼ážšáž‘ំពáŸážšážŠáž¾áž˜ážšáž”ស់អ្នក ឬបង្ហាញការផ្សាយពាណិជ្ជកម្មបន្ážáŸ‚មនៅលើទំពáŸážšážŠáŸ‚លអ្នកបានទៅកាន់)។</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{សកម្មភាព​ដែលបានធ្វើពាក់ពáŸáž“្ធនឹង​ទិន្ននáŸáž™ážŠáŸ‚លបាន​សម្គាល់ážáž¶â€‹áž‡áž¶áž€áž¶ážšážŸáž˜áŸ’ងាážáŸ‹ (សកម្មភាព 1 ចាប់ážáž¶áŸ†áž„ពីចូល)។ <ph name="BEGIN_LINK" />ស្វែងយល់បន្ážáŸ‚ម<ph name="END_LINK" />}other{សកម្មភាព​ដែលបានធ្វើពាក់ពáŸáž“្ធនឹង​ទិន្ននáŸáž™ážŠáŸ‚លបាន​សម្គាល់ážáž¶â€‹áž‡áž¶áž€áž¶ážšážŸáž˜áŸ’ងាážáŸ‹ (សកម្មភាព # ចាប់ážáž¶áŸ†áž„ពីចូល)។ <ph name="BEGIN_LINK" />ស្វែងយល់បន្ážáŸ‚ម<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ប្រអប់​សំបុážáŸ’រទី 6</translation>
+<translation id="7675325315208090829">គ្រប់គ្រង​វិធីបង់ប្រាក់...</translation>
<translation id="7676643023259824263">ស្វែងរក​អážáŸ’ážáž”ទ​ឃ្លីបបáž, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">មើល និងគ្រប់គ្រង​ប្រវážáŸ’ážáž·ážšáž»áž€ážšáž€ážáž¶áž˜â€‹áž¢áŸŠáž¸áž“ធឺណិážâ€‹ážšáž”ស់អ្នកនៅក្នុង​ការកំណážáŸ‹ Chrome</translation>
<translation id="7679947978757153706">áž”áŸážŸáŸ’បល</translation>
@@ -2481,7 +2492,6 @@
<translation id="7766518757692125295">ស្រទាប់ážáž¶áž„ក្រៅ</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">ផ្ងារឡើង​ážáž¶áž˜áž›áŸ†ážŠáž¶áž”់​លំដោយដូចគ្នា</translation>
-<translation id="777702478322588152">អាណាážáŸážáŸ’áž</translation>
<translation id="7791011319128895129">មិនទាន់​ដាក់ចáŸáž‰</translation>
<translation id="7791196057686275387">ចងរមូរ</translation>
<translation id="7791543448312431591">បន្ážáŸ‚ម</translation>
@@ -2573,12 +2583,12 @@
<translation id="8055534648776115597">ការអប់រំបន្ហនិង​វិជ្ជាជីវៈ</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" មិនបានកំណážáŸ‹ážšáž…នាសម្ពáŸáž“្ធážáŸ’រឹមážáŸ’រូវទáŸáŸ” ជាធម្មážáž¶áž€áž¶ážšáž›áž»áž” "<ph name="SOFTWARE_NAME" />" អាចដោះស្រាយបញ្ហានáŸáŸ‡áž”ាន។ <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ផលិážáž€áž˜áŸ’ម​អាហារ</translation>
-<translation id="8066955247577885446">សូម​អភáŸáž™áž‘ោស មានអ្វីមួយ​ážáž»ážŸáž”្រក្រážáž¸áŸ”</translation>
<translation id="8067872629359326442">អ្នកទើបážáŸ‚​បានបញ្ចូល​ពាក្យសម្ងាážáŸ‹â€‹ážšáž”ស់អ្នក​នៅលើ​គáŸáž áž‘ំពáŸážšáž”ញ្ឆោážáŸ” Chromium អាចជួយបាន។ ដើម្បី​ប្ដូរ​ពាក្យសម្ងាážáŸ‹â€‹ážšáž”ស់អ្នក និងជូនដំណឹង​ដល់ Google ážáž¶áž‚ណនី​របស់អ្នក​អាច​ប្រឈម​នឹង​ហានិភáŸáž™ សូមចុច "ការពារ​គណនី"។</translation>
<translation id="8070439594494267500">រូបកម្មវិធី</translation>
<translation id="8074253406171541171">10x13 (ស្រោម​សំបុážáŸ’ážš)</translation>
<translation id="8075736640322370409">បង្កើážâ€‹áž”ញ្ជី Google ážáŸ’មីបានរហáŸážŸ</translation>
<translation id="8075898834294118863">គ្រប់គ្រងការកំណážáŸ‹áž‚áŸáž áž‘ំពáŸážš</translation>
+<translation id="8076492880354921740">ផ្ទាំង</translation>
<translation id="8078141288243656252">មិនអាចដាក់​ចំណារបានទ០នៅពáŸáž›áž”ង្វិល</translation>
<translation id="8079031581361219619">ផ្ទុក​ទំពáŸážšáž¡áž¾áž„​វិញ?</translation>
<translation id="8081087320434522107">ážšážáž™áž“្ážâ€‹áž”្រភáŸáž‘ Sedan</translation>
@@ -2701,6 +2711,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ការកំណážáŸ‹</translation>
+<translation id="8428634594422941299">យល់ហើយ</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, ចុច "Tab" រួចចុច "Enter" ដើម្បីគ្រប់គ្រងចំណូលចិážáŸ’ážážáž¼áž‚ីរបស់អ្នកនៅក្នុងការកំណážáŸ‹ Chrome</translation>
<translation id="8433057134996913067">វានឹងយកអ្នកចáŸáž‰áž–ីគáŸáž áž‘ំពáŸážšáž—ាគច្រើន</translation>
<translation id="8434840396568290395">សážáŸ’វ​ចិញ្ចឹម</translation>
@@ -2798,6 +2809,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> គឺជាលáŸážáž€áž¼ážŠâ€‹ážšáž”ស់អ្នកសម្រាប់ <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">ចំណាំ​ផ្ទាំងនáŸáŸ‡</translation>
<translation id="8751426954251315517">សូមព្យាយាម​ម្ដងទៀážâ€‹áž“ៅពáŸáž›áž€áŸ’រោយ</translation>
+<translation id="8757526089434340176">មាន​ការផ្ដល់ជូន Google Pay</translation>
<translation id="8758885506338294482">ការលáŸáž„​វីដáŸáž¢áž¼áž áŸ’áž‚áŸáž˜â€‹ážŠáŸ‚លមាន​ការប្រកួážáž”្រជែង</translation>
<translation id="8759274551635299824">បណ្ណនáŸáŸ‡áž•áž»ážáž€áŸ†ážŽážáŸ‹áž áž¾áž™</translation>
<translation id="87601671197631245">áž‚áŸáž áž‘ំពáŸážšâ€‹áž“áŸáŸ‡â€‹áž”្រើ​ការកំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ​សុវážáŸ’ážáž·áž—ាព​​ហួសសមáŸáž™ ដែលអាចធ្វើឱ្យ​បែកធ្លាយ​ពáŸážáŸŒáž˜áž¶áž“​របស់អ្នក (ឧទាហរណ០ពាក្យសម្ងាážáŸ‹ សារ ឬ​បណ្ណ​ឥណទាន) នៅពáŸáž›â€‹áž•áŸ’ញើ​ទៅ​គáŸáž áž‘ំពáŸážšâ€‹áž“áŸáŸ‡áŸ”</translation>
@@ -2805,6 +2817,7 @@
<translation id="8763927697961133303">ឧបករណ០USB</translation>
<translation id="8763986294015493060">បិទ​ផ្ទាំងឯកជន​ទាំងអស់​ដែលកំពុងបើក</translation>
<translation id="8766943070169463815">បញ្ជីផ្ទៀងផ្ទាážáŸ‹áž–áŸážáŸŒáž˜áž¶áž“បង់ប្រាក់ដែលមានសុវážáŸ’ážáž·áž—ាពážáŸ’រូវបានបើក</translation>
+<translation id="8767765348545497220">បិទ​ពពុះជំនួយ</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">ម៉ូážáž¼</translation>
<translation id="8790007591277257123">ធ្វើការលុបវិញ</translation>
@@ -2817,6 +2830,7 @@
<translation id="8806285662264631610">ផលិážáž•áž›â€‹ážáŸ‚ទាំដងážáŸ’លួន និង​សម្រាប់សម្អាážážáŸ’លួន</translation>
<translation id="8807160976559152894">ážáž˜áŸ’រឹម​បន្ទាប់ពី​ទំពáŸážšâ€‹áž“ីមួយៗ</translation>
<translation id="8808828119384186784">ការកំណážáŸ‹ Chrome</translation>
+<translation id="8813277370772331957">រំលឹក​ážáŸ’ញុំ​ពáŸáž›áž€áŸ’រោយ</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, ចុច "Tab" រួចចុច "Enter" ដើម្បីដំឡើងកំណែ Chrome ពីការកំណážáŸ‹ Chrome របស់អ្នក</translation>
<translation id="8820817407110198400">ចំណាំ</translation>
<translation id="882338992931677877">រន្ធបញ្ចូលដោយដៃ</translation>
@@ -2996,6 +3010,7 @@
<translation id="988159990683914416">áž›áŸážáž€áŸ†ážŽáŸ‚អ្នកអភិវឌ្ážáž“áŸ</translation>
<translation id="989988560359834682">កែអាសយដ្ឋាន</translation>
<translation id="991413375315957741">ឧបករណáŸâ€‹áž…ាប់ពន្លឺ ឬ​ចាប់ចលនា</translation>
+<translation id="992110854164447044">កាážâ€‹áž“ិម្មិážâ€‹áž›áž¶áž€áŸ‹â€‹áž€áž¶ážâ€‹áž–áž·ážáž”្រាកដ​របស់អ្នក ដើម្បីជួយ​ការពារ​អ្នក​ពី​ការគៃបន្លំ​ដែលអាចកើážáž˜áž¶áž“។ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ផ្កា​ឈូក</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" មិនបានដំឡើងážáŸ’រឹមážáŸ’រូវនៅលើកុំព្យូទáŸážš ឬបណ្ážáž¶áž‰ážšáž”ស់អ្នកទáŸáŸ–
diff --git a/chromium/components/strings/components_strings_kn.xtb b/chromium/components/strings/components_strings_kn.xtb
index 515816d0bdc..07c38ccaebd 100644
--- a/chromium/components/strings/components_strings_kn.xtb
+++ b/chromium/components/strings/components_strings_kn.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ಅನà³à²¦à²¾à²¨à²—ಳà³, ವಿದà³à²¯à²¾à²°à³à²¥à²¿ ವೇತನಗಳೠಮತà³à²¤à³ ಧನ ಸಹಾಯ</translation>
<translation id="1048785276086539861">ನೀವೠಟಿಪà³à²ªà²£à²¿à²—ಳನà³à²¨à³ ಎಡಿಟೠಮಾಡಿದಾಗ, ಈ ಡಾಕà³à²¯à³à²®à³†à²‚ಟೠಒಂದೇ ಪà³à²Ÿà²¦ ವೀಕà³à²·à²£à³†à²—ೆ ಹಿಂತಿರà³à²—à³à²¤à³à²¤à²¦à³†</translation>
<translation id="1050038467049342496">ಇತರ ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³â€à²—ಳನà³à²¨à³ ಮà³à²šà³à²šà²¿</translation>
+<translation id="1053959602163383901">ನೀವೠ<ph name="PROVIDER_ORIGIN" /> ಬಳಸà³à²µ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ಳಲà³à²²à²¿ ದೃಢೀಕರಣ ಸಾಧನವೊಂದರ ಮೂಲಕ ದೃಢೀಕರಿಸà³à²µ ಆಯà³à²•à³†à²¯à²¨à³à²¨à³ ಮಾಡಿದà³à²¦à³€à²°à²¿. ಈ ಪೂರೈಕೆದಾರರೠನಿಮà³à²® ಪಾವತಿ ವಿಧಾನದ ಕà³à²°à²¿à²¤à³ ಮಾಹಿತಿಯನà³à²¨à³ ಸಂಗà³à²°à²¹à²¿à²¸à²¿à²Ÿà³à²Ÿà²¿à²°à²¬à²¹à³à²¦à³, ನೀವೠಅದನà³à²¨à³ <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;ಸೇರಿಸà³à²µà³à²¦à²¨à³à²¨à³ ರದà³à²¦à³à²—ೊಳಿಸಿ</translation>
<translation id="1056663316309890343">ಫೋಟೋ ಸಾಫà³à²Ÿà³â€Œà²µà³‡à²°à³</translation>
<translation id="1056898198331236512">ಎಚà³à²šà²°à²¿à²•à³†</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">ಪಿಕಪೠವಿಧಾನ</translation>
<translation id="1281476433249504884">ಸà³à²Ÿà³à²¯à²¾à²•à²°à³ 1</translation>
<translation id="1285320974508926690">ಈ ಸೈಟೠಅನà³à²¨à³ ಎಂದಿಗೂ ಭಾಷಾಂತರಿಸದಿರಿ</translation>
+<translation id="1288548991597756084">ಕಾರà³à²¡à³ ಅನà³à²¨à³ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿ ಉಳಿಸಿ</translation>
<translation id="1292571435393770077">ಟà³à²°à³‡ 16</translation>
<translation id="1292701964462482250">"ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿ ವೆಬà³â€Œà²—ೆ ಸಂಪರà³à²•à²¿à²¸à³à²µ Chrome ನ ಕಾರà³à²¯à²µà²¨à³à²¨à³ ನಿಮà³à²® ಕಂಪà³à²¯à³‚ಟರà³â€Œà²¨à²²à³à²²à²¿à²°à³à²µ ಸಾಫà³à²Ÿà³â€Œà²µà³‡à²°à³â€Œ ಸà³à²¥à²—ಿತಗೊಳಿಸಿದೆ" (Windows ಕಂಪà³à²¯à³‚ಟರà³â€Œà²—ಳಿಗೆ ಮಾತà³à²°)</translation>
<translation id="1294154142200295408">ಕಮಾಂಡà³-ಲೈನೠವà³à²¯à²¤à³à²¯à²¾à²¸à²—ಳà³</translation>
@@ -222,6 +224,7 @@
<translation id="1507202001669085618">&lt;p&gt;ನೀವೠಆನà³â€Œà²²à³ˆà²¨à³ ಪà³à²°à²µà³‡à²¶à²¿à²¸à³à²µ ಮೊದಲೠಸೈನà³â€Œ ಇನà³â€Œ ಮಾಡಬೇಕಾದ ವೈ-ಫೈ ಪೋರà³à²Ÿà²²à³ ಅನà³à²¨à³ ಬಳಸà³à²¤à³à²¤à²¿à²¦à³à²¦à²°à³† ಈ ದೋಷವನà³à²¨à³ ಕಾಣà³à²¤à³à²¤à³€à²°à²¿.&lt;/p&gt; &lt;p&gt;ನೀವೠತೆರೆಯಲೠಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à³à²¤à³à²¤à²¿à²°à³à²µ ಪà³à²Ÿà²¦à²²à³à²²à²¿ ದೋಷವನà³à²¨à³ ಸರಿಪಡಿಸಲೠ&lt;strong&gt;ಸಂಪರà³à²•à²¿à²¸à²¿&lt;/strong&gt; ಅನà³à²¨à³ ಕà³à²²à²¿à²•à³â€Œ ಮಾಡಿ.&lt;/p&gt;</translation>
<translation id="1507780850870535225">ಲà³à²¯à²¾à²‚ಡà³â€Œà²¸à³à²•à³‡à²ªà³ ವಿನà³à²¯à²¾à²¸</translation>
<translation id="1513706915089223971">ಇತಿಹಾಸ ನಮೂದà³à²—ಳ ಪಟà³à²Ÿà²¿</translation>
+<translation id="1516097932025103760">ಇದನà³à²¨à³ ಎನà³â€Œà²•à³à²°à²¿à²ªà³à²Ÿà³ ಮಾಡಲಾಗಿದೆ ಮತà³à²¤à³ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿ ಸಂಗà³à²°à²¹à²¿à²¸à²²à²¾à²—ಿದೆ ಮತà³à²¤à³ CVC ಅನà³à²¨à³ ಎಂದಿಗೂ ಸಂಗà³à²°à²¹à²¿à²¸à²²à²¾à²—à³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="1517433312004943670">ಫೋನೠಸಂಖà³à²¯à³† ಅಗತà³à²¯à²µà²¿à²¦à³†</translation>
<translation id="1519264250979466059">ಬಿಲà³à²¡à³ ಡೇಟಾ</translation>
<translation id="1521159554480556801">ನಾರೠಮತà³à²¤à³ ಜವಳಿ ಕಲಾಕೃತಿಗಳà³</translation>
@@ -287,6 +290,7 @@
<translation id="1662550410081243962">ಪಾವತಿ ವಿಧಾನಗಳನà³à²¨à³ ಉಳಿಸಿ ಮತà³à²¤à³ ಭರà³à²¤à²¿ ಮಾಡಿ</translation>
<translation id="1663943134801823270">ಕಾರà³à²¡à³â€Œà²—ಳೠಮತà³à²¤à³ ವಿಳಾಸಗಳನà³à²¨à³ Chrome ನಿಂದ ಪಡೆಯಲಾಗಿದೆ. ನೀವೠಅವà³à²—ಳನà³à²¨à³ <ph name="BEGIN_LINK" />ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳಲà³à²²à²¿<ph name="END_LINK" /> ನಿರà³à²µà²¹à²¿à²¸à²¬à²¹à³à²¦à³.</translation>
<translation id="1671391448414634642">ಇಂದಿನಿಂದ <ph name="SOURCE_LANGUAGE" />ಭಾಷೆಯಲà³à²²à²¿à²°à³à²µ ಪà³à²Ÿà²—ಳನà³à²¨à³ <ph name="TARGET_LANGUAGE" />ಭಾಷೆಗೆ ಅನà³à²µà²¾à²¦ ಮಾಡಲಾಗà³à²µà³à²¦à³.</translation>
+<translation id="1673886523110456987">ಆಫರೠಅನà³à²¨à³ ಬಳಸಲೠ<ph name="CARD_DETAIL" /> ಮೂಲಕ ಚೆಕೠಔಟೠಮಾಡಿ</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> ನಿಂದ <ph name="TARGET_LANGUAGE" /> ಗೆ</translation>
<translation id="1682696192498422849">ಚಿಕà³à²• ಅಂಚೠಮೊದಲà³</translation>
<translation id="168693727862418163">ಈ ನೀತಿ ಮೌಲà³à²¯à²µà²¨à³à²¨à³ ಅದರ ರೂಪà³à²°à³‡à²·à³† ವಿರà³à²¦à³à²§ ಮೌಲà³à²¯à³€à²•à²°à²¿à²¸à²²à³ ವಿಫಲವಾಗಿದೆ ಮತà³à²¤à³ ಅದನà³à²¨à³ ನಿರà³à²²à²•à³à²·à²¿à²¸à²²à²¾à²—à³à²¤à³à²¤à²¦à³†.</translation>
@@ -305,6 +309,7 @@
<translation id="1717218214683051432">ಮೋಷನೠಸೆನà³à²¸à²¾à²°à³â€Œà²—ಳà³</translation>
<translation id="1717494416764505390">ಮೇಲà³â€Œà²¬à²¾à²•à³à²¸à³ 3</translation>
<translation id="1718029547804390981">ಟಿಪà³à²ªà²£à²¿ ಮಾಡಲೠಡಾಕà³à²¯à³à²®à³†à²‚ಟà³â€Œ ತà³à²‚ಬಾ ದೊಡà³à²¡à²¦à²¾à²—ಿದೆ</translation>
+<translation id="1720941539803966190">ಟà³à²¯à³à²Ÿà³‹à²°à²¿à²¯à²²à³ ಮà³à²šà³à²šà²¿</translation>
<translation id="1721424275792716183">* ಈ ಫೀಲà³à²¡à³ ಅಗತà³à²¯à²µà²¿à²¦à³†</translation>
<translation id="1727613060316725209">ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²° ಮಾನà³à²¯à²µà²¾à²—ಿದೆ</translation>
<translation id="1727741090716970331">ಮಾನà³à²¯à²µà²¾à²¦ ಕಾರà³à²¡à³ ಸಂಖà³à²¯à³†à²¯à²¨à³à²¨à³ ಸೇರಿಸಿ</translation>
@@ -420,8 +425,8 @@
<translation id="205212645995975601">BBQ ಮತà³à²¤à³ ಗà³à²°à²¿à²²à³à²²à²¿à²‚ಗà³</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" />ಭಾಷೆಯಲà³à²²à²¿à²°à³à²µ ಪà³à²Ÿà²—ಳೠಅನà³à²µà²¾à²¦à²•à³à²•à³Šà²³à²ªà²¡à³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ಈ ನಿಯಂತà³à²°à²£à²µà³ ಆನೠಆಗಿದà³à²¦à³ ಹಾಗೂ ಸà³à²¥à²¿à²¤à²¿à²¯à³ ಸಕà³à²°à²¿à²¯à²µà²¾à²—ಿದà³à²¦à²¾à²—, ಯಾವ ಜನರ ದೊಡà³à²¡ ಗà³à²‚ಪೠಅಥವಾ ಜನರ ತಂಡಕà³à²•à³† ನಿಮà³à²® ಇತà³à²¤à³€à²šà²¿à²¨ ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಚಟà³à²µà²Ÿà²¿à²•à³†à²¯à²¨à³à²¨à³ ಹೋಲà³à²¤à³à²¤à²¦à³† ಎಂದೠChrome ನಿರà³à²§à²°à²¿à²¸à³à²¤à³à²¤à²¦à³†. ಜಾಹೀರಾತà³à²¦à²¾à²°à²°à³ ಗà³à²‚ಪಿಗಾಗಿ ಜಾಹೀರಾತà³à²—ಳನà³à²¨à³ ಆಯà³à²•à³† ಮಾಡಬಹà³à²¦à³ ಹಾಗೂ ನಿಮà³à²® ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಚಟà³à²µà²Ÿà²¿à²•à³†à²¯à²¨à³à²¨à³ ನಿಮà³à²® ಸಾಧನದಲà³à²²à²¿ ಖಾಸಗಿಯಾಗಿ ಇರಿಸಲಾಗà³à²¤à³à²¤à²¦à³†. ನಿಮà³à²® ಗà³à²‚ಪನà³à²¨à³ ಪà³à²°à²¤à²¿à²¦à²¿à²¨ ಅಪà³â€Œà²¡à³‡à²Ÿà³ ಮಾಡಲಾಗà³à²¤à³à²¤à²¦à³†.}=1{ಈ ನಿಯಂತà³à²°à²£à²µà³ ಆನೠಆಗಿದà³à²¦à³ ಹಾಗೂ ಸà³à²¥à²¿à²¤à²¿à²¯à³ ಸಕà³à²°à²¿à²¯à²µà²¾à²—ಿದà³à²¦à²¾à²—, ಯಾವ ಜನರ ದೊಡà³à²¡ ಗà³à²‚ಪೠಅಥವಾ ಜನರ ತಂಡಕà³à²•à³† ನಿಮà³à²® ಇತà³à²¤à³€à²šà²¿à²¨ ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಚಟà³à²µà²Ÿà²¿à²•à³†à²¯à²¨à³à²¨à³ ಹೋಲà³à²¤à³à²¤à²¦à³† ಎಂದೠChrome ನಿರà³à²§à²°à²¿à²¸à³à²¤à³à²¤à²¦à³†. ಜಾಹೀರಾತà³à²¦à²¾à²°à²°à³ ಗà³à²‚ಪಿಗಾಗಿ ಜಾಹೀರಾತà³à²—ಳನà³à²¨à³ ಆಯà³à²•à³† ಮಾಡಬಹà³à²¦à³ ಹಾಗೂ ನಿಮà³à²® ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಚಟà³à²µà²Ÿà²¿à²•à³†à²¯à²¨à³à²¨à³ ನಿಮà³à²® ಸಾಧನದಲà³à²²à²¿ ಖಾಸಗಿಯಾಗಿ ಇರಿಸಲಾಗà³à²¤à³à²¤à²¦à³†. ನಿಮà³à²® ಗà³à²‚ಪನà³à²¨à³ ಪà³à²°à²¤à²¿à²¦à²¿à²¨ ಅಪà³â€Œà²¡à³‡à²Ÿà³ ಮಾಡಲಾಗà³à²¤à³à²¤à²¦à³†.}one{ಈ ನಿಯಂತà³à²°à²£à²µà³ ಆನೠಆಗಿದà³à²¦à³ ಹಾಗೂ ಸà³à²¥à²¿à²¤à²¿à²¯à³ ಸಕà³à²°à²¿à²¯à²µà²¾à²—ಿದà³à²¦à²¾à²—, ಯಾವ ಜನರ ದೊಡà³à²¡ ಗà³à²‚ಪೠಅಥವಾ ಜನರ ತಂಡಕà³à²•à³† ನಿಮà³à²® ಇತà³à²¤à³€à²šà²¿à²¨ ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಚಟà³à²µà²Ÿà²¿à²•à³†à²¯à²¨à³à²¨à³ ಹೋಲà³à²¤à³à²¤à²¦à³† ಎಂದೠChrome ನಿರà³à²§à²°à²¿à²¸à³à²¤à³à²¤à²¦à³†. ಜಾಹೀರಾತà³à²¦à²¾à²°à²°à³ ಗà³à²‚ಪಿಗಾಗಿ ಜಾಹೀರಾತà³à²—ಳನà³à²¨à³ ಆಯà³à²•à³† ಮಾಡಬಹà³à²¦à³ ಹಾಗೂ ನಿಮà³à²® ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಚಟà³à²µà²Ÿà²¿à²•à³†à²¯à²¨à³à²¨à³ ನಿಮà³à²® ಸಾಧನದಲà³à²²à²¿ ಖಾಸಗಿಯಾಗಿ ಇರಿಸಲಾಗà³à²¤à³à²¤à²¦à³†. ನಿಮà³à²® ಗà³à²‚ಪನà³à²¨à³ ಪà³à²°à²¤à²¿ {NUM_DAYS} ದಿನಗಳಿಗೆ ಅಪà³â€Œà²¡à³‡à²Ÿà³ ಮಾಡಲಾಗà³à²¤à³à²¤à²¦à³†.}other{ಈ ನಿಯಂತà³à²°à²£à²µà³ ಆನೠಆಗಿದà³à²¦à³ ಹಾಗೂ ಸà³à²¥à²¿à²¤à²¿à²¯à³ ಸಕà³à²°à²¿à²¯à²µà²¾à²—ಿದà³à²¦à²¾à²—, ಯಾವ ಜನರ ದೊಡà³à²¡ ಗà³à²‚ಪೠಅಥವಾ ಜನರ ತಂಡಕà³à²•à³† ನಿಮà³à²® ಇತà³à²¤à³€à²šà²¿à²¨ ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಚಟà³à²µà²Ÿà²¿à²•à³†à²¯à²¨à³à²¨à³ ಹೋಲà³à²¤à³à²¤à²¦à³† ಎಂದೠChrome ನಿರà³à²§à²°à²¿à²¸à³à²¤à³à²¤à²¦à³†. ಜಾಹೀರಾತà³à²¦à²¾à²°à²°à³ ಗà³à²‚ಪಿಗಾಗಿ ಜಾಹೀರಾತà³à²—ಳನà³à²¨à³ ಆಯà³à²•à³† ಮಾಡಬಹà³à²¦à³ ಹಾಗೂ ನಿಮà³à²® ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಚಟà³à²µà²Ÿà²¿à²•à³†à²¯à²¨à³à²¨à³ ನಿಮà³à²® ಸಾಧನದಲà³à²²à²¿ ಖಾಸಗಿಯಾಗಿ ಇರಿಸಲಾಗà³à²¤à³à²¤à²¦à³†. ನಿಮà³à²® ಗà³à²‚ಪನà³à²¨à³ ಪà³à²°à²¤à²¿ {NUM_DAYS} ದಿನಗಳಿಗೆ ಅಪà³â€Œà²¡à³‡à²Ÿà³ ಮಾಡಲಾಗà³à²¤à³à²¤à²¦à³†.}}</translation>
-<translation id="2053553514270667976">ಪಿನೠಕೋಡà³</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 ಸಲಹೆ}one{# ಸಲಹೆಗಳà³}other{# ಸಲಹೆಗಳà³}}</translation>
+<translation id="2066915425250589881">ಅಳಿಸà³à²µà²‚ತೆ ವಿನಂತಿಸಿಕೊಳà³à²³à²¬à²¹à³à²¦à³</translation>
<translation id="2068528718802935086">ಶಿಶà³à²—ಳೠಮತà³à²¤à³ ತೊಡರà³à²—ಾಲಿಡà³à²µ ಮಕà³à²•à²³à³</translation>
<translation id="2071156619270205202">ಈ ಕಾರà³à²¡à³, ವರà³à²šà³à²µà²²à³ ಕಾರà³à²¡à³ ಸಂಖà³à²¯à³†à²—ೆ ಅರà³à²¹à²µà²¾à²—ಿಲà³à²².</translation>
<translation id="2071692954027939183">ಅಧಿಸೂಚನೆಗಳನà³à²¨à³ ಸà³à²µà²¯à²‚ಚಾಲಿತವಾಗಿ ನಿರà³à²¬à²‚ಧಿಸಲಾಗಿದೆ à²à²•à³†à²‚ದರೆ ನೀವೠಸಾಮಾನà³à²¯à²µà²¾à²—ಿ ಅವà³à²—ಳನà³à²¨à³ ಅನà³à²®à²¤à²¿à²¸à³à²µà³à²¦à²¿à²²à³à²²</translation>
@@ -433,7 +438,6 @@
<translation id="2088086323192747268">ಸಿಂಕೠಬಟನೠಅನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿, Chrome ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳಲà³à²²à²¿ ನೀವೠಯಾವ ಮಾಹಿತಿಯನà³à²¨à³ ಸಿಂಕೠಮಾಡà³à²¤à³à²¤à³€à²°à²¿ ಎಂಬà³à²¦à²¨à³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²²à³ Enter ಒತà³à²¤à²¿</translation>
<translation id="2091887806945687916">ಶಬà³à²§</translation>
<translation id="2094505752054353250">ಡೊಮೇನೠಹೊಂದà³à²¤à³à²¤à²¿à²²à³à²²</translation>
-<translation id="2096368010154057602">ವಿಭಾಗ</translation>
<translation id="2099652385553570808">ಎಡಭಾಗದಲà³à²²à²¿ ಮೂರೠಸà³à²Ÿà³‡à²ªà²²à³ ಹಾಕಿ</translation>
<translation id="2101225219012730419">ಆವೃತà³à²¤à²¿:</translation>
<translation id="2102134110707549001">ಸದೃಢವಾದ ಪಾಸà³â€Œà²µà²°à³à²¡à³ ಸೂಚಿಸಿ…</translation>
@@ -470,7 +474,6 @@
<translation id="2185836064961771414">ಅಮೆರಿಕನೠಫà³à²Ÿà³â€Œà²¬à²¾à²²à³</translation>
<translation id="2187317261103489799">ಪತà³à²¤à³† ಮಾಡಿ (ಡಿಫಾಲà³à²Ÿà³)</translation>
<translation id="2188375229972301266">ಕೆಳಭಾಗದಲà³à²²à²¿ ಅನೇಕ ತೂತà³à²—ಳನà³à²¨à³ ಮಾಡಿ</translation>
-<translation id="2188852899391513400">ನೀವೠಈಗಷà³à²Ÿà³‡ ಬಳಸಿದ ಪಾಸà³â€Œà²µà²°à³à²¡à³ ಡೇಟಾ ಉಲà³à²²à²‚ಘನೆಯಲà³à²²à²¿ ಕಂಡà³à²¬à²‚ದಿದೆ. ನಿಮà³à²® ಖಾತೆಗಳನà³à²¨à³ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿರಿಸಲà³, Google ಪಾಸà³â€Œà²µà²°à³à²¡à³ ನಿರà³à²µà²¾à²¹à²•à²µà³ ಅದನà³à²¨à³ ಈಗಲೇ ಬದಲಾಯಿಸಲೠಮತà³à²¤à³ ನಂತರ ನಿಮà³à²® ಉಳಿಸಿದ ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳನà³à²¨à³ ಪರಿಶೀಲಿಸಲೠಶಿಫಾರಸೠಮಾಡà³à²¤à³à²¤à²¦à³†.</translation>
<translation id="219906046732893612">ಗೃಹ ಅಭಿವೃದà³à²§à²¿</translation>
<translation id="2202020181578195191">ಮಾನà³à²¯à²µà²¾à²¦ ಅವಧಿ-ಮà³à²•à³à²¤à²¾à²¯ ವರà³à²·à²µà²¨à³à²¨à³ ನಮೂದಿಸಿ</translation>
<translation id="22081806969704220">ಟà³à²°à³‡ 3</translation>
@@ -481,6 +484,8 @@
<translation id="2215727959747642672">ಫೈಲೠಎಡಿಟೠಮಾಡà³à²µà²¿à²•à³†</translation>
<translation id="2215963164070968490">ನಾಯಿಗಳà³</translation>
<translation id="2218879909401188352">ದಾಳಿಕೋರರೠಪà³à²°à²¸à³à²¤à³à²¤ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ನಲà³à²²à²¿à²¦à³à²¦à²¾à²°à³† ಮತà³à²¤à³ ನಿಮà³à²® ಸಾಧನಕà³à²•à³† ಹಾನಿಯನà³à²¨à³à²‚ಟೠಮಾಡà³à²µ ಅಪಾಯಕಾರಿ ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³â€Œà²—ಳನà³à²¨à³ ಇನà³â€Œà²¸à³à²Ÿà²¾à²²à³ ಮಾಡಬಹà³à²¦à³, ನಿಮà³à²® ಮೊಬೈಲೠಬಿಲà³â€Œà²—ೆ ಮರೆಮಾಡಿದ ಶà³à²²à³à²•à²—ಳನà³à²¨à³ ಸೇರಿಸಬಹà³à²¦à³, ಅಥವಾ ನಿಮà³à²® ವೈಯಕà³à²¤à²¿à²• ಮಾಹಿತಿಯನà³à²¨à³ ಕದಿಯಬಹà³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ಟà³à²Ÿà³‹à²°à²¿à²¯à²²à³ ಅನà³à²¨à³ ಮರà³à²ªà³à²°à²¾à²°à²‚ಭಿಸಿ</translation>
+<translation id="2219735899272417925">ಸಾಧನವನà³à²¨à³ ರೀಸೆಟೠಮಾಡà³à²µ ಅಗತà³à²¯à²µà²¿à²¦à³†</translation>
<translation id="2224337661447660594">ಇಂಟರà³à²¨à³†à²Ÿà³ ಇಲà³à²²</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />ಡಯಾಗà³à²¨à²¸à³à²Ÿà²¿à²•à³à²¸à³â€Œâ€Œ ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³â€Œ<ph name="END_LINK" /> ಬಳಸಿಕೊಂಡೠನಿಮà³à²® ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಸರಿಪಡಿಸಿ</translation>
<translation id="2239100178324503013">ಈಗ ಕಳà³à²¹à²¿à²¸à²¿</translation>
@@ -578,11 +583,13 @@
<translation id="2512101340618156538">ಅನà³à²®à²¤à²¿ ಇಲà³à²² (ಡೀಫಾಲà³à²Ÿà³)</translation>
<translation id="2512413427717747692">Chrome ಅನà³à²¨à³ ಡೀಫಾಲà³à²Ÿà³ ಬà³à²°à³Œà²¸à²°à³ ಬಟನೠಆಗಿ ಹೊಂದಿಸಿ ಮತà³à²¤à³ iOS ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳಲà³à²²à²¿ Chrome ಅನà³à²¨à³ ಸಿಸà³à²Ÿà²‚ನ ಡೀಫಾಲà³à²Ÿà³ ಬà³à²°à³Œà²¸à²°à³ ಆಗಿ ಹೊಂದಿಸಲೠEnter ಒತà³à²¤à²¿à²°à²¿</translation>
<translation id="2515629240566999685">ನಿಮà³à²® ಪà³à²°à²¦à³†à³•à²¶à²¦à²²à³à²²à²¿à²¨ ಸಿಗà³à²¨à²²à³ ಪರಿಶೀಲಿಸಲಾಗà³à²¤à³à²¤à²¿à²¦à³†</translation>
+<translation id="2515761554693942801">ನೀವೠ<ph name="PROVIDER_ORIGIN" /> ಬಳಸà³à²µ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ಳಲà³à²²à²¿ ಸà³à²ªà²°à³à²¶ ID ಯ ಮೂಲಕ ದೃಢೀಕರಿಸà³à²µ ಆಯà³à²•à³†à²¯à²¨à³à²¨à³ ಮಾಡಿದà³à²¦à³€à²°à²¿. ಈ ಪೂರೈಕೆದಾರರೠನಿಮà³à²® ಪಾವತಿ ವಿಧಾನದ ಕà³à²°à²¿à²¤à³ ಮಾಹಿತಿಯನà³à²¨à³ ಸಂಗà³à²°à²¹à²¿à²¸à²¿à²Ÿà³à²Ÿà²¿à²°à²¬à²¹à³à²¦à³, ನೀವೠಅದನà³à²¨à³ <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">ಕೆಳಗಿನ ಬಲಭಾಗದಲà³à²²à²¿ ಸà³à²Ÿà³‡à²ªà²²à³ ಹಾಕಿ</translation>
<translation id="2521736961081452453">ಫಾರà³à²®à³ ಆನà³à²¨à³ ರಚಿಸಿ</translation>
<translation id="2523886232349826891">ಈ ಸಾಧನದಲà³à²²à²¿ ಮಾತà³à²° ಉಳಿಸಲಾಗಿದೆ</translation>
<translation id="2524461107774643265">ಇನà³à²¨à²·à³à²Ÿà³ ಮಾಹಿತಿಯನà³à²¨à³ ಸೇರಿಸಿ</translation>
<translation id="2529899080962247600">ಈ ಕà³à²·à³‡à²¤à³à²°à²µà³ <ph name="MAX_ITEMS_LIMIT" /> ಕà³à²•à²¿à²‚ತ ಹೆಚà³à²šà²¿à²¨ ನಮೂದà³à²—ಳನà³à²¨à³ ಹೊಂದಿರà³à²µà²‚ತಿಲà³à²². ಮà³à²‚ದಿನ ಎಲà³à²²à²¾ ನಮೂದà³à²—ಳನà³à²¨à³ ನಿರà³à²²à²•à³à²·à²¿à²¸à²²à²¾à²—à³à²¤à³à²¤à²¦à³†.</translation>
+<translation id="253493526287553278">ಪà³à²°à³‹à²®à³‹ ಕೋಡೠವಿವರಗಳನà³à²¨à³ ನೋಡಿ</translation>
<translation id="2535585790302968248">ಖಾಸಗಿಯಾಗಿ ಬà³à²°à³Œà²¸à³ ಮಾಡಲೠಹೊಸ ಅಜà³à²žà²¾à²¤ ಟà³à²¯à²¾à²¬à³ ತೆರೆಯಿರಿ</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{ಮತà³à²¤à³ ಇನà³à²¨à³‚ 1}one{ಮತà³à²¤à³ ಇನà³à²¨à³‚ #}other{ಮತà³à²¤à³ ಇನà³à²¨à³‚ #}}</translation>
<translation id="2536110899380797252">ವಿಳಾಸವನà³à²¨à³ ಸೇರಿಸಿ</translation>
@@ -618,7 +625,6 @@
<translation id="259821504105826686">ಫೋಟೋಗà³à²°à²¾à²«à²¿à²•à³ ಮತà³à²¤à³ ಡಿಜಿಟಲೠಕಲೆಗಳà³</translation>
<translation id="2601150049980261779">ರೋಮà³à²¯à²¾à²¨à³à²¸à³ ಚಲನಚಿತà³à²°à²—ಳà³</translation>
<translation id="2604589665489080024">ಪಾಪೠಸಂಗೀತ</translation>
-<translation id="2609632851001447353">ಪರಿವರà³à²¤à²¨à³†à²—ಳà³</translation>
<translation id="2610561535971892504">ನಕಲಿಸಲೠಕà³à²²à²¿à²•à³ ಮಾಡಿ</translation>
<translation id="2617988307566202237">ಈ ಕೆಳಗಿನ ಮಾಹಿತಿಯನà³à²¨à³ Chrome <ph name="BEGIN_EMPHASIS" />ಉಳಿಸà³à²µà³à²¦à²¿à²²à³à²²<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -651,6 +657,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">ಜನà³à²®à²¦à²¿à²¨à²—ಳೠಮತà³à²¤à³ ನಾಮಕರಣದ ದಿನಗಳà³</translation>
<translation id="2677748264148917807">ತೊರೆಯಿರಿ</translation>
+<translation id="2679714844901977852">ಸà³à²°à²•à³à²·à²¿à²¤ ಮತà³à²¤à³ ತà³à²µà²°à²¿à²¤ ಚೆಕೠಔಟà³â€Œà²—ಳಿಗಾಗಿ ನಿಮà³à²® ಕಾರà³à²¡à³ ಮತà³à²¤à³ ಬಿಲà³à²²à²¿à²‚ಗೠಮಾಹಿತಿಯನà³à²¨à³ ನಿಮà³à²® Google ಖಾತೆ <ph name="USER_EMAIL" /> ನಲà³à²²à²¿ ಉಳಿಸಿ</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">ಅತà³à²¯à³à²¤à³à²¤à²® ಹೊಂದಿಕೆ</translation>
<translation id="2688969097326701645">ಹೌದà³, ಮà³à²‚ದà³à²µà²°à²¿à²¯à²¿à²°à²¿</translation>
@@ -699,7 +706,6 @@
<translation id="2854764410992194509">ಇಂಟರà³à²¨à³†à²Ÿà³ ಸೇವಾ ಪೂರೈಕೆದಾರರೠ(ISPs)</translation>
<translation id="2856444702002559011">ದಾಳಿಕೋರರೠ<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ನಿಂದ ನಿಮà³à²® ಮಾಹಿತಿಯನà³à²¨à³ ಕದಿಯಲೠಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³ (ಉದಾಹರಣೆಗೆ, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳà³, ಸಂದೇಶಗಳೠಅಥವಾ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³â€Œà²—ಳà³). <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ಅತಿಕà³à²°à²®à²£à²•à²¾à²°à²¿à²¯à²¾à²—ಿರà³à²µ ಅಥವಾ ತಪà³à²ªà³à²¦à²¾à²°à²¿à²—ೆಳೆಯà³à²µ ಜಾಹೀರಾತà³à²—ಳನà³à²¨à³ ಈ ಸೈಟೠತೋರಿಸà³à²¤à³à²¤à²¦à³†.</translation>
-<translation id="286512204874376891">ವರà³à²šà³à²µà²²à³ ಕಾರà³à²¡à³ ಸಂಭಾವà³à²¯ ವಂಚನೆಯಿಂದ ನಿಮà³à²®à²¨à³à²¨à³ ರಕà³à²·à²¿à²¸à²²à³ ನಿಮà³à²® ನಿಜವಾದ ಕಾರà³à²¡à³â€Œà²—ೆ ಮಾರà³à²µà³‡à²· ತೊಡಿಸà³à²¤à³à²¤à²¦à³†. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ಸೌಹಾರà³à²¦</translation>
<translation id="28761159517501904">ಚಲನಚಿತà³à²°à²—ಳà³</translation>
<translation id="2876489322757410363">ಬಾಹà³à²¯ ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³â€Œâ€Œ ಮೂಲಕ ಪಾವತಿಸಲೠಅಜà³à²žà²¾à²¤ ಮೋಡà³â€Œâ€Œ ತೊರೆಯಲಾಗà³à²¤à³à²¤à²¿à²¦à³†. ಮà³à²‚ದà³à²µà²°à²¿à²¸à²¬à³‡à²•à³†?</translation>
@@ -800,7 +806,6 @@
<translation id="3158539265159265653">ಡಿಸà³à²•à³</translation>
<translation id="3162559335345991374">ನೀವೠಬಳಸà³à²¤à³à²¤à²¿à²°à³à²µ ವೈ-ಫೈನ ಲಾಗಿನೠಪà³à²Ÿà²•à³à²•à³† ನೀವೠಭೇಟಿ ನೀಡà³à²µ ಅಗತà³à²¯à²µà²¿à²¦à³†.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">ದà³à²µà³€à²ª</translation>
<translation id="3176929007561373547">ಪà³à²°à²¾à²•à³à²¸à²¿ ಸರà³à²µà²°à³ ಕಾರà³à²¯à²µà²¨à²¿à²°à³à²µà²¹à²¿à²¸à³à²¤à³à²¤à²¿à²¦à³†à²¯à³‡ ಎಂಬà³à²¦à²¨à³à²¨à³ ಖಚಿತಪಡಿಸಿಕೊಳà³à²³à²²à³ ನಿಮà³à²® ಪà³à²°à²¾à²•à³à²¸à²¿ ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳನà³à²¨à³ ಪರಿಶೀಲಿಸಿ ಮತà³à²¤à³ ನಿಮà³à²® ನೆಟà³â€Œà²µà²°à³à²•à³ ನಿರà³à²µà²¾à²¹à²•à²°à²¨à³à²¨à³ ಸಂಪರà³à²•à²¿à²¸à²¿. ನೀವೠಪà³à²°à²¾à²•à³à²¸à²¿ ಸರà³à²µà²°à³ ಅನà³à²¨à³ ಬಳಸà³à²¤à³à²¤à²¿à²²à³à²² ಎಂಬ ಅನà³à²®à²¾à²¨ ನಿಮಗಿದà³à²¦à²°à³†:
<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">ನೀವೠಈ ಸಾಧನವನà³à²¨à³ ಸಕà³à²°à²¿à²¯à²µà²¾à²—ಿ ಬಳಸà³à²¤à³à²¤à²¿à²°à³à²µà²¾à²— ಸೈಟà³â€Œà²—ಳೠನಿಮà³à²® ಉಪಸà³à²¥à²¿à²¤à²¿à²¯ ಕà³à²°à²¿à²¤à³ ತಿಳಿದà³à²•à³Šà²³à³à²³à²²à³ ಬಯಸà³à²¤à³à²¤à²¦à³†</translation>
@@ -877,9 +882,6 @@
<translation id="3369192424181595722">ಗಡಿಯಾರ ದೋಷ</translation>
<translation id="3369459162151165748">ವಾಹನದ ಬಿಡಿಭಾಗಗಳೠಮತà³à²¤à³ ಪರಿಕರಗಳà³</translation>
<translation id="3371064404604898522">ಡಿಫಾಲà³à²Ÿà³ ಬà³à²°à³Œà²¸à²°à³â€Œà²¨ ರೀತಿಯಲà³à²²à²¿ Chrome ಅನà³à²¨à³ ಹೊಂದಿಸಿ</translation>
-<translation id="3371076217486966826"><ph name="URL" />, ಈ ಕೆಳಗಿನವà³à²—ಳನà³à²¨à³ ಮಾಡಲೠಬಯಸà³à²¤à³à²¤à²¦à³†:
- • ನಿಮà³à²® ಸà³à²¤à³à²¤à²®à³à²¤à³à²¤à²²à²¿à²¨ 3D ನಕà³à²·à³†à²¯à²¨à³à²¨à³ ರಚಿಸà³à²µà³à²¦à³ ಮತà³à²¤à³ ಕà³à²¯à²¾à²®à²°à²¾ ಸà³à²¥à²¿à²¤à²¿à²¯à²¨à³à²¨à³ ಟà³à²°à³à²¯à²¾à²•à³ ಮಾಡà³à²µà³à²¦à³
- • ನಿಮà³à²® ಕà³à²¯à²¾à²®à²°à²¾à²µà²¨à³à²¨à³ ಬಳಸà³à²µà³à²¦à³</translation>
<translation id="337363190475750230">ಅನà³à²®à²¤à²¿ ನಿರà³à²¬à²‚ಧಿಸಲಾಗಿದೆ</translation>
<translation id="3375754925484257129">Chrome ಸà³à²°à²•à³à²·à²¤à³†à²¯ ಪರಿಶೀಲನೆಯನà³à²¨à³ ರನೠಮಾಡಿ</translation>
<translation id="3377144306166885718">ಸರà³à²µà²°à³ ಬಳಸà³à²µ TLS ಆವೃತà³à²¤à²¿à²¯à³ ಹಳೆಯದಾಗಿದೆ.</translation>
@@ -896,6 +898,7 @@
<translation id="3399952811970034796">ವಿತರಣೆಯ ವಿಳಾಸಗಳà³</translation>
<translation id="3402261774528610252">ಈ ಸೈಟೠಅನà³à²¨à³ ಲೋಡೠಮಾಡಲೠಬಳಸà³à²µ ಕನೆಕà³à²·à²¨à³, TLS 1.0 ಅಥವಾ TLS 1.1 ಅನà³à²¨à³ ಬಳಸà³à²¤à³à²¤à²¦à³†, ಆದರೆ ಈ ಎರಡೂ ಆವೃತà³à²¤à²¿à²—ಳಿಗೆ ಸಮà³à²®à²¤à²¿à²¸à²¿à²²à³à²² ಹಾಗೂ ಮà³à²‚ದಿನ ದಿನಗಳಲà³à²²à²¿ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲಾಗà³à²¤à³à²¤à²¦à³†. ಒಮà³à²®à³† ಅವà³à²—ಳನà³à²¨à³ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಿದ ನಂತರ, ಬಳಕೆದಾರರೠಇನà³à²¨à³ ಮà³à²‚ದೆ ಈ ಸೈಟà³â€Œ ಅನà³à²¨à³ ಲೋಡೠಮಾಡಲೠಸಾಧà³à²¯à²µà²¾à²—à³à²µà³à²¦à²¿à²²à³à²². TLS 1.2 ಅಥವಾ ನಂತರದ ಆವೃತà³à²¤à²¿à²¯à²¨à³à²¨à³ ಸರà³à²µà²°à³ ಸಕà³à²°à²¿à²¯à²—ೊಳಿಸಬೇಕà³.</translation>
<translation id="3405664148539009465">ಫಾಂಟà³â€Œà²—ಳನà³à²¨à³ ಗà³à²°à²¾à²¹à²•à³€à²¯à²—ೊಳಿಸಿ</translation>
+<translation id="3407789382767355356">ಥರà³à²¡à³-ಪಾರà³à²Ÿà²¿ ಸೈನೠಇನà³</translation>
<translation id="3409896703495473338">ಭದà³à²°à²¤à³† ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿</translation>
<translation id="3414952576877147120">ಗಾತà³à²°:</translation>
<translation id="3417660076059365994">ನೀವೠಅಪà³â€Œà²²à³‹à²¡à³ ಮಾಡà³à²µ ಅಥವಾ ಲಗತà³à²¤à²¿à²¸à³à²µ ಫೈಲà³â€Œà²—ಳನà³à²¨à³ ವಿಶà³à²²à³‡à²·à²£à³† ಮಾಡಲà³, ಅವà³à²—ಳನà³à²¨à³ Google ಕà³à²²à³Œà²¡à³ ಅಥವಾ ಥರà³à²¡à³ ಪಾರà³à²Ÿà²¿à²—ಳಿಗೆ ಕಳà³à²¹à²¿à²¸à²²à²¾à²—à³à²¤à³à²¤à²¦à³†. ಉದಾಹರಣೆಗೆ, ಸೂಕà³à²·à³à²®à²µà²¾à²¦ ವೈಯಕà³à²¤à²¿à²• ಡೇಟಾ ಅಥವಾ ಮಾಲà³â€Œà²µà³‡à²°à³ ಅನà³à²¨à³ ಪತà³à²¤à³†à²¹à²šà³à²šà²²à³ ಅವà³à²—ಳನà³à²¨à³ ಸà³à²•à³à²¯à²¾à²¨à³ ಮಾಡಬಹà³à²¦à³.</translation>
@@ -928,6 +931,7 @@
<translation id="3477679029130949506">ಚಲನಚಿತà³à²° ಲಿಸà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳೠಮತà³à²¤à³ ಥಿಯೇಟರೠಶೋಟೈಮà³â€Œà²—ಳà³</translation>
<translation id="3479552764303398839">ಈಗ ಬೇಡ</translation>
<translation id="3484560055331845446">ನಿಮà³à²® Google ಖಾತೆಗೆ ನೀವೠಪà³à²°à²µà³‡à²¶à²µà²¨à³à²¨à³ ಕಳೆದà³à²•à³Šà²³à³à²³à²¬à²¹à³à²¦à³. ನಿಮà³à²® ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œ ಅನà³à²¨à³ ಈಗಲೇ ಬದಲಾಯಿಸà³à²µà²‚ತೆ Chrome ಶಿಫಾರಸೠಮಾಡà³à²¤à³à²¤à²¦à³†. ಸೈನೠಇನೠಮಾಡಲೠನಿಮà³à²®à²¨à³à²¨à³ ಕೇಳಲಾಗà³à²¤à³à²¤à²¦à³†.</translation>
+<translation id="3484861421501147767">ರಿಮೈಂಡರà³: ಉಳಿಸಲಾದ ಪà³à²°à³Šà²®à³Š ಕೋಡೠಲಭà³à²¯à²µà²¿à²¦à³†</translation>
<translation id="3487845404393360112">ಟà³à²°à³‡ 4</translation>
<translation id="3495081129428749620">ಪà³à²Ÿà²¦à²²à³à²²à²¿ ಹà³à²¡à³à²•à²¿
<ph name="PAGE_TITLE" /></translation>
@@ -1051,6 +1055,7 @@
<translation id="3810973564298564668">ನಿರà³à²µà²¹à²¿à²¸à²¿</translation>
<translation id="3816482573645936981">ಮೌಲà³à²¯ (ರದà³à²¦à³à²—ೊಳಿಸಿರà³à²µà³à²¦à³)</translation>
<translation id="382518646247711829">ನೀವೠಪà³à²°à²¾à²•à³à²¸à²¿ ಸರà³à²µà²°à³ ಬಳಸಿದರೆ...</translation>
+<translation id="3826050100957962900">ಥರà³à²¡à³-ಪಾರà³à²Ÿà²¿ ಸೈನೠಇನà³</translation>
<translation id="3827112369919217609">ಪರಿಪೂರà³à²£</translation>
<translation id="3827666161959873541">ಕೌಟà³à²‚ಬಿಕ ಚಲನಚಿತà³à²°à²—ಳà³</translation>
<translation id="3828924085048779000">ಖಾಲಿ ಪಾಸà³â€Œà²«à³à²°à³‡à²¸à³ ಅನà³à²¨à³ ಅನà³à²®à²¤à²¿à²¸à³à²µà³à²¦à²¿à²²à³à²².</translation>
@@ -1063,9 +1068,9 @@
<translation id="3858027520442213535">ದಿನಾಂಕ ಮತà³à²¤à³ ಸಮಯವನà³à²¨à³ ಅಪà³â€Œà²¡à³‡à²Ÿà³â€Œ ಮಾಡಿ</translation>
<translation id="3858860766373142691">ಹೆಸರà³</translation>
<translation id="3872834068356954457">ವಿಜà³à²žà²¾à²¨</translation>
+<translation id="3875783148670536197">ಹೇಗೆಂದೠನನಗೆ ತೋರಿಸಿ</translation>
<translation id="3881478300875776315">ಕೆಲವೇ ಸಾಲà³à²—ಳನà³à²¨à³ ತೋರಿಸಿ</translation>
<translation id="3884278016824448484">ಸಂಘರà³à²·à²—ೊಳà³à²³à³à²¤à³à²¤à²¿à²°à³à²µ ಸಾಧನ ಗà³à²°à³à²¤à²¿à²¸à³à²µà²¿à²•à³†</translation>
-<translation id="3885155851504623709">ಪಾರಿಷà³</translation>
<translation id="388632593194507180">ನಿಗಾ ಇಡà³à²¤à³à²¤à²¿à²°à³à²µà³à²¦à²¨à³à²¨à³ ಪತà³à²¤à³†à²¹à²šà³à²šà²²à²¾à²—ಿದೆ</translation>
<translation id="3886948180919384617">ಸà³à²Ÿà³à²¯à²¾à²•à²°à³ 3</translation>
<translation id="3890664840433101773">ಇಮೇಲೠಸೇರಿಸಿ</translation>
@@ -1095,9 +1100,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> ಅನà³à²¨à³ ನಿರà³à²¬à²‚ಧಿಸಲಾಗಿದೆ</translation>
<translation id="3978338123949022456">ಹà³à²¡à³à²•à²¾à²Ÿ ಮೋಡà³, ಪà³à²°à²¶à³à²¨à³†à²¯à²¨à³à²¨à³ ಟೈಪೠಮಾಡಿ ಮತà³à²¤à³ <ph name="KEYWORD_SUFFIX" /> ಮೂಲಕ ಹà³à²¡à³à²•à²²à³ Enter ಒತà³à²¤à²¿à²°à²¿</translation>
<translation id="398470910934384994">ಪಕà³à²·à²¿à²—ಳà³</translation>
+<translation id="3985750352229496475">ವಿಳಾಸಗಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿...</translation>
<translation id="3986705137476756801">ಈಗ ಲೈವೠಕà³à²¯à²¾à²ªà³à²¶à²¨à³ ಅನà³à²¨à³ ಆಫೠಮಾಡಿ</translation>
<translation id="3987940399970879459">1 MB ಗಿಂತ ಕಡಿಮೆ</translation>
<translation id="3990250421422698716">ಜಾಗೠಆಫà³â€Œà²¸à³†à²Ÿà³</translation>
+<translation id="3992684624889376114">ಈ ಪà³à²Ÿà²¦ ಕà³à²°à²¿à²¤à³</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> ಸೈಟೠತನà³à²¨ ಎಲà³à²²à²¾ ವಿನಂತಿಗಳಿಗೆ ಮೂಲ ಕಾರà³à²¯à²¨à³€à²¤à²¿à²¯à³Šà²‚ದನà³à²¨à³ ಅನà³à²µà²¯à²¿à²¸à²²à³ ವಿನಂತಿಸಿದೆ, ಆದರೆ ಈ ಕಾರà³à²¯à²¨à³€à²¤à²¿à²¯à²¨à³à²¨à³ ಪà³à²°à²¸à³à²¤à³à²¤à²µà²¾à²—ಿ ಅನà³à²µà²¯à²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¾à²—à³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="4006465311664329701">Google Pay ಅನà³à²¨à³ ಬಳಸà³à²¤à³à²¤à²¿à²°à³à²µ ಪಾವತಿ ವಿಧಾನಗಳà³, ಆಫರà³â€Œà²—ಳೠಮತà³à²¤à³ ವಿಳಾಸಗಳà³</translation>
<translation id="4009243425692662128">ನೀವೠಪà³à²°à²¿à²‚ಟೠಮಾಡà³à²µ ಪà³à²Ÿà²—ಳ ವಿಷಯವನà³à²¨à³ ವಿಶà³à²²à³‡à²·à²£à³†à²—ಾಗಿ Google ಕà³à²²à³Œà²¡à³â€Œ ಅಥವಾ ಮೂರನೇ ವà³à²¯à²•à³à²¤à²¿à²—ಳಿಗೆ ಕಳà³à²¹à²¿à²¸à²²à²¾à²—à³à²¤à³à²¤à²¦à³†. ಉದಾಹರಣೆಗೆ, ಸೂಕà³à²·à³à²®à²µà²¾à²¦ ವೈಯಕà³à²¤à²¿à²• ಡೇಟಾವನà³à²¨à³ ಪತà³à²¤à³†à²¹à²šà³à²šà²²à³ ಅದನà³à²¨à³ ಸà³à²•à³à²¯à²¾à²¨à³ ಮಾಡಬಹà³à²¦à³.</translation>
@@ -1217,6 +1224,7 @@
<translation id="4305666528087210886">ನಿಮà³à²® ಫೈಲೠಅನà³à²¨à³ ಪà³à²°à²µà³‡à²¶à²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¾à²—ಲಿಲà³à²²</translation>
<translation id="4306529830550717874">ವಿಳಾಸವನà³à²¨à³ ಉಳಿಸಬೇಕೆ?</translation>
<translation id="4306812610847412719">ಕà³à²²à²¿à²ªà³â€Œà²¬à³‹à²°à³à²¡à³</translation>
+<translation id="4310070645992025887">ನಿಮà³à²® ಪà³à²°à²¯à²¾à²£à²—ಳನà³à²¨à³ ಹà³à²¡à³à²•à²¿</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">ನಿರà³à²¬à²‚ಧಿಸೠ(ಡಿಫಾಲà³à²Ÿà³)</translation>
<translation id="4314815835985389558">ಸಿಂಕೠಅನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿</translation>
@@ -1267,6 +1275,7 @@
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">ಪà³à²°à²¾à²•à³à²¸à²¿à²¯ ಬಳಕೆಯನà³à²¨à³ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲಾಗಿದೆ ಆದರೆ ಬಹಿರಂಗ ಪà³à²°à²¾à²•à³à²¸à²¿ ಕಾನà³à²«à²¿à²—ರೇಶನೠಅನà³à²¨à³ ನಿರà³à²¦à²¿à²·à³à²Ÿà²ªà²¡à²¿à²¸à²²à²¾à²—ಿದೆ.</translation>
<translation id="4441832193888514600">ನೀತಿಯನà³à²¨à³ ಕà³à²²à³Œà²¡à³ ಬಳಕೆದಾರರ ನೀತಿಯಾಗಿ ಮಾತà³à²°à²µà³‡ ಹೊಂದಿಸಬಹà³à²¦à²¾à²—ಿದೆ, ಆದà³à²¦à²°à²¿à²‚ದ ನಿರà³à²²à²•à³à²·à²¿à²¸à²²à²¾à²—ಿದೆ.</translation>
+<translation id="4442470707340296952">Chrome ಟà³à²¯à²¾à²¬à³â€Œà²—ಳà³</translation>
<translation id="4450893287417543264">ಮತà³à²¤à³Šà²®à³à²®à³† ತೋರಿಸಬೇಡಿ</translation>
<translation id="4451135742916150903">HID ಸಾಧನಗಳಿಗೆ ಕನೆಕà³à²Ÿà³ ಮಾಡಲೠಕೇಳಬಹà³à²¦à³</translation>
<translation id="4452328064229197696">ನೀವೠಈಗಷà³à²Ÿà³‡ ಬಳಸಿದ ಪಾಸà³â€Œà²µà²°à³à²¡à³ ಡೇಟಾ ಉಲà³à²²à²‚ಘನೆಯಲà³à²²à²¿ ಕಂಡà³à²¬à²‚ದಿದೆ. ನಿಮà³à²® ಖಾತೆಗಳನà³à²¨à³ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿರಿಸಲà³, Google ಪಾಸà³â€Œà²µà²°à³à²¡à³ ನಿರà³à²µà²¾à²¹à²•à²µà³ ನಿಮà³à²® ಉಳಿಸಿದ ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳನà³à²¨à³ ಪರಿಶೀಲಿಸಲೠಶಿಫಾರಸೠಮಾಡà³à²¤à³à²¤à²¦à³†.</translation>
@@ -1405,6 +1414,7 @@
<translation id="483241715238664915">ಎಚà³à²šà²°à²¿à²•à³†à²—ಳನà³à²¨à³ ಆನೠಮಾಡಿ</translation>
<translation id="4834250788637067901">Google Pay ಅನà³à²¨à³ ಬಳಸà³à²¤à³à²¤à²¿à²°à³à²µ ಪಾವತಿ ವಿಧಾನಗಳà³, ಆಫರà³â€Œà²—ಳೠಮತà³à²¤à³ ವಿಳಾಸಗಳà³</translation>
<translation id="4838327282952368871">ಕನಸà³</translation>
+<translation id="4839087176073128681">ಮà³à²‚ದಿನ ಬಾರಿ ವೇಗವಾಗಿ ಪಾವತಿಸಿ ಮತà³à²¤à³ Google ನ ಉದà³à²¯à²®à²¦à²²à³à²²à³‡ ಮà³à²‚ಚೂಣಿಯಲà³à²²à²¿à²°à³à²µ ಭದà³à²°à²¤à³†à²¯ ಮೂಲಕ ನಿಮà³à²® ಕಾರà³à²¡à³ ಅನà³à²¨à³ ರಕà³à²·à²¿à²¸à²¿.</translation>
<translation id="4840250757394056958">ನಿಮà³à²® Chrome ಇತಿಹಾಸವನà³à²¨à³ ವೀಕà³à²·à²¿à²¸à²¿</translation>
<translation id="484462545196658690">ಆಟೋ</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> ಮತà³à²¤à³ ಇತà³à²¯à²¾à²¦à²¿à²—ಳ ಮೇಲೆ ರಿಯಾಯಿತಿ ಪಡೆಯಿರಿ</translation>
@@ -1467,6 +1477,7 @@
<translation id="5011561501798487822">ಪತà³à²¤à³†à²¯à²¾à²¦ ಭಾಷೆ</translation>
<translation id="5015510746216210676">ಯಂತà³à²°à²¦ ಹೆಸರà³:</translation>
<translation id="5017554619425969104">ನೀವೠನಕಲಿಸಿದ ಪಠà³à²¯</translation>
+<translation id="5017828934289857214">ನಂತರ ನನಗೆ ಜà³à²žà²¾à²ªà²¿à²¸à²¿</translation>
<translation id="5018422839182700155">ಈ ಪà³à²Ÿà²µà²¨à³à²¨à³ ತೆರೆಯಲೠಸಾಧà³à²¯à²µà²¿à²²à³à²²</translation>
<translation id="5019198164206649151">ಕಳಪೆ ಸà³à²¥à²¿à²¤à²¿à²¯à²²à³à²²à²¿ ಸಂಗà³à²°à²¹à²£à³†à²¯à²¨à³à²¨à³ ಹಿಂತಿರà³à²—ಿಸಲಾಗಿದೆ</translation>
<translation id="5020776957610079374">ಜಾಗತಿಕ ಸಂಗೀತ</translation>
@@ -1486,6 +1497,7 @@
<translation id="5051305769747448211">ಲೈವೠಕಾಮಿಡಿ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Nearby ಶೇರೠಬಳಸಿಕೊಂಡೠಈ ಫೈಲೠಅನà³à²¨à³ ಕಳà³à²¹à²¿à²¸à²²à³, ನಿಮà³à²® ಸಾಧನದಲà³à²²à²¿ ಸà³à²¥à²³à²¾à²µà²•à²¾à²¶à²µà²¨à³à²¨à³ (<ph name="DISK_SPACE_SIZE" />) ಮà³à²•à³à²¤à²—ೊಳಿಸಿ}one{Nearby ಶೇರೠಬಳಸಿಕೊಂಡೠಈ ಫೈಲà³â€Œà²—ಳನà³à²¨à³ ಕಳà³à²¹à²¿à²¸à²²à³, ನಿಮà³à²® ಸಾಧನದಲà³à²²à²¿ ಸà³à²¥à²³à²¾à²µà²•à²¾à²¶à²µà²¨à³à²¨à³ (<ph name="DISK_SPACE_SIZE" />) ಮà³à²•à³à²¤à²—ೊಳಿಸಿ}other{Nearby ಶೇರೠಬಳಸಿಕೊಂಡೠಈ ಫೈಲà³â€Œà²—ಳನà³à²¨à³ ಕಳà³à²¹à²¿à²¸à²²à³, ನಿಮà³à²® ಸಾಧನದಲà³à²²à²¿ ಸà³à²¥à²³à²¾à²µà²•à²¾à²¶à²µà²¨à³à²¨à³ (<ph name="DISK_SPACE_SIZE" />) ಮà³à²•à³à²¤à²—ೊಳಿಸಿ}}</translation>
<translation id="5056549851600133418">ನಿಮಗಾಗಿ ಲೇಖನಗಳà³</translation>
+<translation id="5060483733937416656">ನೀವೠ<ph name="PROVIDER_ORIGIN" /> ಬಳಸà³à²µ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ಳಲà³à²²à²¿ Windows Hello ಮೂಲಕ ದೃಢೀಕರಿಸà³à²µ ಆಯà³à²•à³†à²¯à²¨à³à²¨à³ ಮಾಡಿದà³à²¦à³€à²°à²¿. ಈ ಪೂರೈಕೆದಾರರೠನಿಮà³à²® ಪಾವತಿ ವಿಧಾನದ ಕà³à²°à²¿à²¤à³ ಮಾಹಿತಿಯನà³à²¨à³ ಸಂಗà³à²°à²¹à²¿à²¸à²¿à²Ÿà³à²Ÿà²¿à²°à²¬à²¹à³à²¦à³, ನೀವೠಅದನà³à²¨à³ <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">ನೀವೠಹà³à²¡à³à²•à³à²¤à³à²¤à²¿à²°à³à²µà³à²¦à³ <ph name="LOOKALIKE_DOMAIN" /> ತಾನೇ?</translation>
<translation id="5066056036849835175">ಪà³à²°à²¿à²‚ಟಿಂಗೠಇತಿಹಾಸ</translation>
<translation id="5068234115460527047">ಹೆಡà³à²œà³ ಫಂಡà³â€Œà²—ಳà³</translation>
@@ -1499,10 +1511,8 @@
<translation id="5087286274860437796">ಈ ಸಮಯದಲà³à²²à²¿ ಸರà³à²µà²°à³â€Œà²¨ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²° ಮಾನà³à²¯à²µà²¾à²—ಿಲà³à²².</translation>
<translation id="5087580092889165836">ಕಾರà³à²¡à³ ಸೇರಿಸಿ</translation>
<translation id="5088142053160410913">ಆಪರೇಟರà³â€Œà²—ೆ ಸಂದೇಶ</translation>
-<translation id="5089810972385038852">ರಾಜà³à²¯</translation>
<translation id="5093232627742069661">à²à³†à²¡à³ ಮಾದರಿಯಲà³à²²à²¿ ಮಡಿಸಿ</translation>
<translation id="5094747076828555589">ಈ ಸರà³à²µà²°à³ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬà³à²¦à²¨à³à²¨à³ ಸಾಬೀತà³à²ªà²¡à²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¾à²—ಲಿಲà³à²²; ಅದರ ಸà³à²°à²•à³à²·à²¤à²¾ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà³ Chromium ಮೂಲಕ ವಿಶà³à²µà²¾à²¸à²¾à²°à³à²¹à²µà²¾à²—ಿಲà³à²². ಇದೠತಪà³à²ªà³ ಕಾನà³à²«à²¿à²—ರೇಶನà³â€Œà²¨à²¿à²‚ದ ಅಥವಾ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ನಿಮà³à²® ಸಂಪರà³à²•à²¦à²²à³à²²à²¿ ಒಳನà³à²¸à³à²³à²¿à²°à³à²µà³à²¦à²°à²¿à²‚ದ ಆಗಿರಬಹà³à²¦à³.</translation>
-<translation id="5095208057601539847">ಪà³à²°à²¾à²‚ತà³à²¯</translation>
<translation id="5097099694988056070">CPU/RAM ಬಳಕೆ ರೀತಿಯ ಸಾಧನದ ಅಂಕಿಅಂಶಗಳà³</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">ಸೈಟೠಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿಲà³à²²</translation>
@@ -1540,6 +1550,7 @@
<translation id="5171045022955879922">ಹà³à²¡à³à²•à²¾à²Ÿ ನಡೆಸಿ ಅಥವಾ URL ಅನà³à²¨à³ ಟೈಪà³â€Œ ಮಾಡಿ</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">ಯಂತà³à²°</translation>
+<translation id="5177076414499237632">ಈ ಪà³à²Ÿà²¦ ಮೂಲ ಹಾಗೂ ವಿಷಯದ ಕà³à²°à²¿à²¤à³ ತಿಳಿಯಿರಿ</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> ರಲà³à²²à²¿ ಇಲà³à²²à²µà³†? ಈ ದೋಷವನà³à²¨à³ ವರದಿ ಮಾಡಿ</translation>
<translation id="518639307526414276">ಸಾಕà³à²ªà³à²°à²¾à²£à²¿à²—ಳ ಆಹಾರ ಮತà³à²¤à³ ಸಾಕà³à²ªà³à²°à²¾à²£à²¿ ಆರೈಕೆಯ ಸಾಮಗà³à²°à²¿à²—ಳà³</translation>
<translation id="5190835502935405962">ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳ ಬಾರà³</translation>
@@ -1700,6 +1711,7 @@
<translation id="5624120631404540903">ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿</translation>
<translation id="5629630648637658800">ನೀತಿಯ ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳನà³à²¨à³ ಲೋಡೠಮಾಡà³à²µà²²à³à²²à²¿ ವಿಫಲವಾಗಿದೆ</translation>
<translation id="5631439013527180824">ಅಮಾನà³à²¯à²µà²¾à²¦ ಸಾಧನ ನಿರà³à²µà²¹à²£à³† ಟೋಕನà³</translation>
+<translation id="5632485077360054581">ಹೇಗೆಂದೠನನಗೆ ತೋರಿಸಿ</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ನ ದಾಳಿಕೋರರೠನಿಮà³à²® ಕಂಪà³à²¯à³‚ಟರà³â€Œà²¨à²²à³à²²à²¿ ಮಾಹಿತಿಯನà³à²¨à³ (ಉದಾಹರಣೆಗೆ, ಫೋಟೋಗಳà³, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳೠಮತà³à²¤à³ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³ ಮಾಹಿತಿಗಳà³) ಕದಿಯಲೠಇಲà³à²²à²µà³‡ ಅಳಿಸಲೠಅಪಾಯಕಾರಿ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳನà³à²¨à³ ಇನà³â€Œà²¸à³à²Ÿà²¾à²²à³ ಮಾಡಲೠಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">ವಂಚನೀಯ ವಿಷಯವನà³à²¨à³ ನಿರà³à²¬à²‚ಧಿಸಲಾಗಿದೆ.</translation>
<translation id="5633259641094592098">ಕಲà³à²Ÿà³ ಮತà³à²¤à³ ಇಂಡೀ ಚಲನಚಿತà³à²°à²—ಳà³</translation>
@@ -1817,6 +1829,7 @@
<translation id="5989320800837274978">ಹೊಂದಿಸಿದ ಪà³à²°à²¾à²•à³à²¸à²¿ ಸರà³à²µà²°à³â€Œà²—ಳೠಆಗಲಿ ಅಥವಾ .pac ಸà³à²•à³à²°à²¿à²ªà³à²Ÿà³ URL ಅನà³à²¨à³ ನಿರà³à²¦à²¿à²·à³à²Ÿà²ªà²¡à²¿à²¸à²¿à²²à³à²².</translation>
<translation id="5992691462791905444">ಇಂಜಿನಿಯರಿಂಗೠà²à³†à²¡à³ ಮಾದರಿಯಲà³à²²à²¿ ಮಡಿಸಿ</translation>
<translation id="5995727681868049093">ನಿಮà³à²® Google ಖಾತೆಯಲà³à²²à²¿ ನಿಮà³à²® ಮಾಹಿತಿ, ಗೌಪà³à²¯à²¤à³† ಮತà³à²¤à³ ಸà³à²°à²•à³à²·à²¤à³†à²¯à²¨à³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿</translation>
+<translation id="5997247540087773573">ನೀವೠಈಗಷà³à²Ÿà³‡ ಬಳಸಿದ ಪಾಸà³â€Œà²µà²°à³à²¡à³ ಡೇಟಾ ಉಲà³à²²à²‚ಘನೆಯಲà³à²²à²¿ ಕಂಡà³à²¬à²‚ದಿದೆ. ನಿಮà³à²® ಖಾತೆಗಳನà³à²¨à³ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿರಿಸಲà³, Google ಪಾಸà³â€Œà²µà²°à³à²¡à³ ನಿರà³à²µà²¾à²¹à²•à²µà³ ಅದನà³à²¨à³ ಈಗಲೇ ಬದಲಾಯಿಸಲೠಹಾಗೂ ನಿಮà³à²® ಉಳಿಸಿದ ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳನà³à²¨à³ ಪರಿಶೀಲಿಸಲೠಶಿಫಾರಸೠಮಾಡà³à²¤à³à²¤à²¦à³†.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' ಗಾಗಿ <ph name="RESULT_COUNT" /> ಫಲಿತಾಂಶಗಳà³</translation>
<translation id="6006484371116297560">ಕà³à²²à²¾à²¸à²¿à²•à³</translation>
<translation id="6008122969617370890">N-ಇಂದ-1 ಆರà³à²¡à²°à³</translation>
@@ -1911,7 +1924,6 @@
<translation id="627746635834430766">ಮà³à²‚ದಿನ ಬಾರಿ ವೇಗವಾಗಿ ಪಾವತಿಸಲà³, ನಿಮà³à²® ಕಾರà³à²¡à³â€Œ ಮತà³à²¤à³ ಬಿಲà³à²²à²¿à²‚ಗೠವಿಳಾಸವನà³à²¨à³ ನಿಮà³à²® Google ಖಾತೆಯಲà³à²²à²¿ ಉಳಿಸಿ.</translation>
<translation id="6279183038361895380">ನಿಮà³à²® ಕರà³à²¸à²°à³ ತೋರಿಸಲೠ|<ph name="ACCELERATOR" />| ಒತà³à²¤à²¿</translation>
<translation id="6280223929691119688">ಈ ವಿಳಾಸಕà³à²•à³† ತಲà³à²ªà²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¿à²²à³à²². ಬೇರೊಂದೠವಿಳಾಸವನà³à²¨à³ ಆಯà³à²•à³† ಮಾಡಿ.</translation>
-<translation id="6282194474023008486">ಪೋಸà³à²Ÿà²²à³ ಕೋಡà³</translation>
<translation id="6285507000506177184">Chrome ಬಟನà³â€Œà²¨à²²à³à²²à²¿ ಡೌನà³â€Œà²²à³‹à²¡à³â€Œà²—ಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿, ನೀವೠChrome ನಲà³à²²à²¿ ಡೌನà³â€Œà²²à³‹à²¡à³ ಮಾಡಿರà³à²µ ಫೈಲà³â€Œà²—ಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²²à³ Enter ಒತà³à²¤à²¿à²°à²¿</translation>
<translation id="6289939620939689042">ಪà³à²Ÿà²¦ ಬಣà³à²£</translation>
<translation id="6290238015253830360">ನೀವೠಸಲಹೆ ನೀಡಿರà³à²µ ಲೇಖನಗಳೠಇಲà³à²²à²¿ ಕಾಣಿಸಿಕೊಳà³à²³à³à²¤à³à²¤à²µà³†</translation>
@@ -1933,6 +1945,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> ಕà³à²•à²¿à²‚ತ ಕಡಿಮೆ ಇರà³à²µà³à²¦à²¨à³à²¨à³ ತೆಗೆದà³à²¹à²¾à²•à²¿. ನಿಮà³à²® ನಂತರದ ಭೇಟಿಯ ಸಮಯದಲà³à²²à²¿ ಕೆಲವೠಸೈಟà³â€Œà²—ಳೠನಿಧಾನವಾಗಿ ಲೋಡೠಆಗಬಹà³à²¦à³.</translation>
<translation id="6337534724793800597">ಹೆಸರಿನ ಪà³à²°à²•à²¾à²°à²µà²¾à²—ಿ ನೀತಿಗಳನà³à²¨à³ ಫಿಲà³à²Ÿà²°à³ ಮಾಡಿ</translation>
<translation id="6340739886198108203">ನಿರà³à²µà²¾à²¹à²•à²° ನೀತಿಯೠಗೌಪà³à²¯ ವಿಷಯ ಕಾಣಿಸà³à²¤à³à²¤à²¿à²°à³à²µà²¾à²— ಸà³à²•à³à²°à³€à²¨à³â€Œà²¶à²¾à²Ÿà³â€Œà²—ಳನà³à²¨à³ ತೆಗೆದà³à²•à³Šà²³à³à²³à³à²µà³à²¦à²¨à³à²¨à³ ಮತà³à²¤à³ ರೆಕಾರà³à²¡à³ ಮಾಡà³à²µà³à²¦à²¨à³à²¨à³ ಶಿಫಾರಸೠಮಾಡà³à²µà³à²¦à²¿à²²à³à²²:</translation>
+<translation id="6348220984832452017">ಸಕà³à²°à²¿à²¯ ವà³à²¯à²¤à³à²¯à²¾à²¸à²—ಳà³</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ಇನà³â€Œà²¸à³à²Ÿà²¾à²²à³ ಮಾಡಿ</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ಒಂದೂ ಇಲà³à²²}=1{1 ಪಾಸà³â€Œà²µà²°à³à²¡à³ (<ph name="DOMAIN_LIST" /> ಗೆ ಸಂಬಂಧಿಸಿದà³à²¦à³, ಅದನà³à²¨à³ ಸಿಂಕೠಮಾಡಲಾಗಿದೆ)}=2{2 ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳೠ(<ph name="DOMAIN_LIST" /> ಗೆ ಸಂಬಂಧಿಸಿದà³à²¦à³, ಅವà³à²—ಳನà³à²¨à³ ಸಿಂಕೠಮಾಡಲಾಗಿದೆ)}one{# ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳೠ(<ph name="DOMAIN_LIST" /> ಗೆ ಸಂಬಂಧಿಸಿದà³à²¦à³, ಅವà³à²—ಳನà³à²¨à³ ಸಿಂಕೠಮಾಡಲಾಗಿದೆ)}other{# ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳೠ(<ph name="DOMAIN_LIST" /> ಗೆ ಸಂಬಂಧಿಸಿದà³à²¦à³, ಅವà³à²—ಳನà³à²¨à³ ಸಿಂಕೠಮಾಡಲಾಗಿದೆ)}}</translation>
<translation id="6355392890578844978">ಈ ಬà³à²°à³Œà²¸à²°à³ ಅನà³à²¨à³ ಕಂಪನಿ ಅಥವಾ ಇತರ ಸಂಸà³à²¥à³†à²¯à³ ನಿರà³à²µà²¹à²¿à²¸à³à²¤à³à²¤à²¿à²²à³à²². ಈ ಸಾಧನದಲà³à²²à²¿à²¨ ಚಟà³à²µà²Ÿà²¿à²•à³†à²¯à²¨à³à²¨à³ Chromium ನಿಂದ ಹೊರಗೆ ನಿರà³à²µà²¹à²¿à²¸à²¬à²¹à³à²¦à³. <ph name="BEGIN_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LINK" /></translation>
@@ -1964,7 +1977,6 @@
<translation id="643051589346665201">Google ಪಾಸà³â€Œà²µà²°à³à²¡à³ ಅನà³à²¨à³ ಬದಲಾಯಿಸಿ</translation>
<translation id="6433490469411711332">ಸಂಪರà³à²• ಮಾಹಿತಿ ಎಡಿಟೠಮಾಡಿ</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ಸಂಪರà³à²•à²—ೊಳà³à²³à²²à³ ನಿರಾಕರಿಸಿದೆ.</translation>
-<translation id="6438025220577812695">ನಾನೇ ಪಾಸà³â€Œà²µà²°à³à²¡à³ ಬದಲಾಯಿಸà³à²¤à³à²¤à³‡à²¨à³†</translation>
<translation id="6440503408713884761">ನಿರà³à²²à²•à³à²·à²¿à²¸à²²à²¾à²—ಿದೆ</translation>
<translation id="6443406338865242315">ನೀವೠಇನà³â€Œà²¸à³à²Ÿà²¾à²²à³ ಮಾಡಿರà³à²µ ವಿಸà³à²¤à²°à²£à³†à²—ಳೠಮತà³à²¤à³ ಪà³à²²à²—à³â€Œà²‡à²¨à³â€Œà²—ಳà³</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2094,9 +2106,9 @@
<translation id="6828866289116430505">ತಳಿವಿಜà³à²žà²¾à²¨</translation>
<translation id="6831043979455480757">Translate</translation>
<translation id="6833752742582340615">ಸà³à²°à²•à³à²·à²¿à²¤ ಮತà³à²¤à³ ತà³à²µà²°à²¿à²¤ ಚೆಕೠಔಟà³â€Œà²—ಳಿಗಾಗಿ ನಿಮà³à²® ಕಾರà³à²¡à³ ಮತà³à²¤à³ ಬಿಲà³à²²à²¿à²‚ಗೠಮಾಹಿತಿಯನà³à²¨à³ ನಿಮà³à²® Google ಖಾತೆಯಲà³à²²à²¿ ಉಳಿಸಿ</translation>
-<translation id="6839929833149231406">ಪà³à²°à²¦à³‡à²¶</translation>
<translation id="6846340164947227603">ವರà³à²šà³à²µà²²à³ ಕಾರà³à²¡à³ ಸಂಖà³à²¯à³†à²¯à³Šà²‚ದನà³à²¨à³ ಬಳಸಿ...</translation>
<translation id="6852204201400771460">ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³ ಅನà³à²¨à³ ಪà³à²¨à²ƒ ಆರಂಭಿಸಬೇಕೆ?</translation>
+<translation id="6857776781123259569">ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿...</translation>
<translation id="686485648936420384">ಗà³à²°à²¾à²¹à²• ಸಂಪನà³à²®à³‚ಲಗಳà³</translation>
<translation id="6865412394715372076">ಈ ಕಾರà³à²¡à³ ಅನà³à²¨à³ ಈಗಲೇ ಪರಿಶೀಲಿಸಲೠಸಾಧà³à²¯à²µà²¿à²²à³à²²</translation>
<translation id="6869334554832814367">ವೈಯಕà³à²¤à²¿à²• ಸಾಲಗಳà³</translation>
@@ -2145,7 +2157,6 @@
<translation id="6965978654500191972">ಸಾಧನ</translation>
<translation id="696703987787944103">ಗà³à²°à²¹à²¿à²•à³†</translation>
<translation id="6968269510885595029">ನಿಮà³à²® ಭದà³à²°à²¤à³† ಕೀ ಬಳಸಿ</translation>
-<translation id="6970216967273061347">ಜಿಲà³à²²à³†</translation>
<translation id="6971439137020188025">Slides ನಲà³à²²à²¿ ಹೊಸ Google ಪà³à²°à²¸à³à²¤à³à²¤à²¿à²¯à²¨à³à²¨à³ ತà³à²µà²°à²¿à²¤à²µà²¾à²—ಿ ರಚಿಸಿ</translation>
<translation id="6972629891077993081">HID ಸಾಧನಗಳà³</translation>
<translation id="6973656660372572881">ಹೊಂದಿಸಿದ ಪà³à²°à²¾à²•à³à²¸à²¿ ಸರà³à²µà²°à³â€Œà²—ಳೠಮತà³à²¤à³ .pac ಸà³à²•à³à²°à²¿à²ªà³à²Ÿà³ URL ಎರಡನà³à²¨à³‚ ನಿರà³à²¦à²¿à²·à³à²Ÿà²ªà²¡à²¿à²¸à²²à²¾à²—ಿದೆ.</translation>
@@ -2184,7 +2195,6 @@
<translation id="7081308185095828845">ನಿಮà³à²® ಸಾಧನದಲà³à²²à²¿ ಈ ವೈಶಿಷà³à²Ÿà³à²¯ ಲಭà³à²¯à²µà²¿à²²à³à²²</translation>
<translation id="7083258188081898530">ಟà³à²°à³‡ 9</translation>
<translation id="7086090958708083563">ಬಳಕೆದಾರರೠಅಪà³â€Œà²²à³‹à²¡à³ ಮಾಡಲೠವಿನಂತಿಸಿದà³à²¦à²¾à²°à³†</translation>
-<translation id="7087282848513945231">ರಾಷà³à²Ÿà³à²°</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳಲà³à²²à²¿à²¨ ಸೈಟà³â€Œà²—ಳಲà³à²²à²¿ ಸಂಗà³à²°à²¹à²¿à²¸à²²à²¾à²—ಿರà³à²µ ಅನà³à²®à²¤à²¿à²—ಳೠಮತà³à²¤à³ ಡೇಟಾವನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²²à³ Tab ಒತà³à²¤à²¿, ನಂತರ Enter ಒತà³à²¤à²¿</translation>
<translation id="7096937462164235847">ಈ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²¨ ಗà³à²°à³à²¤à²¨à³à²¨à³ ದೃಢೀಕರಿಸಿಲà³à²².</translation>
<translation id="7101893872976785596">ಹಾರರೠಚಲನಚಿತà³à²°à²—ಳà³</translation>
@@ -2203,10 +2213,10 @@
<ph name="LIST_ITEM" />ಫಾರà³à²®à³â€Œà²—ಳಲà³à²²à²¿ ನಮೂದಿಸಿದ ಮಾಹಿತಿ<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">ಈ ವಿಳಾಸಕà³à²•à³† ರವಾನಿಸಲೠಸಾಧà³à²¯à²µà²¿à²²à³à²². ಬೇರೊಂದೠವಿಳಾಸವನà³à²¨à³ ಆಯà³à²•à³† ಮಾಡಿ.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ನಿಮà³à²® ನಿರà³à²µà²¾à²¹à²•à²°à³ ಈ ಡೇಟಾವನà³à²¨à³ ನಕಲಿಸದಂತೆ ನಿಷೇಧಿಸಿದà³à²¦à²¾à²°à³†.</translation>
<translation id="7135130955892390533">ಕಾರà³à²¯à²¨à³€à²¤à²¿ ಸà³à²¥à²¿à²¤à²¿à²¯à²¨à³à²¨à³ ತೋರಿಸಿ</translation>
<translation id="7138472120740807366">ವಿತರಣೆ ವಿಧಾನ</translation>
-<translation id="7139724024395191329">ಎಮಿರೇಟà³</translation>
<translation id="7139892792842608322">ಪà³à²°à²¾à²¥à²®à²¿à²• ಟà³à²°à³‡</translation>
<translation id="714064300541049402">ಅಂಚೠ2 ಚಿತà³à²° X ಶಿಫà³à²Ÿà³</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2422,6 +2432,7 @@
<translation id="7669271284792375604">ನಿಮà³à²® ಬà³à²°à³Œà²¸à²¿à²‚ಗà³â€Œ ಅನà³à²­à²µà²µà²¨à³à²¨à³ ಹಾನಿಮಾಡಲೠಸà³à²¥à²¾à²ªà²¿à²¸à²²à²¾à²—à³à²µ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳಲà³à²²à²¿ ನಿಮà³à²®à²¨à³à²¨à³ ವಂಚಿಸಲೠಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ಈ ಸೈಟà³â€Œ ಮೇಲೆ ದಾಳಿ ಮಾಡಬಹà³à²¦à³ (ಉದಾಹರಣೆಗೆ, ನಿಮà³à²® ಮà³à²–ಪà³à²Ÿà²µà²¨à³à²¨à³ ಬದಲಾಯಿಸಲಾಗà³à²¤à³à²¤à²¦à³† ಅಥವಾ ನೀವೠಭೇಟಿ ನೀಡà³à²µ ಸೈಟà³â€Œà²—ಳಲà³à²²à²¿ ಹೆಚà³à²šà²¿à²¨ ಜಾಹೀರಾತà³à²—ಳನà³à²¨à³ ತೋರಿಸಲಾಗà³à²¤à³à²¤à²¦à³†).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ಗೌಪà³à²¯à²µà³†à²‚ದೠಫà³à²²à³à²¯à²¾à²—ೠಮಾಡಿರà³à²µ ಡೇಟಾಗೆ ಸಂಬಂಧಿಸಿದಂತೆ ತೆಗೆದà³à²•à³Šà²‚ಡ ಕà³à²°à²®à²—ಳೠ(ಲಾಗಿನೠಮಾಡಿದಾಗಿನಿಂದ 1 ಕà³à²°à²®à²µà²¨à³à²¨à³ ತೆಗೆದà³à²•à³Šà²³à³à²³à²²à²¾à²—ಿದೆ). <ph name="BEGIN_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LINK" />}one{ಗೌಪà³à²¯à²µà³†à²‚ದೠಫà³à²²à³à²¯à²¾à²—ೠಮಾಡಿರà³à²µ ಡೇಟಾಗೆ ಸಂಬಂಧಿಸಿದಂತೆ ತೆಗೆದà³à²•à³Šà²‚ಡ ಕà³à²°à²®à²—ಳೠ(ಲಾಗಿನೠಮಾಡಿದಾಗಿನಿಂದ # ಕà³à²°à²®à²—ಳನà³à²¨à³ ತೆಗೆದà³à²•à³Šà²³à³à²³à²²à²¾à²—ಿದೆ). <ph name="BEGIN_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LINK" />}other{ಗೌಪà³à²¯à²µà³†à²‚ದೠಫà³à²²à³à²¯à²¾à²—ೠಮಾಡಿರà³à²µ ಡೇಟಾಗೆ ಸಂಬಂಧಿಸಿದಂತೆ ತೆಗೆದà³à²•à³Šà²‚ಡ ಕà³à²°à²®à²—ಳೠ(ಲಾಗಿನೠಮಾಡಿದಾಗಿನಿಂದ # ಕà³à²°à²®à²—ಳನà³à²¨à³ ತೆಗೆದà³à²•à³Šà²³à³à²³à²²à²¾à²—ಿದೆ). <ph name="BEGIN_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ಮೇಲà³â€Œà²¬à²¾à²•à³à²¸à³ 6</translation>
+<translation id="7675325315208090829">ಪಾವತಿ ವಿಧಾನಗಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿...</translation>
<translation id="7676643023259824263">ಕà³à²²à²¿à²ªà³â€Œà²¬à³‹à²°à³à²¡à³ ಪಠà³à²¯à²•à³à²•à²¾à²—ಿ ಹà³à²¡à³à²•à²¿, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">ನಿಮà³à²® ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಇತಿಹಾಸವನà³à²¨à³ Chrome ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳಲà³à²²à²¿ ವೀಕà³à²·à²¿à²¸à²¿ ಮತà³à²¤à³ ನಿರà³à²µà²¹à²¿à²¸à²¿</translation>
<translation id="7679947978757153706">ಬೇಸà³â€Œà²¬à²¾à²²à³</translation>
@@ -2464,7 +2475,6 @@
<translation id="7766518757692125295">ಸà³à²•à²°à³à²Ÿà³</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">ಒಂದೇ ಆರà³à²¡à²°à³ ಮà³à²– ಮೇಲಕà³à²•à³†</translation>
-<translation id="777702478322588152">ಪà³à²°à²¿à²«à³†à²•à³à²šà²°à³â€Œâ€Œ</translation>
<translation id="7791011319128895129">ಪà³à²°à²•à²Ÿà²¿à²¸à²¿à²°à²¦</translation>
<translation id="7791196057686275387">ಬೇಲà³</translation>
<translation id="7791543448312431591">ಸೇರಿಸà³</translation>
@@ -2555,12 +2565,12 @@
<translation id="8055534648776115597">ಔದà³à²¯à³‹à²—ಿಕ ಮತà³à²¤à³ ಮà³à²‚ದà³à²µà²°à²¿à²•à³† ಶಿಕà³à²·à²£</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" ಅನà³à²¨à³ ಸರಿಯಾಗಿ ಕಾನà³à²«à²¿à²—ರೠಮಾಡಲಾಗಿಲà³à²². ಸಾಮಾನà³à²¯à²µà²¾à²—ಿ ಸಮಸà³à²¯à³†à²¯à²¨à³à²¨à³ ಪರಿಹರಿಸಲೠ"<ph name="SOFTWARE_NAME" />" ಅನà³à²¨à³ ಅನà³â€Œà²‡à²¨à³â€Œà²¸à³à²Ÿà²¾à²²à³ ಮಾಡಲಾಗà³à²¤à³à²¤à²¿à²¦à³†. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ಆಹಾರ ಉತà³à²ªà²¾à²¦à²¨à³†</translation>
-<translation id="8066955247577885446">ಕà³à²·à²®à²¿à²¸à²¿, à²à²¨à³‹ ತಪà³à²ªà²¾à²—ಿದೆ.</translation>
<translation id="8067872629359326442">ನೀವೠಈಗಷà³à²Ÿà³‡ ವಂಚನೆ ಮಾಡà³à²µ ಸೈಟà³â€Œà²¨à²²à³à²²à²¿ ನಿಮà³à²® ಪಾಸà³â€Œà²µà²°à³à²¡à³ ಅನà³à²¨à³ ನಮೂದಿಸಿದà³à²¦à³€à²°à²¿. Chromium ಸಹಾಯ ಮಾಡಬಹà³à²¦à³. ನಿಮà³à²® ಪಾಸà³â€Œà²µà²°à³à²¡à³ ಬದಲಿಸಲೠಮತà³à²¤à³ ನಿಮà³à²® ಖಾತೆಗೆ ಅಪಾಯ ಉಂಟಾಗಿರಬಹà³à²¦à³ ಎಂದೠGoogle ಗೆ ಸೂಚಿಸಲà³, ಖಾತೆ ರಕà³à²·à²¿à²¸à²¿ ಕà³à²²à²¿à²•à³ ಮಾಡಿ.</translation>
<translation id="8070439594494267500">ಆà³à²¯à²ªà³ à²à²•à²¾à²¨à³</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">ಹೊಸ Google Sheet ಅನà³à²¨à³ ತà³à²µà²°à²¿à²¤à²µà²¾à²—ಿ ರಚಿಸಿ</translation>
<translation id="8075898834294118863">ಸೈಟೠಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²¿</translation>
+<translation id="8076492880354921740">ಟà³à²¯à²¾à²¬à³â€Œà²—ಳà³</translation>
<translation id="8078141288243656252">ಡಾಕà³à²¯à³à²®à³†à²‚ಟà³â€Œ ಅನà³à²¨à³ ತಿರà³à²—ಿಸಿದಾಗ ಟಿಪà³à²ªà²£à²¿ ಮಾಡಲೠಸಾಧà³à²¯à²µà²¿à²²à³à²²</translation>
<translation id="8079031581361219619">ಸೈಟೠಅನà³à²¨à³ ಪà³à²¨à²ƒ ಲೋಡೠಮಾಡಬೇಕೇ?</translation>
<translation id="8081087320434522107">ಸೆಡಾನà³â€Œà²—ಳà³</translation>
@@ -2683,6 +2693,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳà³</translation>
+<translation id="8428634594422941299">ಅರà³à²¥à²µà²¾à²¯à²¿à²¤à³</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳಲà³à²²à²¿ ನಿಮà³à²® ಕà³à²•à³€ ಆದà³à²¯à²¤à³†à²—ಳನà³à²¨à³ ನಿರà³à²µà²¹à²¿à²¸à²²à³ Tab ಒತà³à²¤à²¿, ನಂತರ Enter ಒತà³à²¤à²¿</translation>
<translation id="8433057134996913067">ಇದೠನಿಮà³à²®à²¨à³à²¨à³ ಹೆಚà³à²šà²¿à²¨ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ಳಿಂದ ಸೈನà³â€Œ ಔಟà³â€Œ ಮಾಡà³à²¤à³à²¤à²¦à³†.</translation>
<translation id="8434840396568290395">ಸಾಕà³à²ªà³à²°à²¾à²£à²¿à²—ಳà³</translation>
@@ -2781,6 +2792,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> ಗಾಗಿ ನಿಮà³à²® ಕೋಡೠ<ph name="ONE_TIME_CODE" /> ಆಗಿದೆ</translation>
<translation id="874918643257405732">ಈ ಟà³à²¯à²¾à²¬à³ ಅನà³à²¨à³ ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³ ಮಾಡಿ</translation>
<translation id="8751426954251315517">ಮà³à²‚ದಿನ ಬಾರಿ ಮತà³à²¤à³† ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿</translation>
+<translation id="8757526089434340176">Google Pay ಆಫರೠಲಭà³à²¯à²µà²¿à²¦à³†</translation>
<translation id="8758885506338294482">ಸà³à²ªà²°à³à²§à²¾à²¤à³à²®à²• ವೀಡಿಯೊ ಗೇಮಿಂಗà³</translation>
<translation id="8759274551635299824">ಈ ಕಾರà³à²¡à³â€Œà²¨ ಅವಧಿ ಮà³à²•à³à²¤à²¾à²¯à²µà²¾à²—ಿದೆ</translation>
<translation id="87601671197631245">ಈ ಸೈಟೠಹಳೆಯ ಸà³à²°à²•à³à²·à²¤à²¾ ಕಾನà³à²«à²¿à²—ರೇಶನೠಅನà³à²¨à³ ಬಳಸà³à²¤à³à²¤à²¦à³†, ಇದೠನಿಮà³à²® ಮಾಹಿತಿಯನà³à²¨à³ ಈ ಸೈಟà³â€Œà²—ೆ ಕಳà³à²¹à²¿à²¸à²¿à²¦à²¾à²— ಅದನà³à²¨à³ (ಉದಾಹರಣೆಗೆ, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳà³, ಸಂದೇಶಗಳೠಅಥವಾ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³â€Œà²—ಳà³) ಬಹಿರಂಗಪಡಿಸಬಹà³à²¦à³.</translation>
@@ -2788,6 +2800,7 @@
<translation id="8763927697961133303">USB ಸಾಧನ</translation>
<translation id="8763986294015493060">ಪà³à²°à²¸à³à²¤à³à²¤à²µà²¾à²—ಿ ತೆರೆದಿರà³à²µ ಎಲà³à²²à²¾ ಅಜà³à²žà²¾à²¤ ವಿಂಡೋಗಳನà³à²¨à³ ಮà³à²šà³à²šà²¿</translation>
<translation id="8766943070169463815">ಸà³à²°à²•à³à²·à²¿à²¤ ಪಾವತಿ ರà³à²œà³à²µà²¾à²¤à³ ದೃಢೀಕರಣ ಶೀಟೠತೆರೆದಿದೆ</translation>
+<translation id="8767765348545497220">ಸಹಾಯಕ ಬಬಲೠಅನà³à²¨à³ ಮà³à²šà³à²šà²¿</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">ಮೋಟಾರೠಸೈಕಲà³â€Œà²—ಳà³</translation>
<translation id="8790007591277257123">&amp;ಅಳಿಸà³à²µà³à²¦à²¨à³à²¨à³ ಮತà³à²¤à³†à²®à²¾à²¡à³</translation>
@@ -2800,6 +2813,7 @@
<translation id="8806285662264631610">ಸà³à²¨à²¾à²¨ ಮತà³à²¤à³ ದೇಹದ ಆರೈಕೆಯ ಉತà³à²ªà²¨à³à²¨à²—ಳà³</translation>
<translation id="8807160976559152894">ಪà³à²°à²¤à²¿ ಪà³à²Ÿà²¦ ನಂತರ ಟà³à²°à²¿à²®à³ ಮಾಡಿ</translation>
<translation id="8808828119384186784">Chrome ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳà³</translation>
+<translation id="8813277370772331957">ನಂತರ ನನಗೆ ಜà³à²žà²¾à²ªà²¿à²¸à²¿</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, ನಿಮà³à²® Chrome ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳಿಂದ Chrome ಅನà³à²¨à³ ಅಪà³â€Œà²¡à³‡à²Ÿà³â€Œ ಮಾಡಲೠTab ಒತà³à²¤à²¿, ನಂತರ Enter ಒತà³à²¤à²¿à²°à²¿</translation>
<translation id="8820817407110198400">ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳà³</translation>
<translation id="882338992931677877">ಹಸà³à²¤à²šà²¾à²²à²¿à²¤ ಟà³à²°à³‡</translation>
@@ -2978,6 +2992,7 @@
<translation id="988159990683914416">ಡೆವಲಪರೠಬಿಲà³à²¡à³</translation>
<translation id="989988560359834682">ವಿಳಾಸವನà³à²¨à³ ಎಡಿಟೠಮಾಡಿ</translation>
<translation id="991413375315957741">ಮೋಷನೠಅಥವಾ ಲೈಟೠಸೆನà³à²¸à²°à³â€Œà²—ಳà³</translation>
+<translation id="992110854164447044">ವರà³à²šà³à²µà²²à³ ಕಾರà³à²¡à³ ಸಂಭಾವà³à²¯ ವಂಚನೆಯಿಂದ ನಿಮà³à²®à²¨à³à²¨à³ ರಕà³à²·à²¿à²¸à²²à³ ನಿಮà³à²® ನಿಜವಾದ ಕಾರà³à²¡à³ ಅನà³à²¨à³ ಮರೆಮಾಡà³à²¤à³à²¤à²¦à³†. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ಗà³à²²à²¾à²¬à²¿ ಬಣà³à²£</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" ನಿಮà³à²® ಕಂಪà³à²¯à³‚ಟರೠಅಥವಾ ನೆಟà³â€Œà²µà²°à³à²•à³â€Œà²¨à²²à³à²²à²¿ ಸರಿಯಾಗಿ ಇನà³â€Œà²¸à³à²Ÿà²¾à²²à³â€Œ ಮಾಡಲಾಗಿಲà³à²²: &lt;ul&gt; &lt;li&gt;"<ph name="SOFTWARE_NAME" />" ಅನà³à²¨à³ ಅನà³â€Œà²‡à²¨à³â€Œà²¸à³à²Ÿà²¾à²²à³â€Œ ಮಾಡಲೠಅಥವಾ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲೠಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿&lt;/li&gt; &lt;li&gt;ಇನà³à²¨à³Šà²‚ದೠನೆಟà³â€Œà²µà²°à³à²•à³â€Œà²—ೆ ಸಂಪರà³à²•à²¸à²¿à²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿ &lt;/li&gt; &lt;/ul&gt;</translation>
diff --git a/chromium/components/strings/components_strings_ko.xtb b/chromium/components/strings/components_strings_ko.xtb
index 2bc33b9b15c..fb6db02c777 100644
--- a/chromium/components/strings/components_strings_ko.xtb
+++ b/chromium/components/strings/components_strings_ko.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">보조금, 장학금, 재정 지ì›</translation>
<translation id="1048785276086539861">주ì„ì„ ìˆ˜ì •í•˜ë©´ 문서가 ë‹¨ì¼ íŽ˜ì´ì§€ ë·°ë¡œ ëŒì•„갑니다.</translation>
<translation id="1050038467049342496">다른 앱 닫기</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> 주소를 사용하는 웹사ì´íŠ¸ì˜ ì¸ì¦ ë°©ì‹ìœ¼ë¡œ ì¸ì¦ìž 기기를 ì„ íƒí–ˆìŠµë‹ˆë‹¤. ì´ ì œê³µì—…ì²´ì—ì„œ ë‚´ ê²°ì œ 수단 정보를 ì €ìž¥í–ˆì„ ìˆ˜ 있으며 ì§ì ‘ <ph name="LINK_TEXT" />í•  수 있습니다.</translation>
<translation id="1055184225775184556">추가 실행 취소(&amp;U)</translation>
<translation id="1056663316309890343">사진 소프트웨어</translation>
<translation id="1056898198331236512">경고</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">픽업 방법</translation>
<translation id="1281476433249504884">스태커 1</translation>
<translation id="1285320974508926690">ì´ ì‚¬ì´íŠ¸ 번역 안함</translation>
+<translation id="1288548991597756084">카드 안전하게 저장하기</translation>
<translation id="1292571435393770077">íŠ¸ë ˆì´ 16</translation>
<translation id="1292701964462482250">'ì»´í“¨í„°ì˜ ì†Œí”„íŠ¸ì›¨ì–´ë¡œ ì¸í•´ Chromeì´ ì•ˆì „í•˜ê²Œ ì›¹ì— ì ‘ì†í•  수 없습니다'(Windows 컴퓨터만 해당)</translation>
<translation id="1294154142200295408">명령줄 변형</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;오류를 수정하려면 열려는 페ì´ì§€ì—ì„œ &lt;strong&gt;ì—°ê²°&lt;/strong&gt;ì„ í´ë¦­í•˜ì„¸ìš”.&lt;/p&gt;</translation>
<translation id="1507780850870535225">ì¡°ê²½ ë””ìžì¸</translation>
<translation id="1513706915089223971">방문 ê¸°ë¡ í•­ëª© 목ë¡</translation>
+<translation id="1516097932025103760">암호화ë˜ì–´ 안전하게 저장ë˜ë©° CVC는 저장ë˜ì§€ 않습니다.</translation>
<translation id="1517433312004943670">전화번호 필요</translation>
<translation id="1519264250979466059">ìƒì„± 날짜</translation>
<translation id="1521159554480556801">섬유 ë° ì§ë¬¼ 예술</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ê²°ì œ 수단 저장 ë° ìžë™ ìž…ë ¥</translation>
<translation id="1663943134801823270">카드와 주소는 Chromeì—ì„œ 가져왔습니다. ì´ ì •ë³´ëŠ” <ph name="BEGIN_LINK" />설정<ph name="END_LINK" />ì—ì„œ 관리할 수 있습니다.</translation>
<translation id="1671391448414634642">지금부터 <ph name="SOURCE_LANGUAGE" />ë¡œ ëœ íŽ˜ì´ì§€ê°€ <ph name="TARGET_LANGUAGE" />ë¡œ 번역ë©ë‹ˆë‹¤.</translation>
+<translation id="1673886523110456987">혜íƒì„ 사용하려면 <ph name="CARD_DETAIL" />ì—ì„œ 결제하세요.</translation>
<translation id="1674504678466460478">출발어: <ph name="SOURCE_LANGUAGE" />, ë„ì°©ì–´: <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ì§§ì€ ìª½ 먼저</translation>
<translation id="168693727862418163">ì´ ì •ì±… ê°’ì€ ìŠ¤í‚¤ë§ˆì— ëŒ€í•´ ìœ íš¨ì„±ì´ í™•ì¸ë˜ì§€ 않았으므로 무시ë©ë‹ˆë‹¤.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">움ì§ìž„ ê°ì§€ 센서</translation>
<translation id="1717494416764505390">ë©”ì¼ë°•ìŠ¤ 3</translation>
<translation id="1718029547804390981">문서가 너무 커서 주ì„ì„ ë‹¬ 수 없습니다.</translation>
+<translation id="1720941539803966190">튜토리얼 닫기</translation>
<translation id="1721424275792716183">* 필수 입력란</translation>
<translation id="1727613060316725209">ì¸ì¦ì„œê°€ 유효함</translation>
<translation id="1727741090716970331">유효한 카드 번호 추가</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">바비í ë° ê·¸ë¦´</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" />ë¡œ ëœ íŽ˜ì´ì§€ë¥¼ 번역하지 않습니다.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ì´ ì»¨íŠ¸ë¡¤ì´ ì‚¬ìš© 설정ë˜ì–´ 있고 무료 ì²´í—˜ì´ ì œê³µë˜ëŠ” 경우 Chromeì´ ê¸°ê¸° 사용ìžì˜ 최근 íƒìƒ‰ 활ë™ê³¼ 가장 유사한 ëŒ€ê·œëª¨ì˜ ì‚¬ìš©ìž ê·¸ë£¹, 즉 'ë™ì§ˆ 집단'ì„ ê²°ì •í•©ë‹ˆë‹¤. 광고주는 해당 ê·¸ë£¹ì— ê²Œìž¬í•  광고를 ì„ íƒí•  수 있으며 íƒìƒ‰ 활ë™ì€ ì‚¬ìš©ìž ê¸°ê¸°ì—ì„œ 비공개로 유지ë©ë‹ˆë‹¤. ê·¸ë£¹ì€ ë§¤ì¼ ì—…ë°ì´íŠ¸ë©ë‹ˆë‹¤.}=1{ì´ ì»¨íŠ¸ë¡¤ì´ ì‚¬ìš© 설정ë˜ì–´ 있고 무료 ì²´í—˜ì´ ì œê³µë˜ëŠ” 경우 Chromeì´ ê¸°ê¸° 사용ìžì˜ 최근 íƒìƒ‰ 활ë™ê³¼ 가장 유사한 ëŒ€ê·œëª¨ì˜ ì‚¬ìš©ìž ê·¸ë£¹, 즉 'ë™ì§ˆ 집단'ì„ ê²°ì •í•©ë‹ˆë‹¤. 광고주는 해당 ê·¸ë£¹ì— ê²Œìž¬í•  광고를 ì„ íƒí•  수 있으며 íƒìƒ‰ 활ë™ì€ ì‚¬ìš©ìž ê¸°ê¸°ì—ì„œ 비공개로 유지ë©ë‹ˆë‹¤. ê·¸ë£¹ì€ ë§¤ì¼ ì—…ë°ì´íŠ¸ë©ë‹ˆë‹¤.}other{ì´ ì»¨íŠ¸ë¡¤ì´ ì‚¬ìš© 설정ë˜ì–´ 있고 무료 ì²´í—˜ì´ ì œê³µë˜ëŠ” 경우 Chromeì´ ê¸°ê¸° 사용ìžì˜ 최근 íƒìƒ‰ 활ë™ê³¼ 가장 유사한 ëŒ€ê·œëª¨ì˜ ì‚¬ìš©ìž ê·¸ë£¹, 즉 'ë™ì§ˆ 집단'ì„ ê²°ì •í•©ë‹ˆë‹¤. 광고주는 해당 ê·¸ë£¹ì— ê²Œìž¬í•  광고를 ì„ íƒí•  수 있으며 íƒìƒ‰ 활ë™ì€ ì‚¬ìš©ìž ê¸°ê¸°ì—ì„œ 비공개로 유지ë©ë‹ˆë‹¤. ê·¸ë£¹ì€ {NUM_DAYS}ì¼ë§ˆë‹¤ ì—…ë°ì´íŠ¸ë©ë‹ˆë‹¤.}}</translation>
-<translation id="2053553514270667976">우편번호</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{제안 1개}other{제안 #개}}</translation>
+<translation id="2066915425250589881">삭제를 요청</translation>
<translation id="2068528718802935086">ì˜ìœ ì•„</translation>
<translation id="2071156619270205202">ì´ ì¹´ë“œë¡œëŠ” ê°€ìƒ ì¹´ë“œ 번호를 사용할 수 없습니다.</translation>
<translation id="2071692954027939183">ì•Œë¦¼ì„ í‰ì†Œì— 허용하지 않았기 ë•Œë¬¸ì— ì•Œë¦¼ì´ ìžë™ìœ¼ë¡œ 차단ë˜ì—ˆìŠµë‹ˆë‹¤.</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">ë™ê¸°í™” 관리 버튼, Enter를 눌러 Chrome 설정ì—ì„œ ë‚´ê°€ ë™ê¸°í™”하는 ì •ë³´ 관리</translation>
<translation id="2091887806945687916">소리</translation>
<translation id="2094505752054353250">ë„ë©”ì¸ì´ ì¼ì¹˜í•˜ì§€ ì•ŠìŒ</translation>
-<translation id="2096368010154057602">부서</translation>
<translation id="2099652385553570808">왼쪽 트리플 스테ì´í”Œ</translation>
<translation id="2101225219012730419">버전:</translation>
<translation id="2102134110707549001">강력한 비밀번호 추천...</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">미ì‹ì¶•êµ¬</translation>
<translation id="2187317261103489799">ê°ì§€(기본값)</translation>
<translation id="2188375229972301266">하단 다공 펀칭</translation>
-<translation id="2188852899391513400">방금 사용한 비밀번호가 ì •ë³´ 유출로 ì¸í•´ ë…¸ì¶œëœ ê²ƒìœ¼ë¡œ 확ì¸ë©ë‹ˆë‹¤. ê³„ì •ì„ ë³´í˜¸í•˜ê¸° 위해 Google 비밀번호 관리ìžì—ì„œ 지금 바로 비밀번호를 변경하고 ì €ìž¥ëœ ë¹„ë°€ë²ˆí˜¸ë¥¼ 확ì¸í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤.</translation>
<translation id="219906046732893612">ì£¼íƒ ê°œì¡°</translation>
<translation id="2202020181578195191">올바른 만료 ì—°ë„를 입력하세요.</translation>
<translation id="22081806969704220">íŠ¸ë ˆì´ 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">íŒŒì¼ ìˆ˜ì •</translation>
<translation id="2215963164070968490">개</translation>
<translation id="2218879909401188352">현재 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ì˜ ê³µê²©ìžê°€ 기기를 ì†ìƒì‹œí‚¤ê±°ë‚˜, ëª¨ë°”ì¼ ìš”ê¸ˆì— ëª°ëž˜ 추가 ìš”ê¸ˆì„ ë¶€ê³¼í•˜ê±°ë‚˜, ê°œì¸ì •ë³´ë¥¼ ë„용하는 위험한 ì•±ì„ ì„¤ì¹˜í•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ê°€ì´ë“œ 다시 시작</translation>
+<translation id="2219735899272417925">기기 재설정 필요</translation>
<translation id="2224337661447660594">ì¸í„°ë„· ì—°ê²° ì—†ìŒ</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />진단 앱<ph name="END_LINK" />ì„ ì‚¬ìš©í•˜ì—¬ ì—°ê²° 문제를 해결하세요.</translation>
<translation id="2239100178324503013">지금 보내기</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">허용 안함(기본값)</translation>
<translation id="2512413427717747692">Chromeì„ ê¸°ë³¸ 브ë¼ìš°ì €ë¡œ 설정하는 버튼, Enter 키를 눌러 iOS 설정ì—ì„œ Chromeì„ ì‹œìŠ¤í…œ 기본 브ë¼ìš°ì €ë¡œ 설정</translation>
<translation id="2515629240566999685">현재 ì§€ì—­ì˜ ì‹ í˜¸ 확ì¸</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> 주소를 사용하는 웹사ì´íŠ¸ì˜ ì¸ì¦ ë°©ì‹ìœ¼ë¡œ Touch ID를 ì„ íƒí–ˆìŠµë‹ˆë‹¤. ì´ ì œê³µì—…ì²´ì—ì„œ ë‚´ ê²°ì œ 수단 정보를 ì €ìž¥í–ˆì„ ìˆ˜ 있으며 ì§ì ‘ <ph name="LINK_TEXT" />í•  수 있습니다.</translation>
<translation id="2521385132275182522">오른쪽 하단 스테ì´í”Œ</translation>
<translation id="2521736961081452453">ì–‘ì‹ ë§Œë“¤ê¸°</translation>
<translation id="2523886232349826891">ì´ ê¸°ê¸°ì—만 저장ë¨</translation>
<translation id="2524461107774643265">ìžì„¸í•œ ì •ë³´ 추가</translation>
<translation id="2529899080962247600">ìž…ë ¥ëž€ì— í•­ëª©ì´ <ph name="MAX_ITEMS_LIMIT" />ê°œ 넘게 있으면 안 ë©ë‹ˆë‹¤. ì´í›„ í•­ëª©ì€ ëª¨ë‘ ë¬´ì‹œë©ë‹ˆë‹¤.</translation>
+<translation id="253493526287553278">프로모션 코드 세부정보 보기</translation>
<translation id="2535585790302968248">비공개로 íƒìƒ‰í•˜ë ¤ë©´ 새 ì‹œí¬ë¦¿ íƒ­ì„ ì—¬ì„¸ìš”.</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{외 1개}other{외 #개}}</translation>
<translation id="2536110899380797252">주소 추가</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">사진 ë° ë””ì§€í„¸ 예술</translation>
<translation id="2601150049980261779">로맨스 ì˜í™”</translation>
<translation id="2604589665489080024">íŒ ìŒì•…</translation>
-<translation id="2609632851001447353">유사 버전</translation>
<translation id="2610561535971892504">í´ë¦­í•˜ì—¬ 복사</translation>
<translation id="2617988307566202237">Chromeì—서는 ë‹¤ìŒ ì •ë³´ë¥¼ <ph name="BEGIN_EMPHASIS" />저장하지 않습니다<ph name="END_EMPHASIS" />.
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">ìƒì¼ ë° ì˜ëª… 축ì¼</translation>
<translation id="2677748264148917807">나가기</translation>
+<translation id="2679714844901977852">ì¹´ë“œ ë° ê²°ì œ 정보를 Google 계정(<ph name="USER_EMAIL" />)ì— ì €ìž¥í•˜ì—¬ ë” ë¹ ë¥´ê³  안전하게 결제하세요.</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">ìžë™ 맞춤</translation>
<translation id="2688969097326701645">예, ê³„ì† ì§„í–‰í•©ë‹ˆë‹¤</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">ì¸í„°ë„· 서비스 제공업체(ISP)</translation>
<translation id="2856444702002559011">공격ìžê°€ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ì—ì„œ ì •ë³´(예: 비밀번호, 메시지, ì‹ ìš©ì¹´ë“œ 등)를 ë„용하려고 ì‹œë„ ì¤‘ì¼ ìˆ˜ 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ì´ ì‚¬ì´íŠ¸ì—서는 ë°©í•´ê°€ ë˜ê±°ë‚˜ 사용ìžë¥¼ 현혹하는 광고를 표시합니다.</translation>
-<translation id="286512204874376891">ê°€ìƒ ì¹´ë“œëŠ” 실제 카드를 숨겨서 사기 ìœ„í—˜ì„ ì¤„ì—¬ì¤ë‹ˆë‹¤. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">친근함</translation>
<translation id="28761159517501904">ì˜í™”</translation>
<translation id="2876489322757410363">ì‹œí¬ë¦¿ 모드를 종료하고 외부 애플리케ì´ì…˜ì—ì„œ 결제합니다. 계ì†í•˜ì‹œê² ìŠµë‹ˆê¹Œ?</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">디스í¬</translation>
<translation id="3162559335345991374">사용 ì¤‘ì¸ Wi-Fiì—ì„œ ë¡œê·¸ì¸ íŽ˜ì´ì§€ ë°©ë¬¸ì„ ìš”ì²­í•  수 있습니다.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">섬</translation>
<translation id="3176929007561373547">프ë¡ì‹œ ì„¤ì •ì„ í™•ì¸í•˜ê±°ë‚˜ ë„¤íŠ¸ì›Œí¬ ê´€ë¦¬ìžì—게 문ì˜í•˜ì—¬
프ë¡ì‹œ 서버가 ìž‘ë™í•˜ëŠ”지 확ì¸í•˜ì„¸ìš”. 프ë¡ì‹œ 서버를 사용하지 않으려면
ë‹¤ìŒ ë‹¨ê³„ë¥¼ 따르세요.
@@ -877,9 +882,6 @@
<translation id="3369192424181595722">시계 오류</translation>
<translation id="3369459162151165748">ìžë™ì°¨ 부품 ë° ì•¡ì„¸ì„œë¦¬</translation>
<translation id="3371064404604898522">Chromeì„ ê¸°ë³¸ 브ë¼ìš°ì €ë¡œ 설정</translation>
-<translation id="3371076217486966826"><ph name="URL" />ì—ì„œ ë‹¤ìŒ ê¶Œí•œì„ ìš”ì²­í•©ë‹ˆë‹¤.
- • ì£¼ë³€ì˜ 3D ì§€ë„ ë§Œë“¤ê¸° ë° ì¹´ë©”ë¼ ìœ„ì¹˜ 추ì 
- • ì¹´ë©”ë¼ ì‚¬ìš©</translation>
<translation id="337363190475750230">사용 중단ë¨</translation>
<translation id="3375754925484257129">Chrome 안전 í™•ì¸ ì‹¤í–‰</translation>
<translation id="3377144306166885718">서버ì—ì„œ ì§€ì› ì¤‘ë‹¨ëœ TLS ë²„ì „ì„ ì‚¬ìš©í–ˆìŠµë‹ˆë‹¤.</translation>
@@ -896,6 +898,7 @@
<translation id="3399952811970034796">배달 주소</translation>
<translation id="3402261774528610252">ì´ ì‚¬ì´íŠ¸ë¥¼ 로드하는 ë° ì‚¬ìš©ëœ ì—°ê²°ì—는 TLS 1.0 ë˜ëŠ” TLS 1.1ì´ ì‚¬ìš©ë˜ì—ˆìœ¼ë©°, ì´ëŸ¬í•œ TLS는 지ì›ì´ 중단ë˜ì–´ 향후 사용 ì¤‘ì§€ë  ì˜ˆì •ìž…ë‹ˆë‹¤. 사용 ì¤‘ì§€ëœ í›„ì—는 사용ìžê°€ ì´ ì‚¬ì´íŠ¸ë¥¼ 로드할 수 없습니다. 서버ì—ì„œ TLS 1.2 ì´ìƒì„ 사용해야 합니다.</translation>
<translation id="3405664148539009465">글꼴 맞춤설정</translation>
+<translation id="3407789382767355356">서드 파티 로그ì¸</translation>
<translation id="3409896703495473338">보안 설정 관리</translation>
<translation id="3414952576877147120">í¬ê¸°:</translation>
<translation id="3417660076059365994">업로드 ë˜ëŠ” 첨부한 파ì¼ì€ 분ì„ì„ ìœ„í•´ Google Cloud ë˜ëŠ” 타사로 전송ë©ë‹ˆë‹¤. 예를 들어, 민ê°í•œ ì •ë³´ ë˜ëŠ” 멀웨어를 찾기 위해 파ì¼ì„ 스캔할 수 있습니다.</translation>
@@ -928,6 +931,7 @@
<translation id="3477679029130949506">ì˜í™” ìƒì˜ 시간표</translation>
<translation id="3479552764303398839">나중ì—</translation>
<translation id="3484560055331845446">Google ê³„ì •ì— ì•¡ì„¸ìŠ¤í•˜ì§€ 못할 수 있습니다. ë”°ë¼ì„œ 지금 비밀번호를 변경하는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤. 로그ì¸í•˜ë¼ëŠ” 메시지가 표시ë©ë‹ˆë‹¤.</translation>
+<translation id="3484861421501147767">알림: ì €ìž¥ëœ í”„ë¡œëª¨ì…˜ 코드 사용 가능</translation>
<translation id="3487845404393360112">íŠ¸ë ˆì´ 4</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" />
페ì´ì§€ì—ì„œ 찾기</translation>
@@ -1052,6 +1056,7 @@
<translation id="3810973564298564668">관리</translation>
<translation id="3816482573645936981">ê°’(대체ë¨)</translation>
<translation id="382518646247711829">프ë¡ì‹œ 서버를 사용하는 경우</translation>
+<translation id="3826050100957962900">서드 파티 로그ì¸</translation>
<translation id="3827112369919217609">ë…립</translation>
<translation id="3827666161959873541">가족 ì˜í™”</translation>
<translation id="3828924085048779000">암호를 빈 칸으로 ë‘어서는 안 ë©ë‹ˆë‹¤.</translation>
@@ -1064,9 +1069,9 @@
<translation id="3858027520442213535">시간과 날짜 ì—…ë°ì´íŠ¸</translation>
<translation id="3858860766373142691">ì´ë¦„</translation>
<translation id="3872834068356954457">과학</translation>
+<translation id="3875783148670536197">방법 보기</translation>
<translation id="3881478300875776315">ìžë§‰ 접기</translation>
<translation id="3884278016824448484">기기 ì‹ë³„ìž ì¶©ëŒ</translation>
-<translation id="3885155851504623709">êµêµ¬</translation>
<translation id="388632593194507180">ëª¨ë‹ˆí„°ë§ ê°ì§€ë¨</translation>
<translation id="3886948180919384617">스태커 3</translation>
<translation id="3890664840433101773">ì´ë©”ì¼ ì¶”ê°€</translation>
@@ -1096,9 +1101,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" />ì´(ê°€) 차단ë¨</translation>
<translation id="3978338123949022456">검색 모드, 검색어를 입력하고 Enter 키를 눌러 ë‹¤ìŒ í‚¤ì›Œë“œë¥¼ 검색, <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">반려조</translation>
+<translation id="3985750352229496475">주소 관리...</translation>
<translation id="3986705137476756801">지금 실시간 ìžë§‰ 사용 중지하기</translation>
<translation id="3987940399970879459">1MB 미만</translation>
<translation id="3990250421422698716">조그 오프셋</translation>
+<translation id="3992684624889376114">ì´ íŽ˜ì´ì§€ì— 관한 ì •ë³´</translation>
<translation id="3996311196211510766">사ì´íŠ¸ <ph name="ORIGIN" />ì—ì„œ 모든 ìš”ì²­ì— ì¶œì²˜ ì •ì±…ì„ ì ìš©í•˜ë„ë¡ ìš”ì²­í–ˆìœ¼ë‚˜ ì´ ì •ì±…ì€ í˜„ìž¬ ì ìš©í•  수 없습니다.</translation>
<translation id="4006465311664329701">Google Payì— ì‚¬ìš©ë˜ëŠ” ê²°ì œ 수단, ì¿ í°, 주소</translation>
<translation id="4009243425692662128">ì¸ì‡„하는 페ì´ì§€ì˜ 콘í…츠가 분ì„ì„ ìœ„í•´ Google Cloud ë˜ëŠ” 타사로 전송ë©ë‹ˆë‹¤. 예를 들어, 민ê°í•œ 정보를 찾기 위해 í…스트를 스캔할 수 있습니다.</translation>
@@ -1218,6 +1225,7 @@
<translation id="4305666528087210886">파ì¼ì— 액세스할 수 ì—†ìŒ</translation>
<translation id="4306529830550717874">주소를 저장하시겠습니까?</translation>
<translation id="4306812610847412719">í´ë¦½ë³´ë“œ</translation>
+<translation id="4310070645992025887">íƒìƒ‰ 여정 검색</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">차단(기본값)</translation>
<translation id="4314815835985389558">ë™ê¸°í™” 관리</translation>
@@ -1268,6 +1276,7 @@
<translation id="4435702339979719576">엽서)</translation>
<translation id="443673843213245140">프ë¡ì‹œ ì‚¬ìš©ì€ ì¤‘ì§€ë˜ì—ˆì§€ë§Œ ëª…ì‹œì  í”„ë¡ì‹œ ì„¤ì •ì´ ì§€ì •ë˜ì–´ 있습니다.</translation>
<translation id="4441832193888514600">í´ë¼ìš°ë“œ ì‚¬ìš©ìž ì •ì±…ìœ¼ë¡œë§Œ 설정할 수 있는 ì •ì±…ì´ë¯€ë¡œ 무시ë˜ì—ˆìŠµë‹ˆë‹¤.</translation>
+<translation id="4442470707340296952">Chrome 탭</translation>
<translation id="4450893287417543264">다시 표시하지 ì•ŠìŒ</translation>
<translation id="4451135742916150903">HID ê¸°ê¸°ì— ì—°ê²°í•˜ë„ë¡ ìš”ì²­í•  수 있ìŒ</translation>
<translation id="4452328064229197696">방금 사용한 비밀번호가 ì •ë³´ 유출로 ì¸í•´ ë…¸ì¶œëœ ê²ƒìœ¼ë¡œ 확ì¸ë©ë‹ˆë‹¤. ê³„ì •ì„ ë³´í˜¸í•˜ê¸° 위해 Google 비밀번호 관리ìžì—ì„œ ì €ìž¥ëœ ë¹„ë°€ë²ˆí˜¸ë¥¼ 확ì¸í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤.</translation>
@@ -1406,6 +1415,7 @@
<translation id="483241715238664915">경고 켜기</translation>
<translation id="4834250788637067901">Google Payì— ì‚¬ìš©ë˜ëŠ” ê²°ì œ 수단, ì¿ í°, 주소</translation>
<translation id="4838327282952368871">꿈</translation>
+<translation id="4839087176073128681">다ìŒì—는 Googleì˜ ì„ ë„ì ì¸ 보안 기술로 ë” ë¹ ë¥´ê³  안전한 ì¹´ë“œ 결제를 ì´ìš©í•´ 보세요.</translation>
<translation id="4840250757394056958">Chrome 방문 ê¸°ë¡ ë³´ê¸°</translation>
<translation id="484462545196658690">ìžë™</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> 등ì—ì„œ í• ì¸ì„ 받으세요</translation>
@@ -1468,6 +1478,7 @@
<translation id="5011561501798487822">ê°ì§€ëœ 언어</translation>
<translation id="5015510746216210676">컴퓨터 ì´ë¦„:</translation>
<translation id="5017554619425969104">복사한 í…스트</translation>
+<translation id="5017828934289857214">ë‚˜ì¤‘ì— ì•Œë¦¼</translation>
<translation id="5018422839182700155">ì´ íŽ˜ì´ì§€ë¥¼ ì—´ 수 ì—†ìŒ</translation>
<translation id="5019198164206649151">ë³´ì¡° 기억 장치 ìƒíƒœê°€ 잘못ë¨</translation>
<translation id="5020776957610079374">ì œ3세계 ìŒì•…</translation>
@@ -1487,6 +1498,7 @@
<translation id="5051305769747448211">ë¼ì´ë¸Œ 코미디</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Nearby Share를 사용해 ì´ íŒŒì¼ì„ 전송하려면 기기ì—ì„œ 여유 공간(<ph name="DISK_SPACE_SIZE" />)ì„ í™•ë³´í•˜ì„¸ìš”.}other{Nearby Share를 사용해 파ì¼ì„ 전송하려면 기기ì—ì„œ 여유 공간(<ph name="DISK_SPACE_SIZE" />)ì„ í™•ë³´í•˜ì„¸ìš”.}}</translation>
<translation id="5056549851600133418">추천 기사</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> 주소를 사용하는 웹사ì´íŠ¸ì˜ ì¸ì¦ ë°©ì‹ìœ¼ë¡œ Windows Hello를 ì„ íƒí–ˆìŠµë‹ˆë‹¤. ì´ ì œê³µì—…ì²´ì—ì„œ ë‚´ ê²°ì œ 수단 정보를 ì €ìž¥í–ˆì„ ìˆ˜ 있으며 ì§ì ‘ <ph name="LINK_TEXT" />í•  수 있습니다.</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" />ì„(를) 찾으셨나요?</translation>
<translation id="5066056036849835175">ì¸ì‡„ 기ë¡</translation>
<translation id="5068234115460527047">헤지 펀드</translation>
@@ -1500,10 +1512,8 @@
<translation id="5087286274860437796">ì„œë²„ì˜ ì¸ì¦ì„œê°€ 현재 유효하지 않습니다.</translation>
<translation id="5087580092889165836">카드 추가</translation>
<translation id="5088142053160410913">ìš´ì˜ìžì—게 보내는 메시지</translation>
-<translation id="5089810972385038852">ì‹œ/ë„</translation>
<translation id="5093232627742069661">Z í´ë“œ</translation>
<translation id="5094747076828555589">ì´ ì„œë²„ê°€ <ph name="DOMAIN" />ìž„ì„ ìž…ì¦í•  수 없으며 Chromiumì—ì„œ 신뢰하는 보안 ì¸ì¦ì„œê°€ 아닙니다. 서버를 잘못 설정했거나 불법 사용ìžê°€ ì—°ê²°ì„ ê°€ë¡œì±„ê³  있기 ë•Œë¬¸ì¼ ìˆ˜ 있습니다.</translation>
-<translation id="5095208057601539847">주/ë„</translation>
<translation id="5097099694988056070">CPU/RAM 사용 ë“±ì˜ ê¸°ê¸° 통계</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">안전하지 ì•Šì€ ì‚¬ì´íŠ¸</translation>
@@ -1541,6 +1551,7 @@
<translation id="5171045022955879922">검색 ë˜ëŠ” URL ìž…ë ¥</translation>
<translation id="5171689220826475070">Fanfold-유럽</translation>
<translation id="5172758083709347301">컴퓨터</translation>
+<translation id="5177076414499237632">ì´ íŽ˜ì´ì§€ì˜ 출처 ë° ì£¼ì œì— ëŒ€í•´ 알아보기</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />가 아닙니까? 오류 신고</translation>
<translation id="518639307526414276">반려ë™ë¬¼ 사료 ë° ê´€ë¦¬ ìš©í’ˆ</translation>
<translation id="5190835502935405962">ë¶ë§ˆí¬ë°”</translation>
@@ -1701,6 +1712,7 @@
<translation id="5624120631404540903">비밀번호 관리</translation>
<translation id="5629630648637658800">정책 설정 로드 실패</translation>
<translation id="5631439013527180824">ìž˜ëª»ëœ ê¸°ê¸° 관리 토í°</translation>
+<translation id="5632485077360054581">방법 보기</translation>
<translation id="5633066919399395251">현재 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ì˜ ê³µê²©ìžê°€ ì‚¬ìš©ìž ì •ë³´(예: 사진, 비밀번호, 메시지, ì‹ ìš©ì¹´ë“œ)를 ë„용하거나 삭제하는 위험한 í”„ë¡œê·¸ëž¨ì„ ì»´í“¨í„°ì— ì„¤ì¹˜í•˜ë ¤ê³  ì‹œë„í•  수 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">사기성 콘í…츠 차단ë¨</translation>
<translation id="5633259641094592098">컬트 ë° ë…립 ì˜í™”</translation>
@@ -1818,6 +1830,7 @@
<translation id="5989320800837274978">ê³ ì • 프ë¡ì‹œ 서버와 .pac 스í¬ë¦½íŠ¸ URLì´ ëª¨ë‘ ì§€ì •ë˜ì§€ 않았습니다.</translation>
<translation id="5992691462791905444">ì—”ì§€ë‹ˆì–´ë§ Z í´ë“œ</translation>
<translation id="5995727681868049093">Google 계정ì—ì„œ ì •ë³´, ê°œì¸ ì •ë³´ 보호 ë° ë³´ì•ˆ 설정 관리</translation>
+<translation id="5997247540087773573">방금 사용한 비밀번호가 ì •ë³´ 유출로 ì¸í•´ ë…¸ì¶œëœ ê²ƒìœ¼ë¡œ 확ì¸ë©ë‹ˆë‹¤. ê³„ì •ì„ ë³´í˜¸í•˜ê¸° 위해 Google 비밀번호 관리ìžì—ì„œ 지금 바로 비밀번호를 변경하고 ì €ìž¥ëœ ë¹„ë°€ë²ˆí˜¸ë¥¼ 확ì¸í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' 검색결과 <ph name="RESULT_COUNT" />개</translation>
<translation id="6006484371116297560">기본</translation>
<translation id="6008122969617370890">N-to-1 순서</translation>
@@ -1913,7 +1926,6 @@
<translation id="627746635834430766">다ìŒë²ˆì— ë” ë¹ ë¥´ê²Œ 결제할 수 있ë„ë¡ Google ê³„ì •ì— ì¹´ë“œì™€ 청구서 수신 주소를 저장하세요.</translation>
<translation id="6279183038361895380">|<ph name="ACCELERATOR" />|ì„(를) 눌러 커서 표시</translation>
<translation id="6280223929691119688">ì´ ì£¼ì†Œë¡œ 배달할 수 없습니다. 다른 주소를 ì„ íƒí•˜ì„¸ìš”.</translation>
-<translation id="6282194474023008486">우편번호</translation>
<translation id="6285507000506177184">Chrome 다운로드 관리 버튼, Chromeì—ì„œ 다운로드한 파ì¼ì„ 관리하려면 Enter를 누르세요</translation>
<translation id="6289939620939689042">페ì´ì§€ 색ìƒ</translation>
<translation id="6290238015253830360">추천 콘í…츠가 ì—¬ê¸°ì— í‘œì‹œë©ë‹ˆë‹¤.</translation>
@@ -1935,6 +1947,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> ë¯¸ë§Œì˜ ì €ìž¥ìš©ëŸ‰ì„ í™•ë³´í•©ë‹ˆë‹¤. ì¼ë¶€ 사ì´íŠ¸ëŠ” ë‹¤ìŒ ë°©ë¬¸ ì‹œ 로드 ì†ë„ê°€ ëŠë ¤ì§ˆ 수 있습니다.</translation>
<translation id="6337534724793800597">ì´ë¦„별로 ì •ì±… í•„í„°ë§</translation>
<translation id="6340739886198108203">ê´€ë¦¬ìž ì •ì±…ì— ë”°ë¼ ê¸°ë°€ 콘í…츠가 í™”ë©´ì— í‘œì‹œë˜ì–´ ìžˆì„ ë•Œ 스í¬ë¦°ìƒ·ì„ ì°ê±°ë‚˜ 녹화하지 않는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤.</translation>
+<translation id="6348220984832452017">활성 버전</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> 설치</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ì—†ìŒ}=1{비밀번호 1ê°œ(ë„ë©”ì¸: <ph name="DOMAIN_LIST" />, ë™ê¸°í™”ë¨)}=2{비밀번호 2ê°œ(ë„ë©”ì¸: <ph name="DOMAIN_LIST" />, ë™ê¸°í™”ë¨)}other{비밀번호 #ê°œ(ë„ë©”ì¸: <ph name="DOMAIN_LIST" />, ë™ê¸°í™”ë¨)}}</translation>
<translation id="6355392890578844978">ì´ ë¸Œë¼ìš°ì €ëŠ” 회사 ë˜ëŠ” 다른 ì¡°ì§ì—ì„œ 관리하지 않습니다. ì´ ê¸°ê¸°ì˜ í™œë™ì€ Chromium 외부ì—ì„œë„ ê´€ë¦¬í•  수 있습니다. <ph name="BEGIN_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LINK" /></translation>
@@ -1966,7 +1979,6 @@
<translation id="643051589346665201">Google 비밀번호 변경</translation>
<translation id="6433490469411711332">ì—°ë½ì²˜ ì •ë³´ 수정</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" />ì—ì„œ ì—°ê²°ì„ ê±°ë¶€í–ˆìŠµë‹ˆë‹¤.</translation>
-<translation id="6438025220577812695">ì§ì ‘ 변경</translation>
<translation id="6440503408713884761">무시ë¨</translation>
<translation id="6443406338865242315">ë‚´ê°€ 설치한 확장 프로그램 ë° í”ŒëŸ¬ê·¸ì¸</translation>
<translation id="6446163441502663861">Kahu(봉투)</translation>
@@ -2096,9 +2108,9 @@
<translation id="6828866289116430505">유전학</translation>
<translation id="6831043979455480757">번역</translation>
<translation id="6833752742582340615">ì¹´ë“œ ë° ê²°ì œ 정보를 Google ê³„ì •ì— ì €ìž¥í•˜ì—¬ ë” ë¹ ë¥´ê³  안전하게 결제하세요.</translation>
-<translation id="6839929833149231406">지구</translation>
<translation id="6846340164947227603">ê°€ìƒ ì¹´ë“œ 번호 사용...</translation>
<translation id="6852204201400771460">ì•±ì„ ìƒˆë¡œê³ ì¹¨í•˜ì‹œê² ìŠµë‹ˆê¹Œ?</translation>
+<translation id="6857776781123259569">비밀번호 관리...</translation>
<translation id="686485648936420384">소비ìžìš© 참고 ì •ë³´</translation>
<translation id="6865412394715372076">ì§€ê¸ˆì€ ì´ ì¹´ë“œë¥¼ ì¸ì¦í•  수 없습니다.</translation>
<translation id="6869334554832814367">ê°œì¸ ëŒ€ì¶œ</translation>
@@ -2147,7 +2159,6 @@
<translation id="6965978654500191972">기기</translation>
<translation id="696703987787944103">가시 범위</translation>
<translation id="6968269510885595029">보안 키 사용</translation>
-<translation id="6970216967273061347">주소</translation>
<translation id="6971439137020188025">Slidesì—ì„œ 빠르게 새 Google 프레젠테ì´ì…˜ 만들기</translation>
<translation id="6972629891077993081">HID 기기</translation>
<translation id="6973656660372572881">ê³ ì • 프ë¡ì‹œ 서버와 .pac 스í¬ë¦½íŠ¸ URLì´ ëª¨ë‘ ì§€ì •ë˜ì–´ 있습니다.</translation>
@@ -2186,7 +2197,6 @@
<translation id="7081308185095828845">ì´ ê¸°ê¸°ì—ì„œ 지ì›í•˜ì§€ 않는 기능</translation>
<translation id="7083258188081898530">íŠ¸ë ˆì´ 9</translation>
<translation id="7086090958708083563">사용ìžê°€ 요청한 업로드</translation>
-<translation id="7087282848513945231">ì¹´ìš´í‹°</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Tabì„ ëˆ„ë¥¸ ë‹¤ìŒ Enter를 눌러 Chrome 설정ì—ì„œ 여러 사ì´íŠ¸ì˜ 권한 ë° ì €ìž¥ëœ ë°ì´í„° 관리</translation>
<translation id="7096937462164235847">ì¸ì¦ëœ 웹사ì´íŠ¸ê°€ 아닙니다.</translation>
<translation id="7101893872976785596">ê³µí¬ ì˜í™”</translation>
@@ -2205,10 +2215,10 @@
<ph name="LIST_ITEM" />ì–‘ì‹ì— ìž…ë ¥ëœ ì •ë³´<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">ì´ ì£¼ì†Œë¡œ 배송할 수 없습니다. 다른 주소를 ì„ íƒí•˜ì„¸ìš”.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">관리ìžê°€ 복사를 금지한 ë°ì´í„°ìž…니다.</translation>
<translation id="7135130955892390533">ìƒíƒœ 보기</translation>
<translation id="7138472120740807366">배달 방법</translation>
-<translation id="7139724024395191329">ì—미레ì´íŠ¸</translation>
<translation id="7139892792842608322">기본 트레ì´</translation>
<translation id="714064300541049402">ë‘ ë²ˆì§¸ ë©´ ì´ë¯¸ì§€ X 시프트</translation>
<translation id="7152423860607593928">Number-14(봉투)</translation>
@@ -2424,6 +2434,7 @@
<translation id="7669271284792375604">ì´ ì‚¬ì´íŠ¸ì˜ 공격ìžê°€ ì¸í„°ë„· 사용 í™˜ê²½ì— ì•…ì˜í–¥ì„ 미치는 í”„ë¡œê·¸ëž¨ì„ ì„¤ì¹˜í•˜ë„ë¡ ì†ìž„수(예를 들어, 방문하는 사ì´íŠ¸ì˜ 홈페ì´ì§€ë¥¼ 변경하거나 추가로 광고를 표시)를 ì‹œë„í•  수 있습니다.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{기밀로 í‘œì‹œëœ ë°ì´í„°ë¡œ 실행한 ìž‘ì—…(ë¡œê·¸ì¸ ì´í›„ ìž‘ì—… 1ê°œ) <ph name="BEGIN_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LINK" />}other{기밀로 í‘œì‹œëœ ë°ì´í„°ë¡œ 실행한 ìž‘ì—…(ë¡œê·¸ì¸ ì´í›„ ìž‘ì—… #ê°œ) <ph name="BEGIN_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ë©”ì¼ë°•ìŠ¤ 6</translation>
+<translation id="7675325315208090829">결제 수단 관리...</translation>
<translation id="7676643023259824263">í´ë¦½ë³´ë“œ í…스트 <ph name="TEXT" /> 검색</translation>
<translation id="7679367271685653708">Chrome 설정ì—ì„œ 방문 ê¸°ë¡ ë³´ê¸° ë° ê´€ë¦¬</translation>
<translation id="7679947978757153706">야구</translation>
@@ -2466,7 +2477,6 @@
<translation id="7766518757692125295">스커트</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">ê°™ì€ ìˆœì„œë¡œ ì¸ì‡„ë©´ì´ ìœ„ë¥¼ 향하게</translation>
-<translation id="777702478322588152">현</translation>
<translation id="7791011319128895129">미공개</translation>
<translation id="7791196057686275387">ë² ì¼</translation>
<translation id="7791543448312431591">추가</translation>
@@ -2557,12 +2567,12 @@
<translation id="8055534648776115597">ì§ì—…êµìœ¡ ë° í‰ìƒêµìœ¡</translation>
<translation id="8057711352706143257">'<ph name="SOFTWARE_NAME" />ì´(ê°€) 올바르게 설정ë˜ì§€ 않았습니다. ì¼ë°˜ì ìœ¼ë¡œ '<ph name="SOFTWARE_NAME" />'ì„(를) 제거하면 문제가 í•´ê²°ë©ë‹ˆë‹¤. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ì‹í’ˆ ìƒì‚°</translation>
-<translation id="8066955247577885446">문제 ë°œìƒ</translation>
<translation id="8067872629359326442">사기성 사ì´íŠ¸ì— 비밀번호를 입력했습니다. Chromiumì´ ê³„ì • 보호를 ë„와드립니다. 비밀번호를 변경하고 계정 보안 ìœ„í—˜ì— ê´€í•´ Googleì— ì•Œë¦¬ë ¤ë©´ 계정 보호를 í´ë¦­í•˜ì„¸ìš”.</translation>
<translation id="8070439594494267500">앱 ì•„ì´ì½˜</translation>
<translation id="8074253406171541171">10x13(봉투)</translation>
<translation id="8075736640322370409">빠르게 새 Google 시트 만들기</translation>
<translation id="8075898834294118863">사ì´íŠ¸ 설정 관리</translation>
+<translation id="8076492880354921740">탭</translation>
<translation id="8078141288243656252">회전 ì‹œ 주ì„ì„ ë‹¬ 수 없습니다.</translation>
<translation id="8079031581361219619">사ì´íŠ¸ë¥¼ 새로고침하시겠습니까?</translation>
<translation id="8081087320434522107">세단</translation>
@@ -2685,6 +2695,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">설정</translation>
+<translation id="8428634594422941299">확ì¸</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Tabì„ ëˆ„ë¥¸ ë‹¤ìŒ Enter를 눌러 Chrome 설정ì—ì„œ 쿠키 환경설정 관리</translation>
<translation id="8433057134996913067">ì´ ìž‘ì—…ì„ ìˆ˜í–‰í•˜ë©´ ëŒ€ë¶€ë¶„ì˜ ì›¹ì‚¬ì´íŠ¸ì—ì„œ 로그아웃ë©ë‹ˆë‹¤.</translation>
<translation id="8434840396568290395">반려ë™ë¬¼</translation>
@@ -2782,6 +2793,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> 코드는 <ph name="ONE_TIME_CODE" />입니다.</translation>
<translation id="874918643257405732">현재 íƒ­ì„ ë¶ë§ˆí¬ì— 추가</translation>
<translation id="8751426954251315517">ë‚˜ì¤‘ì— ë‹¤ì‹œ ì‹œë„í•´ 주세요.</translation>
+<translation id="8757526089434340176">Google Pay ì¿ í° ì´ìš© 가능</translation>
<translation id="8758885506338294482">e스í¬ì¸ </translation>
<translation id="8759274551635299824">ë§Œë£Œëœ ì¹´ë“œìž…ë‹ˆë‹¤.</translation>
<translation id="87601671197631245">ì´ ì‚¬ì´íŠ¸ì—서는 ì˜¤ëž˜ëœ ë³´ì•ˆ ì„¤ì •ì„ ì‚¬ìš©í•˜ë¯€ë¡œ ê°œì¸ì •ë³´(예: 비밀번호, 메시지 ë˜ëŠ” ì‹ ìš©ì¹´ë“œ)를 ì´ ì‚¬ì´íŠ¸ë¡œ 전송할 경우 ì •ë³´ê°€ ìœ ì¶œë  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤.</translation>
@@ -2789,6 +2801,7 @@
<translation id="8763927697961133303">USB 기기</translation>
<translation id="8763986294015493060">현재 ì—´ë ¤ 있는 ì‹œí¬ë¦¿ ì°½ ëª¨ë‘ ë‹«ê¸°</translation>
<translation id="8766943070169463815">보안 ê²°ì œ ì‚¬ìš©ìž ì¸ì¦ ì •ë³´ ìŠ¹ì¸ ì‹œíŠ¸ê°€ 열림</translation>
+<translation id="8767765348545497220">ë„ì›€ë§ í’ì„  닫기</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">오토바ì´</translation>
<translation id="8790007591277257123">삭제 다시 실행(&amp;R)</translation>
@@ -2801,6 +2814,7 @@
<translation id="8806285662264631610">목욕 ë° ë°”ë”” 제품</translation>
<translation id="8807160976559152894">ê° íŽ˜ì´ì§€ ë’¤ì— íŠ¸ë¦¼</translation>
<translation id="8808828119384186784">Chrome 설정</translation>
+<translation id="8813277370772331957">ë‚˜ì¤‘ì— ì•Œë¦¼</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome 설정ì—ì„œ Chromeì„ ì—…ë°ì´íŠ¸í•˜ë ¤ë©´ Tabì„ ëˆ„ë¥¸ ë‹¤ìŒ Enter 누르기</translation>
<translation id="8820817407110198400">ë¶ë§ˆí¬</translation>
<translation id="882338992931677877">ìˆ˜ë™ ìŠ¬ë¡¯</translation>
@@ -2980,6 +2994,7 @@
<translation id="988159990683914416">ê°œë°œìž ë¹Œë“œ</translation>
<translation id="989988560359834682">주소 수정</translation>
<translation id="991413375315957741">움ì§ìž„ ê°ì§€ ë˜ëŠ” ì¡°ë„ ì„¼ì„œ</translation>
+<translation id="992110854164447044">ê°€ìƒ ì¹´ë“œëŠ” 실제 카드를 숨겨서 사기당할 ìœ„í—˜ì„ ì¤„ì—¬ì¤ë‹ˆë‹¤. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">분í™ìƒ‰</translation>
<translation id="992432478773561401">'<ph name="SOFTWARE_NAME" />'ì´(ê°€) 컴퓨터 ë˜ëŠ” 네트워í¬ì— 제대로 설치ë˜ì§€ 않았습니다.
diff --git a/chromium/components/strings/components_strings_ky.xtb b/chromium/components/strings/components_strings_ky.xtb
index 7f2feae5c2f..a46d9bd1fdf 100644
--- a/chromium/components/strings/components_strings_ky.xtb
+++ b/chromium/components/strings/components_strings_ky.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Гранттар, ÑтипендиÑлар жана каржылык жардам</translation>
<translation id="1048785276086539861">ÐннотациÑларды түзөтүүдө бул документ бир бет көрүнүшүнө кайтып келет</translation>
<translation id="1050038467049342496">Башка колдонмолорду жабуу</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> вебÑайттарында Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ñ‚Ò¯Ð·Ð¼Ó©Ð³Ò¯Ð½ колдонууну тандаганÑыз. Бул провайдер төлөм ыкмаңызды Ñактап койгон болушу мүмкүн. Ðны <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">Кошууну &amp;жаÑабоо</translation>
<translation id="1056663316309890343">Фото үчүн программалык камÑыздоо</translation>
<translation id="1056898198331236512">ЭÑкертүү</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">Ðлып кетүү ыкмаÑÑ‹</translation>
<translation id="1281476433249504884">6-төшөгүч</translation>
<translation id="1285320974508926690">Бул Ñайт Ñч качан которулбаÑын</translation>
+<translation id="1288548991597756084">Карта коопÑуз Ñакталды</translation>
<translation id="1292571435393770077">16-түпкүч</translation>
<translation id="1292701964462482250">"Компьютериңизде иштеп жаткан программа Chrome’дун Интернетке коопÑуз туташууÑуна тоÑкоол болуп жатат" (Windows компьютерлери үчүн гана)</translation>
<translation id="1294154142200295408">Буйрук Ñабынын варианттары</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Катаны оңдоо үчүн ачууга аракет кылып жаткан барактан &lt;strong&gt;Туташуу&lt;/strong&gt; дегенди чыкылдатыңыз.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Ландшафттык дизайн</translation>
<translation id="1513706915089223971">Таржымалдагы жазуулардын тизмеÑи</translation>
+<translation id="1516097932025103760">Ðл шифрленип, коопÑуз Ñакталды. CVC Ñч качан Ñакталбайт.</translation>
<translation id="1517433312004943670">Телефон номери талап кылынат</translation>
<translation id="1519264250979466059">Курама күнү</translation>
<translation id="1521159554480556801">Була жана текÑтиль көркөм өнөрү</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Төлөм ыкмаларын Ñактоо жана автоматтык түрдө толтуруу</translation>
<translation id="1663943134801823270">Карточкалар жана даректер Chrome’дон алынган. Ðларды <ph name="BEGIN_LINK" />Жөндөөлөрдөн<ph name="END_LINK" /> башкарÑаңыз болот.</translation>
<translation id="1671391448414634642">Мындан кийин <ph name="SOURCE_LANGUAGE" /> тилиндеги барактар <ph name="TARGET_LANGUAGE" /> тилине которулат.</translation>
+<translation id="1673886523110456987">Бул Ñунушту колдонуу үчүн <ph name="CARD_DETAIL" /> картаÑын колдонуп көрүңүз</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> тилинен <ph name="TARGET_LANGUAGE" /> тилине</translation>
<translation id="1682696192498422849">Биринчи туураÑÑ‹ боюнча</translation>
<translation id="168693727862418163">Бул ÑаÑÑаттын мааниÑи ÑхемаÑына дал келбей калды, андыктан Ñтибарга алынбайт.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Кыймыл ÑенÑорлору</translation>
<translation id="1717494416764505390">3-Ñлектрондук каттар кутуÑу</translation>
<translation id="1718029547804390981">Документ Ó©Ñ‚Ó© чоң, андыктан ÐÐ½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ð¸Ð½Ð´Ðµ түзөтүүгө болбойт.</translation>
+<translation id="1720941539803966190">Үйрөткүчтү жабуу</translation>
<translation id="1721424275792716183">* Бул талаа милдеттүү түрдө толтурулушу керек</translation>
<translation id="1727613060316725209">ТаÑтыктама жарамдуу</translation>
<translation id="1727741090716970331">Жарактуу карточканын номерин кошуу</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">Барбекю жана гриль</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> тилиндеги барактар которулбайт.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Бул көзөмөлдөө каражаты күйүк жана абалы жигердүү болгондо, Chrome акыркы көргөн вебÑайттарыңыз адамдардын кайÑÑ‹ чоң тобуна же "когортаÑына" ÑÒ£ ылайыктуу Ñкенин аныктайт. Жарнамачылар ал топ үчүн жарнамаларды тандай алат жана көргөн вебÑайттарыңыз түзмөгүңүздө ÐºÑƒÐ¿ÑƒÑ Ñакталат. Тобуңуз күн Ñайын жаңыртылып турат.}=1{Бул көзөмөлдөө каражаты күйүк жана абалы жигердүү болгондо, Chrome акыркы көргөн вебÑайттарыңыз адамдардын кайÑÑ‹ чоң тобуна же "когортаÑына" ÑÒ£ ылайыктуу Ñкенин аныктайт. Жарнамачылар ал топ үчүн жарнамаларды тандай алат жана көргөн вебÑайттарыңыз түзмөгүңүздө ÐºÑƒÐ¿ÑƒÑ Ñакталат. Тобуңуз күн Ñайын жаңыртылып турат.}other{Бул көзөмөлдөө каражаты күйүк жана абалы жигердүү болгондо, Chrome акыркы көргөн вебÑайттарыңыз адамдардын кайÑÑ‹ чоң тобуна же "когортаÑына" ÑÒ£ ылайыктуу Ñкенин аныктайт. Жарнамачылар ал топ үчүн жарнамаларды тандай алат жана көргөн вебÑайттарыңыз түзмөгүңүздө ÐºÑƒÐ¿ÑƒÑ Ñакталат. Тобуңуз {NUM_DAYS} күн Ñайын жаңыртылып турат.}}</translation>
-<translation id="2053553514270667976">Почта индекÑи</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 Ñунуш}other{# Ñунуш}}</translation>
+<translation id="2066915425250589881">өчүрүүнү ÑуранÑаңыз болот</translation>
<translation id="2068528718802935086">Ымыркайлар жана нариÑтелер</translation>
<translation id="2071156619270205202">Бул карта виртуалдык картанын номери катары жарамдуу ÑмеÑ.</translation>
<translation id="2071692954027939183">Билдирмелер автоматтык түрдө бөгөттөлдү, анткени, адатта, аларга урукÑат бербейÑиз</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">Шайкештирүүнү башкаруу баÑкычы, Chrome жөндөөлөрүнөн шайкештирилген маалыматты башкаруу үчүн Enter баÑкычын баÑыңыз</translation>
<translation id="2091887806945687916">Үн</translation>
<translation id="2094505752054353250">Домен дал келбейт</translation>
-<translation id="2096368010154057602">Бөлүм</translation>
<translation id="2099652385553570808">Сол жагын үч жолу илмек менен бекитүү</translation>
<translation id="2101225219012730419">ВерÑиÑÑÑ‹:</translation>
<translation id="2102134110707549001">Татаал ÑÑ‹Ñ€Ñөз ÑунушталÑын…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">Ðмерика футболу</translation>
<translation id="2187317261103489799">Ðныктоо (демейки)</translation>
<translation id="2188375229972301266">Төмөнкү жагын бир нече жолу тешүү</translation>
-<translation id="2188852899391513400">Жаңы Ñле колдонгон ÑÑ‹Ñ€Ñөзүңүздү кимдир бирөө билип алганы аныкталды. Ðккаунттарыңыздын коопÑуздугун коргоо үчүн Google'дун СырÑөздөрдү башкаргычы аны азыр өзгөртүп жана башка Ñакталган ÑÑ‹Ñ€Ñөздөрүңүздү текшерүүнү Ñунуштайт.</translation>
<translation id="219906046732893612">Үйдү жакшыртуу</translation>
<translation id="2202020181578195191">Мөөнөтү аÑктоочу жылды туура киргизиңиз</translation>
<translation id="22081806969704220">3-түпкүч</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">Файл түзөтүү</translation>
<translation id="2215963164070968490">Иттер</translation>
<translation id="2218879909401188352">Учурда <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> Ñайтындагы чабуулчулар түзмөгүңүзгө зыÑн келтирүүчү колдонмолорду орнотуп, мобилдик ÑÑебиңизге жашыруун төлөмдөрдү кошуп же жеке маалыматыңызды уурдашы мүмкүн. <ph name="BEGIN_LEARN_MORE_LINK" />Кеңири маалымат<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Үйрөткүчтү кайра ачуу</translation>
+<translation id="2219735899272417925">Түзмөктү баштапкы абалга келтирүү керек</translation>
<translation id="2224337661447660594">Интернет жок</translation>
<translation id="2230458221926704099">Бузулууларды оңдоо үчүн туташуунун абалын <ph name="BEGIN_LINK" />текшериңиз<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Ðзыр жөнөтүү</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">Тыюу Ñалынган (демейки)</translation>
<translation id="2512413427717747692">Chrome'ду демейки Ñерепчи катары коюу баÑкычы, iOS жөндөөлөрүнөн Chrome'ду ÑиÑтеманын демейки ÑерепчиÑи катары коюу үчүн Enter баÑкычын баÑыңыз</translation>
<translation id="2515629240566999685">Ðймагыңыздагы Ñигналды текшерип көрүңүз</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> вебÑайттарында Touch ID функциÑÑын колдонууну тандаганÑыз. Бул провайдер төлөм ыкмаңызды Ñактап койгон болушу мүмкүн. Ðны <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Төмөнк оң жагын илмек менен бекитүү</translation>
<translation id="2521736961081452453">Форма түзүү</translation>
<translation id="2523886232349826891">Ушул түзмөккө гана Ñакталды</translation>
<translation id="2524461107774643265">Көбүрөөк маалымат кошуу</translation>
<translation id="2529899080962247600">Бул талаага киргизилген маанилердин Ñаны <ph name="MAX_ITEMS_LIMIT" /> ашпашы керек. Бардык андан кийинки жазмалар четке кагылат.</translation>
+<translation id="253493526287553278">Промокоддун чоо-жайын көрүү</translation>
<translation id="2535585790302968248">Жаңы Жашыруун өтмөктү ачып, ÐºÑƒÐ¿ÑƒÑ Ñерептеңиз</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{жана дагы 1}other{жана дагы #}}</translation>
<translation id="2536110899380797252">Дарегин кошуу</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">Фото жана Ñанарип көркөм өнөрү</translation>
<translation id="2601150049980261779">Романтикалык таÑмалар</translation>
<translation id="2604589665489080024">Поп-музыка</translation>
-<translation id="2609632851001447353">Варианттар</translation>
<translation id="2610561535971892504">Көчүрүү үчүн чыкылдатыңыз</translation>
<translation id="2617988307566202237">Chrome төмөнкү маалыматты <ph name="BEGIN_EMPHASIS" />Ñактабайт<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Туулган күндөр</translation>
<translation id="2677748264148917807">Чыгуу</translation>
+<translation id="2679714844901977852">КоопÑуз жана тезирÑÑк төлөө үчүн картаңызды жана ÑÑеп коюу маалыматын Google аккаунтуңузга (<ph name="USER_EMAIL" />) Ñактап коюңуз</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Туура формат</translation>
<translation id="2688969097326701645">Ооба, уланта берели</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">Интернет кызматын жабдуучулар (ISP'лер)</translation>
<translation id="2856444702002559011">Чабуулчулар <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> дарегинен маалыматыңызды (миÑалы, ÑÑ‹Ñ€Ñөздөр, билдирүүлөр же наÑÑ‹Ñ ÐºÐ°Ñ€Ñ‚Ð°Ð»Ð°Ñ€Ñ‹) уурдоого аракет кылып жатышы мүмкүн. <ph name="BEGIN_LEARN_MORE_LINK" />Кеңири маалымат<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Бул Ñайт тажатма же адаштыруучу жарнамаларды көрÑөтүп жатат.</translation>
-<translation id="286512204874376891">Шылуундуктан коргоого жардам берүү үчүн виртуалдык карта чыныгы картаңыздын маалыматын жашырат. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Ынак</translation>
<translation id="28761159517501904">ТаÑмалар</translation>
<translation id="2876489322757410363">Тышкы колдонмо аркылуу төлөө үчүн жашыруун режимден чыкканы жатаÑыз. Уланта береÑизби?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ДиÑк</translation>
<translation id="3162559335345991374">Сиз колдонуп жаткан Wi-Fi башкы кирүү барагына өтүшүңүздү талап кылышы мүмкүн.</translation>
<translation id="3169472444629675720">Ðныктоо</translation>
-<translation id="3174168572213147020">Ðрал</translation>
<translation id="3176929007561373547">ПрокÑи Ñерверинин иштеп жатканын такташ үчүн, прокÑи жөндөөлөрүңүздү
текшерип же тармактык админиÑтраторуңузга кайрылыңыз. ПрокÑи
Ñерверин колдонгум келбейт деÑеңиз
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">Саат катаÑÑ‹</translation>
<translation id="3369459162151165748">Унаанын тетиктери жана акÑеÑÑуарлары</translation>
<translation id="3371064404604898522">Chrome'ду демейки Ñерепчи катары коюу</translation>
-<translation id="3371076217486966826"><ph name="URL" /> төмөнкүнү аткарганы жатат:
- • Ðйланаңыздын 3D картаÑын түзүү жана камераңыздын абалына көз Ñалуу
- • Камераны колдонуу</translation>
<translation id="337363190475750230">Чыгарылды</translation>
<translation id="3375754925484257129">Chrome'дун коопÑуздугун текшерүү</translation>
<translation id="3377144306166885718">Сервер TLS'тин жоюлган верÑиÑÑын колдонду.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">Жеткирүү дареги</translation>
<translation id="3402261774528610252">Бул Ñайтты жүктөө үчүн TLS 1.0 же TLS 1.1 верÑиÑÑÑ‹ колдонулду. Ðлар жоюлган жана келечекте өчүрүлөт. Өчүрүлгөндөн кийин колдонуучулар бул Ñайтты жүктөй албай калышат. Сервер TLS 1.2 же андан кийинки верÑиÑны иштетиши керек.</translation>
<translation id="3405664148539009465">Ðриптерди өзгөчөлөштүрүү</translation>
+<translation id="3407789382767355356">үчүнчү тараптын кирүү аракети</translation>
<translation id="3409896703495473338">КоопÑуздук жөндөөлөрүн башкаруу</translation>
<translation id="3414952576877147120">Өлчөм:</translation>
<translation id="3417660076059365994">Сиз жүктөп берген же тиркеген файлдар Google Булутка же үчүнчү тараптын кызматтарына талдоо үчүн жөнөтүлөт. МиÑалы, аларда ÐºÑƒÐ¿ÑƒÑ Ð¼Ð°Ð°Ð»Ñ‹Ð¼Ð°Ñ‚Ñ‚Ñ‹Ð½ же кеÑепеттүү программанын болууÑу текшерилет.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">Кинолордун тизмеÑи жана театрдын көрÑÓ©Ñ‚Ò¯Ò¯ убактыÑÑ‹</translation>
<translation id="3479552764303398839">Ðзыр ÑмеÑ</translation>
<translation id="3484560055331845446">Кимдир бирөө Google аккаунтуңузга кирип алган окшойт. Chrome ÑÑ‹Ñ€Ñөзүңүздү азыр өзгөртүүнү Ñунуштайт. Ðккаунтуңузга кайра киришиңиз керек болот.</translation>
+<translation id="3484861421501147767">ЭÑтеткич: Сакталган промокод жеткиликтүү</translation>
<translation id="3487845404393360112">4-түпкүч</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" /> барагынан табуу</translation>
<translation id="350069200438440499">Файлдын аталышы:</translation>
@@ -1055,6 +1059,7 @@
<translation id="3810973564298564668">Башкаруу</translation>
<translation id="3816482573645936981">Маани (алмаштырылган)</translation>
<translation id="382518646247711829">Эгер прокÑи Ñервериңизди колдонÑоңуз…</translation>
+<translation id="3826050100957962900">Үчүнчү тараптын кирүү аракети</translation>
<translation id="3827112369919217609">ÐбÑолют</translation>
<translation id="3827666161959873541">Үй-бүлөлүк таÑмалар</translation>
<translation id="3828924085048779000">ÐšÑƒÐ¿ÑƒÑ Ñөз айкашын бош калтырууга болбойт.</translation>
@@ -1067,9 +1072,9 @@
<translation id="3858027520442213535">Күн менен убакытты жаңыртуу</translation>
<translation id="3858860766373142691">Ðталышы</translation>
<translation id="3872834068356954457">Илим</translation>
+<translation id="3875783148670536197">Кантип?</translation>
<translation id="3881478300875776315">Ðзыраак көрÑÓ©Ñ‚Ò¯Ò¯</translation>
<translation id="3884278016824448484">Түзмөктүн далдаштыргычтары дал келбей кагылышууда</translation>
-<translation id="3885155851504623709">ПÑриш</translation>
<translation id="388632593194507180">Көзөмөлдөө аныкталды</translation>
<translation id="3886948180919384617">3-төшөгүч</translation>
<translation id="3890664840433101773">Электрондук почта дарегин кошуу</translation>
@@ -1099,9 +1104,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> Ñайты бөгөттөлгөн</translation>
<translation id="3978338123949022456">Издөө режими, <ph name="KEYWORD_SUFFIX" /> кызматында издөө үчүн Ñурамды терип, Enter баÑкычын баÑыңыз</translation>
<translation id="398470910934384994">Куштар</translation>
+<translation id="3985750352229496475">Даректерди башкаруу…</translation>
<translation id="3986705137476756801">Ыкчам коштомо жазууларды азырынча өчүрүү</translation>
<translation id="3987940399970879459">1 Мб азыраак</translation>
<translation id="3990250421422698716">ОффÑеттик баÑып чыгаруу</translation>
+<translation id="3992684624889376114">Бул бет жөнүндө</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> Ñайты бардык кирүү Ñурамдары үчүн баштапкы ÑаÑÑаты
колдонулÑун деп Ñуранды, бирок бул ÑаÑÑатты учурда колдонууга болбойт.</translation>
<translation id="4006465311664329701">Google Pay'ди колдонгон төлөм ыкмалары, Ñунуштар жана даректер</translation>
@@ -1226,6 +1233,7 @@
<translation id="4305666528087210886">Файл ачылбай койду</translation>
<translation id="4306529830550717874">Дарек ÑакталÑынбы?</translation>
<translation id="4306812610847412719">алмашуу буфери</translation>
+<translation id="4310070645992025887">СаÑкаттарыңызды издеңиз</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Бөгөттөө (демейки)</translation>
<translation id="4314815835985389558">Шайкештирүүнү башкаруу</translation>
@@ -1276,6 +1284,7 @@
<translation id="4435702339979719576">Ðчык кат)</translation>
<translation id="443673843213245140">ПрокÑи пайдалануу мүмкүнчүлүгү өчүрүлгөнү менен, ачык-айкын прокÑи конфигурациÑÑÑ‹ белгиленген.</translation>
<translation id="4441832193888514600">Четке кагылды, анткени ÑаÑÑатты булуттагы колдонуучулар үчүн ÑаÑÑат катары гана коюуга болот.</translation>
+<translation id="4442470707340296952">Chrome өтмөктөрү</translation>
<translation id="4450893287417543264">Экинчи көрүнбөÑүн</translation>
<translation id="4451135742916150903">HID түзмөктөрүнө туташууга урукÑат Ñурай алат</translation>
<translation id="4452328064229197696">Жаңы Ñле колдонгон ÑÑ‹Ñ€Ñөзүңүздү кимдир бирөө билип алганы аныкталды. Ðккаунттарыңыздын коопÑуздугун коргоо үчүн Google'дун СырÑөздөрдү башкаргычы Ñакталган ÑÑ‹Ñ€Ñөздөрүңүздү текшерүүнү Ñунуштайт.</translation>
@@ -1414,6 +1423,7 @@
<translation id="483241715238664915">ЭÑкертүүлөрдү күйгүзүү</translation>
<translation id="4834250788637067901">Google Pay'ди колдонгон төлөм ыкмалары, Ñунуштар жана даректер</translation>
<translation id="4838327282952368871">КыÑлкеч</translation>
+<translation id="4839087176073128681">Эмки жолу тез төлөп, картаңыздын коопÑуздугун Google'дун жаңы технологиÑлары менен коргоңуз.</translation>
<translation id="4840250757394056958">Chrome'до көрүлгөн вебÑайттарды көрүү</translation>
<translation id="484462545196658690">Ðвто</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> Ñайтынан жана башкалардан арзандатуу алыңыз</translation>
@@ -1476,6 +1486,7 @@
<translation id="5011561501798487822">Ðныкталган тил</translation>
<translation id="5015510746216210676">Компьютердин аталышы:</translation>
<translation id="5017554619425969104">Сиз көчүргөн текÑÑ‚</translation>
+<translation id="5017828934289857214">Мага кийинчерÑÑк ÑÑкертилÑин</translation>
<translation id="5018422839182700155">Бул баракча ачылбай жатат</translation>
<translation id="5019198164206649151">Фондук Ñактагыч начар абалда</translation>
<translation id="5020776957610079374">Дүйнөлүк музыка</translation>
@@ -1495,6 +1506,7 @@
<translation id="5051305769747448211">Түз берилүүчү комедиÑ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Бул файлды Nearby Share функциÑÑÑ‹ аркылуу жөнөтүү үчүн түзмөгүңүздө орун (<ph name="DISK_SPACE_SIZE" />) бошотуңуз}other{Бул файлдарды Nearby Share функциÑÑÑ‹ аркылуу жөнөтүү үчүн түзмөгүңүздө орун (<ph name="DISK_SPACE_SIZE" />) бошотуңуз}}</translation>
<translation id="5056549851600133418">Сизге ылайыктуу макалалар</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> вебÑайттарында Windows Hello функциÑÑын колдонууну тандаганÑыз. Бул провайдер төлөм ыкмаңызды Ñактап койгон болушу мүмкүн. Ðны <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" /> ÑÐ¼ÐµÑ Ð±ÐµÐ»Ðµ?</translation>
<translation id="5066056036849835175">БаÑып чыгаруу таржымалы</translation>
<translation id="5068234115460527047">Хедж-фонддор</translation>
@@ -1508,10 +1520,8 @@
<translation id="5087286274860437796">Ðзыркы учурда Ñервердин таÑтыктамаÑÑ‹ жарактуу ÑмеÑ.</translation>
<translation id="5087580092889165836">Карточка кошуу</translation>
<translation id="5088142053160410913">Операторго билдирүү</translation>
-<translation id="5089810972385038852">Штат</translation>
<translation id="5093232627742069661">Z формаÑында бүктөө</translation>
<translation id="5094747076828555589">Бул Ñервер <ph name="DOMAIN" /> Ñкендигин далилдей алган жок; Ñебеби Chromium коопÑуздук таÑтыктамаÑына ишенбейт. Мындай көйгөй туура ÑÐ¼ÐµÑ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¼ÐµÐ½ÐµÐ½ шартталышы мүмкүн же туташууңузга чабуулчу кийлигишип жатат.</translation>
-<translation id="5095208057601539847">Чөлкөм</translation>
<translation id="5097099694988056070">ПроцеÑÑордун же оперативдик ÑÑтутумдун колдонулушу ÑÑ‹Ñктуу түзмөктүн ÑтатиÑтикаÑÑ‹</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Сайт кооптуу</translation>
@@ -1549,6 +1559,7 @@
<translation id="5171045022955879922">URL издеңиз же териңиз</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Шайман</translation>
+<translation id="5177076414499237632">Бул барактын булагы жана темаÑÑ‹ жөнүндө маалымат алыңыз</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> тилинде ÑмеÑпи? Бул ката тууралуу кабарлаңыз</translation>
<translation id="518639307526414276">Үй жаныбарлары үчүн тамак-аш жана кам көрүү</translation>
<translation id="5190835502935405962">КыÑтармалар тилкеÑи</translation>
@@ -1709,6 +1720,7 @@
<translation id="5624120631404540903">СырÑөздөрдү башкаруу</translation>
<translation id="5629630648637658800">СаÑÑат жөндөөлөрү жүктөлбөй калды.</translation>
<translation id="5631439013527180824">Түзмөктү башкаргычуу белгиÑи</translation>
+<translation id="5632485077360054581">Кантип?</translation>
<translation id="5633066919399395251">Учурда <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> Ñайтындагы чабуулчулар компьютериңизге кооптуу программаларды орнотууга жана маалыматыңызды (миÑалы, Ñүрөттөр, ÑÑ‹Ñ€Ñөздөр, билдирүүлөр жана наÑÑ‹Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ñ‡ÐºÐ°Ð»Ð°Ñ€Ñ‹) уурдап же жок кылууга аракет кылышы мүмкүн. <ph name="BEGIN_LEARN_MORE_LINK" />Кеңири маалымат<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Ðдаштыруучу мазмун бөгөттөлдү.</translation>
<translation id="5633259641094592098">Культтук жана инди фильмдери</translation>
@@ -1826,6 +1838,7 @@
<translation id="5989320800837274978">Бекитилген прокÑи Ñерверлер да, .pac Ñкрипттин URL'и да көрÑөтүлгөн жок.</translation>
<translation id="5992691462791905444">Инженердик Z формаÑында бүктөө</translation>
<translation id="5995727681868049093">Маалыматты, купуÑлыкты жана коопÑуздукту Google аккаунтуңузда башкаруу</translation>
+<translation id="5997247540087773573">Жаңы Ñле колдонгон ÑÑ‹Ñ€Ñөзүңүздү кимдир бирөө билип алганы аныкталды. Ðккаунттарыңыздын коопÑуздугун коргоо үчүн Google'дун СырÑөздөрдү башкаргычы аны азыр өзгөртүп жана башка Ñакталган ÑÑ‹Ñ€Ñөздөрүңүздү текшерүүнү Ñунуштайт.</translation>
<translation id="6000758707621254961">"<ph name="SEARCH_TEXT" />" Ñурамы боюнча <ph name="RESULT_COUNT" /> жыйынтык табылды</translation>
<translation id="6006484371116297560">КлаÑÑикалык тема</translation>
<translation id="6008122969617370890">N–1 ирети</translation>
@@ -1921,7 +1934,6 @@
<translation id="627746635834430766">Кийинки жолу тезирÑÑк төлөө үчүн картаңыз менен ÑÑептешүү дарегин Google аккаунтуңузга Ñактап коюңуз.</translation>
<translation id="6279183038361895380">КурÑоруңузду көрÑÓ©Ñ‚Ò¯Ò¯ үчүн |<ph name="ACCELERATOR" />| дегенди баÑыңыз</translation>
<translation id="6280223929691119688">Бул дарекке жеткирүү мүмкүн ÑмеÑ. Башка дарек тандаңыз.</translation>
-<translation id="6282194474023008486">Почта индекÑи</translation>
<translation id="6285507000506177184">"Chrome'до жүктөлүп алынгандарды башкаруу" баÑкычы, Chrome'до жүктөлүп алынган файлдарды башкаруу үчүн Enter баÑкычын баÑыңыз</translation>
<translation id="6289939620939689042">Барактын Ñ‚Ò¯ÑÒ¯</translation>
<translation id="6290238015253830360">Сунушталган макалалар ушул жерде көрүнөт</translation>
@@ -1943,6 +1955,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> чейин орун бошотулат. Кийинки жолу киргениңизде, айрым Ñайттар жайыраак жүктөлүшү мүмкүн.</translation>
<translation id="6337534724793800597">СаÑÑаттарды аталышы боюнча чыпкалоо</translation>
<translation id="6340739886198108203">ÐдминиÑтратордун ÑаÑÑаты ÐºÑƒÐ¿ÑƒÑ Ð¼Ð°Ð·Ð¼ÑƒÐ½ көрүнүп турганда Ñкриншотторду тартууну же жаздырууну Ñунуштабайт:</translation>
+<translation id="6348220984832452017">Ðктивдүү варианттар</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> колдонмоÑун орнотуу</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Жок}=1{1 ÑÑ‹Ñ€Ñөз (<ph name="DOMAIN_LIST" /> доменде, шайкештирилген)}=2{2 ÑÑ‹Ñ€Ñөз (<ph name="DOMAIN_LIST" /> доменде, шайкештирилген)}other{# ÑÑ‹Ñ€Ñөз (<ph name="DOMAIN_LIST" /> доменде, шайкештирилген)}}</translation>
<translation id="6355392890578844978">Бул Ñерепчи компаниÑңыз же башка уюм тарабынан башкарылбайт. Бул түзмөктө аткарылган аракеттер Chromium'дан тышкары башкарылышы мүмкүн. <ph name="BEGIN_LINK" />Кеңири маалымат<ph name="END_LINK" /></translation>
@@ -1974,7 +1987,6 @@
<translation id="643051589346665201">Google ÑÑ‹Ñ€Ñөзүн өзгөртүү</translation>
<translation id="6433490469411711332">Байланыш маалыматын түзөтүү</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> Ñайты туташуудан баш тартты.</translation>
-<translation id="6438025220577812695">Өзүм өзгөртөм</translation>
<translation id="6440503408713884761">Этибарга алынган жок</translation>
<translation id="6443406338865242315">КайÑÑ‹ кеңейтүүлөрдү жана плагиндерди орнотуп алдыңыз</translation>
<translation id="6446163441502663861">Kahu (Конверт)</translation>
@@ -2104,9 +2116,9 @@
<translation id="6828866289116430505">Генетика</translation>
<translation id="6831043979455480757">Которуу</translation>
<translation id="6833752742582340615">КоопÑуз жана тезирÑÑк төлөө үчүн картаңызды жана ÑÑеп коюу маалыматын Google аккаунтуңузга Ñактап коюңуз</translation>
-<translation id="6839929833149231406">Ðймак</translation>
<translation id="6846340164947227603">Виртуалдык картанын номерин колдонуу...</translation>
<translation id="6852204201400771460">Колдонмо кайра жүктөлÑүнбү?</translation>
+<translation id="6857776781123259569">СырÑөздөрдү башкаруу…</translation>
<translation id="686485648936420384">Керектөөчү реÑурÑтары</translation>
<translation id="6865412394715372076">Бул картаны учурда ыраÑтоо мүмкүн ÑмеÑ</translation>
<translation id="6869334554832814367">Жеке наÑÑ‹Ñлар</translation>
@@ -2155,7 +2167,6 @@
<translation id="6965978654500191972">Түзмөк</translation>
<translation id="696703987787944103">Кабыл алуучу</translation>
<translation id="6968269510885595029">КоопÑуздук ачкычыңызды колдонуңуз</translation>
-<translation id="6970216967273061347">Район</translation>
<translation id="6971439137020188025">Slides кызматында жаңы Google презентациÑÑын тез түзүү</translation>
<translation id="6972629891077993081">HID түзмөктөрү</translation>
<translation id="6973656660372572881">Туруктуу прокÑи Ñерверлери да, .pac Ñкрипт URL да белгиленген.</translation>
@@ -2194,7 +2205,6 @@
<translation id="7081308185095828845">Бул Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ñ‚Ò¯Ð·Ð¼Ó©Ð³Ò¯Ò£Ò¯Ð·Ð´Ó© иштебейт</translation>
<translation id="7083258188081898530">9-түпкүч</translation>
<translation id="7086090958708083563">Колдонуучу тарабынан жүктөп берүү Ñуралды</translation>
-<translation id="7087282848513945231">Округ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome жөндөөлөрүнөн урукÑаттарды жана Ñакталган маалыматтарды башкаруу үчүн Tab, андан Ñоң Enter баÑкычын баÑыңыз</translation>
<translation id="7096937462164235847">Бул Ñайттын аныктыгы ыраÑталган жок.</translation>
<translation id="7101893872976785596">Үрөй учурган таÑмалар</translation>
@@ -2213,10 +2223,10 @@
<ph name="LIST_ITEM" />Формаларга киргизилген маалымат<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Бул дарекке жөнөтүү мүмкүн ÑмеÑ. Башка дарек тандаңыз.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ÐдминиÑтратор бул маалыматты көчүрүүгө тыюу Ñалды.</translation>
<translation id="7135130955892390533">Ðбалын көрÑÓ©Ñ‚Ò¯Ò¯</translation>
<translation id="7138472120740807366">Жеткирүү ыкмаÑÑ‹</translation>
-<translation id="7139724024395191329">Эмират</translation>
<translation id="7139892792842608322">Ðегизги түпкүч</translation>
<translation id="714064300541049402">2-тараптын X Ñүрөтүн жылдыруу</translation>
<translation id="7152423860607593928">Number-14 (Конверт)</translation>
@@ -2433,6 +2443,7 @@
<translation id="7669271284792375604">Бул Ñайттагы бүлдүргүчтөр Ñизди алдап, Ñерептөө тажрыйбаңызга зыÑн келтирүүчү программаларды орнотууга аракет кылышы мүмкүн (миÑалы, башкы бетиңизди өзгөртүп же кирген Ñайттарыңызда кошумча жарнамаларды көрÑÓ©Ñ‚Ò¯Ò¯ менен).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ÐšÑƒÐ¿ÑƒÑ Ð¼Ð°Ð°Ð»Ñ‹Ð¼Ð°Ñ‚ колдонулган аракеттер (киргенден бери 1 аракет). <ph name="BEGIN_LINK" />Кеңири маалымат<ph name="END_LINK" />}other{ÐšÑƒÐ¿ÑƒÑ Ð¼Ð°Ð°Ð»Ñ‹Ð¼Ð°Ñ‚ колдонулган аракеттер (киргенден бери # аракет). <ph name="BEGIN_LINK" />Кеңири маалымат<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">6-Ñлектрондук каттар кутуÑу</translation>
+<translation id="7675325315208090829">Төлөм ыкмаларын башкаруу…</translation>
<translation id="7676643023259824263">Ðлмашуу буфериндеги текÑтти издөө, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Chrome жөндөөлөрүндө көрүлгөн вебÑайттарды көрүү жана башкаруу</translation>
<translation id="7679947978757153706">БейÑбол</translation>
@@ -2475,7 +2486,6 @@
<translation id="7766518757692125295">Юбка</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Ошол Ñле иретте алдыңкы бетин өйдө каратып</translation>
-<translation id="777702478322588152">Префектура</translation>
<translation id="7791011319128895129">Чыга Ñлек</translation>
<translation id="7791196057686275387">Таңуу</translation>
<translation id="7791543448312431591">Кошуу</translation>
@@ -2566,12 +2576,12 @@
<translation id="8055534648776115597">КеÑиптик жана үзгүлтүкÑүз билим берүү</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" программаÑÑ‹ туура конфигурациÑланган ÑмеÑ. Ðдатта, мындай көйгөй "<ph name="SOFTWARE_NAME" />" программаÑын чыгарып Ñалуу менен чечилет. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Тамак-аш өндүрүү</translation>
-<translation id="8066955247577885446">КечиреÑиз, бир жерден ката кетти.</translation>
<translation id="8067872629359326442">СырÑөзүңүздү жаңы Ñле адаштыруучу Ñайтта киргиздиңиз. Chromium жардам берет. СырÑөзүңүздү өзгөртүү жана аккаунтуңуздун коопÑуздугу коркунучта Ñкенин Google'га кабарлоо үчүн "Ðккаунттун коопÑуздугун коргоо" баÑкычын баÑыңыз.</translation>
<translation id="8070439594494267500">Колдонмонун ÑүрөтчөÑÒ¯</translation>
<translation id="8074253406171541171">10x13 (Конверт)</translation>
<translation id="8075736640322370409">Жаңы Google таблицаÑын тез түзүү</translation>
<translation id="8075898834294118863">Сайттын жөндөөлөрүн башкаруу</translation>
+<translation id="8076492880354921740">Өтмөктөр</translation>
<translation id="8078141288243656252">Документ бурулуп турганда, ÐÐ½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ð¸Ð½ колдонууга болбойт</translation>
<translation id="8079031581361219619">Сайт кайрадан жүктөлÑүнбү?</translation>
<translation id="8081087320434522107">Седандар</translation>
@@ -2694,6 +2704,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Жөндөөлөр</translation>
+<translation id="8428634594422941299">Түшүнүктүү</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome жөндөөлөрүнөн cookie файлдарынын параметрлерин башкаруу үчүн Tab, андан Ñоң Enter баÑкычын баÑыңыз</translation>
<translation id="8433057134996913067">Ушуну менен көпчүлүк вебÑайттардын каттоо ÑÑебинен чыгарылаÑыз.</translation>
<translation id="8434840396568290395">Үй жаныбарлары</translation>
@@ -2791,6 +2802,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> - бул <ph name="ORIGIN" /> үчүн кодуңуз</translation>
<translation id="874918643257405732">Бул өтмөктү кыÑтарып коюңуз</translation>
<translation id="8751426954251315517">Бир аздан кийин кайталап көрүңүз</translation>
+<translation id="8757526089434340176">Google Pay Ñунушу жеткиликтүү</translation>
<translation id="8758885506338294482">Ðтаандаш видео оюндары</translation>
<translation id="8759274551635299824">Бул карточканын мөөнөтү бүткөн</translation>
<translation id="87601671197631245">Бул Ñайт ÑÑкирген коопÑуздук конфигурациÑÑын колдонуп жатат. Маалыматыңыз (миÑалы, ÑÑ‹Ñ€Ñөздөр, билдирүүлөр же наÑÑ‹Ñ ÐºÐ°Ñ€Ñ‚Ð°Ð»Ð°Ñ€Ñ‹) ушул Ñайтка жөнөтүлгөндө ачык көрүнүп калышы мүмкүн.</translation>
@@ -2798,6 +2810,7 @@
<translation id="8763927697961133303">USB түзмөгү</translation>
<translation id="8763986294015493060">Учурда ачылып турган бардык Жашыруун терезелерди жабыңыз</translation>
<translation id="8766943070169463815">КоопÑуз төлөм үчүн ÑÑептик дайындардын аныктыгын текшерүү барагы ачылды</translation>
+<translation id="8767765348545497220">Жардам булутчаÑын жабуу</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Мотоциклдер</translation>
<translation id="8790007591277257123">Жок кылууну &amp;кайталоо</translation>
@@ -2810,6 +2823,7 @@
<translation id="8806285662264631610">Ваннага жана денеге арналган буюмдар</translation>
<translation id="8807160976559152894">ÐÑ€ бир барактан кийин кыркуу</translation>
<translation id="8808828119384186784">Chrome'дун параметрлери</translation>
+<translation id="8813277370772331957">КийинчерÑÑк ÑÑкертүү</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Chrome жөндөөлөрүңүздөн Chrome'ду жаңыртуу үчүн, Tab, андан Ñоң Enter баÑкычын баÑыңыз</translation>
<translation id="8820817407110198400">КыÑтармалар</translation>
<translation id="882338992931677877">Кол менен киргизилген түпкүч</translation>
@@ -2866,7 +2880,7 @@
<translation id="8987245424886630962"><ph name="VIEW_CHROME_HISTORY_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome'до көрүлгөн вебÑайттарды көрүү үчүн Tab, андан кийин Enter баÑкычын баÑыңыз</translation>
<translation id="8987927404178983737">Ðй</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
-<translation id="8992061558343343009">Тутумдун верÑиÑÑын издеп жатаÑызбы? Төмөнкүгө өтүңүз:</translation>
+<translation id="8992061558343343009">СиÑтеманын верÑиÑÑын издеп жатаÑызбы? Төмөнкүгө өтүңүз:</translation>
<translation id="899688752321268742"><ph name="URL" /> түзмөктү активдүү колдонуп жаткан учур тууралуу билгиÑи келип жатат</translation>
<translation id="8996941253935762404">Ðлдыдагы Ñайтта зыÑнкеч программалар камтылган</translation>
<translation id="8997023839087525404">Сервер берген таÑтыктама "ТаÑтыктаманын тунуктук ÑаÑÑаты" аркылуу ачыкка чыгарылган ÑмеÑ. Бул таÑтыктамалар ишенимдүү Ñкендигине жана чабуулчулардан коргой алаарына кепилдик берүү үчүн талап кылынат.</translation>
@@ -2989,6 +3003,7 @@
<translation id="988159990683914416">Иштеп чыгуучунун курамаÑÑ‹</translation>
<translation id="989988560359834682">Даректи өзгөртүү</translation>
<translation id="991413375315957741">кыймыл же жарык ÑенÑорлору</translation>
+<translation id="992110854164447044">Шылуундуктан коргоого жардам берүү үчүн виртуалдык карта чыныгы картаңыздын маалыматын жашырат. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Кызгылтым</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" программаÑÑ‹ компьютериңизде же тармагыңызда туура орнотулган ÑмеÑ:
diff --git a/chromium/components/strings/components_strings_lo.xtb b/chromium/components/strings/components_strings_lo.xtb
index be33575fced..d0641fbb78c 100644
--- a/chromium/components/strings/components_strings_lo.xtb
+++ b/chromium/components/strings/components_strings_lo.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ທຶນàºàº²àº™àºªàº¶àºàºªàº² à»àº¥àº° àºàº²àº™àºŠà»ˆàº§àºà»€àº«àº¼àº·àº­àº—າງàºàº²àº™à»€àº‡àº´àº™</translation>
<translation id="1048785276086539861">ເມື່ອທ່ານà»àºà»‰à»„ຂຄຳອະທິບາàºàº„ວາມເຫັນດ ເອàºàº°àºªàº²àº™àº™àºµà»‰àºˆàº°àºàº±àºšà»„ປເປັນມຸມມອງà»àºšàºšà»œà»‰àº²àº”ຽວ</translation>
<translation id="1050038467049342496">ປິດà»àº­àº±àºšàº­àº·à»ˆàº™</translation>
+<translation id="1053959602163383901">ທ່ານເລືອàºàº—ີ່ຈະຢັ້ງຢືນດ້ວàºàº­àº¸àº›àº°àºàº­àº™àº•àº»àº§àºžàº´àºªàº¹àº”ຢືນຢັນຢູ່ເວັບໄຊທີ່ໃຊ້ <ph name="PROVIDER_ORIGIN" />. ຜູ້ໃຫ້ບà»àº¥àº´àºàº²àº™àº™àºµà»‰àº­àº²àº”ມີàºàº²àº™àºˆàº±àº”ເàºàº±àºšàº‚à»à»‰àº¡àº¹àº™àºà»ˆàº½àº§àºàº±àºšàº§àº´àº—ີàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™àº‚ອງທ່ານໄວ້, ເຊິ່ງທ່ານສາມາດ <ph name="LINK_TEXT" /> ໄດ້.</translation>
<translation id="1055184225775184556">ປ່ຽນ​ຄືນຄà»àº²àºªàº±à»ˆàº‡à»€àºžàºµà»ˆàº¡</translation>
<translation id="1056663316309890343">ຊອບà»àº§àº®àº¹àºšàºžàº²àºš</translation>
<translation id="1056898198331236512">ຄà»àº²à»€àº•àº·àº­àº™</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">ວິທີàºàº²àº™àº®àº±àºšà»€àº„ື່ອງ</translation>
<translation id="1281476433249504884">ສະà»àº•àº±àºà»€àºàºµà»‰ 1</translation>
<translation id="1285320974508926690">ຢ່າà»àº›à»€àº§àº±àºšâ€‹à»„ຊ​ທ໌ນີ້</translation>
+<translation id="1288548991597756084">ບັນທຶàºàºšàº±àº”ໄວ້ຢ່າງປອດໄພ</translation>
<translation id="1292571435393770077">ຖາດ 16</translation>
<translation id="1292701964462482250">"ຊອບà»àº§àº¢àº¹à»ˆà»ƒàº™àº„ອມພິວເຕີຂອງທ່ານàºàº³àº¥àº±àº‡àº›à»‰àº­àº‡àºàº±àº™àºšà»à»ˆà»ƒàº«à»‰ Chrome ເຊື່ອມຕà»à»ˆàºàº±àºšà»€àº§àº±àºšà»„ຊໄດ້ຢ່າງປອດໄພ" (ຄອມພິວເຕີ Windows ເທົ່ານັ້ນ)</translation>
<translation id="1294154142200295408">àºàº²àº™àº›à»ˆàº½àº™à»àº›àº‡à»àº–ວຄຳສັ່ງ</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;ເພື່ອà»àºà»‰à»„ຂຂà»à»‰àºœàº´àº”ພາດ, àºàº°àº¥àº¸àº™àº²àº„ລິຠ&lt;strong&gt;ເຊື່ອມຕà»à»ˆ&lt;/strong&gt; ຢູ່ໃນໜ້າທີ່ທ່ານàºàº³àº¥àº±àº‡àºžàº°àºàº²àºàº²àº¡à»€àº›àºµàº”.&lt;/p&gt;</translation>
<translation id="1507780850870535225">àºàº²àº™àº­àº­àºà»àºšàºšàºžàº¹àº¡àº¡àº´àº—ັດ</translation>
<translation id="1513706915089223971">ລາàºàºŠàº·à»ˆàº¥àº²àºàºàº²àº™àº›àº°àº«àº§àº±àº”</translation>
+<translation id="1516097932025103760">ມັນຈະຖືàºà»€àº‚ົ້າລະຫັດ, ບັນທຶàºà»„ວ້ຢ່າງປອດໄພ à»àº¥àº° ຈະບà»à»ˆàº¡àºµàºàº²àº™àºˆàº±àº”ເàºàº±àºš CVC ໄວ້ເດັດຂາດ.</translation>
<translation id="1517433312004943670">ຈຳເປັນຕ້ອງມີເບີໂທລະສັບ</translation>
<translation id="1519264250979466059">​ວັນ​ທີສ້າງ</translation>
<translation id="1521159554480556801">ສິນລະປະສິ່ງທ໠à»àº¥àº° ເສັ້ນໃàº</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ບັນທຶຠà»àº¥àº° ປະàºàº­àºšàº‚à»à»‰àº¡àº¹àº™àº§àº´àº—ີàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™</translation>
<translation id="1663943134801823270">ບັດ à»àº¥àº° ທີ່ຢູ່ມາຈາຠChrome. ທ່ານສາມາດຈັດàºàº²àº™àºžàº§àºàº¡àº±àº™à»„ດ້ໃນ <ph name="BEGIN_LINK" />àºàº²àº™àº•àº±à»‰àº‡àº„່າ<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">ລະບົບຈະà»àº›à»œà»‰àº² <ph name="SOURCE_LANGUAGE" /> ເປັນ <ph name="TARGET_LANGUAGE" /> à»àº•à»ˆàº•àº­àº™àº™àºµà»‰à»€àº›àº±àº™àº•àº»à»‰àº™à»„ປ.</translation>
+<translation id="1673886523110456987">ຈ່າàºà»€àº‡àº´àº™àº”້ວຠ<ph name="CARD_DETAIL" /> ເພື່ອໃຊ້ຂà»à»‰àºªàº°à»€à»œàºµ</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> ເປັນ <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ດ້ານຂອບສັ້ນàºà»ˆàº­àº™</translation>
<translation id="168693727862418163">àºàº§àº”ຮັບຮອງຄ່ານະໂàºàºšàº²àºàº™àºµà»‰àº—ຽບàºàº±àºšàº®àº¹àºšà»àºšàºšàº‚ອງມັນບà»à»ˆàºªàº³à»€àº¥àº±àº” à»àº¥àº° ຈະຖືàºàº¥àº°à»€àº¥àºµàº.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">ເຊັນàºàº§àº”ຈັບàºàº²àº™à»€àº„ື່ອນໄຫວ</translation>
<translation id="1717494416764505390">àºà»ˆàº­àº‡àºˆàº»àº”à»àº²àº 3</translation>
<translation id="1718029547804390981">ເອàºàº°àºªàº²àº™à»ƒàº«àºà»ˆà»€àºàºµàº™àº—ີ່ຈະຂຽນຄຳອະທິບາàºàº„ວາມເຫັນໄດ້</translation>
+<translation id="1720941539803966190">ປິດສອນàºàº²àº™àº™àº³à»ƒàºŠà»‰</translation>
<translation id="1721424275792716183">* ຊ່ອງໃ່ສ່ຂà»à»‰àº¡àº¹àº™àº—ີ່ຕ້ອງຕື່ມຂà»à»‰àº¡àº¹àº™</translation>
<translation id="1727613060316725209">ໃບຮັບຮອງຖືàºàº•à»‰àº­àº‡</translation>
<translation id="1727741090716970331">ເພີ່ມເລàºàºšàº±àº”ທີ່ຖືàºàº•à»‰àº­àº‡</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">ບາບີຄິວ à»àº¥àº° àºàº²àº™àº›àº´à»‰àº‡</translation>
<translation id="2053111141626950936">ລະບົບຈະບà»à»ˆà»àº›à»œà»‰àº²à»€àº›àº±àº™ <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ເມື່ອເປີດàºàº²àº™àº„ວບຄຸມນີ້ à»àº¥àº° ສະຖານະເປັນà»àºšàºšà»€àº›àºµàº”ໃຊ້ຢູ່, Chrome ຈະàºàº³àº™àº»àº”ວ່າàºàº¸à»ˆàº¡àº„ົນຂະໜາດໃຫàºà»ˆàºàº¸à»ˆàº¡à»ƒàº” ຫຼື "àºàº¸à»ˆàº¡àº›àº°àºŠàº²àºàº­àº™àº•àº²àº¡àº®àº¸à»ˆàº™" ໃດທີ່ຄ້າàºàºàº±àºšàºàº²àº™à»€àº„ື່ອນໄຫວàºàº²àº™àº—່ອງເວັບຫຼ້າສຸດຂອງທ່ານ. ຜູ້ລົງໂຄສະນາສາມາດເລືອàºà»‚ຄສະນາສຳລັບàºàº¸à»ˆàº¡à»„ດ້ à»àº¥àº° àºàº²àº™à»€àº„ື່ອນໄຫວàºàº²àº™àº—່ອງເວັບຂອງທ່ານຈະຖືàºàºˆàº±àº”ເàºàº±àºšà»„ວ້ເປັນສ່ວນຕົວຢູ່ອຸປະàºàº­àº™àº‚ອງທ່ານ. àºàº¸à»ˆàº¡àº‚ອງທ່ານມີàºàº²àº™àº­àº±àºšà»€àº”ດທຸàºàº¡àº·à»‰.}=1{ເມື່ອເປີດàºàº²àº™àº„ວບຄຸມນີ້ à»àº¥àº° ສະຖານະເປັນà»àºšàºšà»€àº›àºµàº”ໃຊ້ຢູ່, Chrome ຈະàºàº³àº™àº»àº”ວ່າàºàº¸à»ˆàº¡àº„ົນຂະໜາດໃຫàºà»ˆàºàº¸à»ˆàº¡à»ƒàº” ຫຼື "àºàº¸à»ˆàº¡àº›àº°àºŠàº²àºàº­àº™àº•àº²àº¡àº®àº¸à»ˆàº™" ໃດທີ່ຄ້າàºàºàº±àºšàºàº²àº™à»€àº„ື່ອນໄຫວàºàº²àº™àº—່ອງເວັບຫຼ້າສຸດຂອງທ່ານ. ຜູ້ລົງໂຄສະນາສາມາດເລືອàºà»‚ຄສະນາສຳລັບàºàº¸à»ˆàº¡à»„ດ້ à»àº¥àº° àºàº²àº™à»€àº„ື່ອນໄຫວàºàº²àº™àº—່ອງເວັບຂອງທ່ານຈະຖືàºàºˆàº±àº”ເàºàº±àºšà»„ວ້ເປັນສ່ວນຕົວຢູ່ອຸປະàºàº­àº™àº‚ອງທ່ານ. àºàº¸à»ˆàº¡àº‚ອງທ່ານມີàºàº²àº™àº­àº±àºšà»€àº”ດທຸàºàº¡àº·à»‰.}other{ເມື່ອເປີດàºàº²àº™àº„ວບຄຸມນີ້ à»àº¥àº° ສະຖານະເປັນà»àºšàºšà»€àº›àºµàº”ໃຊ້ຢູ່, Chrome ຈະàºàº³àº™àº»àº”ວ່າàºàº¸à»ˆàº¡àº„ົນຂະໜາດໃຫàºà»ˆàºàº¸à»ˆàº¡à»ƒàº” ຫຼື "àºàº¸à»ˆàº¡àº›àº°àºŠàº²àºàº­àº™àº•àº²àº¡àº®àº¸à»ˆàº™" ໃດທີ່ຄ້າàºàºàº±àºšàºàº²àº™à»€àº„ື່ອນໄຫວàºàº²àº™àº—່ອງເວັບຫຼ້າສຸດຂອງທ່ານ. ຜູ້ລົງໂຄສະນາສາມາດເລືອàºà»‚ຄສະນາສຳລັບàºàº¸à»ˆàº¡à»„ດ້ à»àº¥àº° àºàº²àº™à»€àº„ື່ອນໄຫວàºàº²àº™àº—່ອງເວັບຂອງທ່ານຈະຖືàºàºˆàº±àº”ເàºàº±àºšà»„ວ້ເປັນສ່ວນຕົວຢູ່ອຸປະàºàº­àº™àº‚ອງທ່ານ. àºàº¸à»ˆàº¡àº‚ອງທ່ານມີàºàº²àº™àº­àº±àºšà»€àº”ດທຸຠ{NUM_DAYS} ມື້.}}</translation>
-<translation id="2053553514270667976">ລະ​ຫັດ​ຊິບ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 ຄຳ​à»àº™àº°àº™àº³}other{# ຄຳ​à»àº™àº°àº™àº³}}</translation>
+<translation id="2066915425250589881">ຮ້ອງຂà»à»ƒàº«à»‰àº–ືàºàº¥àº¶àºšàº­àº­àº</translation>
<translation id="2068528718802935086">à»àº­àº™à»‰àº­àº à»àº¥àº° ເດັàºàº™à»‰àº­àºàº®àº½àº™àºà»ˆàº²àº‡</translation>
<translation id="2071156619270205202">ບັດນີ້ບà»à»ˆàº¡àºµàºªàº´àº”ໃຊ້à»àº²àºà»€àº¥àºàºšàº±àº”ສະເà»àº·àº­àº™.</translation>
<translation id="2071692954027939183">àºàº²àº™à»àºˆà»‰àº‡à»€àº•àº·àº­àº™àº–ືàºàºšàº¥àº±àº­àºà»„ວ້ອັດຕະໂນມັດເນື່ອງຈາàºàº—່ານບà»à»ˆàº­àº°àº™àº¸àºàº²àº”ພວàºàº¡àº±àº™</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">ຈັດàºàº²àº™àº›àº¸à»ˆàº¡àºŠàº´à»‰àº‡àº‚à»à»‰àº¡àº¹àº™, àºàº»àº” Enter ເພື່ອຈັດàºàº²àº™àº§à»ˆàº²àº‚à»à»‰àº¡àº¹àº™à»ƒàº”ທີ່ທ່ານຈະຊິ້ງຂà»à»‰àº¡àº¹àº™à»ƒàº™àºàº²àº™àº•àº±à»‰àº‡àº„່າ Chrome</translation>
<translation id="2091887806945687916">ສຽງ</translation>
<translation id="2094505752054353250">ໂດເມນບà»à»ˆà»€àº‚ົ້າຄູ່àºàº±àº™</translation>
-<translation id="2096368010154057602">ພະà»àº™àº</translation>
<translation id="2099652385553570808">ໜີບàºàº°àº«àº¼àº±àºšà»€àºŸàºµà»€àºšàº·à»‰àº­àº‡àºŠà»‰àº²àºàºªàº²àº¡à»€àº—ື່ອ</translation>
<translation id="2101225219012730419">ລຸ້ນ:</translation>
<translation id="2102134110707549001">à»àº™àº°àº™àº³àº¥àº°àº«àº±àº”ຜ່ານທີ່ເດົາàºàº²àºâ€¦</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">​ອາ​ເມ​ລິ​àºàº±àº™â€‹àºŸàº¸àº”ບອນ</translation>
<translation id="2187317261103489799">àºàº§àº”ຫາ (ຄ່າເລີ່ມຕົ້ນ)</translation>
<translation id="2188375229972301266">ເຈາະຮູຢູ່ລຸ່ມສຸດຫຼາàºàº®àº¹</translation>
-<translation id="2188852899391513400">ພົບລະຫັດຜ່ານທີ່ທ່ານຫາàºà»à»ƒàºŠà»‰àº™àº±à»‰àº™à»ƒàº™àºàº²àº™àº®àº»à»ˆàº§à»„ຫຼຂà»à»‰àº¡àº¹àº™. ເພື່ອຮັàºàºªàº²àº„ວາມປອດໄພໃຫ້ບັນຊີຂອງທ່ານ, ຕົວຈັດàºàº²àº™àº¥àº°àº«àº±àº”ຜ່ານ Google à»àº™àº°àº™àº³à»ƒàº«à»‰àº›à»ˆàº½àº™àº¡àº±àº™àº”ຽວນີ້à»àº¥à»‰àº§àºˆàº²àºàº™àº±à»‰àº™àºàº§àº”ສອບລະຫັດຜ່ານທີ່ບັນທຶàºà»„ວ້ຂອງທ່ານ.</translation>
<translation id="219906046732893612">àºàº²àº™àº›àº±àºšàº›àº¸àº‡à»€àº®àº·àº­àº™</translation>
<translation id="2202020181578195191">ປ້ອນປີà»àº»àº”ອາàºàº¸àº—ີ່ຖືàºàº•à»‰àº­àº‡</translation>
<translation id="22081806969704220">ຖາດ 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">àºàº²àº™à»àºà»‰à»„ຂໄຟລ໌</translation>
<translation id="2215963164070968490">à»àº²</translation>
<translation id="2218879909401188352">ຜູ້ໂຈມຕີເຊິ່ງໃນປັດຈຸບັນຢູ່ໃນ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ສາມາດຕິດຕັ້ງà»àº­àº±àºšàº­àº±àº™àº•àº°àº¥àº²àºàº—ີ່ເຮັດໃຫ້ອຸປະàºàº­àº™àº‚ອງທ່ານເສàºàº«àº²àºà»„ດ້, ເພີ່ມàºàº²àº™àº„ິດຄ່າທີ່ເຊື່ອງໄວ້ໃສ່ໃບບິນມືຖືຂອງທ່ານ ຫຼື ລັàºà»€àº­àº»àº²àº‚à»à»‰àº¡àº¹àº™àºªà»ˆàº§àº™àº‚ອງທ່ານ. <ph name="BEGIN_LEARN_MORE_LINK" />ສຶàºàºªàº²à»€àºžàºµà»ˆàº¡à»€àº•àºµàº¡<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ຣີສະຕາດàºàº²àº™àºªàº­àº™àºàº²àº™àº™àº³à»ƒàºŠà»‰</translation>
+<translation id="2219735899272417925">ຕ້ອງຣີເຊັດອຸປະàºàº­àº™</translation>
<translation id="2224337661447660594">ບà»à»ˆàº¡àºµàº­àº´àº™à»€àº•àºµà»€àº™àº±àº”</translation>
<translation id="2230458221926704099">à»àºà»‰à»„ຂàºàº²àº™à»€àºŠàº·à»ˆàº­àº¡àº•à»à»ˆàº‚ອງທ່ານ ໂດàºà»ƒàºŠà»‰ <ph name="BEGIN_LINK" />à»àº­àº±àºšàºàº§àº”ວິເຄາະ<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">ສົ່ງດຽວນີ້</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">ບà»à»ˆàº­àº°àº™àº¸àºàº²àº” (ຄ່າເລີ່ມຕົ້ນ)</translation>
<translation id="2512413427717747692">ປຸ່ມຕັ້ງ Chrome ເປັນໂປຣà»àºàº£àº¡àº—່ອງເວັບເລີ່ມຕົ້ນ, àºàº»àº” Enter ເພື່ອຕັ້ງ Chrome ເປັນໂປຣà»àºàº£àº¡àº—່ອງເວັບເລີ່ມຕົ້ນຂອງລະບົບໃນàºàº²àº™àº•àº±à»‰àº‡àº„່າ iOS</translation>
<translation id="2515629240566999685">àºàº³àº¥àº±àº‡àºàº§àº”ເບິ່ງສັນàºàº²àº™à»ƒàº™àºžàº·à»‰àº™àº—ີ່ຂອງທ່ານ</translation>
+<translation id="2515761554693942801">ທ່ານເລືອàºàº—ີ່ຈະຢັ້ງຢືນດ້ວຠTouch ID ຢູ່ເວັບໄຊທີ່ໃຊ້ <ph name="PROVIDER_ORIGIN" />. ຜູ້ໃຫ້ບà»àº¥àº´àºàº²àº™àº™àºµà»‰àº­àº²àº”ມີàºàº²àº™àºˆàº±àº”ເàºàº±àºšàº‚à»à»‰àº¡àº¹àº™àºà»ˆàº½àº§àºàº±àºšàº§àº´àº—ີàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™àº‚ອງທ່ານໄວ້, ເຊິ່ງທ່ານສາມາດ <ph name="LINK_TEXT" /> ໄດ້.</translation>
<translation id="2521385132275182522">ໜີບàºàº°àº«àº¼àº±àºšà»€àºŸàºµàº¢àº¹à»ˆàº¥àº¸à»ˆàº¡àºªàº¸àº”ເບື້ອງຂວາ</translation>
<translation id="2521736961081452453">ສ້າງà»àºšàºšàºŸàº­àº¡</translation>
<translation id="2523886232349826891">ບັນທຶàºà»„ວ້ໃນອຸປະàºàº­àº™àº™àºµà»‰à»€àº—ົ່ານັ້ນ</translation>
<translation id="2524461107774643265">ເພີ່ມຂà»à»‰àº¡àº¹àº™à»€àºžàºµà»ˆàº¡à»€àº•àºµàº¡</translation>
<translation id="2529899080962247600">ຊ່ອງຂà»à»‰àº¡àº¹àº™àº™àºµà»‰àºšà»à»ˆàº„ວນມີຫຼາàºàºàº§à»ˆàº² <ph name="MAX_ITEMS_LIMIT" /> ລາàºàºàº²àº™. ລາàºàºàº²àº™à»€àºžàºµà»ˆàº¡à»€àº•àºµàº¡àº—ັງà»àº»àº”ຈະຖືàºàº¥àº°à»€àº§àº±à»‰àº™.</translation>
+<translation id="253493526287553278">ເບິ່ງລາàºàº¥àº°àº­àº½àº”ລະຫັດໂປຣໂມຊັນ</translation>
<translation id="2535585790302968248">ເປີດà»àº–ບທີ່ບà»à»ˆà»€àº›àºµàº”ເຜີàºàº•àº»àº§àº•àº»àº™à»ƒà»à»ˆà»€àºžàº·à»ˆàº­àº—່ອງເວັບà»àºšàºšàºªà»ˆàº§àº™àº•àº»àº§</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{à»àº¥àº° ອີຠ1 ລາàºàºàº²àº™}other{à»àº¥àº° ອີຠ# ລາàºàºàº²àº™}}</translation>
<translation id="2536110899380797252">ເພີ່ມທີ່ຢູ່</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">ຮູບຖ່າຠà»àº¥àº° ຮູບສິນລະປະດິຈິຕອນ</translation>
<translation id="2601150049980261779">ໜັງໂຣà»àº¡àº™àº•àº´àº</translation>
<translation id="2604589665489080024">ເພງປັອບ</translation>
-<translation id="2609632851001447353">àºàº²àº™â€‹àº›à»ˆàº½àº™â€‹à»àº›àº‡</translation>
<translation id="2610561535971892504">ຄລິàºà»€àºžàº·à»ˆàº­àºªàº³à»€àº™àº»àº²</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />ຈະບà»à»ˆàºšàº±àº™àº—ຶàº<ph name="END_EMPHASIS" /> ຂà»à»‰àº¡àº¹àº™àº•à»à»ˆà»„ປນີ້:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">ວັນເàºàºµàº” à»àº¥àº° ວັນຕັ້ງຊື່</translation>
<translation id="2677748264148917807">ອອàºâ€‹à»„ປ</translation>
+<translation id="2679714844901977852">ບັນທຶàºàºšàº±àº” à»àº¥àº° ຂà»à»‰àº¡àº¹àº™à»ƒàºšàºšàº´àº™àº‚ອງທ່ານໄປໃສ່ບັນຊີ Google <ph name="USER_EMAIL" /> ຂອງທ່ານສຳລັບàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™àº—ີ່ປອດໄພ à»àº¥àº° ໄວຂຶ້ນ</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">ປັບໃຫ້ພà»àº”ີ</translation>
<translation id="2688969097326701645">ຕົàºàº¥àº»àº‡, ສືບຕà»à»ˆ</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">ຜູ້ໃຫ້ບà»àº¥àº´àºàº²àº™àº­àº´àº™à»€àº•àºµà»€àº™àº±àº” (ISP)</translation>
<translation id="2856444702002559011">ຜູ້ໂຈມຕີອາດຈະàºàº³àº¥àº±àº‡àºžàº°àºàº²àºàº²àº¡àº¥àº±àºà»€àº­àº»àº²àº‚à»à»‰àº¡àº¹àº™àº‚ອງທ່ານຈາຠ<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (ຕົວຢ່າງ: ລະຫັດຜ່ານ, ຂà»à»‰àº„ວາມ ຫຼື ບັດເຄຣດິດ). <ph name="BEGIN_LEARN_MORE_LINK" />ສຶàºàºªàº²à»€àºžàºµà»ˆàº¡à»€àº•àºµàº¡<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ເວັບໄຊນີ້ສະà»àº”ງໂຄສະນາທີ່ລົບàºàº§àº™ ຫຼື ຫຼອàºàº¥àº§àº‡.</translation>
-<translation id="286512204874376891">ບັດສະເà»àº·àº­àº™àºˆàº°àº›àº­àº¡à»àº›àº‡àºšàº±àº”à»àº—້ຂອງທ່ານເພື່ອຊ່ວàºàº›àº»àºàº›à»‰àº­àº‡àº—່ານຈາàºàº„ວາມສ່ຽງໃນàºàº²àº™àºªà»à»‰à»‚àºàº‡. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ເປັນມິດ</translation>
<translation id="28761159517501904">ໜັງ</translation>
<translation id="2876489322757410363">àºàº³àº¥àº±àº‡àº­àº­àºàºˆàº²àºà»‚à»àº”ບà»à»ˆà»€àº›àºµàº”ເຜີàºàº•àº»àº§àº•àº»àº™à»€àºžàº·à»ˆàº­àºˆà»ˆàº²àºàºœà»ˆàº²àº™à»àº­àº±àºšàºžàº¥àº´à»€àº„ຊັນພາàºàº™àº­àº ສືບຕà»à»ˆàºšà»?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ດິສ</translation>
<translation id="3162559335345991374">Wi-Fi ທີ່​ທ່ານ​àºàº³â€‹àº¥àº±àº‡â€‹à»ƒàºŠà»‰â€‹àº­àº²àº”​ຈະ​ຕ້ອງ​àºàº²àº™â€‹à»ƒàº«à»‰â€‹àº—່ານ​ເຂົ້າ​ເບິ່ງ​ໜ້າ​ເຂົ້າ​ສູ່ລະ​ບົບ​ຂອງ​ມັນ.</translation>
<translation id="3169472444629675720">ຄົ້ນຫາ</translation>
-<translation id="3174168572213147020">ເàºàº²àº°</translation>
<translation id="3176929007561373547">àºàº§àº”ເບິ່ງàºàº²àº™àº•àº±à»‰àº‡àº„່າພຣັອàºàºŠàºµàº‚ອງທ່ານ ຫຼືຕິດຕà»à»ˆàº«àº²àºœàº¹à»‰à»€àºšàº´à»ˆàº‡à»àºàº‡àº¥àº°àºšàº»àºšà»€àº„ືອຂ່າàºàº‚ອງທ່ານເພື່ອ
ຮັບປະàºàº±àº™àº§à»ˆàº²àºžàº£àº±àº­àºàºŠàºµà»€àºŠàºµàºšà»€àº§àºµà»€àº®àº±àº”ວຽàºàº¢àº¹à»ˆ. ຖ້າທ່ານບà»à»ˆàº„ິດວ່າທ່ານຄວນ
ຈະໃຊ້ພຣັອàºàºŠàºµà»€àºŠàºµàºšà»€àº§àºµ:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">ໂມງ​ຜິດ​ພາດ</translation>
<translation id="3369459162151165748">ອາໄຫລ່ລົດ à»àº¥àº° ອຸປະàºàº­àº™à»€àºªàºµàº¡</translation>
<translation id="3371064404604898522">ຕັ້ງ Chrome ເປັນໂປຣà»àºàº£àº¡àº—່ອງເວັບເລີ່ມຕົ້ນ</translation>
-<translation id="3371076217486966826"><ph name="URL" /> ຕ້ອງàºàº²àº™:
- • ສ້າງà»àºœàº™àº—ີ່ 3D ຂອງສິ່ງອ້ອມຂ້າງທ່ານ à»àº¥àº° ຕິດຕາມຕຳà»à»œà»ˆàº‡àºà»‰àº­àº‡
- • ໃຊ້àºà»‰àº­àº‡àº‚ອງທ່ານ</translation>
<translation id="337363190475750230">ຖອນàºàº²àº™àºˆàº±àº”ໃຫ້à»àº¥à»‰àº§</translation>
<translation id="3375754925484257129">ເປີດໃຊ້àºàº²àº™àºàº§àº”ສອບຄວາມປອດໄພ Chrome</translation>
<translation id="3377144306166885718">ເຊີບເວີໃຊ້ TLS ເວີຊັນເàºàº»à»ˆàº².</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">ທີ່ຢູ່àºàº²àº™àºˆàº±àº”ສົ່ງ</translation>
<translation id="3402261774528610252">àºàº²àº™à»€àºŠàº·à»ˆàº­àº¡àº•à»à»ˆàº—ີ່ໃຊ້ເພື່ອໂຫຼດເວັບໄຊນີ້ໃຊ້ TLS 1.0 ຫຼື TLS 1.1, ເຊິ່ງເຊົາຮອງຮັບà»àº¥à»‰àº§ à»àº¥àº° ຈະຖືàºàº›àº´àº”àºàº²àº™àº™àº³à»ƒàºŠà»‰à»ƒàº™àº­àº°àº™àº²àº„ົດ. ເມື່ອປິດàºàº²àº™àº™àº³à»ƒàºŠà»‰à»àº¥à»‰àº§, ລະບົບຈະປ້ອງàºàº±àº™àºšà»à»ˆà»ƒàº«à»‰àºœàº¹à»‰à»ƒàºŠà»‰à»‚ຫຼດເວັບໄຊນີ້. ເຊີບເວີຄວນເປີດàºàº²àº™àº™àº³à»ƒàºŠà»‰ TLS 1.2 ຫຼື ໃà»à»ˆàºàº§à»ˆàº².</translation>
<translation id="3405664148539009465">àºà»àº²àº™àº»àº”ຟອນເອົາເອງ</translation>
+<translation id="3407789382767355356">àºàº²àº™à»€àº‚ົ້າສູ່ລະບົບຂອງພາàºàºªà»ˆàº§àº™àº—ີສາມ</translation>
<translation id="3409896703495473338">ຈັດàºàº²àº™àºàº²àº™àº•àº±à»‰àº‡àº„່າຄວາມປອດໄພ</translation>
<translation id="3414952576877147120">ຂະໜາດ:</translation>
<translation id="3417660076059365994">ໄຟລ໌ທີ່ທ່ານອັບໂຫຼດ ຫຼື à»àº™àºšàºˆàº°àº–ືàºàºªàº»à»ˆàº‡à»ƒàº«à»‰ Google Cloud ຫຼື ພາàºàºªà»ˆàº§àº™àº—ີສາມເພື່ອວິເຄາະ. ຕົວຢ່າງ: ພວàºàº¡àº±àº™àº­àº²àº”ຈະຖືàºàºªàº°à»àºàº™àº«àº²àº‚à»à»‰àº¡àº¹àº™àº¥àº°àº­àº½àº”ອ່ອນ ຫຼື ເມົາà»àº§.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">ລາàºàºŠàº·à»ˆà»œàº±àº‡ à»àº¥àº° ເວລາສາàºà»‚ຮງໜັງ</translation>
<translation id="3479552764303398839">ບà»à»ˆà»àº¡à»ˆàº™àº”ຽວນີ້</translation>
<translation id="3484560055331845446">ທ່ານສາມາດສູນເສàºàºªàº´àº”ເຂົ້າເຖິງບັນຊີ Google ຂອງທ່ານໄດ້. Chrome à»àº™àº°àº™àº³à»ƒàº«à»‰àº›à»ˆàº½àº™àº¥àº°àº«àº±àº”ຜ່ານຕອນນີ້ເລີàº. ທ່ານຈະຖືàºàº®à»‰àº­àº‡àº‚à»à»ƒàº«à»‰à»€àº‚ົ້າສູ່ລະບົບ.</translation>
+<translation id="3484861421501147767">ຄຳເຕືອນ: ມີລະຫັດໂປຣໂມຊັນທີ່ບັນທຶàºà»„ວ້</translation>
<translation id="3487845404393360112">ຖາດ 4</translation>
<translation id="3495081129428749620">ຊອàºàº«àº²à»ƒàº™à»œà»‰àº²
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">ຈັດàºàº²àº™</translation>
<translation id="3816482573645936981">ຄ່າ (à»àº—ນທີ່à»àº¥à»‰àº§)</translation>
<translation id="382518646247711829">ຖ້າທ່ານໃຊ້ເຊີບເວີພຣັອàºàºŠàºµ...</translation>
+<translation id="3826050100957962900">àºàº²àº™à»€àº‚ົ້າສູ່ລະບົບຂອງພາàºàºªà»ˆàº§àº™àº—ີສາມ</translation>
<translation id="3827112369919217609">Absolute</translation>
<translation id="3827666161959873541">ໜັງສຳລັບຄອບຄົວ</translation>
<translation id="3828924085048779000">​ບà»à»ˆâ€‹àº­àº°â€‹àº™àº¸â€‹àºàº²àº”​ໃຫ້ວະລີຜ່ານຫວ່າງເປົ່າໄດ້​.</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">ອັບ​ເດດ​ວັນ​ທີ à»àº¥àº°â€‹à»€àº§â€‹àº¥àº²</translation>
<translation id="3858860766373142691">ຊື່</translation>
<translation id="3872834068356954457">ວິທະàºàº²àºªàº²àº”</translation>
+<translation id="3875783148670536197">ສະà»àº”ງວິທີໃຫ້ຂ້ອàºà»€àºšàº´à»ˆàº‡</translation>
<translation id="3881478300875776315">ສະà»àº”ງà»àº–ວໜ້ອàºàº¥àº»àº‡</translation>
<translation id="3884278016824448484">ຕົວລະບຸອຸປະàºàº­àº™àº—ີ່ຂັດàºàº±àº™</translation>
-<translation id="3885155851504623709">ຕາà»àºªàº‡</translation>
<translation id="388632593194507180">àºàº§àº”ພົບàºàº²àº™àº•àº´àº”ຕາມເບິ່ງ</translation>
<translation id="3886948180919384617">ສະà»àº•àº±àºà»€àºàºµà»‰ 3</translation>
<translation id="3890664840433101773">ເພີ່ມອີເມວ</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> ຖືàºàºšàº¥àº±àº­àºà»„ວ້</translation>
<translation id="3978338123949022456">ໂà»àº”ຊອàºàº«àº², ພິມຄຳຊອàºàº«àº²à»àº¥à»‰àº§àºàº»àº” Enter ເພື່ອຊອàºàº«àº²àº”້ວຠ<ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">ນົàº</translation>
+<translation id="3985750352229496475">ຈັດàºàº²àº™àº—ີ່ຢູ່...</translation>
<translation id="3986705137476756801">ປິດຄຳບັນàºàº²àºàºªàº»àº”ສຳລັບຕອນນີ້</translation>
<translation id="3987940399970879459">ໜ້ອàºàºàº§à»ˆàº² 1 MB</translation>
<translation id="3990250421422698716">àºàº²àº™àºŠàº»àº”ເຊີàºàºžàº·à»‰àº™àºœàº´àº§àº—ີ່ບà»à»ˆàºªàº°à»à»à»ˆàº²àºªàº°à»€à»àºµ</translation>
+<translation id="3992684624889376114">àºà»ˆàº½àº§àºàº±àºšà»œà»‰àº²àº™àºµà»‰</translation>
<translation id="3996311196211510766">ເວັບໄຊ <ph name="ORIGIN" /> ໄດ້ຂà»à»ƒàº«à»‰àº™àº³à»ƒàºŠà»‰àº™àº°à»‚àºàºšàº²àº
ຕົ້ນທາງàºàº±àºšàº„ຳຂà»àº‚ອງມັນທັງà»àº»àº”, à»àº•à»ˆàº¥àº°àºšàº»àºšàºšà»à»ˆàºªàº²àº¡àº²àº”ນຳໃຊ້ນະໂàºàºšàº²àºàº™àºµà»‰à»„ດ້ໃນປັດຈຸບັນນີ້.</translation>
<translation id="4006465311664329701">ວິທີàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™, ຂà»à»‰àºªàº°à»€à»œàºµ à»àº¥àº° ທີ່ຢູ່ໂດàºà»ƒàºŠà»‰ Google Pay</translation>
@@ -1227,6 +1234,7 @@
<translation id="4305666528087210886">ບà»à»ˆàºªàº²àº¡àº²àº”ເຂົ້າເຖິງໄຟລ໌ຂອງທ່ານໄດ້</translation>
<translation id="4306529830550717874">ບັນທຶàºàº—ີ່ຢູ່ບà»?</translation>
<translation id="4306812610847412719">ຄລິບບອດ</translation>
+<translation id="4310070645992025887">ຊອàºàº«àº²àºšàº±àº™àº—ຶàºàº‚ອງທ່ານ</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">ບລັອຠ(ຄ່າເລີ່ມຕົ້ນ)</translation>
<translation id="4314815835985389558">ຈັດàºàº²àº™àºàº²àº™àºŠàº´à»‰àº‡àº‚à»à»‰àº¡àº¹àº™</translation>
@@ -1277,6 +1285,7 @@
<translation id="4435702339979719576">ໂພສàºàº²àº”)</translation>
<translation id="443673843213245140">àºàº²àº™à»ƒàºŠà»‰àºžàº£àº±àº­àºàºŠàºµàº›àº´àº”ໃຊ້ງານà»àº¥à»‰àº§ à»àº•à»ˆàºàº²àº™àº›àº±àºšàº•àº±à»‰àº‡àºžàº£àº±àº­àºàºŠàºµàºˆàº°à»àºˆà»‰àº‡à»„ດ້ຮັບàºàº²àº™àº¥àº°àºšàº¸.</translation>
<translation id="4441832193888514600">ຖືàºà»€àºžàºµàºà»€àºªàºµàºà»€àº™àº·à»ˆàº­àº‡àºˆàº²àºàºªàº²àº¡àº²àº”ຕັ້ງນະໂàºàºšàº²àºà»€àº›àº±àº™àº™àº°à»‚àºàºšàº²àºàºœàº¹à»‰à»ƒàºŠà»‰àº„ລາວເທົ່ານັ້ນ.</translation>
+<translation id="4442470707340296952">à»àº–ບ Chrome</translation>
<translation id="4450893287417543264">ຢ່າສະà»àº”ງອີàº</translation>
<translation id="4451135742916150903">ສາມາດຂà»à»€àºŠàº·à»ˆàº­àº¡àº•à»à»ˆàº«àº²àº­àº¸àº›àº°àºàº­àº™ HID ໄດ້</translation>
<translation id="4452328064229197696">ພົບລະຫັດຜ່ານທີ່ທ່ານຫາàºà»à»ƒàºŠà»‰àº™àº±à»‰àº™à»ƒàº™àºàº²àº™àº®àº»à»ˆàº§à»„ຫຼຂà»à»‰àº¡àº¹àº™. ເພື່ອຮັàºàºªàº²àº„ວາມປອດໄພໃຫ້ບັນຊີຂອງທ່ານ, ຕົວຈັດàºàº²àº™àº¥àº°àº«àº±àº”ຜ່ານ Google à»àº™àº°àº™àº³à»ƒàº«à»‰àºàº§àº”ສອບລະຫັດຜ່ານທີ່ບັນທຶàºà»„ວ້ຂອງທ່ານ.</translation>
@@ -1415,6 +1424,7 @@
<translation id="483241715238664915">ເປີດໃຊ້ຄຳເຕືອນ</translation>
<translation id="4834250788637067901">ວິທີàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™, ຂà»à»‰àºªàº°à»€à»œàºµ à»àº¥àº° ທີ່ຢູ່ໂດàºà»ƒàºŠà»‰ Google Pay</translation>
<translation id="4838327282952368871">ຊວນàºàº±àº™</translation>
+<translation id="4839087176073128681">ຈ່າàºà»€àº‡àº´àº™à»„ດ້ໄວຂຶ້ນໃນເທື່ອຕà»à»ˆà»„ປ à»àº¥àº° ປົàºàº›à»‰àº­àº‡àºšàº±àº”ຂອງທ່ານດ້ວàºàº„ວາມປອດໄພລະດັບà»àº™àº§à»œà»‰àº²àº‚ອງອຸດສາຫະàºàº³àº‚ອງ Google.</translation>
<translation id="4840250757394056958">ເບິ່ງປະຫວັດ Chrome ຂອງທ່ານ</translation>
<translation id="484462545196658690">ອັດຕະໂນມັດ</translation>
<translation id="484671803914931257">ຮັບສ່ວນຫຼຸດຢູ່ <ph name="MERCHANT_NAME" /> à»àº¥àº° ອື່ນໆ</translation>
@@ -1477,6 +1487,7 @@
<translation id="5011561501798487822">ພາສາທີ່àºàº§àº”ພົບ</translation>
<translation id="5015510746216210676">ຊື່ເຄື່ອງ:</translation>
<translation id="5017554619425969104">ຂà»à»‰àº„ວາມທີ່ທ່ານສຳເນົາ</translation>
+<translation id="5017828934289857214">à»àºˆà»‰àº‡à»€àº•àº·àº­àº™àº‚້ອàºàºžàº²àºàº«àº¼àº±àº‡</translation>
<translation id="5018422839182700155">ບà»à»ˆàºªàº²àº¡àº²àº”ເປີດໜ້ານີ້ໄດ້</translation>
<translation id="5019198164206649151">ໜ່ວàºâ€‹à»€àºàº±àºšâ€‹à»œàº¹àº™â€‹àº¢àº¹à»ˆâ€‹à»ƒàº™â€‹àºªàº°àºžàº²àºšâ€‹àºšà»à»ˆâ€‹àº”ີ</translation>
<translation id="5020776957610079374">ເພງທົ່ວໂລàº</translation>
@@ -1496,6 +1507,7 @@
<translation id="5051305769747448211">àºàº²àº™àºªàº°à»àº”ງຕະຫລົàºàºªàº»àº”</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{ເພື່ອສົ່ງໄຟລ໌ນີ້ໂດàºà»ƒàºŠà»‰àºàº²àº™à»àºšà»ˆàº‡àº›àº±àº™à»ƒàºà»‰àº„ຽງ, ໃຫ້ສ້າງພື້ນທີ່ຫວ່າງ (<ph name="DISK_SPACE_SIZE" />) ຢູ່ອຸປະàºàº­àº™àº‚ອງທ່ານàºà»ˆàº­àº™}other{ເພື່ອສົ່ງໄຟລ໌ເຫຼົ່ານີ້ໂດàºà»ƒàºŠà»‰àºàº²àº™à»àºšà»ˆàº‡àº›àº±àº™à»ƒàºà»‰àº„ຽງ, ໃຫ້ສ້າງພື້ນທີ່ຫວ່າງ (<ph name="DISK_SPACE_SIZE" />) ຢູ່ອຸປະàºàº­àº™àº‚ອງທ່ານàºà»ˆàº­àº™}}</translation>
<translation id="5056549851600133418">ບົດຄວາມສຳລັບທ່ານ</translation>
+<translation id="5060483733937416656">ທ່ານເລືອàºàº—ີ່ຈະຢັ້ງຢືນດ້ວຠWindows Hello ຢູ່ເວັບໄຊທີ່ໃຊ້ <ph name="PROVIDER_ORIGIN" />. ຜູ້ໃຫ້ບà»àº¥àº´àºàº²àº™àº™àºµà»‰àº­àº²àº”ມີàºàº²àº™àºˆàº±àº”ເàºàº±àºšàº‚à»à»‰àº¡àº¹àº™àºà»ˆàº½àº§àºàº±àºšàº§àº´àº—ີàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™àº‚ອງທ່ານໄວ້, ເຊິ່ງທ່ານສາມາດ <ph name="LINK_TEXT" /> ໄດ້.</translation>
<translation id="5061227663725596739">ທ່ານà»àº²àºà»€àº–ິງ <ph name="LOOKALIKE_DOMAIN" /> ບà»?</translation>
<translation id="5066056036849835175">ປະຫວັດàºàº²àº™àºžàº´àº¡</translation>
<translation id="5068234115460527047">àºàº­àº‡àº—ຶນ Hedge</translation>
@@ -1509,10 +1521,8 @@
<translation id="5087286274860437796">ໃນ​ເວ​ລາ​ນີ້​ໃບ​ຢັ້ງ​ຢືນ​ຂອງ​ເຊີບ​ເວີ​ໃຊ້​ບà»à»ˆâ€‹à»„ດ້.</translation>
<translation id="5087580092889165836">ເພີ່ມບັດ</translation>
<translation id="5088142053160410913">ຂà»à»‰àº„ວາມເຖິງຜູ້ໃຫ້ບà»àº¥àº´àºàº²àº™</translation>
-<translation id="5089810972385038852">ລັດ</translation>
<translation id="5093232627742069661">ພັບà»àºšàºšàº•àº»àº§ Z</translation>
<translation id="5094747076828555589">ເຊີບເວີນີ້ບà»à»ˆàºªàº²àº¡àº²àº”ພິສູດໄດ້ວ່າ ມັນà»àº¡à»ˆàº™ <ph name="DOMAIN" />; ໃບຢັ້ງຢືນຄວາມປອດໄພຂອງມັນບà»à»ˆà»„ດ້ຮັບàºàº²àº™à»€àºŠàº·à»ˆàº­à»àº±à»‰àº™àºˆàº²àº Chromium. ອັນນີ້ອາດຈະເຮັດໃຫ້ເàºàºµàº”ມີàºàº²àº™àº›àº±àºšàº•àº±à»‰àº‡àº„່າຜິດ ຫຼືຜູ້ໂຈມຕີອາດຈະດັàºà»€àº­àº»àº²àºàº²àº™à»€àºŠàº·à»ˆàº­àº¡àº•à»à»ˆàº‚ອງທ່ານ.</translation>
-<translation id="5095208057601539847">à»àº‚ວງ</translation>
<translation id="5097099694988056070">ສະຖິຕິອຸປະàºàº­àº™ ເຊັ່ນ: àºàº²àº™à»ƒàºŠà»‰ CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">ເວັບໄຊບà»à»ˆàº›àº­àº”ໄພ</translation>
@@ -1550,6 +1560,7 @@
<translation id="5171045022955879922">ຊອàºàº«àº² ຫຼື ພິມ URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">ເຄື່ອງຈັàº</translation>
+<translation id="5177076414499237632">ສຶàºàºªàº²àºà»ˆàº½àº§àºàº±àºšà»àº«àº¼à»ˆàº‡àº—ີ່ມາ à»àº¥àº° ຫົວຂà»à»‰àº‚ອງໜ້ານີ້</translation>
<translation id="5179510805599951267">ບà»à»ˆà»€àº›àº±àº™ <ph name="ORIGINAL_LANGUAGE" /> ບà»? ລາàºàº‡àº²àº™àº„ວາມຜິດພາດນີ້</translation>
<translation id="518639307526414276">ອາຫານ à»àº¥àº° ອຸປະàºàº­àº™à»€àºšàº´à»ˆàº‡à»àºàº‡àºªàº±àº”ລ້ຽງ</translation>
<translation id="5190835502935405962">à»àº–ບບຸàºàº¡àº²àºàºªà»Œ</translation>
@@ -1710,6 +1721,7 @@
<translation id="5624120631404540903">ຈັດàºàº²àº™àº¥àº°â€‹àº«àº±àº”​ຜ່ານ​</translation>
<translation id="5629630648637658800">ໂຫຼດàºàº²àº™àº•àº±à»‰àº‡àº„່ານະໂàºàºšàº²àºàºšà»à»ˆàºªà»àº²à»€àº¥àº±àº”</translation>
<translation id="5631439013527180824">àºàº²à»àº²àºàº„ຸ້ມຄອງອຸປະàºàº­àº™à»ƒàºŠà»‰àºšà»à»ˆà»„ດ້</translation>
+<translation id="5632485077360054581">ສະà»àº”ງວິທີໃຫ້ຂ້ອàºà»€àºšàº´à»ˆàº‡</translation>
<translation id="5633066919399395251">ໃນປັດຈຸບັນ ຜູ້ໂຈມຕີໃນ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ອາດຈະພະàºàº²àºàº²àº¡àº•àº´àº”ຕັ້ງໂປຣà»àºàº£àº¡àº­àº±àº™àº•àº°àº¥àº²àºà»ƒàºªà»ˆàº„ອມພິວເຕີຂອງທ່ານເພື່ອລັàºà»€àº­àº»àº² ຫຼື ລຶບຂà»à»‰àº¡àº¹àº™àº‚ອງທ່ານ (ຕົວຢ່າງ: ຮູບຖ່າàº, ລະຫັດຜ່ານ, ຂà»à»‰àº„ວາມ à»àº¥àº° ບັດເຄຣດິດ). <ph name="BEGIN_LEARN_MORE_LINK" />ສຶàºàºªàº²à»€àºžàºµà»ˆàº¡à»€àº•àºµàº¡<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">ບລັອàºà»€àº™àº·à»‰àº­àº«àº²àº—ີ່ຫຼອàºàº¥àº§àº‡à»„ວ້à»àº¥à»‰àº§.</translation>
<translation id="5633259641094592098">ໜັງà»àº™àº§àº¥àº±àº”ທິ à»àº¥àº° ອິນດີ້</translation>
@@ -1827,6 +1839,7 @@
<translation id="5989320800837274978">ບà»à»ˆà»„ດ້àºà»àº²àº™àº»àº”ທັງເຊີບເວີພຣັອàºàºŠàºµàº—ີ່àºà»àº²àº™àº»àº”ໄວ້ ຫຼື URL ຕົວຂຽນ .pac.</translation>
<translation id="5992691462791905444">ພັບທົບà»àºšàºš Engineering ຮູບໂຕ Z</translation>
<translation id="5995727681868049093">ຈັດàºàº²àº™àº‚à»à»‰àº¡àº¹àº™, ຄວາມເປັນສ່ວນຕົວ à»àº¥àº° ຄວາມປອດໄພຂອງທ່ານໃນບັນຊີ Google ທ່ານ</translation>
+<translation id="5997247540087773573">ພົບລະຫັດຜ່ານທີ່ທ່ານຫາàºà»à»ƒàºŠà»‰àº™àº±à»‰àº™à»ƒàº™àºàº²àº™àº®àº»à»ˆàº§à»„ຫຼຂà»à»‰àº¡àº¹àº™. ເພື່ອຮັàºàºªàº²àº„ວາມປອດໄພໃຫ້ບັນຊີຂອງທ່ານ, ຕົວຈັດàºàº²àº™àº¥àº°àº«àº±àº”ຜ່ານ Google à»àº™àº°àº™àº³à»ƒàº«à»‰àº›à»ˆàº½àº™àº¡àº±àº™àº”ຽວນີ້à»àº¥à»‰àº§àºˆàº²àºàº™àº±à»‰àº™àºàº§àº”ສອບລະຫັດຜ່ານທີ່ບັນທຶàºà»„ວ້ຂອງທ່ານ.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> ຜົນàºàº²àº™àºŠàº­àºàº«àº²àºªàº³àº¥àº±àºš '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">ຄລາສສິàº</translation>
<translation id="6008122969617370890">ລຳດັບ N ເຖິງ 1</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">ເພື່ອຈ່າàºà»„ດ້ໄວàºàº§à»ˆàº²à»ƒàº™àº„ັ້ງຕà»à»ˆà»„ປ, àºàº°àº¥àº¸àº™àº²àºšàº±àº™àº—ຶàºàºšàº±àº” à»àº¥àº° ທີ່ຢູ່ຮຽàºà»€àºàº±àºšà»€àº‡àº´àº™àº‚ອງທ່ານໄວ້ໃນບັນຊີ Google ຂອງທ່ານ.</translation>
<translation id="6279183038361895380">àºàº»àº” |<ph name="ACCELERATOR" />| ເພື່ອສະà»àº”ງເຄີເຊີຂອງທ່ານ</translation>
<translation id="6280223929691119688">ບà»à»ˆàºªàº²àº¡àº²àº”ສົ່ງຫາທີ່ຢູ່ນີ້ໄດ້. àºàº°àº¥àº¸àº™àº²à»€àº¥àº·àº­àºàº—ີ່ຢູ່ອື່ນ.</translation>
-<translation id="6282194474023008486">ລະຫັດໄປສະນີ</translation>
<translation id="6285507000506177184">ປຸ່ມຈັດàºàº²àº™àºàº²àº™àº”າວໂຫຼດໃນ Chrome, àºàº»àº” Enter ເພື່ອຈັດàºàº²àº™à»„ຟລ໌ທີ່ທ່ານດາວໂຫຼດມາà»àº¥à»‰àº§à»ƒàº™ Chrome</translation>
<translation id="6289939620939689042">ສີໜ້າເຈ້àº</translation>
<translation id="6290238015253830360">ບົດຄວາມທີ່à»àº™àº°àº™àº³àº‚ອງທ່ານຈະປາàºàº»àº”ຢູ່ບ່ອນນີ້</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026">ຂະຫàºàº²àºàºžàº·à»‰àº™àº—ີ່ຫວ່າງໄດ້ໜ້ອàºàºàº§à»ˆàº² <ph name="SIZE" />. ບາງເວັບໄຊອາດຈະໂຫຼດຊ້າàºàº§à»ˆàº²à»ƒàº™àºàº²àº™à»€àº‚ົ້າເບິ່ງຄັ້ງຕà»à»ˆà»„ປຂອງທ່ານ.</translation>
<translation id="6337534724793800597">àºàº±à»ˆàº™àº•àº­àº‡àº™àº°à»‚àºàºšàº²àºàº•àº²àº¡àºŠàº·à»ˆ</translation>
<translation id="6340739886198108203">ນະໂàºàºšàº²àºàºœàº¹à»‰à»€àºšàº´à»ˆàº‡à»àºàº‡àº¥àº°àºšàº»àºšàºšà»à»ˆà»àº™àº°àº™àº³à»ƒàº«à»‰àº–່າàºàº®àº¹àºš ຫຼື ບັນທຶàºà»œà»‰àº²àºˆà»à»ƒàº™à»€àº§àº¥àº²àº›àº²àºàº»àº”ມີເນື້ອຫາທີ່ເປັນຄວາມລັບ:</translation>
+<translation id="6348220984832452017">ຮູບà»àºšàºšàº—ີ່ນຳໃຊ້ຢູ່</translation>
<translation id="6349101878882523185">ຕິດຕັ້ງ <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ບà»à»ˆàº¡àºµ}=1{1 ລະຫັດຜ່ານ (ສຳລັບ <ph name="DOMAIN_LIST" />, ຊິ້ງຂà»à»‰àº¡àº¹àº™à»àº¥à»‰àº§)}=2{2 ລະຫັດຜ່ານ (ສຳລັບ <ph name="DOMAIN_LIST" />, ຊິ້ງຂà»à»‰àº¡àº¹àº™à»àº¥à»‰àº§)}other{# ລະຫັດຜ່ານ (ສຳລັບ <ph name="DOMAIN_LIST" />, ຊິ້ງຂà»à»‰àº¡àº¹àº™à»àº¥à»‰àº§)}}</translation>
<translation id="6355392890578844978">ໂປຣà»àºàº£àº¡àº—່ອງເວັບນີ້ບà»à»ˆà»„ດ້ຖືàºàºˆàº±àº”àºàº²àº™à»‚ດàºàºšà»àº¥àº´àºªàº±àº” ຫຼື ອົງàºàº²àº™àº­àº·à»ˆàº™. àºàº²àº™à»€àº„ື່ອນໄຫວຢູ່ອຸປະàºàº­àº™àº™àºµà»‰àº­àº²àº”ຖືàºàºˆàº±àº”àºàº²àº™àº¢àº¹à»ˆàº™àº­àº Chromium ໄດ້. <ph name="BEGIN_LINK" />ສຶàºàºªàº²à»€àºžàºµà»ˆàº¡à»€àº•àºµàº¡<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">ປ່ຽນລະຫັດຜ່ານ Google</translation>
<translation id="6433490469411711332">à»àºà»‰à»„ຂຂà»à»‰àº¡àº¹àº™àº¥àº²àºàºŠàº·à»ˆàºœàº¹à»‰àº•àº´àº”ຕà»à»ˆ</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ໄດ້ປະຕິເສດàºàº²àº™à»€àºŠàº·à»ˆàº­àº¡àº•à»à»ˆ.</translation>
-<translation id="6438025220577812695">ປ່ຽນມັນດ້ວàºàº•àº»àº§à»€àº­àº‡</translation>
<translation id="6440503408713884761">ລະເລີàº</translation>
<translation id="6443406338865242315">ສ່ວນຂະຫàºàº²àº à»àº¥àº° ປລັàºàº­àº´àº™àº—ີ່ທ່ານຕິດຕັ້ງ</translation>
<translation id="6446163441502663861">Kahu (ຊອງຈົດà»àº²àº)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">ພັນທຸສາດ</translation>
<translation id="6831043979455480757">à»àº›àºžàº²àºªàº²</translation>
<translation id="6833752742582340615">ບັນທຶàºàº‚à»à»‰àº¡àº¹àº™àºšàº±àº” à»àº¥àº° ໃບບິນຂອງທ່ານໄປໃສ່ບັນຊີ Google ຂອງທ່ານສຳລັບàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™àº—ີ່ປອດໄພ à»àº¥àº° ໄວຂຶ້ນ</translation>
-<translation id="6839929833149231406">ເຂດ</translation>
<translation id="6846340164947227603">ໃຊ້à»àº²àºà»€àº¥àºàºšàº±àº”ສະເà»àº·àº­àº™àºˆàº´àº‡...</translation>
<translation id="6852204201400771460">ໂຫຼດà»àº­àº±àºšàº„ືນໃà»à»ˆàºšà»?</translation>
+<translation id="6857776781123259569">ຈັດàºàº²àº™àº¥àº°àº«àº±àº”ຜ່ານ...</translation>
<translation id="686485648936420384">ຂà»à»‰àº¡àº¹àº™àºœàº¹à»‰àºšà»àº¥àº´à»‚ພàº</translation>
<translation id="6865412394715372076">ບà»à»ˆàºªàº²àº¡àº²àº”ຢັ້ງຢືນບັດນີ້ໄດ້ໃນຕອນນີ້</translation>
<translation id="6869334554832814367">àºàº²àº™à»ƒàº«à»‰àº„ຳປຶàºàºªàº²àºà»ˆàº½àº§àºàº±àºšàºªàº´àº™à»€àºŠàº·à»ˆàº­àºªà»ˆàº§àº™àºšàº¸àºàº„ົນ</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">ອຸ​ປະ​àºàº­àº™</translation>
<translation id="696703987787944103">Perceptual</translation>
<translation id="6968269510885595029">ໃຊ້àºàº°à»àºˆàº„ວາມປອດໄພຂອງທ່ານ</translation>
-<translation id="6970216967273061347">ເມືອງ</translation>
<translation id="6971439137020188025">ສ້າງພຣີເຊັນເທເຊິນ Google ໃà»à»ˆà»ƒàº™ Slides ໄດ້ຢ່າງວ່ອງໄວ</translation>
<translation id="6972629891077993081">ອຸປະàºàº­àº™ HID</translation>
<translation id="6973656660372572881">àºà»àº²àº™àº»àº”ທັງເຊີບເວີພຣັອàºàºŠàºµàº—ີ່àºà»àº²àº™àº»àº”ໄວ້ à»àº¥àº° URL ຕົວຂຽນ .pac à»àº¥à»‰àº§.</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">ຄຸນສົມບັດນີ້ບà»à»ˆàºªàº²àº¡àº²àº”ໃຊ້ໄດ້ໃນອຸປະàºàº­àº™àº‚ອງທ່ານ</translation>
<translation id="7083258188081898530">ຖາດ 9</translation>
<translation id="7086090958708083563">àºàº²àº™àº­àº±àºšà»‚ຫຼດທີ່ຂà»à»‚ດàºàºœàº¹à»‰à»ƒàºŠà»‰</translation>
-<translation id="7087282848513945231">ເຂດປົàºàº„ອງ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, àºàº»àº” Tab ຈາàºàº™àº±à»‰àº™àºàº»àº” Enter ເພື່ອຈັດàºàº²àº™àºàº²àº™àº­àº°àº™àº¸àºàº²àº” à»àº¥àº° ຂà»à»‰àº¡àº¹àº™àº—ີ່ຈັດເàºàº±àºšà»„ວ້ໃນເວັບໄຊຕ່າງໆໃນàºàº²àº™àº•àº±à»‰àº‡àº„່າ Chrome</translation>
<translation id="7096937462164235847">ບà»à»ˆà»„ດ້ຢັ້ງຢືນຕົວຕົນຂອງເວັບໄຊນີ້ເທື່ອ.</translation>
<translation id="7101893872976785596">ໜັງສະຫàºàº­àº‡àº‚ວັນ</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />ຂà»à»‰àº¡àº¹àº™àº—ີ່ລະບຸໄວ້ໃນà»àºšàºšàºŸàº­àº¡àº•à»ˆàº²àº‡à»†<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">ບà»à»ˆàºªàº²àº¡àº²àº”ຈັດສົ່ງຫາທີ່ຢູ່ນີ້ໄດ້. àºàº°àº¥àº¸àº™àº²à»€àº¥àº·àº­àºàº—ີ່ຢູ່ອື່ນ.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ຜູ້ເບິ່ງà»àºàº‡àº‚ອງທ່ານຫ້າມບà»à»ˆà»ƒàº«à»‰àºªàº³à»€àº™àº»àº²àº‚à»à»‰àº¡àº¹àº™àº™àºµà»‰.</translation>
<translation id="7135130955892390533">ສະà»àº”ງສະຖານະ</translation>
<translation id="7138472120740807366">ວິທີàºàº²àº™àºªàº»à»ˆàº‡</translation>
-<translation id="7139724024395191329">ອີມິເຣດ</translation>
<translation id="7139892792842608322">ຖາດຫຼັàº</translation>
<translation id="714064300541049402">ປ່ຽນຕຳà»à»œà»ˆàº‡àº®àº¹àºšàºžàº²àºšàº”້ານ 2 ຕາມà»àºàº™ X</translation>
<translation id="7152423860607593928">Number-14 (ຊອງຈົດà»àº²àº)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">ຜູ້ໂຈມຕີໃນເວັບໄຊນີ້ອາດຈະພະàºàº²àºàº²àº¡àº«àº¼àº­àºà»ƒàº«à»‰àº—່ານຕິດຕັ້ງໂປຣà»àºàº£àº¡àº—ີ່ເປັນອັນຕະລາàºàº•à»à»ˆàºàº±àºšàº›àº°àºªàº»àºšàºàº²àº™àº—່ອງເວັບຂອງທ່ານຢູ່ (ເຊັ່ນວ່າ ໂດàºàºàº²àº™àº›à»ˆàº½àº™à»œà»‰àº²àº«àº¼àº±àºàº‚ອງທ່ານ ຫຼື ສະà»àº”ງໂຄສະນາເພີ່ມເຕີມຢູ່ໃນເວັບໄຊທີ່ທ່ານເຂົ້າເບິ່ງ).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ຄຳສັ່ງທີ່ໃຊ້àºàº±àºšàº‚à»à»‰àº¡àº¹àº™à»àº¡à»ˆàº™àº–ືàºà»àº²àºàº—ຸງວ່າເປັນຄວາມລັບ (1 ຄຳສັ່ງຕັ້ງà»àº•à»ˆà»€àº‚ົ້າສູ່ລະບົບມາ). <ph name="BEGIN_LINK" />ສຶàºàºªàº²à»€àºžàºµà»ˆàº¡à»€àº•àºµàº¡<ph name="END_LINK" />}other{ຄຳສັ່ງທີ່ໃຊ້àºàº±àºšàº‚à»à»‰àº¡àº¹àº™à»àº¡à»ˆàº™àº–ືàºà»àº²àºàº—ຸງວ່າເປັນຄວາມລັບ (# ຄຳສັ່ງຕັ້ງà»àº•à»ˆà»€àº‚ົ້າສູ່ລະບົບມາ). <ph name="BEGIN_LINK" />ສຶàºàºªàº²à»€àºžàºµà»ˆàº¡à»€àº•àºµàº¡<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">àºà»ˆàº­àº‡àºˆàº»àº”à»àº²àº 6</translation>
+<translation id="7675325315208090829">ຈັດàºàº²àº™àº§àº´àº—ີàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™...</translation>
<translation id="7676643023259824263">ຊອàºàº«àº²àº‚à»à»‰àº„ວາມໃນຄລິບບອດ, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">ເບິ່ງ à»àº¥àº° ຈັດàºàº²àº™àº›àº°àº«àº§àº±àº”àºàº²àº™àº—່ອງເວັບຂອງທ່ານໃນàºàº²àº™àº•àº±à»‰àº‡àº„່າ Chrome</translation>
<translation id="7679947978757153706">ເບສບອນ</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">ລຳດັບດຽວàºàº±àº™àº›àºµà»‰àº™à»œà»‰àº²àº‚ຶ້ນ</translation>
-<translation id="777702478322588152">à»àº‚ວງ</translation>
<translation id="7791011319128895129">ບà»à»ˆàº—ັນເຜີàºà»àºœà»ˆà»€àº—ື່ອ</translation>
<translation id="7791196057686275387">ບັນຈຸພັນ</translation>
<translation id="7791543448312431591">ເພີ່ມ</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">ວິຊາຊີບ à»àº¥àº° àºàº²àº™àºªàº¶àºàºªàº²àº•à»à»ˆ</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" ບà»à»ˆàº–ືàºàºàº³àº™àº»àº”ຄ່າຢ່າງຖືàºàº•à»‰àº­àº‡. ໂດàºàº›àº»àºàºàº°àº•àº´à»àº¥à»‰àº§àºàº²àº™àº–ອນຕິດຕັ້ງ "<ph name="SOFTWARE_NAME" />" ຈະà»àºà»‰à»„ຂບັນຫາໄດ້. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">àºàº²àº™àºœàº°àº¥àº´àº”ອາຫານ</translation>
-<translation id="8066955247577885446">ຂà»àº­àº°à»„ພ, ມີບາງຢ່າງຜິດພາດ.</translation>
<translation id="8067872629359326442">ທ່ານຫາàºà»àº›à»‰àº­àº™àº¥àº°àº«àº±àº”ຜ່ານຂອງທ່ານໃສ່ເວັບໄຊຫຼອàºàº¥àº§àº‡. Chromium ສາມາດຊ່ວàºà»„ດ້. ເພື່ອປ່ຽນລະຫັດຜ່ານຂອງທ່ານ à»àº¥àº° à»àºˆà»‰àº‡àºšàº­àº Google ວ່າບັນຊີຂອງທ່ານອາດຈະມີຄວາມສ່ຽງ, àºàº°àº¥àº¸àº™àº²àº„ລິàºàº›àº»àºàº›à»‰àº­àº‡àºšàº±àº™àºŠàºµ.</translation>
<translation id="8070439594494267500">ໄອຄອນà»àº­àº±àºš</translation>
<translation id="8074253406171541171">10x13 (ຊອງຈົດà»àº²àº)</translation>
<translation id="8075736640322370409">ສ້າງ Google Sheet ໃà»à»ˆà»„ດ້ຢ່າງວ່ອງໄວ</translation>
<translation id="8075898834294118863">ຈັດàºàº²àº™àºàº²àº™àº•àº±à»‰àº‡àº„່າເວັບໄຊ</translation>
+<translation id="8076492880354921740">à»àº–ບ</translation>
<translation id="8078141288243656252">ບà»à»ˆàºªàº²àº¡àº²àº”ຂຽນອະທິບາàºàº„ວາມເຫັນໃນເວລາà»àº¸àº™à»„ດ້</translation>
<translation id="8079031581361219619">ໂຫຼດເວັບໄຊຄືນໃà»à»ˆàºšà»?</translation>
<translation id="8081087320434522107">ລົດເàºàº±àº‡</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">àºàº²àº™â€‹àº•àº±à»‰àº‡àº„່າ</translation>
+<translation id="8428634594422941299">ເຂົ້າໃຈà»àº¥à»‰àº§</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, àºàº»àº” Tab ຈາàºàº™àº±à»‰àº™àºàº»àº” Enter ເພື່ອຈັດàºàº²àº™àºàº²àº™àº•àº±à»‰àº‡àº„່າຄຸàºàºàºµà»‰àº‚ອງທ່ານໃນàºàº²àº™àº•àº±à»‰àº‡àº„່າ Chrome</translation>
<translation id="8433057134996913067">ນີ້ຈະນຳທ່ານອອàºàºˆàº²àºàº¥àº°àºšàº»àºšà»€àº§àº±àºšà»„ຊສ່ວນໃຫàºà»ˆ.</translation>
<translation id="8434840396568290395">ສັດລ້ຽງ</translation>
@@ -2792,6 +2803,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> à»àº¡à»ˆàº™àº¥àº°àº«àº±àº”ສຳລັບ <ph name="ORIGIN" /> ຂອງທ່ານ</translation>
<translation id="874918643257405732">ບຸàºàº¡àº²àºà»àº–ບນີ້</translation>
<translation id="8751426954251315517">àºàº°àº¥àº¸àº™àº²àº¥àº­àº‡à»ƒà»à»ˆà»ƒàº™à»€àº—ື່ອໜ້າ</translation>
+<translation id="8757526089434340176">ມີຂà»à»‰àºªàº°à»€à»œàºµ Google Pay</translation>
<translation id="8758885506338294482">ວິດີໂອເàºàº¡à»àºšàºšà»àº‚່ງຂັນ</translation>
<translation id="8759274551635299824">ບັດນີ້à»àº»àº”ອາàºàº¸à»àº¥à»‰àº§</translation>
<translation id="87601671197631245">ເວັບໄຊນີ້ໃຊ້àºàº²àº™àº•àº±à»‰àº‡àº„່າຄວາມປອດໄພທີ່ເàºàº»à»ˆàº²à»àº¥à»‰àº§, ເຊິ່ງອາດຈະເຮັດໃຫ້ຂà»à»‰àº¡àº¹àº™àº‚ອງທ່ານມີຄວາມສ່ຽງ (ຕົວຢ່າງ: ລະຫັດຜ່ານ, ຂà»à»‰àº„ວາມ ຫຼື ບັດເຄຣດິດ) ເມື່ອມັນຖືàºàºªàº»à»ˆàº‡à»ƒàº«à»‰à»€àº§àº±àºšà»„ຊນີ້.</translation>
@@ -2799,6 +2811,7 @@
<translation id="8763927697961133303">ອຸປະàºàº­àº™ USB</translation>
<translation id="8763986294015493060">ປິດໜ້າ​ຈà»â€‹àº—ີ່ບà»à»ˆà»€àº›àºµàº”ເຜີàºàº•àº»àº§àº•àº»àº™àº—ັງà»àº»àº”ທີ່àºàº³àº¥àº±àº‡à»€àº›àºµàº”ຢູ່ຕອນນີ້</translation>
<translation id="8766943070169463815">ເປີດຊີດàºàº²àº™àºžàº´àºªàº¹àº”ຢືນຢັນຂà»à»‰àº¡àº¹àº™àºàº²àº™à»€àº‚ົ້າສູ່ລະບົບàºàº²àº™àºˆà»ˆàº²àºà»€àº‡àº´àº™à»àºšàºšàº›àº­àº”ໄພà»àº¥à»‰àº§</translation>
+<translation id="8767765348545497220">ປິດຟອງຊ່ວàºà»€àº«àº¼àº·àº­</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">ລົດຈັàº</translation>
<translation id="8790007591277257123">ເຮັດຄືນຄà»àº²àºªàº±à»ˆàº‡àº¥àº¶àºš</translation>
@@ -2811,6 +2824,7 @@
<translation id="8806285662264631610">ຜະລິດຕະພັນອາບນ້ຳ à»àº¥àº° ຮ່າງàºàº²àº</translation>
<translation id="8807160976559152894">ຕັດອອàºàº«àº¼àº±àº‡àºˆàº²àºà»àº•à»ˆàº¥àº°à»œà»‰àº²</translation>
<translation id="8808828119384186784">àºàº²àº™àº•àº±à»‰àº‡àº„່າ Chrome</translation>
+<translation id="8813277370772331957">à»àºˆà»‰àº‡à»€àº•àº·àº­àº™àº‚້ອàºà»ƒàº™àºžàº²àºàº«àº¼àº±àº‡</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, àºàº»àº”à»àº–ບ ຈາàºàº™àº±à»‰àº™ Enter ເພື່ອອັບເດດ Chrome ຈາàºàºàº²àº™àº•àº±à»‰àº‡àº„່າ Chrome ຂອງທ່ານ</translation>
<translation id="8820817407110198400">ບຸàºàº¡àº²àº</translation>
<translation id="882338992931677877">ຊ່ອງàºàº³àº™àº»àº”ເອງ</translation>
@@ -2990,6 +3004,7 @@
<translation id="988159990683914416">ສ້າງ​ນັàºàºžàº±àº”​ທະ​ນາ</translation>
<translation id="989988560359834682">à»àºà»‰à»„ຂທີ່ຢູ່</translation>
<translation id="991413375315957741">ເຊັນເຊີàºàº§àº”ຈັບàºàº²àº™à»€àº„ື່ອນໄຫວ ຫຼື à»àºªàº‡</translation>
+<translation id="992110854164447044">ບັດສະເà»àº·àº­àº™àºˆàº°à»€àºŠàº·à»ˆàº­àº‡àºšàº±àº”à»àº—້ຂອງທ່ານເພື່ອຊ່ວàºàº›àº»àºàº›à»‰àº­àº‡àº—່ານຈາàºàº„ວາມສ່ຽງໃນàºàº²àº™àºªà»à»‰à»‚àºàº‡. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ສີບົວ</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" ບà»à»ˆà»„ດ້ຖືàºàº•àº´àº”ຕັ້ງຢ່າງຖືàºàº•à»‰àº­àº‡à»ƒàº™àº„ອມພິວເຕີຂອງທ່ານ ຫຼື ເຄືອຂ່າàº:
diff --git a/chromium/components/strings/components_strings_lt.xtb b/chromium/components/strings/components_strings_lt.xtb
index ab48c8dd947..bf136d37155 100644
--- a/chromium/components/strings/components_strings_lt.xtb
+++ b/chromium/components/strings/components_strings_lt.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Dotacijos, stipendijos ir finansinÄ— pagalba</translation>
<translation id="1048785276086539861">Kai redaguosite komentarus, bus pateikta šio dokumento vieno puslapio peržiūra</translation>
<translation id="1050038467049342496">Uždarykite kitas programas</translation>
+<translation id="1053959602163383901">Svetainėse, kuriose naudojama <ph name="PROVIDER_ORIGIN" />, pasirinkote patvirtinti naudodami autentifikavimo priemonės įrenginį. Šis teikėjas galėjo išsaugoti informaciją apie jūsų mokėjimo metodą, ir galite <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Anuliuoti pridÄ—jimÄ…</translation>
<translation id="1056663316309890343">Nuotraukų programinė įranga</translation>
<translation id="1056898198331236512">Įspėjimas</translation>
@@ -119,6 +120,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="1270502636509132238">PaÄ—mimo metodas</translation>
<translation id="1281476433249504884">1 dÄ—tuvÄ—</translation>
<translation id="1285320974508926690">Niekada neversti Å¡ios svetainÄ—s</translation>
+<translation id="1288548991597756084">Patikima kortelės duomenų apsauga</translation>
<translation id="1292571435393770077">Šešioliktas dėklas</translation>
<translation id="1292701964462482250">„Programinė įranga jūsų kompiuteryje neleidžia „Chrome“ saugiai prisijungti prie žiniatinklio“ (tik „Windows“ kompiuteriuose)</translation>
<translation id="1294154142200295408">Komandos eilutÄ—s kintamieji</translation>
@@ -223,6 +225,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
&lt;p&gt;Kad ištaisytumėte šią klaidą, puslapyje, kurį bandote atidaryti, spustelėkite &lt;strong&gt;Prisijungti&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Kraštovaizdžio dizainas</translation>
<translation id="1513706915089223971">Istorijos įrašų sąrašas</translation>
+<translation id="1516097932025103760">Ji bus užšifruota, patikimai saugoma, o kortelės saugos kodas (CVC) niekada nesaugomas.</translation>
<translation id="1517433312004943670">BÅ«tinas telefono numeris</translation>
<translation id="1519264250979466059">Sukūrimo data</translation>
<translation id="1521159554480556801">Pluošto ir audinių menai</translation>
@@ -288,6 +291,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="1662550410081243962">Išsaugoti ir užpildyti mokėjimo metodų informaciją</translation>
<translation id="1663943134801823270">Kortelės ir adresai naudojami iš „Chrome“. Juos galite tvarkyti nuėję į <ph name="BEGIN_LINK" />Nustatymus<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Puslapiai, paraÅ¡yti <ph name="SOURCE_LANGUAGE" />, nuo dabar bus verÄiami į <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Jei norite pasinaudoti pasiūlymu, atsiskaitykite naudodami „<ph name="CARD_DETAIL" />“</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> į <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Pirmiau trumpasis kraštas</translation>
<translation id="168693727862418163">Å ios politikos vertÄ—s nepavyko patvirtinti pagal schemÄ… ir jos bus nepaisoma.</translation>
@@ -306,6 +310,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="1717218214683051432">Judesių jutikliai</translation>
<translation id="1717494416764505390">3 pašto dėžutė</translation>
<translation id="1718029547804390981">Dokumentas per didelis, kad būtų galima komentuoti</translation>
+<translation id="1720941539803966190">Uždaryti mokymo programą</translation>
<translation id="1721424275792716183">* Būtina užpildyti lauką</translation>
<translation id="1727613060316725209">Sertifikatas galioja</translation>
<translation id="1727741090716970331">Tinkamo kortelÄ—s numerio pridÄ—jimas</translation>
@@ -422,8 +427,8 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="205212645995975601">Kepsniai ir kepimas ant grotelių</translation>
<translation id="2053111141626950936">Puslapiai, paraÅ¡yti <ph name="LANGUAGE" />, verÄiami nebus.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Kai Å¡is valdiklis įjungtas ir bÅ«sena aktyvi, „Chrome“ nustato, į kuriÄ… didelÄ™ žmonių grupÄ™ panaÅ¡iausia jÅ«sų pastaroji narÅ¡ymo veikla. Reklamuotojai gali pasirinkti grupÄ—s skelbimus, o jÅ«sų narÅ¡ymo veikla saugoma privaÄiai jÅ«sų įrenginyje. GrupÄ— atnaujinama kiekvienÄ… dienÄ….}=1{Kai Å¡is valdiklis įjungtas ir bÅ«sena aktyvi, „Chrome“ nustato, į kuriÄ… didelÄ™ žmonių grupÄ™ panaÅ¡iausia jÅ«sų pastaroji narÅ¡ymo veikla. Reklamuotojai gali pasirinkti grupÄ—s skelbimus, o jÅ«sų narÅ¡ymo veikla saugoma privaÄiai jÅ«sų įrenginyje. GrupÄ— atnaujinama kiekvienÄ… dienÄ….}one{Kai Å¡is valdiklis įjungtas ir bÅ«sena aktyvi, „Chrome“ nustato, į kuriÄ… didelÄ™ žmonių grupÄ™ panaÅ¡iausia jÅ«sų pastaroji narÅ¡ymo veikla. Reklamuotojai gali pasirinkti grupÄ—s skelbimus, o jÅ«sų narÅ¡ymo veikla saugoma privaÄiai jÅ«sų įrenginyje. GrupÄ— atnaujinama kas {NUM_DAYS} dienÄ….}few{Kai Å¡is valdiklis įjungtas ir bÅ«sena aktyvi, „Chrome“ nustato, į kuriÄ… didelÄ™ žmonių grupÄ™ panaÅ¡iausia jÅ«sų pastaroji narÅ¡ymo veikla. Reklamuotojai gali pasirinkti grupÄ—s skelbimus, o jÅ«sų narÅ¡ymo veikla saugoma privaÄiai jÅ«sų įrenginyje. GrupÄ— atnaujinama kas {NUM_DAYS} dienas.}many{Kai Å¡is valdiklis įjungtas ir bÅ«sena aktyvi, „Chrome“ nustato, į kuriÄ… didelÄ™ žmonių grupÄ™ panaÅ¡iausia jÅ«sų pastaroji narÅ¡ymo veikla. Reklamuotojai gali pasirinkti grupÄ—s skelbimus, o jÅ«sų narÅ¡ymo veikla saugoma privaÄiai jÅ«sų įrenginyje. GrupÄ— atnaujinama kas {NUM_DAYS} dienos.}other{Kai Å¡is valdiklis įjungtas ir bÅ«sena aktyvi, „Chrome“ nustato, į kuriÄ… didelÄ™ žmonių grupÄ™ panaÅ¡iausia jÅ«sų pastaroji narÅ¡ymo veikla. Reklamuotojai gali pasirinkti grupÄ—s skelbimus, o jÅ«sų narÅ¡ymo veikla saugoma privaÄiai jÅ«sų įrenginyje. GrupÄ— atnaujinama kas {NUM_DAYS} dienų.}}</translation>
-<translation id="2053553514270667976">Pašto kodas</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 pasiūlymas}one{# pasiūlymas}few{# pasiūlymai}many{# pasiūlymo}other{# pasiūlymų}}</translation>
+<translation id="2066915425250589881">pateikti užklausą ją ištrinti</translation>
<translation id="2068528718802935086">KÅ«dikiai ir pradedantys vaikÅ¡Äioti vaikai</translation>
<translation id="2071156619270205202">Šiai kortelei negali būti suteiktas virtualios kortelės numeris.</translation>
<translation id="2071692954027939183">Pranešimai automatiškai užblokuoti, nes įprastai jų neleidžiate</translation>
@@ -435,7 +440,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="2088086323192747268">Mygtukas „Tvarkyti sinchronizavimą“; paspauskite „Enter“, jei norite tvarkyti sinchronizuojamą informaciją „Chrome“ nustatymuose</translation>
<translation id="2091887806945687916">Garsas</translation>
<translation id="2094505752054353250">Domeno neatitikimas</translation>
-<translation id="2096368010154057602">Departamentas</translation>
<translation id="2099652385553570808">Trys sankabÄ—lÄ—s kairÄ—je</translation>
<translation id="2101225219012730419">Versija:</translation>
<translation id="2102134110707549001">Siūlyti sudėtingą slaptažodį…</translation>
@@ -472,7 +476,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="2185836064961771414">Amerikietiškasis futbolas</translation>
<translation id="2187317261103489799">Aptikti (numatytoji parinktis)</translation>
<translation id="2188375229972301266">Kelios skylÄ—s apaÄioje</translation>
-<translation id="2188852899391513400">Slaptažodis, kurį ką tik naudojote, buvo atskleistas įvykus duomenų saugos pažeidimui. Siekiant užtikrinti paskyrų saugumą, „Google“ slaptažodžių tvarkytuvė rekomenduoja pakeisti jį dabar ir patikrinti išsaugotus slaptažodžius.</translation>
<translation id="219906046732893612">Namų tobulinimas</translation>
<translation id="2202020181578195191">Įveskite tinkamus galiojimo laiko pabaigos metus</translation>
<translation id="22081806969704220">3 dÄ—klas</translation>
@@ -483,6 +486,8 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="2215727959747642672">Failo redagavimas</translation>
<translation id="2215963164070968490">Å unys</translation>
<translation id="2218879909401188352">Šiuo metu užpuolikai svetainėje <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> gali įdiegti pavojingų programų, kurios gali būti žalingos įrenginiui, atlikti nepageidaujamų veiksmų, dėl kurių padidės mobiliojo telefono sąskaita, arba pavogti asmens informaciją. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Leisti mokymo programą iš naujo</translation>
+<translation id="2219735899272417925">Būtina iš naujo nustatyti įrenginį</translation>
<translation id="2224337661447660594">Nėra interneto ryšio</translation>
<translation id="2230458221926704099">Išspręskite ryšio problemas naudodami <ph name="BEGIN_LINK" />diagnostikos programą<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Siųsti dabar</translation>
@@ -580,11 +585,13 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="2512101340618156538">Neleidžiama (numatytasis nustatymas)</translation>
<translation id="2512413427717747692">„Chrome“ nustatymo kaip numatytosios naršyklės režimas; paspauskite „Enter“, kad nustatytumėte „Chrome“ kaip numatytąją sistemos naršyklę „iOS“ nustatymuose</translation>
<translation id="2515629240566999685">Patikrinti signalo stiprumÄ… savo srityje</translation>
+<translation id="2515761554693942801">Svetainėse, kuriose naudojama <ph name="PROVIDER_ORIGIN" />, pasirinkote patvirtinti naudodami „Touch ID“. Šis teikėjas galėjo išsaugoti informaciją apie jūsų mokėjimo metodą, ir galite <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">SankabÄ—lÄ— apaÄioje deÅ¡inÄ—je</translation>
<translation id="2521736961081452453">Sukurti formÄ…</translation>
<translation id="2523886232349826891">Išsaugota tik šiame įrenginyje</translation>
<translation id="2524461107774643265">Daugiau informacijos pridÄ—jimas</translation>
<translation id="2529899080962247600">Šiame lauke gali būti ne daugiau nei <ph name="MAX_ITEMS_LIMIT" /> įraš. Į visus kitus įrašus nebus atsižvelgiama.</translation>
+<translation id="253493526287553278">Žr. išsamią reklamos kredito kodo informaciją</translation>
<translation id="2535585790302968248">NarÅ¡ykite privaÄiai, atidarÄ™ naujÄ… inkognito skirtukÄ…</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{ir dar 1}one{ir dar #}few{ir dar #}many{ir dar #}other{ir dar #}}</translation>
<translation id="2536110899380797252">PridÄ—ti adresÄ…</translation>
@@ -620,7 +627,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="259821504105826686">Fotografijos ir skaitmeninis menas</translation>
<translation id="2601150049980261779">Romantiniai filmai</translation>
<translation id="2604589665489080024">Popmuzika</translation>
-<translation id="2609632851001447353">Variantai</translation>
<translation id="2610561535971892504">SpustelÄ—kite, jei norite kopijuoti</translation>
<translation id="2617988307566202237">„Chrome“ <ph name="BEGIN_EMPHASIS" />nesaugos<ph name="END_EMPHASIS" /> šios informacijos:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Gimtadieniai ir vardadieniai</translation>
<translation id="2677748264148917807">IÅ¡eiti</translation>
+<translation id="2679714844901977852">IÅ¡saugokite kortelÄ—s ir atsiskaitymo informacijÄ… „Google“ paskyroje <ph name="USER_EMAIL" />, kad galÄ—tumÄ—te saugiai ir sparÄiai atsiskaityti</translation>
<translation id="2684561033061424857">11 x 12</translation>
<translation id="2687555958734450033">Geriausias dydis</translation>
<translation id="2688969097326701645">Taip, tęsti</translation>
@@ -701,7 +708,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="2854764410992194509">Interneto paslaugų teikėjai (IPT)</translation>
<translation id="2856444702002559011">Užpuolikai gali bandyti pavogti jūsų informaciją iš <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (pvz., slaptažodžius, pranešimus ar kredito kortelių duomenis). <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Å ioje svetainÄ—je rodomi nepageidaujami arba klaidinantys skelbimai.</translation>
-<translation id="286512204874376891">Virtuali kortelė maskuoja tikrąją kortelę, kad padėtų apsaugoti jus nuo galimų apgaulių. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Draugiška</translation>
<translation id="28761159517501904">Filmai</translation>
<translation id="2876489322757410363">Išjungiate inkognito režimą, kad galėtumėte sumokėti naudodami išorinę programą. Tęsti?</translation>
@@ -802,7 +808,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="3158539265159265653">Diskas</translation>
<translation id="3162559335345991374">Naudojant šį „Wi-Fi“ tinklą gali būti prašoma apsilankyti prisijungimo puslapyje.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Sala</translation>
<translation id="3176929007561373547">Patikrinkite tarpinio serverio nustatymus arba susisiekite su tinklo
administratoriumi, kad įsitikintumėte, jog tarpinis
serveris veikia. Jei manote, kad tarpinio serverio
@@ -882,9 +887,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="3369192424181595722">Laikrodžio klaida</translation>
<translation id="3369459162151165748">Transporto priemonių dalys ir priedai</translation>
<translation id="3371064404604898522">Nustatyti „Chrome“ kaip numatytąją naršyklę</translation>
-<translation id="3371076217486966826"><ph name="URL" /> nori:
- • kurti jūsų aplinkos 3D žemėlapį ir stebėti kameros padėtį;
- • naudoti kamerą.</translation>
<translation id="337363190475750230">Teikimas nutrauktas</translation>
<translation id="3375754925484257129">Paleisti „Chrome“ saugos patikrą</translation>
<translation id="3377144306166885718">Serveryje naudojama pasenusios versijos TLS.</translation>
@@ -901,6 +903,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="3399952811970034796">Pristatymo adresas</translation>
<translation id="3402261774528610252">Šiai svetainei įkelti naudojamam ryšiui buvo naudojama 1.0 arba 1.1 versijos TLS, kuri nebenaudojama ir ateityje bus išjungta. Išjungus naudotojai nebegalės įkelti šios svetainės. Serveryje reikia įgalinti 1.2 arba naujesnės versijos TLS.</translation>
<translation id="3405664148539009465">Tinkinti Å¡riftus</translation>
+<translation id="3407789382767355356">treÄiosios Å¡alies prisijungimas</translation>
<translation id="3409896703495473338">Tvarkyti saugos nustatymus</translation>
<translation id="3414952576877147120">Dydis:</translation>
<translation id="3417660076059365994">Failai, kuriuos įkeliate ar pridedate, siunÄiami „Google Cloud“ arba treÄiosioms Å¡alims, kad bÅ«tų atlikta analizÄ—. Pavyzdžiui, failai gali bÅ«ti nuskaityti, ar juose nÄ—ra neskelbtinų duomenų ar kenkÄ—jiÅ¡kų programų.</translation>
@@ -933,6 +936,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="3477679029130949506">Filmų sąrašai ir kino teatrų repertuaras</translation>
<translation id="3479552764303398839">Ne dabar</translation>
<translation id="3484560055331845446">Galite prarasti prieigą prie „Google“ paskyros. „Chrome“ rekomenduoja pakeisti slaptažodį dabar. Bus prašoma prisijungti.</translation>
+<translation id="3484861421501147767">Priminimas: yra išsaugotas akcijos kodas</translation>
<translation id="3487845404393360112">4 dÄ—klas</translation>
<translation id="3495081129428749620">Rasti puslapyje
„<ph name="PAGE_TITLE" />“</translation>
@@ -1057,6 +1061,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="3810973564298564668">Tvarkyti</translation>
<translation id="3816482573645936981">VertÄ— (pakeista)</translation>
<translation id="382518646247711829">Jei naudojate tarpinį serverį…</translation>
+<translation id="3826050100957962900">TreÄiosios Å¡alies prisijungimas</translation>
<translation id="3827112369919217609">Absoliutus</translation>
<translation id="3827666161959873541">Å eimai skirti filmai</translation>
<translation id="3828924085048779000">Neleidžiama naudoti tuÅ¡Äios slaptafrazÄ—s.</translation>
@@ -1069,9 +1074,9 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="3858027520442213535">Atnaujinti datÄ… ir laikÄ…</translation>
<translation id="3858860766373142691">Pavadinimas</translation>
<translation id="3872834068356954457">Mokslas</translation>
+<translation id="3875783148670536197">Peržiūrėti, kaip tai padaryti</translation>
<translation id="3881478300875776315">Rodyti mažiau eiluÄių</translation>
<translation id="3884278016824448484">Nesuderinamas įrenginio identifikatorius</translation>
-<translation id="3885155851504623709">Parapija</translation>
<translation id="388632593194507180">Aptiktas stebÄ—jimas</translation>
<translation id="3886948180919384617">3 dÄ—tuvÄ—</translation>
<translation id="3890664840433101773">Pridėti el. pašto adresą</translation>
@@ -1101,9 +1106,11 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="3973234410852337861"><ph name="HOST_NAME" /> užblokuota.</translation>
<translation id="3978338123949022456">Paieškos režimas; įveskite užklausą ir paspauskite „Enter“, jei norite ieškoti naudodami „<ph name="KEYWORD_SUFFIX" />“</translation>
<translation id="398470910934384994">PaukÅ¡Äiai</translation>
+<translation id="3985750352229496475">Tvarkykite adresus...</translation>
<translation id="3986705137476756801">Kol kas išjungti subtitrų realiuoju laiku funkciją</translation>
<translation id="3987940399970879459">Mažiau nei 1 MB</translation>
<translation id="3990250421422698716">Kopijų atskyrimas</translation>
+<translation id="3992684624889376114">Apie šį puslapį</translation>
<translation id="3996311196211510766">Svetainė <ph name="ORIGIN" /> pateikė užklausą taikyti pradinę politiką
visoms jos užklausoms, bet šiuo metu šios politikos taikyti negalima.</translation>
<translation id="4006465311664329701">Mokėjimo metodai, pasiūlymai ir adresai naudojant „Google Pay“</translation>
@@ -1228,6 +1235,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="4305666528087210886">Nepavyko pasiekti failo</translation>
<translation id="4306529830550717874">IÅ¡saugoti adresÄ…?</translation>
<translation id="4306812610847412719">iškarpinė</translation>
+<translation id="4310070645992025887">Ieškoti naudotojo atliekamų veiksmų</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Užblokuoti (numatytoji parinktis)</translation>
<translation id="4314815835985389558">Sinchronizavimo tvarkymas</translation>
@@ -1278,6 +1286,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="4435702339979719576">Atvirukas</translation>
<translation id="443673843213245140">Įgaliotojo serverio naudojimas neleidžiamas, bet nurodyta aiški įgaliotojo serverio konfigūracija.</translation>
<translation id="4441832193888514600">Nepaisoma, nes politika gali būti nustatyta tik kaip debesies naudotojo politika.</translation>
+<translation id="4442470707340296952">„Chrome“ skirtukai</translation>
<translation id="4450893287417543264">Daugiau neberodyti</translation>
<translation id="4451135742916150903">Gali būti prašoma prisijungti prie HID įrenginių</translation>
<translation id="4452328064229197696">Slaptažodis, kurį ką tik naudojote, buvo atskleistas įvykus duomenų saugos pažeidimui. Siekiant užtikrinti paskyrų saugumą, „Google“ slaptažodžių tvarkytuvė rekomenduoja patikrinti išsaugotus slaptažodžius.</translation>
@@ -1416,6 +1425,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="483241715238664915">Įjungti įspėjimus</translation>
<translation id="4834250788637067901">Mokėjimo metodai, pasiūlymai ir adresai naudojant „Google Pay“</translation>
<translation id="4838327282952368871">Svajinga</translation>
+<translation id="4839087176073128681">SparÄiau mokÄ—kite kitÄ… kartÄ… ir apsaugokite kortelÄ™ naudodami geriausias Å¡iame sektoriuje „Google“ saugos priemones.</translation>
<translation id="4840250757394056958">Peržiūrėti „Chrome“ istoriją</translation>
<translation id="484462545196658690">Automatiškai</translation>
<translation id="484671803914931257">Gaukite nuolaidą iš „<ph name="MERCHANT_NAME" />“ ir kt.</translation>
@@ -1478,6 +1488,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="5011561501798487822">Aptikta kalba</translation>
<translation id="5015510746216210676">Įrenginio pavadinimas:</translation>
<translation id="5017554619425969104">Nukopijuotas tekstas</translation>
+<translation id="5017828934289857214">Priminti vÄ—liau</translation>
<translation id="5018422839182700155">Negalima atidaryti Å¡io puslapio</translation>
<translation id="5019198164206649151">Bloga atsarginio atminties įrenginio būsena</translation>
<translation id="5020776957610079374">PasaulinÄ— muzika</translation>
@@ -1497,6 +1508,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="5051305769747448211">Komedija gyvai</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Jei norite siųsti šį failą naudodami funkciją „Bendrinimas netoliese“, atlaisvinkite vietos (<ph name="DISK_SPACE_SIZE" />) įrenginyje}one{Jei norite siųsti šiuos failus naudodami funkciją „Bendrinimas netoliese“, atlaisvinkite vietos (<ph name="DISK_SPACE_SIZE" />) įrenginyje}few{Jei norite siųsti šiuos failus naudodami funkciją „Bendrinimas netoliese“, atlaisvinkite vietos (<ph name="DISK_SPACE_SIZE" />) įrenginyje}many{Jei norite siųsti šiuos failus naudodami funkciją „Bendrinimas netoliese“, atlaisvinkite vietos (<ph name="DISK_SPACE_SIZE" />) įrenginyje}other{Jei norite siųsti šiuos failus naudodami funkciją „Bendrinimas netoliese“, atlaisvinkite vietos (<ph name="DISK_SPACE_SIZE" />) įrenginyje}}</translation>
<translation id="5056549851600133418">Jums skirti straipsniai</translation>
+<translation id="5060483733937416656">Svetainėse, kuriose naudojama <ph name="PROVIDER_ORIGIN" />, pasirinkote patvirtinti naudodami „Windows Hello“. Šis teikėjas galėjo išsaugoti informaciją apie jūsų mokėjimo metodą, ir galite <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">TurÄ—jote omenyje <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Spausdinimo istorija</translation>
<translation id="5068234115460527047">Rizikos draudimo fondai</translation>
@@ -1510,10 +1522,8 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="5087286274860437796">Å iuo metu serverio sertifikatas negalioja.</translation>
<translation id="5087580092889165836">PridÄ—ti kortelÄ™</translation>
<translation id="5088142053160410913">Pranešimas operatoriui</translation>
-<translation id="5089810972385038852">Valstija</translation>
<translation id="5093232627742069661">Z formos perlenkimas</translation>
<translation id="5094747076828555589">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikatas nėra patikimas „Chromium“. Taip gali nutikti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užgrobėjo.</translation>
-<translation id="5095208057601539847">Provincija</translation>
<translation id="5097099694988056070">Įrenginio statistika, pvz., centrinio procesoriaus / laisvosios prieigos atminties naudojimas</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">SvetainÄ— nÄ—ra saugi</translation>
@@ -1551,6 +1561,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="5171045022955879922">Ieškokite ar įveskite URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Įrenginys</translation>
+<translation id="5177076414499237632">Sužinokite apie šio puslapio šaltinį ir temą</translation>
<translation id="5179510805599951267">Ne <ph name="ORIGINAL_LANGUAGE" /> k.? Pranešti apie šią klaidą</translation>
<translation id="518639307526414276">Gyvūnų maistas ir priežiūros priemonės</translation>
<translation id="5190835502935405962">Žymių juosta</translation>
@@ -1711,6 +1722,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="5624120631404540903">Tvarkyti slaptažodžius</translation>
<translation id="5629630648637658800">Įkeliant politikos nustatymus įvyko klaida</translation>
<translation id="5631439013527180824">Netinkamas įrenginio tvarkymo prieigos raktas</translation>
+<translation id="5632485077360054581">Peržiūrėti, kaip tai padaryti</translation>
<translation id="5633066919399395251">Šiuo metu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> užpuolikai gali jūsų kompiuteryje bandyti įdiegti pavojingas programas, kurios vagia arba ištrina informaciją (pvz., nuotraukas, slaptažodžius, pranešimus ir kredito kortelių duomenis). <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Klaidinantis turinys užblokuotas.</translation>
<translation id="5633259641094592098">Kultiniai ir nepriklausomų kūrėjų filmai</translation>
@@ -1828,6 +1840,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="5989320800837274978">Nenurodyti nei fiksuoti įgaliotieji serveriai, nei .pac scenarijaus URL.</translation>
<translation id="5992691462791905444">Neproporcingas Z formos perlenkimas</translation>
<translation id="5995727681868049093">Tvarkykite savo informaciją, privatumo ir saugos nustatymus „Google“ paskyroje</translation>
+<translation id="5997247540087773573">Slaptažodis, kurį ką tik naudojote, buvo atskleistas įvykus duomenų saugos pažeidimui. Siekiant apsaugoti jūsų paskyras, „Google“ slaptažodžių tvarkyklė rekomenduoja pakeisti jį dabar ir patikrinti išsaugotus slaptažodžius.</translation>
<translation id="6000758707621254961">Rezultatų pagal užklausą „<ph name="SEARCH_TEXT" />“: <ph name="RESULT_COUNT" /></translation>
<translation id="6006484371116297560">KlasikinÄ—</translation>
<translation id="6008122969617370890">Tvarka: nuo N iki 1</translation>
@@ -1923,7 +1936,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="627746635834430766">Kad kitÄ… kartÄ… galÄ—tumÄ—te greiÄiau atlikti mokÄ—jimÄ…, iÅ¡saugokite kortelÄ™ ir atsiskaitymo adresÄ… „Google“ paskyroje.</translation>
<translation id="6279183038361895380">Paspauskite |<ph name="ACCELERATOR" />|, kad būtų rodomas žymeklis</translation>
<translation id="6280223929691119688">Negalima pristatyti Å¡iuo adresu. Pasirinkite kitÄ… adresÄ….</translation>
-<translation id="6282194474023008486">Pašto kodas</translation>
<translation id="6285507000506177184">Mygtukas „Tvarkyti atsisiuntimus naršyklėje „Chrome“; paspauskite „Enter“, jei norite tvarkyti failus, kuriuos atsisiuntėte naršyklėje „Chrome“</translation>
<translation id="6289939620939689042">Puslapio spalva</translation>
<translation id="6290238015253830360">JÅ«sų pasiÅ«lyti straipsniai rodomi Äia</translation>
@@ -1945,6 +1957,7 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="6337133576188860026">Atlaisvina mažiau nei <ph name="SIZE" />. Per kitÄ… jÅ«sų apsilankymÄ… kai kurios svetainÄ—s gali bÅ«ti įkeliamos lÄ—Äiau.</translation>
<translation id="6337534724793800597">Filtruoti politikÄ… pagal pavadinimÄ…</translation>
<translation id="6340739886198108203">Pagal administratoriaus politikos nuostatas nerekomenduojama daryti ekrano kopijų arba įrašyti, kai matomas konfidencialus turinys.</translation>
+<translation id="6348220984832452017">Aktyvūs variantai</translation>
<translation id="6349101878882523185">Įdiegti <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Nėra}=1{1 slaptažodis (skirta <ph name="DOMAIN_LIST" />, sinchronizuojama)}=2{2 slaptažodžiai (skirta <ph name="DOMAIN_LIST" />, sinchronizuojama)}one{# slaptažodis (skirta <ph name="DOMAIN_LIST" />, sinchronizuojama)}few{# slaptažodžiai (skirta <ph name="DOMAIN_LIST" />, sinchronizuojama)}many{# slaptažodžio (skirta <ph name="DOMAIN_LIST" />, sinchronizuojama)}other{# slaptažodžių (skirta <ph name="DOMAIN_LIST" />, sinchronizuojama)}}</translation>
<translation id="6355392890578844978">Šios naršyklės netvarko įmonė ar kita organizacija. Veiklą šiame įrenginyje galima tvarkyti ne naršyklėje „Chromium“. <ph name="BEGIN_LINK" />Sužinokite daugiau<ph name="END_LINK" /></translation>
@@ -1976,7 +1989,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="643051589346665201">Keisti „Google“ slaptažodį</translation>
<translation id="6433490469411711332">KontaktinÄ—s informacijos redagavimas</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> atsisakÄ— prisijungti.</translation>
-<translation id="6438025220577812695">Pakeisti patiems</translation>
<translation id="6440503408713884761">Nepaisoma</translation>
<translation id="6443406338865242315">Kuriuos plėtinius ir papildinius įdiegėte</translation>
<translation id="6446163441502663861">„Kahu“ (vokas)</translation>
@@ -2106,9 +2118,9 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">VertÄ—jas</translation>
<translation id="6833752742582340615">IÅ¡saugokite kortelÄ—s ir atsiskaitymo informacijÄ… „Google“ paskyroje, kad galÄ—tumÄ—te saugiai ir sparÄiai atsiskaityti</translation>
-<translation id="6839929833149231406">Sritis</translation>
<translation id="6846340164947227603">Naudoti virtualų kortelės numerį...</translation>
<translation id="6852204201400771460">Įkelti programą iš naujo?</translation>
+<translation id="6857776781123259569">Tvarkykite slaptažodžius...</translation>
<translation id="686485648936420384">Klientų ištekliai</translation>
<translation id="6865412394715372076">Å iuo metu kortelÄ—s patvirtinti nepavyksta</translation>
<translation id="6869334554832814367">AsmeninÄ—s paskolos</translation>
@@ -2157,7 +2169,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="6965978654500191972">Įrenginys</translation>
<translation id="696703987787944103">Subjektyvusis</translation>
<translation id="6968269510885595029">Naudokite saugos raktÄ…</translation>
-<translation id="6970216967273061347">Rajonas</translation>
<translation id="6971439137020188025">Greitai sukurkite naują „Google“ pristatymą Skaidrėse</translation>
<translation id="6972629891077993081">HID įrenginiai</translation>
<translation id="6973656660372572881">Nurodyti fiksuoti įgaliotieji serveriai ir .pac scenarijaus URL.</translation>
@@ -2196,7 +2207,6 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<translation id="7081308185095828845">Ši funkcija jūsų įrenginyje nepasiekiama</translation>
<translation id="7083258188081898530">9 dÄ—klas</translation>
<translation id="7086090958708083563">Naudotojo pateikta įkėlimo užklausa</translation>
-<translation id="7087282848513945231">Apskritis</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />; paspauskite tabuliavimo klavišą, tada „Enter“, jei norite tvarkyti leidimus ir svetainėse saugomus duomenis „Chrome“ nustatymuose</translation>
<translation id="7096937462164235847">Å ios svetainÄ—s tapatybÄ— nepatvirtinta.</translation>
<translation id="7101893872976785596">Siaubo filmai</translation>
@@ -2215,10 +2225,10 @@ Kitu atveju tai bus užblokuota pagal jūsų privatumo nustatymus. Taip turinys,
<ph name="LIST_ITEM" />Formose įvesta informacija<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Negalima pristatyti Å¡iuo adresu. Pasirinkite kitÄ… adresÄ….</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Jūsų administratorius uždraudė kopijuoti šiuos duomenis.</translation>
<translation id="7135130955892390533">Rodyti būseną</translation>
<translation id="7138472120740807366">Pristatymo metodas</translation>
-<translation id="7139724024395191329">Emyratas</translation>
<translation id="7139892792842608322">Pagrindinis dÄ—klas</translation>
<translation id="714064300541049402">2 pusė: sukti vaizdą pagal X ašį</translation>
<translation id="7152423860607593928">„Number-14“ (vokas)</translation>
@@ -2435,6 +2445,7 @@ Papildoma išsami informacija:
<translation id="7669271284792375604">Å ios svetainÄ—s užgrobÄ—jai gali bandyti apgaule priversti jus įdiegti narÅ¡ymo funkcijas trikdanÄių programų (pvz., pakeitÄ™ pagrindinį puslapį ar rodydami papildomų skelbimų svetainÄ—se, kuriose lankotÄ—s).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Veiksmai, atlikti su duomenimis, kurie pažymėti kaip konfidencialūs (1 veiksmas nuo tada, kai buvo prisijungta). <ph name="BEGIN_LINK" />Sužinokite daugiau<ph name="END_LINK" />}one{Veiksmai, atlikti su duomenimis, kurie pažymėti kaip konfidencialūs (# veiksmas nuo tada, kai buvo prisijungta). <ph name="BEGIN_LINK" />Sužinokite daugiau<ph name="END_LINK" />}few{Veiksmai, atlikti su duomenimis, kurie pažymėti kaip konfidencialūs (# veiksmai nuo tada, kai buvo prisijungta). <ph name="BEGIN_LINK" />Sužinokite daugiau<ph name="END_LINK" />}many{Veiksmai, atlikti su duomenimis, kurie pažymėti kaip konfidencialūs (# veiksmo nuo tada, kai buvo prisijungta). <ph name="BEGIN_LINK" />Sužinokite daugiau<ph name="END_LINK" />}other{Veiksmai, atlikti su duomenimis, kurie pažymėti kaip konfidencialūs (# veiksmų nuo tada, kai buvo prisijungta). <ph name="BEGIN_LINK" />Sužinokite daugiau<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">6 pašto dėžutė</translation>
+<translation id="7675325315208090829">Tvarkykite mokÄ—jimo metodus...</translation>
<translation id="7676643023259824263">Ieškoti iškarpinės teksto „<ph name="TEXT" />“</translation>
<translation id="7679367271685653708">Peržiūrėkite ir tvarkykite naršymo istoriją „Chrome“ nustatymuose</translation>
<translation id="7679947978757153706">Beisbolas</translation>
@@ -2477,7 +2488,6 @@ Papildoma išsami informacija:
<translation id="7766518757692125295">Pakraštys</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Ta paÄia tvarka, gerÄ…ja puse į viršų</translation>
-<translation id="777702478322588152">Prefektūra</translation>
<translation id="7791011319128895129">Neišleista</translation>
<translation id="7791196057686275387">Pakavimas</translation>
<translation id="7791543448312431591">PridÄ—ti</translation>
@@ -2568,12 +2578,12 @@ Papildoma išsami informacija:
<translation id="8055534648776115597">Profesinis ir tęstinis mokymasis</translation>
<translation id="8057711352706143257">„<ph name="SOFTWARE_NAME" />“ netinkamai sukonfigūruota. Pašalinus „<ph name="SOFTWARE_NAME" />“ paprastai pavyksta išspręsti šią problemą. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Maisto gamyba</translation>
-<translation id="8066955247577885446">Deja, kažkas nepavyko.</translation>
<translation id="8067872629359326442">Ką tik savo slaptažodį įvedėte apgaulingoje svetainėje. „Chromium“ gali padėti. Norėdami pakeisti slaptažodį ir pranešti „Google“, kad jūsų paskyrai gali grėsti pavojus, spustelėkite „Apsaugoti paskyrą“.</translation>
<translation id="8070439594494267500">Programos piktograma</translation>
<translation id="8074253406171541171">10 x 13 (vokas)</translation>
<translation id="8075736640322370409">Greitai sukurkite naujÄ… „Google“ skaiÄiuoklÄ™</translation>
<translation id="8075898834294118863">Tvarkyti svetainių nustatymus</translation>
+<translation id="8076492880354921740">Skirtukai</translation>
<translation id="8078141288243656252">Negalima rašyti komentarų, kai pasukta</translation>
<translation id="8079031581361219619">Iš naujo įkelti svetainę?</translation>
<translation id="8081087320434522107">Sedanai</translation>
@@ -2696,6 +2706,7 @@ Papildoma išsami informacija:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Nustatymai</translation>
+<translation id="8428634594422941299">Supratau</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />; paspauskite tabuliavimo klavišą, tada „Enter“, jei norite tvarkyti slapukų nuostatas „Chrome“ nustatymuose</translation>
<translation id="8433057134996913067">Tai atlikę atsijungsite nuo daugumos svetainių.</translation>
<translation id="8434840396568290395">Naminiai gyvūnai</translation>
@@ -2794,6 +2805,7 @@ Papildoma išsami informacija:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> yra kodas, skirtas svetainei <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Žymėti šį skirtuką</translation>
<translation id="8751426954251315517">VÄ—liau bandykite dar kartÄ…</translation>
+<translation id="8757526089434340176">Pasiekiamas „Google Pay“ pasiūlymas</translation>
<translation id="8758885506338294482">Konkurenciniai vaizdo žaidimai</translation>
<translation id="8759274551635299824">Å i kortelÄ— nebegalioja</translation>
<translation id="87601671197631245">Å ioje svetainÄ—je naudojama pasenusi saugos konfigÅ«racija, todÄ—l gali bÅ«ti atskleista į Å¡iÄ… svetainÄ™ siunÄiama informacija (pvz., slaptažodžiai, praneÅ¡imai ar kredito kortelių duomenys).</translation>
@@ -2801,6 +2813,7 @@ Papildoma išsami informacija:
<translation id="8763927697961133303">USB įrenginys</translation>
<translation id="8763986294015493060">Uždaryti visus šiuo metu atidarytus inkognito langus</translation>
<translation id="8766943070169463815">Saugaus mokėjimo prisijungimo duomenų autentifikavimo lapas atidarytas</translation>
+<translation id="8767765348545497220">Uždaryti pagalbos debesėlį</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motociklai</translation>
<translation id="8790007591277257123">&amp;IÅ¡trinti dar kartÄ…</translation>
@@ -2813,6 +2826,7 @@ Papildoma išsami informacija:
<translation id="8806285662264631610">Vonios ir kūno priežiūros produktai</translation>
<translation id="8807160976559152894">Apkarpymas po kiekvieno puslapio</translation>
<translation id="8808828119384186784">„Chrome“ nustatymai</translation>
+<translation id="8813277370772331957">Priminti vÄ—liau</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, paspauskite tabuliavimo klavišą, tada – „Enter“, kad „Chrome“ nustatymuose būtų atnaujinta naršyklė „Chrome“</translation>
<translation id="8820817407110198400">Žymės</translation>
<translation id="882338992931677877">NeautomatinÄ— dÄ—tuvÄ—</translation>
@@ -2992,6 +3006,7 @@ Papildoma išsami informacija:
<translation id="988159990683914416">Vykdymo programa sukurta</translation>
<translation id="989988560359834682">Adreso redagavimas</translation>
<translation id="991413375315957741">judesio arba Å¡viesos jutikliai</translation>
+<translation id="992110854164447044">Virtuali kortelė slepia tikrąją kortelę, kad padėtų apsaugoti jus nuo galimų apgaulių. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rožinė</translation>
<translation id="992432478773561401">„<ph name="SOFTWARE_NAME" />“ nebuvo tinkamai įdiegta kompiuteryje ar tinkle.
diff --git a/chromium/components/strings/components_strings_lv.xtb b/chromium/components/strings/components_strings_lv.xtb
index 2a6d3cfaf0a..76861b4037e 100644
--- a/chromium/components/strings/components_strings_lv.xtb
+++ b/chromium/components/strings/components_strings_lv.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Stipendijas un finansiÄla palÄ«dzÄ«ba</translation>
<translation id="1048785276086539861">Ja rediģēsiet piezÄ«mes, dokuments atkal pÄries vienas lapas skatÄ.</translation>
<translation id="1050038467049342496">Aizveriet citas lietotnes</translation>
+<translation id="1053959602163383901">JÅ«s izvÄ“lÄ“jÄties veikt verifikÄciju ar autentifikatora ierÄ«ci vietnÄ“s, kas izmanto <ph name="PROVIDER_ORIGIN" /> pakalpojumus. Å is pakalpojumu sniedzÄ“js, iespÄ“jams, ir saglabÄjis informÄciju par jÅ«su maksÄjuma veidu, un jÅ«s varat <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Pievienošanas atsaukšana</translation>
<translation id="1056663316309890343">FotoattÄ“lu apstrÄdes programmatÅ«ra</translation>
<translation id="1056898198331236512">BrÄ«dinÄjums</translation>
@@ -119,6 +120,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="1270502636509132238">Saņemšanas veids</translation>
<translation id="1281476433249504884">1. izvades vieta</translation>
<translation id="1285320974508926690">Nekad netulkot Å¡o vietni</translation>
+<translation id="1288548991597756084">DroÅ¡i saglabÄjiet karti</translation>
<translation id="1292571435393770077">16. paplÄte</translation>
<translation id="1292701964462482250">“ProgrammatÅ«ra jÅ«su datorÄ, kuras dēļ pÄrlÅ«kÄ Chrome nevar izveidot droÅ¡u tÄ«mekļa savienojumu†(tikai Windows datoros)</translation>
<translation id="1294154142200295408">Komandrindu varianti</translation>
@@ -223,6 +225,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
&lt;p&gt;Lai novÄ“rstu Å¡o kļūdu, lapÄ, kuru mÄ“Ä£inÄt atvÄ“rt, noklikÅ¡Ä·iniet uz &lt;strong&gt;Izveidot savienojumu&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Ainavu arhitektūra</translation>
<translation id="1513706915089223971">VÄ“stures ierakstu saraksts</translation>
+<translation id="1516097932025103760">Karte tiks Å¡ifrÄ“ta, droÅ¡i saglabÄta, un CVC kods nekad netiks saglabÄts.</translation>
<translation id="1517433312004943670">JÄnorÄda tÄlruņa numurs</translation>
<translation id="1519264250979466059">Būvējuma datums</translation>
<translation id="1521159554480556801">Å Ä·iedras un tekstila mÄksla</translation>
@@ -288,6 +291,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="1662550410081243962">SaglabÄt un aizpildÄ«t maksÄjuma veidus</translation>
<translation id="1663943134801823270">Kartes un adreses tiek iegÅ«tas no Chrome. Varat pÄrvaldÄ«t tÄs <ph name="BEGIN_LINK" />iestatÄ«jumos<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">TurpmÄk lapas, kas bÅ«s Å¡ÄdÄ valodÄ: <ph name="SOURCE_LANGUAGE" />, tiks tulkotas Å¡ÄdÄ valodÄ: <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">NorÄ“Ä·inieties ar karti <ph name="CARD_DETAIL" />, lai izmantotu piedÄvÄjumu.</translation>
<translation id="1674504678466460478">No <ph name="SOURCE_LANGUAGE" /> uz <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Vispirms Ä«sÄkÄ mala</translation>
<translation id="168693727862418163">Å o politikas vÄ“rtÄ«bu neizdevÄs apstiprinÄt, izmantojot tÄs shÄ“mu, un tÄ tiks ignorÄ“ta.</translation>
@@ -306,6 +310,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="1717218214683051432">Kustību sensori</translation>
<translation id="1717494416764505390">3. pastkaste</translation>
<translation id="1718029547804390981">Dokuments ir pÄrÄk liels, tÄdēļ tam nevar pievienot piezÄ«mes</translation>
+<translation id="1720941539803966190">AizvÄ“rt mÄcÄ«bu burbuli</translation>
<translation id="1721424275792716183">* ObligÄts lauks</translation>
<translation id="1727613060316725209">SertifikÄts ir derÄ«gs</translation>
<translation id="1727741090716970331">Derīga kartes numura pievienošana</translation>
@@ -422,8 +427,8 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="205212645995975601">Barbekjū un grilēšana</translation>
<translation id="2053111141626950936">Lapas netiks tulkotas no Å¡Ä«s valodas: <ph name="LANGUAGE" /></translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Ja Å¡Ä« vadÄ«kla ir ieslÄ“gta un statuss ir aktÄ«vs, pÄrlÅ«kÄ Chrome tiek noteikts, kurai lielai lietotÄju grupai jeb kohortai jÅ«su nesenÄs pÄrlÅ«koÅ¡anas darbÄ«bas lÄ«dzinÄs visvairÄk. ReklÄmdevÄ“ji var atlasÄ«t grupai atbilstoÅ¡as reklÄmas, bet jÅ«su pÄrlÅ«koÅ¡anas darbÄ«bas tiek privÄti glabÄtas jÅ«su ierÄ«cÄ“. JÅ«su grupas dati tiek atjauninÄti katru dienu.}=1{Ja Å¡Ä« vadÄ«kla ir ieslÄ“gta un statuss ir aktÄ«vs, pÄrlÅ«kÄ Chrome tiek noteikts, kurai lielai lietotÄju grupai jeb kohortai jÅ«su nesenÄs pÄrlÅ«koÅ¡anas darbÄ«bas lÄ«dzinÄs visvairÄk. ReklÄmdevÄ“ji var atlasÄ«t grupai atbilstoÅ¡as reklÄmas, bet jÅ«su pÄrlÅ«koÅ¡anas darbÄ«bas tiek privÄti glabÄtas jÅ«su ierÄ«cÄ“. JÅ«su grupas dati tiek atjauninÄti katru dienu.}zero{Ja Å¡Ä« vadÄ«kla ir ieslÄ“gta un statuss ir aktÄ«vs, pÄrlÅ«kÄ Chrome tiek noteikts, kurai lielai lietotÄju grupai jeb kohortai jÅ«su nesenÄs pÄrlÅ«koÅ¡anas darbÄ«bas lÄ«dzinÄs visvairÄk. ReklÄmdevÄ“ji var atlasÄ«t grupai atbilstoÅ¡as reklÄmas, bet jÅ«su pÄrlÅ«koÅ¡anas darbÄ«bas tiek privÄti glabÄtas jÅ«su ierÄ«cÄ“. JÅ«su grupas dati tiek atjauninÄti ik pÄ“c {NUM_DAYS} dienÄm.}one{Ja Å¡Ä« vadÄ«kla ir ieslÄ“gta un statuss ir aktÄ«vs, pÄrlÅ«kÄ Chrome tiek noteikts, kurai lielai lietotÄju grupai jeb kohortai jÅ«su nesenÄs pÄrlÅ«koÅ¡anas darbÄ«bas lÄ«dzinÄs visvairÄk. ReklÄmdevÄ“ji var atlasÄ«t grupai atbilstoÅ¡as reklÄmas, bet jÅ«su pÄrlÅ«koÅ¡anas darbÄ«bas tiek privÄti glabÄtas jÅ«su ierÄ«cÄ“. JÅ«su grupas dati tiek atjauninÄti ik pÄ“c {NUM_DAYS} dienÄm.}other{Ja Å¡Ä« vadÄ«kla ir ieslÄ“gta un statuss ir aktÄ«vs, pÄrlÅ«kÄ Chrome tiek noteikts, kurai lielai lietotÄju grupai jeb kohortai jÅ«su nesenÄs pÄrlÅ«koÅ¡anas darbÄ«bas lÄ«dzinÄs visvairÄk. ReklÄmdevÄ“ji var atlasÄ«t grupai atbilstoÅ¡as reklÄmas, bet jÅ«su pÄrlÅ«koÅ¡anas darbÄ«bas tiek privÄti glabÄtas jÅ«su ierÄ«cÄ“. JÅ«su grupas dati tiek atjauninÄti ik pÄ“c {NUM_DAYS} dienÄm.}}</translation>
-<translation id="2053553514270667976">Pasta indekss</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 ieteikums}zero{# ieteikumi}one{# ieteikums}other{# ieteikumi}}</translation>
+<translation id="2066915425250589881">pieprasÄ«t tÄs dzÄ“Å¡anu</translation>
<translation id="2068528718802935086">Zīdaiņi un mazi bērni</translation>
<translation id="2071156619270205202">Å Ä« karte neatbilst virtuÄlÄs kartes numuram.</translation>
<translation id="2071692954027939183">Paziņojumi tika automÄtiski bloÄ·Ä“ti, jo jÅ«s tos parasti neatļaujat.</translation>
@@ -435,7 +440,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="2088086323192747268">Poga “PÄrvaldÄ«t sinhronizÄcijuâ€: nospiediet ievadÄ«Å¡anas taustiņu, lai Chrome iestatÄ«jumos pÄrvaldÄ«tu, kÄda informÄcija tiek sinhronizÄ“ta.</translation>
<translation id="2091887806945687916">SignÄls</translation>
<translation id="2094505752054353250">Domēni nesaskan</translation>
-<translation id="2096368010154057602">Departaments</translation>
<translation id="2099652385553570808">TrÄ«skÄrÅ¡s skavojums kreisajÄ pusÄ“</translation>
<translation id="2101225219012730419">versija:</translation>
<translation id="2102134110707549001">Ieteikt drošu paroli…</translation>
@@ -472,7 +476,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="2185836064961771414">AmerikÄņu futbols</translation>
<translation id="2187317261103489799">Noteikt (pēc noklusējuma)</translation>
<translation id="2188375229972301266">VairÄki caurumi apakÅ¡daļÄ</translation>
-<translation id="2188852899391513400">Nupat izmantotÄ parole ir atklÄta datu aizsardzÄ«bas pÄrkÄpuma dēļ. Lai aizsargÄtu kontus, Google paroļu pÄrvaldnieks iesaka nekavÄ“joties to nomainÄ«t un pÄ“c tam pÄrbaudÄ«t saglabÄtÄs paroles.</translation>
<translation id="219906046732893612">MÄjas labiekÄrtoÅ¡ana</translation>
<translation id="2202020181578195191">Ievadiet derīgu gadu</translation>
<translation id="22081806969704220">3. paplÄte</translation>
@@ -483,6 +486,8 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="2215727959747642672">Faila rediģēšana</translation>
<translation id="2215963164070968490">Suņi</translation>
<translation id="2218879909401188352">UzbrucÄ“ji vietnÄ“ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> varÄ“tu instalÄ“t bÄ«stamas lietotnes, kas bojÄ jÅ«su ierÄ«ci, rada slÄ“ptas izmaksas mobilÄ tÄlruņa rÄ“Ä·inÄ vai zog jÅ«su personas informÄciju. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2219658597883514593">AtsÄkt pamÄcÄ«bu</translation>
+<translation id="2219735899272417925">jÄveic ierÄ«ces atiestatÄ«Å¡ana</translation>
<translation id="2224337661447660594">Nav interneta savienojuma</translation>
<translation id="2230458221926704099">Labojiet savienojumu, izmantojot <ph name="BEGIN_LINK" />diagnostikas lietotni<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Sūtīt tūlīt</translation>
@@ -580,11 +585,13 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="2512101340618156538">Atļauja nav piešķirta (noklusējums)</translation>
<translation id="2512413427717747692">Poga “IestatÄ«t Chrome kÄ noklusÄ“juma pÄrlÅ«kuâ€; nospiediet taustiņu Enter, lai iestatÄ«tu Chrome kÄ sistÄ“mas noklusÄ“juma pÄrlÅ«ku iOS iestatÄ«jumos.</translation>
<translation id="2515629240566999685">PÄrbaudiet signÄlu savÄ apkaimÄ“.</translation>
+<translation id="2515761554693942801">JÅ«s izvÄ“lÄ“jÄties veikt verifikÄciju ar Touch ID tÄ«mekļa vietnÄ“s, kas izmanto <ph name="PROVIDER_ORIGIN" /> pakalpojumus. Å is pakalpojumu sniedzÄ“js, iespÄ“jams, ir saglabÄjis informÄciju par jÅ«su maksÄjuma veidu, un jÅ«s varat <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Skavojums apakÅ¡Ä“jÄ labajÄ malÄ</translation>
<translation id="2521736961081452453">Izveidot veidlapu</translation>
<translation id="2523886232349826891">Tiks saglabÄta tikai Å¡ajÄ ierÄ«cÄ“</translation>
<translation id="2524461107774643265">Papildu informÄcijas pievienoÅ¡ana</translation>
<translation id="2529899080962247600">Å ajÄ laukÄ drÄ«kst bÅ«t ne vairÄk kÄ <ph name="MAX_ITEMS_LIMIT" /> ierakstu. Visi turpmÄkie ieraksti tiks ignorÄ“ti.</translation>
+<translation id="253493526287553278">SkatÄ«t akcijas koda informÄciju</translation>
<translation id="2535585790302968248">AtvÄ“rt jaunu inkognito cilni privÄtai pÄrlÅ«koÅ¡anai</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{un vēl 1}zero{un vēl #}one{un vēl #}other{un vēl #}}</translation>
<translation id="2536110899380797252">Pievienot adresi</translation>
@@ -620,7 +627,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="259821504105826686">FotogrÄfija un digitÄlÄ mÄksla</translation>
<translation id="2601150049980261779">RomantiskÄs filmas</translation>
<translation id="2604589665489080024">Popmūzika</translation>
-<translation id="2609632851001447353">Varianti</translation>
<translation id="2610561535971892504">NoklikÅ¡Ä·inÄt, lai kopÄ“tu</translation>
<translation id="2617988307566202237">PÄrlÅ«kÄ Chrome <ph name="BEGIN_EMPHASIS" />netiks saglabÄta<ph name="END_EMPHASIS" /> Å¡Äda informÄcija:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">DzimÅ¡anas un vÄrda dienas</translation>
<translation id="2677748264148917807">Iziet</translation>
+<translation id="2679714844901977852">SaglabÄjiet savu kartes un norÄ“Ä·inu informÄciju Google kontÄ (<ph name="USER_EMAIL" />) droÅ¡iem un ÄtrÄkiem norÄ“Ä·iniem.</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">OptimÄla ietilpinÄÅ¡ana</translation>
<translation id="2688969097326701645">JÄ, turpinÄt</translation>
@@ -701,7 +708,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="2854764410992194509">Interneta pakalpojumu sniedzēji</translation>
<translation id="2856444702002559011">IespÄ“jams, uzbrucÄ“ji mÄ“Ä£ina nozagt jÅ«su informÄciju no vietnes <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (piemÄ“ram, paroles, ziņojumus vai kredÄ«tkarÅ¡u datus). <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2859806420264540918">Å ajÄ vietnÄ“ tiek rÄdÄ«tas traucÄ“joÅ¡as vai maldinoÅ¡as reklÄmas.</translation>
-<translation id="286512204874376891">VirtuÄla karte nomaskÄ“ jÅ«su patieso karti, lai efektÄ«vÄk aizsargÄtu jÅ«s pret iespÄ“jamu krÄpÅ¡anu. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Draudzīga</translation>
<translation id="28761159517501904">Filmas</translation>
<translation id="2876489322757410363">Ja maksÄÅ¡anai tiks izmantota ÄrÄ“ja lietojumprogramma, tiks aizvÄ“rts inkognito režīms. Vai turpinÄt?</translation>
@@ -802,7 +808,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="3158539265159265653">Disks</translation>
<translation id="3162559335345991374">IespÄ“jams, izmantotajÄ Wi-Fi tÄ«klÄ tiks pieprasÄ«ts apmeklÄ“t pieteikÅ¡anÄs lapu.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Sala</translation>
<translation id="3176929007561373547">PÄrbaudiet starpniekservera iestatÄ«jumus vai sazinieties ar tÄ«kla administratoru, lai
pÄrliecinÄtos, vai starpniekserveris darbojas. Ja uzskatÄt, ka jums nav jÄizmanto
starpniekserveris,
@@ -881,9 +886,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="3369192424181595722">Pulksteņa kļūda</translation>
<translation id="3369459162151165748">Transportlīdzekļu detaļas un piederumi</translation>
<translation id="3371064404604898522">IestatÄ«t Chrome kÄ noklusÄ“juma pÄrlÅ«ku</translation>
-<translation id="3371076217486966826"><ph name="URL" /> vēlas:
- • izveidot jÅ«su apkÄrtnes 3D karti un izsekot kameras pozÄ«ciju;
- • izmantot jūsu kameru.</translation>
<translation id="337363190475750230">Tika noņemta piekļuve</translation>
<translation id="3375754925484257129">Veikt Chrome droÅ¡Ä«bas pÄrbaudi</translation>
<translation id="3377144306166885718">Serveris izmantoja novecojušu TLS versiju.</translation>
@@ -900,6 +902,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="3399952811970034796">PiegÄdes adrese</translation>
<translation id="3402261774528610252">Å Ä«s vietnes ielÄdes savienojumam tika izmantots protokols TLS 1.0 vai TLS 1.1. Å Ä«s versijas ir novecojuÅ¡as un vÄ“lÄk tiks atspÄ“jotas. Kad tÄs bÅ«s atspÄ“jotas, lietotÄji nevarÄ“s ielÄdÄ“t Å¡o vietni. Serverim ir jÄiespÄ“jo protokols TLS 1.2 vai jaunÄka versija.</translation>
<translation id="3405664148539009465">PielÄgot fontus</translation>
+<translation id="3407789382767355356">treÅ¡Äs puses pierakstÄ«Å¡anÄs</translation>
<translation id="3409896703495473338">PÄrvaldÄ«t droÅ¡Ä«bas iestatÄ«jumus</translation>
<translation id="3414952576877147120">Lielums:</translation>
<translation id="3417660076059365994">JÅ«su augÅ¡upielÄdÄ“tie vai pievienotie faili tiek nosÅ«tÄ«ti uz Google mÄkoni vai treÅ¡ajÄm pusÄ“m, lai veiktu analÄ«zi. Å ie faili var tikt pÄrmeklÄ“ti, lai konstatÄ“tu, piemÄ“ram, vai tajos ir ietverti sensitÄ«vi dati vai ļaunprÄtÄ«ga programmatÅ«ra.</translation>
@@ -932,6 +935,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="3477679029130949506">Filmu seansi un teÄtru izrÄžu laiki</translation>
<translation id="3479552764303398839">VÄ“lÄk</translation>
<translation id="3484560055331845446">Varat zaudēt piekļuvi savam Google kontam. Chrome iesaka nekavējoties nomainīt paroli. Jums tiks lūgts pierakstīties.</translation>
+<translation id="3484861421501147767">AtgÄdinÄjums: saglabÄtais akcijas kods ir pieejams</translation>
<translation id="3487845404393360112">4. paplÄte</translation>
<translation id="3495081129428749620">MeklÄ“t lapÄ
<ph name="PAGE_TITLE" /></translation>
@@ -1055,6 +1059,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="3810973564298564668">PÄrvaldÄ«t</translation>
<translation id="3816482573645936981">VÄ“rtÄ«ba (aizstÄta)</translation>
<translation id="382518646247711829">Ja izmantojat starpniekserveri...</translation>
+<translation id="3826050100957962900">TreÅ¡Äs puses pierakstÄ«Å¡anÄs</translation>
<translation id="3827112369919217609">PilnÄ«ga precizitÄte</translation>
<translation id="3827666161959873541">Ä¢imenes filmas</translation>
<translation id="3828924085048779000">TukÅ¡a ieejas frÄze nav atļauta.</translation>
@@ -1067,9 +1072,9 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="3858027520442213535">AtjauninÄt datumu un laiku</translation>
<translation id="3858860766373142691">Nosaukums</translation>
<translation id="3872834068356954457">ZinÄtne</translation>
+<translation id="3875783148670536197">ParÄdÄ«t, kÄ</translation>
<translation id="3881478300875776315">RÄdÄ«t mazÄk rindu</translation>
<translation id="3884278016824448484">Ierīces identifikators rada konfliktu.</translation>
-<translation id="3885155851504623709">Pagasts</translation>
<translation id="388632593194507180">KonstatÄ“ta pÄrraudzÄ«ba</translation>
<translation id="3886948180919384617">3. izvades vieta</translation>
<translation id="3890664840433101773">E-pasta adreses pievienošana</translation>
@@ -1099,9 +1104,11 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="3973234410852337861">Vietne <ph name="HOST_NAME" /> ir bloÄ·Ä“ta</translation>
<translation id="3978338123949022456">MeklÄ“Å¡anas režīms; ierakstiet vaicÄjumu un nospiediet taustiņu Enter, lai veiktu meklÄ“Å¡anu pakalpojumÄ <ph name="KEYWORD_SUFFIX" />.</translation>
<translation id="398470910934384994">Putni</translation>
+<translation id="3985750352229496475">PÄrvaldÄ«t adreses…</translation>
<translation id="3986705137476756801">PagaidÄm izslÄ“gt subtitrus reÄllaikÄ</translation>
<translation id="3987940399970879459">MazÄk nekÄ 1 MB</translation>
<translation id="3990250421422698716">Ofseta izlÄ«dzinÄÅ¡ana</translation>
+<translation id="3992684624889376114">Par Å¡o lapu</translation>
<translation id="3996311196211510766">Vietne <ph name="ORIGIN" /> ir pieprasījusi, lai izcelsmes politika attiektos uz visu
tÄs pieprasÄ«jumu, taÄu Å¡obrÄ«d Å¡o politiku nevar piemÄ“rot.</translation>
<translation id="4006465311664329701">MaksÄjumu veidi, piedÄvÄjumi un adreses no pakalpojuma Google Pay</translation>
@@ -1226,6 +1233,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="4305666528087210886">Jūsu failam nevarēja piekļūt</translation>
<translation id="4306529830550717874">Vai saglabÄt adresi?</translation>
<translation id="4306812610847412719">starpliktuve</translation>
+<translation id="4310070645992025887">Meklēt jūsu ceļos</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloķēt (pēc noklusējuma)</translation>
<translation id="4314815835985389558">SinhronizÄcijas pÄrvaldÄ«ba</translation>
@@ -1276,6 +1284,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="4435702339979719576">Pastkarte)</translation>
<translation id="443673843213245140">Starpniekservera lietoÅ¡ana ir atspÄ“jota, bet ir norÄdÄ«ta atklÄta starpniekservera konfigurÄcija.</translation>
<translation id="4441832193888514600">IgnorÄ“ta, jo Å¡o politiku var iestatÄ«t tikai kÄ mÄkoņa lietotÄja politiku.</translation>
+<translation id="4442470707340296952">Chrome cilnes</translation>
<translation id="4450893287417543264">Vairs nerÄdÄ«t</translation>
<translation id="4451135742916150903">Var pieprasīt atļauju veidot savienojumu ar HID ierīcēm</translation>
<translation id="4452328064229197696">Nupat izmantotÄ parole ir atklÄta datu aizsardzÄ«bas pÄrkÄpuma dēļ. Lai aizsargÄtu kontus, Google paroļu pÄrvaldnieks iesaka pÄrbaudÄ«t saglabÄtÄs paroles.</translation>
@@ -1414,6 +1423,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="483241715238664915">IeslÄ“gt brÄ«dinÄjumus</translation>
<translation id="4834250788637067901">MaksÄjumu veidi, piedÄvÄjumi un adreses no pakalpojuma Google Pay</translation>
<translation id="4838327282952368871">Mierīga</translation>
+<translation id="4839087176073128681">NÄkamreiz maksÄjiet ÄtrÄk un aizsargÄjiet savu karti ar nozarÄ“ vadoÅ¡o Google droÅ¡Ä«bas risinÄjumu.</translation>
<translation id="4840250757394056958">Skatīt Chrome vēsturi</translation>
<translation id="484462545196658690">Autom.</translation>
<translation id="484671803914931257">Saņemiet atlaides, iepÄ“rkoties pie <ph name="MERCHANT_NAME" /> un citiem tirgotÄjiem</translation>
@@ -1476,6 +1486,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="5011561501798487822">NoteiktÄ valoda</translation>
<translation id="5015510746216210676">Ierīces nosaukums:</translation>
<translation id="5017554619425969104">Kopētais teksts</translation>
+<translation id="5017828934289857214">AtgÄdinÄt vÄ“lÄk</translation>
<translation id="5018422839182700155">Nevar atvērt šo lapu</translation>
<translation id="5019198164206649151">DublÄ“jumu krÄtuve nav labÄ stÄvoklÄ«.</translation>
<translation id="5020776957610079374">Pasaules mūzika</translation>
@@ -1495,6 +1506,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="5051305769747448211">KomiÄ·u uzstÄÅ¡anÄs</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Lai nosÅ«tÄ«tu Å¡o failu, izmantojot funkciju “KopÄ«goÅ¡ana tuvumÄâ€, atbrÄ«vojiet vietu krÄtuvÄ“ (<ph name="DISK_SPACE_SIZE" />)}zero{Lai nosÅ«tÄ«tu Å¡os failus, izmantojot funkciju “KopÄ«goÅ¡ana tuvumÄâ€, atbrÄ«vojiet vietu krÄtuvÄ“ (<ph name="DISK_SPACE_SIZE" />)}one{Lai nosÅ«tÄ«tu Å¡os failus, izmantojot funkciju “KopÄ«goÅ¡ana tuvumÄâ€, atbrÄ«vojiet vietu krÄtuvÄ“ (<ph name="DISK_SPACE_SIZE" />)}other{Lai nosÅ«tÄ«tu Å¡os failus, izmantojot funkciju “KopÄ«goÅ¡ana tuvumÄâ€, atbrÄ«vojiet vietu krÄtuvÄ“ (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">Jums piemeklēti raksti</translation>
+<translation id="5060483733937416656">JÅ«s izvÄ“lÄ“jÄties veikt verifikÄciju ar Windows Hello tÄ«mekļa vietnÄ“s, kas izmanto <ph name="PROVIDER_ORIGIN" /> pakalpojumus. Å is pakalpojumu sniedzÄ“js, iespÄ“jams, ir saglabÄjis informÄciju par jÅ«su maksÄjuma veidu, un jÅ«s varat <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Vai domÄjÄt <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">DrukÄÅ¡anas vÄ“sture</translation>
<translation id="5068234115460527047">Riska ieguldījumu fondi</translation>
@@ -1508,10 +1520,8 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="5087286274860437796">Servera sertifikÄts Å¡obrÄ«d nav derÄ«gs.</translation>
<translation id="5087580092889165836">Pievienot karti</translation>
<translation id="5088142053160410913">Ziņojums operatoram</translation>
-<translation id="5089810972385038852">Å tats</translation>
<translation id="5093232627742069661">Z-veida locījums</translation>
<translation id="5094747076828555589">Å is serveris nevarÄ“ja pierÄdÄ«t, ka Å¡Ä« ir vietne <ph name="DOMAIN" />; tÄs droÅ¡Ä«bas sertifikÄts netiek uzskatÄ«ts par uzticamu Chromium sistÄ“mÄ. IespÄ“jams, tas ir nepareizas konfigurÄcijas dēļ vai arÄ« kÄds ir ļaunprÄtÄ«gi izmantojis jÅ«su savienojumu.</translation>
-<translation id="5095208057601539847">Province</translation>
<translation id="5097099694988056070">Ierīces statistika, piemēram, CPU/RAM lietojums</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Vietne nav droša</translation>
@@ -1549,6 +1559,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="5171045022955879922">Meklējiet vai ievadiet URL.</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Ierīce</translation>
+<translation id="5177076414499237632">SkatÄ«t informÄciju par Å¡Ä«s lapas avotu un tÄ“mu</translation>
<translation id="5179510805599951267">Vai nav valodÄ: <ph name="ORIGINAL_LANGUAGE" />? Ziņot par Å¡o kļūdu</translation>
<translation id="518639307526414276">MÄjdzÄ«vnieku barÄ«ba un preces mÄjdzÄ«vnieku aprÅ«pei</translation>
<translation id="5190835502935405962">GrÄmatzÄ«mju josla</translation>
@@ -1709,6 +1720,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="5624120631404540903">PÄrvaldÄ«t paroles</translation>
<translation id="5629630648637658800">NeizdevÄs ielÄdÄ“t politikas iestatÄ«jumus.</translation>
<translation id="5631439013527180824">IerÄ«ces pÄrvaldÄ«bas marÄ·ieris nav derÄ«gs.</translation>
+<translation id="5632485077360054581">ParÄdÄ«t, kÄ</translation>
<translation id="5633066919399395251">UzbrucÄ“ji vietnÄ“ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> var mÄ“Ä£inÄt jÅ«su datorÄ instalÄ“t bÄ«stamas programmas, kas zog vai izdzÄ“Å¡ informÄciju (piemÄ“ram, fotoattÄ“lus, paroles, ziņojumus un kredÄ«tkarÅ¡u datus). <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="563324245173044180">Bloķēts maldinošs saturs</translation>
<translation id="5633259641094592098">Kulta un indie filmas</translation>
@@ -1826,6 +1838,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="5989320800837274978">Nav norÄdÄ«ti nedz fiksÄ“ti starpniekserveri, nedz .pac skripta URL.</translation>
<translation id="5992691462791905444">Inženierijas Z-veida locījums</translation>
<translation id="5995727681868049093">PÄrvaldiet savu informÄciju, konfidencialitÄti un droÅ¡Ä«bu savÄ Google kontÄ</translation>
+<translation id="5997247540087773573">Nupat izmantotÄ parole ir atklÄta datu aizsardzÄ«bas pÄrkÄpuma dēļ. Lai aizsargÄtu kontus, Google paroļu pÄrvaldnieks iesaka nekavÄ“joties to nomainÄ«t un pÄ“c tam pÄrbaudÄ«t saglabÄtÄs paroles.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> rezultÄti vaicÄjumam “<ph name="SEARCH_TEXT" />â€</translation>
<translation id="6006484371116297560">Klasiskais motīvs</translation>
<translation id="6008122969617370890">Secība: no N līdz 1.</translation>
@@ -1921,7 +1934,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="627746635834430766">Lai nÄkamreiz veiktu maksÄjumu ÄtrÄk, saglabÄjiet kartes datus un norÄ“Ä·inu adresi savÄ Google kontÄ.</translation>
<translation id="6279183038361895380">Lai tiktu parÄdÄ«ts kursors, nospiediet |<ph name="ACCELERATOR" />|</translation>
<translation id="6280223929691119688">Nevar piegÄdÄt uz Å¡o adresi. Atlasiet citu adresi.</translation>
-<translation id="6282194474023008486">Pasta indekss</translation>
<translation id="6285507000506177184">Poga lejupielÄžu pÄrvaldÄ«bai pÄrlÅ«kÄ Chrome. Lai pÄrvaldÄ«tu pÄrlÅ«kÄ Chrome lejupielÄdÄ“tos failus, nospiediet taustiņu Enter.</translation>
<translation id="6289939620939689042">Lapas krÄsa</translation>
<translation id="6290238015253830360">Ieteiktie raksti tiek parÄdÄ«ti Å¡eit</translation>
@@ -1943,6 +1955,7 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="6337133576188860026">Tiks atbrÄ«vots mazÄk nekÄ <ph name="SIZE" />. Dažas vietnes nÄkamajÄ apmeklÄ“jumÄ var ielÄdÄ“t lÄ“nÄk.</translation>
<translation id="6337534724793800597">Filtrēt politikas pēc nosaukuma</translation>
<translation id="6340739886198108203">Administratora politika nosaka, ka nav ieteicams izveidot ekrÄnuzņēmumus vai videoierakstus, ja ir redzams konfidenciÄls saturs.</translation>
+<translation id="6348220984832452017">Aktīvie varianti</translation>
<translation id="6349101878882523185">Instalēt <ph name="APP_NAME" /> lietotni</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Nav}=1{1 parole (Å¡Ädiem domÄ“niem: <ph name="DOMAIN_LIST" />, sinhronizÄ“ta)}=2{2 paroles (Å¡Ädiem domÄ“niem: <ph name="DOMAIN_LIST" />, sinhronizÄ“tas)}zero{# paroļu (Å¡Ädiem domÄ“niem: <ph name="DOMAIN_LIST" />, sinhronizÄ“tas)}one{# parole (Å¡Ädiem domÄ“niem: <ph name="DOMAIN_LIST" />, sinhronizÄ“tas)}other{# paroles (Å¡Ädiem domÄ“niem: <ph name="DOMAIN_LIST" />, sinhronizÄ“tas)}}</translation>
<translation id="6355392890578844978">Å o pÄrlÅ«ku nepÄrvalda uzņēmums vai cita organizÄcija. DarbÄ«bas Å¡ajÄ ierÄ«cÄ“ var pÄrvaldÄ«t Ärpus pÄrlÅ«ka Chromium. <ph name="BEGIN_LINK" />Uzziniet vairÄk<ph name="END_LINK" />.</translation>
@@ -1974,7 +1987,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="643051589346665201">Mainīt Google paroli</translation>
<translation id="6433490469411711332">KontaktinformÄcijas rediģēšana</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> noraidīja savienojuma izveidi.</translation>
-<translation id="6438025220577812695">NomainÄ«t patstÄvÄ«gi</translation>
<translation id="6440503408713884761">Ignorēts</translation>
<translation id="6443406338865242315">Kurus paplaÅ¡inÄjumus un spraudņus esat instalÄ“jis</translation>
<translation id="6446163441502663861">Kahu (aploksne)</translation>
@@ -2104,9 +2116,9 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="6828866289116430505">Ģenētika</translation>
<translation id="6831043979455480757">Tulkot</translation>
<translation id="6833752742582340615">SaglabÄjiet savu kartes un norÄ“Ä·inu informÄciju Google kontÄ droÅ¡iem un ÄtrÄkiem norÄ“Ä·iniem.</translation>
-<translation id="6839929833149231406">Apgabals</translation>
<translation id="6846340164947227603">Izmantot virtuÄlÄs kartes numuru...</translation>
<translation id="6852204201400771460">Vai atkÄrtoti ielÄdÄ“t lietotni?</translation>
+<translation id="6857776781123259569">PÄrvaldÄ«t paroles…</translation>
<translation id="686485648936420384">PatÄ“rÄ“tÄju resursi</translation>
<translation id="6865412394715372076">Pašlaik nevar verificēt šo karti.</translation>
<translation id="6869334554832814367">Aizdevumi privÄtpersonÄm</translation>
@@ -2155,7 +2167,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="6965978654500191972">Ierīce</translation>
<translation id="696703987787944103">PerceptuÄla</translation>
<translation id="6968269510885595029">Izmantot jūsu drošības atslēgu</translation>
-<translation id="6970216967273061347">Rajons</translation>
<translation id="6971439137020188025">Ä€tri izveidot jaunu Google prezentÄciju pakalpojumÄ PrezentÄcijas</translation>
<translation id="6972629891077993081">HID ierīces</translation>
<translation id="6973656660372572881">Ir norÄdÄ«ti gan fiksÄ“ti starpniekserveri, gan .pac skripta URL.</translation>
@@ -2194,7 +2205,6 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<translation id="7081308185095828845">Šī funkcija jūsu ierīcē nav pieejama</translation>
<translation id="7083258188081898530">9. paplÄte</translation>
<translation id="7086090958708083563">AugÅ¡upielÄdi pieprasÄ«ja lietotÄjs.</translation>
-<translation id="7087282848513945231">GrÄfiste</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />: nospiediet tabulÄ“Å¡anas taustiņu un pÄ“c tam — ievadÄ«Å¡anas taustiņu, lai Chrome iestatÄ«jumos pÄrvaldÄ«tu atļaujas un datus, kas tiek glabÄti vietnÄ“s.</translation>
<translation id="7096937462164235847">Å Ä«s vietnes identitÄte nav verificÄ“ta.</translation>
<translation id="7101893872976785596">Å ausmu filmas</translation>
@@ -2213,10 +2223,10 @@ PretÄ“jÄ gadÄ«jumÄ Å¡Ä« iespÄ“ja bÅ«tu liegta saskaÅ†Ä ar jÅ«su konfidenciali
<ph name="LIST_ITEM" />veidlapÄs ievadÄ«tÄ informÄcija.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Nevar nosūtīt uz šo adresi. Atlasiet citu adresi.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Jūsu administrators ir aizliedzis šo datu kopēšanu.</translation>
<translation id="7135130955892390533">RÄdÄ«t statusu</translation>
<translation id="7138472120740807366">PiegÄdes veids</translation>
-<translation id="7139724024395191329">EmirÄti</translation>
<translation id="7139892792842608322">GalvenÄ paplÄte</translation>
<translation id="714064300541049402">2. puses attēla nobīde uz X ass</translation>
<translation id="7152423860607593928">Number-14 (aploksne)</translation>
@@ -2433,6 +2443,7 @@ Papildu informÄcija:
<translation id="7669271284792375604">UzbrucÄ“ji Å¡ajÄ vietnÄ“ var mudinÄt jÅ«s uz tÄdu programmu instalÄ“Å¡anu, kuras traucÄ“ pÄrlÅ«koÅ¡anu (piemÄ“ram, mainot sÄkumlapu vai apmeklÄ“tajÄs vietnÄ“s rÄdot papildu reklÄmas).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{DarbÄ«bas ar datiem, kas atzÄ«mÄ“ti kÄ konfidenciÄli (viena darbÄ«ba kopÅ¡ pieteikÅ¡anÄs). <ph name="BEGIN_LINK" />UzzinÄt vairÄk<ph name="END_LINK" />}zero{DarbÄ«bas ar datiem, kas atzÄ«mÄ“ti kÄ konfidenciÄli (# darbÄ«bas kopÅ¡ pieteikÅ¡anÄs). <ph name="BEGIN_LINK" />UzzinÄt vairÄk<ph name="END_LINK" />}one{DarbÄ«bas ar datiem, kas atzÄ«mÄ“ti kÄ konfidenciÄli (# darbÄ«ba kopÅ¡ pieteikÅ¡anÄs). <ph name="BEGIN_LINK" />UzzinÄt vairÄk<ph name="END_LINK" />}other{DarbÄ«bas ar datiem, kas atzÄ«mÄ“ti kÄ konfidenciÄli (# darbÄ«bas kopÅ¡ pieteikÅ¡anÄs). <ph name="BEGIN_LINK" />UzzinÄt vairÄk<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">6. pastkaste</translation>
+<translation id="7675325315208090829">PÄrvaldÄ«t maksÄjumu veidus…</translation>
<translation id="7676643023259824263">Meklēt starpliktuves tekstu <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Chrome iestatÄ«jumos skatÄ«t un pÄrvaldÄ«t pÄrlÅ«koÅ¡anas vÄ“sturi</translation>
<translation id="7679947978757153706">Beisbols</translation>
@@ -2475,7 +2486,6 @@ Papildu informÄcija:
<translation id="7766518757692125295">Kontūra</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">TÄda pati secÄ«ba, apdrukÄtÄ puse uz augÅ¡u</translation>
-<translation id="777702478322588152">Prefektūra</translation>
<translation id="7791011319128895129">Nav izlaista</translation>
<translation id="7791196057686275387">Iepakojums</translation>
<translation id="7791543448312431591">Pievienot</translation>
@@ -2566,12 +2576,12 @@ Papildu informÄcija:
<translation id="8055534648776115597">ArodizglÄ«tÄ«ba un tÄlÄkizglÄ«tÄ«ba</translation>
<translation id="8057711352706143257">Programmatūra <ph name="SOFTWARE_NAME" /> nav pareizi konfigurēta. Atinstalējot programmatūru <ph name="SOFTWARE_NAME" />, parasti problēma tiek novērsta. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">PÄrtikas rÅ«pniecÄ«ba</translation>
-<translation id="8066955247577885446">Diemžēl radÄs problÄ“ma.</translation>
<translation id="8067872629359326442">JÅ«s tikko ievadÄ«jÄt savu paroli maldinoÅ¡Ä vietnÄ“. Chromium var palÄ«dzÄ“t. Lai mainÄ«tu paroli un paziņotu uzņēmumam Google, ka jÅ«su konts, iespÄ“jams, ir apdraudÄ“ts, noklikÅ¡Ä·iniet uz pogas AizsargÄt kontu.</translation>
<translation id="8070439594494267500">Lietotnes ikona</translation>
<translation id="8074253406171541171">10x13 (aploksne)</translation>
<translation id="8075736640322370409">Ä€tri izveidot jaunu Google izklÄjlapu</translation>
<translation id="8075898834294118863">PÄrvaldÄ«t vietnes iestatÄ«jumus</translation>
+<translation id="8076492880354921740">Cilnes</translation>
<translation id="8078141288243656252">Pagrieztiem dokumentiem nevar pievienot piezīmes</translation>
<translation id="8079031581361219619">Vai atkÄrtoti ielÄdÄ“t vietni?</translation>
<translation id="8081087320434522107">Sedani</translation>
@@ -2694,6 +2704,7 @@ Papildu informÄcija:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" />, <ph name="SECOND_LABEL" />, <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Iestatījumi</translation>
+<translation id="8428634594422941299">Labi</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />: nospiediet tabulÄ“Å¡anas taustiņu un pÄ“c tam — ievadÄ«Å¡anas taustiņu, lai Chrome iestatÄ«jumos pÄrvaldÄ«tu savas sÄ«kfailu preferences.</translation>
<translation id="8433057134996913067">Å Ädi tiksiet izrakstÄ«ts no lielÄkÄs daļas vietņu.</translation>
<translation id="8434840396568290395">MÄjdzÄ«vnieki</translation>
@@ -2792,6 +2803,7 @@ Papildu informÄcija:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> ir jūsu kods vietnei <ph name="ORIGIN" />.</translation>
<translation id="874918643257405732">SaglabÄt Å¡o cilni kÄ grÄmatzÄ«mi</translation>
<translation id="8751426954251315517">MÄ“Ä£iniet vÄ“lreiz nÄkamajÄ reizÄ“</translation>
+<translation id="8757526089434340176">Pieejams Google Pay piedÄvÄjums</translation>
<translation id="8758885506338294482">Videospēļu sacensības</translation>
<translation id="8759274551635299824">Kartes derīguma termiņš ir beidzies</translation>
<translation id="87601671197631245">Å ajÄ vietnÄ“ tiek izmantota novecojusi droÅ¡Ä«bas konfigurÄcija, tÄdēļ var tikt atklÄta jÅ«su informÄcija (piemÄ“ram, paroles, ziņojumi vai kredÄ«tkarÅ¡u dati), kad tÄ tiek nosÅ«tÄ«ta uz Å¡o vietni.</translation>
@@ -2799,6 +2811,7 @@ Papildu informÄcija:
<translation id="8763927697961133303">USB ierīce</translation>
<translation id="8763986294015493060">Aizveriet visus atvērtos inkognito režīma logus.</translation>
<translation id="8766943070169463815">Ir atvÄ“rta droÅ¡u maksÄjumu akreditÄcijas datu autentifikÄcijas lapa</translation>
+<translation id="8767765348545497220">Aizvērt palīdzības burbuli</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocikli</translation>
<translation id="8790007591277257123">&amp;Atcelt dzēšanas atsaukšanu</translation>
@@ -2811,6 +2824,7 @@ Papildu informÄcija:
<translation id="8806285662264631610">Vannas un ķermeņa kopšanas produkti</translation>
<translation id="8807160976559152894">Apgriešana pēc katras lapas</translation>
<translation id="8808828119384186784">Chrome iestatījumi</translation>
+<translation id="8813277370772331957">AtgÄdinÄt vÄ“lÄk</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Lai Chrome iestatÄ«jumos atjauninÄtu pÄrlÅ«kprogrammu Chrome, nospiediet tabulÄ“Å¡anas taustiņu un pÄ“c tam — ievadÄ«Å¡anas taustiņu.</translation>
<translation id="8820817407110198400">GrÄmatzÄ«mes</translation>
<translation id="882338992931677877">ManuÄlais slots</translation>
@@ -2990,6 +3004,7 @@ Papildu informÄcija:
<translation id="988159990683914416">AttÄ«stÄ«tÄja konstrukcija</translation>
<translation id="989988560359834682">Rediģēt adresi</translation>
<translation id="991413375315957741">kustību un gaismas sensori</translation>
+<translation id="992110854164447044">VirtuÄla karte paslÄ“pj jÅ«su faktisko karti, lai efektÄ«vÄk aizsargÄtu jÅ«s pret iespÄ“jamu krÄpÅ¡anu. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">RozÄ</translation>
<translation id="992432478773561401">ProgrammatÅ«ra <ph name="SOFTWARE_NAME" /> netika pareizi instalÄ“ta datorÄ vai tÄ«klÄ.
diff --git a/chromium/components/strings/components_strings_mk.xtb b/chromium/components/strings/components_strings_mk.xtb
index 09e76976cae..7a5d40b5e34 100644
--- a/chromium/components/strings/components_strings_mk.xtb
+++ b/chromium/components/strings/components_strings_mk.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Грантови, Ñтипендии и финанÑиÑка помош</translation>
<translation id="1048785276086539861">Кога ќе ги изменувате прибелешките, документов ќе Ñе врати на приказ на една Ñтраница</translation>
<translation id="1050038467049342496">Затворете ги другите апликации</translation>
+<translation id="1053959602163383901">Избравте да потврдите Ñо уред за автентикација на веб-Ñтраници кои кориÑтат <ph name="PROVIDER_ORIGIN" />. Давателов на уÑлуги можеби ги зачувал податоците за вашиот начин на плаќање и можете <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Врати додавање</translation>
<translation id="1056663316309890343">Софтвер за фотографии</translation>
<translation id="1056898198331236512">Предупредување</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">Ðачин на подигнување</translation>
<translation id="1281476433249504884">Фиока за Ñкладирање 1</translation>
<translation id="1285320974508926690">Ðикогаш не преведувај ја оваа локација</translation>
+<translation id="1288548991597756084">Безбедно зачувајте ја картичката</translation>
<translation id="1292571435393770077">Фиока 16</translation>
<translation id="1292701964462482250">„Софтверот на вашиот компјутер го Ñпречува Chrome безбедно да Ñе поврзе на интернет“ (Ñамо за компјутери Ñо Windows)</translation>
<translation id="1294154142200295408">Варијации на линијата за наредби</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;За да ја поправите грешката, кликнете &lt;strong&gt;Поврзи&lt;/strong&gt; на Ñтраницата што Ñе обидувате да ја отворите.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Пејзажен дизајн</translation>
<translation id="1513706915089223971">СпиÑок Ñо запиÑи во иÑторијата</translation>
+<translation id="1516097932025103760">Ќе биде шифрирана и безбедно зачувана, а CVC никогаш не Ñе Ñкладира.</translation>
<translation id="1517433312004943670">Потребен е телефонÑки број</translation>
<translation id="1519264250979466059">Датум на комплетна верзија</translation>
<translation id="1521159554480556801">Ткаенина и текÑтилна уметноÑÑ‚</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Зачувувај и пополнувај начини на плаќање</translation>
<translation id="1663943134801823270">Картичките и адреÑите Ñе од Chrome. Со нив може да управувате во <ph name="BEGIN_LINK" />ПоÑтавки<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">ОтÑега, Ñтраниците на <ph name="SOURCE_LANGUAGE" /> ќе Ñе преведуваат на <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Платете Ñо <ph name="CARD_DETAIL" /> за да ја кориÑтите понудата</translation>
<translation id="1674504678466460478">Од <ph name="SOURCE_LANGUAGE" /> на <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Прво краткиот раб</translation>
<translation id="168693727862418163">ВредноÑта на правилото не може да Ñе потврди Ñпоред неговата шема и ќе биде игнорирана.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Сензори за движење</translation>
<translation id="1717494416764505390">ПоштенÑко Ñандаче 3</translation>
<translation id="1718029547804390981">Документот е премногу голем за да Ñе Ñтави прибелешка</translation>
+<translation id="1720941539803966190">Затвори го упатÑтвото</translation>
<translation id="1721424275792716183">* Полето е задолжително</translation>
<translation id="1727613060316725209">Сертификатот е важечки</translation>
<translation id="1727741090716970331">Додајте важечки број на картичка</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">Скари и грил</translation>
<translation id="2053111141626950936">Страниците на <ph name="LANGUAGE" /> нема да Ñе преведуваат.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Кога оваа контрола е вклучена и ÑтатуÑот е активен, Chrome одредува Ñо која голема група на луѓе или „кохортата“ е најÑлична вашата неодамнешна активноÑÑ‚ од прелиÑтувањето. ОглаÑувачите може да избираат реклами за групата, а вашата активноÑÑ‚ од прелиÑтувањето оÑтанува приватна на вашиот уред. Групата Ñе ажурира Ñекој ден.}=1{Кога оваа контрола е вклучена и ÑтатуÑот е активен, Chrome одредува Ñо која голема група на луѓе или „кохортата“ е најÑлична вашата неодамнешна активноÑÑ‚ од прелиÑтувањето. ОглаÑувачите може да избираат реклами за групата, а вашата активноÑÑ‚ од прелиÑтувањето оÑтанува приватна на вашиот уред. Групата Ñе ажурира Ñекој ден.}one{Кога оваа контрола е вклучена и ÑтатуÑот е активен, Chrome одредува Ñо која голема група на луѓе или „кохортата“ е најÑлична вашата неодамнешна активноÑÑ‚ од прелиÑтувањето. ОглаÑувачите може да избираат реклами за групата, а вашата активноÑÑ‚ од прелиÑтувањето оÑтанува приватна на вашиот уред. Групата Ñе ажурира Ñекој {NUM_DAYS} ден.}other{Кога оваа контрола е вклучена и ÑтатуÑот е активен, Chrome одредува Ñо која голема група на луѓе или „кохортата“ е најÑлична вашата неодамнешна активноÑÑ‚ од прелиÑтувањето. ОглаÑувачите може да избираат реклами за групата, а вашата активноÑÑ‚ од прелиÑтувањето оÑтанува приватна на вашиот уред. Групата Ñе ажурира Ñекои {NUM_DAYS} дена.}}</translation>
-<translation id="2053553514270667976">ПоштенÑки број</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 предлог}one{# предлог}other{# предлози}}</translation>
+<translation id="2066915425250589881">да побарате да Ñе избришат</translation>
<translation id="2068528718802935086">Бебиња и мали деца</translation>
<translation id="2071156619270205202">Картичкава не е подобна за број на виртуелна картичка.</translation>
<translation id="2071692954027939183">ИзвеÑтувањата беа блокирани автоматÑки бидејќи обично не ги дозволувате</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">Копче „Управувај Ñо Ñинхронизирањето“, притиÑнете Enter за да управувате Ñо податоците коишто Ñе Ñинхронизираат во поÑтавките за Chrome</translation>
<translation id="2091887806945687916">Звук</translation>
<translation id="2094505752054353250">ÐеÑовпаѓање на домени</translation>
-<translation id="2096368010154057602">ОблаÑÑ‚</translation>
<translation id="2099652385553570808">Тројно Ñпојување лево</translation>
<translation id="2101225219012730419">Верзија:</translation>
<translation id="2102134110707549001">Предложи Ñилна лозинка…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">ÐмериканÑки фудбал</translation>
<translation id="2187317261103489799">Откриј (Ñтандардно)</translation>
<translation id="2188375229972301266">Повеќе дупки долу</translation>
-<translation id="2188852899391513400">Отркиено е дека лозинката која штотуку ја употребивте е меѓу лозинките откриени при упад во податоците. За да ја задржите безбедноÑта на Ñметките, „Управникот Ñо лозинки на Google“ препорачува веднаш да ја промените, а потоа да ги проверите зачуваните лозинки.</translation>
<translation id="219906046732893612">Реновирање на домот</translation>
<translation id="2202020181578195191">ВнеÑете важечка година на иÑтекување</translation>
<translation id="22081806969704220">Фиока 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">Изменување датотеки</translation>
<translation id="2215963164070968490">Кучиња</translation>
<translation id="2218879909401188352">Ðапаѓачите што Ñе моментално на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> може да инÑталираат опаÑни апликации што го оштетуваат вашиот уред, да додаваат Ñкриени трошоци на Ñметката за мобилниот телефон или да крадат лични информации. <ph name="BEGIN_LEARN_MORE_LINK" />Дознајте повеќе<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">РеÑтартирај го упатÑтвото</translation>
+<translation id="2219735899272417925">Треба да Ñе реÑетира уредот</translation>
<translation id="2224337661447660594">Ðема интернет</translation>
<translation id="2230458221926704099">Поправете ја врÑката Ñо кориÑтење на <ph name="BEGIN_LINK" />апликацијата за дијагноÑтицирање<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">ИÑпрати Ñега</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">Ðе е дозволено (Ñтандардно)</translation>
<translation id="2512413427717747692">Копче за поÑтавување на Chrome како Ñтандарден прелиÑтувач, притиÑнете Enter за да го поÑтавите Chrome како Ñтандарден прелиÑтувач на ÑиÑтемот во поÑтавките за iOS</translation>
<translation id="2515629240566999685">Да го проверите Ñигналот во вашата облаÑÑ‚</translation>
+<translation id="2515761554693942801">Избравте да потврдите Ñо Touch ID на веб-Ñтраници кои кориÑтат <ph name="PROVIDER_ORIGIN" />. Давателов на уÑлуги можеби ги зачувал податоците за вашиот начин на плаќање и можете <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Спојување долу деÑно</translation>
<translation id="2521736961081452453">Создај формулар</translation>
<translation id="2523886232349826891">Зачувано Ñамо на овој уред</translation>
<translation id="2524461107774643265">Додајте повеќе информации</translation>
<translation id="2529899080962247600">Полево не треба да има повеќе од <ph name="MAX_ITEMS_LIMIT" /> запиÑи. Сите понатамошни запиÑи ќе Ñе игнорираат.</translation>
+<translation id="253493526287553278">Погледнете ги деталите за промотивниот код</translation>
<translation id="2535585790302968248">Отворете нова картичка „Инкогнито“ за да прелиÑтувате приватно</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{и уште 1}one{и уште #}other{и уште #}}</translation>
<translation id="2536110899380797252">Додајте адреÑа</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">ФотографÑки и дигитални уметноÑти</translation>
<translation id="2601150049980261779">Романтични филмови</translation>
<translation id="2604589665489080024">Поп музика</translation>
-<translation id="2609632851001447353">Варијации</translation>
<translation id="2610561535971892504">Кликнете за копирање</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />нема да ги зачувува<ph name="END_EMPHASIS" /> Ñледниве податоци:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Родендени и имендени</translation>
<translation id="2677748264148917807">Ðапушти</translation>
+<translation id="2679714844901977852">Зачувајте ги податоците за картичката и наплата на вашата Ñметка на Google <ph name="USER_EMAIL" /> за побезбедно и побрзо плаќање</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Ðајдобро вклопување</translation>
<translation id="2688969097326701645">Да, продолжи</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">Интернет-оператори (ISP)</translation>
<translation id="2856444702002559011">Ðапаѓачите можеби Ñе обидуваат да ги украдат вашите информации од <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (на пример, лозинки, пораки или кредитни картички). <ph name="BEGIN_LEARN_MORE_LINK" />Дознајте повеќе<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Сајтов прикажува нападни или лажни реклами.</translation>
-<translation id="286512204874376891">Виртуелната картичка ја маÑкира вашата актуелна картичка за да ја заштити од потенцијална измама. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ПријателÑки</translation>
<translation id="28761159517501904">Филмови</translation>
<translation id="2876489322757410363">Ќе излезете од режимот „Инкогнито“ за да платите преку надворешна апликација. Дали Ñакате да продолжите?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ДиÑк</translation>
<translation id="3162559335345991374">Wi-Fi што го кориÑтите може да бара да ја поÑетите Ñтраницата за најавување.</translation>
<translation id="3169472444629675720">Откриј</translation>
-<translation id="3174168572213147020">ОÑтров</translation>
<translation id="3176929007561373547">Проверете ги поÑтавките на прокÑи или контактирајте Ñо админиÑтраторот
на мрежата за да проверите дали работи прокÑи-Ñерверот. Ðко
Ñметате дека не треба да кориÑтите прокÑи-Ñервер:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">Грешка на чаÑовникот</translation>
<translation id="3369459162151165748">Делови и додатоци за возила</translation>
<translation id="3371064404604898522">ПоÑтави го Chrome како Ñтандарден прелиÑтувач</translation>
-<translation id="3371076217486966826"><ph name="URL" /> Ñака да:
- • Ñоздаде 3D-карта од вашата околина и да ја Ñледи положбата на камерата
- • ја кориÑти вашата камера</translation>
<translation id="337363190475750230">Со оневозможен приÑтап</translation>
<translation id="3375754925484257129">Изврши безбедноÑна проверка во Chrome</translation>
<translation id="3377144306166885718">Серверот кориÑтеше заÑтарена верзија на TLS.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">ÐдреÑа за иÑпорака</translation>
<translation id="3402261774528610252">Ð’Ñ€Ñката за вчитување на Ñајтов кориÑтеше TLS 1.0 или TLS 1.1, што Ñе заÑтарени и ќе бидат оневозможени во иднина. Штом ќе Ñе оневозможат, кориÑниците нема да можат да го вчитуваат Ñајтов. Серверот треба да овозможи TLS 1.2 или понова верзија.</translation>
<translation id="3405664148539009465">ПриÑпоÑобете ги фонтовите</translation>
+<translation id="3407789382767355356">најавување на трети Ñтрани</translation>
<translation id="3409896703495473338">Управувај Ñо безбедноÑните поÑтавки</translation>
<translation id="3414952576877147120">Големина:</translation>
<translation id="3417660076059365994">Датотеките што ги прикачувате или приложувате Ñе иÑпраќаат до Google Cloud или трети Ñтрани за анализа. Ðа пример, можеби ќе Ñе Ñкенираат за да Ñе провери дали Ñодржат чувÑтвителни податоци или злонамерен Ñофтвер.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">Прикажувани филмови и времиња на претÑтави</translation>
<translation id="3479552764303398839">Ðе Ñега</translation>
<translation id="3484560055331845446">Може да го изгубите приÑтапот до Ñметката на Google. Chrome препорачува да ја промените лозинката Ñега. Ќе Ñе побара од Ð²Ð°Ñ Ð´Ð° Ñе најавите.</translation>
+<translation id="3484861421501147767">ПотÑетник: доÑтапен е зачуван промотивен код</translation>
<translation id="3487845404393360112">Фиока 4</translation>
<translation id="3495081129428749620">Ðајди на Ñтраницата
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">Управувај</translation>
<translation id="3816482573645936981">ВредноÑÑ‚ (заменета)</translation>
<translation id="382518646247711829">Ðко кориÑтите прокÑи Ñервер...</translation>
+<translation id="3826050100957962900">Ðајавување на трети Ñтрани</translation>
<translation id="3827112369919217609">ÐпÑолутна</translation>
<translation id="3827666161959873541">Семејни филмови</translation>
<translation id="3828924085048779000">Ðе е дозволена празна лозинка.</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">Ðжурирај датум и време</translation>
<translation id="3858860766373142691">Име</translation>
<translation id="3872834068356954457">Ðаука</translation>
+<translation id="3875783148670536197">Покажи ми како</translation>
<translation id="3881478300875776315">Прикажи помалку редови</translation>
<translation id="3884278016824448484">Идентификувач на конфликтен уред</translation>
-<translation id="3885155851504623709">Парохија</translation>
<translation id="388632593194507180">Откриен е надзор</translation>
<translation id="3886948180919384617">Фиока за Ñкладирање 3</translation>
<translation id="3890664840433101773">Додајте е-пошта</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> е блокиран</translation>
<translation id="3978338123949022456">Режим на пребарување, напишете барање и притиÑнете Enter за да пребарувате Ñо <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Птици</translation>
+<translation id="3985750352229496475">Управувајте Ñо адреÑите…</translation>
<translation id="3986705137476756801">ИÑклучете ја „ÐвтоматÑки титлови“ заÑега</translation>
<translation id="3987940399970879459">Помалку од 1 MB</translation>
<translation id="3990250421422698716">ОфÑет превиткување</translation>
+<translation id="3992684624889376114">За оваа Ñтраница</translation>
<translation id="3996311196211510766">Сајтот <ph name="ORIGIN" /> побарал да Ñе применува правило за потекло
за Ñите барања до него, но ова правило не може да Ñе примени во моментов.</translation>
<translation id="4006465311664329701">Ðачини на плаќање, понуди и адреÑи преку Google Pay</translation>
@@ -1227,6 +1234,7 @@
<translation id="4305666528087210886">Ðе може да Ñе приÑтапи до вашата датотека</translation>
<translation id="4306529830550717874">Да Ñе зачува адреÑата?</translation>
<translation id="4306812610847412719">привремена меморија</translation>
+<translation id="4310070645992025887">Пребарајте ги вашите „Патувања“</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Блокирај (Ñтандардно)</translation>
<translation id="4314815835985389558">Управувајте Ñо Ñинхронизацијата</translation>
@@ -1277,6 +1285,7 @@
<translation id="4435702339979719576">Разгледница)</translation>
<translation id="443673843213245140">Оневозможена е употребата на прокÑи, но одредена е опширна конфигурација на прокÑи.</translation>
<translation id="4441832193888514600">Игнорирано бидејќи правилото може да Ñе поÑтави Ñамо како политика за кориÑници во облак.</translation>
+<translation id="4442470707340296952">Картички на Chrome</translation>
<translation id="4450893287417543264">Ðе прикажувај повторно</translation>
<translation id="4451135742916150903">Може да прашува за да Ñе поврзува Ñо HID-уреди</translation>
<translation id="4452328064229197696">Отркиено е дека лозинката која штотуку ја употребивте е меѓу лозинките откриени при упад во податоците. За да ја задржите безбедноÑта на Ñметките, „Управникот Ñо лозинки на Google“ препорачува да ги проверите зачуваните лозинки.</translation>
@@ -1415,6 +1424,7 @@
<translation id="483241715238664915">Вклучи опомени</translation>
<translation id="4834250788637067901">Ðачини на плаќање, понуди и адреÑи преку Google Pay</translation>
<translation id="4838327282952368871">Ðереален</translation>
+<translation id="4839087176073128681">Платете побрзо Ñледниот пат, а вашата картичка заштитете ја Ñо најнова технологија за безбедноÑÑ‚ од Google.</translation>
<translation id="4840250757394056958">Прикажи ја иÑторијата на Chrome</translation>
<translation id="484462545196658690">ÐвтоматÑки</translation>
<translation id="484671803914931257">Добијте попуÑÑ‚ од <ph name="MERCHANT_NAME" /> и други</translation>
@@ -1477,6 +1487,7 @@
<translation id="5011561501798487822">Откриен јазик</translation>
<translation id="5015510746216210676">Име на уредот:</translation>
<translation id="5017554619425969104">ТекÑтот што го копиравте</translation>
+<translation id="5017828934289857214">ПотÑети ме подоцна</translation>
<translation id="5018422839182700155">Ðе може да Ñе отвори Ñтраницава</translation>
<translation id="5019198164206649151">Продавницата за резервни копии е во лоша ÑоÑтојба</translation>
<translation id="5020776957610079374">СветÑка музика</translation>
@@ -1496,6 +1507,7 @@
<translation id="5051305769747448211">Комедија во живо</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{За да ја иÑпратите датотекава преку „Споделување во близина“, оÑлободете проÑтор (<ph name="DISK_SPACE_SIZE" />) на уредот}one{За да ги иÑпратите датотекиве преку „Споделување во близина“, оÑлободете проÑтор (<ph name="DISK_SPACE_SIZE" />) на уредот}other{За да ги иÑпратите датотекиве преку „Споделување во близина“, оÑлободете проÑтор (<ph name="DISK_SPACE_SIZE" />) на уредот}}</translation>
<translation id="5056549851600133418">Статии за ваÑ</translation>
+<translation id="5060483733937416656">Избравте да потврдите Ñо Windows Hello на веб-Ñтраници кои кориÑтат <ph name="PROVIDER_ORIGIN" />. Давателов на уÑлуги можеби ги зачувал податоците за вашиот начин на плаќање и можете <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Дали миÑлевте на <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">ИÑторија на печатење</translation>
<translation id="5068234115460527047">Хеџ фондови</translation>
@@ -1509,10 +1521,8 @@
<translation id="5087286274860437796">Сертификатот на Ñерверот не е важечки во моментов.</translation>
<translation id="5087580092889165836">Додај картичка</translation>
<translation id="5088142053160410913">Порака до операторот</translation>
-<translation id="5089810972385038852">Држава</translation>
<translation id="5093232627742069661">Z-превиткување</translation>
<translation id="5094747076828555589">Серверот не може да докаже дека е <ph name="DOMAIN" />; Chromium не му верува на неговиот безбедноÑен Ñертификат. Тоа може да Ñе должи на погрешна конфигурација или на напаѓач што го преÑретнува поврзувањето.</translation>
-<translation id="5095208057601539847">Провинција</translation>
<translation id="5097099694988056070">СтатиÑтички податоци за уредот, како на пример, кориÑтење CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Сајтот не е безбеден</translation>
@@ -1550,6 +1560,7 @@
<translation id="5171045022955879922">Барај или впиши URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Машина</translation>
+<translation id="5177076414499237632">Дознајте за изворот и темата на Ñтраницава</translation>
<translation id="5179510805599951267">Ðе е на <ph name="ORIGINAL_LANGUAGE" />? Пријави ја грешката.</translation>
<translation id="518639307526414276">Храна и производи за миленичиња</translation>
<translation id="5190835502935405962">Лента Ñо обележувачи</translation>
@@ -1710,6 +1721,7 @@
<translation id="5624120631404540903">Управувај Ñо лозинки</translation>
<translation id="5629630648637658800">Ðе уÑпеаја да Ñе вчитаат поÑтавките за политика</translation>
<translation id="5631439013527180824">Ðевалиден токен за управување Ñо уреди</translation>
+<translation id="5632485077360054581">Покажи ми како</translation>
<translation id="5633066919399395251">Ðапаѓачите што Ñе моментално на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> можеби ќе Ñе обидат да инÑталираат опаÑни програми на вашиот компјутер што ги крадат или бришат вашите информации (на пример, фотографии, лозинки, пораки и кредитни картички). <ph name="BEGIN_LEARN_MORE_LINK" />Дознајте повеќе<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Лажните Ñодржини Ñе блокирани.</translation>
<translation id="5633259641094592098">Култни и инди филмови</translation>
@@ -1827,6 +1839,7 @@
<translation id="5989320800837274978">Ðе Ñе наведени ниту прокÑи-Ñервери ниту URL на Ñкрипта .pac.</translation>
<translation id="5992691462791905444">ИнженерÑко Z-превиткување</translation>
<translation id="5995727681868049093">Управувајте Ñо податоците, приватноÑта и безбедноÑта на вашата Ñметка на Google</translation>
+<translation id="5997247540087773573">Откриено е дека лозинката која штотуку ја употребивте е меѓу лозинките откриени при упад во податоците. За да ја задржите безбедноÑта на Ñметките, Google Password Manager препорачува веднаш да ја промените и да ги проверите зачуваните лозинки.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> резултати за „<ph name="SEARCH_TEXT" />“</translation>
<translation id="6006484371116297560">КлаÑична</translation>
<translation id="6008122969617370890">РедоÑлед N-до-1</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">За да платите побрзо Ñледниот пат, зачувајте ја картичката и адреÑата за наплата на вашата Ñметка на Google.</translation>
<translation id="6279183038361895380">ПритиÑнете |<ph name="ACCELERATOR" />| за да Ñе покаже покажувачот</translation>
<translation id="6280223929691119688">Ðе може да Ñе доÑтави на оваа адреÑа. Изберете друга.</translation>
-<translation id="6282194474023008486">ПоштенÑки број</translation>
<translation id="6285507000506177184">Копче „Управувајте Ñо преземањата во Chrome“, притиÑнете Enter за да управувате Ñо датотеките што Ñте ги презеле во Chrome</translation>
<translation id="6289939620939689042">Боја на Ñтраницата</translation>
<translation id="6290238015253830360">Предложените Ñтатии ќе Ñе појавуваат тука</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026">Ќе оÑлободи помалку од <ph name="SIZE" />. Ðекои Ñајтови може да Ñе вчитуваат побавно при вашата Ñледна поÑета.</translation>
<translation id="6337534724793800597">Филтрирај правила по име</translation>
<translation id="6340739886198108203">Правилото на админиÑтраторот не препорачува да Ñе прават Ñлики од екранот или да Ñе Ñнима кога има видливи доверливи Ñодржини:</translation>
+<translation id="6348220984832452017">Ðктивни варијации</translation>
<translation id="6349101878882523185">ИнÑталирај <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ðиедна}=1{1 лозинка (за <ph name="DOMAIN_LIST" />, Ñинхронизирана)}=2{2 лозинка (за <ph name="DOMAIN_LIST" />, Ñинхронизирани)}one{# лозинка (за <ph name="DOMAIN_LIST" />, Ñинхронизирани)}other{# лозинки (за <ph name="DOMAIN_LIST" />, Ñинхронизирани)}}</translation>
<translation id="6355392890578844978">ПрелиÑтувачов не е управуван од компанија или друга организација. Со активноÑта на уредов може да Ñе управува надвор од Chromium. <ph name="BEGIN_LINK" />Дознајте повеќе<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">Променете ја лозинката на Google</translation>
<translation id="6433490469411711332">Уредете ги информациите за контакт</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> одби да Ñе поврзе.</translation>
-<translation id="6438025220577812695">Ќе ја променам ÑамоÑтојно</translation>
<translation id="6440503408713884761">Игнориран</translation>
<translation id="6443406338865242315">кои екÑтензии и приклучоци ги имате инÑталирано</translation>
<translation id="6446163441502663861">Kahu (плик)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">Генетика</translation>
<translation id="6831043979455480757">Преведи</translation>
<translation id="6833752742582340615">Зачувајте ги податоците за картичката и наплата на вашата Ñметка на Google за побезбедно и побрзо плаќање</translation>
-<translation id="6839929833149231406">ОблаÑÑ‚</translation>
<translation id="6846340164947227603">КориÑтете број на виртуелна картичка…</translation>
<translation id="6852204201400771460">Дали да Ñе вчита апликацијата повторно?</translation>
+<translation id="6857776781123259569">Управувајте Ñо лозинките…</translation>
<translation id="686485648936420384">Потрошувачки реÑурÑи</translation>
<translation id="6865412394715372076">Картичкава не може да Ñе потврди во моментов</translation>
<translation id="6869334554832814367">Лични заеми</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">Уред</translation>
<translation id="696703987787944103">Перцептивна</translation>
<translation id="6968269510885595029">КориÑтете го безбедноÑниот клуч</translation>
-<translation id="6970216967273061347">ОблаÑÑ‚</translation>
<translation id="6971439137020188025">Ðабрзина Ñоздајте нова презентација во Google Slides</translation>
<translation id="6972629891077993081">HID-уреди</translation>
<translation id="6973656660372572881">Одредени Ñе и фикÑните прокÑи-Ñервери и URL-адреÑата на Ñкриптата .pac.</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">Функцијава не е доÑтапна на вашиот уред</translation>
<translation id="7083258188081898530">Фиока 9</translation>
<translation id="7086090958708083563">Побарано е прикачување од кориÑникот</translation>
-<translation id="7087282848513945231">Округ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, притиÑнете Tab, па Enter за да управувате Ñо дозволите и податоците коишто Ñе Ñкладираат на Ñајтовите во поÑтавките за Chrome</translation>
<translation id="7096937462164235847">Идентитетот на веб-Ñтраницава не е потврден.</translation>
<translation id="7101893872976785596">Хорор филмови</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />податоци внеÑени во формулари<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Ðе може да Ñе иÑпорача на оваа адреÑа. Изберете друга.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ÐдминиÑтраторот забранил копирање на податоциве.</translation>
<translation id="7135130955892390533">Прикажи го ÑтатуÑот</translation>
<translation id="7138472120740807366">Метод на доÑтава</translation>
-<translation id="7139724024395191329">Емират</translation>
<translation id="7139892792842608322">Примарна фиока</translation>
<translation id="714064300541049402">Промена на Ñтраната 2 на Ñликата X</translation>
<translation id="7152423860607593928">Number-14 (плик)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">Ðапаѓачите што Ñе наоѓаат на Ñајтов може да Ñе обидат да ве измамат да инÑталирате програми што ќе му наштетат на вашето иÑкуÑтво Ñо прелиÑтувањето (на пример, Ñо изменување на вашата почетна Ñтраница или Ñо прикажување дополнителни реклами на локациите што ги поÑетувате).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ДејÑтвата преземени Ñо податоци означени како доверливи (1 дејÑтво од најавувањето). <ph name="BEGIN_LINK" />Дознајте повеќе<ph name="END_LINK" />}one{ДејÑтвата преземени Ñо податоци означени како доверливи (# дејÑтво од најавувањето). <ph name="BEGIN_LINK" />Дознајте повеќе<ph name="END_LINK" />}other{ДејÑтвата преземени Ñо податоци означени како доверливи (# дејÑтва од најавувањето). <ph name="BEGIN_LINK" />Дознајте повеќе<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ПоштенÑко Ñандаче 6</translation>
+<translation id="7675325315208090829">Управувајте Ñо начините на плаќање…</translation>
<translation id="7676643023259824263">Пребарајте текÑÑ‚ во привремената меморија, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Прегледајте ја и управувајте Ñо иÑторијата на прелиÑтувањето во поÑтавките за Chrome</translation>
<translation id="7679947978757153706">Бејзбол</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">ПреÑтилка</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">ИÑÑ‚ редоÑлед Ñо лицето нагоре</translation>
-<translation id="777702478322588152">Префектура</translation>
<translation id="7791011319128895129">Ðеобјавено</translation>
<translation id="7791196057686275387">Бала</translation>
<translation id="7791543448312431591">Додај</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">ВокациÑко и продолжено образование</translation>
<translation id="8057711352706143257">Софтверот „<ph name="SOFTWARE_NAME" />“ не е конфигуриран правилно. Проблемот обично Ñе решава Ñо деинÑталирање на „<ph name="SOFTWARE_NAME" />“. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ПроизводÑтво на храна</translation>
-<translation id="8066955247577885446">Ðешто тргна наопаку.</translation>
<translation id="8067872629359326442">Тукушто ја внеÑовте Ñвојата лозинка на измамнички Ñајт. Chromium може да помогне. За да ја Ñмените лозинката и да го извеÑтите Google дека е можно вашата Ñметка да е изложена на ризик, кликнете „Заштити ја Ñметката“.</translation>
<translation id="8070439594494267500">Икона на апликацијата</translation>
<translation id="8074253406171541171">10x13 (плик)</translation>
<translation id="8075736640322370409">Ðабрзина Ñоздајте нова табела на Google</translation>
<translation id="8075898834294118863">Управувај Ñо поÑтавките за Ñајтови</translation>
+<translation id="8076492880354921740">Картички</translation>
<translation id="8078141288243656252">Ðе може да Ñе Ñтави прибелешка кога документот е ротиран</translation>
<translation id="8079031581361219619">Дали повторно да Ñе вчита Ñајтот?</translation>
<translation id="8081087320434522107">Седани</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ПоÑтавки</translation>
+<translation id="8428634594422941299">Сфатив</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, притиÑнете Tab, па Enter за да управувате Ñо поÑтавките за колачиња во поÑтавките на Chrome</translation>
<translation id="8433057134996913067">Ова ќе ве одјави од повеќето веб-Ñајтови.</translation>
<translation id="8434840396568290395">Миленици</translation>
@@ -2742,7 +2753,7 @@
<translation id="8574899947864779331">КориÑтете Touch ID за побрзо потврдување на картичките</translation>
<translation id="8576310925653847813">СиÑтеми за домашно кино</translation>
<translation id="8577348305244205642">Виртуелната картичка не е доÑтапна</translation>
-<translation id="858637041960032120">Додај телефонÑки број</translation>
+<translation id="858637041960032120">Додајте телефонÑки број</translation>
<translation id="8589998999637048520">Ðајдобар квалитет</translation>
<translation id="8600271352425265729">Само овојпат</translation>
<translation id="860043288473659153">Име на ÑопÑтвеникот на картичката</translation>
@@ -2792,6 +2803,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> е вашиот код за <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Обележете ја картичкава</translation>
<translation id="8751426954251315517">Обидете Ñе повторно Ñледниот пат</translation>
+<translation id="8757526089434340176">ДоÑтапна е понуда на Google Pay</translation>
<translation id="8758885506338294482">Конкурентно играње видеоигри</translation>
<translation id="8759274551635299824">Картичкава е иÑтечена</translation>
<translation id="87601671197631245">Сајтов кориÑти заÑтарена конфигурација за безбедноÑÑ‚, што може да ги изложи вашите податоци (на пример, лозинките, пораките или кредитните картички) кога Ñе иÑпраќаат на овој Ñајт.</translation>
@@ -2799,6 +2811,7 @@
<translation id="8763927697961133303">USB-уред</translation>
<translation id="8763986294015493060">Затворете ги Ñите прозорци „Инкогнито“ што Ñе отворени во моментов</translation>
<translation id="8766943070169463815">ЛиÑтот за проверка на акредитиви за безбедно плаќање е отворен</translation>
+<translation id="8767765348545497220">Затворете го балончето за помош</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Мотори</translation>
<translation id="8790007591277257123">&amp;Повтори бришење</translation>
@@ -2811,6 +2824,7 @@
<translation id="8806285662264631610">Производи за бања и тело</translation>
<translation id="8807160976559152894">Кратење по Ñекоја Ñтраница</translation>
<translation id="8808828119384186784">ПоÑтавки за Chrome</translation>
+<translation id="8813277370772331957">ПотÑети ме подоцна</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, притиÑнете Tab, а потоа Enter за да го ажурирате Chrome од поÑтавките за Chrome</translation>
<translation id="8820817407110198400">Обележувачи</translation>
<translation id="882338992931677877">Отвор за рачно полнење</translation>
@@ -2990,6 +3004,7 @@
<translation id="988159990683914416">Верзија за програмер</translation>
<translation id="989988560359834682">Измени адреÑа</translation>
<translation id="991413375315957741">Ñензори за движење или Ñветлина</translation>
+<translation id="992110854164447044">Виртуелната картичка ја Ñокрива вашата актуелна картичка за да ја заштити од потенцијална измама. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Розова</translation>
<translation id="992432478773561401">Софтверот „<ph name="SOFTWARE_NAME" />“ не е правилно инÑталиран на компјутерот или мрежата:
diff --git a/chromium/components/strings/components_strings_ml.xtb b/chromium/components/strings/components_strings_ml.xtb
index f2a997ae065..a29ed03ed9d 100644
--- a/chromium/components/strings/components_strings_ml.xtb
+++ b/chromium/components/strings/components_strings_ml.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">à´—àµà´°à´¾à´¨àµà´±àµà´•à´³àµà´‚ à´¸àµà´•àµ‹à´³àµ¼à´·à´¿à´ªàµà´ªàµà´•à´³àµà´‚ സാമàµà´ªà´¤àµà´¤à´¿à´• സഹായവàµà´‚</translation>
<translation id="1048785276086539861">നിങàµà´™àµ¾ à´•àµà´±à´¿à´ªàµà´ªàµà´•àµ¾ à´Žà´¡à´¿à´±àµà´±àµ ചെയàµà´¯àµà´®àµà´ªàµ‹àµ¾, à´ˆ ഡോകàµà´¯àµà´®àµ†à´¨àµà´±àµ à´’à´±àµà´± പേജൠകാഴàµà´šà´¯à´¿à´²àµ‡à´•àµà´•àµ മടങàµà´™àµà´¨àµà´¨àµ</translation>
<translation id="1050038467049342496">മറàµà´±àµ ആപàµà´ªàµà´•àµ¾ à´…à´Ÿà´¯àµâ€Œà´•àµà´•àµà´•</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> ഉപയോഗികàµà´•àµà´¨àµà´¨ വെബàµà´¸àµˆà´±àµà´±àµà´•à´³à´¿àµ½ ഓതനàµà´±à´¿à´•àµà´•àµ‡à´±àµà´±àµ¼ ഉപകരണം ഉപയോഗിചàµà´šàµ പരിശോധിചàµà´šàµà´±à´ªàµà´ªà´¿à´•àµà´•à´¾àµ» നിങàµà´™àµ¾ തിരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤àµ. നിങàµà´™à´³àµà´Ÿàµ† പേയàµà´®àµ†à´¨àµà´±àµ രീതിയàµà´®à´¾à´¯à´¿ ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´Ÿ വിവരങàµà´™àµ¾ à´ˆ ദാതാവൠസംഭരിചàµà´šà´¿à´Ÿàµà´Ÿàµà´£àµà´Ÿà´¾à´•à´¾à´‚, നിങàµà´™àµ¾à´•àµà´•àµ ഇതൠ<ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;ചേർതàµà´¤à´¤àµ പഴയപടിയാകàµà´•àµà´•</translation>
<translation id="1056663316309890343">ഫോടàµà´Ÿàµ‹ സോഫàµâ€Œà´±àµà´±àµâ€Œà´µàµ†à´¯àµ¼</translation>
<translation id="1056898198331236512">à´®àµà´¨àµà´¨à´±à´¿à´¯à´¿à´ªàµà´ªàµ</translation>
@@ -101,7 +102,7 @@
<translation id="1243027604378859286">രചയിതാവàµ:</translation>
<translation id="1246424317317450637">ബോളàµâ€à´¡àµ</translation>
<translation id="1250759482327835220">à´…à´Ÿàµà´¤àµà´¤ à´ªàµà´°à´¾à´µà´¶àµà´¯à´‚ കൂടàµà´¤àµ½ വേഗതàµà´¤à´¿àµ½ പണമടയàµà´•àµà´•à´¾àµ», നിങàµà´™à´³àµà´Ÿàµ† Google à´…à´•àµà´•àµ—à´£àµà´Ÿà´¿àµ½ കാർഡàµ, പേരàµ, ബിലàµà´²à´¿à´‚ഗൠവിലാസം à´Žà´¨àµà´¨à´¿à´µ സംരകàµà´·à´¿à´•àµà´•àµà´•.</translation>
-<translation id="1252799212227771492">'ഷീറàµà´±àµ സൃഷàµà´Ÿà´¿à´•àµà´•àµà´•' ബടàµà´Ÿàµº, à´ªàµà´¤à´¿à´¯àµŠà´°àµ Google ഷീറàµà´±àµ വേഗതàµà´¤à´¿àµ½ സൃഷàµà´Ÿà´¿à´•àµà´•à´¾àµ» Enter അമർതàµà´¤àµà´•</translation>
+<translation id="1252799212227771492">'ഷീറàµà´±àµ സൃഷàµà´Ÿà´¿à´•àµà´•àµà´•' ബടàµà´Ÿàµº, à´ªàµà´¤à´¿à´¯àµŠà´°àµ Google Sheet വേഗതàµà´¤à´¿àµ½ സൃഷàµà´Ÿà´¿à´•àµà´•à´¾àµ» Enter അമർതàµà´¤àµà´•</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (സമനàµà´µà´¯à´¿à´ªàµà´ªà´¿à´šàµà´šàµ)</translation>
<translation id="1256368399071562588">&lt;p&gt;നിങàµà´™à´³àµŠà´°àµ വെബàµâ€Œà´¸àµˆà´±àµà´±àµ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´•àµà´•àµà´•à´¯àµà´‚ അതൠതàµà´±à´•àµà´•à´¾à´¤à´¿à´°à´¿à´•àµà´•àµà´•à´¯àµà´®à´¾à´£àµ†à´™àµà´•à´¿àµ½, ഇനിപàµà´ªà´±à´¯àµà´¨àµà´¨ à´Ÿàµà´°à´¬à´¿à´³àµâ€à´·àµ‚à´Ÿàµà´Ÿà´¿à´‚ഗൠഘടàµà´Ÿà´™àµà´™àµ¾ ഉപയോഗിചàµà´šàµ, à´ªàµà´°à´¶àµà´¨à´‚ പരിഹരികàµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´•àµà´•àµà´•:&lt;/p&gt;
&lt;ol&gt;
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">പികàµà´•à´ªàµà´ªàµ രീതി</translation>
<translation id="1281476433249504884">à´¸àµà´±àµà´±à´¾à´•àµà´•àµ¼ 1</translation>
<translation id="1285320974508926690">à´ˆ സൈറàµà´±àµ à´’à´°à´¿à´•àµà´•à´²àµà´‚ വിവരàµâ€â€Œà´¤àµà´¤à´¨à´‚ ചെയàµà´¯à´°àµà´¤àµ</translation>
+<translation id="1288548991597756084">കാർഡൠസàµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯à´¿ സംരകàµà´·à´¿à´•àµà´•àµ‚</translation>
<translation id="1292571435393770077">à´Ÿàµà´°àµ‡ 16</translation>
<translation id="1292701964462482250">"വെബിലേകàµà´•àµ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯à´¿ കണകàµâ€Œà´±àµà´±àµ ചെയàµà´¯àµà´¨àµà´¨à´¤à´¿àµ½ നിനàµà´¨àµ Chrome-നെ നിങàµà´™à´³àµà´Ÿàµ† à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´±à´¿à´²àµ† സോഫàµà´±àµà´±àµâ€Œà´µàµ†à´¯àµ¼ തടയàµà´¨àµà´¨àµ" (Windows à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´±àµà´•à´³à´¿àµ½ മാതàµà´°à´‚)</translation>
<translation id="1294154142200295408">കമാൻഡàµ-ലൈൻ à´µàµà´¯à´¤à´¿à´¯à´¾à´¨à´™àµà´™àµ¾</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;പിശകൠപരിഹരികàµà´•à´¾àµ», നിങàµà´™àµ¾ à´¤àµà´±à´•àµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´•àµà´•àµà´¨àµà´¨ പേജിൽ &lt;strong&gt;കണകàµâ€Œà´±àµà´±àµ ചെയàµà´¯àµà´•&lt;/strong&gt; à´•àµà´²à´¿à´•àµà´•àµ ചെയàµà´¯àµà´•.&lt;/p&gt;</translation>
<translation id="1507780850870535225">ലാൻഡàµâ€Œà´¸àµâ€Œà´•àµ‡à´ªàµà´ªàµ ഡിസൈൻ</translation>
<translation id="1513706915089223971">à´šà´°à´¿à´¤àµà´° എൻടàµà´°à´¿à´•à´³àµà´Ÿàµ† ലിസàµà´±àµà´±àµ</translation>
+<translation id="1516097932025103760">ഇതൠഎൻകàµà´°à´¿à´ªàµà´±àµà´±àµ ചെയàµà´¯àµà´‚, à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯à´¿ സംരകàµà´·à´¿à´•àµà´•àµà´‚, CVC à´’à´°à´¿à´•àµà´•à´²àµà´‚ സംഭരികàµà´•à´¿à´²àµà´².</translation>
<translation id="1517433312004943670">ഫോൺ നമàµà´ªàµ¼ ആവശàµà´¯à´®à´¾à´£àµ</translation>
<translation id="1519264250979466059">ബിൽഡൠതീയതി</translation>
<translation id="1521159554480556801">ഫൈബറàµà´‚ ടെകàµâ€Œà´¸àµâ€Œà´±àµà´±àµˆàµ½ ആർടàµà´Ÿàµà´¸àµà´‚</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">പേയàµâ€Œà´®àµ†à´¨àµà´±àµ രീതികൾ സംരകàµà´·à´¿à´šàµà´šàµ à´¸àµà´µà´®àµ‡à´§à´¯à´¾ പൂരിപàµà´ªà´¿à´•àµà´•àµà´•</translation>
<translation id="1663943134801823270">കാർഡàµà´•à´³àµà´‚ വിലാസങàµà´™à´³àµà´‚ Chrome-ൽ നിനàµà´¨àµà´³àµà´³à´¤à´¾à´£àµ. നിങàµà´™àµ¾à´•àµà´•àµ à´…à´µ <ph name="BEGIN_LINK" />à´•àµà´°à´®àµ€à´•à´°à´£à´¤àµà´¤à´¿àµ½<ph name="END_LINK" /> മാനേജൠചെയàµà´¯à´¾à´‚.</translation>
<translation id="1671391448414634642">ഇനിമàµà´¤àµ½ <ph name="SOURCE_LANGUAGE" /> ഭാഷയിലàµà´³àµà´³ പേജàµà´•àµ¾ <ph name="TARGET_LANGUAGE" /> ഭാഷയിലേകàµà´•àµ വിവർതàµà´¤à´¨à´‚ ചെയàµà´¯à´ªàµà´ªàµ†à´Ÿàµà´‚.</translation>
+<translation id="1673886523110456987">ഓഫർ ഉപയോഗികàµà´•à´¾àµ» <ph name="CARD_DETAIL" /> à´Žà´¨àµà´¨à´¤àµà´®à´¾à´¯à´¿ പരിശോധികàµà´•àµà´•</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> à´Žà´¨àµà´¨à´¤à´¿àµ½ നിനàµà´¨àµ <ph name="TARGET_LANGUAGE" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ‡à´•àµà´•àµ</translation>
<translation id="1682696192498422849">നീളം à´•àµà´±à´žàµà´ž അരികൠആദàµà´¯à´‚</translation>
<translation id="168693727862418163">à´ˆ നയ മൂലàµà´¯à´‚ അതിനàµà´±àµ† à´¸àµâ€Œà´•àµ€à´®à´¯àµà´®à´¾à´¯à´¿ സാധൂകരികàµà´•à´¾àµ» കഴിയാതàµà´¤à´¤à´¿à´¨à´¾àµ½ അതൠഅവഗണികàµà´•à´ªàµà´ªàµ†à´Ÿàµà´‚.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">ചലന സെൻസറàµà´•àµ¾</translation>
<translation id="1717494416764505390">മെയിൽബോകàµà´¸àµ 3</translation>
<translation id="1718029547804390981">അനോടàµà´Ÿàµ‡à´±àµà´±àµ ചെയàµà´¯à´¾à´¨à´¾à´µàµà´¨àµà´¨à´¤à´¿à´²àµà´‚ വളരെ വലàµà´¤à´¾à´£àµ à´ˆ ഡോകàµà´¯àµà´®àµ†àµ»àµà´±àµ</translation>
+<translation id="1720941539803966190">à´Ÿàµà´¯àµ‚à´Ÿàµà´Ÿàµ‹à´±à´¿à´¯àµ½ à´…à´Ÿà´¯àµâ€Œà´•àµà´•àµà´•</translation>
<translation id="1721424275792716183">* ഫീൽഡൠആവശàµà´¯à´®à´¾à´£àµ</translation>
<translation id="1727613060316725209">സാധàµà´¤à´¯àµà´³àµà´³ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¾à´£àµ</translation>
<translation id="1727741090716970331">സാധàµà´µà´¾à´¯ കാർഡൠനമàµà´ªàµ¼ ചേർകàµà´•àµà´•</translation>
@@ -393,7 +398,7 @@
<translation id="1959001866257244765">നിങàµà´™àµ¾ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•àµà´¨àµà´¨ à´šà´¿à´² പേജàµà´•à´³àµà´Ÿàµ† <ph name="BEGIN_WHITEPAPER_LINK" />URL, പരിമിത സിസàµâ€Œà´±àµà´±à´‚ വിവരങàµà´™àµ¾, à´šà´¿à´² പേജàµà´•à´³àµà´Ÿàµ† ഉളàµà´³à´Ÿà´•àµà´•à´‚<ph name="END_WHITEPAPER_LINK" /> à´Žà´¨àµà´¨à´¿à´µ Google-ലേകàµà´•àµ അയചàµà´šàµ à´Žà´²àµà´²à´¾à´µàµ¼à´•àµà´•àµà´®à´¾à´¯à´¿ വെബിലെ à´¸àµà´°à´•àµà´· മെചàµà´šà´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¾àµ» സഹായികàµà´•àµà´•. <ph name="BEGIN_PRIVACY_PAGE_LINK" />à´¸àµà´µà´•à´¾à´°àµà´¯à´¤à´¾ നയം<ph name="END_PRIVACY_PAGE_LINK" /></translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> à´¬àµà´•àµà´•àµâ€Œà´®à´¾àµ¼à´•àµà´•àµà´•àµ¾</translation>
<translation id="1973335181906896915">സീരിയലൈസേഷൻ പിശകàµ</translation>
-<translation id="1974060860693918893">നൂതനം</translation>
+<translation id="1974060860693918893">വിപàµà´²à´®à´¾à´¯à´¤àµ</translation>
<translation id="1975457531113383421">ഇൻപàµà´Ÿàµà´Ÿàµ à´Ÿàµà´°àµ‡</translation>
<translation id="1975584088563498795">മെയിൽബോകàµà´¸àµ 10</translation>
<translation id="1978555033938440688">ഫേംവെയർ പതിപàµà´ªàµ</translation>
@@ -421,8 +426,8 @@
<translation id="205212645995975601">BBQ-à´µàµà´‚ à´—àµà´°à´¿à´²àµà´²à´¿à´‚à´—àµà´‚</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> ഭാഷയിലàµà´³àµà´³ പേജàµà´•àµ¾ വിവർതàµà´¤à´¨à´‚ ചെയàµà´¯à´¿à´²àµà´².</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{à´ˆ നിയനàµà´¤àµà´°à´£à´‚ ഓണായിരികàµà´•àµà´•à´¯àµà´‚ നില 'സജീവം' ആയിരികàµà´•àµà´•à´¯àµà´‚ ആണെങàµà´•à´¿àµ½, നിങàµà´™à´³àµà´Ÿàµ† à´…à´Ÿàµà´¤àµà´¤à´¿à´Ÿàµ†à´¯àµà´³àµà´³ à´¬àµà´°àµ—സിംഗൠആകàµà´±àµà´±à´¿à´µà´¿à´±àµà´±à´¿à´¯àµà´®à´¾à´¯à´¿ à´à´±àµà´±à´µàµà´‚ സാമàµà´¯à´®àµà´³àµà´³ ആളàµà´•à´³àµà´Ÿàµ† à´—àµà´°àµ‚à´ªàµà´ªàµ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ "സമാന വിഭാഗതàµà´¤à´¿àµ½à´ªàµà´ªàµ†à´Ÿàµà´Ÿà´µàµ¼" à´à´¤à´¾à´£àµ à´Žà´¨àµà´¨àµ Chrome നിർണàµà´£à´¯à´¿à´•àµà´•àµà´¨àµà´¨àµ. പരസàµà´¯à´¦à´¾à´¤à´¾à´•àµà´•àµ¾à´•àµà´•àµ à´—àµà´°àµ‚à´ªàµà´ªà´¿à´¨àµ† കാണികàµà´•à´¾àµ» പരസàµà´¯à´™àµà´™àµ¾ തിരഞàµà´žàµ†à´Ÿàµà´•àµà´•à´¾à´¨àµà´‚ നിങàµà´™à´³àµà´Ÿàµ† à´¬àµà´°àµ—സിംഗൠആകàµà´±àµà´±à´¿à´µà´¿à´±àµà´±à´¿ ഉപകരണതàµà´¤à´¿àµ½ à´¸àµà´µà´•à´¾à´°àµà´¯à´®à´¾à´¯à´¿ സൂകàµà´·à´¿à´•àµà´•à´¾à´¨àµà´®à´¾à´•àµà´‚. നിങàµà´™à´³àµà´Ÿàµ† à´—àµà´°àµ‚à´ªàµà´ªàµ à´Žà´²àµà´²à´¾ ദിവസവàµà´‚ à´…à´ªàµâ€Œà´¡àµ‡à´±àµà´±àµ ചെയàµà´¯àµà´¨àµà´¨àµ.}=1{à´ˆ നിയനàµà´¤àµà´°à´£à´‚ ഓണായിരികàµà´•àµà´•à´¯àµà´‚ നില 'സജീവം' ആയിരികàµà´•àµà´•à´¯àµà´‚ ആണെങàµà´•à´¿àµ½, നിങàµà´™à´³àµà´Ÿàµ† à´…à´Ÿàµà´¤àµà´¤à´¿à´Ÿàµ†à´¯àµà´³àµà´³ à´¬àµà´°àµ—സിംഗൠആകàµà´±àµà´±à´¿à´µà´¿à´±àµà´±à´¿à´¯àµà´®à´¾à´¯à´¿ à´à´±àµà´±à´µàµà´‚ സാമàµà´¯à´®àµà´³àµà´³ ആളàµà´•à´³àµà´Ÿàµ† à´—àµà´°àµ‚à´ªàµà´ªàµ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ "സമാന വിഭാഗതàµà´¤à´¿àµ½à´ªàµà´ªàµ†à´Ÿàµà´Ÿà´µàµ¼" à´à´¤à´¾à´£àµ à´Žà´¨àµà´¨àµ Chrome നിർണàµà´£à´¯à´¿à´•àµà´•àµà´¨àµà´¨àµ. പരസàµà´¯à´¦à´¾à´¤à´¾à´•àµà´•àµ¾à´•àµà´•àµ à´—àµà´°àµ‚à´ªàµà´ªà´¿à´¨àµ† കാണികàµà´•à´¾àµ» പരസàµà´¯à´™àµà´™àµ¾ തിരഞàµà´žàµ†à´Ÿàµà´•àµà´•à´¾à´¨àµà´‚ നിങàµà´™à´³àµà´Ÿàµ† à´¬àµà´°àµ—സിംഗൠആകàµà´±àµà´±à´¿à´µà´¿à´±àµà´±à´¿ ഉപകരണതàµà´¤à´¿àµ½ à´¸àµà´µà´•à´¾à´°àµà´¯à´®à´¾à´¯à´¿ സൂകàµà´·à´¿à´•àµà´•à´¾à´¨àµà´®à´¾à´•àµà´‚. നിങàµà´™à´³àµà´Ÿàµ† à´—àµà´°àµ‚à´ªàµà´ªàµ à´Žà´²àµà´²à´¾ ദിവസവàµà´‚ à´…à´ªàµâ€Œà´¡àµ‡à´±àµà´±àµ ചെയàµà´¯àµà´¨àµà´¨àµ.}other{à´ˆ നിയനàµà´¤àµà´°à´£à´‚ ഓണായിരികàµà´•àµà´•à´¯àµà´‚ നില 'സജീവം' ആയിരികàµà´•àµà´•à´¯àµà´‚ ആണെങàµà´•à´¿àµ½, നിങàµà´™à´³àµà´Ÿàµ† à´…à´Ÿàµà´¤àµà´¤à´¿à´Ÿàµ†à´¯àµà´³àµà´³ à´¬àµà´°àµ—സിംഗൠആകàµà´±àµà´±à´¿à´µà´¿à´±àµà´±à´¿à´¯àµà´®à´¾à´¯à´¿ à´à´±àµà´±à´µàµà´‚ സാമàµà´¯à´®àµà´³àµà´³ ആളàµà´•à´³àµà´Ÿàµ† à´—àµà´°àµ‚à´ªàµà´ªàµ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ "സമാന വിഭാഗതàµà´¤à´¿àµ½à´ªàµà´ªàµ†à´Ÿàµà´Ÿà´µàµ¼" à´à´¤à´¾à´£àµ à´Žà´¨àµà´¨àµ Chrome നിർണàµà´£à´¯à´¿à´•àµà´•àµà´¨àµà´¨àµ. പരസàµà´¯à´¦à´¾à´¤à´¾à´•àµà´•àµ¾à´•àµà´•àµ à´—àµà´°àµ‚à´ªàµà´ªà´¿à´¨àµ† കാണികàµà´•à´¾àµ» പരസàµà´¯à´™àµà´™àµ¾ തിരഞàµà´žàµ†à´Ÿàµà´•àµà´•à´¾à´¨àµà´‚ നിങàµà´™à´³àµà´Ÿàµ† à´¬àµà´°àµ—സിംഗൠആകàµà´±àµà´±à´¿à´µà´¿à´±àµà´±à´¿ ഉപകരണതàµà´¤à´¿àµ½ à´¸àµà´µà´•à´¾à´°àµà´¯à´®à´¾à´¯à´¿ സൂകàµà´·à´¿à´•àµà´•à´¾à´¨àµà´®à´¾à´•àµà´‚. നിങàµà´™à´³àµà´Ÿàµ† à´—àµà´°àµ‚à´ªàµà´ªàµ ഓരോ {NUM_DAYS} ദിവസം കൂടàµà´®àµà´ªàµ‹à´´àµà´‚ à´…à´ªàµâ€Œà´¡àµ‡à´±àµà´±àµ ചെയàµà´¯àµà´¨àµà´¨àµ.}}</translation>
-<translation id="2053553514270667976">തപാൽ കോഡàµ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{ഒരൠനിർദàµà´¦àµ‡à´¶à´‚}other{# നിർദàµà´¦àµ‡à´¶à´™àµà´™àµ¾}}</translation>
+<translation id="2066915425250589881">ഇലàµà´²à´¾à´¤à´¾à´•àµà´•à´¾àµ» à´…à´­àµà´¯àµ¼à´¤àµà´¥à´¿à´•àµà´•à´¾à´µàµà´¨àµà´¨à´¤à´¾à´£àµ</translation>
<translation id="2068528718802935086">ശിശàµà´•àµà´•à´³àµà´‚ പിചàµà´šà´µà´¯àµà´•àµà´•àµà´¨àµà´¨ à´•àµà´Ÿàµà´Ÿà´¿à´•à´³àµà´‚</translation>
<translation id="2071156619270205202">à´ˆ കാർഡൠവെർചàµà´µàµ½ കാർഡൠനമàµà´ªà´±à´¿à´¨àµ യോഗàµà´¯à´®à´²àµà´².</translation>
<translation id="2071692954027939183">നിങàµà´™àµ¾ സാധാരണയായി അറിയിപàµà´ªàµà´•àµ¾ à´…à´¨àµà´µà´¦à´¿à´•àµà´•à´¾à´¤àµà´¤à´¤à´¿à´¨à´¾àµ½ à´…à´µ à´¸àµà´µà´¯à´®àµ‡à´µ à´¬àµà´²àµ‹à´•àµà´•àµ ചെയàµà´¤à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ</translation>
@@ -434,7 +439,6 @@
<translation id="2088086323192747268">സമനàµà´µà´¯à´¿à´ªàµà´ªà´¿à´•àµà´•àµ½ ബടàµà´Ÿàµº മാനേജൠചെയàµà´¯àµà´•, Chrome à´•àµà´°à´®àµ€à´•à´°à´£à´¤àµà´¤à´¿àµ½ നിങàµà´™àµ¾ സമനàµà´µà´¯à´¿à´ªàµà´ªà´¿à´•àµà´•àµà´¨àµà´¨ വിവരങàµà´™àµ¾ മാനേജൠചെയàµà´¯à´¾àµ» 'Enter' അമർതàµà´¤àµà´•</translation>
<translation id="2091887806945687916">ശബàµâ€Œà´¦à´‚</translation>
<translation id="2094505752054353250">ഡൊമെയàµâ€Œàµ» പൊരàµà´¤àµà´¤à´®à´¿à´²àµà´²à´¾à´¯àµâ€Œà´®</translation>
-<translation id="2096368010154057602">വകàµà´ªàµà´ªàµ</translation>
<translation id="2099652385553570808">ഇടതàµà´µà´¶à´¤àµà´¤àµ മൂനàµà´¨àµ തവണ à´¸àµâ€Œà´±àµà´±àµ‡à´ªàµà´ªà´¿àµ¾ ചെയàµà´¯àµà´•</translation>
<translation id="2101225219012730419">പതിപàµà´ªàµ:</translation>
<translation id="2102134110707549001">ശകàµà´¤à´®à´¾à´¯ പാസàµâ€Œà´µàµ‡à´¡àµ നിർദàµà´¦àµ‡à´¶à´¿à´•àµà´•àµà´•â€¦</translation>
@@ -471,7 +475,6 @@
<translation id="2185836064961771414">അമേരികàµà´•àµ» à´«àµà´Ÿàµà´¬àµ‹àµ¾</translation>
<translation id="2187317261103489799">à´•à´£àµà´Ÿàµ†à´¤àµà´¤àµà´• (ഡിഫോൾടàµà´Ÿàµ)</translation>
<translation id="2188375229972301266">താഴെ à´’à´¨àµà´¨à´¿à´²à´§à´¿à´•à´‚ പഞàµà´šàµ ചെയàµà´¯àµà´•</translation>
-<translation id="2188852899391513400">നിങàµà´™àµ¾ ഇപàµà´ªàµ‹àµ¾ ഉപയോഗിചàµà´š പാസàµâ€Œà´µàµ‡à´¡àµ ഡാറàµà´±à´¾ ലംഘനതàµà´¤à´¿àµ½ à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ. നിങàµà´™à´³àµà´Ÿàµ† à´…à´•àµà´•àµ—à´£àµà´Ÿàµà´•àµ¾ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´•àµà´•à´¾àµ», ഇപàµà´ªàµ‹àµ¾ തനàµà´¨àµ† à´…à´µ മാറàµà´±à´¾à´¨àµà´‚ സംരകàµà´·à´¿à´šàµà´š പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾ പരിശോധികàµà´•à´¾à´¨àµà´‚ Google പാസàµâ€Œà´µàµ‡à´¡àµ മാനേജർ നിർദàµà´¦àµ‡à´¶à´¿à´•àµà´•àµà´¨àµà´¨àµ.</translation>
<translation id="219906046732893612">വീടൠനവീകരണം</translation>
<translation id="2202020181578195191">കാലഹരണപàµà´ªàµ†à´Ÿàµà´¨àµà´¨ ശരിയായ വർഷം നലàµâ€à´•àµà´•</translation>
<translation id="22081806969704220">à´Ÿàµà´°àµ‡ 3</translation>
@@ -482,6 +485,8 @@
<translation id="2215727959747642672">ഫയൽ à´Žà´¡à´¿à´±àµà´±àµ ചെയàµà´¯àµ½</translation>
<translation id="2215963164070968490">നായകൾ</translation>
<translation id="2218879909401188352">നിലവിൽ {<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµà´³àµà´³ à´…à´•àµà´°à´®à´¿à´•àµ¾à´•àµà´•àµ നിങàµà´™à´³àµà´Ÿàµ† ഉപകരണം കേടàµà´µà´°àµà´¤àµà´¤àµà´¨àµà´¨ ആപàµà´ªàµà´•àµ¾ ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¯à´¾à´¨àµ‹ മൊബൈൽ ബിലàµà´²à´¿à´²àµ‡à´•àµà´•àµ നിങàµà´™à´³à´±à´¿à´¯à´¾à´¤àµ† നിരകàµà´•àµà´•àµ¾ ചേർകàµà´•à´¾à´¨àµ‹ നിങàµà´™à´³àµà´Ÿàµ† à´µàµà´¯à´•àµà´¤à´¿à´ªà´°à´®à´¾à´¯ വിവരങàµà´™àµ¾ മോഷàµâ€Œà´Ÿà´¿à´•àµà´•à´¾à´¨àµ‹ à´•à´´à´¿à´¯àµà´‚. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">à´Ÿàµà´¯àµ‚à´Ÿàµà´Ÿàµ‹à´±à´¿à´¯àµ½ റീസàµà´±àµà´±à´¾àµ¼à´Ÿàµà´Ÿàµ ചെയàµà´¯àµà´•</translation>
+<translation id="2219735899272417925">ഉപകരണ റീസെറàµà´±àµ ആവശàµà´¯à´®à´¾à´£àµ</translation>
<translation id="2224337661447660594">ഇനàµà´±àµ¼à´¨àµ†à´±àµà´±àµ ഇലàµà´²</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />ഡയഗണോസàµâ€Œà´±àµà´±à´¿à´•àµâ€Œà´¸àµ ആപàµà´ªàµ<ph name="END_LINK" /> ഉപയോഗിചàµà´šàµ കണകഷൻ à´ªàµà´°à´¶àµâ€Œà´¨à´‚ പരിഹരികàµà´•àµà´•</translation>
<translation id="2239100178324503013">ഇപàµà´ªàµ‹àµ¾ അയയàµâ€Œà´•àµà´•àµà´•</translation>
@@ -579,11 +584,13 @@
<translation id="2512101340618156538">à´…à´¨àµà´µà´¦à´¨àµ€à´¯à´®à´²àµà´² (ഡിഫോൾടàµà´Ÿàµ)</translation>
<translation id="2512413427717747692">ഡിഫോൾടàµà´Ÿàµ à´¬àµà´°àµ—സർ ബടàµà´Ÿà´£à´¾à´¯à´¿ Chrome സജàµà´œàµ€à´•à´°à´¿à´•àµà´•àµà´•, iOS à´•àµà´°à´®àµ€à´•à´°à´£à´¤àµà´¤à´¿àµ½ സിസàµà´±àµà´±à´¤àµà´¤à´¿à´¨àµà´±àµ† ഡിഫോൾടàµà´Ÿàµ à´¬àµà´°àµ—സറായി Chrome സജàµà´œàµ€à´•à´°à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ Enter അമർതàµà´¤àµà´•</translation>
<translation id="2515629240566999685">നിങàµà´™à´³àµà´Ÿàµ† à´à´°à´¿à´¯à´¯à´¿à´²àµ† സിഗàµâ€Œà´¨àµ½ പരിശോധികàµà´•àµà´¨àµà´¨àµ</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> ഉപയോഗികàµà´•àµà´¨àµà´¨ വെബàµà´¸àµˆà´±àµà´±àµà´•à´³à´¿àµ½ Touch ID ഉപയോഗിചàµà´šàµ പരിശോധിചàµà´šàµà´±à´ªàµà´ªà´¿à´•àµà´•à´¾àµ» നിങàµà´™àµ¾ തിരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤àµ. നിങàµà´™à´³àµà´Ÿàµ† പേയàµà´®àµ†à´¨àµà´±àµ രീതിയàµà´®à´¾à´¯à´¿ ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´Ÿ വിവരങàµà´™àµ¾ à´ˆ ദാതാവൠസംഭരിചàµà´šà´¿à´Ÿàµà´Ÿàµà´£àµà´Ÿà´¾à´•à´¾à´‚, നിങàµà´™àµ¾à´•àµà´•àµ ഇതൠ<ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">താഴെ വലതàµà´µà´¶à´¤àµà´¤àµ à´¸àµâ€Œà´±àµà´±àµ‡à´ªàµà´ªà´¿àµ¾ ചെയàµà´¯àµà´•</translation>
<translation id="2521736961081452453">ഫോം സൃഷàµà´Ÿà´¿à´•àµà´•àµà´•</translation>
<translation id="2523886232349826891">à´ˆ ഉപകരണതàµà´¤à´¿àµ½ മാതàµà´°à´‚ സംരകàµà´·à´¿à´•àµà´•à´ªàµà´ªàµ†à´Ÿàµà´‚</translation>
<translation id="2524461107774643265">കൂടàµà´¤àµ½ വിവരങàµà´™àµ¾ ചേർകàµà´•àµà´•</translation>
<translation id="2529899080962247600">à´ˆ ഫീൽഡിൽ <ph name="MAX_ITEMS_LIMIT" />-ൽ കൂടàµà´¤àµ½ എൻ‌ടàµà´°à´¿à´•àµ¾ ഉണàµà´Ÿà´¾à´•à´°àµà´¤àµ. à´¤àµà´Ÿàµ¼à´¨àµà´¨àµà´³àµà´³ à´Žà´²àµà´²à´¾ എൻ‌ടàµà´°à´¿à´•à´³àµà´‚ നിരസികàµà´•àµà´‚.</translation>
+<translation id="253493526287553278">à´ªàµà´°à´®àµ‹ കോഡൠവിശദാംശങàµà´™àµ¾ കാണàµà´•</translation>
<translation id="2535585790302968248">à´¸àµà´µà´•à´¾à´°àµà´¯à´®à´¾à´¯à´¿ à´¬àµà´°àµ—സൠചെയàµà´¯à´¾àµ» à´ªàµà´¤à´¿à´¯ അദൃശàµà´¯ ടാബൠതàµà´±à´•àµà´•àµà´•</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{à´Žà´¨àµà´¨à´¤àµà´‚ മറàµà´±àµŠà´°àµ†à´£àµà´£à´µàµà´‚}other{à´Žà´¨àµà´¨à´¤àµà´‚ മറàµà´±àµ # à´Žà´£àµà´£à´µàµà´‚}}</translation>
<translation id="2536110899380797252">വിലാസം ചേർകàµà´•àµà´•</translation>
@@ -619,7 +626,6 @@
<translation id="259821504105826686">ഫോടàµà´Ÿàµ‹à´—àµà´°à´¾à´«à´¿à´•àµ, ഡിജിറàµà´±àµ½ ആർടàµà´Ÿàµà´¸àµ</translation>
<translation id="2601150049980261779">à´ªàµà´°à´£à´¯ സിനിമകൾ</translation>
<translation id="2604589665489080024">പോപàµà´ªàµ സംഗീതം</translation>
-<translation id="2609632851001447353">വേരിയേഷനàµà´•àµ¾</translation>
<translation id="2610561535971892504">പകർതàµà´¤à´¾àµ» à´•àµà´²à´¿à´•àµà´•àµ ചെയàµà´¯àµà´•</translation>
<translation id="2617988307566202237">ഇനിപàµà´ªà´±à´¯àµà´¨àµà´¨ വിവരങàµà´™àµ¾ Chrome <ph name="BEGIN_EMPHASIS" />സംരകàµà´·à´¿à´•àµà´•à´¿à´²àµà´²<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -652,6 +658,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">ജനàµà´®à´¦à´¿à´¨à´™àµà´™à´³àµà´‚ പേരിടൽ ദിവസങàµà´™à´³àµà´‚</translation>
<translation id="2677748264148917807">ഉപേകàµà´·à´¿à´•àµà´•àµà´•</translation>
+<translation id="2679714844901977852">à´¸àµà´°à´•àµà´·à´¿à´¤à´µàµà´‚ വേഗതàµà´¤à´¿à´²àµà´³àµà´³à´¤àµà´®à´¾à´¯ ചെകàµà´•àµà´”à´Ÿàµà´Ÿàµà´•àµ¾à´•àµà´•àµ, നിങàµà´™à´³àµà´Ÿàµ† കാർഡàµà´‚ ബിലàµà´²à´¿à´‚ഗൠവിവരങàµà´™à´³àµà´‚ <ph name="USER_EMAIL" /> à´Žà´¨àµà´¨ Google à´…à´•àµà´•àµ—à´£àµà´Ÿà´¿àµ½ സംരകàµà´·à´¿à´•àµà´•àµà´•</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">à´à´±àµà´±à´µàµà´‚ à´…à´¨àµà´¯àµ‹à´œàµà´¯à´®à´¾à´¯à´¤àµ</translation>
<translation id="2688969097326701645">ഉവàµà´µàµ, à´¤àµà´Ÿà´°àµà´•</translation>
@@ -700,7 +707,6 @@
<translation id="2854764410992194509">ഇനàµà´±à´°àµâ€à´¨àµ†à´±àµà´±àµ സേവന ദാതാകàµà´•àµ¾ (ISPs)</translation>
<translation id="2856444702002559011"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨à´¤à´¿àµ½ നിനàµà´¨àµ നിങàµà´™à´³àµà´Ÿàµ† വിവരം മോഷàµâ€Œà´Ÿà´¿à´•àµà´•à´¾àµ» à´…à´•àµà´°à´®à´¿à´•àµ¾ à´¶àµà´°à´®à´¿à´•àµà´•àµà´¨àµà´¨àµà´£àµà´Ÿà´¾à´µà´¾à´‚ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾, സനàµà´¦àµ‡à´¶à´™àµà´™àµ¾, à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•àµ¾). <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">à´ˆ സൈറàµà´±àµ, അനാവശàµà´¯à´®àµ‹ തെറàµà´±à´¿à´¦àµà´§à´°à´¿à´ªàµà´ªà´¿à´•àµà´•àµà´¨àµà´¨à´¤àµ‹ ആയ പരസàµà´¯à´™àµà´™àµ¾ കാണികàµà´•àµà´¨àµà´¨àµ.</translation>
-<translation id="286512204874376891">വഞàµà´šà´¿à´•àµà´•à´ªàµà´ªàµ†à´Ÿà´¾à´¨àµà´³àµà´³ സാധàµà´¯à´¤à´•à´³à´¿àµ½ നിനàµà´¨àµ നിങàµà´™à´³àµ† പരിരകàµà´·à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ വെർചàµà´µàµ½ കാർഡൠനിങàµà´™à´³àµà´Ÿàµ† യഥാർതàµà´¥ കാർഡൠവിവരങàµà´™àµ¾ മറയàµà´•àµà´•àµà´¨àµà´¨àµ. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">സൗഹാർദàµà´¦à´ªà´°à´‚</translation>
<translation id="28761159517501904">സിനിമകൾ</translation>
<translation id="2876489322757410363">ബാഹàµà´¯ ആപàµà´ªàµ വഴി പണമടയàµâ€Œà´•àµà´•à´¾àµ» അദൃശàµà´¯ മോഡിൽ നിനàµà´¨àµ à´ªàµà´±à´¤àµà´¤àµà´•à´Ÿà´•àµà´•àµà´¨àµà´¨àµ. à´¤àµà´Ÿà´°à´£àµ‹?</translation>
@@ -801,7 +807,6 @@
<translation id="3158539265159265653">à´¡à´¿à´¸àµâ€Œà´•àµ</translation>
<translation id="3162559335345991374">നിങàµà´™àµ¾ ഉപയോഗികàµà´•àµà´¨àµà´¨ Wi-Fi അതിനàµà´±àµ† ലോഗിൻ പേജൠസനàµà´¦àµ¼à´¶à´¿à´•àµà´•à´¾àµ» നിങàµà´™à´³àµ‹à´Ÿàµ ആവശàµà´¯à´ªàµà´ªàµ†à´Ÿà´¾à´‚.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">à´à´²à´¨àµâ€à´¡àµ</translation>
<translation id="3176929007561373547">à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ സെർവർ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¿à´•àµà´•àµà´¨àµà´¨àµà´µàµ†à´¨àµà´¨àµ ഉറപàµà´ªà´¾à´•àµà´•à´¾àµ»
നിങàµà´™à´³àµà´Ÿàµ† à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ à´•àµà´°à´®àµ€à´•à´°à´£à´‚ പരിശോധികàµà´•àµà´•à´¯àµ‹ നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ à´…à´¡àµâ€Œà´®à´¿à´¨à´¿à´¸àµâ€Œà´Ÿàµà´°àµ‡à´±àµà´±à´±àµ† ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´•à´¯àµ‹ ചെയàµà´¯àµà´•. നിങàµà´™àµ¾ ഉപയോഗികàµà´•àµà´¨àµà´¨à´¤àµ ഒരൠപàµà´°àµ‹à´•àµâ€Œà´¸à´¿ സെർവറാണെനàµà´¨àµ à´•à´°àµà´¤àµà´¨àµà´¨à´¿à´²àµà´²àµ†à´™àµà´•à´¿àµ½:
<ph name="PLATFORM_TEXT" /></translation>
@@ -879,9 +884,6 @@
<translation id="3369192424181595722">à´•àµà´²àµ‹à´•àµà´•à´¿à´²àµ† പിശകàµ</translation>
<translation id="3369459162151165748">വാഹന ഭാഗങàµà´™à´³àµà´‚ ആകàµâ€Œà´¸à´¸à´±à´¿à´•à´³àµà´‚</translation>
<translation id="3371064404604898522">ഡിഫോൾടàµà´Ÿàµ à´¬àµà´°àµ—സറായി Chrome സജàµà´œàµ€à´•à´°à´¿à´•àµà´•àµà´•</translation>
-<translation id="3371076217486966826">ഇനിപàµà´ªà´±à´¯àµà´¨àµà´¨à´¤àµ ചെയàµà´¯à´¾àµ» <ph name="URL" /> താൽപàµà´ªà´°àµà´¯à´ªàµà´ªàµ†à´Ÿàµà´¨àµà´¨àµ:
- • നിങàµà´™à´³àµà´Ÿàµ† à´šàµà´±àµà´±àµà´ªà´¾à´Ÿàµà´•à´³àµà´Ÿàµ† 3D മാപàµà´ªàµ സൃഷàµà´Ÿà´¿à´•àµà´•à´¾à´¨àµà´‚ à´•àµà´¯à´¾à´®à´±à´¯àµà´Ÿàµ† à´¸àµà´¥à´¾à´¨à´‚ à´Ÿàµà´°à´¾à´•àµà´•àµ ചെയàµà´¯à´¾à´¨àµà´‚
- • നിങàµà´™à´³àµà´Ÿàµ† à´•àµà´¯à´¾à´®à´± ഉപയോഗികàµà´•à´¾àµ»</translation>
<translation id="337363190475750230">à´¡à´¿à´ªàµà´°àµŠà´µà´¿à´·àµ» ചെയàµâ€Œà´¤àµ</translation>
<translation id="3375754925484257129">Chrome à´¸àµà´°à´•àµà´·à´¾ പരിശോധന റൺ ചെയàµà´¯àµà´•</translation>
<translation id="3377144306166885718">TLS-à´¨àµà´±àµ† കാലഹരണപàµà´ªàµ†à´Ÿàµà´Ÿ പതിപàµà´ªà´¾à´£àµ സെർവർ ഉപയോഗിചàµà´šà´¤àµ.</translation>
@@ -898,6 +900,7 @@
<translation id="3399952811970034796">ഡെലിവറി നൽകേണàµà´Ÿ വിലാസം</translation>
<translation id="3402261774528610252">à´ˆ സൈറàµà´±àµ ലോഡൠചെയàµà´¯à´¾àµ» ഉപയോഗിചàµà´š കണകàµà´·àµ» ഉപയോഗിചàµà´šà´¤àµ TLS 1.0 à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ TLS 1.1 പതിപàµà´ªà´¾à´£àµ, ഇവ അവസാനിപàµà´ªà´¿à´šàµà´šà´¤àµà´‚ ഭാവിയിൽ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´°à´¹à´¿à´¤à´®à´¾à´•àµà´•à´ªàµà´ªàµ†à´Ÿàµà´¨àµà´¨à´¤àµà´‚ ആണàµ. à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´°à´¹à´¿à´¤à´‚ ആയികàµà´•à´´à´¿à´žàµà´žà´¾àµ½, à´ˆ സൈറàµà´±àµ ലോഡൠചെയàµà´¯àµà´¨àµà´¨à´¤à´¿àµ½ നിനàµà´¨àµ ഉപയോകàµà´¤à´¾à´•àµà´•à´³àµ† തടയàµà´‚. TLS 1.2 à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ അതിനൠശേഷമàµà´³àµà´³ പതിപàµà´ªàµ സെർവർ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´•àµà´·à´®à´®à´¾à´•àµà´•à´£à´‚.</translation>
<translation id="3405664148539009465">ഫോണàµà´Ÿàµà´•à´³àµâ€ ഇചàµà´›à´¾à´¨àµà´¸àµƒà´¤à´®à´¾à´•àµà´•àµà´•</translation>
+<translation id="3407789382767355356">മൂനàµà´¨à´¾à´‚ à´•à´•àµà´·à´¿ സൈൻ ഇൻ</translation>
<translation id="3409896703495473338">à´¸àµà´°à´•àµà´·à´¾ à´•àµà´°à´®àµ€à´•à´°à´£à´‚ മാനേജൠചെയàµà´¯àµà´•</translation>
<translation id="3414952576877147120">വലàµà´ªàµà´ªà´‚:</translation>
<translation id="3417660076059365994">നിങàµà´™àµ¾ à´…à´ªàµâ€Œà´²àµ‹à´¡àµ ചെയàµà´¯àµà´¨àµà´¨ ഫയലàµà´•àµ¾ വിശകലനം ചെയàµà´¯àµà´¨àµà´¨à´¤à´¿à´¨àµ Google à´•àµà´²àµ—ഡിലേകàµà´•àµ‹ മൂനàµà´¨à´¾à´‚ à´•à´•àµà´·à´¿à´•àµ¾à´•àµà´•àµ‹ അയയàµà´•àµà´•àµà´¨àµà´¨àµ. ഉദാഹരണതàµà´¤à´¿à´¨àµ, സെൻസിറàµà´±àµ€à´µà´¾à´¯à´¿à´Ÿàµà´Ÿàµà´³àµà´³ à´µàµà´¯à´•àµà´¤à´¿à´ªà´°à´®à´¾à´¯ ഡാറàµà´±à´¯àµ‹ മാൽവെയറോ ഉണàµà´Ÿàµ‹à´¯àµ†à´¨àµà´¨à´±à´¿à´¯à´¾àµ» à´…à´µ à´¸àµâ€Œà´•à´¾àµ» ചെയàµâ€Œà´¤àµ‡à´•àµà´•à´¾à´‚.</translation>
@@ -930,6 +933,7 @@
<translation id="3477679029130949506">സിനിമാ വിവരങàµà´™à´³àµà´‚ തിയറàµà´±àµ¼ à´ªàµà´°à´¦àµ¼à´¶à´¨ സമയവàµà´‚</translation>
<translation id="3479552764303398839">ഇപàµà´ªàµ‹à´´à´²àµà´²</translation>
<translation id="3484560055331845446">നിങàµà´™à´³àµà´Ÿàµ† Google à´…à´•àµà´•àµ—à´£àµà´Ÿà´¿à´²àµ‡à´•àµà´•àµà´³àµà´³ ആകàµâ€Œà´¸à´¸àµ നഷàµâ€Œà´Ÿà´ªàµà´ªàµ†à´Ÿà´¾à´¨à´¿à´Ÿà´¯àµà´£àµà´Ÿàµ. ഇപàµà´ªàµ‹àµ¾ തനàµà´¨àµ† പാസàµâ€Œà´µàµ‡à´¡àµ മാറàµà´±à´¾àµ» Chrome à´¶àµà´ªà´¾àµ¼à´¶ ചെയàµà´¯àµà´¨àµà´¨àµ. സൈൻ ഇൻ ചെയàµà´¯à´¾àµ» നിങàµà´™à´³àµ‹à´Ÿàµ ആവശàµà´¯à´ªàµà´ªàµ†à´Ÿàµà´‚.</translation>
+<translation id="3484861421501147767">റിമൈൻഡർ: സംരകàµà´·à´¿à´šàµà´šà´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨ à´ªàµà´°à´®àµ‹ കോഡൠലഭàµà´¯à´®à´¾à´£àµ</translation>
<translation id="3487845404393360112">à´Ÿàµà´°àµ‡ 4</translation>
<translation id="3495081129428749620">പേജിൽ à´•à´£àµà´Ÿàµ†à´¤àµà´¤àµà´•
<ph name="PAGE_TITLE" /></translation>
@@ -1054,6 +1058,7 @@
<translation id="3810973564298564668">നിയനàµà´¤àµà´°à´¿à´•àµà´•àµ‚</translation>
<translation id="3816482573645936981">മൂലàµà´¯à´‚ (പകരം മറàµà´±àµŠà´¨àµà´¨àµ സജീവം)</translation>
<translation id="382518646247711829">നിങàµà´™àµ¾ ഒരൠപàµà´°àµ‹à´•àµâ€Œà´¸à´¿ സെർവർ ഉപയോഗികàµà´•àµà´¨àµà´¨àµ†à´™àµà´•à´¿àµ½...</translation>
+<translation id="3826050100957962900">മൂനàµà´¨à´¾à´‚ à´•à´•àµà´·à´¿ സൈൻ ഇൻ</translation>
<translation id="3827112369919217609">à´…à´¬àµâ€Œà´¸à´²àµà´¯àµ‚à´Ÿàµà´Ÿàµ</translation>
<translation id="3827666161959873541">à´•àµà´Ÿàµà´‚à´¬ സിനിമകൾ</translation>
<translation id="3828924085048779000">പാസàµà´«àµà´°àµ†à´¯àµâ€à´¸àµ പൂരിപàµà´ªà´¿à´•àµà´•à´¾à´¤à´¿à´°à´¿à´•àµà´•à´¾à´¨à´¾à´µà´¿à´²àµà´².</translation>
@@ -1066,9 +1071,9 @@
<translation id="3858027520442213535">തീയതിയàµà´‚ സമയവàµà´‚ à´…à´ªàµâ€Œà´¡àµ‡à´±àµà´±àµ ചെയàµà´¯àµà´•</translation>
<translation id="3858860766373142691">പേരàµ</translation>
<translation id="3872834068356954457">ശാസàµà´¤àµà´°à´‚</translation>
+<translation id="3875783148670536197">à´Žà´™àµà´™à´¨àµ†à´¯àµ†à´¨àµà´¨àµ à´Žà´¨àµà´¨àµ† കാണികàµà´•àµà´•</translation>
<translation id="3881478300875776315">à´•àµà´±à´šàµà´šàµ വരികൾ മാതàµà´°à´‚ കാണികàµà´•àµà´•</translation>
<translation id="3884278016824448484">വിരàµà´¦àµà´§ ഉപകരണ à´à´¡à´¨àµà´±à´¿à´«à´¯àµ¼</translation>
-<translation id="3885155851504623709">പാരിഷàµ</translation>
<translation id="388632593194507180">നിരീകàµà´·à´£à´‚ à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¿</translation>
<translation id="3886948180919384617">à´¸àµà´±àµà´±à´¾à´•àµà´•àµ¼ 3</translation>
<translation id="3890664840433101773">ഇമെയിലàµâ€â€Œ ചേരàµâ€â€Œà´•àµà´•àµà´•</translation>
@@ -1098,9 +1103,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> à´¬àµà´²àµ‹à´•àµà´•àµà´šàµ†à´¯àµâ€Œà´¤à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ</translation>
<translation id="3978338123949022456">തിരയൽ മോഡàµ, <ph name="KEYWORD_SUFFIX" /> ഉപയോഗിചàµà´šàµ തിരയാൻ തിരയൽ പദം ടൈപàµà´ªàµ ചെയàµà´¤ ശേഷം Enter അമർതàµà´¤àµà´•</translation>
<translation id="398470910934384994">പകàµà´·à´¿à´•àµ¾</translation>
+<translation id="3985750352229496475">വിലാസങàµà´™àµ¾ മാനേജൠചെയàµà´¯àµà´•...</translation>
<translation id="3986705137476756801">തതàµà´¸à´®à´¯ à´•àµà´¯à´¾à´ªàµà´·àµ» ഇപàµà´ªàµ‹à´´à´¤àµà´¤àµ‡à´•àµà´•àµ ഓഫാകàµà´•àµà´•</translation>
<translation id="3987940399970879459">ഒരൠMB-യിൽ à´•àµà´±à´µà´¾à´£àµ</translation>
<translation id="3990250421422698716">ജോഗൠഓഫàµâ€Œà´¸àµ†à´±àµà´±àµ</translation>
+<translation id="3992684624889376114">à´ˆ പേജിനെകàµà´•àµà´±à´¿à´šàµà´šàµ</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> à´Žà´¨àµà´¨ സൈറàµà´±à´¿à´²àµ‡à´•àµà´•àµà´³àµà´³ à´Žà´²àµà´²à´¾ à´…à´­àµà´¯àµ¼à´¤àµà´¥à´¨à´•àµ¾à´•àµà´•àµà´‚ ഒരൠഉറവിട നയം
ബാധകമാകàµà´®àµ†à´¨àµà´¨àµ ഇതൠഅഭàµà´¯àµ¼à´¤àµà´¥à´¿à´šàµà´šà´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ, à´Žà´¨àµà´¨à´¾àµ½ à´ˆ നയം ഇപàµà´ªàµ‹àµ¾ ബാധകമാകàµà´•à´¾à´¨à´¾à´µà´¿à´²àµà´².</translation>
<translation id="4006465311664329701">Google Pay ഉപയോഗികàµà´•àµà´¨àµà´¨ പേയàµà´®àµ†à´¨àµà´±àµ രീതികളàµà´‚ à´“à´«à´±àµà´•à´³àµà´‚ വിലാസങàµà´™à´³àµà´‚</translation>
@@ -1223,6 +1230,7 @@
<translation id="4305666528087210886">നിങàµà´™à´³àµà´Ÿàµ† ഫയൽ ആകàµâ€Œà´¸à´¸àµ ചെയàµà´¯à´¾à´¨à´¾à´¯à´¿à´²àµà´²</translation>
<translation id="4306529830550717874">വിലാസം സംരകàµà´·à´¿à´•àµà´•à´£àµ‹?</translation>
<translation id="4306812610847412719">à´•àµà´²à´¿à´ªàµà´ªàµà´¬àµ‹àµ¼à´¡àµ</translation>
+<translation id="4310070645992025887">നിങàµà´™à´³àµà´Ÿàµ† യാതàµà´°à´•àµ¾ തിരയàµà´•</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">à´¬àµà´²àµ‹à´•àµà´•àµ ചെയàµà´¯àµà´• (ഡിഫോൾടàµà´Ÿàµ)</translation>
<translation id="4314815835985389558">സമനàµà´µà´¯à´¿à´ªàµà´ªà´¿à´•àµà´•àµ½ മാനേജൠചെയàµà´¯àµà´•</translation>
@@ -1273,6 +1281,7 @@
<translation id="4435702339979719576">പോസàµâ€Œà´±àµà´±àµâ€Œ കാർഡàµ)</translation>
<translation id="443673843213245140">à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ ഉപയോഗം à´…à´ªàµà´°à´¾à´ªàµâ€Œà´¤à´®à´¾à´•àµà´•à´¿ പകàµà´·àµ† ഒരൠവàµà´¯à´•àµà´¤à´®à´¾à´¯ à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ കോൺഫിഗറേഷൻ നിർദàµà´¦àµ‡à´¶à´¿à´šàµà´šàµ.</translation>
<translation id="4441832193888514600">à´•àµà´²àµ—ഡൠഉപയോകàµà´¤àµƒ നയം ആയി മാതàµà´°à´®àµ‡ നയം സജàµà´œàµ€à´•à´°à´¿à´•àµà´•à´¾à´¨à´¾à´•àµ‚ à´Žà´¨àµà´¨à´¤à´¿à´¨à´¾àµ½ അവഗണികàµà´•àµ‚.</translation>
+<translation id="4442470707340296952">Chrome ടാബàµà´•àµ¾</translation>
<translation id="4450893287417543264">വീണàµà´Ÿàµà´‚ കാണികàµà´•à´°àµà´¤àµ</translation>
<translation id="4451135742916150903">HID ഉപകരണങàµà´™à´³à´¿à´²àµ‡à´•àµà´•àµ കണകàµâ€Œà´±àµà´±àµ ചെയàµà´¯à´¾àµ» ആവശàµà´¯à´ªàµà´ªàµ†à´Ÿà´¾à´‚</translation>
<translation id="4452328064229197696">നിങàµà´™àµ¾ ഇപàµà´ªàµ‹àµ¾ ഉപയോഗിചàµà´š പാസàµâ€Œà´µàµ‡à´¡àµ ഡാറàµà´±à´¾ ലംഘനതàµà´¤à´¿àµ½ à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ. നിങàµà´™à´³àµà´Ÿàµ† à´…à´•àµà´•àµ—à´£àµà´Ÿàµà´•àµ¾ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´•àµà´•à´¾àµ», സംരകàµà´·à´¿à´šàµà´š പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾ പരിശോധികàµà´•à´¾àµ» Google പാസàµâ€Œà´µàµ‡à´¡àµ മാനേജർ നിർദàµà´¦àµ‡à´¶à´¿à´•àµà´•àµà´¨àµà´¨àµ.</translation>
@@ -1406,11 +1415,12 @@
<translation id="4813512666221746211">നെറàµà´±àµâ€Œà´µà´°àµâ€à´•àµà´•àµ പിശകàµ</translation>
<translation id="4816492930507672669">പേജിനൠയàµà´•àµà´¤à´®à´¾à´•àµà´•àµà´•</translation>
<translation id="4819347708020428563">ഡിഫോൾടàµà´Ÿàµ കാഴàµâ€Œà´šà´¯à´¿àµ½ à´•àµà´±à´¿à´ªàµà´ªàµà´•àµ¾ à´Žà´¡à´¿à´±àµà´±àµ ചെയàµà´¯à´£àµ‹?</translation>
-<translation id="4825496307559726072"><ph name="CREATE_GOOGLE_SHEET_FOCUSED_FRIENDLY_MATCH_TEXT" />, à´ªàµà´¤à´¿à´¯àµŠà´°àµ Google ഷീറàµà´±àµ വേഗതàµà´¤à´¿àµ½ സൃഷàµà´Ÿà´¿à´•àµà´•à´¾àµ» Tab അമർതàµà´¤àµà´•, à´¤àµà´Ÿàµ¼à´¨àµà´¨àµ Enter അമർതàµà´¤àµà´•</translation>
+<translation id="4825496307559726072"><ph name="CREATE_GOOGLE_SHEET_FOCUSED_FRIENDLY_MATCH_TEXT" />, à´ªàµà´¤à´¿à´¯àµŠà´°àµ Google Sheet വേഗതàµà´¤à´¿àµ½ സൃഷàµà´Ÿà´¿à´•àµà´•à´¾àµ» Tab അമർതàµà´¤àµà´•, à´¤àµà´Ÿàµ¼à´¨àµà´¨àµ Enter അമർതàµà´¤àµà´•</translation>
<translation id="4825507807291741242">ശകàµà´¤à´®à´¾à´¯à´¤àµ</translation>
<translation id="483241715238664915">à´®àµà´¨àµà´¨à´±à´¿à´¯à´¿à´ªàµà´ªàµà´•àµ¾ ഓണാകàµà´•àµà´•</translation>
<translation id="4834250788637067901">Google Pay ഉപയോഗികàµà´•àµà´¨àµà´¨ പേയàµà´®àµ†à´¨àµà´±àµ രീതികളàµà´‚ à´“à´«à´±àµà´•à´³àµà´‚ വിലാസങàµà´™à´³àµà´‚</translation>
<translation id="4838327282952368871">മനോരാജàµà´¯à´¤àµà´¤à´¿àµ½ à´®àµà´´àµà´•à´¿à´¯à´¤àµ</translation>
+<translation id="4839087176073128681">à´…à´Ÿàµà´¤àµà´¤ തവണ കൂടàµà´¤àµ½ വേഗതàµà´¤à´¿àµ½ പണമടയàµà´•àµà´•àµà´•à´¯àµà´‚ Google-à´¨àµà´±àµ†, വിപണിയിൽ ലഭàµà´¯à´®à´¾à´¯ à´à´±àµà´±à´µàµà´‚ മികചàµà´š à´¸àµà´°à´•àµà´· ഉപയോഗിചàµà´šàµ കാർഡൠപരിരകàµà´·à´¿à´•àµà´•àµà´•à´¯àµà´‚ ചെയàµà´¯àµà´•.</translation>
<translation id="4840250757394056958">നിങàµà´™à´³àµà´Ÿàµ† Chrome à´šà´°à´¿à´¤àµà´°à´‚ കാണàµà´•</translation>
<translation id="484462545196658690">à´¸àµà´µà´¯à´®àµ‡à´µ</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµà´‚ മറàµà´±àµà´‚ à´•à´¿à´´à´¿à´µàµà´•àµ¾ നേടàµà´•</translation>
@@ -1473,6 +1483,7 @@
<translation id="5011561501798487822">ഭാഷ തിരിചàµà´šà´±à´¿à´žàµà´žàµ</translation>
<translation id="5015510746216210676">മെഷീനിനàµâ€à´±àµ† പേരàµâ€Œ:</translation>
<translation id="5017554619425969104">നിങàµà´™àµ¾ പകർതàµà´¤à´¿à´¯ ടെകàµâ€Œà´¸àµâ€Œà´±àµà´±àµ</translation>
+<translation id="5017828934289857214">പിനàµà´¨àµ€à´Ÿàµ ഓർമàµà´®à´¿à´ªàµà´ªà´¿à´•àµà´•àµà´•</translation>
<translation id="5018422839182700155">à´ˆ പേജൠതàµà´±à´•àµà´•à´¾à´¨à´¾à´¯à´¿à´²àµà´²</translation>
<translation id="5019198164206649151">ബാകàµà´•à´¿à´‚ഗൠസംഭരണം മോശം അവസàµà´¥à´¯à´¿à´²à´¾à´£àµ</translation>
<translation id="5020776957610079374">ആഗോള സംഗീതം</translation>
@@ -1492,6 +1503,7 @@
<translation id="5051305769747448211">തൽസമയ ഹാസàµà´¯à´‚</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{സമീപമàµà´³àµà´³ പങàµà´•à´¿à´Ÿàµ½ ഉപയോഗിചàµà´šàµ à´ˆ ഫയൽ അയയàµâ€Œà´•àµà´•à´¾àµ» നിങàµà´™à´³àµà´Ÿàµ† ഉപകരണതàµà´¤à´¿àµ½ ഇടം (<ph name="DISK_SPACE_SIZE" />) സൃഷàµà´Ÿà´¿à´•àµà´•àµà´•}other{സമീപമàµà´³àµà´³ പങàµà´•à´¿à´Ÿàµ½ ഉപയോഗിചàµà´šàµ à´ˆ ഫയലàµà´•àµ¾ അയയàµâ€Œà´•àµà´•à´¾àµ» നിങàµà´™à´³àµà´Ÿàµ† ഉപകരണതàµà´¤à´¿àµ½ ഇടം (<ph name="DISK_SPACE_SIZE" />) സൃഷàµà´Ÿà´¿à´•àµà´•àµà´•}}</translation>
<translation id="5056549851600133418">നിങàµà´™àµ¾à´•àµà´•àµà´³àµà´³ ലേഖനങàµà´™àµ¾</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> ഉപയോഗികàµà´•àµà´¨àµà´¨ വെബàµà´¸àµˆà´±àµà´±àµà´•à´³à´¿àµ½ Windows Hello ഉപയോഗിചàµà´šàµ പരിശോധിചàµà´šàµà´±à´ªàµà´ªà´¿à´•àµà´•à´¾àµ» നിങàµà´™àµ¾ തിരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤àµ. നിങàµà´™à´³àµà´Ÿàµ† പേയàµà´®àµ†à´¨àµà´±àµ രീതിയàµà´®à´¾à´¯à´¿ ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´Ÿ വിവരങàµà´™àµ¾ à´ˆ ദാതാവൠസംഭരിചàµà´šà´¿à´Ÿàµà´Ÿàµà´£àµà´Ÿà´¾à´•à´¾à´‚, നിങàµà´™àµ¾à´•àµà´•àµ ഇതൠ<ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" /> ആണോ നിങàµà´™àµ¾ ഉദàµà´¦àµ‡à´¶à´¿à´šàµà´šà´¤àµ?</translation>
<translation id="5066056036849835175">à´ªàµà´°à´¿à´¨àµà´±à´¿à´‚ഗൠചരിതàµà´°à´‚</translation>
<translation id="5068234115460527047">ഹെഡàµâ€Œà´œàµ à´«à´£àµà´Ÿàµà´•àµ¾</translation>
@@ -1505,10 +1517,8 @@
<translation id="5087286274860437796">സെർവറിനàµà´±àµ† സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¿à´¨àµ ഇപàµà´ªàµ‹àµ¾ സാധàµà´¤à´¯à´¿à´²àµà´².</translation>
<translation id="5087580092889165836">കാർഡൠചേർകàµà´•àµà´•</translation>
<translation id="5088142053160410913">à´“à´ªàµà´ªà´±àµ‡à´±àµà´±àµ¼à´•àµà´•àµà´³àµà´³ സനàµà´¦àµ‡à´¶à´‚</translation>
-<translation id="5089810972385038852">à´¸àµà´±àµà´±àµ‡à´±àµà´±àµ</translation>
<translation id="5093232627742069661">Z-മടകàµà´•àµ</translation>
<translation id="5094747076828555589">à´ˆ സെർവറിനൠഅതൠ<ph name="DOMAIN" /> ആണെനàµà´¨àµ തെളിയികàµà´•à´¾à´¨à´¾à´¯à´¿à´²àµà´²; അതിനàµà´±àµ† à´¸àµà´°à´•àµà´· സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¿à´¨àµ† Chromium-à´¤àµà´¤à´¿à´¨àµà´¨àµ പരിചയമിലàµà´². തെറàµà´±à´¾à´¯ കോൺഫിഗറേഷൻ കാരണമോ ഒരൠഅകàµà´°à´®à´£à´•à´¾à´°à´¿ നിങàµà´™à´³àµà´Ÿàµ† കണകàµà´·à´¨àµ† തടസàµà´¸à´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´¨àµà´¨à´¤àµ കൊണàµà´Ÿàµ‹ ആയിരികàµà´•à´¾à´‚ ഇതൠസംഭവിചàµà´šà´¤àµ.</translation>
-<translation id="5095208057601539847">à´ªàµà´°à´µà´¿à´¶àµà´¯</translation>
<translation id="5097099694988056070">CPU/RAM ഉപയോഗം പോലàµà´³àµà´³ ഉപകരണ à´¸àµà´¥à´¿à´¤à´¿à´µà´¿à´µà´°à´•àµà´•à´£à´•àµà´•àµà´•àµ¾</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">സൈറàµà´±àµ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´²àµà´²</translation>
@@ -1546,6 +1556,7 @@
<translation id="5171045022955879922">തിരയàµà´• à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ URL ടൈപàµà´ªàµ ചെയàµà´¯àµà´•</translation>
<translation id="5171689220826475070">ഫാൻഫോൾഡàµ-യൂറോപàµà´¯à´¨àµâ€â€Œ</translation>
<translation id="5172758083709347301">മെഷീൻ</translation>
+<translation id="5177076414499237632">à´ˆ പേജിനàµà´±àµ† ഉറവിടതàµà´¤àµ†à´¯àµà´‚ വിഷയതàµà´¤àµ†à´¯àµà´‚ à´•àµà´±à´¿à´šàµà´šàµ അറിയàµà´•</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµâ€â€Œ ഇലàµà´²àµ‡? à´ˆ പിശകൠറിപàµà´ªàµ‹à´°àµâ€â€Œà´Ÿàµà´Ÿàµ ചെയàµà´¯àµà´•</translation>
<translation id="518639307526414276">വളർതàµà´¤àµà´®àµƒà´—à´™àµà´™à´³àµà´Ÿàµ† à´­à´•àµà´·à´£à´µàµà´‚ വളർതàµà´¤àµà´®àµƒà´— പരിചരണ സാമഗàµà´°à´¿à´•à´³àµà´‚</translation>
<translation id="5190835502935405962">à´¬àµà´•àµà´•àµâ€Œà´®à´¾à´°àµâ€â€Œà´•àµà´•àµ ബാരàµâ€â€Œ</translation>
@@ -1706,6 +1717,7 @@
<translation id="5624120631404540903">പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾ നിയനàµà´¤àµà´°à´¿à´•àµà´•àµà´•</translation>
<translation id="5629630648637658800">നയ à´•àµà´°à´®àµ€à´•à´°à´£à´™àµà´™àµ¾ ലോഡൠചെയàµà´¯àµà´¨àµà´¨à´¤à´¿àµ½ പരാജയപàµà´ªàµ†à´Ÿàµà´Ÿàµ</translation>
<translation id="5631439013527180824">ഉപകരണ മാനേജàµà´®àµ†à´¨àµà´±àµ ടോകàµà´•àµº അസാധàµà´µà´¾à´£àµ</translation>
+<translation id="5632485077360054581">à´Žà´™àµà´™à´¨àµ†à´¯àµ†à´¨àµà´¨àµ à´Žà´¨àµà´¨àµ† കാണികàµà´•àµà´•</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨ സൈറàµà´±à´¿à´²àµ† നിലവിലàµà´³àµà´³ à´…à´±àµà´±à´¾à´•àµà´•àµ¼à´®à´¾àµ¼ നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ മോഷàµâ€Œà´Ÿà´¿à´•àµà´•à´¾à´¨àµ‹ ഇലàµà´²à´¾à´¤à´¾à´•àµà´•à´¾à´¨àµ‹ ഇടയàµà´³àµà´³ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, ഫോടàµà´Ÿàµ‹à´•àµ¾, പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾, സനàµà´¦àµ‡à´¶à´™àµà´™àµ¾, à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•àµ¾ à´Žà´¨àµà´¨à´¿à´µ) അപകടകരമായ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´±à´¿àµ½ ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¯à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ‡à´•àµà´•à´¾à´‚. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">തെറàµà´±à´¿à´¦àµà´§à´°à´¿à´ªàµà´ªà´¿à´•àµà´•àµà´¨àµà´¨ ഉളàµà´³à´Ÿà´•àµà´•à´‚ à´¬àµà´²àµ‹à´•àµà´•àµà´šàµ†à´¯àµâ€Œà´¤àµ.</translation>
<translation id="5633259641094592098">കൾടàµà´Ÿàµ, ഇൻഡി സിനിമകൾ</translation>
@@ -1793,7 +1805,7 @@
<translation id="5904360430676679685">à´•àµà´Ÿàµà´‚ബവàµà´‚ ബനàµà´§à´™àµà´™à´³àµà´‚</translation>
<translation id="5905445707201418379"><ph name="ORIGIN" /> à´Žà´¨àµà´¨à´¤à´¿à´¨àµà´±àµ† ഉറവിട നയതàµà´¤à´¿à´¨àµ à´…à´¨àµà´¸àµƒà´¤à´®à´¾à´¯à´¿ à´¬àµà´²àµ‹à´•àµà´•àµ ചെയàµâ€Œà´¤àµ.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (സമനàµà´µà´¯à´¿à´ªàµà´ªà´¿à´šàµà´šà´¤àµ)</translation>
-<translation id="59174027418879706">à´ªàµà´°à´¾à´ªàµà´¤à´®à´¾à´•àµà´•à´¿</translation>
+<translation id="59174027418879706">à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´•àµà´·à´®à´®à´¾à´•àµà´•à´¿</translation>
<translation id="5919090499915321845">B10</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{ഒരെണàµà´£à´‚ ഉപയോഗതàµà´¤à´¿à´²àµà´£àµà´Ÿàµ}other{# à´Žà´£àµà´£à´‚ ഉപയോഗതàµà´¤à´¿à´²àµà´£àµà´Ÿàµ}}</translation>
<translation id="592031860219554105">à´à´¸àµ à´¸àµâ€Œà´•àµ‡à´±àµà´±à´¿à´‚à´—àµ</translation>
@@ -1823,6 +1835,7 @@
<translation id="5989320800837274978">ഒരൠസàµà´¥à´¿à´°à´®à´¾à´¯ à´ªàµà´°àµ‹à´•àµà´¸à´¿ സെർവർ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ ഒരൠ.pac à´¸àµâ€Œà´•àµà´°à´¿à´ªàµà´±àµà´±àµ URL à´µàµà´¯à´•àµà´¤à´®à´¾à´•àµà´•à´¿à´¯à´¿à´Ÿàµà´Ÿà´¿à´²àµà´².</translation>
<translation id="5992691462791905444">à´Žà´žàµà´šà´¿à´¨àµ€à´¯à´±à´¿à´‚ഗൠZ-മടകàµà´•àµ</translation>
<translation id="5995727681868049093">നിങàµà´™à´³àµà´Ÿàµ† Google à´…à´•àµà´•àµ—à´£àµà´Ÿà´¿à´²àµ† വിവരങàµà´™à´³àµà´‚ à´¸àµà´µà´•à´¾à´°àµà´¯à´¤à´¯àµà´‚ à´¸àµà´°à´•àµà´·à´¯àµà´‚ മാനേജൠചെയàµà´¯àµà´•</translation>
+<translation id="5997247540087773573">നിങàµà´™àµ¾ ഇപàµà´ªàµ‹àµ¾ ഉപയോഗിചàµà´š പാസàµâ€Œà´µàµ‡à´¡àµ ഡാറàµà´±à´¾ ലംഘനതàµà´¤à´¿àµ½ à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ. നിങàµà´™à´³àµà´Ÿàµ† à´…à´•àµà´•àµ—à´£àµà´Ÿàµà´•àµ¾ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´•àµà´•à´¾àµ», ഇപàµà´ªàµ‹àµ¾ തനàµà´¨àµ† à´…à´µ മാറàµà´±à´¾à´¨àµà´‚ സംരകàµà´·à´¿à´šàµà´š പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾ പരിശോധികàµà´•à´¾à´¨àµà´‚ Google Password Manager നിർദàµà´¦àµ‡à´¶à´¿à´•àµà´•àµà´¨àµà´¨àµ.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' à´Žà´¨àµà´¨à´¤à´¿à´¨à´¾à´¯à´¿ <ph name="RESULT_COUNT" /> ഫലങàµà´™à´³àµâ€</translation>
<translation id="6006484371116297560">à´•àµà´²à´¾à´¸à´¿à´•àµ</translation>
<translation id="6008122969617370890">N-1 à´•àµà´°à´®à´¤àµà´¤à´¿àµ½</translation>
@@ -1918,7 +1931,6 @@
<translation id="627746635834430766">à´…à´Ÿàµà´¤àµà´¤ à´ªàµà´°à´¾à´µà´¶àµà´¯à´‚ വേഗതàµà´¤à´¿àµ½ പണമടയàµà´•àµà´•à´¾àµ», നിങàµà´™à´³àµà´Ÿàµ† Google à´…à´•àµà´•àµ—à´£àµà´Ÿà´¿àµ½ à´ˆ കാർഡàµà´‚ ബിലàµà´²à´¿à´‚ഗൠവിലാസവàµà´‚ സംരകàµà´·à´¿à´•àµà´•àµà´•.</translation>
<translation id="6279183038361895380">നിങàµà´™à´³àµà´Ÿàµ† à´•à´´àµâ€Œà´¸àµ¼ കാണികàµà´•à´¾àµ» |<ph name="ACCELERATOR" />| അമർതàµà´¤àµà´•</translation>
<translation id="6280223929691119688">à´ˆ വിലാസതàµà´¤à´¿à´²àµ‡à´•àµà´•àµ ഡെലിവറി ചെയàµà´¯à´¾àµ» കഴിയിലàµà´². മറàµà´±àµŠà´°àµ വിലാസം തിരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•.</translation>
-<translation id="6282194474023008486">തപാലàµâ€ കോഡàµ</translation>
<translation id="6285507000506177184">'Chrome-ൽ ഡൗൺലോഡàµà´•àµ¾ മാനേജൠചെയàµà´¯àµà´•' ബടàµà´Ÿàµº, Chrome-ൽ നിങàµà´™àµ¾ ഡൗൺലോഡൠചെയàµà´¤à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨ ഫയലàµà´•àµ¾ മാനേജൠചെയàµà´¯à´¾àµ» Enter അമർതàµà´¤àµà´•</translation>
<translation id="6289939620939689042">പേജിനàµà´±àµ† നിറം</translation>
<translation id="6290238015253830360">നിങàµà´™à´³àµà´Ÿàµ† നിർദàµà´¦àµ‡à´¶à´¿à´šàµà´š ലേഖനങàµà´™àµ¾ ഇവിടെ ദൃശàµà´¯à´®à´¾à´•àµà´‚</translation>
@@ -1940,6 +1952,7 @@
<translation id="6337133576188860026"><ph name="SIZE" />-യിൽ à´•àµà´±à´žàµà´ž ഡാറàµà´± ലാഭികàµà´•àµà´¨àµà´¨àµ.à´…à´Ÿàµà´¤àµà´¤à´¤à´µà´£ നിങàµà´™àµ¾ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•àµà´®àµà´ªàµ‹àµ¾ à´šà´¿à´² സൈറàµà´±àµà´•àµ¾ ഇതിനേകàµà´•à´¾àµ¾ പതàµà´•àµà´•àµ† ലോഡാകാം.</translation>
<translation id="6337534724793800597">പേരിനàµâ€à´±àµ† à´•àµà´°à´®à´¤àµà´¤à´¿àµ½ നയങàµà´™àµ¾ ഫിൽടàµà´Ÿàµ¼ ചെയàµà´¯àµà´•</translation>
<translation id="6340739886198108203">രഹസàµà´¯à´¾à´¤àµà´®à´• ഉളàµà´³à´Ÿà´•àµà´•à´‚ ദൃശàµà´¯à´®à´¾à´¯à´¿à´°à´¿à´•àµà´•àµà´®àµà´ªàµ‹àµ¾ à´¸àµà´•àµà´°àµ€àµ»à´·àµ‹à´Ÿàµà´Ÿàµà´•àµ¾ à´Žà´Ÿàµà´•àµà´•àµà´¨àµà´¨à´¤àµ‹ റെകàµà´•àµ‹àµ¼à´¡àµ ചെയàµà´¯àµà´¨àµà´¨à´¤àµ‹ à´…à´¡àµâ€Œà´®à´¿àµ» നയം à´¶àµà´ªà´¾àµ¼à´¶ ചെയàµà´¯àµà´¨àµà´¨à´¿à´²àµà´²:</translation>
+<translation id="6348220984832452017">സജീവമായ à´µàµà´¯à´¤à´¿à´¯à´¾à´¨à´™àµà´™àµ¾</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¯àµà´•</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{à´’à´¨àµà´¨àµà´®à´¿à´²àµà´²}=1{<ph name="DOMAIN_LIST" /> à´Žà´¨àµà´¨à´¤à´¿à´¨àµà´³àµà´³ ഒരൠപാസàµâ€Œà´µàµ‡à´¡àµ (സമനàµà´µà´¯à´¿à´ªàµà´ªà´¿à´šàµà´šà´¤àµ)}=2{<ph name="DOMAIN_LIST" /> à´Žà´¨àµà´¨à´¤à´¿à´¨àµà´³àµà´³ 2 പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾ (സമനàµà´µà´¯à´¿à´ªàµà´ªà´¿à´šàµà´šà´¤àµ)}other{<ph name="DOMAIN_LIST" /> à´Žà´¨àµà´¨à´¤à´¿à´¨àµà´³àµà´³# പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾ (സമനàµà´µà´¯à´¿à´ªàµà´ªà´¿à´šàµà´šà´¤àµ)}}</translation>
<translation id="6355392890578844978">à´•à´®àµà´ªà´¨à´¿à´¯àµ‹ മറàµà´±àµ‡à´¤àµ†à´™àµà´•à´¿à´²àµà´‚ à´¸àµà´¥à´¾à´ªà´¨à´®àµ‹ മാനേജൠചെയàµà´¯àµà´¨àµà´¨à´¤à´²àµà´² à´ˆ à´¬àµà´°àµ—സർ. à´ˆ ഉപകരണതàµà´¤à´¿à´²àµ† ആകàµâ€Œà´±àµà´±à´¿à´µà´¿à´±àµà´±à´¿ Chromium-നൠപàµà´±à´¤àµà´¤àµ മാനേജൠചെയàµà´¤àµ‡à´•àµà´•à´¾à´‚. <ph name="BEGIN_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LINK" /></translation>
@@ -1971,7 +1984,6 @@
<translation id="643051589346665201">Google പാസàµâ€Œà´µàµ‡à´¡àµ മാറàµà´±àµà´•</translation>
<translation id="6433490469411711332">കോൺടാകàµâ€Œà´±àµà´±àµ വിവരം à´Žà´¡à´¿à´±àµà´±àµ ചെയàµà´¯àµà´•</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> കണകàµâ€Œà´±àµà´±àµà´šàµ†à´¯àµà´¯àµ½ നിരസിചàµà´šàµ.</translation>
-<translation id="6438025220577812695">നേരിടàµà´Ÿàµ മാറàµà´±àµà´•</translation>
<translation id="6440503408713884761">അവഗണിചàµà´šàµ</translation>
<translation id="6443406338865242315">à´à´¤àµŠà´•àµà´•àµ† വിപàµà´²àµ€à´•à´°à´£à´™àµà´™à´³àµà´‚ à´ªàµà´²à´—à´¿à´¨àµà´•à´³àµà´‚ നിങàµà´™àµ¾ ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¤à´¿à´Ÿàµà´Ÿàµà´£àµà´Ÿàµ à´Žà´¨àµà´¨à´¤àµ</translation>
<translation id="6446163441502663861">കഹൠ(എൻവലപàµà´ªàµ)</translation>
@@ -2101,9 +2113,9 @@
<translation id="6828866289116430505">ജനിതകശാസàµâ€Œà´¤àµà´°à´‚</translation>
<translation id="6831043979455480757">വിവർതàµà´¤à´¨à´‚ ചെയàµà´¯àµà´•</translation>
<translation id="6833752742582340615">à´¸àµà´°à´•àµà´·à´¿à´¤à´µàµà´‚ വേഗതàµà´¤à´¿à´²àµà´³àµà´³à´¤àµà´®à´¾à´¯ ചെകàµà´•àµà´”à´Ÿàµà´Ÿàµà´•àµ¾à´•àµà´•à´¾à´¯à´¿ കാർഡàµà´‚ ബിലàµà´²à´¿à´‚ഗൠവിവരങàµà´™à´³àµà´‚ നിങàµà´™à´³àµà´Ÿàµ† Google à´…à´•àµà´•àµ—à´£àµà´Ÿà´¿àµ½ സംരകàµà´·à´¿à´•àµà´•àµà´•</translation>
-<translation id="6839929833149231406">à´à´°à´¿à´¯</translation>
<translation id="6846340164947227603">വെർചàµà´µàµ½ കാർഡൠനമàµà´ªàµ¼ ഉപയോഗികàµà´•àµà´•...</translation>
<translation id="6852204201400771460">ആപàµà´ªàµ റീലോഡൠചെയàµà´¯à´£àµ‹?</translation>
+<translation id="6857776781123259569">പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾ മാനേജൠചെയàµà´¯àµà´•...</translation>
<translation id="686485648936420384">ഉപയോകàµà´¤àµƒ ഉറവിടങàµà´™àµ¾</translation>
<translation id="6865412394715372076">à´ˆ കാർഡൠഇപàµà´ªàµ‹àµ¾ പരിശോധിചàµà´šàµà´±à´ªàµà´ªà´¿à´•àµà´•à´¾à´¨à´¾à´µà´¿à´²àµà´²</translation>
<translation id="6869334554832814367">à´µàµà´¯à´•àµà´¤à´¿à´—à´¤ വായàµà´ªà´•àµ¾</translation>
@@ -2152,7 +2164,6 @@
<translation id="6965978654500191972">ഉപകരണം</translation>
<translation id="696703987787944103">പെർസെപàµà´šàµà´µàµ½</translation>
<translation id="6968269510885595029">നിങàµà´™à´³àµà´Ÿàµ† à´¸àµà´°à´•àµà´· കീ ഉപയോഗികàµà´•àµà´•</translation>
-<translation id="6970216967273061347">ജിലàµà´²</translation>
<translation id="6971439137020188025">Slides-ൽ à´ªàµà´¤à´¿à´¯àµŠà´°àµ Google അവതരണം വേഗതàµà´¤à´¿àµ½ സൃഷàµà´Ÿà´¿à´•àµà´•àµà´•</translation>
<translation id="6972629891077993081">HID ഉപകരണങàµà´™àµ¾</translation>
<translation id="6973656660372572881">à´¸àµà´¥à´¿à´°à´®à´¾à´¯ à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ സെർവറàµà´•à´³àµà´‚ ഒരൠസàµâ€Œà´•àµà´°à´¿à´ªàµà´±àµà´±àµ URL-ഉം à´µàµà´¯à´•àµà´¤à´®à´¾à´•àµà´•à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ.</translation>
@@ -2191,7 +2202,6 @@
<translation id="7081308185095828845">à´ˆ ഫീചàµà´šàµ¼ നിങàµà´™à´³àµà´Ÿàµ† ഉപകരണതàµà´¤à´¿àµ½ ലഭàµà´¯à´®à´²àµà´²</translation>
<translation id="7083258188081898530">à´Ÿàµà´°àµ‡ 9</translation>
<translation id="7086090958708083563">ഉപയോകàµà´¤à´¾à´µàµ à´…à´­àµà´¯àµ¼à´¤àµà´¥à´¿à´šàµà´š à´…à´ªàµâ€Œà´²àµ‹à´¡àµ</translation>
-<translation id="7087282848513945231">രാജàµà´¯à´‚</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome à´•àµà´°à´®àµ€à´•à´°à´£à´¤àµà´¤à´¿àµ½ സൈറàµà´±àµà´•à´³à´¿à´²àµà´Ÿà´¨àµ€à´³à´‚ സംഭരിചàµà´šà´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨ à´…à´¨àµà´®à´¤à´¿à´•à´³àµà´‚ ഡാറàµà´±à´¯àµà´‚ മാനേജൠചെയàµà´¯à´¾àµ» 'Tab' അമർതàµà´¤à´¿à´¯ ശേഷം 'Enter' അമർതàµà´¤àµà´•</translation>
<translation id="7096937462164235847">à´ˆ വെബàµà´¸àµˆà´±àµà´±à´¿à´¨àµà´±àµ† à´à´¡à´¨àµà´±à´¿à´±àµà´±à´¿ പരിശോധിചàµà´šàµà´±à´ªàµà´ªà´¿à´šàµà´šà´¿à´Ÿàµà´Ÿà´¿à´²àµà´².</translation>
<translation id="7101893872976785596">ഹൊറർ സിനിമകൾ</translation>
@@ -2210,10 +2220,10 @@
<ph name="LIST_ITEM" />ഫോമàµà´•à´³à´¿àµ½ നൽകàµà´¨àµà´¨ വിവരങàµà´™àµ¾<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">à´ˆ വിലാസതàµà´¤à´¿à´²àµ‡à´•àµà´•àµ à´·à´¿à´ªàµà´ªàµ ചെയàµà´¯à´¾àµ» കഴിയിലàµà´². മറàµà´±àµŠà´°àµ വിലാസം തിരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">à´ˆ ഡാറàµà´± പകർതàµà´¤àµà´¨àµà´¨à´¤àµ നിങàµà´™à´³àµà´Ÿàµ† à´…à´¡àµâ€Œà´®à´¿àµ» നിരോധിചàµà´šà´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ.</translation>
<translation id="7135130955892390533">à´¸àµâ€Œà´±àµà´±à´¾à´±àµà´±à´¸àµ കാണികàµà´•àµà´•</translation>
<translation id="7138472120740807366">ഡെലിവറി രീതി</translation>
-<translation id="7139724024395191329">എമിറേറàµà´±àµ</translation>
<translation id="7139892792842608322">à´ªàµà´°à´¾à´¥à´®à´¿à´• à´Ÿàµà´°àµ‡</translation>
<translation id="714064300541049402">സൈഡൠ2 à´šà´¿à´¤àµà´°à´‚ X à´·à´¿à´«àµà´±àµà´±àµ</translation>
<translation id="7152423860607593928">നമàµà´ªàµ¼-14 (എൻവലപàµà´ªàµ)</translation>
@@ -2428,6 +2438,7 @@
<translation id="7669271284792375604">à´ˆ സൈറàµà´±à´¿à´²àµ† ആകàµà´°à´®à´£à´•à´¾à´°à´¿à´•àµ¾, à´¬àµà´°àµ—സർ à´…à´¨àµà´­à´µà´¤àµà´¤àµ† ദോഷകരമായി ബാധികàµà´•àµà´¨àµà´¨ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¯à´¿à´•àµà´•àµà´¨àµà´¨ വിധതàµà´¤à´¿àµ½ നിങàµà´™à´³àµ† കബളിപàµà´ªà´¿à´•àµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ‡à´•àµà´•àµà´‚ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, നിങàµà´™à´³àµà´Ÿàµ† ഹോംപേജൠമാറàµà´±àµà´¨àµà´¨à´¤à´¿à´²àµ‚ടെയോ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•àµà´¨àµà´¨ സൈറàµà´±àµà´•à´³à´¿àµ½ കൂടàµà´¤àµ½ പരസàµà´¯à´™àµà´™àµ¾ കാണികàµà´•àµà´¨àµà´¨à´¤à´¿à´²àµ‚ടെയോ).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{രഹസàµà´¯à´¾à´¤àµà´®à´•à´‚ à´Žà´¨àµà´¨àµ à´«àµà´²à´¾à´—ൠചെയàµà´¤ ഡാറàµà´± ഉപയോഗിചàµà´šàµà´³àµà´³ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´™àµà´™àµ¾ (ലോഗിൻ ചെയàµà´¤à´¤àµ à´®àµà´¤àµ½ ഒരൠപàµà´°à´µàµ¼à´¤àµà´¤à´¨à´‚). <ph name="BEGIN_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LINK" />}other{രഹസàµà´¯à´¾à´¤àµà´®à´•à´‚ à´Žà´¨àµà´¨àµ à´«àµà´²à´¾à´—ൠചെയàµà´¤ ഡാറàµà´± ഉപയോഗിചàµà´šàµà´³àµà´³ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´™àµà´™àµ¾ (ലോഗിൻ ചെയàµà´¤à´¤àµ à´®àµà´¤àµ½ # à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´™àµà´™àµ¾). <ph name="BEGIN_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">മെയിൽബോകàµà´¸àµ 6</translation>
+<translation id="7675325315208090829">പേയàµà´®àµ†à´¨àµà´±àµ രീതികൾ മാനേജൠചെയàµà´¯àµà´•...</translation>
<translation id="7676643023259824263">à´•àµà´²à´¿à´ªàµà´ªàµâ€Œà´¬àµ‹àµ¼à´¡àµ ടെകàµâ€Œà´¸àµâ€Œà´±àµà´±à´¿à´¨à´¾à´¯à´¿ തിരയàµà´•, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Chrome à´•àµà´°à´®àµ€à´•à´°à´£à´¤àµà´¤à´¿àµ½ നിങàµà´™à´³àµà´Ÿàµ† à´¬àµà´°àµ—സിംഗൠചരിതàµà´°à´‚ കാണàµà´•, മാനേജൠചെയàµà´¯àµà´•</translation>
<translation id="7679947978757153706">ബേസàµà´¬àµ‹àµ¾</translation>
@@ -2470,7 +2481,6 @@
<translation id="7766518757692125295">à´¸àµà´•àµ‡àµ¼à´Ÿàµà´Ÿàµ</translation>
<translation id="7770259615151589601">ഡെസിഗàµâ€Œà´¨àµ‡à´±àµà´±àµ ചെയàµâ€Œà´¤à´¤àµ-വലàµà´¤àµ</translation>
<translation id="7773005668374414287">അതേ à´•àµà´°à´®à´¤àµà´¤à´¿àµ½ ഫേസൠഅപàµà´ªàµ</translation>
-<translation id="777702478322588152">à´ªàµà´°à´¿à´«àµ†à´•àµà´šàµà´µà´°àµâ€</translation>
<translation id="7791011319128895129">റിലീസൠചെയàµà´¯à´¾à´¤àµà´¤à´¤àµ</translation>
<translation id="7791196057686275387">ബെയàµâ€Œàµ½</translation>
<translation id="7791543448312431591">ചേരàµâ€à´•àµà´•àµ‚</translation>
@@ -2561,12 +2571,12 @@
<translation id="8055534648776115597">തൊഴിലധിഷàµà´ à´¿à´¤, à´¤àµà´Ÿàµ¼ വിദàµà´¯à´¾à´­àµà´¯à´¾à´¸à´‚</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" ശരിയായി കോൺഫിഗർ ചെയàµâ€Œâ€Œà´¤à´¿à´Ÿàµà´Ÿà´¿à´²àµà´². സാധാരണഗതിയിൽ "<ph name="SOFTWARE_NAME" />" അൺഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¯àµà´¨àµà´¨à´¤à´¿à´²àµ‚ടെ à´ˆ à´ªàµà´°à´¶àµâ€Œà´¨à´‚ പരിഹരികàµà´•à´¾à´‚. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">à´­à´•àµà´·àµà´¯àµ‹àµ½à´ªàµà´ªà´¾à´¦à´¨à´‚</translation>
-<translation id="8066955247577885446">à´•àµà´·à´®à´¿à´•àµà´•à´£à´‚, à´Žà´¨àµà´¤àµ‹ à´•àµà´´à´ªàµà´ªà´®àµà´£àµà´Ÿà´¾à´¯à´¿.</translation>
<translation id="8067872629359326442">വഞàµà´šà´¨à´¾à´ªà´°à´®à´¾à´¯ സൈറàµà´±à´¿àµ½ നിങàµà´™àµ¾ ഇപàµà´ªàµ‹àµ¾ പാസàµâ€à´µàµ‡à´¡àµ നൽകി. Chromium-നൠസഹായികàµà´•à´¾à´¨à´¾à´µàµà´‚. നിങàµà´™à´³àµà´Ÿàµ† പാസàµâ€Œà´µàµ‡à´¡àµ മാറàµà´±à´¾à´¨àµà´‚ നിങàµà´™à´³àµà´Ÿàµ† à´…à´•àµà´•àµ—à´£àµà´Ÿàµ അപകടതàµà´¤à´¿à´²à´¾à´¯à´¿à´°à´¿à´•àµà´•à´¾à´®àµ†à´¨àµà´¨àµ Google-നെ അറിയികàµà´•à´¾à´¨àµà´‚ 'à´…à´•àµà´•àµ—à´£àµà´Ÿàµ പരിരകàµà´·à´¿à´•àµà´•àµà´•' à´•àµà´²à´¿à´•àµà´•àµ ചെയàµà´¯àµà´•.</translation>
<translation id="8070439594494267500">ആപàµà´ªàµ à´à´•àµà´•àµº</translation>
<translation id="8074253406171541171">10x13 (എൻവലപàµà´ªàµ)</translation>
-<translation id="8075736640322370409">à´ªàµà´¤à´¿à´¯àµŠà´°àµ Google ഷീറàµà´±àµ വേഗതàµà´¤à´¿àµ½ സൃഷàµà´Ÿà´¿à´•àµà´•àµà´•</translation>
+<translation id="8075736640322370409">à´ªàµà´¤à´¿à´¯àµŠà´°àµ Google Sheet വേഗതàµà´¤à´¿àµ½ സൃഷàµà´Ÿà´¿à´•àµà´•àµà´•</translation>
<translation id="8075898834294118863">സൈറàµà´±àµ à´•àµà´°à´®àµ€à´•à´°à´£à´‚ മാനേജൠചെയàµà´¯àµà´•</translation>
+<translation id="8076492880354921740">ടാബàµà´•à´³àµâ€â€Œ</translation>
<translation id="8078141288243656252">തിരികàµà´•àµà´®àµà´ªàµ‹àµ¾ അനോടàµà´Ÿàµ‡à´±àµà´±àµ ചെയàµà´¯à´¾à´¨à´¾à´µà´¿à´²àµà´²</translation>
<translation id="8079031581361219619">സൈറàµà´±àµ റീലോഡൠചെയàµà´¯à´£àµ‹?</translation>
<translation id="8081087320434522107">സെഡാൻ കാറàµà´•àµ¾</translation>
@@ -2689,6 +2699,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">à´•àµà´°à´®àµ€à´•à´°à´£à´‚</translation>
+<translation id="8428634594422941299">മനസàµà´¸à´¿à´²à´¾à´¯à´¿</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome à´•àµà´°à´®àµ€à´•à´°à´£à´¤àµà´¤à´¿àµ½ നിങàµà´™à´³àµà´Ÿàµ† à´•àµà´•àµà´•à´¿ à´®àµàµ»à´—ണനകൾ മാനേജൠചെയàµà´¯à´¾àµ» 'Tab' അമർതàµà´¤à´¿à´¯ ശേഷം 'Enter' അമർതàµà´¤àµà´•</translation>
<translation id="8433057134996913067">ഇതൠനിങàµà´™à´³àµ† മികàµà´• വെബàµâ€Œà´¸àµˆà´±àµà´±àµà´•à´³à´¿àµ½ നിനàµà´¨àµà´‚ സൈൻ ഔടàµà´Ÿàµ ചെയàµà´¯à´¿à´•àµà´•àµà´‚.</translation>
<translation id="8434840396568290395">വളർതàµà´¤àµà´®àµƒà´—à´™àµà´™àµ¾</translation>
@@ -2786,6 +2797,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> à´Žà´¨àµà´¨à´¤à´¿à´¨àµà´±àµ† നിങàµà´™à´³àµà´Ÿàµ† കോഡൠ<ph name="ONE_TIME_CODE" /> ആണàµ</translation>
<translation id="874918643257405732">à´ˆ ടാബൠബàµà´•àµà´•àµâ€Œà´®à´¾àµ¼à´•àµà´•àµ ചെയàµà´¯àµà´•</translation>
<translation id="8751426954251315517">പിനàµà´¨àµ€à´Ÿàµ വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´•àµà´•àµà´•</translation>
+<translation id="8757526089434340176">Google Pay ഓഫർ ലഭàµà´¯à´‚</translation>
<translation id="8758885506338294482">മതàµà´¸à´°à´¿à´šàµà´šàµ കളികàµà´•à´¾à´µàµà´¨àµà´¨ വീഡിയോ ഗെയിമിംഗàµ</translation>
<translation id="8759274551635299824">à´ˆ കാർഡൠകാലഹരണപàµà´ªàµ†à´Ÿàµà´Ÿàµ</translation>
<translation id="87601671197631245">à´ˆ സൈറàµà´±àµ കാലഹരണപàµà´ªàµ†à´Ÿàµà´Ÿ à´¸àµà´°à´•àµà´·à´¾ കോൺഫിഗറേഷൻ ഉപയോഗികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾àµ½ നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾, സനàµà´¦àµ‡à´¶à´™àµà´™àµ¾, à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•àµ¾) à´ˆ സൈറàµà´±à´¿à´²àµ‡à´•àµà´•àµ അയചàµà´šà´¾àµ½ à´…à´µ വെളിപàµà´ªàµ†à´Ÿàµà´Ÿàµ‡à´•àµà´•à´¾à´‚.</translation>
@@ -2793,6 +2805,7 @@
<translation id="8763927697961133303">USB ഉപകരണം</translation>
<translation id="8763986294015493060">നിലവിൽ à´¤àµà´±à´¨àµà´¨à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨ à´Žà´²àµà´²à´¾ അദൃശàµà´¯ വിൻഡോകളàµà´‚ à´…à´Ÿà´¯àµà´•àµà´•àµà´•</translation>
<translation id="8766943070169463815">à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯ പേയàµâ€Œà´®àµ†à´¨àµà´±àµ à´•àµà´°àµ†à´¡àµ»à´·àµà´¯àµ½ പരിശോധിചàµà´šàµà´±à´ªàµà´ªà´¿à´•àµà´•àµ½ ഷീറàµà´±àµ à´¤àµà´±à´¨àµà´¨àµ</translation>
+<translation id="8767765348545497220">സഹായ ബബിൾ à´…à´Ÿà´¯àµà´•àµà´•àµà´•</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">മോടàµà´Ÿàµ‹àµ¼à´¸àµˆà´•àµà´•à´¿à´³àµà´•àµ¾</translation>
<translation id="8790007591277257123">&amp;ഇലàµà´²à´¾à´¤à´¾à´•àµà´•àµà´¨àµà´¨à´¤àµ വീണàµà´Ÿàµà´‚ ചെയàµà´¯àµà´•</translation>
@@ -2805,6 +2818,7 @@
<translation id="8806285662264631610">à´•àµà´³à´¿à´•àµà´•à´¾à´¨àµà´‚ ശരീര സംരകàµà´·à´£à´¤àµà´¤à´¿à´¨àµà´®àµà´³àµà´³ ഉൽപàµà´ªà´¨àµà´¨à´™àµà´™àµ¾</translation>
<translation id="8807160976559152894">ഓരോ പേജിനൠശേഷവàµà´‚ à´Ÿàµà´°à´¿à´‚ ചെയàµà´¯àµà´•</translation>
<translation id="8808828119384186784">Chrome à´•àµà´°à´®àµ€à´•à´°à´£à´‚</translation>
+<translation id="8813277370772331957">à´Žà´¨àµà´¨àµ† പിനàµà´¨àµ€à´Ÿàµ ഓർമàµà´®à´¿à´ªàµà´ªà´¿à´•àµà´•àµà´•</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, നിങàµà´™à´³àµà´Ÿàµ† Chrome à´•àµà´°à´®àµ€à´•à´°à´£à´¤àµà´¤à´¿àµ½ Chrome à´…à´ªàµà´¡àµ‡à´±àµà´±àµ ചെയàµà´¯à´¾àµ» 'Tab' അമർതàµà´¤àµà´•, à´¤àµà´Ÿàµ¼à´¨àµà´¨àµ 'Enter' അമർതàµà´¤àµà´•</translation>
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="882338992931677877">മാനàµà´µàµ½ à´¸àµà´²àµ‹à´Ÿàµà´Ÿàµ</translation>
@@ -2984,6 +2998,7 @@
<translation id="988159990683914416">ഡെവലപàµà´ªàµ¼ പതിപàµà´ªàµ</translation>
<translation id="989988560359834682">വിലാസം à´Žà´¡à´¿à´±àµà´±àµà´šàµ†à´¯àµà´¯àµà´•</translation>
<translation id="991413375315957741">ചലന à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ à´ªàµà´°à´•à´¾à´¶ സെൻസറàµà´•àµ¾</translation>
+<translation id="992110854164447044">വഞàµà´šà´¿à´•àµà´•à´ªàµà´ªàµ†à´Ÿà´¾à´¨à´¿à´Ÿà´¯àµà´³àµà´³ സാഹചരàµà´¯à´™àµà´™à´³à´¿àµ½ നിനàµà´¨àµ പരിരകàµà´·à´¿à´•àµà´•à´¾àµ» സഹായികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ വെർചàµà´µàµ½ കാർഡൠനിങàµà´™à´³àµà´Ÿàµ† യഥാർതàµà´¥ കാർഡൠവിവരങàµà´™àµ¾ മറയàµà´•àµà´•àµà´¨àµà´¨àµ. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">പിങàµà´•àµ</translation>
<translation id="992432478773561401">നിങàµà´™à´³àµà´Ÿàµ† à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´±à´¿àµ½ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ നെറàµà´±àµâ€à´µà´°àµâ€à´•àµà´•à´¿àµ½ "<ph name="SOFTWARE_NAME" />" ശരിയായി ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµâ€Œà´¤à´¿à´Ÿàµà´Ÿà´¿à´²àµà´²:
diff --git a/chromium/components/strings/components_strings_mn.xtb b/chromium/components/strings/components_strings_mn.xtb
index bf0bb17f0fe..4270b041b48 100644
--- a/chromium/components/strings/components_strings_mn.xtb
+++ b/chromium/components/strings/components_strings_mn.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ТуÑламж, Ñ‚ÑтгÑлÑг, Ñанхүүгийн туÑламж</translation>
<translation id="1048785276086539861">Та Ñ‚ÑмдÑглÑгÑÑг заÑах үед ÑÐ½Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚ нÑг хуудÑаар харах руу буцна</translation>
<translation id="1050038467049342496">БуÑад аппыг хаах</translation>
+<translation id="1053959602163383901">Та <ph name="PROVIDER_ORIGIN" />-г ашигладаг вебÑайтууд дÑÑÑ€ баталгаажуулагч төхөөрөмжөөр баталгаажуулахаар ÑонгоÑон. Ð­Ð½Ñ Ò¯Ð¹Ð»Ñ‡Ð¸Ð»Ð³ÑÑ Ò¯Ð·Ò¯Ò¯Ð»Ñгч таны төлбөрийн Ñ…ÑÑ€ÑгÑлийн талаарх мÑдÑÑллийг хадгалÑан байж болзошгүй бөгөөд та <ph name="LINK_TEXT" /> боломжтой.</translation>
<translation id="1055184225775184556">&amp; ÐÑмÑÑ… үйлдлийг буцаах</translation>
<translation id="1056663316309890343">Зургийн программ хангамж</translation>
<translation id="1056898198331236512">Ðнхааруулга</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">Ðвах төрөл</translation>
<translation id="1281476433249504884">Гарах цааÑыг зÑÑ€ÑгцүүлÑÑ… тавцан 1</translation>
<translation id="1285320974508926690">Ð­Ð½Ñ Ñайтыг Ñ…ÑзÑÑ Ñ‡ бүү хөрвүүл</translation>
+<translation id="1288548991597756084">Картыг аюулгүй хадгалах</translation>
<translation id="1292571435393770077">Тавиур 16</translation>
<translation id="1292701964462482250">"Таны компьютер дÑÑрх программ хангамж Chrome-г вебÑд аюулгүй холбогдохыг зогÑоож байна" (зөвхөн Windows компьютерт боломжтой)</translation>
<translation id="1294154142200295408">Тушаалын мөрийн хувилбар</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Ðлдааг заÑахын тулд нÑÑхийг оролдож буй нүүр хуудаÑны &lt;strong&gt;Холбогдох&lt;/strong&gt; гÑÑнийг товшино уу.&lt;/p&gt;</translation>
<translation id="1507780850870535225">ЦÑцÑрлÑгжүүлÑлтийн дизайн</translation>
<translation id="1513706915089223971">Түүхийн жагÑаалт</translation>
+<translation id="1516097932025103760">Ð­Ð½Ñ Ð½ÑŒ шифрлÑгдÑÑн, найдвартай хадгалагдÑан ба Карт баталгаажуулалтын код Ñ…ÑзÑÑ Ñ‡ хадгалагдаггүй.</translation>
<translation id="1517433312004943670">УтаÑны дугаар шаардлагатай</translation>
<translation id="1519264250979466059">Огноо Ñуулгах</translation>
<translation id="1521159554480556801">ÐœÑндаÑ, ÑүлжмÑл урлаг</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Төлбөрийн Ñ…ÑÑ€ÑгÑлүүдийг хадгалах болон бөглөх</translation>
<translation id="1663943134801823270">Карт, хаÑгийг Chrome-Ñ Ñ…Ð°Ñ€Ð¶ болно. Та Ñ‚ÑдгÑÑрийг <ph name="BEGIN_LINK" />Тохиргоо<ph name="END_LINK" /> Ñ…ÑÑÑгт Ñ…Ñнах боломжтой.</translation>
<translation id="1671391448414634642">ÐžÐ´Ð¾Ð¾Ð½Ð¾Ð¾Ñ <ph name="SOURCE_LANGUAGE" /> Ñ…Ñл дÑÑрх хуудÑыг <ph name="TARGET_LANGUAGE" /> Ñ…Ñл Ñ€Ò¯Ò¯ орчуулна.</translation>
+<translation id="1673886523110456987">Саналыг ашиглахын тулд <ph name="CARD_DETAIL" />-Ñ Ð»Ð°Ð²Ð»Ð°Ð½Ð° уу</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> -Ñ <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ЭхлÑÑд богино зах</translation>
<translation id="168693727862418163">Ð­Ð½Ñ Ð±Ð¾Ð´Ð»Ð¾Ð³Ñ‹Ð½ утгыг ÑхемтÑй нь тулгаж баталж чадаагүй тул үүнийг үл Ñ…ÑÑ€ÑгÑÑнÑ.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Хөдөлгөөн мÑдрÑгч</translation>
<translation id="1717494416764505390">Шуудангийн хайрцаг 3</translation>
<translation id="1718029547804390981">Документ дÑÑÑ€ Ñ‚ÑмдÑглÑгÑÑ Ñ…Ð¸Ð¹Ñ…Ñд Ñ…ÑÑ‚ том байна</translation>
+<translation id="1720941539803966190">Практик хичÑÑлийг хаах</translation>
<translation id="1721424275792716183">* Талбарыг бөглөх шаардлагатай</translation>
<translation id="1727613060316725209">ГÑрчилгÑÑ Ñ…Ò¯Ñ‡Ð¸Ð½Ñ‚Ñй байна</translation>
<translation id="1727741090716970331">ХүчинтÑй картын дугаар оруулах</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">BBQ, мах шарах</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> Ñ…Ñл дÑÑрх хуудÑыг орчуулахгүй.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Ð­Ð½Ñ Ñ…Ñналт аÑаалттай бөгөөд төлөв нь идÑвхтÑй үед Chrome таны ÑаÑхны хөтчийн үйл ажиллагаа аль том Ñ…ÑмжÑÑний бүлÑг хүн ÑÑвÑл "нÑг үеийнхÑн"-Ñ‚Ñй хамгийн Ñ‚Ó©ÑÑ‚Ñй болохыг тодорхойлно. Сурталчлагчид бүлÑгт зориулж зар Ñонгох боломжтой бөгөөд хөтчийн үйл ажиллагааг таны төхөөрөмж дÑÑÑ€ хувийн байлгадаг. Таны бүлгийг өдөр тутам шинÑчилдÑг.}=1{Ð­Ð½Ñ Ñ…Ñналт аÑаалттай бөгөөд төлөв нь идÑвхтÑй үед Chrome таны ÑаÑхны хөтчийн үйл ажиллагаа аль том Ñ…ÑмжÑÑний бүлÑг хүн ÑÑвÑл "нÑг үеийнхÑн"-Ñ‚Ñй хамгийн Ñ‚Ó©ÑÑ‚Ñй болохыг тодорхойлно. Сурталчлагчид бүлÑгт зориулж зар Ñонгох боломжтой бөгөөд хөтчийн үйл ажиллагааг таны төхөөрөмж дÑÑÑ€ хувийн байлгадаг. Таны бүлгийг өдөр тутам шинÑчилдÑг.}other{Ð­Ð½Ñ Ñ…Ñналт аÑаалттай бөгөөд төлөв нь идÑвхтÑй үед Chrome таны ÑаÑхны хөтчийн үйл ажиллагаа аль том Ñ…ÑмжÑÑний бүлÑг хүн ÑÑвÑл "нÑг үеийнхÑн"-Ñ‚Ñй хамгийн Ñ‚Ó©ÑÑ‚Ñй болохыг тодорхойлно. Сурталчлагчид бүлÑгт зориулж зар Ñонгох боломжтой бөгөөд хөтчийн үйл ажиллагааг таны төхөөрөмж дÑÑÑ€ хувийн байлгадаг. Таны бүлгийг {NUM_DAYS} өдөр тутам шинÑчилдÑг.}}</translation>
-<translation id="2053553514270667976">Зип Код</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 зөвлөмж}other{# зөвлөмж}}</translation>
+<translation id="2066915425250589881">уÑтгуулах Ñ…Ò¯ÑÑлт тавих</translation>
<translation id="2068528718802935086">ÐÑрай, хөлд орж буй хүүхÑд</translation>
<translation id="2071156619270205202">Ð­Ð½Ñ ÐºÐ°Ñ€Ñ‚ нь виртуал картын дугаарын Ñрхгүй байна.</translation>
<translation id="2071692954027939183">Та мÑдÑгдлийг ихÑвчлÑн зөвшөөрдөггүй тул Ñ‚ÑдгÑÑрийг автоматаар блоклоÑон</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">Синк хийхийг удирдах товч. Chrome-н тохиргоонд Ñмар мÑдÑÑлÑл Ñинк хийхÑÑ ÑƒÐ´Ð¸Ñ€Ð´Ð°Ñ…Ñ‹Ð½ тулд Enter дÑÑÑ€ дарна уу</translation>
<translation id="2091887806945687916">Дуу</translation>
<translation id="2094505752054353250">ДомÑйн нÑÑ€ нийцÑхгүй байна</translation>
-<translation id="2096368010154057602">Ð¥ÑлтÑÑ</translation>
<translation id="2099652385553570808">Зүүн ирмÑг дагуу гурван удаа үдÑÑ…</translation>
<translation id="2101225219012730419">Хувилбар:</translation>
<translation id="2102134110707549001">ХүчтÑй нууц үг Ñанал болгох...</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">Ðмерик хөл бөмбөг</translation>
<translation id="2187317261103489799">ИлрүүлÑÑ… (өгөгдмөл)</translation>
<translation id="2188375229972301266">Доод буланд олон нүх цоолох</translation>
-<translation id="2188852899391513400">Таны дөнгөж ÑÐ°Ñ Ð°ÑˆÐ¸Ð³Ð»Ð°Ñан нууц үгийг өгөгдлийн Ð·Ó©Ñ€Ñ‡Ð»Ó©Ó©Ñ Ð¾Ð»Ð»Ð¾Ð¾. Таны бүртгÑлүүдийг хамгаалахын тулд Google-н Ðууц үгний менежер үүнийг одоо өөрчилж, дараа нь хадгалÑан нууц үгнүүдÑÑ ÑˆÐ°Ð»Ð³Ð°Ñ…Ñ‹Ð³ зөвлөж байна.</translation>
<translation id="219906046732893612">ГÑÑ€ тохижуулах</translation>
<translation id="2202020181578195191">ДууÑах оныг зөв оруулна уу</translation>
<translation id="22081806969704220">Гарах цааÑны тавиур 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">Файл заÑах</translation>
<translation id="2215963164070968490">Ðохой</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />-н халдагчид таны төхөөрөмжийг гÑмтÑÑж болзошгүй аюултай апп Ñуулгах, таны мобайл тооцоонд нÑмÑлт төлбөр гаргах, таны хувийн мÑдÑÑллийг хулгайлах боломжтой. <ph name="BEGIN_LEARN_MORE_LINK" />ДÑлгÑÑ€Ñнгүй үзÑÑ…<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Практик хичÑÑлийг дахин ÑхлүүлÑÑ…</translation>
+<translation id="2219735899272417925">Төхөөрөмжийг шинÑчлÑÑ… шаардлагатай</translation>
<translation id="2224337661447660594">ИнтернÑÑ‚ алга</translation>
<translation id="2230458221926704099">Та <ph name="BEGIN_LINK" />оношилгоо апп<ph name="END_LINK" /> ашиглан холболтоо заÑна уу</translation>
<translation id="2239100178324503013">Одоо илгÑÑÑ…</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">Зөвшөөрөөгүй (өгөгдмөл)</translation>
<translation id="2512413427717747692">Chrome-г өгөгдмөл хөтчийн товчлуур болгон тохируулах, iOS-н тохиргоонд Chrome-г ÑиÑтемийн өгөгдмөл хөтөч болгон тохируулахын тулд Enter дÑÑÑ€ дарна уу</translation>
<translation id="2515629240566999685">Таны байгаа газрын дохиог шалгаж байна</translation>
+<translation id="2515761554693942801">Та <ph name="PROVIDER_ORIGIN" />-г ашигладаг вебÑайтууд дÑÑÑ€ Touch ID-Ñ€ баталгаажуулахаар ÑонгоÑон. Ð­Ð½Ñ Ò¯Ð¹Ð»Ñ‡Ð¸Ð»Ð³ÑÑ Ò¯Ð·Ò¯Ò¯Ð»Ñгч таны төлбөрийн Ñ…ÑÑ€ÑгÑлийн талаарх мÑдÑÑллийг хадгалÑан байж болзошгүй бөгөөд та <ph name="LINK_TEXT" /> боломжтой.</translation>
<translation id="2521385132275182522">Баруун доод буланд үдÑÑ…</translation>
<translation id="2521736961081452453">МаÑгт Ò¯Ò¯ÑгÑÑ…</translation>
<translation id="2523886232349826891">Зөвхөн ÑÐ½Ñ Ñ‚Ó©Ñ…Ó©Ó©Ñ€Ó©Ð¼Ð¶ дÑÑÑ€ хадгалÑан</translation>
<translation id="2524461107774643265">ДÑлгÑÑ€Ñнгүй мÑдÑÑлÑл нÑмÑÑ…</translation>
<translation id="2529899080962247600">Ð­Ð½Ñ Ñ‚Ð°Ð»Ð±Ð°Ñ€ нь <ph name="MAX_ITEMS_LIMIT" />-Ñ Ð¾Ð»Ð¾Ð½ оролттой байж болохгүй. Цаашдын бүх оролтыг үл Ñ…ÑÑ€ÑгÑÑнÑ.</translation>
+<translation id="253493526287553278">Урамшууллын кодын дÑлгÑÑ€Ñнгүйг харах</translation>
<translation id="2535585790302968248">Ðууцлалтайгаар үзÑхийн тулд ÑˆÐ¸Ð½Ñ Ðууцлалтай таб нÑÑÐ½Ñ Ò¯Ò¯</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{болон буÑад 1}other{болон буÑад #}}</translation>
<translation id="2536110899380797252">ХаÑг нÑмÑÑ…</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">ГÑÑ€Ñл зураг, дижитал урлаг</translation>
<translation id="2601150049980261779">Хайр дурлалын кино</translation>
<translation id="2604589665489080024">Поп хөгжим</translation>
-<translation id="2609632851001447353">Хувилбарууд</translation>
<translation id="2610561535971892504">Хуулахын тулд товшино уу</translation>
<translation id="2617988307566202237">Chrome нь дараах мÑдÑÑллийг <ph name="BEGIN_EMPHASIS" />хадгалахгүй<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">ТөрÑөн өдөр, нÑрийн өдөр</translation>
<translation id="2677748264148917807">ҮлдÑÑÑ…</translation>
+<translation id="2679714844901977852">Ðюулгүй бөгөөд илүү хурдан тооцоо хийх бол карт болон төлбөр тооцооны мÑдÑÑллÑÑ <ph name="USER_EMAIL" /> Google БүртгÑлдÑÑ Ñ…Ð°Ð´Ð³Ð°Ð»Ð½Ð° уу</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Хамгийн оновтой тохирол</translation>
<translation id="2688969097326701645">Тийм, үргÑлжлүүлье</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">ИнтернÑÑ‚ үйлчилгÑÑ Ð½Ð¸Ð¹Ð»Ò¯Ò¯Ð»Ñгч (ISP)</translation>
<translation id="2856444702002559011">Халдагчид <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />-Ñ Ñ‚Ð°Ð½Ñ‹ нууц үг, меÑÑеж, кредит карт зÑÑ€Ñг мÑдÑÑллийг хулгайлахаар оролдож байж болзошгүй. <ph name="BEGIN_LEARN_MORE_LINK" />ДÑлгÑÑ€Ñнгүй үзÑÑ…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Ð­Ð½Ñ Ñайт төвөгтÑй ÑÑвÑл хуурамч зар харуулдаг.</translation>
-<translation id="286512204874376891">Виртуал карт нь таныг болзошгүй Ð·Ð°Ð»Ð¸Ð»Ð°Ð½Ð³Ð°Ð°Ñ Ñ…Ð°Ð¼Ð³Ð°Ð°Ð»Ð°Ñ…Ð°Ð´ туÑлахын тулд таны жинхÑÐ½Ñ ÐºÐ°Ñ€Ñ‚Ñ‹Ð³ далдалдаг. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ÐÓ©Ñ…Ó©Ñ€Ñөг</translation>
<translation id="28761159517501904">Кино</translation>
<translation id="2876489322757410363">Гадны аппликÑйшнÑÑÑ€ төлбөр төлөхийн тулд Ðууцлалтай Ð³Ð¾Ñ€Ð¸Ð¼Ð¾Ð¾Ñ Ð³Ð°Ñ€Ñ‡ байна. ҮргÑлжлүүлÑÑ… Ò¯Ò¯?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ДиÑк</translation>
<translation id="3162559335345991374">Таны ашиглаж буй Wi-Fi ÑүлжÑÑ Ñ‚Ð°Ð½Ñ‹Ð³ нÑвтрÑÑ… хуудаÑÑ‚ орохыг шаардах магадлалтай.</translation>
<translation id="3169472444629675720">Олох</translation>
-<translation id="3174168572213147020">Ðрал</translation>
<translation id="3176929007561373547">ПрокÑи тохиргоогоо шалгана уу. ЭÑвÑл ÑүлжÑÑний админтайгаа холбогдож 
прокÑи Ñервер ажиллаж байгаа ÑÑÑхийг шалгана уу.
Ð¥ÑÑ€Ñв та прокÑи Ñервер ашиглаж байгаадаа итгÑлгүй байвал:
@@ -882,9 +887,6 @@
<translation id="3369192424181595722">Цагийн тохиргооны алдаа</translation>
<translation id="3369459162151165748">ТÑÑврийн Ñ…ÑÑ€ÑгÑлийн ÑÑлбÑг, дагалдах Ñ…ÑÑ€ÑгÑÑл</translation>
<translation id="3371064404604898522">Chrome-г өгөгдмөл хөтөч болгон тохируулах</translation>
-<translation id="3371076217486966826"><ph name="URL" /> нь дараахад зөвшөөрөл Ñ…Ò¯ÑÑж байна:
- • Таны ÑргÑн тойрны 3D газрын зургийг Ò¯Ò¯ÑгÑж, камерын байршлыг Ñ…Ñнах
- • Таны камерыг ашиглах</translation>
<translation id="337363190475750230">Хадгалагдаагүй</translation>
<translation id="3375754925484257129">Chrome-н аюулгүй байдлын шалгалтыг ажиллуулах</translation>
<translation id="3377144306166885718">Сервер TLS-н хуучирÑан хувилбарыг ашиглаÑан.</translation>
@@ -901,6 +903,7 @@
<translation id="3399952811970034796">ХүргÑлтийн хаÑг</translation>
<translation id="3402261774528610252">Ð­Ð½Ñ Ñайтыг ачаалахад ашиглаÑан холболт TLS 1.0 ÑÑвÑл TLS 1.1-ийг ашиглаÑан байна. ЭдгÑÑÑ€ нь хуучирÑан бөгөөд ÑдгÑÑрийг ирÑÑдүйд идÑвхгүй болгоно. ИдÑвхгүй болгоÑон тохиолдолд Ñ…ÑÑ€ÑглÑгчдийг ÑÐ½Ñ Ñайтыг Ð°Ñ‡Ð°Ð°Ð»Ð°Ñ…Ð°Ð°Ñ ÑÑргийлнÑ. Сервер TLS 1.2 ÑÑвÑл түүнÑÑÑ Ð´ÑÑших хувилбарыг идÑвхжүүлÑÑ… шаардлагатай.</translation>
<translation id="3405664148539009465">Ò®Ñгийн Ñ…Ñвийг өөрчлөх</translation>
+<translation id="3407789382767355356">гуравдагч талын нÑвтрÑлт</translation>
<translation id="3409896703495473338">Ðюулгүй байдлын тохиргоог удирдах</translation>
<translation id="3414952576877147120">Ð¥ÑмжÑÑ:</translation>
<translation id="3417660076059365994">Таны байршуулах ÑÑвÑл хавÑаргах файлуудыг дүн шинжилгÑÑ Ñ…Ð¸Ð¹Ð»Ð³ÑÑ…ÑÑÑ€ Google Cloud ÑÑвÑл гуравдагч тал руу илгÑÑдÑг. ЖишÑÑ Ð½ÑŒ, Ñ‚ÑдгÑÑÑ€ÑÑÑ ÑмзÑг өгөгдөл ÑÑвÑл хортой кодыг Ñкан хийж болно.</translation>
@@ -933,6 +936,7 @@
<translation id="3477679029130949506">Киноны жагÑаалтын театрын үзвÑрийн цаг</translation>
<translation id="3479552764303398839">Одоо биш</translation>
<translation id="3484560055331845446">Та Google БүртгÑлийнхÑÑ Ñ…Ð°Ð½Ð´Ð°Ð»Ñ‚Ñ‹Ð³ алдаж болзошгүй. Chrome таныг нууц үгÑÑ Ð¾Ð´Ð¾Ð¾ Ñолихыг зөвлөж байна. Ð¢Ð°Ð½Ð°Ð°Ñ Ð½ÑвтрÑхийг аÑууна.</translation>
+<translation id="3484861421501147767">Сануулагч: ХадгалÑан урамшууллын код боломжтой</translation>
<translation id="3487845404393360112">Гарах цааÑны тавиур 4</translation>
<translation id="3495081129428749620">ХуудаÑÐ½Ð°Ð°Ñ Ð¾Ð»Ð¾Ñ…
<ph name="PAGE_TITLE" /></translation>
@@ -1057,6 +1061,7 @@
<translation id="3810973564298564668">удирдах</translation>
<translation id="3816482573645936981">Утга (хүчингүй болгоÑон)</translation>
<translation id="382518646247711829">Ð¥ÑÑ€Ñв та прокÑи Ñерверийг ашиглах бол ...</translation>
+<translation id="3826050100957962900">Гуравдагч талын нÑвтрÑлт</translation>
<translation id="3827112369919217609">ҮнÑмлÑхүй</translation>
<translation id="3827666161959873541">ГÑÑ€ бүлийн кино</translation>
<translation id="3828924085048779000">ХооÑон нууц үгийг зөвшөөрөхгүй.</translation>
@@ -1069,9 +1074,9 @@
<translation id="3858027520442213535">Огноо болон цагийг шинÑчлÑÑ…</translation>
<translation id="3858860766373142691">ÐÑÑ€</translation>
<translation id="3872834068356954457">ШинжлÑÑ… ухаан</translation>
+<translation id="3875783148670536197">Ðадад Ñ…ÑрхÑн хийхийг харуулна уу</translation>
<translation id="3881478300875776315">Ðрай цөөн мөр харуулах</translation>
<translation id="3884278016824448484">ЗөрчилдөөнтÑй төхөөрөмж танигч</translation>
-<translation id="3885155851504623709">Тойрог</translation>
<translation id="388632593194507180">Ð¥Ñналтыг илрүүллÑÑ</translation>
<translation id="3886948180919384617">Гарах цааÑыг зÑÑ€ÑгцүүлÑÑ… тавцан 3</translation>
<translation id="3890664840433101773">ИмÑйл нÑмÑÑ…</translation>
@@ -1101,9 +1106,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" />-г блоклоÑон байна</translation>
<translation id="3978338123949022456">Хайлтын горим, аÑуулга бичÑÑд <ph name="KEYWORD_SUFFIX" />-Ñ€ хайхын тулд Enter дÑÑÑ€ дарна уу</translation>
<translation id="398470910934384994">Шувуу</translation>
+<translation id="3985750352229496475">ХаÑгуудыг удирдах...</translation>
<translation id="3986705137476756801">Одоохондоо Шууд тайлбарыг унтраах</translation>
<translation id="3987940399970879459">1 МБ-Ð°Ð°Ñ Ð±Ð°Ð³Ð°</translation>
<translation id="3990250421422698716">ШилжүүлÑн байрлуулах оффÑет</translation>
+<translation id="3992684624889376114">Ð­Ð½Ñ Ñ…ÑƒÑƒÐ´Ð°Ñны тухай</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> Ñайт үндÑÑн удирдамжийг
бүх Ñ…Ò¯ÑÑлтдÑÑ Ñ…ÑÑ€ÑгжүүлÑÑ… Ñ…Ò¯ÑÑлт тавьÑан Ñ…Ñдий ч ÑÐ½Ñ ÑƒÐ´Ð¸Ñ€Ð´Ð°Ð¼Ð¶Ð¸Ð¹Ð³ одоогоор Ñ…ÑÑ€ÑгжүүлÑÑ… боломжгүй байна.</translation>
<translation id="4006465311664329701">Google Pay-г ашиглаж буй төлбөрийн Ñ…ÑÑ€ÑгÑÑл, Ñанал болон хаÑг</translation>
@@ -1228,6 +1235,7 @@
<translation id="4305666528087210886">Таны файлд хандаж чадÑангүй</translation>
<translation id="4306529830550717874">ХаÑгийг хадгалах уу?</translation>
<translation id="4306812610847412719">түр Ñанах ой</translation>
+<translation id="4310070645992025887">ÐÑллаа хайх</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Блоклох (өгөгдмөл)</translation>
<translation id="4314815835985389558">Синк хийхийг удирдах</translation>
@@ -1278,6 +1286,7 @@
<translation id="4435702339979719576">Ил захидал)</translation>
<translation id="443673843213245140">Орлон ашиглах Ñрхийг хааÑан байгаа боловч тодорхой түвшинд орлон ашиглах тохиргоог туÑгаÑан байна.</translation>
<translation id="4441832193888514600">Бодлогыг зөвхөн үүлний Ñ…ÑÑ€ÑглÑгчийн бодлого байдлаар тохируулах боломжтой тул үл Ñ…ÑÑ€ÑгÑÑÑн.</translation>
+<translation id="4442470707340296952">Chrome-н табууд</translation>
<translation id="4450893287417543264">Дахиж бүү харуул</translation>
<translation id="4451135742916150903">HID төхөөрөмжүүдÑд холбогдохыг Ñ…Ò¯ÑÑÑ… боломжтой</translation>
<translation id="4452328064229197696">Таны дөнгөж ÑÐ°Ñ Ð°ÑˆÐ¸Ð³Ð»Ð°Ñан нууц үгийг өгөгдлийн Ð·Ó©Ñ€Ñ‡Ð»Ó©Ó©Ñ Ð¾Ð»Ð»Ð¾Ð¾. Таны бүртгÑлүүдийг хамгаалахын тулд Google-н Ðууц үгний менежер нь хадгалÑан нууц үгнүүдÑÑ ÑˆÐ°Ð»Ð³Ð°Ñ…Ñ‹Ð³ зөвлөж байна.</translation>
@@ -1416,6 +1425,7 @@
<translation id="483241715238664915">Сануулгыг аÑаах</translation>
<translation id="4834250788637067901">Google Pay-г ашиглаж буй төлбөрийн Ñ…ÑÑ€ÑгÑÑл, Ñанал болон хаÑг</translation>
<translation id="4838327282952368871">Зүүд шиг</translation>
+<translation id="4839087176073128681">Дараагийн удаа илүү хурдан төлж, Google-н Ñалбартаа Ñ‚ÑргүүлÑгч хамгаалалтаар картаа хамгаалаарай.</translation>
<translation id="4840250757394056958">Chrome-н түүхÑÑ Ò¯Ð·ÑÑ…</translation>
<translation id="484462545196658690">Ðвтомат</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> болон буÑад худалдаачны хөнгөлөлт аваарай</translation>
@@ -1478,6 +1488,7 @@
<translation id="5011561501798487822">ИлÑÑ€ÑÑн Ñ…Ñл</translation>
<translation id="5015510746216210676">Машины нÑÑ€:</translation>
<translation id="5017554619425969104">Таны хуулÑан текÑÑ‚</translation>
+<translation id="5017828934289857214">Ðадад дараа Ñануулна уу</translation>
<translation id="5018422839182700155">Ð­Ð½Ñ Ñ…ÑƒÑƒÐ´Ñыг нÑÑÑ… боломжгүй байна</translation>
<translation id="5019198164206649151">Муу нөхцөл байдал Ò¯Ò¯ÑÑÑн үед хадгалалт хийх</translation>
<translation id="5020776957610079374">ДÑлхийн хөгжим</translation>
@@ -1497,6 +1508,7 @@
<translation id="5051305769747448211">Шууд хошин шог</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Ойролцоо хуваалцахыг ашиглан ÑÐ½Ñ Ñ„Ð°Ð¹Ð»Ñ‹Ð³ илгÑÑхийн тулд төхөөрөмж дÑÑÑ€ÑÑ Ñул зай (<ph name="DISK_SPACE_SIZE" />) гаргана уу}other{Ойролцоо хуваалцахыг ашиглан ÑдгÑÑÑ€ файлыг илгÑÑхийн тулд төхөөрөмж дÑÑÑ€ÑÑ Ñул зай (<ph name="DISK_SPACE_SIZE" />) гаргана уу}}</translation>
<translation id="5056549851600133418">Танд зориулÑан нийтлÑл</translation>
+<translation id="5060483733937416656">Та <ph name="PROVIDER_ORIGIN" />-г ашигладаг вебÑайтууд дÑÑÑ€ Windows Hello-Ñ€ баталгаажуулахаар ÑонгоÑон. Ð­Ð½Ñ Ò¯Ð¹Ð»Ñ‡Ð¸Ð»Ð³ÑÑ Ò¯Ð·Ò¯Ò¯Ð»Ñгч таны төлбөрийн Ñ…ÑÑ€ÑгÑлийн талаарх мÑдÑÑллийг хадгалÑан байж болзошгүй бөгөөд та <ph name="LINK_TEXT" /> боломжтой.</translation>
<translation id="5061227663725596739">Та <ph name="LOOKALIKE_DOMAIN" />-г хайÑан уу?</translation>
<translation id="5066056036849835175">Ð¥ÑвлÑлийн түүх</translation>
<translation id="5068234115460527047">Ð¥Ñдж Ñан</translation>
@@ -1510,10 +1522,8 @@
<translation id="5087286274860437796">Серверийн гÑрчилгÑÑ Ð½ÑŒ oдоогоор хүчин төгөлдөр Ð±ÑƒÑ Ð±Ð°Ð¹Ð½Ð°.</translation>
<translation id="5087580092889165836">Карт нÑмÑÑ…</translation>
<translation id="5088142053160410913">Операторт илгÑÑÑ… меÑÑеж</translation>
-<translation id="5089810972385038852">Муж</translation>
<translation id="5093232627742069661">Z-нугалаа</translation>
<translation id="5094747076828555589">Ð­Ð½Ñ Ñервер нь <ph name="DOMAIN" />-аа баталж чадÑангүй; учир нь ÑнÑÑ…Ò¯Ò¯ Ñертификатыг Chromium хүлÑÑн зөвшөөрдөггүй. Ð­Ð½Ñ Ð½ÑŒ тохиргоо буруу хийгдÑÑнÑÑÑ ÑÑвÑл халдагч таны холболтонд Ñаад учруулж Ð±Ð°Ð¹Ð³Ð°Ð°Ð³Ð°Ð°Ñ ÑˆÐ°Ð»Ñ‚Ð³Ð°Ð°Ð»Ð¶ болох юм.</translation>
-<translation id="5095208057601539847">Ðймаг</translation>
<translation id="5097099694988056070">CPU/RAM-н ашиглалт зÑÑ€Ñг төхөөрөмжийн ÑтатиÑтик</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Сайт аюултай байна</translation>
@@ -1551,6 +1561,7 @@
<translation id="5171045022955879922">Хайх ÑÑвÑл холбооÑыг бичиж оруул</translation>
<translation id="5171689220826475070">Фанфолд-Европ</translation>
<translation id="5172758083709347301">Машин</translation>
+<translation id="5177076414499237632">Ð­Ð½Ñ Ñ…ÑƒÑƒÐ´Ð°Ñны ÑÑ… Ñурвалж болон ÑÑдвийн талаар мÑдÑж авах</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> Ñ…ÑлÑÑÑ€ байхгүй байна уу? Ð­Ð½Ñ Ð°Ð»Ð´Ð°Ð°Ð³ мÑдÑгдÑÐ½Ñ Ò¯Ò¯.</translation>
<translation id="518639307526414276">ТÑжÑÑвÑÑ€ амьтны хоол, арчилгааны Ñ…ÑÑ€ÑгÑÑл</translation>
<translation id="5190835502935405962">Хайлтын жагÑаалтын цонх</translation>
@@ -1711,6 +1722,7 @@
<translation id="5624120631404540903">Ðууц үгийг удирдах</translation>
<translation id="5629630648637658800">Тохиргоог ачаалж чадÑангүй.</translation>
<translation id="5631439013527180824">Хүчин төгөлдөр Ð±ÑƒÑ Ñ‚Ó©Ñ…Ó©Ó©Ñ€Ó©Ð¼Ð¶Ð¸Ð¹Ð½ удирдлагын Ñ‚ÑмдÑглÑгÑÑ</translation>
+<translation id="5632485077360054581">Ðадад Ñ…ÑрхÑн хийхийг харуулна уу</translation>
<translation id="5633066919399395251">Таны зураг, нууц үг, меÑÑеж, кредит карт зÑÑ€Ñг мÑдÑÑллийг хулгайлах ÑÑвÑл уÑтгах аюултай программыг таны компьютер дÑÑÑ€ Ñуулгаж болзошгүй халдагчид <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />-д байна. <ph name="BEGIN_LEARN_MORE_LINK" />ДÑлгÑÑ€Ñнгүй үзÑÑ…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Хуурамч агуулгыг блоклоÑон.</translation>
<translation id="5633259641094592098">Тахин шүтÑгдÑÑн, инди кино</translation>
@@ -1828,6 +1840,7 @@
<translation id="5989320800837274978">ПрокÑи Ñерверүүд болон а.pac бичвÑрийн холбооÑын аль алиныг нь зааж өгөөгүй байна.</translation>
<translation id="5992691462791905444">ИнженерчлÑлийн Z-нугалаа</translation>
<translation id="5995727681868049093">Google БүртгÑлийнхÑÑ Ð¼ÑдÑÑлÑл, нууцлал болон аюулгүй байдлыг удирдаарай</translation>
+<translation id="5997247540087773573">Таны дөнгөж ÑÐ°Ñ Ð°ÑˆÐ¸Ð³Ð»Ð°Ñан нууц үгийг өгөгдлийн Ð·Ó©Ñ€Ñ‡Ð»Ó©Ó©Ñ Ð¾Ð»Ð»Ð¾Ð¾. Таны бүртгÑлүүдийг хамгаалахын тулд Google Password Manager үүнийг одоо өөрчлөх болон хадгалÑан нууц үгнүүдÑÑ ÑˆÐ°Ð»Ð³Ð°Ñ…Ñ‹Ð³ зөвлөж байна.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />'-н <ph name="RESULT_COUNT" /> илÑрц</translation>
<translation id="6006484371116297560">Сонгодог</translation>
<translation id="6008122969617370890">N-Ñ 1 хүртÑлх дараалал</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">Дараагийн удаа төлбөрөө хурдан төлөхийн тулд Google БүртгÑлдÑÑ ÐºÐ°Ñ€Ñ‚ болон тооцооны хаÑгаа хадгална уу.</translation>
<translation id="6279183038361895380">КурÑороо харуулахын тулд |<ph name="ACCELERATOR" />|-г дарна уу</translation>
<translation id="6280223929691119688">Ð­Ð½Ñ Ñ…Ð°Ñг руу хүргÑÑ… боломжгүй тул Ó©Ó©Ñ€ хаÑг Ñонгоно уу.</translation>
-<translation id="6282194474023008486">Шуудангийн код</translation>
<translation id="6285507000506177184">Chrome-д таталтуудыг удирдах товчлуур, Chrome-д татÑан файлуудаа удирдахын тулд Enter дÑÑÑ€ дарна уу</translation>
<translation id="6289939620939689042">ХуудаÑны өнгө</translation>
<translation id="6290238015253830360">Таны Ñанал болгоÑон нийтлÑл Ñнд харагдана</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026"><ph name="SIZE" />-Ñ Ð±Ð°Ð³Ð° зай гаргана. Зарим Ñайтад зочлох үед удаан ачаалж болзошгүй.</translation>
<translation id="6337534724793800597">Журмуудыг нÑÑ€ÑÑÑ€ нь шүү</translation>
<translation id="6340739886198108203">ÐдминиÑтраторын бодлого нь нууцлалтай контент харагдаж байх үед дÑлгÑцийн агшин авах ÑÑвÑл бичлÑг хийхийг Ñанал болгодоггүй:</translation>
+<translation id="6348220984832452017">ИдÑвхтÑй хувилбарууд</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" />-г Ñуулгах</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Байхгүй}=1{(<ph name="DOMAIN_LIST" />-н) 1 нууц үг (Ñинк хийÑÑн)}=2{(<ph name="DOMAIN_LIST" />-н) 2 нууц үг (Ñинк хийÑÑн)}other{(<ph name="DOMAIN_LIST" />-н) # нууц үг (Ñинк хийÑÑн)}}</translation>
<translation id="6355392890578844978">Ð­Ð½Ñ Ñ…Ó©Ñ‚Ñ‡Ð¸Ð¹Ð³ компани ÑÑвÑл буÑад Ð±Ð°Ð¹Ð³ÑƒÑƒÐ»Ð»Ð°Ð³Ð°Ð°Ñ ÑƒÐ´Ð¸Ñ€Ð´Ð´Ð°Ð³Ð³Ò¯Ð¹. Ð­Ð½Ñ Ñ‚Ó©Ñ…Ó©Ó©Ñ€Ó©Ð¼Ð¶ дÑÑрх үйл ажиллагааг Chromium-Ñ Ð³Ð°Ð´ÑƒÑƒÑ€ удирддаг байж болно. <ph name="BEGIN_LINK" />ÐÑмÑлт мÑдÑÑлÑл авах<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">Google-н нууц үгийг өөрчлөх</translation>
<translation id="6433490469411711332">Харилцагчийн мÑдÑÑллийг заÑах</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> нь Ñ…Ð¾Ð»Ð±Ð¾Ð³Ð´Ð¾Ñ…Ð¾Ð¾Ñ Ñ‚Ð°Ñ‚Ð³Ð°Ð»Ð·Ð»Ð°Ð°.</translation>
-<translation id="6438025220577812695">Үүнийг Ó©Ó©Ñ€Ó©Ó© Ñолих</translation>
<translation id="6440503408713884761">ХаагдÑан</translation>
<translation id="6443406338865242315">Таны Ñмар өргөтгөл болон залгааÑыг ÑуулгаÑан болох</translation>
<translation id="6446163441502663861">Каху (Дугтуй)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">Генетик</translation>
<translation id="6831043979455480757">ХөрвүүлÑÑ…</translation>
<translation id="6833752742582340615">Ðюулгүй бөгөөд илүү хурдан тооцоо хийх бол карт болон төлбөр тооцооны мÑдÑÑллÑÑ Google БүртгÑлдÑÑ Ñ…Ð°Ð´Ð³Ð°Ð»Ð½Ð° уу</translation>
-<translation id="6839929833149231406">Ð¥ÑÑÑг</translation>
<translation id="6846340164947227603">Виртуал картын дугаар ашиглах...</translation>
<translation id="6852204201400771460">Ðппыг дахин ачаалaÑ… уу?</translation>
+<translation id="6857776781123259569">Ðууц үгнүүдийг удирдах...</translation>
<translation id="686485648936420384">Ð¥ÑÑ€ÑглÑгчийн нөөц</translation>
<translation id="6865412394715372076">Ð­Ð½Ñ ÐºÐ°Ñ€Ñ‚Ñ‹Ð³ одоогоор баталгаажуулах боломжгүй байна</translation>
<translation id="6869334554832814367">Хувийн зÑÑл</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">Төхөөрөмж</translation>
<translation id="696703987787944103">Ðарийвчлалд хамааралтай</translation>
<translation id="6968269510885595029">Ðюулгүй байдлын түлхүүрÑÑ Ð°ÑˆÐ¸Ð³Ð»Ð°Ð½Ð° уу</translation>
-<translation id="6970216967273061347">ДүүрÑг</translation>
<translation id="6971439137020188025">ҮзүүлÑнд ÑˆÐ¸Ð½Ñ Google үзүүлÑн шуурхай Ò¯Ò¯ÑгÑÑÑ€Ñй</translation>
<translation id="6972629891077993081">HID төхөөрөмжүүд</translation>
<translation id="6973656660372572881">Тогтмол прокÑи Ñервер болон а.pac бичвÑрийн холбооÑын аль алиныг нь зааж өгнө.</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">Таны төхөөрөмж дÑÑÑ€ ÑÐ½Ñ Ð¾Ð½Ñ†Ð»Ð¾Ð³ боломжгүй байна</translation>
<translation id="7083258188081898530">Гарах цааÑны тавиур 9</translation>
<translation id="7086090958708083563">Ð¥ÑÑ€ÑглÑгч байршуулах Ñ…Ò¯ÑÑлт тавьÑан</translation>
-<translation id="7087282848513945231">Муж</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. Chrome-н тохиргоонд Ñайтууд дÑÑÑ€ хадгалÑан зөвшөөрөл болон өгөгдлийг удирдахын тулд Tab, дараа нь Enter дÑÑÑ€ дарна уу</translation>
<translation id="7096937462164235847">Ð­Ð½Ñ Ð²ÐµÐ±Ñайтын таниулбарыг баталгаажуулаагүй байна.</translation>
<translation id="7101893872976785596">Ðймшгийн кино</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />МаÑгтад оруулÑан мÑдÑÑлÑл<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Ð­Ð½Ñ Ñ…Ð°Ñг руу хүргÑÑ… боломжгүй тул Ó©Ó©Ñ€ хаÑг Ñонгоно уу.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Таны админ ÑÐ½Ñ Ó©Ð³Ó©Ð³Ð´Ð»Ð¸Ð¹Ð³ хуулахыг хориглоÑон.</translation>
<translation id="7135130955892390533">Төлөвийг харуулах</translation>
<translation id="7138472120740807366">ХүргÑлтийн арга</translation>
-<translation id="7139724024395191329">Эмират улÑ</translation>
<translation id="7139892792842608322">ҮндÑÑн тавиур</translation>
<translation id="714064300541049402">ХуудаÑны ар тал дÑÑÑ€ X Ñ‚ÑнхлÑгийн дагуу зураг шилжүүлÑÑ…</translation>
<translation id="7152423860607593928">Дугаар-14 (Дугтуй)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">Ð­Ð½Ñ Ñайтад байгаа халдагчид таны хайлтын горимд гÑмтÑл учруулж болох программ Ñуулгахыг (жишÑÑ Ð½ÑŒ: нүүр хуудÑаа Ñолих, ÑÑвÑл зочилдог Ñайтдаа нÑмÑлт зар харуулах) Ñанал болгож болзошгүй.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Ðууцлалтай гÑж дарцаглаÑан өгөгдөл дÑÑÑ€ хийÑÑн үйлдÑл (нÑвтÑÑ€ÑнÑÑÑ Ñ…Ð¾Ð¹Ñˆ 1 үйлдÑл). <ph name="BEGIN_LINK" />ÐÑмÑлт мÑдÑÑлÑл авах<ph name="END_LINK" />}other{Ðууцлалтай гÑж дарцаглаÑан өгөгдөл дÑÑÑ€ хийÑÑн үйлдлүүд (нÑвтÑÑ€ÑнÑÑÑ Ñ…Ð¾Ð¹Ñˆ # үйлдÑл). <ph name="BEGIN_LINK" />ÐÑмÑлт мÑдÑÑлÑл авах<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Шуудангийн хайрцаг 6</translation>
+<translation id="7675325315208090829">Төлбөрийн Ñ…ÑÑ€ÑгÑлүүдийг удирдах...</translation>
<translation id="7676643023259824263">Түр Ñанах ойн текÑÑ‚ болох <ph name="TEXT" />-г хайх</translation>
<translation id="7679367271685653708">Chrome-н тохиргоонд хөтчийн түүхÑÑ Ò¯Ð·ÑÑ… болон удирдана уу</translation>
<translation id="7679947978757153706">БÑйÑбол</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">Хормой</translation>
<translation id="7770259615151589601">СонгоÑон-Урт</translation>
<translation id="7773005668374414287">ÐүүрÑÑÑ€ нь дÑÑш харуулж, хуудаÑны дарааллын дагуу</translation>
-<translation id="777702478322588152">ДүүрÑг</translation>
<translation id="7791011319128895129">Гараагүй</translation>
<translation id="7791196057686275387">Багцалж захлах</translation>
<translation id="7791543448312431591">ÐÑмÑÑ…</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">ÐœÑргÑжлийн, таÑралтгүй боловÑрол</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />"-г зөв тохируулаагүй байна. "<ph name="SOFTWARE_NAME" />"-г уÑтгаÑнаар аÑуудлыг шийдвÑрлÑÑ… боломжтой. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ХүнÑний үйлдвÑрлÑл</translation>
-<translation id="8066955247577885446">Уучлаарай, Ñмар нÑг алдаа гарлаа.</translation>
<translation id="8067872629359326442">Та дөнгөж ÑÐ°Ñ ÑÑжигтÑй Ñайтад нууц үгÑÑ Ð¾Ñ€ÑƒÑƒÐ»Ð»Ð°Ð°. Chromium туÑлах боломжтой. Ðууц үгÑÑ Ó©Ó©Ñ€Ñ‡Ð¸Ð»Ð¶, таны бүртгÑл ÑÑ€ÑдÑлд oÑ€Ñон байж болзошгүй тухай Google-д мÑдÑгдÑхийн тулд БүртгÑлийг хамгаалах гÑÑнийг товшино уу.</translation>
<translation id="8070439594494267500">Aппын Ð´Ò¯Ñ€Ñ Ñ‚ÑмдÑг</translation>
<translation id="8074253406171541171">10x13 (Дугтуй)</translation>
<translation id="8075736640322370409">Ð¨Ð¸Ð½Ñ Google Ð¥Ò¯ÑнÑгт шуурхай Ò¯Ò¯ÑгÑÑÑ€Ñй</translation>
<translation id="8075898834294118863">Сайтын тохиргоог удирдах</translation>
+<translation id="8076492880354921740">Таб</translation>
<translation id="8078141288243656252">ЭргүүлÑÑн үед Ñ‚ÑмдÑглÑгÑÑ Ñ…Ð¸Ð¹Ñ… боломжгүй</translation>
<translation id="8079031581361219619">Сайтыг дахин ачааллах уу?</translation>
<translation id="8081087320434522107">Седан</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Тохиргоо</translation>
+<translation id="8428634594422941299">Ойлголоо</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. Chrome-н тохиргоонд күүкиний Ñонголтоо удирдахын тулд Tab, дараа нь Enter дÑÑÑ€ дарна уу</translation>
<translation id="8433057134996913067">Ð­Ð½Ñ Ñонголт нь таныг ихÑнх веб ÑÐ°Ð¹Ñ‚Ð°Ð°Ñ Ð³Ð°Ñ€Ð³Ð°Ñ… болно.</translation>
<translation id="8434840396568290395">TÑжÑÑвÑÑ€ амьтан</translation>
@@ -2792,6 +2803,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> бол <ph name="ORIGIN" />-д зориулÑан таны код</translation>
<translation id="874918643257405732">Ð­Ð½Ñ Ñ‚Ð°Ð±Ñ‹Ð³ Ñ‚ÑмдÑглÑÑ…</translation>
<translation id="8751426954251315517">Дараагийн удаа дахин оролдоно уу</translation>
+<translation id="8757526089434340176">Google Pay-н Ñанал боломжтой</translation>
<translation id="8758885506338294482">Цахим Ñпорт</translation>
<translation id="8759274551635299824">Ð­Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð½Ñ‹ хугацаа дууÑÑан байна</translation>
<translation id="87601671197631245">Ð­Ð½Ñ Ñайт аюулгүй байдлын хуучирÑан тохируулга ашиглаж байгаа тул таны мÑдÑÑллийг (жишÑÑлбÑл нууц үг, меÑÑеж ÑÑвÑл кредит карт) ÑÐ½Ñ Ñайт руу илгÑÑÑ… үед задруулж болзошгүй.</translation>
@@ -2799,6 +2811,7 @@
<translation id="8763927697961133303">USB төхөөрөмж</translation>
<translation id="8763986294015493060">Одоогоор нÑÑлттÑй байгаа бүх нууцлалтай цонхыг хаагаарай</translation>
<translation id="8766943070169463815">Ðюулгүй төлбөрийн мандат үнÑмлÑхийг баталгаажуулах Ñ…Ò¯ÑнÑгтийг нÑÑÑÑн</translation>
+<translation id="8767765348545497220">ТуÑламжийн бөмбөлгийг хаах</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Мотоцикл</translation>
<translation id="8790007591277257123">&amp; УÑтгах үйлдлийг дахин хийх</translation>
@@ -2811,6 +2824,7 @@
<translation id="8806285662264631610">УÑанд орох, биеийн бүтÑÑгдÑхүүн</translation>
<translation id="8807160976559152894">Ð¥ÑƒÑƒÐ´Ð°Ñ Ð±Ò¯Ñ€Ð¸Ð¹Ð½ дараа тайрах</translation>
<translation id="8808828119384186784">Chrome-н тохиргоо</translation>
+<translation id="8813277370772331957">Ðадад дараа Ñануулах</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome-н тохиргоонооÑоо Chrome-г шинÑчлÑхийн тулд ÑхлÑÑд Таб дÑÑÑ€, дараа нь Enter дарна уу</translation>
<translation id="8820817407110198400">Хавчуургууд</translation>
<translation id="882338992931677877">Гараар оруулах оролт</translation>
@@ -2990,6 +3004,7 @@
<translation id="988159990683914416">ХөгжүүлÑгчийн хөгжүүлÑÑ… Ñ…ÑÑÑг</translation>
<translation id="989988560359834682">ХаÑгийг заÑварлах</translation>
<translation id="991413375315957741">хөдөлгөөн ÑÑвÑл гÑÑ€Ñл мÑдрÑгч</translation>
+<translation id="992110854164447044">Виртуал карт нь таныг болзошгүй Ð·Ð°Ð»Ð¸Ð»Ð°Ð½Ð³Ð°Ð°Ñ Ñ…Ð°Ð¼Ð³Ð°Ð°Ð»Ð°Ñ…Ð°Ð´ туÑлахын тулд таны жинхÑÐ½Ñ ÐºÐ°Ñ€Ñ‚Ñ‹Ð³ нуудаг. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Ягаан</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />"-г таны компьютер ÑÑвÑл ÑүлжÑÑнд зөв Ñуулгаагүй байна:
diff --git a/chromium/components/strings/components_strings_mr.xtb b/chromium/components/strings/components_strings_mr.xtb
index b41b739d4c8..283c8f17812 100644
--- a/chromium/components/strings/components_strings_mr.xtb
+++ b/chromium/components/strings/components_strings_mr.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">अनà¥à¤¦à¤¾à¤¨à¥‡, शिषà¥à¤¯à¤µà¥ƒà¤¤à¥à¤¤à¥€ आणि वितà¥à¤¤à¤¸à¤¾à¤¹à¥à¤¯</translation>
<translation id="1048785276086539861">तà¥à¤®à¥à¤¹à¥€ भाषà¥à¤¯à¥‡ संपादित करता तेवà¥à¤¹à¤¾ हा दसà¥à¤¤à¤à¤µà¤œ à¤à¤•à¤² पेजवà¥à¤¹à¥à¤¯à¥‚वर परत येईल</translation>
<translation id="1050038467049342496">अनà¥à¤¯ ॲपà¥à¤¸ बंद करा</translation>
+<translation id="1053959602163383901">तà¥à¤®à¥à¤¹à¥€ <ph name="PROVIDER_ORIGIN" /> वापरणाऱà¥à¤¯à¤¾ वेबसाइटवर ऑथेंटिकेटर डिवà¥à¤¹à¤¾à¤‡à¤¸à¤¸à¤¹ पडताळणी करणà¥à¤¯à¤¾à¤šà¥‡ निवडले आहे. या पà¥à¤°à¤µà¤ à¤¾à¤¦à¤¾à¤°à¤¾à¤¨à¥‡ तà¥à¤®à¤šà¥à¤¯à¤¾ पेमेंट पदà¥à¤§à¤¤à¥€à¤µà¤¿à¤·à¤¯à¥€ माहिती सà¥à¤Ÿà¥‹à¤…र केलेली असू शकते, जी तà¥à¤®à¥à¤¹à¥€ <ph name="LINK_TEXT" /> करू शकता.</translation>
<translation id="1055184225775184556">&amp;जोडा पूरà¥à¤µà¤µà¤¤ करा</translation>
<translation id="1056663316309890343">फोटोशी संबंधित सॉफà¥à¤Ÿà¤µà¥‡à¤…र</translation>
<translation id="1056898198331236512">चेतावणी</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">घेणà¥à¤¯à¤¾à¤šà¥€ पदà¥à¤§à¤¤</translation>
<translation id="1281476433249504884">सà¥à¤Ÿà¥…कर १</translation>
<translation id="1285320974508926690">या साइटचा कधीही भाषांतर करॠनका</translation>
+<translation id="1288548991597756084">कारà¥à¤¡ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤ªà¤£à¥‡ सेवà¥à¤¹ करा</translation>
<translation id="1292571435393770077">टà¥à¤°à¥‡ १६</translation>
<translation id="1292701964462482250">"तà¥à¤®à¤šà¥à¤¯à¤¾ कॉंपà¥à¤¯à¥à¤Ÿà¤°à¤µà¤°à¥€à¤² सॉफà¥à¤Ÿà¤µà¥‡à¤…र Chrome ला सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤ªà¤£à¥‡ वेबशी कनेकà¥à¤Ÿ होणà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न थांबवत आहे" (फकà¥â€à¤¤ Windows कॉंपà¥à¤¯à¥à¤Ÿà¤°à¤šà¤¾)</translation>
<translation id="1294154142200295408">विविध कमांड लाइन</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;à¤à¤°à¤° निघून जाणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, तà¥à¤®à¥à¤¹à¥€ उघडणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करत असलेलà¥à¤¯à¤¾ पेजवर &lt;strong&gt;कनेकà¥à¤Ÿ&lt;/strong&gt; वर कà¥à¤²à¤¿à¤• करा.&lt;/p&gt;</translation>
<translation id="1507780850870535225">लॅंडसà¥à¤•à¥‡à¤ª डिà¤à¤¾à¤‡à¤¨</translation>
<translation id="1513706915089223971">इतिहास नोंदींची सूची</translation>
+<translation id="1516097932025103760">ते à¤à¤‚कà¥à¤°à¤¿à¤ªà¥à¤Ÿ केले जाईल, सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤ªà¤£à¥‡ सेवà¥à¤¹ केले जाईल आणि CVC कधीही सà¥à¤Ÿà¥‹à¤…र केला जात नाही.</translation>
<translation id="1517433312004943670">फोन नंबर आवशà¥à¤¯à¤• आहे</translation>
<translation id="1519264250979466059">बिलà¥à¤¡ तारीख</translation>
<translation id="1521159554480556801">फायबर आणि टेकà¥à¤¸à¤Ÿà¤¾à¤‡à¤² आरà¥à¤Ÿ</translation>
@@ -272,7 +275,7 @@
<translation id="1634828734222219955">à¤à¤•à¥‚ण</translation>
<translation id="163669211644121865">कर भरणा आणि नियोजन</translation>
<translation id="1638780421120290329">कारà¥à¤¡ सेवà¥à¤¹ करू शकलो नाही</translation>
-<translation id="1639239467298939599">लोड करीत आहे</translation>
+<translation id="1639239467298939599">लोड करत आहे</translation>
<translation id="1640180200866533862">वापरकरà¥à¤¤à¤¾ धोरणे</translation>
<translation id="1640244768702815859"><ph name="BEGIN_LINK" />साइटचà¥à¤¯à¤¾ होमवर भेट देऊन<ph name="END_LINK" /> पहा.</translation>
<translation id="1641976391427233992">या कालावधीपरà¥à¤¯à¤‚त आउटपà¥à¤Ÿà¤²à¤¾ विलंब करा</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">पेमेंट पदà¥à¤§à¤¤à¥€ सेवà¥à¤¹ करा आणि भरा</translation>
<translation id="1663943134801823270">कारà¥à¤¡ आणि पतà¥à¤¤à¥‡ Chrome कडील आहेत. तà¥à¤®à¥à¤¹à¥€ तà¥à¤¯à¤¾à¤‚ना <ph name="BEGIN_LINK" />सेटिंगà¥â€à¤œ<ph name="END_LINK" /> मधून वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करू शकता.</translation>
<translation id="1671391448414634642">आतापासून <ph name="SOURCE_LANGUAGE" />मधील पेज <ph name="TARGET_LANGUAGE" />मधà¥à¤¯à¥‡ भाषांतरित केली जातील.</translation>
+<translation id="1673886523110456987">ऑफर वापरणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ <ph name="CARD_DETAIL" /> सह चेक आउट करा</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> ते <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">शॉरà¥à¤Ÿ à¤à¤œ पà¥à¤°à¤¥à¤®</translation>
<translation id="168693727862418163">हे धोरण मूलà¥à¤¯ तà¥à¤¯à¤¾à¤šà¥à¤¯à¤¾ सà¥à¤•à¥€à¤®à¤¾à¤¸à¥‹à¤¬à¤¤ पà¥à¤°à¤®à¤¾à¤£à¤¿à¤¤ करता आले नाही आणि तà¥à¤¯à¤¾à¤šà¥à¤¯à¤¾à¤•à¤¡à¥‡ दà¥à¤°à¥à¤²à¤•à¥à¤· केले जाईल.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">मोशन सेनà¥à¤¸à¤°</translation>
<translation id="1717494416764505390">मेलबॉकà¥à¤¸ ३</translation>
<translation id="1718029547804390981">भाषà¥à¤¯ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ दसà¥à¤¤à¤à¤µà¤œ खूपच मोठे आहे</translation>
+<translation id="1720941539803966190">टà¥à¤¯à¥‚टोरियल बंद करा</translation>
<translation id="1721424275792716183">* फीलà¥à¤¡ आवशà¥à¤¯à¤• आहे</translation>
<translation id="1727613060316725209">सरà¥à¤Ÿà¤¿à¤«à¤¿à¤•à¥‡à¤Ÿ योगà¥à¤¯ आहे</translation>
<translation id="1727741090716970331">वैध कारà¥à¤¡ नंबर जोडा</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">BBQ आणि गà¥à¤°à¤¿à¤²à¤¿à¤‚ग</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" />मधील पेज भाषांतरीत केले जाणार नाहीत.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{When this control is on and the status is active, Chrome determines which large group of people, or "cohort," your recent browsing activity is most similar to. Advertisers can select ads for the group and your browsing activity is kept private on your device. Your group is updated every day.}=1{हे नियंतà¥à¤°à¤£ सà¥à¤°à¥‚ असते आणि तà¥à¤¯à¤¾à¤šà¥‡ सà¥à¤Ÿà¥‡à¤Ÿà¤¸ अâ€à¥…कà¥à¤Ÿà¤¿à¤µà¥à¤¹ असते तेवà¥à¤¹à¤¾, तà¥à¤®à¤šà¥€ अलीकडील बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग अâ€à¥…कà¥à¤Ÿà¤¿à¤µà¥à¤¹à¤¿à¤Ÿà¥€ ही लोकांचà¥à¤¯à¤¾ कोणतà¥à¤¯à¤¾ मोठà¥à¤¯à¤¾ गटासारखी किंवा "समूह" यासारखी आहे हे Chrome निशà¥à¤šà¤¿à¤¤ करते. जाहिरातदार हे गटासाठी जाहिराती निवडू शकतात आणि तà¥à¤®à¤šà¥€ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग अâ€à¥…कà¥à¤Ÿà¤¿à¤µà¥à¤¹à¤¿à¤Ÿà¥€ तà¥à¤®à¤šà¥à¤¯à¤¾ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤µà¤° खाजगी ठेवली जाते. तà¥à¤®à¤šà¤¾ गट दररोज अपडेट केला जातो.}other{हे नियंतà¥à¤°à¤£ सà¥à¤°à¥‚ असते आणि तà¥à¤¯à¤¾à¤šà¥‡ सà¥à¤Ÿà¥‡à¤Ÿà¤¸ अâ€à¥…कà¥à¤Ÿà¤¿à¤µà¥à¤¹ असते तेवà¥à¤¹à¤¾, तà¥à¤®à¤šà¥€ अलीकडील बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग अâ€à¥…कà¥à¤Ÿà¤¿à¤µà¥à¤¹à¤¿à¤Ÿà¥€ ही लोकांचà¥à¤¯à¤¾ कोणतà¥à¤¯à¤¾ मोठà¥à¤¯à¤¾ गटासारखी किंवा "समूह" यासारखी आहे हे Chrome निशà¥à¤šà¤¿à¤¤ करते. जाहिरातदार हे गटासाठी जाहिराती निवडू शकतात आणि तà¥à¤®à¤šà¥€ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग अâ€à¥…कà¥à¤Ÿà¤¿à¤µà¥à¤¹à¤¿à¤Ÿà¥€ तà¥à¤®à¤šà¥à¤¯à¤¾ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤µà¤° खाजगी ठेवली जाते. तà¥à¤®à¤šà¤¾ गट दर {NUM_DAYS} दिवसांनी अपडेट केला जातो.}}</translation>
-<translation id="2053553514270667976">पिन कोड</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 सूचना}other{# सूचना}}</translation>
+<translation id="2066915425250589881">हटवणà¥à¤¯à¤¾à¤šà¥€ विनंती</translation>
<translation id="2068528718802935086">बाळ आणि रांगती मà¥à¤²à¥‡</translation>
<translation id="2071156619270205202">हे कारà¥à¤¡ वà¥à¤¹à¤°à¥à¤šà¥à¤¯à¥à¤…ल कारà¥à¤¡ नंबरसाठी पातà¥à¤° नाही.</translation>
<translation id="2071692954027939183">सूचना आपोआप बà¥à¤²à¥‰à¤• केलà¥à¤¯à¤¾ होतà¥à¤¯à¤¾ कारण तà¥à¤®à¥à¤¹à¥€ सहसा तà¥à¤¯à¤¾à¤‚ना अनà¥à¤®à¤¤à¥€ देत नाही</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">सिंक वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा बटण, Chrome सेटिंगà¥à¤œà¤®à¤§à¥à¤¯à¥‡ तà¥à¤®à¥à¤¹à¥€ कोणती माहिती सिंक करता ते वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ à¤à¤‚टर दाबा</translation>
<translation id="2091887806945687916">धà¥à¤µà¤¨à¥€</translation>
<translation id="2094505752054353250">डोमेन जà¥à¤³à¤¤ नाही</translation>
-<translation id="2096368010154057602">विभाग</translation>
<translation id="2099652385553570808">टà¥à¤°à¤¿à¤ªà¤² सà¥à¤Ÿà¥‡à¤ªà¤² लेफà¥à¤Ÿ</translation>
<translation id="2101225219012730419">आवृतà¥à¤¤à¥€:</translation>
<translation id="2102134110707549001">कà¥à¤²à¤¿à¤·à¥à¤Ÿ पासवरà¥à¤¡ सà¥à¤šà¤µà¤¾â€¦</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">अमेरिकन फà¥à¤Ÿà¤¬à¥‰à¤²</translation>
<translation id="2187317261103489799">शोधा (डीफॉलà¥à¤Ÿ)</translation>
<translation id="2188375229972301266">मलà¥à¤Ÿà¤¿à¤ªà¤² पंच बॉटम</translation>
-<translation id="2188852899391513400">तà¥à¤®à¥à¤¹à¥€ नà¥à¤•à¤¤à¤¾à¤š वापरलेला पासवरà¥à¤¡ डेटा भंगामधà¥à¤¯à¥‡ आढळला होता. तà¥à¤®à¤šà¥€ खाती सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, Google पासवरà¥à¤¡ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤• तो आता बदलणà¥à¤¯à¤¾à¤šà¥€ शिफारस करतो आणि तà¥à¤¯à¤¾à¤¨à¤‚तर तà¥à¤®à¤šà¥‡ सेवà¥à¤¹ केलेले पासवरà¥à¤¡ तपासतो.</translation>
<translation id="219906046732893612">घराचà¥à¤¯à¤¾ सजावटीशी संबंधित सेवा</translation>
<translation id="2202020181578195191">वैध समापà¥à¤¤à¥€ वरà¥à¤· à¤à¤‚टर करा</translation>
<translation id="22081806969704220">टà¥à¤°à¥‡ ३</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">फाइलचे संपादन</translation>
<translation id="2215963164070968490">कà¥à¤¤à¥à¤°à¥‡</translation>
<translation id="2218879909401188352">सधà¥à¤¯à¤¾ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />वर असलेले आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¤¾ तà¥à¤®à¤šà¥à¤¯à¤¾ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤²à¤¾ हानी पोहोचवणारे धोकादायक ॲप इंसà¥à¤Ÿà¥‰à¤² करू शकतात, तà¥à¤®à¤šà¥à¤¯à¤¾ मोबाइल बिलामधà¥à¤¯à¥‡ लपलेले शà¥à¤²à¥à¤• जोडू शकतात किंवा तà¥à¤®à¤šà¥€ वैयकà¥à¤¤à¤¿à¤• माहिती चोरू शकतात. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">टà¥à¤¯à¥‚टोरियल रीसà¥à¤Ÿà¤¾à¤°à¥à¤Ÿ करा</translation>
+<translation id="2219735899272417925">डिवà¥à¤¹à¤¾à¤‡à¤¸ रीसेट करणà¥à¤¯à¤¾à¤šà¥€ आवशà¥à¤¯à¤•à¤¤à¤¾ आहे</translation>
<translation id="2224337661447660594">इंटरनेट नाही</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />निदान ॲप<ph name="END_LINK" /> वापरून आपलà¥à¤¯à¤¾ कनेकà¥à¤¶à¤¨à¤šà¥‡ निराकरण करा</translation>
<translation id="2239100178324503013">आता पाठवा</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">अनà¥à¤®à¤¤à¥€ नाही (डीफॉलà¥à¤Ÿ)</translation>
<translation id="2512413427717747692">Chrome ला डीफॉलà¥à¤Ÿ बà¥à¤°à¤¾à¤‰à¤à¤° बटण मà¥à¤¹à¤£à¥‚न सेट करा, iOS सेटिंगà¥à¤œà¤®à¤§à¥à¤¯à¥‡ Chrome ला सिसà¥à¤Ÿà¥€à¤®à¤šà¤¾ डीफॉलà¥à¤Ÿ बà¥à¤°à¤¾à¤‰à¤à¤° मà¥à¤¹à¤£à¥‚न सेट करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ à¤à¤‚टर दाबा</translation>
<translation id="2515629240566999685">आपलà¥à¤¯à¤¾ कà¥à¤·à¥‡à¤¤à¥à¤°à¤¾à¤¤à¥€à¤² सिगà¥à¤¨à¤² तपासणे</translation>
+<translation id="2515761554693942801">तà¥à¤®à¥à¤¹à¥€ <ph name="PROVIDER_ORIGIN" /> वापरणाऱà¥à¤¯à¤¾ वेबसाइटवर टच आयडी वापरून पडताळणी करणà¥à¤¯à¤¾à¤šà¥‡ निवडले आहे. या पà¥à¤°à¤µà¤ à¤¾à¤¦à¤¾à¤°à¤¾à¤¨à¥‡ तà¥à¤®à¤šà¥à¤¯à¤¾ पेमेंट पदà¥à¤§à¤¤à¥€à¤µà¤¿à¤·à¤¯à¥€ माहिती सà¥à¤Ÿà¥‹à¤…र केलेली असू शकते, जी तà¥à¤®à¥à¤¹à¥€ <ph name="LINK_TEXT" /> करू शकता.</translation>
<translation id="2521385132275182522">सà¥à¤Ÿà¥‡à¤ªà¤² बॉटम राइट</translation>
<translation id="2521736961081452453">फॉरà¥à¤® तयार करा</translation>
<translation id="2523886232349826891">फकà¥à¤¤ या डिवà¥à¤¹à¤¾à¤‡à¤¸à¤µà¤° सेवà¥à¤¹ केले जाईल</translation>
<translation id="2524461107774643265">अधिक माहिती जोडा</translation>
<translation id="2529899080962247600">या भागात <ph name="MAX_ITEMS_LIMIT" /> पेकà¥à¤·à¤¾ जासà¥à¤¤ नोंदी असू शकत नाहीत. उरलेलà¥à¤¯à¤¾ सरà¥à¤µ नोंदी दà¥à¤°à¥à¤²à¤•à¥à¤·à¤¿à¤¤ केलà¥à¤¯à¤¾ जातील.</translation>
+<translation id="253493526287553278">पà¥à¤°à¥‹à¤®à¥‹ कोडचे तपशील पहा</translation>
<translation id="2535585790302968248">खाजगीरीतà¥à¤¯à¤¾ बà¥à¤°à¤¾à¤‰à¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ नवीन गà¥à¤ªà¥à¤¤ टॅब उघडा</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{आणि आणखी à¤à¤•}other{आणि आणखी #}}</translation>
<translation id="2536110899380797252">पतà¥à¤¤à¤¾ जोडा</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">फोटोगà¥à¤°à¤¾à¤«à¥€à¤• आणि डिजिटल आरà¥à¤Ÿ</translation>
<translation id="2601150049980261779">रोमानà¥à¤¸ असलेले चितà¥à¤°à¤ªà¤Ÿ</translation>
<translation id="2604589665489080024">पॉप संगीत</translation>
-<translation id="2609632851001447353">तफावत</translation>
<translation id="2610561535971892504">कॉपी करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ कà¥à¤²à¤¿à¤• करा</translation>
<translation id="2617988307566202237">Chrome पà¥à¤¢à¥€à¤² माहिती <ph name="BEGIN_EMPHASIS" />सेवà¥à¤¹ करणार नाही<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">वाढदिवस आणि बारसे</translation>
<translation id="2677748264148917807">सोडा</translation>
+<translation id="2679714844901977852">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ आणि आणखी जलद चेकआउट करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ तà¥à¤®à¤šà¥à¤¯à¤¾ <ph name="USER_EMAIL" /> Google खाते मधà¥à¤¯à¥‡ तà¥à¤®à¤šà¥‡ कारà¥à¤¡ व बिलिंग माहिती सेवà¥à¤¹ करा</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">सरà¥à¤µà¥‹à¤¤à¥à¤¤à¤® फिट</translation>
<translation id="2688969097326701645">होय, सà¥à¤°à¥‚ ठेवा</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">इंटरनेट सेवा पà¥à¤°à¤µà¤ à¤¾à¤¦à¤¾à¤° (ISPs)</translation>
<translation id="2856444702002559011">हलà¥à¤²à¥‡à¤–ोर कदाचित तà¥à¤®à¤šà¥€ माहिती (उदाहरणारà¥à¤¥ पासवरà¥à¤¡, संदेश किंवा कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> मधून चोरणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करत असतील. <ph name="BEGIN_LEARN_MORE_LINK" />आणखी जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ही साइट अनाहूत किंवा दिशाभूल करणाऱà¥à¤¯à¤¾ जाहिराती दाखवते.</translation>
-<translation id="286512204874376891">संभावà¥à¤¯ घोटाळà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न तà¥à¤®à¤šà¥‡ संरकà¥à¤·à¤£ करणà¥à¤¯à¤¾à¤¤ मदत करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, वà¥à¤¹à¤°à¥à¤šà¥à¤¯à¥à¤…ल कारà¥à¤¡ तà¥à¤®à¤šà¥à¤¯à¤¾ मूळ कारà¥à¤¡à¤šà¥€ ओळख लपवते.<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">मैतà¥à¤°à¥€à¤ªà¥‚रà¥à¤£</translation>
<translation id="28761159517501904">चितà¥à¤°à¤ªà¤Ÿ</translation>
<translation id="2876489322757410363">बाहà¥à¤¯ ॲपà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨à¤®à¤¾à¤°à¥à¤«à¤¤ पेमेंट करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ गà¥à¤ªà¥à¤¤ मोडमधून बाहेर पडत आहे. पà¥à¤¢à¥‡ सà¥à¤°à¥‚ ठेवायचे आहे का?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">डिसà¥à¤•</translation>
<translation id="3162559335345991374">तà¥à¤®à¥à¤¹à¥€ वापरत असलेलà¥à¤¯à¤¾ वाय-फाय चà¥à¤¯à¤¾ लॉग इन पेजला तà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ भेट देणà¥à¤¯à¤¾à¤šà¥€ आवशà¥à¤¯à¤•à¤¤à¤¾ असू शकते.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">बेट</translation>
<translation id="3176929007561373547">पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¥à¤¹à¤° कारà¥à¤¯ करत आहे हे निशà¥à¤šà¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ तà¥à¤®à¤šà¥à¤¯à¤¾ पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सेटिंगà¥à¤œ तपासा
किंवा तà¥à¤®à¤šà¥à¤¯à¤¾ नेटवरà¥à¤• ॲडमिनिसà¥à¤Ÿà¥à¤°à¥‡à¤Ÿà¤°à¤¶à¥€ संपरà¥à¤• साधा. तà¥à¤®à¥à¤¹à¥€ पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¥à¤¹à¤° वापरत
आहात यावर तà¥à¤®à¤šà¤¾ विशà¥à¤µà¤¾à¤¸ नसलà¥à¤¯à¤¾à¤¸:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">घडà¥à¤¯à¤¾à¤³ à¤à¤°à¤°</translation>
<translation id="3369459162151165748">वाहनाचे भाग आणि ॲकà¥à¤¸à¥‡à¤¸à¤°à¥€</translation>
<translation id="3371064404604898522">Chrome ला डीफॉलà¥à¤Ÿ बà¥à¤°à¤¾à¤‰à¤à¤° मà¥à¤¹à¤£à¥‚न सेट करा</translation>
-<translation id="3371076217486966826"><ph name="URL" /> ला हे करायचे आहे:
- • तà¥à¤®à¤šà¥à¤¯à¤¾ सभोवतालचा 3D नकाशा तयार करणे आणि कॅमेरा सà¥à¤¥à¤¿à¤¤à¥€à¤šà¤¾ मागोवा घेणे
- • तà¥à¤®à¤šà¤¾ कॅमेरा वापरणे</translation>
<translation id="337363190475750230">तरतूद रदà¥à¤¦ केली</translation>
<translation id="3375754925484257129">Chrome सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¤¾ तपासणी रन करा</translation>
<translation id="3377144306166885718">सरà¥à¤µà¥à¤¹à¤°à¤¨à¥‡ TLS ची ऑबà¥à¤¸à¥‹à¤²à¥€à¤Ÿ आवृतà¥à¤¤à¥€ वापरली.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">वितरण पतà¥à¤¤à¤¾</translation>
<translation id="3402261774528610252">कनेकà¥à¤¶à¤¨à¤¨à¥‡ ही साइट लोड करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ कालबाहà¥à¤¯ à¤à¤¾à¤²à¥‡à¤²à¥‡ TLS 1.0 किंवा TLS 1.1 वापरले आहेत. ते भविषà¥à¤¯à¤¾à¤¤ बंद केले जातील. बंद केलà¥à¤¯à¤¾à¤¨à¤‚तर, वापरकरà¥à¤¤à¥à¤¯à¤¾à¤‚ना ही साइट लोड करणà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न पà¥à¤°à¤¤à¤¿à¤¬à¤‚धित केले जाईल. सरà¥à¤µà¥à¤¹à¤° TLS 1.2 किंवा तà¥à¤¯à¤¾à¤ªà¥à¤¢à¥€à¤² आवृतà¥à¤¤à¥€à¤µà¤° सà¥à¤°à¥‚ करणे आवशà¥à¤¯à¤• आहे.</translation>
<translation id="3405664148539009465">फॉंट कसà¥à¤Ÿà¤®à¤¾à¤‡à¤ करा</translation>
+<translation id="3407789382767355356">तृतीय पकà¥à¤· साइन इन</translation>
<translation id="3409896703495473338">सà¥à¤°à¤•à¥à¤·à¤¾ सेटिंगà¥à¤œ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा</translation>
<translation id="3414952576877147120">आकार:</translation>
<translation id="3417660076059365994">तà¥à¤®à¥à¤¹à¥€ अपलोड किंवा अटॅच केलेलà¥à¤¯à¤¾ फाइल विशà¥à¤²à¥‡à¤·à¤£à¤¾à¤¸à¤¾à¤ à¥€ Google Cloud किंवा तृतीय पकà¥à¤·à¤¾à¤‚कडे पाठवलà¥à¤¯à¤¾ जातात. उदाहरणारà¥à¤¥, तà¥à¤¯à¤¾ संवेदनशील डेटा किंवा मालवेअरसाठी सà¥à¤•à¥…न केलà¥à¤¯à¤¾ जाऊ शकतात.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">चितà¥à¤°à¤ªà¤Ÿà¤¾à¤šà¥à¤¯à¤¾ सूची आणि चितà¥à¤°à¤ªà¤Ÿà¤—ृहाचे शोटाइम</translation>
<translation id="3479552764303398839">सधà¥à¤¯à¤¾ नाही</translation>
<translation id="3484560055331845446">तà¥à¤®à¥à¤¹à¥€ तà¥à¤®à¤šà¥à¤¯à¤¾ Google खातà¥à¤¯à¤¾à¤šà¤¾ ॲकà¥à¤¸à¥‡à¤¸ कदाचित गमवाल. Chrome आता तà¥à¤®à¤šà¤¾ पासवरà¥à¤¡ बदलणà¥à¤¯à¤¾à¤šà¥€ शिफारस करते. तà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ साइन इन करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ सांगितले जाऊ शकते.</translation>
+<translation id="3484861421501147767">रिमाइंडर: सेवà¥à¤¹ केलेला पà¥à¤°à¥‹à¤®à¥‹ कोड उपलबà¥à¤§ आहे</translation>
<translation id="3487845404393360112">टà¥à¤°à¥‡ ४</translation>
<translation id="3495081129428749620">पेज <ph name="PAGE_TITLE" /> मधà¥à¤¯à¥‡ शोधा</translation>
<translation id="350069200438440499">फाइल नाव:</translation>
@@ -1055,6 +1059,7 @@
<translation id="3810973564298564668">वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा</translation>
<translation id="3816482573645936981">मूलà¥à¤¯ (अधिगà¥à¤°à¤¹à¤¿à¤¤ केलेले)</translation>
<translation id="382518646247711829">तà¥à¤®à¥à¤¹à¥€ पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¥à¤¹à¤° वापरत असलà¥à¤¯à¤¾à¤¸...</translation>
+<translation id="3826050100957962900">तृतीय पकà¥à¤· साइन इन</translation>
<translation id="3827112369919217609">अâ€à¥…बà¥à¤¸à¥‹à¤²à¥à¤¯à¥‚ट</translation>
<translation id="3827666161959873541">कौटà¥à¤‚बिक चितà¥à¤°à¤ªà¤Ÿ</translation>
<translation id="3828924085048779000">रिकà¥à¤¤ सांकेतिक पासफà¥à¤°à¥‡à¤à¤¾à¤šà¥€ परवानगी नाही.</translation>
@@ -1067,9 +1072,9 @@
<translation id="3858027520442213535">तारीख आणि वेळ अपडेट करा</translation>
<translation id="3858860766373142691">नाव</translation>
<translation id="3872834068356954457">विजà¥à¤žà¤¾à¤¨</translation>
+<translation id="3875783148670536197">मला कसे ते दाखवा</translation>
<translation id="3881478300875776315">कमी रेषा दाखवा</translation>
<translation id="3884278016824448484">संघरà¥à¤· करणारा डिवà¥à¤¹à¤¾à¤‡à¤¸ अभिजà¥à¤žà¤¾à¤ªà¤•</translation>
-<translation id="3885155851504623709">परगणा</translation>
<translation id="388632593194507180">परीकà¥à¤·à¤£ आढळले</translation>
<translation id="3886948180919384617">सà¥à¤Ÿà¥…कर ३</translation>
<translation id="3890664840433101773">ईमेल जोडा</translation>
@@ -1099,9 +1104,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> बà¥à¤²à¥‰à¤• केले आहे</translation>
<translation id="3978338123949022456">शोध मोड, कà¥à¤µà¥‡à¤°à¥€ टाइप करा आणि <ph name="KEYWORD_SUFFIX" /> सह शोधणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ à¤à¤‚टर दाबा</translation>
<translation id="398470910934384994">पकà¥à¤·à¥€</translation>
+<translation id="3985750352229496475">पतà¥à¤¤à¥‡ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा…</translation>
<translation id="3986705137476756801">आतापà¥à¤°à¤¤à¥‡ लाइवà¥à¤¹ कॅपà¥à¤¶à¤¨ बंद करा</translation>
<translation id="3987940399970879459">1 MB पेकà¥à¤·à¤¾ कमी</translation>
<translation id="3990250421422698716">जॉग ऑफसेट</translation>
+<translation id="3992684624889376114">या पेजबदà¥à¤¦à¤²</translation>
<translation id="3996311196211510766">साइटने <ph name="ORIGIN" /> विनंती केली आहे की, मूळ धोरण
तà¥à¤¯à¤¾à¤šà¥à¤¯à¤¾ सरà¥à¤µ विनंतà¥à¤¯à¤¾à¤‚वर लागू होईल पण हे धोरण सधà¥à¤¯à¤¾ लागू केले जाऊ शकत नाही.</translation>
<translation id="4006465311664329701">Google Pay वापरून पेमेंट पदà¥à¤§à¤¤à¥€, ऑफर आणि पतà¥à¤¤à¥‡</translation>
@@ -1140,7 +1147,7 @@
<translation id="4114146879518089587">साइटवर पà¥à¤¢à¥‡ सà¥à¤°à¥‚ ठेवा</translation>
<translation id="4116663294526079822">या साइटवर नेहमी अनà¥à¤®à¤¤à¥€ दà¥à¤¯à¤¾</translation>
<translation id="4116701314593212016">JIS B7</translation>
-<translation id="4117700440116928470">धोरण ककà¥à¤·à¤¾ समरà¥à¤¥à¤¿à¤¤ नाही.</translation>
+<translation id="4117700440116928470">धोरण ककà¥à¤·à¤¾ याला सपोरà¥à¤Ÿ नाही.</translation>
<translation id="4121428309786185360">रोजी कालबाहà¥à¤¯ होत आहे</translation>
<translation id="4123572138124678573">टà¥à¤°à¤¿à¤ªà¤² पंच बॉटम</translation>
<translation id="4127575959421463246">ChromeOS फà¥à¤²à¥…ग शोधत आहात का? भेट दà¥à¤¯à¤¾</translation>
@@ -1226,6 +1233,7 @@
<translation id="4305666528087210886">तà¥à¤®à¤šà¥€ फाइल अâ€à¥…कà¥à¤¸à¥‡à¤¸ करता आली नाही</translation>
<translation id="4306529830550717874">पतà¥à¤¤à¤¾ सेवà¥à¤¹ करायचा आहे का?</translation>
<translation id="4306812610847412719">कà¥à¤²à¤¿à¤ªà¤¬à¥‹à¤°à¥à¤¡</translation>
+<translation id="4310070645992025887">तà¥à¤®à¤šà¥‡ पà¥à¤°à¤µà¤¾à¤¸ शोधा</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">बà¥à¤²à¥‰à¤• करा (डीफॉलà¥à¤Ÿ)</translation>
<translation id="4314815835985389558">सिंक वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा</translation>
@@ -1276,6 +1284,7 @@
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">पà¥à¤°à¥‰à¤•à¥à¤¸à¥€à¤šà¤¾ वापर अकà¥à¤·à¤® करणà¥â€à¤¯à¤¾à¤¤ आला आहे पण à¤à¤• सà¥à¤¸à¥à¤ªà¤·à¥â€à¤Ÿ पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ कॉनà¥â€à¤«à¤¿à¤—रेशन निरà¥à¤¦à¤¿à¤·à¥â€à¤Ÿ करणà¥â€à¤¯à¤¾à¤¤ आले आहे.</translation>
<translation id="4441832193888514600">दà¥à¤°à¥à¤²à¤•à¥à¤· केले आहे, कारण हे धोरण फकà¥à¤¤ कà¥à¤²à¤¾à¤‰à¤¡ वापरकरà¥à¤¤à¤¾ धोरण मà¥à¤¹à¤£à¥‚न सेट केले जाऊ शकते.</translation>
+<translation id="4442470707340296952">Chrome टॅब</translation>
<translation id="4450893287417543264">पà¥à¤¨à¥à¤¹à¤¾ दाखवू नका</translation>
<translation id="4451135742916150903">HID डिवà¥à¤¹à¤¾à¤‡à¤¸à¤¶à¥€ कनेकà¥à¤Ÿ करणà¥à¤¯à¤¾à¤šà¥€ विनंती करू शकते</translation>
<translation id="4452328064229197696">तà¥à¤®à¥à¤¹à¥€ नà¥à¤•à¤¤à¤¾à¤š वापरलेला पासवरà¥à¤¡ डेटा भंगामधà¥à¤¯à¥‡ आढळला होता. तà¥à¤®à¤šà¥€ खाती सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ , Google पासवरà¥à¤¡ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤• तà¥à¤®à¤šà¥‡ सेवà¥à¤¹ केलेले पासवरà¥à¤¡ तपासणà¥à¤¯à¤¾à¤šà¥€ शिफारस करतो.</translation>
@@ -1414,6 +1423,7 @@
<translation id="483241715238664915">चेतावणà¥à¤¯à¤¾ सà¥à¤°à¥‚ करा</translation>
<translation id="4834250788637067901">Google Pay वापरून पेमेंट पदà¥à¤§à¤¤à¥€, ऑफर आणि पतà¥à¤¤à¥‡</translation>
<translation id="4838327282952368871">सà¥à¤µà¤ªà¥à¤¨à¤µà¤¤</translation>
+<translation id="4839087176073128681">पà¥à¤¢à¥€à¤² वेळी आणखी जलद पैसे दà¥à¤¯à¤¾ आणि Google चà¥à¤¯à¤¾ उदà¥à¤¯à¥‹à¤—ातील आघाडीचà¥à¤¯à¤¾ सà¥à¤°à¤•à¥à¤·à¥‡à¤¸à¤¹ तà¥à¤®à¤šà¥‡ कारà¥à¤¡ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ करा.</translation>
<translation id="4840250757394056958">तà¥à¤®à¤šà¤¾ Chrome इतिहास पहा</translation>
<translation id="484462545196658690">ऑटो</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> आणि आणखी बऱà¥à¤¯à¤¾à¤š गोषà¥à¤Ÿà¥€à¤‚वर सवलत मिळवा</translation>
@@ -1476,6 +1486,7 @@
<translation id="5011561501798487822">डिटेकà¥à¤Ÿ केलेली भाषा</translation>
<translation id="5015510746216210676">मशीन नाव:</translation>
<translation id="5017554619425969104">तà¥à¤®à¥à¤¹à¥€ कॉपी केलेला मजकूर</translation>
+<translation id="5017828934289857214">मला नंतर आठवण करून दà¥à¤¯à¤¾</translation>
<translation id="5018422839182700155">हे पृषà¥â€à¤  उघडू शकत नाही</translation>
<translation id="5019198164206649151">समरà¥à¤¥à¤¨ संचयन खराब सà¥à¤¥à¤¿à¤¤à¥€à¤¤</translation>
<translation id="5020776957610079374">जागतिक संगीत</translation>
@@ -1495,6 +1506,7 @@
<translation id="5051305769747448211">लाइवà¥à¤¹ विनोदी कारà¥à¤¯à¤•à¥à¤°à¤®</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Nearby सह शेअरिंग वापरून ही फाइल पाठवणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, तà¥à¤®à¤šà¥à¤¯à¤¾ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤µà¤°à¥€à¤² (<ph name="DISK_SPACE_SIZE" />) जागा मोकळी करा}other{Nearby सह शेअरिंग वापरून या फाइल पाठवणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, तà¥à¤®à¤šà¥à¤¯à¤¾ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤µà¤°à¥€à¤² (<ph name="DISK_SPACE_SIZE" />) जागा मोकळी करा}}</translation>
<translation id="5056549851600133418">तà¥à¤®à¤šà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ लेख</translation>
+<translation id="5060483733937416656">तà¥à¤®à¥à¤¹à¥€ <ph name="PROVIDER_ORIGIN" /> वापरणाऱà¥à¤¯à¤¾ वेबसाइटवर Windows Hello वापरून पडताळणी करणà¥à¤¯à¤¾à¤šà¥‡ निवडले आहे. या पà¥à¤°à¤µà¤ à¤¾à¤¦à¤¾à¤°à¤¾à¤¨à¥‡ तà¥à¤®à¤šà¥à¤¯à¤¾ पेमेंट पदà¥à¤§à¤¤à¥€à¤µà¤¿à¤·à¤¯à¥€ माहिती सà¥à¤Ÿà¥‹à¤…र केलेली असू शकते, जी तà¥à¤®à¥à¤¹à¥€ <ph name="LINK_TEXT" /> करू शकता.</translation>
<translation id="5061227663725596739">तà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ <ph name="LOOKALIKE_DOMAIN" />असे मà¥à¤¹à¤£à¤¾à¤¯à¤šà¥‡ होते का?</translation>
<translation id="5066056036849835175">पà¥à¤°à¤¿à¤‚टिंग इतिहास</translation>
<translation id="5068234115460527047">हेज फंड</translation>
@@ -1508,10 +1520,8 @@
<translation id="5087286274860437796">यावेळी सरà¥à¤µà¥à¤¹à¤°à¤šà¥‡ सरà¥à¤Ÿà¤¿à¤«à¤¿à¤•à¥‡à¤Ÿ वैध नाही.</translation>
<translation id="5087580092889165836">कारà¥à¤¡ जोडा</translation>
<translation id="5088142053160410913">ऑपरेटरला मेसेज पाठवणे</translation>
-<translation id="5089810972385038852">राजà¥à¤¯</translation>
<translation id="5093232627742069661">Z-फोलà¥à¤¡</translation>
<translation id="5094747076828555589">हा सरà¥à¤µà¥à¤¹à¤° हे <ph name="DOMAIN" /> असलà¥à¤¯à¤¾à¤šà¥‡ सिदà¥à¤§ करू शकला नाही; तà¥à¤¯à¤¾à¤šà¥‡ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¤¾ सरà¥à¤Ÿà¤¿à¤«à¤¿à¤•à¥‡à¤Ÿ Chromium दà¥à¤µà¤¾à¤°à¥‡ विशà¥à¤µà¤¸à¤¨à¥€à¤¯ नाही. हे कदाचित à¤à¤•à¤¾ चà¥à¤•à¥€à¤šà¥à¤¯à¤¾ कॉंफिगरेशनमà¥à¤³à¥‡ किंवा हलà¥à¤²à¥‡à¤–ोराने तà¥à¤®à¤šà¥‡ कनेकà¥à¤¶à¤¨ इंटरसेपà¥à¤Ÿ केलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ à¤à¤¾à¤²à¥‡ असू शकते.</translation>
-<translation id="5095208057601539847">पà¥à¤°à¤¾à¤‚त</translation>
<translation id="5097099694988056070">CPU/RAM वापर यासारखी डिवà¥à¤¹à¤¾à¤‡à¤¸ आकडेवारी</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">साइट सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नाही</translation>
@@ -1549,6 +1559,7 @@
<translation id="5171045022955879922">URL शोधा किंवा टाइप करा</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">मशीन</translation>
+<translation id="5177076414499237632">या पेजचà¥à¤¯à¤¾ सà¥à¤°à¥‹à¤¤ आणि विषयाबदà¥à¤¦à¤² जाणून घà¥à¤¯à¤¾</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> मधà¥à¤¯à¥‡ नाही? ही à¤à¤°à¤° नोंदवा</translation>
<translation id="518639307526414276">पाळीव पà¥à¤°à¤¾à¤£à¥à¤¯à¤¾à¤‚चे खादà¥à¤¯ आणि पाळीव पà¥à¤°à¤¾à¤£à¥à¤¯à¤¾à¤‚चà¥à¤¯à¤¾ निगेशी संबंधित पà¥à¤°à¤µà¤ à¤¾</translation>
<translation id="5190835502935405962">बà¥à¤•à¤®à¤¾à¤°à¥à¤• बार</translation>
@@ -1697,7 +1708,7 @@
<translation id="5593349413089863479">कनेकà¥à¤¶à¤¨ पूरà¥à¤£à¤ªà¤£à¥‡ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नाही</translation>
<translation id="5595485650161345191">पतà¥à¤¤à¤¾ संपादित करा</translation>
<translation id="5598944008576757369">पेमेंट पदà¥à¤§à¤¤ निवडा</translation>
-<translation id="560412284261940334">वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ समरà¥à¤¥à¤¿à¤¤ नाही</translation>
+<translation id="560412284261940334">वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨à¤¾à¤²à¤¾ सपोरà¥à¤Ÿ नाही</translation>
<translation id="5605670050355397069">Ledger</translation>
<translation id="5607240918979444548">Architecture-C</translation>
<translation id="5610142619324316209">कनेकà¥à¤¶à¤¨ तपासणे</translation>
@@ -1709,6 +1720,7 @@
<translation id="5624120631404540903">पासवरà¥à¤¡ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा</translation>
<translation id="5629630648637658800">धोरण सेटिंगà¥à¤œ लोड करणà¥à¤¯à¤¾à¤¤ अयशसà¥à¤µà¥€</translation>
<translation id="5631439013527180824">चà¥à¤•à¥€à¤šà¥‡ डिवà¥à¤¹à¤¾à¤‡à¤¸ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ टोकन</translation>
+<translation id="5632485077360054581">मला कसे ते दाखवा</translation>
<translation id="5633066919399395251">सधà¥à¤¯à¤¾ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> वर असलेले हलà¥à¤²à¥‡à¤–ोर कदाचित तà¥à¤®à¤šà¥à¤¯à¤¾ काà¤à¤ªà¥à¤¯à¥à¤Ÿà¤°à¤®à¤§à¥€à¤² तà¥à¤®à¤šà¥€ माहिती चोरू किंवा हटवू शकणारे धोकादायक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® (उदाहरणारà¥à¤¥, फोटो, पासवरà¥à¤¡, मेसेज आणि कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡à¥‡) इंसà¥à¤Ÿà¥‰à¤² करणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करू शकतील. <ph name="BEGIN_LEARN_MORE_LINK" />आणखी जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">फसवणारा आशय बà¥à¤²à¥‰à¤• केला.</translation>
<translation id="5633259641094592098">कलà¥à¤Ÿ आणि इंडी चितà¥à¤°à¤ªà¤Ÿ</translation>
@@ -1826,6 +1838,7 @@
<translation id="5989320800837274978">निशà¥à¤šà¤¿à¤¤ पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ सरà¥à¤µà¥à¤¹à¤° किंवा .pac सà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ URL देखील निरà¥à¤¦à¤¿à¤·à¥â€à¤Ÿ केलेली नाही.</translation>
<translation id="5992691462791905444">इंजिनीयरिंग Z-फोलà¥à¤¡</translation>
<translation id="5995727681868049093">तà¥à¤®à¤šà¥à¤¯à¤¾ Google खाते मधà¥à¤¯à¥‡ तà¥à¤®à¤šà¥€ माहिती, गोपनीयता आणि सà¥à¤°à¤•à¥à¤·à¤¾ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा</translation>
+<translation id="5997247540087773573">तà¥à¤®à¥à¤¹à¥€ नà¥à¤•à¤¤à¤¾à¤š वापरलेला पासवरà¥à¤¡ डेटा भंगामधà¥à¤¯à¥‡ आढळला होता. तà¥à¤®à¤šà¥€ खाती सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, Google Password Manager तो आता बदलणà¥à¤¯à¤¾à¤šà¥€ शिफारस करतो आणि तà¥à¤®à¤šà¥‡ सेवà¥à¤¹ केलेले पासवरà¥à¤¡ तपासतो.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' साठी <ph name="RESULT_COUNT" /> परिणाम</translation>
<translation id="6006484371116297560">कà¥à¤²à¤¾à¤¸à¤¿à¤•</translation>
<translation id="6008122969617370890">à¤à¤¨ ते १ कà¥à¤°à¤®</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">पà¥à¤¢à¥€à¤² वेळी जलद पेमेंट देणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, तà¥à¤®à¤šà¥à¤¯à¤¾ Google खातà¥à¤¯à¤¾à¤µà¤° तà¥à¤®à¤šà¥‡ कारà¥à¤¡ आणि बिलिंग पतà¥à¤¤à¤¾ सेवà¥à¤¹ करा.</translation>
<translation id="6279183038361895380">तà¥à¤®à¤šà¤¾ करà¥à¤¸à¤° दरà¥à¤¶à¤µà¤¿à¤£à¥â€à¤¯à¤¾à¤¸à¤¾à¤ à¥€ |<ph name="ACCELERATOR" />| दाबा</translation>
<translation id="6280223929691119688">या पतà¥à¤¤à¥à¤¯à¤¾à¤µà¤° देऊ शकत नाही. वेगळा पतà¥à¤¤à¤¾ निवडा.</translation>
-<translation id="6282194474023008486">पोसà¥à¤Ÿà¤² कोड</translation>
<translation id="6285507000506177184">Chrome मधील डाउनलोड वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा बटण, तà¥à¤®à¥à¤¹à¥€ Chrome मधà¥à¤¯à¥‡ डाउनलोड केलेलà¥à¤¯à¤¾ फाइल वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, à¤à¤‚टर दाबा</translation>
<translation id="6289939620939689042">पेजचा रंग</translation>
<translation id="6290238015253830360">तà¥à¤®à¥à¤¹à¥€ सà¥à¤šà¤µà¤¿à¤²à¥‡à¤²à¥‡ लेख येथे दिसतील</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> पेकà¥à¤·à¤¾ कमी जागा मोकळी करते. काही साइट तà¥à¤®à¤šà¥à¤¯à¤¾ पà¥à¤¢à¥€à¤² भेटीचà¥à¤¯à¤¾ वेळी आणखी धीमà¥à¤¯à¤¾ गतीने लोड होऊ शकतात.</translation>
<translation id="6337534724793800597">धोरणे नावानà¥à¤¸à¤¾à¤° फिलà¥à¤Ÿà¤° करा</translation>
<translation id="6340739886198108203">गोपनीय आशय दिसत असेल तेवà¥à¤¹à¤¾, अâ€à¥…डमिनिसà¥à¤Ÿà¥à¤°à¥‡à¤Ÿà¤° धोरण सà¥à¤•à¥à¤°à¥€à¤¨à¤¶à¥‰à¤Ÿ घेणà¥à¤¯à¤¾à¤šà¥€ किंवा रेकॉरà¥à¤¡ करणà¥à¤¯à¤¾à¤šà¥€ शिफारस करत नाही:</translation>
+<translation id="6348220984832452017">अâ€à¥…कà¥à¤Ÿà¤¿à¤µà¥à¤¹ वà¥à¤¹à¥‡à¤°à¤¿à¤à¤¶à¤¨</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> इंसà¥à¤Ÿà¥‰à¤² करा</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{काहीही नाही}=1{(<ph name="DOMAIN_LIST" /> साठी) à¤à¤• पासवरà¥à¤¡ सिंक केला}=2{(<ph name="DOMAIN_LIST" /> साठी) दोन पासवरà¥à¤¡ सिंक केले}other{(<ph name="DOMAIN_LIST" /> साठी) # पासवरà¥à¤¡ सिंक केले}}</translation>
<translation id="6355392890578844978">हा बà¥à¤°à¤¾à¤‰à¤à¤° कंपनी किंवा इतर संसà¥à¤¥à¥‡à¤¦à¥à¤µà¤¾à¤°à¥‡ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ केला जात नाही. या डिवà¥à¤¹à¤¾à¤‡à¤¸à¤µà¤°à¥€à¤² अâ€à¥…कà¥à¤Ÿà¤¿à¤µà¥à¤¹à¤¿à¤Ÿà¥€ Chromium चà¥à¤¯à¤¾ बाहेर वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ केली जाऊ शकते. <ph name="BEGIN_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">Google पासवरà¥à¤¡ बदला</translation>
<translation id="6433490469411711332">संपरà¥à¤• माहिती संपादित करा</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> नी कनेकà¥à¤Ÿ करणà¥à¤¯à¤¾à¤¸ नकार दिला.</translation>
-<translation id="6438025220577812695">मी सà¥à¤µà¤¤à¤ƒ तो बदलेन</translation>
<translation id="6440503408713884761">दà¥à¤°à¥à¤²à¤•à¥à¤· केले</translation>
<translation id="6443406338865242315">तà¥à¤®à¥à¤¹à¥€ कोणती à¤à¤•à¥à¤¸à¥à¤Ÿà¥‡à¤‚शन आणि पà¥à¤²à¤—-इन इंसà¥à¤Ÿà¥‰à¤² केली आहेत</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">अनà¥à¤µà¤‚शशासà¥à¤¤à¥à¤°</translation>
<translation id="6831043979455480757">भाषांतर करा</translation>
<translation id="6833752742582340615">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ आणि आणखी जलद चेकआउट करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ तà¥à¤®à¤šà¥à¤¯à¤¾ Google खाते मधà¥à¤¯à¥‡ तà¥à¤®à¤šà¥‡ कारà¥à¤¡ व बिलिंग माहिती सेवà¥à¤¹ करा</translation>
-<translation id="6839929833149231406">कà¥à¤·à¥‡à¤¤à¥à¤°</translation>
<translation id="6846340164947227603">वà¥à¤¹à¤°à¥à¤šà¥à¤¯à¥à¤…ल कारà¥à¤¡ नंबर वापरा...</translation>
<translation id="6852204201400771460">ॲप रीलोड करायचे?</translation>
+<translation id="6857776781123259569">पासवरà¥à¤¡ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा…</translation>
<translation id="686485648936420384">गà¥à¤°à¤¾à¤¹à¤• संसाधने</translation>
<translation id="6865412394715372076">या कारà¥à¤¡à¤šà¥€ पडताळणी आता करू शकत नाही</translation>
<translation id="6869334554832814367">वैयकà¥à¤¤à¤¿à¤• करà¥à¤œ</translation>
@@ -2119,7 +2131,7 @@
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6888584790432772780">हे पेज वाचणे सोपे करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ Chrome ने ते सà¥à¤²à¤­ केले आहे. Chrome ने मूळ पेज असà¥à¤°à¤•à¥à¤·à¤¿à¤¤ कनेकà¥à¤¶à¤¨à¤µà¤°à¥‚न पà¥à¤¨à¥à¤¹à¤¾ मिळवले आहे.</translation>
<translation id="6890443033788248019">सà¥à¤¥à¤¾à¤¨à¤¾à¤²à¤¾ अनà¥à¤®à¤¤à¥€ दà¥à¤¯à¤¾à¤¯à¤šà¥€ का?</translation>
-<translation id="6891596781022320156">धोरण सà¥à¤¤à¤° समरà¥à¤¥à¤¿à¤¤ नाही.</translation>
+<translation id="6891596781022320156">धोरण पातळी याला सपोरà¥à¤Ÿ नाही.</translation>
<translation id="6895143722905299846">वà¥à¤¹à¤°à¥à¤šà¥à¤¯à¥à¤…ल नंबर:</translation>
<translation id="6895330447102777224">आपलà¥à¤¯à¤¾ कारà¥à¤¡à¤šà¥€ पà¥à¤·à¥à¤Ÿà¥€ केली</translation>
<translation id="6897140037006041989">वापरकरà¥à¤¤à¤¾ à¤à¤œà¤‚ट</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">डिवà¥à¤¹à¤¾à¤‡à¤¸</translation>
<translation id="696703987787944103">परà¥à¤¸à¥‡à¤ªà¥à¤šà¥à¤…ल</translation>
<translation id="6968269510885595029">तà¥à¤®à¤šà¥€ सà¥à¤°à¤•à¥à¤·à¤¾ की वापरा</translation>
-<translation id="6970216967273061347">जिलà¥à¤¹à¤¾</translation>
<translation id="6971439137020188025">Slides मधà¥à¤¯à¥‡ नवीन Google पà¥à¤°à¥‡à¤à¥‡à¤‚टेशन à¤à¤Ÿà¤ªà¤Ÿ तयार करा</translation>
<translation id="6972629891077993081">HID डिवà¥à¤¹à¤¾à¤‡à¤¸</translation>
<translation id="6973656660372572881">निशà¥à¤šà¤¿à¤¤ पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¥à¤¹à¤° आणि .pac सà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ URL निरà¥à¤¦à¤¿à¤·à¥â€à¤Ÿ करणà¥â€à¤¯à¤¾à¤¤ आले आहेत.</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">हे वैशिषà¥à¤Ÿà¥à¤¯ तà¥à¤®à¤šà¥à¤¯à¤¾ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤µà¤° उपलबà¥à¤§ नाही</translation>
<translation id="7083258188081898530">टà¥à¤°à¥‡ ९</translation>
<translation id="7086090958708083563">वापरकरà¥à¤¤à¥à¤¯à¤¾à¤¨à¥‡ अपलोडची विनंती केली आहे</translation>
-<translation id="7087282848513945231">परगणा</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome सेटिंगà¥à¤œà¤®à¤§à¥à¤¯à¥‡ परवानगà¥à¤¯à¤¾ आणि सरà¥à¤µ साइटवर सà¥à¤Ÿà¥‹à¤…र केलेला डेटा वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ टॅब व तà¥à¤¯à¤¾à¤¨à¤‚तर à¤à¤‚टर दाबा</translation>
<translation id="7096937462164235847">या वेबसाइटचà¥à¤¯à¤¾ ओळखीची पडताळणी केलेली नाही.</translation>
<translation id="7101893872976785596">भयपट</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />फॉरà¥à¤®à¤®à¤§à¥à¤¯à¥‡ à¤à¤‚टर केलेली माहिती<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">या पतà¥à¤¤à¥à¤¯à¤¾à¤µà¤° पाठवू शकत नाही. वेगळा पतà¥à¤¤à¤¾ निवडा.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">तà¥à¤®à¤šà¥à¤¯à¤¾ ॲडमिनने हा डेटा कॉपी करणà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न पà¥à¤°à¤¤à¤¿à¤¬à¤‚धित केले आहे.</translation>
<translation id="7135130955892390533">सà¥à¤¥à¤¿à¤¤à¥€ दाखवा</translation>
<translation id="7138472120740807366">वितरण पदà¥à¤§à¤¤</translation>
-<translation id="7139724024395191329">अमिरात</translation>
<translation id="7139892792842608322">पà¥à¤°à¤¾à¤¥à¤®à¤¿à¤• टà¥à¤°à¥‡</translation>
<translation id="714064300541049402">साइड २ इमेज X शिफà¥à¤Ÿ</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">या साइट वरील आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ कदाचित तà¥à¤®à¤šà¥à¤¯à¤¾ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग अनà¥à¤­à¤µà¤¾à¤¸ हानी पोहोचविणारे पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® (उदाहरणारà¥à¤¥, तà¥à¤®à¤šà¥‡ होम बदलून किंवा तà¥à¤®à¥à¤¹à¥€ भेट देता तà¥à¤¯à¤¾ साइटवर अतिरिकà¥à¤¤ जाहिराती दरà¥à¤¶à¤µà¥‚न) इंसà¥à¤Ÿà¥‰à¤² करून तà¥à¤®à¤šà¥€ फसवणूक करणà¥â€à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करू शकतात.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{डेटाशी संबंधित कृती गोपनीय मà¥à¤¹à¤£à¥‚न फà¥à¤²à¥…ग केलà¥à¤¯à¤¾ (लॉगिन केलà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न à¤à¤• कृती). <ph name="BEGIN_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LINK" />}other{डेटाशी संबंधित कृती गोपनीय मà¥à¤¹à¤£à¥‚न फà¥à¤²à¥…ग केलà¥à¤¯à¤¾ (लॉगिन केलà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न # कृती). <ph name="BEGIN_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">मेलबॉकà¥à¤¸ ६</translation>
+<translation id="7675325315208090829">पेमेंट पदà¥à¤§à¤¤à¥€ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा…</translation>
<translation id="7676643023259824263">कà¥à¤²à¤¿à¤ªà¤¬à¥‹à¤°à¥à¤¡ मजकूर, <ph name="TEXT" /> शोधा</translation>
<translation id="7679367271685653708">Chrome सेटिंगà¥à¤œà¤®à¤§à¥à¤¯à¥‡ तà¥à¤®à¤šà¤¾ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग इतिहास पहा आणि वà¥â€à¤¯à¤µà¤¸à¥â€à¤¥à¤¾à¤ªà¤¿à¤¤ करा</translation>
<translation id="7679947978757153706">बेसबॉल</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">सà¥à¤•à¤°à¥à¤Ÿ</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">तोच कà¥à¤°à¤® ठेवून फेस अप</translation>
-<translation id="777702478322588152">परफेकà¥à¤šà¥à¤…र</translation>
<translation id="7791011319128895129">रिलीठन केलेले</translation>
<translation id="7791196057686275387">बेल</translation>
<translation id="7791543448312431591">जोडा</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">वà¥â€à¤¯à¤¾à¤µà¤¸à¤¾à¤¯à¤¿à¤• आणि पà¥à¤°à¥Œà¤¢ शिकà¥à¤·à¤£</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" योगà¥à¤¯ रीतीने कॉंफिगर केलेले नाही. "<ph name="SOFTWARE_NAME" />" अनइंसà¥à¤Ÿà¥‰à¤² केलà¥à¤¯à¤¾à¤¨à¥‡ सहसा समसà¥à¤¯à¤¾ सà¥à¤Ÿà¤¤à¥‡. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">खादà¥à¤¯à¤ªà¤¦à¤¾à¤°à¥à¤¥à¤¾à¤šà¥‡ उतà¥à¤ªà¤¾à¤¦à¤¨</translation>
-<translation id="8066955247577885446">सॉरी, काहीतरी चूक à¤à¤¾à¤²à¥€.</translation>
<translation id="8067872629359326442">तà¥à¤®à¥à¤¹à¥€ आताच à¤à¤•à¤¾ फसवà¥à¤¯à¤¾ साइटवर तà¥à¤®à¤šà¤¾ पासवरà¥à¤¡ à¤à¤‚टर केला आहे. Chromium मदत करू शकते. तà¥à¤®à¤šà¤¾ पासवरà¥à¤¡ बदलणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ आणि तà¥à¤®à¤šà¥à¤¯à¤¾ खातà¥à¤¯à¤¾à¤²à¤¾ धोका असलà¥à¤¯à¤¾à¤šà¥‡ Google ला सूचित करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, खाते संरकà¥à¤·à¤¿à¤¤ करा वर कà¥à¤²à¤¿à¤• करा.</translation>
<translation id="8070439594494267500">अâ€à¥…पचा आयकन</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">नवीन Google शीट à¤à¤Ÿà¤ªà¤Ÿ तयार करा</translation>
<translation id="8075898834294118863">साइट सेटिंगà¥à¤œ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करा</translation>
+<translation id="8076492880354921740">टॅब</translation>
<translation id="8078141288243656252">फिरवलà¥à¤¯à¤¾à¤µà¤° भाषà¥à¤¯ करू शकत नाही</translation>
<translation id="8079031581361219619">साइट रीलोड करायची?</translation>
<translation id="8081087320434522107">सडॅन</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">सेटिंगà¥à¤œ</translation>
+<translation id="8428634594422941299">समजले</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome सेटिंगà¥à¤œà¤®à¤§à¥à¤¯à¥‡ तà¥à¤®à¤šà¥€ कà¥à¤•à¥€ पà¥à¤°à¤¾à¤§à¤¾à¤¨à¥à¤¯à¥‡ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ टॅब आणि तà¥à¤¯à¤¾à¤¨à¤‚तर à¤à¤‚टर दाबा</translation>
<translation id="8433057134996913067">हे तà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ बहà¥à¤¤à¤¾à¤‚श वेबसाइट वरून साइन आउट करेल.</translation>
<translation id="8434840396568290395">पाळीव पà¥à¤°à¤¾à¤£à¥€</translation>
@@ -2792,6 +2803,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> हा तà¥à¤®à¤šà¤¾ <ph name="ORIGIN" />साठीचा कोड आहे</translation>
<translation id="874918643257405732">हा टॅब बà¥à¤•à¤®à¤¾à¤°à¥à¤• करा</translation>
<translation id="8751426954251315517">कृपया पà¥à¤¢à¥€à¤² वेळेस पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा</translation>
+<translation id="8757526089434340176">Google Pay ऑफर उपलबà¥à¤§</translation>
<translation id="8758885506338294482">सà¥à¤ªà¤°à¥à¤§à¤¾à¤¤à¥à¤®à¤• वà¥à¤¹à¤¿à¤¡à¤¿à¤“ गेमिंग</translation>
<translation id="8759274551635299824">या कारà¥à¤¡à¤šà¥€ मà¥à¤¦à¤¤ संपली आहे</translation>
<translation id="87601671197631245">ही साइट कालबाहà¥à¤¯ à¤à¤¾à¤²à¥‡à¤²à¥€ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¤¾ कॉंफिगरेशन वापरत आहे जà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ कदाचित तà¥à¤®à¤šà¥€ माहिती (उदाहरणारà¥à¤¥, पासवरà¥à¤¡, मेसेज किंवा कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡à¥‡) या साइटला पाठवलà¥à¤¯à¤¾à¤¨à¤‚तर ती उघड होऊ शकते.</translation>
@@ -2799,6 +2811,7 @@
<translation id="8763927697961133303">USB डिवà¥à¤¹à¤¾à¤‡à¤¸</translation>
<translation id="8763986294015493060">सधà¥à¤¯à¤¾ उघडà¥à¤¯à¤¾ असलेलà¥à¤¯à¤¾ सरà¥à¤µ गà¥à¤ªà¥à¤¤ विंडो बंद करा</translation>
<translation id="8766943070169463815">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ पेमेंट कà¥à¤°à¥‡à¤¡à¥‡à¤‚शियल ची ऑथेंटिकेशन शीट उघडी आहे</translation>
+<translation id="8767765348545497220">मदतीसंबंधित बबल बंद करा</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">मोटरसायकल</translation>
<translation id="8790007591277257123">&amp;पà¥à¤¨à¥à¤¹à¤¾ करा हटवा</translation>
@@ -2811,6 +2824,7 @@
<translation id="8806285662264631610">पà¥à¤°à¤¸à¤¾à¤§à¤¨à¥‡</translation>
<translation id="8807160976559152894">पà¥à¤°à¤¤à¥à¤¯à¥‡à¤• पेजनंतर टà¥à¤°à¤¿à¤® करा</translation>
<translation id="8808828119384186784">Chrome सेटिंगà¥à¤œ</translation>
+<translation id="8813277370772331957">मला नंतर आठवण करून दà¥à¤¯à¤¾</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, तà¥à¤®à¤šà¥à¤¯à¤¾ Chrome सेटिंगà¥à¤œà¤®à¤§à¥‚न Chrome अपडेट करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ टॅब आणि तà¥à¤¯à¤¾à¤¨à¤‚तर à¤à¤‚टर दाबा</translation>
<translation id="8820817407110198400">बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="882338992931677877">मॅनà¥â€à¤¯à¥à¤…ल सà¥à¤²à¥‰à¤Ÿ</translation>
@@ -2990,6 +3004,7 @@
<translation id="988159990683914416">डेवà¥à¤¹à¤²à¤ªà¤° बिलà¥à¤¡</translation>
<translation id="989988560359834682">पतà¥à¤¤à¤¾ संपादित करा</translation>
<translation id="991413375315957741">मोशन किंवा पà¥à¤°à¤•à¤¾à¤¶ सेनà¥à¤¸à¤°</translation>
+<translation id="992110854164447044">संभावà¥à¤¯ घोटाळà¥à¤¯à¤¾à¤ªà¤¾à¤¸à¥‚न तà¥à¤®à¤šà¥‡ संरकà¥à¤·à¤£ करणà¥à¤¯à¤¾à¤¤ मदत करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€, वà¥à¤¹à¤°à¥à¤šà¥à¤¯à¥à¤…ल कारà¥à¤¡ तà¥à¤®à¤šà¥‡ मूळ कारà¥à¤¡ लपवते. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">गà¥à¤²à¤¾à¤¬à¥€</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" तà¥à¤®à¤šà¥à¤¯à¤¾ काà¤à¤ªà¥à¤¯à¥à¤Ÿà¤°à¤µà¤° किंवा नेटवरà¥à¤•à¤µà¤° योगà¥à¤¯ रीतीने इंसà¥à¤Ÿà¥‰à¤² केले नवà¥à¤¹à¤¤à¥‡:
diff --git a/chromium/components/strings/components_strings_ms.xtb b/chromium/components/strings/components_strings_ms.xtb
index 94b7d285bd6..575e1d3ad93 100644
--- a/chromium/components/strings/components_strings_ms.xtb
+++ b/chromium/components/strings/components_strings_ms.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Geran, biasiswa &amp; bantuan kewangan</translation>
<translation id="1048785276086539861">Apabila anda mengedit anotasi, dokumen ini akan kembali kepada paparan satu halaman</translation>
<translation id="1050038467049342496">Tutup apl lain</translation>
+<translation id="1053959602163383901">Anda memilih untuk mengesahkan dengan peranti pengesah pada laman web yang menggunakan <ph name="PROVIDER_ORIGIN" />. Pembekal ini mungkin telah menyimpan maklumat tentang kaedah pembayaran anda, yang boleh anda <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Buat Asal Tambahkan</translation>
<translation id="1056663316309890343">Perisian foto</translation>
<translation id="1056898198331236512">Amaran</translation>
@@ -119,6 +120,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="1270502636509132238">Kaedah Pengambilan</translation>
<translation id="1281476433249504884">Petak 1</translation>
<translation id="1285320974508926690">Jangan sekali-kali menterjemahkan tapak ini</translation>
+<translation id="1288548991597756084">Simpan kad dengan selamat</translation>
<translation id="1292571435393770077">Dulang 16</translation>
<translation id="1292701964462482250">"Perisian pada komputer anda menghalang Chrome daripada menyambung ke web dengan selamat" (komputer Windows sahaja)</translation>
<translation id="1294154142200295408">Variasi baris perintah</translation>
@@ -223,6 +225,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
&lt;p&gt;Untuk membetulkan ralat itu, klik &lt;strong&gt;Sambung&lt;/strong&gt; pada halaman yang cuba dibuka.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Reka bentuk landskap</translation>
<translation id="1513706915089223971">Senarai masukan sejarah</translation>
+<translation id="1516097932025103760">Kad akan disulitkan, disimpan dengan selamat dan CVC tidak sama sekali disimpan.</translation>
<translation id="1517433312004943670">Nombor telefon diperlukan</translation>
<translation id="1519264250979466059">Tarikh Bina</translation>
<translation id="1521159554480556801">Seni gentian &amp; tekstil</translation>
@@ -288,6 +291,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="1662550410081243962">Simpan dan lengkapkan kaedah pembayaran</translation>
<translation id="1663943134801823270">Kad dan alamat adalah daripada Chrome. Anda boleh mengurus kad dan alamat ini dalam <ph name="BEGIN_LINK" />Tetapan<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Halaman dalam <ph name="SOURCE_LANGUAGE" /> akan diterjemah kepada <ph name="TARGET_LANGUAGE" /> bermula dari sekarang.</translation>
+<translation id="1673886523110456987">Semak <ph name="CARD_DETAIL" /> untuk menggunakan tawaran</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> kepada <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Sisi pendek dahulu</translation>
<translation id="168693727862418163">Nilai dasar ini gagal disahkan dengan skemanya dan akan diabaikan.</translation>
@@ -306,6 +310,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="1717218214683051432">Penderia gerakan</translation>
<translation id="1717494416764505390">Peti mel 3</translation>
<translation id="1718029547804390981">Dokumen terlalu besar untuk dianotasikan</translation>
+<translation id="1720941539803966190">Tutup tutorial</translation>
<translation id="1721424275792716183">* Medan perlu diisi</translation>
<translation id="1727613060316725209">Sijil sah</translation>
<translation id="1727741090716970331">Tambahkan Nombor Kad yang Sah</translation>
@@ -422,8 +427,8 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="205212645995975601">BBQ &amp; menggril</translation>
<translation id="2053111141626950936">Halaman dalam <ph name="LANGUAGE" /> tidak akan diterjemah.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Apabila kawalan ini dihidupkan dan status adalah aktif, Chrome akan menentukan kumpulan besar orang, atau "kohort" yang paling serupa dengan aktiviti penyemakan imbas anda baru-baru ini. Pengiklan boleh memilih iklan untuk kumpulan itu dan aktiviti penyemakan imbas anda dipastikan tertutup pada peranti anda. Kumpulan anda dikemas kini setiap hari.}=1{Apabila kawalan ini dihidupkan dan status adalah aktif, Chrome akan menentukan kumpulan besar orang, atau "kohort" yang paling serupa dengan aktiviti penyemakan imbas anda baru-baru ini. Pengiklan boleh memilih iklan untuk kumpulan itu dan aktiviti penyemakan imbas anda dipastikan tertutup pada peranti anda. Kumpulan anda dikemas kini setiap hari.}other{Apabila kawalan ini dihidupkan dan status adalah aktif, Chrome akan menentukan kumpulan besar orang, atau "kohort" yang paling serupa dengan aktiviti penyemakan imbas anda baru-baru ini. Pengiklan boleh memilih iklan untuk kumpulan itu dan aktiviti penyemakan imbas anda dipastikan tertutup pada peranti anda. Kumpulan anda dikemas kini setiap {NUM_DAYS} hari.}}</translation>
-<translation id="2053553514270667976">Poskod</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 cadangan}other{# cadangan}}</translation>
+<translation id="2066915425250589881">permintaan untuk dipadamkan</translation>
<translation id="2068528718802935086">Bayi &amp; kanak-kanak kecil</translation>
<translation id="2071156619270205202">Kad ini tidak layak untuk nombor kad maya.</translation>
<translation id="2071692954027939183">Pemberitahuan disekat secara automatik kerana biasanya anda tidak membenarkan pemberitahuan</translation>
@@ -435,7 +440,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="2088086323192747268">Butang urus penyegerakan, tekan Enter untuk mengurus maklumat yang anda segerakkan dalam tetapan Chrome</translation>
<translation id="2091887806945687916">Bunyi</translation>
<translation id="2094505752054353250">Domain tidak padan</translation>
-<translation id="2096368010154057602">Jabatan</translation>
<translation id="2099652385553570808">Tiga kokot kiri</translation>
<translation id="2101225219012730419">Versi:</translation>
<translation id="2102134110707549001">Cadangkan Kata Laluan Yang Kukuh…</translation>
@@ -472,7 +476,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="2185836064961771414">Bola sepak Amerika</translation>
<translation id="2187317261103489799">Kesan (lalai)</translation>
<translation id="2188375229972301266">Berbilang tebukan bawah</translation>
-<translation id="2188852899391513400">Kata laluan yang baru sahaja anda gunakan telah ditemukan dalam pelanggaran data. Untuk melindungi akaun anda, Pengurus Kata Laluan Google mengesyorkan agar anda menukarnya sekarang, kemudian menyemak kata laluan yang disimpan.</translation>
<translation id="219906046732893612">Penambahbaikan kediaman</translation>
<translation id="2202020181578195191">Masukkan tahun tamat tempoh yang sah</translation>
<translation id="22081806969704220">Dulang 3</translation>
@@ -483,6 +486,8 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="2215727959747642672">Pengeditan fail</translation>
<translation id="2215963164070968490">Anjing</translation>
<translation id="2218879909401188352">Penyerang yang berada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> boleh memasang apl berbahaya yang boleh merosakkan peranti anda, menambahkan caj yang tersembunyi pada bil mudah alih anda atau mencuri maklumat peribadi anda. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Mulakan semula tutorial</translation>
+<translation id="2219735899272417925">Tetapan semula peranti diperlukan</translation>
<translation id="2224337661447660594">Tiada Internet</translation>
<translation id="2230458221926704099">Betulkan sambungan anda menggunakan <ph name="BEGIN_LINK" />apl diagnostik<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Hantar sekarang</translation>
@@ -580,11 +585,13 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="2512101340618156538">Tidak dibenarkan (lalai)</translation>
<translation id="2512413427717747692">Tetapkan Chrome sebagai butang penyemak imbas lalai, tekan Enter untuk menetapkan Chrome sebagai penyemak imbas lalai sistem dalam tetapan iOS</translation>
<translation id="2515629240566999685">Semak isyarat di kawasan anda</translation>
+<translation id="2515761554693942801">Anda memilih untuk mengesahkan dengan Touch ID pada laman web yang menggunakan <ph name="PROVIDER_ORIGIN" />. Pembekal ini mungkin telah menyimpan maklumat tentang kaedah pembayaran anda, yang boleh anda <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Kokot bawah sebelah kanan</translation>
<translation id="2521736961081452453">Buat borang</translation>
<translation id="2523886232349826891">Disimpan pada peranti ini sahaja</translation>
<translation id="2524461107774643265">Tambahkan Maklumat Lanjut</translation>
<translation id="2529899080962247600">Medan ini tidak boleh mengandungi lebih daripada <ph name="MAX_ITEMS_LIMIT" /> entri. Semua entri berikutnya akan diabaikan.</translation>
+<translation id="253493526287553278">Lihat butiran kod promosi</translation>
<translation id="2535585790302968248">Buka tab Inkognito baharu untuk menyemak imbas secara peribadi</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{dan 1 lagi}other{dan # lagi}}</translation>
<translation id="2536110899380797252">Tambahkan Alamat</translation>
@@ -620,7 +627,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="259821504105826686">Fotografi &amp; seni digital</translation>
<translation id="2601150049980261779">Filem romantik</translation>
<translation id="2604589665489080024">Muzik pop</translation>
-<translation id="2609632851001447353">Variasi</translation>
<translation id="2610561535971892504">Klik untuk menyalin</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />tidak akan menyimpan<ph name="END_EMPHASIS" /> maklumat berikut:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Hari lahir &amp; hari nama</translation>
<translation id="2677748264148917807">Tinggalkan</translation>
+<translation id="2679714844901977852">Simpan kad dan maklumat pengebilan anda pada Google Account anda, <ph name="USER_EMAIL" />, untuk semak keluar yang selamat dan lebih pantas</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Paling sesuai</translation>
<translation id="2688969097326701645">Ya, teruskan</translation>
@@ -701,7 +708,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="2854764410992194509">Penyedia perkhidmatan Internet (ISP)</translation>
<translation id="2856444702002559011">Penyerang mungkin akan cuba mencuri maklumat anda daripada <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (contohnya, kata laluan, mesej atau kad kredit). <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Tapak ini menyiarkan iklan yang mengganggu atau mengelirukan.</translation>
-<translation id="286512204874376891">Kad maya menggantikan kad sebenar anda untuk melindungi anda daripada kemungkinan penipuan. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Mesra</translation>
<translation id="28761159517501904">Filem</translation>
<translation id="2876489322757410363">Meninggalkan mod Inkognito untuk membuat bayaran melalui aplikasi luar. Teruskan?</translation>
@@ -802,7 +808,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="3158539265159265653">Cakera</translation>
<translation id="3162559335345991374">Wi-Fi yang anda gunakan mungkin memerlukan anda untuk melawat halaman log masuknya.</translation>
<translation id="3169472444629675720">Temui</translation>
-<translation id="3174168572213147020">Pulau</translation>
<translation id="3176929007561373547">Semak tetapan proksi anda atau hubungi pentadbir rangkaian anda untuk
memastikan pelayan proksi berfungsi. Jika anda tidak percaya anda perlu
menggunakan pelayan proksi:
@@ -881,9 +886,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="3369192424181595722">Ralat Jam</translation>
<translation id="3369459162151165748">Alat ganti &amp; aksesori kenderaan</translation>
<translation id="3371064404604898522">Tetapkan Chrome sebagai penyemak imbas lalai</translation>
-<translation id="3371076217486966826"><ph name="URL" /> mahu:
- • Membuat peta 3D bagi persekitaran anda dan menjejak kedudukan kamera
- • Gunakan kamera anda</translation>
<translation id="337363190475750230">Nyahperuntukkan</translation>
<translation id="3375754925484257129">Jalankan semakan keselamatan Chrome</translation>
<translation id="3377144306166885718">Pelayan menggunakan versi TLS yang sudah lapuk.</translation>
@@ -900,6 +902,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="3399952811970034796">Alamat Penghantaran</translation>
<translation id="3402261774528610252">Sambungan yang digunakan untuk memuatkan tapak ini menggunakan TLS 1.0 atau TLS 1.1, yang telah ditamatkan dan akan dilumpuhkan pada masa hadapan. Apabila dilumpuhkan, pengguna akan dihalang daripada memuatkan tapak ini. Pelayan harus mendayakan TLS 1.2 atau yang lebih baharu.</translation>
<translation id="3405664148539009465">Sesuaikan fon</translation>
+<translation id="3407789382767355356">log masuk pihak ketiga</translation>
<translation id="3409896703495473338">Urus tetapan keselamatan</translation>
<translation id="3414952576877147120">Saiz:</translation>
<translation id="3417660076059365994">Fail yang anda muat naik atau lampirkan dihantar kepada Google Cloud atau pihak ketiga untuk dianalisis. Contohnya, fail itu mungkin diimbas untuk mencari data sensitif atau perisian hasad.</translation>
@@ -932,6 +935,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="3477679029130949506">Senarai filem &amp; waktu tayangan teater</translation>
<translation id="3479552764303398839">Bukan sekarang</translation>
<translation id="3484560055331845446">Anda mungkin akan kehilangan akses kepada Akaun Google anda. Chrome mengesyorkan supaya anda menukar kata laluan sekarang. Anda akan diminta untuk log masuk.</translation>
+<translation id="3484861421501147767">Peringatan: Kod promosi yang disimpan tersedia</translation>
<translation id="3487845404393360112">Dulang 4</translation>
<translation id="3495081129428749620">Cari dalam halaman
<ph name="PAGE_TITLE" /></translation>
@@ -1057,6 +1061,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="3810973564298564668">Urus</translation>
<translation id="3816482573645936981">Nilai (digantikan)</translation>
<translation id="382518646247711829">Jika anda menggunakan pelayan proksi...</translation>
+<translation id="3826050100957962900">Log masuk pihak ketiga</translation>
<translation id="3827112369919217609">Mutlak</translation>
<translation id="3827666161959873541">Filem keluarga</translation>
<translation id="3828924085048779000">Kosongkan frasa laluan adalah tidak dibenarkan.</translation>
@@ -1069,9 +1074,9 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="3858027520442213535">Kemas kini tarikh dan masa</translation>
<translation id="3858860766373142691">Nama</translation>
<translation id="3872834068356954457">Sains</translation>
+<translation id="3875783148670536197">Tunjukkan Caranya</translation>
<translation id="3881478300875776315">Tunjukkan kurang baris</translation>
<translation id="3884278016824448484">Pengecam peranti bercanggah</translation>
-<translation id="3885155851504623709">Mukim</translation>
<translation id="388632593194507180">Pemantauan Dikesan</translation>
<translation id="3886948180919384617">Petak 3</translation>
<translation id="3890664840433101773">Tambah e-mel</translation>
@@ -1101,9 +1106,11 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="3973234410852337861"><ph name="HOST_NAME" /> disekat</translation>
<translation id="3978338123949022456">Mod carian, taip pertanyaan dan tekan Enter untuk mencari <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Burung</translation>
+<translation id="3985750352229496475">Urus Alamat...</translation>
<translation id="3986705137476756801">Matikan Sari Kata Langsung buat masa ini</translation>
<translation id="3987940399970879459">Kurang daripada 1 MB</translation>
<translation id="3990250421422698716">Ofset jog</translation>
+<translation id="3992684624889376114">Perihal halaman ini</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> tapak telah meminta dasar asal
dikenakan pada semua permintaan yang diterima, tetapi dasar ini tidak dapat dikenakan pada masa ini.</translation>
<translation id="4006465311664329701">Kaedah pembayaran, Tawaran dan Alamat yang Menggunakan Google Pay</translation>
@@ -1228,6 +1235,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="4305666528087210886">Fail anda tidak dapat diakses</translation>
<translation id="4306529830550717874">Simpan alamat?</translation>
<translation id="4306812610847412719">papan keratan</translation>
+<translation id="4310070645992025887">Cari Perjalanan anda</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Sekat (lalai)</translation>
<translation id="4314815835985389558">Urus penyegerakan</translation>
@@ -1278,6 +1286,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="4435702339979719576">Poskad)</translation>
<translation id="443673843213245140">Penggunaan proksi dilumpuhkan tetapi konfigurasi proksi yang jelas dinyatakan.</translation>
<translation id="4441832193888514600">Diabaikan kerana dasar itu hanya boleh ditetapkan sebagai dasar pengguna awan.</translation>
+<translation id="4442470707340296952">Tab Chrome</translation>
<translation id="4450893287417543264">Jangan tunjukkan lagi</translation>
<translation id="4451135742916150903">Boleh meminta untuk menyambung kepada peranti HID</translation>
<translation id="4452328064229197696">Kata laluan yang baru sahaja anda gunakan telah ditemukan dalam pelanggaran data. Untuk melindungi akaun anda, Pengurus Kata Laluan Google mengesyorkan agar anda menyemak kata laluan yang disimpan.</translation>
@@ -1416,6 +1425,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="483241715238664915">Hidupkan amaran</translation>
<translation id="4834250788637067901">Kaedah pembayaran, tawaran dan alamat yang menggunakan Google Pay</translation>
<translation id="4838327282952368871">Berangan</translation>
+<translation id="4839087176073128681">Bayar lebih cepat pada masa akan datang dan lindungi kad anda dengan keselamatan Google yang terulung dalam industri.</translation>
<translation id="4840250757394056958">Lihat sejarah Chrome anda</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">Dapatkan diskaun di <ph name="MERCHANT_NAME" /> dan pelbagai lagi</translation>
@@ -1478,6 +1488,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="5011561501798487822">Bahasa yang Dikesan</translation>
<translation id="5015510746216210676">Nama Mesin:</translation>
<translation id="5017554619425969104">Teks yang anda salin</translation>
+<translation id="5017828934289857214">Ingatkan Saya Kemudian</translation>
<translation id="5018422839182700155">Tidak dapat membuka halaman ini</translation>
<translation id="5019198164206649151">Simpanan penyandaran dalam keadaan buruk</translation>
<translation id="5020776957610079374">Muzik dunia</translation>
@@ -1497,6 +1508,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="5051305769747448211">Jenaka langsung</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Untuk menghantar fail ini menggunakan Kongsi Berdekatan, kosongkan ruang (<ph name="DISK_SPACE_SIZE" />) pada peranti anda}other{Untuk menghantar fail ini menggunakan Kongsi Berdekatan, kosongkan ruang (<ph name="DISK_SPACE_SIZE" />) pada peranti anda}}</translation>
<translation id="5056549851600133418">Artikel untuk anda</translation>
+<translation id="5060483733937416656">Anda memilih untuk mengesahkan dengan Windows Hello pada laman web yang menggunakan <ph name="PROVIDER_ORIGIN" />. Pembekal ini mungkin telah menyimpan maklumat tentang kaedah pembayaran anda, yang boleh anda <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Adakah anda maksudkan <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Sejarah pencetakan</translation>
<translation id="5068234115460527047">Dana lindung nilai</translation>
@@ -1510,10 +1522,8 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="5087286274860437796">Sijil pelayan tidak sah pada masa ini.</translation>
<translation id="5087580092889165836">Tambah kad</translation>
<translation id="5088142053160410913">Mesej kepada pengendali</translation>
-<translation id="5089810972385038852">Negeri</translation>
<translation id="5093232627742069661">Lipatan Z</translation>
<translation id="5094747076828555589">Pelayan ini tidak dapat membuktikan bahawa domainnya ialah <ph name="DOMAIN" />; sijil keselamatannya tidak dipercayai oleh Chromium. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintasi sambungan anda.</translation>
-<translation id="5095208057601539847">Wilayah</translation>
<translation id="5097099694988056070">Statistik peranti seperti penggunaan CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Laman tidak selamat</translation>
@@ -1551,6 +1561,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="5171045022955879922">Buat carian atau taipkan URL</translation>
<translation id="5171689220826475070">Fanfold-Eropah</translation>
<translation id="5172758083709347301">Mesin</translation>
+<translation id="5177076414499237632">Ketahui tentang sumber &amp; topik halaman ini</translation>
<translation id="5179510805599951267">Bukan dalam <ph name="ORIGINAL_LANGUAGE" />? Laporkan ralat ini</translation>
<translation id="518639307526414276">Makanan haiwan kesayangan &amp; bekalan penjagaan haiwan kesayangan</translation>
<translation id="5190835502935405962">Bar Penanda Halaman</translation>
@@ -1711,6 +1722,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="5624120631404540903">Urus kata laluan</translation>
<translation id="5629630648637658800">Gagal memuatkan tetapan dasar</translation>
<translation id="5631439013527180824">Token pengurusan peranti tidak sah</translation>
+<translation id="5632485077360054581">Tunjukkan caranya</translation>
<translation id="5633066919399395251">Penyerang yang sedang berada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin cuba memasang atur cara berbahaya pada komputer anda. Atur cara tersebut boleh mencuri atau memadamkan maklumat anda (contohnya, foto, kata laluan, mesej dan kad kredit). <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Kandungan mengelirukan disekat.</translation>
<translation id="5633259641094592098">Filem kultus &amp; indie</translation>
@@ -1828,6 +1840,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="5989320800837274978">Pelayan proksi tetap begitu juga URL skrip .pac, kedua-duanya tidak ditetapkan.</translation>
<translation id="5992691462791905444">Lipatan Z kejuruteraan</translation>
<translation id="5995727681868049093">Urus maklumat, privasi dan keselamatan anda dalam Google Account anda</translation>
+<translation id="5997247540087773573">Kata laluan yang baru anda gunakan ditemukan dalam pelanggaran data. Untuk melindungi akaun anda, Google Password Manager mengesyorkan agar anda menukar kata laluan sekarang, kemudian menyemak kata laluan anda yang disimpan.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> hasil carian untuk '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">Klasik</translation>
<translation id="6008122969617370890">Susunan N-ke-1</translation>
@@ -1923,7 +1936,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="627746635834430766">Untuk membayar dengan lebih cepat selepas ini, simpan kad dan alamat pengebilan anda ke Akaun Google.</translation>
<translation id="6279183038361895380">Tekan |<ph name="ACCELERATOR" />| untuk memaparkan kursor anda</translation>
<translation id="6280223929691119688">Tidak dapat menghantar ke alamat ini. Pilih alamat lain.</translation>
-<translation id="6282194474023008486">Poskod</translation>
<translation id="6285507000506177184">Butang urus muat turun dalam Chrome, tekan Enter untuk mengurus fail yang telah anda muat turun dalam Chrome</translation>
<translation id="6289939620939689042">Warna Halaman</translation>
<translation id="6290238015253830360">Artikel cadangan anda dipaparkan di sini</translation>
@@ -1945,6 +1957,7 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="6337133576188860026">Mengosongkan kurang daripada <ph name="SIZE" />. Sesetengah tapak mungkin dimuatkan dengan lebih perlahan pada lawatan anda yang seterusnya.</translation>
<translation id="6337534724793800597">Tapis dasar mengikut nama</translation>
<translation id="6340739886198108203">Dasar pentadbir tidak mengesyorkan mengambil tangkapan skrin atau rakaman apabila kandungan sulit kelihatan:</translation>
+<translation id="6348220984832452017">Variasi Aktif</translation>
<translation id="6349101878882523185">Pasang <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Tiada}=1{1 kata laluan (untuk <ph name="DOMAIN_LIST" />, disegerakkan)}=2{2 kata laluan (untuk <ph name="DOMAIN_LIST" />, disegerakkan)}other{# kata laluan (untuk <ph name="DOMAIN_LIST" />, disegerakkan)}}</translation>
<translation id="6355392890578844978">Penyemak imbas ini tidak diurus oleh syarikat atau organisasi lain. Aktiviti pada peranti ini mungkin diurus di luar Chromium. <ph name="BEGIN_LINK" />Ketahui lebih lanjut<ph name="END_LINK" /></translation>
@@ -1976,7 +1989,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="643051589346665201">Tukar kata laluan Google</translation>
<translation id="6433490469411711332">Edit maklumat hubungan</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> enggan menyambung.</translation>
-<translation id="6438025220577812695">Tukar sendiri</translation>
<translation id="6440503408713884761">Diabaikan</translation>
<translation id="6443406338865242315">Sambungan dan pemalam yang telah anda pasang</translation>
<translation id="6446163441502663861">Kahu (Sampul Surat)</translation>
@@ -2106,9 +2118,9 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="6828866289116430505">Genetik</translation>
<translation id="6831043979455480757">Terjemah</translation>
<translation id="6833752742582340615">Simpan kad dan maklumat pengebilan ke Google Account anda untuk semak keluar yang selamat dan lebih pantas</translation>
-<translation id="6839929833149231406">Kawasan</translation>
<translation id="6846340164947227603">Gunakan nombor kad maya...</translation>
<translation id="6852204201400771460">Muat semula apl?</translation>
+<translation id="6857776781123259569">Urus Kata Laluan...</translation>
<translation id="686485648936420384">Sumber pengguna</translation>
<translation id="6865412394715372076">Kad ini tidak dapat disahkan sekarang</translation>
<translation id="6869334554832814367">Pinjaman peribadi</translation>
@@ -2157,7 +2169,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="6965978654500191972">Peranti</translation>
<translation id="696703987787944103">Perseptual</translation>
<translation id="6968269510885595029">Gunakan Kunci Keselamatan anda</translation>
-<translation id="6970216967273061347">Daerah</translation>
<translation id="6971439137020188025">Buat pembentangan Google baharu dalam Slides dengan pantas</translation>
<translation id="6972629891077993081">Peranti HID</translation>
<translation id="6973656660372572881">Pelayan proksi tetap dan juga URL skrip .pac tidak ditetapkan.</translation>
@@ -2196,7 +2207,6 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<translation id="7081308185095828845">Ciri ini tidak tersedia pada peranti anda</translation>
<translation id="7083258188081898530">Dulang 9</translation>
<translation id="7086090958708083563">Muat naik diminta oleh pengguna</translation>
-<translation id="7087282848513945231">Daerah</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, tekan Tab kemudian Enter untuk mengurus kebenaran dan data yang disimpan merentas berbilang laman dalam tetapan Chrome</translation>
<translation id="7096937462164235847">Identiti laman web ini tidak disahkan.</translation>
<translation id="7101893872976785596">Filem seram</translation>
@@ -2215,10 +2225,10 @@ Jika tidak, ini akan disekat oleh tetapan privasi anda. Kebenaran ini akan membo
<ph name="LIST_ITEM" />Maklumat yang dimasukkan dalam borang<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Tidak dapat menghantar ke alamat ini. Pilih alamat lain.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Pentadbir anda telah melarang data ini daripada disalin.</translation>
<translation id="7135130955892390533">Tunjukkan status</translation>
<translation id="7138472120740807366">Kaedah penghantaran</translation>
-<translation id="7139724024395191329">Amiriah</translation>
<translation id="7139892792842608322">Dulang Utama</translation>
<translation id="714064300541049402">Anjakan X sisi 2 imej</translation>
<translation id="7152423860607593928">Number-14 (Sampul Surat)</translation>
@@ -2435,6 +2445,7 @@ Butiran tambahan:
<translation id="7669271284792375604">Penyerang pada tapak ini mungkin cuba menipu dengan meminta anda memasang atur cara yang membahayakan pengalaman penyemakan imbas anda (contohnya, dengan menukar halaman utama anda atau menunjukkan iklan tambahan pada laman yang anda lawati).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Tindakan yang diambil dengan data yang dibenderakan sebagai sulit (1 tindakan sejak log masuk). <ph name="BEGIN_LINK" />Ketahui lebih lanjut<ph name="END_LINK" />}other{Tindakan yang diambil dengan data yang dibenderakan sebagai sulit (# tindakan sejak log masuk). <ph name="BEGIN_LINK" />Ketahui lebih lanjut<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Peti mel 6</translation>
+<translation id="7675325315208090829">Urus Kaedah Pembayaran...</translation>
<translation id="7676643023259824263">Cari teks papan keratan, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Lihat dan urus sejarah penyemakan imbas anda dalam tetapan Chrome</translation>
<translation id="7679947978757153706">Besbol</translation>
@@ -2477,7 +2488,6 @@ Butiran tambahan:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Panjang-Ditetapkan</translation>
<translation id="7773005668374414287">Susunan sama menghadap ke atas</translation>
-<translation id="777702478322588152">Wilayah</translation>
<translation id="7791011319128895129">Belum dilancarkan</translation>
<translation id="7791196057686275387">Bale</translation>
<translation id="7791543448312431591">Tambah</translation>
@@ -2568,12 +2578,12 @@ Butiran tambahan:
<translation id="8055534648776115597">Pendidikan vokasional &amp; lanjutan</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" tidak dikonfigurasi dengan betul. Tindakan menyahpasang "<ph name="SOFTWARE_NAME" />" biasanya dapat menyelesaikan masalah ini. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Pengeluaran makanan</translation>
-<translation id="8066955247577885446">Maaf, kesilapan telah berlaku.</translation>
<translation id="8067872629359326442">Anda baru sahaja memasukkan kata laluan anda pada tapak yang menipu. Chromium boleh membantu. Untuk menukar kata laluan anda dan memaklumi Google bahawa akaun anda mungkin berisiko, klik Lindungi Akaun.</translation>
<translation id="8070439594494267500">Ikon apl</translation>
<translation id="8074253406171541171">10x13 (Sampul Surat)</translation>
<translation id="8075736640322370409">Buat Google Sheets baharu dengan pantas</translation>
<translation id="8075898834294118863">Urus tetapan laman</translation>
+<translation id="8076492880354921740">Tab</translation>
<translation id="8078141288243656252">Tidak boleh melakukan anotasi apabila diputarkan</translation>
<translation id="8079031581361219619">Muatkan semula tapak?</translation>
<translation id="8081087320434522107">Kereta sedan</translation>
@@ -2696,6 +2706,7 @@ Butiran tambahan:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Tetapan</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, tekan Tab kemudian Enter untuk mengurus pilihan kuki anda dalam tetapan Chrome</translation>
<translation id="8433057134996913067">Pemadaman ini akan membuatkan anda dilog keluar daripada kebanyakan laman web.</translation>
<translation id="8434840396568290395">Haiwan kesayangan</translation>
@@ -2794,6 +2805,7 @@ Butiran tambahan:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> ialah kod anda untuk <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Tanda tab ini</translation>
<translation id="8751426954251315517">Sila cuba lagi nanti</translation>
+<translation id="8757526089434340176">Tawaran Google Pay tersedia</translation>
<translation id="8758885506338294482">Permainan video kompetitif</translation>
<translation id="8759274551635299824">Kad ini telah tamat tempoh</translation>
<translation id="87601671197631245">Tapak ini menggunakan konfigurasi keselamatan yang sudah lapuk, yang mungkin mendedahkan maklumat anda (contohnya, kata laluan, mesej atau nombor kad kredit) apabila dihantar kepada tapak ini.</translation>
@@ -2801,6 +2813,7 @@ Butiran tambahan:
<translation id="8763927697961133303">Peranti USB</translation>
<translation id="8763986294015493060">Tutup semua tetingkap Inkognito yang terbuka pada masa ini</translation>
<translation id="8766943070169463815">Helaian pengesahan bukti kelayakan pembayaran selamat dibuka</translation>
+<translation id="8767765348545497220">Tutup gelembung bantuan</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motosikal</translation>
<translation id="8790007591277257123">&amp;Buat semula pemadaman</translation>
@@ -2813,6 +2826,7 @@ Butiran tambahan:
<translation id="8806285662264631610">Produk mandian &amp; badan</translation>
<translation id="8807160976559152894">Pangkas selepas setiap halaman</translation>
<translation id="8808828119384186784">Tetapan Chrome</translation>
+<translation id="8813277370772331957">Ingatkan saya kemudian</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, tekan Tab kemudian Enter untuk mengemas kini Chrome daripada tetapan Chrome anda</translation>
<translation id="8820817407110198400">Penanda buku</translation>
<translation id="882338992931677877">Slot Manual</translation>
@@ -2992,6 +3006,7 @@ Butiran tambahan:
<translation id="988159990683914416">Binaan Pemaju</translation>
<translation id="989988560359834682">Edit Alamat</translation>
<translation id="991413375315957741">penderia gerakan atau cahaya</translation>
+<translation id="992110854164447044">Kad maya menyembunyikan kad sebenar anda untuk melindungi anda daripada kemungkinan penipuan. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Merah Jambu</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" tidak dipasang dengan betul pada komputer atau rangkaian anda:
diff --git a/chromium/components/strings/components_strings_my.xtb b/chromium/components/strings/components_strings_my.xtb
index bc2b62f83d7..74e112e826b 100644
--- a/chromium/components/strings/components_strings_my.xtb
+++ b/chromium/components/strings/components_strings_my.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">အလှူငွေአပညာသင်ထောက်ပံ့ကြေးနှင့် ငွေကြေးအကူအညီ</translation>
<translation id="1048785276086539861">မှá€á€ºá€á€»á€€á€ºá€™á€»á€¬á€¸á€á€Šá€ºá€¸á€–ြá€á€ºá€•á€«á€€ ဤမှá€á€ºá€á€™á€ºá€¸á€žá€Šá€º စာမျက်နှာá€á€…်á€á€¯á€á€Šá€ºá€¸á€žá€­á€¯á€· ပြန်ပြောင်းသွားမည်</translation>
<translation id="1050038467049342496">အá€á€¼á€¬á€¸ အက်ပ်များကို ပိá€á€ºá€•á€«</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> သုံးသည့် á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€á€½á€„် အထောက်အထားစိစစ်သည့်စက်ဖြင့် အá€á€Šá€ºá€•á€¼á€¯á€›á€”် သင်ရွေးထားသည်ዠသင့်ငွေပေးá€á€»á€±á€”ည်းလမ်း အá€á€»á€€á€ºá€¡á€œá€€á€ºá€á€½á€±á€€á€­á€¯ ဤပံ့ပိုးသူက သိမ်းထားနိုင်ပြီး áŽá€„်းá€á€­á€¯á€·á€€á€­á€¯ <ph name="LINK_TEXT" />နိုင်ပါသည်á‹</translation>
<translation id="1055184225775184556">&amp;ထည့်ပေးမှု á€á€…်ဆင့်နောက်ပြန်ရန်</translation>
<translation id="1056663316309890343">ဓာá€á€ºá€•á€¯á€¶á€†á€±á€¬á€·á€–်á€á€²</translation>
<translation id="1056898198331236512">သá€á€­á€•á€±á€¸á€á€»á€€á€º</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">လာယူနည်း</translation>
<translation id="1281476433249504884">စီထည့်သည့်ပုံး á</translation>
<translation id="1285320974508926690">ဒီဆိုက်ကို ဘယ်á€á€±á€¬á€·á€™á€¾ ဘာသာမပြန်ပါနှင့်</translation>
+<translation id="1288548991597756084">ကá€á€ºá€€á€­á€¯ လုံá€á€¼á€¯á€¶á€…ွာသိမ်းရန်</translation>
<translation id="1292571435393770077">ဗန်း áá†</translation>
<translation id="1292701964462482250">"သင်áကွန်ပျူá€á€¬á€›á€¾á€­ ဆော့ဖ်á€á€²á€œá€ºá€žá€Šá€º Chrome ကို á€á€˜á€ºá€žá€­á€¯á€· လုံá€á€¼á€¯á€¶á€…ွာá€á€»á€­á€á€ºá€†á€€á€ºá€™á€¾á€¯ မပြုလုပ်နိုင်ရန် á€á€¬á€¸á€†á€®á€¸á€‘ားပါသည်" (Windows ကွန်ပျူá€á€¬á€™á€»á€¬á€¸á€žá€¬)</translation>
<translation id="1294154142200295408">ကွန်မန်းလိုင်းစနစ် ကွဲပြားမှုများ</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;ထိုအမှားအယွင်းကို ဖြေရှင်းရန် သင်ဖွင့်ရန် ကြိုးစားနေသည့် စာမျက်နှာá€á€½á€„် &lt;strong&gt;á€á€»á€­á€á€ºá€†á€€á€ºá€›á€”်&lt;/strong&gt; ကို နှိပ်ပါá‹&lt;/p&gt;</translation>
<translation id="1507780850870535225">ရှုá€á€„်းပုံဖော်á€á€¼á€„်း ဒီဇိုင်း</translation>
<translation id="1513706915089223971">မှá€á€ºá€á€™á€ºá€¸ ထည့်သွင်းမှုများ စာရင်း</translation>
+<translation id="1516097932025103760">áŽá€„်းကို အသွင်á€á€¾á€€á€ºá လုံá€á€¼á€¯á€¶á€…ွာသိမ်းမည်ဖြစ်ပြီး CVC နံပါá€á€ºá€€á€­á€¯ ဘယ်á€á€±á€¬á€·á€™á€¾ မသိမ်းဆည်းပါá‹</translation>
<translation id="1517433312004943670">ဖုန်းနံပါá€á€ºá€œá€­á€¯á€¡á€•á€ºá€•á€«á€žá€Šá€º</translation>
<translation id="1519264250979466059">á€á€Šá€ºá€†á€±á€¬á€€á€ºá€žá€Šá€·á€º ရက်စွဲ</translation>
<translation id="1521159554480556801">á€á€»á€Šá€ºá€™á€»á€¾á€„်နှင့် အထည်အလိပ်အနုပညာ</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ငွေပေးá€á€»á€±á€”ည်းလမ်းများကို သိမ်းပြီး ဖြည့်ရန်</translation>
<translation id="1663943134801823270">ကဒ်နှင့် လိပ်စာများသည် Chrome မှ ဖြစ်သည်ዠáŽá€„်းá€á€­á€¯á€·á€€á€­á€¯ <ph name="BEGIN_LINK" />ဆက်á€á€„်များ<ph name="END_LINK" /> ထဲá€á€½á€„် စီမံá€á€”့်á€á€½á€²á€”ိုင်သည်á‹</translation>
<translation id="1671391448414634642"><ph name="SOURCE_LANGUAGE" /> ဘာသာဖြင့် စာမျက်နှာများကို ယá€á€¯á€™á€¾á€…á <ph name="TARGET_LANGUAGE" /> ဘာသာသို့ ပြန်ဆိုသွားပါမည်á‹</translation>
+<translation id="1673886523110456987">ကမ်းလှမ်းá€á€»á€€á€ºá€žá€¯á€¶á€¸á€›á€”် <ph name="CARD_DETAIL" /> ဖြင့် ငွေရှင်းပါ</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> မှ <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">á€á€­á€¯á€žá€Šá€·á€ºá€¡á€”ားနှင့် စá€á€„်ရန်</translation>
<translation id="168693727862418163">ဤမူá€á€«á€’သည် áŽá€„်းá စီမံá€á€»á€€á€ºá€”ှင့်á€á€­á€¯á€€á€ºá အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ အá€á€Šá€ºá€•á€¼á€¯áမရသောကြောင့် လျစ်လျူရှုသွားပါမည်á‹</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">လှုပ်ရှားမှု အာရုံá€á€¶á€…နစ်များ</translation>
<translation id="1717494416764505390">စာá€á€­á€¯á€€á€ºá€•á€¯á€¶á€¸ áƒ</translation>
<translation id="1718029547804390981">မှá€á€ºá€á€™á€ºá€¸á€–ိုင်သည် မှá€á€ºá€á€»á€€á€ºá€•á€±á€¸á€›á€”်အá€á€½á€€á€º အလွန်ကြီးနေပါသည်</translation>
+<translation id="1720941539803966190">ရှင်းလင်းပို့á€á€»á€á€»á€€á€ºá€€á€­á€¯ ပိá€á€ºá€›á€”်</translation>
<translation id="1721424275792716183">* ပြထားသော အကွက်ကို ဖြည့်ရန်လိုသည်</translation>
<translation id="1727613060316725209">အသိအမှá€á€ºá€•á€¼á€¯á€œá€€á€ºá€™á€¾á€á€º မှန်ကန်သည်</translation>
<translation id="1727741090716970331">မှန်ကန်သည့် ကá€á€ºá€”ံပါá€á€ºá€€á€­á€¯ ထည့်ပါ</translation>
@@ -423,8 +428,8 @@
<translation id="205212645995975601">BBQ နှင့် အကင်</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> ဘာသာဖြင့် စာမျက်နှာများကို ဘာသာပြန်မည် မဟုá€á€ºá€•á€«á‹</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ဤထိန်းá€á€»á€¯á€•á€ºá€™á€¾á€¯á€€á€­á€¯ ဖွင့်ထားပြီး သုံးနေသည့် အá€á€¼á€±á€¡á€”ေပြသောအá€á€« Chrome သည် သင်áမကြာသေးမီက ကြည့်ရှုá€á€¼á€„်းများနှင့် အနီးစပ်ဆုံးá€á€°á€žá€±á€¬ လူအဖွဲ့ကြီး (သို့) “á€á€…်á€á€»á€­á€”်á€á€Šá€ºá€¸á€¡á€á€°á€á€€á€½ လုပ်ဆောင်á€á€¼á€„်း†ကို သá€á€ºá€™á€¾á€á€ºá€žá€Šá€ºá‹ ကြော်ငြာရှင်များက အဖွဲ့အá€á€½á€€á€º ကြော်ငြာများကို ရွေးနိုင်ပြီး သင်áကြည့်ရှုá€á€¼á€„်းများကို သင့်စက်á€á€½á€„် သီးသန့်ထားရှိသည်ዠသင့်အဖွဲ့ကို နေ့စဉ် အပ်ဒိá€á€ºá€œá€¯á€•á€ºá€žá€Šá€ºá‹}=1{ဤထိန်းá€á€»á€¯á€•á€ºá€™á€¾á€¯á€€á€­á€¯ ဖွင့်ထားပြီး သုံးနေသည့် အá€á€¼á€±á€¡á€”ေပြသောအá€á€« Chrome သည် သင်áမကြာသေးမီက ကြည့်ရှုá€á€¼á€„်းများနှင့် အနီးစပ်ဆုံးá€á€°á€žá€±á€¬ လူအဖွဲ့ကြီး (သို့) “á€á€…်á€á€»á€­á€”်á€á€Šá€ºá€¸á€¡á€á€°á€á€€á€½ လုပ်ဆောင်á€á€¼á€„်း†ကို သá€á€ºá€™á€¾á€á€ºá€žá€Šá€ºá‹ ကြော်ငြာရှင်များက အဖွဲ့အá€á€½á€€á€º ကြော်ငြာများကို ရွေးနိုင်ပြီး သင်áကြည့်ရှုá€á€¼á€„်းများကို သင့်စက်á€á€½á€„် သီးသန့်ထားရှိသည်ዠသင့်အဖွဲ့ကို နေ့စဉ် အပ်ဒိá€á€ºá€œá€¯á€•á€ºá€žá€Šá€ºá‹}other{ဤထိန်းá€á€»á€¯á€•á€ºá€™á€¾á€¯á€€á€­á€¯ ဖွင့်ထားပြီး သုံးနေသည့် အá€á€¼á€±á€¡á€”ေပြသောအá€á€« Chrome သည် သင်áမကြာသေးမီက ကြည့်ရှုá€á€¼á€„်းများနှင့် အနီးစပ်ဆုံးá€á€°á€žá€±á€¬ လူအဖွဲ့ကြီး (သို့) “á€á€…်á€á€»á€­á€”်á€á€Šá€ºá€¸á€¡á€á€°á€á€€á€½ လုပ်ဆောင်á€á€¼á€„်း†ကို သá€á€ºá€™á€¾á€á€ºá€žá€Šá€ºá‹ ကြော်ငြာရှင်များက အဖွဲ့အá€á€½á€€á€º ကြော်ငြာများကို ရွေးနိုင်ပြီး သင်áကြည့်ရှုá€á€¼á€„်းများကို သင့်စက်á€á€½á€„် သီးသန့်ထားရှိသည်ዠသင့်အဖွဲ့ကို {NUM_DAYS} ရက်á€á€…်ကြိမ် အပ်ဒိá€á€ºá€œá€¯á€•á€ºá€žá€Šá€ºá‹}}</translation>
-<translation id="2053553514270667976">ဇစ်ကုဒ်</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{အကြံပြုá€á€»á€€á€º á á€á€¯}other{အကြံပြုá€á€»á€€á€º # á€á€¯}}</translation>
+<translation id="2066915425250589881">ဖျက်ရန် á€á€±á€¬á€„်းဆို</translation>
<translation id="2068528718802935086">မွေးကင်းစနှင့် လမ်းလျှောက်á€á€«á€…ကလေးများ</translation>
<translation id="2071156619270205202">ဤကá€á€ºá€žá€Šá€º ပကá€á€­á€¡á€žá€½á€„်ကá€á€ºá€”ံပါá€á€ºá€¡á€á€½á€€á€º သá€á€ºá€™á€¾á€á€ºá€á€»á€€á€ºá€™á€•á€¼á€Šá€·á€ºá€™á€®á€•á€«á‹</translation>
<translation id="2071692954027939183">အကြောင်းကြားá€á€»á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ သင်က á€á€½á€„့်ပြုလေ့မရှိသဖြင့် áŽá€„်းá€á€­á€¯á€·á€€á€­á€¯ အလိုအလျောက် ပိá€á€ºá€‘ားသည်</translation>
@@ -436,7 +441,6 @@
<translation id="2088086323192747268">စင့်á€á€ºá€œá€¯á€•á€ºá€á€¼á€„်းကို စီမံရန် á€á€œá€¯á€á€ºáŠ Chrome ဆက်á€á€„်များá€á€½á€„် သင်စင့်á€á€ºá€œá€¯á€•á€ºá€žá€±á€¬ အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ စီမံရန် Enter နှိပ်ပါ</translation>
<translation id="2091887806945687916">အသံ</translation>
<translation id="2094505752054353250">ဒိုမိန်း မá€á€­á€¯á€€á€ºá€†á€­á€¯á€„်မှု</translation>
-<translation id="2096368010154057602">ဌာန</translation>
<translation id="2099652385553570808">ဘယ်ဘက်á€á€½á€„် á€á€»á€¯á€•á€ºá€…က်ဖြင့် သုံးá€á€»á€€á€ºá€á€»á€¯á€•á€ºá€›á€”်</translation>
<translation id="2101225219012730419">ဗားရှင်း:</translation>
<translation id="2102134110707549001">á€á€­á€¯á€„်မာသည့် စကားá€á€¾á€€á€º အကြံပြုရန်…</translation>
@@ -473,7 +477,6 @@
<translation id="2185836064961771414">အမေရိကန် ဘောလုံး</translation>
<translation id="2187317261103489799">ရှာကြည့်ရန် (မူရင်း)</translation>
<translation id="2188375229972301266">အောက်á€á€¼á€±á€á€½á€„် အများအပြားဖောက်ရန်</translation>
-<translation id="2188852899391513400">သင်သုံးလိုက်သောစကားá€á€¾á€€á€ºá€€á€­á€¯ ဒေá€á€¬á€€á€»á€­á€¯á€¸á€•á€±á€«á€€á€ºá€™á€¾á€¯á€á€½á€„် á€á€½á€±á€·á€›á€¾á€­á€‘ားသည်ዠသင့်အကောင့်များကို လုံá€á€¼á€¯á€¶á€…ေရန် ‘Google စကားá€á€¾á€€á€ºá€™á€”်နေဂျာ’ က သင်သိမ်းထားသော စကားá€á€¾á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ ယá€á€¯á€•á€¼á€±á€¬á€„်းရန်နှင့် စစ်ဆေးရန် အကြံပြုပါသည်á‹</translation>
<translation id="219906046732893612">အိမ်မွမ်းမံရေး</translation>
<translation id="2202020181578195191">မှန်ကန်သည့် ကုန်ဆုံးမည့်á€á€¯á€”ှစ်ကို ထည့်ပါ</translation>
<translation id="22081806969704220">ဗန်း áƒ</translation>
@@ -484,6 +487,8 @@
<translation id="2215727959747642672">ဖိုင်á€á€Šá€ºá€¸á€–ြá€á€ºá€á€¼á€„်း</translation>
<translation id="2215963164070968490">á€á€½á€±á€¸á€™á€»á€¬á€¸</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ရှိ လက်ရှိá€á€­á€¯á€€á€ºá€á€­á€¯á€€á€ºá€žá€°á€™á€»á€¬á€¸á€žá€Šá€º သင့်စက်ပစ္စည်းကို ပျက်စီးစေနိုင်သည့် အန္á€á€›á€¬á€šá€ºá€›á€¾á€­á€žá€±á€¬á€¡á€€á€ºá€•á€ºá€™á€»á€¬á€¸ ထည့်သွင်းá€á€¼á€„်းአဖုန်းá€á€„ွေပိုမိုကုန်ကျစေá€á€¼á€„်း သို့မဟုá€á€º သင်á ကိုယ်ရေးကိုယ်á€á€¬ အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ á€á€­á€¯á€¸á€šá€°á€á€¼á€„်းá€á€­á€¯á€· ပြုလုပ်နိုင်ပါသည်ዠ<ph name="BEGIN_LEARN_MORE_LINK" />ပိုမိုလေ့လာရန်<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ရှင်းလင်းပို့á€á€»á€á€»á€€á€º ပြန်စရန်</translation>
+<translation id="2219735899272417925">စက်ကို ပြင်ဆင်သá€á€ºá€™á€¾á€á€ºá€›á€”် လိုအပ်သည်</translation>
<translation id="2224337661447660594">အင်á€á€¬á€”က် မရှိပါ</translation>
<translation id="2230458221926704099">သင့်á€á€»á€­á€á€ºá€†á€€á€ºá€™á€¾á€¯á€¡á€¬á€¸ <ph name="BEGIN_LINK" />ပြဿနာအဖြေရှာသည့် အက်ပ်<ph name="END_LINK" /> ကိုအသုံးပြုပြီး ဖြေရှင်းပါ</translation>
<translation id="2239100178324503013">ယá€á€¯á€•á€­á€¯á€·á€›á€”်</translation>
@@ -581,11 +586,13 @@
<translation id="2512101340618156538">á€á€½á€„့်ပြုမထားပါ (မူရင်း)</translation>
<translation id="2512413427717747692">Chrome ကို မူရင်းဘရောင်ဇာအနေဖြင့် သá€á€ºá€™á€¾á€á€ºá€á€¼á€„်းዠChrome ကို iOS ဆက်á€á€„်များá€á€½á€„် စနစ်á မူရင်းဘရောင်ဇာအဖြစ် သá€á€ºá€™á€¾á€á€ºá€›á€”် Enter á€á€œá€¯á€á€º နှိပ်ပါ</translation>
<translation id="2515629240566999685">သင့်နေရာဒေသရှိ လှိုင်းဆွဲအားကို စစ်ဆေးနေသည်</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> သုံးသည့် á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€á€½á€„် Touch ID ဖြင့် အá€á€Šá€ºá€•á€¼á€¯á€›á€”် သင်ရွေးထားသည်ዠသင့်ငွေပေးá€á€»á€±á€”ည်းလမ်း အá€á€»á€€á€ºá€¡á€œá€€á€ºá€á€½á€±á€€á€­á€¯ ဤပံ့ပိုးသူက သိမ်းထားနိုင်ပြီး áŽá€„်းá€á€­á€¯á€·á€€á€­á€¯ <ph name="LINK_TEXT" />နိုင်ပါသည်á‹</translation>
<translation id="2521385132275182522">ညာဘက်အောက်á€á€¼á€±á€á€½á€„် á€á€»á€¯á€•á€ºá€…က်ဖြင့် á€á€»á€¯á€•á€ºá€›á€”်</translation>
<translation id="2521736961081452453">ဖောင်ပြုလုပ်ရန်</translation>
<translation id="2523886232349826891">ဤစက်á€á€½á€„်သာ သိမ်းထားသည်</translation>
<translation id="2524461107774643265">နောက်ထပ် အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ ထည့်á€á€¼á€„်း</translation>
<translation id="2529899080962247600">ဤအကွက်á€á€½á€„် ထည့်သွင်းမှု <ph name="MAX_ITEMS_LIMIT" /> á€á€¯á€‘က်မပိုရပါዠနောက်ထပ် ထည့်သွင်းမှုအားလုံးကို လျစ်လျူရှုပါမည်á‹</translation>
+<translation id="253493526287553278">ပရိုမိုးရှင်းကုဒ်အသေးစိá€á€º ကြည့်ရန်</translation>
<translation id="2535585790302968248">သီးသန့်ကြည့်ရှုရန် ရုပ်ဖျက်á€á€˜á€º အသစ်á€á€…်á€á€¯á€–ွင့်နိုင်သည်</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{နှင့် နောက်ထပ် á á€á€¯}other{နှင့် နောက်ထပ် # á€á€¯}}</translation>
<translation id="2536110899380797252">လိပ်စာထည့်ရန်</translation>
@@ -625,7 +632,6 @@
<translation id="259821504105826686">ဓာá€á€ºá€•á€¯á€¶á€”ှင့် ဒစ်ဂျစ်á€á€šá€ºá€¡á€”ုပညာ</translation>
<translation id="2601150049980261779">အá€á€»á€…်ဇာá€á€ºá€œá€™á€ºá€¸ ရုပ်ရှင်များ</translation>
<translation id="2604589665489080024">ပေါ့ပ်ဂီá€</translation>
-<translation id="2609632851001447353">မူကွဲများ</translation>
<translation id="2610561535971892504">မိá€á€¹á€á€°á€€á€°á€¸á€›á€”် နှိပ်ပါ</translation>
<translation id="2617988307566202237">Chrome သည် အောက်ပါအá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ <ph name="BEGIN_EMPHASIS" />သိမ်းမည်မဟုá€á€ºá€•á€«<ph name="END_EMPHASIS" />-
<ph name="BEGIN_LIST" />
@@ -658,6 +664,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">မွေးနေ့နှင့် ကင်ပွန်းá€á€•á€ºá€”ေ့များ</translation>
<translation id="2677748264148917807">ထွက်á€á€½á€¬á€›á€”်</translation>
+<translation id="2679714844901977852">လုံá€á€¼á€¯á€¶á ပိုမိုမြန်ဆန်စွာ ငွေရှင်းနိုင်ရန် သင့်ကá€á€ºá€”ှင့် ငွေပေးá€á€»á€±á€™á€¾á€¯ အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ Google Account <ph name="USER_EMAIL" /> á€á€½á€„် သိမ်းထားနိုင်သည်</translation>
<translation id="2684561033061424857">ááxáá‚</translation>
<translation id="2687555958734450033">အကောင်းဆုံး အံကိုက်ညှိရန်</translation>
<translation id="2688969097326701645">ရှေ့ဆက်ပါ</translation>
@@ -706,7 +713,6 @@
<translation id="2854764410992194509">အင်á€á€¬á€”က်á€á€”်ဆောင်မှုပေးသူများ (ISPs)</translation>
<translation id="2856444702002559011">á€á€­á€¯á€€á€ºá€á€­á€¯á€€á€ºá€žá€°á€™á€»á€¬á€¸á€€ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ရှိ သင်áအá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ á€á€­á€¯á€¸á€šá€°á€›á€”် ကြိုးပမ်းနိုင်ပါသည် (ဥပမာ- စကားá€á€¾á€€á€ºáŠ မက်ဆေ့ဂျ်နှင့် á€á€›á€€á€ºá€’စ်ကá€á€º အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸)á‹ <ph name="BEGIN_LEARN_MORE_LINK" />ပိုမိုလေ့လာရန်<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ဤá€á€˜á€ºá€†á€­á€¯á€€á€ºá€€ စိá€á€ºá€¡á€”ှောင့်အယှက်ဖြစ်စေသော (သို့) အထင်အမြင်မှားစေသော ကြော်ငြာများကို ပြသည်á‹</translation>
-<translation id="286512204874376891">လိမ်လည်မှုကာကွယ်နိုင်ရန် ပကá€á€­á€¡á€žá€½á€„်ကá€á€ºá€€ သင့်ကá€á€ºá€¡á€…စ်ကို ရုပ်ဖျက်ပေးသည်ዠ<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">နှစ်လိုဖွယ်</translation>
<translation id="28761159517501904">ရုပ်ရှင်များ</translation>
<translation id="2876489322757410363">ပြင်ပအပလီကေးရှင်းဖြင့် ငွေပေးá€á€»á€±á€›á€”် ရုပ်ဖျက်မုဒ်မှ ထွက်နေသည်ዠရှေ့ဆက်မလားá‹</translation>
@@ -807,7 +813,6 @@
<translation id="3158539265159265653">ဒစ်á€á€º</translation>
<translation id="3162559335345991374">သင်အသုံးပြုနေသော Wi-Fi á login စာမျက်နှာသို့ သင်သွားရောက်ကြည့်ရှုရန် လိုမည်á‹</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">ကျွန်း</translation>
<translation id="3176929007561373547">ဤပရော့စီဆာဗာအလုပ်လုပ်ကြောင်း သေá€á€»á€¬á€™á€¾á€¯á€›á€¾á€­á€…ေရန် သင့်ပရော့စီဆက်á€á€„်ကို စစ်ဆေးပါ သို့မဟုá€á€º သင့်ကွန်ယက်စီမံá€á€”့်á€á€½á€²á€žá€°á€€á€­á€¯ ဆက်သွယ်ပါዠအကယ်áသင်မယုံကြည်လျှင် ပရော့စီဆာဗာကို အသုံးပြုသင့်သည် − <ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">ဤကိရိယာသုံးနေá€á€»á€­á€”်ကို သိလိုသည်</translation>
<translation id="3180358318770512945">မိဘအုပ်ထိန်းမှု</translation>
@@ -883,9 +888,6 @@
<translation id="3369192424181595722">နာရီ အမှား</translation>
<translation id="3369459162151165748">ယာဉ်အစိá€á€ºá€¡á€•á€­á€¯á€„်းနှင့် ဆက်စပ်ပစ္စည်းများ</translation>
<translation id="3371064404604898522">Chrome ကို မူရင်းဘရောင်ဇာအဖြစ် သá€á€ºá€™á€¾á€á€ºá€›á€”်</translation>
-<translation id="3371076217486966826"><ph name="URL" /> သည် -
- • သင့်ပá€á€ºá€á€”်းကျင်á 3D မြေပုံ ပြုလုပ်လိုပြီး ကင်မရာ á€á€Šá€ºá€”ေရာကိုá€á€¼á€±á€›á€¬á€á€¶á€œá€­á€¯á€žá€Šá€º
- • သင့်ကင်မရာကို သုံးလိုသည်</translation>
<translation id="337363190475750230">စီမံဆောင်ရွက်မှု မရှိá€á€±á€¬á€·á€žá€±á€¬</translation>
<translation id="3375754925484257129">Chrome လုံá€á€¼á€¯á€¶á€›á€±á€¸ စစ်ဆေးမှု လုပ်ဆောင်ရန်</translation>
<translation id="3377144306166885718">ဆာဗာက TLS ဗာရှင်းအဟောင်းကို သုံးထားသည်á‹</translation>
@@ -902,6 +904,7 @@
<translation id="3399952811970034796">ပေးပို့ရန် လိပ်စာ</translation>
<translation id="3402261774528610252">ဤá€á€˜á€ºá€†á€­á€¯á€€á€ºá€–ွင့်ရန် အသုံးပြုသော á€á€»á€­á€á€ºá€†á€€á€ºá€™á€¾á€¯á€€ TLS 1.0 သို့မဟုá€á€º TLS 1.1 ကို သုံးထားသည်ዠáŽá€„်းကို ရပ်ဆိုင်းထားပြီး နောင်á€á€½á€„်ပိá€á€ºá€œá€­á€¯á€€á€ºá€•á€«á€™á€Šá€ºá‹ ပိá€á€ºá€œá€­á€¯á€€á€ºá€žá€Šá€·á€ºá€¡á€á€« အသုံးပြုသူများက ဤá€á€˜á€ºá€†á€­á€¯á€€á€ºá€€á€­á€¯ ဖွင့်နိုင်á€á€±á€¬á€·á€™á€Šá€ºá€™á€Ÿá€¯á€á€ºá€•á€«á‹ ဆာဗာက TLS 1.2 နှင့်နောက်ပိုင်းကို ဖွင့်ပေးရပါမည်á‹</translation>
<translation id="3405664148539009465">ဖေါင့်များ စိá€á€ºá€€á€¼á€­á€¯á€€á€ºá€•á€¼á€„်ဆင်ရန်</translation>
+<translation id="3407789382767355356">ပြင်ပအဖွဲ့အစည်းသို့ လက်မှá€á€ºá€‘ိုးá€á€„်á€á€¼á€„်း</translation>
<translation id="3409896703495473338">လုံá€á€¼á€¯á€¶á€›á€±á€¸á€†á€€á€ºá€á€„်များ စီမံရန်</translation>
<translation id="3414952576877147120">အရွယ်အစား:</translation>
<translation id="3417660076059365994">သင်အပ်လုဒ်လုပ်သည့် (သို့) á€á€½á€²á€á€»á€­á€á€ºá€žá€Šá€·á€º ဖိုင်များကို ပိုင်းá€á€¼á€¬á€¸á€…ိá€á€ºá€–ြာရန် Google Cloud (သို့) ပြင်ပအဖွဲ့များသို့ ပို့လိုက်သည်ዠဥပမာ သá€á€­á€‘ားရသော ဒေá€á€¬ (သို့) မဲလ်á€á€² ရှိမရှိ áŽá€„်းá€á€­á€¯á€·á€€á€­á€¯ စစ်ဆေးနိုင်သည်á‹</translation>
@@ -934,6 +937,7 @@
<translation id="3477679029130949506">ရုပ်ရှင်စာရင်းနှင့် ရုပ်ရှင်ပြá€á€»á€­á€”်များ</translation>
<translation id="3479552764303398839">ယá€á€¯ မလို</translation>
<translation id="3484560055331845446">သင်á Google အကောင့်ကို အသုံးပြုá€á€½á€„့် ဆုံးရှုံးနိုင်သည်ዠသင့်စကားá€á€¾á€€á€ºá€€á€­á€¯ ယá€á€¯á€•á€„်ပြောင်းရန် Chrome က အကြံပြုပါသည်ዠသင့်အား လက်မှá€á€ºá€‘ိုးá€á€„်á€á€­á€¯á€„်းပါမည်á‹</translation>
+<translation id="3484861421501147767">သá€á€­á€•á€±á€¸á€á€»á€€á€º- သိမ်းထားပြီး သုံးနိုင်သည့် ပရိုမိုးရှင်းကုဒ်</translation>
<translation id="3487845404393360112">ဗန်း á„</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" /> စာမျက်နှာá€á€½á€„်ရှာပါ</translation>
<translation id="350069200438440499">ဖိုင်အမည်-</translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">စီမံရန်</translation>
<translation id="3816482573645936981">á€á€”်ဖိုး (အစားထိုးထားသည်)</translation>
<translation id="382518646247711829">သင်သည် ပရောက်စီ ဆာဗာ သုံးနေလျှင်...</translation>
+<translation id="3826050100957962900">ပြင်ပအဖွဲ့အစည်းသို့ လက်မှá€á€ºá€‘ိုးá€á€„်á€á€¼á€„်း</translation>
<translation id="3827112369919217609">အပြည့်အá€</translation>
<translation id="3827666161959873541">မိသားစု ရုပ်ရှင်များ</translation>
<translation id="3828924085048779000">စကားစုá€á€¾á€€á€º ပလာ ဖြစ်မှု á€á€½á€„့်မပြုပါá‹</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">ရက်စွဲ နှင့် အá€á€»á€­á€”်ကို မွမ်းမံပါ</translation>
<translation id="3858860766373142691">အမည်</translation>
<translation id="3872834068356954457">သိပွံ</translation>
+<translation id="3875783148670536197">လုပ်နည်း ပြရန်</translation>
<translation id="3881478300875776315">စာကြောင်းလျှော့áပြရန်</translation>
<translation id="3884278016824448484">စက်ပစ္စည်း မည်သူမည်á€á€«á€–ြစ်ကြောင်း အသိမှá€á€ºá€•á€¼á€¯á€žá€° ရှုပ်ထွေးနေ</translation>
-<translation id="3885155851504623709">နယ်​မြေ</translation>
<translation id="388632593194507180">စောင့်ကြည့်နေကြောင်း á€á€½á€±á€·á€›á€¾á€­á€‘ားသည်</translation>
<translation id="3886948180919384617">စီထည့်သည့်ပုံး áƒ</translation>
<translation id="3890664840433101773">အီးမေးလ် ထည့်ပါ</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> ကိုပိá€á€ºá€†á€­á€¯á€·á€‘ားသည်</translation>
<translation id="3978338123949022456">ရှာဖွေရေးမုဒ်ዠ<ph name="KEYWORD_SUFFIX" /> ဖြင့်ရှာဖွေရန် မေးမြန်းá€á€»á€€á€ºá€€á€­á€¯ ရိုက်ပြီး Enter á€á€œá€¯á€á€º နှိပ်ပါ</translation>
<translation id="398470910934384994">ငှက်များ</translation>
+<translation id="3985750352229496475">လိပ်စာများ စီမံရန်...</translation>
<translation id="3986705137476756801">'á€á€­á€¯á€€á€ºá€›á€­á€¯á€€á€ºá€…ာá€á€”်း' ကို ယá€á€¯ ပိá€á€ºá€›á€”်</translation>
<translation id="3987940399970879459">á MB ထက်နည်းပါသည်</translation>
<translation id="3990250421422698716">လည်ပá€á€ºá€™á€¾á€¯ á€á€»á€­á€”်ညှိရန်</translation>
+<translation id="3992684624889376114">ဤစာမျက်နှာ အကြောင်း</translation>
<translation id="3996311196211510766">á€á€˜á€ºá€†á€­á€¯á€€á€º <ph name="ORIGIN" /> က áŽá€„်းáá€á€±á€¬á€„်းဆိုá€á€»á€€á€º အားလုံးသို့
ရင်းမြစ်မူá€á€«á€’ ထည့်သွင်းရန် သá€á€ºá€™á€¾á€á€ºá€‘ားသော်လည်း လက်ရှိá€á€½á€„် ဤမူá€á€«á€’ကို ထည့်သွင်းáမရပါá‹</translation>
<translation id="4006465311664329701">Google Pay ကို သုံးထားသော ငွေပေးá€á€»á€±á€”ည်းလမ်းአကမ်းလှမ်းá€á€»á€€á€ºá€”ှင့် လိပ်စာများ</translation>
@@ -1228,6 +1235,7 @@
<translation id="4305666528087210886">သင့်ဖိုင်ကို သုံးáမရနိုင်ပါ</translation>
<translation id="4306529830550717874">လိပ်စာ သိမ်းမလားá‹</translation>
<translation id="4306812610847412719">ကလစ်ဘုá€á€º</translation>
+<translation id="4310070645992025887">သင့် ‘á€á€›á€®á€¸á€…ဉ်များ’ ရှာရန်</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">ပိá€á€ºá€†á€­á€¯á€·á€›á€”် (မူရင်း)</translation>
<translation id="4314815835985389558">စင့်á€á€ºá€€á€­á€¯ စီမံရန်</translation>
@@ -1278,6 +1286,7 @@
<translation id="4435702339979719576">ပို့စ်ကá€á€º)</translation>
<translation id="443673843213245140">ပရောက်စီ သုံးရန်ပိá€á€ºá€‘ားသော်လည်း á€á€­á€€á€»á€žá€Šá€ºá€· ပရောက်စီ စီစဉ်ဖွဲ့စည်းမှုကို သá€á€ºá€™á€¾á€á€ºá€‘ားသည်á‹</translation>
<translation id="4441832193888514600">မူá€á€«á€’ကို cloud အသုံးပြုသူလိုက်နာရမည့် မူá€á€«á€’အဖြစ်သာ သá€á€ºá€™á€¾á€á€ºá€”ိုင်သောကြောင့် လျစ်လျူရှုထားသည်á‹</translation>
+<translation id="4442470707340296952">Chrome á€á€˜á€ºá€™á€»á€¬á€¸</translation>
<translation id="4450893287417543264">ထပ်မပြပါနှင့်</translation>
<translation id="4451135742916150903">HID ကိရိယာများနှင့် á€á€»á€­á€á€ºá€†á€€á€ºá€›á€”် á€á€½á€„့်á€á€±á€¬á€„်းနိုင်သည်</translation>
<translation id="4452328064229197696">သင်သုံးလိုက်သောစကားá€á€¾á€€á€ºá€€á€­á€¯ ဒေá€á€¬á€€á€»á€­á€¯á€¸á€•á€±á€«á€€á€ºá€™á€¾á€¯á€á€½á€„် á€á€½á€±á€·á€›á€¾á€­á€‘ားသည်ዠသင့်အကောင့်များကို လုံá€á€¼á€¯á€¶á€…ေရန် ‘Google စကားá€á€¾á€€á€ºá€™á€”်နေဂျာ’ က သင်သိမ်းထားသော စကားá€á€¾á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ စစ်ဆေးရန် အကြံပြုပါသည်á‹</translation>
@@ -1416,6 +1425,7 @@
<translation id="483241715238664915">သá€á€­á€•á€±á€¸á€á€»á€€á€ºá€™á€»á€¬á€¸ ဖွင့်ရန်</translation>
<translation id="4834250788637067901">Google Pay ကို သုံးထားသော ငွေပေးá€á€»á€±á€”ည်းလမ်းአကမ်းလှမ်းá€á€»á€€á€ºá€”ှင့် လိပ်စာများ</translation>
<translation id="4838327282952368871">အိပ်မက်ဆန်ဆန်</translation>
+<translation id="4839087176073128681">Google á လုပ်ငန်းရှေ့ဆောင် လုံá€á€¼á€¯á€¶á€›á€±á€¸á€…နစ်ဖြင့် နောက်á€á€…်ကြိမ်á€á€½á€„် မြန်ဆန်စွာပေးá€á€»á€±á€•á€¼á€®á€¸ ကá€á€ºá€€á€­á€¯ ကာကွယ်လိုက်ပါá‹</translation>
<translation id="4840250757394056958">သင့် ‘Chrome မှá€á€ºá€á€™á€ºá€¸â€™ ကို ကြည့်ရန်</translation>
<translation id="484462545196658690">အော်á€á€­á€¯</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> နှင့် အá€á€¼á€¬á€¸á€¡á€›á€¬á€™á€»á€¬á€¸á€¡á€á€½á€€á€º လျှော့ဈေးရယူပါ</translation>
@@ -1478,6 +1488,7 @@
<translation id="5011561501798487822">á€á€½á€±á€·á€›á€¾á€­á€žá€±á€¬ ဘာသာစကား</translation>
<translation id="5015510746216210676">စက်ပစ္စည်း အမည်-</translation>
<translation id="5017554619425969104">သင် မိá€á€¹á€á€°á€€á€°á€¸á€‘ားသော စာသား</translation>
+<translation id="5017828934289857214">နောက်မှ သá€á€­á€•á€±á€¸á€›á€”်</translation>
<translation id="5018422839182700155">ဤစာမျက်နှာကို ဖွင့်မရပါ</translation>
<translation id="5019198164206649151">နောက်á€á€¶ စá€á€­á€¯á€¸ အá€á€¼á€±á€¡á€”ေ ဆိုးနေ</translation>
<translation id="5020776957610079374">ကမ္ဘာ့ á€á€±á€¸á€‚ီá€</translation>
@@ -1497,6 +1508,7 @@
<translation id="5051305769747448211">á€á€­á€¯á€€á€ºá€›á€­á€¯á€€á€ºá€œá€½á€¾á€„့် ဟာသ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{‘အနီးá€á€…်á€á€­á€¯á€€á€º မျှá€á€±á€á€¼á€„်း’ သုံးပြီး ဤဖိုင်ကိုပို့ရန် သင်áစက်á€á€½á€„် နေရာလွá€á€º (<ph name="DISK_SPACE_SIZE" />) ပြုလုပ်ပါ}other{‘အနီးá€á€…်á€á€­á€¯á€€á€º မျှá€á€±á€á€¼á€„်း’ သုံးပြီး ဤဖိုင်များကိုပို့ရန် သင်áစက်á€á€½á€„် နေရာလွá€á€º (<ph name="DISK_SPACE_SIZE" />) ပြုလုပ်ပါ}}</translation>
<translation id="5056549851600133418">သင့်အá€á€½á€€á€ºá€†á€±á€¬á€„်းပါးများ</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> သုံးသည့် á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€á€½á€„် Windows Hello ဖြင့် အá€á€Šá€ºá€•á€¼á€¯á€›á€”် သင်ရွေးထားသည်ዠသင့်ငွေပေးá€á€»á€±á€”ည်းလမ်း အá€á€»á€€á€ºá€¡á€œá€€á€ºá€á€½á€±á€€á€­á€¯ ဤပံ့ပိုးသူက သိမ်းထားနိုင်ပြီး áŽá€„်းá€á€­á€¯á€·á€€á€­á€¯ <ph name="LINK_TEXT" />နိုင်ပါသည်á‹</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" /> ကို ဆိုလိုá€á€¼á€„်းဖြစ်ပါသလားá‹</translation>
<translation id="5066056036849835175">ပုံနှိပ်ထုá€á€ºá€šá€°á€™á€¾á€¯ မှá€á€ºá€á€™á€ºá€¸</translation>
<translation id="5068234115460527047">သီးသန့် ရင်းနှီးမြှုပ်နှံငွေ</translation>
@@ -1510,10 +1522,8 @@
<translation id="5087286274860437796">ဆာဗာá အသိမှá€á€ºá€•á€¼á€¯á€œá€€á€ºá€™á€¾á€á€ºá€žá€Šá€º ယá€á€¯á€¡á€á€»á€­á€”်á€á€½á€„် မှန်ကန်မှုမရှိပါá‹</translation>
<translation id="5087580092889165836">ကá€á€ºá€€á€­á€¯á€‘ည့်ရန်</translation>
<translation id="5088142053160410913">အော်ပရေá€á€¬á€žá€­á€¯á€· မက်ဆေ့ဂျ်</translation>
-<translation id="5089810972385038852">ပြည်နယ်</translation>
<translation id="5093232627742069661">Z ပုံစံ á€á€±á€«á€€á€ºá€›á€”်</translation>
<translation id="5094747076828555589">ဒီဆာဗာက <ph name="DOMAIN" /> ဖြစ်á€á€¬á€€á€­á€¯ သက်သေထူ မပြနိုင်á€á€²á€·á€•á€«áŠ áŽá€„်းá လုံá€á€¼á€¯á€¶á€›á€±á€¸ လက်မှá€á€ºá€€á€­á€¯ Chromium ဘက်မှ မယုံကြည်ပါዠဖွဲ့စည်းစီစဉ်မှု အမှားကြောင့် သို့မဟုá€á€º á€á€­á€¯á€€á€ºá€á€­á€¯á€€á€ºá€œá€­á€¯á€žá€°á€€ သင်á á€á€»á€­á€á€ºá€†á€€á€ºá€™á€¾á€¯á€€á€­á€¯ ကြားဖြá€á€ºá€šá€°á€”ေá ထိုသို့ ဖြစ်လာနိုင်á€á€²á€·á€•á€«á€žá€Šá€ºá‹</translation>
-<translation id="5095208057601539847">á€á€­á€¯á€„်း</translation>
<translation id="5097099694988056070">CPU/RAM အသုံးပြုမှုကဲ့သို့ စက်ပစ္စည်းá ကိန်းဂá€á€”်း အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€žá€Šá€º မလုံá€á€¼á€¯á€¶á€•á€«</translation>
@@ -1551,6 +1561,7 @@
<translation id="5171045022955879922">ရှာကြည့်ရန် သို့မဟုá€á€º URL ရိုက်ထည့်ရန်</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">စက်</translation>
+<translation id="5177076414499237632">ဤစာမျက်နှာရင်းမြစ်နှင့် á€á€±á€«á€„်းစဉ်အကြောင်း လေ့လာရန်</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />á€á€½á€„် မရှိဘူးလား? ဤမှားယွင်းá€á€»á€€á€ºá€€á€­á€¯ အစီရင်á€á€¶á€™á€Šá€ºá‹</translation>
<translation id="518639307526414276">á€á€­á€›á€…္ဆာန်အစာနှင့် ပြုစုရေး ပစ္စည်းများ</translation>
<translation id="5190835502935405962">စာညှပ် ဘား</translation>
@@ -1711,6 +1722,7 @@
<translation id="5624120631404540903">စကားá€á€¾á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ စီမံကွပ်ကဲရန်</translation>
<translation id="5629630648637658800">မူá€á€«á€’ ဆက်á€á€„်များကို á€á€„် မပေးနိုင်á€á€²á€·</translation>
<translation id="5631439013527180824">ကိရိယာ စီမံကွပ်ကဲရေး á€á€­á€¯á€€á€„် မမှန်</translation>
+<translation id="5632485077360054581">လုပ်နည်း ပြရန်</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ပေါ်ရှိ လက်ရှိá€á€­á€¯á€€á€ºá€á€­á€¯á€€á€ºá€žá€°á€™á€»á€¬á€¸á€žá€Šá€º သင်á အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸ (ဥပမာ- ဓာá€á€ºá€•á€¯á€¶á€™á€»á€¬á€¸áŠ စကားá€á€¾á€€á€ºá€™á€»á€¬á€¸áŠ စာá€á€­á€¯á€™á€»á€¬á€¸á€”ှင့် á€á€›á€€á€ºá€’စ်ကá€á€ºá€¡á€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸) ကို á€á€­á€¯á€¸á€šá€°á€á€¼á€„်း သို့မဟုá€á€º ဖျက်á€á€¼á€„်းá€á€­á€¯á€· ပြုလုပ်နိုင်သည့် အန္á€á€›á€¬á€šá€ºá€›á€¾á€­á€žá€±á€¬á€•á€›á€­á€¯á€‚ရမ်များကို သင့်ကွန်ပျူá€á€¬á€‘ဲá€á€½á€„် ထည့်သွင်းရန် ကြိုးပမ်းနိုင်ပါသည်ዠ<ph name="BEGIN_LEARN_MORE_LINK" />ပိုမိုလေ့လာရန်<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">လှည့်ဖြားá€á€á€ºá€žá€Šá€·á€º အကြောင်းအရာများကို ပိá€á€ºá€‘ားပါသည်á‹</translation>
<translation id="5633259641094592098">ယုံကြည်မှုဆိုင်ရာနှင့် အင်ဒီး ရုပ်ရှင်များ</translation>
@@ -1828,6 +1840,7 @@
<translation id="5989320800837274978">သá€á€ºá€™á€¾á€á€º ပရောက်စီ ဆာဗာကိုáŽá€„်း .pac စာá€á€”်း URLကိုáŽá€„်း ဖေါ်ပြမပေးထားပါá‹</translation>
<translation id="5992691462791905444">အင်ဂျင်နီယာ Z ပုံစံ စာရွက်á€á€±á€«á€€á€ºá€›á€”်</translation>
<translation id="5995727681868049093">သင်á Google Account á€á€½á€„် သင့်အá€á€»á€€á€ºá€¡á€œá€€á€ºáŠ ပုဂ္ဂိုလ်ရေးနှင့် လုံá€á€¼á€¯á€¶á€›á€±á€¸á€†á€­á€¯á€„်ရာများကို စီမံနိုင်သည်</translation>
+<translation id="5997247540087773573">သင်သုံးလိုက်သောစကားá€á€¾á€€á€ºá€€á€­á€¯ ဒေá€á€¬á€€á€»á€­á€¯á€¸á€•á€±á€«á€€á€ºá€™á€¾á€¯á€á€½á€„် á€á€½á€±á€·á€›á€¾á€­á€‘ားသည်ዠသင့်အကောင့်များကို လုံá€á€¼á€¯á€¶á€…ေရန် ‘Google စကားá€á€¾á€€á€ºá€™á€”်နေဂျာ’ က သင်သိမ်းထားသော စကားá€á€¾á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ ယá€á€¯á€•á€¼á€±á€¬á€„်းရန်နှင့် စစ်ဆေးရန် အကြံပြုပါသည်á‹</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' အá€á€½á€€á€º ရလဒ် <ph name="RESULT_COUNT" /> á€á€¯</translation>
<translation id="6006484371116297560">ဂန္ထá€á€„်</translation>
<translation id="6008122969617370890">N မှ á သို့ အစီအစဉ်</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">နောင်á€á€½á€„် မြန်ဆန်စွာပေးá€á€»á€±á€”ိုင်ရန် သင်áကá€á€ºá€”ှင့် ငွေá€á€±á€¬á€„်းá€á€¶á€œá€½á€¾á€¬á€•á€­á€¯á€·á€žá€±á€¬ လိပ်စာကို သင့် Google အကောင့်á€á€½á€„် သိမ်းပါá‹</translation>
<translation id="6279183038361895380">သင်á ညွှန်းမြားကို ပြပေးရန် |<ph name="ACCELERATOR" />ကို နှိပ်ပါ</translation>
<translation id="6280223929691119688">ဤလိပ်စာသို့ ပို့áမရပါዠအá€á€¼á€¬á€¸á€œá€­á€•á€ºá€…ာá€á€…်á€á€¯á€€á€­á€¯ ရွေးပါá‹</translation>
-<translation id="6282194474023008486">စာပို့သင်္ကေá€</translation>
<translation id="6285507000506177184">Chrome á€á€½á€„် ဒေါင်းလုဒ်များ စီမံရန်á€á€œá€¯á€á€ºáŠ Chrome á€á€½á€„် ဒေါင်းလုဒ်လုပ်á€á€²á€·á€žá€Šá€·á€ºá€–ိုင်များကို စီမံရန် Enter နှိပ်ပါ</translation>
<translation id="6289939620939689042">စာမျက်နှာ အရောင်</translation>
<translation id="6290238015253830360">သင်အကြံပြုထားသည့် ဆောင်းပါးများ ဤနေရာá€á€½á€„် ပေါ်ပါမည်</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> အောက် နေရာလွá€á€ºá€•á€¼á€¯á€œá€¯á€•á€ºá€•á€«á‹ အá€á€»á€­á€¯á€·á€á€˜á€ºá€†á€­á€¯á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ နောက်á€á€…်ကြိမ်ဖွင့်သည့်အá€á€« ပိုမိုနှေးကွေးစွာ ဖွင့်ပါလိမ့်မည်á‹</translation>
<translation id="6337534724793800597">မူá€á€«á€’များကို အမည်များဖြင့် စစ်ထုá€á€ºá€›á€”်</translation>
<translation id="6340739886198108203">လျှို့á€á€¾á€€á€ºá€¡á€€á€¼á€±á€¬á€„်းအရာများ မြင်ရပါက ဖန်သားပြင်ဓာá€á€ºá€•á€¯á€¶á€›á€­á€¯á€€á€ºá€€á€°á€¸á€á€¼á€„်း (သို့) မှá€á€ºá€á€™á€ºá€¸á€á€„်á€á€¼á€„်းá€á€­á€¯á€·á€€á€­á€¯ စီမံá€á€”့်á€á€½á€²á€žá€°á€™á€°á€á€«á€’က အကြံပြုမထားပါ-</translation>
+<translation id="6348220984832452017">မူကွဲများ ဖွင့်ရန်</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ထည့်သွင်းရန်</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{မရှိ}=1{စကားá€á€¾á€€á€º á á€á€¯ (<ph name="DOMAIN_LIST" /> အá€á€½á€€á€ºáŠ စင့်á€á€ºá€œá€¯á€•á€ºá€‘ားသည်)}=2{စကားá€á€¾á€€á€º á‚ á€á€¯ (<ph name="DOMAIN_LIST" /> အá€á€½á€€á€ºáŠ စင့်á€á€ºá€œá€¯á€•á€ºá€‘ားသည်)}other{စကားá€á€¾á€€á€º # á€á€¯ (<ph name="DOMAIN_LIST" /> အá€á€½á€€á€ºáŠ စင့်á€á€ºá€œá€¯á€•á€ºá€‘ားသည်)}}</translation>
<translation id="6355392890578844978">ဤဘရောင်ဇာကို ကုမ္ပá€á€® သို့မဟုá€á€º အá€á€¼á€¬á€¸á€¡á€–ွဲ့အစည်းက စီမံá€á€”့်á€á€½á€²á€™á€‘ားပါዠဤစက်ပေါ်ရှိ လုပ်ဆောင်á€á€»á€€á€ºá€€á€­á€¯ Chromium ပြင်ပမှ စီမံá€á€”့်á€á€½á€²á€‘ားá€á€¼á€„်း ဖြစ်နိုင်သည်ዠ<ph name="BEGIN_LINK" />ပိုမိုလေ့လာရန်<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">Google စကားá€á€¾á€€á€º ပြောင်းရန်</translation>
<translation id="6433490469411711332">အဆက်အသွယ်အá€á€»á€€á€ºá€¡á€œá€€á€ºá€€á€­á€¯ á€á€Šá€ºá€¸á€–ြá€á€ºá€•á€«</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> သည်á€á€»á€­á€á€ºá€†á€€á€ºá€›á€”် ငြင်းပယ်á€á€²á€·á€žá€Šá€ºá‹</translation>
-<translation id="6438025220577812695">ကိုယ်á€á€­á€¯á€„်ပြောင်းရန်</translation>
<translation id="6440503408713884761">လျစ်လျူရှုထားသည်</translation>
<translation id="6443406338865242315">သင်ထည့်သွင်းထားသည့် နောက်ဆက်á€á€½á€²á€”ှင့် ပလá€á€ºá€¡á€„်များ</translation>
<translation id="6446163441502663861">Kahu (စာအိá€á€º)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">မျိုးရိုးဗီဇဗေဒ</translation>
<translation id="6831043979455480757">ဘာသာပြန်ရန်</translation>
<translation id="6833752742582340615">လုံá€á€¼á€¯á€¶á ပိုမိုမြန်ဆန်စွာ ငွေရှင်းနိုင်ရန်အá€á€½á€€á€º သင့်ကá€á€ºá€”ှင့် ငွေပေးá€á€»á€±á€™á€¾á€¯ အá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ Google Account á€á€½á€„် သိမ်းထားနိုင်သည်</translation>
-<translation id="6839929833149231406">နယ်မြေ</translation>
<translation id="6846340164947227603">အသွင်á€á€¯ ကá€á€ºá€”ံပါá€á€ºá€€á€­á€¯ အသုံးပြုပါ...</translation>
<translation id="6852204201400771460">အက်ပ်ကို ပြန်လည်စá€á€„်လိုပါသလားá‹</translation>
+<translation id="6857776781123259569">စကားá€á€¾á€€á€ºá€™á€»á€¬á€¸ စီမံရန်…</translation>
<translation id="686485648936420384">အသုံးပြုသူရင်းမြစ်များ</translation>
<translation id="6865412394715372076">ဤကá€á€ºá€€á€­á€¯ ယá€á€¯á€¡á€á€Šá€ºá€•á€¼á€¯á မရသေးပါ</translation>
<translation id="6869334554832814367">ကိုယ်ရေးကိုယ်á€á€¬á€á€»á€±á€¸á€„ွေများ</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">စက်ပစ္စည်း</translation>
<translation id="696703987787944103">လိုက်လျောညီထွေ</translation>
<translation id="6968269510885595029">သင့်လုံá€á€¼á€¯á€¶á€›á€±á€¸á€€á€®á€¸á€€á€­á€¯ သုံးပါ</translation>
-<translation id="6970216967273061347">ဒေသ</translation>
<translation id="6971439137020188025">Slides á€á€½á€„် Google á€á€„်ပြမှုအသစ် အမြန်ပြုလုပ်ရန်</translation>
<translation id="6972629891077993081">HID စက်များ</translation>
<translation id="6973656660372572881">á€á€­á€€á€»á€žá€Šá€·á€º ပရောက်စီဆာဗာနှင့် .pac script URL á€á€­á€¯á€·á€”ှစ်á€á€¯á€œá€¯á€¶á€¸ သá€á€ºá€™á€¾á€á€ºá€•á€¼á€®á€¸á€•á€«á€•á€¼á€®á‹</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">ဤá€á€”်ဆောင်မှုကို သင့်စက်á€á€½á€„် အသုံးပြုáမရပါ</translation>
<translation id="7083258188081898530">ဗန်း á‰</translation>
<translation id="7086090958708083563">အသုံးပြုသူက အပ်လုဒ်လုပ်ရန် á€á€±á€¬á€„်းဆိုထားသည်</translation>
-<translation id="7087282848513945231">ကောင်á€á€®</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />አChrome ဆက်á€á€„်များá€á€½á€„် á€á€˜á€ºá€†á€­á€¯á€€á€ºá€¡á€¬á€¸á€œá€¯á€¶á€¸á€›á€¾á€­ á€á€½á€„့်ပြုá€á€»á€€á€ºá€™á€»á€¬á€¸á€”ှင့် သိမ်းထားသောဒေá€á€¬á€™á€»á€¬á€¸á€€á€­á€¯ စီမံရန် Enter နှိပ်ပါ</translation>
<translation id="7096937462164235847">ဤá€á€˜á€ºá€†á€­á€¯á€€á€ºá အထောက်အထားကို စိစစ်မထားပါá‹</translation>
<translation id="7101893872976785596">ထိá€á€ºá€œá€”့်စရာရုပ်ရှင်များ</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />ဖောင်များá€á€½á€„်ထည့်ထားသော အá€á€»á€€á€ºá€¡á€œá€€á€º<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">ဤလိပ်စာသို့ ပို့áမရပါዠအá€á€¼á€¬á€¸á€œá€­á€•á€ºá€…ာá€á€…်á€á€¯á€€á€­á€¯ ရွေးပါá‹</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ဤဒေá€á€¬á€€á€°á€¸á€šá€°á€á€¼á€„်းကို သင့်စီမံá€á€”့်á€á€½á€²á€žá€°á€€ á€á€¬á€¸á€™á€¼á€…်ထားသည်á‹</translation>
<translation id="7135130955892390533">အá€á€¼á€±á€¡á€”ေ ပြရန်</translation>
<translation id="7138472120740807366">ပို့ရန် နည်းလမ်း</translation>
-<translation id="7139724024395191329">ယေမီရိá€á€º</translation>
<translation id="7139892792842608322">ပင်မဗန်း</translation>
<translation id="714064300541049402">ဘေး Ⴀပုံ အလျားလိုက် အရွှေ့</translation>
<translation id="7152423860607593928">နံပါá€á€º-áá„ (စာအိá€á€º)</translation>
@@ -2435,6 +2445,7 @@
<translation id="7669271284792375604">ဤဆိုက်ပေါ်ရှိ á€á€­á€¯á€€á€ºá€á€­á€¯á€€á€ºá€žá€°á€™á€»á€¬á€¸á€žá€Šá€ºá€žá€„့်အင်á€á€¬á€”က် အသုံးပြုမှု အá€á€½á€±á€·á€¡á€€á€¼á€¯á€¶á€€á€­á€¯ ထိá€á€­á€¯á€€á€ºá€…ေမည့် (ဥပမာአသင့်ပင်မစာမျက်နှာကို ပြောင်းလဲá€á€¼á€„်း သို့မဟုá€á€º သင်သွားရောက်သည့် ဆိုက်များá€á€½á€„် နောက်ထပ် ကြော်ငြာများ ပြသá€á€¼á€„်း) ပရိုဂရမ်များကို သင့်အား လှည့်ဖြားá ထည့်သွင်းá€á€­á€¯á€„်းမည်ဖြစ်သည်á‹</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ဒေá€á€¬á€žá€¯á€¶á€¸á€‘ားသည့် လုပ်ဆောင်á€á€»á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ လျှို့á€á€¾á€€á€ºá€¡á€–ြစ် အလံပြထားသည် (အကောင့်á€á€„်ပြီးကá€á€Šá€ºá€¸á€€ လုပ်ဆောင်á€á€»á€€á€º 1 á€á€¯)á‹ <ph name="BEGIN_LINK" />ပိုမိုလေ့လာရန်<ph name="END_LINK" />}other{ဒေá€á€¬á€žá€¯á€¶á€¸á€‘ားသည့် လုပ်ဆောင်á€á€»á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ လျှို့á€á€¾á€€á€ºá€¡á€–ြစ် အလံပြထားသည် (အကောင့်á€á€„်ပြီးကá€á€Šá€ºá€¸á€€ လုပ်ဆောင်á€á€»á€€á€º # á€á€¯)á‹ <ph name="BEGIN_LINK" />ပိုမိုလေ့လာရန်<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">စာá€á€­á€¯á€€á€ºá€•á€¯á€¶á€¸ á†</translation>
+<translation id="7675325315208090829">ငွေပေးá€á€»á€±á€”ည်းလမ်းများ စီမံရန်...</translation>
<translation id="7676643023259824263">ကလစ်ဘုá€á€ºá€…ာသား <ph name="TEXT" /> အá€á€½á€€á€º ရှာဖွေမှု</translation>
<translation id="7679367271685653708">Chrome ဆက်á€á€„်များá€á€½á€„် သင့်ကြည့်ရှုá€á€¼á€„်းမှá€á€ºá€á€™á€ºá€¸á€€á€­á€¯ ကြည့်ရှုစီမံပါ</translation>
<translation id="7679947978757153706">ဘေ့စ်ဘော</translation>
@@ -2477,7 +2488,6 @@
<translation id="7766518757692125295">အနားသá€á€º</translation>
<translation id="7770259615151589601">သá€á€ºá€™á€¾á€á€ºá€‘ားသော အရှည်</translation>
<translation id="7773005668374414287">á€á€°á€Šá€®á€žá€Šá€·á€ºá€¡á€…ဉ်ဖြင့် အပေါ်လှန်ထားရန်</translation>
-<translation id="777702478322588152">နယ်​မြေ</translation>
<translation id="7791011319128895129">မထွက်သေးပါ</translation>
<translation id="7791196057686275387">စည်းရန်</translation>
<translation id="7791543448312431591">ထည့်ရန်</translation>
@@ -2569,12 +2579,12 @@
<translation id="8055534648776115597">အသက်မွေးá€á€™á€ºá€¸á€€á€¼á€±á€¬á€„်းနှင့် ဆက်လက်ပညာသင်ကြားá€á€¼á€„်း</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" ကို မှန်ကန်စွာ စီစဉ်သá€á€ºá€™á€¾á€á€ºá€™á€‘ားပါዠ"<ph name="SOFTWARE_NAME" />" ပရိုဂရမ်ကို ဖယ်ရှားလိုက်ဖြင့် ပြဿနာကို ဖြေရှင်းပေးလေ့ရှိပါသည်ዠ<ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">အစားအသောက်ထုá€á€ºá€œá€¯á€•á€ºá€™á€¾á€¯</translation>
-<translation id="8066955247577885446">á€á€…်á€á€¯á€á€¯ မှားသွားသည်á‹</translation>
<translation id="8067872629359326442">လှည့်ဖြားá€á€á€ºá€žá€±á€¬ á€á€˜á€ºá€†á€­á€¯á€€á€ºá€á€…်á€á€¯á€á€½á€„် သင့်စကားá€á€¾á€€á€ºá€€á€­á€¯ သင်က ယá€á€¯á€œá€±á€¸á€á€½á€„် ထည့်လိုက်သည်ዠChromium က ကူညီနိုင်ပါသည်ዠသင်áစကားá€á€¾á€€á€º ပြောင်းရန်နှင့် သင့်အကောင့်အန္á€á€›á€¬á€šá€ºá€›á€¾á€­á€”ိုင်ကြောင်း Google သို့ အသိပေးရန် 'အကောင့် ကာကွယ်ရေး' ကို နှိပ်ပါá‹</translation>
<translation id="8070439594494267500">အက်ပ်သင်္ကေá€</translation>
<translation id="8074253406171541171">áá€xáრ(စာအိá€á€º)</translation>
<translation id="8075736640322370409">Google Sheet အသစ် အမြန်ပြုလုပ်ရန်</translation>
<translation id="8075898834294118863">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€†á€€á€ºá€á€„်များ စီမံရန်</translation>
+<translation id="8076492880354921740">á€á€˜á€ºá€™á€»á€¬á€¸</translation>
<translation id="8078141288243656252">လှည့်ထားသည့်အá€á€« မှá€á€ºá€á€»á€€á€ºá€•á€±á€¸á မရပါ</translation>
<translation id="8079031581361219619">á€á€˜á€ºá€†á€­á€¯á€€á€ºá€€á€­á€¯ ပြန်ဖွင့်လိုပါသလားá‹</translation>
<translation id="8081087320434522107">ဆီဒင်ကားများ</translation>
@@ -2697,6 +2707,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ဆက်á€á€„်များ</translation>
+<translation id="8428634594422941299">ရပါပြီ</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />አChrome ဆက်á€á€„်များá€á€½á€„် သင်áကွá€á€ºá€€á€®á€¸ သá€á€ºá€™á€¾á€á€ºá€á€»á€€á€ºá€™á€»á€¬á€¸á€€á€­á€¯ စီမံရန် ‘á€á€˜á€ºá€á€œá€¯á€á€ºâ€™ နှိပ်ပြီးနောက် Enter နှိပ်ပါ</translation>
<translation id="8433057134996913067">áŽá€„်းသည် သင့်ကို á€á€˜á€ºá€†á€­á€¯á€€á€ºá€¡á€™á€»á€¬á€¸á€…ုမှ ထွက်စေပါမည်á‹</translation>
<translation id="8434840396568290395">အိမ်မွေးá€á€­á€›á€…္ဆာန်</translation>
@@ -2794,6 +2805,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> သည် <ph name="ORIGIN" /> အá€á€½á€€á€º သင်áကုဒ်ဖြစ်သည်</translation>
<translation id="874918643257405732">ဤá€á€˜á€ºáလိပ်စာကို မှá€á€ºá€›á€”်</translation>
<translation id="8751426954251315517">နောက်á€á€…်ကြိမ် ထပ်စမ်းကြည့်ပါ</translation>
+<translation id="8757526089434340176">Google Pay ကမ်းလှမ်းá€á€»á€€á€º ရနိုင်သည်</translation>
<translation id="8758885506338294482">ဗီဒီယိုဂိမ်းကစားယှဉ်ပြိုင်á€á€¼á€„်း</translation>
<translation id="8759274551635299824">ဤကဒ်သက်á€á€™á€ºá€¸ ကုန်သွားပါပြီ</translation>
<translation id="87601671197631245">ဤá€á€˜á€ºá€†á€­á€¯á€€á€ºá€žá€Šá€º လုံá€á€¼á€¯á€¶á€›á€±á€¸ စီစဉ်သá€á€ºá€™á€¾á€á€ºá€á€»á€€á€ºá€¡á€Ÿá€±á€¬á€„်းကို အသုံးပြုသည့်အá€á€½á€€á€º သင်áအá€á€»á€€á€ºá€¡á€œá€€á€ºá€™á€»á€¬á€¸ (ဥပမာ စကားá€á€¾á€€á€ºá€™á€»á€¬á€¸áŠ မက်ဆေ့ဂျ်များ သို့မဟုá€á€º á€á€›á€€á€ºá€’စ်ကá€á€ºá€™á€»á€¬á€¸) ကို ဤá€á€˜á€ºá€†á€­á€¯á€€á€ºá€žá€­á€¯á€·á€•á€­á€¯á€·á€žá€Šá€ºá€·á€¡á€á€« ပေါက်ကြားသွားနိုင်ပါသည်á‹</translation>
@@ -2801,6 +2813,7 @@
<translation id="8763927697961133303">USB ကိရိယာ</translation>
<translation id="8763986294015493060">လက်ရှိဖွင့်ထားသော ရုပ်ဖျက်á€á€„်းဒိုးအားလုံးကို ပိá€á€ºá€”ိုင်သည်</translation>
<translation id="8766943070169463815">လုံá€á€¼á€¯á€¶á€žá€±á€¬á€•á€±á€¸á€á€»á€±á€™á€¾á€¯ အထောက်အထား စိစစ်á€á€¼á€„်းစာရွက် ဖွင့်ထားသည်</translation>
+<translation id="8767765348545497220">အကူအညီ ပူဖောင်းကွက်ကို ပိá€á€ºá€›á€”်</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">မော်á€á€±á€¬á€ºá€†á€­á€¯á€„်ကယ်များ</translation>
<translation id="8790007591277257123">&amp;ဖျက်ရန်ကို ပြန်လုပ်ရန်</translation>
@@ -2813,6 +2826,7 @@
<translation id="8806285662264631610">ရေá€á€»á€­á€¯á€¸á€…ရာနှင့် လိမ်းကျံစရာများ</translation>
<translation id="8807160976559152894">á€á€…်မျက်နှာအပြီး ဖြá€á€ºá€‘ုá€á€ºá€›á€”်</translation>
<translation id="8808828119384186784">Chrome ဆက်á€á€„်များ</translation>
+<translation id="8813277370772331957">နောက်မှ သá€á€­á€•á€±á€¸á€›á€”်</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />አသင့် Chrome ဆက်á€á€„်များမှá€á€…်ဆင့် Chrome ကို အပ်ဒိá€á€ºá€œá€¯á€•á€ºá€›á€”် Tab နှိပ်ပြီးနောက် Enter နှိပ်ပါ</translation>
<translation id="8820817407110198400">စာညှပ်များ</translation>
<translation id="882338992931677877">ကိုယ်á€á€­á€¯á€„်ထည့် အပေါက်</translation>
@@ -2992,6 +3006,7 @@
<translation id="988159990683914416">ပြုစုသူá á€á€Šá€ºá€†á€±á€¬á€€á€ºá€™á€¾á€¯</translation>
<translation id="989988560359834682">လိပ်စာ á€á€Šá€ºá€¸á€–ြá€á€ºá€›á€”်</translation>
<translation id="991413375315957741">လှုပ်ရှားမှု (သို့) အလင်းအာရုံá€á€¶á€€á€­á€›á€­á€šá€¬á€™á€»á€¬á€¸</translation>
+<translation id="992110854164447044">လိမ်လည်á€á€¶á€›á€á€¼á€„်းမှ ကာကွယ်ပေးနိုင်ရန်အá€á€½á€€á€º ပကá€á€­á€¡á€žá€½á€„်ကá€á€ºá€žá€Šá€º သင့်ကá€á€ºá€¡á€…စ်ကို ဖျောက်ပေးသည်ዠ<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ပန်းရောင်</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" ကို သင်áကွန်ပျူá€á€¬ သို့မဟုá€á€º ကွန်ရက်á€á€½á€„် မှန်ကန်စွာ ထည့်သွင်းမထားပါ−
diff --git a/chromium/components/strings/components_strings_ne.xtb b/chromium/components/strings/components_strings_ne.xtb
index a382b01216b..1afafb27a40 100644
--- a/chromium/components/strings/components_strings_ne.xtb
+++ b/chromium/components/strings/components_strings_ne.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">अनà¥à¤¦à¤¾à¤¨, छातà¥à¤°à¤µà¥ƒà¤¤à¥à¤¤à¤¿ तथा आरà¥à¤¥à¤¿à¤• सहयोग</translation>
<translation id="1048785276086539861">तपाईंले à¤à¤¨à¥‹à¤Ÿà¥‡à¤¸à¤¨à¤¹à¤°à¥‚ समà¥à¤ªà¤¾à¤¦à¤¨ गरà¥à¤¦à¤¾ सà¥à¤•à¥à¤°à¤¿à¤¨à¤®à¤¾ यो कागजातको à¤à¤‰à¤Ÿà¤¾ मातà¥à¤° पृषà¥à¤  देखिने छ</translation>
<translation id="1050038467049342496">अनà¥à¤¯ à¤à¤ªà¤¹à¤°à¥‚ बनà¥à¤¦ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="1053959602163383901">तपाईंले <ph name="PROVIDER_ORIGIN" /> पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥‡ वेबसाइटहरूमा पà¥à¤°à¤®à¤¾à¤£à¥€à¤•à¤°à¤£ गरà¥à¤¨à¥‡ डिभाइसमारà¥à¤«à¤¤ पà¥à¤·à¥à¤Ÿà¤¿ गरà¥à¤¨à¥‡ विकलà¥à¤ª छनौट गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ छ। यो पà¥à¤°à¤¦à¤¾à¤¯à¤•à¤²à¥‡ तपाईंको भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ विधिसमà¥à¤¬à¤¨à¥à¤§à¥€ जानकारी भणà¥à¤¡à¤¾à¤°à¤£ गरेको हà¥à¤¨ सकà¥à¤›, तपाईं सो जानकारी <ph name="LINK_TEXT" /> गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›à¥¤</translation>
<translation id="1055184225775184556">&amp;पूरà¥à¤µà¤¸à¥à¤¥à¤¿à¤¤à¤¿à¤®à¤¾ फरà¥à¤•à¤¾à¤‰à¤¨à¥à¤¹à¥‹à¤¸à¥ थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="1056663316309890343">फोटो सफà¥à¤Ÿà¤µà¥‡à¤¯à¤°</translation>
<translation id="1056898198331236512">चेतावनी</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">पिकअपको विधि</translation>
<translation id="1281476433249504884">सà¥à¤Ÿà¥à¤¯à¤¾à¤•à¤° १</translation>
<translation id="1285320974508926690">यो साइट कहिले पनि अनà¥à¤µà¤¾à¤¦ नगरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="1288548991597756084">कारà¥à¤¡ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रूपमा सेभ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="1292571435393770077">टà¥à¤°à¥‡ १६</translation>
<translation id="1292701964462482250">"तपाईंको कमà¥à¤ªà¥à¤¯à¥à¤Ÿà¤°à¤®à¤¾ रहेको सफà¥à¤Ÿà¤µà¥‡à¤¯à¤°à¤²à¥‡ Chrome लाई सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रूपमा वेबमा जडान हà¥à¤¨à¤¬à¤¾à¤Ÿ रोकिरहेको छ" (Windows कमà¥à¤ªà¥à¤¯à¥à¤Ÿà¤°à¤¹à¤°à¥‚ मातà¥à¤°)</translation>
<translation id="1294154142200295408">कमानà¥à¤¡ लाइनसमà¥à¤¬à¤¨à¥à¤§à¥€ विविधताहरू</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;तà¥à¤°à¥à¤Ÿà¤¿ समाधान गरà¥à¤¨, तपाईंले खोलà¥à¤¨à¥‡ पà¥à¤°à¤¯à¤¾à¤¸ गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ पृषà¥à¤ à¤®à¤¾ &lt;strong&gt;कनेकà¥à¤Ÿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥&lt;/strong&gt; मा कà¥à¤²à¤¿à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥â€Œà¥¤&lt;/p&gt;</translation>
<translation id="1507780850870535225">लà¥à¤¯à¤¾à¤¨à¥à¤¡à¤¸à¥à¤•à¥‡à¤ª डिजाइन</translation>
<translation id="1513706915089223971">इतिहाससमà¥à¤¬à¤¨à¥à¤§à¥€ पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿à¤¹à¤°à¥‚को सूची</translation>
+<translation id="1516097932025103760">यो कारà¥à¤¡ इनà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ तथा सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रूपमा सेभ गरिने छ तर CVC भने कहिले पनि भणà¥à¤¡à¤¾à¤°à¤£ गरिà¤à¤¦à¥ˆà¤¨à¥¤</translation>
<translation id="1517433312004943670">फोन नमà¥à¤¬à¤° आवशà¥à¤¯à¤• छ</translation>
<translation id="1519264250979466059">निरà¥à¤®à¤¾à¤£ मिति</translation>
<translation id="1521159554480556801">फाइबर तथा वसà¥à¤¤à¥à¤° कलाहरू</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡à¤¸à¤®à¥à¤¬à¤¨à¥à¤§à¥€ जानकारी सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ गरी फारमहरू भरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="1663943134801823270">कारà¥à¤¡ र ठेगानाहरू Chrome बाट पà¥à¤°à¤¾à¤ªà¥à¤¤ भà¤à¤•à¤¾ हà¥à¤¨à¥à¥¤ तपाईं <ph name="BEGIN_LINK" />सेटिङहरू<ph name="END_LINK" />मा गई तिनीहरूलाई वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¿à¤¤ गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›à¥¤</translation>
<translation id="1671391448414634642">अब उपà¥à¤°à¤¾à¤¨à¥à¤¤ <ph name="SOURCE_LANGUAGE" /> भाषाका पृषà¥à¤ à¤¹à¤°à¥‚लाई <ph name="TARGET_LANGUAGE" /> भाषामा अनà¥à¤µà¤¾à¤¦ गरिने छ।</translation>
+<translation id="1673886523110456987">अफर पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨ <ph name="CARD_DETAIL" /> मारà¥à¤«à¤¤ चेक आउट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> बाट <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">पहिले छोटो किनारा</translation>
<translation id="168693727862418163">यो नीतिको मान यसको सà¥à¤•à¤¿à¤®à¤¾à¤¸à¤à¤— मिलेन। तà¥à¤¯à¤¸à¤•à¤¾à¤°à¤£ यसलाई बेवासà¥à¤¤à¤¾ गरिने छ।</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">चालसमà¥à¤¬à¤¨à¥à¤§à¥€ सेनà¥à¤¸à¤°à¤¹à¤°à¥‚</translation>
<translation id="1717494416764505390">मेलबकà¥à¤¸ ३</translation>
<translation id="1718029547804390981">कागजात à¤à¤¨à¥‹à¤Ÿà¥‡à¤¸à¤¨ गरà¥à¤¨à¥ˆ नमिलà¥à¤¨à¥‡ गरी ठà¥à¤²à¥‹ छ</translation>
+<translation id="1720941539803966190">टà¥à¤¯à¥à¤Ÿà¥‹à¤°à¤¿à¤¯à¤² बनà¥à¤¦ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="1721424275792716183">* कà¥à¤·à¥‡à¤¤à¥à¤° आवशà¥à¤¯à¤• छ</translation>
<translation id="1727613060316725209">पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° वैध छ</translation>
<translation id="1727741090716970331">कारà¥à¤¡à¤•à¥‹ मानà¥à¤¯ नमà¥à¤¬à¤° थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">बारà¥à¤¬à¤¿à¤•à¥à¤¯à¥‚ तथा पोलेका परिकार</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> भाषाका पृषà¥à¤ à¤¹à¤°à¥‚ अनà¥à¤µà¤¾à¤¦ गरिने छैननà¥à¥¤</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{यो सेटिङ अन र यसको सà¥à¤¥à¤¿à¤¤à¤¿ सकà¥à¤°à¤¿à¤¯ भà¤à¤•à¥‹ खणà¥à¤¡à¤®à¤¾ Chrome ले मानà¥à¤›à¥‡à¤¹à¤°à¥‚को ठà¥à¤²à¥‹ समूह वा "साà¤à¤¾ गà¥à¤£ भà¤à¤•à¤¾ मानà¥à¤›à¥‡à¤¹à¤°à¥‚को समूह" मधà¥à¤¯à¥‡ कà¥à¤¨à¤¸à¤à¤— तपाईंले बà¥à¤°à¤¾à¤‰à¤œà¤° पà¥à¤°à¤¯à¥‹à¤— गरी हालसालै गरà¥à¤¨à¥à¤­à¤à¤•à¤¾ कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª सबैभनà¥à¤¦à¤¾ बढी मिलà¥à¤›à¤¨à¥ भनà¥à¤¨à¥‡ कà¥à¤°à¤¾ निरà¥à¤§à¤¾à¤°à¤£ गरà¥à¤›à¥¤ विजà¥à¤žà¤¾à¤ªà¤¨à¤¦à¤¾à¤¤à¤¾à¤¹à¤°à¥‚ उकà¥à¤¤ समूहमा आफूले देखाउन चाहेका विजà¥à¤žà¤¾à¤ªà¤¨à¤¹à¤°à¥‚ छनौट गरà¥à¤¨ सकà¥à¤›à¤¨à¥ र तपाईंले बà¥à¤°à¤¾à¤‰à¤œà¤° पà¥à¤°à¤¯à¥‹à¤— गरी गरà¥à¤¨à¥à¤­à¤à¤•à¤¾ कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª तपाईंको डिभाइसमा गोपà¥à¤¯ राखिनà¥à¤›à¤¨à¥à¥¤ तपाईंको समूह हरेक दिन अपडेट गरिनà¥à¤›à¥¤}=1{यो सेटिङ अन र यसको सà¥à¤¥à¤¿à¤¤à¤¿ सकà¥à¤°à¤¿à¤¯ भà¤à¤•à¥‹ खणà¥à¤¡à¤®à¤¾ Chrome ले मानà¥à¤›à¥‡à¤¹à¤°à¥‚को ठà¥à¤²à¥‹ समूह वा "साà¤à¤¾ गà¥à¤£ भà¤à¤•à¤¾ मानà¥à¤›à¥‡à¤¹à¤°à¥‚को समूह" मधà¥à¤¯à¥‡ कà¥à¤¨à¤¸à¤à¤— तपाईंले बà¥à¤°à¤¾à¤‰à¤œà¤° पà¥à¤°à¤¯à¥‹à¤— गरी हालसालै गरà¥à¤¨à¥à¤­à¤à¤•à¤¾ कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª सबैभनà¥à¤¦à¤¾ बढी मिलà¥à¤›à¤¨à¥ भनà¥à¤¨à¥‡ कà¥à¤°à¤¾ निरà¥à¤§à¤¾à¤°à¤£ गरà¥à¤›à¥¤ विजà¥à¤žà¤¾à¤ªà¤¨à¤¦à¤¾à¤¤à¤¾à¤¹à¤°à¥‚ उकà¥à¤¤ समूहमा आफूले देखाउन चाहेका विजà¥à¤žà¤¾à¤ªà¤¨à¤¹à¤°à¥‚ छनौट गरà¥à¤¨ सकà¥à¤›à¤¨à¥ र तपाईंले बà¥à¤°à¤¾à¤‰à¤œà¤° पà¥à¤°à¤¯à¥‹à¤— गरी गरà¥à¤¨à¥à¤­à¤à¤•à¤¾ कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª तपाईंको डिभाइसमा गोपà¥à¤¯ राखिनà¥à¤›à¤¨à¥à¥¤ तपाईंको समूह हरेक दिन अपडेट गरिनà¥à¤›à¥¤}other{यो सेटिङ अन र यसको सà¥à¤¥à¤¿à¤¤à¤¿ सकà¥à¤°à¤¿à¤¯ भà¤à¤•à¥‹ खणà¥à¤¡à¤®à¤¾ Chrome ले मानà¥à¤›à¥‡à¤¹à¤°à¥‚को ठà¥à¤²à¥‹ समूह वा "साà¤à¤¾ गà¥à¤£ भà¤à¤•à¤¾ मानà¥à¤›à¥‡à¤¹à¤°à¥‚को समूह" मधà¥à¤¯à¥‡ कà¥à¤¨à¤¸à¤à¤— तपाईंले बà¥à¤°à¤¾à¤‰à¤œà¤° पà¥à¤°à¤¯à¥‹à¤— गरी हालसालै गरà¥à¤¨à¥à¤­à¤à¤•à¤¾ कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª सबैभनà¥à¤¦à¤¾ बढी मिलà¥à¤›à¤¨à¥ भनà¥à¤¨à¥‡ कà¥à¤°à¤¾ निरà¥à¤§à¤¾à¤°à¤£ गरà¥à¤›à¥¤ विजà¥à¤žà¤¾à¤ªà¤¨à¤¦à¤¾à¤¤à¤¾à¤¹à¤°à¥‚ उकà¥à¤¤ समूहमा आफूले देखाउन चाहेका विजà¥à¤žà¤¾à¤ªà¤¨à¤¹à¤°à¥‚ छनौट गरà¥à¤¨ सकà¥à¤›à¤¨à¥ र तपाईंले बà¥à¤°à¤¾à¤‰à¤œà¤° पà¥à¤°à¤¯à¥‹à¤— गरी गरà¥à¤¨à¥à¤­à¤à¤•à¤¾ कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª तपाईंको डिभाइसमा गोपà¥à¤¯ राखिनà¥à¤›à¤¨à¥à¥¤ तपाईंको समूह हरेक {NUM_DAYS} दिनमा अपडेट गरिनà¥à¤›à¥¤}}</translation>
-<translation id="2053553514270667976">जिप कोड</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{१ सà¥à¤à¤¾à¤µ}other{# सà¥à¤à¤¾à¤µà¤¹à¤°à¥‚}}</translation>
+<translation id="2066915425250589881">मेटाइदिने अनà¥à¤°à¥‹à¤§</translation>
<translation id="2068528718802935086">शिशॠतथा भरà¥à¤–रै हिà¤à¤¡à¥à¤¨ थालेका बचà¥à¤šà¤¾à¤¹à¤°à¥‚</translation>
<translation id="2071156619270205202">यो कारà¥à¤¡à¤²à¤¾à¤ˆ भरà¥à¤šà¥à¤…ल कारà¥à¤¡ नमà¥à¤¬à¤° पà¥à¤°à¤¦à¤¾à¤¨ गरà¥à¤¨ मिलà¥à¤¦à¥ˆà¤¨à¥¤</translation>
<translation id="2071692954027939183">तपाईं सामानà¥à¤¯à¤¤à¤¯à¤¾ सूचनाहरू देखाउने अनà¥à¤®à¤¤à¤¿ दिनà¥à¤¹à¥à¤¨à¥à¤¨à¥¤ तà¥à¤¯à¤¸à¥ˆà¤²à¥‡ सूचनाहरू सà¥à¤µà¤¤à¤ƒ बà¥à¤²à¤• गरिà¤à¤•à¤¾ छनà¥</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">'सिंक गरà¥à¤¨à¥‡ सà¥à¤µà¤¿à¤§à¤¾ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥' नामक बटन, Chrome का सेटिङमा गई के कसà¥à¤¤à¤¾ जानकारी सिंक गरà¥à¤¨à¥‡ भनà¥à¤¨à¥‡ कà¥à¤°à¤¾ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨ Enter थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="2091887806945687916">आवाज</translation>
<translation id="2094505752054353250">डोमेन बेमेल</translation>
-<translation id="2096368010154057602">विभाग</translation>
<translation id="2099652385553570808">बायाà¤à¤ªà¤Ÿà¥à¤Ÿà¤¿ तीन सà¥à¤Ÿà¤¿à¤š</translation>
<translation id="2101225219012730419">संसà¥à¤•à¤°à¤£:</translation>
<translation id="2102134110707549001">भरपरà¥à¤¦à¥‹ पासवरà¥à¤¡ सिफारिस गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥â€¦</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">अमेरिकी फà¥à¤Ÿà¤¬à¤²</translation>
<translation id="2187317261103489799">पतà¥à¤¤à¤¾ लगाउनà¥à¤¹à¥‹à¤¸à¥ (डिफलà¥à¤Ÿ मान)</translation>
<translation id="2188375229972301266">फेदमा à¤à¤•à¤­à¤¨à¥à¤¦à¤¾ बढी पà¥à¤µà¤¾à¤²</translation>
-<translation id="2188852899391513400">तपाईंले भरà¥à¤–रै पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ पासवरà¥à¤¡ चोरी भà¤à¤•à¥‹ डेटाको सङà¥à¤—à¥à¤°à¤¹à¤®à¤¾ भेटिà¤à¤•à¥‹ छ। Google पासवरà¥à¤¡ मà¥à¤¯à¤¾à¤¨à¥‡à¤œà¤°à¤²à¥‡ तपाईंका खाता सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ राखà¥à¤¨à¥‡ पà¥à¤°à¤¯à¥‹à¤œà¤¨à¤•à¤¾ लागि अहिले नै यो पासवरà¥à¤¡ बदलà¥à¤¨ र तपाईंले सेभ गरेका पासवरà¥à¤¡à¤¹à¤°à¥‚ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ छनॠकि छैननॠजाà¤à¤šà¥à¤¨ सिफारिस गरà¥à¤›à¥¤</translation>
<translation id="219906046732893612">घरको सà¥à¤¤à¤°à¥‹à¤¨à¥à¤¨à¤¤à¤¿</translation>
<translation id="2202020181578195191">मà¥à¤¯à¤¾à¤¦ सकिने मानà¥à¤¯ वरà¥à¤· पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="22081806969704220">टà¥à¤°à¥‡ ३</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">फाइल समà¥à¤ªà¤¾à¤¦à¤¨ गरà¥à¤¦à¥ˆ</translation>
<translation id="2215963164070968490">कà¥à¤•à¥à¤°à¤¹à¤°à¥‚</translation>
<translation id="2218879909401188352">हाल <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> मा रहेका आकà¥à¤°à¤®à¤£à¤•à¤¾à¤°à¥€à¤¹à¤°à¥‚ले तपाईंको यनà¥à¤¤à¥à¤°à¤²à¤¾à¤ˆ नोकà¥à¤¸à¤¾à¤¨à¥€ पà¥à¤±à¥à¤¯à¤¾à¤‰à¤¨à¥‡, तपाईंको मोबाइल बिलमा नदेखिने शà¥à¤²à¥à¤• थपà¥à¤¨à¥‡ वा तपाईंको वà¥à¤¯à¤•à¥à¤¤à¤¿à¤—त जानकारी चोरà¥à¤¨à¥‡ जसà¥à¤¤à¤¾ खतरनाक à¤à¤ªà¤¹à¤°à¥‚ इनà¥à¤¸à¥à¤Ÿà¤² गरà¥à¤¨ सकà¥à¤›à¤¨à¥à¥¤ <ph name="BEGIN_LEARN_MORE_LINK" />थप जानà¥à¤¨à¥à¤¹à¥‹à¤¸à¥<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">टà¥à¤¯à¥‚टोरियल रिसà¥à¤Ÿà¤¾à¤°à¥à¤Ÿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="2219735899272417925">डिभाइस रिसेट गरà¥à¤¨à¥ परà¥à¤¨à¥‡ हà¥à¤¨à¥à¤›</translation>
<translation id="2224337661447660594">इनà¥à¤Ÿà¤°à¤¨à¥‡à¤Ÿ छैन</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />निदान à¤à¤ª<ph name="END_LINK" /> पà¥à¤°à¤¯à¥‹à¤— गरेर आफà¥à¤¨à¥‹ जडान समà¥à¤¬à¤¨à¥à¤§à¥€ समसà¥à¤¯à¤¾à¤•à¥‹ समाधान गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="2239100178324503013">अहिले पठाउनà¥à¤¹à¥‹à¤¸à¥</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">अनà¥à¤®à¤¤à¤¿ नदिइयोसॠ(डिफलà¥à¤Ÿ)</translation>
<translation id="2512413427717747692">Chrome लाई डिफलà¥à¤Ÿ बà¥à¤°à¤¾à¤‰à¤œà¤° बटनका रूपमा सेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥, iOS का सेटिङमा गई Chrome लाई सिसà¥à¤Ÿà¤®à¤•à¥‹ डिफलà¥à¤Ÿ बà¥à¤°à¤¾à¤‰à¤œà¤°à¤•à¤¾ रूपमा सेट गरà¥à¤¨ इनà¥à¤Ÿà¤° थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="2515629240566999685">आफà¥à¤¨à¥‹ कà¥à¤·à¥‡à¤¤à¥à¤°à¤•à¥‹ सिगà¥à¤¨à¤² जाà¤à¤š गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="2515761554693942801">तपाईंले <ph name="PROVIDER_ORIGIN" /> पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥‡ वेबसाइटमा Touch ID मारà¥à¤«à¤¤ पà¥à¤·à¥à¤Ÿà¤¿ गरà¥à¤¨à¥‡ विकलà¥à¤ª छनौट गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ छ। यो पà¥à¤°à¤¦à¤¾à¤¯à¤•à¤²à¥‡ तपाईंको भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ विधिसमà¥à¤¬à¤¨à¥à¤§à¥€ जानकारी भणà¥à¤¡à¤¾à¤°à¤£ गरेको हà¥à¤¨ सकà¥à¤›, तपाईं सो जानकारी <ph name="LINK_TEXT" /> गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›à¥¤</translation>
<translation id="2521385132275182522">फेदको दायाà¤à¤ªà¤Ÿà¥à¤Ÿà¤¿ सà¥à¤Ÿà¤¿à¤š</translation>
<translation id="2521736961081452453">फाराम बनाउनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="2523886232349826891">यो डिभाइसमा भने यस कारà¥à¤¡à¤²à¤¾à¤ˆ अà¤à¥ˆ पनि सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ गरिनà¥à¤›</translation>
<translation id="2524461107774643265">थप जानकारी थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="2529899080962247600">यो फिलà¥à¤¡à¤®à¤¾ <ph name="MAX_ITEMS_LIMIT" /> भनà¥à¤¦à¤¾ बढी पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿ हà¥à¤¨à¥ हà¥à¤à¤¦à¥ˆà¤¨à¥¤ थप सबै पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿ बेवासà¥à¤¤à¤¾ गरिने छनà¥à¥¤</translation>
+<translation id="253493526287553278">पà¥à¤°à¥‹à¤®à¥‹ कोडसमà¥à¤¬à¤¨à¥à¤§à¥€ विवरण हेरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="2535585790302968248">तपाईं गोपà¥à¤¯ रूपमा बà¥à¤°à¤¾à¤‰à¤œ गरà¥à¤¨ चाहनà¥à¤¹à¥à¤¨à¥à¤› भने नयाठइनà¥à¤•à¥‹à¤—à¥à¤¨à¤¿à¤Ÿà¥‹ टà¥à¤¯à¤¾à¤¬ खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{र थप १}other{र थप #}}</translation>
<translation id="2536110899380797252">ठेगाना थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">फोटोगà¥à¤°à¤¾à¤«à¥€ र डिजिटल कला</translation>
<translation id="2601150049980261779">रोमानà¥à¤¸ चलâ€à¤šà¤¿à¤¤à¥à¤°à¤¹à¤°à¥‚</translation>
<translation id="2604589665489080024">पप सङà¥à¤—ीत</translation>
-<translation id="2609632851001447353">विचरणहरू</translation>
<translation id="2610561535971892504">कà¥à¤²à¤¿à¤• टॠकपी</translation>
<translation id="2617988307566202237">Chrome ले निमà¥à¤¨ जानकारी <ph name="BEGIN_EMPHASIS" />सेभ गरà¥à¤¨à¥‡ छैन<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">जनà¥à¤®à¤¦à¤¿à¤¨ तथा नà¥à¤µà¤¾à¤°à¤¾à¤¨</translation>
<translation id="2677748264148917807">छोडà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="2679714844901977852">तपाईं सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रूपमा अठछिटो चेक आउट गरà¥à¤¨ चाहनà¥à¤¹à¥à¤¨à¥à¤› भने Google खाता <ph name="USER_EMAIL" /> मा आफà¥à¤¨à¥‹ कारà¥à¤¡ तथा बिलिङसमà¥à¤¬à¤¨à¥à¤§à¥€ जानकारी सेभ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">सबैभनà¥à¤¦à¤¾ मिलà¥à¤¨à¥‡</translation>
<translation id="2688969097326701645">अà¤, जारी राखà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">इनà¥à¤Ÿà¤°à¤¨à¥‡à¤Ÿ सेवा पà¥à¤°à¤¦à¤¾à¤¯à¤•à¤¹à¤°à¥‚ (ISP)</translation>
<translation id="2856444702002559011">आकà¥à¤°à¤®à¤£à¤•à¤¾à¤°à¥€à¤¹à¤°à¥‚ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> बाट तपाईंको जानकारी (उदाहरणका लागि, पासवरà¥à¤¡, सनà¥à¤¦à¥‡à¤¶ वा कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡à¤¹à¤°à¥‚) चोरà¥à¤¨à¥‡ पà¥à¤°à¤¯à¤¾à¤¸ गरिरहेका हà¥à¤¨à¤¸à¤•à¥à¤›à¤¨à¥à¥¤ <ph name="BEGIN_LEARN_MORE_LINK" />थप जानà¥à¤¨à¥à¤¹à¥‹à¤¸à¥<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">यो साइटले हसà¥à¤¤à¤•à¥à¤·à¥‡à¤ªà¤•à¤¾à¤°à¥€ वा भà¥à¤°à¤¾à¤®à¤• विजà¥à¤žà¤¾à¤ªà¤¨à¤¹à¤°à¥‚ देखाउà¤à¤›à¥¤</translation>
-<translation id="286512204874376891">भरà¥à¤šà¥à¤…ल कारà¥à¤¡à¤²à¥‡ तपाईंलाई ठगीबाट जोगाउनका निमà¥à¤¤à¤¿ तपाईंको कारà¥à¤¡à¤•à¥‹ वासà¥à¤¤à¤µà¤¿à¤• जानकारी लà¥à¤•à¤¾à¤‰à¤à¤›à¥¤ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">मैतà¥à¤°à¥€à¤ªà¥‚रà¥à¤£</translation>
<translation id="28761159517501904">चलचितà¥à¤°à¤¹à¤°à¥‚</translation>
<translation id="2876489322757410363">कà¥à¤¨à¥ˆ बाहà¥à¤¯ à¤à¤ªà¤®à¤¾à¤°à¥à¤«à¤¤ भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ गरà¥à¤¨ इनà¥à¤•à¥‹à¤—à¥à¤¨à¤¿à¤Ÿà¥‹ मोडबाट बाहिरिà¤à¤¦à¥ˆ हà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›à¥¤ जारी राखà¥à¤¨à¥‡ हो?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">डिसà¥à¤•</translation>
<translation id="3162559335345991374">तपाईà¤à¤²à¥‡ पà¥à¤°à¤¯à¥‹à¤— गरिरहनॠभà¤à¤•à¥‹ Wi-Fi लाई तपाईà¤à¤²à¥‡ यसको लगइन पृषà¥à¤ à¤•à¥‹ भà¥à¤°à¤®à¤£ गरà¥à¤¨ आवशà¥à¤¯à¤•à¤¤à¤¾ हà¥à¤¨ सकà¥à¤›à¥¤</translation>
<translation id="3169472444629675720">पतà¥à¤¤à¤¾ लगाउनà¥à¤¹à¥‹à¤¸à¥</translation>
-<translation id="3174168572213147020">टापà¥</translation>
<translation id="3176929007561373547">पà¥à¤°à¥‹à¤•à¥à¤¸à¥€ सरà¥à¤­à¤°à¤²à¥‡ काम गरिरहेको निशà¥à¤šà¤¿à¤¤ गरà¥à¤¨ तपाईंको पà¥à¤°à¥‹à¤•à¥à¤¸à¥€ सेटिङहरू जाà¤à¤šà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ वा तपाईंको नेटवरà¥à¤• पà¥à¤°à¤¶à¤¾à¤¸à¤•à¤²à¤¾à¤ˆ समà¥à¤ªà¤°à¥à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤ यदि तपाईंलाई तपाईंले पà¥à¤°à¥‹à¤•à¥à¤¸à¥€ सरà¥à¤­à¤° पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥ परà¥à¤¦à¤› भनà¥à¤¨à¥‡ विशà¥à¤µà¤¾à¤¸ छैन भने: <ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">तपाईं यो डिभाइस चलाउà¤à¤¦à¥ˆ हà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤› कि हà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤› भनà¥à¤¨à¥‡ कà¥à¤°à¤¾à¤•à¥‹ जानकारी</translation>
<translation id="3180358318770512945">लालनपालन</translation>
@@ -878,9 +883,6 @@
<translation id="3369192424181595722">घडी तà¥à¤°à¥à¤Ÿà¤¿</translation>
<translation id="3369459162151165748">गाडीका पारà¥à¤Ÿà¤ªà¥à¤°à¥à¤œà¤¾ तथा सहायक उपकरणहरू</translation>
<translation id="3371064404604898522">Chrome लाई डिफलà¥à¤Ÿ बà¥à¤°à¤¾à¤‰à¤œà¤°à¤•à¤¾ रूपमा सेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
-<translation id="3371076217486966826"><ph name="URL" /> निमà¥à¤¨ अनà¥à¤®à¤¤à¤¿ मागà¥à¤¦à¥ˆ छ:
- • तपाईंको सेरोफेरोको 3D नकà¥à¤¸à¤¾ बनाउने तथा कà¥à¤¯à¤¾à¤®à¥‡à¤°à¤¾à¤•à¥‹ सà¥à¤¥à¤¿à¤¤à¤¿ टà¥à¤°à¥à¤¯à¤¾à¤• गरà¥à¤¨à¥‡
- • तपाईंको कà¥à¤¯à¤¾à¤®à¥‡à¤°à¤¾ पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥‡</translation>
<translation id="337363190475750230">पà¥à¤°à¤¬à¤¨à¥à¤§ हटाइà¤à¤•à¥‹</translation>
<translation id="3375754925484257129">Chrome को सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤ªà¤¨à¤¾à¤•à¥‹ जाà¤à¤š गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3377144306166885718">सरà¥à¤­à¤°à¤²à¥‡ TLS को पà¥à¤°à¤šà¤²à¤¨à¤®à¤¾ नरहेको संसà¥à¤•à¤°à¤£ पà¥à¤°à¤¯à¥‹à¤— गरà¥â€à¤¯à¥‹à¥¤</translation>
@@ -897,6 +899,7 @@
<translation id="3399952811970034796">डेलिभरी गरà¥à¤¨à¥‡ ठेगाना</translation>
<translation id="3402261774528610252">यो साइट लोड गरà¥à¤¨ पà¥à¤°à¤¯à¥‹à¤— गरिà¤à¤•à¥‹ जडानले पà¥à¤°à¤šà¤²à¤¨à¤¬à¤¾à¤Ÿ हटाइà¤à¤•à¤¾ TLS १.० वा TLS १.१ पà¥à¤°à¤¯à¥‹à¤— गरेको छ र तिनलाई भविषà¥à¤¯à¤®à¤¾ असकà¥à¤·à¤® पारिने छ। असकà¥à¤·à¤® पारेपछि, पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤¹à¤°à¥‚ यो साइट लोड गरà¥à¤¨ सकà¥à¤¨à¥‡ छैननà¥à¥¤ सरà¥à¤­à¤°à¤²à¥‡ TLS १.२ वा तà¥à¤¯à¤¸à¤ªà¤›à¤¿à¤•à¥‹ संसà¥à¤•à¤°à¤£ सकà¥à¤·à¤® पारà¥à¤¨à¥ परà¥à¤›à¥¤</translation>
<translation id="3405664148539009465">फनà¥à¤Ÿà¤¹à¤°à¥‚लाई आफू अनà¥à¤•à¥‚ल गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="3407789382767355356">तेसà¥à¤°à¥‹ पकà¥à¤·à¥€à¤¯ साइन इन</translation>
<translation id="3409896703495473338">सà¥à¤°à¤•à¥à¤·à¤¾à¤¸à¤®à¥à¤¬à¤¨à¥à¤§à¥€ सेटिङ मिलाउनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3414952576877147120">आकार:</translation>
<translation id="3417660076059365994">तपाईंले अपलोड वा à¤à¤Ÿà¥à¤¯à¤¾à¤š गरà¥à¤¨à¥‡ फाइलहरू विशà¥à¤²à¥‡à¤·à¤£ गरà¥à¤¨à¥‡ पà¥à¤°à¤¯à¥‹à¤œà¤¨à¤•à¤¾ लागि Google Cloud मा वा तेसà¥à¤°à¤¾ पकà¥à¤·à¤•à¤¹à¤¾à¤ पठाइनà¥à¤›à¤¨à¥à¥¤ उदाहरणका लागि, ती फाइलमा संवेदनशील डेटा वा मालवेयर छनॠकि छैननॠभनी जाà¤à¤š गरिन सकà¥à¤›à¥¤</translation>
@@ -929,6 +932,7 @@
<translation id="3477679029130949506">चलचितà¥à¤° तथा नाटकघरको समयतालिका</translation>
<translation id="3479552764303398839">अहिले होइन</translation>
<translation id="3484560055331845446">तपाईं आफà¥à¤¨à¥‹ Google खातामाथिको पहà¥à¤à¤š गà¥à¤®à¤¾à¤‰à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›à¥¤ Chrome तपाईंलाई आफà¥à¤¨à¥‹ पासवरà¥à¤¡ अहिले नै परिवरà¥à¤¤à¤¨ गरà¥à¤¨ सिफारिस गरà¥à¤›à¥¤ तपाईंलाई साइन इन गरà¥à¤¨ लगाइने छ।</translation>
+<translation id="3484861421501147767">रिमाइनà¥à¤¡à¤°: सेभ गरिà¤à¤•à¥‹ पà¥à¤°à¥‹à¤®à¥‹ कोड उपलबà¥à¤§ छ</translation>
<translation id="3487845404393360112">टà¥à¤°à¥‡ ४</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" />
पृषà¥à¤ à¤®à¤¾ फेला पारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
@@ -1053,6 +1057,7 @@
<translation id="3810973564298564668">वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¿à¤¤ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3816482573645936981">मान (यो मानको साटो लागू गरिà¤à¤•à¥‹)</translation>
<translation id="382518646247711829">यदि तपाईंले कà¥à¤¨à¥ˆ à¤à¤‰à¤Ÿà¤¾ पà¥à¤°à¥‹à¤•à¥à¤¸à¥€ सरà¥à¤­à¤° पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥à¤­à¤¯à¥‹ भने ...</translation>
+<translation id="3826050100957962900">तेसà¥à¤°à¥‹ पकà¥à¤·à¥€à¤¯ साइन इन</translation>
<translation id="3827112369919217609">निरपेकà¥à¤·</translation>
<translation id="3827666161959873541">पारिवारिक चलâ€à¤šà¤¿à¤¤à¥à¤°à¤¹à¤°à¥‚</translation>
<translation id="3828924085048779000">खाली पासफà¥à¤°à¥‡à¤œà¤•à¥‹ लागि अनà¥à¤®à¤¤à¤¿ छैन।</translation>
@@ -1065,9 +1070,9 @@
<translation id="3858027520442213535">मिति र समय अपडेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3858860766373142691">नाम</translation>
<translation id="3872834068356954457">विजà¥à¤žà¤¾à¤¨</translation>
+<translation id="3875783148670536197">यसो गरà¥à¤¨à¥‡ तरिका देखाइयोसà¥</translation>
<translation id="3881478300875776315">कम हरफ देखाउनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3884278016824448484">विभेदातà¥à¤®à¤• यनà¥à¤¤à¥à¤° पहिचानकरà¥à¤¤à¤¾</translation>
-<translation id="3885155851504623709">पà¥à¤¯à¤¾à¤°à¤¿à¤¸</translation>
<translation id="388632593194507180">निगरानी भइरहेको कà¥à¤°à¤¾ पतà¥à¤¤à¤¾ लागà¥à¤¯à¥‹</translation>
<translation id="3886948180919384617">सà¥à¤Ÿà¥à¤¯à¤¾à¤•à¤° ३</translation>
<translation id="3890664840433101773">इमेल थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
@@ -1097,9 +1102,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> लाई बà¥à¤²à¤• गरिà¤à¤•à¥‹ छ।</translation>
<translation id="3978338123949022456">सरà¥à¤š मोड, <ph name="KEYWORD_SUFFIX" /> मारà¥à¤«à¤¤ कà¥à¤µà¥‡à¤°à¥€ खोजà¥à¤¨ उकà¥à¤¤ कà¥à¤µà¥‡à¤°à¥€ टाइप गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ र इनà¥à¤Ÿà¤° थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="398470910934384994">चराहरू</translation>
+<translation id="3985750352229496475">ठेगानाहरू वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥...</translation>
<translation id="3986705137476756801">अहिलेका लागि लाइभ कà¥à¤¯à¤¾à¤ªà¥à¤¸à¤¨ अफ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3987940399970879459">१ à¤à¤®.बि. भनà¥à¤¦à¤¾ कम</translation>
<translation id="3990250421422698716">जग अफसेट</translation>
+<translation id="3992684624889376114">यो पेजका बारेमा</translation>
<translation id="3996311196211510766">यो साइट <ph name="ORIGIN" /> ले यसलाई गरिने सबै अनà¥à¤°à¥‹à¤§à¤¹à¤°à¥‚मा मूल नीति
लागू गरà¥à¤¨ अनà¥à¤°à¥‹à¤§ गरेको छ। तर अहिले यो नीति लागू गरà¥à¤¨ सकिà¤à¤¦à¥ˆà¤¨à¥¤</translation>
<translation id="4006465311664329701">Google Pay पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¦à¤¾ इमà¥à¤ªà¥‹à¤°à¥à¤Ÿ गरिने भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ विधि, अफर र ठेगानाहरू</translation>
@@ -1223,6 +1230,7 @@
<translation id="4305666528087210886">तपाईंको फाइल हेरà¥à¤¨ र पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨ सकिà¤à¤¨</translation>
<translation id="4306529830550717874">ठेगाना सेभ गरà¥à¤¨à¥‡ हो?</translation>
<translation id="4306812610847412719">कà¥à¤²à¤¿à¤ªà¤¬à¥‹à¤°à¥à¤¡</translation>
+<translation id="4310070645992025887">Journeys खोजà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">रोक लगाउनà¥à¤¹à¥‹à¤¸à¥ (डिफलà¥à¤Ÿ मान)</translation>
<translation id="4314815835985389558">सिंक वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
@@ -1273,6 +1281,7 @@
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">पà¥à¤°à¥‹à¤•à¥à¤¸à¥€à¤•à¥‹ पà¥à¤°à¤¯à¥‹à¤— असकà¥à¤·à¤® गरिà¤à¤•à¥‹ छ तर à¤à¤• सà¥à¤¸à¥à¤ªà¤·à¥à¤Ÿ पà¥à¤°à¥‹à¤•à¥à¤¸à¥€ विनà¥à¤¯à¤¾à¤¸ तोकिà¤à¤•à¥‹ छ।</translation>
<translation id="4441832193888514600">कà¥à¤²à¤¾à¤‰à¤¡à¤•à¤¾ पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤²à¥‡ मातà¥à¤° नीति तय गरà¥à¤¨ मिलà¥à¤¨à¥‡ भà¤à¤•à¤¾à¤²à¥‡ वेवासà¥à¤¤à¤¾ गरिà¤à¤•à¥‹ छ।</translation>
+<translation id="4442470707340296952">Chrome का टà¥à¤¯à¤¾à¤¬à¤¹à¤°à¥‚</translation>
<translation id="4450893287417543264">फेरि नदेखाउनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="4451135742916150903">HID डिभाइसमा कनेकà¥à¤Ÿ गरà¥à¤¨à¥‡ अनà¥à¤®à¤¤à¤¿ मागà¥à¤¨ सकà¥à¤›</translation>
<translation id="4452328064229197696">तपाईंले भरà¥à¤–रै पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ पासवरà¥à¤¡ चोरी भà¤à¤•à¥‹ डेटाको सङà¥à¤—à¥à¤°à¤¹à¤®à¤¾ भेटिà¤à¤•à¥‹ छ। Google पासवरà¥à¤¡ मà¥à¤¯à¤¾à¤¨à¥‡à¤œà¤°à¤²à¥‡ तपाईंका खाता सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ राखà¥à¤¨à¥‡ पà¥à¤°à¤¯à¥‹à¤œà¤¨à¤•à¤¾ लागि तपाईंले सेभ गरेका पासवरà¥à¤¡à¤¹à¤°à¥‚ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ छनॠकि छैननॠजाà¤à¤šà¥à¤¨ सिफारिस गरà¥à¤›à¥¤</translation>
@@ -1411,6 +1420,7 @@
<translation id="483241715238664915">चेतावनी देखाउने सà¥à¤µà¤¿à¤§à¤¾ अन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="4834250788637067901">Google Pay पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¦à¤¾ इमà¥à¤ªà¥‹à¤°à¥à¤Ÿ गरिने भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ विधि, अफर र ठेगानाहरू</translation>
<translation id="4838327282952368871">सà¥à¤µà¤ªà¥à¤¨à¤¿à¤²</translation>
+<translation id="4839087176073128681">अरà¥à¤•à¥‹ पटक छिटो भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ र Google को सूचना पà¥à¤°à¤µà¤¿à¤§à¤¿ उदà¥à¤¯à¥‹à¤—कै सरà¥à¤µà¥‹à¤¤à¥à¤•à¥ƒà¤·à¥à¤Ÿ सà¥à¤°à¤•à¥à¤·à¤¾ उपाय अपनाà¤à¤° आफà¥à¤¨à¥‹ कारà¥à¤¡ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ राखà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤</translation>
<translation id="4840250757394056958">Chrome को बà¥à¤°à¤¾à¤‰à¤œà¤¿à¤™Â à¤‡à¤¤à¤¿à¤¹à¤¾à¤¸ हेरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="484462545196658690">सà¥à¤µà¤¤à¤ƒ</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> र अनà¥à¤¯ सà¥à¤Ÿà¥‹à¤°à¤¹à¤°à¥‚मा छà¥à¤Ÿ पà¥à¤°à¤¾à¤ªà¥à¤¤ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
@@ -1473,6 +1483,7 @@
<translation id="5011561501798487822">पतà¥à¤¤à¤¾ लागेको भाषा</translation>
<translation id="5015510746216210676">मेसिनको नाम:</translation>
<translation id="5017554619425969104">तपाईंले पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ पाठ</translation>
+<translation id="5017828934289857214">मलाई पछि सà¥à¤®à¤°à¤£ गराइयोसà¥</translation>
<translation id="5018422839182700155">यो पृषà¥à¤  खोलà¥à¤¨ सकिà¤à¤¦à¥ˆà¤¨</translation>
<translation id="5019198164206649151">खराब सà¥à¤¥à¤¿à¤¤à¤¿à¤®à¤¾ भणà¥à¤¡à¤¾à¤° बà¥à¤¯à¤¾à¤•à¤¿à¤™ गरà¥à¤¦à¥ˆ</translation>
<translation id="5020776957610079374">विशà¥à¤µ सङà¥à¤—ीत</translation>
@@ -1492,6 +1503,7 @@
<translation id="5051305769747448211">लाइभ कमेडी</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{नजिकैका डिभाइससà¤à¤— सेयर गरà¥à¤¨à¥‡ सà¥à¤µà¤¿à¤§à¤¾ पà¥à¤°à¤¯à¥‹à¤— गरी यो फाइल पठाउन आफà¥à¤¨à¥‹ डिभाइसको भणà¥à¤¡à¤¾à¤°à¤£à¤®à¤¾ ठाउठ(<ph name="DISK_SPACE_SIZE" />) खाली गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥}other{नजिकैका डिभाइससà¤à¤— सेयर गरà¥à¤¨à¥‡ सà¥à¤µà¤¿à¤§à¤¾ पà¥à¤°à¤¯à¥‹à¤— गरी यी फाइलहरू पठाउन आफà¥à¤¨à¥‹ डिभाइसको भणà¥à¤¡à¤¾à¤°à¤£à¤®à¤¾ ठाउठ(<ph name="DISK_SPACE_SIZE" />) खाली गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥}}</translation>
<translation id="5056549851600133418">तपाईà¤à¤•à¤¾ लागि लेखहरू</translation>
+<translation id="5060483733937416656">तपाईंले <ph name="PROVIDER_ORIGIN" /> पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥‡ वेबसाइटमा Windows Hello मारà¥à¤«à¤¤ पà¥à¤·à¥à¤Ÿà¤¿ गरà¥à¤¨à¥‡ विकलà¥à¤ª छनौट गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ छ। यो पà¥à¤°à¤¦à¤¾à¤¯à¤•à¤²à¥‡ तपाईंको भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ विधिसमà¥à¤¬à¤¨à¥à¤§à¥€ जानकारी भणà¥à¤¡à¤¾à¤°à¤£ गरेको हà¥à¤¨ सकà¥à¤›, तपाईं सो जानकारी <ph name="LINK_TEXT" /> गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›à¥¤</translation>
<translation id="5061227663725596739">तपाईंले <ph name="LOOKALIKE_DOMAIN" /> भनà¥à¤¨ चाहनà¥à¤­à¤à¤•à¥‹ हो?</translation>
<translation id="5066056036849835175">विगतमा पà¥à¤°à¤¿à¤¨à¥à¤Ÿ गरिà¤à¤•à¤¾ कà¥à¤°à¤¾à¤¹à¤°à¥‚</translation>
<translation id="5068234115460527047">हेज फनà¥à¤¡à¤¹à¤°à¥‚</translation>
@@ -1505,10 +1517,8 @@
<translation id="5087286274860437796">यो समयमा सरà¥à¤­à¤°à¤•à¥‹ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° मानà¥à¤¯ छैन।</translation>
<translation id="5087580092889165836">कारà¥à¤¡ थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="5088142053160410913">अपरेटरलाई पठाइà¤à¤•à¥‹ सनà¥à¤¦à¥‡à¤¶</translation>
-<translation id="5089810972385038852">राजà¥à¤¯</translation>
<translation id="5093232627742069661">जेड फोलà¥à¤¡</translation>
<translation id="5094747076828555589">यो सरà¥à¤­à¤° हो भनेर पà¥à¤°à¤®à¤¾à¤£à¤¿à¤¤ गरà¥à¤¨ सकेन <ph name="DOMAIN" />; यसको सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤°à¤²à¤¾à¤ˆ Chromium ले विशà¥à¤µà¤¾à¤¸ गरेन। यो à¤à¤• गलत कनà¥à¤«à¤¿à¤—à¥à¤°à¥‡à¤¸à¤¨ वा तपाईंको जडान अवरोध गरà¥à¤¨ खोजà¥à¤¨à¥‡ आकà¥à¤°à¤®à¤£à¤•à¤¾à¤°à¥€à¤•à¥‹ हà¥à¤¨à¤¸à¤•à¥à¤›à¥¤</translation>
-<translation id="5095208057601539847">पà¥à¤°à¤¦à¥‡à¤¶</translation>
<translation id="5097099694988056070">CPU/RAM को पà¥à¤°à¤¯à¥‹à¤— जसà¥à¤¤à¤¾ यनà¥à¤¤à¥à¤°à¤¸à¤®à¥à¤¬à¤¨à¥à¤§à¥€ तथà¥à¤¯à¤¾à¤™à¥à¤•</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">यो साइट सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ छैन</translation>
@@ -1546,6 +1556,7 @@
<translation id="5171045022955879922">यà¥à¤†à¤°à¤à¤² खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ वा टाइप गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">यनà¥à¤¤à¥à¤°</translation>
+<translation id="5177076414499237632">यो पेजका सà¥à¤°à¥‹à¤¤ र विषयका बारेमा जानà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> मा छैन? यो तà¥à¤°à¥à¤Ÿà¤¿ रिपोरà¥à¤Ÿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="518639307526414276">पालà¥à¤¤à¥ जनावरको खाना तथा हेरचाहका सामगà¥à¤°à¥€</translation>
<translation id="5190835502935405962">पृषà¥à¤ à¤®à¤žà¥à¤œà¥‚षा पटà¥à¤Ÿà¥€</translation>
@@ -1706,6 +1717,7 @@
<translation id="5624120631404540903">पासवरà¥à¤¡à¤¹à¤°à¥‚ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="5629630648637658800">नीति सेटिङहरू लोड गरà¥à¤¨ असफल</translation>
<translation id="5631439013527180824">अवैध यनà¥à¤¤à¥à¤° वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ टोकन</translation>
+<translation id="5632485077360054581">यसो गरà¥à¤¨à¥‡ तरिका देखाइयोसà¥</translation>
<translation id="5633066919399395251">हाल <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> मा रहेका आकà¥à¤°à¤®à¤£à¤•à¤¾à¤°à¥€à¤¹à¤°à¥‚ले तपाईंको कमà¥à¤ªà¥à¤¯à¥à¤Ÿà¤°à¤®à¤¾ तपाईंको जानकारी (उदाहरणका लागि तसà¥à¤¬à¤¿à¤°, पासवरà¥à¤¡, सनà¥à¤¦à¥‡à¤¶ र कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡à¤¹à¤°à¥‚) चोरà¥à¤¨à¥‡ वा मेटà¥à¤¨à¤¸à¤•à¥à¤¨à¥‡ खतरनाक कारà¥à¤¯à¤•à¥à¤°à¤®à¤¹à¤°à¥‚ सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ गरà¥à¤¨à¥‡ पà¥à¤°à¤¯à¤¾à¤¸ गरà¥à¤¨à¤¸à¤•à¥à¤›à¤¨à¥à¥¤ <ph name="BEGIN_LEARN_MORE_LINK" />थप जानà¥à¤¨à¥à¤¹à¥‹à¤¸à¥<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">भà¥à¤°à¤¾à¤®à¤• सामगà¥à¤°à¥€à¤®à¤¾à¤¥à¤¿ रोक लगाइयो।</translation>
<translation id="5633259641094592098">पà¥à¤°à¤¶à¤‚सकको जमात भà¤à¤•à¤¾ तथा इनà¥à¤¡à¥€ चलचितà¥à¤°à¤¹à¤°à¥‚</translation>
@@ -1823,6 +1835,7 @@
<translation id="5989320800837274978">न त निशà¥à¤šà¤¿à¤¤ पà¥à¤°à¥‹à¤•à¥à¤¸à¥€ सरà¥à¤­à¤°à¤¹à¤°à¥‚ न त .पà¥à¤¯à¤¾à¤• सà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ यà¥à¤†à¤°à¤à¤²à¤¹à¤°à¥‚ तोकिà¤à¤•à¤¾ छनà¥à¥¤</translation>
<translation id="5992691462791905444">इनà¥à¤œà¤¿à¤¨à¤¿à¤¯à¤°à¤¿à¤™ जेड फोलà¥à¤¡</translation>
<translation id="5995727681868049093">आफà¥à¤¨à¥‹ Google खातामा गई तपाईंको जानकारी, गोपनीयता र सà¥à¤°à¤•à¥à¤·à¤¾à¤•à¥‹ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="5997247540087773573">तपाईंले भरà¥à¤–रै पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ पासवरà¥à¤¡ चोरी भà¤à¤•à¥‹ डेटाको सङà¥à¤—à¥à¤°à¤¹à¤®à¤¾ भेटिà¤à¤•à¥‹ छ। Google पासवरà¥à¤¡ मà¥à¤¯à¤¾à¤¨à¥‡à¤œà¤°à¤²à¥‡ तपाईंका खाता सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ राखà¥à¤¨à¥‡ पà¥à¤°à¤¯à¥‹à¤œà¤¨à¤•à¤¾ लागि अहिले नै यो पासवरà¥à¤¡ बदलà¥à¤¨ र तपाईंले सेभ गरेका पासवरà¥à¤¡à¤¹à¤°à¥‚ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ छनॠकि छैननॠभनी जाà¤à¤šà¥à¤¨ सिफारिस गरà¥à¤›à¥¤</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' मधà¥à¤¯à¥‡ <ph name="RESULT_COUNT" /></translation>
<translation id="6006484371116297560">कà¥à¤²à¤¾à¤¸à¤¿à¤•</translation>
<translation id="6008122969617370890">N देखि १ समà¥à¤®à¤•à¥‹ कà¥à¤°à¤®</translation>
@@ -1918,7 +1931,6 @@
<translation id="627746635834430766">अरà¥à¤•à¥‹ पटक अठछिटो भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ गरà¥à¤¨ आफà¥à¤¨à¥‹ Google खातामा आफà¥à¤¨à¥‹ कारà¥à¤¡ र बिलिङ ठेगाना सेभ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤</translation>
<translation id="6279183038361895380">तपाईंको करà¥à¤¸à¤° देखाउन |<ph name="ACCELERATOR" />| लाई थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="6280223929691119688">यो ठेगानामा डेलिभर गरà¥à¤¨ सकिà¤à¤¦à¥ˆà¤¨à¥¤ कà¥à¤¨à¥ˆ अरà¥à¤•à¥‹ ठेगाना चयन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤</translation>
-<translation id="6282194474023008486">हà¥à¤²à¤¾à¤• कोड</translation>
<translation id="6285507000506177184">Chrome मा डाउनलोडहरू वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥, तपाईं आफूले Chrome मा डाउनलोड गरेका फाइल वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨ चाहनà¥à¤¹à¥à¤¨à¥à¤› भने Enter थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="6289939620939689042">पृषà¥à¤ à¤•à¥‹ रङ</translation>
<translation id="6290238015253830360">तपाईंका सà¥à¤à¤¾à¤µ गरिà¤à¤•à¤¾ लेखहरू यहाठदेखिनà¥à¤›à¤¨à¥</translation>
@@ -1940,6 +1952,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> भनà¥à¤¦à¤¾ कम ठाउठखाली गरà¥à¤¦à¤›à¥¤ तपाईं अरà¥à¤•à¥‹à¤ªà¤Ÿà¤• आउà¤à¤¦à¤¾ केही साइटहरू अठढिलोगरी लोड हà¥à¤¨à¤¸à¤•à¥à¤›à¤¨à¥à¥¤</translation>
<translation id="6337534724793800597">नामदà¥à¤µà¤¾à¤°à¤¾ नीतिहरू फिलà¥à¤Ÿà¤° गरà¥à¤›</translation>
<translation id="6340739886198108203">à¤à¤¡à¥à¤®à¤¿à¤¨à¤²à¥‡ तोकेका नीतिअनà¥à¤¸à¤¾à¤° सà¥à¤•à¥à¤°à¤¿à¤¨à¤®à¤¾ गोपà¥à¤¯ सामगà¥à¤°à¥€ देखिà¤à¤•à¤¾ बेला सà¥à¤•à¥à¤°à¤¿à¤¨à¤¸à¤Ÿ लिन वा सà¥à¤•à¥à¤°à¤¿à¤¨ रेकरà¥à¤¡ गरà¥à¤¨ सिफारिस गरिà¤à¤¦à¥ˆà¤¨:</translation>
+<translation id="6348220984832452017">पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨ मिलà¥à¤¨à¥‡ संसà¥à¤•à¤°à¤£à¤¹à¤°à¥‚</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{कà¥à¤¨à¥ˆ पनि होइन}=1{<ph name="DOMAIN_LIST" /> को १ पासवरà¥à¤¡ (सिंक गरिà¤à¤•à¥‹ छ)}=2{<ph name="DOMAIN_LIST" /> का २ पासवरà¥à¤¡à¤¹à¤°à¥‚ (सिंक गरिà¤à¤•à¤¾ छनà¥)}other{<ph name="DOMAIN_LIST" /> का # पासवरà¥à¤¡à¤¹à¤°à¥‚ (सिंक गरिà¤à¤•à¤¾ छनà¥)}}</translation>
<translation id="6355392890578844978">कà¥à¤¨à¥ˆ कमà¥à¤ªà¤¨à¥€ वा अनà¥à¤¯ सङà¥à¤—ठनले यो बà¥à¤°à¤¾à¤‰à¤œà¤° वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¦à¥ˆà¤¨à¥¤ यो डिभाइसमा गरिà¤à¤•à¤¾ कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª Chromium बाहिरबाट वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरिन सकà¥à¤›à¥¤ <ph name="BEGIN_LINK" />थप जानà¥à¤¨à¥à¤¹à¥‹à¤¸à¥<ph name="END_LINK" /></translation>
@@ -1971,7 +1984,6 @@
<translation id="643051589346665201">Google खाताको पासवरà¥à¤¡ बदलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="6433490469411711332">समà¥à¤ªà¤°à¥à¤• समà¥à¤¬à¤¨à¥à¤§à¥€ जानकारीलाई समà¥à¤ªà¤¾à¤¦à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ले जडान गरà¥à¤¨ असà¥à¤µà¥€à¤•à¤¾à¤° गरà¥à¤¯à¥‹à¥¤</translation>
-<translation id="6438025220577812695">म आफैठबदलà¥à¤¨ चाहनà¥à¤›à¥</translation>
<translation id="6440503408713884761">उपेकà¥à¤·à¤¿à¤¤</translation>
<translation id="6443406338865242315">तपाईंले सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ गरà¥à¤¨à¥à¤­à¤à¤•à¤¾ विसà¥à¤¤à¤¾à¤° र पà¥à¤²à¤—इनहरू</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2101,9 +2113,9 @@
<translation id="6828866289116430505">आनà¥à¤µà¤‚शिक विजà¥à¤žà¤¾à¤¨</translation>
<translation id="6831043979455480757">अनà¥à¤µà¤¾à¤¦ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="6833752742582340615">तपाईं सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रूपमा अठछिटो चेक आउट गरà¥à¤¨ चाहनà¥à¤¹à¥à¤¨à¥à¤› भने Google खातामा आफà¥à¤¨à¥‹ कारà¥à¤¡ तथा बिलिङसमà¥à¤¬à¤¨à¥à¤§à¥€ जानकारी सेभ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
-<translation id="6839929833149231406">कà¥à¤·à¥‡à¤¤à¥à¤°</translation>
<translation id="6846340164947227603">कà¥à¤¨à¥ˆ भरà¥à¤šà¥à¤…ल कारà¥à¤¡ नमà¥à¤¬à¤° पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥...</translation>
<translation id="6852204201400771460">à¤à¤ª पà¥à¤¨à¤ƒ लोड गरà¥à¤¨à¥‡ हो?</translation>
+<translation id="6857776781123259569">पासवरà¥à¤¡à¤¹à¤°à¥‚ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥...</translation>
<translation id="686485648936420384">उपभोकà¥à¤¤à¤¾ सà¥à¤°à¥‹à¤¤à¤¸à¤¾à¤§à¤¨</translation>
<translation id="6865412394715372076">यो कारà¥à¤¡ यस बखत पà¥à¤·à¥à¤Ÿà¤¿ गरà¥à¤¨ सकिà¤à¤¦à¥ˆà¤¨</translation>
<translation id="6869334554832814367">वà¥à¤¯à¤•à¥à¤¤à¤¿à¤—त करà¥à¤œà¤¾</translation>
@@ -2152,7 +2164,6 @@
<translation id="6965978654500191972">यनà¥à¤¤à¥à¤°</translation>
<translation id="696703987787944103">बोधातà¥à¤®à¤•</translation>
<translation id="6968269510885595029">आफà¥à¤¨à¥‹ सà¥à¤°à¤•à¥à¤·à¤¾ साà¤à¤šà¥‹ पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
-<translation id="6970216967273061347">जिलà¥à¤²à¤¾</translation>
<translation id="6971439137020188025">Google Slides पà¥à¤°à¤¯à¥‹à¤— गरी तà¥à¤°à¥à¤¨à¥à¤¤à¥ˆ नयाठपà¥à¤°à¥‡à¤œà¥‡à¤¨à¥à¤Ÿà¥‡à¤¸à¤¨ बनाउनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="6972629891077993081">HID यनà¥à¤¤à¥à¤°à¤¹à¤°à¥‚</translation>
<translation id="6973656660372572881">दà¥à¤µà¥ˆ तय पà¥à¤°à¥‹à¤•à¥à¤¸à¥€ सरà¥à¤­à¤° र à¤à¤• .Pac सà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ यूआरà¤à¤² निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ गरिà¤à¤•à¥‹ छ।</translation>
@@ -2191,7 +2202,6 @@
<translation id="7081308185095828845">तपाईंको डिभाइसमा यो सà¥à¤µà¤¿à¤§à¤¾ उपलबà¥à¤§ छैन</translation>
<translation id="7083258188081898530">टà¥à¤°à¥‡ ९</translation>
<translation id="7086090958708083563">पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤²à¥‡ अनà¥à¤°à¥‹à¤§ गरेको अपलोड</translation>
-<translation id="7087282848513945231">काउनà¥à¤Ÿà¥€</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome का सेटिङमा गई अनà¥à¤®à¤¤à¤¿ तथा विभिनà¥à¤¨ साइटहरूमा भणà¥à¤¡à¤¾à¤°à¤£ गरिà¤à¤•à¤¾ डेटा वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨ Tab थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ अनि Enter थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="7096937462164235847">यो वेबसाइटको पहिचान पà¥à¤·à¥à¤Ÿà¤¿ गरिà¤à¤•à¥‹ छैन।</translation>
<translation id="7101893872976785596">तरà¥à¤¸à¤¾à¤‰à¤¨à¥‡ चलचितà¥à¤°à¤¹à¤°à¥‚</translation>
@@ -2210,10 +2220,10 @@
<ph name="LIST_ITEM" />फाराममा भरिà¤à¤•à¥‹ जानकारी<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">यो ठेगानामा ढà¥à¤µà¤¾à¤¨à¥€ गरà¥à¤¨ सकिà¤à¤¦à¥ˆà¤¨à¥¤ कà¥à¤¨à¥ˆ अरà¥à¤•à¥‹ ठेगाना चयन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">तपाईंका à¤à¤¡à¥à¤®à¤¿à¤¨à¤²à¥‡ लगाà¤à¤•à¥‹ पà¥à¤°à¤¤à¤¿à¤¬à¤¨à¥à¤§à¤•à¤¾ कारण यो डेटा कपी गरà¥à¤¨ मिलà¥à¤¦à¥ˆà¤¨à¥¤</translation>
<translation id="7135130955892390533">सà¥à¤¥à¤¿à¤¤à¤¿ देखाउनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="7138472120740807366">डेलिभरीको विधि</translation>
-<translation id="7139724024395191329">इमिरेट</translation>
<translation id="7139892792842608322">मà¥à¤–à¥à¤¯ टà¥à¤°à¥‡</translation>
<translation id="714064300541049402">सतह २ मा रहेको छविको X सिफà¥à¤Ÿ</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2430,6 +2440,7 @@
<translation id="7669271284792375604">यो साइटमा रहेका आकà¥à¤°à¤®à¤£à¤•à¤¾à¤°à¥€à¤¹à¤°à¥‚ले तपाईंलाई à¤à¥à¤•à¥à¤¯à¤¾à¤à¤° (उदाहरणका लागि तपाईंको गृहपृषà¥à¤  परिवरà¥à¤¤à¤¨ गरेर वा तपाईं जाने साइटहरूमा अतिरिकà¥à¤¤ विजà¥à¤žà¤¾à¤ªà¤¨à¤¹à¤°à¥‚ देखाà¤à¤°) तपाईंको बà¥à¤°à¤¾à¤‰à¤œà¤¿à¤™à¤•à¥‹ अनà¥à¤­à¤µà¤²à¤¾à¤ˆ हानि पà¥à¤°à¥â€à¤¯à¤¾à¤‰à¤¨à¥‡ पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤®à¤¹à¤°à¥‚ सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ गरà¥à¤¨ लगाउने पà¥à¤°à¤¯à¤¾à¤¸ गरà¥à¤¨ सकà¥à¤›à¤¨à¥à¥¤</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{गोपà¥à¤¯ डेटाका रूपमा फà¥à¤²à¥à¤¯à¤¾à¤— गरिà¤à¤•à¥‹ डेटा पà¥à¤°à¤¯à¥‹à¤— गरी गरिà¤à¤•à¤¾ कारबाहीहरू (लग इन गरेदेखि à¤à¤‰à¤Ÿà¤¾ कारबाही गरिà¤à¤•à¥‹ छ)। <ph name="BEGIN_LINK" />थप जानà¥à¤¨à¥à¤¹à¥‹à¤¸à¥<ph name="END_LINK" />}other{गोपà¥à¤¯ डेटाका रूपमा फà¥à¤²à¥à¤¯à¤¾à¤— गरिà¤à¤•à¥‹ डेटा पà¥à¤°à¤¯à¥‹à¤— गरी गरिà¤à¤•à¤¾ कारबाहीहरू (लग इन गरेदेखि # वटा कारबाही गरिà¤à¤•à¤¾ छनà¥)। <ph name="BEGIN_LINK" />थप जानà¥à¤¨à¥à¤¹à¥‹à¤¸à¥<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">मेलबकà¥à¤¸ ६</translation>
+<translation id="7675325315208090829">भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ विधिहरू वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥...</translation>
<translation id="7676643023259824263">कà¥à¤²à¤¿à¤ªà¤¬à¥‹à¤°à¥à¤¡à¤¸à¤®à¥à¤¬à¤¨à¥à¤§à¥€ पाठ <ph name="TEXT" /> को खोजी गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="7679367271685653708">Chrome का सेटिङमा गई आफà¥à¤¨à¥‹ बà¥à¤°à¤¾à¤‰à¤œà¤¿à¤™ इतिहास हेरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ र वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="7679947978757153706">बेसबल</translation>
@@ -2472,7 +2483,6 @@
<translation id="7766518757692125295">सà¥à¤•à¤°à¥à¤Ÿ</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">उही कà¥à¤°à¤® तर माथितिर फरà¥à¤•à¤¾à¤à¤°</translation>
-<translation id="777702478322588152">पà¥à¤°à¤¶à¤¾à¤¸à¤•à¤¿à¤¯ कà¥à¤·à¥‡à¤¤à¥à¤°</translation>
<translation id="7791011319128895129">अपà¥à¤°à¤•à¤¾à¤¶à¤¿à¤¤</translation>
<translation id="7791196057686275387">गाà¤à¤ à¥‹</translation>
<translation id="7791543448312431591">थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
@@ -2563,12 +2573,12 @@
<translation id="8055534648776115597">वà¥à¤¯à¤¾à¤µà¤¸à¤¾à¤¯à¤¿à¤• तथा निरनà¥à¤¤à¤° शिकà¥à¤·à¤¾</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" सही तरिकाले कनà¥à¤«à¤¿à¤—र गरिà¤à¤•à¥‹ छैन। सामानà¥à¤¯à¤¤à¤¯à¤¾ "<ph name="SOFTWARE_NAME" />" को सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ रदà¥à¤¦ गरेमा समसà¥à¤¯à¤¾à¤•à¥‹ समाधान हà¥à¤¨à¥à¤›à¥¤ <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">खादà¥à¤¯à¤µà¤¸à¥à¤¤à¥à¤•à¥‹ उतà¥à¤ªà¤¾à¤¦à¤¨</translation>
-<translation id="8066955247577885446">माफ गरà¥à¤¨à¥à¤¹à¥‹à¤²à¤¾, केही चिज गडबड भयो।</translation>
<translation id="8067872629359326442">तपाईंले भरà¥à¤–रै कà¥à¤¨à¥ˆ भà¥à¤°à¤¾à¤®à¤• साइटमा आफà¥à¤¨à¥‹ पासवरà¥à¤¡ पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿ गरà¥à¤¨à¥à¤­à¤à¤•à¥‹ छ। Chromium ले मदà¥à¤¦à¤¤ गरà¥à¤¨ सकà¥à¤›à¥¤ आफà¥à¤¨à¥‹ पासवरà¥à¤¡ परिवरà¥à¤¤à¤¨ गरà¥à¤¨ र आफà¥à¤¨à¥‹ खाता जोखिममा हà¥à¤¨ सकà¥à¤› भनेर Google लाई सूचित गरà¥à¤¨ खाता सेभ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ नामक विकलà¥à¤ªà¤®à¤¾ कà¥à¤²à¤¿à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤</translation>
<translation id="8070439594494267500">à¤à¤ªà¤•à¥‹ आइकन</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Google Sheets पà¥à¤°à¤¯à¥‹à¤— गरी तà¥à¤°à¥à¤¨à¥à¤¤à¥ˆ नयाठपाना बनाउनà¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="8075898834294118863">साइटसमà¥à¤¬à¤¨à¥à¤§à¥€ सेटिङ मिलाउनà¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="8076492880354921740">टà¥à¤¯à¤¾à¤¬à¤¹à¤°à¥‚</translation>
<translation id="8078141288243656252">घà¥à¤®à¤¾à¤‡à¤à¤•à¤¾ बेला à¤à¤¨à¥‹à¤Ÿà¥‡à¤¸à¤¨ गरà¥à¤¨ मिलà¥à¤¦à¥ˆà¤¨</translation>
<translation id="8079031581361219619">साइट पà¥à¤¨à¤ƒ लोड गरà¥à¤¨à¥‡ हो?</translation>
<translation id="8081087320434522107">सेडान कारहरू</translation>
@@ -2691,6 +2701,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">सेटिङहरू</translation>
+<translation id="8428634594422941299">बà¥à¤à¥‡à¤</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome का सेटिङमा गई कà¥à¤•à¥€à¤¸à¤®à¥à¤¬à¤¨à¥à¤§à¥€ आफà¥à¤¨à¤¾ पà¥à¤°à¤¾à¤¥à¤®à¤¿à¤•à¤¤à¤¾à¤¹à¤°à¥‚ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨ Tab थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ अनि Enter थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="8433057134996913067">यसले तपाईà¤à¤²à¤¾à¤ˆ अधिकांश साइटहरूबाट साइन आउट गराउनेछ।</translation>
<translation id="8434840396568290395">पालà¥à¤¤à¥ जनावरहरू</translation>
@@ -2788,6 +2799,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> का लागि तपाईंको कोड <ph name="ONE_TIME_CODE" /> हो</translation>
<translation id="874918643257405732">यो टà¥à¤¯à¤¾à¤¬ बà¥à¤•à¤®à¤¾à¤°à¥à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="8751426954251315517">कृपया अरà¥à¤•à¥‹ पटक फेरि पà¥à¤°à¤¯à¤¾à¤¸ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="8757526089434340176">Google Pay को अफर उपलबà¥à¤§ छ</translation>
<translation id="8758885506338294482">पà¥à¤°à¤¤à¤¿à¤¸à¥à¤ªà¤°à¥à¤§à¤¾à¤¤à¥à¤®à¤• भिडियो गेमिङ</translation>
<translation id="8759274551635299824">यो कारà¥à¤¡à¤•à¥‹ मà¥à¤¯à¤¾à¤¦ सकिà¤à¤•à¥‹ छ</translation>
<translation id="87601671197631245">यो साइटले à¤à¤‰à¤Ÿà¤¾ अदà¥à¤¯à¤¾à¤µà¤§à¤¿à¤• नभà¤à¤•à¥‹ सà¥à¤°à¤•à¥à¤·à¤¾ कनà¥à¤«à¤¿à¤—à¥à¤°à¥‡à¤¸à¤¨à¤•à¥‹ पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤› जसले यस साइटमा तपाईंको जानकारी (उदाहरणका लागि, पासवरà¥à¤¡, सनà¥à¤¦à¥‡à¤¶ वा कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) पठाइà¤à¤¦à¤¾ देखाउन सकà¥à¤›à¥¤</translation>
@@ -2795,6 +2807,7 @@
<translation id="8763927697961133303">USB यनà¥à¤¤à¥à¤°</translation>
<translation id="8763986294015493060">हाल खोलिà¤à¤•à¤¾ सबै इनà¥à¤•à¥‹à¤—à¥à¤¨à¤¿à¤Ÿà¥‹ विनà¥à¤¡à¥‹à¤¹à¤°à¥‚ बनà¥à¤¦ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="8766943070169463815">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ तरिकाले भà¥à¤•à¥à¤¤à¤¾à¤¨à¥€ गरà¥à¤¨à¤•à¤¾ निमà¥à¤¤à¤¿ कà¥à¤°à¤¿à¤¡à¥‡à¤¨à¥à¤¸à¤¿à¤¯à¤² पà¥à¤·à¥à¤Ÿà¤¿ गरà¥à¤¨à¥‡ पाना खà¥à¤²à¤¾ छ</translation>
+<translation id="8767765348545497220">मदà¥à¤¦à¤¤à¤¸à¤®à¥à¤¬à¤¨à¥à¤§à¥€ बबल बनà¥à¤¦ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">मोटरसाइकलहरू</translation>
<translation id="8790007591277257123">मेटाउने कारà¥à¤¯ &amp;पà¥à¤¨: गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
@@ -2807,6 +2820,7 @@
<translation id="8806285662264631610">नà¥à¤¹à¤¾à¤‰à¤¨à¥‡ तथा शरीरमा लगाइने उतà¥à¤ªà¤¾à¤¦à¤¨à¤¹à¤°à¥‚</translation>
<translation id="8807160976559152894">हरेक पृषà¥à¤ à¤ªà¤›à¤¿ छाà¤à¤Ÿà¤•à¤¾à¤à¤Ÿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="8808828119384186784">Chrome का सेटिङ</translation>
+<translation id="8813277370772331957">मलाई पछि समà¥à¤à¤¾à¤‰à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, आफà¥à¤¨à¥‹ Chrome का सेटिङमा गई Chrome अपडेट गरà¥à¤¨ Tab थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ अनि Enter थिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="8820817407110198400">बà¥à¤•à¤®à¤¾à¤°à¥à¤•à¤¹à¤°à¥‚</translation>
<translation id="882338992931677877">मà¥à¤¯à¤¾à¤¨à¥à¤…ल सà¥à¤²à¤Ÿ</translation>
@@ -2986,6 +3000,7 @@
<translation id="988159990683914416">विकासकरà¥à¤¤à¤¾à¤¦à¥à¤µà¤¾à¤°à¤¾ निरà¥à¤®à¤¿à¤¤</translation>
<translation id="989988560359834682">इमेल ठेगाना</translation>
<translation id="991413375315957741">मोसन सेनà¥à¤¸à¤° वा पà¥à¤°à¤•à¤¾à¤¶à¤¸à¤®à¥à¤¬à¤¨à¥à¤§à¥€ सेनà¥à¤¸à¤°à¤¹à¤°à¥‚</translation>
+<translation id="992110854164447044">भरà¥à¤šà¥à¤…ल कारà¥à¤¡à¤²à¥‡ तपाईंलाई ठगीबाट जोगाउनका निमà¥à¤¤à¤¿ तपाईंको कारà¥à¤¡à¤•à¥‹ वासà¥à¤¤à¤µà¤¿à¤• जानकारी लà¥à¤•à¤¾à¤‰à¤à¤›à¥¤ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">गà¥à¤²à¤¾à¤¬à¥€</translation>
<translation id="992432478773561401">तपाईंको कमà¥à¤ªà¥à¤¯à¥à¤Ÿà¤° वा नेटवरà¥à¤•à¤®à¤¾ "<ph name="SOFTWARE_NAME" />" सही ढङà¥à¤—ले सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ भà¤à¤¨:
diff --git a/chromium/components/strings/components_strings_nl.xtb b/chromium/components/strings/components_strings_nl.xtb
index 9f002f8fb5e..9ecb0b18932 100644
--- a/chromium/components/strings/components_strings_nl.xtb
+++ b/chromium/components/strings/components_strings_nl.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Beurzen, toelagen en financiële ondersteuning</translation>
<translation id="1048785276086539861">Als je annotaties bewerkt, keert dit document terug naar een weergave op één pagina</translation>
<translation id="1050038467049342496">Andere apps sluiten</translation>
+<translation id="1053959602163383901">Je hebt ervoor gekozen om te verifiëren met een authenticator-apparaat op websites die <ph name="PROVIDER_ORIGIN" /> gebruiken. Deze provider heeft misschien informatie over je betaalmethode opgeslagen. Hiervoor kun je <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Toevoegen ongedaan maken</translation>
<translation id="1056663316309890343">Fotosoftware</translation>
<translation id="1056898198331236512">Waarschuwing</translation>
@@ -119,6 +120,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="1270502636509132238">Ophaalmethode</translation>
<translation id="1281476433249504884">Stapeleenheid 1</translation>
<translation id="1285320974508926690">Deze site nooit vertalen</translation>
+<translation id="1288548991597756084">Kaart beveiligd opslaan</translation>
<translation id="1292571435393770077">Lade 16</translation>
<translation id="1292701964462482250">'Software op je computer voorkomt dat Chrome veilig verbinding kan maken met internet' (alleen Windows-computers)</translation>
<translation id="1294154142200295408">Opdrachtregelvarianten</translation>
@@ -223,6 +225,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
&lt;p&gt;​K​li​k ​op​ &lt;​st​ro​ng​&gt;V​er​bi​nd​in​g ​ma​ke​n&lt;​/s​tr​on​g&gt;​ o​p ​de​ p​ag​in​a ​di​e ​je​ p​ro​be​er​t ​te​ o​pe​ne​n ​om​ d​ez​e ​fo​ut​ o​p ​te​ l​os​se​n.​&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landschapsontwerp</translation>
<translation id="1513706915089223971">Lijst met geschiedenisitems</translation>
+<translation id="1516097932025103760">De kaart wordt versleuteld en beveiligd opgeslagen en de CVC wordt nooit opgeslagen.</translation>
<translation id="1517433312004943670">Telefoonnummer vereist</translation>
<translation id="1519264250979466059">Datum van build</translation>
<translation id="1521159554480556801">Textielkunst</translation>
@@ -288,6 +291,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="1662550410081243962">Betaalmethoden opslaan en invullen</translation>
<translation id="1663943134801823270">Passen en adressen zijn afkomstig uit Chrome. Je kunt ze beheren in <ph name="BEGIN_LINK" />Instellingen<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Pagina's in het <ph name="SOURCE_LANGUAGE" /> worden vanaf nu vertaald naar het <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Betaal met <ph name="CARD_DETAIL" /> om de aanbieding te gebruiken</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> naar <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Korte zijde eerst</translation>
<translation id="168693727862418163">Deze beleidswaarde kan niet worden gevalideerd op basis van het bijbehorende schema en wordt genegeerd.</translation>
@@ -306,6 +310,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="1717218214683051432">Bewegingssensoren</translation>
<translation id="1717494416764505390">Mailbox 3</translation>
<translation id="1718029547804390981">Document is te groot om aantekeningen te maken</translation>
+<translation id="1720941539803966190">Tutorial sluiten</translation>
<translation id="1721424275792716183">* Verplicht veld</translation>
<translation id="1727613060316725209">Certificaat is geldig</translation>
<translation id="1727741090716970331">Een geldig kaartnummer toevoegen</translation>
@@ -419,8 +424,8 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="205212645995975601">Barbecue en grillen</translation>
<translation id="2053111141626950936">Pagina's in het <ph name="LANGUAGE" /> worden niet vertaald.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Als dit bedieningselement aanstaat en de status actief is, bepaalt Chrome op welke grote groep mensen (ook wel een cohort genoemd) je recente browse-activiteit het meest lijkt. Adverteerders kunnen advertenties voor de groep selecteren en je browse-activiteit blijft privé op je apparaat. Je groep wordt elke dag geüpdatet.}=1{Als dit bedieningselement aanstaat en de status actief is, bepaalt Chrome op welke grote groep mensen (ook wel een cohort genoemd) je recente browse-activiteit het meest lijkt. Adverteerders kunnen advertenties voor de groep selecteren en je browse-activiteit blijft privé op je apparaat. Je groep wordt elke dag geüpdatet.}other{Als dit bedieningselement aanstaat en de status actief is, bepaalt Chrome op welke grote groep mensen (ook wel een cohort genoemd) je recente browse-activiteit het meest lijkt. Adverteerders kunnen advertenties voor de groep selecteren en je browse-activiteit blijft privé op je apparaat. Je groep wordt elke {NUM_DAYS} dagen geüpdatet.}}</translation>
-<translation id="2053553514270667976">Postcode</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggestie}other{# suggesties}}</translation>
+<translation id="2066915425250589881">een verwijderingsverzoek indienen</translation>
<translation id="2068528718802935086">Baby's en peuters</translation>
<translation id="2071156619270205202">Deze kaart kan niet worden gebruikt als een virtueel kaartnummer.</translation>
<translation id="2071692954027939183">Meldingen zijn automatisch geblokkeerd omdat je deze doorgaans niet toestaat</translation>
@@ -432,7 +437,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="2088086323192747268">Knop Synchronisatie beheren, druk op Enter om de informatie die je synchroniseert te beheren in de Chrome-instellingen</translation>
<translation id="2091887806945687916">Geluid</translation>
<translation id="2094505752054353250">Domeinen komen niet overeen</translation>
-<translation id="2096368010154057602">Departement</translation>
<translation id="2099652385553570808">Drie nietjes links</translation>
<translation id="2101225219012730419">Versie:</translation>
<translation id="2102134110707549001">Sterk wachtwoord voorstellen…</translation>
@@ -469,7 +473,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="2185836064961771414">American football</translation>
<translation id="2187317261103489799">Detecteren (standaard)</translation>
<translation id="2188375229972301266">Meerdere perforaties onder</translation>
-<translation id="2188852899391513400">Het wachtwoord dat je net hebt gebruikt, is gevonden bij een gegevenslek. Google Wachtwoordmanager raadt je aan dit wachtwoord nu te wijzigen en je opgeslagen wachtwoorden te checken om je account te beveiligen.</translation>
<translation id="219906046732893612">Doe-het-zelf</translation>
<translation id="2202020181578195191">Geef een geldig vervaljaar op</translation>
<translation id="22081806969704220">Lade 3</translation>
@@ -480,6 +483,8 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="2215727959747642672">Bestanden bewerken</translation>
<translation id="2215963164070968490">Honden</translation>
<translation id="2218879909401188352">Op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> actieve cybercriminelen kunnen gevaarlijke apps installeren die je apparaat beschadigen, verborgen kosten toevoegen aan je mobiele telefoonrekening of je persoonlijke informatie stelen. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Training opnieuw starten</translation>
+<translation id="2219735899272417925">Apparaat moet worden gereset</translation>
<translation id="2224337661447660594">Geen internet</translation>
<translation id="2230458221926704099">Los problemen met je verbinding op met de <ph name="BEGIN_LINK" />diagnose-app<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Nu verzenden</translation>
@@ -577,11 +582,13 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="2512101340618156538">Niet toegestaan (standaard)</translation>
<translation id="2512413427717747692">Knop Chrome instellen als standaardbrowser, druk op Enter om Chrome in te stellen als de standaardbrowser van het systeem in de iOS-instellingen</translation>
<translation id="2515629240566999685">Check of je bereik hebt in je omgeving</translation>
+<translation id="2515761554693942801">Je hebt ervoor gekozen te verifiëren met Touch ID op websites die <ph name="PROVIDER_ORIGIN" /> gebruiken. Deze provider heeft misschien informatie over je betaalmethode opgeslagen. Hiervoor kun je <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Nietje rechtsonder</translation>
<translation id="2521736961081452453">Formulier maken</translation>
<translation id="2523886232349826891">Alleen opgeslagen op dit apparaat</translation>
<translation id="2524461107774643265">Meer informatie toevoegen</translation>
<translation id="2529899080962247600">Dit veld mag niet meer dan <ph name="MAX_ITEMS_LIMIT" /> items bevatten. Alle andere items worden genegeerd.</translation>
+<translation id="253493526287553278">Details van promotiecode bekijken</translation>
<translation id="2535585790302968248">Een nieuw incognitotabblad openen om privé te browsen</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{en nog 1}other{en nog #}}</translation>
<translation id="2536110899380797252">Adres toevoegen</translation>
@@ -617,7 +624,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="259821504105826686">Fotografie en digitale kunst</translation>
<translation id="2601150049980261779">Romantische films</translation>
<translation id="2604589665489080024">Popmuziek</translation>
-<translation id="2609632851001447353">Varianten</translation>
<translation id="2610561535971892504">Klik om te kopiëren</translation>
<translation id="2617988307566202237">De volgende gegevens worden <ph name="BEGIN_EMPHASIS" />niet opgeslagen<ph name="END_EMPHASIS" /> in Chrome:
<ph name="BEGIN_LIST" />
@@ -650,6 +656,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Verjaardagen en naamdagen</translation>
<translation id="2677748264148917807">Verlaten</translation>
+<translation id="2679714844901977852">Sla je kaart en factureringsgegevens op in je Google-account <ph name="USER_EMAIL" /> zodat je beveiligd en sneller kunt betalen</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Best passend</translation>
<translation id="2688969097326701645">Ja, doorgaan</translation>
@@ -698,7 +705,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="2854764410992194509">Internetproviders</translation>
<translation id="2856444702002559011">Cybercriminelen proberen mogelijk je gegevens van <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> te stelen (bijvoorbeeld wachtwoorden, berichten of creditcardgegevens). <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Site toont opdringerige of misleidende advertenties.</translation>
-<translation id="286512204874376891">Een virtuele kaart vermomt je werkelijke kaart om je te beschermen tegen potentiële fraude. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Vriendelijk</translation>
<translation id="28761159517501904">Films</translation>
<translation id="2876489322757410363">Je verlaat de incognitomodus om te betalen via een externe app. Doorgaan?</translation>
@@ -799,7 +805,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="3158539265159265653">Schijf</translation>
<translation id="3162559335345991374">Het is mogelijk dat je de inlogpagina moet bezoeken van het wifi-netwerk dat je gebruikt.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Eiland</translation>
<translation id="3176929007561373547">Controleer je proxyinstellingen of neem contact op met je netwerkbeheerder om te controleren of de proxyserver werkt. Als je denkt dat je geen proxyserver zou moeten gebruiken: <ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">Weten wanneer je dit apparaat actief gebruikt</translation>
<translation id="3180358318770512945">Ouderschap</translation>
@@ -875,9 +880,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="3369192424181595722">Klokfout</translation>
<translation id="3369459162151165748">Voertuigonderdelen en -accessoires</translation>
<translation id="3371064404604898522">Chrome instellen als standaardbrowser</translation>
-<translation id="3371076217486966826"><ph name="URL" /> wil:
- • Een 3D-kaart van je omgeving maken en de camerapositie volgen
- • De camera gebruiken</translation>
<translation id="337363190475750230">Uitgeschreven</translation>
<translation id="3375754925484257129">Chrome-veiligheidscheck uitvoeren</translation>
<translation id="3377144306166885718">De server gebruikte een verouderde versie van TLS.</translation>
@@ -894,6 +896,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="3399952811970034796">Bezorgadres</translation>
<translation id="3402261774528610252">De verbinding waarmee deze site is geladen, gebruikte TLS 1.0 of TLS 1.1. Deze versies zijn verouderd en worden in de toekomst niet meer gebruikt. Dan kunnen gebruikers deze site niet meer laden. Voor de server moet TLS 1.2 of hoger worden gebruikt.</translation>
<translation id="3405664148539009465">Lettertypen aanpassen</translation>
+<translation id="3407789382767355356">externe login</translation>
<translation id="3409896703495473338">Beveiligingsinstellingen beheren</translation>
<translation id="3414952576877147120">Grootte:</translation>
<translation id="3417660076059365994">Bestanden die je uploadt of bijvoegt, worden voor analyse naar Google Cloud of derden gestuurd. De bestanden kunnen bijvoorbeeld worden gescand op gevoelige gegevens of malware.</translation>
@@ -926,6 +929,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="3477679029130949506">Filmladder en bioscoopagenda</translation>
<translation id="3479552764303398839">Niet nu</translation>
<translation id="3484560055331845446">Je kunt de toegang tot je Google-account kwijtraken. Chrome raadt je aan je wachtwoord nu te wijzigen. Je wordt gevraagd in te loggen op je account.</translation>
+<translation id="3484861421501147767">Herinnering: opgeslagen promotiecode beschikbaar</translation>
<translation id="3487845404393360112">Lade 4</translation>
<translation id="3495081129428749620">Zoeken op pagina <ph name="PAGE_TITLE" /></translation>
<translation id="350069200438440499">Bestandsnaam:</translation>
@@ -1048,6 +1052,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="3810973564298564668">Beheren</translation>
<translation id="3816482573645936981">Waarde (vervangen)</translation>
<translation id="382518646247711829">Als je een proxyserver gebruikt...</translation>
+<translation id="3826050100957962900">Externe login</translation>
<translation id="3827112369919217609">Absoluut</translation>
<translation id="3827666161959873541">Familiefilms</translation>
<translation id="3828924085048779000">Een lege wachtwoordzin is niet toegestaan.</translation>
@@ -1060,9 +1065,9 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="3858027520442213535">Datum en tijd updaten</translation>
<translation id="3858860766373142691">Naam</translation>
<translation id="3872834068356954457">Wetenschap</translation>
+<translation id="3875783148670536197">Laten zien</translation>
<translation id="3881478300875776315">Minder regels bekijken</translation>
<translation id="3884278016824448484">Conflicterende apparaat-ID's</translation>
-<translation id="3885155851504623709">Parochie</translation>
<translation id="388632593194507180">Controle gedetecteerd</translation>
<translation id="3886948180919384617">Stapeleenheid 3</translation>
<translation id="3890664840433101773">E-mailadres toevoegen</translation>
@@ -1092,9 +1097,11 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="3973234410852337861"><ph name="HOST_NAME" /> wordt geblokkeerd</translation>
<translation id="3978338123949022456">Zoekmodus, typ een zoekopdracht en druk op Enter om te zoeken met <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Vogels</translation>
+<translation id="3985750352229496475">Adressen beheren...</translation>
<translation id="3986705137476756801">Live ondertiteling nu uitzetten</translation>
<translation id="3987940399970879459">Minder dan 1 MB</translation>
<translation id="3990250421422698716">Sorteren</translation>
+<translation id="3992684624889376114">Over deze pagina</translation>
<translation id="3996311196211510766">De site <ph name="ORIGIN" /> heeft gevraagd een herkomstbeleid
toe te passen op alle verzoeken aan de site, maar dit beleid kan momenteel niet worden toegepast.</translation>
<translation id="4006465311664329701">Betaalmethoden, aanbiedingen en adressen via Google Pay</translation>
@@ -1219,6 +1226,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="4305666528087210886">Je bestand kan niet worden geopend</translation>
<translation id="4306529830550717874">Adres opslaan?</translation>
<translation id="4306812610847412719">klembord</translation>
+<translation id="4310070645992025887">Je Trajecten doorzoeken</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokkeren (standaard)</translation>
<translation id="4314815835985389558">Synchronisatie beheren</translation>
@@ -1269,6 +1277,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="4435702339979719576">Briefkaart</translation>
<translation id="443673843213245140">Er kan geen proxy worden gebruikt, maar er is wel een expliciete proxyconfiguratie opgegeven.</translation>
<translation id="4441832193888514600">Genegeerd omdat het beleid alleen kan worden ingesteld als cloudgebruikersbeleid.</translation>
+<translation id="4442470707340296952">Chrome-tabbladen</translation>
<translation id="4450893287417543264">Niet meer bekijken</translation>
<translation id="4451135742916150903">Kan vragen of deze site verbinding mag maken met HID-apparaten</translation>
<translation id="4452328064229197696">Het wachtwoord dat je net hebt gebruikt, is gevonden bij een gegevenslek. Google Wachtwoordmanager raadt je aan je opgeslagen wachtwoorden te checken om je accounts te beveiligen.</translation>
@@ -1407,6 +1416,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="483241715238664915">Waarschuwingen aanzetten</translation>
<translation id="4834250788637067901">Betaalmethoden, aanbiedingen en adressen via Google Pay</translation>
<translation id="4838327282952368871">Dromerig</translation>
+<translation id="4839087176073128681">Betaal de volgende keer sneller en bescherm je kaart met de toonaangevende beveiliging van Google.</translation>
<translation id="4840250757394056958">Je Chrome-geschiedenis bekijken</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">Krijg korting bij onder andere <ph name="MERCHANT_NAME" /></translation>
@@ -1469,6 +1479,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="5011561501798487822">Waargenomen taal</translation>
<translation id="5015510746216210676">Naam apparaat:</translation>
<translation id="5017554619425969104">Tekst die je hebt gekopieerd</translation>
+<translation id="5017828934289857214">Later herinneren</translation>
<translation id="5018422839182700155">Kan deze pagina niet openen</translation>
<translation id="5019198164206649151">Backend-opslag in slechte staat</translation>
<translation id="5020776957610079374">Wereldmuziek</translation>
@@ -1488,6 +1499,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="5051305769747448211">Stand-up comedy</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Als je dit bestand wilt sturen met Dichtbij delen, moet je ruimte (<ph name="DISK_SPACE_SIZE" />) op je apparaat vrijmaken}other{Als je deze bestanden wilt sturen met Dichtbij delen, moet je ruimte (<ph name="DISK_SPACE_SIZE" />) op je apparaat vrijmaken}}</translation>
<translation id="5056549851600133418">Artikelen voor jou</translation>
+<translation id="5060483733937416656">Je hebt ervoor gekozen om te verifiëren met Windows Hello op websites die <ph name="PROVIDER_ORIGIN" /> gebruiken. Deze provider heeft misschien informatie over je betaalmethode opgeslagen. Hiervoor kun je <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Bedoelde je <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Afdrukgeschiedenis</translation>
<translation id="5068234115460527047">Hedgefondsen</translation>
@@ -1501,10 +1513,8 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="5087286274860437796">Het servercertificaat is momenteel niet geldig.</translation>
<translation id="5087580092889165836">Pas toevoegen</translation>
<translation id="5088142053160410913">Bericht aan bediener</translation>
-<translation id="5089810972385038852">Staat</translation>
<translation id="5093232627742069661">Zigzagvouw</translation>
<translation id="5094747076828555589">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat van de server wordt niet vertrouwd door Chromium. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept.</translation>
-<translation id="5095208057601539847">Provincie</translation>
<translation id="5097099694988056070">Apparaatstatistieken zoals CPU-/RAM-gebruik rapporteren</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Site is niet beveiligd</translation>
@@ -1542,6 +1552,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="5171045022955879922">Zoek of typ een URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Computer</translation>
+<translation id="5177076414499237632">Meer informatie over de bron en het onderwerp van deze pagina</translation>
<translation id="5179510805599951267">Niet in het <ph name="ORIGINAL_LANGUAGE" />? Deze fout melden</translation>
<translation id="518639307526414276">Voeding en benodigdheden voor huisdieren</translation>
<translation id="5190835502935405962">Bookmarkbalk</translation>
@@ -1702,6 +1713,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="5624120631404540903">Wachtwoorden beheren</translation>
<translation id="5629630648637658800">Laden van beleidsinstellingen is mislukt</translation>
<translation id="5631439013527180824">Ongeldige token voor apparaatbeheer</translation>
+<translation id="5632485077360054581">Laten zien</translation>
<translation id="5633066919399395251">Cybercriminelen op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> proberen mogelijk gevaarlijke programma's op je computer te installeren waarmee je gegevens kunnen worden gestolen of verwijderd (bijvoorbeeld foto's, wachtwoorden, berichten en creditcardgegevens). <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Misleidende content geblokkeerd.</translation>
<translation id="5633259641094592098">Cult- en indiefilms</translation>
@@ -1819,6 +1831,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="5989320800837274978">Er worden geen vaste proxyservers en geen pac-script-URL gespecificeerd.</translation>
<translation id="5992691462791905444">Zizagvouw 2-slag oneven center</translation>
<translation id="5995727681868049093">Je gegevens, privacy en beveiliging beheren in je Google-account</translation>
+<translation id="5997247540087773573">Het wachtwoord dat je net hebt gebruikt, is gevonden bij een gegevenslek. Google Wachtwoordmanager raadt je aan dit wachtwoord nu te wijzigen en je opgeslagen wachtwoorden te checken om je account te beveiligen.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultaten voor '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">Klassiek</translation>
<translation id="6008122969617370890">Volgorde: van N naar 1</translation>
@@ -1913,7 +1926,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="627746635834430766">Sla je pas en factuuradres op in je Google-account zodat je de volgende keer sneller kunt betalen.</translation>
<translation id="6279183038361895380">Druk op |<ph name="ACCELERATOR" />| om je cursor te bekijken</translation>
<translation id="6280223929691119688">Kan niet bezorgen op dit adres. Selecteer een ander adres.</translation>
-<translation id="6282194474023008486">Postcode</translation>
<translation id="6285507000506177184">De knop Downloads in Chrome beheren, druk op Enter om bestanden te beheren die je hebt gedownload in Chrome</translation>
<translation id="6289939620939689042">Paginakleur</translation>
<translation id="6290238015253830360">Je voorgestelde artikelen zie je hier</translation>
@@ -1935,6 +1947,7 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="6337133576188860026">Hiermee wordt minder dan <ph name="SIZE" /> vrijgemaakt. Sommige sites kunnen langzamer worden geladen wanneer je ze weer bezoekt.</translation>
<translation id="6337534724793800597">Beleid filteren op naam</translation>
<translation id="6340739886198108203">Op basis van het beheerdersbeleid wordt afgeraden screenshots of opnamen te maken als er vertrouwelijke content zichtbaar is:</translation>
+<translation id="6348220984832452017">Actieve variaties</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> installeren</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Geen}=1{1 wachtwoord (voor <ph name="DOMAIN_LIST" />, gesynchroniseerd)}=2{2 wachtwoorden (voor <ph name="DOMAIN_LIST" />, gesynchroniseerd)}other{# wachtwoorden (voor <ph name="DOMAIN_LIST" />, gesynchroniseerd)}}</translation>
<translation id="6355392890578844978">Deze browser wordt niet beheerd door een bedrijf of andere organisatie. Activiteit op dit apparaat kan buiten Chromium worden beheerd. <ph name="BEGIN_LINK" />Meer informatie<ph name="END_LINK" /></translation>
@@ -1966,7 +1979,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="643051589346665201">Google-wachtwoord wijzigen</translation>
<translation id="6433490469411711332">Contactgegevens bewerken</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> heeft de verbinding geweigerd.</translation>
-<translation id="6438025220577812695">Zelf wijzigen</translation>
<translation id="6440503408713884761">Genegeerd</translation>
<translation id="6443406338865242315">Welke extensies en plug-ins je hebt geïnstalleerd</translation>
<translation id="6446163441502663861">Kahu (envelop)</translation>
@@ -2096,9 +2108,9 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="6828866289116430505">Genetica</translation>
<translation id="6831043979455480757">Vertalen</translation>
<translation id="6833752742582340615">Sla je kaart en factureringsgegevens op in je Google-account zodat je beveiligd en sneller kunt betalen</translation>
-<translation id="6839929833149231406">Gebied</translation>
<translation id="6846340164947227603">Een virtueel kaartnummer gebruiken…</translation>
<translation id="6852204201400771460">App opnieuw laden?</translation>
+<translation id="6857776781123259569">Wachtwoorden beheren…</translation>
<translation id="686485648936420384">Hulpbronnen voor consumenten</translation>
<translation id="6865412394715372076">Deze pas kan momenteel niet worden geverifieerd</translation>
<translation id="6869334554832814367">Persoonlijke leningen</translation>
@@ -2147,7 +2159,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="6965978654500191972">Apparaat</translation>
<translation id="696703987787944103">Op waarneming gebaseerd</translation>
<translation id="6968269510885595029">Je beveiligingssleutel gebruiken</translation>
-<translation id="6970216967273061347">District</translation>
<translation id="6971439137020188025">Snel een nieuwe Google-presentatie in Presentaties maken</translation>
<translation id="6972629891077993081">HID-apparaten</translation>
<translation id="6973656660372572881">Zowel vaste proxyservers als een pac-script-URL worden gespecificeerd.</translation>
@@ -2186,7 +2197,6 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<translation id="7081308185095828845">Deze functie is niet beschikbaar op je apparaat</translation>
<translation id="7083258188081898530">Lade 9</translation>
<translation id="7086090958708083563">Upload aangevraagd door gebruiker</translation>
-<translation id="7087282848513945231">County</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, druk op Tab en daarna op Enter om rechten en opgeslagen gegevens voor sites te beheren in de Chrome-instellingen</translation>
<translation id="7096937462164235847">De identiteit van deze website is niet geverifieerd.</translation>
<translation id="7101893872976785596">Horrorfilms</translation>
@@ -2205,10 +2215,10 @@ Anders wordt dit geblokkeerd door je privacyinstellingen. Hierdoor werkt de cont
<ph name="LIST_ITEM" />Informatie die je in formulieren hebt opgegeven<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Kan niet verzenden naar dit adres. Selecteer een ander adres.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Je beheerder heeft voorkomen dat deze gegevens worden gekopieerd.</translation>
<translation id="7135130955892390533">Status bekijken</translation>
<translation id="7138472120740807366">Bezorgingsmethode</translation>
-<translation id="7139724024395191329">Emiraat</translation>
<translation id="7139892792842608322">Primaire lade</translation>
<translation id="714064300541049402">Beeldverschuiving X van zijde 2</translation>
<translation id="7152423860607593928">Number-14 (envelop)</translation>
@@ -2425,6 +2435,7 @@ Aanvullende informatie:
<translation id="7669271284792375604">Cybercriminelen op deze site proberen je mogelijk over te halen om programma's te installeren die schadelijk zijn voor de browsefunctionaliteit (door bijvoorbeeld je homepage te wijzigen of extra advertenties te bekijken op sites die je bezoekt).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Acties ondernomen met gegevens die zijn gemarkeerd als vertrouwelijk (1 actie sinds het inloggen). <ph name="BEGIN_LINK" />Meer informatie<ph name="END_LINK" />}other{Acties ondernomen met gegevens die zijn gemarkeerd als vertrouwelijk (# acties sinds het inloggen). <ph name="BEGIN_LINK" />Meer informatie<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Mailbox 6</translation>
+<translation id="7675325315208090829">Betaalmethoden beheren...</translation>
<translation id="7676643023259824263">Zoeken naar klembordtekst, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Je browsegeschiedenis bekijken en beheren in de Chrome-instellingen</translation>
<translation id="7679947978757153706">Honkbal</translation>
@@ -2467,7 +2478,6 @@ Aanvullende informatie:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Dezelfde volgorde met de bedrukte zijde omhoog</translation>
-<translation id="777702478322588152">Prefectuur</translation>
<translation id="7791011319128895129">Niet uitgebracht</translation>
<translation id="7791196057686275387">Bundelen</translation>
<translation id="7791543448312431591">Toevoegen</translation>
@@ -2558,12 +2568,12 @@ Aanvullende informatie:
<translation id="8055534648776115597">Beroeps- en vervolgonderwijs</translation>
<translation id="8057711352706143257">'<ph name="SOFTWARE_NAME" />' is niet correct geconfigureerd. Als je '<ph name="SOFTWARE_NAME" />' verwijdert, wordt het probleem meestal opgelost. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Levensmiddelenindustrie</translation>
-<translation id="8066955247577885446">Er is iets misgegaan.</translation>
<translation id="8067872629359326442">Je hebt zojuist je wachtwoord opgegeven op een misleidende site. Chromium kan je laten zien wat je nu kunt doen. Klik op 'Account beschermen' om je wachtwoord te wijzigen en Google te laten weten dat je account mogelijk gevaar loopt.</translation>
<translation id="8070439594494267500">App-icoon</translation>
<translation id="8074253406171541171">10x13 (envelop)</translation>
<translation id="8075736640322370409">Snel een nieuwe Google-spreadsheet maken</translation>
<translation id="8075898834294118863">Site-instellingen beheren</translation>
+<translation id="8076492880354921740">Tabbladen</translation>
<translation id="8078141288243656252">Kan geen aantekeningen maken als het document is gedraaid</translation>
<translation id="8079031581361219619">Site opnieuw laden?</translation>
<translation id="8081087320434522107">Sedans</translation>
@@ -2686,6 +2696,7 @@ Aanvullende informatie:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Instellingen</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, druk op Tab en daarna op Enter om je cookievoorkeuren te beheren in de Chrome-instellingen</translation>
<translation id="8433057134996913067">Hiermee word je uitgelogd van de meeste websites.</translation>
<translation id="8434840396568290395">Huisdieren</translation>
@@ -2783,6 +2794,7 @@ Aanvullende informatie:
<translation id="8742371904523228557">Je code voor <ph name="ORIGIN" /> is<ph name="ONE_TIME_CODE" />.</translation>
<translation id="874918643257405732">Bookmark toevoegen voor dit tabblad</translation>
<translation id="8751426954251315517">Probeer het de volgende keer opnieuw</translation>
+<translation id="8757526089434340176">Google Pay-aanbieding beschikbaar</translation>
<translation id="8758885506338294482">Competitief videogamen</translation>
<translation id="8759274551635299824">Deze pas is verlopen</translation>
<translation id="87601671197631245">Deze site gebruikt een verouderde beveiligingsconfiguratie, waardoor je gegevens (zoals wachtwoorden, berichten of creditcardnummers) openbaar kunnen worden gemaakt als ze naar deze site worden gestuurd.</translation>
@@ -2790,6 +2802,7 @@ Aanvullende informatie:
<translation id="8763927697961133303">USB-apparaat</translation>
<translation id="8763986294015493060">Alle momenteel geopende incognitovensters sluiten</translation>
<translation id="8766943070169463815">Blad voor beveiligde verificatie van betalingsgegevens is geopend</translation>
+<translation id="8767765348545497220">Hulpballon sluiten</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorfietsen</translation>
<translation id="8790007591277257123">&amp;Opnieuw verwijderen</translation>
@@ -2802,6 +2815,7 @@ Aanvullende informatie:
<translation id="8806285662264631610">Bad- en lichaamsverzorgingsproducten</translation>
<translation id="8807160976559152894">Bijsnijden na elke pagina</translation>
<translation id="8808828119384186784">Chrome-instellingen</translation>
+<translation id="8813277370772331957">Later herinneren</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Druk op Tab en vervolgens op Enter om Chrome te updaten via je Chrome-instellingen</translation>
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="882338992931677877">Handmatige sleuf</translation>
@@ -2981,6 +2995,7 @@ Aanvullende informatie:
<translation id="988159990683914416">Ontwikkelaarsbuild</translation>
<translation id="989988560359834682">Adres bewerken</translation>
<translation id="991413375315957741">bewegings- of lichtsensoren</translation>
+<translation id="992110854164447044">Een virtuele kaart verbergt je werkelijke kaart om je te beschermen tegen potentiële fraude. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Roze</translation>
<translation id="992432478773561401"><ph name="SOFTWARE_NAME" /> is niet correct geïnstalleerd op je computer of het netwerk:
diff --git a/chromium/components/strings/components_strings_no.xtb b/chromium/components/strings/components_strings_no.xtb
index c45f1e99d37..ee9b4036251 100644
--- a/chromium/components/strings/components_strings_no.xtb
+++ b/chromium/components/strings/components_strings_no.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Legater, stipender og økonomisk støtte</translation>
<translation id="1048785276086539861">Når du redigerer annoteringer, går dette dokumentet tilbake til enkeltsidevisning</translation>
<translation id="1050038467049342496">Lukk andre apper</translation>
+<translation id="1053959602163383901">Du har valgt å bekrefte med en autentiseringsenhet på nettsteder som bruker <ph name="PROVIDER_ORIGIN" />. Denne leverandøren kan ha lagret informasjon om betalingsmåten din, som du kan <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Angre tilleggingen</translation>
<translation id="1056663316309890343">Fotoprogramvare</translation>
<translation id="1056898198331236512">Advarsel</translation>
@@ -119,6 +120,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="1270502636509132238">Hentemåte</translation>
<translation id="1281476433249504884">Hylle 1</translation>
<translation id="1285320974508926690">Oversett aldri dette nettstedet</translation>
+<translation id="1288548991597756084">Lagre kortet sikkert</translation>
<translation id="1292571435393770077">Skuff 16</translation>
<translation id="1292701964462482250">«Det er programvare på datamaskinen din som hindrer Chrome i å koble trygt til Internett» (bare på Windows-datamaskiner)</translation>
<translation id="1294154142200295408">Variasjoner i kommandolinjen</translation>
@@ -223,6 +225,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
&lt;p&gt;For å fikse feilen, klikk på &lt;strong&gt;Koble til&lt;/strong&gt; på siden du prøver å åpne.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landskapsarkitektur</translation>
<translation id="1513706915089223971">Liste over loggoppføringer</translation>
+<translation id="1516097932025103760">Det blir kryptert og lagret sikkert, og verifiseringskoden lagres aldri.</translation>
<translation id="1517433312004943670">Det kreves et telefonnummer</translation>
<translation id="1519264250979466059">Versjonsdato</translation>
<translation id="1521159554480556801">Fiber- og tekstilkunst</translation>
@@ -288,6 +291,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="1662550410081243962">Lagre og fyll ut betalingsmåter</translation>
<translation id="1663943134801823270">Kortene og adressene er fra Chrome. Du kan administrere dem i <ph name="BEGIN_LINK" />Innstillinger<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Fra nå av oversettes sider på <ph name="SOURCE_LANGUAGE" /> til <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Betal med <ph name="CARD_DETAIL" /> for å bruke tilbudet</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> til <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Kortsiden først</translation>
<translation id="168693727862418163">Denne regelverdien kunne ikke valideres mot oppsettet og blir derfor ignorert.</translation>
@@ -306,6 +310,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="1717218214683051432">Bevegelsessensorer</translation>
<translation id="1717494416764505390">Postkasse 3</translation>
<translation id="1718029547804390981">Dokumentet er for stort til å annoteres</translation>
+<translation id="1720941539803966190">Lukk veiledningen</translation>
<translation id="1721424275792716183">* Feltet er obligatorisk</translation>
<translation id="1727613060316725209">Sertifikatet er gyldig</translation>
<translation id="1727741090716970331">Legg til et gyldig kortnummer</translation>
@@ -422,8 +427,8 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="205212645995975601">Grilling</translation>
<translation id="2053111141626950936">Sider på <ph name="LANGUAGE" /> oversettes ikke.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Når denne kontrollen er på og statusen er aktiv, bestemmer Chrome hvilken stor gruppe mennesker, eller "kohort", den siste nettleseraktiviteten din ligner mest på. Annonsører kan velge annonser for gruppen, og nettlesingsaktiviteten din holdes privat på enheten din. Gruppen din oppdateres hver dag.}=1{Når denne kontrollen er på og statusen er aktiv, bestemmer Chrome hvilken stor gruppe mennesker, eller "kohort", den siste nettleseraktiviteten din ligner mest på. Annonsører kan velge annonser for gruppen, og nettlesingsaktiviteten din holdes privat på enheten din. Gruppen din oppdateres hver dag.}other{Når denne kontrollen er på og statusen er aktiv, bestemmer Chrome hvilken stor gruppe mennesker, eller "kohort", den siste nettleseraktiviteten din ligner mest på. Annonsører kan velge annonser for gruppen, og nettlesingsaktiviteten din holdes privat på enheten din. Gruppen oppdateres hver {NUM_DAYS}. dag.}}</translation>
-<translation id="2053553514270667976">Postnummer</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 forslag}other{# forslag}}</translation>
+<translation id="2066915425250589881">be om å få slettet</translation>
<translation id="2068528718802935086">Babyer og småbarn</translation>
<translation id="2071156619270205202">Dette kortet er ikke kvalifisert for virtuelt kortnummer.</translation>
<translation id="2071692954027939183">Varsler ble blokkert automatisk fordi du vanligvis ikke tillater dem</translation>
@@ -435,7 +440,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="2088086323192747268">Knappen «Administrer synkronisering» – trykk på Enter for å administrere hvilken informasjon du synkroniserer, i Chrome-innstillingene</translation>
<translation id="2091887806945687916">Lyd</translation>
<translation id="2094505752054353250">Domenene samsvarer ikke</translation>
-<translation id="2096368010154057602">Departement</translation>
<translation id="2099652385553570808">Tre stifter venstre</translation>
<translation id="2101225219012730419">Versjon:</translation>
<translation id="2102134110707549001">Foreslå et sterkt passord…</translation>
@@ -472,7 +476,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="2185836064961771414">Amerikansk fotball</translation>
<translation id="2187317261103489799">Oppdag (standard)</translation>
<translation id="2188375229972301266">Flere hull bunn</translation>
-<translation id="2188852899391513400">Passordet du nettopp brukte, er funnet i et databrudd. For å sikre kontoene dine anbefaler Google Passordlagring at du endrer det nå og deretter sjekker de lagrede passordene dine.</translation>
<translation id="219906046732893612">Oppussing</translation>
<translation id="2202020181578195191">Angi et gyldig utløpsår</translation>
<translation id="22081806969704220">Skuff 3</translation>
@@ -483,6 +486,8 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="2215727959747642672">Filredigering</translation>
<translation id="2215963164070968490">Hunder</translation>
<translation id="2218879909401188352">Angripere som er på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, kan installere farlige apper som skader enheten din, legge til skjulte belastninger på mobilregningen din eller stjele personopplysningene dine. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Start veiledningen på nytt</translation>
+<translation id="2219735899272417925">Enheten må tilbakestilles</translation>
<translation id="2224337661447660594">Ingen nettilkobling</translation>
<translation id="2230458221926704099">Løs tilkoblingsproblemene med <ph name="BEGIN_LINK" />diagnostikkappen<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Send nå</translation>
@@ -580,11 +585,13 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="2512101340618156538">Ikke tillatt (standard)</translation>
<translation id="2512413427717747692">Knappen «Angi Chrome som standardnettleser» – trykk på Enter for å angi Chrome som systemets standardnettleser i iOS-innstillingene</translation>
<translation id="2515629240566999685">Sjekk signalet i området ditt</translation>
+<translation id="2515761554693942801">Du har valgt å bekrefte med Touch ID på nettsteder som bruker <ph name="PROVIDER_ORIGIN" />. Denne leverandøren kan ha lagret informasjon om betalingsmåten din, som du kan <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Stift nede til høyre</translation>
<translation id="2521736961081452453">Opprett skjema</translation>
<translation id="2523886232349826891">Kun lagret på denne enheten</translation>
<translation id="2524461107774643265">Legg til mer informasjon</translation>
<translation id="2529899080962247600">Dette feltet skal ikke ha mer enn <ph name="MAX_ITEMS_LIMIT" /> oppføringer. Eventuelle oppføringer som overskrider dette, ignoreres.</translation>
+<translation id="253493526287553278">Se informasjon om kampanjekoder</translation>
<translation id="2535585790302968248">Åpne en ny inkognitofane for å surfe privat</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{og 1 til}other{og # til}}</translation>
<translation id="2536110899380797252">Legg til adresse</translation>
@@ -620,7 +627,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="259821504105826686">Fotografi og digital kunst</translation>
<translation id="2601150049980261779">Romantiske filmer</translation>
<translation id="2604589665489080024">Popmusikk</translation>
-<translation id="2609632851001447353">Varianter</translation>
<translation id="2610561535971892504">Klikk for å kopiere</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />lagrer ikke<ph name="END_EMPHASIS" /> følgende informasjon:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Bursdager og navnedager</translation>
<translation id="2677748264148917807">GÃ¥ ut</translation>
+<translation id="2679714844901977852">Lagre kortet og faktureringsinformasjonen i Google-kontoen din (<ph name="USER_EMAIL" />) for sikker og raskere betaling</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Beste tilpassing</translation>
<translation id="2688969097326701645">Ja, fortsett</translation>
@@ -701,7 +708,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="2854764410992194509">Internettleverandører (ISP-er)</translation>
<translation id="2856444702002559011">Det kan hende at angripere prøver å stjele informasjonen din fra <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (for eksempel passord, meldinger og kredittkortinformasjon). <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Dette nettstedet viser forstyrrende eller villedende annonser.</translation>
-<translation id="286512204874376891">Et virtuelt kort skjuler det faktiske kortet ditt for å beskytte deg mot potensiell svindel. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Vennskapelig</translation>
<translation id="28761159517501904">Filmer</translation>
<translation id="2876489322757410363">Går ut av Inkognitomodus for å betale via en ekstern app. Vil du fortsette?</translation>
@@ -802,7 +808,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="3158539265159265653">Plate</translation>
<translation id="3162559335345991374">Det kan hende at Wi-Fi-nettverket du bruker, krever at du besøker en påloggingsside.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Øy</translation>
<translation id="3176929007561373547">Sjekk innstillingene for proxy-tjeneren eller kontakt nettverksadministratoren
for å forsikre deg om at den fungerer. Følg disse instruksjonene hvis du
ikke tror du trenger å bruke noen proxy-tjener:
@@ -881,9 +886,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="3369192424181595722">Klokkefeil</translation>
<translation id="3369459162151165748">Bildeler og -tilbehør</translation>
<translation id="3371064404604898522">Angi Chrome som standardnettleser</translation>
-<translation id="3371076217486966826"><ph name="URL" /> vil
- • lage et 3D-kart av omgivelsene dine og spore kameraposisjonen
- • bruke kameraet ditt</translation>
<translation id="337363190475750230">Godkjenningen er opphevet</translation>
<translation id="3375754925484257129">Kjør Chrome-sikkerhetssjekk</translation>
<translation id="3377144306166885718">Tjeneren brukte en foreldet versjon av TLS.</translation>
@@ -900,6 +902,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="3399952811970034796">Leveringsadresse</translation>
<translation id="3402261774528610252">Tilkoblingen som ble brukt til å laste inn dette nettstedet, brukte TLS 1.0 eller TLS 1.1, som er foreldet og kommer til å bli slått av i fremtiden. Når det er slått av, blir brukerne hindret i å laste inn dette nettstedet. Tjeneren bør slå på TLS 1.2 eller nyere.</translation>
<translation id="3405664148539009465">Tilpass skrifttyper</translation>
+<translation id="3407789382767355356">tredjepartspålogging</translation>
<translation id="3409896703495473338">Administrer sikkerhetsinnstillinger</translation>
<translation id="3414952576877147120">Størrelse:</translation>
<translation id="3417660076059365994">Filer du laster opp eller legger ved, sendes til Google Cloud eller tredjeparter for analyse. De kan for eksempel bli skannet for sensitive data eller skadelig programvare.</translation>
@@ -932,6 +935,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="3477679029130949506">Kino- og teaterprogram</translation>
<translation id="3479552764303398839">Ikke nå</translation>
<translation id="3484560055331845446">Du kan miste tilgangen til Google-kontoen din. Chrome anbefaler at du endrer passordet ditt nå. Du blir bedt om å logge på.</translation>
+<translation id="3484861421501147767">PÃ¥minnelse: En lagret kampanjekode er tilgjengelig</translation>
<translation id="3487845404393360112">Skuff 4</translation>
<translation id="3495081129428749620">Finn på siden
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="3810973564298564668">Administrer</translation>
<translation id="3816482573645936981">Verdi (overstyrt)</translation>
<translation id="382518646247711829">Hvis du bruker en proxy-tjener...</translation>
+<translation id="3826050100957962900">Tredjepartspålogging</translation>
<translation id="3827112369919217609">Absolutt</translation>
<translation id="3827666161959873541">Familiefilmer</translation>
<translation id="3828924085048779000">Tom passordfrase er ikke tillatt.</translation>
@@ -1068,9 +1073,9 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="3858027520442213535">Oppdater dato og klokkeslett</translation>
<translation id="3858860766373142691">Navn</translation>
<translation id="3872834068356954457">Vitenskap</translation>
+<translation id="3875783148670536197">Vis meg hvordan</translation>
<translation id="3881478300875776315">Vis færre linjer</translation>
<translation id="3884278016824448484">Motstridende enhetsidentifikator</translation>
-<translation id="3885155851504623709">Sogn</translation>
<translation id="388632593194507180">Overvåking er oppdaget</translation>
<translation id="3886948180919384617">Hylle 3</translation>
<translation id="3890664840433101773">Legg til e-post</translation>
@@ -1100,9 +1105,11 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="3973234410852337861"><ph name="HOST_NAME" /> er blokkert</translation>
<translation id="3978338123949022456">Søkemodus – skriv inn et søk, og trykk på Enter for å søke på <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Fugler</translation>
+<translation id="3985750352229496475">Administrer adresser…</translation>
<translation id="3986705137476756801">Slå av Direkteteksting inntil videre</translation>
<translation id="3987940399970879459">Under 1 MB</translation>
<translation id="3990250421422698716">Støtforskyvning</translation>
+<translation id="3992684624889376114">Om denne siden</translation>
<translation id="3996311196211510766">Nettstedet <ph name="ORIGIN" /> har bedt om at en opphavsregel
skal gjelde for alle forespørsler som sendes til det, men denne regelen kan ikke tas i bruk akkurat nå.</translation>
<translation id="4006465311664329701">Betalingsmåter, tilbud og adresser som bruker Google Pay</translation>
@@ -1227,6 +1234,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="4305666528087210886">Kunne ikke åpne filen</translation>
<translation id="4306529830550717874">Vil du lagre adressen?</translation>
<translation id="4306812610847412719">utklippstavle</translation>
+<translation id="4310070645992025887">Søk i søkereisene dine</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokkér (standard)</translation>
<translation id="4314815835985389558">Administrer synkronisering</translation>
@@ -1277,6 +1285,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="4435702339979719576">postkort)</translation>
<translation id="443673843213245140">Bruk av proxy-tjener er deaktivert, men det er angitt en uttrykkelig proxy-tjenerkonfigurasjon.</translation>
<translation id="4441832193888514600">Ignorert fordi regelen kun kan angis som brukerinnstillinger i nettskyen.</translation>
+<translation id="4442470707340296952">Chrome-faner</translation>
<translation id="4450893287417543264">Ikke vis igjen</translation>
<translation id="4451135742916150903">Kan be om å få koble til HID-enheter</translation>
<translation id="4452328064229197696">Passordet du nettopp brukte, er funnet i et databrudd. For å sikre kontoene dine anbefaler Google Passordlagring at du sjekker de lagrede passordene dine.</translation>
@@ -1415,6 +1424,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="483241715238664915">Slå på varsler</translation>
<translation id="4834250788637067901">Betalingsmåter, tilbud og adresser som bruker Google Pay</translation>
<translation id="4838327282952368871">Drømmende</translation>
+<translation id="4839087176073128681">Betal raskere neste gang, og beskytt kortet ditt med Googles bransjeledende sikkerhet.</translation>
<translation id="4840250757394056958">Se Chrome-loggen din</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">FÃ¥ rabatt hos <ph name="MERCHANT_NAME" /> og flere</translation>
@@ -1477,6 +1487,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="5011561501798487822">gjenkjent språk</translation>
<translation id="5015510746216210676">Maskinnavn:</translation>
<translation id="5017554619425969104">Teksten du kopierte</translation>
+<translation id="5017828934289857214">PÃ¥minn meg senere</translation>
<translation id="5018422839182700155">Kan ikke åpne denne siden</translation>
<translation id="5019198164206649151">Ugyldig funksjonalitet for sikkerhetskopiering</translation>
<translation id="5020776957610079374">Verdensmusikk</translation>
@@ -1496,6 +1507,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="5051305769747448211">Livekomedie</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{For å sende denne filen med nærdeling, frigjør plass (<ph name="DISK_SPACE_SIZE" />) på enheten}other{For å sende disse filene med nærdeling, frigjør plass (<ph name="DISK_SPACE_SIZE" />) på enheten}}</translation>
<translation id="5056549851600133418">Artikler for deg</translation>
+<translation id="5060483733937416656">Du har valgt å bekrefte med Windows Hello på nettsteder som bruker <ph name="PROVIDER_ORIGIN" />. Denne leverandøren kan ha lagret informasjon om betalingsmåten din, som du kan <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Mente du <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Utskriftslogg</translation>
<translation id="5068234115460527047">Hedgefond</translation>
@@ -1509,10 +1521,8 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="5087286274860437796">Sertifikatet til tjeneren er ikke gyldig for øyeblikket.</translation>
<translation id="5087580092889165836">Legg til et kort</translation>
<translation id="5088142053160410913">Melding til operatøren</translation>
-<translation id="5089810972385038852">Fylke/delstat</translation>
<translation id="5093232627742069661">Z-fals</translation>
<translation id="5094747076828555589">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />. Sikkerhetssertifikatet til tjeneren er ikke klarert av Chromium. Dette kan være forårsaket av en feilkonfigurering eller en angriper som avskjærer tilkoblingen din.</translation>
-<translation id="5095208057601539847">Provins</translation>
<translation id="5097099694988056070">enhetsstatistikk, som prosessor- og minnebruk</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Nettstedet er ikke sikkert</translation>
@@ -1550,6 +1560,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="5171045022955879922">Søk, eller skriv inn en nettadresse</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Datamaskin</translation>
+<translation id="5177076414499237632">Finn ut mer om sidens kilde og emne</translation>
<translation id="5179510805599951267">Er ikke dette <ph name="ORIGINAL_LANGUAGE" />? Rapportér denne feilen</translation>
<translation id="518639307526414276">Dyremat og -utstyr</translation>
<translation id="5190835502935405962">Bokmerkerad</translation>
@@ -1710,6 +1721,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="5624120631404540903">Administrer passord</translation>
<translation id="5629630648637658800">Kunne ikke laste in angivelsen for enhetsinnstillinger</translation>
<translation id="5631439013527180824">Ugyldig token for enhetsadministrering</translation>
+<translation id="5632485077360054581">Vis meg hvordan</translation>
<translation id="5633066919399395251">Angripere som er på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, kan prøve å installere farlige programmer på datamaskinen du bruker, for å stjele eller slette informasjonen din (for eksempel bilder, passord, meldinger og kredittkortinformasjon). <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Villedende innhold er blokkert.</translation>
<translation id="5633259641094592098">Kult- og indiefilmer</translation>
@@ -1827,6 +1839,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="5989320800837274978">Verken statiske proxytjenere eller en nettadresse med .pac-skript er angitt.</translation>
<translation id="5992691462791905444">Ingeniør-Z-fals</translation>
<translation id="5995727681868049093">Administrer informasjon, personvern og sikkerhet i Google-kontoen din</translation>
+<translation id="5997247540087773573">Passordet du nettopp brukte, er funnet i et databrudd. For å sikre kontoene dine anbefaler Google Passordlagring at du endrer det nå og sjekker de lagrede passordene dine.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultater for «<ph name="SEARCH_TEXT" />»</translation>
<translation id="6006484371116297560">Klassisk</translation>
<translation id="6008122969617370890">N-til-1-rekkefølge</translation>
@@ -1922,7 +1935,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="627746635834430766">For å betale raskere neste gang, lagre kortet ditt og faktureringsadressen i Google-kontoen din.</translation>
<translation id="6279183038361895380">Trykk på |<ph name="ACCELERATOR" />| for å se markøren</translation>
<translation id="6280223929691119688">Kan ikke levere til denne adressen. Velg en annen adresse.</translation>
-<translation id="6282194474023008486">Postnummer</translation>
<translation id="6285507000506177184">Knappen «Administrer nedlastinger i Chrome» – trykk på Enter for å administrere filer du har lastet ned i Chrome</translation>
<translation id="6289939620939689042">Sidefarge</translation>
<translation id="6290238015253830360">De foreslåtte artiklene dine vises her</translation>
@@ -1944,6 +1956,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="6337133576188860026">Frigjør mindre enn <ph name="SIZE" />. Det kan hende enkelte nettsteder lastes inn tregere neste gang du besøker dem.</translation>
<translation id="6337534724793800597">Filtrér retningslinjer etter navn</translation>
<translation id="6340739886198108203">Administratorreglene anbefaler ikke at du tar skjermdumper eller gjør opptak når konfidensielt innhold er synlig:</translation>
+<translation id="6348220984832452017">Aktive variasjoner</translation>
<translation id="6349101878882523185">Installer <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ingen}=1{1 passord (for <ph name="DOMAIN_LIST" /> – synkronisert)}=2{2 passord (for <ph name="DOMAIN_LIST" /> – synkronisert)}other{# passord (for <ph name="DOMAIN_LIST" /> – synkronisert)}}</translation>
<translation id="6355392890578844978">Denne nettleseren administreres ikke av et selskap eller en annen organisasjon. Aktiviteten på denne enheten kan bli administrert utenfor Chromium. <ph name="BEGIN_LINK" />Finn ut mer<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="643051589346665201">Bytt Google-passord</translation>
<translation id="6433490469411711332">Endre kontaktinformasjonen</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> avviste tilkoblingsforsøket.</translation>
-<translation id="6438025220577812695">Jeg endrer det selv</translation>
<translation id="6440503408713884761">Ignorert</translation>
<translation id="6443406338865242315">hvilke utvidelser og programtillegg du har installert</translation>
<translation id="6446163441502663861">Kahu (konvolutt)</translation>
@@ -2105,9 +2117,9 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="6828866289116430505">Genetikk</translation>
<translation id="6831043979455480757">Oversett</translation>
<translation id="6833752742582340615">Lagre kortet ditt og faktureringsinformasjonen din i Google-kontoen for sikker og raskere betaling</translation>
-<translation id="6839929833149231406">Område</translation>
<translation id="6846340164947227603">Bruk et virtuelt kortnummer</translation>
<translation id="6852204201400771460">Vil du laste inn appen på nytt?</translation>
+<translation id="6857776781123259569">Administrer passord…</translation>
<translation id="686485648936420384">Forbrukerressurser</translation>
<translation id="6865412394715372076">Dette kortet kan ikke bekreftes akkurat nå</translation>
<translation id="6869334554832814367">Personlige lån</translation>
@@ -2156,7 +2168,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="6965978654500191972">Enhet</translation>
<translation id="696703987787944103">Perseptuell</translation>
<translation id="6968269510885595029">Bruk sikkerhetsnøkkelen din</translation>
-<translation id="6970216967273061347">Område</translation>
<translation id="6971439137020188025">Opprett en ny Google-presentasjon i Presentasjoner raskt</translation>
<translation id="6972629891077993081">HID-enheter</translation>
<translation id="6973656660372572881">BÃ¥de statiske proxytjenere og en .pac-skriptnettadresse er angitt.</translation>
@@ -2195,7 +2206,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="7081308185095828845">Denne funksjonen er ikke tilgjengelig på enheten din</translation>
<translation id="7083258188081898530">Skuff 9</translation>
<translation id="7086090958708083563">Opplasting forespurt av bruker</translation>
-<translation id="7087282848513945231">Fylke</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" /> – trykk på Tab og deretter på Enter for å administrere tillatelser og data som er lagret på nettsteder, i Chrome-innstillingene</translation>
<translation id="7096937462164235847">Identiteten til dette nettstedet er ikke bekreftet.</translation>
<translation id="7101893872976785596">Skrekkfilmer</translation>
@@ -2214,10 +2224,10 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<ph name="LIST_ITEM" />Informasjon du har lagt inn i skjemaer<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Kan ikke sende til denne adressen. Velg en annen adresse.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Administratoren din har forbudt kopiering av disse dataene.</translation>
<translation id="7135130955892390533">Vis statusen</translation>
<translation id="7138472120740807366">Leveringsmetode</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Hovedskuff</translation>
<translation id="714064300541049402">X-forskyvning av bilde på side 2</translation>
<translation id="7152423860607593928">Number-14 (konvolutt)</translation>
@@ -2433,6 +2443,7 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="7669271284792375604">Angripere på dette nettstedet kan forsøke å lure deg til å installere programmer som ødelegger surfeopplevelsen din (for eksempel ved å endre startsiden din eller vise ekstra annonser på nettstedene du besøker).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Handlinger som utføres med data som er merket som konfidensielle (1 handling siden pålogging). <ph name="BEGIN_LINK" />Finn ut mer<ph name="END_LINK" />}other{Handlinger som utføres med data som er merket som konfidensielle (# handlinger siden pålogging). <ph name="BEGIN_LINK" />Finn ut mer<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Postkasse 6</translation>
+<translation id="7675325315208090829">Administrer betalingsmåter…</translation>
<translation id="7676643023259824263">Søk etter teksten <ph name="TEXT" /> på utklippstavlen</translation>
<translation id="7679367271685653708">Se og administrer nettleserloggen din i Chrome-innstillingene</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2475,7 +2486,6 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="7766518757692125295">Skjørt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Samme rekkefølge – forsiden opp</translation>
-<translation id="777702478322588152">Prefektur</translation>
<translation id="7791011319128895129">Ikke lansert</translation>
<translation id="7791196057686275387">Bunt</translation>
<translation id="7791543448312431591">Legg til</translation>
@@ -2566,12 +2576,12 @@ I motsatt fall blir dette blokkert av personverninnstillingene. Dette gjør at i
<translation id="8055534648776115597">Yrkesopplæring og videreutdanning</translation>
<translation id="8057711352706143257">«<ph name="SOFTWARE_NAME" />» er ikke riktig konfigurert. Avinstallering av «<ph name="SOFTWARE_NAME" />» løser vanligvis problemet. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Matproduksjon</translation>
-<translation id="8066955247577885446">Beklager, noe gikk galt.</translation>
<translation id="8067872629359326442">Du har nettopp skrevet inn passordet ditt på et villedende nettsted. Chromium kan hjelpe deg. For å bytte passord og varsle Google om at kontoen din kanskje er i fare, klikk på Beskytt konto.</translation>
<translation id="8070439594494267500">Appikon</translation>
<translation id="8074253406171541171">10x13 (konvolutt)</translation>
<translation id="8075736640322370409">Opprett et nytt Google-regneark raskt</translation>
<translation id="8075898834294118863">Administrer nettstedsinnstillinger</translation>
+<translation id="8076492880354921740">Faner</translation>
<translation id="8078141288243656252">Kan ikke legge til annoteringer når dokumentet er rotert</translation>
<translation id="8079031581361219619">Vil du laste inn nettstedet på nytt?</translation>
<translation id="8081087320434522107">Sedaner</translation>
@@ -2694,6 +2704,7 @@ Mer informasjon:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Innstillinger</translation>
+<translation id="8428634594422941299">Greit</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" /> – trykk på Tab og deretter på Enter for å administrere preferansene dine for informasjonskapsler i Chrome-innstillingene</translation>
<translation id="8433057134996913067">Dette logger deg av de fleste nettsteder.</translation>
<translation id="8434840396568290395">Kjæledyr</translation>
@@ -2791,6 +2802,7 @@ Mer informasjon:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> er koden din for <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Bokmerk denne fanen</translation>
<translation id="8751426954251315517">Prøv på nytt neste gang</translation>
+<translation id="8757526089434340176">Google Pay-tilbud er tilgjengelig</translation>
<translation id="8758885506338294482">Videospillkonkurranser</translation>
<translation id="8759274551635299824">Dette kortet er utløpt</translation>
<translation id="87601671197631245">Dette nettstedet bruker en utdatert sikkerhetskonfigurasjon, som betyr at informasjonen din (for eksempel passord, meldinger eller kredittkort) kan bli avslørt når den sendes til dette nettstedet.</translation>
@@ -2798,6 +2810,7 @@ Mer informasjon:
<translation id="8763927697961133303">USB-enhet</translation>
<translation id="8763986294015493060">Lukk alle åpne inkognitovinduer</translation>
<translation id="8766943070169463815">Ark for sikker autentisering av betalingslegitimasjon er åpnet</translation>
+<translation id="8767765348545497220">Lukk hjelpeboble</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorsykler</translation>
<translation id="8790007591277257123">&amp;Slett likevel</translation>
@@ -2810,6 +2823,7 @@ Mer informasjon:
<translation id="8806285662264631610">Bade- og kroppsprodukter</translation>
<translation id="8807160976559152894">Beskjær etter hver side</translation>
<translation id="8808828119384186784">Chrome-innstillinger</translation>
+<translation id="8813277370772331957">PÃ¥minn meg senere</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" /> – trykk på Tab og deretter på Enter for å oppdatere Chrome fra Chrome-innstillingene</translation>
<translation id="8820817407110198400">Bokmerker</translation>
<translation id="882338992931677877">Manuelt spor</translation>
@@ -2989,6 +3003,7 @@ Mer informasjon:
<translation id="988159990683914416">Utviklerversjon</translation>
<translation id="989988560359834682">Rediger adresse</translation>
<translation id="991413375315957741">bevegelses- eller lyssensorer</translation>
+<translation id="992110854164447044">Et virtuelt kort skjuler det faktiske kortet ditt for å beskytte deg mot potensiell svindel. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401">«<ph name="SOFTWARE_NAME" />» er ikke riktig installert på datamaskinen eller på nettverket:
diff --git a/chromium/components/strings/components_strings_or.xtb b/chromium/components/strings/components_strings_or.xtb
index 73d8fb1ac24..48dc7583101 100644
--- a/chromium/components/strings/components_strings_or.xtb
+++ b/chromium/components/strings/components_strings_or.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ଗà­à¬°à¬¾à¬£à­à¬Ÿ, ବୃତà­à¬¤à¬¿ à¬à¬¬à¬‚ ଆରà­à¬¥à¬¿à¬• ସହାୟତା</translation>
<translation id="1048785276086539861">ଯେତେବେଳେ ଆପଣ à¬à¬¨à­‹à¬Ÿà­‡à¬¸à¬¨à¬—à­à­œà¬¿à¬•à­ à¬à¬¡à¬¿à¬Ÿà­ କରନà­à¬¤à¬¿, à¬à¬¹à¬¿ ଡକà­à­Ÿà­à¬®à­‡à¬£à­à¬Ÿ ସିଙà­à¬—ଲୠପେଜୠଭà­à­Ÿà­à¬•à­ ଫେରି ଆସିବ</translation>
<translation id="1050038467049342496">ଅନà­à­Ÿ ଆପà­â€ ବନà­à¬¦ କରନà­à¬¤à­</translation>
+<translation id="1053959602163383901">ଆପଣ <ph name="PROVIDER_ORIGIN" /> ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରà­à¬¥à¬¿à¬¬à¬¾ ୱେବସାଇଟଗà­à­œà¬¿à¬•à¬°à­‡ à¬à¬• ଅଥେଣà­à¬Ÿà¬¿à¬•à­‡à¬Ÿà¬° ଡିଭାଇସ ମାଧà­à­Ÿà¬®à¬°à­‡ ଯାଞà­à¬š କରିବାକୠବାଛିଛନà­à¬¤à¬¿à¥¤ à¬à¬¹à¬¿ ପà­à¬°à¬¦à¬¾à¬¨à¬•à¬¾à¬°à­€ ଆପଣଙà­à¬• ପେମେଣà­à¬Ÿ ପଦà­à¬§à¬¤à¬¿ ବିଷୟରେ ସୂଚନା ଷà­à¬Ÿà­‹à¬° କରିଥାଇପାରନà­à¬¤à¬¿, ଯାହାକୠଆପଣ <ph name="LINK_TEXT" /> କରିପାରିବେ।</translation>
<translation id="1055184225775184556">ଯୋଗ କରାଯାଇଥିବା କାରà­à¬¯à­à­Ÿà¬•à­ &amp;ପୂରà­à¬¬à¬¬à¬¤à­ କରନà­à¬¤à­</translation>
<translation id="1056663316309890343">ଫଟୋ ସଫà­à¬Ÿà­±à­‡à¬°</translation>
<translation id="1056898198331236512">ଚେତାବନୀ</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">ଉଠାଇବା ପଦà­à¬§à¬¤à¬¿</translation>
<translation id="1281476433249504884">ଷà­à¬Ÿà¬¾à¬•à¬°à­ 1</translation>
<translation id="1285320974508926690">à¬à¬¹à¬¿ ସାଇଟà­â€à¬•à­ କଦାପି ଅନà­à¬¬à¬¾à¬¦ କରନà­à¬¤à­ ନାହିà¬</translation>
+<translation id="1288548991597756084">ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ଭାବେ କାରà­à¬¡à¬•à­ ସେଭ କରନà­à¬¤à­</translation>
<translation id="1292571435393770077">ଟà­à¬°à­‡ 16</translation>
<translation id="1292701964462482250">"ଆପଣଙà­à¬• କମà­à¬ªà­à­Ÿà­à¬Ÿà¬°à­â€Œà¬°à­‡ ଥିବା ସଫà­à¬Ÿà­±à­‡à­Ÿà¬¾à¬°à­ Chromeକୠୱେବୠସହ ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ସଂଯୋଗ କରିବାରେ ବାଧା ଦେଉଛି" (କେବଳ Windows କମà­à¬ªà­à­Ÿà­à¬Ÿà¬°à­)</translation>
<translation id="1294154142200295408">କମାଣà­à¬¡-ଲାଇନà­â€ ଭିନà­à¬¨à¬¤à¬¾</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;ତà­à¬°à­à¬Ÿà¬¿ ସମାଧାନ କରିବାକà­, ଆପଣ ଖୋଲିବାକୠଚେଷà­à¬Ÿà¬¾ କରà­à¬¥à¬¿à¬¬à¬¾ ପୃଷà­à¬ à¬¾à¬°à­‡ &lt;strong&gt;ସଂଯୋଗ କରନà­à¬¤à­&lt;/strong&gt;ରେ କà­à¬²à¬¿à¬•à­ କରନà­à¬¤à­à¥¤&lt;/p&gt;</translation>
<translation id="1507780850870535225">ଲà­à­Ÿà¬¾à¬£à­à¬¡à¬¸à­à¬•à­‡à¬ª ଡିଜାଇନ</translation>
<translation id="1513706915089223971">à¬à¬£à­à¬Ÿà­à¬°à¬¿ କରାଯାଉଥିବା ଇତିହାସର ତାଲିକା</translation>
+<translation id="1516097932025103760">à¬à¬¹à¬¾à¬•à­ à¬à¬¨à¬•à­à¬°à¬¿à¬ªà­à¬Ÿ କରାଯାଇ ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ଭାବେ ସେଭ କରାଯିବ à¬à¬¬à¬‚ CVCକୠକେବେ ବି ଷà­à¬Ÿà­‹à¬° କରାଯିବ ନାହିà¬à¥¤</translation>
<translation id="1517433312004943670">ଫୋନୠନମà­à¬¬à¬° ଆବଶà­à­Ÿà¬• ଅଟେ</translation>
<translation id="1519264250979466059">ବିଲà­à¬¡ ତାରିଖ</translation>
<translation id="1521159554480556801">ଫାଇବର à¬à¬¬à¬‚ ଟେକà­à¬¸à¬Ÿà¬¾à¬‡à¬² ଆରà­à¬Ÿà­à¬¸</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ଠିକଣାଗà­à­œà¬¿à¬• ସେଭୠକରି ପୂରଣ କରନà­à¬¤à­</translation>
<translation id="1663943134801823270">କାରà­à¬¡ ଓ ଠିକଣାଗà­à­œà¬¿à¬• ଆପଣଙà­à¬•à¬° Chromeରୠଆସିଛି। ଆପଣ ସେଗà­à­œà¬¿à¬•à­ <ph name="BEGIN_LINK" />ସେଟିଂସà­â€<ph name="END_LINK" />ରେ ପରିଚାଳନ କରିପାରିବେ।</translation>
<translation id="1671391448414634642">à¬à¬¬à­‡ ଠାରୠ<ph name="SOURCE_LANGUAGE" />ର ପୃଷà­à¬ à¬¾à¬—à­à¬¡à¬¼à¬¿à¬•à­ <ph name="TARGET_LANGUAGE" />ରେ ଅନà­à¬¬à¬¾à¬¦ କରାଯିବ।</translation>
+<translation id="1673886523110456987">ଅଫର ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରିବା ପାଇଠ<ph name="CARD_DETAIL" /> ମାଧà­à­Ÿà¬®à¬°à­‡ ଯାଞà­à¬š କରନà­à¬¤à­</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> ରୠ<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ପà­à¬°à¬¥à¬®à­‡ ଛୋଟ à¬à¬¡à¬œà­</translation>
<translation id="168693727862418163">à¬à¬¹à¬¿ ନୀତିର ମୂଲà­à­Ÿ à¬à¬¹à¬¾à¬° ସà­à¬•à¬¿à¬®à¬¾ ଅନà­à¬¯à¬¾à­Ÿà­€ ବୈଧ ହେବାରେ ବିଫଳ ହୋଇଛି à¬à¬¬à¬‚ à¬à¬¹à¬¾à¬•à­ ଅଣଦେଖା କରାଯିବ।</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">ମୋସନୠସେନà­à¬¸à¬°à­</translation>
<translation id="1717494416764505390">ମେଲବକà­à¬¸ 3</translation>
<translation id="1718029547804390981">ଅଂଶୀଦାର ହେବା ପାଇଠଡକà­à­Ÿà­à¬®à­‡à¬£à­à¬Ÿ ବହà­à¬¤ ବଡ଼ ଅଟେ</translation>
+<translation id="1720941539803966190">ଟà­à­Ÿà­à¬Ÿà­‹à¬°à¬¿à¬†à¬²à­ ବନà­à¬¦ କରନà­à¬¤à­</translation>
<translation id="1721424275792716183">* ଫିଲà­à¬¡ ଆବଶà­à­Ÿà¬• ଅଟେ</translation>
<translation id="1727613060316725209">ସାରà­à¬Ÿà¬¿à¬«à¬¿à¬•à­‡à¬Ÿà¬Ÿà¬¿ ବୈଧ ଅଟେ</translation>
<translation id="1727741090716970331">ବୈଧ କାରà­à¬¡ ନମà­à¬¬à¬° ଯୋଗ କରନà­à¬¤à­</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">BBQ à¬à¬¬à¬‚ ଗà­à¬°à¬¿à¬²à¬¿à¬‚</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" />ରେ ଥିବା ପୃଷà­à¬ à¬¾à¬—à­à­œà¬¿à¬• ଅନà­à¬¬à¬¾à¬¦ କରାଯିବ ନାହିà¬à¥¤</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{à¬à¬¹à¬¿ ନିୟନà­à¬¤à­à¬°à¬£ ଚାଲୠଥିଲେ à¬à¬¬à¬‚ ସà­à¬¥à¬¿à¬¤à¬¿ ସକà­à¬°à¬¿à­Ÿ ଥିଲେ, ଆପଣଙà­à¬•à¬° ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨à¬° ବà­à¬°à¬¾à¬‰à¬œà¬¿à¬‚ କାରà­à¬¯à­à­Ÿà¬•à¬³à¬¾à¬ª ଲୋକମାନଙà­à¬•à¬° କେଉଠବଡ଼ ଗୋଷà­à¬ à­€ କିମà­à¬¬à¬¾ "ଦଳ" ସହ ସବà­à¬ à¬¾à¬°à­ ଅଧିକ ସମାନ ଅଟେ ତାହା Chrome ନିରà­à¬¦à­à¬§à¬¾à¬°à¬£ କରେ। ବିଜà­à¬žà¬¾à¬ªà¬¨à¬¦à¬¾à¬¤à¬¾à¬®à¬¾à¬¨à­‡ ଗୋଷà­à¬ à­€ ପାଇଠବିଜà­à¬žà¬¾à¬ªà¬¨à¬—à­à­œà¬¿à¬• ବାଛିପାରିବେ à¬à¬¬à¬‚ ଆପଣଙà­à¬• ବà­à¬°à¬¾à¬‰à¬œà¬¿à¬‚ କାରà­à¬¯à­à­Ÿà¬•à¬³à¬¾à¬ª ଆପଣଙà­à¬• ଡିଭାଇସରେ ବà­à­Ÿà¬•à­à¬¤à¬¿à¬—ତ ରଖାଯାà¬à¥¤ ଆପଣଙà­à¬• ଗୋଷà­à¬ à­€à¬•à­ ପà­à¬°à¬¤à¬¿à¬¦à¬¿à¬¨ ଅପଡେଟୠକରାଯାà¬à¥¤}=1{à¬à¬¹à¬¿ ନିୟନà­à¬¤à­à¬°à¬£ ଚାଲୠଥିଲେ à¬à¬¬à¬‚ ସà­à¬¥à¬¿à¬¤à¬¿ ସକà­à¬°à¬¿à­Ÿ ଥିଲେ, ଆପଣଙà­à¬•à¬° ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨à¬° ବà­à¬°à¬¾à¬‰à¬œà¬¿à¬‚ କାରà­à¬¯à­à­Ÿà¬•à¬³à¬¾à¬ª ଲୋକମାନଙà­à¬•à¬° କେଉଠବଡ଼ ଗୋଷà­à¬ à­€ କିମà­à¬¬à¬¾ "ଦଳ" ସହ ସବà­à¬ à¬¾à¬°à­ ଅଧିକ ସମାନ ଅଟେ ତାହା Chrome ନିରà­à¬¦à­à¬§à¬¾à¬°à¬£ କରେ। ବିଜà­à¬žà¬¾à¬ªà¬¨à¬¦à¬¾à¬¤à¬¾à¬®à¬¾à¬¨à­‡ ଗୋଷà­à¬ à­€ ପାଇଠବିଜà­à¬žà¬¾à¬ªà¬¨à¬—à­à­œà¬¿à¬• ବାଛିପାରିବେ à¬à¬¬à¬‚ ଆପଣଙà­à¬• ବà­à¬°à¬¾à¬‰à¬œà¬¿à¬‚ କାରà­à¬¯à­à­Ÿà¬•à¬³à¬¾à¬ª ଆପଣଙà­à¬• ଡିଭାଇସରେ ବà­à­Ÿà¬•à­à¬¤à¬¿à¬—ତ ରଖାଯାà¬à¥¤ ଆପଣଙà­à¬• ଗୋଷà­à¬ à­€à¬•à­ ପà­à¬°à¬¤à¬¿à¬¦à¬¿à¬¨ ଅପଡେଟୠକରାଯାà¬à¥¤}other{à¬à¬¹à¬¿ ନିୟନà­à¬¤à­à¬°à¬£ ଚାଲୠଥିଲେ à¬à¬¬à¬‚ ସà­à¬¥à¬¿à¬¤à¬¿ ସକà­à¬°à¬¿à­Ÿ ଥିଲେ, ଆପଣଙà­à¬•à¬° ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨à¬° ବà­à¬°à¬¾à¬‰à¬œà¬¿à¬‚ କାରà­à¬¯à­à­Ÿà¬•à¬³à¬¾à¬ª ଲୋକମାନଙà­à¬•à¬° କେଉଠବଡ଼ ଗୋଷà­à¬ à­€ କିମà­à¬¬à¬¾ "ଦଳ" ସହ ସବà­à¬ à¬¾à¬°à­ ଅଧିକ ସମାନ ଅଟେ ତାହା Chrome ନିରà­à¬¦à­à¬§à¬¾à¬°à¬£ କରେ। ବିଜà­à¬žà¬¾à¬ªà¬¨à¬¦à¬¾à¬¤à¬¾à¬®à¬¾à¬¨à­‡ ଗୋଷà­à¬ à­€ ପାଇଠବିଜà­à¬žà¬¾à¬ªà¬¨à¬—à­à­œà¬¿à¬• ବାଛିପାରିବେ à¬à¬¬à¬‚ ଆପଣଙà­à¬• ବà­à¬°à¬¾à¬‰à¬œà¬¿à¬‚ କାରà­à¬¯à­à­Ÿà¬•à¬³à¬¾à¬ª ଆପଣଙà­à¬• ଡିଭାଇସରେ ବà­à­Ÿà¬•à­à¬¤à¬¿à¬—ତ ରଖାଯାà¬à¥¤ ଆପଣଙà­à¬• ଗୋଷà­à¬ à­€à¬•à­ ପà­à¬°à¬¤à¬¿ {NUM_DAYS} ଦିନରେ ଅପଡେଟୠକରାଯାà¬à¥¤}}</translation>
-<translation id="2053553514270667976">ZIP କୋଡà­</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1ଟି ପରାମରà­à¬¶}other{#ଟି ପରାମରà­à¬¶}}</translation>
+<translation id="2066915425250589881">ଡିଲିଟ କରିବାର ଅନà­à¬°à­‹à¬§</translation>
<translation id="2068528718802935086">ଶିଶୠà¬à¬¬à¬‚ ଛୋଟ ପିଲାମାନେ</translation>
<translation id="2071156619270205202">ଭରà­à¬šà­à¬†à¬²à­ କାରà­à¬¡ ନମà­à¬¬à¬° ପାଇଠà¬à¬¹à¬¿ କାରà­à¬¡à¬Ÿà¬¿ ଯୋଗà­à­Ÿ ନà­à¬¹à­‡à¬à¥¤</translation>
<translation id="2071692954027939183">ଆପଣ ସାଧାରଣତଃ ବିଜà­à¬žà¬ªà­à¬¤à¬¿à¬—à­à­œà¬¿à¬•à­ ଅନà­à¬®à¬¤à¬¿ ଦେଉନଥିବା ଯୋଗà­à¬ ସେଗà­à­œà¬¿à¬•à­ ସà­à­±à¬šà¬¾à¬³à¬¿à¬¤ ଭାବରେ ବà­à¬²à¬•à­ କରାଯାଇଛି</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">'ସିଙà­à¬•à­ ପରିଚାଳନା କରନà­à¬¤à­' ବଟନà­, Chrome ସେଟିଂସରେ ଆପଣ କେଉଠସୂଚନା ସିଙà­à¬•à­ କରନà­à¬¤à¬¿ ତାହା ପରିଚାଳନା କରିବାକୠEnter ଦବାନà­à¬¤à­</translation>
<translation id="2091887806945687916">ଧà­à­±à¬¨à¬¿</translation>
<translation id="2094505752054353250">ଡୋମେନୠମେଳ ଖାଉ ନାହିà¬</translation>
-<translation id="2096368010154057602">ବିଭାଗ</translation>
<translation id="2099652385553570808">ବାମ ପଟରେ ତିନୋଟି ଷà­à¬Ÿà¬¾à¬ªà¬²à­</translation>
<translation id="2101225219012730419">ସଂସà­à¬•à¬°à¬£:</translation>
<translation id="2102134110707549001">ବଳିଷà­à¬  ପାସà­â€à­±à¬°à­à¬¡ ପାଇଠପà­à¬°à¬¸à­à¬¤à¬¾à¬¬ ଦିଅନà­à¬¤à­â€¦</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">ଆମେରିକାନ ଫà­à¬Ÿà¬¬à¬²</translation>
<translation id="2187317261103489799">ଚିହà­à¬¨à¬Ÿ କରନà­à¬¤à­ (ଡିଫଲà­à¬Ÿ)</translation>
<translation id="2188375229972301266">ତଳ ପଟରେ à¬à¬•à¬¾à¬§à¬¿à¬• ପଞà­à¬šà­</translation>
-<translation id="2188852899391513400">ଆପଣ à¬à¬¬à­‡ ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରିଥିବା ପାସୱାରà­à¬¡ à¬à¬• ଡାଟା ଉଲà­à¬²à¬™à­à¬˜à¬¨à¬°à­‡ ମିଳିଛି। ଆପଣଙà­à¬• ଆକାଉଣà­à¬Ÿà¬—à­à­œà¬¿à¬•à­ ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ରଖିବା ପାଇà¬, ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨ à¬à¬¹à¬¿ ପାସୱାରà­à¬¡à¬•à­ ପରିବରà­à¬¤à­à¬¤à¬¨ କରିବା à¬à¬¬à¬‚ ତା’ପରେ ଆପଣଙà­à¬• ଦà­à­±à¬¾à¬°à¬¾ ସେଭ କରାଯାଇଥିବା ପାସୱାରà­à¬¡à¬—à­à­œà¬¿à¬•à¬° ଯାଞà­à¬š କରିବାକୠGoogle ପାସୱାରà­à¬¡ ମà­à­Ÿà¬¾à¬¨à­‡à¬œà¬° ସà­à¬ªà¬¾à¬°à¬¿à¬¶ କରେ।</translation>
<translation id="219906046732893612">ଘରର ଉନà­à¬¨à¬¤à¬¿</translation>
<translation id="2202020181578195191">ସମୟସୀମା ଶେଷ ହେଉଥିବା à¬à¬• ବୈଧ ବରà­à¬· ଲେଖନà­à¬¤à­</translation>
<translation id="22081806969704220">ଟà­à¬°à­‡ 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">ଫାଇଲୠà¬à¬¡à¬¿à¬Ÿà¬¿à¬‚</translation>
<translation id="2215963164070968490">କà­à¬•à­à¬°</translation>
<translation id="2218879909401188352">ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨ ଆକà­à¬°à¬®à¬£à¬•à¬¾à¬°à­€à¬®à¬¾à¬¨à­‡ <ph name="BEGIN_BOLD" /><ph name="SITE" />ରେ<ph name="END_BOLD" /> ବିପଦପୂରà­à¬£à­à¬£ ଆପà­â€à¬—à­à­œà¬¿à¬•à­ ଇନà­â€à¬·à­à¬Ÿà¬²à­ କରିପାରନà­à¬¤à¬¿ ଯାହା, ଆପଣଙà­à¬• ଡିଭାଇସà­â€à¬° କà­à¬·à¬¤à¬¿ କରିପାରେ, ଆପଣଙà­à¬• ମୋବାଇଲୠବିଲà­â€â€à¬°à­‡ ଗà­à¬ªà­à¬¤ ଶà­à¬³à­à¬•à¬—à­à­œà¬¿à¬•à­ ଯୋଗ କରିପାରେ କିମà­à¬¬à¬¾ ଆପଣଙà­à¬•à¬° ବà­à­Ÿà¬•à­à¬¤à¬¿à¬—ତ ସୂଚନା ଚୋରି କରିପାରେ। <ph name="BEGIN_LEARN_MORE_LINK" />ଅଧିକ ଜାଣନà­à¬¤à­<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ଟà­à­Ÿà­à¬Ÿà­‹à¬°à¬¿à¬†à¬² ରିଷà­à¬Ÿà¬¾à¬°à­à¬Ÿ କରନà­à¬¤à­</translation>
+<translation id="2219735899272417925">ଡିଭାଇସକୠରିସେଟ କରିବା ଆବଶà­à­Ÿà¬•</translation>
<translation id="2224337661447660594">କୌଣସି ଇଣà­à¬Ÿà¬°à­à¬¨à­‡à¬Ÿà­â€ ନାହିà¬</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />ଡାଇଗà­à¬¨à­‹à¬·à­à¬Ÿà¬¿à¬•à­à¬¸ ଆପà­<ph name="END_LINK" /> ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରି ଆପଣଙà­à¬• ସଂଯୋଗ ସମସà­à­Ÿà¬¾à¬° ସମାଧାନ କରନà­à¬¤à­</translation>
<translation id="2239100178324503013">ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨ ପଠାନà­à¬¤à­</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">ଅନà­à¬®à¬¤à¬¿ ଦିଆଯାଇନାହିଠ(ଡିଫଲà­à¬Ÿ)</translation>
<translation id="2512413427717747692">Chromeକୠଡିଫଲà­à¬Ÿ ବà­à¬°à¬¾à¬‰à¬œà¬° ବଟନ ଭାବେ ସେଟ କରନà­à¬¤à­, iOS ସେଟିଂସରେ ସିଷà­à¬Ÿà¬®à¬° ଡିଫଲà­à¬Ÿ ବà­à¬°à¬¾à¬‰à¬œà¬° ଭାବେ ସେଟ କରିବା ପାଇଠEnter ଦବାନà­à¬¤à­</translation>
<translation id="2515629240566999685">ଆପଣଙà­à¬• କà­à¬·à­‡à¬¤à­à¬°à¬°à­‡ ଥିବା ସିଗନାଲà­â€à¬•à­ ଯାଞà­à¬š କରାଯାଉଛି</translation>
+<translation id="2515761554693942801">ଆପଣ <ph name="PROVIDER_ORIGIN" /> ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରà­à¬¥à¬¿à¬¬à¬¾ ୱେବସାଇଟଗà­à­œà¬¿à¬•à¬°à­‡ ଟଚ ID ମାଧà­à­Ÿà¬®à¬°à­‡ ଯାଞà­à¬š କରିବାକୠବାଛିଛନà­à¬¤à¬¿à¥¤ à¬à¬¹à¬¿ ପà­à¬°à¬¦à¬¾à¬¨à¬•à¬¾à¬°à­€ ଆପଣଙà­à¬• ପେମେଣà­à¬Ÿ ପଦà­à¬§à¬¤à¬¿ ବିଷୟରେ ସୂଚନା ଷà­à¬Ÿà­‹à¬° କରିଥାଇପାରନà­à¬¤à¬¿, ଯାହାକୠଆପଣ <ph name="LINK_TEXT" /> କରିପାରିବେ।</translation>
<translation id="2521385132275182522">ତଳ ଡାହାଣ ପଟରେ ଷà­à¬Ÿà¬¾à¬ªà¬²à­</translation>
<translation id="2521736961081452453">ଫରà­à¬® ତିଆରି କରନà­à¬¤à­</translation>
<translation id="2523886232349826891">କେବଳ à¬à¬¹à¬¿ ଡିଭାଇସà­â€à¬°à­‡ ସେଭୠକରାଯାଇଛି</translation>
<translation id="2524461107774643265">ଅଧିକ ସୂଚନା ଯୋଗ କରନà­à¬¤à­</translation>
<translation id="2529899080962247600">à¬à¬¹à¬¿ ଫିଲà­à¬¡à¬°à­‡ <ph name="MAX_ITEMS_LIMIT" />ରୠଅଧିକ à¬à¬£à­à¬Ÿà­à¬°à¬¿ ରହିବା ଉଚିତ ନà­à¬¹à­‡à¬à¥¤ ସମସà­à¬¤ ପରବରà­à¬¤à­à¬¤à­€ à¬à¬£à­à¬Ÿà­à¬°à¬¿à¬•à­ ଅଣଦେଖା କରାଯିବ।</translation>
+<translation id="253493526287553278">ପà­à¬°à¬®à­‹ କୋଡ ବିବରଣୀ ଦେଖନà­à¬¤à­</translation>
<translation id="2535585790302968248">ବà­à­Ÿà¬•à­à¬¤à¬¿à¬—ତ ଭାବେ ବà­à¬°à¬¾à¬‰à¬œ କରିବାକୠà¬à¬• ନୂଆ ଇନକଗà­à¬¨à¬¿à¬Ÿà­‹ ଟାବ ଖୋଲନà­à¬¤à­</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{à¬à¬¬à¬‚ ଅଧିକ 1ଟି}other{à¬à¬¬à¬‚ ଅଧିକ #ଟି}}</translation>
<translation id="2536110899380797252">ଠିକଣା ଯୋଗ କରନà­à¬¤à­</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">ଫଟୋଗà­à¬°à¬¾à¬«à¬¿à¬• à¬à¬¬à¬‚ ଡିଜିଟାଲ ଆରà­à¬Ÿà­à¬¸</translation>
<translation id="2601150049980261779">ରୋମାନà­à¬¸ ମà­à¬­à¬¿à¬—à­à­œà¬¿à¬•</translation>
<translation id="2604589665489080024">ପପ ମà­à­Ÿà­à¬œà¬¿à¬•</translation>
-<translation id="2609632851001447353">ବିବିଧତା</translation>
<translation id="2610561535971892504">କପି କରିବାକୠକà­à¬²à¬¿à¬•à­ କରନà­à¬¤à­</translation>
<translation id="2617988307566202237">Chrome ନିମà­à¬¨à­‹à¬•à­à¬¤ ସୂଚନାକୠ<ph name="BEGIN_EMPHASIS" />ସେଭ କରିବ ନାହିà¬<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">ଜନà­à¬®à¬¦à¬¿à¬¨ à¬à¬¬à¬‚ ନାମ ଦିନଗà­à­œà¬¿à¬•</translation>
<translation id="2677748264148917807">ଛାଡ଼ନà­à¬¤à­</translation>
+<translation id="2679714844901977852">ସà­à¬°à¬•à­à¬·à¬¿à¬¤ à¬à¬¬à¬‚ ଶୀଘà­à¬° ଚେକଆଉଟ ପାଇଠଆପଣଙà­à¬•à¬° କାରà­à¬¡ ଓ ବିଲିଂ ସୂଚନାକୠଆପଣଙà­à¬• Google ଆକାଉଣà­à¬Ÿ <ph name="USER_EMAIL" />ରେ ସେଭ କରନà­à¬¤à­</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">ଉତà­à¬¤à¬® ଫିଟà­</translation>
<translation id="2688969097326701645">ହà¬, ଜାରି ରଖନà­à¬¤à­</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">ଇଣà­à¬Ÿà¬°à¬¨à­‡à¬Ÿ ସେବା ପà­à¬°à¬¦à¬¾à¬¨à¬•à¬¾à¬°à­€à¬—à­à­œà¬¿à¬• (ISPs)</translation>
<translation id="2856444702002559011">ଆକà­à¬°à¬®à¬£à¬•à¬¾à¬°à­€ ହà­à¬à¬¤ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ରୠଆପଣଙà­à¬•à¬° ସୂଚନା ଚୋରାଇବାକୠଚେଷà­à¬Ÿà¬¾ କରିପାରନà­à¬¤à¬¿ (ଉଦାହରଣ ସà­à­±à¬°à­‚ପ, ପାସà­â€à­±à¬°à­à¬¡, ମେସେଜୠକିମà­à¬¬à¬¾ କà­à¬°à­‡à¬¡à¬¿à¬Ÿà­â€ କାରà­à¬¡)। <ph name="BEGIN_LEARN_MORE_LINK" />ଅଧିକ ଜାଣନà­à¬¤à­<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">à¬à¬¹à¬¿ ସାଇଟୠଅନଧିକାର ପà­à¬°à¬¬à­‡à¬¶ କରିଥିବା କିମà­à¬¬à¬¾ ବିଭà­à¬°à¬¾à¬¨à­à¬¤à¬¿à¬•à¬° ବିଜà­à¬žà¬¾à¬ªà¬¨ ପà­à¬°à¬¦à¬°à­à¬¶à¬¨ କରà­à¬›à¬¿à¥¤</translation>
-<translation id="286512204874376891">ଆପଣଙà­à¬•à­ ସମà­à¬­à¬¾à¬¬à­à­Ÿ ଠକାମୀରୠରକà­à¬·à¬¾ କରିବାରେ ସାହାଯà­à­Ÿ କରିବା ପାଇà¬, à¬à¬• ଭରà­à¬šà­à¬†à¬² କାରà­à¬¡ ଆପଣଙà­à¬• ପà­à¬°à¬•à­ƒà¬¤ କାରà­à¬¡à¬° ସୂଚନାକୠଲà­à¬šà¬¾à¬‡à¬¥à¬¾à¬à¥¤ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ବନà­à¬§à­à¬¤à­à¬µà¬ªà­‚ରà­à¬£à­à¬£</translation>
<translation id="28761159517501904">ମà­à¬­à¬¿à¬—à­à­œà¬¿à¬•</translation>
<translation id="2876489322757410363">à¬à¬• à¬à¬•à­à¬¸à¬Ÿà¬°à­à¬¨à¬²à­ ଆପà­à¬²à¬¿à¬•à­‡à¬¸à¬¨à­ ମାଧà­à­Ÿà¬®à¬°à­‡ ପେମେଣà­à¬Ÿ କରିବାକୠଇନକଗà­à¬¨à¬¿à¬Ÿà­‹ ମୋଡରୠବାହାରି ଯାଉଛନà­à¬¤à¬¿à¥¤ ଜାରି ରଖିବେ?</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">ଡିସà­à¬•</translation>
<translation id="3162559335345991374">ଆପଣ ଯେଉଠୱାଇ-ଫାଇ ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରà­à¬›à¬¨à­à¬¤à¬¿, ଆପଣଙà­à¬•à­ à¬à¬¹à¬¾à¬° ଲଗୠଇନୠପୃଷà­à¬ à¬¾à¬•à­ ଯିବାର ଆବଶà­à­Ÿà¬•à¬¤à¬¾ ହୋଇପାରେ।</translation>
<translation id="3169472444629675720">ଖୋଜି ପାଆନà­à¬¤à­</translation>
-<translation id="3174168572213147020">ଦà­à­±à­€à¬ª</translation>
<translation id="3176929007561373547">ପà­à¬°à¬•à­à¬¸à¬¿ ସରà­à¬­à¬° କାରà­à¬¯à­à­Ÿà¬•à­à¬·à¬® ଥିବା ନିଶà­à¬šà¬¿à¬¤ କରିବାକୠଆପଣଙà­à¬•à¬° ପà­à¬°à¬•à­à¬¸à¬¿ ସେଟିଂସୠଯାଞà­à¬š କରନà­à¬¤à­ କିମà­à¬¬à¬¾ ନିଜର ନେଟà­â€Œà­±à¬°à­à¬• ବà­à­Ÿà¬¬à¬¸à­à¬¥à¬¾à¬ªà¬•à¬™à­à¬• ସହିତ ଯୋଗାଯୋଗ କରନà­à¬¤à­à¥¤ ଯଦି à¬à¬• ପà­à¬°à¬•à­à¬¸à¬¿ ସରà­à¬­à¬° ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରିବାକୠଆପଣ ଉଚିତୠମନେକରà­à¬¨à¬¾à¬¹à¬¾à¬¨à­à¬¤à¬¿:
<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">ଆପଣ କେତେବେଳେ à¬à¬¹à¬¿ ଡିଭାଇସକୠସକà­à¬°à¬¿à­Ÿ ଭାବରେ ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରà­à¬›à¬¨à­à¬¤à¬¿ ତାହା ଜାଣିବାକୠଚାହà­à¬à¬›à¬¿</translation>
@@ -875,9 +880,6 @@
<translation id="3369192424181595722">ଘଣà­à¬Ÿà¬¾à¬°à­‡ ତà­à¬°à­à¬Ÿà¬¿</translation>
<translation id="3369459162151165748">ଗାଡ଼ି ପାରà­à¬Ÿ à¬à¬¬à¬‚ ଆକସେସୋରୀଗà­à¬¡à¬¼à¬¿à¬•</translation>
<translation id="3371064404604898522">Chromeକୠଡିଫଲà­à¬Ÿ ବà­à¬°à¬¾à¬‰à¬œà¬° ଭାବେ ସେଟ କରନà­à¬¤à­</translation>
-<translation id="3371076217486966826"><ph name="URL" /> à¬à¬¹à¬¾ କରିବାକୠଚାହà­à¬à¬›à¬¿:
- • ଆପଣଙà­à¬• ପରିପାରà­à¬¶à­à­±à¬° à¬à¬• 3D ମà­à­Ÿà¬¾à¬ªà­ ତିଆରି କରି କà­à­Ÿà¬¾à¬®à­‡à¬°à¬¾à¬° ସà­à¬¥à¬¿à¬¤à¬¿ ଟà­à¬°à¬¾à¬•à­ କରିବା
- • ଆପଣଙà­à¬• କà­à­Ÿà¬¾à¬®à­‡à¬°à¬¾ ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରିବା</translation>
<translation id="337363190475750230">ପà­à¬°à¬¾à¬¬à¬§à¬¾à¬¨ କରାଯାଇନାହିà¬</translation>
<translation id="3375754925484257129">Chrome ସà­à¬°à¬•à­à¬·à¬¾ ଯାଞà­à¬š ଚଲାନà­à¬¤à­</translation>
<translation id="3377144306166885718">ସରà­à¬­à¬°à¬Ÿà¬¿ TLSର à¬à¬• ଅପà­à¬°à¬šà¬³à¬¿à¬¤ ସଂସà­à¬•à¬°à¬£ ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରିଛି।</translation>
@@ -894,6 +896,7 @@
<translation id="3399952811970034796">ବିତରଣ ଠିକଣା</translation>
<translation id="3402261774528610252">à¬à¬¹à¬¿ ସାଇଟକୠଲୋଡୠକରିବା ପାଇଠବà­à­Ÿà¬¬à¬¹à­ƒà¬¤ ସଂଯୋଗ TLS 1.0 କିମà­à¬¬à¬¾ TLS 1.1ର ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରିଛି, ଯେଉà¬à¬—à­à¬¡à¬¼à¬¿à¬•à­ ଅଗà­à¬°à¬¾à¬¹à­à­Ÿ କରାଯାଇଛି à¬à¬¬à¬‚ ଭବିଷà­à­Ÿà¬¤à¬°à­‡ à¬à¬¹à¬¾ ଅକà­à¬·à¬® ହୋଇଯିବ। ଥରେ ଅକà­à¬·à¬® ହୋଇଗଲା ପରେ, ଉପଯୋଗକରà­à¬¤à­à¬¤à¬¾à¬®à¬¾à¬¨à¬™à­à¬•à­ à¬à¬¹à¬¿ ସାଇଟୠଲୋଡୠକରିବାରୠପà­à¬°à¬¤à¬¿à¬°à­‹à¬§ କରାଯିବ। TLS 1.2 କିମà­à¬¬à¬¾ ତା'ପରର ସଂସà­à¬•à¬°à¬£à¬•à­ ସରà­à¬­à¬° ସକà­à¬·à¬® କରିବା ଉଚିତ।</translation>
<translation id="3405664148539009465">ଫଣà­à¬Ÿà¬—à­à­œà¬¿à¬•à­ କଷà­à¬Ÿà¬®à¬¾à¬‡à¬œà­ କରନà­à¬¤à­</translation>
+<translation id="3407789382767355356">ତୃତୀୟ-ପକà­à¬· ସାଇନ-ଇନ</translation>
<translation id="3409896703495473338">ସà­à¬°à¬•à­à¬·à¬¾ ସେଟିଂସକୠପରିଚାଳନା କରନà­à¬¤à­</translation>
<translation id="3414952576877147120">ଆକାର:</translation>
<translation id="3417660076059365994">ଆପଣ ଅପଲୋଡୠକିମà­à¬¬à¬¾ ଆଟାଚୠକରିଥିବା ଫାଇଲଗà­à­œà¬¿à¬•à­ ବିଶà­à¬³à­‡à¬·à¬£ ପାଇଠGoogle Cloud କିମà­à¬¬à¬¾ ତୃତୀୟ ପକà­à¬·à¬—à­à­œà¬¿à¬•à­ ପଠାଯାà¬à¥¤ ଉଦାହରଣ ସà­à¬µà¬°à­‚ପ, ସେଗà­à­œà¬¿à¬•à­ ସମà­à¬¬à­‡à¬¦à¬¨à¬¶à­€à¬³ ଡାଟା କିମà­à¬¬à¬¾ ମାଲୱେରୠପାଇଠସà­à¬•à¬¾à¬¨à­ କରାଯାଇପାରେ।</translation>
@@ -926,6 +929,7 @@
<translation id="3477679029130949506">ମà­à¬­à¬¿ ତାଲିକା à¬à¬¬à¬‚ ଥିà¬à¬Ÿà¬° ଶୋ'ଟାଇମଗà­à­œà¬¿à¬•</translation>
<translation id="3479552764303398839">à¬à¬¬à­‡ ନà­à¬¹à­‡à¬</translation>
<translation id="3484560055331845446">ଆପଣ, ନିଜର Google ଆକାଉଣà­à¬Ÿà¬° ଆକà­à¬¸à­‡à¬¸à­â€Œ ହରାଇପାରନà­à¬¤à¬¿à¥¤ ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨ Chrome ଆପଣଙà­à¬•à­ ପାସà­â€Œà­±à¬¾à¬°à­à¬¡ ପରିବରà­à¬¤à­à¬¤à¬¨ କରିବା ପାଇଠସà­à¬ªà¬¾à¬°à¬¿à¬¶ କରà­à¬›à¬¿à¥¤ ଆପଣଙà­à¬•à­ ସାଇନà­â€Œ ଇନୠପାଇଠକà­à¬¹à¬¾à¬¯à¬¿à¬¬à¥¤</translation>
+<translation id="3484861421501147767">ରିମାଇଣà­à¬¡à¬°: ସେଭ କରାଯାଇଥିବା ପà­à¬°à¬®à­‹ କୋଡ ଉପଲବà­à¬§ ଅଛି</translation>
<translation id="3487845404393360112">ଟà­à¬°à­‡ 4</translation>
<translation id="3495081129428749620">ପୃଷà­à¬ à¬¾à¬°à­‡ ଖୋଜନà­à¬¤à­
<ph name="PAGE_TITLE" /></translation>
@@ -1049,6 +1053,7 @@
<translation id="3810973564298564668">ପରିଚାଳନା</translation>
<translation id="3816482573645936981">ମୂଲà­à­Ÿ (à¬à¬¹à¬¾à¬° ସà­à¬¥à¬¾à¬¨ ଅଧିକାର କରାଯାଇଛି)</translation>
<translation id="382518646247711829">ଯଦି ଆପଣ à¬à¬• ପà­à¬°à¬•à­à¬¸à¬¿ ସରà­à¬­à¬° ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରନà­à¬¤à¬¿...</translation>
+<translation id="3826050100957962900">ତୃତୀୟ-ପକà­à¬· ସାଇନ-ଇନ</translation>
<translation id="3827112369919217609">ସମà­à¬ªà­‚ରà­à¬£à­à¬£</translation>
<translation id="3827666161959873541">ପାରିବାରିକ ମà­à¬­à¬¿à¬—à­à­œà¬¿à¬•</translation>
<translation id="3828924085048779000">ଶୂନà­à­Ÿ ପାସà­â€Œà¬«à­à¬°à­‡à¬œà­â€Œà¬° ଅନà­à¬®à¬¤à¬¿ ନାହିà¬à¥¤</translation>
@@ -1061,9 +1066,9 @@
<translation id="3858027520442213535">ତାରିଖ ଓ ସମୟ ଅପà­â€Œà¬¡à­‡à¬Ÿà­â€Œ କରନà­à¬¤à­</translation>
<translation id="3858860766373142691">ନାମ</translation>
<translation id="3872834068356954457">ବିଜà­à¬žà¬¾à¬¨</translation>
+<translation id="3875783148670536197">କିପରି କରାଯାଠତାହା ମୋତେ ଦେଖାନà­à¬¤à­</translation>
<translation id="3881478300875776315">କମୠଲାଇନୠଦେଖାନà­à¬¤à­</translation>
<translation id="3884278016824448484">ପରସà­à¬ªà¬° ବିରୋଧୀ ଡିଭାଇସୠଚିହà­à¬¨à¬Ÿà¬•à¬¾à¬°à­€</translation>
-<translation id="3885155851504623709">ପାରିସà­</translation>
<translation id="388632593194507180">ନିରୀକà­à¬·à¬£ କରାଯାଉଥିବା ଚିହà­à¬¨à¬Ÿ କରାଯାଇଛି</translation>
<translation id="3886948180919384617">ଷà­à¬Ÿà¬¾à¬•à¬°à­ 3</translation>
<translation id="3890664840433101773">ଇମେଲୠଯୋଗ କରନà­à¬¤à­</translation>
@@ -1093,9 +1098,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" />କୠବà­à¬²à¬•à­ କରାଯାଇଛି</translation>
<translation id="3978338123949022456">ସନà­à¬§à¬¾à¬¨ ମୋଡ, à¬à¬• କà­à­±à­‡à¬°à­€ ଟାଇପ କରନà­à¬¤à­ à¬à¬¬à¬‚ <ph name="KEYWORD_SUFFIX" /> ମାଧà­à­Ÿà¬®à¬°à­‡ ସନà­à¬§à¬¾à¬¨ କରିବାକୠEnter ଦବାନà­à¬¤à­</translation>
<translation id="398470910934384994">ପକà­à¬·à­€à¬—à­à­œà¬¿à¬•</translation>
+<translation id="3985750352229496475">ଠିକଣାଗà­à­œà¬¿à¬•à­ ପରିଚାଳନା କରନà­à¬¤à­...</translation>
<translation id="3986705137476756801">ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨ ପାଇଠଲାଇଭୠକà­à­Ÿà¬¾à¬ªà­à¬¸à¬¨à­ ବନà­à¬¦ କରନà­à¬¤à­</translation>
<translation id="3987940399970879459">1 MBରୠକମୠଅଛି</translation>
<translation id="3990250421422698716">ଜଗୠଅଫà­â€Œà¬¸à­‡à¬Ÿà­</translation>
+<translation id="3992684624889376114">à¬à¬¹à¬¿ ପୃଷà­à¬ à¬¾ ବିଷୟରେ</translation>
<translation id="3996311196211510766">à¬à¬‡ ସାଇଟକୠକରାଯାଇଥିବା ସମସà­à¬¤ ଅନà­à¬°à­‹à¬§à¬°à­‡ à¬à¬• ମୂଳ ନୀତି ଲାଗୠକରାଯାଉ ବୋଲି à¬à¬¹à¬¿ <ph name="ORIGIN" /> ସାଇଟୠଅନà­à¬°à­‹à¬§ କରିଛି, କିନà­à¬¤à­ ଠନୀତି ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨ ଲାଗୠକରାଯାଇପାରିବ ନାହିà¬à¥¤</translation>
<translation id="4006465311664329701">Google Pay ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରà­à¬¥à¬¿à¬¬à¬¾ ପେମେଣà­à¬Ÿ ପଦà­à¬§à¬¤à¬¿, ଅଫର à¬à¬¬à¬‚ ଠିକଣାଗà­à¬¡à¬¼à¬¿à¬•</translation>
<translation id="4009243425692662128">ଆପଣ ପà­à¬°à¬¿à¬£à­à¬Ÿ କରà­à¬¥à¬¿à¬¬à¬¾ ପୃଷà­à¬ à¬¾à¬—à­à­œà¬¿à¬•à¬° ବିଷୟବସà­à¬¤à­ ବିଶà­à¬³à­‡à¬·à¬£ ପାଇଠGoogle Cloud କିମà­à¬¬à¬¾ ତୃତୀୟ ପକà­à¬·à¬—à­à­œà¬¿à¬•à­ ପଠାଯାà¬à¥¤ ଉଦାହରଣ ସà­à¬µà¬°à­‚ପ, à¬à¬¹à¬¾à¬•à­ ସମà­à¬¬à­‡à¬¦à¬¨à¬¶à­€à¬³ ଡାଟା ପାଇଠସà­à¬•à¬¾à¬¨ କରାଯାଇପାରେ।</translation>
@@ -1215,6 +1222,7 @@
<translation id="4305666528087210886">ଆପଣଙà­à¬• ଫାଇଲକୠଆକà­à¬¸à­‡à¬¸à­ କରାଯାଇପାରିଲା ନାହିà¬</translation>
<translation id="4306529830550717874">ଠିକଣା ସେଭୠକରିବେ?</translation>
<translation id="4306812610847412719">କà­à¬²à¬¿à¬ªà¬¬à­‹à¬°à­à¬¡</translation>
+<translation id="4310070645992025887">ଆପଣଙà­à¬• ଯାତà­à¬°à¬¾à¬—à­à­œà¬¿à¬•à­ ସନà­à¬§à¬¾à¬¨ କରନà­à¬¤à­</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">ବà­à¬²à¬•à­ କରନà­à¬¤à­ (ଡିଫଲà­à¬Ÿ)</translation>
<translation id="4314815835985389558">ସିଙà­à¬• ପରିଚାଳନା କରନà­à¬¤à­</translation>
@@ -1265,6 +1273,7 @@
<translation id="4435702339979719576">ପୋଷà­à¬Ÿà¬•à¬¾à¬°à­à¬¡)</translation>
<translation id="443673843213245140">à¬à¬• ପà­à¬°à­‹à¬•à­à¬¸à¬¿à¬° ବà­à­Ÿà¬¬à¬¹à¬¾à¬° ଅକà­à¬·à¬® କରାଯାଇଛି କିନà­à¬¤à­ à¬à¬• ସà­à¬¨à¬¿à¬°à­à¬¦à­à¬¦à¬¿à¬·à­à¬Ÿ ପà­à¬°à¬•à­à¬¸à¬¿ କନà­â€Œà¬«à¬¿à¬—à­â€Œà¬°à­‡à¬¸à¬¨à­ ନିରà­à¬¦à­à¬¦à¬¿à¬·à­à¬Ÿ କରାଯାଇଛି।</translation>
<translation id="4441832193888514600">à¬à¬¹à¬¿ ନୀତିକୠକେବଳ à¬à¬• କà­à¬²à¬¾à¬‰à¬¡ ଉପଯୋଗକରà­à¬¤à­à¬¤à¬¾ ନୀତି ଭାବରେ ସେଟ କରାଯାଇପାରà­à¬¥à¬¿à¬¬à¬¾ ଯୋଗà­à¬ à¬à¬¹à¬¾à¬•à­ ଅଣଦେଖା କରାଯାଇଛି।</translation>
+<translation id="4442470707340296952">Chrome ଟାବ</translation>
<translation id="4450893287417543264">ଆଉ ଦେଖାନà­à¬¤à­ ନାହିà¬</translation>
<translation id="4451135742916150903">HID ଡିଭାଇସଗà­à­œà¬¿à¬• ସହ ସଂଯୋଗ କରିବାକୠସାଇଟୠପଚାରିପାରେ</translation>
<translation id="4452328064229197696">ଆପଣ à¬à¬¬à­‡ ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରିଥିବା ପାସୱାରà­à¬¡ à¬à¬• ଡାଟା ଉଲà­à¬²à¬™à­à¬˜à¬¨à¬°à­‡ ମିଳିଛି। ଆପଣଙà­à¬• ଆକାଉଣà­à¬Ÿà¬—à­à­œà¬¿à¬•à­ ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ରଖିବା ପାଇà¬, Google ପାସୱାରà­à¬¡ ମà­à­Ÿà¬¾à¬¨à­‡à¬œà¬° ଆପଣଙà­à¬• ଦà­à­±à¬¾à¬°à¬¾ ସେଭ କରାଯାଇଥିବା ପାସୱାରà­à¬¡à¬—à­à­œà¬¿à¬•à¬° ଯାଞà­à¬š କରିବାକୠସà­à¬ªà¬¾à¬°à¬¿à¬¶ କରେ।</translation>
@@ -1403,6 +1412,8 @@
<translation id="483241715238664915">ଚେତାବନୀ ଚାଲୠକରନà­à¬¤à­</translation>
<translation id="4834250788637067901">Google Pay ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରà­à¬¥à¬¿à¬¬à¬¾ ପେମେଣà­à¬Ÿ ପଦà­à¬§à¬¤à¬¿, ଅଫର à¬à¬¬à¬‚ ଠିକଣାଗà­à¬¡à¬¼à¬¿à¬•</translation>
<translation id="4838327282952368871">ଡà­à¬°à¬¿à¬®à¬¿</translation>
+<translation id="4839087176073128681">ପରବରà­à¬¤à­à¬¤à­€ ସମୟରେ ଶୀଘà­à¬° ପେମେଣà­à¬Ÿ କରନà­à¬¤à­ à¬à¬¬à¬‚ Googleର
+ଉଦà­à­Ÿà­‹à¬— ଜଗତର-ମà­à¬–à­à­Ÿ ସà­à¬°à¬•à­à¬·à¬¾ ସହ ଆପଣଙà­à¬• କାରà­à¬¡à¬•à­ ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ରଖନà­à¬¤à­à¥¤</translation>
<translation id="4840250757394056958">ଆପଣଙà­à¬• Chrome ଇତିହାସ ଦେଖନà­à¬¤à­</translation>
<translation id="484462545196658690">ସà­à­±à¬¤à¬ƒ</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> à¬à¬¬à¬‚ ଆହà­à¬°à¬¿ ଅନେକ କିଛିରେ ରିହାତି ପାଆନà­à¬¤à­</translation>
@@ -1465,6 +1476,7 @@
<translation id="5011561501798487822">ଚିହà­à¬¨à¬Ÿ କରାଯାଇଥିବା ଭାଷା</translation>
<translation id="5015510746216210676">ମେସିନୠନାମ:</translation>
<translation id="5017554619425969104">ଆପଣ କପି କରିଥିବା ଟେକà­à¬¸à¬Ÿà­</translation>
+<translation id="5017828934289857214">ମୋତେ ପରେ ରିମାଇଣà­à¬¡ କରନà­à¬¤à­</translation>
<translation id="5018422839182700155">à¬à¬¹à¬¿ ପୃଷà­à¬ à¬¾à¬•à­ ଖୋଲିପାରିବ ନାହିà¬</translation>
<translation id="5019198164206649151">ବà­à­Ÿà¬¾à¬•à¬¿à¬‚ ଷà­à¬Ÿà­‹à¬°à­â€Œ ଖରାପ ସà­à¬¥à¬¿à¬¤à¬¿à¬°à­‡ ଅଛି</translation>
<translation id="5020776957610079374">ୱାରà­à¬²à­à¬¡ ମà­à­Ÿà­à¬œà¬¿à¬•</translation>
@@ -1484,6 +1496,7 @@
<translation id="5051305769747448211">ଲାଇଭ କମେଡି</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Nearby Share ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରି à¬à¬¹à¬¿ ଫାଇଲ ପଠାଇବା ପାଇଠଆପଣଙà­à¬• ଡିଭାଇସରେ (<ph name="DISK_SPACE_SIZE" />) ସà­à¬ªà­‡à¬¸ ଖାଲି କରନà­à¬¤à­}other{Nearby Share ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରି à¬à¬¹à¬¿ ଫାଇଲଗà­à­œà¬¿à¬•à­ ପଠାଇବା ପାଇଠଆପଣଙà­à¬• ଡିଭାଇସରେ (<ph name="DISK_SPACE_SIZE" />) ସà­à¬ªà­‡à¬¸ ଖାଲି କରନà­à¬¤à­}}</translation>
<translation id="5056549851600133418">ଆପଣଙà­à¬• ପାଇଠଆରà­à¬Ÿà¬¿à¬•à¬²à­â€Œà¬—à­à¬¡à¬¼à¬¿à¬•</translation>
+<translation id="5060483733937416656">ଆପଣ <ph name="PROVIDER_ORIGIN" /> ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରà­à¬¥à¬¿à¬¬à¬¾ ୱେବସାଇଟଗà­à­œà¬¿à¬•à¬°à­‡ Windows Hello ମାଧà­à­Ÿà¬®à¬°à­‡ ଯାଞà­à¬š କରିବାକୠବାଛିଛନà­à¬¤à¬¿à¥¤ à¬à¬¹à¬¿ ପà­à¬°à¬¦à¬¾à¬¨à¬•à¬¾à¬°à­€ ଆପଣଙà­à¬• ପେମେଣà­à¬Ÿ ପଦà­à¬§à¬¤à¬¿ ବିଷୟରେ ସୂଚନା ଷà­à¬Ÿà­‹à¬° କରିଥାଇପାରନà­à¬¤à¬¿, ଯାହାକୠଆପଣ <ph name="LINK_TEXT" /> କରିପାରିବେ।</translation>
<translation id="5061227663725596739">ଆପଣ କ’ଣ <ph name="LOOKALIKE_DOMAIN" /> ବିଷୟରେ କହିବାକୠଚାହà­à¬à¬›à¬¨à­à¬¤à¬¿?</translation>
<translation id="5066056036849835175">ପà­à¬°à¬¿à¬£à­à¬Ÿà¬¿à¬‚ ଇତିହାସ</translation>
<translation id="5068234115460527047">ହେଜ ଫଣà­à¬¡à¬—à­à­œà¬¿à¬•</translation>
@@ -1497,10 +1510,8 @@
<translation id="5087286274860437796">ସରà­à¬­à¬°à­â€à¬° ସାରà­à¬Ÿà¬¿à¬«à¬¿à¬•à­‡à¬Ÿà­ à¬à¬¹à¬¿ ସମୟରେ ବୈଧ ନà­à¬¹à­‡à¬à¥¤</translation>
<translation id="5087580092889165836">କାରà­à¬¡ ଯୋଗ କରନà­à¬¤à­</translation>
<translation id="5088142053160410913">ଅପରେଟର ପାଇଠମେସେଜà­</translation>
-<translation id="5089810972385038852">ରାଜà­à­Ÿ</translation>
<translation id="5093232627742069661">Z-ଫୋଲà­à¬¡</translation>
<translation id="5094747076828555589">à¬à¬¹à¬¿ ସରà­à¬­à¬°à­ à¬à¬¹à¬¾ ପà­à¬°à¬®à¬¾à¬£ କରିପାରିଲା ନାହିଠଯେ, à¬à¬¹à¬¾ à¬à¬• <ph name="DOMAIN" /> ଅଟେ; à¬à¬¹à¬¾à¬° ସà­à¬°à¬•à­à¬·à¬¾ ସାରà­à¬Ÿà¬¿à¬«à¬¿à¬•à­‡à¬Ÿà­ Chromium ଦà­à­±à¬¾à¬°à¬¾ ବିଶà­à­±à¬¸à¬¨à­€à­Ÿ ନà­à¬¹à­‡à¬à¥¤ à¬à¬¹à¬¾ ହà­à¬à¬¤ à¬à¬• ଭà­à¬²à­ କନà­â€Œà¬«à¬¿à¬—à­â€â€à¬°à­‡à¬¸à¬¨à­ କିମà­à¬¬à¬¾ ଜଣେ ଆକà­à¬°à¬®à¬£à¬•à¬¾à¬°à­€ ଆପଣଙà­à¬•à¬° ସଂଯୋଗକୠପà­à¬°à¬¤à¬¿à¬°à­‹à¬§ କରà­à¬¥à¬¿à¬¬à¬¾ କାରଣରୠହୋଇପାରେ।</translation>
-<translation id="5095208057601539847">ପà­à¬°à¬—ଣା</translation>
<translation id="5097099694988056070">ଡିଭାଇସୠପରିସଂଖà­à­Ÿà¬¾à¬¨ ଯେପରି CPU/RAMର ବà­à­Ÿà¬¬à¬¹à¬¾à¬°</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">ସାଇଟୠସà­à¬°à¬•à­à¬·à¬¿à¬¤ ନà­à¬¹à­‡à¬</translation>
@@ -1538,6 +1549,7 @@
<translation id="5171045022955879922">ଖୋଜନà­à¬¤à­ କିମà­à¬¬à¬¾ URL ଟାଇପୠକରନà­à¬¤à­</translation>
<translation id="5171689220826475070">ଫà­à­Ÿà¬¾à¬¨à­â€Œà¬«à­‹à¬²à­à¬¡-à­Ÿà­à¬°à­‹à¬ªà¬¿à¬†à¬¨à­</translation>
<translation id="5172758083709347301">ମେସିନà­</translation>
+<translation id="5177076414499237632">à¬à¬¹à¬¿ ପୃଷà­à¬ à¬¾à¬° ସୋରà­à¬¸ à¬à¬¬à¬‚ ବିଷୟ ସମà­à¬¬à¬¨à­à¬§à¬°à­‡ ଜାଣନà­à¬¤à­</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />ରେ ନାହିà¬? à¬à¬¹à¬¿ ତà­à¬°à­à¬Ÿà¬¿à¬•à­ ରିପୋରà­à¬Ÿ କରନà­à¬¤à­</translation>
<translation id="518639307526414276">ଗୃହପାଳିତ ପଶà­à¬™à­à¬• ଖାଦà­à­Ÿ à¬à¬¬à¬‚ ଯତà­à¬¨ ପାଇଠସାମଗà­à¬°à­€</translation>
<translation id="5190835502935405962">ବà­à¬•à¬®à¬¾à¬°à­à¬• ବାରà­</translation>
@@ -1698,6 +1710,7 @@
<translation id="5624120631404540903">ପାସà­â€Œà­±à¬°à­à¬¡à¬—à­à¬¡à¬¼à¬¿à¬•à­ ପରିଚାଳନା କରନà­à¬¤à­</translation>
<translation id="5629630648637658800">ନୀତି ସେଟିଂସୠଲୋଡà­â€ କରିବାରେ ବିଫଳ ହେଲା</translation>
<translation id="5631439013527180824">ଅବୈଧ ଡିଭାଇସୠପରିଚାଳନା ଟୋକନà­</translation>
+<translation id="5632485077360054581">କିପରି କରାଯାଠତାହା ମୋତେ ଦେଖାନà­à¬¤à­</translation>
<translation id="5633066919399395251">à¬à¬¹à¬¿ ସମୟରେ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ରେ ଥିବା ଆକà­à¬°à¬®à¬£à¬•à¬¾à¬°à­€ ଆପଣଙà­à¬•à¬° କମà­à¬ªà­à­Ÿà­à¬Ÿà¬°à­â€Œà¬°à­‡ à¬à¬ªà¬°à¬¿ ବିପଦଜà­à¬œà¬¨à¬• ପà­à¬°à­‹à¬—à­à¬°à¬¾à¬®à­â€Œ ଇନà­â€Œà¬·à­à¬Ÿà¬²à­â€Œ କରିବାକୠଚେଷà­à¬Ÿà¬¾ କରିପାରନà­à¬¤à¬¿ ଯାହା ଆପଣଙà­à¬• ସୂଚନା (ଉଦାହରଣ ସà­à­±à¬°à­‚ପ ଫଟୋ, ପାସà­â€Œà­±à¬°à­à¬¡, ମେସେଜà­â€ କିମà­à¬¬à¬¾ କà­à¬°à­‡à¬¡à¬¿à¬Ÿà­â€Œ କାରà­à¬¡) ଚୋରି କରନà­à¬¤à¬¿ କିମà­à¬¬à¬¾ ଆପଣଙà­à¬•à¬° ସୂଚନା ଡିଲିଟà­â€Œ କରନà­à¬¤à¬¿à¥¤ <ph name="BEGIN_LEARN_MORE_LINK" />ଅଧିକ ଜାଣନà­à¬¤à­<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">ପà­à¬°à¬¤à¬¾à¬°à¬£à¬¾à¬®à­‚ଳକ ବିଷୟବସà­à¬¤à­ ବà­à¬²à¬•à­ କରାଯାଇଛି।</translation>
<translation id="5633259641094592098">କଲà­à¬Ÿ à¬à¬¬à¬‚ ଇଣà­à¬¡à¬¿ ମà­à¬­à¬¿à¬—à­à­œà¬¿à¬•</translation>
@@ -1815,6 +1828,7 @@
<translation id="5989320800837274978">ନା ସà­à¬¥à¬¿à¬° ପà­à¬°à¬•à­à¬¸à¬¿ ସରà­à¬­à¬°à­â€Œà¬—à­à¬¡à¬¼à¬¿à¬•à­ ନା à¬à¬• .pac ସà­à¬•à­à¬°à¬¿à¬ªà­à¬Ÿ URL ନିରà­à¬¦à­à¬¦à¬¿à¬·à­à¬Ÿ କରାଯାଇଛି।</translation>
<translation id="5992691462791905444">ଇଞà­à¬œà¬¿à¬¨à¬¿à­Ÿà¬°à¬¿à¬‚ Z-ଫୋଲà­à¬¡</translation>
<translation id="5995727681868049093">ଆପଣଙà­à¬• Google ଆକାଉଣà­à¬Ÿà¬°à­‡ ଆପଣଙà­à¬•à¬° ସୂଚନା, ଗୋପନୀୟତା à¬à¬¬à¬‚ ସà­à¬°à¬•à­à¬·à¬¾à¬•à­ ପରିଚାଳନା କରନà­à¬¤à­</translation>
+<translation id="5997247540087773573">ଆପଣ à¬à¬¬à­‡ ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରିଥିବା ପାସୱାରà­à¬¡ à¬à¬• ଡାଟା ଉଲà­à¬²à¬™à­à¬˜à¬¨à¬°à­‡ ମିଳିଛି। ଆପଣଙà­à¬• ଆକାଉଣà­à¬Ÿà¬—à­à­œà¬¿à¬•à­ ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ରଖିବା ପାଇà¬, ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨ à¬à¬¹à¬¿ ପାସୱାରà­à¬¡à¬•à­ ପରିବରà­à¬¤à­à¬¤à¬¨ କରିବାକୠà¬à¬¬à¬‚ ଆପଣଙà­à¬• ଦà­à­±à¬¾à¬°à¬¾ ସେଭ କରାଯାଇଥିବା ପାସୱାରà­à¬¡à¬—à­à­œà¬¿à¬•à¬° ଯାଞà­à¬š କରିବାକୠGoogle Password Manager ସà­à¬ªà¬¾à¬°à¬¿à¬¶ କରେ।</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' ପାଇଠ<ph name="RESULT_COUNT" />ଟି ପରିଣାମ</translation>
<translation id="6006484371116297560">କà­à¬²à¬¾à¬¸à¬¿à¬•à­</translation>
<translation id="6008122969617370890">N-ରà­-1 ଅରà­à¬¡à¬°à­</translation>
@@ -1909,7 +1923,6 @@
<translation id="627746635834430766">ପରବରà­à¬¤à­à¬¤à­€ ସମୟରେ ପୈଠ ପà­à¬°à¬•à­à¬°à¬¿à­Ÿà¬¾à¬•à­ ଦà­à¬°à­à¬¤à¬¤à¬° କରିବା ପାଇଠଆପଣଙà­à¬•à¬° Google ଆକାଉଣà­à¬Ÿà¬°à­‡ ଆପଣଙà­à¬• କାରà­à¬¡ ଓ ବିଲିଂ ଠିକଣା ସେଭୠକରନà­à¬¤à­à¥¤</translation>
<translation id="6279183038361895380">ଆପଣଙà­à¬•à¬° କରà­à¬¸à¬°à­â€Œ ଦେଖାଇବାକୠ|<ph name="ACCELERATOR" />| ଦବାନà­à¬¤à­</translation>
<translation id="6280223929691119688">à¬à¬¹à¬¿ ଠିକଣାକୠପହଞà­à¬šà¬¾à¬‡à¬ªà¬¾à¬°à¬¿à¬¬ ନାହିà¬à¥¤ à¬à¬• ଭିନà­à¬¨ ଠିକଣା ଚୟନ କରନà­à¬¤à­à¥¤</translation>
-<translation id="6282194474023008486">ପୋଷà­à¬Ÿà¬¾à¬²à­ କୋଡà­</translation>
<translation id="6285507000506177184">"Chromeରେ ଡାଉନଲୋଡଗà­à¬¡à¬¼à¬¿à¬•à­ ପରିଚାଳନା କରନà­à¬¤à­" ବଟନ, Chromeରେ ଆପଣ ଡାଉନଲୋଡ କରିଥିବା ଫାଇଲଗà­à¬¡à¬¼à¬¿à¬•à­ ପରିଚାଳନା କରିବା ପାଇଠEnter ଦବାନà­à¬¤à­</translation>
<translation id="6289939620939689042">ପୃଷà­à¬ à¬¾à¬° ରଙà­à¬—</translation>
<translation id="6290238015253830360">ଆପଣଙà­à¬• ଦà­à­±à¬¾à¬°à¬¾ ପରାମରà­à¬¶à¬¿à¬¤ ନିବନà­à¬§à¬—à­à­œà¬¿à¬• à¬à¬ à¬¾à¬°à­‡ ଦେଖାଯାà¬</translation>
@@ -1931,6 +1944,7 @@
<translation id="6337133576188860026"><ph name="SIZE" />ଠାରୠକମୠଖାଲି କରନà­à¬¤à¬¿à¥¤ କିଛି ଆପଣଙà­à¬•à¬° ପରବରà­à¬¤à­à¬¤à­€ ଭà­à¬°à¬®à¬£ ସମୟରେ ଆହà­à¬°à¬¿ ଧୀରେ ଲୋଡୠହୋଇପାରନà­à¬¤à¬¿à¥¤</translation>
<translation id="6337534724793800597">ନାମ ଅନà­à¬¯à¬¾à­Ÿà­€ ନୀତିଗà­à­œà¬¿à¬• ଫିଲà­à¬Ÿà¬°à­â€ କରନà­à¬¤à­</translation>
<translation id="6340739886198108203">ଗୋପନୀୟ ବିଷୟବସà­à¬¤à­ ଦେଖାଯାଉଥିବା ବେଳେ ସà­à¬•à­à¬°à¬¿à¬¨à¬¸à¬Ÿ ନେବା କିମà­à¬¬à¬¾ ରେକରà­à¬¡ କରିବା ପାଇଠଆଡମିନିଷà­à¬Ÿà­à¬°à­‡à¬Ÿà¬° ନୀତି ସà­à¬ªà¬¾à¬°à¬¿à¬¶ କରେ ନାହିà¬:</translation>
+<translation id="6348220984832452017">ସକà­à¬°à¬¿à­Ÿ ଭେରିà¬à¬¸à¬¨à¬—à­à­œà¬¿à¬•</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ଇନଷà­à¬Ÿà¬²à­â€ କରନà­à¬¤à­</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{କୌଣସିଟି ନà­à¬¹à­‡à¬}=1{1ଟି ପାସà­â€Œà­±à¬¾à¬°à­à¬¡ (<ph name="DOMAIN_LIST" /> ପାଇà¬, ସିଙà­à¬•à­ କରାଯାଇଛି)}=2{2ଟି ପାସà­â€Œà­±à¬¾à¬°à­à¬¡ (<ph name="DOMAIN_LIST" /> ପାଇà¬, ସିଙà­à¬•à­ କରାଯାଇଛି)}other{#ଟି ପାସà­â€Œà­±à¬¾à¬°à­à¬¡ (<ph name="DOMAIN_LIST" /> ପାଇà¬, ସିଙà­à¬•à­ କରାଯାଇଛି)}}</translation>
<translation id="6355392890578844978">à¬à¬¹à¬¿ ବà­à¬°à¬¾à¬‰à¬œà¬°à­ à¬à¬• କମà­à¬ªà¬¾à¬¨à­€ କିମà­à¬¬à¬¾ ଅନà­à­Ÿ ସଂସà­à¬¥à¬¾ ଦà­à­±à¬¾à¬°à¬¾ ପରିଚାଳିତ ହେଉନାହିà¬à¥¤ à¬à¬¹à¬¿ ଡିଭାଇସରେ କରାଯାଉଥିବା କାରà­à¬¯à­à­Ÿà¬•à¬³à¬¾à¬ª Chromium ବାହାରେ ପରିଚାଳନା କରାଯାଇପାରେ। <ph name="BEGIN_LINK" />ଅଧିକ ଜାଣନà­à¬¤à­<ph name="END_LINK" /></translation>
@@ -1962,7 +1976,6 @@
<translation id="643051589346665201">Google ପାସୱାରà­à¬¡ ପରିବରà­à¬¤à­à¬¤à¬¨ କରନà­à¬¤à­</translation>
<translation id="6433490469411711332">ଯୋଗାଯୋଗ ସୂଚନା à¬à¬¡à¬¿à¬Ÿà­â€ କରନà­à¬¤à­</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ସଂଯୋଗ କରିବାକୠଅସà­à­±à­€à¬•à¬¾à¬° କରିଛନà­à¬¤à¬¿à¥¤</translation>
-<translation id="6438025220577812695">ନିଜେ à¬à¬¹à¬¾à¬•à­ ପରିବରà­à¬¤à­à¬¤à¬¨ କରନà­à¬¤à­</translation>
<translation id="6440503408713884761">ଅଣଦେଖା କରାଗଲା</translation>
<translation id="6443406338865242315">ଆପଣ କେଉଠà¬à¬•à­à¬¸à¬Ÿà­‡à¬¨à¬¸à¬¨à­ à¬à¬¬à¬‚ ପà­à¬²à¬—ଇନଗà­à­œà¬¿à¬• ଇନଷà­à¬Ÿà¬²à­ କରିଛନà­à¬¤à¬¿</translation>
<translation id="6446163441502663861">କାହୠ(à¬à¬¨à¬­à¬²à¬ªà­)</translation>
@@ -2092,9 +2105,9 @@
<translation id="6828866289116430505">ଜେନେଟିକà­à¬¸</translation>
<translation id="6831043979455480757">Translate</translation>
<translation id="6833752742582340615">ସà­à¬°à¬•à­à¬·à¬¿à¬¤ à¬à¬¬à¬‚ ଦà­à¬°à­à¬¤à¬¤à¬° ଚେକଆଉଟ ପାଇଠଆପଣଙà­à¬•à¬° କାରà­à¬¡ ଓ ବିଲିଂ ସୂଚନାକୠଆପଣଙà­à¬• Google ଆକାଉଣà­à¬Ÿà¬°à­‡ ସେଭ କରନà­à¬¤à­</translation>
-<translation id="6839929833149231406">କà­à¬·à­‡à¬¤à­à¬°</translation>
<translation id="6846340164947227603">à¬à¬• ଭରà­à¬šà­à¬†à¬²à­ କାରà­à¬¡ ନମà­à¬¬à¬° ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରନà­à¬¤à­...</translation>
<translation id="6852204201400771460">ଆପà­â€Œà¬•à­ ରିଲୋଡୠକରିବେ କି?</translation>
+<translation id="6857776781123259569">ପାସୱାରà­à¬¡à¬—à­à¬¡à¬¼à¬¿à¬•à­ ପରିଚାଳନା କରନà­à¬¤à­...</translation>
<translation id="686485648936420384">ଉପଭୋକà­à¬¤à¬¾ ରିସୋରà­à¬¸à¬—à­à­œà¬¿à¬•</translation>
<translation id="6865412394715372076">ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨ à¬à¬¹à¬¿ କାରà­à¬¡ ଯାଞà­à¬š କରାଯାଇପାରିବ ନାହିà¬</translation>
<translation id="6869334554832814367">ବà­à­Ÿà¬•à­à¬¤à¬¿à¬—ତ ଋଣ</translation>
@@ -2143,7 +2156,6 @@
<translation id="6965978654500191972">ଡିଭାଇସà­</translation>
<translation id="696703987787944103">ପରସେପଚà­à¬†à¬²à­</translation>
<translation id="6968269510885595029">ଆପଣଙà­à¬• ସà­à¬°à¬•à­à¬·à¬¾ କୀ ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରନà­à¬¤à­</translation>
-<translation id="6970216967273061347">ଜିଲà­à¬²à¬¾</translation>
<translation id="6971439137020188025">Slidesରେ ଶୀଘà­à¬° à¬à¬• ନୂଆ Google ପà­à¬°à­‡à¬œà­‡à¬£à­à¬Ÿà­‡à¬¸à¬¨à­ ତିଆରି କରନà­à¬¤à­</translation>
<translation id="6972629891077993081">HID ଡିଭାଇସଗà­à­œà¬¿à¬•</translation>
<translation id="6973656660372572881">ଉଭୟ ନିରà­à¬¦à­à¬¦à¬¿à¬·à­à¬Ÿ ପà­à¬°à¬•à­à¬¸à¬¿ ସରà­à¬­à¬°à­ ଓ .pac ସà­à¬•à­à¬°à¬¿à¬ªà­à¬Ÿ URL ନିରà­à¬¦à­à¬¦à¬¿à¬·à­à¬Ÿ କରାଯାଇଛି।</translation>
@@ -2182,7 +2194,6 @@
<translation id="7081308185095828845">ଆପଣଙà­à¬•à¬° ଡିଭାଇସà­â€Œà¬°à­‡ à¬à¬¹à¬¿ ଫିଚରà­â€à¬Ÿà¬¿ ଉପଲବà­à¬§ ନାହିà¬</translation>
<translation id="7083258188081898530">ଟà­à¬°à­‡ 9</translation>
<translation id="7086090958708083563">ଉପଯୋଗକରà­à¬¤à­à¬¤à¬¾à¬™à­à¬• ଦà­à­±à¬¾à¬°à¬¾ ଅପଲୋଡୠପାଇଠଅନà­à¬°à­‹à¬§ କରାଯାଇଛି</translation>
-<translation id="7087282848513945231">ଦେଶ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome ସେଟିଂସରେ ସମସà­à¬¤ ସାଇଟରେ ଷà­à¬Ÿà­‹à¬°à­ କରାଯାଇଥିବା ଅନà­à¬®à¬¤à¬¿ à¬à¬¬à¬‚ ଡାଟାକୠପରିଚାଳନା କରିବାକୠTab କରି Enter ଦବାନà­à¬¤à­</translation>
<translation id="7096937462164235847">à¬à¬¹à¬¿ ୱେବସାଇଟର ପରିଚୟ ଯାଞà­à¬š କରାଯାଇନାହିà¬à¥¤</translation>
<translation id="7101893872976785596">ହରର ମà­à¬­à¬¿à¬—à­à­œà¬¿à¬•</translation>
@@ -2201,10 +2212,10 @@
<ph name="LIST_ITEM" />ଫରà­à¬®à¬—à­à­œà¬¿à¬•à¬°à­‡ ଲେଖାଯାଇଥିବା ସୂଚନା<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">à¬à¬¹à¬¿ ଠିକଣାକୠପଠାଯାଇପାରିବ ନାହିà¬à¥¤ à¬à¬• ଭିନà­à¬¨ ଠିକଣା ଚୟନ କରନà­à¬¤à­à¥¤</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ଆପଣଙà­à¬• ଆଡମିନ à¬à¬¹à¬¿ ଡାଟାକୠକପି କରିବାରୠପà­à¬°à¬¤à¬¿à¬¬à¬¨à­à¬§à¬¿à¬¤ କରିଛନà­à¬¤à¬¿à¥¤</translation>
<translation id="7135130955892390533">ସà­à¬¥à¬¿à¬¤à¬¿ ଦେଖାନà­à¬¤à­</translation>
<translation id="7138472120740807366">ଡେଲିଭରୀ ପଦà­à¬§à¬¤à¬¿</translation>
-<translation id="7139724024395191329">à¬à¬®à¬¿à¬°à¬¾à¬Ÿà­</translation>
<translation id="7139892792842608322">ପà­à¬°à¬¾à¬¥à¬®à¬¿à¬• ଟà­à¬°à­‡</translation>
<translation id="714064300541049402">ସାଇଡୠ2 ଇମେଜୠX ସିଫà­à¬Ÿ</translation>
<translation id="7152423860607593928">ନମà­à¬¬à¬°-11 (à¬à¬¨à¬­à¬²à¬ªà­)</translation>
@@ -2421,6 +2432,7 @@
<translation id="7669271284792375604">à¬à¬¹à¬¿ ସାଇଟà­â€Œà¬°à­‡ ଥିବା ଆକà­à¬°à¬®à¬£à¬•à¬¾à¬°à­€à¬®à¬¾à¬¨à­‡ କୌଶଳରେ ଆପଣଙà­à¬• ଦà­à¬µà¬¾à¬°à¬¾ à¬à¬ªà¬°à¬¿ ପà­à¬°à­‹à¬—à­à¬°à¬¾à¬®à­ ଇନà­â€Œà¬·à­à¬Ÿà¬²à­ କରିବାକୠଚେଷà­à¬Ÿà¬¾ କରିପାରନà­à¬¤à¬¿ ଆପଣଙà­à¬•à¬° ବà­à¬°à¬¾à¬‰à¬œà¬¿à¬‚ ଅଭିଜà­à¬žà¬¤à¬¾à¬° କà­à¬·à¬¤à¬¿ କରିପାରେ (ଉଦାହରଣ ସà­à¬µà¬°à­‚ପ, ଆପଣଙà­à¬•à¬° ମୂଳପୃଷà­à¬ à¬¾à¬°à­‡ ପରିବରà­à¬¤à­à¬¤à¬¨ କରିବା କିମà­à¬¬à¬¾ ଆପଣ ଦେଖà­à¬¥à¬¿à¬¬à¬¾ ସାଇଟà­â€Œà¬—à­à­œà¬¿à¬•à¬°à­‡ ଅତିରିକà­à¬¤ ବିଜà­à¬žà¬¾à¬ªà¬¨ ପà­à¬°à¬¦à¬°à­à¬¶à¬¨ କରିବା)।</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ଗୋପନୀୟ ଭାବରେ ଫà­à¬²à¬¾à¬— କରାଯାଇଥିବା ଡାଟା ପାଇଠନିଆଯାଇଥିବା ପଦକà­à¬·à­‡à¬ª (ଲଗଇନ ପରେ 1ଟି ପଦକà­à¬·à­‡à¬ª)। <ph name="BEGIN_LINK" />ଅଧିକ ଜାଣନà­à¬¤à­<ph name="END_LINK" />}other{ଗୋପନୀୟ ଭାବରେ ଫà­à¬²à¬¾à¬— କରାଯାଇଥିବା ଡାଟା ପାଇଠନିଆଯାଇଥିବା ପଦକà­à¬·à­‡à¬ª (ଲଗଇନ ପରେ #ଟି ପଦକà­à¬·à­‡à¬ª)। <ph name="BEGIN_LINK" />ଅଧିକ ଜାଣନà­à¬¤à­<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ମେଲବକà­à¬¸ 6</translation>
+<translation id="7675325315208090829">ପେମେଣà­à¬Ÿ ପଦà­à¬§à¬¤à¬¿à¬—à­à­œà¬¿à¬•à­ ପରିଚାଳନା କରନà­à¬¤à­...</translation>
<translation id="7676643023259824263">କà­à¬²à¬¿à¬ªà­â€Œà¬¬à­‹à¬°à­à¬¡ ଟେକà­à¬¸à¬Ÿà­ <ph name="TEXT" /> ଖୋଜନà­à¬¤à­</translation>
<translation id="7679367271685653708">Chrome ସେଟିଂସରେ ଆପଣଙà­à¬• ବà­à¬°à¬¾à¬‰à¬œà¬¿à¬‚ ଇତିହାସ ଦେଖନà­à¬¤à­ à¬à¬¬à¬‚ ପରିଚାଳନା କରନà­à¬¤à­</translation>
<translation id="7679947978757153706">ବେସବଲ</translation>
@@ -2463,7 +2475,6 @@
<translation id="7766518757692125295">ସà­à¬•à¬°à­à¬Ÿà­</translation>
<translation id="7770259615151589601">ନିରà­à¬¦à­à¬§à¬¾à¬°à¬¿à¬¤-ଦୀରà­à¬˜à¬¤à¬¾</translation>
<translation id="7773005668374414287">ସେହି ସମାନ ଅରà­à¬¡à¬°à¬°à­‡ ଫେସୠଅପà­</translation>
-<translation id="777702478322588152">ପà­à¬°à¬¶à¬¾à¬¸à¬•à­€à­Ÿ କà­à¬·à­‡à¬¤à­à¬°</translation>
<translation id="7791011319128895129">ରିଲିଜୠହୋଇନାହିà¬</translation>
<translation id="7791196057686275387">ବେଲà­</translation>
<translation id="7791543448312431591">ଯୋଡନà­à¬¤à­</translation>
@@ -2554,12 +2565,12 @@
<translation id="8055534648776115597">ଧନà­à¬¦à¬¾à¬®à­‚ଳକ à¬à¬¬à¬‚ ନିରନà­à¬¤à¬° ଶିକà­à¬·à¬¾</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" ସଠିକୠଭାବରେ କନà­â€Œà¬«à¬¿à¬—ରୠହୋଇନାହିà¬à¥¤ "<ph name="SOFTWARE_NAME" />" ଅନà­â€Œà¬‡à¬¨à­â€Œà¬·à­à¬Ÿà¬²à­ କରିବା ଦà­à­±à¬¾à¬°à¬¾ ସାମାନà­à­Ÿà¬¤à¬ƒ ସମସà­à­Ÿà¬¾à¬° ସମାଧାନ ହୋଇଯାà¬à¥¤ <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ଖାଦà­à­Ÿ ଉତà­à¬ªà¬¾à¬¦à¬¨</translation>
-<translation id="8066955247577885446">କà­à¬·à¬®à¬¾ କରିବେ, କିଛି ଭà­à¬² ହୋଇଗଲା।</translation>
<translation id="8067872629359326442">ଆପଣ à¬à¬• ପà­à¬°à¬¤à¬¾à¬°à¬£à¬¾à¬®à­‚ଳକ ସାଇଟà­â€Œà¬°à­‡ à¬à¬¬à­‡ ଆପଣଙà­à¬•à¬° ପାସà­â€à­±à¬¾à¬°à­à¬¡ ଲେଖିଛନà­à¬¤à¬¿à¥¤ Chromium ସାହାଯà­à­Ÿ କରିପାରିବ। ଆପଣଙà­à¬•à¬° ପାସà­â€Œà­±à¬¾à¬°à­à¬¡ ପରିବରà­à¬¤à­à¬¤à¬¨ କରିବାକୠଓ ଆପଣଙà­à¬•à¬° ଆକାଉଣà­à¬Ÿ ବିପଦରେ ପଡ଼ିପାରେ ବୋଲି Googleକୠସୂଚିତ କରିବା ପାଇà¬, ’ଆକାଉଣà­à¬Ÿà¬° ସà­à¬°à¬•à­à¬·à¬¾ କରନà­à¬¤à­â€™à¬°à­‡ କà­à¬²à¬¿à¬•à­ କରନà­à¬¤à­à¥¤</translation>
<translation id="8070439594494267500">ଆପୠଆଇକନà­</translation>
<translation id="8074253406171541171">10x13 (à¬à¬¨à¬­à¬²à¬ªà­)</translation>
<translation id="8075736640322370409">ଶୀଘà­à¬° à¬à¬• ନୂଆ Google Sheet ତିଆରି କରନà­à¬¤à­</translation>
<translation id="8075898834294118863">ସାଇଟୠସେଟିଂସକୠପରିଚାଳନା କରନà­à¬¤à­</translation>
+<translation id="8076492880354921740">ଟାବà­</translation>
<translation id="8078141288243656252">ଘୂରିଯାଇଥିବା ବେଳେ ବà­à­Ÿà¬¾à¬–à­à­Ÿà¬¾ କରିପାରିବେ ନାହିà¬</translation>
<translation id="8079031581361219619">ସାଇଟà­â€Œà¬•à­ ରିଲୋଡୠକରିବେ?</translation>
<translation id="8081087320434522107">ସେଡାନଗà­à­œà¬¿à¬•</translation>
@@ -2682,6 +2693,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ସେଟିଂସà­</translation>
+<translation id="8428634594422941299">ବà­à¬à¬¿à¬—ଲି</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome ସେଟିଂସରେ ଆପଣଙà­à¬• କà­à¬•à­€ ପସନà­à¬¦à¬—à­à­œà¬¿à¬•à­ ପରିଚାଳନା କରିବା ପାଇଠTab କରି Enter ଦବାନà­à¬¤à­</translation>
<translation id="8433057134996913067">à¬à¬¹à¬¾ ଆପଣଙà­à¬•à­ ଅଧିକାଂଶ ୱେବà­â€à¬¸à¬¾à¬‡à¬Ÿà­â€à¬°à­ ସାଇନୠଆଉଟୠକରିଦେବ।</translation>
<translation id="8434840396568290395">ଗୃହପାଳିତ ପଶà­</translation>
@@ -2729,7 +2741,7 @@
<translation id="8574899947864779331">କାରà­à¬¡à¬—à­à­œà¬¿à¬•à­ ଅତି ଶୀଘà­à¬° ସà­à¬¨à¬¿à¬¶à­à¬šà¬¿à¬¤ କରିବା ପାଇଠTouch ID ବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରନà­à¬¤à­</translation>
<translation id="8576310925653847813">ହୋମ ଥିà¬à¬Ÿà¬° ସିଷà­à¬Ÿà¬®à¬—à­à­œà¬¿à¬•</translation>
<translation id="8577348305244205642">ଭରà­à¬šà­à¬†à¬²à­ କାରà­à¬¡ ଉପଲବà­à¬§ ନାହିà¬</translation>
-<translation id="858637041960032120">ଫୋନà­â€Œ ନମà­à¬¬à¬°à­â€ ଯୋଗ କରନà­à¬¤à­</translation>
+<translation id="858637041960032120">ଫୋନ ନମà­à¬¬à¬° ଯୋଗ କରନà­à¬¤à­</translation>
<translation id="8589998999637048520">ଉତà­à¬¤à¬® ଗà­à¬£à¬¬à¬¤à­à¬¤à¬¾</translation>
<translation id="8600271352425265729">କେବଳ à¬à¬¹à¬¿ ସମୟ ପାଇà¬</translation>
<translation id="860043288473659153">କାରà­à¬¡à¬¹à­‹à¬²à­à¬¡à¬°à­â€Œà¬° ନାମ</translation>
@@ -2779,6 +2791,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> ପାଇଠ<ph name="ONE_TIME_CODE" /> ହେଉଛି ଆପଣଙà­à¬•à¬° କୋଡà­</translation>
<translation id="874918643257405732">à¬à¬¹à¬¿ ଟାବà­â€Œà¬•à­ ବà­à¬•à­â€Œà¬®à¬¾à¬°à­à¬• କରନà­à¬¤à­</translation>
<translation id="8751426954251315517">ଦୟାକରି ପରବରà­à¬¤à­à¬¤à­€ ସମୟରେ ପà­à¬£à¬¿ ଚେଷà­à¬Ÿà¬¾ କରନà­à¬¤à­</translation>
+<translation id="8757526089434340176">Google Pay ଅଫରୠଉପଲବà­à¬§ ଅଛି</translation>
<translation id="8758885506338294482">କମà­à¬ªà¬¿à¬Ÿà­‡à¬Ÿà¬¿à¬­ ଭିଡିଓ ଗେମିଂ</translation>
<translation id="8759274551635299824">à¬à¬¹à¬¿ କାରà­à¬¡à¬° ସମୟ ସୀମା ସମାପà­à¬¤ ହୋଇଛି</translation>
<translation id="87601671197631245">à¬à¬¹à¬¿ ସାଇଟୠà¬à¬• ପà­à¬°à­à¬£à¬¾ ସà­à¬°à¬•à­à¬·à¬¾ କନଫିଗରେସନୠବà­à­Ÿà¬¬à¬¹à¬¾à¬° କରେ, ଯାହା ଆପଣଙà­à¬•à¬° ସୂଚନା (ଉଦାହରଣ ସà­à­±à¬°à­‚ପ, ପାସୱାରà­à¬¡, ମେସେଜ କିମà­à¬¬à¬¾ କà­à¬°à­‡à¬¡à¬¿à¬Ÿà­ କାରà­à¬¡à¬—à­à¬¡à¬¼à¬¿à¬•), à¬à¬¹à¬¿ ସାଇଟକୠପଠାଇବା ସମୟରେ ପà­à¬°à¬•à¬¾à¬¶ କରିଦେଇପାରେ।</translation>
@@ -2786,6 +2799,7 @@
<translation id="8763927697961133303">USB ଡିଭାଇସà­</translation>
<translation id="8763986294015493060">ବରà­à¬¤à­à¬¤à¬®à¬¾à¬¨ ଖୋଲା ଥିବା ସମସà­à¬¤ ଇନକଗà­à¬¨à¬¿à¬Ÿà­‹ ୱିଣà­à¬¡à­‹ ବନà­à¬¦ କରନà­à¬¤à­</translation>
<translation id="8766943070169463815">ସà­à¬°à¬•à­à¬·à¬¿à¬¤ ଭାବରେ ପେମେଣà­à¬Ÿ କରିବା ପାଇଠକà­à¬°à­‡à¬¡à­‡à¬¨à¬¸à¬¿à¬†à¬²à­ ପà­à¬°à¬®à¬¾à¬£à­€à¬•à¬°à¬£ ସିଟୠଖୋଲାଯାଇଛି</translation>
+<translation id="8767765348545497220">ସହାୟତା ବବଲକୠବନà­à¬¦ କରନà­à¬¤à­</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">ମୋଟରସାଇକେଲଗà­à­œà¬¿à¬•</translation>
<translation id="8790007591277257123">ଡିଲିଟà­â€Œ &amp;ପà­à¬¨à¬ƒà¬¬à¬¤à­ କରନà­à¬¤à­</translation>
@@ -2798,6 +2812,7 @@
<translation id="8806285662264631610">ସà­à¬¨à¬¾à¬¨ à¬à¬¬à¬‚ ବଡି ପà­à¬°à¬¡à¬•à­à¬Ÿà¬—à­à­œà¬¿à¬•</translation>
<translation id="8807160976559152894">ପà­à¬°à¬¤à­à­Ÿà­‡à¬• ଡକà­à­Ÿà­à¬®à­‡à¬£à­à¬Ÿ ପରେ ଟà­à¬°à¬¿à¬®à­ କରନà­à¬¤à­</translation>
<translation id="8808828119384186784">Chrome ସେଟିଂସ</translation>
+<translation id="8813277370772331957">ମୋତେ ପରେ ରିମାଇଣà­à¬¡ କରନà­à¬¤à­</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, ଆପଣଙà­à¬• Chrome ସେଟିଂସରୠChrome ଅପଡେଟୠକରିବାକୠTab କରି Enter ଦବାନà­à¬¤à­</translation>
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="882338992931677877">ମାନà­à¬†à¬²à­ ସà­à¬²à¬Ÿà­</translation>
@@ -2977,6 +2992,7 @@
<translation id="988159990683914416">ଡେଭେଲପରୠବିଲà­à¬¡</translation>
<translation id="989988560359834682">ଠିକଣା à¬à¬¡à¬¿à¬Ÿà­ କରନà­à¬¤à­</translation>
<translation id="991413375315957741">ମୋସନୠକିମà­à¬¬à¬¾ ଲାଇଟୠସେନà­à¬¸à¬°à¬—à­à­œà¬¿à¬•</translation>
+<translation id="992110854164447044">ଆପଣଙà­à¬•à­ ସମà­à¬­à¬¾à¬¬à­à­Ÿ ଠକାମୀରୠସà­à¬°à¬•à­à¬·à¬¿à¬¤ ରଖିବାରେ ସାହାଯà­à­Ÿ କରିବା ପାଇଠà¬à¬• ଭରà­à¬šà­à¬†à¬² କାରà­à¬¡ ଆପଣଙà­à¬• ପà­à¬°à¬•à­ƒà¬¤ କାରà­à¬¡à¬•à­ ଲà­à¬šà¬¾à¬‡ ରଖିଥାà¬à¥¤ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992256792861109788">ଗୋଲାପି</translation>
<translation id="992432478773561401">ଆପଣଙà­à¬•à¬° କମà­à¬ªà­à­Ÿà­à¬Ÿà¬°à­ ବା ନେଟà­â€Œà­±à¬°à­à¬•à¬°à­‡ "<ph name="SOFTWARE_NAME" />" ସଠିକୠଭାବରେ ଇନà­â€Œà¬·à­à¬Ÿà¬²à­ ହୋଇନାହିà¬:
&lt;ul&gt;
diff --git a/chromium/components/strings/components_strings_pa.xtb b/chromium/components/strings/components_strings_pa.xtb
index 61eefe4e95f..d16f635c09e 100644
--- a/chromium/components/strings/components_strings_pa.xtb
+++ b/chromium/components/strings/components_strings_pa.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ਅਨà©à¨¦à¨¾à¨¨, ਵਜ਼ੀਫ਼ੇ ਅਤੇ ਵਿੱਤੀ ਸਹਾਇਤਾ</translation>
<translation id="1048785276086539861">ਜਦੋਂ ਤà©à¨¸à©€à¨‚ à¨à¨¨à©‹à¨Ÿà©‡à¨¶à¨¨à¨¾à¨‚ ਦਾ ਸੰਪਾਦਨ ਕਰਦੇ ਹੋ, ਤਾਂ ਇਹ ਦਸਤਾਵੇਜ਼ ਇਕਹਿਰੇ ਪੰਨਾ-ਦà©à¨°à¨¿à¨¶ 'ਤੇ ਵਾਪਸ ਆ ਜਾਵੇਗਾ</translation>
<translation id="1050038467049342496">ਦੂਜੀਆਂ à¨à¨ªà¨¾à¨‚ ਬੰਦ ਕਰੋ</translation>
+<translation id="1053959602163383901">ਤà©à¨¸à©€à¨‚ <ph name="PROVIDER_ORIGIN" /> ਦੀ ਵਰਤੋਂ ਕਰਨ ਵਾਲੀਆਂ ਵੈੱਬਸਾਈਟਾਂ 'ਤੇ ਪà©à¨°à¨®à¨¾à¨£à¨•à¨°à¨¤à¨¾ ਡੀਵਾਈਸ ਨਾਲ ਪà©à¨¶à¨Ÿà©€ ਕਰਨਾ ਚà©à¨£à¨¿à¨† ਹੈ। ਇਸ ਪà©à¨°à¨¦à¨¾à¨¨à¨• ਨੇ ਤà©à¨¹à¨¾à¨¡à©€ ਭà©à¨—ਤਾਨ ਵਿਧੀ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਟੋਰ ਕੀਤਾ ਹੋ ਸਕਦਾ ਹੈ, ਜਿਸ ਲਈ ਤà©à¨¸à©€à¨‚ <ph name="LINK_TEXT" /> ਕਰ ਸਕਦੇ ਹੋ।</translation>
<translation id="1055184225775184556">&amp;ਜੋੜੋ ਨੂੰ ਅਨਡੂ ਕਰੋ</translation>
<translation id="1056663316309890343">ਫ਼ੋਟੋ ਸਾਫ਼ਟਵੇਅਰ</translation>
<translation id="1056898198331236512">ਚਿਤਾਵਨੀ</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">ਪਿਕਅੱਪ ਵਿਧੀ</translation>
<translation id="1281476433249504884">ਸਟੈਕਰ 1</translation>
<translation id="1285320974508926690">ਕਦੇ ਵੀ ਇਸ ਸਾਈਟ ਦਾ ਅਨà©à¨µà¨¾à¨¦ ਨਾ ਕਰੋ</translation>
+<translation id="1288548991597756084">ਸà©à¨°à©±à¨–ਿਅਤ ਢੰਗ ਨਾਲ ਕਾਰਡ ਨੂੰ ਰੱਖਿਅਤ ਕਰੋ</translation>
<translation id="1292571435393770077">ਟà©à¨°à©‡à¨… 16</translation>
<translation id="1292701964462482250">"ਤà©à¨¹à¨¾à¨¡à©‡ ਕੰਪਿਊਟਰ ਦਾ ਸਾਫ਼ਟਵੇਅਰ Chrome ਨੂੰ ਵੈੱਬ ਨਾਲ ਸà©à¨°à©±à¨–ਿਅਤ ਤੌਰ 'ਤੇ ਕਨੈਕਟ ਹੋਣ ਤੋਂ ਰੋਕ ਰਿਹਾ ਹੈ" (ਸਿਰਫ਼ Windows ਕੰਪਿਊਟਰ)</translation>
<translation id="1294154142200295408">ਆਦੇਸ਼-ਰੇਖਾ ਵਖਰੇਂਵੇ</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;ਗੜਬੜ ਠੀਕ ਕਰਨ ਲਈ, ਜਿਸ ਪੰਨੇ ਨੂੰ ਤà©à¨¸à©€à¨‚ ਖੋਲà©à¨¹à¨£ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਹੇ ਹੋ ਉਸ 'ਤੇ &lt;strong&gt;ਕਨੈਕਟ ਕਰੋ&lt;/strong&gt; 'ਤੇ ਕਲਿੱਕ ਕਰੋ।&lt;/p&gt;</translation>
<translation id="1507780850870535225">ਲੈਂਡਸਕੇਪ ਡਿਜ਼ਾਈਨ</translation>
<translation id="1513706915089223971">ਇਤਿਹਾਸ ਇੰਦਰਾਜਾਂ ਦੀ ਸੂਚੀ</translation>
+<translation id="1516097932025103760">ਇਹ ਇਨਕà©à¨°à¨¿à¨ªà¨Ÿà¨¡ ਅਤੇ ਸà©à¨°à©±à¨–ਿਅਤ ਢੰਗ ਨਾਲ ਰੱਖਿਅਤ ਹੋਵੇਗਾ ਅਤੇ CVC ਨੂੰ ਕਦੇ ਵੀ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।</translation>
<translation id="1517433312004943670">ਫ਼ੋਨ ਨੰਬਰ ਲੋੜੀਂਦਾ ਹੈ</translation>
<translation id="1519264250979466059">ਬਿਲਡ ਤਾਰੀਖ</translation>
<translation id="1521159554480556801">ਫਾਈਬਰ ਅਤੇ ਬà©à¨£à¨¾à¨ˆ ਕਲਾ</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ਭà©à¨—ਤਾਨ ਵਿਧੀਆਂ ਰੱਖਿਅਤ ਕਰੋ ਅਤੇ ਭਰੋ</translation>
<translation id="1663943134801823270">ਕਾਰਡ ਅਤੇ ਪਤੇ Chrome ਤੋਂ ਹਨ। ਤà©à¨¸à©€à¨‚ ਉਹਨਾਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ <ph name="BEGIN_LINK" />ਸੈਟਿੰਗਾਂ<ph name="END_LINK" /> ਵਿੱਚ ਜਾ ਕੇ ਕਰ ਸਕਦੇ ਹੋ।</translation>
<translation id="1671391448414634642"><ph name="SOURCE_LANGUAGE" /> ਵਾਲੇ ਪੰਨਿਆਂ ਦਾ ਹà©à¨£ ਤੋਂ <ph name="TARGET_LANGUAGE" /> ਵਿੱਚ ਅਨà©à¨µà¨¾à¨¦ ਕੀਤਾ ਜਾਵੇਗਾ।</translation>
+<translation id="1673886523110456987">ਪੇਸ਼ਕਸ਼ ਵਰਤਣ ਲਈ <ph name="CARD_DETAIL" /> ਨਾਲ ਚੈੱਕ-ਆਊਟ ਕਰੋ</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> ਤੋਂ <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ਪਹਿਲਾਂ ਛੋਟੇ ਕਿਨਾਰੇ ਵਾਲੇ</translation>
<translation id="168693727862418163">ਇਸ ਨੀਤੀ ਮà©à©±à¨² ਨੂੰ ਇਸਦੀ ਸਕੀਮ ਨਾਲ ਪà©à¨°à¨®à¨¾à¨£à¨¿à¨¤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ ਅਤੇ ਇਸ ਨੂੰ ਅਣਡਿੱਠ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ।</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">ਗਤੀਸ਼ੀਲਤਾ ਸੈਂਸਰ</translation>
<translation id="1717494416764505390">ਮੇਲਬਾਕਸ 3</translation>
<translation id="1718029547804390981">ਦਸਤਾਵੇਜ਼ ਵੱਡਾ ਹੋਣ ਕਰਕੇ à¨à¨¨à©‹à¨Ÿà©‡à¨¶à¨¨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ</translation>
+<translation id="1720941539803966190">ਟਿਊਟੋਰੀਅਲ ਨੂੰ ਬੰਦ ਕਰੋ</translation>
<translation id="1721424275792716183">* ਖੇਤਰ ਲੋੜੀਂਦਾ ਹੈ</translation>
<translation id="1727613060316725209">ਪà©à¨°à¨®à¨¾à¨£-ਪੱਤਰ ਵੈਧ ਹੈ</translation>
<translation id="1727741090716970331">ਵੈਧ ਕਾਰਡ ਨੰਬਰ ਸ਼ਾਮਲ ਕਰੋ</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">BBQ ਅਤੇ ਗà©à¨°à¨¿à¨²à¨¿à©°à¨—</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> ਭਾਸ਼ਾ ਵਾਲੇ ਪੰਨਿਆਂ ਦਾ ਅਨà©à¨µà¨¾à¨¦ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ।</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ਇਸ ਕੰਟਰੋਲ ਦੇ ਚਾਲੂ ਹੋਣ ਅਤੇ ਸਥਿਤੀ ਦੇ ਕਿਰਿਆਸ਼ੀਲ ਹੋਣ 'ਤੇ, Chrome ਨਿਰਧਾਰਿਤ ਕਰਦਾ ਹੈ ਤà©à¨¹à¨¾à¨¡à©€ ਹਾਲੀਆ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨¿à©°à¨— ਸਰਗਰਮੀ ਸਭ ਤੋਂ ਜ਼ਿਆਦਾ ਲੋਕਾਂ ਦੇ ਕਿਹੜੇ ਵੱਡੇ ਗਰà©à©±à¨ª ਜਾਂ "ਸਮਗà©à¨£" ਦੇ ਸਮਾਨ ਹੈ। ਵਿਗਿਆਪਨਦਾਤੇ ਗਰà©à©±à¨ª ਲਈ ਵਿਗਿਆਪਨਾਂ ਨੂੰ ਚà©à¨£ ਸਕਦੇ ਹਨ ਅਤੇ ਤà©à¨¹à¨¾à¨¡à©‡ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨¿à©°à¨— ਇਤਿਹਾਸ ਨੂੰ ਤà©à¨¹à¨¾à¨¡à©‡ ਡੀਵਾਈਸ 'ਤੇ ਨਿੱਜੀ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਤà©à¨¹à¨¾à¨¡à©‡ ਗਰà©à©±à¨ª ਨੂੰ ਹਰ ਰੋਜ਼ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।}=1{ਇਸ ਕੰਟਰੋਲ ਦੇ ਚਾਲੂ ਹੋਣ ਅਤੇ ਸਥਿਤੀ ਦੇ ਕਿਰਿਆਸ਼ੀਲ ਹੋਣ 'ਤੇ, Chrome ਨਿਰਧਾਰਿਤ ਕਰਦਾ ਹੈ ਤà©à¨¹à¨¾à¨¡à©€ ਹਾਲੀਆ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨¿à©°à¨— ਸਰਗਰਮੀ ਸਭ ਤੋਂ ਜ਼ਿਆਦਾ ਲੋਕਾਂ ਦੇ ਕਿਹੜੇ ਵੱਡੇ ਗਰà©à©±à¨ª ਜਾਂ "ਸਮਗà©à¨£" ਦੇ ਸਮਾਨ ਹੈ। ਵਿਗਿਆਪਨਦਾਤੇ ਗਰà©à©±à¨ª ਲਈ ਵਿਗਿਆਪਨਾਂ ਨੂੰ ਚà©à¨£ ਸਕਦੇ ਹਨ ਅਤੇ ਤà©à¨¹à¨¾à¨¡à©‡ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨¿à©°à¨— ਇਤਿਹਾਸ ਨੂੰ ਤà©à¨¹à¨¾à¨¡à©‡ ਡੀਵਾਈਸ 'ਤੇ ਨਿੱਜੀ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਤà©à¨¹à¨¾à¨¡à©‡ ਗਰà©à©±à¨ª ਨੂੰ ਹਰ ਰੋਜ਼ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।}other{ਇਸ ਕੰਟਰੋਲ ਦੇ ਚਾਲੂ ਹੋਣ ਅਤੇ ਸਥਿਤੀ ਦੇ ਕਿਰਿਆਸ਼ੀਲ ਹੋਣ 'ਤੇ, Chrome ਨਿਰਧਾਰਿਤ ਕਰਦਾ ਹੈ ਤà©à¨¹à¨¾à¨¡à©€ ਹਾਲੀਆ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨¿à©°à¨— ਸਰਗਰਮੀ ਸਭ ਤੋਂ ਜ਼ਿਆਦਾ ਲੋਕਾਂ ਦੇ ਕਿਹੜੇ ਵੱਡੇ ਗਰà©à©±à¨ª ਜਾਂ "ਸਮਗà©à¨£" ਦੇ ਸਮਾਨ ਹੈ। ਵਿਗਿਆਪਨਦਾਤੇ ਗਰà©à©±à¨ª ਲਈ ਵਿਗਿਆਪਨਾਂ ਨੂੰ ਚà©à¨£ ਸਕਦੇ ਹਨ ਅਤੇ ਤà©à¨¹à¨¾à¨¡à©‡ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨¿à©°à¨— ਇਤਿਹਾਸ ਨੂੰ ਤà©à¨¹à¨¾à¨¡à©‡ ਡੀਵਾਈਸ 'ਤੇ ਨਿੱਜੀ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਤà©à¨¹à¨¾à¨¡à©‡ ਗਰà©à©±à¨ª ਨੂੰ ਹਰ {NUM_DAYS} ਦਿਨਾਂ ਬਾਅਦ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।}}</translation>
-<translation id="2053553514270667976">ਜ਼ਿਪ ਕੋਡ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 ਸà©à¨à¨¾à¨…}one{# ਸà©à¨à¨¾à¨…}other{ # ਸà©à¨à¨¾à¨…}}</translation>
+<translation id="2066915425250589881">ਮਿਟਾਉਣ ਦੀ ਬੇਨਤੀ</translation>
<translation id="2068528718802935086">ਬਾਲ ਅਤੇ ਛੋਟੇ ਬੱਚੇ</translation>
<translation id="2071156619270205202">ਇਹ ਕਾਰਡ ਆਭਾਸੀ ਕਾਰਡ ਨੰਬਰ ਲਈ ਯੋਗ ਨਹੀਂ ਹੈ।</translation>
<translation id="2071692954027939183">ਸੂਚਨਾਵਾਂ ਸਵੈਚਲਿਤ ਤੌਰ 'ਤੇ ਬਲਾਕ ਕੀਤੀਆਂ ਗਈਆਂ ਕਿਉਂਕਿ ਤà©à¨¸à©€à¨‚ ਆਮ ਤੌਰ 'ਤੇ ਉਹਨਾਂ ਨੂੰ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦੇ ਹੋ</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">'ਸਿੰਕ' ਬਟਨ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ, Chrome ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਤà©à¨¸à©€à¨‚ ਜਿਹੜੀ ਜਾਣਕਾਰੀ ਦਾ ਸਿੰਕ ਕਰਦੇ ਹੋ, ਉਸਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰਨ ਲਈ Enter ਦਬਾਓ</translation>
<translation id="2091887806945687916">ਧà©à¨¨à©€</translation>
<translation id="2094505752054353250">ਡੋਮੇਨ ਮਿਸਮੈਚ</translation>
-<translation id="2096368010154057602">ਵਿਭਾਗ</translation>
<translation id="2099652385553570808">ਖੱਬੇ ਪਾਸੇ ਤਿੰਨ ਪਿੰਨਾਂ</translation>
<translation id="2101225219012730419">ਵਰਜਨ:</translation>
<translation id="2102134110707549001">ਮਜ਼ਬੂਤ ਪਾਸਵਰਡ ਸà©à¨à¨¾à¨“…</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">ਅਮਰੀਕੀ ਫà©à©±à¨Ÿà¨¬à¨¾à¨²</translation>
<translation id="2187317261103489799">ਪਤਾ ਲਗਾਓ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)</translation>
<translation id="2188375229972301266">ਹੇਠਾਂ ਇੱਕ ਤੋਂ ਵੱਧ ਮੋਰੀਆਂ</translation>
-<translation id="2188852899391513400">ਤà©à¨¹à¨¾à¨¡à©‡ ਵੱਲੋਂ ਹà©à¨£à©‡ ਹੀ ਵਰਤਿਆ ਗਿਆ ਪਾਸਵਰਡ ਡਾਟਾ ਉਲੰਘਣਾ ਵਿੱਚ ਮਿਲਿਆ ਸੀ। ਆਪਣੇ ਖਾਤਿਆਂ ਨੂੰ ਸà©à¨°à©±à¨–ਿਅਤ ਕਰਨ ਲਈ, Google ਪਾਸਵਰਡ ਪà©à¨°à¨¬à©°à¨§à¨• ਇਸਨੂੰ ਹà©à¨£à©‡ ਬਦਲਣ ਅਤੇ ਫਿਰ ਆਪਣੇ ਰੱਖਿਅਤ ਕੀਤੇ ਪਾਸਵਰਡਾਂ ਦੀ ਜਾਂਚ ਕਰਨ ਦੀ ਸਿਫ਼ਾਰਸ਼ ਕਰਦਾ ਹੈ।</translation>
<translation id="219906046732893612">ਘਰ ਸੰਬੰਧੀ ਸà©à¨§à¨¾à¨°</translation>
<translation id="2202020181578195191">ਇੱਕ ਵੈਧ ਮਿਆਦ ਸਮਾਪਤੀ ਸਾਲ ਦਾਖਲ ਕਰੋ</translation>
<translation id="22081806969704220">ਟà©à¨°à©‡à¨… 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">ਫ਼ਾਈਲ ਸੰਪਾਦਨ</translation>
<translation id="2215963164070968490">ਕà©à©±à¨¤à©‡</translation>
<translation id="2218879909401188352">ਇਸ ਵੇਲੇ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 'ਤੇ ਹਮਲਾਵਰ ਤà©à¨¹à¨¾à¨¡à©€ ਡੀਵਾਈਸ 'ਤੇ ਖਤਰਨਾਕ à¨à¨ªà¨¾à¨‚ ਨੂੰ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹਨ, ਜੋ ਤà©à¨¹à¨¾à¨¡à©€ ਡੀਵਾਈਸ ਨੂੰ ਨà©à¨•à¨¸à¨¾à¨¨ ਪਹà©à©°à¨šà¨¾ ਸਕਦੀਆਂ ਹਨ, ਤà©à¨¹à¨¾à¨¡à©‡ ਮੋਬਾਈਲ ਬਿਲ ਵਿੱਚ ਗà©à©±à¨à©‡ ਖਰਚੇ ਸ਼ਾਮਲ ਕਰ ਸਕਦੀਆਂ ਹਨ, ਜਾਂ ਤà©à¨¹à¨¾à¨¡à©€ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਨੂੰ ਚੋਰੀ ਕਰ ਸਕਦੀਆਂ ਹਨ। <ph name="BEGIN_LEARN_MORE_LINK" />ਹੋਰ ਜਾਣੋ<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ਟਿਊਟੋਰੀਅਲ ਮà©à©œ-ਸ਼à©à¨°à©‚ ਕਰੋ</translation>
+<translation id="2219735899272417925">ਡੀਵਾਈਸ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨ ਦੀ ਲੋੜ ਹੈ</translation>
<translation id="2224337661447660594">ਇੰਟਰਨੈੱਟ ਨਹੀਂ</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />ਨਿਦਾਨ à¨à¨ª<ph name="END_LINK" /> ਦੀ ਵਰਤੋਂ ਨਾਲ ਆਪਣੇ ਕਨੈਕਸ਼ਨ ਨੂੰ ਠੀਕ ਕਰੋ</translation>
<translation id="2239100178324503013">ਹà©à¨£à©‡ ਭੇਜੋ</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)</translation>
<translation id="2512413427717747692">'Chrome ਨੂੰ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨° ਵਜੋਂ ਸੈੱਟ ਕਰੋ' ਸੰਬੰਧੀ ਬਟਨ, iOS ਸੈਟਿੰਗਾਂ ਵਿੱਚ Chrome ਨੂੰ ਸਿਸਟਮ ਦੇ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨° ਵਜੋਂ ਸੈੱਟ ਕਰਨ ਲਈ Enter ਦਬਾਓ</translation>
<translation id="2515629240566999685">ਆਪਣੇ ਖੇਤਰ ਵਿੱਚ ਸਿਗਨਲ ਦੀ ਜਾਂਚ ਕਰੋ</translation>
+<translation id="2515761554693942801">ਤà©à¨¸à©€à¨‚ <ph name="PROVIDER_ORIGIN" /> ਦੀ ਵਰਤੋਂ ਕਰਨ ਵਾਲੀਆਂ ਵੈੱਬਸਾਈਟਾਂ 'ਤੇ ਸਪਰਸ਼ ਆਈਡੀ ਨਾਲ ਪà©à¨¶à¨Ÿà©€ ਕਰਨਾ ਚà©à¨£à¨¿à¨† ਹੈ। ਇਸ ਪà©à¨°à¨¦à¨¾à¨¨à¨• ਨੇ ਤà©à¨¹à¨¾à¨¡à©€ ਭà©à¨—ਤਾਨ ਵਿਧੀ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਟੋਰ ਕੀਤਾ ਹੋ ਸਕਦਾ ਹੈ, ਜਿਸ ਲਈ ਤà©à¨¸à©€à¨‚ <ph name="LINK_TEXT" /> ਕਰ ਸਕਦੇ ਹੋ।</translation>
<translation id="2521385132275182522">ਹੇਠਾਂ ਸੱਜੇ ਪਾਸੇ ਪਿੰਨ</translation>
<translation id="2521736961081452453">ਫ਼ਾਰਮ ਬਣਾਓ</translation>
<translation id="2523886232349826891">ਸਿਰਫ਼ ਇਸ ਡੀਵਾਈਸ 'ਤੇ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ</translation>
<translation id="2524461107774643265">ਹੋਰ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਕਰੋ</translation>
<translation id="2529899080962247600">ਇਸ ਖੇਤਰ ਵਿੱਚ <ph name="MAX_ITEMS_LIMIT" /> ਤੋਂ ਵੱਧ ਇੰਦਰਾਜ ਨਹੀਂ ਹੋਣੇ ਚਾਹੀਦੇ ਹਨ। ਅਗਲੇ ਸਾਰੇ ਇੰਦਰਾਜ ਅਣਡਿੱਠ ਕੀਤੇ ਜਾਣਗੇ।</translation>
+<translation id="253493526287553278">ਪà©à¨°à©‹à¨®à©‹ ਕੋਡ ਦੇ ਵੇਰਵੇ ਦੇਖੋ</translation>
<translation id="2535585790302968248">ਨਿੱਜੀ ਤੌਰ 'ਤੇ ਬà©à¨°à¨¾à¨Šà¨œà¨¼ ਕਰਨ ਲਈ ਨਵੀਂ ਇਨਕੋਗਨਿਟੋ ਟੈਬ ਖੋਲà©à¨¹à©‹</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{ਅਤੇ 1 ਹੋਰ}one{ਅਤੇ # ਹੋਰ}other{ਅਤੇ # ਹੋਰ}}</translation>
<translation id="2536110899380797252">ਪਤਾ ਸ਼ਾਮਲ ਕਰੋ</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">ਫ਼ੋਟੋਗà©à¨°à¨¾à¨«à¨¼à©€ ਅਤੇ ਡਿਜੀਟਲ ਕਲਾਵਾਂ</translation>
<translation id="2601150049980261779">ਰà©à¨®à¨¾à¨‚ਸ ਵਾਲੀਆਂ ਫ਼ਿਲਮਾਂ</translation>
<translation id="2604589665489080024">ਪੌਪ ਸੰਗੀਤ</translation>
-<translation id="2609632851001447353">ਵੈਰੀà¨à¨¶à©°à¨¸</translation>
<translation id="2610561535971892504">ਕਾਪੀ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ</translation>
<translation id="2617988307566202237">Chrome ਅੱਗੇ ਦਿੱਤੀ ਜਾਣਕਾਰੀ ਨੂੰ <ph name="BEGIN_EMPHASIS" />ਰੱਖਿਅਤ ਨਹੀਂ ਕਰੇਗਾ<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">ਜਨਮਦਿਨ ਅਤੇ ਨਾਮ ਦਿਨ</translation>
<translation id="2677748264148917807">ਛੱਡੋ</translation>
+<translation id="2679714844901977852">ਸà©à¨°à©±à¨–ਿਅਤ ਅਤੇ ਵਧੇਰੇ ਤੇਜ਼ ਚੈੱਕ-ਆਊਟਾਂ ਲਈ ਆਪਣੇ ਕਾਰਡ ਅਤੇ ਬਿਲਿੰਗ ਜਾਣਕਾਰੀ ਨੂੰ ਆਪਣੇ Google ਖਾਤੇ <ph name="USER_EMAIL" /> ਵਿੱਚ ਰੱਖਿਅਤ ਕਰੋ</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">ਪੂਰੀ ਤਰà©à¨¹à¨¾à¨‚ ਫਿੱਟ</translation>
<translation id="2688969097326701645">ਹਾਂ, ਜਾਰੀ ਰੱਖੋ</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">ਇੰਟਰਨੈੱਟ ਸੇਵਾ ਪà©à¨°à¨¦à¨¾à¨¨à¨• (ISP)</translation>
<translation id="2856444702002559011">ਹਮਲਾਵਰ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ਤੋਂ ਤà©à¨¹à¨¾à¨¡à©€ ਜਾਣਕਾਰੀ ਨੂੰ ਚੋਰੀ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਸਕਦੇ ਹਨ (ਉਦਾਹਰਨ ਲਈ, ਪਾਸਵਰਡ, ਸà©à¨¨à©‡à¨¹à©‡, ਜਾਂ ਕà©à¨°à©ˆà¨¡à¨¿à¨Ÿ ਕਾਰਡ)। <ph name="BEGIN_LEARN_MORE_LINK" />ਹੋਰ ਜਾਣੋ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ਇਹ ਸਾਈਟ ਦਖਲਅੰਦਾਜ਼ੀ ਜਾਂ ਗà©à¨®à¨°à¨¾à¨¹ ਕਰਨ ਵਾਲੇ ਵਿਗਿਆਪਨ ਦਿਖਾਉਂਦੀ ਹੈ।</translation>
-<translation id="286512204874376891">ਆਭਾਸੀ ਕਾਰਡ ਤà©à¨¹à¨¾à¨¨à©‚à©° ਸੰਭਾਵੀ ਧੋਖਾਧੜੀ ਤੋਂ ਬਚਾਉਣ ਵਿੱਚ ਮਦਦ ਕਰਨ ਲਈ ਤà©à¨¹à¨¾à¨¡à©‡ ਅਸਲ ਕਾਰਡ ਦਾ ਭੇਸ ਲੈਂਦਾ ਹੈ। <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ਦੋਸਤਾਨਾ</translation>
<translation id="28761159517501904">ਫ਼ਿਲਮਾਂ</translation>
<translation id="2876489322757410363">ਕਿਸੇ ਬਾਹਰੀ à¨à¨ªà¨²à©€à¨•à©‡à¨¸à¨¼à¨¨ ਰਾਹੀਂ ਭà©à¨—ਤਾਨ ਕਰਨ ਲਈ ਇਨਕੋਗਨਿਟੋ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਇਆ ਜਾ ਰਿਹਾ ਹੈ। ਕੀ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">ਡਿਸਕ</translation>
<translation id="3162559335345991374">ਤà©à¨¹à¨¾à¨¡à©‡ ਵੱਲੋਂ ਵਰਤਿਆ ਜਾ ਰਿਹਾ ਵਾਈ-ਫਾਈ ਇਹ ਚਾਹ ਸਕਦਾ ਹੈ ਕਿ ਤà©à¨¸à©€à¨‚ ਇਸਦੇ ਲੌਗ-ਇਨ ਪੰਨੇ 'ਤੇ ਜਾਓ।</translation>
<translation id="3169472444629675720">ਖੋਜੋ</translation>
-<translation id="3174168572213147020">ਟਾਪੂ</translation>
<translation id="3176929007561373547">ਪà©à¨°à©Œà¨•à¨¸à©€ ਸਰਵਰ ਕੰਮ ਕਰ ਰਿਹਾ ਹੈ ਇਹ ਪੱਕਾ ਕਰੋ ਕਰਨ ਲਈ ਆਪਣੀਆਂ ਪà©à¨°à©Œà¨•à¨¸à©€ ਸੈਟਿੰਗਾਂ ਦੀ ਜਾਂਚ ਕਰੋ ਜਾਂ ਆਪਣੇ ਨੈੱਟਵਰਕ ਪà©à¨°à¨¸à¨¼à¨¾à¨¸à¨• ਨੂੰ ਸੰਪਰਕ ਕਰੋ, ਜੇਕਰ ਤà©à¨¸à©€à¨‚ ਪੱਕਾ ਨਹੀਂ ਕਰਦੇ ਤਾਂ ਤà©à¨¹à¨¾à¨¨à©‚à©° ਇੱਕ ਪà©à¨°à©Œà¨•à¨¸à©€ ਸਰਵਰ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਚਾਹੀਦੀ ਹੈ:
<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">ਜਾਣਨਾ ਚਾਹà©à©°à¨¦à©€ ਹੈ ਕਿ ਤà©à¨¸à©€à¨‚ ਕਿਰਿਆਸ਼ੀਲ ਤੌਰ 'ਤੇ ਇਸ ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਦੋਂ ਕਰਦੇ ਹੋ</translation>
@@ -875,9 +880,6 @@
<translation id="3369192424181595722">ਘੜੀ ਵਿੱਚ ਗੜਬੜ</translation>
<translation id="3369459162151165748">ਵਾਹਨ ਦੇ ਪà©à¨°à©›à©‡ ਅਤੇ à¨à¨•à¨¸à©ˆà¨¸à¨°à©€à¨†à¨‚</translation>
<translation id="3371064404604898522">Chrome ਨੂੰ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨° ਵਜੋਂ ਸੈੱਟ ਕਰੋ</translation>
-<translation id="3371076217486966826"><ph name="URL" /> ਇਹ ਕਰਨਾ ਚਾਹà©à©°à¨¦à¨¾ ਹੈ:
- • ਤà©à¨¹à¨¾à¨¡à©‡ ਆਲੇ-ਦà©à¨†à¨²à©‡ ਦਾ 3D ਨਕਸ਼ਾ ਬਣਾਉਣਾ ਅਤੇ ਕੈਮਰਾ ਸਥਿਤੀ ਨੂੰ ਟਰੈਕ ਕਰਨਾ
- • ਤà©à¨¹à¨¾à¨¡à©‡ ਕੈਮਰੇ ਦੀ ਵਰਤੋਂ ਕਰਨਾ</translation>
<translation id="337363190475750230">ਡੀਪà©à¨°à©‹à¨µà¨¿à¨œà¨¼à¨¨à¨¡</translation>
<translation id="3375754925484257129">Chrome ਸà©à¨°à©±à¨–ਿਆ ਜਾਂਚ ਚਲਾਓ</translation>
<translation id="3377144306166885718">ਸਰਵਰ ਨੇ TLS ਦਾ ਬਹà©à¨¤ ਪà©à¨°à¨¾à¨£à¨¾ ਵਰਜਨ ਵਰਤਿਆ।</translation>
@@ -894,6 +896,7 @@
<translation id="3399952811970034796">ਡਿਲੀਵਰੀ ਪਤਾ</translation>
<translation id="3402261774528610252">ਕਨੈਕਸ਼ਨ ਨੇ ਇਸ ਸਾਈਟ ਨੂੰ ਲੋਡ ਕਰਨ ਲਈ TLS 1.0 ਜਾਂ TLS 1.1 ਨੂੰ ਵਰਤਿਆ, ਜਿਨà©à¨¹à¨¾à¨‚ ਨੂੰ ਨਾਪਸੰਦ ਕੀਤਾ ਗਿਆ ਅਤੇ ਭਵਿੱਖ ਵਿੱਚ ਬੰਦ ਕੀਤਾ ਜਾਵੇਗਾ। ਬੰਦ ਕਰਨ 'ਤੇ, ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਇਹ ਸਾਈਟ ਲੋਡ ਕਰਨ ਤੋਂ ਰੋਕਿਆ ਜਾਵੇਗਾ। ਸਰਵਰ ਨੂੰ TLS 1.2 ਜਾਂ ਬਾਅਦ ਵਾਲੇ ਵਰਜਨ ਨੂੰ ਵਰਤਣਾ ਚਾਹੀਦਾ ਹੈ।</translation>
<translation id="3405664148539009465">ਫੌਂਟਾਂ ਨੂੰ ਵਿਉਂਤਬੱਧ ਕਰੋ</translation>
+<translation id="3407789382767355356">ਤੀਜੀ-ਧਿਰ ਦਾ ਸਾਈਨ-ਇਨ</translation>
<translation id="3409896703495473338">ਸà©à¨°à©±à¨–ਿਆ ਸੈਟਿੰਗਾਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ</translation>
<translation id="3414952576877147120">ਆਕਾਰ:</translation>
<translation id="3417660076059365994">ਤà©à¨¹à¨¾à¨¡à©‡ ਵੱਲੋਂ ਅੱਪਲੋਡ ਜਾਂ ਨੱਥੀ ਕੀਤੀਆਂ ਫ਼ਾਈਲਾਂ ਨੂੰ ਵਿਸ਼ਲੇਸ਼ਣ ਲਈ Google ਕਲਾਊਡ ਜਾਂ ਤੀਜੀਆਂ ਧਿਰਾਂ ਨੂੰ ਭੇਜਿਆ ਜਾਂਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਉਹਨਾਂ ਨੂੰ ਸੰਵੇਦਨਸ਼ੀਲ ਡਾਟੇ ਜਾਂ ਮਾਲਵੇਅਰ ਲਈ ਸਕੈਨ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।</translation>
@@ -926,6 +929,7 @@
<translation id="3477679029130949506">ਫ਼ਿਲਮ ਸੂਚੀਆਂ ਅਤੇ ਥੀà¨à¨Ÿà¨° ਸ਼ੋਅਟਾਈਮ</translation>
<translation id="3479552764303398839">ਹà©à¨£ ਨਹੀਂ</translation>
<translation id="3484560055331845446">ਤà©à¨¸à©€à¨‚ ਆਪਣੇ Google ਖਾਤੇ ਤੱਕ ਪਹà©à©°à¨š ਗà©à¨† ਸਕਦੇ ਹੋ। Chrome ਵੱਲੋਂ ਹà©à¨£à©‡ ਤà©à¨¹à¨¾à¨¡à¨¾ ਪਾਸਵਰਡ ਬਦਲਣ ਦੀ ਸਿਫ਼ਾਰਸ਼ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਤà©à¨¹à¨¾à¨¨à©‚à©° ਸਾਈਨ-ਇਨ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਵੇਗਾ।</translation>
+<translation id="3484861421501147767">ਰਿਮਾਈਂਡਰ: ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ ਪà©à¨°à©‹à¨®à©‹ ਕੋਡ ਉਪਲਬਧ ਹੈ</translation>
<translation id="3487845404393360112">ਟà©à¨°à©‡à¨… 4</translation>
<translation id="3495081129428749620">ਪੰਨੇ ਵਿੱਚ ਲੱਭੋ
<ph name="PAGE_TITLE" /></translation>
@@ -1049,6 +1053,7 @@
<translation id="3810973564298564668">ਵਿਵਸਥਿਤ ਕਰੋ</translation>
<translation id="3816482573645936981">ਮà©à©±à¨² (ਪà©à¨°à¨¤à¨¿à¨¸à¨¥à¨¾à¨ªà¨¨ ਕੀਤਾ)</translation>
<translation id="382518646247711829">ਜੇਕਰ ਤà©à¨¸à©€à¨‚ ਇੱਕ ਪà©à¨°à©Œà¨•à¨¸à©€ ਸਰਵਰ ਵਰਤਦੇ ਹੋ...</translation>
+<translation id="3826050100957962900">ਤੀਜੀ-ਧਿਰ ਦਾ ਸਾਈਨ-ਇਨ</translation>
<translation id="3827112369919217609">ਸੰਪੂਰਨ</translation>
<translation id="3827666161959873541">ਪਰਿਵਾਰਕ ਫ਼ਿਲਮਾਂ</translation>
<translation id="3828924085048779000">ਖਾਲੀ ਪਾਸਫਰੇਜ਼ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।</translation>
@@ -1061,9 +1066,9 @@
<translation id="3858027520442213535">ਤਾਰੀਖ ਅਤੇ ਸਮਾਂ ਅੱਪਡੇਟ ਕਰੋ</translation>
<translation id="3858860766373142691">ਨਾਮ</translation>
<translation id="3872834068356954457">ਵਿਗਿਆਨ</translation>
+<translation id="3875783148670536197">ਮੈਨੂੰ ਤਰੀਕਾ ਦਿਖਾਓ</translation>
<translation id="3881478300875776315">ਘੱਟ ਲਾਈਨਾਂ ਦਿਖਾਓ</translation>
<translation id="3884278016824448484">ਵਿਪਰੀਤ ਡੀਵਾਈਸ ਪਛਾਣਕਰਤਾ</translation>
-<translation id="3885155851504623709">ਪੈਰਿਸ਼</translation>
<translation id="388632593194507180">ਨਿਗਰਾਨੀ ਦਾ ਪਤਾ ਲੱਗਿਆ</translation>
<translation id="3886948180919384617">ਸਟੈਕਰ 3</translation>
<translation id="3890664840433101773">ਈਮੇਲ ਸ਼ਾਮਲ ਕਰੋ</translation>
@@ -1093,9 +1098,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> ਨੂੰ ਬਲੌਕ ਕੀਤਾ ਗਿਆ ਹੈ</translation>
<translation id="3978338123949022456">ਖੋਜ ਮੋਡ, <ph name="KEYWORD_SUFFIX" /> ਨਾਲ ਖੋਜ ਕਰਨ ਲਈ ਪà©à©±à¨›à¨—ਿੱਛ ਟਾਈਪ ਕਰ ਕੇ Enter ਦਬਾਓ</translation>
<translation id="398470910934384994">ਪੰਛੀ</translation>
+<translation id="3985750352229496475">ਪਤਿਆਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ...</translation>
<translation id="3986705137476756801">ਫ਼ਿਲਹਾਲ ਲਈ ਲਾਈਵ ਸà©à¨°à¨–ੀਆਂ ਬੰਦ ਕਰੋ</translation>
<translation id="3987940399970879459">1 MB ਤੋਂ ਘੱਟ</translation>
<translation id="3990250421422698716">ਜੌਗ ਆਫ਼ਸੈੱਟ</translation>
+<translation id="3992684624889376114">ਇਸ ਪੰਨੇ ਬਾਰੇ</translation>
<translation id="3996311196211510766">ਸਾਈਟ <ph name="ORIGIN" /> ਨੇ ਬੇਨਤੀ ਕੀਤੀ ਹੈ ਕਿ ਮੂਲ ਸੰਬੰਧੀ ਨੀਤੀ ਨੂੰ ਉਸ ਦੀਆਂ ਸਾਰੀਆਂ ਬੇਨਤੀਆਂ 'ਤੇ ਲਾਗੂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਪਰ ਇਸ ਨੀਤੀ ਨੂੰ ਫਿਲਹਾਲ ਲਾਗੂ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।</translation>
<translation id="4006465311664329701">Google Pay ਦੀ ਵਰਤੋਂ ਕਰਨ ਵਾਲੀਆਂ ਭà©à¨—ਤਾਨ ਵਿਧੀਆਂ, ਪੇਸ਼ਕਸ਼ਾਂ ਅਤੇ ਪਤੇ</translation>
<translation id="4009243425692662128">ਤà©à¨¹à¨¾à¨¡à©‡ ਵੱਲੋਂ ਪà©à¨°à¨¿à©°à¨Ÿ ਕੀਤੇ ਗਠਪੰਨਿਆਂ ਦੀ ਸਮੱਗਰੀ ਨੂੰ ਵਿਸ਼ਲੇਸ਼ਣ ਲਈ Google ਕਲਾਊਡ ਜਾਂ ਤੀਜੀਆਂ ਧਿਰਾਂ ਨੂੰ ਭੇਜਿਆ ਜਾਂਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਸਨੂੰ ਸੰਵੇਦਨਸ਼ੀਲ ਡਾਟੇ ਲਈ ਸਕੈਨ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।</translation>
@@ -1215,6 +1222,7 @@
<translation id="4305666528087210886">ਤà©à¨¹à¨¾à¨¡à©€ ਫ਼ਾਈਲ 'ਤੇ ਪਹà©à©°à¨š ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ</translation>
<translation id="4306529830550717874">ਕੀ ਤà©à¨¸à©€à¨‚ ਪਤਾ ਰੱਖਿਅਤ ਕਰਨਾ ਚਾਹà©à©°à¨¦à©‡ ਹੋ?</translation>
<translation id="4306812610847412719">ਕਲਿੱਪਬੋਰਡ</translation>
+<translation id="4310070645992025887">ਆਪਣੇ ਖੋਜ ਸਫ਼ਰਾਂ ਲਈ ਖੋਜ ਕਰੋ</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">ਬਲਾਕ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)</translation>
<translation id="4314815835985389558">ਸਿੰਕ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ</translation>
@@ -1265,6 +1273,7 @@
<translation id="4435702339979719576">ਪੋਸਟਕਾਰਡ)</translation>
<translation id="443673843213245140">ਇੱਕ ਪà©à¨°à©Œà¨•à¨¸à©€ ਦੀ ਵਰਤੋਂ ਅਸਮਰਥਿਤ ਹੈ ਪਰੰਤੂ ਇੱਕ ਸਪਸ਼ਟ ਪà©à¨°à©Œà¨•à¨¸à©€ ਕੌਂਫਿਗਰੇਸ਼ਨ ਨਿਸ਼ਚਿਤ ਹੈ।</translation>
<translation id="4441832193888514600">ਅਣਡਿੱਠ ਕੀਤਾ ਗਿਆ ਕਿਉਂਕਿ ਨੀਤੀ ਨੂੰ ਸਿਰਫ਼ ਕਲਾਊਡ ਵਰਤੋਂਕਾਰ ਨੀਤੀ ਵਜੋਂ ਹੀ ਸੈੱਟ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।</translation>
+<translation id="4442470707340296952">Chrome ਟੈਬ</translation>
<translation id="4450893287417543264">ਦà©à¨¬à¨¾à¨°à¨¾ ਨਾ ਦਿਖਾਓ</translation>
<translation id="4451135742916150903">ਸਾਈਟ HID ਡੀਵਾਈਸਾਂ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਲਈ ਪà©à©±à¨› ਸਕਦੀ ਹੈ</translation>
<translation id="4452328064229197696">ਤà©à¨¹à¨¾à¨¡à©‡ ਵੱਲੋਂ ਹà©à¨£à©‡ ਹੀ ਵਰਤਿਆ ਗਿਆ ਪਾਸਵਰਡ ਡਾਟਾ ਉਲੰਘਣਾ ਵਿੱਚ ਮਿਲਿਆ ਸੀ। ਆਪਣੇ ਖਾਤਿਆਂ ਨੂੰ ਸà©à¨°à©±à¨–ਿਅਤ ਕਰਨ ਲਈ, Google ਪਾਸਵਰਡ ਪà©à¨°à¨¬à©°à¨§à¨• ਆਪਣੇ ਰੱਖਿਅਤ ਕੀਤੇ ਪਾਸਵਰਡਾਂ ਦੀ ਜਾਂਚ ਕਰਨ ਦੀ ਸਿਫ਼ਾਰਸ਼ ਕਰਦਾ ਹੈ।</translation>
@@ -1403,6 +1412,7 @@
<translation id="483241715238664915">ਚਿਤਾਵਨੀਆਂ ਚਾਲੂ ਕਰੋ</translation>
<translation id="4834250788637067901">Google Pay ਦੀ ਵਰਤੋਂ ਕਰਨ ਵਾਲੀਆਂ ਭà©à¨—ਤਾਨ ਵਿਧੀਆਂ, ਪੇਸ਼ਕਸ਼ਾਂ ਅਤੇ ਪਤੇ</translation>
<translation id="4838327282952368871">ਸà©à¨ªà¨¨à¨®à¨ˆ</translation>
+<translation id="4839087176073128681">ਅਗਲੀ ਵਾਰ ਜ਼ਿਆਦਾ ਜਲਦੀ ਭà©à¨—ਤਾਨ ਕਰੋ ਅਤੇ ਆਪਣੇ ਕਾਰਡ ਨੂੰ Google ਦੀ ਉੱਨਤ ਸà©à¨°à©±à¨–ਿਆ ਵਿਸ਼ੇਸ਼ਤਾ ਨਾਲ ਸà©à¨°à©±à¨–ਿਅਤ ਰੱਖੋ।</translation>
<translation id="4840250757394056958">ਆਪਣਾ Chrome ਇਤਿਹਾਸ ਦੇਖੋ</translation>
<translation id="484462545196658690">ਸਵੈ</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> ਅਤੇ ਹੋਰ ਚੀਜ਼ਾਂ 'ਤੇ ਛੋਟ ਪà©à¨°à¨¾à¨ªà¨¤ ਕਰੋ</translation>
@@ -1465,6 +1475,7 @@
<translation id="5011561501798487822">ਪਛਾਣੀ ਗਈ ਭਾਸ਼ਾ</translation>
<translation id="5015510746216210676">ਮਸ਼ੀਨ ਦਾ ਨਾਮ:</translation>
<translation id="5017554619425969104">ਤà©à¨¹à¨¾à¨¡à©‡ ਵੱਲੋਂ ਕਾਪੀ ਕੀਤੀ ਲਿਖਤ</translation>
+<translation id="5017828934289857214">ਮੈਨੂੰ ਬਾਅਦ ਵਿੱਚ ਯਾਦ ਕਰਵਾਓ</translation>
<translation id="5018422839182700155">ਇਸ ਪੰਨੇ ਨੂੰ ਨਹੀਂ ਖੋਲà©à¨¹à¨¿à¨† ਜਾ ਸਕਦਾ</translation>
<translation id="5019198164206649151">ਬੈਕਿੰਗ ਸਟੋਰ ਖ਼ਰਾਬ ਸਥਿਤੀ ਵਿੱਚ</translation>
<translation id="5020776957610079374">ਵਿਸ਼ਵ ਸੰਗੀਤ</translation>
@@ -1472,7 +1483,7 @@
<translation id="5029568752722684782">ਕਾਪੀ ਹਟਾਓ</translation>
<translation id="5030338702439866405">ਵੱਲੋਂ ਜਾਰੀ ਕੀਤਾ</translation>
<translation id="503069730517007720">"<ph name="SOFTWARE_NAME" />" ਲਈ 'ਰੂਟ ਪà©à¨°à¨®à¨¾à¨£-ਪੱਤਰ' ਲੋੜੀਂਦਾ ਹੈ ਪਰ ਸਥਾਪਤ ਨਹੀਂ ਹੈ। ਇਸ ਸਮੱਸਿਆ ਨੂੰ ਠੀਕ ਕਰਨ ਲਈ ਤà©à¨¹à¨¾à¨¡à©‡ ਆਈ.ਟੀ ਪà©à¨°à¨¶à¨¾à¨¸à¨• ਨੂੰ "<ph name="SOFTWARE_NAME" />" ਲਈ ਰੂਪ-ਰੇਖਾ ਹਿਦਾਇਤਾਂ ਨੂੰ ਦੇਖਣਾ ਚਾਹੀਦਾ ਹੈ। <ph name="FURTHER_EXPLANATION" /></translation>
-<translation id="5031870354684148875">Google ਅਨà©à¨µà¨¾à¨¦ ਬਾਰੇ</translation>
+<translation id="5031870354684148875">Google Translate ਬਾਰੇ</translation>
<translation id="503498442187459473"><ph name="HOST" /> ਦੀ ਤà©à¨¹à¨¾à¨¡à¨¾ ਕੈਮਰਾ ਅਤੇ ਮਾਈਕà©à¨°à©‹à©žà©‹à¨¨ ਵਰਤਣ ਦੀ ਇੱਛਾ ਹੈ</translation>
<translation id="5035135400558156732">ਬਾਗਬਾਨੀ</translation>
<translation id="5039762155821394373">ਫ਼ੌਂਟ ਆਕਾਰ</translation>
@@ -1484,6 +1495,7 @@
<translation id="5051305769747448211">ਲਾਈਵ ਕਾਮੇਡੀ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{ਨਜ਼ਦੀਕੀ ਸਾਂਠਦੀ ਵਰਤੋਂ ਕਰਦਿਆਂ ਇਹ ਫ਼ਾਈਲ ਭੇਜਣ ਲਈ, ਆਪਣੇ ਡੀਵਾਈਸ 'ਤੇ ਜਗà©à¨¹à¨¾ (<ph name="DISK_SPACE_SIZE" />) ਖਾਲੀ ਕਰੋ}one{ਨਜ਼ਦੀਕੀ ਸਾਂਠਦੀ ਵਰਤੋਂ ਕਰਦਿਆਂ ਇਹ ਫ਼ਾਈਲ ਭੇਜਣ ਲਈ, ਆਪਣੇ ਡੀਵਾਈਸ 'ਤੇ ਜਗà©à¨¹à¨¾ (<ph name="DISK_SPACE_SIZE" />) ਖਾਲੀ ਕਰੋ}other{ਨਜ਼ਦੀਕੀ ਸਾਂਠਦੀ ਵਰਤੋਂ ਕਰਦਿਆਂ ਇਨà©à¨¹à¨¾à¨‚ ਫ਼ਾਈਲਾਂ ਨੂੰ ਭੇਜਣ ਲਈ, ਆਪਣੇ ਡੀਵਾਈਸ 'ਤੇ ਜਗà©à¨¹à¨¾ (<ph name="DISK_SPACE_SIZE" />) ਖਾਲੀ ਕਰੋ}}</translation>
<translation id="5056549851600133418">ਤà©à¨¹à¨¾à¨¡à©‡ ਲਈ ਲੇਖ</translation>
+<translation id="5060483733937416656">ਤà©à¨¸à©€à¨‚ <ph name="PROVIDER_ORIGIN" /> ਦੀ ਵਰਤੋਂ ਕਰਨ ਵਾਲੀਆਂ ਵੈੱਬਸਾਈਟਾਂ 'ਤੇ Windows Hello ਨਾਲ ਪà©à¨¶à¨Ÿà©€ ਕਰਨਾ ਚà©à¨£à¨¿à¨† ਹੈ। ਇਸ ਪà©à¨°à¨¦à¨¾à¨¨à¨• ਨੇ ਤà©à¨¹à¨¾à¨¡à©€ ਭà©à¨—ਤਾਨ ਵਿਧੀ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਟੋਰ ਕੀਤਾ ਹੋ ਸਕਦਾ ਹੈ, ਜਿਸ ਲਈ ਤà©à¨¸à©€à¨‚ <ph name="LINK_TEXT" /> ਕਰ ਸਕਦੇ ਹੋ।</translation>
<translation id="5061227663725596739">ਕੀ ਤà©à¨¹à¨¾à¨¡à¨¾ ਮਤਲਬ <ph name="LOOKALIKE_DOMAIN" /> ਤੋਂ ਸੀ?</translation>
<translation id="5066056036849835175">ਪà©à¨°à¨¿à©°à¨Ÿà¨¿à©°à¨— ਇਤਿਹਾਸ</translation>
<translation id="5068234115460527047">ਹੇਜ ਫੰਡ</translation>
@@ -1497,10 +1509,8 @@
<translation id="5087286274860437796">ਇਸ ਸਮੇਂ ਸਰਵਰ ਦਾ ਪà©à¨°à¨®à¨¾à¨£ ਪੱਤਰ ਵੈਧ ਨਹੀਂ ਹੈ।</translation>
<translation id="5087580092889165836">ਕਾਰਡ ਸ਼ਾਮਲ ਕਰੋ</translation>
<translation id="5088142053160410913">ਓਪਰੇਟਰ ਲਈ ਸà©à¨¨à©‡à¨¹à¨¾</translation>
-<translation id="5089810972385038852">ਰਾਜ</translation>
<translation id="5093232627742069661">Z-ਤਹਿ</translation>
<translation id="5094747076828555589">ਇਹ ਸਰਵਰ ਇਹ ਸਾਬਤ ਨਹੀਂ ਕਰ ਸਕਿਆ ਕਿ ਇਹ <ph name="DOMAIN" /> ਹੈ; ਇਸਦਾ ਸà©à¨°à©±à¨–ਿਆ ਪà©à¨°à¨®à¨¾à¨£-ਪੱਤਰ Chromium ਵੱਲੋਂ ਭਰੋਸੇਯੋਗ ਨਹੀਂ ਹੈ। ਇਹ ਇੱਕ ਗਲਤ ਸੰਰੂਪਣ ਕਾਰਨ ਹੋ ਸਕਦਾ ਹੈ ਜਾਂ ਕੋਈ ਹਮਲਾਵਰ ਤà©à¨¹à¨¾à¨¡à©‡ ਕਨੈਕਸ਼ਨ ਨੂੰ ਰਾਹ ਵਿੱਚ ਰੋਕ ਰਿਹਾ ਹੈ।</translation>
-<translation id="5095208057601539847">ਸੂਬਾ</translation>
<translation id="5097099694988056070">CPU/RAM ਵਰਤੋਂ ਵਰਗੇ ਡੀਵਾਈਸ ਅੰਕੜੇ</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">ਸਾਈਟ ਸà©à¨°à©±à¨–ਿਅਤ ਨਹੀਂ ਹੈ</translation>
@@ -1538,6 +1548,7 @@
<translation id="5171045022955879922">ਖੋਜੋ ਜਾਂ URL ਟਾਈਪ ਕਰੋ</translation>
<translation id="5171689220826475070">ਫੈਨਫੋਲਡ-ਯੂਰਪੀ</translation>
<translation id="5172758083709347301">ਮਸ਼ੀਨ</translation>
+<translation id="5177076414499237632">ਇਸ ਪੰਨੇ ਦੇ ਸਰੋਤ ਅਤੇ ਵਿਸ਼ੇ ਬਾਰੇ ਜਾਣੋ</translation>
<translation id="5179510805599951267">ਕੀ <ph name="ORIGINAL_LANGUAGE" /> ਵਿੱਚ ਨਹੀਂ ਹੈ? ਇਸ ਗੜਬੜ ਦੀ ਰਿਪੋਰਟ ਕਰੋ</translation>
<translation id="518639307526414276">ਪਾਲਤੂ ਜਾਨਵਰਾਂ ਦਾ ਭੋਜਨ ਅਤੇ ਪਾਲਤੂ ਜਾਨਵਰਾਂ ਦੀ ਦੇਖਭਾਲ ਸੰਬੰਧੀ ਸਮੱਗਰੀ</translation>
<translation id="5190835502935405962">ਬà©à©±à¨•à¨®à¨¾à¨°à¨¸ ਬਾਰ</translation>
@@ -1698,6 +1709,7 @@
<translation id="5624120631404540903">ਪਾਸਵਰਡ ਵਿਵਸਥਿਤ ਕਰੋ</translation>
<translation id="5629630648637658800">ਨੀਤੀ ਸੈਟਿੰਗਾਂ ਲੋਡ ਕਰਨ ਵਿੱਚ ਅਸਫਲ</translation>
<translation id="5631439013527180824">ਅਵੈਧ ਡੀਵਾਈਸ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਟੋਕਨ</translation>
+<translation id="5632485077360054581">ਮੈਨੂੰ ਤਰੀਕਾ ਦਿਖਾਓ</translation>
<translation id="5633066919399395251">ਇਸ ਵੇਲੇ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 'ਤੇ ਮੌਜੂਦ ਹਮਲਾਵਰ ਤà©à¨¹à¨¾à¨¡à©‡ ਕੰਪਿਊਟਰ 'ਤੇ ਅਜਿਹੇ ਖਤਰਨਾਕ ਪà©à¨°à©‹à¨—ਰਾਮ ਸਥਾਪਤ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਸਕਦੇ ਹੋ, ਜੋ ਤà©à¨¹à¨¾à¨¡à©€ ਜਾਣਕਾਰੀ ਚੋਰੀ ਕਰ ਸਕਦੇ ਹਨ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹਨ (ਉਦਾਹਰਨ ਲਈ, ਫ਼ੋਟੋਆਂ, ਪਾਸਵਰਡ, ਸà©à¨¨à©‡à¨¹à©‡, ਅਤੇ ਕà©à¨°à©ˆà¨¡à¨¿à¨Ÿ ਕਾਰਡ)। <ph name="BEGIN_LEARN_MORE_LINK" />ਹੋਰ ਜਾਣੋ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">ਭਰਮਪੂਰਨ ਸਮੱਗਰੀ ਬਲਾਕ ਕੀਤੀ ਗਈ।</translation>
<translation id="5633259641094592098">ਪà©à¨°à¨¸à¨¿à©±à¨§ ਅਤੇ ਇੰਡੀ ਫ਼ਿਲਮਾਂ</translation>
@@ -1815,6 +1827,7 @@
<translation id="5989320800837274978">ਨਾ ਤਾਂ ਸਥਿਰ ਪà©à¨°à©Œà¨•à¨¸à©€ ਸਰਵਰ ਨਾ ਹੀ .pac ਸਕà©à¨°à¨¿à¨ªà¨Ÿ URL ਨਿਰਦਿਸ਼ਟ ਕੀਤੇ ਗਠਹਨ।</translation>
<translation id="5992691462791905444">ਇੰਜੀਨੀਅਰਿੰਗ Z-ਤਹਿ</translation>
<translation id="5995727681868049093">ਆਪਣੇ Google ਖਾਤੇ ਵਿੱਚ ਆਪਣੀ ਜਾਣਕਾਰੀ, ਪਰਦੇਦਾਰੀ ਅਤੇ ਸà©à¨°à©±à¨–ਿਆ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ</translation>
+<translation id="5997247540087773573">ਤà©à¨¹à¨¾à¨¡à©‡ ਵੱਲੋਂ ਹà©à¨£à©‡ ਹੀ ਵਰਤਿਆ ਗਿਆ ਪਾਸਵਰਡ ਡਾਟਾ ਉਲੰਘਣਾ ਵਿੱਚ ਮਿਲਿਆ ਸੀ। ਆਪਣੇ ਖਾਤਿਆਂ ਨੂੰ ਸà©à¨°à©±à¨–ਿਅਤ ਕਰਨ ਲਈ, Google Password Manager ਇਸਨੂੰ ਹà©à¨£à©‡ ਬਦਲਣ ਅਤੇ ਫਿਰ ਤà©à¨¹à¨¾à¨¡à©‡ ਰੱਖਿਅਤ ਕੀਤੇ ਪਾਸਵਰਡਾਂ ਦੀ ਜਾਂਚ ਕਰਨ ਦੀ ਸਿਫ਼ਾਰਸ਼ ਕਰਦਾ ਹੈ।</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' ਲਈ <ph name="RESULT_COUNT" /> ਨਤੀਜੇ</translation>
<translation id="6006484371116297560">ਕਲਾਸਿਕ</translation>
<translation id="6008122969617370890">N-ਤੋਂ-1 ਕà©à¨°à¨®</translation>
@@ -1909,7 +1922,6 @@
<translation id="627746635834430766">ਅਗਲੀ ਵਾਰ ਵਧੇਰੇ ਤੇਜ਼ੀ ਨਾਲ ਭà©à¨—ਤਾਨ ਕਰਨ ਲਈ, ਆਪਣੇ ਕਾਰਡ ਅਤੇ ਬਿਲਿੰਗ ਪਤੇ ਨੂੰ ਆਪਣੇ Google ਖਾਤੇ ਵਿੱਚ ਰੱਖਿਅਤ ਕਰੋ।</translation>
<translation id="6279183038361895380">ਆਪਣਾ ਕਰਸਰ ਵਿਖਾਉਣ ਲਈ |<ph name="ACCELERATOR" />| ਦਬਾਓ</translation>
<translation id="6280223929691119688">ਇਸ ਪਤੇ 'ਤੇ ਅਦਾਇਗੀ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਕੋਈ ਵੱਖਰਾ ਪਤਾ ਚà©à¨£à©‹à¥¤</translation>
-<translation id="6282194474023008486">ਡਾਕ ਕੋਡ</translation>
<translation id="6285507000506177184">'Chrome ਵਿੱਚ ਡਾਊਨਲੋਡਾਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ' ਬਟਨ, Chrome ਵਿੱਚ ਤà©à¨¹à¨¾à¨¡à©‡ ਵੱਲੋਂ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਫ਼ਾਈਲਾਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰਨ ਲਈ Enter ਦਬਾਓ</translation>
<translation id="6289939620939689042">ਪੰਨੇ ਦਾ ਰੰਗ</translation>
<translation id="6290238015253830360">ਤà©à¨¹à¨¾à¨¡à©‡ ਸà©à¨à¨¾à¨ ਗਠਲੇਖ ਇੱਥੇ ਦਿਖਾਈ ਦੇਣਗੇ</translation>
@@ -1931,6 +1943,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> ਤੋਂ ਘੱਟ ਜਗà©à¨¹à¨¾ ਖਾਲੀ ਕਰਦਾ ਹੈ। ਤà©à¨¹à¨¾à¨¡à©€ ਅਗਲੀ ਫੇਰੀ 'ਤੇ ਕà©à¨ ਸਾਈਟਾਂ ਵਧੇਰੇ ਹੌਲੀ ਲੋਡ ਹੋ ਸਕਦੀਆਂ ਹਨ।</translation>
<translation id="6337534724793800597">ਨਾਮ ਮà©à¨¤à¨¾à¨¬à¨• ਨੀਤੀਆਂ ਫਿਲਟਰ ਕਰੋ</translation>
<translation id="6340739886198108203">ਗà©à¨ªà¨¤ ਸਮੱਗਰੀ ਦੇ ਦਿਸਣਯੋਗ ਹੋਣ 'ਤੇ ਪà©à¨°à¨¸à¨¼à¨¾à¨¸à¨• ਨੀਤੀ ਸਕà©à¨°à©€à¨¨à¨¸à¨¼à¨¾à¨Ÿ ਲੈਣ ਜਾਂ ਰਿਕਾਰਡਿੰਗਾਂ ਕਰਨ ਦੀ ਸਿਫ਼ਾਰਸ਼ ਨਹੀਂ ਕਰਦੀ ਹੈ:</translation>
+<translation id="6348220984832452017">ਕਿਰਿਆਸ਼ੀਲ ਭਿੰਨਤਾਵਾਂ</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ਸਥਾਪਤ ਕਰੋ</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ਕੋਈ ਨਹੀਂ}=1{1 ਪਾਸਵਰਡ (<ph name="DOMAIN_LIST" /> ਲਈ, ਸਿੰਕ ਕੀਤਾ ਗਿਆ)}=2{2 ਪਾਸਵਰਡ (<ph name="DOMAIN_LIST" /> ਲਈ, ਸਿੰਕ ਕੀਤੇ ਗà¨)}other{# ਪਾਸਵਰਡ (<ph name="DOMAIN_LIST" /> ਲਈ, ਸਿੰਕ ਕੀਤੇ ਗà¨)}}</translation>
<translation id="6355392890578844978">ਇਹ ਬà©à¨°à¨¾à¨Šà©›à¨° ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਿਸੇ ਕੰਪਨੀ ਜਾਂ ਹੋਰ ਸੰਸਥਾ ਵੱਲੋਂ ਨਹੀਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਡੀਵਾਈਸ ਦੀ ਸਰਗਰਮੀ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ Chromium ਤੋਂ ਬਾਹਰ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ। <ph name="BEGIN_LINK" />ਹੋਰ ਜਾਣੋ<ph name="END_LINK" /></translation>
@@ -1962,7 +1975,6 @@
<translation id="643051589346665201">Google ਪਾਸਵਰਡ ਬਦਲੋ</translation>
<translation id="6433490469411711332">ਸੰਪਰਕ ਜਾਣਕਾਰੀ ਦਾ ਸੰਪਾਦਨ ਕਰੋ</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ਨੇ ਕਨੈਕਟ ਹੋਣ ਤੋਂ ਇਨਕਾਰ ਕੀਤਾ।</translation>
-<translation id="6438025220577812695">ਮੈਨੂੰ ਬਦਲਣ ਦਿਓ</translation>
<translation id="6440503408713884761">ਅਣਡਿੱਠ ਕੀਤਾ ਗਿਆ</translation>
<translation id="6443406338865242315">ਤà©à¨¸à©€à¨‚ ਕਿਹੜੀਆਂ à¨à¨•à¨¸à¨Ÿà©ˆà¨‚ਸ਼ਨਾਂ ਅਤੇ ਪਲੱਗਇਨ ਸਥਾਪਤ ਕੀਤੇ ਹੋਠਹਨ</translation>
<translation id="6446163441502663861">Kahu (ਲਿਫ਼ਾਫ਼ਾ)</translation>
@@ -2092,9 +2104,9 @@
<translation id="6828866289116430505">ਜੈਨੇਟਿਕਸ</translation>
<translation id="6831043979455480757">ਅਨà©à¨µà¨¾à¨¦ ਕਰੋ</translation>
<translation id="6833752742582340615">ਸà©à¨°à©±à¨–ਿਅਤ ਅਤੇ ਵਧੇਰੇ ਤੇਜ਼ ਚੈੱਕ-ਆਊਟਾਂ ਲਈ ਆਪਣੇ ਕਾਰਡ ਅਤੇ ਬਿਲਿੰਗ ਜਾਣਕਾਰੀ ਨੂੰ ਆਪਣੇ Google ਖਾਤੇ ਵਿੱਚ ਰੱਖਿਅਤ ਕਰੋ</translation>
-<translation id="6839929833149231406">ਖੇਤਰ</translation>
<translation id="6846340164947227603">ਕੋਈ ਆਭਾਸੀ ਕਾਰਡ ਨੰਬਰ ਵਰਤੋ...</translation>
<translation id="6852204201400771460">ਕੀ à¨à¨ª ਰੀਲੋਡ ਕਰਨੀ ਹੈ?</translation>
+<translation id="6857776781123259569">ਪਾਸਵਰਡਾਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ...</translation>
<translation id="686485648936420384">ਖਪਤਕਾਰ ਸਰੋਤ</translation>
<translation id="6865412394715372076">ਇਸ ਕਾਰਡ ਦੀ ਫਿਲਹਾਲ ਪà©à¨¸à¨¼à¨Ÿà©€ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।</translation>
<translation id="6869334554832814367">ਨਿੱਜੀ ਲੋਨ</translation>
@@ -2113,7 +2125,7 @@
<translation id="6898699227549475383">ਕੰਪਨੀ (O)</translation>
<translation id="6899000063526916106"><ph name="NUMBER_OF_DIGITS" />-ਅੰਕਾਂ ਦਾ ਕੋਡ ਦਾਖਲ ਕਰੋ</translation>
<translation id="6907293445143367439"><ph name="SITE_NAME" /> ਨੂੰ ਇਹ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ:</translation>
-<translation id="6907458757809079309">ਤੰਦਰà©à¨¸à¨¤à©€</translation>
+<translation id="6907458757809079309">ਫਿੱਟਨੈੱਸ</translation>
<translation id="6910240653697687763"><ph name="URL" /> ਦੀ ਤà©à¨¹à¨¾à¨¡à©‡ MIDI ਡੀਵਾਈਸਾਂ 'ਤੇ ਪੂਰਾ ਕੰਟਰੋਲ ਕਰਨ ਦੀ ਇੱਛਾ ਹੈ</translation>
<translation id="691024665142758461">ਕਈ ਫ਼ਾਈਲਾਂ ਡਾਊਨਲੋਡ ਕਰੋ</translation>
<translation id="6915804003454593391">ਵਰਤੋਂਕਾਰ:</translation>
@@ -2143,7 +2155,6 @@
<translation id="6965978654500191972">ਡੀਵਾਈਸ</translation>
<translation id="696703987787944103">ਉਲਟ ਰੰਗੀ</translation>
<translation id="6968269510885595029">ਆਪਣੀ ਸà©à¨°à©±à¨–ਿਆ ਕà©à©°à¨œà©€ ਵਰਤੋ</translation>
-<translation id="6970216967273061347">ਜ਼ਿਲਾ</translation>
<translation id="6971439137020188025">Slides ਵਿੱਚ ਤੇਜ਼ੀ ਨਾਲ ਨਵੀਂ Google ਪੇਸ਼ਕਾਰੀ ਬਣਾਓ</translation>
<translation id="6972629891077993081">HID ਡੀਵਾਈਸ</translation>
<translation id="6973656660372572881">ਦੋਵੇਂ ਸਥਿਰ ਪà©à¨°à©Œà¨•à¨¸à©€ ਸਰਵਰ ਅਤੇ ਇੱਕ .pac ਸਕà©à¨°à¨¿à¨ªà¨Ÿ URL ਨਿਰਦਿਸ਼ਟ ਹਨ।</translation>
@@ -2182,7 +2193,6 @@
<translation id="7081308185095828845">ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਤà©à¨¹à¨¾à¨¡à©‡ ਡੀਵਾਈਸ 'ਤੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ</translation>
<translation id="7083258188081898530">ਟà©à¨°à©‡à¨… 9</translation>
<translation id="7086090958708083563">ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਅੱਪਲੋਡ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਗਈ</translation>
-<translation id="7087282848513945231">ਕਾਉਂਟੀ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Tab ਦਬਾਓ, ਫਿਰ Chrome ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਜਾਜ਼ਤਾਂ ਅਤੇ ਸਾਰੀਆਂ ਸਾਈਟਾਂ ਵਿੱਚ ਸਟੋਰ ਕੀਤੇ ਡਾਟੇ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰਨ ਲਈ Enter ਦਬਾਓ</translation>
<translation id="7096937462164235847">ਇਸ ਵੈੱਬਸਾਈਟ ਦੀ ਪਛਾਣ ਦੀ ਪà©à¨¶à¨Ÿà©€ ਨਹੀਂ ਹੋਈ ਹੈ।</translation>
<translation id="7101893872976785596">ਡਰਾਉਣੀਆਂ ਫ਼ਿਲਮਾਂ</translation>
@@ -2201,10 +2211,10 @@
<ph name="LIST_ITEM" />ਫ਼ਾਰਮਾਂ ਵਿੱਚ ਦਾਖਲ ਕੀਤੀ ਜਾਣਕਾਰੀ<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">ਇਸ ਪਤੇ 'ਤੇ ਸ਼ਿਪ ਨਹੀਂ ਕਰ ਸਕਦੇ। ਕੋਈ ਵੱਖਰਾ ਪਤਾ ਚà©à¨£à©‹à¥¤</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ਤà©à¨¹à¨¾à¨¡à©‡ ਪà©à¨°à¨¶à¨¾à¨¸à¨• ਨੇ ਇਸ ਡਾਟੇ ਨੂੰ ਕਾਪੀ ਕੀਤੇ ਜਾਣ ਤੋਂ ਪà©à¨°à¨¤à¨¿à¨¬à©°à¨§à¨¿à¨¤ ਕੀਤਾ ਹੈ।</translation>
<translation id="7135130955892390533">ਸਥਿਤੀ ਦਿਖਾਓ</translation>
<translation id="7138472120740807366">ਅਦਾਇਗੀ ਵਿਧੀ</translation>
-<translation id="7139724024395191329">ਅਮੀਰਾਤ</translation>
<translation id="7139892792842608322">ਪà©à¨°à¨¾à¨‡à¨®à¨°à©€ ਟà©à¨°à©‡à¨…</translation>
<translation id="714064300541049402">ਸਾਈਡ 2 ਚਿੱਤਰ X ਸ਼ਿਫਟ</translation>
<translation id="7152423860607593928">ਨੰਬਰ-14 (ਲਿਫ਼ਾਫ਼ਾ)</translation>
@@ -2421,6 +2431,7 @@
<translation id="7669271284792375604">ਇਸ ਸਾਈਟ 'ਤੇ ਹਮਲਾਵਰ ਉਹਨਾਂ ਪà©à¨°à©‹à¨—ਰਾਮਾਂ ਨੂੰ ਸਥਾਪਤ ਕਰਨ ਲਈ ਤà©à¨¹à¨¾à¨¡à©‡ ਨਾਲ ਚਾਲਬਾਜ਼ੀ ਕਰ ਸਕਦੇ ਹਨ, ਜੋ ਤà©à¨¹à¨¾à¨¡à©‡ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨¿à©°à¨— ਅਨà©à¨­à¨µ ਨੂੰ ਹਾਨੀ ਪਹà©à©°à¨šà¨¾ ਸਕਦੇ ਹਨ (ਉਦਾਹਰਨ ਲਈ, ਤà©à¨¹à¨¾à¨¡à¨¾ ਮà©à©±à¨– ਪੰਨਾ ਬਦਲਦੇ ਹੋਠਜਾਂ ਉਹਨਾਂ ਸਾਈਟਾਂ 'ਤੇ ਵਾਧੂ ਵਿਗਿਆਪਨ ਵਿਖਾਉਂਦੇ ਹੋà¨, ਜਿਨà©à¨¹à¨¾à¨‚ 'ਤੇ ਤà©à¨¸à©€à¨‚ ਜਾਂਦੇ ਹੋ)</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ਗà©à¨ªà¨¤ ਵਜੋਂ ਫਲੈਗ ਕੀਤੇ ਗਠਡਾਟੇ 'ਤੇ ਕੀਤੀਆਂ ਗਈਆਂ ਕਾਰਵਾਈਆਂ (ਲੌਗ-ਇਨ ਤੋਂ ਬਾਅਦ 1 ਕਾਰਵਾਈ)। <ph name="BEGIN_LINK" />ਹੋਰ ਜਾਣੋ<ph name="END_LINK" />}one{ਗà©à¨ªà¨¤ ਵਜੋਂ ਫਲੈਗ ਕੀਤੇ ਗਠਡਾਟੇ 'ਤੇ ਕੀਤੀਆਂ ਗਈਆਂ ਕਾਰਵਾਈਆਂ (ਲੌਗ-ਇਨ ਤੋਂ ਬਾਅਦ # ਕਾਰਵਾਈ)। <ph name="BEGIN_LINK" />ਹੋਰ ਜਾਣੋ<ph name="END_LINK" />}other{ਗà©à¨ªà¨¤ ਵਜੋਂ ਫਲੈਗ ਕੀਤੇ ਗਠਡਾਟੇ 'ਤੇ ਕੀਤੀਆਂ ਗਈਆਂ ਕਾਰਵਾਈਆਂ (ਲੌਗ-ਇਨ ਤੋਂ ਬਾਅਦ # ਕਾਰਵਾਈਆਂ)। <ph name="BEGIN_LINK" />ਹੋਰ ਜਾਣੋ<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ਮੇਲਬਾਕਸ 6</translation>
+<translation id="7675325315208090829">ਭà©à¨—ਤਾਨ ਵਿਧੀਆਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ...</translation>
<translation id="7676643023259824263">ਕਲਿੱਪਬੋਰਡ ਲਿਖਤ, <ph name="TEXT" /> ਖੋਜੋ</translation>
<translation id="7679367271685653708">Chrome ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਆਪਣੇ ਬà©à¨°à¨¾à¨Šà¨œà¨¼à¨¿à©°à¨— ਇਤਿਹਾਸ ਨੂੰ ਦੇਖੋ ਅਤੇ ਉਸ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ</translation>
<translation id="7679947978757153706">ਬੇਸਬਾਲ</translation>
@@ -2463,7 +2474,6 @@
<translation id="7766518757692125295">ਕਿਨਾਰਾ</translation>
<translation id="7770259615151589601">ਮਨੋਨੀਤ-ਲੰਬਾ</translation>
<translation id="7773005668374414287">ਉਹੀ ਕà©à¨°à¨® ਵਿੱਚ ਪਾਸਾ ਉੱਪਰ ਕੀਤੇ</translation>
-<translation id="777702478322588152">ਪà©à¨°à¨«à©€à¨šà¨°</translation>
<translation id="7791011319128895129">ਰੀਲੀਜ਼ ਨਹੀਂ ਕੀਤੀ ਗਈ</translation>
<translation id="7791196057686275387">ਪੰਡ</translation>
<translation id="7791543448312431591">ਜੋੜੋ</translation>
@@ -2554,12 +2564,12 @@
<translation id="8055534648776115597">ਰà©à©›à¨—ਾਰ ਸੰਬੰਧੀ ਅਤੇ ਨਿਰੰਤਰ ਸਿੱਖਿਆ</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" ਦੀ ਸਹੀ ਢੰਗ ਨਾਲ ਰੂਪ-ਰੇਖਾ ਬਦਲੀ ਨਹੀਂ ਗਈ ਹੈ। ਆਮ ਤੌਰ 'ਤੇ "<ph name="SOFTWARE_NAME" />" ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨ 'ਤੇ ਸਮੱਸਿਆ ਠੀਕ ਹੋ ਜਾਂਦੀ ਹੈ। <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ਭੋਜਨ ਉਤਪਾਦਨ</translation>
-<translation id="8066955247577885446">ਮਾਫ਼ ਕਰਨਾ, ਕੋਈ ਗੜਬੜ ਹੋ ਗਈ।</translation>
<translation id="8067872629359326442">ਤà©à¨¸à©€à¨‚ ਹà©à¨£à©‡-ਹà©à¨£à©‡ ਕਿਸੇ ਭਰਮਪੂਰਨ ਸਾਈਟ 'ਤੇ ਆਪਣਾ ਪਾਸਵਰਡ ਦਾਖਲ ਕੀਤਾ ਹੈ। Chromium ਮਦਦ ਕਰ ਸਕਦਾ ਹੈ। ਆਪਣਾ ਪਾਸਵਰਡ ਬਦਲਣ ਅਤੇ ਆਪਣੇ ਖਾਤੇ ਦੇ ਜੋਖਮ ਵਿੱਚ ਹੋਣ ਬਾਰੇ Google ਨੂੰ ਸੂਚਿਤ ਕਰਨ ਲਈ, 'ਖਾਤੇ ਦੀ ਸà©à¨°à©±à¨–ਿਆ ਕਰੋ' 'ਤੇ ਕਲਿੱਕ ਕਰੋ।</translation>
<translation id="8070439594494267500">à¨à¨ª ਪà©à¨°à¨¤à©€à¨•</translation>
<translation id="8074253406171541171">10x13 (ਲਿਫ਼ਾਫ਼ਾ)</translation>
<translation id="8075736640322370409">ਤੇਜ਼ੀ ਨਾਲ ਨਵੀਂ Google ਸ਼ੀਟ ਬਣਾਓ</translation>
<translation id="8075898834294118863">ਸਾਈਟ ਸੈਟਿੰਗਾਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰੋ</translation>
+<translation id="8076492880354921740">ਟੈਬਾਂ</translation>
<translation id="8078141288243656252">ਘà©à¨®à¨¾à¨‰à¨£ 'ਤੇ à¨à¨¨à©‹à¨Ÿà©‡à¨Ÿ ਨਹੀਂ ਕਰ ਸਕਦੇ</translation>
<translation id="8079031581361219619">ਕੀ ਸਾਈਟ ਰੀਲੋਡ ਕਰਨੀ ਹੈ?</translation>
<translation id="8081087320434522107">ਸੇਡਾਨ</translation>
@@ -2682,6 +2692,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ਸੈਟਿੰਗਾਂ</translation>
+<translation id="8428634594422941299">ਸਮਠਲਿਆ</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Tab ਦਬਾਓ, ਫਿਰ Chrome ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਕà©à¨•à©€ ਤਰਜੀਹਾਂ ਦਾ ਪà©à¨°à¨¬à©°à¨§à¨¨ ਕਰਨ ਲਈ Enter ਦਬਾਓ</translation>
<translation id="8433057134996913067">ਇਹ ਤà©à¨¹à¨¾à¨¨à©‚à©° ਜ਼ਿਆਦਾਤਰ ਵੈੱਬਸਾਈਟਾਂ ਤੋਂ ਸਾਈਨ ਆਊਟ ਕਰ ਦੇਵੇਗਾ।</translation>
<translation id="8434840396568290395">ਪਾਲਤੂ ਜਾਨਵਰ</translation>
@@ -2779,6 +2790,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> ਲਈ <ph name="ONE_TIME_CODE" /> ਤà©à¨¹à¨¾à¨¡à¨¾ ਕੋਡ ਹੈ</translation>
<translation id="874918643257405732">ਇਹ ਟੈਬ ਬà©à©±à¨•à¨®à¨¾à¨°à¨• ਕਰੋ</translation>
<translation id="8751426954251315517">ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦà©à¨¬à¨¾à¨°à¨¾ ਕੋਸ਼ਿਸ਼ ਕਰੋ</translation>
+<translation id="8757526089434340176">Google Pay ਪੇਸ਼ਕਸ਼ ਉਪਲਬਧ ਹੈ</translation>
<translation id="8758885506338294482">ਪà©à¨°à¨¤à¨¿à¨¯à©‹à¨—à©€ ਵੀਡੀਓ ਗੇਮਿੰਗ</translation>
<translation id="8759274551635299824">ਇਸ ਕਾਰਡ ਦੀ ਮਿਆਦ ਸਮਾਪਤ ਹੋ ਗਈ ਹੈ</translation>
<translation id="87601671197631245">ਇਹ ਸਾਈਟ ਬਹà©à¨¤ ਪà©à¨°à¨¾à¨£à¨¾ ਸà©à¨°à©±à¨–ਿਆ ਸੰਰੂਪਣ ਵਰਤਦੀ ਹੈ, ਜਿਸ ਕਰਕੇ ਸ਼ਾਇਦ ਤà©à¨¹à¨¾à¨¡à©€ ਜਾਣਕਾਰੀ (ਉਦਾਹਰਨ ਵਜੋਂ, ਪਾਸਵਰਡ, ਸà©à¨¨à©‡à¨¹à©‡ ਜਾਂ ਕà©à¨°à©ˆà¨¡à¨¿à¨Ÿ ਕਾਰਡ) ਨੂੰ ਇਸ ਸਾਈਟ 'ਤੇ ਭੇਜਣ ਵੇਲੇ ਉਹ ਜਾਣਕਾਰੀ ਉਜਾਗਰ ਹੋ ਜਾਵੇ।</translation>
@@ -2786,6 +2798,7 @@
<translation id="8763927697961133303">USB ਡੀਵਾਈਸ</translation>
<translation id="8763986294015493060">ਫ਼ਿਲਹਾਲ ਖà©à©±à¨²à©à¨¹à©€à¨†à¨‚ ਸਾਰੀਆਂ ਇਨਕੋਗਨਿਟੋ ਵਿੰਡੋਆਂ ਨੂੰ ਬੰਦ ਕਰੋ</translation>
<translation id="8766943070169463815">ਸà©à¨°à©±à¨–ਿਅਤ ਭà©à¨—ਤਾਨ ਕà©à¨°à©€à¨¡à©ˆà¨‚ਸ਼ੀਅਲ ਪà©à¨°à¨®à¨¾à¨£à©€à¨•à¨°à¨¨ ਸ਼ੀਟ ਖà©à©±à¨²à©à¨¹à©€ ਹੈ</translation>
+<translation id="8767765348545497220">ਮਦਦ ਬà©à¨²à¨¬à©à¨²à¨¾ ਬੰਦ ਕਰੋ</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">ਮੋਟਰਸਾਈਕਲ</translation>
<translation id="8790007591277257123">&amp;ਮਿਟਾਠਗਠਨੂੰ ਮà©à©œ-ਓਹੀ ਕਰੋ</translation>
@@ -2798,6 +2811,7 @@
<translation id="8806285662264631610">ਇਸ਼ਨਾਨ ਅਤੇ ਸਰੀਰ ਸੰਬੰਧੀ ਉਤਪਾਦ</translation>
<translation id="8807160976559152894">ਹਰ ਪੰਨੇ ਤੋਂ ਬਾਅਦ ਕਟਾਈ</translation>
<translation id="8808828119384186784">Chrome ਸੈਟਿੰਗਾਂ</translation>
+<translation id="8813277370772331957">ਮੈਨੂੰ ਬਾਅਦ ਵਿੱਚ ਯਾਦ ਕਰਵਾਓ</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Tab ਦਬਾਓ, ਫਿਰ ਆਪਣੀਆਂ Chrome ਸੈਟਿੰਗਾਂ ਤੋਂ Chrome ਅੱਪਡੇਟ ਕਰਨ ਲਈ Enter ਦਬਾਓ</translation>
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="882338992931677877">ਮੈਨੂਅਲ ਸਲੌਟ</translation>
@@ -2977,6 +2991,7 @@
<translation id="988159990683914416">ਵਿਕਾਸਕਾਰ ਬਿਲਡ</translation>
<translation id="989988560359834682">ਪਤਾ ਸੰਪਾਦਿਤ ਕਰੋ</translation>
<translation id="991413375315957741">ਮੋਸ਼ਨ ਜਾਂ ਲਾਈਟ ਸੈਂਸਰ</translation>
+<translation id="992110854164447044">ਤà©à¨¹à¨¾à¨¨à©‚à©° ਸੰਭਾਵੀ ਧੋਖਾਧੜੀ ਤੋਂ ਬਚਾਉਣ ਵਿੱਚ ਮਦਦ ਕਰਨ ਲਈ ਆਭਾਸੀ ਕਾਰਡ ਤà©à¨¹à¨¾à¨¡à©‡ ਅਸਲ ਕਾਰਡ ਨੂੰ ਲà©à¨•à¨¾ ਦਿੰਦਾ ਹੈ। <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">ਗà©à¨²à¨¾à¨¬à©€</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" ਤà©à¨¹à¨¾à¨¡à©‡ ਕੰਪਿਊਟਰ ਜਾਂ ਨੈੱਟਵਰਕ 'ਤੇ ਚੰਗੀ ਤਰà©à¨¹à¨¾à¨‚ ਸਥਾਪਤ ਨਹੀਂ ਹੋਇਆ:
diff --git a/chromium/components/strings/components_strings_pl.xtb b/chromium/components/strings/components_strings_pl.xtb
index ca4e27e343a..c9e430cc449 100644
--- a/chromium/components/strings/components_strings_pl.xtb
+++ b/chromium/components/strings/components_strings_pl.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Dotacje, stypendia i wsparcie finansowe</translation>
<translation id="1048785276086539861">Gdy zmodyfikujesz adnotacje, dokument wróci do widoku jednej strony</translation>
<translation id="1050038467049342496">Zamknij inne aplikacje</translation>
+<translation id="1053959602163383901">W witrynach korzystających z <ph name="PROVIDER_ORIGIN" /> potwierdzasz swoją tożsamość za pomocą urządzenia uwierzytelniającego. Możliwe, że ten dostawca przechowuje dane Twojej formy płatności. Możesz <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Cofnij dodanie</translation>
<translation id="1056663316309890343">Oprogramowanie do zdjęć</translation>
<translation id="1056898198331236512">Ostrzeżenie</translation>
@@ -119,6 +120,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="1270502636509132238">Metoda odbioru</translation>
<translation id="1281476433249504884">Układarka 1</translation>
<translation id="1285320974508926690">Nigdy nie tłumacz tej witryny</translation>
+<translation id="1288548991597756084">Zapisz bezpiecznie kartÄ™</translation>
<translation id="1292571435393770077">Taca 16</translation>
<translation id="1292701964462482250">„Oprogramowanie na Twoim komputerze uniemożliwia Chrome bezpieczne połączenie się z internetem†(tylko na komputerach z systemem Windows)</translation>
<translation id="1294154142200295408">Odmiany w wierszu poleceń</translation>
@@ -223,6 +225,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
&lt;p&gt;Aby naprawić błąd, kliknij &lt;strong&gt;Połącz&lt;/strong&gt; na stronie, którą chcesz otworzyć.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Architektura krajobrazu</translation>
<translation id="1513706915089223971">Lista wpisów historii</translation>
+<translation id="1516097932025103760">Zostanie ona zaszyfrowana i bezpiecznie zapisana. Kod CVC nigdy nie jest zapisywany.</translation>
<translation id="1517433312004943670">Numer telefonu jest wymagany</translation>
<translation id="1519264250979466059">Data kompilacji</translation>
<translation id="1521159554480556801">Sztuka włókiennicza i tekstylna</translation>
@@ -288,6 +291,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="1662550410081243962">Zapisuj i automatycznie uzupełniaj informacje o formach płatności</translation>
<translation id="1663943134801823270">Karty i adresy pochodzą z Chrome. Możesz nimi zarządzać w <ph name="BEGIN_LINK" />Ustawieniach<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Od teraz strony, których językiem jest <ph name="SOURCE_LANGUAGE" />, będą tłumaczone na <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Aby skorzystać z oferty, zapłać kartą <ph name="CARD_DETAIL" /></translation>
<translation id="1674504678466460478">Z: <ph name="SOURCE_LANGUAGE" /> na: <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Najpierw krótsza krawędź</translation>
<translation id="168693727862418163">Nie udało się zweryfikować tej wartości zasady względem jej schematu. Będzie ona ignorowana.</translation>
@@ -306,6 +310,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="1717218214683051432">Czujniki ruchu</translation>
<translation id="1717494416764505390">Zestaw tac odbiorczych 3</translation>
<translation id="1718029547804390981">Dokument jest za duży i nie można dodawać do niego adnotacji</translation>
+<translation id="1720941539803966190">Zamknij samouczek</translation>
<translation id="1721424275792716183">* Pole jest wymagane</translation>
<translation id="1727613060316725209">Certyfikat jest ważny</translation>
<translation id="1727741090716970331">Dodaj prawidłowy numer karty</translation>
@@ -343,7 +348,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="1807246157184219062">Jasny</translation>
<translation id="1807528111851433570">Arkusz poczÄ…tkowy</translation>
<translation id="1812527064848182527">orientacja pozioma</translation>
-<translation id="1813414402673211292">Wyczyść historię przeglądania</translation>
+<translation id="1813414402673211292">Wyczyść dane przeglądania</translation>
<translation id="182139138257690338">pobieranie automatyczne</translation>
<translation id="1821930232296380041">Nieprawidłowe żądanie lub jego parametry</translation>
<translation id="1822540298136254167">Odwiedzone strony internetowe i spędzony na nich czas</translation>
@@ -418,8 +423,8 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="205212645995975601">Grillowanie</translation>
<translation id="2053111141626950936">Strony w tym języku (<ph name="LANGUAGE" />) nie będą tłumaczone.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Gdy ta opcja jest włączona i ma stan aktywny, Chrome znajduje dużą grupę osób (kohortę), której aktywność związana z przeglądaniem jest najbardziej podobna do Twojej. Reklamodawcy mogą wybierać reklamy dla grupy, a Twoja aktywność związana z przeglądaniem pozostaje prywatna i jest przechowywana tylko na Twoim urządzeniu. Grupa, do której należysz, jest codziennie aktualizowana.}=1{Gdy ta opcja jest włączona i ma stan aktywny, Chrome znajduje dużą grupę osób (kohortę), której aktywność związana z przeglądaniem jest najbardziej podobna do Twojej. Reklamodawcy mogą wybierać reklamy dla grupy, a Twoja aktywność związana z przeglądaniem pozostaje prywatna i jest przechowywana tylko na Twoim urządzeniu. Grupa, do której należysz, jest codziennie aktualizowana.}few{Gdy ta opcja jest włączona i ma stan aktywny, Chrome znajduje dużą grupę osób (kohortę), której aktywność związana z przeglądaniem jest najbardziej podobna do Twojej. Reklamodawcy mogą wybierać reklamy dla grupy, a Twoja aktywność związana z przeglądaniem pozostaje prywatna i jest przechowywana tylko na Twoim urządzeniu. Grupa, do której należysz, jest aktualizowana co {NUM_DAYS} dni.}many{Gdy ta opcja jest włączona i ma stan aktywny, Chrome znajduje dużą grupę osób (kohortę), której aktywność związana z przeglądaniem jest najbardziej podobna do Twojej. Reklamodawcy mogą wybierać reklamy dla grupy, a Twoja aktywność związana z przeglądaniem pozostaje prywatna i jest przechowywana tylko na Twoim urządzeniu. Grupa, do której należysz, jest aktualizowana co {NUM_DAYS} dni.}other{Gdy ta opcja jest włączona i ma stan aktywny, Chrome znajduje dużą grupę osób (kohortę), której aktywność związana z przeglądaniem jest najbardziej podobna do Twojej. Reklamodawcy mogą wybierać reklamy dla grupy, a Twoja aktywność związana z przeglądaniem pozostaje prywatna i jest przechowywana tylko na Twoim urządzeniu. Grupa, do której należysz, jest aktualizowana co {NUM_DAYS} dnia.}}</translation>
-<translation id="2053553514270667976">Kod pocztowy</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 podpowiedź}few{# podpowiedzi}many{# podpowiedzi}other{# podpowiedzi}}</translation>
+<translation id="2066915425250589881">poprosić o ich usunięcie</translation>
<translation id="2068528718802935086">Niemowlęta i małe dzieci</translation>
<translation id="2071156619270205202">Ta karta nie kwalifikuje siÄ™ do nadania numeru karty wirtualnej.</translation>
<translation id="2071692954027939183">Powiadomienia zostały automatycznie zablokowane, bo zazwyczaj na nie nie zezwalasz</translation>
@@ -431,7 +436,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="2088086323192747268">Przycisk zarządzania synchronizacją; aby zarządzać w ustawieniach Chrome danymi, które mają być synchronizowane, naciśnij Enter</translation>
<translation id="2091887806945687916">Dźwięk</translation>
<translation id="2094505752054353250">Niewłaściwa domena</translation>
-<translation id="2096368010154057602">Departament</translation>
<translation id="2099652385553570808">Trzy zszywki po lewej</translation>
<translation id="2101225219012730419">Wersja:</translation>
<translation id="2102134110707549001">Zaproponuj silne hasło…</translation>
@@ -468,7 +472,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="2185836064961771414">Futbol amerykański</translation>
<translation id="2187317261103489799">Wykrywaj (domyślnie)</translation>
<translation id="2188375229972301266">Wiele otworów na dole</translation>
-<translation id="2188852899391513400">Użyte właśnie hasło znaleźliśmy jako ujawnione w wyniku naruszenia bezpieczeństwa danych. Menedżer haseł Google zaleca natychmiastową zmianę tego hasła, a następnie sprawdzenie zapisanych haseł – pozwoli to zabezpieczyć Twoje konta.</translation>
<translation id="219906046732893612">Remont domu</translation>
<translation id="2202020181578195191">Wpisz rok w prawidłowym formacie</translation>
<translation id="22081806969704220">Taca 3</translation>
@@ -479,6 +482,8 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="2215727959747642672">Edytowanie pliku</translation>
<translation id="2215963164070968490">Psy</translation>
<translation id="2218879909401188352">Osoby obecnie atakujące stronę <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogą instalować niebezpieczne aplikacje, które mogą uszkodzić Twoje urządzenie, dodać ukryte opłaty do rachunku za usługi telefoniczne lub wykraść Twoje dane osobowe. <ph name="BEGIN_LEARN_MORE_LINK" />Więcej informacji<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Ponownie wyświetl samouczek</translation>
+<translation id="2219735899272417925">Wymagane zresetowanie urzÄ…dzenia</translation>
<translation id="2224337661447660594">Brak internetu</translation>
<translation id="2230458221926704099">Napraw połączenie, używając <ph name="BEGIN_LINK" />aplikacji diagnostycznej<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Wyślij teraz</translation>
@@ -576,11 +581,13 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="2512101340618156538">Nie zezwolono (domyślnie)</translation>
<translation id="2512413427717747692">Przycisk ustawiania Chrome jako przeglądarki domyślnej. Naciśnij Enter, aby ustawić Chrome jako domyślną przeglądarkę systemową w ustawieniach iOS</translation>
<translation id="2515629240566999685">Sprawdź sygnał w swojej okolicy</translation>
+<translation id="2515761554693942801">W witrynach korzystających z <ph name="PROVIDER_ORIGIN" /> potwierdzasz swoją tożsamość za pomocą Touch ID. Możliwe, że ten dostawca przechowuje dane Twojej formy płatności. Możesz <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Zszywka w prawym dolnym rogu</translation>
<translation id="2521736961081452453">Utwórz formularz</translation>
<translation id="2523886232349826891">Zapisana tylko na tym urzÄ…dzeniu</translation>
<translation id="2524461107774643265">Dodaj więcej informacji</translation>
<translation id="2529899080962247600">Maksymalna liczba wpisów w tym polu to <ph name="MAX_ITEMS_LIMIT" />. Wszystkie następne wpisy będą ignorowane.</translation>
+<translation id="253493526287553278">Zobacz szczegóły kodu promocyjnego</translation>
<translation id="2535585790302968248">Otwórz nową kartę incognito, aby przeglądać prywatnie</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{i jeszcze 1}few{i jeszcze #}many{i jeszcze #}other{i jeszcze #}}</translation>
<translation id="2536110899380797252">Dodaj adres</translation>
@@ -616,7 +623,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="259821504105826686">Sztuka fotograficzna i cyfrowa</translation>
<translation id="2601150049980261779">Filmy romantyczne</translation>
<translation id="2604589665489080024">Muzyka pop</translation>
-<translation id="2609632851001447353">Odmiany</translation>
<translation id="2610561535971892504">Kliknij, aby skopiować</translation>
<translation id="2617988307566202237">W Chrome <ph name="BEGIN_EMPHASIS" />nie są zapisywane<ph name="END_EMPHASIS" /> te informacje:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Urodziny i imieniny</translation>
<translation id="2677748264148917807">Wyjdź</translation>
+<translation id="2679714844901977852">Aby szybciej dokonywać bezpiecznych płatności, zapisz dane karty i informacje rozliczeniowe na koncie Google <ph name="USER_EMAIL" /></translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Najlepsze dopasowanie</translation>
<translation id="2688969097326701645">Tak, kontynuuj</translation>
@@ -697,7 +704,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="2854764410992194509">Dostawcy usług internetowych</translation>
<translation id="2856444702002559011">Osoby atakujące mogą próbować wykraść Twoje informacje ze strony <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (na przykład hasła, wiadomości lub dane kart kredytowych). <ph name="BEGIN_LEARN_MORE_LINK" />Więcej informacji<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Na tej stronie wyświetlają się uciążliwe lub wprowadzające w błąd reklamy.</translation>
-<translation id="286512204874376891">Karta wirtualna ukrywa Twoją prawdziwą kartę, aby chronić Cię przed oszustwami. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Przyjazny</translation>
<translation id="28761159517501904">Filmy</translation>
<translation id="2876489322757410363">Opuszczasz tryb incognito, aby zapłacić w aplikacji zewnętrznej. Czy chcesz kontynuować?</translation>
@@ -798,7 +804,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="3158539265159265653">Dysk</translation>
<translation id="3162559335345991374">Sieć Wi-Fi, której używasz, może wymagać otwarcia strony logowania.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Wyspa</translation>
<translation id="3176929007561373547">Sprawdź ustawienia serwera proxy lub skontaktuj się z administratorem sieci,
by upewnić się, że serwer proxy działa. Jeśli uważasz, że
nie powinien być on używany:
@@ -877,9 +882,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="3369192424181595722">BÅ‚Ä…d zegara</translation>
<translation id="3369459162151165748">Części i akcesoria samochodowe</translation>
<translation id="3371064404604898522">Ustaw Chrome jako przeglądarkę domyślną</translation>
-<translation id="3371076217486966826"><ph name="URL" /> chce:
- • utworzyć mapę 3D otoczenia i śledzić pozycję kamery,
- • używać Twojej kamery.</translation>
<translation id="337363190475750230">Anulowano obsługę</translation>
<translation id="3375754925484257129">Uruchom kontrolę zabezpieczeń Chrome</translation>
<translation id="3377144306166885718">Serwer użył przestarzałej wersji protokołu TLS.</translation>
@@ -896,6 +898,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="3399952811970034796">Adres dostawy</translation>
<translation id="3402261774528610252">Do połączenia z tą stroną użyto protokołu TLS 1.0 lub 1.1. Obie te wersje zostały wycofane, a w przyszłości zostaną wyłączone. Po ich wyłączeniu użytkownicy nie będą mogli otwierać tej strony. Serwer powinien korzystać z protokołu TLS w wersji 1.2 lub nowszej.</translation>
<translation id="3405664148539009465">Dostosuj czcionki</translation>
+<translation id="3407789382767355356">logowanie się w przypadku innych firm</translation>
<translation id="3409896703495473338">Zarządzaj ustawieniami zabezpieczeń</translation>
<translation id="3414952576877147120">Rozmiar:</translation>
<translation id="3417660076059365994">Przesyłane lub dołączone pliki są wysyłane do Google Cloud lub innych firm w celu przeanalizowania. Na przykład mogą zostać przeskanowane w poszukiwaniu danych wrażliwych lub złośliwego oprogramowania.</translation>
@@ -928,6 +931,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="3477679029130949506">Wykazy filmów i repertuary kin</translation>
<translation id="3479552764303398839">Nie teraz</translation>
<translation id="3484560055331845446">Możesz stracić dostęp do swojego konta Google. Chrome zaleca natychmiastową zmianę hasła. Zobaczysz prośbę, by się zalogować.</translation>
+<translation id="3484861421501147767">Przypomnienie: dostępny jest zapisany kod promocyjny</translation>
<translation id="3487845404393360112">Taca 4</translation>
<translation id="3495081129428749620">Znajdź na stronie
<ph name="PAGE_TITLE" /></translation>
@@ -1052,6 +1056,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="3810973564298564668">ZarzÄ…dzaj</translation>
<translation id="3816482573645936981">Wartość (zastąpiona)</translation>
<translation id="382518646247711829">Jeśli używasz serwera proxy...</translation>
+<translation id="3826050100957962900">Logowanie się w przypadku innych firm</translation>
<translation id="3827112369919217609">Bezwzględna</translation>
<translation id="3827666161959873541">Kino familijne</translation>
<translation id="3828924085048779000">Puste hasło jest niedozwolone.</translation>
@@ -1064,9 +1069,9 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="3858027520442213535">Zaktualizuj datÄ™ i godzinÄ™</translation>
<translation id="3858860766373142691">Nazwa</translation>
<translation id="3872834068356954457">Nauka</translation>
+<translation id="3875783148670536197">Pokaż jak</translation>
<translation id="3881478300875776315">Pokaż mniej wierszy</translation>
<translation id="3884278016824448484">Konflikt identyfikatorów urządzeń</translation>
-<translation id="3885155851504623709">Parafia</translation>
<translation id="388632593194507180">Wykryto monitorowanie</translation>
<translation id="3886948180919384617">Układarka 3</translation>
<translation id="3890664840433101773">Dodaj adres e-mail</translation>
@@ -1096,9 +1101,11 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="3973234410852337861">Strona <ph name="HOST_NAME" /> jest zablokowana</translation>
<translation id="3978338123949022456">Tryb wyszukiwania. Wpisz zapytanie i naciśnij Enter, aby wyszukać w <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Ptaki</translation>
+<translation id="3985750352229496475">ZarzÄ…dzaj adresami...</translation>
<translation id="3986705137476756801">Na razie wyłącz napisy na żywo</translation>
<translation id="3987940399970879459">Mniej niż 1 MB</translation>
<translation id="3990250421422698716">Odsunięcie poszczególnych kopii</translation>
+<translation id="3992684624889376114">Informacje o tej stronie</translation>
<translation id="3996311196211510766">Strona <ph name="ORIGIN" /> zażądała, by do wszystkich kierowanych do niej żądań stosować zasadę dotyczącą źródła, ale tej zasady nie można teraz zastosować.</translation>
<translation id="4006465311664329701">Formy płatności, oferty i adresy korzystające z Google Pay</translation>
<translation id="4009243425692662128">Zawartość drukowanych stron jest wysyłana do Google Cloud lub innych firm w celu przeanalizowania. Może na przykład zostać przeskanowana w poszukiwaniu danych wrażliwych.</translation>
@@ -1218,6 +1225,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="4305666528087210886">Nie udało się uzyskać dostępu do pliku</translation>
<translation id="4306529830550717874">Zapisać adres?</translation>
<translation id="4306812610847412719">schowek</translation>
+<translation id="4310070645992025887">Przeszukaj serie czynności</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokuj (domyślnie)</translation>
<translation id="4314815835985389558">ZarzÄ…dzanie synchronizacjÄ…</translation>
@@ -1268,6 +1276,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="4435702339979719576">pocztówka)</translation>
<translation id="443673843213245140">Korzystanie z serwera proxy jest wyłączone, ale podano konfigurację proxy.</translation>
<translation id="4441832193888514600">Ignorowana, ponieważ tę zasadę można skonfigurować wyłącznie jako dotyczącą użytkowników i obowiązującą w chmurze.</translation>
+<translation id="4442470707340296952">Karty w Chrome</translation>
<translation id="4450893287417543264">Nie pokazuj ponownie</translation>
<translation id="4451135742916150903">Może prosić o zgodę na połączenie z urządzeniami HID</translation>
<translation id="4452328064229197696">Użyte właśnie hasło znaleźliśmy jako ujawnione w wyniku naruszenia bezpieczeństwa danych. Menedżer haseł Google zaleca sprawdzenie zapisanych haseł – pozwoli to zabezpieczyć Twoje konta.</translation>
@@ -1406,6 +1415,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="483241715238664915">Włącz ostrzeżenia</translation>
<translation id="4834250788637067901">Formy płatności, oferty i adresy korzystające z Google Pay</translation>
<translation id="4838327282952368871">Uroczy</translation>
+<translation id="4839087176073128681">Następnym razem zapłać szybciej i zadbaj o bezpieczeństwo swojej karty, korzystając z najlepszych w branży zabezpieczeń od Google.</translation>
<translation id="4840250757394056958">Wyświetl historię Chrome</translation>
<translation id="484462545196658690">Automatycznie</translation>
<translation id="484671803914931257">Otrzymaj rabat w sklepie <ph name="MERCHANT_NAME" /> i nie tylko</translation>
@@ -1468,6 +1478,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="5011561501798487822">Wykryty język</translation>
<translation id="5015510746216210676">Nazwa komputera:</translation>
<translation id="5017554619425969104">Skopiowany tekst</translation>
+<translation id="5017828934289857214">Przypomnij później</translation>
<translation id="5018422839182700155">Nie można otworzyć tej strony</translation>
<translation id="5019198164206649151">Nieprawidłowy stan magazynu wspomagającego</translation>
<translation id="5020776957610079374">Muzyka świata</translation>
@@ -1487,6 +1498,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="5051305769747448211">Kabaret</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Aby wysłać plik przez Udostępnianie w pobliżu, zwolnij miejsce (<ph name="DISK_SPACE_SIZE" />) na urządzeniu}few{Aby wysłać pliki przez Udostępnianie w pobliżu, zwolnij miejsce (<ph name="DISK_SPACE_SIZE" />) na urządzeniu}many{Aby wysłać pliki przez Udostępnianie w pobliżu, zwolnij miejsce (<ph name="DISK_SPACE_SIZE" />) na urządzeniu}other{Aby wysłać pliki przez Udostępnianie w pobliżu, zwolnij miejsce (<ph name="DISK_SPACE_SIZE" />) na urządzeniu}}</translation>
<translation id="5056549851600133418">Artykuły dla Ciebie</translation>
+<translation id="5060483733937416656">W witrynach korzystających z <ph name="PROVIDER_ORIGIN" /> potwierdzasz swoją tożsamość za pomocą Windows Hello. Możliwe, że ten dostawca przechowuje dane Twojej formy płatności. Możesz <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Czy chodziło Ci o <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Historia drukowania</translation>
<translation id="5068234115460527047">Fundusze hedgingowe</translation>
@@ -1500,10 +1512,8 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="5087286274860437796">Certyfikat serwera nie jest obecnie ważny.</translation>
<translation id="5087580092889165836">Dodaj kartÄ™</translation>
<translation id="5088142053160410913">Wiadomość do operatora</translation>
-<translation id="5089810972385038852">Stan</translation>
<translation id="5093232627742069661">Składanie typu Z</translation>
<translation id="5094747076828555589">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat bezpieczeństwa nie jest zaufany w Chromium. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego.</translation>
-<translation id="5095208057601539847">Prowincja</translation>
<translation id="5097099694988056070">Statystyki na temat sprzętu, np. użycie procesora czy pamięci RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Strona nie jest bezpieczna</translation>
@@ -1541,6 +1551,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="5171045022955879922">Wyszukaj lub wpisz URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Komputer</translation>
+<translation id="5177076414499237632">Więcej informacji o źródle i temacie tej strony</translation>
<translation id="5179510805599951267">Jeśli to nie jest język <ph name="ORIGINAL_LANGUAGE" />, zgłoś błąd</translation>
<translation id="518639307526414276">Karma i artykuły dla zwierząt</translation>
<translation id="5190835502935405962">Pasek zakładek</translation>
@@ -1701,6 +1712,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="5624120631404540903">Zarządzaj hasłami</translation>
<translation id="5629630648637658800">Åadowanie ustawieÅ„ zasady nie powiodÅ‚o siÄ™</translation>
<translation id="5631439013527180824">Nieprawidłowy token zarządzania urządzeniem</translation>
+<translation id="5632485077360054581">Pokaż jak</translation>
<translation id="5633066919399395251">Osoby obecnie atakujące stronę <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogą próbować zainstalować na Twoim komputerze niebezpieczne programy, które wykradają lub usuwają informacje (na przykład zdjęcia, hasła, wiadomości lub dane kart kredytowych). <ph name="BEGIN_LEARN_MORE_LINK" />Więcej informacji<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Zablokowano treści wprowadzające w błąd.</translation>
<translation id="5633259641094592098">Kultowe filmy i kino niezależne</translation>
@@ -1818,6 +1830,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="5989320800837274978">Nie określono ani stałych serwerów proxy, ani adresu URL skryptu PAC.</translation>
<translation id="5992691462791905444">Składanie typu Z</translation>
<translation id="5995727681868049093">Zarządzaj swoimi danymi, prywatnością i bezpieczeństwem na koncie Google</translation>
+<translation id="5997247540087773573">Znaleźliśmy użyte właśnie hasło wśród ujawnionych w wyniku naruszenia bezpieczeństwa danych. Menedżer haseł Google zaleca natychmiastową zmianę tego hasła oraz sprawdzenie innych zapisanych haseł – pozwoli to zabezpieczyć Twoje konta.</translation>
<translation id="6000758707621254961">Wyniki wyszukiwania dla zapytania „<ph name="SEARCH_TEXT" />â€: <ph name="RESULT_COUNT" /></translation>
<translation id="6006484371116297560">Klasyczny</translation>
<translation id="6008122969617370890">Kolejność od N do 1</translation>
@@ -1913,7 +1926,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="627746635834430766">Aby następnym razem zapłacić szybciej, zapisz kartę i adres rozliczeniowy na swoim koncie Google.</translation>
<translation id="6279183038361895380">Naciśnij |<ph name="ACCELERATOR" />|, by wyświetlić kursor</translation>
<translation id="6280223929691119688">Nie można dostarczyć pod ten adres. Wybierz inny.</translation>
-<translation id="6282194474023008486">Kod pocztowy</translation>
<translation id="6285507000506177184">Przycisk Zarządzaj pobranymi plikami w Chrome; aby zarządzać pobranymi plikami w Chrome, naciśnij Enter</translation>
<translation id="6289939620939689042">Kolor strony</translation>
<translation id="6290238015253830360">Tutaj wyświetlą się proponowane artykuły</translation>
@@ -1935,6 +1947,7 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="6337133576188860026">Zwolni się mniej niż <ph name="SIZE" />. Podczas następnej wizyty niektóre strony mogą ładować się wolniej.</translation>
<translation id="6337534724793800597">Filtruj zasady według nazwy</translation>
<translation id="6340739886198108203">Zgodnie z zasadą administratora nie zaleca się robienia zrzutów ekranu, gdy są widoczne treści poufne:</translation>
+<translation id="6348220984832452017">Aktywne warianty</translation>
<translation id="6349101878882523185">Zainstaluj aplikacjÄ™ <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Brak}=1{1 hasło (do <ph name="DOMAIN_LIST" />, zsynchronizowano)}=2{2 hasła (do <ph name="DOMAIN_LIST" />, zsynchronizowano)}few{# hasła (do <ph name="DOMAIN_LIST" />, zsynchronizowano)}many{# haseł (do <ph name="DOMAIN_LIST" />, zsynchronizowano)}other{# hasła (do <ph name="DOMAIN_LIST" />, zsynchronizowano)}}</translation>
<translation id="6355392890578844978">Ta przeglądarka nie jest zarządzana przez firmę ani inną organizację. Aktywność na tym urządzeniu może być zarządzana poza Chromium. <ph name="BEGIN_LINK" />Więcej informacji<ph name="END_LINK" /></translation>
@@ -1966,7 +1979,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="643051589346665201">Zmień hasło do Google</translation>
<translation id="6433490469411711332">Edytuj dane kontaktowe</translation>
<translation id="6433595998831338502">Serwer <ph name="HOST_NAME" /> odrzucił połączenie.</translation>
-<translation id="6438025220577812695">ZmieniÄ™ samodzielnie</translation>
<translation id="6440503408713884761">Ignorowany</translation>
<translation id="6443406338865242315">Zainstalowane przez Ciebie rozszerzenia i wtyczki</translation>
<translation id="6446163441502663861">Kahu (koperta)</translation>
@@ -2096,9 +2108,9 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="6828866289116430505">Genetyka</translation>
<translation id="6831043979455480757">TÅ‚umacz</translation>
<translation id="6833752742582340615">Aby szybciej dokonywać bezpiecznych płatności, zapisz dane karty i informacje rozliczeniowe w Asystencie Google</translation>
-<translation id="6839929833149231406">Obszar</translation>
<translation id="6846340164947227603">Użyj numeru karty wirtualnej...</translation>
<translation id="6852204201400771460">Załadować ponownie aplikację?</translation>
+<translation id="6857776781123259569">Zarządzaj hasłami…</translation>
<translation id="686485648936420384">Informacje dla konsumentów</translation>
<translation id="6865412394715372076">Nie można teraz zweryfikować karty</translation>
<translation id="6869334554832814367">Kredyty konsumpcyjne</translation>
@@ -2147,7 +2159,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="6965978654500191972">UrzÄ…dzenie</translation>
<translation id="696703987787944103">Percepcyjne</translation>
<translation id="6968269510885595029">Użyj swojego klucza bezpieczeństwa</translation>
-<translation id="6970216967273061347">Okręg</translation>
<translation id="6971439137020188025">Szybko utwórz nową prezentację w Prezentacjach Google</translation>
<translation id="6972629891077993081">UrzÄ…dzenia HID</translation>
<translation id="6973656660372572881">Określono zarówno stałe serwery proxy, jak i URL skryptu PAC.</translation>
@@ -2186,7 +2197,6 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<translation id="7081308185095828845">Ta funkcja jest niedostępna na Twoim urządzeniu</translation>
<translation id="7083258188081898530">Taca 9</translation>
<translation id="7086090958708083563">Użytkownik poprosił o przesłanie</translation>
-<translation id="7087282848513945231">Hrabstwo</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />; aby zarządzać uprawnieniami i danymi zapisanymi dla różnych witryn w ustawieniach Chrome, naciśnij Tab, a potem Enter</translation>
<translation id="7096937462164235847">Tożsamość tej strony nie została zweryfikowana.</translation>
<translation id="7101893872976785596">Horrory</translation>
@@ -2205,10 +2215,10 @@ Jeśli się nie zgodzisz, zostaną one zablokowane ze względu na Twoje ustawien
<ph name="LIST_ITEM" />informacje wpisane w formularzach.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Nie można wysłać pod ten adres. Wybierz inny.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Administrator nie zezwala na kopiowanie tych danych.</translation>
<translation id="7135130955892390533">Wyświetlanie informacji o obecności</translation>
<translation id="7138472120740807366">Metoda dostawy</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Taca główna</translation>
<translation id="714064300541049402">Strona 2 – przesunięcie obrazu wzdłuż osi X</translation>
<translation id="7152423860607593928">Number-14 (koperta)</translation>
@@ -2425,6 +2435,7 @@ Dodatkowe informacje:
<translation id="7669271284792375604">Osoby atakujące tę stronę mogą podstępem próbować nakłonić Cię do zainstalowania programów utrudniających przeglądanie internetu (np. zmieniających stronę główną lub wyświetlających dodatkowe reklamy na stronach, na które wchodzisz).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Wykonane działania dotyczące danych oznaczonych jako poufne (od momentu zalogowania zgłoszono 1 działanie). <ph name="BEGIN_LINK" />Więcej informacji<ph name="END_LINK" />}few{Wykonane działania dotyczące danych oznaczonych jako poufne (od momentu zalogowania zgłoszono # działania). <ph name="BEGIN_LINK" />Więcej informacji<ph name="END_LINK" />}many{Wykonane działania dotyczące danych oznaczonych jako poufne (od momentu zalogowania zgłoszono # działań). <ph name="BEGIN_LINK" />Więcej informacji<ph name="END_LINK" />}other{Wykonane działania dotyczące danych oznaczonych jako poufne (od momentu zalogowania zgłoszono # działania). <ph name="BEGIN_LINK" />Więcej informacji<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Zestaw tac odbiorczych 6</translation>
+<translation id="7675325315208090829">Zarządzaj formami płatności…</translation>
<translation id="7676643023259824263">Wyszukaj tekst ze schowka, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Wyświetl historię przeglądania i zarządzaj nią w ustawieniach Chrome</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2467,7 +2478,6 @@ Dodatkowe informacje:
<translation id="7766518757692125295">Fartuch</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Ta sama kolejność, strona do drukowania skierowana w górę</translation>
-<translation id="777702478322588152">Prefektura</translation>
<translation id="7791011319128895129">Nieopublikowana</translation>
<translation id="7791196057686275387">Owijanie</translation>
<translation id="7791543448312431591">Dodaj</translation>
@@ -2558,12 +2568,12 @@ Dodatkowe informacje:
<translation id="8055534648776115597">Edukacja zawodowa i podyplomowa</translation>
<translation id="8057711352706143257">Oprogramowanie „<ph name="SOFTWARE_NAME" />†nie jest prawidłowo skonfigurowane. Odinstalowanie oprogramowania „<ph name="SOFTWARE_NAME" />†zwykle rozwiązuje problem. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Produkcja żywności</translation>
-<translation id="8066955247577885446">Coś poszło nie tak.</translation>
<translation id="8067872629359326442">Przed chwilą wpisano hasło na stronie wprowadzającej w błąd. Chromium może pomóc. Aby zmienić hasło i powiadomić Google, że Twoje konto może być zagrożone, kliknij Chroń konto.</translation>
<translation id="8070439594494267500">Ikona aplikacji</translation>
<translation id="8074253406171541171">10x13 (koperta)</translation>
<translation id="8075736640322370409">Szybko utwórz nowy arkusz Google</translation>
<translation id="8075898834294118863">ZarzÄ…dzaj ustawieniami witryn</translation>
+<translation id="8076492880354921740">Karty</translation>
<translation id="8078141288243656252">Nie można dodawać adnotacji, gdy dokument jest obrócony</translation>
<translation id="8079031581361219619">Załadować ponownie stronę?</translation>
<translation id="8081087320434522107">Sedany</translation>
@@ -2686,6 +2696,7 @@ Dodatkowe informacje:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ustawienia</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />; aby zarządzać opcjami plików cookie w ustawieniach Chrome, naciśnij Tab, a potem Enter</translation>
<translation id="8433057134996913067">Zostaniesz wylogowany z większości stron internetowych.</translation>
<translation id="8434840396568290395">Zwierzęta domowe</translation>
@@ -2783,6 +2794,7 @@ Dodatkowe informacje:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> to Twój kod do <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Dodaj tę kartę do zakładek</translation>
<translation id="8751426954251315517">Spróbuj ponownie później</translation>
+<translation id="8757526089434340176">Dostępna oferta Google Pay</translation>
<translation id="8758885506338294482">Zawody w grach wideo</translation>
<translation id="8759274551635299824">Ta karta straciła ważność</translation>
<translation id="87601671197631245">Ta strona używa nieaktualnej konfiguracji zabezpieczeń, co oznacza, że Twoje dane (np. hasła, wiadomości i numery kart kredytowych) mogą być zagrożone podczas przesyłania na tę stronę.</translation>
@@ -2790,6 +2802,7 @@ Dodatkowe informacje:
<translation id="8763927697961133303">UrzÄ…dzenie USB</translation>
<translation id="8763986294015493060">Zamknij wszystkie otwarte okna incognito</translation>
<translation id="8766943070169463815">Arkusz uwierzytelniania danych logowania na potrzeby bezpiecznych płatności jest otwarty</translation>
+<translation id="8767765348545497220">Zamknij okno pomocy</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocykle</translation>
<translation id="8790007591277257123">&amp;Ponów usunięcie</translation>
@@ -2802,6 +2815,7 @@ Dodatkowe informacje:
<translation id="8806285662264631610">Produkty do kąpieli i pielęgnacji ciała</translation>
<translation id="8807160976559152894">Przycięcie po każdej stronie</translation>
<translation id="8808828119384186784">Ustawienia Chrome</translation>
+<translation id="8813277370772331957">Przypomnij później</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Naciśnij Tab, a potem Enter, by zaktualizować przeglądarkę Chrome z poziomu jej ustawień.</translation>
<translation id="8820817407110198400">Zakładki</translation>
<translation id="882338992931677877">Szczelina ręczna</translation>
@@ -2981,6 +2995,7 @@ Dodatkowe informacje:
<translation id="988159990683914416">Build</translation>
<translation id="989988560359834682">Edytuj adres</translation>
<translation id="991413375315957741">czujniki ruchu lub światła</translation>
+<translation id="992110854164447044">Karta wirtualna ukrywa Twoją prawdziwą kartę, aby chronić Cię przed oszustwami. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Różowy</translation>
<translation id="992432478773561401">Oprogramowanie „<ph name="SOFTWARE_NAME" />†nie zostało prawidłowo zainstalowane na komputerze lub w sieci:
diff --git a/chromium/components/strings/components_strings_pt-BR.xtb b/chromium/components/strings/components_strings_pt-BR.xtb
index 727ec9a6ff9..d0c01d7db04 100644
--- a/chromium/components/strings/components_strings_pt-BR.xtb
+++ b/chromium/components/strings/components_strings_pt-BR.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Bolsas de estudo, subsídios e ajuda financeira</translation>
<translation id="1048785276086539861">Quando você editar as anotações, este documento voltará à visualização de uma página</translation>
<translation id="1050038467049342496">Fechar outros apps</translation>
+<translation id="1053959602163383901">Você escolheu fazer a verificação com um dispositivo autenticador em sites que usam o provedor <ph name="PROVIDER_ORIGIN" />. Esse provedor pode ter armazenado informações sobre sua forma de pagamento, mas você pode <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Desfazer adicionar</translation>
<translation id="1056663316309890343">Software para fotografia</translation>
<translation id="1056898198331236512">Aviso</translation>
@@ -119,6 +120,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="1270502636509132238">Método de Retirada</translation>
<translation id="1281476433249504884">Empilhador 1</translation>
<translation id="1285320974508926690">Nunca traduzir este site</translation>
+<translation id="1288548991597756084">Salvar cartão de forma segura</translation>
<translation id="1292571435393770077">Bandeja 16</translation>
<translation id="1292701964462482250">"Algum software no seu computador está impedindo o Google Chrome de se conectar com segurança à Web" (somente computadores Windows)</translation>
<translation id="1294154142200295408">Variações de linha de comando</translation>
@@ -223,6 +225,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
&lt;p&gt;Para corrigir o erro, clique em &lt;strong&gt;Conectar&lt;/strong&gt; na página que você está tentando abrir.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Paisagismo</translation>
<translation id="1513706915089223971">Lista de entradas no histórico</translation>
+<translation id="1516097932025103760">Ele será criptografado e salvo de forma segura. O CVC nunca é armazenado.</translation>
<translation id="1517433312004943670">Número de telefone necessário</translation>
<translation id="1519264250979466059">Data da versão</translation>
<translation id="1521159554480556801">Artes têxteis e de fibra</translation>
@@ -288,6 +291,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="1662550410081243962">Salvar e preencher as formas de pagamento</translation>
<translation id="1663943134801823270">As informações dos cartões e os endereços foram acessadas pelo Chrome. É possível gerenciar essas opções em <ph name="BEGIN_LINK" />Configurações<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Páginas em <ph name="SOURCE_LANGUAGE" /> serão traduzidas para <ph name="TARGET_LANGUAGE" /> de agora em diante.</translation>
+<translation id="1673886523110456987">Finalize a compra com o <ph name="CARD_DETAIL" /> para aproveitar a oferta</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> para <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Borda menor primeiro</translation>
<translation id="168693727862418163">Houve uma falha na validação do valor da política em relação ao esquema, e ele será ignorado.</translation>
@@ -306,6 +310,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="1717218214683051432">Sensores de movimento</translation>
<translation id="1717494416764505390">Caixa de e-mails 3</translation>
<translation id="1718029547804390981">O documento é grande demais para receber anotações</translation>
+<translation id="1720941539803966190">Fechar tutorial</translation>
<translation id="1721424275792716183">* Campo obrigatório</translation>
<translation id="1727613060316725209">O certificado é válido</translation>
<translation id="1727741090716970331">Adicione um Número de Cartão Válido</translation>
@@ -418,8 +423,8 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="205212645995975601">Churrasco e grelhados</translation>
<translation id="2053111141626950936">Páginas em <ph name="LANGUAGE" /> não serão traduzidas.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Quando este controle estiver ligado e o status estiver ativo, o Chrome determinará com qual grupo grande de pessoas, ou "coorte", sua atividade de navegação se parece mais. Os anunciantes podem selecionar anúncios para o grupo e sua atividade de navegação será armazenada no dispositivo de forma particular. Seu grupo é atualizado todos os dias.}=1{Quando este controle estiver ligado e o status estiver ativo, o Chrome determinará com qual grupo grande de pessoas, ou "coorte", sua atividade de navegação se parece mais. Os anunciantes podem selecionar anúncios para o grupo e sua atividade de navegação será armazenada no dispositivo de forma particular. Seu grupo é atualizado todos os dias.}one{Quando este controle estiver ligado e o status estiver ativo, o Chrome determinará com qual grupo grande de pessoas, ou "coorte", sua atividade de navegação se parece mais. Os anunciantes podem selecionar anúncios para o grupo e sua atividade de navegação será armazenada no dispositivo de forma particular. Seu grupo é atualizado a cada {NUM_DAYS} dia.}other{Quando este controle estiver ligado e o status estiver ativo, o Chrome determinará com qual grupo grande de pessoas, ou "coorte", sua atividade de navegação se parece mais. Os anunciantes podem selecionar anúncios para o grupo e sua atividade de navegação será armazenada no dispositivo de forma particular. Seu grupo é atualizado a cada {NUM_DAYS} dias.}}</translation>
-<translation id="2053553514270667976">CEP</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 sugestão}one{# sugestão}other{# sugestões}}</translation>
+<translation id="2066915425250589881">pedir para que elas sejam excluídas</translation>
<translation id="2068528718802935086">Bebês e crianças</translation>
<translation id="2071156619270205202">Esse não é um número de cartão virtual.</translation>
<translation id="2071692954027939183">As notificações foram bloqueadas automaticamente porque você não costuma permiti-las</translation>
@@ -431,7 +436,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="2088086323192747268">Botão "Gerenciar sincronização". Pressione "Enter" para gerenciar quais informações são sincronizadas nas configurações do Chrome</translation>
<translation id="2091887806945687916">Som</translation>
<translation id="2094505752054353250">Incompatibilidade de domínio</translation>
-<translation id="2096368010154057602">Departamento</translation>
<translation id="2099652385553570808">Três grampos no lado esquerdo</translation>
<translation id="2101225219012730419">Versão:</translation>
<translation id="2102134110707549001">Sugerir senha forte…</translation>
@@ -468,7 +472,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="2185836064961771414">Futebol americano</translation>
<translation id="2187317261103489799">Detectar (padrão)</translation>
<translation id="2188375229972301266">Perfuração múltipla na parte inferior</translation>
-<translation id="2188852899391513400">A senha que você usou foi encontrada em uma violação de dados. Para proteger suas contas, o Gerenciador de senhas do Google recomenda que ela seja mudada e que você revise suas senhas salvas.</translation>
<translation id="219906046732893612">Reforma de casas</translation>
<translation id="2202020181578195191">Informe um ano de validade válido</translation>
<translation id="22081806969704220">Bandeja 3</translation>
@@ -479,6 +482,8 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="2215727959747642672">Edição de arquivo</translation>
<translation id="2215963164070968490">Cães</translation>
<translation id="2218879909401188352">Os invasores que estão em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> no momento podem instalar apps perigosos que danificam seu dispositivo, acrescentam cobranças ocultas junto à operadora ou roubam suas informações pessoais. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Reiniciar tutorial</translation>
+<translation id="2219735899272417925">É necessário redefinir o dispositivo</translation>
<translation id="2224337661447660594">Sem Internet</translation>
<translation id="2230458221926704099">Corrija sua conexão usando o <ph name="BEGIN_LINK" />app de diagnóstico<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Enviar agora</translation>
@@ -576,11 +581,13 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="2512101340618156538">Sem permissão (padrão)</translation>
<translation id="2512413427717747692">Botão Definir o Chrome como navegador padrão: pressione Enter para definir o Chrome como o navegador padrão do sistema nas configurações do iOS</translation>
<translation id="2515629240566999685">Verificar o sinal na sua área</translation>
+<translation id="2515761554693942801">Você escolheu fazer a verificação com o Touch ID em sites que usam o provedor <ph name="PROVIDER_ORIGIN" />. Esse provedor pode ter armazenado informações sobre sua forma de pagamento, mas você pode <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Grampo na parte inferior direita</translation>
<translation id="2521736961081452453">Criar formulário</translation>
<translation id="2523886232349826891">Salvo apenas neste dispositivo</translation>
<translation id="2524461107774643265">Adicione Mais Informações</translation>
<translation id="2529899080962247600">Esse campo não pode ter mais que <ph name="MAX_ITEMS_LIMIT" /> entradas. Todas as entradas futuras serão ignoradas.</translation>
+<translation id="253493526287553278">Consulte os detalhes do código promocional</translation>
<translation id="2535585790302968248">Abra uma nova guia anônima para navegar com privacidade</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{e mais 1}one{e mais #}other{e mais #}}</translation>
<translation id="2536110899380797252">Adicionar Endereço</translation>
@@ -616,7 +623,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="259821504105826686">Artes digitais e fotográficas</translation>
<translation id="2601150049980261779">Filmes românticos</translation>
<translation id="2604589665489080024">Música pop</translation>
-<translation id="2609632851001447353">Variações</translation>
<translation id="2610561535971892504">Clique para copiar</translation>
<translation id="2617988307566202237">O Chrome <ph name="BEGIN_EMPHASIS" />não vai salvar<ph name="END_EMPHASIS" /> estas informações:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="2676271551327853224">8K 273 mm x 394 mm</translation>
<translation id="2677696497921480781">Aniversários e datas comemorativas</translation>
<translation id="2677748264148917807">Sair</translation>
+<translation id="2679714844901977852">Salve seu cartão e as informações de faturamento na Conta do Google <ph name="USER_EMAIL" /> para fazer compras mais rápidas e seguras</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Melhor ajuste</translation>
<translation id="2688969097326701645">Sim, continuar</translation>
@@ -697,7 +704,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="2854764410992194509">Provedores de acesso à Internet (ISPs)</translation>
<translation id="2856444702002559011">Invasores podem estar tentando roubar suas informações de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (por exemplo, senhas, mensagens ou cartões de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Neste site, há exibição de anúncios invasivos ou enganosos</translation>
-<translation id="286512204874376891">O cartão virtual mascara o real para proteger você contra possíveis tentativas de fraude. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Amigável</translation>
<translation id="28761159517501904">Filmes</translation>
<translation id="2876489322757410363">Saindo do modo de navegação anônima para pagar usando um aplicativo externo. Continuar?</translation>
@@ -798,7 +804,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="3158539265159265653">Disco</translation>
<translation id="3162559335345991374">O Wi-Fi que você está usando pode exigir a visita a uma página de login.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ilha</translation>
<translation id="3176929007561373547">Verifique suas configurações de proxy ou entre em contato com o administrador de rede para
verificar se o servidor proxy está funcionando. Se você acredita que não deve
usar um servidor proxy:
@@ -877,9 +882,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="3369192424181595722">Erro do relógio</translation>
<translation id="3369459162151165748">Peças e acessórios de veículos</translation>
<translation id="3371064404604898522">Definir o Chrome como navegador padrão</translation>
-<translation id="3371076217486966826"><ph name="URL" /> quer:
- • criar um mapa 3D do ambiente a sua volta e acompanhar a posição da câmera;
- • usar a câmera.</translation>
<translation id="337363190475750230">Desprovisionado</translation>
<translation id="3375754925484257129">Executar a confirmação de segurança do Chrome</translation>
<translation id="3377144306166885718">Versão obsoleta do TLS usada no servidor.</translation>
@@ -896,6 +898,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="3399952811970034796">Endereço de Entrega</translation>
<translation id="3402261774528610252">A conexão para carregar este site usou o TLS 1.0 ou o TLS 1.1, que estão obsoletos e serão desativados em breve. Depois da desativação, os usuários não poderão carregar este site. É necessário ativar o TLS 1.2 ou mais recente no servidor.</translation>
<translation id="3405664148539009465">Personalizar fontes</translation>
+<translation id="3407789382767355356">login de terceiros</translation>
<translation id="3409896703495473338">Gerenciar configurações de segurança</translation>
<translation id="3414952576877147120">Tamanho:</translation>
<translation id="3417660076059365994">Os arquivos transferidos por upload ou anexados são enviados ao Google Cloud ou a terceiros para análise. Por exemplo, eles podem ser verificados em busca de dados confidenciais ou malware.</translation>
@@ -928,6 +931,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="3477679029130949506">Listas de filmes e horários de apresentações de teatro</translation>
<translation id="3479552764303398839">Não agora</translation>
<translation id="3484560055331845446">Você pode perder o acesso à sua Conta do Google. O Chrome recomenda que sua senha seja alterada agora. Solicitaremos que você faça login.</translation>
+<translation id="3484861421501147767">Lembrete: o código promocional salvo está disponível</translation>
<translation id="3487845404393360112">Bandeja 4</translation>
<translation id="3495081129428749620">Encontrar na página
<ph name="PAGE_TITLE" /></translation>
@@ -1052,6 +1056,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="3810973564298564668">Gerenciar</translation>
<translation id="3816482573645936981">Valor (substituído)</translation>
<translation id="382518646247711829">Se você usa um servidor proxy...</translation>
+<translation id="3826050100957962900">Login de terceiros</translation>
<translation id="3827112369919217609">Absoluto</translation>
<translation id="3827666161959873541">Filmes para a família</translation>
<translation id="3828924085048779000">Uma senha vazia não é permitida.</translation>
@@ -1064,9 +1069,9 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="3858027520442213535">Atualizar data e hora</translation>
<translation id="3858860766373142691">Nome</translation>
<translation id="3872834068356954457">Ciência</translation>
+<translation id="3875783148670536197">Me ajude a fazer isso</translation>
<translation id="3881478300875776315">Mostrar menos linhas</translation>
<translation id="3884278016824448484">Identificador de dispositivo em conflito</translation>
-<translation id="3885155851504623709">Município</translation>
<translation id="388632593194507180">Monitoramento detectado</translation>
<translation id="3886948180919384617">Empilhador 3</translation>
<translation id="3890664840433101773">Adicionar e-mail</translation>
@@ -1096,9 +1101,11 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="3973234410852337861"><ph name="HOST_NAME" /> está bloqueado</translation>
<translation id="3978338123949022456">Modo de pesquisa: digite uma consulta e pressione Enter para pesquisar com <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Pássaros</translation>
+<translation id="3985750352229496475">Gerenciar endereços…</translation>
<translation id="3986705137476756801">Desativar "Legenda instantânea" por enquanto</translation>
<translation id="3987940399970879459">Menos de 1 MB</translation>
<translation id="3990250421422698716">Ajustar desvio</translation>
+<translation id="3992684624889376114">Sobre esta página</translation>
<translation id="3996311196211510766">O site <ph name="ORIGIN" /> solicitou que uma política de origem seja aplicada a todas as solicitações feitas a ele, mas não é possível aplicá-la no momento.</translation>
<translation id="4006465311664329701">Formas de pagamento, ofertas e endereços que usam o Google Pay</translation>
<translation id="4009243425692662128">O conteúdo das páginas que você imprime é enviado ao Google Cloud ou a terceiros para análise. Por exemplo, ele pode ser verificado em busca de dados sensíveis.</translation>
@@ -1218,6 +1225,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="4305666528087210886">Não foi possível acessar seu arquivo</translation>
<translation id="4306529830550717874">Salvar endereço?</translation>
<translation id="4306812610847412719">área de transferência</translation>
+<translation id="4310070645992025887">Pesquisar suas jornadas</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloquear (padrão)</translation>
<translation id="4314815835985389558">Gerenciar sincronização</translation>
@@ -1268,6 +1276,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="4435702339979719576">Postal</translation>
<translation id="443673843213245140">O uso de um proxy está desativado, mas uma configuração explícita de proxy é especificada.</translation>
<translation id="4441832193888514600">Ignorada porque a política só pode ser definida como uma política do usuário em nuvem.</translation>
+<translation id="4442470707340296952">Guias do Chrome</translation>
<translation id="4450893287417543264">Não mostrar novamente</translation>
<translation id="4451135742916150903">Pode pedir para se conectar a dispositivos HID</translation>
<translation id="4452328064229197696">A senha que você usou foi encontrada em uma violação de dados. Para proteger suas contas, o Gerenciador de senhas do Google recomenda que você revise suas senhas salvas.</translation>
@@ -1406,6 +1415,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="483241715238664915">Ativar avisos</translation>
<translation id="4834250788637067901">Formas de pagamento, ofertas e endereços que usam o Google Pay</translation>
<translation id="4838327282952368871">Sonho</translation>
+<translation id="4839087176073128681">Pague de forma mais rápida da próxima vez e proteja seu cartão com a segurança de ponta do Google.</translation>
<translation id="4840250757394056958">Ver seu histórico do Chrome</translation>
<translation id="484462545196658690">Automática</translation>
<translation id="484671803914931257">Receba descontos para <ph name="MERCHANT_NAME" /> e mais</translation>
@@ -1468,6 +1478,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="5011561501798487822">Idioma detectado</translation>
<translation id="5015510746216210676">Nome do computador:</translation>
<translation id="5017554619425969104">Texto que você copiou</translation>
+<translation id="5017828934289857214">Lembrar mais tarde</translation>
<translation id="5018422839182700155">Não é possível abrir essa página</translation>
<translation id="5019198164206649151">Armazenamento de backup em estado inválido</translation>
<translation id="5020776957610079374">Música mundial</translation>
@@ -1487,6 +1498,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="5051305769747448211">Shows de comédia ao vivo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Para enviar este arquivo usando o Compartilhar por proximidade, libere espaço (<ph name="DISK_SPACE_SIZE" />) no seu dispositivo}one{Para enviar este arquivo usando o Compartilhar por proximidade, libere espaço (<ph name="DISK_SPACE_SIZE" />) no seu dispositivo}other{Para enviar estes arquivos usando o Compartilhar por proximidade, libere espaço (<ph name="DISK_SPACE_SIZE" />) no seu dispositivo}}</translation>
<translation id="5056549851600133418">Artigos para você</translation>
+<translation id="5060483733937416656">Você escolheu fazer a verificação com o Windows Hello em sites que usam o provedor <ph name="PROVIDER_ORIGIN" />. Esse provedor pode ter armazenado informações sobre sua forma de pagamento, mas você pode <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Você quis dizer "<ph name="LOOKALIKE_DOMAIN" />"?</translation>
<translation id="5066056036849835175">Histórico de impressão</translation>
<translation id="5068234115460527047">Fundos de cobertura</translation>
@@ -1500,10 +1512,8 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="5087286274860437796">O certificado do servidor não é válido no momento.</translation>
<translation id="5087580092889165836">Adicionar cartão</translation>
<translation id="5088142053160410913">Mensagem para o operador</translation>
-<translation id="5089810972385038852">Estado</translation>
<translation id="5093232627742069661">Dobra sanfona</translation>
<translation id="5094747076828555589">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança não é confiável para o Chromium. Isso pode ser causado por uma configuração incorreta ou pela interceptação da sua conexão por um invasor.</translation>
-<translation id="5095208057601539847">Província</translation>
<translation id="5097099694988056070">Estatísticas do dispositivo, como uso de CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">O site não é seguro</translation>
@@ -1541,6 +1551,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="5171045022955879922">Pesquisar ou digitar URL</translation>
<translation id="5171689220826475070">Fanfold European</translation>
<translation id="5172758083709347301">Máquina</translation>
+<translation id="5177076414499237632">Saiba mais sobre a origem e o assunto desta página</translation>
<translation id="5179510805599951267">Não está em <ph name="ORIGINAL_LANGUAGE" />? Informe este erro</translation>
<translation id="518639307526414276">Produtos para alimentação e bem-estar de animais de estimação</translation>
<translation id="5190835502935405962">Barra de favoritos</translation>
@@ -1701,6 +1712,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="5624120631404540903">Gerenciar senhas</translation>
<translation id="5629630648637658800">Falha ao carregar as configurações da política</translation>
<translation id="5631439013527180824">Token de gerenciamento de dispositivo inválido</translation>
+<translation id="5632485077360054581">Me ajude a fazer isso</translation>
<translation id="5633066919399395251">Invasores que estão em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> no momento podem tentar instalar programas perigosos no seu computador e roubar ou excluir suas informações (por exemplo, fotos, senhas, mensagens e cartões de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Conteúdo enganoso bloqueado.</translation>
<translation id="5633259641094592098">Filmes cult e indie</translation>
@@ -1818,6 +1830,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="5989320800837274978">Nem os servidores proxy fixos nem o URL de script .pac foram especificados.</translation>
<translation id="5992691462791905444">Dobra sanfona</translation>
<translation id="5995727681868049093">Gerenciar suas informações, sua privacidade e sua segurança na Conta do Google.</translation>
+<translation id="5997247540087773573">A senha que você usou foi encontrada em uma violação de dados. Para proteger suas contas, o Gerenciador de senhas do Google recomenda que você a mude e revise as senhas salvas.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultados para "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Clássico</translation>
<translation id="6008122969617370890">Ordem N a 1</translation>
@@ -1913,7 +1926,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="627746635834430766">Para agilizar o pagamento na próxima vez, salve o cartão e o endereço de faturamento na sua Conta do Google.</translation>
<translation id="6279183038361895380">Pressione |<ph name="ACCELERATOR" />| para exibir seu cursor</translation>
<translation id="6280223929691119688">Não é possível entregar nesse endereço. Selecione um endereço diferente.</translation>
-<translation id="6282194474023008486">CEP</translation>
<translation id="6285507000506177184">Botão "Gerenciar downloads no Chrome". Pressione Enter para gerenciar os arquivos transferidos por download no Chrome</translation>
<translation id="6289939620939689042">Cor da página</translation>
<translation id="6290238015253830360">Os artigos sugeridos aparecerão aqui</translation>
@@ -1935,6 +1947,7 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="6337133576188860026">Libera menos de <ph name="SIZE" />. O carregamento de alguns sites pode ficar mais lento no seu próximo acesso.</translation>
<translation id="6337534724793800597">Filtrar políticas por nome</translation>
<translation id="6340739886198108203">A política do administrador não recomenda fazer capturas de tela ou gravações quando há conteúdo confidencial visível:</translation>
+<translation id="6348220984832452017">Variações ativas</translation>
<translation id="6349101878882523185">Instalar <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Nenhuma}=1{1 senha (para <ph name="DOMAIN_LIST" />, sincronizada)}=2{2 senhas (para <ph name="DOMAIN_LIST" />, sincronizadas)}one{# senha (para <ph name="DOMAIN_LIST" />, sincronizada)}other{# senhas (para <ph name="DOMAIN_LIST" />, sincronizadas)}}</translation>
<translation id="6355392890578844978">Este navegador não é gerenciado por uma empresa ou outra organização. A atividade deste dispositivo pode ser gerenciada fora do Chromium. <ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" /></translation>
@@ -1966,7 +1979,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="643051589346665201">Mudar senha do Google</translation>
<translation id="6433490469411711332">Editar informações de contato</translation>
<translation id="6433595998831338502">A conexão com <ph name="HOST_NAME" /> foi recusada.</translation>
-<translation id="6438025220577812695">Mudar manualmente</translation>
<translation id="6440503408713884761">Ignorada</translation>
<translation id="6443406338865242315">quais extensões e plug-ins você instalou;</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2096,9 +2108,9 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="6828866289116430505">Genética</translation>
<translation id="6831043979455480757">Traduzir</translation>
<translation id="6833752742582340615">Salve seu cartão e as informações de cobrança na Conta do Google para fazer compras mais rápidas e seguras</translation>
-<translation id="6839929833149231406">Ãrea</translation>
<translation id="6846340164947227603">Usar um número de cartão virtual…</translation>
<translation id="6852204201400771460">Recarregar app?</translation>
+<translation id="6857776781123259569">Gerenciar senhas…</translation>
<translation id="686485648936420384">Recursos para o consumidor</translation>
<translation id="6865412394715372076">Não é possível verificar este cartão no momento</translation>
<translation id="6869334554832814367">Empréstimos pessoais</translation>
@@ -2147,7 +2159,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="6965978654500191972">Dispositivo</translation>
<translation id="696703987787944103">Perceptivo</translation>
<translation id="6968269510885595029">Usar sua chave de segurança</translation>
-<translation id="6970216967273061347">Distrito</translation>
<translation id="6971439137020188025">Criar rapidamente um novo arquivo no Apresentações Google</translation>
<translation id="6972629891077993081">Dispositivos HID</translation>
<translation id="6973656660372572881">Ambos os servidores proxy fixo e um URL de script .pac foram especificados.</translation>
@@ -2186,7 +2197,6 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<translation id="7081308185095828845">Esse recurso não está disponível no seu dispositivo</translation>
<translation id="7083258188081898530">Bandeja 9</translation>
<translation id="7086090958708083563">Upload solicitado pelo usuário</translation>
-<translation id="7087282848513945231">Condado</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. Pressione "Tab" e "Enter" para gerenciar as permissões e os dados armazenados nos sites nas configurações do Chrome</translation>
<translation id="7096937462164235847">A identidade deste site não foi verificada.</translation>
<translation id="7101893872976785596">Filmes de terror</translation>
@@ -2205,10 +2215,10 @@ Se não fizer isso, a permissão será bloqueada pelas configurações de privac
<ph name="LIST_ITEM" /><ph name="END_LIST_ITEM" />Informações inseridas em formulários
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Não é possível enviar para esse endereço. Selecione um endereço diferente.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">O administrador bloqueou a cópia destes dados.</translation>
<translation id="7135130955892390533">Mostrar status</translation>
<translation id="7138472120740807366">Método de entrega</translation>
-<translation id="7139724024395191329">Emirado</translation>
<translation id="7139892792842608322">Bandeja principal</translation>
<translation id="714064300541049402">Mudança X no lado 2 da imagem</translation>
<translation id="7152423860607593928">Nº 14 (Envelope)</translation>
@@ -2425,6 +2435,7 @@ Mais detalhes:
<translation id="7669271284792375604">Invasores nesse site podem tentar enganar você para que instale programas que prejudicam sua experiência de navegação (por exemplo, alterando sua página inicial ou mostrando mais anúncios nos sites que você visita).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Ações realizadas em dados marcados como confidenciais (1 ação desde o login). <ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" />}one{Ações realizadas em dados marcados como confidenciais (# ação desde o login). <ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" />}other{Ações realizadas em dados marcados como confidenciais (# ações desde o login). <ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Caixa de e-mails 6</translation>
+<translation id="7675325315208090829">Gerenciar formas de pagamento…</translation>
<translation id="7676643023259824263">Pesquisar um texto na área de transferência, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Ver e gerenciar seu histórico de navegação nas configurações do Chrome</translation>
<translation id="7679947978757153706">Beisebol</translation>
@@ -2467,7 +2478,6 @@ Mais detalhes:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Envelope DL</translation>
<translation id="7773005668374414287">Mesma ordem virada para cima</translation>
-<translation id="777702478322588152">Município</translation>
<translation id="7791011319128895129">Não lançada</translation>
<translation id="7791196057686275387">Agrupar</translation>
<translation id="7791543448312431591">Adicionar</translation>
@@ -2558,12 +2568,12 @@ Mais detalhes:
<translation id="8055534648776115597">Ensino técnico e complementar</translation>
<translation id="8057711352706143257">O software "<ph name="SOFTWARE_NAME" />" não foi configurado corretamente. A desinstalação do "<ph name="SOFTWARE_NAME" />" costuma resolver o problema. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Produção de alimentos</translation>
-<translation id="8066955247577885446">Algo deu errado.</translation>
<translation id="8067872629359326442">Você acabou de digitar sua senha em um site suspeito. O Chromium pode ajudar. Para mudar a senha e notificar o Google de que sua conta pode estar em risco, clique em "Proteger conta".</translation>
<translation id="8070439594494267500">Ãcone do app</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Criar rapidamente um novo arquivo do Planilhas Google</translation>
<translation id="8075898834294118863">Gerenciar configurações do site</translation>
+<translation id="8076492880354921740">Guias</translation>
<translation id="8078141288243656252">Não é possível fazer anotações em documentos girados</translation>
<translation id="8079031581361219619">Atualizar o site?</translation>
<translation id="8081087320434522107">Sedãs</translation>
@@ -2686,6 +2696,7 @@ Mais detalhes:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Configurações</translation>
+<translation id="8428634594422941299">Entendi</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. Pressione "Tab" e "Enter" para gerenciar suas preferências de cookies nas configurações do Chrome</translation>
<translation id="8433057134996913067">Essa opção desconecta você da maioria dos websites.</translation>
<translation id="8434840396568290395">Animais de estimação</translation>
@@ -2784,6 +2795,7 @@ Mais detalhes:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> é seu código para <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Adicionar esta guia aos favoritos</translation>
<translation id="8751426954251315517">Tente novamente mais tarde</translation>
+<translation id="8757526089434340176">Oferta do Google Pay disponível</translation>
<translation id="8758885506338294482">Competições de videogames</translation>
<translation id="8759274551635299824">Este cartão expirou</translation>
<translation id="87601671197631245">Este site usa uma configuração de segurança desatualizada que pode expor suas informações (como senhas, mensagens ou números de cartão de crédito) quando elas forem enviadas para o site.</translation>
@@ -2791,6 +2803,7 @@ Mais detalhes:
<translation id="8763927697961133303">Dispositivo USB</translation>
<translation id="8763986294015493060">Fechar todas as janelas anônimas abertas</translation>
<translation id="8766943070169463815">A página de autenticação de credenciais para pagamento seguro está aberta</translation>
+<translation id="8767765348545497220">Fechar balão de ajuda</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motos</translation>
<translation id="8790007591277257123">&amp;Refazer excluir</translation>
@@ -2803,6 +2816,7 @@ Mais detalhes:
<translation id="8806285662264631610">Produtos para banho e corpo</translation>
<translation id="8807160976559152894">Cortar depois de cada página</translation>
<translation id="8808828119384186784">Configurações do Chrome</translation>
+<translation id="8813277370772331957">Lembrar mais tarde</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Pressione "Tab" e depois "Enter" para atualizar o Chrome pelas configurações dele</translation>
<translation id="8820817407110198400">Favoritos</translation>
<translation id="882338992931677877">Entrada manual</translation>
@@ -2983,6 +2997,7 @@ incomuns e incorretas. Isso pode acontecer quando um invasor está fingindo ser
<translation id="988159990683914416">Versão do desenvolvedor</translation>
<translation id="989988560359834682">Editar endereço</translation>
<translation id="991413375315957741">sensores de luz ou movimento</translation>
+<translation id="992110854164447044">O cartão virtual oculta o físico para proteger você contra possíveis tentativas de fraude. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401">O software "<ph name="SOFTWARE_NAME" />" não foi instalado corretamente no computador ou na rede:
diff --git a/chromium/components/strings/components_strings_pt-PT.xtb b/chromium/components/strings/components_strings_pt-PT.xtb
index 9aeb71e3cec..37c9489b37e 100644
--- a/chromium/components/strings/components_strings_pt-PT.xtb
+++ b/chromium/components/strings/components_strings_pt-PT.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Subsídios, bolsas e assistência financeira</translation>
<translation id="1048785276086539861">Quando edita anotações, este documento volta à vista de página única.</translation>
<translation id="1050038467049342496">Fechar outras aplicações</translation>
+<translation id="1053959602163383901">Optou por validar com um dispositivo autenticador nos Websites que usam o fornecedor <ph name="PROVIDER_ORIGIN" />. Este fornecedor pode ter armazenado informações sobre o seu método de pagamento, que pode <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Anular adição</translation>
<translation id="1056663316309890343">Software de foto</translation>
<translation id="1056898198331236512">Aviso</translation>
@@ -119,6 +120,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="1270502636509132238">Método de recolha</translation>
<translation id="1281476433249504884">Empilhador 1</translation>
<translation id="1285320974508926690">Nunca traduzir este site</translation>
+<translation id="1288548991597756084">Guarde o cartão de forma segura</translation>
<translation id="1292571435393770077">Tabuleiro 16</translation>
<translation id="1292701964462482250">"Existe software no computador que está a impedir que o Chrome se ligue à Web em segurança" (apenas para computadores com Windows)</translation>
<translation id="1294154142200295408">Variações da linha de comandos</translation>
@@ -223,6 +225,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
&lt;p&gt;Para corrigir o erro, clique em &lt;strong&gt;Ligar&lt;/strong&gt; na página que está a tentar abrir.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Design paisagístico</translation>
<translation id="1513706915089223971">Lista de entradas no histórico</translation>
+<translation id="1516097932025103760">Vai ser encriptado, guardado de forma segura e o CVC (Código de Segurança) nunca é armazenado.</translation>
<translation id="1517433312004943670">Número de telefone obrigatório</translation>
<translation id="1519264250979466059">Data da Compilação</translation>
<translation id="1521159554480556801">Fibra e tapeçaria</translation>
@@ -272,7 +275,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="1634828734222219955">Total</translation>
<translation id="163669211644121865">Preparação e planeamento de impostos</translation>
<translation id="1638780421120290329">Não é possível guardar o cartão</translation>
-<translation id="1639239467298939599">A carregar</translation>
+<translation id="1639239467298939599">A carregar…</translation>
<translation id="1640180200866533862">Políticas do utilizador</translation>
<translation id="1640244768702815859">Experimente <ph name="BEGIN_LINK" />aceder à página inicial do site<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">Atrasar saída até</translation>
@@ -288,6 +291,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="1662550410081243962">Guardar e preencher métodos de pagamento</translation>
<translation id="1663943134801823270">Os cartões e os endereços são provenientes do Chrome. Pode geri-los nas <ph name="BEGIN_LINK" />Definições<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">As páginas em <ph name="SOURCE_LANGUAGE" /> serão traduzidas para <ph name="TARGET_LANGUAGE" /> a partir de agora.</translation>
+<translation id="1673886523110456987">Pague com <ph name="CARD_DETAIL" /> para usar a oferta</translation>
<translation id="1674504678466460478">De <ph name="SOURCE_LANGUAGE" /> para <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Margem estreita primeiro</translation>
<translation id="168693727862418163">Ocorreu um erro na validação desta política em relação ao respetivo esquema e a mesma será ignorada.</translation>
@@ -306,6 +310,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="1717218214683051432">Sensores de movimento</translation>
<translation id="1717494416764505390">Caixa de correio 3</translation>
<translation id="1718029547804390981">O documento é demasiado grande para ser anotado.</translation>
+<translation id="1720941539803966190">Fechar tutorial</translation>
<translation id="1721424275792716183">* Campo de preenchimento obrigatório</translation>
<translation id="1727613060316725209">O certificado é válido</translation>
<translation id="1727741090716970331">Adicionar número de cartão válido</translation>
@@ -422,8 +427,8 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="205212645995975601">Churrascos e grelhados</translation>
<translation id="2053111141626950936">As páginas em <ph name="LANGUAGE" /> não serão traduzidas.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Quando este controlo está ativado e o estado está ativo, o Chrome determina a que grupo alargado de pessoas, ou "coorte", a sua atividade de navegação recente é mais semelhante. Os anunciantes podem selecionar anúncios para o grupo e a sua atividade de navegação permanece privada no seu dispositivo. O seu grupo é atualizado todos os dias.}=1{Quando este controlo está ativado e o estado está ativo, o Chrome determina a que grupo alargado de pessoas, ou "coorte", a sua atividade de navegação recente é mais semelhante. Os anunciantes podem selecionar anúncios para o grupo e a sua atividade de navegação permanece privada no seu dispositivo. O seu grupo é atualizado todos os dias.}other{Quando este controlo está ativado e o estado está ativo, o Chrome determina a que grupo alargado de pessoas, ou "coorte", a sua atividade de navegação recente é mais semelhante. Os anunciantes podem selecionar anúncios para o grupo e a sua atividade de navegação permanece privada no seu dispositivo. O seu grupo é atualizado a cada {NUM_DAYS} dias.}}</translation>
-<translation id="2053553514270667976">Código postal</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 sugestão}other{# sugestões}}</translation>
+<translation id="2066915425250589881">pedir para eliminar</translation>
<translation id="2068528718802935086">Bebés e crianças pequenas</translation>
<translation id="2071156619270205202">Este cartão não é elegível para um número de cartão virtual.</translation>
<translation id="2071692954027939183">As notificações foram automaticamente bloqueadas porque, geralmente, não as permite.</translation>
@@ -435,7 +440,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="2088086323192747268">Botão Gerir sincronização. Prima Enter para gerir as informações que sincroniza nas Definições do Chrome.</translation>
<translation id="2091887806945687916">Som</translation>
<translation id="2094505752054353250">Falta de correspondência de domínio</translation>
-<translation id="2096368010154057602">Departamento</translation>
<translation id="2099652385553570808">Agrafo triplo à esquerda</translation>
<translation id="2101225219012730419">Versão:</translation>
<translation id="2102134110707549001">Sugerir palavra-passe forte…</translation>
@@ -472,7 +476,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="2185836064961771414">Futebol americano</translation>
<translation id="2187317261103489799">Detetar (predefinição)</translation>
<translation id="2188375229972301266">Perfuração múltipla na parte inferior</translation>
-<translation id="2188852899391513400">A palavra-passe que acabou de utilizar foi encontrada numa violação de dados. Para proteger as suas contas, o Gestor de palavras-passe da Google recomenda que a altere agora e, em seguida, verifique as suas palavras-passe guardadas.</translation>
<translation id="219906046732893612">Bricolage</translation>
<translation id="2202020181578195191">Introduza um ano de expiração válido</translation>
<translation id="22081806969704220">Tabuleiro 3</translation>
@@ -483,6 +486,8 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="2215727959747642672">Edição de ficheiros</translation>
<translation id="2215963164070968490">Cães</translation>
<translation id="2218879909401188352">Os utilizadores mal-intencionados que se encontram em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem instalar aplicações perigosas que danificam o dispositivo, adicionam cobranças ocultas à fatura de dados móveis ou roubam as suas informações pessoais. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Reiniciar tutorial</translation>
+<translation id="2219735899272417925">É necessário repor o dispositivo</translation>
<translation id="2224337661447660594">Sem Internet</translation>
<translation id="2230458221926704099">Utilize a <ph name="BEGIN_LINK" />aplicação de diagnóstico<ph name="END_LINK" /> para corrigir a ligação</translation>
<translation id="2239100178324503013">Enviar agora</translation>
@@ -580,11 +585,13 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="2512101340618156538">Não permitidos (predefinição)</translation>
<translation id="2512413427717747692">Botão para definir o Chrome como o navegador predefinido, prima Enter para definir o Chrome como o navegador predefinido do sistema nas definições do iOS</translation>
<translation id="2515629240566999685">Verificar o sinal na área</translation>
+<translation id="2515761554693942801">Optou por validar com o Touch ID em Websites que usam o fornecedor <ph name="PROVIDER_ORIGIN" />. Este fornecedor pode ter armazenado informações sobre o seu método de pagamento, que pode <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Agrafar parte inferior direita</translation>
<translation id="2521736961081452453">Criar formulário</translation>
<translation id="2523886232349826891">Guardado apenas neste dispositivo.</translation>
<translation id="2524461107774643265">Adicionar mais informações</translation>
<translation id="2529899080962247600">Este campo não deve ter mais de <ph name="MAX_ITEMS_LIMIT" /> entradas. Todas as restantes entradas serão ignoradas.</translation>
+<translation id="253493526287553278">Ver os detalhes do código promocional</translation>
<translation id="2535585790302968248">Abra um novo separador de navegação anónima para navegar em privado</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{e mais 1}other{e mais #}}</translation>
<translation id="2536110899380797252">Adicionar endereço</translation>
@@ -620,7 +627,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="259821504105826686">Arte fotográfica e digital</translation>
<translation id="2601150049980261779">Filmes de romance</translation>
<translation id="2604589665489080024">Música pop</translation>
-<translation id="2609632851001447353">Variações</translation>
<translation id="2610561535971892504">Clique para copiar</translation>
<translation id="2617988307566202237">O Chrome <ph name="BEGIN_EMPHASIS" />não guardará<ph name="END_EMPHASIS" /> as seguintes informações:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Aniversários e dias onomásticos</translation>
<translation id="2677748264148917807">Sair</translation>
+<translation id="2679714844901977852">Guarde o seu cartão e informações de faturação na sua Conta Google, <ph name="USER_EMAIL" />, para pagamentos seguros e mais rápidos</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Tamanho otimizado</translation>
<translation id="2688969097326701645">Sim, continuar</translation>
@@ -701,7 +708,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="2854764410992194509">Fornecedores de serviços de Internet (ISPs)</translation>
<translation id="2856444702002559011">Os atacantes poderão estar a tentar roubar as suas informações de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (por exemplo, palavras-passe, mensagens ou cartões de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Este site apresenta anúncios intrusivos ou enganadores.</translation>
-<translation id="286512204874376891">Um cartão virtual disfarça o seu cartão real como medida de proteção contra potenciais fraudes. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Amigável</translation>
<translation id="28761159517501904">Filmes</translation>
<translation id="2876489322757410363">Está a sair do Modo de navegação anónima para pagar através de uma aplicação externa. Pretende continuar?</translation>
@@ -802,7 +808,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="3158539265159265653">Disco</translation>
<translation id="3162559335345991374">A rede Wi-Fi que está a utilizar pode exigir que visite a respetiva página de início de sessão.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ilha</translation>
<translation id="3176929007561373547">Verifique as definições de proxy ou contacte o gestor de rede para
se certificar de que o servidor proxy está a funcionar. Se achar que não deve
utilizar um servidor proxy:
@@ -881,9 +886,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="3369192424181595722">Erro de relógio</translation>
<translation id="3369459162151165748">Peças e acessórios de veículos</translation>
<translation id="3371064404604898522">Definir o Chrome como o navegador predefinido</translation>
-<translation id="3371076217486966826"><ph name="URL" /> pretende:
- • Criar um mapa 3D do ambiente à sua volta e monitorizar a posição da câmara.
- • Utilizar a sua câmara.</translation>
<translation id="337363190475750230">Desaprovisionado</translation>
<translation id="3375754925484257129">Executar verificação de segurança do Chrome</translation>
<translation id="3377144306166885718">O servidor utilizou uma versão obsoleta do TLS.</translation>
@@ -900,6 +902,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="3399952811970034796">Endereço de entrega</translation>
<translation id="3402261774528610252">A ligação utilizada para carregar este site utilizou as versões TLS 1.0 ou TLS 1.1, que foram descontinuadas e serão desativadas no futuro. Após a desativação, os utilizadores serão impedidos de carregar este site. O servidor deverá ativar a versão TLS 1.2 ou posterior.</translation>
<translation id="3405664148539009465">Personalizar tipos de letra</translation>
+<translation id="3407789382767355356">início de sessão de terceiros</translation>
<translation id="3409896703495473338">Gerir definições de segurança</translation>
<translation id="3414952576877147120">Tamanho:</translation>
<translation id="3417660076059365994">Os ficheiros que carrega ou anexa são enviados para o Google Cloud ou para terceiros para análise. Por exemplo, podem ser analisados quanto a dados confidenciais ou software malicioso.</translation>
@@ -932,6 +935,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="3477679029130949506">Fichas de filmes e horários de salas de cinema</translation>
<translation id="3479552764303398839">Agora não</translation>
<translation id="3484560055331845446">Pode perder o acesso à sua Conta Google. O Chrome recomenda que altere a palavra-passe agora. Ser-lhe-á pedido para iniciar sessão.</translation>
+<translation id="3484861421501147767">Lembrete: código promocional guardado disponível</translation>
<translation id="3487845404393360112">Tabuleiro 4</translation>
<translation id="3495081129428749620">Procurar na página
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="3810973564298564668">Administrar</translation>
<translation id="3816482573645936981">Valor (substituído)</translation>
<translation id="382518646247711829">Se utilizar um servidor de proxy...</translation>
+<translation id="3826050100957962900">Início de sessão de terceiros</translation>
<translation id="3827112369919217609">Absoluto</translation>
<translation id="3827666161959873541">Filmes de família</translation>
<translation id="3828924085048779000">Não é permitida uma frase de acesso vazia.</translation>
@@ -1068,9 +1073,9 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="3858027520442213535">Atualizar a data e a hora</translation>
<translation id="3858860766373142691">Nome</translation>
<translation id="3872834068356954457">Ciência</translation>
+<translation id="3875783148670536197">Mostrar-me como</translation>
<translation id="3881478300875776315">Mostrar menos linhas</translation>
<translation id="3884278016824448484">Identificador do dispositivo em conflito</translation>
-<translation id="3885155851504623709">Freguesia</translation>
<translation id="388632593194507180">Monitorização detetada</translation>
<translation id="3886948180919384617">Empilhador 3</translation>
<translation id="3890664840433101773">Adicionar email</translation>
@@ -1100,9 +1105,11 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="3973234410852337861"><ph name="HOST_NAME" /> está bloqueado</translation>
<translation id="3978338123949022456">Modo de pesquisa, introduza uma consulta e prima Enter para pesquisar com <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Pássaros</translation>
+<translation id="3985750352229496475">Faça a gestão dos endereços…</translation>
<translation id="3986705137476756801">Desativar as Legendas instantâneas por agora</translation>
<translation id="3987940399970879459">Menos de 1 MB</translation>
<translation id="3990250421422698716">Separar conjuntos</translation>
+<translation id="3992684624889376114">Acerca desta página</translation>
<translation id="3996311196211510766">O site <ph name="ORIGIN" /> solicitou que seja aplicada uma política de origem
a todos os pedidos, mas, de momento, não é possível aplicar essa política.</translation>
<translation id="4006465311664329701">Métodos de pagamento, ofertas e endereços com o Google Pay</translation>
@@ -1227,6 +1234,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="4305666528087210886">Não foi possível aceder ao seu ficheiro</translation>
<translation id="4306529830550717874">Pretende guardar o endereço?</translation>
<translation id="4306812610847412719">área de transferência</translation>
+<translation id="4310070645992025887">Pesquise os seus percursos</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloquear (predefinição)</translation>
<translation id="4314815835985389558">Gerir sincronização</translation>
@@ -1277,6 +1285,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">A utilização de um proxy está desativada, mas existe uma configuração de proxy explícita especificada.</translation>
<translation id="4441832193888514600">Ignorado porque a política só pode ser definida como uma Política do Utilizador da nuvem.</translation>
+<translation id="4442470707340296952">Separadores do Chrome</translation>
<translation id="4450893287417543264">Não mostrar de novo</translation>
<translation id="4451135742916150903">Pode solicitar a ligação a dispositivos HID</translation>
<translation id="4452328064229197696">A palavra-passe que acabou de utilizar foi encontrada numa violação de dados. Para proteger as suas contas, o Gestor de palavras-passe da Google recomenda que verifique as suas palavras-passe guardadas.</translation>
@@ -1415,6 +1424,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="483241715238664915">Ativar avisos</translation>
<translation id="4834250788637067901">Métodos de pagamento, ofertas e endereços com o Google Pay</translation>
<translation id="4838327282952368871">Sonhador</translation>
+<translation id="4839087176073128681">Pague mais rápido da próxima vez e proteja o seu cartão com a segurança líder da indústria da Google.</translation>
<translation id="4840250757394056958">Ver o Histórico do Chrome</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">Obtenha descontos no comerciante <ph name="MERCHANT_NAME" /> e outros</translation>
@@ -1477,6 +1487,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="5011561501798487822">Idioma detetado</translation>
<translation id="5015510746216210676">Nome do computador:</translation>
<translation id="5017554619425969104">Texto que copiou</translation>
+<translation id="5017828934289857214">Lembrar-me mais tarde</translation>
<translation id="5018422839182700155">Não é possível abrir esta página</translation>
<translation id="5019198164206649151">Armazenamento de segurança em mau estado</translation>
<translation id="5020776957610079374">Música do mundo</translation>
@@ -1496,6 +1507,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="5051305769747448211">Comédia ao vivo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Para enviar este ficheiro através da funcionalidade Partilhar na proximidade, liberte espaço (<ph name="DISK_SPACE_SIZE" />) no seu dispositivo}other{Para enviar estes ficheiros através da funcionalidade Partilhar na proximidade, liberte espaço (<ph name="DISK_SPACE_SIZE" />) no seu dispositivo}}</translation>
<translation id="5056549851600133418">Artigos para si</translation>
+<translation id="5060483733937416656">Optou por validar com o Windows Hello em Websites que usam o fornecedor <ph name="PROVIDER_ORIGIN" />. Este fornecedor pode ter armazenado informações sobre o seu método de pagamento, que pode <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Será que quis dizer <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Histórico de impressão</translation>
<translation id="5068234115460527047">Fundos especulativos</translation>
@@ -1509,10 +1521,8 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="5087286274860437796">De momento, o certificado do servidor não é válido.</translation>
<translation id="5087580092889165836">Adicionar cartão</translation>
<translation id="5088142053160410913">Mensagem para o operador</translation>
-<translation id="5089810972385038852">Estado</translation>
<translation id="5093232627742069661">Dobra em Z</translation>
<translation id="5094747076828555589">Este servidor não conseguiu provar que é o domínio <ph name="DOMAIN" />; o Chromium não confia no respetivo certificado de segurança. Isto pode ser o resultado de uma configuração incorreta ou de um invasor a intercetar a sua ligação.</translation>
-<translation id="5095208057601539847">Província</translation>
<translation id="5097099694988056070">Estatísticas do dispositivo, como utilização da CPU/RAM.</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">O site não é seguro</translation>
@@ -1550,6 +1560,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="5171045022955879922">Pesquise ou escreva URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Equipamento</translation>
+<translation id="5177076414499237632">Saiba mais sobre a origem e o tópico desta página</translation>
<translation id="5179510805599951267">Não está em <ph name="ORIGINAL_LANGUAGE" />? Comunicar este erro</translation>
<translation id="518639307526414276">Alimentação e artigos para animais de estimação</translation>
<translation id="5190835502935405962">Barra de marcadores</translation>
@@ -1710,6 +1721,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="5624120631404540903">Gerir palavras-passe</translation>
<translation id="5629630648637658800">Falha ao carregar as definições da política</translation>
<translation id="5631439013527180824">Token de gestão do dispositivo inválido</translation>
+<translation id="5632485077360054581">Mostrar-me como</translation>
<translation id="5633066919399395251">Os atacantes atualmente em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem tentar instalar programas perigosos no seu computador que roubem ou eliminem as suas informações (por exemplo, fotos, palavras-passe, mensagens e cartões de crédito). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Conteúdo fraudulento bloqueado.</translation>
<translation id="5633259641094592098">Filmes de culto e alternativos</translation>
@@ -1827,6 +1839,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="5989320800837274978">Não foram especificados servidores proxy fixos nem um URL de script .pac.</translation>
<translation id="5992691462791905444">Dobra em Z vertical com margem superior</translation>
<translation id="5995727681868049093">Faça a gestão das suas informações, privacidade e segurança na Conta Google</translation>
+<translation id="5997247540087773573">A palavra-passe que acabou de usar foi encontrada numa violação de dados. Para proteger as suas contas, o Gestor de Palavras-passe da Google recomenda que a altere agora e verifique as suas palavras-passe guardadas.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultados para "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Clássico</translation>
<translation id="6008122969617370890">Ordem de N para 1</translation>
@@ -1922,7 +1935,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="627746635834430766">Para pagar mais rapidamente da próxima vez, guarde o cartão e o endereço de faturação na sua Conta Google.</translation>
<translation id="6279183038361895380">Premir |<ph name="ACCELERATOR" />| para mostrar o cursor</translation>
<translation id="6280223929691119688">Não é possível entregar neste endereço. Selecione um diferente.</translation>
-<translation id="6282194474023008486">Código postal</translation>
<translation id="6285507000506177184">Botão Gerir transferências no Chrome, prima Enter para gerir os ficheiros que transferiu no Chrome</translation>
<translation id="6289939620939689042">Cor da página</translation>
<translation id="6290238015253830360">Os seus artigos sugeridos são apresentados aqui</translation>
@@ -1944,6 +1956,7 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="6337133576188860026">Liberta menos de <ph name="SIZE" />. É possível que alguns sites sejam carregados mais lentamente na sua próxima visita.</translation>
<translation id="6337534724793800597">Filtrar políticas pelo nome</translation>
<translation id="6340739886198108203">A política do administrador não recomenda fazer capturas de ecrã nem gravações quando existe conteúdo confidencial visível:</translation>
+<translation id="6348220984832452017">Variações ativas</translation>
<translation id="6349101878882523185">Instale a aplicação <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Nenhuma}=1{1 palavra-passe (de <ph name="DOMAIN_LIST" />, sincronizada)}=2{2 palavras-passe (de <ph name="DOMAIN_LIST" />, sincronizadas)}other{# palavras-passe (de <ph name="DOMAIN_LIST" />, sincronizadas)}}</translation>
<translation id="6355392890578844978">Este navegador não é gerido por uma empresa ou outra entidade. A atividade neste dispositivo pode ser gerida fora do Chromium. <ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="643051589346665201">Alterar palavra-passe Google</translation>
<translation id="6433490469411711332">Editar informações de contacto</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> recusou estabelecer ligação.</translation>
-<translation id="6438025220577812695">Alterar manualmente</translation>
<translation id="6440503408713884761">Ignorado</translation>
<translation id="6443406338865242315">As extensões e os plug-ins que instalou.</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2105,9 +2117,9 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="6828866289116430505">Genética</translation>
<translation id="6831043979455480757">Traduzir</translation>
<translation id="6833752742582340615">Guarde o seu cartão e informações de faturação na sua Conta Google para pagamentos seguros e mais rápidos</translation>
-<translation id="6839929833149231406">Ãrea</translation>
<translation id="6846340164947227603">Utilizar um número de cartão virtual…</translation>
<translation id="6852204201400771460">Pretende atualizar a aplicação?</translation>
+<translation id="6857776781123259569">Faça a gestão das palavras-passe…</translation>
<translation id="686485648936420384">Recursos para o consumidor</translation>
<translation id="6865412394715372076">Não é possível validar este cartão neste momento.</translation>
<translation id="6869334554832814367">Empréstimos pessoais</translation>
@@ -2156,7 +2168,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="6965978654500191972">Dispositivo</translation>
<translation id="696703987787944103">Percetual</translation>
<translation id="6968269510885595029">Utilizar a sua chave de segurança</translation>
-<translation id="6970216967273061347">Distrito</translation>
<translation id="6971439137020188025">Crie rapidamente uma nova apresentação Google no Slides</translation>
<translation id="6972629891077993081">Dispositivos HID</translation>
<translation id="6973656660372572881">Foram especificados servidores proxy fixos e um URL de script .pac.</translation>
@@ -2195,7 +2206,6 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<translation id="7081308185095828845">Esta funcionalidade não está disponível no seu dispositivo.</translation>
<translation id="7083258188081898530">Tabuleiro 9</translation>
<translation id="7086090958708083563">Carregamento solicitado pelo utilizador</translation>
-<translation id="7087282848513945231">Município</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, prima Tab e, em seguida, Enter para gerir as autorizações e os dados armazenados em sites nas Definições do Chrome.</translation>
<translation id="7096937462164235847">A identidade deste Website não foi validada.</translation>
<translation id="7101893872976785596">Filmes de terror</translation>
@@ -2214,10 +2224,10 @@ Se não permitir, isto será bloqueado pelas suas definições de privacidade. I
<ph name="LIST_ITEM" />Informações introduzidas em formulários<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Não é possível enviar para este endereço. Selecione um diferente.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">O seu administrador proibiu a cópia destes dados.</translation>
<translation id="7135130955892390533">Mostrar estado</translation>
<translation id="7138472120740807366">Método de fornecimento</translation>
-<translation id="7139724024395191329">Emirato</translation>
<translation id="7139892792842608322">Tabuleiro principal</translation>
<translation id="714064300541049402">Turno X da imagem 2 lateral</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2433,6 +2443,7 @@ Detalhes adicionais:
<translation id="7669271284792375604">Os utilizadores mal intencionados neste site podem tentar enganá-lo para instalar programas que são prejudiciais para a sua experiência de navegação (por exemplo, ao alterar a sua página inicial ou ao mostrar anúncios adicionais em sites que visita).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Ações tomadas com dados sinalizados como confidenciais (1 ação desde o início de sessão). <ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" />}other{Ações tomadas com dados sinalizados como confidenciais (# ações desde o início de sessão). <ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Caixa de correio 6</translation>
+<translation id="7675325315208090829">Faça a gestão dos métodos de pagamento…</translation>
<translation id="7676643023259824263">Pesquisar texto da área de transferência, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Veja e faça a gestão do histórico de navegação nas definições do Chrome</translation>
<translation id="7679947978757153706">Basebol</translation>
@@ -2475,7 +2486,6 @@ Detalhes adicionais:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Na mesma ordem, com orientação para cima</translation>
-<translation id="777702478322588152">Município</translation>
<translation id="7791011319128895129">Não lançada</translation>
<translation id="7791196057686275387">Empacotar</translation>
<translation id="7791543448312431591">Adicionar</translation>
@@ -2566,12 +2576,12 @@ Detalhes adicionais:
<translation id="8055534648776115597">Formação vocacional e contínua</translation>
<translation id="8057711352706143257">O "<ph name="SOFTWARE_NAME" />" não está configurado corretamente. Normalmente, a desinstalação do "<ph name="SOFTWARE_NAME" />" resolve o problema. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Produção de alimentos</translation>
-<translation id="8066955247577885446">Lamentamos, mas ocorreu um erro.</translation>
<translation id="8067872629359326442">Acabou de introduzir a sua palavra-passe num site fraudulento. O Chromium pode ajudar. Para alterar a sua palavra-passe e notificar a Google de que a sua conta pode estar em risco, clique em Proteger conta.</translation>
<translation id="8070439594494267500">Ãcone da app</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Crie rapidamente uma nova Folha de cálculo do Google Sheets</translation>
<translation id="8075898834294118863">Gerir definições de sites</translation>
+<translation id="8076492880354921740">Separadores</translation>
<translation id="8078141288243656252">Não é possível criar anotação se tiver sido aplicada uma rotação ao documento</translation>
<translation id="8079031581361219619">Pretende atualizar o site?</translation>
<translation id="8081087320434522107">Veículos de três volumes</translation>
@@ -2694,6 +2704,7 @@ Detalhes adicionais:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Definições</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, prima Tab e, em seguida, Enter para gerir as suas preferências de cookies nas Definições do Chrome.</translation>
<translation id="8433057134996913067">Esta opção termina a sessão na maioria dos Sites.</translation>
<translation id="8434840396568290395">Animais de estimação</translation>
@@ -2792,6 +2803,7 @@ Detalhes adicionais:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> é o seu código para <ph name="ORIGIN" />.</translation>
<translation id="874918643257405732">Adicione este separador aos marcadores</translation>
<translation id="8751426954251315517">Tente novamente na próxima oportunidade.</translation>
+<translation id="8757526089434340176">Oferta do Google Pay disponível</translation>
<translation id="8758885506338294482">Competições de videojogos</translation>
<translation id="8759274551635299824">Este cartão expirou</translation>
<translation id="87601671197631245">Este site utiliza uma configuração de segurança desatualizada, que pode expor as suas informações (por exemplo, palavras-passe, mensagens ou cartões de crédito) quando são enviadas para este site.</translation>
@@ -2799,6 +2811,7 @@ Detalhes adicionais:
<translation id="8763927697961133303">Dispositivo USB</translation>
<translation id="8763986294015493060">Feche todas as janelas de navegação anónima atualmente abertas</translation>
<translation id="8766943070169463815">A página de autenticação das credenciais de pagamento seguro está aberta</translation>
+<translation id="8767765348545497220">Fechar balão de ajuda</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motos</translation>
<translation id="8790007591277257123">&amp;Refazer eliminação</translation>
@@ -2811,6 +2824,7 @@ Detalhes adicionais:
<translation id="8806285662264631610">Produtos para o banho e o corpo</translation>
<translation id="8807160976559152894">Cortar no fim de cada página</translation>
<translation id="8808828119384186784">Definições do Chrome</translation>
+<translation id="8813277370772331957">Lembrar-me mais tarde</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, prima Tab e, em seguida, Enter para atualizar o Chrome nas Definições do Chrome.</translation>
<translation id="8820817407110198400">Marcadores</translation>
<translation id="882338992931677877">Ranhura manual</translation>
@@ -2990,6 +3004,7 @@ Detalhes adicionais:
<translation id="988159990683914416">Compilação de programador</translation>
<translation id="989988560359834682">Editar endereço</translation>
<translation id="991413375315957741">sensores de movimento ou de luz</translation>
+<translation id="992110854164447044">Um cartão virtual oculta o seu cartão real como medida de proteção contra potenciais fraudes. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401">O "<ph name="SOFTWARE_NAME" />" não foi instalado corretamente no computador ou na rede:
diff --git a/chromium/components/strings/components_strings_ro.xtb b/chromium/components/strings/components_strings_ro.xtb
index f25386d2cbb..b9ee5534256 100644
--- a/chromium/components/strings/components_strings_ro.xtb
+++ b/chromium/components/strings/components_strings_ro.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Burse și ajutoare financiare</translation>
<translation id="1048785276086539861">Când editezi adnotări, acest document va reveni la afișarea cu o pagină</translation>
<translation id="1050038467049342496">închide celelalte aplicații;</translation>
+<translation id="1053959602163383901">Ai ales să confirmi folosind un dispozitiv de autentificare pe site-urile care folosesc <ph name="PROVIDER_ORIGIN" />. Este posibil ca acest furnizor să fi stocat informații despre metoda de plată, pe care le poți <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Anulați adăugarea</translation>
<translation id="1056663316309890343">Software foto</translation>
<translation id="1056898198331236512">Avertisment</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">Metodă de preluare</translation>
<translation id="1281476433249504884">Receptorul de hârtie 1</translation>
<translation id="1285320974508926690">Nu traduce niciodată acest site</translation>
+<translation id="1288548991597756084">Salvează cardul în siguranță</translation>
<translation id="1292571435393770077">Tava 16</translation>
<translation id="1292701964462482250">„Pe computer există software care împiedică browserul Chrome să se conecteze în siguranță la internet†(doar pentru computerele Windows)</translation>
<translation id="1294154142200295408">Variații ale liniilor de comandă</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Pentru a remedia eroarea, dă clic pe &lt;strong&gt;Conectează-te&lt;/strong&gt;, în pagina pe care încerci să o deschizi.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Peisagistică</translation>
<translation id="1513706915089223971">Lista intrărilor în istoric</translation>
+<translation id="1516097932025103760">Va fi criptat, salvat în siguranță și codul CVC nu este stocat niciodată.</translation>
<translation id="1517433312004943670">Numărul de telefon este obligatoriu</translation>
<translation id="1519264250979466059">Dată versiune:</translation>
<translation id="1521159554480556801">Arte cu fibre și textile</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Salvează și completează metodele de plată</translation>
<translation id="1663943134801823270">Cardurile și adresele sunt din Chrome. Le poți gestiona în <ph name="BEGIN_LINK" />Setări<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Paginile în <ph name="SOURCE_LANGUAGE" /> vor fi traduse în <ph name="TARGET_LANGUAGE" /> de acum înainte.</translation>
+<translation id="1673886523110456987">Finalizează achiziția cu cardul <ph name="CARD_DETAIL" /> pentru a folosi oferta</translation>
<translation id="1674504678466460478">Din <ph name="SOURCE_LANGUAGE" /> în <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Cu latura scurtă înainte</translation>
<translation id="168693727862418163">Valoarea politicii nu s-a validat în raport cu schema și va fi ignorată.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Senzori de mișcare</translation>
<translation id="1717494416764505390">Căsuța de e-mail 3</translation>
<translation id="1718029547804390981">Documentul este prea mare pentru a face adnotări</translation>
+<translation id="1720941539803966190">ÃŽnchide tutorialul</translation>
<translation id="1721424275792716183">* Câmp obligatoriu</translation>
<translation id="1727613060316725209">Certificatul este valid</translation>
<translation id="1727741090716970331">Adaugă un număr de card valid</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">Grătare</translation>
<translation id="2053111141626950936">Paginile în <ph name="LANGUAGE" /> nu vor fi traduse.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Când opțiunea este activată și starea este Activă, Chrome stabilește un grup mare de utilizatori cu care se aseamănă cel mai mult activitatea ta de navigare recentă. Advertiserii pot selecta anunțuri pentru grup și activitatea ta de navigare este păstrată privată pe dispozitivul tău. Grupul este actualizat în fiecare zi.}=1{Când opțiunea este activată și starea este Activă, Chrome stabilește un grup mare de utilizatori cu care se aseamănă cel mai mult activitatea ta de navigare recentă. Advertiserii pot selecta anunțuri pentru grup și activitatea ta de navigare este păstrată privată pe dispozitivul tău. Grupul este actualizat în fiecare zi.}few{Când opțiunea este activată și starea este Activă, Chrome stabilește un grup mare de utilizatori cu care se aseamănă cel mai mult activitatea ta de navigare recentă. Advertiserii pot selecta anunțuri pentru grup și activitatea ta de navigare este păstrată privată pe dispozitivul tău. Grupul este actualizat o dată la {NUM_DAYS} zile.}other{Când opțiunea este activată și starea este Activă, Chrome stabilește un grup mare de utilizatori cu care se aseamănă cel mai mult activitatea ta de navigare recentă. Advertiserii pot selecta anunțuri pentru grup și activitatea ta de navigare este păstrată privată pe dispozitivul tău. Grupul este actualizat o dată la {NUM_DAYS} de zile.}}</translation>
-<translation id="2053553514270667976">Cod zip</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 sugestie}few{# sugestii}other{# de sugestii}}</translation>
+<translation id="2066915425250589881">solicitare de ștergere</translation>
<translation id="2068528718802935086">Bebeluși și copii</translation>
<translation id="2071156619270205202">Cardul nu este eligibil pentru un număr de card virtual.</translation>
<translation id="2071692954027939183">Notificările au fost blocate automat deoarece nu le permiți de obicei</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">Butonul Gestionează sincronizarea, apasă pe Enter pentru a gestiona informațiile de sincronizat din setările Chrome</translation>
<translation id="2091887806945687916">Sunet</translation>
<translation id="2094505752054353250">Nepotrivire domeniu</translation>
-<translation id="2096368010154057602">Departament</translation>
<translation id="2099652385553570808">Capsare triplă în stânga</translation>
<translation id="2101225219012730419">Versiune:</translation>
<translation id="2102134110707549001">Sugerează o parolă puternică…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">Fotbal american</translation>
<translation id="2187317261103489799">Detectează (în mod prestabilit)</translation>
<translation id="2188375229972301266">Perforare multiplă în partea de jos</translation>
-<translation id="2188852899391513400">Parola pe care tocmai ai folosit-o a fost găsită într-o încălcare a securității datelor. Pentru a-ți proteja conturile, Managerul de parole Google îți recomandă să schimbi parola acum, iar apoi să verifici parolele salvate.</translation>
<translation id="219906046732893612">Renovarea casei</translation>
<translation id="2202020181578195191">Introdu un an de expirare valid</translation>
<translation id="22081806969704220">Tava 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">Editează fișiere</translation>
<translation id="2215963164070968490">Câini</translation>
<translation id="2218879909401188352">Atacatorii de pe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ar putea să instaleze aplicații periculoase care deteriorează dispozitivul, adaugă costuri ascunse pe factura de telefonie mobilă sau îți fură informațiile cu caracter personal. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Repornește tutorialul</translation>
+<translation id="2219735899272417925">Este necesară resetarea dispozitivului</translation>
<translation id="2224337661447660594">Nu există conexiune la internet</translation>
<translation id="2230458221926704099">Remediază conexiunea folosind <ph name="BEGIN_LINK" />aplicația de diagnosticare<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Trimite acum</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">Nepermise (prestabilit)</translation>
<translation id="2512413427717747692">Butonul Setează Chrome ca browser prestabilit, apasă pe Enter ca să setezi Chrome ca browser prestabilit pentru sistem în setările iOS</translation>
<translation id="2515629240566999685">să verifici semnalul din zona ta.</translation>
+<translation id="2515761554693942801">Ai ales să confirmi cu Touch ID pe site-urile care folosesc <ph name="PROVIDER_ORIGIN" />. Este posibil ca acest furnizor să fi stocat informații despre metoda de plată, pe care le poți <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Capsare în dreapta jos</translation>
<translation id="2521736961081452453">Creează un formular</translation>
<translation id="2523886232349826891">Salvat numai pe acest dispozitiv</translation>
<translation id="2524461107774643265">Adaugă mai multe informații</translation>
<translation id="2529899080962247600">Acest câmp nu poate să conțină mai mult de <ph name="MAX_ITEMS_LIMIT" /> intrări. Intrările suplimentare vor fi ignorate.</translation>
+<translation id="253493526287553278">Vezi detaliile codului promoțional</translation>
<translation id="2535585790302968248">Deschide o nouă filă incognito pentru a naviga în privat</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{și încă una}few{și încă #}other{și încă #}}</translation>
<translation id="2536110899380797252">Adaugă o adresă</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">Artă fotografică și digitală</translation>
<translation id="2601150049980261779">Filme romantice</translation>
<translation id="2604589665489080024">Muzică pop</translation>
-<translation id="2609632851001447353">Modificări</translation>
<translation id="2610561535971892504">Dă clic pentru a copia</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />nu va salva<ph name="END_EMPHASIS" /> următoarele informații:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Aniversări și onomastici</translation>
<translation id="2677748264148917807">Ieși</translation>
+<translation id="2679714844901977852">Salvează-ți cardul și informațiile de facturare în Contul Google <ph name="USER_EMAIL" /> ca să finalizezi achizițiile mai rapid și în siguranță</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Potrivire optimă</translation>
<translation id="2688969097326701645">Da, continuă</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">Furnizori de servicii de internet (ISP)</translation>
<translation id="2856444702002559011">Atacatorii pot încerca să îți fure informațiile de pe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (de exemplu, parole, mesaje sau date despre cardurile de credit). <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Acest site afișează anunțuri deranjante sau înșelătoare.</translation>
-<translation id="286512204874376891">Cardul virtual îți maschează cardul fizic pentru a te proteja împotriva fraudelor. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Amical</translation>
<translation id="28761159517501904">Filme</translation>
<translation id="2876489322757410363">Vei părăsi modul incognito pentru a plăti folosind o aplicație externă. Continui?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">Disc</translation>
<translation id="3162559335345991374">Rețeaua Wi-Fi pe care o folosești poate solicita accesarea paginii de conectare.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Insulă</translation>
<translation id="3176929007561373547">Verifică setările de proxy sau contactează administratorul de rețea
pentru a te asigura că serverul proxy funcționează. Dacă nu consideri că ar trebui
să folosești un server proxy:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">Eroare de ceas</translation>
<translation id="3369459162151165748">Piese și accesorii pentru vehicule</translation>
<translation id="3371064404604898522">Setează Chrome ca browser prestabilit</translation>
-<translation id="3371076217486966826"><ph name="URL" /> vrea:
- • să creeze o hartă 3D a lucrurilor din jurul tău și să urmărească poziția camerei video;
- • să utilizeze camera.</translation>
<translation id="337363190475750230">Scos din uz</translation>
<translation id="3375754925484257129">Rulează verificarea de siguranță pentru Chrome</translation>
<translation id="3377144306166885718">Serverul a folosit o versiune TLS învechită.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">Adresă de livrare</translation>
<translation id="3402261774528610252">Conexiunea folosită pentru a încărca acest site a utilizat TLS 1.0 sau TLS 1.1, care sunt învechite și vor fi dezactivate în viitor. După dezactivare, utilizatorii nu vor mai putea să încarce site-ul. Serverul ar trebui să activeze TLS 1.2 sau o versiune ulterioară.</translation>
<translation id="3405664148539009465">Personalizați fonturile</translation>
+<translation id="3407789382767355356">conectarea prin terți</translation>
<translation id="3409896703495473338">Gestionează setările de securitate</translation>
<translation id="3414952576877147120">Dimensiune:</translation>
<translation id="3417660076059365994">Fișierele pe care le încarci sau atașezi sunt trimise la Google Cloud sau la terți spre analiză. De exemplu, se pot căuta date sensibile sau programe malware.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">Liste de filme și ore de difuzare la teatru</translation>
<translation id="3479552764303398839">Nu acum</translation>
<translation id="3484560055331845446">Este posibil să pierzi accesul la Contul Google. Chrome îți recomandă să îți schimbi parola acum. Ți se va cere să te conectezi.</translation>
+<translation id="3484861421501147767">Memento: codul promoțional salvat este disponibil</translation>
<translation id="3487845404393360112">Tava 4</translation>
<translation id="3495081129428749620">Caută în pagină
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">Gestionează</translation>
<translation id="3816482573645936981">Valoare (înlocuită)</translation>
<translation id="382518646247711829">Dacă utilizați un server proxy...</translation>
+<translation id="3826050100957962900">Third-party sign-in</translation>
<translation id="3827112369919217609">Absolută</translation>
<translation id="3827666161959873541">Filme pentru toată familia</translation>
<translation id="3828924085048779000">Trebuie să fie introdusă expresia de acces.</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">Actualizează data și ora</translation>
<translation id="3858860766373142691">Nume</translation>
<translation id="3872834068356954457">Știință</translation>
+<translation id="3875783148670536197">Arată-mi cum</translation>
<translation id="3881478300875776315">Afișează mai puține rânduri</translation>
<translation id="3884278016824448484">Identificator de gadget în conflict</translation>
-<translation id="3885155851504623709">Parohie</translation>
<translation id="388632593194507180">S-a detectat monitorizarea</translation>
<translation id="3886948180919384617">Receptorul de hârtie 3</translation>
<translation id="3890664840433101773">Adaugă o adresă de e-mail</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> este blocat</translation>
<translation id="3978338123949022456">Modul de căutare, introdu o interogare și apasă pe Enter ca să cauți cu <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Păsări</translation>
+<translation id="3985750352229496475">Gestionează adresele…</translation>
<translation id="3986705137476756801">Dezactivează Subtitrările live pentru moment</translation>
<translation id="3987940399970879459">Mai puțin de 1 MB</translation>
<translation id="3990250421422698716">Decalaj de îndoire</translation>
+<translation id="3992684624889376114">Despre această pagină</translation>
<translation id="3996311196211510766">Site-ul <ph name="ORIGIN" /> a solicitat ca o politică de origine
să se aplice tuturor solicitărilor, dar această politică nu poate fi aplicată momentan.</translation>
<translation id="4006465311664329701">Metode de plată, oferte și adrese care folosesc Google Pay</translation>
@@ -1227,6 +1234,7 @@
<translation id="4305666528087210886">Fișierul nu a putut fi accesat</translation>
<translation id="4306529830550717874">Salvezi adresa?</translation>
<translation id="4306812610847412719">Clipboard</translation>
+<translation id="4310070645992025887">Caută parcursuri</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blochează (în mod prestabilit)</translation>
<translation id="4314815835985389558">Gestionează sincronizarea</translation>
@@ -1277,6 +1285,7 @@
<translation id="4435702339979719576">Carte poștală)</translation>
<translation id="443673843213245140">Utilizarea unui proxy este dezactivată, dar o configurare proxy este specificată în mod explicit.</translation>
<translation id="4441832193888514600">Ignorată deoarece politica poate fi setată numai ca politică privind utilizatorii cloud.</translation>
+<translation id="4442470707340296952">File Chrome</translation>
<translation id="4450893287417543264">Nu mai afișa</translation>
<translation id="4451135742916150903">Poate solicita permisiunea de a se conecta la dispozitive HID</translation>
<translation id="4452328064229197696">Parola pe care tocmai ai folosit-o a fost găsită într-o încălcare a securității datelor. Pentru a-ți proteja conturile, Managerul de parole Google îți recomandă să verifici parolele salvate.</translation>
@@ -1415,6 +1424,7 @@
<translation id="483241715238664915">Activează avertizările</translation>
<translation id="4834250788637067901">Metode de plată, oferte și adrese care folosesc Google Pay</translation>
<translation id="4838327282952368871">Visare</translation>
+<translation id="4839087176073128681">Plătește mai rapid data viitoare și protejează-ți cardul cu cele mai bune funcții de securitate din domeniu de la Google.</translation>
<translation id="4840250757394056958">Vezi istoricul Chrome</translation>
<translation id="484462545196658690">Automat</translation>
<translation id="484671803914931257">Obține reduceri de la <ph name="MERCHANT_NAME" /> și mulți alții</translation>
@@ -1477,6 +1487,7 @@
<translation id="5011561501798487822">Limba detectată</translation>
<translation id="5015510746216210676">Numele dispozitivului:</translation>
<translation id="5017554619425969104">Textul copiat de tine</translation>
+<translation id="5017828934289857214">Amintește-mi mai târziu</translation>
<translation id="5018422839182700155">Pagina nu poate fi deschisă</translation>
<translation id="5019198164206649151">Depozit de fundal în stare nevalidă</translation>
<translation id="5020776957610079374">Muzică World Music</translation>
@@ -1496,6 +1507,7 @@
<translation id="5051305769747448211">Comedie în direct</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Pentru a trimite fișierul prin Trimitere în apropiere, eliberează spațiu (<ph name="DISK_SPACE_SIZE" />) pe dispozitiv}few{Pentru a trimite fișierele prin Trimitere în apropiere, eliberează spațiu (<ph name="DISK_SPACE_SIZE" />) pe dispozitiv}other{Pentru a trimite fișierele prin Trimitere în apropiere, eliberează spațiu (<ph name="DISK_SPACE_SIZE" />) pe dispozitiv}}</translation>
<translation id="5056549851600133418">Articole pentru tine</translation>
+<translation id="5060483733937416656">Ai ales să confirmi cu Windows Hello pe site-urile care folosesc <ph name="PROVIDER_ORIGIN" />. Este posibil ca acest furnizor să fi stocat informații despre metoda de plată, pe care le poți <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Ai vrut să scrii <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Istoricul de printare</translation>
<translation id="5068234115460527047">Fonduri speculative</translation>
@@ -1509,10 +1521,8 @@
<translation id="5087286274860437796">Momentan, certificatul serverului este nevalid.</translation>
<translation id="5087580092889165836">Adaugă un card</translation>
<translation id="5088142053160410913">Mesajul pentru operator</translation>
-<translation id="5089810972385038852">Stat</translation>
<translation id="5093232627742069661">Îndoire în Z</translation>
<translation id="5094747076828555589">Acest server nu a putut dovedi că este <ph name="DOMAIN" />; Chromium nu consideră că certificatul său de securitate este de încredere. Cauza poate fi o configurare greșită sau interceptarea conexiunii de către un atacator.</translation>
-<translation id="5095208057601539847">Provincie</translation>
<translation id="5097099694988056070">Statistici privind dispozitivul, cum ar fi utilizarea CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Site-ul nu este securizat</translation>
@@ -1550,6 +1560,7 @@
<translation id="5171045022955879922">Caută sau introdu adresa URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Computer</translation>
+<translation id="5177076414499237632">Află despre sursa și subiectul acestei pagini</translation>
<translation id="5179510805599951267">Nu este în <ph name="ORIGINAL_LANGUAGE" />? Semnalează această eroare.</translation>
<translation id="518639307526414276">Alimente și accesorii de îngrijire pentru animalele de companie</translation>
<translation id="5190835502935405962">Bară de marcaje</translation>
@@ -1710,6 +1721,7 @@
<translation id="5624120631404540903">Gestionați parolele</translation>
<translation id="5629630648637658800">Setările pentru politică nu au putut fi încărcate</translation>
<translation id="5631439013527180824">Indicativ nevalid pentru gestionarea gadgetului</translation>
+<translation id="5632485077360054581">Arată-mi cum</translation>
<translation id="5633066919399395251">Atacatorii de pe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pot încerca să instaleze programe periculoase pe computerul tău, care să îți fure sau să îți șteargă informațiile (de exemplu, fotografii, parole, mesaje sau date despre cardurile de credit). <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Conținutul înșelător a fost blocat.</translation>
<translation id="5633259641094592098">Filme cult și indie</translation>
@@ -1827,6 +1839,7 @@
<translation id="5989320800837274978">Nu sunt specificate nici servere proxy fixe și nici o adresă URL pentru scripturi .pac.</translation>
<translation id="5992691462791905444">Îndoire în Z pentru inginerie</translation>
<translation id="5995727681868049093">Gestionează-ți informațiile, confidențialitatea și securitatea Contului Google</translation>
+<translation id="5997247540087773573">Parola pe care tocmai ai folosit-o a fost găsită într-o încălcare a securității datelor. Pentru a-ți proteja conturile, Managerul de parole Google îți recomandă să schimbi parola acum și să verifici parolele salvate.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> rezultate pentru „<ph name="SEARCH_TEXT" />â€</translation>
<translation id="6006484371116297560">Tema clasică</translation>
<translation id="6008122969617370890">ÃŽn ordinea de la N-la-1</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">Pentru a plăti mai rapid data viitoare, salvează cardul și adresa de facturare în Contul Google.</translation>
<translation id="6279183038361895380">Apasă pe |<ph name="ACCELERATOR" />| pentru a fi afișat cursorul</translation>
<translation id="6280223929691119688">Nu se poate livra la această adresă. Selectează altă adresă.</translation>
-<translation id="6282194474023008486">Cod poștal</translation>
<translation id="6285507000506177184">Butonul de gestionare a descărcărilor în Chrome, apasă pe Enter pentru a gestiona fișierele pe care le-ai descărcat în Chrome</translation>
<translation id="6289939620939689042">Culoarea paginii</translation>
<translation id="6290238015253830360">Articolele sugerate apar aici</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026">Eliberează mai puțin de <ph name="SIZE" />. Este posibil ca unele site-uri să se încarce mai lent la următoarea accesare.</translation>
<translation id="6337534724793800597">Filtrați politicile după nume</translation>
<translation id="6340739886198108203">Politica implementată de administrator nu recomandă să faci capturi de ecran sau înregistrări atunci când este vizibil conținut confidențial:</translation>
+<translation id="6348220984832452017">Variante active</translation>
<translation id="6349101878882523185">Instalează <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Niciuna}=1{O parolă (pentru <ph name="DOMAIN_LIST" />, sincronizată)}=2{2 parole (pentru <ph name="DOMAIN_LIST" />, sincronizate)}few{# parole (pentru <ph name="DOMAIN_LIST" />, sincronizate)}other{# de parole (pentru <ph name="DOMAIN_LIST" />, sincronizate)}}</translation>
<translation id="6355392890578844978">Browserul nu este gestionat de o companie sau o altă organizație. Este posibil ca activitatea de pe acest dispozitiv să fie gestionată în afara Chromium. <ph name="BEGIN_LINK" />Află mai multe<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">Schimbă parola Google</translation>
<translation id="6433490469411711332">Editează informațiile de contact</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> a refuzat conexiunea.</translation>
-<translation id="6438025220577812695">Schimbați dvs.</translation>
<translation id="6440503408713884761">Ignorat</translation>
<translation id="6443406338865242315">extensiile și pluginurile instalate,</translation>
<translation id="6446163441502663861">Kahu (Plic)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">Genetică</translation>
<translation id="6831043979455480757">Tradu</translation>
<translation id="6833752742582340615">Salvează-ți cardul și informațiile de facturare în Contul Google ca să finalizezi achizițiile mai rapid și în siguranță</translation>
-<translation id="6839929833149231406">Zonă</translation>
<translation id="6846340164947227603">Folosește un număr de card virtual…</translation>
<translation id="6852204201400771460">Reîncarci aplicația?</translation>
+<translation id="6857776781123259569">Gestionează parolele…</translation>
<translation id="686485648936420384">Resurse pentru consumatori</translation>
<translation id="6865412394715372076">Acest card nu poate fi confirmat chiar acum.</translation>
<translation id="6869334554832814367">Credite personale</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">Dispozitiv</translation>
<translation id="696703987787944103">Perceptivă</translation>
<translation id="6968269510885595029">Folosește cheia de securitate</translation>
-<translation id="6970216967273061347">JudeÈ›</translation>
<translation id="6971439137020188025">Creează rapid o prezentare Google în Prezentări</translation>
<translation id="6972629891077993081">Dispozitive HID</translation>
<translation id="6973656660372572881">Sunt specificate atât servere proxy fixe, cât și o adresă URL pentru scripturi .pac.</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">Această funcție nu este disponibilă pe dispozitivul tău</translation>
<translation id="7083258188081898530">Tava 9</translation>
<translation id="7086090958708083563">Utilizatorul a solicitat încărcarea</translation>
-<translation id="7087282848513945231">Comitat</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, apasă pe Tab, apoi pe Enter pentru a gestiona permisiunile și datele stocate pe toate site-urile din setările Chrome</translation>
<translation id="7096937462164235847">Identitatea site-ului nu este confirmată.</translation>
<translation id="7101893872976785596">Filme horror</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />informațiile introduse în formulare.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Nu se poate expedia la această adresă. Selectează altă adresă.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Administratorul a interzis copierea acestor date.</translation>
<translation id="7135130955892390533">Arată starea</translation>
<translation id="7138472120740807366">Metodă de livrare</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Tava principală</translation>
<translation id="714064300541049402">Deplasarea poziției X a imaginii de pe fața 2</translation>
<translation id="7152423860607593928">Number-14 (Plic)</translation>
@@ -2434,6 +2444,7 @@ Detalii suplimentare:
<translation id="7669271284792375604">Atacatorii de pe acest site pot încerca să te înșele pentru a instala programe care dăunează experienței de navigare (de exemplu, schimbând pagina de pornire sau afișând anunțuri suplimentare pe site-urile pe care le accesezi).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Acțiuni realizate cu datele marcate drept confidențiale (o acțiune de la conectare). <ph name="BEGIN_LINK" />Află mai multe<ph name="END_LINK" />}few{Acțiuni realizate cu datele marcate drept confidențiale (# acțiuni de la conectare). <ph name="BEGIN_LINK" />Află mai multe<ph name="END_LINK" />}other{Acțiuni realizate cu datele marcate drept confidențiale (# de acțiuni de la conectare). <ph name="BEGIN_LINK" />Află mai multe<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Căsuța de e-mail 6</translation>
+<translation id="7675325315208090829">Gestionează metodele de plată…</translation>
<translation id="7676643023259824263">Caută text din clipboard, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Vezi și gestionează istoricul de navigare în setările Chrome</translation>
<translation id="7679947978757153706">Baseball</translation>
@@ -2476,7 +2487,6 @@ Detalii suplimentare:
<translation id="7766518757692125295">Contur</translation>
<translation id="7770259615151589601">Designated-Lung</translation>
<translation id="7773005668374414287">În aceeași ordine, cu fața în sus</translation>
-<translation id="777702478322588152">Prefectură</translation>
<translation id="7791011319128895129">Nelansată</translation>
<translation id="7791196057686275387">Balot</translation>
<translation id="7791543448312431591">Adaugă</translation>
@@ -2567,12 +2577,12 @@ Detalii suplimentare:
<translation id="8055534648776115597">Educație vocațională și continuă</translation>
<translation id="8057711352706143257">„<ph name="SOFTWARE_NAME" />†nu este configurat corect. De obicei, problema se remediază dezinstalând „<ph name="SOFTWARE_NAME" />†<ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Producția de alimente</translation>
-<translation id="8066955247577885446">A apărut o eroare.</translation>
<translation id="8067872629359326442">Ai introdus parola pe un site înșelător. Chromium te poate ajuta. Ca să schimbi parola și să anunți Google că respectivul cont poate fi în pericol, dă clic pe Protejează contul.</translation>
<translation id="8070439594494267500">Pictograma aplicației</translation>
<translation id="8074253406171541171">10x13 (Plic)</translation>
<translation id="8075736640322370409">Creează rapid o Foaie de calcul Google</translation>
<translation id="8075898834294118863">Butonul Gestionează setările pentru site-uri</translation>
+<translation id="8076492880354921740">File</translation>
<translation id="8078141288243656252">Nu se poate adnota când este rotit</translation>
<translation id="8079031581361219619">Reîncarci site-ul?</translation>
<translation id="8081087320434522107">Berline</translation>
@@ -2695,6 +2705,7 @@ Detalii suplimentare:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Setări</translation>
+<translation id="8428634594422941299">Am înțeles</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, apasă pe Tab, apoi pe Enter pentru a gestiona preferințele privind cookie-urile din setările Chrome</translation>
<translation id="8433057134996913067">Astfel, te vei deconecta de pe majoritatea site-urilor.</translation>
<translation id="8434840396568290395">Animale de companie</translation>
@@ -2793,6 +2804,7 @@ Detalii suplimentare:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> este codul tău pentru <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Marchează această filă</translation>
<translation id="8751426954251315517">Încearcă mai târziu</translation>
+<translation id="8757526089434340176">Ofertă Google Pay disponibilă</translation>
<translation id="8758885506338294482">Competiții de jocuri video</translation>
<translation id="8759274551635299824">Acest card este expirat</translation>
<translation id="87601671197631245">Site-ul folosește o configurație de securitate învechită, care îți poate expune informațiile (de exemplu, parolele, mesajele sau cardurile de credit) când sunt trimise la acest site.</translation>
@@ -2800,6 +2812,7 @@ Detalii suplimentare:
<translation id="8763927697961133303">Dispozitiv USB</translation>
<translation id="8763986294015493060">ÃŽnchide toate ferestrele incognito deschise</translation>
<translation id="8766943070169463815">Foaia de autentificare cu date de conectare pentru plăți securizate este deschisă</translation>
+<translation id="8767765348545497220">ÃŽnchide balonul de ajutor</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motociclete</translation>
<translation id="8790007591277257123">&amp;Repetați ștergerea</translation>
@@ -2812,6 +2825,7 @@ Detalii suplimentare:
<translation id="8806285662264631610">Produse pentru baie și corp</translation>
<translation id="8807160976559152894">Decupare după fiecare pagină</translation>
<translation id="8808828119384186784">Setări Chrome</translation>
+<translation id="8813277370772331957">Amintește-mi mai târziu</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, apasă pe Tab, apoi pe Enter pentru a actualiza Chrome din setările Chrome</translation>
<translation id="8820817407110198400">Marcaje</translation>
<translation id="882338992931677877">Slot manual</translation>
@@ -2991,6 +3005,7 @@ Detalii suplimentare:
<translation id="988159990683914416">Versiune de programare</translation>
<translation id="989988560359834682">Editați adresa</translation>
<translation id="991413375315957741">Senzori de mișcare sau de lumină</translation>
+<translation id="992110854164447044">Cardul virtual îți ascunde cardul fizic pentru a te proteja împotriva fraudelor. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Roz</translation>
<translation id="992432478773561401">„<ph name="SOFTWARE_NAME" />†nu a fost instalat corect pe computer sau în rețea:
diff --git a/chromium/components/strings/components_strings_ru.xtb b/chromium/components/strings/components_strings_ru.xtb
index f2073a408f4..e32cafaf7f2 100644
--- a/chromium/components/strings/components_strings_ru.xtb
+++ b/chromium/components/strings/components_strings_ru.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Гранты, Ñтипендии и финанÑÐ¾Ð²Ð°Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒ</translation>
<translation id="1048785276086539861">При редактировании заметок документ вернетÑÑ Ð² одноÑтраничный режим проÑмотра.</translation>
<translation id="1050038467049342496">Закройте другие приложениÑ.</translation>
+<translation id="1053959602163383901">Ð’Ñ‹ выбрали приложение Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿Ð¾ÐºÑƒÐ¿Ð¾Ðº на Ñайтах, пользующихÑÑ ÑƒÑлугами поÑтавщика <ph name="PROVIDER_ORIGIN" />. Он мог Ñохранить информацию о вашем ÑпоÑобе оплаты. У Ð²Ð°Ñ ÐµÑÑ‚ÑŒ право <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Отменить добавление</translation>
<translation id="1056663316309890343">ПО Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ñ„Ð¾Ñ‚Ð¾Ð³Ñ€Ð°Ñ„Ð¸Ñми</translation>
<translation id="1056898198331236512">Внимание</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">СпоÑоб получениÑ</translation>
<translation id="1281476433249504884">Укладчик 1</translation>
<translation id="1285320974508926690">Ðикогда не переводить Ñтот Ñайт</translation>
+<translation id="1288548991597756084">Храните данные карты в безопаÑноÑти</translation>
<translation id="1292571435393770077">Лоток 16</translation>
<translation id="1292701964462482250">"ПО, уÑтановленное на компьютере, не позволÑет Chrome безопаÑно подключитьÑÑ Ðº Интернету" (только Ð´Ð»Ñ Windows)</translation>
<translation id="1294154142200295408">Модификации Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð¹ Ñтроки</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Чтобы уÑтранить проблему, нажмите &lt;strong&gt;ПодключитьÑÑ&lt;/strong&gt; на нужной веб-Ñтранице.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Ландшафтный дизайн</translation>
<translation id="1513706915089223971">СпиÑок запиÑей из иÑтории</translation>
+<translation id="1516097932025103760">Данные карты шифруютÑÑ Ð¸ хранÑÑ‚ÑÑ Ð² безопаÑноÑти. CVC-код никогда не запиÑываетÑÑ.</translation>
<translation id="1517433312004943670">Укажите номер телефона</translation>
<translation id="1519264250979466059">Дата Ñборки</translation>
<translation id="1521159554480556801">Ðрт-квилтинг и текÑтильное иÑкуÑÑтво</translation>
@@ -272,7 +275,7 @@
<translation id="1634828734222219955">Ð’Ñего</translation>
<translation id="163669211644121865">РаÑчет и планирование налогов</translation>
<translation id="1638780421120290329">Ðе удалоÑÑŒ Ñохранить карту</translation>
-<translation id="1639239467298939599">Загрузка</translation>
+<translation id="1639239467298939599">Загрузка…</translation>
<translation id="1640180200866533862">ПользовательÑкие правила</translation>
<translation id="1640244768702815859">Попробуйте <ph name="BEGIN_LINK" />открыть главную Ñтраницу Ñайта<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">Задержать вывод результатов</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">СохранÑÑ‚ÑŒ и автоматичеÑки подÑтавлÑÑ‚ÑŒ платежные данные</translation>
<translation id="1663943134801823270">Это карты и адреÑа, указанные в Chrome. Ð’Ñ‹ можете изменить их на Ñтранице <ph name="BEGIN_LINK" />ÐаÑтройки<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Страницы на Ñтом Ñзыке (<ph name="SOURCE_LANGUAGE" />) будут автоматичеÑки переводитьÑÑ Ð½Ð° <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">ВоÑпользоватьÑÑ Ñпециальным предложением можно через <ph name="CARD_DETAIL" />.</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> – <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ÐÐ°Ñ‡Ð¸Ð½Ð°Ñ Ñ ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¾Ð³Ð¾ краÑ</translation>
<translation id="168693727862418163">Такое значение правила не предуÑмотрено и будет проигнорировано.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Датчики движениÑ</translation>
<translation id="1717494416764505390">Почтовый Ñщик 3</translation>
<translation id="1718029547804390981">ÐедоÑтупно, так как документ Ñлишком большой</translation>
+<translation id="1720941539803966190">Закрыть руководÑтво</translation>
<translation id="1721424275792716183">*ОбÑзательное поле</translation>
<translation id="1727613060316725209">ДейÑтвительный Ñертификат</translation>
<translation id="1727741090716970331">Введите правильный номер карты</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">Барбекю и гриль</translation>
<translation id="2053111141626950936">Страницы на Ñтом Ñзыке (<ph name="LANGUAGE" />) не будут переводитьÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ЕÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° и активирована, Chrome определÑет по вашим недавним дейÑтвиÑм в браузере, к какой группе людей (когорте) Ð²Ð°Ñ Ð¾Ñ‚Ð½ÐµÑти. Рекламодатели могут выбрать объÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтой группы. Данные о ваших дейÑтвиÑÑ… в браузере хранÑÑ‚ÑÑ Ð½Ð° уÑтройÑтве и доÑтупны только вам. Группа обновлÑетÑÑ ÐµÐ¶ÐµÐ´Ð½ÐµÐ²Ð½Ð¾.}=1{ЕÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° и активирована, Chrome определÑет по вашим недавним дейÑтвиÑм в браузере, к какой группе людей (когорте) Ð²Ð°Ñ Ð¾Ñ‚Ð½ÐµÑти. Рекламодатели могут выбрать объÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтой группы. Данные о ваших дейÑтвиÑÑ… в браузере хранÑÑ‚ÑÑ Ð½Ð° уÑтройÑтве и доÑтупны только вам. Группа обновлÑетÑÑ ÐµÐ¶ÐµÐ´Ð½ÐµÐ²Ð½Ð¾.}one{ЕÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° и активирована, Chrome определÑет по вашим недавним дейÑтвиÑм в браузере, к какой группе людей (когорте) Ð²Ð°Ñ Ð¾Ñ‚Ð½ÐµÑти. Рекламодатели могут выбрать объÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтой группы. Данные о ваших дейÑтвиÑÑ… в браузере хранÑÑ‚ÑÑ Ð½Ð° уÑтройÑтве и доÑтупны только вам. Группа обновлÑетÑÑ ÐºÐ°Ð¶Ð´Ñ‹Ðµ {NUM_DAYS} день.}few{ЕÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° и активирована, Chrome определÑет по вашим недавним дейÑтвиÑм в браузере, к какой группе людей (когорте) Ð²Ð°Ñ Ð¾Ñ‚Ð½ÐµÑти. Рекламодатели могут выбрать объÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтой группы. Данные о ваших дейÑтвиÑÑ… в браузере хранÑÑ‚ÑÑ Ð½Ð° уÑтройÑтве и доÑтупны только вам. Группа обновлÑетÑÑ ÐºÐ°Ð¶Ð´Ñ‹Ðµ {NUM_DAYS} днÑ.}many{ЕÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° и активирована, Chrome определÑет по вашим недавним дейÑтвиÑм в браузере, к какой группе людей (когорте) Ð²Ð°Ñ Ð¾Ñ‚Ð½ÐµÑти. Рекламодатели могут выбрать объÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтой группы. Данные о ваших дейÑтвиÑÑ… в браузере хранÑÑ‚ÑÑ Ð½Ð° уÑтройÑтве и доÑтупны только вам. Группа обновлÑетÑÑ ÐºÐ°Ð¶Ð´Ñ‹Ðµ {NUM_DAYS} дней.}other{ЕÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° и активирована, Chrome определÑет по вашим недавним дейÑтвиÑм в браузере, к какой группе людей (когорте) Ð²Ð°Ñ Ð¾Ñ‚Ð½ÐµÑти. Рекламодатели могут выбрать объÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтой группы. Данные о ваших дейÑтвиÑÑ… в браузере хранÑÑ‚ÑÑ Ð½Ð° уÑтройÑтве и доÑтупны только вам. Группа обновлÑетÑÑ ÐºÐ°Ð¶Ð´Ñ‹Ðµ {NUM_DAYS} днÑ.}}</translation>
-<translation id="2053553514270667976">Почтовый индекÑ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 вариант}one{# вариант}few{# варианта}many{# вариантов}other{# варианта}}</translation>
+<translation id="2066915425250589881">запроÑить ее удаление</translation>
<translation id="2068528718802935086">Младенцы и маленькие дети</translation>
<translation id="2071156619270205202">Указанный номер Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать Ð´Ð»Ñ Ð²Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð¾Ð¹ карты.</translation>
<translation id="2071692954027939183">Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½Ñ‹ автоматичеÑки, потому что вы обычно запрещаете их показ.</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">Кнопка "ÐаÑтройки Ñинхронизации". Ðажмите Ввод, чтобы открыть наÑтройки Chrome и указать, ÐºÐ°ÐºÐ°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° ÑинхронизироватьÑÑ.</translation>
<translation id="2091887806945687916">Звук</translation>
<translation id="2094505752054353250">ÐеÑоответÑтвие домена</translation>
-<translation id="2096368010154057602">Департамент</translation>
<translation id="2099652385553570808">Три Ñкобы Ñлева</translation>
<translation id="2101225219012730419">ВерÑиÑ:</translation>
<translation id="2102134110707549001">Сгенерировать надежный пароль</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">ÐмериканÑкий футбол</translation>
<translation id="2187317261103489799">ОпределÑÑ‚ÑŒ (по умолчанию)</translation>
<translation id="2188375229972301266">ÐеÑколько отверÑтий Ñнизу</translation>
-<translation id="2188852899391513400">Пароль, который вы только что иÑпользовали, был раÑкрыт в результате утечки данных. Чтобы защитить Ñвои аккаунты, измените его прÑмо ÑÐµÐ¹Ñ‡Ð°Ñ Ð¸ проверьте Ñохраненные пароли в ДиÑпетчере паролей Google.</translation>
<translation id="219906046732893612">Ремонт дома</translation>
<translation id="2202020181578195191">ÐедопуÑтимый формат года.</translation>
<translation id="22081806969704220">Лоток 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">Редактирование файлов</translation>
<translation id="2215963164070968490">Собаки</translation>
<translation id="2218879909401188352">Злоумышленники могут иÑпользовать Ñайт <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, чтобы уÑтановить опаÑные приложениÑ, которые причинÑÑŽÑ‚ вред уÑтройÑтвам, увеличивают раÑходы на мобильную ÑвÑзь и крадут личные данные. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Открыть руководÑтво</translation>
+<translation id="2219735899272417925">требуетÑÑ ÑброÑить наÑтройки уÑтройÑтва</translation>
<translation id="2224337661447660594">Ðет Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº Интернету</translation>
<translation id="2230458221926704099">Чтобы уÑтранить неполадки, проведите <ph name="BEGIN_LINK" />диагноÑтику<ph name="END_LINK" /> подключениÑ.</translation>
<translation id="2239100178324503013">Отправить</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">Запрещено (по умолчанию)</translation>
<translation id="2512413427717747692">Кнопка "Сделать Chrome браузером по умолчанию". Ðажмите Ввод, чтобы Ñделать Chrome браузером по умолчанию в наÑтройках iOS.</translation>
<translation id="2515629240566999685">Проверьте уровень Ñигнала Ñети.</translation>
+<translation id="2515761554693942801">Ð’Ñ‹ выбрали Touch ID Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿Ð¾ÐºÑƒÐ¿Ð¾Ðº на Ñайтах, пользующихÑÑ ÑƒÑлугами поÑтавщика <ph name="PROVIDER_ORIGIN" />. Он мог Ñохранить информацию о вашем ÑпоÑобе оплаты. У Ð²Ð°Ñ ÐµÑÑ‚ÑŒ право <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Скоба в правом нижнем углу</translation>
<translation id="2521736961081452453">Создать форму</translation>
<translation id="2523886232349826891">Карта будет Ñохранена только на Ñтом уÑтройÑтве</translation>
<translation id="2524461107774643265">Укажите дополнительную информацию</translation>
<translation id="2529899080962247600">МакÑимальное чиÑло запиÑей в Ñтом поле: <ph name="MAX_ITEMS_LIMIT" />. ОÑтальные запиÑи будут игнорироватьÑÑ.</translation>
+<translation id="253493526287553278">ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ промокоде</translation>
<translation id="2535585790302968248">Открыть новую вкладку в режиме инкогнито</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{и ещё 1}one{и ещё #}few{и ещё #}many{и ещё #}other{и ещё #}}</translation>
<translation id="2536110899380797252">Добавить адреÑ</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">Ð¤Ð¾Ñ‚Ð¾Ð³Ñ€Ð°Ñ„Ð¸Ñ Ð¸ цифровые иÑкуÑÑтва</translation>
<translation id="2601150049980261779">РомантичеÑкие фильмы</translation>
<translation id="2604589665489080024">Поп-музыка</translation>
-<translation id="2609632851001447353">Варианты</translation>
<translation id="2610561535971892504">Ðажмите, чтобы Ñкопировать</translation>
<translation id="2617988307566202237">Ð’ Chrome <ph name="BEGIN_EMPHASIS" />не будет ÑохранÑÑ‚ÑŒÑÑ<ph name="END_EMPHASIS" /> ÑÐ»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Дни Ñ€Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¸ именины</translation>
<translation id="2677748264148917807">Закрыть</translation>
+<translation id="2679714844901977852">Сохраните карту и платежную информацию в аккаунте Google (<ph name="USER_EMAIL" />) Ð´Ð»Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñной и более быÑтрой оплаты покупок.</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Оптимальный вариант</translation>
<translation id="2688969097326701645">Да, продолжить</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">Интернет-провайдеры</translation>
<translation id="2856444702002559011">Злоумышленники могут пытатьÑÑ Ð¿Ð¾Ñ…Ð¸Ñ‚Ð¸Ñ‚ÑŒ ваши данные Ñ Ñайта <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (например, пароли, ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ номера банковÑких карт). <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Этот Ñайт показывает навÑзчивую или вводÑщую в заблуждение рекламу.</translation>
-<translation id="286512204874376891">Чтобы защитить ваши данные от мошенников, вмеÑто наÑтоÑщей карты будет иÑпользоватьÑÑ Ð²Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð°Ñ. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Дружелюбный</translation>
<translation id="28761159517501904">Фильмы</translation>
<translation id="2876489322757410363">Вы выйдете из режима инкогнито, чтобы выполнить оплату во внешнем приложении. Продолжить?</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">ДиÑк</translation>
<translation id="3162559335345991374">Возможно, вам нужно перейти на Ñтраницу входа Ñети Wi-Fi.</translation>
<translation id="3169472444629675720">Рекомендации</translation>
-<translation id="3174168572213147020">ОÑтров</translation>
<translation id="3176929007561373547">Проверьте наÑтройки прокÑи-Ñервера или попроÑите админиÑтратора
задать верные параметры. Ð’ противном Ñлучае^
<ph name="PLATFORM_TEXT" /></translation>
@@ -876,9 +881,6 @@
<translation id="3369192424181595722">Ошибка чаÑов</translation>
<translation id="3369459162151165748">ЗапаÑные чаÑти и акÑеÑÑуары Ð´Ð»Ñ Ñ‚Ñ€Ð°Ð½Ñпортных ÑредÑтв</translation>
<translation id="3371064404604898522">Сделать Chrome браузером по умолчанию</translation>
-<translation id="3371076217486966826"><ph name="URL" /> запрашивает разрешение на:
- • Создание 3D-карты меÑта, в котором вы находитеÑÑŒ, и отÑлеживание Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ÐºÐ°Ð¼ÐµÑ€Ñ‹.
- • ИÑпользование камеры.</translation>
<translation id="337363190475750230">Отключен</translation>
<translation id="3375754925484257129">ЗапуÑтить проверку безопаÑноÑти</translation>
<translation id="3377144306166885718">Этот Ñервер иÑпользует уÑтаревшую верÑию протокола TLS.</translation>
@@ -895,6 +897,7 @@
<translation id="3399952811970034796">ÐÐ´Ñ€ÐµÑ Ð´Ð¾Ñтавки</translation>
<translation id="3402261774528610252">Сайт иÑпользует уÑтаревший протокол TLS 1.0 или TLS 1.1. Ð’ будущем Ñти протоколы будут отключены, из-за чего пользователи не Ñмогут зайти на Ñайт. Ðа Ñервере нужно включить протокол TLS 1.2 или более поздней верÑии.</translation>
<translation id="3405664148539009465">ÐаÑтроить шрифты</translation>
+<translation id="3407789382767355356">вход через Ñторонние ÑервиÑÑ‹</translation>
<translation id="3409896703495473338">Открыть наÑтройки безопаÑноÑти</translation>
<translation id="3414952576877147120">Размер:</translation>
<translation id="3417660076059365994">Загруженные или прикрепленные вами файлы передаютÑÑ Ð½Ð° проверку в Google Cloud или Ñторонние ÑервиÑÑ‹. Ð’ чаÑтноÑти, файлы могут быть проÑканированы на наличие конфиденциальных данных или вредоноÑного ПО.</translation>
@@ -927,6 +930,7 @@
<translation id="3477679029130949506">Кино- и театральные афиши</translation>
<translation id="3479552764303398839">Ðе ÑейчаÑ</translation>
<translation id="3484560055331845446">Ð’Ñ‹ можете потерÑÑ‚ÑŒ доÑтуп к аккаунту Google. Рекомендуем немедленно Ñменить пароль. ПоÑле Ñтого потребуетÑÑ Ñнова войти в аккаунт.</translation>
+<translation id="3484861421501147767">Ðапоминание: вам доÑтупен Ñохраненный промокод</translation>
<translation id="3487845404393360112">Лоток 4</translation>
<translation id="3495081129428749620">Ðайти на Ñтранице
"<ph name="PAGE_TITLE" />"</translation>
@@ -1051,6 +1055,7 @@
<translation id="3810973564298564668">Управление</translation>
<translation id="3816482573645936981">Значение (заменÑемое)</translation>
<translation id="382518646247711829">ЕÑли вы иÑпользуете прокÑи-Ñервер...</translation>
+<translation id="3826050100957962900">ÐŸÑ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð²Ð¾Ð¹Ñ‚Ð¸ в аккаунт от третьих Ñторон</translation>
<translation id="3827112369919217609">ÐбÑолютный</translation>
<translation id="3827666161959873541">Семейные фильмы</translation>
<translation id="3828924085048779000">ПуÑтые кодовые фразы запрещены.</translation>
@@ -1063,9 +1068,9 @@
<translation id="3858027520442213535">Обновить дату и времÑ</translation>
<translation id="3858860766373142691">Ðазвание</translation>
<translation id="3872834068356954457">Ðаука</translation>
+<translation id="3875783148670536197">Показать</translation>
<translation id="3881478300875776315">Показать меньше Ñтрок</translation>
<translation id="3884278016824448484">Конфликт идентификаторов уÑтройÑтв</translation>
-<translation id="3885155851504623709">Округ</translation>
<translation id="388632593194507180">Соединение отÑлеживаетÑÑ</translation>
<translation id="3886948180919384617">Укладчик 3</translation>
<translation id="3890664840433101773">Добавление адреÑа Ñлектронной почты</translation>
@@ -1095,9 +1100,11 @@
<translation id="3973234410852337861">Сайт <ph name="HOST_NAME" /> заблокирован</translation>
<translation id="3978338123949022456">Чтобы выполнить поиÑк по ключевому Ñлову "<ph name="KEYWORD_SUFFIX" />", запуÑтите режим поиÑка, введите Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸ нажмите Ввод.</translation>
<translation id="398470910934384994">Птицы</translation>
+<translation id="3985750352229496475">ÐаÑтроить адреÑа</translation>
<translation id="3986705137476756801">Отключить автоматичеÑкие Ñубтитры</translation>
<translation id="3987940399970879459">Менее 1 МБ</translation>
<translation id="3990250421422698716">Смещение</translation>
+<translation id="3992684624889376114">Об Ñтой Ñтранице</translation>
<translation id="3996311196211510766">Сайт <ph name="ORIGIN" />, на который вы переходите, требует применÑÑ‚ÑŒ правило иÑточника ко вÑем запроÑам, однако Ñто ÑÐµÐ¹Ñ‡Ð°Ñ Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾.</translation>
<translation id="4006465311664329701">СпоÑобы оплаты, адреÑа и Ñпециальные Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð· Google Pay</translation>
<translation id="4009243425692662128">Содержание Ñтраниц, которые вы печатаете, отправлÑетÑÑ Ð² Google Cloud или третьим лицам Ð´Ð»Ñ Ð°Ð½Ð°Ð»Ð¸Ð·Ð°. Ð’ чаÑтноÑти, оно может быть проÑканировано на наличие конфиденциальных данных.</translation>
@@ -1217,6 +1224,7 @@
<translation id="4305666528087210886">Ðе удалоÑÑŒ получить доÑтуп к файлу</translation>
<translation id="4306529830550717874">Сохранить адреÑ?</translation>
<translation id="4306812610847412719">Буфер обмена</translation>
+<translation id="4310070645992025887">ПоиÑк по теме</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Блокировать (по умолчанию)</translation>
<translation id="4314815835985389558">ÐаÑтройки Ñинхронизации</translation>
@@ -1267,6 +1275,7 @@
<translation id="4435702339979719576">Открытка</translation>
<translation id="443673843213245140">ПрокÑи-Ñервер отключен, но при Ñтом его ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð·Ð°Ð´Ð°Ð½Ð° Ñвным образом.</translation>
<translation id="4441832193888514600">ИгнорируетÑÑ, так как правило может быть наÑтроено только на уровне облака.</translation>
+<translation id="4442470707340296952">Вкладки Chrome</translation>
<translation id="4450893287417543264">Больше не показывать</translation>
<translation id="4451135742916150903">Разрешено отправлÑÑ‚ÑŒ Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° подключение к HID-уÑтройÑтвам</translation>
<translation id="4452328064229197696">Пароль, который вы только что иÑпользовали, был раÑкрыт в результате утечки данных. Чтобы защитить Ñвои аккаунты, проверьте Ñохраненные пароли в ДиÑпетчере паролей Google.</translation>
@@ -1405,6 +1414,7 @@
<translation id="483241715238664915">Включить предупреждениÑ</translation>
<translation id="4834250788637067901">СпоÑобы оплаты, адреÑа и Ñпециальные Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð· Google Pay</translation>
<translation id="4838327282952368871">Сказочный</translation>
+<translation id="4839087176073128681">БыÑтрее оплачивайте покупки и защищайте данные карты Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñовременных технологий безопаÑноÑти Google.</translation>
<translation id="4840250757394056958">ПоÑмотреть иÑторию Chrome</translation>
<translation id="484462545196658690">Выбрать автоматичеÑки</translation>
<translation id="484671803914931257">Получите Ñкидку в магазине "<ph name="MERCHANT_NAME" />" и у других продавцов</translation>
@@ -1467,6 +1477,7 @@
<translation id="5011561501798487822">Обнаружен Ñзык</translation>
<translation id="5015510746216210676">Ð˜Ð¼Ñ ÐºÐ¾Ð¼Ð¿ÑŒÑŽÑ‚ÐµÑ€Ð°:</translation>
<translation id="5017554619425969104">Скопированный текÑÑ‚</translation>
+<translation id="5017828934289857214">Ðапомнить позже</translation>
<translation id="5018422839182700155">Ðе удалоÑÑŒ открыть Ñтраницу</translation>
<translation id="5019198164206649151">Данные в хранилище повреждены</translation>
<translation id="5020776957610079374">ЭтничеÑÐºÐ°Ñ Ð¼ÑƒÐ·Ñ‹ÐºÐ°</translation>
@@ -1486,6 +1497,7 @@
<translation id="5051305769747448211">Комедийные выÑтуплениÑ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Чтобы отправить Ñтот файл Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ функции "Обмен Ñ Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸ÐµÐ¼", оÑвободите меÑто (<ph name="DISK_SPACE_SIZE" />) на Ñвоем уÑтройÑтве.}one{Чтобы отправить Ñти файлы Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ функции "Обмен Ñ Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸ÐµÐ¼", оÑвободите меÑто (<ph name="DISK_SPACE_SIZE" />) на Ñвоем уÑтройÑтве.}few{Чтобы отправить Ñти файлы Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ функции "Обмен Ñ Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸ÐµÐ¼", оÑвободите меÑто (<ph name="DISK_SPACE_SIZE" />) на Ñвоем уÑтройÑтве.}many{Чтобы отправить Ñти файлы Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ функции "Обмен Ñ Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸ÐµÐ¼", оÑвободите меÑто (<ph name="DISK_SPACE_SIZE" />) на Ñвоем уÑтройÑтве.}other{Чтобы отправить Ñти файлы Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ функции "Обмен Ñ Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸ÐµÐ¼", оÑвободите меÑто (<ph name="DISK_SPACE_SIZE" />) на Ñвоем уÑтройÑтве.}}</translation>
<translation id="5056549851600133418">Статьи Ð´Ð»Ñ Ð²Ð°Ñ</translation>
+<translation id="5060483733937416656">Ð’Ñ‹ выбрали Windows Hello Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿Ð¾ÐºÑƒÐ¿Ð¾Ðº на Ñайтах, пользующихÑÑ ÑƒÑлугами поÑтавщика <ph name="PROVIDER_ORIGIN" />. Он мог Ñохранить информацию о вашем ÑпоÑобе оплаты. У Ð²Ð°Ñ ÐµÑÑ‚ÑŒ право <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Вам нужен домен <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">ИÑторию печати.</translation>
<translation id="5068234115460527047">Хедж-фонды</translation>
@@ -1499,10 +1511,8 @@
<translation id="5087286274860437796">Сертификат Ñервера не дейÑтвителен в наÑтоÑщее времÑ.</translation>
<translation id="5087580092889165836">Добавить карту</translation>
<translation id="5088142053160410913">Сообщение оператору</translation>
-<translation id="5089810972385038852">Штат</translation>
<translation id="5093232627742069661">Фальцовка гармошкой в два Ñгиба</translation>
<translation id="5094747076828555589">Ðе удалоÑÑŒ подтвердить, что Ñто Ñервер <ph name="DOMAIN" />. Chromium не доверÑет его Ñертификату безопаÑноÑти. Возможно, Ñервер наÑтроен неправильно или кто-то пытаетÑÑ Ð¿ÐµÑ€ÐµÑ…Ð²Ð°Ñ‚Ð¸Ñ‚ÑŒ ваши данные.</translation>
-<translation id="5095208057601539847">ПровинциÑ</translation>
<translation id="5097099694988056070">ÑтатиÑтичеÑкие данные уÑтройÑтва, например об иÑпользовании процеÑÑора или оперативной памÑти;</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Сайт не защищен</translation>
@@ -1540,6 +1550,7 @@
<translation id="5171045022955879922">Введите Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸Ð»Ð¸ URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Локальный компьютер</translation>
+<translation id="5177076414499237632">Подробнее об иÑточнике и теме Ñтой Ñтраницы</translation>
<translation id="5179510805599951267">Это не <ph name="ORIGINAL_LANGUAGE" />? Сообщите об ошибке</translation>
<translation id="518639307526414276">Корм и товары Ð´Ð»Ñ Ð´Ð¾Ð¼Ð°ÑˆÐ½Ð¸Ñ… животных</translation>
<translation id="5190835502935405962">Панель закладок</translation>
@@ -1700,6 +1711,7 @@
<translation id="5624120631404540903">ÐаÑтройки паролей</translation>
<translation id="5629630648637658800">Ðе удалоÑÑŒ применить наÑтройки политики</translation>
<translation id="5631439013527180824">Токен уÑтройÑтва недейÑтвителен</translation>
+<translation id="5632485077360054581">Показать</translation>
<translation id="5633066919399395251">Сайт <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> может уÑтановить на ваш компьютер вредоноÑное ПО, которое крадет или удалÑет личную информацию (например, фотографии, пароли, ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ реквизиты банковÑких карт). <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">МошенничеÑкий контент заблокирован</translation>
<translation id="5633259641094592098">ÐÑ€Ñ‚Ñ…Ð°ÑƒÑ Ð¸ незавиÑимое кино</translation>
@@ -1817,6 +1829,7 @@
<translation id="5989320800837274978">Ðи фикÑированные прокÑи-Ñерверы, ни URL PAC-Ñкриптов не указаны.</translation>
<translation id="5992691462791905444">Ð£ÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ð°Ñ Ñ„Ð°Ð»ÑŒÑ†Ð¾Ð²ÐºÐ° гармошкой</translation>
<translation id="5995727681868049093">ÐаÑтроить параметры конфиденциальноÑти и безопаÑноÑти в аккаунте Google</translation>
+<translation id="5997247540087773573">Пароль, который вы только что иÑпользовали, был раÑкрыт в результате утечки данных. Чтобы защитить Ñвои аккаунты, измените его прÑмо ÑÐµÐ¹Ñ‡Ð°Ñ Ð¸ проверьте Ñохраненные пароли в Google Менеджере паролей.</translation>
<translation id="6000758707621254961">КоличеÑтво результатов поиÑка по запроÑу "<ph name="SEARCH_TEXT" />": <ph name="RESULT_COUNT" /></translation>
<translation id="6006484371116297560">КлаÑÑичеÑкаÑ</translation>
<translation id="6008122969617370890">Ð’ порÑдке от N до 1</translation>
@@ -1912,7 +1925,6 @@
<translation id="627746635834430766">Чтобы уÑкорить процеÑÑ Ð¾Ð¿Ð»Ð°Ñ‚Ñ‹ в будущем, Ñохраните карту и платежный Ð°Ð´Ñ€ÐµÑ Ð² аккаунте Google.</translation>
<translation id="6279183038361895380">Чтобы показать курÑор, нажмите |<ph name="ACCELERATOR" />|</translation>
<translation id="6280223929691119688">Ðевозможно доÑтавить заказ по Ñтому адреÑу. Выберите другой вариант.</translation>
-<translation id="6282194474023008486">Почтовый индекÑ</translation>
<translation id="6285507000506177184">Кнопка "УправлÑÑ‚ÑŒ Ñкачанными файлами в Chrome". Ðажмите Ввод, чтобы управлÑÑ‚ÑŒ Ñкачанными файлами в Chrome.</translation>
<translation id="6289939620939689042">Цвет Ñтраницы</translation>
<translation id="6290238015253830360">ЗдеÑÑŒ поÑвÑÑ‚ÑÑ Ñ€ÐµÐºÐ¾Ð¼ÐµÐ½Ð´ÑƒÐµÐ¼Ñ‹Ðµ Ñтатьи.</translation>
@@ -1934,6 +1946,7 @@
<translation id="6337133576188860026">ОÑвободитÑÑ Ð¼ÐµÐ½ÐµÐµ <ph name="SIZE" /> проÑтранÑтва. ПоÑле Ñтого некоторые веб-Ñтраницы могут загружатьÑÑ Ð´Ð¾Ð»ÑŒÑˆÐµ обычного.</translation>
<translation id="6337534724793800597">Фильтровать правила по названию</translation>
<translation id="6340739886198108203">ÐдминиÑтратор не рекомендует делать Ñнимки и видеозапиÑи Ñкрана, когда на нем показаны конфиденциальные данные:</translation>
+<translation id="6348220984832452017">Текущие изменениÑ</translation>
<translation id="6349101878882523185">УÑтановить приложение "<ph name="APP_NAME" />"</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ðет}=1{1 пароль (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронизировано)}=2{2 Ð¿Ð°Ñ€Ð¾Ð»Ñ (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронизировано)}one{# пароль (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронизировано)}few{# Ð¿Ð°Ñ€Ð¾Ð»Ñ (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронизировано)}many{# паролей (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронизировано)}other{# Ð¿Ð°Ñ€Ð¾Ð»Ñ (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронизировано)}}</translation>
<translation id="6355392890578844978">ÐšÐ¾Ð¼Ð¿Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ Ð¾Ñ€Ð³Ð°Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½Ðµ управлÑет Ñтим браузером. ДейÑтвиÑми на Ñтом уÑтройÑтве можно управлÑÑ‚ÑŒ вне браузера Chromium. <ph name="BEGIN_LINK" />Подробнее…<ph name="END_LINK" /></translation>
@@ -1965,7 +1978,6 @@
<translation id="643051589346665201">Изменить пароль Google</translation>
<translation id="6433490469411711332">Изменить контактную информацию</translation>
<translation id="6433595998831338502">Сайт <ph name="HOST_NAME" /> не позволÑет уÑтановить Ñоединение.</translation>
-<translation id="6438025220577812695">Изменить вручную</translation>
<translation id="6440503408713884761">ПропуÑкаетÑÑ</translation>
<translation id="6443406338865242315">УÑтановленные раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ Ð¸ плагины.</translation>
<translation id="6446163441502663861">Kahu (конверт)</translation>
@@ -2095,9 +2107,9 @@
<translation id="6828866289116430505">Генетика</translation>
<translation id="6831043979455480757">ПеревеÑти</translation>
<translation id="6833752742582340615">Сохранив данные банковÑкой карты и платежную информацию в Ñвоем аккаунте Google, вы Ñможете оплачивать покупки быÑтрее. Это безопаÑно.</translation>
-<translation id="6839929833149231406">Район</translation>
<translation id="6846340164947227603">ИÑпользовать номер виртуальной карты</translation>
<translation id="6852204201400771460">ПерезапуÑтить приложение?</translation>
+<translation id="6857776781123259569">ÐаÑтроить пароли</translation>
<translation id="686485648936420384">РеÑурÑÑ‹ Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»ÐµÐ¹</translation>
<translation id="6865412394715372076">Ðевозможно подтвердить карту.</translation>
<translation id="6869334554832814367">ПотребительÑкие кредиты</translation>
@@ -2146,7 +2158,6 @@
<translation id="6965978654500191972">УÑтройÑтво</translation>
<translation id="696703987787944103">Перцепционный</translation>
<translation id="6968269510885595029">ИÑпользуйте Ñлектронный ключ.</translation>
-<translation id="6970216967273061347">Округ</translation>
<translation id="6971439137020188025">БыÑтро Ñоздать презентацию Google</translation>
<translation id="6972629891077993081">УÑтройÑтва HID</translation>
<translation id="6973656660372572881">Указаны как фикÑированные прокÑи-Ñерверы, так и URL PAC-Ñкриптов.</translation>
@@ -2185,7 +2196,6 @@
<translation id="7081308185095828845">Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð½ÐµÐ´Ð¾Ñтупна на вашем уÑтройÑтве</translation>
<translation id="7083258188081898530">Лоток 9</translation>
<translation id="7086090958708083563">Загрузка запрошена пользователем</translation>
-<translation id="7087282848513945231">Округ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. Ðажмите Tab, а затем – Ввод, чтобы открыть наÑтройки Chrome и задать Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñайтов, а также указать, какие данные они могут хранить.</translation>
<translation id="7096937462164235847">ПодлинноÑÑ‚ÑŒ Ñтого Ñайта не подтверждена.</translation>
<translation id="7101893872976785596">Фильмы ужаÑов</translation>
@@ -2204,10 +2214,10 @@
<ph name="LIST_ITEM" />данные, указанные в формах.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Ðевозможно отправить заказ по Ñтому адреÑу. Выберите другой вариант.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ÐдминиÑтратор запретил копирование Ñтих данных.</translation>
<translation id="7135130955892390533">Показать ÑтатуÑ</translation>
<translation id="7138472120740807366">СпоÑоб доÑтавки</translation>
-<translation id="7139724024395191329">Эмират</translation>
<translation id="7139892792842608322">ОÑновной лоток</translation>
<translation id="714064300541049402">Смещение изображений на оборотной Ñтороне по оÑи X</translation>
<translation id="7152423860607593928">Number-14 (конверт)</translation>
@@ -2424,6 +2434,7 @@
<translation id="7669271284792375604">ПоÑещение Ñтого Ñайта может привеÑти к уÑтановке вредоноÑного ПО, которое будет мешать работе браузера (например, менÑÑ Ñтартовую Ñтраницу или Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½ÑƒÑŽ рекламу на Ñайтах).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ДейÑÑ‚Ð²Ð¸Ñ Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸, помеченными как конфиденциальные (Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° входа обнаружено 1 дейÑтвие). <ph name="BEGIN_LINK" />Подробнее…<ph name="END_LINK" />}one{ДейÑÑ‚Ð²Ð¸Ñ Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸, помеченными как конфиденциальные (Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° входа обнаружено # дейÑтвие). <ph name="BEGIN_LINK" />Подробнее…<ph name="END_LINK" />}few{ДейÑÑ‚Ð²Ð¸Ñ Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸, помеченными как конфиденциальные (Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° входа обнаружено # дейÑтвиÑ). <ph name="BEGIN_LINK" />Подробнее…<ph name="END_LINK" />}many{ДейÑÑ‚Ð²Ð¸Ñ Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸, помеченными как конфиденциальные (Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° входа обнаружено # дейÑтвий). <ph name="BEGIN_LINK" />Подробнее…<ph name="END_LINK" />}other{ДейÑÑ‚Ð²Ð¸Ñ Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸, помеченными как конфиденциальные (Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° входа обнаружено # дейÑтвиÑ). <ph name="BEGIN_LINK" />Подробнее…<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Почтовый Ñщик 6</translation>
+<translation id="7675325315208090829">ÐаÑтроить ÑпоÑобы оплаты</translation>
<translation id="7676643023259824263">ПоиÑк по текÑту "<ph name="TEXT" />" из буфера обмена</translation>
<translation id="7679367271685653708">ПоÑмотреть иÑторию браузера в наÑтройках Chrome и управлÑÑ‚ÑŒ ею</translation>
<translation id="7679947978757153706">БейÑбол</translation>
@@ -2466,7 +2477,6 @@
<translation id="7766518757692125295">Юбка</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Ð’ том же порÑдке лицевой Ñтороной вверх</translation>
-<translation id="777702478322588152">Префектура</translation>
<translation id="7791011319128895129">Ðе выпущено</translation>
<translation id="7791196057686275387">ПреÑÑование</translation>
<translation id="7791543448312431591">Добавить</translation>
@@ -2557,12 +2567,12 @@
<translation id="8055534648776115597">ПрофеÑÑиональное образование и повышение квалификации</translation>
<translation id="8057711352706143257">Программа "<ph name="SOFTWARE_NAME" />" наÑтроена неправильно. Чтобы уÑтранить проблему, удалите программу "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ПроизводÑтво пищевых продуктов</translation>
-<translation id="8066955247577885446">Произошла ошибка.</translation>
<translation id="8067872629359326442">Ð’Ñ‹ только что ввели пароль на поддельном Ñайте. Чтобы изменить пароль и Ñообщить Google о возможной угрозе безопаÑноÑти, нажмите "Защитить аккаунт".</translation>
<translation id="8070439594494267500">Значок приложениÑ</translation>
<translation id="8074253406171541171">10x13 (конверт)</translation>
<translation id="8075736640322370409">БыÑтро Ñоздать таблицу Google</translation>
<translation id="8075898834294118863">Открыть наÑтройки Ñайтов</translation>
+<translation id="8076492880354921740">Вкладки</translation>
<translation id="8078141288243656252">ÐедоÑтупно, когда документ повернут</translation>
<translation id="8079031581361219619">Перезагрузить Ñайт?</translation>
<translation id="8081087320434522107">Седаны</translation>
@@ -2685,6 +2695,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ÐаÑтройки</translation>
+<translation id="8428634594422941299">ОК</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. Ðажмите Tab, а затем – Ввод, чтобы задать параметры файлов cookie в наÑтройках Chrome.</translation>
<translation id="8433057134996913067">Ðа большинÑтве Ñайтов будет выполнен выход из аккаунта.</translation>
<translation id="8434840396568290395">Домашние животные</translation>
@@ -2782,6 +2793,7 @@
<translation id="8742371904523228557">Ваш код Ð´Ð»Ñ ÑервиÑа <ph name="ORIGIN" />: <ph name="ONE_TIME_CODE" />.</translation>
<translation id="874918643257405732">Добавить Ñтраницу в закладки</translation>
<translation id="8751426954251315517">Повторите попытку позже</translation>
+<translation id="8757526089434340176">ДоÑтупно Ñпециальное предложение Google Pay</translation>
<translation id="8758885506338294482">КиберÑпорт</translation>
<translation id="8759274551635299824">Срок дейÑÑ‚Ð²Ð¸Ñ ÐºÐ°Ñ€Ñ‚Ñ‹ иÑтек.</translation>
<translation id="87601671197631245">ÐаÑтройки безопаÑноÑти Ñтого Ñайта уÑтарели. Злоумышленники могут получить доÑтуп к вашим данным (например, паролÑм, ÑообщениÑм и номерам банковÑких карт) при их отправке на Ñтот Ñайт.</translation>
@@ -2789,6 +2801,7 @@
<translation id="8763927697961133303">USB-уÑтройÑтво</translation>
<translation id="8763986294015493060">Закрыть вÑе окна, открытые в режиме инкогнито</translation>
<translation id="8766943070169463815">Открыт Ñкран проверки учетных данных Ð´Ð»Ñ Ð·Ð°Ñ‰Ð¸Ñ‰ÐµÐ½Ð½Ñ‹Ñ… платежей</translation>
+<translation id="8767765348545497220">Закрыть вÑплывающую подÑказку</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Мотоциклы</translation>
<translation id="8790007591277257123">&amp;Повторить удаление</translation>
@@ -2801,6 +2814,7 @@
<translation id="8806285662264631610">Товары Ð´Ð»Ñ ÑƒÑ…Ð¾Ð´Ð° за телом</translation>
<translation id="8807160976559152894">Подрезать поÑле каждой Ñтраницы</translation>
<translation id="8808828119384186784">ÐаÑтройки Chrome</translation>
+<translation id="8813277370772331957">Ðапомнить позже</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />. Ðажмите Tab и затем Ввод, чтобы обновить Chrome.</translation>
<translation id="8820817407110198400">Закладки</translation>
<translation id="882338992931677877">Слот Ð´Ð»Ñ Ñ€ÑƒÑ‡Ð½Ð¾Ð¹ подачи</translation>
@@ -2980,6 +2994,7 @@
<translation id="988159990683914416">Сборка Ð´Ð»Ñ Ñ€Ð°Ð·Ñ€Ð°Ð±Ð¾Ñ‚Ñ‡Ð¸ÐºÐ¾Ð²</translation>
<translation id="989988560359834682">Изменение адреÑа</translation>
<translation id="991413375315957741">Датчики Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¸ оÑвещенноÑти</translation>
+<translation id="992110854164447044">Ð’Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° помогает Ñкрыть ваши данные от мошенников. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Розовый</translation>
<translation id="992432478773561401">Программа "<ph name="SOFTWARE_NAME" />" была уÑтановлена неправильно.
diff --git a/chromium/components/strings/components_strings_si.xtb b/chromium/components/strings/components_strings_si.xtb
index ca354151be0..a95fb157f23 100644
--- a/chromium/components/strings/components_strings_si.xtb
+++ b/chromium/components/strings/components_strings_si.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ප්â€à¶»à¶¯à·à¶±, à·à·’à·‚à·Šâ€à¶ºà¶­à·Šà·€ සහ මූල්â€à¶º ආධà·à¶»</translation>
<translation id="1048785276086539861">ඔබ අනුසටහන් සංස්කරණය කරන විට, මෙම ලේඛනය තනි පිටු දසුනකට ආපසු යනු ඇත</translation>
<translation id="1050038467049342496">අනෙකුත් යෙදුම් වසන්න</translation>
+<translation id="1053959602163383901">ඔබ <ph name="PROVIDER_ORIGIN" /> භà·à·€à·’ත කරන වෙබ් අඩවිවල සත්â€à¶ºà·à¶´à¶š උපà·à¶‚ගයක් සමඟ සත්â€à¶ºà·à¶´à¶±à¶º කිරීමට තà·à¶»à· ඇත. මෙම සපයන්න෠ඔබට <ph name="LINK_TEXT" /> à·„à·à¶šà·’, ඔබගේ ගෙවීම් ක්â€à¶»à¶¸à¶º පිළිබඳ තොරතුරු ගබඩ෠කර තිබිය à·„à·à¶šà·’ය.</translation>
<translation id="1055184225775184556">&amp;එක් කිරීම පසුගමනය කිරීම</translation>
<translation id="1056663316309890343">ඡà·à¶ºà·à¶»à·–ප මෘදුකà·à¶‚ගය</translation>
<translation id="1056898198331236512">අවවà·à¶¯à¶ºà¶ºà·’</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">නංව෠ගà·à¶±à·“මේ ක්â€à¶»à¶¸à¶º</translation>
<translation id="1281476433249504884">අට්ටිය 1</translation>
<translation id="1285320974508926690">මෙම අඩවිය කිසිවිට පරිවර්තනය නොකරන්න</translation>
+<translation id="1288548991597756084">කà·à¶©à·Šà¶´à¶­ ආරක්ෂිතව සුරකින්න</translation>
<translation id="1292571435393770077">Tray 16</translation>
<translation id="1292701964462482250">"ඔබේ පරිගණකයේ මෘදුකà·à¶‚ග වෙබයට සුරක්ෂිතව සබà·à¶³à·“මෙන් Chrome නවතයි" (Windows පරිගණක පමණි)</translation>
<translation id="1294154142200295408">විධà·à¶±-පේළි ප්â€à¶»à¶·à·šà¶¯à¶±à¶ºà¶±à·Š</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;දà·à·‚ය නිරà·à¶šà¶»à¶«à¶º කිරීම සඳහà·, ඔබ විවෘත කිරීමට උත්සà·à·„ කරන පිටුවෙහි &lt;strong&gt;සබඳින්න&lt;/strong&gt; ක්ලික් කරන්න.&lt;/p&gt;</translation>
<translation id="1507780850870535225">භූදර්à·à¶± à·ƒà·à¶½à·ƒà·”ම</translation>
<translation id="1513706915089223971">ඉතිහà·à·ƒ ඇතුළත් කිරීම් ලà·à¶ºà·’ස්තුව</translation>
+<translation id="1516097932025103760">එය සංකේතනය කර, ආරක්ෂිතව සුරකිනු ඇති අතර CVC කිසිද෠ගබඩ෠නොකෙරේ.</translation>
<translation id="1517433312004943670">දුරකථන අංකය අවà·à·Šâ€à¶ºà¶ºà·’</translation>
<translation id="1519264250979466059">නිà·à·Šà¶´à·à¶¯à¶š දිනය</translation>
<translation id="1521159554480556801">ෆයිබර් සහ රෙදිපිළි කලà·</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ගෙවීමේ ක්â€à¶»à¶¸ සුරක්ෂිත කර පුරවන්න</translation>
<translation id="1663943134801823270">කà·à¶©à·Šà¶´à¶­à·Š සහ ලිපින Chrome වෙතිනි. ඔබට ඒව෠<ph name="BEGIN_LINK" />à·ƒà·à¶šà·ƒà·“ම්<ph name="END_LINK" /> තුළ කළමනà·à¶šà¶»à¶«à¶º කළ à·„à·à¶šà·’ය.</translation>
<translation id="1671391448414634642"><ph name="SOURCE_LANGUAGE" /> à·„à·’ ඇති පිටු දà·à¶±à·Š සිට <ph name="TARGET_LANGUAGE" /> වෙත පරිවර්තන කරනු ලà·à¶¶à·š.</translation>
+<translation id="1673886523110456987">මෙම දීමනà·à·€ භà·à·€à·’ත කිරීමට <ph name="CARD_DETAIL" /> සමගින් ගෙවන්න</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> සිට <ph name="TARGET_LANGUAGE" /> ට</translation>
<translation id="1682696192498422849">පළමුව කෙටි දà·à¶»à¶º</translation>
<translation id="168693727862418163">මෙම ප්â€à¶»à¶­à·’පත්ති අගය එහි නිරූපණයට අනුව වලංගු කිරීමට අසමත් වූ බà·à·€à·’න් නොසලක෠හරිනු ලà·à¶¶à·š.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">චලන සංවේදක</translation>
<translation id="1717494416764505390">තà·à¶´à·à¶½à·Š පෙට්ටිය 3</translation>
<translation id="1718029547804390981">ලේඛනය අනුසටහන් කිරීමට විà·à·à¶½ à·€à·à¶©à·’යි</translation>
+<translation id="1720941539803966190">නිබන්ධනය වසන්න</translation>
<translation id="1721424275792716183">* ක්ෂේත්â€à¶»à¶º අවà·à·Šâ€à¶ºà¶ºà·’</translation>
<translation id="1727613060316725209">සහතිකය වලංගුය</translation>
<translation id="1727741090716970331">වලංගු කà·à¶©à·Šà¶´à¶­à·Š අංකයක් එක් කරන්න</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">බà·à¶¶à·’කියු සහ ග්â€à¶»à·’ලින්</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> හි පිටු පරිවර්තන නොකෙරේ</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{මෙම පà·à¶½à¶±à¶º ක්â€à¶»à·’යà·à¶­à·Šà¶¸à¶š සහ තත්ත්වය සක්â€à¶»à·’යව ඇති විට, ඔබගේ මෑත බ්â€à¶»à·€à·”ස් කිරීමේ ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸ වඩà·à¶­à·Šà¶¸ සමà·à¶± වන්නේ කුමන විà·à·à¶½ පුද්ගලයන් සමූහයකටද, නà·à¶­à·„ොත් “කණ්ඩà·à¶ºà¶¸à¶šà¶§à¶¯â€ යන්න Chrome තීරණය කරයි. වෙළඳ ප්â€à¶»à¶ à·à¶»à¶šà¶ºà¶±à·Šà¶§ සමූහය සඳහ෠වෙළඳ දà·à¶±à·Šà·€à·“ම් තà·à¶»à· ගත à·„à·à¶šà·’ අතර ඔබගේ බ්â€à¶»à·€à·”ස් කිරීමේ ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š ඔබගේ උපà·à¶‚ගයේ පුද්ගලිකව තබ෠ගà·à¶±à·š. ඔබගේ සමූහය සෑම දිනකම යà·à·€à¶­à·Šà¶šà·à¶½à·“න වේ.}=1{මෙම පà·à¶½à¶±à¶º ක්â€à¶»à·’යà·à¶­à·Šà¶¸à¶š සහ තත්ත්වය සක්â€à¶»à·’යව ඇති විට, ඔබගේ මෑත බ්â€à¶»à·€à·”ස් කිරීමේ ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸ වඩà·à¶­à·Šà¶¸ සමà·à¶± වන්නේ කුමන විà·à·à¶½ පුද්ගලයන් සමූහයකටද, නà·à¶­à·„ොත් “කණ්ඩà·à¶ºà¶¸à¶šà¶§à¶¯â€ යන්න Chrome තීරණය කරයි. වෙළඳ ප්â€à¶»à¶ à·à¶»à¶šà¶ºà¶±à·Šà¶§ සමූහය සඳහ෠වෙළඳ දà·à¶±à·Šà·€à·“ම් තà·à¶»à· ගත à·„à·à¶šà·’ අතර ඔබගේ බ්â€à¶»à·€à·”ස් කිරීමේ ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š ඔබගේ උපà·à¶‚ගයේ පුද්ගලිකව තබ෠ගà·à¶±à·š. ඔබගේ සමූහය සෑම දිනකම යà·à·€à¶­à·Šà¶šà·à¶½à·“න වේ.}one{මෙම පà·à¶½à¶±à¶º ක්â€à¶»à·’යà·à¶­à·Šà¶¸à¶š සහ තත්ත්වය සක්â€à¶»à·’යව ඇති විට, ඔබගේ මෑත බ්â€à¶»à·€à·”ස් කිරීමේ ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸ වඩà·à¶­à·Šà¶¸ සමà·à¶± වන්නේ කුමන විà·à·à¶½ පුද්ගලයන් සමූහයකටද, නà·à¶­à·„ොත් “කණ්ඩà·à¶ºà¶¸à¶šà¶§à¶¯â€ යන්න Chrome තීරණය කරයි. වෙළඳ ප්â€à¶»à¶ à·à¶»à¶šà¶ºà¶±à·Šà¶§ සමූහය සඳහ෠වෙළඳ දà·à¶±à·Šà·€à·“ම් තà·à¶»à· ගත à·„à·à¶šà·’ අතර ඔබගේ බ්â€à¶»à·€à·”ස් කිරීමේ ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š ඔබගේ උපà·à¶‚ගයේ පුද්ගලිකව තබ෠ගà·à¶±à·š. ඔබගේ සමූහය සෑම දින {NUM_DAYS}කම යà·à·€à¶­à·Šà¶šà·à¶½à·“න වේ.}other{මෙම පà·à¶½à¶±à¶º ක්â€à¶»à·’යà·à¶­à·Šà¶¸à¶š සහ තත්ත්වය සක්â€à¶»à·’යව ඇති විට, ඔබගේ මෑත බ්â€à¶»à·€à·”ස් කිරීමේ ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸ වඩà·à¶­à·Šà¶¸ සමà·à¶± වන්නේ කුමන විà·à·à¶½ පුද්ගලයන් සමූහයකටද, නà·à¶­à·„ොත් “කණ්ඩà·à¶ºà¶¸à¶šà¶§à¶¯â€ යන්න Chrome තීරණය කරයි. වෙළඳ ප්â€à¶»à¶ à·à¶»à¶šà¶ºà¶±à·Šà¶§ සමූහය සඳහ෠වෙළඳ දà·à¶±à·Šà·€à·“ම් තà·à¶»à· ගත à·„à·à¶šà·’ අතර ඔබගේ බ්â€à¶»à·€à·”ස් කිරීමේ ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š ඔබගේ උපà·à¶‚ගයේ පුද්ගලිකව තබ෠ගà·à¶±à·š. ඔබගේ සමූහය සෑම දින {NUM_DAYS}කම යà·à·€à¶­à·Šà¶šà·à¶½à·“න වේ.}}</translation>
-<translation id="2053553514270667976">ZIP කේතය</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{යà·à¶¢à¶±à· 1}one{යà·à¶¢à¶±à· #}other{යà·à¶¢à¶±à· #}}</translation>
+<translation id="2066915425250589881">මà·à¶šà·“මට ඉල්ලීම</translation>
<translation id="2068528718802935086">ළදරුවන් සහ සිඟිත්තන්</translation>
<translation id="2071156619270205202">මෙම කà·à¶©à·Šà¶´à¶­ අතථ්â€à¶º කà·à¶©à·Šà¶´à¶­à·Š අංකයකට සුදුසුකම් නොලබයි.</translation>
<translation id="2071692954027939183">ඔබ à·ƒà·à¶¸à·à¶±à·Šâ€à¶ºà¶ºà·™à¶±à·Š ඒවà·à¶§ ඉඩ නොදෙන නිස෠දà·à¶±à·”ම්දීම් ස්වයංක්â€à¶»à·’යව අවහිර කරන ලදි</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">සමමුහුර්ත කිරීම කළමනà·à¶šà¶»à¶«à¶º කරන්න බොත්තම, Chrome à·ƒà·à¶šà·ƒà·“ම් තුළ ඔබ සමමුමුහුර්ත කරන්නේ කුමන තොරතුරුද යන්න කළමනà·à¶šà¶»à¶«à¶º කිරීමට Enter ඔබන්න</translation>
<translation id="2091887806945687916">හඬ</translation>
<translation id="2094505752054353250">වසම් නොගà·à¶½à¶´à·š</translation>
-<translation id="2096368010154057602">දෙපà·à¶»à·Šà¶­à¶¸à·šà¶±à·Šà¶­à·”à·€</translation>
<translation id="2099652385553570808">වමට ස්ටේපල් තුනක්</translation>
<translation id="2101225219012730419">අනුවà·à¶¯à¶º:</translation>
<translation id="2102134110707549001">ප්â€à¶»à¶¶à¶½ මුරපදයක් යà·à¶¢à¶±à· කරන්න…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">ඇමරිකà·à¶±à·” පà·à¶´à¶±à·Šà¶¯à·”</translation>
<translation id="2187317261103489799">හදුනà·à¶œà¶±à·Šà¶± (පෙරනිමි)</translation>
<translation id="2188375229972301266">පහළට ඇනීම් බොහොමයක්</translation>
-<translation id="2188852899391513400">ඔබ මේ දà·à¶±à·Š භà·à·€à·’ත෠කළ මුරපදය දත්ත කඩ කිරීමකින් සොය෠ගන්න෠ලදී. ඔබගේ ගිණුම් සුරක්ෂිත කිරීමට, Google මුරපද කළමනà·à¶šà¶»à·” එය දà·à¶±à·Š වෙනස් කර ඔබගේ සුරකින ලද මුරපද පරීක්ෂ෠කිරීම නිර්දේ෠කරයි.</translation>
<translation id="219906046732893612">නිවà·à·ƒ à·€à·à¶©à·’ දියුණු කිරීම</translation>
<translation id="2202020181578195191">වලංගු කල් ඉකුත් වීමේ වසරක් ඇතුළු කරන්න</translation>
<translation id="22081806969704220">බඳුන 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">ගොනු සංස්කරණය</translation>
<translation id="2215963164070968490">සුනඛයන්</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à·„à·’ දà·à¶±à¶§ සිටින ප්â€à¶»à·„à·à¶»à¶šà¶ºà·’න්ට ඔබේ උපà·à¶‚ගයට à·„à·à¶±à·’ කරන, ඔබේ ජංගම බිලට à·ƒà·à¶Ÿà·€à·”ණු ගà·à·ƒà·Šà¶­à·” එක් කරන, හ෠ඔබේ පුද්ගලික තොරතුරු සොරකම් කරන භයà·à¶±à¶š යෙදුම් ස්ථà·à¶´à¶±à¶º කළ à·„à·à¶šà·’ය. <ph name="BEGIN_LEARN_MORE_LINK" />තව දà·à¶± ගන්න<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">නිබන්ධනය යළි අරඹන්න</translation>
+<translation id="2219735899272417925">උපà·à¶‚ගය යළි පිහිටුවීම අවà·à·Šâ€à¶ºà¶ºà·’</translation>
<translation id="2224337661447660594">අන්තර්ජà·à¶½à¶º නà·à¶­</translation>
<translation id="2230458221926704099">ඔබේ සම්බන්ධතà·à·€ <ph name="BEGIN_LINK" />දà·à·‚ නිර්ණ යෙදුම<ph name="END_LINK" /> භà·à·€à·’තයෙන් නිවà·à¶»à¶¯à·’ කරන්න</translation>
<translation id="2239100178324503013">දà·à¶±à·Š යවන්න</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">ඉඩ නොදේ (පෙරනිමි)</translation>
<translation id="2512413427717747692">Chrome පෙරනිමි බ්â€à¶»à·€à·”සර බොත්තම ලෙස සකසන්න, iOS à·ƒà·à¶šà·ƒà·“ම් තුළ Chrome පද්ධතියේ පෙරනිමි බ්â€à¶»à·€à·”සරය ලෙස à·ƒà·à¶šà·ƒà·“මට Enter ඔබන්න</translation>
<translation id="2515629240566999685">ඔබේ ප්â€à¶»à¶¯à·šà·à¶ºà·š සංඥ෠පරීක්ෂ෠කරමින්</translation>
+<translation id="2515761554693942801">ඔබ <ph name="PROVIDER_ORIGIN" /> භà·à·€à·’ත කරන වෙබ් අඩවිවල ස්පර්෠ID සමඟ සත්â€à¶ºà·à¶´à¶±à¶º කිරීමට තà·à¶»à· ඇත. මෙම සපයන්න෠ඔබට <ph name="LINK_TEXT" /> à·„à·à¶šà·’, ඔබගේ ගෙවීම් ක්â€à¶»à¶¸à¶º පිළිබඳ තොරතුරු ගබඩ෠කර තිබිය à·„à·à¶šà·’ය.</translation>
<translation id="2521385132275182522">පහළ දකුණ ස්ටේපල් කරන්න</translation>
<translation id="2521736961081452453">පà·à¶»à¶¸à¶º තනන්න</translation>
<translation id="2523886232349826891">මෙම උපà·à¶‚ගයෙහි පමණක් සුරà·à¶šà·’ණි</translation>
<translation id="2524461107774643265">à·€à·à¶©à·’දුර තොරතුරු එක් කරන්න</translation>
<translation id="2529899080962247600">මෙම ක්ෂේත්â€à¶»à¶ºà¶§ <ph name="MAX_ITEMS_LIMIT" />කට වඩ෠වà·à¶©à·’ ඇතුළත් කිරීම් තිබිය යුතුය. සියලු à·€à·à¶©à·’දුර ඇතුළත් කිරීම් නොසලක෠හරිනු ඇත.</translation>
+<translation id="253493526287553278">ප්â€à¶»à·€à¶»à·Šà¶°à¶± කේත විස්තර බලන්න</translation>
<translation id="2535585790302968248">පුද්ගලිකව බ්â€à¶»à·€à·”ස් කිරීමට නව අප්â€à¶»à¶šà¶§ ටà·à¶¶à¶ºà¶šà·Š විවෘත කරන්න</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{සහ තව 1 ක්}one{සහ තව #ක්}other{සහ තව #ක්}}</translation>
<translation id="2536110899380797252">ලිපිනය එක් කරන්න</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">ඡà·à¶ºà·à¶»à·–ප සහ ඩිජිටල් කලà·</translation>
<translation id="2601150049980261779">ප්â€à¶»à·šà¶¸ වෘත්තà·à¶±à·Šà¶­ චිත්â€à¶»à¶´à¶§</translation>
<translation id="2604589665489080024">පොප් සංගීතය</translation>
-<translation id="2609632851001447353">විචලතà·</translation>
<translation id="2610561535971892504">පිටපත් කිරීමට ක්ලික් කිරීම</translation>
<translation id="2617988307566202237">Chrome පහත තොරතුරු <ph name="BEGIN_EMPHASIS" />නොසුරකිනු ඇත<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">උපන් දින සහ නම් දින</translation>
<translation id="2677748264148917807">à·„à·à¶» යන්න</translation>
+<translation id="2679714844901977852">ආරක්ෂිත සහ වඩ෠වේගවත් ගෙව෠පිටවීම් සඳහ෠ඔබගේ කà·à¶©à·Šà¶´à¶­ සහ බිල්පත් තොරතුරු ඔබගේ Google ගිණුමට <ph name="USER_EMAIL" /> සුරකින්න</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">හොඳින්ම ගà·à·…පේ</translation>
<translation id="2688969097326701645">ඔව්, දිගටම කරගෙන යන්න</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">අන්තර්ජà·à¶½ සේව෠සපයන්නන් (ISP)</translation>
<translation id="2856444702002559011">ප්â€à¶»à·„à·à¶»à¶šà¶ºà¶±à·Š <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> වෙතින් ඔබේ තොරතුරු සොරකම් කිරීමට උත්සà·à·„ කරනව෠විය à·„à·à¶š (උදà·à·„රණ ලෙස, මුරපද, පණිවිඩ, හ෠ණයපත්). <ph name="BEGIN_LEARN_MORE_LINK" />තව දà·à¶± ගන්න<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">මෙම වෙබ් අඩවිය ආක්â€à¶»à¶¸à¶«à·’ක හ෠නොමඟ යවන දà·à¶±à·Šà·€à·“ම් පෙන්වයි.</translation>
-<translation id="286512204874376891">අතථ්â€à¶º කà·à¶©à·Šà¶´à¶­à¶šà·Š සිදු විය à·„à·à¶šà·’ වංචà·à·€à·™à¶±à·Š ඔබව සුරක්ෂිත කර ගà·à¶±à·“මට ඔබගේ à·ƒà·à¶¶à·‘ කà·à¶©à·Šà¶´à¶­ මෙන් වෙස්වළ෠ගනී. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">මිත්â€à¶»</translation>
<translation id="28761159517501904">චිත්â€à¶»à¶´à¶§</translation>
<translation id="2876489322757410363">බà·à·„ිර යෙදුමක් හරහ෠ගෙවීමට අප්â€à¶»à·ƒà·’ද්ධ ප්â€à¶»à¶šà·à¶»à¶º à·„à·à¶» යමින්. ඉදිරියට යන්නද?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">Disc</translation>
<translation id="3162559335345991374">ඔබ භà·à·€à·’ත෠කරන Wi-Fi මගින් ඔබ එහි පුරනය වීමේ පිටුවට පිවිසීමට අවà·à·Šâ€à¶º විය à·„à·à¶š.</translation>
<translation id="3169472444629675720">අනà·à·€à¶»à¶«à¶º</translation>
-<translation id="3174168572213147020">දූපත</translation>
<translation id="3176929007561373547">ඔබේ ප්â€à¶»à·œà¶šà·Šà·ƒà·’ à·ƒà·à¶šà·ƒà·“ම් පරීක්ෂ෠කරන්න, නà·à¶­à·„ොත් ප්â€à¶»à·œà¶šà·Šà·ƒà·’ සේවà·à¶¯à·à¶ºà¶šà¶º ක්â€à¶»à·’ය෠කරන්නේදà·à¶ºà·’
තහවුරු කරගà·à¶±à·“මට ඔබේ ජà·à¶½ පරිපà·à¶½à¶š අමතන්න. ඔබ ප්â€à¶»à·œà¶šà·Šà·ƒà·’ සේවà·à¶¯à·à¶ºà¶šà¶ºà¶šà·Š භà·à·€à·’ත
කළ යුතු බව ඔබ විà·à·Šà·€à·à·ƒ නොකරන්නේ නම්:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">ඔරලà·à·ƒà·” දà·à·‚ය</translation>
<translation id="3369459162151165748">à·€à·à·„න අමතර කොටස් සහ උපà·à¶‚ග</translation>
<translation id="3371064404604898522">Chrome පෙරනිමි බ්â€à¶»à·€à·Šà·ƒà¶»à¶º ලෙස සකසන්න</translation>
-<translation id="3371076217486966826"><ph name="URL" /> හට පහත දේවල් අවà·à·Šâ€à¶ºà¶ºà·’:
- • ඔබගේ අවට 3D සිතියමක් තà·à¶±à·“ම සහ කà·à¶¸à¶»à· පිහිටීම හඹ෠යà·à¶¸
- • ඔබගේ කà·à¶¸à¶»à·à·€ භà·à·€à·’ත කිරීම</translation>
<translation id="337363190475750230">ප්â€à¶»à¶­à·’විධà·à¶± කර ඇත</translation>
<translation id="3375754925484257129">Chrome ආරක්ෂක පරීක්ෂà·à·€ ධà·à·€à¶±à¶º කරන්න</translation>
<translation id="3377144306166885718">සේවà·à¶¯à·à¶ºà¶šà¶º TLS à·„à·’ යල් පà·à¶± ගිය අනුවà·à¶¯à¶ºà¶šà·Š භà·à·€à·’ත කළà·.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">බෙද෠හà·à¶»à·“මේ ලිපිනය</translation>
<translation id="3402261774528610252">මෙම වෙබ් අඩවිය පූරණ කිරීමට භà·à·€à·’ත කළ සම්බන්ධතà·à·€ TLS 1.0 à·„à· TLS 1.1 භà·à·€à·’ත කළà·, ඒව෠අත à·„à·à¶» තිබෙන අතර අනà·à¶œà¶­à¶ºà·šà¶¯à·“ අබල කෙරේ. වරක් අබල කළ විට, පරිà·à·“ලකයින් මෙම වෙබ් අඩවිය පූරණ කිරීම වළක්වනු ලà·à¶¶à·š. සේවà·à¶¯à·à¶ºà¶šà¶º TLS 1.2 හ෠පසු අනුවà·à¶¯à¶º සබල කළ යුතුය.</translation>
<translation id="3405664148539009465">ෆොන්ට අභිරුචිකරණය කරන්න</translation>
+<translation id="3407789382767355356">තෙවන පà·à¶»à·Šà·à·Šà·€ පිරීම</translation>
<translation id="3409896703495473338">ආරක්ෂක à·ƒà·à¶šà·ƒà·“ම් කළමනà·à¶šà¶»à¶«à¶º කරන්න</translation>
<translation id="3414952576877147120">ප්â€à¶»à¶¸à·à¶«à¶º:</translation>
<translation id="3417660076059365994">විà·à·Šà¶½à·šà·‚ණය සඳහ෠ඔබ අලවන හ෠අමුණන ගොනු Google Cloud හ෠තෙවන පà·à¶»à·Šà·à·Šà·€ වෙත යවයි. උදà·à·„රණයක් à·€à·à¶ºà·™à¶±à·Š, සංවේදි දත්ත හ෠මà·à¶½à·Šà·€à·™à¶ºà·à¶»à·Š සඳහ෠ඒව෠ස්කෑන් කරනු ලà·à¶¶à·’ය à·„à·à¶šà·’ය.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">චිත්â€à¶»à¶´à¶§ ලà·à¶ºà·’ස්තුගත කිරීම් සහ චිත්â€à¶»à¶´à¶§ දර්à·à¶± වේලà·à·€à¶±à·Š</translation>
<translation id="3479552764303398839">දà·à¶±à·Š නොවේ</translation>
<translation id="3484560055331845446">ඔබට ඔබේ Google ගිණුමට ප්â€à¶»à·€à·šà·à¶º අහිමි විය à·„à·à¶š. Chrome දà·à¶±à·Š ඔබේ මුරපදය වෙනස් කිරීම නිර්දේ෠කරයි. පුරන ලෙස ඔබෙන් අසනු ලà·à¶¶à·š.</translation>
+<translation id="3484861421501147767">සිහිකà·à¶³à·€à·“ම: සුරà·à¶šà·’ ප්â€à¶»à·€à¶»à·Šà¶°à¶± කේතය තිබේ</translation>
<translation id="3487845404393360112">බඳුන 4</translation>
<translation id="3495081129428749620">පිටුව තුළ සොයන්න
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">කළමනà·à¶šà¶»à¶«à¶º</translation>
<translation id="3816482573645936981">අගය (ඉක්මව෠ඇත)</translation>
<translation id="382518646247711829">ඔබ ප්â€à¶»à·œà¶šà·Šà·ƒà·’ සර්වරය භà·à·€à·’ත෠කළහොත්...</translation>
+<translation id="3826050100957962900">තුන්වන පà·à¶»à·Šà·à·Šà·€ පිරීම</translation>
<translation id="3827112369919217609">නියත</translation>
<translation id="3827666161959873541">පවුලේ චිත්â€à¶»à¶´à¶§</translation>
<translation id="3828924085048779000">හිස් රහස්පද ඉඩ නොදේ.</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">දිනය සහ වේලà·à·€ යà·à·€à¶­à·Šà¶šà·à¶½à·“න කරන්න</translation>
<translation id="3858860766373142691">නම</translation>
<translation id="3872834068356954457">විද්â€à¶ºà·à·€</translation>
+<translation id="3875783148670536197">කෙසේද කිය෠මට පෙන්වන්න</translation>
<translation id="3881478300875776315">පේළි අඩුවෙන් පෙන්වන්න</translation>
<translation id="3884278016824448484">උපà·à¶‚ගය හඳුන෠ගà·à¶±à·“ම ප්â€à¶»à¶­à·’විරුද්ධයි</translation>
-<translation id="3885155851504623709">කà·à¶»à¶½à¶º</translation>
<translation id="388632593194507180">අධීක්ෂණය අනà·à·€à¶»à¶«à¶º විය</translation>
<translation id="3886948180919384617">අට්ටිය 3</translation>
<translation id="3890664840433101773">ඊ-තà·à¶´à·‘ල එක් කරන්න</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> අවහිර කර ඇත</translation>
<translation id="3978338123949022456">සෙවීම් ප්â€à¶»à¶šà·à¶»à¶º, විමසුමක් ටයිප් කර <ph name="KEYWORD_SUFFIX" /> සමග සෙවීමට Enter ඔබන්න</translation>
<translation id="398470910934384994">පක්ෂීන්</translation>
+<translation id="3985750352229496475">ලිපින කළමනà·à¶šà¶»à¶«à¶º කරන්න...</translation>
<translation id="3986705137476756801">දà·à¶±à¶§ සජීවී සිරස්තල ක්â€à¶»à·’ය෠විරහිත කරන්න</translation>
<translation id="3987940399970879459">1 MBට වඩ෠අඩු</translation>
<translation id="3990250421422698716">අනුලම්බයේ සෙමින් දුවන්න</translation>
+<translation id="3992684624889376114">මෙම පිටුව පිළිබඳ</translation>
<translation id="3996311196211510766">මූලà·à¶»à¶¸à·Šà¶· ප්â€à¶»à¶­à·’පත්තිය එය වෙත සියලුම ඉල්ලීම්වලට යෙදීමට <ph name="ORIGIN" /> වෙබ් අඩවිය
ඉල්ල෠ඇති නමුත්, දà·à¶±à¶§ මෙම ප්â€à¶»à¶­à·’පත්තිය යෙදිය නොහà·à¶š.</translation>
<translation id="4006465311664329701">Google Pay භà·à·€à·’තයෙන් ගෙවීමේ ක්â€à¶»à¶¸, දීමන෠සහ ලිපින</translation>
@@ -1224,6 +1231,7 @@
<translation id="4305666528087210886">ඔබගේ ගොනුවට පිවිසීමට නොහà·à¶šà·’ විය.</translation>
<translation id="4306529830550717874">ලිපිනය සුරකින්නද?</translation>
<translation id="4306812610847412719">පසුරු පුවරුව</translation>
+<translation id="4310070645992025887">ඔබගේ චà·à¶»à·’ක෠සොයන්න</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">අවහිර කරන්න (පෙරනිමි)</translation>
<translation id="4314815835985389558">සමමුහුර්තය කළමනà·à¶šà¶»à¶«à¶º</translation>
@@ -1274,6 +1282,7 @@
<translation id="4435702339979719576">තà·à¶´à·à¶½à·Š පත)</translation>
<translation id="443673843213245140">ප්â€à¶»à·œà¶šà·Šà·ƒà·’යක භà·à·€à·’තය අබල කර ඇති නමුත් ප්â€à¶»à¶šà·à·à·’ත ප්â€à¶»à·œà¶šà·Šà·ƒà·’ වින්â€à¶ºà·à·ƒà¶šà¶»à¶«à¶ºà¶šà·Š නිà·à·Šà¶ à¶º කර ඇත.</translation>
<translation id="4441832193888514600">ප්â€à¶»à¶­à·’පත්තිය ක්ලවුඩ් පරිà·à·“ලක ප්â€à¶»à¶­à·’පත්තියක් ලෙස පමණක් à·ƒà·à¶šà·ƒà·’ය à·„à·à¶šà·’ බà·à·€à·’න් නොසලක෠හරින ලදී.</translation>
+<translation id="4442470707340296952">Chrome ටà·à¶¶</translation>
<translation id="4450893287417543264">නà·à·€à¶­ නොපෙන්වන්න</translation>
<translation id="4451135742916150903">HID උපà·à¶‚ග වෙත සම්බන්ධ වීමට ඉල්ලිය à·„à·à¶šà·’ය</translation>
<translation id="4452328064229197696">ඔබ මේ දà·à¶±à·Š භà·à·€à·’ත෠කළ මුරපදය දත්ත කඩ කිරීමකින් සොය෠ගන්න෠ලදී. ඔබගේ ගිණුම් සුරක්ෂිත කිරීමට, Google මුරපද කළමනà·à¶šà¶»à·” ඔබගේ සුරකින ලද මුරපද පරීක්ෂ෠කිරීම නිර්දේ෠කරයි.</translation>
@@ -1412,6 +1421,7 @@
<translation id="483241715238664915">අනතුරු ඇඟවීම් ක්â€à¶»à·’යà·à¶­à·Šà¶¸à¶š කරන්න</translation>
<translation id="4834250788637067901">Google Pay භà·à·€à·’තයෙන් ගෙවීමේ ක්â€à¶»à¶¸, දීමන෠සහ ලිපින</translation>
<translation id="4838327282952368871">සිහිනමය</translation>
+<translation id="4839087176073128681">ඊළඟ වතà·à·€à·š වඩà·à¶­à·Š වේගයෙන් ගෙව෠Google à·„à·’ කර්මà·à¶±à·Šà¶­à¶ºà·š ප්â€à¶»à¶¸à·”ඛ ආරක්ෂà·à·€ සමඟින් ඔබගේ කà·à¶©à·Šà¶´à¶­ ආරක්ෂ෠කරන්න.</translation>
<translation id="4840250757394056958">ඔබගේ Chrome ඉතිහà·à·ƒà¶º බලන්න</translation>
<translation id="484462545196658690">ස්වයං</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> හි වට්ටම් සහ තවත් දේ ලබ෠ගන්න</translation>
@@ -1474,6 +1484,7 @@
<translation id="5011561501798487822">අනà·à·€à¶»à¶«à¶º කර ගත් භà·à·‚à·à·€</translation>
<translation id="5015510746216210676">යන්ත්â€à¶»à¶ºà·š නම:</translation>
<translation id="5017554619425969104">ඔබ පිටපත් කළ පà·à¶¨à¶º</translation>
+<translation id="5017828934289857214">මට පසුව සිහිකà·à¶³à·€à¶±à·Šà¶±</translation>
<translation id="5018422839182700155">මෙම පිටුව විවෘත කළ නොහà·à¶š</translation>
<translation id="5019198164206649151">උපස්ථ ගබඩà·à·€ දුර්වල තත්වයක</translation>
<translation id="5020776957610079374">ලà·à¶š සංගීතය</translation>
@@ -1493,6 +1504,7 @@
<translation id="5051305769747448211">සජීවී ප්â€à¶»à·„සන</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{ළඟ බෙද෠ගà·à¶±à·“ම භà·à·€à·’තයෙන් මෙම ගොනුව යà·à·€à·“මට, ඔබගේ උපà·à¶‚ගයෙහි (<ph name="DISK_SPACE_SIZE" />) හිස් කරන්න}one{ළඟ බෙද෠ගà·à¶±à·“ම භà·à·€à·’තයෙන් මෙම ගොනු යà·à·€à·“මට, ඔබගේ උපà·à¶‚ගයෙහි (<ph name="DISK_SPACE_SIZE" />) හිස් කරන්න}other{ළඟ බෙද෠ගà·à¶±à·“ම භà·à·€à·’තයෙන් මෙම ගොනු යà·à·€à·“මට, ඔබගේ උපà·à¶‚ගයෙහි (<ph name="DISK_SPACE_SIZE" />) හිස් කරන්න}}</translation>
<translation id="5056549851600133418">ඔබ සඳහ෠ලිපි</translation>
+<translation id="5060483733937416656">ඔබ <ph name="PROVIDER_ORIGIN" /> භà·à·€à·’ත කරන වෙබ් අඩවිවල Windows Hello සමඟ සත්â€à¶ºà·à¶´à¶±à¶º කිරීමට තà·à¶»à· ඇත. මෙම සපයන්න෠ඔබට <ph name="LINK_TEXT" /> à·„à·à¶šà·’, ඔබගේ ගෙවීම් ක්â€à¶»à¶¸à¶º පිළිබඳ තොරතුරු ගබඩ෠කර තිබිය à·„à·à¶šà·’ය.</translation>
<translation id="5061227663725596739">ඔබ අදහස් කළේ <ph name="LOOKALIKE_DOMAIN" /> ද?</translation>
<translation id="5066056036849835175">මුද්â€à¶»à¶« ඉතිහà·à·ƒà¶º</translation>
<translation id="5068234115460527047">හෙජ් අරමුදල්</translation>
@@ -1506,10 +1518,8 @@
<translation id="5087286274860437796">මෙම අවස්ථà·à·€à·š සේවà·à¶¯à·à¶ºà¶šà¶ºà·š සහතිකය වලංගු නà·à¶­.</translation>
<translation id="5087580092889165836">කà·à¶©à·Šà¶´à¶­ එක් කරන්න</translation>
<translation id="5088142053160410913">මෙහෙකරු වෙත පණිවුඩය</translation>
-<translation id="5089810972385038852">ජනපදය</translation>
<translation id="5093232627742069661">Z-නà·à¶¸à·“ම</translation>
<translation id="5094747076828555589">මෙම සේවà·à¶¯à·à¶ºà¶šà¶ºà¶§ එය <ph name="DOMAIN" /> බව සනà·à¶® කිරීමට නොහà·à¶šà·’ විය; එහි ආරක්ෂණ සහතිකය Chromium මගින් විà·à·Šà·€à·à·ƒ නොකරයි. මෙය à·€à·à¶»à¶¯à·’ වින්â€à¶ºà·à·ƒ කිරීමක් හ෠ප්â€à¶»à·„à·à¶»à¶šà¶ºà¶šà·” ඔබගේ සබà·à¶³à·”මට බà·à¶°à· කිරීමක් නිස෠විය à·„à·à¶šà·’ය.</translation>
-<translation id="5095208057601539847">පළà·à¶­</translation>
<translation id="5097099694988056070">CPU/RAM භà·à·€à·’තය à·€à·à¶±à·’ උපà·à¶‚ග සංඛ්â€à¶ºà·à¶±</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">අඩවිය ආරක්ෂිත නà·à¶­</translation>
@@ -1547,6 +1557,7 @@
<translation id="5171045022955879922">සොයන්න හ෠ලිපිනය යොදන්න</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">යන්ත්â€à¶»à¶º</translation>
+<translation id="5177076414499237632">මෙම පිටුවේ මූලà·à·à·Šâ€à¶»à¶º සහ මà·à¶­à·˜à¶šà·à·€ ගà·à¶± දà·à¶± ගන්න</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> තුළ නà·à¶­à·’ද? මෙම දà·à·‚ය à·€à·à¶»à·Šà¶­à· කරන්න</translation>
<translation id="518639307526414276">සුරතල් සතුන්ගේ ආහà·à¶» සහ සුරතල් සතුන්ගේ සත්කà·à¶» à·ƒà·à¶´à¶ºà·”ම්</translation>
<translation id="5190835502935405962">පොත්සලකුණු තීරුව</translation>
@@ -1707,6 +1718,7 @@
<translation id="5624120631404540903">මුරපද පà·à¶½à¶±à¶º</translation>
<translation id="5629630648637658800">ප්â€à¶»à¶­à·’පත්ති à·ƒà·à¶šà·ƒà·”ම් පූර්ණය කළ නොහà·à¶šà·’ විය</translation>
<translation id="5631439013527180824">à·€à·à¶»à¶¯à·’ උපà·à¶‚ග කළමනà·à¶šà¶»à¶« සංකේතය</translation>
+<translation id="5632485077360054581">කෙසේද කිය෠මට පෙන්වන්න</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à·„à·’ දà·à¶±à¶§ සිටින ප්â€à¶»à·„à·à¶»à¶šà¶ºà¶±à·Š ඔබේ පරිගණකය මත ඔබේ තොරතුරු (උදà·à·„රණ ලෙස, ඡà·à¶ºà·à¶»à·–ප, මුරපද, පණිවිඩ සහ ණයපත්) සොරකම් කරන හ෠මකන භයà·à¶±à¶š ක්â€à¶»à¶¸à¶½à·šà¶› ස්ථà·à¶´à¶±à¶º කිරීමට උත්සà·à·„ දà·à¶»à·’ය à·„à·à¶š. <ph name="BEGIN_LEARN_MORE_LINK" />තව දà·à¶± ගන්න<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">රà·à·€à¶§à·’ලිකà·à¶»à·“ අන්තර්ගතය අවහිරයි.</translation>
<translation id="5633259641094592098">කල්ට් සහ ඉන්ඩී චිත්â€à¶»à¶´à¶§</translation>
@@ -1824,6 +1836,7 @@
<translation id="5989320800837274978">ස්ථිර ප්â€à¶»à·œà¶šà·Šà·ƒà·’ සේවà·à¶¯à·à¶ºà¶š à·„à· .pac ස්ක්â€à¶»à·’ප්ට ලිපින සඳහන් කර නොමà·à¶­.</translation>
<translation id="5992691462791905444">ඉංජිනේරු විද්â€à¶ºà· Z-නà·à¶¸à·“ම</translation>
<translation id="5995727681868049093">ඔබගේ Google ගිණුම තුළ ඔබගේ තොරතුරු, පෞද්ගලිකත්වය සහ ආරක්ෂà·à·€ කළමනà·à¶šà¶»à¶«à¶º කරන්න</translation>
+<translation id="5997247540087773573">ඔබ මේ දà·à¶±à·Š භà·à·€à·’ත කළ මුරපදය දත්ත කඩ කිරීමකදී සොය෠ගන්න෠ලදී. ඔබගේ ගිණුම් සුරක්ෂිත කිරීමට, Google මුරපද කළමනà·à¶šà¶»à·” එය දà·à¶±à·Š වෙනස් කර ඔබගේ සුරකින ලද මුරපද පරීක්ෂ෠කිරීම නිර්දේ෠කරයි.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' සඳහ෠ප්â€à¶»à¶­à·’ඵල <ph name="RESULT_COUNT" />ක්</translation>
<translation id="6006484371116297560">පà·à¶»à¶«à·’</translation>
<translation id="6008122969617370890">N-සිට-1 පිළිවෙළ</translation>
@@ -1919,7 +1932,6 @@
<translation id="627746635834430766">මීළඟ වතà·à·€à·š දී වේගවත්ව ගෙවීමට, ඔබේ Google ගිණුමට ඔබේ කà·à¶©à·Šà¶´à¶­ සහ බිල්පත් ලිපිනය සුරකින්න.</translation>
<translation id="6279183038361895380">ඔබේ කර්සරය පෙන්වීමට |<ph name="ACCELERATOR" />| ඔබන්න</translation>
<translation id="6280223929691119688">මෙම ලිපිනයට බෙද෠හà·à¶»à·“මට නොහà·à¶šà·’ය. වෙනත් ලිපිනයක් තà·à¶»à¶±à·Šà¶±.</translation>
-<translation id="6282194474023008486">තà·à¶´à·à¶½à·Š කේතය</translation>
<translation id="6285507000506177184">Chrome තුළ බà·à¶œà·à¶±à·“ම් කළමනà·à¶šà¶»à¶«à¶º කරන්න බොත්තම, ඔබ Chrome තුළ බà·à¶œà¶­à·Š ගොනු කළමනà·à¶šà¶»à¶«à¶º කිරීමට Enter ඔබන්න</translation>
<translation id="6289939620939689042">පිටුවේ වර්ණය</translation>
<translation id="6290238015253830360">ඔබේ යà·à¶¢à·’ත ලිපි මෙහි දිස්වනු ඇත</translation>
@@ -1941,6 +1953,7 @@
<translation id="6337133576188860026"><ph name="SIZE" />ට වඩ෠අඩුවෙන් හිස් කරයි. ඔබගේ ඊළඟ පිවිසීමේදී සමහර අඩවි වඩà·à¶­à·Š සෙමින් පූරණය විය à·„à·à¶šà·’ය.</translation>
<translation id="6337534724793800597">නම් à·€à·à¶ºà·™à¶±à·Š ප්â€à¶»à¶­à·’පත්ති පෙරන්න</translation>
<translation id="6340739886198108203">පරිපà·à¶½à¶š ප්â€à¶»à¶­à·’පත්තිය රහසිගත අන්තර්ගතය දෘà·à·Šâ€à¶ºà¶¸à·à¶± වන විට තිරපිටපත් හ෠පටිගත කිරීම් ගà·à¶±à·“ම නිර්දේ෠නොකරයි:</translation>
+<translation id="6348220984832452017">සක්â€à¶»à·’ය විචලන</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ස්ථà·à¶´à¶± කරන්න</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{කිසිවක් නà·à¶­}=1{1 මුරපදයක් (<ph name="DOMAIN_LIST" /> සඳහà·, සමමුහූර්තයි)}=2{මුරපද 2 ක් (<ph name="DOMAIN_LIST" /> සඳහà·, සමමුහූර්තයි)}one{මුරපද # ක් (<ph name="DOMAIN_LIST" /> සඳහà·, සමමුහූර්තයි)}other{මුරපද # ක් (<ph name="DOMAIN_LIST" /> සඳහà·, සමමුහූර්තයි)}}</translation>
<translation id="6355392890578844978">මෙම බ්â€à¶»à·€à·”සරය සමà·à¶œà¶¸à¶šà·’න් හ෠වෙනත් සංවිධà·à¶±à¶ºà¶šà·’න් කළමනà·à¶šà¶»à¶«à¶º නොකරයි. මෙම උපà·à¶‚ගයේ ක්â€à¶»à·’යà·à¶šà·à¶»à¶šà¶¸à·Š Chromium වෙතින් බà·à·„à·à¶»à·€ කළමනà·à¶šà¶»à¶«à¶º කිරීමට à·„à·à¶šà·’ය. <ph name="BEGIN_LINK" />තව දà·à¶± ගන්න<ph name="END_LINK" /></translation>
@@ -1972,7 +1985,6 @@
<translation id="643051589346665201">Google මුරපදය වෙනස් කරන්න</translation>
<translation id="6433490469411711332">සම්බන්ධත෠තොරතුරු සංස්කරණය</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> සබà·à¶³à·“ම ප්â€à¶»à¶­à·’ක්ෂේප කරන ලදී.</translation>
-<translation id="6438025220577812695">එය ම෠විසින්ම වෙනස් කරන්න</translation>
<translation id="6440503408713884761">නොතà·à¶šà·–</translation>
<translation id="6443406338865242315">ඔබ ස්ථà·à¶´à¶± කර ඇති දිගු සහ පේනු මෘදුකà·à¶‚ග</translation>
<translation id="6446163441502663861">Kahu (ලියුම් කවරය)</translation>
@@ -2102,9 +2114,9 @@
<translation id="6828866289116430505">ජà·à¶±à·€à·šà¶¯à¶º</translation>
<translation id="6831043979455480757">පරිවර්තනය කරන්න</translation>
<translation id="6833752742582340615">ආරක්ෂිත සහ වේගවත් ගෙව෠පිටවීම් සඳහ෠ඔබගේ කà·à¶©à·Šà¶´à¶­ සහ බිල්පත් තොරතුරු ඔබගේ Google ගිණුමට සුරකින්න</translation>
-<translation id="6839929833149231406">ප්â€à¶»à¶¯à·šà·à¶º</translation>
<translation id="6846340164947227603">අතත්â€à¶º කà·à¶©à·Šà¶´à¶­à·Š අංකයක් භà·à·€à·’ත කරන්න...</translation>
<translation id="6852204201400771460">යෙදුම නà·à·€à¶­ පූරණය කරන්න ද?</translation>
+<translation id="6857776781123259569">මුරපද කළමනà·à¶šà¶»à¶«à¶º කරන්න...</translation>
<translation id="686485648936420384">පà·à¶»à·’භà·à¶œà·’ක සම්පත්</translation>
<translation id="6865412394715372076">මෙම කà·à¶©à·Šà¶´à¶­ දà·à¶±à·Š තහවුරු කළ නොහà·à¶šà·’ය</translation>
<translation id="6869334554832814367">පුද්ගලික ණය</translation>
@@ -2153,7 +2165,6 @@
<translation id="6965978654500191972">උපà·à¶‚ගය</translation>
<translation id="696703987787944103">සංජà·à¶±à¶š</translation>
<translation id="6968269510885595029">ඔබගේ ආරක්ෂිත යතුර භà·à·€à·’ත කරන්න</translation>
-<translation id="6970216967273061347">දිස්ත්â€à¶»à·’ක්කය</translation>
<translation id="6971439137020188025">ඉක්මනින් Slides තුළ නව Google ඉදිරිපත් කිරීමක් තනන්න</translation>
<translation id="6972629891077993081">HID උපà·à¶‚ග</translation>
<translation id="6973656660372572881">ස්ථිර ප්â€à¶»à·œà¶šà·Šà·ƒà·’ සේවà·à¶¯à·à¶ºà¶š à·„à· .pac ස්ක්â€à¶»à·’ප්ට ලිපින සඳහන් කර ඇත</translation>
@@ -2192,7 +2203,6 @@
<translation id="7081308185095828845">මෙම විà·à·šà·‚à·à¶‚ගය ඔබේ උපà·à¶‚ගයේ නොලà·à¶¶à·š</translation>
<translation id="7083258188081898530">බඳුන 9</translation>
<translation id="7086090958708083563">පරිà·à·“ලකය෠විසින් ඉල්ලනු ලà·à¶¶à·– උඩුගත කිරීම</translation>
-<translation id="7087282848513945231">ප්â€à¶»à·à¶±à·Šà¶­à¶º</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome à·ƒà·à¶šà·ƒà·“ම් තුළ අඩවි හරහ෠ගබඩ෠කර ඇති අවසර සහ දත්ත කළමනà·à¶šà¶»à¶«à¶º කිරීමට Tab ඔබ෠අනතුරුව Enter ඔබන්න</translation>
<translation id="7096937462164235847">මෙම වෙබ් අඩවියේ අනන්â€à¶ºà¶­à·à·€ සත්â€à¶ºà·à¶´à¶±à¶º කර නà·à¶­.</translation>
<translation id="7101893872976785596">ත්â€à¶»à·à·ƒà¶¢à¶±à¶š චිත්â€à¶»à¶´à¶§</translation>
@@ -2211,10 +2221,10 @@
<ph name="LIST_ITEM" />පà·à¶»à¶¸à·€à¶½ ඇතුළත් කළ තොරතුරු<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">මෙම ලිපිනයට නà·à·€à·Šà¶œà¶­ කළ නොහà·à¶šà·’ය. වෙනත් ලිපිනයක් තà·à¶»à¶±à·Šà¶±.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ඔබගේ පරිපà·à¶½à¶š මෙම දත්ත පිටපත් කිරීම තහනම් කර ඇත.</translation>
<translation id="7135130955892390533">තත්ත්â€à·€à¶º පෙන්වන්න</translation>
<translation id="7138472120740807366">බෙද෠හà·à¶»à·“මේ ක්â€à¶»à¶¸à¶º</translation>
-<translation id="7139724024395191329">එමීර් රà·à¶¢à·Šâ€à¶º</translation>
<translation id="7139892792842608322">Primary Tray</translation>
<translation id="714064300541049402">පà·à¶­à·Šà¶­ 2 රූප X à·€à·à¶© මුරය</translation>
<translation id="7152423860607593928">Number-14 (ලියුම් කවරය)</translation>
@@ -2431,6 +2441,7 @@
<translation id="7669271284792375604">මෙම අඩවියෙහි ප්â€à¶»à·„à·à¶»à¶šà¶ºà¶±à·Š ඔබගේ බ්â€à¶»à·€à·”ස් කිරීමේ අත්දà·à¶šà·“මට à·„à·à¶±à·’ කරන ක්â€à¶»à¶¸à¶½à·šà¶› ස්ථà·à¶´à¶±à¶º කිරීමට ඔබව මුල෠කිරීමට උත්සà·à·„ කළ à·„à·à¶šà·’ය (උදà·à·„රණයක් à·€à·à¶ºà·™à¶±à·Š, ඔබගේ මුල් පිටුව වෙනස් කිරීමෙන් හ෠ඔබ පිවිසෙන අඩවිවල අමතර වෙළඳ දà·à¶±à·Šà·€à·“ම් පෙන්වීමෙන්).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{රහසිගත ලෙස ලකුණු කළ දත්ත සම්බන්ධව ගත් ක්â€à¶»à·’ය෠(පිරීමේ සිට ක්â€à¶»à·’ය෠1). <ph name="BEGIN_LINK" />තව දà·à¶± ගන්න<ph name="END_LINK" />}one{රහසිගත ලෙස ලකුණු කළ දත්ත සම්බන්ධව ගත් ක්â€à¶»à·’ය෠(පිරීමේ සිට ක්â€à¶»à·’ය෠#). <ph name="BEGIN_LINK" />තව දà·à¶± ගන්න<ph name="END_LINK" />}other{රහසිගත ලෙස ලකුණු කළ දත්ත සම්බන්ධව ගත් ක්â€à¶»à·’ය෠(පිරීමේ සිට ක්â€à¶»à·’ය෠#). <ph name="BEGIN_LINK" />තව දà·à¶± ගන්න<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">තà·à¶´à·à¶½à·Š පෙට්ටිය 6</translation>
+<translation id="7675325315208090829">ගෙවීමේ ක්â€à¶»à¶¸ කළමනà·à¶šà¶»à¶«à¶º කරන්න...</translation>
<translation id="7676643023259824263">පසුරු පුවරු පà·à¶¨à¶º සොයන්න, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Chrome à·ƒà·à¶šà·ƒà·“ම් තුළ ඔබගේ බ්â€à¶»à·€à·”ස් කිරීමේ ඉතිහà·à·ƒà¶º බලන්න සහ කළමනà·à¶šà¶»à¶«à¶º කරන්න</translation>
<translation id="7679947978757153706">බේස්බà·à¶½à·Š</translation>
@@ -2473,7 +2484,6 @@
<translation id="7766518757692125295">à·ƒà·à¶º</translation>
<translation id="7770259615151589601">නියම කළ දිග</translation>
<translation id="7773005668374414287">එකම පිළිවෙළ මුහුණු ඉහළට</translation>
-<translation id="777702478322588152">ප්â€à¶»à·à¶±à·Šà¶­à¶º</translation>
<translation id="7791011319128895129">මුද෠නොකළ</translation>
<translation id="7791196057686275387">මිටිය</translation>
<translation id="7791543448312431591">එක් කරන්න</translation>
@@ -2564,12 +2574,12 @@
<translation id="8055534648776115597">වෘත්තීය සහ අඛණ්ඩ අධ්â€à¶ºà·à¶´à¶±à¶º</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" නිවà·à¶»à¶¯à·’à·€ වින්â€à¶ºà·à·ƒ කර නොමà·à¶­. "<ph name="SOFTWARE_NAME" />" අස්ථà·à¶´à¶±à¶ºà·™à¶±à·Š à·ƒà·à¶¸à·à¶±à·Šâ€à¶ºà¶ºà·™à¶±à·Š ගà·à¶§à¶½à·”à·€ විසඳයි. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">අහà·à¶» නිෂ්පà·à¶¯à¶±à¶º</translation>
-<translation id="8066955247577885446">කනගà·à¶§à·”යි, යමක් à·€à·à¶»à¶¯à·’ණි.</translation>
<translation id="8067872629359326442">ඔබ මේ දà·à¶±à·Š වංචනික වෙබ් අඩවියක් මත ඔබේ මුරපදය ඇතුළත් කළà·. Chromium උදවු කළ හà·à¶š. ඔබේ මුරපදය වෙනස් කිරීමටත් ඔබේ ගිණුම අවදà·à¶±à¶¸à·š තිබිය à·„à·à¶šà·’ බව Google වෙත දà·à¶±à·”ම් දිමටත්, ගිණුම ආරක්â€à·‚෠කරන්න ක්ලික් කරන්න.</translation>
<translation id="8070439594494267500">යෙදුම් නිරූපකය</translation>
<translation id="8074253406171541171">10x13 (ලියුම් කවරය)</translation>
<translation id="8075736640322370409">ඉක්මනින් නව Google Sheet එකක් තනන්න</translation>
<translation id="8075898834294118863">අඩවි à·ƒà·à¶šà·ƒà·“ම් කළමනà·à¶šà¶»à¶«à¶º කරන්න</translation>
+<translation id="8076492880354921740">පටිති</translation>
<translation id="8078141288243656252">කරකà·à·€à·– විට අනුසටහන් කළ නොහà·à¶š</translation>
<translation id="8079031581361219619">අඩවිය නà·à·€à¶­ පූරණය කරන්න ද?</translation>
<translation id="8081087320434522107">සෙඩà·à¶±à·Š</translation>
@@ -2692,6 +2702,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">à·ƒà·à¶šà·ƒà·”ම්</translation>
+<translation id="8428634594422941299">තේරුණà·</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome à·ƒà·à¶šà·ƒà·“ම් තුළ ඔබගේ කුකි මනà·à¶´ කළමනà·à¶šà¶»à¶«à¶º කිරීමට Tab ඔබ෠අනතුරුව Enter ඔබන්න</translation>
<translation id="8433057134996913067">මෙය ඔබව බොහ෠අඩවිවලින් වරනු ඇත.</translation>
<translation id="8434840396568290395">සුරතලුන්</translation>
@@ -2789,6 +2800,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> යනු <ph name="ORIGIN" /> සඳහ෠ඔබේ කේතය වෙයි</translation>
<translation id="874918643257405732">මෙම පටිත්ත පිටුසන් කරන්න</translation>
<translation id="8751426954251315517">මීළඟ වරට නà·à·€à¶­ උත්සà·à·„ කරන්න</translation>
+<translation id="8757526089434340176">Google Pay දීමනà·à·€ ලබ෠ගත à·„à·à¶šà·’ය</translation>
<translation id="8758885506338294482">තරඟකà·à¶»à·“ වීඩිය෠ක්â€à¶»à·“ඩà·</translation>
<translation id="8759274551635299824">මෙම කà·à¶©à·Šà¶´à¶­ කල් ඉකුත් වී ඇත</translation>
<translation id="87601671197631245">මෙම වෙබ් අඩවිය යල් පà·à¶± ගිය ආරක්â€à·‚ක වින්â€à¶ºà·à·ƒà¶ºà¶šà·Š භà·à·€à·’ත කරයි, එමඟින් ඔබේ තොරතුරු (උදà·à·„රණයක් à·€à·à¶ºà·™à¶±à·Š මුරපද, පණිවුඩ, හ෠ණය කà·à¶©à·Šà¶´à¶­à·Š) මෙම වෙබ් අඩවියට යවන විට හෙළි කළ à·„à·à¶š.</translation>
@@ -2796,6 +2808,7 @@
<translation id="8763927697961133303">USB උපà·à¶‚ගය</translation>
<translation id="8763986294015493060">දà·à¶±à¶§ විවෘතව ඇති සියලු අප්â€à¶»à¶šà¶§ කවුළු වසන්න</translation>
<translation id="8766943070169463815">ආරක්ෂිත ගෙවීම් අක්තපත්â€à¶» සත්â€à¶ºà·à¶´à¶±à¶º කිරීමේ පත්â€à¶»à¶º විවෘත කර ඇත</translation>
+<translation id="8767765348545497220">උදවු බුබුළ වසන්න</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">යතුරුපà·à¶¯à·’</translation>
<translation id="8790007591277257123">මà·à¶šà·“ම &amp;යළි කරන්න</translation>
@@ -2808,6 +2821,7 @@
<translation id="8806285662264631610">නà·à¶± සහ à·à¶»à·“ර නිෂ්පà·à¶¯à¶±</translation>
<translation id="8807160976559152894">සෑම පිටුවකටම පසුව කප්පà·à¶¯à·” කරන්න</translation>
<translation id="8808828119384186784">Chrome à·ƒà·à¶šà·ƒà·“ම්</translation>
+<translation id="8813277370772331957">මට පසුව සිහිකà·à¶³à·€à¶±à·Šà¶±</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, ඔබගේ Chrome à·ƒà·à¶šà·ƒà·“ම් වෙතින් Chrome යà·à·€à¶­à·Šà¶šà·à¶½à·“න කිරීමට Tab ඔබ෠අනතුරුව Enter ඔබන්න</translation>
<translation id="8820817407110198400">පිටුසන්</translation>
<translation id="882338992931677877">Manual Slot</translation>
@@ -2987,6 +3001,7 @@
<translation id="988159990683914416">සංවර්ධක නිකුතුව</translation>
<translation id="989988560359834682">ලිපිනය සංස්කරණය කරන්න</translation>
<translation id="991413375315957741">චලන හ෠ආලà·à¶š සංවේදක</translation>
+<translation id="992110854164447044">විය à·„à·à¶šà·’ වංචà·à·€à·™à¶±à·Š ඔබව ආරක්ෂ෠කිරීමට උදවු කිරීමට අතථ්â€à¶º කà·à¶©à·Šà¶´à¶­à¶šà·Š ඔබගේ à·ƒà·à¶¶à·‘ කà·à¶©à·Šà¶´à¶­ සඟවයි. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">රà·à·ƒ</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" ඔබේ පරිගණකයේ හ෠ජà·à¶½à¶ºà·š නිසියà·à¶šà·à¶»à·€ ස්ථà·à¶´à·’ත කර නà·à¶­:
diff --git a/chromium/components/strings/components_strings_sk.xtb b/chromium/components/strings/components_strings_sk.xtb
index 5cb4c047e94..83ba257faf0 100644
--- a/chromium/components/strings/components_strings_sk.xtb
+++ b/chromium/components/strings/components_strings_sk.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Príspevky, Å¡tipendiá a finanÄná pomoc</translation>
<translation id="1048785276086539861">KeÄ upravíte poznámky, tento dokument sa vráti na zobrazenie jednej stránky</translation>
<translation id="1050038467049342496">Zavrite ostatné aplikácie</translation>
+<translation id="1053959602163383901">Na weboch využívajúcich poskytovateľa <ph name="PROVIDER_ORIGIN" /> ste vybrali overenie zariadením s overovateľom. Tento poskytovateľ mohol ukladať informácie o vašom spôsobe platby, o ktorých odstránenie môžete <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Vrátiť späť pridanie</translation>
<translation id="1056663316309890343">Softvér na spracovanie fotiek</translation>
<translation id="1056898198331236512">Upozornenie</translation>
@@ -119,6 +120,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="1270502636509132238">Spôsob vyzdvihnutia</translation>
<translation id="1281476433249504884">OdkladaÄ Ä. 1</translation>
<translation id="1285320974508926690">Nikdy neprekladať tieto webové stránky</translation>
+<translation id="1288548991597756084">Kartu bezpeÄne uložte</translation>
<translation id="1292571435393770077">Zásobník Ä. 16</translation>
<translation id="1292701964462482250">„Softvér vo vaÅ¡om poÄítaÄi bráni Chromu bezpeÄne sa pripojiÅ¥ k internetu“ (iba poÄítaÄe so systémom Windows)</translation>
<translation id="1294154142200295408">Variácie v príkazovom riadku</translation>
@@ -223,6 +225,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
&lt;p&gt;Ak ju chcete odstrániť, kliknite na možnosť &lt;strong&gt;Pripojiť&lt;/strong&gt; na stránke, ktorú sa pokúšate otvoriť.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Exteriérový dizajn</translation>
<translation id="1513706915089223971">Zoznam historických vstupov</translation>
+<translation id="1516097932025103760">Bude Å¡ifrovaná a bezpeÄne uložená. Overovací kód karty sa neukladá.</translation>
<translation id="1517433312004943670">Vyžaduje sa telefónne Äíslo</translation>
<translation id="1519264250979466059">Dátum zostavenia</translation>
<translation id="1521159554480556801">TkáÄske a textilné umenie</translation>
@@ -272,7 +275,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="1634828734222219955">Celkove</translation>
<translation id="163669211644121865">Daňová príprava a plánovanie</translation>
<translation id="1638780421120290329">Karta sa nedá uložiť</translation>
-<translation id="1639239467298939599">Prebieha naÄítavanie</translation>
+<translation id="1639239467298939599">NaÄítava sa</translation>
<translation id="1640180200866533862">Pravidlá pre používateľa</translation>
<translation id="1640244768702815859">Skúste <ph name="BEGIN_LINK" />navštíviť domovskú stránku webu<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">Odložiť výstup do</translation>
@@ -288,6 +291,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="1662550410081243962">Ukladať a dopĺňať spôsoby platby</translation>
<translation id="1663943134801823270">Karty a adresy pochádzajú z Chromu. Môžete ich spravovať v <ph name="BEGIN_LINK" />Nastaveniach<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Stránky v jazyku <ph name="SOURCE_LANGUAGE" /> budú odteraz prekladané do jazyka <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Ak chcete ponuku uplatniť, zaplaťte kartou <ph name="CARD_DETAIL" /></translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> – <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Napred krátkym okrajom</translation>
<translation id="168693727862418163">Túto hodnotu pravidla sa nepodarilo v jeho schéme overiť a bude ignorovaná.</translation>
@@ -306,6 +310,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="1717218214683051432">Senzory pohybu</translation>
<translation id="1717494416764505390">PoÅ¡tová schránka Ä. 3</translation>
<translation id="1718029547804390981">Dokument nie je možné anotovať, pretože je príliš veľký</translation>
+<translation id="1720941539803966190">Zavrieť návod</translation>
<translation id="1721424275792716183">* Toto pole je povinné</translation>
<translation id="1727613060316725209">Certifikát je platný</translation>
<translation id="1727741090716970331">Pridanie platného Äísla karty</translation>
@@ -418,8 +423,8 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="205212645995975601">Opekanie a grilovanie</translation>
<translation id="2053111141626950936">Stránky v jazyku <ph name="LANGUAGE" /> nebudú prekladané.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{KeÄ je tento ovládací prvok zapnutý a stav je aktívny, Chrome stanoví, ktorej veľkej skupine ľudí („kohorte“) je vaÅ¡a nedávna aktivita prehliadania najviac podobná. Inzerenti môžu pre túto skupinu vybraÅ¥ reklamy a vaÅ¡a aktivita prehliadania zostane uchovaná v súkromí vo vaÅ¡om zariadení. Skupina sa aktualizuje každý deň.}=1{KeÄ je tento ovládací prvok zapnutý a stav je aktívny, Chrome stanoví, ktorej veľkej skupine ľudí („kohorte“) je vaÅ¡a nedávna aktivita prehliadania najviac podobná. Inzerenti môžu pre túto skupinu vybraÅ¥ reklamy a vaÅ¡a aktivita prehliadania zostane uchovaná v súkromí vo vaÅ¡om zariadení. Skupina sa aktualizuje každý deň.}few{KeÄ je tento ovládací prvok zapnutý a stav je aktívny, Chrome stanoví, ktorej veľkej skupine ľudí („kohorte“) je vaÅ¡a nedávna aktivita prehliadania najviac podobná. Inzerenti môžu pre túto skupinu vybraÅ¥ reklamy a vaÅ¡a aktivita prehliadania zostane uchovaná v súkromí vo vaÅ¡om zariadení. Skupina sa aktualizuje každé {NUM_DAYS} dni.}many{KeÄ je tento ovládací prvok zapnutý a stav je aktívny, Chrome stanoví, ktorej veľkej skupine ľudí („kohorte“) je vaÅ¡a nedávna aktivita prehliadania najviac podobná. Inzerenti môžu pre túto skupinu vybraÅ¥ reklamy a vaÅ¡a aktivita prehliadania zostane uchovaná v súkromí vo vaÅ¡om zariadení. Skupina sa aktualizuje každého {NUM_DAYS} dňa.}other{KeÄ je tento ovládací prvok zapnutý a stav je aktívny, Chrome stanoví, ktorej veľkej skupine ľudí („kohorte“) je vaÅ¡a nedávna aktivita prehliadania najviac podobná. Inzerenti môžu pre túto skupinu vybraÅ¥ reklamy a vaÅ¡a aktivita prehliadania zostane uchovaná v súkromí vo vaÅ¡om zariadení. Skupina sa aktualizuje každých {NUM_DAYS} dní.}}</translation>
-<translation id="2053553514270667976">PSČ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 návrh}few{# návrhy}many{# návrhu}other{# návrhov}}</translation>
+<translation id="2066915425250589881">požiadať</translation>
<translation id="2068528718802935086">Bábätká a batoľatá</translation>
<translation id="2071156619270205202">Táto karta nespĺňa podmienky na pridelenie Äísla virtuálnej karty.</translation>
<translation id="2071692954027939183">Upozornenia boli automaticky blokované, pretože ich zvyÄajne nepovoľujete</translation>
@@ -431,7 +436,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="2088086323192747268">TlaÄidlo SpravovaÅ¥ synchronizáciu. StlaÄením klávesa Enter môžete v nastaveniach Chromu spravovaÅ¥, aké údaje sa synchronizujú.</translation>
<translation id="2091887806945687916">Zvuk</translation>
<translation id="2094505752054353250">Domény sa nezhodujú</translation>
-<translation id="2096368010154057602">Department</translation>
<translation id="2099652385553570808">Tri spinky vľavo</translation>
<translation id="2101225219012730419">Verzia:</translation>
<translation id="2102134110707549001">Navrhnúť silné heslo…</translation>
@@ -468,7 +472,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="2185836064961771414">Americký futbal</translation>
<translation id="2187317261103489799">Rozpoznávať (predvolené)</translation>
<translation id="2188375229972301266">Viacero dierok dole</translation>
-<translation id="2188852899391513400">Heslo, ktoré ste práve použili, bolo nájdené v zozname hesiel odhalených pri poruÅ¡ení ochrany údajov. Ak chcete svoje úÄty zabezpeÄiÅ¥, správca hesiel od Googlu odporúÄa okamžite ho zmeniÅ¥ a uložené heslá skontrolovaÅ¥.</translation>
<translation id="219906046732893612">Domáci majstri</translation>
<translation id="2202020181578195191">Zadajte platný rok vypršania platnosti</translation>
<translation id="22081806969704220">Priehradka Ä. 3</translation>
@@ -479,6 +482,8 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="2215727959747642672">Úprava súborov</translation>
<translation id="2215963164070968490">Psy</translation>
<translation id="2218879909401188352">ÚtoÄníci na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> by mohli nainÅ¡talovaÅ¥ nebezpeÄné aplikácie, ktoré poÅ¡kodia vaÅ¡e zariadenia, pridaÅ¥ skryté poplatky do vaÅ¡ej faktúry za mobilné služby alebo ukradnúť vaÅ¡e osobné informácie. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Reštartovať návod</translation>
+<translation id="2219735899272417925">Vyžaduje sa resetovanie zariadenia</translation>
<translation id="2224337661447660594">Bez internetu</translation>
<translation id="2230458221926704099">Opravte svoje pripojenie pomocou <ph name="BEGIN_LINK" />diagnostickej aplikácie<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Odoslať</translation>
@@ -576,11 +581,13 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="2512101340618156538">Nepovolené (predvolené)</translation>
<translation id="2512413427717747692">TlaÄidlo nastavenia Chromu ako predvoleného prehliadaÄa, stlaÄením klávesa Enter nastavíte Chrome ako predvolený prehliadaÄ systému v nastaveniach iOS</translation>
<translation id="2515629240566999685">Skontrolovať signál vo vašej oblasti</translation>
+<translation id="2515761554693942801">Na weboch využívajúcich poskytovateľa <ph name="PROVIDER_ORIGIN" /> ste vybrali overenie pomocou funkcie Touch ID. Tento poskytovateľ mohol ukladať informácie o vašom spôsobe platby, o ktorých odstránenie môžete <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Zošiť spinkou vpravo dolu</translation>
<translation id="2521736961081452453">Vytvoriť formulár</translation>
<translation id="2523886232349826891">Uložené iba na tomto zariadení</translation>
<translation id="2524461107774643265">Pridanie Äalších informácií</translation>
<translation id="2529899080962247600">Toto pole by nemalo maÅ¥ viac ako tento poÄet vstupov: <ph name="MAX_ITEMS_LIMIT" />. VÅ¡etky ÄalÅ¡ie vstupy budú ignorované.</translation>
+<translation id="253493526287553278">Zobraziť podrobnosti promo kódov</translation>
<translation id="2535585790302968248">Otvoriť novú kartu inkognito a prehliadať v súkromí</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{a 1 ÄalÅ¡ia}few{a # ÄalÅ¡ie}many{and # more}other{a # Äalších}}</translation>
<translation id="2536110899380797252">Pridať adresu</translation>
@@ -616,7 +623,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="259821504105826686">Fotografické a digitálne umenie</translation>
<translation id="2601150049980261779">Romantické filmy</translation>
<translation id="2604589665489080024">Popová hudba</translation>
-<translation id="2609632851001447353">Variácie</translation>
<translation id="2610561535971892504">Skopírovať kliknutím</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />neuloží<ph name="END_EMPHASIS" /> nasledujúce informácie:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Narodeniny a meniny</translation>
<translation id="2677748264148917807">Odísť</translation>
+<translation id="2679714844901977852">Uložte si informácie o karte a fakturaÄné údaje do svojho úÄtu Google (<ph name="USER_EMAIL" />), aby ste mohli platiÅ¥ bezpeÄnejÅ¡ie a rýchlejÅ¡ie</translation>
<translation id="2684561033061424857">11 x 12</translation>
<translation id="2687555958734450033">Prispôsobená veľkosť</translation>
<translation id="2688969097326701645">Ãno, pokraÄovaÅ¥</translation>
@@ -697,7 +704,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="2854764410992194509">Poskytovatelia internetu</translation>
<translation id="2856444702002559011">ÚtoÄníci sa môžu pokúsiÅ¥ ukradnúť vaÅ¡e informácie z webu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (napríklad heslá, správy alebo kreditné karty). <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Tento web zobrazuje obťažujúce alebo zavádzajúce reklamy.</translation>
-<translation id="286512204874376891">Virtuálnou kartou sa zamaskuje vaÅ¡a skutoÄná karta, Äo vás lepÅ¡ie ochráni pred potenciálnymi podvodmi. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Milé</translation>
<translation id="28761159517501904">Filmy</translation>
<translation id="2876489322757410363">Ak zaplatíte pomocou externej aplikácie, opustíte režim inkognito. Chcete pokraÄovaÅ¥?</translation>
@@ -798,7 +804,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">Sieť Wi‑Fi, ktorú používate, môže vyžadovať, aby ste navštívili jej prihlasovaciu stránku</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ostrov</translation>
<translation id="3176929007561373547">Skontrolujte nastavenia proxy servera alebo kontaktujte správcu siete a požiadajte ho, aby skontroloval, Äi proxy server funguje. Ak sa domnievate, že by ste nemali používaÅ¥ proxy server: <ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">Zistiť, kedy aktívne používate toto zariadenie</translation>
<translation id="3180358318770512945">RodiÄovstvo</translation>
@@ -874,9 +879,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="3369192424181595722">Chyba hodín</translation>
<translation id="3369459162151165748">Diely a príslušenstvo pre vozidlá</translation>
<translation id="3371064404604898522">NastaviÅ¥ Chrome ako predvolený prehliadaÄ</translation>
-<translation id="3371076217486966826"><ph name="URL" /> chce:
- • vytvoriť 3D mapu vášho okolia a sledovať umiestnenia kamier;
- • používať vašu kameru.</translation>
<translation id="337363190475750230">Odstránené</translation>
<translation id="3375754925484257129">SpustiÅ¥ kontrolu bezpeÄnosti Chromu</translation>
<translation id="3377144306166885718">Daný server používa zastaranú verziu protokolu TLS.</translation>
@@ -893,6 +895,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="3399952811970034796">DoruÄovacia adresa</translation>
<translation id="3402261774528610252">Pripojenie, pomocou ktorého bol naÄítaný tento web, používa protokol TLS 1.0 alebo TLS 1.1, ktorého podpora je zastaraná a ktorý bude v budúcnosti zakázaný. Po jeho zakázaní už používatelia nebudú môcÅ¥ tento web naÄítaÅ¥. Daný server by mal aktivovaÅ¥ protokol TLS verzie 1.2 alebo novÅ¡ej.</translation>
<translation id="3405664148539009465">Prispôsobiť písma</translation>
+<translation id="3407789382767355356">prihlásenie tretej strany</translation>
<translation id="3409896703495473338">SpravovaÅ¥ nastavenia zabezpeÄenia</translation>
<translation id="3414952576877147120">Veľkosť:</translation>
<translation id="3417660076059365994">Súbory, ktoré si nahráte alebo priložíte, sa odosielajú na analýzu do služby Google Cloud alebo tretím stranám. Môže sa v nich napríklad kontrolovať prítomnosť citlivých údajov alebo malvéru.</translation>
@@ -925,6 +928,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="3477679029130949506">Zoznamy filmov a Äasy premietania</translation>
<translation id="3479552764303398839">Teraz nie</translation>
<translation id="3484560055331845446">Môžete stratiÅ¥ prístup do úÄtu Google. Chrome odporúÄa, aby ste si ihneÄ zmenili heslo. Zobrazí sa výzva na prihlásenie.</translation>
+<translation id="3484861421501147767">Pripomenutie: k dispozícii je uložený promo kód</translation>
<translation id="3487845404393360112">Priehradka Ä. 4</translation>
<translation id="3495081129428749620">Nájsť na stránke
<ph name="PAGE_TITLE" /></translation>
@@ -1048,6 +1052,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="3810973564298564668">Spravovať</translation>
<translation id="3816482573645936981">Hodnota (nahradená)</translation>
<translation id="382518646247711829">Ak používate server proxy...</translation>
+<translation id="3826050100957962900">Prihlásenie tretej strany</translation>
<translation id="3827112369919217609">Absolútne</translation>
<translation id="3827666161959873541">Rodinné filmy</translation>
<translation id="3828924085048779000">Prístupová fráza nemôže byť prázdna.</translation>
@@ -1060,9 +1065,9 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="3858027520442213535">AktualizovaÅ¥ dátum a Äas</translation>
<translation id="3858860766373142691">Názov</translation>
<translation id="3872834068356954457">Veda</translation>
+<translation id="3875783148670536197">Ukázať postup</translation>
<translation id="3881478300875776315">Zobraziť menej riadkov</translation>
<translation id="3884278016824448484">Kolidujúci identifikátor zariadenia</translation>
-<translation id="3885155851504623709">Farnosť</translation>
<translation id="388632593194507180">Bolo rozpoznané monitorovanie</translation>
<translation id="3886948180919384617">OdkladaÄ Ä. 3</translation>
<translation id="3890664840433101773">Pridanie e-mailu</translation>
@@ -1092,9 +1097,11 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="3973234410852337861">Web <ph name="HOST_NAME" /> je zablokovaný</translation>
<translation id="3978338123949022456">Režim vyhľadávania, po zadaní dopytu a stlaÄení klávesa Enter spustíte vyhľadávanie prostredníctvom služby <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Vtáky</translation>
+<translation id="3985750352229496475">Spravujte adresy…</translation>
<translation id="3986705137476756801">Zapnúť zatiaľ živý prepis</translation>
<translation id="3987940399970879459">Menej ako 1 MB</translation>
<translation id="3990250421422698716">Odsadenie zarovnania okrajov</translation>
+<translation id="3992684624889376114">Táto stránka</translation>
<translation id="3996311196211510766">Web <ph name="ORIGIN" /> požiadal, aby sa na všetky jeho požiadavky vzťahovalo pravidlo pre zdroj, ale toto pravidlo sa momentálne nedá uplatniť.</translation>
<translation id="4006465311664329701">Spôsoby platby, ponuky a adresy používajúce Google Pay</translation>
<translation id="4009243425692662128">Obsah stránok, ktoré vytlaÄíte, sa odosiela na analýzu do služby Google Cloud alebo tretím stranám. Môže sa v ňom napríklad kontrolovaÅ¥ prítomnosÅ¥ citlivých údajov.</translation>
@@ -1214,6 +1221,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="4305666528087210886">Súbor sa nepodarilo otvoriť</translation>
<translation id="4306529830550717874">Chcete adresu uložiť?</translation>
<translation id="4306812610847412719">schránka</translation>
+<translation id="4310070645992025887">Vyhľadávajte cesty</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokovať (predvolené)</translation>
<translation id="4314815835985389558">Správa synchronizácie</translation>
@@ -1264,6 +1272,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="4435702339979719576">pohľadnica)</translation>
<translation id="443673843213245140">Použitie servera proxy je zakázané, ale je urÄená explicitná konfigurácia servera proxy.</translation>
<translation id="4441832193888514600">Ignorované, pretože pravidlá sa dajú nastaviť iba pravidlami cloudu pre používateľov.</translation>
+<translation id="4442470707340296952">Karty Chromu</translation>
<translation id="4450893287417543264">Nabudúce nezobrazovať</translation>
<translation id="4451135742916150903">Môže žiadať o povolenie pripájať sa k zariadeniam HID</translation>
<translation id="4452328064229197696">Heslo, ktoré ste práve použili, bolo nájdené v zozname hesiel odhalených pri poruÅ¡ení ochrany údajov. Ak chcete svoje úÄty zabezpeÄiÅ¥, správca hesiel od Googlu odporúÄa skontrolovaÅ¥ uložené heslá.</translation>
@@ -1402,6 +1411,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="483241715238664915">Zapnúť upozornenia</translation>
<translation id="4834250788637067901">Spôsoby platby, ponuky a adresy používajúce Google Pay</translation>
<translation id="4838327282952368871">Rozprávkové</translation>
+<translation id="4839087176073128681">NajlepÅ¡ie zabezpeÄenie v odvetví od Googlu vám umožní nabudúce zaplatiÅ¥ rýchlejÅ¡ie a ochráni vaÅ¡u kartu.</translation>
<translation id="4840250757394056958">Zobraziť históriu Chromu</translation>
<translation id="484462545196658690">Automaticky</translation>
<translation id="484671803914931257">Získajte zľavu u obchodníka <ph name="MERCHANT_NAME" /> a Äalších</translation>
@@ -1464,6 +1474,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="5011561501798487822">Rozpoznaný jazyk</translation>
<translation id="5015510746216210676">Názov poÄítaÄa:</translation>
<translation id="5017554619425969104">Skopírovaný text</translation>
+<translation id="5017828934289857214">Pripomenúť neskôr</translation>
<translation id="5018422839182700155">Táto stránka sa nedá otvoriť</translation>
<translation id="5019198164206649151">Zlý stav záložného ukladacieho priestoru</translation>
<translation id="5020776957610079374">Svetová hudba</translation>
@@ -1483,6 +1494,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="5051305769747448211">Živé komédie</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Ak chcete tento súbor poslať pomocou Zdieľania nablízku, uvoľnite vo svojom zariadení priestor (<ph name="DISK_SPACE_SIZE" />)}few{Ak chcete tieto súbory poslať pomocou Zdieľania nablízku, uvoľnite vo svojom zariadení priestor (<ph name="DISK_SPACE_SIZE" />)}many{To send these files using Nearby Share, free up space (<ph name="DISK_SPACE_SIZE" />) on your device}other{Ak chcete tieto súbory poslať pomocou Zdieľania nablízku, uvoľnite vo svojom zariadení priestor (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">Články pre vás</translation>
+<translation id="5060483733937416656">Na weboch využívajúcich poskytovateľa <ph name="PROVIDER_ORIGIN" /> ste vybrali overenie pomocou funkcie Windows Hello. Tento poskytovateľ mohol ukladať informácie o vašom spôsobe platby, o ktorých odstránenie môžete <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Mysleli ste <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">História tlaÄe</translation>
<translation id="5068234115460527047">Hedžové fondy</translation>
@@ -1496,10 +1508,8 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="5087286274860437796">Certifikát servera je momentálne neplatný</translation>
<translation id="5087580092889165836">Pridať kartu</translation>
<translation id="5088142053160410913">Správa operátorovi</translation>
-<translation id="5089810972385038852">Štát</translation>
<translation id="5093232627742069661">Záhyb v tvare písmena Z</translation>
<translation id="5094747076828555589">Server nedokáže overiÅ¥, Äi ide o doménu <ph name="DOMAIN" />, Chromium nedôveruje jej bezpeÄnostnému certifikátu. Môže to byÅ¥ spôsobené nesprávnou konfiguráciou alebo tým, že vaÅ¡e pripojenie zachytil útoÄník.</translation>
-<translation id="5095208057601539847">Provincia</translation>
<translation id="5097099694988056070">Štatistiky zariadenia, ako napríklad využitie procesora alebo pamäte RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Web nie je zabezpeÄený</translation>
@@ -1537,6 +1547,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="5171045022955879922">Vyhľadajte alebo zadajte webovú adresu</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">PoÄítaÄ</translation>
+<translation id="5177076414499237632">Informácie o zdroji a téme tejto stránky</translation>
<translation id="5179510805599951267">Text sa nezobrazil v jazyku <ph name="ORIGINAL_LANGUAGE" />? Nahláste túto chybu</translation>
<translation id="518639307526414276">Potrava a doplnky na starostlivosÅ¥ o domácich miláÄikov</translation>
<translation id="5190835502935405962">Panel so záložkami</translation>
@@ -1697,6 +1708,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="5624120631404540903">Správa hesiel</translation>
<translation id="5629630648637658800">Nastavenia pravidla sa nepodarilo naÄítaÅ¥</translation>
<translation id="5631439013527180824">Neplatný token správy zariadenia</translation>
+<translation id="5632485077360054581">Ukázať postup</translation>
<translation id="5633066919399395251">ÚtoÄníci na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sa môžu pokúsiÅ¥ nainÅ¡talovaÅ¥ nebezpeÄné programy vo vaÅ¡om poÄítaÄi, pomocou ktorých ukradnú alebo odstránia informácie (napríklad fotky, heslá, správy a kreditné karty). <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Klamlivý obsah bol zablokovaný.</translation>
<translation id="5633259641094592098">Kultové a indie filmy</translation>
@@ -1814,6 +1826,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="5989320800837274978">Nie sú urÄené pevne dané servery proxy ani skript PAC webovej adresy.</translation>
<translation id="5992691462791905444">Inžiniersky záhyb v tvare písmena Z</translation>
<translation id="5995727681868049093">SpravovaÅ¥ v úÄte Google informácie, ochranu súkromia a zabezpeÄenie</translation>
+<translation id="5997247540087773573">Heslo, ktoré ste práve použili, bolo nájdené v zozname hesiel odhalených pri poruÅ¡ení ochrany údajov. Ak chcete svoje úÄty zabezpeÄiÅ¥, Správca hesiel Google odporúÄa heslo okamžite zmeniÅ¥ a skontrolovaÅ¥ uložené heslá.</translation>
<translation id="6000758707621254961">Viacero výsledkov (<ph name="RESULT_COUNT" />) pre hľadaný výraz „<ph name="SEARCH_TEXT" />“</translation>
<translation id="6006484371116297560">Klasický</translation>
<translation id="6008122969617370890">Poradie N až 1</translation>
@@ -1908,7 +1921,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="627746635834430766">Ak chcete nabudúce zaplatiÅ¥ rýchlejÅ¡ie, uložte si kartu a fakturaÄnú adresu do úÄtu Google.</translation>
<translation id="6279183038361895380">StlaÄením klávesa |<ph name="ACCELERATOR" />| zobrazíte kurzor</translation>
<translation id="6280223929691119688">DoruÄenie na túto adresu nie je možné. Vyberte inú adresu.</translation>
-<translation id="6282194474023008486">PoÅ¡tové smerovacie Äíslo</translation>
<translation id="6285507000506177184">TlaÄidlo na správu stiahnutých súborov v Chrome, stlaÄením klávesa Enter spravujte súbory stiahnuté v Chrome</translation>
<translation id="6289939620939689042">Farba stránky</translation>
<translation id="6290238015253830360">Tu sa zobrazia vaÅ¡e navrhované Älánky</translation>
@@ -1930,6 +1942,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="6337133576188860026">Uvoľní menej ako <ph name="SIZE" />. Niektoré weby sa môžu pri ÄalÅ¡ej návÅ¡teve naÄítaÅ¥ pomalÅ¡ie.</translation>
<translation id="6337534724793800597">Filtrovať pravidlá podľa mena</translation>
<translation id="6340739886198108203">Pravidlá správcu neodporúÄajú vytváranie snímok obrazovky alebo nahrávok, keÄ je viditeľný dôverný obsah:</translation>
+<translation id="6348220984832452017">Aktívne variácie</translation>
<translation id="6349101878882523185">Inštalovať aplikáciu <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Žiadne}=1{Jedno heslo (pre zoznam domén <ph name="DOMAIN_LIST" />, synchronizované)}=2{Dve heslá (pre zoznam domén <ph name="DOMAIN_LIST" />, synchronizované)}few{# heslá (pre zoznam domén <ph name="DOMAIN_LIST" />, synchronizované)}many{# passwords (for <ph name="DOMAIN_LIST" />, synced)}other{# hesiel (pre zoznam domén <ph name="DOMAIN_LIST" />, synchronizovaných)}}</translation>
<translation id="6355392890578844978">Tento prehliadaÄ nespravuje firma ani iná organizácia. Aktivita v tomto zariadení môže byÅ¥ spravovaná mimo prehliadaÄa Chromium. <ph name="BEGIN_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LINK" /></translation>
@@ -1961,7 +1974,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="643051589346665201">Zmeniť heslo Google</translation>
<translation id="6433490469411711332">Úprava kontaktných informácií</translation>
<translation id="6433595998831338502">Web <ph name="HOST_NAME" /> zamietol pripojenie.</translation>
-<translation id="6438025220577812695">Zmením ho ruÄne</translation>
<translation id="6440503408713884761">Ignorované</translation>
<translation id="6443406338865242315">Ktoré rozšírenia a doplnky máte nainštalované</translation>
<translation id="6446163441502663861">Kahu (obálka)</translation>
@@ -2091,9 +2103,9 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Preložiť</translation>
<translation id="6833752742582340615">Uložte si informácie o karte a fakturaÄné údaje do svojho úÄtu Google, aby ste mohli platiÅ¥ bezpeÄnejÅ¡ie a rýchlejÅ¡ie</translation>
-<translation id="6839929833149231406">Oblasť</translation>
<translation id="6846340164947227603">PoužiÅ¥ Äíslo virtuálnej karty…</translation>
<translation id="6852204201400771460">NaÄítaÅ¥ znova aplikáciu?</translation>
+<translation id="6857776781123259569">Spravujte heslá…</translation>
<translation id="686485648936420384">Zdroje informácií pre spotrebiteľov</translation>
<translation id="6865412394715372076">Táto karta sa momentálne nedá overiť</translation>
<translation id="6869334554832814367">Osobné úvery</translation>
@@ -2142,7 +2154,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="6965978654500191972">Zariadenie</translation>
<translation id="696703987787944103">PercepÄné</translation>
<translation id="6968269510885595029">Použite svoj bezpeÄnostný kľúÄ</translation>
-<translation id="6970216967273061347">Okres</translation>
<translation id="6971439137020188025">Rýchlo vytvoriť novú prezentáciu Google v Prezentáciách</translation>
<translation id="6972629891077993081">Zariadenia HID</translation>
<translation id="6973656660372572881">UrÄené sú pevne dané servery proxy aj skript PAC webovej adresy.</translation>
@@ -2181,7 +2192,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="7081308185095828845">Táto funkcia nie je vo vašom zariadení k dispozícii</translation>
<translation id="7083258188081898530">Priehradka Ä. 9</translation>
<translation id="7086090958708083563">O nahranie požiadal používateľ</translation>
-<translation id="7087282848513945231">Okres</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, postupným stlaÄením klávesov Tab a Enter môžete v nastaveniach Chromu spravovaÅ¥ povolenia a údaje uložené na rôznych weboch</translation>
<translation id="7096937462164235847">Identita tohto webu nie je overená.</translation>
<translation id="7101893872976785596">Horory</translation>
@@ -2200,10 +2210,10 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<ph name="LIST_ITEM" />Informácie zadané vo formulároch<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Dodanie na túto adresu nie je možné. Vyberte inú adresu.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Váš správca zakázal kopírovanie týchto údajov.</translation>
<translation id="7135130955892390533">Zobraziť stav</translation>
<translation id="7138472120740807366">Spôsob doruÄenia</translation>
-<translation id="7139724024395191329">Emirát</translation>
<translation id="7139892792842608322">Hlavný zásobník</translation>
<translation id="714064300541049402">Posun obrázka strany Ä. 2 na osi X</translation>
<translation id="7152423860607593928">Number-14 (obálka)</translation>
@@ -2420,6 +2430,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="7669271284792375604">ÚtoÄníci na tomto webe sa vás môžu pokúsiÅ¥ podvodom presvedÄiÅ¥, aby ste si nainÅ¡talovali programy poÅ¡kodzujúce vaÅ¡e prostredie prehliadania (napríklad zmenou domovskej stránky alebo zobrazovaním Äalších reklám na weboch, ktoré navÅ¡tevujete).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Akcie vykonané s údajmi oznaÄenými ako dôvernými (1 akcia od prihlásenia). <ph name="BEGIN_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LINK" />}few{Akcie vykonané s údajmi oznaÄenými ako dôvernými (# akcie od prihlásenia). <ph name="BEGIN_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LINK" />}many{Actions taken with data flagged as confidential (# actions since login). <ph name="BEGIN_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LINK" />}other{Akcie vykonané s údajmi oznaÄenými ako dôvernými (# akcií od prihlásenia). <ph name="BEGIN_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">PoÅ¡tová schránka Ä. 6</translation>
+<translation id="7675325315208090829">Spravujte spôsoby platby…</translation>
<translation id="7676643023259824263">Vyhľadať v schránke text <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Zobraziť a spravovať históriu prehliadania v nastaveniach Chromu</translation>
<translation id="7679947978757153706">Bejzbal</translation>
@@ -2462,7 +2473,6 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="7766518757692125295">Lem</translation>
<translation id="7770259615151589601">UrÄené – dlhé</translation>
<translation id="7773005668374414287">V rovnakom poradí lícom nahor</translation>
-<translation id="777702478322588152">Prefektúra</translation>
<translation id="7791011319128895129">Nevydané</translation>
<translation id="7791196057686275387">Zviazať do balíka</translation>
<translation id="7791543448312431591">Pridať</translation>
@@ -2553,12 +2563,12 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="8055534648776115597">Odborné a celoživotné vzdelávanie</translation>
<translation id="8057711352706143257">Softvér <ph name="SOFTWARE_NAME" /> nie je správne nakonfigurovaný. Tento problém sa zvyÄajne odstráni odinÅ¡talovaním softvéru <ph name="SOFTWARE_NAME" />. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Výroba jedál</translation>
-<translation id="8066955247577885446">Je nám to ľúto, ale vyskytol sa problém.</translation>
<translation id="8067872629359326442">Práve ste zadali svoje heslo na podvodnom webe. Chromium vám s tým pomôže. Ak chcete zmeniÅ¥ heslo a upozorniÅ¥ Google, že váš úÄet môže byÅ¥ ohrozený, kliknite na OchrániÅ¥ úÄet.</translation>
<translation id="8070439594494267500">Ikona aplikácie</translation>
<translation id="8074253406171541171">10x13 (obálka)</translation>
<translation id="8075736640322370409">Rýchlo vytvoriť novú tabuľku Google</translation>
<translation id="8075898834294118863">Spravovať nastavenia webov</translation>
+<translation id="8076492880354921740">Karty</translation>
<translation id="8078141288243656252">Po otoÄení dokumentu nie je anotácia možná</translation>
<translation id="8079031581361219619">Znova naÄítaÅ¥ web?</translation>
<translation id="8081087320434522107">Sedany</translation>
@@ -2681,6 +2691,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Nastavenia</translation>
+<translation id="8428634594422941299">Dobre</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, postupným stlaÄením klávesov Tab a Enter môžete v nastaveniach Chromu spravovaÅ¥ predvoľby súborov cookie</translation>
<translation id="8433057134996913067">Táto možnosÅ¥ vás odhlási z väÄÅ¡iny webov.</translation>
<translation id="8434840396568290395">Domáce zvieratá</translation>
@@ -2779,6 +2790,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> je kód pre <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Pridať kartu medzi záložky</translation>
<translation id="8751426954251315517">Skúste to neskôr</translation>
+<translation id="8757526089434340176">K dispozícii je ponuka Google Pay</translation>
<translation id="8758885506338294482">Súťažné hranie videohier</translation>
<translation id="8759274551635299824">Platnosť tejto karty vypršala</translation>
<translation id="87601671197631245">Tento web používa zastaranú konfiguráciu zabezpeÄenia, ktorá môže odhaliÅ¥ vaÅ¡e informácie (napríklad heslá, správy alebo kreditné karty), keÄ ich naň odoÅ¡lete.</translation>
@@ -2786,6 +2798,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="8763927697961133303">Zariadenie USB</translation>
<translation id="8763986294015493060">Zavrite všetky okná inkognito, ktoré sú momentálne otvorené</translation>
<translation id="8766943070169463815">Hárok overenia poverenia zabezpeÄenej platby je otvorený</translation>
+<translation id="8767765348545497220">Zavrieť bublinu pomocníka</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocykle</translation>
<translation id="8790007591277257123">&amp;Znova vymazať</translation>
@@ -2798,6 +2811,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="8806285662264631610">Výrobky do kúpeľa a na starostlivosť o telo</translation>
<translation id="8807160976559152894">Orezať po každej strane</translation>
<translation id="8808828119384186784">Nastavenia Chromu</translation>
+<translation id="8813277370772331957">Pripomenúť neskôr</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, postupným stlaÄením klávesov Tab a Enter aktualizujte Chrome v jeho nastaveniach</translation>
<translation id="8820817407110198400">Záložky</translation>
<translation id="882338992931677877">Manuálny slot</translation>
@@ -2977,6 +2991,7 @@ Ich používanie by bolo inak blokované vašimi nastaveniami ochrany súkromia.
<translation id="988159990683914416">Zostavenie pre vývojárov</translation>
<translation id="989988560359834682">Úprava adresy</translation>
<translation id="991413375315957741">senzory pohybu alebo svetla</translation>
+<translation id="992110854164447044">Virtuálnou kartou sa zamaskuje vaÅ¡a skutoÄná karta, Äo vás lepÅ¡ie ochráni pred potenciálnymi podvodmi. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Ružová</translation>
<translation id="992432478773561401">Softvér <ph name="SOFTWARE_NAME" /> nebol v poÄítaÄi alebo sieti riadne nainÅ¡talovaný:
diff --git a/chromium/components/strings/components_strings_sl.xtb b/chromium/components/strings/components_strings_sl.xtb
index 725e63712e0..820a097aeb9 100644
--- a/chromium/components/strings/components_strings_sl.xtb
+++ b/chromium/components/strings/components_strings_sl.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Å tipendije in finanÄna pomoÄ</translation>
<translation id="1048785276086539861">ÄŒe boste urejali pripise, se bo ta dokument vrnil v pogled ene strani.</translation>
<translation id="1050038467049342496">Zaprite druge aplikacije</translation>
+<translation id="1053959602163383901">Na spletnih mestih, ki uporabljajo <ph name="PROVIDER_ORIGIN" />, ste izbrali preverjanje z overiteljsko napravo. Ta ponudnik je morda shranil podatke o vaÅ¡em plaÄilnem sredstvu, za katere lahko <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Razveljavi dodajanje</translation>
<translation id="1056663316309890343">Programska oprema za fotografije</translation>
<translation id="1056898198331236512">Opozorilo</translation>
@@ -119,6 +120,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="1270502636509132238">NaÄin prevzema</translation>
<translation id="1281476433249504884">Zlagalnik 1</translation>
<translation id="1285320974508926690">Nikoli ne prevedi tega spletnega mesta</translation>
+<translation id="1288548991597756084">Varno shranite kartico</translation>
<translation id="1292571435393770077">Pladenj 16</translation>
<translation id="1292701964462482250">»Programska oprema v raÄunalniku Chromu prepreÄuje vzpostavitev varne povezave s spletom« (samo raÄunalniki s sistemom Windows)</translation>
<translation id="1294154142200295408">RazliÄice ukazne vrstice</translation>
@@ -223,6 +225,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
&lt;p&gt;Napravo odpravite tako, da na strani, ki jo poskušate odpreti, kliknete &lt;strong&gt;Poveži&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Krajinska arhitektura</translation>
<translation id="1513706915089223971">Seznam vnosov v zgodovino</translation>
+<translation id="1516097932025103760">Podatki o kartici bodo Å¡ifrirani in varno shranjeni, CVC pa se nikoli ne shrani.</translation>
<translation id="1517433312004943670">Telefonska Å¡tevilka je obvezna</translation>
<translation id="1519264250979466059">Datum gradnje</translation>
<translation id="1521159554480556801">Umetniški izdelki iz volne in blaga</translation>
@@ -288,6 +291,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="1662550410081243962">Shranjevanje in izpolnjevanje podatkov o plaÄilnih sredstvih</translation>
<translation id="1663943134801823270">Kartice in naslovi so iz Chroma. Upravljate jih lahko v <ph name="BEGIN_LINK" />nastavitvah<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Strani v jeziku <ph name="SOURCE_LANGUAGE" /> bodo odslej prevedene v jezik <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Če želite izkoristiti ponudbo, na blagajni uporabite kartico <ph name="CARD_DETAIL" />.</translation>
<translation id="1674504678466460478">Iz jezika <ph name="SOURCE_LANGUAGE" /> v jezik <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Najprej kratki rob</translation>
<translation id="168693727862418163">Preverjanje vrednosti tega pravilnika v skladu z njegovo shemo ni uspelo in bo prezrta.</translation>
@@ -306,6 +310,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="1717218214683051432">Tipala gibanja</translation>
<translation id="1717494416764505390">Nabiralnik 3</translation>
<translation id="1718029547804390981">Dokument je prevelik, da bi mu dodali pripise</translation>
+<translation id="1720941539803966190">Zapiranje vadnice</translation>
<translation id="1721424275792716183">*Polje je obvezno</translation>
<translation id="1727613060316725209">Potrdilo je veljavno</translation>
<translation id="1727741090716970331">Dodajanje veljavne Å¡tevilke kartice</translation>
@@ -422,8 +427,8 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="205212645995975601">Žar</translation>
<translation id="2053111141626950936">Strani v jeziku <ph name="LANGUAGE" /> ne bodo prevedene.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{ÄŒe je ta kontrolnik vklopljen in stanje aktivno, Chrome doloÄi, kateri veliki skupini oseb ali »kohorti« je vaÅ¡a nedavna dejavnost brskanja najbolj podobna. Izdajatelji lahko izberejo oglase za skupino in vaÅ¡a dejavnost brskanja je zasebna v napravi. Skupina se posodobi vsak dan.}=1{ÄŒe je ta kontrolnik vklopljen in stanje aktivno, Chrome doloÄi, kateri veliki skupini oseb ali »kohorti« je vaÅ¡a nedavna dejavnost brskanja najbolj podobna. Izdajatelji lahko izberejo oglase za skupino in vaÅ¡a dejavnost brskanja je zasebna v napravi. Skupina se posodobi vsak dan.}one{ÄŒe je ta kontrolnik vklopljen in stanje aktivno, Chrome doloÄi, kateri veliki skupini oseb ali »kohorti« je vaÅ¡a nedavna dejavnost brskanja najbolj podobna. Izdajatelji lahko izberejo oglase za skupino in vaÅ¡a dejavnost brskanja je zasebna v napravi. Skupina se posodobi vsak {NUM_DAYS} dan.}two{ÄŒe je ta kontrolnik vklopljen in stanje aktivno, Chrome doloÄi, kateri veliki skupini oseb ali »kohorti« je vaÅ¡a nedavna dejavnost brskanja najbolj podobna. Izdajatelji lahko izberejo oglase za skupino in vaÅ¡a dejavnost brskanja je zasebna v napravi. Skupina se posodobi vsak {NUM_DAYS} dneva.}few{ÄŒe je ta kontrolnik vklopljen in stanje aktivno, Chrome doloÄi, kateri veliki skupini oseb ali »kohorti« je vaÅ¡a nedavna dejavnost brskanja najbolj podobna. Izdajatelji lahko izberejo oglase za skupino in vaÅ¡a dejavnost brskanja je zasebna v napravi. Skupina se posodobi vsak {NUM_DAYS} dni.}other{ÄŒe je ta kontrolnik vklopljen in stanje aktivno, Chrome doloÄi, kateri veliki skupini oseb ali »kohorti« je vaÅ¡a nedavna dejavnost brskanja najbolj podobna. Izdajatelji lahko izberejo oglase za skupino in vaÅ¡a dejavnost brskanja je zasebna v napravi. Skupina se posodobi vsak {NUM_DAYS} dni.}}</translation>
-<translation id="2053553514270667976">Poštna številka</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 predlog}one{# predlog}two{# predloga}few{# predlogi}other{# predlogov}}</translation>
+<translation id="2066915425250589881">zahtevate, da jih izbriše</translation>
<translation id="2068528718802935086">DojenÄki in malÄki</translation>
<translation id="2071156619270205202">Ta kartica ni ustrezna za Å¡tevilko virtualne kartice.</translation>
<translation id="2071692954027939183">Obvestila so bila samodejno blokirana, ker jih obiÄajno ne dovolite.</translation>
@@ -435,7 +440,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="2088086323192747268">Gumb za upravljanje sinhronizacije, pritisnite Enter, Äe želite upravljati, kateri podatki se sinhronizirajo v Chromovih nastavitvah.</translation>
<translation id="2091887806945687916">Zvok</translation>
<translation id="2094505752054353250">Neujemanje domen</translation>
-<translation id="2096368010154057602">PodroÄje</translation>
<translation id="2099652385553570808">Trojno spenjanje na levi</translation>
<translation id="2101225219012730419">RazliÄica:</translation>
<translation id="2102134110707549001">Predlagaj zapleteno geslo …</translation>
@@ -472,7 +476,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="2185836064961771414">Ameriški nogomet</translation>
<translation id="2187317261103489799">Zaznava (privzeto)</translation>
<translation id="2188375229972301266">VeÄkratno luknjanje spodaj</translation>
-<translation id="2188852899391513400">Geslo, ki ste ga pravkar uporabili, je bilo najdeno v podatkovni krÅ¡itvi. Zaradi zavarovanja raÄunov Google Upravitelj gesel priporoÄa, da ga spremenite zdaj in nato preverite shranjena gesla.</translation>
<translation id="219906046732893612">Prenova doma</translation>
<translation id="2202020181578195191">Vnesite veljavno leto poteka veljavnosti</translation>
<translation id="22081806969704220">Pladenj 3</translation>
@@ -483,6 +486,8 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="2215727959747642672">Urejanje datotek</translation>
<translation id="2215963164070968490">Psi</translation>
<translation id="2218879909401188352">Napadalci, ki so trenutno na spletnem mestu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, bi lahko namestili nevarne aplikacije, ki poÅ¡kodujejo napravo, dodajo skrite stroÅ¡ke na raÄun za mobilno napravo ali ukradejo osebne podatke. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Znova zaženi vadnico</translation>
+<translation id="2219735899272417925">Potrebna je ponastavitev naprave</translation>
<translation id="2224337661447660594">Ni internetne povezave</translation>
<translation id="2230458221926704099">Odpravite težave s povezavo z <ph name="BEGIN_LINK" />aplikacijo za diagnostiko<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Pošlji</translation>
@@ -580,11 +585,13 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="2512101340618156538">Ni dovoljeno (privzeto)</translation>
<translation id="2512413427717747692">Gumb za nastavite Chroma kot privzeti brskalnik, pritisnite tipko Enter, Äe želite nastaviti Chrome kot privzeti brskalnik sistema v nastavitvah sistema iOS.</translation>
<translation id="2515629240566999685">preveriti signal na svojem obmoÄju</translation>
+<translation id="2515761554693942801">Na spletnih mestih, ki uporabljajo <ph name="PROVIDER_ORIGIN" />, ste izbrali preverjanje s funkcijo Touch ID. Ta ponudnik je morda shranil podatke o vaÅ¡em plaÄilnem sredstvu, za katere lahko <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Spenjanje spodaj desno</translation>
<translation id="2521736961081452453">Ustvari obrazec</translation>
<translation id="2523886232349826891">Shranjena samo v tej napravi</translation>
<translation id="2524461107774643265">Dodajanje veÄ podatkov</translation>
<translation id="2529899080962247600">V tem polju je lahko najveÄ toliko vnosov: <ph name="MAX_ITEMS_LIMIT" />. Vsi nadaljnji vnosi bodo prezrti.</translation>
+<translation id="253493526287553278">Ogled podrobnosti o promocijskih kodah</translation>
<translation id="2535585790302968248">Odpiranje novega anonimnega zavihka za zasebno brskanje</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{in Å¡e 1}one{in Å¡e #}two{in Å¡e #}few{in Å¡e #}other{in Å¡e #}}</translation>
<translation id="2536110899380797252">Dodaj naslov</translation>
@@ -620,7 +627,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="259821504105826686">Fotografska in digitalna umetnost</translation>
<translation id="2601150049980261779">RomantiÄni filmi</translation>
<translation id="2604589665489080024">Pop glasba</translation>
-<translation id="2609632851001447353">RazliÄice</translation>
<translation id="2610561535971892504">Kliknite za kopiranje</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />ne shrani<ph name="END_EMPHASIS" /> teh podatkov:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="2676271551327853224">ROC 16K</translation>
<translation id="2677696497921480781">Rojstni dnevi in godovi</translation>
<translation id="2677748264148917807">Zapusti</translation>
+<translation id="2679714844901977852">Podatke o kartici in podatke za obraÄunavanje shranite v raÄun Google <ph name="USER_EMAIL" /> za varno in hitrejÅ¡e dokonÄanje nakupov.</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Najustreznejše</translation>
<translation id="2688969097326701645">Da, nadaljuj</translation>
@@ -701,7 +708,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="2854764410992194509">Ponudniki internetnih storitev</translation>
<translation id="2856444702002559011">Morda poskuÅ¡ajo napadalci ukrasti vaÅ¡e podatke s spletnega mesta <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (na primer gesla, sporoÄila ali podatke kreditnih kartic). <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">To spletno mesto prikazuje vsiljive ali zavajajoÄe oglase.</translation>
-<translation id="286512204874376891">Navidezna kartica zakrije dejansko kartico in ste tako zaÅ¡Äiteni pred morebitno prevaro. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Prijateljsko</translation>
<translation id="28761159517501904">Filmi</translation>
<translation id="2876489322757410363">Zaradi plaÄila v zunanji aplikaciji boste zapustili anonimni naÄin. Želite nadaljevati?</translation>
@@ -802,7 +808,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">Omrežje Wi-Fi, ki ga uporabljate, morda zahteva, da obiÅ¡Äete stran za prijavo.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Otok</translation>
<translation id="3176929007561373547">Preverite nastavitve strežnika proxy ali se obrnite na skrbnika omrežja in
poskrbite za delovanje strežnika proxy. Če menite, da vam strežnika proxy
ni treba uporabljati:
@@ -881,9 +886,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="3369192424181595722">Napaka ure</translation>
<translation id="3369459162151165748">Deli in oprema za vozila</translation>
<translation id="3371064404604898522">Nastavi Chrome kot privzeti brskalnik</translation>
-<translation id="3371076217486966826"><ph name="URL" /> želi:
- • Ustvariti 3D-zemljevid vaše okolice in spremljati položaj kamere.
- • Uporabiti vašo kamero.</translation>
<translation id="337363190475750230">Nepripravljen</translation>
<translation id="3375754925484257129">Zaženi varnostno preverjanje v Chromu</translation>
<translation id="3377144306166885718">Strežnik je uporabljal zastarelo razliÄico standarda TLS.</translation>
@@ -900,6 +902,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="3399952811970034796">Naslov za dostavo</translation>
<translation id="3402261774528610252">Povezava, prek katere je bilo naloženo to spletno mesto, uporablja standard TLS 1.0 ali TLS 1.1; ta standarda sta zastarela in bosta v prihodnje onemogoÄena. Ko bosta onemogoÄena, bo uporabnikom prepreÄeno naložiti to spletno mesto. V strežniku omogoÄite standard TLS 1.2 ali novejÅ¡ega.</translation>
<translation id="3405664148539009465">Prilagodi pisave</translation>
+<translation id="3407789382767355356">prijava prek zunanjih ponudnikov</translation>
<translation id="3409896703495473338">Upravljanje varnostnih nastavitev</translation>
<translation id="3414952576877147120">Velikost:</translation>
<translation id="3417660076059365994">Datoteke, ki jih naložite ali pripnete, so poslane v oblak Google Cloud ali drugim ponudnikom v analizo. Lahko so na primer pregledane glede tega, ali vsebujejo obÄutljive podatke ali zlonamerno programsko opremo.</translation>
@@ -932,6 +935,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="3477679029130949506">Filmski sporedi in sporedi gledaliških predstav</translation>
<translation id="3479552764303398839">Ne zdaj</translation>
<translation id="3484560055331845446">Izgubite lahko dostop do Google RaÄuna. Chrome priporoÄa, da spremenite geslo. Pozvani boste, da se prijavite.</translation>
+<translation id="3484861421501147767">Opomnik: Shranjena promocijska koda je na voljo</translation>
<translation id="3487845404393360112">Pladenj 4</translation>
<translation id="3495081129428749620">Iskanje na strani
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="3810973564298564668">Upravljaj</translation>
<translation id="3816482573645936981">Vrednost (nadomeÅ¡Äena)</translation>
<translation id="382518646247711829">Če uporabite namestniški strežnik ...</translation>
+<translation id="3826050100957962900">Prijava prek zunanjih ponudnikov</translation>
<translation id="3827112369919217609">Absolutno</translation>
<translation id="3827666161959873541">Družinski filmi</translation>
<translation id="3828924085048779000">Prazno geslo ni dovoljeno.</translation>
@@ -1068,9 +1073,9 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="3858027520442213535">Posodobi datum in uro</translation>
<translation id="3858860766373142691">Ime</translation>
<translation id="3872834068356954457">Znanost</translation>
+<translation id="3875783148670536197">Postopek</translation>
<translation id="3881478300875776315">Pokaži manj vrstic</translation>
<translation id="3884278016824448484">Identifikator naprave je v sporu</translation>
-<translation id="3885155851504623709">Župnija</translation>
<translation id="388632593194507180">Zaznano je nadzorovanje</translation>
<translation id="3886948180919384617">Zlagalnik 3</translation>
<translation id="3890664840433101773">Dodajanje e-poštnega naslova</translation>
@@ -1100,9 +1105,11 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="3973234410852337861">Spletno mesto <ph name="HOST_NAME" /> je blokirano</translation>
<translation id="3978338123949022456">NaÄin za iskanje, vnesite poizvedbo in pritisnite tipko Enter, Äe želite iskati s tem: <ph name="KEYWORD_SUFFIX" />.</translation>
<translation id="398470910934384994">Ptice</translation>
+<translation id="3985750352229496475">Upravljanje naslovov …</translation>
<translation id="3986705137476756801">Izklop samodejnih podnapisov za zdaj</translation>
<translation id="3987940399970879459">Manj kot 1 MB</translation>
<translation id="3990250421422698716">Zamik v naÄinu dela »jog«</translation>
+<translation id="3992684624889376114">O tej strani</translation>
<translation id="3996311196211510766">Spletno mesto <ph name="ORIGIN" /> je zahtevalo, da izvorni pravilnik
velja za vse njegove zahteve, vendar tega pravilnika trenutno ni mogoÄe uveljaviti.</translation>
<translation id="4006465311664329701">PlaÄilna sredstva, ponudbe in naslovi z Googlom Pay</translation>
@@ -1227,6 +1234,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="4305666528087210886">Ni bilo mogoÄe dostopati do datoteke</translation>
<translation id="4306529830550717874">Želite shraniti naslov?</translation>
<translation id="4306812610847412719">odložiÅ¡Äe</translation>
+<translation id="4310070645992025887">IÅ¡Äite po poteh</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokira (privzeto)</translation>
<translation id="4314815835985389558">Upravljanje sinhronizacije</translation>
@@ -1277,6 +1285,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Uporaba strežnika proxy je onemogoÄena, vendar je njegova konfiguracija izrecno doloÄena.</translation>
<translation id="4441832193888514600">Prezrto, ker je pravilnik mogoÄe nastaviti samo kot uporabniÅ¡ki pravilnik za oblak.</translation>
+<translation id="4442470707340296952">Zavihki v Chromu</translation>
<translation id="4450893287417543264">Tega ne prikaži veÄ</translation>
<translation id="4451135742916150903">Lahko zahteva vzpostavitev povezave z napravami HID.</translation>
<translation id="4452328064229197696">Geslo, ki ste ga pravkar uporabili, je bilo najdeno v podatkovni krÅ¡itvi. Zaradi zavarovanja raÄunov Google Upravitelj gesel priporoÄa, da preverite shranjena gesla.</translation>
@@ -1415,6 +1424,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="483241715238664915">Vklopi opozorila</translation>
<translation id="4834250788637067901">PlaÄilna sredstva, ponudbe in naslovi z Googlom Pay</translation>
<translation id="4838327282952368871">Zasanjano</translation>
+<translation id="4839087176073128681">NaslednjiÄ plaÄajte hitreje in zaÅ¡Äitite kartico z Googlovo najboljÅ¡o varnostno zaÅ¡Äito v panogi.</translation>
<translation id="4840250757394056958">Ogled zgodovine v Chromu</translation>
<translation id="484462545196658690">Samodejno</translation>
<translation id="484671803914931257">Prejmite popust pri trgovcu <ph name="MERCHANT_NAME" /> in drugo</translation>
@@ -1477,6 +1487,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="5011561501798487822">Zaznan jezik</translation>
<translation id="5015510746216210676">Ime naprave:</translation>
<translation id="5017554619425969104">Besedilo, ki ste ga kopirali</translation>
+<translation id="5017828934289857214">Opomni me pozneje</translation>
<translation id="5018422839182700155">Te strani ni mogoÄe odpreti</translation>
<translation id="5019198164206649151">Neprimerno stanje rezervne shrambe</translation>
<translation id="5020776957610079374">Svetovna glasba</translation>
@@ -1496,6 +1507,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="5051305769747448211">Komedije v živo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Če želite to datoteko poslati z deljenjem v bližini, sprostite prostor v napravi (<ph name="DISK_SPACE_SIZE" />).}one{Če želite te datoteke poslati z deljenjem v bližini, sprostite prostor v napravi (<ph name="DISK_SPACE_SIZE" />).}two{Če želite te datoteke poslati z deljenjem v bližini, sprostite prostor v napravi (<ph name="DISK_SPACE_SIZE" />).}few{Če želite te datoteke poslati z deljenjem v bližini, sprostite prostor v napravi (<ph name="DISK_SPACE_SIZE" />).}other{Če želite te datoteke poslati z deljenjem v bližini, sprostite prostor v napravi (<ph name="DISK_SPACE_SIZE" />).}}</translation>
<translation id="5056549851600133418">ÄŒlanki za vas</translation>
+<translation id="5060483733937416656">Na spletnih mestih, ki uporabljajo <ph name="PROVIDER_ORIGIN" />, ste izbrali preverjanje s funkcijo Windows Hello. Ta ponudnik je morda shranil podatke o vaÅ¡em plaÄilnem sredstvu, za katere lahko <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Ali ste mislili <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Zgodovina tiskanja</translation>
<translation id="5068234115460527047">Hedge skladi</translation>
@@ -1509,10 +1521,8 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="5087286274860437796">Potrdilo strežnika trenutno ni veljavno.</translation>
<translation id="5087580092889165836">Dodaj kartico</translation>
<translation id="5088142053160410913">SporoÄilo operaterju</translation>
-<translation id="5089810972385038852">Država:</translation>
<translation id="5093232627742069661">Cikcakasto prepogibanje</translation>
<translation id="5094747076828555589">Strežniku ni uspelo dokazati, da je <ph name="DOMAIN" />; Chromium ne zaupa njegovemu varnostnemu potrdilu. Razlog za to je lahko napaÄna konfiguracija ali napadalÄevo prestrezanje povezave.</translation>
-<translation id="5095208057601539847">Provinca</translation>
<translation id="5097099694988056070">StatistiÄni podatki naprave, kot je uporaba CPE-ja/RAM-a</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Spletno mesto ni varno</translation>
@@ -1550,6 +1560,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="5171045022955879922">PoiÅ¡Äite ali vnesite URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">RaÄunalnik</translation>
+<translation id="5177076414499237632">VeÄ o virih in temah na tej strani</translation>
<translation id="5179510805599951267">Ni v <ph name="ORIGINAL_LANGUAGE" />? Obvestite nas o tej napaki</translation>
<translation id="518639307526414276">Hrana in izdelki za ljubljenÄke</translation>
<translation id="5190835502935405962">Vrstica z zaznamki</translation>
@@ -1710,6 +1721,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="5624120631404540903">Upravljanje gesel</translation>
<translation id="5629630648637658800">Nastavitev pravilnika ni bilo mogoÄe naložiti</translation>
<translation id="5631439013527180824">Neveljaven žeton za upravljanje naprave</translation>
+<translation id="5632485077360054581">Postopek</translation>
<translation id="5633066919399395251">Napadalci na spletnem mestu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> lahko poskusijo v vaÅ¡em raÄunalniku namestiti nevarne programe, ki kradejo ali briÅ¡ejo podatke (na primer fotografije, gesla, sporoÄila in podatke kreditnih kartic). <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">ZavajajoÄa vsebina blokirana.</translation>
<translation id="5633259641094592098">Kultni in neodvisni filmi</translation>
@@ -1827,6 +1839,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="5989320800837274978">DoloÄeni niso ne stalni strežniki proxy ne URL skripta .pac.</translation>
<translation id="5992691462791905444">Prepogibanje v obliki harmonike z dvema zgiboma</translation>
<translation id="5995727681868049093">Upravljanje podatkov, zasebnosti in varnosti v raÄunu Google</translation>
+<translation id="5997247540087773573">Geslo, ki ste ga pravkar uporabili, je bilo najdeno v podatkovni krÅ¡itvi. Zaradi zavarovanja raÄunov Google Upravitelj gesel priporoÄa, da ga spremenite zdaj in preverite shranjena gesla.</translation>
<translation id="6000758707621254961">Rezultati za »<ph name="SEARCH_TEXT" />« (<ph name="RESULT_COUNT" />)</translation>
<translation id="6006484371116297560">KlasiÄna</translation>
<translation id="6008122969617370890">Vrstni red N–1</translation>
@@ -1922,7 +1935,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="627746635834430766">ÄŒe želite naslednjiÄ hitreje plaÄati, shranite kartico in naslov za izstavitev raÄuna v Google RaÄunu.</translation>
<translation id="6279183038361895380">Pritisnite |<ph name="ACCELERATOR" />| za prikaz kazalca</translation>
<translation id="6280223929691119688">Dostava na ta naslov ni mogoÄa. Izberite drugega.</translation>
-<translation id="6282194474023008486">Poštna številka</translation>
<translation id="6285507000506177184">Gumb za upravljanje prenosov v Chromu, pritisnite Enter, Äe želite upravljati datoteke, ki ste jih prenesli v Chromu.</translation>
<translation id="6289939620939689042">Barva strani</translation>
<translation id="6290238015253830360">Tu so prikazani predlagani Älanki</translation>
@@ -1944,6 +1956,7 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="6337133576188860026">Sprosti manj kot <ph name="SIZE" />. Nekatera spletna mesta se bodo ob naslednjem obisku morda poÄasneje naložila.</translation>
<translation id="6337534724793800597">Filtriraj pravilnike po imenu</translation>
<translation id="6340739886198108203">Pravilnik skrbnika ne priporoÄa ustvarjanja posnetkov zaslona ali videoposnetkov, ko je vidna zaupna vsebina:</translation>
+<translation id="6348220984832452017">Aktivne razliÄice</translation>
<translation id="6349101878882523185">Namestite <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{NiÄ}=1{Eno geslo (za <ph name="DOMAIN_LIST" />, sinhronizirano)}=2{Dve gesli (za <ph name="DOMAIN_LIST" />, sinhronizirano)}one{# geslo (za <ph name="DOMAIN_LIST" />, sinhronizirano)}two{# gesli (za <ph name="DOMAIN_LIST" />, sinhronizirano)}few{# gesla (za <ph name="DOMAIN_LIST" />, sinhronizirano)}other{# gesel (za <ph name="DOMAIN_LIST" />, sinhronizirano)}}</translation>
<translation id="6355392890578844978">Tega brskalnika ne upravlja podjetje ali druga organizacija. Dejavnost v tej napravi morda upravljajo zunaj Chromiuma. <ph name="BEGIN_LINK" />VeÄ o tem<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="643051589346665201">Spremeni geslo za Google</translation>
<translation id="6433490469411711332">Uredi informacije o stiku</translation>
<translation id="6433595998831338502">Spletno mesto <ph name="HOST_NAME" /> ni dovolilo povezave.</translation>
-<translation id="6438025220577812695">Spremenil(-a) bom sam(-a)</translation>
<translation id="6440503408713884761">Prezrto</translation>
<translation id="6443406338865242315">Katere razÅ¡iritve in vtiÄnike ste namestili</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2105,9 +2117,9 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Prevedi</translation>
<translation id="6833752742582340615">Podatke o kartici in podatke za obraÄunavanje shranite v raÄun Google za varno in hitrejÅ¡e dokonÄanje nakupov.</translation>
-<translation id="6839929833149231406">ObmoÄje</translation>
<translation id="6846340164947227603">Uporabi Å¡tevilko virtualne kartice ...</translation>
<translation id="6852204201400771460">Želite znova naložiti aplikacijo?</translation>
+<translation id="6857776781123259569">Upravljanje gesel …</translation>
<translation id="686485648936420384">Potrošniški viri</translation>
<translation id="6865412394715372076">Te kartice trenutno ni mogoÄe preveriti</translation>
<translation id="6869334554832814367">Osebna posojila</translation>
@@ -2156,7 +2168,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="6965978654500191972">Naprava</translation>
<translation id="696703987787944103">Zaznavno</translation>
<translation id="6968269510885595029">Uporaba varnostnega kljuÄa</translation>
-<translation id="6970216967273061347">Okrožje</translation>
<translation id="6971439137020188025">Hitro ustvarjanje nove Googlove predstavitve v Predstavitvah</translation>
<translation id="6972629891077993081">Naprave HID</translation>
<translation id="6973656660372572881">DoloÄeni so stalni strežniki proxy in URL skripta .pac.</translation>
@@ -2195,7 +2206,6 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<translation id="7081308185095828845">Ta funkcija ni na voljo v vaši napravi</translation>
<translation id="7083258188081898530">Pladenj 9</translation>
<translation id="7086090958708083563">Nalaganje je zahteval uporabnik</translation>
-<translation id="7087282848513945231">Okrožje</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite Enter, Äe želite upravljati dovoljenja in podatke, shranjene na spletnih mestih v Chromovih nastavitvah.</translation>
<translation id="7096937462164235847">Identiteta tega spletnega mesta ni potrjena.</translation>
<translation id="7101893872976785596">Grozljivke</translation>
@@ -2214,10 +2224,10 @@ V nasprotnem primeru bodo to blokirale nastavitve zasebnosti. S tem bo vsebina,
<ph name="LIST_ITEM" />podatki, vneseni v obrazce.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">PoÅ¡iljanje na ta naslov ni mogoÄe. Izberite drugega.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Skrbnik je prepovedal kopiranje teh podatkov.</translation>
<translation id="7135130955892390533">Prikaz stanja</translation>
<translation id="7138472120740807366">NaÄin dostave</translation>
-<translation id="7139724024395191329">Emirat:</translation>
<translation id="7139892792842608322">Glavni pladenj</translation>
<translation id="714064300541049402">Pomik slike na 2. strani v X</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2434,6 +2444,7 @@ Dodatne podrobnosti:
<translation id="7669271284792375604">Napadalci na tem spletnem mestu vas bodo morda poskusili zavesti, da bi namestili programe, ki Å¡kodljivo vplivajo na brskanje (na primer tako, da spremenijo vaÅ¡o domaÄo stran ali na spletnih mestih, ki jih obiÅ¡Äete, prikazujejo dodatne oglase).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Izvedena dejanja s podatki, ki so oznaÄeni kot zaupni (od prijave je bilo 1 dejanje). <ph name="BEGIN_LINK" />VeÄ o tem<ph name="END_LINK" />}one{Izvedena dejanja s podatki, ki so oznaÄeni kot zaupni (od prijave je bilo # dejanje). <ph name="BEGIN_LINK" />VeÄ o tem<ph name="END_LINK" />}two{Izvedena dejanja s podatki, ki so oznaÄeni kot zaupni (od prijave sta bili # dejanji). <ph name="BEGIN_LINK" />VeÄ o tem<ph name="END_LINK" />}few{Izvedena dejanja s podatki, ki so oznaÄeni kot zaupni (od prijave so bila # dejanja). <ph name="BEGIN_LINK" />VeÄ o tem<ph name="END_LINK" />}other{Izvedena dejanja s podatki, ki so oznaÄeni kot zaupni (od prijave je bilo # dejanj). <ph name="BEGIN_LINK" />VeÄ o tem<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Nabiralnik 6</translation>
+<translation id="7675325315208090829">Upravljanje plaÄilnih sredstev …</translation>
<translation id="7676643023259824263">Iskanje besedila v odložiÅ¡Äu, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Ogled in upravljanje zgodovine brskanja v Chromovih nastavitvah</translation>
<translation id="7679947978757153706">Bejzbol</translation>
@@ -2476,7 +2487,6 @@ Dodatne podrobnosti:
<translation id="7766518757692125295">Votla podlaga</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">V istem vrstnem redu s tiskom na zgornji strani</translation>
-<translation id="777702478322588152">Prefektura</translation>
<translation id="7791011319128895129">Neobjavljeno</translation>
<translation id="7791196057686275387">Vezava »bale«</translation>
<translation id="7791543448312431591">Dodaj</translation>
@@ -2567,12 +2577,12 @@ Dodatne podrobnosti:
<translation id="8055534648776115597">Poklicno in vseživljenjsko izobraževanje</translation>
<translation id="8057711352706143257">Programska oprema »<ph name="SOFTWARE_NAME" />« ni pravilno konfigurirana. ObiÄajno težavo odpravite tako, da odstranite programsko opremo »<ph name="SOFTWARE_NAME" />«. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Pridelava živil</translation>
-<translation id="8066955247577885446">Prišlo je do napake.</translation>
<translation id="8067872629359326442">Pravkar ste vnesli geslo na zavajajoÄem spletnem mestu. Chromium lahko pomaga. ÄŒe želite spremeniti geslo in obvestiti Google, da je vaÅ¡ raÄun morda ogrožen, kliknite »ZaÅ¡Äita raÄuna«.</translation>
<translation id="8070439594494267500">Ikona aplikacije</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Hitro ustvarjanje nove Google Preglednice</translation>
<translation id="8075898834294118863">Upravljanje nastavitev spletnega mesta</translation>
+<translation id="8076492880354921740">Zavihki</translation>
<translation id="8078141288243656252">Ko je dokument zasukan, pripisovanje ni mogoÄe</translation>
<translation id="8079031581361219619">Želite znova naložiti spletno mesto?</translation>
<translation id="8081087320434522107">Limuzine</translation>
@@ -2695,6 +2705,7 @@ Dodatne podrobnosti:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" />, <ph name="SECOND_LABEL" />, <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Nastavitve</translation>
+<translation id="8428634594422941299">Razumem</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite tabulatorko, nato Enter, Äe želite upravljati nastavitve piÅ¡kotkov v Chromovih nastavitvah.</translation>
<translation id="8433057134996913067">S tem boste odjavljeni z veÄine spletnih mest.</translation>
<translation id="8434840396568290395">DomaÄe živali</translation>
@@ -2792,6 +2803,7 @@ Dodatne podrobnosti:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> je vaša koda za <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Dodajanje tega zavihka med zaznamke</translation>
<translation id="8751426954251315517">Poskusite znova pozneje</translation>
+<translation id="8757526089434340176">Na voljo je ponudba Googla Pay</translation>
<translation id="8758885506338294482">VideoigriÄarska tekmovanja (e-Å¡porti)</translation>
<translation id="8759274551635299824">Ta kartica je potekla</translation>
<translation id="87601671197631245">To spletno mesto uporablja zastarelo varnostno konfiguracijo, zaradi Äesar so lahko vaÅ¡i podatki ob poÅ¡iljanju temu spletnemu mestu izpostavljeni (npr. gesla, sporoÄila ali Å¡tevilke kreditnih kartic).</translation>
@@ -2799,6 +2811,7 @@ Dodatne podrobnosti:
<translation id="8763927697961133303">Naprava USB</translation>
<translation id="8763986294015493060">Zapiranje vseh trenutno odprtih anonimnih oken.</translation>
<translation id="8766943070169463815">Preglednica za preverjanje pristnosti poverilnice za varno plaÄilo je odprta</translation>
+<translation id="8767765348545497220">Zapiranje oblaÄka s pomoÄjo</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorna kolesa</translation>
<translation id="8790007591277257123">&amp;Uveljavi izbris</translation>
@@ -2811,6 +2824,7 @@ Dodatne podrobnosti:
<translation id="8806285662264631610">Izdelki za pripravo kopeli in nego telesa</translation>
<translation id="8807160976559152894">Obrezovanje po vsaki strani</translation>
<translation id="8808828119384186784">Chromove nastavitve</translation>
+<translation id="8813277370772331957">Opomni me pozneje</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite tabulatorko, nato Enter, Äe želite posodobiti Chrome v Chromovih nastavitvah</translation>
<translation id="8820817407110198400">Zaznamki</translation>
<translation id="882338992931677877">RoÄni pladenj</translation>
@@ -2990,6 +3004,7 @@ Dodatne podrobnosti:
<translation id="988159990683914416">RazliÄica za razvijalce</translation>
<translation id="989988560359834682">Uredi naslov</translation>
<translation id="991413375315957741">tipalo gibanja ali svetlobe</translation>
+<translation id="992110854164447044">Navidezna kartica zakrije dejansko kartico in ste tako zaÅ¡Äiteni pred morebitno prevaro. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rožnata</translation>
<translation id="992432478773561401">Programska oprema »<ph name="SOFTWARE_NAME" />« ni bila pravilno nameÅ¡Äena v raÄunalniku ali omrežju:
diff --git a/chromium/components/strings/components_strings_sq.xtb b/chromium/components/strings/components_strings_sq.xtb
index 9033036faf4..03649a9bad4 100644
--- a/chromium/components/strings/components_strings_sq.xtb
+++ b/chromium/components/strings/components_strings_sq.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Grantet, bursat dhe ndihma financiare</translation>
<translation id="1048785276086539861">Kur redakton shënimet, ky dokument do të kthehet te pamja me një faqe</translation>
<translation id="1050038467049342496">Mbyll aplikacionet e tjera</translation>
+<translation id="1053959602163383901">Të zgjodhe që të verifikohesh me një pajisje vërtetimi në sajtet e uebit që përdorin <ph name="PROVIDER_ORIGIN" />. Ky ofrues mund të ketë ruajtur informacione për mënyrën tënde të pagesës, të cilat mund <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Zhbëj shtimin</translation>
<translation id="1056663316309890343">Softueri i fotografive</translation>
<translation id="1056898198331236512">Paralajmërim</translation>
@@ -119,6 +120,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="1270502636509132238">Mënyra e marrjes</translation>
<translation id="1281476433249504884">Stivuesi 1</translation>
<translation id="1285320974508926690">Asnjëherë mos e përkthe këtë sajt</translation>
+<translation id="1288548991597756084">Ruaje kartën në mënyrë të sigurt</translation>
<translation id="1292571435393770077">Tabakaja 16</translation>
<translation id="1292701964462482250">"Një softuer në kompjuterin tënd po e ndalon Chrome që të lidhet në mënyrë të sigurt me uebin" (vetëm kompjuterët me Windows)</translation>
<translation id="1294154142200295408">Variantet e rreshtit të komandës</translation>
@@ -223,6 +225,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
&lt;p&gt;Për të rregulluar gabimin, kliko te &lt;strong&gt;Lidhu&lt;/strong&gt; në faqen që po përpiqesh të hapësh.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Projektimi i terrenit</translation>
<translation id="1513706915089223971">Lista e regjistrimeve të historikut</translation>
+<translation id="1516097932025103760">Ajo do të enkriptohet, do të ruhet në mënyrë të sigurt dhe kodi CVC nuk do të ruhet asnjëherë.</translation>
<translation id="1517433312004943670">Numri i telefonit është i detyrueshëm</translation>
<translation id="1519264250979466059">Data e ndërtimit</translation>
<translation id="1521159554480556801">Fibra dhe artet tekstile</translation>
@@ -288,6 +291,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="1662550410081243962">Ruaj dhe plotëso mënyrat e pagesës</translation>
<translation id="1663943134801823270">Kartat dhe adresat janë nga Chrome. Ato mund t'i menaxhosh te <ph name="BEGIN_LINK" />Cilësimet<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Faqet në <ph name="SOURCE_LANGUAGE" /> do të përkthehen në <ph name="TARGET_LANGUAGE" /> nga tani e në vazhdim.</translation>
+<translation id="1673886523110456987">Përfundoje blerjen me kartën <ph name="CARD_DETAIL" /> për të përdorur ofertën</translation>
<translation id="1674504678466460478">Nga <ph name="SOURCE_LANGUAGE" /> në <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Ana e shkurtër në fillim</translation>
<translation id="168693727862418163">Vërtetimi i kësaj vlere të politikës kundrejt skemës së saj dështoi dhe do të shpërfillet.</translation>
@@ -306,6 +310,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="1717218214683051432">Sensorët e lëvizjes</translation>
<translation id="1717494416764505390">Kutia postare 3</translation>
<translation id="1718029547804390981">Dokumenti është shumë i madh për t'u shënuar</translation>
+<translation id="1720941539803966190">Mbyll udhëzuesin</translation>
<translation id="1721424275792716183">* Fusha është e detyrueshme</translation>
<translation id="1727613060316725209">Certifikata është e vlefshme</translation>
<translation id="1727741090716970331">Shto numër të vlefshëm karte</translation>
@@ -421,8 +426,8 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="205212645995975601">Skara dhe pjekja në skarë</translation>
<translation id="2053111141626950936">Faqet në <ph name="LANGUAGE" /> nuk do të përkthehen.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Kur ky kontroll është aktiv dhe statusi është aktiv, Chrome përcakton se me cilin grup të madh personash (kohortë) është më i ngjashëm aktiviteti yt i shfletimit së fundi. Reklamuesit mund të zgjedhin reklamat për grupin dhe aktiviteti yt i shfletimit mbahet privat në pajisjen tënde. Grupi yt përditësohet çdo ditë.}=1{Kur ky kontroll është aktiv dhe statusi është aktiv, Chrome përcakton se me cilin grup të madh personash (kohortë) është më i ngjashëm aktiviteti yt i shfletimit së fundi. Reklamuesit mund të zgjedhin reklamat për grupin dhe aktiviteti yt i shfletimit mbahet privat në pajisjen tënde. Grupi yt përditësohet çdo ditë.}other{Kur ky kontroll është aktiv dhe statusi është aktiv, Chrome përcakton se me cilin grup të madh personash (kohortë) është më i ngjashëm aktiviteti yt i shfletimit së fundi. Reklamuesit mund të zgjedhin reklamat për grupin dhe aktiviteti yt i shfletimit mbahet privat në pajisjen tënde. Grupi yt përditësohet çdo {NUM_DAYS} ditë.}}</translation>
-<translation id="2053553514270667976">Kodi postar</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 sugjerim}other{# sugjerime}}</translation>
+<translation id="2066915425250589881">të kërkosh që të fshihen</translation>
<translation id="2068528718802935086">Foshnjat dhe fëmijët e vegjël</translation>
<translation id="2071156619270205202">Kjo kartë nuk është e përshtatshme për numrin e kartës virtuale.</translation>
<translation id="2071692954027939183">Njoftimet bllokohen automatikisht sepse ti zakonisht nuk i lejon ato</translation>
@@ -434,7 +439,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="2088086323192747268">Butoni "Menaxho sinkronizimin". Shtyp "Enter" për të menaxhuar se çfarë informacionesh sinkronizon ti te cilësimet e Chrome</translation>
<translation id="2091887806945687916">Tingulli</translation>
<translation id="2094505752054353250">Mospërputhje domeni</translation>
-<translation id="2096368010154057602">Departamenti</translation>
<translation id="2099652385553570808">Tri kapje me tel majtas</translation>
<translation id="2101225219012730419">Versioni:</translation>
<translation id="2102134110707549001">Sugjero fjalëkalim të fortë…</translation>
@@ -471,7 +475,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="2185836064961771414">Futbolli amerikan</translation>
<translation id="2187317261103489799">Zbulo (parazgjedhja)</translation>
<translation id="2188375229972301266">Shumë shpime poshtë</translation>
-<translation id="2188852899391513400">Fjalëkalimi që sapo përdore u gjet në një nxjerrje të paautorizuar të të dhënave. Për të mbrojtur llogaritë e tua, "Menaxheri i fjalëkalimeve" i Google rekomandon ta ndryshosh tani dhe më pas të kontrollosh fjalëkalimet e ruajtura.</translation>
<translation id="219906046732893612">Përmirësimi i shtëpisë</translation>
<translation id="2202020181578195191">Fut një vit të vlefshëm skadimi</translation>
<translation id="22081806969704220">Tabakaja 3</translation>
@@ -482,6 +485,8 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="2215727959747642672">Modifikimi i skedarëve</translation>
<translation id="2215963164070968490">Qentë</translation>
<translation id="2218879909401188352">Sulmuesit aktualisht në <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mund të instalojnë aplikacione të rrezikshme që e dëmtojnë pajisjen tënde, shtojnë tarifa të fshehura në faturën tënde të celularit ose vjedhin informacionet e tua personale. <ph name="BEGIN_LEARN_MORE_LINK" />Mëso më shumë<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Rinis udhëzuesin</translation>
+<translation id="2219735899272417925">Kërkohet rivendosja e pajisjes</translation>
<translation id="2224337661447660594">Nuk ka internet</translation>
<translation id="2230458221926704099">Rregullo lidhjen duke përdorur <ph name="BEGIN_LINK" />aplikacionin e diagnostikimit<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Dërgo tani</translation>
@@ -579,11 +584,13 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="2512101340618156538">Nuk lejohet (e parazgjedhur)</translation>
<translation id="2512413427717747692">Cakto Chrome si butonin e shfletuesit të parazgjedhur; shtyp "Enter" për të caktuar Chrome si shfletuesin e parazgjedhur të sistemit te cilësimet e iOS</translation>
<translation id="2515629240566999685">Të kontrollosh sinjalin në zonën tënde</translation>
+<translation id="2515761554693942801">Ti zgjodhe që të verifikohesh me Touch ID në sajtet e uebit që përdorin <ph name="PROVIDER_ORIGIN" />. Ky ofrues mund të ketë ruajtur informacione për mënyrën tënde të pagesës, të cilat mund <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Kapje me tel poshtë djathtas</translation>
<translation id="2521736961081452453">Krijo formular</translation>
<translation id="2523886232349826891">Ruajtur vetëm në këtë pajisje</translation>
<translation id="2524461107774643265">Shto më shumë informacion</translation>
<translation id="2529899080962247600">Kjo fushë nuk duhet të ketë më shumë se <ph name="MAX_ITEMS_LIMIT" /> regjistrime. Të gjitha hyrjet e mëtejshme do të shpërfillen.</translation>
+<translation id="253493526287553278">Shiko detajet e kodit promocional</translation>
<translation id="2535585790302968248">Hap një skedë të re "të fshehtë" për të shfletuar në mënyrë private</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{dhe 1 tjetër}other{dhe # të tjera}}</translation>
<translation id="2536110899380797252">Shto adresë</translation>
@@ -619,7 +626,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="259821504105826686">Artet fotografike dhe dixhitale</translation>
<translation id="2601150049980261779">Filmat romantikë</translation>
<translation id="2604589665489080024">Muzika pop</translation>
-<translation id="2609632851001447353">Variantet</translation>
<translation id="2610561535971892504">Kliko për të kopjuar</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />nuk do të ruajë<ph name="END_EMPHASIS" /> informacionet e mëposhtme:
<ph name="BEGIN_LIST" />
@@ -652,6 +658,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Ditëlindjet dhe ditët e emrave</translation>
<translation id="2677748264148917807">Largohu</translation>
+<translation id="2679714844901977852">Ruaj informacionet e kartës dhe të faturimit në "Llogarinë tënde të Google" <ph name="USER_EMAIL" /> për të përfunduar blerjet në mënyrë të sigurt dhe më shpejt</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Përshtatja më e mirë</translation>
<translation id="2688969097326701645">Po, vazhdo!</translation>
@@ -700,7 +707,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="2854764410992194509">Ofruesit e shërbimeve të internetit (ISP-të)</translation>
<translation id="2856444702002559011">Sulmuesit mund të përpiqen të vjedhin informacionet e tua nga <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (për shembull, fjalëkalimet, mesazhet ose kartat e kreditit). <ph name="BEGIN_LEARN_MORE_LINK" />Mëso më shumë<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Ky sajt shfaq reklama ndërhyrëse ose mashtruese.</translation>
-<translation id="286512204874376891">Një kartë virtuale fsheh kartën tënde reale për të të mbrojtur nga mashtrimi i mundshëm.<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Miqësor</translation>
<translation id="28761159517501904">Filmat</translation>
<translation id="2876489322757410363">Po del nga modaliteti "i fshehtë" për të paguar nëpërmjet një aplikacioni të jashtëm. Të vazhdohet?</translation>
@@ -801,7 +807,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="3158539265159265653">Disku</translation>
<translation id="3162559335345991374">Lidhja Wi-Fi që po përdor mund të të kërkojë që të vizitosh faqen e saj të identifikimit.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ishulli</translation>
<translation id="3176929007561373547">Kontrollo cilësimet e përfaqësuesit ose kontakto me administratorin e rrjetit
për t'u siguruar që serveri përfaqësues po punon. Nëse nuk beson
se duhet të përdorësh një server përfaqësues:
@@ -880,9 +885,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="3369192424181595722">Gabim i orës</translation>
<translation id="3369459162151165748">Pjesët dhe aksesorët e automjeteve</translation>
<translation id="3371064404604898522">Cakto Chrome si shfletuesin e parazgjedhur</translation>
-<translation id="3371076217486966826"><ph name="URL" /> kërkon që:
- • Të krijojë një hartë 3D të ambientit tënd rrethues ose të gjurmojë pozicionin e kamerës
- • Të përdorë kamerën tënde</translation>
<translation id="337363190475750230">Qasja u çaktivizua</translation>
<translation id="3375754925484257129">Ekzekuto kontrollin e sigurisë të Chrome</translation>
<translation id="3377144306166885718">Serveri ka përdorur një version të vjetruar të protokollit TLS.</translation>
@@ -899,6 +901,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="3399952811970034796">Adresa e dorëzimit</translation>
<translation id="3402261774528610252">Lidhja e përdorur për të ngarkuar këtë sajt ka përdorur versionin TLS 1.0 ose TLS 1.1, të cilët janë të vjetëruar dhe do të çaktivizohen në të ardhmen. Pasi të çaktivizohen, përdoruesit nuk do të lejohen ta ngarkojnë këtë sajt. Serveri duhet të aktivizojë versionin TLS 1.2 ose një version të mëvonshëm.</translation>
<translation id="3405664148539009465">Personalizo shkrimin</translation>
+<translation id="3407789382767355356">identifikimi i palëve të treta</translation>
<translation id="3409896703495473338">Menaxho cilësimet e sigurisë</translation>
<translation id="3414952576877147120">Madhësia:</translation>
<translation id="3417660076059365994">Skedarët që ngarkon ose bashkëngjit i dërgohen Google Cloud ose palëve të treta për analizim. Për shembull, mund të skanohen për të dhëna delikate ose softuerë keqdashës.</translation>
@@ -931,6 +934,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="3477679029130949506">Listimet e filmave dhe oraret e shfaqjeve në kinema</translation>
<translation id="3479552764303398839">Jo tani</translation>
<translation id="3484560055331845446">Mund të humbësh qasjen në "Llogarinë tënde të Google". Chrome rekomandon që ta ndryshosh fjalëkalimin tani. Do të të kërkohet që të identifikohesh.</translation>
+<translation id="3484861421501147767">Rikujtesë: Një kod promocional i ruajtur është i disponueshëm</translation>
<translation id="3487845404393360112">Tabakaja 4</translation>
<translation id="3495081129428749620">Gjej në faqen
<ph name="PAGE_TITLE" /></translation>
@@ -1055,6 +1059,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="3810973564298564668">Menaxho</translation>
<translation id="3816482573645936981">Vlera (e zëvendësuar)</translation>
<translation id="382518646247711829">Nëse përdor një server përfaqësues...</translation>
+<translation id="3826050100957962900">Identifikimi i palëve të treta</translation>
<translation id="3827112369919217609">Absolut</translation>
<translation id="3827666161959873541">Filmat për familjen</translation>
<translation id="3828924085048779000">Lënia bosh e frazës së kalimit nuk lejohet.</translation>
@@ -1067,9 +1072,9 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="3858027520442213535">Përditëso datën dhe kohën</translation>
<translation id="3858860766373142691">Emri</translation>
<translation id="3872834068356954457">Shkenca</translation>
+<translation id="3875783148670536197">Më trego mënyrën</translation>
<translation id="3881478300875776315">Shfaq më pak rreshta</translation>
<translation id="3884278016824448484">Identifikuesi i pajisjes bie në konflikt</translation>
-<translation id="3885155851504623709">Famullia</translation>
<translation id="388632593194507180">U zbulua monitorim</translation>
<translation id="3886948180919384617">Stivuesi 3</translation>
<translation id="3890664840433101773">Shto një email</translation>
@@ -1099,9 +1104,11 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="3973234410852337861"><ph name="HOST_NAME" /> është bllokuar</translation>
<translation id="3978338123949022456">Modaliteti i kërkimit; shkruaj një kërkesë dhe shtyp "Enter" për të kërkuar me <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Shpendët</translation>
+<translation id="3985750352229496475">Menaxho adresat...</translation>
<translation id="3986705137476756801">Çaktivizo "Titrat në çast" për momentin</translation>
<translation id="3987940399970879459">Më pak se 1 MB</translation>
<translation id="3990250421422698716">Zhvendosja e bordurës</translation>
+<translation id="3992684624889376114">Rreth kësaj faqeje</translation>
<translation id="3996311196211510766">Sajti <ph name="ORIGIN" /> ka kërkuar që të zbatohet një politikë e origjinës
për të gjitha kërkesat drejtuar atij, por kjo politikë nuk mund të zbatohet aktualisht.</translation>
<translation id="4006465311664329701">Mënyrat e pagesës, ofertat dhe adresat që përdorin Google Pay</translation>
@@ -1224,6 +1231,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="4305666528087210886">Qasja te skedari ishte e pamundur</translation>
<translation id="4306529830550717874">Të ruhet adresa?</translation>
<translation id="4306812610847412719">kujtesa e fragmenteve</translation>
+<translation id="4310070645992025887">Kërko për udhëtime</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blloko (parazgjedhja)</translation>
<translation id="4314815835985389558">Menaxho sinkronizimin</translation>
@@ -1274,9 +1282,10 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Përdorimi i një përfaqësuesi është i çaktivizuar, por është specifikuar një konfigurim i qartë përfaqësuesi.</translation>
<translation id="4441832193888514600">Shpërfillur për shkak se politika mund të shtohet vetëm si politikë e përdoruesit të resë kompjuterike.</translation>
+<translation id="4442470707340296952">Skedat e Chrome</translation>
<translation id="4450893287417543264">Mos e shfaq përsëri</translation>
<translation id="4451135742916150903">Mund të kërkojë të lidhet me pajisjet HID</translation>
-<translation id="4452328064229197696">Fjalëkalimi që sapo përdore u gjet në një nxjerrje të paautorizuar të të dhënave. Për të mbrojtur llogaritë e tua, "Menaxheri i fjalëkalimeve" i Google rekomandon të kontrollosh fjalëkalimet e ruajtura.</translation>
+<translation id="4452328064229197696">Fjalëkalimi që sapo përdore u gjet në një nxjerrje të paautorizuar të të dhënave. Për të mbrojtur llogaritë e tua, "Menaxheri i fjalëkalimeve i Google" rekomandon të kontrollosh fjalëkalimet e ruajtura.</translation>
<translation id="4455222631300069614">Ndrysho fjalëkalimin tënd tani</translation>
<translation id="4460315069258617173">Lejohet derisa të mbyllësh skedat për këtë faqe</translation>
<translation id="4464826014807964867">Sajtet e uebit me informacione nga organizata jote</translation>
@@ -1412,6 +1421,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="483241715238664915">Aktivizo paralajmërimet</translation>
<translation id="4834250788637067901">Mënyrat e pagesës, ofertat dhe adresat që përdorin Google Pay</translation>
<translation id="4838327282952368871">Si në ëndërr</translation>
+<translation id="4839087176073128681">Paguaj më shpejt herën tjetër dhe mbroje kartën tënde me sigurinë e nivelit më të lartë në këtë industri nga Google.</translation>
<translation id="4840250757394056958">Shiko historikun tënd të Chrome</translation>
<translation id="484462545196658690">Automatike</translation>
<translation id="484671803914931257">Merr zbritje në <ph name="MERCHANT_NAME" /> dhe të tjera</translation>
@@ -1474,6 +1484,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="5011561501798487822">Gjuha e zbuluar</translation>
<translation id="5015510746216210676">Emri i pajisjes:</translation>
<translation id="5017554619425969104">Teksti që kopjove</translation>
+<translation id="5017828934289857214">Më kujto më vonë</translation>
<translation id="5018422839182700155">Kjo faqe nuk mund të hapet</translation>
<translation id="5019198164206649151">Mbështetja e ruajtjes është në gjendje të keqe</translation>
<translation id="5020776957610079374">Muzika botërore</translation>
@@ -1493,6 +1504,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="5051305769747448211">Komedia e drejtpërdrejtë</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Për ta dërguar këtë skedar duke përdorur "Ndarjen në afërsi", liro hapësirë (<ph name="DISK_SPACE_SIZE" />) në pajisjen tënde}other{Për ta dërguar këta skedarë duke përdorur "Ndarjen në afërsi", liro hapësirë (<ph name="DISK_SPACE_SIZE" />) në pajisjen tënde}}</translation>
<translation id="5056549851600133418">Artikujt për ty</translation>
+<translation id="5060483733937416656">Ti zgjodhe që të verifikohesh me Windows Hello në sajtet e uebit që përdorin <ph name="PROVIDER_ORIGIN" />. Ky ofrues mund të ketë ruajtur informacione për mënyrën tënde të pagesës, të cilat mund <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Mos dëshiron të thuash <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Historiku i printimeve</translation>
<translation id="5068234115460527047">Fonde spekulative</translation>
@@ -1506,10 +1518,8 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="5087286274860437796">Certifikata e serverit është e pavlefshme në këtë moment.</translation>
<translation id="5087580092889165836">Shto kartë</translation>
<translation id="5088142053160410913">Mesazhi për operatorin</translation>
-<translation id="5089810972385038852">Shteti</translation>
<translation id="5093232627742069661">Palosje Z</translation>
<translation id="5094747076828555589">Ky server nuk mundi të dëshmonte se kjo është <ph name="DOMAIN" />; certifikata e tij e sigurisë nuk është e besueshme nga Chromium. Kjo mund të shkaktohet nga keqkonfigurimi ose një sulmues që po kap lidhjen tënde.</translation>
-<translation id="5095208057601539847">Krahina</translation>
<translation id="5097099694988056070">Statistikat e pajisjes si p.sh. përdorimin e njësisë CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Sajti nuk është i sigurt</translation>
@@ -1547,6 +1557,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="5171045022955879922">Kërko ose shkruaj URL-në</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Pajisja</translation>
+<translation id="5177076414499237632">Mëso rreth temës dhe burimit të kësaj faqeje</translation>
<translation id="5179510805599951267">Jo në <ph name="ORIGINAL_LANGUAGE" />? Raportoje këtë gabim</translation>
<translation id="518639307526414276">Ushqimi për kafshë dhe artikujt për kujdesin e kafshëve</translation>
<translation id="5190835502935405962">Shiriti i faqeshënuesve</translation>
@@ -1707,6 +1718,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="5624120631404540903">Menaxho fjalëkalimet</translation>
<translation id="5629630648637658800">Dështoi në ngarkimin e cilësimeve të politikës</translation>
<translation id="5631439013527180824">Shenjë e pavlefshme e menaxhimit të pajisjes</translation>
+<translation id="5632485077360054581">Më trego mënyrën</translation>
<translation id="5633066919399395251">Sulmuesit aktualisht në <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mund të tentojnë të instalojnë programe të rrezikshme që vjedhin ose fshijnë informacionin tënd (për shembull, fotografi, fjalëkalime, mesazhe dhe karta krediti). <ph name="BEGIN_LEARN_MORE_LINK" />Mëso më shumë<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">U bllokua një përmbajtje mashtruese.</translation>
<translation id="5633259641094592098">Filmat e kulteve dhe indie</translation>
@@ -1824,6 +1836,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="5989320800837274978">Nuk janë specifikuar serverë përfaqësues fiksë dhe as ndonjë URL e skriptit .pac.</translation>
<translation id="5992691462791905444">Palosje inxhinierike Z</translation>
<translation id="5995727681868049093">Menaxho informacionin, privatësinë dhe sigurinë tënde në "Llogarinë tënde të Google"</translation>
+<translation id="5997247540087773573">Fjalëkalimi që sapo përdore u gjet në një nxjerrje të paautorizuar të të dhënave. Për të mbrojtur llogaritë e tua, "Menaxheri i fjalëkalimeve i Google" rekomandon ta ndryshosh atë tani dhe të kontrollosh fjalëkalimet e ruajtura.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> rezultate për "<ph name="SEARCH_TEXT" />"</translation>
<translation id="6006484371116297560">Klasike</translation>
<translation id="6008122969617370890">Renditja N deri në 1</translation>
@@ -1854,7 +1867,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="6051221802930200923">Nuk mund ta vizitosh <ph name="SITE" /> në këtë moment sepse sajti i uebit përdor gozhdimin e certifikatës. Gabimet dhe sulmet e rrjetit janë zakonisht të përkohshme, prandaj kjo faqe ndoshta do të funksionojë më vonë.</translation>
<translation id="6051898664905071243">Numri i faqeve:</translation>
<translation id="6052284303005792909">•</translation>
-<translation id="6052319569711353666">Fjalëkalimi që sapo përdore u gjet në një nxjerrje të paautorizuar të të dhënave. "Menaxheri i fjalëkalimeve" i Google rekomandon ndryshimin e këtij fjalëkalimi tani.</translation>
+<translation id="6052319569711353666">Fjalëkalimi që sapo përdore u gjet në një nxjerrje të paautorizuar të të dhënave. "Menaxheri i fjalëkalimeve i Google" rekomandon ndryshimin e këtij fjalëkalimi tani.</translation>
<translation id="6055888660316801977">Kredencial pagese të sigurt pa fletë përkatëse kredenciali</translation>
<translation id="6058977677006700226">Dëshiron t'i përdorësh kartat në të gjitha pajisjet e tua?</translation>
<translation id="6059925163896151826">Pajisjet USB</translation>
@@ -1918,7 +1931,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="627746635834430766">Për të paguar më shpejt herën tjetër, ruaje kartën dhe adresën tënde të faturimit në llogarinë tënde të Google.</translation>
<translation id="6279183038361895380">Shtyp |<ph name="ACCELERATOR" />| për të shfaqur kursorin</translation>
<translation id="6280223929691119688">Nuk mund të dorëzohet në këtë adresë. Zgjidh një adresë tjetër.</translation>
-<translation id="6282194474023008486">Kodi postar</translation>
<translation id="6285507000506177184">Butoni "Menaxho shkarkimet në Chrome". Shtyp "Enter" për të menaxhuar skedarët që ke shkarkuar në Chrome</translation>
<translation id="6289939620939689042">Ngjyra e faqes</translation>
<translation id="6290238015253830360">Artikujt e tu të sugjeruar shfaqen këtu</translation>
@@ -1940,6 +1952,7 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="6337133576188860026">Liron më pak se <ph name="SIZE" />. Disa sajte mund të ngarkohen më ngadalë gjatë vizitës tënde të radhës.</translation>
<translation id="6337534724793800597">Filtro politikat sipas emrit</translation>
<translation id="6340739886198108203">Politika e administratorit nuk rekomandon nxjerrjen e pamjeve të ekranit apo regjistrimeve kur përmbajtja konfidenciale është e dukshme:</translation>
+<translation id="6348220984832452017">Variantet aktive</translation>
<translation id="6349101878882523185">Instalo <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Asnjë}=1{1 fjalëkalim (për <ph name="DOMAIN_LIST" />, sinkronizuar)}=2{2 fjalëkalime (për <ph name="DOMAIN_LIST" />, sinkronizuar)}other{# fjalëkalime (për <ph name="DOMAIN_LIST" />, sinkronizuar)}}</translation>
<translation id="6355392890578844978">Ky shfletues nuk menaxhohet nga një kompani ose organizatë tjetër. Aktiviteti në këtë pajisje mund të menaxhohet jashtë Chromium. <ph name="BEGIN_LINK" />Mëso më shumë<ph name="END_LINK" /></translation>
@@ -1971,7 +1984,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="643051589346665201">Ndrysho fjalëkalimin e Google</translation>
<translation id="6433490469411711332">Redakto informacionin e kontaktit</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> e refuzoi lidhjen.</translation>
-<translation id="6438025220577812695">E ndryshoj vetë</translation>
<translation id="6440503408713884761">Shpërfillur</translation>
<translation id="6443406338865242315">Cilat shtesa dhe përbërës shtesë ke instaluar</translation>
<translation id="6446163441502663861">Kahu (Zarf)</translation>
@@ -2101,9 +2113,9 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="6828866289116430505">Gjenetika</translation>
<translation id="6831043979455480757">Përkthe</translation>
<translation id="6833752742582340615">Ruaj informacionin e kartës dhe të faturimit në "Llogarinë tënde të Google" për të përfunduar blerjet në mënyrë të sigurt dhe më shpejt</translation>
-<translation id="6839929833149231406">Zona</translation>
<translation id="6846340164947227603">Përdor një numër karte virtuale...</translation>
<translation id="6852204201400771460">Dëshiron ta ringarkosh aplikacionin?</translation>
+<translation id="6857776781123259569">Menaxho fjalëkalimet...</translation>
<translation id="686485648936420384">Burimet e konsumatorit</translation>
<translation id="6865412394715372076">Kjo kartë nuk mund të verifikohet për momentin</translation>
<translation id="6869334554832814367">Kreditë personale</translation>
@@ -2152,7 +2164,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="6965978654500191972">Pajisja</translation>
<translation id="696703987787944103">Perceptues</translation>
<translation id="6968269510885595029">Përdor "Çelësin e sigurisë"</translation>
-<translation id="6970216967273061347">Distrikti</translation>
<translation id="6971439137020188025">Krijo shpejt një prezantim të ri të Google në Slides</translation>
<translation id="6972629891077993081">Pajisjet HID</translation>
<translation id="6973656660372572881">Janë specifikuar serverë përfaqësues fiksë dhe një URL e skriptit .pac.</translation>
@@ -2191,7 +2202,6 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<translation id="7081308185095828845">Kjo veçori nuk ofrohet në pajisjen tënde</translation>
<translation id="7083258188081898530">Tabakaja 9</translation>
<translation id="7086090958708083563">Përditësimi u kërkua nga përdoruesi</translation>
-<translation id="7087282848513945231">Konteja</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. Shtyp "Tab" dhe më pas "Enter" për të menaxhuar lejet dhe të dhënat e ruajtura nëpër sajte te cilësimet e Chrome</translation>
<translation id="7096937462164235847">Identiteti i këtij sajti uebi nuk është i verifikuar.</translation>
<translation id="7101893872976785596">Filmat horror</translation>
@@ -2210,10 +2220,10 @@ Ndryshe kjo do të bllokohet nga cilësimet e tua të privatësisë. Kjo do ta l
<ph name="LIST_ITEM" />Informacionet e futura në formularë<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Nuk mund të dërgohet në këtë adresë. Zgjidh një adresë tjetër.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Administratori yt e ka ndaluar kopjimin e këtyre të dhënave.</translation>
<translation id="7135130955892390533">Shfaq statusin</translation>
<translation id="7138472120740807366">Mënyra e dorëzimit</translation>
-<translation id="7139724024395191329">Emirati</translation>
<translation id="7139892792842608322">Tabakaja parësore</translation>
<translation id="714064300541049402">Zhvendosja e imazhit në boshtin X në anën 2</translation>
<translation id="7152423860607593928">Number-14 (Zarf)</translation>
@@ -2430,6 +2440,7 @@ Detaje shtesë:
<translation id="7669271284792375604">Sulmuesit në këtë sajt mund të përpiqen të të mashtrojnë të instalosh programe që dëmtojnë përvojën tënde të shfletimit (për shembull, duke ndryshuar faqen kryesore ose duke shfaqur reklama shtesë në sajtet që viziton).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Veprimet e kryera me të dhënat e raportuara si konfidenciale (1 veprim që nga identifikimi). <ph name="BEGIN_LINK" />Mëso më shumë<ph name="END_LINK" />}other{Veprimet e kryera me të dhënat e raportuara si konfidenciale (# veprime që nga identifikimi). <ph name="BEGIN_LINK" />Mëso më shumë<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Kutia postare 6</translation>
+<translation id="7675325315208090829">Menaxho mënyrat e pagesës...</translation>
<translation id="7676643023259824263">Kërko për tekstin e kujtesës së fragmenteve, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Shiko dhe menaxho historikun e shfletimit te cilësimet e Chrome</translation>
<translation id="7679947978757153706">Bejsbolli</translation>
@@ -2472,7 +2483,6 @@ Detaje shtesë:
<translation id="7766518757692125295">Bordura</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">E njëjta renditje e kthyer lart</translation>
-<translation id="777702478322588152">Prefektura</translation>
<translation id="7791011319128895129">E papublikuar</translation>
<translation id="7791196057686275387">Lidhja në stivë</translation>
<translation id="7791543448312431591">Shtoje</translation>
@@ -2563,12 +2573,12 @@ Detaje shtesë:
<translation id="8055534648776115597">Arsimi profesional dhe i vazhduar</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" nuk është konfiguruar mirë. Zakonisht këtë problem e zgjidh çinstalimi i "<ph name="SOFTWARE_NAME" />". <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Prodhimi ushqimor</translation>
-<translation id="8066955247577885446">Na vjen keq! Diçka shkoi keq.</translation>
<translation id="8067872629359326442">Sapo fute fjalëkalimin tënd në një sajt mashtrues. Chromium mund të të ndihmojë. Për të ndryshuar fjalëkalimin dhe për të njoftuar Google se llogaria jote mund të jetë në rrezik, kliko te "Mbroje llogarinë".</translation>
<translation id="8070439594494267500">Ikona e aplikacionit</translation>
<translation id="8074253406171541171">10x13 (Zarf)</translation>
<translation id="8075736640322370409">Krijo shpejt një "Fletë të re të Google"</translation>
<translation id="8075898834294118863">Menaxho cilësimet e sajtit</translation>
+<translation id="8076492880354921740">Skedat</translation>
<translation id="8078141288243656252">Nuk mund të shënosh kur rrotullohet</translation>
<translation id="8079031581361219619">Dëshiron të ngarkosh përsëri sajtin?</translation>
<translation id="8081087320434522107">Veturat</translation>
@@ -2691,6 +2701,7 @@ Detaje shtesë:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Cilësimet</translation>
+<translation id="8428634594422941299">E kuptova</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. Shtyp "Tab" dhe më pas "Enter" për të menaxhuar preferencat e tua për kukit te cilësimet e Chrome</translation>
<translation id="8433057134996913067">Kjo do të bëjë të dalësh nga shumica e sajteve të uebit.</translation>
<translation id="8434840396568290395">Kafshët shtëpiake</translation>
@@ -2788,6 +2799,7 @@ Detaje shtesë:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> është kodi për <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Shtoje këtë skedë te faqeshënuesit</translation>
<translation id="8751426954251315517">Provo përsëri herën tjetër</translation>
+<translation id="8757526089434340176">Disponohet ofertë e Google Pay</translation>
<translation id="8758885506338294482">Videolojërat konkurruese</translation>
<translation id="8759274551635299824">Kjo kartë ka skaduar</translation>
<translation id="87601671197631245">Ky sajt përdor një konfigurim të vjetruar të sigurisë, i cili mund t'i ekspozojë informacionet e tua (p.sh. fjalëkalimet, mesazhet ose kartat e kreditit) kur dërgohen te ky sajt.</translation>
@@ -2795,6 +2807,7 @@ Detaje shtesë:
<translation id="8763927697961133303">Pajisja USB</translation>
<translation id="8763986294015493060">Mbylli të gjitha dritaret "e fshehta" që janë aktualisht të hapura</translation>
<translation id="8766943070169463815">Fleta e vërtetimit të sigurt të kredencialeve të pagesës është e hapur</translation>
+<translation id="8767765348545497220">Mbyll flluskën e ndihmës</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motoçikletat</translation>
<translation id="8790007591277257123">&amp;Bëje përsëri fshirjen</translation>
@@ -2807,6 +2820,7 @@ Detaje shtesë:
<translation id="8806285662264631610">Produktet për larjen dhe trupin</translation>
<translation id="8807160976559152894">Prerje pas çdo faqeje</translation>
<translation id="8808828119384186784">Cilësimet e Chrome</translation>
+<translation id="8813277370772331957">Më kujto më vonë</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, shtyp "Tab", më pas "Enter" për të përditësuar Chrome nga cilësimet e tua të Chrome</translation>
<translation id="8820817407110198400">Faqeshënuesit</translation>
<translation id="882338992931677877">Foleja manuale</translation>
@@ -2986,6 +3000,7 @@ Detaje shtesë:
<translation id="988159990683914416">Ndërtimi i zhvilluesit</translation>
<translation id="989988560359834682">Redakto adresën</translation>
<translation id="991413375315957741">sensorët e lëvizjes ose të dritës</translation>
+<translation id="992110854164447044">Një kartë virtuale fsheh kartën tënde aktuale për të të mbrojtur nga mashtrimi i mundshëm. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rozë</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" nuk është instaluar mirë në kompjuter ose në rrjet:
diff --git a/chromium/components/strings/components_strings_sr-Latn.xtb b/chromium/components/strings/components_strings_sr-Latn.xtb
index 53a0ddedad3..b44bbb1b8ee 100644
--- a/chromium/components/strings/components_strings_sr-Latn.xtb
+++ b/chromium/components/strings/components_strings_sr-Latn.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Donacije, stipendije i finansijska pomocÌ</translation>
<translation id="1048785276086539861">Kada izmenite komentare, ovaj dokument se vracÌa na prikaz pojedinaÄne stranice.</translation>
<translation id="1050038467049342496">Zatvorite druge aplikacije</translation>
+<translation id="1053959602163383901">Odabrali ste da se verifikujete pomocÌu ureÄ‘aja za potvrdu identiteta na veb-sajtovima koji koriste <ph name="PROVIDER_ORIGIN" />. Ovaj dobavljaÄ je možda saÄuvao informacije o naÄinu placÌanja, za koji možete da <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Opozovi dodavanje</translation>
<translation id="1056663316309890343">Softver za obradu fotografija</translation>
<translation id="1056898198331236512">Upozorenje</translation>
@@ -119,6 +120,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="1270502636509132238">NaÄin preuzimanja</translation>
<translation id="1281476433249504884">1. pregrada za slaganje</translation>
<translation id="1285320974508926690">Nikad ne prevodi ovaj sajt</translation>
+<translation id="1288548991597756084">Bezbedno saÄuvajte karticu</translation>
<translation id="1292571435393770077">16. fioka</translation>
<translation id="1292701964462482250">„Softver na vaÅ¡em raÄunaru onemogucÌava Chrome-u da se bezbedno poveže na veb“ (samo na Windows raÄunarima)</translation>
<translation id="1294154142200295408">Varijacije komandne linije</translation>
@@ -223,6 +225,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
&lt;p&gt;Da biste otklonili tu grešku, kliknite na &lt;strong&gt;Poveži se&lt;/strong&gt; na stranici koju pokušavate da otvorite.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Pejzažna arhitektura</translation>
<translation id="1513706915089223971">Lista unosa u istoriji</translation>
+<translation id="1516097932025103760">BicÌe Å¡ifrovana, bezbedno saÄuvana, a CVC se nikad ne Äuva.</translation>
<translation id="1517433312004943670">Broj telefona je obavezan</translation>
<translation id="1519264250979466059">Datum verzije</translation>
<translation id="1521159554480556801">Tekstilna umetnost</translation>
@@ -272,7 +275,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="1634828734222219955">Ukupno</translation>
<translation id="163669211644121865">Priprema i planiranje poreza</translation>
<translation id="1638780421120290329">ÄŒuvanje kartice nije uspelo</translation>
-<translation id="1639239467298939599">UÄitavanje</translation>
+<translation id="1639239467298939599">UÄitava se</translation>
<translation id="1640180200866533862">Smernice za korisnike</translation>
<translation id="1640244768702815859">PokuÅ¡ajte da <ph name="BEGIN_LINK" />posetite poÄetnu stranicu sajta<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">Odloži izlaz do</translation>
@@ -288,6 +291,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="1662550410081243962">ÄŒuvaj i unosi naÄine placÌanja</translation>
<translation id="1663943134801823270">Kartice i adrese su iz Chrome-a. Njima možete da upravljate u <ph name="BEGIN_LINK" />podešavanjima<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Stranice na jeziku <ph name="SOURCE_LANGUAGE" /> se od sada prevode na <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Platite pomocÌu kartice <ph name="CARD_DETAIL" /> da biste iskoristili ponudu</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> na <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Prvo kratka ivica</translation>
<translation id="168693727862418163">Validacija Å¡eme ove vrednosti za smernice nije uspela i zanemaricÌe se.</translation>
@@ -306,6 +310,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="1717218214683051432">Senzori pokreta</translation>
<translation id="1717494416764505390">3. poÅ¡tansko sanduÄe</translation>
<translation id="1718029547804390981">Dokument je isuviše veliki da biste mu dodali napomene</translation>
+<translation id="1720941539803966190">Zatvorite vodiÄ</translation>
<translation id="1721424275792716183">* Polje je obavezno</translation>
<translation id="1727613060316725209">Sertifikat je važecÌi</translation>
<translation id="1727741090716970331">Dodajte važecÌi broj kartice</translation>
@@ -422,8 +427,8 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="205212645995975601">Roštilj</translation>
<translation id="2053111141626950936">Stranice na jeziku <ph name="LANGUAGE" /> necÌe biti prevedene.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Kada je ova kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje veliku grupu ljudi, ili kohortu, kojoj su vaÅ¡e nedavne aktivnosti pregledanja najsliÄnije. OglaÅ¡avaÄi mogu da biraju oglase za grupu i vaÅ¡e aktivnosti pregledanja ostaju privatne na ureÄ‘aju. Grupa se ažurira svakog dana.}=1{Kada je ova kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje veliku grupu ljudi, ili kohortu, kojoj su vaÅ¡e nedavne aktivnosti pregledanja najsliÄnije. OglaÅ¡avaÄi mogu da biraju oglase za grupu i vaÅ¡e aktivnosti pregledanja ostaju privatne na ureÄ‘aju. Grupa se ažurira svakog dana.}one{Kada je ova kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje veliku grupu ljudi, ili kohortu, kojoj su vaÅ¡e nedavne aktivnosti pregledanja najsliÄnije. OglaÅ¡avaÄi mogu da biraju oglase za grupu i vaÅ¡e aktivnosti pregledanja ostaju privatne na ureÄ‘aju. Grupa se ažurira na {NUM_DAYS} dan.}few{Kada je ova kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje veliku grupu ljudi, ili kohortu, kojoj su vaÅ¡e nedavne aktivnosti pregledanja najsliÄnije. OglaÅ¡avaÄi mogu da biraju oglase za grupu i vaÅ¡e aktivnosti pregledanja ostaju privatne na ureÄ‘aju. Grupa se ažurira na {NUM_DAYS} dana.}other{Kada je ova kontrola ukljuÄena i status je aktivan, Chrome odreÄ‘uje veliku grupu ljudi, ili kohortu, kojoj su vaÅ¡e nedavne aktivnosti pregledanja najsliÄnije. OglaÅ¡avaÄi mogu da biraju oglase za grupu i vaÅ¡e aktivnosti pregledanja ostaju privatne na ureÄ‘aju. Grupa se ažurira na {NUM_DAYS} dana.}}</translation>
-<translation id="2053553514270667976">Poštanski broj</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 predlog}one{# predlog}few{# predloga}other{# predloga}}</translation>
+<translation id="2066915425250589881">zatražite brisanje</translation>
<translation id="2068528718802935086">Bebe i mala deca</translation>
<translation id="2071156619270205202">Ova kartica ne ispunjava uslove za broj virtuelne kartice.</translation>
<translation id="2071692954027939183">ObaveÅ¡tenja su automatski blokirana jer ih obiÄno ne dozvoljavate</translation>
@@ -435,7 +440,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="2088086323192747268">Dugme Upravljajte sinhronizacijom, pritisnite Enter da biste upravljali time koje informacije sinhronizujete u podešavanjima Chrome-a</translation>
<translation id="2091887806945687916">Zvuk</translation>
<translation id="2094505752054353250">Domeni se ne podudaraju</translation>
-<translation id="2096368010154057602">Odsek</translation>
<translation id="2099652385553570808">Trostruko spajanje na levoj strani</translation>
<translation id="2101225219012730419">Verzija:</translation>
<translation id="2102134110707549001">Predloži jaku lozinku…</translation>
@@ -472,7 +476,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="2185836064961771414">AmeriÄki fudbal</translation>
<translation id="2187317261103489799">Otkrij (podrazumevano)</translation>
<translation id="2188375229972301266">Višestruko bušenje na dnu</translation>
-<translation id="2188852899391513400">Lozinka koju ste upravo koristili je pronaÄ‘ena pri povredi podataka. Da biste zaÅ¡titili naloge, Google menadžer lozinki preporuÄuje da je odmah promenite, pa da proverite saÄuvane lozinke.</translation>
<translation id="219906046732893612">Uređenje doma</translation>
<translation id="2202020181578195191">Unesite važecÌu godinu isteka</translation>
<translation id="22081806969704220">3. fioka</translation>
@@ -483,6 +486,8 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="2215727959747642672">Menjanje datoteka</translation>
<translation id="2215963164070968490">Psi</translation>
<translation id="2218879909401188352">NapadaÄi koji su trenutno na <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogu da instaliraju opasne aplikacije koje cÌe vam oÅ¡tetiti ureÄ‘aj, dodati neželjene troÅ¡kove kod mobilnog operatera ili ukrasti liÄne podatke. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Restartuj vodiÄ</translation>
+<translation id="2219735899272417925">Treba da resetujete uređaj</translation>
<translation id="2224337661447660594">Nema interneta</translation>
<translation id="2230458221926704099">Popravite vezu pomocÌu <ph name="BEGIN_LINK" />aplikacije za dijagnostiku<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Pošalji odmah</translation>
@@ -580,11 +585,13 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="2512101340618156538">Nije dozvoljeno (podrazumevano)</translation>
<translation id="2512413427717747692">Dugme Podesi Chrome kao podrazumevani pregledaÄ, pritisnite Enter da biste podesili Chrome kao podrazumevani pregledaÄ sistema u podeÅ¡avanjima iOS-a</translation>
<translation id="2515629240566999685">da proverite signal u svojoj oblasti</translation>
+<translation id="2515761554693942801">Odabrali ste da se verifikujete pomocÌu Touch ID-a na veb-sajtovima koji koriste <ph name="PROVIDER_ORIGIN" />. Ovaj dobavljaÄ je možda saÄuvao informacije o naÄinu placÌanja, za koji možete da <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Spajanje u donjem desnom uglu</translation>
<translation id="2521736961081452453">Napravite upitnik</translation>
<translation id="2523886232349826891">SaÄuvano je samo na ovom ureÄ‘aju</translation>
<translation id="2524461107774643265">Dodajte još informacija</translation>
<translation id="2529899080962247600">Ovo polje ne sme da ima viÅ¡e od <ph name="MAX_ITEMS_LIMIT" /> unosa. Svi buducÌi unosi cÌe se zanemariti.</translation>
+<translation id="253493526287553278">Prikaži detalje o promotivnom kodu</translation>
<translation id="2535585790302968248">Otvorite novu karticu bez arhiviranja da biste pregledali privatno</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{i još 1}one{i još #}few{i još #}other{i još #}}</translation>
<translation id="2536110899380797252">Dodaj adresu</translation>
@@ -620,7 +627,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="259821504105826686">Fotografska i digitalna umetnost</translation>
<translation id="2601150049980261779">Ljubavni filmovi</translation>
<translation id="2604589665489080024">Pop muzika</translation>
-<translation id="2609632851001447353">Varijacije</translation>
<translation id="2610561535971892504">Kliknite da biste kopirali</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />ne Äuva<ph name="END_EMPHASIS" /> sledecÌe informacije:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Rođendani i imendani</translation>
<translation id="2677748264148917807">Zatvori</translation>
+<translation id="2679714844901977852">SaÄuvajte informacije o kartici i obraÄunu na Google nalogu <ph name="USER_EMAIL" /> radi bezbednih i bržih placÌanja</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Najbolja veliÄina</translation>
<translation id="2688969097326701645">Da, nastavi</translation>
@@ -701,7 +708,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="2854764410992194509">Internet provajderi</translation>
<translation id="2856444702002559011">NapadaÄi možda pokuÅ¡avaju da ukradu informacije sa <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (na primer, lozinke, poruke ili kreditne kartice). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Ovaj sajt prikazuje oglase koji ometaju aktivnosti ili obmanjujucÌe oglase.</translation>
-<translation id="286512204874376891">Virtuelna kartica krije vašu stvarnu karticu da bi vas zaštitila od potencijalne prevare. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Prijateljski</translation>
<translation id="28761159517501904">Filmovi</translation>
<translation id="2876489322757410363">NapusticÌete režim bez arhiviranja da biste platili u spoljnoj aplikaciji. Želite li da nastavite?</translation>
@@ -802,7 +808,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">WiFi mreža koju koristite cÌe možda zahtevati da posetite stranicu za prijavljivanje.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ostrvo</translation>
<translation id="3176929007561373547">Proverite podešavanja proksija ili kontaktirajte administratora mreže da
biste se uverili da proksi server funkcioniše. Ako mislite da ne
treba da koristite proksi server:
@@ -881,9 +886,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="3369192424181595722">Greška na satu</translation>
<translation id="3369459162151165748">Delovi i oprema za vozila</translation>
<translation id="3371064404604898522">Podesi Chrome kao podrazumevani pregledaÄ</translation>
-<translation id="3371076217486966826"><ph name="URL" /> želi:
- • da pravi 3D mapu okruženja i da prati položaj kamere
- • da koristi kameru</translation>
<translation id="337363190475750230">OnemogucÌen je</translation>
<translation id="3375754925484257129">Pokreni Chrome proveru bezbednosti</translation>
<translation id="3377144306166885718">Server koristi zastarelu verziju TLS-a.</translation>
@@ -900,6 +902,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="3399952811970034796">Adresa isporuke</translation>
<translation id="3402261774528610252">Veza koriÅ¡cÌena za uÄitavanje ovog sajta je koristila TLS 1.0 ili TLS 1.1, koji su zastareli i bicÌe onemogucÌeni u buducÌnosti. Kada budu onemogucÌeni, korisnici necÌe mocÌi da uÄitaju ovaj sajt. Server treba da omogucÌi TLS 1.2 ili noviju verziju.</translation>
<translation id="3405664148539009465">Prilagodi fontove</translation>
+<translation id="3407789382767355356">prijavljivanje trecÌe strane</translation>
<translation id="3409896703495473338">Upravljajte bezbednosnim podešavanjima</translation>
<translation id="3414952576877147120">VeliÄina:</translation>
<translation id="3417660076059365994">Datoteke koje otpremite ili priložite se Å¡alju u Google Cloud ili trecÌim stranama na analizu. Na primer, možda cÌe biti skenirane u potrazi za osetljivim podacima ili malverom.</translation>
@@ -932,6 +935,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="3477679029130949506">Liste filmova i repertoari bioskopa</translation>
<translation id="3479552764303398839">Ne sada</translation>
<translation id="3484560055331845446">Mogli biste da izgubite pristup Google nalogu. Chrome preporuÄuje da odmah promenite lozinku. MoracÌete da se prijavite.</translation>
+<translation id="3484861421501147767">Podsetnik: Dostupan je saÄuvani promotivni kôd</translation>
<translation id="3487845404393360112">4. fioka</translation>
<translation id="3495081129428749620">Pronađite na stranici
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="3810973564298564668">Upravljaj</translation>
<translation id="3816482573645936981">Vrednost (zamenjena)</translation>
<translation id="382518646247711829">Ako koristite proksi server...</translation>
+<translation id="3826050100957962900">Prijavljivanje trecÌe strane</translation>
<translation id="3827112369919217609">Apsolut</translation>
<translation id="3827666161959873541">PorodiÄni filmovi</translation>
<translation id="3828924085048779000">Nije dozvoljeno da polje za pristupnu frazu bude prazno.</translation>
@@ -1068,9 +1073,9 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="3858027520442213535">Ažurirajte datum i vreme</translation>
<translation id="3858860766373142691">Naziv</translation>
<translation id="3872834068356954457">Nauka</translation>
+<translation id="3875783148670536197">Pokaži mi kako</translation>
<translation id="3881478300875776315">Prikaži manje redova</translation>
<translation id="3884278016824448484">Neusaglašeni identifikator uređaja</translation>
-<translation id="3885155851504623709">Parohija</translation>
<translation id="388632593194507180">Otkriveno je pracÌenje</translation>
<translation id="3886948180919384617">3. pregrada za slaganje</translation>
<translation id="3890664840433101773">Dodajte imejl</translation>
@@ -1100,9 +1105,11 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="3973234410852337861">Host <ph name="HOST_NAME" /> je blokiran</translation>
<translation id="3978338123949022456">Režim pretrage, unesite upit i pritisnite Enter da biste pretražili sa sufiksom <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Ptice</translation>
+<translation id="3985750352229496475">Upravljajte adresama…</translation>
<translation id="3986705137476756801">IskljuÄi Titl uživo za sada</translation>
<translation id="3987940399970879459">Manje od 1 MB</translation>
<translation id="3990250421422698716">Protresanje radi poravnanja</translation>
+<translation id="3992684624889376114">O ovoj stranici</translation>
<translation id="3996311196211510766">Sajt <ph name="ORIGIN" /> je zatražio da se smernice za poreklo
primene na sve zahteve ka njemu, ali ove smernice trenutno ne mogu da se primene.</translation>
<translation id="4006465311664329701">NaÄini placÌanja, ponude i adrese koji koriste Google Pay</translation>
@@ -1227,6 +1234,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="4305666528087210886">Pristup datoteci nije uspeo</translation>
<translation id="4306529830550717874">Želite da saÄuvate adresu?</translation>
<translation id="4306812610847412719">privremena memorija</translation>
+<translation id="4310070645992025887">Pretražite svoje puteve</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blokiraj (podrazumevano)</translation>
<translation id="4314815835985389558">Upravljajte sinhronizacijom</translation>
@@ -1277,6 +1285,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="4435702339979719576">razglednica)</translation>
<translation id="443673843213245140">KoriÅ¡cÌenje proksija je onemogucÌeno, ali je navedena eksplicitna konfiguracija proksija.</translation>
<translation id="4441832193888514600">Ignoriše se jer smernice mogu da se podese samo kao smernice za korisnika u klaudu.</translation>
+<translation id="4442470707340296952">Chrome kartice</translation>
<translation id="4450893287417543264">Ne prikazuj ponovo</translation>
<translation id="4451135742916150903">Može da traži da se povezuje sa HID uređajima</translation>
<translation id="4452328064229197696">Lozinka koju ste upravo koristili je pronaÄ‘ena pri povredi podataka. Da biste zaÅ¡titili naloge, Google menadžer lozinki preporuÄuje da proverite saÄuvane lozinke.</translation>
@@ -1415,6 +1424,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="483241715238664915">UkljuÄi upozorenja</translation>
<translation id="4834250788637067901">NaÄini placÌanja, ponude i adrese koji koriste Google Pay</translation>
<translation id="4838327282952368871">ÄŒarobno</translation>
+<translation id="4839087176073128681">SledecÌeg puta platite brže i zaÅ¡titite karticu pomocÌu vrhunske Google bezbednosti.</translation>
<translation id="4840250757394056958">Pregledajte istoriju Chrome-a</translation>
<translation id="484462545196658690">Automatski</translation>
<translation id="484671803914931257">Ostvarite popust kod prodavca <ph name="MERCHANT_NAME" /> i drugih prodavaca</translation>
@@ -1477,6 +1487,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="5011561501798487822">Otkriveni jezik</translation>
<translation id="5015510746216210676">Naziv uređaja:</translation>
<translation id="5017554619425969104">Tekst koji ste kopirali</translation>
+<translation id="5017828934289857214">Podseti me kasnije</translation>
<translation id="5018422839182700155">Ne možemo da otvorimo ovu stranicu</translation>
<translation id="5019198164206649151">Skladište toka podataka je u lošem stanju</translation>
<translation id="5020776957610079374">Etno muzika</translation>
@@ -1496,6 +1507,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="5051305769747448211">Komedija uživo</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Da biste poslali ovaj fajl pomocÌu Deljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na ureÄ‘aju}one{Da biste poslali ove fajlove pomocÌu Deljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na ureÄ‘aju}few{Da biste poslali ove fajlove pomocÌu Deljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na ureÄ‘aju}other{Da biste poslali ove fajlove pomocÌu Deljenja u blizini, oslobodite prostor (<ph name="DISK_SPACE_SIZE" />) na ureÄ‘aju}}</translation>
<translation id="5056549851600133418">ÄŒlanci za vas</translation>
+<translation id="5060483733937416656">Odabrali ste da se verifikujete pomocÌu Windows Hello-a na veb-sajtovima koji koriste <ph name="PROVIDER_ORIGIN" />. Ovaj dobavljaÄ je možda saÄuvao informacije o naÄinu placÌanja, za koji možete da <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Da li ste mislili <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Istorija Å¡tampanja</translation>
<translation id="5068234115460527047">Hedž fondovi</translation>
@@ -1509,10 +1521,8 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="5087286274860437796">Sertifikat servera trenutno nije važecÌi.</translation>
<translation id="5087580092889165836">Dodaj karticu</translation>
<translation id="5088142053160410913">Poruka operateru</translation>
-<translation id="5089810972385038852">Država</translation>
<translation id="5093232627742069661">Presavijanje u obliku slova Z</translation>
<translation id="5094747076828555589">Ovaj server ne može da dokaže da je <ph name="DOMAIN" />; Chromium nema poverenja u njegov bezbednosni sertifikat. Uzrok tome je možda pogreÅ¡na konfiguracija ili napadaÄ koji je prekinuo vezu.</translation>
-<translation id="5095208057601539847">Pokrajina</translation>
<translation id="5097099694988056070">StatistiÄki podaci o ureÄ‘aju, poput iskoriÅ¡cÌenosti procesora/RAM memorije</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Sajt nije bezbedan</translation>
@@ -1550,6 +1560,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="5171045022955879922">Pretražite ili unesite URL adresu</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">RaÄunar</translation>
+<translation id="5177076414499237632">Saznajte više o izvoru i temi ove stranice</translation>
<translation id="5179510805599951267">Nije <ph name="ORIGINAL_LANGUAGE" />? Prijavite ovu grešku</translation>
<translation id="518639307526414276">Hrana i sredstva za negu kucÌnih ljubimaca</translation>
<translation id="5190835502935405962">Traka sa obeleživaÄima</translation>
@@ -1710,6 +1721,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="5624120631404540903">Upravljaj lozinkama</translation>
<translation id="5629630648637658800">UÄitavanje podeÅ¡avanja smernica nije uspelo</translation>
<translation id="5631439013527180824">NevažecÌi token za upravljanje ureÄ‘ajima</translation>
+<translation id="5632485077360054581">Pokaži mi kako</translation>
<translation id="5633066919399395251">NapadaÄi na <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> cÌe možda pokuÅ¡ati da instaliraju opasne programe na raÄunaru koji kradu ili briÅ¡u informacije (na primer, slike, lozinke, poruke i brojeve kreditnih kartica). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">ObmanjujucÌi sadržaj je blokiran.</translation>
<translation id="5633259641094592098">Kultni i indi filmovi</translation>
@@ -1827,6 +1839,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="5989320800837274978">Nisu navedeni ni fiksni proksi serveri niti URL adresa .pac skripte.</translation>
<translation id="5992691462791905444">Presavijanje u obliku polu-harmonike</translation>
<translation id="5995727681868049093">Upravljajte informacijama, privatnoÅ¡cÌu i bezbednoÅ¡cÌu na Google nalogu</translation>
+<translation id="5997247540087773573">Lozinka koju ste upravo koristili je pronaÄ‘ena pri povredi podataka. Da biste zaÅ¡titili naloge, Google menadžer lozinki preporuÄuje da je odmah promenite i proverite saÄuvane lozinke.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> rezultata za „<ph name="SEARCH_TEXT" />“</translation>
<translation id="6006484371116297560">KlasiÄna</translation>
<translation id="6008122969617370890">Redosled od n do 1</translation>
@@ -1921,7 +1934,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="627746635834430766">Da biste sledecÌi put platili brže, saÄuvajte karticu i adresu za obraÄun na Google nalogu.</translation>
<translation id="6279183038361895380">Pritisnite |<ph name="ACCELERATOR" />| da biste prikazali pokazivaÄ</translation>
<translation id="6280223929691119688">Isporuka na ovu adresu nije mogucÌa. Izaberite drugu adresu.</translation>
-<translation id="6282194474023008486">Poštanski broj</translation>
<translation id="6285507000506177184">Dugme za upravljanje preuzimanjima u Chrome-u, pritisnite Enter da biste upravljali fajlovima koje ste preuzeli u Chrome-u</translation>
<translation id="6289939620939689042">Boja stranice</translation>
<translation id="6290238015253830360">Predloženi Älanci se prikazuju ovde</translation>
@@ -1943,6 +1955,7 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="6337133576188860026">OslobaÄ‘a manje od <ph name="SIZE" />. Neki sajtovi cÌe se možda sporije uÄitavati kad ih sledecÌi put posetite.</translation>
<translation id="6337534724793800597">Filtriraj smernice prema nazivu</translation>
<translation id="6340739886198108203">Smernice administratora ne preporuÄuju pravljenje snimaka ekrana ili snimaka kada je poverljiv sadržaj vidljiv:</translation>
+<translation id="6348220984832452017">Aktivne varijacije</translation>
<translation id="6349101878882523185">Instalirajte <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Nijedna}=1{1 lozinka (za <ph name="DOMAIN_LIST" />, sinhronizovano)}=2{2 lozinke (za <ph name="DOMAIN_LIST" />, sinhronizovano)}one{# lozinka (za <ph name="DOMAIN_LIST" />, sinhronizovano)}few{# lozinke (za <ph name="DOMAIN_LIST" />, sinhronizovano)}other{# lozinki (za <ph name="DOMAIN_LIST" />, sinhronizovano)}}</translation>
<translation id="6355392890578844978">Ovim pregledaÄem ne upravlja kompanija niti druga organizacija. Aktivnostima na ovom ureÄ‘aju se možda upravlja van Chromium-a. <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" /></translation>
@@ -1974,7 +1987,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="643051589346665201">Promenite Google lozinku</translation>
<translation id="6433490469411711332">Izmenite kontakt informacije</translation>
<translation id="6433595998831338502">Host <ph name="HOST_NAME" /> je odbio povezivanje.</translation>
-<translation id="6438025220577812695">LiÄno cÌu to promeniti</translation>
<translation id="6440503408713884761">Zanemareno</translation>
<translation id="6443406338865242315">dodatke i dodatne komponenta koje ste instalirali</translation>
<translation id="6446163441502663861">Kahu (koverat)</translation>
@@ -2104,9 +2116,9 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Prevedi</translation>
<translation id="6833752742582340615">SaÄuvajte informacije o kartici i obraÄunu na Google nalogu radi bezbednih i bržih placÌanja</translation>
-<translation id="6839929833149231406">Oblast</translation>
<translation id="6846340164947227603">Koristi broj virtuelne kartice...</translation>
<translation id="6852204201400771460">Želite li da ponovo uÄitate aplikaciju?</translation>
+<translation id="6857776781123259569">Upravljajte lozinkama…</translation>
<translation id="686485648936420384">Resursi za potroÅ¡aÄe</translation>
<translation id="6865412394715372076">Trenutno ne možemo da verifikujemo ovu karticu</translation>
<translation id="6869334554832814367">LiÄne pozajmice</translation>
@@ -2155,7 +2167,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="6965978654500191972">Uređaj</translation>
<translation id="696703987787944103">Opažajno</translation>
<translation id="6968269510885595029">Koristite bezbednosni kljuÄ</translation>
-<translation id="6970216967273061347">Okrug</translation>
<translation id="6971439137020188025">Brzo napravite novu Google prezentaciju u Prezentacijama</translation>
<translation id="6972629891077993081">Uređaji sa interfejsom</translation>
<translation id="6973656660372572881">Navedeni su i fiksni proksi serveri i URL adresa .pac skripte.</translation>
@@ -2194,7 +2205,6 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<translation id="7081308185095828845">Ova funkcija nije dostupna na uređaju</translation>
<translation id="7083258188081898530">9. fioka</translation>
<translation id="7086090958708083563">Korisnik je tražio otpremanje</translation>
-<translation id="7087282848513945231">Okrug</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite Tab, pa Enter da biste upravljali dozvolama i podacima saÄuvanim na razliÄitim sajtovima u podeÅ¡avanjima Chrome-a</translation>
<translation id="7096937462164235847">Identitet ovog veb-sajta nije verifikovan.</translation>
<translation id="7101893872976785596">Horor filmovi</translation>
@@ -2213,10 +2223,10 @@ To inaÄe blokiraju podeÅ¡avanja privatnosti. To omogucÌava da sadržaj sa koji
<ph name="LIST_ITEM" />Informacije unete u obrascima<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Slanje na ovu adresu nije mogucÌe. Izaberite drugu adresu.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Administrator je zabranio kopiranje ovih podataka.</translation>
<translation id="7135130955892390533">Prikaži status</translation>
<translation id="7138472120740807366">NaÄin isporuke</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Primarna fioka</translation>
<translation id="714064300541049402">Pomeranje slike 2. strane po X osi</translation>
<translation id="7152423860607593928">Broj-14 (koverat)</translation>
@@ -2433,6 +2443,7 @@ Dodatni detalji:
<translation id="7669271284792375604">NapadaÄi na ovom sajtu mogu da pokuÅ¡aju da vas prevare da biste instalirali programe koji Å¡tete doživljaju pregledanja (na primer, tako Å¡to menjaju poÄetnu stranicu ili prikazuju dodatne oglase na sajtovima koje posecÌujete).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Radnje preduzete sa podacima koji su oznaÄeni kao poverljivi (1 radnja od kada ste se prijavili). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}one{Radnje preduzete sa podacima koji su oznaÄeni kao poverljivi (# radnja od kada ste se prijavili). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}few{Radnje preduzete sa podacima koji su oznaÄeni kao poverljivi (# radnje od kada ste se prijavili). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}other{Radnje preduzete sa podacima koji su oznaÄeni kao poverljivi (# radnji od kada ste se prijavili). <ph name="BEGIN_LINK" />Saznajte viÅ¡e<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">6. poÅ¡tansko sanduÄe</translation>
+<translation id="7675325315208090829">Upravljajte naÄinima placÌanja…</translation>
<translation id="7676643023259824263">Potražite tekst u privremenoj memoriji, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Pregledajte istoriju pregledanja i upravljajte njom u podešavanjima Chrome-a</translation>
<translation id="7679947978757153706">Bejzbol</translation>
@@ -2475,7 +2486,6 @@ Dodatni detalji:
<translation id="7766518757692125295">Obod</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Istim redosledom sa odštampanom stranom nagore</translation>
-<translation id="777702478322588152">Prefektura</translation>
<translation id="7791011319128895129">Neobjavljeno</translation>
<translation id="7791196057686275387">Baliranje</translation>
<translation id="7791543448312431591">Dodaj</translation>
@@ -2566,12 +2576,12 @@ Dodatni detalji:
<translation id="8055534648776115597">StruÄno osposobljavanje i kontinuirano obrazovanje</translation>
<translation id="8057711352706143257">Softver „<ph name="SOFTWARE_NAME" />“ nije pravilno konfigurisan. Deinstaliranje softvera „<ph name="SOFTWARE_NAME" />“ obiÄno reÅ¡ava problem. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Proizvodnja hrane</translation>
-<translation id="8066955247577885446">Žao nam je, došlo je do greške.</translation>
<translation id="8067872629359326442">Upravo ste uneli lozinku na obmanjujucÌem sajtu. Chromium može da vam pomogne. Da biste promenili lozinku i obavestili Google da je nalog možda ugrožen, kliknite na ZaÅ¡titi nalog.</translation>
<translation id="8070439594494267500">Ikona aplikacije</translation>
<translation id="8074253406171541171">10x13 (koverat)</translation>
<translation id="8075736640322370409">Brzo napravite novu Google tabelu</translation>
<translation id="8075898834294118863">Upravljajte podešavanjima sajtova</translation>
+<translation id="8076492880354921740">Kartice</translation>
<translation id="8078141288243656252">Dodavanje napomene nije mogucÌe kada je rotiran</translation>
<translation id="8079031581361219619">Želite li ponovo da uÄitate sajt?</translation>
<translation id="8081087320434522107">Sedani</translation>
@@ -2694,6 +2704,7 @@ Dodatni detalji:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Podešavanja</translation>
+<translation id="8428634594422941299">Važi</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite Tab, pa Enter da biste upravljali podeÅ¡avanjima kolaÄicÌa u podeÅ¡avanjima Chrome-a</translation>
<translation id="8433057134996913067">Ovako cÌete se odjaviti sa vecÌine veb-sajtova.</translation>
<translation id="8434840396568290395">KucÌni ljubimci</translation>
@@ -2792,6 +2803,7 @@ Dodatni detalji:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> je vaš kôd za <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Obeležite ovu karticu</translation>
<translation id="8751426954251315517">Probajte ponovo sledecÌi put</translation>
+<translation id="8757526089434340176">Dostupna je Google Pay ponuda</translation>
<translation id="8758885506338294482">TakmiÄenja u video igrama</translation>
<translation id="8759274551635299824">Ova kartica je istekla</translation>
<translation id="87601671197631245">Ovaj sajt koristi zastarelu bezbednosnu konfiguraciju koja može da otkrije vaše informacije (na primer, lozinke, poruke ili kreditne kartice) kada se šalju tom sajtu.</translation>
@@ -2799,6 +2811,7 @@ Dodatni detalji:
<translation id="8763927697961133303">USB uređaj</translation>
<translation id="8763986294015493060">Zatvorite sve prozore bez arhiviranja koji su trenutno otvoreni</translation>
<translation id="8766943070169463815">Tabela za potvrdu akreditiva za bezbedno placÌanje je otvorena</translation>
+<translation id="8767765348545497220">Zatvorite oblaÄicÌ za pomocÌ</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motocikli</translation>
<translation id="8790007591277257123">&amp;Ponovi brisanje</translation>
@@ -2811,6 +2824,7 @@ Dodatni detalji:
<translation id="8806285662264631610">Proizvodi za kupanje i negu tela</translation>
<translation id="8807160976559152894">SkracÌivanje posle svake stranice</translation>
<translation id="8808828119384186784">Podešavanje Chrome-a</translation>
+<translation id="8813277370772331957">Podseti me kasnije</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, pritisnite Tab, pa Enter da biste ažurirali Chrome iz podešavanja Chrome-a</translation>
<translation id="8820817407110198400">ObeleživaÄi</translation>
<translation id="882338992931677877">Otvor za ruÄni unos</translation>
@@ -2990,6 +3004,7 @@ Dodatni detalji:
<translation id="988159990683914416">Verzija za programere</translation>
<translation id="989988560359834682">Izmena adrese</translation>
<translation id="991413375315957741">senzori za pokret ili svetlo</translation>
+<translation id="992110854164447044">Virtuelna kartica krije vašu stvarnu karticu da bi vas zaštitila od potencijalne prevare. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">RužiÄasta</translation>
<translation id="992432478773561401">Softver „<ph name="SOFTWARE_NAME" />“ nije pravilno instaliran na raÄunaru ili mreži:
diff --git a/chromium/components/strings/components_strings_sr.xtb b/chromium/components/strings/components_strings_sr.xtb
index a11d8ec6880..7da64fa81d1 100644
--- a/chromium/components/strings/components_strings_sr.xtb
+++ b/chromium/components/strings/components_strings_sr.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Донације, Ñтипендије и финанÑијÑка помоћ</translation>
<translation id="1048785276086539861">Када измените коментаре, овај документ Ñе враћа на приказ појединачне Ñтранице.</translation>
<translation id="1050038467049342496">Затворите друге апликације</translation>
+<translation id="1053959602163383901">Одабрали Ñте да Ñе верификујете помоћу уређаја за потврду идентитета на веб-Ñајтовима који кориÑте <ph name="PROVIDER_ORIGIN" />. Овај добављач је можда Ñачувао информације о начину плаћања, за који можете да <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Опозови додавање</translation>
<translation id="1056663316309890343">Софтвер за обраду фотографија</translation>
<translation id="1056898198331236512">Упозорење</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">Ðачин преузимања</translation>
<translation id="1281476433249504884">1. преграда за Ñлагање</translation>
<translation id="1285320974508926690">Ðикад не преводи овај Ñајт</translation>
+<translation id="1288548991597756084">Безбедно Ñачувајте картицу</translation>
<translation id="1292571435393770077">16. фиока</translation>
<translation id="1292701964462482250">„Софтвер на вашем рачунару онемогућава Chrome-у да Ñе безбедно повеже на веб“ (Ñамо на Windows рачунарима)</translation>
<translation id="1294154142200295408">Варијације командне линије</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Да биÑте отклонили ту грешку, кликните на &lt;strong&gt;Повежи Ñе&lt;/strong&gt; на Ñтраници коју покушавате да отворите.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Пејзажна архитектура</translation>
<translation id="1513706915089223971">ЛиÑта уноÑа у иÑторији</translation>
+<translation id="1516097932025103760">Биће шифрована, безбедно Ñачувана, а CVC Ñе никад не чува.</translation>
<translation id="1517433312004943670">Број телефона је обавезан</translation>
<translation id="1519264250979466059">Датум верзије</translation>
<translation id="1521159554480556801">ТекÑтилна уметноÑÑ‚</translation>
@@ -272,7 +275,7 @@
<translation id="1634828734222219955">Укупно</translation>
<translation id="163669211644121865">Припрема и планирање пореза</translation>
<translation id="1638780421120290329">Чување картице није уÑпело</translation>
-<translation id="1639239467298939599">Учитавање</translation>
+<translation id="1639239467298939599">Учитава Ñе</translation>
<translation id="1640180200866533862">Смернице за кориÑнике</translation>
<translation id="1640244768702815859">Покушајте да <ph name="BEGIN_LINK" />поÑетите почетну Ñтраницу Ñајта<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">Одложи излаз до</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Чувај и уноÑи начине плаћања</translation>
<translation id="1663943134801823270">Картице и адреÑе Ñу из Chrome-а. Њима можете да управљате у <ph name="BEGIN_LINK" />подешавањима<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Странице на језику <ph name="SOURCE_LANGUAGE" /> Ñе од Ñада преводе на <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Платите помоћу картице <ph name="CARD_DETAIL" /> да биÑте иÑкориÑтили понуду</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> на <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Прво кратка ивица</translation>
<translation id="168693727862418163">Валидација шеме ове вредноÑти за Ñмернице није уÑпела и занемариће Ñе.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Сензори покрета</translation>
<translation id="1717494416764505390">3. поштанÑко Ñандуче</translation>
<translation id="1718029547804390981">Документ је иÑувише велики да биÑте му додали напомене</translation>
+<translation id="1720941539803966190">Затворите водич</translation>
<translation id="1721424275792716183">* Поље је обавезно</translation>
<translation id="1727613060316725209">Сертификат је важећи</translation>
<translation id="1727741090716970331">Додајте важећи број картице</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">Роштиљ</translation>
<translation id="2053111141626950936">Странице на језику <ph name="LANGUAGE" /> неће бити преведене.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Када је ова контрола укључена и ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ˜Ðµ активан, Chrome одређује велику групу људи, или кохорту, којој Ñу ваше недавне активноÑти прегледања најÑличније. Оглашавачи могу да бирају оглаÑе за групу и ваше активноÑти прегледања оÑтају приватне на уређају. Група Ñе ажурира Ñваког дана.}=1{Када је ова контрола укључена и ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ˜Ðµ активан, Chrome одређује велику групу људи, или кохорту, којој Ñу ваше недавне активноÑти прегледања најÑличније. Оглашавачи могу да бирају оглаÑе за групу и ваше активноÑти прегледања оÑтају приватне на уређају. Група Ñе ажурира Ñваког дана.}one{Када је ова контрола укључена и ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ˜Ðµ активан, Chrome одређује велику групу људи, или кохорту, којој Ñу ваше недавне активноÑти прегледања најÑличније. Оглашавачи могу да бирају оглаÑе за групу и ваше активноÑти прегледања оÑтају приватне на уређају. Група Ñе ажурира на {NUM_DAYS} дан.}few{Када је ова контрола укључена и ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ˜Ðµ активан, Chrome одређује велику групу људи, или кохорту, којој Ñу ваше недавне активноÑти прегледања најÑличније. Оглашавачи могу да бирају оглаÑе за групу и ваше активноÑти прегледања оÑтају приватне на уређају. Група Ñе ажурира на {NUM_DAYS} дана.}other{Када је ова контрола укључена и ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ˜Ðµ активан, Chrome одређује велику групу људи, или кохорту, којој Ñу ваше недавне активноÑти прегледања најÑличније. Оглашавачи могу да бирају оглаÑе за групу и ваше активноÑти прегледања оÑтају приватне на уређају. Група Ñе ажурира на {NUM_DAYS} дана.}}</translation>
-<translation id="2053553514270667976">ПоштанÑки број</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 предлог}one{# предлог}few{# предлога}other{# предлога}}</translation>
+<translation id="2066915425250589881">затражите бриÑање</translation>
<translation id="2068528718802935086">Бебе и мала деца</translation>
<translation id="2071156619270205202">Ова картица не иÑпуњава уÑлове за број виртуелне картице.</translation>
<translation id="2071692954027939183">Обавештења Ñу аутоматÑки блокирана јер их обично не дозвољавате</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">Дугме Управљајте Ñинхронизацијом, притиÑните Enter да биÑте управљали тиме које информације Ñинхронизујете у подешавањима Chrome-а</translation>
<translation id="2091887806945687916">Звук</translation>
<translation id="2094505752054353250">Домени Ñе не подударају</translation>
-<translation id="2096368010154057602">ОдÑек</translation>
<translation id="2099652385553570808">ТроÑтруко Ñпајање на левој Ñтрани</translation>
<translation id="2101225219012730419">Верзија:</translation>
<translation id="2102134110707549001">Предложи јаку лозинку…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">Ðмерички фудбал</translation>
<translation id="2187317261103489799">Откриј (подразумевано)</translation>
<translation id="2188375229972301266">ВишеÑтруко бушење на дну</translation>
-<translation id="2188852899391513400">Лозинка коју Ñте управо кориÑтили је пронађена при повреди података. Да биÑте заштитили налоге, Google менаџер лозинки препоручује да је одмах промените, па да проверите Ñачуване лозинке.</translation>
<translation id="219906046732893612">Уређење дома</translation>
<translation id="2202020181578195191">УнеÑите важећу годину иÑтека</translation>
<translation id="22081806969704220">3. фиока</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">Мењање датотека</translation>
<translation id="2215963164070968490">ПÑи</translation>
<translation id="2218879909401188352">Ðападачи који Ñу тренутно на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> могу да инÑталирају опаÑне апликације које ће вам оштетити уређај, додати нежељене трошкове код мобилног оператера или украÑти личне податке. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">РеÑтартуј водич</translation>
+<translation id="2219735899272417925">Треба да реÑетујете уређај</translation>
<translation id="2224337661447660594">Ðема интернета</translation>
<translation id="2230458221926704099">Поправите везу помоћу <ph name="BEGIN_LINK" />апликације за дијагноÑтику<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Пошаљи одмах</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">Ðије дозвољено (подразумевано)</translation>
<translation id="2512413427717747692">Дугме ПодеÑи Chrome као подразумевани прегледач, притиÑните Enter да биÑте подеÑили Chrome као подразумевани прегледач ÑиÑтема у подешавањима iOS-а</translation>
<translation id="2515629240566999685">да проверите Ñигнал у Ñвојој облаÑти</translation>
+<translation id="2515761554693942801">Одабрали Ñте да Ñе верификујете помоћу Touch ID-а на веб-Ñајтовима који кориÑте <ph name="PROVIDER_ORIGIN" />. Овај добављач је можда Ñачувао информације о начину плаћања, за који можете да <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Спајање у доњем деÑном углу</translation>
<translation id="2521736961081452453">Ðаправите упитник</translation>
<translation id="2523886232349826891">Сачувано је Ñамо на овом уређају</translation>
<translation id="2524461107774643265">Додајте још информација</translation>
<translation id="2529899080962247600">Ово поље не Ñме да има више од <ph name="MAX_ITEMS_LIMIT" /> уноÑа. Сви будући уноÑи ће Ñе занемарити.</translation>
+<translation id="253493526287553278">Прикажи детаље о промотивном коду</translation>
<translation id="2535585790302968248">Отворите нову картицу без архивирања да биÑте прегледали приватно</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{и још 1}one{и још #}few{и још #}other{и још #}}</translation>
<translation id="2536110899380797252">Додај адреÑу</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">ФотографÑка и дигитална уметноÑÑ‚</translation>
<translation id="2601150049980261779">Љубавни филмови</translation>
<translation id="2604589665489080024">Поп музика</translation>
-<translation id="2609632851001447353">Варијације</translation>
<translation id="2610561535971892504">Кликните да биÑте копирали</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />не чува<ph name="END_EMPHASIS" /> Ñледеће информације:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Рођендани и имендани</translation>
<translation id="2677748264148917807">Затвори</translation>
+<translation id="2679714844901977852">Сачувајте информације о картици и обрачуну на Google налогу <ph name="USER_EMAIL" /> ради безбедних и бржих плаћања</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Ðајбоља величина</translation>
<translation id="2688969097326701645">Да, наÑтави</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">Интернет провајдери</translation>
<translation id="2856444702002559011">Ðападачи можда покушавају да украду информације Ñа <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (на пример, лозинке, поруке или кредитне картице). <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Овај Ñајт приказује оглаÑе који ометају активноÑти или обмањујуће оглаÑе.</translation>
-<translation id="286512204874376891">Виртуелна картица крије вашу Ñтварну картицу да би Ð²Ð°Ñ Ð·Ð°ÑˆÑ‚Ð¸Ñ‚Ð¸Ð»Ð° од потенцијалне преваре. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ПријатељÑки</translation>
<translation id="28761159517501904">Филмови</translation>
<translation id="2876489322757410363">ÐапуÑтићете режим без архивирања да биÑте платили у Ñпољној апликацији. Желите ли да наÑтавите?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ДиÑк</translation>
<translation id="3162559335345991374">WiFi мрежа коју кориÑтите ће можда захтевати да поÑетите Ñтраницу за пријављивање.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">ОÑтрво</translation>
<translation id="3176929007561373547">Проверите подешавања прокÑија или контактирајте админиÑтратора мреже да
биÑте Ñе уверили да прокÑи Ñервер функционише. Ðко миÑлите да не
треба да кориÑтите прокÑи Ñервер:
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">Грешка на Ñату</translation>
<translation id="3369459162151165748">Делови и опрема за возила</translation>
<translation id="3371064404604898522">ПодеÑи Chrome као подразумевани прегледач</translation>
-<translation id="3371076217486966826"><ph name="URL" /> жели:
- • да прави 3D мапу окружења и да прати положај камере
- • да кориÑти камеру</translation>
<translation id="337363190475750230">Онемогућен је</translation>
<translation id="3375754925484257129">Покрени Chrome проверу безбедноÑти</translation>
<translation id="3377144306166885718">Сервер кориÑти заÑтарелу верзију TLS-а.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">ÐдреÑа иÑпоруке</translation>
<translation id="3402261774528610252">Веза коришћена за учитавање овог Ñајта је кориÑтила TLS 1.0 или TLS 1.1, који Ñу заÑтарели и биће онемогућени у будућноÑти. Када буду онемогућени, кориÑници неће моћи да учитају овај Ñајт. Сервер треба да омогући TLS 1.2 или новију верзију.</translation>
<translation id="3405664148539009465">Прилагоди фонтове</translation>
+<translation id="3407789382767355356">пријављивање треће Ñтране</translation>
<translation id="3409896703495473338">Управљајте безбедноÑним подешавањима</translation>
<translation id="3414952576877147120">Величина:</translation>
<translation id="3417660076059365994">Датотеке које отпремите или приложите Ñе шаљу у Google Cloud или трећим Ñтранама на анализу. Ðа пример, можда ће бити Ñкениране у потрази за оÑетљивим подацима или малвером.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">ЛиÑте филмова и репертоари биоÑкопа</translation>
<translation id="3479552764303398839">Ðе Ñада</translation>
<translation id="3484560055331845446">Могли биÑте да изгубите приÑтуп Google налогу. Chrome препоручује да одмах промените лозинку. Мораћете да Ñе пријавите.</translation>
+<translation id="3484861421501147767">ПодÑетник: ДоÑтупан је Ñачувани промотивни кôд</translation>
<translation id="3487845404393360112">4. фиока</translation>
<translation id="3495081129428749620">Пронађите на Ñтраници
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">Управљај</translation>
<translation id="3816482573645936981">ВредноÑÑ‚ (замењена)</translation>
<translation id="382518646247711829">Ðко кориÑтите прокÑи Ñервер...</translation>
+<translation id="3826050100957962900">Пријављивање треће Ñтране</translation>
<translation id="3827112369919217609">ÐпÑолут</translation>
<translation id="3827666161959873541">Породични филмови</translation>
<translation id="3828924085048779000">Ðије дозвољено да поље за приÑтупну фразу буде празно.</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">Ðжурирајте датум и време</translation>
<translation id="3858860766373142691">Ðазив</translation>
<translation id="3872834068356954457">Ðаука</translation>
+<translation id="3875783148670536197">Покажи ми како</translation>
<translation id="3881478300875776315">Прикажи мање редова</translation>
<translation id="3884278016824448484">ÐеуÑаглашени идентификатор уређаја</translation>
-<translation id="3885155851504623709">Парохија</translation>
<translation id="388632593194507180">Откривено је праћење</translation>
<translation id="3886948180919384617">3. преграда за Ñлагање</translation>
<translation id="3890664840433101773">Додајте имејл</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861">ХоÑÑ‚ <ph name="HOST_NAME" /> је блокиран</translation>
<translation id="3978338123949022456">Режим претраге, унеÑите упит и притиÑните Enter да биÑте претражили Ñа ÑуфикÑом <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Птице</translation>
+<translation id="3985750352229496475">Управљајте адреÑама…</translation>
<translation id="3986705137476756801">ИÑкључи Титл уживо за Ñада</translation>
<translation id="3987940399970879459">Мање од 1 MB</translation>
<translation id="3990250421422698716">ПротреÑање ради поравнања</translation>
+<translation id="3992684624889376114">О овој Ñтраници</translation>
<translation id="3996311196211510766">Сајт <ph name="ORIGIN" /> је затражио да Ñе Ñмернице за порекло
примене на Ñве захтеве ка њему, али ове Ñмернице тренутно не могу да Ñе примене.</translation>
<translation id="4006465311664329701">Ðачини плаћања, понуде и адреÑе који кориÑте Google Pay</translation>
@@ -1227,6 +1234,7 @@
<translation id="4305666528087210886">ПриÑтуп датотеци није уÑпео</translation>
<translation id="4306529830550717874">Желите да Ñачувате адреÑу?</translation>
<translation id="4306812610847412719">привремена меморија</translation>
+<translation id="4310070645992025887">Претражите Ñвоје путеве</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Блокирај (подразумевано)</translation>
<translation id="4314815835985389558">Управљајте Ñинхронизацијом</translation>
@@ -1277,6 +1285,7 @@
<translation id="4435702339979719576">разгледница)</translation>
<translation id="443673843213245140">Коришћење прокÑија је онемогућено, али је наведена екÑплицитна конфигурација прокÑија.</translation>
<translation id="4441832193888514600">Игнорише Ñе јер Ñмернице могу да Ñе подеÑе Ñамо као Ñмернице за кориÑника у клауду.</translation>
+<translation id="4442470707340296952">Chrome картице</translation>
<translation id="4450893287417543264">Ðе приказуј поново</translation>
<translation id="4451135742916150903">Може да тражи да Ñе повезује Ñа HID уређајима</translation>
<translation id="4452328064229197696">Лозинка коју Ñте управо кориÑтили је пронађена при повреди података. Да биÑте заштитили налоге, Google менаџер лозинки препоручује да проверите Ñачуване лозинке.</translation>
@@ -1415,6 +1424,7 @@
<translation id="483241715238664915">Укључи упозорења</translation>
<translation id="4834250788637067901">Ðачини плаћања, понуде и адреÑе који кориÑте Google Pay</translation>
<translation id="4838327282952368871">Чаробно</translation>
+<translation id="4839087176073128681">Следећег пута платите брже и заштитите картицу помоћу врхунÑке Google безбедноÑти.</translation>
<translation id="4840250757394056958">Прегледајте иÑторију Chrome-а</translation>
<translation id="484462545196658690">ÐутоматÑки</translation>
<translation id="484671803914931257">ОÑтварите попуÑÑ‚ код продавца <ph name="MERCHANT_NAME" /> и других продаваца</translation>
@@ -1477,6 +1487,7 @@
<translation id="5011561501798487822">Откривени језик</translation>
<translation id="5015510746216210676">Ðазив уређаја:</translation>
<translation id="5017554619425969104">ТекÑÑ‚ који Ñте копирали</translation>
+<translation id="5017828934289857214">ПодÑети ме каÑније</translation>
<translation id="5018422839182700155">Ðе можемо да отворимо ову Ñтраницу</translation>
<translation id="5019198164206649151">Складиште тока података је у лошем Ñтању</translation>
<translation id="5020776957610079374">Етно музика</translation>
@@ -1496,6 +1507,7 @@
<translation id="5051305769747448211">Комедија уживо</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Да биÑте поÑлали овај фајл помоћу Дељења у близини, оÑлободите проÑтор (<ph name="DISK_SPACE_SIZE" />) на уређају}one{Да биÑте поÑлали ове фајлове помоћу Дељења у близини, оÑлободите проÑтор (<ph name="DISK_SPACE_SIZE" />) на уређају}few{Да биÑте поÑлали ове фајлове помоћу Дељења у близини, оÑлободите проÑтор (<ph name="DISK_SPACE_SIZE" />) на уређају}other{Да биÑте поÑлали ове фајлове помоћу Дељења у близини, оÑлободите проÑтор (<ph name="DISK_SPACE_SIZE" />) на уређају}}</translation>
<translation id="5056549851600133418">Чланци за ваÑ</translation>
+<translation id="5060483733937416656">Одабрали Ñте да Ñе верификујете помоћу Windows Hello-а на веб-Ñајтовима који кориÑте <ph name="PROVIDER_ORIGIN" />. Овај добављач је можда Ñачувао информације о начину плаћања, за који можете да <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Да ли Ñте миÑлили <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">ИÑторија штампања</translation>
<translation id="5068234115460527047">Хеџ фондови</translation>
@@ -1509,10 +1521,8 @@
<translation id="5087286274860437796">Сертификат Ñервера тренутно није важећи.</translation>
<translation id="5087580092889165836">Додај картицу</translation>
<translation id="5088142053160410913">Порука оператеру</translation>
-<translation id="5089810972385038852">Држава</translation>
<translation id="5093232627742069661">ПреÑавијање у облику Ñлова Z</translation>
<translation id="5094747076828555589">Овај Ñервер не може да докаже да је <ph name="DOMAIN" />; Chromium нема поверења у његов безбедноÑни Ñертификат. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
-<translation id="5095208057601539847">Покрајина</translation>
<translation id="5097099694988056070">СтатиÑтички подаци о уређају, попут иÑкоришћеноÑти процеÑора/RAM меморије</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Сајт није безбедан</translation>
@@ -1550,6 +1560,7 @@
<translation id="5171045022955879922">Претражите или унеÑите URL адреÑу</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Рачунар</translation>
+<translation id="5177076414499237632">Сазнајте више о извору и теми ове Ñтранице</translation>
<translation id="5179510805599951267">Ðије <ph name="ORIGINAL_LANGUAGE" />? Пријавите ову грешку</translation>
<translation id="518639307526414276">Храна и ÑредÑтва за негу кућних љубимаца</translation>
<translation id="5190835502935405962">Трака Ñа обележивачима</translation>
@@ -1710,6 +1721,7 @@
<translation id="5624120631404540903">Управљај лозинкама</translation>
<translation id="5629630648637658800">Учитавање подешавања Ñмерница није уÑпело</translation>
<translation id="5631439013527180824">Ðеважећи токен за управљање уређајима</translation>
+<translation id="5632485077360054581">Покажи ми како</translation>
<translation id="5633066919399395251">Ðападачи на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ће можда покушати да инÑталирају опаÑне програме на рачунару који краду или бришу информације (на пример, Ñлике, лозинке, поруке и бројеве кредитних картица). <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Обмањујући Ñадржај је блокиран.</translation>
<translation id="5633259641094592098">Култни и инди филмови</translation>
@@ -1827,6 +1839,7 @@
<translation id="5989320800837274978">ÐиÑу наведени ни фикÑни прокÑи Ñервери нити URL адреÑа .pac Ñкрипте.</translation>
<translation id="5992691462791905444">ПреÑавијање у облику полу-хармонике</translation>
<translation id="5995727681868049093">Управљајте информацијама, приватношћу и безбедношћу на Google налогу</translation>
+<translation id="5997247540087773573">Лозинка коју Ñте управо кориÑтили је пронађена при повреди података. Да биÑте заштитили налоге, Google менаџер лозинки препоручује да је одмах промените и проверите Ñачуване лозинке.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> резултата за „<ph name="SEARCH_TEXT" />“</translation>
<translation id="6006484371116297560">КлаÑична</translation>
<translation id="6008122969617370890">РедоÑлед од n до 1</translation>
@@ -1921,7 +1934,6 @@
<translation id="627746635834430766">Да биÑте Ñледећи пут платили брже, Ñачувајте картицу и адреÑу за обрачун на Google налогу.</translation>
<translation id="6279183038361895380">ПритиÑните |<ph name="ACCELERATOR" />| да биÑте приказали показивач</translation>
<translation id="6280223929691119688">ИÑпорука на ову адреÑу није могућа. Изаберите другу адреÑу.</translation>
-<translation id="6282194474023008486">ПоштанÑки број</translation>
<translation id="6285507000506177184">Дугме за управљање преузимањима у Chrome-у, притиÑните Enter да биÑте управљали фајловима које Ñте преузели у Chrome-у</translation>
<translation id="6289939620939689042">Боја Ñтранице</translation>
<translation id="6290238015253830360">Предложени чланци Ñе приказују овде</translation>
@@ -1943,6 +1955,7 @@
<translation id="6337133576188860026">ОÑлобађа мање од <ph name="SIZE" />. Ðеки Ñајтови ће Ñе можда Ñпорије учитавати кад их Ñледећи пут поÑетите.</translation>
<translation id="6337534724793800597">Филтрирај Ñмернице према називу</translation>
<translation id="6340739886198108203">Смернице админиÑтратора не препоручују прављење Ñнимака екрана или Ñнимака када је поверљив Ñадржај видљив:</translation>
+<translation id="6348220984832452017">Ðктивне варијације</translation>
<translation id="6349101878882523185">ИнÑталирајте <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ðиједна}=1{1 лозинка (за <ph name="DOMAIN_LIST" />, Ñинхронизовано)}=2{2 лозинке (за <ph name="DOMAIN_LIST" />, Ñинхронизовано)}one{# лозинка (за <ph name="DOMAIN_LIST" />, Ñинхронизовано)}few{# лозинке (за <ph name="DOMAIN_LIST" />, Ñинхронизовано)}other{# лозинки (за <ph name="DOMAIN_LIST" />, Ñинхронизовано)}}</translation>
<translation id="6355392890578844978">Овим прегледачем не управља компанија нити друга организација. ÐктивноÑтима на овом уређају Ñе можда управља ван Chromium-а. <ph name="BEGIN_LINK" />Сазнајте више<ph name="END_LINK" /></translation>
@@ -1974,7 +1987,6 @@
<translation id="643051589346665201">Промените Google лозинку</translation>
<translation id="6433490469411711332">Измените контакт информације</translation>
<translation id="6433595998831338502">ХоÑÑ‚ <ph name="HOST_NAME" /> је одбио повезивање.</translation>
-<translation id="6438025220577812695">Лично ћу то променити</translation>
<translation id="6440503408713884761">Занемарено</translation>
<translation id="6443406338865242315">додатке и додатне компонента које Ñте инÑталирали</translation>
<translation id="6446163441502663861">Kahu (коверат)</translation>
@@ -2104,9 +2116,9 @@
<translation id="6828866289116430505">Генетика</translation>
<translation id="6831043979455480757">Преведи</translation>
<translation id="6833752742582340615">Сачувајте информације о картици и обрачуну на Google налогу ради безбедних и бржих плаћања</translation>
-<translation id="6839929833149231406">ОблаÑÑ‚</translation>
<translation id="6846340164947227603">КориÑти број виртуелне картице...</translation>
<translation id="6852204201400771460">Желите ли да поново учитате апликацију?</translation>
+<translation id="6857776781123259569">Управљајте лозинкама…</translation>
<translation id="686485648936420384">РеÑурÑи за потрошаче</translation>
<translation id="6865412394715372076">Тренутно не можемо да верификујемо ову картицу</translation>
<translation id="6869334554832814367">Личне позајмице</translation>
@@ -2155,7 +2167,6 @@
<translation id="6965978654500191972">Уређај</translation>
<translation id="696703987787944103">Опажајно</translation>
<translation id="6968269510885595029">КориÑтите безбедноÑни кључ</translation>
-<translation id="6970216967273061347">Округ</translation>
<translation id="6971439137020188025">Брзо направите нову Google презентацију у Презентацијама</translation>
<translation id="6972629891077993081">Уређаји Ñа интерфејÑом</translation>
<translation id="6973656660372572881">Ðаведени Ñу и фикÑни прокÑи Ñервери и URL адреÑа .pac Ñкрипте.</translation>
@@ -2194,7 +2205,6 @@
<translation id="7081308185095828845">Ова функција није доÑтупна на уређају</translation>
<translation id="7083258188081898530">9. фиока</translation>
<translation id="7086090958708083563">КориÑник је тражио отпремање</translation>
-<translation id="7087282848513945231">Округ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, притиÑните Tab, па Enter да биÑте управљали дозволама и подацима Ñачуваним на различитим Ñајтовима у подешавањима Chrome-а</translation>
<translation id="7096937462164235847">Идентитет овог веб-Ñајта није верификован.</translation>
<translation id="7101893872976785596">Хорор филмови</translation>
@@ -2213,10 +2223,10 @@
<ph name="LIST_ITEM" />Информације унете у обраÑцима<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Слање на ову адреÑу није могуће. Изаберите другу адреÑу.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ÐдминиÑтратор је забранио копирање ових података.</translation>
<translation id="7135130955892390533">Прикажи ÑтатуÑ</translation>
<translation id="7138472120740807366">Ðачин иÑпоруке</translation>
-<translation id="7139724024395191329">Емират</translation>
<translation id="7139892792842608322">Примарна фиока</translation>
<translation id="714064300541049402">Померање Ñлике 2. Ñтране по X оÑи</translation>
<translation id="7152423860607593928">Број-14 (коверат)</translation>
@@ -2433,6 +2443,7 @@
<translation id="7669271284792375604">Ðападачи на овом Ñајту могу да покушају да Ð²Ð°Ñ Ð¿Ñ€ÐµÐ²Ð°Ñ€Ðµ да биÑте инÑталирали програме који штете доживљају прегледања (на пример, тако што мењају почетну Ñтраницу или приказују додатне оглаÑе на Ñајтовима које поÑећујете).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Радње предузете Ñа подацима који Ñу означени као поверљиви (1 радња од када Ñте Ñе пријавили). <ph name="BEGIN_LINK" />Сазнајте више<ph name="END_LINK" />}one{Радње предузете Ñа подацима који Ñу означени као поверљиви (# радња од када Ñте Ñе пријавили). <ph name="BEGIN_LINK" />Сазнајте више<ph name="END_LINK" />}few{Радње предузете Ñа подацима који Ñу означени као поверљиви (# радње од када Ñте Ñе пријавили). <ph name="BEGIN_LINK" />Сазнајте више<ph name="END_LINK" />}other{Радње предузете Ñа подацима који Ñу означени као поверљиви (# радњи од када Ñте Ñе пријавили). <ph name="BEGIN_LINK" />Сазнајте више<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">6. поштанÑко Ñандуче</translation>
+<translation id="7675325315208090829">Управљајте начинима плаћања…</translation>
<translation id="7676643023259824263">Потражите текÑÑ‚ у привременој меморији, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Прегледајте иÑторију прегледања и управљајте њом у подешавањима Chrome-а</translation>
<translation id="7679947978757153706">Бејзбол</translation>
@@ -2475,7 +2486,6 @@
<translation id="7766518757692125295">Обод</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">ИÑтим редоÑледом Ñа одштампаном Ñтраном нагоре</translation>
-<translation id="777702478322588152">Префектура</translation>
<translation id="7791011319128895129">Ðеобјављено</translation>
<translation id="7791196057686275387">Балирање</translation>
<translation id="7791543448312431591">Додај</translation>
@@ -2566,12 +2576,12 @@
<translation id="8055534648776115597">Стручно оÑпоÑобљавање и континуирано образовање</translation>
<translation id="8057711352706143257">Софтвер „<ph name="SOFTWARE_NAME" />“ није правилно конфигуриÑан. ДеинÑталирање Ñофтвера „<ph name="SOFTWARE_NAME" />“ обично решава проблем. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Производња хране</translation>
-<translation id="8066955247577885446">Жао нам је, дошло је до грешке.</translation>
<translation id="8067872629359326442">Управо Ñте унели лозинку на обмањујућем Ñајту. Chromium може да вам помогне. Да биÑте променили лозинку и обавеÑтили Google да је налог можда угрожен, кликните на Заштити налог.</translation>
<translation id="8070439594494267500">Икона апликације</translation>
<translation id="8074253406171541171">10x13 (коверат)</translation>
<translation id="8075736640322370409">Брзо направите нову Google табелу</translation>
<translation id="8075898834294118863">Управљајте подешавањима Ñајтова</translation>
+<translation id="8076492880354921740">Картице</translation>
<translation id="8078141288243656252">Додавање напомене није могуће када је ротиран</translation>
<translation id="8079031581361219619">Желите ли поново да учитате Ñајт?</translation>
<translation id="8081087320434522107">Седани</translation>
@@ -2694,6 +2704,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Подешавања</translation>
+<translation id="8428634594422941299">Важи</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, притиÑните Tab, па Enter да биÑте управљали подешавањима колачића у подешавањима Chrome-а</translation>
<translation id="8433057134996913067">Овако ћете Ñе одјавити Ñа већине веб-Ñајтова.</translation>
<translation id="8434840396568290395">Кућни љубимци</translation>
@@ -2792,6 +2803,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> је ваш кôд за <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Обележите ову картицу</translation>
<translation id="8751426954251315517">Пробајте поново Ñледећи пут</translation>
+<translation id="8757526089434340176">ДоÑтупна је Google Pay понуда</translation>
<translation id="8758885506338294482">Такмичења у видео играма</translation>
<translation id="8759274551635299824">Ова картица је иÑтекла</translation>
<translation id="87601671197631245">Овај Ñајт кориÑти заÑтарелу безбедноÑну конфигурацију која може да открије ваше информације (на пример, лозинке, поруке или кредитне картице) када Ñе шаљу том Ñајту.</translation>
@@ -2799,6 +2811,7 @@
<translation id="8763927697961133303">USB уређај</translation>
<translation id="8763986294015493060">Затворите Ñве прозоре без архивирања који Ñу тренутно отворени</translation>
<translation id="8766943070169463815">Табела за потврду акредитива за безбедно плаћање је отворена</translation>
+<translation id="8767765348545497220">Затворите облачић за помоћ</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Мотоцикли</translation>
<translation id="8790007591277257123">&amp;Понови бриÑање</translation>
@@ -2811,6 +2824,7 @@
<translation id="8806285662264631610">Производи за купање и негу тела</translation>
<translation id="8807160976559152894">Скраћивање поÑле Ñваке Ñтранице</translation>
<translation id="8808828119384186784">Подешавање Chrome-а</translation>
+<translation id="8813277370772331957">ПодÑети ме каÑније</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, притиÑните Tab, па Enter да биÑте ажурирали Chrome из подешавања Chrome-а</translation>
<translation id="8820817407110198400">Обележивачи</translation>
<translation id="882338992931677877">Отвор за ручни уноÑ</translation>
@@ -2990,6 +3004,7 @@
<translation id="988159990683914416">Верзија за програмере</translation>
<translation id="989988560359834682">Измена адреÑе</translation>
<translation id="991413375315957741">Ñензори за покрет или Ñветло</translation>
+<translation id="992110854164447044">Виртуелна картица крије вашу Ñтварну картицу да би Ð²Ð°Ñ Ð·Ð°ÑˆÑ‚Ð¸Ñ‚Ð¸Ð»Ð° од потенцијалне преваре. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">РужичаÑта</translation>
<translation id="992432478773561401">Софтвер „<ph name="SOFTWARE_NAME" />“ није правилно инÑталиран на рачунару или мрежи:
diff --git a/chromium/components/strings/components_strings_sv.xtb b/chromium/components/strings/components_strings_sv.xtb
index 735bf3d3314..40df881f578 100644
--- a/chromium/components/strings/components_strings_sv.xtb
+++ b/chromium/components/strings/components_strings_sv.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Anslag, stipendier och ekonomiskt stöd</translation>
<translation id="1048785276086539861">När du redigerar kommentarer aktiveras enkelsidig vy igen för dokumentet</translation>
<translation id="1050038467049342496">Stäng andra appar</translation>
+<translation id="1053959602163383901">Du har valt att verifiera dig med en autentiseringsenhet på webbplatser där<ph name="PROVIDER_ORIGIN" /> används. Den här leverantören kan ha lagrat uppgifter om din betalningsmetod som du kan <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Ångra Lägg till</translation>
<translation id="1056663316309890343">Fotoprogramvara</translation>
<translation id="1056898198331236512">Varning</translation>
@@ -119,6 +120,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="1270502636509132238">Alternativ för utlämning</translation>
<translation id="1281476433249504884">Utmatningsfack 1</translation>
<translation id="1285320974508926690">Översätt aldrig den här webbplatsen</translation>
+<translation id="1288548991597756084">Spara kortet säkert</translation>
<translation id="1292571435393770077">Fack 16</translation>
<translation id="1292701964462482250">Programvara på datorn förhindrar att Chrome ansluter till internet på ett säkert sätt (endast Windows-datorer)</translation>
<translation id="1294154142200295408">Varianter på kommandoraden</translation>
@@ -223,6 +225,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
&lt;p&gt;Åtgärda problemet genom att klicka på &lt;strong&gt;Anslut&lt;/strong&gt; på sidan du försöker öppna.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landskapsarkitektur</translation>
<translation id="1513706915089223971">Lista över historikposter</translation>
+<translation id="1516097932025103760">Det krypteras och sparas säkert. CVC-koden lagras aldrig.</translation>
<translation id="1517433312004943670">Telefonnummer är obligatoriskt</translation>
<translation id="1519264250979466059">Programversionsdatum</translation>
<translation id="1521159554480556801">Textilkonsthantverk</translation>
@@ -272,7 +275,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="1634828734222219955">Totalt</translation>
<translation id="163669211644121865">Skattefrågor och skatteplanering</translation>
<translation id="1638780421120290329">Det gick inte att spara kortet</translation>
-<translation id="1639239467298939599">Läser in</translation>
+<translation id="1639239467298939599">Profilen läses in</translation>
<translation id="1640180200866533862">Användarpolicyer</translation>
<translation id="1640244768702815859">Testa att <ph name="BEGIN_LINK" />besöka webbplatsens startsida<ph name="END_LINK" />.</translation>
<translation id="1641976391427233992">Senarelägga utmatning till</translation>
@@ -288,6 +291,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="1662550410081243962">Spara och fyll i betalningsmetoder</translation>
<translation id="1663943134801823270">Kort och adresser har hämtats från Chrome. Du hanterar dessa under <ph name="BEGIN_LINK" />Inställningar<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Sidor på <ph name="SOURCE_LANGUAGE" /> översätts till <ph name="TARGET_LANGUAGE" /> från och med nu.</translation>
+<translation id="1673886523110456987">Betala med <ph name="CARD_DETAIL" /> för att använda erbjudandet</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> till <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Kortsidan först</translation>
<translation id="168693727862418163">Principen ignoreras eftersom den inte gick att validera mot schemat.</translation>
@@ -306,6 +310,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="1717218214683051432">Rörelsesensorer</translation>
<translation id="1717494416764505390">Postfack 3</translation>
<translation id="1718029547804390981">Det går inte att annotera det här dokumentet eftersom det är för stort</translation>
+<translation id="1720941539803966190">Stäng självstudierna</translation>
<translation id="1721424275792716183">* Fältet är obligatoriskt</translation>
<translation id="1727613060316725209">Certifikatet är giltigt</translation>
<translation id="1727741090716970331">Lägg till ett giltigt kortnummer</translation>
@@ -422,8 +427,8 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="205212645995975601">BBQ och grillning</translation>
<translation id="2053111141626950936">Sidor på <ph name="LANGUAGE" /> översätts inte.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{När den här inställningen är på och statusen är Aktiv fastställer Chrome vilken grupp, eller kohort, som du tillhör utifrån din senaste webbaktivitet. Annonsörer kan välja annonser för denna grupp och din webbhistorik förblir privat på enheten. Grupptillhörigheten uppdateras dagligen.}=1{När den här inställningen är på och statusen är Aktiv fastställer Chrome vilken grupp, eller kohort, som du tillhör utifrån din senaste webbaktivitet. Annonsörer kan välja annonser för denna grupp och din webbhistorik förblir privat på enheten. Grupptillhörigheten uppdateras dagligen.}other{När den här inställningen är på och statusen är Aktiv fastställer Chrome vilken grupp, eller kohort, som du tillhör utifrån din senaste webbaktivitet. Annonsörer kan välja annonser för denna grupp och din webbhistorik förblir privat på enheten. Grupptillhörigheten uppdateras med {NUM_DAYS} dagars mellanrum.}}</translation>
-<translation id="2053553514270667976">Postnummer</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 förslag}other{# förslag}}</translation>
+<translation id="2066915425250589881">begära ska tas bort</translation>
<translation id="2068528718802935086">Baby och småbarn</translation>
<translation id="2071156619270205202">Detta kort uppfyller inte kraven för virtuella kortnummer.</translation>
<translation id="2071692954027939183">Aviseringar blockerades automatiskt eftersom du vanligtvis inte tillåter dem</translation>
@@ -435,7 +440,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="2088086323192747268">Knappen Hantera synkronisering, tryck på Retur om du vill hantera vilken information som synkroniseras i inställningarna för Chrome</translation>
<translation id="2091887806945687916">Ljud</translation>
<translation id="2094505752054353250">Domänen matchar inte</translation>
-<translation id="2096368010154057602">Departement</translation>
<translation id="2099652385553570808">Tre häftklamrar till vänster</translation>
<translation id="2101225219012730419">Version:</translation>
<translation id="2102134110707549001">Föreslå ett starkt lösenord …</translation>
@@ -472,7 +476,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="2185836064961771414">Amerikansk fotboll</translation>
<translation id="2187317261103489799">Identifiera (standard)</translation>
<translation id="2188375229972301266">Flera hål längst ned</translation>
-<translation id="2188852899391513400">Lösenordet som du precis använde har läckt ut vid ett dataintrång. Du bör byta det nu och se över dina sparade lösenord för att skydda dina konton.</translation>
<translation id="219906046732893612">Husrenovering</translation>
<translation id="2202020181578195191">Ange ett giltigt utgångsår</translation>
<translation id="22081806969704220">Fack 3</translation>
@@ -483,6 +486,8 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="2215727959747642672">Filredigering</translation>
<translation id="2215963164070968490">Hundar</translation>
<translation id="2218879909401188352">Angripare på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> skulle kunna installera farliga appar som skadar enheten, lägger till dolda avgifter på din mobilfaktura eller stjäl personliga uppgifter. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Starta om guiden</translation>
+<translation id="2219735899272417925">Enheten måste återställas</translation>
<translation id="2224337661447660594">Inget internet</translation>
<translation id="2230458221926704099">Åtgärda anslutningsproblemet med hjälp av <ph name="BEGIN_LINK" />diagnostiseringsappen<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Skicka nu</translation>
@@ -580,11 +585,13 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="2512101340618156538">Tillåts inte (standard)</translation>
<translation id="2512413427717747692">Knappen Ställ in Chrome som standardwebbläsare: tryck på Retur om du vill ställa in Chrome som standardwebbläsare i systeminställningarna för iOS</translation>
<translation id="2515629240566999685">kontrollera mottagningen i området</translation>
+<translation id="2515761554693942801">Du har valt att verifiera dig med Touch ID på webbplatser där <ph name="PROVIDER_ORIGIN" /> används. Den här leverantören kan ha lagrat uppgifter om din betalningsmetod som du kan <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Häfta nere till höger</translation>
<translation id="2521736961081452453">Skapa formulär</translation>
<translation id="2523886232349826891">Endast sparat på den här enheten</translation>
<translation id="2524461107774643265">Lägg till mer information</translation>
<translation id="2529899080962247600">Fältet får ha högst <ph name="MAX_ITEMS_LIMIT" /> poster. Alla poster utöver det ignoreras.</translation>
+<translation id="253493526287553278">Visa information om kampanjkoden</translation>
<translation id="2535585790302968248">Öppna en ny inkognitoflik och surfa privat</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{och 1 till}other{och # till}}</translation>
<translation id="2536110899380797252">Lägg till adress</translation>
@@ -620,7 +627,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="259821504105826686">Fotokonst och digital konst</translation>
<translation id="2601150049980261779">Romantik</translation>
<translation id="2604589665489080024">Popmusik</translation>
-<translation id="2609632851001447353">Varianter</translation>
<translation id="2610561535971892504">Klicka för att kopiera</translation>
<translation id="2617988307566202237">Följande information sparas <ph name="BEGIN_EMPHASIS" />inte<ph name="END_EMPHASIS" /> i Chrome:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Födelsedagar och namnsdagar</translation>
<translation id="2677748264148917807">Lämna</translation>
+<translation id="2679714844901977852">Spara dina kort- och faktureringsuppgifter i Google-kontot <ph name="USER_EMAIL" /> så du kan betala snabbare och säkrare</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Bästa passning</translation>
<translation id="2688969097326701645">Ja, fortsätt</translation>
@@ -701,7 +708,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="2854764410992194509">Internetleverantörer</translation>
<translation id="2856444702002559011">En angripare kan försöka stjäla dina uppgifter från <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (t.ex. lösenord, meddelanden eller kreditkortsuppgifter). <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Påträngande eller vilseledande annonser visas på den här webbplatsen.</translation>
-<translation id="286512204874376891">Ett virtuellt kort döljer det riktiga kortet och skyddar dig från eventuella bedrägerier. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Vänlig</translation>
<translation id="28761159517501904">Filmer</translation>
<translation id="2876489322757410363">Om du betalar i ett externt program sker inte det i inkognitoläge. Vill du fortsätta?</translation>
@@ -802,7 +808,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="3158539265159265653">Skiva</translation>
<translation id="3162559335345991374">wifi-nätverket du använder kanske kräver att du besöker dess inloggningssida.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ö</translation>
<translation id="3176929007561373547">Kontrollera dina proxyinställningar eller kontakta nätverksadministratören om du vill
kontrollera att proxyservern fungerar. Om du inte tror att du ska
använda en proxyserver:
@@ -881,9 +886,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="3369192424181595722">Fel på klockan</translation>
<translation id="3369459162151165748">Fordonsdelar och tillbehör</translation>
<translation id="3371064404604898522">Ställ in Chrome som standardwebbläsare</translation>
-<translation id="3371076217486966826"><ph name="URL" /> vill
- • skapa en 3D-karta över dina omgivningar och registrera kamerans position
- • använda kameran.</translation>
<translation id="337363190475750230">Tagen ur bruk</translation>
<translation id="3375754925484257129">Kör säkerhetskontroll i Chrome</translation>
<translation id="3377144306166885718">En föråldrad version av TLS används på servern.</translation>
@@ -900,6 +902,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="3399952811970034796">Leveransadress</translation>
<translation id="3402261774528610252">TLS 1.0 eller TLS 1.1 användes för anslutningen när den här webbplatsen lästes in. De versionerna har fasats ut och kommer att inaktiveras i framtiden. Efter inaktiveringen kan användarna inte längre läsa in den här webbplatsen. TLS 1.2 eller senare behöver aktiveras på servern.</translation>
<translation id="3405664148539009465">Anpassa teckensnitt</translation>
+<translation id="3407789382767355356">inloggning hos tredje part</translation>
<translation id="3409896703495473338">Hantera säkerhetsinställningar</translation>
<translation id="3414952576877147120">Storlek:</translation>
<translation id="3417660076059365994">Filer som du laddar upp eller bifogar skickas till Google Cloud eller tredje parter för analys. De kan till exempel genomsökas efter känsliga uppgifter eller skadlig programvara.</translation>
@@ -932,6 +935,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="3477679029130949506">Film- och teaterutbud</translation>
<translation id="3479552764303398839">Inte nu</translation>
<translation id="3484560055331845446">Du kan förlora tillgången till Google-kontot. Du rekommenderas att ändra lösenordet nu. Du blir uppmanad att logga in.</translation>
+<translation id="3484861421501147767">Påminnelse: En sparad kampanjkod är tillgänglig</translation>
<translation id="3487845404393360112">Fack 4</translation>
<translation id="3495081129428749620">Hitta på sidan
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="3810973564298564668">Hantera</translation>
<translation id="3816482573645936981">Värde (ersatt)</translation>
<translation id="382518646247711829">Om du använder en proxyserver ...</translation>
+<translation id="3826050100957962900">Inloggning hos tredje part</translation>
<translation id="3827112369919217609">Absolut</translation>
<translation id="3827666161959873541">Familjefilmer</translation>
<translation id="3828924085048779000">Lösenfrasen får inte vara tom.</translation>
@@ -1068,9 +1073,9 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="3858027520442213535">Uppdatera datum och tid</translation>
<translation id="3858860766373142691">Namn</translation>
<translation id="3872834068356954457">Vetenskap</translation>
+<translation id="3875783148670536197">Visa hur man gör</translation>
<translation id="3881478300875776315">Visa färre rader</translation>
<translation id="3884278016824448484">Motstridiga enhetsidentifierare</translation>
-<translation id="3885155851504623709">Församling</translation>
<translation id="388632593194507180">Övervakning har upptäckts</translation>
<translation id="3886948180919384617">Utmatningsfack 3</translation>
<translation id="3890664840433101773">Lägg till e-post</translation>
@@ -1100,9 +1105,11 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="3973234410852337861"><ph name="HOST_NAME" /> har blockerats</translation>
<translation id="3978338123949022456">Sökläge: skriv en sökfråga och tryck på Retur för sökning på <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">FÃ¥glar</translation>
+<translation id="3985750352229496475">Hantera adresser …</translation>
<translation id="3986705137476756801">Inaktivera Live Caption tills vidare</translation>
<translation id="3987940399970879459">Mindre än 1 MB</translation>
<translation id="3990250421422698716">Stötvis förskjutning</translation>
+<translation id="3992684624889376114">Om den här sidan</translation>
<translation id="3996311196211510766">Webbplatsen <ph name="ORIGIN" /> har begärt att en ursprungsprincip
ska gälla varje gång den svarar på en begäran, men denna princip går inte att tillämpa just nu.</translation>
<translation id="4006465311664329701">Betalningsmetoder, erbjudanden och adresser med Google Pay</translation>
@@ -1227,6 +1234,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="4305666528087210886">Ingen åtkomst till filen</translation>
<translation id="4306529830550717874">Vill du spara adressen?</translation>
<translation id="4306812610847412719">urklipp</translation>
+<translation id="4310070645992025887">Sök bland dina sökningar</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Blockera (standard)</translation>
<translation id="4314815835985389558">Hantera synkronisering</translation>
@@ -1277,6 +1285,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="4435702339979719576">vykort)</translation>
<translation id="443673843213245140">Användning av proxy är inaktiverad men en explicit proxykonfiguration har angetts.</translation>
<translation id="4441832193888514600">Ignoreras eftersom principen bara kan ställas in som en princip för molnanvändare.</translation>
+<translation id="4442470707340296952">Chrome-flikar</translation>
<translation id="4450893287417543264">Visa inte igen</translation>
<translation id="4451135742916150903">Får begära tillstånd att ansluta till HID-enheter</translation>
<translation id="4452328064229197696">Lösenordet som du precis använde har läckt ut vid ett dataintrång. Du bör se över dina sparade lösenord för att skydda dina konton.</translation>
@@ -1415,6 +1424,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="483241715238664915">Aktivera varningar</translation>
<translation id="4834250788637067901">Betalningsmetoder, erbjudanden och adresser med Google Pay</translation>
<translation id="4838327282952368871">Drömsk</translation>
+<translation id="4839087176073128681">Betala snabbare nästa gång och skydda kortet med Googles branschledande säkerhet.</translation>
<translation id="4840250757394056958">Visa Chrome-historiken</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257">Få rabatt på <ph name="MERCHANT_NAME" /> med mera</translation>
@@ -1477,6 +1487,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="5011561501798487822">upptäckt språk</translation>
<translation id="5015510746216210676">Datornamn:</translation>
<translation id="5017554619425969104">Texten som du kopierade</translation>
+<translation id="5017828934289857214">PÃ¥minn mig senare</translation>
<translation id="5018422839182700155">Det går inte att öppna den här sidan</translation>
<translation id="5019198164206649151">Säkerhetskopian har dålig status</translation>
<translation id="5020776957610079374">Världsmusik</translation>
@@ -1496,6 +1507,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="5051305769747448211">Ståuppkomedi</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Om du vill skicka den här filen med Närdelning ska du frigöra utrymme (<ph name="DISK_SPACE_SIZE" />) på enheten}other{Om du vill skicka de här filerna med Närdelning ska du frigöra utrymme (<ph name="DISK_SPACE_SIZE" />) på enheten}}</translation>
<translation id="5056549851600133418">Artiklar för dig</translation>
+<translation id="5060483733937416656">Du har valt att verifiera dig med Windows Hello på webbplatser där <ph name="PROVIDER_ORIGIN" /> används. Den här leverantören kan ha lagrat uppgifter om din betalningsmetod som du kan <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Menade du <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Utskriftshistorik</translation>
<translation id="5068234115460527047">Hedgefonder</translation>
@@ -1509,10 +1521,8 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="5087286274860437796">Servercertifikatet är inte giltigt för närvarande.</translation>
<translation id="5087580092889165836">Lägg till kort</translation>
<translation id="5088142053160410913">Meddelande till operatören</translation>
-<translation id="5089810972385038852">Stat</translation>
<translation id="5093232627742069661">Z-falsning</translation>
<translation id="5094747076828555589">Servern kunde inte bevisa att den är <ph name="DOMAIN" /> eftersom Chromium inte litar på dess säkerhetscertifikat. Detta kan orsakas av en felaktig konfigurering eller att någon spärrar anslutningen.</translation>
-<translation id="5095208057601539847">Provins</translation>
<translation id="5097099694988056070">Enhetsstatistik, till exempel processor- och RAM-användning</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Webbplatsen är inte säker</translation>
@@ -1550,6 +1560,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="5171045022955879922">Sök eller ange webbadress</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Dator</translation>
+<translation id="5177076414499237632">Läs mer om sidans källa och ämne</translation>
<translation id="5179510805599951267">Inte på <ph name="ORIGINAL_LANGUAGE" />? Rapportera felet</translation>
<translation id="518639307526414276">Djurmat och djurvård</translation>
<translation id="5190835502935405962">Bokmärkesfältet</translation>
@@ -1710,6 +1721,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="5624120631404540903">Hantera lösenord</translation>
<translation id="5629630648637658800">Det gick inte att läsa in policyinställningarna</translation>
<translation id="5631439013527180824">Ogiltig enhetshanteringstoken</translation>
+<translation id="5632485077360054581">Visa hur man gör</translation>
<translation id="5633066919399395251">Angripare på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kan försöka installera skadliga program som stjäl eller raderar information (t.ex. foton, lösenord, meddelanden och kreditkortsuppgifter) på datorn. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Bedrägligt innehåll har blockerats.</translation>
<translation id="5633259641094592098">Kultklassiker och indiefilmer</translation>
@@ -1827,6 +1839,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="5989320800837274978">Varken fasta proxyservrar eller en webbadress med PAC-skript har angetts.</translation>
<translation id="5992691462791905444">Förskjuten Z-falsning</translation>
<translation id="5995727681868049093">Hantera uppgifter, integritet och säkerhet i Google-kontot</translation>
+<translation id="5997247540087773573">Lösenordet som du precis använde har läckt ut vid ett dataintrång. Du rekommenderas av Google Lösenordshantering att byta det nu och se över dina sparade lösenord för att skydda dina konton.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> resultat för <ph name="SEARCH_TEXT" /></translation>
<translation id="6006484371116297560">Klassiskt</translation>
<translation id="6008122969617370890">N till 1-ordning</translation>
@@ -1922,7 +1935,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="627746635834430766">Spara kortet och faktureringsadressen i Google-kontot så går det snabbare att betala nästa gång.</translation>
<translation id="6279183038361895380">Tryck på |<ph name="ACCELERATOR" />| om du vill visa markören</translation>
<translation id="6280223929691119688">Det går inte att leverera till den här adressen. Välj en annan adress.</translation>
-<translation id="6282194474023008486">Postnummer</translation>
<translation id="6285507000506177184">Knappen Hantera nedladdningar i Chrome: tryck på Retur om du vill hantera filer du har laddat ned i Chrome</translation>
<translation id="6289939620939689042">Sidfärg</translation>
<translation id="6290238015253830360">Rekommenderade artiklar visas här</translation>
@@ -1944,6 +1956,7 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="6337133576188860026">Frigör mindre än <ph name="SIZE" />. Vissa webbplatser kan läsas in långsammare nästa gång du besöker dem.</translation>
<translation id="6337534724793800597">Filtrera princip efter namn</translation>
<translation id="6340739886198108203">Enligt administratörspolicyn är det inte rekommenderat att ta skärmbilder eller göra inspelningar när konfidentiellt innehåll är synligt:</translation>
+<translation id="6348220984832452017">Aktiva variationer</translation>
<translation id="6349101878882523185">Installera <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Inga}=1{1 lösenord (för <ph name="DOMAIN_LIST" />, synkroniseras)}=2{2 lösenord (för <ph name="DOMAIN_LIST" />, synkroniseras)}other{# lösenord (för <ph name="DOMAIN_LIST" />, synkroniseras)}}</translation>
<translation id="6355392890578844978">Den här webbläsaren hanteras inte av ett företag eller en organisation. Aktiviteter på den här enheten kan hanteras utanför Chromium. <ph name="BEGIN_LINK" />Läs mer<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="643051589346665201">Byt lösenord till Google</translation>
<translation id="6433490469411711332">Redigera kontaktuppgifter</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> avvisade anslutningen.</translation>
-<translation id="6438025220577812695">Ändra det själv</translation>
<translation id="6440503408713884761">Ignoreras</translation>
<translation id="6443406338865242315">Vilka tillägg och pluginprogram som du har installerat</translation>
<translation id="6446163441502663861">Kahu (kuvert)</translation>
@@ -2105,9 +2117,9 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="6828866289116430505">Genetik</translation>
<translation id="6831043979455480757">Översätt</translation>
<translation id="6833752742582340615">Spara dina kort- och faktureringsuppgifter i Google-kontot så du kan betala snabbare och säkrare</translation>
-<translation id="6839929833149231406">Område</translation>
<translation id="6846340164947227603">Använd ett virtuellt kreditkortsnummer …</translation>
<translation id="6852204201400771460">Vill du läsa in appen igen?</translation>
+<translation id="6857776781123259569">Hantera lösenord …</translation>
<translation id="686485648936420384">Resurser för konsumenter</translation>
<translation id="6865412394715372076">Det går inte att verifiera kortet just nu</translation>
<translation id="6869334554832814367">Privatlån</translation>
@@ -2156,7 +2168,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="6965978654500191972">Enhet</translation>
<translation id="696703987787944103">Perceptuell</translation>
<translation id="6968269510885595029">Använd säkerhetsnyckeln</translation>
-<translation id="6970216967273061347">Distrikt</translation>
<translation id="6971439137020188025">Skapa en ny Google-presentation i Presentationer snabbt</translation>
<translation id="6972629891077993081">HID-enheter</translation>
<translation id="6973656660372572881">Både fasta proxyservrar och en webbadress för PAC-skript anges.</translation>
@@ -2195,7 +2206,6 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<translation id="7081308185095828845">Funktionen är inte tillgänglig på din enhet</translation>
<translation id="7083258188081898530">Fack 9</translation>
<translation id="7086090958708083563">Uppladdningsbegäran från användare</translation>
-<translation id="7087282848513945231">Kommun</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, tryck på Tabb och sedan på Retur om du vill hantera behörigheter och data som lagras på olika webbplatser i inställningarna för Chrome</translation>
<translation id="7096937462164235847">Webbplatsens identitet har inte verifierats.</translation>
<translation id="7101893872976785596">Skräckfilmer</translation>
@@ -2214,10 +2224,10 @@ Annars blockeras detta av integritetsinställningarna. Om du tillåter detta kan
<ph name="LIST_ITEM" />Uppgifter som anges i formulär<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Det går inte att skicka till den här adressen. Välj en annan adress.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Administratören tillåter inte att denna data kopieras.</translation>
<translation id="7135130955892390533">Visa status</translation>
<translation id="7138472120740807366">Leveranssätt</translation>
-<translation id="7139724024395191329">Emirat</translation>
<translation id="7139892792842608322">Primär bricka</translation>
<translation id="714064300541049402">Sida 2 bild X – byte</translation>
<translation id="7152423860607593928">Number-14 (kuvert)</translation>
@@ -2434,6 +2444,7 @@ Mer information.
<translation id="7669271284792375604">Angripare på den här webbplatsen kan försöka lura dig att installera program som skadar din webbupplevelse (till exempel genom att byta ut din startsida eller visa extra annonser på webbplatser som du besöker).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Åtgärder som gjorts med data som markerats som konfidentiell (1 åtgärd sedan inloggningen) <ph name="BEGIN_LINK" />Läs mer<ph name="END_LINK" />}other{Åtgärder som gjorts med data som markerats som konfidentiell (# åtgärder sedan inloggningen) <ph name="BEGIN_LINK" />Läs mer<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Postfack 6</translation>
+<translation id="7675325315208090829">Hantera betalningsmetoder …</translation>
<translation id="7676643023259824263">Sök efter texten i Urklipp, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Öppna och hantera webbhistoriken i Chrome-inställningarna</translation>
<translation id="7679947978757153706">Baseboll</translation>
@@ -2476,7 +2487,6 @@ Mer information.
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Samma ordning – framsidan uppåt</translation>
-<translation id="777702478322588152">Prefektur</translation>
<translation id="7791011319128895129">Opublicerat</translation>
<translation id="7791196057686275387">Balbindning</translation>
<translation id="7791543448312431591">Lägg till</translation>
@@ -2567,12 +2577,12 @@ Mer information.
<translation id="8055534648776115597">Yrkesutbildning och vidareutbildning</translation>
<translation id="8057711352706143257"><ph name="SOFTWARE_NAME" /> har inte konfigurerats korrekt. Ofta hjälper det att avinstallera <ph name="SOFTWARE_NAME" />. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Livsmedelsproduktion</translation>
-<translation id="8066955247577885446">Något gick tyvärr fel.</translation>
<translation id="8067872629359326442">Du angav just ditt lösenord på en bedräglig webbplats. Du kan få hjälp i Chromium. Klicka på Skydda kontot för att ändra ditt lösenord och meddela Google om att kontot kan vara utsatt för fara.</translation>
<translation id="8070439594494267500">App-ikon</translation>
<translation id="8074253406171541171">10x13 (kuvert)</translation>
<translation id="8075736640322370409">Skapa ett nytt Google-kalkylark snabbt</translation>
<translation id="8075898834294118863">Hantera webbplatsinställningar</translation>
+<translation id="8076492880354921740">Flikar</translation>
<translation id="8078141288243656252">Det går inte att annotera när dokumentet har roterats</translation>
<translation id="8079031581361219619">Vill du läsa in webbplatsen igen?</translation>
<translation id="8081087320434522107">Sedanbilar</translation>
@@ -2695,6 +2705,7 @@ Mer information:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Inställningar</translation>
+<translation id="8428634594422941299">Jag förstår</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, tryck på Tabb och sedan på Retur om du vill hantera cookies i inställningarna för Chrome</translation>
<translation id="8433057134996913067">Alternativet innebär att du loggas ut från de flesta webbplatser.</translation>
<translation id="8434840396568290395">Husdjur</translation>
@@ -2793,6 +2804,7 @@ Mer information:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> är din kod för <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Bokmärk den här fliken</translation>
<translation id="8751426954251315517">Försök igen senare</translation>
+<translation id="8757526089434340176">Ett Google Pay-erbjudande är tillgängligt</translation>
<translation id="8758885506338294482">E-sport</translation>
<translation id="8759274551635299824">Kortets giltighetstid har löpt ut</translation>
<translation id="87601671197631245">Den här webbplatsens säkerhetskonfiguration är inaktuell. Det kan leda till att dina uppgifter (t.ex. lösenord, meddelanden eller kreditkort) exponeras när de skickas till den här webbplatsen.</translation>
@@ -2800,6 +2812,7 @@ Mer information:
<translation id="8763927697961133303">USB-enhet</translation>
<translation id="8763986294015493060">Stäng alla inkognitofönster som är öppna för närvarande</translation>
<translation id="8766943070169463815">Ett arbetsblad för autentisering av användaruppgifter för säker betalning har öppnats</translation>
+<translation id="8767765348545497220">Stäng hjälpbubblan</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motorcyklar</translation>
<translation id="8790007591277257123">&amp;Gör om Ta bort</translation>
@@ -2812,6 +2825,7 @@ Mer information:
<translation id="8806285662264631610">Dusch- och badprodukter</translation>
<translation id="8807160976559152894">Beskär efter varje sida</translation>
<translation id="8808828119384186784">Inställningar för Chrome</translation>
+<translation id="8813277370772331957">PÃ¥minn mig senare</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, tryck på Tabb och sedan på Retur om du vill uppdatera Chrome i inställningarna för Chrome</translation>
<translation id="8820817407110198400">Bokmärken</translation>
<translation id="882338992931677877">Manuell plats</translation>
@@ -2991,6 +3005,7 @@ Mer information:
<translation id="988159990683914416">Utvecklarversion</translation>
<translation id="989988560359834682">Redigera adress</translation>
<translation id="991413375315957741">rörelse- eller ljussensorer</translation>
+<translation id="992110854164447044">Ett virtuellt kort döljer det riktiga kortet och skyddar dig från eventuella bedrägerier. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Rosa</translation>
<translation id="992432478773561401"><ph name="SOFTWARE_NAME" /> har inte installerats korrekt på datorn eller nätverket:
diff --git a/chromium/components/strings/components_strings_sw.xtb b/chromium/components/strings/components_strings_sw.xtb
index 96d380391b5..7902d7f8447 100644
--- a/chromium/components/strings/components_strings_sw.xtb
+++ b/chromium/components/strings/components_strings_sw.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Ruzuku, ufadhili wa masomo na msaada wa kifedha</translation>
<translation id="1048785276086539861">Ukibadilisha vidokezo, hati hii itarejea kwenye mwonekano wa ukurasa mmoja</translation>
<translation id="1050038467049342496">Funga programu nyingine</translation>
+<translation id="1053959602163383901">Ulichagua kuthibitisha kwa kutumia kifaa cha uthibitishaji kwenye tovuti zinazotumia <ph name="PROVIDER_ORIGIN" />. Huenda mtoa huduma huyu amehifadhi maelezo ya njia yako ya kulipa ambayo unaweza <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">Tendua Kuongeza</translation>
<translation id="1056663316309890343">Programu ya kuhariri picha</translation>
<translation id="1056898198331236512">Ilani</translation>
@@ -119,6 +120,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="1270502636509132238">Mbinu ya Kuchukua</translation>
<translation id="1281476433249504884">Tupio la kutoa la printa la kwanza</translation>
<translation id="1285320974508926690">Kamwe usitafsiri tovuti hii</translation>
+<translation id="1288548991597756084">Hifadhi kadi kwa usalama</translation>
<translation id="1292571435393770077">Trei ya 16</translation>
<translation id="1292701964462482250">"Kuna programu kwenye kompyuta yako inayoizuia Chrome isiunganishe kwenye wavuti kwa usalama" (kompyuta za Windows pekee)</translation>
<translation id="1294154142200295408">Tofauti za miundo ya amri</translation>
@@ -223,6 +225,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
&lt;p&gt;Ili urekebishe hitilafu, bofya &lt;strong&gt;Unganisha&lt;/strong&gt; kwenye ukurasa unaojaribu kufungua.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Muundo wa mlalo</translation>
<translation id="1513706915089223971">Orodha ya historia ya maudhui yaliyowekwa</translation>
+<translation id="1516097932025103760">Itasimbwa kwa njia fiche, itahifadhiwa kwa usalama na faili ya CVC haitaifadhiwa.</translation>
<translation id="1517433312004943670">Nambari ya simu inahitajika</translation>
<translation id="1519264250979466059">Unda Tarehe</translation>
<translation id="1521159554480556801">Sanaa ya ufumaji vitambaa na usokotaji nyuzi</translation>
@@ -288,6 +291,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="1662550410081243962">Hifadhi na ujaze njia za kulipa</translation>
<translation id="1663943134801823270">Kadi na anwani zinatoka Chrome. Unaweza kuzidhibiti kwenye <ph name="BEGIN_LINK" />Mipangilio<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Itatafsiri kurasa za <ph name="SOURCE_LANGUAGE" /> katika <ph name="TARGET_LANGUAGE" /> kuanzia sasa.</translation>
+<translation id="1673886523110456987">Nunua kwa kutumia <ph name="CARD_DETAIL" /> ili utumie ofa</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> kwenda <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Pembe fupi kwanza</translation>
<translation id="168693727862418163">Thamani ya sera hii imeshindwa kuthibitishwa kwenye taratibu yake na itapuuzwa.</translation>
@@ -306,6 +310,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="1717218214683051432">Vitambuzi vya mwendo</translation>
<translation id="1717494416764505390">Kikasha cha barua cha tatu</translation>
<translation id="1718029547804390981">Huwezi kuweka vidokezo kwenye hati kwa kuwa ni kubwa mno</translation>
+<translation id="1720941539803966190">Funga mafunzo</translation>
<translation id="1721424275792716183">* Unahitaji kujaza sehemu hii</translation>
<translation id="1727613060316725209">Cheti ni sahihi</translation>
<translation id="1727741090716970331">Ongeza Nambari Sahihi ya Kadi</translation>
@@ -421,8 +426,8 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="205212645995975601">Kubanika na kuchoma nyama</translation>
<translation id="2053111141626950936">Haitatafsiri kurasa za <ph name="LANGUAGE" />.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Wakati kidhibiti hiki kimewashwa na teknolojia hii inatumika, Chrome hubaini kikundi kikubwa cha watu, au “kundiâ€, ambalo shughuli zako za kuvinjari za hivi majuzi zinafanana nalo kwa ukaribu zaidi. Watangazaji wanaweza kuchagua matangazo kwa ajili ya kikundi hicho na shughuli zako za kuvinjari huwekwa kwa faragha kwenye kifaa chako. Kikundi chako husasishwa kila siku.}=1{Wakati kidhibiti hiki kimewashwa na teknolojia hii inatumika, Chrome hubaini kikundi kikubwa cha watu, au “kundiâ€, ambalo shughuli zako za kuvinjari za hivi majuzi zinafanana nalo kwa ukaribu zaidi. Watangazaji wanaweza kuchagua matangazo kwa ajili ya kikundi hicho na shughuli zako za kuvinjari huwekwa kwa faragha kwenye kifaa chako. Kikundi chako husasishwa kila siku.}other{Wakati kidhibiti hiki kimewashwa na teknolojia hii inatumika, Chrome hubaini kikundi kikubwa cha watu, au “kundiâ€, ambalo shughuli zako za kuvinjari za hivi majuzi zinafanana nalo kwa ukaribu zaidi. Watangazaji wanaweza kuchagua matangazo kwa ajili ya kikundi hicho na shughuli zako za kuvinjari huwekwa kwa faragha kwenye kifaa chako. Kikundi chako husasishwa kila baada ya siku {NUM_DAYS}.}}</translation>
-<translation id="2053553514270667976">Msimbo wa eneo</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{Pendekezo 1}other{Mapendekezo #}}</translation>
+<translation id="2066915425250589881">kuomba yafutwe</translation>
<translation id="2068528718802935086">Watoto wachanga na watoto wenye umri wa miaka mitatu hadi mitano</translation>
<translation id="2071156619270205202">Huwezi kupata nambari ya kadi pepe kwa kutumia kadi hii.</translation>
<translation id="2071692954027939183">Arifa zimezuiwa kiotomatiki kwa sababu kwa kawaida huziruhusu</translation>
@@ -434,7 +439,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="2088086323192747268">Kitufe cha 'Dhibiti usawazishaji', bonyeza 'Enter' ili udhibiti maelezo unayosawazisha katika mipangilio ya Chrome</translation>
<translation id="2091887806945687916">Sauti</translation>
<translation id="2094505752054353250">Kitolingana kwa kikoa</translation>
-<translation id="2096368010154057602">Idara</translation>
<translation id="2099652385553570808">Bana kushoto mara tatu</translation>
<translation id="2101225219012730419">Toleo:</translation>
<translation id="2102134110707549001">Pendekeza Nenosiri Thabiti…</translation>
@@ -471,7 +475,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="2185836064961771414">Mpira wa miguu wa kimarekani</translation>
<translation id="2187317261103489799">Gundua (chaguomsingi)</translation>
<translation id="2188375229972301266">Toboa mara kadhaa chini</translation>
-<translation id="2188852899391513400">Nenosiri ulilotumia sasa hivi limepatikana kwenye tukio la ufichuzi haramu wa data. Ili uimarishe usalama wa akaunti zako, Kidhibiti cha Manenosiri cha Google kinapendekeza ulibadilishe sasa kisha ukague manenosiri yako yaliyohifadhiwa.</translation>
<translation id="219906046732893612">Uboreshaji wa nyumba</translation>
<translation id="2202020181578195191">Andika mwaka sahihi wa kuisha kwa muda wa matumizi</translation>
<translation id="22081806969704220">Trei ya tatu</translation>
@@ -482,6 +485,8 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="2215727959747642672">Kubadilisha faili</translation>
<translation id="2215963164070968490">Mbwa</translation>
<translation id="2218879909401188352">Wavamizi walio kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hivi sasa, wanaweza kusakinisha programu hatari zinazoweza kukiharibu kifaa chako, kuongeza gharama fiche kwenye malipo yako ya simu, au kuiba maelezo yako ya binafsi. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Anzisha upya mafunzo</translation>
+<translation id="2219735899272417925">Lazima kifaa kirejeshwe katika mipangilio kiliyotoka nayo kiwandani</translation>
<translation id="2224337661447660594">Hakuna intaneti</translation>
<translation id="2230458221926704099">Weka muunganisho wako kwa kutumia <ph name="BEGIN_LINK" />programu ya kuchunguza<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Tuma sasa</translation>
@@ -579,11 +584,13 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="2512101340618156538">Hairuhusiwi (chaguomsingi)</translation>
<translation id="2512413427717747692">Kitufe cha kufanya Chrome iwe kivinjari chaguomsingi, bonyeza Enter ili uifanye Chrome iwe kivinjari chaguomsingi cha mfumo kwenye mipangilio ya iOS</translation>
<translation id="2515629240566999685">Kuangalia uthabiti wa mawimbi katika eneo lako</translation>
+<translation id="2515761554693942801">Ulichagua kuthibitisha kwa kutumia Touch ID kwenye tovuti zinazotumia <ph name="PROVIDER_ORIGIN" />. Huenda mtoa huduma huyu amehifadhi maelezo ya njia yako ya kulipa ambayo unaweza <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Bana chini kulia</translation>
<translation id="2521736961081452453">Unda fomu</translation>
<translation id="2523886232349826891">Imehifadhiwa kwenye kifaa hiki pekee</translation>
<translation id="2524461107774643265">Ongeza Maelezo Zaidi</translation>
<translation id="2529899080962247600">Hupaswi kuweka zaidi ya vipengee <ph name="MAX_ITEMS_LIMIT" /> katika sehemu hii. Vipengee vyote vya ziada vitapuuzwa.</translation>
+<translation id="253493526287553278">Angalia maelezo ya kuponi ya ofa</translation>
<translation id="2535585790302968248">Fungua kichupo fiche kipya ili uvinjari kwa faragha</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{na lingine 1}other{na mengine #}}</translation>
<translation id="2536110899380797252">Ongeza Anwani</translation>
@@ -619,7 +626,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="259821504105826686">Sanaa za kidijitali na upigaji picha</translation>
<translation id="2601150049980261779">Filamu za mahaba</translation>
<translation id="2604589665489080024">Muziki wa Pop</translation>
-<translation id="2609632851001447353">Vipera</translation>
<translation id="2610561535971892504">Bofya ili unakili</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />haitahifadhi<ph name="END_EMPHASIS" /> maelezo yafuatayo:
<ph name="BEGIN_LIST" />
@@ -652,6 +658,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Sherehe za siku ya kuzaliwa na siku muhimu zinazoambatana na jina</translation>
<translation id="2677748264148917807">Ondoka</translation>
+<translation id="2679714844901977852">Hifadhi maelezo yako ya kadi na malipo kwenye Akaunti yako ya Google <ph name="USER_EMAIL" /> ili kulipa kwa usalama na kwa haraka</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Itoshee vizuri</translation>
<translation id="2688969097326701645">Ndiyo, endelea</translation>
@@ -700,7 +707,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="2854764410992194509">Watoa huduma za intaneti</translation>
<translation id="2856444702002559011">Huenda wavamizi wanajaribu kuiba maelezo yako kutoka <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (kwa mfano, manenosiri, ujumbe au kadi za mikopo).<ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Tovuti inaonyesha matangazo yanayopotosha au yanayokatiza huduma.</translation>
-<translation id="286512204874376891">Kadi pepe huficha kadi yako halisi ili kusaidia kukulinda dhidi ya ulaghai unaoweza kutokea. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Friendly</translation>
<translation id="28761159517501904">Filamu</translation>
<translation id="2876489322757410363">Inafunga hali fiche ili kulipa kupitia programu ya nje. Ungependa kuendelea?</translation>
@@ -801,7 +807,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="3158539265159265653">Diski</translation>
<translation id="3162559335345991374">Wi-Fi unayotumia inaweza kukuhitaji kutembelea ukurasa wake wa kuingia katika akaunti.</translation>
<translation id="3169472444629675720">Gundua</translation>
-<translation id="3174168572213147020">Kisiwa</translation>
<translation id="3176929007561373547">Angalia mipangilio yako ya seva mbadala au wasiliana na msimamizi wako wa mtandao ili
kuhakikisha kuwa seva mbadala inafanya kazi. Ikiwa huamini kwamba unapaswa kuwa
ukitumia seva mbadala:
@@ -880,9 +885,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="3369192424181595722">Hitilafu ya saa</translation>
<translation id="3369459162151165748">Vipuri na vifaa vya magari</translation>
<translation id="3371064404604898522">Ifanye Chrome iwe kivinjari chaguomsingi</translation>
-<translation id="3371076217486966826"><ph name="URL" /> inataka:
- • Kuunda ramani ya 3D ya mazingira yako na kufuatilia mkao wa kamera yako
- • Kutumia kamera yako</translation>
<translation id="337363190475750230">Imewekwa katika hali ya kutotumika</translation>
<translation id="3375754925484257129">Fanya ukaguzi wa usalama kwenye Chrome</translation>
<translation id="3377144306166885718">Seva ilitumia toleo la TLS lililopitwa na wakati.</translation>
@@ -899,6 +901,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="3399952811970034796">Mahali Bidhaa Itapelekwa</translation>
<translation id="3402261774528610252">Muunganisho uliotumika kupakia tovuti hii ulitumia TLS 1.0 au TLS 1.1, ambayo ni matoleo yaliyoacha kutumika na yatazimwa baadaye. Yakishazimwa, watumiaji watazuiwa wasipakie tovuti hii. Seva inahitaji kuwasha TLS 1.2 au toleo jipya zaidi.</translation>
<translation id="3405664148539009465">Badilisha fonti zikufae</translation>
+<translation id="3407789382767355356">kuingia katika akaunti kwenye huduma nyingine</translation>
<translation id="3409896703495473338">Dhibiti mipangilio ya usalama</translation>
<translation id="3414952576877147120">Ukubwa:</translation>
<translation id="3417660076059365994">Faili unazopakia au kuambatisha hutumwa kwa Wingu la Google au mifumo ya wengine ili kuchanganuliwa. Kwa mfano, zinaweza kuchanganuliwa ili kubaini data nyeti au programu hasidi.</translation>
@@ -931,6 +934,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="3477679029130949506">Orodha ya filamu na vipindi vya maonyesho ya ukumbini</translation>
<translation id="3479552764303398839">Si sasa</translation>
<translation id="3484560055331845446">Unaweza kupoteza uwezo wa kufikia Akaunti yako ya Google. Chrome inapendekeza ubadilishe nenosiri lako sasa. Utaombwa uingie katika akaunti.</translation>
+<translation id="3484861421501147767">Kikumbusho: Kuponi ya ofa iliyohifadhiwa inapatikana</translation>
<translation id="3487845404393360112">Trei ya nne</translation>
<translation id="3495081129428749620">Pata katika ukurasa wa
<ph name="PAGE_TITLE" /></translation>
@@ -1055,6 +1059,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="3810973564298564668">Dhibiti</translation>
<translation id="3816482573645936981">Thamani (nafasi yake imechukuliwa)</translation>
<translation id="382518646247711829">Ukitumia seva mbadala...</translation>
+<translation id="3826050100957962900">Kuingia katika akaunti kwenye huduma nyingine</translation>
<translation id="3827112369919217609">Kamili</translation>
<translation id="3827666161959873541">Filamu za familia</translation>
<translation id="3828924085048779000">Kaulisiri tupu hairuhusiwi.</translation>
@@ -1067,9 +1072,9 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="3858027520442213535">Sasisha tarehe na saa</translation>
<translation id="3858860766373142691">Jina</translation>
<translation id="3872834068356954457">Sayansi</translation>
+<translation id="3875783148670536197">Nionyeshe Jinsi ya Kufanya</translation>
<translation id="3881478300875776315">Onyesha mistari michache</translation>
<translation id="3884278016824448484">Kitambulisho cha kifaa kinachokinzana</translation>
-<translation id="3885155851504623709">Parokia</translation>
<translation id="388632593194507180">Tukio la Ufuatiliaji Limetambuliwa</translation>
<translation id="3886948180919384617">Tupio la kutoa la printa la tatu</translation>
<translation id="3890664840433101773">Ongeza anwani ya barua pepe</translation>
@@ -1099,9 +1104,11 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="3973234410852337861"><ph name="HOST_NAME" /> imezuiwa.</translation>
<translation id="3978338123949022456">Hali ya kutafuta, andika hoja na ubonyeze Enter ili utafute kwa kutumia <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Ndege</translation>
+<translation id="3985750352229496475">Dhibiti Anwani...</translation>
<translation id="3986705137476756801">Zima kipengele cha Manukuu Papo Hapo kwa sasa</translation>
<translation id="3987940399970879459">Chini ya MB 1</translation>
<translation id="3990250421422698716">Kunja kuwa katika muundo wa Z</translation>
+<translation id="3992684624889376114">Kuhusu ukurasa huu</translation>
<translation id="3996311196211510766">Tovuti ya <ph name="ORIGIN" /> inataka sera ya asili
itumike kwenye maombi yote inayopokea, lakini sera hii haiwezi kutumika kwa sasa.</translation>
<translation id="4006465311664329701">Njia za Kulipa, Ofa na Anwani Zinazotumia Google Pay</translation>
@@ -1226,6 +1233,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="4305666528087210886">Imeshindwa kufikia faili yako</translation>
<translation id="4306529830550717874">Ungependa kuhifadhi anwani?</translation>
<translation id="4306812610847412719">ubao wa kunakili</translation>
+<translation id="4310070645992025887">Tafuta Ziara zako</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Zuia (chaguomsingi)</translation>
<translation id="4314815835985389558">Dhibiti usawazishaji</translation>
@@ -1276,6 +1284,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="4435702339979719576">Kad ya Posta)</translation>
<translation id="443673843213245140">Matumizi ya proksi yamelemazwa lakini usanidi wa proksi wazi umebainishwa.</translation>
<translation id="4441832193888514600">Imepuuzwa kwa sababu sera inaweza kuwekwa tu kama sera ya mtumiaji wa wingu.</translation>
+<translation id="4442470707340296952">Vichupo vya Chrome</translation>
<translation id="4450893287417543264">Usionyeshe tena</translation>
<translation id="4451135742916150903">Inaweza kuomba ruhusa ya kuunganisha kwenye vifaa vya HID</translation>
<translation id="4452328064229197696">Nenosiri ulilotumia sasa hivi limepatikana kwenye tukio la ufichuzi haramu wa data. Ili uimarishe usalama wa akaunti zako, Kidhibiti cha Manenosiri cha Google kinapendekeza ukague manenosiri yako yaliyohifadhiwa.</translation>
@@ -1414,6 +1423,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="483241715238664915">Washa maonyo</translation>
<translation id="4834250788637067901">Njia za kulipa, ofa na anwani zinazotumia Google Pay</translation>
<translation id="4838327282952368871">Dreamy</translation>
+<translation id="4839087176073128681">Lipa kwa haraka zaidi wakati mwingine na ulinde kadi yako kwa kutumia ulinzi wa Google ulio bora zaidi katika sekta.</translation>
<translation id="4840250757394056958">Angalia historia yako kwenye Chrome</translation>
<translation id="484462545196658690">Otomatiki</translation>
<translation id="484671803914931257">Pata punguzo kwenye <ph name="MERCHANT_NAME" /> na zaidi</translation>
@@ -1476,6 +1486,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="5011561501798487822">Lugha Iliyotambuliwa</translation>
<translation id="5015510746216210676">Jina la Mashine:</translation>
<translation id="5017554619425969104">Maandishi uliyonakili</translation>
+<translation id="5017828934289857214">Nikumbushe Baadaye</translation>
<translation id="5018422839182700155">Ukurasa huu haufunguki</translation>
<translation id="5019198164206649151">Hifadhi la kucheleza liko katika hali mbaya</translation>
<translation id="5020776957610079374">Muziki wa kimataifa</translation>
@@ -1495,6 +1506,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="5051305769747448211">Vichekesho vya moja kwa moja</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Ili utume faili hii kwa kutumia kipengele cha Uhamishaji wa Karibu, futa baadhi ya faili kwenye kifaa chako ili upate nafasi (<ph name="DISK_SPACE_SIZE" />)}other{Ili utume faili hizi kwa kutumia kipengele cha Uhamishaji wa Karibu, futa baadhi ya faili kwenye kifaa chako ili upate nafasi (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">Makala kwa ajili yako</translation>
+<translation id="5060483733937416656">Ulichagua kuthibitisha kwa kutumia Windows Hello kwenye tovuti zinazotumia <ph name="PROVIDER_ORIGIN" />. Huenda mtoa huduma huyu amehifadhi maelezo ya njia yako ya kulipa ambayo unaweza <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Je, ulimaanisha <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Historia ya kuchapisha</translation>
<translation id="5068234115460527047">Fedha za uwekezaji kwa tahadhari kubwa</translation>
@@ -1508,10 +1520,8 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="5087286274860437796">Cheti cha seva si sahihi kwa sasa.</translation>
<translation id="5087580092889165836">Ongeza kadi</translation>
<translation id="5088142053160410913">Ujumbe kwa ishara maalum ya utafutaji</translation>
-<translation id="5089810972385038852">Jimbo</translation>
<translation id="5093232627742069661">Mkunjo wa Z</translation>
<translation id="5094747076828555589">Seva hii haikuweza kuthibitisha kuwa ni <ph name="DOMAIN" />; cheti chake cha usalama hakiaminiwi na Chromium. Hii inaweza kusababishwa na kusanidi kusikofaa au mvamizi kuingilia muunganisho wako.</translation>
-<translation id="5095208057601539847">Mkoa</translation>
<translation id="5097099694988056070">Takwimu za kifaa kama vile matumizi ya CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Tovuti hii si salama</translation>
@@ -1549,6 +1559,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="5171045022955879922">Tafuta au charaza URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Mashine</translation>
+<translation id="5177076414499237632">Pata maelezo zaidi kuhusu mada na chanzo cha ukurasa huu</translation>
<translation id="5179510805599951267">Haiko katika <ph name="ORIGINAL_LANGUAGE" />? Ripoti hitilafu hii</translation>
<translation id="518639307526414276">Vyakula na bidhaa za utunzaji wa wanyama vipenzi</translation>
<translation id="5190835502935405962">Sehemu ya Alamisho</translation>
@@ -1709,6 +1720,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="5624120631404540903">Dhibiti manenosiri</translation>
<translation id="5629630648637658800">Imeshindwa kupakia mipangilio ya sera</translation>
<translation id="5631439013527180824">Ishara ya usimamizi wa kifaa batili</translation>
+<translation id="5632485077360054581">Nionyeshe jinsi ya kufanya</translation>
<translation id="5633066919399395251">Wavamizi ambao sasa wako kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> huenda wakajaribu kusakinisha programu hatari kwenye kompyuta yako ambazo zinaiba au kufuta maelezo yako (kwa mfano, picha, manenosiri, ujumbe na kadi za mikopo). <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Maudhui ya udanganyifu yamezuiwa.</translation>
<translation id="5633259641094592098">Filamu za imani fulani na huru</translation>
@@ -1826,6 +1838,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="5989320800837274978">Siyo seva proksi za kudumu wala URL ya hati ya .pac zimebainishwa.</translation>
<translation id="5992691462791905444">Mkunjo wa Z wa uhandisi</translation>
<translation id="5995727681868049093">Dhibiti maelezo, faragha na usalama wako katika Akaunti yako ya Google</translation>
+<translation id="5997247540087773573">Nenosiri ulilotumia hivi punde limepatikana kwenye ufichuzi haramu wa data. Ili uimarishe usalama wa akaunti zako, Kidhibiti cha Manenosiri cha Google kinapendekeza ulibadilishe sasa na ukague manenosiri yako yaliyohifadhiwa.</translation>
<translation id="6000758707621254961">Matokeo <ph name="RESULT_COUNT" /> ya '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">Kawaida</translation>
<translation id="6008122969617370890">Mpangilio wa N hadi moja</translation>
@@ -1921,7 +1934,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="627746635834430766">Ili ulipe kwa haraka wakati ujao, hifadhi anwani ya kutuma bili na maelezo ya kadi yako kwenye Akaunti yako ya Google.</translation>
<translation id="6279183038361895380">Bonyeza |<ph name="ACCELERATOR" />| ili kiteuzi kionekane</translation>
<translation id="6280223929691119688">Haiwezi kuwasilisha kwenye anwani hii. Chagua anwani tofauti.</translation>
-<translation id="6282194474023008486">Msimbo wa eneo</translation>
<translation id="6285507000506177184">Kitufe cha 'Dhibiti vipakuliwa kwenye Chrome', bonyeza 'Enter' ili udhibiti faili ulizozipakua katika Chrome</translation>
<translation id="6289939620939689042">Rangi ya Ukurasa</translation>
<translation id="6290238015253830360">Makala unayopendekezewa yataonekana hapa</translation>
@@ -1943,6 +1955,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="6337133576188860026">Huongeza nafasi isiyozidi <ph name="SIZE" />. Baadhi ya tovuti huenda zikapakia polepole zaidi utakapozivinjari tena.</translation>
<translation id="6337534724793800597">Chuja sera kwa jina</translation>
<translation id="6340739886198108203">Sera ya msimamizi haipendekezi kurekodi au kupiga picha za skrini wakati maudhui ya siri yanaonekana:</translation>
+<translation id="6348220984832452017">Aina Zinazotumika</translation>
<translation id="6349101878882523185">Sakinisha <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Hamna}=1{Nenosiri 1 (la <ph name="DOMAIN_LIST" />, limesawazishwa)}=2{Manenosiri 2 (ya <ph name="DOMAIN_LIST" />, yamesawazishwa)}other{Manenosiri # (ya <ph name="DOMAIN_LIST" />, yamesawazishwa)}}</translation>
<translation id="6355392890578844978">Kivinjari hiki hakidhibitiwi na kampuni au shirika lingine. Huenda shughuli kwenye kifaa hiki zikadhibitiwa nje ya Chromium. <ph name="BEGIN_LINK" />Pata maelezo zaidi<ph name="END_LINK" /></translation>
@@ -1974,7 +1987,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="643051589346665201">Badilisha nenosiri la Google</translation>
<translation id="6433490469411711332">Badilisha maelezo ya mawasiliano</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> imekataa kuunganisha.</translation>
-<translation id="6438025220577812695">Nitabadilisha mwenyewe</translation>
<translation id="6440503408713884761">Imepuuzwa</translation>
<translation id="6443406338865242315">Viendelezi au programu jalizi ulizosakinisha</translation>
<translation id="6446163441502663861">Kahu (Bahasha)</translation>
@@ -2104,9 +2116,9 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="6828866289116430505">Jenetiki</translation>
<translation id="6831043979455480757">Tafsiri</translation>
<translation id="6833752742582340615">Hifadhi maelezo yako ya kadi na malipo kwenye Akaunti yako ya Google ili kulipa kwa usalama na kwa haraka</translation>
-<translation id="6839929833149231406">Eneo</translation>
<translation id="6846340164947227603">Tumia nambari ya kadi pepe...</translation>
<translation id="6852204201400771460">Ungependa kupakia programu upya?</translation>
+<translation id="6857776781123259569">Dhibiti Manenosiri...</translation>
<translation id="686485648936420384">Rasilimali za watumiaji</translation>
<translation id="6865412394715372076">Imeshindwa kuthibitisha kadi hii kwa sasa</translation>
<translation id="6869334554832814367">Mikopo binafsi</translation>
@@ -2155,7 +2167,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="6965978654500191972">Kifaa</translation>
<translation id="696703987787944103">Ubadilishaji rangi</translation>
<translation id="6968269510885595029">Tumia Ufunguo wako wa Usalama</translation>
-<translation id="6970216967273061347">Wilaya</translation>
<translation id="6971439137020188025">Unda wasilisho jipya la Google katika huduma ya Slaidi za Google kwa haraka</translation>
<translation id="6972629891077993081">Vifaa vya HID</translation>
<translation id="6973656660372572881">Seva zote za proksi thabiti na URL ya hati ya .pac zimebainishwa.</translation>
@@ -2194,7 +2205,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="7081308185095828845">Kipengele hiki hakipatikani kwenye kifaa chako</translation>
<translation id="7083258188081898530">Trei ya tisa</translation>
<translation id="7086090958708083563">Mtumiaji ameomba kupakia</translation>
-<translation id="7087282848513945231">Jimbo</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, bonyeza 'Tab' kisha 'Enter' ili udhibiti ruhusa na data iliyohifadhiwa kwenye tovuti mbalimbali katika mipangilio ya Chrome</translation>
<translation id="7096937462164235847">Utambulisho wa tovuti hii haujathibitishwa.</translation>
<translation id="7101893872976785596">Filamu za kutisha</translation>
@@ -2213,10 +2223,10 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<ph name="LIST_ITEM" />Maelezo unayojaza katika fomu<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Haiwezi kusafirisha kwenda kwenye anwani hii. Chagua anwani tofauti.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Msimamizi wako amezuia data hii isinakiliwe.</translation>
<translation id="7135130955892390533">Onyesha hali</translation>
<translation id="7138472120740807366">Njia ya kusafirisha</translation>
-<translation id="7139724024395191329">Emirate</translation>
<translation id="7139892792842608322">Trei ya Msingi</translation>
<translation id="714064300541049402">Ugeuzaji wa upande wa pili wa picha ya X</translation>
<translation id="7152423860607593928">Number-14 (Bahasha)</translation>
@@ -2431,6 +2441,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="7669271284792375604">Wavamizi kwenye tovuti hii wanaweza kujaribu kukulaghai kusakinisha programu zinazoathiri hali yako ya kuvinjari (kwa mfano, kwa kubadilisha ukurasa wako wa kwanza au kwa kuonyesha matangazo ya ziada kwenye tovuti unazotembelea).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Hatua zilizochukuliwa kwenye data iliyotiwa alama kuwa ni ya siri (hatua moja tangu wakati wa kuingia katika akaunti). <ph name="BEGIN_LINK" />Pata maelezo zaidi<ph name="END_LINK" />}other{Hatua zilizochukuliwa kwenye data iliyotiwa alama kuwa ni ya siri (hatua # tangu wakati wa kuingia katika akaunti). <ph name="BEGIN_LINK" />Pata maelezo zaidi<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Kikasha cha barua cha sita</translation>
+<translation id="7675325315208090829">Dhibiti Njia za Kulipa...</translation>
<translation id="7676643023259824263">Tafuta maandishi ya ubao wa kunakili, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Angalia na udhibiti historia yako ya kuvinjari katika mipangilio ya Chrome</translation>
<translation id="7679947978757153706">Mpira wa besiboli</translation>
@@ -2473,7 +2484,6 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="7766518757692125295">Sketi</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Mpangilio sawa zikiangalia juu</translation>
-<translation id="777702478322588152">Mkoa</translation>
<translation id="7791011319128895129">Haijachapishwa</translation>
<translation id="7791196057686275387">Robota</translation>
<translation id="7791543448312431591">Ongeza</translation>
@@ -2564,12 +2574,12 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="8055534648776115597">Elimu ya Ufundi na elimu ya kujiendeleza</translation>
<translation id="8057711352706143257">Haikuweka mipangilio ya "<ph name="SOFTWARE_NAME" />" kwa njia sahihi. Kwa kawaida, kuondoa "<ph name="SOFTWARE_NAME" />" hurekebisha tatizo hili. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Uzalishaji wa chakula</translation>
-<translation id="8066955247577885446">Samahani, hitilafu fulani imetokea.</translation>
<translation id="8067872629359326442">Umeweka nenosiri lako kwenye tovuti inayotiliwa shaka. Chromium inaweza kukusaidia. Ili ubadilishe nenosiri lako na uarifu Google kwamba huenda akaunti yako imo hatarini, bofya Linda Akaunti.</translation>
<translation id="8070439594494267500">Aikoni ya programu</translation>
<translation id="8074253406171541171">10x13 (Bahasha)</translation>
<translation id="8075736640322370409">Unda Jedwali jipya la Google kwa haraka</translation>
<translation id="8075898834294118863">Dhibiti mipangilio ya tovuti</translation>
+<translation id="8076492880354921740">Vichupo</translation>
<translation id="8078141288243656252">Huwezi kuweka vidokezo ikiwa imezungushwa</translation>
<translation id="8079031581361219619">Ungependa kupakia upya tovuti?</translation>
<translation id="8081087320434522107">Gari aina ya Sedan</translation>
@@ -2690,6 +2700,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="8416694386774425977">Mipangilio ya mtandao si sahihi na haikuweza kupakiwa. Maelezo ya ziada: <ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Mipangilio</translation>
+<translation id="8428634594422941299">Nimeelewa</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, bonyeza 'Tab' kisha 'Enter' ili udhibiti mapendeleo yako ya vidakuzi katika mipangilio ya Chrome</translation>
<translation id="8433057134996913067">Kufanya hivyo kutakuondoa kwenye tovuti nyingi.</translation>
<translation id="8434840396568290395">Wanyama vipenzi</translation>
@@ -2788,6 +2799,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> ni nambari yako ya <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Alamisha kichupo hiki</translation>
<translation id="8751426954251315517">Tafadhali jaribu tena baadaye</translation>
+<translation id="8757526089434340176">Ofa ya Google Pay inapatikana</translation>
<translation id="8758885506338294482">Michezo ya video yenye ushindani</translation>
<translation id="8759274551635299824">Muda wa matumizi wa kadi hii umekwisha</translation>
<translation id="87601671197631245">Tovuti hii inatumia mipangilio ya usalama iliyopitwa na wakati, hali ambayo inaweza kuonyesha taarifa zako (kwa mfano, manenosiri, ujumbe au kadi za mikopo) zikitumwa kwenye tovuti hii.</translation>
@@ -2795,6 +2807,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="8763927697961133303">Kifaa cha USB</translation>
<translation id="8763986294015493060">Funga madirisha yote fiche yaliyofunguliwa kwa sasa</translation>
<translation id="8766943070169463815">Laha ya uthibitishaji wa kitambulisho cha malipo salama imefunguliwa</translation>
+<translation id="8767765348545497220">Funga kiputo cha usaidizi</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Pikipiki</translation>
<translation id="8790007591277257123">Rudia kufuta</translation>
@@ -2807,6 +2820,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="8806285662264631610">Bidhaa za kuogea na kutunza mwili</translation>
<translation id="8807160976559152894">Punguza baada ya kila ukurasa</translation>
<translation id="8808828119384186784">Mipangilio ya Chrome</translation>
+<translation id="8813277370772331957">Nikumbushe baadaye</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, bonyeza 'Tab' kisha 'Enter' ili usasishe Chrome katika mipangilio yako ya Chrome</translation>
<translation id="8820817407110198400">Alamisho</translation>
<translation id="882338992931677877">Nafasi ya Kuweka Mwenyewe</translation>
@@ -2986,6 +3000,7 @@ Usipoiruhusu, itazuiwa na mipangilio yako ya faragha. Hali hii itaruhusu maudhui
<translation id="988159990683914416">Muundo wa Wasanidi Programu</translation>
<translation id="989988560359834682">Badilisha Anwani</translation>
<translation id="991413375315957741">vitambuzi vya mwendo au mwangaza</translation>
+<translation id="992110854164447044">Kadi pepe inaficha kadi yako halisi ili kusaidia kukulinda dhidi ya ulaghai unaoweza kujitokeza. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Waridi</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" haikusakinishwa kwa njia sahihi kwenye mtandao au kompyuta yako:
diff --git a/chromium/components/strings/components_strings_ta.xtb b/chromium/components/strings/components_strings_ta.xtb
index 5432757e57c..eca8cb9a76c 100644
--- a/chromium/components/strings/components_strings_ta.xtb
+++ b/chromium/components/strings/components_strings_ta.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">மானியஙà¯à®•à®³à¯, கலà¯à®µà®¿ உதவிதà¯à®¤à¯Šà®•à¯ˆà®•à®³à¯ &amp; நிதி உதவி</translation>
<translation id="1048785276086539861">விரிவà¯à®°à¯ˆà®•à®³à¯ˆà®¤à¯ திரà¯à®¤à¯à®¤à®¿à®©à®¾à®²à¯ இநà¯à®¤ ஆவணம௠ஒறà¯à®±à¯ˆà®ªà¯ பகà¯à®•à®•à¯à®•à®¾à®Ÿà¯à®šà®¿à®•à¯à®•à¯ மாறà¯à®®à¯</translation>
<translation id="1050038467049342496">பிற ஆபà¯à®¸à¯ˆ மூடவà¯à®®à¯</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ தளஙà¯à®•à®³à®¿à®²à¯ à®…à®™à¯à®•à¯€à®•à®°à®¿à®ªà¯à®ªà¯à®šà¯ சாதனம௠மூலம௠சரிபாரà¯à®•à¯à®•à®¤à¯ தேரà¯à®µà¯à®šà¯†à®¯à¯à®¤à¯à®³à¯à®³à¯€à®°à¯à®•à®³à¯. உஙà¯à®•à®³à¯ பேமெணà¯à®Ÿà¯ à®®à¯à®±à¯ˆ கà¯à®±à®¿à®¤à¯à®¤ தகவலà¯à®•à®³à¯ˆ இநà¯à®¤ வழஙà¯à®•à¯à®¨à®°à¯ சேமிதà¯à®¤à®¿à®°à¯à®•à¯à®•à®•à¯à®•à¯‚டà¯à®®à¯. அவறà¯à®±à¯ˆ நீஙà¯à®•à®³à¯ <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;சேரà¯à®¤à¯à®¤à®²à¯ˆà®šà¯ செயலà¯à®¤à®µà®¿à®°à¯</translation>
<translation id="1056663316309890343">பட மெனà¯à®ªà¯Šà®°à¯à®³à¯</translation>
<translation id="1056898198331236512">எசà¯à®šà®°à®¿à®•à¯à®•à¯ˆ</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">பிகà¯à®…ப௠மà¯à®±à¯ˆ</translation>
<translation id="1281476433249504884">ஸà¯à®Ÿà¯‡à®•à¯à®•à®°à¯ 1</translation>
<translation id="1285320974508926690">இநà¯à®¤ தளதà¯à®¤à¯ˆ எபà¯à®ªà¯‹à®¤à¯à®®à¯ மொழிபெயரà¯à®•à¯à®• வேணà¯à®Ÿà®¾à®®à¯</translation>
+<translation id="1288548991597756084">காரà¯à®Ÿà¯ˆà®ªà¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®•à®šà¯ சேமியà¯à®™à¯à®•à®³à¯</translation>
<translation id="1292571435393770077">டிரே 16</translation>
<translation id="1292701964462482250">"உஙà¯à®•à®³à¯ கமà¯à®ªà¯à®¯à¯‚டà¯à®Ÿà®°à®¿à®²à¯ உளà¯à®³ மெனà¯à®ªà¯Šà®°à¯à®³à¯ இணையதà¯à®¤à¯à®Ÿà®©à¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®• இணைவதிலிரà¯à®¨à¯à®¤à¯ Chromeமைத௠தடà¯à®•à¯à®•à®¿à®±à®¤à¯" (Windows கமà¯à®ªà¯à®¯à¯‚டà¯à®Ÿà®°à¯à®•à®³à¯ மடà¯à®Ÿà¯à®®à¯)</translation>
<translation id="1294154142200295408">கடà¯à®Ÿà®³à¯ˆ வரி மாறà¯à®±à¯ வடிவஙà¯à®•à®³à¯</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;பிழையைச௠சரிசெயà¯à®¯, நீஙà¯à®•à®³à¯ திறகà¯à®• à®®à¯à®¯à®²à¯à®®à¯ பகà¯à®•à®¤à¯à®¤à®¿à®²à¯ உளà¯à®³ &lt;strong&gt;இணை&lt;/strong&gt; எனà¯à®ªà®¤à¯ˆà®•à¯ கிளிக௠செயà¯à®¯à®µà¯à®®à¯.&lt;/p&gt;</translation>
<translation id="1507780850870535225">லேணà¯à®Ÿà¯à®¸à¯à®•à¯‡à®ªà¯ டிசைனà¯</translation>
<translation id="1513706915089223971">வரலாறà¯à®±à¯ உளà¯à®³à¯€à®Ÿà¯à®•à®³à®¿à®©à¯ படà¯à®Ÿà®¿à®¯à®²à¯</translation>
+<translation id="1516097932025103760">கிரெடிட௠காரà¯à®Ÿà¯ தகவலà¯à®•à®³à¯ எனà¯à®•à®¿à®°à®¿à®ªà¯à®Ÿà¯ செயà¯à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®ªà¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®•à®šà¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯. CVC à®’à®°à¯à®ªà¯‹à®¤à¯à®®à¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà®¾à®¤à¯.</translation>
<translation id="1517433312004943670">ஃபோன௠எண௠தேவை</translation>
<translation id="1519264250979466059">உரà¯à®µà®¾à®•à¯à®•à®¿à®¯ தேதி</translation>
<translation id="1521159554480556801">ஃபைபர௠&amp; டெகà¯à®¸à¯à®Ÿà¯ˆà®²à¯ சாரà¯à®¨à¯à®¤ கலைகளà¯</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">கடà¯à®Ÿà®£ à®®à¯à®±à¯ˆà®•à®³à¯ˆà®šà¯ சேமிதà¯à®¤à¯à®¤à¯ தானாக நிரபà¯à®ªà¯</translation>
<translation id="1663943134801823270">காரà¯à®Ÿà¯à®•à®³à¯à®®à¯ à®®à¯à®•à®µà®°à®¿à®•à®³à¯à®®à¯ Chrome இலிரà¯à®¨à¯à®¤à¯ பெறபà¯à®ªà®Ÿà¯à®Ÿà®µà¯ˆà®¯à®¾à®•à¯à®®à¯. <ph name="BEGIN_LINK" />அமைபà¯à®ªà¯à®•à®³à®¿à®²à¯<ph name="END_LINK" /> அவறà¯à®±à¯ˆ நிரà¯à®µà®•à®¿à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="1671391448414634642"><ph name="SOURCE_LANGUAGE" /> மொழியிலà¯à®³à¯à®³ பகà¯à®•à®™à¯à®•à®³à¯ இபà¯à®ªà¯‹à®¤à¯ à®®à¯à®¤à®²à¯ <ph name="TARGET_LANGUAGE" /> மொழியில௠மொழிபெயரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯.</translation>
+<translation id="1673886523110456987">ஆஃபரைப௠பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ <ph name="CARD_DETAIL" /> மூலம௠செக௠அவà¯à®Ÿà¯ செயà¯à®¯à®µà¯à®®à¯</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> இலிரà¯à®¨à¯à®¤à¯ <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">கà¯à®±à¯à®•à®¿à®¯ ஓரம௠மà¯à®¤à®²à®¿à®²à¯</translation>
<translation id="168693727862418163">இநà¯à®¤à®•à¯ கொளà¯à®•à¯ˆ அதன௠திடà¯à®Ÿà®ªà¯à®ªà®£à®¿à®¤à¯ தரவà¯à®•à¯à®•à¯ எதிராக மதிபà¯à®ªà¯€à®Ÿà¯ செயà¯à®¯à®ªà¯à®ªà®Ÿà®¾à®¤à®¤à®¾à®²à¯ பà¯à®±à®•à¯à®•à®£à®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">மோஷன௠செனà¯à®šà®¾à®°à¯à®•à®³à¯</translation>
<translation id="1717494416764505390">அஞà¯à®šà®²à¯ பெடà¯à®Ÿà®¿ 3</translation>
<translation id="1718029547804390981">ஆவணம௠மிகப௠பெரிதாக இரà¯à®ªà¯à®ªà®¤à®¾à®²à¯ அதில௠விரிவà¯à®°à¯ˆà®¯à¯ˆà®šà¯ சேரà¯à®•à¯à®• à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯</translation>
+<translation id="1720941539803966190">பயிறà¯à®šà®¿à®¯à¯ˆ மூடà¯à®®à¯</translation>
<translation id="1721424275792716183">* அவசியமான பà¯à®²à®®à¯</translation>
<translation id="1727613060316725209">சானà¯à®±à®¿à®¤à®´à¯ சரியானதà¯</translation>
<translation id="1727741090716970331">சரியான காரà¯à®Ÿà¯ எணà¯à®£à¯ˆà®šà¯ சேரà¯à®•à¯à®•à®µà¯à®®à¯</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">BBQ &amp; கிரிலà¯à®²à®¿à®™à¯</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> மொழியில௠உளà¯à®³ பகà¯à®•à®™à¯à®•à®³à¯ மொழிபெயரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà®¾à®¤à¯.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{இநà¯à®¤à®•à¯ கடà¯à®Ÿà¯à®ªà¯à®ªà®¾à®Ÿà¯ இயகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯ ‘செயலில௠உளà¯à®³à®¤à¯â€™ எனà¯à®± நிலையில௠இரà¯à®¨à¯à®¤à®¾à®²à¯ உஙà¯à®•à®³à¯ சமீபதà¯à®¤à®¿à®¯ உலாவல௠செயலà¯à®ªà®¾à®Ÿà¯à®Ÿà®¿à®±à¯à®•à¯ மிகவà¯à®®à¯ பொரà¯à®¨à¯à®¤à®•à¯à®•à¯‚டிய பெரிய கà¯à®´à¯ அலà¯à®²à®¤à¯ “கà¯à®´à¯à®µà®¿à®©à®°à¯ˆâ€ Chrome அறிநà¯à®¤à¯à®•à¯Šà®³à¯à®³à¯à®®à¯. விளமà¯à®ªà®°à®¤à®¾à®°à®°à¯à®•à®³à¯ கà¯à®´à¯à®µà®¿à®±à¯à®•à®¾à®© விளமà¯à®ªà®°à®™à¯à®•à®³à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®²à®¾à®®à¯. மேலà¯à®®à¯ உஙà¯à®•à®³à¯ உலாவல௠செயலà¯à®ªà®¾à®Ÿà¯ உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®²à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ à®®à¯à®±à¯ˆà®¯à®¿à®²à¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯. தினமà¯à®®à¯ உஙà¯à®•à®³à¯ கà¯à®´à¯ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯.}=1{இநà¯à®¤à®•à¯ கடà¯à®Ÿà¯à®ªà¯à®ªà®¾à®Ÿà¯ இயகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯ ‘செயலில௠உளà¯à®³à®¤à¯â€™ எனà¯à®± நிலையில௠இரà¯à®¨à¯à®¤à®¾à®²à¯ உஙà¯à®•à®³à¯ சமீபதà¯à®¤à®¿à®¯ உலாவல௠செயலà¯à®ªà®¾à®Ÿà¯à®Ÿà®¿à®±à¯à®•à¯ மிகவà¯à®®à¯ பொரà¯à®¨à¯à®¤à®•à¯à®•à¯‚டிய பெரிய கà¯à®´à¯ அலà¯à®²à®¤à¯ “கà¯à®´à¯à®µà®¿à®©à®°à¯ˆâ€ Chrome அறிநà¯à®¤à¯à®•à¯Šà®³à¯à®³à¯à®®à¯. விளமà¯à®ªà®°à®¤à®¾à®°à®°à¯à®•à®³à¯ கà¯à®´à¯à®µà®¿à®±à¯à®•à®¾à®© விளமà¯à®ªà®°à®™à¯à®•à®³à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®²à®¾à®®à¯. மேலà¯à®®à¯ உஙà¯à®•à®³à¯ உலாவல௠செயலà¯à®ªà®¾à®Ÿà¯ உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®²à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ à®®à¯à®±à¯ˆà®¯à®¿à®²à¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯. தினமà¯à®®à¯ உஙà¯à®•à®³à¯ கà¯à®´à¯ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯.}other{இநà¯à®¤à®•à¯ கடà¯à®Ÿà¯à®ªà¯à®ªà®¾à®Ÿà¯ இயகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯ ‘செயலில௠உளà¯à®³à®¤à¯â€™ எனà¯à®± நிலையில௠இரà¯à®¨à¯à®¤à®¾à®²à¯ உஙà¯à®•à®³à¯ சமீபதà¯à®¤à®¿à®¯ உலாவல௠செயலà¯à®ªà®¾à®Ÿà¯à®Ÿà®¿à®±à¯à®•à¯ மிகவà¯à®®à¯ பொரà¯à®¨à¯à®¤à®•à¯à®•à¯‚டிய பெரிய கà¯à®´à¯ அலà¯à®²à®¤à¯ “கà¯à®´à¯à®µà®¿à®©à®°à¯ˆâ€ Chrome அறிநà¯à®¤à¯à®•à¯Šà®³à¯à®³à¯à®®à¯. விளமà¯à®ªà®°à®¤à®¾à®°à®°à¯à®•à®³à¯ கà¯à®´à¯à®µà®¿à®±à¯à®•à®¾à®© விளமà¯à®ªà®°à®™à¯à®•à®³à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®²à®¾à®®à¯. மேலà¯à®®à¯ உஙà¯à®•à®³à¯ உலாவல௠செயலà¯à®ªà®¾à®Ÿà¯ உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®²à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ à®®à¯à®±à¯ˆà®¯à®¿à®²à¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯. {NUM_DAYS} நாடà¯à®•à®³à¯à®•à¯à®•à¯ à®’à®°à¯à®®à¯à®±à¯ˆ உஙà¯à®•à®³à¯ கà¯à®´à¯ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯.}}</translation>
-<translation id="2053553514270667976">அஞà¯à®šà®²à¯ கà¯à®±à®¿à®¯à¯€à®Ÿà¯</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 பரிநà¯à®¤à¯à®°à¯ˆ}other{# பரிநà¯à®¤à¯à®°à¯ˆà®•à®³à¯}}</translation>
+<translation id="2066915425250589881">நீகà¯à®•à¯à®®à®¾à®±à¯ கோரலாமà¯</translation>
<translation id="2068528718802935086">கà¯à®´à®¨à¯à®¤à¯ˆà®•à®³à¯ &amp; மழலையரà¯à®•à®³à¯</translation>
<translation id="2071156619270205202">இநà¯à®¤à®•à¯ காரà¯à®Ÿà¯ எணà¯à®£à¯ˆ விரà¯à®šà¯à®šà¯à®µà®²à¯ காரà¯à®Ÿà¯ எணà¯à®£à®¾à®•à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯.</translation>
<translation id="2071692954027939183">வழகà¯à®•à®®à®¾à®• நீஙà¯à®•à®³à¯ அறிவிபà¯à®ªà¯à®•à®³à¯ˆ அனà¯à®®à®¤à®¿à®ªà¯à®ªà®¤à®¿à®²à¯à®²à¯ˆ எனà¯à®ªà®¤à®¾à®²à¯ அவை தானாகவே தடà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®©</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">ஒதà¯à®¤à®¿à®šà¯ˆà®µà¯ˆ நிரà¯à®µà®•à®¿à®ªà¯à®ªà®¤à®±à¯à®•à®¾à®© படà¯à®Ÿà®©à¯. எநà¯à®¤à¯†à®¨à¯à®¤à®¤à¯ தகவலà¯à®•à®³à¯ˆ ஒதà¯à®¤à®¿à®šà¯ˆà®•à¯à®• வேணà¯à®Ÿà¯à®®à¯ எனà¯à®ªà®¤à¯ˆ Chrome அமைபà¯à®ªà¯à®•à®³à®¿à®²à¯ நிரà¯à®µà®•à®¿à®•à¯à®•, Enter விசையை à®…à®´à¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯</translation>
<translation id="2091887806945687916">ஒலி</translation>
<translation id="2094505752054353250">டொமைன௠பொரà¯à®¨à¯à®¤à®µà®¿à®²à¯à®²à¯ˆ</translation>
-<translation id="2096368010154057602">தà¯à®±à¯ˆ</translation>
<translation id="2099652385553570808">டிரிபà¯à®ªà®¿à®²à¯ ஸà¯à®Ÿà¯‡à®ªà¯à®ªà®¿à®²à¯ லெஃபà¯à®Ÿà¯</translation>
<translation id="2101225219012730419">பதிபà¯à®ªà¯:</translation>
<translation id="2102134110707549001">வலà¯à®µà®¾à®© கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆà®ªà¯ பரிநà¯à®¤à¯à®°à¯ˆà®šà¯†à®¯à¯â€¦</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">அமெரிகà¯à®• காலà¯à®ªà®¨à¯à®¤à¯</translation>
<translation id="2187317261103489799">கணà¯à®Ÿà®±à®¿ (இயலà¯à®ªà¯)</translation>
<translation id="2188375229972301266">மலà¯à®Ÿà®¿à®ªà¯à®ªà®¿à®²à¯ பஞà¯à®šà¯ பாடà¯à®Ÿà®®à¯</translation>
-<translation id="2188852899391513400">நீஙà¯à®•à®³à¯ தறà¯à®ªà¯‹à®¤à¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®¯ கடவà¯à®šà¯à®šà¯Šà®²à¯, தரவ௠மீறலà¯à®•à¯à®•à¯ உடà¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®ªà¯à®ªà®¤à®¾à®•à®•à¯ கணà¯à®Ÿà®±à®¿à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯. உஙà¯à®•à®³à¯ கணகà¯à®•à¯à®•à®³à¯ˆà®ªà¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®• வைதà¯à®¤à®¿à®°à¯à®•à¯à®•, இபà¯à®ªà¯‹à®¤à¯‡ கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ மாறà¯à®±à¯à®®à®¾à®±à¯à®®à¯ சேமிதà¯à®¤ கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯ˆà®šà¯ சரிபாரà¯à®•à¯à®•à¯à®®à®¾à®±à¯à®®à¯ Googleளின௠கடவà¯à®šà¯à®šà¯Šà®²à¯ நிரà¯à®µà®¾à®•à®¿ பரிநà¯à®¤à¯à®°à¯ˆà®•à¯à®•à®¿à®±à®¤à¯.</translation>
<translation id="219906046732893612">வீடà¯à®Ÿà¯ மேமà¯à®ªà®¾à®Ÿà¯</translation>
<translation id="2202020181578195191">சரியான காலாவதி ஆணà¯à®Ÿà¯ˆ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯</translation>
<translation id="22081806969704220">தடà¯à®Ÿà¯ 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">ஃபைலைத௠திரà¯à®¤à¯à®¤à¯à®¤à®²à¯</translation>
<translation id="2215963164070968490">நாயà¯à®•à®³à¯</translation>
<translation id="2218879909401188352">தறà¯à®ªà¯‹à®¤à¯ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> எனà¯à®®à¯ தளதà¯à®¤à®¿à®²à¯ உளà¯à®³ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯à®•à®³à¯ உஙà¯à®•à®³à¯ சாதனதà¯à®¤à¯ˆà®šà¯ சேதபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ ஆபதà¯à®¤à®¾à®© ஆபà¯à®¸à¯ˆ நிறà¯à®µà®²à®¾à®®à¯, மொபைல௠கடà¯à®Ÿà®£à®¤à¯à®¤à®¿à®²à¯ மறைமà¯à®•à®•à¯ கடà¯à®Ÿà®£à®™à¯à®•à®³à¯ˆà®šà¯ சேரà¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ உஙà¯à®•à®³à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ தகவலைத௠திரà¯à®Ÿà®²à®¾à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">பயிறà¯à®šà®¿à®¯à¯ˆ மீணà¯à®Ÿà¯à®®à¯ தொடஙà¯à®•à¯</translation>
+<translation id="2219735899272417925">சாதனதà¯à®¤à¯ˆ ரீசெட௠செயà¯à®¯ வேணà¯à®Ÿà¯à®®à¯</translation>
<translation id="2224337661447660594">இணைய இணைபà¯à®ªà¯ இலà¯à®²à¯ˆ</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />கணà¯à®Ÿà®±à®¿à®¯à¯à®®à¯ பயனà¯à®ªà®¾à®Ÿà¯à®Ÿà¯ˆà®ªà¯<ph name="END_LINK" /> பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿ இணைபà¯à®ªà¯ˆà®šà¯ சரிசெயà¯à®¯à®µà¯à®®à¯</translation>
<translation id="2239100178324503013">இபà¯à®ªà¯‹à®¤à¯‡ அனà¯à®ªà¯à®ªà¯</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">அனà¯à®®à®¤à®¿ à®®à¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯ (இயலà¯à®ªà¯)</translation>
<translation id="2512413427717747692">Chromeமை இயலà¯à®ªà¯ உலாவியாக அமைபà¯à®ªà®¤à®±à¯à®•à®¾à®© படà¯à®Ÿà®©à¯, iOS அமைபà¯à®ªà¯à®•à®³à¯à®•à¯à®•à¯à®šà¯ செனà¯à®±à¯ சிஸà¯à®Ÿà®¤à¯à®¤à®¿à®©à¯ இயலà¯à®ªà¯ உலாவியாக Chromeமை அமைகà¯à®•, Enter படà¯à®Ÿà®©à¯ˆ à®…à®´à¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯</translation>
<translation id="2515629240566999685">உஙà¯à®•à®³à¯ பகà¯à®¤à®¿à®¯à®¿à®²à¯ உளà¯à®³ சிகà¯à®©à®²à¯ˆà®šà¯ சரிபாரà¯à®¤à¯à®¤à®²à¯</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ தளஙà¯à®•à®³à®¿à®²à¯ Touch ID மூலம௠சரிபாரà¯à®•à¯à®•à®¤à¯ தேரà¯à®µà¯à®šà¯†à®¯à¯à®¤à¯à®³à¯à®³à¯€à®°à¯à®•à®³à¯. உஙà¯à®•à®³à¯ பேமெணà¯à®Ÿà¯ à®®à¯à®±à¯ˆ கà¯à®±à®¿à®¤à¯à®¤ தகவலà¯à®•à®³à¯ˆ இநà¯à®¤ வழஙà¯à®•à¯à®¨à®°à¯ சேமிதà¯à®¤à®¿à®°à¯à®•à¯à®•à®•à¯à®•à¯‚டà¯à®®à¯. அவறà¯à®±à¯ˆ நீஙà¯à®•à®³à¯ <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">ஸà¯à®Ÿà¯‡à®ªà¯à®ªà®¿à®²à¯ பாடà¯à®Ÿà®®à¯ ரைடà¯</translation>
<translation id="2521736961081452453">படிவதà¯à®¤à¯ˆ உரà¯à®µà®¾à®•à¯à®•à¯</translation>
<translation id="2523886232349826891">இநà¯à®¤à®šà¯ சாதனதà¯à®¤à®¿à®²à¯ மடà¯à®Ÿà¯à®®à¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯</translation>
<translation id="2524461107774643265">மேலà¯à®®à¯ தகவலைச௠சேரà¯à®•à¯à®•à®µà¯à®®à¯</translation>
<translation id="2529899080962247600">இதில௠<ph name="MAX_ITEMS_LIMIT" /> உளà¯à®³à¯€à®Ÿà¯à®•à®³à¯à®•à¯à®•à¯ மேல௠இரà¯à®•à¯à®•à®•à¯à®•à¯‚டாதà¯. அதறà¯à®•à¯ மேல௠இரà¯à®ªà¯à®ªà®µà¯ˆ நிராகரிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯.</translation>
+<translation id="253493526287553278">விளமà¯à®ªà®°à®•à¯ கà¯à®±à®¿à®¯à¯€à®Ÿà¯à®Ÿà¯ விவரஙà¯à®•à®³à¯ˆà®ªà¯ பாரà¯à®™à¯à®•à®³à¯</translation>
<translation id="2535585790302968248">தனிபà¯à®ªà®Ÿà¯à®Ÿ à®®à¯à®±à¯ˆà®¯à®¿à®²à¯ உலாவ, பà¯à®¤à®¿à®¯ மறைநிலைப௠பகà¯à®•à®¤à¯à®¤à¯ˆà®¤à¯ திறகà¯à®•à¯à®®à¯</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{மேலà¯à®®à¯ ஒனà¯à®±à¯}other{மேலà¯à®®à¯ #}}</translation>
<translation id="2536110899380797252">à®®à¯à®•à®µà®°à®¿à®¯à¯ˆà®šà¯ சேரà¯</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">பà¯à®•à¯ˆà®ªà¯à®ªà®Ÿà®®à¯ &amp; டிஜிடà¯à®Ÿà®²à¯ கலை</translation>
<translation id="2601150049980261779">ரொமானà¯à®¸à¯ திரைபà¯à®ªà®Ÿà®™à¯à®•à®³à¯</translation>
<translation id="2604589665489080024">பாப௠இசை</translation>
-<translation id="2609632851001447353">வேறà¯à®ªà®¾à®Ÿà¯à®•à®³à¯</translation>
<translation id="2610561535971892504">கிளிக௠செயà¯à®¤à¯ நகலெடà¯à®•à¯à®•à®µà¯à®®à¯</translation>
<translation id="2617988307566202237">பினà¯à®µà®°à¯à®®à¯ தகவலà¯à®•à®³à¯ˆ Chrome <ph name="BEGIN_EMPHASIS" />சேமிகà¯à®•à®¾à®¤à¯<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">பிறநà¯à®¤à®¨à®¾à®Ÿà¯à®•à®³à¯ &amp; பெயர௠வைகà¯à®•à¯à®®à¯ நாடà¯à®•à®³à¯</translation>
<translation id="2677748264148917807">வெளியேறà¯</translation>
+<translation id="2679714844901977852">பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© &amp; விரைவான செகà¯-அவà¯à®Ÿà¯à®•à®³à¯à®•à¯à®•à¯ உஙà¯à®•à®³à¯ காரà¯à®Ÿà¯ˆà®¯à¯à®®à¯ பிலà¯à®²à®¿à®™à¯ à®®à¯à®•à®µà®°à®¿à®¯à¯ˆà®¯à¯à®®à¯ <ph name="USER_EMAIL" /> எனà¯à®± Google கணகà¯à®•à®¿à®²à¯ சேமியà¯à®™à¯à®•à®³à¯</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">கசà¯à®šà®¿à®¤à®®à®¾à®•à®ªà¯ பொரà¯à®¨à¯à®¤à¯à®®à¯ அளவà¯</translation>
<translation id="2688969097326701645">ஆமà¯, தொடரà¯à®•</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">இணைய சேவை வழஙà¯à®•à¯à®¨à®°à¯à®•à®³à¯ (ISPகளà¯)</translation>
<translation id="2856444702002559011"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> தளதà¯à®¤à®¿à®²à®¿à®°à¯à®¨à¯à®¤à¯ ஹேகà¯à®•à®°à¯à®•à®³à¯ உஙà¯à®•à®³à¯ தகவலை (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, செயà¯à®¤à®¿à®•à®³à¯ அலà¯à®²à®¤à¯ கிரெடிட௠காரà¯à®Ÿà¯à®•à®³à¯) திரà¯à®Ÿ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®•à¯à®•à¯‚டà¯à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">கà¯à®±à¯à®•à¯à®•à®¿à®Ÿà¯à®®à¯ அலà¯à®²à®¤à¯ தவறாக வழிநடதà¯à®¤à¯à®®à¯ விளமà¯à®ªà®°à®™à¯à®•à®³à¯ˆ இநà¯à®¤à®¤à¯ தளம௠காணà¯à®ªà®¿à®•à¯à®•à®¿à®±à®¤à¯.</translation>
-<translation id="286512204874376891">மோசடி அபாயஙà¯à®•à®³à®¿à®²à¯ இரà¯à®¨à¯à®¤à¯ உஙà¯à®•à®³à¯ அசல௠காரà¯à®Ÿà¯ˆà®ªà¯ பாதà¯à®•à®¾à®•à¯à®• விரà¯à®šà¯à®šà¯à®µà®²à¯ காரà¯à®Ÿà¯ உதவà¯à®•à®¿à®±à®¤à¯.<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ஃபà¯à®°à®£à¯à®Ÿà¯à®²à®¿</translation>
<translation id="28761159517501904">திரைபà¯à®ªà®Ÿà®™à¯à®•à®³à¯</translation>
<translation id="2876489322757410363">மூனà¯à®±à®¾à®®à¯ தரபà¯à®ªà¯ ஆபà¯à®¸à®¿à®©à¯ மூலம௠பணதà¯à®¤à¯ˆà®šà¯ செலà¯à®¤à¯à®¤, மறைநிலைப௠பயனà¯à®®à¯à®±à¯ˆà®¯à®¿à®²à¯ இரà¯à®¨à¯à®¤à¯ வெளியேறà¯à®•à®¿à®±à®¤à¯. தொடரவா?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">டிஸà¯à®•à¯</translation>
<translation id="3162559335345991374">நீஙà¯à®•à®³à¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®•à¯ கொணà¯à®Ÿà®¿à®°à¯à®•à¯à®•à¯à®®à¯ வைஃபை, அதன௠உளà¯à®¨à¯à®´à¯ˆà®µà¯à®ªà¯ பகà¯à®•à®¤à¯à®¤à¯ˆ நீஙà¯à®•à®³à¯ பாரà¯à®•à¯à®•à®•à¯ கோரலாமà¯.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">தீவà¯</translation>
<translation id="3176929007561373547">பà¯à®°à®¾à®•à¯à®¸à®¿ சரà¯à®µà®°à¯ இயகà¯à®•à®¤à¯à®¤à®¿à®²à¯ உளà¯à®³à®¤à¯ எனà¯à®ªà®¤à¯ˆ உறà¯à®¤à®¿à®ªà¯à®ªà®Ÿà¯à®¤à¯à®¤ உஙà¯à®•à®³à¯ பà¯à®°à®¾à®•à¯à®¸à®¿ அமைபà¯à®ªà¯à®•à®³à¯ˆà®šà¯ சரிபாரà¯à®•à¯à®•à®µà¯à®®à¯ அலà¯à®²à®¤à¯ நெடà¯à®µà¯Šà®°à¯à®•à¯ நிரà¯à®µà®¾à®•à®¿à®¯à¯ˆà®¤à¯ தொடரà¯à®ªà¯à®•à¯Šà®³à¯à®³à®µà¯à®®à¯. நீஙà¯à®•à®³à¯ பà¯à®°à®¾à®•à¯à®¸à®¿ சரà¯à®µà®°à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®²à®¾à®®à¯ எனà¯à®ªà®¤à®¿à®²à¯ நமà¯à®ªà®¿à®•à¯à®•à¯ˆ இலà¯à®²à¯ˆà®¯à¯†à®©à¯à®±à®¾à®²à¯, பினà¯à®µà®°à¯à®µà®¤à¯ˆà®šà¯ செயà¯à®¯à®µà¯à®®à¯:<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">இநà¯à®¤à®šà¯ சாதனதà¯à®¤à®¿à®²à¯ நீஙà¯à®•à®³à¯ செயலில௠இரà¯à®ªà¯à®ªà®¤à¯ கà¯à®±à®¿à®¤à¯à®¤à¯ அறிநà¯à®¤à¯à®•à¯Šà®³à¯à®³à¯à®¤à®²à¯</translation>
<translation id="3180358318770512945">கà¯à®´à®¨à¯à®¤à¯ˆ வளரà¯à®ªà¯à®ªà¯</translation>
@@ -878,9 +883,6 @@
<translation id="3369192424181595722">கடிகாரப௠பிழை</translation>
<translation id="3369459162151165748">வாகன உதிரிபà¯à®ªà®¾à®•à®™à¯à®•à®³à¯ &amp; தà¯à®£à¯ˆà®•à¯à®•à®°à¯à®µà®¿à®•à®³à¯</translation>
<translation id="3371064404604898522">Chromeமை இயலà¯à®ªà¯ உலாவியாக அமை</translation>
-<translation id="3371076217486966826"><ph name="URL" /> இவறà¯à®±à¯ˆà®šà¯ செயà¯à®¯ அனà¯à®®à®¤à®¿ கோரà¯à®•à®¿à®±à®¤à¯:
- • உஙà¯à®•à®³à¯ˆà®šà¯ சà¯à®±à¯à®±à®¿à®¯à¯à®³à¯à®³ இடதà¯à®¤à®¿à®©à¯ 3D மேபà¯à®ªà¯ˆ உரà¯à®µà®¾à®•à¯à®• &amp; கேமராவின௠நிலையைக௠கணà¯à®•à®¾à®£à®¿à®•à¯à®•
- • உஙà¯à®•à®³à¯ கேமராவைப௠பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤</translation>
<translation id="337363190475750230">விடà¯à®µà®¿à®¤à¯à®¤à®¤à¯</translation>
<translation id="3375754925484257129">Chrome பாதà¯à®•à®¾à®ªà¯à®ªà¯à®šà¯ சரிபாரà¯à®ªà¯à®ªà¯ˆ இயகà¯à®•à¯</translation>
<translation id="3377144306166885718">காலாவதியான TLS பதிபà¯à®ªà¯ˆ சேவையகம௠பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®¯à®¤à¯.</translation>
@@ -897,6 +899,7 @@
<translation id="3399952811970034796">டெலிவரி à®®à¯à®•à®µà®°à®¿</translation>
<translation id="3402261774528610252">இநà¯à®¤à®¤à¯ தளதà¯à®¤à¯ˆ à®à®±à¯à®±à¯à®µà®¤à®±à¯à®•à¯à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿ இணைபà¯à®ªà¯ TLS 1.0 அலà¯à®²à®¤à¯ TLS 1.1 பதிபà¯à®ªà¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®¯à®¤à¯, இவை நிறà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯‹à®Ÿà¯ விரைவில௠மà¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯. à®®à¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®µà¯à®Ÿà®©à¯ பயனரà¯à®•à®³à®¾à®²à¯ இநà¯à®¤à®¤à¯ தளதà¯à®¤à¯ˆ à®à®±à¯à®± à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯. சேவையகம௠TLS 1.2 அலà¯à®²à®¤à¯ அதறà¯à®•à¯à®ªà¯ பிநà¯à®¤à¯ˆà®¯ பதிபà¯à®ªà¯ˆ இயகà¯à®• வேணà¯à®Ÿà¯à®®à¯.</translation>
<translation id="3405664148539009465">எழà¯à®¤à¯à®¤à¯à®°à¯à®•à¯à®•à®³à¯ˆà®ªà¯ பிரதà¯à®¤à®¿à®¯à¯‡à®•à®®à®¾à®•à¯à®•à¯</translation>
+<translation id="3407789382767355356">மூனà¯à®±à®¾à®®à¯ தரபà¯à®ªà¯ உளà¯à®¨à¯à®´à¯ˆà®µà¯</translation>
<translation id="3409896703495473338">பாதà¯à®•à®¾à®ªà¯à®ªà¯ அமைபà¯à®ªà¯à®•à®³à¯ˆ நிரà¯à®µà®•à®¿à®¯à¯à®™à¯à®•à®³à¯</translation>
<translation id="3414952576877147120">அளவà¯:</translation>
<translation id="3417660076059365994">நீஙà¯à®•à®³à¯ பதிவேறà¯à®±à¯à®®à¯/இணைகà¯à®•à¯à®®à¯ ஃபைலà¯à®•à®³à¯ Google கிளவà¯à®Ÿà¯à®•à¯à®•à¯‹ மூனà¯à®±à®¾à®®à¯ தரபà¯à®ªà®¿à®©à®°à¯à®•à¯à®•à¯‹ ஆயà¯à®µà¯à®•à¯à®•à®¾à®• அனà¯à®ªà¯à®ªà®ªà¯à®ªà®Ÿà¯à®®à¯. எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, பாதà¯à®•à®¾à®•à¯à®•à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà®¿à®¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ தரவோ மாலà¯à®µà¯‡à®°à¯‹ உளà¯à®³à®¤à®¾ எனà¯à®±à¯ கணà¯à®Ÿà®±à®¿à®µà®¤à®±à¯à®•à®¾à®• அவை ஸà¯à®•à¯‡à®©à¯ செயà¯à®¯à®ªà¯à®ªà®Ÿà®²à®¾à®®à¯.</translation>
@@ -929,6 +932,7 @@
<translation id="3477679029130949506">திரைபà¯à®ªà®Ÿà®ªà¯ படà¯à®Ÿà®¿à®¯à®²à¯à®•à®³à¯ &amp; தியேடà¯à®Ÿà®°à¯ காடà¯à®šà®¿à®¨à¯‡à®°à®™à¯à®•à®³à¯</translation>
<translation id="3479552764303398839">இபà¯à®ªà¯Šà®´à¯à®¤à¯ இலà¯à®²à¯ˆ</translation>
<translation id="3484560055331845446">உஙà¯à®•à®³à¯ Google கணகà¯à®•à®¿à®±à¯à®•à®¾à®© அணà¯à®•à®²à¯ˆ நீஙà¯à®•à®³à¯ இழகà¯à®•à®•à¯à®•à¯‚டà¯à®®à¯. இபà¯à®ªà¯‹à®¤à¯‡ உஙà¯à®•à®³à¯ கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ மாறà¯à®±à¯à®®à®¾à®±à¯ Chrome பரிநà¯à®¤à¯à®°à¯ˆà®•à¯à®•à®¿à®±à®¤à¯. அதறà¯à®•à¯ நீஙà¯à®•à®³à¯ உளà¯à®¨à¯à®´à¯ˆà®¯ வேணà¯à®Ÿà¯à®®à¯.</translation>
+<translation id="3484861421501147767">நினைவூடà¯à®Ÿà®²à¯: சேமிதà¯à®¤ விளமà¯à®ªà®°à®•à¯ கà¯à®±à®¿à®¯à¯€à®Ÿà¯ உளà¯à®³à®¤à¯</translation>
<translation id="3487845404393360112">தடà¯à®Ÿà¯ 4</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" /> பகà¯à®•à®¤à¯à®¤à®¿à®²à¯ கணà¯à®Ÿà®±à®¿à®•</translation>
<translation id="350069200438440499">ஃபைல௠பெயரà¯:</translation>
@@ -1052,6 +1056,7 @@
<translation id="3810973564298564668">நிரà¯à®µà®•à®¿</translation>
<translation id="3816482573645936981">மதிபà¯à®ªà¯ (மாறà¯à®±à®¿à®¯à®®à¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯)</translation>
<translation id="382518646247711829">நீஙà¯à®•à®³à¯ பிராகà¯à®šà®¿ சரà¯à®µà®°à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®©à®¾à®²à¯....</translation>
+<translation id="3826050100957962900">மூனà¯à®±à®¾à®®à¯ தரபà¯à®ªà¯ உளà¯à®¨à¯à®´à¯ˆà®µà¯</translation>
<translation id="3827112369919217609">தà¯à®²à¯à®²à®¿à®¯à®®à®¾à®©à®¤à¯</translation>
<translation id="3827666161959873541">கà¯à®Ÿà¯à®®à¯à®ªà®®à¯ சாரà¯à®¨à¯à®¤ திரைபà¯à®ªà®Ÿà®™à¯à®•à®³à¯</translation>
<translation id="3828924085048779000">வெறà¯à®±à¯ கடவà¯à®šà¯à®šà¯Šà®±à¯à®±à¯Šà®Ÿà®°à¯à®•à¯à®•à¯ அனà¯à®®à®¤à®¿à®¯à®¿à®²à¯à®²à¯ˆ.</translation>
@@ -1064,9 +1069,9 @@
<translation id="3858027520442213535">தேதியையà¯à®®à¯ நேரதà¯à®¤à¯ˆà®¯à¯à®®à¯ பà¯à®¤à¯à®ªà¯à®ªà®¿</translation>
<translation id="3858860766373142691">பெயரà¯</translation>
<translation id="3872834068356954457">அறிவியலà¯</translation>
+<translation id="3875783148670536197">எபà¯à®ªà®Ÿà®¿ எனக௠காடà¯à®Ÿà¯</translation>
<translation id="3881478300875776315">கà¯à®±à¯ˆà®µà®¾à®© வரிகளைக௠காடà¯à®Ÿà¯à®®à¯</translation>
<translation id="3884278016824448484">à®®à¯à®°à®£à¯à®ªà®¾à®Ÿà®¾à®© சாதன அடையாளஙà¯à®•à®¾à®Ÿà¯à®Ÿà®¿</translation>
-<translation id="3885155851504623709">வடà¯à®Ÿà®¾à®°à®®à¯</translation>
<translation id="388632593194507180">கணà¯à®•à®¾à®£à®¿à®ªà¯à®ªà¯ கணà¯à®Ÿà®±à®¿à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯</translation>
<translation id="3886948180919384617">ஸà¯à®Ÿà¯‡à®•à¯à®•à®°à¯ 3</translation>
<translation id="3890664840433101773">மினà¯à®©à®žà¯à®šà®²à¯ˆà®šà¯ சேரà¯</translation>
@@ -1096,9 +1101,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> தடà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯</translation>
<translation id="3978338123949022456">தேடல௠பயனà¯à®®à¯à®±à¯ˆ, <ph name="KEYWORD_SUFFIX" /> இல௠தேட, வினவலை டைப௠செயà¯à®¤à¯ Enter படà¯à®Ÿà®©à¯ˆ à®…à®´à¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯</translation>
<translation id="398470910934384994">பறவைகளà¯</translation>
+<translation id="3985750352229496475">à®®à¯à®•à®µà®°à®¿à®•à®³à¯ˆ நிரà¯à®µà®•à®¿...</translation>
<translation id="3986705137476756801">'உடனடி வசனமà¯' à®…à®®à¯à®šà®¤à¯à®¤à¯ˆà®¤à¯ தறà¯à®ªà¯‹à®¤à¯ˆà®•à¯à®•à¯ à®®à¯à®Ÿà®•à¯à®•à¯à®®à¯</translation>
<translation id="3987940399970879459">1 மெ.பை. அளவை விடக௠கà¯à®±à¯ˆà®µà®¾à®• உளà¯à®³à®¤à¯</translation>
<translation id="3990250421422698716">ஜாக௠ஆஃபà¯à®šà¯†à®Ÿà¯</translation>
+<translation id="3992684624889376114">இநà¯à®¤à®ªà¯ பகà¯à®•à®®à¯ - ஓர௠அறிமà¯à®•à®®à¯</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> தளமà¯, அதறà¯à®•à®¾à®© அனைதà¯à®¤à¯à®•à¯ கோரிகà¯à®•à¯ˆà®•à®³à¯à®•à¯à®•à¯à®®à¯
அசல௠கொளà¯à®•à¯ˆà®¯à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ வேணà¯à®Ÿà¯à®®à¯†à®©à®•à¯ கோரிகà¯à®•à¯ˆ விடà¯à®¤à¯à®¤à¯à®³à¯à®³à®¤à¯, ஆனால௠தறà¯à®šà®®à®¯à®®à¯ அதைச௠செயà¯à®¯ à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯.</translation>
<translation id="4006465311664329701">Google Payயைப௠பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ பேமெணà¯à®Ÿà¯ à®®à¯à®±à¯ˆà®•à®³à¯, ஆஃபரà¯à®•à®³à¯ மறà¯à®±à¯à®®à¯ à®®à¯à®•à®µà®°à®¿à®•à®³à¯</translation>
@@ -1223,6 +1230,7 @@
<translation id="4305666528087210886">உஙà¯à®•à®³à¯ ஃபைலை அணà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="4306529830550717874">à®®à¯à®•à®µà®°à®¿à®¯à¯ˆà®šà¯ சேமிகà¯à®•à®µà®¾?</translation>
<translation id="4306812610847412719">கிளிபà¯à®ªà¯‹à®°à¯à®Ÿà¯</translation>
+<translation id="4310070645992025887">உஙà¯à®•à®³à¯ செயலà¯à®ªà®¾à®Ÿà¯à®•à®³à¯ˆà®¤à¯ தேடà¯à®•</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">தட௠(இயலà¯à®ªà¯)</translation>
<translation id="4314815835985389558">ஒதà¯à®¤à®¿à®šà¯ˆà®µà¯ˆ நிரà¯à®µà®•à®¿à®•à¯à®•à¯à®®à¯ பகà¯à®•à®®à¯</translation>
@@ -1273,6 +1281,7 @@
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">பà¯à®°à®¾à®•à¯à®¸à®¿ பயனà¯à®ªà®¾à®Ÿà¯ à®®à¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯. ஆனால௠வெளிபà¯à®ªà®Ÿà¯ˆà®¯à®¾à®© பà¯à®°à®¾à®•à¯à®¸à®¿ உளà¯à®³à®®à¯ˆà®µà¯ கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯.</translation>
<translation id="4441832193888514600">கிளவà¯à®Ÿà¯ பயனர௠கொளà¯à®•à¯ˆà®¯à®¾à®• மடà¯à®Ÿà¯à®®à¯‡ கொளà¯à®•à¯ˆà®¯à¯ˆ அமைகà¯à®• à®®à¯à®Ÿà®¿à®¯à¯à®®à¯†à®©à¯à®ªà®¤à®¾à®²à¯ பà¯à®±à®•à¯à®•à®£à®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.</translation>
+<translation id="4442470707340296952">Chrome பகà¯à®•à®™à¯à®•à®³à¯</translation>
<translation id="4450893287417543264">மீணà¯à®Ÿà¯à®®à¯ காடà¯à®Ÿà®¾à®¤à¯‡</translation>
<translation id="4451135742916150903">HID சாதனஙà¯à®•à®³à¯à®Ÿà®©à¯ இணைய à®®à¯à®¯à®²à¯à®®à¯à®ªà¯‹à®¤à¯ அனà¯à®®à®¤à®¿ கேடà¯à®• வேணà¯à®Ÿà¯à®®à¯</translation>
<translation id="4452328064229197696">நீஙà¯à®•à®³à¯ தறà¯à®ªà¯‹à®¤à¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®¯ கடவà¯à®šà¯à®šà¯Šà®²à¯, தரவ௠மீறலà¯à®•à¯à®•à¯ உடà¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®ªà¯à®ªà®¤à®¾à®•à®•à¯ கணà¯à®Ÿà®±à®¿à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯. உஙà¯à®•à®³à¯ கணகà¯à®•à¯à®•à®³à¯ˆà®ªà¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®• வைதà¯à®¤à®¿à®°à¯à®•à¯à®•, சேமிதà¯à®¤ கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯ˆà®šà¯ சரிபாரà¯à®•à¯à®•à¯à®®à®¾à®±à¯ Googleளின௠கடவà¯à®šà¯à®šà¯Šà®²à¯ நிரà¯à®µà®¾à®•à®¿ பரிநà¯à®¤à¯à®°à¯ˆà®•à¯à®•à®¿à®±à®¤à¯.</translation>
@@ -1411,6 +1420,7 @@
<translation id="483241715238664915">எசà¯à®šà®°à®¿à®•à¯à®•à¯ˆà®•à®³à¯ˆ இயகà¯à®•à¯</translation>
<translation id="4834250788637067901">Google Payயைப௠பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ பேமெணà¯à®Ÿà¯ à®®à¯à®±à¯ˆà®•à®³à¯, ஆஃபரà¯à®•à®³à¯ மறà¯à®±à¯à®®à¯ à®®à¯à®•à®µà®°à®¿à®•à®³à¯</translation>
<translation id="4838327282952368871">டà¯à®°à¯€à®®à®¿</translation>
+<translation id="4839087176073128681">Googleளின௠சிறநà¯à®¤ பாதà¯à®•à®¾à®ªà¯à®ªà¯ à®…à®®à¯à®šà®™à¯à®•à®³à¯ மூலம௠அடà¯à®¤à¯à®¤ à®®à¯à®±à¯ˆ விரைவாகப௠பணம௠செலà¯à®¤à¯à®¤à®²à®¾à®®à¯, உஙà¯à®•à®³à¯ காரà¯à®Ÿà¯ தகவலà¯à®•à®³à¯ˆà®ªà¯ பாதà¯à®•à®¾à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="4840250757394056958">எனத௠Chrome செயலà¯à®ªà®¾à®Ÿà¯à®•à®³à¯ˆà®•à¯ காடà¯à®Ÿà¯</translation>
<translation id="484462545196658690">தானியஙà¯à®•à¯</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" />, மேலà¯à®®à¯ பலவறà¯à®±à®¿à®²à¯ தளà¯à®³à¯à®ªà®Ÿà®¿ பெறà¯à®™à¯à®•à®³à¯</translation>
@@ -1473,6 +1483,7 @@
<translation id="5011561501798487822">கணà¯à®Ÿà®±à®¿à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿ மொழி</translation>
<translation id="5015510746216210676">சாதனப௠பெயரà¯:</translation>
<translation id="5017554619425969104">நீஙà¯à®•à®³à¯ நகலெடà¯à®¤à¯à®¤ உரை</translation>
+<translation id="5017828934289857214">பினà¯à®©à®°à¯ நினைவூடà¯à®Ÿà¯</translation>
<translation id="5018422839182700155">பகà¯à®•à®¤à¯à®¤à¯ˆà®¤à¯ திறகà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="5019198164206649151">தவறான நிலையில௠மீடà¯à®ªà¯ சேமிபà¯à®ªà¯ உளà¯à®³à®¤à¯</translation>
<translation id="5020776957610079374">சரà¯à®µà®¤à¯‡à®š இசை</translation>
@@ -1492,6 +1503,7 @@
<translation id="5051305769747448211">நேரலை நகைசà¯à®šà¯à®µà¯ˆ நிகழà¯à®šà¯à®šà®¿</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{‘அரà¯à®•à®¿à®²à¯à®³à¯à®³à®µà®±à¯à®±à¯à®Ÿà®©à¯ பகிரà¯à®¤à®²à¯â€™ à®…à®®à¯à®šà®¤à¯à®¤à®¿à®©à¯ மூலம௠இநà¯à®¤ ஃபைலை அனà¯à®ªà¯à®ª, உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®²à¯ <ph name="DISK_SPACE_SIZE" /> சேமிபà¯à®ªà®¿à®Ÿà®¤à¯à®¤à¯ˆà®•à¯ காலியாகà¯à®•à®µà¯à®®à¯}other{‘அரà¯à®•à®¿à®²à¯à®³à¯à®³à®µà®±à¯à®±à¯à®Ÿà®©à¯ பகிரà¯à®¤à®²à¯â€™ à®…à®®à¯à®šà®¤à¯à®¤à®¿à®©à¯ மூலம௠இநà¯à®¤ ஃபைலà¯à®•à®³à¯ˆ அனà¯à®ªà¯à®ª, உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®²à¯ <ph name="DISK_SPACE_SIZE" /> சேமிபà¯à®ªà®¿à®Ÿà®¤à¯à®¤à¯ˆà®•à¯ காலியாகà¯à®•à®µà¯à®®à¯}}</translation>
<translation id="5056549851600133418">உஙà¯à®•à®³à¯à®•à¯à®•à®¾à®© செயà¯à®¤à®¿à®•à¯ கடà¯à®Ÿà¯à®°à¯ˆà®•à®³à¯</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ தளஙà¯à®•à®³à®¿à®²à¯ Windows Hello மூலம௠சரிபாரà¯à®•à¯à®•à®¤à¯ தேரà¯à®µà¯à®šà¯†à®¯à¯à®¤à¯à®³à¯à®³à¯€à®°à¯à®•à®³à¯. உஙà¯à®•à®³à¯ பேமெணà¯à®Ÿà¯ à®®à¯à®±à¯ˆ கà¯à®±à®¿à®¤à¯à®¤ தகவலà¯à®•à®³à¯ˆ இநà¯à®¤ வழஙà¯à®•à¯à®¨à®°à¯ சேமிதà¯à®¤à®¿à®°à¯à®•à¯à®•à®•à¯à®•à¯‚டà¯à®®à¯. அவறà¯à®±à¯ˆ நீஙà¯à®•à®³à¯ <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739"><ph name="LOOKALIKE_DOMAIN" />à®à®•à¯ கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?</translation>
<translation id="5066056036849835175">இதà¯à®µà®°à¯ˆà®¯à®¾à®© பிரிணà¯à®Ÿà®¿à®™à¯ செயலà¯à®•à®³à¯</translation>
<translation id="5068234115460527047">ஹெடà¯à®œà¯ நிதிகளà¯</translation>
@@ -1505,10 +1517,8 @@
<translation id="5087286274860437796">தறà¯à®ªà¯‹à®¤à¯ சேவையகதà¯à®¤à®¿à®©à¯ சானà¯à®±à®¿à®¤à®´à¯ செலà¯à®²à¯à®ªà®Ÿà®¿à®¯à®¾à®•à®¾à®¤à¯.</translation>
<translation id="5087580092889165836">காரà¯à®Ÿà¯ˆà®šà¯ சேரà¯</translation>
<translation id="5088142053160410913">ஆபà¯à®ªà®°à¯‡à®Ÿà¯à®Ÿà®°à¯à®•à¯à®•à®¾à®© மெசேஜà¯</translation>
-<translation id="5089810972385038852">மாநிலமà¯</translation>
<translation id="5093232627742069661">Z-ஃபோலà¯à®Ÿà¯</translation>
<translation id="5094747076828555589">இத௠<ph name="DOMAIN" /> தான௠எனà¯à®ªà®¤à¯ˆ இநà¯à®¤à®šà¯ சேவையகம௠உறà¯à®¤à®¿à®ªà¯à®ªà®Ÿà¯à®¤à¯à®¤à®µà®¿à®²à¯à®²à¯ˆ; இதன௠பாதà¯à®•à®¾à®ªà¯à®ªà¯à®šà¯ சானà¯à®±à®¿à®¤à®´à¯ˆ Chromium நமà¯à®ªà®µà®¿à®²à¯à®²à¯ˆ. இத௠தவறான உளà¯à®³à®®à¯ˆà®µà®¾à®²à¯ à®à®±à¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯ உஙà¯à®•à®³à¯ இணைபà¯à®ªà®¿à®²à¯ கà¯à®±à¯à®•à¯à®•à®¿à®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯.</translation>
-<translation id="5095208057601539847">பிராநà¯à®¤à®¿à®¯à®®à¯</translation>
<translation id="5097099694988056070">CPU/RAM உபயோகம௠போனà¯à®± சாதனப௠பà¯à®³à¯à®³à®¿à®µà®¿à®µà®°à®™à¯à®•à®³à¯</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">பாதà¯à®•à®¾à®ªà¯à®ªà®±à¯à®± தளமà¯</translation>
@@ -1546,6 +1556,7 @@
<translation id="5171045022955879922">தேடà¯à®• அலà¯à®²à®¤à¯ URLலை உளà¯à®³à®¿à®Ÿà¯à®•</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">இயநà¯à®¤à®¿à®°à®®à¯</translation>
+<translation id="5177076414499237632">இநà¯à®¤à®ªà¯ பகà¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à®¾à®© மூலதà¯à®¤à¯ˆà®¯à¯à®®à¯ தலைபà¯à®ªà¯ˆà®¯à¯à®®à¯ கà¯à®±à®¿à®¤à¯à®¤à¯ அறியலாமà¯</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> இல௠இலà¯à®²à¯ˆà®¯à®¾? இநà¯à®¤à®ªà¯ பிழையை தெரிவிகà¯à®•à®µà¯à®®à¯</translation>
<translation id="518639307526414276">செலà¯à®²à®ªà¯à®ªà®¿à®°à®¾à®£à®¿ உணவ௠&amp; பராமரிபà¯à®ªà¯à®ªà¯ பொரà¯à®Ÿà¯à®•à®³à¯</translation>
<translation id="5190835502935405962">பà¯à®•à¯à®®à®¾à®°à¯à®•à¯ படà¯à®Ÿà®¿</translation>
@@ -1706,6 +1717,7 @@
<translation id="5624120631404540903">கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯ˆ நிரà¯à®µà®•à®¿</translation>
<translation id="5629630648637658800">கொளà¯à®•à¯ˆ அமைபà¯à®ªà¯à®•à®³à¯ˆ à®à®±à¯à®±à¯à®µà®¤à®¿à®²à¯ தோலà¯à®µà®¿</translation>
<translation id="5631439013527180824">தவறான சாதன நிரà¯à®µà®¾à®• டோகà¯à®•à®©à¯</translation>
+<translation id="5632485077360054581">எபà¯à®ªà®Ÿà®¿ எனக௠காடà¯à®Ÿà¯</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> தளதà¯à®¤à®¿à®²à¯ தறà¯à®ªà¯‹à®¤à¯à®³à¯à®³ ஹேகà¯à®•à®°à¯à®•à®³à¯ உஙà¯à®•à®³à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ தகவலை (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, படஙà¯à®•à®³à¯, கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, செயà¯à®¤à®¿à®•à®³à¯ மறà¯à®±à¯à®®à¯ கிரெடிட௠காரà¯à®Ÿà¯à®•à®³à¯) திரà¯à®Ÿà®•à¯à®•à¯‚டிய அலà¯à®²à®¤à¯ நீகà¯à®•à®•à¯à®•à¯‚டிய ஆபதà¯à®¤à®¾à®© நிரலà¯à®•à®³à¯ˆ உஙà¯à®•à®³à¯ கமà¯à®ªà¯à®¯à¯‚டà¯à®Ÿà®°à®¿à®²à¯ நிறà¯à®µ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®•à¯à®•à¯‚டà¯à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">à®à®®à®¾à®±à¯à®±à®¤à¯à®¤à®•à¯à®• உளà¯à®³à®Ÿà®•à¯à®•à®®à¯ தடà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.</translation>
<translation id="5633259641094592098">கலà¯à®Ÿà¯ &amp; இணà¯à®Ÿà¯€ திரைபà¯à®ªà®Ÿà®™à¯à®•à®³à¯</translation>
@@ -1823,6 +1835,7 @@
<translation id="5989320800837274978">பà¯à®°à®¾à®•à¯à®¸à®¿ சேவையகம௠சரிசெயà¯à®¯à®ªà¯à®ªà®Ÿà®µà¯à®®à¯ இலà¯à®²à¯ˆ .pac ஸà¯à®•à®¿à®°à®¿à®ªà¯à®Ÿà¯ URL கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà®ªà¯à®ªà®Ÿà®µà¯à®®à®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="5992691462791905444">இனà¯à®œà®¿à®©à®¿à®¯à®°à®¿à®™à¯ Z-ஃபோலà¯à®Ÿà¯</translation>
<translation id="5995727681868049093">உஙà¯à®•à®³à¯ Google கணகà¯à®•à®¿à®²à¯ உளà¯à®³ தகவலà¯à®•à®³à¯, தனியà¯à®°à®¿à®®à¯ˆ, பாதà¯à®•à®¾à®ªà¯à®ªà¯ ஆகியவறà¯à®±à¯ˆ நிரà¯à®µà®•à®¿à®•à¯à®•à®²à®¾à®®à¯</translation>
+<translation id="5997247540087773573">நீஙà¯à®•à®³à¯ தறà¯à®ªà¯‹à®¤à¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®¯ கடவà¯à®šà¯à®šà¯Šà®²à¯, தரவ௠மீறலà¯à®•à¯à®•à¯ உளà¯à®³à®¾à®•à®¿à®¯à®¿à®°à¯à®ªà¯à®ªà®¤à®¾à®•à®•à¯ கணà¯à®Ÿà®±à®¿à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯. உஙà¯à®•à®³à¯ கணகà¯à®•à¯à®•à®³à¯ˆà®ªà¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®• வைதà¯à®¤à®¿à®°à¯à®•à¯à®•, இபà¯à®ªà¯‹à®¤à¯‡ கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ மாறà¯à®±à¯à®®à¯à®ªà®Ÿà®¿à®¯à¯à®®à¯ சேமிதà¯à®¤ கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯ˆà®šà¯ சரிபாரà¯à®•à¯à®•à¯à®®à¯à®ªà®Ÿà®¿à®¯à¯à®®à¯ Google Password Manager பரிநà¯à®¤à¯à®°à¯ˆà®•à¯à®•à®¿à®±à®¤à¯.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />'கà¯à®•à¯ <ph name="RESULT_COUNT" /> à®®à¯à®Ÿà®¿à®µà¯à®•à®³à¯ உளà¯à®³à®©</translation>
<translation id="6006484371116297560">கிளாசிகà¯</translation>
<translation id="6008122969617370890">பினà¯à®©à¯‹à®•à¯à®•à®¿à®¯ வரிசை</translation>
@@ -1917,7 +1930,6 @@
<translation id="627746635834430766">அடà¯à®¤à¯à®¤ à®®à¯à®±à¯ˆ விரைவாகப௠பணம௠அனà¯à®ªà¯à®ª, உஙà¯à®•à®³à¯ காரà¯à®Ÿà¯ˆà®¯à¯à®®à¯ பிலà¯à®²à®¿à®™à¯ à®®à¯à®•à®µà®°à®¿à®¯à¯ˆà®¯à¯à®®à¯ Google கணகà¯à®•à®¿à®²à¯ சேமிகà¯à®•à®µà¯à®®à¯.</translation>
<translation id="6279183038361895380">உஙà¯à®•à®³à¯ சà¯à®Ÿà¯à®Ÿà®¿à®¯à¯ˆà®•à¯ காடà¯à®Ÿ |<ph name="ACCELERATOR" />| எனà¯à®ªà®¤à¯ˆ à®…à®´à¯à®¤à¯à®¤à®µà¯à®®à¯</translation>
<translation id="6280223929691119688">இநà¯à®¤ à®®à¯à®•à®µà®°à®¿à®•à¯à®•à¯ டெலிவரி செயà¯à®¯ à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯. வேற௠மà¯à®•à®µà®°à®¿à®¯à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯.</translation>
-<translation id="6282194474023008486">அஞà¯à®šà®²à¯ கà¯à®±à®¿à®¯à¯€à®Ÿà¯</translation>
<translation id="6285507000506177184">Chromeமில௠பதிவிறகà¯à®•à®¿à®¯à®µà®±à¯à®±à¯ˆ நிரà¯à®µà®•à®¿à®ªà¯à®ªà®¤à®±à¯à®•à®¾à®© படà¯à®Ÿà®©à¯. Chromeமில௠நீஙà¯à®•à®³à¯ பதிவிறகà¯à®•à®¿à®¯ ஃபைலà¯à®•à®³à¯ˆ நிரà¯à®µà®•à®¿à®•à¯à®•, Enter படà¯à®Ÿà®©à¯ˆ à®…à®´à¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯</translation>
<translation id="6289939620939689042">பகà¯à®• வணà¯à®£à®®à¯</translation>
<translation id="6290238015253830360">நீஙà¯à®•à®³à¯ பரிநà¯à®¤à¯à®°à¯ˆà®¤à¯à®¤ கடà¯à®Ÿà¯à®°à¯ˆà®•à®³à¯ இஙà¯à®•à¯‡ தோனà¯à®±à¯à®®à¯</translation>
@@ -1939,6 +1951,7 @@
<translation id="6337133576188860026"><ph name="SIZE" />கà¯à®•à¯à®®à¯ கà¯à®±à¯ˆà®µà®¾à®© அளவைக௠காலியாகà¯à®•à¯à®®à¯. நீஙà¯à®•à®³à¯ அடà¯à®¤à¯à®¤ à®®à¯à®±à¯ˆ பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà¯à®®à¯ போதà¯, சில தளஙà¯à®•à®³à¯ மிகவà¯à®®à¯ மெதà¯à®µà®¾à®• à®à®±à¯à®±à®ªà¯à®ªà®Ÿà®²à®¾à®®à¯.</translation>
<translation id="6337534724793800597">பெயரினà¯à®ªà®Ÿà®¿ கொளà¯à®•à¯ˆà®•à®³à¯ˆ வடி</translation>
<translation id="6340739886198108203">ரகசிய உளà¯à®³à®Ÿà®•à¯à®•à®®à¯ காடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®®à¯à®ªà¯‹à®¤à¯ ஸà¯à®•à®¿à®°à¯€à®©à¯à®·à®¾à®Ÿà¯ எடà¯à®ªà¯à®ªà®¤à¯ˆà®¯à¯à®®à¯ ரெகà¯à®•à®¾à®°à¯à®Ÿà¯ செயà¯à®µà®¤à¯ˆà®¯à¯à®®à¯ நிரà¯à®µà®¾à®•à®•à¯ கொளà¯à®•à¯ˆ பரிநà¯à®¤à¯à®°à¯ˆà®•à¯à®•à®µà®¿à®²à¯à®²à¯ˆ:</translation>
+<translation id="6348220984832452017">செயலில௠உளà¯à®³ பதிபà¯à®ªà¯à®•à®³à¯</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ஆபà¯à®¸à¯ˆ நிறà¯à®µà¯à®•</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{எதà¯à®µà¯à®®à®¿à®²à¯à®²à¯ˆ}=1{1 கடவà¯à®šà¯à®šà¯Šà®²à¯ (டொமைனà¯à®•à®³à¯: <ph name="DOMAIN_LIST" />, ஒதà¯à®¤à®¿à®šà¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯)}=2{2 கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯ (டொமைனà¯à®•à®³à¯: <ph name="DOMAIN_LIST" />, ஒதà¯à®¤à®¿à®šà¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®©)}other{# கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯ (டொமைனà¯à®•à®³à¯: <ph name="DOMAIN_LIST" />, ஒதà¯à®¤à®¿à®šà¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®©)}}</translation>
<translation id="6355392890578844978">இநà¯à®¤ உலாவியை ஒர௠நிறà¯à®µà®©à®®à¯‹ பிற அமைபà¯à®ªà¯‹ நிரà¯à®µà®•à®¿à®•à¯à®•à®µà®¿à®²à¯à®²à¯ˆ. இநà¯à®¤à®šà¯ சாதனதà¯à®¤à®¿à®©à¯ செயலà¯à®ªà®¾à®Ÿà¯à®•à®³à¯ˆ Chromium அலà¯à®²à®¾à®®à®²à¯ வேற௠இடதà¯à®¤à®¿à®²à¯ இரà¯à®¨à¯à®¤à¯ நிரà¯à®µà®•à®¿à®•à¯à®•à®²à®¾à®®à¯. <ph name="BEGIN_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LINK" /></translation>
@@ -1970,7 +1983,6 @@
<translation id="643051589346665201">Google கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ மாறà¯à®±à¯</translation>
<translation id="6433490469411711332">தொடரà¯à®ªà¯à®¤à¯ தகவலை மாறà¯à®±à¯</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> இணைகà¯à®• மறà¯à®¤à¯à®¤à®¤à¯.</translation>
-<translation id="6438025220577812695">நானே மாறà¯à®±à®¿à®•à¯à®•à¯Šà®³à¯à®•à®¿à®±à¯‡à®©à¯</translation>
<translation id="6440503408713884761">பà¯à®±à®•à¯à®•à®£à®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯</translation>
<translation id="6443406338865242315">நீஙà¯à®•à®³à¯ நிறà¯à®µà®¿à®¯à¯à®³à¯à®³ நீடà¯à®Ÿà®¿à®ªà¯à®ªà¯à®•à®³à¯à®®à¯ செரà¯à®•à¯à®¨à®¿à®°à®²à¯à®•à®³à¯à®®à¯</translation>
<translation id="6446163441502663861">Kahu (எனà¯à®µà®²à®ªà¯)</translation>
@@ -2100,9 +2112,9 @@
<translation id="6828866289116430505">மரபியலà¯</translation>
<translation id="6831043979455480757">Translate</translation>
<translation id="6833752742582340615">பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© &amp; விரைவான செகà¯-அவà¯à®Ÿà¯à®•à®³à¯à®•à¯à®•à¯, உஙà¯à®•à®³à¯ காரà¯à®Ÿà¯ˆà®¯à¯à®®à¯ பிலà¯à®²à®¿à®™à¯ à®®à¯à®•à®µà®°à®¿à®¯à¯ˆà®¯à¯à®®à¯ Google கணகà¯à®•à®¿à®²à¯ சேமியà¯à®™à¯à®•à®³à¯</translation>
-<translation id="6839929833149231406">பரபà¯à®ªà¯</translation>
<translation id="6846340164947227603">விரà¯à®šà¯à®šà¯à®µà®²à¯ காரà¯à®Ÿà¯ எணà¯à®£à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®•...</translation>
<translation id="6852204201400771460">ஆபà¯à®¸à¯ˆ ரெஃபà¯à®°à¯†à®·à¯ செயà¯à®¯à®µà®¾?</translation>
+<translation id="6857776781123259569">கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯ˆ நிரà¯à®µà®•à®¿...</translation>
<translation id="686485648936420384">வாடிகà¯à®•à¯ˆà®¯à®¾à®³à®°à¯à®•à¯à®•à¯ வழஙà¯à®•à®•à¯à®•à¯‚டிய தகவலà¯à®•à®³à¯</translation>
<translation id="6865412394715372076">இநà¯à®¤à®•à¯ காரà¯à®Ÿà¯ˆ இபà¯à®ªà¯‹à®¤à¯ சரிபாரà¯à®•à¯à®• à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯</translation>
<translation id="6869334554832814367">தனிநபர௠கடனà¯à®•à®³à¯</translation>
@@ -2151,7 +2163,6 @@
<translation id="6965978654500191972">சாதனமà¯</translation>
<translation id="696703987787944103">பெரà¯à®šà¯†à®ªà¯à®šà¯à®šà¯à®µà®²à¯</translation>
<translation id="6968269510885595029">உஙà¯à®•à®³à¯ பாதà¯à®•à®¾à®ªà¯à®ªà¯ விசையைப௠பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯</translation>
-<translation id="6970216967273061347">மாவடà¯à®Ÿà®®à¯</translation>
<translation id="6971439137020188025">Slidesஸில௠பà¯à®¤à®¿à®¯ Google விளகà¯à®•à®•à¯à®•à®¾à®Ÿà¯à®šà®¿à®¯à¯ˆ விரைவாக உரà¯à®µà®¾à®•à¯à®•à¯à®®à¯</translation>
<translation id="6972629891077993081">HID சாதனஙà¯à®•à®³à¯</translation>
<translation id="6973656660372572881">நிலையான பà¯à®°à®¾à®•à¯à®¸à®¿ சேவையகஙà¯à®•à®³à¯à®®à¯ .pac ஸà¯à®•à®¿à®°à®¿à®ªà¯à®Ÿà¯ URL ஆகிய இரணà¯à®Ÿà¯à®®à¯ கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯.</translation>
@@ -2190,7 +2201,6 @@
<translation id="7081308185095828845">உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®²à¯ இநà¯à®¤ à®…à®®à¯à®šà®®à¯ இலà¯à®²à¯ˆ</translation>
<translation id="7083258188081898530">தடà¯à®Ÿà¯ 9</translation>
<translation id="7086090958708083563">பயனர௠கோரிய பதிவேறà¯à®±à®®à¯</translation>
-<translation id="7087282848513945231">மாகாணமà¯</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />. Chrome அமைபà¯à®ªà¯à®•à®³à®¿à®²à¯ அனà¯à®®à®¤à®¿à®•à®³à¯ˆà®¯à¯à®®à¯ தளஙà¯à®•à®³à¯ à®®à¯à®´à¯à®µà®¤à¯à®®à¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³ தரவையà¯à®®à¯ நிரà¯à®µà®•à®¿à®•à¯à®•, Enter விசையை à®…à®´à¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯</translation>
<translation id="7096937462164235847">இநà¯à®¤ இணையதளதà¯à®¤à®¿à®©à¯ அடையாளம௠சரிபாரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="7101893872976785596">திகில௠திரைபà¯à®ªà®Ÿà®™à¯à®•à®³à¯</translation>
@@ -2209,10 +2219,10 @@
<ph name="LIST_ITEM" />படிவஙà¯à®•à®³à®¿à®²à¯ டைப௠செயà¯à®¤ தகவலà¯à®•à®³à¯<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">இநà¯à®¤ à®®à¯à®•à®µà®°à®¿à®•à¯à®•à¯ அனà¯à®ªà¯à®ª à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯. வேற௠மà¯à®•à®µà®°à®¿à®¯à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">இநà¯à®¤à®¤à¯ தரவை நகலெடà¯à®ªà¯à®ªà®¤à¯ˆ உஙà¯à®•à®³à¯ நிரà¯à®µà®¾à®•à®¿ தடà¯à®¤à¯à®¤à¯à®³à¯à®³à®¾à®°à¯.</translation>
<translation id="7135130955892390533">நிலையைக௠காடà¯à®Ÿà¯</translation>
<translation id="7138472120740807366">டெலிவரி à®®à¯à®±à¯ˆ</translation>
-<translation id="7139724024395191329">எமிரேடà¯</translation>
<translation id="7139892792842608322">à®®à¯à®¤à®©à¯à®®à¯ˆ டிரே</translation>
<translation id="714064300541049402">சைட௠2 இமேஜ௠X ஷிஃபà¯à®Ÿà¯</translation>
<translation id="7152423860607593928">Number-14 (எனà¯à®µà®²à®ªà¯)</translation>
@@ -2429,6 +2439,7 @@
<translation id="7669271284792375604">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®²à¯ உளà¯à®³ ஹேகà¯à®•à®°à¯à®•à®³à¯, உஙà¯à®•à®³à¯ˆ à®à®®à®¾à®±à¯à®±à®¿ (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, உஙà¯à®•à®³à¯ à®®à¯à®•à®ªà¯à®ªà¯à®ªà¯ பகà¯à®•à®¤à¯à®¤à¯ˆ மாறà¯à®±à¯à®µà®¤à¯ அலà¯à®²à®¤à¯ நீஙà¯à®•à®³à¯ பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà¯à®®à¯ தளஙà¯à®•à®³à®¿à®²à¯ கூடà¯à®¤à®²à¯ விளமà¯à®ªà®°à®™à¯à®•à®³à¯ˆà®•à¯ காடà¯à®Ÿà¯à®µà®¤à¯), உஙà¯à®•à®³à¯ உலாவல௠அனà¯à®ªà®µà®¤à¯à®¤à¯ˆà®ªà¯ பாதிகà¯à®•à®•à¯à®•à¯‚டிய நிரலà¯à®•à®³à¯ˆ நிறà¯à®µ வைகà¯à®•à®²à®¾à®®à¯.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{ரகசியமானத௠எனக௠கொடியிடபà¯à®ªà®Ÿà¯à®Ÿ தரவில௠மேறà¯à®•à¯Šà®³à¯à®³à®ªà¯à®ªà®Ÿà¯à®Ÿ செயலà¯à®•à®³à¯ (உளà¯à®¨à¯à®´à¯ˆà®¨à¯à®¤à®¤à®¿à®²à¯ இரà¯à®¨à¯à®¤à¯ ஒர௠செயலà¯). <ph name="BEGIN_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LINK" />}other{ரகசியமானத௠எனக௠கொடியிடபà¯à®ªà®Ÿà¯à®Ÿ தரவில௠மேறà¯à®•à¯Šà®³à¯à®³à®ªà¯à®ªà®Ÿà¯à®Ÿ செயலà¯à®•à®³à¯ (உளà¯à®¨à¯à®´à¯ˆà®¨à¯à®¤à®¤à®¿à®²à¯ இரà¯à®¨à¯à®¤à¯ # செயலà¯à®•à®³à¯). <ph name="BEGIN_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">அஞà¯à®šà®²à¯ பெடà¯à®Ÿà®¿ 6</translation>
+<translation id="7675325315208090829">பேமெணà¯à®Ÿà¯ à®®à¯à®±à¯ˆà®•à®³à¯ˆ நிரà¯à®µà®•à®¿à®•à¯à®•à®µà¯à®®à¯...</translation>
<translation id="7676643023259824263"><ph name="TEXT" /> கிளிபà¯-போரà¯à®Ÿà¯ உரையைத௠தேடà¯à®®à¯</translation>
<translation id="7679367271685653708">இதà¯à®µà®°à¯ˆ இணையதà¯à®¤à®¿à®²à¯ பாரà¯à®¤à¯à®¤à®µà®±à¯à®±à¯ˆ Chrome அமைபà¯à®ªà¯à®•à®³à®¿à®²à¯ தெரிநà¯à®¤à¯à®•à¯Šà®³à¯à®³à®²à®¾à®®à¯ நிரà¯à®µà®•à®¿à®•à¯à®•à®²à®¾à®®à¯</translation>
<translation id="7679947978757153706">பேஸà¯à®ªà®¾à®²à¯</translation>
@@ -2471,7 +2482,6 @@
<translation id="7766518757692125295">ஸà¯à®•à®°à¯à®Ÿà¯</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">அதே வரிசையில௠மேல௠நோகà¯à®•à®¿à®¯ பாணி</translation>
-<translation id="777702478322588152">பà¯à®°à¯€à®ƒà®ªà¯†à®•à¯à®šà®°à¯</translation>
<translation id="7791011319128895129">வெளியிடபà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="7791196057686275387">பேலà¯</translation>
<translation id="7791543448312431591">சேரà¯</translation>
@@ -2562,12 +2572,12 @@
<translation id="8055534648776115597">தொழிலà¯à®®à¯à®±à¯ˆ &amp; தொடர௠கலà¯à®µà®¿</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" மெனà¯à®ªà¯Šà®°à¯à®³à¯ சரியாக உளà¯à®³à®®à¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ. வழகà¯à®•à®®à®¾à®•, "<ph name="SOFTWARE_NAME" />"஠நிறà¯à®µà®²à¯ நீகà¯à®•à®¿à®©à®¾à®²à¯ சிகà¯à®•à®²à¯ சரியாகிவிடà¯à®®à¯. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">உணவà¯à®¤à¯ தயாரிபà¯à®ªà¯</translation>
-<translation id="8066955247577885446">à®à®¤à¯‡à®¾ தவறாகிவிடà¯à®Ÿà®¤à¯.</translation>
<translation id="8067872629359326442">மோசடிசெயà¯à®¯à¯à®®à¯ இணையதளதà¯à®¤à®¿à®²à¯ உஙà¯à®•à®³à¯ கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ உளà¯à®³à®¿à®Ÿà¯à®Ÿà¯à®³à¯à®³à¯€à®°à¯à®•à®³à¯. இதில௠Chromium உஙà¯à®•à®³à¯à®•à¯à®•à¯ உதவலாமà¯. உஙà¯à®•à®³à¯ கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ மாறà¯à®±à¯à®µà®¤à®±à¯à®•à¯à®®à¯ உஙà¯à®•à®³à®¿à®©à¯ கணகà¯à®•à¯ ஆபதà¯à®¤à®¿à®²à¯ இரà¯à®•à¯à®•à®•à¯à®•à¯‚டà¯à®®à¯ எனà¯à®ªà®¤à¯ˆ Googleளà¯à®•à¯à®•à¯à®¤à¯ தெரியபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®µà®¤à®±à¯à®•à¯à®®à¯ 'கணகà¯à®•à¯ˆà®ªà¯ பாதà¯à®•à®¾à®¤à¯à®¤à®¿à®Ÿà¯' எனà¯à®ªà®¤à¯ˆà®•à¯ கிளிக௠செயà¯à®¯à¯à®™à¯à®•à®³à¯.</translation>
<translation id="8070439594494267500">ஆபà¯à®¸à¯ à®à®•à®¾à®©à¯</translation>
<translation id="8074253406171541171">10x13 (எனà¯à®µà®²à®ªà¯)</translation>
<translation id="8075736640322370409">பà¯à®¤à®¿à®¯ Google விரிதாளை விரைவாக உரà¯à®µà®¾à®•à¯à®•à¯à®®à¯</translation>
<translation id="8075898834294118863">தள அமைபà¯à®ªà¯à®•à®³à¯ˆ நிரà¯à®µà®•à®¿à®¯à¯à®™à¯à®•à®³à¯</translation>
+<translation id="8076492880354921740">தாவலà¯à®•à®³à¯</translation>
<translation id="8078141288243656252">சà¯à®´à®±à¯à®±à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®ªà¯‹à®¤à¯ ஆவணதà¯à®¤à®¿à®²à¯ விரிவà¯à®°à¯ˆà®¯à¯ˆà®šà¯ சேரà¯à®•à¯à®• à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯</translation>
<translation id="8079031581361219619">தளதà¯à®¤à¯ˆ மீணà¯à®Ÿà¯à®®à¯ à®à®±à¯à®±à®µà®¾?</translation>
<translation id="8081087320434522107">செடான௠காரà¯à®•à®³à¯</translation>
@@ -2690,6 +2700,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">அமைபà¯à®ªà¯à®•à®³à¯</translation>
+<translation id="8428634594422941299">நிராகரி</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />. Chrome அமைபà¯à®ªà¯à®•à®³à®¿à®²à¯ கà¯à®•à¯à®•à¯€ விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯‡à®°à¯à®µà¯à®•à®³à¯ˆ நிரà¯à®µà®•à®¿à®•à¯à®•, Tab விசையை à®…à®´à¯à®¤à¯à®¤à®¿à®µà®¿à®Ÿà¯à®Ÿà¯ Enter விசையை à®…à®´à¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯</translation>
<translation id="8433057134996913067">இதனால௠பெரà¯à®®à¯à®ªà®¾à®²à®¾à®© வலைதà¯à®¤à®³à®™à¯à®•à®³à®¿à®²à¯ இரà¯à®¨à¯à®¤à¯ வெளியேறà¯à®±à®ªà¯à®ªà®Ÿà¯à®µà¯€à®°à¯à®•à®³à¯.</translation>
<translation id="8434840396568290395">செலà¯à®²à®ªà¯à®ªà®¿à®°à®¾à®£à®¿à®•à®³à¯</translation>
@@ -2787,6 +2798,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" />கà¯à®•à®¾à®© உஙà¯à®•à®³à¯ கà¯à®±à®¿à®¯à¯€à®Ÿà¯: <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">இநà¯à®¤à®¤à¯ தாவலைப௠பà¯à®•à¯à®®à®¾à®°à¯à®•à¯ செயà¯à®¯à¯à®®à¯</translation>
<translation id="8751426954251315517">பிறக௠மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®²à®µà¯à®®à¯</translation>
+<translation id="8757526089434340176">Google Pay சலà¯à®•à¯ˆ உளà¯à®³à®¤à¯</translation>
<translation id="8758885506338294482">போடà¯à®Ÿà®¿ சாரà¯à®¨à¯à®¤ வீடியோ கேமà¯à®•à®³à¯</translation>
<translation id="8759274551635299824">காரà¯à®Ÿà¯ காலாவதியாகிவிடà¯à®Ÿà®¤à¯</translation>
<translation id="87601671197631245">இநà¯à®¤à®¤à¯ தளம௠காலாவதியான பாதà¯à®•à®¾à®ªà¯à®ªà¯ உளà¯à®³à®®à¯ˆà®µà¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®•à®¿à®±à®¤à¯, இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®±à¯à®•à¯ உஙà¯à®•à®³à¯ தகவலà¯à®•à®³à¯ˆ (உதாரணமாக, கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, மெசேஜà¯à®•à®³à¯ அலà¯à®²à®¤à¯ கிரெடிட௠காரà¯à®Ÿà¯ தகவலà¯à®•à®³à¯ போனà¯à®±à®µà¯ˆ) அனà¯à®ªà¯à®ªà¯à®®à¯à®ªà¯‹à®¤à¯ அவறà¯à®±à¯ˆ இத௠வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à®•à¯à®•à¯‚டà¯à®®à¯.</translation>
@@ -2794,6 +2806,7 @@
<translation id="8763927697961133303">USB சாதனமà¯</translation>
<translation id="8763986294015493060">தறà¯à®ªà¯‹à®¤à¯ திறநà¯à®¤à¯à®³à¯à®³ மறைநிலைச௠சாளரஙà¯à®•à®³à¯ அனைதà¯à®¤à¯ˆà®¯à¯à®®à¯ மூடà¯à®®à¯</translation>
<translation id="8766943070169463815">பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© பேமெணà¯à®Ÿà¯à®Ÿà®¿à®©à¯ அனà¯à®®à®¤à®¿à®šà¯ சானà¯à®±à¯ˆ à®…à®™à¯à®•à¯€à®•à®°à®¿à®ªà¯à®ªà®¤à®±à¯à®•à®¾à®© தாள௠திறகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯</translation>
+<translation id="8767765348545497220">உதவிக௠கà¯à®®à®¿à®´à¯ˆ மூடà¯à®®à¯</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">பைகà¯à®•à¯à®•à®³à¯</translation>
<translation id="8790007591277257123">&amp;நீகà¯à®•à¯à®¤à®²à¯ˆ மீணà¯à®Ÿà¯à®®à¯ செயà¯</translation>
@@ -2806,6 +2819,7 @@
<translation id="8806285662264631610">கà¯à®³à®¿à®¯à®²à¯ &amp; உடல௠சாரà¯à®¨à¯à®¤ தயாரிபà¯à®ªà¯à®•à®³à¯</translation>
<translation id="8807160976559152894">டிரிம௠ஆஃபà¯à®Ÿà®°à¯ ஈச௠பேஜà¯</translation>
<translation id="8808828119384186784">Chrome அமைபà¯à®ªà¯à®•à®³à¯</translation>
+<translation id="8813277370772331957">பினà¯à®©à®°à¯ நினைவூடà¯à®Ÿà¯</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome அமைபà¯à®ªà¯à®•à®³à®¿à®²à¯ Chromeமைப௠பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®• Tab விசையை à®…à®´à¯à®¤à¯à®¤à®¿à®¯ பிறக௠Enter விசையை à®…à®´à¯à®¤à¯à®¤à¯à®™à¯à®•à®³à¯</translation>
<translation id="8820817407110198400">பà¯à®•à¯à®®à®¾à®°à¯à®•à¯à®•à¯à®•à®³à¯</translation>
<translation id="882338992931677877">மேனà¯à®µà®²à¯ ஸà¯à®²à®¾à®Ÿà¯</translation>
@@ -2986,6 +3000,7 @@
<translation id="988159990683914416">டெவெலபà¯à®ªà®°à¯ கடà¯à®Ÿà®®à¯ˆà®ªà¯à®ªà¯</translation>
<translation id="989988560359834682">à®®à¯à®•à®µà®°à®¿à®¯à¯ˆà®¤à¯ திரà¯à®¤à¯à®¤à¯</translation>
<translation id="991413375315957741">மோஷன௠அலà¯à®²à®¤à¯ ஒளி செனà¯à®šà®¾à®°à¯à®•à®³à¯</translation>
+<translation id="992110854164447044">மோசடி அபாயஙà¯à®•à®³à®¿à®²à¯ இரà¯à®¨à¯à®¤à¯ பாதà¯à®•à®¾à®•à¯à®• உஙà¯à®•à®³à¯ அசல௠காரà¯à®Ÿà¯ˆ விரà¯à®šà¯à®šà¯à®µà®²à¯ காரà¯à®Ÿà¯ மறைகà¯à®•à®¿à®±à®¤à¯. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">பிஙà¯à®•à¯</translation>
<translation id="992432478773561401">உஙà¯à®•à®³à¯ கமà¯à®ªà¯à®¯à¯‚டà¯à®Ÿà®°à¯ அலà¯à®²à®¤à¯ நெடà¯à®µà¯Šà®°à¯à®•à¯à®•à®¿à®²à¯ "<ph name="SOFTWARE_NAME" />" மெனà¯à®ªà¯Šà®°à¯à®³à¯ சரியாக நிறà¯à®µà®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ. பினà¯à®µà®°à¯à®µà®©à®µà®±à¯à®±à¯ˆ à®®à¯à®¯à®©à¯à®±à¯ பாரà¯à®•à¯à®•à®µà¯à®®à¯:
diff --git a/chromium/components/strings/components_strings_te.xtb b/chromium/components/strings/components_strings_te.xtb
index 0b6192bff66..2ac939df302 100644
--- a/chromium/components/strings/components_strings_te.xtb
+++ b/chromium/components/strings/components_strings_te.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">à°—à±à°°à°¾à°‚à°Ÿà±â€Œà°²à±, à°¸à±à°•à°¾à°²à°°à±â€Œà°·à°¿à°ªà±â€Œà°²à± &amp; ఆరà±à°§à°¿à°• సహాయం</translation>
<translation id="1048785276086539861">మీరౠఅదనపౠగమనికలనౠఎడిటౠచేసినపà±à°ªà±à°¡à±, à°ˆ డాకà±à°¯à±à°®à±†à°‚టౠసింగిలౠపేజీ వీకà±à°·à°£à°•à± తిరిగి వసà±à°¤à±à°‚ది</translation>
<translation id="1050038467049342496">ఇతర యాపà±â€Œà°²à°¨à± మూసివేయండి</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" />‌నౠఉపయోగించే వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°²à°²à±‹ à°ªà±à°°à°¾à°®à°¾à°£à±€à°•à°°à°£à°¦à°¾à°°à± పరికరంతో వెరిఫై చేయాలని మీరౠఎంచà±à°•à±à°¨à±à°¨à°¾à°°à±. à°ˆ à°ªà±à°°à±Šà°µà±ˆà°¡à°°à± మీ పేమెంటౠఆపà±à°·à°¨à±â€Œà°•à± సంబంధించిన సమాచారానà±à°¨à°¿ à°¸à±à°Ÿà±‹à°°à± చేసà±à°•à±Šà°¨à°¿ ఉండవచà±à°šà±, దీనిని మీరౠ<ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;జోడించడానà±à°¨à°¿ à°°à°¦à±à°¦à± చేయి</translation>
<translation id="1056663316309890343">ఫోటో సాఫà±à°Ÿà±â€Œà°µà±‡à°°à±</translation>
<translation id="1056898198331236512">హెచà±à°šà°°à°¿à°•</translation>
@@ -119,10 +120,11 @@
<translation id="1270502636509132238">పికపౠపదà±à°§à°¤à°¿</translation>
<translation id="1281476433249504884">à°¸à±à°Ÿà°¾à°•à°°à± 1</translation>
<translation id="1285320974508926690">à°ˆ సైటà±â€Œà°¨à± à°Žà°ªà±à°ªà°Ÿà°¿à°•à±€ à°…à°¨à±à°µà°¦à°¿à°‚చవదà±à°¦à±</translation>
+<translation id="1288548991597756084">కారà±à°¡à±â€Œà°¨à± à°¸à±à°°à°•à±à°·à°¿à°¤à°‚à°—à°¾ సేవౠచేయండి</translation>
<translation id="1292571435393770077">à°Ÿà±à°°à±‡ 16</translation>
<translation id="1292701964462482250">"మీ à°•à°‚à°ªà±à°¯à±‚à°Ÿà°°à±â€Œà°²à±‹ ఉనà±à°¨ సాఫà±à°Ÿà±â€Œà°µà±‡à°°à± కారణంగా Chrome à°¸à±à°°à°•à±à°·à°¿à°¤à°‚à°—à°¾ వెబà±â€Œà°•à± కనెకà±à°Ÿà± కాలేకపోతోంది" (Windows à°•à°‚à°ªà±à°¯à±‚à°Ÿà°°à±â€Œà°² కోసం మాతà±à°°à°®à±‡)</translation>
<translation id="1294154142200295408">ఆదేశ-పంకà±à°¤à°¿ à°µà±à°¯à°¤à±à°¯à°¾à°¸à°¾à°²à±</translation>
-<translation id="129553762522093515">ఇటీవల మూసివెయà±à°¯à°¬à°¡à°¿à°¨à°µà°¿</translation>
+<translation id="129553762522093515">ఇటీవల మూసివేయబడినవి</translation>
<translation id="1296930489679394997">వికà±à°°à°¯à°¾à°²à±</translation>
<translation id="129863573139666797"><ph name="BEGIN_LINK" />మీ à°•à±à°•à±à°•à±€à°²à°¨à± తీసివేయడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿<ph name="END_LINK" /></translation>
<translation id="1301324364792935241">మీ సెకà±à°¯à±‚à°°à± DNS సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± చెకౠచేయండి</translation>
@@ -224,7 +226,8 @@
<translation id="1507202001669085618">&lt;p&gt;ఆనà±â€Œà°²à±ˆà°¨à±â€Œà°•à± వెళà±à°²à°¡à°‚ కంటే à°®à±à°‚దౠసైనౠఇనౠచేయాలà±à°¸à°¿à°¨ అవసరం ఉనà±à°¨ Wi-Fi పోరà±à°Ÿà°²à±â€Œà°¨à± మీరౠఉపయోగిసà±à°¤à±à°¨à±à°¨à°Ÿà±à°²à°¯à°¿à°¤à±‡ మీకౠఈ à°Žà°°à±à°°à°°à± కనిపిసà±à°¤à±à°‚ది.&lt;/p&gt;
&lt;p&gt;à°Žà°°à±à°°à°°à±â€Œà°¨à± పరిషà±à°•à°°à°¿à°‚చడానికి, మీరౠతెరవాలనà±à°•à±à°‚à°Ÿà±à°¨à±à°¨ పేజీలో &lt;strong&gt;కనెకà±à°Ÿà± చేయి&lt;/strong&gt;ని నొకà±à°•à°‚à°¡à°¿.&lt;/p&gt;</translation>
<translation id="1507780850870535225">à°²à±à°¯à°¾à°‚à°¡à±â€Œà°¸à±à°•à±‡à°ªà± డిజైనà±</translation>
-<translation id="1513706915089223971">à°šà°°à°¿à°¤à±à°° నమోదà±à°² లిసà±à°Ÿà±â€Œ</translation>
+<translation id="1513706915089223971">హిసà±à°Ÿà°°à±€ నమోదà±à°² లిసà±à°Ÿà±â€Œ</translation>
+<translation id="1516097932025103760">ఇది à°Žà°¨à±â€Œà°•à±à°°à°¿à°ªà±à°Ÿà± చేయబడà±à°¤à±à°‚ది, à°¸à±à°°à°•à±à°·à°¿à°¤à°‚à°—à°¾ సేవౠచేయబడà±à°¤à±à°‚ది, అలాగే CVC à°Žà°ªà±à°ªà±à°¡à±‚ à°¸à±à°Ÿà±‹à°°à± చేయబడదà±.</translation>
<translation id="1517433312004943670">ఫోనౠనంబరౠఅవసరం</translation>
<translation id="1519264250979466059">బిలà±à°¡à± తేదీ</translation>
<translation id="1521159554480556801">ఫైబరౠ&amp; వసà±à°¤à±à°° కళలà±</translation>
@@ -274,7 +277,7 @@
<translation id="1634828734222219955">మొతà±à°¤à°‚</translation>
<translation id="163669211644121865">à°Ÿà±à°¯à°¾à°•à±à°¸à± రూపకలà±à°ªà°¨ &amp; à°ªà±à°²à°¾à°¨à°¿à°‚à°—à±</translation>
<translation id="1638780421120290329">కారà±à°¡à±â€Œà°¨à± సేవౠచేయలేకపోయింది</translation>
-<translation id="1639239467298939599">లోడౠఅవà±à°¤à±‹à°‚ది</translation>
+<translation id="1639239467298939599">లోడౠచేసà±à°¤à±‹à°‚ది</translation>
<translation id="1640180200866533862">వినియోగదారౠవిధానాలà±</translation>
<translation id="1640244768702815859"><ph name="BEGIN_LINK" />సైటౠయొకà±à°• హోమà±â€Œà°ªà±‡à°œà±€à°¨à°¿ సందరà±à°¶à°¿à°‚à°šà°¡à°‚<ph name="END_LINK" /> à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="1641976391427233992">à°…à°µà±à°Ÿà±â€Œà°ªà±à°Ÿà±â€Œà°¨à± ఇపà±à°ªà°Ÿà°¿ వరకౠఆలసà±à°¯à°‚ చేయి</translation>
@@ -290,6 +293,7 @@
<translation id="1662550410081243962">పేమెంటౠఆపà±à°·à°¨à±â€Œà°²à°¨à± సేవౠచేసి, ఆటోమేటికà±â€Œà°—à°¾ ఫిలౠచేయండి</translation>
<translation id="1663943134801823270">కారà±à°¡à±â€Œà°²à± మరియౠఅడà±à°°à°¸à±â€Œà°²à± Chrome à°¨à±à°‚à°¡à°¿ పొందినవి. మీరౠ<ph name="BEGIN_LINK" />సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à±<ph name="END_LINK" />లో వాటిని నిరà±à°µà°¹à°¿à°‚చవచà±à°šà±.</translation>
<translation id="1671391448414634642">ఇపà±à°ªà°Ÿà°¿ à°¨à±à°‚à°¡à°¿ <ph name="SOURCE_LANGUAGE" /> భాషలో ఉనà±à°¨ పేజీలౠ<ph name="TARGET_LANGUAGE" /> భాషలోకి à°…à°¨à±à°µà°¦à°¿à°‚చబడతాయి.</translation>
+<translation id="1673886523110456987">ఆఫరà±â€Œà°¨à± వినియోగించà±à°•à±‹à°µà°¡à°¾à°¨à°¿à°•à°¿ <ph name="CARD_DETAIL" /> కారà±à°¡à±â€Œà°¤à±‹ చెకౠఅవà±à°Ÿà± చేయండి</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> à°¨à±à°‚à°¡à°¿ <ph name="TARGET_LANGUAGE" />లోకి</translation>
<translation id="1682696192498422849">పేజీని à°…à°¡à±à°¡à°‚à°—à°¾ తిపà±à°ªà°¿ à°ªà±à°°à°¿à°‚టౠచేయి</translation>
<translation id="168693727862418163">à°ˆ పాలసీ విలà±à°µ దాని à°¸à±à°•à±€à°®à°¾à°•à± à°µà±à°¯à°¤à°¿à°°à±‡à°•à°‚à°—à°¾ వాలిడేటౠచేయడంలో విఫలమైంది, కాబటà±à°Ÿà°¿ విసà±à°®à°°à°¿à°‚చబడà±à°¤à±à°‚ది.</translation>
@@ -308,6 +312,7 @@
<translation id="1717218214683051432">మోషనౠసెనà±à°¸à°¾à°°à±â€Œà°²à±</translation>
<translation id="1717494416764505390">మెయిలà±â€Œà°¬à°¾à°•à±à°¸à± 3</translation>
<translation id="1718029547804390981">డాకà±à°¯à±à°®à±†à°‚à°Ÿà±â€Œ అదనపౠగమనికలనౠజోడించడానికి వీలౠలేకà±à°‚à°¡à°¾ చాలా అధిక పరిమాణంలో ఉంది</translation>
+<translation id="1720941539803966190">à°Ÿà±à°¯à±à°Ÿà±‹à°°à°¿à°¯à°²à±â€Œà°¨à± మూసివేయండి</translation>
<translation id="1721424275792716183">* అవసరమైన ఫీలà±à°¡à±</translation>
<translation id="1727613060316725209">సరà±à°Ÿà°¿à°«à°¿à°•à±†à°Ÿà± చెలà±à°²à±à°¬à°¾à°Ÿà± à°…à°µà±à°¤à±à°‚ది</translation>
<translation id="1727741090716970331">చెలà±à°²à±à°¬à°¾à°Ÿà°¯à±à°¯à±‡ కారà±à°¡à± నంబరà±â€Œà°¨à± జోడించండి</translation>
@@ -424,8 +429,8 @@
<translation id="205212645995975601">BBQ &amp; à°—à±à°°à°¿à°²à±à°²à°¿à°‚à°—à±</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> భాషలో ఉనà±à°¨ పేజీలౠఅనà±à°µà°¦à°¿à°‚చబడవà±.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{à°ˆ à°•à°‚à°Ÿà±à°°à±‹à°²à± ఆనà±â€Œà°²à±‹ ఉనà±à°¨à°ªà±à°ªà±à°¡à±, అలాగే యాకà±à°Ÿà°¿à°µà±â€Œà°—à°¾ ఉనà±à°¨à°ªà±à°ªà±à°¡à±, మీ ఇటీవలి à°¬à±à°°à±Œà°œà°¿à°‚గౠయాకà±à°Ÿà°¿à°µà°¿à°Ÿà±€ ఠపెదà±à°¦ à°µà±à°¯à°•à±à°¤à±à°² à°—à±à°°à±‚పౠలేదా “ఒకే రకమైన యూజరà±â€Œà°² à°—à±à°°à±‚à°ªà±"తో పోలి ఉందని Chrome నిరà±à°£à°¯à°¿à°¸à±à°¤à±à°‚ది. à°…à°¡à±à°µà°°à±à°Ÿà°¯à°¿à°œà°°à±â€Œà°²à± à°ˆ à°—à±à°°à±‚à°ªà±â€Œà°•à± యాడà±â€Œà°²à°¨à± à°Žà°‚à°šà±à°•à±‹à°µà°šà±à°šà±, అలాగే మీ à°¬à±à°°à±Œà°œà°¿à°‚గౠయాకà±à°Ÿà°¿à°µà°¿à°Ÿà±€ à°ˆ పరికరంలో à°ªà±à°°à±ˆà°µà±‡à°Ÿà±â€Œà°—à°¾ ఉంచబడà±à°¤à±à°‚ది. మీ à°—à±à°°à±‚పౠపà±à°°à°¤à°¿ రోజౠఅపà±â€Œà°¡à±‡à°Ÿà± చేయబడà±à°¤à±à°‚ది.}=1{à°ˆ à°•à°‚à°Ÿà±à°°à±‹à°²à± ఆనà±â€Œà°²à±‹ ఉనà±à°¨à°ªà±à°ªà±à°¡à±, అలాగే యాకà±à°Ÿà°¿à°µà±â€Œà°—à°¾ ఉనà±à°¨à°ªà±à°ªà±à°¡à±, మీ ఇటీవలి à°¬à±à°°à±Œà°œà°¿à°‚గౠయాకà±à°Ÿà°¿à°µà°¿à°Ÿà±€ ఠపెదà±à°¦ à°µà±à°¯à°•à±à°¤à±à°² à°—à±à°°à±‚పౠలేదా “ఒకే రకమైన యూజరà±â€Œà°² à°—à±à°°à±‚à°ªà±"తో పోలి ఉందని Chrome నిరà±à°£à°¯à°¿à°¸à±à°¤à±à°‚ది. à°…à°¡à±à°µà°°à±à°Ÿà°¯à°¿à°œà°°à±â€Œà°²à± à°ˆ à°—à±à°°à±‚à°ªà±â€Œà°•à± యాడà±â€Œà°²à°¨à± à°Žà°‚à°šà±à°•à±‹à°µà°šà±à°šà±, అలాగే మీ à°¬à±à°°à±Œà°œà°¿à°‚గౠయాకà±à°Ÿà°¿à°µà°¿à°Ÿà±€ à°ˆ పరికరంలో à°ªà±à°°à±ˆà°µà±‡à°Ÿà±â€Œà°—à°¾ ఉంచబడà±à°¤à±à°‚ది. మీ à°—à±à°°à±‚పౠపà±à°°à°¤à°¿ రోజౠఅపà±â€Œà°¡à±‡à°Ÿà± చేయబడà±à°¤à±à°‚ది.}other{à°ˆ à°•à°‚à°Ÿà±à°°à±‹à°²à± ఆనà±â€Œà°²à±‹ ఉనà±à°¨à°ªà±à°ªà±à°¡à±, అలాగే యాకà±à°Ÿà°¿à°µà±â€Œà°—à°¾ ఉనà±à°¨à°ªà±à°ªà±à°¡à±, మీ ఇటీవలి à°¬à±à°°à±Œà°œà°¿à°‚గౠయాకà±à°Ÿà°¿à°µà°¿à°Ÿà±€ ఠపెదà±à°¦ à°µà±à°¯à°•à±à°¤à±à°² à°—à±à°°à±‚పౠలేదా “ఒకే రకమైన యూజరà±â€Œà°² à°—à±à°°à±‚à°ªà±"తో పోలి ఉందని Chrome నిరà±à°£à°¯à°¿à°¸à±à°¤à±à°‚ది. à°…à°¡à±à°µà°°à±à°Ÿà°¯à°¿à°œà°°à±â€Œà°²à± à°ˆ à°—à±à°°à±‚à°ªà±â€Œà°•à± యాడà±â€Œà°²à°¨à± à°Žà°‚à°šà±à°•à±‹à°µà°šà±à°šà±, అలాగే మీ à°¬à±à°°à±Œà°œà°¿à°‚గౠయాకà±à°Ÿà°¿à°µà°¿à°Ÿà±€ à°ˆ పరికరంలో à°ªà±à°°à±ˆà°µà±‡à°Ÿà±â€Œà°—à°¾ ఉంచబడà±à°¤à±à°‚ది. మీ à°—à±à°°à±‚పౠపà±à°°à°¤à°¿ {NUM_DAYS} రోజà±à°²à°•à± à°…à°ªà±â€Œà°¡à±‡à°Ÿà± చేయబడà±à°¤à±à°‚ది.}}</translation>
-<translation id="2053553514270667976">జిపౠకోడà±</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 సూచన}other{# సూచనలà±}}</translation>
+<translation id="2066915425250589881">తొలగించవలసిందిగా à°°à°¿à°•à±à°µà±†à°¸à±à°Ÿà± చేయవచà±à°šà±</translation>
<translation id="2068528718802935086">పిలà±à°²à°²à± &amp; పసిపిలà±à°²à°²à±</translation>
<translation id="2071156619270205202">వరà±à°šà±à°µà°²à± కారà±à°¡à± నంబరౠకోసం à°ˆ కారà±à°¡à±â€Œà°•à± à°…à°°à±à°¹à°¤ లేదà±.</translation>
<translation id="2071692954027939183">మీరౠసాధారణంగా వాటిని à°…à°¨à±à°®à°¤à°¿à°‚చనందà±à°¨ నోటిఫికేషనà±â€Œà°²à± ఆటోమేటికà±â€Œà°—à°¾ à°¬à±à°²à°¾à°•à± చేయబడà±à°¡à°¾à°¯à°¿</translation>
@@ -437,7 +442,6 @@
<translation id="2088086323192747268">'సింకà±â€Œà°¨à± మేనేజౠచేయి' బటనà±, Chrome సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°²à±‹ మీరౠఠసమాచారానà±à°¨à°¿ సింకౠచేయాలనà±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à±‹ మేనేజౠచేయడానికి 'Enter'నౠనొకà±à°•à°‚à°¡à°¿</translation>
<translation id="2091887806945687916">à°§à±à°µà°¨à°¿</translation>
<translation id="2094505752054353250">డొమైనౠసరిపోలలేదà±</translation>
-<translation id="2096368010154057602">శాఖ</translation>
<translation id="2099652385553570808">ఎడమవైపౠటà±à°°à°¿à°ªà±à°²à± à°¸à±à°Ÿà±‡à°ªà±à°²à±</translation>
<translation id="2101225219012730419">వెరà±à°·à°¨à±:</translation>
<translation id="2102134110707549001">బలమైన పాసà±â€Œà°µà°°à±à°¡à±â€Œà°¨à± సూచించà±â€¦</translation>
@@ -474,7 +478,6 @@
<translation id="2185836064961771414">అమెరికనౠఫà±à°Ÿà±â€Œà°¬à°¾à°²à±</translation>
<translation id="2187317261103489799">à°—à±à°°à±à°¤à°¿à°‚à°šà± (డిఫాలà±à°Ÿà±)</translation>
<translation id="2188375229972301266">దిగà±à°µ భాగంలో అనేక à°°à°‚à°§à±à°°à°¾à°²à±</translation>
-<translation id="2188852899391513400">మీరౠఇపà±à°ªà±à°¡à±‡ ఉపయోగించిన పాసà±â€Œà°µà°°à±à°¡à±, డేటా ఉలà±à°²à°‚ఘనలో కనగొనబడింది. మీ ఖాతాలనౠసà±à°°à°•à±à°·à°¿à°¤à°‚ చేయడానికి, దానిని ఇపà±à°ªà±à°¡à±‡ మారà±à°šà°¿, ఆపై మీరౠసేవౠచేసిన పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à°¨à± చెకౠచేయమని Google పాసà±â€Œà°µà°°à±à°¡à± మేనేజరౠసిఫారà±à°¸à± చేసà±à°¤à±‹à°‚ది.</translation>
<translation id="219906046732893612">గృహ మరమà±à°®à°¤à±à°¤à±à°²à±</translation>
<translation id="2202020181578195191">చెలà±à°²à±à°¬à°¾à°Ÿà± à°…à°¯à±à°¯à±‡ à°—à°¡à±à°µà± à°®à±à°—ింపౠసంవతà±à°¸à°°à°¾à°¨à±à°¨à°¿ నమోదౠచేయండి</translation>
<translation id="22081806969704220">à°Ÿà±à°°à±‡ 3</translation>
@@ -485,6 +488,8 @@
<translation id="2215727959747642672">ఫైలà±â€Œà°¨à± ఎడిటౠచేయడం</translation>
<translation id="2215963164070968490">à°•à±à°•à±à°•à°²à±</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />లోని à°¹à±à°¯à°¾à°•à°°à±â€Œà°²à± మీ పరికరంలో హానికరమైన యాపà±â€Œà°²à°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయవచà±à°šà±. మీ మొబైలౠబిలà±â€Œà°²à±‹ అదృశà±à°¯ ఛారà±à°œà±€à°²à°•à± కారణం కావచà±à°šà± లేదా మీ à°µà±à°¯à°•à±à°¤à°¿à°—à°¤ సమాచారానà±à°¨à°¿ దొంగిలించవచà±à°šà±. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">à°Ÿà±à°¯à±à°Ÿà±‹à°°à°¿à°¯à°²à±â€Œà°¨à± రీసà±à°Ÿà°¾à°°à±à°Ÿà± చేయండి</translation>
+<translation id="2219735899272417925">పరికరానà±à°¨à°¿ రీసెటౠచేయాలà±à°¸à°¿à°¨ అవసరం ఉంది</translation>
<translation id="2224337661447660594">ఇంటరà±à°¨à±†à°Ÿà± లేదà±</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />విశà±à°²à±‡à°·à°£à°² యాపà±â€Œ<ph name="END_LINK" />నౠఉపయోగించి మీ కనెకà±à°·à°¨à±â€Œà°¨à± సరి చేయండి</translation>
<translation id="2239100178324503013">ఇపà±à°ªà±à°¡à±‡ పంపండి</translation>
@@ -582,11 +587,13 @@
<translation id="2512101340618156538">à°…à°¨à±à°®à°¤à°¿à°‚చనివి (ఆటోమేటికౠసెటà±à°Ÿà°¿à°‚à°—à±)</translation>
<translation id="2512413427717747692">Chromeనౠఆటోమేటికౠబà±à°°à±Œà°œà°°à±â€Œà°—à°¾ సెటౠచేసే బటనà±, 'Enter'నౠనొకà±à°•à°¿ iOS సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°²à±‹ Chromeనౠఆటోమేటికౠబà±à°°à±Œà°œà°°à±â€Œà°—à°¾ సెటౠచేయండి</translation>
<translation id="2515629240566999685">మీ à°ªà±à°°à°¾à°‚తంలో సిగà±à°¨à°²à±â€Œà°¨à± చెకౠచేయడం</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" />‌నౠఉపయోగించే వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°²à°²à±‹ Touch IDతో వెరిఫై చేయాలని మీరౠఎంచà±à°•à±à°¨à±à°¨à°¾à°°à±. à°ˆ à°ªà±à°°à±Šà°µà±ˆà°¡à°°à± మీ పేమెంటౠఆపà±à°·à°¨à±â€Œà°•à± సంబంధించిన సమాచారానà±à°¨à°¿ à°¸à±à°Ÿà±‹à°°à± చేసà±à°•à±Šà°¨à°¿ ఉండవచà±à°šà±, దీనిని మీరౠ<ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">à°•à±à°¡à°¿à°µà±ˆà°ªà± దిగà±à°µ భాగంలో à°¸à±à°Ÿà±‡à°ªà±à°²à±</translation>
<translation id="2521736961081452453">ఫారమà±â€Œà°¨à± à°•à±à°°à°¿à°¯à±‡à°Ÿà± చేయండి</translation>
<translation id="2523886232349826891">à°ˆ పరికరంలో మాతà±à°°à°®à±‡ సేవౠచేయబడి ఉంటà±à°‚ది</translation>
<translation id="2524461107774643265">మరింత సమాచారానà±à°¨à°¿ జోడించండి</translation>
<translation id="2529899080962247600">à°ˆ ఫీలà±à°¡à±â€Œà°²à±‹ <ph name="MAX_ITEMS_LIMIT" /> కంటే à°Žà°•à±à°•à±à°µ à°Žà°‚à°Ÿà±à°°à±€à°²à± ఉండకూడదà±. à°…à°¨à±à°¨à°¿ తదà±à°ªà°°à°¿ à°Žà°‚à°Ÿà±à°°à±€à°²à± విసà±à°®à°°à°¿à°‚చబడతాయి.</translation>
+<translation id="253493526287553278">à°ªà±à°°à±‹à°®à±‹ కోడౠవివరాలనౠచూడండి</translation>
<translation id="2535585790302968248">à°ªà±à°°à±ˆà°µà±‡à°Ÿà±â€Œà°—à°¾ à°¬à±à°°à±Œà°œà± చేయడానికి కొతà±à°¤ à°…à°œà±à°žà°¾à°¤ à°Ÿà±à°¯à°¾à°¬à±â€Œà°¨à± తెరవండి</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{మరియౠమరో 1}other{మరియౠమరో #}}</translation>
<translation id="2536110899380797252">à°…à°¡à±à°°à°¸à±â€Œà°¨à± జోడించà±</translation>
@@ -622,7 +629,6 @@
<translation id="259821504105826686">ఫోటోగà±à°°à°¾à°«à°¿à°•à± &amp; డిజిటలౠఆరà±à°Ÿà±à°¸à±</translation>
<translation id="2601150049980261779">శృంగార సినిమాలà±</translation>
<translation id="2604589665489080024">పాపౠమà±à°¯à±‚జికà±</translation>
-<translation id="2609632851001447353">à°µà±à°¯à°¤à±à°¯à°¾à°¸à°¾à°²à±</translation>
<translation id="2610561535971892504">కాపీ చేయడానికి à°•à±à°²à°¿à°•à± చేయండి</translation>
<translation id="2617988307566202237">Chrome à°ˆ కింది సమాచారానà±à°¨à°¿ <ph name="BEGIN_EMPHASIS" />సేవౠచేయదà±<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -650,11 +656,12 @@
<translation id="2666117266261740852">ఇతర à°Ÿà±à°¯à°¾à°¬à±â€Œà°²à± లేదా యాపà±â€Œà°²à°¨à± మూసివేయండి</translation>
<translation id="2672201172023654893">మీ à°¬à±à°°à±Œà°œà°°à± మేనేజౠచేయబడలేదà±.</translation>
<translation id="2673968385134502798">గేమà±â€Œà°²à±</translation>
-<translation id="2674170444375937751">మీ à°šà°°à°¿à°¤à±à°° à°¨à±à°‚à°¡à°¿ à°ˆ పేజీలనౠతొలగించదలిచారా?</translation>
+<translation id="2674170444375937751">మీ హిసà±à°Ÿà°°à±€ à°¨à±à°‚à°¡à°¿ à°ˆ పేజీలనౠతొలగించదలిచారా?</translation>
<translation id="2674804415323431591">సూచనలనౠదాచà±</translation>
<translation id="2676271551327853224">ROC 8K</translation>
<translation id="2677696497921480781">à°ªà±à°Ÿà±à°Ÿà°¿à°¨à°°à±‹à°œà±à°²à± &amp; నామకరణం చేసిన రోజà±à°²à±</translation>
<translation id="2677748264148917807">నిషà±à°•à±à°°à°®à°¿à°‚à°šà°‚à°¡à°¿</translation>
+<translation id="2679714844901977852">à°¸à±à°°à°•à±à°·à°¿à°¤à°®à±ˆà°¨, వేగవంతమైన చెకౠఅవà±à°Ÿà±â€Œà°² కోసం మీ కారà±à°¡à±, అలాగే బిలà±à°²à°¿à°‚గౠసమాచారానà±à°¨à°¿ మీ Google ఖాతా <ph name="USER_EMAIL" />‌లో సేవౠచేయండి</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">సరిపోయేలా అమరà±à°šà±</translation>
<translation id="2688969097326701645">à°…à°µà±à°¨à±, కొనసాగించà±</translation>
@@ -703,7 +710,6 @@
<translation id="2854764410992194509">ఇంటరà±à°¨à±†à°Ÿà± సరà±à°µà±€à°¸à± à°ªà±à°°à±Šà°µà±ˆà°¡à°°à±â€Œà°²à± (ISPà°²à±)</translation>
<translation id="2856444702002559011">à°¹à±à°¯à°¾à°•à°°à±â€Œà°²à± <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à°¨à±à°‚à°¡à°¿ మీ సమాచారానà±à°¨à°¿ దొంగిలించడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°¸à±à°¤à±‚ ఉండవచà±à°šà± (ఉదాహరణకà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, మెసేజà±â€Œà°²à± లేదా à°•à±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±). <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">à°ˆ సైటౠఅనà±à°šà°¿à°¤à°®à±ˆà°¨ లేదా తపà±à°ªà±à°¦à°¾à°°à°¿ పటà±à°Ÿà°¿à°‚చే à°ªà±à°°à°•à°Ÿà°¨à°²à°¨à± చూపà±à°¤à±à°‚ది.</translation>
-<translation id="286512204874376891">మోసం జరిగే అవకాశమà±à°¨à±à°¨ సందరà±à°­à°‚లో మిమà±à°®à°²à±à°¨à°¿ à°°à°•à±à°·à°¿à°‚చడంలో సహాయపడటానికి వరà±à°šà±à°µà°²à± కారà±à°¡à± మీ అసలౠకారà±à°¡à±â€Œà°²à°¾ à°µà±à°¯à°µà°¹à°°à°¿à°¸à±à°¤à±à°‚ది. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">à°¸à±à°¨à±‡à°¹à°ªà±‚à°°à±à°µà°•à°®à±ˆà°¨à°¦à°¿</translation>
<translation id="28761159517501904">సినిమాలà±</translation>
<translation id="2876489322757410363">వెలà±à°ªà°²à°¿ à°…à°ªà±à°²à°¿à°•à±‡à°·à°¨à± à°¦à±à°µà°¾à°°à°¾ పేమెంటౠచేయడానికి à°…à°œà±à°žà°¾à°¤ మోడౠనà±à°‚à°¡à°¿ నిషà±à°•à±à°°à°®à°¿à°¸à±à°¤à±‹à°‚ది. కొనసాగించాలా?</translation>
@@ -740,7 +746,7 @@
<translation id="2951588413176968965">నా మెయిలà±â€Œà°¬à°¾à°•à±à°¸à±</translation>
<translation id="2952820037279740115">à°…à°œà±à°žà°¾à°¤ విండోలనà±à°¨à°¿à°‚టినీ మూసివేయండి</translation>
<translation id="295526156371527179">హెచà±à°šà°°à°¿à°•: à°ˆ విధానం à°’à°• నిఘంటà±à°µà± కానందà±à°¨, విధానంలో పేరà±à°•à±Šà°¨à±à°¨à°Ÿà±à°²à± నిఘంటà±à°µà± లాగా విలీనం చేయబడలేదà±.</translation>
-<translation id="2955913368246107853">à°•à°¨à±à°—ొనౠపటà±à°Ÿà±€à°¨à°¿ మూసివేయి</translation>
+<translation id="2955913368246107853">à°•à°¨à±à°—ొనౠబారà±â€Œà°¨à± మూసివేయి</translation>
<translation id="2958544468932521864">à°•à±à°°à°¿à°•à±†à°Ÿà±</translation>
<translation id="2959113999220720579">à°®à±à°–à°‚ &amp; శరీర సంరకà±à°·à°£</translation>
<translation id="2968103128155246731">à°µà±à°¯à°¾à°¨à±â€Œà°²à± &amp; మినీ à°µà±à°¯à°¾à°¨à±â€Œà°²à±</translation>
@@ -804,7 +810,6 @@
<translation id="3158539265159265653">à°¡à°¿à°¸à±à°•à±</translation>
<translation id="3162559335345991374">మీరౠఉపయోగిసà±à°¤à±à°¨à±à°¨ Wi-Fià°•à°¿ మీరౠదాని లాగినౠపేజీని సందరà±à°¶à°¿à°‚à°šà°¡à°‚ అవసరం.</translation>
<translation id="3169472444629675720">à°•à°¨à±à°—ొనà±</translation>
-<translation id="3174168572213147020">దీవి</translation>
<translation id="3176929007561373547">à°ªà±à°°à°¾à°•à±à°¸à±€ సరà±à°µà°°à± పని చేసà±à°¤à±à°¨à±à°¨à°Ÿà±à°²à± నిరà±à°§à°¾à°°à°¿à°‚à°šà±à°•à±‹à°µà°¡à°¾à°¨à°¿à°•à°¿ మీ à°ªà±à°°à°¾à°•à±à°¸à±€ సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± చెకౠచేయండి లేదా
మీ నెటà±â€Œà°µà°°à±à°•à± నిరà±à°µà°¾à°¹à°•à±à°¡à°¿à°¨à°¿ సంపà±à°°à°¦à°¿à°‚à°šà°‚à°¡à°¿. మీరౠపà±à°°à°¾à°•à±à°¸à±€ సరà±à°µà°°à±â€Œà°¨à±‡ ఉపయోగిసà±à°¤à±à°¨à±à°¨à°Ÿà±à°²à± మీకà±
నమà±à°®à°•à°‚à°—à°¾ లేకà±à°‚టే:
@@ -883,9 +888,6 @@
<translation id="3369192424181595722">గడియారం à°Žà°°à±à°°à°°à±</translation>
<translation id="3369459162151165748">వాహన భాగాలౠ&amp; ఉపకరణాలà±</translation>
<translation id="3371064404604898522">Chromeనౠఆటోమేటికౠబà±à°°à±Œà°œà°°à±â€Œà°—à°¾ సెటౠచేయండి</translation>
-<translation id="3371076217486966826"><ph name="URL" />కౠకింది వాటి కోసం à°…à°¨à±à°®à°¤à°¿ అవసరం:
- • మీ పరిసరాల 3D à°®à±à°¯à°¾à°ªà±â€Œà°¨à± à°•à±à°°à°¿à°¯à±‡à°Ÿà± చేయడానికి, కెమెరా à°¸à±à°¥à°¾à°¨à°¾à°¨à±à°¨à°¿ à°Ÿà±à°°à°¾à°•à± చేయడానికి
- • మీ కెమెరానౠఉపయోగించడానికి</translation>
<translation id="337363190475750230">కేటాయింపౠతీసివేయబడింది</translation>
<translation id="3375754925484257129">Chrome à°­à°¦à±à°°à°¤à°¾ తనిఖీని రనౠచేయండి</translation>
<translation id="3377144306166885718">సరà±à°µà°°à± వాడà±à°•à°²à±‹ లేని TLS వెరà±à°·à°¨à±â€Œà°¨à± ఉపయోగించింది.</translation>
@@ -902,6 +904,7 @@
<translation id="3399952811970034796">డెలివరీ à°…à°¡à±à°°à°¸à±â€Œ</translation>
<translation id="3402261774528610252">à°ˆ సైటà±â€Œà°¨à± లోడౠచేయడానికి ఉపయోగించిన కనెకà±à°·à°¨à± TLS 1.0 లేదా TLS 1.1నౠఉపయోగించింది, ఇవి విసà±à°®à°°à°¿à°‚చబడà±à°¡à°¾à°¯à°¿, భవిషà±à°¯à°¤à±à°¤à±à°²à±‹ నిలిపివేయబడతాయి. à°’à°• సారి నిలిపివేయబడితే, యూజరà±â€Œà°²à± à°ˆ సైటà±â€Œà°¨à± లోడౠచేయకà±à°‚à°¡à°¾ నివారించబడతారà±. సరà±à°µà°°à± TLS 1.2 లేదా ఆపై వెరà±à°·à°¨à±â€Œà°²à°¨à± ఎనేబà±à°²à± చేయాలి.</translation>
<translation id="3405664148539009465">ఫాంటà±â€Œà°²à°¨à± à°…à°¨à±à°•à±‚లంగా మారà±à°šà°‚à°¡à°¿</translation>
+<translation id="3407789382767355356">థరà±à°¡à±-పారà±à°Ÿà±€ సైనౠఇనà±</translation>
<translation id="3409896703495473338">à°­à°¦à±à°°à°¤à°¾ సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± మేనేజౠచేయండి</translation>
<translation id="3414952576877147120">పరిమాణం:</translation>
<translation id="3417660076059365994">మీరౠఅపà±â€Œà°²à±‹à°¡à± లేదా జోడించిన ఫైలà±à°¸à± Google à°•à±à°²à±Œà°¡à± లేదా థరà±à°¡à± పారà±à°Ÿà±€à°²à°•à± విశà±à°²à±‡à°·à°£ కోసం పంపబడతాయి. ఉదాహరణకà±, వాటిని à°¸à±à°¨à±à°¨à°¿à°¤à°®à±†à±–à°¨ à°µà±à°¯à°•à±à°¤à°¿à°—à°¤ సమాచారం లేదా మాలà±â€Œà°µà±‡à°°à± కోసం à°¸à±à°•à°¾à°¨à± చేయబడవచà±à°šà±.</translation>
@@ -934,6 +937,7 @@
<translation id="3477679029130949506">సినిమాల లిసà±à°Ÿà±â€Œà°²à± &amp; థియేటరà±â€Œà°²à±‹ à°ªà±à°°à°¦à°°à±à°¶à°¿à°‚చే సమయాలà±</translation>
<translation id="3479552764303398839">ఇపà±à°ªà±à°¡à± కాదà±</translation>
<translation id="3484560055331845446">మీరౠమీ Google ఖాతాకౠయాకà±à°¸à±†à°¸à±â€Œà°¨à°¿ కోలà±à°ªà±‹à°µà°šà±à°šà±. మీరౠఇపà±à°ªà±à°¡à±‡ మీ పాసà±â€Œà°µà°°à±à°¡à±â€Œà°¨à°¿ మారà±à°šà°¾à°²à±à°¸à°¿à°‚దిగా Chrome సిఫారà±à°¸à± చేసà±à°¤à±‹à°‚ది. మీరౠసైనౠఇనౠచేయాలà±à°¸à°¿ ఉంటà±à°‚ది.</translation>
+<translation id="3484861421501147767">రిమైండరà±: సేవౠచేసిన à°ªà±à°°à±‹à°®à±‹ కోడౠఅందà±à°¬à°¾à°Ÿà±à°²à±‹ ఉంది</translation>
<translation id="3487845404393360112">à°Ÿà±à°°à±‡ 4</translation>
<translation id="3495081129428749620">పేజీలో à°•à°¨à±à°—ొనà±
<ph name="PAGE_TITLE" /></translation>
@@ -976,7 +980,7 @@
<translation id="3614934205542186002"><ph name="RUN_CHROME_SAFETY_CHECK_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°²à±‹ à°­à°¦à±à°°à°¤à°¾ తనిఖీని రనౠచేయడానికి 'Tab'నౠనొకà±à°•à°¿ ఆపై 'Enter'నౠనొకà±à°•à°‚à°¡à°¿</translation>
<translation id="3615877443314183785">చెలà±à°²à±à°¬à°¾à°Ÿà± à°…à°¯à±à°¯à±‡ à°—à°¡à±à°µà± à°®à±à°—ింపౠతేదీని నమోదౠచేయండి</translation>
<translation id="36224234498066874">à°¬à±à°°à±Œà°œà°¿à°‚గౠడేటానౠకà±à°²à°¿à°¯à°°à± చేయండి...</translation>
-<translation id="362276910939193118">పూరà±à°¤à°¿ à°šà°°à°¿à°¤à±à°°à°¨à± చూపించà±</translation>
+<translation id="362276910939193118">పూరà±à°¤à°¿ హిసà±à°Ÿà°°à±€à°¨à°¿ చూపించà±</translation>
<translation id="3630155396527302611">ఇపà±à°ªà°Ÿà°¿à°•à±‡ ఇది నెటà±â€Œà°µà°°à±à°•à±â€Œà°¨à± యాకà±à°¸à±†à°¸à± చేయడానికి à°…à°¨à±à°®à°¤à°¿à°‚చబడిన à°ªà±à°°à±‹à°—à±à°°à°¾à°®à± లాగా లిసà±à°Ÿà±â€Œ చేయబడి ఉంటే,
దీనà±à°¨à°¿ లిసà±à°Ÿà±â€Œ à°¨à±à°‚à°¡à°¿ తీసివేసి, ఆపై మళà±à°²à±€ జోడించి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="3630699740441428070">à°ˆ పరికరానికి సంబంధించిన à°…à°¡à±à°®à°¿à°¨à°¿à°¸à±à°Ÿà±à°°à±‡à°Ÿâ€Œà°°à±â€Œà°²à± మీ నెటà±â€Œà°µà°°à±à°•à± కనెకà±à°·à°¨à±â€Œà°¨à± కానà±à°«à°¿à°—రౠచేశారà±, ఇది మీరౠసందరà±à°¶à°¿à°‚చే వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°²à°¤à±‹ సహా మీ నెటà±â€Œà°µà°°à±à°•à± à°Ÿà±à°°à°¾à°«à°¿à°•à±â€Œà°¨à± చూడటానికి వారిని à°…à°¨à±à°®à°¤à°¿à°‚చవచà±à°šà±.</translation>
@@ -1058,6 +1062,7 @@
<translation id="3810973564298564668">నిరà±à°µà°¹à°¿à°‚à°šà±</translation>
<translation id="3816482573645936981">విలà±à°µ (అధిగమించబడింది)</translation>
<translation id="382518646247711829">మీరౠపà±à°°à°¾à°•à±à°¸à±€ సరà±à°µà°°à±â€Œà°¨à± ఉపయోగిసà±à°¤à±‡...</translation>
+<translation id="3826050100957962900">థరà±à°¡à±-పారà±à°Ÿà±€ సైనౠఇనà±</translation>
<translation id="3827112369919217609">à°…à°¬à±à°¸à°²à±à°¯à±‚à°Ÿà±</translation>
<translation id="3827666161959873541">à°•à±à°Ÿà±à°‚à°¬ తరహా సినిమాలà±</translation>
<translation id="3828924085048779000">ఖాళీ రహసà±à°¯ పదబంధం à°…à°¨à±à°®à°¤à°¿à°‚చబడదà±.</translation>
@@ -1070,9 +1075,9 @@
<translation id="3858027520442213535">తేదీని, సమయానà±à°¨à°¿ à°…à°ªà±â€Œà°¡à±‡à°Ÿà± చేయి</translation>
<translation id="3858860766373142691">పేరà±</translation>
<translation id="3872834068356954457">సైనà±à°¸à±</translation>
+<translation id="3875783148670536197">ఎలా చేయాలో నాకౠచూపించà±</translation>
<translation id="3881478300875776315">కొనà±à°¨à°¿ వరà±à°¸à°²à°¨à± మాతà±à°°à°®à±‡ చూపించà±</translation>
<translation id="3884278016824448484">వైరà±à°§à±à°¯à°®à±ˆà°¨ పరికరం à°à°¡à±†à°‚టిఫైయరà±</translation>
-<translation id="3885155851504623709">పారిషà±</translation>
<translation id="388632593194507180">పరà±à°¯à°µà±‡à°•à±à°·à°£ à°—à±à°°à±à°¤à°¿à°‚చబడింది</translation>
<translation id="3886948180919384617">à°¸à±à°Ÿà°¾à°•à°°à± 3</translation>
<translation id="3890664840433101773">ఈమెయిలà±â€Œà°¨à± జోడించండి</translation>
@@ -1102,9 +1107,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> à°¬à±à°²à°¾à°•à± చేయబడింది</translation>
<translation id="3978338123949022456">సెరà±à°šà± మోడà±, <ph name="KEYWORD_SUFFIX" />‌తో సెరà±à°šà± చేయడానికి à°•à±à°µà±†à°°à±€à°¨à°¿ టైపౠచేసి, 'Enter'నౠనొకà±à°•à°‚à°¡à°¿</translation>
<translation id="398470910934384994">పకà±à°·à±à°²à±</translation>
+<translation id="3985750352229496475">à°…à°¡à±à°°à°¸à±â€Œà°²à°¨à± మేనేజౠచేయండి...</translation>
<translation id="3986705137476756801">à°ªà±à°°à°¸à±à°¤à±à°¤à°¾à°¨à°¿à°•à°¿ లైవౠకà±à°¯à°¾à°ªà±à°·à°¨à±â€Œà°¨à± ఆఫౠచేయి</translation>
<translation id="3987940399970879459">1 MB కంటే తకà±à°•à±à°µ</translation>
<translation id="3990250421422698716">జోగౠఆఫà±â€Œà°¸à±†à°Ÿà±</translation>
+<translation id="3992684624889376114">à°ˆ పేజీ à°—à±à°°à°¿à°‚à°šà°¿</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> సైటà±, తనకౠచేసే à°…à°¨à±à°¨à°¿ à°°à°¿à°•à±à°µà±†à°¸à±à°Ÿà±â€Œà°²à°•à± ఆరిజినౠపాలసీని
వరà±à°¤à°¿à°‚పజేయమని à°…à°­à±à°¯à°°à±à°¥à°¿à°‚చింది. కానీ, à°ªà±à°°à°¸à±à°¤à±à°¤à°‚ à°ˆ పాలసీని వరà±à°¤à°¿à°‚పజేయడం సాధà±à°¯à°ªà°¡à°¦à±.</translation>
<translation id="4006465311664329701">Google Payనౠఉపయోగిసà±à°¤à±à°¨à±à°¨ పేమెంటౠఆపà±à°·à°¨à±â€Œà°²à±, ఆఫరà±â€Œà°²à±, à°…à°¡à±à°°à°¸à±â€Œà°²à±</translation>
@@ -1229,6 +1236,7 @@
<translation id="4305666528087210886">మీ ఫైలà±â€Œà°¨à± యాకà±à°¸à±†à°¸à± చేయడం సాధà±à°¯à°ªà°¡à°²à±‡à°¦à±</translation>
<translation id="4306529830550717874">à°…à°¡à±à°°à°¸à±â€Œà°¨à± సేవౠచేయాలా?</translation>
<translation id="4306812610847412719">à°•à±à°²à°¿à°ªà±â€Œà°¬à±‹à°°à±à°¡à±</translation>
+<translation id="4310070645992025887">మీ à°¬à±à°°à±Œà°œà°¿à°‚గౠజరà±à°¨à±€à°²à°¨à± సెరà±à°šà± చేయండి</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">à°¬à±à°²à°¾à°•à± చేయండి (డిఫాలà±à°Ÿà±)</translation>
<translation id="4314815835985389558">సింకà±â€Œà°¨à± నిరà±à°µà°¹à°¿à°‚à°šà°‚à°¡à°¿</translation>
@@ -1279,6 +1287,7 @@
<translation id="4435702339979719576">పోసà±à°Ÿà±â€Œà°•à°¾à°°à±à°¡à±)</translation>
<translation id="443673843213245140">à°ªà±à°°à°¾à°•à±à°¸à±€à°¨à°¿ ఉపయోగించడం ఆపివేయబడింది కానీ à°¸à±à°ªà°·à±à°Ÿà°®à±ˆà°¨ à°ªà±à°°à°¾à°•à±à°¸à±€ కానà±à°«à°¿à°—రేషనౠపేరà±à°•à±Šà°¨à°¬à°¡à°¿à°‚ది.</translation>
<translation id="4441832193888514600">à°•à±à°²à±Œà°¡à± యూజరౠపాలసీ à°¦à±à°µà°¾à°°à°¾ మాతà±à°°à°®à±‡ పాలసీని సెటౠచేయాలి కాబటà±à°Ÿà°¿ ఇది విసà±à°®à°°à°¿à°‚చబడింది.</translation>
+<translation id="4442470707340296952">Chrome à°Ÿà±à°¯à°¾à°¬à±â€Œà°²à±</translation>
<translation id="4450893287417543264">మళà±à°²à±€ చూపవదà±à°¦à±</translation>
<translation id="4451135742916150903">HID పరికరాలకౠకనెకà±à°Ÿà± చేయడానికి సైటౠఅనà±à°®à°¤à°¿ అడగవచà±à°šà±</translation>
<translation id="4452328064229197696">మీరౠఇపà±à°ªà±à°¡à±‡ ఉపయోగించిన పాసà±â€Œà°µà°°à±à°¡à±, డేటా ఉలà±à°²à°‚ఘనలో కనగొనబడింది. మీ ఖాతాలనౠసà±à°°à°•à±à°·à°¿à°¤à°‚ చేయడానికి, మీరౠసేవౠచేసిన పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à°¨à± చెకౠచేయమని Google పాసà±â€Œà°µà°°à±à°¡à± మేనేజరౠసిఫారà±à°¸à± చేసà±à°¤à±‹à°‚ది.</translation>
@@ -1417,6 +1426,7 @@
<translation id="483241715238664915">హెచà±à°šà°°à°¿à°•à°²à°¨à± ఆనౠచేయండి</translation>
<translation id="4834250788637067901">Google Payనౠఉపయోగిసà±à°¤à±à°¨à±à°¨ పేమెంటౠఆపà±à°·à°¨à±â€Œà°²à±, ఆఫరà±â€Œà°²à±, à°…à°¡à±à°°à°¸à±â€Œà°²à±</translation>
<translation id="4838327282952368871">à°¸à±à°µà°ªà±à°¨à°‚ లాంటిది</translation>
+<translation id="4839087176073128681">తరà±à°µà°¾à°¤à°¿à°¸à°¾à°°à°¿ పేమెంటà±â€Œà°¨à± వేగంగా చేయండి, అలాగే Googleకౠచెందిన à°…à°¤à±à°¯à°‚à°¤ à°…à°§à±à°¨à°¾à°¤à°¨à°®à±ˆà°¨ సెకà±à°¯à±‚రిటీతో మీ కారà±à°¡à±â€Œà°¨à± సంరకà±à°·à°¿à°‚à°šà±à°•à±‹à°‚à°¡à°¿.</translation>
<translation id="4840250757394056958">మీ Chrome హిసà±à°Ÿà°°à±€à°¨à°¿ చూడండి</translation>
<translation id="484462545196658690">ఆటో</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" />, మరి కొందరి à°¨à±à°‚à°¡à°¿ à°¡à°¿à°¸à±à°•à±Œà°‚టౠపొందండి</translation>
@@ -1433,7 +1443,7 @@
<translation id="4877083676943085827">{COUNT,plural, =0{à°à°µà±€ లేవà±}=1{<ph name="EXAMPLE_DOMAIN_1" />}=2{<ph name="EXAMPLE_DOMAIN_1" />, <ph name="EXAMPLE_DOMAIN_2" />}other{<ph name="EXAMPLE_DOMAIN_1" />, <ph name="EXAMPLE_DOMAIN_2" />, <ph name="AND_MORE" />}}</translation>
<translation id="4877422487531841831"><ph name="TEXT" /> శోధన</translation>
<translation id="4879491255372875719">ఆటోమేటికౠ(డిఫాలà±à°Ÿà±)</translation>
-<translation id="4880827082731008257">శోధన à°šà°°à°¿à°¤à±à°°</translation>
+<translation id="4880827082731008257">హిసà±à°Ÿà°°à±€à°²à±‹ సెరà±à°šà± చేయండి</translation>
<translation id="4881695831933465202">తెరà±à°µà±</translation>
<translation id="4885256590493466218">చెకà±à°…à°µà±à°Ÿà±â€Œà°²à±‹ <ph name="CARD_DETAIL" />తో పేమెంటౠచేయండి</translation>
<translation id="4888600795924685526">విదేశీ భాషా à°…à°§à±à°¯à°¯à°¨à°‚</translation>
@@ -1479,6 +1489,7 @@
<translation id="5011561501798487822">à°—à±à°°à±à°¤à°¿à°‚చబడిన భాష</translation>
<translation id="5015510746216210676">మెషీనౠపేరà±:</translation>
<translation id="5017554619425969104">మీరౠకాపీ చేసిన వచనం</translation>
+<translation id="5017828934289857214">నాకౠతరà±à°µà°¾à°¤ à°—à±à°°à±à°¤à± చేయి</translation>
<translation id="5018422839182700155">à°ˆ పేజీని తెరవడం సాధà±à°¯à°ªà°¡à°¦à±</translation>
<translation id="5019198164206649151">à°¬à±à°¯à°¾à°•à°¿à°‚గౠనిలà±à°µ చెలà±à°²à°¨à°¿ à°¸à±à°¥à°¿à°¤à°¿à°²à±‹ ఉంది</translation>
<translation id="5020776957610079374">à°ªà±à°°à°ªà°‚à°šà°µà±à°¯à°¾à°ªà±à°¤à°‚గాా వినదగిన à°®à±à°¯à±‚జికà±</translation>
@@ -1498,6 +1509,7 @@
<translation id="5051305769747448211">లైవౠకామెడీ</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{'సమీప షేరింగà±'నౠఉపయోగించి à°ˆ ఫైలà±â€Œà°¨à± పంపడానికి, మీ పరికరంలో à°¸à±à°ªà±‡à°¸à± (<ph name="DISK_SPACE_SIZE" />)నౠఖాళీ చేయండి}other{'సమీప షేరింగà±'నౠఉపయోగించి à°ˆ ఫైళà±à°³à°¨à± పంపడానికి, మీ పరికరంలో à°¸à±à°ªà±‡à°¸à± (<ph name="DISK_SPACE_SIZE" />)నౠఖాళీ చేయండి}}</translation>
<translation id="5056549851600133418">మీ కోసం కథనాలà±</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" />‌నౠఉపయోగించే వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°²à°²à±‹ Windows Helloతో వెరిఫై చేయాలని మీరౠఎంచà±à°•à±à°¨à±à°¨à°¾à°°à±. à°ˆ à°ªà±à°°à±Šà°µà±ˆà°¡à°°à± మీ పేమెంటౠఆపà±à°·à°¨à±â€Œà°•à± సంబంధించిన సమాచారానà±à°¨à°¿ à°¸à±à°Ÿà±‹à°°à± చేసà±à°•à±Šà°¨à°¿ ఉండవచà±à°šà±, దీనిని మీరౠ<ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">మీరౠ<ph name="LOOKALIKE_DOMAIN" /> à°—à±à°°à°¿à°‚à°šà°¿ అడిగారా?</translation>
<translation id="5066056036849835175">à°ªà±à°°à°¿à°‚టింగౠహిసà±à°Ÿà°°à±€</translation>
<translation id="5068234115460527047">à°­à°¦à±à°°à°¤à°¾ నిధà±à°²à±</translation>
@@ -1511,10 +1523,8 @@
<translation id="5087286274860437796">à°ªà±à°°à°¸à±à°¤à±à°¤à°‚ సరà±à°µà°°à± à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ చెలà±à°²à°¦à±.</translation>
<translation id="5087580092889165836">కారà±à°¡à±â€Œà°¨à± జోడించà±</translation>
<translation id="5088142053160410913">ఆపరేటరà±â€Œà°•à± మెసేజà±â€Œ పంపà±</translation>
-<translation id="5089810972385038852">రాషà±à°Ÿà±à°°à°‚</translation>
<translation id="5093232627742069661">Z-ఫోలà±à°¡à±</translation>
<translation id="5094747076828555589">à°ˆ సరà±à°µà°°à± <ph name="DOMAIN" /> అని నిరూపించà±à°•à±‹à°²à±‡à°•à°ªà±‹à°¯à°¿à°‚ది; దీని à°­à°¦à±à°°à°¤à°¾ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à±à°¨à°¿ Chromium విశà±à°µà°¸à°¿à°‚చలేదà±. ఇది తపà±à°ªà±à°—à°¾ కానà±à°«à°¿à°—రౠచేయడం వలన లేదా దాడిచేసే à°µà±à°¯à°•à±à°¤à°¿ మీ కనెకà±à°·à°¨à±â€Œà°•à± అంతరాయం కలిగించడం వలన జరిగి ఉండవచà±à°šà±.</translation>
-<translation id="5095208057601539847">à°ªà±à°°à°¾à°µà°¿à°¨à±à°¸à±</translation>
<translation id="5097099694988056070">CPU/RAM వినియోగం లాంటి పరికర గణాంకాలà±</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">సైటౠసà±à°°à°•à±à°·à°¿à°¤à°®à±ˆà°¨à°¦à°¿ కాదà±</translation>
@@ -1552,6 +1562,7 @@
<translation id="5171045022955879922">URLనౠవెతకండి లేదా టైపౠచేయండి</translation>
<translation id="5171689220826475070">à°«à±à°¯à°¾à°¨à±â€Œà°«à±‹à°²à±à°¡à±-యూరోపియనà±</translation>
<translation id="5172758083709347301">మెషీనà±</translation>
+<translation id="5177076414499237632">à°ˆ పేజీ సోరà±à°¸à± &amp; టాపికౠగà±à°°à°¿à°‚à°šà°¿ తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" />లో లేదా? à°ˆ à°Žà°°à±à°°à°°à±â€Œà°¨à± రిపోరà±à°Ÿà± చేయండి</translation>
<translation id="518639307526414276">పెంపà±à°¡à± జంతà±à°µà±à°² ఆహారం &amp; సంరకà±à°·à°£ వసà±à°¤à±à°µà±à°²à±</translation>
<translation id="5190835502935405962">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°² బారà±</translation>
@@ -1712,6 +1723,7 @@
<translation id="5624120631404540903">పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à°¨à± నిరà±à°µà°¹à°¿à°‚à°šà°‚à°¡à°¿</translation>
<translation id="5629630648637658800">విధాన సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± లోడౠచేయడంలో విఫలమైంది</translation>
<translation id="5631439013527180824">చెలà±à°²à°¨à°¿ పరికర నిరà±à°µà°¹à°£ టోకెనà±</translation>
+<translation id="5632485077360054581">ఎలా చేయాలో నాకౠచూపించà±</translation>
<translation id="5633066919399395251"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> లో à°¹à±à°¯à°¾à°•à°°à±â€Œà°²à± మీ సమాచారానà±à°¨à°¿ (ఉదాహరణకà±, ఫోటోలà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, మెసేజà±â€Œà°²à± మరియౠకà±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±) దొంగిలించగల లేదా తొలగించగల హానికరమైన à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± మీ à°•à°‚à°ªà±à°¯à±‚à°Ÿà°°à±â€Œà°²à±‹ ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చవచà±à°šà±.<ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">మోసపూరిత కంటెంటౠబà±à°²à°¾à°•à± చేయబడింది.</translation>
<translation id="5633259641094592098">à°•à°²à±à°Ÿà± &amp; ఇండీ సినిమాలà±</translation>
@@ -1817,7 +1829,7 @@
<translation id="5955063559762970069">హోటళà±à°²à± &amp; వసతà±à°²à±</translation>
<translation id="5963413905009737549">విభాగం</translation>
<translation id="5967592137238574583">సంపà±à°°à°¦à°¿à°‚పౠసమాచారానà±à°¨à°¿ ఎడిటౠచేయండి</translation>
-<translation id="5967867314010545767">à°šà°°à°¿à°¤à±à°° à°¨à±à°‚à°¡à°¿ తీసివేయండి</translation>
+<translation id="5967867314010545767">హిసà±à°Ÿà°°à±€ à°¨à±à°‚à°¡à°¿ తీసివేయండి</translation>
<translation id="5968793460449681917">à°ªà±à°°à°¤à°¿ సందరà±à°¶à°¨à°²à±‹</translation>
<translation id="5974052231147553524">సికà±à°¸à±à°¤à± రోలà±</translation>
<translation id="5975083100439434680">దూరంగా జూమౠచేయి</translation>
@@ -1829,6 +1841,7 @@
<translation id="5989320800837274978">à°¸à±à°¥à°¿à°° à°ªà±à°°à°¾à°•à±à°¸à±€ సరà±à°µà°°à±â€Œà°²à± లేదా à°’à°• .pac à°¸à±à°•à±à°°à°¿à°ªà±à°Ÿà± URL పేరà±à°•à±Šà°¨à°¬à°¡à°²à±‡à°¦à±.</translation>
<translation id="5992691462791905444">ఇంజినీరింగౠ'Z' ఫోలà±à°¡à±</translation>
<translation id="5995727681868049093">మీ Google ఖాతాలో మీ సమాచారం, గోపà±à°¯à°¤, ఇంకా à°­à°¦à±à°°à°¤à°¨à± మేనేజౠచేయండి</translation>
+<translation id="5997247540087773573">మీరౠఇపà±à°ªà±à°¡à±‡ ఉపయోగించిన పాసà±â€Œà°µà°°à±à°¡à±, డేటా ఉలà±à°²à°‚ఘనలో కనగొనబడింది. మీ ఖాతాలనౠసà±à°°à°•à±à°·à°¿à°¤à°‚ చేయడానికి, దానిని ఇపà±à°ªà±à°¡à±‡ మారà±à°šà°¿, మీరౠసేవౠచేసిన పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à°¨à± చెకౠచేయమని Google పాసà±â€Œà°µà°°à±à°¡à± మేనేజరౠసిఫారà±à°¸à± చేసà±à°¤à±‹à°‚ది.</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' అనే దానికి <ph name="RESULT_COUNT" /> ఫలితాలౠలభించాయి</translation>
<translation id="6006484371116297560">à°•à±à°²à°¾à°¸à°¿à°•à±</translation>
<translation id="6008122969617370890">N-à°¨à±à°‚à°¡à°¿-1 వరకౠఉనà±à°¨ à°•à±à°°à°®à°‚</translation>
@@ -1924,7 +1937,6 @@
<translation id="627746635834430766">తరà±à°µà°¾à°¤à°¿à°¸à°¾à°°à°¿ మరింత వేగంగా పేమెంటౠచేయడానికి, మీ కారà±à°¡à±, బిలà±à°²à°¿à°‚à°—à± à°…à°¡à±à°°à°¸à±â€Œà°¨à± మీ Google ఖాతాకౠసేవౠచేయండి.</translation>
<translation id="6279183038361895380">మీ à°•à°°à±à°¸à°°à±â€Œà°¨à± చూపడానికి |<ph name="ACCELERATOR" />| నొకà±à°•à°‚à°¡à°¿</translation>
<translation id="6280223929691119688">à°ˆ à°…à°¡à±à°°à°¸à±â€Œà°•à± డెలివరీ చేయడం సాధà±à°¯à°‚ కాదà±. వేరే à°…à°¡à±à°°à°¸à±â€Œà°¨à± à°Žà°‚à°šà±à°•à±‹à°‚à°¡à°¿.</translation>
-<translation id="6282194474023008486">పోసà±à°Ÿà°²à± కోడà±</translation>
<translation id="6285507000506177184">'Chromeలో డౌనà±â€Œà°²à±‹à°¡à±â€Œà°²à°¨à± మేనేజౠచేయండి' బటనà±, Chromeలో మీరౠడౌనà±â€Œà°²à±‹à°¡à± చేసిన ఫైలà±à°¸à±â€Œà°¨à± మేనేజౠచేయడానికి 'Enter'నౠనొకà±à°•à°‚à°¡à°¿</translation>
<translation id="6289939620939689042">పేజీ à°°à°‚à°—à±</translation>
<translation id="6290238015253830360">మీకౠసూచించిన కథనాలౠఇకà±à°•à°¡ కనిపిసà±à°¤à°¾à°¯à°¿</translation>
@@ -1946,6 +1958,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> కంటే తకà±à°•à±à°µ à°¸à±à°¥à°²à°¾à°¨à±à°¨à°¿ ఖాళీ చేసà±à°¤à±à°‚ది. మీ తదà±à°ªà°°à°¿ సందరà±à°¶à°¨à°²à±‹ కొనà±à°¨à°¿ సైటà±â€Œà°²à± మరింత నెమà±à°®à°¦à°¿à°—à°¾ లోడౠకావచà±à°šà±.</translation>
<translation id="6337534724793800597">పేరౠదà±à°µà°¾à°°à°¾ విధానాలనౠఫిలà±à°Ÿà°°à± చేయి</translation>
<translation id="6340739886198108203">గోపà±à°¯à°®à±ˆà°¨ కంటెంటౠకనిపించినపà±à°ªà±à°¡à±, దానిని à°¸à±à°•à±à°°à±€à°¨à±â€Œà°·à°¾à°Ÿà±â€Œà°²à± తీయవదà±à°¦à°¨à°¿ లేదా రికారà±à°¡à± చేయవదà±à°¦à°¨à°¿ à°…à°¡à±à°®à°¿à°¨à°¿à°¸à±à°Ÿà±à°°à±‡à°Ÿà°°à± పాలసీ సిఫారà±à°¸à± చేసà±à°¤à±‹à°‚ది:</translation>
+<translation id="6348220984832452017">యాకà±à°Ÿà°¿à°µà±â€Œà°—à°¾ ఉనà±à°¨ వేరియేషనà±â€Œà°²à±</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" />‌నౠఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయండి</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{à°à°µà±€ లేవà±}=1{1 పాసà±â€Œà°µà°°à±à°¡à± (<ph name="DOMAIN_LIST" />కౠచెందినది, సింకౠచేయబడింది)}=2{2 పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à± (<ph name="DOMAIN_LIST" />కౠచెందినవి, సింకౠచేయబడà±à°¡à°¾à°¯à°¿)}other{# పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à± (<ph name="DOMAIN_LIST" />కౠచెందినవి, సింకౠచేయబడà±à°¡à°¾à°¯à°¿)}}</translation>
<translation id="6355392890578844978">à°ˆ à°¬à±à°°à±Œà°œà°°à± కంపెనీ లేదా ఇతర సంసà±à°¥ à°¦à±à°µà°¾à°°à°¾ మేనేజౠచేయబడదà±. à°ˆ పరికరంలోని యాకà±à°Ÿà°¿à°µà°¿à°Ÿà±€à°¨à°¿ Chromium వెలà±à°ªà°² మేనేజౠచేసà±à°¤à±à°‚డవచà±à°šà±. <ph name="BEGIN_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LINK" /></translation>
@@ -1977,7 +1990,6 @@
<translation id="643051589346665201">Google పాసà±â€Œà°µà°°à±à°¡à±â€Œà°¨à± మారà±à°šà°‚à°¡à°¿</translation>
<translation id="6433490469411711332">సంపà±à°°à°¦à°¿à°‚పౠసమాచారానà±à°¨à°¿ ఎడిటౠచేయండి</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> కనెకà±à°Ÿà± కావడానికి నిరాకరించింది.</translation>
-<translation id="6438025220577812695">నేనే మారà±à°¸à±à°¤à°¾à°¨à±</translation>
<translation id="6440503408713884761">విసà±à°®à°°à°¿à°‚చబడింది</translation>
<translation id="6443406338865242315">à° à°Žà°•à±à°¸à±â€Œà°Ÿà±†à°¨à±à°·à°¨à±â€Œà°²à± మరియౠపà±à°²à°—à°¿à°¨à±â€Œà°²à°¨à± మీరౠఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేశారà±</translation>
<translation id="6446163441502663861">Kahu (à°Žà°¨à±à°µà°²à°ªà±)</translation>
@@ -2107,9 +2119,9 @@
<translation id="6828866289116430505">జనà±à°¯à±à°¶à°¾à°¸à±à°¤à±à°°à°‚</translation>
<translation id="6831043979455480757">à°…à°¨à±à°µà°¦à°¿à°‚à°šà±</translation>
<translation id="6833752742582340615">à°¸à±à°°à°•à±à°·à°¿à°¤à°®à±ˆà°¨, వేగవంతమైన చెకౠఅవà±à°Ÿà±â€Œà°² కోసం మీ కారà±à°¡à±, అలాగే బిలà±à°²à°¿à°‚గౠసమాచారానà±à°¨à°¿ మీ Google ఖాతాలో సేవౠచేయండి</translation>
-<translation id="6839929833149231406">à°ªà±à°°à°¾à°‚తం</translation>
<translation id="6846340164947227603">వరà±à°šà±à°µà°²à± కారà±à°¡à± నంబరà±â€Œà°¨à± ఉపయోగించండి...</translation>
<translation id="6852204201400771460">యాపà±â€Œà°¨à± మళà±à°²à±€ లోడౠచేయాలా?</translation>
+<translation id="6857776781123259569">పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à°¨à± మేనేజౠచేయండి...</translation>
<translation id="686485648936420384">à°•à°¨à±à°œà±à°¯à±‚మరౠరిసోరà±à°¸à±â€Œà°²à±</translation>
<translation id="6865412394715372076">à°ªà±à°°à°¸à±à°¤à±à°¤à°‚ à°ˆ కారà±à°¡à±â€Œà°¨à°¿ ధృవీకరించడం సాధà±à°¯à°ªà°¡à°¦à±</translation>
<translation id="6869334554832814367">à°µà±à°¯à°•à±à°¤à°¿à°—à°¤ à°°à±à°£à°¾à°²à±</translation>
@@ -2158,7 +2170,6 @@
<translation id="6965978654500191972">పరికరం</translation>
<translation id="696703987787944103">పరà±à°¸à±†à°ªà±à°šà±à°µà°²à±</translation>
<translation id="6968269510885595029">మీ à°­à°¦à±à°°à°¤à°¾ కీని ఉపయోగించండి</translation>
-<translation id="6970216967273061347">జిలà±à°²à°¾</translation>
<translation id="6971439137020188025">Slidesలో à°¤à±à°µà°°à°—à°¾ కొతà±à°¤ Google à°ªà±à°°à±†à°œà±†à°‚టేషనà±â€Œà°¨à± à°•à±à°°à°¿à°¯à±‡à°Ÿà± చేయండి</translation>
<translation id="6972629891077993081">HID పరికరాలà±</translation>
<translation id="6973656660372572881">రెండౠసà±à°¥à°¿à°° à°ªà±à°°à°¾à°•à±à°¸à±€ సరà±à°µà°°à±à°²à± మరియౠఒక .pac à°¸à±à°•à±à°°à°¿à°ªà±à°Ÿà± URL పేరà±à°•à±Šà°¨à°¬à°¡à±à°¡à°¾à°¯à°¿.</translation>
@@ -2197,7 +2208,6 @@
<translation id="7081308185095828845">మీ పరికరంలో à°ˆ ఫీచరౠఅందà±à°¬à°¾à°Ÿà±à°²à±‹ లేదà±</translation>
<translation id="7083258188081898530">à°Ÿà±à°°à±‡ 9</translation>
<translation id="7086090958708083563">à°…à°ªà±â€Œà°²à±‹à°¡à±â€Œà°¨à± యూజరౠరికà±à°µà±†à°¸à±à°Ÿà± చేశారà±</translation>
-<translation id="7087282848513945231">కౌంటి</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°²à±‹ à°…à°¨à±à°®à°¤à±à°²à°¨à± మేనేజౠచేయడానికి, అలాగే సైటà±â€Œà°² అంతటా à°¸à±à°Ÿà±‹à°°à± చేయబడిన డేటానౠమేనేజౠచేయడానికి 'Tab'నౠనొకà±à°•à°¿, ఆపై 'Enter'నౠనొకà±à°•à°‚à°¡à°¿</translation>
<translation id="7096937462164235847">à°ˆ వెబà±â€à°¸à±ˆà°Ÿà±â€Œà°•à± సంబంధించిన à°—à±à°°à±à°¤à°¿à°‚పౠవెరిఫై చేయబడలేదà±.</translation>
<translation id="7101893872976785596">భయానక సినిమాలà±</translation>
@@ -2216,10 +2226,10 @@
<ph name="LIST_ITEM" />ఫారమà±â€Œà°²à°²à±‹ ఎంటరౠచేసిన సమాచారం<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">à°ˆ à°…à°¡à±à°°à°¸à±â€Œà°•à± రవాణా చేయడం సాధà±à°¯à°‚ కాదà±. వేరే à°…à°¡à±à°°à°¸à±â€Œà°¨à± à°Žà°‚à°šà±à°•à±‹à°‚à°¡à°¿.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">మీ à°…à°¡à±à°®à°¿à°¨à± à°ˆ డేటానౠకాపీ చేయకà±à°‚à°¡à°¾ నిషేధించారà±.</translation>
<translation id="7135130955892390533">à°¸à±à°¥à°¿à°¤à°¿à°¨à°¿ చూపà±</translation>
<translation id="7138472120740807366">డెలివరీ పదà±à°§à°¤à°¿</translation>
-<translation id="7139724024395191329">ఎమిరేటà±</translation>
<translation id="7139892792842608322">à°ªà±à°°à±ˆà°®à°°à±€ à°Ÿà±à°°à±‡</translation>
<translation id="714064300541049402">2à°µ వైపౠపà±à°°à°¿à°‚à°Ÿà±â€Œà°²à±‹ à°šà°¿à°¤à±à°°à°¾à°¨à±à°¨à°¿ X à°…à°•à±à°·à°‚లో జరపà±</translation>
<translation id="7152423860607593928">నంబరà±-14 (à°Žà°¨à±à°µà°²à°ªà±)</translation>
@@ -2436,6 +2446,7 @@
<translation id="7669271284792375604">à°ˆ సైటà±â€Œà°²à±‹à°¨à°¿ దాడి చేసేవారౠమీ à°¬à±à°°à±Œà°œà°¿à°‚à°—à± à°…à°¨à±à°­à°µà°¾à°¨à°¿à°•à°¿ (ఉదాహరణకà±, మీ హోమౠపేజీని మారà±à°šà°¡à°‚ లేదా మీరౠసందరà±à°¶à°¿à°‚చే సైటà±â€Œà°²à±à°²à±‹ అదనపౠపà±à°°à°•à°Ÿà°¨à°²à°¨à± చూపడం à°¦à±à°µà°¾à°°à°¾) హాని కలిగించే à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేసే విధంగా మిమà±à°®à°²à±à°¨à°¿ మోసగించడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చవచà±à°šà±.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{గోపà±à°¯à°®à±ˆà°¨à°µà°¿à°—à°¾ à°«à±à°²à°¾à°—ౠచేయబడిన డేటాతో à°šà°°à±à°¯à°²à± తీసà±à°•à±‹à°¬à°¡à±à°¡à°¾à°¯à°¿ (లాగినౠచేసినపà±à°ªà°Ÿà°¿ à°¨à±à°‚à°šà°¿ 1 à°šà°°à±à°¯ రిపోరà±à°Ÿà± చేయబడింది). <ph name="BEGIN_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LINK" />}other{గోపà±à°¯à°®à±ˆà°¨à°µà°¿à°—à°¾ à°«à±à°²à°¾à°—ౠచేయబడిన డేటాతో à°šà°°à±à°¯à°²à± తీసà±à°•à±‹à°¬à°¡à±à°¡à°¾à°¯à°¿ (లాగినౠచేసినపà±à°ªà°Ÿà°¿ à°¨à±à°‚à°šà°¿ # à°šà°°à±à°¯à°²à± రిపోరà±à°Ÿà± చేయబడà±à°¡à°¾à°¯à°¿). <ph name="BEGIN_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">మెయిలà±â€Œà°¬à°¾à°•à±à°¸à± 6</translation>
+<translation id="7675325315208090829">పేమెంటౠఆపà±à°·à°¨à±â€Œà°²à°¨à± మేనేజౠచేయండి...</translation>
<translation id="7676643023259824263">à°•à±à°²à°¿à°ªà±â€Œà°¬à±‹à°°à±à°¡à± వచనం, <ph name="TEXT" /> కోసం వెతకండి</translation>
<translation id="7679367271685653708">Chrome సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°²à±‹ మీ à°¬à±à°°à±Œà°œà°¿à°‚గౠహిసà±à°Ÿà°°à±€à°¨à°¿ చూడండి, మేనేజౠచేయండి</translation>
<translation id="7679947978757153706">బేసà±â€Œà°¬à°¾à°²à±</translation>
@@ -2478,7 +2489,6 @@
<translation id="7766518757692125295">à°¸à±à°•à°°à±à°Ÿà±</translation>
<translation id="7770259615151589601">పొడవà±à°—à°¾ నిరà±à°¦à±‡à°¶à°¿à°‚చబడింది</translation>
<translation id="7773005668374414287">ఒకే à°•à±à°°à°®à°‚లో ఉనà±à°¨ ఫేసౠఅపà±</translation>
-<translation id="777702478322588152">అధికారిక నివాసం</translation>
<translation id="7791011319128895129">రిలీజౠకానివి</translation>
<translation id="7791196057686275387">బండిలà±</translation>
<translation id="7791543448312431591">జోడించà±</translation>
@@ -2569,12 +2579,12 @@
<translation id="8055534648776115597">వృతà±à°¤à°¿ సంబంధ &amp; కొనసాగింపౠవిదà±à°¯</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />"ని సరిగà±à°—à°¾ కానà±à°«à°¿à°—రౠచేయలేదà±. సాధారణంగా "<ph name="SOFTWARE_NAME" />"ని à°…à°¨à±à°‡à°¨à±â€Œà°¸à±à°Ÿà°¾à°²à± చేయడం à°¦à±à°µà°¾à°°à°¾ సమసà±à°¯ పరిషà±à°•à°¾à°°à°‚ కావచà±à°šà±. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">ఆహార ఉతà±à°ªà°¤à±à°¤à°¿</translation>
-<translation id="8066955247577885446">à°•à±à°·à°®à°¿à°‚à°šà°‚à°¡à°¿, à°à°¦à±‹ తపà±à°ªà± జరిగింది.</translation>
<translation id="8067872629359326442">మీరౠమోసపూరితమైన సైటà±â€Œà°²à±‹ మీ పాసà±â€Œà°µà°°à±à°¡à±â€Œà°¨à± ఎంటరౠచేశారà±. Chromium సహాయపడగలదà±. మీ పాసà±â€Œà°µà°°à±à°¡à±â€Œâ€Œà°¨à± మారà±à°šà°¿, మీ ఖాతా à°ªà±à°°à°®à°¾à°¦à°‚లో ఉండవచà±à°šà°¨à°¿ Googleకౠతెలియజేయడానికి, 'ఖాతానౠసంరకà±à°·à°¿à°‚à°šà±'నౠకà±à°²à°¿à°•à± చేయండి.</translation>
<translation id="8070439594494267500">యాపౠచిహà±à°¨à°‚</translation>
<translation id="8074253406171541171">10x13 (à°Žà°¨à±à°µà°²à°ªà±)</translation>
<translation id="8075736640322370409">కొతà±à°¤ Google షీటà±â€Œà°¨à± à°¤à±à°µà°°à°—à°¾ à°•à±à°°à°¿à°¯à±‡à°Ÿà± చేయండి</translation>
<translation id="8075898834294118863">సైటౠసెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± మేనేజౠచేయి</translation>
+<translation id="8076492880354921740">à°Ÿà±à°¯à°¾à°¬à±â€Œà°²à±</translation>
<translation id="8078141288243656252">తిపà±à°ªà°¿à°¨à°ªà±à°ªà±à°¡à± అదనపౠగమనికలనౠఅందించడం సాధà±à°¯à°ªà°¡à°¦à±</translation>
<translation id="8079031581361219619">సైటà±â€Œà°¨à± తిరిగి లోడౠచేయాలా?</translation>
<translation id="8081087320434522107">సెడానà±â€Œà°²à±</translation>
@@ -2674,7 +2684,7 @@
<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">మీ à°­à°¦à±à°°à°¤à°•à± వాటిలà±à°²à±‡ ఆపదల à°—à±à°°à°¿à°‚à°šà°¿ మీకౠఅరà±à°¥à°‚ à°…à°¯à±à°¯à°¿ ఉంటే, హానికర à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à± తీసివేయబడటానికి à°®à±à°‚దే మీరౠ<ph name="BEGIN_LINK" />à°ˆ సైటà±â€Œà°¨à± సందరà±à°¶à°¿à°‚చవచà±à°šà±<ph name="END_LINK" />.</translation>
<translation id="8347658365704983341">దూర విదà±à°¯</translation>
-<translation id="8349305172487531364">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°² పటà±à°Ÿà±€</translation>
+<translation id="8349305172487531364">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°² బారà±â€Œ</translation>
<translation id="8351131234907093545">గమనికనౠకà±à°°à°¿à°¯à±‡à°Ÿà± చేయండి</translation>
<translation id="8355270400102541638">à°¸à±à°¥à°¾à°¨à°¿à°• à°•à±à°°à°¾à°·à± సందరà±à°­à°‚:</translation>
<translation id="8363502534493474904">ఎయిరà±â€Œà°ªà±à°²à±ˆà°¨à± మోడà±â€Œà°¨à± ఆఫౠచేయడం</translation>
@@ -2697,6 +2707,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à±</translation>
+<translation id="8428634594422941299">à°…à°°à±à°¥à°®à±ˆà°‚ది</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°²à±‹ మీ à°•à±à°•à±à°•à±€ à°ªà±à°°à°¾à°§à°¾à°¨à±à°¯à°¤à°²à°¨à± మేనేజౠచేయడానికి 'Tab'నౠనొకà±à°•à°¿, ఆపై 'Enter'నౠనొకà±à°•à°‚à°¡à°¿</translation>
<translation id="8433057134996913067">దీని వలన మీరౠచాలా వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°² à°¨à±à°‚à°¡à°¿ సైనౠఅవà±à°Ÿà± చేయబడతారà±.</translation>
<translation id="8434840396568290395">పెంపà±à°¡à± జంతà±à°µà±à°²à±</translation>
@@ -2794,6 +2805,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> కోసం మీ కోడౠ<ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">à°ˆ à°Ÿà±à°¯à°¾à°¬à±â€Œà°¨à± à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à± చేయండి</translation>
<translation id="8751426954251315517">దయచేసి తరà±à°µà°¾à°¤ మళà±à°²à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿</translation>
+<translation id="8757526089434340176">Google Pay ఆఫరౠఅందà±à°¬à°¾à°Ÿà±à°²à±‹ ఉంది</translation>
<translation id="8758885506338294482">వీడియో గేమౠపోటీలà±</translation>
<translation id="8759274551635299824">à°ˆ కారà±à°¡à± à°—à°¡à±à°µà± à°®à±à°—ిసింది</translation>
<translation id="87601671197631245">à°ˆ సైటౠఉపయోగిసà±à°¤à±à°¨à±à°¨ à°­à°¦à±à°°à°¤à°¾ కానà±à°«à°¿à°—రేషనౠగడà±à°µà± à°®à±à°—ిసింది, దీని వలన మీరౠమీ సమాచారానà±à°¨à°¿ (ఉదాహరణకà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, మెసేజà±â€Œà°²à± లేదా à°•à±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±) à°ˆ సైటà±â€Œà°•à± పంపించినపà±à°ªà±à°¡à± అది బహిరà±à°—తం à°…à°µà±à°µà°µà°šà±à°šà±.</translation>
@@ -2801,6 +2813,7 @@
<translation id="8763927697961133303">USB పరికరం</translation>
<translation id="8763986294015493060">à°ªà±à°°à°¸à±à°¤à±à°¤à°‚ తెరిచి ఉనà±à°¨ à°…à°œà±à°žà°¾à°¤ విండోలనà±à°¨à°¿à°‚టినీ మూసివేయండి</translation>
<translation id="8766943070169463815">à°¸à±à°°à°•à±à°·à°¿à°¤ పేమెంటౠఆధారాల à°ªà±à°°à°¾à°®à°¾à°£à±€à°•à°°à°£ షీటౠతెరవబడింది</translation>
+<translation id="8767765348545497220">సహాయ బబà±à°²à±â€Œà°¨à± మూసివేయి</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">మోటారà±à°¸à±ˆà°•à°¿à°³à±à°²à±</translation>
<translation id="8790007591277257123">&amp;తొలగించడానà±à°¨à°¿ రిపీటà±â€Œ చేయి</translation>
@@ -2813,6 +2826,7 @@
<translation id="8806285662264631610">à°¸à±à°¨à°¾à°¨à°¾à°¨à°¿à°•à°¿ వినియోగించే &amp; శరీర à°ªà±à°°à±‹à°¡à°•à±à°Ÿà±â€Œà°²à±</translation>
<translation id="8807160976559152894">à°ªà±à°°à°¤à°¿ పేజీ తరà±à°µà°¾à°¤ à°•à°¤à±à°¤à°¿à°°à°¿à°‚à°šà°‚à°¡à°¿</translation>
<translation id="8808828119384186784">Chrome సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à±</translation>
+<translation id="8813277370772331957">నాకౠతరà±à°µà°¾à°¤ à°—à±à°°à±à°¤à± చేయి</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, మీ Chrome సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°² à°¨à±à°‚à°¡à°¿ Chromeని à°…à°ªà±â€Œà°¡à±‡à°Ÿà± చేయడానికి 'Tab' నొకà±à°•à°¿, ఆపై 'Enter'నౠనొకà±à°•à°‚à°¡à°¿</translation>
<translation id="8820817407110198400">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°²à±</translation>
<translation id="882338992931677877">మానà±à°¯à±à°µà°²à± à°¸à±à°²à°¾à°Ÿà±</translation>
@@ -2862,7 +2876,7 @@
<translation id="8963213021028234748"><ph name="MARKUP_1" />సూచనలà±:<ph name="MARKUP_2" />మీకౠడేటా కనెకà±à°·à°¨à± ఉందని నిరà±à°§à°¾à°°à°¿à°‚à°šà±à°•à±‹à°‚à°¡à°¿<ph name="MARKUP_3" />à°ˆ వెబà±â€Œà°ªà±‡à°œà±€à°¨à°¿ తరà±à°µà°¾à°¤ మళà±à°²à±€ లోడౠచేయండి<ph name="MARKUP_4" />మీరౠనమోదౠచేసిన à°…à°¡à±à°°à°¸à±â€Œ చెకౠచేయండి<ph name="MARKUP_5" /></translation>
<translation id="8968766641738584599">కారà±à°¡à±â€Œà°¨à°¿ సేవౠచేయండి</translation>
<translation id="8971063699422889582">సరà±à°µà°°à± à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ à°—à°¡à±à°µà± à°®à±à°—ిసింది.</translation>
-<translation id="8975012916872825179">ఫోనౠనంబరà±â€Œà°²à±, ఈమెయిలà±â€Œ à°…à°¡à±à°°à°¸à±â€Œà°²à± మరియౠడెలివరీ à°…à°¡à±à°°à°¸à±â€Œà°²à± లాంటి సమాచారం ఉంటà±à°‚ది</translation>
+<translation id="8975012916872825179">ఫోనౠనంబరà±â€Œà°²à±, ఈమెయిలà±â€Œ à°…à°¡à±à°°à°¸à±â€Œà°²à± మరియౠడెలివరీ à°…à°¡à±à°°à°¸à±â€Œà°² వంటి సమాచారం ఉంటà±à°‚ది</translation>
<translation id="8975263830901772334">మీరౠమà±à°¦à±à°°à°¿à°‚చే ఫైళà±à°² పేరà±à°²à±</translation>
<translation id="8978053250194585037">Google à°¸à±à°°à°•à±à°·à°¿à°¤ à°¬à±à°°à±Œà°œà°¿à°‚గౠఇటీవల <ph name="SITE" />లో <ph name="BEGIN_LINK" />à°«à°¿à°·à°¿à°‚à°—à±â€Œà°¨à°¿ à°—à±à°°à±à°¤à°¿à°‚చింది<ph name="END_LINK" />. ఫిషింగౠసైటà±â€Œà°²à± వేరే వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°² లాగా à°ªà±à°°à°µà°°à±à°¤à°¿à°‚à°šà°¡à°‚ à°¦à±à°µà°¾à°°à°¾ మిమà±à°®à°²à±à°¨à°¿ మాయ చేయవచà±à°šà±.</translation>
<translation id="8983369100812962543">మీరౠఇపà±à°ªà±à°¡à± యాపౠపరిమాణం మారà±à°šà°µà°šà±à°šà±</translation>
@@ -2902,7 +2916,7 @@
<translation id="9062620674789239642">ఇది తరలించబడి గానీ, ఎడిటౠచేసి గానీ లేదా తొలగించబడి ఉండవచà±à°šà±.</translation>
<translation id="9063800855227801443">గోపà±à°¯à°®à±ˆà°¨ కంటెంటà±â€Œà°¨à± à°•à±à°¯à°¾à°ªà±à°šà°°à± చేయడం సాధà±à°¯à°‚ కాదà±</translation>
<translation id="9065203028668620118">à°Žà°¡à°¿à°Ÿà±</translation>
-<translation id="9065745800631924235">à°šà°°à°¿à°¤à±à°° à°¨à±à°‚à°¡à°¿ <ph name="TEXT" /> శోధన</translation>
+<translation id="9065745800631924235">హిసà±à°Ÿà°°à±€ à°¨à±à°‚à°¡à°¿ <ph name="TEXT" /> శోధన</translation>
<translation id="9069693763241529744">à°Žà°•à±à°¸à±â€Œà°Ÿà±†à°¨à±à°·à°¨à±â€Œ à°¦à±à°µà°¾à°°à°¾ à°¬à±à°²à°¾à°•à± చేయబడింది</translation>
<translation id="9073799351042754113">మీరౠఈ సైటౠకోసం సెకà±à°¯à±‚à°°à°¿à°Ÿà±€ హెచà±à°šà°°à°¿à°•à°²à°¨à± ఆఫౠచేయాలà±à°¸à°¿à°‚దిగా à°Žà°‚à°šà±à°•à±à°¨à±à°¨à°¾à°°à±.</translation>
<translation id="9078964945751709336">మరింత సమాచారం ఆవశà±à°¯à°•à°‚</translation>
@@ -2992,6 +3006,7 @@
<translation id="988159990683914416">డెవలపరౠబిలà±à°¡à±</translation>
<translation id="989988560359834682">à°…à°¡à±à°°à°¸à±â€Œà°¨à± ఎడిటౠచేయండి</translation>
<translation id="991413375315957741">మోషనౠలేదా కాంతి సెనà±à°¸à°¾à°°à±â€Œà°²à±</translation>
+<translation id="992110854164447044">మోసం జరిగే అవకాశమà±à°¨à±à°¨ సందరà±à°­à°‚లో మిమà±à°®à°²à±à°¨à°¿ à°°à°•à±à°·à°¿à°‚చడంలో సహాయపడటానికి, వరà±à°šà±à°µà°²à± కారà±à°¡à± మీ అసలౠకారà±à°¡à± సమాచారానà±à°¨à°¿ దాచిపెడà±à°¤à±à°‚ది. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">à°—à±à°²à°¾à°¬à°¿ à°°à°‚à°—à±</translation>
<translation id="992432478773561401">మీ à°•à°‚à°ªà±à°¯à±‚టరౠలేదా నెటà±â€Œà°µà°°à±à°•à±â€Œà°²à±‹ "<ph name="SOFTWARE_NAME" />" సరిగà±à°—à°¾ ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± కాలేదà±:
diff --git a/chromium/components/strings/components_strings_th.xtb b/chromium/components/strings/components_strings_th.xtb
index 6c87ea4a69a..41b023438d4 100644
--- a/chromium/components/strings/components_strings_th.xtb
+++ b/chromium/components/strings/components_strings_th.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">ทุนà¸à¸²à¸£à¸¨à¸¶à¸à¸©à¸²à¹à¸¥à¸°à¸à¸²à¸£à¸Šà¹ˆà¸§à¸¢à¹€à¸«à¸¥à¸·à¸­à¸—างà¸à¸²à¸£à¹€à¸‡à¸´à¸™</translation>
<translation id="1048785276086539861">เมื่อคุณà¹à¸à¹‰à¹„ขคำอธิบายประà¸à¸­à¸š เอà¸à¸ªà¸²à¸£à¸™à¸µà¹‰à¸ˆà¸°à¸à¸¥à¸±à¸šà¹„ปเป็นมุมมองà¹à¸šà¸šà¸«à¸™à¹‰à¸²à¹€à¸”ียว</translation>
<translation id="1050038467049342496">ปิดà¹à¸­à¸›à¸­à¸·à¹ˆà¸™à¹†</translation>
+<translation id="1053959602163383901">คุณเลือà¸à¸¢à¸·à¸™à¸¢à¸±à¸™à¸”้วยอุปà¸à¸£à¸“์ Authenticator ในเว็บไซต์ที่ใช้ <ph name="PROVIDER_ORIGIN" /> ผู้ให้บริà¸à¸²à¸£à¸™à¸µà¹‰à¸­à¸²à¸ˆà¸ˆà¸±à¸”เà¸à¹‡à¸šà¸‚้อมูลเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸§à¸´à¸˜à¸µà¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™à¸‚องคุณไว้à¹à¸¥à¹‰à¸§ โดยคุณ<ph name="LINK_TEXT" />ได้</translation>
<translation id="1055184225775184556">&amp;เลิà¸à¸—ำà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡</translation>
<translation id="1056663316309890343">ซอฟต์à¹à¸§à¸£à¹Œà¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸£à¸¹à¸›à¸–่าย</translation>
<translation id="1056898198331236512">คำเตือน</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">วิธีà¸à¸²à¸£à¸£à¸±à¸šà¸ªà¸´à¸™à¸„้า</translation>
<translation id="1281476433249504884">สà¹à¸•à¹‡à¸à¹€à¸à¸­à¸£à¹Œ 1</translation>
<translation id="1285320974508926690">ไม่ต้องà¹à¸›à¸¥à¹€à¸§à¹‡à¸šà¹„ซต์นี้</translation>
+<translation id="1288548991597756084">บันทึà¸à¸šà¸±à¸•à¸£à¹„ว้อย่างปลอดภัย</translation>
<translation id="1292571435393770077">ถาด 16</translation>
<translation id="1292701964462482250">"ซอฟต์à¹à¸§à¸£à¹Œà¹ƒà¸™à¸„อมพิวเตอร์ของคุณทำให้ Chrome เชื่อมต่อà¸à¸±à¸šà¹€à¸§à¹‡à¸šà¸­à¸¢à¹ˆà¸²à¸‡à¸›à¸¥à¸­à¸”ภัยไม่ได้" (คอมพิวเตอร์ที่ใช้ระบบปà¸à¸´à¸šà¸±à¸•à¸´à¸à¸²à¸£ Windows เท่านั้น)</translation>
<translation id="1294154142200295408">บรรทัดคำสั่งรูปà¹à¸šà¸šà¸•à¹ˆà¸²à¸‡à¹†</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;ในà¸à¸²à¸£à¹à¸à¹‰à¹„ขข้อผิดพลาด ให้คลิà¸&lt;strong&gt;เชื่อมต่อ&lt;/strong&gt;บนหน้าเว็บที่คุณพยายามจะเปิด&lt;/p&gt;</translation>
<translation id="1507780850870535225">à¸à¸²à¸£à¸­à¸­à¸à¹à¸šà¸šà¸ à¸¹à¸¡à¸´à¸—ัศน์</translation>
<translation id="1513706915089223971">ลิสต์ประวัติรายà¸à¸²à¸£</translation>
+<translation id="1516097932025103760">ระบบจะเข้ารหัส บันทึà¸à¹„ว้อย่างปลอดภัย à¹à¸¥à¸°à¸ˆà¸°à¹„ม่จัดเà¸à¹‡à¸š CVC</translation>
<translation id="1517433312004943670">ต้องระบุหมายเลขโทรศัพท์</translation>
<translation id="1519264250979466059">วันที่สร้าง</translation>
<translation id="1521159554480556801">ศิลปะเส้นใยà¹à¸¥à¸°à¸ªà¸´à¹ˆà¸‡à¸—อ</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">บันทึà¸à¹à¸¥à¸°à¸à¸£à¸­à¸à¸§à¸´à¸˜à¸µà¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™</translation>
<translation id="1663943134801823270">ข้อมูลบัตรà¹à¸¥à¸°à¸—ี่อยู่มาจาภChrome คุณสามารถจัดà¸à¸²à¸£à¸‚้อมูลเหล่านี้ใน<ph name="BEGIN_LINK" />à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า<ph name="END_LINK" /></translation>
<translation id="1671391448414634642">จาà¸à¸™à¸µà¹‰à¹„ประบบจะà¹à¸›à¸¥à¸«à¸™à¹‰à¸²à¸ à¸²à¸©à¸²<ph name="SOURCE_LANGUAGE" />เป็นภาษา<ph name="TARGET_LANGUAGE" /></translation>
+<translation id="1673886523110456987">ชà¹à¸²à¸£à¸°à¹€à¸‡à¸´à¸™à¸”้วย <ph name="CARD_DETAIL" /> เพื่อใช้ข้อเสนอ</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" />เป็น<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">ด้านขอบสั้นà¸à¹ˆà¸­à¸™</translation>
<translation id="168693727862418163">ตรวจสอบค่านโยบายนี้à¸à¸±à¸šà¸ªà¸„ีมาไม่ได้ ระบบจะเพิà¸à¹€à¸‰à¸¢à¸•à¹ˆà¸­à¸„่านี้</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">เซ็นเซอร์ตรวจจับความเคลื่อนไหว</translation>
<translation id="1717494416764505390">à¸à¸¥à¹ˆà¸­à¸‡à¸ˆà¸”หมาย 3</translation>
<translation id="1718029547804390981">เอà¸à¸ªà¸²à¸£à¸¡à¸µà¸‚นาดใหà¸à¹ˆà¹€à¸à¸´à¸™à¸à¸§à¹ˆà¸²à¸ˆà¸°à¹ƒà¸ªà¹ˆà¸«à¸¡à¸²à¸¢à¹€à¸«à¸•à¸¸à¹„ด้</translation>
+<translation id="1720941539803966190">ปิดบทà¹à¸™à¸°à¸™à¸³</translation>
<translation id="1721424275792716183">* ช่องที่ต้องà¸à¸£à¸­à¸</translation>
<translation id="1727613060316725209">ใบรับรองถูà¸à¸•à¹‰à¸­à¸‡</translation>
<translation id="1727741090716970331">เพิ่มหมายเลขบัตรที่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">ปิ้งย่างà¹à¸¥à¸°à¸šà¸²à¸£à¹Œà¸šà¸µà¸„ิว</translation>
<translation id="2053111141626950936">ระบบจะไม่à¹à¸›à¸¥à¸«à¸™à¹‰à¸²à¹€à¸§à¹‡à¸šà¸ à¸²à¸©à¸²<ph name="LANGUAGE" /></translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{เมื่อเปิดà¸à¸²à¸£à¸„วบคุมนี้ไว้à¹à¸¥à¸°à¸ªà¸–านะเป็น "ใช้งานอยู่" Chrome จะระบุว่าà¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸à¸²à¸£à¸—่องเว็บล่าสุดของคุณคล้ายà¸à¸±à¸šà¸à¸¥à¸¸à¹ˆà¸¡à¸„นจำนวนมาà¸à¸«à¸£à¸·à¸­ "à¸à¸¥à¸¸à¹ˆà¸¡à¸›à¸£à¸°à¸Šà¸²à¸à¸£à¸•à¸²à¸¡à¸£à¸¸à¹ˆà¸™" à¸à¸¥à¸¸à¹ˆà¸¡à¹ƒà¸”มาà¸à¸—ี่สุด ผู้ลงโฆษณาจะเลือà¸à¹‚ฆษณาสำหรับà¸à¸¥à¸¸à¹ˆà¸¡à¸”ังà¸à¸¥à¹ˆà¸²à¸§à¹„ด้ à¹à¸¥à¸°à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸à¸²à¸£à¸—่องเว็บจะเà¸à¹‡à¸šà¹„ว้เป็นส่วนตัวในอุปà¸à¸£à¸“์ของคุณ à¸à¸¥à¸¸à¹ˆà¸¡à¸ˆà¸°à¸­à¸±à¸›à¹€à¸”ตทุà¸à¸§à¸±à¸™}=1{เมื่อเปิดà¸à¸²à¸£à¸„วบคุมนี้ไว้à¹à¸¥à¸°à¸ªà¸–านะเป็น "ใช้งานอยู่" Chrome จะระบุว่าà¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸à¸²à¸£à¸—่องเว็บล่าสุดของคุณคล้ายà¸à¸±à¸šà¸à¸¥à¸¸à¹ˆà¸¡à¸„นจำนวนมาà¸à¸«à¸£à¸·à¸­ "à¸à¸¥à¸¸à¹ˆà¸¡à¸›à¸£à¸°à¸Šà¸²à¸à¸£à¸•à¸²à¸¡à¸£à¸¸à¹ˆà¸™" à¸à¸¥à¸¸à¹ˆà¸¡à¹ƒà¸”มาà¸à¸—ี่สุด ผู้ลงโฆษณาจะเลือà¸à¹‚ฆษณาสำหรับà¸à¸¥à¸¸à¹ˆà¸¡à¸”ังà¸à¸¥à¹ˆà¸²à¸§à¹„ด้ à¹à¸¥à¸°à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸à¸²à¸£à¸—่องเว็บจะเà¸à¹‡à¸šà¹„ว้เป็นส่วนตัวในอุปà¸à¸£à¸“์ของคุณ à¸à¸¥à¸¸à¹ˆà¸¡à¸ˆà¸°à¸­à¸±à¸›à¹€à¸”ตทุà¸à¸§à¸±à¸™}other{เมื่อเปิดà¸à¸²à¸£à¸„วบคุมนี้ไว้à¹à¸¥à¸°à¸ªà¸–านะเป็น "ใช้งานอยู่" Chrome จะระบุว่าà¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸à¸²à¸£à¸—่องเว็บล่าสุดของคุณคล้ายà¸à¸±à¸šà¸à¸¥à¸¸à¹ˆà¸¡à¸„นจำนวนมาà¸à¸«à¸£à¸·à¸­ "à¸à¸¥à¸¸à¹ˆà¸¡à¸›à¸£à¸°à¸Šà¸²à¸à¸£à¸•à¸²à¸¡à¸£à¸¸à¹ˆà¸™" à¸à¸¥à¸¸à¹ˆà¸¡à¹ƒà¸”มาà¸à¸—ี่สุด ผู้ลงโฆษณาจะเลือà¸à¹‚ฆษณาสำหรับà¸à¸¥à¸¸à¹ˆà¸¡à¸”ังà¸à¸¥à¹ˆà¸²à¸§à¹„ด้ à¹à¸¥à¸°à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸à¸²à¸£à¸—่องเว็บจะเà¸à¹‡à¸šà¹„ว้เป็นส่วนตัวในอุปà¸à¸£à¸“์ของคุณ à¸à¸¥à¸¸à¹ˆà¸¡à¸ˆà¸°à¸­à¸±à¸›à¹€à¸”ตทุภ{NUM_DAYS} วัน}}</translation>
-<translation id="2053553514270667976">รหัสไปรษณีย์</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 คำà¹à¸™à¸°à¸™à¸³}other{# คำà¹à¸™à¸°à¸™à¸³}}</translation>
+<translation id="2066915425250589881">ส่งคำขอให้ลบ</translation>
<translation id="2068528718802935086">ทารà¸à¹à¸¥à¸°à¹€à¸”็à¸à¹€à¸¥à¹‡à¸</translation>
<translation id="2071156619270205202">บัตรนี้ไม่มีสิทธิ์ใช้หมายเลขบัตรเสมือน</translation>
<translation id="2071692954027939183">à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•à¸·à¸­à¸™à¸–ูà¸à¸šà¸¥à¹‡à¸­à¸à¹‚ดยอัตโนมัติเนื่องจาà¸à¸„ุณมัà¸à¹„ม่อนุà¸à¸²à¸•à¹ƒà¸«à¹‰à¹à¸ªà¸”ง</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">ปุ่มจัดà¸à¸²à¸£à¸à¸²à¸£à¸‹à¸´à¸‡à¸„์ à¸à¸” Enter เพื่อจัดà¸à¸²à¸£à¸‚้อมูลที่คุณซิงค์ในà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า Chrome</translation>
<translation id="2091887806945687916">เสียง</translation>
<translation id="2094505752054353250">โดเมนไม่ตรง</translation>
-<translation id="2096368010154057602">à¹à¸œà¸™à¸</translation>
<translation id="2099652385553570808">เย็บด้วยลวดเย็บà¸à¸£à¸°à¸”าษด้านซ้าย 3 ครั้ง</translation>
<translation id="2101225219012730419">รุ่น:</translation>
<translation id="2102134110707549001">à¹à¸™à¸°à¸™à¸³à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸—ี่รัดà¸à¸¸à¸¡â€¦</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">อเมริà¸à¸±à¸™à¸Ÿà¸¸à¸•à¸šà¸­à¸¥</translation>
<translation id="2187317261103489799">ตรวจหา (ค่าเริ่มต้น)</translation>
<translation id="2188375229972301266">เจาะรูด้านล่างหลายรู</translation>
-<translation id="2188852899391513400">ระบบพบว่ารหัสผ่านที่คุณเพิ่งใช้มีà¸à¸²à¸£à¸£à¸±à¹ˆà¸§à¹„หลในà¸à¸²à¸£à¸¥à¸°à¹€à¸¡à¸´à¸”ข้อมูลครั้งหนึ่ง เพื่อรัà¸à¸©à¸²à¸„วามปลอดภัยของบัà¸à¸Šà¸µ เครื่องมือจัดà¸à¸²à¸£à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸‚อง Google à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸—ันที จาà¸à¸™à¸±à¹‰à¸™à¸•à¸£à¸§à¸ˆà¸ªà¸­à¸šà¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸—ี่บันทึà¸à¹„ว้</translation>
<translation id="219906046732893612">à¸à¸²à¸£à¸›à¸£à¸±à¸šà¸›à¸£à¸¸à¸‡à¸šà¹‰à¸²à¸™</translation>
<translation id="2202020181578195191">ป้อนปีที่หมดอายุที่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
<translation id="22081806969704220">ถาด 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">à¸à¸²à¸£à¹à¸à¹‰à¹„ขไฟล์</translation>
<translation id="2215963164070968490">สุนัข</translation>
<translation id="2218879909401188352">ผู้โจมตีที่à¸à¸³à¸¥à¸±à¸‡à¸­à¸¢à¸¹à¹ˆà¹ƒà¸™ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> สามารถติดตั้งà¹à¸­à¸›à¸­à¸±à¸™à¸•à¸£à¸²à¸¢à¸—ี่ทำลายอุปà¸à¸£à¸“์ของคุณ เพิ่มค่าใช้จ่ายà¹à¸à¸‡à¹ƒà¸™à¹ƒà¸šà¹à¸ˆà¹‰à¸‡à¸¢à¸­à¸”มือถือ หรือขโมยข้อมูลส่วนบุคคล <ph name="BEGIN_LEARN_MORE_LINK" />ดูข้อมูลเพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">เริ่มต้นบทà¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¸¡à¹ˆà¸­à¸µà¸à¸„รั้ง</translation>
+<translation id="2219735899272417925">ต้องรีเซ็ตอุปà¸à¸£à¸“์</translation>
<translation id="2224337661447660594">ไม่มีอินเทอร์เน็ต</translation>
<translation id="2230458221926704099">à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸‚องคุณด้วย<ph name="BEGIN_LINK" />à¹à¸­à¸›à¸à¸²à¸£à¸§à¸´à¸™à¸´à¸ˆà¸‰à¸±à¸¢<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">ส่งเลย</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">ไม่อนุà¸à¸²à¸• (ค่าเริ่มต้น)</translation>
<translation id="2512413427717747692">ปุ่มตั้ง Chrome เป็นเบราว์เซอร์เริ่มต้น à¸à¸” Enter เพื่อตั้ง Chrome เป็นเบราว์เซอร์เริ่มต้นของระบบในà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า iOS</translation>
<translation id="2515629240566999685">ตรวจสอบสัà¸à¸à¸²à¸“ในพื้นที่ของคุณ</translation>
+<translation id="2515761554693942801">คุณเลือà¸à¸¢à¸·à¸™à¸¢à¸±à¸™à¸”้วย Touch ID ในเว็บไซต์ที่ใช้ <ph name="PROVIDER_ORIGIN" /> ผู้ให้บริà¸à¸²à¸£à¸™à¸µà¹‰à¸­à¸²à¸ˆà¸ˆà¸±à¸”เà¸à¹‡à¸šà¸‚้อมูลเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸§à¸´à¸˜à¸µà¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™à¸‚องคุณไว้à¹à¸¥à¹‰à¸§ โดยคุณ<ph name="LINK_TEXT" />ได้</translation>
<translation id="2521385132275182522">เย็บด้วยลวดเย็บà¸à¸£à¸°à¸”าษด้านขวาล่าง</translation>
<translation id="2521736961081452453">สร้างà¹à¸šà¸šà¸Ÿà¸­à¸£à¹Œà¸¡</translation>
<translation id="2523886232349826891">บันทึà¸à¹„ว้เฉพาะในอุปà¸à¸£à¸“์นี้</translation>
<translation id="2524461107774643265">เพิ่มข้อมูลอื่นๆ</translation>
<translation id="2529899080962247600">ช่องนี้ต้องมีข้อมูลไม่เà¸à¸´à¸™ <ph name="MAX_ITEMS_LIMIT" /> รายà¸à¸²à¸£ ระบบจะไม่สนใจรายà¸à¸²à¸£à¸­à¸·à¹ˆà¸™à¹† ทั้งหมด</translation>
+<translation id="253493526287553278">ดูรายละเอียดรหัสโปรโมชัน</translation>
<translation id="2535585790302968248">เปิดà¹à¸—็บที่ไม่ระบุตัวตนà¹à¸—็บใหม่เพื่อท่องเว็บà¹à¸šà¸šà¸ªà¹ˆà¸§à¸™à¸•à¸±à¸§</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{à¹à¸¥à¸°à¸­à¸µà¸ 1 โดเมน}other{à¹à¸¥à¸°à¸­à¸µà¸ # โดเมน}}</translation>
<translation id="2536110899380797252">เพิ่มที่อยู่</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">ศิลปะภาพถ่ายà¹à¸¥à¸°à¸”ิจิทัล</translation>
<translation id="2601150049980261779">ภาพยนตร์โรà¹à¸¡à¸™à¸•à¸´à¸</translation>
<translation id="2604589665489080024">เพลงป๊อป</translation>
-<translation id="2609632851001447353">รูปà¹à¸šà¸šà¸•à¹ˆà¸²à¸‡à¹†</translation>
<translation id="2610561535971892504">คลิà¸à¹€à¸žà¸·à¹ˆà¸­à¸„ัดลอà¸</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />จะไม่บันทึà¸<ph name="END_EMPHASIS" />ข้อมูลต่อไปนี้
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">วันเà¸à¸´à¸”à¹à¸¥à¸°à¸§à¸±à¸™à¸•à¸±à¹‰à¸‡à¸Šà¸·à¹ˆà¸­</translation>
<translation id="2677748264148917807">ออà¸</translation>
+<translation id="2679714844901977852">บันทึà¸à¸šà¸±à¸•à¸£à¹à¸¥à¸°à¸‚้อมูลà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸à¹€à¸à¹‡à¸šà¹€à¸‡à¸´à¸™à¹„ว้ในบัà¸à¸Šà¸µ Google <ph name="USER_EMAIL" /> เพื่อให้ชำระเงินได้อย่างปลอดภัยà¹à¸¥à¸°à¸£à¸§à¸”เร็วยิ่งขึ้น</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">ปรับพอดี</translation>
<translation id="2688969097326701645">ใช่ ดำเนินà¸à¸²à¸£à¸•à¹ˆà¸­</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">ผู้ให้บริà¸à¸²à¸£à¸­à¸´à¸™à¹€à¸—อร์เน็ต (ISP)</translation>
<translation id="2856444702002559011">ผู้โจมตีอาจพยายามขโมยข้อมูลจาภ<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (ตัวอย่างเช่น รหัสผ่าน ข้อความ หรือบัตรเครดิต) <ph name="BEGIN_LEARN_MORE_LINK" />ดูข้อมูลเพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">เว็บไซต์นี้à¹à¸ªà¸”งโฆษณาที่à¹à¸—รà¸à¸«à¸£à¸·à¸­à¸—ำให้เข้าใจผิด</translation>
-<translation id="286512204874376891">บัตรเสมือนจะอำพรางบัตรจริงไว้เพื่อช่วยปà¸à¸›à¹‰à¸­à¸‡à¸„ุณจาà¸à¸à¸²à¸£à¸‰à¹‰à¸­à¹‚à¸à¸‡à¸—ี่อาจเà¸à¸´à¸”ขึ้น <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">เป็นà¸à¸±à¸™à¹€à¸­à¸‡</translation>
<translation id="28761159517501904">ภาพยนตร์</translation>
<translation id="2876489322757410363">à¸à¸³à¸¥à¸±à¸‡à¸­à¸­à¸à¸ˆà¸²à¸à¹‚หมดไม่ระบุตัวตนเพื่อชำระเงินผ่านà¹à¸­à¸›à¸žà¸¥à¸´à¹€à¸„ชันภายนอภต้องà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸­à¹„หม</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">ดิสà¸à¹Œ</translation>
<translation id="3162559335345991374">Wi-Fi ที่คุณใช้อาจต้องà¸à¸²à¸£à¹ƒà¸«à¹‰à¸„ุณไปที่หน้าà¸à¸²à¸£à¹€à¸‚้าสู่ระบบ</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">เà¸à¸²à¸°</translation>
<translation id="3176929007561373547">ตรวจสอบà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าพร็อà¸à¸‹à¸µà¸«à¸£à¸·à¸­à¸•à¸´à¸”ต่อผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¹€à¸„รือข่ายของคุณเพื่อ
ตรวจสอบว่าพร็อà¸à¸‹à¸µà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¸—ำงานอยู่ หาà¸à¸„ุณคิดว่าไม่ควร
ใช้พร็อà¸à¸‹à¸µà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œ ให้ดำเนินà¸à¸²à¸£à¸”ังนี้:
@@ -877,9 +882,6 @@
<translation id="3369192424181595722">ข้อผิดพลาดของนาฬิà¸à¸²</translation>
<translation id="3369459162151165748">อุปà¸à¸£à¸“์à¹à¸¥à¸°à¸­à¸°à¹„หล่ยานพาหนะ</translation>
<translation id="3371064404604898522">ตั้ง Chrome เป็นเบราว์เซอร์เริ่มต้น</translation>
-<translation id="3371076217486966826"><ph name="URL" /> ต้องà¸à¸²à¸£
- • สร้างà¹à¸œà¸™à¸—ี่ 3 มิติของสิ่งที่อยู่รอบตัวคุณà¹à¸¥à¸°à¸•à¸´à¸”ตามตำà¹à¸«à¸™à¹ˆà¸‡à¸‚องà¸à¸¥à¹‰à¸­à¸‡
- • ใช้à¸à¸¥à¹‰à¸­à¸‡à¸‚องคุณ</translation>
<translation id="337363190475750230">ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¸ˆà¸±à¸”เตรียมà¹à¸¥à¹‰à¸§</translation>
<translation id="3375754925484257129">เรียà¸à¹ƒà¸Šà¹‰à¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸­à¸šà¸„วามปลอดภัยของ Chrome</translation>
<translation id="3377144306166885718">เซิร์ฟเวอร์ใช้ TLS เวอร์ชันที่ล้าสมัย</translation>
@@ -896,6 +898,7 @@
<translation id="3399952811970034796">ที่อยู่สำหรับจัดส่ง</translation>
<translation id="3402261774528610252">à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸—ี่ใช้โหลดเว็บไซต์นี้ใช้ TLS 1.0 หรือ TLS 1.1 ซึ่งเลิà¸à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹à¸¥à¹‰à¸§à¹à¸¥à¸°à¸ˆà¸°à¸–ูà¸à¸›à¸´à¸”ใช้ในอนาคต เมื่อปิดใช้à¹à¸¥à¹‰à¸§ ระบบจะไม่ให้ผู้ใช้โหลดเว็บไซต์นี้ เซิร์ฟเวอร์ควรเปิดใช้ TLS 1.2 ขึ้นไป</translation>
<translation id="3405664148539009465">à¸à¸³à¸«à¸™à¸”ค่าà¹à¸šà¸šà¸­à¸±à¸à¸©à¸£</translation>
+<translation id="3407789382767355356">à¸à¸²à¸£à¸¥à¸‡à¸Šà¸·à¹ˆà¸­à¹€à¸‚้าใช้ของบุคคลที่สาม</translation>
<translation id="3409896703495473338">จัดà¸à¸²à¸£à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าความปลอดภัย</translation>
<translation id="3414952576877147120">ขนาด:</translation>
<translation id="3417660076059365994">ไฟล์ที่คุณอัปโหลดหรือà¹à¸™à¸šà¸ˆà¸°à¸ªà¹ˆà¸‡à¹„ปยัง Google Cloud หรือบุคคลที่สามเพื่อทำà¸à¸²à¸£à¸§à¸´à¹€à¸„ราะห์ เช่น สà¹à¸à¸™à¸«à¸²à¸‚้อมูลที่ละเอียดอ่อนหรือมัลà¹à¸§à¸£à¹Œ</translation>
@@ -928,6 +931,7 @@
<translation id="3477679029130949506">รายชื่อภาพยนตร์à¹à¸¥à¸°à¹€à¸§à¸¥à¸²à¸‰à¸²à¸¢</translation>
<translation id="3479552764303398839">ไม่ใช่ตอนนี้</translation>
<translation id="3484560055331845446">คุณอาจสูà¸à¹€à¸ªà¸µà¸¢à¸ªà¸´à¸—ธิ์เข้าถึงบัà¸à¸Šà¸µ Google ของคุณ Chrome ขอà¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸—ันที ระบบจะขอให้คุณลงชื่อเข้าใช้</translation>
+<translation id="3484861421501147767">à¸à¸²à¸£à¸Šà¹ˆà¸§à¸¢à¹€à¸•à¸·à¸­à¸™: รหัสโปรโมชันที่บันทึà¸à¹„ว้พร้อมใช้งานà¹à¸¥à¹‰à¸§</translation>
<translation id="3487845404393360112">ถาด 4</translation>
<translation id="3495081129428749620">ค้นหาในหน้าเว็บ <ph name="PAGE_TITLE" /></translation>
<translation id="350069200438440499">ชื่อไฟล์:</translation>
@@ -1051,6 +1055,7 @@
<translation id="3810973564298564668">จัดà¸à¸²à¸£</translation>
<translation id="3816482573645936981">ค่า (ถูà¸à¹à¸—นที่)</translation>
<translation id="382518646247711829">หาà¸à¸„ุณใช้พร็อà¸à¸‹à¸µà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œ...</translation>
+<translation id="3826050100957962900">à¸à¸²à¸£à¸¥à¸‡à¸Šà¸·à¹ˆà¸­à¹€à¸‚้าใช้ของบุคคลที่สาม</translation>
<translation id="3827112369919217609">Absolute</translation>
<translation id="3827666161959873541">ภาพยนตร์ครอบครัว</translation>
<translation id="3828924085048779000">ข้อความรหัสผ่านต้องไม่เว้นว่างไว้</translation>
@@ -1063,9 +1068,9 @@
<translation id="3858027520442213535">อัปเดตวันที่à¹à¸¥à¸°à¹€à¸§à¸¥à¸²</translation>
<translation id="3858860766373142691">ชื่อ</translation>
<translation id="3872834068356954457">วิทยาศาสตร์</translation>
+<translation id="3875783148670536197">à¹à¸ªà¸”งวิธีà¸à¸²à¸£</translation>
<translation id="3881478300875776315">à¹à¸ªà¸”งบรรทัดน้อยลง</translation>
<translation id="3884278016824448484">ตัวชี้อุปà¸à¸£à¸“์ขัดà¹à¸¢à¹‰à¸‡à¸à¸±à¸™</translation>
-<translation id="3885155851504623709">Parish</translation>
<translation id="388632593194507180">ตรวจพบà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸­à¸š</translation>
<translation id="3886948180919384617">สà¹à¸•à¹‡à¸à¹€à¸à¸­à¸£à¹Œ 3</translation>
<translation id="3890664840433101773">เพิ่มอีเมล</translation>
@@ -1095,9 +1100,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> ถูà¸à¸šà¸¥à¹‡à¸­à¸</translation>
<translation id="3978338123949022456">โหมดค้นหา พิมพ์คำถามà¹à¸¥à¸°à¸à¸” Enter เพื่อค้นหาด้วย <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">นà¸</translation>
+<translation id="3985750352229496475">จัดà¸à¸²à¸£à¸—ี่อยู่...</translation>
<translation id="3986705137476756801">ปิดคำบรรยายสดไปà¸à¹ˆà¸­à¸™</translation>
<translation id="3987940399970879459">ไม่ถึง 1 MB</translation>
<translation id="3990250421422698716">à¸à¸²à¸£à¸Šà¸”เชยพื้นผิวที่ไม่สม่ำเสมอ</translation>
+<translation id="3992684624889376114">เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸«à¸™à¹‰à¸²à¸™à¸µà¹‰</translation>
<translation id="3996311196211510766">เว็บไซต์ <ph name="ORIGIN" /> ขอให้บังคับใช้นโยบายดั้งเดิมà¸à¸±à¸šà¸„ำขอทั้งหมดที่ส่งถึงเว็บไซต์ à¹à¸•à¹ˆà¸‚ณะนี้ใช้นโยบายนี้ไม่ได้</translation>
<translation id="4006465311664329701">วิธีà¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™ ข้อเสนอ à¹à¸¥à¸°à¸—ี่อยู่จาภGoogle Pay</translation>
<translation id="4009243425692662128">ระบบจะส่งเนื้อหาของหน้าที่คุณพิมพ์ไปยัง Google Cloud หรือบุคคลที่สามเพื่อà¸à¸²à¸£à¸§à¸´à¹€à¸„ราะห์ เช่น สà¹à¸à¸™à¸«à¸²à¸‚้อมูลที่ละเอียดอ่อน</translation>
@@ -1217,6 +1224,7 @@
<translation id="4305666528087210886">เข้าถึงไฟล์ไม่ได้</translation>
<translation id="4306529830550717874">บันทึà¸à¸—ี่อยู่ไหม</translation>
<translation id="4306812610847412719">คลิปบอร์ด</translation>
+<translation id="4310070645992025887">ค้นหาเส้นทางà¸à¸²à¸£à¸—่องเว็บ</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">บล็อภ(ค่าเริ่มต้น)</translation>
<translation id="4314815835985389558">จัดà¸à¸²à¸£à¸à¸²à¸£à¸‹à¸´à¸‡à¸„์</translation>
@@ -1267,6 +1275,7 @@
<translation id="4435702339979719576">โปสà¸à¸²à¸£à¹Œà¸”)</translation>
<translation id="443673843213245140">à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸žà¸£à¹‡à¸­à¸à¸‹à¸µà¸–ูà¸à¸›à¸´à¸”ใช้งาน à¹à¸•à¹ˆà¸¡à¸µà¸à¸²à¸£à¸£à¸°à¸šà¸¸à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าพร็อà¸à¸‹à¸µà¸­à¸¢à¹ˆà¸²à¸‡à¸Šà¸±à¸”เจน</translation>
<translation id="4441832193888514600">ถูà¸à¸¥à¸°à¹€à¸§à¹‰à¸™à¹€à¸™à¸·à¹ˆà¸­à¸‡à¸ˆà¸²à¸à¸™à¹‚ยบายจะต้องตั้งค่าเป็นนโยบายผู้ใช้ระบบคลาวด์เท่านั้น</translation>
+<translation id="4442470707340296952">à¹à¸—็บ Chrome</translation>
<translation id="4450893287417543264">ไม่ต้องà¹à¸ªà¸”งอีà¸</translation>
<translation id="4451135742916150903">สามารถขอเชื่อมต่อà¸à¸±à¸šà¸­à¸¸à¸›à¸à¸£à¸“์ HID ได้</translation>
<translation id="4452328064229197696">ระบบพบว่ารหัสผ่านที่คุณเพิ่งใช้มีà¸à¸²à¸£à¸£à¸±à¹ˆà¸§à¹„หลในà¸à¸²à¸£à¸¥à¸°à¹€à¸¡à¸´à¸”ข้อมูลครั้งหนึ่ง เพื่อรัà¸à¸©à¸²à¸„วามปลอดภัยของบัà¸à¸Šà¸µ เครื่องมือจัดà¸à¸²à¸£à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸‚อง Google à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¸•à¸£à¸§à¸ˆà¸ªà¸­à¸šà¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸—ี่บันทึà¸à¹„ว้</translation>
@@ -1405,6 +1414,7 @@
<translation id="483241715238664915">เปิดคำเตือน</translation>
<translation id="4834250788637067901">วิธีà¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™ ข้อเสนอ à¹à¸¥à¸°à¸—ี่อยู่จาภGoogle Pay</translation>
<translation id="4838327282952368871">ชวนà¸à¸±à¸™</translation>
+<translation id="4839087176073128681">ชำระเงินได้เร็วขึ้นในครั้งถัดไปà¹à¸¥à¸°à¸›à¸à¸›à¹‰à¸­à¸‡à¸šà¸±à¸•à¸£à¸‚องคุณด้วยà¸à¸²à¸£à¸£à¸±à¸à¸©à¸²à¸„วามปลอดภัยระดับà¹à¸™à¸§à¸«à¸™à¹‰à¸²à¸‚องอุตสาหà¸à¸£à¸£à¸¡à¸ˆà¸²à¸ Google</translation>
<translation id="4840250757394056958">ดูประวัติà¸à¸²à¸£à¹€à¸‚้าชมใน Chrome</translation>
<translation id="484462545196658690">อัตโนมัติ</translation>
<translation id="484671803914931257">รับส่วนลดสำหรับ <ph name="MERCHANT_NAME" /> à¹à¸¥à¸°à¸­à¸·à¹ˆà¸™à¹†</translation>
@@ -1467,6 +1477,7 @@
<translation id="5011561501798487822">ภาษาที่ตรวจพบ</translation>
<translation id="5015510746216210676">ชื่อเครื่อง:</translation>
<translation id="5017554619425969104">ข้อความที่คุณคัดลอà¸</translation>
+<translation id="5017828934289857214">เตือนฉันภายหลัง</translation>
<translation id="5018422839182700155">ไม่สามารถเปิดหน้านี้</translation>
<translation id="5019198164206649151">ไม่สามารถจัดเà¸à¹‡à¸šà¹€à¸™à¸·à¹ˆà¸­à¸‡à¸ˆà¸²à¸à¸£à¸°à¸šà¸šà¹à¸šà¹‡à¸„เอนด์อยู่ในสถานะไม่ดี</translation>
<translation id="5020776957610079374">เวิลด์มิวสิà¸</translation>
@@ -1486,6 +1497,7 @@
<translation id="5051305769747448211">à¸à¸²à¸£à¹à¸ªà¸”งตลà¸à¹à¸šà¸šà¸ªà¸”</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{หาà¸à¸•à¹‰à¸­à¸‡à¸à¸²à¸£à¸ªà¹ˆà¸‡à¹„ฟล์นี้โดยใช้à¸à¸²à¸£à¹à¸Šà¸£à¹Œà¹ƒà¸à¸¥à¹‰à¹€à¸„ียง ให้เพิ่มพื้นที่ว่าง (<ph name="DISK_SPACE_SIZE" />) ในอุปà¸à¸£à¸“์}other{หาà¸à¸•à¹‰à¸­à¸‡à¸à¸²à¸£à¸ªà¹ˆà¸‡à¹„ฟล์เหล่านี้โดยใช้à¸à¸²à¸£à¹à¸Šà¸£à¹Œà¹ƒà¸à¸¥à¹‰à¹€à¸„ียง ให้เพิ่มพื้นที่ว่าง (<ph name="DISK_SPACE_SIZE" />) ในอุปà¸à¸£à¸“์}}</translation>
<translation id="5056549851600133418">บทความสำหรับคุณ</translation>
+<translation id="5060483733937416656">คุณเลือà¸à¸¢à¸·à¸™à¸¢à¸±à¸™à¸”้วย Windows Hello ในเว็บไซต์ที่ใช้ <ph name="PROVIDER_ORIGIN" /> ผู้ให้บริà¸à¸²à¸£à¸™à¸µà¹‰à¸­à¸²à¸ˆà¸ˆà¸±à¸”เà¸à¹‡à¸šà¸‚้อมูลเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸§à¸´à¸˜à¸µà¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™à¸‚องคุณไว้à¹à¸¥à¹‰à¸§ โดยคุณ<ph name="LINK_TEXT" />ได้</translation>
<translation id="5061227663725596739">หรือคุณหมายถึง <ph name="LOOKALIKE_DOMAIN" /></translation>
<translation id="5066056036849835175">ประวัติà¸à¸²à¸£à¸žà¸´à¸¡à¸žà¹Œ</translation>
<translation id="5068234115460527047">à¸à¸­à¸‡à¸—ุนบริหารความเสี่ยง</translation>
@@ -1499,10 +1511,8 @@
<translation id="5087286274860437796">ใบรับรองของเซิร์ฟเวอร์ไม่สามารถใช้ได้ในขณะนี้</translation>
<translation id="5087580092889165836">เพิ่มบัตร</translation>
<translation id="5088142053160410913">ข้อความถึงผู้ดำเนินà¸à¸²à¸£</translation>
-<translation id="5089810972385038852">รัà¸</translation>
<translation id="5093232627742069661">พับà¹à¸šà¸šà¸•à¸±à¸§ Z</translation>
<translation id="5094747076828555589">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะ Chromium ไม่เชื่อถือใบรับรองความปลอดภัย โดยอาจเà¸à¸´à¸”จาà¸à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าผิดหรือผู้บุà¸à¸£à¸¸à¸à¸—ี่ขัดขวางà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸‚องคุณ</translation>
-<translation id="5095208057601539847">จังหวัด</translation>
<translation id="5097099694988056070">สถิติของอุปà¸à¸£à¸“์ เช่น à¸à¸²à¸£à¹ƒà¸Šà¹‰ CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">เว็บไซต์ไม่ปลอดภัย</translation>
@@ -1540,6 +1550,7 @@
<translation id="5171045022955879922">ค้นหาหรือพิมพ์ URL</translation>
<translation id="5171689220826475070">à¸à¸£à¸°à¸”าษต่อเนื่องà¹à¸šà¸šà¸¢à¸¸à¹‚รป</translation>
<translation id="5172758083709347301">ผู้ใช้คอมพิวเตอร์นี้</translation>
+<translation id="5177076414499237632">ดูข้อมูลเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹à¸«à¸¥à¹ˆà¸‡à¸—ี่มาà¹à¸¥à¸°à¸«à¸±à¸§à¸‚้อของหน้านี้</translation>
<translation id="5179510805599951267">หาà¸à¹„ม่มีในภาษา <ph name="ORIGINAL_LANGUAGE" /> ให้รายงานข้อผิดพลาดนี้</translation>
<translation id="518639307526414276">อาหารà¹à¸¥à¸°à¸­à¸¸à¸›à¸à¸£à¸“์ดูà¹à¸¥à¸ªà¸±à¸•à¸§à¹Œà¹€à¸¥à¸µà¹‰à¸¢à¸‡</translation>
<translation id="5190835502935405962">à¹à¸–บบุ๊à¸à¸¡à¸²à¸£à¹Œà¸</translation>
@@ -1700,6 +1711,7 @@
<translation id="5624120631404540903">จัดà¸à¸²à¸£à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™</translation>
<translation id="5629630648637658800">ไม่สามารถโหลดà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านโยบาย</translation>
<translation id="5631439013527180824">โทเค็นà¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¸­à¸¸à¸›à¸à¸£à¸“์ไม่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
+<translation id="5632485077360054581">à¹à¸ªà¸”งวิธีà¸à¸²à¸£</translation>
<translation id="5633066919399395251">ผู้โจมตีที่à¸à¸³à¸¥à¸±à¸‡à¸­à¸¢à¸¹à¹ˆà¹ƒà¸™ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> อาจพยายามติดตั้งโปรà¹à¸à¸£à¸¡à¸­à¸±à¸™à¸•à¸£à¸²à¸¢à¸‹à¸¶à¹ˆà¸‡à¸ˆà¸°à¸‚โมยหรือลบข้อมูล (ตัวอย่างเช่น รูปภาพ รหัสผ่าน ข้อความ à¹à¸¥à¸°à¸šà¸±à¸•à¸£à¹€à¸„รดิต) ลงในคอมพิวเตอร์ของคุณ <ph name="BEGIN_LEARN_MORE_LINK" />ดูข้อมูลเพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">บล็อà¸à¹€à¸™à¸·à¹‰à¸­à¸«à¸²à¸—ี่หลอà¸à¸¥à¸§à¸‡à¹à¸¥à¹‰à¸§</translation>
<translation id="5633259641094592098">ภาพยนตร์ลัทธิà¹à¸¥à¸°à¸™à¸­à¸à¸à¸£à¸°à¹à¸ª</translation>
@@ -1817,6 +1829,7 @@
<translation id="5989320800837274978">ไม่มีà¸à¸²à¸£à¸£à¸°à¸šà¸¸à¸—ั้งพร็อà¸à¸‹à¸µà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹à¸šà¸šà¸„งที่หรือ URL สคริปต์ .pac</translation>
<translation id="5992691462791905444">พับทบà¹à¸šà¸š Engineering รูปตัว Z</translation>
<translation id="5995727681868049093">จัดà¸à¸²à¸£à¸‚้อมูล ความเป็นส่วนตัว à¹à¸¥à¸°à¸à¸²à¸£à¸£à¸±à¸à¸©à¸²à¸„วามปลอดภัยในบัà¸à¸Šà¸µ Google ของคุณ</translation>
+<translation id="5997247540087773573">ระบบพบว่ารหัสผ่านที่คุณเพิ่งใช้มีà¸à¸²à¸£à¸£à¸±à¹ˆà¸§à¹„หลในà¸à¸²à¸£à¸¥à¸°à¹€à¸¡à¸´à¸”ข้อมูลครั้งหนึ่ง เพื่อรัà¸à¸©à¸²à¸„วามปลอดภัยของบัà¸à¸Šà¸µ Google เครื่องมือจัดà¸à¸²à¸£à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸—ันทีà¹à¸¥à¸°à¸•à¸£à¸§à¸ˆà¸ªà¸­à¸šà¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸—ี่บันทึà¸à¹„ว้</translation>
<translation id="6000758707621254961">มีผลà¸à¸²à¸£à¸„้นหา "<ph name="SEARCH_TEXT" />" <ph name="RESULT_COUNT" /> รายà¸à¸²à¸£</translation>
<translation id="6006484371116297560">คลาสสิà¸</translation>
<translation id="6008122969617370890">ลำดับ N ถึง 1</translation>
@@ -1912,7 +1925,6 @@
<translation id="627746635834430766">เพื่อให้ชำระเงินได้เร็วขึ้นในครั้งถัดไป โปรดบันทึà¸à¸šà¸±à¸•à¸£à¹à¸¥à¸°à¸—ี่อยู่สำหรับà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸à¹€à¸à¹‡à¸šà¹€à¸‡à¸´à¸™à¹„ว้ในบัà¸à¸Šà¸µ Google</translation>
<translation id="6279183038361895380">à¸à¸” |<ph name="ACCELERATOR" />| เพื่อà¹à¸ªà¸”งเคอร์เซอร์ของคุณ</translation>
<translation id="6280223929691119688">ไม่สามารถนำส่งสินค้าไปยังที่อยู่นี้ โปรดเลือà¸à¸—ี่อยู่อื่น</translation>
-<translation id="6282194474023008486">รหัสไปรษณีย์</translation>
<translation id="6285507000506177184">ปุ่มจัดà¸à¸²à¸£à¸à¸²à¸£à¸”าวน์โหลดใน Chrome à¸à¸” Enter เพื่อจัดà¸à¸²à¸£à¹„ฟล์ที่คุณดาวน์โหลดใน Chrome</translation>
<translation id="6289939620939689042">สีของหน้าเว็บ</translation>
<translation id="6290238015253830360">บทความที่à¹à¸™à¸°à¸™à¸³à¸ˆà¸°à¸›à¸£à¸²à¸à¸à¸—ี่นี่</translation>
@@ -1934,6 +1946,7 @@
<translation id="6337133576188860026">หาà¸à¹€à¸žà¸´à¹ˆà¸¡à¸žà¸·à¹‰à¸™à¸—ี่ว่างไม่ถึง <ph name="SIZE" /> ไซต์บางà¹à¸«à¹ˆà¸‡à¸­à¸²à¸ˆà¹‚หลดช้าลงเมื่อคุณเข้าชมครั้งถัดไป</translation>
<translation id="6337534724793800597">à¸à¸£à¸­à¸‡à¸™à¹‚ยบายตามชื่อ</translation>
<translation id="6340739886198108203">นโยบายของผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¹„ม่à¹à¸™à¸°à¸™à¸³à¹ƒà¸«à¹‰à¸–่ายภาพหน้าจอหรือบันทึà¸à¸§à¸´à¸”ีโอเมื่อมีà¸à¸²à¸£à¹à¸ªà¸”งเนื้อหาที่เป็นความลับ</translation>
+<translation id="6348220984832452017">รูปà¹à¸šà¸šà¸—ี่ใช้งานอยู่</translation>
<translation id="6349101878882523185">ติดตั้ง <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ไม่มี}=1{มีรหัสผ่าน 1 รายà¸à¸²à¸£ (สำหรับ <ph name="DOMAIN_LIST" />, ซิงค์à¹à¸¥à¹‰à¸§)}=2{มีรหัสผ่าน 2 รายà¸à¸²à¸£ (สำหรับ <ph name="DOMAIN_LIST" />, ซิงค์à¹à¸¥à¹‰à¸§)}other{มีรหัสผ่าน # รายà¸à¸²à¸£ (สำหรับ <ph name="DOMAIN_LIST" />, ซิงค์à¹à¸¥à¹‰à¸§)}}</translation>
<translation id="6355392890578844978">เบราว์เซอร์นี้ไม่ได้จัดà¸à¸²à¸£à¹‚ดยบริษัทหรือองค์à¸à¸£à¸­à¸·à¹ˆà¸™à¹† à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¹ƒà¸™à¸­à¸¸à¸›à¸à¸£à¸“์นี้อาจมีà¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¸ à¸²à¸¢à¸™à¸­à¸ Chromium <ph name="BEGIN_LINK" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK" /></translation>
@@ -1965,7 +1978,6 @@
<translation id="643051589346665201">เปลี่ยนรหัสผ่าน Google</translation>
<translation id="6433490469411711332">à¹à¸à¹‰à¹„ขข้อมูลติดต่อ</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> ปà¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­</translation>
-<translation id="6438025220577812695">เปลี่ยนด้วยตนเอง</translation>
<translation id="6440503408713884761">ละเว้น</translation>
<translation id="6443406338865242315">ส่วนขยายà¹à¸¥à¸°à¸›à¸¥à¸±à¹Šà¸à¸­à¸´à¸™à¸—ี่คุณติดตั้ง</translation>
<translation id="6446163441502663861">Kahu (ซองจดหมาย)</translation>
@@ -2095,9 +2107,9 @@
<translation id="6828866289116430505">พันธุศาสตร์</translation>
<translation id="6831043979455480757">à¹à¸›à¸¥à¸ à¸²à¸©à¸²</translation>
<translation id="6833752742582340615">บันทึà¸à¸šà¸±à¸•à¸£à¹à¸¥à¸°à¸‚้อมูลà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸à¹€à¸à¹‡à¸šà¹€à¸‡à¸´à¸™à¹„ว้ในบัà¸à¸Šà¸µ Google เพื่อให้ชำระเงินได้อย่างปลอดภัยà¹à¸¥à¸°à¸£à¸§à¸”เร็วยิ่งขึ้น</translation>
-<translation id="6839929833149231406">พื้นที่</translation>
<translation id="6846340164947227603">ใช้หมายเลขบัตรเสมือน...</translation>
<translation id="6852204201400771460">โหลดà¹à¸­à¸›à¸‹à¹‰à¸³à¹„หม</translation>
+<translation id="6857776781123259569">จัดà¸à¸²à¸£à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™...</translation>
<translation id="686485648936420384">à¹à¸«à¸¥à¹ˆà¸‡à¸‚้อมูลสำหรับผู้บริโภค</translation>
<translation id="6865412394715372076">ยืนยันบัตรนี้ไม่ได้ในขณะนี้</translation>
<translation id="6869334554832814367">สินเชื่อส่วนบุคคล</translation>
@@ -2146,7 +2158,6 @@
<translation id="6965978654500191972">อุปà¸à¸£à¸“์</translation>
<translation id="696703987787944103">Perceptual</translation>
<translation id="6968269510885595029">ใช้คีย์ความปลอดภัย</translation>
-<translation id="6970216967273061347">อำเภอ</translation>
<translation id="6971439137020188025">สร้างงานนำเสนอใหม่ใน Google สไลด์อย่างรวดเร็ว</translation>
<translation id="6972629891077993081">อุปà¸à¸£à¸“์ HID</translation>
<translation id="6973656660372572881">มีà¸à¸²à¸£à¸£à¸°à¸šà¸¸à¸—ั้งพร็อà¸à¸‹à¸µà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹à¸šà¸šà¸„งที่à¹à¸¥à¸° URL สคริปต์ .pac ไว้</translation>
@@ -2185,7 +2196,6 @@
<translation id="7081308185095828845">ฟีเจอร์นี้ใช้ไม่ได้ในอุปà¸à¸£à¸“์ของคุณ</translation>
<translation id="7083258188081898530">ถาด 9</translation>
<translation id="7086090958708083563">ผู้ใช้ขอà¸à¸²à¸£à¸­à¸±à¸›à¹‚หลด</translation>
-<translation id="7087282848513945231">ประเทศ</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" /> à¸à¸” Tab ตามด้วย Enter เพื่อจัดà¸à¸²à¸£à¸ªà¸´à¸—ธิ์à¹à¸¥à¸°à¸‚้อมูลที่จัดเà¸à¹‡à¸šà¹ƒà¸™à¹€à¸§à¹‡à¸šà¹„ซต์ต่างๆ ในà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า Chrome</translation>
<translation id="7096937462164235847">เว็บไซต์นี้ไม่ได้รับà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸•à¸±à¸§à¸•à¸™</translation>
<translation id="7101893872976785596">ภาพยนตร์สยองขวัà¸</translation>
@@ -2204,10 +2214,10 @@
<ph name="LIST_ITEM" />ข้อมูลที่ป้อนในà¹à¸šà¸šà¸Ÿà¸­à¸£à¹Œà¸¡<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">ไม่สามารถจัดส่งสินค้าไปยังที่อยู่นี้ โปรดเลือà¸à¸—ี่อยู่อื่น</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¸‚องคุณห้ามไม่ให้คัดลอà¸à¸‚้อมูลนี้</translation>
<translation id="7135130955892390533">à¹à¸ªà¸”งสถานะ</translation>
<translation id="7138472120740807366">วิธีà¸à¸²à¸£à¸™à¸³à¸ªà¹ˆà¸‡à¸ªà¸´à¸™à¸„้า</translation>
-<translation id="7139724024395191329">เอมิเรต</translation>
<translation id="7139892792842608322">ถาดหลัà¸</translation>
<translation id="714064300541049402">เปลี่ยนตำà¹à¸«à¸™à¹ˆà¸‡à¸£à¸¹à¸›à¸ à¸²à¸žà¸”้าน 2 ตามà¹à¸à¸™ X</translation>
<translation id="7152423860607593928">Number-14 (ซองจดหมาย)</translation>
@@ -2263,7 +2273,7 @@
<translation id="7308436126008021607">à¸à¸²à¸£à¸‹à¸´à¸‡à¸„์ในเบื้องหลัง</translation>
<translation id="7310392214323165548">อุปà¸à¸£à¸“์จะรีสตาร์ทเร็วๆ นี้</translation>
<translation id="7319430975418800333">A3</translation>
-<translation id="7320336641823683070">ความช่วยเหลือเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­</translation>
+<translation id="7320336641823683070">ความช่วยเหลือในà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­</translation>
<translation id="7323804146520582233">ซ่อนส่วน "<ph name="SECTION" />"</translation>
<translation id="733354035281974745">ลบล้างบัà¸à¸Šà¸µà¸ à¸²à¸¢à¹ƒà¸™à¸­à¸¸à¸›à¸à¸£à¸“์</translation>
<translation id="7334320624316649418">&amp;ทำซ้ำà¸à¸²à¸£à¸ˆà¸±à¸”ลำดับใหม่</translation>
@@ -2424,6 +2434,7 @@
<translation id="7669271284792375604">ผู้โจมตีเว็บไซต์นี้อาจพยายามหลอà¸à¸¥à¹ˆà¸­à¹ƒà¸«à¹‰à¸„ุณติดตั้งโปรà¹à¸à¸£à¸¡à¸—ี่เป็นอันตรายต่อประสบà¸à¸²à¸£à¸“์à¸à¸²à¸£à¸—่องเว็บของคุณ (ตัวอย่างเช่น โดยà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸«à¸™à¹‰à¸²à¹à¸£à¸à¸«à¸£à¸·à¸­à¹à¸ªà¸”งโฆษณาเพิ่มเติมในเว็บไซต์ที่คุณเข้าชม)</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸à¸±à¸šà¸‚้อมูลที่à¹à¸ˆà¹‰à¸‡à¸§à¹ˆà¸²à¹€à¸›à¹‡à¸™à¸„วามลับ (มีà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£ 1 ครั้งตั้งà¹à¸•à¹ˆà¹€à¸‚้าสู่ระบบ) <ph name="BEGIN_LINK" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK" />}other{à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸à¸±à¸šà¸‚้อมูลที่à¹à¸ˆà¹‰à¸‡à¸§à¹ˆà¸²à¹€à¸›à¹‡à¸™à¸„วามลับ (มีà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£ # ครั้งตั้งà¹à¸•à¹ˆà¹€à¸‚้าสู่ระบบ) <ph name="BEGIN_LINK" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">à¸à¸¥à¹ˆà¸­à¸‡à¸ˆà¸”หมาย 6</translation>
+<translation id="7675325315208090829">จัดà¸à¸²à¸£à¸§à¸´à¸˜à¸µà¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™...</translation>
<translation id="7676643023259824263">ค้นหาข้อความในคลิปบอร์ด <ph name="TEXT" /></translation>
<translation id="7679367271685653708">ดูà¹à¸¥à¸°à¸ˆà¸±à¸”à¸à¸²à¸£à¸›à¸£à¸°à¸§à¸±à¸•à¸´à¸à¸²à¸£à¸—่องเว็บในà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า Chrome</translation>
<translation id="7679947978757153706">เบสบอล</translation>
@@ -2466,7 +2477,6 @@
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">ลำดับเดียวà¸à¸±à¸™à¸«à¸‡à¸²à¸¢à¸«à¸™à¹‰à¸²à¸‚ึ้น</translation>
-<translation id="777702478322588152">เขตปà¸à¸„รอง</translation>
<translation id="7791011319128895129">ยังไม่ได้เผยà¹à¸žà¸£à¹ˆ</translation>
<translation id="7791196057686275387">บรรจุภัณฑ์</translation>
<translation id="7791543448312431591">เพิ่ม</translation>
@@ -2557,12 +2567,12 @@
<translation id="8055534648776115597">อาชีวะà¹à¸¥à¸°à¸à¸²à¸£à¸¨à¸¶à¸à¸©à¸²à¸•à¹ˆà¸­à¹€à¸™à¸·à¹ˆà¸­à¸‡</translation>
<translation id="8057711352706143257">à¸à¸³à¸«à¸™à¸”ค่า "<ph name="SOFTWARE_NAME" />" ไม่ถูà¸à¸•à¹‰à¸­à¸‡ à¸à¸²à¸£à¸–อนà¸à¸²à¸£à¸•à¸´à¸”ตั้ง "<ph name="SOFTWARE_NAME" />" มัà¸à¹à¸à¹‰à¹„ขปัà¸à¸«à¸²à¸™à¸µà¹‰à¹„ด้ <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">à¸à¸²à¸£à¸œà¸¥à¸´à¸•à¸­à¸²à¸«à¸²à¸£</translation>
-<translation id="8066955247577885446">ขออภัย มีบางอย่างผิดพลาด</translation>
<translation id="8067872629359326442">คุณเพิ่งใส่รหัสผ่านในเว็บไซต์ที่มีà¸à¸²à¸£à¸«à¸¥à¸­à¸à¸¥à¸§à¸‡ Chromium ช่วยคุณได้ หาà¸à¸•à¹‰à¸­à¸‡à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¹à¸¥à¸°à¹à¸ˆà¹‰à¸‡à¹ƒà¸«à¹‰ Google ทราบว่าบัà¸à¸Šà¸µà¸‚องคุณอาจมีความเสี่ยง ให้คลิภ"ปà¸à¸›à¹‰à¸­à¸‡à¸šà¸±à¸à¸Šà¸µ"</translation>
<translation id="8070439594494267500">ไอคอนà¹à¸­à¸›</translation>
<translation id="8074253406171541171">10x13 (ซองจดหมาย)</translation>
<translation id="8075736640322370409">สร้างชีตใหม่ใน Google ชีตอย่างรวดเร็ว</translation>
<translation id="8075898834294118863">จัดà¸à¸²à¸£à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าเว็บไซต์</translation>
+<translation id="8076492880354921740">à¹à¸—็บ</translation>
<translation id="8078141288243656252">เมื่อหมุนà¹à¸¥à¹‰à¸§à¸ˆà¸°à¹ƒà¸ªà¹ˆà¸«à¸¡à¸²à¸¢à¹€à¸«à¸•à¸¸à¹„ม่ได้</translation>
<translation id="8079031581361219619">โหลดเว็บไซต์ซ้ำไหม</translation>
<translation id="8081087320434522107">รถซีดาน</translation>
@@ -2685,6 +2695,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า</translation>
+<translation id="8428634594422941299">รับทราบ</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" /> à¸à¸” Tab ตามด้วย Enter เพื่อจัดà¸à¸²à¸£à¸„่าà¸à¸³à¸«à¸™à¸”ของคุà¸à¸à¸µà¹‰à¹ƒà¸™à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า Chrome</translation>
<translation id="8433057134996913067">วิธีนี้จะทำให้คุณออà¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸šà¸‚องเว็บไซต์ส่วนใหà¸à¹ˆ</translation>
<translation id="8434840396568290395">สัตว์เลี้ยง</translation>
@@ -2783,6 +2794,7 @@
<translation id="8742371904523228557">รหัสสำหรับ <ph name="ORIGIN" /> คือ <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¹à¸—็บนี้</translation>
<translation id="8751426954251315517">โปรดลองอีà¸à¸„รั้งในคราวหน้า</translation>
+<translation id="8757526089434340176">มีข้อเสนอจาภGoogle Pay</translation>
<translation id="8758885506338294482">วิดีโอเà¸à¸¡à¹à¸šà¸šà¹à¸‚่งขัน</translation>
<translation id="8759274551635299824">บัตรนี้หมดอายุà¹à¸¥à¹‰à¸§</translation>
<translation id="87601671197631245">เว็บไซต์นี้ใช้à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าความปลอดภัยที่ล้าสมัย ซึ่งอาจเปิดเผยข้อมูลของคุณ (เช่น รหัสผ่าน ข้อความ หรือบัตรเครดิต) เมื่อมีà¸à¸²à¸£à¸ªà¹ˆà¸‡à¹„ปยังเว็บไซต์นี้</translation>
@@ -2790,6 +2802,7 @@
<translation id="8763927697961133303">อุปà¸à¸£à¸“์ USB</translation>
<translation id="8763986294015493060">ปิดหน้าต่างที่ไม่ระบุตัวตนทั้งหมดที่เปิดอยู่</translation>
<translation id="8766943070169463815">ชีตà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸­à¸šà¸ªà¸´à¸—ธิ์ข้อมูลเข้าสู่ระบบà¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™à¸—ี่ปลอดภัยเปิดขึ้น</translation>
+<translation id="8767765348545497220">ปิดลูà¸à¹‚ป่งความช่วยเหลือ</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">จัà¸à¸£à¸¢à¸²à¸™à¸¢à¸™à¸•à¹Œ</translation>
<translation id="8790007591277257123">&amp;ทำซ้ำà¸à¸²à¸£à¸™à¸³à¸­à¸­à¸</translation>
@@ -2802,6 +2815,7 @@
<translation id="8806285662264631610">ผลิตภัณฑ์ชำระล้างร่างà¸à¸²à¸¢</translation>
<translation id="8807160976559152894">ตัดออà¸à¸«à¸¥à¸±à¸‡à¸«à¸™à¹‰à¸²à¹à¸•à¹ˆà¸¥à¸°à¸«à¸™à¹‰à¸²</translation>
<translation id="8808828119384186784">à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า Chrome</translation>
+<translation id="8813277370772331957">เตือนฉันภายหลัง</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" /> à¸à¸” Tab ตามด้วย Enter เพื่ออัปเดต Chrome จาà¸à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า Chrome</translation>
<translation id="8820817407110198400">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸</translation>
<translation id="882338992931677877">ช่องà¹à¸šà¸šà¹€à¸¥à¸·à¸­à¸à¸”้วยตนเอง</translation>
@@ -2981,6 +2995,7 @@
<translation id="988159990683914416">รุ่นนัà¸à¸žà¸±à¸’นา</translation>
<translation id="989988560359834682">à¹à¸à¹‰à¹„ขที่อยู่</translation>
<translation id="991413375315957741">เซ็นเซอร์ตรวจจับà¸à¸²à¸£à¹€à¸„ลื่อนไหวหรือเซ็นเซอร์à¹à¸ªà¸‡</translation>
+<translation id="992110854164447044">บัตรเสมือนจะซ่อนบัตรจริงไว้เพื่อช่วยปà¸à¸›à¹‰à¸­à¸‡à¸„ุณจาà¸à¸à¸²à¸£à¸›à¸£à¸°à¸žà¸¤à¸•à¸´à¸¡à¸´à¸Šà¸­à¸šà¸—ี่อาจเà¸à¸´à¸”ขึ้น <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">สีชมพู</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" ไม่ได้ติดตั้งในคอมพิวเตอร์หรือเครือข่ายอย่างถูà¸à¸•à¹‰à¸­à¸‡
diff --git a/chromium/components/strings/components_strings_tr.xtb b/chromium/components/strings/components_strings_tr.xtb
index fa50bc0c880..64e93707b66 100644
--- a/chromium/components/strings/components_strings_tr.xtb
+++ b/chromium/components/strings/components_strings_tr.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Hibeler, burslar ve maddi yardım</translation>
<translation id="1048785276086539861">Ek açıklamaları düzenlediğinizde bu doküman tek sayfalı görünüme geri döner</translation>
<translation id="1050038467049342496">Diğer uygulamaları kapatın</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> kullanan web sitelerinde kimlik doğrulayıcı bir cihazla doğrulamayı seçtiniz. Bu sağlayıcı, ödeme yönteminizle ilgili bilgileri depolamış olabilir. Bu bilgilerin <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">Eklemeyi &amp;Geri Al</translation>
<translation id="1056663316309890343">Fotoğraf yazılımları</translation>
<translation id="1056898198331236512">Uyarı</translation>
@@ -119,6 +120,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="1270502636509132238">Alma Yöntemi</translation>
<translation id="1281476433249504884">Yığınlayıcı 1</translation>
<translation id="1285320974508926690">Bu siteyi hiçbir zaman çevirme</translation>
+<translation id="1288548991597756084">Kartı güvenli bir şekilde kaydedin</translation>
<translation id="1292571435393770077">Tepsi 16</translation>
<translation id="1292701964462482250">"Bilgisayarınızdaki yazılım, Chrome'un web'e güvenli bir şekilde bağlanmasını engelliyor" (yalnızca Windows bilgisayarlar)</translation>
<translation id="1294154142200295408">Komut satırı varyasyonları</translation>
@@ -223,6 +225,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
&lt;p&gt;Hatayı gidermek için, açmaya çalıştığınız sayfada &lt;strong&gt;Bağlan&lt;/strong&gt;'ı tıklayın.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Peyzaj tasarımı</translation>
<translation id="1513706915089223971">Geçmiş girişlerin listesi</translation>
+<translation id="1516097932025103760">Kart bilgileri şifrelenir ve güvenli bir şekilde kaydedilir. CVC ise hiçbir zaman saklanmaz.</translation>
<translation id="1517433312004943670">Telefon numarası gerekli</translation>
<translation id="1519264250979466059">OluÅŸturma Tarihi</translation>
<translation id="1521159554480556801">Fiber ve kumaş sanatları</translation>
@@ -288,6 +291,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="1662550410081243962">Ödeme yöntemlerini kaydet ve doldur</translation>
<translation id="1663943134801823270">Kartlar ve adresler Chrome'dan alınmaktadır. Bu bilgileri <ph name="BEGIN_LINK" />Ayarlar<ph name="END_LINK" />'dan yönetebilirsiniz.</translation>
<translation id="1671391448414634642"><ph name="SOURCE_LANGUAGE" /> dilindeki sayfalar artık <ph name="TARGET_LANGUAGE" /> diline çevrilecek</translation>
+<translation id="1673886523110456987">Fırsatı kullanmak için <ph name="CARD_DETAIL" /> ile ödeme yapın</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> dilinden <ph name="TARGET_LANGUAGE" /> diline</translation>
<translation id="1682696192498422849">Önce kısa kenar</translation>
<translation id="168693727862418163">Bu politika değeri, şemasına göre doğrulanamadı, yok sayılacak.</translation>
@@ -306,6 +310,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="1717218214683051432">Hareket sensörleri</translation>
<translation id="1717494416764505390">Posta kutusu 3</translation>
<translation id="1718029547804390981">Doküman, ek açıklama ilave edilemeyecek kadar büyük</translation>
+<translation id="1720941539803966190">EÄŸitimi kapat</translation>
<translation id="1721424275792716183">* Zorunlu alan</translation>
<translation id="1727613060316725209">Sertifika geçerli</translation>
<translation id="1727741090716970331">Geçerli Kart Numarası Ekleyin</translation>
@@ -419,8 +424,8 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="205212645995975601">Barbekü ve ızgara</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> dilindeki sayfalar çevrilmeyecek.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Bu denetim açık ve durum etkin olduğunda Chrome, son tarama etkinliklerinizin en çok benzediği geniş kullanıcı grubu veya "kohort"u belirler. Reklamverenler grup için reklam seçebilirler ve tarama etkinliğiniz cihazınızda gizli tutulur. Grubunuz her gün güncellenir.}=1{Bu denetim açık ve durum etkin olduğunda Chrome, son tarama etkinliklerinizin en çok benzediği geniş kullanıcı grubu veya "kohort"u belirler. Reklamverenler grup için reklam seçebilirler ve tarama etkinliğiniz cihazınızda gizli tutulur. Grubunuz her gün güncellenir.}other{Bu denetim açık ve durum etkin olduğunda Chrome, son tarama etkinliklerinizin en çok benzediği geniş kullanıcı grubu veya "kohort"u belirler. Reklamverenler grup için reklam seçebilirler ve tarama etkinliğiniz cihazınızda gizli tutulur. Grubunuz {NUM_DAYS} günde bir güncellenir.}}</translation>
-<translation id="2053553514270667976">Posta kodu</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 öneri}other{# öneri}}</translation>
+<translation id="2066915425250589881">silinmesini isteyebilirsiniz</translation>
<translation id="2068528718802935086">Bebekler ve 1-5 yaş çocuklar</translation>
<translation id="2071156619270205202">Bu kart, sanal kart numarası için uygun değil.</translation>
<translation id="2071692954027939183">Genelde izin vermediğiniz için bildirimler otomatik olarak engellendi</translation>
@@ -432,7 +437,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="2088086323192747268">Senkronizasyonu yönet düğmesi, Chrome ayarlarında hangi bilgileri senkronize ettiğinizi yönetmek için Enter'a basın</translation>
<translation id="2091887806945687916">Ses</translation>
<translation id="2094505752054353250">Alan adı uyuşmazlığı</translation>
-<translation id="2096368010154057602">Bölüm</translation>
<translation id="2099652385553570808">Solda üçlü tel zımba</translation>
<translation id="2101225219012730419">Sürüm:</translation>
<translation id="2102134110707549001">Güçlü Şifre Öner…</translation>
@@ -469,7 +473,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="2185836064961771414">Amerikan futbolu</translation>
<translation id="2187317261103489799">Algıla (varsayılan)</translation>
<translation id="2188375229972301266">Altta çoklu zımba</translation>
-<translation id="2188852899391513400">Az önce kullandığınız şifrenin bir veri ihlali sonucunda açığa çıktığı anlaşıldı. Google Şifre Yöneticisi, hesaplarınızın güvenliğini sağlamak için bu şifreyi hemen değiştirmenizi ve ardından kayıtlı şifrelerinizi kontrol etmenizi öneriyor.</translation>
<translation id="219906046732893612">Ev tadilatı</translation>
<translation id="2202020181578195191">Geçerli bir son kullanma yılı girin</translation>
<translation id="22081806969704220">Tepsi 3</translation>
@@ -480,6 +483,8 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="2215727959747642672">Dosya düzenleme</translation>
<translation id="2215963164070968490">Köpekler</translation>
<translation id="2218879909401188352">Şu anda <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> öğesini kullanan saldırganlar cihazınıza zarar verebilecek uygulamalar yükleyebilir, sizden habersiz mobil faturanıza ücretler ekleyebilir veya kişisel bilgilerinizi çalabilirler. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">EÄŸitimi baÅŸtan baÅŸlat</translation>
+<translation id="2219735899272417925">Cihazın sıfırlanması gerekiyor</translation>
<translation id="2224337661447660594">İnternet bağlantısı yok</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />Tanılama uygulamasını<ph name="END_LINK" /> kullanarak bağlantınızı düzeltin</translation>
<translation id="2239100178324503013">Şimdi gönder</translation>
@@ -577,11 +582,13 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="2512101340618156538">İzin verilmiyor (varsayılan)</translation>
<translation id="2512413427717747692">Chrome'u varsayılan tarayıcı olarak ayarlama düğmesi, Chrome'u iOS ayarlarından sistemin varsayılan tarayıcısı olarak ayarlamak için Enter'a basın</translation>
<translation id="2515629240566999685">Bulunduğunuz bölgedeki sinyali kontrol etme</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> kullanan web sitelerinde Touch ID ile doğrulamayı seçtiniz. Bu sağlayıcı, ödeme yönteminizle ilgili bilgileri depolamış olabilir. Bu bilgilerin <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Sağ altta tel zımba</translation>
<translation id="2521736961081452453">Form oluÅŸtur</translation>
<translation id="2523886232349826891">Yalnızca bu cihazda kaydedildi</translation>
<translation id="2524461107774643265">Daha Fazla Bilgi Ekleyin</translation>
<translation id="2529899080962247600">Bu alanda en çok <ph name="MAX_ITEMS_LIMIT" /> giriş olabilir. Bundan sonraki tüm girişler yok sayılacak.</translation>
+<translation id="253493526287553278">Promosyon kodu ayrıntılarını göster</translation>
<translation id="2535585790302968248">Gizli olarak göz atmak için yeni bir gizli sekme aç</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{ve 1 tane daha}other{ve # tane daha}}</translation>
<translation id="2536110899380797252">Adres Ekle</translation>
@@ -617,7 +624,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="259821504105826686">Fotografik ve dijital sanatlar</translation>
<translation id="2601150049980261779">AÅŸk filmleri</translation>
<translation id="2604589665489080024">Pop müzik</translation>
-<translation id="2609632851001447353">Varyasyonlar</translation>
<translation id="2610561535971892504">Kopyalamak için tıkla</translation>
<translation id="2617988307566202237">Chrome aşağıdaki bilgileri <ph name="BEGIN_EMPHASIS" />kaydetmez<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -650,6 +656,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Doğum günleri ve isim günleri</translation>
<translation id="2677748264148917807">Çık</translation>
+<translation id="2679714844901977852">Daha güvenli ve hızlı ödemeler için kartınızı ve fatura bilgilerinizi Google Hesabınıza (<ph name="USER_EMAIL" />) kaydedin</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">En uygun</translation>
<translation id="2688969097326701645">Evet, devam et</translation>
@@ -698,7 +705,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="2854764410992194509">İnternet servis sağlayıcılar (İSS'ler)</translation>
<translation id="2856444702002559011">Saldırganlar <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> üzerinden bilgilerinizi çalmaya çalışıyor olabilir (örneğin, şifreler, mesajlar veya kredi kartları). <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Bu site, araya giren veya yanıltıcı reklamlar gösteriyor.</translation>
-<translation id="286512204874376891">Sanal kart gerçek kartınızı saklayarak olası sahtekarlıklardan korunmanıza yardımcı olur. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Dost Canlısı</translation>
<translation id="28761159517501904">Filmler</translation>
<translation id="2876489322757410363">Harici bir uygulama üzerinden ödeme gerçekleştirmek için Gizli moddan çıkılıyor. Devam etmek istiyor musunuz?</translation>
@@ -799,7 +805,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">Kullandığınız Kablosuz ağ, giriş sayfasını ziyaret etmenizi gerektiriyor olabilir.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Ada</translation>
<translation id="3176929007561373547">Proxy sunucunun çalışıyor olduğundan emin olmak için
proxy ayarlarınızı kontrol edin veya ağ yöneticinize danışın. Proxy sunucu
kullanmamanız gerektiğini düşünüyorsanız:
@@ -878,9 +883,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="3369192424181595722">Saat hatası</translation>
<translation id="3369459162151165748">Araç parçaları ve aksesuarları</translation>
<translation id="3371064404604898522">Chrome'u varsayılan tarayıcı olarak ayarla</translation>
-<translation id="3371076217486966826"><ph name="URL" /> şunları yapmak istiyor:
- • Çevrenizin 3D haritasını oluşturma ve kamera konumunu takip etme
- • Kameranızı kullanma</translation>
<translation id="337363190475750230">Temel hazırlık iptal edildi</translation>
<translation id="3375754925484257129">Chrome güvenlik kontrolü yürüt</translation>
<translation id="3377144306166885718">Sunucu TLS'nin eski bir sürümünü kullandı.</translation>
@@ -897,6 +899,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="3399952811970034796">Teslimat Adresi</translation>
<translation id="3402261774528610252">Bu siteyi yüklemek için kullanılan bağlantı, kullanımdan kaldırılan ve ileride devre dışı bırakılacak olan TLS 1.0 veya TLS 1.1 kullandı. Devre dışı bırakıldığında, kullanıcıların bu siteyi yüklemeleri engellenir. Sunucu TLS 1.2 veya sonraki sürümleri etkinleştirmelidir.</translation>
<translation id="3405664148539009465">Yazı tiplerini özelleştir</translation>
+<translation id="3407789382767355356">üçüncü taraf oturum açma</translation>
<translation id="3409896703495473338">Güvenlik ayarlarını yönet</translation>
<translation id="3414952576877147120">Boyut:</translation>
<translation id="3417660076059365994">Yüklediğiniz veya eklediğiniz dosyalar analiz amacıyla Google Cloud ve üçüncü taraflara gönderilir. Dosyalarınızın hassas veriler veya kötü amaçlı yazılım içerip içermediği kontrol edilir.</translation>
@@ -929,6 +932,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="3477679029130949506">Film listeleri ve sinema gösteri zamanları</translation>
<translation id="3479552764303398839">Åžimdi deÄŸil</translation>
<translation id="3484560055331845446">Google Hesabınıza erişimi kaybedebilirsiniz. Chrome, şifrenizi hemen değiştirmenizi önerir. Oturum açmanız istenecektir.</translation>
+<translation id="3484861421501147767">Hatırlatıcı: Kayıtlı promosyon kodu kullanılabilir</translation>
<translation id="3487845404393360112">Tepsi 4</translation>
<translation id="3495081129428749620">Åžu sayfada bul:
<ph name="PAGE_TITLE" /></translation>
@@ -1053,6 +1057,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="3810973564298564668">Yönet</translation>
<translation id="3816482573645936981">Değer (geçersiz kılındı)</translation>
<translation id="382518646247711829">Proxy sunucu kullanıyorsanız...</translation>
+<translation id="3826050100957962900">Üçüncü taraf oturum açma</translation>
<translation id="3827112369919217609">Mutlak</translation>
<translation id="3827666161959873541">Aile filmleri</translation>
<translation id="3828924085048779000">BoÅŸ parolaya izin verilmez.</translation>
@@ -1065,9 +1070,9 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="3858027520442213535">Tarih ve saati güncelle</translation>
<translation id="3858860766373142691">Ad</translation>
<translation id="3872834068356954457">Bilim</translation>
+<translation id="3875783148670536197">Nasıl Yapıldığını Göster</translation>
<translation id="3881478300875776315">Daha az satır göster</translation>
<translation id="3884278016824448484">Çakışan cihaz tanımlayıcısı</translation>
-<translation id="3885155851504623709">Ä°l</translation>
<translation id="388632593194507180">Ä°zleme Tespit Edildi</translation>
<translation id="3886948180919384617">Yığınlayıcı 3</translation>
<translation id="3890664840433101773">E-posta adresi ekle</translation>
@@ -1097,9 +1102,11 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="3973234410852337861"><ph name="HOST_NAME" /> engellendi</translation>
<translation id="3978338123949022456">Arama modu, <ph name="KEYWORD_SUFFIX" /> ile arama yapmak için bir sorgu yazıp Enter'a basın</translation>
<translation id="398470910934384994">KuÅŸlar</translation>
+<translation id="3985750352229496475">Adresleri Yönet...</translation>
<translation id="3986705137476756801">Canlı Altyazı'yı şimdilik kapat</translation>
<translation id="3987940399970879459">1 MB'tan az</translation>
<translation id="3990250421422698716">Jog ofset</translation>
+<translation id="3992684624889376114">Bu sayfa hakkında</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> sitesi, bir kaynak politikasının kendisine gelen tüm talepler için geçerli olmasını istedi, ancak bu politika şu anda uygulanamıyor.</translation>
<translation id="4006465311664329701">Google Pay'i Kullanan Ödeme Yöntemleri, Teklifler ve Adresler</translation>
<translation id="4009243425692662128">Yazdırdığınız sayfaların içeriği analiz edilmek üzere Google Cloud'a veya üçüncü taraflara gönderilir. Dosyalarınızın hassas veriler içerip içermediği kontrol edilir.</translation>
@@ -1220,6 +1227,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="4305666528087210886">Dosyanıza erişilemedi</translation>
<translation id="4306529830550717874">Adres kaydedilsin mi?</translation>
<translation id="4306812610847412719">pano</translation>
+<translation id="4310070645992025887">Arama yolculuklarınızı arayın</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Engelle (varsayılan)</translation>
<translation id="4314815835985389558">Senkronizasyonu yönetin</translation>
@@ -1270,6 +1278,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="4435702339979719576">Kartpostal)</translation>
<translation id="443673843213245140">Proxy kullanımı devre dışı, ancak açık bir proxy yapılandırması belirtildi.</translation>
<translation id="4441832193888514600">Yalnızca bulut kullanıcı politikası olarak ayarlanabileceği için politika yoksayıldı.</translation>
+<translation id="4442470707340296952">Chrome Sekmeleri</translation>
<translation id="4450893287417543264">Bir daha gösterme</translation>
<translation id="4451135742916150903">HID cihazlarına bağlanmak isteyebilir</translation>
<translation id="4452328064229197696">Az önce kullandığınız şifrenin bir veri ihlali sonucunda açığa çıktığı anlaşıldı. Google Şifre Yöneticisi, hesaplarınızın güvenliğini sağlamak için kayıtlı şifrelerinizi kontrol etmenizi öneriyor.</translation>
@@ -1408,6 +1417,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="483241715238664915">Uyarıları açın</translation>
<translation id="4834250788637067901">Google Pay'i kullanan ödeme yöntemleri, teklifler ve adresler</translation>
<translation id="4838327282952368871">Hayalperest</translation>
+<translation id="4839087176073128681">Google'ın sektör lideri güvenliği sayesinde bir dahaki sefer daha hızlı ödeme yapın ve kartınızı koruyun.</translation>
<translation id="4840250757394056958">Chrome geçmişimi göster</translation>
<translation id="484462545196658690">Auto</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> ve diğer satıcılardan indirimli alışveriş yapın</translation>
@@ -1470,6 +1480,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="5011561501798487822">Algılanan Dil</translation>
<translation id="5015510746216210676">Makine Adı:</translation>
<translation id="5017554619425969104">Kopyalanan metin</translation>
+<translation id="5017828934289857214">Daha Sonra Hatırlat</translation>
<translation id="5018422839182700155">Bu sayfa açılamıyor</translation>
<translation id="5019198164206649151">Yedekleme deposu kötü durumda</translation>
<translation id="5020776957610079374">Dünya müzikleri</translation>
@@ -1489,6 +1500,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="5051305769747448211">Canlı komedi</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Bu dosyayı Yakındakilerle Paylaş özelliğini kullanarak göndermek için cihazınızda yer açın (<ph name="DISK_SPACE_SIZE" />)}other{Bu dosyaları Yakındakilerle Paylaş özelliğini kullanarak göndermek için cihazınızda yer açın (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">Sizin için seçilmiş makaleler</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> kullanan web sitelerinde Windows Hello ile doğrulamayı seçtiniz. Bu sağlayıcı, ödeme yönteminizle ilgili bilgileri depolamış olabilir. Bu bilgilerin <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Åžunu mu demek istediniz: <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Yazdırma geçmişi</translation>
<translation id="5068234115460527047">Yüksek riskli yatırım fonları</translation>
@@ -1502,10 +1514,8 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="5087286274860437796">Sunucu sertifikası şu anda geçerli değil.</translation>
<translation id="5087580092889165836">Kart ekle</translation>
<translation id="5088142053160410913">Operatöre mesaj</translation>
-<translation id="5089810972385038852">Eyalet</translation>
<translation id="5093232627742069661">Z katlama</translation>
<translation id="5094747076828555589">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı; Chromium, sunucunun güvenlik sertifikasına güvenmiyor. Bu durum, bir yanlış yapılandırmadan veya bağlantıya müdahale eden bir saldırgandan kaynaklanıyor olabilir.</translation>
-<translation id="5095208057601539847">Eyalet</translation>
<translation id="5097099694988056070">CPU/RAM kullanımı gibi cihaz istatistikleri</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Site güvenli değil</translation>
@@ -1543,6 +1553,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="5171045022955879922">Arayın veya URL'yi yazın</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Makine</translation>
+<translation id="5177076414499237632">Bu sayfanın kaynağı ve konusu hakkında bilgi edinin</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> dilinde değil mi? Bu hatayı bildirin</translation>
<translation id="518639307526414276">Evcil hayvan mama ve bakım malzemeleri</translation>
<translation id="5190835502935405962">Yer İşareti Çubuğu</translation>
@@ -1703,6 +1714,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="5624120631404540903">Şifreleri yönet</translation>
<translation id="5629630648637658800">Politika ayarları yüklenemedi</translation>
<translation id="5631439013527180824">Geçersiz cihaz yönetimi jetonu</translation>
+<translation id="5632485077360054581">Nasıl yapıldığını göster</translation>
<translation id="5633066919399395251">Şu anda <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sitesindeki saldırganlar, bilgilerinizi (örneğin, fotoğraflar, şifreler, mesajlar ve kredi kartları) çalabilecek veya silebilecek tehlikeli programları bilgisayarınıza yüklemeye çalışabilir. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Yanıltıcı içerik engellendi.</translation>
<translation id="5633259641094592098">Kült ve bağımsız filmler</translation>
@@ -1820,6 +1832,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="5989320800837274978">Sabit proxy sunucular veya bir .pac komut dosyası URL'si belirtilmedi.</translation>
<translation id="5992691462791905444">Yarım Z katlama</translation>
<translation id="5995727681868049093">Google Hesabınızda bilgilerinizi, gizliliğinizi ve güvenliğinizi yönetin</translation>
+<translation id="5997247540087773573">Az önce kullandığınız şifrenin bir veri ihlali sonucunda açığa çıktığı anlaşıldı. Google Şifre Yöneticisi, hesaplarınızın güvenliğini sağlamak için bu şifreyi hemen değiştirmenizi ve kayıtlı şifrelerinizi kontrol etmenizi öneriyor.</translation>
<translation id="6000758707621254961">"<ph name="SEARCH_TEXT" />" için bulunan <ph name="RESULT_COUNT" /> sonuç gösteriliyor</translation>
<translation id="6006484371116297560">Klasik</translation>
<translation id="6008122969617370890">N'den 1'e sıralı</translation>
@@ -1915,7 +1928,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="627746635834430766">Bir dahaki sefere daha hızlı ödeme yapmak için kartınızı ve fatura adresinizi Google Hesabınıza kaydedin.</translation>
<translation id="6279183038361895380">İmlecinizi göstermek için |<ph name="ACCELERATOR" />| tuşuna basın</translation>
<translation id="6280223929691119688">Bu adrese teslimat yapılamıyor. Farklı bir adres seçin.</translation>
-<translation id="6282194474023008486">Posta kodu</translation>
<translation id="6285507000506177184">Chrome'da indirilenleri yönetin düğmesi. Chrome'da indirdiğiniz dosyaları yönetmek için Enter'a basın</translation>
<translation id="6289939620939689042">Sayfa Rengi</translation>
<translation id="6290238015253830360">Önerilen makaleler burada görünür</translation>
@@ -1937,6 +1949,7 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="6337133576188860026"><ph name="SIZE" /> boyutundan daha az yer açar. Bir sonraki ziyaretinizde bazı siteler daha yavaş yüklenebilir.</translation>
<translation id="6337534724793800597">Politikalara ada göre filtre uygula</translation>
<translation id="6340739886198108203">Yönetici politikası, gizli içerik görünür olduğunda ekran görüntüsü veya kayıt alınmasını önermiyor:</translation>
+<translation id="6348220984832452017">Etkin Varyasyonlar</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> uygulamasını yükle</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Yok}=1{1 şifre (<ph name="DOMAIN_LIST" /> için, senkronize edildi)}=2{2 şifre (<ph name="DOMAIN_LIST" /> için, senkronize edildi)}other{# şifre (<ph name="DOMAIN_LIST" /> için, senkronize edildi)}}</translation>
<translation id="6355392890578844978">Bu tarayıcı bir şirket veya başka bir kuruluş tarafından yönetilmemektedir. Bu cihazdaki etkinlikler Chromium dışında yönetilebilir. <ph name="BEGIN_LINK" />Daha fazla bilgi<ph name="END_LINK" /></translation>
@@ -1968,7 +1981,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="643051589346665201">Google ÅŸifresini deÄŸiÅŸtir</translation>
<translation id="6433490469411711332">İletişim bilgilerini düzenle</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> bağlanmayı reddetti.</translation>
-<translation id="6438025220577812695">Kendim deÄŸiÅŸtireceÄŸim</translation>
<translation id="6440503408713884761">Yoksayıldı</translation>
<translation id="6443406338865242315">Hangi uzantıları ve eklentileri yüklediğiniz</translation>
<translation id="6446163441502663861">Kahu (Zarf)</translation>
@@ -2098,9 +2110,9 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="6828866289116430505">Genetik bilimi</translation>
<translation id="6831043979455480757">Çevir</translation>
<translation id="6833752742582340615">Daha güvenli ve hızlı ödemeler için kartınızı ve fatura bilgilerinizi Google Hesabınıza kaydedin</translation>
-<translation id="6839929833149231406">Bölge</translation>
<translation id="6846340164947227603">Sanal kart numarası kullanın...</translation>
<translation id="6852204201400771460">Uygulama yeniden yüklensin mi?</translation>
+<translation id="6857776781123259569">Şifreleri Yönet...</translation>
<translation id="686485648936420384">Tüketici kaynakları</translation>
<translation id="6865412394715372076">Bu kart şu anda doğrulanamıyor</translation>
<translation id="6869334554832814367">Bireysel krediler</translation>
@@ -2149,7 +2161,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="6965978654500191972">Cihaz</translation>
<translation id="696703987787944103">Algısal</translation>
<translation id="6968269510885595029">Güvenlik Anahtarınızı kullanın</translation>
-<translation id="6970216967273061347">Bölge</translation>
<translation id="6971439137020188025">Slaytlar'da hızlıca yeni bir Google sunusu oluşturun</translation>
<translation id="6972629891077993081">HID cihazlar</translation>
<translation id="6973656660372572881">Hem sabit proxy sunucular hem de bir .pac komut dosyası URL'si belirtildi.</translation>
@@ -2188,7 +2199,6 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<translation id="7081308185095828845">Bu özellik cihazınızda kullanılamıyor</translation>
<translation id="7083258188081898530">Tepsi 9</translation>
<translation id="7086090958708083563">Kullanıcı tarafından istenen yükleme</translation>
-<translation id="7087282848513945231">Ãœlke</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome ayarlarında izinleri ve sitelerde depolanan verileri yönetmek için Sekme'ye, ardından Enter'a basın</translation>
<translation id="7096937462164235847">Bu web sitesinin kimliği doğrulanmamış.</translation>
<translation id="7101893872976785596">Korku filmleri</translation>
@@ -2207,10 +2217,10 @@ Aksi halde bu işlem gizlilik ayarlarınız tarafından engellenecek. Buna izin
<ph name="LIST_ITEM" />Formlara girilen bilgiler<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Bu adrese gönderim yapılamıyor. Farklı bir adres seçin.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Yöneticiniz bu verilerin kopyalanmasını engellemiş.</translation>
<translation id="7135130955892390533">Durumu göster</translation>
<translation id="7138472120740807366">Teslimat yöntemi</translation>
-<translation id="7139724024395191329">Emirlik</translation>
<translation id="7139892792842608322">Birincil Tepsi</translation>
<translation id="714064300541049402">Taraf 2 resim X kayması</translation>
<translation id="7152423860607593928">Number-14 (Zarf)</translation>
@@ -2427,6 +2437,7 @@ Ek ayrıntılar:
<translation id="7669271284792375604">Bu sitedeki saldırganlar web'e göz atma deneyiminize zarar veren programlar yüklemeniz için sizi kandırmayı (örneğin ana sayfanızı değiştirerek ya da ziyaret ettiğiniz sitelerde ek reklamlar görüntüleyerek) deneyebilir.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Gizli olarak işaretlenmiş verilerle yapılan işlemler (girişten itibaren 1 işlem). <ph name="BEGIN_LINK" />Daha fazla bilgi<ph name="END_LINK" />}other{Gizli olarak işaretlenmiş verilerle yapılan işlemler (girişten itibaren # işlem). <ph name="BEGIN_LINK" />Daha fazla bilgi<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Posta kutusu 6</translation>
+<translation id="7675325315208090829">Ödeme Yöntemlerini yönet...</translation>
<translation id="7676643023259824263">Panoda metni (<ph name="TEXT" />) ara</translation>
<translation id="7679367271685653708">Chrome ayarlarında tarama geçmişinizi görüntüleyin ve yönetin</translation>
<translation id="7679947978757153706">Beyzbol</translation>
@@ -2469,7 +2480,6 @@ Ek ayrıntılar:
<translation id="7766518757692125295">Skirt</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Aynı sırada ön yüz yukarı bakacak şekilde</translation>
-<translation id="777702478322588152">Bölge</translation>
<translation id="7791011319128895129">Yayınlanmadı</translation>
<translation id="7791196057686275387">Balya</translation>
<translation id="7791543448312431591">Ekle</translation>
@@ -2560,12 +2570,12 @@ Ek ayrıntılar:
<translation id="8055534648776115597">Mesleki ve lise sonrası eğitim</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" doğru şekilde yapılandırılmamış. Genellikle "<ph name="SOFTWARE_NAME" />" kaldırıldığında sorun çözülür. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Gıda üretimi</translation>
-<translation id="8066955247577885446">Maalesef bir hata oluÅŸtu.</translation>
<translation id="8067872629359326442">Az önce şifrenizi yanıltıcı bir sitede girdiniz. Chromium yardım edebilir. Şifrenizi değiştirmek ve hesabınızın risk altında olabileceğini Google'a bildirmek için Hesabı Koru'yu tıklayın.</translation>
<translation id="8070439594494267500">Uygulama simgesi</translation>
<translation id="8074253406171541171">10x13 (Zarf)</translation>
<translation id="8075736640322370409">Hızlıca yeni bir Google E-Tablosu oluşturun</translation>
<translation id="8075898834294118863">Site ayarlarını yönet</translation>
+<translation id="8076492880354921740">Sekmeler</translation>
<translation id="8078141288243656252">Doküman döndürüldüğünde ek açıklama özelliği kullanılamaz</translation>
<translation id="8079031581361219619">Site yeniden yüklensin mi?</translation>
<translation id="8081087320434522107">Sedan araçlar</translation>
@@ -2688,6 +2698,7 @@ Ek ayrıntılar:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Ayarlar</translation>
+<translation id="8428634594422941299">Anladım!</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome ayarlarında çerez tercihlerinizi yönetmek için Sekme'ye, ardından Enter'a basın</translation>
<translation id="8433057134996913067">Bu işlem, çoğu web sitesinden çıkış yapmanıza neden olacak.</translation>
<translation id="8434840396568290395">Evcil hayvanlar</translation>
@@ -2786,6 +2797,7 @@ Ek ayrıntılar:
<translation id="8742371904523228557"><ph name="ORIGIN" /> için kodunuz: <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">Bu sekmeye yer iÅŸareti koy</translation>
<translation id="8751426954251315517">Lütfen bir dahaki sefer tekrar deneyin</translation>
+<translation id="8757526089434340176">Google Pay teklifi mevcut</translation>
<translation id="8758885506338294482">Rekabetçi video oyunları</translation>
<translation id="8759274551635299824">Bu kartın kullanım süresi doldu</translation>
<translation id="87601671197631245">Bu site, eski bir güvenlik yapılandırması kullanıyor. Bu siteye gönderildiğinde bilgileriniz (ör. şifreler, mesajlar veya kredi kartları) gösterilebilir.</translation>
@@ -2793,6 +2805,7 @@ Ek ayrıntılar:
<translation id="8763927697961133303">USB cihaz</translation>
<translation id="8763986294015493060">Açık olan tüm gizli pencereleri kapat</translation>
<translation id="8766943070169463815">Güvenli ödeme kimlik bilgisi doğrulama sayfası açıldı</translation>
+<translation id="8767765348545497220">Yardım balonunu kapat</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Motosikletler</translation>
<translation id="8790007591277257123">Silmeyi &amp;yeniden yap</translation>
@@ -2805,6 +2818,7 @@ Ek ayrıntılar:
<translation id="8806285662264631610">Banyo ve vücut ürünleri</translation>
<translation id="8807160976559152894">Her sayfadan sonra kırp</translation>
<translation id="8808828119384186784">Chrome Ayarları</translation>
+<translation id="8813277370772331957">Sonra hatırlat</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome ayarlarınızdan Chrome'u güncellemek için Sekme'ye, sonra Enter'a basın</translation>
<translation id="8820817407110198400">Yer iÅŸaretleri</translation>
<translation id="882338992931677877">Manuel Yuva</translation>
@@ -2984,6 +2998,7 @@ Ek ayrıntılar:
<translation id="988159990683914416">GeliÅŸtirici Derlemesi</translation>
<translation id="989988560359834682">Adresi Düzenle</translation>
<translation id="991413375315957741">hareket veya ışık sensörleri</translation>
+<translation id="992110854164447044">Sanal kart, gerçek kartınızı saklayarak olası sahtekarlıklardan korunmanıza yardımcı olur. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Pembe</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" yazılımı bilgisayarınıza veya ağa düzgün şekilde yüklenmemiş:
diff --git a/chromium/components/strings/components_strings_uk.xtb b/chromium/components/strings/components_strings_uk.xtb
index 7b7f783cf63..00a1d8768ee 100644
--- a/chromium/components/strings/components_strings_uk.xtb
+++ b/chromium/components/strings/components_strings_uk.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Гранти, Ñтипендії й фінанÑова допомога</translation>
<translation id="1048785276086539861">Коли ви редагуєте примітки, документ переходить у режим переглÑду по одній Ñторінці</translation>
<translation id="1050038467049342496">Закрийте інші додатки</translation>
+<translation id="1053959602163383901">Ви вибрали Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð·Ð° допомогою приÑтрою Ð´Ð»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— на веб-Ñайтах, Ñкі викориÑтовують <ph name="PROVIDER_ORIGIN" />. У цього поÑтачальника могла зберегтиÑÑŒ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ваш ÑпоÑіб оплати, але ви можете надіÑлати <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Відмінити додаваннÑ</translation>
<translation id="1056663316309890343">Програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¾Ð±Ñ€Ð¾Ð±ÐºÐ¸ фотографій</translation>
<translation id="1056898198331236512">ЗаÑтереженнÑ</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">СпоÑіб отриманнÑ</translation>
<translation id="1281476433249504884">Ðакопичувач 1</translation>
<translation id="1285320974508926690">Ðіколи не перекладати цей Ñайт</translation>
+<translation id="1288548991597756084">Ðадійно зберігайте Ñвою картку</translation>
<translation id="1292571435393770077">Лоток 16</translation>
<translation id="1292701964462482250">"Програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð½Ð° вашому комп’ютері перешкоджає веб-переглÑдачеві Chrome безпечно під’єднуватиÑÑ Ð´Ð¾ Інтернету" (лише Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¿â€™ÑŽÑ‚ÐµÑ€Ñ–Ð² з ОС Windows)</translation>
<translation id="1294154142200295408">Варіанти командного Ñ€Ñдка</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;Щоб виправити цю помилку, натиÑніть &lt;strong&gt;ПідключитиÑÑ&lt;/strong&gt; на Ñторінці, Ñку ви намагаєтеÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Ландшафтний дизайн</translation>
<translation id="1513706915089223971">СпиÑок запиÑів в Ñ–Ñторії</translation>
+<translation id="1516097932025103760">Дані картки буде зашифровано й безпечно збережено. Код CVC ніколи не запиÑуєтьÑÑ.</translation>
<translation id="1517433312004943670">Введіть номер телефону</translation>
<translation id="1519264250979466059">Дата ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²ÐµÑ€ÑÑ–Ñ—</translation>
<translation id="1521159554480556801">Виробництва волокна й тканин</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">Зберігати й заповнювати ÑпоÑоби оплати</translation>
<translation id="1663943134801823270">Дані картки та ÑпиÑок Ð°Ð´Ñ€ÐµÑ Ð¼Ñ–ÑÑ‚ÑÑ‚ÑŒÑÑ Ð² Chrome. Ðими можна керувати в <ph name="BEGIN_LINK" />ÐалаштуваннÑÑ…<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Ðадалі Ñторінки цією мовою (<ph name="SOURCE_LANGUAGE" />) перекладатимутьÑÑ Ñ‚Ð°ÐºÐ¾ÑŽ мовою: <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Щоб ÑкориÑтатиÑÑ Ð¿Ñ€Ð¾Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ñ”ÑŽ, здійÑніть оплату за допомогою картки <ph name="CARD_DETAIL" /></translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> – <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Спочатку по ширині</translation>
<translation id="168693727862418163">Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ правила в Ñхемі, тому воно ігноруватиметьÑÑ.</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">Датчики руху</translation>
<translation id="1717494416764505390">Поштова Ñкринька 3</translation>
<translation id="1718029547804390981">Документ завеликий Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð¼Ñ–Ñ‚Ð¾Ðº</translation>
+<translation id="1720941539803966190">Закрити навчальний поÑібник</translation>
<translation id="1721424275792716183">* Обов’Ñзкове поле</translation>
<translation id="1727613060316725209">Сертифікат дійÑний</translation>
<translation id="1727741090716970331">Додайте дійÑний номер картки</translation>
@@ -422,8 +427,8 @@
<translation id="205212645995975601">Барбекю та гриль</translation>
<translation id="2053111141626950936">Сторінки цією мовою (<ph name="LANGUAGE" />) не перекладатимутьÑÑ.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Коли цей елемент ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾ й активовано, Chrome аналізує ваші нещодавні дії у веб-переглÑдачі та визначає, до Ñкої великої групи чи "когорти" людей Ð²Ð°Ñ Ð²Ñ–Ð´Ð½ÐµÑти. Рекламодавці можуть вибирати Ð¾Ð³Ð¾Ð»Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи. Ваші дії у веб-переглÑдачі конфіденційно зберігаютьÑÑ Ð½Ð° приÑтрої. Ваша група оновлюєтьÑÑ Ñ‰Ð¾Ð´Ð½Ñ.}=1{Коли цей елемент ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾ й активовано, Chrome аналізує ваші нещодавні дії у веб-переглÑдачі та визначає, до Ñкої великої групи чи "когорти" людей Ð²Ð°Ñ Ð²Ñ–Ð´Ð½ÐµÑти. Рекламодавці можуть вибирати Ð¾Ð³Ð¾Ð»Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи. Ваші дії у веб-переглÑдачі конфіденційно зберігаютьÑÑ Ð½Ð° приÑтрої. Ваша група оновлюєтьÑÑ Ñ‰Ð¾Ð´Ð½Ñ.}one{Коли цей елемент ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾ й активовано, Chrome аналізує ваші нещодавні дії у веб-переглÑдачі та визначає, до Ñкої великої групи чи "когорти" людей Ð²Ð°Ñ Ð²Ñ–Ð´Ð½ÐµÑти. Рекламодавці можуть вибирати Ð¾Ð³Ð¾Ð»Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи. Ваші дії у веб-переглÑдачі конфіденційно зберігаютьÑÑ Ð½Ð° приÑтрої. Ваша група оновлюєтьÑÑ ÐºÐ¾Ð¶ÐµÐ½ {NUM_DAYS} день.}few{Коли цей елемент ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾ й активовано, Chrome аналізує ваші нещодавні дії у веб-переглÑдачі та визначає, до Ñкої великої групи чи "когорти" людей Ð²Ð°Ñ Ð²Ñ–Ð´Ð½ÐµÑти. Рекламодавці можуть вибирати Ð¾Ð³Ð¾Ð»Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи. Ваші дії у веб-переглÑдачі конфіденційно зберігаютьÑÑ Ð½Ð° приÑтрої. Ваша група оновлюєтьÑÑ ÐºÐ¾Ð¶Ð½Ñ– {NUM_DAYS} дні.}many{Коли цей елемент ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾ й активовано, Chrome аналізує ваші нещодавні дії у веб-переглÑдачі та визначає, до Ñкої великої групи чи "когорти" людей Ð²Ð°Ñ Ð²Ñ–Ð´Ð½ÐµÑти. Рекламодавці можуть вибирати Ð¾Ð³Ð¾Ð»Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи. Ваші дії у веб-переглÑдачі конфіденційно зберігаютьÑÑ Ð½Ð° приÑтрої. Ваша група оновлюєтьÑÑ ÐºÐ¾Ð¶Ð½Ñ– {NUM_DAYS} днів.}other{Коли цей елемент ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾ й активовано, Chrome аналізує ваші нещодавні дії у веб-переглÑдачі та визначає, до Ñкої великої групи чи "когорти" людей Ð²Ð°Ñ Ð²Ñ–Ð´Ð½ÐµÑти. Рекламодавці можуть вибирати Ð¾Ð³Ð¾Ð»Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи. Ваші дії у веб-переглÑдачі конфіденційно зберігаютьÑÑ Ð½Ð° приÑтрої. Ваша група оновлюєтьÑÑ ÐºÐ¾Ð¶Ð½Ñ– {NUM_DAYS} днÑ.}}</translation>
-<translation id="2053553514270667976">Поштовий індекÑ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 пропозиціÑ}one{# пропозиціÑ}few{# пропозиції}many{# пропозицій}other{# пропозиції}}</translation>
+<translation id="2066915425250589881">запит на видаленнÑ</translation>
<translation id="2068528718802935086">ÐемовлÑта й малюки</translation>
<translation id="2071156619270205202">Указаний номер не підходить Ð´Ð»Ñ Ð²Ñ–Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð¾Ñ— картки.</translation>
<translation id="2071692954027939183">Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾ заблоковано, оÑкільки ви зазвичай не дозволÑєте Ñ—Ñ…</translation>
@@ -435,7 +440,6 @@
<translation id="2088086323192747268">Кнопка "Керувати Ñинхронізацією"; натиÑніть Enter, щоб вибрати в налаштуваннÑÑ… Chrome, Ñку інформацію Ñинхронізувати</translation>
<translation id="2091887806945687916">Звук</translation>
<translation id="2094505752054353250">ÐевідповідніÑÑ‚ÑŒ домену</translation>
-<translation id="2096368010154057602">Департамент</translation>
<translation id="2099652385553570808">Скріпити тричі ліворуч</translation>
<translation id="2101225219012730419">ВерÑÑ–Ñ:</translation>
<translation id="2102134110707549001">Запропонувати надійний пароль…</translation>
@@ -472,7 +476,6 @@
<translation id="2185836064961771414">ÐмериканÑький футбол</translation>
<translation id="2187317261103489799">Визначати (за умовчаннÑм)</translation>
<translation id="2188375229972301266">Пробити кілька отворів унизу</translation>
-<translation id="2188852899391513400">Введений пароль розкрито через Ð¿Ð¾Ñ€ÑƒÑˆÐµÐ½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ даних. Щоб захиÑтити ваші облікові запиÑи, Менеджер паролів Google радить негайно його змінити й перевірити збережені паролі.</translation>
<translation id="219906046732893612">ÐžÐ±Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¶Ð¸Ñ‚Ð»Ð°</translation>
<translation id="2202020181578195191">Введіть дійÑний рік Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ñ‚ÐµÑ€Ð¼Ñ–Ð½Ñƒ дії</translation>
<translation id="22081806969704220">Лоток 3</translation>
@@ -483,6 +486,8 @@
<translation id="2215727959747642672">Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ</translation>
<translation id="2215963164070968490">Собаки</translation>
<translation id="2218879909401188352">ЗловмиÑники можуть викориÑтати Ñайт <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, щоб уÑтановити небезпечні додатки, Ñкі шкодÑÑ‚ÑŒ приÑтроÑм, наприклад, додають приховані платежі за мобільний зв’Ñзок або викрадають оÑобиÑту інформацію. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ПереглÑнути навчальний поÑібник знову</translation>
+<translation id="2219735899272417925">Потрібно Ñкинути Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ñтрою</translation>
<translation id="2224337661447660594">Ðемає Інтернету</translation>
<translation id="2230458221926704099">Відновіть Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ð° допомогою <ph name="BEGIN_LINK" />додатка Ð´Ð»Ñ Ð´Ñ–Ð°Ð³Ð½Ð¾Ñтики<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">ÐадіÑлати</translation>
@@ -580,11 +585,13 @@
<translation id="2512101340618156538">Заборонено (за умовчаннÑм)</translation>
<translation id="2512413427717747692">Кнопка "Зробити Chrome веб-переглÑдачем за умовчаннÑм": щоб зробити Chrome веб-переглÑдачем ÑиÑтеми за умовчаннÑм у налаштуваннÑÑ… iOS, натиÑніть Enter</translation>
<translation id="2515629240566999685">перевірити Ñигнал у Ñвоїй міÑцевоÑÑ‚Ñ–</translation>
+<translation id="2515761554693942801">Ви вибрали Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð·Ð° допомогою Touch ID на веб-Ñайтах, Ñкі викориÑтовують <ph name="PROVIDER_ORIGIN" />. У цього поÑтачальника могла зберегтиÑÑŒ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ваш ÑпоÑіб оплати, але ви можете надіÑлати <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Скріпити внизу праворуч</translation>
<translation id="2521736961081452453">Створити форму</translation>
<translation id="2523886232349826891">Збережено лише на цьому приÑтрої</translation>
<translation id="2524461107774643265">Додайте більше інформації</translation>
<translation id="2529899080962247600">МакÑимальна кількіÑÑ‚ÑŒ запиÑів у цьому полі: <ph name="MAX_ITEMS_LIMIT" />. УÑÑ– подальші запиÑи ігноруватимутьÑÑ.</translation>
+<translation id="253493526287553278">ПереглÑнути деталі промокоду</translation>
<translation id="2535585790302968248">Відкрийте нову анонімну вкладку</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{і ще 1}one{і ще #}few{і ще #}many{і ще #}other{і ще #}}</translation>
<translation id="2536110899380797252">Додати адреÑу</translation>
@@ -620,7 +627,6 @@
<translation id="259821504105826686">МиÑтецтво фотографії й цифрове миÑтецтво</translation>
<translation id="2601150049980261779">Романтичні фільми</translation>
<translation id="2604589665489080024">Поп-музика</translation>
-<translation id="2609632851001447353">Різновиди</translation>
<translation id="2610561535971892504">ÐатиÑнути, щоб Ñкопіювати</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />не зберігатиме<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Дні Ð½Ð°Ñ€Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð¹ іменини</translation>
<translation id="2677748264148917807">Вийти</translation>
+<translation id="2679714844901977852">Збережіть Ñвою картку та платіжну інформацію в обліковому запиÑÑ– Google <ph name="USER_EMAIL" />, щоб Ñплачувати безпечно й швидко</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Оптимальний розмір</translation>
<translation id="2688969097326701645">Так, продовжити</translation>
@@ -701,7 +708,6 @@
<translation id="2854764410992194509">ПоÑтачальники поÑлуг Інтернету (ISP)</translation>
<translation id="2856444702002559011">ЗловмиÑники можуть намагатиÑÑ Ð²Ð¸ÐºÑ€Ð°Ñти вашу інформацію із Ñайту <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (наприклад, паролі, Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‡Ð¸ дані кредитних карток). <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Цей Ñайт показує нав’Ñзливі чи оманливі оголошеннÑ.</translation>
-<translation id="286512204874376891">Щоб захиÑтити Ð²Ð°Ñ Ð²Ñ–Ð´ потенційного шахрайÑтва, заміÑÑ‚ÑŒ Ñправжньої картки буде викориÑтовуватиÑÑ Ð²Ñ–Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð°. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">ПриÑзний</translation>
<translation id="28761159517501904">Фільми</translation>
<translation id="2876489322757410363">Щоб оплатити в зовнішньому додатку, ви вийдете з режиму анонімного переглÑду. Продовжити?</translation>
@@ -802,7 +808,6 @@
<translation id="3158539265159265653">ДиÑк</translation>
<translation id="3162559335345991374">Можливо, щоб під’єднатиÑÑ Ð´Ð¾ цієї мережі Wi-Fi, потрібно відвідати Ñ—Ñ— Ñторінку входу.</translation>
<translation id="3169472444629675720">Рекомендації</translation>
-<translation id="3174168572213147020">ОÑтрів</translation>
<translation id="3176929007561373547">Перевірте Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñвого прокÑÑ–-Ñервера чи звернітьÑÑ Ð´Ð¾ адмініÑтратора мережі,
щоб переконатиÑÑ, що прокÑÑ–-Ñервер працює. Якщо ви вважаєте, що не потрібно
викориÑтовувати прокÑÑ–-Ñервер, виконайте вказані нижче дії.
@@ -881,9 +886,6 @@
<translation id="3369192424181595722">Помилка годинника</translation>
<translation id="3369459162151165748">ЗапаÑні чаÑтини й акÑеÑуари Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð¾Ð±Ñ–Ð»Ñ–Ð²</translation>
<translation id="3371064404604898522">Зробити Chrome веб-переглÑдачем за умовчаннÑм</translation>
-<translation id="3371076217486966826">URL-адреÑÑ– потрібен дозвіл, щоб <ph name="URL" />:
- • Ñтворити 3D-карту вашого Ð¾Ñ‚Ð¾Ñ‡ÐµÐ½Ð½Ñ Ñ‚Ð° відÑтежувати Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ ÐºÐ°Ð¼ÐµÑ€Ð¸;
- • викориÑтовувати вашу камеру.</translation>
<translation id="337363190475750230">Деініціалізовано</translation>
<translation id="3375754925484257129">Виконати перевірку безпеки Chrome</translation>
<translation id="3377144306166885718">Сервер викориÑтовував заÑтарілу верÑÑ–ÑŽ TLS.</translation>
@@ -900,6 +902,7 @@
<translation id="3399952811970034796">ÐдреÑа доÑтавки</translation>
<translation id="3402261774528610252">Цей веб-Ñайт викориÑтовує протоколи TLS верÑÑ–Ñ— 1.0 або 1.1, Ñкі вже не підтримуютьÑÑ. Коли Ñ—Ñ… буде вимкнено, кориÑтувачі більше не зможуть його завантажувати. Ðа Ñервері має бути вÑтановлено протокол TLS 1.2 або новішої верÑÑ–Ñ—.</translation>
<translation id="3405664148539009465">Ðалаштувати шрифти</translation>
+<translation id="3407789382767355356">вхід через Ñторонні ÑервіÑи</translation>
<translation id="3409896703495473338">Керувати налаштуваннÑми безпеки</translation>
<translation id="3414952576877147120">Розмір:</translation>
<translation id="3417660076059365994">Файли, Ñкі ви додаєте або долучаєте, надÑилаютьÑÑ Ð½Ð° платформу Google Cloud або Ñтороннім ÑервіÑам на аналіз. Ðаприклад, Ñ—Ñ… можуть Ñканувати на наÑвніÑÑ‚ÑŒ конфіденційних даних або зловмиÑного програмного забезпеченнÑ.</translation>
@@ -932,6 +935,7 @@
<translation id="3477679029130949506">Кіноафіши й години показу фільмів</translation>
<translation id="3479552764303398839">Ðе зараз</translation>
<translation id="3484560055331845446">Ви можете втратити доÑтуп до облікового запиÑу Google. Chrome радить змінити пароль. Вам буде запропоновано ввійти в обліковий запиÑ.</translation>
+<translation id="3484861421501147767">ÐагадуваннÑ: вам доÑтупний збережений промокод</translation>
<translation id="3487845404393360112">Лоток 4</translation>
<translation id="3495081129428749620">Знайти на Ñторінці
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@
<translation id="3810973564298564668">Керувати</translation>
<translation id="3816482573645936981">Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ (замінено)</translation>
<translation id="382518646247711829">Якщо ви викориÑтовуєте прокÑÑ–-Ñервер…</translation>
+<translation id="3826050100957962900">Вхід через Ñторонні ÑервіÑи</translation>
<translation id="3827112369919217609">ÐбÑолютна</translation>
<translation id="3827666161959873541">Сімейні фільми</translation>
<translation id="3828924085048779000">ÐŸÐ¾Ñ€Ð¾Ð¶Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŒÐ½Ð° фраза заборонена.</translation>
@@ -1068,9 +1073,9 @@
<translation id="3858027520442213535">Оновити дату й чаÑ</translation>
<translation id="3858860766373142691">Ðазва</translation>
<translation id="3872834068356954457">Ðаука</translation>
+<translation id="3875783148670536197">Докладніше</translation>
<translation id="3881478300875776315">Показати менше Ñ€Ñдків</translation>
<translation id="3884278016824448484">Конфліктуючий ідентифікатор приÑтрою</translation>
-<translation id="3885155851504623709">Цивільний округ</translation>
<translation id="388632593194507180">ВиÑвлено відÑтеженнÑ</translation>
<translation id="3886948180919384617">Ðакопичувач 3</translation>
<translation id="3890664840433101773">Додати електронну адреÑу</translation>
@@ -1100,9 +1105,11 @@
<translation id="3973234410852337861">ХоÑÑ‚ <ph name="HOST_NAME" /> заблокований</translation>
<translation id="3978338123949022456">Режим пошуку: щоб здійÑнити пошук на <ph name="KEYWORD_SUFFIX" />, введіть запит Ñ– натиÑніть Enter</translation>
<translation id="398470910934384994">Птахи</translation>
+<translation id="3985750352229496475">Керувати адреÑами…</translation>
<translation id="3986705137476756801">Ðаразі вимкнути живі Ñубтитри</translation>
<translation id="3987940399970879459">Менше 1 МБ</translation>
<translation id="3990250421422698716">ПоÑтупове зміщеннÑ</translation>
+<translation id="3992684624889376114">Про цю Ñторінку</translation>
<translation id="3996311196211510766">Сайт <ph name="ORIGIN" /> проÑить, щоб правило джерела
заÑтоÑовувалоÑÑ Ð´Ð¾ вÑÑ–Ñ… його запитів, однак наразі це неможливо.</translation>
<translation id="4006465311664329701">СпоÑоби оплати, пропозиції й адреÑи з Google Pay</translation>
@@ -1227,6 +1234,7 @@
<translation id="4305666528087210886">Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ доÑтуп до файлу</translation>
<translation id="4306529830550717874">Зберегти адреÑу?</translation>
<translation id="4306812610847412719">буфер обміну</translation>
+<translation id="4310070645992025887">Пошук у ÑеанÑах</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Блокувати (за умовчаннÑм)</translation>
<translation id="4314815835985389558">Керувати Ñинхронізацією</translation>
@@ -1277,6 +1285,7 @@
<translation id="4435702339979719576">ЛиÑтівка)</translation>
<translation id="443673843213245140">ВикориÑÑ‚Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾ÐºÑÑ–-Ñервера вимкнено, але чітко вказано Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾ÐºÑÑ–-Ñервера.</translation>
<translation id="4441832193888514600">ІгноруєтьÑÑ, оÑкільки це правило можна налаштувати лише Ñк хмарне правило Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів.</translation>
+<translation id="4442470707340296952">Вкладки Chrome</translation>
<translation id="4450893287417543264">Більше не показувати</translation>
<translation id="4451135742916150903">Може проÑити дозвіл підключатиÑÑ Ð´Ð¾ приÑтроїв HID</translation>
<translation id="4452328064229197696">Введений пароль розкрито через Ð¿Ð¾Ñ€ÑƒÑˆÐµÐ½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ даних. Щоб захиÑтити ваші облікові запиÑи, Менеджер паролів Google радить перевірити збережені паролі.</translation>
@@ -1415,6 +1424,7 @@
<translation id="483241715238664915">Увімкнути попередженнÑ</translation>
<translation id="4834250788637067901">СпоÑоби оплати, пропозиції й адреÑи з Google Pay</translation>
<translation id="4838327282952368871">Мрійливий</translation>
+<translation id="4839087176073128681">Швидше оплачуйте покупки й захиÑÑ‚Ñ–Ñ‚ÑŒ Ñвою картку за допомогою провідних галузевих технологій безпеки Google.</translation>
<translation id="4840250757394056958">ПереглÑнути Ñ–Ñторію Chrome</translation>
<translation id="484462545196658690">Ðвтоматично</translation>
<translation id="484671803914931257">Отримайте знижку в Ð¿Ñ€Ð¾Ð´Ð°Ð²Ñ†Ñ "<ph name="MERCHANT_NAME" />" та інших магазинах</translation>
@@ -1477,6 +1487,7 @@
<translation id="5011561501798487822">Визначена мова</translation>
<translation id="5015510746216210676">Ðазва приÑтрою:</translation>
<translation id="5017554619425969104">Скопійований текÑÑ‚</translation>
+<translation id="5017828934289857214">Ðагадати пізніше</translation>
<translation id="5018422839182700155">Ðе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ цю Ñторінку</translation>
<translation id="5019198164206649151">Резервний ноÑій пошкоджено</translation>
<translation id="5020776957610079374">Музика народів Ñвіту</translation>
@@ -1496,6 +1507,7 @@
<translation id="5051305769747448211">ВиÑтупи коміків наживо</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Щоб надіÑлати цей файл через функцію "ÐŸÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð±Ð»Ð¸Ð·Ñƒ", звільніть міÑце (<ph name="DISK_SPACE_SIZE" />) на приÑтрої}one{Щоб надіÑлати ці файли через функцію "ÐŸÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð±Ð»Ð¸Ð·Ñƒ", звільніть міÑце (<ph name="DISK_SPACE_SIZE" />) на приÑтрої}few{Щоб надіÑлати ці файли через функцію "ÐŸÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð±Ð»Ð¸Ð·Ñƒ", звільніть міÑце (<ph name="DISK_SPACE_SIZE" />) на приÑтрої}many{Щоб надіÑлати ці файли через функцію "ÐŸÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð±Ð»Ð¸Ð·Ñƒ", звільніть міÑце (<ph name="DISK_SPACE_SIZE" />) на приÑтрої}other{Щоб надіÑлати ці файли через функцію "ÐŸÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð±Ð»Ð¸Ð·Ñƒ", звільніть міÑце (<ph name="DISK_SPACE_SIZE" />) на приÑтрої}}</translation>
<translation id="5056549851600133418">Статті Ð´Ð»Ñ Ð²Ð°Ñ</translation>
+<translation id="5060483733937416656">Ви вибрали Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð·Ð° допомогою Windows Hello на веб-Ñайтах, Ñкі викориÑтовують <ph name="PROVIDER_ORIGIN" />. У цього поÑтачальника могла зберегтиÑÑŒ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ваш ÑпоÑіб оплати, але ви можете надіÑлати <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Можливо, ви мали на увазі <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð´Ñ€ÑƒÐºÑƒ</translation>
<translation id="5068234115460527047">Хедж-фонди</translation>
@@ -1509,10 +1521,8 @@
<translation id="5087286274860437796">Сертифікат Ñервера зараз недійÑний.</translation>
<translation id="5087580092889165836">Додати картку</translation>
<translation id="5088142053160410913">ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ‚Ð¾Ñ€Ñƒ</translation>
-<translation id="5089810972385038852">Штат/провінціÑ</translation>
<translation id="5093232627742069661">Зігнути гармошкою</translation>
<translation id="5094747076828555589">Цей Ñервер не зміг довеÑти, що він – домен <ph name="DOMAIN" />. Chromium не вважає його Ñертифікат безпеки надійним. Імовірні причини: неправильна ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð°Ð±Ð¾ хтоÑÑŒ намагаєтьÑÑ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð¿Ð¸Ñ‚Ð¸ ваше з’єднаннÑ.</translation>
-<translation id="5095208057601539847">ПровінціÑ</translation>
<translation id="5097099694988056070">СтатиÑтика приÑтрою, Ñк-от викориÑÑ‚Ð°Ð½Ð½Ñ Ð¦ÐŸ чи оперативної пам’ÑÑ‚Ñ–</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Сайт не захищений</translation>
@@ -1550,6 +1560,7 @@
<translation id="5171045022955879922">Введіть запит або URL-адреÑу</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Комп’ютер</translation>
+<translation id="5177076414499237632">Докладніше про джерело й тему цієї Ñторінки</translation>
<translation id="5179510805599951267">Це не <ph name="ORIGINAL_LANGUAGE" />? Повідомте про помилку</translation>
<translation id="518639307526414276">Корм Ð´Ð»Ñ Ñ‚Ð²Ð°Ñ€Ð¸Ð½ Ñ– товари Ð´Ð»Ñ Ð´Ð¾Ð³Ð»Ñду</translation>
<translation id="5190835502935405962">Панель закладок</translation>
@@ -1710,6 +1721,7 @@
<translation id="5624120631404540903">Керувати паролÑми</translation>
<translation id="5629630648637658800">Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ правила</translation>
<translation id="5631439013527180824">ÐедійÑний маркер ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ñтрою</translation>
+<translation id="5632485077360054581">Докладніше</translation>
<translation id="5633066919399395251">ЗловмиÑники можуть викориÑтати Ñайт <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, щоб уÑтановити на ваш комп’ютер небезпечні програми, що викрадають або видалÑÑŽÑ‚ÑŒ інформацію (фотографії, паролі, повідомленнÑб дані кредитних карток тощо). <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Заблоковано оманливий вміÑÑ‚.</translation>
<translation id="5633259641094592098">Культові й альтернативні фільми</translation>
@@ -1827,6 +1839,7 @@
<translation id="5989320800837274978">Ðе вказано ні фікÑованих прокÑÑ–-Ñерверів, ні URL-Ð°Ð´Ñ€ÐµÑ Ñценарію .pac.</translation>
<translation id="5992691462791905444">Зігнути гармошкою</translation>
<translation id="5995727681868049093">Керуйте Ñвоєю інформацією, конфіденційніÑÑ‚ÑŽ й безпекою в обліковому запиÑÑ– Google</translation>
+<translation id="5997247540087773573">Введений пароль розкрито через Ð¿Ð¾Ñ€ÑƒÑˆÐµÐ½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ даних. Щоб захиÑтити ваші облікові запиÑи, Менеджер паролів Google радить негайно його змінити й перевірити збережені паролі.</translation>
<translation id="6000758707621254961">Результатів Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ "<ph name="SEARCH_TEXT" />": <ph name="RESULT_COUNT" /></translation>
<translation id="6006484371116297560">КлаÑична</translation>
<translation id="6008122969617370890">ПорÑдок від N до 1</translation>
@@ -1922,7 +1935,6 @@
<translation id="627746635834430766">Щоб наÑтупного разу платити швидше, збережіть дані картки та платіжну адреÑу в обліковому запиÑÑ– Google.</translation>
<translation id="6279183038361895380">ÐатиÑніть |<ph name="ACCELERATOR" />|, щоб побачити курÑор</translation>
<translation id="6280223929691119688">Ðеможливо доÑтавити Ð·Ð°Ð¼Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð° цією адреÑою. Укажіть іншу адреÑу.</translation>
-<translation id="6282194474023008486">Поштовий код</translation>
<translation id="6285507000506177184">Кнопка "Керувати завантаженнÑми в Chrome"; натиÑніть Enter, щоб керувати завантаженими файлами в Chrome</translation>
<translation id="6289939620939689042">Колір Ñторінки</translation>
<translation id="6290238015253830360">Тут відображатимутьÑÑ Ñ€ÐµÐºÐ¾Ð¼ÐµÐ½Ð´Ð¾Ð²Ð°Ð½Ñ– Ñтатті</translation>
@@ -1944,6 +1956,7 @@
<translation id="6337133576188860026">ЗвільнÑÑ” менше <ph name="SIZE" />. ДеÑкі Ñайти можуть завантажуватиÑÑ Ð¿Ð¾Ð²Ñ–Ð»ÑŒÐ½Ñ–ÑˆÐµ під Ñ‡Ð°Ñ Ð½Ð°Ñтупного відвідуваннÑ.</translation>
<translation id="6337534724793800597">Фільтрувати правила за назвою</translation>
<translation id="6340739886198108203">Згідно з правилом адмініÑтратора не рекомендовано здійÑнювати Ð·Ð°Ð¿Ð¸Ñ ÐµÐºÑ€Ð°Ð½Ð°, коли на ньому видно конфіденційний контент:</translation>
+<translation id="6348220984832452017">Ðктивні варіанти</translation>
<translation id="6349101878882523185">УÑтановіть додаток <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Ðемає}=1{1 пароль (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронізовано)}=2{2 паролі (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронізовано)}one{# пароль (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронізовано)}few{# паролі (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронізовано)}many{# паролів (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронізовано)}other{# Ð¿Ð°Ñ€Ð¾Ð»Ñ (Ð´Ð»Ñ <ph name="DOMAIN_LIST" />, Ñинхронізовано)}}</translation>
<translation id="6355392890578844978">Цим веб-переглÑдачем не керує адмініÑтратор компанії чи іншої організації. ДіÑми на цьому приÑтрої можна керувати за межами Chromium. <ph name="BEGIN_LINK" />Докладніше<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@
<translation id="643051589346665201">Змінити пароль Google</translation>
<translation id="6433490469411711332">Змінити контактні дані</translation>
<translation id="6433595998831338502">ХоÑÑ‚ <ph name="HOST_NAME" /> відхилив запит на з’єднаннÑ.</translation>
-<translation id="6438025220577812695">Змінити вручну</translation>
<translation id="6440503408713884761">ІгноруєтьÑÑ</translation>
<translation id="6443406338865242315">вÑтановлені Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð° плагіни;</translation>
<translation id="6446163441502663861">Kahu (конверт)</translation>
@@ -2105,9 +2117,9 @@
<translation id="6828866289116430505">Генетика</translation>
<translation id="6831043979455480757">ПереклаÑти</translation>
<translation id="6833752742582340615">Збережіть Ñвою картку й платіжну інформацію в обліковому запиÑÑ– Google, щоб Ñплачувати безпечно й швидко</translation>
-<translation id="6839929833149231406">ОблаÑÑ‚ÑŒ</translation>
<translation id="6846340164947227603">СкориÑтатиÑÑ Ð½Ð¾Ð¼ÐµÑ€Ð¾Ð¼ віртуальної картки…</translation>
<translation id="6852204201400771460">ПерезапуÑтити додаток?</translation>
+<translation id="6857776781123259569">Керувати паролÑми…</translation>
<translation id="686485648936420384">РеÑурÑи Ð´Ð»Ñ Ñпоживачів</translation>
<translation id="6865412394715372076">Зараз цю картку не можна підтвердити</translation>
<translation id="6869334554832814367">Кредити фізичним оÑобам</translation>
@@ -2156,7 +2168,6 @@
<translation id="6965978654500191972">ПриÑтрій</translation>
<translation id="696703987787944103">Перцептивна</translation>
<translation id="6968269510885595029">ВикориÑтовувати ваш ключ безпеки</translation>
-<translation id="6970216967273061347">Округ</translation>
<translation id="6971439137020188025">Швидко Ñтворюйте нові презентації в Google ПрезентаціÑÑ…</translation>
<translation id="6972629891077993081">ПриÑтрої HID</translation>
<translation id="6973656660372572881">Указано фікÑовані прокÑÑ–-Ñервери та URL-адреÑа Ñценарію .pac.</translation>
@@ -2195,7 +2206,6 @@
<translation id="7081308185095828845">Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð½ÐµÐ´Ð¾Ñтупна на вашому приÑтрої</translation>
<translation id="7083258188081898530">Лоток 9</translation>
<translation id="7086090958708083563">Запит на Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ кориÑтувача</translation>
-<translation id="7087282848513945231">Країна</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />; натиÑніть Tab, а потім – Enter, щоб керувати в налаштуваннÑÑ… Chrome дозволами й даними, що зберігаютьÑÑ Ð½Ð° Ñайтах</translation>
<translation id="7096937462164235847">Ідентифікаційні дані цього веб-Ñайту не підтверджено.</translation>
<translation id="7101893872976785596">Фільми жахів</translation>
@@ -2214,10 +2224,10 @@
<ph name="LIST_ITEM" />інформацію, Ñку ви вводите у формах.<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Ðеможливо відправити Ð·Ð°Ð¼Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð° цю адреÑу. Укажіть іншу адреÑу.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Ваш адмініÑтратор заборонив копіювати ці дані.</translation>
<translation id="7135130955892390533">Показати ÑтатуÑ</translation>
<translation id="7138472120740807366">СпоÑіб доÑтавки</translation>
-<translation id="7139724024395191329">Емірат</translation>
<translation id="7139892792842608322">ОÑновний рулон</translation>
<translation id="714064300541049402">Горизонтальний зÑув Ñторони 2 зображеннÑ</translation>
<translation id="7152423860607593928">Number-14 (конверт)</translation>
@@ -2434,6 +2444,7 @@
<translation id="7669271284792375604">ЗловмиÑники на цьому Ñайті можуть обманом змуÑити Ð²Ð°Ñ ÑƒÑтановити програми, Ñкі погіршують роботу в Інтернеті (наприклад, змінюють вашу домашню Ñторінку або показують додаткову рекламу на Ñайтах, Ñкі ви відвідуєте).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Дії з даними, Ñкі позначено Ñк конфіденційні (з моменту входу повідомлено про 1 дію). <ph name="BEGIN_LINK" />Докладніше<ph name="END_LINK" />}one{Дії з даними, Ñкі позначено Ñк конфіденційні (з моменту входу повідомлено про # дію). <ph name="BEGIN_LINK" />Докладніше<ph name="END_LINK" />}few{Дії з даними, Ñкі позначено Ñк конфіденційні (з моменту входу повідомлено про # дії). <ph name="BEGIN_LINK" />Докладніше<ph name="END_LINK" />}many{Дії з даними, Ñкі позначено Ñк конфіденційні (з моменту входу повідомлено про # дій). <ph name="BEGIN_LINK" />Докладніше<ph name="END_LINK" />}other{Дії з даними, Ñкі позначено Ñк конфіденційні (з моменту входу повідомлено про # дії). <ph name="BEGIN_LINK" />Докладніше<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Поштова Ñкринька 6</translation>
+<translation id="7675325315208090829">Керувати ÑпоÑобами оплати…</translation>
<translation id="7676643023259824263">Пошук текÑту з буфера обміну: <ph name="TEXT" /></translation>
<translation id="7679367271685653708">ПереглÑнути Ñ–Ñторію веб-переглÑду та керувати нею в налаштуваннÑÑ… Chrome</translation>
<translation id="7679947978757153706">БейÑбол</translation>
@@ -2476,7 +2487,6 @@
<translation id="7766518757692125295">З полÑми</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Однаковий порÑдок лицевою Ñтороною вгору</translation>
-<translation id="777702478322588152">Префектура</translation>
<translation id="7791011319128895129">Ðе випущено</translation>
<translation id="7791196057686275387">Скріпити</translation>
<translation id="7791543448312431591">Додати</translation>
@@ -2567,12 +2577,12 @@
<translation id="8055534648776115597">ПрофеÑійно-технічна й піÑлÑдипломна оÑвіта</translation>
<translation id="8057711352706143257"><ph name="SOFTWARE_NAME" /> налаштовано неправильно. Якщо видалити програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ <ph name="SOFTWARE_NAME" />, проблема зазвичай зникає. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Виробництво харчових продуктів</translation>
-<translation id="8066955247577885446">СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°.</translation>
<translation id="8067872629359326442">Щойно ви ввели пароль на оманливому Ñайті. Chromium може допомогти. Щоб змінити пароль Ñ– повідомити Google про можливу небезпеку Ð´Ð»Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу, натиÑніть "ЗахиÑтити обліковий запиÑ".</translation>
<translation id="8070439594494267500">Значок додатка</translation>
<translation id="8074253406171541171">10x13 (конверт)</translation>
<translation id="8075736640322370409">Швидко Ñтворюйте нові таблиці Google</translation>
<translation id="8075898834294118863">Керувати налаштуваннÑми Ñайтів</translation>
+<translation id="8076492880354921740">Вкладки</translation>
<translation id="8078141288243656252">Коли документ обернено, ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð¼Ñ–Ñ‚Ð¾Ðº недоÑтупне</translation>
<translation id="8079031581361219619">Оновити Ñайт?</translation>
<translation id="8081087320434522107">Седани</translation>
@@ -2695,6 +2705,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ÐалаштуваннÑ</translation>
+<translation id="8428634594422941299">Зрозуміло</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />; натиÑніть Tab, а потім – Enter, щоб керувати параметрами файлів cookie в налаштуваннÑÑ… Chrome</translation>
<translation id="8433057134996913067">Ви вийдете з облікового запиÑу на більшоÑÑ‚Ñ– веб-Ñайтів.</translation>
<translation id="8434840396568290395">Домашні тварини</translation>
@@ -2793,6 +2804,7 @@
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> – ваш код Ð´Ð»Ñ Ñайту <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Зробити закладку Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— вкладки</translation>
<translation id="8751426954251315517">Повторіть Ñпробу пізніше</translation>
+<translation id="8757526089434340176">ДоÑтупна Ð¿Ñ€Ð¾Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ñ Google Pay</translation>
<translation id="8758885506338294482">Ð—Ð¼Ð°Ð³Ð°Ð½Ð½Ñ Ð· відеоігор</translation>
<translation id="8759274551635299824">Термін дії цієї картки минув</translation>
<translation id="87601671197631245">Ðа цьому Ñайті викориÑтовуєтьÑÑ Ð·Ð°Ñтаріла ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸. Якщо ви надÑилатимете на нього ваші дані (Ñк-от паролі, Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¹ інформацію про кредитні картки), Ñ—Ñ… може бути розкрито.</translation>
@@ -2800,6 +2812,7 @@
<translation id="8763927697961133303">ПриÑтрій USB</translation>
<translation id="8763986294015493060">Закрийте вÑÑ– відкриті вікна в режимі анонімного переглÑду</translation>
<translation id="8766943070169463815">Екран автентифікації облікових даних Ð´Ð»Ñ Ð±ÐµÐ·Ð¿ÐµÑ‡Ð½Ð¸Ñ… платежів відкрито</translation>
+<translation id="8767765348545497220">Закрити Ñпливаючу підказку</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Мотоцикли</translation>
<translation id="8790007591277257123">&amp;Повторити видаленнÑ</translation>
@@ -2812,6 +2825,7 @@
<translation id="8806285662264631610">КоÑметика Ð´Ð»Ñ Ð²Ð°Ð½Ð½Ð¾Ñ— та доглÑду за тілом</translation>
<translation id="8807160976559152894">Обрізати піÑÐ»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ñ— Ñторінки</translation>
<translation id="8808828119384186784">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Chrome</translation>
+<translation id="8813277370772331957">Ðагадати пізніше</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />; натиÑніть Tab, а потім – Enter, щоб оновити веб-переглÑдач Chrome у його налаштуваннÑÑ…</translation>
<translation id="8820817407110198400">Закладки</translation>
<translation id="882338992931677877">Лоток Ð´Ð»Ñ Ñ€ÑƒÑ‡Ð½Ð¾Ð³Ð¾ введеннÑ</translation>
@@ -2991,6 +3005,7 @@
<translation id="988159990683914416">КонÑÑ‚Ñ€ÑƒÐºÑ†Ñ–Ñ Ñ€Ð¾Ð·Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ°</translation>
<translation id="989988560359834682">Редагувати адреÑу</translation>
<translation id="991413375315957741">датчики руху та Ñвітла</translation>
+<translation id="992110854164447044">Ð”Ð»Ñ Ð·Ð°Ñ…Ð¸Ñту від потенційного шахрайÑтва заміÑÑ‚ÑŒ Ñправжньої картки можна викориÑтовувати віртуальну. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Рожевий</translation>
<translation id="992432478773561401"><ph name="SOFTWARE_NAME" /> неправильно вÑтановлено на вашому комп’ютері або в мережі.
diff --git a/chromium/components/strings/components_strings_ur.xtb b/chromium/components/strings/components_strings_ur.xtb
index 73f04b4cdd3..dd7486bb326 100644
--- a/chromium/components/strings/components_strings_ur.xtb
+++ b/chromium/components/strings/components_strings_ur.xtb
@@ -28,6 +28,7 @@
<translation id="1043382569739532657">گرانٹس، اسکالرشپس اور مالی مدد</translation>
<translation id="1048785276086539861">آپ Ú©Û’ تشریحات میں ترمیم کرنے پر، ÛŒÛ Ø¯Ø³ØªØ§ÙˆÛŒØ² واحد صÙØ­Û’ Ú©Û’ منظر پر واپس Ø¢ جائے گا</translation>
<translation id="1050038467049342496">دیگر ایپس بند کریں</translation>
+<translation id="1053959602163383901">آپ Ù†Û’ <ph name="PROVIDER_ORIGIN" /> استعمال کرنے والی ویب سائٹس پر ایک مستند Ø¢Ù„Û Ø³Û’ تصدیق کرنے کا انتخاب کیا۔ ممکن ÛÛ’ Ú©Û Ø§Ø³ ÙراÛÙ… Ú©Ù†Ù†Ø¯Û Ú©Û’ پاس آپ Ú©ÛŒ ادائیگی Ú©Û’ Ø·Ø±ÛŒÙ‚Û Ú©Û’ بارے میں معلومات اسٹور Ú©Ø±Ø¯Û ÛÛ’ جن Ú©Ùˆ <ph name="LINK_TEXT" />آپ کر سکتے Ûیں۔</translation>
<translation id="1055184225775184556">شامل کریں کو &amp;کالعدم کریں</translation>
<translation id="1056663316309890343">تصویر کا ساÙÙ¹ ویئر</translation>
<translation id="1056898198331236512">وارننگ</translation>
@@ -120,6 +121,7 @@
<translation id="1270502636509132238">Ù¾ÙÚ© اپ کا طریقÛ</translation>
<translation id="1281476433249504884">اسٹیکر 1</translation>
<translation id="1285320974508926690">اس سائٹ کا ØªØ±Ø¬Ù…Û Ú©Ø¨Ú¾ÛŒ Ù†Û Ú©Ø±ÛŒÚº</translation>
+<translation id="1288548991597756084">کارڈ Ú©Ùˆ محÙوظ طریقے سے محÙوظ کریں</translation>
<translation id="1292571435393770077">ٹرے 16</translation>
<translation id="1292701964462482250">â€"آپ Ú©Û’ کمپیوٹر میں موجود ساÙÙ¹ ویئر Chrome Ú©Ùˆ محÙوظ طور پر ویب سے منسلک Ûونے سے روک رÛا ÛÛ’" (صر٠Windows کمپیوٹرز کیلئے)</translation>
<translation id="1294154142200295408">کمانڈ لائن کے تغیرات</translation>
@@ -224,6 +226,7 @@
&lt;p&gt;خرابی ٹھیک کرنے کیلئے، اس صÙØ­Û’ پر &lt;strong&gt;منسلک کریں&lt;/strong&gt; پر Ú©Ù„Ú© کریں جسے آپ کھولنے Ú©ÛŒ کوشش کر رÛÛ’ Ûیں۔&lt;/p&gt;</translation>
<translation id="1507780850870535225">لینڈ اسکیپ ڈیزائن</translation>
<translation id="1513706915089223971">سرگزشت Ú©Û’ اندراجات Ú©ÛŒ ÙÛرست</translation>
+<translation id="1516097932025103760">â€Ø§Ø³Û’ مرموز کیا جائے گا، محÙوظ طریقے سے محÙوظ کیا جائے گا اور CVC Ú©Ùˆ کبھی بھی اسٹور Ù†Ûیں کیا جائے گا۔</translation>
<translation id="1517433312004943670">Ùون نمبر درکار ÛÛ’</translation>
<translation id="1519264250979466059">بلڈ کی تاریخ</translation>
<translation id="1521159554480556801">Ùائبر اور ٹیکسٹائل آرٹس</translation>
@@ -289,6 +292,7 @@
<translation id="1662550410081243962">ادائیگی Ú©Û’ طریقوں Ú©Ùˆ محÙوظ کریں اور بھریں</translation>
<translation id="1663943134801823270">â€Ú©Ø§Ø±ÚˆØ² اور پتے Chrome Ú©ÛŒ جانب سے Ûیں۔ آپ <ph name="BEGIN_LINK" />ترتیبات<ph name="END_LINK" /> میں ان کا نظم کر سکتے Ûیں۔</translation>
<translation id="1671391448414634642">Ø¢Ø¦Ù†Ø¯Û Ø³Û’ <ph name="SOURCE_LANGUAGE" /> Ú©Û’ صÙحات کا <ph name="TARGET_LANGUAGE" /> میں ØªØ±Ø¬Ù…Û Ú©ÛŒØ§ جائے گا۔</translation>
+<translation id="1673886523110456987">پیشکش کو استعمال کرنے کے لیے <ph name="CARD_DETAIL" /> کے ساتھ چیک کریں</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> سے <ph name="TARGET_LANGUAGE" /> میں</translation>
<translation id="1682696192498422849">چھوٹا Ú©Ù†Ø§Ø±Û Ù¾ÛÙ„Û’</translation>
<translation id="168693727862418163">اس پالیسی Ú©ÛŒ قدر اس Ú©Û’ سکیما Ú©Ùˆ توثیق کرنے میں ناکام ÛÙˆ گئی اور اسے نظر انداز کر دیا جائے گا۔</translation>
@@ -307,6 +311,7 @@
<translation id="1717218214683051432">موشن سینسرز</translation>
<translation id="1717494416764505390">میل باکس 3</translation>
<translation id="1718029547804390981">دستاویز اتنی بڑی ÛÛ’ Ú©Û Ø§Ø³ Ú©ÛŒ تشریح Ù†Ûیں Ú©ÛŒ جا سکتی</translation>
+<translation id="1720941539803966190">ٹیوٹوریل بند کریں</translation>
<translation id="1721424275792716183">* Ùیلڈ لازمی ÛÛ’</translation>
<translation id="1727613060316725209">سرٹیÙیکیٹ درست ÛÛ’</translation>
<translation id="1727741090716970331">درست کارڈ نمبر شامل کریں</translation>
@@ -423,8 +428,8 @@
<translation id="205212645995975601">â€BBQ اور گرÙلنگ</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> Ú©Û’ صÙحات کا ØªØ±Ø¬Ù…Û Ù†Ûیں کیا جائے گا۔</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{â€Ø§Ø³ کنٹرول Ú©Û’ آن Ûونے اور اسٹیٹس Ú©Û’ Ùعال Ûونے پر، Chrome اس بات کا تعین کرتا ÛÛ’ Ú©Û Ù„ÙˆÚ¯ÙˆÚº Ú©Û’ کون سے بڑے گروپ یا "مشترک خصوصیات Ú©Û’ حامل لوگوں Ú©Û’ گروپ" سے آپ Ú©ÛŒ Ø­Ø§Ù„ÛŒÛ Ø¨Ø±Ø§Ø¤Ø²Ù†Ú¯ Ú©ÛŒ سرگرمی سب سے Ø²ÛŒØ§Ø¯Û Ù…Ù„ØªÛŒ جلتی ÛÛ’Û” مشتÛرین گروپ Ú©Û’ لیے اشتÛارات کا انتخاب کر سکتے Ûیں اور آپ Ú©ÛŒ براؤزنگ Ú©ÛŒ سرگرمی Ú©Ùˆ آپ Ú©Û’ آلے پر نجی رکھا جاتا ÛÛ’Û” آپ کا گروپ Ûر روز اپ ڈیٹ کیا جاتا ÛÛ’Û”}=1{â€Ø§Ø³ کنٹرول Ú©Û’ آن Ûونے اور اسٹیٹس Ú©Û’ Ùعال Ûونے پر، Chrome اس بات کا تعین کرتا ÛÛ’ Ú©Û Ù„ÙˆÚ¯ÙˆÚº Ú©Û’ کون سے بڑے گروپ یا "مشترک خصوصیات Ú©Û’ حامل لوگوں Ú©Û’ گروپ" سے آپ Ú©ÛŒ Ø­Ø§Ù„ÛŒÛ Ø¨Ø±Ø§Ø¤Ø²Ù†Ú¯ Ú©ÛŒ سرگرمی سب سے Ø²ÛŒØ§Ø¯Û Ù…Ù„ØªÛŒ جلتی ÛÛ’Û” مشتÛرین گروپ Ú©Û’ لیے اشتÛارات کا انتخاب کر سکتے Ûیں اور آپ Ú©ÛŒ براؤزنگ Ú©ÛŒ سرگرمی Ú©Ùˆ آپ Ú©Û’ آلے پر نجی رکھا جاتا ÛÛ’Û” آپ کا گروپ Ûر روز اپ ڈیٹ کیا جاتا ÛÛ’Û”}other{â€Ø§Ø³ کنٹرول Ú©Û’ آن Ûونے اور اسٹیٹس Ú©Û’ Ùعال Ûونے پر، Chrome اس بات کا تعین کرتا ÛÛ’ Ú©Û Ù„ÙˆÚ¯ÙˆÚº Ú©Û’ کون سے بڑے گروپ یا "مشترک خصوصیات Ú©Û’ حامل لوگوں Ú©Û’ گروپ" سے آپ Ú©ÛŒ Ø­Ø§Ù„ÛŒÛ Ø¨Ø±Ø§Ø¤Ø²Ù†Ú¯ Ú©ÛŒ سرگرمی سب سے Ø²ÛŒØ§Ø¯Û Ù…Ù„ØªÛŒ جلتی ÛÛ’Û” مشتÛرین گروپ Ú©Û’ لیے اشتÛارات کا انتخاب کر سکتے Ûیں اور آپ Ú©ÛŒ براؤزنگ Ú©ÛŒ سرگرمی Ú©Ùˆ آپ Ú©Û’ آلے پر نجی رکھا جاتا ÛÛ’Û” آپ کا گروپ Ûر {NUM_DAYS} دن میں اپ ڈیٹ کیا جاتا ÛÛ’Û”}}</translation>
-<translation id="2053553514270667976">زپ کوڈ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 تجویز}other{# تجاویز}}</translation>
+<translation id="2066915425250589881">حذ٠کرنے کی درخواست</translation>
<translation id="2068528718802935086">بچے اور نونÛال</translation>
<translation id="2071156619270205202">ÛŒÛ Ú©Ø§Ø±Úˆ ورچوئل کارڈ نمبر کیلئے اÛÙ„ Ù†Ûیں ÛÛ’Û”</translation>
<translation id="2071692954027939183">اطلاعات Ú©Ùˆ خودکار طور پر مسدود کر دیا گیا Ú©ÛŒÙˆÙ†Ú©Û Ø¢Ù¾ عموماً انÛیں اجازت Ù†Ûیں دیتے Ûیں</translation>
@@ -436,7 +441,6 @@
<translation id="2088086323192747268">â€Ù…طابقت پذیری Ú©Û’ بٹن کا نظم کریں، آپ Chrome ترتیبات میں Ú©Ù† معلومات Ú©Ùˆ مطابقت پذیر بناتے Ûیں ان کا نظم کرنے Ú©Û’ لئے اینٹر دبائیں</translation>
<translation id="2091887806945687916">آواز</translation>
<translation id="2094505752054353250">ڈومین کی عدم مماثلت</translation>
-<translation id="2096368010154057602">محکمÛ</translation>
<translation id="2099652385553570808">بائیں طر٠تین سٹیپل</translation>
<translation id="2101225219012730419">ورژن:</translation>
<translation id="2102134110707549001">مضبوط پاس ورڈ تجویز کریں…</translation>
@@ -473,7 +477,6 @@
<translation id="2185836064961771414">امریکی Ùٹبال</translation>
<translation id="2187317261103489799">پتا لگائیں (ÚˆÛŒÙالٹ)</translation>
<translation id="2188375229972301266">نیچے متعدد سوراخ</translation>
-<translation id="2188852899391513400">â€Ø¢Ù¾ Ù†Û’ ابھی جو پاس ورڈ استعمال کیا ÙˆÛ ÚˆÛŒÙ¹Ø§ Ú©ÛŒ خلا٠ورزی میں پایا گیا۔ اپنے اکاؤنٹس محÙوظ کرنے Ú©Û’ لیے، Google پاس ورڈ مینیجر اسے ابھی تبدیل کرنے اور پھر آپ Ú©Û’ محÙوظ Ú©Ø±Ø¯Û Ù¾Ø§Ø³ ورڈز Ú©Ùˆ چیک کرنے Ú©ÛŒ تجویز کرتا ÛÛ’Û”</translation>
<translation id="219906046732893612">گھریلو اصلاحات</translation>
<translation id="2202020181578195191">ایک درست سال٠اختتام درج کریں</translation>
<translation id="22081806969704220">ٹرے 3</translation>
@@ -484,6 +487,8 @@
<translation id="2215727959747642672">Ùائل میں ترمیم کریں</translation>
<translation id="2215963164070968490">کتے</translation>
<translation id="2218879909401188352">Ø­Ù…Ù„Û Ø¢ÙˆØ± ÙÛŒ الحال <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> پر آپ Ú©Û’ Ø¢Ù„Û Ú©Ùˆ نقصان Ù¾Ûنچانے والی، آپ Ú©Û’ موبائل بل پر مخÙÛŒ چارجز شامل کرنے والی یا آپ Ú©ÛŒ ذاتی معلومات Ú©ÛŒ چوری کرنے والی خطرناک ایپس انسٹال کر سکتے Ûیں۔<ph name="BEGIN_LEARN_MORE_LINK" />مزید جانیں<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">ٹیوٹوریل ری سٹارٹ کریں</translation>
+<translation id="2219735899272417925">آلے Ú©Ùˆ ری سیٹ کرنے Ú©ÛŒ ضرورت ÛÛ’</translation>
<translation id="2224337661447660594">انٹرنیٹ Ù†Ûیں ÛÛ’</translation>
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />تشخیصات ایپ<ph name="END_LINK" /> کا استعمال کر کے اپنا کنکشن درست کریں</translation>
<translation id="2239100178324503013">ابھی بھیجیں</translation>
@@ -581,11 +586,13 @@
<translation id="2512101340618156538">اجازت Ù†Ûیں ÛÛ’ (ÚˆÛŒÙالٹ)</translation>
<translation id="2512413427717747692">â€Chrome Ú©Ùˆ بطور ÚˆÛŒÙالٹ براؤزر بٹن سیٹ کریں، iOS ترتیبات میں Chrome Ú©Ùˆ سسٹم Ú©Û’ ÚˆÛŒÙالٹ براؤزر Ú©Û’ بطور سیٹ کرنے Ú©Û’ لیے Enter دبائیں</translation>
<translation id="2515629240566999685">آپ Ú©Û’ Ø¹Ù„Ø§Ù‚Û Ù…ÛŒÚº سگنل چیک کیا جا رÛا ÛÛ’</translation>
+<translation id="2515761554693942801">â€Ø¢Ù¾ Ù†Û’ <ph name="PROVIDER_ORIGIN" /> استعمال کرنے والی ویب سائٹس پر Touch ID سے تصدیق کرنے کا انتخاب کیا۔ ممکن ÛÛ’ Ú©Û Ø§Ø³ ÙراÛÙ… Ú©Ù†Ù†Ø¯Û Ú©Û’ پاس آپ Ú©ÛŒ ادائیگی Ú©Û’ Ø·Ø±ÛŒÙ‚Û Ú©Û’ بارے میں معلومات اسٹور Ú©Ø±Ø¯Û ÛÛ’ جن Ú©Ùˆ <ph name="LINK_TEXT" />آپ کر سکتے Ûیں۔</translation>
<translation id="2521385132275182522">نیچے بائیں طر٠سٹیپل</translation>
<translation id="2521736961081452453">Ùارم تخلیق کریں</translation>
<translation id="2523886232349826891">صر٠اس Ø¢Ù„Û Ù¾Ø± محÙوظ ÛÛ’</translation>
<translation id="2524461107774643265">مزید معلومات شامل کریں</translation>
<translation id="2529899080962247600">اس Ùیلڈ میں <ph name="MAX_ITEMS_LIMIT" /> سے Ø²ÛŒØ§Ø¯Û Ø§Ù†Ø¯Ø±Ø§Ø¬Ø§Øª Ù†Ûیں Ûونے چاÛیے۔ مزید تمام اندراجات Ú©Ùˆ نظر انداز کر دیا جائے گا۔</translation>
+<translation id="253493526287553278">پرومو Ú©ÙˆÚˆ Ú©ÛŒ تÙصیلات دیکھیں</translation>
<translation id="2535585790302968248">نجی طور پر براؤز کرنے کے لیے ایک نیا پوشیدگی ٹیب کھولیں</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{اور 1 مزید}other{اور # مزید}}</translation>
<translation id="2536110899380797252">Ù¾ØªÛ Ø´Ø§Ù…Ù„ کریں</translation>
@@ -621,7 +628,6 @@
<translation id="259821504105826686">ÙوٹوگراÙÚ© اور ڈیجیٹل آرٹس</translation>
<translation id="2601150049980261779">رومانوی Ùلمیں</translation>
<translation id="2604589665489080024">پاپ میوزک</translation>
-<translation id="2609632851001447353">تغیرات</translation>
<translation id="2610561535971892504">کلک کر کے کاپی کریں</translation>
<translation id="2617988307566202237">â€Chrome درج ذیل معلومات Ú©Ùˆ <ph name="BEGIN_EMPHASIS" />محÙوظ Ù†Ûیں کرے گا<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -654,6 +660,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">سالگرÛیں اور ناموں سے منسوب دن</translation>
<translation id="2677748264148917807">چھوڑیں</translation>
+<translation id="2679714844901977852">â€Ù…Ø­Ùوظ اور تیز چیک آؤٹس Ú©Û’ لیے اپنے کارڈ اور بلنگ Ú©ÛŒ معلومات Ú©Ùˆ اپنے Google اکاؤنٹ <ph name="USER_EMAIL" /> میں محÙوظ کریں</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">بÛترین ÙÙ¹</translation>
<translation id="2688969097326701645">Ûاں، جاری رکھیں</translation>
@@ -702,7 +709,6 @@
<translation id="2854764410992194509">â€Ø§Ù†Ù¹Ø±Ù†ÛŒÙ¹ سروس پرووائیڈرز (ISPs)</translation>
<translation id="2856444702002559011">Ø­Ù…Ù„Û Ø¢ÙˆØ± <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> سے آپ Ú©ÛŒ معلومات (مثلاً، پاس ورڈز، پیغامات یا کریڈٹ کارڈز) چوری کرنے Ú©ÛŒ کوشش کر سکتے Ûیں۔ <ph name="BEGIN_LEARN_MORE_LINK" />مزید جانیں<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">ÛŒÛ Ø³Ø§Ø¦Ù¹ دخل انداز یا Ú¯Ù…Ø±Ø§Û Ú©Ù† اشتÛارات دکھاتی ÛÛ’Û”</translation>
-<translation id="286512204874376891">ورچوئل کارڈ آپ Ú©Û’ اصل کارڈ Ú©Ùˆ چھپاتا ÛÛ’ ØªØ§Ú©Û Ø¢Ù¾ Ú©ÛŒ Ù…Ù…Ú©Ù†Û Ø¯Ú¾ÙˆÚ©Û Ø¯ÛÛŒ سے Ø­Ùاظت کرنے میں مدد کر سکے۔ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">دوستانÛ</translation>
<translation id="28761159517501904">موویز</translation>
<translation id="2876489322757410363">کسی خارجی ایپلیکیشن Ú©Û’ ذریعے ادائیگی کرنے Ú©Û’ لیے پوشیدگی وضع Ú©Ùˆ Ú†Ú¾ÙˆÚ‘ رÛÛ’ Ûیں۔ جاری رکھیں؟</translation>
@@ -803,7 +809,6 @@
<translation id="3158539265159265653">ڈسک</translation>
<translation id="3162559335345991374">â€Ø¢Ù¾ جو Wi-Fi استعمال کر رÛÛ’ Ûیں ÙˆÛ Ø¢Ù¾ سے اپنا لاگ ان صÙØ­Û Ù…Ù„Ø§Ø­Ø¸Û Ú©Ø±Ù†Û’ کا ØªÙ‚Ø§Ø¶Û Ú©Ø± سکتا ÛÛ’Û”</translation>
<translation id="3169472444629675720">دریاÙت کریں</translation>
-<translation id="3174168572213147020">جزیرÛ</translation>
<translation id="3176929007561373547">اپنی پراکسی کی ترتیبات چیک کریں یا اپنے
نیٹ ورک Ú©Û’ منتظم سے Ø±Ø§Ø¨Ø·Û Ú©Ø± Ú©Û’ ÛŒÛ ÛŒÙ‚ÛŒÙ†ÛŒ بنائیں Ú©Û Ù¾Ø±Ø§Ú©Ø³ÛŒ
سرور کام کر رÛا ÛÛ’Û” اگر آپ Ú©Ùˆ یقین Ù†Ûیں ÛÛ’ تو
@@ -883,9 +888,6 @@
<translation id="3369192424181595722">گھڑی کی خرابی</translation>
<translation id="3369459162151165748">گاڑی کے پرزے اور لوازمات</translation>
<translation id="3371064404604898522">â€Chrome Ú©Ùˆ ÚˆÙŠÙالٹ براؤزر Ú©Û’ بطور سیٹ کریں</translation>
-<translation id="3371076217486966826">â€<ph name="URL" /> درج ذیل چیزیں کرنا چاÛتا ÛÛ’:
- • اپنے اطرا٠کا ایک 3D Ù†Ù‚Ø´Û ØªØ®Ù„ÛŒÙ‚ کرنا اور کیمرے Ú©ÛŒ پوزیشن Ú©Ùˆ ٹریک کرنا
- • اپنے کیمرے کا استعمال کرنا</translation>
<translation id="337363190475750230">کالعدم کردÛ</translation>
<translation id="3375754925484257129">â€Chrome کا سیÙÙ¹ÛŒ چیک چلائیں</translation>
<translation id="3377144306166885718">â€Ø³Ø±ÙˆØ± Ù†Û’ TLS کا متروک ورژن استعمال کیا۔</translation>
@@ -902,6 +904,7 @@
<translation id="3399952811970034796">ڈیلیوری کا پتÛ</translation>
<translation id="3402261774528610252">â€Ø§Ø³ سائٹ Ú©Ùˆ لوڈ کرنے Ú©Û’ لئے استعمال Ûونے والے کنکشن میں TLS 1.0 یا TLS 1.1 استعمال کیا گیا تھا، جو ÙØ±Ø³ÙˆØ¯Û Ûیں اور مستقبل میں بھی غیر Ùعال کر دیے جائیں Ú¯Û’Û” ایک بار غیر Ùعال Ûوجانے پر، صارÙین Ú©Ùˆ اس سائٹ Ú©Ùˆ لوڈ کرنے سے روکا جائے گا۔ سرور Ú©Ùˆ TLS 1.2 یا اس سے اعلی ورژن Ú©Ùˆ Ùعال کرنا چاÛیے۔</translation>
<translation id="3405664148539009465">Ùونٹس Ú©Ùˆ حسب ضرورت بنائیں</translation>
+<translation id="3407789382767355356">Ùریق ثالث سائن ان</translation>
<translation id="3409896703495473338">سیکیورٹی کی ترتیبات کا نظم کریں</translation>
<translation id="3414952576877147120">سائز:</translation>
<translation id="3417660076059365994">â€Ø¬Ù† Ùائلز Ú©Ùˆ آپ اپ لوڈ یا منسلک کرتے Ûیں ÙˆÛ Google کلاؤڈ یا تیسرے Ùریق Ú©Ùˆ ØªØ¬Ø²ÛŒÛ Ú©ÛŒÙ„Ø¦Û’ بھیجی جاتی Ûیں۔ مثال Ú©Û’ طور پر، انÛیں حساس ڈیٹا یا میلوئیر کیلئے اسکین کیا جا سکتا ÛÛ’Û”</translation>
@@ -934,6 +937,7 @@
<translation id="3477679029130949506">موویز Ú©ÛŒ ÙÛرستیں اور تھیئٹر شوز Ú©Û’ اوقات</translation>
<translation id="3479552764303398839">ابھی Ù†Ûیں</translation>
<translation id="3484560055331845446">â€Ø¢Ù¾ اپنے Google اکاؤنٹ تک رسائی سے محروم ÛÙˆ سکتے Ûیں۔ Chrome تجویز کرتا ÛÛ’ Ú©Û Ø¢Ù¾ ابھی اپنا پاس ورڈ تبدیل کریں۔ آپ سے سائن ان کرنے Ú©Ùˆ Ú©Ûا جائے گا۔</translation>
+<translation id="3484861421501147767">یاد دÛانی: محÙوظ Ú©Ø±Ø¯Û Ù¾Ø±ÙˆÙ…Ùˆ Ú©ÙˆÚˆ دستیاب ÛÛ’</translation>
<translation id="3487845404393360112">ٹرے 4</translation>
<translation id="3495081129428749620"><ph name="PAGE_TITLE" />
صÙØ­Û’ میں تلاش کریں</translation>
@@ -1058,6 +1062,7 @@
<translation id="3810973564298564668">نظم کریں</translation>
<translation id="3816482573645936981">قدر (اس Ú©ÛŒ Ø¬Ú¯Û Ù†Ø§ÙØ° کردÛ)</translation>
<translation id="382518646247711829">اگر آپ ایک پراکسی سرور استعمال کرتے Ûیں تو…</translation>
+<translation id="3826050100957962900">Ùریق ثالث سائن ان</translation>
<translation id="3827112369919217609">یقینی</translation>
<translation id="3827666161959873541">خاندان پر مبنی Ùلمیں</translation>
<translation id="3828924085048779000">خالی پاس Ùریز Ú©ÛŒ اجازت Ù†Ûیں ÛÛ’Û”</translation>
@@ -1070,9 +1075,9 @@
<translation id="3858027520442213535">تاریخ اور وقت اپ ڈیٹ کریں</translation>
<translation id="3858860766373142691">نام</translation>
<translation id="3872834068356954457">سائنس</translation>
+<translation id="3875783148670536197">مجھے Ø·Ø±ÛŒÙ‚Û Ø¯Ú©Ú¾Ø§Ø¦ÛŒÚº</translation>
<translation id="3881478300875776315">قدرے کم لائنز دکھائیں</translation>
<translation id="3884278016824448484">متنازع Ø¢Ù„Û Ø´Ù†Ø§Ø®Øª کنندÛ</translation>
-<translation id="3885155851504623709">پیرش</translation>
<translation id="388632593194507180">مانیٹر کرنے کا Ù¾ØªÛ Ú†Ù„Ø§</translation>
<translation id="3886948180919384617">اسٹیکر 3</translation>
<translation id="3890664840433101773">ای میل شامل کریں</translation>
@@ -1102,9 +1107,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> مسدود ÛÛ’</translation>
<translation id="3978338123949022456">â€ØªÙ„اش وضع، ایک استÙسار ٹائپ کریں اور <ph name="KEYWORD_SUFFIX" /> سے تلاش کرنے Ú©Û’ لیے Enter دبائیں</translation>
<translation id="398470910934384994">پرندے</translation>
+<translation id="3985750352229496475">پتے کا نظم کریں...</translation>
<translation id="3986705137476756801">ابھی کیلئے لائیو کیپشن آ٠کریں</translation>
<translation id="3987940399970879459">â€â€Ž1 MB‎ سے Ú©Ù…</translation>
<translation id="3990250421422698716">جاگ آÙسیٹ</translation>
+<translation id="3992684624889376114">اس صÙØ­Û Ú©Û’ بارے میں</translation>
<translation id="3996311196211510766">اس سائٹ <ph name="ORIGIN" /> Ù†Û’ درخواست Ú©ÛŒ ÛÛ’ Ú©Û Ø§ÛŒÚ© ماخذ پالیسی کا اطلاق
اس سے Ú©ÛŒ جانے والی سبھی درخواستوں پر کیا جائے، لیکن ÙÛŒ الحال اس پالیسی Ú©Ùˆ لاگو Ù†Ûیں کیا جا سکتا۔</translation>
<translation id="4006465311664329701">â€Google Pay استعمال کرنے والے ادائیگی Ú©Û’ طریقے، پیشکشیں اور پتے</translation>
@@ -1229,6 +1236,7 @@
<translation id="4305666528087210886">آپ Ú©ÛŒ Ùائل تک رسائی Ù†Ûیں ÛÙˆ سکی</translation>
<translation id="4306529830550717874">Ù¾ØªÛ Ù…Ø­Ùوظ کریں؟</translation>
<translation id="4306812610847412719">کلپ بورڈ</translation>
+<translation id="4310070645992025887">اپنے صار٠کے تجربے کو تلاش کریں</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">مسدود کریں (ÚˆÛŒÙالٹ)</translation>
<translation id="4314815835985389558">مطابقت پذیری کا نظم کريں</translation>
@@ -1279,6 +1287,7 @@
<translation id="4435702339979719576">Postcard)‎</translation>
<translation id="443673843213245140">پراکسی کا استعمال غیر Ùعال کر دیا گیا ÛÛ’ لیکن ایک واضح پراکسی Ú©Ù†Ùیگریشن متعین Ú©ÛŒ گئی ÛÛ’Û”</translation>
<translation id="4441832193888514600">نظر انداز کر دیا گیا Ú©ÛŒÙˆÙ†Ú©Û Ù¾Ø§Ù„ÛŒØ³ÛŒ صر٠کلاؤڈ صار٠کی پالیسی Ú©Û’ بطور سیٹ Ú©ÛŒ جا سکتی ÛÛ’Û”</translation>
+<translation id="4442470707340296952">â€Chrome Ú©Û’ ٹیبز</translation>
<translation id="4450893287417543264">Ø¯ÙˆØ¨Ø§Ø±Û Ù†Û Ø¯Ú©Ú¾Ø§Ø¦ÛŒÚº</translation>
<translation id="4451135742916150903">â€Ø³Ø§Ø¦Ù¹ HID آلات سے منسلک Ûونے کیلئے پوچھ سکتی ÛÛ’</translation>
<translation id="4452328064229197696">â€Ø¢Ù¾ Ù†Û’ ابھی جو پاس ورڈ استعمال کیا ÙˆÛ ÚˆÛŒÙ¹Ø§ Ú©ÛŒ خلا٠ورزی میں پایا گیا۔ اپنے اکاؤنٹس محÙوظ کرنے Ú©Û’ لیے، Google پاس ورڈ مینیجر آپ Ú©Û’ محÙوظ Ú©Ø±Ø¯Û Ù¾Ø§Ø³ ورڈز Ú©Ùˆ چیک کرنے Ú©ÛŒ تجویز کرتا ÛÛ’Û”</translation>
@@ -1312,7 +1321,7 @@
<translation id="4524138615196389145">â€Ø§Ø¨ سے WebAuthn Ú©Û’ استعمال سے تیزی سے اپنے کارڈز Ú©ÛŒ تصدیق کریں</translation>
<translation id="4524805452350978254">کارڈز کا نظم کریں</translation>
<translation id="4526465106919207193">لوگ اور معاشرÛ</translation>
-<translation id="4530347922939905757">متنی پیغام</translation>
+<translation id="4530347922939905757">ٹیکسٹ پیغام</translation>
<translation id="4541810033354695636">اÙØ²ÙˆØ¯Û Ø­Ù‚ÛŒÙ‚Øª</translation>
<translation id="4542971377163063093">ٹرے 6</translation>
<translation id="455113658016510503">A9</translation>
@@ -1417,6 +1426,7 @@
<translation id="483241715238664915">وارننگز کو آن کریں</translation>
<translation id="4834250788637067901">â€Google Pay استعمال کرنے والے ادائیگی Ú©Û’ طریقے، پیشکشیں اور پتے</translation>
<translation id="4838327282952368871">خیالی</translation>
+<translation id="4839087176073128681">â€Ø§Ú¯Ù„ÛŒ بار تیزی سے ادائیگی کریں اور Google Ú©ÛŒ انڈسٹری Ú©ÛŒ ص٠اول Ú©ÛŒ سیکیورٹی Ú©Û’ ساتھ اپنے کارڈ Ú©ÛŒ Ø­Ùاظت کریں۔</translation>
<translation id="4840250757394056958">â€Ø§Ù¾Ù†Û’ Chrome Ú©ÛŒ سرگزشت دیکھیں</translation>
<translation id="484462545196658690">خودکار</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> اور مزید پر رعایت حاصل کریں</translation>
@@ -1479,6 +1489,7 @@
<translation id="5011561501798487822">شناخت Ú©Ø±Ø¯Û Ø²Ø¨Ø§Ù†</translation>
<translation id="5015510746216210676">مشین کا نام:</translation>
<translation id="5017554619425969104">آپ کا کاپی Ú©Ø±Ø¯Û Ù…ØªÙ†</translation>
+<translation id="5017828934289857214">مجھے بعد میں یاد دلائیں</translation>
<translation id="5018422839182700155">ÛŒÛ ØµÙØ­Û Ù†Ûیں کھول سکتا ÛÛ’</translation>
<translation id="5019198164206649151">اسٹور Ú©Ùˆ غلط حالت میں بیک کیا جا رÛا ÛÛ’</translation>
<translation id="5020776957610079374">عالمی موسیقی</translation>
@@ -1498,6 +1509,7 @@
<translation id="5051305769747448211">لائیو کامیڈی</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{اس Ùائل Ú©Ùˆ قریبی آلات Ú©Û’ ساتھ اشتراک کا استعمال کرتے Ûوئے بھیجنے Ú©Û’ لیے اپنے آلے پر <ph name="DISK_SPACE_SIZE" /> Ø¬Ú¯Û Ø®Ø§Ù„ÛŒ کریں}other{ان Ùائلز Ú©Ùˆ قریبی آلات Ú©Û’ ساتھ اشتراک کا استعمال کرتے Ûوئے بھیجنے Ú©Û’ لیے اپنے آلے پر <ph name="DISK_SPACE_SIZE" /> Ø¬Ú¯Û Ø®Ø§Ù„ÛŒ کریں}}</translation>
<translation id="5056549851600133418">آپ کیلئے مضامین</translation>
+<translation id="5060483733937416656">â€Ø¢Ù¾ Ù†Û’ <ph name="PROVIDER_ORIGIN" /> کا استعمال کرنے والی ویب سائٹس پر Windows Hello سے تصدیق کرنے کا انتخاب کیا ÛÛ’Û” ممکن ÛÛ’ Ú©Û Ø§Ø³ ÙراÛÙ… Ú©Ù†Ù†Ø¯Û Ú©Û’ پاس آپ Ú©ÛŒ ادائیگی Ú©Û’ Ø·Ø±ÛŒÙ‚Û Ú©Û’ بارے میں معلومات اسٹور Ú©Ø±Ø¯Û ÛÛ’ جن Ú©Ùˆ <ph name="LINK_TEXT" />آپ کر سکتے Ûیں۔</translation>
<translation id="5061227663725596739">کیا آپ کا مطلب <ph name="LOOKALIKE_DOMAIN" /> ÛÛ’ØŸ</translation>
<translation id="5066056036849835175">پرنٹنگ کی سرگزشت</translation>
<translation id="5068234115460527047">باڑ Ú©Û’ Ùنڈز</translation>
@@ -1511,10 +1523,8 @@
<translation id="5087286274860437796">سرور کا سرٹیÙکیٹ اس وقت درست Ù†Ûیں ÛÛ’Û”</translation>
<translation id="5087580092889165836">کارڈ شامل کریں</translation>
<translation id="5088142053160410913">آپریٹر کو پیغام</translation>
-<translation id="5089810972385038852">ریاست</translation>
<translation id="5093232627742069661">â€Z-Ùولڈ</translation>
<translation id="5094747076828555589">â€ÛŒÛ سرور ÛŒÛ Ø«Ø§Ø¨Øª Ù†Ûیں کر سکا Ú©Û ÛŒÛ <ph name="DOMAIN" /> ÛÛ’Ø› اس Ú©Û’ سیکیورٹی سرٹیÙکیٹ پر Chromium Ú©Ùˆ Ø¨Ú¾Ø±ÙˆØ³Û Ù†Ûیں ÛÛ’Û” ÛŒÛ ØºÙ„Ø· Ú©Ù†Ùیگرییشن یا آپ Ú©Û’ کنکشن Ú©Ùˆ قطع کرنے والے کسی Ø­Ù…Ù„Û Ø¢ÙˆØ± Ú©ÛŒ ÙˆØ¬Û Ø³Û’ ÛÙˆ سکتا ÛÛ’Û”</translation>
-<translation id="5095208057601539847">صوبÛ</translation>
<translation id="5097099694988056070">â€CPU/RAM Ú©Û’ استعمال جیسے آلے Ú©Û’ اعداد Ùˆ شمار</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">سائٹ محÙوظ Ù†Ûیں ÛÛ’</translation>
@@ -1552,6 +1562,7 @@
<translation id="5171045022955879922">â€ØªÙ„اش کریں یا URL ٹائپ کریں</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">مشین</translation>
+<translation id="5177076414499237632">اس صÙØ­Û Ú©Û’ ماخذ اور موضوع Ú©Û’ بارے میں جانیں</translation>
<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> میں Ù†Ûیں ÛÛ’ØŸ اس خرابی Ú©ÛŒ اطلاع دیں</translation>
<translation id="518639307526414276">پالتو جانور کا کھانا اور پالتو جانور Ú©ÛŒ Ù†Ú¯Ûداشت Ú©ÛŒ سپلائز</translation>
<translation id="5190835502935405962">بÙÚ© مارکس بار</translation>
@@ -1712,6 +1723,7 @@
<translation id="5624120631404540903">پاس ورڈز کا نظم کریں</translation>
<translation id="5629630648637658800">پالیسی Ú©ÛŒ ترتیبات لوڈ Ûونے میں ناکام</translation>
<translation id="5631439013527180824">آلے کے مینیجمنٹ کا غلط ٹوکن</translation>
+<translation id="5632485077360054581">مجھے Ø·Ø±ÛŒÙ‚Û Ø¯Ú©Ú¾Ø§Ø¦ÛŒÚº</translation>
<translation id="5633066919399395251">ÙÛŒ الحال <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> پر موجود Ø­Ù…Ù„Û Ø¢ÙˆØ± آپ Ú©Û’ کمپیوٹر پر ایسے خطرناک پروگرامز انسٹال کرنے Ú©ÛŒ کوشش کر سکتے Ûیں جن سے آپ Ú©ÛŒ معلومات (مثلاً، تصاویر، پاس ورڈز، پیغامات، اور کریڈٹ کارڈز) چوری یا حذ٠ÛÙˆ سکتی Ûیں۔ <ph name="BEGIN_LEARN_MORE_LINK" />مزید جانیں<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Ù¾Ùر Ùریب مواد مسدود ÛÛ’Û”</translation>
<translation id="5633259641094592098">مخصوص پسند Ú©ÛŒ اور انڈی Ùلمیں</translation>
@@ -1829,6 +1841,7 @@
<translation id="5989320800837274978">â€Ù†Û تو Ùکس Ú©Ø±Ø¯Û Ù¾Ø±Ø§Ú©Ø³ÛŒ سرورز Ù†Û ÛÛŒ ‎.pac اسکرپٹ URL کا تعین کیا گیا ÛÛ’Û”</translation>
<translation id="5992691462791905444">â€Ø§Ù†Ø¬ÛŒÙ†ÛŒØ¦Ø±Ù†Ú¯ Z-Ùولڈ</translation>
<translation id="5995727681868049093">â€Ø§Ù¾Ù†Û’ Google اکاؤنٹ میں اپنی معلومات، رازداری اور سیکیورٹی کا نظم کریں</translation>
+<translation id="5997247540087773573">â€Ø¢Ù¾ Ù†Û’ ابھی جو پاس ورڈ استعمال کیا ÙˆÛ ÚˆÛŒÙ¹Ø§ Ú©ÛŒ خلا٠ورزی میں پایا گیا۔ اپنے اکاؤنٹس محÙوظ کرنے Ú©Û’ لیے، Google پاس ورڈ مینیجر اسے ابھی تبدیل کرنے اور آپ Ú©Û’ محÙوظ Ú©Ø±Ø¯Û Ù¾Ø§Ø³ ورڈز Ú©Ùˆ چیک کرنے Ú©ÛŒ تجویز کرتا ÛÛ’Û”</translation>
<translation id="6000758707621254961">'<ph name="SEARCH_TEXT" />' کے <ph name="RESULT_COUNT" /> نتائج</translation>
<translation id="6006484371116297560">کلاسک</translation>
<translation id="6008122969617370890">â€N سے 1 ترتیب</translation>
@@ -1925,7 +1938,6 @@
<translation id="627746635834430766">â€Ø§Ú¯Ù„ÛŒ بار Ø²ÛŒØ§Ø¯Û ØªÛŒØ²ÛŒ سے ادائیگی کرنے Ú©Û’ لیے، اپنے کارڈ اور بلنگ Ù¾ØªÛ Ú©Ùˆ اپنے Google اکاؤنٹ میں محÙوظ کریں۔</translation>
<translation id="6279183038361895380">اپنا کرسر دکھانے کیلئے |<ph name="ACCELERATOR" />| کو دبائیں</translation>
<translation id="6280223929691119688">اس پتے پر ڈیلیوری Ù†Ûیں ÛÙˆ سکتی۔ کوئی Ù…Ø®ØªÙ„Ù Ù¾ØªÛ Ù…Ù†ØªØ®Ø¨ کریں۔</translation>
-<translation id="6282194474023008486">پوسٹل کوڈ</translation>
<translation id="6285507000506177184">â€Chrome بٹن میں ڈاؤن لوڈز کا نظم کریں، Chrome میں ڈاؤن لوڈ Ú©Ø±Ø¯Û Ø§Ù¾Ù†ÛŒ Ùائلز کا نظم کرنے Ú©Û’ لیے اینٹر دبائیں</translation>
<translation id="6289939620939689042">صÙØ­Û’ کا رنگ</translation>
<translation id="6290238015253830360">آپ Ú©Û’ تجویز Ú©Ø±Ø¯Û Ù…Ø¶Ø§Ù…ÛŒÙ† ÛŒÛاں ظاÛر Ûوتے Ûیں</translation>
@@ -1947,6 +1959,7 @@
<translation id="6337133576188860026"><ph name="SIZE" /> سے Ú©Ù… بچت Ûوتی ÛÛ’Û” آپ Ú©Û’ اگلے Ù…Ù„Ø§Ø­Ø¸Û Ù¾Ø± Ú©Ú†Ú¾ سائٹس مزید Ø¢ÛØ³ØªÛ Ø³Û’ لوڈ ÛÙˆ سکتی Ûیں۔</translation>
<translation id="6337534724793800597">نام Ú©Û’ لحاظ سے پالیسیاں Ùلٹر کریں</translation>
<translation id="6340739886198108203">منتظم Ú©ÛŒ پالیسی Ø±Ø§Ø²Ø¯Ø§Ø±Ø§Ù†Û Ù…ÙˆØ§Ø¯ Ú©Û’ مرئی Ûونے پر اسکرین شاٹس یا ریکارڈنگز لینے Ú©ÛŒ تجویز Ù†Ûیں کرتی ÛÛ’:</translation>
+<translation id="6348220984832452017">Ùعال تغیرات</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> انسٹال کریں</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{کوئی Ù†Ûیں}=1{1 پاس ورڈ (برائے <ph name="DOMAIN_LIST" />ØŒ مطابقت پذیر ÛÛ’)}=2{2 پاس ورڈز (برائے <ph name="DOMAIN_LIST" />ØŒ مطابقت پذیر Ûیں)}other{# پاس ورڈز (برائے <ph name="DOMAIN_LIST" />ØŒ مطابقت پذیر Ûیں)}}</translation>
<translation id="6355392890578844978">â€ÛŒÛ براؤزر کسی کمپنی یا دوسری تنظیم Ú©Û’ زیر انتظام Ù†Ûیں ÛÛ’Û” اس آلے پر Ûونے والی سرگرمی کا نظم Chromium سے باÛر کیا جا سکتا ÛÛ’Û” <ph name="BEGIN_LINK" />مزید جانیں<ph name="END_LINK" /></translation>
@@ -1978,7 +1991,6 @@
<translation id="643051589346665201">â€Google کا پاس ورڈ تبدیل کریں</translation>
<translation id="6433490469411711332">Ø±Ø§Ø¨Ø·Û Ú©ÛŒ معلومات میں ترمیم کریں</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> Ù†Û’ منسلک Ûونے سے منع کر دیا۔</translation>
-<translation id="6438025220577812695">اپنے لیے اسے تبدیل کریں</translation>
<translation id="6440503408713884761">نظر انداز کردÛ</translation>
<translation id="6443406338865242315">آپ Ù†Û’ Ú©Ù† ایکسٹینشنز اور پلگ انز Ú©Ùˆ انسٹال کیا ÛÛ’</translation>
<translation id="6446163441502663861">Kahu ‎(Envelope‎)‎</translation>
@@ -2108,9 +2120,9 @@
<translation id="6828866289116430505">جینیات</translation>
<translation id="6831043979455480757">ØªØ±Ø¬Ù…Û Ú©Ø±ÛŒÚº</translation>
<translation id="6833752742582340615">â€Ù…Ø­Ùوظ اور تیز چیک آؤٹس Ú©Û’ لیے اپنے کارڈ اور بلنگ Ú©ÛŒ معلومات Ú©Ùˆ اپنے Google اکاؤنٹ میں محÙوظ کریں</translation>
-<translation id="6839929833149231406">علاقÛ</translation>
<translation id="6846340164947227603">ورچوئل کارڈ نمبر استعمال کریں...</translation>
<translation id="6852204201400771460">ایپ Ø¯ÙˆØ¨Ø§Ø±Û Ù„ÙˆÚˆ کریں؟</translation>
+<translation id="6857776781123259569">پاس ورڈز کا نظم کریں...</translation>
<translation id="686485648936420384">صار٠کے وسائل</translation>
<translation id="6865412394715372076">ابھی اس کارڈ Ú©ÛŒ توثیق Ù†Ûیں Ú©ÛŒ جا سکتی</translation>
<translation id="6869334554832814367">ذاتی قرضے</translation>
@@ -2159,7 +2171,6 @@
<translation id="6965978654500191972">آلÛ</translation>
<translation id="696703987787944103">ادراکی</translation>
<translation id="6968269510885595029">اپنی سیکیورٹی کلید کا استعمال کریں</translation>
-<translation id="6970216967273061347">ضلع</translation>
<translation id="6971439137020188025">â€Slides میں تیزی سے ایک نئی Google پیشکش تخلیق کریں</translation>
<translation id="6972629891077993081">â€HID آلات</translation>
<translation id="6973656660372572881">â€Ùکس Ú©Ø±Ø¯Û Ù¾Ø±Ø§Ú©Ø³ÛŒ سرورز اور ‎.pac اسکرپٹ URL دونوں کا تعین کیا گیا ÛÛ’Û”</translation>
@@ -2198,7 +2209,6 @@
<translation id="7081308185095828845">ÛŒÛ Ø®ØµÙˆØµÛŒØª آپ Ú©Û’ آلے پر دستیاب Ù†Ûیں ÛÛ’</translation>
<translation id="7083258188081898530">ٹرے 9</translation>
<translation id="7086090958708083563">صار٠کے ذریعے اپ لوڈ کی درخواست کی گئی</translation>
-<translation id="7087282848513945231">کاؤنٹی</translation>
<translation id="7095139009144195559">â€<ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />ØŒ Chrome Ú©ÛŒ ترتیبات میں موجود تمام سائٹس پر اسٹور Ú©Ø±Ø¯Û Ø§Ø¬Ø§Ø²ØªÙˆÚº اور ڈیٹا کا نظم کرنے Ú©Û’ لیے ٹیب پھر اینٹر دبائیں</translation>
<translation id="7096937462164235847">اس ویب سائٹ Ú©ÛŒ شناخت توثیق Ú©Ø±Ø¯Û Ù†Ûیں ÛÛ’Û”</translation>
<translation id="7101893872976785596">ڈراؤنی Ùلمیں</translation>
@@ -2217,10 +2227,10 @@
<ph name="LIST_ITEM" />Ùارمز میں درج Ú©Ø±Ø¯Û Ù…Ø¹Ù„ÙˆÙ…Ø§Øª<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">اس پتے پر ترسیل Ù†Ûیں ÛÙˆ سکتی۔ کوئی Ù…Ø®ØªÙ„Ù Ù¾ØªÛ Ù…Ù†ØªØ®Ø¨ کریں۔</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">آپ Ú©Û’ منتظم Ù†Û’ اس ڈیٹا Ú©Û’ کاپی کیے جانے پر پابندی لگائی ÛÛ’Û”</translation>
<translation id="7135130955892390533">اسٹیٹس دکھائیں</translation>
<translation id="7138472120740807366">ڈیلیوری کا طریقÛ</translation>
-<translation id="7139724024395191329">امارات</translation>
<translation id="7139892792842608322">بنیادی ٹرے</translation>
<translation id="714064300541049402">â€Ø³Ø§Ø¦ÛŒÚˆ 2 تصویر X Ø´ÙÙ¹</translation>
<translation id="7152423860607593928">Number-14 ‎(Envelope‎)‎</translation>
@@ -2437,6 +2447,7 @@
<translation id="7669271284792375604">اس سائٹ پر موجود Ø­Ù…Ù„Û Ø¢ÙˆØ± آپ Ú©Ùˆ ایسے پروگرامز انسٹال کرنے Ú©Û’ جال میں پھنسانے Ú©ÛŒ کوشش کر سکتے Ûیں جو آپ Ú©Û’ براؤزنگ Ú©Û’ تجربے Ú©Ùˆ نقصان Ù¾Ûنچا سکتے Ûیں (مثلاً، آپ کا Ûوم پیج تبدیل کر Ú©Û’ یا آپ جو سائٹس Ù…Ù„Ø§Ø­Ø¸Û Ú©Ø±ØªÛ’ Ûیں ان پر اضاÙÛŒ اشتÛارات دکھا کر)Û”</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Ø±Ø§Ø²Ø¯Ø§Ø±Ø§Ù†Û Ú©Û’ بطور پرچم لگائے گئے ڈیٹا Ú©Û’ متعلق Ú©ÛŒ گئی کاروائیاں (لاگ ان Ú©Û’ بعد سے 1 کارروائی)Û” <ph name="BEGIN_LINK" />مزید جانیں<ph name="END_LINK" />}other{Ø±Ø§Ø²Ø¯Ø§Ø±Ø§Ù†Û Ú©Û’ بطور پرچم لگائے گئے ڈیٹا Ú©Û’ متعلق Ú©ÛŒ گئی کاروائیاں (لاگ ان Ú©Û’ بعد سے # کارروائیاں)Û” <ph name="BEGIN_LINK" />مزید جانیں<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">میل باکس 6</translation>
+<translation id="7675325315208090829">ادائیگی کے طریقوں کا نظم کریں...</translation>
<translation id="7676643023259824263">کلپ بورڈ ٹیکسٹ تلاش کریں، <ph name="TEXT" /></translation>
<translation id="7679367271685653708">â€Chrome Ú©ÛŒ ترتیبات میں اپنی براؤزنگ Ú©ÛŒ سرگزشت دیکھیں اور اس کا نظم کریں</translation>
<translation id="7679947978757153706">بیس بال</translation>
@@ -2479,7 +2490,6 @@
<translation id="7766518757692125295">اسکرٹ</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">اسی ترتیب میں، صÙحات کا رÙØ® اوپر Ú©ÛŒ جانب</translation>
-<translation id="777702478322588152">انتظامی حلقÛ</translation>
<translation id="7791011319128895129">غیر ریلیز کردÛ</translation>
<translation id="7791196057686275387">گانٹھ باندھیں</translation>
<translation id="7791543448312431591">شامل کریں</translation>
@@ -2570,12 +2580,12 @@
<translation id="8055534648776115597">Ù¾ÛŒØ´Û ÙˆØ±Ø§Ù†Û Ø§ÙˆØ± جاری تعلیم</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" صحیح طور پر Ú©Ù†Ùیگر Ú©Ø±Ø¯Û Ù†Ûیں ÛÛ’Û” عام طور سے "<ph name="SOFTWARE_NAME" />" Ú©Ùˆ ان انسٹال کرنے سے ÛŒÛ Ù…Ø³Ø¦Ù„Û Ø­Ù„ ÛÙˆ جاتا ÛÛ’Û” <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">غذا کی تیاری</translation>
-<translation id="8066955247577885446">معذرت، Ú©Ú†Ú¾ غلط ÛÙˆ گیا۔</translation>
<translation id="8067872629359326442">â€Ø¢Ù¾ Ù†Û’ ابھی ایک Ù¾ÙرÙریب سائٹ پر اپنا پاس ورڈ درج کیا ÛÛ’Û” Chromium مدد کر سکتا ÛÛ’Û” اپنا پاس ورڈ تبدیل کرنے اور Google Ú©Ùˆ ÛŒÛ Ù…Ø·Ù„Ø¹ کرنے Ú©Û’ لیے Ú©Û Ø§Ù“Ù¾ کا اکاؤنٹ خطرے میں ÛÙˆ سکتا ÛÛ’ØŒ 'اکاؤنٹ کا تحÙظ کریں' پر Ú©Ù„Ú© کریں۔</translation>
<translation id="8070439594494267500">ایپ آئیکن</translation>
<translation id="8074253406171541171">10x13 ‎(Envelope‎)‎</translation>
<translation id="8075736640322370409">â€ØªÛŒØ²ÛŒ سے ایک نئی Google Sheet تخلیق کریں</translation>
<translation id="8075898834294118863">سائٹ کی ترتیبات کا نظم کریں</translation>
+<translation id="8076492880354921740">ٹیبز</translation>
<translation id="8078141288243656252">گھماتے وقت تشریح Ù†Ûیں Ú©ÛŒ جا سکتی</translation>
<translation id="8079031581361219619">سائٹ Ø¯ÙˆØ¨Ø§Ø±Û Ù„ÙˆÚˆ کریں؟</translation>
<translation id="8081087320434522107">سیڈانز</translation>
@@ -2698,6 +2708,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">ترتیبات</translation>
+<translation id="8428634594422941299">سمجھ آ گئی</translation>
<translation id="8431194080598727332">â€<ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />ØŒ Chrome Ú©ÛŒ ترتیبات میں اپنی Ú©ÙˆÚ©ÛŒ Ú©ÛŒ ترجیحات کا نظم کرنے Ú©Û’ لیے ٹیب پھر اینٹر دبائیں</translation>
<translation id="8433057134996913067">ÛŒÛ Ø¢Ù¾ Ú©Ùˆ بیشتر ویب سائٹس سے سائن آؤٹ کر دے گا۔</translation>
<translation id="8434840396568290395">پالتو جانور</translation>
@@ -2795,6 +2806,7 @@
<translation id="8742371904523228557"><ph name="ORIGIN" /> کیلئے آپ کا Ú©ÙˆÚˆ <ph name="ONE_TIME_CODE" /> ÛÛ’</translation>
<translation id="874918643257405732">اس ٹیب Ú©Ùˆ بÙÚ© مارک کریں</translation>
<translation id="8751426954251315517">Ø¨Ø±Ø§Û Ú©Ø±Ù… اگلی بار Ø¯ÙˆØ¨Ø§Ø±Û Ú©ÙˆØ´Ø´ کریں</translation>
+<translation id="8757526089434340176">â€Google Pay Ú©ÛŒ پیشکش دستیاب ÛÛ’</translation>
<translation id="8758885506338294482">مسابقتی ویڈیو گیمنگ</translation>
<translation id="8759274551635299824">اس کارڈ Ú©ÛŒ میعاد ختم ÛÙˆ گئی ÛÛ’</translation>
<translation id="87601671197631245">ÛŒÛ Ø³Ø§Ø¦Ù¹ پرانی سیکیورٹی Ú©Ù†Ùیگریشن Ú©Ùˆ استعمال کر رÛÛŒ ÛÛ’ جو اس سائٹ پر بھیجے جانے پر آپ Ú©ÛŒ معلومات (مثلاً، پاس ورڈز، پیغامات یا کریڈٹ کارڈز) Ú©Ùˆ ظاÛر کر سکتی ÛÛ’Û”</translation>
@@ -2802,6 +2814,7 @@
<translation id="8763927697961133303">â€USB آلÛ</translation>
<translation id="8763986294015493060">ÙÛŒ الحال Ú©Ú¾Ù„ÛŒ Ûوئی تمام پوشیدگی ونڈوز Ú©Ùˆ بند کریں</translation>
<translation id="8766943070169463815">محÙوظ طریقے سے ادائیگی کرنے کیلئے سند Ú©ÛŒ تصدیق کرنے Ú©ÛŒ شیٹ Ú©Ú¾Ù„ÛŒ ÛÛ’</translation>
+<translation id="8767765348545497220">بلبلے کی مدد بند کریں</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">موٹر سائیکلیں</translation>
<translation id="8790007591277257123">&amp;Ø¯ÙˆØ¨Ø§Ø±Û Ø­Ø°Ù Ú©Ø±ÛŒÚº</translation>
@@ -2814,6 +2827,7 @@
<translation id="8806285662264631610">Ù†Ûانے اور جسم Ú©Û’ پروڈکٹس</translation>
<translation id="8807160976559152894">Ûر صÙØ­Û Ú©Û’ بعد تراشیں</translation>
<translation id="8808828119384186784">â€Chrome Ú©ÛŒ ترتیبات</translation>
+<translation id="8813277370772331957">مجھے بعد میں یاد دلائیں</translation>
<translation id="8816395686387277279">â€<ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />ØŒ اپنی Chrome Ú©ÛŒ ترتیبات سے Chrome اپ ڈیٹ کرنے Ú©Û’ لیے ٹیب، پھر اینٹر دبائیں</translation>
<translation id="8820817407110198400">بÙÚ© مارکس</translation>
<translation id="882338992931677877">دستی سلاٹ</translation>
@@ -2994,6 +3008,7 @@
<translation id="988159990683914416">ڈویلپر بلڈ</translation>
<translation id="989988560359834682">Ù¾ØªÛ Ù…ÛŒÚº ترمیم کریں</translation>
<translation id="991413375315957741">موشن یا لائٹ سینسرز</translation>
+<translation id="992110854164447044">ورچوئل کارڈ آپ Ú©Û’ اصل کارڈ Ú©Ùˆ چھپاتا ÛÛ’ ØªØ§Ú©Û Ø¢Ù¾ Ú©ÛŒ Ù…Ù…Ú©Ù†Û Ø¯Ú¾ÙˆÚ©Û Ø¯ÛÛŒ سے Ø­Ùاظت کرنے میں مدد کر سکے۔ <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">گلابی</translation>
<translation id="992432478773561401">â€"<ph name="SOFTWARE_NAME" />" آپ Ú©Û’ کمپیوٹر یا نیٹ ورک پر مناسب طریقے سے انسٹال Ù†Ûیں Ûوا تھا:
diff --git a/chromium/components/strings/components_strings_uz.xtb b/chromium/components/strings/components_strings_uz.xtb
index a17d46b23af..9d9d7d53141 100644
--- a/chromium/components/strings/components_strings_uz.xtb
+++ b/chromium/components/strings/components_strings_uz.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Grantlar, stipendiyalar va moliyaviy yordam</translation>
<translation id="1048785276086539861">Izohlarni tahrirlash vaqtida bu hujjat bir sahifali shaklga qaytadi</translation>
<translation id="1050038467049342496">Boshqa ilovalarni yoping</translation>
+<translation id="1053959602163383901"><ph name="PROVIDER_ORIGIN" /> saytlarida shaxsni autentifikatsiya qurilmasi yordamida tasdiqlash mumkin. Bu provayder toʻlov usulingiz axborotini saqlashi mumkin, lekin uning <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Qo‘shishni bekor qilish</translation>
<translation id="1056663316309890343">Fotodasturlar</translation>
<translation id="1056898198331236512">Ogohlantirish</translation>
@@ -56,7 +57,7 @@
<translation id="1123753900084781868">Jonli izoh hozirda mavjud emas</translation>
<translation id="1125573121925420732">Saytlar xavfsizlik sozlamalarini yangilayotganda ogohlantirishlar umumiy bo‘lishi mumkin. Bu tezda yashilanadi.</translation>
<translation id="112840717907525620">Siyosat keshida xatoliklar yo‘q</translation>
-<translation id="1130564665089811311">“Sahifani tarjima qilish†tugmasi, bu sahifani Google Tarjimon orqali tarjima qilish uchun Enter tugmasini bosing</translation>
+<translation id="1130564665089811311">“Sahifani tarjima qilish†tugmasi, bu sahifani Google Tarjima orqali tarjima qilish uchun Enter tugmasini bosing</translation>
<translation id="1131264053432022307">Nusxalangan rasm</translation>
<translation id="1142846828089312304">Inkognito rejimidagi tashqi cookie fayllarni taqiqlash</translation>
<translation id="1147769322402934017">Onlayn rasm galereyalari</translation>
@@ -119,6 +120,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="1270502636509132238">Olib ketish usuli</translation>
<translation id="1281476433249504884">Taxlovchi 1</translation>
<translation id="1285320974508926690">Bu sayt hech qachon tarjima qilinmasin</translation>
+<translation id="1288548991597756084">Karta axborotini ishonchli saqlash</translation>
<translation id="1292571435393770077">Tarnov 16</translation>
<translation id="1292701964462482250">“Kompyuteringizdagi dastur Chrome brauzerini internetga xavfsiz ulanishiga imkon bermayapti†(Faqat Windows o‘rnatilgan tizimlar uchun)</translation>
<translation id="1294154142200295408">Buyruqlar qatori variantlari</translation>
@@ -223,6 +225,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
&lt;p&gt;Muammoni hal qilish uchun ochilishi kerak bo‘lgan sahifadagi &lt;strong&gt;Ulanish&lt;/strong&gt; tugmasini bosing.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Landshaft dizayni</translation>
<translation id="1513706915089223971">Tarix yozuvlari roʻyxati</translation>
+<translation id="1516097932025103760">Karta raqami shifrlangan holda ishonchli saqlanadi va CVC kodi hech qachon saqlanmaydi.</translation>
<translation id="1517433312004943670">Telefon raqami kiritilishi shart</translation>
<translation id="1519264250979466059">Nashr sanasi</translation>
<translation id="1521159554480556801">Toʻqimachilik sanʼati</translation>
@@ -287,6 +290,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="1662550410081243962">To‘lov usullarini saqlash va avtomatik kiritish</translation>
<translation id="1663943134801823270">Chrome brauzerida saqlangan karta va manzillar. Siz ularni <ph name="BEGIN_LINK" />Sozlamalar<ph name="END_LINK" /> orqali boshqarishingiz mumkin.</translation>
<translation id="1671391448414634642">Endi <ph name="SOURCE_LANGUAGE" /> tilidagi sahifalar <ph name="TARGET_LANGUAGE" /> tiliga tarjima qilinadi.</translation>
+<translation id="1673886523110456987">Taklifdan foydalanish uchun <ph name="CARD_DETAIL" /> bilan hisob-kitob qiling</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> – <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Avval eniga</translation>
<translation id="168693727862418163">Bu parametr oʻz andozasiga mos tushmagani uchun inkor etiladi.</translation>
@@ -305,6 +309,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="1717218214683051432">Harakat sensorlari</translation>
<translation id="1717494416764505390">Pochta qutisi 3</translation>
<translation id="1718029547804390981">Hujjat juda kattaligi uchun izohlanmaydi</translation>
+<translation id="1720941539803966190">Qoʻllanmani yopish</translation>
<translation id="1721424275792716183">* Bu maydoncha kiritilishi shart</translation>
<translation id="1727613060316725209">Sertifikat yaroqli</translation>
<translation id="1727741090716970331">Karta raqamini xatosiz kiriting</translation>
@@ -421,8 +426,8 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="205212645995975601">Barbekyu va grilda pishirish</translation>
<translation id="2053111141626950936"><ph name="LANGUAGE" /> tilidagi sahifalar tarjima qilinmaydi</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Bu boshqaruv elementi yoniq va faol boʻlsa, Chrome brauzerdagi oxirgi harakatlaringizga eng mos katta yoki “kogort†guruh odamlarni aniqlaydi. Reklama beruvchilar eʼlonlari uchun guruh tanlaganda qurilmadagi harakatlaringiz maxfiyligi saqlanib qoladi. Guruhingiz har kuni yangilanib turadi.}=1{Bu boshqaruv elementi yoniq va faol boʻlsa, Chrome brauzerdagi oxirgi harakatlaringizga eng mos katta yoki “kogort†guruh odamlarni aniqlaydi. Reklama beruvchilar eʼlonlari uchun guruh tanlaganda qurilmadagi harakatlaringiz maxfiyligi saqlanib qoladi. Guruhingiz har kuni yangilanib turadi.}other{Bu boshqaruv elementi yoniq va faol boʻlsa, Chrome brauzerdagi oxirgi harakatlaringizga eng mos katta yoki “kogort†guruh odamlarni aniqlaydi. Reklama beruvchilar eʼlonlari uchun guruh tanlaganda qurilmadagi harakatlaringiz maxfiyligi saqlanib qoladi. Guruhingiz har {NUM_DAYS} kunda yangilanib turadi.}}</translation>
-<translation id="2053553514270667976">Pochta indeksi</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 ta tavsiya}other{# ta tavsiya}}</translation>
+<translation id="2066915425250589881">oʻchirilishini talab qilish mumkin</translation>
<translation id="2068528718802935086">Chaqaloqlar uchun</translation>
<translation id="2071156619270205202">Koʻrsatilgan raqamdan virtual karta raqami sifatida foydalanib boʻlmaydi.</translation>
<translation id="2071692954027939183">Odatda ruxsat bermasligingiz uchun bildirishnomalar avtomatik bloklanadi</translation>
@@ -434,7 +439,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="2088086323192747268">Sinxronizatsiyani boshqarish tugmasi, Chrome sozlamalari orqali sinxronlanadigan maʼlumotlarni boshqarish uchun Enter tugmasini bosing</translation>
<translation id="2091887806945687916">Tovush</translation>
<translation id="2094505752054353250">Domen noto‘g‘ri kiritildi</translation>
-<translation id="2096368010154057602">Okrug</translation>
<translation id="2099652385553570808">Chapdan 3 marta steplerlash</translation>
<translation id="2101225219012730419">Versiya:</translation>
<translation id="2102134110707549001">Ishonchli parol yaratish…</translation>
@@ -471,7 +475,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="2185836064961771414">Amerikancha futbol</translation>
<translation id="2187317261103489799">Aniqlansin (standart parametr)</translation>
<translation id="2188375229972301266">Quyidan bir nechta teshik ochish</translation>
-<translation id="2188852899391513400">Hozirgina siz foydalangan parol oshkor qilingan. Hisoblaringizni himoyalash maqsadida Google Parollar menejeri uni hoziroq almashtirishni va saqlangan parollaringizni tekshirishni tavsiya etadi.</translation>
<translation id="219906046732893612">Uy-joylarni taʼmirlash</translation>
<translation id="2202020181578195191">Muddati tugaydigan yilni xatosiz kiriting</translation>
<translation id="22081806969704220">Tarnov 3</translation>
@@ -482,6 +485,8 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="2215727959747642672">Fayllarni tahrirlash</translation>
<translation id="2215963164070968490">Itlar</translation>
<translation id="2218879909401188352">Firibgarlar <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> saytida xavfli ilovalarni o‘rnatishlari mumkin. Bu ilovalar qurilmangizni buzishi, mobil hisobingizdan pul o‘g‘irlashi yoki shaxsiy ma’lumotlaringizni olishi mumkin. <ph name="BEGIN_LEARN_MORE_LINK" />Batafsil<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Qoʻllanmani boshidan boshlash</translation>
+<translation id="2219735899272417925">Qurilmani asliga qaytarish kerak</translation>
<translation id="2224337661447660594">Internet yo‘q</translation>
<translation id="2230458221926704099">Aloqani sozlash uchun <ph name="BEGIN_LINK" />tashxis ilovasidan<ph name="END_LINK" /> foydalaning</translation>
<translation id="2239100178324503013">Hozir yuborish</translation>
@@ -579,11 +584,13 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="2512101340618156538">Ruxsat berilmagan (standart)</translation>
<translation id="2512413427717747692">Chrome brauzerini asosiy brauzer tugmasi sifatida sozlash, iOS sozlamalari orqali Chrome brauzerini tizimning asosiy brauzeri sifatida sozlash uchun Enter tugmasini bosing.</translation>
<translation id="2515629240566999685">Mobil aloqa signalini tekshirish</translation>
+<translation id="2515761554693942801"><ph name="PROVIDER_ORIGIN" /> saytlarida shaxsni Touch ID yordamida tasdiqlash mumkin. Bu provayder toʻlov usulingiz axborotini saqlashi mumkin, lekin uning <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Quyi oʻngdan steplerlash</translation>
<translation id="2521736961081452453">Shakl yaratish</translation>
<translation id="2523886232349826891">Faqat shu qurilmada saqlanadi</translation>
<translation id="2524461107774643265">Qo‘shimcha axborot qo‘shish</translation>
<translation id="2529899080962247600">Bu maydonchadagi elementlar soni <ph name="MAX_ITEMS_LIMIT" /> yozuvdan oshmasligi kerak. Qolganlari inobatga olinmaydi.</translation>
+<translation id="253493526287553278">Promokod tafsilotlari</translation>
<translation id="2535585790302968248">Internetdan maxfiy foydalanish uchun yangi Inkognito varaq ochish</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{va yana 1 ta}other{va yana # ta}}</translation>
<translation id="2536110899380797252">Manzil kiritish</translation>
@@ -619,7 +626,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="259821504105826686">Fotografiya va raqamli sanʼat</translation>
<translation id="2601150049980261779">Melodramalar</translation>
<translation id="2604589665489080024">Pop musiqa</translation>
-<translation id="2609632851001447353">Variantlar</translation>
<translation id="2610561535971892504">Nusxa olish uchun bosing</translation>
<translation id="2617988307566202237">Chrome quyidagi maʼlumotlarni <ph name="BEGIN_EMPHASIS" />saqlamaydi<ph name="END_EMPHASIS" />:
<ph name="BEGIN_LIST" />
@@ -652,6 +658,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Tavallud va ism berish kunlari</translation>
<translation id="2677748264148917807">Tark etish</translation>
+<translation id="2679714844901977852">Xavfsiz va tez hisob-kitob qilish uchun Google hisobingizga (<ph name="USER_EMAIL" />) karta va hisob-kitob maʼlumotlarini saqlang</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Eng mos</translation>
<translation id="2688969097326701645">Ha, davom etilsin</translation>
@@ -700,7 +707,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="2854764410992194509">Internet provayderlar (ISP)</translation>
<translation id="2856444702002559011">Firibgarlar <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> saytidan ma’lumotlaringizni (parol, xabar va kredit karta ma’lumotlari kabilarni) o‘g‘irlashga urinayotgan bo‘lishi mumkin. <ph name="BEGIN_LEARN_MORE_LINK" />Batafsil<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Bu saytda yoqimsiz yoki befoyda reklamalar chiqadi.</translation>
-<translation id="286512204874376891">Virtual karta haqiqiy kartani ehtimoliy firibgarlikdan himoya qiladi. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Doʻstona</translation>
<translation id="28761159517501904">Filmlar</translation>
<translation id="2876489322757410363">Tashqi ilova orqali toʻlash uchun inkognito rejimidan chiqib ketasiz. Davom etasizmi?</translation>
@@ -801,7 +807,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="3158539265159265653">Disk</translation>
<translation id="3162559335345991374">Siz ulangan Wi-Fi tarmog‘i tizimga kirishingizni talab qilishi mumkin.</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">Orol</translation>
<translation id="3176929007561373547">Proksi sozlamalarini tekshiring yoki administratordan kerakli parametrlarni so‘rang. Aks holda:
<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">Qurilmadan foydalanish haqidagi maʼlumotlarga kirish</translation>
@@ -878,9 +883,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="3369192424181595722">Soat xatoligi</translation>
<translation id="3369459162151165748">Avtomobil ehtiyot qismlari va aksessuarlar</translation>
<translation id="3371064404604898522">Chrome brauzerini standart etib tayinlash</translation>
-<translation id="3371076217486966826"><ph name="URL" /> sayti quyidagi amallarga ruxsat olmoqchi:
- • Atrofingiz 3D xaritasini yaratish yoki kamera holatini kuzatish
- • Kameradan foydalanish</translation>
<translation id="337363190475750230">O‘chirib qo‘yilgan</translation>
<translation id="3375754925484257129">Chrome xavfsizlik tekshiruvini bajarish</translation>
<translation id="3377144306166885718">Server eskirgan TLS versiyasidan foydalanadi.</translation>
@@ -897,6 +899,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="3399952811970034796">Yetkazib berish manzili</translation>
<translation id="3402261774528610252">Bu sayt eskirgan TLS 1.0 yoki TLS 1.1 ishlatadi, shuning uchun keyingi versiyalarda faolsizlantiriladi. Faolsizlantirilsa, foydalanuvchilar bu saytni ocha olmaydi. Serverda TLS 1.2 yoki yuqoriroq versiya yoqilishi lozim.</translation>
<translation id="3405664148539009465">Shriftlarni sozlash</translation>
+<translation id="3407789382767355356">tashqi xizmatga kirish</translation>
<translation id="3409896703495473338">Xavfsizlik sozlamalarini boshqarish</translation>
<translation id="3414952576877147120">Hajmi:</translation>
<translation id="3417660076059365994">Siz yuklaydigan fayllar tekshiruv uchun Google Cloud yoki boshqa tashqi xizmatlarga yuboriladi. Masalan, tarkibida maxfiy yoki zararli axborotlar mavjudligini aniqlash uchun.</translation>
@@ -929,6 +932,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="3477679029130949506">Kinoteatrdagi filmlar va namoyish vaqtlari</translation>
<translation id="3479552764303398839">Hozir emas</translation>
<translation id="3484560055331845446">Google hisobingiz xavf ostida. Parolingizni yangilashni tavsiya qilamiz. Hisobingizga qayta kirishingiz talab qilinadi.</translation>
+<translation id="3484861421501147767">Eslatma: Saqlangan promo kod mavjud</translation>
<translation id="3487845404393360112">Tarnov 4</translation>
<translation id="3495081129428749620">“<ph name="PAGE_TITLE" />†sahifasidan qidirish</translation>
<translation id="350069200438440499">Fayl nomi:</translation>
@@ -1051,6 +1055,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="3810973564298564668">Sozlash</translation>
<translation id="3816482573645936981">Qiymat (almashtirildi)</translation>
<translation id="382518646247711829">Agar proksi-server ishlatsangiz...</translation>
+<translation id="3826050100957962900">Tashqi xizmatga kirish</translation>
<translation id="3827112369919217609">Mutlaq</translation>
<translation id="3827666161959873541">Oilaviy filmlar</translation>
<translation id="3828924085048779000">Parol jumlasini bo‘sh qoldirib bo‘lmaydi.</translation>
@@ -1063,9 +1068,9 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="3858027520442213535">Sana va vaqtni yangilash</translation>
<translation id="3858860766373142691">Ism</translation>
<translation id="3872834068356954457">Ilm-fan</translation>
+<translation id="3875783148670536197">Koʻrsatish</translation>
<translation id="3881478300875776315">Kamroq qatorlarni koʻrsatish</translation>
<translation id="3884278016824448484">Ziddiyatli qurilma identifikatori</translation>
-<translation id="3885155851504623709">Okrug</translation>
<translation id="388632593194507180">Kuzatuv aniqlandi</translation>
<translation id="3886948180919384617">Taxlovchi 3</translation>
<translation id="3890664840433101773">Email manzilini kiriting</translation>
@@ -1095,9 +1100,11 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="3973234410852337861"><ph name="HOST_NAME" /> saytiga kirish taqiqlangan</translation>
<translation id="3978338123949022456">Qidiruv rejimi, soʻrovni kiriting va <ph name="KEYWORD_SUFFIX" /> orqali qidirish uchun Enter tugmasini bosing</translation>
<translation id="398470910934384994">Qushlar</translation>
+<translation id="3985750352229496475">Manzillarni boshqarish...</translation>
<translation id="3986705137476756801">Jonli izohni hozircha faolsizlantirish</translation>
<translation id="3987940399970879459">1 MBdan kam</translation>
<translation id="3990250421422698716">Qogʻozlarni ajratib toʻplash</translation>
+<translation id="3992684624889376114">Bu sahifa haqida</translation>
<translation id="3996311196211510766"><ph name="ORIGIN" /> sayti manba siyosati tekshiruvini talab qildi
lekin bu tekshiruv barcha soʻrovlar uchun ishlatilganda bu siyosat uchun qoʻllanilmaydi.</translation>
<translation id="4006465311664329701">Google Pay xizmatidagi bank kartalari, takliflar va manzillar</translation>
@@ -1222,6 +1229,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="4305666528087210886">Fayldan foydalanishga ruxsat berilmadi</translation>
<translation id="4306529830550717874">Manzil saqlansinmi?</translation>
<translation id="4306812610847412719">vaqtinchalik xotira</translation>
+<translation id="4310070645992025887">Sayohatlar xizmatidan qidirish</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Bloklansin (standart parametr)</translation>
<translation id="4314815835985389558">Sinxronizatsiyani boshqarish</translation>
@@ -1272,6 +1280,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">Proksi-server o‘chiq, lekin uning sozlamalari aniq belgilangan.</translation>
<translation id="4441832193888514600">Parametr faqat bulutdagi foydalanuvchilar siyosati tomonidan belgilanishi mumkinligi uchun inkor etildi.</translation>
+<translation id="4442470707340296952">Chrome varaqlari</translation>
<translation id="4450893287417543264">Boshqa ko‘rsatilmasin</translation>
<translation id="4451135742916150903">HID qurilmalarga ulanish uchun ruxsat soʻrashi mumkin</translation>
<translation id="4452328064229197696">Hozirgina siz foydalangan parol oshkor qilingan. Hisoblaringizni himoyalash maqsadida Google Parollar menejeri saqlangan parollaringizni tekshirishni tavsiya etadi.</translation>
@@ -1410,6 +1419,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="483241715238664915">Ogohlantiruvlarni yoqish</translation>
<translation id="4834250788637067901">Google Pay xizmatidagi bank kartalari, takliflar va manzillar</translation>
<translation id="4838327282952368871">Xayolparast</translation>
+<translation id="4839087176073128681">Keyingi safar Googlening eng oxirgi toʻlov tizimi bilan tezroq toʻlang va kartangizni himoyalang.</translation>
<translation id="4840250757394056958">Chrome tarixini koʻrish</translation>
<translation id="484462545196658690">Avto</translation>
<translation id="484671803914931257"><ph name="MERCHANT_NAME" /> va doʻkonlardan chegirmalar</translation>
@@ -1472,6 +1482,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="5011561501798487822">Til aniqlandi</translation>
<translation id="5015510746216210676">Kompyuter nomi:</translation>
<translation id="5017554619425969104">Nusxalangan matn</translation>
+<translation id="5017828934289857214">Keyinroq eslatilsin</translation>
<translation id="5018422839182700155">Sahifani ochib bo‘lmadi</translation>
<translation id="5019198164206649151">Ombordagi ma’lumotlar shikastlangan</translation>
<translation id="5020776957610079374">Etnik musiqa</translation>
@@ -1479,7 +1490,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="5029568752722684782">Nusxani o‘chirib tashlash</translation>
<translation id="5030338702439866405">Berilgan vaqt:</translation>
<translation id="503069730517007720">“<ph name="SOFTWARE_NAME" />†uchun negiz sertifikati zarur, lekin o‘rnatilmagan. Tarmoq administratori bu muammoning yechimini “<ph name="SOFTWARE_NAME" />†konfiguratsiyasi ko‘rsatmalaridan izlashi zarur. <ph name="FURTHER_EXPLANATION" /></translation>
-<translation id="5031870354684148875">Google Tarjimon haqida</translation>
+<translation id="5031870354684148875">Google Tarjima haqida</translation>
<translation id="503498442187459473"><ph name="HOST" /> kamera va mikrofoningizdan foydalanishni istaydi</translation>
<translation id="5035135400558156732">Bogʻdorchilik</translation>
<translation id="5039762155821394373">Shrift oʻlchami</translation>
@@ -1491,6 +1502,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="5051305769747448211">Jonli komediya</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Nearby Share yordamida bu faylni yuborish uchun qurilmangizda (<ph name="DISK_SPACE_SIZE" />) joy boʻshating}other{Nearby Share yordamida bu faylni yuborish uchun qurilmangizda (<ph name="DISK_SPACE_SIZE" />) joy boʻshating}}</translation>
<translation id="5056549851600133418">Sizga atalgan maqolalar</translation>
+<translation id="5060483733937416656"><ph name="PROVIDER_ORIGIN" /> saytlarida shaxsni Windows Hello yordamida tasdiqlash mumkin. Bu provayder toʻlov usulingiz axborotini saqlashi mumkin, lekin uning <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Buni nazarda tutdingizmi: <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Chop etish tarixi</translation>
<translation id="5068234115460527047">Xedj fondlari</translation>
@@ -1504,10 +1516,8 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="5087286274860437796">Server sertifikati ayni paytda yaroqsiz.</translation>
<translation id="5087580092889165836">Yangi karta qo‘shish</translation>
<translation id="5088142053160410913">Operatorga xabar yuborish</translation>
-<translation id="5089810972385038852">Shtat</translation>
<translation id="5093232627742069661">Z shaklida taxlash</translation>
<translation id="5094747076828555589">Bu <ph name="DOMAIN" /> serveri ekanligini tasdiqlab bo‘lmadi. Uning havfsizlik sertifikati Chromium‘ga ishonchli tuyulmayapti. Server noto‘g‘ri sozlangan yoki kimdir ma’lumotlaringizni o‘g‘rilashga urinayotgan bo‘lishi mumkin.</translation>
-<translation id="5095208057601539847">Tuman</translation>
<translation id="5097099694988056070">Protsessor yoki operativ xotiradan foydalanish (CPU/RAM) statistikasi</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Sayt xavfsiz emas</translation>
@@ -1545,6 +1555,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="5171045022955879922">Qidiring yoki URL manzili kiriting</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Mahalliy kompyuter</translation>
+<translation id="5177076414499237632">Bu sahifa manbasi va mavzusi haqida batafsil</translation>
<translation id="5179510805599951267">Bu <ph name="ORIGINAL_LANGUAGE" /> tili emasmi? Xatolik haqida xabar yuboring</translation>
<translation id="518639307526414276">Uy hayvonlari uchun oziq-ovqat va parvarishlash vositalari</translation>
<translation id="5190835502935405962">Xatcho‘plar paneli</translation>
@@ -1698,13 +1709,14 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="5607240918979444548">Architecture-C</translation>
<translation id="5610142619324316209">Internet aloqasini tekshiring</translation>
<translation id="5610807607761827392">Karta va manzillarni <ph name="BEGIN_LINK" />Sozlamalar<ph name="END_LINK" /> orqali boshqarish mumkin.</translation>
-<translation id="561165882404867731">Bu sahifani Google Tarjimon bilan tarjima qilish</translation>
+<translation id="561165882404867731">Bu sahifani Google Tarjima bilan tarjima qilish</translation>
<translation id="5612720917913232150"><ph name="URL" /> sayti kompyuteringiz joylashuvi ma’lumotlaridan foydalanmoqchi</translation>
<translation id="561669346091975195">Ilmiy fantastik va fentezi TV seriallar</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> sayti juda ko‘p marta qayta yo‘naltirildi.</translation>
<translation id="5624120631404540903">Sozlash</translation>
<translation id="5629630648637658800">Siyosat sozlamalarini qo‘llab bo‘lmadi</translation>
<translation id="5631439013527180824">Qurilma tokeni yaroqsiz</translation>
+<translation id="5632485077360054581">Koʻrsatish</translation>
<translation id="5633066919399395251">Firibgarlar ma’lumotlaringizni (rasm, parol, xabar va kredit karta ma’lumotlari kabilarni) o‘g‘irlash yoki o‘chirish maqsadida <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sayti orqali kompyuteringizga zararli dasturlarni o‘rnatishi mumkin. <ph name="BEGIN_LEARN_MORE_LINK" />Batafsil<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Soxta kontent bloklandi.</translation>
<translation id="5633259641094592098">Kult va indi filmlar</translation>
@@ -1822,6 +1834,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="5989320800837274978">Sozlangan proksi serverlar ham, PAC-skriptlar URL manzillari ham ko‘rsatilmagan.</translation>
<translation id="5992691462791905444">Engineering-Z shaklida taxlash</translation>
<translation id="5995727681868049093">Google hisobingizdagi axborotingiz, maxfiylik va xavfsizlik sozlamalarini boshqarish</translation>
+<translation id="5997247540087773573">Hozirgina siz foydalangan parol oshkor qilingan. Hisoblaringizni himoyalash maqsadida Google Parollar menejeri uni hoziroq almashtirib, saqlangan parollaringizni tekshirishni tavsiya etadi.</translation>
<translation id="6000758707621254961">“<ph name="SEARCH_TEXT" />†uchun <ph name="RESULT_COUNT" /> ta natija</translation>
<translation id="6006484371116297560">Klassik</translation>
<translation id="6008122969617370890">N dan 1 gacha tartibda</translation>
@@ -1916,7 +1929,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="627746635834430766">Keyingi safar tezroq to‘lash uchun kartangizni Google hisobingizga saqlang.</translation>
<translation id="6279183038361895380">Kursorni ko‘rish uchun |<ph name="ACCELERATOR" />| tugmasini bosing</translation>
<translation id="6280223929691119688">Bu manzilga yetkazib bera olmaymiz. Boshqa manzilni tanlang.</translation>
-<translation id="6282194474023008486">Pochta indeksi</translation>
<translation id="6285507000506177184">Chromeda yuklanmalarni boshqarish tugmasi, Chromeda yuklab olingan fayllarni boshqarish uchun Enter tugmasini bosing</translation>
<translation id="6289939620939689042">Sahifa rangi</translation>
<translation id="6290238015253830360">Tavsiya etiladigan maqolalar shu yerda chiqadi</translation>
@@ -1938,6 +1950,7 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="6337133576188860026">Xotirada <ph name="SIZE" /> tozalandi. Bundan keyin ba’zi veb-saytlar sekinroq yuklanishi mumkin.</translation>
<translation id="6337534724793800597">Qoidalarni nomi bo‘yicha filtrlash</translation>
<translation id="6340739886198108203">Maxfiy kontent koʻringanda administrator skrinshot olish yoki yozib olishni tavsiya qilmaydi:</translation>
+<translation id="6348220984832452017">Joriy oʻzgarishlar</translation>
<translation id="6349101878882523185"><ph name="APP_NAME" /> ilovasini oʻrnatish</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Hech qanday}=1{1 ta parol (<ph name="DOMAIN_LIST" /> uchun, sinxronlandi)}=2{2 ta parol (<ph name="DOMAIN_LIST" /> uchun, sinxronlandi)}other{# ta parol (<ph name="DOMAIN_LIST" /> uchun, sinxronlandi)}}</translation>
<translation id="6355392890578844978">Bu brauzer kompaniya yoki tashkilot boshqaruvida emas. Bu qurilmadagi amallar Chromiumdan tashqarida boshqarilishi mumkin. <ph name="BEGIN_LINK" />Batafsil<ph name="END_LINK" /></translation>
@@ -1969,7 +1982,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="643051589346665201">Google parolini oʻzgartirish</translation>
<translation id="6433490469411711332">Aloqa ma’lumotini tahrirlash</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> serveri ulanish so‘rovini rad etdi.</translation>
-<translation id="6438025220577812695">Oʻzim oʻzgartiraman</translation>
<translation id="6440503408713884761">Rad etilgan</translation>
<translation id="6443406338865242315">Oʻrnatilgan kengaytma va plaginlar</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2099,9 +2111,9 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="6828866289116430505">Genetika</translation>
<translation id="6831043979455480757">Tarjima</translation>
<translation id="6833752742582340615">Xavfsiz va tez hisob-kitob qilish uchun Google hisobingizga karta va hisob-kitob maʼlumotlarini saqlang</translation>
-<translation id="6839929833149231406">Tuman</translation>
<translation id="6846340164947227603">Virtual karta raqamidan foydalanish</translation>
<translation id="6852204201400771460">Ilova qayta yuklansinmi?</translation>
+<translation id="6857776781123259569">Parollarni boshqarish...</translation>
<translation id="686485648936420384">Isteʼmolchilar uchun</translation>
<translation id="6865412394715372076">Hozir bu kartani tekshirib bo‘lmaydi</translation>
<translation id="6869334554832814367">Isteʼmol krediti</translation>
@@ -2150,7 +2162,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="6965978654500191972">Qurilma</translation>
<translation id="696703987787944103">Perseptsion</translation>
<translation id="6968269510885595029">Elektron kalitdan foydalanish</translation>
-<translation id="6970216967273061347">Tuman</translation>
<translation id="6971439137020188025">Google Slidesda yangi taqdimotni tez yaratish</translation>
<translation id="6972629891077993081">HID qurilmalar</translation>
<translation id="6973656660372572881">Sozlangan proksi-serverlar va PAC-skriptlar URL manzillari ko‘rsatilgan</translation>
@@ -2189,7 +2200,6 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<translation id="7081308185095828845">Bu funksiya qurilmangizda ishlamaydi</translation>
<translation id="7083258188081898530">Tarnov 9</translation>
<translation id="7086090958708083563">Foydalanuvchi hisobot yuklanishini talab qilgan</translation>
-<translation id="7087282848513945231">Okrug</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome sozlamalari orqali ruxsatlar va saytlarda saqlanadigan maʼlumotlarni boshqarish uchun avval Tab, keyin Enter tugmasini bosing</translation>
<translation id="7096937462164235847">Bu saytning identifikatsiya maʼlumotlari tekshirilmadi.</translation>
<translation id="7101893872976785596">Dahshatli filmlar</translation>
@@ -2208,10 +2218,10 @@ Aks holda bu maxfiylik sozlamalaringiz tomonidan bloklanadi. Bunda siz ochgan sa
<ph name="LIST_ITEM" />Shakllarga kiritilgan maʼlumotlar<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Bu manzilga yetkazib bera olmaymiz. Boshqa manzilni tanlang.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Administrator bu maʼlumotlardan nusxa olishni taqiqlagan.</translation>
<translation id="7135130955892390533">Holat axboroti</translation>
<translation id="7138472120740807366">Yetkazib berish usuli</translation>
-<translation id="7139724024395191329">Amirlik</translation>
<translation id="7139892792842608322">Asosiy tarnov</translation>
<translation id="714064300541049402">Tasvirning orqa tomondagi X oʻqidagi siljish</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2428,6 +2438,7 @@ Batafsil axborot:
<translation id="7669271284792375604">Bu sayt qurilmangizga brauzerning ishlashiga xalaqit qiluvchi (masalan, boshlang‘ich sahifani o‘zgartiruvchi yoki saytlarda qo‘shimcha reklamalarni ko‘rsatuvchi) xavfli dasturlarni o‘rnatishi mumkin.</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Maʼlumotlar bilan bajariladigan amallar maxfiy hisoblanadi (oxirgi kirishdan keyin 1 ta amal xabar qilingan). <ph name="BEGIN_LINK" />Batafsil<ph name="END_LINK" />}other{Maʼlumotlar bilan bajariladigan amallar maxfiy hisoblanadi (oxirgi kirishdan keyin # ta amal xabar qilingan). <ph name="BEGIN_LINK" />Batafsil<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Pochta qutisi 6</translation>
+<translation id="7675325315208090829">Toʻlov usullarini boshqarish...</translation>
<translation id="7676643023259824263">Klipborddagi matnni qidirish, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Chrome sozlamalari orqali brauzer tarixini koʻrish va boshqarish</translation>
<translation id="7679947978757153706">Beysbol</translation>
@@ -2470,7 +2481,6 @@ Batafsil axborot:
<translation id="7766518757692125295">Yubka</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Bir xil tartibda old tomonida</translation>
-<translation id="777702478322588152">Prefektura</translation>
<translation id="7791011319128895129">Hali chiqmagan</translation>
<translation id="7791196057686275387">Beyl</translation>
<translation id="7791543448312431591">Qo‘shish</translation>
@@ -2561,12 +2571,12 @@ Batafsil axborot:
<translation id="8055534648776115597">Taʼtildagi va davomiy taʼlim</translation>
<translation id="8057711352706143257">“<ph name="SOFTWARE_NAME" />†noto‘g‘ri sozlangan. “<ph name="SOFTWARE_NAME" />†dasturini o‘chirib ko‘ring. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Oziq-ovqat ishlab chiqarish</translation>
-<translation id="8066955247577885446">Xatolik yuz berdi.</translation>
<translation id="8067872629359326442">Hozirgina shubhali saytda parol kiritdingiz. Chromium yordamga tayyor. Parolni almashtirish va Googlega bu hisob xavf ostida ekanini xabar qilish uchun Hisobni himoyalash ustiga bosing.</translation>
<translation id="8070439594494267500">Ilova belgisi</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Google Sheetda yangi jadvalni tez yaratish</translation>
<translation id="8075898834294118863">Sayt sozlamalarini boshqarish</translation>
+<translation id="8076492880354921740">Varaqlar</translation>
<translation id="8078141288243656252">Aylantirilganda izohlanmaydi</translation>
<translation id="8079031581361219619">Sayt qayta yuklansinmi?</translation>
<translation id="8081087320434522107">Sedanlar</translation>
@@ -2689,6 +2699,7 @@ Batafsil axborot:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Sozlamalar</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome sozlamalari orqali cookie parametrlarini boshqarish uchun avval Tab, keyin Enter tugmasini bosing</translation>
<translation id="8433057134996913067">Bu amal sizni ko‘p saytlardagi hisoblaringizdan chiqarib yuboradi.</translation>
<translation id="8434840396568290395">Uy hayvonlari</translation>
@@ -2786,6 +2797,7 @@ Batafsil axborot:
<translation id="8742371904523228557"><ph name="ORIGIN" /> sayti uchun tasdiq kodingiz: <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">Bu varaqni bukmarklash</translation>
<translation id="8751426954251315517">Keyingi safar qayta urining</translation>
+<translation id="8757526089434340176">Google Pay taklifi mavjud</translation>
<translation id="8758885506338294482">Raqobatbardosh video oʻyinlar</translation>
<translation id="8759274551635299824">Kredit kartaning amal qilish muddati tugagan</translation>
<translation id="87601671197631245">Bu sayt eskirgan xavfsizlik sozlamasidan foydalanadi. maʼlumotlaringiz (masalan, parollar, xabarlar va kredit kartalari) shu saytga yuborilganda ular xavf ostida qolishi mumkin.</translation>
@@ -2793,6 +2805,7 @@ Batafsil axborot:
<translation id="8763927697961133303">USB qurilma</translation>
<translation id="8763986294015493060">Hozir ochilgan barcha Inkognito varaqlarni yopish</translation>
<translation id="8766943070169463815">Xavfsiz toʻlovlar uchun hisob maʼlumotlarini tekshirish ekrani ochiq</translation>
+<translation id="8767765348545497220">Yordam bildirgisini yopish</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Mototsikllar</translation>
<translation id="8790007591277257123">&amp;Qayta o‘chirish</translation>
@@ -2805,6 +2818,7 @@ Batafsil axborot:
<translation id="8806285662264631610">Vanna va tana parvarishi mahsulotlari</translation>
<translation id="8807160976559152894">Har bir sahifa chekkasini qirqish</translation>
<translation id="8808828119384186784">Chrome sozlamalari</translation>
+<translation id="8813277370772331957">Keyinroq eslatilsin</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome sozlamalari orqali Chromeni yangilash uchun Enter tugmasini bosing</translation>
<translation id="8820817407110198400">Bukmarklar</translation>
<translation id="882338992931677877">Mustaqil orqaliq</translation>
@@ -2897,7 +2911,7 @@ Batafsil axborot:
<translation id="9065745800631924235">tarixdan <ph name="TEXT" /> deb qidiring</translation>
<translation id="9069693763241529744">Kengaytma bloklagan</translation>
<translation id="9073799351042754113">Bu sayt uchun xavfsizlik ogohlantiruvlari faolsizlantirilgan.</translation>
-<translation id="9078964945751709336">Yana ko‘proq ma’lumot so‘ralmoqda</translation>
+<translation id="9078964945751709336">Axborot yetarli emas</translation>
<translation id="9080712759204168376">Buyurtma axboroti</translation>
<translation id="908796725011587895">Kollej sporti</translation>
<translation id="9089260154716455634">Qoidalar amal qilmaydigan vaqt:</translation>
@@ -2984,6 +2998,7 @@ Batafsil axborot:
<translation id="988159990683914416">Dasturchilar yig‘masi</translation>
<translation id="989988560359834682">Manzilni o‘zgartirish</translation>
<translation id="991413375315957741">harakat va yorugʻlik sensorlari</translation>
+<translation id="992110854164447044">Virtual karta haqiqiy kartani ehtimoliy firibgarlikdan himoya qilish maqsadida berkitadi. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Pushti</translation>
<translation id="992432478773561401">“<ph name="SOFTWARE_NAME" />†dasturi noto‘g‘ri o‘rnatilgan:
diff --git a/chromium/components/strings/components_strings_vi.xtb b/chromium/components/strings/components_strings_vi.xtb
index 3dfcb6d7633..19b86845ab4 100644
--- a/chromium/components/strings/components_strings_vi.xtb
+++ b/chromium/components/strings/components_strings_vi.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Trợ cấp, há»c bổng và há»— trợ tài chính</translation>
<translation id="1048785276086539861">Khi bạn chỉnh sửa chú thích, tài liệu này sẽ trở vỠchế độ xem một trang</translation>
<translation id="1050038467049342496">Äóng các ứng dụng khác</translation>
+<translation id="1053959602163383901">Bạn đã chá»n xác minh bằng má»™t thiết bị xác thá»±c trên các trang web sá»­ dụng <ph name="PROVIDER_ORIGIN" />. Nhà cung cấp này có thể đã lÆ°u trữ thông tin vá» phÆ°Æ¡ng thức thanh toán của bạn mà bạn có thể <ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Hoàn tác thêm</translation>
<translation id="1056663316309890343">Phần má»m ảnh</translation>
<translation id="1056898198331236512">Cảnh báo</translation>
@@ -41,7 +42,7 @@
<translation id="1072594122896439679">Nhạc và âm thanh</translation>
<translation id="1074497978438210769">Không bảo mật</translation>
<translation id="1075079914415273530">Nông nghiệp và lâm nghiệp</translation>
-<translation id="1080116354587839789">Vừa vá»›i chiá»u rá»™ng</translation>
+<translation id="1080116354587839789">Chỉnh cho vừa vá»›i chiá»u rá»™ng</translation>
<translation id="1081061862829655580">Khay 19</translation>
<translation id="1086953900555227778">Index-5x8</translation>
<translation id="1088860948719068836">Thêm tên trên thẻ</translation>
@@ -119,6 +120,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="1270502636509132238">Phương thức nhận hàng</translation>
<translation id="1281476433249504884">Khay xếp chồng 1</translation>
<translation id="1285320974508926690">Không bao giỠdịch trang web này</translation>
+<translation id="1288548991597756084">Lưu thẻ một cách an toàn</translation>
<translation id="1292571435393770077">Khay 16</translation>
<translation id="1292701964462482250">"Phần má»m trên máy tính của bạn Ä‘ang ngăn không cho Chrome kết nối an toàn vá»›i web" (chỉ trên máy tính Windows)</translation>
<translation id="1294154142200295408">Các biến thể của dòng lệnh</translation>
@@ -223,6 +225,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
&lt;p&gt;Äể khắc phục lá»—i này, hãy nhấp vào &lt;strong&gt;Kết nối&lt;/strong&gt; trên trang mà bạn Ä‘ang cố mở.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Thiết kế cảnh quan</translation>
<translation id="1513706915089223971">Danh sách các mục lịch sử</translation>
+<translation id="1516097932025103760">Thẻ sẽ được mã hóa, lưu một cách bảo mật, còn CVC sẽ không được lưu trữ.</translation>
<translation id="1517433312004943670">Phải có số điện thoại</translation>
<translation id="1519264250979466059">Ngày tạo</translation>
<translation id="1521159554480556801">Nghệ thuật sợi và dệt</translation>
@@ -288,6 +291,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="1662550410081243962">LÆ°u và Ä‘iá»n phÆ°Æ¡ng thức thanh toán</translation>
<translation id="1663943134801823270">Thẻ và địa chỉ từ Chrome. Bạn có thể quản lý thẻ và địa chỉ trong <ph name="BEGIN_LINK" />Cài đặt<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Kể từ bây giỠtrở đi, các trang viết bằng <ph name="SOURCE_LANGUAGE" /> sẽ được dịch sang <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="1673886523110456987">Thanh toán bằng <ph name="CARD_DETAIL" /> để sử dụng ưu đãi</translation>
<translation id="1674504678466460478"><ph name="SOURCE_LANGUAGE" /> sang <ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Cạnh ngắn trước</translation>
<translation id="168693727862418163">Giá trị chính sách này không xác thực được dựa vào giản đồ và sẽ bị bỠqua.</translation>
@@ -306,6 +310,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="1717218214683051432">Cảm biến chuyển động</translation>
<translation id="1717494416764505390">Há»™p thÆ° 3</translation>
<translation id="1718029547804390981">Tài liệu quá lớn nên không thể chú thích được</translation>
+<translation id="1720941539803966190">Äóng hÆ°á»›ng dẫn</translation>
<translation id="1721424275792716183">TrÆ°á»ng * là bắt buá»™c</translation>
<translation id="1727613060316725209">Chứng chỉ hợp lệ</translation>
<translation id="1727741090716970331">Thêm số thẻ hợp lệ</translation>
@@ -422,8 +427,8 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="205212645995975601">BBQ và đồ nướng</translation>
<translation id="2053111141626950936">Các trang viết bằng <ph name="LANGUAGE" /> sẽ không được dịch.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Khi chế Ä‘á»™ Ä‘iá»u khiển này bật và trạng thái là Ä‘ang hoạt Ä‘á»™ng, Chrome sẽ xác định xem hoạt Ä‘á»™ng duyệt web gần đây của bạn giống vá»›i nhóm đông ngÆ°á»i hoặc "nhóm thuần tập" nào nhất. Các nhà quảng cáo có thể chá»n quảng cáo cho nhóm này và hoạt Ä‘á»™ng duyệt web sẽ được lÆ°u giữ riêng tÆ° trên thiết bị của bạn. Nhóm của bạn được cập nhật hằng ngày.}=1{Khi chế Ä‘á»™ Ä‘iá»u khiển này bật và trạng thái là Ä‘ang hoạt Ä‘á»™ng, Chrome sẽ xác định xem hoạt Ä‘á»™ng duyệt web gần đây của bạn giống vá»›i nhóm đông ngÆ°á»i hoặc "nhóm thuần tập" nào nhất. Các nhà quảng cáo có thể chá»n quảng cáo cho nhóm này và hoạt Ä‘á»™ng duyệt web sẽ được lÆ°u giữ riêng tÆ° trên thiết bị của bạn. Nhóm của bạn được cập nhật hằng ngày.}other{Khi chế Ä‘á»™ Ä‘iá»u khiển này bật và trạng thái là Ä‘ang hoạt Ä‘á»™ng, Chrome sẽ xác định xem hoạt Ä‘á»™ng duyệt web gần đây của bạn giống vá»›i nhóm đông ngÆ°á»i hoặc "nhóm thuần tập" nào nhất. Các nhà quảng cáo có thể chá»n quảng cáo cho nhóm này và hoạt Ä‘á»™ng duyệt web sẽ được lÆ°u giữ riêng tÆ° trên thiết bị của bạn. Nhóm của bạn được cập nhật {NUM_DAYS} ngày má»™t lần.}}</translation>
-<translation id="2053553514270667976">Mã ZIP</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 đỠxuất}other{# đỠxuất}}</translation>
+<translation id="2066915425250589881">yêu cầu xóa</translation>
<translation id="2068528718802935086">Em bé và trẻ mới biết đi</translation>
<translation id="2071156619270205202">Thẻ này không đáp ứng Ä‘iá»u kiện để tạo số thẻ ảo.</translation>
<translation id="2071692954027939183">Các thông báo tá»± Ä‘á»™ng bị chặn do bạn thÆ°á»ng xuyên không cho phép các thông báo đó</translation>
@@ -435,7 +440,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="2088086323192747268">Nút Quản lý dữ liệu đồng bộ hóa, hãy nhấn Enter để quản lý loại thông tin bạn đồng bộ hóa trong phần Cài đặt của Chrome</translation>
<translation id="2091887806945687916">Âm thanh</translation>
<translation id="2094505752054353250">Miá»n không khá»›p</translation>
-<translation id="2096368010154057602">Khu vực hành chính</translation>
<translation id="2099652385553570808">Dập 3 ghim bên trái</translation>
<translation id="2101225219012730419">Phiên bản:</translation>
<translation id="2102134110707549001">Äá» xuất mật khẩu mạnh…</translation>
@@ -472,7 +476,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="2185836064961771414">Bóng bầu dục Mỹ</translation>
<translation id="2187317261103489799">Phát hiện (mặc định)</translation>
<translation id="2188375229972301266">Äục nhiá»u lá»— dÆ°á»›i cùng</translation>
-<translation id="2188852899391513400">Mật khẩu bạn vừa sá»­ dụng đã bị lá»™ trong má»™t sá»± cố rò rỉ dữ liệu. Äể bảo mật tài khoản, Trình quản lý mật khẩu của Google khuyên bạn nên thay đổi mật khẩu này ngay, sau đó kiểm tra các mật khẩu bạn đã lÆ°u.</translation>
<translation id="219906046732893612">Sửa sang nhà cửa</translation>
<translation id="2202020181578195191">Nhập năm hết hạn hợp lệ</translation>
<translation id="22081806969704220">Khay 3</translation>
@@ -483,6 +486,8 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="2215727959747642672">Chỉnh sửa tệp</translation>
<translation id="2215963164070968490">Chó</translation>
<translation id="2218879909401188352">Những kẻ tấn công hiện ở trên <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> có thể cài đặt ứng dụng nguy hiểm làm há»ng thiết bị của bạn, thêm các khoản phí ẩn vào hóa Ä‘Æ¡n di Ä‘á»™ng hoặc lấy cắp thông tin cá nhân của bạn. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Khởi động lại hướng dẫn</translation>
+<translation id="2219735899272417925">Yêu cầu thiết lập lại thiết bị</translation>
<translation id="2224337661447660594">Không có Internet</translation>
<translation id="2230458221926704099">Sửa kết nối bằng <ph name="BEGIN_LINK" />ứng dụng chẩn đoán<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Gá»­i bây giá»</translation>
@@ -580,11 +585,13 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="2512101340618156538">Không được phép (mặc định)</translation>
<translation id="2512413427717747692">Äặt Chrome làm nút trình duyệt mặc định, nhấn Enter để đặt Chrome làm trình duyệt mặc định của hệ thống trong phần cài đặt iOS</translation>
<translation id="2515629240566999685">Kiểm tra tín hiệu trong khu vực của bạn</translation>
+<translation id="2515761554693942801">Bạn đã chá»n xác minh bằng Touch ID trên các trang web sá»­ dụng <ph name="PROVIDER_ORIGIN" />. Nhà cung cấp này có thể đã lÆ°u trữ thông tin vá» phÆ°Æ¡ng thức thanh toán của bạn mà bạn có thể <ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Dập ghim dưới cùng bên phải</translation>
<translation id="2521736961081452453">Tạo biểu mẫu</translation>
<translation id="2523886232349826891">Chỉ lưu trên thiết bị này</translation>
<translation id="2524461107774643265">Thêm thông tin khác</translation>
<translation id="2529899080962247600">TrÆ°á»ng này không được chứa nhiá»u hÆ¡n <ph name="MAX_ITEMS_LIMIT" /> mục. Tất cả mục thừa sẽ bị bá» qua.</translation>
+<translation id="253493526287553278">Xem chi tiết mã khuyến mại</translation>
<translation id="2535585790302968248">Mở một thẻ ẩn danh mới để duyệt web ở chế độ riêng tư</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{và 1 miá»n khác}other{và # miá»n khác}}</translation>
<translation id="2536110899380797252">Thêm địa chỉ</translation>
@@ -620,7 +627,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="259821504105826686">Nghệ thuật kỹ thuật số và nhiếp ảnh</translation>
<translation id="2601150049980261779">Phim lãng mạn</translation>
<translation id="2604589665489080024">Nhạc pop</translation>
-<translation id="2609632851001447353">Các biến thể</translation>
<translation id="2610561535971892504">Nhấp để sao chép</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />sẽ không lưu<ph name="END_EMPHASIS" /> những thông tin sau:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Sinh nhật và ngày đặt tên</translation>
<translation id="2677748264148917807">Rá»i khá»i</translation>
+<translation id="2679714844901977852">Lưu thông tin thẻ và thông tin thanh toán vào Tài khoản Google <ph name="USER_EMAIL" /> để thanh toán an toàn và nhanh hơn</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Phù hợp nhất</translation>
<translation id="2688969097326701645">Có, tiếp tục</translation>
@@ -701,7 +708,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="2854764410992194509">Nhà cung cấp dịch vụ Internet (ISP)</translation>
<translation id="2856444702002559011">Những kẻ tấn công có thể đang cố gắng đánh cắp thông tin của bạn từ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (ví dụ: mật khẩu, thư hoặc thẻ tín dụng). <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Trang web này hiển thị quảng cáo xâm nhập hoặc quảng cáo gây hiểu nhầm.</translation>
-<translation id="286512204874376891">Thẻ ảo giúp ngụy trang thẻ thá»±c của bạn để bảo vệ bạn khá»i nguy cÆ¡ bị lừa đảo.<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Thân thiện</translation>
<translation id="28761159517501904">Phim</translation>
<translation id="2876489322757410363">Äang thoát khá»i chế Ä‘á»™ Ẩn danh để thanh toán qua má»™t ứng dụng bên ngoài. Bạn muốn tiếp tục?</translation>
@@ -802,7 +808,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="3158539265159265653">ÄÄ©a</translation>
<translation id="3162559335345991374">Wi-Fi mà bạn đang sử dụng có thể yêu cầu bạn phải truy cập trang đăng nhập của mạng đó.</translation>
<translation id="3169472444629675720">Khám phá</translation>
-<translation id="3174168572213147020">Äảo</translation>
<translation id="3176929007561373547">Kiểm tra cài đặt proxy của bạn hoặc liên hệ với quản trị viên mạng để
đảm bảo rằng máy chủ proxy đang hoạt động. Nếu bạn cho rằng mình không cần
sử dụng máy chủ proxy:
@@ -881,9 +886,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="3369192424181595722">Lỗi đồng hồ</translation>
<translation id="3369459162151165748">Phụ tùng và phụ kiện xe cộ</translation>
<translation id="3371064404604898522">Äặt Chrome làm trình duyệt mặc định</translation>
-<translation id="3371076217486966826"><ph name="URL" /> muốn:
- • Tạo bản đồ 3D vỠcác khu vực xung quanh và theo dõi thông tin vị trí của máy ảnh
- • Dùng máy ảnh của bạn</translation>
<translation id="337363190475750230">Äã hủy cấp phép</translation>
<translation id="3375754925484257129">Chạy quy trình kiểm tra an toàn trên Chrome</translation>
<translation id="3377144306166885718">Máy chủ sá»­ dụng phiên bản TLS lá»—i thá»i.</translation>
@@ -900,6 +902,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="3399952811970034796">Äịa chỉ giao hàng</translation>
<translation id="3402261774528610252">ÄÆ°á»ng kết nối dùng để tải trang web này đã sá»­ dụng TLS 1.0 hoặc TLS 1.1. Các phiên bản này không được dùng nữa và sẽ bị vô hiệu hóa trong tÆ°Æ¡ng lai. Sau khi phiên bản bị vô hiệu hóa, ngÆ°á»i dùng sẽ không thể tải trang web này. Máy chủ cần bật phiên bản TLS 1.2 trở lên.</translation>
<translation id="3405664148539009465">Tùy chỉnh phông chữ</translation>
+<translation id="3407789382767355356">đăng nhập qua bên thứ ba</translation>
<translation id="3409896703495473338">Quản lý chế độ cài đặt bảo mật</translation>
<translation id="3414952576877147120">Kích thước:</translation>
<translation id="3417660076059365994">Tệp bạn tải lên hoặc đính kèm được gá»­i đến Google Cloud hoặc bên thứ ba để phân tích. Ví dụ: các tệp đó có thể được quét tìm dữ liệu nhạy cảm hoặc phần má»m Ä‘á»™c hại.</translation>
@@ -932,6 +935,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="3477679029130949506">Danh sách phim và lịch chiếu</translation>
<translation id="3479552764303398839">Äể sau</translation>
<translation id="3484560055331845446">Bạn có thể mất quyá»n truy cập vào Tài khoản Google của mình. Chrome khuyên bạn nên đổi mật khẩu ngay bây giá». Bạn sẽ được yêu cầu đăng nhập.</translation>
+<translation id="3484861421501147767">Lá»i nhắc: Bạn có mã khuyến mãi đã lÆ°u</translation>
<translation id="3487845404393360112">Khay 4</translation>
<translation id="3495081129428749620">Tìm trong trang
<ph name="PAGE_TITLE" /></translation>
@@ -1056,6 +1060,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="3810973564298564668">Quản lý</translation>
<translation id="3816482573645936981">Giá trị (đã thay thế)</translation>
<translation id="382518646247711829">Nếu bạn sử dụng máy chủ proxy...</translation>
+<translation id="3826050100957962900">Äăng nhập qua bên thứ ba</translation>
<translation id="3827112369919217609">Tuyệt đối</translation>
<translation id="3827666161959873541">Phim gia đình</translation>
<translation id="3828924085048779000">Không cho phép cụm mật khẩu trống.</translation>
@@ -1068,9 +1073,9 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="3858027520442213535">Cập nhật ngày và giá»</translation>
<translation id="3858860766373142691">Tên</translation>
<translation id="3872834068356954457">Khoa há»c</translation>
+<translation id="3875783148670536197">Chỉ cho tôi cách làm</translation>
<translation id="3881478300875776315">Ẩn bớt dòng</translation>
<translation id="3884278016824448484">Số nhận dạng thiết bị xung đột</translation>
-<translation id="3885155851504623709">Giáo xứ</translation>
<translation id="388632593194507180">Hoạt động giám sát đã phát hiện</translation>
<translation id="3886948180919384617">Khay xếp chồng 3</translation>
<translation id="3890664840433101773">Thêm email</translation>
@@ -1100,9 +1105,11 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="3973234410852337861"><ph name="HOST_NAME" /> bị chặn</translation>
<translation id="3978338123949022456">Chế Ä‘á»™ tìm kiếm, nhập câu há»i rồi nhấn Enter để tìm vá»›i <ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Chim</translation>
+<translation id="3985750352229496475">Quản lý địa chỉ...</translation>
<translation id="3986705137476756801">Tắt tính năng Phụ đỠtrực tiếp lúc này</translation>
<translation id="3987940399970879459">DÆ°á»›i 1 MB</translation>
<translation id="3990250421422698716">Bù dịch chuyển</translation>
+<translation id="3992684624889376114">Giới thiệu vỠtrang này</translation>
<translation id="3996311196211510766">Trang web <ph name="ORIGIN" /> đã yêu cầu phải áp dụng một chính sách nguồn gốc
cho tất cả yêu cầu gửi tới trang web đó. Tuy nhiên, chính sách này hiện không áp dụng được.</translation>
<translation id="4006465311664329701">Phương thức thanh toán, ưu đãi và địa chỉ sử dụng Google Pay</translation>
@@ -1227,6 +1234,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="4305666528087210886">Không thể truy cập vào tệp của bạn</translation>
<translation id="4306529830550717874">Lưu địa chỉ?</translation>
<translation id="4306812610847412719">bảng nhớ tạm</translation>
+<translation id="4310070645992025887">Tìm kiếm trong Hành trình</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Chặn (mặc định)</translation>
<translation id="4314815835985389558">Quản lý dữ liệu đồng bộ hóa</translation>
@@ -1277,6 +1285,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="4435702339979719576">Bưu thiếp)</translation>
<translation id="443673843213245140">Äã tắt sá»­ dụng proxy nhÆ°ng cấu hình proxy rõ ràng được chỉ định.</translation>
<translation id="4441832193888514600">Bá» qua vì chính sách này chỉ có thể thiết lập thành chính sách ngÆ°á»i dùng trên đám mây.</translation>
+<translation id="4442470707340296952">Thẻ trên trình duyệt Chrome</translation>
<translation id="4450893287417543264">Không hiện lại</translation>
<translation id="4451135742916150903">Trang web có thể yêu cầu kết nối với thiết bị HID</translation>
<translation id="4452328064229197696">Mật khẩu bạn vừa sá»­ dụng đã bị lá»™ trong má»™t sá»± cố rò rỉ dữ liệu. Äể bảo mật tài khoản của bạn, Trình quản lý mật khẩu của Google khuyên bạn nên kiểm tra các mật khẩu đã lÆ°u.</translation>
@@ -1408,13 +1417,14 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="4809079943450490359">Hướng dẫn từ quản trị viên thiết bị:</translation>
<translation id="4812751092864334025">Sản phẩm công nghệ có thể bị đeo</translation>
<translation id="4813512666221746211">Lỗi mạng</translation>
-<translation id="4816492930507672669">Vừa với trang</translation>
+<translation id="4816492930507672669">Chỉnh cho vừa với trang</translation>
<translation id="4819347708020428563">Chỉnh sửa chú thích ở chế độ xem mặc định?</translation>
<translation id="4825496307559726072"><ph name="CREATE_GOOGLE_SHEET_FOCUSED_FRIENDLY_MATCH_TEXT" />, nhấn phím Tab rồi nhấn phím Enter để tạo nhanh một trang tính mới trong Google Trang tính</translation>
<translation id="4825507807291741242">Mạnh mẽ</translation>
<translation id="483241715238664915">Bật cảnh báo</translation>
<translation id="4834250788637067901">Phương thức thanh toán, ưu đãi và địa chỉ sử dụng Google Pay</translation>
<translation id="4838327282952368871">MÆ¡ má»™ng</translation>
+<translation id="4839087176073128681">Thanh toán nhanh hơn lần sau và bảo vệ thẻ của bạn bằng tính năng bảo mật hàng đầu trong ngành của Google.</translation>
<translation id="4840250757394056958">Xem nhật ký duyệt web trên Chrome</translation>
<translation id="484462545196658690">Tá»± Ä‘á»™ng</translation>
<translation id="484671803914931257">Nhận chiết khấu tại <ph name="MERCHANT_NAME" /> và nhiá»u nÆ¡i khác</translation>
@@ -1477,6 +1487,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="5011561501798487822">Ngôn ngữ được phát hiện</translation>
<translation id="5015510746216210676">Tên máy:</translation>
<translation id="5017554619425969104">Văn bản bạn đã sao chép</translation>
+<translation id="5017828934289857214">Nhắc tôi sau</translation>
<translation id="5018422839182700155">Không thể mở trang này</translation>
<translation id="5019198164206649151">Không thể lưu trữ do chương trình phụ trợ ở trạng thái xấu</translation>
<translation id="5020776957610079374">Âm nhạc thế giới</translation>
@@ -1496,6 +1507,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="5051305769747448211">Hài kịch trực tiếp</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Äể gá»­i tệp này bằng tính năng Chia sẻ lân cận, hãy giải phóng dung lượng (<ph name="DISK_SPACE_SIZE" />) trên thiết bị của bạn}other{Äể gá»­i các tệp này bằng tính năng Chia sẻ lân cận, hãy giải phóng dung lượng (<ph name="DISK_SPACE_SIZE" />) trên thiết bị của bạn}}</translation>
<translation id="5056549851600133418">Tin bài dành cho bạn</translation>
+<translation id="5060483733937416656">Bạn đã chá»n xác minh vá»›i Windows Hello trên các trang web sá»­ dụng <ph name="PROVIDER_ORIGIN" />. Nhà cung cấp này có thể đã lÆ°u trữ thông tin vá» phÆ°Æ¡ng thức thanh toán của bạn mà bạn có thể <ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">à của bạn là <ph name="LOOKALIKE_DOMAIN" /> phải không?</translation>
<translation id="5066056036849835175">Danh sách tài liệu đã in</translation>
<translation id="5068234115460527047">Quỹ phòng hộ</translation>
@@ -1509,10 +1521,8 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="5087286274860437796">Chứng chỉ của máy chủ không hợp lệ tại thá»i Ä‘iểm này.</translation>
<translation id="5087580092889165836">Thêm thẻ</translation>
<translation id="5088142053160410913">Thông báo gửi đến trình vận hành</translation>
-<translation id="5089810972385038852">Tỉnh</translation>
<translation id="5093232627742069661">Gấp kiểu chữ Z</translation>
<translation id="5094747076828555589">Máy chủ này không chứng minh được rằng đó là <ph name="DOMAIN" />; chứng chỉ bảo mật của máy chủ này không được Chromium tin cậy. Äiá»u này có thể do định cấu hình sai hoặc có kẻ tấn công chặn kết nối của bạn.</translation>
-<translation id="5095208057601539847">Tỉnh</translation>
<translation id="5097099694988056070">Số liệu thống kê vỠthiết bị, chẳng hạn như mức sử dụng CPU/RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Trang web không an</translation>
@@ -1550,6 +1560,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="5171045022955879922">Tìm kiếm hoặc nhập URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Máy</translation>
+<translation id="5177076414499237632">Tìm hiểu vỠnguồn và chủ đỠcủa trang này</translation>
<translation id="5179510805599951267">Không ở <ph name="ORIGINAL_LANGUAGE" />? Báo cáo lỗi này</translation>
<translation id="518639307526414276">Thức ăn cho thú cưng và đồ dùng chăm sóc thú cưng</translation>
<translation id="5190835502935405962">Thanh Dấu trang</translation>
@@ -1710,6 +1721,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="5624120631404540903">Quản lý mật khẩu</translation>
<translation id="5629630648637658800">Không thể tải cài đặt chính sách</translation>
<translation id="5631439013527180824">Mã thông báo quản lý thiết bị không hợp lệ</translation>
+<translation id="5632485077360054581">Chỉ cho tôi cách làm</translation>
<translation id="5633066919399395251">Những kẻ tấn công hiện ở trên <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> có thể cố gắng cài đặt các chương trình nguy hiểm vào máy tính của bạn. Các chương trình này sẽ đánh cắp hoặc xóa thông tin của bạn (ví dụ: ảnh, mật khẩu, thư và thẻ tín dụng). <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Äã chặn ná»™i dung lừa đảo.</translation>
<translation id="5633259641094592098">Phim indie và phim kinh điển</translation>
@@ -1827,6 +1839,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="5989320800837274978">Cả máy chủ proxy cố định và URL tập lệnh .pac Ä‘á»u chÆ°a được chỉ định.</translation>
<translation id="5992691462791905444">Gấp kiểu chữ Z kỹ thuật</translation>
<translation id="5995727681868049093">Quản lý thông tin, quyá»n riêng tÆ° và chế Ä‘á»™ bảo mật cho Tài khoản Google của bạn</translation>
+<translation id="5997247540087773573">Mật khẩu bạn vừa sá»­ dụng đã bị lá»™ trong má»™t sá»± cố rò rỉ dữ liệu. Äể bảo mật tài khoản, Trình quản lý mật khẩu khuyên bạn nên thay đổi mật khẩu này ngay và kiểm tra các mật khẩu bạn đã lÆ°u.</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> kết quả cho '<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">Cổ điển</translation>
<translation id="6008122969617370890">Thứ tự từ N đến 1</translation>
@@ -1922,7 +1935,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="627746635834430766">Äể thanh toán nhanh hÆ¡n vào lần tiếp theo, hãy lÆ°u địa chỉ thanh toán và thẻ vào Tài khoản Google của bạn.</translation>
<translation id="6279183038361895380">Nhấn |<ph name="ACCELERATOR" />| để hiển thị con trỠcủa bạn</translation>
<translation id="6280223929691119688">Không thể phân phối đến địa chỉ này. Chá»n má»™t địa chỉ khác.</translation>
-<translation id="6282194474023008486">Mã bưu chính</translation>
<translation id="6285507000506177184">Nút quản lý tệp đã tải xuống trong Chrome, nhấn phím Enter để quản lý các tệp bạn đã tải xuống trong Chrome</translation>
<translation id="6289939620939689042">Màu trang</translation>
<translation id="6290238015253830360">Bài viết đỠxuất cho bạn sẽ xuất hiện ở đây</translation>
@@ -1944,6 +1956,7 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="6337133576188860026">Bộ nhớ đệm còn chưa đầy <ph name="SIZE" />. Một số trang web có thể tải chậm hơn vào lần tới bạn truy cập.</translation>
<translation id="6337534724793800597">Lá»c chính sách theo tên</translation>
<translation id="6340739886198108203">Chính sách của quản trị viên không khuyến khích chụp ảnh màn hình hoặc ghi âm khi đang có nội dung bảo mật trên màn hình:</translation>
+<translation id="6348220984832452017">Biến thể đang hoạt động</translation>
<translation id="6349101878882523185">Cài đặt <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Không có}=1{1 mật khẩu (cho <ph name="DOMAIN_LIST" />, đã đồng bộ hóa)}=2{2 mật khẩu (cho <ph name="DOMAIN_LIST" />, đã đồng bộ hóa)}other{# mật khẩu (cho <ph name="DOMAIN_LIST" />, đã đồng bộ hóa)}}</translation>
<translation id="6355392890578844978">Không có công ty hay tổ chức nào quản lý trình duyệt này. Hoạt động trên thiết bị này có thể được quản lý ở bên ngoài Chromium. <ph name="BEGIN_LINK" />Tìm hiểu thêm<ph name="END_LINK" /></translation>
@@ -1975,7 +1988,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="643051589346665201">Thay đổi mật khẩu Google</translation>
<translation id="6433490469411711332">Chỉnh sửa thông tin liên hệ</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> đã từ chối kết nối.</translation>
-<translation id="6438025220577812695">Tự thay đổi</translation>
<translation id="6440503408713884761">Bị bỠqua</translation>
<translation id="6443406338865242315">Các tiện ích và plugin bạn đã cài đặt</translation>
<translation id="6446163441502663861">Kahu (Phong bì)</translation>
@@ -2105,9 +2117,9 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="6828866289116430505">Di truyá»n há»c</translation>
<translation id="6831043979455480757">Dịch</translation>
<translation id="6833752742582340615">Lưu thông tin thẻ và thông tin thanh toán vào Tài khoản Google để thanh toán an toàn và nhanh hơn</translation>
-<translation id="6839929833149231406">Vùng</translation>
<translation id="6846340164947227603">Dùng số thẻ ảo...</translation>
<translation id="6852204201400771460">Tải lại ứng dụng?</translation>
+<translation id="6857776781123259569">Quản lý mật khẩu...</translation>
<translation id="686485648936420384">Tài nguyên dành cho ngÆ°á»i tiêu dùng</translation>
<translation id="6865412394715372076">Hiện không thể xác minh thẻ này</translation>
<translation id="6869334554832814367">Khoản vay cá nhân</translation>
@@ -2156,7 +2168,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="6965978654500191972">Thiết bị</translation>
<translation id="696703987787944103">Cảm tính</translation>
<translation id="6968269510885595029">Sử dụng Khóa bảo mật của bạn</translation>
-<translation id="6970216967273061347">Quận</translation>
<translation id="6971439137020188025">Tạo nhanh một bản trình bày mới trong Google Trang trình bày</translation>
<translation id="6972629891077993081">Thiết bị HID</translation>
<translation id="6973656660372572881">Cả hai máy chủ proxy cố định và URL tập lệnh .pac Ä‘á»u được chỉ định.</translation>
@@ -2195,7 +2206,6 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<translation id="7081308185095828845">Thiết bị của bạn không dùng được tính năng này</translation>
<translation id="7083258188081898530">Khay 9</translation>
<translation id="7086090958708083563">NgÆ°á»i dùng yêu cầu tải lên</translation>
-<translation id="7087282848513945231">Hạt</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, hãy nhấn Tab rồi nhấn Enter để quản lý quyá»n và dữ liệu lÆ°u trữ trên các trang web trong phần Cài đặt của Chrome</translation>
<translation id="7096937462164235847">Danh tính của trang web này chưa được xác minh.</translation>
<translation id="7101893872976785596">Phim kinh dị</translation>
@@ -2214,10 +2224,10 @@ Nếu bạn từ chối, chế Ä‘á»™ cài đặt quyá»n riêng tÆ° của bạn
<ph name="LIST_ITEM" />Thông tin đã nhập vào biểu mẫu<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Không thể giao hàng đến địa chỉ này. Chá»n má»™t địa chỉ khác.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Quản trị viên của bạn đã cấm sao chép dữ liệu này.</translation>
<translation id="7135130955892390533">Hiển thị trạng thái</translation>
<translation id="7138472120740807366">Phương thức phân phối</translation>
-<translation id="7139724024395191329">Tiểu vương quốc Ả rập</translation>
<translation id="7139892792842608322">Khay chính</translation>
<translation id="714064300541049402">Trục X của hình ảnh mặt 2</translation>
<translation id="7152423860607593928">Number-14 (Phong bì)</translation>
@@ -2434,6 +2444,7 @@ Thông tin chi tiết bổ sung:
<translation id="7669271284792375604">Những kẻ tấn công trên trang web này có thể đánh lừa bạn cài đặt các chương trình ảnh hưởng đến trải nghiệm duyệt web của bạn (ví dụ: bằng cách thay đổi trang chủ của bạn hoặc hiển thị thêm quảng cáo trên các trang web bạn truy cập).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Các thao tác được thực hiện trên dữ liệu được gắn cỠlà bí mật (1 thao tác kể từ khi đăng nhập). <ph name="BEGIN_LINK" />Tìm hiểu thêm<ph name="END_LINK" />}other{Các thao tác đã thực hiện trên dữ liệu được gắn cỠlà bí mật (# thao tác kể từ khi đăng nhập). <ph name="BEGIN_LINK" />Tìm hiểu thêm<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Há»™p thÆ° 6</translation>
+<translation id="7675325315208090829">Quản lý phương thức thanh toán...</translation>
<translation id="7676643023259824263">Tìm kiếm văn bản trong bảng nhớ tạm, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Xem và quản lý nhật ký duyệt web của bạn trong phần cài đặt Chrome</translation>
<translation id="7679947978757153706">Bóng chày</translation>
@@ -2476,7 +2487,6 @@ Thông tin chi tiết bổ sung:
<translation id="7766518757692125295">Viá»n</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">Cùng thứ tự hướng lên</translation>
-<translation id="777702478322588152">Quận</translation>
<translation id="7791011319128895129">Chưa phát hành</translation>
<translation id="7791196057686275387">Äóng kiện</translation>
<translation id="7791543448312431591">Thêm</translation>
@@ -2567,12 +2577,12 @@ Thông tin chi tiết bổ sung:
<translation id="8055534648776115597">Dạy nghá» và giáo dục thÆ°á»ng xuyên</translation>
<translation id="8057711352706143257">Cấu hình của "<ph name="SOFTWARE_NAME" />" không chính xác. Việc gỡ cài đặt "<ph name="SOFTWARE_NAME" />" thÆ°á»ng sẽ khắc phục được sá»± cố này. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Sản xuất thực phẩm</translation>
-<translation id="8066955247577885446">Rất tiếc, đã xảy ra lỗi.</translation>
<translation id="8067872629359326442">Bạn vừa nhập mật khẩu vào má»™t trang web lừa đảo. Chromium có thể trợ giúp bạn. Äể đổi mật khẩu và thông báo cho Google biết rằng tài khoản của bạn có thể gặp nguy hiểm, hãy nhấp vào Bảo vệ tài khoản.</translation>
<translation id="8070439594494267500">Biểu tượng ứng dụng</translation>
<translation id="8074253406171541171">10x13 (Phong bì)</translation>
<translation id="8075736640322370409">Tạo nhanh một trang tính mới trên Google Trang tính</translation>
<translation id="8075898834294118863">Quản lý chế độ cài đặt trang web</translation>
+<translation id="8076492880354921740">Thẻ</translation>
<translation id="8078141288243656252">Không thể chú thích khi xoay</translation>
<translation id="8079031581361219619">Tải lại trang web?</translation>
<translation id="8081087320434522107">Xe sedan</translation>
@@ -2695,6 +2705,7 @@ Thông tin chi tiết bổ sung:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Cài đặt</translation>
+<translation id="8428634594422941299">OK</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, hãy nhấn Tab rồi nhấn Enter để quản lý các lá»±a chá»n Æ°u tiên vá» cookie trong phần Cài đặt của Chrome</translation>
<translation id="8433057134996913067">Thao tác này sẽ đăng xuất bạn khá»i hầu hết các trang web.</translation>
<translation id="8434840396568290395">Thú cưng</translation>
@@ -2793,6 +2804,7 @@ Thông tin chi tiết bổ sung:
<translation id="8742371904523228557"><ph name="ONE_TIME_CODE" /> là mã của bạn cho <ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Äánh dấu thẻ này</translation>
<translation id="8751426954251315517">Vui lòng thử lại vào lần tiếp theo</translation>
+<translation id="8757526089434340176">Ưu đãi hiện có của Google Pay</translation>
<translation id="8758885506338294482">Thể thao điện tử</translation>
<translation id="8759274551635299824">Thẻ này đã hết hạn</translation>
<translation id="87601671197631245">Trang web này dùng cấu hình bảo mật lá»—i thá»i nên có thể làm lá»™ thông tin của bạn (chẳng hạn nhÆ° mật khẩu, thÆ° hoặc thẻ tín dụng) trong quá trình gá»­i các thông tin này đến trang web này.</translation>
@@ -2800,6 +2812,7 @@ Thông tin chi tiết bổ sung:
<translation id="8763927697961133303">Thiết bị USB</translation>
<translation id="8763986294015493060">Äóng tất cả cá»­a sổ ẩn danh Ä‘ang mở</translation>
<translation id="8766943070169463815">Äã mở bảng xác thá»±c thông tin thanh toán an toàn</translation>
+<translation id="8767765348545497220">Äóng bong bóng trợ giúp</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Xe mô tô</translation>
<translation id="8790007591277257123">&amp;Làm lại xóa</translation>
@@ -2812,6 +2825,7 @@ Thông tin chi tiết bổ sung:
<translation id="8806285662264631610">Sản phẩm tắm gội và chăm sóc cơ thể</translation>
<translation id="8807160976559152894">Cắt bỠsau mỗi trang</translation>
<translation id="8808828119384186784">Cài đặt Chrome</translation>
+<translation id="8813277370772331957">Nhắc tôi sau</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, nhấn phím Tab rồi nhấn Enter để cập nhật Chrome trong phần cài đặt của Chrome</translation>
<translation id="8820817407110198400">Dấu trang</translation>
<translation id="882338992931677877">Khe thủ công</translation>
@@ -2991,6 +3005,7 @@ Thông tin chi tiết bổ sung:
<translation id="988159990683914416">Phiên bản dành cho Nhà phát triển</translation>
<translation id="989988560359834682">Chỉnh sửa địa chỉ</translation>
<translation id="991413375315957741">cảm biến chuyển động hoặc ánh sáng</translation>
+<translation id="992110854164447044">Thẻ ảo giúp ngụy trang thẻ thá»±c của bạn để bảo vệ bạn khá»i nguy cÆ¡ bị lừa đảo. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Màu hồng</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" đã được cài đặt không đúng trên máy tính hoặc mạng của bạn:
diff --git a/chromium/components/strings/components_strings_zh-CN.xtb b/chromium/components/strings/components_strings_zh-CN.xtb
index 59c5615c3ca..c916af4dd75 100644
--- a/chromium/components/strings/components_strings_zh-CN.xtb
+++ b/chromium/components/strings/components_strings_zh-CN.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">助学金ã€å¥–学金与ç»æµŽèµ„助</translation>
<translation id="1048785276086539861">当您修改注释时,此文档会æ¢å¤ä¸ºå•é¡µè§†å›¾</translation>
<translation id="1050038467049342496">关闭其他应用</translation>
+<translation id="1053959602163383901">您已选择在使用 <ph name="PROVIDER_ORIGIN" /> 的网站上通过一ç§èº«ä»½éªŒè¯å™¨æœåŠ¡æ¥éªŒè¯èº«ä»½ã€‚æ­¤æ供商å¯èƒ½å·²å­˜å‚¨æ‚¨çš„付款方å¼ä¿¡æ¯ï¼Œä¸è¿‡æ‚¨å¯ä»¥<ph name="LINK_TEXT" />相应信æ¯ã€‚</translation>
<translation id="1055184225775184556">撤消添加(&amp;U)</translation>
<translation id="1056663316309890343">照片软件</translation>
<translation id="1056898198331236512">警告</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">å–货方å¼</translation>
<translation id="1281476433249504884">å †å å‡ºçº¸å™¨ 1</translation>
<translation id="1285320974508926690">一律ä¸ç¿»è¯‘此网站</translation>
+<translation id="1288548991597756084">妥善ä¿å­˜é“¶è¡Œå¡ä¿¡æ¯</translation>
<translation id="1292571435393770077">纸匣 16</translation>
<translation id="1292701964462482250">“您计算机上的软件导致 Chrome 无法安全地连接到网络â€ï¼ˆä»…é™ Windows 计算机)</translation>
<translation id="1294154142200295408">命令行å˜ä½“</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;è¦ä¿®æ­£è¯¥é”™è¯¯ï¼Œè¯·åœ¨æ‚¨å°è¯•æ‰“开的网页上点击&lt;strong&gt;连接&lt;/strong&gt;。&lt;/p&gt;</translation>
<translation id="1507780850870535225">景观设计</translation>
<translation id="1513706915089223971">历å²è®°å½•æ¡ç›®åˆ—表</translation>
+<translation id="1516097932025103760">系统会对该银行å¡è¿›è¡ŒåŠ å¯†å¹¶å¦¥å–„ä¿å­˜å®ƒï¼Œä½†ç»ä¸ä¼šå­˜å‚¨é“¶è¡Œå¡éªŒè¯ç  (CVC)。</translation>
<translation id="1517433312004943670">å¿…é¡»æ供电è¯å·ç </translation>
<translation id="1519264250979466059">构建日期</translation>
<translation id="1521159554480556801">纤维与纺织å“艺术</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">ä¿å­˜å¹¶å¡«å†™ä»˜æ¬¾æ–¹å¼</translation>
<translation id="1663943134801823270">信用å¡é€‰é¡¹å’Œåœ°å€é€‰é¡¹å‡æ¥è‡ª Chrome。您å¯åœ¨<ph name="BEGIN_LINK" />设置<ph name="END_LINK" />中管ç†è¿™äº›é€‰é¡¹ã€‚</translation>
<translation id="1671391448414634642">从现在开始,<ph name="SOURCE_LANGUAGE" />网页一律会被翻译æˆ<ph name="TARGET_LANGUAGE" />。</translation>
+<translation id="1673886523110456987">通过“<ph name="CARD_DETAIL" />â€ç»“å¸ä»¥ä½¿ç”¨ä¼˜æƒ </translation>
<translation id="1674504678466460478">从<ph name="SOURCE_LANGUAGE" />到<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">短边先入</translation>
<translation id="168693727862418163">此政策值未能根æ®å…¶æž¶æž„进行验è¯ï¼Œå› æ­¤ä¼šè¢«å¿½ç•¥ã€‚</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">移动传感器</translation>
<translation id="1717494416764505390">ä¿¡ç®± 3</translation>
<translation id="1718029547804390981">无法为该文档添加注释,因为它太大</translation>
+<translation id="1720941539803966190">关闭教程</translation>
<translation id="1721424275792716183">标有“*â€çš„字段å‡æ˜¯å¿…填字段</translation>
<translation id="1727613060316725209">è¯ä¹¦æœ‰æ•ˆ</translation>
<translation id="1727741090716970331">添加有效å¡å·</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">烧烤</translation>
<translation id="2053111141626950936">系统ä¸ä¼šç¿»è¯‘<ph name="LANGUAGE" />网页。</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{如果此控件已开å¯ä¸”处于有效状æ€ï¼ŒChrome 会确定您近期的æµè§ˆæ´»åŠ¨ä¸Žå“ªä¸ªå¤§åž‹ç”¨æˆ·ç¾¤ç»„或“åŒç±»ç¾¤ç»„â€æœ€æŽ¥è¿‘。广告主å¯ä¸ºç¾¤ç»„选择广告,您的æµè§ˆæ´»åŠ¨è®°å½•ä¼šç§å¯†åœ°ä¿ç•™åœ¨æ‚¨çš„设备上。系统会按æ¯å¤© 1 次的频率更新您所属的群组。}=1{如果此控件已开å¯ä¸”处于有效状æ€ï¼ŒChrome 会确定您近期的æµè§ˆæ´»åŠ¨ä¸Žå“ªä¸ªå¤§åž‹ç”¨æˆ·ç¾¤ç»„或“åŒç±»ç¾¤ç»„â€æœ€æŽ¥è¿‘。广告主å¯ä¸ºç¾¤ç»„选择广告,您的æµè§ˆæ´»åŠ¨è®°å½•ä¼šç§å¯†åœ°ä¿ç•™åœ¨æ‚¨çš„设备上。系统会按æ¯å¤© 1 次的频率更新您所属的群组。}other{如果此控件已开å¯ä¸”处于有效状æ€ï¼ŒChrome 会确定您近期的æµè§ˆæ´»åŠ¨ä¸Žå“ªä¸ªå¤§åž‹ç”¨æˆ·ç¾¤ç»„或“åŒç±»ç¾¤ç»„â€æœ€æŽ¥è¿‘。广告主å¯ä¸ºç¾¤ç»„选择广告,您的æµè§ˆæ´»åŠ¨è®°å½•ä¼šç§å¯†åœ°ä¿ç•™åœ¨æ‚¨çš„è®¾å¤‡ä¸Šã€‚ç³»ç»Ÿä¼šæŒ‰æ¯ {NUM_DAYS} 天 1 次的频率更新您所属的群组。}}</translation>
-<translation id="2053553514270667976">邮政编ç </translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 æ¡å»ºè®®}other{# æ¡å»ºè®®}}</translation>
+<translation id="2066915425250589881">请求删除</translation>
<translation id="2068528718802935086">婴幼儿</translation>
<translation id="2071156619270205202">这张å¡çš„å¡å·æ— æ³•ç”¨ä½œè™šæ‹Ÿå¡å·ã€‚</translation>
<translation id="2071692954027939183">由于您通常ä¸å…许显示通知,系统已自动å±è”½é€šçŸ¥</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">“管ç†åŒæ­¥æ•°æ®â€æŒ‰é’®ï¼ŒæŒ‰ Enter é”®å³å¯åœ¨ Chrome 设置中管ç†æ‚¨è¦åŒæ­¥çš„ä¿¡æ¯</translation>
<translation id="2091887806945687916">声音</translation>
<translation id="2094505752054353250">网域ä¸åŒ¹é…</translation>
-<translation id="2096368010154057602">çœ</translation>
<translation id="2099652385553570808">三钉(左侧)</translation>
<translation id="2101225219012730419">版本:</translation>
<translation id="2102134110707549001">建议安全系数高的密ç â€¦</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">美å¼æ©„榄çƒ</translation>
<translation id="2187317261103489799">检测(默认)</translation>
<translation id="2188375229972301266">多孔(底部)</translation>
-<translation id="2188852899391513400">您刚æ‰ä½¿ç”¨çš„密ç é­é‡äº†æ•°æ®æ³„露。为确ä¿æ‚¨çš„å¸å·å®‰å…¨ï¼ŒGoogle 密ç ç®¡ç†å™¨å»ºè®®æ‚¨ç«‹å³æ›´æ”¹æ­¤å¯†ç å¹¶æ£€æŸ¥å·²ä¿å­˜çš„所有密ç ã€‚</translation>
<translation id="219906046732893612">家居改善</translation>
<translation id="2202020181578195191">请输入有效的失效年份</translation>
<translation id="22081806969704220">纸匣 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">文件修改</translation>
<translation id="2215963164070968490">ç‹—</translation>
<translation id="2218879909401188352"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 上的现有攻击者å¯èƒ½ä¼šå®‰è£…å±é™©åº”用æ¥æŸå®³æ‚¨çš„设备ã€ç»™æ‚¨çš„手机å¸å•å¢žæ·»éšå«è´¹ç”¨æˆ–窃å–您的个人信æ¯ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">é‡å¯æ•™ç¨‹</translation>
+<translation id="2219735899272417925">需è¦é‡ç½®è®¾å¤‡</translation>
<translation id="2224337661447660594">未连接到互è”网</translation>
<translation id="2230458221926704099">请使用<ph name="BEGIN_LINK" />诊断应用<ph name="END_LINK" />ä¿®å¤ç½‘络连接</translation>
<translation id="2239100178324503013">ç«‹å³å‘é€</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">ä¸å¾—使用(默认)</translation>
<translation id="2512413427717747692">“将 Chrome 设为默认æµè§ˆå™¨â€æŒ‰é’®ï¼ŒæŒ‰ä¸‹ Enter é”®å³å¯åœ¨ iOS 设置中将 Chrome 设置为系统的默认æµè§ˆå™¨</translation>
<translation id="2515629240566999685">检查您所在区域的网络信å·</translation>
+<translation id="2515761554693942801">您已选择在使用 <ph name="PROVIDER_ORIGIN" /> 的网站上通过触控 ID æ¥éªŒè¯èº«ä»½ã€‚æ­¤æ供商å¯èƒ½å·²å­˜å‚¨æ‚¨çš„付款方å¼ä¿¡æ¯ï¼Œä¸è¿‡æ‚¨å¯ä»¥<ph name="LINK_TEXT" />相应信æ¯ã€‚</translation>
<translation id="2521385132275182522">钉装(å³ä¸‹è§’)</translation>
<translation id="2521736961081452453">创建表å•</translation>
<translation id="2523886232349826891">仅会ä¿å­˜åœ¨æ­¤è®¾å¤‡ä¸Š</translation>
<translation id="2524461107774643265">添加更多信æ¯</translation>
<translation id="2529899080962247600">此字段最多åªèƒ½åŒ…å« <ph name="MAX_ITEMS_LIMIT" /> 个æ¡ç›®ï¼Œåœ¨è¾¾åˆ°è¿™ä¸€ä¸Šé™åŽæ·»åŠ çš„所有æ¡ç›®éƒ½å°†è¢«å¿½ç•¥ã€‚</translation>
+<translation id="253493526287553278">查看促销代ç è¯¦ç»†ä¿¡æ¯</translation>
<translation id="2535585790302968248">打开新的无痕å¼æ ‡ç­¾é¡µå³å¯è¿›è¡Œæ— ç—•æµè§ˆ</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{以åŠå¦å¤– 1 个网域}other{以åŠå¦å¤– # 个网域}}</translation>
<translation id="2536110899380797252">添加地å€</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">摄影与数ç è‰ºæœ¯</translation>
<translation id="2601150049980261779">爱情片</translation>
<translation id="2604589665489080024">æµè¡ŒéŸ³ä¹</translation>
-<translation id="2609632851001447353">其他å˜ä½“</translation>
<translation id="2610561535971892504">点击å¤åˆ¶</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />ä¸ä¼šä¿å­˜<ph name="END_EMPHASIS" />以下信æ¯ï¼š
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">生日与命åæ—¥</translation>
<translation id="2677748264148917807">离开</translation>
+<translation id="2679714844901977852">将您的付款å¡ä¿¡æ¯å’Œç»“ç®—ä¿¡æ¯ä¿å­˜åˆ°æ‚¨çš„ Google å¸å· <ph name="USER_EMAIL" />,以便您安全快æ·åœ°ç»“è´¦</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">最适åˆ</translation>
<translation id="2688969097326701645">是的,继续</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">互è”网æœåŠ¡æ供商 (ISP)</translation>
<translation id="2856444702002559011">攻击者å¯èƒ½ä¼šè¯•å›¾ä»Ž <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 窃å–您的信æ¯ï¼ˆä¾‹å¦‚:密ç ã€é€šè®¯å†…容或信用å¡ä¿¡æ¯ï¼‰ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">此网站展示过侵扰性或误导性广告。</translation>
-<translation id="286512204874376891">虚拟å¡ä¼šä¼ªè£…æˆå®žä½“å¡ï¼Œä»Žè€Œä¿æŠ¤æ‚¨å…é­æ¬ºè¯ˆã€‚<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">å‹å¥½</translation>
<translation id="28761159517501904">影片</translation>
<translation id="2876489322757410363">å°†è¦é€€å‡ºæ— ç—•æ¨¡å¼ï¼Œä»¥ä¾¿é€šè¿‡å¤–部应用付款。是å¦ç»§ç»­ï¼Ÿ</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">光碟</translation>
<translation id="3162559335345991374">您è¦ä½¿ç”¨çš„ Wi-Fi 网络å¯èƒ½éœ€è¦æ‚¨è®¿é—®å…¶ç™»å½•é¡µé¢ã€‚</translation>
<translation id="3169472444629675720">探索</translation>
-<translation id="3174168572213147020">å²›</translation>
<translation id="3176929007561373547">请检查您的代ç†æœåŠ¡å™¨è®¾ç½®æˆ–与网络管ç†å‘˜è”系,以确ä¿ä»£ç†æœåŠ¡å™¨æ­£å¸¸è¿è¡Œã€‚如果您认为自己ä¸éœ€è¦ä½¿ç”¨ä»£ç†æœåŠ¡å™¨ï¼Œè¯·æ‰§è¡Œä»¥ä¸‹æ“作:
<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">了解您何时在主动使用此设备</translation>
@@ -875,9 +880,6 @@
<translation id="3369192424181595722">时钟错误</translation>
<translation id="3369459162151165748">车辆零部件与é…件</translation>
<translation id="3371064404604898522">å°† Chrome 设为默认æµè§ˆå™¨</translation>
-<translation id="3371076217486966826"><ph name="URL" /> 想:
- • 为您的周边环境创建 3D 地图并跟踪摄åƒå¤´ä½ç½®
- • 使用您的摄åƒå¤´</translation>
<translation id="337363190475750230">å·²å–消é…ç½®</translation>
<translation id="3375754925484257129">è¿è¡Œ Chrome 安全检查</translation>
<translation id="3377144306166885718">æœåŠ¡å™¨ä½¿ç”¨çš„ TLS 版本已过时。</translation>
@@ -894,6 +896,7 @@
<translation id="3399952811970034796">递é€åœ°å€</translation>
<translation id="3402261774528610252">用于加载此网站的连接使用的是 TLS 1.0 或 TLS 1.1,这两个 TLS 版本都已过时,将在ä¸ä¹…åŽå®Œå…¨åœç”¨ã€‚届时,用户将无法å†åŠ è½½æ­¤ç½‘站。æœåŠ¡å™¨åº”å¯ç”¨ TLS 1.2 或更高版本。</translation>
<translation id="3405664148539009465">自定义字体</translation>
+<translation id="3407789382767355356">第三方登录</translation>
<translation id="3409896703495473338">管ç†å®‰å…¨è®¾ç½®</translation>
<translation id="3414952576877147120">大å°ï¼š</translation>
<translation id="3417660076059365994">系统会将您粘贴或附加的文件å‘é€ç»™ Google Cloud 或第三方以供分æžã€‚例如,这些文件å¯èƒ½ä¼šè¢«æ‰«æ,以确定其中是å¦åŒ…å«æ•æ„Ÿæ•°æ®æˆ–æ¶æ„软件。</translation>
@@ -926,6 +929,7 @@
<translation id="3477679029130949506">上映的影片与放映时间</translation>
<translation id="3479552764303398839">以åŽå†è¯´</translation>
<translation id="3484560055331845446">您å¯èƒ½ä¼šæ— æ³•å†è®¿é—®è‡ªå·±çš„ Google å¸å·ã€‚Chrome 建议您立å³æ›´æ”¹å¯†ç ã€‚系统会è¦æ±‚您登录。</translation>
+<translation id="3484861421501147767">æ醒:å¯ä»¥ä½¿ç”¨å·²ä¿å­˜çš„促销代ç </translation>
<translation id="3487845404393360112">纸匣 4</translation>
<translation id="3495081129428749620">在网页“<ph name="PAGE_TITLE" />â€ä¸­æŸ¥æ‰¾</translation>
<translation id="350069200438440499">文件å:</translation>
@@ -1048,6 +1052,7 @@
<translation id="3810973564298564668">管ç†</translation>
<translation id="3816482573645936981">值(已被å–代)</translation>
<translation id="382518646247711829">如果您使用代ç†æœåŠ¡å™¨â€¦</translation>
+<translation id="3826050100957962900">第三方登录</translation>
<translation id="3827112369919217609">ç»å¯¹</translation>
<translation id="3827666161959873541">é¢å‘家庭的影片</translation>
<translation id="3828924085048779000">密ç è¾“入字段ä¸èƒ½ç•™ç©ºã€‚</translation>
@@ -1060,9 +1065,9 @@
<translation id="3858027520442213535">更新日期和时间</translation>
<translation id="3858860766373142691">å称</translation>
<translation id="3872834068356954457">科学</translation>
+<translation id="3875783148670536197">æ“作步骤</translation>
<translation id="3881478300875776315">éšè—部分行</translation>
<translation id="3884278016824448484">设备标识符存在冲çª</translation>
-<translation id="3885155851504623709">教区</translation>
<translation id="388632593194507180">检测到正被监控</translation>
<translation id="3886948180919384617">å †å å‡ºçº¸å™¨ 3</translation>
<translation id="3890664840433101773">添加电å­é‚®ä»¶åœ°å€</translation>
@@ -1092,9 +1097,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> 已被å±è”½</translation>
<translation id="3978338123949022456">æœç´¢æ¨¡å¼ï¼Œè¾“入查询内容并按 Enter é”®å³å¯ç”¨ <ph name="KEYWORD_SUFFIX" /> æœç´¢</translation>
<translation id="398470910934384994">养鸟/观鸟</translation>
+<translation id="3985750352229496475">管ç†åœ°å€â€¦</translation>
<translation id="3986705137476756801">暂时关闭“实时字幕â€åŠŸèƒ½</translation>
<translation id="3987940399970879459">å°äºŽ 1 MB</translation>
<translation id="3990250421422698716">撞页å移</translation>
+<translation id="3992684624889376114">关于此页é¢</translation>
<translation id="3996311196211510766">网站 <ph name="ORIGIN" /> å·²è¦æ±‚收到的所有请求都应用一项æ¥æºæ”¿ç­–,但此政策目å‰æ— æ³•åº”用。</translation>
<translation id="4006465311664329701">Google Pay 中存储的付款方å¼ã€ä¼˜æƒ å’Œåœ°å€ä¿¡æ¯</translation>
<translation id="4009243425692662128">您打å°çš„页é¢å†…容会被å‘é€åˆ° Google Cloud 或第三方进行分æžã€‚例如,这类内容å¯èƒ½ä¼šè¢«æ‰«æ,以确定其中是å¦åŒ…å«æ•æ„Ÿæ•°æ®ã€‚</translation>
@@ -1214,6 +1221,7 @@
<translation id="4305666528087210886">无法访问您的文件</translation>
<translation id="4306529830550717874">ä¿å­˜åœ°å€ï¼Ÿ</translation>
<translation id="4306812610847412719">剪贴æ¿</translation>
+<translation id="4310070645992025887">在您的历程中æœç´¢</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">å±è”½ï¼ˆé»˜è®¤ï¼‰</translation>
<translation id="4314815835985389558">管ç†åŒæ­¥æ•°æ®</translation>
@@ -1264,6 +1272,7 @@
<translation id="4435702339979719576">Postcard)</translation>
<translation id="443673843213245140">å·²åœç”¨ä»£ç†ï¼Œä½†æ˜¯æŒ‡å®šäº†æ˜Žç¡®çš„代ç†é…置。</translation>
<translation id="4441832193888514600">被忽略了,因为此政策åªèƒ½è®¾ä¸ºäº‘端用户政策。</translation>
+<translation id="4442470707340296952">Chrome 标签页</translation>
<translation id="4450893287417543264">ä¸å†æ˜¾ç¤º</translation>
<translation id="4451135742916150903">å¯ä»¥è¯¢é—®èƒ½å¦è¿žæŽ¥åˆ° HID 设备</translation>
<translation id="4452328064229197696">您刚æ‰ä½¿ç”¨çš„密ç é­é‡äº†æ•°æ®æ³„露。为确ä¿æ‚¨çš„å¸å·å®‰å…¨ï¼ŒGoogle 密ç ç®¡ç†å™¨å»ºè®®æ‚¨æ£€æŸ¥å·²ä¿å­˜çš„所有密ç ã€‚</translation>
@@ -1402,6 +1411,7 @@
<translation id="483241715238664915">å¼€å¯è­¦å‘Š</translation>
<translation id="4834250788637067901">Google Pay 中存储的付款方å¼ã€ä¼˜æƒ å’Œåœ°å€ä¿¡æ¯</translation>
<translation id="4838327282952368871">梦幻</translation>
+<translation id="4839087176073128681">业界领先的 Google 安防机制让您能在下次购物时更快æ·åœ°ä»˜æ¬¾å¹¶ä¿æŠ¤æ‚¨çš„银行å¡ã€‚</translation>
<translation id="4840250757394056958">查看您的 Chrome 历å²è®°å½•</translation>
<translation id="484462545196658690">自动</translation>
<translation id="484671803914931257">获å–<ph name="MERCHANT_NAME" />åŠå…¶ä»–商家的折扣信æ¯</translation>
@@ -1464,6 +1474,7 @@
<translation id="5011561501798487822">检测到的语言</translation>
<translation id="5015510746216210676">设备å称:</translation>
<translation id="5017554619425969104">您å¤åˆ¶çš„文字</translation>
+<translation id="5017828934289857214">ç¨åŽæ醒我</translation>
<translation id="5018422839182700155">无法打开此网页</translation>
<translation id="5019198164206649151">åŽå¤‡å­˜å‚¨çŠ¶æ€ä¸ä½³</translation>
<translation id="5020776957610079374">世界音ä¹</translation>
@@ -1483,6 +1494,7 @@
<translation id="5051305769747448211">相声/å°å“</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{è‹¥è¦ä½¿ç”¨â€œé™„近分享â€åŠŸèƒ½å‘é€è¿™ä¸ªæ–‡ä»¶ï¼Œè¯·å…ˆåœ¨è®¾å¤‡ä¸Šé‡Šæ”¾ç©ºé—´ (<ph name="DISK_SPACE_SIZE" />)}other{è‹¥è¦ä½¿ç”¨â€œé™„近分享â€åŠŸèƒ½å‘é€è¿™äº›æ–‡ä»¶ï¼Œè¯·å…ˆåœ¨è®¾å¤‡ä¸Šé‡Šæ”¾ç©ºé—´ (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">为您推è的文章</translation>
+<translation id="5060483733937416656">您已选择在使用 <ph name="PROVIDER_ORIGIN" /> 的网站上通过 Windows Hello æ¥éªŒè¯èº«ä»½ã€‚æ­¤æ供商å¯èƒ½å·²å­˜å‚¨æ‚¨çš„付款方å¼ä¿¡æ¯ï¼Œä¸è¿‡æ‚¨å¯ä»¥<ph name="LINK_TEXT" />相应信æ¯ã€‚</translation>
<translation id="5061227663725596739">您是ä¸æ˜¯è¦å‰å¾€ <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">打å°è®°å½•</translation>
<translation id="5068234115460527047">对冲基金</translation>
@@ -1496,10 +1508,8 @@
<translation id="5087286274860437796">æœåŠ¡å™¨çš„è¯ä¹¦ç›®å‰æ— æ•ˆã€‚</translation>
<translation id="5087580092889165836">添加新å¡</translation>
<translation id="5088142053160410913">æ“作人员须知</translation>
-<translation id="5089810972385038852">å·ž/çœ/直辖市/自治区</translation>
<translation id="5093232627742069661">Z 型折</translation>
<translation id="5094747076828555589">æ­¤æœåŠ¡å™¨æ— æ³•è¯æ˜Žå®ƒæ˜¯<ph name="DOMAIN" />ï¼›Chromiumä¸ä¿¡ä»»å…¶å®‰å…¨è¯ä¹¦ã€‚出现此问题的原因å¯èƒ½æ˜¯é…置有误或您的连接被拦截了。</translation>
-<translation id="5095208057601539847">çœ</translation>
<translation id="5097099694988056070">设备统计信æ¯ï¼ˆä¾‹å¦‚ CPU/RAM 使用情况)</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">网站ä¸å®‰å…¨</translation>
@@ -1537,6 +1547,7 @@
<translation id="5171045022955879922">æœç´¢æˆ–输入网å€</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">本机</translation>
+<translation id="5177076414499237632">了解此页é¢çš„æ¥æºå’Œä¸»é¢˜</translation>
<translation id="5179510805599951267">ä¸æ˜¯<ph name="ORIGINAL_LANGUAGE" />?报告此错误</translation>
<translation id="518639307526414276">宠物食å“与宠物护ç†ç”¨å“</translation>
<translation id="5190835502935405962">书签æ </translation>
@@ -1697,6 +1708,7 @@
<translation id="5624120631404540903">管ç†å¯†ç </translation>
<translation id="5629630648637658800">无法加载策略设置</translation>
<translation id="5631439013527180824">设备管ç†ä»¤ç‰Œæ— æ•ˆ</translation>
+<translation id="5632485077360054581">æ“作步骤</translation>
<translation id="5633066919399395251">攻击者å¯èƒ½ä¼šè¯•å›¾é€šè¿‡ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 在您的计算机上安装å±é™©ç¨‹åºï¼Œä»¥çªƒå–或删除您的信æ¯ï¼ˆå¦‚照片ã€å¯†ç ã€é€šè®¯å†…容和信用å¡ä¿¡æ¯ï¼‰ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">欺骗性内容已被拦截。</translation>
<translation id="5633259641094592098">邪典电影与独立电影</translation>
@@ -1814,6 +1826,7 @@
<translation id="5989320800837274978">固定代ç†æœåŠ¡å™¨å’Œ .pac 脚本网å€å‡æœªæŒ‡å®šã€‚</translation>
<translation id="5992691462791905444">工程 Z 型折</translation>
<translation id="5995727681868049093">在您的 Google å¸å·ä¸­ç®¡ç†è‡ªå·±çš„ä¿¡æ¯ã€éšç§å’Œå®‰å…¨</translation>
+<translation id="5997247540087773573">您刚æ‰ä½¿ç”¨çš„密ç é­é‡äº†æ•°æ®æ³„露。为确ä¿æ‚¨çš„å¸å·å®‰å…¨ï¼ŒGoogle 密ç ç®¡ç†å·¥å…·å»ºè®®æ‚¨ç«‹å³æ›´æ”¹æ­¤å¯†ç å¹¶æ£€æŸ¥æ‚¨å·²ä¿å­˜çš„所有密ç ã€‚</translation>
<translation id="6000758707621254961"><ph name="RESULT_COUNT" /> 个与“<ph name="SEARCH_TEXT" />â€ç›¸ç¬¦çš„æœç´¢ç»“æžœ</translation>
<translation id="6006484371116297560">ç»å…¸</translation>
<translation id="6008122969617370890">从 N 到 1 的顺åº</translation>
@@ -1908,7 +1921,6 @@
<translation id="627746635834430766">若想在下次购物时更快æ·åœ°ä»˜æ¬¾ï¼Œè¯·å°†æ‚¨çš„付款å¡ä¿¡æ¯å’Œå¸å•é‚®å¯„地å€ä¿å­˜åˆ°æ‚¨çš„ Google å¸å·ä¸­ã€‚</translation>
<translation id="6279183038361895380">按 |<ph name="ACCELERATOR" />| å¯æ˜¾ç¤ºé¼ æ ‡å…‰æ ‡</translation>
<translation id="6280223929691119688">无法递é€åˆ°æ­¤åœ°å€ã€‚请å¦é€‰ä¸€ä¸ªåœ°å€ã€‚</translation>
-<translation id="6282194474023008486">邮政编ç </translation>
<translation id="6285507000506177184">â€œç®¡ç† Chrome 中的下载内容â€æŒ‰é’®ï¼ŒæŒ‰ Enter é”®å³å¯ç®¡ç†æ‚¨å·²åœ¨ Chrome 中下载的文件</translation>
<translation id="6289939620939689042">页é¢é¢œè‰²</translation>
<translation id="6290238015253830360">为您推è的文章会显示在此处</translation>
@@ -1930,6 +1942,7 @@
<translation id="6337133576188860026">释放ä¸åˆ° <ph name="SIZE" /> 空间。当您下次访问时,æŸäº›ç½‘站的加载速度å¯èƒ½ä¼šæ›´æ…¢ã€‚</translation>
<translation id="6337534724793800597">按å称过滤政策</translation>
<translation id="6340739886198108203">管ç†å‘˜æ”¿ç­–ä¸å»ºè®®åœ¨å±å¹•æ˜¾ç¤ºæœºå¯†å†…容时截å±/录å±ï¼š</translation>
+<translation id="6348220984832452017">使用中的å˜ä½“</translation>
<translation id="6349101878882523185">安装“<ph name="APP_NAME" />â€</translation>
<translation id="6353505687280762741">{COUNT,plural, =0{æ— }=1{1 个密ç ï¼ˆç”¨äºŽ <ph name="DOMAIN_LIST" />,已åŒæ­¥ï¼‰}=2{2 个密ç ï¼ˆç”¨äºŽ <ph name="DOMAIN_LIST" />,已åŒæ­¥ï¼‰}other{# 个密ç ï¼ˆç”¨äºŽ <ph name="DOMAIN_LIST" />,已åŒæ­¥ï¼‰}}</translation>
<translation id="6355392890578844978">æ­¤æµè§ˆå™¨å¹¶æœªç”±æŸä¸ªå…¬å¸æˆ–其他组织管ç†ã€‚此设备上的活动å¯èƒ½åœ¨æŽ¥å— Chromium 外部的管ç†ã€‚<ph name="BEGIN_LINK" />了解详情<ph name="END_LINK" /></translation>
@@ -1961,7 +1974,6 @@
<translation id="643051589346665201">更改 Google 密ç </translation>
<translation id="6433490469411711332">修改è”系信æ¯</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> æ‹’ç»äº†æˆ‘们的连接请求。</translation>
-<translation id="6438025220577812695">我自行更改</translation>
<translation id="6440503408713884761">已忽略</translation>
<translation id="6443406338865242315">您已安装的扩展程åºå’Œæ’件</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2091,9 +2103,9 @@
<translation id="6828866289116430505">é—ä¼ å­¦</translation>
<translation id="6831043979455480757">翻译</translation>
<translation id="6833752742582340615">将您的付款å¡ä¿¡æ¯å’Œç»“ç®—ä¿¡æ¯ä¿å­˜åˆ°æ‚¨çš„ Google å¸å·ä¸­ï¼Œä¾¿äºŽæ‚¨å®‰å…¨å¿«æ·åœ°ç»“è´¦</translation>
-<translation id="6839929833149231406">地域</translation>
<translation id="6846340164947227603">使用虚拟å¡å·â€¦</translation>
<translation id="6852204201400771460">è¦é‡æ–°åŠ è½½åº”用å—?</translation>
+<translation id="6857776781123259569">管ç†å¯†ç â€¦</translation>
<translation id="686485648936420384">é¢å‘消费者的资æº</translation>
<translation id="6865412394715372076">暂时无法验è¯æ­¤ä¿¡ç”¨å¡</translation>
<translation id="6869334554832814367">个人贷款</translation>
@@ -2142,7 +2154,6 @@
<translation id="6965978654500191972">设备</translation>
<translation id="696703987787944103">å¯æ„ŸçŸ¥</translation>
<translation id="6968269510885595029">使用您的安全密钥</translation>
-<translation id="6970216967273061347">区</translation>
<translation id="6971439137020188025">在 Google å¹»ç¯ç‰‡ä¸­å¿«é€Ÿåˆ›å»ºæ–°çš„ Google 演示文稿</translation>
<translation id="6972629891077993081">HID 设备</translation>
<translation id="6973656660372572881">固定代ç†æœåŠ¡å™¨å’Œ .pac 脚本网å€å‡å·²æŒ‡å®šã€‚</translation>
@@ -2181,7 +2192,6 @@
<translation id="7081308185095828845">您的设备无法使用此功能</translation>
<translation id="7083258188081898530">纸匣 9</translation>
<translation id="7086090958708083563">用户已请求上传</translation>
-<translation id="7087282848513945231">县</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />,ä¾æ¬¡æŒ‰ Tab 键和 Enter é”®å³å¯åœ¨ Chrome 设置中管ç†å„网站的æƒé™åŠå­˜å‚¨çš„æ•°æ®</translation>
<translation id="7096937462164235847">此网站的身份未ç»éªŒè¯ã€‚</translation>
<translation id="7101893872976785596">æ怖片</translation>
@@ -2200,10 +2210,10 @@
<ph name="LIST_ITEM" />在表å•ä¸­å¡«å†™çš„ä¿¡æ¯<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">无法å‘此地å€é€è´§ã€‚请å¦é€‰ä¸€ä¸ªåœ°å€ã€‚</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /><ph name="DETAILS" /></translation>
<translation id="7132939140423847331">您的管ç†å‘˜å·²ç¦æ­¢å¤åˆ¶æ­¤æ•°æ®ã€‚</translation>
<translation id="7135130955892390533">显示状æ€</translation>
<translation id="7138472120740807366">递é€æ–¹å¼</translation>
-<translation id="7139724024395191329">酋长国</translation>
<translation id="7139892792842608322">主è¦çº¸åŒ£</translation>
<translation id="714064300541049402">侧边 2,图片沿 X è½´ä½ç§»</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2420,6 +2430,7 @@
<translation id="7669271284792375604">此网站上的攻击者å¯èƒ½ä¼šè¯•å›¾è¯±éª—您安装有æŸæµè§ˆä½“验的程åºï¼ˆä¾‹å¦‚:通过更改您的主页或在您访问的网站上显示é¢å¤–的广告)。</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{对标记为“机密â€çš„æ•°æ®æ‰§è¡Œçš„æ“作(自登录åŽæŠ¥å‘Šäº† 1 项æ“作)。<ph name="BEGIN_LINK" />了解详情<ph name="END_LINK" />}other{对标记为“机密â€çš„æ•°æ®æ‰§è¡Œçš„æ“作(自登录åŽæŠ¥å‘Šäº† # 项æ“作)。<ph name="BEGIN_LINK" />了解详情<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">ä¿¡ç®± 6</translation>
+<translation id="7675325315208090829">管ç†ä»˜æ¬¾æ–¹å¼â€¦</translation>
<translation id="7676643023259824263">æœç´¢å‰ªè´´æ¿ä¸Šçš„文字:<ph name="TEXT" /></translation>
<translation id="7679367271685653708">在 Chrome 设置中查看和管ç†æ‚¨çš„æµè§ˆè®°å½•</translation>
<translation id="7679947978757153706">棒çƒ</translation>
@@ -2462,7 +2473,6 @@
<translation id="7766518757692125295">裙å¼</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">顺åºä¸å˜ï¼Œæ­£é¢æœä¸Š</translation>
-<translation id="777702478322588152">县</translation>
<translation id="7791011319128895129">未å‘布</translation>
<translation id="7791196057686275387">æ†ç»‘å¼è£…订</translation>
<translation id="7791543448312431591">添加</translation>
@@ -2553,12 +2563,12 @@
<translation id="8055534648776115597">èŒä¸šæ•™è‚²ä¸Žç»§ç»­æ•™è‚²</translation>
<translation id="8057711352706143257">“<ph name="SOFTWARE_NAME" />â€çš„é…置有误。å¸è½½â€œ<ph name="SOFTWARE_NAME" />â€é€šå¸¸å¯è§£å†³æ­¤é—®é¢˜ã€‚<ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">食å“生产</translation>
-<translation id="8066955247577885446">抱歉,出了点问题。</translation>
<translation id="8067872629359326442">您刚刚在一个诈骗网站中输入了密ç ã€‚Chromium å¯ä»¥ä¸ºæ‚¨æ供帮助。若è¦æ›´æ”¹å¯†ç å¹¶è®© Google 知晓您的å¸å·å¯èƒ½å¤„于å±é™©çŠ¶æ€ï¼Œè¯·ç‚¹å‡»â€œä¿æŠ¤å¸å·â€ã€‚</translation>
<translation id="8070439594494267500">应用图标</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">快速创建新的 Google 表格</translation>
<translation id="8075898834294118863">管ç†ç½‘站设置</translation>
+<translation id="8076492880354921740">标签页</translation>
<translation id="8078141288243656252">无法在旋转åŽæ·»åŠ æ³¨é‡Š</translation>
<translation id="8079031581361219619">é‡æ–°åŠ è½½æ­¤ç½‘站?</translation>
<translation id="8081087320434522107">轿车</translation>
@@ -2681,6 +2691,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> - <ph name="SECOND_LABEL" /> - <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">设置</translation>
+<translation id="8428634594422941299">知é“了</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />,ä¾æ¬¡æŒ‰ Tab 键和 Enter é”®å³å¯åœ¨ Chrome 设置中管ç†æ‚¨çš„ Cookie å好设置</translation>
<translation id="8433057134996913067">这将使您退出大多数网站。</translation>
<translation id="8434840396568290395">宠物</translation>
@@ -2779,6 +2790,7 @@
<translation id="8742371904523228557">您的 <ph name="ORIGIN" /> 验è¯ç æ˜¯ <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">为此标签页添加书签</translation>
<translation id="8751426954251315517">请ç¨åŽé‡è¯•</translation>
+<translation id="8757526089434340176">有å¯ç”¨çš„ Google Pay 优惠</translation>
<translation id="8758885506338294482">竞技类视频游æˆ</translation>
<translation id="8759274551635299824">此信用å¡å·²å¤±æ•ˆ</translation>
<translation id="87601671197631245">此网站使用的安全性é…置已过时,这å¯èƒ½ä¼šå¯¼è‡´æ‚¨çš„ä¿¡æ¯ï¼ˆä¾‹å¦‚密ç ã€æ¶ˆæ¯æˆ–信用å¡å¡å·ï¼‰åœ¨å‘é€è‡³æ­¤ç½‘站的过程中é­åˆ°æ³„露。</translation>
@@ -2786,6 +2798,7 @@
<translation id="8763927697961133303">USB 设备</translation>
<translation id="8763986294015493060">关闭当å‰æ‰“开的所有无痕å¼çª—å£</translation>
<translation id="8766943070169463815">安全付款凭æ®èº«ä»½éªŒè¯è¡¨å•å·²æ‰“å¼€</translation>
+<translation id="8767765348545497220">关闭帮助气泡</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">摩托车</translation>
<translation id="8790007591277257123">æ¢å¤åˆ é™¤(&amp;R)</translation>
@@ -2798,6 +2811,7 @@
<translation id="8806285662264631610">æ²æµ´ä¸Žèº«ä½“护ç†äº§å“</translation>
<translation id="8807160976559152894">完æˆæ¯é¡µåŽè£åˆ‡</translation>
<translation id="8808828119384186784">Chrome 设置</translation>
+<translation id="8813277370772331957">ç¨åŽæ醒我</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />,ä¾æ¬¡æŒ‰ Tab 键和 Enter é”®å³å¯å‰å¾€ Chrome 设置以更新 Chrome</translation>
<translation id="8820817407110198400">书签</translation>
<translation id="882338992931677877">手动æ’槽</translation>
@@ -2977,6 +2991,7 @@
<translation id="988159990683914416">å¼€å‘者内部版本</translation>
<translation id="989988560359834682">修改地å€</translation>
<translation id="991413375315957741">移动传感器或光传感器</translation>
+<translation id="992110854164447044">使用虚拟å¡å¯éšè—您的实体å¡ä¿¡æ¯ï¼Œä¿æŠ¤æ‚¨å…é­æ½œåœ¨æ¬ºè¯ˆæ´»åŠ¨çš„å¨èƒã€‚<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">粉色</translation>
<translation id="992432478773561401">“<ph name="SOFTWARE_NAME" />â€æ²¡æœ‰æ­£ç¡®åœ°å®‰è£…到您的计算机或网络上:
diff --git a/chromium/components/strings/components_strings_zh-HK.xtb b/chromium/components/strings/components_strings_zh-HK.xtb
index b04b29fe026..6c7af33f3aa 100644
--- a/chromium/components/strings/components_strings_zh-HK.xtb
+++ b/chromium/components/strings/components_strings_zh-HK.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">補助金ã€çŽå­¸é‡‘和財務資助</translation>
<translation id="1048785276086539861">在您編輯註解時,此文件將會返回單é æª¢è¦–模å¼</translation>
<translation id="1050038467049342496">關閉其他應用程å¼</translation>
+<translation id="1053959602163383901">您已é¸æ“‡é€éŽé©—證器è£ç½®åœ¨ä½¿ç”¨ <ph name="PROVIDER_ORIGIN" /> 的網站進行驗證。此供應商å¯èƒ½å·²å„²å­˜æ‚¨çš„付款方法相關資料,您å¯ä»¥ <ph name="LINK_TEXT" />。</translation>
<translation id="1055184225775184556">復原新增(&amp;U)</translation>
<translation id="1056663316309890343">相片軟件</translation>
<translation id="1056898198331236512">警告</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">å–貨方å¼</translation>
<translation id="1281476433249504884">堆疊器 1</translation>
<translation id="1285320974508926690">æ°¸ä¸ç¿»è­¯æ­¤ç¶²ç«™</translation>
+<translation id="1288548991597756084">安全地儲存信用å¡</translation>
<translation id="1292571435393770077">紙匣 16</translation>
<translation id="1292701964462482250">「您電腦上的軟件正阻止 Chrome 建立安全網絡連線ã€(åƒ…é™ Windows 電腦)</translation>
<translation id="1294154142200295408">指令的變化</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;如è¦ä¿®æ­£éŒ¯èª¤ï¼Œè«‹åœ¨å˜—試開啟的網é ä¸ŠæŒ‰ä¸€ä¸‹ [連線]&lt;strong&gt;&lt;/strong&gt;。&lt;/p&gt;</translation>
<translation id="1507780850870535225">園景設計</translation>
<translation id="1513706915089223971">記錄項目清單</translation>
+<translation id="1516097932025103760">信用å¡å°‡æœƒåŠ å¯†ä¸¦å¦¥å–„儲存,而且系統絕ä¸æœƒå„²å­˜ CVC。</translation>
<translation id="1517433312004943670">è«‹æ供電話號碼</translation>
<translation id="1519264250979466059">版本日期</translation>
<translation id="1521159554480556801">纖維和紡織è—è¡“</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">儲存和填入付款方法</translation>
<translation id="1663943134801823270">信用å¡å’Œåœ°å€é¸é …å‡ä¾†è‡ª Chrome。您å¯åœ¨ã€Œ<ph name="BEGIN_LINK" />設定<ph name="END_LINK" />ã€ä¸­ç®¡ç†é€™äº›é¸é …。</translation>
<translation id="1671391448414634642">從ç¾åœ¨é–‹å§‹ï¼Œä¾†æºèªžè¨€ç‚º<ph name="SOURCE_LANGUAGE" />的網é ä¸€å¾‹æœƒç¿»è­¯æˆ<ph name="TARGET_LANGUAGE" />。</translation>
+<translation id="1673886523110456987">使用 <ph name="CARD_DETAIL" /> çµå¸³å³å¯äº«æœ‰å„ªæƒ </translation>
<translation id="1674504678466460478">從<ph name="SOURCE_LANGUAGE" />翻譯æˆ<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">先從短邊開始</translation>
<translation id="168693727862418163">此政策的值無法通éŽå…¶ç¶±è¦ (schema) 的驗證,因此會被忽略。</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">動作感應器</translation>
<translation id="1717494416764505390">出紙槽 3</translation>
<translation id="1718029547804390981">文件éŽå¤§ï¼Œç„¡æ³•åŠ å…¥è¨»é‡‹</translation>
+<translation id="1720941539803966190">閂教學課程</translation>
<translation id="1721424275792716183">* 為必填欄ä½</translation>
<translation id="1727613060316725209">憑證有效</translation>
<translation id="1727741090716970331">新增有效的信用å¡è™Ÿç¢¼</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">燒烤</translation>
<translation id="2053111141626950936">系統將ä¸æœƒç¿»è­¯<ph name="LANGUAGE" />網é ã€‚</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{如果此功能已啟用且處於有效狀態,Chrome 就會判斷您最近的ç€è¦½æ´»å‹•èˆ‡å“ªå€‹å¤§åž‹ä½¿ç”¨è€…群組 (åˆç¨±ã€Œçµ„別ã€) 最為相似。廣告客戶å¯ä»¥é¸æ“‡è¦å‘該群組顯示的廣告,而您的ç€è¦½æ´»å‹•æœƒä¿ç•™åœ¨è£ç½®ä¸Šï¼Œçµ•ä¸æœƒå¤–洩。系統會æ¯æ—¥æ›´æ–°æ‚¨æ‰€å±¬çš„群組。}=1{如果此功能已啟用且處於有效狀態,Chrome 就會判斷您最近的ç€è¦½æ´»å‹•èˆ‡å“ªå€‹å¤§åž‹ä½¿ç”¨è€…群組 (åˆç¨±ã€Œçµ„別ã€) 最為相似。廣告客戶å¯ä»¥é¸æ“‡è¦å‘該群組顯示的廣告,而您的ç€è¦½æ´»å‹•æœƒä¿ç•™åœ¨è£ç½®ä¸Šï¼Œçµ•ä¸æœƒå¤–洩。系統會æ¯æ—¥æ›´æ–°æ‚¨æ‰€å±¬çš„群組。}other{如果此功能已啟用且處於有效狀態,Chrome 就會判斷您最近的ç€è¦½æ´»å‹•èˆ‡å“ªå€‹å¤§åž‹ä½¿ç”¨è€…群組 (åˆç¨±ã€Œçµ„別ã€) 最為相似。廣告客戶å¯ä»¥é¸æ“‡è¦å‘該群組顯示的廣告,而您的ç€è¦½æ´»å‹•æœƒä¿ç•™åœ¨è£ç½®ä¸Šï¼Œçµ•ä¸æœƒå¤–洩。系統會æ¯éš” {NUM_DAYS} 日更新您所屬的群組。}}</translation>
-<translation id="2053553514270667976">郵éžå€è™Ÿ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 個建議}other{# 個建議}}</translation>
+<translation id="2066915425250589881">è¦æ±‚刪除已儲存的資料</translation>
<translation id="2068528718802935086">嬰幼兒</translation>
<translation id="2071156619270205202">æ­¤å¡ä¸èƒ½å»ºç«‹è™›æ“¬å¡è™Ÿã€‚</translation>
<translation id="2071692954027939183">由於您通常ä¸å…許通知,因此系統已自動å°éŽ–通知</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">管ç†åŒæ­¥åŠŸèƒ½æŒ‰éˆ•ï¼Œã©’一下 Enter éµå°±å¯ä»¥å–º Chrome 設定度管ç†åŒæ­¥è³‡æ–™</translation>
<translation id="2091887806945687916">音效</translation>
<translation id="2094505752054353250">網域ä¸ç¬¦</translation>
-<translation id="2096368010154057602">çœ</translation>
<translation id="2099652385553570808">三釘 (å·¦å´)</translation>
<translation id="2101225219012730419">版本:</translation>
<translation id="2102134110707549001">建議安全性強的密碼…</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">美å¼è¶³çƒ</translation>
<translation id="2187317261103489799">åµæ¸¬ (é è¨­)</translation>
<translation id="2188375229972301266">多孔 (底部)</translation>
-<translation id="2188852899391513400">系統發ç¾æ‚¨å‰›æ‰ä½¿ç”¨çš„密碼因資料外洩而被洩露。為確ä¿å¸³æˆ¶å®‰å…¨ï¼ŒGoogle 密碼管ç†å·¥å…·å»ºè­°æ‚¨ç«‹å³è®Šæ›´å¯†ç¢¼ï¼Œç„¶å¾Œæª¢æŸ¥å·²å„²å­˜çš„密碼。</translation>
<translation id="219906046732893612">家居è£ä¿®</translation>
<translation id="2202020181578195191">請輸入有效的到期年份</translation>
<translation id="22081806969704220">紙匣 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">檔案編輯</translation>
<translation id="2215963164070968490">ç‹—</translation>
<translation id="2218879909401188352">ç›®å‰åœ¨ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 上的攻擊者å¯èƒ½æœƒå®‰è£å±éšªçš„應用程å¼ä¾†æ害您的è£ç½®ã€æš—中加入隱è—çš„æµå‹•æœå‹™è²»ç”¨ï¼Œæˆ–ç«Šå–您的個人資料。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">é‡æ–°å•Ÿå‹•æ•™å­¸èª²ç¨‹</translation>
+<translation id="2219735899272417925">å¿…é ˆé‡è¨­è£ç½®</translation>
<translation id="2224337661447660594">沒有互è¯ç¶²</translation>
<translation id="2230458221926704099">請使用<ph name="BEGIN_LINK" />診斷應用程å¼<ph name="END_LINK" />修正連線å•é¡Œ</translation>
<translation id="2239100178324503013">ç«‹å³å‚³é€</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">ä¸å…許 (é è¨­)</translation>
<translation id="2512413427717747692">å°‡ Chrome 設定åšé è¨­ç€è¦½å™¨æŒ‰éˆ•ï¼Œã©’ Enter éµå°±å¯ä»¥å–º iOS 設定度將 Chrome 設定åšç³»çµ±å˜…é è¨­ç€è¦½å™¨</translation>
<translation id="2515629240566999685">檢查您所在地å€çš„網絡訊號</translation>
+<translation id="2515761554693942801">您已é¸æ“‡é€éŽ Touch ID 在使用 <ph name="PROVIDER_ORIGIN" /> 的網站上進行驗證。此供應商å¯èƒ½å·²å„²å­˜æ‚¨çš„付款方法相關資料,您å¯ä»¥ <ph name="LINK_TEXT" />。</translation>
<translation id="2521385132275182522">é‡˜è£ (å³ä¸‹æ–¹)</translation>
<translation id="2521736961081452453">建立表格</translation>
<translation id="2523886232349826891">åªæœƒå„²å­˜è‡³æ­¤è£ç½®</translation>
<translation id="2524461107774643265">新增更多資料</translation>
<translation id="2529899080962247600">此欄ä½åªèƒ½åŒ…å« <ph name="MAX_ITEMS_LIMIT" /> 個項目,超éŽçš„項目會被忽略。</translation>
+<translation id="253493526287553278">查看宣傳代碼詳情</translation>
<translation id="2535585790302968248">é–‹å•Ÿæ–°çš„ç„¡ç—•å¼åˆ†é ä»¥ç§ä¸‹ç€è¦½å…§å®¹</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{å’Œå¦å¤– 1 個網域}other{å’Œå¦å¤– # 個網域}}</translation>
<translation id="2536110899380797252">新增地å€</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">æ”影和數碼è—è¡“</translation>
<translation id="2601150049980261779">愛情電影</translation>
<translation id="2604589665489080024">æµè¡ŒéŸ³æ¨‚</translation>
-<translation id="2609632851001447353">變化</translation>
<translation id="2610561535971892504">點擊複製</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />ä¸æœƒå„²å­˜<ph name="END_EMPHASIS" />以下資料:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">生日和命åæ—¥</translation>
<translation id="2677748264148917807">離開</translation>
+<translation id="2679714844901977852">將信用å¡å’Œå¸³å–®è³‡æ–™å„²å­˜è‡³ Google 帳戶 <ph name="USER_EMAIL" />,享å—更安全快速的çµå¸³ç¨‹åº</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">最é©åˆ</translation>
<translation id="2688969097326701645">是,請繼續</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">互è¯ç¶²æœå‹™ä¾›æ‡‰å•† (ISP)</translation>
<translation id="2856444702002559011">攻擊者å¯èƒ½æœƒè©¦åœ–é€éŽ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ç«Šå–您的資料 (例如密碼ã€è¨Šæ¯æˆ–信用å¡è³‡æ–™)。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">此網站顯示滋擾性或誤導廣告。</translation>
-<translation id="286512204874376891">虛擬å¡æœƒéš±è—您的實體å¡ï¼Œä¿è­·æ‚¨å…å—潛在欺è©è¡Œç‚ºçš„å¨è„…。<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">å‹å–„</translation>
<translation id="28761159517501904">電影</translation>
<translation id="2876489322757410363">å³å°‡é€€å‡ºç„¡ç—•æ¨¡å¼ï¼Œæ”¹ç”¨å¤–部應用程å¼ä»˜æ¬¾ã€‚è¦ç¹¼çºŒå—Žï¼Ÿ</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">光碟</translation>
<translation id="3162559335345991374">ç›®å‰ä½¿ç”¨çš„ Wi-Fi å¯èƒ½è¦æ±‚您å‰å¾€å…¶ç™»å…¥é é¢ã€‚</translation>
<translation id="3169472444629675720">探索</translation>
-<translation id="3174168572213147020">島嶼</translation>
<translation id="3176929007561373547">檢查您的 Proxy 設定,或與網絡管ç†å“¡è¯çµ¡ï¼Œä»¥
ç¢ºèª Proxy 伺æœå™¨é‹ä½œæ­£å¸¸ã€‚如果您èªç‚ºä¸éœ€è¦ä½¿ç”¨
Proxy 伺æœå™¨ï¼š
@@ -877,9 +882,6 @@
<translation id="3369192424181595722">時é˜éŒ¯èª¤</translation>
<translation id="3369459162151165748">汽車零件和é…件</translation>
<translation id="3371064404604898522">å°‡ Chrome 設定為é è¨­ç€è¦½å™¨</translation>
-<translation id="3371076217486966826"><ph name="URL" /> è¦æ±‚以下權é™ï¼š
- • 根據您身處環境建立 3D 地圖並追蹤æ”錄機ä½ç½®
- • 使用æ”錄機</translation>
<translation id="337363190475750230">å·²å–消佈建</translation>
<translation id="3375754925484257129">執行 Chrome 安全檢查</translation>
<translation id="3377144306166885718">伺æœå™¨çš„ TLS 版本已éŽæ™‚。</translation>
@@ -896,6 +898,7 @@
<translation id="3399952811970034796">é€è²¨åœ°å€</translation>
<translation id="3402261774528610252">用於載入此網站的連線使用 TLS 1.0 或 TLS 1.1 版本ç¾å·²è¢«æ·˜æ±°ï¼Œä¸¦æœƒæ–¼æ—¥å¾Œåœç”¨ã€‚åœç”¨å¾Œï¼Œä½¿ç”¨è€…將無法載入此網站。伺æœå™¨æ‡‰å•Ÿç”¨ TLS 1.2 或以上版本。</translation>
<translation id="3405664148539009465">自訂字型</translation>
+<translation id="3407789382767355356">第三方登入</translation>
<translation id="3409896703495473338">管ç†å®‰å…¨æ€§è¨­å®š</translation>
<translation id="3414952576877147120">大å°ï¼š</translation>
<translation id="3417660076059365994">您上載或附加的檔案將會傳é€çµ¦ Google Cloud 或第三方進行分æžã€‚例如,檔案å¯èƒ½æœƒè¢«æŽƒæ,以查看當中是å¦å«æœ‰æ•æ„Ÿè³‡æ–™æˆ–惡æ„軟件。</translation>
@@ -928,6 +931,7 @@
<translation id="3477679029130949506">電影場次åŠæ”¾æ˜ æ™‚é–“</translation>
<translation id="3479552764303398839">ç•¥éŽ</translation>
<translation id="3484560055331845446">您å¯èƒ½æœƒå¤±åŽ» Google 帳戶存å–權。Chrome 建議您立å³è®Šæ›´å¯†ç¢¼ã€‚系統會è¦æ±‚您登入。</translation>
+<translation id="3484861421501147767">æ醒:å¯ä½¿ç”¨å·²å„²å­˜çš„宣傳代碼</translation>
<translation id="3487845404393360112">紙匣 4</translation>
<translation id="3495081129428749620">喺「<ph name="PAGE_TITLE" />ã€ç¶²é åº¦æµ</translation>
<translation id="350069200438440499">檔案å稱:</translation>
@@ -1051,6 +1055,7 @@
<translation id="3810973564298564668">管ç†</translation>
<translation id="3816482573645936981">值 (已被å–代)</translation>
<translation id="382518646247711829">如果您使用 Proxy 伺æœå™¨â€¦</translation>
+<translation id="3826050100957962900">第三方登入</translation>
<translation id="3827112369919217609">絕å°</translation>
<translation id="3827666161959873541">家庭電影</translation>
<translation id="3828924085048779000">複雜密碼欄ä½ä¸å¾—留空。</translation>
@@ -1063,9 +1068,9 @@
<translation id="3858027520442213535">更新日期和時間</translation>
<translation id="3858860766373142691">å稱</translation>
<translation id="3872834068356954457">科學</translation>
+<translation id="3875783148670536197">顯示方法</translation>
<translation id="3881478300875776315">顯示較少行</translation>
<translation id="3884278016824448484">è£ç½®è­˜åˆ¥ç¢¼ç™¼ç”Ÿè¡çª</translation>
-<translation id="3885155851504623709">管轄å€</translation>
<translation id="388632593194507180">åµæ¸¬åˆ°ç›£æŽ§</translation>
<translation id="3886948180919384617">堆疊器 3</translation>
<translation id="3890664840433101773">新增電郵</translation>
@@ -1095,9 +1100,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> å·²é­å°éŽ–</translation>
<translation id="3978338123949022456">æœå°‹æ¨¡å¼ï¼Œè¼¸å…¥æŸ¥è©¢åŒåŸ‹ã©’ Enter éµå°±å¯ä»¥ç”¨ <ph name="KEYWORD_SUFFIX" /> æœå°‹</translation>
<translation id="398470910934384994">é³¥</translation>
+<translation id="3985750352229496475">管ç†åœ°å€â€¦</translation>
<translation id="3986705137476756801">暫時關閉å³æ™‚字幕</translation>
<translation id="3987940399970879459">å°æ–¼ 1 MB</translation>
<translation id="3990250421422698716">æ’žé å移</translation>
+<translation id="3992684624889376114">關於本é </translation>
<translation id="3996311196211510766">網站 <ph name="ORIGIN" /> 指定所有收到的è¦æ±‚都必須套用來æºæ”¿ç­–,但目å‰ç„¡æ³•å¥—用此政策。</translation>
<translation id="4006465311664329701">使用 Google Pay 儲存的付款方法ã€å„ªæƒ å’Œåœ°å€</translation>
<translation id="4009243425692662128">您列å°çš„é é¢å…§å®¹æœƒå‚³é€è‡³ Google Cloud 或第三方進行分æžã€‚例如,Google Cloud 或第三方å¯èƒ½æœƒæŽƒçž„文字,檢查是å¦å«æœ‰æ•æ„Ÿè³‡æ–™ã€‚</translation>
@@ -1217,6 +1224,7 @@
<translation id="4305666528087210886">無法存å–您的檔案</translation>
<translation id="4306529830550717874">è¦å„²å­˜åœ°å€å—Žï¼Ÿ</translation>
<translation id="4306812610847412719">剪貼簿</translation>
+<translation id="4310070645992025887">æœå°‹ç€è¦½éŽç¨‹</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">å°éŽ– (é è¨­)</translation>
<translation id="4314815835985389558">管ç†åŒæ­¥è³‡æ–™</translation>
@@ -1267,6 +1275,7 @@
<translation id="4435702339979719576">明信片</translation>
<translation id="443673843213245140">雖然已åœç”¨ Proxy,ä¸éŽå·²æ˜Žç¢ºæŒ‡å®šäº† Proxy 設定。</translation>
<translation id="4441832193888514600">由於此政策åªèƒ½è¨­å®šç‚ºé›²ç«¯ä½¿ç”¨è€…政策,因此系統已忽略。</translation>
+<translation id="4442470707340296952">Chrome 分é </translation>
<translation id="4450893287417543264">ä¸è¦å†é¡¯ç¤º</translation>
<translation id="4451135742916150903">å¯è¦æ±‚連接 HID è£ç½®</translation>
<translation id="4452328064229197696">系統發ç¾æ‚¨å‰›æ‰ä½¿ç”¨çš„密碼因資料外洩而被洩露。為確ä¿å¸³æˆ¶å®‰å…¨ï¼ŒGoogle 密碼管ç†å·¥å…·å»ºè­°æ‚¨æª¢æŸ¥å·²å„²å­˜çš„密碼。</translation>
@@ -1405,6 +1414,7 @@
<translation id="483241715238664915">開啟警告</translation>
<translation id="4834250788637067901">使用 Google Pay 儲存的付款方法ã€å„ªæƒ å’Œåœ°å€</translation>
<translation id="4838327282952368871">夢幻</translation>
+<translation id="4839087176073128681">é€éŽ Google 領先業界的安全措施,加快下次çµå¸³é€Ÿåº¦ï¼Œä¸¦ä¿è­·ä¿¡ç”¨å¡ã€‚</translation>
<translation id="4840250757394056958">查看 Chrome 記錄</translation>
<translation id="484462545196658690">自動</translation>
<translation id="484671803914931257">å–å¾— <ph name="MERCHANT_NAME" /> 和其他商家的折扣</translation>
@@ -1467,6 +1477,7 @@
<translation id="5011561501798487822">åµæ¸¬åˆ°çš„語言</translation>
<translation id="5015510746216210676">電腦å稱:</translation>
<translation id="5017554619425969104">您複製的文字</translation>
+<translation id="5017828934289857214">ç¨å¾Œæ醒我</translation>
<translation id="5018422839182700155">無法開啟此網é </translation>
<translation id="5019198164206649151">備份儲存狀態ä¸ä½³</translation>
<translation id="5020776957610079374">世界音樂</translation>
@@ -1486,6 +1497,7 @@
<translation id="5051305769747448211">實æ³å–œåŠ‡</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{如è¦ä½¿ç”¨ã€Œå’«å°ºå…±äº«ã€å‚³é€æ­¤æª”案,請在è£ç½®ä¸Šé¨°å‡ºç©ºé–“ (<ph name="DISK_SPACE_SIZE" />)}other{如è¦ä½¿ç”¨ã€Œå’«å°ºå…±äº«ã€å‚³é€é€™äº›æª”案,請在è£ç½®ä¸Šé¨°å‡ºç©ºé–“ (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">為您推薦的文章</translation>
+<translation id="5060483733937416656">您已é¸æ“‡é€éŽ Windows Hello 在使用 <ph name="PROVIDER_ORIGIN" /> 的網站上進行驗證。此供應商å¯èƒ½å·²å„²å­˜æ‚¨çš„付款方法相關資料,您å¯ä»¥ <ph name="LINK_TEXT" />。</translation>
<translation id="5061227663725596739">您是ä¸æ˜¯è¦å‰å¾€ <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">列å°è¨˜éŒ„</translation>
<translation id="5068234115460527047">å°æ²–基金</translation>
@@ -1499,10 +1511,8 @@
<translation id="5087286274860437796">伺æœå™¨æ†‘證目å‰ç„¡æ•ˆã€‚</translation>
<translation id="5087580092889165836">加入新å¡</translation>
<translation id="5088142053160410913">給æ“作員的訊æ¯</translation>
-<translation id="5089810972385038852">å·ž</translation>
<translation id="5093232627742069661">風ç´æ‘º</translation>
<translation id="5094747076828555589">伺æœå™¨ç„¡æ³•è­‰æ˜Žå…¶å±¬æ–¼ <ph name="DOMAIN" /> 網域;其安全性憑證未å–å¾— Chromium 的信任。這å¯èƒ½æ˜¯å› ç‚ºè¨­å®šéŒ¯èª¤ï¼Œæˆ–有攻擊者攔截您的連線。</translation>
-<translation id="5095208057601539847">çœ</translation>
<translation id="5097099694988056070">CPU/RAM 使用情æ³ç­‰è£ç½®çµ±è¨ˆè³‡æ–™</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">這是ä¸å®‰å…¨çš„網站</translation>
@@ -1540,6 +1550,7 @@
<translation id="5171045022955879922">æœå°‹æˆ–輸入網å€</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">本機</translation>
+<translation id="5177076414499237632">進一步瞭解此é é¢çš„來æºå’Œä¸»é¡Œ</translation>
<translation id="5179510805599951267">網é å…§å®¹ä¸æ˜¯<ph name="ORIGINAL_LANGUAGE" />嗎?請報告此錯誤</translation>
<translation id="518639307526414276">寵物食å“和護ç†ç”¨å“</translation>
<translation id="5190835502935405962">書籤列</translation>
@@ -1700,6 +1711,7 @@
<translation id="5624120631404540903">管ç†å¯†ç¢¼</translation>
<translation id="5629630648637658800">無法載入政策設定</translation>
<translation id="5631439013527180824">è£ç½®ç®¡ç†æ†‘證無效</translation>
+<translation id="5632485077360054581">顯示方法</translation>
<translation id="5633066919399395251">ç›®å‰åœ¨ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 上的攻擊者å¯èƒ½æœƒè©¦åœ–在您的電腦上安è£å±éšªçš„應用程å¼ï¼Œä»¥ç«Šå–或刪除您的資料 (例如相片ã€å¯†ç¢¼ã€è¨Šæ¯å’Œä¿¡ç”¨å¡è³‡æ–™)。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">å·²å°éŽ–欺è©å…§å®¹ã€‚</translation>
<translation id="5633259641094592098">å¦é¡žå’Œç¨ç«‹é›»å½±</translation>
@@ -1817,6 +1829,7 @@
<translation id="5989320800837274978">沒有指定固定的 Proxy 伺æœå™¨å’Œ .pac 指令碼網å€ã€‚</translation>
<translation id="5992691462791905444">大å°é¢¨ç´æ‘º</translation>
<translation id="5995727681868049093">在 Google 帳戶管ç†æ‚¨çš„資料ã€ç§éš±æ¬Šå’Œå®‰å…¨è¨­å®š</translation>
+<translation id="5997247540087773573">系統發ç¾æ‚¨å‰›æ‰ä½¿ç”¨çš„密碼因資料外洩而被洩露。為確ä¿å¸³æˆ¶å®‰å…¨ï¼Œã€ŒGoogle 密碼管ç†å·¥å…·ã€å»ºè­°æ‚¨ç«‹å³è®Šæ›´å¯†ç¢¼ï¼Œä¸¦æª¢æŸ¥å·²å„²å­˜çš„密碼。</translation>
<translation id="6000758707621254961">有 <ph name="RESULT_COUNT" /> 個åŒã€Œ<ph name="SEARCH_TEXT" />ã€ç›¸é—œå˜…æœå°‹çµæžœ</translation>
<translation id="6006484371116297560">經典主題</translation>
<translation id="6008122969617370890">N 至 1 çš„é †åº</translation>
@@ -1912,7 +1925,6 @@
<translation id="627746635834430766">åªè¦å°‡æ­¤ä»˜æ¬¾å¡å’Œå¸³å–®åœ°å€å„²å­˜è‡³æ‚¨çš„ Google 帳戶,下次å³å¯æ›´å¿«å®Œæˆä»˜æ¬¾ç¨‹åºã€‚</translation>
<translation id="6279183038361895380">按下 |<ph name="ACCELERATOR" />| å³å¯é¡¯ç¤ºæ¸¸æ¨™</translation>
<translation id="6280223929691119688">無法é€è²¨è‡³æ­¤åœ°å€ï¼Œè«‹é¸å–其他地å€ã€‚</translation>
-<translation id="6282194474023008486">郵éžå€è™Ÿ</translation>
<translation id="6285507000506177184">ç®¡ç† Chrome 嘅下載項目按鈕,㩒一下 Enter éµå°±å¯ä»¥ç®¡ç†æ‚¨å–º Chrome 度下載嘅檔案</translation>
<translation id="6289939620939689042">é é¢é¡è‰²</translation>
<translation id="6290238015253830360">為您推薦的文章會在這裡顯示</translation>
@@ -1934,6 +1946,7 @@
<translation id="6337133576188860026">釋的快å–ä¸åˆ° <ph name="SIZE" />。在您下次ç€è¦½éƒ¨åˆ†ç¶²ç«™æ™‚,載入速度å¯èƒ½æœƒè®Šæ…¢ã€‚</translation>
<translation id="6337534724793800597">ä¾å稱篩é¸æ”¿ç­–</translation>
<translation id="6340739886198108203">當螢幕上出ç¾æ©Ÿå¯†å…§å®¹æ™‚,管ç†å“¡æ”¿ç­–ä¸å»ºè­°æ“·å–螢幕截圖或錄製內容:</translation>
+<translation id="6348220984832452017">使用中的變化版本</translation>
<translation id="6349101878882523185">å®‰è£ <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ç„¡}=1{有 1 個密碼 (<ph name="DOMAIN_LIST" />,已åŒæ­¥)}=2{有 2 個密碼 (<ph name="DOMAIN_LIST" />,已åŒæ­¥)}other{有 # 個密碼 (<ph name="DOMAIN_LIST" />,已åŒæ­¥)}}</translation>
<translation id="6355392890578844978">æ­¤ç€è¦½å™¨æœªå—任何公å¸æˆ–其他機構管ç†ã€‚æ­¤è£ç½®ä¸Šçš„活動å¯é€éŽ Chromium 以外的æœå‹™ç®¡ç†ã€‚<ph name="BEGIN_LINK" />瞭解詳情<ph name="END_LINK" /></translation>
@@ -1965,7 +1978,6 @@
<translation id="643051589346665201">變更 Google 密碼</translation>
<translation id="6433490469411711332">編輯è¯çµ¡äººè³‡æ–™</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> 已拒絕連線。</translation>
-<translation id="6438025220577812695">我會自行變更密碼</translation>
<translation id="6440503408713884761">已略éŽ</translation>
<translation id="6443406338865242315">您已安è£çš„擴充程å¼å’Œå¤–掛程å¼</translation>
<translation id="6446163441502663861">Kahu (ä¿¡å°)</translation>
@@ -2095,9 +2107,9 @@
<translation id="6828866289116430505">éºå‚³å­¸</translation>
<translation id="6831043979455480757">翻譯</translation>
<translation id="6833752742582340615">將信用å¡å’Œå¸³å–®è³‡æ–™å„²å­˜è‡³ Google 帳戶,享å—更安全快速的çµå¸³ç¨‹åº</translation>
-<translation id="6839929833149231406">地å€</translation>
<translation id="6846340164947227603">使用虛擬付款å¡è™Ÿç¢¼â€¦</translation>
<translation id="6852204201400771460">è¦é‡æ–°è¼‰å…¥æ‡‰ç”¨ç¨‹å¼å—Žï¼Ÿ</translation>
+<translation id="6857776781123259569">管ç†å¯†ç¢¼â€¦</translation>
<translation id="686485648936420384">消費者資æº</translation>
<translation id="6865412394715372076">ç›®å‰ç„¡æ³•é©—證此信用å¡</translation>
<translation id="6869334554832814367">ç§äººè²¸æ¬¾</translation>
@@ -2146,7 +2158,6 @@
<translation id="6965978654500191972">è£ç½®</translation>
<translation id="696703987787944103">感知</translation>
<translation id="6968269510885595029">使用您的安全密鑰</translation>
-<translation id="6970216967273061347">å€</translation>
<translation id="6971439137020188025">在「簡報ã€ä¸­å¿«é€Ÿå»ºç«‹æ–°çš„ Google ç°¡å ±</translation>
<translation id="6972629891077993081">HID è£ç½®</translation>
<translation id="6973656660372572881">已指定固定的 Proxy 伺æœå™¨å’Œ .pac 指令碼網å€ã€‚</translation>
@@ -2185,7 +2196,6 @@
<translation id="7081308185095828845">您的è£ç½®ä¸æ”¯æ´æ­¤åŠŸèƒ½</translation>
<translation id="7083258188081898530">紙匣 9</translation>
<translation id="7086090958708083563">使用者已è¦æ±‚上載</translation>
-<translation id="7087282848513945231">郡</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />,㩒一下 Tab éµï¼Œç„¶å¾Œã©’一下 Enter éµå°±å¯ä»¥å–º Chrome 設定度管ç†æ‰€æœ‰ç¶²ç«™å˜…權é™åŒå„²å­˜å˜…資料</translation>
<translation id="7096937462164235847">這個網站的身分未經驗證。</translation>
<translation id="7101893872976785596">æ怖電影</translation>
@@ -2204,10 +2214,10 @@
<ph name="LIST_ITEM" />在表格中輸入的資料<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">無法é‹é€è‡³æ­¤åœ°å€ï¼Œè«‹é¸å–其他地å€ã€‚</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /><ph name="DETAILS" /></translation>
<translation id="7132939140423847331">管ç†å“¡å·²ç¦æ­¢è¤‡è£½æ­¤è³‡æ–™ã€‚</translation>
<translation id="7135130955892390533">顯示狀態</translation>
<translation id="7138472120740807366">é€è²¨æ–¹å¼</translation>
-<translation id="7139724024395191329">酋長國</translation>
<translation id="7139892792842608322">主è¦ç´™åŒ£</translation>
<translation id="714064300541049402">å´é‚Š 2 圖片 X 軸移動</translation>
<translation id="7152423860607593928">Number-14 (ä¿¡å°)</translation>
@@ -2424,6 +2434,7 @@
<translation id="7669271284792375604">攻擊者å¯èƒ½æœƒç¶“由此網站,嘗試誘使您安è£å¦¨ç¤™ç€è¦½é«”é©—çš„ç¨‹å¼ (例如變更您的首é ï¼Œæˆ–在您ç€è¦½çš„網站上顯示多餘的廣告)。</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{在標示為機密的資料所執行的動作 (自登入起已報告 1 個動作)。<ph name="BEGIN_LINK" />瞭解詳情<ph name="END_LINK" />}other{在標示為機密的資料所執行的動作 (自登入起已報告 # 個動作)。<ph name="BEGIN_LINK" />瞭解詳情<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">出紙槽 6</translation>
+<translation id="7675325315208090829">管ç†ä»˜æ¬¾æ–¹æ³•â€¦</translation>
<translation id="7676643023259824263">æµå‰ªè²¼ç°¿æ–‡å­—:<ph name="TEXT" /></translation>
<translation id="7679367271685653708">在 Chrome 設定中查看並管ç†æ‚¨çš„ç€è¦½è¨˜éŒ„</translation>
<translation id="7679947978757153706">棒çƒ</translation>
@@ -2466,7 +2477,6 @@
<translation id="7766518757692125295">é å°</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">相åŒé †åº (æ­£é¢æœä¸Š)</translation>
-<translation id="777702478322588152">縣</translation>
<translation id="7791011319128895129">未發佈</translation>
<translation id="7791196057686275387">綑ç¶å¼é‡˜è£</translation>
<translation id="7791543448312431591">新增</translation>
@@ -2557,12 +2567,12 @@
<translation id="8055534648776115597">è·æ¥­å’ŒæŒçºŒæ•™è‚²</translation>
<translation id="8057711352706143257">無法正確安è£ã€Œ<ph name="SOFTWARE_NAME" />ã€ï¼Œè§£é™¤å®‰è£ã€Œ<ph name="SOFTWARE_NAME" />ã€é€šå¸¸èƒ½ä¿®æ­£ç›¸é—œå•é¡Œã€‚<ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">食å“生產</translation>
-<translation id="8066955247577885446">很抱歉,系統發生錯誤。</translation>
<translation id="8067872629359326442">您剛æ‰åœ¨æ¬ºè©ç¶²ç«™ä¸Šè¼¸å…¥äº†å¯†ç¢¼ã€‚Chromium å¯ä»¥å°±æ­¤æä¾›å”助。如需變更密碼或通知 Google 您的帳戶å¯èƒ½é¢è‡¨é¢¨éšªï¼Œè«‹æŒ‰ [ä¿è­·å¸³æˆ¶]。</translation>
<translation id="8070439594494267500">應用程å¼åœ–示</translation>
<translation id="8074253406171541171">10x13 (ä¿¡å°)</translation>
<translation id="8075736640322370409">快速建立新的 Google 試算表</translation>
<translation id="8075898834294118863">管ç†ç¶²ç«™è¨­å®š</translation>
+<translation id="8076492880354921740">分é </translation>
<translation id="8078141288243656252">文件旋轉後無法加入註釋</translation>
<translation id="8079031581361219619">è¦é‡æ–°è¼‰å…¥ç¶²ç«™å—Žï¼Ÿ</translation>
<translation id="8081087320434522107">轎車</translation>
@@ -2685,6 +2695,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">設定</translation>
+<translation id="8428634594422941299">知é“了</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />,㩒一下 Tab éµï¼Œç„¶å¾Œã©’一下 Enter éµå°±å¯ä»¥å–º Chrome è¨­å®šåº¦ç®¡ç† Cookie å好設定</translation>
<translation id="8433057134996913067">您會因此登出大多數網站。</translation>
<translation id="8434840396568290395">寵物</translation>
@@ -2782,6 +2793,7 @@
<translation id="8742371904523228557">您的 <ph name="ORIGIN" /> 驗證碼是 <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">將此分é åŠ å…¥æ›¸ç±¤</translation>
<translation id="8751426954251315517">è«‹ç¨å€™å†è©¦</translation>
+<translation id="8757526089434340176">æä¾› Google Pay 優惠</translation>
<translation id="8758885506338294482">é›»å­ç«¶æŠ€</translation>
<translation id="8759274551635299824">此信用å¡å·²éŽæœŸ</translation>
<translation id="87601671197631245">此網站的安全性設定éŽèˆŠï¼Œå› æ­¤æ‚¨å‚³é€çµ¦æ­¤ç¶²ç«™çš„資料 (例如密碼ã€è¨Šæ¯æˆ–信用å¡è³‡æ–™) å¯èƒ½æœƒå¤–洩。</translation>
@@ -2789,6 +2801,7 @@
<translation id="8763927697961133303">USB è£ç½®</translation>
<translation id="8763986294015493060">關閉目å‰é–‹å•Ÿçš„所有無痕å¼è¦–窗</translation>
<translation id="8766943070169463815">開咗安全付款憑證驗證工作表</translation>
+<translation id="8767765348545497220">閂說明å°è©±æ°£æ³¡</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">電單車</translation>
<translation id="8790007591277257123">é‡åšåˆªé™¤(&amp;R)</translation>
@@ -2801,6 +2814,7 @@
<translation id="8806285662264631610">æ²æµ´å’Œèº«é«”è­·ç†ç”¢å“</translation>
<translation id="8807160976559152894">完æˆæ¯é å¾Œä¿®å‰ª</translation>
<translation id="8808828119384186784">Chrome 設定</translation>
+<translation id="8813277370772331957">ç¨å¾Œæ醒我</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />,㩒一下 Tab éµï¼Œç„¶å¾Œã©’一下 Enter éµå°±å¯ä»¥é€éŽ Chrome 設定更新 Chrome</translation>
<translation id="8820817407110198400">書籤</translation>
<translation id="882338992931677877">手動æ’槽</translation>
@@ -2980,6 +2994,7 @@
<translation id="988159990683914416">開發人員版本</translation>
<translation id="989988560359834682">編輯地å€</translation>
<translation id="991413375315957741">動態或光線感應器</translation>
+<translation id="992110854164447044">虛擬å¡æœƒéš±è—您的實體å¡ï¼Œä¿è­·æ‚¨å…å—潛在欺è©è¡Œç‚ºçš„å¨è„…。<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">粉紅色</translation>
<translation id="992432478773561401">無法在您的電腦或網絡上正確安è£ã€Œ<ph name="SOFTWARE_NAME" />ã€ï¼š
diff --git a/chromium/components/strings/components_strings_zh-TW.xtb b/chromium/components/strings/components_strings_zh-TW.xtb
index 1f5509976d4..f39e64b2d04 100644
--- a/chromium/components/strings/components_strings_zh-TW.xtb
+++ b/chromium/components/strings/components_strings_zh-TW.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">補助金ã€çŽå­¸é‡‘與財務補助</translation>
<translation id="1048785276086539861">這份文件會在你編輯註解時é‡è¨­ç‚ºå–®é æª¢è¦–</translation>
<translation id="1050038467049342496">關閉其他應用程å¼</translation>
+<translation id="1053959602163383901">ä½ å·²é¸æ“‡ä½¿ç”¨é©—證器è£ç½®åœ¨ä½¿ç”¨ <ph name="PROVIDER_ORIGIN" /> 的網站進行驗證。這個供應商å¯èƒ½å·²å„²å­˜ä½ çš„付款方å¼ç›¸é—œè³‡è¨Šï¼Œä½ å¯ä»¥ <ph name="LINK_TEXT" />。</translation>
<translation id="1055184225775184556">復原新增(&amp;U)</translation>
<translation id="1056663316309890343">相片軟體</translation>
<translation id="1056898198331236512">警告</translation>
@@ -119,6 +120,7 @@
<translation id="1270502636509132238">å–件方å¼</translation>
<translation id="1281476433249504884">堆疊出紙器 1</translation>
<translation id="1285320974508926690">一律ä¸ç¿»è­¯æ­¤ç¶²ç«™</translation>
+<translation id="1288548991597756084">安全地儲存å¡ç‰‡</translation>
<translation id="1292571435393770077">紙匣 16</translation>
<translation id="1292701964462482250">「你的電腦上有軟體阻止 Chrome 建立安全的網路連線ã€(åƒ…é™ Windows 電腦)</translation>
<translation id="1294154142200295408">命令列變化版本</translation>
@@ -223,6 +225,7 @@
&lt;p&gt;如è¦ä¿®æ­£é€™å€‹éŒ¯èª¤ï¼Œè«‹åœ¨æ‚¨è¦å˜—試開啟的網é ä¸ŠæŒ‰ä¸€ä¸‹ [連線]&lt;strong&gt;&lt;/strong&gt;。&lt;/p&gt;</translation>
<translation id="1507780850870535225">景觀設計</translation>
<translation id="1513706915089223971">æ­·å²è¨˜éŒ„項目清單</translation>
+<translation id="1516097932025103760">系統會將å¡ç‰‡åŠ å¯†ä¸¦ä»¥å®‰å…¨çš„æ–¹å¼å„²å­˜ï¼Œä½†çµ•ä¸æœƒå„²å­˜ä¿¡ç”¨å¡é©—證碼。</translation>
<translation id="1517433312004943670">必須輸入電話號碼</translation>
<translation id="1519264250979466059">建立日期</translation>
<translation id="1521159554480556801">纖維與紡織è—è¡“</translation>
@@ -288,6 +291,7 @@
<translation id="1662550410081243962">儲存åŠå¡«å…¥ä»˜æ¬¾æ–¹å¼</translation>
<translation id="1663943134801823270">信用å¡å’Œåœ°å€è³‡è¨Šçš†ä¾†è‡ª Chrome。你å¯ä»¥åœ¨<ph name="BEGIN_LINK" />設定<ph name="END_LINK" />é é¢ç®¡ç†é€™äº›è³‡è¨Šã€‚</translation>
<translation id="1671391448414634642">從ç¾åœ¨èµ·ï¼Œç³»çµ±æœƒå°‡<ph name="SOURCE_LANGUAGE" />網é ç¿»è­¯æˆ<ph name="TARGET_LANGUAGE" />。</translation>
+<translation id="1673886523110456987">使用 <ph name="CARD_DETAIL" /> çµå¸³å³å¯äº«æœ‰å„ªæƒ </translation>
<translation id="1674504678466460478">從<ph name="SOURCE_LANGUAGE" />翻譯為<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">先從短邊開始</translation>
<translation id="168693727862418163">這項政策的值未通éŽå…¶çµæ§‹å®šç¾©çš„驗證,因此會é­åˆ°å¿½ç•¥ã€‚</translation>
@@ -306,6 +310,7 @@
<translation id="1717218214683051432">動作感應器</translation>
<translation id="1717494416764505390">出紙槽 3</translation>
<translation id="1718029547804390981">文件éŽå¤§ï¼Œç„¡æ³•åŠ è¨»</translation>
+<translation id="1720941539803966190">關閉教學課程</translation>
<translation id="1721424275792716183">* 這是必填欄ä½</translation>
<translation id="1727613060316725209">憑證有效</translation>
<translation id="1727741090716970331">新增有效的信用å¡è™Ÿç¢¼</translation>
@@ -418,8 +423,8 @@
<translation id="205212645995975601">烤肉與燒烤</translation>
<translation id="2053111141626950936">系統ä¸æœƒç¿»è­¯<ph name="LANGUAGE" />網é ã€‚</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{如果這項功能已啟用且處於有效狀態,Chrome 就能判斷你近期的ç€è¦½æ´»å‹•èˆ‡å“ªå€‹å¤§åž‹ä½¿ç”¨è€…群組 (或稱「åŒé¡žç¾¤çµ„ã€) 最為相近。廣告客戶å¯ä»¥é¸æ“‡è¦å‘該群組顯示的廣告,而你的ç€è¦½è¨˜éŒ„會ä¿ç•™åœ¨è£ç½®ä¸Šï¼Œåƒ…供本人存å–。你所在的群組會æ¯å¤©æ›´æ–°ã€‚}=1{如果這項功能已啟用且處於有效狀態,Chrome 就能判斷你近期的ç€è¦½æ´»å‹•èˆ‡å“ªå€‹å¤§åž‹ä½¿ç”¨è€…群組 (或稱「åŒé¡žç¾¤çµ„ã€) 最為相近。廣告客戶å¯ä»¥é¸æ“‡è¦å‘該群組顯示的廣告,而你的ç€è¦½è¨˜éŒ„會ä¿ç•™åœ¨è£ç½®ä¸Šï¼Œåƒ…供本人存å–。你所在的群組會æ¯å¤©æ›´æ–°ã€‚}other{如果這項功能已啟用且處於有效狀態,Chrome 就能判斷你近期的ç€è¦½æ´»å‹•èˆ‡å“ªå€‹å¤§åž‹ä½¿ç”¨è€…群組 (或稱「åŒé¡žç¾¤çµ„ã€) 最為相近。廣告客戶å¯ä»¥é¸æ“‡è¦å‘該群組顯示的廣告,而你的ç€è¦½è¨˜éŒ„會ä¿ç•™åœ¨è£ç½®ä¸Šï¼Œåƒ…供本人存å–。你所在的群組會æ¯éš” {NUM_DAYS} 天更新。}}</translation>
-<translation id="2053553514270667976">郵éžå€è™Ÿ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 個建議項目}other{# 個建議項目}}</translation>
+<translation id="2066915425250589881">è¦æ±‚刪除已儲存的資料</translation>
<translation id="2068528718802935086">嬰幼兒</translation>
<translation id="2071156619270205202">這張å¡ç‰‡ä¸èƒ½å»ºç«‹è™›æ“¬å¡è™Ÿã€‚</translation>
<translation id="2071692954027939183">系統已按照你的習慣自動å°éŽ–通知</translation>
@@ -431,7 +436,6 @@
<translation id="2088086323192747268">「管ç†åŒæ­¥åŠŸèƒ½ã€æŒ‰éˆ•ï¼›æŒ‰ä¸‹ Enter éµå³å¯åœ¨ Chrome 設定中管ç†è¦åŒæ­¥è™•ç†å“ªäº›è³‡è¨Š</translation>
<translation id="2091887806945687916">音訊</translation>
<translation id="2094505752054353250">網域ä¸ç¬¦</translation>
-<translation id="2096368010154057602">部門</translation>
<translation id="2099652385553570808">三釘 (å·¦å´)</translation>
<translation id="2101225219012730419">版本:</translation>
<translation id="2102134110707549001">建議高強度密碼…</translation>
@@ -468,7 +472,6 @@
<translation id="2185836064961771414">美å¼è¶³çƒ</translation>
<translation id="2187317261103489799">åµæ¸¬ (é è¨­)</translation>
<translation id="2188375229972301266">多孔 (底部)</translation>
-<translation id="2188852899391513400">系統發ç¾ä½ å‰›æ‰ä½¿ç”¨çš„密碼因為資料侵害事件而é­åˆ°å¤–洩。為確ä¿å¸³æˆ¶å®‰å…¨ï¼ŒGoogle 密碼管ç†å“¡å»ºè­°ä½ ç«‹å³è®Šæ›´é€™çµ„密碼,然後檢查已儲存的密碼。</translation>
<translation id="219906046732893612">居家修繕</translation>
<translation id="2202020181578195191">請輸入有效的到期年份</translation>
<translation id="22081806969704220">紙匣 3</translation>
@@ -479,6 +482,8 @@
<translation id="2215727959747642672">檔案編輯</translation>
<translation id="2215963164070968490">ç‹—</translation>
<translation id="2218879909401188352">ç›®å‰åœ¨ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 上的攻擊者å¯èƒ½æœƒè®“你安è£ä¸å®‰å…¨çš„應用程å¼ï¼Œå°Žè‡´è£ç½®å—æã€æ‰‹æ©Ÿå¸³å–®ä¸­å¤šå‡ºéš±è—費用,或是個人資訊é­ç«Šã€‚<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">é‡æ–°å•Ÿå‹•æ•™å­¸èª²ç¨‹</translation>
+<translation id="2219735899272417925">å¿…é ˆé‡è¨­è£ç½®</translation>
<translation id="2224337661447660594">沒有網際網路連線</translation>
<translation id="2230458221926704099">請使用<ph name="BEGIN_LINK" />診斷應用程å¼<ph name="END_LINK" />修正連線å•é¡Œ</translation>
<translation id="2239100178324503013">ç«‹å³å‚³é€</translation>
@@ -576,11 +581,13 @@
<translation id="2512101340618156538">ä¸å…許 (é è¨­)</translation>
<translation id="2512413427717747692">å°‡ Chrome 設為é è¨­ç€è¦½å™¨çš„按鈕,按下 Enter éµå³å¯åœ¨ iOS 設定中將 Chrome 設為系統的é è¨­ç€è¦½å™¨</translation>
<translation id="2515629240566999685">檢查所在ä½ç½®çš„網路訊號</translation>
+<translation id="2515761554693942801">ä½ å·²é¸æ“‡ä½¿ç”¨ Touch ID 在使用 <ph name="PROVIDER_ORIGIN" /> 的網站上進行驗證。這個供應商å¯èƒ½å·²å„²å­˜ä½ çš„付款方å¼ç›¸é—œè³‡è¨Šï¼Œä½ å¯ä»¥ <ph name="LINK_TEXT" />。</translation>
<translation id="2521385132275182522">é‡˜è£ (å³ä¸‹æ–¹)</translation>
<translation id="2521736961081452453">建立表單</translation>
<translation id="2523886232349826891">僅儲存在這部è£ç½®</translation>
<translation id="2524461107774643265">新增詳細資訊</translation>
<translation id="2529899080962247600">這個欄ä½æœ€å¤šåªèƒ½åŒ…å« <ph name="MAX_ITEMS_LIMIT" /> 個項目,超éŽçš„項目會é­åˆ°å¿½ç•¥ã€‚</translation>
+<translation id="253493526287553278">查看促銷代碼詳情</translation>
<translation id="2535585790302968248">開啟新的無痕分é ä»¥é€²è¡Œç§å¯†ç€è¦½</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{å’Œå¦å¤– 1 個網域}other{å’Œå¦å¤– # 個網域}}</translation>
<translation id="2536110899380797252">新增地å€</translation>
@@ -616,7 +623,6 @@
<translation id="259821504105826686">æ”影與數ä½è—è¡“</translation>
<translation id="2601150049980261779">浪漫愛情片</translation>
<translation id="2604589665489080024">æµè¡Œæ¨‚</translation>
-<translation id="2609632851001447353">變化版本</translation>
<translation id="2610561535971892504">點é¸è¤‡è£½</translation>
<translation id="2617988307566202237">Chrome <ph name="BEGIN_EMPHASIS" />ä¸æœƒå„²å­˜<ph name="END_EMPHASIS" />下列資訊:
<ph name="BEGIN_LIST" />
@@ -649,6 +655,7 @@
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">生日與命åæ—¥</translation>
<translation id="2677748264148917807">離開</translation>
+<translation id="2679714844901977852">å°‡å¡ç‰‡å’Œå¸³å–®è³‡è¨Šå„²å­˜åœ¨ä½ çš„ Google 帳戶 <ph name="USER_EMAIL" /> 中,享å—更安全便æ·çš„çµå¸³ç¨‹åº</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">最é©é…ç½®</translation>
<translation id="2688969097326701645">是,請繼續</translation>
@@ -697,7 +704,6 @@
<translation id="2854764410992194509">網際網路æœå‹™ä¾›æ‡‰å•† (ISP)</translation>
<translation id="2856444702002559011">攻擊者å¯èƒ½æœƒè©¦åœ–從 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ç«Šå–你的資訊 (例如密碼ã€éƒµä»¶æˆ–信用å¡è³‡æ–™)。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">這個網站會顯示干擾性或誤導性的廣告。</translation>
-<translation id="286512204874376891">虛擬å¡ç‰‡æœƒå½è£æˆå¯¦é«”å¡ç‰‡ï¼Œä¿è­·ä½ å…å—潛在è©æ¬ºäº‹ä»¶çš„侵擾。<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">å‹å–„</translation>
<translation id="28761159517501904">電影</translation>
<translation id="2876489322757410363">å³å°‡é€€å‡ºç„¡ç—•æ¨¡å¼ï¼Œæ”¹ç‚ºä½¿ç”¨å¤–部應用程å¼ä»˜æ¬¾ï¼Œè¦ç¹¼çºŒå—Žï¼Ÿ</translation>
@@ -798,7 +804,6 @@
<translation id="3158539265159265653">光碟</translation>
<translation id="3162559335345991374">ç›®å‰ä½¿ç”¨çš„ Wi-Fi 網路å¯èƒ½æœƒè¦æ±‚您造訪登入網é ã€‚</translation>
<translation id="3169472444629675720">Discover</translation>
-<translation id="3174168572213147020">島</translation>
<translation id="3176929007561373547">檢查您的 Proxy 設定,或是與您的網路管ç†å“¡è¯çµ¡
確èªæ‚¨çš„ Proxy 伺æœå™¨é‹ä½œæ­£å¸¸ã€‚如果您èªç‚ºè‡ªå·±ä¸éœ€è¦ä½¿ç”¨
Proxy 伺æœå™¨ï¼š
@@ -877,9 +882,6 @@
<translation id="3369192424181595722">時é˜éŒ¯èª¤</translation>
<translation id="3369459162151165748">交通工具零é…件</translation>
<translation id="3371064404604898522">å°‡ Chrome 設為é è¨­ç€è¦½å™¨</translation>
-<translation id="3371076217486966826"><ph name="URL" /> è¦æ±‚:
- • 根據你的周é­ç’°å¢ƒå»ºç«‹ 3D 地圖並追蹤æ”影機ä½ç½®
- • 使用è£ç½®çš„æ”影機</translation>
<translation id="337363190475750230">å·²å–消佈建</translation>
<translation id="3375754925484257129">執行 Chrome 安全檢查</translation>
<translation id="3377144306166885718">伺æœå™¨çš„傳輸層安全標準 (TLS) 版本已éŽæ™‚。</translation>
@@ -896,6 +898,7 @@
<translation id="3399952811970034796">å¿«éžåœ°å€</translation>
<translation id="3402261774528610252">用來載入這個網站的連線使用傳輸層安全標準 (TLS) 1.0 或 1.1,ç¾å·²ä¸é©ç”¨ï¼Œä¸¦å°‡åœ¨æ—¥å¾Œé­åˆ°åœç”¨ã€‚一旦åœç”¨ï¼Œä½¿ç”¨è€…將無法載入這個網站。伺æœå™¨æ‡‰å•Ÿç”¨å‚³è¼¸å±¤å®‰å…¨æ¨™æº– (TLS) 1.2 以上版本。</translation>
<translation id="3405664148539009465">自訂字型</translation>
+<translation id="3407789382767355356">第三方登入</translation>
<translation id="3409896703495473338">管ç†å®‰å…¨æ€§è¨­å®š</translation>
<translation id="3414952576877147120">空間大å°ï¼š</translation>
<translation id="3417660076059365994">系統會將你上傳或附加的檔案傳é€åˆ° Google Cloud 或第三方進行分æžã€‚舉例來說,Google Cloud 或第三方å¯èƒ½æœƒæŽƒæ檔案,檢查是å¦å«æœ‰æ©Ÿå¯†è³‡æ–™æˆ–惡æ„軟體。</translation>
@@ -928,6 +931,7 @@
<translation id="3477679029130949506">電影場次與放映時間</translation>
<translation id="3479552764303398839">ç¾åœ¨ä¸è¦</translation>
<translation id="3484560055331845446">為é¿å…失去 Google 帳戶存å–權,Chrome 建議你立å³è®Šæ›´å¯†ç¢¼ã€‚系統會è¦æ±‚你登入帳戶。</translation>
+<translation id="3484861421501147767">æ醒:å¯ä½¿ç”¨å·²å„²å­˜çš„促銷代碼</translation>
<translation id="3487845404393360112">紙匣 4</translation>
<translation id="3495081129428749620">在
「<ph name="PAGE_TITLE" />ã€ç¶²é ä¸­å°‹æ‰¾</translation>
@@ -1052,6 +1056,7 @@
<translation id="3810973564298564668">管ç†</translation>
<translation id="3816482573645936981">值 (已被å–代)</translation>
<translation id="382518646247711829">如果你使用 Proxy 伺æœå™¨...</translation>
+<translation id="3826050100957962900">第三方登入</translation>
<translation id="3827112369919217609">絕å°</translation>
<translation id="3827666161959873541">家庭電影</translation>
<translation id="3828924085048779000">通關密語欄ä½ä¸å¾—留空。</translation>
@@ -1064,9 +1069,9 @@
<translation id="3858027520442213535">更新日期和時間</translation>
<translation id="3858860766373142691">å稱</translation>
<translation id="3872834068356954457">科學</translation>
+<translation id="3875783148670536197">顯示æ“作說明</translation>
<translation id="3881478300875776315">顯示較少行</translation>
<translation id="3884278016824448484">è£ç½® ID 發生è¡çª</translation>
-<translation id="3885155851504623709">æ•™å€</translation>
<translation id="388632593194507180">åµæ¸¬åˆ°ä½ æ­£é­åˆ°ç›£æŽ§</translation>
<translation id="3886948180919384617">堆疊出紙器 3</translation>
<translation id="3890664840433101773">新增電å­éƒµä»¶åœ°å€</translation>
@@ -1096,9 +1101,11 @@
<translation id="3973234410852337861"><ph name="HOST_NAME" /> é­åˆ°å°éŽ–</translation>
<translation id="3978338123949022456">æœå°‹æ¨¡å¼ï¼Œè¼¸å…¥æŸ¥è©¢ä¸¦æŒ‰ä¸‹ Enter éµå³å¯ç”¨ <ph name="KEYWORD_SUFFIX" /> æœå°‹</translation>
<translation id="398470910934384994">é³¥</translation>
+<translation id="3985750352229496475">管ç†åœ°å€...</translation>
<translation id="3986705137476756801">暫時關閉å³æ™‚字幕</translation>
<translation id="3987940399970879459">ä¸åˆ° 1 MB</translation>
<translation id="3990250421422698716">æ’žé å移</translation>
+<translation id="3992684624889376114">為何顯示此é </translation>
<translation id="3996311196211510766">「<ph name="ORIGIN" />ã€ç¶²ç«™æŒ‡å®šæ‰€æœ‰æ”¶åˆ°çš„è¦æ±‚都必須套用來æºæ”¿ç­–,但目å‰ç„¡æ³•å¥—用此政策。</translation>
<translation id="4006465311664329701">儲存在 Google Pay 的付款方å¼ã€å„ªæƒ å’Œåœ°å€è³‡è¨Š</translation>
<translation id="4009243425692662128">你列å°çš„é é¢å…§å®¹æœƒå‚³é€çµ¦ Google Cloud 或第三方進行分æžã€‚舉例來說,Google Cloud 或第三方å¯èƒ½æœƒæŽƒæ文字,檢查是å¦å«æœ‰æ©Ÿå¯†è³‡æ–™ã€‚</translation>
@@ -1218,6 +1225,7 @@
<translation id="4305666528087210886">無法存å–你的檔案</translation>
<translation id="4306529830550717874">è¦å„²å­˜åœ°å€å—Žï¼Ÿ</translation>
<translation id="4306812610847412719">剪貼簿</translation>
+<translation id="4310070645992025887">æœå°‹ç€è¦½æ­·ç¨‹</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">å°éŽ– (é è¨­)</translation>
<translation id="4314815835985389558">管ç†åŒæ­¥è³‡æ–™</translation>
@@ -1268,6 +1276,7 @@
<translation id="4435702339979719576">明信片)</translation>
<translation id="443673843213245140">雖然已åœç”¨ Proxy,ä¸éŽå·²æŒ‡å®šæ˜Žç¢º Proxy 設定。</translation>
<translation id="4441832193888514600">由於這項政策åªèƒ½è¨­ç‚ºé›²ç«¯ä½¿ç”¨è€…政策,因此系統予以忽略。</translation>
+<translation id="4442470707340296952">Chrome 分é </translation>
<translation id="4450893287417543264">ä¸è¦å†é¡¯ç¤º</translation>
<translation id="4451135742916150903">å¯ä»¥è¦æ±‚連線至 HID è£ç½®</translation>
<translation id="4452328064229197696">系統發ç¾ä½ å‰›æ‰ä½¿ç”¨çš„密碼因為資料侵害事件而é­åˆ°å¤–洩。為確ä¿å¸³æˆ¶å®‰å…¨ï¼ŒGoogle 密碼管ç†å“¡å»ºè­°ä½ ç«‹å³æª¢æŸ¥å·²å„²å­˜çš„密碼。</translation>
@@ -1406,6 +1415,7 @@
<translation id="483241715238664915">開啟警告</translation>
<translation id="4834250788637067901">儲存在 Google Pay 的付款方å¼ã€å„ªæƒ å’Œåœ°å€è³‡è¨Š</translation>
<translation id="4838327282952368871">夢幻</translation>
+<translation id="4839087176073128681">é€éŽ Google 領先業界的安全防護機制,加快下次çµå¸³é€Ÿåº¦ä¸¦ä¿è­·ä½ çš„å¡ç‰‡ã€‚</translation>
<translation id="4840250757394056958">查看 Chrome æ­·å²è¨˜éŒ„</translation>
<translation id="484462545196658690">自動</translation>
<translation id="484671803914931257">å–å¾— <ph name="MERCHANT_NAME" /> 和其他商家的折扣</translation>
@@ -1468,6 +1478,7 @@
<translation id="5011561501798487822">åµæ¸¬åˆ°çš„語言</translation>
<translation id="5015510746216210676">è£ç½®å稱:</translation>
<translation id="5017554619425969104">你複製的文字</translation>
+<translation id="5017828934289857214">ç¨å¾Œæ醒我</translation>
<translation id="5018422839182700155">無法開啟這個網é </translation>
<translation id="5019198164206649151">備份儲存狀態ä¸ä½³</translation>
<translation id="5020776957610079374">世界音樂</translation>
@@ -1487,6 +1498,7 @@
<translation id="5051305769747448211">實æ³å–œåŠ‡</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{如è¦ä½¿ç”¨é„°è¿‘分享功能傳é€é€™å€‹æª”案,請釋出è£ç½®çš„儲存空間 (<ph name="DISK_SPACE_SIZE" />)}other{如è¦ä½¿ç”¨é„°è¿‘分享功能傳é€é€™äº›æª”案,請釋出è£ç½®çš„儲存空間 (<ph name="DISK_SPACE_SIZE" />)}}</translation>
<translation id="5056549851600133418">為你推薦的文章</translation>
+<translation id="5060483733937416656">ä½ å·²é¸æ“‡ä½¿ç”¨ Windows Hello 在使用 <ph name="PROVIDER_ORIGIN" /> 的網站上進行驗證。這個供應商å¯èƒ½å·²å„²å­˜ä½ çš„付款方å¼ç›¸é—œè³‡è¨Šï¼Œä½ å¯ä»¥ <ph name="LINK_TEXT" />。</translation>
<translation id="5061227663725596739">你是ä¸æ˜¯è¦å‰å¾€ <ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">列å°è¨˜éŒ„</translation>
<translation id="5068234115460527047">é¿éšªåŸºé‡‘</translation>
@@ -1500,10 +1512,8 @@
<translation id="5087286274860437796">伺æœå™¨æ†‘證目å‰ç„¡æ•ˆã€‚</translation>
<translation id="5087580092889165836">新增信用å¡</translation>
<translation id="5088142053160410913">給æ“作員的訊æ¯</translation>
-<translation id="5089810972385038852">å·ž</translation>
<translation id="5093232627742069661">彈簧二摺</translation>
<translation id="5094747076828555589">伺æœå™¨ç„¡æ³•è­‰æ˜Žå…¶å±¬æ–¼ <ph name="DOMAIN" /> 網域;其安全性憑證未å–å¾— Chromium 的信任。這å¯èƒ½æ˜¯å› ç‚ºè¨­å®šéŒ¯èª¤ï¼Œæˆ–有攻擊者攔截你的連線所致。</translation>
-<translation id="5095208057601539847">å·ž/çœ</translation>
<translation id="5097099694988056070">CPU/RAM 使用é‡ç­‰è£ç½®çµ±è¨ˆè³‡æ–™</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">這是ä¸å®‰å…¨çš„網站</translation>
@@ -1541,6 +1551,7 @@
<translation id="5171045022955879922">æœå°‹æˆ–輸入網å€</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">本機</translation>
+<translation id="5177076414499237632">進一步瞭解這個é é¢çš„來æºå’Œä¸»é¡Œ</translation>
<translation id="5179510805599951267">網é å…§å®¹ä¸æ˜¯<ph name="ORIGINAL_LANGUAGE" />嗎?請回報此錯誤</translation>
<translation id="518639307526414276">寵物食å“與用å“</translation>
<translation id="5190835502935405962">書籤列</translation>
@@ -1701,6 +1712,7 @@
<translation id="5624120631404540903">管ç†å¯†ç¢¼</translation>
<translation id="5629630648637658800">無法載入政策設定</translation>
<translation id="5631439013527180824">è£ç½®ç®¡ç†ç¬¦è¨˜ç„¡æ•ˆ</translation>
+<translation id="5632485077360054581">顯示æ“作說明</translation>
<translation id="5633066919399395251">攻擊者目å‰å¯èƒ½æœƒè©¦åœ–é€éŽ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 在你的電腦上安è£å±éšªç¨‹å¼ï¼Œè—‰æ­¤ç«Šå–或刪除你的資訊 (例如相片ã€å¯†ç¢¼ã€éƒµä»¶å’Œä¿¡ç”¨å¡è³‡æ–™)。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">å·²å°éŽ–欺騙性內容。</translation>
<translation id="5633259641094592098">å¦é¡žèˆ‡ç¨ç«‹é›»å½±</translation>
@@ -1818,6 +1830,7 @@
<translation id="5989320800837274978">沒有指定固定的 Proxy 伺æœå™¨å’Œ .pac 指令碼網å€ã€‚</translation>
<translation id="5992691462791905444">大å°å½ˆç°§æ‘º</translation>
<translation id="5995727681868049093">ç®¡ç† Google 帳戶的資訊ã€éš±ç§æ¬Šå’Œå®‰å…¨æ€§</translation>
+<translation id="5997247540087773573">你剛æ‰ä½¿ç”¨çš„密碼已在資料侵害事件中é­åˆ°å¤–洩。為確ä¿å¸³æˆ¶å®‰å…¨ï¼ŒGoogle 密碼管ç†å“¡å»ºè­°ä½ ç«‹å³è®Šæ›´é€™çµ„密碼,並檢查已儲存的密碼。</translation>
<translation id="6000758707621254961">有 <ph name="RESULT_COUNT" /> 個與「<ph name="SEARCH_TEXT" />ã€ç›¸ç¬¦çš„æœå°‹çµæžœ</translation>
<translation id="6006484371116297560">傳統版</translation>
<translation id="6008122969617370890">N 到 1 çš„é †åº</translation>
@@ -1913,7 +1926,6 @@
<translation id="627746635834430766">åªè¦å°‡å¡ç‰‡è³‡è¨Šèˆ‡å¸³å–®åœ°å€å„²å­˜åˆ°ä½ çš„ Google 帳戶中,下次å³å¯æ›´å¿«å®Œæˆä»˜æ¬¾ç¨‹åºã€‚</translation>
<translation id="6279183038361895380">按下 |<ph name="ACCELERATOR" />| å³å¯é¡¯ç¤ºæ¸¸æ¨™</translation>
<translation id="6280223929691119688">å¿«éžç„¡æ³•é€è²¨åˆ°é€™å€‹åœ°å€ï¼Œè«‹æ”¹ç”¨å…¶ä»–地å€ã€‚</translation>
-<translation id="6282194474023008486">郵éžå€è™Ÿ</translation>
<translation id="6285507000506177184">[ç®¡ç† Chrome 下載內容] 按鈕;按下 Enter éµå³å¯ç®¡ç†åœ¨ Chrome 中下載的檔案</translation>
<translation id="6289939620939689042">é é¢é¡è‰²</translation>
<translation id="6290238015253830360">這裡會顯示推薦給你的文章</translation>
@@ -1935,6 +1947,7 @@
<translation id="6337133576188860026">釋出ä¸åˆ° <ph name="SIZE" />。下次造訪部分網站時,載入速度å¯èƒ½æœƒè®Šæ…¢ã€‚</translation>
<translation id="6337534724793800597">ä¾å稱篩é¸æ”¿ç­–</translation>
<translation id="6340739886198108203">根據管ç†å“¡æ”¿ç­–,當畫é¢ä¸Šæœ‰æ©Ÿå¯†å…§å®¹æ™‚,ä¸å»ºè­°ä½ æ“·å–螢幕截圖或錄影:</translation>
+<translation id="6348220984832452017">使用中的變化版本</translation>
<translation id="6349101878882523185">å®‰è£ <ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{ç„¡}=1{有 1 組密碼 (<ph name="DOMAIN_LIST" />,已åŒæ­¥è™•ç†)}=2{有 2 組密碼 (<ph name="DOMAIN_LIST" />,已åŒæ­¥è™•ç†)}other{有 # 組密碼 (<ph name="DOMAIN_LIST" />,已åŒæ­¥è™•ç†)}}</translation>
<translation id="6355392890578844978">這個ç€è¦½å™¨æœªå—到任何公å¸æˆ–其他機構管ç†ã€‚這部è£ç½®ä¸Šçš„活動å¯é€éŽ Chromium 以外的æœå‹™ç®¡ç†ã€‚<ph name="BEGIN_LINK" />瞭解詳情<ph name="END_LINK" /></translation>
@@ -1966,7 +1979,6 @@
<translation id="643051589346665201">變更 Google 密碼</translation>
<translation id="6433490469411711332">編輯è¯çµ¡è³‡è¨Š</translation>
<translation id="6433595998831338502"><ph name="HOST_NAME" /> 拒絕連線。</translation>
-<translation id="6438025220577812695">我自行變更</translation>
<translation id="6440503408713884761">已忽略</translation>
<translation id="6443406338865242315">你安è£çš„擴充功能和外掛程å¼</translation>
<translation id="6446163441502663861">Kahu (ä¿¡å°)</translation>
@@ -2096,9 +2108,9 @@
<translation id="6828866289116430505">éºå‚³å­¸</translation>
<translation id="6831043979455480757">翻譯</translation>
<translation id="6833752742582340615">å°‡å¡ç‰‡å’Œå¸³å–®è³‡è¨Šå„²å­˜åœ¨ä½ çš„ Google 帳戶中,享å—更安全便æ·çš„çµå¸³ç¨‹åº</translation>
-<translation id="6839929833149231406">å€åŸŸ</translation>
<translation id="6846340164947227603">使用虛擬å¡è™Ÿ...</translation>
<translation id="6852204201400771460">è¦é‡æ–°è¼‰å…¥æ‡‰ç”¨ç¨‹å¼å—Žï¼Ÿ</translation>
+<translation id="6857776781123259569">管ç†å¯†ç¢¼...</translation>
<translation id="686485648936420384">消費者資æº</translation>
<translation id="6865412394715372076">ç›®å‰ç„¡æ³•é©—證這張信用å¡</translation>
<translation id="6869334554832814367">個人信貸</translation>
@@ -2147,7 +2159,6 @@
<translation id="6965978654500191972">è£ç½®</translation>
<translation id="696703987787944103">感知</translation>
<translation id="6968269510885595029">使用安全金鑰</translation>
-<translation id="6970216967273061347">District</translation>
<translation id="6971439137020188025">在 Google 簡報中快速建立新簡報</translation>
<translation id="6972629891077993081">HID è£ç½®</translation>
<translation id="6973656660372572881">已指定固定的 Proxy 伺æœå™¨å’Œ .pac 指令碼網å€ã€‚</translation>
@@ -2186,7 +2197,6 @@
<translation id="7081308185095828845">ä½ çš„è£ç½®ä¸æ”¯æ´é€™é …功能</translation>
<translation id="7083258188081898530">紙匣 9</translation>
<translation id="7086090958708083563">使用者è¦æ±‚上傳</translation>
-<translation id="7087282848513945231">郡/縣</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />;按下 Tab éµå†æŒ‰ä¸‹ Enter éµå³å¯åœ¨ Chrome 設定中管ç†å„網站的權é™åŠå„²å­˜çš„資料</translation>
<translation id="7096937462164235847">這個網站的身分未經éŽé©—證。</translation>
<translation id="7101893872976785596">æ怖片</translation>
@@ -2205,10 +2215,10 @@
<ph name="LIST_ITEM" />在表單中輸入的資訊<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">無法é‹é€åˆ°é€™å€‹åœ°å€ï¼Œè«‹æ”¹ç”¨å…¶ä»–地å€ã€‚</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /><ph name="DETAILS" /></translation>
<translation id="7132939140423847331">管ç†å“¡å·²ç¦æ­¢è¤‡è£½é€™å€‹è³‡æ–™ã€‚</translation>
<translation id="7135130955892390533">顯示狀態</translation>
<translation id="7138472120740807366">å¿«éžæ–¹å¼</translation>
-<translation id="7139724024395191329">埃米爾管轄å€</translation>
<translation id="7139892792842608322">主è¦ç´™åŒ£</translation>
<translation id="714064300541049402">å´é‚Š 2 圖片 X 批次</translation>
<translation id="7152423860607593928">Number-14 (Envelope)</translation>
@@ -2425,6 +2435,7 @@
<translation id="7669271284792375604">攻擊者å¯èƒ½æœƒè©¦åœ–é€éŽé€™å€‹ç¶²ç«™èª˜ä½¿ä½ å®‰è£å°ç€è¦½é«”驗有害 (例如變更你的首é ï¼Œæˆ–是在你造訪的網站上顯示多餘的廣告) 的程å¼ã€‚</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{é‡å°æ¨™è¨˜ç‚ºæ©Ÿå¯†çš„資料所執行的動作 (自登入後已回報 1 個動作)。<ph name="BEGIN_LINK" />瞭解詳情<ph name="END_LINK" />}other{é‡å°æ¨™è¨˜ç‚ºæ©Ÿå¯†çš„資料所執行的動作 (自登入後已回報 # 個動作)。<ph name="BEGIN_LINK" />瞭解詳情<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">出紙槽 6</translation>
+<translation id="7675325315208090829">管ç†ä»˜æ¬¾æ–¹å¼...</translation>
<translation id="7676643023259824263">æœå°‹å‰ªè²¼ç°¿æ–‡å­—:<ph name="TEXT" /></translation>
<translation id="7679367271685653708">在 Chrome 設定中查看åŠç®¡ç†ç€è¦½è¨˜éŒ„</translation>
<translation id="7679947978757153706">棒çƒ</translation>
@@ -2467,7 +2478,6 @@
<translation id="7766518757692125295">é æ“ åœˆ</translation>
<translation id="7770259615151589601">Designated-Long</translation>
<translation id="7773005668374414287">相åŒé †åº (æ­£é¢æœä¸Š)</translation>
-<translation id="777702478322588152">縣</translation>
<translation id="7791011319128895129">尚未發布</translation>
<translation id="7791196057686275387">綑ç¶å¼è£è¨‚</translation>
<translation id="7791543448312431591">新增</translation>
@@ -2558,12 +2568,12 @@
<translation id="8055534648776115597">技è·èˆ‡é€²ä¿®æ•™è‚²</translation>
<translation id="8057711352706143257">「<ph name="SOFTWARE_NAME" />ã€çš„設定ä¸æ­£ç¢ºã€‚通常解除安è£ã€Œ<ph name="SOFTWARE_NAME" />ã€å³å¯ä¿®æ­£é€™å€‹å•é¡Œã€‚<ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">食å“生產</translation>
-<translation id="8066955247577885446">抱歉,系統發生錯誤。</translation>
<translation id="8067872629359326442">你剛æ‰åœ¨è©é¨™ç¶²ç«™ä¸Šè¼¸å…¥äº†å¯†ç¢¼ã€‚Chromium 能夠幫助你。如è¦è®Šæ›´å¯†ç¢¼ä¸¦é€šçŸ¥ Google 你的帳戶å¯èƒ½é¢è‡¨é¢¨éšªï¼Œè«‹æŒ‰ä¸€ä¸‹ [ä¿è­·å¸³æˆ¶]。</translation>
<translation id="8070439594494267500">應用程å¼åœ–示</translation>
<translation id="8074253406171541171">10x13 (ä¿¡å°)</translation>
<translation id="8075736640322370409">快速建立新的 Google 試算表</translation>
<translation id="8075898834294118863">管ç†ç¶²ç«™è¨­å®š</translation>
+<translation id="8076492880354921740">分é </translation>
<translation id="8078141288243656252">文件旋轉後無法加註</translation>
<translation id="8079031581361219619">è¦é‡æ–°è¼‰å…¥ç¶²ç«™å—Žï¼Ÿ</translation>
<translation id="8081087320434522107">家用轎車</translation>
@@ -2686,6 +2696,7 @@
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">設定</translation>
+<translation id="8428634594422941299">我知é“了</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />;按下 Tab éµå†æŒ‰ä¸‹ Enter éµå³å¯åœ¨ Chrome è¨­å®šä¸­ç®¡ç† Cookie å好設定</translation>
<translation id="8433057134996913067">您會因此登出大多數網站。</translation>
<translation id="8434840396568290395">寵物</translation>
@@ -2783,6 +2794,7 @@
<translation id="8742371904523228557">你的 <ph name="ORIGIN" /> 驗證碼是 <ph name="ONE_TIME_CODE" /></translation>
<translation id="874918643257405732">將此分é åŠ å…¥æ›¸ç±¤</translation>
<translation id="8751426954251315517">è«‹ç¨å¾Œå†è©¦</translation>
+<translation id="8757526089434340176">æä¾› Google Pay 優惠</translation>
<translation id="8758885506338294482">é›»å­ç«¶æŠ€</translation>
<translation id="8759274551635299824">這張信用å¡å·²éŽæœŸ</translation>
<translation id="87601671197631245">這個網站的安全性設定éŽèˆŠï¼Œå› æ­¤ä½ å‚³é€çµ¦é€™å€‹ç¶²ç«™çš„的資訊 (例如密碼ã€è¨Šæ¯æˆ–信用å¡è³‡è¨Š) å¯èƒ½æœƒå¤–洩。</translation>
@@ -2790,6 +2802,7 @@
<translation id="8763927697961133303">USB è£ç½®</translation>
<translation id="8763986294015493060">關閉目å‰é–‹å•Ÿçš„所有無痕å¼è¦–窗</translation>
<translation id="8766943070169463815">已開啟安全付款憑證驗證表</translation>
+<translation id="8767765348545497220">關閉說明泡泡</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">摩托車</translation>
<translation id="8790007591277257123">é‡åšåˆªé™¤(&amp;R)</translation>
@@ -2802,6 +2815,7 @@
<translation id="8806285662264631610">æ²æµ´èˆ‡èº«é«”è­·ç†ç”¢å“</translation>
<translation id="8807160976559152894">æ¯å®Œæˆä¸€é å¾Œè£åˆ‡</translation>
<translation id="8808828119384186784">Chrome 設定</translation>
+<translation id="8813277370772331957">ç¨å¾Œæ醒我</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />,按下 Tab éµå†æŒ‰ä¸‹ Enter éµå³å¯é€éŽ Chrome 設定來更新 Chrome</translation>
<translation id="8820817407110198400">書籤</translation>
<translation id="882338992931677877">手動æ’槽</translation>
@@ -2981,6 +2995,7 @@
<translation id="988159990683914416">開發人員版本</translation>
<translation id="989988560359834682">編輯地å€</translation>
<translation id="991413375315957741">動作或光æºæ„Ÿæ‡‰å™¨</translation>
+<translation id="992110854164447044">虛擬å¡ç‰‡å¯éš±è—真正的å¡ç‰‡ï¼Œå”助防範潛在è©æ¬ºæ´»å‹•ã€‚<ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">粉紅色</translation>
<translation id="992432478773561401">「<ph name="SOFTWARE_NAME" />ã€æœªæ­£ç¢ºå®‰è£åœ¨é›»è…¦æˆ–網路上:
diff --git a/chromium/components/strings/components_strings_zu.xtb b/chromium/components/strings/components_strings_zu.xtb
index 7070d0c76b7..3fe77b0661e 100644
--- a/chromium/components/strings/components_strings_zu.xtb
+++ b/chromium/components/strings/components_strings_zu.xtb
@@ -27,6 +27,7 @@
<translation id="1043382569739532657">Amagranti, imifundaze nosizo lwezezimali</translation>
<translation id="1048785276086539861">Lapho uhlela izichasiselo, le dokhumenti izobuyela ekuhloleni ikhasi kuqala okukodwa</translation>
<translation id="1050038467049342496">Vala ezinye izinhlelo zokusebenza</translation>
+<translation id="1053959602163383901">Ukhethe ukuqinisekisa ngedivayisi efakazela ubuqiniso kumawebhusayithi asebenzisa i-<ph name="PROVIDER_ORIGIN" />. Lo mhlinzeki kungenzeka ukuthi ulondoloze ulwazi olumayelana nendlela yakho yokukhokha, ongakwazi uku-<ph name="LINK_TEXT" />.</translation>
<translation id="1055184225775184556">&amp;Hlehlisa ukungeza</translation>
<translation id="1056663316309890343">Isofthiwe yesithombe</translation>
<translation id="1056898198331236512">Isexwayiso</translation>
@@ -119,6 +120,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="1270502636509132238">Indlela yokulandwa</translation>
<translation id="1281476433249504884">Isitaki esingu-1</translation>
<translation id="1285320974508926690">Ungalokothi uhumushe leli sayithi</translation>
+<translation id="1288548991597756084">Londoloza ikhadi ngokuvikelekile</translation>
<translation id="1292571435393770077">Ithileyi 16</translation>
<translation id="1292701964462482250">"Isofthiwe kukhompuyutha yakho imisa i-Chrome kusukela ekuxhumekeni ngokuphepha kuwebhu" (Amakhompyutha e-Windows kuphela)</translation>
<translation id="1294154142200295408">Ukuhluka komugqa womyalo</translation>
@@ -223,6 +225,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
&lt;p&gt;Ukuze ulungise iphutha, chofoza okuthi &lt;strong&gt;Xhuma&lt;/strong&gt; ekhasini ozama ukulivula.&lt;/p&gt;</translation>
<translation id="1507780850870535225">Ukudizayinwa kwamagceke</translation>
<translation id="1513706915089223971">Uhlu lokufakiwe komlando</translation>
+<translation id="1516097932025103760">Izobethelwa, ilondolozwe ngokuvikelekile futhi i-CVC ayilokothi igcinwe.</translation>
<translation id="1517433312004943670">Inombolo yefoni iyadingeka</translation>
<translation id="1519264250979466059">Idethi yokwakha</translation>
<translation id="1521159554480556801">Ubuciko be-fiber ne-textile</translation>
@@ -288,6 +291,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="1662550410081243962">Londoloza futhi ugcwalise zonke izindlela zokukhokha</translation>
<translation id="1663943134801823270">Amakhadi namakheli asuka ku-Chrome. Ungawaphatha <ph name="BEGIN_LINK" />kuzilungiselelo<ph name="END_LINK" />.</translation>
<translation id="1671391448414634642">Amakhasi akusi-<ph name="SOURCE_LANGUAGE" /> azohunyushelwa kusi-<ph name="TARGET_LANGUAGE" /> kusukela manje.</translation>
+<translation id="1673886523110456987">Phuma nge-<ph name="CARD_DETAIL" /> ukuze usebenzise umtitilizo</translation>
<translation id="1674504678466460478">Isi-<ph name="SOURCE_LANGUAGE" /> ukuya kusi-<ph name="TARGET_LANGUAGE" /></translation>
<translation id="1682696192498422849">Umkhawulo omfushane kuqala</translation>
<translation id="168693727862418163">Ukubaluleka kwale nqubomgomo kuhlulekile ukuqinisekisa ngokumelene ne-schema sayo futhi ngeke inakwe.</translation>
@@ -306,6 +310,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="1717218214683051432">Izinzwa zokunyakaza</translation>
<translation id="1717494416764505390">Ibhokisi lemeyili elingu-3</translation>
<translation id="1718029547804390981">Idokhumenti likhulu kakhulu ukuthi lingachasiswa</translation>
+<translation id="1720941539803966190">Vala okokufundisa?</translation>
<translation id="1721424275792716183">* Inkambu iyadingeka</translation>
<translation id="1727613060316725209">Isitifiketi asivumelekile</translation>
<translation id="1727741090716970331">Engeza inombolo yekhadi evumelekile</translation>
@@ -422,8 +427,8 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="205212645995975601">I-BBQ nokuthosa</translation>
<translation id="2053111141626950936">Amakhasi angesi-<ph name="LANGUAGE" /> ngeke aze ahunyushwe.</translation>
<translation id="2053373601901562871">{NUM_DAYS,plural, =0{Uma lokhu kulawula kuvuliwe futhi nesimo sisebenza, i-Chrome inquma ukuthi yiliphi iqembu elikhulu labantu, noma "iqoqo," umsebenzi wakho wokuphequlula wakamuva ofana kakhulu nalo. Abakhangisi bangakhetha izikhangiso zeqembu futhi umsebenzi wakho wokuphequlula ugcinwa uyimfihlo kudivayisi yakho. Iqembu lakho libuyekezwa nsuku zonke.}=1{Uma lokhu kulawula kuvuliwe futhi nesimo sisebenza, i-Chrome inquma ukuthi yiliphi iqembu elikhulu labantu, noma "iqoqo," umsebenzi wakho wokuphequlula wakamuva ofana kakhulu nalo. Abakhangisi bangakhetha izikhangiso zeqembu futhi umsebenzi wakho wokuphequlula ugcinwa uyimfihlo kudivayisi yakho. Iqembu lakho libuyekezwa nsuku zonke.}one{Uma lokhu kulawula kuvuliwe futhi nesimo sisebenza, i-Chrome inquma ukuthi yiliphi iqembu elikhulu labantu, noma "iqoqo," umsebenzi wakho wokuphequlula wakamuva ofana kakhulu nalo. Abakhangisi bangakhetha izikhangiso zeqembu futhi umsebenzi wakho wokuphequlula ugcinwa uyimfihlo kudivayisi yakho. Iqembu lakho libuyekezwa njalo ezinsukwini ezingu-{NUM_DAYS}.}other{Uma lokhu kulawula kuvuliwe futhi nesimo sisebenza, i-Chrome inquma ukuthi yiliphi iqembu elikhulu labantu, noma "iqoqo," umsebenzi wakho wokuphequlula wakamuva ofana kakhulu nalo. Abakhangisi bangakhetha izikhangiso zeqembu futhi umsebenzi wakho wokuphequlula ugcinwa uyimfihlo kudivayisi yakho. Iqembu lakho libuyekezwa njalo ezinsukwini ezingu-{NUM_DAYS}.}}</translation>
-<translation id="2053553514270667976">Ikhodi ye-Zip</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 isiphakamiso}one{# iziphakamiso}other{# iziphakamiso}}</translation>
+<translation id="2066915425250589881">isicelo sokusulwa</translation>
<translation id="2068528718802935086">Izingane nabantwana</translation>
<translation id="2071156619270205202">Leli khadi alifanelekeli inombolo yekhadi le-virtual</translation>
<translation id="2071692954027939183">Izaziso zivinjelwe ngokuzenzakalela ngoba ngokuvamile awuzivumeli</translation>
@@ -435,7 +440,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="2088086323192747268">Inkinobho yokuphatha ukuvumelanisa, cindezela u-Enter ukuphatha ukuthi yiluphi ulwazi oluvumelanisa kumasethingi we-Chrome</translation>
<translation id="2091887806945687916">Umsindo</translation>
<translation id="2094505752054353250">Ukungafani kwesizinda</translation>
-<translation id="2096368010154057602">Umnyango</translation>
<translation id="2099652385553570808">Uunamathisela kathathu kesokunxele</translation>
<translation id="2101225219012730419">Inguqulo:</translation>
<translation id="2102134110707549001">Iphakamisa iphasiwedi eqinile…</translation>
@@ -472,7 +476,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="2185836064961771414">Ibhola lase-America</translation>
<translation id="2187317261103489799">Thola (okuzenzakalelayo)</translation>
<translation id="2188375229972301266">Ukushaya kaningi phansi</translation>
-<translation id="2188852899391513400">Iphasiwedi osanda kuyisebenzisa itholakele ekwephuleni isivumelwano sedatha. Ukuze ivikele ama-akhawunti akho, i-Google Password Manager incoma ukuthi ukuyishintshe manje bese uhlola amaphasiwedi akho alondoloziwe.</translation>
<translation id="219906046732893612">Awezokuthuthukiswa kwekhaya</translation>
<translation id="2202020181578195191">Faka unyaka ovumelekile wokuphelelwa isikhathi</translation>
<translation id="22081806969704220">Ithileyi elingu-3</translation>
@@ -483,6 +486,8 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="2215727959747642672">Ukuhlelwa kwefayela</translation>
<translation id="2215963164070968490">Izinja</translation>
<translation id="2218879909401188352">Abahlaseli okwamanje ku-<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> bangafaka izinhlelo zokusebenza eziyingozi ezona idivayisi yakho, zingeza izindleko ezifihlakele enkokhelweni yakho yeselula, noma zitshontshe ulwazi lwakho oluyimfihlo. <ph name="BEGIN_LEARN_MORE_LINK" />Funda kabanzi<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2219658597883514593">Qalisa kabusha okokufundisa</translation>
+<translation id="2219735899272417925">Kudingeka ukusetha kabusha idivayisi</translation>
<translation id="2224337661447660594">Ayikho i-inthanethi</translation>
<translation id="2230458221926704099">Lungisa uxhumo lwakho usebenza <ph name="BEGIN_LINK" />uhlelo lokusebenza lokuxilonga<ph name="END_LINK" /></translation>
<translation id="2239100178324503013">Thumela manje</translation>
@@ -580,11 +585,13 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="2512101340618156538">Akuvumelekile (okuzenzekelayo)</translation>
<translation id="2512413427717747692">Setha i-Chrome njengenkinobho yebhrawuza ezenzakalelayo, cindezela okuthi Enter ukuze usethe i-Chrome njengebhrawuza ezenzakalelayo yesistimu kumasethingi e-iOS</translation>
<translation id="2515629240566999685">Ukuhlola isignali endaweni yakho</translation>
+<translation id="2515761554693942801">Ukhethe ukuqinisekisa nge-Touch ID kumawebhusayithi asebenzisa i-<ph name="PROVIDER_ORIGIN" />. Lo mhlinzeki kungenzeka ukuthi ulondoloze ulwazi olumayelana nendlela yakho yokukhokha, ongakwazi uku-<ph name="LINK_TEXT" />.</translation>
<translation id="2521385132275182522">Namathisela phansi kwesokudla</translation>
<translation id="2521736961081452453">Dala ifomu</translation>
<translation id="2523886232349826891">Kulondolozwe kule divayisi kuphela</translation>
<translation id="2524461107774643265">Engeza ulwazi olungeziwe</translation>
<translation id="2529899080962247600">Le nkambu akufanele ibe nokungena okungaphe kokungu-<ph name="MAX_ITEMS_LIMIT" />. Konke okufakiwe okwengeziwe kuzoshaywa indiva.</translation>
+<translation id="253493526287553278">Bona imininingwane yekhodi yephromo</translation>
<translation id="2535585790302968248">Vula iwindi elisha le-Incognito ukuze ubhrawuze ngokugodliwe</translation>
<translation id="2535659140340599600">{COUNT,plural, =1{nokungu-1 ngaphezulu}one{nokungu-# ngaphezulu}other{nokungu-# ngaphezulu}}</translation>
<translation id="2536110899380797252">Engeza ikheli</translation>
@@ -620,7 +627,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="259821504105826686">Ubuciko bezithombe nobedijithali</translation>
<translation id="2601150049980261779">Awama-movie wothando</translation>
<translation id="2604589665489080024">Umculo we-Pop</translation>
-<translation id="2609632851001447353">Ukwahluka</translation>
<translation id="2610561535971892504">Chofoza ukuze ukopishe</translation>
<translation id="2617988307566202237">I-Chrome <ph name="BEGIN_EMPHASIS" />ngeke ilondoloze<ph name="END_EMPHASIS" /> ulwazi olulandelayo:
<ph name="BEGIN_LIST" />
@@ -653,6 +659,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="2676271551327853224">Roc-8K</translation>
<translation id="2677696497921480781">Izinsuku zokuzalwa nezinsuku zamagama</translation>
<translation id="2677748264148917807">Hamba</translation>
+<translation id="2679714844901977852">Londoloza ulwazi lwekhadi lakho nokukhokha ku-Google Account ethi <ph name="USER_EMAIL" /> ukuze ube nokuphuma okuvikelekile nokusheshayo</translation>
<translation id="2684561033061424857">11x12</translation>
<translation id="2687555958734450033">Kulingana kahle kakhulu</translation>
<translation id="2688969097326701645">Yebo, qhubeka</translation>
@@ -701,7 +708,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="2854764410992194509">Ama-Internet service provider (ama-ISP)</translation>
<translation id="2856444702002559011">Abahlaseli bangazama ukutshontsha ulwazi lwakho kusuka ku-<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> (isibonelo, amaphasiwedi, imilayezo, noma amakhadi esikweletu). <ph name="BEGIN_LEARN_MORE_LINK" />Funda kabanzi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2859806420264540918">Leli sayithi libonisa izikhangiso ezingathandeki noma ezidukisayo.</translation>
-<translation id="286512204874376891">Ikhadi le-virtual lifihla ikhadi lakho langempela ukusiza ukukuvikela ekukhwabaniseni okungase kube khona. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="287596039013813457">Ubungani</translation>
<translation id="28761159517501904">Ama-movie</translation>
<translation id="2876489322757410363">Ushiya imodi ye-incognito ukuze ukhokhe nge-app yangaphandle. Qhubeka?</translation>
@@ -802,7 +808,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="3158539265159265653">Idiski</translation>
<translation id="3162559335345991374">I-Wi-Fi oyisebenzisayo ingadinga ukuthi uvakashele ikhasi layo lokungena ngemvume.</translation>
<translation id="3169472444629675720">Zitholele</translation>
-<translation id="3174168572213147020">Isiqhingi</translation>
<translation id="3176929007561373547">Hlola izilungiselelo zakho zommeleli noma uxhumane nomlawuli wakho wenethiwekhi ukuze uqiniseke ukuthi iseva elibamba iyasebenza. Uma ungakholwa ukuthi kumele usebenzise iseva elibamba:
<ph name="PLATFORM_TEXT" /></translation>
<translation id="317878711435188021">Yazi lapho usebenzisa le divayisi</translation>
@@ -879,9 +884,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="3369192424181595722">Iphutha lewashi</translation>
<translation id="3369459162151165748">Izingxenye zemoto nezinsiza</translation>
<translation id="3371064404604898522">Setha i-Chrome njengebhrawuza ezenzakalelayo</translation>
-<translation id="3371076217486966826">I-<ph name="URL" /> ifuna:
- • Ukudala imephu engu-3D yakho nokulandelela ukuma kwekhamera
- • Ukusebenzisa ikhamera yakho</translation>
<translation id="337363190475750230">Iyekiswe ukunikezwa</translation>
<translation id="3375754925484257129">Yenza Ukuhlola kokuphepha kwe-Chrome</translation>
<translation id="3377144306166885718">Iseva isebenzise inguqulo ye-TLS ephelelwe isikhathi.</translation>
@@ -898,6 +900,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="3399952811970034796">Ikheli lokulethwa</translation>
<translation id="3402261774528610252">Ukuxhuma okusetshenziselwe ukulayisha le sayithi kusebenzise i-TLS 1.0 noma i-TLS 1.1, engavunyelwe futhi ekuzokhutshazwa esikhathini esizayo. Lapho isikhutshaziwe, abasebenzisi bazovinjelwa ukulayisha le sayithi. Iseva kufanele inike amandla i-TLS 1.2 noma yakamuva.</translation>
<translation id="3405664148539009465">Enza ngendlela oyifisayo amafonti</translation>
+<translation id="3407789382767355356">ukungena ngemvume kongahlangene ngqo</translation>
<translation id="3409896703495473338">Phatha izilungiselelo zokuvikela</translation>
<translation id="3414952576877147120">Usayizi:</translation>
<translation id="3417660076059365994">Amafayela owalayishayo noma owanamathiselayo athunyelwa ku-Google Cloud noma kuzinkampani zangaphandle ukuze ahlaziywe. Isibonelo, angapheqululwa mayelana nedatha ezwelayo noma uhlelo olungayilungele ikhompuyutha.</translation>
@@ -930,6 +933,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="3477679029130949506">Uhlu lwama-movie nezikhathi zokubonisa zetiyetha</translation>
<translation id="3479552764303398839">Hhayi manje</translation>
<translation id="3484560055331845446">Ungalahlekelwa wukufinyelela ku-akhawunti yakho ye-Google. I-Chrome incoma ukuthi ushintshe iphasiwedi yakho manje. Uzocelwa ukuthi ungene ngemvume.</translation>
+<translation id="3484861421501147767">Isikhumbuzi: Ikhodi yephromo elondoloziwe iyatholakala</translation>
<translation id="3487845404393360112">Ithileyi elingu-4</translation>
<translation id="3495081129428749620">Thola kukhasi
<ph name="PAGE_TITLE" /></translation>
@@ -1053,6 +1057,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="3810973564298564668">Phatha</translation>
<translation id="3816482573645936981">Inani (lithathelwe isikhundla)</translation>
<translation id="382518646247711829">Uma ngabe usebenzisa iseva elibamba...</translation>
+<translation id="3826050100957962900">Ukungena ngemvume kwenkampani yangaphandle</translation>
<translation id="3827112369919217609">Okuphelele</translation>
<translation id="3827666161959873541">Awama-movie womndeni</translation>
<translation id="3828924085048779000">Umushwana wokungena ongenalutho awuvunyelwe.</translation>
@@ -1065,9 +1070,9 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="3858027520442213535">Buyekeza idethi nesikhathi</translation>
<translation id="3858860766373142691">Igama</translation>
<translation id="3872834068356954457">Isayensi</translation>
+<translation id="3875783148670536197">Ngibonise Ukuthi Kanjani</translation>
<translation id="3881478300875776315">Bonisa imigqa embalwa</translation>
<translation id="3884278016824448484">Isikhombi sedivayisi esishayisanayo</translation>
-<translation id="3885155851504623709">I-Parish</translation>
<translation id="388632593194507180">Ukwengamela kutholiwe</translation>
<translation id="3886948180919384617">Isitaki esingu-3</translation>
<translation id="3890664840433101773">Engeza i-imeyili</translation>
@@ -1097,9 +1102,11 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="3973234410852337861">I-<ph name="HOST_NAME" /> ivinjiwe</translation>
<translation id="3978338123949022456">Imodi yosesho, thayipha umbuzo bese ucindezela okuthi Enter ukuze useshe nge-<ph name="KEYWORD_SUFFIX" /></translation>
<translation id="398470910934384994">Izinyoni</translation>
+<translation id="3985750352229496475">Phatha Amakheli...</translation>
<translation id="3986705137476756801">Vala Okushuthwe Bukhoma okwamanje</translation>
<translation id="3987940399970879459">Ngaphansi kunokungu-1 MB</translation>
<translation id="3990250421422698716">I-Jog offset</translation>
+<translation id="3992684624889376114">Mayelana naleli khasi</translation>
<translation id="3996311196211510766">Isayithi le-<ph name="ORIGIN" /> licele ukuthi inqubomgomo yokwangempela
isebenze kuzo zonke izicelo zayo, kodwa le nqubomgomo ayikwazi ukusetshenziswa okwamanje.</translation>
<translation id="4006465311664329701">Izindlela Zokukhokha, Imititilizo, Namakheli Asebenzisa I-Google Pay</translation>
@@ -1224,6 +1231,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="4305666528087210886">Ifayela lakho alikwazanga ukufinyelelwa</translation>
<translation id="4306529830550717874">Londoloza ikheli?</translation>
<translation id="4306812610847412719">ibhodi yokunamathisela</translation>
+<translation id="4310070645992025887">Sesha Amahambo Akho</translation>
<translation id="4312613361423056926">B2</translation>
<translation id="4312866146174492540">Vimba (ngokuzenzakalela)</translation>
<translation id="4314815835985389558">Phatha ukuvumelanisa</translation>
@@ -1274,6 +1282,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="4435702339979719576">Ikhadi lokuposa)</translation>
<translation id="443673843213245140">Ukusetshenziswa ummeleli kukhitshaziwe kodwa ukulungiselelwa okubekelwe obala kommeleli kucacisiwe.</translation>
<translation id="4441832193888514600">Kuzitshwe ngenxa yokuthi inqubomgomo ikwazi ukusethwa kuphela njengenqubomgomo yomsebenzisi we-cloud.</translation>
+<translation id="4442470707340296952">Amathebhu we-Chrome</translation>
<translation id="4450893287417543264">Ungabonisi futhi</translation>
<translation id="4451135742916150903">Ingacela ukuxhuma kumadivayisi we-HID</translation>
<translation id="4452328064229197696">Iphasiwedi osanda kuyisebenzisa itholakele ekwephuleni isivumelwano sedatha. Ukuze ivikele ama-akhawunti akho, i-Google Password Manager incoma ukuhlola amaphasiwedi akho alondoloziwe.</translation>
@@ -1412,6 +1421,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="483241715238664915">Vula izexwayiso</translation>
<translation id="4834250788637067901">Izindlela zokukhokha, imititilizo, namakheli asebenzisa i-Google Pay</translation>
<translation id="4838327282952368871">I-Dreamy</translation>
+<translation id="4839087176073128681">Khokha ngokushesha ngesikhathi esizayo futhi uvikele ikhadi lakho ngokuvikeleka okuhamba phambili embonini ye-Google.</translation>
<translation id="4840250757394056958">Buka umlando wakho we-Chrome</translation>
<translation id="484462545196658690">Okuzenzakalelayo</translation>
<translation id="484671803914931257">Thola isaphulelo ku-<ph name="MERCHANT_NAME" /> nokwengeziwe</translation>
@@ -1474,6 +1484,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="5011561501798487822">Ulimi Olutholiwe</translation>
<translation id="5015510746216210676">Igama lomshini:</translation>
<translation id="5017554619425969104">Umbhalo owukopishile</translation>
+<translation id="5017828934289857214">Ngikhumbuze ngemuva kwesikhathi</translation>
<translation id="5018422839182700155">Ayikwazi ukuvula leli khasi</translation>
<translation id="5019198164206649151">Isekela isitolo esisesimeni esibi</translation>
<translation id="5020776957610079374">Umculo womhlaba</translation>
@@ -1493,6 +1504,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="5051305769747448211">Ihlaya elibukhoma</translation>
<translation id="5056425809654826431">{NUM_FILES,plural, =1{Ukuze uthumele leli fayela usebenzisa i-Nearby Share, khulula isikhala (<ph name="DISK_SPACE_SIZE" />) kudivayisi yakho}one{Ukuze uthumele lamafayela usebenzisa i-Nearby Share, khulula isikhala (<ph name="DISK_SPACE_SIZE" />) kudivayisi yakho}other{Ukuze uthumele lamafayela usebenzisa i-Nearby Share, khulula isikhala (<ph name="DISK_SPACE_SIZE" />) kudivayisi yakho}}</translation>
<translation id="5056549851600133418">Ama-athikili akho</translation>
+<translation id="5060483733937416656">Ukhethe ukuqinisekisa nge-Windows Hello kumawebhusayithi asebenzisa i-<ph name="PROVIDER_ORIGIN" />. Lo mhlinzeki kungenzeka ukuthi ulondoloze ulwazi olumayelana nendlela yakho yokukhokha, ongakwazi uku-<ph name="LINK_TEXT" />.</translation>
<translation id="5061227663725596739">Ingabe ubukade usho i-<ph name="LOOKALIKE_DOMAIN" />?</translation>
<translation id="5066056036849835175">Umlando wokuphrinta</translation>
<translation id="5068234115460527047">Izimali ezivikela ukulahlekelwa</translation>
@@ -1506,10 +1518,8 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="5087286274860437796">Isitifiketi seseva asivumelekile ngalesi sikhathi.</translation>
<translation id="5087580092889165836">Engeza ikhadi</translation>
<translation id="5088142053160410913">Umlayezo oya ku-opharetha</translation>
-<translation id="5089810972385038852">Izwe</translation>
<translation id="5093232627742069661">Z-fold</translation>
<translation id="5094747076828555589">Le seva ayikwazanga ukukhombisa ukuthi iyi-<ph name="DOMAIN" />; isitifiketi sayo sokuvikeleka asithenjiwe i-Chromium. Lokhu kungenzeka kubangelwe ukulungisa okungalungile noma umhlaseli uzama ukufinyelela uxhumo lwakho.</translation>
-<translation id="5095208057601539847">Isifunda</translation>
<translation id="5097099694988056070">Izibalo zedivayisi ezifana nokusetshenziswa kwe-CPU/i-RAM</translation>
<translation id="5097501891273180634">A2</translation>
<translation id="5108881358339761672">Isayithi aliphephile</translation>
@@ -1547,6 +1557,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="5171045022955879922">Sesha noma thayipha i-URL</translation>
<translation id="5171689220826475070">Fanfold-European</translation>
<translation id="5172758083709347301">Umshini</translation>
+<translation id="5177076414499237632">Funda mayelana nomthombo waleli khasi nesihloko</translation>
<translation id="5179510805599951267">Akukho ngesi-<ph name="ORIGINAL_LANGUAGE" />? Bika leli phutha</translation>
<translation id="518639307526414276">Ukudla kwemfuyo nokunakekelwa kwemfuyo</translation>
<translation id="5190835502935405962">Ibha yamabhukhimakhi</translation>
@@ -1707,6 +1718,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="5624120631404540903">Phatha amaphasiwedi</translation>
<translation id="5629630648637658800">Yehlulekile ukulayisha izilungiselelo zenqubomgomo</translation>
<translation id="5631439013527180824">Ithokheni yokuphatha idivayisi engavumelekile</translation>
+<translation id="5632485077360054581">Ngibonise ukuthi kanjani</translation>
<translation id="5633066919399395251">Abahlaseli okwamanje ku-<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> bangazama ukufaka izinhlelo eziyingozi kukhompyutha yakho ezitshontsha noma ezisula ulwazi lwakho (isibonelo, izithombe, amaphasiwedi, imilayezo, namakhadi esikweletu). <ph name="BEGIN_LEARN_MORE_LINK" />Funda kabanzi<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="563324245173044180">Okuqukethwe okulahlekisayo kuvinjelwe.</translation>
<translation id="5633259641094592098">Ama-movie wenkolo newe-indie</translation>
@@ -1824,6 +1836,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="5989320800837274978">Akuwona amaseva alibamba agxilile noma i-URL yesikripthi se-.pac acacisiwe.</translation>
<translation id="5992691462791905444">I-Engineering Z-fold</translation>
<translation id="5995727681868049093">Phatha ulwazi lwakho, ubumfihlo, nokuvikeleka Ku-akhawunti yakho ye-Google</translation>
+<translation id="5997247540087773573">Iphasiwedi osanda kuyisebenzisa itholakele ekwephuleni isivumelwano sedatha. Ukuze uvikele ama-akhawunti akho, Umphathi Wephasiwedi Ye-Google uncoma ukuthi uyishintshe manje futhi uhlole amaphasiwedi akho alondoloziwe.</translation>
<translation id="6000758707621254961">imiphumela engu-<ph name="RESULT_COUNT" /> ye-'<ph name="SEARCH_TEXT" />'</translation>
<translation id="6006484371116297560">Okwakudala</translation>
<translation id="6008122969617370890">N-ukuya ku-oda elingu-1</translation>
@@ -1919,7 +1932,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="627746635834430766">Khokha ngokushesha ngesikhathi esizayo, londoloza ikheli lakho lokukhokha ku-akhawunti yakho ye-Google.</translation>
<translation id="6279183038361895380">Cindezela u-|<ph name="ACCELERATOR" />| ukuze ubonise ikhesa lakho</translation>
<translation id="6280223929691119688">Ayikwazi ukuletha kuleli kheli. Khetha ikheli elihlukile.</translation>
-<translation id="6282194474023008486">Ikhodi yeposi</translation>
<translation id="6285507000506177184">Phatha okudawunilodiwe kwinkinobho ye-Chrome, cindezela u-Enter ukuphatha amafayela owadawunilodile ku-Chrome</translation>
<translation id="6289939620939689042">Umbala Wekhasi</translation>
<translation id="6290238015253830360">Ama-athikili akho aphakanyisiwe avela lapha</translation>
@@ -1941,6 +1953,7 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="6337133576188860026">Khulula kufika ngaphansi kuka-<ph name="SIZE" />. Amanye amasayithi angalayisha kancane kakhulu ekuvakasheni kwakho okulandelayo.</translation>
<translation id="6337534724793800597">Hlunga izinqubomgomo ngegama</translation>
<translation id="6340739886198108203">Inqubomgomo yomlawuli ayincomi ukuthatha izithombe-skrini noma ukurekhoda lapho okuqukethwe okuyimfihlo kubonakala:</translation>
+<translation id="6348220984832452017">Ukwahlukahluka Okusebenzayo</translation>
<translation id="6349101878882523185">Faka i-<ph name="APP_NAME" /></translation>
<translation id="6353505687280762741">{COUNT,plural, =0{Lutho}=1{1 iphasiwedi (ye-<ph name="DOMAIN_LIST" />, ivunyelanisiwe)}=2{2 amaphasiwedi (e-<ph name="DOMAIN_LIST" />, avunyelanisiwe)}one{# amaphasiwedi (e-<ph name="DOMAIN_LIST" />, avunyelanisiwe)}other{# amaphasiwedi (e-<ph name="DOMAIN_LIST" />, avunyelanisiwe)}}</translation>
<translation id="6355392890578844978">Lesi siphequluli asiphethwe inkampani noma enye inhlangano. Umsebenzi okule divayisi ungaphathwa ngaphandle kwe-Chromium. <ph name="BEGIN_LINK" />Funda kabanzi<ph name="END_LINK" /></translation>
@@ -1972,7 +1985,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="643051589346665201">Shintsha iphasiwedi ye-Google</translation>
<translation id="6433490469411711332">Hlela ulwazi loxhumana naye</translation>
<translation id="6433595998831338502">I-<ph name="HOST_NAME" /> inqabe ukuxhumeka.</translation>
-<translation id="6438025220577812695">Ngiyishintsha ngokwami</translation>
<translation id="6440503408713884761">Izitshiwe</translation>
<translation id="6443406338865242315">Iziphi izandiso nama-plugins owafakile</translation>
<translation id="6446163441502663861">Kahu (Envelope)</translation>
@@ -2102,9 +2114,9 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="6828866289116430505">Ufuzo</translation>
<translation id="6831043979455480757">Humusha</translation>
<translation id="6833752742582340615">Londoloza imininingwane yekhadi lakho nokukhokha ku-Google Account ukuze ube nokuphuma okuvikelekile nokusheshayo</translation>
-<translation id="6839929833149231406">Indawo</translation>
<translation id="6846340164947227603">Sebenzisa inombolo yekhadi yokubuka...</translation>
<translation id="6852204201400771460">Layisha kabusha uhlelo lokusebenza?</translation>
+<translation id="6857776781123259569">Phatha Amaphasiwedi...</translation>
<translation id="686485648936420384">Izinsiza sekhasimende</translation>
<translation id="6865412394715372076">Leli khadi alikwazi ukuqinisekiswa khona manje</translation>
<translation id="6869334554832814367">Izimalimboleko zomuntu siqu</translation>
@@ -2153,7 +2165,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="6965978654500191972">Idivayisi</translation>
<translation id="696703987787944103">Ukubonakala</translation>
<translation id="6968269510885595029">Sebenzisa ukhiye wakho Wokuqinisekisa ubunikazi</translation>
-<translation id="6970216967273061347">Isifunda</translation>
<translation id="6971439137020188025">Dala iphrezentheshini entsha ye-Google ngokushesha ku-Slides</translation>
<translation id="6972629891077993081">Amadivayisi we-HID</translation>
<translation id="6973656660372572881">Womabili amaseva wokubamba alungisiwe ne-URL yesikripthi se-.pac kucacisiwe.</translation>
@@ -2192,7 +2203,6 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<translation id="7081308185095828845">Lesi sici asitholakali kudivayisi yakho</translation>
<translation id="7083258188081898530">Ithileyi elingu-9</translation>
<translation id="7086090958708083563">Okulayishwayo kucelwe ngumsebenzisi</translation>
-<translation id="7087282848513945231">Izwe</translation>
<translation id="7095139009144195559"><ph name="MANAGE_SITE_SETTINGS_FOCUSED_FRIENDLY_MATCH_TEXT" />, cindezela u-Tab bese u-Enter ukuphatha izimvume nedatha egcinwe kuwo wonke amasayithi kumasethingi we-Chrome</translation>
<translation id="7096937462164235847">Ubunikazi bale webhusayithi abuqinisekisiwe.</translation>
<translation id="7101893872976785596">Awama-movie athusayo</translation>
@@ -2211,10 +2221,10 @@ Lokhu uma kungenjalo kuzovinjelwa izilungiselelo zakho zobumfihlo. Lokhu kuzovum
<ph name="LIST_ITEM" />Ulwazi olufakwa kumafomu<ph name="END_LIST_ITEM" />
<ph name="END_LIST" /></translation>
<translation id="7129409597930077180">Ayikwazi ukuthumela kuleli kheli. Khetha ikheli elihlukile.</translation>
+<translation id="7129809579943936035"><ph name="VALUE_PROP" /> <ph name="DETAILS" /></translation>
<translation id="7132939140423847331">Umphathi wakho uvimbele ukuthi le datha ikopishwe.</translation>
<translation id="7135130955892390533">Bonisa isimo</translation>
<translation id="7138472120740807366">Indlela yokulethwa</translation>
-<translation id="7139724024395191329">I-Emirate</translation>
<translation id="7139892792842608322">Umqulu Oyinhloko</translation>
<translation id="714064300541049402">I-Side 2 image X shift</translation>
<translation id="7152423860607593928">Inombolo-14 (Envelope)</translation>
@@ -2431,6 +2441,7 @@ Imininingwane engeziwe:
<translation id="7669271284792375604">Abahlaseli kuleli sayithi bangazama ukukukhohlisa ukuthi ufake izinhlelo ezilimaza umuzwa wakho wokuphequlula (isibonelo, ngokushintsha ikhasi lakho lasekhaya noma ukubonisa izikhangiso ezingeziwe kumasayithi owavakashelayo).</translation>
<translation id="7669907849388166732">{COUNT,plural, =1{Izenzo ezithathwe ngedatha zimakwe njengeziyimfihlo (isenzo esi-1 kusukela lapho ungene khona ngemvume). <ph name="BEGIN_LINK" />Funda kabanzi<ph name="END_LINK" />}one{Izenzo ezithathwe ngedatha zimakwe njengeziyimfihlo (izenzo ezingu-# selokhu ungene ngemvume). <ph name="BEGIN_LINK" />Funda kabanzi<ph name="END_LINK" />}other{Izenzo ezithathwe ngedatha zimakwe njengeziyimfihlo (izenzo ezingu-# selokhu ungene ngemvume). <ph name="BEGIN_LINK" />Funda kabanzi<ph name="END_LINK" />}}</translation>
<translation id="7673278391011283842">Ibhokisi lemeyili elingu-6</translation>
+<translation id="7675325315208090829">Phatha Izindlela Zokukhokha...</translation>
<translation id="7676643023259824263">Sesha wonke umbhalo osebhodini lokunamathisela, <ph name="TEXT" /></translation>
<translation id="7679367271685653708">Buka futhi phatha umlando wakho wokuphequlula kumasethingi we-Chrome</translation>
<translation id="7679947978757153706">I-Baseball</translation>
@@ -2473,7 +2484,6 @@ Imininingwane engeziwe:
<translation id="7766518757692125295">Isiketi</translation>
<translation id="7770259615151589601">Ubude obunikiwe</translation>
<translation id="7773005668374414287">I-oda elifanayo libheke phezulu</translation>
-<translation id="777702478322588152">Isifunda</translation>
<translation id="7791011319128895129">Akukhishiwe</translation>
<translation id="7791196057686275387">I-Bale</translation>
<translation id="7791543448312431591">Engeza</translation>
@@ -2564,12 +2574,12 @@ Imininingwane engeziwe:
<translation id="8055534648776115597">Imfundo yokuqala neqhubekayo</translation>
<translation id="8057711352706143257">"<ph name="SOFTWARE_NAME" />" ayilungisiwe kahle. Ukukhipha i-"<ph name="SOFTWARE_NAME" />" ngokuvamile kulungisa inkinga. <ph name="FURTHER_EXPLANATION" /></translation>
<translation id="8058009102480785916">Ukukhiqizwa kokudla</translation>
-<translation id="8066955247577885446">Uxolo, kukhona okungahambanga kahle.</translation>
<translation id="8067872629359326442">Usanda kufaka iphasiwedi yakho kusayithi elikhohlisayo. I-Chromium ingasiza. Ukuze ushintshe iphasiwedi yakho futhi wazise i-Google ukuthi i-akhawunti yakho ingaba sengozini, chofoza i-akhawunti y-Protect.</translation>
<translation id="8070439594494267500">Isithonjana sohlelo lokusebenza</translation>
<translation id="8074253406171541171">10x13 (Envelope)</translation>
<translation id="8075736640322370409">Dala i-Google Sheet entsha ngokushesha</translation>
<translation id="8075898834294118863">Phatha amasethingi wesayithi</translation>
+<translation id="8076492880354921740">Okwamathebhu</translation>
<translation id="8078141288243656252">Ayikwazi ukuchasisa uma izungezisiwe</translation>
<translation id="8079031581361219619">Layisha kabusha isayithi?</translation>
<translation id="8081087320434522107">Izimoto ezincane</translation>
@@ -2692,6 +2702,7 @@ Imininingwane engeziwe:
<ph name="DEBUG_INFO" /></translation>
<translation id="8424582179843326029"><ph name="FIRST_LABEL" /> <ph name="SECOND_LABEL" /> <ph name="THIRD_LABEL" /></translation>
<translation id="8428213095426709021">Izilungiselelo</translation>
+<translation id="8428634594422941299">Ngiyezwa</translation>
<translation id="8431194080598727332"><ph name="MANAGE_COOKIES_FOCUSED_FRIENDLY_MATCH_TEXT" />, cindezela u-Tab bese u-Enter ukuphatha okuncanyelwayo kwamakhukhi wakho kumasethingi we-Chrome</translation>
<translation id="8433057134996913067">Lokhu kuzokukhipha kumawebhusayithi amaningi.</translation>
<translation id="8434840396568290395">Izilwane zasekhaya</translation>
@@ -2789,6 +2800,7 @@ Imininingwane engeziwe:
<translation id="8742371904523228557">I-<ph name="ONE_TIME_CODE" /> ikhodi yakho ye-<ph name="ORIGIN" /></translation>
<translation id="874918643257405732">Yenza ibhukhimakhi leli thebhu</translation>
<translation id="8751426954251315517">Sicela uzame futhi ngesikhathi esilandelayo</translation>
+<translation id="8757526089434340176">Okunikezwayo kwe-Google Pay kuyatholakala</translation>
<translation id="8758885506338294482">Imidlalo yevidiyo yokuncintisana</translation>
<translation id="8759274551635299824">Leli khadi liphelelwe isikhathi</translation>
<translation id="87601671197631245">Le sayithi isebenzisa ukulungiselelwa kokuvikeleka okuphelelwe isikhathi, okugaveza ulwazi lwakho (ngokwesibonelo, amaphasiwedi, imilayezo, noma amakhadi wesikweletu) uma kuthunyelwe kuleli sayithi.</translation>
@@ -2796,6 +2808,7 @@ Imininingwane engeziwe:
<translation id="8763927697961133303">Idivayisi ye-USB</translation>
<translation id="8763986294015493060">Vala wonke amawindi e-Incognito avuliwe njengamanje</translation>
<translation id="8766943070169463815">Ishidi lokufakazela ubuqiniso lezimfanelo zenkokhelo elivikelekile livuliwe</translation>
+<translation id="8767765348545497220">Vala ibhamuza losizo</translation>
<translation id="877985182522063539">A4</translation>
<translation id="8785658048882205566">Izithuthuthu</translation>
<translation id="8790007591277257123">&amp;Yenza futhi ukususa</translation>
@@ -2808,6 +2821,7 @@ Imininingwane engeziwe:
<translation id="8806285662264631610">Imikhiqizo yokugeza nomzimba</translation>
<translation id="8807160976559152894">Sika ngemuva kwekhasi ngalinye</translation>
<translation id="8808828119384186784">Amasethingi E-Chrome</translation>
+<translation id="8813277370772331957">Ngikhumbuze ngesinye isikhathi</translation>
<translation id="8816395686387277279"><ph name="UPDATE_CHROME_FOCUSED_FRIENDLY_MATCH_TEXT" />, cindezela u-Tab bese u-Enter ukuze ubuyekeze i-Chrome kusuka kumasethingi akho e-Chrome</translation>
<translation id="8820817407110198400">Amabhukhimakhi</translation>
<translation id="882338992931677877">Isikhala Sokuzenzela Ngokwakho</translation>
@@ -2987,6 +3001,7 @@ Imininingwane engeziwe:
<translation id="988159990683914416">Ukwakhiwa konjiniyela</translation>
<translation id="989988560359834682">Hlela ikheli</translation>
<translation id="991413375315957741">izinzwa zokunyakaza noma zokukhanya</translation>
+<translation id="992110854164447044">Ikhadi le-virtual lifihla ikhadi lakho langempela ukusiza ukukuvikela ekukhwabaniseni okungase kube khona. <ph name="IDS_AUTOFILL_VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK_LABEL" /></translation>
<translation id="992115559265932548"><ph name="MICROSOFT_ACTIVE_DIRECTORY" /></translation>
<translation id="992256792861109788">Okuphinki</translation>
<translation id="992432478773561401">"<ph name="SOFTWARE_NAME" />" ayifakiwe kahle kwikhompyutha yakho noma inethiwekhi:
diff --git a/chromium/components/subresource_filter/android/BUILD.gn b/chromium/components/subresource_filter/android/BUILD.gn
index 88300eb8a5f..1f44b7c28cc 100644
--- a/chromium/components/subresource_filter/android/BUILD.gn
+++ b/chromium/components/subresource_filter/android/BUILD.gn
@@ -22,6 +22,8 @@ android_library("java") {
":java_resources",
":subresource_filter_jni_headers",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/infobars/android:infobar_android_enums_java",
"//components/infobars/android:java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/components/subresource_filter/content/browser/ad_tagging_browser_test_utils.cc b/chromium/components/subresource_filter/content/browser/ad_tagging_browser_test_utils.cc
index fb7520ea100..138c50ab95c 100644
--- a/chromium/components/subresource_filter/content/browser/ad_tagging_browser_test_utils.cc
+++ b/chromium/components/subresource_filter/content/browser/ad_tagging_browser_test_utils.cc
@@ -36,7 +36,9 @@ RenderFrameHost* CreateFrameImpl(const content::ToRenderFrameHost& adapter,
std::string ad_type = ad_script ? "Ad" : "";
RenderFrameHost* previous_most_recent_fenced_frame =
- FencedFrameTestHelper::GetMostRecentlyAddedFencedFrame(rfh);
+ is_fenced_frame
+ ? FencedFrameTestHelper::GetMostRecentlyAddedFencedFrame(rfh)
+ : nullptr;
if (is_prerender) {
// TODO(bokan): We must avoid using a TestNavigationObserver if executing
diff --git a/chromium/components/subresource_filter/content/browser/ads_blocked_message_delegate.cc b/chromium/components/subresource_filter/content/browser/ads_blocked_message_delegate.cc
index bb298bf2aa9..7ca9b5d179d 100644
--- a/chromium/components/subresource_filter/content/browser/ads_blocked_message_delegate.cc
+++ b/chromium/components/subresource_filter/content/browser/ads_blocked_message_delegate.cc
@@ -63,9 +63,9 @@ void AdsBlockedMessageDelegate::ShowMessage() {
message->SetSecondaryIconResourceId(
message_dispatcher_bridge->MapToJavaDrawableId(IDR_ANDROID_SETTINGS));
- message->SetSecondaryActionCallback(
- base::BindOnce(&AdsBlockedMessageDelegate::HandleMessageManageClicked,
- base::Unretained(this)));
+ message->SetSecondaryActionCallback(base::BindRepeating(
+ &AdsBlockedMessageDelegate::HandleMessageManageClicked,
+ base::Unretained(this)));
// TODO(crbug.com/1223078): On rare occasions, such as the moment when
// activity is being recreated or destroyed, ads blocked message will not be
diff --git a/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc b/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
index 54345af1b4c..4749b13c1a2 100644
--- a/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
+++ b/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
@@ -209,7 +209,7 @@ class ContentSubresourceFilterThrottleManagerTest
content::RenderViewHostTestHarness::SetUp();
content::WebContents* web_contents =
RenderViewHostTestHarness::web_contents();
- CreateAgentForHost(web_contents->GetMainFrame());
+ CreateAgentForHost(web_contents->GetPrimaryMainFrame());
// Initialize the ruleset dealer. Allowlisted URLs must also match a
// disallowed rule in order to work correctly.
@@ -288,15 +288,9 @@ class ContentSubresourceFilterThrottleManagerTest
void CreateTestNavigation(const GURL& url,
content::RenderFrameHost* render_frame_host) {
DCHECK(render_frame_host);
- if (render_frame_host->IsFencedFrameRoot()) {
- navigation_simulator_ =
- content::NavigationSimulator::CreateForFencedFrame(url,
- render_frame_host);
- } else {
- navigation_simulator_ =
- content::NavigationSimulator::CreateRendererInitiated(
- url, render_frame_host);
- }
+ navigation_simulator_ =
+ content::NavigationSimulator::CreateRendererInitiated(
+ url, render_frame_host);
}
content::NavigationSimulator* navigation_simulator() {
@@ -346,7 +340,8 @@ class ContentSubresourceFilterThrottleManagerTest
bool ads_blocked_in_content_settings() {
auto* content_settings =
content_settings::PageSpecificContentSettings::GetForFrame(
- content::RenderViewHostTestHarness::web_contents()->GetMainFrame());
+ content::RenderViewHostTestHarness::web_contents()
+ ->GetPrimaryMainFrame());
return content_settings->IsContentBlocked(ContentSettingsType::ADS);
}
@@ -1749,6 +1744,7 @@ TEST_P(ContentSubresourceFilterThrottleManagerFencedFrameTest,
throttle_manager);
navigation_simulator()->Commit();
+ fenced_frame_root = navigation_simulator()->GetFinalRenderFrameHost();
// Committing the fenced frame navigation should not change the Page's
// throttle manager.
diff --git a/chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.cc b/chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.cc
index 26456dd1cb0..98c82769cdc 100644
--- a/chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.cc
+++ b/chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.cc
@@ -94,9 +94,6 @@ bool FakeSafeBrowsingDatabaseManager::CheckResourceUrl(const GURL& url,
return true;
}
-bool FakeSafeBrowsingDatabaseManager::IsSupported() const {
- return true;
-}
bool FakeSafeBrowsingDatabaseManager::ChecksAreAlwaysAsync() const {
return false;
}
diff --git a/chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h b/chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h
index a742882d8ed..65aa98ac3e2 100644
--- a/chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h
+++ b/chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h
@@ -48,7 +48,6 @@ class FakeSafeBrowsingDatabaseManager
// safe_browsing::TestSafeBrowsingDatabaseManager:
bool CheckUrlForSubresourceFilter(const GURL& url, Client* client) override;
bool CheckResourceUrl(const GURL& url, Client* client) override;
- bool IsSupported() const override;
void CancelCheck(Client* client) override;
bool ChecksAreAlwaysAsync() const override;
bool CanCheckRequestDestination(
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
index 36e52fbbb1f..463c5b23ebd 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
@@ -923,7 +923,7 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
SimulateStartAndExpectProceed(url);
navigation_simulator()->SetReferrer(blink::mojom::Referrer::New(
RenderViewHostTestHarness::web_contents()
- ->GetMainFrame()
+ ->GetPrimaryMainFrame()
->GetLastCommittedURL(),
network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
SimulateCommitAndExpectProceed();
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.cc b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.cc
index f8443b39c6d..76f94c34d08 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.cc
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.cc
@@ -48,7 +48,6 @@ void SubresourceFilterSafeBrowsingClientRequest::Start(const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
// Just return SAFE if the database is not supported.
bool synchronous_finish =
- !database_manager_->IsSupported() ||
database_manager_->CheckUrlForSubresourceFilter(url, this);
if (synchronous_finish) {
request_completed_ = true;
diff --git a/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc b/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc
index 185921e5387..819c5a63e91 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -44,7 +44,7 @@ class CommaSeparatedStrings {
bool CaseInsensitiveContains(base::StringPiece lowercase_key) const {
const auto predicate = [lowercase_key](base::StringPiece element) {
- return base::LowerCaseEqualsASCII(element, lowercase_key);
+ return base::EqualsCaseInsensitiveASCII(element, lowercase_key);
};
return std::find_if(pieces_.begin(), pieces_.end(), predicate) !=
pieces_.end();
@@ -68,18 +68,21 @@ std::string TakeVariationParamOrReturnEmpty(
mojom::ActivationLevel ParseActivationLevel(
const base::StringPiece activation_level) {
- if (base::LowerCaseEqualsASCII(activation_level, kActivationLevelEnabled))
+ if (base::EqualsCaseInsensitiveASCII(activation_level,
+ kActivationLevelEnabled))
return mojom::ActivationLevel::kEnabled;
- else if (base::LowerCaseEqualsASCII(activation_level, kActivationLevelDryRun))
+ else if (base::EqualsCaseInsensitiveASCII(activation_level,
+ kActivationLevelDryRun))
return mojom::ActivationLevel::kDryRun;
return mojom::ActivationLevel::kDisabled;
}
ActivationScope ParseActivationScope(const base::StringPiece activation_scope) {
- if (base::LowerCaseEqualsASCII(activation_scope, kActivationScopeAllSites))
+ if (base::EqualsCaseInsensitiveASCII(activation_scope,
+ kActivationScopeAllSites))
return ActivationScope::ALL_SITES;
- else if (base::LowerCaseEqualsASCII(activation_scope,
- kActivationScopeActivationList))
+ else if (base::EqualsCaseInsensitiveASCII(activation_scope,
+ kActivationScopeActivationList))
return ActivationScope::ACTIVATION_LIST;
return ActivationScope::NO_SITES;
}
diff --git a/chromium/components/sync/BUILD.gn b/chromium/components/sync/BUILD.gn
index 60cf1de55a6..8f63797014a 100644
--- a/chromium/components/sync/BUILD.gn
+++ b/chromium/components/sync/BUILD.gn
@@ -132,6 +132,7 @@ source_set("unit_tests") {
sources = [
"base/client_tag_hash_unittest.cc",
"base/model_type_unittest.cc",
+ "base/page_transition_conversion_unittest.cc",
"base/protobuf_unittest.cc",
"base/sync_prefs_unittest.cc",
"base/sync_util_unittest.cc",
@@ -219,6 +220,7 @@ source_set("unit_tests") {
"trusted_vault/trusted_vault_connection_impl_unittest.cc",
"trusted_vault/trusted_vault_crypto_unittest.cc",
"trusted_vault/trusted_vault_request_unittest.cc",
+ "trusted_vault/trusted_vault_server_constants_unittest.cc",
]
}
@@ -270,6 +272,7 @@ source_set("unit_tests") {
"//third_party/leveldatabase",
"//third_party/protobuf:protobuf_lite",
"//third_party/zlib/google:compression_utils",
+ "//ui/base",
"//url",
]
diff --git a/chromium/components/sync/android/BUILD.gn b/chromium/components/sync/android/BUILD.gn
index 0c1a5d70e7e..4ebefb2b199 100644
--- a/chromium/components/sync/android/BUILD.gn
+++ b/chromium/components/sync/android/BUILD.gn
@@ -7,16 +7,13 @@ import("//build/config/android/rules.gni")
android_library("sync_java") {
deps = [
- "//base:base_java",
"//net/android:net_java",
"//third_party/android_deps:com_google_code_findbugs_jsr305_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
srcjar_deps = [ ":java_enums" ]
- sources = [
- "java/src/org/chromium/components/sync/Passphrase.java",
- ]
+ sources = [ "java/src/org/chromium/components/sync/Passphrase.java" ]
}
java_cpp_enum("java_enums") {
diff --git a/chromium/components/sync/base/BUILD.gn b/chromium/components/sync/base/BUILD.gn
index b97d4084ff7..96eeda61639 100644
--- a/chromium/components/sync/base/BUILD.gn
+++ b/chromium/components/sync/base/BUILD.gn
@@ -33,6 +33,8 @@ static_library("base") {
"legacy_directory_deletion.h",
"model_type.cc",
"model_type.h",
+ "page_transition_conversion.cc",
+ "page_transition_conversion.h",
"passphrase_enums.cc",
"passphrase_enums.h",
"pref_names.h",
diff --git a/chromium/components/sync/driver/resources/BUILD.gn b/chromium/components/sync/driver/resources/BUILD.gn
index dfc3af9fcdd..9ec0770abe3 100644
--- a/chromium/components/sync/driver/resources/BUILD.gn
+++ b/chromium/components/sync/driver/resources/BUILD.gn
@@ -106,10 +106,7 @@ js_library("search") {
}
js_library("sync_index") {
- deps = [
- "//ui/webui/resources/js:util.m",
- "//ui/webui/resources/js/cr/ui:tabs",
- ]
+ deps = [ "//ui/webui/resources/js:util.m" ]
}
js_library("sync_log") {
diff --git a/chromium/components/sync/engine/BUILD.gn b/chromium/components/sync/engine/BUILD.gn
index e9fcf08e35b..e000e2ef469 100644
--- a/chromium/components/sync/engine/BUILD.gn
+++ b/chromium/components/sync/engine/BUILD.gn
@@ -47,8 +47,6 @@ static_library("engine") {
"cycle/sync_cycle_snapshot.h",
"data_type_activation_response.cc",
"data_type_activation_response.h",
- "data_type_debug_info_listener.cc",
- "data_type_debug_info_listener.h",
"debug_info_event_listener.cc",
"debug_info_event_listener.h",
"engine_components_factory.h",
diff --git a/chromium/components/sync/protocol/protocol_sources.gni b/chromium/components/sync/protocol/protocol_sources.gni
index 000d0ff8cf2..d73f98f2fd6 100644
--- a/chromium/components/sync/protocol/protocol_sources.gni
+++ b/chromium/components/sync/protocol/protocol_sources.gni
@@ -3,10 +3,10 @@
# found in the LICENSE file.
sync_protocol_sources = [
+ "app_list_specifics.proto",
"app_notification_specifics.proto",
"app_setting_specifics.proto",
"app_specifics.proto",
- "app_list_specifics.proto",
"arc_package_specifics.proto",
"autofill_offer_specifics.proto",
"autofill_specifics.proto",
@@ -28,6 +28,7 @@ sync_protocol_sources = [
"gaia_password_reuse.proto",
"get_updates_caller_info.proto",
"history_delete_directive_specifics.proto",
+ "history_specifics.proto",
"history_status.proto",
"list_passwords_result.proto",
"local_trusted_vault.proto",
diff --git a/chromium/components/sync_bookmarks/BUILD.gn b/chromium/components/sync_bookmarks/BUILD.gn
index 1b76549b77a..c359c1f51ea 100644
--- a/chromium/components/sync_bookmarks/BUILD.gn
+++ b/chromium/components/sync_bookmarks/BUILD.gn
@@ -69,6 +69,7 @@ source_set("unit_tests") {
"//skia",
"//testing/gmock",
"//testing/gtest",
+ "//ui/base",
"//ui/gfx",
]
}
diff --git a/chromium/components/sync_bookmarks/bookmark_model_merger.cc b/chromium/components/sync_bookmarks/bookmark_model_merger.cc
index ad86f18a912..996571963df 100644
--- a/chromium/components/sync_bookmarks/bookmark_model_merger.cc
+++ b/chromium/components/sync_bookmarks/bookmark_model_merger.cc
@@ -593,7 +593,7 @@ void BookmarkModelMerger::Merge() {
DCHECK_EQ(permanent_folder->guid(),
GetPermanentFolderGUIDForServerDefinedUniqueTag(
server_defined_unique_tag));
- MergeSubtree(/*local_subtree_root=*/permanent_folder,
+ MergeSubtree(/*local_node=*/permanent_folder,
/*remote_node=*/root);
}
diff --git a/chromium/components/sync_bookmarks/bookmark_model_merger_unittest.cc b/chromium/components/sync_bookmarks/bookmark_model_merger_unittest.cc
index 7b49c8af82c..bf31be698ab 100644
--- a/chromium/components/sync_bookmarks/bookmark_model_merger_unittest.cc
+++ b/chromium/components/sync_bookmarks/bookmark_model_merger_unittest.cc
@@ -31,7 +31,6 @@
using testing::_;
using testing::Eq;
using testing::IsNull;
-using testing::Ne;
using testing::NotNull;
using testing::UnorderedElementsAre;
@@ -1696,7 +1695,7 @@ TEST(BookmarkModelMergerTest, ShouldLogMetricsForInvalidSpecifics) {
histogram_tester.ExpectUniqueSample(
"Sync.ProblematicServerSideBookmarksDuringMerge",
/*sample=*/ExpectedRemoteBookmarkUpdateError::kInvalidSpecifics,
- /*count=*/1);
+ /*expected_bucket_count=*/1);
}
TEST(BookmarkModelMergerTest, ShouldLogMetricsForChildrenOfNonFolder) {
@@ -1740,7 +1739,7 @@ TEST(BookmarkModelMergerTest, ShouldLogMetricsForChildrenOfNonFolder) {
histogram_tester.ExpectUniqueSample(
"Sync.ProblematicServerSideBookmarksDuringMerge",
/*sample=*/ExpectedRemoteBookmarkUpdateError::kParentNotFolder,
- /*count=*/3);
+ /*expected_bucket_count=*/3);
}
TEST(BookmarkModelMergerTest, ShouldLogMetricsForChildrenOfOrphanUpdates) {
@@ -1771,7 +1770,7 @@ TEST(BookmarkModelMergerTest, ShouldLogMetricsForChildrenOfOrphanUpdates) {
histogram_tester.ExpectUniqueSample(
"Sync.ProblematicServerSideBookmarksDuringMerge",
/*sample=*/ExpectedRemoteBookmarkUpdateError::kMissingParentEntity,
- /*count=*/1);
+ /*expected_bucket_count=*/1);
EXPECT_THAT(histogram_tester.GetTotalSum(
"Sync.BookmarkModelMerger.ReachableInputUpdates"),
Eq(1));
@@ -1792,7 +1791,7 @@ TEST(BookmarkModelMergerTest, ShouldLogMetricsForUnsupportedServerTag) {
histogram_tester.ExpectUniqueSample(
"Sync.ProblematicServerSideBookmarksDuringMerge",
/*sample=*/ExpectedRemoteBookmarkUpdateError::kUnsupportedPermanentFolder,
- /*count=*/1);
+ /*expected_bucket_count=*/1);
}
TEST(BookmarkModelMergerTest, ShouldLogMetricsForkDescendantOfRootNode) {
@@ -1822,7 +1821,7 @@ TEST(BookmarkModelMergerTest, ShouldLogMetricsForkDescendantOfRootNode) {
histogram_tester.ExpectUniqueSample(
"Sync.ProblematicServerSideBookmarksDuringMerge",
/*sample=*/ExpectedRemoteBookmarkUpdateError::kMissingParentEntity,
- /*count=*/1);
+ /*expected_bucket_count=*/1);
}
TEST(BookmarkModelMergerTest, ShouldRemoveMatchingDuplicatesByGUID) {
@@ -1881,7 +1880,8 @@ TEST(BookmarkModelMergerTest, ShouldRemoveMatchingDuplicatesByGUID) {
Eq(4));
histogram_tester.ExpectBucketCount(
"Sync.BookmarksGUIDDuplicates",
- /*sample=*/ExpectedBookmarksGUIDDuplicates::kMatchingUrls, /*count=*/1);
+ /*sample=*/ExpectedBookmarksGUIDDuplicates::kMatchingUrls,
+ /*expected_count=*/1);
EXPECT_THAT(histogram_tester.GetTotalSum(
"Sync.BookmarkModelMerger.ReachableInputUpdates"),
Eq(3));
@@ -1930,7 +1930,8 @@ TEST(BookmarkModelMergerTest, ShouldRemoveDifferentDuplicatesByGUID) {
UnorderedElementsAre(HasTitle(base::UTF8ToUTF16(kTitle1))));
histogram_tester.ExpectBucketCount(
"Sync.BookmarksGUIDDuplicates",
- /*sample=*/ExpectedBookmarksGUIDDuplicates::kDifferentUrls, /*count=*/1);
+ /*sample=*/ExpectedBookmarksGUIDDuplicates::kDifferentUrls,
+ /*expected_count=*/1);
}
TEST(BookmarkModelMergerTest, ShouldRemoveMatchingFolderDuplicatesByGUID) {
@@ -1973,7 +1974,7 @@ TEST(BookmarkModelMergerTest, ShouldRemoveMatchingFolderDuplicatesByGUID) {
histogram_tester.ExpectBucketCount(
"Sync.BookmarksGUIDDuplicates",
/*sample=*/ExpectedBookmarksGUIDDuplicates::kMatchingFolders,
- /*count=*/1);
+ /*expected_count=*/1);
EXPECT_THAT(tracker->GetEntityForSyncId("Id1"), IsNull());
EXPECT_THAT(tracker->GetEntityForSyncId("Id2"), NotNull());
}
@@ -2029,7 +2030,7 @@ TEST(BookmarkModelMergerTest, ShouldRemoveDifferentFolderDuplicatesByGUID) {
histogram_tester.ExpectBucketCount(
"Sync.BookmarksGUIDDuplicates",
/*sample=*/ExpectedBookmarksGUIDDuplicates::kDifferentFolders,
- /*count=*/1);
+ /*expected_count=*/1);
EXPECT_THAT(tracker->GetEntityForSyncId("Id1"), NotNull());
EXPECT_THAT(tracker->GetEntityForSyncId("Id2"), IsNull());
EXPECT_EQ(bookmark_bar_node->children().front()->GetTitle(),
@@ -2184,7 +2185,7 @@ TEST(BookmarkModelMergerTest, ShouldRemoveDifferentTypeDuplicatesByGUID) {
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksGUIDDuplicates",
/*sample=*/ExpectedBookmarksGUIDDuplicates::kDifferentTypes,
- /*count=*/1);
+ /*expected_bucket_count=*/1);
EXPECT_THAT(tracker->GetEntityForSyncId("Id1"), NotNull());
EXPECT_THAT(tracker->GetEntityForSyncId("Id2"), IsNull());
EXPECT_EQ(bookmark_bar_node->children().front()->children().size(), 1u);
diff --git a/chromium/components/sync_bookmarks/bookmark_model_observer_impl.cc b/chromium/components/sync_bookmarks/bookmark_model_observer_impl.cc
index 9e29b72efdd..9ef9d34f9b5 100644
--- a/chromium/components/sync_bookmarks/bookmark_model_observer_impl.cc
+++ b/chromium/components/sync_bookmarks/bookmark_model_observer_impl.cc
@@ -75,6 +75,7 @@ void BookmarkModelObserverImpl::BookmarkNodeMoved(
// Mark the entity that it needs to be committed.
bookmark_tracker_->IncrementSequenceNumber(entity);
nudge_for_commit_closure_.Run();
+ bookmark_tracker_->CheckAllNodesTracked(model);
}
void BookmarkModelObserverImpl::BookmarkNodeAdded(
@@ -119,6 +120,10 @@ void BookmarkModelObserverImpl::BookmarkNodeAdded(
// Mark the entity that it needs to be committed.
bookmark_tracker_->IncrementSequenceNumber(entity);
nudge_for_commit_closure_.Run();
+
+ // Do not check if all nodes are tracked because it's still possible that some
+ // nodes are untracked, e.g. if current node has been just restored and its
+ // children will be added soon.
}
void BookmarkModelObserverImpl::OnWillRemoveBookmarks(
@@ -130,7 +135,7 @@ void BookmarkModelObserverImpl::OnWillRemoveBookmarks(
return;
}
bookmark_tracker_->CheckAllNodesTracked(model);
- ProcessDelete(parent, node);
+ ProcessDelete(node);
nudge_for_commit_closure_.Run();
}
@@ -147,11 +152,13 @@ void BookmarkModelObserverImpl::BookmarkNodeRemoved(
void BookmarkModelObserverImpl::OnWillRemoveAllUserBookmarks(
bookmarks::BookmarkModel* model) {
+ bookmark_tracker_->CheckAllNodesTracked(model);
const bookmarks::BookmarkNode* root_node = model->root_node();
for (const auto& permanent_node : root_node->children()) {
for (const auto& child : permanent_node->children()) {
- if (model->client()->CanSyncNode(child.get()))
- ProcessDelete(permanent_node.get(), child.get());
+ if (model->client()->CanSyncNode(child.get())) {
+ ProcessDelete(child.get());
+ }
}
}
nudge_for_commit_closure_.Run();
@@ -161,6 +168,7 @@ void BookmarkModelObserverImpl::BookmarkAllUserNodesRemoved(
bookmarks::BookmarkModel* model,
const std::set<GURL>& removed_urls) {
// All the work should have already been done in OnWillRemoveAllUserBookmarks.
+ bookmark_tracker_->CheckAllNodesTracked(model);
}
void BookmarkModelObserverImpl::BookmarkNodeChanged(
@@ -261,15 +269,14 @@ void BookmarkModelObserverImpl::BookmarkNodeChildrenReordered(
// The given node's children got reordered. We need to reorder all the
// corresponding sync node.
-
- // TODO(crbug/com/516866): Make sure that single-move case doesn't produce
- // unnecessary updates. One approach would be something like:
+ // TODO(crbug.com/1321519): children reordering is used to move one bookmark
+ // on Android, it should be either taken into account here or another bridge
+ // interface should be provided. One approach would be something like:
// 1. Find a subsequence of elements in the beginning of the vector that is
// already sorted.
// 2. The same for the end.
// 3. If the two overlap, adjust so they don't.
// 4. Sort the middle, using Between (e.g. recursive implementation).
-
syncer::UniquePosition position;
for (const auto& child : node->children()) {
const SyncedBookmarkTrackerEntity* entity =
@@ -386,11 +393,11 @@ void BookmarkModelObserverImpl::ProcessUpdate(
}
void BookmarkModelObserverImpl::ProcessDelete(
- const bookmarks::BookmarkNode* parent,
const bookmarks::BookmarkNode* node) {
// If not a leaf node, process all children first.
- for (const auto& child : node->children())
- ProcessDelete(node, child.get());
+ for (const auto& child : node->children()) {
+ ProcessDelete(child.get());
+ }
// Process the current node.
const SyncedBookmarkTrackerEntity* entity =
bookmark_tracker_->GetEntityForBookmarkNode(node);
diff --git a/chromium/components/sync_bookmarks/bookmark_model_observer_impl.h b/chromium/components/sync_bookmarks/bookmark_model_observer_impl.h
index 90f0e7ac8a5..f1768b6f382 100644
--- a/chromium/components/sync_bookmarks/bookmark_model_observer_impl.h
+++ b/chromium/components/sync_bookmarks/bookmark_model_observer_impl.h
@@ -91,8 +91,7 @@ class BookmarkModelObserverImpl : public bookmarks::BookmarkModelObserver {
// |bookmark_tracker_| accordingly. If |node| is a bookmark, it gets marked
// as deleted and that it requires a commit. If it's a folder, it recurses
// over all children before processing the folder itself.
- void ProcessDelete(const bookmarks::BookmarkNode* parent,
- const bookmarks::BookmarkNode* node);
+ void ProcessDelete(const bookmarks::BookmarkNode* node);
// Points to the tracker owned by the processor. It keeps the mapping between
// bookmark nodes and corresponding sync server entities.
diff --git a/chromium/components/sync_bookmarks/bookmark_model_type_processor.cc b/chromium/components/sync_bookmarks/bookmark_model_type_processor.cc
index 4ffd5e42e74..a24dfd2d0cc 100644
--- a/chromium/components/sync_bookmarks/bookmark_model_type_processor.cc
+++ b/chromium/components/sync_bookmarks/bookmark_model_type_processor.cc
@@ -238,6 +238,7 @@ void BookmarkModelTypeProcessor::ModelReadyToSync(
const base::RepeatingClosure& schedule_save_closure,
bookmarks::BookmarkModel* model) {
DCHECK(model);
+ DCHECK(model->loaded());
DCHECK(!bookmark_model_);
DCHECK(!bookmark_tracker_);
DCHECK(!bookmark_model_observer_);
@@ -255,7 +256,6 @@ void BookmarkModelTypeProcessor::ModelReadyToSync(
model, std::move(model_metadata));
if (bookmark_tracker_) {
- bookmark_tracker_->CheckAllNodesTracked(bookmark_model_);
StartTrackingMetadata();
} else if (!metadata_str.empty()) {
DLOG(WARNING)
@@ -490,8 +490,9 @@ void BookmarkModelTypeProcessor::GetAllNodesForDebugging(
const bookmarks::BookmarkNode* model_root_node = bookmark_model_->root_node();
int i = 0;
- for (const auto& child : model_root_node->children())
+ for (const auto& child : model_root_node->children()) {
AppendNodeAndChildrenForDebugging(child.get(), i++, all_nodes.get());
+ }
std::move(callback).Run(syncer::BOOKMARKS, std::move(all_nodes));
}
@@ -538,9 +539,6 @@ void BookmarkModelTypeProcessor::AppendNodeAndChildrenForDebugging(
std::unique_ptr<base::DictionaryValue> data_dictionary =
data.ToDictionaryValue();
- // TODO(https://crbug.com/516866): Prepending the ID with an "s" is consistent
- // with the implementation in ClientTagBasedModelTypeProcessor. Double check
- // if this is actually needed and update both implementations if makes sense.
// Set ID value as in legacy directory-based implementation, "s" means server.
data_dictionary->SetString("ID", "s" + metadata->server_id());
if (node->is_permanent_node()) {
@@ -562,8 +560,9 @@ void BookmarkModelTypeProcessor::AppendNodeAndChildrenForDebugging(
base::Value::FromUniquePtrValue(std::move(data_dictionary)));
int i = 0;
- for (const auto& child : node->children())
+ for (const auto& child : node->children()) {
AppendNodeAndChildrenForDebugging(child.get(), i++, all_nodes);
+ }
}
void BookmarkModelTypeProcessor::GetTypeEntitiesCountForDebugging(
diff --git a/chromium/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc b/chromium/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
index eab2da9587b..83920e00348 100644
--- a/chromium/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
+++ b/chromium/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
@@ -36,13 +36,13 @@
#include "components/undo/bookmark_undo_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/models/tree_node_iterator.h"
namespace sync_bookmarks {
namespace {
using base::ASCIIToUTF16;
-using testing::_;
using testing::ElementsAre;
using testing::Eq;
using testing::IsEmpty;
@@ -129,7 +129,10 @@ sync_pb::ModelTypeState CreateDummyModelTypeState() {
// |node| must not be nullptr.
sync_pb::BookmarkMetadata CreateNodeMetadata(
const bookmarks::BookmarkNode* node,
- const std::string& server_id) {
+ const std::string& server_id,
+ const syncer::UniquePosition& unique_position =
+ syncer::UniquePosition::InitialPosition(
+ syncer::UniquePosition::RandomSuffix())) {
sync_pb::BookmarkMetadata bookmark_metadata;
bookmark_metadata.set_id(node->id());
bookmark_metadata.mutable_metadata()->set_server_id(server_id);
@@ -137,6 +140,8 @@ sync_pb::BookmarkMetadata CreateNodeMetadata(
syncer::ClientTagHash::FromUnhashed(syncer::BOOKMARKS,
node->guid().AsLowercaseString())
.value());
+ *bookmark_metadata.mutable_metadata()->mutable_unique_position() =
+ unique_position.ToProto();
return bookmark_metadata;
}
@@ -193,38 +198,6 @@ void AssertState(const BookmarkModelTypeProcessor* processor,
}
}
-// TODO(crbug.com/516866): Replace with a simpler implementation (e.g. by
-// simulating loading from metadata) instead of receiving remote updates.
-// Inititalizes the processor with the bookmarks in |bookmarks|.
-void InitWithSyncedBookmarks(const std::vector<BookmarkInfo>& bookmarks,
- BookmarkModelTypeProcessor* processor) {
- syncer::UpdateResponseDataList updates;
- syncer::UniquePosition pos = syncer::UniquePosition::InitialPosition(
- syncer::UniquePosition::RandomSuffix());
- // Add update for the permanent folders "Bookmarks bar", "Other Bookmarks" and
- // "Mobile Bookmarks".
- updates.push_back(
- CreateUpdateResponseData({kBookmarkBarId, std::string(), std::string(),
- kBookmarksRootId, kBookmarkBarTag},
- pos, /*response_version=*/0));
- updates.push_back(
- CreateUpdateResponseData({kOtherBookmarksId, std::string(), std::string(),
- kBookmarksRootId, kOtherBookmarksTag},
- pos, /*response_version=*/0));
- updates.push_back(CreateUpdateResponseData(
- {kMobileBookmarksId, std::string(), std::string(), kBookmarksRootId,
- kMobileBookmarksTag},
- pos, /*response_version=*/0));
- for (BookmarkInfo bookmark : bookmarks) {
- pos = syncer::UniquePosition::After(pos,
- syncer::UniquePosition::RandomSuffix());
- updates.push_back(
- CreateUpdateResponseData(bookmark, pos, /*response_version=*/0));
- }
- processor->OnUpdateReceived(CreateDummyModelTypeState(), std::move(updates));
- AssertState(processor, bookmarks);
-}
-
class MockCommitQueue : public syncer::CommitQueue {
public:
MOCK_METHOD(void, NudgeForCommit, (), (override));
@@ -249,13 +222,47 @@ class BookmarkModelTypeProcessorTest : public testing::Test {
: processor_(std::make_unique<BookmarkModelTypeProcessor>(
&bookmark_undo_service_)),
bookmark_model_(bookmarks::TestBookmarkClient::CreateModel()) {
- // TODO(crbug.com/516866): This class assumes model is loaded and sync has
- // started before running tests. We should test other variations (i.e. model
- // isn't loaded yet and/or sync didn't start yet).
processor_->SetFaviconService(&favicon_service_);
}
- void SimulateModelReadyToSync() {
+ // Initialized the processor with bookmarks from the existing model and always
+ // initializes permanent folders.
+ void SimulateModelReadyToSyncWithInitialSyncDone() {
+ sync_pb::BookmarkModelMetadata model_metadata =
+ CreateMetadataForPermanentNodes(bookmark_model_.get());
+ DCHECK(model_metadata.model_type_state().initial_sync_done());
+
+ // By default, set that bookmarks are reuploaded to avoid reupload logic.
+ model_metadata.set_bookmarks_hierarchy_fields_reuploaded(true);
+
+ // Rely on the order of iterating over the tree: left child is always
+ // handled before the current one. In this case increasing unique position
+ // will always represent the right order.
+ syncer::UniquePosition next_unique_position =
+ syncer::UniquePosition::InitialPosition(
+ syncer::UniquePosition::RandomSuffix());
+ ui::TreeNodeIterator<const bookmarks::BookmarkNode> iterator(
+ bookmark_model_->root_node());
+ while (iterator.has_next()) {
+ const bookmarks::BookmarkNode* node = iterator.Next();
+ if (node->is_permanent_node()) {
+ // Permanent nodes have been added explicitly.
+ continue;
+ }
+
+ *model_metadata.add_bookmarks_metadata() = CreateNodeMetadata(
+ node, /*server_id=*/"id_" + node->guid().AsLowercaseString(),
+ next_unique_position);
+ next_unique_position = syncer::UniquePosition::After(
+ next_unique_position, syncer::UniquePosition::RandomSuffix());
+ }
+ processor_->ModelReadyToSync(model_metadata.SerializeAsString(),
+ schedule_save_closure_.Get(),
+ bookmark_model_.get());
+ ASSERT_THAT(processor_->GetTrackerForTest(), NotNull());
+ }
+
+ void SimulateModelReadyToSyncWithoutLocalMetadata() {
processor_->ModelReadyToSync(
/*metadata_str=*/std::string(), schedule_save_closure_.Get(),
bookmark_model_.get());
@@ -322,7 +329,7 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldDoInitialMerge) {
syncer::UniquePosition::InitialPosition(
syncer::UniquePosition::RandomSuffix());
- SimulateModelReadyToSync();
+ SimulateModelReadyToSyncWithoutLocalMetadata();
SimulateOnSyncStarting();
syncer::UpdateResponseDataList updates;
@@ -350,13 +357,12 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldDoInitialMerge) {
histogram_tester.ExpectUniqueSample(
"Sync.ModelTypeInitialUpdateReceived",
/*sample=*/syncer::ModelTypeHistogramValue(syncer::BOOKMARKS),
- /*expected_count=*/3);
+ /*expected_bucket_count=*/3);
}
TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteCreation) {
- SimulateModelReadyToSync();
+ SimulateModelReadyToSyncWithInitialSyncDone();
SimulateOnSyncStarting();
- InitWithSyncedBookmarks(/*bookmarks=*/{}, processor());
// Add update for another node under the bookmarks bar.
const std::string kNodeId = "node_id";
@@ -386,35 +392,29 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteCreation) {
}
TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteUpdate) {
- const std::string kNodeId = "node_id";
const std::string kTitle = "title";
- const std::string kUrl = "http://www.url.com";
+ const GURL kUrl("http://www.url.com");
const syncer::UniquePosition kRandomPosition =
syncer::UniquePosition::InitialPosition(
syncer::UniquePosition::RandomSuffix());
- std::vector<BookmarkInfo> bookmarks = {
- {kNodeId, kTitle, kUrl, kBookmarkBarId, /*server_tag=*/std::string()}};
-
- SimulateModelReadyToSync();
- SimulateOnSyncStarting();
- InitWithSyncedBookmarks(bookmarks, processor());
-
- // Make sure original bookmark exists.
const bookmarks::BookmarkNode* bookmark_bar =
bookmark_model()->bookmark_bar_node();
- const bookmarks::BookmarkNode* bookmark_node =
- bookmark_bar->children().front().get();
- ASSERT_THAT(bookmark_node, NotNull());
- ASSERT_THAT(bookmark_node->GetTitle(), Eq(ASCIIToUTF16(kTitle)));
- ASSERT_THAT(bookmark_node->url(), Eq(GURL(kUrl)));
+ const bookmarks::BookmarkNode* bookmark_node = bookmark_model()->AddURL(
+ bookmark_bar, /*index=*/0, base::UTF8ToUTF16(kTitle), kUrl);
+ SimulateOnSyncStarting();
+ SimulateModelReadyToSyncWithInitialSyncDone();
+
+ const SyncedBookmarkTrackerEntity* entity =
+ processor()->GetTrackerForTest()->GetEntityForBookmarkNode(bookmark_node);
+ ASSERT_THAT(entity, NotNull());
// Process an update for the same bookmark.
const std::string kNewTitle = "new-title";
const std::string kNewUrl = "http://www.new-url.com";
syncer::UpdateResponseDataList updates;
updates.push_back(CreateUpdateResponseData(
- {kNodeId, kNewTitle, kNewUrl, kBookmarkBarId,
+ {entity->metadata()->server_id(), kNewTitle, kNewUrl, kBookmarkBarId,
/*server_tag=*/std::string()},
kRandomPosition, /*response_version=*/1, bookmark_node->guid()));
@@ -430,37 +430,33 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteUpdate) {
histogram_tester.ExpectUniqueSample(
"Sync.ModelTypeIncrementalUpdateReceived",
/*sample=*/syncer::ModelTypeHistogramValue(syncer::BOOKMARKS),
- /*expected_count=*/1);
+ /*expected_bucket_count=*/1);
}
TEST_F(
BookmarkModelTypeProcessorTest,
ShouldScheduleSaveAfterRemoteUpdateWithOnlyMetadataChangeAndReflections) {
- const std::string kNodeId = "node_id";
const std::string kTitle = "title";
- const std::string kUrl = "http://www.url.com";
+ const GURL kUrl("http://www.url.com");
const syncer::UniquePosition kRandomPosition =
syncer::UniquePosition::InitialPosition(
syncer::UniquePosition::RandomSuffix());
- std::vector<BookmarkInfo> bookmarks = {
- {kNodeId, kTitle, kUrl, kBookmarkBarId, /*server_tag=*/std::string()}};
-
- SimulateModelReadyToSync();
- SimulateOnSyncStarting();
- InitWithSyncedBookmarks(bookmarks, processor());
-
- // Make sure original bookmark exists.
const bookmarks::BookmarkNode* bookmark_bar =
bookmark_model()->bookmark_bar_node();
- const bookmarks::BookmarkNode* bookmark_node =
- bookmark_bar->children().front().get();
- ASSERT_THAT(bookmark_node, NotNull());
+ const bookmarks::BookmarkNode* bookmark_node = bookmark_model()->AddURL(
+ bookmark_bar, /*index=*/0, base::UTF8ToUTF16(kTitle), kUrl);
+ SimulateModelReadyToSyncWithInitialSyncDone();
+ SimulateOnSyncStarting();
+
+ const SyncedBookmarkTrackerEntity* entity =
+ processor()->GetTrackerForTest()->GetEntityForBookmarkNode(bookmark_node);
+ ASSERT_THAT(entity, NotNull());
// Process an update for the same bookmark with the same data.
syncer::UpdateResponseDataList updates;
updates.push_back(CreateUpdateResponseData(
- {kNodeId, kTitle, kUrl, kBookmarkBarId,
+ {entity->metadata()->server_id(), kTitle, kUrl.spec(), kBookmarkBarId,
/*server_tag=*/std::string()},
kRandomPosition, /*response_version=*/1, bookmark_node->guid()));
updates[0].response_version++;
@@ -484,7 +480,7 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldDecodeSyncMetadata) {
/*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle),
GURL(kUrl));
- SimulateModelReadyToSync();
+ SimulateModelReadyToSyncWithoutLocalMetadata();
SimulateOnSyncStarting();
sync_pb::BookmarkModelMetadata model_metadata =
@@ -508,34 +504,27 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldDecodeSyncMetadata) {
TEST_F(BookmarkModelTypeProcessorTest, ShouldDecodeEncodedSyncMetadata) {
const std::string kNodeId1 = "node_id1";
const std::string kTitle1 = "title1";
- const std::string kUrl1 = "http://www.url1.com";
+ const GURL kUrl1("http://www.url1.com");
const std::string kNodeId2 = "node_id2";
const std::string kTitle2 = "title2";
- const std::string kUrl2 = "http://www.url2.com";
+ const GURL kUrl2("http://www.url2.com");
- std::vector<BookmarkInfo> bookmarks = {
- {kNodeId1, kTitle1, kUrl1, kBookmarkBarId, /*server_tag=*/std::string()},
- {kNodeId2, kTitle2, kUrl2, kBookmarkBarId,
- /*server_tag=*/std::string()}};
- SimulateModelReadyToSync();
+ const bookmarks::BookmarkNode* bookmark_bar =
+ bookmark_model()->bookmark_bar_node();
+ bookmark_model()->AddURL(bookmark_bar, /*index=*/0,
+ base::UTF8ToUTF16(kTitle1), kUrl1);
+ bookmark_model()->AddURL(bookmark_bar, /*index=*/1,
+ base::UTF8ToUTF16(kTitle2), kUrl2);
SimulateOnSyncStarting();
- InitWithSyncedBookmarks(bookmarks, processor());
-
- std::string metadata_str = processor()->EncodeSyncMetadata();
- // TODO(crbug.com/516866): Remove this after initial sync done is properly set
- // within the processor.
- sync_pb::BookmarkModelMetadata model_metadata;
- model_metadata.ParseFromString(metadata_str);
- model_metadata.mutable_model_type_state()->set_initial_sync_done(true);
+ SimulateModelReadyToSyncWithInitialSyncDone();
// Create a new processor and init it with the same metadata str.
BookmarkModelTypeProcessor new_processor(bookmark_undo_service());
- model_metadata.SerializeToString(&metadata_str);
- new_processor.ModelReadyToSync(metadata_str, base::DoNothing(),
- bookmark_model());
+ new_processor.ModelReadyToSync(processor()->EncodeSyncMetadata(),
+ base::DoNothing(), bookmark_model());
- AssertState(&new_processor, bookmarks);
+ new_processor.GetTrackerForTest()->CheckAllNodesTracked(bookmark_model());
// Make sure shutdown doesn't crash.
DestroyBookmarkModel();
@@ -548,7 +537,7 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldDecodeEncodedSyncMetadata) {
TEST_F(BookmarkModelTypeProcessorTest, ShouldDecodeEmptyMetadata) {
// No save should be scheduled.
EXPECT_CALL(*schedule_save_closure(), Run()).Times(0);
- SimulateModelReadyToSync();
+ SimulateModelReadyToSyncWithoutLocalMetadata();
EXPECT_THAT(processor()->GetTrackerForTest(), IsNull());
}
@@ -610,10 +599,9 @@ TEST_F(BookmarkModelTypeProcessorTest,
// key name.
TEST_F(BookmarkModelTypeProcessorTest,
ShouldUpdateModelTypeStateUponHandlingRemoteUpdates) {
- SimulateModelReadyToSync();
- SimulateOnSyncStarting();
// Initialize the process to make sure the tracker has been created.
- InitWithSyncedBookmarks({}, processor());
+ SimulateModelReadyToSyncWithInitialSyncDone();
+ SimulateOnSyncStarting();
const SyncedBookmarkTracker* tracker = processor()->GetTrackerForTest();
// The encryption key name should be empty.
ASSERT_TRUE(tracker->model_type_state().encryption_key_name().empty());
@@ -640,11 +628,10 @@ TEST_F(BookmarkModelTypeProcessorTest,
// needed.
TEST_F(BookmarkModelTypeProcessorTest,
ShouldNotRecommitEntitiesWhenEncryptionIsUpToDate) {
- SimulateModelReadyToSync();
+ // Initialize the process to make sure the tracker has been created.
SimulateOnSyncStarting();
+ SimulateModelReadyToSyncWithInitialSyncDone();
SimulateConnectSync();
- // Initialize the process to make sure the tracker has been created.
- InitWithSyncedBookmarks({}, processor());
const SyncedBookmarkTracker* tracker = processor()->GetTrackerForTest();
// The encryption key name should be empty.
ASSERT_TRUE(tracker->model_type_state().encryption_key_name().empty());
@@ -677,7 +664,7 @@ TEST_F(BookmarkModelTypeProcessorTest,
// Verifies that the processor doesn't crash if sync is stopped before receiving
// remote updates or tracking metadata.
TEST_F(BookmarkModelTypeProcessorTest, ShouldStopBeforeReceivingRemoteUpdates) {
- SimulateModelReadyToSync();
+ SimulateModelReadyToSyncWithoutLocalMetadata();
SimulateOnSyncStarting();
ASSERT_THAT(processor()->GetTrackerForTest(), IsNull());
processor()->OnSyncStopping(syncer::CLEAR_METADATA);
@@ -685,10 +672,9 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldStopBeforeReceivingRemoteUpdates) {
}
TEST_F(BookmarkModelTypeProcessorTest, ShouldStopAfterReceivingRemoteUpdates) {
- SimulateModelReadyToSync();
- SimulateOnSyncStarting();
// Initialize the process to make sure the tracker has been created.
- InitWithSyncedBookmarks({}, processor());
+ SimulateModelReadyToSyncWithInitialSyncDone();
+ SimulateOnSyncStarting();
ASSERT_THAT(processor()->GetTrackerForTest(), NotNull());
processor()->OnSyncStopping(syncer::CLEAR_METADATA);
EXPECT_THAT(processor()->GetTrackerForTest(), IsNull());
@@ -809,17 +795,24 @@ TEST_F(BookmarkModelTypeProcessorTest,
}
TEST_F(BookmarkModelTypeProcessorTest, ShouldReuploadLegacyBookmarksOnStart) {
- const std::string kNodeId = "node_id";
const std::string kTitle = "title";
- const std::string kUrl = "http://www.url.com";
+ const GURL kUrl("http://www.url.com");
- std::vector<BookmarkInfo> bookmarks = {
- {kNodeId, kTitle, kUrl, kBookmarkBarId, /*server_tag=*/std::string()}};
+ const bookmarks::BookmarkNode* node =
+ bookmark_model()->AddURL(bookmark_model()->bookmark_bar_node(),
+ /*index=*/0, base::UTF8ToUTF16(kTitle), kUrl);
- SimulateModelReadyToSync();
SimulateOnSyncStarting();
+ SimulateModelReadyToSyncWithInitialSyncDone();
SimulateConnectSync();
- InitWithSyncedBookmarks(bookmarks, processor());
+
+ ASSERT_THAT(processor()->GetTrackerForTest()->GetEntityForBookmarkNode(node),
+ NotNull());
+ const std::string server_id = processor()
+ ->GetTrackerForTest()
+ ->GetEntityForBookmarkNode(node)
+ ->metadata()
+ ->server_id();
sync_pb::BookmarkModelMetadata model_metadata =
processor()->GetTrackerForTest()->BuildBookmarkModelMetadata();
@@ -842,7 +835,7 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldReuploadLegacyBookmarksOnStart) {
ASSERT_THAT(processor()->GetTrackerForTest(), NotNull());
const SyncedBookmarkTrackerEntity* entity =
- processor()->GetTrackerForTest()->GetEntityForSyncId(kNodeId);
+ processor()->GetTrackerForTest()->GetEntityForSyncId(server_id);
ASSERT_THAT(entity, NotNull());
// Entity should be synced before until first update is received.
diff --git a/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.cc b/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.cc
index 1a85a334ed1..ab13ec97357 100644
--- a/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.cc
+++ b/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.cc
@@ -671,13 +671,16 @@ void BookmarkRemoteUpdatesHandler::ProcessDelete(
bookmark_model_->Remove(node);
}
+// This method doesn't explicitly handle conflicts as a result of re-encryption:
+// remote update wins even if there wasn't a real change in specifics. However,
+// this scenario is very unlikely and hence the implementation is less
+// sophisticated than in ClientTagBasedModelTypeProcessor (it would require
+// introducing base hash specifics to track remote changes).
const SyncedBookmarkTrackerEntity*
BookmarkRemoteUpdatesHandler::ProcessConflict(
const syncer::UpdateResponseData& update,
const SyncedBookmarkTrackerEntity* tracked_entity) {
const syncer::EntityData& update_entity = update.entity;
- // TODO(crbug.com/516866): Handle the case of conflict as a result of
- // re-encryption request.
// Can only conflict with existing nodes.
DCHECK(tracked_entity);
@@ -779,8 +782,9 @@ void BookmarkRemoteUpdatesHandler::RemoveEntityAndChildrenFromTracker(
DCHECK(entity);
bookmark_tracker_->Remove(entity);
- for (const auto& child : node->children())
+ for (const auto& child : node->children()) {
RemoveEntityAndChildrenFromTracker(child.get());
+ }
}
const bookmarks::BookmarkNode* BookmarkRemoteUpdatesHandler::GetParentNode(
diff --git a/chromium/components/sync_bookmarks/bookmark_sync_service.h b/chromium/components/sync_bookmarks/bookmark_sync_service.h
index 2022a881473..5e4771d62e5 100644
--- a/chromium/components/sync_bookmarks/bookmark_sync_service.h
+++ b/chromium/components/sync_bookmarks/bookmark_sync_service.h
@@ -8,7 +8,7 @@
#include <memory>
#include <string>
-#include "base/callback.h"
+#include "base/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "components/keyed_service/core/keyed_service.h"
diff --git a/chromium/components/sync_bookmarks/parent_guid_preprocessing.cc b/chromium/components/sync_bookmarks/parent_guid_preprocessing.cc
index 40b7356aea7..bb49c31c319 100644
--- a/chromium/components/sync_bookmarks/parent_guid_preprocessing.cc
+++ b/chromium/components/sync_bookmarks/parent_guid_preprocessing.cc
@@ -9,6 +9,7 @@
#include "base/check.h"
#include "base/guid.h"
+#include "base/memory/raw_ptr.h"
#include "base/strings/string_piece.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/sync/protocol/bookmark_specifics.pb.h"
@@ -126,7 +127,7 @@ class LazySyncIdToGuidMapInUpdates {
}
}
- const syncer::UpdateResponseDataList* const updates_;
+ const raw_ptr<const syncer::UpdateResponseDataList> updates_;
bool initialized_ = false;
std::
unordered_map<base::StringPiece, base::StringPiece, base::StringPieceHash>
diff --git a/chromium/components/sync_bookmarks/synced_bookmark_tracker.cc b/chromium/components/sync_bookmarks/synced_bookmark_tracker.cc
index 44b1766a74d..a129b0799df 100644
--- a/chromium/components/sync_bookmarks/synced_bookmark_tracker.cc
+++ b/chromium/components/sync_bookmarks/synced_bookmark_tracker.cc
@@ -235,8 +235,6 @@ void SyncedBookmarkTracker::Update(const SyncedBookmarkTrackerEntity* entity,
mutable_entity->metadata()->mutable_specifics_hash());
mutable_entity->metadata()->set_bookmark_favicon_hash(
base::PersistentHash(specifics.bookmark().favicon()));
- // TODO(crbug.com/516866): in case of conflict, the entity might exist in
- // |ordered_local_tombstones_| as well if it has been locally deleted.
}
void SyncedBookmarkTracker::UpdateServerVersion(
@@ -305,8 +303,6 @@ void SyncedBookmarkTracker::IncrementSequenceNumber(
DCHECK(!entity->bookmark_node() ||
!entity->bookmark_node()->is_permanent_node());
- // TODO(crbug.com/516866): Update base hash specifics here if the entity is
- // not already out of sync.
AsMutableEntity(entity)->metadata()->set_sequence_number(
entity->metadata()->sequence_number() + 1);
}
@@ -587,8 +583,9 @@ SyncedBookmarkTracker::ReorderUnsyncedEntitiesExceptDeletions(
// Remove those who are direct children of another node.
for (const SyncedBookmarkTrackerEntity* entity : entities) {
const bookmarks::BookmarkNode* node = entity->bookmark_node();
- for (const auto& child : node->children())
+ for (const auto& child : node->children()) {
nodes.erase(child.get());
+ }
}
// |nodes| contains only roots of all trees in the forest all of which are
// ready to be processed because their parents have no pending updates.
@@ -677,7 +674,6 @@ void SyncedBookmarkTracker::UpdateUponCommitResponse(
const std::string& sync_id,
int64_t server_version,
int64_t acked_sequence_number) {
- // TODO(crbug.com/516866): Update specifics if we decide to keep it.
DCHECK(entity);
SyncedBookmarkTrackerEntity* mutable_entity = AsMutableEntity(entity);
@@ -774,32 +770,22 @@ void SyncedBookmarkTracker::ClearSpecificsHashForTest(
void SyncedBookmarkTracker::CheckAllNodesTracked(
const bookmarks::BookmarkModel* bookmark_model) const {
- // TODO(crbug.com/516866): The method is added to debug some crashes.
- // Since it's relatively expensive, it should run on debug enabled
- // builds only after the root cause is found.
- CHECK(GetEntityForBookmarkNode(bookmark_model->bookmark_bar_node()));
- CHECK(GetEntityForBookmarkNode(bookmark_model->other_node()));
- CHECK(GetEntityForBookmarkNode(bookmark_model->mobile_node()));
+#if DCHECK_IS_ON()
+ DCHECK(GetEntityForBookmarkNode(bookmark_model->bookmark_bar_node()));
+ DCHECK(GetEntityForBookmarkNode(bookmark_model->other_node()));
+ DCHECK(GetEntityForBookmarkNode(bookmark_model->mobile_node()));
ui::TreeNodeIterator<const bookmarks::BookmarkNode> iterator(
bookmark_model->root_node());
while (iterator.has_next()) {
const bookmarks::BookmarkNode* node = iterator.Next();
if (!bookmark_model->client()->CanSyncNode(node)) {
- // TODO(crbug.com/516866): The below CHECK is added to debug some crashes.
- // Should be converted to a DCHECK after the root cause if found.
- CHECK(!GetEntityForBookmarkNode(node));
- continue;
- }
- // Root node is usually tracked, unless the sync data has been provided by
- // the USS migrator.
- if (node == bookmark_model->root_node()) {
+ DCHECK(!GetEntityForBookmarkNode(node));
continue;
}
- // TODO(crbug.com/516866): The below CHECK is added to debug some crashes.
- // Should be converted to a DCHECK after the root cause if found.
- CHECK(GetEntityForBookmarkNode(node));
+ DCHECK(GetEntityForBookmarkNode(node));
}
+#endif // DCHECK_IS_ON()
}
} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc b/chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
index e82431997c4..489920930f8 100644
--- a/chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
+++ b/chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
@@ -235,8 +235,6 @@ TEST(SyncedBookmarkTrackerTest,
EXPECT_THAT(tracker->HasLocalChanges(), Eq(false));
tracker->IncrementSequenceNumber(entity);
EXPECT_THAT(tracker->HasLocalChanges(), Eq(true));
- // TODO(crbug.com/516866): Test HasLocalChanges after submitting commit
- // request in a separate test probably.
}
TEST(SyncedBookmarkTrackerTest, ShouldAckSequenceNumber) {
@@ -636,7 +634,8 @@ TEST(SyncedBookmarkTrackerTest, ShouldNotInvalidateMetadata) {
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
- /*sample=*/ExpectedCorruptionReason::NO_CORRUPTION, /*expected_count=*/1);
+ /*sample=*/ExpectedCorruptionReason::NO_CORRUPTION,
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest, ShouldNotRequireClientTagsForPermanentNodes) {
@@ -686,7 +685,8 @@ TEST(SyncedBookmarkTrackerTest, ShouldInvalidateMetadataIfMissingMobileFolder) {
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
- /*sample=*/ExpectedCorruptionReason::UNTRACKED_BOOKMARK, /*count=*/1);
+ /*sample=*/ExpectedCorruptionReason::UNTRACKED_BOOKMARK,
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest, ShouldInvalidateMetadataIfMissingServerId) {
@@ -709,7 +709,8 @@ TEST(SyncedBookmarkTrackerTest, ShouldInvalidateMetadataIfMissingServerId) {
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
- /*sample=*/ExpectedCorruptionReason::MISSING_SERVER_ID, /*count=*/1);
+ /*sample=*/ExpectedCorruptionReason::MISSING_SERVER_ID,
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest,
@@ -736,7 +737,8 @@ TEST(SyncedBookmarkTrackerTest,
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
- /*sample=*/ExpectedCorruptionReason::MISSING_BOOKMARK_ID, /*count=*/1);
+ /*sample=*/ExpectedCorruptionReason::MISSING_BOOKMARK_ID,
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest,
@@ -763,7 +765,7 @@ TEST(SyncedBookmarkTrackerTest,
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
/*sample=*/ExpectedCorruptionReason::BOOKMARK_ID_IN_TOMBSTONE,
- /*count=*/1);
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest,
@@ -791,7 +793,7 @@ TEST(SyncedBookmarkTrackerTest,
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
/*sample=*/ExpectedCorruptionReason::UNKNOWN_BOOKMARK_ID,
- /*count=*/1);
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest, ShouldInvalidateMetadataIfGuidMismatch) {
@@ -819,7 +821,8 @@ TEST(SyncedBookmarkTrackerTest, ShouldInvalidateMetadataIfGuidMismatch) {
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
- /*sample=*/ExpectedCorruptionReason::BOOKMARK_GUID_MISMATCH, /*count=*/1);
+ /*sample=*/ExpectedCorruptionReason::BOOKMARK_GUID_MISMATCH,
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest,
@@ -861,7 +864,7 @@ TEST(SyncedBookmarkTrackerTest,
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
/*sample=*/ExpectedCorruptionReason::DUPLICATED_CLIENT_TAG_HASH,
- /*count=*/1);
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest,
@@ -889,7 +892,7 @@ TEST(SyncedBookmarkTrackerTest,
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
/*sample=*/ExpectedCorruptionReason::MISSING_CLIENT_TAG_HASH,
- /*count=*/1);
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest,
@@ -920,7 +923,8 @@ TEST(SyncedBookmarkTrackerTest,
IsNull());
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
- /*sample=*/ExpectedCorruptionReason::TRACKED_MANAGED_NODE, /*count=*/1);
+ /*sample=*/ExpectedCorruptionReason::TRACKED_MANAGED_NODE,
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest,
@@ -946,7 +950,8 @@ TEST(SyncedBookmarkTrackerTest,
NotNull());
histogram_tester.ExpectUniqueSample(
"Sync.BookmarksModelMetadataCorruptionReason",
- /*sample=*/ExpectedCorruptionReason::NO_CORRUPTION, /*count=*/1);
+ /*sample=*/ExpectedCorruptionReason::NO_CORRUPTION,
+ /*expected_bucket_count=*/1);
}
TEST(SyncedBookmarkTrackerTest,
diff --git a/chromium/components/sync_device_info/device_info.h b/chromium/components/sync_device_info/device_info.h
index 0c856c39529..fd022044bba 100644
--- a/chromium/components/sync_device_info/device_info.h
+++ b/chromium/components/sync_device_info/device_info.h
@@ -11,7 +11,6 @@
#include <string>
#include <vector>
-#include "base/callback.h"
#include "base/time/time.h"
#include "components/sync/base/model_type.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chromium/components/sync_device_info/device_info_sync_bridge.cc b/chromium/components/sync_device_info/device_info_sync_bridge.cc
index 709b63f9fb7..3922345285e 100644
--- a/chromium/components/sync_device_info/device_info_sync_bridge.cc
+++ b/chromium/components/sync_device_info/device_info_sync_bridge.cc
@@ -37,7 +37,6 @@ namespace syncer {
using base::Time;
using sync_pb::DeviceInfoSpecifics;
using sync_pb::FeatureSpecificFields;
-using sync_pb::ModelTypeState;
using sync_pb::SharingSpecificFields;
using Record = ModelTypeStore::Record;
@@ -595,8 +594,9 @@ void DeviceInfoSyncBridge::ForcePulseForTest() {
}
void DeviceInfoSyncBridge::NotifyObservers() {
- for (auto& observer : observers_)
+ for (auto& observer : observers_) {
observer.OnDeviceInfoChange();
+ }
}
void DeviceInfoSyncBridge::StoreSpecifics(
@@ -623,8 +623,9 @@ std::string DeviceInfoSyncBridge::GetLocalClientName() const {
// |sync_mode_| may not be ready when this function is called.
if (!sync_mode_) {
auto device_it = all_data_.find(local_cache_guid_);
- if (device_it != all_data_.end())
+ if (device_it != all_data_.end()) {
return device_it->second->client_name();
+ }
}
return sync_mode_ == SyncMode::kFull
diff --git a/chromium/components/sync_device_info/device_info_sync_bridge_unittest.cc b/chromium/components/sync_device_info/device_info_sync_bridge_unittest.cc
index 2d36de421f5..63425fb90ac 100644
--- a/chromium/components/sync_device_info/device_info_sync_bridge_unittest.cc
+++ b/chromium/components/sync_device_info/device_info_sync_bridge_unittest.cc
@@ -49,7 +49,6 @@
namespace syncer {
namespace {
-using base::OneShotTimer;
using sync_pb::DeviceInfoSpecifics;
using sync_pb::EntitySpecifics;
using sync_pb::ModelTypeState;
@@ -87,8 +86,10 @@ MATCHER_P(EqualsProto, expected, "") {
}
MATCHER_P(ModelEqualsSpecifics, expected_specifics, "") {
- if (expected_specifics.has_sharing_fields() != arg.sharing_info().has_value())
+ if (expected_specifics.has_sharing_fields() !=
+ arg.sharing_info().has_value()) {
return false;
+ }
if (expected_specifics.has_sharing_fields()) {
auto& expected_fields = expected_specifics.sharing_fields();
@@ -110,8 +111,10 @@ MATCHER_P(ModelEqualsSpecifics, expected_specifics, "") {
}
for (int i = 0; i < expected_fields.enabled_features_size(); ++i) {
- if (!arg_info.enabled_features.count(expected_fields.enabled_features(i)))
+ if (!arg_info.enabled_features.count(
+ expected_fields.enabled_features(i))) {
return false;
+ }
}
}
@@ -478,8 +481,9 @@ class DeviceInfoSyncBridgeTest : public testing::Test,
~DeviceInfoSyncBridgeTest() override {
// Some tests may never initialize the bridge.
- if (bridge_)
+ if (bridge_) {
bridge_->RemoveObserver(this);
+ }
// Force all remaining (store) tasks to execute so we don't leak memory.
base::RunLoop().RunUntilIdle();
diff --git a/chromium/components/sync_device_info/device_info_sync_client.h b/chromium/components/sync_device_info/device_info_sync_client.h
index 67766d828aa..bd64fc0da99 100644
--- a/chromium/components/sync_device_info/device_info_sync_client.h
+++ b/chromium/components/sync_device_info/device_info_sync_client.h
@@ -23,6 +23,9 @@ class DeviceInfoSyncClient {
virtual ~DeviceInfoSyncClient();
virtual std::string GetSigninScopedDeviceId() const = 0;
+ // TODO(crbug.com/1324936): This only returns false for one embedder, it can
+ // be replaced with a check for whether send-tab-to-self is "enabled"
+ // (preconditions met?).
virtual bool GetSendTabToSelfReceivingEnabled() const = 0;
virtual absl::optional<DeviceInfo::SharingInfo> GetLocalSharingInfo()
const = 0;
diff --git a/chromium/components/sync_device_info/device_info_sync_service.h b/chromium/components/sync_device_info/device_info_sync_service.h
index 4a54d2c0fd5..11672a21993 100644
--- a/chromium/components/sync_device_info/device_info_sync_service.h
+++ b/chromium/components/sync_device_info/device_info_sync_service.h
@@ -5,8 +5,6 @@
#ifndef COMPONENTS_SYNC_DEVICE_INFO_DEVICE_INFO_SYNC_SERVICE_H_
#define COMPONENTS_SYNC_DEVICE_INFO_DEVICE_INFO_SYNC_SERVICE_H_
-#include "base/callback.h"
-#include "base/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "components/keyed_service/core/keyed_service.h"
diff --git a/chromium/components/sync_device_info/device_info_sync_service_impl.cc b/chromium/components/sync_device_info/device_info_sync_service_impl.cc
index ce55a57c281..62ce9982b3b 100644
--- a/chromium/components/sync_device_info/device_info_sync_service_impl.cc
+++ b/chromium/components/sync_device_info/device_info_sync_service_impl.cc
@@ -6,7 +6,7 @@
#include <utility>
-#include "base/callback_helpers.h"
+#include "base/callback.h"
#include "components/sync/base/report_unrecoverable_error.h"
#include "components/sync/invalidations/sync_invalidations_service.h"
#include "components/sync/model/client_tag_based_model_type_processor.h"
diff --git a/chromium/components/sync_device_info/device_info_sync_service_impl.h b/chromium/components/sync_device_info/device_info_sync_service_impl.h
index 11544094e90..bb183cfaf46 100644
--- a/chromium/components/sync_device_info/device_info_sync_service_impl.h
+++ b/chromium/components/sync_device_info/device_info_sync_service_impl.h
@@ -7,7 +7,6 @@
#include <memory>
-#include "base/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "components/sync/invalidations/fcm_registration_token_observer.h"
#include "components/sync/invalidations/interested_data_types_handler.h"
diff --git a/chromium/components/sync_device_info/fake_device_info_sync_service.h b/chromium/components/sync_device_info/fake_device_info_sync_service.h
index 53dbc96ba7d..12c40b046a6 100644
--- a/chromium/components/sync_device_info/fake_device_info_sync_service.h
+++ b/chromium/components/sync_device_info/fake_device_info_sync_service.h
@@ -5,7 +5,6 @@
#ifndef COMPONENTS_SYNC_DEVICE_INFO_FAKE_DEVICE_INFO_SYNC_SERVICE_H_
#define COMPONENTS_SYNC_DEVICE_INFO_FAKE_DEVICE_INFO_SYNC_SERVICE_H_
-#include "base/callback.h"
#include "components/sync/test/model/fake_model_type_controller_delegate.h"
#include "components/sync_device_info/device_info_sync_service.h"
#include "components/sync_device_info/fake_device_info_tracker.h"
diff --git a/chromium/components/sync_device_info/fake_device_info_tracker.cc b/chromium/components/sync_device_info/fake_device_info_tracker.cc
index 2ac833439fa..8818ce95b99 100644
--- a/chromium/components/sync_device_info/fake_device_info_tracker.cc
+++ b/chromium/components/sync_device_info/fake_device_info_tracker.cc
@@ -56,8 +56,9 @@ std::vector<std::unique_ptr<DeviceInfo>>
FakeDeviceInfoTracker::GetAllDeviceInfo() const {
std::vector<std::unique_ptr<DeviceInfo>> list;
- for (const DeviceInfo* device : devices_)
+ for (const DeviceInfo* device : devices_) {
list.push_back(CloneDeviceInfo(*device));
+ }
return list;
}
@@ -72,12 +73,14 @@ void FakeDeviceInfoTracker::RemoveObserver(Observer* observer) {
std::map<sync_pb::SyncEnums_DeviceType, int>
FakeDeviceInfoTracker::CountActiveDevicesByType() const {
- if (device_count_per_type_override_)
+ if (device_count_per_type_override_) {
return *device_count_per_type_override_;
+ }
std::map<sync_pb::SyncEnums_DeviceType, int> count_by_type;
- for (const auto* device : devices_)
+ for (const auto* device : devices_) {
count_by_type[device->device_type()]++;
+ }
return count_by_type;
}
@@ -92,8 +95,9 @@ bool FakeDeviceInfoTracker::IsRecentLocalCacheGuid(
void FakeDeviceInfoTracker::Add(const DeviceInfo* device) {
devices_.push_back(device);
- for (auto& observer : observers_)
+ for (auto& observer : observers_) {
observer.OnDeviceInfoChange();
+ }
}
void FakeDeviceInfoTracker::Replace(const DeviceInfo* old_device,
@@ -102,15 +106,17 @@ void FakeDeviceInfoTracker::Replace(const DeviceInfo* old_device,
base::ranges::find(devices_, old_device);
DCHECK(devices_.end() != it) << "Tracker doesn't contain device";
*it = new_device;
- for (auto& observer : observers_)
+ for (auto& observer : observers_) {
observer.OnDeviceInfoChange();
+ }
}
void FakeDeviceInfoTracker::OverrideActiveDeviceCount(
const std::map<sync_pb::SyncEnums_DeviceType, int>& counts) {
device_count_per_type_override_ = counts;
- for (auto& observer : observers_)
+ for (auto& observer : observers_) {
observer.OnDeviceInfoChange();
+ }
}
void FakeDeviceInfoTracker::SetLocalCacheGuid(const std::string& cache_guid) {
diff --git a/chromium/components/sync_device_info/fake_local_device_info_provider.cc b/chromium/components/sync_device_info/fake_local_device_info_provider.cc
index a017140a1d4..d35efe69ae8 100644
--- a/chromium/components/sync_device_info/fake_local_device_info_provider.cc
+++ b/chromium/components/sync_device_info/fake_local_device_info_provider.cc
@@ -49,8 +49,9 @@ FakeLocalDeviceInfoProvider::RegisterOnInitializedCallback(
void FakeLocalDeviceInfoProvider::SetReady(bool ready) {
bool got_ready = !ready_ && ready;
ready_ = ready;
- if (got_ready)
+ if (got_ready) {
closure_list_.Notify();
+ }
}
DeviceInfo* FakeLocalDeviceInfoProvider::GetMutableDeviceInfo() {
diff --git a/chromium/components/sync_device_info/local_device_info_provider_impl_unittest.cc b/chromium/components/sync_device_info/local_device_info_provider_impl_unittest.cc
index 6022e8d3272..9e3d559307f 100644
--- a/chromium/components/sync_device_info/local_device_info_provider_impl_unittest.cc
+++ b/chromium/components/sync_device_info/local_device_info_provider_impl_unittest.cc
@@ -34,11 +34,9 @@ const sync_pb::SharingSpecificFields::EnabledFeatures
kSharingEnabledFeatures[] = {
sync_pb::SharingSpecificFields::CLICK_TO_CALL_V2};
-using testing::_;
using testing::NiceMock;
using testing::NotNull;
using testing::Return;
-using testing::ReturnRef;
class MockDeviceInfoSyncClient : public DeviceInfoSyncClient {
public:
diff --git a/chromium/components/sync_device_info/local_device_info_util_linux.cc b/chromium/components/sync_device_info/local_device_info_util_linux.cc
index 8e0b28b98dd..30f8bfb3efc 100644
--- a/chromium/components/sync_device_info/local_device_info_util_linux.cc
+++ b/chromium/components/sync_device_info/local_device_info_util_linux.cc
@@ -39,8 +39,9 @@ std::string GetPersonalizableDeviceNameInternal() {
return GetChromeOSDeviceNameFromType();
#else
char hostname[HOST_NAME_MAX];
- if (gethostname(hostname, HOST_NAME_MAX) == 0) // Success.
+ if (gethostname(hostname, HOST_NAME_MAX) == 0) { // Success.
return hostname;
+ }
return base::GetLinuxDistro();
#endif
}
diff --git a/chromium/components/sync_preferences/pref_model_associator.cc b/chromium/components/sync_preferences/pref_model_associator.cc
index 34433b9dff5..e4caacfab59 100644
--- a/chromium/components/sync_preferences/pref_model_associator.cc
+++ b/chromium/components/sync_preferences/pref_model_associator.cc
@@ -10,6 +10,7 @@
#include <utility>
#include "base/auto_reset.h"
+#include "base/callback.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/json/json_reader.h"
@@ -32,9 +33,6 @@
#include "components/sync_preferences/pref_model_associator_client.h"
#include "components/sync_preferences/pref_service_syncable.h"
-using syncer::PREFERENCES;
-using syncer::PRIORITY_PREFERENCES;
-
namespace sync_preferences {
namespace {
@@ -238,8 +236,9 @@ PrefModelAssociator::MergeDataAndStartSyncing(
// We don't call InitPrefAndAssociate because we don't want the initial sync
// to trigger outgoing changes -- these prefs are only tracked to send
// updates back to older clients.
- if (pref_service_->GetUserPrefValue(legacy_pref_name))
+ if (pref_service_->GetUserPrefValue(legacy_pref_name)) {
synced_preferences_.insert(legacy_pref_name);
+ }
}
// Push updates to sync.
@@ -268,14 +267,17 @@ base::Value PrefModelAssociator::MergePreference(
// to merge for all migrated values.
if (client_) {
std::string new_pref_name;
- if (client_->IsMergeableListPreference(name))
+ if (client_->IsMergeableListPreference(name)) {
return std::move(*MergeListValues(local_value, server_value));
- if (client_->IsMergeableDictionaryPreference(name))
+ }
+ if (client_->IsMergeableDictionaryPreference(name)) {
return MergeDictionaryValues(local_value, server_value);
+ }
base::Value merged_value =
client_->MaybeMergePreferenceValues(name, local_value, server_value);
- if (!merged_value.is_none())
+ if (!merged_value.is_none()) {
return merged_value;
+ }
}
// If this is not a specially handled preference, server wins.
@@ -313,18 +315,21 @@ bool PrefModelAssociator::CreatePrefSyncData(
std::unique_ptr<base::Value> PrefModelAssociator::MergeListValues(
const base::Value& from_value,
const base::Value& to_value) {
- if (from_value.is_none())
+ if (from_value.is_none()) {
return base::Value::ToUniquePtrValue(to_value.Clone());
- if (to_value.is_none())
+ }
+ if (to_value.is_none()) {
return base::Value::ToUniquePtrValue(from_value.Clone());
+ }
DCHECK(from_value.type() == base::Value::Type::LIST);
DCHECK(to_value.type() == base::Value::Type::LIST);
base::Value result = to_value.Clone();
for (const auto& value : from_value.GetListDeprecated()) {
- if (!base::Contains(result.GetListDeprecated(), value))
+ if (!base::Contains(result.GetListDeprecated(), value)) {
result.Append(value.Clone());
+ }
}
return base::Value::ToUniquePtrValue(std::move(result));
@@ -333,10 +338,12 @@ std::unique_ptr<base::Value> PrefModelAssociator::MergeListValues(
base::Value PrefModelAssociator::MergeDictionaryValues(
const base::Value& from_value,
const base::Value& to_value) {
- if (from_value.is_none())
+ if (from_value.is_none()) {
return to_value.Clone();
- if (to_value.is_none())
+ }
+ if (to_value.is_none()) {
return from_value.Clone();
+ }
DCHECK(from_value.is_dict());
DCHECK(to_value.is_dict());
@@ -371,12 +378,14 @@ syncer::SyncDataList PrefModelAssociator::GetAllSyncDataForTesting(
for (const std::string& name : synced_preferences_) {
const PrefService::Preference* pref = pref_service_->FindPreference(name);
DCHECK(pref);
- if (!pref->IsUserControlled() || pref->IsDefaultValue())
+ if (!pref->IsUserControlled() || pref->IsDefaultValue()) {
continue; // This is not data we care about.
+ }
// TODO(zea): plumb a way to read the user controlled value.
syncer::SyncData sync_data;
- if (!CreatePrefSyncData(name, *pref->GetValue(), &sync_data))
+ if (!CreatePrefSyncData(name, *pref->GetValue(), &sync_data)) {
continue;
+ }
current_data.push_back(sync_data);
}
return current_data;
@@ -401,8 +410,9 @@ absl::optional<syncer::ModelError> PrefModelAssociator::ProcessSyncChanges(
// Windows client, the Windows client does not support
// kConfirmToQuitEnabled. Ignore updates from these preferences.
std::string pref_name = pref_specifics.name();
- if (!IsPrefRegistered(pref_name))
+ if (!IsPrefRegistered(pref_name)) {
continue;
+ }
if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
pref_service_->ClearPref(pref_name);
@@ -457,8 +467,9 @@ absl::optional<base::Value> PrefModelAssociator::ReadPreferenceSpecifics(
void PrefModelAssociator::AddSyncedPrefObserver(const std::string& name,
SyncedPrefObserver* observer) {
auto& observers = synced_pref_observers_[name];
- if (!observers)
+ if (!observers) {
observers = std::make_unique<SyncedPrefObserverList>();
+ }
observers->AddObserver(observer);
}
@@ -467,8 +478,9 @@ void PrefModelAssociator::RemoveSyncedPrefObserver(
const std::string& name,
SyncedPrefObserver* observer) {
auto observer_iter = synced_pref_observers_.find(name);
- if (observer_iter == synced_pref_observers_.end())
+ if (observer_iter == synced_pref_observers_.end()) {
return;
+ }
observer_iter->second->RemoveObserver(observer);
}
@@ -505,19 +517,22 @@ bool PrefModelAssociator::IsLegacyModelTypePref(const std::string& name) const {
}
void PrefModelAssociator::ProcessPrefChange(const std::string& name) {
- if (processing_syncer_changes_)
+ if (processing_syncer_changes_) {
return; // These are changes originating from us, ignore.
+ }
// We only process changes if we've already associated models.
// This also filters out local changes during the initial merge.
- if (!models_associated_)
+ if (!models_associated_) {
return;
+ }
const PrefService::Preference* preference =
pref_service_->FindPreference(name);
// TODO(tschumann): When can this ever happen? Should this be a DCHECK?
- if (!preference)
+ if (!preference) {
return;
+ }
if (!IsPrefRegistered(name) && !IsLegacyModelTypePref(name)) {
// We are not syncing this preference -- this also filters out synced
@@ -577,22 +592,25 @@ void PrefModelAssociator::SetPrefService(PrefServiceSyncable* pref_service) {
void PrefModelAssociator::NotifySyncedPrefObservers(const std::string& path,
bool from_sync) const {
auto observer_iter = synced_pref_observers_.find(path);
- if (observer_iter == synced_pref_observers_.end())
+ if (observer_iter == synced_pref_observers_.end()) {
return;
+ }
// Don't notify for prefs we are only observing to support old clients.
// The PrefModelAssociator for the new ModelType will notify.
if (IsLegacyModelTypePref(path)) {
DCHECK(!from_sync);
return;
}
- for (auto& observer : *observer_iter->second)
+ for (auto& observer : *observer_iter->second) {
observer.OnSyncedPrefChanged(path, from_sync);
+ }
}
void PrefModelAssociator::SetPrefWithTypeCheck(const std::string& pref_name,
const base::Value& new_value) {
- if (TypeMatchesUserPrefStore(pref_name, new_value))
+ if (TypeMatchesUserPrefStore(pref_name, new_value)) {
pref_service_->Set(pref_name, new_value);
+ }
}
bool PrefModelAssociator::TypeMatchesUserPrefStore(
@@ -600,8 +618,9 @@ bool PrefModelAssociator::TypeMatchesUserPrefStore(
const base::Value& new_value) const {
const base::Value* local_value = nullptr;
user_pref_store_->GetValue(pref_name, &local_value);
- if (!local_value || local_value->type() == new_value.type())
+ if (!local_value || local_value->type() == new_value.type()) {
return true;
+ }
DLOG(WARNING) << "Unexpected type mis-match for pref. "
<< "Synced value for " << pref_name << " is of type "
@@ -633,11 +652,13 @@ void PrefModelAssociator::EnforceRegisteredTypeInStore(
void PrefModelAssociator::NotifyStartedSyncing(const std::string& path) const {
auto observer_iter = synced_pref_observers_.find(path);
- if (observer_iter == synced_pref_observers_.end())
+ if (observer_iter == synced_pref_observers_.end()) {
return;
+ }
- for (auto& observer : *observer_iter->second)
+ for (auto& observer : *observer_iter->second) {
observer.OnStartedSyncing(path);
+ }
}
} // namespace sync_preferences
diff --git a/chromium/components/sync_preferences/pref_model_associator.h b/chromium/components/sync_preferences/pref_model_associator.h
index 71c8e4e4924..70768f8d017 100644
--- a/chromium/components/sync_preferences/pref_model_associator.h
+++ b/chromium/components/sync_preferences/pref_model_associator.h
@@ -10,7 +10,7 @@
#include <string>
#include <unordered_map>
-#include "base/callback.h"
+#include "base/callback_forward.h"
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
diff --git a/chromium/components/sync_preferences/pref_model_associator_unittest.cc b/chromium/components/sync_preferences/pref_model_associator_unittest.cc
index f66146d2f1c..1117b07e1a8 100644
--- a/chromium/components/sync_preferences/pref_model_associator_unittest.cc
+++ b/chromium/components/sync_preferences/pref_model_associator_unittest.cc
@@ -50,8 +50,9 @@ class TestPrefModelAssociatorClient : public PrefModelAssociatorClient {
const std::string& pref_name,
const base::Value& local_value,
const base::Value& server_value) const override {
- if (pref_name == kCustomMergePrefName)
+ if (pref_name == kCustomMergePrefName) {
return local_value.Clone();
+ }
return base::Value();
}
};
@@ -98,12 +99,13 @@ class AbstractPreferenceMergeTest : public testing::Test {
pref_service_->FindPreference(pref_name.c_str());
ASSERT_TRUE(pref);
base::Value::Type type = pref->GetType();
- if (type == base::Value::Type::DICTIONARY)
+ if (type == base::Value::Type::DICTIONARY) {
empty_value = std::make_unique<base::DictionaryValue>();
- else if (type == base::Value::Type::LIST)
+ } else if (type == base::Value::Type::LIST) {
empty_value = std::make_unique<base::ListValue>();
- else
+ } else {
FAIL();
+ }
pref_service_->Set(pref_name.c_str(), *empty_value);
}
diff --git a/chromium/components/sync_preferences/pref_service_syncable.cc b/chromium/components/sync_preferences/pref_service_syncable.cc
index c31691f8fce..78387c8c005 100644
--- a/chromium/components/sync_preferences/pref_service_syncable.cc
+++ b/chromium/components/sync_preferences/pref_service_syncable.cc
@@ -109,8 +109,9 @@ PrefServiceSyncable::CreateIncognitoPrefService(
auto incognito_pref_store = base::MakeRefCounted<OverlayUserPrefStore>(
overlay.get(), user_pref_store_.get());
- for (const char* persistent_pref_name : persistent_pref_names)
+ for (const char* persistent_pref_name : persistent_pref_names) {
incognito_pref_store->RegisterPersistentPref(persistent_pref_name);
+ }
auto pref_value_store = pref_value_store_->CloneAndSpecialize(
nullptr, // managed
@@ -247,8 +248,9 @@ void PrefServiceSyncable::AddRegisteredSyncablePreference(
}
void PrefServiceSyncable::OnIsSyncingChanged() {
- for (auto& observer : observer_list_)
+ for (auto& observer : observer_list_) {
observer.OnIsSyncingChanged();
+ }
}
void PrefServiceSyncable::ProcessPrefChange(const std::string& name) {
diff --git a/chromium/components/sync_preferences/pref_service_syncable_unittest.cc b/chromium/components/sync_preferences/pref_service_syncable_unittest.cc
index b52c76a6967..2fd7da6af91 100644
--- a/chromium/components/sync_preferences/pref_service_syncable_unittest.cc
+++ b/chromium/components/sync_preferences/pref_service_syncable_unittest.cc
@@ -50,9 +50,7 @@ using syncer::SyncData;
using testing::Eq;
using testing::IsEmpty;
using testing::Matches;
-using testing::Not;
using testing::NotNull;
-using testing::SizeIs;
using testing::UnorderedElementsAre;
using user_prefs::PrefRegistrySyncable;
@@ -88,13 +86,14 @@ MATCHER_P(MatchesModelType, model_type, "") {
class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
public:
explicit TestSyncProcessorStub(syncer::SyncChangeList* output)
- : output_(output), fail_next_(false) {}
+ : output_(output) {}
absl::optional<syncer::ModelError> ProcessSyncChanges(
const base::Location& from_here,
const syncer::SyncChangeList& change_list) override {
- if (output_)
+ if (output_) {
output_->insert(output_->end(), change_list.begin(), change_list.end());
+ }
if (fail_next_) {
fail_next_ = false;
return syncer::ModelError(FROM_HERE, "Error");
@@ -106,7 +105,7 @@ class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
private:
raw_ptr<syncer::SyncChangeList> output_;
- bool fail_next_;
+ bool fail_next_ = false;
};
class TestSyncedPrefObserver : public SyncedPrefObserver {
@@ -137,8 +136,9 @@ class TestPrefServiceSyncableObserver : public PrefServiceSyncableObserver {
~TestPrefServiceSyncableObserver() override = default;
void OnIsSyncingChanged() override {
- if (sync_pref_observer_ && sync_pref_observer_->sync_started_count_ > 0)
+ if (sync_pref_observer_ && sync_pref_observer_->sync_started_count_ > 0) {
is_syncing_changed_ = true;
+ }
}
void SetSyncedPrefObserver(const TestSyncedPrefObserver* sync_pref_observer) {
diff --git a/chromium/components/sync_sessions/BUILD.gn b/chromium/components/sync_sessions/BUILD.gn
index e2abfdffa84..95fea0f97a8 100644
--- a/chromium/components/sync_sessions/BUILD.gn
+++ b/chromium/components/sync_sessions/BUILD.gn
@@ -53,6 +53,7 @@ static_library("sync_sessions") {
"//components/bookmarks/browser",
"//components/favicon/core",
"//components/favicon_base",
+ "//components/history/core/browser",
"//components/history/core/common",
"//components/keyed_service/core",
"//components/prefs",
diff --git a/chromium/components/sync_sessions/DEPS b/chromium/components/sync_sessions/DEPS
index 793c04ef43f..9e64a7d1275 100644
--- a/chromium/components/sync_sessions/DEPS
+++ b/chromium/components/sync_sessions/DEPS
@@ -2,6 +2,7 @@ include_rules = [
"+components/bookmarks/browser",
"+components/favicon/core",
"+components/favicon_base",
+ "+components/history/core/browser/sync",
"+components/history/core/common",
"+components/keyed_service/core",
"+components/prefs",
diff --git a/chromium/components/sync_sessions/local_session_event_handler_impl.cc b/chromium/components/sync_sessions/local_session_event_handler_impl.cc
index 329ec8a6af6..580809d3e00 100644
--- a/chromium/components/sync_sessions/local_session_event_handler_impl.cc
+++ b/chromium/components/sync_sessions/local_session_event_handler_impl.cc
@@ -278,8 +278,9 @@ void LocalSessionEventHandlerImpl::AssociateTab(
session_tracker_->GetTab(current_session_tag_, tab_id);
int old_index = session_tab->normalized_navigation_index();
GURL old_url;
- if (session_tab->navigations.size() > static_cast<size_t>(old_index))
+ if (session_tab->navigations.size() > static_cast<size_t>(old_index)) {
old_url = session_tab->navigations[old_index].virtual_url();
+ }
// Produce the specifics.
auto specifics = std::make_unique<sync_pb::SessionSpecifics>();
@@ -376,15 +377,17 @@ sync_pb::SessionTab LocalSessionEventHandlerImpl::GetTabSpecificsFromDelegate(
tab_delegate.GetSerializedNavigationAtIndex(i, &serialized_entry);
// Set current_navigation_index to the index in navigations.
- if (i == current_index)
+ if (i == current_index) {
specifics.set_current_navigation_index(specifics.navigation_size());
+ }
sync_pb::TabNavigation* navigation = specifics.add_navigation();
SessionNavigationToSyncData(serialized_entry).Swap(navigation);
const std::string page_language = tab_delegate.GetPageLanguageAtIndex(i);
- if (!page_language.empty())
+ if (!page_language.empty()) {
navigation->set_page_language(page_language);
+ }
if (has_child_account) {
navigation->set_blocked_state(
diff --git a/chromium/components/sync_sessions/local_session_event_handler_impl_unittest.cc b/chromium/components/sync_sessions/local_session_event_handler_impl_unittest.cc
index 9617156917e..c3c7ac4191e 100644
--- a/chromium/components/sync_sessions/local_session_event_handler_impl_unittest.cc
+++ b/chromium/components/sync_sessions/local_session_event_handler_impl_unittest.cc
@@ -29,12 +29,10 @@ using sessions::SerializedNavigationEntry;
using sessions::SerializedNavigationEntryTestHelper;
using testing::_;
using testing::ByMove;
-using testing::Eq;
using testing::IsEmpty;
using testing::NiceMock;
using testing::Pointee;
using testing::Return;
-using testing::SizeIs;
using testing::StrictMock;
const char kFoo1[] = "http://foo1/";
@@ -336,7 +334,7 @@ TEST_F(LocalSessionEventHandlerImplTest, AssociateWindowsAndTabsIfEmpty) {
auto mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
EXPECT_CALL(*mock_batch,
Put(Pointee(MatchesHeader(kSessionTag, /*window_ids=*/IsEmpty(),
- /*tabs_ids=*/IsEmpty()))));
+ /*tab_ids=*/IsEmpty()))));
EXPECT_CALL(*mock_batch, Commit());
EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
.WillOnce(Return(ByMove(std::move(mock_batch))));
diff --git a/chromium/components/sync_sessions/open_tabs_ui_delegate_impl.cc b/chromium/components/sync_sessions/open_tabs_ui_delegate_impl.cc
index 415b2f8fe35..3cfb7d17988 100644
--- a/chromium/components/sync_sessions/open_tabs_ui_delegate_impl.cc
+++ b/chromium/components/sync_sessions/open_tabs_ui_delegate_impl.cc
@@ -49,20 +49,23 @@ bool OpenTabsUIDelegateImpl::GetForeignSessionTabs(
const std::string& tag,
std::vector<const sessions::SessionTab*>* tabs) {
std::vector<const sessions::SessionWindow*> windows;
- if (!session_tracker_->LookupSessionWindows(tag, &windows))
+ if (!session_tracker_->LookupSessionWindows(tag, &windows)) {
return false;
+ }
// Prune those tabs that are not syncable or are NewTabPage, then sort them
// from most recent to least recent, independent of which window the tabs were
// from.
for (const sessions::SessionWindow* window : windows) {
for (const std::unique_ptr<sessions::SessionTab>& tab : window->tabs) {
- if (tab->navigations.empty())
+ if (tab->navigations.empty()) {
continue;
+ }
const sessions::SerializedNavigationEntry& current_navigation =
tab->navigations.at(tab->normalized_navigation_index());
- if (!sessions_client_->ShouldSyncURL(current_navigation.virtual_url()))
+ if (!sessions_client_->ShouldSyncURL(current_navigation.virtual_url())) {
continue;
+ }
tabs->push_back(tab.get());
}
}
diff --git a/chromium/components/sync_sessions/proxy_tabs_data_type_controller.cc b/chromium/components/sync_sessions/proxy_tabs_data_type_controller.cc
index 306cfd9c5a5..541df0bc5f6 100644
--- a/chromium/components/sync_sessions/proxy_tabs_data_type_controller.cc
+++ b/chromium/components/sync_sessions/proxy_tabs_data_type_controller.cc
@@ -7,22 +7,30 @@
#include <memory>
#include <utility>
+#include "base/callback.h"
#include "base/values.h"
#include "components/sync/driver/configure_context.h"
#include "components/sync/engine/data_type_activation_response.h"
-#include "components/sync/engine/model_type_configurer.h"
#include "components/sync/model/type_entities_count.h"
namespace sync_sessions {
ProxyTabsDataTypeController::ProxyTabsDataTypeController(
+ syncer::SyncService* sync_service,
+ PrefService* pref_service,
const base::RepeatingCallback<void(State)>& state_changed_cb)
: DataTypeController(syncer::PROXY_TABS),
- state_changed_cb_(state_changed_cb),
- state_(NOT_RUNNING) {}
+ helper_(syncer::PROXY_TABS, sync_service, pref_service),
+ state_changed_cb_(state_changed_cb) {}
ProxyTabsDataTypeController::~ProxyTabsDataTypeController() = default;
+syncer::DataTypeController::PreconditionState
+ProxyTabsDataTypeController::GetPreconditionState() const {
+ DCHECK(CalledOnValidThread());
+ return helper_.GetPreconditionState();
+}
+
void ProxyTabsDataTypeController::LoadModels(
const syncer::ConfigureContext& configure_context,
const ModelLoadCallback& model_load_callback) {
diff --git a/chromium/components/sync_sessions/proxy_tabs_data_type_controller.h b/chromium/components/sync_sessions/proxy_tabs_data_type_controller.h
index 3d979cd0ca2..63167192212 100644
--- a/chromium/components/sync_sessions/proxy_tabs_data_type_controller.h
+++ b/chromium/components/sync_sessions/proxy_tabs_data_type_controller.h
@@ -8,8 +8,15 @@
#include <memory>
#include "base/callback_forward.h"
+#include "components/history/core/browser/sync/history_model_type_controller_helper.h"
#include "components/sync/driver/data_type_controller.h"
+class PrefService;
+
+namespace syncer {
+class SyncService;
+} // namespace syncer
+
namespace sync_sessions {
// Controller for PROXY_TABS. Proxy tabs have no representation in sync, and
@@ -17,7 +24,9 @@ namespace sync_sessions {
class ProxyTabsDataTypeController : public syncer::DataTypeController {
public:
// |state_changed_cb| can be used to listen to state changes.
- explicit ProxyTabsDataTypeController(
+ ProxyTabsDataTypeController(
+ syncer::SyncService* sync_service,
+ PrefService* pref_service,
const base::RepeatingCallback<void(State)>& state_changed_cb);
ProxyTabsDataTypeController(const ProxyTabsDataTypeController&) = delete;
@@ -27,6 +36,7 @@ class ProxyTabsDataTypeController : public syncer::DataTypeController {
~ProxyTabsDataTypeController() override;
// DataTypeController interface.
+ PreconditionState GetPreconditionState() const override;
void LoadModels(const syncer::ConfigureContext& configure_context,
const ModelLoadCallback& model_load_callback) override;
std::unique_ptr<syncer::DataTypeActivationResponse> Connect() override;
@@ -41,8 +51,11 @@ class ProxyTabsDataTypeController : public syncer::DataTypeController {
void RecordMemoryUsageAndCountsHistograms() override;
private:
+ history::HistoryModelTypeControllerHelper helper_;
+
const base::RepeatingCallback<void(State)> state_changed_cb_;
- State state_;
+
+ State state_ = NOT_RUNNING;
};
} // namespace sync_sessions
diff --git a/chromium/components/sync_sessions/session_model_type_controller.cc b/chromium/components/sync_sessions/session_model_type_controller.cc
index f0c69d5b714..c0851813bff 100644
--- a/chromium/components/sync_sessions/session_model_type_controller.cc
+++ b/chromium/components/sync_sessions/session_model_type_controller.cc
@@ -6,11 +6,6 @@
#include <utility>
-#include "base/bind.h"
-#include "components/history/core/common/pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "components/sync/driver/sync_service.h"
-
namespace sync_sessions {
SessionModelTypeController::SessionModelTypeController(
@@ -18,29 +13,14 @@ SessionModelTypeController::SessionModelTypeController(
PrefService* pref_service,
std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate)
: ModelTypeController(syncer::SESSIONS, std::move(delegate)),
- sync_service_(sync_service),
- pref_service_(pref_service) {
- pref_registrar_.Init(pref_service);
- pref_registrar_.Add(
- prefs::kSavingBrowserHistoryDisabled,
- base::BindRepeating(
- &SessionModelTypeController::OnSavingBrowserHistoryPrefChanged,
- base::AsWeakPtr(this)));
-}
+ helper_(syncer::SESSIONS, sync_service, pref_service) {}
SessionModelTypeController::~SessionModelTypeController() = default;
syncer::DataTypeController::PreconditionState
SessionModelTypeController::GetPreconditionState() const {
DCHECK(CalledOnValidThread());
- return pref_service_->GetBoolean(prefs::kSavingBrowserHistoryDisabled)
- ? PreconditionState::kMustStopAndKeepData
- : PreconditionState::kPreconditionsMet;
-}
-
-void SessionModelTypeController::OnSavingBrowserHistoryPrefChanged() {
- DCHECK(CalledOnValidThread());
- sync_service_->DataTypePreconditionChanged(type());
+ return helper_.GetPreconditionState();
}
} // namespace sync_sessions
diff --git a/chromium/components/sync_sessions/session_model_type_controller.h b/chromium/components/sync_sessions/session_model_type_controller.h
index 01a991b52bb..df25943c13d 100644
--- a/chromium/components/sync_sessions/session_model_type_controller.h
+++ b/chromium/components/sync_sessions/session_model_type_controller.h
@@ -7,8 +7,7 @@
#include <memory>
-#include "base/memory/raw_ptr.h"
-#include "components/prefs/pref_change_registrar.h"
+#include "components/history/core/browser/sync/history_model_type_controller_helper.h"
#include "components/sync/driver/model_type_controller.h"
class PrefService;
@@ -19,7 +18,6 @@ class SyncService;
namespace sync_sessions {
-// Overrides LoadModels to check if history sync is allowed by policy.
class SessionModelTypeController : public syncer::ModelTypeController {
public:
SessionModelTypeController(
@@ -37,12 +35,7 @@ class SessionModelTypeController : public syncer::ModelTypeController {
PreconditionState GetPreconditionState() const override;
private:
- void OnSavingBrowserHistoryPrefChanged();
-
- const raw_ptr<syncer::SyncService> sync_service_;
- const raw_ptr<PrefService> pref_service_;
-
- PrefChangeRegistrar pref_registrar_;
+ history::HistoryModelTypeControllerHelper helper_;
};
} // namespace sync_sessions
diff --git a/chromium/components/sync_sessions/session_store_unittest.cc b/chromium/components/sync_sessions/session_store_unittest.cc
index 1cd9c272bb1..3a30f328bad 100644
--- a/chromium/components/sync_sessions/session_store_unittest.cc
+++ b/chromium/components/sync_sessions/session_store_unittest.cc
@@ -45,7 +45,6 @@ using testing::_;
using testing::ElementsAre;
using testing::Eq;
using testing::IsEmpty;
-using testing::IsNull;
using testing::Matcher;
using testing::NiceMock;
using testing::NotNull;
diff --git a/chromium/components/sync_sessions/session_sync_bridge.cc b/chromium/components/sync_sessions/session_sync_bridge.cc
index 6484e51294f..f89d63297b6 100644
--- a/chromium/components/sync_sessions/session_sync_bridge.cc
+++ b/chromium/components/sync_sessions/session_sync_bridge.cc
@@ -35,8 +35,6 @@ namespace {
using sync_pb::SessionSpecifics;
using syncer::MetadataChangeList;
-using syncer::ModelTypeStore;
-using syncer::ModelTypeSyncBridge;
// Default time without activity after which a session is considered stale and
// becomes a candidate for garbage collection.
diff --git a/chromium/components/sync_sessions/session_sync_bridge_unittest.cc b/chromium/components/sync_sessions/session_sync_bridge_unittest.cc
index 6eab3a00287..2bd81b21adf 100644
--- a/chromium/components/sync_sessions/session_sync_bridge_unittest.cc
+++ b/chromium/components/sync_sessions/session_sync_bridge_unittest.cc
@@ -54,17 +54,13 @@ using syncer::EntityData;
using syncer::FailedCommitResponseDataList;
using syncer::IsEmptyMetadataBatch;
using syncer::MetadataBatch;
-using syncer::MetadataChangeList;
using syncer::MockModelTypeChangeProcessor;
using testing::_;
using testing::AtLeast;
-using testing::Contains;
using testing::ElementsAre;
using testing::Eq;
-using testing::InSequence;
using testing::IsEmpty;
using testing::IsNull;
-using testing::Matcher;
using testing::Not;
using testing::NotNull;
using testing::Pair;
diff --git a/chromium/components/sync_sessions/session_sync_test_helper.cc b/chromium/components/sync_sessions/session_sync_test_helper.cc
index 59868e672a9..d0017d7c201 100644
--- a/chromium/components/sync_sessions/session_sync_test_helper.cc
+++ b/chromium/components/sync_sessions/session_sync_test_helper.cc
@@ -60,10 +60,11 @@ void SessionSyncTestHelper::VerifySyncedSession(
for (const std::vector<SessionID>& window : windows) {
sessions::SessionWindow* win_ptr;
auto map_iter = session.windows.find(SessionID::FromSerializedValue(i));
- if (map_iter != session.windows.end())
+ if (map_iter != session.windows.end()) {
win_ptr = &map_iter->second->wrapped_window;
- else
+ } else {
FAIL();
+ }
ASSERT_EQ(window.size(), win_ptr->tabs.size());
ASSERT_EQ(0, win_ptr->selected_tab_index);
ASSERT_EQ(sessions::SessionWindow::TYPE_NORMAL, win_ptr->type);
@@ -104,6 +105,7 @@ sync_pb::SessionSpecifics SessionSyncTestHelper::BuildTabSpecifics(
specifics.set_session_tag(tag);
specifics.set_tab_node_id(tab_node_id);
sync_pb::SessionTab* tab = specifics.mutable_tab();
+ tab->set_window_id(window_id.id());
tab->set_tab_id(tab_id.id());
tab->set_tab_visual_index(1);
tab->set_current_navigation_index(0);
diff --git a/chromium/components/sync_sessions/sessions_global_id_mapper_unittest.cc b/chromium/components/sync_sessions/sessions_global_id_mapper_unittest.cc
index 9aabe95edf4..7c490ccfc95 100644
--- a/chromium/components/sync_sessions/sessions_global_id_mapper_unittest.cc
+++ b/chromium/components/sync_sessions/sessions_global_id_mapper_unittest.cc
@@ -12,8 +12,6 @@
namespace sync_sessions {
namespace {
-using testing::_;
-
const base::Time kTime1 = base::Time::FromInternalValue(110);
const base::Time kTime2 = base::Time::FromInternalValue(120);
const base::Time kTime3 = base::Time::FromInternalValue(130);
diff --git a/chromium/components/sync_sessions/synced_session.cc b/chromium/components/sync_sessions/synced_session.cc
index d413ae13d27..b7aba4e572e 100644
--- a/chromium/components/sync_sessions/synced_session.cc
+++ b/chromium/components/sync_sessions/synced_session.cc
@@ -6,9 +6,9 @@
#include <vector>
-#include "base/notreached.h"
#include "base/strings/utf_string_conversions.h"
#include "components/sessions/core/serialized_navigation_driver.h"
+#include "components/sync/base/page_transition_conversion.h"
#include "components/sync/base/time.h"
#include "ui/base/page_transition_types.h"
@@ -28,100 +28,6 @@ const int kObsoleteReferrerPolicyNever = 2;
// server. And after all, the favicon is somewhat optional.
const int kMaxFaviconUrlSizeToSync = 2048;
-sync_pb::SyncEnums_PageTransition ToSyncPageTransition(
- ui::PageTransition transition_type) {
- switch (ui::PageTransitionStripQualifier(transition_type)) {
- case ui::PAGE_TRANSITION_LINK:
- return sync_pb::SyncEnums_PageTransition_LINK;
-
- case ui::PAGE_TRANSITION_TYPED:
- return sync_pb::SyncEnums_PageTransition_TYPED;
-
- case ui::PAGE_TRANSITION_AUTO_BOOKMARK:
- return sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK;
-
- case ui::PAGE_TRANSITION_AUTO_SUBFRAME:
- return sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME;
-
- case ui::PAGE_TRANSITION_MANUAL_SUBFRAME:
- return sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME;
-
- case ui::PAGE_TRANSITION_GENERATED:
- return sync_pb::SyncEnums_PageTransition_GENERATED;
-
- case ui::PAGE_TRANSITION_AUTO_TOPLEVEL:
- return sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL;
-
- case ui::PAGE_TRANSITION_FORM_SUBMIT:
- return sync_pb::SyncEnums_PageTransition_FORM_SUBMIT;
-
- case ui::PAGE_TRANSITION_RELOAD:
- return sync_pb::SyncEnums_PageTransition_RELOAD;
-
- case ui::PAGE_TRANSITION_KEYWORD:
- return sync_pb::SyncEnums_PageTransition_KEYWORD;
-
- case ui::PAGE_TRANSITION_KEYWORD_GENERATED:
- return sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED;
-
- // Non-core values listed here although unreachable:
- case ui::PAGE_TRANSITION_CORE_MASK:
- case ui::PAGE_TRANSITION_BLOCKED:
- case ui::PAGE_TRANSITION_FORWARD_BACK:
- case ui::PAGE_TRANSITION_FROM_ADDRESS_BAR:
- case ui::PAGE_TRANSITION_HOME_PAGE:
- case ui::PAGE_TRANSITION_FROM_API:
- case ui::PAGE_TRANSITION_CHAIN_START:
- case ui::PAGE_TRANSITION_CHAIN_END:
- case ui::PAGE_TRANSITION_CLIENT_REDIRECT:
- case ui::PAGE_TRANSITION_SERVER_REDIRECT:
- case ui::PAGE_TRANSITION_IS_REDIRECT_MASK:
- case ui::PAGE_TRANSITION_QUALIFIER_MASK:
- break;
- }
- NOTREACHED();
- return sync_pb::SyncEnums_PageTransition_LINK;
-}
-
-ui::PageTransition FromSyncPageTransition(
- sync_pb::SyncEnums_PageTransition transition_type) {
- switch (transition_type) {
- case sync_pb::SyncEnums_PageTransition_LINK:
- return ui::PAGE_TRANSITION_LINK;
-
- case sync_pb::SyncEnums_PageTransition_TYPED:
- return ui::PAGE_TRANSITION_TYPED;
-
- case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK:
- return ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-
- case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME:
- return ui::PAGE_TRANSITION_AUTO_SUBFRAME;
-
- case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME:
- return ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
-
- case sync_pb::SyncEnums_PageTransition_GENERATED:
- return ui::PAGE_TRANSITION_GENERATED;
-
- case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL:
- return ui::PAGE_TRANSITION_AUTO_TOPLEVEL;
-
- case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT:
- return ui::PAGE_TRANSITION_FORM_SUBMIT;
-
- case sync_pb::SyncEnums_PageTransition_RELOAD:
- return ui::PAGE_TRANSITION_RELOAD;
-
- case sync_pb::SyncEnums_PageTransition_KEYWORD:
- return ui::PAGE_TRANSITION_KEYWORD;
-
- case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED:
- return ui::PAGE_TRANSITION_KEYWORD_GENERATED;
- }
- return ui::PAGE_TRANSITION_LINK;
-}
-
} // namespace
SerializedNavigationEntry SessionNavigationFromSyncData(
@@ -140,7 +46,8 @@ SerializedNavigationEntry SessionNavigationFromSyncData(
navigation.set_virtual_url(GURL(sync_data.virtual_url()));
navigation.set_title(base::UTF8ToUTF16(sync_data.title()));
- uint32_t transition = FromSyncPageTransition(sync_data.page_transition());
+ uint32_t transition =
+ syncer::FromSyncPageTransition(sync_data.page_transition());
if (sync_data.has_redirect_type()) {
switch (sync_data.redirect_type()) {
@@ -152,22 +59,28 @@ SerializedNavigationEntry SessionNavigationFromSyncData(
break;
}
}
- if (sync_data.navigation_forward_back())
+ if (sync_data.navigation_forward_back()) {
transition |= ui::PAGE_TRANSITION_FORWARD_BACK;
- if (sync_data.navigation_from_address_bar())
+ }
+ if (sync_data.navigation_from_address_bar()) {
transition |= ui::PAGE_TRANSITION_FROM_ADDRESS_BAR;
- if (sync_data.navigation_home_page())
+ }
+ if (sync_data.navigation_home_page()) {
transition |= ui::PAGE_TRANSITION_HOME_PAGE;
- if (sync_data.navigation_chain_start())
+ }
+ if (sync_data.navigation_chain_start()) {
transition |= ui::PAGE_TRANSITION_CHAIN_START;
- if (sync_data.navigation_chain_end())
+ }
+ if (sync_data.navigation_chain_end()) {
transition |= ui::PAGE_TRANSITION_CHAIN_END;
+ }
navigation.set_transition_type(static_cast<ui::PageTransition>(transition));
navigation.set_timestamp(syncer::ProtoTimeToTime(sync_data.timestamp_msec()));
- if (sync_data.has_favicon_url())
+ if (sync_data.has_favicon_url()) {
navigation.set_favicon_url(GURL(sync_data.favicon_url()));
+ }
if (sync_data.has_password_state()) {
navigation.set_password_state(
@@ -183,7 +96,7 @@ SerializedNavigationEntry SessionNavigationFromSyncData(
GURL(sync_data.replaced_navigation().first_committed_url());
replaced_entry_data.first_timestamp = syncer::ProtoTimeToTime(
sync_data.replaced_navigation().first_timestamp_msec());
- replaced_entry_data.first_transition_type = FromSyncPageTransition(
+ replaced_entry_data.first_transition_type = syncer::FromSyncPageTransition(
sync_data.replaced_navigation().first_page_transition());
navigation.set_replaced_entry_data(replaced_entry_data);
}
@@ -206,12 +119,8 @@ sync_pb::TabNavigation SessionNavigationToSyncData(
sync_data.set_title(base::UTF16ToUTF8(navigation.title()));
// Page transition core.
- static_assert(static_cast<int32_t>(ui::PAGE_TRANSITION_LAST_CORE) ==
- static_cast<int32_t>(ui::PAGE_TRANSITION_KEYWORD_GENERATED),
- "PAGE_TRANSITION_LAST_CORE must equal "
- "PAGE_TRANSITION_KEYWORD_GENERATED");
const ui::PageTransition transition_type = navigation.transition_type();
- sync_data.set_page_transition(ToSyncPageTransition(transition_type));
+ sync_data.set_page_transition(syncer::ToSyncPageTransition(transition_type));
// Page transition qualifiers.
if (ui::PageTransitionIsRedirect(transition_type)) {
@@ -282,8 +191,8 @@ sync_pb::TabNavigation SessionNavigationToSyncData(
replaced_entry_data->first_committed_url.spec());
replaced_navigation->set_first_timestamp_msec(
syncer::TimeToProtoTime(replaced_entry_data->first_timestamp));
- replaced_navigation->set_first_page_transition(
- ToSyncPageTransition(replaced_entry_data->first_transition_type));
+ replaced_navigation->set_first_page_transition(syncer::ToSyncPageTransition(
+ replaced_entry_data->first_transition_type));
}
sync_data.set_is_restored(navigation.is_restored());
@@ -341,8 +250,9 @@ sync_pb::SessionWindow SyncedSessionWindow::ToSessionWindowProto() const {
sync_data.set_window_id(wrapped_window.window_id.id());
sync_data.set_selected_tab_index(wrapped_window.selected_tab_index);
- for (const auto& tab : wrapped_window.tabs)
+ for (const auto& tab : wrapped_window.tabs) {
sync_data.add_tab(tab->tab_id.id());
+ }
return sync_data;
}
diff --git a/chromium/components/sync_sessions/synced_session_tracker.cc b/chromium/components/sync_sessions/synced_session_tracker.cc
index ea069d0ae25..490a11091b9 100644
--- a/chromium/components/sync_sessions/synced_session_tracker.cc
+++ b/chromium/components/sync_sessions/synced_session_tracker.cc
@@ -61,12 +61,14 @@ bool IsValidSessionHeader(const sync_pb::SessionHeader& header) {
std::set<int> session_window_ids;
std::set<int> session_tab_ids;
for (const sync_pb::SessionWindow& window : header.window()) {
- if (!session_window_ids.insert(window.window_id()).second)
+ if (!session_window_ids.insert(window.window_id()).second) {
return false;
+ }
for (int tab_id : window.tab()) {
- if (!session_tab_ids.insert(tab_id).second)
+ if (!session_tab_ids.insert(tab_id).second) {
return false;
+ }
}
}
@@ -93,8 +95,9 @@ void PopulateSyncedSessionWindowFromSpecifics(
session_window->window_id =
SessionID::FromSerializedValue(specifics.window_id());
}
- if (specifics.has_selected_tab_index())
+ if (specifics.has_selected_tab_index()) {
session_window->selected_tab_index = specifics.selected_tab_index();
+ }
synced_session_window->window_type = specifics.browser_type();
if (specifics.has_browser_type()) {
if (specifics.browser_type() ==
@@ -121,8 +124,9 @@ void PopulateSyncedSessionFromSpecifics(
base::Time mtime,
SyncedSession* synced_session,
SyncedSessionTracker* tracker) {
- if (header_specifics.has_client_name())
+ if (header_specifics.has_client_name()) {
synced_session->session_name = header_specifics.client_name();
+ }
if (header_specifics.has_device_type()) {
synced_session->device_type = header_specifics.device_type();
}
@@ -192,8 +196,9 @@ bool SyncedSessionTracker::LookupSessionWindows(
windows->clear();
const TrackedSession* session = LookupTrackedSession(session_tag);
- if (!session)
+ if (!session) {
return false; // We have no record of this session.
+ }
for (const auto& [window_id, window] : session->synced_session.windows) {
windows->push_back(&window->wrapped_window);
@@ -205,16 +210,19 @@ bool SyncedSessionTracker::LookupSessionWindows(
const sessions::SessionTab* SyncedSessionTracker::LookupSessionTab(
const std::string& tag,
SessionID tab_id) const {
- if (!tab_id.is_valid())
+ if (!tab_id.is_valid()) {
return nullptr;
+ }
const TrackedSession* session = LookupTrackedSession(tag);
- if (!session)
+ if (!session) {
return nullptr; // We have no record of this session.
+ }
auto tab_iter = session->synced_tab_map.find(tab_id);
- if (tab_iter == session->synced_tab_map.end())
+ if (tab_iter == session->synced_tab_map.end()) {
return nullptr; // We have no record of this tab.
+ }
return tab_iter->second;
}
@@ -223,12 +231,14 @@ absl::optional<sync_pb::SessionWindow::BrowserType>
SyncedSessionTracker::LookupWindowType(const std::string& session_tag,
SessionID window_id) const {
const TrackedSession* session = LookupTrackedSession(session_tag);
- if (!session)
+ if (!session) {
return absl::nullopt;
+ }
auto window_iter = session->synced_window_map.find(window_id);
- if (window_iter == session->synced_window_map.end())
+ if (window_iter == session->synced_window_map.end()) {
return absl::nullopt; // We have no record of this window.
+ }
return window_iter->second->window_type;
}
@@ -259,8 +269,9 @@ bool SyncedSessionTracker::DeleteForeignSession(
DCHECK_NE(local_session_tag_, session_tag);
auto iter = session_map_.find(session_tag);
- if (iter == session_map_.end())
+ if (iter == session_map_.end()) {
return false;
+ }
// An implicitly created session that has children tabs but no header node
// will have never had the device_type changed from unset.
@@ -296,8 +307,9 @@ void SyncedSessionTracker::DeleteForeignTab(const std::string& session_tag,
int tab_node_id) {
DCHECK_NE(local_session_tag_, session_tag);
TrackedSession* session = LookupTrackedSession(session_tag);
- if (session)
+ if (session) {
session->tab_node_pool.DeleteTabNode(tab_node_id);
+ }
}
const SyncedSessionTracker::TrackedSession*
@@ -316,8 +328,9 @@ SyncedSessionTracker::LookupTrackedSession(const std::string& session_tag) {
SyncedSessionTracker::TrackedSession* SyncedSessionTracker::GetTrackedSession(
const std::string& session_tag) {
TrackedSession* session = LookupTrackedSession(session_tag);
- if (session)
+ if (session) {
return session;
+ }
session = &session_map_[session_tag];
DVLOG(1) << "Creating new session with tag " << session_tag << " at "
@@ -354,8 +367,9 @@ void SyncedSessionTracker::CleanupSessionImpl(
const base::RepeatingCallback<bool(int /*tab_node_id*/)>&
is_tab_node_unsynced_cb) {
TrackedSession* session = LookupTrackedSession(session_tag);
- if (!session)
+ if (!session) {
return;
+ }
for (const auto& [window_id, window] : session->unmapped_windows) {
session->synced_window_map.erase(window_id);
@@ -394,8 +408,9 @@ void SyncedSessionTracker::CleanupSessionImpl(
bool SyncedSessionTracker::IsTabUnmappedForTesting(SessionID tab_id) {
const TrackedSession* session = LookupTrackedSession(local_session_tag_);
- if (!session)
+ if (!session) {
return false;
+ }
return session->unmapped_tabs.count(tab_id) != 0;
}
@@ -719,8 +734,9 @@ void UpdateTrackerWithSpecifics(const sync_pb::SessionSpecifics& specifics,
// Delete any closed windows and unused tabs as necessary. We exclude the
// local session here because it should be cleaned up explicitly with
// CleanupLocalTabs().
- if (session_tag != tracker->GetLocalSessionTag())
+ if (session_tag != tracker->GetLocalSessionTag()) {
tracker->CleanupSession(session_tag);
+ }
} else if (specifics.has_tab()) {
const sync_pb::SessionTab& tab_s = specifics.tab();
SessionID tab_id = SessionID::FromSerializedValue(tab_s.tab_id());
@@ -771,8 +787,9 @@ void UpdateTrackerWithSpecifics(const sync_pb::SessionSpecifics& specifics,
SetSessionTabFromSyncData(tab_s, modification_time, tab);
// Update the last modified time.
- if (session->modified_time < modification_time)
+ if (session->modified_time < modification_time) {
session->modified_time = modification_time;
+ }
} else {
LOG(WARNING) << "Ignoring session node with missing header/tab "
<< "fields and tag " << session_tag << ".";
diff --git a/chromium/components/sync_sessions/synced_session_unittest.cc b/chromium/components/sync_sessions/synced_session_unittest.cc
index db59256cfb7..6e39368962b 100644
--- a/chromium/components/sync_sessions/synced_session_unittest.cc
+++ b/chromium/components/sync_sessions/synced_session_unittest.cc
@@ -172,8 +172,9 @@ TEST(SyncedSessionTest, SessionNavigationToSyncDataWithTransitionTypes) {
// breaking.
for (uint32_t qualifier = ui::PAGE_TRANSITION_FORWARD_BACK; qualifier != 0;
qualifier <<= 1) {
- if (qualifier == static_cast<uint32_t>(ui::PAGE_TRANSITION_FROM_API))
+ if (qualifier == static_cast<uint32_t>(ui::PAGE_TRANSITION_FROM_API)) {
continue; // We don't sync PAGE_TRANSITION_FROM_API.
+ }
ui::PageTransition transition =
ui::PageTransitionFromInt(core_type | qualifier);
SerializedNavigationEntryTestHelper::SetTransitionType(transition,
diff --git a/chromium/components/sync_sessions/tab_node_pool_unittest.cc b/chromium/components/sync_sessions/tab_node_pool_unittest.cc
index 865fc97e108..18f8ac6a11e 100644
--- a/chromium/components/sync_sessions/tab_node_pool_unittest.cc
+++ b/chromium/components/sync_sessions/tab_node_pool_unittest.cc
@@ -13,7 +13,6 @@ namespace sync_sessions {
namespace {
using testing::ElementsAre;
-using testing::IsEmpty;
using testing::UnorderedElementsAre;
const int kTabNodeId1 = 10;
diff --git a/chromium/components/sync_sessions/test_synced_window_delegates_getter.cc b/chromium/components/sync_sessions/test_synced_window_delegates_getter.cc
index d7f06bd474e..cba0213b200 100644
--- a/chromium/components/sync_sessions/test_synced_window_delegates_getter.cc
+++ b/chromium/components/sync_sessions/test_synced_window_delegates_getter.cc
@@ -80,8 +80,9 @@ int TestSyncedTabDelegate::GetCurrentEntryIndex() const {
}
GURL TestSyncedTabDelegate::GetVirtualURLAtIndex(int i) const {
- if (static_cast<size_t>(i) >= entries_.size())
+ if (static_cast<size_t>(i) >= entries_.size()) {
return GURL();
+ }
return entries_[i]->virtual_url();
}
@@ -93,8 +94,9 @@ std::string TestSyncedTabDelegate::GetPageLanguageAtIndex(int i) const {
void TestSyncedTabDelegate::GetSerializedNavigationAtIndex(
int i,
sessions::SerializedNavigationEntry* serialized_entry) const {
- if (static_cast<size_t>(i) >= entries_.size())
+ if (static_cast<size_t>(i) >= entries_.size()) {
return;
+ }
*serialized_entry = *entries_[i];
}
@@ -142,10 +144,12 @@ bool TestSyncedTabDelegate::ShouldSync(SyncSessionsClient* sessions_client) {
// that there is at least one http:// url.
int http_count = 0;
for (auto& entry : entries_) {
- if (!entry->virtual_url().is_valid())
+ if (!entry->virtual_url().is_valid()) {
return false;
- if (entry->virtual_url().SchemeIsHTTPOrHTTPS())
+ }
+ if (entry->virtual_url().SchemeIsHTTPOrHTTPS()) {
http_count++;
+ }
}
return http_count > 0;
}
@@ -277,8 +281,9 @@ TestSyncedWindowDelegate::~TestSyncedWindowDelegate() = default;
void TestSyncedWindowDelegate::OverrideTabAt(int index,
SyncedTabDelegate* delegate) {
- if (index >= static_cast<int>(tab_delegates_.size()))
+ if (index >= static_cast<int>(tab_delegates_.size())) {
tab_delegates_.resize(index + 1, nullptr);
+ }
tab_delegates_[index] = delegate;
}
@@ -322,16 +327,18 @@ bool TestSyncedWindowDelegate::IsTabPinned(const SyncedTabDelegate* tab) const {
}
SyncedTabDelegate* TestSyncedWindowDelegate::GetTabAt(int index) const {
- if (index >= static_cast<int>(tab_delegates_.size()))
+ if (index >= static_cast<int>(tab_delegates_.size())) {
return nullptr;
+ }
return tab_delegates_[index];
}
SessionID TestSyncedWindowDelegate::GetTabIdAt(int index) const {
SyncedTabDelegate* delegate = GetTabAt(index);
- if (!delegate)
+ if (!delegate) {
return SessionID::InvalidValue();
+ }
return delegate->GetSessionId();
}
@@ -391,8 +398,9 @@ void TestSyncedWindowDelegatesGetter::CloseTab(SessionID tab_id) {
}
void TestSyncedWindowDelegatesGetter::SessionRestoreComplete() {
- for (auto& window : windows_)
+ for (auto& window : windows_) {
window->SetIsSessionRestoreInProgress(false);
+ }
router_.NotifySessionRestoreComplete();
}
@@ -409,8 +417,9 @@ TestSyncedWindowDelegatesGetter::GetSyncedWindowDelegates() {
const SyncedWindowDelegate* TestSyncedWindowDelegatesGetter::FindById(
SessionID session_id) {
for (const auto& [window_id, delegate] : delegates_) {
- if (delegate->GetSessionId() == session_id)
+ if (delegate->GetSessionId() == session_id) {
return delegate;
+ }
}
return nullptr;
}
@@ -430,14 +439,16 @@ void TestSyncedWindowDelegatesGetter::DummyRouter::Stop() {
void TestSyncedWindowDelegatesGetter::DummyRouter::NotifyNav(
SyncedTabDelegate* tab) {
- if (handler_)
+ if (handler_) {
handler_->OnLocalTabModified(tab);
+ }
}
void TestSyncedWindowDelegatesGetter::DummyRouter::
NotifySessionRestoreComplete() {
- if (handler_)
+ if (handler_) {
handler_->OnSessionRestoreComplete();
+ }
}
} // namespace sync_sessions
diff --git a/chromium/components/sync_user_events/user_event_service_impl_unittest.cc b/chromium/components/sync_user_events/user_event_service_impl_unittest.cc
index 5d7082b73d0..d088e3111dc 100644
--- a/chromium/components/sync_user_events/user_event_service_impl_unittest.cc
+++ b/chromium/components/sync_user_events/user_event_service_impl_unittest.cc
@@ -17,7 +17,6 @@
#include "testing/gtest/include/gtest/gtest.h"
using sync_pb::UserEventSpecifics;
-using testing::_;
namespace syncer {
diff --git a/chromium/components/sync_user_events/user_event_sync_bridge_unittest.cc b/chromium/components/sync_user_events/user_event_sync_bridge_unittest.cc
index 21efb52e62a..d1085ed1a22 100644
--- a/chromium/components/sync_user_events/user_event_sync_bridge_unittest.cc
+++ b/chromium/components/sync_user_events/user_event_sync_bridge_unittest.cc
@@ -26,7 +26,6 @@ namespace {
using sync_pb::UserEventSpecifics;
using testing::_;
using testing::ElementsAre;
-using testing::Eq;
using testing::InvokeWithoutArgs;
using testing::IsEmpty;
using testing::IsNull;
diff --git a/chromium/components/system_media_controls/mac/remote_command_center_delegate.h b/chromium/components/system_media_controls/mac/remote_command_center_delegate.h
index f52d04fb6a8..b9256e3a82b 100644
--- a/chromium/components/system_media_controls/mac/remote_command_center_delegate.h
+++ b/chromium/components/system_media_controls/mac/remote_command_center_delegate.h
@@ -22,7 +22,7 @@ class SystemMediaControlsObserver;
namespace internal {
// Wraps an NSObject which interfaces with the MPRemoteCommandCenter.
-class API_AVAILABLE(macos(10.12.2)) RemoteCommandCenterDelegate {
+class RemoteCommandCenterDelegate {
public:
RemoteCommandCenterDelegate();
diff --git a/chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.h b/chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.h
index 2adb94c6199..25c210e3d53 100644
--- a/chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.h
+++ b/chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.h
@@ -14,7 +14,6 @@ class RemoteCommandCenterDelegate;
} // namespace internal
} // namespace system_media_controls
-API_AVAILABLE(macos(10.12.2))
@interface RemoteCommandCenterDelegateCocoa : NSObject {
@private
system_media_controls::internal::RemoteCommandCenterDelegate* _delegate;
diff --git a/chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.mm b/chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.mm
index 610cacb65ef..710af92af42 100644
--- a/chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.mm
+++ b/chromium/components/system_media_controls/mac/remote_command_center_delegate_cocoa.mm
@@ -9,7 +9,6 @@
#include "base/time/time.h"
#include "components/system_media_controls/mac/remote_command_center_delegate.h"
-API_AVAILABLE(macos(10.12.2))
@interface RemoteCommandCenterDelegateCocoa ()
- (void)setCommand:(MPRemoteCommand*)command enabled:(bool)enabled;
diff --git a/chromium/components/thin_webview/BUILD.gn b/chromium/components/thin_webview/BUILD.gn
index 204b6a05533..d0aa3fb9aec 100644
--- a/chromium/components/thin_webview/BUILD.gn
+++ b/chromium/components/thin_webview/BUILD.gn
@@ -22,7 +22,6 @@ android_library("java") {
]
deps = [
- "//base:base_java",
"//components/embedder_support/android:web_contents_delegate_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
@@ -39,7 +38,6 @@ android_library("factory_java") {
deps = [
":java",
"internal:internal_java",
- "//base:base_java",
"//content/public/android:content_java",
"//ui/android:ui_java",
]
diff --git a/chromium/components/thin_webview/internal/BUILD.gn b/chromium/components/thin_webview/internal/BUILD.gn
index 9a12e7f86ff..3f96b8301f1 100644
--- a/chromium/components/thin_webview/internal/BUILD.gn
+++ b/chromium/components/thin_webview/internal/BUILD.gn
@@ -36,6 +36,8 @@ android_library("internal_java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/embedder_support/android:web_contents_delegate_java",
"//components/thin_webview:java",
"//content/public/android:content_java",
diff --git a/chromium/components/thin_webview/internal/compositor_view_impl.cc b/chromium/components/thin_webview/internal/compositor_view_impl.cc
index 08ce98662b4..e1d39c9dffe 100644
--- a/chromium/components/thin_webview/internal/compositor_view_impl.cc
+++ b/chromium/components/thin_webview/internal/compositor_view_impl.cc
@@ -58,7 +58,9 @@ CompositorViewImpl::CompositorViewImpl(JNIEnv* env,
root_layer_->SetIsDrawable(true);
absl::optional<SkColor> background_color =
ui::JavaColorToOptionalSkColor(java_background_color);
- root_layer_->SetBackgroundColor(background_color.value());
+ // TODO(crbug/1308932): Remove FromColor and make all SkColor4f.
+ root_layer_->SetBackgroundColor(
+ SkColor4f::FromColor(background_color.value()));
}
CompositorViewImpl::~CompositorViewImpl() = default;
diff --git a/chromium/components/tracing/BUILD.gn b/chromium/components/tracing/BUILD.gn
index 79940ddc927..db698308e59 100644
--- a/chromium/components/tracing/BUILD.gn
+++ b/chromium/components/tracing/BUILD.gn
@@ -50,6 +50,26 @@ component("background_tracing_metrics_provider") {
]
}
+component("background_tracing_utils") {
+ sources = [
+ "common/background_tracing_state_manager.cc",
+ "common/background_tracing_state_manager.h",
+ "common/background_tracing_utils.cc",
+ "common/background_tracing_utils.h",
+ "common/pref_names.cc",
+ "common/pref_names.h",
+ ]
+
+ defines = [ "IS_BACKGROUND_TRACING_UTILS_IMPL" ]
+
+ deps = [
+ ":startup_tracing",
+ "//base",
+ "//components/prefs",
+ "//content/public/browser",
+ ]
+}
+
source_set("unit_tests") {
testonly = true
@@ -68,6 +88,19 @@ source_set("unit_tests") {
sources = [ "common/trace_startup_config_unittest.cc" ]
deps += [ ":startup_tracing" ]
}
+
+ sources += [
+ "common/background_tracing_state_manager_unittest.cc",
+ "common/background_tracing_utils_unittest.cc",
+ ]
+ deps += [
+ ":background_tracing_utils",
+ ":startup_tracing",
+ "//components/metrics:metrics",
+ "//components/prefs:test_support",
+ "//content/public/browser",
+ "//content/test:test_support",
+ ]
}
test("tracing_perftests") {
diff --git a/chromium/components/tracing/DEPS b/chromium/components/tracing/DEPS
index 116393a7b5c..43605247fd7 100644
--- a/chromium/components/tracing/DEPS
+++ b/chromium/components/tracing/DEPS
@@ -1,12 +1,18 @@
include_rules = [
"+ipc",
"+components/metrics",
+ "+components/prefs",
"+third_party/metrics_proto",
"+content/public/browser/background_tracing_manager.h",
+ "+content/public/browser/background_tracing_config.h",
+ "+content/public/browser/browser_thread.h",
]
specific_include_rules = {
'.*browsertest\.cc': [
"+content/public/test",
],
+ '.*unittest.cc': [
+ "+content/public/test",
+ ],
}
diff --git a/chromium/components/tracing/common/background_tracing_metrics_provider.cc b/chromium/components/tracing/common/background_tracing_metrics_provider.cc
index 3ce2c68fdca..00967f40484 100644
--- a/chromium/components/tracing/common/background_tracing_metrics_provider.cc
+++ b/chromium/components/tracing/common/background_tracing_metrics_provider.cc
@@ -25,20 +25,15 @@ void BackgroundTracingMetricsProvider::Init() {
}
bool BackgroundTracingMetricsProvider::HasIndependentMetrics() {
- return content::BackgroundTracingManager::GetInstance()->HasTraceToUpload();
+ return content::BackgroundTracingManager::GetInstance().HasTraceToUpload();
}
void BackgroundTracingMetricsProvider::ProvideIndependentMetrics(
base::OnceCallback<void(bool)> done_callback,
metrics::ChromeUserMetricsExtension* uma_proto,
base::HistogramSnapshotManager* snapshot_manager) {
- auto* tracing_manager = content::BackgroundTracingManager::GetInstance();
- // TODO(crbug.com/1290887): remove this when
- // content::BackgroundTracingManager::GetInstance() is updated to return a
- // reference.
- DCHECK(tracing_manager);
-
- auto serialized_trace = tracing_manager->GetLatestTraceToUpload();
+ auto serialized_trace =
+ content::BackgroundTracingManager::GetInstance().GetLatestTraceToUpload();
if (serialized_trace.empty()) {
std::move(done_callback).Run(false);
return;
diff --git a/chromium/components/tracing/common/background_tracing_state_manager.cc b/chromium/components/tracing/common/background_tracing_state_manager.cc
new file mode 100644
index 00000000000..e8490484716
--- /dev/null
+++ b/chromium/components/tracing/common/background_tracing_state_manager.cc
@@ -0,0 +1,219 @@
+// Copyright 2022 The Chromium 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/tracing/common/background_tracing_state_manager.h"
+
+#include "base/json/values_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/tracing/common/pref_names.h"
+#include "content/public/browser/background_tracing_config.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+
+constexpr char kTracingStateKey[] = "state";
+constexpr char kUploadTimesKey[] = "upload_times";
+constexpr char kScenarioKey[] = "scenario";
+constexpr char kUploadTimestampKey[] = "time";
+
+const int kMinDaysUntilNextUpload = 7;
+
+// Removes any version numbers from the scenario name.
+std::string StripScenarioName(const std::string& scenario_name) {
+ std::string stripped_scenario_name;
+ base::RemoveChars(scenario_name, "1234567890", &stripped_scenario_name);
+ return stripped_scenario_name;
+}
+
+} // namespace
+
+namespace tracing {
+
+BackgroundTracingStateManager::BackgroundTracingStateManager() = default;
+BackgroundTracingStateManager::~BackgroundTracingStateManager() = default;
+
+BackgroundTracingStateManager& BackgroundTracingStateManager::GetInstance() {
+ static base::NoDestructor<BackgroundTracingStateManager> instance;
+ return *instance;
+}
+
+void BackgroundTracingStateManager::SetPrefServiceForTesting(
+ PrefService* local_state) {
+ if (!local_state_ && local_state)
+ local_state_ = local_state;
+}
+
+void BackgroundTracingStateManager::Initialize(PrefService* local_state) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (initialized_)
+ return;
+
+ initialized_ = true;
+
+ if (!local_state_ && local_state)
+ local_state_ = local_state;
+
+ DCHECK(local_state_);
+ const base::Value* dict =
+ local_state_->GetDictionary(kBackgroundTracingSessionState);
+
+ if (!dict) {
+ SaveState();
+ return;
+ }
+
+ absl::optional<int> state = dict->FindIntKey(kTracingStateKey);
+
+ if (state) {
+ if (*state >= 0 &&
+ *state <= static_cast<int>(BackgroundTracingState::LAST)) {
+ last_session_end_state_ = static_cast<BackgroundTracingState>(*state);
+ } else {
+ last_session_end_state_ = BackgroundTracingState::NOT_ACTIVATED;
+ }
+ }
+
+ const base::Value* upload_times = dict->FindListKey(kUploadTimesKey);
+ if (upload_times) {
+ for (const auto& scenario_dict : upload_times->GetListDeprecated()) {
+ DCHECK(scenario_dict.is_dict());
+ const std::string* scenario = scenario_dict.FindStringKey(kScenarioKey);
+ const base::Value* timestamp_val =
+ scenario_dict.FindKey(kUploadTimestampKey);
+ if (!scenario || !timestamp_val) {
+ continue;
+ }
+
+ absl::optional<base::Time> upload_time = base::ValueToTime(timestamp_val);
+ if (!upload_time) {
+ continue;
+ }
+
+ if ((base::Time::Now() - *upload_time) >
+ base::Days(kMinDaysUntilNextUpload)) {
+ continue;
+ }
+ scenario_last_upload_timestamp_[*scenario] = *upload_time;
+ }
+ }
+
+ // Save state to update the current session state, replacing the previous
+ // session state.
+ SaveState();
+}
+
+void BackgroundTracingStateManager::SaveState() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(initialized_);
+ SaveState(scenario_last_upload_timestamp_, state_);
+}
+
+void BackgroundTracingStateManager::SaveState(
+ const ScenarioUploadTimestampMap& scenario_upload_times,
+ BackgroundTracingState state) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetIntKey(kTracingStateKey, static_cast<int>(state));
+ base::Value upload_times(base::Value::Type::LIST);
+ for (const auto& it : scenario_upload_times) {
+ base::Value scenario(base::Value::Type::DICTIONARY);
+ scenario.SetStringKey(kScenarioKey, StripScenarioName(it.first));
+ scenario.SetKey(kUploadTimestampKey, base::TimeToValue(it.second));
+ upload_times.Append(std::move(scenario));
+ }
+
+ dict.SetKey(kUploadTimesKey, std::move(upload_times));
+
+ local_state_->Set(kBackgroundTracingSessionState, std::move(dict));
+ local_state_->CommitPendingWrite();
+}
+
+bool BackgroundTracingStateManager::DidLastSessionEndUnexpectedly() const {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(initialized_);
+ switch (last_session_end_state_) {
+ case BackgroundTracingState::NOT_ACTIVATED:
+ case BackgroundTracingState::RAN_30_SECONDS:
+ case BackgroundTracingState::FINALIZATION_STARTED:
+ return false;
+ case BackgroundTracingState::STARTED:
+ // If the browser did not run for 30 seconds after tracing started in
+ // previous session then do not start tracing in current session as a
+ // safeguard. This would be impacted by short sessions (eg: on Android),
+ // but worth the tradeoff of crashing loop on startup. Checking for
+ // previous session crash status is platform dependent and the crash
+ // status is initialized at later point than when tracing begins. So, this
+ // check is safer than waiting for crash metrics to be available. Note
+ // that this setting only checks for last session and not sessions before
+ // that. So, the next session might still crash due to tracing if the user
+ // has another tracing experiment. But, meanwhile we would be able to turn
+ // off tracing experiments based on uploaded crash metrics.
+ return true;
+ }
+}
+
+bool BackgroundTracingStateManager::DidRecentlyUploadForScenario(
+ const content::BackgroundTracingConfig& config) const {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(initialized_);
+ std::string stripped_scenario_name =
+ StripScenarioName(config.scenario_name());
+ auto it = scenario_last_upload_timestamp_.find(stripped_scenario_name);
+ if (it != scenario_last_upload_timestamp_.end()) {
+ return (base::Time::Now() - it->second) <=
+ base::Days(kMinDaysUntilNextUpload);
+ }
+ return false;
+}
+
+void BackgroundTracingStateManager::NotifyTracingStarted() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ SetState(BackgroundTracingState::STARTED);
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, base::BindOnce([]() {
+ BackgroundTracingStateManager::GetInstance().SetState(
+ BackgroundTracingState::RAN_30_SECONDS);
+ }),
+ base::Seconds(30));
+}
+
+void BackgroundTracingStateManager::NotifyFinalizationStarted() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ SetState(BackgroundTracingState::FINALIZATION_STARTED);
+}
+
+void BackgroundTracingStateManager::SetState(BackgroundTracingState new_state) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(initialized_);
+ if (state_ == new_state) {
+ return;
+ }
+ // If finalization started before 30 seconds, skip recording the new state.
+ if (new_state == BackgroundTracingState::RAN_30_SECONDS &&
+ state_ == BackgroundTracingState::FINALIZATION_STARTED) {
+ return;
+ }
+ state_ = new_state;
+ SaveState();
+}
+
+void BackgroundTracingStateManager::OnScenarioUploaded(
+ const std::string& scenario_name) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(initialized_);
+
+ scenario_last_upload_timestamp_[StripScenarioName(scenario_name)] =
+ base::Time::Now();
+ SaveState();
+}
+
+void BackgroundTracingStateManager::Reset() {
+ initialized_ = false;
+ local_state_ = nullptr;
+ state_ = BackgroundTracingState::NOT_ACTIVATED;
+ last_session_end_state_ = BackgroundTracingState::NOT_ACTIVATED;
+ scenario_last_upload_timestamp_.clear();
+}
+
+} // namespace tracing
diff --git a/chromium/components/tracing/common/background_tracing_state_manager.h b/chromium/components/tracing/common/background_tracing_state_manager.h
new file mode 100644
index 00000000000..a3f0677e67d
--- /dev/null
+++ b/chromium/components/tracing/common/background_tracing_state_manager.h
@@ -0,0 +1,116 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_TRACING_COMMON_BACKGROUND_TRACING_STATE_MANAGER_H_
+#define COMPONENTS_TRACING_COMMON_BACKGROUND_TRACING_STATE_MANAGER_H_
+
+#include "base/component_export.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/raw_ptr.h"
+#include "base/no_destructor.h"
+#include "base/time/time.h"
+#include "components/prefs/pref_service.h"
+
+namespace android_webview {
+class AwTracingDelegateTest;
+}
+
+namespace content {
+class BackgroundTracingConfig;
+}
+
+namespace tracing {
+
+class BackgroundTracingStateManagerTest;
+
+// Do not remove or change the order of enum fields since it is stored in
+// preferences.
+enum class BackgroundTracingState : int {
+ // Default state when tracing is not started in previous session, or when
+ // state is not found or invalid.
+ NOT_ACTIVATED = 0,
+ STARTED = 1,
+ RAN_30_SECONDS = 2,
+ FINALIZATION_STARTED = 3,
+ LAST = FINALIZATION_STARTED,
+};
+
+// Manages local state prefs for background tracing, and tracks state from
+// previous background tracing session(s). All the calls are expected to run on
+// UI thread.
+class COMPONENT_EXPORT(BACKGROUND_TRACING_UTILS) BackgroundTracingStateManager {
+ public:
+ using ScenarioUploadTimestampMap = base::flat_map<std::string, base::Time>;
+
+ static BackgroundTracingStateManager& GetInstance();
+
+ // Initializes state from previous session and writes current state to
+ // prefs, when called the first time. NOOP on any calls after that. It also
+ // deletes any expired entries from prefs.
+ void Initialize(PrefService* local_state);
+
+ // Used in tests when other methods of this class need to be called before
+ // Initialize().
+ void SetPrefServiceForTesting(PrefService* local_state);
+
+ // True if last session potentially crashed and it is unsafe to turn on
+ // background tracing in current session.
+ bool DidLastSessionEndUnexpectedly() const;
+
+ // True if the embedder uploaded a trace for the given |config| recently, and
+ // uploads should be throttled for the |config|.
+ bool DidRecentlyUploadForScenario(
+ const content::BackgroundTracingConfig& config) const;
+
+ // The embedder should call this method every time background tracing starts
+ // so that the state in prefs is updated. Posts a timer task to the current
+ // sequence to update the state once more to denote no crashes after a
+ // reasonable time (see DidLastSessionEndUnexpectedly()).
+ void NotifyTracingStarted();
+
+ // The embedder should call this method every time background tracing finishes
+ // so that the state in prefs is updated.
+ void NotifyFinalizationStarted();
+
+ // Updates the state to include the upload time for |scenario_name|, and
+ // saves it to prefs.
+ void OnScenarioUploaded(const std::string& scenario_name);
+
+ // Saves the given state to prefs, public for testing.
+ void SaveState(const ScenarioUploadTimestampMap& upload_times,
+ BackgroundTracingState state);
+
+ private:
+ friend base::NoDestructor<BackgroundTracingStateManager>;
+ friend class tracing::BackgroundTracingStateManagerTest;
+ friend class android_webview::AwTracingDelegateTest;
+
+ BackgroundTracingStateManager();
+ ~BackgroundTracingStateManager();
+
+ void SaveState();
+
+ // Updates the current tracing state and saves it to prefs.
+ void SetState(BackgroundTracingState new_state);
+
+ // Used in tests to reset the state since a singleton instance is never
+ // destroyed.
+ void Reset();
+
+ BackgroundTracingState state_ = BackgroundTracingState::NOT_ACTIVATED;
+
+ bool initialized_ = false;
+
+ raw_ptr<PrefService> local_state_ = nullptr;
+
+ // Following are valid only when |initialized_| = true.
+ BackgroundTracingState last_session_end_state_ =
+ BackgroundTracingState::NOT_ACTIVATED;
+
+ ScenarioUploadTimestampMap scenario_last_upload_timestamp_;
+};
+
+} // namespace tracing
+
+#endif // COMPONENTS_TRACING_COMMON_BACKGROUND_TRACING_STATE_MANAGER_H_
diff --git a/chromium/components/tracing/common/background_tracing_state_manager_unittest.cc b/chromium/components/tracing/common/background_tracing_state_manager_unittest.cc
new file mode 100644
index 00000000000..260c3e250f3
--- /dev/null
+++ b/chromium/components/tracing/common/background_tracing_state_manager_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright 2022 The Chromium 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/tracing/common/background_tracing_state_manager.h"
+
+#include "base/json/json_writer.h"
+#include "base/json/values_util.h"
+#include "base/strings/pattern.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/tracing/common/pref_names.h"
+#include "content/public/browser/background_tracing_config.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+
+class BackgroundTracingStateManagerTest : public testing::Test {
+ public:
+ void SetUp() override {
+ pref_service_ = std::make_unique<TestingPrefServiceSimple>();
+ pref_service_->registry()->RegisterBooleanPref(
+ metrics::prefs::kMetricsReportingEnabled, false);
+ pref_service_->SetBoolean(metrics::prefs::kMetricsReportingEnabled, true);
+ tracing::RegisterPrefs(pref_service_->registry());
+ tracing::BackgroundTracingStateManager::GetInstance()
+ .SetPrefServiceForTesting(pref_service_.get());
+ }
+
+ void TearDown() override {
+ tracing::BackgroundTracingStateManager::GetInstance().Reset();
+ }
+
+ std::string GetSessionStateJson() {
+ const base::Value* state =
+ pref_service_->GetDictionary(tracing::kBackgroundTracingSessionState);
+
+ std::string json;
+ EXPECT_TRUE(base::JSONWriter::Write(*state, &json));
+ return json;
+ }
+
+ std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+
+ private:
+ content::BrowserTaskEnvironment task_environment_;
+};
+
+TEST_F(BackgroundTracingStateManagerTest, InitializeEmptyPrefs) {
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+ EXPECT_EQ(GetSessionStateJson(), R"({"state":0,"upload_times":[]})");
+}
+
+TEST_F(BackgroundTracingStateManagerTest, InitializeInvalidState) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetIntKey("state",
+ static_cast<int>(tracing::BackgroundTracingState::LAST) + 1);
+ pref_service_->Set(tracing::kBackgroundTracingSessionState, std::move(dict));
+
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+ EXPECT_EQ(GetSessionStateJson(), R"({"state":0,"upload_times":[]})");
+}
+
+TEST_F(BackgroundTracingStateManagerTest, InitializeNoScenario) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetIntKey("state", static_cast<int>(
+ tracing::BackgroundTracingState::NOT_ACTIVATED));
+ base::Value upload_times(base::Value::Type::LIST);
+ base::Value scenario(base::Value::Type::DICTIONARY);
+ scenario.SetKey("time", base::TimeToValue(base::Time::Now()));
+ upload_times.Append(std::move(scenario));
+ dict.SetKey("upload_times", std::move(upload_times));
+ pref_service_->Set(tracing::kBackgroundTracingSessionState, std::move(dict));
+
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+ EXPECT_EQ(GetSessionStateJson(), R"({"state":0,"upload_times":[]})");
+}
+
+TEST_F(BackgroundTracingStateManagerTest, InitializeValidPrefs) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetIntKey("state", static_cast<int>(
+ tracing::BackgroundTracingState::NOT_ACTIVATED));
+ base::Value upload_times(base::Value::Type::LIST);
+ base::Value scenario(base::Value::Type::DICTIONARY);
+ scenario.SetStringKey("scenario", "TestScenario");
+ scenario.SetKey("time", base::TimeToValue(base::Time::Now()));
+ upload_times.Append(std::move(scenario));
+ dict.SetKey("upload_times", std::move(upload_times));
+ pref_service_->Set(tracing::kBackgroundTracingSessionState, std::move(dict));
+
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+ EXPECT_TRUE(base::MatchPattern(
+ GetSessionStateJson(),
+ R"({"state":0,"upload_times":[{"scenario":"TestScenario","time":"*"}]})"))
+ << "Actual: " << GetSessionStateJson();
+ ;
+}
+
+TEST_F(BackgroundTracingStateManagerTest, SaveStateValidPrefs) {
+ tracing::BackgroundTracingStateManager::GetInstance().SaveState(
+ {{"TestScenario", base::Time::Now()}},
+ tracing::BackgroundTracingState::NOT_ACTIVATED);
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+
+ EXPECT_TRUE(base::MatchPattern(
+ GetSessionStateJson(),
+ R"({"state":0,"upload_times":[{"scenario":"TestScenario","time":"*"}]})"))
+ << "Actual: " << GetSessionStateJson();
+ EXPECT_FALSE(tracing::BackgroundTracingStateManager::GetInstance()
+ .DidLastSessionEndUnexpectedly());
+}
+
+TEST_F(BackgroundTracingStateManagerTest, SessionEndedUnexpectedly) {
+ tracing::BackgroundTracingStateManager::GetInstance().SaveState(
+ {}, tracing::BackgroundTracingState::STARTED);
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+ EXPECT_TRUE(tracing::BackgroundTracingStateManager::GetInstance()
+ .DidLastSessionEndUnexpectedly());
+}
+
+TEST_F(BackgroundTracingStateManagerTest, NotUploadedRecently) {
+ tracing::BackgroundTracingStateManager::GetInstance().SaveState(
+ {{"TestScenario", base::Time::Now() - base::Days(8)}},
+ tracing::BackgroundTracingState::NOT_ACTIVATED);
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetStringKey("scenario_name", "TestScenario");
+ dict.SetStringKey("mode", "PREEMPTIVE_TRACING_MODE");
+ dict.SetStringKey("custom_categories", "toplevel");
+ base::Value rules_list(base::Value::Type::LIST);
+
+ {
+ base::Value rules_dict(base::Value::Type::DICTIONARY);
+ rules_dict.SetStringKey("rule", "MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED");
+ rules_dict.SetStringKey("trigger_name", "test");
+ rules_list.Append(std::move(rules_dict));
+ }
+
+ dict.SetKey("configs", std::move(rules_list));
+ std::unique_ptr<content::BackgroundTracingConfig> config(
+ content::BackgroundTracingConfig::FromDict(std::move(dict)));
+
+ EXPECT_FALSE(tracing::BackgroundTracingStateManager::GetInstance()
+ .DidRecentlyUploadForScenario(*config));
+}
+
+TEST_F(BackgroundTracingStateManagerTest, UploadedRecently) {
+ tracing::BackgroundTracingStateManager::GetInstance().SaveState(
+ {{"TestScenario", base::Time::Now() - base::Days(1)}},
+ tracing::BackgroundTracingState::NOT_ACTIVATED);
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetStringKey("scenario_name", "TestScenario");
+ dict.SetStringKey("mode", "PREEMPTIVE_TRACING_MODE");
+ dict.SetStringKey("custom_categories", "toplevel");
+ base::Value rules_list(base::Value::Type::LIST);
+
+ {
+ base::Value rules_dict(base::Value::Type::DICTIONARY);
+ rules_dict.SetStringKey("rule", "MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED");
+ rules_dict.SetStringKey("trigger_name", "test");
+ rules_list.Append(std::move(rules_dict));
+ }
+
+ dict.SetKey("configs", std::move(rules_list));
+ std::unique_ptr<content::BackgroundTracingConfig> config(
+ content::BackgroundTracingConfig::FromDict(std::move(dict)));
+
+ EXPECT_TRUE(tracing::BackgroundTracingStateManager::GetInstance()
+ .DidRecentlyUploadForScenario(*config));
+}
+
+TEST_F(BackgroundTracingStateManagerTest, NotifyTracingStarted) {
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+ tracing::BackgroundTracingStateManager::GetInstance().NotifyTracingStarted();
+ EXPECT_TRUE(base::MatchPattern(GetSessionStateJson(),
+ R"({"state":1,"upload_times":[]})"))
+ << "Actual: " << GetSessionStateJson();
+}
+
+TEST_F(BackgroundTracingStateManagerTest, NotifyFinalizationStarted) {
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+ tracing::BackgroundTracingStateManager::GetInstance()
+ .NotifyFinalizationStarted();
+ EXPECT_TRUE(base::MatchPattern(GetSessionStateJson(),
+ R"({"state":3,"upload_times":[]})"))
+ << "Actual: " << GetSessionStateJson();
+}
+
+TEST_F(BackgroundTracingStateManagerTest, OnScenarioUploaded) {
+ tracing::BackgroundTracingStateManager::GetInstance().Initialize(nullptr);
+ tracing::BackgroundTracingStateManager::GetInstance()
+ .NotifyFinalizationStarted();
+ tracing::BackgroundTracingStateManager::GetInstance().OnScenarioUploaded(
+ "TestScenario");
+ EXPECT_TRUE(base::MatchPattern(
+ GetSessionStateJson(),
+ R"({"state":3,"upload_times":[{"scenario":"TestScenario","time":"*"}]})"))
+ << "Actual: " << GetSessionStateJson();
+}
+} // namespace tracing
diff --git a/chromium/components/tracing/common/background_tracing_utils.cc b/chromium/components/tracing/common/background_tracing_utils.cc
new file mode 100644
index 00000000000..24a417553c7
--- /dev/null
+++ b/chromium/components/tracing/common/background_tracing_utils.cc
@@ -0,0 +1,183 @@
+// Copyright 2022 The Chromium 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/tracing/common/background_tracing_utils.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "components/tracing/common/tracing_switches.h"
+#include "content/public/browser/background_tracing_manager.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace tracing {
+
+namespace {
+
+bool BlockingWriteTraceToFile(const base::FilePath& output_file,
+ std::unique_ptr<std::string> file_contents) {
+ if (base::WriteFile(output_file, *file_contents)) {
+ LOG(ERROR) << "Background trace written to "
+ << output_file.LossyDisplayName();
+ return true;
+ }
+ LOG(ERROR) << "Failed to write background trace to "
+ << output_file.LossyDisplayName();
+ return false;
+}
+
+void WriteTraceToFile(
+ const base::FilePath& output_file,
+ std::unique_ptr<std::string> file_contents,
+ content::BackgroundTracingManager::FinishedProcessingCallback
+ done_callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE, {base::MayBlock()},
+ base::BindOnce(&BlockingWriteTraceToFile, output_file,
+ std::move(file_contents)),
+ std::move(done_callback));
+}
+
+std::unique_ptr<content::BackgroundTracingConfig>
+GetBackgroundTracingConfigFromFile(const base::FilePath& config_file) {
+ std::string config_text;
+ if (!base::ReadFileToString(config_file, &config_text) ||
+ config_text.empty()) {
+ LOG(ERROR) << "Failed to read background tracing config file "
+ << config_file.value();
+ return nullptr;
+ }
+
+ base::JSONReader::ValueWithError value_with_error =
+ base::JSONReader::ReadAndReturnValueWithError(
+ config_text, base::JSON_ALLOW_TRAILING_COMMAS);
+ if (!value_with_error.value) {
+ LOG(ERROR) << "Background tracing has incorrect config: "
+ << value_with_error.error_message;
+ return nullptr;
+ }
+
+ if (!value_with_error.value->is_dict()) {
+ LOG(ERROR) << "Background tracing config is not a dict";
+ return nullptr;
+ }
+
+ auto config = content::BackgroundTracingConfig::FromDict(
+ std::move(*(value_with_error.value)));
+
+ if (!config) {
+ LOG(ERROR) << "Background tracing config dict has invalid contents";
+ return nullptr;
+ }
+
+ return config;
+}
+
+} // namespace
+
+void RecordDisallowedMetric(TracingFinalizationDisallowedReason reason) {
+ UMA_HISTOGRAM_ENUMERATION("Tracing.Background.FinalizationDisallowedReason",
+ reason);
+}
+
+void SetupBackgroundTracingWithOutputFile(
+ std::unique_ptr<content::BackgroundTracingConfig> config,
+ const base::FilePath& output_file) {
+ if (output_file.empty()) {
+ return;
+ }
+
+ auto receive_callback = base::BindRepeating(&WriteTraceToFile, output_file);
+
+ // NO_DATA_FILTERING is set because the trace is saved to a local output file
+ // instead of being uploaded to a metrics server, so there are no PII
+ // concerns.
+ content::BackgroundTracingManager::GetInstance()
+ .SetActiveScenarioWithReceiveCallback(
+ std::move(config), std::move(receive_callback),
+ content::BackgroundTracingManager::NO_DATA_FILTERING);
+}
+
+void SetupBackgroundTracingFromConfigFile(const base::FilePath& config_file,
+ const base::FilePath& output_file) {
+ std::unique_ptr<content::BackgroundTracingConfig> config =
+ GetBackgroundTracingConfigFromFile(config_file);
+ if (!config) {
+ return;
+ }
+
+ SetupBackgroundTracingWithOutputFile(std::move(config), output_file);
+}
+
+bool SetupBackgroundTracingFromCommandLine(
+ const std::string& field_trial_name) {
+ auto& manager = content::BackgroundTracingManager::GetInstance();
+ auto* command_line = base::CommandLine::ForCurrentProcess();
+
+ switch (GetBackgroundTracingSetupMode()) {
+ case BackgroundTracingSetupMode::kDisabledInvalidCommandLine:
+ return false;
+ case BackgroundTracingSetupMode::kFromConfigFile:
+ SetupBackgroundTracingFromConfigFile(
+ command_line->GetSwitchValuePath(switches::kEnableBackgroundTracing),
+ command_line->GetSwitchValuePath(
+ switches::kBackgroundTracingOutputFile));
+ return true;
+ case BackgroundTracingSetupMode::kFromFieldTrialLocalOutput:
+ SetupBackgroundTracingWithOutputFile(
+ manager.GetBackgroundTracingConfig(field_trial_name),
+ command_line->GetSwitchValuePath(
+ switches::kBackgroundTracingOutputFile));
+ return true;
+ case BackgroundTracingSetupMode::kFromFieldTrial:
+ return false;
+ }
+}
+
+BackgroundTracingSetupMode GetBackgroundTracingSetupMode() {
+ auto* command_line = base::CommandLine::ForCurrentProcess();
+ if (!command_line->HasSwitch(switches::kEnableBackgroundTracing)) {
+ if (!command_line->HasSwitch(switches::kBackgroundTracingOutputFile)) {
+ return BackgroundTracingSetupMode::kFromFieldTrial;
+ }
+
+ if (command_line->GetSwitchValuePath(switches::kBackgroundTracingOutputFile)
+ .empty()) {
+ LOG(ERROR)
+ << "--background-tracing-output-file needs an output file path";
+ return BackgroundTracingSetupMode::kDisabledInvalidCommandLine;
+ }
+
+ return BackgroundTracingSetupMode::kFromFieldTrialLocalOutput;
+ }
+
+ if (command_line->GetSwitchValueNative(switches::kEnableBackgroundTracing)
+ .empty()) {
+ LOG(ERROR) << "--enable-background-tracing needs a config file path";
+ return BackgroundTracingSetupMode::kDisabledInvalidCommandLine;
+ }
+
+ const auto output_file = command_line->GetSwitchValueNative(
+ switches::kBackgroundTracingOutputFile);
+ if (output_file.empty()) {
+ LOG(ERROR) << "--background-tracing-output-file needs an output file path";
+ return BackgroundTracingSetupMode::kDisabledInvalidCommandLine;
+ }
+
+ return BackgroundTracingSetupMode::kFromConfigFile;
+}
+
+} // namespace tracing
diff --git a/chromium/components/tracing/common/background_tracing_utils.h b/chromium/components/tracing/common/background_tracing_utils.h
new file mode 100644
index 00000000000..07e0991fa29
--- /dev/null
+++ b/chromium/components/tracing/common/background_tracing_utils.h
@@ -0,0 +1,67 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_TRACING_COMMON_BACKGROUND_TRACING_UTILS_H_
+#define COMPONENTS_TRACING_COMMON_BACKGROUND_TRACING_UTILS_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/files/file_path.h"
+#include "content/public/browser/background_tracing_config.h"
+
+namespace tracing {
+
+// These values are logged to UMA. Entries should not be renumbered and numeric
+// values should never be reused. Please keep in sync with
+// "TracingFinalizationDisallowedReason" in
+// src/tools/metrics/histograms/enums.xml.
+enum class TracingFinalizationDisallowedReason {
+ kIncognitoLaunched = 0,
+ kProfileNotLoaded = 1,
+ kCrashMetricsNotLoaded = 2,
+ kLastSessionCrashed = 3,
+ kMetricsReportingDisabled = 4,
+ kTraceUploadedRecently = 5,
+ kLastTracingSessionDidNotEnd = 6,
+ kMaxValue = kLastTracingSessionDidNotEnd
+};
+
+COMPONENT_EXPORT(BACKGROUND_TRACING_UTILS)
+void RecordDisallowedMetric(TracingFinalizationDisallowedReason reason);
+
+enum class BackgroundTracingSetupMode {
+ // Background tracing config comes from a field trial.
+ kFromFieldTrial,
+
+ // Background tracing config comes from a field trial but the trace is written
+ // into a local file (for local testing).
+ kFromFieldTrialLocalOutput,
+
+ // Background tracing config comes from a config file passed on the
+ // command-line (for local testing).
+ kFromConfigFile,
+
+ // Background tracing is disabled due to invalid command-line flags.
+ kDisabledInvalidCommandLine,
+};
+
+COMPONENT_EXPORT(BACKGROUND_TRACING_UTILS)
+void SetupBackgroundTracingWithOutputFile(
+ std::unique_ptr<content::BackgroundTracingConfig> config,
+ const base::FilePath& output_file);
+
+COMPONENT_EXPORT(BACKGROUND_TRACING_UTILS)
+void SetupBackgroundTracingFromConfigFile(const base::FilePath& config_file,
+ const base::FilePath& output_file);
+
+COMPONENT_EXPORT(BACKGROUND_TRACING_UTILS)
+bool SetupBackgroundTracingFromCommandLine(const std::string& field_trial_name);
+
+COMPONENT_EXPORT(BACKGROUND_TRACING_UTILS)
+BackgroundTracingSetupMode GetBackgroundTracingSetupMode();
+
+} // namespace tracing
+
+#endif // COMPONENTS_TRACING_COMMON_BACKGROUND_TRACING_UTILS_H_
diff --git a/chromium/components/tracing/common/background_tracing_utils_unittest.cc b/chromium/components/tracing/common/background_tracing_utils_unittest.cc
new file mode 100644
index 00000000000..86b7b28735c
--- /dev/null
+++ b/chromium/components/tracing/common/background_tracing_utils_unittest.cc
@@ -0,0 +1,230 @@
+// Copyright 2022 The Chromium 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 <vector>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/scoped_command_line.h"
+#include "components/tracing/common/background_tracing_utils.h"
+#include "components/tracing/common/tracing_switches.h"
+#include "content/public/browser/background_tracing_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using tracing::BackgroundTracingSetupMode;
+
+namespace {
+
+const char kInvalidTracingConfig[] = "{][}";
+
+struct SetupModeParams {
+ const char* enable_background_tracing = nullptr;
+ const char* trace_output_file = nullptr;
+ BackgroundTracingSetupMode expected_mode;
+};
+
+TEST(BackgroundTracingUtilsTest, GetBackgroundTracingSetupMode) {
+ const std::vector<SetupModeParams> kParams = {
+ // No config file param.
+ {nullptr, nullptr, BackgroundTracingSetupMode::kFromFieldTrial},
+ // Empty config filename.
+ {"", "output_file.gz",
+ BackgroundTracingSetupMode::kDisabledInvalidCommandLine},
+ // No output location switch.
+ {"config.json", nullptr,
+ BackgroundTracingSetupMode::kDisabledInvalidCommandLine},
+ // Empty output location switch.
+ {"config.json", "",
+ BackgroundTracingSetupMode::kDisabledInvalidCommandLine},
+ // file is valid for proto traces.
+ {"config.json", "output_file.gz",
+ BackgroundTracingSetupMode::kFromConfigFile},
+ // Field trial with output location switch.
+ {nullptr, "output_file.gz",
+ BackgroundTracingSetupMode::kFromFieldTrialLocalOutput},
+ // Field trial, empty output location switch.
+ {nullptr, "", BackgroundTracingSetupMode::kDisabledInvalidCommandLine},
+ };
+
+ for (const SetupModeParams& params : kParams) {
+ SCOPED_TRACE(::testing::Message()
+ << "enable_background_tracing "
+ << params.enable_background_tracing << " trace_output_file "
+ << params.trace_output_file);
+ base::test::ScopedCommandLine scoped_command_line;
+ base::CommandLine* command_line =
+ scoped_command_line.GetProcessCommandLine();
+ if (params.enable_background_tracing) {
+ command_line->AppendSwitchASCII(switches::kEnableBackgroundTracing,
+ params.enable_background_tracing);
+ }
+ if (params.trace_output_file) {
+ command_line->AppendSwitchASCII(switches::kBackgroundTracingOutputFile,
+ params.trace_output_file);
+ }
+
+ EXPECT_EQ(tracing::GetBackgroundTracingSetupMode(), params.expected_mode);
+ }
+}
+
+TEST(BackgroundTracingUtilTest, SetupBackgroundTracingFromConfigFileFailed) {
+ ASSERT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+
+ base::test::ScopedCommandLine scoped_command_line;
+ base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
+ command_line->AppendSwitchASCII(switches::kBackgroundTracingOutputFile, "");
+ command_line->AppendSwitchASCII(switches::kEnableBackgroundTracing, "");
+
+ ASSERT_EQ(tracing::GetBackgroundTracingSetupMode(),
+ BackgroundTracingSetupMode::kDisabledInvalidCommandLine);
+ tracing::SetupBackgroundTracingFromConfigFile(base::FilePath(),
+ base::FilePath());
+ EXPECT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+}
+
+TEST(BackgroundTracingUtilTest,
+ SetupBackgroundTracingFromConfigFileEmptyOutputFailed) {
+ ASSERT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ base::FilePath config_file_path =
+ temp_dir.GetPath().AppendASCII("config.json");
+ base::WriteFile(config_file_path, kInvalidTracingConfig,
+ sizeof(kInvalidTracingConfig) - 1);
+
+ base::test::ScopedCommandLine scoped_command_line;
+ base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
+ command_line->AppendSwitchPath(switches::kEnableBackgroundTracing,
+ config_file_path);
+ command_line->AppendSwitchASCII(switches::kBackgroundTracingOutputFile, "");
+
+ ASSERT_EQ(tracing::GetBackgroundTracingSetupMode(),
+ BackgroundTracingSetupMode::kDisabledInvalidCommandLine);
+ tracing::SetupBackgroundTracingFromConfigFile(config_file_path,
+ base::FilePath());
+ EXPECT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+}
+
+TEST(BackgroundTracingUtilTest,
+ SetupBackgroundTracingFromConfigFileMissingOutputFailed) {
+ ASSERT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ base::FilePath config_file_path =
+ temp_dir.GetPath().AppendASCII("config.json");
+ base::WriteFile(config_file_path, kInvalidTracingConfig,
+ sizeof(kInvalidTracingConfig) - 1);
+
+ base::test::ScopedCommandLine scoped_command_line;
+ base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
+ command_line->AppendSwitchPath(switches::kEnableBackgroundTracing,
+ config_file_path);
+
+ ASSERT_EQ(tracing::GetBackgroundTracingSetupMode(),
+ BackgroundTracingSetupMode::kDisabledInvalidCommandLine);
+ tracing::SetupBackgroundTracingFromConfigFile(config_file_path,
+ base::FilePath());
+ EXPECT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+}
+
+TEST(BackgroundTracingUtilTest,
+ SetupBackgroundTracingFromConfigFileInvalidConfig) {
+ ASSERT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ base::FilePath config_file_path =
+ temp_dir.GetPath().AppendASCII("config.json");
+ base::WriteFile(config_file_path, kInvalidTracingConfig,
+ sizeof(kInvalidTracingConfig) - 1);
+ auto output_file_path =
+ temp_dir.GetPath().AppendASCII("test_trace.perfetto.gz");
+
+ base::test::ScopedCommandLine scoped_command_line;
+ base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
+ command_line->AppendSwitchPath(switches::kBackgroundTracingOutputFile,
+ output_file_path);
+ command_line->AppendSwitchPath(switches::kEnableBackgroundTracing,
+ config_file_path);
+
+ ASSERT_EQ(tracing::GetBackgroundTracingSetupMode(),
+ BackgroundTracingSetupMode::kFromConfigFile);
+
+ tracing::SetupBackgroundTracingFromConfigFile(config_file_path,
+ output_file_path);
+ EXPECT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+}
+
+TEST(BackgroundTracingUtilTest, SetupBackgroundTracingWithOutputFileFailed) {
+ ASSERT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+
+ base::test::ScopedCommandLine scoped_command_line;
+ base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
+ command_line->AppendSwitchASCII(switches::kBackgroundTracingOutputFile, "");
+
+ ASSERT_EQ(tracing::GetBackgroundTracingSetupMode(),
+ BackgroundTracingSetupMode::kDisabledInvalidCommandLine);
+ tracing::SetupBackgroundTracingWithOutputFile(nullptr, base::FilePath());
+ EXPECT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+}
+
+TEST(BackgroundTracingUtilTest, SetupBackgroundTracingFromCommandLineInvalid) {
+ ASSERT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+ base::test::ScopedCommandLine scoped_command_line;
+ base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
+ command_line->AppendSwitchASCII(switches::kBackgroundTracingOutputFile, "");
+
+ ASSERT_EQ(tracing::GetBackgroundTracingSetupMode(),
+ BackgroundTracingSetupMode::kDisabledInvalidCommandLine);
+ EXPECT_FALSE(tracing::SetupBackgroundTracingFromCommandLine(""));
+ EXPECT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+}
+
+TEST(BackgroundTracingUtilTest, SetupBackgroundTracingFromCommandLineConfig) {
+ ASSERT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+ base::test::ScopedCommandLine scoped_command_line;
+ base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
+ command_line->AppendSwitchASCII(switches::kEnableBackgroundTracing,
+ "config.json");
+ command_line->AppendSwitchASCII(switches::kBackgroundTracingOutputFile,
+ "test_trace.perfetto.gz");
+
+ ASSERT_EQ(tracing::GetBackgroundTracingSetupMode(),
+ BackgroundTracingSetupMode::kFromConfigFile);
+ EXPECT_TRUE(tracing::SetupBackgroundTracingFromCommandLine(""));
+ EXPECT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+}
+
+TEST(BackgroundTracingUtilTest,
+ SetupBackgroundTracingFromCommandLineFieldTrial) {
+ ASSERT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+
+ ASSERT_EQ(tracing::GetBackgroundTracingSetupMode(),
+ BackgroundTracingSetupMode::kFromFieldTrial);
+ EXPECT_FALSE(tracing::SetupBackgroundTracingFromCommandLine(""));
+ EXPECT_FALSE(
+ content::BackgroundTracingManager::GetInstance().HasActiveScenario());
+}
+
+} // namespace
diff --git a/chromium/components/tracing/common/pref_names.cc b/chromium/components/tracing/common/pref_names.cc
new file mode 100644
index 00000000000..6d8ba35aae8
--- /dev/null
+++ b/chromium/components/tracing/common/pref_names.cc
@@ -0,0 +1,17 @@
+// Copyright 2022 The Chromium 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/tracing/common/pref_names.h"
+
+#include "components/prefs/pref_registry_simple.h"
+
+namespace tracing {
+const char kBackgroundTracingSessionState[] =
+ "background_tracing.session_state";
+
+void RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterDictionaryPref(kBackgroundTracingSessionState);
+}
+
+} // namespace tracing
diff --git a/chromium/components/tracing/common/pref_names.h b/chromium/components/tracing/common/pref_names.h
new file mode 100644
index 00000000000..eb50e261c23
--- /dev/null
+++ b/chromium/components/tracing/common/pref_names.h
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_TRACING_COMMON_PREF_NAMES_H_
+#define COMPONENTS_TRACING_COMMON_PREF_NAMES_H_
+
+#include "base/component_export.h"
+
+class PrefRegistrySimple;
+
+namespace tracing {
+
+COMPONENT_EXPORT(BACKGROUND_TRACING_UTILS)
+extern const char kBackgroundTracingSessionState[];
+
+COMPONENT_EXPORT(BACKGROUND_TRACING_UTILS)
+void RegisterPrefs(PrefRegistrySimple* registry);
+
+} // namespace tracing
+
+#endif // COMPONENTS_TRACING_COMMON_PREF_NAMES_H_
diff --git a/chromium/components/translate/content/android/BUILD.gn b/chromium/components/translate/content/android/BUILD.gn
index fd8fecd3e47..2f4055b5dd8 100644
--- a/chromium/components/translate/content/android/BUILD.gn
+++ b/chromium/components/translate/content/android/BUILD.gn
@@ -41,6 +41,8 @@ android_library("java") {
deps = [
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/widget/android:java",
"//components/messages/android:java",
"//content/public/android:content_java",
@@ -99,11 +101,9 @@ android_library("javatests") {
[ "java/src/org/chromium/components/translate/TranslateOptionsTest.java" ]
deps = [
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
- "//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit:junit",
@@ -135,7 +135,6 @@ source_set("unit_tests") {
"//components/translate/core/common",
"//testing/gmock",
"//testing/gtest",
- "//ui/base",
"//url",
]
}
diff --git a/chromium/components/translate/core/browser/BUILD.gn b/chromium/components/translate/core/browser/BUILD.gn
index f57e625ae42..b6a72be69b1 100644
--- a/chromium/components/translate/core/browser/BUILD.gn
+++ b/chromium/components/translate/core/browser/BUILD.gn
@@ -9,8 +9,6 @@ static_library("browser") {
"language_state.cc",
"language_state.h",
"page_translated_details.h",
- "translate_accept_languages.cc",
- "translate_accept_languages.h",
"translate_browser_metrics.cc",
"translate_browser_metrics.h",
"translate_client.h",
@@ -113,7 +111,6 @@ source_set("unit_tests") {
testonly = true
sources = [
"language_state_unittest.cc",
- "translate_accept_languages_unittest.cc",
"translate_browser_metrics_unittest.cc",
"translate_language_list_unittest.cc",
"translate_manager_unittest.cc",
diff --git a/chromium/components/translate/core/language_detection/BUILD.gn b/chromium/components/translate/core/language_detection/BUILD.gn
index 2669f1768af..44cb037c6d8 100644
--- a/chromium/components/translate/core/language_detection/BUILD.gn
+++ b/chromium/components/translate/core/language_detection/BUILD.gn
@@ -38,6 +38,7 @@ static_library("language_detection") {
":chinese_script_classifier",
"//base",
"//components/language/core/common",
+ "//components/optimization_guide/core:features",
"//components/translate/core/common",
"//third_party/cld_3/src/src:cld_3",
"//third_party/flatbuffers",
diff --git a/chromium/components/translate/ios/browser/BUILD.gn b/chromium/components/translate/ios/browser/BUILD.gn
index 17a5101ea21..4092e811a91 100644
--- a/chromium/components/translate/ios/browser/BUILD.gn
+++ b/chromium/components/translate/ios/browser/BUILD.gn
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//ios/web/js_compile.gni")
+import("//ios/web/public/js_messaging/optimize_js.gni")
source_set("browser") {
configs += [ "//build/config/compiler:enable_arc" ]
@@ -13,6 +13,8 @@ source_set("browser") {
"js_translate_manager.mm",
"language_detection_controller.h",
"language_detection_controller.mm",
+ "language_detection_model_service.h",
+ "language_detection_model_service.mm",
"string_clipping_util.cc",
"string_clipping_util.h",
"translate_controller.h",
@@ -22,9 +24,11 @@ source_set("browser") {
deps = [
":injected_js",
"//base",
+ "//components/keyed_service/core",
"//components/language/ios/browser",
"//components/prefs",
"//components/translate/core/browser",
+ "//components/translate/core/browser:translate_model_service",
"//components/translate/core/common",
"//components/translate/core/language_detection",
"//components/ukm/ios:ukm_url_recorder",
@@ -39,15 +43,10 @@ source_set("browser") {
]
}
-js_compile_checked("injected_js") {
+optimize_js("injected_js") {
visibility = [ ":browser" ]
+ primary_script = "resources/translate_ios.js"
sources = [ "resources/translate_ios.js" ]
-
- js_modules = [
- "//ios/web/web_state/js/resources/base.js",
- "//ios/web/web_state/js/resources/common.js",
- "//ios/web/js_messaging/resources/message.js",
- ]
}
source_set("unit_tests") {
diff --git a/chromium/components/translate_strings.grdp b/chromium/components/translate_strings.grdp
index 7e657193c51..178462cafe0 100644
--- a/chromium/components/translate_strings.grdp
+++ b/chromium/components/translate_strings.grdp
@@ -52,12 +52,6 @@
<message name="IDS_TRANSLATE_INFOBAR_RETRY" desc="Text to show for translate infobar to retry translation of page">
Try again
</message>
- <message name="IDS_TRANSLATE_MESSAGE_ALWAYS_TRANSLATE_LANGUAGE" desc="Option in the overflow menu. User can click the 'Always Translate' option to indicate that they want the app to translate pages in this language automatically. Imperative.">
- Always translate pages in <ph name="SOURCE_LANGUAGE">$1<ex>French</ex></ph>
- </message>
- <message name="IDS_TRANSLATE_MESSAGE_NEVER_TRANSLATE_LANGUAGE" desc="Option in the overflow menu. User can click the 'Never Translate' option to indicate that they never want the app to translate pages in this language. Imperative.">
- Never translate pages in <ph name="SOURCE_LANGUAGE">$1<ex>French</ex></ph>
- </message>
<if expr="is_android">
<message name="IDS_TRANSLATE_INFOBAR_ERROR" formatter_data="android_java">
@@ -101,14 +95,23 @@
Cancel
</message>
- <message name="IDS_TRANSLATE_MESSAGE_BEFORE_TRANSLATE_TITLE" desc="A text label on the translate message popup that prompts the user to translate the page." formatter_data="android_java">
+ <message name="IDS_TRANSLATE_MESSAGE_BEFORE_TRANSLATE_TITLE" desc="A text label on the translate message popup that prompts the user to translate the page.">
Translate page?
</message>
- <message name="IDS_TRANSLATE_MESSAGE_AFTER_TRANSLATE_TITLE" desc="A text label on the translate message popup that announces to the user that Chrome has finished translating the page." formatter_data="android_java">
+ <message name="IDS_TRANSLATE_MESSAGE_AFTER_TRANSLATE_TITLE" desc="A text label on the translate message popup that announces to the user that Chrome has finished translating the page.">
Page translated
</message>
- <message name="IDS_TRANSLATE_MESSAGE_DESCRIPTION" desc="A text label on the translate message popup that lets the user know what the source and target languages are for translating the page." formatter_data="android_java">
- <ph name="source_language">%1$s<ex>French</ex></ph> to <ph name="target_language">%2$s<ex>English</ex></ph>
+ <message name="IDS_TRANSLATE_MESSAGE_DESCRIPTION" desc="A text label on the translate message popup that lets the user know what the source and target languages are for translating the page.">
+ <ph name="source_language">$1<ex>French</ex></ph> to <ph name="target_language">$2<ex>English</ex></ph>
+ </message>
+ <message name="IDS_TRANSLATE_MESSAGE_UNDO_BUTTON" desc="A text label for the button on the translate message popup that, when clicked, will undo the translation.">
+ Undo
+ </message>
+ <message name="IDS_TRANSLATE_MESSAGE_ALWAYS_TRANSLATE_LANGUAGE" desc="Option in the overflow menu. User can click the 'Always Translate' option to indicate that they want the app to translate pages in this language automatically. Imperative.">
+ Always translate pages in <ph name="SOURCE_LANGUAGE">$1<ex>French</ex></ph>
+ </message>
+ <message name="IDS_TRANSLATE_MESSAGE_NEVER_TRANSLATE_LANGUAGE" desc="Option in the overflow menu. User can click the 'Never Translate' option to indicate that they never want the app to translate pages in this language. Imperative.">
+ Never translate pages in <ph name="SOURCE_LANGUAGE">$1<ex>French</ex></ph>
</message>
</if>
diff --git a/chromium/components/translate_strings_grdp/IDS_TRANSLATE_MESSAGE_DESCRIPTION.png.sha1 b/chromium/components/translate_strings_grdp/IDS_TRANSLATE_MESSAGE_DESCRIPTION.png.sha1
index 546fe22e3c5..21f4b148725 100644
--- a/chromium/components/translate_strings_grdp/IDS_TRANSLATE_MESSAGE_DESCRIPTION.png.sha1
+++ b/chromium/components/translate_strings_grdp/IDS_TRANSLATE_MESSAGE_DESCRIPTION.png.sha1
@@ -1 +1 @@
-41578749df4f622b19e8cae217bf4deaba12fb92 \ No newline at end of file
+f4377aaf9982922fa45d7a048e941d37ff7b4343 \ No newline at end of file
diff --git a/chromium/components/translate_strings_grdp/IDS_TRANSLATE_MESSAGE_UNDO_BUTTON.png.sha1 b/chromium/components/translate_strings_grdp/IDS_TRANSLATE_MESSAGE_UNDO_BUTTON.png.sha1
new file mode 100644
index 00000000000..5777c639533
--- /dev/null
+++ b/chromium/components/translate_strings_grdp/IDS_TRANSLATE_MESSAGE_UNDO_BUTTON.png.sha1
@@ -0,0 +1 @@
+57f3aca5e803860e3c58dd1ee307d5fdbd46b770 \ No newline at end of file
diff --git a/chromium/components/ui_devtools/views/overlay_agent_unittest.cc b/chromium/components/ui_devtools/views/overlay_agent_unittest.cc
index dbefbdb6456..09f6c3c3789 100644
--- a/chromium/components/ui_devtools/views/overlay_agent_unittest.cc
+++ b/chromium/components/ui_devtools/views/overlay_agent_unittest.cc
@@ -344,7 +344,7 @@ TEST_F(OverlayAgentTest, MouseEventsGenerateFEEventsInInspectMode) {
// Press escape to exit inspect mode. We're intentionally not supporting
// this on Mac due do difficulties in receiving key events without aura::Env.
#if defined(USE_AURA)
- generator.PressKey(ui::KeyboardCode::VKEY_ESCAPE, ui::EventFlags::EF_NONE);
+ generator.PressKey(ui::KeyboardCode::VKEY_ESCAPE, ui::EF_NONE);
// Upon exiting inspect mode, the element is inspected and highlighted.
EXPECT_EQ(inspect_node_notification_count + 1,
GetOverlayInspectNodeRequestedCount(node_id));
diff --git a/chromium/components/ui_devtools/views/view_element_unittest.cc b/chromium/components/ui_devtools/views/view_element_unittest.cc
index 951c7e59729..c5a20323b66 100644
--- a/chromium/components/ui_devtools/views/view_element_unittest.cc
+++ b/chromium/components/ui_devtools/views/view_element_unittest.cc
@@ -329,9 +329,15 @@ TEST_F(ViewElementTest, GetSources) {
// ViewElement should have two sources: from MockNamedTestView and from View.
EXPECT_EQ(sources.size(), 2U);
+#if defined(__clang__) && defined(_MSC_VER)
+ EXPECT_EQ(sources[0].path_,
+ "components\\ui_devtools\\views\\view_element_unittest.cc");
+ EXPECT_EQ(sources[1].path_, "ui\\views\\view.h");
+#else
EXPECT_EQ(sources[0].path_,
"components/ui_devtools/views/view_element_unittest.cc");
EXPECT_EQ(sources[1].path_, "ui/views/view.h");
+#endif
}
TEST_F(ViewElementTest, DispatchMouseEvent) {
diff --git a/chromium/components/ukm/android/BUILD.gn b/chromium/components/ukm/android/BUILD.gn
index 7a98d7a82ef..a30b74151a8 100644
--- a/chromium/components/ukm/android/BUILD.gn
+++ b/chromium/components/ukm/android/BUILD.gn
@@ -8,7 +8,8 @@ android_library("java") {
sources = [ "java/src/org/chromium/components/ukm/UkmRecorder.java" ]
deps = [
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
diff --git a/chromium/components/ukm/android/ukm_recorder.cc b/chromium/components/ukm/android/ukm_recorder.cc
index d89832176ab..d630e85dc26 100644
--- a/chromium/components/ukm/android/ukm_recorder.cc
+++ b/chromium/components/ukm/android/ukm_recorder.cc
@@ -6,7 +6,6 @@
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "components/ukm/android/jni_headers/UkmRecorder_jni.h"
-#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/web_contents.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_entry_builder.h"
@@ -22,7 +21,7 @@ static void JNI_UkmRecorder_RecordEventWithBooleanMetric(
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(j_web_contents);
const ukm::SourceId source_id =
- ukm::GetSourceIdForWebContentsDocument(web_contents);
+ web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId();
const std::string event_name(ConvertJavaStringToUTF8(env, j_event_name));
ukm::UkmEntryBuilder builder(source_id, event_name);
builder.SetMetric(ConvertJavaStringToUTF8(env, j_metric_name), true);
@@ -39,7 +38,7 @@ static void JNI_UkmRecorder_RecordEventWithIntegerMetric(
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(j_web_contents);
const ukm::SourceId source_id =
- ukm::GetSourceIdForWebContentsDocument(web_contents);
+ web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId();
const std::string event_name(ConvertJavaStringToUTF8(env, j_event_name));
ukm::UkmEntryBuilder builder(source_id, event_name);
builder.SetMetric(ConvertJavaStringToUTF8(env, j_metric_name),
diff --git a/chromium/components/ukm/content/source_url_recorder.cc b/chromium/components/ukm/content/source_url_recorder.cc
index 284b34ffe03..e047c865992 100644
--- a/chromium/components/ukm/content/source_url_recorder.cc
+++ b/chromium/components/ukm/content/source_url_recorder.cc
@@ -343,12 +343,4 @@ void InitializeSourceUrlRecorderForWebContents(
web_contents);
}
-SourceId GetSourceIdForWebContentsDocument(
- const content::WebContents* web_contents) {
- const internal::SourceUrlRecorderWebContentsObserver* obs =
- internal::SourceUrlRecorderWebContentsObserver::FromWebContents(
- web_contents);
- return obs ? obs->GetLastCommittedSourceId() : kInvalidSourceId;
-}
-
} // namespace ukm
diff --git a/chromium/components/ukm/content/source_url_recorder.h b/chromium/components/ukm/content/source_url_recorder.h
index 64f704d22b7..372abeb6136 100644
--- a/chromium/components/ukm/content/source_url_recorder.h
+++ b/chromium/components/ukm/content/source_url_recorder.h
@@ -17,11 +17,6 @@ namespace ukm {
void InitializeSourceUrlRecorderForWebContents(
content::WebContents* web_contents);
-// Get a UKM SourceId for the currently committed document of web contents.
-// Returns kInvalidSourceId if no commit has been observed.
-SourceId GetSourceIdForWebContentsDocument(
- const content::WebContents* web_contents);
-
} // namespace ukm
#endif // COMPONENTS_UKM_CONTENT_SOURCE_URL_RECORDER_H_
diff --git a/chromium/components/ukm/content/source_url_recorder_browsertest.cc b/chromium/components/ukm/content/source_url_recorder_browsertest.cc
index 2e29645749b..2e20fa2b802 100644
--- a/chromium/components/ukm/content/source_url_recorder_browsertest.cc
+++ b/chromium/components/ukm/content/source_url_recorder_browsertest.cc
@@ -59,7 +59,7 @@ class SourceUrlRecorderWebContentsObserverBrowserTest
GURL GetAssociatedURLForWebContentsDocument() {
const ukm::UkmSource* src = test_ukm_recorder_->GetSourceForSourceId(
- ukm::GetSourceIdForWebContentsDocument(shell()->web_contents()));
+ shell()->web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
return src ? src->url() : GURL();
}
@@ -326,6 +326,10 @@ IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverPrerenderBrowserTest,
GetSourceForNavigationId(prerender_observer.navigation_id());
EXPECT_EQ(1u, source2->urls().size());
EXPECT_EQ(prerender_url, source2->url());
- EXPECT_EQ(prerender_url, GetAssociatedURLForWebContentsDocument());
+ GURL expected_ukm_url;
+ // TODO(crbug.com/1245014): The URL is not assigned yet for prerendering
+ // UKM source ids, so expect it to not be set.
+ // expected_ukm_url = prerender_url;
+ EXPECT_EQ(expected_ukm_url, GetAssociatedURLForWebContentsDocument());
EXPECT_NE(source1, source2);
}
diff --git a/chromium/components/ukm/content/source_url_recorder_test.cc b/chromium/components/ukm/content/source_url_recorder_test.cc
index 97ffd712528..066d33bc182 100644
--- a/chromium/components/ukm/content/source_url_recorder_test.cc
+++ b/chromium/components/ukm/content/source_url_recorder_test.cc
@@ -27,7 +27,7 @@ class SourceUrlRecorderWebContentsObserverTest
GURL GetAssociatedURLForWebContentsDocument() {
const ukm::UkmSource* src = test_ukm_recorder_.GetSourceForSourceId(
- ukm::GetSourceIdForWebContentsDocument(web_contents()));
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
return src ? src->url() : GURL();
}
diff --git a/chromium/components/ukm/debug/ukm_internals.js b/chromium/components/ukm/debug/ukm_internals.js
index 54f88ef9868..991fd1d75e0 100644
--- a/chromium/components/ukm/debug/ukm_internals.js
+++ b/chromium/components/ukm/debug/ukm_internals.js
@@ -302,7 +302,7 @@ function populateThreadIds(sources) {
for (const id of options) {
if (!currentOptions.has(id)) {
- const option = document.createElement("option");
+ const option = document.createElement('option');
option.textContent = id;
option.setAttribute('value', id);
threadIdSelect.add(option);
diff --git a/chromium/components/ukm/observers/ukm_consent_state_observer_unittest.cc b/chromium/components/ukm/observers/ukm_consent_state_observer_unittest.cc
index 1e3157f42e6..d65b26921e2 100644
--- a/chromium/components/ukm/observers/ukm_consent_state_observer_unittest.cc
+++ b/chromium/components/ukm/observers/ukm_consent_state_observer_unittest.cc
@@ -42,10 +42,8 @@ class MockSyncService : public syncer::TestSyncService {
// SyncCycleSnapshot is initialized at all.
SetLastCycleSnapshot(syncer::SyncCycleSnapshot(
/*birthday=*/std::string(), /*bag_of_chips=*/std::string(),
- syncer::ModelNeutralState(), syncer::ProgressMarkerMap(), false, 0, 0,
- 0, true, 0, base::Time::Now(), base::Time::Now(),
- std::vector<int>(syncer::GetNumModelTypes(), 0),
- std::vector<int>(syncer::GetNumModelTypes(), 0),
+ syncer::ModelNeutralState(), syncer::ProgressMarkerMap(), false, 0,
+ true, base::Time::Now(), base::Time::Now(),
sync_pb::SyncEnums::UNKNOWN_ORIGIN, base::Minutes(1), false));
NotifyObserversOfStateChanged();
diff --git a/chromium/components/ukm/test_ukm_recorder.cc b/chromium/components/ukm/test_ukm_recorder.cc
index 344227840ea..83c67c74291 100644
--- a/chromium/components/ukm/test_ukm_recorder.cc
+++ b/chromium/components/ukm/test_ukm_recorder.cc
@@ -54,7 +54,7 @@ void TestUkmRecorder::AddEntry(mojom::UkmEntryPtr entry) {
on_add_entry_ && entry && entry_hash_to_wait_for_ == entry->event_hash;
UkmRecorderImpl::AddEntry(std::move(entry));
if (should_run_callback)
- std::move(on_add_entry_).Run();
+ on_add_entry_.Run();
}
const UkmSource* TestUkmRecorder::GetSourceForSourceId(
@@ -79,8 +79,9 @@ const ukm::mojom::UkmEntry* TestUkmRecorder::GetDocumentCreatedEntryForSourceId(
return nullptr;
}
-void TestUkmRecorder::SetOnAddEntryCallback(base::StringPiece entry_name,
- base::OnceClosure on_add_entry) {
+void TestUkmRecorder::SetOnAddEntryCallback(
+ base::StringPiece entry_name,
+ base::RepeatingClosure on_add_entry) {
on_add_entry_ = std::move(on_add_entry);
entry_hash_to_wait_for_ = base::HashMetricName(entry_name);
}
diff --git a/chromium/components/ukm/test_ukm_recorder.h b/chromium/components/ukm/test_ukm_recorder.h
index 39930e524a1..8f06175db77 100644
--- a/chromium/components/ukm/test_ukm_recorder.h
+++ b/chromium/components/ukm/test_ukm_recorder.h
@@ -73,7 +73,7 @@ class TestUkmRecorder : public UkmRecorderImpl {
// Sets a callback that will be called when recording an entry for entry name.
void SetOnAddEntryCallback(base::StringPiece entry_name,
- base::OnceClosure on_add_entry);
+ base::RepeatingClosure on_add_entry);
// Gets all of the entries recorded for entry name.
std::vector<const mojom::UkmEntry*> GetEntriesByName(
@@ -123,7 +123,7 @@ class TestUkmRecorder : public UkmRecorderImpl {
private:
uint64_t entry_hash_to_wait_for_ = 0;
- base::OnceClosure on_add_entry_;
+ base::RepeatingClosure on_add_entry_;
};
// Similar to a TestUkmRecorder, but also sets itself as the global UkmRecorder
diff --git a/chromium/components/ukm/ukm_recorder_impl.cc b/chromium/components/ukm/ukm_recorder_impl.cc
index b27ac5d9e97..76b6e59b2ba 100644
--- a/chromium/components/ukm/ukm_recorder_impl.cc
+++ b/chromium/components/ukm/ukm_recorder_impl.cc
@@ -105,6 +105,24 @@ void RecordDroppedSource(bool already_recorded_another_reason,
void RecordDroppedEntry(uint64_t event_hash, DroppedDataReason reason) {
LogEventHashAsUmaHistogram("UKM.Entries.Dropped.ByEntryHash", event_hash);
+ // Because the "UKM.Entries.Dropped.ByEntryHash" histogram will be emitted to
+ // every single time an entry is dropped, it will be dominated by the
+ // RECORDING_DISABLED reason (which is not very insightful). More interesting
+ // dropped reasons are MAX_HIT and SAMPLED_OUT, so we also emit histograms
+ // split by those reasons.
+ switch (reason) {
+ case DroppedDataReason::MAX_HIT:
+ LogEventHashAsUmaHistogram("UKM.Entries.Dropped.MaxHit.ByEntryHash",
+ event_hash);
+ break;
+ case DroppedDataReason::SAMPLED_OUT:
+ LogEventHashAsUmaHistogram("UKM.Entries.Dropped.SampledOut.ByEntryHash",
+ event_hash);
+ break;
+ default:
+ break;
+ }
+
UMA_HISTOGRAM_ENUMERATION(
"UKM.Entries.Dropped", static_cast<int>(reason),
static_cast<int>(DroppedDataReason::NUM_DROPPED_DATA_REASONS));
diff --git a/chromium/components/undo/undo_manager.cc b/chromium/components/undo/undo_manager.cc
index d40960e4da3..86717b3a96e 100644
--- a/chromium/components/undo/undo_manager.cc
+++ b/chromium/components/undo/undo_manager.cc
@@ -94,9 +94,9 @@ void UndoManager::AddUndoOperation(std::unique_ptr<UndoOperation> operation) {
if (group_actions_count_) {
pending_grouped_action_->AddOperation(std::move(operation));
} else {
- UndoGroup* new_action = new UndoGroup();
+ auto new_action = std::make_unique<UndoGroup>();
new_action->AddOperation(std::move(operation));
- AddUndoGroup(new_action);
+ AddUndoGroup(std::move(new_action));
}
}
@@ -116,7 +116,7 @@ void UndoManager::EndGroupingActions() {
bool is_user_action = !performing_undo_ && !performing_redo_;
if (!pending_grouped_action_->undo_operations().empty()) {
- AddUndoGroup(pending_grouped_action_.release());
+ AddUndoGroup(std::move(pending_grouped_action_));
} else {
// No changes were executed since we started grouping actions, so the
// pending UndoGroup should be discarded.
@@ -185,8 +185,8 @@ void UndoManager::NotifyOnUndoManagerStateChange() {
observer.OnUndoManagerStateChange();
}
-void UndoManager::AddUndoGroup(UndoGroup* new_undo_group) {
- GetActiveUndoGroup()->push_back(base::WrapUnique<UndoGroup>(new_undo_group));
+void UndoManager::AddUndoGroup(std::unique_ptr<UndoGroup> new_undo_group) {
+ GetActiveUndoGroup()->push_back(std::move(new_undo_group));
// User actions invalidate any available redo actions.
if (is_user_action())
diff --git a/chromium/components/undo/undo_manager.h b/chromium/components/undo/undo_manager.h
index 5a11ae1003e..6f510498553 100644
--- a/chromium/components/undo/undo_manager.h
+++ b/chromium/components/undo/undo_manager.h
@@ -104,7 +104,7 @@ class UndoManager {
void NotifyOnUndoManagerStateChange();
// Handle the addition of |new_undo_group| to the active undo group container.
- void AddUndoGroup(UndoGroup* new_undo_group);
+ void AddUndoGroup(std::unique_ptr<UndoGroup> new_undo_group);
// Returns the undo or redo UndoGroup container that should store the next
// change taking into account if an undo or redo is being executed.
diff --git a/chromium/components/update_client/background_downloader_win.cc b/chromium/components/update_client/background_downloader_win.cc
index c45f849c1cd..2f113918e98 100644
--- a/chromium/components/update_client/background_downloader_win.cc
+++ b/chromium/components/update_client/background_downloader_win.cc
@@ -793,9 +793,9 @@ HRESULT BackgroundDownloader::CompleteJob() {
if (FAILED(hr))
return hr;
- // Sanity check the post-conditions of a successful download, including
- // the file and job invariants. The byte counts for a job and its file
- // must match as a job only contains one file.
+ // Check the post-conditions of a successful download, including the file and
+ // job invariants. The byte counts for a job and its file must match as a job
+ // only contains one file.
DCHECK(progress.Completed);
DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred);
diff --git a/chromium/components/update_client/configurator.h b/chromium/components/update_client/configurator.h
index 657b9e35c03..cce961d0999 100644
--- a/chromium/components/update_client/configurator.h
+++ b/chromium/components/update_client/configurator.h
@@ -56,7 +56,8 @@ class Configurator : public base::RefCountedThreadSafe<Configurator> {
virtual int UpdateDelay() const = 0;
// The URLs for the update checks. The URLs are tried in order, the first one
- // that succeeds wins.
+ // that succeeds wins. Since some components cannot be updated over HTTP,
+ // HTTPS URLs should appear first.
virtual std::vector<GURL> UpdateUrl() const = 0;
// The URLs for pings. Returns an empty vector if and only if pings are
diff --git a/chromium/components/update_client/protocol_serializer.cc b/chromium/components/update_client/protocol_serializer.cc
index 5ddc8e7f86f..16bd70bb02e 100644
--- a/chromium/components/update_client/protocol_serializer.cc
+++ b/chromium/components/update_client/protocol_serializer.cc
@@ -4,6 +4,7 @@
#include "components/update_client/protocol_serializer.h"
+#include <cmath>
#include <string>
#include <utility>
#include <vector>
diff --git a/chromium/components/update_client/request_sender.cc b/chromium/components/update_client/request_sender.cc
index 8a23e72f621..f4868948c5d 100644
--- a/chromium/components/update_client/request_sender.cc
+++ b/chromium/components/update_client/request_sender.cc
@@ -25,10 +25,10 @@ namespace update_client {
namespace {
// This is an ECDSA prime256v1 named-curve key.
-constexpr int kKeyVersion = 11;
+constexpr int kKeyVersion = 12;
constexpr char kKeyPubBytesBase64[] =
- "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgH30WRJf4g6I2C1FKsBQF3qHANLw"
- "thwYsNt2PWTDQBS0ufSRE83piOPoJQcePzTkMfbghjnZerDjLJhBsDkfFg==";
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETGhzuDJHRFx7//YqqdaNTY3dZ8X9"
+ "XhUC0o7fmZtOFe9cjPML9KVHPa2M5hxhJThIbRQMElksHy4llZoWg6KTZw==";
// The content type for all protocol requests.
constexpr char kContentType[] = "application/json";
diff --git a/chromium/components/update_client/request_sender_unittest.cc b/chromium/components/update_client/request_sender_unittest.cc
index 3f6ed7492a3..0bbb2af4f55 100644
--- a/chromium/components/update_client/request_sender_unittest.cc
+++ b/chromium/components/update_client/request_sender_unittest.cc
@@ -149,7 +149,6 @@ TEST_P(RequestSenderTest, RequestSendSuccess) {
EXPECT_EQ(0, post_interceptor_->GetHitCountForURL(GURL(kUrl2)))
<< post_interceptor_->GetRequestsAsString();
- // Sanity check the request.
EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str());
// Check the response post conditions.
diff --git a/chromium/components/update_client/test_configurator.cc b/chromium/components/update_client/test_configurator.cc
index dfddc4cd73e..89ba0341722 100644
--- a/chromium/components/update_client/test_configurator.cc
+++ b/chromium/components/update_client/test_configurator.cc
@@ -75,8 +75,8 @@ int TestConfigurator::UpdateDelay() const {
}
std::vector<GURL> TestConfigurator::UpdateUrl() const {
- if (!update_check_url_.is_empty())
- return std::vector<GURL>(1, update_check_url_);
+ if (!update_check_urls_.empty())
+ return update_check_urls_;
return MakeDefaultUrls();
}
@@ -191,7 +191,11 @@ void TestConfigurator::SetDownloadPreference(
}
void TestConfigurator::SetUpdateCheckUrl(const GURL& url) {
- update_check_url_ = url;
+ update_check_urls_ = {url};
+}
+
+void TestConfigurator::SetUpdateCheckUrls(const std::vector<GURL>& urls) {
+ update_check_urls_ = urls;
}
void TestConfigurator::SetPingUrl(const GURL& url) {
diff --git a/chromium/components/update_client/test_configurator.h b/chromium/components/update_client/test_configurator.h
index d19fa702256..9d49d9087d0 100644
--- a/chromium/components/update_client/test_configurator.h
+++ b/chromium/components/update_client/test_configurator.h
@@ -109,6 +109,7 @@ class TestConfigurator : public Configurator {
void SetDownloadPreference(const std::string& download_preference);
void SetEnabledCupSigning(bool use_cup_signing);
void SetUpdateCheckUrl(const GURL& url);
+ void SetUpdateCheckUrls(const std::vector<GURL>& urls);
void SetPingUrl(const GURL& url);
void SetCrxDownloaderFactory(
scoped_refptr<CrxDownloaderFactory> crx_downloader_factory);
@@ -130,7 +131,7 @@ class TestConfigurator : public Configurator {
std::string download_preference_;
bool enabled_cup_signing_;
raw_ptr<PrefService> pref_service_; // Not owned by this class.
- GURL update_check_url_;
+ std::vector<GURL> update_check_urls_;
GURL ping_url_;
scoped_refptr<update_client::UnzipChromiumFactory> unzip_factory_;
diff --git a/chromium/components/update_client/update_checker.cc b/chromium/components/update_client/update_checker.cc
index 1647f5c9ed3..8b7c644cb73 100644
--- a/chromium/components/update_client/update_checker.cc
+++ b/chromium/components/update_client/update_checker.cc
@@ -32,24 +32,15 @@
#include "components/update_client/request_sender.h"
#include "components/update_client/task_traits.h"
#include "components/update_client/update_client.h"
+#include "components/update_client/update_engine.h"
#include "components/update_client/utils.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
namespace update_client {
namespace {
-// Returns true if at least one item requires network encryption.
-bool IsEncryptionRequired(const IdToComponentPtrMap& components) {
- for (const auto& item : components) {
- const auto& component = item.second;
- if (component->crx_component() &&
- component->crx_component()->requires_network_encryption)
- return true;
- }
- return false;
-}
-
class UpdateCheckerImpl : public UpdateChecker {
public:
UpdateCheckerImpl(scoped_refptr<Configurator> config,
@@ -62,24 +53,25 @@ class UpdateCheckerImpl : public UpdateChecker {
// Overrides for UpdateChecker.
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_checked,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override;
private:
UpdaterStateAttributes ReadUpdaterStateAttributes() const;
void CheckForUpdatesHelper(
- const std::string& session_id,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
+ const std::vector<GURL>& urls,
const base::flat_map<std::string, std::string>& additional_attributes,
const UpdaterStateAttributes& updater_state_attributes,
const std::set<std::string>& active_ids);
- void OnRequestSenderComplete(int error,
+ void OnRequestSenderComplete(scoped_refptr<UpdateContext> context,
+ absl::optional<base::OnceClosure> fallback,
+ int error,
const std::string& response,
int retry_after_sec);
- void UpdateCheckSucceeded(const ProtocolParser::Results& results,
+ void UpdateCheckSucceeded(scoped_refptr<UpdateContext> context,
+ const ProtocolParser::Results& results,
int retry_after_sec);
void UpdateCheckFailed(ErrorCategory error_category,
int error,
@@ -89,7 +81,6 @@ class UpdateCheckerImpl : public UpdateChecker {
const scoped_refptr<Configurator> config_;
raw_ptr<PersistedData> metadata_ = nullptr;
- std::vector<std::string> ids_checked_;
UpdateCheckCallback update_check_callback_;
std::unique_ptr<RequestSender> request_sender_;
};
@@ -103,19 +94,16 @@ UpdateCheckerImpl::~UpdateCheckerImpl() {
}
void UpdateCheckerImpl::CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_checked,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
- ids_checked_ = ids_checked;
update_check_callback_ = std::move(update_check_callback);
auto check_for_updates_invoker = base::BindOnce(
&UpdateCheckerImpl::CheckForUpdatesHelper, base::Unretained(this),
- session_id, std::cref(components), additional_attributes);
+ context, config_->UpdateUrl(), additional_attributes);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, kTaskTraits,
@@ -132,7 +120,7 @@ void UpdateCheckerImpl::CheckForUpdates(
updater_state_attributes));
},
std::move(check_for_updates_invoker), base::Unretained(metadata_),
- ids_checked));
+ context->components_to_check_for_updates));
}
// This function runs on the blocking pool task runner.
@@ -148,36 +136,49 @@ UpdaterStateAttributes UpdateCheckerImpl::ReadUpdaterStateAttributes() const {
}
void UpdateCheckerImpl::CheckForUpdatesHelper(
- const std::string& session_id,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
+ const std::vector<GURL>& urls,
const base::flat_map<std::string, std::string>& additional_attributes,
const UpdaterStateAttributes& updater_state_attributes,
const std::set<std::string>& active_ids) {
DCHECK(thread_checker_.CalledOnValidThread());
- auto urls(config_->UpdateUrl());
- if (IsEncryptionRequired(components))
- RemoveUnsecureUrls(&urls);
+ if (urls.empty()) {
+ UpdateCheckFailed(ErrorCategory::kUpdateCheck,
+ static_cast<int>(ProtocolError::MISSING_URLS), 0);
+ return;
+ }
+ GURL url = urls.front();
// Components in this update check are either all foreground, or all
// background since this member is inherited from the component's update
// context. Pick the state of the first component to use in the update check.
- DCHECK(!components.empty());
- const bool is_foreground = components.at(ids_checked_[0])->is_foreground();
+ DCHECK(!context->components.empty());
+ const bool is_foreground =
+ context->components.cbegin()->second->is_foreground();
DCHECK(
- std::all_of(components.cbegin(), components.cend(),
+ std::all_of(context->components.cbegin(), context->components.cend(),
[is_foreground](IdToComponentPtrMap::const_reference& elem) {
return is_foreground == elem.second->is_foreground();
}));
+ std::vector<std::string> sent_ids;
+
std::vector<protocol_request::App> apps;
- for (const auto& app_id : ids_checked_) {
- DCHECK_EQ(1u, components.count(app_id));
- const auto& component = components.at(app_id);
+ for (const auto& app_id : context->components_to_check_for_updates) {
+ DCHECK_EQ(1u, context->components.count(app_id));
+ const auto& component = context->components.at(app_id);
DCHECK_EQ(component->id(), app_id);
const auto& crx_component = component->crx_component();
DCHECK(crx_component);
+ if (crx_component->requires_network_encryption &&
+ !url.SchemeIsCryptographic()) {
+ continue;
+ }
+
+ sent_ids.push_back(app_id);
+
std::string install_source;
if (!crx_component->install_source.empty())
install_source = crx_component->install_source;
@@ -207,8 +208,15 @@ void UpdateCheckerImpl::CheckForUpdatesHelper(
absl::nullopt));
}
+ if (sent_ids.empty()) {
+ // No apps could be checked over this URL.
+ UpdateCheckFailed(ErrorCategory::kUpdateCheck,
+ static_cast<int>(ProtocolError::MISSING_URLS), 0);
+ return;
+ }
+
const auto request = MakeProtocolRequest(
- !config_->IsPerUserInstall(), session_id, config_->GetProdId(),
+ !config_->IsPerUserInstall(), context->session_id, config_->GetProdId(),
config_->GetBrowserVersion().GetString(), config_->GetChannel(),
config_->GetOSLongName(), config_->GetDownloadPreference(),
config_->IsMachineExternallyManaged(), additional_attributes,
@@ -216,18 +224,28 @@ void UpdateCheckerImpl::CheckForUpdatesHelper(
request_sender_ = std::make_unique<RequestSender>(config_);
request_sender_->Send(
- urls,
+ {url},
BuildUpdateCheckExtraRequestHeaders(config_->GetProdId(),
config_->GetBrowserVersion(),
- ids_checked_, is_foreground),
+ sent_ids, is_foreground),
config_->GetProtocolHandlerFactory()->CreateSerializer()->Serialize(
request),
config_->EnabledCupSigning(),
base::BindOnce(&UpdateCheckerImpl::OnRequestSenderComplete,
- base::Unretained(this)));
+ base::Unretained(this), context,
+ urls.size() > 1
+ ? absl::optional<base::OnceClosure>(base::BindOnce(
+ &UpdateCheckerImpl::CheckForUpdatesHelper,
+ base::Unretained(this), context,
+ std::vector<GURL>(urls.begin() + 1, urls.end()),
+ additional_attributes, updater_state_attributes,
+ active_ids))
+ : absl::nullopt));
}
void UpdateCheckerImpl::OnRequestSenderComplete(
+ scoped_refptr<UpdateContext> context,
+ absl::optional<base::OnceClosure> fallback,
int error,
const std::string& response,
int retry_after_sec) {
@@ -235,7 +253,11 @@ void UpdateCheckerImpl::OnRequestSenderComplete(
if (error) {
VLOG(1) << "RequestSender failed " << error;
- UpdateCheckFailed(ErrorCategory::kUpdateCheck, error, retry_after_sec);
+ if (fallback && retry_after_sec <= 0) {
+ std::move(*fallback).Run();
+ } else {
+ UpdateCheckFailed(ErrorCategory::kUpdateCheck, error, retry_after_sec);
+ }
return;
}
@@ -249,10 +271,11 @@ void UpdateCheckerImpl::OnRequestSenderComplete(
}
DCHECK_EQ(0, error);
- UpdateCheckSucceeded(parser->results(), retry_after_sec);
+ UpdateCheckSucceeded(context, parser->results(), retry_after_sec);
}
void UpdateCheckerImpl::UpdateCheckSucceeded(
+ scoped_refptr<UpdateContext> context,
const ProtocolParser::Results& results,
int retry_after_sec) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -276,7 +299,8 @@ void UpdateCheckerImpl::UpdateCheckSucceeded(
ErrorCategory::kNone, 0, retry_after_sec);
if (daynum != ProtocolParser::kNoDaystart) {
- metadata_->SetDateLastData(ids_checked_, daynum, std::move(reply));
+ metadata_->SetDateLastData(context->components_to_check_for_updates, daynum,
+ std::move(reply));
return;
}
diff --git a/chromium/components/update_client/update_checker.h b/chromium/components/update_client/update_checker.h
index 29df8730c3b..f0cf9a62d20 100644
--- a/chromium/components/update_client/update_checker.h
+++ b/chromium/components/update_client/update_checker.h
@@ -21,6 +21,7 @@ namespace update_client {
class Configurator;
class PersistedData;
+struct UpdateContext;
class UpdateChecker {
public:
@@ -40,15 +41,11 @@ class UpdateChecker {
virtual ~UpdateChecker() = default;
// Initiates an update check for the components specified by their ids.
- // |additional_attributes| provides a way to customize the <request> element.
- // |is_foreground| controls the value of "X-Goog-Update-Interactivity"
- // header which is sent with the update check.
- // On completion, the state of |components| is mutated as required by the
- // server response received.
+ // `update_context` contains the updateable apps. `additional_attributes`
+ // specifies any extra request data to send. On completion, the state of
+ // `components` is mutated as required by the server response received.
virtual void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> update_context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) = 0;
diff --git a/chromium/components/update_client/update_checker_unittest.cc b/chromium/components/update_client/update_checker_unittest.cc
index f2555a22758..952e627c8f4 100644
--- a/chromium/components/update_client/update_checker_unittest.cc
+++ b/chromium/components/update_client/update_checker_unittest.cc
@@ -140,6 +140,7 @@ void UpdateCheckerTest::SetUp() {
retry_after_sec_ = 0;
update_context_ = MakeMockUpdateContext();
update_context_->is_foreground = is_foreground_;
+ update_context_->components_to_check_for_updates = {kUpdateItemId};
}
void UpdateCheckerTest::TearDown() {
@@ -230,16 +231,14 @@ TEST_P(UpdateCheckerTest, UpdateCheckSuccess) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] =
+ update_context_->components[kUpdateItemId] =
MakeComponent("TEST", "foobar_install_data_index");
- auto& component = components[kUpdateItemId];
+ auto& component = update_context_->components[kUpdateItemId];
component->crx_component_->installer_attributes["ap"] = "some_ap";
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components,
- {{"extra", "params"}, {"testrequest", "1"}},
+ update_context_, {{"extra", "params"}, {"testrequest", "1"}},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -249,7 +248,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckSuccess) {
ASSERT_EQ(1, post_interceptor_->GetCount())
<< post_interceptor_->GetRequestsAsString();
- // Sanity check the request.
+ // Check the request.
const auto root =
base::JSONReader::Read(post_interceptor_->GetRequestBody(0));
ASSERT_TRUE(root);
@@ -316,7 +315,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckSuccess) {
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
#endif // IS_WIN
- // Sanity check the arguments of the callback after parsing.
+ // Check the arguments of the callback after parsing.
EXPECT_EQ(ErrorCategory::kNone, error_category_);
EXPECT_EQ(0, error_);
EXPECT_TRUE(results_);
@@ -353,15 +352,14 @@ TEST_P(UpdateCheckerTest, UpdateCheckInvalidAp) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent("TEST");
+ update_context_->components[kUpdateItemId] = MakeComponent("TEST");
// Make "ap" too long.
- auto& component = components[kUpdateItemId];
+ auto& component = update_context_->components[kUpdateItemId];
component->crx_component_->installer_attributes["ap"] = std::string(257, 'a');
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
@@ -396,11 +394,10 @@ TEST_P(UpdateCheckerTest, UpdateCheckSuccessNoBrand) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent("TOOLONG");
+ update_context_->components[kUpdateItemId] = MakeComponent("TOOLONG");
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
@@ -427,6 +424,34 @@ TEST_P(UpdateCheckerTest, UpdateCheckSuccessNoBrand) {
->GetString());
}
+TEST_P(UpdateCheckerTest, UpdateCheckFallback) {
+ config_->SetUpdateCheckUrls(
+ {GURL("https://localhost2/update2"), GURL("https://localhost2/update2")});
+
+ // 404 first.
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(
+ std::make_unique<PartialMatch>("updatecheck"), net::HTTP_NOT_FOUND));
+ // Then OK.
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(
+ std::make_unique<PartialMatch>("updatecheck"),
+ test_file("updatecheck_reply_1.json")));
+
+ update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+ update_context_->components[kUpdateItemId] = MakeComponent("TOOLONG");
+
+ update_checker_->CheckForUpdates(
+ update_context_, {},
+ base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+ base::Unretained(this)));
+
+ RunThreads();
+ EXPECT_EQ(2, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(2, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+}
+
// Simulates a 403 server response error.
TEST_P(UpdateCheckerTest, UpdateCheckError) {
EXPECT_TRUE(post_interceptor_->ExpectRequest(
@@ -434,11 +459,10 @@ TEST_P(UpdateCheckerTest, UpdateCheckError) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -462,12 +486,10 @@ TEST_P(UpdateCheckerTest, UpdateCheckDownloadPreference) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components,
- {{"extra", "params"}},
+ update_context_, {{"extra", "params"}},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -491,11 +513,10 @@ TEST_P(UpdateCheckerTest, UpdateCheckCupError) {
config_->SetEnabledCupSigning(true);
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent("TEST");
+ update_context_->components[kUpdateItemId] = MakeComponent("TEST");
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
@@ -506,7 +527,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckCupError) {
ASSERT_EQ(1, post_interceptor_->GetCount())
<< post_interceptor_->GetRequestsAsString();
- // Sanity check the request.
+ // Check the request.
const auto& request = post_interceptor_->GetRequestBody(0);
const auto root = base::JSONReader::Read(request);
ASSERT_TRUE(root);
@@ -539,14 +560,13 @@ TEST_P(UpdateCheckerTest, UpdateCheckRequiresEncryptionError) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
- auto& component = components[kUpdateItemId];
+ auto& component = update_context_->components[kUpdateItemId];
component->crx_component_->requires_network_encryption = true;
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -567,22 +587,19 @@ TEST_P(UpdateCheckerTest, UpdateCheckLastRollCall) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
// Do two update-checks.
activity_data_service_->SetDaysSinceLastRollCall(kUpdateItemId, 5);
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components,
- {{"extra", "params"}},
+ update_context_, {{"extra", "params"}},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components,
- {{"extra", "params"}},
+ update_context_, {{"extra", "params"}},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -618,14 +635,12 @@ TEST_P(UpdateCheckerTest, UpdateCheckLastActive) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
activity_data_service_->SetActiveBit(kUpdateItemId, true);
activity_data_service_->SetDaysSinceLastActive(kUpdateItemId, 10);
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components,
- {{"extra", "params"}},
+ update_context_, {{"extra", "params"}},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -636,8 +651,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckLastActive) {
activity_data_service_->SetActiveBit(kUpdateItemId, true);
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components,
- {{"extra", "params"}},
+ update_context_, {{"extra", "params"}},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -647,8 +661,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckLastActive) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components,
- {{"extra", "params"}},
+ update_context_, {{"extra", "params"}},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -693,10 +706,9 @@ TEST_P(UpdateCheckerTest, UpdateCheckLastActive) {
TEST_P(UpdateCheckerTest, UpdateCheckInstallSource) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
- auto& component = components[kUpdateItemId];
+ auto& component = update_context_->components[kUpdateItemId];
auto crx_component = component->crx_component();
if (is_foreground_) {
@@ -707,7 +719,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckInstallSource) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -729,7 +741,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckInstallSource) {
crx_component->install_location = "policy";
component->set_crx_component(*crx_component);
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -752,7 +764,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckInstallSource) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -773,7 +785,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckInstallSource) {
crx_component->install_location = "external";
component->set_crx_component(*crx_component);
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -790,10 +802,9 @@ TEST_P(UpdateCheckerTest, UpdateCheckInstallSource) {
TEST_P(UpdateCheckerTest, ComponentDisabled) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
- auto& component = components[kUpdateItemId];
+ auto& component = update_context_->components[kUpdateItemId];
auto crx_component = component->crx_component();
{
@@ -803,7 +814,7 @@ TEST_P(UpdateCheckerTest, ComponentDisabled) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -825,7 +836,7 @@ TEST_P(UpdateCheckerTest, ComponentDisabled) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -847,7 +858,7 @@ TEST_P(UpdateCheckerTest, ComponentDisabled) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -870,7 +881,7 @@ TEST_P(UpdateCheckerTest, ComponentDisabled) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -894,7 +905,7 @@ TEST_P(UpdateCheckerTest, ComponentDisabled) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -920,7 +931,7 @@ TEST_P(UpdateCheckerTest, ComponentDisabled) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -942,10 +953,9 @@ TEST_P(UpdateCheckerTest, ComponentDisabled) {
TEST_P(UpdateCheckerTest, UpdateCheckUpdateDisabled) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
- auto& component = components[kUpdateItemId];
+ auto& component = update_context_->components[kUpdateItemId];
auto crx_component = component->crx_component();
// Ignore this test parameter to keep the test simple.
@@ -961,7 +971,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckUpdateDisabled) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -987,7 +997,7 @@ TEST_P(UpdateCheckerTest, UpdateCheckUpdateDisabled) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -1006,10 +1016,9 @@ TEST_P(UpdateCheckerTest, UpdateCheckUpdateDisabled) {
TEST_P(UpdateCheckerTest, SameVersionUpdateAllowed) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
- auto& component = components[kUpdateItemId];
+ auto& component = update_context_->components[kUpdateItemId];
auto crx_component = component->crx_component();
EXPECT_FALSE(crx_component->same_version_update_allowed);
{
@@ -1021,7 +1030,7 @@ TEST_P(UpdateCheckerTest, SameVersionUpdateAllowed) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_noupdate.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -1044,7 +1053,7 @@ TEST_P(UpdateCheckerTest, SameVersionUpdateAllowed) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_noupdate.json")));
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -1063,11 +1072,10 @@ TEST_P(UpdateCheckerTest, NoUpdateActionRun) {
test_file("updatecheck_reply_noupdate.json")));
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -1077,7 +1085,7 @@ TEST_P(UpdateCheckerTest, NoUpdateActionRun) {
ASSERT_EQ(1, post_interceptor_->GetCount())
<< post_interceptor_->GetRequestsAsString();
- // Sanity check the arguments of the callback after parsing.
+ // Check the arguments of the callback after parsing.
EXPECT_EQ(ErrorCategory::kNone, error_category_);
EXPECT_EQ(0, error_);
EXPECT_TRUE(results_);
@@ -1101,14 +1109,13 @@ TEST_P(UpdateCheckerTest, UpdatePauseResume) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent("TEST");
+ update_context_->components[kUpdateItemId] = MakeComponent("TEST");
// Ignore this test parameter to keep the test simple.
update_context_->is_foreground = false;
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -1145,12 +1152,11 @@ TEST_P(UpdateCheckerTest, UpdateResetUpdateChecker) {
std::move(quit_closure)));
post_interceptor_->Pause();
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
runloop.Run();
@@ -1165,11 +1171,10 @@ TEST_P(UpdateCheckerTest, ParseErrorProtocolVersionMismatch) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -1194,11 +1199,10 @@ TEST_P(UpdateCheckerTest, ParseErrorAppStatusErrorUnknownApplication) {
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
@@ -1224,12 +1228,11 @@ TEST_P(UpdateCheckerTest, DomainJoined) {
test_file("updatecheck_reply_noupdate.json")));
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
- IdToComponentPtrMap components;
- components[kUpdateItemId] = MakeComponent();
+ update_context_->components[kUpdateItemId] = MakeComponent();
config_->SetIsMachineExternallyManaged(is_managed);
update_checker_->CheckForUpdates(
- update_context_->session_id, {kUpdateItemId}, components, {},
+ update_context_, {},
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
RunThreads();
diff --git a/chromium/components/update_client/update_client_unittest.cc b/chromium/components/update_client/update_client_unittest.cc
index edcd0e6d93e..e6fc6da1a18 100644
--- a/chromium/components/update_client/update_client_unittest.cc
+++ b/chromium/components/update_client/update_client_unittest.cc
@@ -38,6 +38,7 @@
#include "components/update_client/update_checker.h"
#include "components/update_client/update_client_errors.h"
#include "components/update_client/update_client_internal.h"
+#include "components/update_client/update_engine.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -301,18 +302,16 @@ TEST_F(UpdateClientTest, OneCrxNoUpdate) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check.front());
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates.front());
+ EXPECT_EQ(1u, context->components.count(id));
- auto& component = components.at(id);
+ auto& component = context->components.at(id);
EXPECT_TRUE(component->is_foreground());
@@ -430,9 +429,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
/*
@@ -459,14 +456,14 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
</app>
</response>
*/
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(2u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(2u, context->components_to_check_for_updates.size());
ProtocolParser::Results results;
{
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
@@ -482,20 +479,20 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
result.manifest.packages.push_back(package);
results.list.push_back(result);
- EXPECT_FALSE(components.at(id)->is_foreground());
+ EXPECT_FALSE(context->components.at(id)->is_foreground());
}
{
const std::string id = "abagagagagagagagagagagagagagagag";
- EXPECT_EQ(id, ids_to_check[1]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[1]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result result;
result.extension_id = id;
result.status = "noupdate";
results.list.push_back(result);
- EXPECT_FALSE(components.at(id)->is_foreground());
+ EXPECT_FALSE(context->components.at(id)->is_foreground());
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -684,9 +681,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateFirstServerIgnoresSecond) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
/*
@@ -710,14 +705,14 @@ TEST_F(UpdateClientTest, TwoCrxUpdateFirstServerIgnoresSecond) {
</app>
</response>
*/
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(2u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(2u, context->components_to_check_for_updates.size());
ProtocolParser::Results results;
{
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
@@ -733,7 +728,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateFirstServerIgnoresSecond) {
result.manifest.packages.push_back(package);
results.list.push_back(result);
- EXPECT_FALSE(components.at(id)->is_foreground());
+ EXPECT_FALSE(context->components.at(id)->is_foreground());
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -912,9 +907,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentData) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
/*
@@ -938,14 +931,14 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentData) {
</app>
</response>
*/
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
ProtocolParser::Results results;
{
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
@@ -961,7 +954,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentData) {
result.manifest.packages.push_back(package);
results.list.push_back(result);
- EXPECT_FALSE(components.at(id)->is_foreground());
+ EXPECT_FALSE(context->components.at(id)->is_foreground());
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -1119,9 +1112,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentDataAtAll) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
NOTREACHED();
@@ -1233,9 +1224,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
/*
@@ -1274,14 +1263,14 @@ TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) {
</response>
*/
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(2u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(2u, context->components_to_check_for_updates.size());
ProtocolParser::Results results;
{
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
@@ -1300,8 +1289,8 @@ TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) {
{
const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
- EXPECT_EQ(id, ids_to_check[1]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[1]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
@@ -1535,12 +1524,10 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
- EXPECT_FALSE(session_id.empty());
+ EXPECT_FALSE(context->session_id.empty());
static int num_call = 0;
++num_call;
@@ -1569,8 +1556,8 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) {
</response>
*/
const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
@@ -1612,8 +1599,8 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) {
</response>
*/
const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx";
@@ -1943,9 +1930,7 @@ TEST_F(UpdateClientTest, OneCrxInstallError) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
/*
@@ -1969,11 +1954,11 @@ TEST_F(UpdateClientTest, OneCrxInstallError) {
</app>
</response>
*/
- EXPECT_FALSE(session_id.empty());
+ EXPECT_FALSE(context->session_id.empty());
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
@@ -2151,12 +2136,10 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
- EXPECT_FALSE(session_id.empty());
+ EXPECT_FALSE(context->session_id.empty());
static int num_call = 0;
++num_call;
@@ -2186,8 +2169,8 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) {
</response>
*/
const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
@@ -2230,8 +2213,8 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) {
</response>
*/
const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx";
@@ -2497,18 +2480,16 @@ TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check.front());
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates.front());
+ EXPECT_EQ(1u, context->components.count(id));
- auto& component = components.at(id);
+ auto& component = context->components.at(id);
EXPECT_FALSE(component->is_foreground());
@@ -2640,9 +2621,7 @@ TEST_F(UpdateClientTest, OneCrxInstall) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
/*
@@ -2667,12 +2646,12 @@ TEST_F(UpdateClientTest, OneCrxInstall) {
</app>
</response>
*/
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
@@ -2694,7 +2673,7 @@ TEST_F(UpdateClientTest, OneCrxInstall) {
results.list.push_back(result);
// Verify that calling Install sets ondemand.
- EXPECT_TRUE(components.at(id)->is_foreground());
+ EXPECT_TRUE(context->components.at(id)->is_foreground());
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
@@ -2873,9 +2852,7 @@ TEST_F(UpdateClientTest, OneCrxInstallNoCrxComponentData) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
NOTREACHED();
@@ -2995,16 +2972,14 @@ TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check.front());
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates.front());
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result result;
result.extension_id = id;
@@ -3014,7 +2989,7 @@ TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) {
results.list.push_back(result);
// Verify that calling Install sets |is_foreground| for the component.
- EXPECT_TRUE(components.at(id)->is_foreground());
+ EXPECT_TRUE(context->components.at(id)->is_foreground());
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
@@ -3119,9 +3094,7 @@ TEST_F(UpdateClientTest, EmptyIdList) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
NOTREACHED();
@@ -3177,9 +3150,7 @@ TEST_F(UpdateClientTest, SendUninstallPing) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
NOTREACHED();
@@ -3286,12 +3257,10 @@ TEST_F(UpdateClientTest, RetryAfter) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
- EXPECT_FALSE(session_id.empty());
+ EXPECT_FALSE(context->session_id.empty());
static int num_call = 0;
++num_call;
@@ -3304,10 +3273,10 @@ TEST_F(UpdateClientTest, RetryAfter) {
retry_after_sec = 60 * 60; // 1 hour.
}
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check.front());
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates.front());
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result result;
result.extension_id = id;
@@ -3465,9 +3434,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
/*
@@ -3506,14 +3473,14 @@ TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) {
</response>
*/
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(2u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(2u, context->components_to_check_for_updates.size());
ProtocolParser::Results results;
{
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
@@ -3532,8 +3499,8 @@ TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) {
{
const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
- EXPECT_EQ(id, ids_to_check[1]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[1]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
@@ -3729,16 +3696,14 @@ TEST_F(UpdateClientTest, OneCrxUpdateCheckFails) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check.front());
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates.front());
+ EXPECT_EQ(1u, context->components.count(id));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(update_check_callback), absl::nullopt,
@@ -3878,13 +3843,11 @@ TEST_F(UpdateClientTest, OneCrxErrorUnknownApp) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(4u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(4u, context->components_to_check_for_updates.size());
const std::string update_response =
")]}'"
@@ -4031,9 +3994,7 @@ TEST_F(UpdateClientTest, ActionRun_Install) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
/*
@@ -4059,12 +4020,12 @@ TEST_F(UpdateClientTest, ActionRun_Install) {
</app>
</response>
*/
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
const std::string id = "gjpmebpgbhcamgdgjcmnjfhggjpgcimm";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result::Manifest::Package package;
package.name = "runaction_test_win.crx3";
@@ -4220,9 +4181,7 @@ TEST_F(UpdateClientTest, ActionRun_NoUpdate) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
/*
@@ -4239,11 +4198,11 @@ TEST_F(UpdateClientTest, ActionRun_NoUpdate) {
</app>
</response>
*/
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
const std::string id = "gjpmebpgbhcamgdgjcmnjfhggjpgcimm";
- EXPECT_EQ(id, ids_to_check[0]);
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates[0]);
+ EXPECT_EQ(1u, context->components.count(id));
ProtocolParser::Result result;
result.extension_id = id;
@@ -4402,18 +4361,16 @@ TEST_F(UpdateClientTest, CustomAttributeNoUpdate) {
}
void CheckForUpdates(
- const std::string& session_id,
- const std::vector<std::string>& ids_to_check,
- const IdToComponentPtrMap& components,
+ scoped_refptr<UpdateContext> context,
const base::flat_map<std::string, std::string>& additional_attributes,
UpdateCheckCallback update_check_callback) override {
- EXPECT_FALSE(session_id.empty());
- EXPECT_EQ(1u, ids_to_check.size());
+ EXPECT_FALSE(context->session_id.empty());
+ EXPECT_EQ(1u, context->components_to_check_for_updates.size());
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
- EXPECT_EQ(id, ids_to_check.front());
- EXPECT_EQ(1u, components.count(id));
+ EXPECT_EQ(id, context->components_to_check_for_updates.front());
+ EXPECT_EQ(1u, context->components.count(id));
- auto& component = components.at(id);
+ auto& component = context->components.at(id);
EXPECT_TRUE(component->is_foreground());
diff --git a/chromium/components/update_client/update_engine.cc b/chromium/components/update_client/update_engine.cc
index 8ea994e1067..c09017a1537 100644
--- a/chromium/components/update_client/update_engine.cc
+++ b/chromium/components/update_client/update_engine.cc
@@ -162,9 +162,7 @@ void UpdateEngine::DoUpdateCheck(scoped_refptr<UpdateContext> update_context) {
update_checker_factory_(config_, metadata_.get());
update_context->update_checker->CheckForUpdates(
- update_context->session_id,
- update_context->components_to_check_for_updates,
- update_context->components, config_->ExtraRequestParams(),
+ update_context, config_->ExtraRequestParams(),
base::BindOnce(&UpdateEngine::UpdateCheckResultsAvailable, this,
update_context));
}
diff --git a/chromium/components/update_client/update_query_params.cc b/chromium/components/update_client/update_query_params.cc
index a36b56d3079..a707c0ea2ca 100644
--- a/chromium/components/update_client/update_query_params.cc
+++ b/chromium/components/update_client/update_query_params.cc
@@ -32,7 +32,7 @@ const char kOs[] =
"win";
#elif BUILDFLAG(IS_ANDROID)
"android";
-#elif BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_CHROMEOS)
"cros";
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
"linux";
@@ -124,10 +124,8 @@ const char* UpdateQueryParams::GetNaclArch() {
#else
return "x86-32";
#endif
-#elif defined(ARCH_CPU_ARMEL)
+#elif defined(ARCH_CPU_ARM_FAMILY)
return "arm";
-#elif defined(ARCH_CPU_ARM64)
- return "arm64";
#elif defined(ARCH_CPU_MIPSEL)
return "mips32";
#elif defined(ARCH_CPU_MIPS64EL)
diff --git a/chromium/components/url_formatter/android/BUILD.gn b/chromium/components/url_formatter/android/BUILD.gn
index 02ebbef4b94..06b27dda0bc 100644
--- a/chromium/components/url_formatter/android/BUILD.gn
+++ b/chromium/components/url_formatter/android/BUILD.gn
@@ -6,7 +6,8 @@ import("//build/config/android/rules.gni")
android_library("url_formatter_java") {
deps = [
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//url:gurl_java",
"//url:origin_java",
diff --git a/chromium/components/url_formatter/elide_url.cc b/chromium/components/url_formatter/elide_url.cc
index 0ea12974699..fa9f5458590 100644
--- a/chromium/components/url_formatter/elide_url.cc
+++ b/chromium/components/url_formatter/elide_url.cc
@@ -8,6 +8,7 @@
#include "base/check_op.h"
#include "base/i18n/rtl.h"
+#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
@@ -15,7 +16,6 @@
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/url_formatter/url_formatter.h"
-#include "net/base/escape.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "ui/gfx/text_constants.h"
#include "ui/gfx/text_elider.h"
@@ -94,40 +94,6 @@ std::u16string ElideComponentizedPath(
available_pixel_width, gfx::ELIDE_TAIL);
}
-// Splits the hostname in the |url| into sub-strings for the full hostname,
-// the domain (TLD+1), and the subdomain (everything leading the domain).
-void SplitHost(const GURL& url,
- std::u16string* url_host,
- std::u16string* url_domain,
- std::u16string* url_subdomain) {
- // GURL stores IDN hostnames in punycode. Convert back to Unicode for
- // display to the user. (IDNToUnicode() will only perform this conversion
- // if it's safe to display this host/domain in Unicode.)
- *url_host = url_formatter::IDNToUnicode(url.host());
-
- // Get domain and registry information from the URL.
- std::string domain_puny =
- net::registry_controlled_domains::GetDomainAndRegistry(
- url, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
- *url_domain = domain_puny.empty() ?
- *url_host : url_formatter::IDNToUnicode(domain_puny);
-
- // Add port if required.
- if (!url.port().empty()) {
- *url_host += base::UTF8ToUTF16(":" + url.port());
- *url_domain += base::UTF8ToUTF16(":" + url.port());
- }
-
- // Get sub domain.
- const size_t domain_start_index = url_host->find(*url_domain);
- constexpr base::StringPiece16 kWwwPrefix = u"www.";
- if (domain_start_index != std::u16string::npos)
- *url_subdomain = url_host->substr(0, domain_start_index);
- if ((*url_subdomain == kWwwPrefix || url_subdomain->empty() ||
- url.SchemeIsFile())) {
- url_subdomain->clear();
- }
-}
#endif // !BUILDFLAG(IS_ANDROID)
bool ShouldShowScheme(base::StringPiece scheme,
@@ -172,7 +138,7 @@ std::u16string ElideUrl(const GURL& url,
// Get a formatted string and corresponding parsing of the url.
url::Parsed parsed;
const std::u16string url_string = url_formatter::FormatUrl(
- url, url_formatter::kFormatUrlOmitDefaults, net::UnescapeRule::SPACES,
+ url, url_formatter::kFormatUrlOmitDefaults, base::UnescapeRule::SPACES,
&parsed, nullptr, nullptr);
if (available_pixel_width <= 0)
return url_string;
@@ -211,7 +177,7 @@ std::u16string ElideUrl(const GURL& url,
std::u16string url_host;
std::u16string url_domain;
std::u16string url_subdomain;
- SplitHost(url, &url_host, &url_domain, &url_subdomain);
+ url_formatter::SplitHost(url, &url_host, &url_domain, &url_subdomain);
// If this is a file type, the path is now defined as everything after ":".
// For example, "C:/aa/aa/bb", the path is "/aa/bb/cc". Interesting, the
@@ -341,7 +307,7 @@ std::u16string ElideHost(const GURL& url,
std::u16string url_host;
std::u16string url_domain;
std::u16string url_subdomain;
- SplitHost(url, &url_host, &url_domain, &url_subdomain);
+ url_formatter::SplitHost(url, &url_host, &url_domain, &url_subdomain);
const float pixel_width_url_host = gfx::GetStringWidthF(url_host, font_list);
if (available_pixel_width >= pixel_width_url_host)
@@ -439,7 +405,57 @@ std::u16string FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
url_formatter::kFormatUrlTrimAfterHost |
url_formatter::kFormatUrlOmitHTTPS |
url_formatter::kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::SPACES, nullptr, nullptr, nullptr);
+ base::UnescapeRule::SPACES, nullptr, nullptr, nullptr);
+}
+
+#if BUILDFLAG(IS_IOS)
+std::u16string
+FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix(
+ const GURL& url) {
+ return url_formatter::FormatUrl(
+ url,
+ url_formatter::kFormatUrlOmitDefaults |
+ url_formatter::kFormatUrlTrimAfterHost |
+ url_formatter::kFormatUrlOmitHTTPS |
+ url_formatter::kFormatUrlOmitTrivialSubdomains |
+ url_formatter::kFormatUrlOmitMobilePrefix,
+ base::UnescapeRule::SPACES, nullptr, nullptr, nullptr);
+}
+#endif
+
+void SplitHost(const GURL& url,
+ std::u16string* url_host,
+ std::u16string* url_domain,
+ std::u16string* url_subdomain) {
+ // GURL stores IDN hostnames in punycode. Convert back to Unicode for
+ // display to the user. (IDNToUnicode() will only perform this conversion
+ // if it's safe to display this host/domain in Unicode.)
+ *url_host = url_formatter::IDNToUnicode(url.host());
+
+ // Get domain and registry information from the URL.
+ std::string domain_puny =
+ net::registry_controlled_domains::GetDomainAndRegistry(
+ url, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+ *url_domain = domain_puny.empty() ? *url_host
+ : url_formatter::IDNToUnicode(domain_puny);
+
+ // Add port if required.
+ if (!url.port().empty()) {
+ *url_host += base::UTF8ToUTF16(":" + url.port());
+ *url_domain += base::UTF8ToUTF16(":" + url.port());
+ }
+
+ // Get sub domain if requested.
+ if (url_subdomain) {
+ const size_t domain_start_index = url_host->find(*url_domain);
+ constexpr base::StringPiece16 kWwwPrefix = u"www.";
+ if (domain_start_index != std::u16string::npos)
+ *url_subdomain = url_host->substr(0, domain_start_index);
+ if ((*url_subdomain == kWwwPrefix || url_subdomain->empty() ||
+ url.SchemeIsFile())) {
+ url_subdomain->clear();
+ }
+ }
}
} // namespace url_formatter
diff --git a/chromium/components/url_formatter/elide_url.h b/chromium/components/url_formatter/elide_url.h
index b3782d1ad61..9ec74f98108 100644
--- a/chromium/components/url_formatter/elide_url.h
+++ b/chromium/components/url_formatter/elide_url.h
@@ -113,6 +113,35 @@ std::u16string FormatOriginForSecurityDisplay(
std::u16string FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
const GURL& url);
+// This is a convenience function for formatting a URL in a concise and
+// human-friendly way, omitting the HTTP/HTTPS scheme, the username and
+// password, the path and removing trivial subdomains and the mobile prefix
+// "m.".
+
+// The IDN hostname is turned to Unicode if the Unicode representation is deemed
+// safe, including RTL characters (as opposed to
+// `url_formatter::FormatUrlForSecurityDisplay()`).
+
+// Example:
+// - "http://user:password@example.com/%20test" -> "example.com"
+// - "http://user:password@example.com/" -> "example.com"
+// - "http://www.xn--frgbolaget-q5a.se" -> "färgbolaget.se"
+// - "http://www.m.google.com" -> "google.com"
+// - "http://m.google.com" -> "google.com"
+#if BUILDFLAG(IS_IOS)
+std::u16string
+FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix(
+ const GURL& url);
+#endif
+
+// Splits the hostname in the `url` into sub-strings for the full hostname,
+// the domain (TLD+1), and the subdomain (everything leading the domain).
+// The `url_subdomain` may be nullptr if it isn't needed by the caller.
+void SplitHost(const GURL& url,
+ std::u16string* url_host,
+ std::u16string* url_domain,
+ std::u16string* url_subdomain);
+
} // namespace url_formatter
#endif // COMPONENTS_URL_FORMATTER_ELIDE_URL_H_
diff --git a/chromium/components/url_formatter/elide_url_unittest.cc b/chromium/components/url_formatter/elide_url_unittest.cc
index c6144d1b922..fcb1d8816f4 100644
--- a/chromium/components/url_formatter/elide_url_unittest.cc
+++ b/chromium/components/url_formatter/elide_url_unittest.cc
@@ -9,11 +9,11 @@
#include "base/cxx17_backports.h"
#include "base/logging.h"
#include "base/run_loop.h"
+#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "components/url_formatter/url_formatter.h"
-#include "net/base/escape.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/text_elider.h"
@@ -696,4 +696,27 @@ TEST(TextEliderTest, FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains) {
GURL("https://xn--mgbh0fb.xn--kgbechtv/")));
}
+TEST(TextEliderTest,
+ FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix) {
+#if BUILDFLAG(IS_IOS)
+ EXPECT_EQ(
+ u"google.com",
+ url_formatter::
+ FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix(
+ GURL("http://m.google.com/example")));
+ EXPECT_EQ(
+ u"google.com",
+ url_formatter::
+ FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix(
+ GURL("http://www.m.google.com/example")));
+ EXPECT_EQ(
+ u"google.com",
+ url_formatter::
+ FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix(
+ GURL("http://m.www.google.com/example")));
+#else
+ GTEST_SKIP();
+#endif
+}
+
} // namespace
diff --git a/chromium/components/url_formatter/spoof_checks/idn_spoof_checker.cc b/chromium/components/url_formatter/spoof_checks/idn_spoof_checker.cc
index e5b11df39b0..bbd09a1cd7d 100644
--- a/chromium/components/url_formatter/spoof_checks/idn_spoof_checker.cc
+++ b/chromium/components/url_formatter/spoof_checks/idn_spoof_checker.cc
@@ -317,10 +317,12 @@ IDNSpoofChecker::IDNSpoofChecker() {
// These characters are, or look like, digits. A domain label entirely made of
// digit-lookalikes or digits is blocked.
+ // IMPORTANT: When you add a new character here, make sure to add it to
+ // extra_confusable_mapper_ in skeleton_generator.cc too.
digits_ = icu::UnicodeSet(UNICODE_STRING_SIMPLE("[0-9]"), status);
digits_.freeze();
digit_lookalikes_ = icu::UnicodeSet(
- icu::UnicodeString::fromUTF8("[θ२২੨੨૨೩೭շзҙӡउওਤ੩૩౩ဒვპੜ੫丩ã„ճ৪੪୫૭୨౨]"),
+ icu::UnicodeString::fromUTF8("[θ२২੨੨૨೩೭շзҙӡउওਤ੩૩౩ဒვპੜკ੫丩ã„ճ৪੪୫૭୨౨]"),
status);
digit_lookalikes_.freeze();
@@ -417,6 +419,7 @@ IDNSpoofChecker::Result IDNSpoofChecker::SafeToDisplayAsUnicode(
}
}
// Disallow domains that contain only numbers and number-spoofs.
+ // This check is reached if domain characters come from single script.
if (IsDigitLookalike(label_string))
return Result::kDigitLookalikes;
@@ -424,6 +427,10 @@ IDNSpoofChecker::Result IDNSpoofChecker::SafeToDisplayAsUnicode(
}
// Disallow domains that contain only numbers and number-spoofs.
+ // This check is reached if domain characters are from different scripts.
+ // This is generally rare. An example case when it would return true is when
+ // the domain contains Latin + Japanese characters that are also digit
+ // lookalikes.
if (IsDigitLookalike(label_string))
return Result::kDigitLookalikes;
diff --git a/chromium/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc b/chromium/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
index ae6cd082442..274cff9b48b 100644
--- a/chromium/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
+++ b/chromium/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
@@ -133,7 +133,7 @@ const IDNTestCase kIdnCases[] = {
kSafe},
// Block mixed numeric + numeric lookalike (12.com, using U+0577).
- {"xn--1-9dd.com", u"1\u07f3.com", kUnsafe},
+ {"xn--1-xcc.com", u"1\u0577.com", kUnsafe},
// Block mixed numeric lookalike + numeric (੨0.com, uses U+0A68).
{"xn--0-6ee.com", u"\u0a680.com", kUnsafe},
@@ -1079,6 +1079,22 @@ const IDNTestCase kIdnCases[] = {
// not a top domain. Should not be decoded to unicode.
{"xn--xample-9ua.test.xn--nt-bja", u"\u00e9xample.test.n\u00e9t", kUnsafe},
+ // Digit lookalike check of 16კ.com with character “კ†(U+10D9)
+ // Test case for https://crbug.com/1156531
+ {"xn--16-1ik.com", u"16\u10d9.com", kUnsafe},
+
+ // Skeleton generator check of officeკ65.com with character “კ†(U+10D9)
+ // Test case for https://crbug.com/1156531
+ {"xn--office65-l04a.com", u"office\u10d965.com", kUnsafe},
+
+ // Digit lookalike check of 16ੜ.com with character “ੜ†(U+0A5C)
+ // Test case for https://crbug.com/1156531 (missed skeleton map)
+ {"xn--16-ogg.com", u"16\u0a5c.com", kUnsafe},
+
+ // Skeleton generator check of officeੜ65.com with character “ੜ†(U+0A5C)
+ // Test case for https://crbug.com/1156531 (missed skeleton map)
+ {"xn--office65-hts.com", u"office\u0a5c65.com", kUnsafe},
+
// New test cases go ↑↑ above.
// /!\ WARNING: You MUST use tools/security/idn_test_case_generator.py to
@@ -1172,7 +1188,17 @@ TEST_F(IDNSpoofCheckerTest, GetSimilarTopDomain) {
{u"subdomain.test.net", ""},
// An IDN subdomain of a top domain should not return a similar top domain
// result.
- {u"subdómain.test.net", ""}};
+ {u"subdómain.test.net", ""},
+ // Test cases for https://crbug.com/1250993:
+ {u"tesł.net", "test.net"},
+ {u"Å‚est.net", "test.net"},
+ {u"łesł.net", "test.net"},
+ // Test case for https://crbug.com/1207187
+ {u"စ2.com", "o2.com"},
+ // Test case for https://crbug.com/1156531
+ {u"კ9.com", "39.com"},
+ // Test case for https://crbug.com/1156531 (missed skeleton map)
+ {u"ੜ9.com", "39.com"}};
for (const TestCase& test_case : kTestCases) {
const TopDomainEntry entry =
IDNSpoofChecker().GetSimilarTopDomain(test_case.hostname);
@@ -1392,10 +1418,58 @@ TEST(IDNSpoofCheckerNoFixtureTest, AlternativeSkeletons) {
{u"Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“", 0, {}},
{u"Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“", 1, {}},
{u"Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“", 2, {}},
- {u"Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“", 100, {}}};
+ {u"Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“Å“", 100, {}},
+
+ {u"łwiłłer", 0, {}},
+ {u"łwiłłer", 1, {u"łwiłłer"}},
+ {u"łwiłłer",
+ 2,
+ {u"\x142wi\x142ler",
+ u"\x142wi\x142\x142"
+ u"er"}},
+ {u"łwiłłer",
+ 100,
+ {u"lwiller",
+ u"lwilter",
+ u"lwil\x142"
+ u"er",
+ u"lwitler",
+ u"lwitter",
+ u"lwit\x142"
+ u"er",
+ u"lwi\x142ler",
+ u"lwi\x142ter",
+ u"lwi\x142\x142"
+ u"er",
+ u"twiller",
+ u"twilter",
+ u"twil\x142"
+ u"er",
+ u"twitler",
+ u"twitter",
+ u"twit\x142"
+ u"er",
+ u"twi\x142ler",
+ u"twi\x142ter",
+ u"twi\x142\x142"
+ u"er",
+ u"\x142willer",
+ u"\x142wilter",
+ u"\x142wil\x142"
+ u"er",
+ u"\x142witler",
+ u"\x142witter",
+ u"\x142wit\x142"
+ u"er",
+ u"\x142wi\x142ler",
+ u"\x142wi\x142ter",
+ u"\x142wi\x142\x142"
+ u"er"}},
+ };
SkeletonMap skeleton_map;
skeleton_map[u'Å“'] = {"ce", "oe"};
skeleton_map[u'þ'] = {"b", "p"};
+ skeleton_map[u'Å‚'] = {"l", "t"};
for (const TestCase& test_case : kTestCases) {
const auto strings = SkeletonGenerator::GenerateSupplementalHostnames(
diff --git a/chromium/components/url_formatter/spoof_checks/skeleton_generator.cc b/chromium/components/url_formatter/spoof_checks/skeleton_generator.cc
index 2dfed8e8388..3ba02470897 100644
--- a/chromium/components/url_formatter/spoof_checks/skeleton_generator.cc
+++ b/chromium/components/url_formatter/spoof_checks/skeleton_generator.cc
@@ -5,7 +5,6 @@
#include "components/url_formatter/spoof_checks/skeleton_generator.h"
#include <ostream>
-
#include <queue>
#include "base/i18n/unicodestring.h"
@@ -21,6 +20,11 @@ namespace {
using QueueItem = std::vector<std::u16string>;
+// Maximum length of a hostname whose supplemental hostnames we'll calculate.
+// For hostnames longer than this length, the supplemental hostnames will be
+// empty.
+const size_t kMaxHostnameLengthToComputeSupplementalHostnames = 32;
+
// Maximum number of supplemental hostname to generate for a given input.
// If this number is too high, we may end up DOSing the browser process.
// If it's too low, we may not be able to cover some lookalike URLs.
@@ -72,7 +76,7 @@ SkeletonGenerator::SkeletonGenerator(const USpoofChecker* checker)
lgc_letters_n_ascii_.freeze();
// Supplement the Unicode confusable list by the following mapping.
- // NOTE: Adding a digit-lookalike? Add it to digit_lookalikes_ in
+ // IMPORTANT: Adding a digit-lookalike? Add it to digit_lookalikes_ in
// idn_spoof_checker.cc, too.
// - {U+00E6 (æ), U+04D5 (ӕ)} => "ae"
// - {U+03FC (ϼ), U+048F (Ò)} => p
@@ -99,7 +103,7 @@ SkeletonGenerator::SkeletonGenerator(const USpoofChecker* checker)
// - {U+0D1F (ട), U+0E23 (ร), U+0EA3 (ຣ), U+0EAE (ຮ)} => s
// - U+1042 (á‚) => j
// - {U+0966 (०), U+09E6 (০), U+0A66 (੦), U+0AE6 (૦), U+0B30 (ଠ),
- // U+0B66 (୦), U+0CE6 (೦)} => o,
+ // U+0B66 (୦), U+0CE6 (೦), U+1005 (စ)} => o,
// - {U+09ED (৭), U+0A67 (੧), U+0AE7 (૧)} => q,
// - {U+0E1A (บ), U+0E9A (ບ)} => u,
// - {U+03B8 (θ)} => 0,
@@ -107,7 +111,7 @@ SkeletonGenerator::SkeletonGenerator(const USpoofChecker* checker)
// U+0ce9 (೩), U+0ced (೭), U+0577 (շ)} => 2,
// - {U+0437 (з), U+0499 (ҙ), U+04E1 (ӡ), U+0909 (उ), U+0993 (ও),
// U+0A24 (ਤ), U+0A69 (੩), U+0AE9 (૩), U+0C69 (౩),
- // U+1012 (ဒ), U+10D5 (ვ), U+10DE (პ)} => 3
+ // U+1012 (ဒ), U+10D5 (ვ), U+10DE (პ), U+0A5C (ੜ), U+10D9 (კ)} => 3
// - {U+0A6B (à©«), U+4E29 (丩), U+3110 (ã„)} => 4,
// - U+0573 (Õ³) => 6
// - {U+09EA (৪), U+0A6A (੪), U+0b6b (୫)} => 8,
@@ -125,12 +129,12 @@ SkeletonGenerator::SkeletonGenerator(const USpoofChecker* checker)
"[мӎ] > m; [єҽҿá”] > e; Ò‘ > r; [Ò“Ó»] > f;"
"[ҫင] > c; [ұ丫] > y; [χҳӽӿ乂] > x;"
"[ԃძ] > d; [Ôဌ] > g; [ടรຣຮ] > s; á‚ > j;"
- "[०০੦૦ଠ୦೦] > o;"
+ "[०০੦૦ଠ୦೦စ] > o;"
"[৭੧૧] > q;"
"[บບ] > u;"
"[θ] > 0;"
"[२২੨੨૨೩೭շ] > 2;"
- "[зҙӡउওਤ੩૩౩ဒვპ] > 3;"
+ "[зҙӡउওਤ੩૩౩ဒვპੜკ] > 3;"
"[੫丩ã„] > 4;"
"[Õ³] > 6;"
"[৪੪୫] > 8;"
@@ -144,6 +148,16 @@ SkeletonGenerator::SkeletonGenerator(const USpoofChecker* checker)
// Characters that look like multiple characters.
character_map_[u'þ'] = {"b", "p"};
character_map_[u'Å“'] = {"ce", "oe"};
+ // https://crbug.com/1250993:
+ character_map_[u'Å‚'] = {"l", "t"};
+
+ // Find the characters with diacritics that have multiple skeletons.
+ for (const auto& it : character_map_) {
+ std::u16string char_str(1, it.first);
+ if (char_str != MaybeRemoveDiacritics(char_str)) {
+ characters_with_multiple_skeletons_with_diacritics_.insert(it.first);
+ }
+ }
}
SkeletonGenerator::~SkeletonGenerator() = default;
@@ -164,13 +178,41 @@ std::u16string SkeletonGenerator::MaybeRemoveDiacritics(
return base::i18n::UnicodeStringToString16(host);
}
+bool SkeletonGenerator::ShouldComputeSupplementalHostnamesWithDiacritics(
+ base::StringPiece16 input_hostname) const {
+ for (const char16_t c : characters_with_multiple_skeletons_with_diacritics_) {
+ if (input_hostname.find(c) != base::StringPiece16::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
Skeletons SkeletonGenerator::GetSkeletons(base::StringPiece16 input_hostname) {
+ // Generate supplemental hostnames for the input hostname with and without
+ // diacritics. We do this to cover characters whose diacritic versions can
+ // look like completely other characters, such as LATIN SMALL LETTER L WITH
+ // STROKE (Å‚) looking like t. By doing this, we can generate multiple
+ // skeletons for Å‚ (l and t).
+ //
+ // Ideally, we'd compute a hostname variant for each character with and
+ // without its diacritic. That would result in 2^n hostname variants where n
+ // is the number of characters in the hostname with diacritics, which is too
+ // expensive. Currently, there is only one character with a diacritic that has
+ // multiple skeletons (Å‚), so this isn't needed.
std::u16string hostname_no_diacritics = MaybeRemoveDiacritics(input_hostname);
+ base::flat_set<std::u16string> all_variants = GenerateSupplementalHostnames(
+ hostname_no_diacritics, kMaxSupplementalHostnames, character_map_);
+ if (ShouldComputeSupplementalHostnamesWithDiacritics(input_hostname)) {
+ base::flat_set<std::u16string> diacritic_variants =
+ GenerateSupplementalHostnames(input_hostname, kMaxSupplementalHostnames,
+ character_map_);
+ all_variants.insert(diacritic_variants.begin(), diacritic_variants.end());
+ }
- // Generate alternative versions of the input hostname and extract skeletons.
+ // Extract skeletons of all hostname variants.
Skeletons skeletons;
- for (const std::u16string& hostname : GenerateSupplementalHostnames(
- hostname_no_diacritics, kMaxSupplementalHostnames, character_map_)) {
+ for (const std::u16string& hostname : all_variants) {
size_t hostname_length =
hostname.length() - (hostname.back() == '.' ? 1 : 0);
icu::UnicodeString hostname_unicode(false, hostname.data(),
@@ -234,7 +276,9 @@ base::flat_set<std::u16string> SkeletonGenerator::GenerateSupplementalHostnames(
size_t max_alternatives,
const SkeletonMap& mapping) {
base::flat_set<std::u16string> output;
- if (!input.size() || max_alternatives == 0) {
+ if (!input.size() ||
+ input.size() > kMaxHostnameLengthToComputeSupplementalHostnames ||
+ max_alternatives == 0) {
return output;
}
icu::UnicodeString input_unicode =
diff --git a/chromium/components/url_formatter/spoof_checks/skeleton_generator.h b/chromium/components/url_formatter/spoof_checks/skeleton_generator.h
index 114371afc17..e3e42181aa0 100644
--- a/chromium/components/url_formatter/spoof_checks/skeleton_generator.h
+++ b/chromium/components/url_formatter/spoof_checks/skeleton_generator.h
@@ -93,6 +93,10 @@ class SkeletonGenerator {
int32_t mapped_char,
Skeletons* skeletons);
void MaybeRemoveDiacritics(icu::UnicodeString& hostname);
+ // Returns true if supplemental hostnames of `input_hostname` should be
+ // generated without removing its diacritics.
+ bool ShouldComputeSupplementalHostnamesWithDiacritics(
+ base::StringPiece16 input_hostname) const;
icu::UnicodeSet lgc_letters_n_ascii_;
@@ -101,6 +105,10 @@ class SkeletonGenerator {
// Map of characters to their skeletons. This map is manually curated.
std::map<char16_t, Skeletons> character_map_;
+ // Contains the characters from character_map_ that have diacritics. This is
+ // used to determine if we should compute supplemental hostnames for a
+ // hostname without removing its diacritics.
+ base::flat_set<char16_t> characters_with_multiple_skeletons_with_diacritics_;
raw_ptr<const USpoofChecker> checker_;
};
diff --git a/chromium/components/url_formatter/url_fixer.cc b/chromium/components/url_formatter/url_fixer.cc
index cc280a6e2a4..c74b5bf341f 100644
--- a/chromium/components/url_formatter/url_fixer.cc
+++ b/chromium/components/url_formatter/url_fixer.cc
@@ -12,13 +12,13 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/ranges/algorithm.h"
+#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/url_formatter/url_formatter.h"
-#include "net/base/escape.h"
#include "net/base/filename_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/third_party/mozilla/url_parse.h"
@@ -195,7 +195,7 @@ std::string FixupPath(const std::string& text) {
if (file_url.is_valid()) {
return base::UTF16ToUTF8(url_formatter::FormatUrl(
file_url, url_formatter::kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr));
+ base::UnescapeRule::NORMAL, nullptr, nullptr, nullptr));
}
// Invalid file URL, just return the input.
@@ -575,7 +575,7 @@ GURL FixupURL(const std::string& text, const std::string& desired_tld) {
// 'about:blank' and 'about:srcdoc' are special-cased in various places in the
// code and shouldn't use the chrome: scheme.
- if (base::LowerCaseEqualsASCII(scheme, url::kAboutScheme)) {
+ if (base::EqualsCaseInsensitiveASCII(scheme, url::kAboutScheme)) {
GURL about_url(base::ToLowerASCII(trimmed));
if (about_url.IsAboutBlank() || about_url.IsAboutSrcdoc())
return about_url;
@@ -650,13 +650,12 @@ GURL FixupRelativeFile(const base::FilePath& base_dir,
// Not a path as entered, try unescaping it in case the user has
// escaped things. We need to go through 8-bit since the escaped values
// only represent 8-bit values.
- std::string unescaped = net::UnescapeURLComponent(
- trimmed,
- net::UnescapeRule::SPACES | net::UnescapeRule::PATH_SEPARATORS |
- net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
+std::string unescaped = base::UnescapeURLComponent(
+ trimmed, base::UnescapeRule::SPACES | base::UnescapeRule::PATH_SEPARATORS |
+ base::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
- if (!ValidPathForFile(unescaped, &full_path))
- is_file = false;
+if (!ValidPathForFile(unescaped, &full_path))
+ is_file = false;
}
// Put back the current directory if we saved it.
@@ -668,7 +667,7 @@ GURL FixupRelativeFile(const base::FilePath& base_dir,
if (file_url.is_valid())
return GURL(base::UTF16ToUTF8(url_formatter::FormatUrl(
file_url, url_formatter::kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr)));
+ base::UnescapeRule::NORMAL, nullptr, nullptr, nullptr)));
// Invalid files fall through to regular processing.
}
diff --git a/chromium/components/url_formatter/url_formatter.cc b/chromium/components/url_formatter/url_formatter.cc
index db6a38b413e..52f4a40fb95 100644
--- a/chromium/components/url_formatter/url_formatter.cc
+++ b/chromium/components/url_formatter/url_formatter.cc
@@ -33,6 +33,8 @@ namespace {
const char kWww[] = "www.";
constexpr size_t kWwwLength = 4;
+const char kMobilePrefix[] = "m.";
+constexpr size_t kMobilePrefixLength = 2;
IDNConversionResult IDNToUnicodeWithAdjustments(
base::StringPiece host,
@@ -71,52 +73,91 @@ class AppendComponentTransform {
class HostComponentTransform : public AppendComponentTransform {
public:
- explicit HostComponentTransform(bool trim_trivial_subdomains)
- : trim_trivial_subdomains_(trim_trivial_subdomains) {}
+ HostComponentTransform(bool trim_trivial_subdomains, bool trim_mobile_prefix)
+ : trim_trivial_subdomains_(trim_trivial_subdomains),
+ trim_mobile_prefix_(trim_mobile_prefix) {}
private:
std::u16string Execute(
const std::string& component_text,
base::OffsetAdjuster::Adjustments* adjustments) const override {
- if (!trim_trivial_subdomains_)
+ // Nothing to change.
+ if (!trim_trivial_subdomains_ && !trim_mobile_prefix_)
return IDNToUnicodeWithAdjustments(component_text, adjustments).result;
- std::string www_stripped_component_text = StripWWW(component_text);
- // If StripWWW() did nothing, then "www." wasn't a prefix, or it otherwise
- // didn't meet conditions for stripping "www." (such as intranet hostnames).
- // In this case, no adjustments for trivial subdomains are needed.
- if (www_stripped_component_text == component_text)
+ std::string stripped_component_text = component_text;
+ if (base::StartsWith(component_text, "www.m.") &&
+ trim_trivial_subdomains_ && trim_mobile_prefix_) {
+ stripped_component_text = StripWWW(stripped_component_text);
+ stripped_component_text = StripMobilePrefix(stripped_component_text);
+ } else if (base::StartsWith(component_text, "m.www.") &&
+ trim_mobile_prefix_ && trim_trivial_subdomains_) {
+ stripped_component_text = StripMobilePrefix(stripped_component_text);
+ stripped_component_text = StripWWW(stripped_component_text);
+ } else {
+ if (trim_trivial_subdomains_) {
+ stripped_component_text = StripWWW(component_text);
+ }
+ if (trim_mobile_prefix_) {
+ stripped_component_text = StripMobilePrefix(stripped_component_text);
+ }
+ }
+
+ // If StripWWW() and StripMobilePrefix() did nothing, then "www." and "m."
+ // weren't a prefix, or it otherwise didn't meet conditions for stripping
+ // "www." (such as intranet hostnames). In this case, no adjustments for
+ // trivial subdomains are needed.
+ if (stripped_component_text == component_text)
return IDNToUnicodeWithAdjustments(component_text, adjustments).result;
- base::OffsetAdjuster::Adjustments trivial_subdomains_adjustments;
- trivial_subdomains_adjustments.push_back(
- base::OffsetAdjuster::Adjustment(0, kWwwLength, 0));
+
+ base::OffsetAdjuster::Adjustments offset_adjustments;
+
+ if (component_text.length() ==
+ stripped_component_text.length() + kMobilePrefixLength + kWwwLength) {
+ // Add www. and m. offsets.
+ offset_adjustments.push_back(
+ base::OffsetAdjuster::Adjustment(0, kWwwLength, 0));
+ offset_adjustments.push_back(
+ base::OffsetAdjuster::Adjustment(0, kMobilePrefixLength, 0));
+ } else if (component_text.length() ==
+ stripped_component_text.length() + kWwwLength) {
+ // Add www. offset.
+ offset_adjustments.push_back(
+ base::OffsetAdjuster::Adjustment(0, kWwwLength, 0));
+ } else if (component_text.length() ==
+ stripped_component_text.length() + kMobilePrefixLength) {
+ // Add m. offset
+ offset_adjustments.push_back(
+ base::OffsetAdjuster::Adjustment(0, kMobilePrefixLength, 0));
+ }
std::u16string unicode_result =
- IDNToUnicodeWithAdjustments(www_stripped_component_text, adjustments)
+ IDNToUnicodeWithAdjustments(stripped_component_text, adjustments)
.result;
- base::OffsetAdjuster::MergeSequentialAdjustments(
- trivial_subdomains_adjustments, adjustments);
+ base::OffsetAdjuster::MergeSequentialAdjustments(offset_adjustments,
+ adjustments);
return unicode_result;
}
bool trim_trivial_subdomains_;
+ bool trim_mobile_prefix_;
};
class NonHostComponentTransform : public AppendComponentTransform {
public:
- explicit NonHostComponentTransform(net::UnescapeRule::Type unescape_rules)
+ explicit NonHostComponentTransform(base::UnescapeRule::Type unescape_rules)
: unescape_rules_(unescape_rules) {}
private:
std::u16string Execute(
const std::string& component_text,
base::OffsetAdjuster::Adjustments* adjustments) const override {
- return (unescape_rules_ == net::UnescapeRule::NONE)
+ return (unescape_rules_ == base::UnescapeRule::NONE)
? base::UTF8ToUTF16WithAdjustments(component_text, adjustments)
- : net::UnescapeAndDecodeUTF8URLComponentWithAdjustments(
+ : base::UnescapeAndDecodeUTF8URLComponentWithAdjustments(
component_text, unescape_rules_, adjustments);
}
- const net::UnescapeRule::Type unescape_rules_;
+ const base::UnescapeRule::Type unescape_rules_;
};
// Transforms the portion of |spec| covered by |original_component| according to
@@ -192,7 +233,7 @@ void AdjustAllComponentsButScheme(int delta, url::Parsed* parsed) {
std::u16string FormatViewSourceUrl(
const GURL& url,
FormatUrlTypes format_types,
- net::UnescapeRule::Type unescape_rules,
+ base::UnescapeRule::Type unescape_rules,
url::Parsed* new_parsed,
size_t* prefix_end,
base::OffsetAdjuster::Adjustments* adjustments) {
@@ -500,6 +541,7 @@ const FormatUrlType kFormatUrlOmitTrivialSubdomains = 1 << 5;
const FormatUrlType kFormatUrlTrimAfterHost = 1 << 6;
const FormatUrlType kFormatUrlOmitFileScheme = 1 << 7;
const FormatUrlType kFormatUrlOmitMailToScheme = 1 << 8;
+const FormatUrlType kFormatUrlOmitMobilePrefix = 1 << 9;
const FormatUrlType kFormatUrlOmitDefaults =
kFormatUrlOmitUsernamePassword | kFormatUrlOmitHTTP |
@@ -507,7 +549,7 @@ const FormatUrlType kFormatUrlOmitDefaults =
std::u16string FormatUrl(const GURL& url,
FormatUrlTypes format_types,
- net::UnescapeRule::Type unescape_rules,
+ base::UnescapeRule::Type unescape_rules,
url::Parsed* new_parsed,
size_t* prefix_end,
size_t* offset_for_adjustment) {
@@ -524,7 +566,7 @@ std::u16string FormatUrl(const GURL& url,
std::u16string FormatUrlWithOffsets(
const GURL& url,
FormatUrlTypes format_types,
- net::UnescapeRule::Type unescape_rules,
+ base::UnescapeRule::Type unescape_rules,
url::Parsed* new_parsed,
size_t* prefix_end,
std::vector<size_t>* offsets_for_adjustment) {
@@ -539,7 +581,7 @@ std::u16string FormatUrlWithOffsets(
std::u16string FormatUrlWithAdjustments(
const GURL& url,
FormatUrlTypes format_types,
- net::UnescapeRule::Type unescape_rules,
+ base::UnescapeRule::Type unescape_rules,
url::Parsed* new_parsed,
size_t* prefix_end,
base::OffsetAdjuster::Adjustments* adjustments) {
@@ -618,9 +660,11 @@ std::u16string FormatUrlWithAdjustments(
// Host.
bool trim_trivial_subdomains =
(format_types & kFormatUrlOmitTrivialSubdomains) != 0;
- AppendFormattedComponent(spec, parsed.host,
- HostComponentTransform(trim_trivial_subdomains),
- &url_string, &new_parsed->host, adjustments);
+ bool trim_mobile_prefix = (format_types & kFormatUrlOmitMobilePrefix) != 0;
+ AppendFormattedComponent(
+ spec, parsed.host,
+ HostComponentTransform(trim_trivial_subdomains, trim_mobile_prefix),
+ &url_string, &new_parsed->host, adjustments);
// Port.
if (parsed.port.is_nonempty()) {
@@ -741,7 +785,7 @@ bool CanStripTrailingSlash(const GURL& url) {
void AppendFormattedHost(const GURL& url, std::u16string* output) {
AppendFormattedComponent(
url.possibly_invalid_spec(), url.parsed_for_possibly_invalid_spec().host,
- HostComponentTransform(false), output, nullptr, nullptr);
+ HostComponentTransform(false, false), output, nullptr, nullptr);
}
IDNConversionResult UnsafeIDNToUnicodeWithDetails(base::StringPiece host) {
@@ -775,6 +819,14 @@ void StripWWWFromHostComponent(const std::string& url, url::Component* host) {
host->len -= kWwwLength;
}
+std::string StripMobilePrefix(const std::string& text) {
+ return text.size() >= kMobilePrefixLength &&
+ base::StartsWith(text, kMobilePrefix,
+ base::CompareCase::SENSITIVE)
+ ? text.substr(kMobilePrefixLength)
+ : text;
+}
+
Skeletons GetSkeletons(const std::u16string& host) {
return g_idn_spoof_checker.Get().GetSkeletons(host);
}
diff --git a/chromium/components/url_formatter/url_formatter.h b/chromium/components/url_formatter/url_formatter.h
index 5c8f693bce1..94582eec1ee 100644
--- a/chromium/components/url_formatter/url_formatter.h
+++ b/chromium/components/url_formatter/url_formatter.h
@@ -22,10 +22,10 @@
#include <vector>
#include "base/containers/flat_set.h"
+#include "base/strings/escape.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_offset_string_conversions.h"
#include "components/url_formatter/spoof_checks/idn_spoof_checker.h"
-#include "net/base/escape.h"
class GURL;
@@ -80,7 +80,7 @@ extern const FormatUrlType kFormatUrlOmitTrailingSlashOnBareHostname;
// If the scheme is 'https://', it's removed. Not in kFormatUrlOmitDefaults.
extern const FormatUrlType kFormatUrlOmitHTTPS;
-// Omits some trivially informative subdomains such as "www" or "m". Not in
+// Omits some trivially informative subdomains such as "www". Not in
// kFormatUrlOmitDefaults.
extern const FormatUrlType kFormatUrlOmitTrivialSubdomains;
@@ -94,6 +94,9 @@ extern const FormatUrlType kFormatUrlOmitFileScheme;
// If the scheme is 'mailto:', it's removed. Not in kFormatUrlOmitDefaults.
extern const FormatUrlType kFormatUrlOmitMailToScheme;
+// Omits the mobile prefix "m". Not in kFormatUrlOmitDefaults.
+extern const FormatUrlType kFormatUrlOmitMobilePrefix;
+
// Convenience for omitting all unnecessary types. Does not include HTTPS scheme
// removal, or experimental flags.
extern const FormatUrlType kFormatUrlOmitDefaults;
@@ -131,7 +134,7 @@ extern const FormatUrlType kFormatUrlOmitDefaults;
// components are adjacent.
std::u16string FormatUrl(const GURL& url,
FormatUrlTypes format_types,
- net::UnescapeRule::Type unescape_rules,
+ base::UnescapeRule::Type unescape_rules,
url::Parsed* new_parsed,
size_t* prefix_end,
size_t* offset_for_adjustment);
@@ -139,7 +142,7 @@ std::u16string FormatUrl(const GURL& url,
std::u16string FormatUrlWithOffsets(
const GURL& url,
FormatUrlTypes format_types,
- net::UnescapeRule::Type unescape_rules,
+ base::UnescapeRule::Type unescape_rules,
url::Parsed* new_parsed,
size_t* prefix_end,
std::vector<size_t>* offsets_for_adjustment);
@@ -151,7 +154,7 @@ std::u16string FormatUrlWithOffsets(
std::u16string FormatUrlWithAdjustments(
const GURL& url,
FormatUrlTypes format_types,
- net::UnescapeRule::Type unescape_rules,
+ base::UnescapeRule::Type unescape_rules,
url::Parsed* new_parsed,
size_t* prefix_end,
base::OffsetAdjuster::Adjustments* adjustments);
@@ -162,7 +165,7 @@ std::u16string FormatUrlWithAdjustments(
// cautious about using this for URLs which will be parsed or sent to other
// applications.
inline std::u16string FormatUrl(const GURL& url) {
- return FormatUrl(url, kFormatUrlOmitDefaults, net::UnescapeRule::SPACES,
+ return FormatUrl(url, kFormatUrlOmitDefaults, base::UnescapeRule::SPACES,
nullptr, nullptr, nullptr);
}
@@ -192,6 +195,9 @@ IDNConversionResult UnsafeIDNToUnicodeWithDetails(base::StringPiece host);
// hostname, and if "www." is part of the subdomain (not the eTLD+1).
std::string StripWWW(const std::string& host);
+// Strips a "m." prefix from |host| if present.
+std::string StripMobilePrefix(const std::string& text);
+
// If the |host| component of |url| begins with a "www." prefix (and meets the
// conditions described for StripWWW), then updates |host| to strip the "www."
// prefix.
diff --git a/chromium/components/url_formatter/url_formatter_android.cc b/chromium/components/url_formatter/url_formatter_android.cc
index a80f933d06b..3feb035a278 100644
--- a/chromium/components/url_formatter/url_formatter_android.cc
+++ b/chromium/components/url_formatter/url_formatter_android.cc
@@ -48,7 +48,7 @@ JNI_UrlFormatter_FormatUrlForDisplayOmitScheme(
JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
url_formatter::kFormatUrlOmitDefaults |
url_formatter::kFormatUrlOmitHTTPS,
- net::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
+ base::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
}
static ScopedJavaLocalRef<jstring>
@@ -58,8 +58,8 @@ JNI_UrlFormatter_FormatUrlForDisplayOmitHTTPScheme(
return base::android::ConvertUTF16ToJavaString(
env, url_formatter::FormatUrl(
JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
- url_formatter::kFormatUrlOmitDefaults, net::UnescapeRule::SPACES,
- nullptr, nullptr, nullptr));
+ url_formatter::kFormatUrlOmitDefaults,
+ base::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
}
static ScopedJavaLocalRef<jstring>
@@ -71,7 +71,7 @@ JNI_UrlFormatter_FormatUrlForDisplayOmitUsernamePassword(
JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
url_formatter::kFormatUrlOmitUsernamePassword |
kFormatUrlOmitTrailingSlashOnBareHostname,
- net::UnescapeRule::NONE, nullptr, nullptr, nullptr));
+ base::UnescapeRule::NONE, nullptr, nullptr, nullptr));
}
static ScopedJavaLocalRef<jstring> JNI_UrlFormatter_FormatUrlForCopy(
@@ -80,7 +80,7 @@ static ScopedJavaLocalRef<jstring> JNI_UrlFormatter_FormatUrlForCopy(
return base::android::ConvertUTF16ToJavaString(
env, url_formatter::FormatUrl(
JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
- url_formatter::kFormatUrlOmitNothing, net::UnescapeRule::NORMAL,
+ url_formatter::kFormatUrlOmitNothing, base::UnescapeRule::NORMAL,
nullptr, nullptr, nullptr));
}
@@ -128,7 +128,7 @@ JNI_UrlFormatter_FormatUrlForDisplayOmitSchemeOmitTrivialSubdomains(
url_formatter::kFormatUrlOmitDefaults |
url_formatter::kFormatUrlOmitHTTPS |
url_formatter::kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
+ base::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
}
static ScopedJavaLocalRef<jstring>
diff --git a/chromium/components/url_formatter/url_formatter_unittest.cc b/chromium/components/url_formatter/url_formatter_unittest.cc
index 2d3ef3ab277..fc6e0b8cf95 100644
--- a/chromium/components/url_formatter/url_formatter_unittest.cc
+++ b/chromium/components/url_formatter/url_formatter_unittest.cc
@@ -35,7 +35,7 @@ struct UrlTestData {
const char* const description;
const char* const input;
FormatUrlTypes format_types;
- net::UnescapeRule::Type escape_rules;
+ base::UnescapeRule::Type escape_rules;
const wchar_t* output; // Use |wchar_t| to handle Unicode constants easily.
size_t prefix_len;
};
@@ -52,7 +52,7 @@ void VerboseExpect(size_t expected,
void CheckAdjustedOffsets(const std::string& url_string,
FormatUrlTypes format_types,
- net::UnescapeRule::Type unescape_rules,
+ base::UnescapeRule::Type unescape_rules,
const size_t* output_offsets) {
GURL url(url_string);
size_t url_length = url_string.length();
@@ -79,53 +79,53 @@ TEST(UrlFormatterTest, FormatUrl) {
FormatUrlTypes default_format_type = kFormatUrlOmitUsernamePassword;
// clang-format off
const UrlTestData tests[] = {
- {"Empty URL", "", default_format_type, net::UnescapeRule::NORMAL, L"", 0},
+ {"Empty URL", "", default_format_type, base::UnescapeRule::NORMAL, L"", 0},
{"Simple URL", "http://www.google.com/", default_format_type,
- net::UnescapeRule::NORMAL, L"http://www.google.com/", 7},
+ base::UnescapeRule::NORMAL, L"http://www.google.com/", 7},
{"With a port number and a reference",
"http://www.google.com:8080/#\xE3\x82\xB0", default_format_type,
- net::UnescapeRule::NORMAL, L"http://www.google.com:8080/#\x30B0", 7},
+ base::UnescapeRule::NORMAL, L"http://www.google.com:8080/#\x30B0", 7},
// -------- IDN tests --------
{"Japanese IDN with ja", "http://xn--l8jvb1ey91xtjb.jp",
- default_format_type, net::UnescapeRule::NORMAL,
+ default_format_type, base::UnescapeRule::NORMAL,
L"http://\x671d\x65e5\x3042\x3055\x3072.jp/", 7},
{"mailto: with Japanese IDN", "mailto:foo@xn--l8jvb1ey91xtjb.jp",
- default_format_type, net::UnescapeRule::NORMAL,
+ default_format_type, base::UnescapeRule::NORMAL,
// GURL doesn't assume an email address's domain part as a host name.
L"mailto:foo@xn--l8jvb1ey91xtjb.jp", 7},
{"file: with Japanese IDN", "file://xn--l8jvb1ey91xtjb.jp/config.sys",
- default_format_type, net::UnescapeRule::NORMAL,
+ default_format_type, base::UnescapeRule::NORMAL,
L"file://\x671d\x65e5\x3042\x3055\x3072.jp/config.sys", 7},
{"ftp: with Japanese IDN", "ftp://xn--l8jvb1ey91xtjb.jp/config.sys",
- default_format_type, net::UnescapeRule::NORMAL,
+ default_format_type, base::UnescapeRule::NORMAL,
L"ftp://\x671d\x65e5\x3042\x3055\x3072.jp/config.sys", 6},
// -------- omit_username_password flag tests --------
{"With username and password, omit_username_password=false",
"http://user:passwd@example.com/foo", kFormatUrlOmitNothing,
- net::UnescapeRule::NORMAL, L"http://user:passwd@example.com/foo", 19},
+ base::UnescapeRule::NORMAL, L"http://user:passwd@example.com/foo", 19},
{"With username and password, omit_username_password=true",
"http://user:passwd@example.com/foo", default_format_type,
- net::UnescapeRule::NORMAL, L"http://example.com/foo", 7},
+ base::UnescapeRule::NORMAL, L"http://example.com/foo", 7},
{"With username and no password", "http://user@example.com/foo",
- default_format_type, net::UnescapeRule::NORMAL,
+ default_format_type, base::UnescapeRule::NORMAL,
L"http://example.com/foo", 7},
{"Just '@' without username and password", "http://@example.com/foo",
- default_format_type, net::UnescapeRule::NORMAL,
+ default_format_type, base::UnescapeRule::NORMAL,
L"http://example.com/foo", 7},
// GURL doesn't think local-part of an email address is username for URL.
{"mailto:, omit_username_password=true", "mailto:foo@example.com",
- default_format_type, net::UnescapeRule::NORMAL,
+ default_format_type, base::UnescapeRule::NORMAL,
L"mailto:foo@example.com", 7},
// -------- unescape flag tests --------
@@ -133,7 +133,7 @@ TEST(UrlFormatterTest, FormatUrl) {
"http://%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB.jp/"
"%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
"?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB",
- default_format_type, net::UnescapeRule::NONE,
+ default_format_type, base::UnescapeRule::NONE,
// GURL parses %-encoded hostnames into Punycode.
L"http://\x30B0\x30FC\x30B0\x30EB.jp/"
L"%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
@@ -144,240 +144,281 @@ TEST(UrlFormatterTest, FormatUrl) {
"http://%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB.jp/"
"%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
"?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB",
- default_format_type, net::UnescapeRule::NORMAL,
+ default_format_type, base::UnescapeRule::NORMAL,
L"http://\x30B0\x30FC\x30B0\x30EB.jp/\x30B0\x30FC\x30B0\x30EB"
L"?q=\x30B0\x30FC\x30B0\x30EB",
7},
{"Unescape normally with BiDi control character",
"http://example.com/%E2%80%AEabc?q=%E2%80%8Fxy", default_format_type,
- net::UnescapeRule::NORMAL,
+ base::UnescapeRule::NORMAL,
L"http://example.com/%E2%80%AEabc?q=%E2%80%8Fxy", 7},
{"Unescape normally including unescape spaces",
"http://www.google.com/search?q=Hello%20World", default_format_type,
- net::UnescapeRule::SPACES, L"http://www.google.com/search?q=Hello World",
+ base::UnescapeRule::SPACES, L"http://www.google.com/search?q=Hello World",
7},
/*
{"unescape=true with some special characters",
"http://user%3A:%40passwd@example.com/foo%3Fbar?q=b%26z",
- kFormatUrlOmitNothing, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitNothing, base::UnescapeRule::NORMAL,
L"http://user%3A:%40passwd@example.com/foo%3Fbar?q=b%26z", 25},
*/
// Disabled: the resultant URL becomes "...user%253A:%2540passwd...".
// -------- omit http: --------
{"omit http", "http://www.google.com/", kFormatUrlOmitHTTP,
- net::UnescapeRule::NORMAL, L"www.google.com/", 0},
+ base::UnescapeRule::NORMAL, L"www.google.com/", 0},
{"omit http on bare scheme", "http://", kFormatUrlOmitDefaults,
- net::UnescapeRule::NORMAL, L"", 0},
+ base::UnescapeRule::NORMAL, L"", 0},
{"omit http with user name", "http://user@example.com/foo",
- kFormatUrlOmitDefaults, net::UnescapeRule::NORMAL, L"example.com/foo",
+ kFormatUrlOmitDefaults, base::UnescapeRule::NORMAL, L"example.com/foo",
0},
{"omit http with https", "https://www.google.com/", kFormatUrlOmitHTTP,
- net::UnescapeRule::NORMAL, L"https://www.google.com/", 8},
+ base::UnescapeRule::NORMAL, L"https://www.google.com/", 8},
{"omit http starts with ftp.", "http://ftp.google.com/",
- kFormatUrlOmitHTTP, net::UnescapeRule::NORMAL, L"http://ftp.google.com/",
+ kFormatUrlOmitHTTP, base::UnescapeRule::NORMAL, L"http://ftp.google.com/",
7},
// -------- omit file: --------
#if BUILDFLAG(IS_WIN)
{"omit file on Windows", "file:///C:/Users/homedirname/folder/file.pdf/",
- kFormatUrlOmitFileScheme, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitFileScheme, base::UnescapeRule::NORMAL,
L"C:/Users/homedirname/folder/file.pdf/", static_cast<size_t>(-1)},
#else
{"omit file", "file:///Users/homedirname/folder/file.pdf/",
- kFormatUrlOmitFileScheme, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitFileScheme, base::UnescapeRule::NORMAL,
L"/Users/homedirname/folder/file.pdf/", 0},
#endif
// -------- omit mailto: --------
{ "omit mailto", "mailto:foo@bar.com",
- kFormatUrlOmitMailToScheme, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitMailToScheme, base::UnescapeRule::NORMAL,
L"foo@bar.com", 0 },
// -------- omit trailing slash on bare hostname --------
{"omit slash when it's the entire path", "http://www.google.com/",
- kFormatUrlOmitTrailingSlashOnBareHostname, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitTrailingSlashOnBareHostname, base::UnescapeRule::NORMAL,
L"http://www.google.com", 7},
{"omit slash when there's a ref", "http://www.google.com/#ref",
- kFormatUrlOmitTrailingSlashOnBareHostname, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitTrailingSlashOnBareHostname, base::UnescapeRule::NORMAL,
L"http://www.google.com/#ref", 7},
{"omit slash when there's a query", "http://www.google.com/?",
- kFormatUrlOmitTrailingSlashOnBareHostname, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitTrailingSlashOnBareHostname, base::UnescapeRule::NORMAL,
L"http://www.google.com/?", 7},
{"omit slash when it's not the entire path", "http://www.google.com/foo",
- kFormatUrlOmitTrailingSlashOnBareHostname, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitTrailingSlashOnBareHostname, base::UnescapeRule::NORMAL,
L"http://www.google.com/foo", 7},
{"omit slash for nonstandard URLs", "data:/",
- kFormatUrlOmitTrailingSlashOnBareHostname, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitTrailingSlashOnBareHostname, base::UnescapeRule::NORMAL,
L"data:/", 5},
{"omit slash for file URLs", "file:///",
- kFormatUrlOmitTrailingSlashOnBareHostname, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitTrailingSlashOnBareHostname, base::UnescapeRule::NORMAL,
L"file:///", 7},
// -------- view-source: --------
{"view-source", "view-source:http://xn--qcka1pmc.jp/",
- default_format_type, net::UnescapeRule::NORMAL,
+ default_format_type, base::UnescapeRule::NORMAL,
L"view-source:http://\x30B0\x30FC\x30B0\x30EB.jp/", 19},
{"view-source of view-source",
"view-source:view-source:http://xn--qcka1pmc.jp/", default_format_type,
- net::UnescapeRule::NORMAL,
+ base::UnescapeRule::NORMAL,
L"view-source:view-source:http://xn--qcka1pmc.jp/", 12},
// view-source should omit http and trailing slash where non-view-source
// would.
{"view-source omit http", "view-source:http://a.b/c",
- kFormatUrlOmitDefaults, net::UnescapeRule::NORMAL, L"view-source:a.b/c",
+ kFormatUrlOmitDefaults, base::UnescapeRule::NORMAL, L"view-source:a.b/c",
12},
{"view-source omit http starts with ftp.", "view-source:http://ftp.b/c",
- kFormatUrlOmitDefaults, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitDefaults, base::UnescapeRule::NORMAL,
L"view-source:http://ftp.b/c", 19},
{"view-source omit slash when it's the entire path",
"view-source:http://a.b/", kFormatUrlOmitDefaults,
- net::UnescapeRule::NORMAL, L"view-source:a.b", 12},
+ base::UnescapeRule::NORMAL, L"view-source:a.b", 12},
{"view-source never applies destructive elisions to its inner URL",
"view-source:https://www.google.com/foo",
kFormatUrlOmitDefaults | kFormatUrlOmitHTTPS |
kFormatUrlOmitTrivialSubdomains | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"view-source:https://www.google.com/foo",
+ base::UnescapeRule::NORMAL, L"view-source:https://www.google.com/foo",
20},
#if BUILDFLAG(IS_WIN)
{"view-source should not omit file on Windows",
"view-source:file:///C:/Users/homedirname/folder/file.pdf/",
kFormatUrlOmitDefaults | kFormatUrlOmitFileScheme,
- net::UnescapeRule::NORMAL,
+ base::UnescapeRule::NORMAL,
L"view-source:file:///C:/Users/homedirname/folder/file.pdf/", 19},
#else
{"view-source should not omit file",
"view-source:file:///Users/homedirname/folder/file.pdf/",
kFormatUrlOmitDefaults | kFormatUrlOmitFileScheme,
- net::UnescapeRule::NORMAL,
+ base::UnescapeRule::NORMAL,
L"view-source:file:///Users/homedirname/folder/file.pdf/", 19},
#endif
// -------- omit https --------
{"omit https", "https://www.google.com/", kFormatUrlOmitHTTPS,
- net::UnescapeRule::NORMAL, L"www.google.com/", 0},
+ base::UnescapeRule::NORMAL, L"www.google.com/", 0},
{"omit https but do not omit http", "http://www.google.com/",
- kFormatUrlOmitHTTPS, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitHTTPS, base::UnescapeRule::NORMAL,
L"http://www.google.com/", 7},
{"omit https, username, and password",
"https://user:password@example.com/foo",
- kFormatUrlOmitDefaults | kFormatUrlOmitHTTPS, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitDefaults | kFormatUrlOmitHTTPS, base::UnescapeRule::NORMAL,
L"example.com/foo", 0},
{"omit https, but preserve user name and password",
"https://user:password@example.com/foo", kFormatUrlOmitHTTPS,
- net::UnescapeRule::NORMAL, L"user:password@example.com/foo", 14},
+ base::UnescapeRule::NORMAL, L"user:password@example.com/foo", 14},
{"omit https should not affect hosts starting with ftp.",
"https://ftp.google.com/", kFormatUrlOmitHTTP | kFormatUrlOmitHTTPS,
- net::UnescapeRule::NORMAL, L"https://ftp.google.com/", 8},
+ base::UnescapeRule::NORMAL, L"https://ftp.google.com/", 8},
// -------- omit trivial subdomains --------
{"omit trivial subdomains - trim leading www",
"http://www.wikipedia.org/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://wikipedia.org/", 7},
+ base::UnescapeRule::NORMAL, L"http://wikipedia.org/", 7},
{"omit trivial subdomains - don't trim leading m",
"http://m.google.com/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://m.google.com/", 7},
+ base::UnescapeRule::NORMAL, L"http://m.google.com/", 7},
{"omit trivial subdomains - don't trim www after a leading m",
"http://m.www.google.com/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://m.www.google.com/", 7},
+ base::UnescapeRule::NORMAL, L"http://m.www.google.com/", 7},
{"omit trivial subdomains - trim first www only",
"http://www.www.www.wikipedia.org/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://www.www.wikipedia.org/", 7},
+ base::UnescapeRule::NORMAL, L"http://www.www.wikipedia.org/", 7},
{"omit trivial subdomains - don't trim www from middle",
"http://en.www.wikipedia.org/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://en.www.wikipedia.org/", 7},
+ base::UnescapeRule::NORMAL, L"http://en.www.wikipedia.org/", 7},
{"omit trivial subdomains - don't do blind substring matches for www",
"http://foowww.google.com/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://foowww.google.com/", 7},
+ base::UnescapeRule::NORMAL, L"http://foowww.google.com/", 7},
{"omit trivial subdomains - don't crash on multiple delimiters",
"http://www....foobar...google.com/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://...foobar...google.com/", 7},
+ base::UnescapeRule::NORMAL, L"http://...foobar...google.com/", 7},
{"omit trivial subdomains - sanity check for ordinary subdomains",
"http://mail.yahoo.com/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://mail.yahoo.com/", 7},
+ base::UnescapeRule::NORMAL, L"http://mail.yahoo.com/", 7},
{"omit trivial subdomains - sanity check for auth",
"http://www:m@google.com/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://www:m@google.com/", 13},
+ base::UnescapeRule::NORMAL, L"http://www:m@google.com/", 13},
{"omit trivial subdomains - sanity check for path",
"http://google.com/www.m.foobar", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://google.com/www.m.foobar", 7},
+ base::UnescapeRule::NORMAL, L"http://google.com/www.m.foobar", 7},
{"omit trivial subdomains - sanity check for IDN",
"http://www.xn--cy2a840a.www.xn--cy2a840a.com",
- kFormatUrlOmitTrivialSubdomains, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitTrivialSubdomains, base::UnescapeRule::NORMAL,
L"http://\x89c6\x9891.www.\x89c6\x9891.com/", 7},
{"omit trivial subdomains but leave registry and domain alone - trivial",
"http://google.com/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://google.com/", 7},
+ base::UnescapeRule::NORMAL, L"http://google.com/", 7},
{"omit trivial subdomains but leave registry and domain alone - www",
"http://www.com/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://www.com/", 7},
+ base::UnescapeRule::NORMAL, L"http://www.com/", 7},
{"omit trivial subdomains but leave registry and domain alone - co.uk",
"http://m.co.uk/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://m.co.uk/", 7},
+ base::UnescapeRule::NORMAL, L"http://m.co.uk/", 7},
{"omit trivial subdomains but leave eTLD (effective TLD) alone",
"http://www.appspot.com/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://www.appspot.com/", 7},
+ base::UnescapeRule::NORMAL, L"http://www.appspot.com/", 7},
{"omit trivial subdomains but leave intranet hostnames alone",
"http://router/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://router/", 7},
+ base::UnescapeRule::NORMAL, L"http://router/", 7},
{"omit trivial subdomains but leave alone if host itself is a registry",
"http://co.uk/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, L"http://co.uk/", 7},
+ base::UnescapeRule::NORMAL, L"http://co.uk/", 7},
+
+#if BUILDFLAG(IS_IOS)
+ // -------- omit mobile prefix --------
+ {"omit mobile prefix - trim leading m.",
+ "http://m.wikipedia.org/", kFormatUrlOmitMobilePrefix,
+ base::UnescapeRule::NORMAL, L"http://wikipedia.org/", 7},
+ {"omit mobile prefix - trim leading m., but don't trim www",
+ "http://m.www.google.com/", kFormatUrlOmitMobilePrefix,
+ base::UnescapeRule::NORMAL, L"http://www.google.com/", 7},
+ {"omit mobile prefix - trim first m. only",
+ "http://m.m.m.wikipedia.org/", kFormatUrlOmitMobilePrefix,
+ base::UnescapeRule::NORMAL, L"http://m.m.wikipedia.org/", 7},
+ {"omit mobile prefix - don't trim m. from middle",
+ "http://en.m.wikipedia.org/", kFormatUrlOmitMobilePrefix,
+ base::UnescapeRule::NORMAL, L"http://en.m.wikipedia.org/", 7},
+ {"omit mobile prefix - don't do blind substring matches for m.",
+ "http://foom.google.com/", kFormatUrlOmitMobilePrefix,
+ base::UnescapeRule::NORMAL, L"http://foom.google.com/", 7},
+ {"omit mobile prefix - don't crash on multiple delimiters",
+ "http://m....foobar...google.com/", kFormatUrlOmitMobilePrefix,
+ base::UnescapeRule::NORMAL, L"http://...foobar...google.com/", 7},
+ {"omit mobile prefix - sanity check for ordinary subdomains",
+ "http://mail.yahoo.com/", kFormatUrlOmitMobilePrefix,
+ base::UnescapeRule::NORMAL, L"http://mail.yahoo.com/", 7},
+ {"omit mobile prefix - sanity check for path",
+ "http://google.com/www.m.foobar", kFormatUrlOmitMobilePrefix,
+ base::UnescapeRule::NORMAL, L"http://google.com/www.m.foobar", 7},
+
+ // -------- omit mobile prefix and trivial subdomains --------
+ {"omit mobile prefix and trivial subdomains - trim leading m. and www.",
+ "http://m.www.wikipedia.org/", kFormatUrlOmitMobilePrefix | kFormatUrlOmitTrivialSubdomains,
+ base::UnescapeRule::NORMAL, L"http://wikipedia.org/", 7},
+ {"omit mobile prefix and trivial subdomains - trim leading m., "
+ "but don't trim www", "http://m.wwwwikipedia.org/",
+ kFormatUrlOmitMobilePrefix | kFormatUrlOmitTrivialSubdomains,
+ base::UnescapeRule::NORMAL, L"http://wwwwikipedia.org/", 7},
+ {"omit mobile prefix and trivial subdomains - trim leading www. and m.",
+ "http://www.m.wikipedia.org/", kFormatUrlOmitMobilePrefix |
+ kFormatUrlOmitTrivialSubdomains, base::UnescapeRule::NORMAL,
+ L"http://wikipedia.org/", 7},
+#endif
// -------- trim after host --------
{"omit the trailing slash when ommitting the path", "http://google.com/",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"omit the simple file path when ommitting the path",
"http://google.com/foo",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"omit the file and folder path when ommitting the path",
"http://google.com/ab/cd",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"omit everything after host with query only",
"http://google.com/?foo=bar",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"omit everything after host with ref only", "http://google.com/#foobar",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"omit everything after host with path and query only",
"http://google.com/foo?a=b",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"omit everything after host with path and ref only",
"http://google.com/foo#c",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"omit everything after host with query and ref only",
"http://google.com/?a=b#c",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"omit everything after host with path, query and ref",
"http://google.com/foo?a=b#c",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"omit everything after host with repeated delimiters (sanity check)",
"http://google.com////???####",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, L"google.com", 0},
+ base::UnescapeRule::NORMAL, L"google.com", 0},
{"never trim file paths", "file:///Users/homedirname/folder/file.pdf/",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL,
+ base::UnescapeRule::NORMAL,
L"file:///Users/homedirname/folder/file.pdf/", 7},
};
// clang-format on
@@ -398,7 +439,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) {
std::u16string formatted =
FormatUrl(GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
"%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
- kFormatUrlOmitNothing, net::UnescapeRule::NONE, &parsed,
+ kFormatUrlOmitNothing, base::UnescapeRule::NONE, &parsed,
nullptr, nullptr);
EXPECT_EQ(
u"http://%E3%82%B0:%E3%83%BC@\x30B0\x30FC\x30B0\x30EB.jp:8080"
@@ -421,7 +462,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) {
formatted =
FormatUrl(GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
"%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
- kFormatUrlOmitNothing, net::UnescapeRule::NORMAL, &parsed,
+ kFormatUrlOmitNothing, base::UnescapeRule::NORMAL, &parsed,
nullptr, nullptr);
EXPECT_EQ(
u"http://\x30B0:\x30FC@\x30B0\x30FC\x30B0\x30EB.jp:8080"
@@ -443,7 +484,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) {
formatted =
FormatUrl(GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
"%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
- kFormatUrlOmitUsernamePassword, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitUsernamePassword, base::UnescapeRule::NORMAL,
&parsed, nullptr, nullptr);
EXPECT_EQ(
u"http://\x30B0\x30FC\x30B0\x30EB.jp:8080"
@@ -462,7 +503,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) {
// View-source case.
formatted =
FormatUrl(GURL("view-source:http://user:passwd@host:81/path?query#ref"),
- kFormatUrlOmitUsernamePassword, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitUsernamePassword, base::UnescapeRule::NORMAL,
&parsed, nullptr, nullptr);
EXPECT_EQ(u"view-source:http://host:81/path?query#ref", formatted);
EXPECT_EQ(u"view-source:http",
@@ -479,7 +520,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) {
formatted = FormatUrl(
GURL(
"view-source: view-source:http://user:passwd@host:81/path?query#ref"),
- kFormatUrlOmitUsernamePassword, net::UnescapeRule::NORMAL, &parsed,
+ kFormatUrlOmitUsernamePassword, base::UnescapeRule::NORMAL, &parsed,
nullptr, nullptr);
EXPECT_EQ(
u"view-source: view-source:http://user:passwd@host:81/path?query#ref",
@@ -497,7 +538,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) {
// omit http case.
formatted = FormatUrl(GURL("http://host:8000/a?b=c#d"), kFormatUrlOmitHTTP,
- net::UnescapeRule::NORMAL, &parsed, nullptr, nullptr);
+ base::UnescapeRule::NORMAL, &parsed, nullptr, nullptr);
EXPECT_EQ(u"host:8000/a?b=c#d", formatted);
EXPECT_FALSE(parsed.scheme.is_valid());
EXPECT_FALSE(parsed.username.is_valid());
@@ -509,9 +550,9 @@ TEST(UrlFormatterTest, FormatUrlParsed) {
EXPECT_EQ(u"d", formatted.substr(parsed.ref.begin, parsed.ref.len));
// omit http starts with ftp case.
- formatted = FormatUrl(GURL("http://ftp.host:8000/a?b=c#d"),
- kFormatUrlOmitHTTP, net::UnescapeRule::NORMAL, &parsed,
- nullptr, nullptr);
+ formatted =
+ FormatUrl(GURL("http://ftp.host:8000/a?b=c#d"), kFormatUrlOmitHTTP,
+ base::UnescapeRule::NORMAL, &parsed, nullptr, nullptr);
EXPECT_EQ(u"http://ftp.host:8000/a?b=c#d", formatted);
EXPECT_TRUE(parsed.scheme.is_valid());
EXPECT_FALSE(parsed.username.is_valid());
@@ -525,7 +566,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) {
// omit http starts with 'f' case.
formatted = FormatUrl(GURL("http://f/"), kFormatUrlOmitHTTP,
- net::UnescapeRule::NORMAL, &parsed, nullptr, nullptr);
+ base::UnescapeRule::NORMAL, &parsed, nullptr, nullptr);
EXPECT_EQ(u"f/", formatted);
EXPECT_FALSE(parsed.scheme.is_valid());
EXPECT_FALSE(parsed.username.is_valid());
@@ -547,7 +588,7 @@ TEST(UrlFormatterTest, FormatUrlRoundTripPathASCII) {
size_t prefix_len;
std::u16string formatted =
FormatUrl(url, kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::NORMAL, nullptr, &prefix_len, nullptr);
+ base::UnescapeRule::NORMAL, nullptr, &prefix_len, nullptr);
EXPECT_EQ(url.spec(), GURL(formatted).spec());
}
}
@@ -564,7 +605,7 @@ TEST(UrlFormatterTest, FormatUrlRoundTripPathEscaped) {
size_t prefix_len;
std::u16string formatted =
FormatUrl(url, kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::NORMAL, nullptr, &prefix_len, nullptr);
+ base::UnescapeRule::NORMAL, nullptr, &prefix_len, nullptr);
EXPECT_EQ(url.spec(), GURL(formatted).spec());
}
}
@@ -578,7 +619,7 @@ TEST(UrlFormatterTest, FormatUrlRoundTripQueryASCII) {
size_t prefix_len;
std::u16string formatted =
FormatUrl(url, kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::NORMAL, nullptr, &prefix_len, nullptr);
+ base::UnescapeRule::NORMAL, nullptr, &prefix_len, nullptr);
EXPECT_EQ(url.spec(), GURL(formatted).spec());
}
}
@@ -599,7 +640,7 @@ TEST(UrlFormatterTest, FormatUrlRoundTripQueryEscaped) {
size_t prefix_len;
std::u16string formatted =
FormatUrl(url, kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::NORMAL, nullptr, &prefix_len, nullptr);
+ base::UnescapeRule::NORMAL, nullptr, &prefix_len, nullptr);
if (test_char &&
strchr(kUnescapedCharacters, static_cast<char>(test_char))) {
@@ -639,15 +680,14 @@ TEST(UrlFormatterTest, StripWWWFromHostComponent) {
TEST(UrlFormatterTest, FormatUrlWithOffsets) {
CheckAdjustedOffsets(std::string(), kFormatUrlOmitNothing,
- net::UnescapeRule::NORMAL, nullptr);
+ base::UnescapeRule::NORMAL, nullptr);
const size_t basic_offsets[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25
};
- CheckAdjustedOffsets("http://www.google.com/foo/",
- kFormatUrlOmitNothing, net::UnescapeRule::NORMAL,
- basic_offsets);
+ CheckAdjustedOffsets("http://www.google.com/foo/", kFormatUrlOmitNothing,
+ base::UnescapeRule::NORMAL, basic_offsets);
const size_t omit_auth_offsets_1[] = {
0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 7,
@@ -655,7 +695,7 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
};
CheckAdjustedOffsets("http://foo:bar@www.google.com/",
kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::NORMAL, omit_auth_offsets_1);
+ base::UnescapeRule::NORMAL, omit_auth_offsets_1);
const size_t omit_auth_offsets_2[] = {
0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, 7, 8, 9, 10, 11, 12, 13, 14,
@@ -663,7 +703,7 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
};
CheckAdjustedOffsets("http://foo@www.google.com/",
kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::NORMAL, omit_auth_offsets_2);
+ base::UnescapeRule::NORMAL, omit_auth_offsets_2);
const size_t dont_omit_auth_offsets[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
@@ -673,7 +713,7 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
};
// Unescape to "http://foo\x30B0:\x30B0bar@www.google.com".
CheckAdjustedOffsets("http://foo%E3%82%B0:%E3%82%B0bar@www.google.com/",
- kFormatUrlOmitNothing, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitNothing, base::UnescapeRule::NORMAL,
dont_omit_auth_offsets);
const size_t view_source_offsets[] = {
@@ -682,7 +722,7 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
};
CheckAdjustedOffsets("view-source:http://foo@www.google.com/",
kFormatUrlOmitUsernamePassword,
- net::UnescapeRule::NORMAL, view_source_offsets);
+ base::UnescapeRule::NORMAL, view_source_offsets);
const size_t idn_hostname_offsets_1[] = {
0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos,
@@ -691,7 +731,7 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
};
// Convert punycode to "http://\x671d\x65e5\x3042\x3055\x3072.jp/foo/".
CheckAdjustedOffsets("http://xn--l8jvb1ey91xtjb.jp/foo/",
- kFormatUrlOmitNothing, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitNothing, base::UnescapeRule::NORMAL,
idn_hostname_offsets_1);
const size_t idn_hostname_offsets_2[] = {
@@ -703,8 +743,8 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
// Convert punycode to
// "http://test.\x89c6\x9891.\x5317\x4eac\x5927\x5b78.test/".
CheckAdjustedOffsets("http://test.xn--cy2a840a.xn--1lq90ic7f1rc.test/",
- kFormatUrlOmitNothing,
- net::UnescapeRule::NORMAL, idn_hostname_offsets_2);
+ kFormatUrlOmitNothing, base::UnescapeRule::NORMAL,
+ idn_hostname_offsets_2);
const size_t unescape_offsets[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
@@ -716,7 +756,7 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
// Unescape to "http://www.google.com/foo bar/\x30B0\x30FC\x30B0\x30EB".
CheckAdjustedOffsets(
"http://www.google.com/foo%20bar/%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB",
- kFormatUrlOmitNothing, net::UnescapeRule::SPACES, unescape_offsets);
+ kFormatUrlOmitNothing, base::UnescapeRule::SPACES, unescape_offsets);
const size_t ref_offsets[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
@@ -727,7 +767,7 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
// Unescape to "http://www.google.com/foo.html#\x30B0\x30B0z".
CheckAdjustedOffsets("http://www.google.com/foo.html#%E3%82%B0%E3%82%B0z",
- kFormatUrlOmitNothing, net::UnescapeRule::NORMAL,
+ kFormatUrlOmitNothing, base::UnescapeRule::NORMAL,
ref_offsets);
const size_t omit_http_offsets[] = {
@@ -735,13 +775,13 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
10, 11, 12, 13, 14
};
CheckAdjustedOffsets("http://www.google.com/", kFormatUrlOmitHTTP,
- net::UnescapeRule::NORMAL, omit_http_offsets);
+ base::UnescapeRule::NORMAL, omit_http_offsets);
const size_t omit_http_start_with_ftp_offsets[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
};
CheckAdjustedOffsets("http://ftp.google.com/", kFormatUrlOmitHTTP,
- net::UnescapeRule::NORMAL,
+ base::UnescapeRule::NORMAL,
omit_http_start_with_ftp_offsets);
const size_t omit_all_offsets[] = {
@@ -749,35 +789,35 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
0, 1, 2, 3, 4, 5, 6, 7
};
CheckAdjustedOffsets("http://user@foo.com/", kFormatUrlOmitDefaults,
- net::UnescapeRule::NORMAL, omit_all_offsets);
+ base::UnescapeRule::NORMAL, omit_all_offsets);
const size_t trim_after_host_offsets[] = {
0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0, 1, 2, 3, 4,
5, 6, 7, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 9};
CheckAdjustedOffsets("http://foo.com/abcdefg",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, trim_after_host_offsets);
+ base::UnescapeRule::NORMAL, trim_after_host_offsets);
CheckAdjustedOffsets("http://foo.com/abc/def",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, trim_after_host_offsets);
+ base::UnescapeRule::NORMAL, trim_after_host_offsets);
CheckAdjustedOffsets("http://foo.com/abc?a=b",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, trim_after_host_offsets);
+ base::UnescapeRule::NORMAL, trim_after_host_offsets);
CheckAdjustedOffsets("http://foo.com/abc#def",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, trim_after_host_offsets);
+ base::UnescapeRule::NORMAL, trim_after_host_offsets);
CheckAdjustedOffsets("http://foo.com/a?a=b#f",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, trim_after_host_offsets);
+ base::UnescapeRule::NORMAL, trim_after_host_offsets);
CheckAdjustedOffsets("http://foo.com//??###",
kFormatUrlOmitDefaults | kFormatUrlTrimAfterHost,
- net::UnescapeRule::NORMAL, trim_after_host_offsets);
+ base::UnescapeRule::NORMAL, trim_after_host_offsets);
const size_t omit_https_offsets[] = {
0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
CheckAdjustedOffsets("https://www.google.com/", kFormatUrlOmitHTTPS,
- net::UnescapeRule::NORMAL, omit_https_offsets);
+ base::UnescapeRule::NORMAL, omit_https_offsets);
const size_t omit_https_with_auth_offsets[] = {
0, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 0,
@@ -785,14 +825,15 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
6, 7, 8, 9, 10, 11, 12, 13, 14};
CheckAdjustedOffsets("https://u:p@www.google.com/",
kFormatUrlOmitDefaults | kFormatUrlOmitHTTPS,
- net::UnescapeRule::NORMAL, omit_https_with_auth_offsets);
+ base::UnescapeRule::NORMAL,
+ omit_https_with_auth_offsets);
const size_t strip_trivial_subdomains_offsets_1[] = {
0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos, kNpos, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};
CheckAdjustedOffsets(
"http://www.google.com/foo/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, strip_trivial_subdomains_offsets_1);
+ base::UnescapeRule::NORMAL, strip_trivial_subdomains_offsets_1);
const size_t strip_trivial_subdomains_from_idn_offsets[] = {
0, 1, 2, 3, 4, 5, 6, 7, kNpos, kNpos,
@@ -801,7 +842,7 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) {
13, 14, 15, 16, 17, 18, 19};
CheckAdjustedOffsets(
"http://www.xn--l8jvb1ey91xtjb.jp/foo/", kFormatUrlOmitTrivialSubdomains,
- net::UnescapeRule::NORMAL, strip_trivial_subdomains_from_idn_offsets);
+ base::UnescapeRule::NORMAL, strip_trivial_subdomains_from_idn_offsets);
}
} // namespace url_formatter
diff --git a/chromium/components/url_matcher/BUILD.gn b/chromium/components/url_matcher/BUILD.gn
index f1d627705dc..9113049ae77 100644
--- a/chromium/components/url_matcher/BUILD.gn
+++ b/chromium/components/url_matcher/BUILD.gn
@@ -6,10 +6,6 @@ component("url_matcher") {
sources = [
"regex_set_matcher.cc",
"regex_set_matcher.h",
- "string_pattern.cc",
- "string_pattern.h",
- "substring_set_matcher.cc",
- "substring_set_matcher.h",
"url_matcher.cc",
"url_matcher.h",
"url_matcher_constants.cc",
@@ -38,8 +34,6 @@ source_set("unit_tests") {
testonly = true
sources = [
"regex_set_matcher_unittest.cc",
- "string_pattern_unittest.cc",
- "substring_set_matcher_unittest.cc",
"url_matcher_factory_unittest.cc",
"url_matcher_unittest.cc",
"url_util_unittest.cc",
diff --git a/chromium/components/url_matcher/regex_set_matcher.cc b/chromium/components/url_matcher/regex_set_matcher.cc
index 60b04c6a24b..92cbbc57613 100644
--- a/chromium/components/url_matcher/regex_set_matcher.cc
+++ b/chromium/components/url_matcher/regex_set_matcher.cc
@@ -11,17 +11,19 @@
#include "base/logging.h"
#include "base/strings/string_util.h"
-#include "components/url_matcher/substring_set_matcher.h"
+#include "base/substring_set_matcher/substring_set_matcher.h"
#include "third_party/re2/src/re2/filtered_re2.h"
#include "third_party/re2/src/re2/re2.h"
+using base::MatcherStringPattern;
+
namespace url_matcher {
RegexSetMatcher::RegexSetMatcher() = default;
RegexSetMatcher::~RegexSetMatcher() = default;
void RegexSetMatcher::AddPatterns(
- const std::vector<const StringPattern*>& regex_list) {
+ const std::vector<const MatcherStringPattern*>& regex_list) {
if (regex_list.empty())
return;
for (size_t i = 0; i < regex_list.size(); ++i) {
@@ -37,7 +39,7 @@ void RegexSetMatcher::ClearPatterns() {
}
bool RegexSetMatcher::Match(const std::string& text,
- std::set<StringPattern::ID>* matches) const {
+ std::set<MatcherStringPattern::ID>* matches) const {
size_t old_number_of_matches = matches->size();
if (regexes_.empty())
return false;
@@ -54,7 +56,7 @@ bool RegexSetMatcher::Match(const std::string& text,
filtered_re2_->AllMatches(text, atoms, &re2_ids);
for (size_t i = 0; i < re2_ids.size(); ++i) {
- StringPattern::ID id = re2_id_map_[re2_ids[i]];
+ MatcherStringPattern::ID id = re2_id_map_[re2_ids[i]];
matches->insert(id);
}
return old_number_of_matches != matches->size();
@@ -66,7 +68,7 @@ bool RegexSetMatcher::IsEmpty() const {
std::vector<RegexSetMatcher::RE2ID> RegexSetMatcher::FindSubstringMatches(
const std::string& text) const {
- std::set<int> atoms_set;
+ std::set<base::MatcherStringPattern::ID> atoms_set;
substring_matcher_->Match(text, &atoms_set);
return std::vector<RE2ID>(atoms_set.begin(), atoms_set.end());
}
@@ -79,8 +81,8 @@ void RegexSetMatcher::RebuildMatcher() {
for (auto it = regexes_.begin(); it != regexes_.end(); ++it) {
RE2ID re2_id;
- RE2::ErrorCode error = filtered_re2_->Add(
- it->second->pattern(), RE2::DefaultOptions, &re2_id);
+ RE2::ErrorCode error =
+ filtered_re2_->Add(it->second->pattern(), RE2::DefaultOptions, &re2_id);
if (error == RE2::NoError) {
DCHECK_EQ(static_cast<RE2ID>(re2_id_map_.size()), re2_id);
re2_id_map_.push_back(it->first);
@@ -95,15 +97,16 @@ void RegexSetMatcher::RebuildMatcher() {
std::vector<std::string> strings_to_match;
filtered_re2_->Compile(&strings_to_match);
- std::vector<url_matcher::StringPattern> substring_patterns;
+ std::vector<MatcherStringPattern> substring_patterns;
substring_patterns.reserve(strings_to_match.size());
// Build SubstringSetMatcher from |strings_to_match|.
for (size_t i = 0; i < strings_to_match.size(); ++i)
substring_patterns.emplace_back(std::move(strings_to_match[i]), i);
- substring_matcher_ =
- std::make_unique<SubstringSetMatcher>(substring_patterns);
+ substring_matcher_ = std::make_unique<base::SubstringSetMatcher>();
+ bool success = substring_matcher_->Build(substring_patterns);
+ CHECK(success);
}
} // namespace url_matcher
diff --git a/chromium/components/url_matcher/regex_set_matcher.h b/chromium/components/url_matcher/regex_set_matcher.h
index 0c19c123dd3..2aadc18ee2a 100644
--- a/chromium/components/url_matcher/regex_set_matcher.h
+++ b/chromium/components/url_matcher/regex_set_matcher.h
@@ -11,8 +11,8 @@
#include <string>
#include <vector>
-#include "components/url_matcher/string_pattern.h"
-#include "components/url_matcher/substring_set_matcher.h"
+#include "base/substring_set_matcher/matcher_string_pattern.h"
+#include "base/substring_set_matcher/substring_set_matcher.h"
#include "components/url_matcher/url_matcher_export.h"
namespace re2 {
@@ -34,7 +34,8 @@ class URL_MATCHER_EXPORT RegexSetMatcher {
// the FilteredRE2 matcher; thus, for efficiency, prefer adding multiple
// patterns at once.
// Ownership of the patterns remains with the caller.
- void AddPatterns(const std::vector<const StringPattern*>& regex_list);
+ void AddPatterns(
+ const std::vector<const base::MatcherStringPattern*>& regex_list);
// Removes all regex patterns.
void ClearPatterns();
@@ -42,14 +43,16 @@ class URL_MATCHER_EXPORT RegexSetMatcher {
// Appends the IDs of regular expressions in our set that match the |text|
// to |matches|.
bool Match(const std::string& text,
- std::set<StringPattern::ID>* matches) const;
+ std::set<base::MatcherStringPattern::ID>* matches) const;
bool IsEmpty() const;
private:
typedef int RE2ID;
- typedef std::map<StringPattern::ID, const StringPattern*> RegexMap;
- typedef std::vector<StringPattern::ID> RE2IDMap;
+ typedef std::map<base::MatcherStringPattern::ID,
+ const base::MatcherStringPattern*>
+ RegexMap;
+ typedef std::vector<base::MatcherStringPattern::ID> RE2IDMap;
// Use Aho-Corasick SubstringSetMatcher to find which literal patterns
// match the |text|.
@@ -61,14 +64,14 @@ class URL_MATCHER_EXPORT RegexSetMatcher {
// apparently not supported by FilteredRE2.
void RebuildMatcher();
- // Mapping of regex StringPattern::IDs to regexes.
+ // Mapping of regex MatcherStringPattern::IDs to regexes.
RegexMap regexes_;
// Mapping of RE2IDs from FilteredRE2 (which are assigned in order)
- // to regex StringPattern::IDs.
+ // to regex MatcherStringPattern::IDs.
RE2IDMap re2_id_map_;
std::unique_ptr<re2::FilteredRE2> filtered_re2_;
- std::unique_ptr<SubstringSetMatcher> substring_matcher_;
+ std::unique_ptr<base::SubstringSetMatcher> substring_matcher_;
};
} // namespace url_matcher
diff --git a/chromium/components/url_matcher/regex_set_matcher_unittest.cc b/chromium/components/url_matcher/regex_set_matcher_unittest.cc
index bb9ca4f5edd..1dea84fa0f1 100644
--- a/chromium/components/url_matcher/regex_set_matcher_unittest.cc
+++ b/chromium/components/url_matcher/regex_set_matcher_unittest.cc
@@ -10,50 +10,53 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+using base::MatcherStringPattern;
+using base::SubstringSetMatcher;
+
namespace url_matcher {
TEST(RegexSetMatcherTest, MatchRegexes) {
- StringPattern pattern_1("ab.*c", 42);
- StringPattern pattern_2("f*f", 17);
- StringPattern pattern_3("c(ar|ra)b|brac", 239);
- std::vector<const StringPattern*> regexes;
+ MatcherStringPattern pattern_1("ab.*c", 42);
+ MatcherStringPattern pattern_2("f*f", 17);
+ MatcherStringPattern pattern_3("c(ar|ra)b|brac", 239);
+ std::vector<const MatcherStringPattern*> regexes;
regexes.push_back(&pattern_1);
regexes.push_back(&pattern_2);
regexes.push_back(&pattern_3);
RegexSetMatcher matcher;
matcher.AddPatterns(regexes);
- std::set<StringPattern::ID> result1;
+ std::set<MatcherStringPattern::ID> result1;
matcher.Match("http://abracadabra.com", &result1);
EXPECT_EQ(2U, result1.size());
EXPECT_TRUE(base::Contains(result1, 42));
EXPECT_TRUE(base::Contains(result1, 239));
- std::set<StringPattern::ID> result2;
+ std::set<MatcherStringPattern::ID> result2;
matcher.Match("https://abfffffffffffffffffffffffffffffff.fi/cf", &result2);
EXPECT_EQ(2U, result2.size());
EXPECT_TRUE(base::Contains(result2, 17));
EXPECT_TRUE(base::Contains(result2, 42));
- std::set<StringPattern::ID> result3;
+ std::set<MatcherStringPattern::ID> result3;
matcher.Match("http://nothing.com/", &result3);
EXPECT_EQ(0U, result3.size());
}
TEST(RegexSetMatcherTest, CaseSensitivity) {
- StringPattern pattern_1("AAA", 51);
- StringPattern pattern_2("aaA", 57);
- std::vector<const StringPattern*> regexes;
+ MatcherStringPattern pattern_1("AAA", 51);
+ MatcherStringPattern pattern_2("aaA", 57);
+ std::vector<const MatcherStringPattern*> regexes;
regexes.push_back(&pattern_1);
regexes.push_back(&pattern_2);
RegexSetMatcher matcher;
matcher.AddPatterns(regexes);
- std::set<StringPattern::ID> result1;
+ std::set<MatcherStringPattern::ID> result1;
matcher.Match("http://aaa.net/", &result1);
EXPECT_EQ(0U, result1.size());
- std::set<StringPattern::ID> result2;
+ std::set<MatcherStringPattern::ID> result2;
matcher.Match("http://aaa.net/quaaACK", &result2);
EXPECT_EQ(1U, result2.size());
EXPECT_TRUE(base::Contains(result2, 57));
diff --git a/chromium/components/url_matcher/string_pattern.cc b/chromium/components/url_matcher/string_pattern.cc
deleted file mode 100644
index 1ac691ba70f..00000000000
--- a/chromium/components/url_matcher/string_pattern.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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 "components/url_matcher/string_pattern.h"
-
-#include <tuple>
-#include <utility>
-
-#include "base/check_op.h"
-
-namespace url_matcher {
-
-StringPattern::StringPattern(std::string pattern, StringPattern::ID id)
- : pattern_(std::move(pattern)), id_(id) {
- DCHECK_NE(kInvalidId, id_);
-}
-
-StringPattern::~StringPattern() {}
-
-StringPattern::StringPattern(StringPattern&&) = default;
-StringPattern& StringPattern::operator=(StringPattern&&) = default;
-
-bool StringPattern::operator<(const StringPattern& rhs) const {
- return std::tie(id_, pattern_) < std::tie(rhs.id_, rhs.pattern_);
-}
-
-} // namespace url_matcher
diff --git a/chromium/components/url_matcher/string_pattern.h b/chromium/components/url_matcher/string_pattern.h
deleted file mode 100644
index 290f95324f1..00000000000
--- a/chromium/components/url_matcher/string_pattern.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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 COMPONENTS_URL_MATCHER_STRING_PATTERN_H_
-#define COMPONENTS_URL_MATCHER_STRING_PATTERN_H_
-
-#include <string>
-
-#include "components/url_matcher/url_matcher_export.h"
-
-namespace url_matcher {
-
-// An individual pattern of a substring or regex matcher. A pattern consists of
-// a string (interpreted as individual bytes, no character encoding) and an
-// identifier.
-// IDs are returned to the caller of SubstringSetMatcher::Match() or
-// RegexMatcher::MatchURL() to help the caller to figure out what
-// patterns matched a string. All patterns registered to a matcher
-// need to contain unique IDs.
-class URL_MATCHER_EXPORT StringPattern {
- public:
- typedef int ID;
-
- // An invalid ID value. Clients must not use this as the id.
- static constexpr ID kInvalidId = -1;
-
- StringPattern(std::string pattern, ID id);
-
- StringPattern(const StringPattern&) = delete;
- StringPattern& operator=(const StringPattern&) = delete;
-
- ~StringPattern();
- StringPattern(StringPattern&&);
- StringPattern& operator=(StringPattern&&);
- const std::string& pattern() const { return pattern_; }
- ID id() const { return id_; }
-
- bool operator<(const StringPattern& rhs) const;
-
- private:
- std::string pattern_;
- ID id_;
-};
-
-} // namespace url_matcher
-
-#endif // COMPONENTS_URL_MATCHER_STRING_PATTERN_H_
diff --git a/chromium/components/url_matcher/string_pattern_unittest.cc b/chromium/components/url_matcher/string_pattern_unittest.cc
deleted file mode 100644
index 460f431fc6f..00000000000
--- a/chromium/components/url_matcher/string_pattern_unittest.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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 "components/url_matcher/string_pattern.h"
-
-#include <string>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace url_matcher {
-
-TEST(StringPatternTest, StringPattern) {
- StringPattern r1("Test", 2);
- EXPECT_EQ("Test", r1.pattern());
- EXPECT_EQ(2, r1.id());
-
- EXPECT_FALSE(r1 < r1);
- StringPattern r2("Test", 3);
- EXPECT_TRUE(r1 < r2);
- StringPattern r3("ZZZZ", 2);
- EXPECT_TRUE(r1 < r3);
-}
-
-} // namespace url_matcher
diff --git a/chromium/components/url_matcher/substring_set_matcher.cc b/chromium/components/url_matcher/substring_set_matcher.cc
deleted file mode 100644
index 12c6c4104b2..00000000000
--- a/chromium/components/url_matcher/substring_set_matcher.cc
+++ /dev/null
@@ -1,316 +0,0 @@
-// 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 "components/url_matcher/substring_set_matcher.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <queue>
-
-#include "base/check_op.h"
-#include "base/containers/contains.h"
-#include "base/containers/queue.h"
-#include "base/numerics/checked_math.h"
-#include "base/trace_event/memory_usage_estimator.h"
-
-namespace url_matcher {
-
-namespace {
-
-// Compare StringPattern instances based on their string patterns.
-bool ComparePatterns(const StringPattern* a, const StringPattern* b) {
- return a->pattern() < b->pattern();
-}
-
-std::vector<const StringPattern*> GetVectorOfPointers(
- const std::vector<StringPattern>& patterns) {
- std::vector<const StringPattern*> pattern_pointers;
- pattern_pointers.reserve(patterns.size());
-
- for (const StringPattern& pattern : patterns)
- pattern_pointers.push_back(&pattern);
-
- return pattern_pointers;
-}
-
-} // namespace
-
-SubstringSetMatcher::SubstringSetMatcher(
- const std::vector<StringPattern>& patterns)
- : SubstringSetMatcher(GetVectorOfPointers(patterns)) {}
-
-SubstringSetMatcher::SubstringSetMatcher(
- std::vector<const StringPattern*> patterns) {
- // Ensure there are no duplicate IDs and all pattern strings are distinct.
-#if DCHECK_IS_ON()
- {
- std::set<StringPattern::ID> ids;
- std::set<std::string> pattern_strings;
- for (const StringPattern* pattern : patterns) {
- CHECK(!base::Contains(ids, pattern->id()));
- CHECK(!base::Contains(pattern_strings, pattern->pattern()));
- ids.insert(pattern->id());
- pattern_strings.insert(pattern->pattern());
- }
- }
-#endif
-
- // Compute the total number of tree nodes needed.
- std::sort(patterns.begin(), patterns.end(), ComparePatterns);
- tree_.reserve(GetTreeSize(patterns));
- BuildAhoCorasickTree(patterns);
-
- // Sanity check that no new allocations happened in the tree and our computed
- // size was correct.
- DCHECK_EQ(tree_.size(), static_cast<size_t>(GetTreeSize(patterns)));
-
- is_empty_ = patterns.empty() && tree_.size() == 1u;
-}
-
-SubstringSetMatcher::~SubstringSetMatcher() = default;
-
-bool SubstringSetMatcher::Match(const std::string& text,
- std::set<StringPattern::ID>* matches) const {
- const size_t old_number_of_matches = matches->size();
-
- // Handle patterns matching the empty string.
- const AhoCorasickNode* const root = &tree_[kRootID];
- AccumulateMatchesForNode(root, matches);
-
- const AhoCorasickNode* current_node = root;
- for (const char c : text) {
- NodeID child = current_node->GetEdge(c);
-
- // If the child not can't be found, progressively iterate over the longest
- // proper suffix of the string represented by the current node. In a sense
- // we are pruning prefixes from the text.
- while (child == kInvalidNodeID && current_node != root) {
- current_node = &tree_[current_node->failure()];
- child = current_node->GetEdge(c);
- }
-
- if (child != kInvalidNodeID) {
- // The string represented by |child| is the longest possible suffix of the
- // current position of |text| in the trie.
- current_node = &tree_[child];
- AccumulateMatchesForNode(current_node, matches);
- } else {
- // The empty string is the longest possible suffix of the current position
- // of |text| in the trie.
- DCHECK_EQ(root, current_node);
- }
- }
-
- return old_number_of_matches != matches->size();
-}
-
-size_t SubstringSetMatcher::EstimateMemoryUsage() const {
- return base::trace_event::EstimateMemoryUsage(tree_);
-}
-
-// static
-constexpr SubstringSetMatcher::NodeID SubstringSetMatcher::kInvalidNodeID;
-constexpr SubstringSetMatcher::NodeID SubstringSetMatcher::kRootID;
-
-SubstringSetMatcher::NodeID SubstringSetMatcher::GetTreeSize(
- const std::vector<const StringPattern*>& patterns) const {
- DCHECK(std::is_sorted(patterns.begin(), patterns.end(), ComparePatterns));
-
- base::CheckedNumeric<NodeID> result = 1u; // 1 for the root node.
- if (patterns.empty())
- return result.ValueOrDie();
-
- auto last = patterns.begin();
- auto current = last + 1;
- // For the first pattern, each letter is a label of an edge to a new node.
- result += (*last)->pattern().size();
-
- // For the subsequent patterns, only count the edges which were not counted
- // yet. For this it suffices to test against the previous pattern, because the
- // patterns are sorted.
- for (; current != patterns.end(); ++last, ++current) {
- const std::string& last_pattern = (*last)->pattern();
- const std::string& current_pattern = (*current)->pattern();
- size_t prefix_bound = std::min(last_pattern.size(), current_pattern.size());
-
- size_t common_prefix = 0;
- while (common_prefix < prefix_bound &&
- last_pattern[common_prefix] == current_pattern[common_prefix]) {
- ++common_prefix;
- }
-
- result -= common_prefix;
- result += current_pattern.size();
- }
-
- return result.ValueOrDie();
-}
-
-void SubstringSetMatcher::BuildAhoCorasickTree(
- const SubstringPatternVector& patterns) {
- DCHECK(tree_.empty());
-
- // Initialize root node of tree.
- tree_.emplace_back();
-
- // Build the initial trie for all the patterns.
- for (const StringPattern* pattern : patterns)
- InsertPatternIntoAhoCorasickTree(pattern);
-
- // Trie creation is complete and edges are finalized. Shrink to fit each edge
- // map to save on memory.
- for (AhoCorasickNode& node : tree_)
- node.ShrinkEdges();
-
- CreateFailureAndOutputEdges();
-}
-
-void SubstringSetMatcher::InsertPatternIntoAhoCorasickTree(
- const StringPattern* pattern) {
- const std::string& text = pattern->pattern();
- const std::string::const_iterator text_end = text.end();
-
- // Iterators on the tree and the text.
- AhoCorasickNode* current_node = &tree_[kRootID];
- std::string::const_iterator i = text.begin();
-
- // Follow existing paths for as long as possible.
- while (i != text_end) {
- NodeID child = current_node->GetEdge(*i);
- if (child == kInvalidNodeID)
- break;
- current_node = &tree_[child];
- ++i;
- }
-
- // Create new nodes if necessary.
- while (i != text_end) {
- tree_.emplace_back();
- current_node->SetEdge(*i, tree_.size() - 1);
- current_node = &tree_.back();
- ++i;
- }
-
- // Register match.
- current_node->SetMatchID(pattern->id());
-}
-
-void SubstringSetMatcher::CreateFailureAndOutputEdges() {
- base::queue<AhoCorasickNode*> queue;
-
- // Initialize the failure edges for |root| and its children.
- AhoCorasickNode* const root = &tree_[0];
-
- // Assigning |root| as the failure edge for itself doesn't strictly abide by
- // the definition of "proper" suffix. The proper suffix of an empty string
- // should probably be defined as null, but we assign it to the |root| to
- // simplify the code and have the invariant that the failure edge is always
- // defined.
- root->SetFailure(kRootID);
-
- root->SetOutputLink(kInvalidNodeID);
-
- NodeID root_output_link = root->IsEndOfPattern() ? kRootID : kInvalidNodeID;
-
- for (const auto& edge : root->edges()) {
- AhoCorasickNode* child = &tree_[edge.second];
- child->SetFailure(kRootID);
- child->SetOutputLink(root_output_link);
- queue.push(child);
- }
-
- // Do a breadth first search over the trie to create failure edges. We
- // maintain the invariant that any node in |queue| has had its |failure_| and
- // |output_link_| edge already initialized.
- while (!queue.empty()) {
- AhoCorasickNode* current_node = queue.front();
- queue.pop();
-
- // Compute the failure and output edges of children using the failure edges
- // of the current node.
- for (const auto& edge : current_node->edges()) {
- const char edge_label = edge.first;
- AhoCorasickNode* child = &tree_[edge.second];
-
- const AhoCorasickNode* failure_candidate_parent =
- &tree_[current_node->failure()];
- NodeID failure_candidate_id =
- failure_candidate_parent->GetEdge(edge_label);
- while (failure_candidate_id == kInvalidNodeID &&
- failure_candidate_parent != root) {
- failure_candidate_parent = &tree_[failure_candidate_parent->failure()];
- failure_candidate_id = failure_candidate_parent->GetEdge(edge_label);
- }
-
- if (failure_candidate_id == kInvalidNodeID) {
- DCHECK_EQ(root, failure_candidate_parent);
- // |failure_candidate| is invalid and we can't proceed further since we
- // have reached the root. Hence the longest proper suffix of this string
- // represented by this node is the empty string (represented by root).
- failure_candidate_id = kRootID;
- }
-
- child->SetFailure(failure_candidate_id);
-
- const AhoCorasickNode* failure_candidate = &tree_[failure_candidate_id];
- // Now |failure_candidate| is |child|'s longest possible proper suffix in
- // the trie. We also know that since we are doing a breadth first search,
- // we would have established |failure_candidate|'s output link by now.
- // Hence we can define |child|'s output link as follows:
- child->SetOutputLink(failure_candidate->IsEndOfPattern()
- ? failure_candidate_id
- : failure_candidate->output_link());
-
- queue.push(child);
- }
- }
-}
-
-void SubstringSetMatcher::AccumulateMatchesForNode(
- const AhoCorasickNode* node,
- std::set<StringPattern::ID>* matches) const {
- DCHECK(matches);
-
- if (node->IsEndOfPattern())
- matches->insert(node->GetMatchID());
-
- NodeID node_id = node->output_link();
- while (node_id != kInvalidNodeID) {
- node = &tree_[node_id];
- matches->insert(node->GetMatchID());
- node_id = node->output_link();
- }
-}
-
-SubstringSetMatcher::AhoCorasickNode::AhoCorasickNode() = default;
-SubstringSetMatcher::AhoCorasickNode::~AhoCorasickNode() = default;
-
-SubstringSetMatcher::AhoCorasickNode::AhoCorasickNode(AhoCorasickNode&& other) =
- default;
-
-SubstringSetMatcher::AhoCorasickNode& SubstringSetMatcher::AhoCorasickNode::
-operator=(AhoCorasickNode&& other) = default;
-
-SubstringSetMatcher::NodeID SubstringSetMatcher::AhoCorasickNode::GetEdge(
- char c) const {
- auto i = edges_.find(c);
- return i == edges_.end() ? kInvalidNodeID : i->second;
-}
-
-void SubstringSetMatcher::AhoCorasickNode::SetEdge(char c, NodeID node) {
- DCHECK_NE(kInvalidNodeID, node);
- edges_[c] = node;
-}
-
-void SubstringSetMatcher::AhoCorasickNode::SetFailure(NodeID node) {
- DCHECK_NE(kInvalidNodeID, node);
- failure_ = node;
-}
-
-size_t SubstringSetMatcher::AhoCorasickNode::EstimateMemoryUsage() const {
- return base::trace_event::EstimateMemoryUsage(edges_);
-}
-
-} // namespace url_matcher
diff --git a/chromium/components/url_matcher/substring_set_matcher.h b/chromium/components/url_matcher/substring_set_matcher.h
deleted file mode 100644
index f067b640d54..00000000000
--- a/chromium/components/url_matcher/substring_set_matcher.h
+++ /dev/null
@@ -1,190 +0,0 @@
-// 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 COMPONENTS_URL_MATCHER_SUBSTRING_SET_MATCHER_H_
-#define COMPONENTS_URL_MATCHER_SUBSTRING_SET_MATCHER_H_
-
-#include <stdint.h>
-
-#include <limits>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/containers/flat_map.h"
-#include "components/url_matcher/string_pattern.h"
-#include "components/url_matcher/url_matcher_export.h"
-
-namespace url_matcher {
-
-// Class that store a set of string patterns and can find for a string S,
-// which string patterns occur in S.
-class URL_MATCHER_EXPORT SubstringSetMatcher {
- public:
- // Registers all |patterns|. Each pattern needs to have a unique ID and all
- // pattern strings must be unique.
- // Complexity:
- // Let n = number of patterns.
- // Let S = sum of pattern lengths.
- // Let k = range of char. Generally 256.
- // Complexity = O(nlogn + S * logk)
- // nlogn comes from sorting the patterns.
- // log(k) comes from our usage of std::map to store edges.
- SubstringSetMatcher(const std::vector<StringPattern>& patterns);
- SubstringSetMatcher(std::vector<const StringPattern*> patterns);
-
- SubstringSetMatcher(const SubstringSetMatcher&) = delete;
- SubstringSetMatcher& operator=(const SubstringSetMatcher&) = delete;
-
- ~SubstringSetMatcher();
-
- // Matches |text| against all registered StringPatterns. Stores the IDs
- // of matching patterns in |matches|. |matches| is not cleared before adding
- // to it.
- // Complexity:
- // Let t = length of |text|.
- // Let k = range of char. Generally 256.
- // Let z = number of matches returned.
- // Complexity = O(t * logk + zlogz)
- bool Match(const std::string& text,
- std::set<StringPattern::ID>* matches) const;
-
- // Returns true if this object retains no allocated data.
- bool IsEmpty() const { return is_empty_; }
-
- // Returns the dynamically allocated memory usage in bytes. See
- // base/trace_event/memory_usage_estimator.h for details.
- size_t EstimateMemoryUsage() const;
-
- private:
- // Represents the index of the node within |tree_|. It is specifically
- // uint32_t so that we can be sure it takes up 4 bytes. If the computed size
- // of |tree_| is larger than what can be stored within an uint32_t, there will
- // be a CHECK failure.
- using NodeID = uint32_t;
-
- // This is the maximum possible size of |tree_| and hence can't be a valid ID.
- static constexpr NodeID kInvalidNodeID = std::numeric_limits<NodeID>::max();
-
- static constexpr NodeID kRootID = 0;
-
- // A node of an Aho Corasick Tree. See
- // http://web.stanford.edu/class/archive/cs/cs166/cs166.1166/lectures/02/Small02.pdf
- // to understand the algorithm.
- //
- // The algorithm is based on the idea of building a trie of all registered
- // patterns. Each node of the tree is annotated with a set of pattern
- // IDs that are used to report matches.
- //
- // The root of the trie represents an empty match. If we were looking whether
- // any registered pattern matches a text at the beginning of the text (i.e.
- // whether any pattern is a prefix of the text), we could just follow
- // nodes in the trie according to the matching characters in the text.
- // E.g., if text == "foobar", we would follow the trie from the root node
- // to its child labeled 'f', from there to child 'o', etc. In this process we
- // would report all pattern IDs associated with the trie nodes as matches.
- //
- // As we are not looking for all prefix matches but all substring matches,
- // this algorithm would need to compare text.substr(0), text.substr(1), ...
- // against the trie, which is in O(|text|^2).
- //
- // The Aho Corasick algorithm improves this runtime by using failure edges.
- // In case we have found a partial match of length k in the text
- // (text[i, ..., i + k - 1]) in the trie starting at the root and ending at
- // a node at depth k, but cannot find a match in the trie for character
- // text[i + k] at depth k + 1, we follow a failure edge. This edge
- // corresponds to the longest proper suffix of text[i, ..., i + k - 1] that
- // is a prefix of any registered pattern.
- //
- // If your brain thinks "Forget it, let's go shopping.", don't worry.
- // Take a nap and read an introductory text on the Aho Corasick algorithm.
- // It will make sense. Eventually.
- class AhoCorasickNode {
- public:
- // Map from edge label to NodeID.
- using Edges = base::flat_map<char, NodeID>;
-
- AhoCorasickNode();
- ~AhoCorasickNode();
- AhoCorasickNode(AhoCorasickNode&& other);
- AhoCorasickNode& operator=(AhoCorasickNode&& other);
-
- NodeID GetEdge(char c) const;
- void SetEdge(char c, NodeID node);
- const Edges& edges() const { return edges_; }
-
- void ShrinkEdges() { edges_.shrink_to_fit(); }
-
- NodeID failure() const { return failure_; }
- void SetFailure(NodeID failure);
-
- void SetMatchID(StringPattern::ID id) {
- DCHECK(!IsEndOfPattern());
- match_id_ = id;
- }
-
- // Returns true if this node corresponds to a pattern.
- bool IsEndOfPattern() const {
- return match_id_ != StringPattern::kInvalidId;
- }
-
- // Must only be called if |IsEndOfPattern| returns true for this node.
- StringPattern::ID GetMatchID() const {
- DCHECK(IsEndOfPattern());
- return match_id_;
- }
-
- void SetOutputLink(NodeID node) { output_link_ = node; }
- NodeID output_link() const { return output_link_; }
-
- size_t EstimateMemoryUsage() const;
-
- private:
- // Outgoing edges of current node.
- Edges edges_;
-
- // Node index that failure edge leads to. The failure node corresponds to
- // the node which represents the longest proper suffix (include empty
- // string) of the string represented by this node. Must be valid, equal to
- // kInvalidNodeID when uninitialized.
- NodeID failure_ = kInvalidNodeID;
-
- // If valid, this node represents the end of a pattern. It stores the ID of
- // the corresponding pattern.
- StringPattern::ID match_id_ = StringPattern::kInvalidId;
-
- // Node index that corresponds to the longest proper suffix (including empty
- // suffix) of this node and which also represents the end of a pattern. Can
- // be invalid.
- NodeID output_link_ = kInvalidNodeID;
- };
-
- using SubstringPatternVector = std::vector<const StringPattern*>;
-
- // Given the set of patterns, compute how many nodes will the corresponding
- // Aho-Corasick tree have. Note that |patterns| need to be sorted.
- NodeID GetTreeSize(const std::vector<const StringPattern*>& patterns) const;
-
- void BuildAhoCorasickTree(const SubstringPatternVector& patterns);
-
- // Inserts a path for |pattern->pattern()| into the tree and adds
- // |pattern->id()| to the set of matches.
- void InsertPatternIntoAhoCorasickTree(const StringPattern* pattern);
-
- void CreateFailureAndOutputEdges();
-
- // Adds all pattern IDs to |matches| which are a suffix of the string
- // represented by |node|.
- void AccumulateMatchesForNode(const AhoCorasickNode* node,
- std::set<StringPattern::ID>* matches) const;
-
- // The nodes of a Aho-Corasick tree.
- std::vector<AhoCorasickNode> tree_;
-
- bool is_empty_ = true;
-};
-
-} // namespace url_matcher
-
-#endif // COMPONENTS_URL_MATCHER_SUBSTRING_SET_MATCHER_H_
diff --git a/chromium/components/url_matcher/substring_set_matcher_perftest.cc b/chromium/components/url_matcher/substring_set_matcher_perftest.cc
deleted file mode 100644
index ce847a14e47..00000000000
--- a/chromium/components/url_matcher/substring_set_matcher_perftest.cc
+++ /dev/null
@@ -1,85 +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 "components/url_matcher/substring_set_matcher.h"
-
-#include <limits>
-#include <string>
-#include <vector>
-
-#include "base/containers/contains.h"
-#include "base/rand_util.h"
-#include "base/time/time.h"
-#include "base/timer/elapsed_timer.h"
-#include "base/trace_event/memory_usage_estimator.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/perf/perf_result_reporter.h"
-
-namespace url_matcher {
-
-namespace {
-
-// Returns a random string of the given length using characters from 'a' to 'z'.
-std::string GetRandomString(size_t len) {
- std::vector<char> random_chars;
- random_chars.reserve(len);
- for (size_t i = 0; i < len; i++)
- random_chars.push_back(base::RandInt('a', 'z'));
-
- return std::string(random_chars.begin(), random_chars.end());
-}
-
-// Tests performance of SubstringSetMatcher for 20000 random patterns of length
-// 30.
-TEST(SubstringSetMatcherPerfTest, RandomKeys) {
- std::vector<StringPattern> patterns;
- std::set<std::string> pattern_strings;
-
- // Create patterns.
- const size_t kNumPatterns = 20000;
- const size_t kPatternLen = 30;
- for (size_t i = 0; i < kNumPatterns; i++) {
- std::string str = GetRandomString(kPatternLen);
-
- // Ensure we don't have any duplicate pattern strings.
- if (base::Contains(pattern_strings, str))
- continue;
-
- pattern_strings.insert(str);
- patterns.emplace_back(str, i);
- }
-
- base::ElapsedTimer init_timer;
-
- // Allocate SubstringSetMatcher on the heap so that EstimateMemoryUsage below
- // also includes its stack allocated memory.
- auto matcher = std::make_unique<SubstringSetMatcher>(patterns);
- base::TimeDelta init_time = init_timer.Elapsed();
-
- // Match patterns against a random string of 500 characters.
- const size_t kTextLen = 500;
- base::ElapsedTimer match_timer;
- std::set<StringPattern::ID> matches;
- matcher->Match(GetRandomString(kTextLen), &matches);
- base::TimeDelta match_time = match_timer.Elapsed();
-
- const char* kInitializationTime = ".init_time";
- const char* kMatchTime = ".match_time";
- const char* kMemoryUsage = ".memory_usage";
- auto reporter =
- perf_test::PerfResultReporter("SubstringSetMatcher", "RandomKeys");
- reporter.RegisterImportantMetric(kInitializationTime, "us");
- reporter.RegisterImportantMetric(kMatchTime, "us");
- reporter.RegisterImportantMetric(kMemoryUsage, "Mb");
-
- reporter.AddResult(kInitializationTime, init_time);
- reporter.AddResult(kMatchTime, match_time);
- reporter.AddResult(
- kMemoryUsage,
- (base::trace_event::EstimateMemoryUsage(matcher) * 1.0 / (1 << 20)));
-}
-
-} // namespace
-
-} // namespace url_matcher
diff --git a/chromium/components/url_matcher/substring_set_matcher_unittest.cc b/chromium/components/url_matcher/substring_set_matcher_unittest.cc
deleted file mode 100644
index 7efa1bd3037..00000000000
--- a/chromium/components/url_matcher/substring_set_matcher_unittest.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// 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 "components/url_matcher/substring_set_matcher.h"
-
-#include <stddef.h>
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace url_matcher {
-
-namespace {
-
-void TestOnePattern(const std::string& test_string,
- const std::string& pattern,
- bool is_match) {
- std::string test =
- "TestOnePattern(" + test_string + ", " + pattern + ", " +
- (is_match ? "1" : "0") + ")";
- std::vector<StringPattern> patterns;
- patterns.emplace_back(pattern, 1);
- SubstringSetMatcher matcher(patterns);
- std::set<int> matches;
- matcher.Match(test_string, &matches);
-
- size_t expected_matches = (is_match ? 1 : 0);
- EXPECT_EQ(expected_matches, matches.size()) << test;
- EXPECT_EQ(is_match, matches.find(1) != matches.end()) << test;
-}
-
-void TestTwoPatterns(const std::string& test_string,
- const std::string& pattern_1,
- const std::string& pattern_2,
- bool is_match_1,
- bool is_match_2) {
- std::string test =
- "TestTwoPatterns(" + test_string + ", " + pattern_1 + ", " + pattern_2 +
- ", " + (is_match_1 ? "1" : "0") + ", " + (is_match_2 ? "1" : "0") + ")";
- ASSERT_NE(pattern_1, pattern_2);
- StringPattern substring_pattern_1(pattern_1, 1);
- StringPattern substring_pattern_2(pattern_2, 2);
- // In order to make sure that the order in which patterns are registered
- // does not make any difference we try both permutations.
- for (int permutation = 0; permutation < 2; ++permutation) {
- std::vector<const StringPattern*> patterns;
- if (permutation == 0) {
- patterns.push_back(&substring_pattern_1);
- patterns.push_back(&substring_pattern_2);
- } else {
- patterns.push_back(&substring_pattern_2);
- patterns.push_back(&substring_pattern_1);
- }
- SubstringSetMatcher matcher(patterns);
- std::set<int> matches;
- matcher.Match(test_string, &matches);
-
- size_t expected_matches = (is_match_1 ? 1 : 0) + (is_match_2 ? 1 : 0);
- EXPECT_EQ(expected_matches, matches.size()) << test;
- EXPECT_EQ(is_match_1, matches.find(1) != matches.end()) << test;
- EXPECT_EQ(is_match_2, matches.find(2) != matches.end()) << test;
- }
-}
-
-} // namespace
-
-TEST(SubstringSetMatcherTest, TestMatcher) {
- // Test overlapping patterns
- // String abcde
- // Pattern 1 bc
- // Pattern 2 cd
- TestTwoPatterns("abcde", "bc", "cd", true, true);
-
- // Test subpatterns - part 1
- // String abcde
- // Pattern 1 bc
- // Pattern 2 b
- TestTwoPatterns("abcde", "bc", "b", true, true);
-
- // Test subpatterns - part 2
- // String abcde
- // Pattern 1 bc
- // Pattern 2 c
- TestTwoPatterns("abcde", "bc", "c", true, true);
-
- // Test identical matches
- // String abcde
- // Pattern 1 abcde
- TestOnePattern("abcde", "abcde", true);
-
- // Test multiple matches
- // String aaaaa
- // Pattern 1 a
- TestOnePattern("abcde", "a", true);
-
- // Test matches at beginning and end
- // String abcde
- // Pattern 1 ab
- // Pattern 2 de
- TestTwoPatterns("abcde", "ab", "de", true, true);
-
- // Test non-match
- // String abcde
- // Pattern 1 fg
- TestOnePattern("abcde", "fg", false);
-
- // Test empty pattern and too long pattern
- // String abcde
- // Pattern 1
- // Pattern 2 abcdef
- TestTwoPatterns("abcde", std::string(), "abcdef", true, false);
-}
-
-TEST(SubstringSetMatcherTest, TestMatcher2) {
- StringPattern pattern_1("a", 1);
- StringPattern pattern_2("b", 2);
- StringPattern pattern_3("c", 3);
-
- std::vector<const StringPattern*> patterns = {&pattern_1, &pattern_2,
- &pattern_3};
- auto matcher = std::make_unique<SubstringSetMatcher>(patterns);
-
- std::set<int> matches;
- matcher->Match("abd", &matches);
- EXPECT_EQ(2u, matches.size());
- EXPECT_TRUE(matches.end() != matches.find(1));
- EXPECT_TRUE(matches.end() != matches.find(2));
-
- patterns = {&pattern_1, &pattern_3};
- matcher = std::make_unique<SubstringSetMatcher>(patterns);
-
- matches.clear();
- matcher->Match("abd", &matches);
- EXPECT_EQ(1u, matches.size());
- EXPECT_TRUE(matches.end() != matches.find(1));
- EXPECT_TRUE(matches.end() == matches.find(2));
-
- matcher = std::make_unique<SubstringSetMatcher>(
- std::vector<const StringPattern*>());
- EXPECT_TRUE(matcher->IsEmpty());
-}
-
-TEST(SubstringSetMatcherTest, TestMatcher3) {
- std::string text = "abcde";
-
- std::vector<StringPattern> patterns;
- int id = 0;
- // Add all substrings of this string, including empty string.
- patterns.emplace_back("", id++);
- for (size_t i = 0; i < text.length(); i++) {
- for (size_t j = i; j < text.length(); j++) {
- patterns.emplace_back(text.substr(i, j - i + 1), id++);
- }
- }
-
- SubstringSetMatcher matcher(patterns);
- std::set<int> matches;
- matcher.Match(text, &matches);
- EXPECT_EQ(patterns.size(), matches.size());
- for (const StringPattern& pattern : patterns) {
- EXPECT_TRUE(matches.find(pattern.id()) != matches.end())
- << pattern.pattern();
- }
-}
-
-TEST(SubstringSetMatcherTest, TestEmptyMatcher) {
- std::vector<StringPattern> patterns;
- SubstringSetMatcher matcher(patterns);
- std::set<int> matches;
- matcher.Match("abd", &matches);
- EXPECT_TRUE(matches.empty());
- EXPECT_TRUE(matcher.IsEmpty());
-}
-
-} // namespace url_matcher
diff --git a/chromium/components/url_matcher/url_matcher.cc b/chromium/components/url_matcher/url_matcher.cc
index 6bde1664517..17c6ca4260e 100644
--- a/chromium/components/url_matcher/url_matcher.cc
+++ b/chromium/components/url_matcher/url_matcher.cc
@@ -15,19 +15,22 @@
#include "url/gurl.h"
#include "url/url_canon.h"
+using base::MatcherStringPattern;
+using base::SubstringSetMatcher;
+
namespace url_matcher {
// This set of classes implement a mapping of URL Component Patterns, such as
-// host_prefix, host_suffix, host_equals, ..., etc., to StringPatterns
+// host_prefix, host_suffix, host_equals, ..., etc., to MatcherStringPatterns
// for use in substring comparisons.
//
// The idea of this mapping is to reduce the problem of comparing many
// URL Component Patterns against one URL to the problem of searching many
// substrings in one string:
//
-// ---------------------- -----------------
-// | URL Query operator | ----translate----> | StringPattern |
-// ---------------------- -----------------
+// ---------------------- ------------------------
+// | URL Query operator | ----translate----> | MatcherStringPattern |
+// ---------------------- ------------------------
// ^
// |
// compare
@@ -95,7 +98,7 @@ namespace url_matcher {
//
// Similarly for path query parameters ({path, query}_{prefix, suffix, equals}).
//
-// With this, we can search the StringPatterns in the normalized URL.
+// With this, we can search the MatcherStringPatterns in the normalized URL.
//
//
// Case 2: url_{prefix,suffix,equals,contains} searches.
@@ -170,13 +173,11 @@ URLMatcherCondition::~URLMatcherCondition() {}
URLMatcherCondition::URLMatcherCondition(
Criterion criterion,
- const StringPattern* string_pattern)
- : criterion_(criterion),
- string_pattern_(string_pattern) {}
+ const MatcherStringPattern* string_pattern)
+ : criterion_(criterion), string_pattern_(string_pattern) {}
URLMatcherCondition::URLMatcherCondition(const URLMatcherCondition& rhs)
- : criterion_(rhs.criterion_),
- string_pattern_(rhs.string_pattern_) {}
+ : criterion_(rhs.criterion_), string_pattern_(rhs.string_pattern_) {}
URLMatcherCondition& URLMatcherCondition::operator=(
const URLMatcherCondition& rhs) {
@@ -186,8 +187,10 @@ URLMatcherCondition& URLMatcherCondition::operator=(
}
bool URLMatcherCondition::operator<(const URLMatcherCondition& rhs) const {
- if (criterion_ < rhs.criterion_) return true;
- if (criterion_ > rhs.criterion_) return false;
+ if (criterion_ < rhs.criterion_)
+ return true;
+ if (criterion_ > rhs.criterion_)
+ return false;
if (string_pattern_ != nullptr && rhs.string_pattern_ != nullptr)
return *string_pattern_ < *rhs.string_pattern_;
if (string_pattern_ == nullptr && rhs.string_pattern_ != nullptr)
@@ -225,7 +228,7 @@ bool URLMatcherCondition::IsOriginAndPathRegexCondition() const {
}
bool URLMatcherCondition::IsMatch(
- const std::set<StringPattern::ID>& matching_patterns,
+ const std::set<MatcherStringPattern::ID>& matching_patterns,
const GURL& url) const {
DCHECK(string_pattern_);
if (!base::Contains(matching_patterns, string_pattern_->id()))
@@ -235,14 +238,11 @@ bool URLMatcherCondition::IsMatch(
// that the match was found in the correct component of the URL.
switch (criterion_) {
case HOST_CONTAINS:
- return url.host().find(string_pattern_->pattern()) !=
- std::string::npos;
+ return url.host().find(string_pattern_->pattern()) != std::string::npos;
case PATH_CONTAINS:
- return url.path().find(string_pattern_->pattern()) !=
- std::string::npos;
+ return url.path().find(string_pattern_->pattern()) != std::string::npos;
case QUERY_CONTAINS:
- return url.query().find(string_pattern_->pattern()) !=
- std::string::npos;
+ return url.query().find(string_pattern_->pattern()) != std::string::npos;
default:
break;
}
@@ -265,10 +265,9 @@ const char kEndOfURL[] = {static_cast<char>(-5), 0};
const char kQuerySeparator = '&';
} // namespace
-URLMatcherConditionFactory::URLMatcherConditionFactory() : id_counter_(0) {}
+URLMatcherConditionFactory::URLMatcherConditionFactory() = default;
-URLMatcherConditionFactory::~URLMatcherConditionFactory() {
-}
+URLMatcherConditionFactory::~URLMatcherConditionFactory() = default;
std::string URLMatcherConditionFactory::CanonicalizeURLForComponentSearches(
const GURL& url) const {
@@ -282,13 +281,13 @@ std::string URLMatcherConditionFactory::CanonicalizeURLForComponentSearches(
URLMatcherCondition URLMatcherConditionFactory::CreateHostPrefixCondition(
const std::string& prefix) {
return CreateCondition(URLMatcherCondition::HOST_PREFIX,
- kBeginningOfURL + CanonicalizeHostPrefix(prefix));
+ kBeginningOfURL + CanonicalizeHostPrefix(prefix));
}
URLMatcherCondition URLMatcherConditionFactory::CreateHostSuffixCondition(
const std::string& suffix) {
return CreateCondition(URLMatcherCondition::HOST_SUFFIX,
- CanonicalizeHostSuffix(suffix) + kEndOfDomain);
+ CanonicalizeHostSuffix(suffix) + kEndOfDomain);
}
URLMatcherCondition URLMatcherConditionFactory::CreateHostContainsCondition(
@@ -298,14 +297,15 @@ URLMatcherCondition URLMatcherConditionFactory::CreateHostContainsCondition(
URLMatcherCondition URLMatcherConditionFactory::CreateHostEqualsCondition(
const std::string& str) {
- return CreateCondition(URLMatcherCondition::HOST_EQUALS,
+ return CreateCondition(
+ URLMatcherCondition::HOST_EQUALS,
kBeginningOfURL + CanonicalizeHostname(str) + kEndOfDomain);
}
URLMatcherCondition URLMatcherConditionFactory::CreatePathPrefixCondition(
const std::string& prefix) {
return CreateCondition(URLMatcherCondition::PATH_PREFIX,
- kEndOfDomain + prefix);
+ kEndOfDomain + prefix);
}
URLMatcherCondition URLMatcherConditionFactory::CreatePathSuffixCondition(
@@ -321,7 +321,7 @@ URLMatcherCondition URLMatcherConditionFactory::CreatePathContainsCondition(
URLMatcherCondition URLMatcherConditionFactory::CreatePathEqualsCondition(
const std::string& str) {
return CreateCondition(URLMatcherCondition::PATH_EQUALS,
- kEndOfDomain + str + kEndOfPath);
+ kEndOfDomain + str + kEndOfPath);
}
URLMatcherCondition URLMatcherConditionFactory::CreateQueryPrefixCondition(
@@ -366,10 +366,11 @@ URLMatcherCondition URLMatcherConditionFactory::CreateQueryEqualsCondition(
}
URLMatcherCondition
- URLMatcherConditionFactory::CreateHostSuffixPathPrefixCondition(
+URLMatcherConditionFactory::CreateHostSuffixPathPrefixCondition(
const std::string& host_suffix,
const std::string& path_prefix) {
- return CreateCondition(URLMatcherCondition::HOST_SUFFIX_PATH_PREFIX,
+ return CreateCondition(
+ URLMatcherCondition::HOST_SUFFIX_PATH_PREFIX,
CanonicalizeHostSuffix(host_suffix) + kEndOfDomain + path_prefix);
}
@@ -378,8 +379,8 @@ URLMatcherConditionFactory::CreateHostEqualsPathPrefixCondition(
const std::string& host,
const std::string& path_prefix) {
return CreateCondition(URLMatcherCondition::HOST_EQUALS_PATH_PREFIX,
- kBeginningOfURL + CanonicalizeHostname(host) + kEndOfDomain +
- path_prefix);
+ kBeginningOfURL + CanonicalizeHostname(host) +
+ kEndOfDomain + path_prefix);
}
std::string URLMatcherConditionFactory::CanonicalizeURLForFullSearches(
@@ -397,12 +398,11 @@ std::string URLMatcherConditionFactory::CanonicalizeURLForFullSearches(
}
}
return kBeginningOfURL + url.ReplaceComponents(replacements).spec() +
- kEndOfURL;
+ kEndOfURL;
}
-static std::string CanonicalizeURLForRegexSearchesHelper(
- const GURL& url,
- bool clear_query) {
+static std::string CanonicalizeURLForRegexSearchesHelper(const GURL& url,
+ bool clear_query) {
GURL::Replacements replacements;
replacements.ClearPassword();
replacements.ClearUsername();
@@ -434,7 +434,7 @@ URLMatcherConditionFactory::CanonicalizeURLForOriginAndPathRegexSearches(
URLMatcherCondition URLMatcherConditionFactory::CreateURLPrefixCondition(
const std::string& prefix) {
return CreateCondition(URLMatcherCondition::URL_PREFIX,
- kBeginningOfURL + prefix);
+ kBeginningOfURL + prefix);
}
URLMatcherCondition URLMatcherConditionFactory::CreateURLSuffixCondition(
@@ -450,7 +450,7 @@ URLMatcherCondition URLMatcherConditionFactory::CreateURLContainsCondition(
URLMatcherCondition URLMatcherConditionFactory::CreateURLEqualsCondition(
const std::string& str) {
return CreateCondition(URLMatcherCondition::URL_EQUALS,
- kBeginningOfURL + str + kEndOfURL);
+ kBeginningOfURL + str + kEndOfURL);
}
URLMatcherCondition URLMatcherConditionFactory::CreateURLMatchesCondition(
@@ -465,7 +465,7 @@ URLMatcherConditionFactory::CreateOriginAndPathMatchesCondition(
}
void URLMatcherConditionFactory::ForgetUnusedPatterns(
- const std::set<StringPattern::ID>& used_patterns) {
+ const std::set<MatcherStringPattern::ID>& used_patterns) {
auto i = substring_pattern_singletons_.begin();
while (i != substring_pattern_singletons_.end()) {
if (base::Contains(used_patterns, i->first->id()))
@@ -493,14 +493,14 @@ void URLMatcherConditionFactory::ForgetUnusedPatterns(
bool URLMatcherConditionFactory::IsEmpty() const {
return substring_pattern_singletons_.empty() &&
- regex_pattern_singletons_.empty() &&
- origin_and_path_regex_pattern_singletons_.empty();
+ regex_pattern_singletons_.empty() &&
+ origin_and_path_regex_pattern_singletons_.empty();
}
URLMatcherCondition URLMatcherConditionFactory::CreateCondition(
URLMatcherCondition::Criterion criterion,
const std::string& pattern) {
- StringPattern search_pattern(pattern, 0);
+ MatcherStringPattern search_pattern(pattern, 0);
PatternSingletons* pattern_singletons = nullptr;
if (IsRegexCriterion(criterion))
pattern_singletons = &regex_pattern_singletons_;
@@ -514,7 +514,8 @@ URLMatcherCondition URLMatcherConditionFactory::CreateCondition(
if (iter != pattern_singletons->end())
return URLMatcherCondition(criterion, iter->first);
- StringPattern* new_pattern = new StringPattern(pattern, GetNextID());
+ MatcherStringPattern* new_pattern =
+ new MatcherStringPattern(pattern, GetNextID());
(*pattern_singletons)[new_pattern] = base::WrapUnique(new_pattern);
return URLMatcherCondition(criterion, new_pattern);
}
@@ -559,18 +560,18 @@ std::string URLMatcherConditionFactory::CanonicalizeQuery(
return query;
}
-int URLMatcherConditionFactory::GetNextID() {
+base::MatcherStringPattern::ID URLMatcherConditionFactory::GetNextID() {
id_counter_++;
- if (id_counter_ == StringPattern::kInvalidId)
+ if (id_counter_ == MatcherStringPattern::kInvalidId)
id_counter_++;
return id_counter_;
}
-bool URLMatcherConditionFactory::StringPatternPointerCompare::operator()(
- StringPattern* lhs,
- StringPattern* rhs) const {
+bool URLMatcherConditionFactory::MatcherStringPatternPointerCompare::operator()(
+ MatcherStringPattern* lhs,
+ MatcherStringPattern* rhs) const {
if (lhs == nullptr && rhs != nullptr)
return true;
if (lhs != nullptr && rhs != nullptr)
@@ -655,8 +656,8 @@ bool URLQueryElementMatcherCondition::IsMatch(
size_t offset;
while ((offset = url_for_component_searches.find(key_, start)) !=
std::string::npos) {
- if (url_for_component_searches.compare(
- offset + key_length_, value_length_, value_) != 0) {
+ if (url_for_component_searches.compare(offset + key_length_,
+ value_length_, value_) != 0) {
return false;
} else {
++found;
@@ -667,13 +668,13 @@ bool URLQueryElementMatcherCondition::IsMatch(
}
case MATCH_FIRST: {
size_t offset = url_for_component_searches.find(key_);
- return url_for_component_searches.compare(
- offset + key_length_, value_length_, value_) == 0;
+ return url_for_component_searches.compare(offset + key_length_,
+ value_length_, value_) == 0;
}
case MATCH_LAST: {
size_t offset = url_for_component_searches.rfind(key_);
- return url_for_component_searches.compare(
- offset + key_length_, value_length_, value_) == 0;
+ return url_for_component_searches.compare(offset + key_length_,
+ value_length_, value_) == 0;
}
}
NOTREACHED();
@@ -736,13 +737,12 @@ URLMatcherPortFilter::Range URLMatcherPortFilter::CreateRange(int port) {
URLMatcherConditionSet::~URLMatcherConditionSet() {}
URLMatcherConditionSet::URLMatcherConditionSet(
- ID id,
+ base::MatcherStringPattern::ID id,
const Conditions& conditions)
- : id_(id),
- conditions_(conditions) {}
+ : id_(id), conditions_(conditions) {}
URLMatcherConditionSet::URLMatcherConditionSet(
- ID id,
+ base::MatcherStringPattern::ID id,
const Conditions& conditions,
std::unique_ptr<URLMatcherSchemeFilter> scheme_filter,
std::unique_ptr<URLMatcherPortFilter> port_filter)
@@ -752,7 +752,7 @@ URLMatcherConditionSet::URLMatcherConditionSet(
port_filter_(std::move(port_filter)) {}
URLMatcherConditionSet::URLMatcherConditionSet(
- ID id,
+ base::MatcherStringPattern::ID id,
const Conditions& conditions,
const QueryConditions& query_conditions,
std::unique_ptr<URLMatcherSchemeFilter> scheme_filter,
@@ -764,13 +764,13 @@ URLMatcherConditionSet::URLMatcherConditionSet(
port_filter_(std::move(port_filter)) {}
bool URLMatcherConditionSet::IsMatch(
- const std::set<StringPattern::ID>& matching_patterns,
+ const std::set<MatcherStringPattern::ID>& matching_patterns,
const GURL& url) const {
return IsMatch(matching_patterns, url, std::string());
}
bool URLMatcherConditionSet::IsMatch(
- const std::set<StringPattern::ID>& matching_patterns,
+ const std::set<MatcherStringPattern::ID>& matching_patterns,
const GURL& url,
const std::string& url_for_component_searches) const {
for (auto i = conditions_.begin(); i != conditions_.end(); ++i) {
@@ -809,18 +809,18 @@ void URLMatcher::AddConditionSets(
const URLMatcherConditionSet::Vector& condition_sets) {
for (auto i = condition_sets.begin(); i != condition_sets.end(); ++i) {
DCHECK(url_matcher_condition_sets_.find((*i)->id()) ==
- url_matcher_condition_sets_.end());
+ url_matcher_condition_sets_.end());
url_matcher_condition_sets_[(*i)->id()] = *i;
}
UpdateInternalDatastructures();
}
void URLMatcher::RemoveConditionSets(
- const std::vector<URLMatcherConditionSet::ID>& condition_set_ids) {
- for (auto i = condition_set_ids.begin(); i != condition_set_ids.end(); ++i) {
- DCHECK(url_matcher_condition_sets_.find(*i) !=
- url_matcher_condition_sets_.end());
- url_matcher_condition_sets_.erase(*i);
+ const std::vector<base::MatcherStringPattern::ID>& condition_set_ids) {
+ for (auto id : condition_set_ids) {
+ DCHECK(url_matcher_condition_sets_.find(id) !=
+ url_matcher_condition_sets_.end());
+ url_matcher_condition_sets_.erase(id);
}
UpdateInternalDatastructures();
}
@@ -829,12 +829,12 @@ void URLMatcher::ClearUnusedConditionSets() {
UpdateConditionFactory();
}
-std::set<URLMatcherConditionSet::ID> URLMatcher::MatchURL(
+std::set<base::MatcherStringPattern::ID> URLMatcher::MatchURL(
const GURL& url) const {
- // Find all IDs of StringPatterns that match |url|.
+ // Find all IDs of MatcherStringPatterns that match |url|.
// See URLMatcherConditionFactory for the canonicalization of URLs and the
// distinction between full url searches and url component searches.
- std::set<StringPattern::ID> matches;
+ std::set<MatcherStringPattern::ID> matches;
std::string url_for_component_searches;
if (!IsMatcherEmpty(full_url_matcher_)) {
@@ -858,7 +858,7 @@ std::set<URLMatcherConditionSet::ID> URLMatcher::MatchURL(
// Calculate all URLMatcherConditionSets for which all URLMatcherConditions
// were fulfilled.
- std::set<URLMatcherConditionSet::ID> result;
+ std::set<base::MatcherStringPattern::ID> result;
for (auto i = matches.begin(); i != matches.end(); ++i) {
// For each URLMatcherConditionSet there is exactly one condition
// registered in substring_match_triggers_. This means that the following
@@ -867,13 +867,13 @@ std::set<URLMatcherConditionSet::ID> URLMatcher::MatchURL(
auto triggered_condition_sets_iter = substring_match_triggers_.find(*i);
if (triggered_condition_sets_iter == substring_match_triggers_.end())
continue; // Not all substring matches are triggers for a condition set.
- const std::set<URLMatcherConditionSet::ID>& condition_sets =
+ const std::set<base::MatcherStringPattern::ID>& condition_sets =
triggered_condition_sets_iter->second;
for (auto j = condition_sets.begin(); j != condition_sets.end(); ++j) {
auto condition_set_iter = url_matcher_condition_sets_.find(*j);
DCHECK(condition_set_iter != url_matcher_condition_sets_.end());
- if (condition_set_iter->second->IsMatch(
- matches, url, url_for_component_searches))
+ if (condition_set_iter->second->IsMatch(matches, url,
+ url_for_component_searches))
result.insert(*j);
}
}
@@ -897,11 +897,11 @@ void URLMatcher::UpdateSubstringSetMatcher(bool full_url_conditions) {
// Determine which patterns need to be registered when this function
// terminates.
- std::set<const StringPattern*> new_patterns;
+ std::set<const MatcherStringPattern*> new_patterns;
for (URLMatcherConditionSets::const_iterator condition_set_iter =
- url_matcher_condition_sets_.begin();
- condition_set_iter != url_matcher_condition_sets_.end();
- ++condition_set_iter) {
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
const URLMatcherConditionSet::Conditions& conditions =
condition_set_iter->second->conditions();
for (auto condition_iter = conditions.begin();
@@ -930,19 +930,20 @@ void URLMatcher::UpdateSubstringSetMatcher(bool full_url_conditions) {
std::unique_ptr<SubstringSetMatcher>& url_matcher =
full_url_conditions ? full_url_matcher_ : url_component_matcher_;
- url_matcher =
- std::make_unique<SubstringSetMatcher>(std::vector<const StringPattern*>(
- new_patterns.begin(), new_patterns.end()));
+ url_matcher = std::make_unique<SubstringSetMatcher>();
+ bool success = url_matcher->Build(std::vector<const MatcherStringPattern*>(
+ new_patterns.begin(), new_patterns.end()));
+ CHECK(success);
}
void URLMatcher::UpdateRegexSetMatcher() {
- std::vector<const StringPattern*> new_patterns;
- std::vector<const StringPattern*> new_origin_and_path_patterns;
+ std::vector<const MatcherStringPattern*> new_patterns;
+ std::vector<const MatcherStringPattern*> new_origin_and_path_patterns;
for (URLMatcherConditionSets::const_iterator condition_set_iter =
- url_matcher_condition_sets_.begin();
- condition_set_iter != url_matcher_condition_sets_.end();
- ++condition_set_iter) {
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
const URLMatcherConditionSet::Conditions& conditions =
condition_set_iter->second->conditions();
for (auto condition_iter = conditions.begin();
@@ -966,16 +967,16 @@ void URLMatcher::UpdateRegexSetMatcher() {
void URLMatcher::UpdateTriggers() {
// Count substring pattern frequencies.
- std::map<StringPattern::ID, size_t> substring_pattern_frequencies;
+ std::map<MatcherStringPattern::ID, size_t> substring_pattern_frequencies;
for (URLMatcherConditionSets::const_iterator condition_set_iter =
- url_matcher_condition_sets_.begin();
- condition_set_iter != url_matcher_condition_sets_.end();
- ++condition_set_iter) {
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
const URLMatcherConditionSet::Conditions& conditions =
condition_set_iter->second->conditions();
for (auto condition_iter = conditions.begin();
condition_iter != conditions.end(); ++condition_iter) {
- const StringPattern* pattern = condition_iter->string_pattern();
+ const MatcherStringPattern* pattern = condition_iter->string_pattern();
substring_pattern_frequencies[pattern->id()]++;
}
@@ -984,13 +985,14 @@ void URLMatcher::UpdateTriggers() {
for (auto query_condition_iter = query_conditions.begin();
query_condition_iter != query_conditions.end();
++query_condition_iter) {
- const StringPattern* pattern = query_condition_iter->string_pattern();
+ const MatcherStringPattern* pattern =
+ query_condition_iter->string_pattern();
substring_pattern_frequencies[pattern->id()]++;
}
}
// Update trigger conditions: Determine for each URLMatcherConditionSet which
- // URLMatcherCondition contains a StringPattern that occurs least
+ // URLMatcherCondition contains a MatcherStringPattern that occurs least
// frequently in this URLMatcher. We assume that this condition is very
// specific and occurs rarely in URLs. If a match occurs for this
// URLMatcherCondition, we want to test all other URLMatcherCondition in the
@@ -998,19 +1000,19 @@ void URLMatcher::UpdateTriggers() {
// URLMatcherConditionSet is considered matching.
substring_match_triggers_.clear();
for (URLMatcherConditionSets::const_iterator condition_set_iter =
- url_matcher_condition_sets_.begin();
- condition_set_iter != url_matcher_condition_sets_.end();
- ++condition_set_iter) {
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
const URLMatcherConditionSet::Conditions& conditions =
condition_set_iter->second->conditions();
if (conditions.empty())
continue;
auto condition_iter = conditions.begin();
- StringPattern::ID trigger = condition_iter->string_pattern()->id();
+ MatcherStringPattern::ID trigger = condition_iter->string_pattern()->id();
// We skip the first element in the following loop.
++condition_iter;
for (; condition_iter != conditions.end(); ++condition_iter) {
- StringPattern::ID current_id =
+ MatcherStringPattern::ID current_id =
condition_iter->string_pattern()->id();
if (substring_pattern_frequencies[trigger] >
substring_pattern_frequencies[current_id]) {
@@ -1023,7 +1025,7 @@ void URLMatcher::UpdateTriggers() {
for (auto query_condition_iter = query_conditions.begin();
query_condition_iter != query_conditions.end();
++query_condition_iter) {
- StringPattern::ID current_id =
+ MatcherStringPattern::ID current_id =
query_condition_iter->string_pattern()->id();
if (substring_pattern_frequencies[trigger] >
substring_pattern_frequencies[current_id]) {
@@ -1036,11 +1038,11 @@ void URLMatcher::UpdateTriggers() {
}
void URLMatcher::UpdateConditionFactory() {
- std::set<StringPattern::ID> used_patterns;
+ std::set<MatcherStringPattern::ID> used_patterns;
for (URLMatcherConditionSets::const_iterator condition_set_iter =
- url_matcher_condition_sets_.begin();
- condition_set_iter != url_matcher_condition_sets_.end();
- ++condition_set_iter) {
+ url_matcher_condition_sets_.begin();
+ condition_set_iter != url_matcher_condition_sets_.end();
+ ++condition_set_iter) {
const URLMatcherConditionSet::Conditions& conditions =
condition_set_iter->second->conditions();
for (auto condition_iter = conditions.begin();
diff --git a/chromium/components/url_matcher/url_matcher.h b/chromium/components/url_matcher/url_matcher.h
index 74c19e111f2..15acc05f570 100644
--- a/chromium/components/url_matcher/url_matcher.h
+++ b/chromium/components/url_matcher/url_matcher.h
@@ -13,8 +13,8 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
+#include "base/substring_set_matcher/substring_set_matcher.h"
#include "components/url_matcher/regex_set_matcher.h"
-#include "components/url_matcher/substring_set_matcher.h"
#include "components/url_matcher/url_matcher_export.h"
class GURL;
@@ -24,9 +24,9 @@ namespace url_matcher {
// This class represents a single URL matching condition, e.g. a match on the
// host suffix or the containment of a string in the query component of a GURL.
//
-// The difference from a simple StringPattern is that this also supports
+// The difference from a simple MatcherStringPattern is that this also supports
// checking whether the {Host, Path, Query} of a URL contains a string. The
-// reduction of URL matching conditions to StringPatterns conducted by
+// reduction of URL matching conditions to MatcherStringPatterns conducted by
// URLMatcherConditionFactory is not capable of expressing that alone.
//
// Also supported is matching regular expressions against the URL (URL_MATCHES).
@@ -58,13 +58,13 @@ class URL_MATCHER_EXPORT URLMatcherCondition {
URLMatcherCondition();
~URLMatcherCondition();
URLMatcherCondition(Criterion criterion,
- const StringPattern* substring_pattern);
+ const base::MatcherStringPattern* substring_pattern);
URLMatcherCondition(const URLMatcherCondition& rhs);
URLMatcherCondition& operator=(const URLMatcherCondition& rhs);
bool operator<(const URLMatcherCondition& rhs) const;
Criterion criterion() const { return criterion_; }
- const StringPattern* string_pattern() const {
+ const base::MatcherStringPattern* string_pattern() const {
return string_pattern_;
}
@@ -83,16 +83,17 @@ class URL_MATCHER_EXPORT URLMatcherCondition {
// Returns whether this condition is fulfilled according to
// |matching_patterns| and |url|.
- bool IsMatch(const std::set<StringPattern::ID>& matching_patterns,
- const GURL& url) const;
+ bool IsMatch(
+ const std::set<base::MatcherStringPattern::ID>& matching_patterns,
+ const GURL& url) const;
private:
// |criterion_| and |string_pattern_| describe together what property a URL
// needs to fulfill to be considered a match.
Criterion criterion_;
- // This is the StringPattern that is used in a SubstringSetMatcher.
- raw_ptr<const StringPattern> string_pattern_;
+ // This is the MatcherStringPattern that is used in a SubstringSetMatcher.
+ raw_ptr<const base::MatcherStringPattern> string_pattern_;
};
// Class to map the problem of finding {host, path, query} {prefixes, suffixes,
@@ -112,7 +113,7 @@ class URL_MATCHER_EXPORT URLMatcherCondition {
// of a dictionary in a text" problem, which can be solved very efficiently
// by the Aho-Corasick algorithm.
//
-// IMPORTANT: The URLMatcherConditionFactory owns the StringPattern
+// IMPORTANT: The URLMatcherConditionFactory owns the MatcherStringPattern
// referenced by created URLMatcherConditions. Therefore, it must outlive
// all created URLMatcherCondition and the SubstringSetMatcher.
class URL_MATCHER_EXPORT URLMatcherConditionFactory {
@@ -181,14 +182,14 @@ class URL_MATCHER_EXPORT URLMatcherConditionFactory {
// |used_patterns|. These patterns are not referenced any more and get
// freed.
void ForgetUnusedPatterns(
- const std::set<StringPattern::ID>& used_patterns);
+ const std::set<base::MatcherStringPattern::ID>& used_patterns);
// Returns true if this object retains no allocated data. Only for debugging.
bool IsEmpty() const;
private:
// Creates a URLMatcherCondition according to the parameters passed.
- // The URLMatcherCondition will refer to a StringPattern that is
+ // The URLMatcherCondition will refer to a MatcherStringPattern that is
// owned by |pattern_singletons_|.
URLMatcherCondition CreateCondition(URLMatcherCondition::Criterion criterion,
const std::string& pattern);
@@ -205,23 +206,26 @@ class URL_MATCHER_EXPORT URLMatcherConditionFactory {
bool prepend_beginning_of_query_component,
bool append_end_of_query_component) const;
- // Return the next StringPattern id to use.
- int GetNextID();
+ // Return the next MatcherStringPattern id to use.
+ base::MatcherStringPattern::ID GetNextID();
- // Counter that ensures that all created StringPatterns have unique IDs.
- // Note that substring patterns and regex patterns will use different IDs.
- int id_counter_;
+ // Counter that ensures that all created MatcherStringPatterns have unique
+ // IDs. Note that substring patterns and regex patterns will use different
+ // IDs.
+ base::MatcherStringPattern::ID id_counter_ = 0;
// This comparison considers only the pattern() value of the
- // StringPatterns.
- struct StringPatternPointerCompare {
- bool operator()(StringPattern* lhs, StringPattern* rhs) const;
+ // MatcherStringPatterns.
+ struct MatcherStringPatternPointerCompare {
+ bool operator()(base::MatcherStringPattern* lhs,
+ base::MatcherStringPattern* rhs) const;
};
- // Set to ensure that we generate only one StringPattern for each content
- // of StringPattern::pattern().
- using PatternSingletons = std::map<StringPattern*,
- std::unique_ptr<StringPattern>,
- StringPatternPointerCompare>;
+ // Set to ensure that we generate only one MatcherStringPattern for each
+ // content of MatcherStringPattern::pattern().
+ using PatternSingletons =
+ std::map<base::MatcherStringPattern*,
+ std::unique_ptr<base::MatcherStringPattern>,
+ MatcherStringPatternPointerCompare>;
PatternSingletons substring_pattern_singletons_;
PatternSingletons regex_pattern_singletons_;
PatternSingletons origin_and_path_regex_pattern_singletons_;
@@ -264,7 +268,9 @@ class URL_MATCHER_EXPORT URLQueryElementMatcherCondition {
// Returns whether the URL query satisfies the key value constraint.
bool IsMatch(const std::string& canonical_url_query) const;
- const StringPattern* string_pattern() const { return string_pattern_; }
+ const base::MatcherStringPattern* string_pattern() const {
+ return string_pattern_;
+ }
private:
Type match_type_;
@@ -272,7 +278,7 @@ class URL_MATCHER_EXPORT URLQueryElementMatcherCondition {
std::string value_;
size_t key_length_;
size_t value_length_;
- raw_ptr<const StringPattern> string_pattern_;
+ raw_ptr<const base::MatcherStringPattern> string_pattern_;
};
// This class represents a filter for the URL scheme to be hooked up into a
@@ -320,19 +326,18 @@ class URL_MATCHER_EXPORT URLMatcherPortFilter {
class URL_MATCHER_EXPORT URLMatcherConditionSet
: public base::RefCounted<URLMatcherConditionSet> {
public:
- // Valid IDs will be >= 0.
- typedef int ID;
typedef std::set<URLMatcherCondition> Conditions;
typedef std::set<URLQueryElementMatcherCondition> QueryConditions;
- typedef std::vector<scoped_refptr<URLMatcherConditionSet> > Vector;
+ typedef std::vector<scoped_refptr<URLMatcherConditionSet>> Vector;
// Matches if all conditions in |conditions| are fulfilled.
- URLMatcherConditionSet(ID id, const Conditions& conditions);
+ URLMatcherConditionSet(base::MatcherStringPattern::ID id,
+ const Conditions& conditions);
// Matches if all conditions in |conditions|, |scheme_filter| and
// |port_filter| are fulfilled. |scheme_filter| and |port_filter| may be NULL,
// in which case, no restrictions are imposed on the scheme/port of a URL.
- URLMatcherConditionSet(ID id,
+ URLMatcherConditionSet(base::MatcherStringPattern::ID id,
const Conditions& conditions,
std::unique_ptr<URLMatcherSchemeFilter> scheme_filter,
std::unique_ptr<URLMatcherPortFilter> port_filter);
@@ -341,7 +346,7 @@ class URL_MATCHER_EXPORT URLMatcherConditionSet
// |scheme_filter| and |port_filter| are fulfilled. |scheme_filter| and
// |port_filter| may be NULL, in which case, no restrictions are imposed on
// the scheme/port of a URL.
- URLMatcherConditionSet(ID id,
+ URLMatcherConditionSet(base::MatcherStringPattern::ID id,
const Conditions& conditions,
const QueryConditions& query_conditions,
std::unique_ptr<URLMatcherSchemeFilter> scheme_filter,
@@ -350,21 +355,23 @@ class URL_MATCHER_EXPORT URLMatcherConditionSet
URLMatcherConditionSet(const URLMatcherConditionSet&) = delete;
URLMatcherConditionSet& operator=(const URLMatcherConditionSet&) = delete;
- ID id() const { return id_; }
+ base::MatcherStringPattern::ID id() const { return id_; }
const Conditions& conditions() const { return conditions_; }
const QueryConditions& query_conditions() const { return query_conditions_; }
- bool IsMatch(const std::set<StringPattern::ID>& matching_patterns,
- const GURL& url) const;
+ bool IsMatch(
+ const std::set<base::MatcherStringPattern::ID>& matching_patterns,
+ const GURL& url) const;
- bool IsMatch(const std::set<StringPattern::ID>& matching_patterns,
- const GURL& url,
- const std::string& url_for_component_searches) const;
+ bool IsMatch(
+ const std::set<base::MatcherStringPattern::ID>& matching_patterns,
+ const GURL& url,
+ const std::string& url_for_component_searches) const;
private:
friend class base::RefCounted<URLMatcherConditionSet>;
~URLMatcherConditionSet();
- ID id_;
+ base::MatcherStringPattern::ID id_ = 0;
Conditions conditions_;
QueryConditions query_conditions_;
std::unique_ptr<URLMatcherSchemeFilter> scheme_filter_;
@@ -393,13 +400,13 @@ class URL_MATCHER_EXPORT URLMatcher {
// currently registered. This function should be called with large batches
// of |condition_set_ids| at a time to improve performance.
void RemoveConditionSets(
- const std::vector<URLMatcherConditionSet::ID>& condition_set_ids);
+ const std::vector<base::MatcherStringPattern::ID>& condition_set_ids);
// Removes all unused condition sets from the ConditionFactory.
void ClearUnusedConditionSets();
// Returns the IDs of all URLMatcherConditionSet that match to this |url|.
- std::set<URLMatcherConditionSet::ID> MatchURL(const GURL& url) const;
+ std::set<base::MatcherStringPattern::ID> MatchURL(const GURL& url) const;
// Returns the URLMatcherConditionFactory that must be used to create
// URLMatcherConditionSets for this URLMatcher.
@@ -421,19 +428,20 @@ class URL_MATCHER_EXPORT URLMatcher {
// Maps the ID of a URLMatcherConditionSet to the respective
// URLMatcherConditionSet.
- typedef std::map<URLMatcherConditionSet::ID,
- scoped_refptr<URLMatcherConditionSet> >
+ typedef std::map<base::MatcherStringPattern::ID,
+ scoped_refptr<URLMatcherConditionSet>>
URLMatcherConditionSets;
URLMatcherConditionSets url_matcher_condition_sets_;
- // Maps a StringPattern ID to the URLMatcherConditions that need to
- // be triggered in case of a StringPattern match.
- typedef std::map<StringPattern::ID, std::set<URLMatcherConditionSet::ID> >
- StringPatternTriggers;
- StringPatternTriggers substring_match_triggers_;
+ // Maps a MatcherStringPattern ID to the URLMatcherConditions that need to
+ // be triggered in case of a MatcherStringPattern match.
+ typedef std::map<base::MatcherStringPattern::ID,
+ std::set<base::MatcherStringPattern::ID>>
+ MatcherStringPatternTriggers;
+ MatcherStringPatternTriggers substring_match_triggers_;
- std::unique_ptr<SubstringSetMatcher> full_url_matcher_;
- std::unique_ptr<SubstringSetMatcher> url_component_matcher_;
+ std::unique_ptr<base::SubstringSetMatcher> full_url_matcher_;
+ std::unique_ptr<base::SubstringSetMatcher> url_component_matcher_;
RegexSetMatcher regex_set_matcher_;
RegexSetMatcher origin_and_path_regex_set_matcher_;
};
diff --git a/chromium/components/url_matcher/url_matcher_factory.cc b/chromium/components/url_matcher/url_matcher_factory.cc
index 0c94822b93c..cdc11bf81c4 100644
--- a/chromium/components/url_matcher/url_matcher_factory.cc
+++ b/chromium/components/url_matcher/url_matcher_factory.cc
@@ -108,17 +108,16 @@ static base::LazyInstance<URLMatcherConditionFactoryMethods>::DestructorAtExit
scoped_refptr<URLMatcherConditionSet>
URLMatcherFactory::CreateFromURLFilterDictionary(
URLMatcherConditionFactory* url_matcher_condition_factory,
- const base::DictionaryValue* url_filter_dict,
- URLMatcherConditionSet::ID id,
+ const base::Value::Dict& url_filter_dict,
+ base::MatcherStringPattern::ID id,
std::string* error) {
std::unique_ptr<URLMatcherSchemeFilter> url_matcher_schema_filter;
std::unique_ptr<URLMatcherPortFilter> url_matcher_port_filter;
URLMatcherConditionSet::Conditions url_matcher_conditions;
- for (base::DictionaryValue::Iterator iter(*url_filter_dict);
- !iter.IsAtEnd(); iter.Advance()) {
- const std::string& condition_attribute_name = iter.key();
- const base::Value& condition_attribute_value = iter.value();
+ for (const auto iter : url_filter_dict) {
+ const std::string& condition_attribute_name = iter.first;
+ const base::Value& condition_attribute_value = iter.second;
if (IsURLMatcherConditionAttribute(condition_attribute_name)) {
// Handle {host, path, ...}{Prefix, Suffix, Contains, Equals}.
URLMatcherCondition url_matcher_condition =
diff --git a/chromium/components/url_matcher/url_matcher_factory.h b/chromium/components/url_matcher/url_matcher_factory.h
index 4bb3e798940..ab11083666a 100644
--- a/chromium/components/url_matcher/url_matcher_factory.h
+++ b/chromium/components/url_matcher/url_matcher_factory.h
@@ -8,14 +8,10 @@
#include <memory>
#include <string>
+#include "base/values.h"
#include "components/url_matcher/url_matcher.h"
#include "components/url_matcher/url_matcher_export.h"
-namespace base {
-class DictionaryValue;
-class Value;
-}
-
namespace url_matcher {
class URL_MATCHER_EXPORT URLMatcherFactory {
@@ -36,8 +32,8 @@ class URL_MATCHER_EXPORT URLMatcherFactory {
// URLMatcherFactory. Otherwise you leak memory.
static scoped_refptr<URLMatcherConditionSet> CreateFromURLFilterDictionary(
URLMatcherConditionFactory* url_matcher_condition_factory,
- const base::DictionaryValue* url_filter_dict,
- URLMatcherConditionSet::ID id,
+ const base::Value::Dict& url_filter_dict,
+ base::MatcherStringPattern::ID id,
std::string* error);
private:
diff --git a/chromium/components/url_matcher/url_matcher_factory_unittest.cc b/chromium/components/url_matcher/url_matcher_factory_unittest.cc
index aa5c8fe7959..eef54358b87 100644
--- a/chromium/components/url_matcher/url_matcher_factory_unittest.cc
+++ b/chromium/components/url_matcher/url_matcher_factory_unittest.cc
@@ -27,21 +27,21 @@ TEST(URLMatcherFactoryTest, CreateFromURLFilterDictionary) {
scoped_refptr<URLMatcherConditionSet> result;
// Invalid key: {"invalid": "foobar"}
- base::DictionaryValue invalid_condition;
- invalid_condition.SetStringKey("invalid", "foobar");
+ base::Value::Dict invalid_condition;
+ invalid_condition.Set("invalid", "foobar");
// Invalid value type: {"hostSuffix": []}
- base::DictionaryValue invalid_condition2;
- invalid_condition2.SetKey(keys::kHostSuffixKey,
- base::Value(base::Value::Type::LIST));
+ base::Value::Dict invalid_condition2;
+ invalid_condition2.Set(keys::kHostSuffixKey,
+ base::Value(base::Value::Type::LIST));
// Invalid regex value: {"urlMatches": "*"}
- base::DictionaryValue invalid_condition3;
- invalid_condition3.SetStringKey(keys::kURLMatchesKey, "*");
+ base::Value::Dict invalid_condition3;
+ invalid_condition3.Set(keys::kURLMatchesKey, "*");
// Invalid regex value: {"originAndPathMatches": "*"}
- base::DictionaryValue invalid_condition4;
- invalid_condition4.SetStringKey(keys::kOriginAndPathMatchesKey, "*");
+ base::Value::Dict invalid_condition4;
+ invalid_condition4.Set(keys::kOriginAndPathMatchesKey, "*");
// Valid values:
// {
@@ -62,43 +62,43 @@ TEST(URLMatcherFactoryTest, CreateFromURLFilterDictionary) {
base::Value scheme_list(base::Value::Type::LIST);
scheme_list.Append("http");
- base::DictionaryValue valid_condition;
- valid_condition.SetStringKey(keys::kHostSuffixKey, "example.com");
- valid_condition.SetStringKey(keys::kHostPrefixKey, "www");
- valid_condition.SetKey(keys::kPortsKey, std::move(port_ranges));
- valid_condition.SetKey(keys::kSchemesKey, std::move(scheme_list));
+ base::Value::Dict valid_condition;
+ valid_condition.Set(keys::kHostSuffixKey, "example.com");
+ valid_condition.Set(keys::kHostPrefixKey, "www");
+ valid_condition.Set(keys::kPortsKey, std::move(port_ranges));
+ valid_condition.Set(keys::kSchemesKey, std::move(scheme_list));
// Test wrong condition name passed.
error.clear();
result = URLMatcherFactory::CreateFromURLFilterDictionary(
- matcher.condition_factory(), &invalid_condition, 1, &error);
+ matcher.condition_factory(), invalid_condition, 1, &error);
EXPECT_FALSE(error.empty());
EXPECT_FALSE(result);
// Test wrong datatype in hostSuffix.
error.clear();
result = URLMatcherFactory::CreateFromURLFilterDictionary(
- matcher.condition_factory(), &invalid_condition2, 2, &error);
+ matcher.condition_factory(), invalid_condition2, 2, &error);
EXPECT_FALSE(error.empty());
EXPECT_FALSE(result);
// Test invalid regex in urlMatches.
error.clear();
result = URLMatcherFactory::CreateFromURLFilterDictionary(
- matcher.condition_factory(), &invalid_condition3, 3, &error);
+ matcher.condition_factory(), invalid_condition3, 3, &error);
EXPECT_FALSE(error.empty());
EXPECT_FALSE(result);
error.clear();
result = URLMatcherFactory::CreateFromURLFilterDictionary(
- matcher.condition_factory(), &invalid_condition4, 4, &error);
+ matcher.condition_factory(), invalid_condition4, 4, &error);
EXPECT_FALSE(error.empty());
EXPECT_FALSE(result);
// Test success.
error.clear();
result = URLMatcherFactory::CreateFromURLFilterDictionary(
- matcher.condition_factory(), &valid_condition, 100, &error);
+ matcher.condition_factory(), valid_condition, 100, &error);
EXPECT_EQ("", error);
ASSERT_TRUE(result.get());
@@ -125,39 +125,35 @@ TEST(URLMatcherFactoryTest, UpperCase) {
scoped_refptr<URLMatcherConditionSet> result;
// {"hostContains": "exaMple"}
- base::DictionaryValue invalid_condition1;
- invalid_condition1.SetString(keys::kHostContainsKey, "exaMple");
+ base::Value::Dict invalid_condition1;
+ invalid_condition1.Set(keys::kHostContainsKey, "exaMple");
// {"hostSuffix": ".Com"}
- base::DictionaryValue invalid_condition2;
- invalid_condition2.SetString(keys::kHostSuffixKey, ".Com");
+ base::Value::Dict invalid_condition2;
+ invalid_condition2.Set(keys::kHostSuffixKey, ".Com");
// {"hostPrefix": "WWw."}
- base::DictionaryValue invalid_condition3;
- invalid_condition3.SetString(keys::kHostPrefixKey, "WWw.");
+ base::Value::Dict invalid_condition3;
+ invalid_condition3.Set(keys::kHostPrefixKey, "WWw.");
// {"hostEquals": "WWW.example.Com"}
- base::DictionaryValue invalid_condition4;
- invalid_condition4.SetString(keys::kHostEqualsKey, "WWW.example.Com");
+ base::Value::Dict invalid_condition4;
+ invalid_condition4.Set(keys::kHostEqualsKey, "WWW.example.Com");
// {"scheme": ["HTTP"]}
- auto scheme_list = std::make_unique<base::ListValue>();
- scheme_list->Append("HTTP");
- base::DictionaryValue invalid_condition5;
+ base::Value::List scheme_list;
+ scheme_list.Append("HTTP");
+ base::Value::Dict invalid_condition5;
invalid_condition5.Set(keys::kSchemesKey, std::move(scheme_list));
- const base::DictionaryValue* invalid_conditions[] = {
- &invalid_condition1,
- &invalid_condition2,
- &invalid_condition3,
- &invalid_condition4,
- &invalid_condition5
- };
+ const base::Value::Dict* invalid_conditions[] = {
+ &invalid_condition1, &invalid_condition2, &invalid_condition3,
+ &invalid_condition4, &invalid_condition5};
for (size_t i = 0; i < std::size(invalid_conditions); ++i) {
error.clear();
result = URLMatcherFactory::CreateFromURLFilterDictionary(
- matcher.condition_factory(), invalid_conditions[i], 1, &error);
+ matcher.condition_factory(), *invalid_conditions[i], 1, &error);
EXPECT_FALSE(error.empty()) << "in iteration " << i;
EXPECT_FALSE(result) << "in iteration " << i;
}
@@ -232,14 +228,14 @@ void UrlConditionCaseTest::Test() const {
void UrlConditionCaseTest::CheckCondition(
const std::string& value,
UrlConditionCaseTest::ResultType expected_result) const {
- base::DictionaryValue condition;
+ base::Value::Dict condition;
if (use_list_of_strings_) {
auto list = std::make_unique<base::ListValue>();
list->Append(value);
- condition.SetKey(condition_key_,
- base::Value::FromUniquePtrValue(std::move(list)));
+ condition.Set(condition_key_,
+ base::Value::FromUniquePtrValue(std::move(list)));
} else {
- condition.SetKey(condition_key_, base::Value(value));
+ condition.Set(condition_key_, base::Value(value));
}
URLMatcher matcher;
@@ -247,7 +243,7 @@ void UrlConditionCaseTest::CheckCondition(
scoped_refptr<URLMatcherConditionSet> result;
result = URLMatcherFactory::CreateFromURLFilterDictionary(
- matcher.condition_factory(), &condition, 1, &error);
+ matcher.condition_factory(), condition, 1, &error);
if (expected_result == CREATE_FAILURE) {
EXPECT_FALSE(error.empty());
EXPECT_FALSE(result);
diff --git a/chromium/components/url_matcher/url_matcher_unittest.cc b/chromium/components/url_matcher/url_matcher_unittest.cc
index 698c5015e61..6d5545f1ffb 100644
--- a/chromium/components/url_matcher/url_matcher_unittest.cc
+++ b/chromium/components/url_matcher/url_matcher_unittest.cc
@@ -13,6 +13,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+using base::MatcherStringPattern;
+
namespace url_matcher {
//
@@ -20,7 +22,7 @@ namespace url_matcher {
//
TEST(URLMatcherConditionTest, Constructors) {
- StringPattern pattern("example.com", 1);
+ MatcherStringPattern pattern("example.com", 1);
URLMatcherCondition m1(URLMatcherCondition::HOST_SUFFIX, &pattern);
EXPECT_EQ(URLMatcherCondition::HOST_SUFFIX, m1.criterion());
EXPECT_EQ(&pattern, m1.string_pattern());
@@ -66,35 +68,35 @@ TEST(URLMatcherPortFilter, TestMatching) {
}
TEST(URLMatcherConditionTest, IsFullURLCondition) {
- StringPattern pattern("example.com", 1);
- EXPECT_FALSE(URLMatcherCondition(URLMatcherCondition::HOST_SUFFIX,
- &pattern).IsFullURLCondition());
-
- EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::HOST_CONTAINS,
- &pattern).IsFullURLCondition());
- EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::PATH_CONTAINS,
- &pattern).IsFullURLCondition());
- EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::QUERY_CONTAINS,
- &pattern).IsFullURLCondition());
-
- EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_PREFIX,
- &pattern).IsFullURLCondition());
- EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_SUFFIX,
- &pattern).IsFullURLCondition());
- EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_CONTAINS,
- &pattern).IsFullURLCondition());
- EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_EQUALS,
- &pattern).IsFullURLCondition());
+ MatcherStringPattern pattern("example.com", 1);
+ EXPECT_FALSE(URLMatcherCondition(URLMatcherCondition::HOST_SUFFIX, &pattern)
+ .IsFullURLCondition());
+
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::HOST_CONTAINS, &pattern)
+ .IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::PATH_CONTAINS, &pattern)
+ .IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::QUERY_CONTAINS, &pattern)
+ .IsFullURLCondition());
+
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_PREFIX, &pattern)
+ .IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_SUFFIX, &pattern)
+ .IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_CONTAINS, &pattern)
+ .IsFullURLCondition());
+ EXPECT_TRUE(URLMatcherCondition(URLMatcherCondition::URL_EQUALS, &pattern)
+ .IsFullURLCondition());
}
TEST(URLMatcherConditionTest, IsMatch) {
GURL url1("http://www.example.com/www.foobar.com/index.html");
GURL url2("http://www.foobar.com/example.com/index.html");
- StringPattern pattern("example.com", 1);
+ MatcherStringPattern pattern("example.com", 1);
URLMatcherCondition m1(URLMatcherCondition::HOST_SUFFIX, &pattern);
- std::set<StringPattern::ID> matching_patterns;
+ std::set<MatcherStringPattern::ID> matching_patterns;
// matches = {0} --> matcher did not indicate that m1 was a match.
matching_patterns.insert(0);
@@ -113,8 +115,8 @@ TEST(URLMatcherConditionTest, IsMatch) {
}
TEST(URLMatcherConditionTest, Comparison) {
- StringPattern p1("foobar.com", 1);
- StringPattern p2("foobar.com", 2);
+ MatcherStringPattern p1("foobar.com", 1);
+ MatcherStringPattern p2("foobar.com", 2);
// The first component of each test is expected to be < than the second.
URLMatcherCondition test_smaller[][2] = {
{URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, &p1),
@@ -153,8 +155,7 @@ TEST(URLMatcherConditionTest, Comparison) {
namespace {
bool Matches(const URLMatcherCondition& condition, const std::string& text) {
- return text.find(condition.string_pattern()->pattern()) !=
- std::string::npos;
+ return text.find(condition.string_pattern()->pattern()) != std::string::npos;
}
} // namespace
@@ -196,12 +197,12 @@ TEST(URLMatcherConditionFactoryTest, Criteria) {
factory.CreateQueryContainsCondition("foo").criterion());
EXPECT_EQ(URLMatcherCondition::QUERY_EQUALS,
factory.CreateQueryEqualsCondition("foo").criterion());
- EXPECT_EQ(URLMatcherCondition::HOST_SUFFIX_PATH_PREFIX,
- factory.CreateHostSuffixPathPrefixCondition("foo",
- "bar").criterion());
- EXPECT_EQ(URLMatcherCondition::HOST_EQUALS_PATH_PREFIX,
- factory.CreateHostEqualsPathPrefixCondition("foo",
- "bar").criterion());
+ EXPECT_EQ(
+ URLMatcherCondition::HOST_SUFFIX_PATH_PREFIX,
+ factory.CreateHostSuffixPathPrefixCondition("foo", "bar").criterion());
+ EXPECT_EQ(
+ URLMatcherCondition::HOST_EQUALS_PATH_PREFIX,
+ factory.CreateHostEqualsPathPrefixCondition("foo", "bar").criterion());
EXPECT_EQ(URLMatcherCondition::URL_PREFIX,
factory.CreateURLPrefixCondition("foo").criterion());
EXPECT_EQ(URLMatcherCondition::URL_SUFFIX,
@@ -224,8 +225,7 @@ TEST(URLMatcherConditionFactoryTest, TestSingletonProperty) {
EXPECT_EQ(c2.criterion(), c3.criterion());
EXPECT_NE(c2.string_pattern(), c3.string_pattern());
EXPECT_NE(c2.string_pattern()->id(), c3.string_pattern()->id());
- EXPECT_NE(c2.string_pattern()->pattern(),
- c3.string_pattern()->pattern());
+ EXPECT_NE(c2.string_pattern()->pattern(), c3.string_pattern()->pattern());
URLMatcherCondition c4 = factory.CreateURLMatchesCondition("www.google.com");
URLMatcherCondition c5 = factory.CreateURLContainsCondition("www.google.com");
// Regex patterns and substring patterns do not share IDs.
@@ -233,11 +233,11 @@ TEST(URLMatcherConditionFactoryTest, TestSingletonProperty) {
EXPECT_NE(c5.string_pattern(), c4.string_pattern());
EXPECT_NE(c5.string_pattern()->id(), c4.string_pattern()->id());
- // Check that all StringPattern singletons are freed if we call
+ // Check that all MatcherStringPattern singletons are freed if we call
// ForgetUnusedPatterns.
- StringPattern::ID old_id_1 = c1.string_pattern()->id();
- StringPattern::ID old_id_4 = c4.string_pattern()->id();
- factory.ForgetUnusedPatterns(std::set<StringPattern::ID>());
+ MatcherStringPattern::ID old_id_1 = c1.string_pattern()->id();
+ MatcherStringPattern::ID old_id_4 = c4.string_pattern()->id();
+ factory.ForgetUnusedPatterns(std::set<MatcherStringPattern::ID>());
EXPECT_TRUE(factory.IsEmpty());
URLMatcherCondition c6 = factory.CreateHostEqualsCondition("www.google.com");
EXPECT_NE(old_id_1, c6.string_pattern()->id());
@@ -247,10 +247,12 @@ TEST(URLMatcherConditionFactoryTest, TestSingletonProperty) {
TEST(URLMatcherConditionFactoryTest, TestComponentSearches) {
URLMatcherConditionFactory factory;
- GURL gurl("https://www.google.com:1234/webhp?sourceid=chrome-instant&ie=UTF-8"
+ GURL gurl(
+ "https://www.google.com:1234/webhp?sourceid=chrome-instant&ie=UTF-8"
"&ion=1#hl=en&output=search&sclient=psy-ab&q=chrome%20is%20awesome");
std::string url = factory.CanonicalizeURLForComponentSearches(gurl);
- GURL gurl2("https://www.google.com.:1234/webhp?sourceid=chrome-instant"
+ GURL gurl2(
+ "https://www.google.com.:1234/webhp?sourceid=chrome-instant"
"&ie=UTF-8&ion=1#hl=en&output=search&sclient=psy-ab"
"&q=chrome%20is%20awesome");
std::string url2 = factory.CanonicalizeURLForComponentSearches(gurl2);
@@ -318,7 +320,6 @@ TEST(URLMatcherConditionFactoryTest, TestComponentSearches) {
EXPECT_FALSE(
Matches(factory.CreatePathEqualsCondition("www.google.com"), url));
-
// Test query component.
EXPECT_TRUE(Matches(factory.CreateQueryPrefixCondition(std::string()), url));
EXPECT_TRUE(Matches(factory.CreateQueryPrefixCondition("sourceid"), url));
@@ -330,32 +331,41 @@ TEST(URLMatcherConditionFactoryTest, TestComponentSearches) {
EXPECT_FALSE(Matches(factory.CreateQuerySuffixCondition("www"), url));
// "Suffix" condition + pattern starting with '?' = "equals" condition.
EXPECT_FALSE(Matches(factory.CreateQuerySuffixCondition(
- "?sourceid=chrome-instant&ie=UTF-8&ion="), url));
+ "?sourceid=chrome-instant&ie=UTF-8&ion="),
+ url));
EXPECT_TRUE(Matches(factory.CreateQuerySuffixCondition(
- "?sourceid=chrome-instant&ie=UTF-8&ion=1"), url));
+ "?sourceid=chrome-instant&ie=UTF-8&ion=1"),
+ url));
EXPECT_FALSE(Matches(factory.CreateQueryEqualsCondition(
- "?sourceid=chrome-instant&ie=UTF-8&ion="), url));
+ "?sourceid=chrome-instant&ie=UTF-8&ion="),
+ url));
EXPECT_FALSE(Matches(factory.CreateQueryEqualsCondition(
- "sourceid=chrome-instant&ie=UTF-8&ion="), url));
+ "sourceid=chrome-instant&ie=UTF-8&ion="),
+ url));
EXPECT_TRUE(Matches(factory.CreateQueryEqualsCondition(
- "sourceid=chrome-instant&ie=UTF-8&ion=1"), url));
+ "sourceid=chrome-instant&ie=UTF-8&ion=1"),
+ url));
// The '?' at the beginning is just ignored.
EXPECT_TRUE(Matches(factory.CreateQueryEqualsCondition(
- "?sourceid=chrome-instant&ie=UTF-8&ion=1"), url));
+ "?sourceid=chrome-instant&ie=UTF-8&ion=1"),
+ url));
EXPECT_FALSE(
Matches(factory.CreateQueryEqualsCondition("www.google.com"), url));
-
// Test adjacent components
- EXPECT_TRUE(Matches(factory.CreateHostSuffixPathPrefixCondition(
- "google.com", "/webhp"), url));
- EXPECT_TRUE(Matches(factory.CreateHostSuffixPathPrefixCondition(
- "google.com", "/webhp"), url2));
- EXPECT_TRUE(Matches(factory.CreateHostSuffixPathPrefixCondition(
- "google.com.", "/webhp"), url));
- EXPECT_TRUE(Matches(factory.CreateHostSuffixPathPrefixCondition(
- "google.com.", "/webhp"), url2));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostSuffixPathPrefixCondition("google.com", "/webhp"),
+ url));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostSuffixPathPrefixCondition("google.com", "/webhp"),
+ url2));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostSuffixPathPrefixCondition("google.com.", "/webhp"),
+ url));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostSuffixPathPrefixCondition("google.com.", "/webhp"),
+ url2));
EXPECT_TRUE(Matches(
factory.CreateHostSuffixPathPrefixCondition(std::string(), "/webhp"),
url));
@@ -365,14 +375,18 @@ TEST(URLMatcherConditionFactoryTest, TestComponentSearches) {
EXPECT_FALSE(Matches(
factory.CreateHostSuffixPathPrefixCondition("www", std::string()), url));
- EXPECT_TRUE(Matches(factory.CreateHostEqualsPathPrefixCondition(
- "www.google.com", "/webhp"), url));
- EXPECT_TRUE(Matches(factory.CreateHostEqualsPathPrefixCondition(
- "www.google.com", "/webhp"), url2));
- EXPECT_TRUE(Matches(factory.CreateHostEqualsPathPrefixCondition(
- ".www.google.com.", "/webhp"), url));
- EXPECT_TRUE(Matches(factory.CreateHostEqualsPathPrefixCondition(
- ".www.google.com.", "/webhp"), url2));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostEqualsPathPrefixCondition("www.google.com", "/webhp"),
+ url));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostEqualsPathPrefixCondition("www.google.com", "/webhp"),
+ url2));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostEqualsPathPrefixCondition(".www.google.com.", "/webhp"),
+ url));
+ EXPECT_TRUE(Matches(
+ factory.CreateHostEqualsPathPrefixCondition(".www.google.com.", "/webhp"),
+ url2));
EXPECT_FALSE(Matches(
factory.CreateHostEqualsPathPrefixCondition(std::string(), "/webhp"),
url));
@@ -386,7 +400,8 @@ TEST(URLMatcherConditionFactoryTest, TestComponentSearches) {
TEST(URLMatcherConditionFactoryTest, TestFullSearches) {
// The Port 443 is stripped because it is the default port for https.
- GURL gurl("https://www.google.com:443/webhp?sourceid=chrome-instant&ie=UTF-8"
+ GURL gurl(
+ "https://www.google.com:443/webhp?sourceid=chrome-instant&ie=UTF-8"
"&ion=1#hl=en&output=search&sclient=psy-ab&q=chrome%20is%20awesome");
URLMatcherConditionFactory factory;
std::string url = factory.CanonicalizeURLForFullSearches(gurl);
@@ -394,12 +409,12 @@ TEST(URLMatcherConditionFactoryTest, TestFullSearches) {
EXPECT_TRUE(Matches(factory.CreateURLPrefixCondition(std::string()), url));
EXPECT_TRUE(
Matches(factory.CreateURLPrefixCondition("https://www.goog"), url));
- EXPECT_TRUE(Matches(factory.CreateURLPrefixCondition(
- "https://www.google.com"), url));
- EXPECT_TRUE(Matches(factory.CreateURLPrefixCondition(
- "https://www.google.com/webhp?"), url));
- EXPECT_FALSE(Matches(factory.CreateURLPrefixCondition(
- "http://www.google.com"), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateURLPrefixCondition("https://www.google.com"), url));
+ EXPECT_TRUE(Matches(
+ factory.CreateURLPrefixCondition("https://www.google.com/webhp?"), url));
+ EXPECT_FALSE(
+ Matches(factory.CreateURLPrefixCondition("http://www.google.com"), url));
EXPECT_FALSE(Matches(factory.CreateURLPrefixCondition("webhp"), url));
EXPECT_TRUE(Matches(factory.CreateURLSuffixCondition(std::string()), url));
@@ -418,18 +433,21 @@ TEST(URLMatcherConditionFactoryTest, TestFullSearches) {
EXPECT_FALSE(Matches(factory.CreateURLContainsCondition(":443"), url));
EXPECT_TRUE(Matches(factory.CreateURLEqualsCondition(
- "https://www.google.com/webhp?sourceid=chrome-instant&ie=UTF-8&ion=1"),
- url));
+ "https://www.google.com/"
+ "webhp?sourceid=chrome-instant&ie=UTF-8&ion=1"),
+ url));
EXPECT_FALSE(
Matches(factory.CreateURLEqualsCondition("https://www.google.com"), url));
// Same as above but this time with a non-standard port.
- gurl = GURL("https://www.google.com:1234/webhp?sourceid=chrome-instant&"
+ gurl = GURL(
+ "https://www.google.com:1234/webhp?sourceid=chrome-instant&"
"ie=UTF-8&ion=1#hl=en&output=search&sclient=psy-ab&q=chrome%20is%20"
"awesome");
url = factory.CanonicalizeURLForFullSearches(gurl);
- EXPECT_TRUE(Matches(factory.CreateURLPrefixCondition(
- "https://www.google.com:1234/webhp?"), url));
+ EXPECT_TRUE(Matches(
+ factory.CreateURLPrefixCondition("https://www.google.com:1234/webhp?"),
+ url));
EXPECT_TRUE(Matches(factory.CreateURLContainsCondition(":1234"), url));
}
@@ -448,7 +466,7 @@ TEST(URLMatcherConditionSetTest, Constructor) {
scoped_refptr<URLMatcherConditionSet> condition_set(
new URLMatcherConditionSet(1, conditions));
- EXPECT_EQ(1, condition_set->id());
+ EXPECT_EQ(1u, condition_set->id());
EXPECT_EQ(2u, condition_set->conditions().size());
}
@@ -468,10 +486,10 @@ TEST(URLMatcherConditionSetTest, Matching) {
scoped_refptr<URLMatcherConditionSet> condition_set(
new URLMatcherConditionSet(1, conditions));
- EXPECT_EQ(1, condition_set->id());
+ EXPECT_EQ(1u, condition_set->id());
EXPECT_EQ(2u, condition_set->conditions().size());
- std::set<StringPattern::ID> matching_patterns;
+ std::set<MatcherStringPattern::ID> matching_patterns;
matching_patterns.insert(m1.string_pattern()->id());
EXPECT_FALSE(condition_set->IsMatch(matching_patterns, url1));
@@ -550,12 +568,8 @@ bool IsQueryMatch(
conditions.insert(m1);
conditions.insert(m2);
- URLQueryElementMatcherCondition q1(key,
- value,
- query_value_match_type,
- query_element_type,
- match_type,
- &factory);
+ URLQueryElementMatcherCondition q1(key, value, query_value_match_type,
+ query_element_type, match_type, &factory);
URLMatcherConditionSet::QueryConditions query_conditions;
query_conditions.insert(q1);
@@ -580,233 +594,157 @@ bool IsQueryMatch(
} // namespace
TEST(URLMatcherConditionSetTest, QueryMatching) {
+ EXPECT_TRUE(IsQueryMatch(
+ "a=foo&b=foo&a=barr", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "bar",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_ANY));
+ EXPECT_FALSE(IsQueryMatch(
+ "a=foo&b=foo&a=barr", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "bar",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_ANY));
EXPECT_TRUE(
- IsQueryMatch("a=foo&b=foo&a=barr",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "bar",
+ IsQueryMatch("a=foo&b=foo&a=barr", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY, "bar",
URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
URLQueryElementMatcherCondition::MATCH_ANY));
EXPECT_FALSE(
- IsQueryMatch("a=foo&b=foo&a=barr",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "bar",
+ IsQueryMatch("a=foo&b=foo&a=barr", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY, "bar",
URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
URLQueryElementMatcherCondition::MATCH_ANY));
- EXPECT_TRUE(
- IsQueryMatch("a=foo&b=foo&a=barr",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "bar",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_ANY));
+ EXPECT_TRUE(IsQueryMatch(
+ "a&b=foo&a=barr", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
+ "bar", URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_ANY));
EXPECT_FALSE(
- IsQueryMatch("a=foo&b=foo&a=barr",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "bar",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_ANY));
- EXPECT_TRUE(
- IsQueryMatch("a&b=foo&a=barr",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "bar",
+ IsQueryMatch("a=foo&b=foo&a=barr", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY, "bar",
URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
URLQueryElementMatcherCondition::MATCH_ANY));
- EXPECT_FALSE(
- IsQueryMatch("a=foo&b=foo&a=barr",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "bar",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_ANY));
-
- EXPECT_FALSE(
- IsQueryMatch("a=foo&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "bar",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_TRUE(
- IsQueryMatch("a=bar&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "bar",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_TRUE(
- IsQueryMatch("a=bar&b=foo&a=bar",
- "b",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_FALSE(
- IsQueryMatch("a=bar&b=foo&a=bar",
- "b",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "goo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_FALSE(
- IsQueryMatch("a=bar&b=foo&a=bar",
- "c",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "goo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_TRUE(
- IsQueryMatch("a=foo1&b=foo&a=foo2",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_FALSE(
- IsQueryMatch("a=foo1&b=foo&a=fo02",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_TRUE(
- IsQueryMatch("a&b=foo&a",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_TRUE(
- IsQueryMatch("alt&b=foo",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_TRUE(
- IsQueryMatch("b=foo&a",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_FALSE(
- IsQueryMatch("b=foo",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_ALL));
- EXPECT_TRUE(
- IsQueryMatch("b=foo&a",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_ALL));
-
- EXPECT_TRUE(
- IsQueryMatch("a=foo&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_FIRST));
- EXPECT_FALSE(
- IsQueryMatch("a=foo&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "bar",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_FIRST));
- EXPECT_TRUE(
- IsQueryMatch("a=foo1&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_FIRST));
- EXPECT_FALSE(
- IsQueryMatch("a=foo1&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_FIRST));
- EXPECT_TRUE(
- IsQueryMatch("a&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_FIRST));
- EXPECT_TRUE(
- IsQueryMatch("alt&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_FIRST));
- EXPECT_FALSE(
- IsQueryMatch("alt&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_FIRST));
- EXPECT_FALSE(
- IsQueryMatch("a=foo&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_LAST));
- EXPECT_TRUE(
- IsQueryMatch("a=foo&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "bar",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_LAST));
- EXPECT_FALSE(
- IsQueryMatch("a=foo1&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_LAST));
- EXPECT_TRUE(
- IsQueryMatch("a=foo1&b=foo&a=bar1",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE,
- "bar",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_LAST));
- EXPECT_FALSE(
- IsQueryMatch("a&b=foo&a=bar",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_LAST));
- EXPECT_TRUE(
- IsQueryMatch("b=foo&alt",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
- URLQueryElementMatcherCondition::MATCH_LAST));
- EXPECT_FALSE(
- IsQueryMatch("b=foo&alt",
- "a",
- URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
- "foo",
- URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
- URLQueryElementMatcherCondition::MATCH_LAST));
+ EXPECT_FALSE(IsQueryMatch(
+ "a=foo&b=foo&a=bar", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "bar",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_TRUE(IsQueryMatch(
+ "a=bar&b=foo&a=bar", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "bar",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_TRUE(IsQueryMatch(
+ "a=bar&b=foo&a=bar", "b",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_FALSE(IsQueryMatch(
+ "a=bar&b=foo&a=bar", "b",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "goo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_FALSE(IsQueryMatch(
+ "a=bar&b=foo&a=bar", "c",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "goo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_TRUE(IsQueryMatch(
+ "a=foo1&b=foo&a=foo2", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_FALSE(IsQueryMatch(
+ "a=foo1&b=foo&a=fo02", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_TRUE(IsQueryMatch(
+ "a&b=foo&a", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
+ "foo", URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_TRUE(IsQueryMatch(
+ "alt&b=foo", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
+ "foo", URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_TRUE(IsQueryMatch(
+ "b=foo&a", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_FALSE(IsQueryMatch(
+ "b=foo", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+ EXPECT_TRUE(IsQueryMatch(
+ "b=foo&a", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_ALL));
+
+ EXPECT_TRUE(IsQueryMatch(
+ "a=foo&b=foo&a=bar", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_FIRST));
+ EXPECT_FALSE(IsQueryMatch(
+ "a=foo&b=foo&a=bar", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "bar",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_FIRST));
+ EXPECT_TRUE(IsQueryMatch(
+ "a=foo1&b=foo&a=bar", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_FIRST));
+ EXPECT_FALSE(IsQueryMatch(
+ "a=foo1&b=foo&a=bar", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_FIRST));
+ EXPECT_TRUE(IsQueryMatch(
+ "a&b=foo&a=bar", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
+ "foo", URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_FIRST));
+ EXPECT_TRUE(IsQueryMatch(
+ "alt&b=foo&a=bar", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
+ "foo", URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_FIRST));
+ EXPECT_FALSE(IsQueryMatch(
+ "alt&b=foo&a=bar", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
+ "foo", URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_FIRST));
+
+ EXPECT_FALSE(IsQueryMatch(
+ "a=foo&b=foo&a=bar", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_LAST));
+ EXPECT_TRUE(IsQueryMatch(
+ "a=foo&b=foo&a=bar", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "bar",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_LAST));
+ EXPECT_FALSE(IsQueryMatch(
+ "a=foo1&b=foo&a=bar", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "foo",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_LAST));
+ EXPECT_TRUE(IsQueryMatch(
+ "a=foo1&b=foo&a=bar1", "a",
+ URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY_VALUE, "bar",
+ URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_LAST));
+ EXPECT_FALSE(IsQueryMatch(
+ "a&b=foo&a=bar", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
+ "foo", URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_LAST));
+ EXPECT_TRUE(IsQueryMatch(
+ "b=foo&alt", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
+ "foo", URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_PREFIX,
+ URLQueryElementMatcherCondition::MATCH_LAST));
+ EXPECT_FALSE(IsQueryMatch(
+ "b=foo&alt", "a", URLQueryElementMatcherCondition::ELEMENT_TYPE_KEY,
+ "foo", URLQueryElementMatcherCondition::QUERY_VALUE_MATCH_EXACT,
+ URLQueryElementMatcherCondition::MATCH_LAST));
}
//
@@ -825,7 +763,7 @@ TEST(URLMatcherTest, FullTest) {
conditions1.insert(factory->CreateHostSuffixCondition("example.com"));
conditions1.insert(factory->CreatePathContainsCondition("foo"));
- const int kConditionSetId1 = 1;
+ const base::MatcherStringPattern::ID kConditionSetId1 = 1;
URLMatcherConditionSet::Vector insert1;
insert1.push_back(base::MakeRefCounted<URLMatcherConditionSet>(
kConditionSetId1, conditions1));
@@ -837,7 +775,7 @@ TEST(URLMatcherTest, FullTest) {
URLMatcherConditionSet::Conditions conditions2;
conditions2.insert(factory->CreateHostSuffixCondition("example.com"));
- const int kConditionSetId2 = 2;
+ const base::MatcherStringPattern::ID kConditionSetId2 = 2;
URLMatcherConditionSet::Vector insert2;
insert2.push_back(base::MakeRefCounted<URLMatcherConditionSet>(
kConditionSetId2, conditions2));
@@ -846,15 +784,15 @@ TEST(URLMatcherTest, FullTest) {
EXPECT_EQ(1u, matcher.MatchURL(url2).size());
// This should be the cached singleton.
- int patternId1 = factory->CreateHostSuffixCondition(
- "example.com").string_pattern()->id();
+ base::MatcherStringPattern::ID patternId1 =
+ factory->CreateHostSuffixCondition("example.com").string_pattern()->id();
// Third insert.
URLMatcherConditionSet::Conditions conditions3;
conditions3.insert(factory->CreateHostSuffixCondition("example.com"));
conditions3.insert(factory->CreateURLMatchesCondition("x.*[0-9]"));
- const int kConditionSetId3 = 3;
+ const base::MatcherStringPattern::ID kConditionSetId3 = 3;
URLMatcherConditionSet::Vector insert3;
insert3.push_back(base::MakeRefCounted<URLMatcherConditionSet>(
kConditionSetId3, conditions3));
@@ -863,21 +801,21 @@ TEST(URLMatcherTest, FullTest) {
EXPECT_EQ(1u, matcher.MatchURL(url2).size());
// Removal of third insert.
- std::vector<URLMatcherConditionSet::ID> remove3;
+ std::vector<base::MatcherStringPattern::ID> remove3;
remove3.push_back(kConditionSetId3);
matcher.RemoveConditionSets(remove3);
EXPECT_EQ(2u, matcher.MatchURL(url1).size());
EXPECT_EQ(1u, matcher.MatchURL(url2).size());
// Removal of second insert.
- std::vector<URLMatcherConditionSet::ID> remove2;
+ std::vector<base::MatcherStringPattern::ID> remove2;
remove2.push_back(kConditionSetId2);
matcher.RemoveConditionSets(remove2);
EXPECT_EQ(1u, matcher.MatchURL(url1).size());
EXPECT_EQ(0u, matcher.MatchURL(url2).size());
// Removal of first insert.
- std::vector<URLMatcherConditionSet::ID> remove1;
+ std::vector<base::MatcherStringPattern::ID> remove1;
remove1.push_back(kConditionSetId1);
matcher.RemoveConditionSets(remove1);
EXPECT_EQ(0u, matcher.MatchURL(url1).size());
@@ -887,8 +825,8 @@ TEST(URLMatcherTest, FullTest) {
// The cached singleton in matcher.condition_factory_ should be destroyed to
// free memory.
- int patternId2 = factory->CreateHostSuffixCondition(
- "example.com").string_pattern()->id();
+ base::MatcherStringPattern::ID patternId2 =
+ factory->CreateHostSuffixCondition("example.com").string_pattern()->id();
// If patternId1 and patternId2 are different that indicates that
// matcher.condition_factory_ does not leak memory by holding onto
// unused patterns.
@@ -940,7 +878,7 @@ TEST(URLMatcherTest, TestComponentsImplyContains) {
// Due to '?' the condition created here is a prefix-testing condition.
conditions.insert(factory->CreateQueryContainsCondition("?test=val&a=b"));
- const int kConditionSetId = 1;
+ const base::MatcherStringPattern::ID kConditionSetId = 1;
URLMatcherConditionSet::Vector insert;
insert.push_back(base::MakeRefCounted<URLMatcherConditionSet>(kConditionSetId,
conditions));
@@ -958,7 +896,7 @@ TEST(URLMatcherTest, TestOriginAndPathRegExPositive) {
URLMatcherConditionSet::Conditions conditions;
conditions.insert(factory->CreateOriginAndPathMatchesCondition("w..hp"));
- const int kConditionSetId = 1;
+ const base::MatcherStringPattern::ID kConditionSetId = 1;
URLMatcherConditionSet::Vector insert;
insert.push_back(base::MakeRefCounted<URLMatcherConditionSet>(kConditionSetId,
conditions));
@@ -976,7 +914,7 @@ TEST(URLMatcherTest, TestOriginAndPathRegExNegative) {
URLMatcherConditionSet::Conditions conditions;
conditions.insert(factory->CreateOriginAndPathMatchesCondition("val"));
- const int kConditionSetId = 1;
+ const base::MatcherStringPattern::ID kConditionSetId = 1;
URLMatcherConditionSet::Vector insert;
insert.push_back(base::MakeRefCounted<URLMatcherConditionSet>(kConditionSetId,
conditions));
diff --git a/chromium/components/url_matcher/url_util.cc b/chromium/components/url_matcher/url_util.cc
index af3a52abe4a..4cec96d7196 100644
--- a/chromium/components/url_matcher/url_util.cc
+++ b/chromium/components/url_matcher/url_util.cc
@@ -10,12 +10,12 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/no_destructor.h"
+#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "components/google/core/common/google_util.h"
#include "components/url_formatter/url_fixer.h"
#include "components/url_matcher/url_matcher.h"
-#include "net/base/escape.h"
#include "net/base/filename_util.h"
#include "net/base/url_util.h"
#include "third_party/re2/src/re2/re2.h"
@@ -139,7 +139,7 @@ class EmbeddedURLExtractor {
if (re2::RE2::FullMatch(url.path(), google_amp_viewer_path_regex_, &s,
&embedded)) {
// The embedded URL may be percent-encoded. Undo that.
- embedded = net::UnescapeBinaryURLComponent(embedded);
+ embedded = base::UnescapeBinaryURLComponent(embedded);
return BuildURL(!s.empty(), embedded);
}
}
@@ -260,7 +260,7 @@ bool FilterComponents::IsWildcard() const {
scoped_refptr<URLMatcherConditionSet> CreateConditionSet(
URLMatcher* url_matcher,
- int id,
+ base::MatcherStringPattern::ID id,
const std::string& scheme,
const std::string& host,
bool match_subdomains,
@@ -410,9 +410,9 @@ bool FilterToComponents(const std::string& filter,
void AddFilters(URLMatcher* matcher,
bool allow,
- URLMatcherConditionSet::ID* id,
+ base::MatcherStringPattern::ID* id,
const base::ListValue* patterns,
- std::map<url_matcher::URLMatcherConditionSet::ID,
+ std::map<base::MatcherStringPattern::ID,
url_matcher::util::FilterComponents>* filters) {
URLMatcherConditionSet::Vector all_conditions;
base::Value::ConstListView patterns_list = patterns->GetListDeprecated();
@@ -449,9 +449,9 @@ void AddFilters(URLMatcher* matcher,
void AddFilters(URLMatcher* matcher,
bool allow,
- URLMatcherConditionSet::ID* id,
+ base::MatcherStringPattern::ID* id,
const std::vector<std::string>& patterns,
- std::map<url_matcher::URLMatcherConditionSet::ID,
+ std::map<base::MatcherStringPattern::ID,
url_matcher::util::FilterComponents>* filters) {
URLMatcherConditionSet::Vector all_conditions;
size_t size = std::min(kMaxFiltersAllowed, patterns.size());
@@ -481,13 +481,13 @@ void AddFilters(URLMatcher* matcher,
void AddAllowFilters(url_matcher::URLMatcher* matcher,
const base::ListValue* patterns) {
- url_matcher::URLMatcherConditionSet::ID id(0);
+ base::MatcherStringPattern::ID id(0);
AddFilters(matcher, true, &id, patterns);
}
void AddAllowFilters(url_matcher::URLMatcher* matcher,
const std::vector<std::string>& patterns) {
- url_matcher::URLMatcherConditionSet::ID id(0);
+ base::MatcherStringPattern::ID id(0);
AddFilters(matcher, true, &id, patterns);
}
diff --git a/chromium/components/url_matcher/url_util.h b/chromium/components/url_matcher/url_util.h
index 868f2eac54b..8a29308f8b4 100644
--- a/chromium/components/url_matcher/url_util.h
+++ b/chromium/components/url_matcher/url_util.h
@@ -57,7 +57,7 @@ struct URL_MATCHER_EXPORT FilterComponents {
// or block-list (false) filter.
URL_MATCHER_EXPORT scoped_refptr<url_matcher::URLMatcherConditionSet>
CreateConditionSet(url_matcher::URLMatcher* url_matcher,
- url_matcher::URLMatcherConditionSet::ID id,
+ base::MatcherStringPattern::ID id,
const std::string& scheme,
const std::string& host,
bool match_subdomains,
@@ -98,9 +98,9 @@ URL_MATCHER_EXPORT bool FilterToComponents(const std::string& filter,
URL_MATCHER_EXPORT void AddFilters(
url_matcher::URLMatcher* matcher,
bool allow,
- url_matcher::URLMatcherConditionSet::ID* id,
+ base::MatcherStringPattern::ID* id,
const base::ListValue* patterns,
- std::map<url_matcher::URLMatcherConditionSet::ID,
+ std::map<base::MatcherStringPattern::ID,
url_matcher::util::FilterComponents>* filters = nullptr);
// Adds the filters in `patterns` to `url_matcher` as a ConditionSet::Vector.
@@ -115,9 +115,9 @@ URL_MATCHER_EXPORT void AddFilters(
URL_MATCHER_EXPORT void AddFilters(
url_matcher::URLMatcher* matcher,
bool allow,
- url_matcher::URLMatcherConditionSet::ID* id,
+ base::MatcherStringPattern::ID* id,
const std::vector<std::string>& patterns,
- std::map<url_matcher::URLMatcherConditionSet::ID,
+ std::map<base::MatcherStringPattern::ID,
url_matcher::util::FilterComponents>* filters = nullptr);
URL_MATCHER_EXPORT void AddAllowFilters(url_matcher::URLMatcher* matcher,
diff --git a/chromium/components/url_param_filter/DEPS b/chromium/components/url_param_filter/DEPS
new file mode 100644
index 00000000000..69341f56eee
--- /dev/null
+++ b/chromium/components/url_param_filter/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+ "+base",
+ "+net",
+ "+ui",
+ "+testing",
+ # UrlParamFilter is a layered component; subdirectories must explicitly
+ # introduce the ability to use the content layer as appropriate.
+ "-components/url_param_filter/content",
+]
diff --git a/chromium/components/url_param_filter/OWNERS b/chromium/components/url_param_filter/OWNERS
new file mode 100644
index 00000000000..17e7e3b9b8a
--- /dev/null
+++ b/chromium/components/url_param_filter/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/url_param_filter/OWNERS
diff --git a/chromium/components/url_param_filter/README.md b/chromium/components/url_param_filter/README.md
new file mode 100644
index 00000000000..8e681b5d5c2
--- /dev/null
+++ b/chromium/components/url_param_filter/README.md
@@ -0,0 +1,11 @@
+This is a layered component that allows embedders to filter urls.
+
+Directory structure:
+
+**content/** -
+contains logic that relies on //content for
+use on platforms that embed //content (e.g. Desktop, Android).
+
+
+**core/** - contains logic that can be shared across
+content-embedding platforms and others (e.g. Desktop, Android, iOS).
diff --git a/chromium/components/url_param_filter/content/BUILD.gn b/chromium/components/url_param_filter/content/BUILD.gn
new file mode 100644
index 00000000000..d6edaab13b1
--- /dev/null
+++ b/chromium/components/url_param_filter/content/BUILD.gn
@@ -0,0 +1,45 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("content") {
+ if (is_component_build) {
+ check_includes = false
+ }
+ sources = [
+ "cross_otr_observer.cc",
+ "cross_otr_observer.h",
+ "url_param_filter_throttle.cc",
+ "url_param_filter_throttle.h",
+ ]
+ deps = [ "//components/url_param_filter/core" ]
+ public_deps = [
+ "//base",
+ "//content/public/browser",
+ "//content/public/common",
+ "//net",
+ "//services/network/public/cpp",
+ "//third_party/abseil-cpp:absl",
+ "//third_party/blink/public/common",
+ "//ui/base",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "cross_otr_observer_unittest.cc",
+ "url_param_filter_throttle_unittest.cc",
+ ]
+ deps = [
+ ":content",
+ "//base/test:test_support",
+ "//components/url_param_filter/core",
+ "//components/url_param_filter/core:test_support",
+ "//components/url_param_filter/core:url_param_filter_classification_proto",
+ ]
+ public_deps = [
+ "//content/test:test_support",
+ "//services/network/public/mojom",
+ ]
+}
diff --git a/chromium/components/url_param_filter/content/DEPS b/chromium/components/url_param_filter/content/DEPS
new file mode 100644
index 00000000000..6068e46e512
--- /dev/null
+++ b/chromium/components/url_param_filter/content/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+ "+components/url_param_filter/core",
+ "+content/public/browser",
+ "+content/public/common",
+ "+content/public/test",
+ "+services/network/public/cpp",
+ "+services/network/public/mojom",
+ "+third_party/blink/public/common/loader",
+]
diff --git a/chromium/components/url_param_filter/content/cross_otr_observer.cc b/chromium/components/url_param_filter/content/cross_otr_observer.cc
new file mode 100644
index 00000000000..049c0105ced
--- /dev/null
+++ b/chromium/components/url_param_filter/content/cross_otr_observer.cc
@@ -0,0 +1,202 @@
+// Copyright (c) 2022 The Chromium 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/url_param_filter/content/cross_otr_observer.h"
+
+#include <memory>
+
+#include "base/metrics/histogram_functions.h"
+#include "base/no_destructor.h"
+#include "base/strings/string_util.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/reload_type.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "ui/base/page_transition_types.h"
+
+namespace url_param_filter {
+namespace {
+constexpr char kInternalRedirectHeaderStatusLine[] =
+ "HTTP/1.1 307 Internal Redirect";
+
+bool IsInternalRedirect(const net::HttpResponseHeaders* headers) {
+ return base::EqualsCaseInsensitiveASCII(headers->GetStatusLine(),
+ kInternalRedirectHeaderStatusLine);
+}
+} // anonymous namespace
+
+void CrossOtrObserver::MaybeCreateForWebContents(
+ content::WebContents* web_contents,
+ bool privacy_sensitivity_is_cross_otr,
+ bool started_from_context_menu,
+ ui::PageTransition transition) {
+ if (privacy_sensitivity_is_cross_otr && started_from_context_menu &&
+ !ui::PageTransitionCoreTypeIs(transition,
+ ui::PAGE_TRANSITION_AUTO_BOOKMARK)) {
+ // Inherited from WebContentsUserData and checks for an already-attached
+ // instance internally.
+ CrossOtrObserver::CreateForWebContents(web_contents);
+ }
+}
+
+CrossOtrObserver::CrossOtrObserver(content::WebContents* web_contents)
+ : content::WebContentsObserver(web_contents),
+ content::WebContentsUserData<CrossOtrObserver>(*web_contents),
+ weak_factory_(this) {}
+
+CrossOtrObserver::~CrossOtrObserver() = default;
+
+bool CrossOtrObserver::IsCrossOtrState() const {
+ return protecting_navigations_;
+}
+
+void CrossOtrObserver::DidStartNavigation(
+ content::NavigationHandle* navigation_handle) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ // If we've already observed the end of a navigation, and the navigation is in
+ // the primary main frame, and it is not the result of a client redirect,
+ // we've finished the cross-OTR case. Note that observing user activation
+ // would also serve to stop the protecting_navigations_ case. Note that
+ // refreshes after page load also trigger this, and thus are not at risk of
+ // being considered part of the cross-OTR case.
+ if (observed_response_ && navigation_handle->IsInPrimaryMainFrame() &&
+ !(navigation_handle->GetPageTransition() &
+ ui::PAGE_TRANSITION_CLIENT_REDIRECT)) {
+ protecting_navigations_ = false;
+ }
+}
+
+void CrossOtrObserver::DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (!navigation_handle->IsInPrimaryMainFrame() ||
+ navigation_handle->IsSameDocument()) {
+ // We only are concerned with top-level, non-same doc navigations.
+ return;
+ }
+
+ // We only want the first navigation, including client redirects occurring
+ // without having observed user activation, to be counted; after that, no
+ // response codes should be tracked. The observer is left in place to track
+ // refreshes on the first page.
+ if (protecting_navigations_) {
+ observed_response_ = true;
+ const net::HttpResponseHeaders* headers =
+ navigation_handle->GetResponseHeaders();
+
+ // Metrics will not be collected for non intervened navigation chains and
+ // navigations occurring prior to params filtering.
+ if (headers && did_filter_params_) {
+ WriteResponseMetric(
+ net::HttpUtil::MapStatusCodeForHistogram(headers->response_code()));
+ }
+ return;
+ }
+ if (navigation_handle->GetReloadType() != content::ReloadType::NONE) {
+ refresh_count_++;
+ return;
+ }
+ if (navigation_handle->HasCommitted() && !protecting_navigations_) {
+ Detach();
+ // DO NOT add code past this point. `this` is destroyed.
+ }
+}
+
+void CrossOtrObserver::DidRedirectNavigation(
+ content::NavigationHandle* navigation_handle) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (!navigation_handle->IsInPrimaryMainFrame() ||
+ navigation_handle->IsSameDocument()) {
+ // We only are concerned with top-level, non-same doc navigations.
+ return;
+ }
+
+ const net::HttpResponseHeaders* headers =
+ navigation_handle->GetResponseHeaders();
+ // After the first full navigation has committed, including any client
+ // redirects that occur without user activation, we no longer want to track
+ // redirects.
+ // Metrics will not be collected for non intervened navigation chains and
+ // navigations occurring prior to params filtering.
+ if (protecting_navigations_ && headers && did_filter_params_ &&
+ !IsInternalRedirect(headers)) {
+ WriteResponseMetric(
+ net::HttpUtil::MapStatusCodeForHistogram(headers->response_code()));
+ }
+}
+void CrossOtrObserver::WebContentsDestroyed() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ // The user has closed the tab or otherwise destroyed the web contents. Flush
+ // metrics and cease observation.
+ Detach();
+ // DO NOT add code past this point. `this` is destroyed.
+}
+
+void CrossOtrObserver::FrameReceivedUserActivation(
+ content::RenderFrameHost* render_frame_host) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ // Anytime the user activates a frame in the web contents, we cease to
+ // consider the case cross-OTR.
+ protecting_navigations_ = false;
+}
+
+void CrossOtrObserver::Detach() {
+ // Metrics will not be collected for non intervened navigation chains and
+ // navigations occurring prior to params filtering.
+ if (did_filter_params_) {
+ WriteRefreshMetric(refresh_count_);
+ }
+ web_contents()->RemoveUserData(CrossOtrObserver::UserDataKey());
+ // DO NOT add code past this point. `this` is destroyed.
+}
+
+base::WeakPtr<CrossOtrObserver> CrossOtrObserver::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+void CrossOtrObserver::SetDidFilterParams(
+ bool value,
+ ClassificationExperimentStatus experiment_status) {
+ did_filter_params_ = value;
+ // If we have already seen experimental params, treat all metrics as coming
+ // after an experimental param classification. In other words, we consider all
+ // response codes/refresh counts after an experimental param has been stripped
+ // as being influenced by that experimental parameter removal.
+ if (experiment_status_ != ClassificationExperimentStatus::EXPERIMENTAL) {
+ experiment_status_ = experiment_status;
+ }
+}
+
+void CrossOtrObserver::WriteRefreshMetric(int refresh_count) {
+ // If we used experimental classifications, write the experimental metric in
+ // addition to the standard one for additional segmentation (default vs
+ // experimental).
+ if (experiment_status_ == ClassificationExperimentStatus::EXPERIMENTAL) {
+ base::UmaHistogramCounts100(
+ "Navigation.CrossOtr.ContextMenu.RefreshCountExperimental",
+ refresh_count);
+ }
+ base::UmaHistogramCounts100("Navigation.CrossOtr.ContextMenu.RefreshCount",
+ refresh_count);
+}
+void CrossOtrObserver::WriteResponseMetric(int response_code) {
+ // If we used experimental classifications, write the experimental metric in
+ // addition to the standard one for additional segmentation (default vs
+ // experimental).
+ if (experiment_status_ == ClassificationExperimentStatus::EXPERIMENTAL) {
+ base::UmaHistogramSparse(
+ "Navigation.CrossOtr.ContextMenu.ResponseCodeExperimental",
+ response_code);
+ }
+ base::UmaHistogramSparse("Navigation.CrossOtr.ContextMenu.ResponseCode",
+ response_code);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(CrossOtrObserver);
+
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/content/cross_otr_observer.h b/chromium/components/url_param_filter/content/cross_otr_observer.h
new file mode 100644
index 00000000000..6585cc8f876
--- /dev/null
+++ b/chromium/components/url_param_filter/content/cross_otr_observer.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2022 The Chromium 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 COMPONENTS_URL_PARAM_FILTER_CONTENT_CROSS_OTR_OBSERVER_H_
+#define COMPONENTS_URL_PARAM_FILTER_CONTENT_CROSS_OTR_OBSERVER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "ui/base/page_transition_types.h"
+
+namespace url_param_filter {
+
+// Observes navigations that originate in normal browsing and move into OTR
+// browsing. This class can be thought of as a state machine:
+// start-->blocking-->monitoring-->detached
+// where the initial cross-OTR navigation moves to blocking; user activation or
+// the start of a second navigation not initiated via client redirect moves to
+// monitoring; and the next completed non-refresh navigation after that point,
+// regardless of cause, detaches. Note that for our purposes, navigation above
+// refers to top-level, main frame navigations only; we do not consider e.g.,
+// subframe loads.
+//
+// Changes made to the logic in this observer should also be made on the ios
+// equivalent CrossOtrTabHelper at components/url_param_filter/ios.
+class CrossOtrObserver : public content::WebContentsObserver,
+ public content::WebContentsUserData<CrossOtrObserver> {
+ public:
+ ~CrossOtrObserver() override;
+
+ // Attaches the observer in cases where it should do so; leaves `web_contents`
+ // unchanged otherwise.
+ static void MaybeCreateForWebContents(content::WebContents* web_contents,
+ bool is_cross_otr,
+ bool started_from_context_menu,
+ ui::PageTransition transition);
+ bool IsCrossOtrState() const;
+ // content::WebContentsObserver:
+ void DidStartNavigation(
+ content::NavigationHandle* navigation_handle) override;
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override;
+ void DidRedirectNavigation(
+ content::NavigationHandle* navigation_handle) override;
+ void WebContentsDestroyed() override;
+ void FrameReceivedUserActivation(
+ content::RenderFrameHost* render_frame_host) override;
+ // Inherited from content::WebContentsUserData, but should not be used outside
+ // this class or its Android counterpart. MaybeCreateForWebContents should be
+ // used instead.
+ using content::WebContentsUserData<CrossOtrObserver>::CreateForWebContents;
+
+ base::WeakPtr<CrossOtrObserver> GetWeakPtr();
+
+ // Inform the observer that params were filtered, which means metrics should
+ // be written. `experiment_status` indicates whether the parameters stripped
+ // were based on experimental classifications.
+ void SetDidFilterParams(bool value,
+ ClassificationExperimentStatus experiment_status);
+
+ private:
+ explicit CrossOtrObserver(content::WebContents* web_contents);
+
+ friend class content::WebContentsUserData<CrossOtrObserver>;
+ // Flushes metrics and removes the observer from the WebContents.
+ void Detach();
+
+ // Writes response code metric(s) to monitor for potential breakge.
+ void WriteResponseMetric(int response_code);
+
+ // Writes refresh count metric(s) to monitor for potential breakage.
+ void WriteRefreshMetric(int refresh_count);
+ // Drives state machine logic; we write the cross-OTR response code metric
+ // only for the first navigation, which is that which would have parameters
+ // filtered.
+ bool observed_response_ = false;
+ // Tracks refreshes observed, which could point to an issue with param
+ // filtering causing unexpected behavior for the user.
+ int refresh_count_ = 0;
+
+ // Whether top-level navigations should have filtering applied. Starts true,
+ // and switched to false once a navigation completes and then either:
+ // user interaction is observed or a new navigation starts that is not a
+ // client redirect.
+ bool protecting_navigations_ = true;
+ bool did_filter_params_ = false;
+ ClassificationExperimentStatus experiment_status_ =
+ ClassificationExperimentStatus::NON_EXPERIMENTAL;
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+ base::WeakPtrFactory<CrossOtrObserver> weak_factory_;
+};
+
+} // namespace url_param_filter
+#endif // COMPONENTS_URL_PARAM_FILTER_CONTENT_CROSS_OTR_OBSERVER_H_
diff --git a/chromium/components/url_param_filter/content/cross_otr_observer_unittest.cc b/chromium/components/url_param_filter/content/cross_otr_observer_unittest.cc
new file mode 100644
index 00000000000..c92e2f103ec
--- /dev/null
+++ b/chromium/components/url_param_filter/content/cross_otr_observer_unittest.cc
@@ -0,0 +1,766 @@
+// Copyright 2022 The Chromium 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/url_param_filter/content/cross_otr_observer.h"
+
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "content/public/browser/reload_type.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/mock_navigation_handle.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
+#include "net/http/http_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/page_transition_types.h"
+
+using ::testing::NiceMock;
+
+namespace url_param_filter {
+
+constexpr char kExperimentalResponseCodeMetricName[] =
+ "Navigation.CrossOtr.ContextMenu.ResponseCodeExperimental";
+constexpr char kResponseCodeMetricName[] =
+ "Navigation.CrossOtr.ContextMenu.ResponseCode";
+constexpr char kExperimentalCrossOtrRefreshCountMetricName[] =
+ "Navigation.CrossOtr.ContextMenu.RefreshCountExperimental";
+constexpr char kCrossOtrRefreshCountMetricName[] =
+ "Navigation.CrossOtr.ContextMenu.RefreshCount";
+
+class CrossOtrObserverTest : public content::RenderViewHostTestHarness {
+ public:
+ CrossOtrObserverTest() = default;
+};
+
+TEST_F(CrossOtrObserverTest, NotContextMenuInitiated) {
+ CrossOtrObserver::MaybeCreateForWebContents(
+ web_contents(), /*is_cross_otr=*/false,
+ /*started_from_context_menu=*/false, ui::PAGE_TRANSITION_LINK);
+
+ ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents()), nullptr);
+}
+TEST_F(CrossOtrObserverTest, DefaultSensitivity) {
+ CrossOtrObserver::MaybeCreateForWebContents(
+ web_contents(), /*is_cross_otr=*/false,
+ /*started_from_context_menu=*/false, ui::PAGE_TRANSITION_LINK);
+
+ ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents()), nullptr);
+}
+TEST_F(CrossOtrObserverTest, BookmarkLink) {
+ CrossOtrObserver::MaybeCreateForWebContents(
+ web_contents(), /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+
+ ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents()), nullptr);
+}
+TEST_F(CrossOtrObserverTest, CreateKey) {
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+
+ ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr);
+}
+TEST_F(CrossOtrObserverTest, DuplicateCreateKey) {
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+
+ ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr);
+}
+TEST_F(CrossOtrObserverTest, HandleRedirects) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+
+ std::vector<const std::string> redirect_sequence{
+ "HTTP/1.1 302 Moved Temporarily",
+ "HTTP/1.1 307 Temporary Redirect", // 2 'external' 307 redirects
+ "HTTP/1.1 307 Temporary Redirect",
+ "HTTP/1.1 307 Internal Redirect" // 1 internal 307 redirect, is omitted
+ };
+ for (auto redirect_header : redirect_sequence) {
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>(redirect_header);
+ handle->set_response_headers(response);
+ observer->DidRedirectNavigation(handle.get());
+ }
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 3);
+ histogram_tester.ExpectBucketCount(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(302),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(307),
+ 2);
+}
+TEST_F(CrossOtrObserverTest, HandleRedirectsExperimentalMetrics) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(true,
+ ClassificationExperimentStatus::EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+
+ std::vector<const std::string> redirect_sequence{
+ "HTTP/1.1 302 Moved Temporarily",
+ "HTTP/1.1 307 Temporary Redirect", // 2 'external' 307 redirects
+ "HTTP/1.1 307 Temporary Redirect",
+ "HTTP/1.1 307 Internal Redirect" // 1 internal 307 redirect, is omitted
+ };
+ for (auto redirect_header : redirect_sequence) {
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>(redirect_header);
+ handle->set_response_headers(response);
+ observer->DidRedirectNavigation(handle.get());
+ }
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 3);
+ histogram_tester.ExpectBucketCount(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(302),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(307),
+ 2);
+ histogram_tester.ExpectTotalCount(kExperimentalResponseCodeMetricName, 3);
+ histogram_tester.ExpectBucketCount(
+ kExperimentalResponseCodeMetricName,
+ net::HttpUtil::MapStatusCodeForHistogram(302), 1);
+ histogram_tester.ExpectBucketCount(
+ kExperimentalResponseCodeMetricName,
+ net::HttpUtil::MapStatusCodeForHistogram(307), 2);
+}
+TEST_F(CrossOtrObserverTest,
+ HandleRedirectsExperimentalAndNonExperimentalMetrics) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ // The `ClassificationExperimentStatus::EXPERIMENTAL` should take precedence,
+ // since we consider an experimental metric filtered anywhere in the
+ // navigation to mean the results after are contingent on that parameter's
+ // removal.
+ observer->SetDidFilterParams(true,
+ ClassificationExperimentStatus::EXPERIMENTAL);
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+
+ std::vector<const std::string> redirect_sequence{
+ "HTTP/1.1 302 Moved Temporarily",
+ "HTTP/1.1 307 Temporary Redirect", // 2 'external' 307 redirects
+ "HTTP/1.1 307 Temporary Redirect",
+ "HTTP/1.1 307 Internal Redirect" // 1 internal 307 redirect, is omitted
+ };
+ for (auto redirect_header : redirect_sequence) {
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>(redirect_header);
+ handle->set_response_headers(response);
+ observer->DidRedirectNavigation(handle.get());
+ }
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 3);
+ histogram_tester.ExpectBucketCount(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(302),
+ 1);
+ histogram_tester.ExpectBucketCount(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(307),
+ 2);
+ histogram_tester.ExpectTotalCount(kExperimentalResponseCodeMetricName, 3);
+ histogram_tester.ExpectBucketCount(
+ kExperimentalResponseCodeMetricName,
+ net::HttpUtil::MapStatusCodeForHistogram(302), 1);
+ histogram_tester.ExpectBucketCount(
+ kExperimentalResponseCodeMetricName,
+ net::HttpUtil::MapStatusCodeForHistogram(307), 2);
+}
+TEST_F(CrossOtrObserverTest, HandleRedirectsNoParamsFiltering) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>(
+ "HTTP/1.1 302 Moved Temporarily");
+ handle->set_response_headers(response);
+ observer->DidRedirectNavigation(handle.get());
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 0);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(302),
+ 0);
+}
+TEST_F(CrossOtrObserverTest, FinishedNavigation) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+
+ ASSERT_TRUE(observer->IsCrossOtrState());
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+}
+TEST_F(CrossOtrObserverTest,
+ FinishedNavigationNonExperimentalThenExperimental) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+
+ observer->SetDidFilterParams(true,
+ ClassificationExperimentStatus::EXPERIMENTAL);
+ observer->DidFinishNavigation(handle.get());
+
+ ASSERT_TRUE(observer->IsCrossOtrState());
+ // We first filtered params with non-experimental params, then filtered an
+ // experimental param. Both metrics should be written, with responses showing
+ // in the normal metric and only the experimental result in the experimental
+ // metric.
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 2);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 2);
+ histogram_tester.ExpectTotalCount(kExperimentalResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kExperimentalResponseCodeMetricName,
+ net::HttpUtil::MapStatusCodeForHistogram(200), 1);
+}
+TEST_F(CrossOtrObserverTest, FinishedNavigationNoParamsFiltering) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+
+ ASSERT_TRUE(observer->IsCrossOtrState());
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 0);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 0);
+}
+TEST_F(CrossOtrObserverTest, BadRedirectResponse) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ handle->set_response_headers(nullptr);
+ observer->DidRedirectNavigation(handle.get());
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 0);
+}
+TEST_F(CrossOtrObserverTest, BadNavigationResponse) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ handle->set_response_headers(nullptr);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 0);
+
+ // The observer should not cease observation after first load, regardless of
+ // whether the headers include a response code. We still want to see
+ // the refresh count.
+ ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr);
+}
+TEST_F(CrossOtrObserverTest, RefreshedAfterNavigation) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+
+ handle->set_reload_type(content::ReloadType::NORMAL);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ observer->WebContentsDestroyed();
+
+ histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 1);
+ ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 1);
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+}
+TEST_F(CrossOtrObserverTest, RefreshedAfterNavigationExperimental) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(true,
+ ClassificationExperimentStatus::EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+
+ handle->set_reload_type(content::ReloadType::NORMAL);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ observer->WebContentsDestroyed();
+
+ histogram_tester.ExpectTotalCount(kExperimentalCrossOtrRefreshCountMetricName,
+ 1);
+ ASSERT_EQ(
+ histogram_tester.GetTotalSum(kExperimentalCrossOtrRefreshCountMetricName),
+ 1);
+ // An experimental classification was used, so both metrics should be written.
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+ histogram_tester.ExpectTotalCount(kExperimentalResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kExperimentalResponseCodeMetricName,
+ net::HttpUtil::MapStatusCodeForHistogram(200), 1);
+}
+TEST_F(CrossOtrObserverTest, RefreshedAfterNavigationNoParamsFiltering) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+
+ handle->set_reload_type(content::ReloadType::NORMAL);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ observer->WebContentsDestroyed();
+
+ histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 0);
+ ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 0);
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 0);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 0);
+}
+TEST_F(CrossOtrObserverTest, UncommittedNavigationWithRefresh) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ ASSERT_TRUE(observer->IsCrossOtrState());
+
+ // Finish a non-reload navigation, but one that isn't committed (so no actual
+ // navigation away from the monitored page)
+ handle->set_reload_type(content::ReloadType::NONE);
+ handle->set_is_in_primary_main_frame(true);
+ handle->set_is_same_document(false);
+ handle->set_has_committed(false);
+
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ // We just observed another navigation not due to a client redirect, so should
+ // no longer be in the cross-OTR state.
+ ASSERT_FALSE(observer->IsCrossOtrState());
+
+ // After that uncommitted navigation, trigger a redirect, then destroy.
+ handle->set_reload_type(content::ReloadType::NORMAL);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ observer->WebContentsDestroyed();
+
+ // We had 1 relevant refresh.
+ histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 1);
+ ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 1);
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+}
+TEST_F(CrossOtrObserverTest,
+ UncommittedNavigationWithRefreshNoParamsFiltering) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ ASSERT_TRUE(observer->IsCrossOtrState());
+
+ // Finish a non-reload navigation, but one that isn't committed (so no actual
+ // navigation away from the monitored page)
+ handle->set_reload_type(content::ReloadType::NONE);
+ handle->set_is_in_primary_main_frame(true);
+ handle->set_is_same_document(false);
+ handle->set_has_committed(false);
+
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ // We just observed another navigation not due to a client redirect, so should
+ // no longer be in the cross-OTR state.
+ ASSERT_FALSE(observer->IsCrossOtrState());
+
+ // After that uncommitted navigation, trigger a redirect, then destroy.
+ handle->set_reload_type(content::ReloadType::NORMAL);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ observer->WebContentsDestroyed();
+
+ // We had 1 relevant refresh.
+ histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 0);
+ ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 0);
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 0);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 0);
+}
+TEST_F(CrossOtrObserverTest, MultipleRefreshesAfterNavigation) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+
+ // Reload twice and ensure the count is persisted.
+ handle->set_reload_type(content::ReloadType::NORMAL);
+ observer->DidStartNavigation(handle.get());
+ // With the refresh navigation started, we are no longer in cross-OTR mode.
+ ASSERT_FALSE(observer->IsCrossOtrState());
+ observer->DidFinishNavigation(handle.get());
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+
+ // Navigating away means no more observer.
+ handle->set_reload_type(content::ReloadType::NONE);
+ handle->set_is_in_primary_main_frame(true);
+ handle->set_is_same_document(false);
+ handle->set_has_committed(true);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+
+ ASSERT_EQ(CrossOtrObserver::FromWebContents(contents), nullptr);
+
+ histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 1);
+ ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 2);
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+}
+TEST_F(CrossOtrObserverTest, RedirectsAfterNavigation) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+
+ // Simulate params filtering, making it okay to collect metrics.
+ observer->SetDidFilterParams(
+ true, ClassificationExperimentStatus::NON_EXPERIMENTAL);
+
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ // The first navigation has finished, but we remain cross-OTR until either
+ // user activation or a non-client redirect navigation begins
+ ASSERT_TRUE(observer->IsCrossOtrState());
+
+ // Redirects observed on navigations after the first should not
+ // write responses.
+ observer->DidStartNavigation(handle.get());
+
+ // A new, non-client redirect navigation began, so we should no longer be
+ // filtering.
+ ASSERT_FALSE(observer->IsCrossOtrState());
+
+ response = base::MakeRefCounted<net::HttpResponseHeaders>(
+ "HTTP/1.1 302 Moved Temporarily");
+ handle->set_response_headers(response);
+ observer->DidRedirectNavigation(handle.get());
+
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+}
+TEST_F(CrossOtrObserverTest, RedirectsAfterNavigationNoParamsFiltering) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ // The first navigation has finished, but we remain cross-OTR until either
+ // user activation or a non-client redirect navigation begins
+ ASSERT_TRUE(observer->IsCrossOtrState());
+
+ // Redirects observed on navigations after the first should not
+ // write responses.
+ observer->DidStartNavigation(handle.get());
+
+ // A new, non-client redirect navigation began, so we should no longer be
+ // filtering.
+ ASSERT_FALSE(observer->IsCrossOtrState());
+
+ response = base::MakeRefCounted<net::HttpResponseHeaders>(
+ "HTTP/1.1 302 Moved Temporarily");
+ handle->set_response_headers(response);
+ observer->DidRedirectNavigation(handle.get());
+
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 0);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 0);
+}
+TEST_F(CrossOtrObserverTest, ClientRedirectCrossOtr) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ // The first navigation has finished, but we remain cross-OTR until either
+ // user activation or a non-client redirect navigation begins
+ ASSERT_TRUE(observer->IsCrossOtrState());
+
+ handle->set_page_transition(ui::PAGE_TRANSITION_CLIENT_REDIRECT);
+ observer->DidStartNavigation(handle.get());
+ ASSERT_TRUE(observer->IsCrossOtrState());
+ observer->DidFinishNavigation(handle.get());
+ ASSERT_TRUE(observer->IsCrossOtrState());
+
+ handle->set_page_transition(ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+ observer->DidStartNavigation(handle.get());
+ // A new, non-client redirect navigation began, so we should no longer be
+ // filtering.
+ ASSERT_FALSE(observer->IsCrossOtrState());
+}
+TEST_F(CrossOtrObserverTest, ClientRedirectAfterActivationNotCrossOtr) {
+ base::HistogramTester histogram_tester;
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true,
+ /*started_from_context_menu=*/true, ui::PAGE_TRANSITION_LINK);
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
+ ASSERT_NE(observer, nullptr);
+ std::unique_ptr<content::MockNavigationHandle> handle =
+ std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ handle->set_response_headers(response);
+ observer->DidStartNavigation(handle.get());
+ observer->DidFinishNavigation(handle.get());
+ // The first navigation has finished, but we remain cross-OTR until either
+ // user activation or a non-client redirect navigation begins
+ ASSERT_TRUE(observer->IsCrossOtrState());
+
+ handle->set_page_transition(ui::PAGE_TRANSITION_CLIENT_REDIRECT);
+ observer->DidStartNavigation(handle.get());
+ ASSERT_TRUE(observer->IsCrossOtrState());
+ observer->DidFinishNavigation(handle.get());
+ ASSERT_TRUE(observer->IsCrossOtrState());
+
+ observer->FrameReceivedUserActivation(nullptr);
+ // Receiving user activation means we leave the cross-OTR state and instead
+ // allow client redirects to occur unfiltered.
+ ASSERT_FALSE(observer->IsCrossOtrState());
+ handle->set_page_transition(ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+ observer->DidStartNavigation(handle.get());
+ // The client redirect should not reset the OTR state.
+ ASSERT_FALSE(observer->IsCrossOtrState());
+}
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/content/url_param_filter_throttle.cc b/chromium/components/url_param_filter/content/url_param_filter_throttle.cc
new file mode 100644
index 00000000000..104645bf9bc
--- /dev/null
+++ b/chromium/components/url_param_filter/content/url_param_filter_throttle.cc
@@ -0,0 +1,125 @@
+// Copyright 2022 The Chromium 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/url_param_filter/content/url_param_filter_throttle.h"
+
+#include <memory>
+
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filterer.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/url_request/redirect_info.h"
+#include "services/network/public/cpp/resource_request.h"
+
+namespace url_param_filter {
+namespace {
+// Write metrics about results of param filtering.
+void WriteMetrics(FilterResult result) {
+ // When experimental classifications are used, write a metric indicating this.
+ // This allows validation of experimental results as being due to the
+ // experiment vs filtering that would happen regardless.
+ if (result.experimental_status ==
+ ClassificationExperimentStatus::EXPERIMENTAL) {
+ base::UmaHistogramCounts100(
+ "Navigation.UrlParamFilter.FilteredParamCountExperimental",
+ result.filtered_param_count);
+ }
+ base::UmaHistogramCounts100("Navigation.UrlParamFilter.FilteredParamCount",
+ result.filtered_param_count);
+}
+} // anonymous namespace
+
+void UrlParamFilterThrottle::MaybeCreateThrottle(
+ bool enabled_by_policy,
+ content::WebContents* web_contents,
+ const network::ResourceRequest& request,
+ std::vector<std::unique_ptr<blink::URLLoaderThrottle>>* throttle_list) {
+ // If the enterprise escape hatch policy has been set, do not create the
+ // throttle.
+ if (!enabled_by_policy) {
+ return;
+ }
+ // If we lack a web_contents, do not create the throttle.
+ if (!web_contents) {
+ return;
+ }
+ // Only outermost main frame navigations are in scope. We do not modify other
+ // navigations.
+ if (!request.is_outermost_main_frame) {
+ return;
+ }
+ CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(web_contents);
+ if (observer && observer->IsCrossOtrState()) {
+ throttle_list->push_back(std::make_unique<UrlParamFilterThrottle>(
+ request.request_initiator, observer->GetWeakPtr()));
+ }
+}
+
+UrlParamFilterThrottle::UrlParamFilterThrottle(
+ const absl::optional<url::Origin>& request_initiator_origin,
+ base::WeakPtr<CrossOtrObserver> observer)
+ : should_filter_(base::GetFieldTrialParamByFeatureAsBool(
+ features::kIncognitoParamFilterEnabled,
+ "should_filter",
+ false)) {
+ last_hop_initiator_ = request_initiator_origin.has_value()
+ ? request_initiator_origin->GetURL()
+ : GURL();
+ observer_ = observer;
+}
+UrlParamFilterThrottle::~UrlParamFilterThrottle() = default;
+
+void UrlParamFilterThrottle::DetachFromCurrentSequence() {}
+
+void UrlParamFilterThrottle::WillStartRequest(network::ResourceRequest* request,
+ bool* defer) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ FilterResult result = FilterUrl(last_hop_initiator_, request->url);
+
+ if (should_filter_) {
+ request->url = result.filtered_url;
+ WriteMetrics(result);
+ }
+
+ if (observer_ && result.filtered_param_count) {
+ observer_->SetDidFilterParams(true, result.experimental_status);
+ }
+ last_hop_initiator_ = request->url;
+}
+
+void UrlParamFilterThrottle::WillRedirectRequest(
+ net::RedirectInfo* redirect_info,
+ const network::mojom::URLResponseHead& response_head,
+ bool* defer,
+ std::vector<std::string>* to_be_removed_request_headers,
+ net::HttpRequestHeaders* modified_request_headers,
+ net::HttpRequestHeaders* modified_cors_exempt_request_headers) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ FilterResult result = FilterUrl(last_hop_initiator_, redirect_info->new_url);
+
+ if (should_filter_) {
+ redirect_info->new_url = result.filtered_url;
+ WriteMetrics(result);
+ }
+
+ if (observer_ && result.filtered_param_count) {
+ observer_->SetDidFilterParams(true, result.experimental_status);
+ }
+
+ // Future redirects should use the redirect's domain as the navigation
+ // source.
+ last_hop_initiator_ = redirect_info->new_url;
+}
+
+bool UrlParamFilterThrottle::makes_unsafe_redirect() {
+ // Scheme changes are not possible with this throttle. Only URL params are
+ // modified.
+ return false;
+}
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/content/url_param_filter_throttle.h b/chromium/components/url_param_filter/content/url_param_filter_throttle.h
new file mode 100644
index 00000000000..d8bf3bdb122
--- /dev/null
+++ b/chromium/components/url_param_filter/content/url_param_filter_throttle.h
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_URL_PARAM_FILTER_CONTENT_URL_PARAM_FILTER_THROTTLE_H_
+#define COMPONENTS_URL_PARAM_FILTER_CONTENT_URL_PARAM_FILTER_THROTTLE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/loader/url_loader_throttle.h"
+#include "url/origin.h"
+
+namespace url_param_filter {
+class UrlParamFilterThrottle : public blink::URLLoaderThrottle {
+ public:
+ // Create the throttle if conditions warrant doing so, and add it to
+ // `throttle_list` if created. Otherwise, leave `throttle_list` unchanged and
+ // do nothing.
+ static void MaybeCreateThrottle(
+ bool enabled_by_policy,
+ content::WebContents* web_contents,
+ const network::ResourceRequest& request,
+ std::vector<std::unique_ptr<blink::URLLoaderThrottle>>* throttle_list);
+ explicit UrlParamFilterThrottle(
+ const absl::optional<url::Origin>& request_initiator_origin,
+ base::WeakPtr<CrossOtrObserver> observer);
+ ~UrlParamFilterThrottle() override;
+
+ UrlParamFilterThrottle(const UrlParamFilterThrottle&) = delete;
+ UrlParamFilterThrottle& operator=(const UrlParamFilterThrottle&) = delete;
+
+ // blink::URLLoaderThrottle implementation.
+ void DetachFromCurrentSequence() override;
+ void WillStartRequest(network::ResourceRequest* request,
+ bool* defer) override;
+ void WillRedirectRequest(
+ net::RedirectInfo* redirect_info,
+ const network::mojom::URLResponseHead& response_head,
+ bool* defer,
+ std::vector<std::string>* to_be_removed_request_headers,
+ net::HttpRequestHeaders* modified_request_headers,
+ net::HttpRequestHeaders* modified_cors_exempt_request_headers) override;
+ bool makes_unsafe_redirect() override;
+
+ private:
+ GURL last_hop_initiator_;
+ bool should_filter_;
+ base::WeakPtr<CrossOtrObserver> observer_;
+};
+} // namespace url_param_filter
+#endif // COMPONENTS_URL_PARAM_FILTER_CONTENT_URL_PARAM_FILTER_THROTTLE_H_
diff --git a/chromium/components/url_param_filter/content/url_param_filter_throttle_unittest.cc b/chromium/components/url_param_filter/content/url_param_filter_throttle_unittest.cc
new file mode 100644
index 00000000000..5b45ed25642
--- /dev/null
+++ b/chromium/components/url_param_filter/content/url_param_filter_throttle_unittest.cc
@@ -0,0 +1,369 @@
+// Copyright 2022 The Chromium 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/url_param_filter/content/url_param_filter_throttle.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "components/url_param_filter/content/cross_otr_observer.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
+#include "content/public/test/test_renderer_host.h"
+#include "net/http/http_request_headers.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/loader/url_loader_throttle.h"
+#include "url/origin.h"
+
+namespace url_param_filter {
+
+namespace {
+constexpr static const char kHistogramName[] =
+ "Navigation.UrlParamFilter.FilteredParamCount";
+} // namespace
+
+// Tests the UrlParamFilterThrottle, which is currently a very thin wrapper
+// around the url_param_filter::FilterUrl() function. Coverage is accordingly
+// somewhat less thorough than that seen in url_param_filterer_unittest.
+class UrlParamFilterThrottleTest : public content::RenderViewHostTestHarness {
+ public:
+ UrlParamFilterThrottleTest() = default;
+
+ protected:
+ std::string encoded_classification =
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{"source.xyz", {"plzblock"}},
+ {"redirect.abc", {"plzblockredirect"}},
+ {"redirect2.abc", {"plzblockredirect2"}}},
+ {{"destination.xyz", {"plzblock1"}}});
+
+ void CreateCrossOtrState() {
+ content::WebContents* contents = web_contents();
+ CrossOtrObserver::MaybeCreateForWebContents(
+ contents, /*is_cross_otr=*/true, /*started_from_context_menu=*/true,
+ ui::PAGE_TRANSITION_LINK);
+ }
+};
+
+class UrlParamFilterThrottleFilteringEnabledTest
+ : public UrlParamFilterThrottleTest {
+ public:
+ UrlParamFilterThrottleFilteringEnabledTest() {
+ // With should_filter set true, the URL should be filtered.
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled,
+ {{"classifications", encoded_classification},
+ {"should_filter", "true"}});
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ ShouldCreateThrottleNullContents) {
+ network::ResourceRequest resource_request;
+ resource_request.is_outermost_main_frame = true;
+ std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
+
+ UrlParamFilterThrottle::MaybeCreateThrottle(
+ /*enabled_by_policy=*/true, nullptr, resource_request, &result);
+
+ ASSERT_EQ(result.size(), 0u);
+}
+
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ ShouldCreateThrottleNotCrossOtr) {
+ network::ResourceRequest resource_request;
+ resource_request.is_outermost_main_frame = true;
+ std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
+
+ UrlParamFilterThrottle::MaybeCreateThrottle(
+ /*enabled_by_policy=*/true, web_contents(), resource_request, &result);
+
+ ASSERT_EQ(result.size(), 0u);
+}
+
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ ShouldCreateThrottleNotMainFrame) {
+ CreateCrossOtrState();
+ network::ResourceRequest resource_request;
+ resource_request.is_outermost_main_frame = false;
+ std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
+
+ UrlParamFilterThrottle::MaybeCreateThrottle(
+ /*enabled_by_policy=*/true, web_contents(), resource_request, &result);
+
+ ASSERT_EQ(result.size(), 0u);
+}
+
+TEST_F(UrlParamFilterThrottleTest, ShouldCreateThrottlePolicyDisabled) {
+ CreateCrossOtrState();
+ network::ResourceRequest resource_request;
+ resource_request.is_outermost_main_frame = true;
+ std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
+
+ UrlParamFilterThrottle::MaybeCreateThrottle(
+ /*enabled_by_policy=*/false, web_contents(), resource_request, &result);
+
+ ASSERT_EQ(result.size(), 0u);
+}
+
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ ShouldCreateThrottleTrueCase) {
+ CreateCrossOtrState();
+ network::ResourceRequest resource_request;
+ resource_request.is_outermost_main_frame = true;
+ std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
+
+ UrlParamFilterThrottle::MaybeCreateThrottle(
+ /*enabled_by_policy=*/true, web_contents(), resource_request, &result);
+
+ ASSERT_EQ(result.size(), 1u);
+}
+
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ WillStartRequestNullInitiatorNoChanges) {
+ base::HistogramTester histograms;
+
+ std::unique_ptr<network::ResourceRequest> resource_request =
+ std::make_unique<network::ResourceRequest>();
+ GURL expected_url = GURL("https://no-rule.xyz?asdf=1");
+ UrlParamFilterThrottle throttle =
+ UrlParamFilterThrottle(absl::nullopt, nullptr);
+ resource_request->url = expected_url;
+
+ bool defer = false;
+
+ histograms.ExpectTotalCount(kHistogramName, 0);
+ throttle.WillStartRequest(resource_request.get(), &defer);
+
+ // Filtered no parameters.
+ ASSERT_EQ(histograms.GetTotalSum(kHistogramName), 0);
+ ASSERT_EQ(resource_request->url, expected_url);
+ ASSERT_FALSE(defer);
+}
+
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ WillStartRequestInitiatorChanges) {
+ base::HistogramTester histograms;
+
+ std::unique_ptr<network::ResourceRequest> resource_request =
+ std::make_unique<network::ResourceRequest>();
+ GURL destination_url = GURL("https://no-rule.xyz?asdf=1&plzblock=1");
+ GURL expected_url = GURL("https://no-rule.xyz?asdf=1");
+ absl::optional<url::Origin> initiator =
+ absl::make_optional(url::Origin::Create(GURL("https://source.xyz")));
+ UrlParamFilterThrottle throttle = UrlParamFilterThrottle(initiator, nullptr);
+ resource_request->url = destination_url;
+
+ bool defer = false;
+
+ histograms.ExpectTotalCount(kHistogramName, 0);
+ throttle.WillStartRequest(resource_request.get(), &defer);
+
+ // Filtered one parameter.
+ ASSERT_EQ(histograms.GetTotalSum(kHistogramName), 1);
+ ASSERT_EQ(resource_request->url, expected_url);
+ ASSERT_FALSE(defer);
+}
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ WillStartRequestNoInitiatorDestinationChanges) {
+ base::HistogramTester histograms;
+
+ std::unique_ptr<network::ResourceRequest> resource_request =
+ std::make_unique<network::ResourceRequest>();
+ GURL destination_url = GURL("https://destination.xyz?asdf=1&plzblock1=1");
+ GURL expected_url = GURL("https://destination.xyz?asdf=1");
+ UrlParamFilterThrottle throttle =
+ UrlParamFilterThrottle(absl::nullopt, nullptr);
+ resource_request->url = destination_url;
+
+ bool defer = false;
+
+ histograms.ExpectTotalCount(kHistogramName, 0);
+ throttle.WillStartRequest(resource_request.get(), &defer);
+
+ // Filtered one parameter.
+ ASSERT_EQ(histograms.GetTotalSum(kHistogramName), 1);
+ ASSERT_EQ(resource_request->url, expected_url);
+ ASSERT_FALSE(defer);
+}
+
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ WillRedirectRequestNullInitiatorNoChanges) {
+ base::HistogramTester histograms;
+
+ std::unique_ptr<net::RedirectInfo> redirect_info =
+ std::make_unique<net::RedirectInfo>();
+ GURL expected_url = GURL("https://no-rule.xyz?asdf=1");
+ UrlParamFilterThrottle throttle =
+ UrlParamFilterThrottle(absl::nullopt, nullptr);
+ redirect_info->new_url = expected_url;
+
+ bool defer = false;
+ net::HttpRequestHeaders headers;
+ std::vector<std::string> removed_headers;
+ auto response_head = network::mojom::URLResponseHead::New();
+
+ histograms.ExpectTotalCount(kHistogramName, 0);
+ throttle.WillRedirectRequest(redirect_info.get(), *response_head, &defer,
+ &removed_headers, &headers, &headers);
+
+ // Filtered no parameters.
+ ASSERT_EQ(histograms.GetTotalSum(kHistogramName), 0);
+ ASSERT_EQ(redirect_info->new_url, expected_url);
+ ASSERT_FALSE(defer);
+}
+
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ WillRedirectRequestInitiatorChanges) {
+ base::HistogramTester histograms;
+
+ std::unique_ptr<net::RedirectInfo> redirect_info =
+ std::make_unique<net::RedirectInfo>();
+ GURL destination_url = GURL("https://no-rule.xyz?asdf=1&plzblock=1");
+ GURL expected_url = GURL("https://no-rule.xyz?asdf=1");
+ absl::optional<url::Origin> initiator =
+ absl::make_optional(url::Origin::Create(GURL("https://source.xyz")));
+ UrlParamFilterThrottle throttle = UrlParamFilterThrottle(initiator, nullptr);
+ redirect_info->new_url = destination_url;
+
+ bool defer = false;
+ net::HttpRequestHeaders headers;
+ std::vector<std::string> removed_headers;
+ auto response_head = network::mojom::URLResponseHead::New();
+
+ histograms.ExpectTotalCount(kHistogramName, 0);
+ throttle.WillRedirectRequest(redirect_info.get(), *response_head, &defer,
+ &removed_headers, &headers, &headers);
+
+ // Filtered one parameter.
+ ASSERT_EQ(histograms.GetTotalSum(kHistogramName), 1);
+ ASSERT_EQ(redirect_info->new_url, expected_url);
+ ASSERT_FALSE(defer);
+}
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest,
+ WillRedirectRequestNoInitiatorDestinationChanges) {
+ base::HistogramTester histograms;
+
+ std::unique_ptr<net::RedirectInfo> redirect_info =
+ std::make_unique<net::RedirectInfo>();
+ GURL destination_url = GURL("https://destination.xyz?asdf=1&plzblock1=1");
+ GURL expected_url = GURL("https://destination.xyz?asdf=1");
+ UrlParamFilterThrottle throttle =
+ UrlParamFilterThrottle(absl::nullopt, nullptr);
+ redirect_info->new_url = destination_url;
+
+ bool defer = false;
+ net::HttpRequestHeaders headers;
+ std::vector<std::string> removed_headers;
+ auto response_head = network::mojom::URLResponseHead::New();
+
+ histograms.ExpectTotalCount(kHistogramName, 0);
+ throttle.WillRedirectRequest(redirect_info.get(), *response_head, &defer,
+ &removed_headers, &headers, &headers);
+
+ // Filtered one parameter.
+ ASSERT_EQ(histograms.GetTotalSum(kHistogramName), 1);
+ ASSERT_EQ(redirect_info->new_url, expected_url);
+ ASSERT_FALSE(defer);
+}
+
+TEST_F(UrlParamFilterThrottleFilteringEnabledTest, MultipleRedirects) {
+ std::unique_ptr<net::RedirectInfo> redirect_info =
+ std::make_unique<net::RedirectInfo>();
+ std::unique_ptr<network::ResourceRequest> resource_request =
+ std::make_unique<network::ResourceRequest>();
+
+ // The chain is:
+ // source.xyz-->redirect.abc-->redirect2.abc-->destination.xyz.
+ resource_request->url = GURL("https://redirect.abc?plzblock=1");
+ GURL expected_first_intermediate_url = GURL("https://redirect.abc");
+ GURL redirect_url = GURL("https://redirect2.abc?plzblockredirect=1");
+ GURL expected_second_intermediate_url = GURL("https://redirect2.abc");
+ GURL destination_url =
+ GURL("https://destination.xyz?asdf=1&plzblockredirect2=1");
+ GURL expected_url = GURL("https://destination.xyz?asdf=1");
+ UrlParamFilterThrottle throttle = UrlParamFilterThrottle(
+ url::Origin::Create(GURL("https://source.xyz")), nullptr);
+ redirect_info->new_url = redirect_url;
+
+ bool defer = false;
+ net::HttpRequestHeaders headers;
+ std::vector<std::string> removed_headers;
+ auto response_head = network::mojom::URLResponseHead::New();
+
+ throttle.WillStartRequest(resource_request.get(), &defer);
+ ASSERT_EQ(resource_request->url, expected_first_intermediate_url);
+
+ throttle.WillRedirectRequest(redirect_info.get(), *response_head, &defer,
+ &removed_headers, &headers, &headers);
+ ASSERT_EQ(redirect_info->new_url, expected_second_intermediate_url);
+ // The new source should be redirect.abc; the new destination includes
+ // plzblockredirect, which should be filtered.
+ redirect_info->new_url = destination_url;
+ throttle.WillRedirectRequest(redirect_info.get(), *response_head, &defer,
+ &removed_headers, &headers, &headers);
+
+ ASSERT_EQ(redirect_info->new_url, expected_url);
+ ASSERT_FALSE(defer);
+}
+
+class UrlParamFilterThrottleFilteringDisabledTest
+ : public UrlParamFilterThrottleTest {
+ public:
+ UrlParamFilterThrottleFilteringDisabledTest() {
+ // With should_filter set false, the URL shouldn't be filtered.
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled,
+ {{"classifications", encoded_classification},
+ {"should_filter", "false"}});
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(UrlParamFilterThrottleFilteringDisabledTest,
+ DoesntFilter_MultipleRedirects) {
+ std::unique_ptr<net::RedirectInfo> redirect_info =
+ std::make_unique<net::RedirectInfo>();
+ std::unique_ptr<network::ResourceRequest> resource_request =
+ std::make_unique<network::ResourceRequest>();
+
+ // The chain is:
+ // source.xyz-->redirect.abc-->redirect2.abc-->destination.xyz.
+ resource_request->url = GURL("https://redirect.abc?plzblock=1");
+ GURL redirect_url = GURL("https://redirect2.abc?plzblockredirect=1");
+ GURL destination_url =
+ GURL("https://destination.xyz?asdf=1&plzblockredirect2=1");
+ UrlParamFilterThrottle throttle = UrlParamFilterThrottle(
+ url::Origin::Create(GURL("https://source.xyz")), nullptr);
+ redirect_info->new_url = redirect_url;
+
+ bool defer = false;
+ net::HttpRequestHeaders headers;
+ std::vector<std::string> removed_headers;
+ auto response_head = network::mojom::URLResponseHead::New();
+
+ throttle.WillStartRequest(resource_request.get(), &defer);
+ // The param isn't filtered by the UrlParamFilterThrottle.
+ EXPECT_EQ(resource_request->url, GURL("https://redirect.abc?plzblock=1"));
+
+ throttle.WillRedirectRequest(redirect_info.get(), *response_head, &defer,
+ &removed_headers, &headers, &headers);
+ // The param isn't filtered by the UrlParamFilterThrottle.
+ EXPECT_EQ(redirect_info->new_url,
+ GURL("https://redirect2.abc?plzblockredirect=1"));
+ redirect_info->new_url = destination_url;
+ throttle.WillRedirectRequest(redirect_info.get(), *response_head, &defer,
+ &removed_headers, &headers, &headers);
+
+ EXPECT_EQ(redirect_info->new_url,
+ GURL("https://destination.xyz?asdf=1&plzblockredirect2=1"));
+ EXPECT_FALSE(defer);
+}
+
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/core/BUILD.gn b/chromium/components/url_param_filter/core/BUILD.gn
new file mode 100644
index 00000000000..6e7d2d07d89
--- /dev/null
+++ b/chromium/components/url_param_filter/core/BUILD.gn
@@ -0,0 +1,62 @@
+# Copyright 2022 The Chromium 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("//third_party/protobuf/proto_library.gni")
+
+source_set("core") {
+ sources = [
+ "features.cc",
+ "features.h",
+ "url_param_classifications_loader.cc",
+ "url_param_classifications_loader.h",
+ "url_param_filterer.cc",
+ "url_param_filterer.h",
+ ]
+ deps = [ ":url_param_filter_classification_proto" ]
+ public_deps = [
+ "//base",
+ "//net",
+ "//third_party/abseil-cpp:absl",
+ "//third_party/zlib/google:compression_utils",
+ "//url",
+ ]
+}
+
+proto_library("url_param_filter_classification_proto") {
+ sources = [ "url_param_filter_classification.proto" ]
+ proto_out_dir = "components/url_param_filter/core"
+}
+
+source_set("test_support") {
+ testonly = true
+ sources = [
+ "url_param_filter_test_helper.cc",
+ "url_param_filter_test_helper.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//skia",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/zlib/google:compression_utils",
+ ]
+
+ deps = [ ":core" ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "url_param_classifications_loader_unittest.cc",
+ "url_param_filterer_unittest.cc",
+ ]
+
+ deps = [
+ ":core",
+ ":test_support",
+ ":url_param_filter_classification_proto",
+ "//base/test:test_support",
+ ]
+}
diff --git a/chromium/components/url_param_filter/core/DEPS b/chromium/components/url_param_filter/core/DEPS
new file mode 100644
index 00000000000..5d33c933dc9
--- /dev/null
+++ b/chromium/components/url_param_filter/core/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+third_party/zlib/google",
+ "+url",
+]
diff --git a/chromium/components/url_param_filter/core/features.cc b/chromium/components/url_param_filter/core/features.cc
new file mode 100644
index 00000000000..af197d3e1b0
--- /dev/null
+++ b/chromium/components/url_param_filter/core/features.cc
@@ -0,0 +1,14 @@
+// Copyright 2022 The Chromium 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/url_param_filter/core/features.h"
+
+#include "base/feature_list.h"
+
+namespace url_param_filter::features {
+
+const base::Feature kIncognitoParamFilterEnabled{
+ "IncognitoParamFilterEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
+
+} // namespace url_param_filter::features
diff --git a/chromium/components/url_param_filter/core/features.h b/chromium/components/url_param_filter/core/features.h
new file mode 100644
index 00000000000..860f0fd6b4e
--- /dev/null
+++ b/chromium/components/url_param_filter/core/features.h
@@ -0,0 +1,16 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_URL_PARAM_FILTER_CORE_FEATURES_H_
+#define COMPONENTS_URL_PARAM_FILTER_CORE_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace url_param_filter::features {
+
+extern const base::Feature kIncognitoParamFilterEnabled;
+
+} // namespace url_param_filter::features
+
+#endif // COMPONENTS_URL_PARAM_FILTER_CORE_FEATURES_H_
diff --git a/chromium/components/url_param_filter/core/url_param_classifications_loader.cc b/chromium/components/url_param_filter/core/url_param_classifications_loader.cc
new file mode 100644
index 00000000000..04c168e71a4
--- /dev/null
+++ b/chromium/components/url_param_filter/core/url_param_classifications_loader.cc
@@ -0,0 +1,204 @@
+// Copyright 2022 The Chromium 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/url_param_filter/core/url_param_classifications_loader.h"
+
+#include <string>
+#include <utility>
+
+#include "base/base64.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/no_destructor.h"
+#include "base/sequence_checker.h"
+#include "base/strings/string_util.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/zlib/google/compression_utils.h"
+
+namespace url_param_filter {
+
+namespace {
+constexpr char DEFAULT_TAG[] = "default";
+
+bool HasExperimentTag(const FilterClassification& classification,
+ const std::string& tag) {
+ // We expect this list to almost never exceed 2 items, making a loop
+ // acceptable.
+ for (auto i : classification.experiment_tags()) {
+ if (i == tag) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void AppendParams(ClassificationMap& map,
+ const FilterClassification& classification,
+ FilterClassification::UseCase use_case) {
+ for (const FilterParameter& param : classification.parameters()) {
+ // Any non-matching experimental params have been discarded previously.
+ // We retain whether the classification was experimental, however, to write
+ // a separate metric when those classifications are used.
+ map[classification.site()][use_case][base::ToLowerASCII(param.name())] =
+ !classification.experiment_tags().empty() &&
+ !HasExperimentTag(classification, DEFAULT_TAG)
+ ? ClassificationExperimentStatus::EXPERIMENTAL
+ : ClassificationExperimentStatus::NON_EXPERIMENTAL;
+ }
+}
+
+void ProcessClassification(ClassificationMap& map,
+ const FilterClassification& classification) {
+ if (classification.use_cases_size() > 0) {
+ for (int use_case : classification.use_cases()) {
+ AppendParams(map, classification,
+ static_cast<FilterClassification::UseCase>(use_case));
+ }
+ } else {
+ AppendParams(map, classification, FilterClassification::USE_CASE_UNKNOWN);
+ }
+}
+
+ClassificationMap GetClassificationsFromFeature(
+ const std::string& feature_classifications,
+ FilterClassification_SiteRole role) {
+ FilterClassifications classifications;
+ ClassificationMap map;
+ std::string out;
+ base::Base64Decode(feature_classifications, &out);
+ std::string uncompressed;
+ if (compression::GzipUncompress(out, &uncompressed)) {
+ if (classifications.ParseFromString(uncompressed)) {
+ for (auto i : classifications.classifications()) {
+ if (i.site_role() == role) {
+ // When retrieving classifications from the feature, we do not allow
+ // additional experiment overrides.
+ DCHECK(i.experiment_tags().empty());
+ ProcessClassification(map, i);
+ }
+ }
+ }
+ }
+ return map;
+}
+
+// If this is called before `ReadClassifications` has read classifications from
+// the component, returns an empty map.
+ClassificationMap GetClassificationMap(
+ const absl::optional<std::vector<FilterClassification>>& classifications) {
+ if (!classifications.has_value())
+ return ClassificationMap();
+ ClassificationMap map;
+ for (const FilterClassification& classification : classifications.value()) {
+ ProcessClassification(map, classification);
+ }
+ return map;
+}
+
+} // anonymous namespace
+
+// static
+ClassificationsLoader* ClassificationsLoader::GetInstance() {
+ static base::NoDestructor<ClassificationsLoader> instance;
+ return instance.get();
+}
+
+ClassificationMap ClassificationsLoader::GetSourceClassifications() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return GetClassificationsInternal(
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+}
+
+ClassificationMap ClassificationsLoader::GetDestinationClassifications() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return GetClassificationsInternal(
+ FilterClassification_SiteRole::FilterClassification_SiteRole_DESTINATION);
+}
+
+void ClassificationsLoader::ReadClassifications(
+ const std::string& raw_classifications) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ FilterClassifications classification_list;
+ if (!classification_list.ParseFromString(raw_classifications))
+ return;
+
+ std::vector<FilterClassification> source_classifications,
+ destination_classifications;
+ int total_applicable_source_classifications = 0;
+ int total_applicable_destination_classifications = 0;
+ std::string experiment_identifier = base::GetFieldTrialParamValueByFeature(
+ features::kIncognitoParamFilterEnabled, "experiment_identifier");
+ // If there is no experiment identifier passed via the feature, then use the
+ // classifications that are marked `default`.
+ if (experiment_identifier.empty()) {
+ experiment_identifier = DEFAULT_TAG;
+ }
+ for (const FilterClassification& fc : classification_list.classifications()) {
+ DCHECK(fc.has_site());
+ DCHECK(fc.has_site_role());
+ if (!HasExperimentTag(fc, experiment_identifier)) {
+ continue;
+ }
+ if (fc.site_role() == FilterClassification_SiteRole_SOURCE) {
+ source_classifications.push_back(fc);
+ total_applicable_source_classifications++;
+ }
+
+ if (fc.site_role() == FilterClassification_SiteRole_DESTINATION) {
+ destination_classifications.push_back(fc);
+ total_applicable_destination_classifications++;
+ }
+ }
+
+ component_source_classifications_ = std::move(source_classifications);
+ component_destination_classifications_ =
+ std::move(destination_classifications);
+
+ base::UmaHistogramCounts10000(
+ "Navigation.UrlParamFilter.ApplicableClassificationCount.Source",
+ total_applicable_source_classifications);
+ base::UmaHistogramCounts10000(
+ "Navigation.UrlParamFilter.ApplicableClassificationCount.Destination",
+ total_applicable_destination_classifications);
+}
+
+void ClassificationsLoader::ResetListsForTesting() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ component_source_classifications_.reset();
+ component_destination_classifications_.reset();
+}
+
+ClassificationsLoader::ClassificationsLoader() = default;
+ClassificationsLoader::~ClassificationsLoader() = default;
+
+ClassificationMap ClassificationsLoader::GetClassificationsInternal(
+ FilterClassification_SiteRole role) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // Classifications from field trial params take precedence over the ones from
+ // Component Updater.
+ base::FieldTrialParams params;
+ bool has_feature_params = base::GetFieldTrialParamsByFeature(
+ features::kIncognitoParamFilterEnabled, &params);
+ // Retrieve classifications from feature if provided as a parameter.
+ if (has_feature_params && params.find("classifications") != params.end()) {
+ return GetClassificationsFromFeature(params.find("classifications")->second,
+ role);
+ }
+
+ // If no feature classifications are given, use the component-provided
+ // classifications.
+ switch (role) {
+ case FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE:
+ return GetClassificationMap(component_source_classifications_);
+ case FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION:
+ return GetClassificationMap(component_destination_classifications_);
+ case FilterClassification_SiteRole_SITE_ROLE_UNKNOWN:
+ return ClassificationMap();
+ }
+}
+
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/core/url_param_classifications_loader.h b/chromium/components/url_param_filter/core/url_param_classifications_loader.h
new file mode 100644
index 00000000000..d6960198764
--- /dev/null
+++ b/chromium/components/url_param_filter/core/url_param_classifications_loader.h
@@ -0,0 +1,76 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_CLASSIFICATIONS_LOADER_H_
+#define COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_CLASSIFICATIONS_LOADER_H_
+
+#include <unordered_map>
+
+#include "base/no_destructor.h"
+#include "base/sequence_checker.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace url_param_filter {
+
+enum ClassificationExperimentStatus { EXPERIMENTAL, NON_EXPERIMENTAL };
+
+// `unordered_map` is used for the outer map of domains, which is likely to have
+// hundreds. `map` is used for the inner map of `UseCase`, which will have a
+// single digit number of keys.
+using ClassificationMap = std::unordered_map<
+ std::string,
+ std::map<FilterClassification::UseCase,
+ std::map<std::string, ClassificationExperimentStatus>>>;
+
+class ClassificationsLoader {
+ public:
+ static ClassificationsLoader* GetInstance();
+
+ ClassificationsLoader(const ClassificationsLoader&) = delete;
+ ClassificationsLoader& operator=(const ClassificationsLoader&) = delete;
+
+ // Returns a mapping from site to its source classifications.
+ ClassificationMap GetSourceClassifications();
+
+ // Returns a mapping from site to its destination classifications.
+ ClassificationMap GetDestinationClassifications();
+
+ // Deserializes the proto from |raw_classifications|. The classifications that
+ // are being read will have already been validated in the VerifyInstallation
+ // method in our ComponentInstaller, so we can assume this input is valid.
+ //
+ // The component_source_classifications_ and
+ // component_destination_classifications_ data members are populated by this
+ // method if the proto is deserialized successfully.
+ void ReadClassifications(const std::string& raw_classifications);
+
+ // Resets the stored classification lists for testing.
+ void ResetListsForTesting();
+
+ private:
+ friend class base::NoDestructor<ClassificationsLoader>;
+
+ ClassificationsLoader();
+ ~ClassificationsLoader();
+
+ // Creates a mapping from a site to its `role` classifications by retrieving
+ // classifications from either the Component Updater or the feature flag.
+ // If classifications from both are provided, then the feature flag
+ // classifications take precedence.
+ ClassificationMap GetClassificationsInternal(
+ FilterClassification_SiteRole role);
+
+ absl::optional<std::vector<FilterClassification>>
+ component_source_classifications_ GUARDED_BY_CONTEXT(sequence_checker_) =
+ absl::nullopt;
+ absl::optional<std::vector<FilterClassification>>
+ component_destination_classifications_
+ GUARDED_BY_CONTEXT(sequence_checker_) = absl::nullopt;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+};
+
+} // namespace url_param_filter
+#endif // COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_CLASSIFICATIONS_LOADER_H_
diff --git a/chromium/components/url_param_filter/core/url_param_classifications_loader_unittest.cc b/chromium/components/url_param_filter/core/url_param_classifications_loader_unittest.cc
new file mode 100644
index 00000000000..dbccec5e4c2
--- /dev/null
+++ b/chromium/components/url_param_filter/core/url_param_classifications_loader_unittest.cc
@@ -0,0 +1,829 @@
+// Copyright 2022 The Chromium 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/url_param_filter/core/url_param_classifications_loader.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/raw_ptr.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
+
+namespace url_param_filter {
+namespace {
+constexpr char kApplicableClassificationsSourceMetric[] =
+ "Navigation.UrlParamFilter.ApplicableClassificationCount.Source";
+constexpr char kApplicableClassificationsDestinationMetric[] =
+ "Navigation.UrlParamFilter.ApplicableClassificationCount.Destination";
+constexpr char kApplicableClassificationsInvalidMetric[] =
+ "Navigation.UrlParamFilter.ApplicableClassificationCount.Invalid";
+
+class UrlParamClassificationsLoaderTest : public ::testing::Test {
+ public:
+ UrlParamClassificationsLoaderTest() {
+ classifications_loader_ = ClassificationsLoader::GetInstance();
+ }
+
+ ~UrlParamClassificationsLoaderTest() override {
+ classifications_loader_->ResetListsForTesting();
+ }
+
+ const std::string kSourceSite = "source.xyz";
+ const std::string kDestinationSite = "destination.xyz";
+ const FilterClassification_SiteRole kSourceSiteRole =
+ FilterClassification_SiteRole_SOURCE;
+ const FilterClassification_SiteRole kDestinationSiteRole =
+ FilterClassification_SiteRole_DESTINATION;
+
+ protected:
+ void SetFeatureParams(const std::map<std::string, std::string>& params_map) {
+ // Note, we can initialize the ScopedFeatureList this way since this
+ // unittest is single threaded. If the test is multi-threaded, this would
+ // have to be initialized in the tests constructor.
+
+ // With the flag set, the URL should be filtered using this param.
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled, params_map);
+ }
+
+ void SetComponentFileContents(base::StringPiece content) {
+ base::ScopedTempDir temp_dir;
+ CHECK(temp_dir.CreateUniqueTempDir());
+ CHECK(temp_dir.IsValid());
+
+ base::FilePath path =
+ temp_dir.GetPath().Append(FILE_PATH_LITERAL("classifications.pb"));
+ CHECK(base::WriteFile(path, content));
+
+ std::string file_str;
+ CHECK(base::PathExists(path));
+ CHECK(base::ReadFileToString(path, &file_str));
+ raw_test_file_ = file_str;
+ }
+
+ ClassificationsLoader* loader() { return classifications_loader_; }
+ std::string test_file_contents() { return raw_test_file_; }
+
+ private:
+ raw_ptr<ClassificationsLoader> classifications_loader_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+ std::string raw_test_file_;
+};
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetClassifications_MissingComponentAndFeature) {
+ // Neither Component nor feature provide classifications.
+ EXPECT_THAT(loader()->GetSourceClassifications(), IsEmpty());
+ EXPECT_THAT(loader()->GetDestinationClassifications(), IsEmpty());
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ ReadClassifications_NonserializedProto) {
+ loader()->ReadClassifications("clearly not proto");
+ EXPECT_THAT(loader()->GetSourceClassifications(), IsEmpty());
+ EXPECT_THAT(loader()->GetDestinationClassifications(), IsEmpty());
+}
+
+TEST_F(UrlParamClassificationsLoaderTest, ReadClassifications_EmptyList) {
+ FilterClassifications classifications =
+ MakeClassificationsProtoFromMap({}, {});
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+ EXPECT_THAT(loader()->GetSourceClassifications(), IsEmpty());
+ EXPECT_THAT(loader()->GetDestinationClassifications(), IsEmpty());
+}
+
+TEST_F(UrlParamClassificationsLoaderTest, ReadClassifications_OnlySources) {
+ base::HistogramTester histogram_tester;
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{"source1.xyz", {"plzblock1"}}, {"source2.xyz", {"plzblock2"}}}, {});
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(
+ Pair("source1.xyz",
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "plzblock1",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL))))),
+ Pair("source2.xyz",
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "plzblock2",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+ EXPECT_THAT(loader()->GetDestinationClassifications(), IsEmpty());
+
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsSourceMetric, 1);
+ ASSERT_EQ(
+ histogram_tester.GetTotalSum(kApplicableClassificationsSourceMetric), 2);
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsDestinationMetric,
+ 1);
+ ASSERT_EQ(
+ histogram_tester.GetTotalSum(kApplicableClassificationsDestinationMetric),
+ 0);
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsInvalidMetric, 0);
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ ReadClassifications_OnlyDestinations) {
+ base::HistogramTester histogram_tester;
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {}, {{"destination1.xyz", {"plzblock1"}},
+ {"destination2.xyz", {"plzblock2"}}});
+
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(loader()->GetSourceClassifications(), IsEmpty());
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(
+ Pair("destination1.xyz",
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "plzblock1",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL))))),
+ Pair("destination2.xyz",
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "plzblock2",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsSourceMetric, 1);
+ ASSERT_EQ(
+ histogram_tester.GetTotalSum(kApplicableClassificationsSourceMetric), 0);
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsDestinationMetric,
+ 1);
+ ASSERT_EQ(
+ histogram_tester.GetTotalSum(kApplicableClassificationsDestinationMetric),
+ 2);
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsInvalidMetric, 0);
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ ReadClassifications_SourcesAndDestinations) {
+ base::HistogramTester histogram_tester;
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{"source1.xyz", {"plzblock1"}}}, {{"destination2.xyz", {"plzblock2"}}});
+
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(
+ Pair("source1.xyz",
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "plzblock1",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(
+ Pair("destination2.xyz",
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "plzblock2",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsSourceMetric, 1);
+ ASSERT_EQ(
+ histogram_tester.GetTotalSum(kApplicableClassificationsSourceMetric), 1);
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsDestinationMetric,
+ 1);
+ ASSERT_EQ(
+ histogram_tester.GetTotalSum(kApplicableClassificationsDestinationMetric),
+ 1);
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsInvalidMetric, 0);
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ ReadClassifications_NormalizeToLowercase) {
+ FilterClassifications classifications =
+ MakeClassificationsProtoFromMap({{"source1.xyz", {"UPPERCASE"}}},
+ {{"destination2.xyz", {"mixedCase123"}}});
+
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(
+ Pair("source1.xyz",
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "uppercase",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(
+ Pair("destination2.xyz",
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "mixedcase123",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetClassifications_ComponentOnlyWithExperiment) {
+ base::HistogramTester histogram_tester;
+ const std::string experiment_identifier = "mattwashere";
+ base::test::ScopedFeatureList scoped_feature_list;
+ base::FieldTrialParams params;
+ params["experiment_identifier"] = experiment_identifier;
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled, params);
+
+ // Create proto with both Source + Destination Classifications, with the
+ // default experiment tag. Because we apply a non-default tag, these should
+ // not be present.
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+ FilterClassification destination_experiment_classification =
+ MakeFilterClassification(kDestinationSite,
+ FilterClassification_SiteRole_DESTINATION,
+ {"plzblock5"}, {}, experiment_identifier);
+ FilterClassification source_experiment_classification =
+ MakeFilterClassification(kSourceSite,
+ FilterClassification_SiteRole_SOURCE,
+ {"plzblock7"}, {}, experiment_identifier);
+ // These do not match our experiment identifier, so they should not appear in
+ // the result.
+ FilterClassification inapplicable_destination_experiment_classification =
+ MakeFilterClassification(kDestinationSite,
+ FilterClassification_SiteRole_DESTINATION,
+ {"plzblock6"}, {}, "not_our_experiment");
+ FilterClassification inapplicable_source_experiment_classification =
+ MakeFilterClassification(kSourceSite,
+ FilterClassification_SiteRole_SOURCE,
+ {"plzblock8"}, {}, "not_our_experiment");
+ *classifications.add_classifications() =
+ std::move(destination_experiment_classification);
+ *classifications.add_classifications() =
+ std::move(source_experiment_classification);
+ *classifications.add_classifications() =
+ std::move(inapplicable_destination_experiment_classification);
+ *classifications.add_classifications() =
+ std::move(inapplicable_source_experiment_classification);
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kDestinationSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "plzblock5",
+ ClassificationExperimentStatus::EXPERIMENTAL)))))));
+ EXPECT_THAT(loader()->GetSourceClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(Pair(
+ "plzblock7",
+ ClassificationExperimentStatus::EXPERIMENTAL)))))));
+
+ // Although there are 6 total classifications, only one source and one
+ // destination classification is applicable due to the experiment override.
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsSourceMetric, 1);
+ ASSERT_EQ(
+ histogram_tester.GetTotalSum(kApplicableClassificationsSourceMetric), 1);
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsDestinationMetric,
+ 1);
+ ASSERT_EQ(
+ histogram_tester.GetTotalSum(kApplicableClassificationsDestinationMetric),
+ 1);
+ histogram_tester.ExpectTotalCount(kApplicableClassificationsInvalidMetric, 0);
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetSourceClassifications_NoSourceClassificationsProvided) {
+ // Create proto with only Destination classifications.
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {}, {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ // No source classifications were loaded.
+ EXPECT_THAT(loader()->GetSourceClassifications(), IsEmpty());
+
+ // Provide classifications from the feature.
+ std::map<std::string, std::vector<std::string>> source_params;
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ source_params, {{kDestinationSite, {"plzblock3", "plzblock4"}}})}});
+
+ // No source classifications were loaded.
+ EXPECT_THAT(loader()->GetSourceClassifications(), IsEmpty());
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetSourceClassifications_ComponentInvalid) {
+ // Provide classifications from the Component.
+ SetComponentFileContents("clearly not proto");
+ loader()->ReadClassifications(test_file_contents());
+
+ // Invalid classifications list result in an empty ClassificationMap.
+ EXPECT_THAT(loader()->GetSourceClassifications(), IsEmpty());
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetSourceClassifications_ComponentOnly) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblock1",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock2",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetSourceClassifications_FeatureOnly) {
+ // Provide classifications using the feature flag.
+ std::map<std::string, std::vector<std::string>> dest_params;
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}}, dest_params)}});
+
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblock1",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock2",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetSourceClassifications_FeatureOnly_NormalizeToLowercase) {
+ // Provide classifications using the feature flag.
+ std::map<std::string, std::vector<std::string>> dest_params;
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{kSourceSite, {"UPPERCASE", "mixedCase123"}}}, dest_params)}});
+
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("uppercase",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("mixedcase123",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetSourceClassifications_ComponentThenFeature) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ // Provide classifications using the feature flag.
+ std::map<std::string, std::vector<std::string>> dest_params;
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{kSourceSite, {"plzblockA", "plzblockB"}}}, dest_params)}});
+
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblocka",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblockb",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetSourceClassifications_FeatureThenComponent) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Provide classifications using the feature flag.
+ std::map<std::string, std::vector<std::string>> dest_params;
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{kSourceSite, {"plzblockA", "plzblockB"}}}, dest_params)}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblocka",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblockb",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetSourceClassifications_ComponentAndFeatureWithoutParams) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Don't provide classifications using the feature flag.
+ SetFeatureParams({{}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ // Expect that Component classifications are returned since no feature
+ // classifications were provided.
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblock1",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock2",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetSourceClassifications_ComponentAndFeatureWithShouldFilterParamOnly) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Don't provide classifications using the feature flag.
+ SetFeatureParams({{"should_filter", "true"}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ // Expect that Component classifications are returned since no feature
+ // classifications were provided.
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblock1",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock2",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_NoDestinationClassificationsProvided) {
+ // Create proto with only Source classifications.
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}}, {});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ // No destination classifications were loaded.
+ EXPECT_THAT(loader()->GetDestinationClassifications(), IsEmpty());
+
+ // Provide classifications from the feature.
+ std::map<std::string, std::vector<std::string>> destination_params;
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}}, destination_params)}});
+
+ // No destination classifications were loaded.
+ EXPECT_THAT(loader()->GetDestinationClassifications(), IsEmpty());
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_ComponentInvalid) {
+ // Provide classifications from the Component.
+ SetComponentFileContents("clearly not proto");
+ loader()->ReadClassifications(test_file_contents());
+
+ // Invalid classifications list result in an empty ClassificationMap.
+ EXPECT_THAT(loader()->GetDestinationClassifications(), IsEmpty());
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_ComponentOnly) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kDestinationSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblock3",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock4",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_ComponentOnlyWithUseCases) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications =
+ MakeClassificationsProtoFromMapWithUseCases(
+ {{kSourceSite,
+ {{FilterClassification::CROSS_SITE_NO_3PC,
+ {"plzblock1", "plzblock2"}}}}},
+ {{kDestinationSite,
+ {{FilterClassification::CROSS_OTR, {"plzblock3", "plzblock4"}}}}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::CROSS_SITE_NO_3PC,
+ UnorderedElementsAre(
+ Pair("plzblock1",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock2",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kDestinationSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::CROSS_OTR,
+ UnorderedElementsAre(
+ Pair("plzblock3",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock4",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_FeatureOnly) {
+ // Provide classifications using the feature flag.
+ std::map<std::string, std::vector<std::string>> source_params;
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ source_params, {{kDestinationSite, {"plzblock3", "plzblock4"}}})}});
+
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kDestinationSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblock3",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock4",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_FeatureOnlyWithUseCases) {
+ // Provide classifications using the feature flag.
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{kSourceSite,
+ {{FilterClassification::CROSS_SITE_NO_3PC, {"plzblock"}}}}},
+ {{kDestinationSite,
+ {{FilterClassification::CROSS_OTR,
+ {"plzblock3", "plzblock4"}}}}})}});
+
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kDestinationSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::CROSS_OTR,
+ UnorderedElementsAre(
+ Pair("plzblock3",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock4",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+ EXPECT_THAT(
+ loader()->GetSourceClassifications(),
+ UnorderedElementsAre(
+ Pair(Eq(kSourceSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::CROSS_SITE_NO_3PC,
+ UnorderedElementsAre(Pair(
+ "plzblock",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_ComponentThenFeature) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ // Provide classifications using the feature flag.
+ std::map<std::string, std::vector<std::string>> source_params;
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ source_params, {{kDestinationSite, {"plzblockA", "plzblockB"}}})}});
+
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kDestinationSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblocka",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblockb",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_FeatureThenComponent) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Provide classifications using the feature flag.
+ std::map<std::string, std::vector<std::string>> source_params;
+ SetFeatureParams(
+ {{"classifications",
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ source_params, {{kDestinationSite, {"plzblockA", "plzblockB"}}})}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kDestinationSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblocka",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblockb",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_ComponentAndFeatureWithoutParams) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Don't provide classifications using the feature flag.
+ SetFeatureParams({{}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ // Expect that Component classifications are returned since no feature
+ // classifications were provided.
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kDestinationSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblock3",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock4",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+TEST_F(
+ UrlParamClassificationsLoaderTest,
+ GetDestinationClassifications_ComponentAndFeatureWithShouldFilterParamOnly) {
+ // Create proto with both Source + Destination Classifications
+ FilterClassifications classifications = MakeClassificationsProtoFromMap(
+ {{kSourceSite, {"plzblock1", "plzblock2"}}},
+ {{kDestinationSite, {"plzblock3", "plzblock4"}}});
+
+ // Don't provide classifications using the feature flag.
+ SetFeatureParams({{"should_filter", "true"}});
+
+ // Provide classifications from the Component.
+ SetComponentFileContents(classifications.SerializeAsString());
+ loader()->ReadClassifications(test_file_contents());
+
+ // Expect that Component classifications are returned since no feature
+ // classifications were provided.
+ EXPECT_THAT(
+ loader()->GetDestinationClassifications(),
+ UnorderedElementsAre(Pair(
+ Eq(kDestinationSite),
+ UnorderedElementsAre(Pair(
+ FilterClassification::USE_CASE_UNKNOWN,
+ UnorderedElementsAre(
+ Pair("plzblock3",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL),
+ Pair("plzblock4",
+ ClassificationExperimentStatus::NON_EXPERIMENTAL)))))));
+}
+
+} // namespace
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/core/url_param_filter_classification.proto b/chromium/components/url_param_filter/core/url_param_filter_classification.proto
new file mode 100644
index 00000000000..e9316630b28
--- /dev/null
+++ b/chromium/components/url_param_filter/core/url_param_filter_classification.proto
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package url_param_filter;
+
+message FilterParameter {
+ optional string name = 1; // required, eg. the "foo" in "?foo=bar"
+}
+
+message FilterClassification {
+ enum SiteRole {
+ SITE_ROLE_UNKNOWN = 0;
+ SOURCE = 1;
+ DESTINATION = 2;
+ }
+ enum UseCase {
+ USE_CASE_UNKNOWN = 0;
+ CROSS_OTR = 1;
+ CROSS_SITE_NO_3PC = 2;
+ }
+ optional string site = 1; // eTLD+1, required
+ optional SiteRole site_role = 2; // required
+ repeated FilterParameter parameters = 3;
+ repeated UseCase use_cases = 4 [packed = true];
+ repeated string experiment_tags = 5;
+}
+
+message FilterClassifications {
+ repeated FilterClassification classifications = 1;
+}
diff --git a/chromium/components/url_param_filter/core/url_param_filter_test_helper.cc b/chromium/components/url_param_filter/core/url_param_filter_test_helper.cc
new file mode 100644
index 00000000000..fa17d4375fe
--- /dev/null
+++ b/chromium/components/url_param_filter/core/url_param_filter_test_helper.cc
@@ -0,0 +1,248 @@
+// Copyright 2022 The Chromium 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/url_param_filter/core/url_param_filter_test_helper.h"
+
+#include "base/base64.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filterer.h"
+#include "third_party/zlib/google/compression_utils.h"
+
+namespace url_param_filter {
+
+namespace {
+constexpr char DEFAULT_TAG[] = "default";
+std::map<std::string,
+ std::map<FilterClassification::UseCase, std::vector<std::string>>>
+ConvertToDefaultUseCases(
+ const std::map<std::string, std::vector<std::string>>& source) {
+ std::map<std::string,
+ std::map<FilterClassification::UseCase, std::vector<std::string>>>
+ source_with_use_cases;
+ for (auto i : source) {
+ source_with_use_cases[i.first][FilterClassification::USE_CASE_UNKNOWN] =
+ i.second;
+ }
+ return source_with_use_cases;
+}
+} // namespace
+
+// Create a ClassificationMap with the default non-experimental status.
+ClassificationMap CreateClassificationMapForTesting(
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& source,
+ FilterClassification_SiteRole role) {
+ url_param_filter::ClassificationMap result;
+ for (auto i : source) {
+ for (auto j : i.second) {
+ for (auto k : j.second) {
+ result[i.first][j.first][k] =
+ ClassificationExperimentStatus::NON_EXPERIMENTAL;
+ }
+ }
+ }
+ return result;
+}
+
+ClassificationMap CreateClassificationMapForTesting(
+ const std::map<std::string, std::vector<std::string>>& source,
+ url_param_filter::FilterClassification_SiteRole role) {
+ return CreateClassificationMapForTesting(ConvertToDefaultUseCases(source),
+ role);
+}
+
+std::string CreateSerializedUrlParamFilterClassificationForTesting(
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& source_params,
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& destination_params,
+ const std::vector<std::string>& experiment_tags) {
+ url_param_filter::FilterClassifications classifications;
+ for (auto i : CreateClassificationMapForTesting(
+ source_params, url_param_filter::FilterClassification_SiteRole::
+ FilterClassification_SiteRole_SOURCE)) {
+ for (auto j : i.second) {
+ url_param_filter::FilterClassification classification;
+ classification.set_site(i.first);
+ for (auto i : experiment_tags) {
+ classification.add_experiment_tags(i);
+ }
+ classification.set_site_role(
+ url_param_filter::FilterClassification::SOURCE);
+ if (j.first != FilterClassification::USE_CASE_UNKNOWN) {
+ classification.add_use_cases(j.first);
+ }
+ for (auto k : j.second) {
+ url_param_filter::FilterParameter* parameter =
+ classification.add_parameters();
+ parameter->set_name(k.first);
+ }
+ *classifications.add_classifications() = std::move(classification);
+ }
+ }
+ for (auto i : CreateClassificationMapForTesting(
+ destination_params, url_param_filter::FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION)) {
+ for (auto j : i.second) {
+ url_param_filter::FilterClassification classification;
+ classification.set_site(i.first);
+ for (auto i : experiment_tags) {
+ classification.add_experiment_tags(i);
+ }
+ classification.set_site_role(
+ url_param_filter::FilterClassification::DESTINATION);
+ if (j.first != FilterClassification::USE_CASE_UNKNOWN) {
+ classification.add_use_cases(j.first);
+ }
+ for (auto k : j.second) {
+ url_param_filter::FilterParameter* parameter =
+ classification.add_parameters();
+ parameter->set_name(k.first);
+ }
+ *classifications.add_classifications() = std::move(classification);
+ }
+ }
+ return classifications.SerializeAsString();
+}
+
+std::string CreateSerializedUrlParamFilterClassificationForTesting(
+ const std::map<std::string, std::vector<std::string>>& source_params,
+ const std::map<std::string, std::vector<std::string>>& destination_params,
+ const std::vector<std::string>& experiment_tags) {
+ return CreateSerializedUrlParamFilterClassificationForTesting(
+ ConvertToDefaultUseCases(source_params),
+ ConvertToDefaultUseCases(destination_params), experiment_tags);
+}
+
+std::string CreateBase64EncodedFilterParamClassificationForTesting(
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& source_params,
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& destination_params) {
+ std::string compressed;
+ compression::GzipCompress(
+ CreateSerializedUrlParamFilterClassificationForTesting(
+ source_params, destination_params, {}),
+ &compressed);
+ std::string out;
+ base::Base64Encode(compressed, &out);
+ return out;
+}
+
+std::string CreateBase64EncodedFilterParamClassificationForTesting(
+ const std::map<std::string, std::vector<std::string>>& source_params,
+ const std::map<std::string, std::vector<std::string>>& destination_params) {
+ std::string compressed;
+ compression::GzipCompress(
+ CreateSerializedUrlParamFilterClassificationForTesting(
+ source_params, destination_params, {}),
+ &compressed);
+ std::string out;
+ base::Base64Encode(compressed, &out);
+ return out;
+}
+
+FilterClassifications MakeClassificationsProtoFromMapWithUseCases(
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& source_map,
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& dest_map) {
+ url_param_filter::FilterClassifications classifications;
+ for (const auto& [site, param_map] : source_map) {
+ for (const auto& [use_case, params] : param_map) {
+ AddClassification(classifications.add_classifications(), site,
+ FilterClassification_SiteRole_SOURCE, params,
+ {use_case}, {DEFAULT_TAG});
+ }
+ }
+ for (const auto& [site, param_map] : dest_map) {
+ for (const auto& [use_case, params] : param_map) {
+ AddClassification(classifications.add_classifications(), site,
+ FilterClassification_SiteRole_DESTINATION, params,
+ {use_case}, {DEFAULT_TAG});
+ }
+ }
+ return classifications;
+}
+
+FilterClassifications MakeClassificationsProtoFromMap(
+ const std::map<std::string, std::vector<std::string>>& source_map,
+ const std::map<std::string, std::vector<std::string>>& dest_map) {
+ url_param_filter::FilterClassifications classifications;
+ std::vector<FilterClassification::UseCase> use_cases;
+ for (const auto& [site, params] : source_map) {
+ AddClassification(classifications.add_classifications(), site,
+ FilterClassification_SiteRole_SOURCE, params, use_cases,
+ {});
+ }
+ for (const auto& [site, params] : dest_map) {
+ AddClassification(classifications.add_classifications(), site,
+ FilterClassification_SiteRole_DESTINATION, params,
+ use_cases, {});
+ }
+ return classifications;
+}
+
+FilterClassification MakeFilterClassification(
+ const std::string& site,
+ FilterClassification_SiteRole role,
+ const std::vector<std::string>& params) {
+ return MakeFilterClassification(site, role, params, {}, DEFAULT_TAG);
+}
+
+FilterClassification MakeFilterClassification(
+ const std::string& site,
+ FilterClassification_SiteRole role,
+ const std::vector<std::string>& params,
+ const std::vector<FilterClassification::UseCase>& use_cases) {
+ return MakeFilterClassification(site, role, params, use_cases, DEFAULT_TAG);
+}
+
+FilterClassification MakeFilterClassification(
+ const std::string& site,
+ FilterClassification_SiteRole role,
+ const std::vector<std::string>& params,
+ const std::vector<FilterClassification::UseCase>& use_cases,
+ const std::string& experiment_identifier) {
+ FilterClassification fc;
+ AddClassification(&fc, site, role, params, use_cases,
+ {experiment_identifier});
+ return fc;
+}
+
+void AddClassification(
+ FilterClassification* classification,
+ const std::string& site,
+ FilterClassification_SiteRole role,
+ const std::vector<std::string>& params,
+ const std::vector<FilterClassification::UseCase>& use_cases,
+ const std::vector<std::string>& experiment_tags) {
+ classification->set_site(site);
+ classification->set_site_role(role);
+ // The proto distinguishes between empty and not set; for the purposes of this
+ // test helper, we avoid empty being considered an experiment.
+ if (!experiment_tags.empty()) {
+ for (auto i : experiment_tags) {
+ classification->add_experiment_tags(i);
+ }
+ } else {
+ classification->add_experiment_tags(DEFAULT_TAG);
+ }
+ for (const FilterClassification::UseCase& use_case : use_cases) {
+ classification->add_use_cases(use_case);
+ }
+ for (const std::string& param : params) {
+ FilterParameter* parameters = classification->add_parameters();
+ parameters->set_name(param);
+ }
+}
+
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/core/url_param_filter_test_helper.h b/chromium/components/url_param_filter/core/url_param_filter_test_helper.h
new file mode 100644
index 00000000000..97bc294bce6
--- /dev/null
+++ b/chromium/components/url_param_filter/core/url_param_filter_test_helper.h
@@ -0,0 +1,119 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTER_TEST_HELPER_H_
+#define COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTER_TEST_HELPER_H_
+
+#include "components/url_param_filter/core/url_param_filterer.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace url_param_filter {
+
+MATCHER_P(EqualsProto,
+ want,
+ "Matches an argument against an expected a proto Message.") {
+ return arg.SerializeAsString() == want.SerializeAsString();
+}
+
+// A helper to easily create URL param filter classification maps based
+// on the passed-in source. `source` should map an eTLD+1 to a vector
+// of params for the given role. For example, for eTLD+1 source.xyz, when
+// observed as the source (aka referer) of a navigation, block params
+// "plzblock" and "plzblock1".
+url_param_filter::ClassificationMap CreateClassificationMapForTesting(
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& source,
+ url_param_filter::FilterClassification_SiteRole role);
+
+// Equivalent to the other overload, but uses the default (unknown) use case for
+// all parameters.
+url_param_filter::ClassificationMap CreateClassificationMapForTesting(
+ const std::map<std::string, std::vector<std::string>>& source,
+ url_param_filter::FilterClassification_SiteRole role);
+
+// Creates and serializes the URL param filter classifications proto.
+// Used for simulating reading the classifications file from Component Updater.
+std::string CreateSerializedUrlParamFilterClassificationForTesting(
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& source_params,
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& destination_params,
+ const std::vector<std::string>& experiment_tags);
+
+// Equivalent to the other overload, but uses empty use case lists for all
+// parameters.
+std::string CreateSerializedUrlParamFilterClassificationForTesting(
+ const std::map<std::string, std::vector<std::string>>& source_params,
+ const std::map<std::string, std::vector<std::string>>& destination_params,
+ const std::vector<std::string>& experiment_tags);
+
+// Create a base64 representation of the URL param filter classifications
+// proto. Used for initialization of the feature params in tests.
+std::string CreateBase64EncodedFilterParamClassificationForTesting(
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& source_params,
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& destination_params);
+
+// Equivalent to the other overload, but uses empty use case lists for all
+// parameters.
+std::string CreateBase64EncodedFilterParamClassificationForTesting(
+ const std::map<std::string, std::vector<std::string>>& source_params,
+ const std::map<std::string, std::vector<std::string>>& destination_params);
+
+// Make a FilterClassifications proto using two maps, for source and destination
+// classifications. Each map takes the form "site"->["p1", "p2", ...] where
+// each "pi" in the list is a param that should be filtered from that site.
+FilterClassifications MakeClassificationsProtoFromMapWithUseCases(
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& source_map,
+ const std::map<std::string,
+ std::map<FilterClassification::UseCase,
+ std::vector<std::string>>>& dest_map);
+
+// Equivalent to the other overload, but uses empty use case lists for all
+// parameters.
+FilterClassifications MakeClassificationsProtoFromMap(
+ const std::map<std::string, std::vector<std::string>>& source_map,
+ const std::map<std::string, std::vector<std::string>>& dest_map);
+
+// Make a FilterClassification proto provided a site, role, and list of params.
+FilterClassification MakeFilterClassification(
+ const std::string& site,
+ FilterClassification_SiteRole role,
+ const std::vector<std::string>& params,
+ const std::vector<FilterClassification::UseCase>& use_cases);
+
+// Equivalent to the other overload, but uses an empty list of use cases.
+FilterClassification MakeFilterClassification(
+ const std::string& site,
+ FilterClassification_SiteRole role,
+ const std::vector<std::string>& params);
+
+// Make a FilterClassification proto provided a site, role, experiment override,
+// and list of params.
+FilterClassification MakeFilterClassification(
+ const std::string& site,
+ FilterClassification_SiteRole role,
+ const std::vector<std::string>& params,
+ const std::vector<FilterClassification::UseCase>& use_cases,
+ const std::string& experiment_identifier);
+
+// Helper method for adding repeated classifications on a FilterClassification.
+void AddClassification(
+ FilterClassification* classification,
+ const std::string& site,
+ FilterClassification_SiteRole role,
+ const std::vector<std::string>& params,
+ const std::vector<FilterClassification::UseCase>& use_cases,
+ const std::vector<std::string>& experiment_tags);
+} // namespace url_param_filter
+#endif // COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTER_TEST_HELPER_H_
diff --git a/chromium/components/url_param_filter/core/url_param_filterer.cc b/chromium/components/url_param_filter/core/url_param_filterer.cc
new file mode 100644
index 00000000000..3bc4ee8b229
--- /dev/null
+++ b/chromium/components/url_param_filter/core/url_param_filterer.cc
@@ -0,0 +1,181 @@
+// Copyright 2022 The Chromium 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/url_param_filter/core/url_param_filterer.h"
+
+#include <vector>
+
+#include "base/base64.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/no_destructor.h"
+#include "base/strings/escape.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "net/base/url_util.h"
+#include "third_party/zlib/google/compression_utils.h"
+#include "url/gurl.h"
+
+namespace url_param_filter {
+namespace {
+
+// Get the ETLD+1 of the URL, which means any subdomain is treated equivalently.
+// IP addresses are returned verbatim. Note that this is schemeless, so
+// filtering is applied equivalently regardless of http vs https vs others.
+std::string GetClassifiedSite(const GURL& gurl) {
+ if (gurl.HostIsIPAddress()) {
+ return gurl.host();
+ }
+ return net::registry_controlled_domains::GetDomainAndRegistry(
+ gurl, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+}
+
+// Filter a given URL according to the passed-in classifications, optionally
+// checking any encoded, nested URLs.
+FilterResult FilterUrl(const GURL& source_url,
+ const GURL& destination_url,
+ const ClassificationMap& source_classification_map,
+ const ClassificationMap& destination_classification_map,
+ const bool check_nested,
+ const FilterClassification::UseCase use_case) {
+ GURL result = GURL{destination_url};
+ int filtered_params_count = 0;
+ ClassificationExperimentStatus experiment_status =
+ ClassificationExperimentStatus::NON_EXPERIMENTAL;
+
+ // If there's no query string, we can short-circuit immediately.
+ if (!destination_url.has_query()) {
+ return FilterResult{destination_url, filtered_params_count,
+ experiment_status};
+ }
+
+ std::string source_classified_site = GetClassifiedSite(source_url);
+ std::string destination_classified_site = GetClassifiedSite(destination_url);
+
+ std::map<std::string, ClassificationExperimentStatus> blocked_parameters;
+ // Check whether source site, as seen by the classifier (eTLD+1 or IP), has
+ // params classified as requiring filtering. If so, and the params are present
+ // on the destination URL, or any nested URLs, remove them.
+ auto source_classification_result =
+ source_classification_map.find(source_classified_site);
+ if (source_classification_result != source_classification_map.end()) {
+ auto source_classification_with_use_case =
+ source_classification_result->second.find(use_case);
+ if (source_classification_with_use_case !=
+ source_classification_result->second.end()) {
+ blocked_parameters.insert(
+ source_classification_with_use_case->second.begin(),
+ source_classification_with_use_case->second.end());
+ }
+ }
+ auto destination_classification_result =
+ destination_classification_map.find(destination_classified_site);
+ if (destination_classification_result !=
+ destination_classification_map.end()) {
+ auto destination_classification_with_use_case =
+ destination_classification_result->second.find(use_case);
+ if (destination_classification_with_use_case !=
+ destination_classification_result->second.end()) {
+ blocked_parameters.insert(
+ destination_classification_with_use_case->second.begin(),
+ destination_classification_with_use_case->second.end());
+ }
+ }
+ // Return quickly if there are no parameters we care about.
+ if (blocked_parameters.size() == 0) {
+ return FilterResult{destination_url, filtered_params_count,
+ experiment_status};
+ }
+
+ std::vector<std::string> query_parts;
+ for (net::QueryIterator it(result); !it.IsAtEnd(); it.Advance()) {
+ const std::string key = std::string{it.GetKey()};
+ // If we don't find the given param in our set of blocked parameters, we can
+ // add it to the result safely.
+ auto classification = blocked_parameters.find(base::ToLowerASCII(key));
+ if (classification == blocked_parameters.end()) {
+ std::string value = std::string{it.GetValue()};
+ if (check_nested) {
+ GURL nested = GURL{base::UnescapeBinaryURLComponent(value)};
+ if (nested.is_valid()) {
+ FilterResult nested_result =
+ FilterUrl(destination_url, nested, source_classification_map,
+ destination_classification_map, false, use_case);
+ // If a nested URL contains a param we must filter, do so now.
+ if (nested != nested_result.filtered_url) {
+ value = base::EscapeQueryParamValue(
+ nested_result.filtered_url.spec(), /*use_plus=*/false);
+ filtered_params_count += nested_result.filtered_param_count;
+ if (nested_result.experimental_status ==
+ ClassificationExperimentStatus::EXPERIMENTAL) {
+ experiment_status = ClassificationExperimentStatus::EXPERIMENTAL;
+ }
+ }
+ }
+ }
+ if (value != "") {
+ query_parts.push_back(base::StrCat({key, "=", value}));
+ } else {
+ query_parts.push_back(key);
+ }
+ } else {
+ filtered_params_count++;
+ if (classification->second ==
+ ClassificationExperimentStatus::EXPERIMENTAL) {
+ experiment_status = classification->second;
+ }
+ }
+ }
+
+ std::string new_query = base::JoinString(query_parts, "&");
+ GURL::Replacements replacements;
+ if (new_query == "") {
+ replacements.ClearQuery();
+ } else {
+ replacements.SetQueryStr(new_query);
+ }
+ result = result.ReplaceComponents(replacements);
+ return FilterResult{result, filtered_params_count, experiment_status};
+}
+} // anonymous namespace
+
+FilterResult FilterUrl(const GURL& source_url,
+ const GURL& destination_url,
+ const ClassificationMap& source_classification_map,
+ const ClassificationMap& destination_classification_map,
+ const FilterClassification::UseCase use_case) {
+ return FilterUrl(source_url, destination_url, source_classification_map,
+ destination_classification_map, true, use_case);
+}
+
+FilterResult FilterUrl(const GURL& source_url, const GURL& destination_url) {
+ if (!base::FeatureList::IsEnabled(features::kIncognitoParamFilterEnabled)) {
+ return FilterResult{destination_url, 0};
+ }
+ return FilterUrl(
+ source_url, destination_url,
+ ClassificationsLoader::GetInstance()->GetSourceClassifications(),
+ ClassificationsLoader::GetInstance()->GetDestinationClassifications(),
+ FilterClassification::USE_CASE_UNKNOWN);
+}
+
+FilterResult FilterUrl(const GURL& source_url,
+ const GURL& destination_url,
+ const FilterClassification::UseCase use_case) {
+ if (!base::FeatureList::IsEnabled(features::kIncognitoParamFilterEnabled)) {
+ return FilterResult{destination_url, 0,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL};
+ }
+ return FilterUrl(
+ source_url, destination_url,
+ ClassificationsLoader::GetInstance()->GetSourceClassifications(),
+ ClassificationsLoader::GetInstance()->GetDestinationClassifications(),
+ use_case);
+}
+
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/core/url_param_filterer.h b/chromium/components/url_param_filter/core/url_param_filterer.h
new file mode 100644
index 00000000000..069a5363ccc
--- /dev/null
+++ b/chromium/components/url_param_filter/core/url_param_filterer.h
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTERER_H_
+#define COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTERER_H_
+
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "url/gurl.h"
+
+// Used to filter URL parameters based on backend classification rules. Note
+// that all functions, unless otherwise specified, do not normalize the query
+// string.
+namespace url_param_filter {
+
+// Represents the result of filtering; includes the resulting URL (which may be
+// unmodified), along with the count of params filtered.
+struct FilterResult {
+ GURL filtered_url;
+ int filtered_param_count;
+ ClassificationExperimentStatus experimental_status;
+};
+
+// Filter the destination URL according to the parameter classifications for the
+// source and destination URLs. Used internally by the 2-arg overload, and
+// called directly from tests.
+// Currently experimental; not intended for broad consumption.
+FilterResult FilterUrl(const GURL& source_url,
+ const GURL& destination_url,
+ const ClassificationMap& source_classification_map,
+ const ClassificationMap& destination_classification_map,
+ const FilterClassification::UseCase use_case);
+
+// Filter the destination URL according to the default parameter classifications
+// for the source and destination URLs. Equivalent to calling the three-arg
+// version with a `use_case` of `UNKNOWN`. This overload is included for
+// backward compatibility and will be removed.
+// Currently experimental; not intended for broad consumption.
+FilterResult FilterUrl(const GURL& source_url, const GURL& destination_url);
+
+// Filter the destination URL according to the default parameter classifications
+// for the source and destination URLs, only if the classifications include the
+// passed-in `UseCase`.
+// Currently experimental; not intended for broad consumption.
+FilterResult FilterUrl(const GURL& source_url,
+ const GURL& destination_url,
+ const FilterClassification::UseCase use_case);
+
+} // namespace url_param_filter
+#endif // COMPONENTS_URL_PARAM_FILTER_CORE_URL_PARAM_FILTERER_H_
diff --git a/chromium/components/url_param_filter/core/url_param_filterer_unittest.cc b/chromium/components/url_param_filter/core/url_param_filterer_unittest.cc
new file mode 100644
index 00000000000..31e20f06eb2
--- /dev/null
+++ b/chromium/components/url_param_filter/core/url_param_filterer_unittest.cc
@@ -0,0 +1,738 @@
+// Copyright 2022 The Chromium 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/url_param_filter/core/url_param_filterer.h"
+
+#include <string>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/url_param_filter/core/features.h"
+#include "components/url_param_filter/core/url_param_classifications_loader.h"
+#include "components/url_param_filter/core/url_param_filter_classification.pb.h"
+#include "components/url_param_filter/core/url_param_filter_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace url_param_filter {
+
+class UrlParamFiltererTest : public ::testing::Test {};
+
+TEST_F(UrlParamFiltererTest, FilterUrlEmptyClassifications) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ // If no classifications are passed in, don't modify the destination URL.
+ FilterResult result =
+ FilterUrl(source, expected, ClassificationMap(), ClassificationMap(),
+ FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 0);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlNoChanges) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // If classifications are passed in, but the destination URL doesn't contain
+ // any blocked params, don't modify it.
+ FilterResult result = FilterUrl(source, expected, source_classification_map,
+ destination_classification_map,
+ FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 0);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlSourceBlocked) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination = GURL{"https://destination.xyz?plzblock=123&nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+
+ // Navigations from source.xyz with a param called plzblock should have that
+ // param removed, regardless of destination.
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ FilterResult result =
+ FilterUrl(source, destination, source_classification_map,
+ ClassificationMap(), FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 1);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlSourceBlockedNoValue) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination = GURL{"https://destination.xyz?plzblock&nochange"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+
+ // Navigations from source.xyz with a param called plzblock should have that
+ // param removed, regardless of missing a value.
+ GURL expected = GURL{"https://destination.xyz?nochange"};
+ FilterResult result =
+ FilterUrl(source, destination, source_classification_map,
+ ClassificationMap(), FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 1);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlMultipleSourceBlocked) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination =
+ GURL{"https://destination.xyz?plzblock=123&plzblock1=321&nochange=asdf"};
+ std::map<std::string, std::vector<std::string>> classifications = {
+ {"source.xyz", {"plzblock", "plzblock1"}}};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ classifications,
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+
+ // Navigations from source.xyz with a param called plzblock or plzblock1
+ // should have those params removed, regardless of destination.
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ FilterResult result =
+ FilterUrl(source, destination, source_classification_map,
+ ClassificationMap(), FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlDestinationBlocked) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination = GURL{"https://destination.xyz?plzblock=123&nochange=asdf"};
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // Navigations to destination.xyz with a param called plzblock should have
+ // that param removed, regardless of source.
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ FilterResult result = FilterUrl(source, destination, ClassificationMap(),
+ destination_classification_map,
+ FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 1);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlMultipleDestinationBlocked) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination =
+ GURL{"https://destination.xyz?plzblock=123&plzblock1=321&nochange=asdf"};
+ std::map<std::string, std::vector<std::string>> classifications = {
+ {"destination.xyz", {"plzblock", "plzblock1"}}};
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ classifications, FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // Navigations to destination.xyz with a param called plzblock and/or
+ // plzblock1 should have those param removed, regardless of source.
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ FilterResult result = FilterUrl(source, destination, ClassificationMap(),
+ destination_classification_map,
+ FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlSourceAndDestinationBlocked) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination =
+ GURL{"https://destination.xyz?plzblock=123&plzblock1=321&nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // Both source and destination have associated URL param filtering rules. Only
+ // nochange should remain.
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlSourceAndDestinationAsIPBlocked) {
+ GURL source = GURL{"https://127.0.0.1"};
+ GURL destination =
+ GURL{"https://123.0.0.1?plzblock=123&plzblock1=321&nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"127.0.0.1", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"123.0.0.1", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // Both source and destination have associated URL param filtering rules. Only
+ // nochange should remain.
+ GURL expected = GURL{"https://123.0.0.1?nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlSourceAndDestinationAsIPv6Blocked) {
+ GURL source = GURL{"https://[::1]"};
+ GURL destination = GURL{
+ "https://"
+ "[2001:db8:ac10:fe01::]?plzblock=123&plzblock1=321&nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"[::1]", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"[2001:db8:ac10:fe01::]", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // Both source and destination have associated URL param filtering rules. Only
+ // nochange should remain.
+ GURL expected = GURL{"https://[2001:db8:ac10:fe01::]?nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest,
+ FilterUrlSourceAndDestinationMixedIPv6AndIPv4Blocked) {
+ GURL source = GURL{"https://127.0.0.1"};
+ GURL destination = GURL{
+ "https://"
+ "[2001:db8:ac10:fe01::]?plzblock=123&plzblock1=321&nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"127.0.0.1", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"[2001:db8:ac10:fe01::]", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // Both source and destination have associated URL param filtering rules. Only
+ // nochange should remain.
+ GURL expected = GURL{"https://[2001:db8:ac10:fe01::]?nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest,
+ FilterUrlSourceAndDestinationBlockedCheckOrderingPreserved) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination = GURL{
+ "https://"
+ "destination.xyz?plzblock=123&plzblock1=321&nochange=asdf&laternochange="
+ "fdsa"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // Both source and destination have associated URL param filtering rules. Only
+ // nochange should remain.
+ GURL expected =
+ GURL{"https://destination.xyz?nochange=asdf&laternochange=fdsa"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlSubdomainsApplied) {
+ GURL source = GURL{"https://subdomain.source.xyz"};
+ GURL destination = GURL{
+ "https://"
+ "subdomain.destination.xyz?plzblock=123&plzblock1=321&nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ GURL expected = GURL{"https://subdomain.destination.xyz?nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlCaseIgnored) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination =
+ GURL{"https://destination.xyz?PlZbLoCk=123&PLZBLOCK1=321&nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // The disallowed params PlZbLoCk and PLZBLOCK1 should be removed.
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlWithNestedUrl) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination = GURL{
+ "https://"
+ "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
+ "3Fplzblock1%"
+ "3D123%26nochange%3Dasdf&PLZBLOCK1=321&nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock1"}}, {"source.xyz", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // The nested URL pattern is commonly observed; we do not want the parameter
+ // to leak.
+ GURL expected = GURL{
+ "https://"
+ "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
+ "3Fnochange%"
+ "3Dasdf&nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FilterUrlWithNestedUrlNotNeedingFiltering) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination = GURL{
+ "https://"
+ "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
+ "3Fnochange%"
+ "3Dasdf&PLZBLOCK1=321&nochange=asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock1"}}, {"source.xyz", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // The nested URL does not have filtered parameters and should be left alone.
+ GURL expected = GURL{
+ "https://"
+ "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
+ "3Fnochange%"
+ "3Dasdf&nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 1);
+}
+TEST_F(UrlParamFiltererTest, FilterUrlWithNestedUrlAndDuplicates) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination = GURL{
+ "https://"
+ "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
+ "3Fplzblock1%"
+ "3D123%26nochange%3Dasdf%26plzblock1%3D123&PLZBLOCK1=321&nochange=asdf&"
+ "PLZBLOCK1=321"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock1"}}, {"source.xyz", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // The nested URL pattern is commonly observed; we do not want the parameter
+ // to leak.
+ GURL expected = GURL{
+ "https://"
+ "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
+ "3Fnochange%"
+ "3Dasdf&nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 4);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FeatureDeactivated) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ // When the feature is not explicitly activated, the 2-parameter version of
+ // the function should be inert.
+ GURL result = FilterUrl(source, expected).filtered_url;
+
+ ASSERT_EQ(result, expected);
+}
+
+TEST_F(UrlParamFiltererTest, FeatureDeactivatedUseCaseVariant) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ // When the feature is not explicitly activated, the 3-parameter version of
+ // the function should be inert.
+ FilterResult result =
+ FilterUrl(source, expected, FilterClassification::CROSS_OTR);
+
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 0);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FeatureActivatedNoQueryString) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL destination = GURL{"https://destination.xyz"};
+
+ std::string encoded_classification =
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{"source.xyz", {"plzblock"}}}, {{"destination.xyz", {"plzblock1"}}});
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ // With the flag set, the URL should be filtered.
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled,
+ {{"classifications", encoded_classification}});
+
+ GURL expected = GURL{"https://destination.xyz"};
+ FilterResult result = FilterUrl(source, destination);
+
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 0);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FeatureActivatedAllRemoved) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL destination =
+ GURL{"https://destination.xyz?plzblock=adf&plzblock1=asdffdsa"};
+
+ std::string encoded_classification =
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{"source.xyz", {"plzblock"}}}, {{"destination.xyz", {"plzblock1"}}});
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ // With the flag set, the URL should be filtered.
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled,
+ {{"classifications", encoded_classification}});
+
+ GURL expected = GURL{"https://destination.xyz"};
+ FilterResult result = FilterUrl(source, destination);
+
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, FeatureActivatedSourceAndDestinationRemoval) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL destination =
+ GURL{"https://destination.xyz?plzblock=1&plzblock1=2&nochange=asdf"};
+
+ std::string encoded_classification =
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{"source.xyz", {"plzblock"}}}, {{"destination.xyz", {"plzblock1"}}});
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ // With the flag set, the URL should be filtered.
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled,
+ {{"classifications", encoded_classification}});
+
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ FilterResult result = FilterUrl(source, destination);
+
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, CrossOtrUseCase) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL destination =
+ GURL{"https://destination.xyz?plzblock=1&plzblock1=2&nochange=asdf"};
+
+ std::string encoded_classification =
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{"source.xyz", {{FilterClassification::CROSS_OTR, {"plzblock"}}}}},
+ {{"destination.xyz",
+ {{FilterClassification::CROSS_OTR, {"plzblock1"}}}}});
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ // With the flag set, the URL should be filtered.
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled,
+ {{"classifications", encoded_classification}});
+
+ GURL expected = GURL{"https://destination.xyz?nochange=asdf"};
+ FilterResult result =
+ FilterUrl(source, destination, FilterClassification::CROSS_OTR);
+
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, MismatchedUseCases) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL destination =
+ GURL{"https://destination.xyz?plzblock=1&plzblock1=2&nochange=asdf"};
+
+ std::string encoded_classification =
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{"source.xyz", {{FilterClassification::CROSS_OTR, {"plzblock"}}}}},
+ {{"destination.xyz",
+ {{FilterClassification::CROSS_OTR, {"plzblock1"}}}}});
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ // With the flag set, the URL could be filtered.
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled,
+ {{"classifications", encoded_classification}});
+
+ FilterResult result =
+ FilterUrl(source, destination, FilterClassification::CROSS_SITE_NO_3PC);
+
+ // There are no rules for CROSS_SITE_NO_3PC, so the URL should not change.
+ ASSERT_EQ(result.filtered_url, destination);
+ ASSERT_EQ(result.filtered_param_count, 0);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, MixedUseCases) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL destination =
+ GURL{"https://destination.xyz?plzblock=1&plzblock1=2&nochange=asdf"};
+
+ std::string encoded_classification =
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{"source.xyz",
+ {{FilterClassification::CROSS_SITE_NO_3PC, {"plzblock"}}}}},
+ {{"destination.xyz",
+ {{FilterClassification::CROSS_OTR, {"plzblock1"}}}}});
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ // With the flag set, the URL could be filtered.
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled,
+ {{"classifications", encoded_classification}});
+
+ GURL expected = GURL{"https://destination.xyz?plzblock1=2&nochange=asdf"};
+ FilterResult result =
+ FilterUrl(source, destination, FilterClassification::CROSS_SITE_NO_3PC);
+
+ // The only rule for CROSS_SITE_NO_3PC is to remove `plzblock` from
+ // source.xyz.
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 1);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, MultipleUseCases) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL destination = GURL{
+ "https://"
+ "destination.xyz?blockotrsource=1&blockno3pcdest=1&plzblock=1&plzblock1="
+ "2&nochange=asdf"};
+
+ std::string encoded_classification =
+ CreateBase64EncodedFilterParamClassificationForTesting(
+ {{"source.xyz",
+ {{FilterClassification::CROSS_OTR, {"blockotrsource"}},
+ {FilterClassification::CROSS_SITE_NO_3PC, {"plzblock"}}}}},
+ {{"destination.xyz",
+ {{FilterClassification::CROSS_SITE_NO_3PC, {"blockno3pcdest"}},
+ {FilterClassification::CROSS_OTR, {"plzblock1"}}}}});
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ // With the flag set, the URL could be filtered.
+ scoped_feature_list.InitAndEnableFeatureWithParameters(
+ features::kIncognitoParamFilterEnabled,
+ {{"classifications", encoded_classification}});
+
+ GURL expected = GURL{
+ "https://destination.xyz?blockotrsource=1&plzblock1=2&nochange=asdf"};
+ FilterResult result =
+ FilterUrl(source, destination, FilterClassification::CROSS_SITE_NO_3PC);
+
+ // `blockotrsource` should be left alone, but `blockno3pcdest` should not.
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 2);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::NON_EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, ExperimentalClassifications) {
+ GURL source = GURL{"http://source.xyz"};
+ GURL destination = GURL{
+ "https://"
+ "destination.xyz?srcexperimental=1&destexperimental=2&noblock=1&plzblock="
+ "1&plzblockdest=1"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ // In addition to the default `plzblock`, also set `srcexperimental` as an
+ // experiment-driven parameter.
+ source_classification_map["source.xyz"]
+ [FilterClassification::USE_CASE_UNKNOWN]
+ ["srcexperimental"] =
+ ClassificationExperimentStatus::EXPERIMENTAL;
+
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblockdest"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+ // In addition to the default `plzblockdest`, also set `destexperimental` as
+ // an experiment-driven parameter.
+ destination_classification_map
+ ["destination.xyz"][FilterClassification::USE_CASE_UNKNOWN]
+ ["destexperimental"] = ClassificationExperimentStatus::EXPERIMENTAL;
+
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ // `blockotrsource` should be left alone, but `blockno3pcdest` should not.
+ GURL expected = GURL{"https://destination.xyz?noblock=1"};
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 4);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::EXPERIMENTAL);
+}
+
+TEST_F(UrlParamFiltererTest, ExperimentalClassificationsWithNestedUrl) {
+ GURL source = GURL{"https://source.xyz"};
+ GURL destination = GURL{
+ "https://"
+ "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
+ "3Fplzblock1%"
+ "3D123%26destexperimental%3D1%26nochange%3Dasdf&PLZBLOCK1=321&nochange="
+ "asdf"};
+ ClassificationMap source_classification_map =
+ CreateClassificationMapForTesting(
+ {{"source.xyz", {"plzblock"}}},
+ FilterClassification_SiteRole::FilterClassification_SiteRole_SOURCE);
+ ClassificationMap destination_classification_map =
+ CreateClassificationMapForTesting(
+ {{"destination.xyz", {"plzblock1"}}, {"source.xyz", {"plzblock1"}}},
+ FilterClassification_SiteRole::
+ FilterClassification_SiteRole_DESTINATION);
+
+ // In addition to the default params, also set `destexperimental` as
+ // an experiment-driven parameter and verify the nested URL correctly results
+ // in `ClassificationExperimentStatus::EXPERIMENTAL`.
+ destination_classification_map
+ ["destination.xyz"][FilterClassification::USE_CASE_UNKNOWN]
+ ["destexperimental"] = ClassificationExperimentStatus::EXPERIMENTAL;
+
+ GURL expected = GURL{
+ "https://"
+ "subdomain.source.xyz?destination=https%3A%2F%2Fdestination.xyz%2F%"
+ "3Fnochange%"
+ "3Dasdf&nochange=asdf"};
+ FilterResult result = FilterUrl(
+ source, destination, source_classification_map,
+ destination_classification_map, FilterClassification::USE_CASE_UNKNOWN);
+ ASSERT_EQ(result.filtered_url, expected);
+ ASSERT_EQ(result.filtered_param_count, 3);
+ ASSERT_EQ(result.experimental_status,
+ ClassificationExperimentStatus::EXPERIMENTAL);
+}
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/ios/BUILD.gn b/chromium/components/url_param_filter/ios/BUILD.gn
new file mode 100644
index 00000000000..063acaaf603
--- /dev/null
+++ b/chromium/components/url_param_filter/ios/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+source_set("ios") {
+ configs += [ "//build/config/compiler:enable_arc" ]
+ sources = [
+ "cross_otr_tab_helper.h",
+ "cross_otr_tab_helper.mm",
+ ]
+ deps = [
+ "//base",
+ "//components/url_param_filter/core",
+ "//ios/web/public:public",
+ "//ios/web/public:web_state_observer",
+ "//ios/web/public/navigation:navigation",
+ "//ui/base:base",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ configs += [ "//build/config/compiler:enable_arc" ]
+ sources = [ "cross_otr_tab_helper_unittest.mm" ]
+ deps = [
+ ":ios",
+ "//base/test:test_support",
+ "//components/url_param_filter/core:core",
+ "//components/url_param_filter/core:test_support",
+ "//components/url_param_filter/core:url_param_filter_classification_proto",
+ "//ios/web/public/test",
+ "//ios/web/public/test:test_fixture",
+ "//ios/web/public/test/fakes:fakes",
+ "//testing/gtest",
+ "//ui/base",
+ "//url",
+ ]
+}
diff --git a/chromium/components/url_param_filter/ios/DEPS b/chromium/components/url_param_filter/ios/DEPS
new file mode 100644
index 00000000000..65222662596
--- /dev/null
+++ b/chromium/components/url_param_filter/ios/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+ios/web/public",
+ "+ios/web/public/test",
+ "+components/url_param_filter/core",
+]
diff --git a/chromium/components/url_param_filter/ios/cross_otr_tab_helper.h b/chromium/components/url_param_filter/ios/cross_otr_tab_helper.h
new file mode 100644
index 00000000000..c154d41ea28
--- /dev/null
+++ b/chromium/components/url_param_filter/ios/cross_otr_tab_helper.h
@@ -0,0 +1,74 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_URL_PARAM_FILTER_IOS_CROSS_OTR_TAB_HELPER_H_
+#define COMPONENTS_URL_PARAM_FILTER_IOS_CROSS_OTR_TAB_HELPER_H_
+
+#include "ios/web/public/navigation/navigation_context.h"
+#include "ios/web/public/web_state.h"
+#include "ios/web/public/web_state_observer.h"
+#include "ios/web/public/web_state_user_data.h"
+#include "ui/base/page_transition_types.h"
+
+namespace web {
+class WebState;
+class NavigationContext;
+} // namespace web
+
+namespace url_param_filter {
+
+// This class is created to measure the effect of experimentally filtering
+// URLs. It is only attached to WebStates created via an "Open In Incognito"
+// press.
+//
+// The state-machine logic measuring refreshes in class should be kept in sync
+// with the CrossOtrObserver at components/url_param_filter/content/ which
+// performs similar observations.
+class CrossOtrTabHelper : public web::WebStateObserver,
+ public web::WebStateUserData<CrossOtrTabHelper> {
+ public:
+ ~CrossOtrTabHelper() override;
+
+ // Attaches the observer in cases where it should do so; leaves `web_state`
+ // unchanged otherwise.
+ static void CreateForWebState(web::WebState* web_state);
+
+ // web::WebStateObserver:
+ void DidStartNavigation(web::WebState* web_state,
+ web::NavigationContext* navigation_context) override;
+ void DidFinishNavigation(web::WebState* web_state,
+ web::NavigationContext* navigation_context) override;
+ void WebStateDestroyed(web::WebState* web_state) override;
+
+ // Returns whether this observer is in Cross-OTR state, used for testing.
+ bool GetCrossOtrStateForTesting() const;
+
+ private:
+ CrossOtrTabHelper(web::WebState* web_state);
+ friend class WebStateUserData<CrossOtrTabHelper>;
+
+ // Flushes metrics and removes the observer from the WebState.
+ void Detach(web::WebState* web_state);
+
+ // Drives state machine logic; we write the cross-OTR response code metric
+ // only for the first navigation, which is that which would have parameters
+ // filtered.
+ bool observed_response_ = false;
+
+ // Tracks refreshes observed, which could point to an issue with param
+ // filtering causing unexpected behavior for the user.
+ int refresh_count_ = 0;
+
+ // Whether top-level navigations should have filtering applied. Starts true
+ // then switches to false once a navigation completes and then either:
+ // user interaction is observed or a new navigation starts that is not a
+ // client redirect.
+ bool protecting_navigations_ = true;
+
+ WEB_STATE_USER_DATA_KEY_DECL();
+};
+
+} // namespace url_param_filter
+
+#endif // COMPONENTS_URL_PARAM_FILTER_IOS_CROSS_OTR_TAB_HELPER_H_
diff --git a/chromium/components/url_param_filter/ios/cross_otr_tab_helper.mm b/chromium/components/url_param_filter/ios/cross_otr_tab_helper.mm
new file mode 100644
index 00000000000..aad6c29c509
--- /dev/null
+++ b/chromium/components/url_param_filter/ios/cross_otr_tab_helper.mm
@@ -0,0 +1,126 @@
+// Copyright 2022 The Chromium 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/url_param_filter/ios/cross_otr_tab_helper.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
+#include "components/url_param_filter/core/features.h"
+#include "ios/web/public/browser_state.h"
+#include "ios/web/public/navigation/navigation_context.h"
+#include "ios/web/public/web_state.h"
+#include "ios/web/public/web_state_observer.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "ui/base/page_transition_types.h"
+
+namespace url_param_filter {
+
+namespace {
+
+constexpr char kCrossOtrRefreshCountMetricName[] =
+ "Navigation.CrossOtr.ContextMenu.RefreshCountExperimental";
+constexpr char kCrossOtrResponseCodeMetricName[] =
+ "Navigation.CrossOtr.ContextMenu.ResponseCodeExperimental";
+
+// Returns true if the web_state corresponds to one where a user enters
+// incognito by long-pressing on an embedded link and selecting "Open In
+// Incognito".
+bool IsOpenInIncognito(web::WebState* web_state,
+ web::NavigationContext* navigation_context) {
+ // "Open In Incognito" that creates an OTR browser state causes a TYPED
+ // transition.
+ return navigation_context->HasUserGesture() &&
+ ui::PageTransitionCoreTypeIs(navigation_context->GetPageTransition(),
+ ui::PAGE_TRANSITION_TYPED);
+}
+} // namespace
+
+// static
+void CrossOtrTabHelper::CreateForWebState(web::WebState* web_state) {
+ DCHECK(web_state);
+ WebStateUserData<CrossOtrTabHelper>::CreateForWebState(web_state);
+}
+
+CrossOtrTabHelper::CrossOtrTabHelper(web::WebState* web_state) {
+ DCHECK(web_state);
+ web_state->AddObserver(this);
+}
+
+CrossOtrTabHelper::~CrossOtrTabHelper() = default;
+
+void CrossOtrTabHelper::DidStartNavigation(
+ web::WebState* web_state,
+ web::NavigationContext* navigation_context) {
+ if (!observed_response_) {
+ protecting_navigations_ = IsOpenInIncognito(web_state, navigation_context);
+ return;
+ }
+
+ if (!(navigation_context->GetPageTransition() &
+ ui::PAGE_TRANSITION_CLIENT_REDIRECT) ||
+ navigation_context->HasUserGesture()) {
+ // If a navigation that isn't a client redirect occurs, or a user-activated
+ // navigation occurs we stop protecting navigations.
+ protecting_navigations_ = false;
+ }
+}
+
+void CrossOtrTabHelper::DidFinishNavigation(
+ web::WebState* web_state,
+ web::NavigationContext* navigation_context) {
+ // web::NavigationContext doesn't expose whether or not it's a main frame
+ // navigation.
+ if (navigation_context->IsSameDocument()) {
+ // We only are concerned with non-same doc navigations.
+ return;
+ }
+ // We only want the first navigation, including client redirects occurring
+ // without having observed user activation, to be counted; after that, no
+ // response codes should be tracked. The observer is left in place to track
+ // refreshes on the first page.
+ if (protecting_navigations_) {
+ observed_response_ = true;
+ const net::HttpResponseHeaders* headers =
+ navigation_context->GetResponseHeaders();
+ // TODO(https://crbug.com/1324194) See comment two about restricting metric
+ // collection here.
+ if (headers) {
+ base::UmaHistogramSparse(
+ kCrossOtrResponseCodeMetricName,
+ net::HttpUtil::MapStatusCodeForHistogram(headers->response_code()));
+ }
+ return;
+ }
+ if (ui::PageTransitionCoreTypeIs(navigation_context->GetPageTransition(),
+ ui::PAGE_TRANSITION_RELOAD)) {
+ refresh_count_++;
+ return;
+ }
+ if (navigation_context->HasCommitted() && !protecting_navigations_) {
+ Detach(web_state);
+ // DO NOT add code past this point. `this` is destroyed.
+ }
+}
+
+void CrossOtrTabHelper::WebStateDestroyed(web::WebState* web_state) {
+ Detach(web_state);
+ // DO NOT add code past this point. `this` is destroyed.
+}
+
+bool CrossOtrTabHelper::GetCrossOtrStateForTesting() const {
+ return protecting_navigations_;
+}
+
+void CrossOtrTabHelper::Detach(web::WebState* web_state) {
+ // TODO(https://crbug.com/1324194) See comment two about restricting metric
+ // collection here.
+ base::UmaHistogramCounts100(kCrossOtrRefreshCountMetricName, refresh_count_);
+ web_state->RemoveObserver(this);
+ web_state->RemoveUserData(CrossOtrTabHelper::UserDataKey());
+}
+
+WEB_STATE_USER_DATA_KEY_IMPL(CrossOtrTabHelper)
+
+} // namespace url_param_filter
diff --git a/chromium/components/url_param_filter/ios/cross_otr_tab_helper_unittest.mm b/chromium/components/url_param_filter/ios/cross_otr_tab_helper_unittest.mm
new file mode 100644
index 00000000000..1f35ff9d217
--- /dev/null
+++ b/chromium/components/url_param_filter/ios/cross_otr_tab_helper_unittest.mm
@@ -0,0 +1,270 @@
+// Copyright 2022 The Chromium 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/url_param_filter/ios/cross_otr_tab_helper.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "ios/web/public/test/fakes/fake_browser_state.h"
+#include "ios/web/public/test/fakes/fake_navigation_context.h"
+#include "ios/web/public/test/fakes/fake_web_state.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/platform_test.h"
+
+namespace web {
+class FakeNavigationContext;
+class FakeBrowserState;
+} // namespace web
+
+namespace url_param_filter {
+
+constexpr char kResponseCodeMetricName[] =
+ "Navigation.CrossOtr.ContextMenu.ResponseCodeExperimental";
+constexpr char kCrossOtrRefreshCountMetricName[] =
+ "Navigation.CrossOtr.ContextMenu.RefreshCountExperimental";
+
+class CrossOtrTabHelperTest : public PlatformTest {
+ public:
+ CrossOtrTabHelperTest() : PlatformTest() {
+ // Create new OTR web_state to navigate to.
+ std::unique_ptr<web::FakeWebState> unique_web_state =
+ std::make_unique<web::FakeWebState>();
+ web::FakeBrowserState browser_state;
+ browser_state.SetOffTheRecord(true);
+ unique_web_state->SetBrowserState(&browser_state);
+ web_state_ = unique_web_state.get();
+
+ // Initialize observer with this web_state.
+ CrossOtrTabHelper::CreateForWebState(web_state_);
+ observer_ = CrossOtrTabHelper::FromWebState(web_state_);
+
+ // Create Cross OTR navigation which...
+ // (1) navigates into OTR
+ context_.SetWebState(std::move(unique_web_state));
+ // (2) transitions into OTR from "Open In Incognito"
+ context_.SetPageTransition(ui::PAGE_TRANSITION_TYPED);
+ // (3) is a user-initiated navigation
+ context_.SetHasUserGesture(true);
+ }
+
+ protected:
+ web::FakeWebState* web_state() { return web_state_; };
+ CrossOtrTabHelper* observer() { return observer_; };
+ web::FakeNavigationContext* context() { return &context_; };
+
+ private:
+ base::test::ScopedFeatureList features_;
+ web::FakeWebState* web_state_;
+ CrossOtrTabHelper* observer_;
+ web::FakeNavigationContext context_;
+};
+
+TEST_F(CrossOtrTabHelperTest, ObserverAttached) {
+ auto web_state = std::make_unique<web::FakeWebState>();
+ CrossOtrTabHelper::CreateForWebState(web_state.get());
+ // The observer should have been attached.
+ ASSERT_NE(CrossOtrTabHelper::FromWebState(web_state.get()), nullptr);
+}
+
+TEST_F(CrossOtrTabHelperTest, CreateKey) {
+ auto web_state = std::make_unique<web::FakeWebState>();
+ CrossOtrTabHelper::CreateForWebState(web_state.get());
+ // The observer should have been attached.
+ ASSERT_NE(CrossOtrTabHelper::FromWebState(web_state.get()), nullptr);
+}
+TEST_F(CrossOtrTabHelperTest, DuplicateCreateKey) {
+ auto web_state = std::make_unique<web::FakeWebState>();
+ CrossOtrTabHelper::CreateForWebState(web_state.get());
+ CrossOtrTabHelper::CreateForWebState(web_state.get());
+ // The observer should have been attached.
+ ASSERT_NE(CrossOtrTabHelper::FromWebState(web_state.get()), nullptr);
+}
+
+TEST_F(CrossOtrTabHelperTest, TransitionsWithNonTypedLink_NotCrossOtr) {
+ // Create new non-OTR web_state to navigate to.
+ std::unique_ptr<web::FakeWebState> unique_web_state =
+ std::make_unique<web::FakeWebState>();
+ web::FakeBrowserState browser_state;
+ browser_state.SetOffTheRecord(true);
+ unique_web_state->SetBrowserState(&browser_state);
+ web::FakeWebState* web_state = unique_web_state.get();
+
+ // Initialize observer with this web_state.
+ CrossOtrTabHelper::CreateForWebState(web_state);
+ CrossOtrTabHelper* observer = CrossOtrTabHelper::FromWebState(web_state);
+ ASSERT_NE(observer, nullptr);
+
+ // Create navigation context.
+ web::FakeNavigationContext context;
+ context.SetWebState(std::move(unique_web_state));
+ context.SetPageTransition(ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+ context.SetHasUserGesture(true);
+
+ // We don't enter cross-OTR state since we didn't navigate to OTR via "Open In
+ // Incognito".
+ observer->DidStartNavigation(web_state, &context);
+ EXPECT_FALSE(observer->GetCrossOtrStateForTesting());
+}
+
+TEST_F(CrossOtrTabHelperTest, NonUserInitiatedNavigation_NotCrossOtr) {
+ // Create new non-OTR web_state to navigate to.
+ std::unique_ptr<web::FakeWebState> unique_web_state =
+ std::make_unique<web::FakeWebState>();
+ web::FakeBrowserState browser_state;
+ browser_state.SetOffTheRecord(true);
+ unique_web_state->SetBrowserState(&browser_state);
+ web::FakeWebState* web_state = unique_web_state.get();
+
+ // Initialize observer with this web_state.
+ CrossOtrTabHelper::CreateForWebState(web_state);
+ CrossOtrTabHelper* observer = CrossOtrTabHelper::FromWebState(web_state);
+ ASSERT_NE(observer, nullptr);
+
+ // Create navigation context.
+ web::FakeNavigationContext context;
+ context.SetWebState(std::move(unique_web_state));
+ context.SetPageTransition(ui::PAGE_TRANSITION_TYPED);
+ context.SetHasUserGesture(false);
+
+ // We don't enter cross-OTR state since we didn't navigate to OTR via
+ // user-initiated press.
+ observer->DidStartNavigation(web_state, &context);
+ EXPECT_FALSE(observer->GetCrossOtrStateForTesting());
+}
+
+TEST_F(CrossOtrTabHelperTest, FinishedNavigation) {
+ base::HistogramTester histogram_tester;
+ ASSERT_NE(observer(), nullptr);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ context()->SetResponseHeaders(response);
+
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+
+ ASSERT_TRUE(observer()->GetCrossOtrStateForTesting());
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+}
+
+TEST_F(CrossOtrTabHelperTest, BadNavigationResponse) {
+ base::HistogramTester histogram_tester;
+ ASSERT_NE(observer(), nullptr);
+ context()->SetResponseHeaders(nullptr);
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 0);
+ // The observer should not cease observation after first load, regardless of
+ // whether the headers include a response code. We still want to see
+ // the refresh count.
+ ASSERT_NE(CrossOtrTabHelper::FromWebState(web_state()), nullptr);
+}
+TEST_F(CrossOtrTabHelperTest, RefreshedAfterNavigation) {
+ base::HistogramTester histogram_tester;
+ ASSERT_NE(observer(), nullptr);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ context()->SetResponseHeaders(response);
+
+ // Perform the initial navigation.
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+
+ // Simulate a reload.
+ context()->SetPageTransition(ui::PAGE_TRANSITION_RELOAD);
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+ observer()->WebStateDestroyed(web_state());
+
+ histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 1);
+ ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 1);
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+}
+TEST_F(CrossOtrTabHelperTest, UncommittedNavigationWithRefresh) {
+ base::HistogramTester histogram_tester;
+ ASSERT_NE(observer(), nullptr);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ context()->SetResponseHeaders(response);
+
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+ ASSERT_TRUE(observer()->GetCrossOtrStateForTesting());
+
+ // Finish a non-reload navigation, but one that isn't committed (so no
+ // actual navigation away from the monitored page)
+ context()->SetPageTransition(ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+ context()->SetIsSameDocument(false);
+ context()->SetHasCommitted(false);
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+ // We just observed another navigation not due to a client redirect, so
+ // should no longer be in the cross-OTR state.
+ ASSERT_FALSE(observer()->GetCrossOtrStateForTesting());
+
+ // After that uncommitted navigation, trigger a refresh, then destroy.
+ context()->SetPageTransition(ui::PAGE_TRANSITION_RELOAD);
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+ observer()->WebStateDestroyed(web_state());
+
+ // We had 1 relevant refresh.
+ histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 1);
+ ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 1);
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+}
+TEST_F(CrossOtrTabHelperTest, MultipleRefreshesAfterNavigation) {
+ base::HistogramTester histogram_tester;
+ ASSERT_NE(observer(), nullptr);
+
+ scoped_refptr<net::HttpResponseHeaders> response =
+ base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+ context()->SetResponseHeaders(response);
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+
+ // Reload twice and ensure the count is persisted.
+ context()->SetPageTransition(ui::PAGE_TRANSITION_RELOAD);
+ observer()->DidStartNavigation(web_state(), context());
+ // With the refresh navigation started, we are no longer in cross-OTR mode.
+ ASSERT_FALSE(observer()->GetCrossOtrStateForTesting());
+ observer()->DidFinishNavigation(web_state(), context());
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+
+ // Navigating away means no more observer.
+ context()->SetPageTransition(ui::PAGE_TRANSITION_LINK);
+ context()->SetIsSameDocument(false);
+ context()->SetHasCommitted(true);
+ observer()->DidStartNavigation(web_state(), context());
+ observer()->DidFinishNavigation(web_state(), context());
+
+ ASSERT_EQ(CrossOtrTabHelper::FromWebState(web_state()), nullptr);
+
+ histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 1);
+ ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 2);
+ histogram_tester.ExpectTotalCount(kResponseCodeMetricName, 1);
+ histogram_tester.ExpectUniqueSample(
+ kResponseCodeMetricName, net::HttpUtil::MapStatusCodeForHistogram(200),
+ 1);
+}
+
+} // namespace url_param_filter
diff --git a/chromium/components/url_pattern_index/fuzzy_pattern_matching.cc b/chromium/components/url_pattern_index/fuzzy_pattern_matching.cc
index 92b6032c17f..e80eba3f5e7 100644
--- a/chromium/components/url_pattern_index/fuzzy_pattern_matching.cc
+++ b/chromium/components/url_pattern_index/fuzzy_pattern_matching.cc
@@ -6,6 +6,8 @@
#include <algorithm>
+#include "base/check_op.h"
+
namespace url_pattern_index {
namespace {
diff --git a/chromium/components/url_pattern_index/ngram_extractor.h b/chromium/components/url_pattern_index/ngram_extractor.h
index 0459d4b6f8f..296ff401c92 100644
--- a/chromium/components/url_pattern_index/ngram_extractor.h
+++ b/chromium/components/url_pattern_index/ngram_extractor.h
@@ -37,8 +37,14 @@ template <size_t N,
class NGramExtractor {
public:
// An STL compatible input iterator over N-grams contained in a string.
- class Iterator : public std::iterator<std::input_iterator_tag, NGramType> {
+ class Iterator {
public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = NGramType;
+ using difference_type = std::ptrdiff_t;
+ using pointer = NGramType*;
+ using reference = NGramType&;
+
// Creates an iterator, which points to the leftmost valid N-gram within the
// |extractor|'s string, starting from |head|.
Iterator(const NGramExtractor& extractor,
diff --git a/chromium/components/url_pattern_index/string_splitter.h b/chromium/components/url_pattern_index/string_splitter.h
index 1de7fc08714..b6cb9e51206 100644
--- a/chromium/components/url_pattern_index/string_splitter.h
+++ b/chromium/components/url_pattern_index/string_splitter.h
@@ -23,9 +23,14 @@ namespace url_pattern_index {
template <typename IsSeparator>
class StringSplitter {
public:
- class Iterator
- : public std::iterator<std::input_iterator_tag, base::StringPiece> {
+ class Iterator {
public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = base::StringPiece;
+ using difference_type = std::ptrdiff_t;
+ using pointer = base::StringPiece*;
+ using reference = base::StringPiece&;
+
// Creates an iterator, which points to the leftmost token within the
// |splitter|'s |text|, starting from |head|.
Iterator(const StringSplitter& splitter,
diff --git a/chromium/components/url_pattern_index/url_pattern_index.cc b/chromium/components/url_pattern_index/url_pattern_index.cc
index 8911cd6110c..8546d93d956 100644
--- a/chromium/components/url_pattern_index/url_pattern_index.cc
+++ b/chromium/components/url_pattern_index/url_pattern_index.cc
@@ -774,6 +774,13 @@ bool IsRuleGeneric(const flat::UrlRule& rule) {
return !rule.initiator_domains_included();
}
+// Returns whether the `host` matches the domain conditions. It's considered a
+// match if both:
+// 1. An included domain matches the `host`, or `domains_included` is omitted
+// entirely (since rules match all domains by default).
+// 2. No excluded domain match the `host`, or the longest matching excluded
+// domain is shorter than the longest matching included domain (since
+// longer, more specific domain matches take precedence).
bool DoesHostMatchDomainLists(
base::StringPiece host,
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>*
diff --git a/chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager.cc b/chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager.cc
index 2bd5b1dc322..fd24292a803 100644
--- a/chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager.cc
+++ b/chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager.cc
@@ -101,7 +101,7 @@ void UrlRequestRewriteRulesManager::Updater::OnRulesUpdated(
void UrlRequestRewriteRulesManager::Updater::MaybeRegisterExistingRenderFrame(
content::RenderFrameHost* render_frame_host) {
- if (render_frame_host->IsRenderFrameCreated()) {
+ if (render_frame_host->IsRenderFrameLive()) {
// Call RenderFrameCreated() for frames that were created before this
// observer started observing this WebContents.
RenderFrameCreated(render_frame_host);
diff --git a/chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager_browsertest.cc b/chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager_browsertest.cc
index 9dec9ec63c7..ac4a7c7f78f 100644
--- a/chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager_browsertest.cc
+++ b/chromium/components/url_rewrite/browser/url_request_rewrite_rules_manager_browsertest.cc
@@ -4,6 +4,7 @@
#include "base/base_paths.h"
#include "base/files/file_path.h"
+#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/test/scoped_feature_list.h"
#include "components/url_rewrite/browser/url_request_rewrite_rules_manager.h"
@@ -44,7 +45,7 @@ class InnerWebContentsHandler : public content::WebContentsObserver {
}
base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
- UrlRequestRewriteRulesManager* url_request_rewrite_rules_manager_;
+ raw_ptr<UrlRequestRewriteRulesManager> url_request_rewrite_rules_manager_;
};
class UrlRequestRewriteRulesManagerBrowserTest
diff --git a/chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation.cc b/chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation.cc
index 2a9cc3ba408..0a1861d82c7 100644
--- a/chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation.cc
+++ b/chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation.cc
@@ -95,18 +95,18 @@ bool ValidateAppendToQuery(
bool ValidateRewrite(const mojom::UrlRequestActionPtr& action) {
switch (action->which()) {
- case mojom::UrlRequestAction::Tag::ADD_HEADERS:
+ case mojom::UrlRequestAction::Tag::kAddHeaders:
return ValidateAddHeaders(action->get_add_headers());
- case mojom::UrlRequestAction::Tag::REMOVE_HEADER:
+ case mojom::UrlRequestAction::Tag::kRemoveHeader:
return ValidateRemoveHeader(action->get_remove_header());
- case mojom::UrlRequestAction::Tag::SUBSTITUTE_QUERY_PATTERN:
+ case mojom::UrlRequestAction::Tag::kSubstituteQueryPattern:
return ValidateSubstituteQueryPattern(
action->get_substitute_query_pattern());
- case mojom::UrlRequestAction::Tag::REPLACE_URL:
+ case mojom::UrlRequestAction::Tag::kReplaceUrl:
return ValidateReplaceUrl(action->get_replace_url());
- case mojom::UrlRequestAction::Tag::APPEND_TO_QUERY:
+ case mojom::UrlRequestAction::Tag::kAppendToQuery:
return ValidateAppendToQuery(action->get_append_to_query());
- case mojom::UrlRequestAction::Tag::POLICY:
+ case mojom::UrlRequestAction::Tag::kPolicy:
return true;
}
}
diff --git a/chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation_unittest.cc b/chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation_unittest.cc
index 393c6259e00..11d0105510e 100644
--- a/chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation_unittest.cc
+++ b/chromium/components/url_rewrite/browser/url_request_rewrite_rules_validation_unittest.cc
@@ -144,9 +144,9 @@ TEST(UrlRequestRewriteRulesValidationTest, ValidateAppendToQuery) {
}
// Tests validation is working as expected.
-TEST(UrlRequestRewriteRulesValidationTest, ValidateEmptyAction) {
+TEST(UrlRequestRewriteRulesValidationTest, ValidateNullAction) {
// Empty action.
- EXPECT_FALSE(ValidateRulesFromAction(mojom::UrlRequestAction::New()));
+ EXPECT_FALSE(ValidateRulesFromAction(nullptr));
}
TEST(UrlRequestRewriteRulesValidationTest, ValidateNullRules) {
diff --git a/chromium/components/url_rewrite/common/url_loader_throttle.cc b/chromium/components/url_rewrite/common/url_loader_throttle.cc
index 5ebd0bccb6e..443b4daaadb 100644
--- a/chromium/components/url_rewrite/common/url_loader_throttle.cc
+++ b/chromium/components/url_rewrite/common/url_loader_throttle.cc
@@ -150,7 +150,7 @@ bool IsRequestAllowed(network::ResourceRequest* request,
if (rule->actions.size() != 1)
continue;
- if (rule->actions[0]->which() != mojom::UrlRequestAction::Tag::POLICY)
+ if (rule->actions[0]->which() != mojom::UrlRequestAction::Tag::kPolicy)
continue;
if (!RuleFiltersMatchRequest(request, rule))
@@ -214,23 +214,23 @@ void URLLoaderThrottle::ApplyRewrite(
network::ResourceRequest* request,
const mojom::UrlRequestActionPtr& rewrite) {
switch (rewrite->which()) {
- case mojom::UrlRequestAction::Tag::ADD_HEADERS:
+ case mojom::UrlRequestAction::Tag::kAddHeaders:
ApplyAddHeaders(request, rewrite->get_add_headers());
return;
- case mojom::UrlRequestAction::Tag::REMOVE_HEADER:
+ case mojom::UrlRequestAction::Tag::kRemoveHeader:
ApplyRemoveHeader(request, rewrite->get_remove_header());
return;
- case mojom::UrlRequestAction::Tag::SUBSTITUTE_QUERY_PATTERN:
+ case mojom::UrlRequestAction::Tag::kSubstituteQueryPattern:
ApplySubstituteQueryPattern(request,
rewrite->get_substitute_query_pattern());
return;
- case mojom::UrlRequestAction::Tag::REPLACE_URL:
+ case mojom::UrlRequestAction::Tag::kReplaceUrl:
ApplyReplaceUrl(request, rewrite->get_replace_url());
return;
- case mojom::UrlRequestAction::Tag::APPEND_TO_QUERY:
+ case mojom::UrlRequestAction::Tag::kAppendToQuery:
ApplyAppendToQuery(request, rewrite->get_append_to_query());
return;
- case mojom::UrlRequestAction::Tag::POLICY:
+ case mojom::UrlRequestAction::Tag::kPolicy:
// "Policy" is interpreted elsewhere; it is a no-op for rewriting.
return;
}
diff --git a/chromium/components/user_education/DEPS b/chromium/components/user_education/DEPS
new file mode 100644
index 00000000000..a74f63f361d
--- /dev/null
+++ b/chromium/components/user_education/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+base",
+ "+components/feature_engagement/public",
+ "+components/strings",
+ "+components/vector_icons",
+ "+ui/accessibility",
+ "+ui/base",
+ "+ui/color",
+ "+ui/gfx",
+]
diff --git a/chromium/components/user_education/DIR_METADATA b/chromium/components/user_education/DIR_METADATA
new file mode 100644
index 00000000000..527573022d8
--- /dev/null
+++ b/chromium/components/user_education/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+ component: "UI>Browser>UserEducation"
+}
diff --git a/chromium/components/user_education/OWNERS b/chromium/components/user_education/OWNERS
new file mode 100644
index 00000000000..a93db645538
--- /dev/null
+++ b/chromium/components/user_education/OWNERS
@@ -0,0 +1,6 @@
+dfried@chromium.org
+dpenning@chromium.org
+
+# Backup
+bsep@chromium.org
+pbos@chromium.org
diff --git a/chromium/components/user_education/README.md b/chromium/components/user_education/README.md
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/components/user_education/README.md
diff --git a/chromium/components/user_education/common/BUILD.gn b/chromium/components/user_education/common/BUILD.gn
new file mode 100644
index 00000000000..a90db5a0de6
--- /dev/null
+++ b/chromium/components/user_education/common/BUILD.gn
@@ -0,0 +1,78 @@
+# Copyright 2022 The Chromium 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/buildflag_header.gni")
+import("//build/config/ui.gni")
+
+source_set("common") {
+ sources = [
+ "feature_promo_controller.cc",
+ "feature_promo_controller.h",
+ "feature_promo_registry.cc",
+ "feature_promo_registry.h",
+ "feature_promo_snooze_service.cc",
+ "feature_promo_snooze_service.h",
+ "feature_promo_specification.cc",
+ "feature_promo_specification.h",
+ "help_bubble.cc",
+ "help_bubble.h",
+ "help_bubble_factory.h",
+ "help_bubble_factory_registry.cc",
+ "help_bubble_factory_registry.h",
+ "help_bubble_params.cc",
+ "help_bubble_params.h",
+ "scoped_new_badge_tracker_base.cc",
+ "scoped_new_badge_tracker_base.h",
+ "tutorial.cc",
+ "tutorial.h",
+ "tutorial_description.cc",
+ "tutorial_description.h",
+ "tutorial_identifier.h",
+ "tutorial_registry.cc",
+ "tutorial_registry.h",
+ "tutorial_service.cc",
+ "tutorial_service.h",
+ "user_education_class_properties.cc",
+ "user_education_class_properties.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/feature_engagement/public",
+ "//components/strings",
+ "//components/variations",
+ "//components/vector_icons",
+ "//skia",
+ "//third_party/abseil-cpp:absl",
+ "//ui/accessibility",
+ "//ui/base",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "feature_promo_snooze_service_unittest.cc",
+ "help_bubble_factory_registry_unittest.cc",
+ "tutorial_unittest.cc",
+ ]
+
+ deps = [
+ ":common",
+ "//base",
+ "//base/test:test_support",
+ "//components/feature_engagement/public",
+ "//components/strings",
+ "//components/user_education/test",
+ "//components/variations",
+ "//skia",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/abseil-cpp:absl",
+ "//ui/accessibility",
+ "//ui/base",
+ "//ui/base:test_support",
+ ]
+}
diff --git a/chromium/components/user_education/common/feature_promo_controller.cc b/chromium/components/user_education/common/feature_promo_controller.cc
new file mode 100644
index 00000000000..2b6481360de
--- /dev/null
+++ b/chromium/components/user_education/common/feature_promo_controller.cc
@@ -0,0 +1,551 @@
+// 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/user_education/common/feature_promo_controller.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/callback_list.h"
+#include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
+#include "components/feature_engagement/public/event_constants.h"
+#include "components/feature_engagement/public/feature_constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_education/common/feature_promo_registry.h"
+#include "components/user_education/common/feature_promo_snooze_service.h"
+#include "components/user_education/common/help_bubble_factory_registry.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/common/tutorial.h"
+#include "components/user_education/common/tutorial_service.h"
+#include "ui/accessibility/ax_mode.h"
+#include "ui/accessibility/platform/ax_platform_node.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace user_education {
+
+FeaturePromoController::PromoHandle::PromoHandle() = default;
+
+FeaturePromoController::PromoHandle::PromoHandle(
+ base::WeakPtr<FeaturePromoController> controller,
+ const base::Feature* feature)
+ : controller_(std::move(controller)), feature_(feature) {
+ DCHECK(feature_);
+}
+
+FeaturePromoController::PromoHandle::PromoHandle(PromoHandle&& other)
+ : controller_(std::move(other.controller_)),
+ feature_(std::exchange(other.feature_, nullptr)) {}
+
+FeaturePromoController::PromoHandle::~PromoHandle() {
+ Release();
+}
+
+FeaturePromoController::PromoHandle&
+FeaturePromoController::PromoHandle::operator=(PromoHandle&& other) {
+ if (this != &other) {
+ Release();
+ controller_ = std::move(other.controller_);
+ feature_ = std::exchange(other.feature_, nullptr);
+ }
+
+ return *this;
+}
+
+void FeaturePromoController::PromoHandle::Release() {
+ if (controller_)
+ controller_->FinishContinuedPromo(feature_);
+ controller_.reset();
+ feature_ = nullptr;
+}
+
+FeaturePromoController::FeaturePromoController() = default;
+FeaturePromoController::~FeaturePromoController() = default;
+
+FeaturePromoControllerCommon::FeaturePromoControllerCommon(
+ feature_engagement::Tracker* feature_engagement_tracker,
+ FeaturePromoRegistry* registry,
+ HelpBubbleFactoryRegistry* help_bubble_registry,
+ FeaturePromoSnoozeService* snooze_service,
+ TutorialService* tutorial_service)
+ : registry_(registry),
+ feature_engagement_tracker_(feature_engagement_tracker),
+ bubble_factory_registry_(help_bubble_registry),
+ snooze_service_(snooze_service),
+ tutorial_service_(tutorial_service) {
+ DCHECK(feature_engagement_tracker_);
+ DCHECK(bubble_factory_registry_);
+ DCHECK(snooze_service_);
+}
+
+FeaturePromoControllerCommon::~FeaturePromoControllerCommon() = default;
+
+bool FeaturePromoControllerCommon::MaybeShowPromo(
+ const base::Feature& iph_feature,
+ FeaturePromoSpecification::StringReplacements body_text_replacements,
+ BubbleCloseCallback close_callback) {
+ const FeaturePromoSpecification* spec =
+ registry()->GetParamsForFeature(iph_feature);
+ if (!spec)
+ return false;
+
+ DCHECK_EQ(&iph_feature, spec->feature());
+ DCHECK(spec->anchor_element_id());
+
+ // Fetch the anchor element. For now, assume all elements are Views.
+ ui::TrackedElement* const anchor_element =
+ spec->GetAnchorElement(GetAnchorContext());
+
+ if (!anchor_element)
+ return false;
+
+ return MaybeShowPromoFromSpecification(*spec, anchor_element,
+ std::move(body_text_replacements),
+ std::move(close_callback));
+}
+
+bool FeaturePromoControllerCommon::MaybeShowPromoForDemoPage(
+ const base::Feature* iph_feature,
+ FeaturePromoSpecification::StringReplacements body_text_replacements,
+ BubbleCloseCallback close_callback) {
+ if (current_iph_feature_ && promo_bubble_)
+ CloseBubble(*current_iph_feature_);
+ iph_feature_bypassing_tracker_ = iph_feature;
+
+ bool showed_promo = MaybeShowPromo(*iph_feature);
+
+ if (!showed_promo && iph_feature_bypassing_tracker_)
+ iph_feature_bypassing_tracker_ = nullptr;
+
+ return showed_promo;
+}
+
+bool FeaturePromoControllerCommon::MaybeShowPromoFromSpecification(
+ const FeaturePromoSpecification& spec,
+ ui::TrackedElement* anchor_element,
+ FeaturePromoSpecification::StringReplacements body_text_replacements,
+ BubbleCloseCallback close_callback) {
+ CHECK(anchor_element);
+
+ if (promos_blocked_for_testing_)
+ return false;
+
+ // A normal promo cannot show if a critical promo is displayed. These
+ // are not registered with |tracker_| so check here.
+ if (critical_promo_bubble_)
+ return false;
+
+ // Some contexts and anchors are not appropriate for showing normal promos.
+ if (!CanShowPromo(anchor_element))
+ return false;
+
+ // Some checks should not be done in demo mode, because we absolutely want to
+ // trigger the bubble if possible. Put any checks that should be bypassed in
+ // demo mode in this block.
+ const base::Feature* feature = spec.feature();
+ bool feature_is_bypassing_tracker = feature == iph_feature_bypassing_tracker_;
+ if (!(base::FeatureList::IsEnabled(feature_engagement::kIPHDemoMode) ||
+ feature_is_bypassing_tracker) &&
+ snooze_service_->IsBlocked(*feature))
+ return false;
+
+ // Can't show a standard promo if another help bubble is visible.
+ if (bubble_factory_registry_->is_any_bubble_showing())
+ return false;
+
+ // TODO(crbug.com/1258216): Currently this must be called before
+ // ShouldTriggerHelpUI() below. See bug for details.
+ const bool screen_reader_available = CheckScreenReaderPromptAvailable();
+
+ if (!feature_is_bypassing_tracker &&
+ !feature_engagement_tracker_->ShouldTriggerHelpUI(*feature))
+ return false;
+
+ // If the tracker says we should trigger, but we have a promo
+ // currently showing, there is a bug somewhere in here.
+ DCHECK(!current_iph_feature_);
+ current_iph_feature_ = feature;
+
+ // Try to show the bubble and bail out if we cannot.
+ promo_bubble_ = ShowPromoBubbleImpl(
+ spec, anchor_element, std::move(body_text_replacements),
+ screen_reader_available, /* is_critical_promo =*/false);
+ if (!promo_bubble_) {
+ current_iph_feature_ = nullptr;
+ if (!feature_is_bypassing_tracker)
+ feature_engagement_tracker_->Dismissed(*feature);
+ return false;
+ }
+
+ bubble_closed_callback_ = std::move(close_callback);
+
+ // Record count of previous snoozes when an IPH triggers.
+ if (!feature_is_bypassing_tracker) {
+ int snooze_count = snooze_service_->GetSnoozeCount(*feature);
+ base::UmaHistogramExactLinear(
+ "InProductHelp.Promos.SnoozeCountAtTrigger." +
+ std::string(feature->name),
+ snooze_count, FeaturePromoSnoozeService::kUmaMaxSnoozeCount);
+ snooze_service_->OnPromoShown(*feature);
+ }
+ return true;
+}
+
+std::unique_ptr<HelpBubble> FeaturePromoControllerCommon::ShowCriticalPromo(
+ const FeaturePromoSpecification& spec,
+ ui::TrackedElement* anchor_element,
+ FeaturePromoSpecification::StringReplacements body_text_replacements) {
+ if (promos_blocked_for_testing_)
+ return nullptr;
+
+ // Don't preempt an existing critical promo.
+ if (critical_promo_bubble_)
+ return nullptr;
+
+ // If a normal bubble is showing, close it. Won't affect a promo continued
+ // after its bubble has closed.
+ if (current_iph_feature_)
+ CloseBubble(*current_iph_feature_);
+
+ // Snooze and tutorial are not supported for critical promos.
+ DCHECK_NE(FeaturePromoSpecification::PromoType::kSnooze, spec.promo_type());
+ DCHECK_NE(FeaturePromoSpecification::PromoType::kTutorial, spec.promo_type());
+
+ // Create the bubble.
+ auto bubble = ShowPromoBubbleImpl(
+ spec, anchor_element, std::move(body_text_replacements),
+ CheckScreenReaderPromptAvailable(), /* is_critical_promo =*/true);
+ critical_promo_bubble_ = bubble.get();
+ return bubble;
+}
+
+bool FeaturePromoControllerCommon::IsPromoActive(
+ const base::Feature& iph_feature,
+ bool include_continued_promos) const {
+ if (current_iph_feature_ != &iph_feature)
+ return false;
+ return include_continued_promos ||
+ (promo_bubble_ && promo_bubble_->is_open());
+}
+
+bool FeaturePromoControllerCommon::CloseBubble(
+ const base::Feature& iph_feature) {
+ if (current_iph_feature_ != &iph_feature)
+ return false;
+ const bool was_open = promo_bubble_ && promo_bubble_->is_open();
+ if (promo_bubble_)
+ promo_bubble_->Close();
+ if (!continuing_after_bubble_closed_ &&
+ iph_feature_bypassing_tracker_ == &iph_feature) {
+ iph_feature_bypassing_tracker_ = nullptr;
+ }
+ return was_open;
+}
+
+bool FeaturePromoControllerCommon::DismissNonCriticalBubbleInRegion(
+ const gfx::Rect& screen_bounds) {
+ if (promo_bubble_ && promo_bubble_->is_open() &&
+ promo_bubble_->GetBoundsInScreen().Intersects(screen_bounds)) {
+ const bool result = CloseBubble(*current_iph_feature_);
+ DCHECK(result);
+ return result;
+ }
+ return false;
+}
+
+FeaturePromoController::PromoHandle
+FeaturePromoControllerCommon::CloseBubbleAndContinuePromo(
+ const base::Feature& iph_feature) {
+ DCHECK_EQ(current_iph_feature_, &iph_feature);
+ continuing_after_bubble_closed_ = true;
+ const bool result = CloseBubble(iph_feature);
+ DCHECK(result);
+ return PromoHandle(GetAsWeakPtr(), &iph_feature);
+}
+
+base::WeakPtr<FeaturePromoController>
+FeaturePromoControllerCommon::GetAsWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
+FeaturePromoControllerCommon::TestLock
+FeaturePromoControllerCommon::BlockPromosForTesting() {
+ if (current_iph_feature_)
+ CloseBubble(*current_iph_feature_);
+ return std::make_unique<base::AutoReset<bool>>(&promos_blocked_for_testing_,
+ true);
+}
+
+bool FeaturePromoControllerCommon::CheckScreenReaderPromptAvailable() const {
+ if (!ui::AXPlatformNode::GetAccessibilityMode().has_mode(
+ ui::AXMode::kScreenReader)) {
+ return false;
+ }
+
+ // If we're in demo mode and screen reader is on, always play the demo
+ // without querying the FE backend, since the backend will return false for
+ // all promos other than the one that's being demoed. If we didn't have this
+ // code the screen reader prompt would never play.
+ if (base::FeatureList::IsEnabled(feature_engagement::kIPHDemoMode) ||
+ iph_feature_bypassing_tracker_)
+ return true;
+
+ const base::Feature* const prompt_feature =
+ GetScreenReaderPromptPromoFeature();
+ if (!prompt_feature ||
+ !feature_engagement_tracker_->ShouldTriggerHelpUI(*prompt_feature))
+ return false;
+
+ // TODO(crbug.com/1258216): Once we have our answer, immediately dismiss
+ // so that this doesn't interfere with actually showing the bubble. This
+ // dismiss can be moved elsewhere once we support concurrency.
+ feature_engagement_tracker_->Dismissed(*prompt_feature);
+
+ return true;
+}
+
+std::unique_ptr<HelpBubble> FeaturePromoControllerCommon::ShowPromoBubbleImpl(
+ const FeaturePromoSpecification& spec,
+ ui::TrackedElement* anchor_element,
+ FeaturePromoSpecification::StringReplacements body_text_replacements,
+ bool screen_reader_prompt_available,
+ bool is_critical_promo) {
+ HelpBubbleParams create_params;
+ create_params.body_text = l10n_util::GetStringFUTF16(
+ spec.bubble_body_string_id(), std::move(body_text_replacements), nullptr);
+ create_params.title_text = spec.bubble_title_text();
+ if (spec.screen_reader_string_id()) {
+ create_params.screenreader_text =
+ spec.screen_reader_accelerator()
+ ? l10n_util::GetStringFUTF16(
+ spec.screen_reader_string_id(),
+ spec.screen_reader_accelerator()
+ .GetAccelerator(GetAcceleratorProvider())
+ .GetShortcutText())
+ : l10n_util::GetStringUTF16(spec.screen_reader_string_id());
+ }
+ create_params.body_icon = spec.bubble_icon();
+ if (spec.bubble_body_string_id())
+ create_params.body_icon_alt_text = GetBodyIconAltText();
+ create_params.arrow = spec.bubble_arrow();
+
+ // Critical promos don't time out.
+ if (is_critical_promo)
+ create_params.timeout = base::Seconds(0);
+
+ // Feature isn't present for some critical promos.
+ if (spec.feature()) {
+ create_params.dismiss_callback = base::BindOnce(
+ &FeaturePromoControllerCommon::OnHelpBubbleDismissed,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(spec.feature()));
+ }
+
+ if (spec.promo_type() == FeaturePromoSpecification::PromoType::kSnooze) {
+ CHECK(spec.feature());
+ create_params.buttons = CreateSnoozeButtons(*spec.feature());
+ } else if (spec.promo_type() ==
+ FeaturePromoSpecification::PromoType::kTutorial) {
+ CHECK(spec.feature());
+ create_params.buttons =
+ CreateTutorialButtons(*spec.feature(), spec.tutorial_id());
+ create_params.force_close_button = true;
+ create_params.close_button_alt_text =
+ l10n_util::GetStringUTF16(IDS_CLOSE_PROMO);
+
+ create_params.dismiss_callback = base::BindOnce(
+ &FeaturePromoControllerCommon::OnTutorialHelpBubbleDismissed,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(spec.feature()),
+ spec.tutorial_id());
+ }
+
+ bool had_screen_reader_promo = false;
+ if (spec.promo_type() == FeaturePromoSpecification::PromoType::kTutorial) {
+ create_params.keyboard_navigation_hint = GetTutorialScreenReaderHint();
+ } else if (CheckScreenReaderPromptAvailable()) {
+ create_params.keyboard_navigation_hint = GetFocusHelpBubbleScreenReaderHint(
+ spec.promo_type(), anchor_element, is_critical_promo);
+ had_screen_reader_promo = !create_params.keyboard_navigation_hint.empty();
+ }
+
+ auto help_bubble = bubble_factory_registry_->CreateHelpBubble(
+ anchor_element, std::move(create_params));
+ if (help_bubble) {
+ // Record that the focus help message was actually read to the user. See the
+ // note in MaybeShowPromoImpl().
+ // TODO(crbug.com/1258216): Rewrite this when we have the ability for FE
+ // promos to ignore other active promos.
+ if (had_screen_reader_promo) {
+ feature_engagement_tracker_->NotifyEvent(
+ GetScreenReaderPromptPromoEventName());
+ }
+
+ // Listen for the bubble being closed.
+ bubble_closed_subscription_ = help_bubble->AddOnCloseCallback(
+ base::BindOnce(&FeaturePromoControllerCommon::OnHelpBubbleClosed,
+ base::Unretained(this)));
+ }
+
+ return help_bubble;
+}
+
+void FeaturePromoControllerCommon::FinishContinuedPromo(
+ const base::Feature* iph_feature) {
+ DCHECK(continuing_after_bubble_closed_);
+ if (iph_feature_bypassing_tracker_ != iph_feature)
+ feature_engagement_tracker_->Dismissed(*iph_feature);
+ else
+ iph_feature_bypassing_tracker_ = nullptr;
+ if (current_iph_feature_ == iph_feature) {
+ current_iph_feature_ = nullptr;
+ continuing_after_bubble_closed_ = false;
+ }
+}
+
+void FeaturePromoControllerCommon::OnHelpBubbleClosed(HelpBubble* bubble) {
+ // Since we're in the middle of processing callbacks we can't reset our
+ // subscription but since it's a weak pointer (internally) and since we should
+ // should only get called here once, it's not a big deal if we don't reset
+ // it.
+ if (bubble == critical_promo_bubble_) {
+ critical_promo_bubble_ = nullptr;
+ } else if (bubble == promo_bubble_.get()) {
+ if (!continuing_after_bubble_closed_) {
+ if (iph_feature_bypassing_tracker_.get() != current_iph_feature_)
+ feature_engagement_tracker_->Dismissed(*current_iph_feature_);
+ else
+ iph_feature_bypassing_tracker_ = nullptr;
+ current_iph_feature_ = nullptr;
+ }
+ promo_bubble_.reset();
+ } else {
+ NOTREACHED();
+ }
+
+ if (bubble_closed_callback_)
+ std::move(bubble_closed_callback_).Run();
+}
+
+void FeaturePromoControllerCommon::OnHelpBubbleSnoozed(
+ const base::Feature* feature) {
+ if (iph_feature_bypassing_tracker_ != feature)
+ snooze_service_->OnUserSnooze(*feature);
+}
+
+void FeaturePromoControllerCommon::OnHelpBubbleDismissed(
+ const base::Feature* feature) {
+ if (snooze_service_ && iph_feature_bypassing_tracker_ != feature)
+ snooze_service_->OnUserDismiss(*feature);
+}
+
+void FeaturePromoControllerCommon::OnTutorialHelpBubbleSnoozed(
+ const base::Feature* iph_feature,
+ TutorialIdentifier tutorial_id) {
+ OnHelpBubbleSnoozed(iph_feature);
+ tutorial_service_->LogIPHLinkClicked(tutorial_id, false);
+}
+
+void FeaturePromoControllerCommon::OnTutorialHelpBubbleDismissed(
+ const base::Feature* iph_feature,
+ TutorialIdentifier tutorial_id) {
+ OnHelpBubbleDismissed(iph_feature);
+ tutorial_service_->LogIPHLinkClicked(tutorial_id, false);
+}
+
+void FeaturePromoControllerCommon::OnTutorialStarted(
+ const base::Feature* iph_feature,
+ TutorialIdentifier tutorial_id) {
+ if (!promo_bubble_ || !promo_bubble_->is_open()) {
+ NOTREACHED();
+ } else {
+ DCHECK_EQ(current_iph_feature_, iph_feature);
+ tutorial_promo_handle_ = CloseBubbleAndContinuePromo(*iph_feature);
+ DCHECK(tutorial_promo_handle_.is_valid());
+ if (tutorial_service_->StartTutorial(
+ tutorial_id, GetAnchorContext(),
+ base::BindOnce(&FeaturePromoControllerCommon::OnTutorialComplete,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(iph_feature)),
+ base::BindOnce(&FeaturePromoControllerCommon::OnTutorialAborted,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(iph_feature))))
+ tutorial_service_->LogIPHLinkClicked(tutorial_id, true);
+ }
+}
+
+void FeaturePromoControllerCommon::OnTutorialComplete(
+ const base::Feature* iph_feature) {
+ tutorial_promo_handle_.Release();
+ if (snooze_service_)
+ snooze_service_->OnUserDismiss(*iph_feature);
+}
+
+void FeaturePromoControllerCommon::OnTutorialAborted(
+ const base::Feature* iph_feature) {
+ tutorial_promo_handle_.Release();
+ if (snooze_service_)
+ snooze_service_->OnUserSnooze(*iph_feature);
+}
+
+std::vector<HelpBubbleButtonParams>
+FeaturePromoControllerCommon::CreateSnoozeButtons(
+ const base::Feature& feature) {
+ std::vector<HelpBubbleButtonParams> buttons;
+
+ HelpBubbleButtonParams snooze_button;
+ snooze_button.text = l10n_util::GetStringUTF16(IDS_PROMO_SNOOZE_BUTTON);
+ snooze_button.is_default = false;
+ snooze_button.callback = base::BindOnce(
+ &FeaturePromoControllerCommon::OnHelpBubbleSnoozed,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(&feature));
+ buttons.push_back(std::move(snooze_button));
+
+ HelpBubbleButtonParams dismiss_button;
+ dismiss_button.text = l10n_util::GetStringUTF16(IDS_PROMO_DISMISS_BUTTON);
+ dismiss_button.is_default = true;
+ dismiss_button.callback = base::BindOnce(
+ &FeaturePromoControllerCommon::OnHelpBubbleDismissed,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(&feature));
+ buttons.push_back(std::move(dismiss_button));
+
+ return buttons;
+}
+
+std::vector<HelpBubbleButtonParams>
+FeaturePromoControllerCommon::CreateTutorialButtons(
+ const base::Feature& feature,
+ TutorialIdentifier tutorial_id) {
+ std::vector<HelpBubbleButtonParams> buttons;
+
+ HelpBubbleButtonParams snooze_button;
+ snooze_button.text = l10n_util::GetStringUTF16(IDS_PROMO_SNOOZE_BUTTON);
+ snooze_button.is_default = false;
+ snooze_button.callback = base::BindRepeating(
+ &FeaturePromoControllerCommon::OnTutorialHelpBubbleSnoozed,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(&feature), tutorial_id);
+ buttons.push_back(std::move(snooze_button));
+
+ HelpBubbleButtonParams tutorial_button;
+ tutorial_button.text =
+ l10n_util::GetStringUTF16(IDS_PROMO_SHOW_TUTORIAL_BUTTON);
+ tutorial_button.is_default = true;
+ tutorial_button.callback = base::BindRepeating(
+ &FeaturePromoControllerCommon::OnTutorialStarted,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(&feature), tutorial_id);
+ buttons.push_back(std::move(tutorial_button));
+
+ return buttons;
+}
+
+// static
+bool FeaturePromoControllerCommon::active_window_check_blocked_ = false;
+
+// static
+FeaturePromoControllerCommon::TestLock
+FeaturePromoControllerCommon::BlockActiveWindowCheckForTesting() {
+ return std::make_unique<base::AutoReset<bool>>(&active_window_check_blocked_,
+ true);
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/feature_promo_controller.h b/chromium/components/user_education/common/feature_promo_controller.h
new file mode 100644
index 00000000000..a189b998690
--- /dev/null
+++ b/chromium/components/user_education/common/feature_promo_controller.h
@@ -0,0 +1,395 @@
+// 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 COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_CONTROLLER_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_CONTROLLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/feature_engagement/public/tracker.h"
+#include "components/user_education/common/feature_promo_registry.h"
+#include "components/user_education/common/feature_promo_specification.h"
+#include "components/user_education/common/help_bubble.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/common/tutorial_identifier.h"
+
+namespace base {
+struct Feature;
+}
+
+namespace ui {
+class AcceleratorProvider;
+class TrackedElement;
+} // namespace ui
+
+// Declaring these in the global namespace for testing purposes.
+class BrowserFeaturePromoControllerTest;
+class FeaturePromoSnoozeInteractiveTest;
+
+namespace user_education {
+
+class FeaturePromoSnoozeService;
+class HelpBubbleFactoryRegistry;
+class TutorialService;
+
+// Mostly virtual base class for feature promos; used to mock the interface in
+// tests.
+class FeaturePromoController {
+ public:
+ using BubbleCloseCallback = base::OnceClosure;
+
+ // Represents a promo that has been continued after its bubble has been
+ // hidden, as a result of calling CloseBubbleAndContinuePromo().
+ //
+ // The promo is considered still active until the handle is released or
+ // destroyed and no other promos will be allowed to show.
+ //
+ // PromoHandle is a value-typed, movable smart reference; default constructed
+ // instances are falsy (i.e. operator bool and is_valid() return false), as
+ // are any instances that have been moved or released.
+ class PromoHandle {
+ public:
+ PromoHandle();
+ PromoHandle(base::WeakPtr<FeaturePromoController> controller,
+ const base::Feature* feature);
+ PromoHandle(PromoHandle&&);
+ ~PromoHandle();
+
+ PromoHandle& operator=(PromoHandle&&);
+
+ explicit operator bool() const { return is_valid(); }
+ bool operator!() const { return !is_valid(); }
+
+ // Returns whether the handle refers to a valid promo. Returns null for
+ // default-constructed objects and after being moved or released.
+ bool is_valid() const { return feature_; }
+
+ // Releases the promo and resets the handle. After release, operator bool
+ // will return false regardless of the previous state.
+ void Release();
+
+ private:
+ base::WeakPtr<FeaturePromoController> controller_;
+ raw_ptr<const base::Feature> feature_ = nullptr;
+ };
+
+ FeaturePromoController();
+ FeaturePromoController(const FeaturePromoController& other) = delete;
+ virtual ~FeaturePromoController();
+ void operator=(const FeaturePromoController& other) = delete;
+
+ // Starts the promo if possible. Returns whether it started.
+ // |iph_feature| must be an IPH feature defined in
+ // components/feature_engagement/public/feature_list.cc and registered
+ // with |FeaturePromoRegistry|. Note that this is different than the
+ // feature that the IPH is showing for.
+ //
+ // If the body text is parameterized, pass text replacements in
+ // |body_text_replacements|.
+ //
+ // If a bubble was shown and |close_callback| was provided, it will be
+ // called when the bubble closes. |close_callback| must be valid as
+ // long as the bubble shows.
+ //
+ // For users that can't register their parameters with
+ // FeaturePromoRegistry, see
+ // |FeaturePromoControllerViews::MaybeShowPromoWithParams()|. Prefer
+ // statically registering params with FeaturePromoRegistry and using
+ // this method when possible.
+ virtual bool MaybeShowPromo(
+ const base::Feature& iph_feature,
+ FeaturePromoSpecification::StringReplacements body_text_replacements = {},
+ BubbleCloseCallback close_callback = BubbleCloseCallback()) = 0;
+
+ // Returns whether a bubble is showing for the given promo. If
+ // `include_continued_promos` is set, also returns true if a promo bubble has
+ // been hidden with CloseBubbleAndContinuePromo() but the promo is still
+ // active in the background.
+ virtual bool IsPromoActive(const base::Feature& iph_feature,
+ bool include_continued_promos) const = 0;
+
+ // Starts a promo with the settings for skipping any logging or filtering
+ // provided by the implementation for MaybeShowPromo.
+ virtual bool MaybeShowPromoForDemoPage(
+ const base::Feature* iph_feature,
+ FeaturePromoSpecification::StringReplacements body_text_replacements = {},
+ BubbleCloseCallback close_callback = BubbleCloseCallback()) = 0;
+
+ // If a bubble is showing for |iph_feature| close it and end the
+ // promo. Does nothing otherwise. Returns true if a bubble was closed
+ // and false otherwise.
+ //
+ // Calling this has no effect if |CloseBubbleAndContinuePromo()| was
+ // called for |iph_feature|.
+ virtual bool CloseBubble(const base::Feature& iph_feature) = 0;
+
+ // Like CloseBubble() but does not end the promo yet. The caller takes
+ // ownership of the promo (e.g. to show a highlight in a menu or on a
+ // button). The returned PromoHandle represents this ownership.
+ virtual PromoHandle CloseBubbleAndContinuePromo(
+ const base::Feature& iph_feature) = 0;
+
+ // Returns a weak pointer to this object.
+ virtual base::WeakPtr<FeaturePromoController> GetAsWeakPtr() = 0;
+
+ protected:
+ // Called when PromoHandle is destroyed to finish the promo.
+ virtual void FinishContinuedPromo(const base::Feature* iph_feature) = 0;
+};
+
+// Manages display of in-product help promos. All IPH displays in Top
+// Chrome should go through here.
+class FeaturePromoControllerCommon : public FeaturePromoController {
+ public:
+ using TestLock = std::unique_ptr<base::AutoReset<bool>>;
+
+ FeaturePromoControllerCommon(
+ feature_engagement::Tracker* feature_engagement_tracker,
+ FeaturePromoRegistry* registry,
+ HelpBubbleFactoryRegistry* help_bubble_registry,
+ FeaturePromoSnoozeService* snooze_service,
+ TutorialService* tutorial_service);
+ ~FeaturePromoControllerCommon() override;
+
+ // Only for security or privacy critical promos. Immedialy shows a
+ // promo with |params|, cancelling any normal promo and blocking any
+ // further promos until it's done.
+ //
+ // Returns an ID that can be passed to CloseBubbleForCriticalPromo()
+ // if successful. This can fail if another critical promo is showing.
+ std::unique_ptr<HelpBubble> ShowCriticalPromo(
+ const FeaturePromoSpecification& spec,
+ ui::TrackedElement* anchor_element,
+ FeaturePromoSpecification::StringReplacements body_text_replacements =
+ {});
+
+ // For systems where there are rendering issues of e.g. displaying the
+ // omnibox and a bubble in the same region on the screen, dismisses a non-
+ // critical promo bubble which overlaps a given screen region. Returns true
+ // if a bubble is closed as a result.
+ bool DismissNonCriticalBubbleInRegion(const gfx::Rect& screen_bounds);
+
+ // Blocks further promos and closes any existing non-critical ones.
+ [[nodiscard]] TestLock BlockPromosForTesting();
+
+ // Returns the associated feature engagement tracker.
+ feature_engagement::Tracker* feature_engagement_tracker() {
+ return feature_engagement_tracker_;
+ }
+
+ // FeaturePromoController:
+ bool MaybeShowPromo(
+ const base::Feature& iph_feature,
+ FeaturePromoSpecification::StringReplacements body_text_replacements = {},
+ BubbleCloseCallback close_callback = BubbleCloseCallback()) override;
+ bool IsPromoActive(const base::Feature& iph_feature,
+ bool include_continued_promos = false) const override;
+ bool MaybeShowPromoForDemoPage(
+ const base::Feature* iph_feature,
+ FeaturePromoSpecification::StringReplacements body_text_replacements = {},
+ BubbleCloseCallback close_callback = BubbleCloseCallback()) override;
+ bool CloseBubble(const base::Feature& iph_feature) override;
+ PromoHandle CloseBubbleAndContinuePromo(
+ const base::Feature& iph_feature) override;
+ base::WeakPtr<FeaturePromoController> GetAsWeakPtr() override;
+
+ HelpBubbleFactoryRegistry* bubble_factory_registry() {
+ return bubble_factory_registry_;
+ }
+
+ HelpBubble* promo_bubble_for_testing() { return promo_bubble(); }
+ HelpBubble* critical_promo_bubble_for_testing() {
+ return critical_promo_bubble();
+ }
+
+ TutorialService* tutorial_service_for_testing() { return tutorial_service_; }
+
+ // Blocks a check whether the IPH would be created in an inactive window or
+ // app before showing the IPH. Intended for browser and unit tests.
+ // The actual implementation of the check is in the platform-specific
+ // implementation of CanShowPromo().
+ [[nodiscard]] static TestLock BlockActiveWindowCheckForTesting();
+
+ protected:
+ friend BrowserFeaturePromoControllerTest;
+ friend FeaturePromoSnoozeInteractiveTest;
+
+ // For IPH not registered with |FeaturePromoRegistry|. Only use this
+ // if it is infeasible to pre-register your IPH.
+ bool MaybeShowPromoFromSpecification(
+ const FeaturePromoSpecification& spec,
+ ui::TrackedElement* anchor_element,
+ FeaturePromoSpecification::StringReplacements body_text_replacements,
+ BubbleCloseCallback close_callback);
+
+ FeaturePromoSnoozeService* snooze_service() { return snooze_service_; }
+ HelpBubble* promo_bubble() { return promo_bubble_.get(); }
+ const HelpBubble* promo_bubble() const { return promo_bubble_.get(); }
+ HelpBubble* critical_promo_bubble() { return critical_promo_bubble_; }
+ const HelpBubble* critical_promo_bubble() const {
+ return critical_promo_bubble_;
+ }
+
+ // Gets the context in which to locate the anchor view.
+ virtual ui::ElementContext GetAnchorContext() const = 0;
+
+ // Determine if the current context and anchor element allow showing a promo.
+ // This lets us rule out e.g. inactive and incognito windows/apps for
+ // non-critical promos.
+ //
+ // Note: Implementations should make sure to check
+ // active_window_check_blocked().
+ virtual bool CanShowPromo(ui::TrackedElement* anchor_element) const = 0;
+
+ // Get the accelerator provider to use to look up accelerators.
+ virtual const ui::AcceleratorProvider* GetAcceleratorProvider() const = 0;
+
+ // Gets the alt text to use for body icons.
+ virtual std::u16string GetBodyIconAltText() const = 0;
+
+ // Gets the feature associated with prompting the user how to navigate to help
+ // bubbles via the keyboard. It is its own promo, and will stop playing in
+ // most cases when the user has made use of it enough times.
+ //
+ // If null is returned, no attempt will be made to play a prompt.
+ virtual const base::Feature* GetScreenReaderPromptPromoFeature() const = 0;
+
+ // This is the associated event with the promo feature above. The event is
+ // recorded only if and when the promo is actually played to the user.
+ virtual const char* GetScreenReaderPromptPromoEventName() const = 0;
+
+ // Returns the special prompt to play with the initial bubble of a tutorial;
+ // instead of the general navigation help prompt returned by
+ // GetFocusHelpBubbleScreenReaderHint().
+ virtual std::u16string GetTutorialScreenReaderHint() const = 0;
+
+ // This method returns an appropriate prompt for promoting using a navigation
+ // accelerator to focus the help bubble.
+ virtual std::u16string GetFocusHelpBubbleScreenReaderHint(
+ FeaturePromoSpecification::PromoType promo_type,
+ ui::TrackedElement* anchor_element,
+ bool is_critical_promo) const = 0;
+
+ FeaturePromoRegistry* registry() { return registry_; }
+
+ static bool active_window_check_blocked() {
+ return active_window_check_blocked_;
+ }
+
+ private:
+ // FeaturePromoController:
+ void FinishContinuedPromo(const base::Feature* iph_feature) override;
+
+ // Returns whether we can play a screen reader prompt for the "focus help
+ // bubble" promo.
+ // TODO(crbug.com/1258216): This must be called *before* we ask if the bubble
+ // will show because a limitation in the current FE backend causes
+ // ShouldTriggerHelpUI() to always return false if another promo is being
+ // displayed. Once we have machinery to allow concurrency in the FE system
+ // all of this logic can be rewritten.
+ bool CheckScreenReaderPromptAvailable() const;
+
+ // Method that creates the bubble for a feature promo. May return null if the
+ // bubble cannot be shown.
+ std::unique_ptr<HelpBubble> ShowPromoBubbleImpl(
+ const FeaturePromoSpecification& spec,
+ ui::TrackedElement* anchor_element,
+ FeaturePromoSpecification::StringReplacements body_text_replacements,
+ bool screen_reader_prompt_available,
+ bool is_critical_promo);
+
+ // Callback that cleans up a help bubble when it is closed.
+ void OnHelpBubbleClosed(HelpBubble* bubble);
+
+ // Callback for snoozed features.
+ void OnHelpBubbleSnoozed(const base::Feature* feature);
+
+ // Callback for snoozed tutorial features. .
+ void OnTutorialHelpBubbleSnoozed(const base::Feature* iph_feature,
+ TutorialIdentifier tutorial_id);
+
+ // Callback when a feature's help bubble is dismissed by any means other than
+ // snoozing (including "OK" or "Got it!" buttons).
+ void OnHelpBubbleDismissed(const base::Feature* feature);
+
+ // Callback when the dismiss button for IPH for tutorials is clicked.
+ void OnTutorialHelpBubbleDismissed(const base::Feature* iph_feature,
+ TutorialIdentifier tutorial_id);
+
+ // Callback when a tutorial triggered from a promo is actually started.
+ void OnTutorialStarted(const base::Feature* iph_feature,
+ TutorialIdentifier tutorial_id);
+
+ // Called when a tutorial launched via StartTutorial() completes.
+ void OnTutorialComplete(const base::Feature* iph_feature);
+
+ // Called when a tutorial launched via StartTutorial() aborts.
+ void OnTutorialAborted(const base::Feature* iph_feature);
+
+ // Create appropriate buttons for a snoozable promo on the current platform.
+ std::vector<HelpBubbleButtonParams> CreateSnoozeButtons(
+ const base::Feature& feature);
+
+ // Create appropriate buttons for a tutorial promo on the current platform.
+ std::vector<HelpBubbleButtonParams> CreateTutorialButtons(
+ const base::Feature& feature,
+ TutorialIdentifier tutorial_id);
+
+ // The feature promo registry to use.
+ const raw_ptr<FeaturePromoRegistry> registry_;
+
+ // Non-null as long as a promo is showing. Corresponds to an IPH
+ // feature registered with |feature_engagement_tracker_|.
+ raw_ptr<const base::Feature> current_iph_feature_ = nullptr;
+ bool continuing_after_bubble_closed_ = false;
+
+ // The help bubble, if a feature promo bubble is showing.
+ std::unique_ptr<HelpBubble> promo_bubble_;
+
+ // Has a value if a critical promo is showing. If this has a value,
+ // |current_iph_feature_| will usually be null. There is one edge case
+ // where this may not be true: when a critical promo is requested
+ // between a normal promo's CloseBubbleAndContinuePromo() call and its
+ // end.
+ raw_ptr<HelpBubble> critical_promo_bubble_ = nullptr;
+
+ // Promo that is being continued during a tutorial launched from the promo
+ // bubble.
+ PromoHandle tutorial_promo_handle_;
+
+ base::OnceClosure bubble_closed_callback_;
+ base::CallbackListSubscription bubble_closed_subscription_;
+
+ const raw_ptr<feature_engagement::Tracker> feature_engagement_tracker_;
+ const raw_ptr<HelpBubbleFactoryRegistry> bubble_factory_registry_;
+ const raw_ptr<FeaturePromoSnoozeService> snooze_service_;
+ const raw_ptr<TutorialService> tutorial_service_;
+
+ // When set to true, promos will never be shown.
+ bool promos_blocked_for_testing_ = false;
+
+ // In the case where the user education demo page wants to bypass the feature
+ // engagement tracker, the current iph feature will be set and then checked
+ // against to verify the right feature is bypassing. this page is located at
+ // internals/user-education.
+ raw_ptr<const base::Feature> iph_feature_bypassing_tracker_ = nullptr;
+
+ base::WeakPtrFactory<FeaturePromoControllerCommon> weak_ptr_factory_{this};
+
+ // Whether IPH should be allowed to show in an inactive window or app.
+ // Should be checked in implementations of CanShowPromo(). Typically only
+ // modified in tests.
+ static bool active_window_check_blocked_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_CONTROLLER_H_
diff --git a/chromium/components/user_education/common/feature_promo_registry.cc b/chromium/components/user_education/common/feature_promo_registry.cc
new file mode 100644
index 00000000000..b578d35676d
--- /dev/null
+++ b/chromium/components/user_education/common/feature_promo_registry.cc
@@ -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.
+
+#include "components/user_education/common/feature_promo_registry.h"
+
+#include "base/containers/contains.h"
+#include "base/feature_list.h"
+#include "components/user_education/common/feature_promo_specification.h"
+
+namespace user_education {
+
+FeaturePromoRegistry::FeaturePromoRegistry() = default;
+FeaturePromoRegistry::~FeaturePromoRegistry() = default;
+
+bool FeaturePromoRegistry::IsFeatureRegistered(
+ const base::Feature& iph_feature) const {
+ return base::Contains(feature_promo_data_, &iph_feature);
+}
+
+const FeaturePromoSpecification* FeaturePromoRegistry::GetParamsForFeature(
+ const base::Feature& iph_feature) const {
+ auto data_it = feature_promo_data_.find(&iph_feature);
+ DCHECK(data_it != feature_promo_data_.end());
+ return &data_it->second;
+}
+
+void FeaturePromoRegistry::RegisterFeature(FeaturePromoSpecification spec) {
+ const base::Feature* const iph_feature = spec.feature();
+ CHECK(iph_feature);
+ const auto result = feature_promo_data_.emplace(iph_feature, std::move(spec));
+ DCHECK(result.second) << "Duplicate IPH feature registered: "
+ << iph_feature->name;
+}
+
+void FeaturePromoRegistry::ClearFeaturesForTesting() {
+ feature_promo_data_.clear();
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/feature_promo_registry.h b/chromium/components/user_education/common/feature_promo_registry.h
new file mode 100644
index 00000000000..3e784d3b2ef
--- /dev/null
+++ b/chromium/components/user_education/common/feature_promo_registry.h
@@ -0,0 +1,59 @@
+// 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 COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_REGISTRY_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_REGISTRY_H_
+
+#include <map>
+
+#include "components/user_education/common/feature_promo_specification.h"
+
+namespace base {
+struct Feature;
+}
+
+namespace user_education {
+
+// Stores parameters for in-product help promos. For each registered
+// IPH, has the bubble parameters and a method for getting an anchor
+// view for a given BrowserView. Promos should be registered here when
+// feasible.
+class FeaturePromoRegistry {
+ public:
+ FeaturePromoRegistry();
+ ~FeaturePromoRegistry();
+
+ // Determines whether or not a particular feature is registered.
+ bool IsFeatureRegistered(const base::Feature& iph_feature) const;
+
+ // Returns the FeaturePromoSpecification to start an IPH for
+ // the given feature. |iph_feature| is the feature to show for.
+ // |browser_view| is the window it should show in.
+ //
+ // The params must be used immediately since it contains a View
+ // pointer that may become stale. This may return nothing in which
+ // case the promo shouldn't show.
+ const FeaturePromoSpecification* GetParamsForFeature(
+ const base::Feature& iph_feature) const;
+
+ // Registers a feature promo.
+ //
+ // Prefer putting these calls in the body of RegisterKnownFeatures()
+ // when possible.
+ void RegisterFeature(FeaturePromoSpecification spec);
+
+ const std::map<const base::Feature*, FeaturePromoSpecification>&
+ GetRegisteredFeaturePromoSpecifications() {
+ return feature_promo_data_;
+ }
+
+ void ClearFeaturesForTesting();
+
+ private:
+ std::map<const base::Feature*, FeaturePromoSpecification> feature_promo_data_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_REGISTRY_H_
diff --git a/chromium/components/user_education/common/feature_promo_snooze_service.cc b/chromium/components/user_education/common/feature_promo_snooze_service.cc
new file mode 100644
index 00000000000..de4632e9d78
--- /dev/null
+++ b/chromium/components/user_education/common/feature_promo_snooze_service.cc
@@ -0,0 +1,143 @@
+// 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/user_education/common/feature_promo_snooze_service.h"
+
+#include <ostream>
+
+#include "base/feature_list.h"
+#include "base/json/values_util.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/feature_engagement/public/feature_constants.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace user_education {
+
+namespace {
+
+constexpr base::FeatureParam<FeaturePromoSnoozeService::NonClickerPolicy>::
+ Option kNonClickerPolicyOptions[] = {
+ {FeaturePromoSnoozeService::NonClickerPolicy::kDismiss, "dismiss"},
+ {FeaturePromoSnoozeService::NonClickerPolicy::kLongSnooze,
+ "long_snooze"}};
+
+// Used in UMA histogram to track if the user snoozes for once or more.
+enum class SnoozeType {
+ // The user snoozes the IPH for the first time.
+ kFirstTimeSnooze = 0,
+ // The user snoozes the IPH for the second time or more.
+ kRepeatingSnooze = 1,
+ kMaxValue = kRepeatingSnooze
+};
+} // namespace
+
+constexpr int FeaturePromoSnoozeService::kUmaMaxSnoozeCount;
+constexpr base::TimeDelta FeaturePromoSnoozeService::kDefaultSnoozeDuration;
+
+FeaturePromoSnoozeService::FeaturePromoSnoozeService() = default;
+FeaturePromoSnoozeService::~FeaturePromoSnoozeService() = default;
+
+void FeaturePromoSnoozeService::OnUserSnooze(const base::Feature& iph_feature,
+ base::TimeDelta snooze_duration) {
+ DCHECK(snooze_duration > base::Seconds(0));
+ auto snooze_data = ReadSnoozeData(iph_feature);
+
+ if (!snooze_data)
+ snooze_data = SnoozeData();
+
+ base::UmaHistogramEnumeration(
+ "InProductHelp.Promos.Snooze." + std::string(iph_feature.name),
+ snooze_data->snooze_count == 0 ? SnoozeType::kFirstTimeSnooze
+ : SnoozeType::kRepeatingSnooze);
+
+ snooze_data->last_snooze_time = base::Time::Now();
+ snooze_data->last_snooze_duration = snooze_duration;
+ snooze_data->snooze_count++;
+
+ SaveSnoozeData(iph_feature, *snooze_data);
+}
+
+void FeaturePromoSnoozeService::OnUserDismiss(
+ const base::Feature& iph_feature) {
+ auto snooze_data = ReadSnoozeData(iph_feature);
+
+ if (!snooze_data)
+ snooze_data = SnoozeData();
+
+ snooze_data->is_dismissed = true;
+
+ SaveSnoozeData(iph_feature, *snooze_data);
+
+ // Record count of previous snoozes when the IPH gets dismissed by "Got It"
+ // button.
+ base::UmaHistogramExactLinear(
+ "InProductHelp.Promos.SnoozeCountAtAcknowledge." +
+ std::string(iph_feature.name),
+ snooze_data->snooze_count, kUmaMaxSnoozeCount);
+}
+
+void FeaturePromoSnoozeService::OnPromoShown(const base::Feature& iph_feature) {
+ auto snooze_data = ReadSnoozeData(iph_feature);
+
+ if (!snooze_data)
+ snooze_data = SnoozeData();
+
+ snooze_data->last_show_time = base::Time::Now();
+ snooze_data->show_count++;
+
+ SaveSnoozeData(iph_feature, *snooze_data);
+}
+
+bool FeaturePromoSnoozeService::IsBlocked(const base::Feature& iph_feature) {
+ auto snooze_data = ReadSnoozeData(iph_feature);
+
+ if (!snooze_data)
+ return false;
+
+ // This IPH has been dismissed by user permanently.
+ if (snooze_data->is_dismissed)
+ return true;
+
+ // This IPH is shown for the first time.
+ if (snooze_data->show_count == 0)
+ return false;
+
+ if (snooze_data->snooze_count > 0 &&
+ snooze_data->last_snooze_time >= snooze_data->last_show_time) {
+ // The IPH was snoozed on last display.
+
+ // Corruption: Snooze time is in the future.
+ if (snooze_data->last_snooze_time > base::Time::Now())
+ return true;
+
+ // This IPH is snoozed. Test if snooze period has expired.
+ return base::Time::Now() <
+ snooze_data->last_snooze_time + snooze_data->last_snooze_duration;
+ } else {
+ // The IPH was neither snoozed or dismissed on last display.
+ const base::FeatureParam<FeaturePromoSnoozeService::NonClickerPolicy>
+ kNonClickerPolicy{
+ &iph_feature, "x_iph_snooze_non_clicker_policy",
+ FeaturePromoSnoozeService::NonClickerPolicy::kLongSnooze,
+ &kNonClickerPolicyOptions};
+
+ NonClickerPolicy non_clicker_policy = kNonClickerPolicy.Get();
+
+ if (non_clicker_policy == NonClickerPolicy::kDismiss)
+ return true;
+
+ return base::Time::Now() < snooze_data->last_show_time + base::Days(14);
+ }
+}
+
+int FeaturePromoSnoozeService::GetSnoozeCount(
+ const base::Feature& iph_feature) {
+ absl::optional<SnoozeData> snooze_data = ReadSnoozeData(iph_feature);
+ return snooze_data ? snooze_data->snooze_count : 0;
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/feature_promo_snooze_service.h b/chromium/components/user_education/common/feature_promo_snooze_service.h
new file mode 100644
index 00000000000..20aa8bd3be0
--- /dev/null
+++ b/chromium/components/user_education/common/feature_promo_snooze_service.h
@@ -0,0 +1,100 @@
+// 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 COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_SNOOZE_SERVICE_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_SNOOZE_SERVICE_H_
+
+#include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace base {
+struct Feature;
+} // namespace base
+
+// Declare in the global namespace for test purposes.
+class FeaturePromoSnoozeInteractiveTest;
+
+namespace user_education {
+
+// This service manages snooze and dismiss of snoozable in-product help promo.
+// It is an abstract base class in order to support multiple frameworks/
+// platforms and potentially to migrate the backing to the new snooze feature
+// of the feature engagement tracker.
+//
+// Before showing an IPH, the IPH controller should ask if the IPH is blocked.
+// The controller should also notify after the IPH is shown and after the user
+// clicks the snooze/dismiss button.
+class FeaturePromoSnoozeService {
+ public:
+ // Policies to handler users who don't interact with the IPH.
+ enum class NonClickerPolicy {
+ // Permanently dismiss the IPH. Equivalent to clicking the dismiss button.
+ kDismiss = 0,
+ // Reshow the IPH later after at least 14 days.
+ kLongSnooze
+ };
+
+ // Maximum count of snoozes to track in UMA histogram.
+ // Snooze counts that are equal or larger than this value will be conflated.
+ static constexpr int kUmaMaxSnoozeCount = 10;
+
+ // The snooze duration defaults to 1 day plus 2 additional hours in hope to
+ // stagger busy hours in the days.
+ static constexpr base::TimeDelta kDefaultSnoozeDuration = base::Hours(26);
+
+ FeaturePromoSnoozeService();
+ virtual ~FeaturePromoSnoozeService();
+
+ // Disallow copy and assign.
+ FeaturePromoSnoozeService(const FeaturePromoSnoozeService&) = delete;
+ FeaturePromoSnoozeService& operator=(const FeaturePromoSnoozeService&) =
+ delete;
+
+ // The IPH controller must call this method when the user snoozes an IPH.
+ void OnUserSnooze(const base::Feature& iph_feature,
+ base::TimeDelta snooze_duration = kDefaultSnoozeDuration);
+
+ // The IPH controller must call this method when the user actively dismiss an
+ // IPH. Don't call this method in case of a passive dismiss, i.e. auto dismiss
+ // after a fixed amount of time.
+ void OnUserDismiss(const base::Feature& iph_feature);
+
+ // The IPH controller must call this method after an IPH is shown.
+ void OnPromoShown(const base::Feature& iph_feature);
+
+ // The IPH controller must call this method to check if an IPH is blocked by
+ // dismiss or snooze. An IPH will be approved if it is not snoozed or the
+ // snoozing period has timed out.
+ bool IsBlocked(const base::Feature& iph_feature);
+
+ // Reset the state of |iph_feature|.
+ virtual void Reset(const base::Feature& iph_feature) = 0;
+
+ // Read the count of previous snoozes for |iph_feature| from profile.
+ int GetSnoozeCount(const base::Feature& iph_feature);
+
+ protected:
+ // Snooze information dictionary saved under path
+ // in_product_help.snoozed_feature.[iph_name] in PerfService.
+ struct SnoozeData {
+ bool is_dismissed = false;
+ base::Time last_show_time = base::Time();
+ base::Time last_snooze_time = base::Time();
+ base::TimeDelta last_snooze_duration = base::TimeDelta();
+ int snooze_count = 0;
+ int show_count = 0;
+ };
+
+ virtual absl::optional<SnoozeData> ReadSnoozeData(
+ const base::Feature& iph_feature) = 0;
+ virtual void SaveSnoozeData(const base::Feature& iph_feature,
+ const SnoozeData& snooze_data) = 0;
+
+ private:
+ friend FeaturePromoSnoozeInteractiveTest;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_SNOOZE_SERVICE_H_
diff --git a/chromium/components/user_education/common/feature_promo_snooze_service_unittest.cc b/chromium/components/user_education/common/feature_promo_snooze_service_unittest.cc
new file mode 100644
index 00000000000..6e71d5aa323
--- /dev/null
+++ b/chromium/components/user_education/common/feature_promo_snooze_service_unittest.cc
@@ -0,0 +1,120 @@
+// 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/user_education/common/feature_promo_snooze_service.h"
+
+#include <memory>
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_param_associator.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "components/feature_engagement/public/feature_constants.h"
+#include "components/user_education/common/feature_promo_snooze_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace user_education {
+
+namespace {
+base::Feature kTestIPHFeature{"TestIPHFeature",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+base::Feature kTestIPHFeature2{"TestIPHFeature2",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+class TestFeaturePromoSnoozeService : public FeaturePromoSnoozeService {
+ public:
+ TestFeaturePromoSnoozeService() = default;
+ ~TestFeaturePromoSnoozeService() override = default;
+
+ void Reset(const base::Feature& iph_feature) override {
+ snooze_data_.erase(&iph_feature);
+ }
+
+ absl::optional<FeaturePromoSnoozeService::SnoozeData> ReadSnoozeData(
+ const base::Feature& iph_feature) override {
+ const auto it = snooze_data_.find(&iph_feature);
+ return it == snooze_data_.end() ? absl::nullopt
+ : absl::make_optional(it->second);
+ }
+
+ void SaveSnoozeData(const base::Feature& iph_feature,
+ const SnoozeData& snooze_data) override {
+ snooze_data_[&iph_feature] = snooze_data;
+ }
+
+ private:
+ std::map<const base::Feature*, SnoozeData> snooze_data_;
+};
+
+} // namespace
+
+class FeaturePromoSnoozeServiceTest : public testing::Test {
+ public:
+ FeaturePromoSnoozeServiceTest()
+ : task_environment_{
+ base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME} {}
+
+ protected:
+ base::test::TaskEnvironment task_environment_;
+ TestFeaturePromoSnoozeService service_;
+};
+
+TEST_F(FeaturePromoSnoozeServiceTest, AllowFirstTimeIPH) {
+ service_.Reset(kTestIPHFeature);
+ EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+}
+
+TEST_F(FeaturePromoSnoozeServiceTest, BlockDismissedIPH) {
+ service_.Reset(kTestIPHFeature);
+ service_.OnPromoShown(kTestIPHFeature);
+ service_.OnUserDismiss(kTestIPHFeature);
+ EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature));
+ service_.Reset(kTestIPHFeature);
+ EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+}
+
+TEST_F(FeaturePromoSnoozeServiceTest, BlockSnoozedIPH) {
+ service_.Reset(kTestIPHFeature);
+ service_.OnPromoShown(kTestIPHFeature);
+ service_.OnUserSnooze(kTestIPHFeature);
+ EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature));
+}
+
+TEST_F(FeaturePromoSnoozeServiceTest, ReleaseSnoozedIPH) {
+ service_.Reset(kTestIPHFeature);
+ service_.OnPromoShown(kTestIPHFeature);
+ service_.OnUserSnooze(kTestIPHFeature, base::Hours(1));
+ EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature));
+ task_environment_.FastForwardBy(base::Hours(2));
+ EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+}
+
+TEST_F(FeaturePromoSnoozeServiceTest, MultipleIPH) {
+ service_.Reset(kTestIPHFeature);
+ service_.Reset(kTestIPHFeature2);
+ service_.OnPromoShown(kTestIPHFeature);
+ service_.OnUserSnooze(kTestIPHFeature, base::Hours(1));
+ service_.OnPromoShown(kTestIPHFeature2);
+ service_.OnUserSnooze(kTestIPHFeature2, base::Hours(3));
+ EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature));
+ EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature2));
+ task_environment_.FastForwardBy(base::Hours(2));
+ EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+ EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature2));
+ task_environment_.FastForwardBy(base::Hours(2));
+ EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+ EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature2));
+}
+
+TEST_F(FeaturePromoSnoozeServiceTest, SnoozeNonClicker) {
+ base::test::ScopedFeatureList feature_list;
+ service_.Reset(kTestIPHFeature);
+ service_.OnPromoShown(kTestIPHFeature);
+ EXPECT_TRUE(service_.IsBlocked(kTestIPHFeature));
+ task_environment_.FastForwardBy(base::Days(15));
+ EXPECT_FALSE(service_.IsBlocked(kTestIPHFeature));
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/feature_promo_specification.cc b/chromium/components/user_education/common/feature_promo_specification.cc
new file mode 100644
index 00000000000..a6bb1864c71
--- /dev/null
+++ b/chromium/components/user_education/common/feature_promo_specification.cc
@@ -0,0 +1,182 @@
+// Copyright 2021 The Chromium 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/user_education/common/feature_promo_specification.h"
+
+#include <string>
+
+#include "base/feature_list.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace user_education {
+
+FeaturePromoSpecification::AcceleratorInfo::AcceleratorInfo() = default;
+FeaturePromoSpecification::AcceleratorInfo::AcceleratorInfo(
+ const AcceleratorInfo& other) = default;
+FeaturePromoSpecification::AcceleratorInfo::~AcceleratorInfo() = default;
+FeaturePromoSpecification::AcceleratorInfo::AcceleratorInfo(ValueType value)
+ : value_(value) {}
+FeaturePromoSpecification::AcceleratorInfo&
+FeaturePromoSpecification::AcceleratorInfo::operator=(
+ const AcceleratorInfo& other) = default;
+
+FeaturePromoSpecification::AcceleratorInfo&
+FeaturePromoSpecification::AcceleratorInfo::operator=(ValueType value) {
+ value_ = value;
+ return *this;
+}
+
+FeaturePromoSpecification::AcceleratorInfo::operator bool() const {
+ return absl::holds_alternative<ui::Accelerator>(value_) ||
+ absl::get<int>(value_);
+}
+
+ui::Accelerator FeaturePromoSpecification::AcceleratorInfo::GetAccelerator(
+ const ui::AcceleratorProvider* provider) const {
+ if (absl::holds_alternative<ui::Accelerator>(value_))
+ return absl::get<ui::Accelerator>(value_);
+
+ const int command_id = absl::get<int>(value_);
+ DCHECK_GT(command_id, 0);
+
+ ui::Accelerator result;
+ DCHECK(provider->GetAcceleratorForCommandId(command_id, &result));
+ return result;
+}
+
+FeaturePromoSpecification::DemoPageInfo::DemoPageInfo(
+ std::string display_title_,
+ std::string display_description_,
+ base::RepeatingClosure setup_for_feature_promo_callback_)
+ : display_title(display_title_),
+ display_description(display_description_),
+ setup_for_feature_promo_callback(setup_for_feature_promo_callback_) {}
+
+FeaturePromoSpecification::DemoPageInfo::DemoPageInfo(
+ const DemoPageInfo& other) = default;
+
+FeaturePromoSpecification::DemoPageInfo::~DemoPageInfo() = default;
+
+FeaturePromoSpecification::DemoPageInfo&
+FeaturePromoSpecification::DemoPageInfo::operator=(const DemoPageInfo& other) =
+ default;
+
+// static
+constexpr HelpBubbleArrow FeaturePromoSpecification::kDefaultBubbleArrow;
+
+FeaturePromoSpecification::FeaturePromoSpecification() = default;
+
+FeaturePromoSpecification::FeaturePromoSpecification(
+ FeaturePromoSpecification&& other) = default;
+
+FeaturePromoSpecification::FeaturePromoSpecification(
+ const base::Feature* feature,
+ PromoType promo_type,
+ ui::ElementIdentifier anchor_element_id,
+ int bubble_body_string_id)
+ : feature_(feature),
+ promo_type_(promo_type),
+ anchor_element_id_(anchor_element_id),
+ bubble_body_string_id_(bubble_body_string_id),
+ demo_page_info_(DemoPageInfo(feature ? feature->name : std::string())) {
+ DCHECK_NE(promo_type, PromoType::kUnspecifiied);
+ DCHECK(bubble_body_string_id_);
+}
+
+FeaturePromoSpecification::~FeaturePromoSpecification() = default;
+
+FeaturePromoSpecification& FeaturePromoSpecification::operator=(
+ FeaturePromoSpecification&& other) = default;
+
+// static
+FeaturePromoSpecification FeaturePromoSpecification::CreateForToastPromo(
+ const base::Feature& feature,
+ ui::ElementIdentifier anchor_element_id,
+ int body_text_string_id,
+ int accessible_text_string_id,
+ AcceleratorInfo accessible_accelerator) {
+ FeaturePromoSpecification spec(&feature, PromoType::kToast, anchor_element_id,
+ body_text_string_id);
+ spec.screen_reader_string_id_ = accessible_text_string_id;
+ spec.screen_reader_accelerator_ = std::move(accessible_accelerator);
+ return spec;
+}
+
+// static
+FeaturePromoSpecification FeaturePromoSpecification::CreateForSnoozePromo(
+ const base::Feature& feature,
+ ui::ElementIdentifier anchor_element_id,
+ int body_text_string_id) {
+ return FeaturePromoSpecification(&feature, PromoType::kSnooze,
+ anchor_element_id, body_text_string_id);
+}
+
+// static
+FeaturePromoSpecification FeaturePromoSpecification::CreateForTutorialPromo(
+ const base::Feature& feature,
+ ui::ElementIdentifier anchor_element_id,
+ int body_text_string_id,
+ TutorialIdentifier tutorial_id) {
+ FeaturePromoSpecification spec(&feature, PromoType::kTutorial,
+ anchor_element_id, body_text_string_id);
+ DCHECK(!tutorial_id.empty());
+ spec.tutorial_id_ = tutorial_id;
+ return spec;
+}
+
+// static
+FeaturePromoSpecification FeaturePromoSpecification::CreateForLegacyPromo(
+ const base::Feature* feature,
+ ui::ElementIdentifier anchor_element_id,
+ int body_text_string_id) {
+ return FeaturePromoSpecification(feature, PromoType::kLegacy,
+ anchor_element_id, body_text_string_id);
+}
+
+FeaturePromoSpecification& FeaturePromoSpecification::SetBubbleTitleText(
+ int title_text_string_id) {
+ DCHECK_NE(promo_type_, PromoType::kUnspecifiied);
+ bubble_title_text_ = l10n_util::GetStringUTF16(title_text_string_id);
+ return *this;
+}
+
+FeaturePromoSpecification& FeaturePromoSpecification::SetBubbleIcon(
+ const gfx::VectorIcon* bubble_icon) {
+ DCHECK_NE(promo_type_, PromoType::kUnspecifiied);
+ bubble_icon_ = bubble_icon;
+ return *this;
+}
+
+FeaturePromoSpecification& FeaturePromoSpecification::SetBubbleArrow(
+ HelpBubbleArrow bubble_arrow) {
+ bubble_arrow_ = bubble_arrow;
+ return *this;
+}
+
+FeaturePromoSpecification& FeaturePromoSpecification::SetAnchorElementFilter(
+ AnchorElementFilter anchor_element_filter) {
+ anchor_element_filter_ = std::move(anchor_element_filter);
+ return *this;
+}
+
+FeaturePromoSpecification& FeaturePromoSpecification::SetDemoPageInfo(
+ DemoPageInfo demo_page_info) {
+ demo_page_info_ = std::move(demo_page_info);
+ return *this;
+}
+
+ui::TrackedElement* FeaturePromoSpecification::GetAnchorElement(
+ ui::ElementContext context) const {
+ auto* const element_tracker = ui::ElementTracker::GetElementTracker();
+ return anchor_element_filter_ ? anchor_element_filter_.Run(
+ element_tracker->GetAllMatchingElements(
+ anchor_element_id_, context))
+ : element_tracker->GetFirstMatchingElement(
+ anchor_element_id_, context);
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/feature_promo_specification.h b/chromium/components/user_education/common/feature_promo_specification.h
new file mode 100644
index 00000000000..69792ce6ebc
--- /dev/null
+++ b/chromium/components/user_education/common/feature_promo_specification.h
@@ -0,0 +1,231 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_SPECIFICATION_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_SPECIFICATION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/raw_ptr.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/common/tutorial_identifier.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+
+namespace base {
+struct Feature;
+}
+
+namespace gfx {
+struct VectorIcon;
+}
+
+namespace user_education {
+
+// Specifies the parameters for a feature promo and its associated bubble.
+class FeaturePromoSpecification {
+ public:
+ // The body text (specified by |bubble_body_string_id|) can have parameters
+ // that can be specified situationally. When specifying these parameters,
+ // use a |StringReplacements| object.
+ using StringReplacements = std::vector<std::u16string>;
+
+ // Optional method that filters a set of potential `elements` to choose and
+ // return the anchor element, or null if none of the inputs is appropriate.
+ // This method can return an element different from the input list, or null
+ // if no valid element is found (this will cause the IPH not to run).
+ using AnchorElementFilter = base::RepeatingCallback<ui::TrackedElement*(
+ const ui::ElementTracker::ElementList& elements)>;
+
+ // Describes the type of promo. Used to configure defaults for the promo's
+ // bubble.
+ enum class PromoType {
+ // Uninitialized/invalid specification.
+ kUnspecifiied,
+ // A toast-style promo.
+ kToast,
+ // A snooze-style promo.
+ kSnooze,
+ // A tutorial promo.
+ kTutorial,
+ // A simple promo that acts like a toast but without the required
+ // accessibility data.
+ kLegacy,
+ };
+
+ // Represents a command or command accelerator. Can be valueless (falsy) if
+ // neither a command ID nor an explicit accelerator is specified.
+ class AcceleratorInfo {
+ public:
+ // You can assign either an int (command ID) or a ui::Accelerator to an
+ // AcceleratorInfo object.
+ using ValueType = absl::variant<int, ui::Accelerator>;
+
+ AcceleratorInfo();
+ AcceleratorInfo(const AcceleratorInfo& other);
+ ~AcceleratorInfo();
+
+ explicit AcceleratorInfo(ValueType value);
+ AcceleratorInfo& operator=(ValueType value);
+ AcceleratorInfo& operator=(const AcceleratorInfo& other);
+
+ explicit operator bool() const;
+ bool operator!() const { return !static_cast<bool>(*this); }
+
+ ui::Accelerator GetAccelerator(
+ const ui::AcceleratorProvider* provider) const;
+
+ private:
+ ValueType value_;
+ };
+
+ struct DemoPageInfo {
+ std::string display_title;
+ std::string display_description;
+ base::RepeatingClosure setup_for_feature_promo_callback;
+
+ explicit DemoPageInfo(
+ std::string display_title_ = std::string(),
+ std::string display_description_ = std::string(),
+ base::RepeatingClosure setup_for_feature_promo_callback_ =
+ base::DoNothing());
+ ~DemoPageInfo();
+ DemoPageInfo(const DemoPageInfo& other);
+ DemoPageInfo& operator=(const DemoPageInfo& other);
+ };
+
+ FeaturePromoSpecification();
+ FeaturePromoSpecification(FeaturePromoSpecification&& other);
+ ~FeaturePromoSpecification();
+ FeaturePromoSpecification& operator=(FeaturePromoSpecification&& other);
+
+ // Specifies a standard toast promo.
+ // Because toasts are transient, they expect a separate screen reader prompt.
+ // It is recommended that the prompt include an
+ static FeaturePromoSpecification CreateForToastPromo(
+ const base::Feature& feature,
+ ui::ElementIdentifier anchor_element_id,
+ int body_text_string_id,
+ int accessible_text_string_id,
+ AcceleratorInfo accessible_accelerator);
+
+ // Specifies a promo with snooze buttons.
+ static FeaturePromoSpecification CreateForSnoozePromo(
+ const base::Feature& feature,
+ ui::ElementIdentifier anchor_element_id,
+ int body_text_string_id);
+
+ // Specifies a promo that launches a tutorial.
+ static FeaturePromoSpecification CreateForTutorialPromo(
+ const base::Feature& feature,
+ ui::ElementIdentifier anchor_element_id,
+ int body_text_string_id,
+ TutorialIdentifier tutorial_id);
+
+ // Specifies a text-only promo without additional accessibility information.
+ // Deprecated. Only included for backwards compatibility with existing
+ // promos. This is the only case in which |feature| can be null, and if it is
+ // the result can only be used for a critical promo.
+ static FeaturePromoSpecification CreateForLegacyPromo(
+ const base::Feature* feature,
+ ui::ElementIdentifier anchor_element_id,
+ int body_text_string_id);
+
+ // Set the optional bubble title. This text appears above the body text in a
+ // slightly larger font.
+ FeaturePromoSpecification& SetBubbleTitleText(int title_text_string_id);
+
+ // Set the optional bubble icon. This is displayed next to the title or body
+ // text.
+ FeaturePromoSpecification& SetBubbleIcon(const gfx::VectorIcon* bubble_icon);
+
+ // Set the bubble arrow. Default is top-left.
+ FeaturePromoSpecification& SetBubbleArrow(HelpBubbleArrow bubble_arrow);
+
+ // Set the anchor element filter.
+ FeaturePromoSpecification& SetAnchorElementFilter(
+ AnchorElementFilter anchor_element_filter);
+
+ // Get the anchor element based on `anchor_element_id`,
+ // `anchor_element_filter`, and `context`.
+ ui::TrackedElement* GetAnchorElement(ui::ElementContext context) const;
+
+ const base::Feature* feature() const { return feature_; }
+ PromoType promo_type() const { return promo_type_; }
+ ui::ElementIdentifier anchor_element_id() const { return anchor_element_id_; }
+ const AnchorElementFilter& anchor_element_filter() const {
+ return anchor_element_filter_;
+ }
+ int bubble_body_string_id() const { return bubble_body_string_id_; }
+ const std::u16string& bubble_title_text() const { return bubble_title_text_; }
+ const gfx::VectorIcon* bubble_icon() const { return bubble_icon_; }
+ HelpBubbleArrow bubble_arrow() const { return bubble_arrow_; }
+ int screen_reader_string_id() const { return screen_reader_string_id_; }
+ const AcceleratorInfo& screen_reader_accelerator() const {
+ return screen_reader_accelerator_;
+ }
+ const DemoPageInfo& demo_page_info() const { return demo_page_info_; }
+ FeaturePromoSpecification& SetDemoPageInfo(DemoPageInfo demo_page_info);
+ const TutorialIdentifier& tutorial_id() const { return tutorial_id_; }
+
+ private:
+ static constexpr HelpBubbleArrow kDefaultBubbleArrow =
+ HelpBubbleArrow::kTopRight;
+
+ FeaturePromoSpecification(const base::Feature* feature,
+ PromoType promo_type,
+ ui::ElementIdentifier anchor_element_id,
+ int bubble_body_string_id);
+
+ raw_ptr<const base::Feature> feature_ = nullptr;
+
+ // The type of promo. A promo with type kUnspecified is not valid.
+ PromoType promo_type_ = PromoType::kUnspecifiied;
+
+ // The element identifier of the element to attach the promo to.
+ ui::ElementIdentifier anchor_element_id_;
+
+ // The filter to use if there is more than one matching element, or
+ // additional processing is needed (default is to always use the first
+ // matching element).
+ AnchorElementFilter anchor_element_filter_;
+
+ // Text to be displayed in the promo bubble body. Should not be zero for
+ // valid bubbles. We keep the string ID around because we can specify format
+ // parameters when we actually create the bubble.
+ int bubble_body_string_id_ = 0;
+
+ // Optional text that is displayed at the top of the bubble, in a slightly
+ // more prominent font.
+ std::u16string bubble_title_text_;
+
+ // Optional icon that is displayed next to bubble text.
+ raw_ptr<const gfx::VectorIcon> bubble_icon_ = nullptr;
+
+ // Optional arrow pointing to the promo'd element. Defaults to top left.
+ HelpBubbleArrow bubble_arrow_ = kDefaultBubbleArrow;
+
+ // Optional screen reader announcement that replaces bubble text when the
+ // bubble is first announced.
+ int screen_reader_string_id_ = 0;
+
+ // Accelerator that is used to fill in a parametric field in
+ // screen_reader_string_id_.
+ AcceleratorInfo screen_reader_accelerator_;
+
+ // Information to be displayed on the demo page
+ DemoPageInfo demo_page_info_;
+
+ // Tutorial identifier if the user decides to view a tutorial.
+ TutorialIdentifier tutorial_id_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_SPECIFICATION_H_
diff --git a/chromium/components/user_education/common/help_bubble.cc b/chromium/components/user_education/common/help_bubble.cc
new file mode 100644
index 00000000000..27a1fc04ad7
--- /dev/null
+++ b/chromium/components/user_education/common/help_bubble.cc
@@ -0,0 +1,68 @@
+// Copyright 2021 The Chromium 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/user_education/common/help_bubble.h"
+
+#include "base/auto_reset.h"
+#include "base/notreached.h"
+
+namespace user_education {
+
+HelpBubble::HelpBubble()
+ : on_close_callbacks_(std::make_unique<CallbackList>()) {}
+
+HelpBubble::~HelpBubble() {
+ // Derived classes must call Close() in destructor lest the bubble be
+ // destroyed without cleaning up the framework-specific implementation. Since
+ // Close() itself depends on framework-specific logic, however, it cannot be
+ // called here, as virtual functions are no longer available in the base
+ // destructor.
+ CHECK(is_closed());
+}
+
+bool HelpBubble::Close() {
+ // This prevents us from re-entrancy during CloseBubbleImpl() or after the
+ // bubble is closed.
+ if (is_closed() || closing_)
+ return false;
+
+ {
+ // Prevent re-entrancy until is_closed() becomes true, which happens during
+ // NotifyBubbleClosed().
+ base::AutoReset<bool> closing_guard(&closing_, true);
+ CloseBubbleImpl();
+ }
+
+ // This call could delete `this` so no code can come after it.
+ NotifyBubbleClosed();
+ return true;
+}
+
+void HelpBubble::OnAnchorBoundsChanged() {}
+
+gfx::Rect HelpBubble::GetBoundsInScreen() const {
+ return gfx::Rect();
+}
+
+base::CallbackListSubscription HelpBubble::AddOnCloseCallback(
+ ClosedCallback callback) {
+ if (is_closed()) {
+ NOTREACHED();
+ return base::CallbackListSubscription();
+ }
+
+ return on_close_callbacks_->Add(std::move(callback));
+}
+
+void HelpBubble::NotifyBubbleClosed() {
+ // We can't destruct the callback list during callbacks, so ensure that it
+ // sticks around until the callbacks are all finished. This also has the side
+ // effect of making is_closed() true since it resets the value of
+ // `on_close_callbacks_`.
+ std::unique_ptr<CallbackList> temp = std::move(on_close_callbacks_);
+ if (temp)
+ temp->Notify(this);
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/help_bubble.h b/chromium/components/user_education/common/help_bubble.h
new file mode 100644
index 00000000000..d63e8d2c476
--- /dev/null
+++ b/chromium/components/user_education/common/help_bubble.h
@@ -0,0 +1,76 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_H_
+
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "base/compiler_specific.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/framework_specific_implementation.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace user_education {
+
+// HelpBubble is an interface for the lifecycle of an IPH or tutorial bubble.
+// it is implemented by a framework's bubble. It is returned as the result of
+// HelpBubbleFactory's CreateBubble...() method.
+class HelpBubble : public ui::FrameworkSpecificImplementation {
+ public:
+ // Callback to be notified when the help bubble is closed. Note that the
+ // pointer passed in is entirely for reference and should not be dereferenced
+ // as another callback may have deleted the bubble itself.
+ using ClosedCallback = base::OnceCallback<void(HelpBubble*)>;
+
+ HelpBubble();
+ ~HelpBubble() override;
+
+ // Sets input focus on the bubble or on the bubble's anchor.
+ virtual bool ToggleFocusForAccessibility() = 0;
+
+ // Closes the bubble if it is not already closed. Returns whether the bubble
+ // was open.
+ bool Close();
+
+ // Notify that the element the help bubble is anchored to may have moved.
+ // Default is no-op.
+ virtual void OnAnchorBoundsChanged();
+
+ // Get the bounds of the bubble in the screen. Default is gfx::Rect(), which
+ // indicates that the bubble's screen position is not identifiable, or that
+ // the bubble is not visible.
+ virtual gfx::Rect GetBoundsInScreen() const;
+
+ // Returns the context of this help bubble (if there is one).
+ virtual ui::ElementContext GetContext() const = 0;
+
+ // Add a callback to know when a bubble is going away.
+ [[nodiscard]] base::CallbackListSubscription AddOnCloseCallback(
+ ClosedCallback callback);
+
+ bool is_open() const { return !is_closed() && !closing_; }
+
+ protected:
+ // Actually close the bubble.
+ virtual void CloseBubbleImpl() = 0;
+
+ // Updates internal state to indicate that the bubble has been closed.
+ // Called by Close(), but can also be called if the bubble is closed by user
+ // action, etc.
+ void NotifyBubbleClosed();
+
+ private:
+ // Closed callbacks are cleared out on close, so this keeps us from having to
+ // store extra data about closed status that could become out of sync.
+ bool is_closed() const { return !on_close_callbacks_; }
+
+ using CallbackList = base::OnceCallbackList<ClosedCallback::RunType>;
+ std::unique_ptr<CallbackList> on_close_callbacks_;
+ bool closing_ = false;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_H_
diff --git a/chromium/components/user_education/common/help_bubble_factory.h b/chromium/components/user_education/common/help_bubble_factory.h
new file mode 100644
index 00000000000..430525a7043
--- /dev/null
+++ b/chromium/components/user_education/common/help_bubble_factory.h
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_FACTORY_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_FACTORY_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "ui/base/interaction/framework_specific_implementation.h"
+
+namespace ui {
+class TrackedElement;
+}
+
+namespace user_education {
+
+class HelpBubble;
+struct HelpBubbleParams;
+
+// HelpBubbleFactory is an interface for opening and closing bubbles from a
+// running user education journey. Since some platforms/implementations of
+// bubbles will have their own reqirements/constraints for if/when/how to show
+// a bubble, we only call into the implementation asking it to show or hide the
+// bubble. All other state must be maintained by the caller.
+class HelpBubbleFactory : public ui::FrameworkSpecificImplementation {
+ public:
+ HelpBubbleFactory() = default;
+ ~HelpBubbleFactory() override = default;
+
+ // Returns whether the bubble owner can show a bubble for the TrackedElement.
+ virtual bool CanBuildBubbleForTrackedElement(
+ const ui::TrackedElement* element) const = 0;
+
+ // Called to actually show the bubble.
+ virtual std::unique_ptr<HelpBubble> CreateBubble(ui::TrackedElement* element,
+ HelpBubbleParams params) = 0;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_FACTORY_H_
diff --git a/chromium/components/user_education/common/help_bubble_factory_registry.cc b/chromium/components/user_education/common/help_bubble_factory_registry.cc
new file mode 100644
index 00000000000..e3bc9ecda5d
--- /dev/null
+++ b/chromium/components/user_education/common/help_bubble_factory_registry.cc
@@ -0,0 +1,90 @@
+// Copyright 2021 The Chromium 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/user_education/common/help_bubble_factory_registry.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback_list.h"
+#include "base/no_destructor.h"
+#include "components/user_education/common/help_bubble.h"
+#include "components/user_education/common/help_bubble_factory.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+
+namespace user_education {
+
+HelpBubbleFactoryRegistry::HelpBubbleFactoryRegistry() = default;
+
+HelpBubbleFactoryRegistry::~HelpBubbleFactoryRegistry() {
+ for (auto& pr : help_bubbles_) {
+ // Unsubscribe from the bubble before trying to close it so we don't try to
+ // modify the map while we're iterating it.
+ pr.second = base::CallbackListSubscription();
+ pr.first->Close();
+ }
+}
+
+std::unique_ptr<HelpBubble> HelpBubbleFactoryRegistry::CreateHelpBubble(
+ ui::TrackedElement* element,
+ HelpBubbleParams params) {
+ CHECK(element);
+ for (auto& bubble_factory : factories_) {
+ if (bubble_factory.CanBuildBubbleForTrackedElement(element)) {
+ auto result = bubble_factory.CreateBubble(element, std::move(params));
+ if (result) {
+ help_bubbles_.emplace(
+ result.get(), result->AddOnCloseCallback(base::BindOnce(
+ &HelpBubbleFactoryRegistry::OnHelpBubbleClosed,
+ base::Unretained(this))));
+ }
+ return result;
+ }
+ }
+ return nullptr;
+}
+
+void HelpBubbleFactoryRegistry::NotifyAnchorBoundsChanged(
+ ui::ElementContext context) {
+ for (const auto& pr : help_bubbles_) {
+ if (pr.first->GetContext() == context)
+ pr.first->OnAnchorBoundsChanged();
+ }
+}
+
+bool HelpBubbleFactoryRegistry::ToggleFocusForAccessibility(
+ ui::ElementContext context) {
+ for (const auto& pr : help_bubbles_) {
+ if (pr.first->GetContext() == context &&
+ pr.first->ToggleFocusForAccessibility()) {
+ toggle_focus_callbacks_.Notify(pr.first);
+ return true;
+ }
+ }
+ return false;
+}
+
+base::CallbackListSubscription
+HelpBubbleFactoryRegistry::AddToggleFocusCallback(
+ ToggleFocusCallback callback) {
+ return toggle_focus_callbacks_.Add(std::move(callback));
+}
+
+HelpBubble* HelpBubbleFactoryRegistry::GetHelpBubble(
+ ui::ElementContext context) {
+ for (const auto& pr : help_bubbles_) {
+ if (pr.first->GetContext() == context)
+ return pr.first;
+ }
+ return nullptr;
+}
+
+void HelpBubbleFactoryRegistry::OnHelpBubbleClosed(HelpBubble* bubble) {
+ const auto result = help_bubbles_.erase(bubble);
+ DCHECK(result);
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/help_bubble_factory_registry.h b/chromium/components/user_education/common/help_bubble_factory_registry.h
new file mode 100644
index 00000000000..430411e91d0
--- /dev/null
+++ b/chromium/components/user_education/common/help_bubble_factory_registry.h
@@ -0,0 +1,85 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_FACTORY_REGISTRY_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_FACTORY_REGISTRY_H_
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/callback_list.h"
+#include "components/user_education/common/help_bubble_factory.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/interaction/framework_specific_implementation.h"
+
+namespace user_education {
+
+// HelpBubbleFactoryRegistry provides access to framework specific bubble
+// factory implementations. HelpBubbleFactoryRegistry is not a strict singleton
+// that instances can be created multiple times in a test environment.
+class HelpBubbleFactoryRegistry {
+ public:
+ using ToggleFocusCallback = base::RepeatingCallback<void(HelpBubble*)>;
+
+ HelpBubbleFactoryRegistry();
+ ~HelpBubbleFactoryRegistry();
+ HelpBubbleFactoryRegistry(const HelpBubbleFactoryRegistry&) = delete;
+ HelpBubbleFactoryRegistry& operator=(const HelpBubbleFactoryRegistry&) =
+ delete;
+
+ // Finds the appropriate factory and creates a bubble, or returns null if the
+ // bubble cannot be created.
+ std::unique_ptr<HelpBubble> CreateHelpBubble(ui::TrackedElement* element,
+ HelpBubbleParams params);
+
+ // Returns true if any bubble is showing in any framework.
+ bool is_any_bubble_showing() const { return !help_bubbles_.empty(); }
+
+ // Notifies frameworks that the anchor bounds for a bubble in the given
+ // context might have changed. For frameworks which automatically reposition
+ // bubbles, this will be a no-op.
+ void NotifyAnchorBoundsChanged(ui::ElementContext context);
+
+ // Sets focus on a help bubble if there is one in the given context (or
+ // toggles between help) bubble and its anchor; returns false if there is no
+ // bubble or nothing can be focused.
+ bool ToggleFocusForAccessibility(ui::ElementContext context);
+
+ // Listens for ToggleFocusForAccessibility() calls for metrics purposes.
+ base::CallbackListSubscription AddToggleFocusCallback(
+ ToggleFocusCallback callback);
+
+ // Gets the first visible help bubble in the given context, or null if none
+ // exists.
+ HelpBubble* GetHelpBubble(ui::ElementContext context);
+
+ // Adds a bubble factory of type `T` to the list of bubble factories, if it
+ // is not already present.
+ template <class T, typename... Args>
+ void MaybeRegister(Args&&... args) {
+ factories_.MaybeRegister<T>(std::forward<Args>(args)...);
+ }
+
+ private:
+ void OnHelpBubbleClosed(HelpBubble* help_bubble);
+
+ // The list of known factories.
+ ui::FrameworkSpecificRegistrationList<HelpBubbleFactory> factories_;
+
+ // The list of known help bubbles.
+ std::map<HelpBubble*, base::CallbackListSubscription> help_bubbles_;
+
+ // For listening
+ base::RepeatingCallbackList<typename ToggleFocusCallback::RunType>
+ toggle_focus_callbacks_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_FACTORY_REGISTRY_H_
diff --git a/chromium/components/user_education/common/help_bubble_factory_registry_unittest.cc b/chromium/components/user_education/common/help_bubble_factory_registry_unittest.cc
new file mode 100644
index 00000000000..275636d2fb0
--- /dev/null
+++ b/chromium/components/user_education/common/help_bubble_factory_registry_unittest.cc
@@ -0,0 +1,249 @@
+// Copyright 2021 The Chromium 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/user_education/common/help_bubble_factory_registry.h"
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/callback_helpers.h"
+#include "base/memory/raw_ptr.h"
+#include "base/test/mock_callback.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/test/test_help_bubble.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_test_util.h"
+#include "ui/base/interaction/expect_call_in_scope.h"
+
+namespace user_education {
+
+namespace {
+
+// Placeholder ID and context for test elements.
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestElementIdentifier);
+const ui::ElementContext kTestElementContext(1U);
+const ui::ElementContext kTestElementContext2(2U);
+
+} // namespace
+
+class HelpBubbleFactoryRegistryTest : public testing::Test {
+ public:
+ HelpBubbleFactoryRegistryTest()
+ : test_element_(kTestElementIdentifier, kTestElementContext) {
+ help_bubble_factory_registry_.MaybeRegister<test::TestHelpBubbleFactory>();
+ }
+
+ ~HelpBubbleFactoryRegistryTest() override = default;
+
+ void TearDown() override {
+ // By the time we get to tear-down, all bubbles should be closed.
+ ASSERT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+ }
+
+ protected:
+ HelpBubbleParams GetBubbleParams() {
+ HelpBubbleParams params;
+ params.body_text = u"To X, do Y";
+ params.arrow = HelpBubbleArrow::kTopRight;
+ return params;
+ }
+
+ HelpBubbleFactoryRegistry help_bubble_factory_registry_;
+ ui::test::TestElement test_element_;
+};
+
+TEST_F(HelpBubbleFactoryRegistryTest, ShowsBubble) {
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ ASSERT_TRUE(bubble);
+ EXPECT_TRUE(bubble->is_open());
+ EXPECT_TRUE(help_bubble_factory_registry_.is_any_bubble_showing());
+
+ bubble.reset();
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, ShowSecondBubble) {
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ auto bubble2 = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+
+ EXPECT_TRUE(bubble);
+ EXPECT_TRUE(bubble2);
+ EXPECT_TRUE(help_bubble_factory_registry_.is_any_bubble_showing());
+ bubble.reset();
+ EXPECT_TRUE(help_bubble_factory_registry_.is_any_bubble_showing());
+ bubble2.reset();
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, DeleteBubble) {
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ bubble.reset();
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, DeleteTwoBubbles) {
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ auto bubble2 = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ bubble.reset();
+ EXPECT_TRUE(help_bubble_factory_registry_.is_any_bubble_showing());
+ bubble2.reset();
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, CloseBubble) {
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, close_callback);
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, timeout_callback);
+ auto params = GetBubbleParams();
+ params.dismiss_callback = close_callback.Get();
+ params.timeout_callback = timeout_callback.Get();
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, std::move(params));
+
+ bubble->Close();
+ EXPECT_FALSE(bubble->is_open());
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, DismissBubble) {
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, close_callback);
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, timeout_callback);
+ auto params = GetBubbleParams();
+ params.dismiss_callback = close_callback.Get();
+ params.timeout_callback = timeout_callback.Get();
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, std::move(params));
+ auto* const test_bubble = static_cast<test::TestHelpBubble*>(bubble.get());
+
+ EXPECT_CALL_IN_SCOPE(close_callback, Run, test_bubble->SimulateDismiss());
+ EXPECT_FALSE(bubble->is_open());
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, BubbleTimeout) {
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, close_callback);
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, timeout_callback);
+ auto params = GetBubbleParams();
+ params.dismiss_callback = close_callback.Get();
+ params.timeout_callback = timeout_callback.Get();
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, std::move(params));
+ auto* const test_bubble = static_cast<test::TestHelpBubble*>(bubble.get());
+
+ EXPECT_CALL_IN_SCOPE(timeout_callback, Run, test_bubble->SimulateTimeout());
+ EXPECT_FALSE(bubble->is_open());
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, PressButton) {
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, close_callback);
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, timeout_callback);
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, button_callback);
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, button2_callback);
+ auto params = GetBubbleParams();
+ params.dismiss_callback = close_callback.Get();
+ params.timeout_callback = timeout_callback.Get();
+ HelpBubbleButtonParams button_params;
+ button_params.text = u"press me";
+ button_params.is_default = true;
+ button_params.callback = button_callback.Get();
+ params.buttons.push_back(std::move(button_params));
+ HelpBubbleButtonParams button2_params;
+ button2_params.text = u"other";
+ button2_params.is_default = false;
+ button2_params.callback = button2_callback.Get();
+ params.buttons.push_back(std::move(button2_params));
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, std::move(params));
+ auto* const test_bubble = static_cast<test::TestHelpBubble*>(bubble.get());
+
+ EXPECT_CALL_IN_SCOPE(button_callback, Run,
+ test_bubble->SimulateButtonPress(0));
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, PressButton2) {
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, close_callback);
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, timeout_callback);
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, button_callback);
+ UNCALLED_MOCK_CALLBACK(base::OnceClosure, button2_callback);
+ auto params = GetBubbleParams();
+ params.dismiss_callback = close_callback.Get();
+ params.timeout_callback = timeout_callback.Get();
+ HelpBubbleButtonParams button_params;
+ button_params.text = u"press me";
+ button_params.is_default = true;
+ button_params.callback = button_callback.Get();
+ params.buttons.push_back(std::move(button_params));
+ HelpBubbleButtonParams button2_params;
+ button2_params.text = u"other";
+ button2_params.is_default = false;
+ button2_params.callback = button2_callback.Get();
+ params.buttons.push_back(std::move(button2_params));
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, std::move(params));
+ auto* const test_bubble = static_cast<test::TestHelpBubble*>(bubble.get());
+
+ EXPECT_CALL_IN_SCOPE(button2_callback, Run,
+ test_bubble->SimulateButtonPress(1));
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, ToggleFocusForAccessibility) {
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ ASSERT_TRUE(bubble);
+ auto* const test_bubble = static_cast<test::TestHelpBubble*>(bubble.get());
+ EXPECT_EQ(0, test_bubble->focus_count());
+ const bool result = help_bubble_factory_registry_.ToggleFocusForAccessibility(
+ kTestElementContext);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(1, test_bubble->focus_count());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest,
+ ToggleFocusForAccessibility_WrongContext) {
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ ASSERT_TRUE(bubble);
+ auto* const test_bubble = static_cast<test::TestHelpBubble*>(bubble.get());
+ EXPECT_EQ(0, test_bubble->focus_count());
+ const bool result = help_bubble_factory_registry_.ToggleFocusForAccessibility(
+ kTestElementContext2);
+ EXPECT_FALSE(result);
+ EXPECT_EQ(0, test_bubble->focus_count());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, CloseTwoBubbles) {
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ auto bubble2 = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ bubble->Close();
+ EXPECT_TRUE(help_bubble_factory_registry_.is_any_bubble_showing());
+ bubble2->Close();
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+}
+
+TEST_F(HelpBubbleFactoryRegistryTest, OpenSecondBubbleAfterClose) {
+ auto bubble = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ EXPECT_TRUE(help_bubble_factory_registry_.is_any_bubble_showing());
+ bubble->Close();
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+ auto bubble2 = help_bubble_factory_registry_.CreateHelpBubble(
+ &test_element_, GetBubbleParams());
+ EXPECT_TRUE(help_bubble_factory_registry_.is_any_bubble_showing());
+ bubble2->Close();
+ EXPECT_FALSE(help_bubble_factory_registry_.is_any_bubble_showing());
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/help_bubble_params.cc b/chromium/components/user_education/common/help_bubble_params.cc
new file mode 100644
index 00000000000..833b9a54a9d
--- /dev/null
+++ b/chromium/components/user_education/common/help_bubble_params.cc
@@ -0,0 +1,21 @@
+// Copyright 2021 The Chromium 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/user_education/common/help_bubble_params.h"
+
+namespace user_education {
+
+HelpBubbleButtonParams::HelpBubbleButtonParams() = default;
+HelpBubbleButtonParams::HelpBubbleButtonParams(HelpBubbleButtonParams&&) =
+ default;
+HelpBubbleButtonParams::~HelpBubbleButtonParams() = default;
+HelpBubbleButtonParams& HelpBubbleButtonParams::operator=(
+ HelpBubbleButtonParams&&) = default;
+
+HelpBubbleParams::HelpBubbleParams() = default;
+HelpBubbleParams::HelpBubbleParams(HelpBubbleParams&&) = default;
+HelpBubbleParams::~HelpBubbleParams() = default;
+HelpBubbleParams& HelpBubbleParams::operator=(HelpBubbleParams&&) = default;
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/help_bubble_params.h b/chromium/components/user_education/common/help_bubble_params.h
new file mode 100644
index 00000000000..f7e03a9c6f1
--- /dev/null
+++ b/chromium/components/user_education/common/help_bubble_params.h
@@ -0,0 +1,99 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_PARAMS_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_PARAMS_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/gfx/vector_icon_types.h"
+
+namespace user_education {
+
+// Mirrors most values of views::BubbleBorder::Arrow.
+// All values except kNone show a visible arrow between the bubble and the
+// anchor element.
+enum class HelpBubbleArrow {
+ kNone, // Positions the bubble directly beneath the anchor with no arrow.
+ kTopLeft,
+ kTopRight,
+ kBottomLeft,
+ kBottomRight,
+ kLeftTop,
+ kRightTop,
+ kLeftBottom,
+ kRightBottom,
+ kTopCenter,
+ kBottomCenter,
+ kLeftCenter,
+ kRightCenter,
+};
+
+struct HelpBubbleButtonParams {
+ HelpBubbleButtonParams();
+ HelpBubbleButtonParams(HelpBubbleButtonParams&&);
+ ~HelpBubbleButtonParams();
+ HelpBubbleButtonParams& operator=(HelpBubbleButtonParams&&);
+
+ std::u16string text;
+ bool is_default = false;
+ base::OnceClosure callback = base::DoNothing();
+};
+
+struct HelpBubbleParams {
+ HelpBubbleParams();
+ HelpBubbleParams(HelpBubbleParams&&);
+ ~HelpBubbleParams();
+ HelpBubbleParams& operator=(HelpBubbleParams&&);
+
+ HelpBubbleArrow arrow = HelpBubbleArrow::kTopRight;
+
+ std::u16string title_text;
+ raw_ptr<const gfx::VectorIcon> body_icon = nullptr;
+ std::u16string body_icon_alt_text;
+ std::u16string body_text;
+ std::u16string screenreader_text;
+
+ // Additional message to be read to screen reader users to aid in
+ // navigation.
+ std::u16string keyboard_navigation_hint;
+
+ // The buttons to display. Depending on platform defaults, a
+ // HelpBubbleFactory may choose to move a default button to the leading or
+ // trailing edge of the bubble; however the order of non-default buttons is
+ // guaranteed to remain stable.
+ std::vector<HelpBubbleButtonParams> buttons;
+
+ // If set to true, a close button will always be shown.
+ bool force_close_button = false;
+
+ // Alt text to use for the close button.
+ std::u16string close_button_alt_text;
+
+ // Determines whether a progress indicator will be displayed; if set the
+ // first value is current progress and the second is max progress.
+ absl::optional<std::pair<int, int>> progress;
+
+ // Sets the bubble timeout. If a timeout is not provided a default will
+ // be used. If the timeout is 0, the bubble never times out.
+ absl::optional<base::TimeDelta> timeout;
+
+ // Called when the bubble is actively dismissed by the user, using the close
+ // button or the ESC key.
+ base::OnceClosure dismiss_callback = base::DoNothing();
+
+ // Called when the bubble times out.
+ base::OnceClosure timeout_callback = base::DoNothing();
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_HELP_BUBBLE_PARAMS_H_
diff --git a/chromium/components/user_education/common/scoped_new_badge_tracker_base.cc b/chromium/components/user_education/common/scoped_new_badge_tracker_base.cc
new file mode 100644
index 00000000000..dc71054f5f2
--- /dev/null
+++ b/chromium/components/user_education/common/scoped_new_badge_tracker_base.cc
@@ -0,0 +1,58 @@
+// Copyright 2021 The Chromium 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/user_education/common/scoped_new_badge_tracker_base.h"
+
+#include "base/containers/contains.h"
+#include "base/feature_list.h"
+#include "components/feature_engagement/public/tracker.h"
+
+namespace user_education {
+
+ScopedNewBadgeTrackerBase::ScopedNewBadgeTrackerBase(
+ feature_engagement::Tracker* tracker)
+ : tracker_(tracker) {}
+
+// TODO(crbug.com/1258216): When we have the ability to do concurrent FE promos,
+// dismiss all of the badge promos here instead of in TryShowNewBadge().
+ScopedNewBadgeTrackerBase::~ScopedNewBadgeTrackerBase() = default;
+
+bool ScopedNewBadgeTrackerBase::TryShowNewBadge(
+ const base::Feature& badge_feature,
+ const base::Feature* promoted_feature) {
+ // In the event of a submenu that the user could open multiple times while
+ // navigating the same top-level menu, and we don't want to count those as
+ // separate times the user sees the New Badge:
+ if (base::Contains(active_badge_features_, &badge_feature))
+ return true;
+
+ // If there is no tracker available or the feature being promoted is disabled,
+ // do not show the New Badge.
+ if (!tracker_)
+ return false;
+ if (promoted_feature && !base::FeatureList::IsEnabled(*promoted_feature))
+ return false;
+
+ const bool result = tracker_->ShouldTriggerHelpUI(badge_feature);
+ if (result) {
+ active_badge_features_.insert(&badge_feature);
+ // TODO(crbug.com/1258216): Immediately dismiss to work around an issue
+ // where the FE backend disallows concurrent promos; move the call to
+ // Dismiss() to the destructor when concurrency is added.
+ //
+ // Note that "Dismiss" in this case does not dismiss the UI. It's telling
+ // the FE backend that the promo is done so that other promos can run. A
+ // badge showing in a menu should not block e.g. other badges from
+ // displaying (never mind help bubbles).
+ tracker_->Dismissed(badge_feature);
+ }
+ return result;
+}
+
+void ScopedNewBadgeTrackerBase::ActionPerformed(const char* event_name) {
+ if (tracker_)
+ tracker_->NotifyEvent(event_name);
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/scoped_new_badge_tracker_base.h b/chromium/components/user_education/common/scoped_new_badge_tracker_base.h
new file mode 100644
index 00000000000..16efd249f70
--- /dev/null
+++ b/chromium/components/user_education/common/scoped_new_badge_tracker_base.h
@@ -0,0 +1,134 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_SCOPED_NEW_BADGE_TRACKER_BASE_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_SCOPED_NEW_BADGE_TRACKER_BASE_H_
+
+#include <set>
+
+#include "base/memory/raw_ptr.h"
+
+namespace base {
+struct Feature;
+}
+
+namespace feature_engagement {
+class Tracker;
+}
+
+namespace user_education {
+
+// Works with the Feature Engagement system to determine when/how many times a
+// New Badge is displayed to the user. Wraps a feature_engagement::Tracker so
+// the correct calls are made to the Feature Engagement backend.
+//
+// Derive a subclass for a more convenient constructor (e.g. one that takes a
+// Browser or Profile rather than a raw feature_engagement::Tracker).
+//
+// The lifespan of a ScopedNewBadgeTracker should match the time the dialog or
+// menu containing the New Badge is visible to the user.
+//
+// You may use a single tracker for New Badges on multiple features in the same
+// menu or dialog, but make sure the feature flags and event names are distinct.
+//
+// Usage:
+//
+// * Menus
+//
+// Below is an example of using a ScopedNewBadgeTracker to add a New Badge to a
+// menu where the object implementing ui::SimpleMenuModel::Delegate is created
+// each time the menu is shown (e.g. AppMenuModel, TabContextMenuContents,
+// etc.) The case where the delegate object is persistent will be discussed
+// later.
+//
+// // In OnMenuWillShow(menu):
+// menu->SetIsNewFeatureAt(
+// menu->GetIndexOfCommandId(IDC_MY_FEATURE),
+// new_badge_tracker_.TryShowNewBadge(
+// feature_engagement::kIPHMyFeatureNewBadge,
+// &ui_features::kMyFeature));
+//
+// // In ExecuteCommand():
+// case IDC_MY_FEATURE:
+// new_badge_tracker_.EventPerformed("my_feature_activated");
+// ...
+// break;
+//
+// If the New Badge is in the top-level menu, you can move the call to
+// SetIsNewFeatureAt() to immediately after the menu model is initialized
+// (typically in the constructor or "Init" method) and you will not have to
+// override OnMenuWillBeShown().
+//
+// If you are handling multiple New Badges for different features, you will
+// want to check the result of GetIndexOfCommand() to make sure the menu being
+// shown is the one that contains the item receiving the new badge.
+//
+// If the ui::SimpleMenuModel::Delegate is a persistent object and is not
+// created each time the menu is displayed, you will need to move the tracker
+// down into the ui::SimpleMenuModel for your menu, and move your code from
+// OnMenuWillShow() to MenuWillShow() and from ExecuteCommand() to
+// ActivatedAt(int, int). Be sure to invoke base class behavior when overriding
+// these methods!
+//
+// * Dialogs
+//
+// Add a ScopedNewBadgeTracker member variable to your DialogDelegateView.
+// Dialogs are typically not re-usable; we create a new DialogDelegateView for
+// each time we show them. If you are following this pattern, include this in
+// your constructor or "Init" function after creating the NewBadgeLabel (but
+// before showing the dialog):
+//
+// new_badge_label_->SetDisplayNewBadge(
+// new_badge_tracker_.TryShowNewBadge(
+// feature_engagement::kIPHMyFeatureNewBadge,
+// &ui_features::kMyFeature));
+//
+// Then in the callback for the button that activates the feature being
+// promoted, call:
+//
+// new_badge_tracker_.EventPerformed("my_feature_activated");
+//
+// If for some reason you are re-using a dialog delegate, you must dynamically
+// create and destroy the tracker when the dialog is shown and hidden.
+class ScopedNewBadgeTrackerBase {
+ public:
+ // Constructs a scoped tracker for browser with |profile|.
+ explicit ScopedNewBadgeTrackerBase(feature_engagement::Tracker* tracker);
+
+ // This object should be destructed when the New Badge is going away, such as
+ // when a menu with a New Badge or a dialog with a NewBadgeLabel is closing.
+ // If TryShowNewBadge() returned true, the tracker will be informed that the
+ // promo has ended.
+ ~ScopedNewBadgeTrackerBase();
+
+ ScopedNewBadgeTrackerBase(const ScopedNewBadgeTrackerBase& other) = delete;
+ void operator=(const ScopedNewBadgeTrackerBase& other) = delete;
+
+ // Returns whether the New Badge should be shown.
+ //
+ // |badge_feature| is the feature flag for the New Badge itself.
+ //
+ // |promoted_feature|, if specified, is the flag for the feature the New Badge
+ // is promoting. You generally want to specify this feature even if the two
+ // flags are controlled by the same Finch study, because the user could
+ // override one but not the other. This parameter is optional because a New
+ // Badge promo could be shown for a feature without a flag, or for a feature
+ // that has already rolled to 100% and whose flag has been removed.
+ bool TryShowNewBadge(const base::Feature& badge_feature,
+ const base::Feature* promoted_feature = nullptr);
+
+ // Indicates that the user performed a promoted action. |action_event_name|
+ // should be the value referenced in the "event_used" parameter of your field
+ // trial configuration.
+ // Note: this is a wrapper around feature_engagement::Tracker::NotifyEvent().
+ void ActionPerformed(const char* action_event_name);
+
+ private:
+ const raw_ptr<feature_engagement::Tracker> tracker_;
+ std::set<const base::Feature*> active_badge_features_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_SCOPED_NEW_BADGE_TRACKER_BASE_H_
diff --git a/chromium/components/user_education/common/tutorial.cc b/chromium/components/user_education/common/tutorial.cc
new file mode 100644
index 00000000000..4d3695695da
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial.cc
@@ -0,0 +1,361 @@
+// Copyright 2021 The Chromium 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/user_education/common/tutorial.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/time/time.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_education/common/help_bubble.h"
+#include "components/user_education/common/help_bubble_factory.h"
+#include "components/user_education/common/help_bubble_factory_registry.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/common/tutorial_description.h"
+#include "components/user_education/common/tutorial_service.h"
+#include "components/vector_icons/vector_icons.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/interaction/interaction_sequence.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace user_education {
+
+Tutorial::StepBuilder::StepBuilder() = default;
+Tutorial::StepBuilder::StepBuilder(const TutorialDescription::Step& step)
+ : step_(step) {}
+Tutorial::StepBuilder::~StepBuilder() = default;
+
+// static
+std::unique_ptr<ui::InteractionSequence::Step>
+Tutorial::StepBuilder::BuildFromDescriptionStep(
+ const TutorialDescription::Step& step,
+ absl::optional<std::pair<int, int>> progress,
+ bool is_last_step,
+ bool can_be_restarted,
+ TutorialService* tutorial_service) {
+ Tutorial::StepBuilder step_builder(step);
+ step_builder.SetProgress(progress)
+ .SetIsLastStep(is_last_step)
+ .SetCanBeRestarted(can_be_restarted);
+
+ return step_builder.Build(tutorial_service);
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetAnchorElementID(
+ ui::ElementIdentifier anchor_element_id) {
+ // Element ID and Element Name are mutually exclusive
+ DCHECK(!anchor_element_id || step_.element_name.empty());
+
+ step_.element_id = anchor_element_id;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetAnchorElementName(
+ std::string anchor_element_name) {
+ // Element ID and Element Name are mutually exclusive
+ DCHECK(anchor_element_name.empty() || !step_.element_id);
+ step_.element_name = anchor_element_name;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetTitleTextID(
+ int title_text_id) {
+ step_.title_text_id = title_text_id;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetBodyTextID(int body_text_id) {
+ step_.body_text_id = body_text_id;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetStepType(
+ ui::InteractionSequence::StepType step_type_,
+ ui::CustomElementEventType event_type_) {
+ DCHECK_EQ(step_type_ == ui::InteractionSequence::StepType::kCustomEvent,
+ static_cast<bool>(event_type_))
+ << "`event_type_` should be set if and only if `step_type_` is "
+ "kCustomEvent.";
+ step_.step_type = step_type_;
+ step_.event_type = event_type_;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetProgress(
+ absl::optional<std::pair<int, int>> progress_) {
+ progress = progress_;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetArrow(HelpBubbleArrow arrow_) {
+ step_.arrow = arrow_;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetIsLastStep(
+ bool is_last_step_) {
+ is_last_step = is_last_step_;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetMustRemainVisible(
+ bool must_remain_visible_) {
+ step_.must_remain_visible = must_remain_visible_;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetTransitionOnlyOnEvent(
+ bool transition_only_on_event_) {
+ step_.transition_only_on_event = transition_only_on_event_;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetNameElementsCallback(
+ TutorialDescription::NameElementsCallback name_elements_callback_) {
+ step_.name_elements_callback = name_elements_callback_;
+ return *this;
+}
+
+Tutorial::StepBuilder& Tutorial::StepBuilder::SetCanBeRestarted(
+ bool can_be_restarted_) {
+ can_be_restarted = can_be_restarted_;
+ return *this;
+}
+
+std::unique_ptr<ui::InteractionSequence::Step> Tutorial::StepBuilder::Build(
+ TutorialService* tutorial_service) {
+ std::unique_ptr<ui::InteractionSequence::StepBuilder>
+ interaction_sequence_step_builder =
+ std::make_unique<ui::InteractionSequence::StepBuilder>();
+
+ if (step_.element_id)
+ interaction_sequence_step_builder->SetElementID(step_.element_id);
+
+ if (!step_.element_name.empty())
+ interaction_sequence_step_builder->SetElementName(step_.element_name);
+
+ interaction_sequence_step_builder->SetType(step_.step_type, step_.event_type);
+
+ if (step_.must_remain_visible.has_value())
+ interaction_sequence_step_builder->SetMustRemainVisible(
+ step_.must_remain_visible.value());
+
+ interaction_sequence_step_builder->SetTransitionOnlyOnEvent(
+ step_.transition_only_on_event);
+
+ interaction_sequence_step_builder->SetStartCallback(
+ BuildStartCallback(tutorial_service));
+ interaction_sequence_step_builder->SetEndCallback(
+ BuildHideBubbleCallback(tutorial_service));
+
+ return interaction_sequence_step_builder->Build();
+}
+
+ui::InteractionSequence::StepStartCallback
+Tutorial::StepBuilder::BuildStartCallback(TutorialService* tutorial_service) {
+ // get show bubble callback
+ ui::InteractionSequence::StepStartCallback maybe_show_bubble_callback =
+ BuildMaybeShowBubbleCallback(tutorial_service);
+
+ return base::BindOnce(
+ [](TutorialDescription::NameElementsCallback name_elements_callback,
+ ui::InteractionSequence::StepStartCallback maybe_show_bubble_callback,
+ ui::InteractionSequence* sequence, ui::TrackedElement* element) {
+ if (name_elements_callback)
+ name_elements_callback.Run(sequence, element);
+ if (maybe_show_bubble_callback)
+ std::move(maybe_show_bubble_callback).Run(sequence, element);
+ },
+ step_.name_elements_callback, std::move(maybe_show_bubble_callback));
+}
+
+ui::InteractionSequence::StepStartCallback
+Tutorial::StepBuilder::BuildMaybeShowBubbleCallback(
+ TutorialService* tutorial_service) {
+ if (!step_.ShouldShowBubble())
+ return ui::InteractionSequence::StepStartCallback();
+
+ const std::u16string title_text =
+ step_.title_text_id ? l10n_util::GetStringUTF16(step_.title_text_id)
+ : std::u16string();
+
+ const std::u16string body_text =
+ step_.body_text_id ? l10n_util::GetStringUTF16(step_.body_text_id)
+ : std::u16string();
+
+ return base::BindOnce(
+ [](TutorialService* tutorial_service, std::u16string title_text_,
+ std::u16string body_text_, HelpBubbleArrow arrow_,
+ absl::optional<std::pair<int, int>> progress_, bool is_last_step_,
+ bool can_be_restarted_, ui::InteractionSequence* sequence,
+ ui::TrackedElement* element) {
+ DCHECK(tutorial_service);
+
+ tutorial_service->HideCurrentBubbleIfShowing();
+
+ HelpBubbleParams params;
+ params.title_text = title_text_;
+ params.body_text = body_text_;
+ params.progress = progress_;
+ params.arrow = arrow_;
+ params.timeout = base::TimeDelta();
+ params.dismiss_callback = base::BindOnce(
+ [](absl::optional<int> step_number,
+ TutorialService* tutorial_service) {
+ tutorial_service->AbortTutorial(step_number);
+ },
+ progress_.has_value() ? absl::make_optional(progress_.value().first)
+ : absl::nullopt,
+ base::Unretained(tutorial_service));
+
+ if (is_last_step_) {
+ params.body_icon = &vector_icons::kCelebrationIcon;
+ params.body_icon_alt_text =
+ tutorial_service->GetBodyIconAltText(true);
+ params.dismiss_callback = base::BindOnce(
+ [](TutorialService* tutorial_service) {
+ tutorial_service->CompleteTutorial();
+ },
+ base::Unretained(tutorial_service));
+
+ if (can_be_restarted_) {
+ HelpBubbleButtonParams restart_button;
+ restart_button.text =
+ l10n_util::GetStringUTF16(IDS_TUTORIAL_RESTART_TUTORIAL);
+ restart_button.is_default = false;
+ restart_button.callback = base::BindOnce(
+ [](TutorialService* tutorial_service) {
+ tutorial_service->RestartTutorial();
+ },
+ base::Unretained(tutorial_service));
+ params.buttons.emplace_back(std::move(restart_button));
+ }
+
+ HelpBubbleButtonParams close_button;
+ close_button.text =
+ l10n_util::GetStringUTF16(IDS_TUTORIAL_CLOSE_TUTORIAL);
+ close_button.is_default = true;
+ close_button.callback = base::BindOnce(
+ [](TutorialService* tutorial_service) {
+ tutorial_service->CompleteTutorial();
+ },
+ base::Unretained(tutorial_service));
+ params.buttons.emplace_back(std::move(close_button));
+ }
+ params.close_button_alt_text =
+ l10n_util::GetStringUTF16(IDS_CLOSE_TUTORIAL);
+
+ std::unique_ptr<HelpBubble> bubble =
+ tutorial_service->bubble_factory_registry()->CreateHelpBubble(
+ element, std::move(params));
+ tutorial_service->SetCurrentBubble(std::move(bubble));
+ },
+ base::Unretained(tutorial_service), title_text, body_text, step_.arrow,
+ progress, is_last_step, can_be_restarted);
+}
+
+ui::InteractionSequence::StepEndCallback
+Tutorial::StepBuilder::BuildHideBubbleCallback(
+ TutorialService* tutorial_service) {
+ return base::BindOnce(
+ [](TutorialService* tutorial_service, ui::TrackedElement* element) {},
+ base::Unretained(tutorial_service));
+}
+
+Tutorial::Builder::Builder()
+ : builder_(std::make_unique<ui::InteractionSequence::Builder>()) {}
+Tutorial::Builder::~Builder() = default;
+
+// static
+std::unique_ptr<Tutorial> Tutorial::Builder::BuildFromDescription(
+ const TutorialDescription& description,
+ TutorialService* tutorial_service,
+ ui::ElementContext context) {
+ Tutorial::Builder builder;
+ builder.SetContext(context);
+
+ // Last step doesn't have a progress counter.
+ const int max_progress =
+ std::count_if(description.steps.begin(), description.steps.end(),
+ [](const auto& step) { return step.ShouldShowBubble(); }) -
+ 1;
+
+ int current_step = 0;
+ for (const auto& step : description.steps) {
+ const bool is_last_step = &step == &description.steps.back();
+ if (!is_last_step && step.ShouldShowBubble())
+ ++current_step;
+ const auto progress =
+ !is_last_step && max_progress > 0
+ ? absl::make_optional(std::make_pair(current_step, max_progress))
+ : absl::nullopt;
+ builder.AddStep(Tutorial::StepBuilder::BuildFromDescriptionStep(
+ step, progress, is_last_step, description.can_be_restarted,
+ tutorial_service));
+ }
+ DCHECK_EQ(current_step, max_progress);
+
+ // Note that the step number we are using here is not the same as the the
+ // InteractionSequence::AbortCallback step (`sequence_step`) which counts all
+ // steps; `current_step` in this case is the visual bubble count, which does
+ // not count hidden steps.
+ builder.SetAbortedCallback(base::BindOnce(
+ [](int step_number, TutorialService* tutorial_service, int sequence_step,
+ ui::TrackedElement* last_element, ui::ElementIdentifier last_id,
+ ui::InteractionSequence::StepType last_step_type,
+ ui::InteractionSequence::AbortedReason aborted_reason) {
+ tutorial_service->AbortTutorial(step_number);
+ },
+ current_step, tutorial_service));
+
+ return builder.Build();
+}
+
+Tutorial::Builder& Tutorial::Builder::AddStep(
+ std::unique_ptr<ui::InteractionSequence::Step> step) {
+ builder_->AddStep(std::move(step));
+ return *this;
+}
+
+Tutorial::Builder& Tutorial::Builder::SetAbortedCallback(
+ ui::InteractionSequence::AbortedCallback callback) {
+ builder_->SetAbortedCallback(std::move(callback));
+ return *this;
+}
+
+Tutorial::Builder& Tutorial::Builder::SetCompletedCallback(
+ ui::InteractionSequence::CompletedCallback callback) {
+ builder_->SetCompletedCallback(std::move(callback));
+ return *this;
+}
+
+Tutorial::Builder& Tutorial::Builder::SetContext(
+ ui::ElementContext element_context) {
+ builder_->SetContext(element_context);
+ return *this;
+}
+
+std::unique_ptr<Tutorial> Tutorial::Builder::Build() {
+ return absl::WrapUnique(new Tutorial(builder_->Build()));
+}
+
+Tutorial::Tutorial(
+ std::unique_ptr<ui::InteractionSequence> interaction_sequence)
+ : interaction_sequence_(std::move(interaction_sequence)) {}
+Tutorial::~Tutorial() = default;
+
+void Tutorial::Start() {
+ DCHECK(interaction_sequence_);
+ if (interaction_sequence_)
+ interaction_sequence_->Start();
+}
+
+void Tutorial::Abort() {
+ if (interaction_sequence_)
+ interaction_sequence_.reset();
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/tutorial.h b/chromium/components/user_education/common/tutorial.h
new file mode 100644
index 00000000000..1cedf37e2b9
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial.h
@@ -0,0 +1,154 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_H_
+
+#include "components/user_education/common/help_bubble_factory.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/common/tutorial_description.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/interaction/interaction_sequence.h"
+
+namespace user_education {
+
+class TutorialService;
+
+// Tutorials are a user initiated IPH which spans 1 or more Interactions.
+// It utilizes the InteractionSequence Framework to provide a tracked list of
+// interactions with tracked elements.
+//
+// Each tutorial consists of a list of InteractionSequence steps which, in the
+// default case, create a HelpBubble which is implementation specific to
+// the platform the tutorial is written for. It is possible to create custom
+// InteractionSequenceSteps when using the traditional constructor and not
+// using the TutorialStepBuilder.
+//
+// Because of implementation details in InteractionSequence, a tutorial can only
+// be run once, see documentation for InteractionSequence.
+//
+// Basic tutorials use a TutorialDescription struct and the
+// Builder::BuildFromDescription method to construct the tutorial.
+//
+// the end user of a Tutorial would define a tutorial description in a
+// TutorialRegistry, for the platform the tutorial is implemented on. (see
+// BrowserTutorialServiceFactory)
+//
+// TODO: Provide an in-depth readme.md for tutorials
+//
+class Tutorial {
+ public:
+ ~Tutorial();
+
+ // Step Builder provides an interface for constructing an
+ // InteractionSequence::Step from a TutorialDescription::Step.
+ // TutorialDescription is used as the basis for the StepBuilder since all
+ // parameters of the Description will be needed to create the bubble or build
+ // the interaction sequence step. In order to use the The StepBuilder should
+ // only be used by Tutorial::Builder to construct the steps in the tutorial.
+ class StepBuilder {
+ public:
+ StepBuilder();
+ explicit StepBuilder(const TutorialDescription::Step& step);
+ StepBuilder(const StepBuilder&) = delete;
+ StepBuilder& operator=(const StepBuilder&) = delete;
+ ~StepBuilder();
+
+ // Constructs the InteractionSequenceStepDirectly from the
+ // TutorialDescriptionStep. This method is used by
+ // Tutorial::Builder::BuildFromDescription to create tutorials.
+ static std::unique_ptr<ui::InteractionSequence::Step>
+ BuildFromDescriptionStep(const TutorialDescription::Step& step,
+ absl::optional<std::pair<int, int>> progress,
+ bool is_last_step,
+ bool can_be_restarted,
+ TutorialService* tutorial_service);
+
+ StepBuilder& SetAnchorElementID(ui::ElementIdentifier anchor_element_id);
+ StepBuilder& SetAnchorElementName(std::string anchor_element_name);
+ StepBuilder& SetTitleTextID(int title_text_id);
+ StepBuilder& SetBodyTextID(int body_text_id);
+ // Sets the step type; `event_type_` should be set only for custom events.
+ StepBuilder& SetStepType(
+ ui::InteractionSequence::StepType step_type_,
+ ui::CustomElementEventType event_type_ = ui::CustomElementEventType());
+ StepBuilder& SetArrow(HelpBubbleArrow arrow_);
+ StepBuilder& SetProgress(absl::optional<std::pair<int, int>> progress_);
+ StepBuilder& SetIsLastStep(bool is_last_step_);
+ StepBuilder& SetMustRemainVisible(bool must_remain_visible_);
+ StepBuilder& SetTransitionOnlyOnEvent(bool transition_only_on_event_);
+ StepBuilder& SetNameElementsCallback(
+ TutorialDescription::NameElementsCallback name_elements_callback_);
+ StepBuilder& SetCanBeRestarted(bool can_be_restarted_);
+
+ std::unique_ptr<ui::InteractionSequence::Step> Build(
+ TutorialService* tutorial_service);
+
+ private:
+ absl::optional<std::pair<int, int>> progress;
+ bool is_last_step = false;
+ bool can_be_restarted = false;
+
+ ui::InteractionSequence::StepStartCallback BuildStartCallback(
+ TutorialService* tutorial_service);
+
+ ui::InteractionSequence::StepStartCallback BuildMaybeShowBubbleCallback(
+ TutorialService* tutorial_service);
+
+ ui::InteractionSequence::StepEndCallback BuildHideBubbleCallback(
+ TutorialService* tutorial_service);
+ TutorialDescription::Step step_;
+ };
+
+ class Builder {
+ public:
+ Builder();
+ ~Builder();
+
+ static std::unique_ptr<Tutorial> BuildFromDescription(
+ const TutorialDescription& description,
+ TutorialService* tutorial_service,
+ ui::ElementContext context);
+
+ Builder(const Builder& other) = delete;
+ void operator=(Builder& other) = delete;
+
+ Builder& AddStep(std::unique_ptr<ui::InteractionSequence::Step> step);
+ Builder& SetContext(ui::ElementContext element_context);
+
+ Builder& SetAbortedCallback(
+ ui::InteractionSequence::AbortedCallback callback);
+ Builder& SetCompletedCallback(
+ ui::InteractionSequence::CompletedCallback callback);
+
+ std::unique_ptr<Tutorial> Build();
+
+ private:
+ std::unique_ptr<ui::InteractionSequence::Builder> builder_;
+ };
+
+ // Starts the Tutorial. has the same constraints as
+ // InteractionSequence::Start.
+ void Start();
+
+ // Cancels the Tutorial. Calls the destructor of the InteractionSequence,
+ // calling the abort callback if necessary.
+ void Abort();
+
+ private:
+ // Tutorial Constructor that takes an InteractionSequence. Should only be
+ // used in cases where custom step logic must be called
+ explicit Tutorial(
+ std::unique_ptr<ui::InteractionSequence> interaction_sequence);
+
+ // The Interaction Sequence which controls the tutorial bubbles opening and
+ // closing
+ std::unique_ptr<ui::InteractionSequence> interaction_sequence_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_H_
diff --git a/chromium/components/user_education/common/tutorial_description.cc b/chromium/components/user_education/common/tutorial_description.cc
new file mode 100644
index 00000000000..62dd8643763
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial_description.cc
@@ -0,0 +1,60 @@
+// Copyright 2021 The Chromium 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/user_education/common/tutorial_description.h"
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/interaction_sequence.h"
+
+namespace user_education {
+
+TutorialDescription::TutorialDescription() = default;
+TutorialDescription::~TutorialDescription() = default;
+TutorialDescription::TutorialDescription(TutorialDescription&&) = default;
+TutorialDescription& TutorialDescription::operator=(TutorialDescription&&) =
+ default;
+
+TutorialDescription::Step::Step()
+ : step_type(ui::InteractionSequence::StepType::kShown),
+ arrow(HelpBubbleArrow::kNone) {}
+TutorialDescription::Step::~Step() = default;
+
+TutorialDescription::Step::Step(
+ int title_text_id_,
+ int body_text_id_,
+ ui::InteractionSequence::StepType step_type_,
+ ui::ElementIdentifier element_id_,
+ std::string element_name_,
+ HelpBubbleArrow arrow_,
+ ui::CustomElementEventType event_type_,
+ absl::optional<bool> must_remain_visible_,
+ bool transition_only_on_event_,
+ TutorialDescription::NameElementsCallback name_elements_callback_)
+ : title_text_id(title_text_id_),
+ body_text_id(body_text_id_),
+ step_type(step_type_),
+ event_type(event_type_),
+ element_id(element_id_),
+ element_name(element_name_),
+ arrow(arrow_),
+ must_remain_visible(must_remain_visible_),
+ transition_only_on_event(transition_only_on_event_),
+ name_elements_callback(name_elements_callback_) {
+ DCHECK(!title_text_id || body_text_id)
+ << "Tutorial bubble should not have a title without body text.";
+}
+
+TutorialDescription::Step::Step(const TutorialDescription::Step&) = default;
+TutorialDescription::Step& TutorialDescription::Step::operator=(
+ const TutorialDescription::Step&) = default;
+
+bool TutorialDescription::Step::Step::ShouldShowBubble() const {
+ // Hide steps and steps with no body text are "hidden" steps.
+
+ return body_text_id &&
+ step_type != ui::InteractionSequence::StepType::kHidden;
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/tutorial_description.h b/chromium/components/user_education/common/tutorial_description.h
new file mode 100644
index 00000000000..bbecc1aa54b
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial_description.h
@@ -0,0 +1,197 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_DESCRIPTION_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_DESCRIPTION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/metrics/histogram_macros.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/interaction/interaction_sequence.h"
+
+namespace user_education {
+
+// Holds the data required to properly store histograms for a given tutorial.
+// Abstract base class because best practice is to statically declare
+// histograms and so we need some compile-time polymorphism to actually
+// implement the RecordXXX() calls.
+//
+// Use MakeTutorialHistograms() below to create a concrete instance of this
+// class.
+class TutorialHistograms {
+ public:
+ TutorialHistograms() = default;
+ TutorialHistograms(const TutorialHistograms& other) = delete;
+ virtual ~TutorialHistograms() = default;
+ void operator=(const TutorialHistograms& other) = delete;
+
+ // Records whether the tutorial was completed or not.
+ virtual void RecordComplete(bool value) = 0;
+
+ // Records the step on which the tutorial was aborted.
+ virtual void RecordAbortStep(int step) = 0;
+
+ // Records whether, when an IPH offered the tutorial, the user opted into
+ // seeing the tutorial or not.
+ virtual void RecordIphLinkClicked(bool value) = 0;
+};
+
+namespace internal {
+
+constexpr char kTutorialHistogramPrefix[] = "Tutorial.";
+
+template <const char histogram_name[]>
+class TutorialHistogramsImpl : public TutorialHistograms {
+ public:
+ explicit TutorialHistogramsImpl(int max_steps)
+ : histogram_name_(histogram_name),
+ completed_name_(kTutorialHistogramPrefix + histogram_name_ +
+ ".Completion"),
+ aborted_name_(kTutorialHistogramPrefix + histogram_name_ +
+ ".AbortStep"),
+ link_clicked_name_(kTutorialHistogramPrefix + histogram_name_ +
+ ".IPHLinkClicked"),
+ max_steps_(max_steps) {}
+ ~TutorialHistogramsImpl() override = default;
+
+ protected:
+ void RecordComplete(bool value) override {
+ UMA_HISTOGRAM_BOOLEAN(completed_name_, value);
+ }
+
+ void RecordAbortStep(int step) override {
+ UMA_HISTOGRAM_EXACT_LINEAR(aborted_name_, step, max_steps_);
+ }
+
+ void RecordIphLinkClicked(bool value) override {
+ UMA_HISTOGRAM_BOOLEAN(link_clicked_name_, value);
+ }
+
+ private:
+ const std::string histogram_name_;
+ const std::string completed_name_;
+ const std::string aborted_name_;
+ const std::string link_clicked_name_;
+ const int max_steps_;
+};
+
+} // namespace internal
+
+// Call to create a tutorial-specific histograms object for use with the
+// tutorial. The template parameter should be a reference to a const char[]
+// that is a compile-time constant. Also remember to add a matching entry to
+// the "TutorialID" variant in histograms.xml corresponding to your tutorial.
+//
+// Example:
+// const char kMyTutorialName[] = "MyTutorial";
+// tutorial_descriptions.histograms =
+// MakeTutorialHistograms<kMyTutorialName>(
+// tutorial_description.steps.size());
+template <const char* histogram_name>
+std::unique_ptr<TutorialHistograms> MakeTutorialHistograms(int max_steps) {
+ return std::make_unique<internal::TutorialHistogramsImpl<histogram_name>>(
+ max_steps);
+}
+
+// A Struct that provides all of the data necessary to construct a Tutorial.
+// A Tutorial Description is a list of Steps for a tutorial. Each step has info
+// for constructing the InteractionSequence::Step from the
+// TutorialDescription::Step.
+struct TutorialDescription {
+ using NameElementsCallback =
+ base::RepeatingCallback<bool(ui::InteractionSequence*,
+ ui::TrackedElement*)>;
+
+ TutorialDescription();
+ ~TutorialDescription();
+ TutorialDescription(TutorialDescription&& other);
+ TutorialDescription& operator=(TutorialDescription&& other);
+
+ struct Step {
+ Step();
+ Step(int title_text_id_,
+ int body_text_id_,
+ ui::InteractionSequence::StepType step_type_,
+ ui::ElementIdentifier element_id_,
+ std::string element_name_,
+ HelpBubbleArrow arrow_,
+ ui::CustomElementEventType event_type_ = ui::CustomElementEventType(),
+ absl::optional<bool> must_remain_visible_ = absl::nullopt,
+ bool transition_only_on_event_ = false,
+ NameElementsCallback name_elements_callback_ = NameElementsCallback());
+ Step(const Step& other);
+ Step& operator=(const Step& other);
+ ~Step();
+
+ // The title text to be populated in the bubble.
+ int title_text_id = 0;
+
+ // The body text to be populated in the bubble.
+ int body_text_id = 0;
+
+ // The step type for InteractionSequence::Step.
+ ui::InteractionSequence::StepType step_type;
+
+ // The event type for the step if `step_type` is kCustomEvent.
+ ui::CustomElementEventType event_type;
+
+ // The element used by interaction sequence to observe and attach a bubble.
+ ui::ElementIdentifier element_id;
+
+ // The element, referred to by name, used by the interaction sequence
+ // to observe and potentially attach a bubble. must be non-empty.
+ std::string element_name;
+
+ // The positioning of the bubble arrow.
+ HelpBubbleArrow arrow = HelpBubbleArrow::kTopRight;
+
+ // Should the element remain visible through the entire step, this should be
+ // set to false for hidden steps and for shown steps that precede hidden
+ // steps on the same element. if left empty the interaction sequence will
+ // decide what its value should be based on the generated
+ // InteractionSequence::StepBuilder
+ absl::optional<bool> must_remain_visible;
+
+ // Should the step only be completed when an event like shown or hidden only
+ // happens during current step. for more information on the implementation
+ // take a look at transition_only_on_event in InteractionSequence::Step
+ bool transition_only_on_event = false;
+
+ // lambda which is called on the start callback of the InteractionSequence
+ // which provides the interaction sequence and the current element that
+ // belongs to the step. The intention for this functionality is to name one
+ // or many elements using the Framework's Specific API finding an element
+ // and naming it OR using the current element from the sequence as the
+ // element for naming. The return value is a boolean which controls whether
+ // the Interaction Sequence should continue or not. If false is returned
+ // the tutorial will abort
+ NameElementsCallback name_elements_callback;
+
+ // returns true iff all of the required parameters exist to display a
+ // bubble.
+ bool ShouldShowBubble() const;
+ };
+
+ // the list of TutorialDescription steps
+ std::vector<Step> steps;
+
+ // The histogram data to use. Use MakeTutorialHistograms() above to create a
+ // value to use, if you want to record specific histograms for this tutorial.
+ std::unique_ptr<TutorialHistograms> histograms;
+
+ // The ability for the tutorial to be restarted. In some cases tutorials can
+ // leave the UI in a state where it can not re-run the tutorial. In these
+ // cases this flag should be set to false so that the restart tutorial button
+ // is not displayed.
+ bool can_be_restarted = false;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_DESCRIPTION_H_
diff --git a/chromium/components/user_education/common/tutorial_identifier.h b/chromium/components/user_education/common/tutorial_identifier.h
new file mode 100644
index 00000000000..240baa46e92
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial_identifier.h
@@ -0,0 +1,15 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_IDENTIFIER_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_IDENTIFIER_H_
+
+namespace user_education {
+
+// TutorialIdentifier is used as a handle for Tutorials.
+using TutorialIdentifier = std::string;
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_IDENTIFIER_H_
diff --git a/chromium/components/user_education/common/tutorial_registry.cc b/chromium/components/user_education/common/tutorial_registry.cc
new file mode 100644
index 00000000000..dd38f13acb4
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial_registry.cc
@@ -0,0 +1,52 @@
+// Copyright 2021 The Chromium 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/user_education/common/tutorial_registry.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/containers/contains.h"
+#include "components/user_education/common/tutorial.h"
+#include "components/user_education/common/tutorial_description.h"
+#include "components/user_education/common/tutorial_identifier.h"
+
+namespace user_education {
+
+TutorialRegistry::TutorialRegistry() = default;
+TutorialRegistry::~TutorialRegistry() = default;
+
+bool TutorialRegistry::IsTutorialRegistered(TutorialIdentifier id) const {
+ return base::Contains(tutorial_registry_, id);
+}
+
+TutorialDescription* TutorialRegistry::GetTutorialDescription(
+ TutorialIdentifier id) {
+ DCHECK(tutorial_registry_.size() > 0);
+ auto pair = tutorial_registry_.find(id);
+ if (pair == tutorial_registry_.end())
+ return nullptr;
+ return &pair->second;
+}
+
+const std::vector<TutorialIdentifier>
+TutorialRegistry::GetTutorialIdentifiers() {
+ DCHECK(tutorial_registry_.size() > 0);
+ std::vector<TutorialIdentifier> id_strings;
+ std::transform(tutorial_registry_.begin(), tutorial_registry_.end(),
+ std::back_inserter(id_strings),
+ [](const auto& pair) { return pair.first; });
+ return id_strings;
+}
+
+void TutorialRegistry::AddTutorial(TutorialIdentifier id,
+ TutorialDescription description) {
+ tutorial_registry_.emplace(id, std::move(description));
+}
+
+void TutorialRegistry::RemoveTutorialForTesting(TutorialIdentifier id) {
+ tutorial_registry_.erase(id);
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/tutorial_registry.h b/chromium/components/user_education/common/tutorial_registry.h
new file mode 100644
index 00000000000..0e357a560b0
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial_registry.h
@@ -0,0 +1,51 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_REGISTRY_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_REGISTRY_H_
+
+#include "components/user_education/common/help_bubble_factory_registry.h"
+#include "components/user_education/common/tutorial.h"
+#include "components/user_education/common/tutorial_identifier.h"
+
+namespace user_education {
+
+class Tutorial;
+
+// A TutorialRegistry is a Map of TutorialIdentifier to TutorialDescription.
+// TutorialDescriptions should be added to the registry by subclassing the
+// registry and overriding the RegisterTutorials method.
+class TutorialRegistry {
+ public:
+ TutorialRegistry();
+ ~TutorialRegistry();
+ TutorialRegistry(const TutorialRegistry&) = delete;
+ TutorialRegistry& operator=(const TutorialRegistry&) = delete;
+
+ // Determines whether the given tutorial is registered.
+ bool IsTutorialRegistered(TutorialIdentifier id) const;
+
+ // Returns a list of Tutorial Identifiers if the tutorial registry exists.
+ // If RegisterTutorials has not been called this returns an empty vector.
+ const std::vector<TutorialIdentifier> GetTutorialIdentifiers();
+
+ // Gets the TutorialDescription from the registry. Returns nullptr if
+ // there is no registered tutorial under the given ID.
+ TutorialDescription* GetTutorialDescription(TutorialIdentifier id);
+
+ // Adds a TutorialID, TutorialDescription pair to the registry. This should
+ // be used by the RegisterTutorials method to Add Tutorials.
+ void AddTutorial(TutorialIdentifier id, TutorialDescription description);
+
+ // Removes a TutorialID and its associated TutorialDescription from the
+ // registry, to clean up from tests that need to inject test-only tutorials.
+ void RemoveTutorialForTesting(TutorialIdentifier id);
+
+ private:
+ std::map<TutorialIdentifier, TutorialDescription> tutorial_registry_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_REGISTRY_H_
diff --git a/chromium/components/user_education/common/tutorial_service.cc b/chromium/components/user_education/common/tutorial_service.cc
new file mode 100644
index 00000000000..50a839e1877
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial_service.cc
@@ -0,0 +1,193 @@
+// Copyright 2021 The Chromium 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/user_education/common/tutorial_service.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "components/user_education/common/help_bubble.h"
+#include "components/user_education/common/help_bubble_factory_registry.h"
+#include "components/user_education/common/tutorial.h"
+#include "components/user_education/common/tutorial_identifier.h"
+#include "components/user_education/common/tutorial_registry.h"
+
+namespace user_education {
+
+TutorialService::TutorialCreationParams::TutorialCreationParams(
+ TutorialDescription* description,
+ ui::ElementContext context)
+ : description_(description), context_(context) {}
+
+TutorialService::TutorialService(
+ TutorialRegistry* tutorial_registry,
+ HelpBubbleFactoryRegistry* help_bubble_factory_registry)
+ : tutorial_registry_(tutorial_registry),
+ help_bubble_factory_registry_(help_bubble_factory_registry) {
+ toggle_focus_subscription_ =
+ help_bubble_factory_registry->AddToggleFocusCallback(
+ base::BindRepeating(&TutorialService::OnFocusToggledForAccessibility,
+ base::Unretained(this)));
+}
+
+TutorialService::~TutorialService() = default;
+
+bool TutorialService::StartTutorial(TutorialIdentifier id,
+ ui::ElementContext context,
+ CompletedCallback completed_callback,
+ AbortedCallback aborted_callback) {
+ // Overriding an existing running tutorial is not supported. In this case
+ // return false to the caller.
+ if (running_tutorial_)
+ return false;
+
+ // Get the description from the tutorial registry.
+ TutorialDescription* description =
+ tutorial_registry_->GetTutorialDescription(id);
+ CHECK(description);
+
+ // Construct the tutorial from the dsecription.
+ running_tutorial_ =
+ Tutorial::Builder::BuildFromDescription(*description, this, context);
+
+ // Set the external callbacks.
+ completed_callback_ = std::move(completed_callback);
+ aborted_callback_ = std::move(aborted_callback);
+
+ // Save the params for creating the tutorial to be used when restarting.
+ running_tutorial_creation_params_ =
+ std::make_unique<TutorialCreationParams>(description, context);
+
+ // Start the tutorial and mark the params used to created it for restarting.
+ running_tutorial_->Start();
+ toggle_focus_count_ = 0;
+
+ return true;
+}
+
+void TutorialService::LogIPHLinkClicked(TutorialIdentifier id,
+ bool iph_link_was_clicked) {
+ TutorialDescription* description =
+ tutorial_registry_->GetTutorialDescription(id);
+ CHECK(description);
+
+ if (description->histograms)
+ description->histograms->RecordIphLinkClicked(iph_link_was_clicked);
+}
+
+bool TutorialService::RestartTutorial() {
+ DCHECK(running_tutorial_ && running_tutorial_creation_params_);
+ base::AutoReset<bool> resetter(&is_restarting_, true);
+
+ currently_displayed_bubble_.reset();
+
+ running_tutorial_ = Tutorial::Builder::BuildFromDescription(
+ *running_tutorial_creation_params_->description_, this,
+ running_tutorial_creation_params_->context_);
+ if (!running_tutorial_) {
+ ResetRunningTutorial();
+ return false;
+ }
+
+ // Note: if we restart the tutorial, we won't record whether the user pressed
+ // the pane focus key to focus the help bubble until the user actually decides
+ // they're finished, but we also won't reset the count, so at the end we can
+ // record the total.
+ // TODO(dfried): decide if this is actually the correct behavior.
+ running_tutorial_was_restarted_ = true;
+ running_tutorial_->Start();
+
+ return true;
+}
+
+void TutorialService::AbortTutorial(absl::optional<int> abort_step) {
+ // For various reasons, we could get called here while e.g. tearing down the
+ // interaction sequence. We only want to actually run AbortTutorial() or
+ // CompleteTutorial() exactly once, so we won't continue if the tutorial has
+ // already been disposed. We also only want to abort the tutorial if we are
+ // not in the process of restarting. When calling reset on the help bubble,
+ // or when resetting the tutorial, the interaction sequence or callbacks could
+ // call the abort.
+ if (!running_tutorial_ || is_restarting_)
+ return;
+
+ // If the tutorial had been restarted and then aborted, The tutorial should be
+ // considered completed.
+ if (running_tutorial_was_restarted_)
+ return CompleteTutorial();
+
+ if (abort_step.has_value())
+ running_tutorial_creation_params_->description_->histograms
+ ->RecordAbortStep(abort_step.value());
+
+ // Log the failure of completion for the tutorial.
+ if (running_tutorial_creation_params_->description_->histograms)
+ running_tutorial_creation_params_->description_->histograms->RecordComplete(
+ false);
+ UMA_HISTOGRAM_BOOLEAN("Tutorial.Completion", false);
+
+ // Reset the tutorial and call the external abort callback.
+ ResetRunningTutorial();
+
+ // Record how many times the user toggled focus during the tutorial using
+ // the keyboard.
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Tutorial.FocusToggleCount.Aborted",
+ toggle_focus_count_, 0, 50, 6);
+ toggle_focus_count_ = 0;
+
+ if (aborted_callback_) {
+ std::move(aborted_callback_).Run();
+ }
+}
+
+void TutorialService::CompleteTutorial() {
+ DCHECK(running_tutorial_);
+
+ // Log the completion metric based on if the tutorial was restarted or not.
+ if (running_tutorial_creation_params_->description_->histograms)
+ running_tutorial_creation_params_->description_->histograms->RecordComplete(
+ true);
+ UMA_HISTOGRAM_BOOLEAN("Tutorial.Completion", true);
+
+ ResetRunningTutorial();
+
+ // Record how many times the user toggled focus during the tutorial using
+ // the keyboard.
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Tutorial.FocusToggleCount.Completed",
+ toggle_focus_count_, 0, 50, 6);
+ toggle_focus_count_ = 0;
+
+ std::move(completed_callback_).Run();
+}
+
+void TutorialService::SetCurrentBubble(std::unique_ptr<HelpBubble> bubble) {
+ DCHECK(running_tutorial_);
+ currently_displayed_bubble_ = std::move(bubble);
+}
+
+void TutorialService::HideCurrentBubbleIfShowing() {
+ if (currently_displayed_bubble_) {
+ currently_displayed_bubble_.reset();
+ }
+}
+
+bool TutorialService::IsRunningTutorial() const {
+ return running_tutorial_ != nullptr;
+}
+
+void TutorialService::ResetRunningTutorial() {
+ DCHECK(running_tutorial_);
+ running_tutorial_.reset();
+ running_tutorial_creation_params_.reset();
+ running_tutorial_was_restarted_ = false;
+ currently_displayed_bubble_.reset();
+}
+
+void TutorialService::OnFocusToggledForAccessibility(HelpBubble* bubble) {
+ if (bubble == currently_displayed_bubble_.get())
+ ++toggle_focus_count_;
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/tutorial_service.h b/chromium/components/user_education/common/tutorial_service.h
new file mode 100644
index 00000000000..dc0536489ea
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial_service.h
@@ -0,0 +1,143 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_SERVICE_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/callback_helpers.h"
+#include "base/callback_list.h"
+#include "base/memory/raw_ptr.h"
+#include "components/user_education/common/tutorial.h"
+#include "components/user_education/common/tutorial_identifier.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_tracker.h"
+
+// Declare in the global scope for testing purposes.
+class TutorialInteractiveUitest;
+
+namespace user_education {
+
+class HelpBubble;
+class HelpBubbleFactoryRegistry;
+class TutorialRegistry;
+
+// A profile based service which provides the current running tutorial. A
+// TutorialService should be constructed by a factory which fills in the correct
+// tutorials based on the platform the tutorial targets.
+class TutorialService {
+ public:
+ TutorialService(TutorialRegistry* tutorial_registry,
+ HelpBubbleFactoryRegistry* help_bubble_factory_registry);
+ virtual ~TutorialService();
+
+ using CompletedCallback = base::OnceClosure;
+ using AbortedCallback = base::OnceClosure;
+
+ // Returns true if there is a currently running tutorial.
+ bool IsRunningTutorial() const;
+
+ // Sets the current help bubble stored by the service.
+ void SetCurrentBubble(std::unique_ptr<HelpBubble> bubble);
+
+ // Hides the current help bubble currently being shown by the service.
+ void HideCurrentBubbleIfShowing();
+
+ // Starts the tutorial by looking for the id in the Tutorial Registry.
+ bool StartTutorial(TutorialIdentifier id,
+ ui::ElementContext context,
+ CompletedCallback completed_callback = base::DoNothing(),
+ AbortedCallback aborted_callback = base::DoNothing());
+
+ void LogIPHLinkClicked(TutorialIdentifier id, bool iph_link_was_clicked);
+
+ // Uses the stored tutorial creation params to restart a tutorial. Replaces
+ // the current_tutorial with a newly generated tutorial.
+ bool RestartTutorial();
+
+ // Accessors for registries.
+ TutorialRegistry* tutorial_registry() { return tutorial_registry_; }
+ HelpBubbleFactoryRegistry* bubble_factory_registry() {
+ return help_bubble_factory_registry_;
+ }
+
+ // Accessors for the help bubble used in tests.
+ HelpBubble* currently_displayed_bubble() {
+ return currently_displayed_bubble_.get();
+ }
+
+ // Calls the abort code for the running tutorial.
+ void AbortTutorial(absl::optional<int> abort_step);
+
+ // Returns application-specific strings.
+ virtual std::u16string GetBodyIconAltText(bool is_last_step) const = 0;
+
+ private:
+ friend class Tutorial;
+ friend TutorialInteractiveUitest;
+
+ // Struct used to reconstruct a tutorial from the params initially used to
+ // create it.
+ struct TutorialCreationParams {
+ TutorialCreationParams(TutorialDescription* description,
+ ui::ElementContext context);
+
+ raw_ptr<TutorialDescription> description_;
+ ui::ElementContext context_;
+ };
+
+ // Calls the completion code for the running tutorial.
+ // TODO (dpenning): allow for registering a callback that performs any
+ // IPH/other code on completion of tutorial
+ void CompleteTutorial();
+
+ // Reset all of the running tutorial member variables.
+ void ResetRunningTutorial();
+
+ // Tracks when the user toggles focus to a help bubble via the keyboard.
+ void OnFocusToggledForAccessibility(HelpBubble* bubble);
+
+ // Creation params for the last started tutorial. Used to restart the
+ // tutorial after it has been completed.
+ std::unique_ptr<TutorialCreationParams> running_tutorial_creation_params_;
+
+ // The current tutorial created by the start or restart methods. This
+ // tutorial is not required to have it's interaction sequence started to
+ // be stored in the service.
+ std::unique_ptr<Tutorial> running_tutorial_;
+
+ // Was restarted denotes that the current running tutorial was restarted,
+ // and when logging that the tutorial aborts, instead should log as completed.
+ bool running_tutorial_was_restarted_ = false;
+
+ // Called when the current tutorial is completed.
+ CompletedCallback completed_callback_ = base::DoNothing();
+
+ // Called if the current tutorial is aborted.
+ AbortedCallback aborted_callback_ = base::DoNothing();
+
+ // The current help bubble displayed by the tutorial. This is owned by the
+ // service so that when the tutorial exits, the bubble can continue existing.
+ std::unique_ptr<HelpBubble> currently_displayed_bubble_;
+
+ // Pointers to the registries used for constructing and showing tutorials and
+ // help bubbles.
+ const raw_ptr<TutorialRegistry> tutorial_registry_;
+ const raw_ptr<HelpBubbleFactoryRegistry> help_bubble_factory_registry_;
+
+ // Number of times focus was toggled during the current tutorial.
+ int toggle_focus_count_ = 0;
+ base::CallbackListSubscription toggle_focus_subscription_;
+
+ // status bit to denote that the tutorial service is in the process of
+ // restarting a tutorial. This prevents calling the abort callbacks.
+ bool is_restarting_ = false;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_SERVICE_H_
diff --git a/chromium/components/user_education/common/tutorial_unittest.cc b/chromium/components/user_education/common/tutorial_unittest.cc
new file mode 100644
index 00000000000..9099c57a5ff
--- /dev/null
+++ b/chromium/components/user_education/common/tutorial_unittest.cc
@@ -0,0 +1,365 @@
+// Copyright 2021 The Chromium 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/user_education/common/tutorial.h"
+
+#include <string>
+
+#include "base/test/bind.h"
+#include "base/test/mock_callback.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_education/common/help_bubble_factory_registry.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/common/tutorial_description.h"
+#include "components/user_education/common/tutorial_identifier.h"
+#include "components/user_education/common/tutorial_registry.h"
+#include "components/user_education/common/tutorial_service.h"
+#include "components/user_education/test/test_help_bubble.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_test_util.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/interaction/expect_call_in_scope.h"
+#include "ui/base/interaction/interaction_sequence.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace user_education {
+
+namespace {
+
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestIdentifier1);
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestIdentifier2);
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestIdentifier3);
+DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kCustomEventType1);
+
+const char kTestElementName1[] = "ELEMENT_NAME_1";
+
+const ui::ElementContext kTestContext1(1);
+
+class TestTutorialService : public TutorialService {
+ public:
+ TestTutorialService(TutorialRegistry* tutorial_registry,
+ HelpBubbleFactoryRegistry* help_bubble_factory_registry)
+ : TutorialService(tutorial_registry, help_bubble_factory_registry) {}
+ ~TestTutorialService() override = default;
+
+ std::u16string GetBodyIconAltText(bool is_last_step) const override {
+ return std::u16string();
+ }
+};
+
+std::unique_ptr<HelpBubbleFactoryRegistry>
+CreateTestTutorialBubbleFactoryRegistry() {
+ auto bubble_factory_registry = std::make_unique<HelpBubbleFactoryRegistry>();
+ bubble_factory_registry->MaybeRegister<test::TestHelpBubbleFactory>();
+ return bubble_factory_registry;
+}
+
+void ClickDismissButton(HelpBubble* bubble) {
+ auto* const help_bubble = static_cast<test::TestHelpBubble*>(bubble);
+ help_bubble->SimulateDismiss();
+}
+
+void ClickCloseButton(HelpBubble* bubble) {
+ auto* const help_bubble = static_cast<test::TestHelpBubble*>(bubble);
+ int button_index = help_bubble->GetIndexOfButtonWithText(
+ l10n_util::GetStringUTF16(IDS_TUTORIAL_CLOSE_TUTORIAL));
+ EXPECT_TRUE(button_index != test::TestHelpBubble::kNoButtonWithTextIndex);
+ help_bubble->SimulateButtonPress(button_index);
+}
+
+void ClickRestartButton(HelpBubble* bubble) {
+ auto* const help_bubble = static_cast<test::TestHelpBubble*>(bubble);
+ int button_index = help_bubble->GetIndexOfButtonWithText(
+ l10n_util::GetStringUTF16(IDS_TUTORIAL_RESTART_TUTORIAL));
+
+ EXPECT_TRUE(button_index != test::TestHelpBubble::kNoButtonWithTextIndex);
+ help_bubble->SimulateButtonPress(button_index);
+}
+
+const TutorialIdentifier kTestTutorial1{"kTestTutorial1"};
+} // namespace
+
+class TutorialTest : public testing::Test {};
+
+TEST_F(TutorialTest, TutorialBuilder) {
+ const auto bubble_factory_registry =
+ CreateTestTutorialBubbleFactoryRegistry();
+ TutorialRegistry registry;
+ TestTutorialService service(&registry, bubble_factory_registry.get());
+
+ Tutorial::Builder builder;
+
+ // build a step with an ElementID
+ std::unique_ptr<ui::InteractionSequence::Step> step1 =
+ Tutorial::StepBuilder()
+ .SetAnchorElementID(kTestIdentifier1)
+ .Build(&service);
+
+ // build a step that names an element
+ std::unique_ptr<ui::InteractionSequence::Step> step2 =
+ Tutorial::StepBuilder()
+ .SetAnchorElementID(kTestIdentifier1)
+ .SetNameElementsCallback(
+ base::BindRepeating([](ui::InteractionSequence* sequence,
+ ui::TrackedElement* element) {
+ sequence->NameElement(element, "TEST ELEMENT");
+ return true;
+ }))
+ .Build(&service);
+
+ // build a step with a named element
+ std::unique_ptr<ui::InteractionSequence::Step> step3 =
+ Tutorial::StepBuilder()
+ .SetAnchorElementName(std::string(kTestElementName1))
+ .Build(&service);
+
+ // transition event
+ std::unique_ptr<ui::InteractionSequence::Step> step4 =
+ Tutorial::StepBuilder()
+ .SetAnchorElementID(kTestIdentifier1)
+ .SetTransitionOnlyOnEvent(true)
+ .Build(&service);
+
+ builder.SetContext(kTestContext1)
+ .AddStep(std::move(step1))
+ .AddStep(std::move(step2))
+ .AddStep(std::move(step3))
+ .AddStep(std::move(step4))
+ .Build();
+}
+
+TEST_F(TutorialTest, TutorialRegistryRegistersTutorials) {
+ std::unique_ptr<TutorialRegistry> registry =
+ std::make_unique<TutorialRegistry>();
+
+ {
+ TutorialDescription description;
+ description.steps.emplace_back(TutorialDescription::Step(
+ 0, IDS_OK, ui::InteractionSequence::StepType::kShown, kTestIdentifier1,
+ std::string(), HelpBubbleArrow::kNone));
+ description.can_be_restarted = true;
+ registry->AddTutorial(kTestTutorial1, std::move(description));
+ }
+
+ std::unique_ptr<HelpBubbleFactoryRegistry> bubble_factory_registry =
+ std::make_unique<HelpBubbleFactoryRegistry>();
+
+ registry->GetTutorialIdentifiers();
+}
+
+TEST_F(TutorialTest, SingleInteractionTutorialRuns) {
+ UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed);
+
+ const auto bubble_factory_registry =
+ CreateTestTutorialBubbleFactoryRegistry();
+ TutorialRegistry registry;
+ TestTutorialService service(&registry, bubble_factory_registry.get());
+
+ // build elements and keep them for triggering show/hide
+ ui::test::TestElement element_1(kTestIdentifier1, kTestContext1);
+ element_1.Show();
+
+ // Build the tutorial Description
+ TutorialDescription description;
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ kTestIdentifier1, "", HelpBubbleArrow::kNone));
+ registry.AddTutorial(kTestTutorial1, std::move(description));
+
+ service.StartTutorial(kTestTutorial1, element_1.context(), completed.Get());
+
+ EXPECT_TRUE(service.currently_displayed_bubble());
+ EXPECT_CALL_IN_SCOPE(completed, Run,
+ ClickCloseButton(service.currently_displayed_bubble()));
+}
+
+TEST_F(TutorialTest, TutorialWithCustomEvent) {
+ UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed);
+
+ const auto bubble_factory_registry =
+ CreateTestTutorialBubbleFactoryRegistry();
+ TutorialRegistry registry;
+ TestTutorialService service(&registry, bubble_factory_registry.get());
+
+ // build elements and keep them for triggering show/hide
+ ui::test::TestElement element_1(kTestIdentifier1, kTestContext1);
+ element_1.Show();
+
+ // Build the tutorial Description
+ TutorialDescription description;
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kCustomEvent,
+ kTestIdentifier1, "", HelpBubbleArrow::kNone, kCustomEventType1));
+ registry.AddTutorial(kTestTutorial1, std::move(description));
+
+ service.StartTutorial(kTestTutorial1, element_1.context(), completed.Get());
+ ui::ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
+ &element_1, kCustomEventType1);
+
+ EXPECT_CALL_IN_SCOPE(completed, Run,
+ ClickCloseButton(service.currently_displayed_bubble()));
+}
+
+TEST_F(TutorialTest, TutorialWithNamedElement) {
+ UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed);
+ static constexpr char kElementName[] = "Element Name";
+
+ const auto bubble_factory_registry =
+ CreateTestTutorialBubbleFactoryRegistry();
+ TutorialRegistry registry;
+ TestTutorialService service(&registry, bubble_factory_registry.get());
+
+ // build elements and keep them for triggering show/hide
+ ui::test::TestElement element_1(kTestIdentifier1, kTestContext1);
+ element_1.Show();
+
+ // Build the tutorial Description
+ TutorialDescription description;
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ kTestIdentifier1, std::string(), HelpBubbleArrow::kNone,
+ ui::CustomElementEventType(),
+ /* must_remain_visible =*/true,
+ /* transition_only_on_event =*/false,
+ base::BindLambdaForTesting(
+ [](ui::InteractionSequence* sequence, ui::TrackedElement* element) {
+ sequence->NameElement(element, base::StringPiece(kElementName));
+ return true;
+ })));
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ ui::ElementIdentifier(), kElementName, HelpBubbleArrow::kNone));
+ registry.AddTutorial(kTestTutorial1, std::move(description));
+
+ service.StartTutorial(kTestTutorial1, element_1.context(), completed.Get());
+
+ EXPECT_CALL_IN_SCOPE(completed, Run,
+ ClickCloseButton(service.currently_displayed_bubble()));
+}
+
+TEST_F(TutorialTest, SingleStepRestartTutorial) {
+ UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed);
+
+ const auto bubble_factory_registry =
+ CreateTestTutorialBubbleFactoryRegistry();
+ TutorialRegistry registry;
+ TestTutorialService service(&registry, bubble_factory_registry.get());
+
+ // build elements and keep them for triggering show/hide
+ ui::test::TestElement element_1(kTestIdentifier1, kTestContext1);
+ element_1.Show();
+
+ // Build the tutorial Description
+ TutorialDescription description;
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ kTestIdentifier1, "", HelpBubbleArrow::kNone));
+ description.can_be_restarted = true;
+ registry.AddTutorial(kTestTutorial1, std::move(description));
+
+ service.StartTutorial(kTestTutorial1, element_1.context(), completed.Get());
+
+ ClickRestartButton(service.currently_displayed_bubble());
+
+ EXPECT_CALL_IN_SCOPE(completed, Run,
+ ClickCloseButton(service.currently_displayed_bubble()));
+}
+
+// Starts a tutorial with 3 steps, completes steps, then clicks restart tutorial
+// then completes the tutorial again and closes it from the close button.
+// Expects to call the completed callback.
+TEST_F(TutorialTest, MultiStepRestartTutorialWithCloseOnComplete) {
+ UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed);
+
+ const auto bubble_factory_registry =
+ CreateTestTutorialBubbleFactoryRegistry();
+ TutorialRegistry registry;
+ TestTutorialService service(&registry, bubble_factory_registry.get());
+
+ // build elements and keep them for triggering show/hide
+ ui::test::TestElement element_1(kTestIdentifier1, kTestContext1);
+ ui::test::TestElement element_2(kTestIdentifier2, kTestContext1);
+ ui::test::TestElement element_3(kTestIdentifier3, kTestContext1);
+
+ element_1.Show();
+
+ // Build the tutorial Description
+ TutorialDescription description;
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ kTestIdentifier1, "", HelpBubbleArrow::kNone));
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ kTestIdentifier2, "", HelpBubbleArrow::kNone));
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ kTestIdentifier3, "", HelpBubbleArrow::kNone));
+ description.can_be_restarted = true;
+ registry.AddTutorial(kTestTutorial1, std::move(description));
+
+ service.StartTutorial(kTestTutorial1, element_1.context(), completed.Get());
+ element_2.Show();
+ element_3.Show();
+
+ element_2.Hide();
+
+ ClickRestartButton(service.currently_displayed_bubble());
+
+ EXPECT_TRUE(service.IsRunningTutorial());
+ element_2.Show();
+
+ EXPECT_CALL_IN_SCOPE(completed, Run,
+ ClickCloseButton(service.currently_displayed_bubble()));
+}
+
+// Starts a tutorial with 3 steps, completes steps, then clicks restart tutorial
+// then closes the tutorial on the first step. Expects to call the completed
+// callback.
+TEST_F(TutorialTest, MultiStepRestartTutorialWithDismissAfterRestart) {
+ UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed);
+
+ const auto bubble_factory_registry =
+ CreateTestTutorialBubbleFactoryRegistry();
+ TutorialRegistry registry;
+ TestTutorialService service(&registry, bubble_factory_registry.get());
+
+ // build elements and keep them for triggering show/hide
+ ui::test::TestElement element_1(kTestIdentifier1, kTestContext1);
+ ui::test::TestElement element_2(kTestIdentifier2, kTestContext1);
+ ui::test::TestElement element_3(kTestIdentifier3, kTestContext1);
+
+ element_1.Show();
+
+ // Build the tutorial Description
+ TutorialDescription description;
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ kTestIdentifier1, "", HelpBubbleArrow::kNone));
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ kTestIdentifier2, "", HelpBubbleArrow::kNone));
+ description.steps.emplace_back(TutorialDescription::Step(
+ IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown,
+ kTestIdentifier3, "", HelpBubbleArrow::kNone));
+ description.can_be_restarted = true;
+ registry.AddTutorial(kTestTutorial1, std::move(description));
+
+ service.StartTutorial(kTestTutorial1, element_1.context(), completed.Get());
+ element_2.Show();
+ element_3.Show();
+
+ element_2.Hide();
+
+ ClickRestartButton(service.currently_displayed_bubble());
+
+ EXPECT_TRUE(service.IsRunningTutorial());
+ EXPECT_TRUE(service.currently_displayed_bubble() != nullptr);
+
+ EXPECT_CALL_IN_SCOPE(
+ completed, Run, ClickDismissButton(service.currently_displayed_bubble()));
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/user_education_class_properties.cc b/chromium/components/user_education/common/user_education_class_properties.cc
new file mode 100644
index 00000000000..3874871c12a
--- /dev/null
+++ b/chromium/components/user_education/common/user_education_class_properties.cc
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/user_education/common/user_education_class_properties.h"
+
+namespace user_education {
+
+DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHasInProductHelpPromoKey, false)
+
+} // namespace user_education
diff --git a/chromium/components/user_education/common/user_education_class_properties.h b/chromium/components/user_education/common/user_education_class_properties.h
new file mode 100644
index 00000000000..e02f99b78c5
--- /dev/null
+++ b/chromium/components/user_education/common/user_education_class_properties.h
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_USER_EDUCATION_COMMON_USER_EDUCATION_CLASS_PROPERTIES_H_
+#define COMPONENTS_USER_EDUCATION_COMMON_USER_EDUCATION_CLASS_PROPERTIES_H_
+
+#include "ui/base/class_property.h"
+
+namespace user_education {
+
+// Set to true for a UI elements that support ui::PropertyHandler if a help
+// bubble is showing for that element. The element can respond however is
+// appropriate, e.g. with a highlight or a color change.
+//
+// Individual help bubble [factory] implementations should set this value, as
+// not all UI elements implement ui::PropertyHandler.
+extern const ui::ClassProperty<bool>* const kHasInProductHelpPromoKey;
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_COMMON_USER_EDUCATION_CLASS_PROPERTIES_H_
diff --git a/chromium/components/user_education/test/BUILD.gn b/chromium/components/user_education/test/BUILD.gn
new file mode 100644
index 00000000000..c41a9476d29
--- /dev/null
+++ b/chromium/components/user_education/test/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2022 The Chromium 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/buildflag_header.gni")
+import("//build/config/ui.gni")
+
+source_set("test") {
+ testonly = true
+
+ sources = [
+ "mock_feature_promo_controller.cc",
+ "mock_feature_promo_controller.h",
+ "test_help_bubble.cc",
+ "test_help_bubble.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/feature_engagement/public",
+ "//components/user_education/common",
+ "//skia",
+ "//testing/gmock",
+ "//third_party/abseil-cpp:absl",
+ "//ui/base",
+ "//ui/base:test_support",
+ ]
+}
diff --git a/chromium/components/user_education/views/BUILD.gn b/chromium/components/user_education/views/BUILD.gn
new file mode 100644
index 00000000000..74532a2e023
--- /dev/null
+++ b/chromium/components/user_education/views/BUILD.gn
@@ -0,0 +1,72 @@
+# Copyright 2022 The Chromium 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/buildflag_header.gni")
+import("//build/config/ui.gni")
+
+source_set("views") {
+ sources = [
+ "help_bubble_delegate.h",
+ "help_bubble_factory_views.cc",
+ "help_bubble_factory_views.h",
+ "help_bubble_view.cc",
+ "help_bubble_view.h",
+ "new_badge_label.cc",
+ "new_badge_label.h",
+ ]
+
+ if (is_mac) {
+ sources += [
+ "help_bubble_factory_mac.h",
+ "help_bubble_factory_mac.mm",
+ ]
+ }
+
+ public_deps = [ "//components/user_education/common" ]
+
+ deps = [
+ "//base",
+ "//components/strings",
+ "//components/variations",
+ "//components/vector_icons",
+ "//skia",
+ "//third_party/abseil-cpp:absl",
+ "//ui/accessibility",
+ "//ui/base",
+ "//ui/color",
+ "//ui/gfx",
+ "//ui/views",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "help_bubble_view_unittest.cc",
+ "new_badge_label_unittest.cc",
+ ]
+
+ deps = [
+ ":views",
+ "//base",
+ "//base/test:test_support",
+ "//components/feature_engagement/public",
+ "//components/strings",
+ "//components/user_education/common",
+ "//components/user_education/test",
+ "//components/variations",
+ "//skia",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/abseil-cpp:absl",
+ "//ui/accessibility",
+ "//ui/base",
+ "//ui/base:test_support",
+ "//ui/color",
+ "//ui/gfx",
+ "//ui/views",
+ "//ui/views:test_support",
+ ]
+}
diff --git a/chromium/components/user_education/views/DEPS b/chromium/components/user_education/views/DEPS
new file mode 100644
index 00000000000..53a99dbc4e7
--- /dev/null
+++ b/chromium/components/user_education/views/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+ui/views",
+ "+components/variations",
+]
diff --git a/chromium/components/user_education/views/help_bubble_delegate.h b/chromium/components/user_education/views/help_bubble_delegate.h
new file mode 100644
index 00000000000..d4172820162
--- /dev/null
+++ b/chromium/components/user_education/views/help_bubble_delegate.h
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_DELEGATE_H_
+#define COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_DELEGATE_H_
+
+#include <vector>
+
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/color/color_id.h"
+
+namespace ui {
+class TrackedElement;
+}
+
+namespace user_education {
+
+// Provides access to app-specific strings, styles, and navigation accelerators
+// so we can properly handle them.
+class HelpBubbleDelegate {
+ public:
+ HelpBubbleDelegate() = default;
+ HelpBubbleDelegate(const HelpBubbleDelegate&) = delete;
+ void operator=(const HelpBubbleDelegate&) = delete;
+ virtual ~HelpBubbleDelegate() = default;
+
+ // Gets a list of accelerators that can be used to navigate panes, which
+ // should trigger HelpBubble::ToggleFocusForAccessibility(). We need this
+ // because we do not by default have access to the current app's
+ // accelerator provider nor to the specific command IDs.
+ virtual std::vector<ui::Accelerator> GetPaneNavigationAccelerators(
+ ui::TrackedElement* anchor_element) const = 0;
+
+ // These methods return text contexts that will be handled by the app's
+ // typography system.
+ virtual int GetTitleTextContext() const = 0;
+ virtual int GetBodyTextContext() const = 0;
+ virtual int GetButtonTextContext() const = 0;
+
+ // These methods return color codes that will be handled by the app's theming
+ // system.
+ virtual ui::ColorId GetHelpBubbleBackgroundColorId() const = 0;
+ virtual ui::ColorId GetHelpBubbleForegroundColorId() const = 0;
+ virtual ui::ColorId GetHelpBubbleDefaultButtonBackgroundColorId() const = 0;
+ virtual ui::ColorId GetHelpBubbleDefaultButtonForegroundColorId() const = 0;
+ virtual ui::ColorId GetHelpBubbleButtonBorderColorId() const = 0;
+ virtual ui::ColorId GetHelpBubbleCloseButtonInkDropColorId() const = 0;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_FACTORY_DELEGATE_H_ \ No newline at end of file
diff --git a/chromium/components/user_education/views/help_bubble_factory_mac.h b/chromium/components/user_education/views/help_bubble_factory_mac.h
new file mode 100644
index 00000000000..8213549e8df
--- /dev/null
+++ b/chromium/components/user_education/views/help_bubble_factory_mac.h
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_FACTORY_MAC_H_
+#define COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_FACTORY_MAC_H_
+
+#include "base/memory/raw_ptr.h"
+#include "components/user_education/common/help_bubble_factory.h"
+
+namespace user_education {
+
+class HelpBubbleDelegate;
+
+// Factory implementation for HelpBubbleViews.
+class HelpBubbleFactoryMac : public HelpBubbleFactory {
+ public:
+ explicit HelpBubbleFactoryMac(const HelpBubbleDelegate* delegate);
+ ~HelpBubbleFactoryMac() override;
+
+ DECLARE_FRAMEWORK_SPECIFIC_METADATA()
+
+ // HelpBubbleFactory:
+ std::unique_ptr<HelpBubble> CreateBubble(ui::TrackedElement* element,
+ HelpBubbleParams params) override;
+ bool CanBuildBubbleForTrackedElement(
+ const ui::TrackedElement* element) const override;
+
+ private:
+ base::raw_ptr<const HelpBubbleDelegate> delegate_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_FACTORY_MAC_H_
diff --git a/chromium/components/user_education/views/help_bubble_factory_mac.mm b/chromium/components/user_education/views/help_bubble_factory_mac.mm
new file mode 100644
index 00000000000..fd1e4f832b1
--- /dev/null
+++ b/chromium/components/user_education/views/help_bubble_factory_mac.mm
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium 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/user_education/views/help_bubble_factory_mac.h"
+
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/views/help_bubble_delegate.h"
+#include "components/user_education/views/help_bubble_factory_views.h"
+#include "components/user_education/views/help_bubble_view.h"
+#include "ui/base/interaction/element_tracker_mac.h"
+#include "ui/base/interaction/framework_specific_implementation.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/views/interaction/element_tracker_views.h"
+#include "ui/views/widget/widget.h"
+
+namespace user_education {
+
+DEFINE_FRAMEWORK_SPECIFIC_METADATA(HelpBubbleFactoryMac)
+
+HelpBubbleFactoryMac::HelpBubbleFactoryMac(const HelpBubbleDelegate* delegate)
+ : delegate_(delegate) {}
+HelpBubbleFactoryMac::~HelpBubbleFactoryMac() = default;
+
+std::unique_ptr<HelpBubble> HelpBubbleFactoryMac::CreateBubble(
+ ui::TrackedElement* element,
+ HelpBubbleParams params) {
+ auto* const element_mac = element->AsA<ui::TrackedElementMac>();
+ views::Widget* const widget =
+ views::ElementTrackerViews::GetInstance()->GetWidgetForContext(
+ element_mac->context());
+ auto* const anchor_view = widget->GetRootView();
+ gfx::Rect anchor_rect = element_mac->screen_bounds();
+
+ // We don't want the bubble to be flush with either the side or top of the
+ // Mac native menu, because it looks funny.
+ constexpr auto kMacMenuInsets = gfx::Insets::VH(10, -5);
+ anchor_rect.Inset(kMacMenuInsets);
+
+ return base::WrapUnique(new HelpBubbleViews(new HelpBubbleView(
+ delegate_, anchor_view, std::move(params), anchor_rect)));
+}
+
+bool HelpBubbleFactoryMac::CanBuildBubbleForTrackedElement(
+ const ui::TrackedElement* element) const {
+ return element->IsA<ui::TrackedElementMac>();
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/views/help_bubble_factory_views.cc b/chromium/components/user_education/views/help_bubble_factory_views.cc
new file mode 100644
index 00000000000..3adbe55e3cf
--- /dev/null
+++ b/chromium/components/user_education/views/help_bubble_factory_views.cc
@@ -0,0 +1,180 @@
+// Copyright 2021 The Chromium 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/user_education/views/help_bubble_factory_views.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/common/user_education_class_properties.h"
+#include "components/user_education/views/help_bubble_delegate.h"
+#include "components/user_education/views/help_bubble_view.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/views/accessible_pane_view.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/interaction/element_tracker_views.h"
+#include "ui/views/view_utils.h"
+
+namespace user_education {
+
+DEFINE_FRAMEWORK_SPECIFIC_METADATA(HelpBubbleViews)
+DEFINE_FRAMEWORK_SPECIFIC_METADATA(HelpBubbleFactoryViews)
+
+HelpBubbleViews::HelpBubbleViews(HelpBubbleView* help_bubble_view)
+ : help_bubble_view_(help_bubble_view) {
+ DCHECK(help_bubble_view);
+ DCHECK(help_bubble_view->GetWidget());
+ scoped_observation_.Observe(help_bubble_view->GetWidget());
+}
+
+HelpBubbleViews::~HelpBubbleViews() {
+ // Needs to be called here while we still have access to HelpBubbleViews-
+ // specific logic.
+ Close();
+}
+
+bool HelpBubbleViews::ToggleFocusForAccessibility() {
+ // // If the bubble isn't present or can't be meaningfully focused, stop.
+ if (!help_bubble_view_)
+ return false;
+
+ // If the focus isn't in the help bubble, focus the help bubble.
+ // Note that if is_focus_in_ancestor_widget is true, then anchor both exists
+ // and has a widget, so anchor->GetWidget() will always be valid.
+ if (!help_bubble_view_->IsFocusInHelpBubble()) {
+ help_bubble_view_->GetWidget()->Activate();
+ help_bubble_view_->RequestFocus();
+ return true;
+ }
+
+ auto* const anchor = help_bubble_view_->GetAnchorView();
+ if (!anchor)
+ return false;
+
+ bool set_focus = false;
+ if (anchor->IsAccessibilityFocusable()) {
+#if BUILDFLAG(IS_MAC)
+ // Mac does not automatically pass activation on focus, so we have to do it
+ // manually.
+ anchor->GetWidget()->Activate();
+#else
+ // Focus the anchor. We can't request focus for an accessibility-only view
+ // until we turn on keyboard accessibility for its focus manager.
+ anchor->GetFocusManager()->SetKeyboardAccessible(true);
+#endif
+ anchor->RequestFocus();
+ set_focus = true;
+ } else if (views::IsViewClass<views::AccessiblePaneView>(anchor)) {
+ // An AccessiblePaneView can receive focus, but is not necessarily itself
+ // accessibility focusable. Use the built-in functionality for focusing
+ // elements of AccessiblePaneView instead.
+#if BUILDFLAG(IS_MAC)
+ // Mac does not automatically pass activation on focus, so we have to do it
+ // manually.
+ anchor->GetWidget()->Activate();
+#else
+ // You can't focus an accessible pane if it's already in accessibility
+ // mode, so avoid doing that; the SetPaneFocus() call will go back into
+ // accessibility navigation mode.
+ anchor->GetFocusManager()->SetKeyboardAccessible(false);
+#endif
+ set_focus =
+ static_cast<views::AccessiblePaneView*>(anchor)->SetPaneFocus(nullptr);
+ }
+
+ return set_focus;
+}
+
+void HelpBubbleViews::OnAnchorBoundsChanged() {
+ if (help_bubble_view_)
+ help_bubble_view_->OnAnchorBoundsChanged();
+}
+
+gfx::Rect HelpBubbleViews::GetBoundsInScreen() const {
+ return help_bubble_view_
+ ? help_bubble_view_->GetWidget()->GetWindowBoundsInScreen()
+ : gfx::Rect();
+}
+
+ui::ElementContext HelpBubbleViews::GetContext() const {
+ return help_bubble_view_
+ ? views::ElementTrackerViews::GetContextForView(help_bubble_view_)
+ : ui::ElementContext();
+}
+
+bool HelpBubbleViews::AcceleratorPressed(const ui::Accelerator& accelerator) {
+ if (CanHandleAccelerators()) {
+ ToggleFocusForAccessibility();
+ return true;
+ }
+
+ return false;
+}
+
+bool HelpBubbleViews::CanHandleAccelerators() const {
+ return help_bubble_view_ && help_bubble_view_->GetWidget() &&
+ help_bubble_view_->GetWidget()->IsActive();
+}
+
+void HelpBubbleViews::MaybeResetAnchorView() {
+ if (!help_bubble_view_)
+ return;
+ auto* const anchor_view = help_bubble_view_->GetAnchorView();
+ if (!anchor_view)
+ return;
+ anchor_view->SetProperty(kHasInProductHelpPromoKey, false);
+}
+
+void HelpBubbleViews::CloseBubbleImpl() {
+ if (!help_bubble_view_)
+ return;
+
+ scoped_observation_.Reset();
+ MaybeResetAnchorView();
+ help_bubble_view_->GetWidget()->Close();
+ help_bubble_view_ = nullptr;
+}
+
+void HelpBubbleViews::OnWidgetDestroying(views::Widget* widget) {
+ scoped_observation_.Reset();
+ MaybeResetAnchorView();
+ help_bubble_view_ = nullptr;
+ NotifyBubbleClosed();
+}
+
+HelpBubbleFactoryViews::HelpBubbleFactoryViews(
+ const HelpBubbleDelegate* delegate)
+ : delegate_(delegate) {
+ DCHECK(delegate_);
+}
+
+HelpBubbleFactoryViews::~HelpBubbleFactoryViews() = default;
+
+std::unique_ptr<HelpBubble> HelpBubbleFactoryViews::CreateBubble(
+ ui::TrackedElement* element,
+ HelpBubbleParams params) {
+ views::View* const anchor_view =
+ element->AsA<views::TrackedElementViews>()->view();
+ anchor_view->SetProperty(kHasInProductHelpPromoKey, true);
+ auto result = base::WrapUnique(new HelpBubbleViews(
+ new HelpBubbleView(delegate_, anchor_view, std::move(params))));
+ for (const auto& accelerator :
+ delegate_->GetPaneNavigationAccelerators(element)) {
+ result->bubble_view()->GetFocusManager()->RegisterAccelerator(
+ accelerator, ui::AcceleratorManager::HandlerPriority::kNormalPriority,
+ result.get());
+ }
+ return result;
+}
+
+bool HelpBubbleFactoryViews::CanBuildBubbleForTrackedElement(
+ const ui::TrackedElement* element) const {
+ return element->IsA<views::TrackedElementViews>();
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/views/help_bubble_factory_views.h b/chromium/components/user_education/views/help_bubble_factory_views.h
new file mode 100644
index 00000000000..bcc11be9fa4
--- /dev/null
+++ b/chromium/components/user_education/views/help_bubble_factory_views.h
@@ -0,0 +1,96 @@
+// Copyright 2021 The Chromium 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 COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_FACTORY_VIEWS_H_
+#define COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_FACTORY_VIEWS_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/scoped_observation.h"
+#include "components/user_education/common/help_bubble.h"
+#include "components/user_education/common/help_bubble_factory.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/interaction/framework_specific_implementation.h"
+#include "ui/color/color_id.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace user_education {
+
+class HelpBubbleView;
+class HelpBubbleDelegate;
+
+// Views-specific implementation of the help bubble.
+//
+// Because this is a FrameworkSpecificImplementation, you can use:
+// help_bubble->AsA<HelpBubbleViews>()->bubble_view()
+// to retrieve the underlying bubble view.
+class HelpBubbleViews : public HelpBubble,
+ public views::WidgetObserver,
+ public ui::AcceleratorTarget {
+ public:
+ ~HelpBubbleViews() override;
+
+ DECLARE_FRAMEWORK_SPECIFIC_METADATA()
+
+ // Retrieve the bubble view. If the bubble has been closed, this may return
+ // null.
+ HelpBubbleView* bubble_view() { return help_bubble_view_; }
+ const HelpBubbleView* bubble_view() const { return help_bubble_view_; }
+
+ // HelpBubble:
+ bool ToggleFocusForAccessibility() override;
+ void OnAnchorBoundsChanged() override;
+ gfx::Rect GetBoundsInScreen() const override;
+ ui::ElementContext GetContext() const override;
+
+ // ui::AcceleratorTarget
+ bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
+ bool CanHandleAccelerators() const override;
+
+ private:
+ friend class HelpBubbleFactoryViews;
+ friend class HelpBubbleFactoryMac;
+
+ explicit HelpBubbleViews(HelpBubbleView* help_bubble_view);
+
+ // Clean up properties on the anchor view, if applicable.
+ void MaybeResetAnchorView();
+
+ // HelpBubble:
+ void CloseBubbleImpl() override;
+
+ // views::WidgetObserver:
+ void OnWidgetDestroying(views::Widget* widget) override;
+
+ raw_ptr<HelpBubbleView> help_bubble_view_;
+ base::ScopedObservation<views::Widget, views::WidgetObserver>
+ scoped_observation_{this};
+};
+
+// Factory implementation for HelpBubbleViews.
+class HelpBubbleFactoryViews : public HelpBubbleFactory {
+ public:
+ explicit HelpBubbleFactoryViews(const HelpBubbleDelegate* delegate);
+ ~HelpBubbleFactoryViews() override;
+
+ DECLARE_FRAMEWORK_SPECIFIC_METADATA()
+
+ // HelpBubbleFactory:
+ std::unique_ptr<HelpBubble> CreateBubble(ui::TrackedElement* element,
+ HelpBubbleParams params) override;
+ bool CanBuildBubbleForTrackedElement(
+ const ui::TrackedElement* element) const override;
+
+ private:
+ base::raw_ptr<const HelpBubbleDelegate> delegate_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_FACTORY_VIEWS_H_
diff --git a/chromium/components/user_education/views/help_bubble_view.cc b/chromium/components/user_education/views/help_bubble_view.cc
new file mode 100644
index 00000000000..a9811d3e4aa
--- /dev/null
+++ b/chromium/components/user_education/views/help_bubble_view.cc
@@ -0,0 +1,729 @@
+// 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 "components/user_education/views/help_bubble_view.h"
+
+#include <initializer_list>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/raw_ptr.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/views/help_bubble_delegate.h"
+#include "components/user_education/views/help_bubble_factory_views.h"
+#include "components/variations/variations_associated_data.h"
+#include "components/vector_icons/vector_icons.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/models/image_model.h"
+#include "ui/color/color_provider.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/text_constants.h"
+#include "ui/gfx/text_utils.h"
+#include "ui/gfx/vector_icon_types.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/animation/ink_drop.h"
+#include "ui/views/background.h"
+#include "ui/views/bubble/bubble_border.h"
+#include "ui/views/bubble/bubble_frame_view.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/button/image_button_factory.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/button/md_text_button.h"
+#include "ui/views/controls/dot_indicator.h"
+#include "ui/views/controls/focus_ring.h"
+#include "ui/views/controls/highlight_path_generator.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/event_monitor.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/views/layout/layout_types.h"
+#include "ui/views/style/platform_style.h"
+#include "ui/views/style/typography.h"
+#include "ui/views/vector_icons.h"
+#include "ui/views/view_class_properties.h"
+#include "ui/views/view_tracker.h"
+#include "ui/views/view_utils.h"
+
+namespace user_education {
+
+namespace {
+
+// The amount of time the promo should stay onscreen.
+constexpr base::TimeDelta kDefaultTimeoutWithoutButtons = base::Seconds(10);
+constexpr base::TimeDelta kDefaultTimeoutWithButtons = base::Seconds(0);
+
+// Maximum width of the bubble. Longer strings will cause wrapping.
+constexpr int kBubbleMaxWidthDip = 340;
+
+// The insets from the bubble border to the text inside.
+constexpr auto kBubbleContentsInsets = gfx::Insets::VH(16, 20);
+
+// The insets from the button border to the text inside.
+constexpr auto kBubbleButtonPadding = gfx::Insets::VH(6, 16);
+
+// Translates from HelpBubbleArrow to the Views equivalent.
+views::BubbleBorder::Arrow TranslateArrow(HelpBubbleArrow arrow) {
+ switch (arrow) {
+ case HelpBubbleArrow::kNone:
+ return views::BubbleBorder::NONE;
+ case HelpBubbleArrow::kTopLeft:
+ return views::BubbleBorder::TOP_LEFT;
+ case HelpBubbleArrow::kTopRight:
+ return views::BubbleBorder::TOP_RIGHT;
+ case HelpBubbleArrow::kBottomLeft:
+ return views::BubbleBorder::BOTTOM_LEFT;
+ case HelpBubbleArrow::kBottomRight:
+ return views::BubbleBorder::BOTTOM_RIGHT;
+ case HelpBubbleArrow::kLeftTop:
+ return views::BubbleBorder::LEFT_TOP;
+ case HelpBubbleArrow::kRightTop:
+ return views::BubbleBorder::RIGHT_TOP;
+ case HelpBubbleArrow::kLeftBottom:
+ return views::BubbleBorder::LEFT_BOTTOM;
+ case HelpBubbleArrow::kRightBottom:
+ return views::BubbleBorder::RIGHT_BOTTOM;
+ case HelpBubbleArrow::kTopCenter:
+ return views::BubbleBorder::TOP_CENTER;
+ case HelpBubbleArrow::kBottomCenter:
+ return views::BubbleBorder::BOTTOM_CENTER;
+ case HelpBubbleArrow::kLeftCenter:
+ return views::BubbleBorder::LEFT_CENTER;
+ case HelpBubbleArrow::kRightCenter:
+ return views::BubbleBorder::RIGHT_CENTER;
+ }
+}
+
+class MdIPHBubbleButton : public views::MdTextButton {
+ public:
+ METADATA_HEADER(MdIPHBubbleButton);
+
+ MdIPHBubbleButton(const HelpBubbleDelegate* delegate,
+ PressedCallback callback,
+ const std::u16string& text,
+ bool is_default_button)
+ : MdTextButton(callback, text, delegate->GetButtonTextContext()),
+ delegate_(delegate),
+ is_default_button_(is_default_button) {
+ // Prominent style gives a button hover highlight.
+ SetProminent(true);
+ GetViewAccessibility().OverrideIsLeaf(true);
+ }
+ MdIPHBubbleButton(const MdIPHBubbleButton&) = delete;
+ MdIPHBubbleButton& operator=(const MdIPHBubbleButton&) = delete;
+ ~MdIPHBubbleButton() override = default;
+
+ void UpdateBackgroundColor() override {
+ // Prominent MD button does not have a border.
+ // Override this method to draw a border.
+ // Adapted from MdTextButton::UpdateBackgroundColor()
+ const auto* color_provider = GetColorProvider();
+ if (!color_provider)
+ return;
+ SkColor background_color = color_provider->GetColor(
+ is_default_button_
+ ? delegate_->GetHelpBubbleDefaultButtonBackgroundColorId()
+ : delegate_->GetHelpBubbleBackgroundColorId());
+ if (GetState() == STATE_PRESSED) {
+ background_color =
+ GetNativeTheme()->GetSystemButtonPressedColor(background_color);
+ }
+ const SkColor stroke_color = color_provider->GetColor(
+ is_default_button_
+ ? delegate_->GetHelpBubbleDefaultButtonBackgroundColorId()
+ : delegate_->GetHelpBubbleButtonBorderColorId());
+ SetBackground(CreateBackgroundFromPainter(
+ views::Painter::CreateRoundRectWith1PxBorderPainter(
+ background_color, stroke_color, GetCornerRadius())));
+ }
+
+ void OnThemeChanged() override {
+ views::MdTextButton::OnThemeChanged();
+
+ const auto* color_provider = GetColorProvider();
+ const SkColor background_color =
+ color_provider->GetColor(delegate_->GetHelpBubbleBackgroundColorId());
+ views::FocusRing::Get(this)->SetColor(background_color);
+
+ const SkColor foreground_color = color_provider->GetColor(
+ is_default_button_
+ ? delegate_->GetHelpBubbleDefaultButtonForegroundColorId()
+ : delegate_->GetHelpBubbleForegroundColorId());
+ SetEnabledTextColors(foreground_color);
+
+ // TODO(crbug/1112244): Temporary fix for Mac. Bubble shouldn't be in
+ // inactive style when the bubble loses focus.
+ SetTextColor(ButtonState::STATE_DISABLED, foreground_color);
+ }
+
+ private:
+ const base::raw_ptr<const HelpBubbleDelegate> delegate_;
+ bool is_default_button_;
+};
+
+BEGIN_METADATA(MdIPHBubbleButton, views::MdTextButton)
+END_METADATA
+
+// Displays a simple "X" close button that will close a promo bubble view.
+// The alt-text and button callback can be set based on the needs of the
+// specific bubble.
+class ClosePromoButton : public views::ImageButton {
+ public:
+ METADATA_HEADER(ClosePromoButton);
+ ClosePromoButton(const HelpBubbleDelegate* delegate,
+ const std::u16string accessible_name,
+ PressedCallback callback)
+ : delegate_(delegate) {
+ SetCallback(callback);
+ views::ConfigureVectorImageButton(this);
+ views::HighlightPathGenerator::Install(
+ this,
+ std::make_unique<views::CircleHighlightPathGenerator>(gfx::Insets()));
+ SetAccessibleName(accessible_name);
+ SetTooltipText(accessible_name);
+
+ constexpr int kIconSize = 16;
+ SetImageModel(views::ImageButton::STATE_NORMAL,
+ ui::ImageModel::FromVectorIcon(
+ views::kIcCloseIcon,
+ delegate_->GetHelpBubbleForegroundColorId(), kIconSize));
+
+ constexpr float kCloseButtonFocusRingHaloThickness = 1.25f;
+ views::FocusRing::Get(this)->SetHaloThickness(
+ kCloseButtonFocusRingHaloThickness);
+ }
+
+ void OnThemeChanged() override {
+ views::ImageButton::OnThemeChanged();
+ const auto* color_provider = GetColorProvider();
+ views::InkDrop::Get(this)->SetBaseColor(color_provider->GetColor(
+ delegate_->GetHelpBubbleCloseButtonInkDropColorId()));
+ views::FocusRing::Get(this)->SetColor(
+ color_provider->GetColor(delegate_->GetHelpBubbleForegroundColorId()));
+ }
+
+ private:
+ const base::raw_ptr<const HelpBubbleDelegate> delegate_;
+};
+
+BEGIN_METADATA(ClosePromoButton, views::ImageButton)
+END_METADATA
+
+class DotView : public views::View {
+ public:
+ METADATA_HEADER(DotView);
+ DotView(const HelpBubbleDelegate* delegate, gfx::Size size, bool should_fill)
+ : delegate_(delegate), size_(size), should_fill_(should_fill) {
+ // In order to anti-alias properly, we'll grow by the stroke width and then
+ // have the excess space be subtracted from the margins by the layout.
+ SetProperty(views::kInternalPaddingKey, gfx::Insets(kStrokeWidth));
+ }
+ ~DotView() override = default;
+
+ // views::View:
+ gfx::Size CalculatePreferredSize() const override {
+ gfx::Size size = size_;
+ const gfx::Insets* const insets = GetProperty(views::kInternalPaddingKey);
+ size.Enlarge(insets->width(), insets->height());
+ return size;
+ }
+
+ void OnPaint(gfx::Canvas* canvas) override {
+ gfx::RectF local_bounds = gfx::RectF(GetLocalBounds());
+ DCHECK_GT(local_bounds.width(), size_.width());
+ DCHECK_GT(local_bounds.height(), size_.height());
+ const gfx::PointF center_point = local_bounds.CenterPoint();
+ const float radius = (size_.width() - kStrokeWidth) / 2.0f;
+
+ const SkColor color = GetColorProvider()->GetColor(
+ delegate_->GetHelpBubbleForegroundColorId());
+ if (should_fill_) {
+ cc::PaintFlags fill_flags;
+ fill_flags.setStyle(cc::PaintFlags::kFill_Style);
+ fill_flags.setAntiAlias(true);
+ fill_flags.setColor(color);
+ canvas->DrawCircle(center_point, radius, fill_flags);
+ }
+
+ cc::PaintFlags stroke_flags;
+ stroke_flags.setStyle(cc::PaintFlags::kStroke_Style);
+ stroke_flags.setStrokeWidth(kStrokeWidth);
+ stroke_flags.setAntiAlias(true);
+ stroke_flags.setColor(color);
+ canvas->DrawCircle(center_point, radius, stroke_flags);
+ }
+
+ private:
+ static constexpr int kStrokeWidth = 1;
+
+ base::raw_ptr<const HelpBubbleDelegate> delegate_;
+ const gfx::Size size_;
+ const bool should_fill_;
+};
+
+constexpr int DotView::kStrokeWidth;
+
+BEGIN_METADATA(DotView, views::View)
+END_METADATA
+
+} // namespace
+
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(HelpBubbleView,
+ kHelpBubbleElementIdForTesting);
+
+// Explicitly don't use the default DIALOG_SHADOW as it will show a black
+// outline in dark mode on Mac. Use our own shadow instead. The shadow type is
+// the same for all other platforms.
+HelpBubbleView::HelpBubbleView(const HelpBubbleDelegate* delegate,
+ views::View* anchor_view,
+ HelpBubbleParams params,
+ absl::optional<gfx::Rect> anchor_rect)
+ : BubbleDialogDelegateView(anchor_view,
+ TranslateArrow(params.arrow),
+ views::BubbleBorder::STANDARD_SHADOW),
+ delegate_(delegate),
+ force_anchor_rect_(anchor_rect) {
+ // The anchor for promo bubbles should not highlight.
+ set_highlight_button_when_shown(false);
+ DCHECK(anchor_view)
+ << "A bubble that closes on blur must be initially focused.";
+ UseCompactMargins();
+
+ // Default timeout depends on whether non-close buttons are present.
+ timeout_ = params.timeout.value_or(params.buttons.empty()
+ ? kDefaultTimeoutWithoutButtons
+ : kDefaultTimeoutWithButtons);
+ if (!timeout_.is_zero())
+ timeout_callback_ = std::move(params.timeout_callback);
+ SetCancelCallback(std::move(params.dismiss_callback));
+
+ accessible_name_ = params.title_text;
+ if (!accessible_name_.empty())
+ accessible_name_ += u". ";
+ accessible_name_ += params.screenreader_text.empty()
+ ? params.body_text
+ : params.screenreader_text;
+ screenreader_hint_text_ = params.keyboard_navigation_hint;
+
+ // Since we don't have any controls for the user to interact with (we're just
+ // an information bubble), override our role to kAlert.
+ SetAccessibleRole(ax::mojom::Role::kAlert);
+
+ // Layout structure:
+ //
+ // [***ooo x] <--- progress container
+ // [@ TITLE x] <--- top text container
+ // body text
+ // [ cancel ok] <--- button container
+ //
+ // Notes:
+ // - The close button's placement depends on the presence of a progress
+ // indicator.
+ // - The body text takes the place of TITLE if there is no title.
+ // - If there is both a title and icon, the body text is manually indented to
+ // align with the title; this avoids having to nest an additional vertical
+ // container.
+ // - Unused containers are set to not be visible.
+ views::View* const progress_container =
+ AddChildView(std::make_unique<views::View>());
+ views::View* const top_text_container =
+ AddChildView(std::make_unique<views::View>());
+ views::View* const button_container =
+ AddChildView(std::make_unique<views::View>());
+
+ // Add progress indicator (optional) and its container.
+ if (params.progress) {
+ DCHECK(params.progress->second);
+ // TODO(crbug.com/1197208): surface progress information in a11y tree
+ for (int i = 0; i < params.progress->second; ++i) {
+ // TODO(crbug.com/1197208): formalize dot size
+ progress_container->AddChildView(std::make_unique<DotView>(
+ delegate, gfx::Size(8, 8), i < params.progress->first));
+ }
+ } else {
+ progress_container->SetVisible(false);
+ }
+
+ // Add the body icon (optional).
+ constexpr int kBodyIconSize = 20;
+ constexpr int kBodyIconBackgroundSize = 24;
+ if (params.body_icon) {
+ icon_view_ = top_text_container->AddChildViewAt(
+ std::make_unique<views::ImageView>(ui::ImageModel::FromVectorIcon(
+ *params.body_icon, delegate->GetHelpBubbleBackgroundColorId(),
+ kBodyIconSize)),
+ 0);
+ icon_view_->SetPreferredSize(
+ gfx::Size(kBodyIconBackgroundSize, kBodyIconBackgroundSize));
+ icon_view_->SetAccessibleName(params.body_icon_alt_text);
+ }
+
+ // Add title (optional) and body label.
+ if (!params.title_text.empty()) {
+ labels_.push_back(
+ top_text_container->AddChildView(std::make_unique<views::Label>(
+ params.title_text, delegate->GetTitleTextContext())));
+ labels_.push_back(
+ AddChildViewAt(std::make_unique<views::Label>(
+ params.body_text, delegate->GetBodyTextContext()),
+ GetIndexOf(button_container)));
+ } else {
+ labels_.push_back(
+ top_text_container->AddChildView(std::make_unique<views::Label>(
+ params.body_text, delegate->GetBodyTextContext())));
+ }
+
+ // Set common label properties.
+ for (views::Label* label : labels_) {
+ label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ label->SetMultiLine(true);
+ label->SetElideBehavior(gfx::NO_ELIDE);
+ }
+
+ // Add close button (optional).
+ if (params.buttons.empty() || params.force_close_button) {
+ std::u16string alt_text = params.close_button_alt_text;
+
+ // This can be empty if a test doesn't set it. Set a reasonable default to
+ // avoid an assertion (generated when a button with no text has no
+ // accessible name).
+ if (alt_text.empty())
+ alt_text = l10n_util::GetStringUTF16(IDS_CLOSE);
+
+ // Since we set the cancel callback, we will use CancelDialog() to dismiss.
+ close_button_ = (params.progress ? progress_container : top_text_container)
+ ->AddChildView(std::make_unique<ClosePromoButton>(
+ delegate, alt_text,
+ base::BindRepeating(&DialogDelegate::CancelDialog,
+ base::Unretained(this))));
+ }
+
+ // Add other buttons.
+ if (!params.buttons.empty()) {
+ auto run_callback_and_close = [](HelpBubbleView* bubble_view,
+ base::OnceClosure callback) {
+ // We want to call the button callback before deleting the bubble in case
+ // the caller needs to do something with it, but the callback itself
+ // could close the bubble. Therefore, we need to ensure that the
+ // underlying bubble view is not deleted before trying to close it.
+ views::ViewTracker tracker(bubble_view);
+ std::move(callback).Run();
+ auto* const view = tracker.view();
+ if (view && view->GetWidget() && !view->GetWidget()->IsClosed())
+ view->GetWidget()->Close();
+ };
+
+ // We will hold the default button to add later, since where we add it in
+ // the sequence depends on platform style.
+ std::unique_ptr<MdIPHBubbleButton> default_button;
+ for (HelpBubbleButtonParams& button_params : params.buttons) {
+ auto button = std::make_unique<MdIPHBubbleButton>(
+ delegate,
+ base::BindRepeating(run_callback_and_close, base::Unretained(this),
+ base::Passed(std::move(button_params.callback))),
+ button_params.text, button_params.is_default);
+ button->SetMinSize(gfx::Size(0, 0));
+ button->SetCustomPadding(kBubbleButtonPadding);
+ if (button_params.is_default) {
+ DCHECK(!default_button);
+ default_button = std::move(button);
+ } else {
+ non_default_buttons_.push_back(
+ button_container->AddChildView(std::move(button)));
+ }
+ }
+
+ // Add the default button if there is one based on platform style.
+ if (default_button) {
+ if (views::PlatformStyle::kIsOkButtonLeading) {
+ default_button_ =
+ button_container->AddChildViewAt(std::move(default_button), 0);
+ } else {
+ default_button_ =
+ button_container->AddChildView(std::move(default_button));
+ }
+ }
+ } else {
+ button_container->SetVisible(false);
+ }
+
+ // Set up layouts. This is the default vertical spacing that is also used to
+ // separate progress indicators for symmetry.
+ // TODO(dfried): consider whether we could take font ascender and descender
+ // height and factor them into margin calculations.
+ const views::LayoutProvider* layout_provider = views::LayoutProvider::Get();
+ const int default_spacing = layout_provider->GetDistanceMetric(
+ views::DISTANCE_RELATED_CONTROL_VERTICAL);
+
+ // Create primary layout (vertical).
+ SetLayoutManager(std::make_unique<views::FlexLayout>())
+ ->SetOrientation(views::LayoutOrientation::kVertical)
+ .SetMainAxisAlignment(views::LayoutAlignment::kCenter)
+ .SetInteriorMargin(kBubbleContentsInsets)
+ .SetCollapseMargins(true)
+ .SetDefault(views::kMarginsKey,
+ gfx::Insets::TLBR(0, 0, default_spacing, 0))
+ .SetIgnoreDefaultMainAxisMargins(true);
+
+ // Set up top row container layout.
+ const int kCloseButtonHeight = 24;
+ auto& progress_layout =
+ progress_container
+ ->SetLayoutManager(std::make_unique<views::FlexLayout>())
+ ->SetOrientation(views::LayoutOrientation::kHorizontal)
+ .SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
+ .SetMinimumCrossAxisSize(kCloseButtonHeight)
+ .SetDefault(views::kMarginsKey,
+ gfx::Insets::TLBR(0, default_spacing, 0, 0))
+ .SetIgnoreDefaultMainAxisMargins(true);
+ progress_container->SetProperty(
+ views::kFlexBehaviorKey,
+ views::FlexSpecification(progress_layout.GetDefaultFlexRule()));
+
+ // Close button should float right in whatever container it's in.
+ if (close_button_) {
+ close_button_->SetProperty(
+ views::kFlexBehaviorKey,
+ views::FlexSpecification(views::LayoutOrientation::kHorizontal,
+ views::MinimumFlexSizeRule::kPreferred,
+ views::MaximumFlexSizeRule::kUnbounded)
+ .WithAlignment(views::LayoutAlignment::kEnd));
+ close_button_->SetProperty(views::kMarginsKey,
+ gfx::Insets::TLBR(0, default_spacing, 0, 0));
+ }
+
+ // Icon view should have padding between it and the title or body label.
+ if (icon_view_) {
+ icon_view_->SetProperty(views::kMarginsKey,
+ gfx::Insets::TLBR(0, 0, 0, default_spacing));
+ }
+
+ // Set label flex properties. This ensures that if the width of the bubble
+ // maxes out the text will shrink on the cross-axis and grow to multiple
+ // lines without getting cut off.
+ const views::FlexSpecification text_flex(
+ views::LayoutOrientation::kVertical,
+ views::MinimumFlexSizeRule::kPreferred,
+ views::MaximumFlexSizeRule::kPreferred,
+ /* adjust_height_for_width = */ true,
+ views::MinimumFlexSizeRule::kScaleToMinimum);
+
+ for (views::Label* label : labels_)
+ label->SetProperty(views::kFlexBehaviorKey, text_flex);
+
+ auto& top_text_layout =
+ top_text_container
+ ->SetLayoutManager(std::make_unique<views::FlexLayout>())
+ ->SetOrientation(views::LayoutOrientation::kHorizontal)
+ .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
+ .SetIgnoreDefaultMainAxisMargins(true);
+ top_text_container->SetProperty(
+ views::kFlexBehaviorKey,
+ views::FlexSpecification(top_text_layout.GetDefaultFlexRule()));
+
+ // If the body icon is present, labels after the first are not parented to
+ // the top text container, but still need to be inset to align with the
+ // title.
+ if (icon_view_) {
+ const int indent = kBubbleContentsInsets.left() + kBodyIconBackgroundSize +
+ default_spacing;
+ for (size_t i = 1; i < labels_.size(); ++i) {
+ labels_[i]->SetProperty(views::kMarginsKey,
+ gfx::Insets::TLBR(0, indent, 0, 0));
+ }
+ }
+
+ // Set up button container layout.
+ // Add in the default spacing between bubble content and bottom/buttons.
+ button_container->SetProperty(
+ views::kMarginsKey,
+ gfx::Insets::TLBR(
+ layout_provider->GetDistanceMetric(
+ views::DISTANCE_DIALOG_CONTENT_MARGIN_BOTTOM_CONTROL),
+ 0, 0, 0));
+
+ // Create button container internal layout.
+ auto& button_layout =
+ button_container->SetLayoutManager(std::make_unique<views::FlexLayout>())
+ ->SetOrientation(views::LayoutOrientation::kHorizontal)
+ .SetMainAxisAlignment(views::LayoutAlignment::kEnd)
+ .SetDefault(
+ views::kMarginsKey,
+ gfx::Insets::TLBR(0,
+ layout_provider->GetDistanceMetric(
+ views::DISTANCE_RELATED_BUTTON_HORIZONTAL),
+ 0, 0))
+ .SetIgnoreDefaultMainAxisMargins(true);
+ button_container->SetProperty(
+ views::kFlexBehaviorKey,
+ views::FlexSpecification(button_layout.GetDefaultFlexRule()));
+
+ // Want a consistent initial focused view if one is available.
+ if (!button_container->children().empty()) {
+ SetInitiallyFocusedView(button_container->children()[0]);
+ } else if (close_button_) {
+ SetInitiallyFocusedView(close_button_);
+ }
+
+ SetProperty(views::kElementIdentifierKey, kHelpBubbleElementIdForTesting);
+ set_margins(gfx::Insets());
+ set_title_margins(gfx::Insets());
+ SetButtons(ui::DIALOG_BUTTON_NONE);
+ set_close_on_deactivate(false);
+ set_focus_traversable_from_anchor_view(false);
+
+ views::Widget* widget = views::BubbleDialogDelegateView::CreateBubble(this);
+
+ // This gets reset to the platform default when we call CreateBubble(), so we
+ // have to change it afterwards:
+ set_adjust_if_offscreen(true);
+ auto* const frame_view = GetBubbleFrameView();
+ frame_view->SetCornerRadius(
+ views::LayoutProvider::Get()->GetCornerRadiusMetric(
+ views::Emphasis::kHigh));
+ frame_view->SetDisplayVisibleArrow(!force_anchor_rect_.has_value() &&
+ params.arrow != HelpBubbleArrow::kNone);
+ SizeToContents();
+
+ widget->ShowInactive();
+ auto* const anchor_bubble =
+ anchor_view->GetWidget()->widget_delegate()->AsBubbleDialogDelegate();
+ if (anchor_bubble)
+ anchor_pin_ = anchor_bubble->PreventCloseOnDeactivate();
+ MaybeStartAutoCloseTimer();
+}
+
+HelpBubbleView::~HelpBubbleView() = default;
+
+void HelpBubbleView::MaybeStartAutoCloseTimer() {
+ if (timeout_.is_zero())
+ return;
+
+ auto_close_timer_.Start(FROM_HERE, timeout_, this,
+ &HelpBubbleView::OnTimeout);
+}
+
+void HelpBubbleView::OnTimeout() {
+ std::move(timeout_callback_).Run();
+ GetWidget()->Close();
+}
+
+bool HelpBubbleView::OnMousePressed(const ui::MouseEvent& event) {
+ base::RecordAction(
+ base::UserMetricsAction("InProductHelp.Promos.BubbleClicked"));
+ return false;
+}
+
+std::u16string HelpBubbleView::GetAccessibleWindowTitle() const {
+ std::u16string result = accessible_name_;
+
+ // If there's a keyboard navigation hint, append it after a full stop.
+ if (!screenreader_hint_text_.empty() && activate_count_ <= 1)
+ result += u". " + screenreader_hint_text_;
+
+ return result;
+}
+
+void HelpBubbleView::OnWidgetActivationChanged(views::Widget* widget,
+ bool active) {
+ if (widget == GetWidget()) {
+ if (active) {
+ ++activate_count_;
+ auto_close_timer_.AbandonAndStop();
+ } else {
+ MaybeStartAutoCloseTimer();
+ }
+ }
+}
+
+void HelpBubbleView::OnThemeChanged() {
+ views::BubbleDialogDelegateView::OnThemeChanged();
+
+ const auto* color_provider = GetColorProvider();
+ const SkColor background_color =
+ color_provider->GetColor(delegate_->GetHelpBubbleBackgroundColorId());
+ set_color(background_color);
+
+ const SkColor foreground_color =
+ color_provider->GetColor(delegate_->GetHelpBubbleForegroundColorId());
+ if (icon_view_) {
+ icon_view_->SetBackground(views::CreateRoundedRectBackground(
+ foreground_color, icon_view_->GetPreferredSize().height() / 2));
+ }
+
+ for (auto* label : labels_) {
+ label->SetBackgroundColor(background_color);
+ label->SetEnabledColor(foreground_color);
+ }
+}
+
+gfx::Size HelpBubbleView::CalculatePreferredSize() const {
+ const gfx::Size layout_manager_preferred_size =
+ View::CalculatePreferredSize();
+
+ // Wrap if the width is larger than |kBubbleMaxWidthDip|.
+ if (layout_manager_preferred_size.width() > kBubbleMaxWidthDip) {
+ return gfx::Size(kBubbleMaxWidthDip, GetHeightForWidth(kBubbleMaxWidthDip));
+ }
+
+ return layout_manager_preferred_size;
+}
+
+gfx::Rect HelpBubbleView::GetAnchorRect() const {
+ return force_anchor_rect_.value_or(BubbleDialogDelegateView::GetAnchorRect());
+}
+
+// static
+bool HelpBubbleView::IsHelpBubble(views::DialogDelegate* dialog) {
+ auto* const contents = dialog->GetContentsView();
+ return contents && views::IsViewClass<HelpBubbleView>(contents);
+}
+
+bool HelpBubbleView::IsFocusInHelpBubble() const {
+#if BUILDFLAG(IS_MAC)
+ if (close_button_ && close_button_->HasFocus())
+ return true;
+ if (default_button_ && default_button_->HasFocus())
+ return true;
+ for (auto* button : non_default_buttons_) {
+ if (button->HasFocus())
+ return true;
+ }
+ return false;
+#else
+ return GetWidget()->IsActive();
+#endif
+}
+
+views::LabelButton* HelpBubbleView::GetDefaultButtonForTesting() const {
+ return default_button_;
+}
+
+views::LabelButton* HelpBubbleView::GetNonDefaultButtonForTesting(
+ int index) const {
+ return non_default_buttons_[index];
+}
+
+BEGIN_METADATA(HelpBubbleView, views::BubbleDialogDelegateView)
+END_METADATA
+
+} // namespace user_education
diff --git a/chromium/components/user_education/views/help_bubble_view.h b/chromium/components/user_education/views/help_bubble_view.h
new file mode 100644
index 00000000000..d1d19158865
--- /dev/null
+++ b/chromium/components/user_education/views/help_bubble_view.h
@@ -0,0 +1,118 @@
+// 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 COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_VIEW_H_
+#define COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_VIEW_H_
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/bubble/bubble_border.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/controls/button/label_button.h"
+
+namespace ui {
+class MouseEvent;
+} // namespace ui
+
+namespace views {
+class ImageView;
+class Label;
+class MdTextButton;
+} // namespace views
+
+namespace user_education {
+
+class HelpBubbleDelegate;
+
+// The HelpBubbleView is a special BubbleDialogDelegateView for
+// in-product help which educates users about certain Chrome features in
+// a deferred context.
+class HelpBubbleView : public views::BubbleDialogDelegateView {
+ public:
+ METADATA_HEADER(HelpBubbleView);
+ DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kHelpBubbleElementIdForTesting);
+
+ HelpBubbleView(const HelpBubbleDelegate* delegate,
+ views::View* anchor_view,
+ HelpBubbleParams params,
+ absl::optional<gfx::Rect> anchor_rect = absl::nullopt);
+ HelpBubbleView(const HelpBubbleView&) = delete;
+ HelpBubbleView& operator=(const HelpBubbleView&) = delete;
+ ~HelpBubbleView() override;
+
+ // Returns whether the given dialog is a help bubble.
+ static bool IsHelpBubble(views::DialogDelegate* dialog);
+
+ bool IsFocusInHelpBubble() const;
+
+ views::LabelButton* GetDefaultButtonForTesting() const;
+ views::LabelButton* GetNonDefaultButtonForTesting(int index) const;
+
+ protected:
+ // BubbleDialogDelegateView:
+ bool OnMousePressed(const ui::MouseEvent& event) override;
+ std::u16string GetAccessibleWindowTitle() const override;
+ void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
+ void OnThemeChanged() override;
+ gfx::Size CalculatePreferredSize() const override;
+ gfx::Rect GetAnchorRect() const override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(HelpBubbleViewTimeoutTest,
+ RespectsProvidedTimeoutAfterActivate);
+
+ void MaybeStartAutoCloseTimer();
+
+ void OnTimeout();
+
+ const base::raw_ptr<const HelpBubbleDelegate> delegate_;
+
+ // Forces the anchor rect to the specified rectangle (in screen coordinates).
+ // If an artificial anchor rect is used, we assume the exact target cannot be
+ // localized, and a visible arrow is not shown.
+ absl::optional<gfx::Rect> force_anchor_rect_;
+
+ base::raw_ptr<views::ImageView> icon_view_ = nullptr;
+ std::vector<views::Label*> labels_;
+
+ // If the bubble has buttons, it must be focusable.
+ std::vector<views::MdTextButton*> non_default_buttons_;
+ base::raw_ptr<views::MdTextButton> default_button_ = nullptr;
+ base::raw_ptr<views::Button> close_button_ = nullptr;
+
+ // This is the base accessible name of the window.
+ std::u16string accessible_name_;
+
+ // This is any additional hint text to read.
+ std::u16string screenreader_hint_text_;
+
+ // Track the number of times the widget has been activated; if it's greater
+ // than 1 we won't re-read the screenreader hint again.
+ int activate_count_ = 0;
+
+ // Prevents the widget we're anchored to from disappearing when it loses
+ // focus, even if it's marked as close_on_deactivate.
+ std::unique_ptr<CloseOnDeactivatePin> anchor_pin_;
+
+ // Auto close timeout. If the value is 0 (default), the bubble never times
+ // out.
+ base::TimeDelta timeout_;
+ base::OneShotTimer auto_close_timer_;
+
+ base::OnceClosure timeout_callback_;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_VIEWS_HELP_BUBBLE_VIEW_H_
diff --git a/chromium/components/user_education/views/help_bubble_view_unittest.cc b/chromium/components/user_education/views/help_bubble_view_unittest.cc
new file mode 100644
index 00000000000..8ed909a245b
--- /dev/null
+++ b/chromium/components/user_education/views/help_bubble_view_unittest.cc
@@ -0,0 +1,211 @@
+// 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/user_education/views/help_bubble_view.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/callback_helpers.h"
+#include "base/memory/raw_ptr.h"
+#include "base/test/mock_callback.h"
+#include "components/user_education/common/feature_promo_specification.h"
+#include "components/user_education/common/help_bubble_params.h"
+#include "components/user_education/views/help_bubble_delegate.h"
+#include "components/user_education/views/help_bubble_factory_views.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/base/interaction/expect_call_in_scope.h"
+#include "ui/base/interaction/interaction_test_util.h"
+#include "ui/base/theme_provider.h"
+#include "ui/views/interaction/interaction_test_util_views.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace user_education {
+
+namespace {
+
+// Fake delegate implementation that does not depend on the browser.
+class TestDelegate : public HelpBubbleDelegate {
+ public:
+ TestDelegate() = default;
+ ~TestDelegate() override = default;
+
+ std::vector<ui::Accelerator> GetPaneNavigationAccelerators(
+ ui::TrackedElement* anchor_element) const override {
+ return std::vector<ui::Accelerator>();
+ }
+
+ // These methods return text contexts that will be handled by the app's
+ // typography system.
+ int GetTitleTextContext() const override { return 0; }
+ int GetBodyTextContext() const override { return 0; }
+ int GetButtonTextContext() const override { return 0; }
+
+ // These methods return color codes that will be handled by the app's theming
+ // system.
+ ui::ColorId GetHelpBubbleBackgroundColorId() const override { return 0; }
+ ui::ColorId GetHelpBubbleForegroundColorId() const override { return 0; }
+ ui::ColorId GetHelpBubbleDefaultButtonBackgroundColorId() const override {
+ return 0;
+ }
+ ui::ColorId GetHelpBubbleDefaultButtonForegroundColorId() const override {
+ return 0;
+ }
+ ui::ColorId GetHelpBubbleButtonBorderColorId() const override { return 0; }
+ ui::ColorId GetHelpBubbleCloseButtonInkDropColorId() const override {
+ return 0;
+ }
+};
+
+// Fake theme provider. There's a similar TestThemeProvider in chrome/test but
+// we're avoiding using chrome-specific code here.
+class TestThemeProvider : public ui::ThemeProvider {
+ public:
+ TestThemeProvider() = default;
+ ~TestThemeProvider() override = default;
+
+ gfx::ImageSkia* GetImageSkiaNamed(int id) const override { return nullptr; }
+ SkColor GetColor(int id) const override { return SK_ColorRED; }
+ color_utils::HSL GetTint(int id) const override { return color_utils::HSL(); }
+ int GetDisplayProperty(int id) const override { return 0; }
+ bool ShouldUseNativeFrame() const override { return false; }
+ bool HasCustomImage(int id) const override { return false; }
+ base::RefCountedMemory* GetRawData(
+ int id,
+ ui::ResourceScaleFactor scale_factor) const override {
+ return nullptr;
+ }
+};
+
+// A top-level widget that reports a dummy theme provider.
+class TestThemedWidget : public views::Widget {
+ public:
+ const ui::ThemeProvider* GetThemeProvider() const override {
+ return &test_theme_provider_;
+ }
+
+ private:
+ TestThemeProvider test_theme_provider_;
+};
+
+} // namespace
+
+// Unit tests for HelpBubbleView. Timeout functionality isn't tested here due to
+// the vagaries of trying to get simulated timed events to run without a full
+// execution environment (specifically, Mac tests were extremely flaky without
+// the browser).
+//
+// Timeouts are tested in:
+// chrome/browser/ui/views/user_education/help_bubble_view_timeout_unittest.cc
+class HelpBubbleViewTest : public views::ViewsTestBase {
+ public:
+ HelpBubbleViewTest() = default;
+ ~HelpBubbleViewTest() override = default;
+
+ void SetUp() override {
+ ViewsTestBase::SetUp();
+ widget_ = std::make_unique<TestThemedWidget>();
+ widget_->Init(CreateParamsForTestWidget());
+ view_ = widget_->SetContentsView(std::make_unique<views::View>());
+ widget_->Show();
+ }
+
+ void TearDown() override {
+ widget_.reset();
+ ViewsTestBase::TearDown();
+ }
+
+ protected:
+ HelpBubbleView* CreateHelpBubbleView(HelpBubbleParams params) {
+ return new HelpBubbleView(&test_delegate_, view_, std::move(params));
+ }
+
+ HelpBubbleView* CreateHelpBubbleView(base::RepeatingClosure button_callback) {
+ HelpBubbleParams params;
+ params.body_text = u"To X, do Y";
+ params.arrow = HelpBubbleArrow::kTopRight;
+
+ if (button_callback) {
+ HelpBubbleButtonParams button_params;
+ button_params.text = u"Go away";
+ button_params.is_default = true;
+ button_params.callback = std::move(button_callback);
+ params.buttons.push_back(std::move(button_params));
+ }
+
+ return CreateHelpBubbleView(std::move(params));
+ }
+
+ TestDelegate test_delegate_;
+ base::raw_ptr<views::View> view_;
+ std::unique_ptr<views::Widget> widget_;
+};
+
+TEST_F(HelpBubbleViewTest, CallButtonCallback_Mouse) {
+ UNCALLED_MOCK_CALLBACK(base::RepeatingClosure, mock_callback);
+
+ HelpBubbleView* const bubble = CreateHelpBubbleView(mock_callback.Get());
+
+ // Simulate clicks on dismiss button.
+ EXPECT_CALL_IN_SCOPE(
+ mock_callback, Run,
+ views::test::InteractionTestUtilSimulatorViews::PressButton(
+ bubble->GetDefaultButtonForTesting(),
+ ui::test::InteractionTestUtil::InputType::kMouse));
+
+ bubble->GetWidget()->Close();
+}
+
+TEST_F(HelpBubbleViewTest, CallButtonCallback_Keyboard) {
+ UNCALLED_MOCK_CALLBACK(base::RepeatingClosure, mock_callback);
+
+ HelpBubbleView* const bubble = CreateHelpBubbleView(mock_callback.Get());
+
+ // Simulate clicks on dismiss button.
+ EXPECT_CALL_IN_SCOPE(
+ mock_callback, Run,
+ views::test::InteractionTestUtilSimulatorViews::PressButton(
+ bubble->GetDefaultButtonForTesting(),
+ ui::test::InteractionTestUtil::InputType::kKeyboard));
+
+ bubble->GetWidget()->Close();
+}
+
+TEST_F(HelpBubbleViewTest, StableButtonOrder) {
+ HelpBubbleParams params;
+ params.body_text = u"To X, do Y";
+ params.arrow = HelpBubbleArrow::kTopRight;
+
+ constexpr char16_t kButton1Text[] = u"button 1";
+ constexpr char16_t kButton2Text[] = u"button 2";
+ constexpr char16_t kButton3Text[] = u"button 3";
+
+ HelpBubbleButtonParams button1;
+ button1.text = kButton1Text;
+ button1.is_default = false;
+ params.buttons.push_back(std::move(button1));
+
+ HelpBubbleButtonParams button2;
+ button2.text = kButton2Text;
+ button2.is_default = true;
+ params.buttons.push_back(std::move(button2));
+
+ HelpBubbleButtonParams button3;
+ button3.text = kButton3Text;
+ button3.is_default = false;
+ params.buttons.push_back(std::move(button3));
+
+ auto* bubble = new HelpBubbleView(&test_delegate_, view_, std::move(params));
+ EXPECT_EQ(kButton1Text, bubble->GetNonDefaultButtonForTesting(0)->GetText());
+ EXPECT_EQ(kButton2Text, bubble->GetDefaultButtonForTesting()->GetText());
+ EXPECT_EQ(kButton3Text, bubble->GetNonDefaultButtonForTesting(1)->GetText());
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education/views/new_badge_label.cc b/chromium/components/user_education/views/new_badge_label.cc
new file mode 100644
index 00000000000..8b840a8a97c
--- /dev/null
+++ b/chromium/components/user_education/views/new_badge_label.cc
@@ -0,0 +1,180 @@
+// 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/user_education/views/new_badge_label.h"
+
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/gfx/text_utils.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/menu/new_badge.h"
+#include "ui/views/metadata/type_conversion.h"
+#include "ui/views/view.h"
+#include "ui/views/view_class_properties.h"
+#include "ui/views/widget/widget.h"
+
+namespace user_education {
+
+NewBadgeLabel::NewBadgeLabel(const std::u16string& text,
+ int text_context,
+ int text_style,
+ gfx::DirectionalityMode directionality_mode)
+ : Label(text, text_context, text_style, directionality_mode) {
+ UpdatePaddingForNewBadge();
+}
+
+NewBadgeLabel::NewBadgeLabel(const std::u16string& text, const CustomFont& font)
+ : Label(text, font) {
+ UpdatePaddingForNewBadge();
+}
+
+NewBadgeLabel::~NewBadgeLabel() = default;
+
+void NewBadgeLabel::SetDisplayNewBadge(bool display_new_badge) {
+ DCHECK(!GetWidget() || !GetVisible() || !GetWidget()->IsVisible())
+ << "New badge display should not be toggled while this element is "
+ "visible.";
+ if (display_new_badge_ == display_new_badge)
+ return;
+
+ display_new_badge_ = display_new_badge;
+
+ // At this point we know the display setting has changed, so we must add or
+ // remove the relevant padding and insets.
+ if (display_new_badge_) {
+ UpdatePaddingForNewBadge();
+ } else {
+ // Clearing these only when display is set to false - rather than in e.g.
+ // UpdatePaddingForNewBadge() - ensures that any subsequent modifications to
+ // the border or padding are not discarded.
+ views::Label::SetBorder(nullptr);
+ ClearProperty(views::kInternalPaddingKey);
+ }
+
+ OnPropertyChanged(&display_new_badge_, views::kPropertyEffectsLayout);
+}
+
+void NewBadgeLabel::SetPadAfterNewBadge(bool pad_after_new_badge) {
+ if (pad_after_new_badge_ == pad_after_new_badge)
+ return;
+
+ pad_after_new_badge_ = pad_after_new_badge;
+ UpdatePaddingForNewBadge();
+ OnPropertyChanged(&pad_after_new_badge_, views::kPropertyEffectsLayout);
+}
+
+void NewBadgeLabel::SetBadgePlacement(BadgePlacement badge_placement) {
+ if (badge_placement_ == badge_placement)
+ return;
+
+ badge_placement_ = badge_placement;
+ UpdatePaddingForNewBadge();
+ OnPropertyChanged(&badge_placement_, views::kPropertyEffectsPaint);
+}
+
+void NewBadgeLabel::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+ Label::GetAccessibleNodeData(node_data);
+ std::u16string accessible_name = GetText();
+ if (display_new_badge_) {
+ accessible_name.push_back(' ');
+ accessible_name.append(views::NewBadge::GetNewBadgeAccessibleDescription());
+ }
+ node_data->SetName(accessible_name);
+}
+
+gfx::Size NewBadgeLabel::CalculatePreferredSize() const {
+ gfx::Size size = Label::CalculatePreferredSize();
+ if (display_new_badge_)
+ size.SetToMax(views::NewBadge::GetNewBadgeSize(font_list()));
+ return size;
+}
+
+gfx::Size NewBadgeLabel::GetMinimumSize() const {
+ gfx::Size size = Label::GetMinimumSize();
+ if (display_new_badge_)
+ size.SetToMax(views::NewBadge::GetNewBadgeSize(font_list()));
+ return size;
+}
+
+int NewBadgeLabel::GetHeightForWidth(int w) const {
+ int height = Label::GetHeightForWidth(w);
+ if (display_new_badge_) {
+ height = std::max(height,
+ views::NewBadge::GetNewBadgeSize(font_list()).height());
+ }
+ return height;
+}
+
+void NewBadgeLabel::OnDeviceScaleFactorChanged(float old_device_scale_factor,
+ float new_device_scale_factor) {
+ UpdatePaddingForNewBadge();
+}
+
+void NewBadgeLabel::OnPaint(gfx::Canvas* canvas) {
+ Label::OnPaint(canvas);
+ if (!display_new_badge_)
+ return;
+ const gfx::Rect contents_bounds = GetContentsBounds();
+ int extra_width = 0;
+ if (badge_placement_ == BadgePlacement::kImmediatelyAfterText)
+ extra_width = std::max(0, width() - GetPreferredSize().width());
+ const int badge_x = views::NewBadge::kNewBadgeHorizontalMargin - extra_width +
+ (base::i18n::IsRTL() ? width() - contents_bounds.x()
+ : contents_bounds.right());
+
+ views::NewBadge::DrawNewBadge(canvas, this, badge_x, GetFontListY(),
+ font_list());
+}
+
+void NewBadgeLabel::UpdatePaddingForNewBadge() {
+ if (!display_new_badge_)
+ return;
+
+ // Calculate the width required for the badge plus separation from the text.
+ int width = views::NewBadge::GetNewBadgeSize(font_list()).width();
+ int right_padding = 0;
+ if (pad_after_new_badge_) {
+ width += 2 * views::NewBadge::kNewBadgeHorizontalMargin;
+ right_padding = views::NewBadge::kNewBadgeHorizontalMargin;
+ } else {
+ width += views::NewBadge::kNewBadgeHorizontalMargin;
+ }
+
+ // Reserve adequate space above and below the label so that the badge will fit
+ // vertically, and to the right to actually hold the badge.
+ gfx::Insets border = gfx::AdjustVisualBorderForFont(
+ font_list(), gfx::Insets(views::NewBadge::kNewBadgeInternalPadding));
+ if (base::i18n::IsRTL()) {
+ border.set_left(width);
+ border.set_right(0);
+ } else {
+ border.set_left(0);
+ border.set_right(width);
+ }
+ views::Label::SetBorder(views::CreateEmptyBorder(border));
+
+ // If there is right-padding, ensure that layouts understand it can be
+ // collapsed into a margin.
+ SetProperty(views::kInternalPaddingKey,
+ gfx::Insets::TLBR(0, 0, 0, right_padding));
+}
+
+void NewBadgeLabel::SetBorder(std::unique_ptr<views::Border> b) {
+ NOTREACHED() << "Calling SetBorder() externally is currently not allowed.";
+}
+
+BEGIN_METADATA(NewBadgeLabel, views::Label)
+ADD_PROPERTY_METADATA(bool, DisplayNewBadge)
+ADD_PROPERTY_METADATA(NewBadgeLabel::BadgePlacement, BadgePlacement)
+ADD_PROPERTY_METADATA(bool, PadAfterNewBadge)
+END_METADATA
+
+} // namespace user_education
+
+DEFINE_ENUM_CONVERTERS(
+ user_education::NewBadgeLabel::BadgePlacement,
+ {user_education::NewBadgeLabel::BadgePlacement::kImmediatelyAfterText,
+ u"kImmediatelyAfterText"},
+ {user_education::NewBadgeLabel::BadgePlacement::kTrailingEdge,
+ u"kTrailingEdge"})
diff --git a/chromium/components/user_education/views/new_badge_label.h b/chromium/components/user_education/views/new_badge_label.h
new file mode 100644
index 00000000000..98241c715c6
--- /dev/null
+++ b/chromium/components/user_education/views/new_badge_label.h
@@ -0,0 +1,98 @@
+// 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 COMPONENTS_USER_EDUCATION_VIEWS_NEW_BADGE_LABEL_H_
+#define COMPONENTS_USER_EDUCATION_VIEWS_NEW_BADGE_LABEL_H_
+
+#include <memory>
+
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/menu/new_badge.h"
+#include "ui/views/style/typography.h"
+
+namespace views {
+class Border;
+}
+
+namespace user_education {
+
+// Extends views::Label to optionally display a "New" badge next to the text,
+// drawing attention to a new feature in Chrome.
+//
+// When |display_new_badge| is set to false, behaves exactly as a normal Label,
+// with the caveat that the following are explicitly disallowed:
+// * Calling SetDisplayNewBadge() when the label is visible to the user.
+// * Calling SetBorder() from external code, as the border is used to create
+// space to render the badge.
+class NewBadgeLabel : public views::Label {
+ public:
+ // Determines how the badge is placed relative to the label text if the label
+ // is wider than its preferred size (has no effect otherwise).
+ enum class BadgePlacement {
+ // Places the "New" badge immediately after the label text (default).
+ kImmediatelyAfterText,
+ // Places the "New" badge all the way at the trailing edge of the control,
+ // which is the right edge for LTR and the left edge for RTL.
+ kTrailingEdge
+ };
+
+ METADATA_HEADER(NewBadgeLabel);
+
+ // Constructs a new badge label. Designed to be argument-compatible with the
+ // views::Label constructor so they can be substituted.
+ explicit NewBadgeLabel(const std::u16string& text = std::u16string(),
+ int text_context = views::style::CONTEXT_LABEL,
+ int text_style = views::style::STYLE_PRIMARY,
+ gfx::DirectionalityMode directionality_mode =
+ gfx::DirectionalityMode::DIRECTIONALITY_FROM_TEXT);
+ NewBadgeLabel(const std::u16string& text, const CustomFont& font);
+ ~NewBadgeLabel() override;
+
+ // Sets whether the New badge is shown on this label.
+ // Should only be called before the label is shown.
+ void SetDisplayNewBadge(bool display_new_badge);
+ bool GetDisplayNewBadge() const { return display_new_badge_; }
+
+ void SetPadAfterNewBadge(bool pad_after_new_badge);
+ bool GetPadAfterNewBadge() const { return pad_after_new_badge_; }
+
+ void SetBadgePlacement(BadgePlacement badge_placement);
+ BadgePlacement GetBadgePlacement() const { return badge_placement_; }
+
+ // Label:
+ void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+ gfx::Size CalculatePreferredSize() const override;
+ gfx::Size GetMinimumSize() const override;
+ int GetHeightForWidth(int w) const override;
+ void OnDeviceScaleFactorChanged(float old_device_scale_factor,
+ float new_device_scale_factor) override;
+ void OnPaint(gfx::Canvas* canvas) override;
+
+ private:
+ // Hide the SetBorder() method so that external callers can't use it since we
+ // rely on it to add padding. This won't prevent access via downcast, however.
+ void SetBorder(std::unique_ptr<views::Border> b) override;
+
+ // Specifies whether the badge should be displayed. Defaults to true, but we
+ // allow the badge to be selectively disabled during experiments/feature
+ // rollouts without having to swap this object with a vanilla Label.
+ bool display_new_badge_ = true;
+
+ // Add the required internal padding to the label so that there is room to
+ // display the new badge.
+ void UpdatePaddingForNewBadge();
+
+ // Specifies the placement of the "New" badge when the label is wider than its
+ // preferred size.
+ BadgePlacement badge_placement_ = BadgePlacement::kImmediatelyAfterText;
+
+ // Determines whether there is additional internal margin to the right of the
+ // "New" badge. When set to true, the space will be allocated, and
+ // kInternalPaddingKey will be set so that layouts know this space is empty.
+ bool pad_after_new_badge_ = true;
+};
+
+} // namespace user_education
+
+#endif // COMPONENTS_USER_EDUCATION_VIEWS_NEW_BADGE_LABEL_H_
diff --git a/chromium/components/user_education/views/new_badge_label_unittest.cc b/chromium/components/user_education/views/new_badge_label_unittest.cc
new file mode 100644
index 00000000000..90e3799254b
--- /dev/null
+++ b/chromium/components/user_education/views/new_badge_label_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright 2021 The Chromium 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/user_education/views/new_badge_label.h"
+
+#include "base/memory/raw_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/layout/layout_types.h"
+#include "ui/views/style/typography.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/view_class_properties.h"
+
+namespace user_education {
+
+class NewBadgeLabelTest : public views::ViewsTestBase {
+ public:
+ NewBadgeLabelTest() = default;
+ ~NewBadgeLabelTest() override = default;
+
+ void SetUp() override {
+ ViewsTestBase::SetUp();
+
+ widget_ = std::make_unique<views::Widget>();
+ views::Widget::InitParams params =
+ CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ constexpr gfx::Size kNewBadgeLabelTestWidgetSize(300, 300);
+ params.bounds = gfx::Rect(gfx::Point(), kNewBadgeLabelTestWidgetSize);
+ widget_->Init(std::move(params));
+ contents_ = widget_->SetContentsView(std::make_unique<views::View>());
+ contents_->SetLayoutManager(std::make_unique<views::FlexLayout>())
+ ->SetOrientation(views::LayoutOrientation::kVertical)
+ .SetCrossAxisAlignment(views::LayoutAlignment::kStart);
+
+ control_label_ = contents_->AddChildView(
+ std::make_unique<views::Label>(u"test", views::style::CONTEXT_LABEL));
+ new_badge_label_ = contents_->AddChildView(
+ std::make_unique<NewBadgeLabel>(u"test", views::style::CONTEXT_LABEL));
+ }
+
+ void TearDown() override {
+ widget_.reset();
+ contents_ = nullptr;
+ control_label_ = nullptr;
+ new_badge_label_ = nullptr;
+ ViewsTestBase::TearDown();
+ }
+
+ views::Widget* widget() const { return widget_.get(); }
+ views::Label* control_label() const { return control_label_; }
+ NewBadgeLabel* new_badge_label() const { return new_badge_label_; }
+
+ private:
+ std::unique_ptr<views::Widget> widget_;
+ raw_ptr<views::View> contents_ = nullptr;
+ raw_ptr<views::Label> control_label_ = nullptr;
+ raw_ptr<NewBadgeLabel> new_badge_label_ = nullptr;
+};
+
+TEST_F(NewBadgeLabelTest, NoBadgeReportsSameSizes) {
+ new_badge_label()->SetDisplayNewBadge(false);
+ const gfx::Size preferred_size = control_label()->GetPreferredSize();
+ EXPECT_EQ(preferred_size, new_badge_label()->GetPreferredSize());
+ EXPECT_EQ(control_label()->GetMinimumSize(),
+ new_badge_label()->GetMinimumSize());
+ EXPECT_EQ(control_label()->GetHeightForWidth(preferred_size.width()),
+ new_badge_label()->GetHeightForWidth(preferred_size.width()));
+ EXPECT_EQ(control_label()->GetHeightForWidth(preferred_size.width() / 2),
+ new_badge_label()->GetHeightForWidth(preferred_size.width() / 2));
+}
+
+TEST_F(NewBadgeLabelTest, NoBadgeLayoutsAreTheSame) {
+ new_badge_label()->SetDisplayNewBadge(false);
+ widget()->Show();
+ widget()->LayoutRootViewIfNecessary();
+ EXPECT_EQ(control_label()->size(), new_badge_label()->size());
+ EXPECT_EQ(control_label()->GetInsets(), new_badge_label()->GetInsets());
+ EXPECT_EQ(nullptr,
+ new_badge_label()->GetProperty(views::kInternalPaddingKey));
+}
+
+TEST_F(NewBadgeLabelTest, WithBadgeReportsDifferentSizes) {
+ // Width should be less for the control than for the new badge label.
+ EXPECT_LT(control_label()->GetPreferredSize().width(),
+ new_badge_label()->GetPreferredSize().width());
+ EXPECT_LT(control_label()->GetMinimumSize().width(),
+ new_badge_label()->GetMinimumSize().width());
+ // Height should be less or the same for the control than for the new badge
+ // label.
+ EXPECT_LE(control_label()->GetPreferredSize().height(),
+ new_badge_label()->GetPreferredSize().height());
+ EXPECT_LE(control_label()->GetMinimumSize().height(),
+ new_badge_label()->GetMinimumSize().height());
+}
+
+TEST_F(NewBadgeLabelTest, WithBadgeLayoutsAreDifferent) {
+ widget()->Show();
+ widget()->LayoutRootViewIfNecessary();
+ // Width should be less for the control than for the new badge label.
+ EXPECT_LT(control_label()->size().width(), new_badge_label()->size().width());
+ // Height should be less or the same for the control than for the new badge
+ // label.
+ EXPECT_LE(control_label()->size().height(),
+ new_badge_label()->size().height());
+
+ EXPECT_NE(control_label()->GetInsets(), new_badge_label()->GetInsets());
+ EXPECT_NE(nullptr,
+ new_badge_label()->GetProperty(views::kInternalPaddingKey));
+}
+
+TEST_F(NewBadgeLabelTest, SetDisplayNewBadgeCorrectlyAffectsCalculations) {
+ EXPECT_TRUE(new_badge_label()->GetDisplayNewBadge());
+
+ // Default is true. Setting it again should have no effect.
+ new_badge_label()->SetDisplayNewBadge(true);
+ EXPECT_TRUE(new_badge_label()->GetDisplayNewBadge());
+ EXPECT_LT(control_label()->GetPreferredSize().width(),
+ new_badge_label()->GetPreferredSize().width());
+
+ // Toggle to false, observe correct behavior.
+ new_badge_label()->SetDisplayNewBadge(false);
+ EXPECT_FALSE(new_badge_label()->GetDisplayNewBadge());
+ EXPECT_EQ(control_label()->GetPreferredSize().width(),
+ new_badge_label()->GetPreferredSize().width());
+
+ // Set to false again, no change.
+ new_badge_label()->SetDisplayNewBadge(false);
+ EXPECT_FALSE(new_badge_label()->GetDisplayNewBadge());
+ EXPECT_EQ(control_label()->GetPreferredSize().width(),
+ new_badge_label()->GetPreferredSize().width());
+
+ // Set back to true and verify default behavior.
+ new_badge_label()->SetDisplayNewBadge(true);
+ EXPECT_TRUE(new_badge_label()->GetDisplayNewBadge());
+ EXPECT_LT(control_label()->GetPreferredSize().width(),
+ new_badge_label()->GetPreferredSize().width());
+}
+
+} // namespace user_education
diff --git a/chromium/components/user_education_strings.grdp b/chromium/components/user_education_strings.grdp
new file mode 100644
index 00000000000..99e0ba1f1af
--- /dev/null
+++ b/chromium/components/user_education_strings.grdp
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+ <message name="IDS_CLOSE_TUTORIAL" desc="Accessible name used by a screen reader for the tutorial bubble close button." is_accessibility_with_no_ui="true">
+ Close tutorial
+ </message>
+ <message name="IDS_CLOSE_PROMO" desc="Accessible name used by a screen reader for the in-product-help bubble close button." is_accessibility_with_no_ui="true">
+ Close help bubble
+ </message>
+ <if expr="use_titlecase">
+ <message name="IDS_PROMO_DISMISS_BUTTON" desc="Text shown on the button for the user to acknowledge that they understand the message">
+ Got It
+ </message>
+ <message name="IDS_PROMO_SNOOZE_BUTTON" desc="Text shown on the button to snooze a promotional UI, which indicates that users want to see this promotion at a later time">
+ Remind Me Later
+ </message>
+ <message name="IDS_PROMO_SHOW_TUTORIAL_BUTTON" desc="Text shown on a promo bubble button for the user to initiate a tutorial after being asked if they want to learn more.">
+ Show Me How
+ </message>
+ </if>
+ <if expr="not use_titlecase">
+ <message name="IDS_PROMO_DISMISS_BUTTON" desc="Text shown on the button for the user to acknowledge that they understand the message">
+ Got it
+ </message>
+ <message name="IDS_PROMO_SNOOZE_BUTTON" desc="Text shown on the button to snooze a promotional UI, which indicates that users want to see this promotion at a later time">
+ Remind me later
+ </message>
+ <message name="IDS_PROMO_SHOW_TUTORIAL_BUTTON" desc="Text shown on a promo bubble button for the user to initiate a tutorial after being asked if they want to learn more.">
+ Show me how
+ </message>
+ </if>
+ <!-- Strings associated with help bubbles, IPH, and other promos. -->
+ <message name="IDS_TUTORIAL_RESTART_TUTORIAL" desc="The text shown on the button for restarting a tutorial">
+ Restart tutorial
+ </message>
+ <message name="IDS_TUTORIAL_CLOSE_TUTORIAL" desc="The text shown on the button for closing a tutorial">
+ Close
+ </message>
+
+</grit-part>
diff --git a/chromium/components/user_education_strings_grdp/IDS_PROMO_DISMISS_BUTTON.png.sha1 b/chromium/components/user_education_strings_grdp/IDS_PROMO_DISMISS_BUTTON.png.sha1
new file mode 100644
index 00000000000..7e25922a822
--- /dev/null
+++ b/chromium/components/user_education_strings_grdp/IDS_PROMO_DISMISS_BUTTON.png.sha1
@@ -0,0 +1 @@
+c09432bf41131fb1758631e16958925a4228d8b3 \ No newline at end of file
diff --git a/chromium/components/user_education_strings_grdp/IDS_PROMO_SHOW_TUTORIAL_BUTTON.png.sha1 b/chromium/components/user_education_strings_grdp/IDS_PROMO_SHOW_TUTORIAL_BUTTON.png.sha1
new file mode 100644
index 00000000000..2856f8498b5
--- /dev/null
+++ b/chromium/components/user_education_strings_grdp/IDS_PROMO_SHOW_TUTORIAL_BUTTON.png.sha1
@@ -0,0 +1 @@
+fbe4052fec2c0001f6cded7e573c2cbff6083daa \ No newline at end of file
diff --git a/chromium/components/user_education_strings_grdp/IDS_PROMO_SNOOZE_BUTTON.png.sha1 b/chromium/components/user_education_strings_grdp/IDS_PROMO_SNOOZE_BUTTON.png.sha1
new file mode 100644
index 00000000000..7e25922a822
--- /dev/null
+++ b/chromium/components/user_education_strings_grdp/IDS_PROMO_SNOOZE_BUTTON.png.sha1
@@ -0,0 +1 @@
+c09432bf41131fb1758631e16958925a4228d8b3 \ No newline at end of file
diff --git a/chromium/components/user_education_strings_grdp/IDS_TUTORIAL_CLOSE_TUTORIAL.png.sha1 b/chromium/components/user_education_strings_grdp/IDS_TUTORIAL_CLOSE_TUTORIAL.png.sha1
new file mode 100644
index 00000000000..65897a5c52b
--- /dev/null
+++ b/chromium/components/user_education_strings_grdp/IDS_TUTORIAL_CLOSE_TUTORIAL.png.sha1
@@ -0,0 +1 @@
+3aa389d61a8dd31ca183a93d5dac530dcc9a91f5 \ No newline at end of file
diff --git a/chromium/components/user_education_strings_grdp/IDS_TUTORIAL_RESTART_TUTORIAL.png.sha1 b/chromium/components/user_education_strings_grdp/IDS_TUTORIAL_RESTART_TUTORIAL.png.sha1
new file mode 100644
index 00000000000..65897a5c52b
--- /dev/null
+++ b/chromium/components/user_education_strings_grdp/IDS_TUTORIAL_RESTART_TUTORIAL.png.sha1
@@ -0,0 +1 @@
+3aa389d61a8dd31ca183a93d5dac530dcc9a91f5 \ No newline at end of file
diff --git a/chromium/components/user_manager/user_manager.cc b/chromium/components/user_manager/user_manager.cc
index ed044419516..f97b7d92c95 100644
--- a/chromium/components/user_manager/user_manager.cc
+++ b/chromium/components/user_manager/user_manager.cc
@@ -19,6 +19,10 @@ void UserManager::Observer::LocalStateChanged(UserManager* user_manager) {}
void UserManager::Observer::OnUserImageChanged(const User& user) {}
+void UserManager::Observer::OnUserImageIsEnterpriseManagedChanged(
+ const User& user,
+ bool is_enterprise_managed) {}
+
void UserManager::Observer::OnUserProfileImageUpdateFailed(const User& user) {}
void UserManager::Observer::OnUserProfileImageUpdated(
diff --git a/chromium/components/user_manager/user_manager.h b/chromium/components/user_manager/user_manager.h
index e9361659cdc..f364889092f 100644
--- a/chromium/components/user_manager/user_manager.h
+++ b/chromium/components/user_manager/user_manager.h
@@ -50,6 +50,11 @@ class USER_MANAGER_EXPORT UserManager {
// Called when the image of the given user is changed.
virtual void OnUserImageChanged(const User& user);
+ // Called when the user image enterprise state of the given user is changed.
+ virtual void OnUserImageIsEnterpriseManagedChanged(
+ const User& user,
+ bool is_enterprise_managed);
+
// Called when the profile image download for the given user fails or
// user has the default profile image or no porfile image at all.
virtual void OnUserProfileImageUpdateFailed(const User& user);
@@ -262,6 +267,9 @@ class USER_MANAGER_EXPORT UserManager {
virtual void SaveUserDisplayEmail(const AccountId& account_id,
const std::string& display_email) = 0;
+ // Returns stored user type or USER_TYPE_REGULAR by default.
+ virtual UserType GetUserType(const AccountId& account_id) = 0;
+
// Saves user's type for |user| into local state preferences.
virtual void SaveUserType(const User* user) = 0;
@@ -332,6 +340,9 @@ class USER_MANAGER_EXPORT UserManager {
virtual void NotifyLocalStateChanged() = 0;
virtual void NotifyUserImageChanged(const User& user) = 0;
+ virtual void NotifyUserImageIsEnterpriseManagedChanged(
+ const User& user,
+ bool is_enterprise_managed) = 0;
virtual void NotifyUserProfileImageUpdateFailed(const User& user) = 0;
virtual void NotifyUserProfileImageUpdated(
const User& user,
diff --git a/chromium/components/user_manager/user_manager_base.cc b/chromium/components/user_manager/user_manager_base.cc
index 1aeed9fb62f..d97abe6a2ef 100644
--- a/chromium/components/user_manager/user_manager_base.cc
+++ b/chromium/components/user_manager/user_manager_base.cc
@@ -358,7 +358,10 @@ void UserManagerBase::RemoveNonOwnerUserInternal(const AccountId& account_id,
NotifyUserToBeRemoved(account_id);
AsyncRemoveCryptohome(account_id);
RemoveUserFromList(account_id);
- NotifyUserRemoved(account_id, reason);
+ // |account_id| cannot be used after the |RemoveUserFromList| call, use
+ // |account_id_copy| instead if needed.
+
+ NotifyUserRemoved(account_id_copy, reason);
if (delegate)
delegate->OnUserRemoved(account_id_copy);
@@ -507,6 +510,12 @@ void UserManagerBase::SaveUserDisplayEmail(const AccountId& account_id,
display_email_update->SetStringKey(account_id.GetUserEmail(), display_email);
}
+UserType UserManagerBase::GetUserType(const AccountId& account_id) {
+ const base::Value* prefs_user_types =
+ GetLocalState()->GetDictionary(kUserType);
+ return GetStoredUserType(prefs_user_types, account_id);
+}
+
void UserManagerBase::SaveUserType(const User* user) {
DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
@@ -737,6 +746,15 @@ void UserManagerBase::NotifyUserImageChanged(const User& user) {
observer.OnUserImageChanged(user);
}
+void UserManagerBase::NotifyUserImageIsEnterpriseManagedChanged(
+ const User& user,
+ bool is_enterprise_managed) {
+ DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
+ for (auto& observer : observer_list_) {
+ observer.OnUserImageIsEnterpriseManagedChanged(user, is_enterprise_managed);
+ }
+}
+
void UserManagerBase::NotifyUserProfileImageUpdateFailed(const User& user) {
DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
for (auto& observer : observer_list_)
diff --git a/chromium/components/user_manager/user_manager_base.h b/chromium/components/user_manager/user_manager_base.h
index 857ee12723f..5a089398a86 100644
--- a/chromium/components/user_manager/user_manager_base.h
+++ b/chromium/components/user_manager/user_manager_base.h
@@ -111,6 +111,7 @@ class USER_MANAGER_EXPORT UserManagerBase : public UserManager {
std::u16string GetUserDisplayName(const AccountId& account_id) const override;
void SaveUserDisplayEmail(const AccountId& account_id,
const std::string& display_email) override;
+ UserType GetUserType(const AccountId& account_id) override;
void SaveUserType(const User* user) override;
void UpdateUserAccountData(const AccountId& account_id,
const UserAccountData& account_data) override;
@@ -141,6 +142,9 @@ class USER_MANAGER_EXPORT UserManagerBase : public UserManager {
UserManager::UserSessionStateObserver* obs) override;
void NotifyLocalStateChanged() override;
void NotifyUserImageChanged(const User& user) override;
+ void NotifyUserImageIsEnterpriseManagedChanged(
+ const User& user,
+ bool is_enterprise_managed) override;
void NotifyUserProfileImageUpdateFailed(const User& user) override;
void NotifyUserProfileImageUpdated(
const User& user,
diff --git a/chromium/components/user_notes/README.md b/chromium/components/user_notes/README.md
index ede064bf344..0458a8e6839 100644
--- a/chromium/components/user_notes/README.md
+++ b/chromium/components/user_notes/README.md
@@ -7,6 +7,9 @@ feature.
- `browser/`
- Contains business logic classes that live in the browser process.
+- `interfaces/`
+ - Contains abstract interfaces for contracts between components, as well as
+ some shared data structure / container classes.
- `model/`
- Contains model classes that represent core concepts of the User Notes
feature, such as a note's metadata, a note's target, a note's body, etc.
diff --git a/chromium/components/user_notes/browser/BUILD.gn b/chromium/components/user_notes/browser/BUILD.gn
index d507f6a1e7e..f3a4e23bb8c 100644
--- a/chromium/components/user_notes/browser/BUILD.gn
+++ b/chromium/components/user_notes/browser/BUILD.gn
@@ -4,14 +4,16 @@
static_library("browser") {
sources = [
+ "frame_user_note_changes.cc",
+ "frame_user_note_changes.h",
"user_note_instance.cc",
"user_note_instance.h",
+ "user_note_manager.cc",
+ "user_note_manager.h",
"user_note_service.cc",
"user_note_service.h",
- "user_notes_change.cc",
- "user_notes_change.h",
- "user_notes_manager.cc",
- "user_notes_manager.h",
+ "user_note_utils.cc",
+ "user_note_utils.h",
]
deps = [
@@ -22,22 +24,31 @@ static_library("browser") {
"//components/user_notes/model",
"//components/user_notes/storage",
"//content/public/browser",
+ "//ui/gfx/geometry:geometry",
]
}
source_set("unit_tests") {
testonly = true
sources = [
+ "frame_user_note_changes_unittest.cc",
+ "user_note_base_test.cc",
+ "user_note_base_test.h",
+ "user_note_instance_unittest.cc",
+ "user_note_manager_unittest.cc",
"user_note_service_unittest.cc",
- "user_notes_manager_unittest.cc",
+ "user_note_utils_unittest.cc",
]
deps = [
":browser",
"//base/test:test_support",
"//components/user_notes:features",
+ "//components/user_notes/interfaces",
+ "//components/user_notes/model",
"//components/user_notes/model:unit_tests",
"//content/public/browser",
+ "//content/test:test_support",
"//testing/gtest",
]
}
diff --git a/chromium/components/user_notes/browser/DEPS b/chromium/components/user_notes/browser/DEPS
index 1c35d9ca694..6b1fe342a12 100644
--- a/chromium/components/user_notes/browser/DEPS
+++ b/chromium/components/user_notes/browser/DEPS
@@ -1,3 +1,8 @@
include_rules = [
"+content/public/browser",
+ "+content/public/test",
+ "+mojo/public/cpp/bindings",
+ "+services/service_manager/public/cpp/interface_provider.h",
+ "+third_party/blink/public/mojom/annotation",
+ "+ui/gfx/geometry",
]
diff --git a/chromium/components/user_notes/browser/frame_user_note_changes.cc b/chromium/components/user_notes/browser/frame_user_note_changes.cc
new file mode 100644
index 00000000000..dbe4bd84444
--- /dev/null
+++ b/chromium/components/user_notes/browser/frame_user_note_changes.cc
@@ -0,0 +1,85 @@
+// Copyright 2022 The Chromium 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/user_notes/browser/frame_user_note_changes.h"
+
+#include "base/barrier_closure.h"
+#include "components/user_notes/browser/user_note_manager.h"
+#include "components/user_notes/model/user_note.h"
+#include "components/user_notes/model/user_note_target.h"
+#include "content/public/browser/render_frame_host.h"
+
+namespace user_notes {
+
+FrameUserNoteChanges::FrameUserNoteChanges(
+ base::SafeRef<UserNoteService> service,
+ content::RenderFrameHost* rfh,
+ const ChangeList& notes_added,
+ const ChangeList& notes_modified,
+ const ChangeList& notes_removed)
+ : service_(service),
+ rfh_(rfh),
+ notes_added_(notes_added),
+ notes_modified_(notes_modified),
+ notes_removed_(notes_removed) {
+ DCHECK(!notes_added_.empty() || !notes_modified_.empty() ||
+ !notes_removed_.empty());
+ DCHECK(rfh_);
+}
+
+FrameUserNoteChanges::FrameUserNoteChanges(
+ base::SafeRef<UserNoteService> service,
+ content::RenderFrameHost* rfh,
+ ChangeList&& notes_added,
+ ChangeList&& notes_modified,
+ ChangeList&& notes_removed)
+ : service_(service),
+ rfh_(rfh),
+ notes_added_(std::move(notes_added)),
+ notes_modified_(std::move(notes_modified)),
+ notes_removed_(std::move(notes_removed)) {
+ DCHECK(!notes_added_.empty() || !notes_modified_.empty() ||
+ !notes_removed_.empty());
+ DCHECK(rfh_);
+}
+
+FrameUserNoteChanges::FrameUserNoteChanges(FrameUserNoteChanges&& other) =
+ default;
+
+FrameUserNoteChanges::~FrameUserNoteChanges() = default;
+
+void FrameUserNoteChanges::Apply(base::OnceClosure callback) {
+ UserNoteManager* manager = UserNoteManager::GetForPage(rfh_->GetPage());
+ DCHECK(manager);
+
+ // Removed notes can be synchronously deleted from the note manager. There is
+ // no need to wait for the async removal of the page highlights on the
+ // renderer side.
+ for (const base::UnguessableToken& note_id : notes_removed_) {
+ manager->RemoveNote(note_id);
+ }
+
+ // For added notes, the async highlight creation on the renderer side must be
+ // awaited, because the order in which notes are shown in the Notes UI depends
+ // on the order of the corresponding highlights in the page. Use a barrier
+ // closure to wait until all note highlights have been created in the page.
+ base::RepeatingClosure barrier =
+ base::BarrierClosure(notes_added_.size(), std::move(callback));
+ for (const base::UnguessableToken& note_id : notes_added_) {
+ const UserNote* note = service_->GetNoteModel(note_id);
+ DCHECK(note);
+
+ std::unique_ptr<UserNoteInstance> instance_unique =
+ MakeNoteInstance(note, manager);
+ manager->AddNoteInstance(std::move(instance_unique), barrier);
+ }
+}
+
+std::unique_ptr<UserNoteInstance> FrameUserNoteChanges::MakeNoteInstance(
+ const UserNote* note_model,
+ UserNoteManager* manager) const {
+ return std::make_unique<UserNoteInstance>(note_model->GetSafeRef(), manager);
+}
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/frame_user_note_changes.h b/chromium/components/user_notes/browser/frame_user_note_changes.h
new file mode 100644
index 00000000000..053570bfc3b
--- /dev/null
+++ b/chromium/components/user_notes/browser/frame_user_note_changes.h
@@ -0,0 +1,70 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_USER_NOTES_BROWSER_FRAME_USER_NOTE_CHANGES_H_
+#define COMPONENTS_USER_NOTES_BROWSER_FRAME_USER_NOTE_CHANGES_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/safe_ref.h"
+#include "base/unguessable_token.h"
+#include "components/user_notes/browser/user_note_instance.h"
+#include "components/user_notes/browser/user_note_service.h"
+
+namespace content {
+class RenderFrameHost;
+} // namespace content
+
+namespace user_notes {
+
+// A container to represent changes to a frame's displayed User Notes. Includes
+// the logic to apply the changes in the associated frame.
+class FrameUserNoteChanges {
+ public:
+ using ChangeList = std::vector<base::UnguessableToken>;
+
+ FrameUserNoteChanges(base::SafeRef<UserNoteService> service,
+ content::RenderFrameHost* rfh,
+ const ChangeList& notes_added,
+ const ChangeList& notes_modified,
+ const ChangeList& notes_removed);
+ FrameUserNoteChanges(base::SafeRef<UserNoteService> service,
+ content::RenderFrameHost* rfh,
+ ChangeList&& notes_added,
+ ChangeList&& notes_modified,
+ ChangeList&& notes_removed);
+ FrameUserNoteChanges(const FrameUserNoteChanges&) = delete;
+ FrameUserNoteChanges& operator=(const FrameUserNoteChanges&) = delete;
+ FrameUserNoteChanges(FrameUserNoteChanges&& other);
+ virtual ~FrameUserNoteChanges();
+
+ // Kicks off the asynchronous logic to add and remove highlights in the frame
+ // as necessary. Invokes the provided callback after the changes have fully
+ // propagated to the note manager and the new notes have had their highlights
+ // created in the web page.
+ void Apply(base::OnceClosure callback);
+
+ protected:
+ // Called by `Apply()` to construct a new note instance pointing to the
+ // provided model. Can be overridden by tests to construct a mocked instance.
+ virtual std::unique_ptr<UserNoteInstance> MakeNoteInstance(
+ const UserNote* note_model,
+ UserNoteManager* manager) const;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(UserNoteUtilsTest, CalculateNoteChanges);
+
+ base::SafeRef<UserNoteService> service_;
+ raw_ptr<content::RenderFrameHost> rfh_;
+ ChangeList notes_added_;
+ ChangeList notes_modified_;
+ ChangeList notes_removed_;
+};
+
+} // namespace user_notes
+
+#endif // COMPONENTS_USER_NOTES_BROWSER_FRAME_USER_NOTE_CHANGES_H_
diff --git a/chromium/components/user_notes/browser/frame_user_note_changes_unittest.cc b/chromium/components/user_notes/browser/frame_user_note_changes_unittest.cc
new file mode 100644
index 00000000000..5fabf4e8c4a
--- /dev/null
+++ b/chromium/components/user_notes/browser/frame_user_note_changes_unittest.cc
@@ -0,0 +1,179 @@
+// Copyright 2022 The Chromium 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/user_notes/browser/frame_user_note_changes.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/safe_ref.h"
+#include "base/test/bind.h"
+#include "base/unguessable_token.h"
+#include "components/user_notes/browser/user_note_base_test.h"
+#include "components/user_notes/browser/user_note_instance.h"
+#include "components/user_notes/model/user_note.h"
+#include "content/public/browser/render_frame_host.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Mock;
+
+namespace user_notes {
+
+namespace {
+
+// A mock for a note instance that synchronously invokes the callback when
+// initializing the text highlight.
+class MockUserNoteInstance : public UserNoteInstance {
+ public:
+ explicit MockUserNoteInstance(base::SafeRef<UserNote> model_ref,
+ UserNoteManager* manager)
+ : UserNoteInstance(model_ref, manager) {}
+
+ MOCK_METHOD(void,
+ InitializeHighlightIfNeeded,
+ (base::OnceClosure callback),
+ (override));
+};
+
+// Partially mock the object under test so calls to `MakeNoteInstance` can be
+// intercepted, allowing the tests to create mocked instances.
+class MockFrameUserNoteChanges : public FrameUserNoteChanges {
+ public:
+ MockFrameUserNoteChanges(
+ base::SafeRef<UserNoteService> service,
+ content::RenderFrameHost* rfh,
+ const FrameUserNoteChanges::ChangeList& notes_added,
+ const FrameUserNoteChanges::ChangeList& notes_modified,
+ const FrameUserNoteChanges::ChangeList& notes_removed)
+ : FrameUserNoteChanges(service,
+ rfh,
+ notes_added,
+ notes_modified,
+ notes_removed) {}
+
+ MOCK_METHOD(std::unique_ptr<UserNoteInstance>,
+ MakeNoteInstance,
+ (const UserNote* note_model, UserNoteManager* manager),
+ (const override));
+};
+
+void MockInitializeHighlightIfNeeded(base::OnceClosure callback) {
+ std::move(callback).Run();
+}
+
+std::unique_ptr<UserNoteInstance> MockMakeNoteInstance(
+ const UserNote* note_model,
+ UserNoteManager* manager) {
+ auto instance_mock =
+ std::make_unique<MockUserNoteInstance>(note_model->GetSafeRef(), manager);
+
+ EXPECT_CALL(*instance_mock, InitializeHighlightIfNeeded(_))
+ .Times(1)
+ .WillOnce(&MockInitializeHighlightIfNeeded);
+
+ return instance_mock;
+}
+
+} // namespace
+
+class FrameUserNoteChangesTest : public UserNoteBaseTest {};
+
+// Tests that added notes correctly kick off highlight initialization on the
+// renderer side, and new instances are correctly added to the note manager.
+TEST_F(FrameUserNoteChangesTest, ApplyAddedNotes) {
+ AddNewNotesToService(3);
+ UserNoteManager* m = ConfigureNewManager();
+ AddNewInstanceToManager(m, note_ids_[0]);
+
+ std::vector<base::UnguessableToken> added({note_ids_[1], note_ids_[2]});
+ std::vector<base::UnguessableToken> modified;
+ std::vector<base::UnguessableToken> removed;
+
+ auto mock_changes = std::make_unique<MockFrameUserNoteChanges>(
+ note_service_->GetSafeRef(), web_contents_list_[0]->GetPrimaryMainFrame(),
+ added, modified, removed);
+
+ EXPECT_CALL(*mock_changes, MakeNoteInstance(_, _))
+ .Times(2)
+ .WillRepeatedly(&MockMakeNoteInstance);
+
+ bool callback_called = false;
+ mock_changes->Apply(
+ base::BindLambdaForTesting([&]() { callback_called = true; }));
+
+ // The mocks ensure the callback is invoked synchronously, so verifications
+ // can happen immediately.
+ EXPECT_TRUE(callback_called);
+ EXPECT_EQ(InstanceMapSize(m), 3u);
+ EXPECT_TRUE(m->GetNoteInstance(note_ids_[0]));
+ EXPECT_TRUE(m->GetNoteInstance(note_ids_[1]));
+ EXPECT_TRUE(m->GetNoteInstance(note_ids_[2]));
+}
+
+// Tests that modified notes don't impact instances in the note manager.
+TEST_F(FrameUserNoteChangesTest, ApplyModifiedNotes) {
+ AddNewNotesToService(3);
+ UserNoteManager* m = ConfigureNewManager();
+ AddNewInstanceToManager(m, note_ids_[0]);
+ AddNewInstanceToManager(m, note_ids_[1]);
+ AddNewInstanceToManager(m, note_ids_[2]);
+
+ std::vector<base::UnguessableToken> added;
+ std::vector<base::UnguessableToken> modified({note_ids_[0], note_ids_[2]});
+ std::vector<base::UnguessableToken> removed;
+
+ auto mock_changes = std::make_unique<MockFrameUserNoteChanges>(
+ note_service_->GetSafeRef(), web_contents_list_[0]->GetPrimaryMainFrame(),
+ added, modified, removed);
+
+ EXPECT_CALL(*mock_changes, MakeNoteInstance(_, _)).Times(0);
+
+ bool callback_called = false;
+ mock_changes->Apply(
+ base::BindLambdaForTesting([&]() { callback_called = true; }));
+
+ // The mocks ensure the callback is invoked synchronously, so verifications
+ // can happen immediately.
+ EXPECT_TRUE(callback_called);
+ EXPECT_EQ(InstanceMapSize(m), 3u);
+ EXPECT_TRUE(m->GetNoteInstance(note_ids_[0]));
+ EXPECT_TRUE(m->GetNoteInstance(note_ids_[1]));
+ EXPECT_TRUE(m->GetNoteInstance(note_ids_[2]));
+}
+
+// Tests that removed notes correctly have their instances removed from the
+// note manager.
+TEST_F(FrameUserNoteChangesTest, ApplyRemovedNotes) {
+ AddNewNotesToService(3);
+ UserNoteManager* m = ConfigureNewManager();
+ AddNewInstanceToManager(m, note_ids_[0]);
+ AddNewInstanceToManager(m, note_ids_[1]);
+ AddNewInstanceToManager(m, note_ids_[2]);
+
+ std::vector<base::UnguessableToken> added;
+ std::vector<base::UnguessableToken> modified;
+ std::vector<base::UnguessableToken> removed({note_ids_[0], note_ids_[2]});
+
+ auto mock_changes = std::make_unique<MockFrameUserNoteChanges>(
+ note_service_->GetSafeRef(), web_contents_list_[0]->GetPrimaryMainFrame(),
+ added, modified, removed);
+
+ EXPECT_CALL(*mock_changes, MakeNoteInstance(_, _)).Times(0);
+
+ bool callback_called = false;
+ mock_changes->Apply(
+ base::BindLambdaForTesting([&]() { callback_called = true; }));
+
+ // The mocks ensure the callback is invoked synchronously, so verifications
+ // can happen immediately.
+ EXPECT_TRUE(callback_called);
+ EXPECT_EQ(InstanceMapSize(m), 1u);
+ EXPECT_TRUE(m->GetNoteInstance(note_ids_[1]));
+ EXPECT_FALSE(m->GetNoteInstance(note_ids_[0]));
+ EXPECT_FALSE(m->GetNoteInstance(note_ids_[2]));
+}
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_note_base_test.cc b/chromium/components/user_notes/browser/user_note_base_test.cc
new file mode 100644
index 00000000000..5a83fda9e46
--- /dev/null
+++ b/chromium/components/user_notes/browser/user_note_base_test.cc
@@ -0,0 +1,136 @@
+// Copyright 2022 The Chromium 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/user_notes/browser/user_note_base_test.h"
+
+#include <memory>
+#include <vector>
+
+#include "components/user_notes/model/user_note_model_test_utils.h"
+#include "components/user_notes/user_notes_features.h"
+#include "content/public/browser/page.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/test/navigation_simulator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace user_notes {
+
+namespace {
+
+const char kBaseUrl[] = "https://www.example.com/";
+
+} // namespace
+
+UserNoteBaseTest::UserNoteBaseTest() {
+ scoped_feature_list_.InitAndEnableFeature(user_notes::kUserNotes);
+}
+
+UserNoteBaseTest::~UserNoteBaseTest() = default;
+
+void UserNoteBaseTest::SetUp() {
+ content::RenderViewHostTestHarness::SetUp();
+ note_service_ = std::make_unique<UserNoteService>(/*delegate=*/nullptr);
+}
+
+void UserNoteBaseTest::TearDown() {
+ // Owned web contentses must be destroyed before the test harness.
+ web_contents_list_.clear();
+ content::RenderViewHostTestHarness::TearDown();
+}
+
+void UserNoteBaseTest::AddNewNotesToService(size_t count) {
+ for (size_t i = 0; i < count; ++i) {
+ size_t last = note_ids_.size();
+ note_ids_.push_back(base::UnguessableToken::Create());
+ UserNoteService::ModelMapEntry entry(std::make_unique<UserNote>(
+ note_ids_[last], GetTestUserNoteMetadata(), GetTestUserNoteBody(),
+ GetTestUserNotePageTarget()));
+ note_service_->model_map_.emplace(note_ids_[last], std::move(entry));
+ }
+}
+
+void UserNoteBaseTest::AddPartialNotesToService(size_t count) {
+ for (size_t i = 0; i < count; ++i) {
+ size_t last = note_ids_.size();
+ note_ids_.push_back(base::UnguessableToken::Create());
+ UserNoteService::ModelMapEntry entry(std::make_unique<UserNote>(
+ note_ids_[last], GetTestUserNoteMetadata(), GetTestUserNoteBody(),
+ GetTestUserNotePageTarget()));
+ note_service_->creation_map_.emplace(note_ids_[last], std::move(entry));
+ }
+}
+
+UserNoteManager* UserNoteBaseTest::ConfigureNewManager() {
+ // Create a test frame and navigate it to a unique URL.
+ std::unique_ptr<content::WebContents> wc = CreateTestWebContents();
+ content::RenderFrameHostTester::For(wc->GetPrimaryMainFrame())
+ ->InitializeRenderFrameIfNeeded();
+ content::NavigationSimulator::NavigateAndCommitFromBrowser(
+ wc.get(),
+ GURL(kBaseUrl + base::NumberToString(web_contents_list_.size())));
+
+ // Create and attach a `UserNoteManager` to the primary page.
+ content::Page& page = wc->GetPrimaryPage();
+ UserNoteManager::CreateForPage(page, note_service_->GetSafeRef());
+ UserNoteManager* note_manager = UserNoteManager::GetForPage(page);
+ DCHECK(note_manager);
+ web_contents_list_.emplace_back(std::move(wc));
+
+ return note_manager;
+}
+
+void UserNoteBaseTest::AddNewInstanceToManager(UserNoteManager* manager,
+ base::UnguessableToken note_id) {
+ DCHECK(manager);
+ const auto& entry_it = note_service_->model_map_.find(note_id);
+ ASSERT_FALSE(entry_it == note_service_->model_map_.end());
+ manager->AddNoteInstance(std::make_unique<UserNoteInstance>(
+ entry_it->second.model->GetSafeRef(), manager));
+}
+
+size_t UserNoteBaseTest::ManagerCountForId(
+ const base::UnguessableToken& note_id) {
+ const auto& entry_it = note_service_->model_map_.find(note_id);
+ if (entry_it == note_service_->model_map_.end()) {
+ return -1;
+ }
+ return entry_it->second.managers.size();
+}
+
+bool UserNoteBaseTest::DoesModelExist(const base::UnguessableToken& note_id) {
+ const auto& entry_it = note_service_->model_map_.find(note_id);
+ return entry_it != note_service_->model_map_.end();
+}
+
+bool UserNoteBaseTest::DoesPartialModelExist(
+ const base::UnguessableToken& note_id) {
+ const auto& entry_it = note_service_->creation_map_.find(note_id);
+ return entry_it != note_service_->creation_map_.end();
+}
+
+bool UserNoteBaseTest::DoesManagerExistForId(
+ const base::UnguessableToken& note_id,
+ UserNoteManager* manager) {
+ const auto& model_entry_it = note_service_->model_map_.find(note_id);
+ if (model_entry_it == note_service_->model_map_.end()) {
+ return false;
+ }
+ const auto& manager_entry_it = model_entry_it->second.managers.find(manager);
+ return manager_entry_it != model_entry_it->second.managers.end();
+}
+
+size_t UserNoteBaseTest::ModelMapSize() {
+ return note_service_->model_map_.size();
+}
+
+size_t UserNoteBaseTest::CreationMapSize() {
+ return note_service_->creation_map_.size();
+}
+
+size_t UserNoteBaseTest::InstanceMapSize(UserNoteManager* manager) {
+ DCHECK(manager);
+ return manager->instance_map_.size();
+}
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_note_base_test.h b/chromium/components/user_notes/browser/user_note_base_test.h
new file mode 100644
index 00000000000..9c18b56af16
--- /dev/null
+++ b/chromium/components/user_notes/browser/user_note_base_test.h
@@ -0,0 +1,65 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_BASE_TEST_H_
+#define COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_BASE_TEST_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/test/scoped_feature_list.h"
+#include "base/unguessable_token.h"
+#include "components/user_notes/browser/user_note_manager.h"
+#include "components/user_notes/browser/user_note_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_renderer_host.h"
+
+namespace user_notes {
+
+// A base test harness for User Notes unit tests. The harness sets up a note
+// service and exposes methods to create new note models, as well as methods to
+// create and manipulate note managers attached to mock pages.
+class UserNoteBaseTest : public content::RenderViewHostTestHarness {
+ public:
+ UserNoteBaseTest();
+ ~UserNoteBaseTest() override;
+
+ protected:
+ void SetUp() override;
+
+ void TearDown() override;
+
+ void AddNewNotesToService(size_t count);
+
+ void AddPartialNotesToService(size_t count);
+
+ UserNoteManager* ConfigureNewManager();
+
+ void AddNewInstanceToManager(UserNoteManager* manager,
+ base::UnguessableToken note_id);
+
+ size_t ManagerCountForId(const base::UnguessableToken& note_id);
+
+ bool DoesModelExist(const base::UnguessableToken& note_id);
+
+ bool DoesPartialModelExist(const base::UnguessableToken& note_id);
+
+ bool DoesManagerExistForId(const base::UnguessableToken& note_id,
+ UserNoteManager* manager);
+
+ size_t ModelMapSize();
+
+ size_t CreationMapSize();
+
+ size_t InstanceMapSize(UserNoteManager* manager);
+
+ base::test::ScopedFeatureList scoped_feature_list_;
+ std::vector<std::unique_ptr<content::WebContents>> web_contents_list_;
+ std::unique_ptr<UserNoteService> note_service_;
+ std::vector<base::UnguessableToken> note_ids_;
+};
+
+} // namespace user_notes
+
+#endif // COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_BASE_TEST_H_
diff --git a/chromium/components/user_notes/browser/user_note_instance.cc b/chromium/components/user_notes/browser/user_note_instance.cc
index 39c10da85d3..c55eb615b08 100644
--- a/chromium/components/user_notes/browser/user_note_instance.cc
+++ b/chromium/components/user_notes/browser/user_note_instance.cc
@@ -4,11 +4,75 @@
#include "components/user_notes/browser/user_note_instance.h"
+#include "components/user_notes/browser/user_note_manager.h"
+#include "components/user_notes/model/user_note_target.h"
+
namespace user_notes {
-UserNoteInstance::UserNoteInstance(base::SafeRef<UserNote> model)
- : model_(model) {}
+UserNoteInstance::UserNoteInstance(base::SafeRef<UserNote> model,
+ UserNoteManager* parent_manager)
+ : UserNoteInstance(model, parent_manager, gfx::Rect()) {}
+
+UserNoteInstance::UserNoteInstance(base::SafeRef<UserNote> model,
+ UserNoteManager* parent_manager,
+ gfx::Rect rect)
+ : model_(model),
+ parent_manager_(parent_manager),
+ rect_(rect),
+ receiver_(this) {}
UserNoteInstance::~UserNoteInstance() = default;
+bool UserNoteInstance::IsDetached() const {
+ return is_initialized_ && rect_.IsEmpty() &&
+ model_->target().type() == UserNoteTarget::TargetType::kPageText;
+}
+
+void UserNoteInstance::InitializeHighlightIfNeeded(base::OnceClosure callback) {
+ DCHECK(!is_initialized_);
+
+ if (model_->target().type() == UserNoteTarget::TargetType::kPage) {
+ // Page-level notes are not associated with text in the page, so there is no
+ // highlight to create on the renderer side.
+ is_initialized_ = true;
+ DCHECK(callback);
+ std::move(callback).Run();
+ } else {
+ did_finish_attachment_callback_ = std::move(callback);
+ InitializeHighlightInternal();
+ }
+}
+
+void UserNoteInstance::DidFinishAttachment(const gfx::Rect& rect) {
+ is_initialized_ = true;
+ rect_ = rect;
+
+ DCHECK(did_finish_attachment_callback_);
+ std::move(did_finish_attachment_callback_).Run();
+}
+
+void UserNoteInstance::OnNoteDetached() {
+ rect_ = gfx::Rect();
+ DCHECK(IsDetached());
+
+ // TODO(gujen): Notify the service so it can invalidate the UI.
+}
+
+void UserNoteInstance::InitializeHighlightInternal() {
+ DCHECK_EQ(model_->target().type(), UserNoteTarget::TargetType::kPageText);
+
+ parent_manager_->note_agent_container()->CreateAgent(
+ receiver_.BindNewPipeAndPassRemote(), agent_.BindNewPipeAndPassReceiver(),
+ blink::mojom::AnnotationType::kUserNote, model_->target().selector());
+
+ // Set a disconnect handler because the renderer can close the pipe at any
+ // moment to signal that the highlight has been removed from the page. It's ok
+ // to use base::unretained here because the note instances are always
+ // destroyed before the manager (the manager's destructor explicitly destroys
+ // them).
+ agent_.set_disconnect_handler(
+ base::BindOnce(&UserNoteManager::RemoveNote,
+ base::Unretained(parent_manager_), model_->id()));
+}
+
} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_note_instance.h b/chromium/components/user_notes/browser/user_note_instance.h
index d55b61aa953..0f8ac9398ef 100644
--- a/chromium/components/user_notes/browser/user_note_instance.h
+++ b/chromium/components/user_notes/browser/user_note_instance.h
@@ -5,26 +5,89 @@
#ifndef COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_INSTANCE_H_
#define COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_INSTANCE_H_
+#include "base/barrier_closure.h"
#include "base/memory/safe_ref.h"
#include "components/user_notes/model/user_note.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/mojom/annotation/annotation.mojom.h"
+#include "ui/gfx/geometry/rect.h"
namespace user_notes {
+class UserNoteManager;
+
// A class that represents the manifestation of a note within a specific web
// page.
-class UserNoteInstance {
+class UserNoteInstance : public blink::mojom::AnnotationAgentHost {
public:
- explicit UserNoteInstance(base::SafeRef<UserNote> model);
- ~UserNoteInstance();
+ // The main constructor.
+ UserNoteInstance(base::SafeRef<UserNote> model,
+ UserNoteManager* parent_manager);
+
+ // A constructor for when the bounding rect of the highlight is known in
+ // advance, for example during the note creation process.
+ UserNoteInstance(base::SafeRef<UserNote> model,
+ UserNoteManager* parent_manager,
+ gfx::Rect rect);
+
+ ~UserNoteInstance() override;
UserNoteInstance(const UserNoteInstance&) = delete;
UserNoteInstance& operator=(const UserNoteInstance&) = delete;
- const UserNote& model() { return *model_; }
+ UserNote& model() const { return *model_; }
+ const gfx::Rect& rect() const { return rect_; }
+
+ // Returns true if the note is detached, false otherwise. A note is considered
+ // detached if all of the following are true:
+ // 1) It has a target text (i.e. it is not page-level);
+ // 2) Its highlight has been initialized already;
+ // 3) Its bounding rect is empty.
+ bool IsDetached() const;
+
+ // If this note is a text-level note, this method kicks off the asynchronous
+ // process to set up the Mojo connection with the corresponding agent in the
+ // renderer process. Otherwise, it invokes the provided callback.
+ // Marked virtual for tests to override.
+ virtual void InitializeHighlightIfNeeded(base::OnceClosure callback);
+
+ // blink::mojom::AnnotationAgentHost implementation.
+ void DidFinishAttachment(const gfx::Rect& rect) override;
+
+ // TODO(gujen) and TODO(bokan): add the following method to the
+ // AnnotationAgentHost interface so it's called when a note becomes detached.
+ // Mark this one as override.
+ void OnNoteDetached();
private:
+ friend class UserNoteInstanceTest;
+
+ // Performs the actual Mojo initialization. Marked virtual for tests to
+ // override.
+ virtual void InitializeHighlightInternal();
+
// A ref to the backing model of this note instance. The model is owned by
// |UserNoteService|. The model is expected to outlive this class.
base::SafeRef<UserNote> model_;
+
+ // The owning note manager.
+ raw_ptr<UserNoteManager> parent_manager_;
+
+ // A rect that corresponds to the location in the webpage where the associated
+ // highlight is. Can be empty if the note is page-level, if the target text
+ // could not be found, or if the highlight hasn't been attached yet.
+ gfx::Rect rect_;
+
+ // Callback to invoke after the renderer agent has initialized.
+ base::OnceClosure did_finish_attachment_callback_;
+
+ // Stores whether this note instance has had its highlight initialized in the
+ // renderer process.
+ bool is_initialized_ = false;
+
+ // Receiver and agent for communication with the renderer process.
+ mojo::Receiver<blink::mojom::AnnotationAgentHost> receiver_;
+ mojo::Remote<blink::mojom::AnnotationAgent> agent_;
};
} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_note_instance_unittest.cc b/chromium/components/user_notes/browser/user_note_instance_unittest.cc
new file mode 100644
index 00000000000..70fe08739e0
--- /dev/null
+++ b/chromium/components/user_notes/browser/user_note_instance_unittest.cc
@@ -0,0 +1,131 @@
+// Copyright 2022 The Chromium 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/user_notes/browser/user_note_instance.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/safe_ref.h"
+#include "base/test/bind.h"
+#include "base/unguessable_token.h"
+#include "components/user_notes/browser/user_note_base_test.h"
+#include "components/user_notes/model/user_note.h"
+#include "components/user_notes/model/user_note_model_test_utils.h"
+#include "content/public/browser/page.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::Mock;
+
+namespace user_notes {
+
+namespace {
+
+// Partially mock the object under test so calls to
+// `InitializeHighlightInternal` can be intercepted to prevent side effects.
+class MockUserNoteInstance : public UserNoteInstance {
+ public:
+ explicit MockUserNoteInstance(base::SafeRef<UserNote> model_ref,
+ UserNoteManager* manager,
+ const gfx::Rect& simulate_attach_rect)
+ : UserNoteInstance(model_ref, manager),
+ attach_rect_(simulate_attach_rect) {}
+
+ const gfx::Rect& attach_rect() { return attach_rect_; };
+
+ MOCK_METHOD(void, InitializeHighlightInternal, (), (override));
+
+ void MockInitializeHighlightInternal() { DidFinishAttachment(attach_rect_); }
+
+ private:
+ gfx::Rect attach_rect_;
+};
+
+} // namespace
+
+class UserNoteInstanceTest : public UserNoteBaseTest {
+ public:
+ void AddNewNoteToService(UserNoteTarget::TargetType type) {
+ base::UnguessableToken id = base::UnguessableToken::Create();
+ note_ids_.emplace_back(id);
+
+ // Original text, target URL and selector are not used in these tests.
+ auto target = std::make_unique<UserNoteTarget>(
+ type, /*original_text=*/"", GURL("https://www.example.com/"),
+ /*selector=*/"");
+ UserNoteService::ModelMapEntry entry(
+ std::make_unique<UserNote>(id, GetTestUserNoteMetadata(),
+ GetTestUserNoteBody(), std::move(target)));
+ note_service_->model_map_.emplace(id, std::move(entry));
+ }
+
+ std::unique_ptr<MockUserNoteInstance> CreateInstanceForId(
+ base::UnguessableToken note_id,
+ UserNoteManager* manager,
+ const gfx::Rect& simulate_attach_rect) {
+ const auto& entry_it = note_service_->model_map_.find(note_id);
+ return std::make_unique<MockUserNoteInstance>(
+ entry_it->second.model->GetSafeRef(), manager, simulate_attach_rect);
+ }
+
+ bool IsNoteInstanceInitialized(UserNoteInstance* instance) {
+ return instance->is_initialized_;
+ }
+};
+
+// Tests that async highlight initialization is skipped for page-level notes.
+TEST_F(UserNoteInstanceTest, InitializeHighlightSkipForPageLevelNote) {
+ AddNewNoteToService(UserNoteTarget::TargetType::kPage);
+ UserNoteManager* manager = ConfigureNewManager();
+
+ // Page-level notes should expect an empty rect.
+ gfx::Rect empty_rect;
+ std::unique_ptr<MockUserNoteInstance> instance =
+ CreateInstanceForId(note_ids_[0], manager, empty_rect);
+
+ EXPECT_CALL(*instance, InitializeHighlightInternal).Times(0);
+
+ bool callback_called = false;
+ instance->InitializeHighlightIfNeeded(
+ base::BindLambdaForTesting([&]() { callback_called = true; }));
+
+ // The mocks ensure the callback is invoked synchronously, so verifications
+ // can happen immediately.
+ EXPECT_TRUE(callback_called);
+ EXPECT_TRUE(IsNoteInstanceInitialized(instance.get()));
+ EXPECT_EQ(instance->rect(), empty_rect);
+}
+
+// Tests that async highlight initialization works as expected for text notes.
+TEST_F(UserNoteInstanceTest, InitializeHighlightTextNote) {
+ AddNewNoteToService(UserNoteTarget::TargetType::kPageText);
+ UserNoteManager* manager = ConfigureNewManager();
+
+ gfx::Rect rect(10, 15, 100, 50);
+ std::unique_ptr<MockUserNoteInstance> instance =
+ CreateInstanceForId(note_ids_[0], manager, rect);
+
+ EXPECT_CALL(*instance, InitializeHighlightInternal)
+ .Times(1)
+ .WillOnce(Invoke(instance.get(),
+ &MockUserNoteInstance::MockInitializeHighlightInternal));
+
+ bool callback_called = false;
+ instance->InitializeHighlightIfNeeded(
+ base::BindLambdaForTesting([&]() { callback_called = true; }));
+
+ // The mocks ensure the callback is invoked synchronously, so verifications
+ // can happen immediately.
+ EXPECT_TRUE(callback_called);
+ EXPECT_TRUE(IsNoteInstanceInitialized(instance.get()));
+ EXPECT_EQ(instance->rect(), rect);
+}
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_note_manager.cc b/chromium/components/user_notes/browser/user_note_manager.cc
new file mode 100644
index 00000000000..915e1b8175d
--- /dev/null
+++ b/chromium/components/user_notes/browser/user_note_manager.cc
@@ -0,0 +1,91 @@
+// Copyright 2022 The Chromium 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/user_notes/browser/user_note_manager.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/user_notes/browser/user_note_instance.h"
+#include "content/public/browser/page.h"
+#include "content/public/browser/render_frame_host.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+
+namespace user_notes {
+
+UserNoteManager::UserNoteManager(content::Page& page,
+ base::SafeRef<UserNoteService> service)
+ : PageUserData<UserNoteManager>(page), service_(service) {
+ // TODO(crbug.com/1313967): If / when user notes are supported in subframes,
+ // caching the agent container of the primary main frame will not work. In
+ // that case, the frame's container will probably need to be fetched on each
+ // note instance initialization in
+ // UserNoteInstance::InitializeHighlightInternal.
+ content::RenderFrameHost& frame = page.GetMainDocument();
+ frame.GetRemoteInterfaces()->GetInterface(
+ note_agent_container_.BindNewPipeAndPassReceiver());
+}
+
+UserNoteManager::~UserNoteManager() {
+ for (const auto& entry_it : instance_map_) {
+ service_->OnNoteInstanceRemovedFromPage(entry_it.second->model().id(),
+ this);
+ }
+}
+
+UserNoteInstance* UserNoteManager::GetNoteInstance(
+ const base::UnguessableToken& id) {
+ const auto& entry_it = instance_map_.find(id);
+ if (entry_it == instance_map_.end()) {
+ return nullptr;
+ }
+
+ return entry_it->second.get();
+}
+
+const std::vector<UserNoteInstance*> UserNoteManager::GetAllNoteInstances() {
+ std::vector<UserNoteInstance*> notes;
+ notes.reserve(instance_map_.size());
+ for (const auto& entry_it : instance_map_) {
+ notes.push_back(entry_it.second.get());
+ }
+
+ return notes;
+}
+
+void UserNoteManager::RemoveNote(const base::UnguessableToken& id) {
+ const auto& entry_it = instance_map_.find(id);
+ DCHECK(entry_it != instance_map_.end())
+ << "Attempted to remove a note instance from a page where it didn't "
+ "exist";
+
+ service_->OnNoteInstanceRemovedFromPage(id, this);
+ instance_map_.erase(entry_it);
+}
+
+void UserNoteManager::AddNoteInstance(std::unique_ptr<UserNoteInstance> note) {
+ AddNoteInstance(std::move(note), base::DoNothing());
+}
+
+void UserNoteManager::AddNoteInstance(
+ std::unique_ptr<UserNoteInstance> note_instance,
+ base::OnceClosure initialize_callback) {
+ // TODO(crbug.com/1313967): This DCHECK is only applicable if notes are only
+ // supported in the top-level frame. If notes are ever supported in subframes,
+ // it is possible for the same note ID to be added to the same page more than
+ // once. For example, if website A has notes and website B embeds website A
+ // multiple times via iframes, then notes in website A will be added to this
+ // manager once for each frame.
+ DCHECK(instance_map_.find(note_instance->model().id()) == instance_map_.end())
+ << "Attempted to add a note instance for the same note to the same page "
+ "more than once";
+
+ service_->OnNoteInstanceAddedToPage(note_instance->model().id(), this);
+ UserNoteInstance* note_instance_raw = note_instance.get();
+ instance_map_.emplace(note_instance->model().id(), std::move(note_instance));
+ note_instance_raw->InitializeHighlightIfNeeded(
+ std::move(initialize_callback));
+}
+
+PAGE_USER_DATA_KEY_IMPL(UserNoteManager);
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_note_manager.h b/chromium/components/user_notes/browser/user_note_manager.h
new file mode 100644
index 00000000000..9482866c2c2
--- /dev/null
+++ b/chromium/components/user_notes/browser/user_note_manager.h
@@ -0,0 +1,95 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_MANAGER_H_
+#define COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_MANAGER_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/safe_ref.h"
+#include "base/unguessable_token.h"
+#include "components/user_notes/browser/user_note_instance.h"
+#include "components/user_notes/browser/user_note_service.h"
+#include "content/public/browser/page_user_data.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/mojom/annotation/annotation.mojom.h"
+
+namespace content {
+class Page;
+} // namespace content
+
+namespace user_notes {
+
+// A class responsible for holding the note instances that appear on a specific
+// |Page|. Its lifecycle is tied to the |Page| it is associated with, so it
+// implements |PageUserData|. A tab helper is responsible for attaching an
+// instance of this class to each new |Page|.
+class UserNoteManager : public content::PageUserData<UserNoteManager> {
+ public:
+ ~UserNoteManager() override;
+ UserNoteManager(const UserNoteManager&) = delete;
+ UserNoteManager& operator=(const UserNoteManager&) = delete;
+
+ const mojo::Remote<blink::mojom::AnnotationAgentContainer>&
+ note_agent_container() {
+ return note_agent_container_;
+ };
+
+ // Returns the note instance for the given ID, or nullptr if this page does
+ // not have an instance of that note.
+ UserNoteInstance* GetNoteInstance(const base::UnguessableToken& id);
+
+ // Returns all note instances for the |Page| this object is attached to.
+ const std::vector<UserNoteInstance*> GetAllNoteInstances();
+
+ // Destroys the note instance associated with the given GUID.
+ void RemoveNote(const base::UnguessableToken& id);
+
+ // Stores the given note instance into this object's instance map, then kicks
+ // off its asynchronous initialization in the renderer process, passing it the
+ // provided callback for when it finishes.
+ // TODO(gujen): Remove the overload without the callback after tests are
+ // fixed.
+ void AddNoteInstance(std::unique_ptr<UserNoteInstance> note);
+ void AddNoteInstance(std::unique_ptr<UserNoteInstance> note,
+ base::OnceClosure initialize_callback);
+
+ private:
+ friend class content::PageUserData<UserNoteManager>;
+ friend class UserNoteBaseTest;
+ friend class UserNoteUtilsTest;
+
+ UserNoteManager(content::Page& page, base::SafeRef<UserNoteService> service);
+
+ PAGE_USER_DATA_KEY_DECL();
+
+ // A ref to the note service. The service is always expected to outlive this
+ // class.
+ base::SafeRef<UserNoteService> service_;
+
+ // The list of note instances displayed in this page, mapped by their note ID.
+ // TODO(crbug.com/1313967): Holding the instances in an ID -> Instance map
+ // works while only top-level frames are supported, but won't always work if
+ // subframes are supported. For example, if website A has notes and website B
+ // embeds website A multiple times via iframes, then there will be multiple
+ // note instances in this manager for the same note ID, which this data
+ // structure can't handle. In that case the data structure will probably need
+ // to be something like ID -> Frame -> Instance.
+ std::unordered_map<base::UnguessableToken,
+ std::unique_ptr<UserNoteInstance>,
+ base::UnguessableTokenHash>
+ instance_map_;
+
+ // A connection to the annotation agent container on the renderer side to
+ // bind note instances to their agent counterpart.
+ mojo::Remote<blink::mojom::AnnotationAgentContainer> note_agent_container_;
+};
+
+} // namespace user_notes
+
+#endif // COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_MANAGER_H_
diff --git a/chromium/components/user_notes/browser/user_note_manager_unittest.cc b/chromium/components/user_notes/browser/user_note_manager_unittest.cc
new file mode 100644
index 00000000000..e0d30105b86
--- /dev/null
+++ b/chromium/components/user_notes/browser/user_note_manager_unittest.cc
@@ -0,0 +1,207 @@
+// Copyright 2022 The Chromium 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/user_notes/browser/user_note_manager.h"
+
+#include <vector>
+
+#include "base/unguessable_token.h"
+#include "components/user_notes/browser/user_note_base_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace user_notes {
+
+class UserNoteManagerTest : public UserNoteBaseTest {
+ protected:
+ void SetUp() override {
+ UserNoteBaseTest::SetUp();
+ AddNewNotesToService(3);
+ }
+
+ bool DoResultsContainId(const std::vector<UserNoteInstance*>& instances,
+ const base::UnguessableToken& id) {
+ bool found = false;
+ for (UserNoteInstance* instance : instances) {
+ if (instance->model().id() == id) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+};
+
+TEST_F(UserNoteManagerTest, Destructor) {
+ // Initial setup.
+ UserNoteManager* m1 = ConfigureNewManager();
+ UserNoteManager* m2 = ConfigureNewManager();
+ AddNewInstanceToManager(m2, note_ids_[0]);
+ AddNewInstanceToManager(m2, note_ids_[1]);
+ AddNewInstanceToManager(m2, note_ids_[2]);
+
+ // Verify initial state.
+ EXPECT_EQ(ModelMapSize(), 3u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[2]), 1u);
+ EXPECT_EQ(InstanceMapSize(m1), 0u);
+ EXPECT_EQ(InstanceMapSize(m2), 3u);
+
+ // Destroy a manager with no instances. There should be no impact on the model
+ // map. To do that, destroy the corresponding `WebContents` stored in the
+ // test, which will destroy the attached manager.
+ web_contents_list_[0].reset();
+ EXPECT_EQ(ModelMapSize(), 3u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[2]), 1u);
+
+ // Destroy a manager with instances. Refs to the manager should be removed
+ // from the model map for all notes. In this case, since this was also the
+ // last ref for the test notes, the models will be removed from the model map.
+ web_contents_list_[1].reset();
+ EXPECT_EQ(ModelMapSize(), 0u);
+}
+
+TEST_F(UserNoteManagerTest, GetNoteInstance) {
+ // Initial setup.
+ UserNoteManager* m = ConfigureNewManager();
+ AddNewInstanceToManager(m, note_ids_[0]);
+ AddNewInstanceToManager(m, note_ids_[1]);
+
+ // Verify initial state.
+ EXPECT_EQ(InstanceMapSize(m), 2u);
+
+ // Try to get an instance that doesn't exist. There should be no crash.
+ UserNoteInstance* i = m->GetNoteInstance(note_ids_[2]);
+ EXPECT_EQ(i, nullptr);
+
+ // Try to get an instance that exists. It should return the expected instance.
+ AddNewInstanceToManager(m, note_ids_[2]);
+ i = m->GetNoteInstance(note_ids_[2]);
+ EXPECT_NE(i, nullptr);
+ EXPECT_EQ(i->model().id(), note_ids_[2]);
+}
+
+TEST_F(UserNoteManagerTest, GetAllNoteInstances) {
+ // Initial setup.
+ UserNoteManager* m = ConfigureNewManager();
+
+ // Verify initial state.
+ EXPECT_EQ(InstanceMapSize(m), 0u);
+
+ // Try to get instances when there are none. It should return an empty vector.
+ const auto& emptyResults = m->GetAllNoteInstances();
+ EXPECT_EQ(emptyResults.size(), 0u);
+
+ // Add a few instances to the manager and try to get them. All instances
+ // should be returned.
+ AddNewInstanceToManager(m, note_ids_[0]);
+ AddNewInstanceToManager(m, note_ids_[1]);
+ AddNewInstanceToManager(m, note_ids_[2]);
+
+ EXPECT_EQ(InstanceMapSize(m), 3u);
+ const auto& results = m->GetAllNoteInstances();
+ EXPECT_EQ(results.size(), 3u);
+ EXPECT_TRUE(DoResultsContainId(results, note_ids_[0]));
+ EXPECT_TRUE(DoResultsContainId(results, note_ids_[1]));
+ EXPECT_TRUE(DoResultsContainId(results, note_ids_[2]));
+}
+
+TEST_F(UserNoteManagerTest, RemoveNote) {
+ // Initial setup.
+ UserNoteManager* m1 = ConfigureNewManager();
+ UserNoteManager* m2 = ConfigureNewManager();
+ AddNewInstanceToManager(m1, note_ids_[0]);
+ AddNewInstanceToManager(m1, note_ids_[1]);
+ AddNewInstanceToManager(m2, note_ids_[0]);
+ AddNewInstanceToManager(m2, note_ids_[1]);
+
+ // Verify initial state.
+ EXPECT_EQ(ModelMapSize(), 3u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0u);
+ EXPECT_EQ(InstanceMapSize(m1), 2u);
+ EXPECT_EQ(InstanceMapSize(m2), 2u);
+
+ // Remove a note instance from a manager. It should not affect the other
+ // managers this note appears in, and it should not affect the other instances
+ // for this manager.
+ m1->RemoveNote(note_ids_[0]);
+ EXPECT_EQ(ModelMapSize(), 3u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0u);
+ EXPECT_EQ(InstanceMapSize(m1), 1u);
+ EXPECT_EQ(InstanceMapSize(m2), 2u);
+ EXPECT_FALSE(DoesManagerExistForId(note_ids_[0], m1));
+ EXPECT_TRUE(DoesManagerExistForId(note_ids_[1], m1));
+ EXPECT_TRUE(DoesManagerExistForId(note_ids_[0], m2));
+
+ // Remove the last instance of a manager. It should not cause a problem or
+ // affect the other managers.
+ m1->RemoveNote(note_ids_[1]);
+ EXPECT_EQ(ModelMapSize(), 3u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0u);
+ EXPECT_EQ(InstanceMapSize(m1), 0u);
+ EXPECT_EQ(InstanceMapSize(m2), 2u);
+ EXPECT_FALSE(DoesManagerExistForId(note_ids_[0], m1));
+ EXPECT_FALSE(DoesManagerExistForId(note_ids_[1], m1));
+ EXPECT_TRUE(DoesManagerExistForId(note_ids_[0], m2));
+}
+
+TEST_F(UserNoteManagerTest, AddNoteInstance) {
+ // Initial setup.
+ UserNoteManager* m1 = ConfigureNewManager();
+ UserNoteManager* m2 = ConfigureNewManager();
+
+ // Verify initial state.
+ EXPECT_EQ(ModelMapSize(), 3u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 0u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 0u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0u);
+ EXPECT_EQ(InstanceMapSize(m1), 0u);
+ EXPECT_EQ(InstanceMapSize(m2), 0u);
+
+ // Add some note instances to a manager. It should be correctly reflected in
+ // both the manager's instance map and the service's model map. It should not
+ // affect other managers.
+ AddNewInstanceToManager(m1, note_ids_[0]);
+ AddNewInstanceToManager(m1, note_ids_[1]);
+ EXPECT_EQ(ModelMapSize(), 3u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0u);
+ EXPECT_EQ(InstanceMapSize(m1), 2u);
+ EXPECT_EQ(InstanceMapSize(m2), 0u);
+ EXPECT_TRUE(DoesManagerExistForId(note_ids_[0], m1));
+ EXPECT_TRUE(DoesManagerExistForId(note_ids_[1], m1));
+ EXPECT_FALSE(DoesManagerExistForId(note_ids_[2], m1));
+ EXPECT_FALSE(DoesManagerExistForId(note_ids_[0], m2));
+ EXPECT_FALSE(DoesManagerExistForId(note_ids_[1], m2));
+ EXPECT_FALSE(DoesManagerExistForId(note_ids_[2], m2));
+
+ // Add instances to another manager. It should be correctly reflected in
+ // both the manager's instance map and the service's model map. It should not
+ // affect other managers or instances in other managers.
+ AddNewInstanceToManager(m2, note_ids_[1]);
+ AddNewInstanceToManager(m2, note_ids_[2]);
+ EXPECT_EQ(ModelMapSize(), 3u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[2]), 1u);
+ EXPECT_EQ(InstanceMapSize(m1), 2u);
+ EXPECT_EQ(InstanceMapSize(m2), 2u);
+ EXPECT_TRUE(DoesManagerExistForId(note_ids_[0], m1));
+ EXPECT_TRUE(DoesManagerExistForId(note_ids_[1], m1));
+ EXPECT_FALSE(DoesManagerExistForId(note_ids_[2], m1));
+ EXPECT_FALSE(DoesManagerExistForId(note_ids_[0], m2));
+ EXPECT_TRUE(DoesManagerExistForId(note_ids_[1], m2));
+ EXPECT_TRUE(DoesManagerExistForId(note_ids_[2], m2));
+}
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_note_service.cc b/chromium/components/user_notes/browser/user_note_service.cc
index 2f86926415b..c6a70fcc413 100644
--- a/chromium/components/user_notes/browser/user_note_service.cc
+++ b/chromium/components/user_notes/browser/user_note_service.cc
@@ -5,9 +5,11 @@
#include "components/user_notes/browser/user_note_service.h"
#include "base/notreached.h"
-#include "components/user_notes/browser/user_notes_manager.h"
+#include "components/user_notes/browser/user_note_manager.h"
+#include "components/user_notes/interfaces/user_notes_ui.h"
#include "components/user_notes/user_notes_features.h"
#include "content/public/browser/render_frame_host.h"
+#include "ui/gfx/geometry/rect.h"
namespace user_notes {
@@ -17,30 +19,53 @@ UserNoteService::UserNoteService(
UserNoteService::~UserNoteService() = default;
-base::SafeRef<UserNoteService> UserNoteService::GetSafeRef() {
+base::SafeRef<UserNoteService> UserNoteService::GetSafeRef() const {
return weak_ptr_factory_.GetSafeRef();
}
+const UserNote* UserNoteService::GetNoteModel(
+ const base::UnguessableToken& id) const {
+ const auto& entry_it = model_map_.find(id);
+ return entry_it == model_map_.end() ? nullptr : entry_it->second.model.get();
+}
+
+bool UserNoteService::IsNoteInProgress(const base::UnguessableToken& id) const {
+ return creation_map_.find(id) != creation_map_.end();
+}
+
void UserNoteService::OnFrameNavigated(content::RenderFrameHost* rfh) {
DCHECK(IsUserNotesEnabled());
// For now, Notes are only supported in the main frame.
+ // TODO(crbug.com/1313967): This will need to be changed when User Notes are
+ // supported in subframes and / or AMP viewers.
if (!rfh->IsInPrimaryMainFrame()) {
return;
}
+ // TODO(crbug.com/1313967): Should non-web URLs such as chrome:// and
+ // file:/// also be ignored here?
if (rfh->GetPage().GetMainDocument().IsErrorDocument()) {
return;
}
- DCHECK(UserNotesManager::GetForPage(rfh->GetPage()));
+ DCHECK(UserNoteManager::GetForPage(rfh->GetPage()));
NOTIMPLEMENTED();
}
void UserNoteService::OnNoteInstanceAddedToPage(
const base::UnguessableToken& id,
- UserNotesManager* manager) {
+ UserNoteManager* manager) {
DCHECK(IsUserNotesEnabled());
+
+ // If this note is in the creation map, it means it is still in progress, so
+ // it won't be in the model map yet.
+ const auto& creation_entry_it = creation_map_.find(id);
+ if (creation_entry_it != creation_map_.end()) {
+ DCHECK(model_map_.find(id) == model_map_.end());
+ return;
+ }
+
const auto& entry_it = model_map_.find(id);
DCHECK(entry_it != model_map_.end())
<< "A note instance without backing model was added to a page";
@@ -50,42 +75,137 @@ void UserNoteService::OnNoteInstanceAddedToPage(
void UserNoteService::OnNoteInstanceRemovedFromPage(
const base::UnguessableToken& id,
- UserNotesManager* manager) {
+ UserNoteManager* manager) {
DCHECK(IsUserNotesEnabled());
- const auto& entry_it = model_map_.find(id);
- DCHECK(entry_it != model_map_.end())
- << "A note model was destroyed before all its instances";
-
- auto deleteCount = entry_it->second.managers.erase(manager);
- DCHECK(deleteCount > 0) << "Attempted to remove a ref to a note manager that "
- "wasn't in the model map";
-
- // If there are no longer any pages displaying this model, destroy it.
- if (entry_it->second.managers.empty()) {
- model_map_.erase(id);
+ // If this note was in progress, its model will be in the creation map, not
+ // the model map. Look for it there first.
+ const auto& creation_entry_it = creation_map_.find(id);
+ if (creation_entry_it != creation_map_.end()) {
+ DCHECK(model_map_.find(id) == model_map_.end());
+
+ // Erase the whole entry, as this note has been cancelled and should no
+ // longer exist.
+ creation_map_.erase(creation_entry_it);
+ } else {
+ const auto& entry_it = model_map_.find(id);
+ DCHECK(entry_it != model_map_.end())
+ << "A note model was destroyed before all its instances";
+
+ auto deleteCount = entry_it->second.managers.erase(manager);
+ DCHECK_GT(deleteCount, 0u) << "Attempted to remove a ref to a note manager "
+ "that wasn't in the model map";
+
+ // If there are no longer any pages displaying this model, destroy it.
+ if (entry_it->second.managers.empty()) {
+ model_map_.erase(id);
+ }
}
}
+void UserNoteService::OnAddNoteRequested(content::RenderFrameHost* frame,
+ std::string original_text,
+ std::string selector,
+ gfx::Rect rect) {
+ DCHECK(IsUserNotesEnabled());
+ DCHECK(frame);
+ UserNoteManager* manager = UserNoteManager::GetForPage(frame->GetPage());
+ DCHECK(manager);
+
+ // TODO(gujen): This partial note creation logic will be moved to an API
+ // exposed by the storage layer in order to keep the creation of UserNote
+ // models centralized. However, until the storage layer is finished, manually
+ // create a partial note here.
+ base::Time now = base::Time::Now();
+ int note_version = 1;
+ auto metadata = std::make_unique<UserNoteMetadata>(now, now, note_version);
+ auto body = std::make_unique<UserNoteBody>(/*plain_text_value=*/"");
+ auto target = std::make_unique<UserNoteTarget>(
+ UserNoteTarget::TargetType::kPageText, original_text,
+ GURL(frame->GetLastCommittedURL()), selector);
+ auto partial_note = std::make_unique<UserNote>(
+ base::UnguessableToken::Create(), std::move(metadata), std::move(body),
+ std::move(target));
+ UserNote* partial_note_raw = partial_note.get();
+
+ // Store the partial note model into the creation map (not the model map)
+ // until it is finalized.
+ UserNoteService::ModelMapEntry entry(std::move(partial_note));
+ entry.managers.emplace(manager);
+ DCHECK(creation_map_.find(entry.model->id()) == creation_map_.end())
+ << "Attempted to create a partial note that already exists";
+ creation_map_.emplace(entry.model->id(), std::move(entry));
+
+ // Create an instance for this note so the highlight can be shown in the page,
+ // and add it to the page's note manager. The instance's initialization does
+ // not need to be awaited, since the highlight's rect is already known.
+ auto instance = std::make_unique<UserNoteInstance>(
+ partial_note_raw->GetSafeRef(), manager, rect);
+ UserNoteInstance* instance_raw = instance.get();
+ manager->AddNoteInstance(std::move(instance), base::DoNothing());
+
+ // Finally, notify the UI layer that it should start the note creation UX for
+ // this note. The UI layer will eventually call either `OnNoteCreationDone` or
+ // `OnNoteCreationCancelled`, in which the partial note will be finalized or
+ // deleted, respectively.
+ UserNotesUI* ui = delegate_->GetUICoordinatorForFrame(frame);
+ ui->StartNoteCreation(instance_raw);
+}
+
void UserNoteService::OnNoteFocused(const base::UnguessableToken& id) {
DCHECK(IsUserNotesEnabled());
NOTIMPLEMENTED();
}
+void UserNoteService::OnNoteDeleted(const base::UnguessableToken& id) {
+ DCHECK(IsUserNotesEnabled());
+ NOTIMPLEMENTED();
+}
+
void UserNoteService::OnNoteCreationDone(const base::UnguessableToken& id,
const std::string& note_content) {
DCHECK(IsUserNotesEnabled());
- NOTIMPLEMENTED();
+
+ // Retrieve the partial note from the creation map and send it to the storage
+ // layer so it can officially be created and persisted. This will trigger a
+ // note change event, which will cause the service to propagate this new note
+ // to all relevant pages via `FrameUserNoteChanges::Apply()`. The partial
+ // model will be cleaned up from the creation map as part of that process.
+ const auto& creation_entry_it = creation_map_.find(id);
+ DCHECK(creation_entry_it != creation_map_.end())
+ << "Attempted to complete the creation of a note that doesn't exist";
+ // TODO(gujen): Call
+ // UserNoteStorage::UpdateNote(entry.model, content, /*is_creation=*/true).
+
+ // TODO(gujen): Make sure to transfer the model from the creation map to the
+ // model map in the OnNotesChanged() event sent by the storage layer.
}
void UserNoteService::OnNoteCreationCancelled(
const base::UnguessableToken& id) {
DCHECK(IsUserNotesEnabled());
+
+ // Simply remove the instance from its manager. This will in turn call
+ // `OnNoteInstanceRemovedFromPage`, which will clean up the partial model from
+ // the creation map.
+ const auto& entry_it = creation_map_.find(id);
+ DCHECK(entry_it != creation_map_.end())
+ << "Attempted to cancel the creation of a note that doesn't exist";
+ DCHECK_EQ(entry_it->second.managers.size(), 1u)
+ << "Unexpectedly had more than one manager ref in the creation map for a "
+ "partial note.";
+
+ (*entry_it->second.managers.begin())->RemoveNote(id);
+}
+
+void UserNoteService::OnNoteUpdated(const base::UnguessableToken& id,
+ const std::string& note_content) {
+ DCHECK(IsUserNotesEnabled());
NOTIMPLEMENTED();
}
-UserNoteService::ModelMapEntry::ModelMapEntry(std::unique_ptr<UserNote> m)
- : model(std::move(m)) {}
+UserNoteService::ModelMapEntry::ModelMapEntry(std::unique_ptr<UserNote> model)
+ : model(std::move(model)) {}
UserNoteService::ModelMapEntry::ModelMapEntry(ModelMapEntry&& other) = default;
diff --git a/chromium/components/user_notes/browser/user_note_service.h b/chromium/components/user_notes/browser/user_note_service.h
index 5a3722cbaa2..277e0e5beba 100644
--- a/chromium/components/user_notes/browser/user_note_service.h
+++ b/chromium/components/user_notes/browser/user_note_service.h
@@ -20,13 +20,19 @@
#include "components/user_notes/interfaces/user_notes_ui_delegate.h"
#include "components/user_notes/model/user_note.h"
+class UserNoteUICoordinatorTest;
+
namespace content {
class RenderFrameHost;
} // namespace content
+namespace gfx {
+class Rect;
+} // namespace gfx
+
namespace user_notes {
-class UserNotesManager;
+class UserNoteManager;
// Keyed service coordinating the different parts (Renderer, UI layer, storage
// layer) of the User Notes feature for the current user profile.
@@ -37,50 +43,74 @@ class UserNoteService : public KeyedService, public UserNotesUIDelegate {
UserNoteService(const UserNoteService&) = delete;
UserNoteService& operator=(const UserNoteService&) = delete;
- base::SafeRef<UserNoteService> GetSafeRef();
+ base::SafeRef<UserNoteService> GetSafeRef() const;
+
+ // Returns a pointer to the note model associated with the given ID, or
+ // `nullptr` if none exists.
+ const UserNote* GetNoteModel(const base::UnguessableToken& id) const;
+
+ // Returns true if the provided ID is found in the creation map, indicating
+ // the note is still in progress, and false otherwise.
+ bool IsNoteInProgress(const base::UnguessableToken& id) const;
// Called by the embedder when a frame navigates to a new URL. Queries the
// storage to find notes associated with that URL, and if there are any, kicks
// off the logic to display them in the page.
- void OnFrameNavigated(content::RenderFrameHost* rfh);
+ virtual void OnFrameNavigated(content::RenderFrameHost* rfh);
- // Called by |UserNotesManager| objects when a |UserNoteInstance| is added to
+ // Called by `UserNoteManager` objects when a `UserNoteInstance` is added to
// the page they're attached to. Updates the model map to add a ref to the
- // given |UserNotesManager| for the note with the specified ID.
+ // given `UserNoteManager` for the note with the specified ID.
void OnNoteInstanceAddedToPage(const base::UnguessableToken& id,
- UserNotesManager* manager);
+ UserNoteManager* manager);
- // Same as |OnNoteInstanceAddedToPage|, except for when a note is removed from
+ // Same as `OnNoteInstanceAddedToPage`, except for when a note is removed from
// a page. Updates the model map to remove the ref to the given
- // |UserNotesManager|. If this is the last page where the note was displayed,
+ // `UserNoteManager`. If this is the last page where the note was displayed,
// also deletes the model from the model map.
void OnNoteInstanceRemovedFromPage(const base::UnguessableToken& id,
- UserNotesManager* manager);
+ UserNoteManager* manager);
+
+ // Called by a note manager when the user selects "Add a note" from the
+ // associated page's context menu. Kicks off the note creation process.
+ // TODO(gujen) and TODO(bokan): Make the renderer notify the manager and the
+ // manager call this method. Also consider pre-creating the agent on the
+ // renderer side so it is immediately ready when the browser side receives the
+ // request.
+ void OnAddNoteRequested(content::RenderFrameHost* frame,
+ std::string original_text,
+ std::string selector,
+ gfx::Rect rect);
// UserNotesUIDelegate implementation.
void OnNoteFocused(const base::UnguessableToken& id) override;
+ void OnNoteDeleted(const base::UnguessableToken& id) override;
void OnNoteCreationDone(const base::UnguessableToken& id,
const std::string& note_content) override;
void OnNoteCreationCancelled(const base::UnguessableToken& id) override;
+ void OnNoteUpdated(const base::UnguessableToken& id,
+ const std::string& note_content) override;
private:
struct ModelMapEntry {
explicit ModelMapEntry(std::unique_ptr<UserNote> m);
~ModelMapEntry();
- ModelMapEntry(const ModelMapEntry&) = delete;
ModelMapEntry(ModelMapEntry&& other);
+ ModelMapEntry(const ModelMapEntry&) = delete;
ModelMapEntry& operator=(const ModelMapEntry&) = delete;
std::unique_ptr<UserNote> model;
- std::unordered_set<UserNotesManager*> managers;
+ std::unordered_set<UserNoteManager*> managers;
};
- friend class UserNoteServiceTest;
- friend class UserNotesManagerTest;
+ friend class UserNoteBaseTest;
+ friend class UserNoteInstanceTest;
+ friend class UserNoteUtilsTest;
+ friend class ::UserNoteUICoordinatorTest;
// Source of truth for the in-memory note models. Any note currently being
// displayed in a tab is stored in this data structure. Each entry also
- // contains a set of pointers to all |UserNotesManager| objects holding an
+ // contains a set of pointers to all `UserNoteManager` objects holding an
// instance of that note, which is necessary to clean up the models when
// they're no longer in use and to remove notes from affected web pages when
// they're deleted by the user.
@@ -89,6 +119,14 @@ class UserNoteService : public KeyedService, public UserNotesUIDelegate {
base::UnguessableTokenHash>
model_map_;
+ // A map to store in-progress notes during the note creation process. This is
+ // needed in order to separate incomplete notes from the rest of the real
+ // note models. Completed notes are eventually moved to the model map.
+ std::unordered_map<base::UnguessableToken,
+ ModelMapEntry,
+ base::UnguessableTokenHash>
+ creation_map_;
+
std::unique_ptr<UserNoteServiceDelegate> delegate_;
base::WeakPtrFactory<UserNoteService> weak_ptr_factory_{this};
};
diff --git a/chromium/components/user_notes/browser/user_note_service_unittest.cc b/chromium/components/user_notes/browser/user_note_service_unittest.cc
index 21e2bfc5d84..ec981c468f3 100644
--- a/chromium/components/user_notes/browser/user_note_service_unittest.cc
+++ b/chromium/components/user_notes/browser/user_note_service_unittest.cc
@@ -4,144 +4,177 @@
#include "components/user_notes/browser/user_note_service.h"
-#include <memory>
+#include <vector>
-#include "base/test/scoped_feature_list.h"
-#include "components/user_notes/browser/user_notes_manager.h"
-#include "components/user_notes/model/user_note_model_test_utils.h"
-#include "components/user_notes/user_notes_features.h"
-#include "content/public/browser/page.h"
+#include "base/unguessable_token.h"
+#include "components/user_notes/browser/user_note_base_test.h"
+#include "components/user_notes/browser/user_note_manager.h"
+#include "components/user_notes/browser/user_note_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace user_notes {
-namespace {
-
-// This is a hack to use a null |Page| in the tests instead of creating a
-// mock, which is a relatively high effort task. Passing |nullptr| to this
-// function and using the return value in a constructor requiring a |Page&|
-// will satisfy the compiler. Attempting to use the return value will cause a
-// crash.
-content::Page& CreatePageNullRef(content::Page* page_null_ptr) {
- return *page_null_ptr;
-}
-
-// A shortcut for calling |CreatePageNullRef| above.
-content::Page& NullPage() {
- return CreatePageNullRef(nullptr);
-}
-
-class UserNoteServiceDelegateMockImpl : public UserNoteServiceDelegate {
- public:
- std::vector<content::WebContents*> GetAllWebContents() override {
- return std::vector<content::WebContents*>();
- }
-
- UserNotesUI* GetUICoordinatorForWebContents(
- const content::WebContents* wc) override {
- return nullptr;
+class UserNoteServiceTest : public UserNoteBaseTest {
+ protected:
+ void SetUp() override {
+ UserNoteBaseTest::SetUp();
+ AddNewNotesToService(2);
}
};
-} // namespace
-
-class UserNoteServiceTest : public testing::Test {
- public:
- UserNoteServiceTest() {
- // Create 2 note ids.
- note_ids_.push_back(base::UnguessableToken::Create());
- note_ids_.push_back(base::UnguessableToken::Create());
-
- scoped_feature_list_.InitAndEnableFeature(user_notes::kUserNotes);
- note_service_ = std::make_unique<UserNoteService>(
- std::make_unique<UserNoteServiceDelegateMockImpl>());
- auto note1 = std::make_unique<UserNote>(
- note_ids_[0], GetTestUserNoteMetadata(), GetTestUserNoteBody(),
- GetTestUserNotePageTarget());
- auto note2 = std::make_unique<UserNote>(
- note_ids_[1], GetTestUserNoteMetadata(), GetTestUserNoteBody(),
- GetTestUserNotePageTarget());
- UserNoteService::ModelMapEntry entry1(std::move(note1));
- UserNoteService::ModelMapEntry entry2(std::move(note2));
- note_service_->model_map_.emplace(note_ids_[0], std::move(entry1));
- note_service_->model_map_.emplace(note_ids_[1], std::move(entry2));
- }
-
- int ManagerCountForId(const base::UnguessableToken& id) {
- const auto& entry_it = note_service_->model_map_.find(id);
- if (entry_it == note_service_->model_map_.end()) {
- return -1;
- }
- return entry_it->second.managers.size();
- }
-
- bool DoesModelExist(const base::UnguessableToken& id) {
- const auto& entry_it = note_service_->model_map_.find(id);
- return entry_it != note_service_->model_map_.end();
- }
-
- int ModelMapSize() { return note_service_->model_map_.size(); }
-
- protected:
- base::test::ScopedFeatureList scoped_feature_list_;
- std::unique_ptr<UserNoteService> note_service_;
- std::vector<base::UnguessableToken> note_ids_;
-};
+// Tests that note models are returned correctly by the service.
+TEST_F(UserNoteServiceTest, GetNoteModel) {
+ // Verify initial state.
+ EXPECT_EQ(ModelMapSize(), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 0u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 0u);
+
+ // Getting existing note models should return the expected model.
+ const UserNote* model1 = note_service_->GetNoteModel(note_ids_[0]);
+ const UserNote* model2 = note_service_->GetNoteModel(note_ids_[1]);
+ ASSERT_TRUE(model1);
+ ASSERT_TRUE(model2);
+ EXPECT_EQ(model1->id(), note_ids_[0]);
+ EXPECT_EQ(model2->id(), note_ids_[1]);
+
+ // Getting a note model that doesn't exist should return `nullptr` and not
+ // crash.
+ EXPECT_EQ(note_service_->GetNoteModel(base::UnguessableToken::Create()),
+ nullptr);
+}
-TEST_F(UserNoteServiceTest, AddNoteIntancesToModelMap) {
+// Tests that references to note managers are correctly added to the model map.
+TEST_F(UserNoteServiceTest, OnNoteInstanceAddedToPage) {
// Verify initial state.
- EXPECT_EQ(ModelMapSize(), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 0);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 0);
+ EXPECT_EQ(ModelMapSize(), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 0u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 0u);
// Simulate note instances being created in managers.
- auto m1 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- auto m2 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- note_service_->OnNoteInstanceAddedToPage(note_ids_[0], m1.get());
- note_service_->OnNoteInstanceAddedToPage(note_ids_[0], m2.get());
- note_service_->OnNoteInstanceAddedToPage(note_ids_[1], m1.get());
-
- EXPECT_EQ(ModelMapSize(), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1);
+ UserNoteManager* m1 = ConfigureNewManager();
+ UserNoteManager* m2 = ConfigureNewManager();
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[0], m1);
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[0], m2);
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[1], m1);
+
+ EXPECT_EQ(ModelMapSize(), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
}
-TEST_F(UserNoteServiceTest, RemoveNoteIntancesFromModelMap) {
+// Tests that references to note managers are correctly removed from the model
+// map.
+TEST_F(UserNoteServiceTest, OnNoteInstanceRemovedFromPage) {
// Initial setup.
- auto m1 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- auto m2 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- note_service_->OnNoteInstanceAddedToPage(note_ids_[0], m1.get());
- note_service_->OnNoteInstanceAddedToPage(note_ids_[0], m2.get());
- note_service_->OnNoteInstanceAddedToPage(note_ids_[1], m1.get());
+ UserNoteManager* m1 = ConfigureNewManager();
+ UserNoteManager* m2 = ConfigureNewManager();
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[0], m1);
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[0], m2);
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[1], m1);
// Verify initial state.
- EXPECT_EQ(ModelMapSize(), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1);
+ EXPECT_EQ(ModelMapSize(), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
// Simulate a note instance being removed from a page. Its ref should be
// removed from the model map, but only for the removed note.
- note_service_->OnNoteInstanceRemovedFromPage(note_ids_[0], m1.get());
- EXPECT_EQ(ModelMapSize(), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1);
+ note_service_->OnNoteInstanceRemovedFromPage(note_ids_[0], m1);
+ EXPECT_EQ(ModelMapSize(), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
// Simulate the last instance of a note being removed from its page. Its model
// should be cleaned up from the model map.
- note_service_->OnNoteInstanceRemovedFromPage(note_ids_[0], m2.get());
- EXPECT_EQ(ModelMapSize(), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1);
+ note_service_->OnNoteInstanceRemovedFromPage(note_ids_[0], m2);
+ EXPECT_EQ(ModelMapSize(), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
EXPECT_FALSE(DoesModelExist(note_ids_[0]));
// Repeat with the other note instance.
- note_service_->OnNoteInstanceRemovedFromPage(note_ids_[1], m1.get());
- EXPECT_EQ(ModelMapSize(), 0);
+ note_service_->OnNoteInstanceRemovedFromPage(note_ids_[1], m1);
+ EXPECT_EQ(ModelMapSize(), 0u);
EXPECT_FALSE(DoesModelExist(note_ids_[0]));
EXPECT_FALSE(DoesModelExist(note_ids_[1]));
}
+// Tests that partial notes are correctly identified as such.
+TEST_F(UserNoteServiceTest, IsNoteInProgress) {
+ EXPECT_EQ(CreationMapSize(), 0u);
+ AddPartialNotesToService(2);
+ EXPECT_EQ(CreationMapSize(), 2u);
+
+ EXPECT_FALSE(note_service_->IsNoteInProgress(note_ids_[0]));
+ EXPECT_FALSE(note_service_->IsNoteInProgress(note_ids_[1]));
+ EXPECT_TRUE(note_service_->IsNoteInProgress(note_ids_[2]));
+ EXPECT_TRUE(note_service_->IsNoteInProgress(note_ids_[3]));
+
+ // The method should also return false for notes that don't exist.
+ EXPECT_FALSE(
+ note_service_->IsNoteInProgress(base::UnguessableToken::Create()));
+}
+
+// Tests that adding an instance of a partial note to a page does not impact
+// the model map and the note manager references.
+TEST_F(UserNoteServiceTest, AddPartialNoteInstance) {
+ // Initial setup.
+ UserNoteManager* manager = ConfigureNewManager();
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[0], manager);
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[1], manager);
+
+ // Verify initial setup.
+ EXPECT_EQ(ModelMapSize(), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
+
+ // Create an in-progress note.
+ EXPECT_EQ(CreationMapSize(), 0u);
+ AddPartialNotesToService(1);
+ EXPECT_EQ(CreationMapSize(), 1u);
+
+ // Simulate the instance of the in-progress note being added to the note
+ // manager.
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[2], manager);
+
+ // Verify the model map hasn't been impacted and that the creation map is
+ // still as expected.
+ EXPECT_EQ(ModelMapSize(), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
+ EXPECT_EQ(CreationMapSize(), 1u);
+ EXPECT_TRUE(note_service_->IsNoteInProgress(note_ids_[2]));
+}
+
+// Tests that removing an instance of a partial note from a page does not impact
+// the model map and the note manager references, and correctly clears the
+// partial note from the creation map.
+TEST_F(UserNoteServiceTest, RemovePartialNoteInstance) {
+ // Initial setup.
+ UserNoteManager* manager = ConfigureNewManager();
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[0], manager);
+ note_service_->OnNoteInstanceAddedToPage(note_ids_[1], manager);
+
+ // Verify initial setup.
+ EXPECT_EQ(ModelMapSize(), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
+
+ // Create an in-progress note.
+ EXPECT_EQ(CreationMapSize(), 0u);
+ AddPartialNotesToService(1);
+ EXPECT_EQ(CreationMapSize(), 1u);
+
+ // Simulate the instance of the in-progress note being removed from the note
+ // manager.
+ note_service_->OnNoteInstanceRemovedFromPage(note_ids_[2], manager);
+
+ // Verify the model map hasn't been impacted and the partial note has been
+ // removed from the creation map.
+ EXPECT_EQ(ModelMapSize(), 2u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1u);
+ EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1u);
+ EXPECT_EQ(CreationMapSize(), 0u);
+ EXPECT_FALSE(note_service_->IsNoteInProgress(note_ids_[2]));
+}
+
} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_note_utils.cc b/chromium/components/user_notes/browser/user_note_utils.cc
new file mode 100644
index 00000000000..abef4b06238
--- /dev/null
+++ b/chromium/components/user_notes/browser/user_note_utils.cc
@@ -0,0 +1,98 @@
+// Copyright 2022 The Chromium 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/user_notes/browser/user_note_utils.h"
+
+#include "base/unguessable_token.h"
+#include "components/user_notes/browser/frame_user_note_changes.h"
+#include "components/user_notes/browser/user_note_manager.h"
+#include "components/user_notes/browser/user_note_service.h"
+#include "components/user_notes/interfaces/user_note_metadata_snapshot.h"
+#include "components/user_notes/model/user_note_metadata.h"
+#include "content/public/browser/render_frame_host.h"
+#include "url/gurl.h"
+
+namespace user_notes {
+
+std::vector<FrameUserNoteChanges> CalculateNoteChanges(
+ const UserNoteService& note_service,
+ const std::vector<content::RenderFrameHost*>& rfhs,
+ const UserNoteMetadataSnapshot& metadata_snapshot) {
+ std::vector<FrameUserNoteChanges> result;
+
+ // Create an empty metadata map to simplify the algorithm below for cases
+ // where there's no entry in the metadata snapshot for a frame's URL.
+ UserNoteMetadataSnapshot::IdToMetadataMap empty_map;
+
+ for (content::RenderFrameHost* rfh : rfhs) {
+ // Notes should only be processed for the primary page.
+ DCHECK(rfh->GetMainFrame()->IsInPrimaryMainFrame());
+
+ UserNoteManager* notes_manager =
+ UserNoteManager::GetForPage(rfh->GetPage());
+
+ if (!notes_manager) {
+ // Frame is part of an uninteresting page (for User Notes purposes), such
+ // as the error document.
+ continue;
+ }
+
+ const std::vector<UserNoteInstance*>& instances =
+ notes_manager->GetAllNoteInstances();
+ GURL url = rfh->GetLastCommittedURL();
+ const UserNoteMetadataSnapshot::IdToMetadataMap* map_for_url =
+ metadata_snapshot.GetMapForUrl(url);
+ const UserNoteMetadataSnapshot::IdToMetadataMap* metadata_map =
+ map_for_url ? map_for_url : &empty_map;
+
+ std::vector<base::UnguessableToken> added;
+ std::vector<base::UnguessableToken> modified;
+ std::vector<base::UnguessableToken> removed;
+
+ // First, iterate through the existing notes in the frame to detect notes
+ // that have been removed and notes that have been modified.
+ for (UserNoteInstance* instance : instances) {
+ const UserNote& model = instance->model();
+ const auto& metadata_it = metadata_map->find(model.id());
+
+ if (metadata_it == metadata_map->end()) {
+ // In-progress notes have an instance in the manager, but are not part
+ // of the metadata snapshot because they haven't been persisted to disk
+ // yet. Ignore them.
+ if (note_service.IsNoteInProgress(model.id())) {
+ continue;
+ }
+
+ if (url == model.target().target_page()) {
+ // Note has been removed.
+ removed.emplace_back(base::UnguessableToken(model.id()));
+ }
+ } else if (metadata_it->second->modification_date() >
+ model.metadata().modification_date()) {
+ // Note has been modified.
+ modified.emplace_back(base::UnguessableToken(model.id()));
+ }
+ }
+
+ // Finally, iterate through the notes in the metadata to detect notes that
+ // have been added.
+ for (const auto& metadata_it : *metadata_map) {
+ const base::UnguessableToken& id = metadata_it.first;
+ if (!notes_manager->GetNoteInstance(id)) {
+ // This is a new note.
+ added.emplace_back(base::UnguessableToken(id));
+ }
+ }
+
+ if (!added.empty() || !removed.empty() || !modified.empty()) {
+ result.emplace_back(
+ FrameUserNoteChanges(note_service.GetSafeRef(), rfh, std::move(added),
+ std::move(modified), std::move(removed)));
+ }
+ }
+
+ return result;
+}
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_note_utils.h b/chromium/components/user_notes/browser/user_note_utils.h
new file mode 100644
index 00000000000..a13806f4d80
--- /dev/null
+++ b/chromium/components/user_notes/browser/user_note_utils.h
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_UTILS_H_
+#define COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_UTILS_H_
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+namespace content {
+class RenderFrameHost;
+} // namespace content
+
+namespace user_notes {
+
+class FrameUserNoteChanges;
+class UserNoteMetadataSnapshot;
+class UserNoteService;
+
+// Compares the notes each frame currently contains with the notes it should
+// actually contain based on the provided metadata snapshot. A
+// `FrameUserNoteChanges` object is generated for each frame where notes
+// don't match the metadata.
+std::vector<FrameUserNoteChanges> CalculateNoteChanges(
+ const UserNoteService& note_service,
+ const std::vector<content::RenderFrameHost*>& rfhs,
+ const UserNoteMetadataSnapshot& metadata_snapshot);
+
+} // namespace user_notes
+
+#endif // COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_UTILS_H_
diff --git a/chromium/components/user_notes/browser/user_note_utils_unittest.cc b/chromium/components/user_notes/browser/user_note_utils_unittest.cc
new file mode 100644
index 00000000000..fcaf1800ed9
--- /dev/null
+++ b/chromium/components/user_notes/browser/user_note_utils_unittest.cc
@@ -0,0 +1,755 @@
+// Copyright 2022 The Chromium 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/user_notes/browser/user_note_utils.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "base/unguessable_token.h"
+#include "components/user_notes/browser/frame_user_note_changes.h"
+#include "components/user_notes/browser/user_note_manager.h"
+#include "components/user_notes/interfaces/user_note_metadata_snapshot.h"
+#include "components/user_notes/model/user_note_model_test_utils.h"
+#include "components/user_notes/user_notes_features.h"
+#include "content/public/browser/page.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace user_notes {
+
+namespace {
+
+using NoteIdList = std::vector<int>;
+
+const char kUrl1[] = "https://www.google.com/1";
+const char kUrl2[] = "https://www.google.com/2";
+const char kUrl3[] = "https://www.google.com/3";
+const char kUrl4[] = "https://www.google.com/4";
+const char kUrl5[] = "https://www.google.com/5";
+
+const base::Time kInitialTimeStamp = base::Time::FromDoubleT(1000);
+const base::Time kUpdatedTimeStamp = base::Time::FromDoubleT(2000);
+
+// An enum describing the different types of update that can happen to a note
+// during a test case. This determines what kind of updated metadata will be
+// generated during the test.
+enum NoteUpdateType {
+ // The updated metadata will be the same as the note's initial metadata.
+ UNCHANGED = 0,
+
+ // The note will not be added to frames at test setup time, but the update
+ // snapshot will have a metadata entry for it.
+ ADDED,
+
+ // The updated metadata will have a later modification timestamp than the
+ // note's initial metadata.
+ MODIFIED,
+
+ // No metadata will be generated for this note in the update snapshot.
+ REMOVED
+};
+
+// Configuration for simulating a note in a test case.
+struct NoteConfig {
+ NoteConfig(int id,
+ std::string url,
+ NoteUpdateType update = NoteUpdateType::UNCHANGED)
+ : test_id(id), target_url(url), update_type(update) {}
+
+ // A test-only, stable ID to use for this note, which makes test case
+ // authoring simpler and test output easier to understand compared to using
+ // `base::UnguessableToken` directly. An actual `base::UnguessableToken` will
+ // be generated by the test setup for building this note. The token will be
+ // mapped to this test ID to ensure consistency.
+ int test_id;
+
+ // The URL for this note's target page.
+ std::string target_url;
+
+ // Configures what kind of updated metadata should be generated during the
+ // test for this note. If set to `ADDED`, the test setup will not add this
+ // note to any frame. For every other update type, a note instance will be
+ // added at test setup time to each frame whose URL corresponds to this note's
+ // target URL. Defaults to `UNCHANGED`.
+ NoteUpdateType update_type;
+};
+
+// Configuration for initializing one `RenderFrameHost` during a test case and
+// setting its diff expectations.
+struct FrameConfig {
+ explicit FrameConfig(int id, std::string url) : test_id(id), url(url) {}
+
+ bool operator==(const FrameConfig& other) const {
+ return other.test_id == test_id;
+ }
+
+ // Sets the expectations for the added notes.
+ FrameConfig& ExpectAdded(const NoteIdList& notes) {
+ expect_diff = true;
+ added = notes;
+ return *this;
+ }
+
+ // Sets the expectations for the modified notes.
+ FrameConfig& ExpectModified(const NoteIdList& notes) {
+ expect_diff = true;
+ modified = notes;
+ return *this;
+ }
+
+ // Sets the expectations for the removed notes.
+ FrameConfig& ExpectRemoved(const NoteIdList& notes) {
+ expect_diff = true;
+ removed = notes;
+ return *this;
+ }
+
+ // A test-only, stable ID for this frame to help read the test output in case
+ // of failures.
+ int test_id;
+
+ // The desired URL for this frame. The test setup will navigate this frame to
+ // this URL and add to it all notes whose target page corresponds to this URL
+ // (except for notes that have an update type of `ADDED`).
+ std::string url;
+
+ // Whether a diff is expected to be generated for this frame. Defaults to
+ // false.
+ bool expect_diff{false};
+
+ // IDs of the notes that the diff should identify as having been added. Order
+ // doesn't matter. These IDs must all correspond to one of the IDs used in the
+ // `NoteConfig` objects for this test case. Defaults to the empty vector.
+ NoteIdList added;
+
+ // IDs of the notes that the diff should identify as having been modified.
+ // Order doesn't matter. These IDs must all correspond to one of the IDs used
+ // in the `NoteConfig` objects for this test case. Defaults to the empty
+ // vector.
+ NoteIdList modified;
+
+ // IDs of the notes that the diff should identify as having been removed.
+ // Order doesn't matter. These IDs must all correspond to one of the IDs used
+ // in the `NoteConfig` objects for this test case. Defaults to the empty
+ // vector.
+ NoteIdList removed;
+};
+
+// A test case for the diff calculation tests.
+struct UserNoteDiffTestCase {
+ explicit UserNoteDiffTestCase(const std::string& test_name)
+ : test_name(test_name) {}
+
+ // Adds a note config to this test case.
+ UserNoteDiffTestCase AddNote(const NoteConfig& note) {
+ notes.emplace_back(note);
+ return *this;
+ }
+
+ // Adds a frame config to this test case.
+ UserNoteDiffTestCase AddFrame(const FrameConfig& frame) {
+ frames.emplace_back(frame);
+ return *this;
+ }
+
+ // A short description of this test case, that will be used as the generated
+ // test name instead of /0, /1, etc.
+ std::string test_name;
+
+ // The configuration for the notes in this test case.
+ std::vector<NoteConfig> notes;
+
+ // The configuration for the frames in this test case. For each entry, a test
+ // frame will be created, navigated to the specified URL, and filled with note
+ // instances based on the `notes` configuration.
+ std::vector<FrameConfig> frames;
+};
+
+// Hasher for using frame configs as keys in an unordered map.
+struct FrameConfigHash {
+ size_t operator()(const FrameConfig& frame_config) const {
+ return std::hash<int>()(frame_config.test_id);
+ }
+};
+
+// Helper for setting the test case's name.
+static std::string DescribeParams(
+ const testing::TestParamInfo<UserNoteDiffTestCase>& info) {
+ return info.param.test_name;
+}
+
+// Helper to copy a vector of test IDs and sort them.
+NoteIdList CopyAndSort(NoteIdList test_ids) {
+ NoteIdList copy(test_ids);
+ std::sort(copy.begin(), copy.end());
+ return copy;
+}
+
+// Helper method to convert note token IDs to their equivalent test IDs as
+// configured in the provided map.
+NoteIdList ConvertToSortedTestIds(
+ const std::vector<base::UnguessableToken>& token_ids,
+ const std::unordered_map<base::UnguessableToken,
+ int,
+ base::UnguessableTokenHash>& token_to_test_id) {
+ NoteIdList converted;
+ for (const base::UnguessableToken& token : token_ids) {
+ converted.emplace_back(token_to_test_id.find(token)->second);
+ }
+ std::sort(converted.begin(), converted.end());
+ return converted;
+}
+
+} // namespace
+
+class UserNoteUtilsTest
+ : public content::RenderViewHostTestHarness,
+ public testing::WithParamInterface<UserNoteDiffTestCase> {
+ public:
+ UserNoteUtilsTest() {
+ scoped_feature_list_.InitAndEnableFeature(user_notes::kUserNotes);
+ }
+
+ protected:
+ void SetUp() override {
+ content::RenderViewHostTestHarness::SetUp();
+
+ // Create the note service and the note models that will be used in this
+ // test case. A service delegate isn't needed for these tests.
+ note_service_ = std::make_unique<UserNoteService>(/*delegate=*/nullptr);
+ for (const NoteConfig& note : GetParam().notes) {
+ CreateNewNoteAndAddToService(note);
+ }
+
+ // Set up the frames.
+ for (const FrameConfig& frame : GetParam().frames) {
+ ConfigureNewFrame(frame, GetParam().notes);
+ }
+ }
+
+ void TearDown() override {
+ // Owned web contentses must be destroyed before the test harness. Before
+ // doing that, however, clear the instance map of all `UserNoteManager`
+ // objects to avoid clean-up issues where the managers attempt to remove
+ // themselves from the `UserNoteService`, which won't work because the test
+ // setup does not add the manager refs in the service.
+ for (const auto& wc : web_contents_list_) {
+ UserNoteManager::GetForPage(wc->GetPrimaryPage())->instance_map_.clear();
+ }
+ web_contents_list_.clear();
+ content::RenderViewHostTestHarness::TearDown();
+ }
+
+ void CreateNewNoteAndAddToService(const NoteConfig& note_config) {
+ int test_id = note_config.test_id;
+ ASSERT_TRUE(test_id_to_token_.find(test_id) == test_id_to_token_.end())
+ << "Invalid test case configuration: the same note ID (" << test_id
+ << ") is used more than once";
+
+ auto token_id = base::UnguessableToken::Create();
+ test_id_to_token_.emplace(test_id, token_id);
+ token_to_test_id_.emplace(token_id, test_id);
+
+ // Creation time and minimum version are not important for these tests. For
+ // modification time, use the default timestamp so that tests with modified
+ // notes can use a later timestamp.
+ auto note_metadata = std::make_unique<UserNoteMetadata>(
+ /*creation_date=*/kInitialTimeStamp,
+ /*modification_date=*/kInitialTimeStamp,
+ /*min_note_version=*/1);
+
+ auto note = std::make_unique<UserNote>(
+ token_id, std::move(note_metadata), GetTestUserNoteBody(),
+ GetTestUserNotePageTarget(note_config.target_url));
+ UserNoteService::ModelMapEntry entry(std::move(note));
+ note_service_->model_map_.emplace(token_id, std::move(entry));
+ }
+
+ void ConfigureNewFrame(const FrameConfig& frame_config,
+ const std::vector<NoteConfig>& note_configs) {
+ ASSERT_TRUE(config_to_frame_.find(frame_config) == config_to_frame_.end())
+ << "Invalid test case configuration: the same frame ID ("
+ << frame_config.test_id << ") is used more than once";
+
+ // Create a test frame and navigate it to the specified URL.
+ std::unique_ptr<content::WebContents> wc = CreateTestWebContents();
+ content::RenderFrameHostTester::For(wc->GetPrimaryMainFrame())
+ ->InitializeRenderFrameIfNeeded();
+ content::NavigationSimulator::NavigateAndCommitFromBrowser(
+ wc.get(), GURL(frame_config.url));
+
+ // Create and attach a `UserNoteManager` to the primary page.
+ content::Page& page = wc->GetPrimaryPage();
+ UserNoteManager::CreateForPage(page, note_service_->GetSafeRef());
+ UserNoteManager* note_manager = UserNoteManager::GetForPage(page);
+ DCHECK(note_manager);
+
+ // Attach all notes that have a target URL corresponding to this frame's
+ // URL except if their update type is `ADDED`.
+ for (const NoteConfig& note_config : note_configs) {
+ if (note_config.target_url == frame_config.url &&
+ note_config.update_type != NoteUpdateType::ADDED) {
+ const auto token_it = test_id_to_token_.find(note_config.test_id);
+ DCHECK(token_it != test_id_to_token_.end());
+
+ const auto note_entry_it =
+ note_service_->model_map_.find(token_it->second);
+ UserNote* model = note_entry_it->second.model.get();
+ note_manager->instance_map_.emplace(
+ model->id(), std::make_unique<UserNoteInstance>(model->GetSafeRef(),
+ note_manager));
+ }
+ }
+
+ frame_to_config_.emplace(wc->GetPrimaryMainFrame(), frame_config);
+ config_to_frame_.emplace(frame_config, wc->GetPrimaryMainFrame());
+ web_contents_list_.emplace_back(std::move(wc));
+ }
+
+ void GenerateMetadataUpdateForNote(UserNoteMetadataSnapshot& snapshot,
+ const NoteConfig& note_config) {
+ if (note_config.update_type == NoteUpdateType::REMOVED) {
+ // To simulate this note being removed, simply don't include it in the
+ // updated metadata snapshot.
+ return;
+ }
+
+ base::Time modification_date =
+ note_config.update_type == NoteUpdateType::MODIFIED ? kUpdatedTimeStamp
+ : kInitialTimeStamp;
+
+ // Creation time and minimum version are not important for these tests.
+ auto note_metadata = std::make_unique<UserNoteMetadata>(
+ /*creation_date=*/kInitialTimeStamp, modification_date,
+ /*min_note_version=*/1);
+
+ const auto token_it = test_id_to_token_.find(note_config.test_id);
+ DCHECK(token_it != test_id_to_token_.end());
+
+ snapshot.AddEntry(GURL(note_config.target_url), token_it->second,
+ std::move(note_metadata));
+ }
+
+ std::unordered_map<base::UnguessableToken, int, base::UnguessableTokenHash>
+ token_to_test_id_;
+ std::unordered_map<int, base::UnguessableToken> test_id_to_token_;
+ std::unordered_map<content::RenderFrameHost*, FrameConfig> frame_to_config_;
+ std::unordered_map<FrameConfig, content::RenderFrameHost*, FrameConfigHash>
+ config_to_frame_;
+ std::vector<std::unique_ptr<content::WebContents>> web_contents_list_;
+ std::unique_ptr<UserNoteService> note_service_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+std::vector<UserNoteDiffTestCase> BuildTestCases() {
+ std::vector<UserNoteDiffTestCase> test_cases = {
+ // Cases without frames and / or notes.
+ UserNoteDiffTestCase("no_frames"),
+
+ UserNoteDiffTestCase("multiple_frames_all_without_notes")
+ .AddFrame(FrameConfig(0, kUrl1))
+ .AddFrame(FrameConfig(1, kUrl2))
+ .AddFrame(FrameConfig(2, kUrl3)),
+
+ UserNoteDiffTestCase("multiple_frames_same_url_all_without_notes")
+ .AddFrame(FrameConfig(0, kUrl1))
+ .AddFrame(FrameConfig(1, kUrl2))
+ .AddFrame(FrameConfig(2, kUrl2)),
+
+ // Cases with unchanged notes.
+ UserNoteDiffTestCase("one_frame_with_unchanged_notes")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1))
+ .AddFrame(FrameConfig(0, kUrl1)),
+
+ UserNoteDiffTestCase("multiple_frames_some_with_unchanged_notes")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1))
+ .AddFrame(FrameConfig(0, kUrl1))
+ .AddFrame(FrameConfig(1, kUrl2))
+ .AddFrame(FrameConfig(2, kUrl3)),
+
+ UserNoteDiffTestCase("multiple_frames_all_with_unchanged_notes")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1))
+ .AddNote(NoteConfig(2, kUrl1))
+ .AddFrame(FrameConfig(0, kUrl1))
+ .AddNote(NoteConfig(3, kUrl2))
+ .AddNote(NoteConfig(4, kUrl2))
+ .AddNote(NoteConfig(5, kUrl2))
+ .AddFrame(FrameConfig(1, kUrl2))
+ .AddNote(NoteConfig(6, kUrl3))
+ .AddNote(NoteConfig(7, kUrl3))
+ .AddNote(NoteConfig(8, kUrl3))
+ .AddFrame(FrameConfig(2, kUrl3)),
+
+ UserNoteDiffTestCase("multiple_frames_same_url_all_with_unchanged_notes")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1))
+ .AddNote(NoteConfig(2, kUrl1))
+ .AddFrame(FrameConfig(0, kUrl1))
+ .AddFrame(FrameConfig(1, kUrl1))
+ .AddNote(NoteConfig(3, kUrl2))
+ .AddNote(NoteConfig(4, kUrl2))
+ .AddNote(NoteConfig(5, kUrl2))
+ .AddFrame(FrameConfig(2, kUrl2)),
+
+ // Cases with added notes.
+ UserNoteDiffTestCase("one_frame_with_one_added_note")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectAdded({0})),
+
+ UserNoteDiffTestCase("one_frame_with_multiple_added_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectAdded({0, 1})),
+
+ UserNoteDiffTestCase("multiple_frames_some_with_added_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectAdded({0, 1, 2}))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(1, kUrl2).ExpectAdded({3, 4, 5}))
+ .AddFrame(FrameConfig(2, kUrl3)),
+
+ UserNoteDiffTestCase("multiple_frames_all_with_added_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectAdded({0, 1, 2}))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(1, kUrl2).ExpectAdded({3, 4, 5}))
+ .AddNote(NoteConfig(6, kUrl3, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(7, kUrl3, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(8, kUrl3, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(2, kUrl3).ExpectAdded({6, 7, 8})),
+
+ UserNoteDiffTestCase("multiple_frames_same_url_all_with_added_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectAdded({0, 1, 2}))
+ .AddFrame(FrameConfig(1, kUrl1).ExpectAdded({0, 1, 2}))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(2, kUrl2).ExpectAdded({3, 4, 5})),
+
+ // Cases with modified notes.
+ UserNoteDiffTestCase("one_frame_with_one_modified_note")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectModified({0})),
+
+ UserNoteDiffTestCase("one_frame_with_multiple_modified_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectModified({0, 1, 2})),
+
+ UserNoteDiffTestCase("multiple_frames_some_with_modified_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectModified({0, 1, 2}))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(1, kUrl2).ExpectModified({3, 4, 5}))
+ .AddFrame(FrameConfig(2, kUrl3)),
+
+ UserNoteDiffTestCase("multiple_frames_all_with_modified_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectModified({0, 1, 2}))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(1, kUrl2).ExpectModified({3, 4, 5}))
+ .AddNote(NoteConfig(6, kUrl3, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(7, kUrl3, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(8, kUrl3, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(2, kUrl3).ExpectModified({6, 7, 8})),
+
+ UserNoteDiffTestCase("multiple_frames_same_url_all_with_modified_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectModified({0, 1, 2}))
+ .AddFrame(FrameConfig(1, kUrl1).ExpectModified({0, 1, 2}))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(2, kUrl2).ExpectModified({3, 4, 5})),
+
+ // Cases with removed notes.
+ UserNoteDiffTestCase("one_frame_one_removed_note")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectRemoved({0})),
+
+ UserNoteDiffTestCase("one_frame_with_multiple_removed_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectRemoved({0, 1})),
+
+ UserNoteDiffTestCase("multiple_frames_some_with_removed_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectRemoved({0, 1, 2}))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(1, kUrl2).ExpectRemoved({3, 4, 5}))
+ .AddFrame(FrameConfig(2, kUrl3)),
+
+ UserNoteDiffTestCase("multiple_frames_all_with_removed_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectRemoved({0, 1, 2}))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(1, kUrl2).ExpectRemoved({3, 4, 5}))
+ .AddNote(NoteConfig(6, kUrl3, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(7, kUrl3, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(8, kUrl3, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(2, kUrl3).ExpectRemoved({6, 7, 8})),
+
+ UserNoteDiffTestCase("multiple_frames_same_url_all_with_removed_notes")
+ .AddNote(NoteConfig(0, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectRemoved({0, 1, 2}))
+ .AddFrame(FrameConfig(1, kUrl1).ExpectRemoved({0, 1, 2}))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(2, kUrl2).ExpectRemoved({3, 4, 5})),
+
+ // Cases that mix update types.
+ UserNoteDiffTestCase("one_frame_with_one_of_each_update_type")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(3, kUrl1, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(0, kUrl1)
+ .ExpectAdded({1})
+ .ExpectModified({2})
+ .ExpectRemoved({3})),
+
+ UserNoteDiffTestCase("one_frame_with_multiple_of_each_update_type")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(3, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(4, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(5, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(6, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(7, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(8, kUrl1, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(0, kUrl1)
+ .ExpectAdded({2, 3})
+ .ExpectModified({4, 5, 6})
+ .ExpectRemoved({7, 8})),
+
+ UserNoteDiffTestCase("multiple_frames_with_different_update_types")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(3, kUrl1, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(0, kUrl1).ExpectAdded({2, 3}))
+ .AddNote(NoteConfig(4, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(5, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(6, kUrl2, NoteUpdateType::REMOVED))
+ .AddFrame(
+ FrameConfig(1, kUrl2).ExpectModified({4, 5}).ExpectRemoved({6}))
+ .AddFrame(FrameConfig(2, kUrl3)),
+
+ UserNoteDiffTestCase(
+ "multiple_frames_with_multiple_notes_of_each_update_type")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(3, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(4, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(5, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(6, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(7, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(8, kUrl1, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(0, kUrl1)
+ .ExpectAdded({2, 3})
+ .ExpectModified({4, 5, 6})
+ .ExpectRemoved({7, 8}))
+ .AddNote(NoteConfig(9, kUrl2))
+ .AddNote(NoteConfig(10, kUrl2))
+ .AddNote(NoteConfig(11, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(12, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(13, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(14, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(15, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(16, kUrl2, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(17, kUrl2, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(1, kUrl2)
+ .ExpectAdded({11, 12})
+ .ExpectModified({13, 14, 15})
+ .ExpectRemoved({16, 17}))
+ .AddNote(NoteConfig(18, kUrl3))
+ .AddNote(NoteConfig(19, kUrl3))
+ .AddNote(NoteConfig(20, kUrl3, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(21, kUrl3, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(22, kUrl3, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(23, kUrl3, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(24, kUrl3, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(25, kUrl3, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(26, kUrl3, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(2, kUrl3)
+ .ExpectAdded({20, 21})
+ .ExpectModified({22, 23, 24})
+ .ExpectRemoved({25, 26})),
+
+ UserNoteDiffTestCase(
+ "multiple_frames_same_url_with_multiple_notes_of_each_update_type")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1))
+ .AddNote(NoteConfig(2, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(3, kUrl1, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(4, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(5, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(6, kUrl1, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(7, kUrl1, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(8, kUrl1, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(0, kUrl1)
+ .ExpectAdded({2, 3})
+ .ExpectModified({4, 5, 6})
+ .ExpectRemoved({7, 8}))
+ .AddFrame(FrameConfig(1, kUrl1)
+ .ExpectAdded({2, 3})
+ .ExpectModified({4, 5, 6})
+ .ExpectRemoved({7, 8}))
+ .AddNote(NoteConfig(9, kUrl2))
+ .AddNote(NoteConfig(10, kUrl2))
+ .AddNote(NoteConfig(11, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(12, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(13, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(14, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(15, kUrl2, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(16, kUrl2, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(17, kUrl2, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(2, kUrl2)
+ .ExpectAdded({11, 12})
+ .ExpectModified({13, 14, 15})
+ .ExpectRemoved({16, 17})),
+
+ UserNoteDiffTestCase("multiple_frames_each_with_different_update_types")
+ .AddNote(NoteConfig(0, kUrl1))
+ .AddNote(NoteConfig(1, kUrl1))
+ .AddFrame(FrameConfig(0, kUrl1))
+ .AddNote(NoteConfig(2, kUrl2, NoteUpdateType::ADDED))
+ .AddNote(NoteConfig(3, kUrl2, NoteUpdateType::ADDED))
+ .AddFrame(FrameConfig(1, kUrl2).ExpectAdded({2, 3}))
+ .AddNote(NoteConfig(4, kUrl3, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(5, kUrl3, NoteUpdateType::MODIFIED))
+ .AddNote(NoteConfig(6, kUrl3, NoteUpdateType::MODIFIED))
+ .AddFrame(FrameConfig(2, kUrl3).ExpectModified({4, 5, 6}))
+ .AddNote(NoteConfig(7, kUrl4, NoteUpdateType::REMOVED))
+ .AddNote(NoteConfig(8, kUrl4, NoteUpdateType::REMOVED))
+ .AddFrame(FrameConfig(3, kUrl4).ExpectRemoved({7, 8}))
+ .AddFrame(FrameConfig(4, kUrl5))};
+
+ return test_cases;
+}
+
+INSTANTIATE_TEST_SUITE_P(/* No prefix */,
+ UserNoteUtilsTest,
+ testing::ValuesIn(BuildTestCases()),
+ DescribeParams);
+
+TEST_P(UserNoteUtilsTest, CalculateNoteChanges) {
+ // Construct the metadata snapshot from the note configs as if there had been
+ // an update sent by the database.
+ UserNoteMetadataSnapshot metadata_snapshot;
+ for (const NoteConfig& note_config : GetParam().notes) {
+ GenerateMetadataUpdateForNote(metadata_snapshot, note_config);
+ }
+
+ // Round up the test frames as if they were the user's open tabs.
+ std::vector<content::RenderFrameHost*> frame_hosts;
+ frame_hosts.reserve(frame_to_config_.size());
+ for (const auto& config_it : frame_to_config_) {
+ frame_hosts.push_back(config_it.first);
+ }
+
+ // Calculate the diff between the notes in the frames and the notes in the
+ // metadata.
+ const std::vector<FrameUserNoteChanges>& actual_diffs =
+ CalculateNoteChanges(*note_service_, frame_hosts, metadata_snapshot);
+
+ std::unordered_set<content::RenderFrameHost*> frames_with_diff;
+ for (const FrameUserNoteChanges& diff : actual_diffs) {
+ // Find the frame config for this diff's frame.
+ const auto config_it = frame_to_config_.find(diff.rfh_);
+ DCHECK(config_it != frame_to_config_.end());
+ FrameConfig frame_config = config_it->second;
+
+ // Make sure there is at most one diff per frame.
+ EXPECT_TRUE(frames_with_diff.find(diff.rfh_) == frames_with_diff.end())
+ << "More than one diff generated for frame " << frame_config.test_id;
+ frames_with_diff.emplace(diff.rfh_);
+
+ // Verify that a diff was expected for this frame.
+ EXPECT_TRUE(frame_config.expect_diff)
+ << "A diff was unexpectedly generated for frame "
+ << frame_config.test_id;
+
+ // Verify added, modified and removed notes are as expected. Use copies to
+ // prevent any side effect of sorting in place.
+ NoteIdList actual_added =
+ ConvertToSortedTestIds(diff.notes_added_, token_to_test_id_);
+ NoteIdList expected_added = CopyAndSort(frame_config.added);
+ EXPECT_EQ(actual_added, expected_added)
+ << "Unexpected ADDED results for frame " << frame_config.test_id;
+
+ NoteIdList actual_modified =
+ ConvertToSortedTestIds(diff.notes_modified_, token_to_test_id_);
+ NoteIdList expected_modified = CopyAndSort(frame_config.modified);
+ EXPECT_EQ(actual_modified, expected_modified)
+ << "Unexpected MODIFIED results for frame " << frame_config.test_id;
+
+ NoteIdList actual_removed =
+ ConvertToSortedTestIds(diff.notes_removed_, token_to_test_id_);
+ NoteIdList expected_removed = CopyAndSort(frame_config.removed);
+ EXPECT_EQ(actual_removed, expected_removed)
+ << "Unexpected REMOVED results for frame " << frame_config.test_id;
+ }
+
+ // Make sure there are no missing diffs.
+ for (const auto& frame_it : config_to_frame_) {
+ if (frame_it.first.expect_diff) {
+ EXPECT_FALSE(frames_with_diff.find(frame_it.second) ==
+ frames_with_diff.end())
+ << "A diff was unexpectedly missing for frame "
+ << frame_it.first.test_id;
+ }
+ }
+}
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_notes_change.cc b/chromium/components/user_notes/browser/user_notes_change.cc
deleted file mode 100644
index 389b26dc3a1..00000000000
--- a/chromium/components/user_notes/browser/user_notes_change.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2022 The Chromium 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/user_notes/browser/user_notes_change.h"
-
-#include "base/notreached.h"
-#include "content/public/browser/render_frame_host.h"
-
-namespace user_notes {
-
-UserNotesChange::UserNotesChange(content::RenderFrameHost* rfh,
- const ChangeList& new_notes,
- const ChangeList& modified_notes,
- const ChangeList& deleted_notes)
- : rfh_(rfh),
- new_notes_(new_notes),
- modified_notes_(modified_notes),
- deleted_notes_(deleted_notes) {}
-
-UserNotesChange::UserNotesChange(content::RenderFrameHost* rfh,
- ChangeList&& new_notes,
- ChangeList&& modified_notes,
- ChangeList&& deleted_notes)
- : rfh_(rfh),
- new_notes_(std::move(new_notes)),
- modified_notes_(std::move(modified_notes)),
- deleted_notes_(std::move(deleted_notes)) {}
-
-UserNotesChange::~UserNotesChange() = default;
-
-void UserNotesChange::Apply() {
- NOTIMPLEMENTED();
-}
-
-} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_notes_change.h b/chromium/components/user_notes/browser/user_notes_change.h
deleted file mode 100644
index fa4f643f364..00000000000
--- a/chromium/components/user_notes/browser/user_notes_change.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2022 The Chromium 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 COMPONENTS_USER_NOTES_BROWSER_USER_NOTES_CHANGE_H_
-#define COMPONENTS_USER_NOTES_BROWSER_USER_NOTES_CHANGE_H_
-
-#include <vector>
-
-#include "base/memory/raw_ptr.h"
-#include "base/unguessable_token.h"
-
-namespace content {
-class RenderFrameHost;
-} // namespace content
-
-namespace user_notes {
-
-typedef std::vector<base::UnguessableToken> ChangeList;
-
-// A container to represent changes to a web page's (or more precisely, a render
-// frame's) displayed User Notes. Includes logic to actually apply the changes
-// in the associated frame and the User Notes UI.
-class UserNotesChange {
- public:
- UserNotesChange(content::RenderFrameHost* rfh,
- const ChangeList& new_notes,
- const ChangeList& modified_notes,
- const ChangeList& deleted_notes);
- UserNotesChange(content::RenderFrameHost* rfh,
- ChangeList&& new_notes,
- ChangeList&& modified_notes,
- ChangeList&& deleted_notes);
- ~UserNotesChange();
- UserNotesChange(const UserNotesChange&) = delete;
- UserNotesChange& operator=(const UserNotesChange&) = delete;
-
- // Kicks off the asynchronous logic to propagate the Notes changes to the web
- // page and UI. This includes creating and removing highlights in the frame,
- // and requesting the UI to update its list of displayed notes if the web page
- // associated with this change was in the active tab of its browser.
- void Apply();
-
- private:
- raw_ptr<content::RenderFrameHost> rfh_;
- ChangeList new_notes_;
- ChangeList modified_notes_;
- ChangeList deleted_notes_;
-};
-
-} // namespace user_notes
-
-#endif // COMPONENTS_USER_NOTES_BROWSER_USER_NOTES_CHANGE_H_
diff --git a/chromium/components/user_notes/browser/user_notes_manager.cc b/chromium/components/user_notes/browser/user_notes_manager.cc
deleted file mode 100644
index f316f61323f..00000000000
--- a/chromium/components/user_notes/browser/user_notes_manager.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2022 The Chromium 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/user_notes/browser/user_notes_manager.h"
-
-#include "base/memory/ptr_util.h"
-#include "components/user_notes/browser/user_note_instance.h"
-#include "content/public/browser/page.h"
-
-namespace user_notes {
-
-// static
-std::unique_ptr<UserNotesManager> UserNotesManager::CreateForTest(
- content::Page& page,
- base::SafeRef<UserNoteService> service) {
- return base::WrapUnique(new UserNotesManager(page, service));
-}
-
-UserNotesManager::UserNotesManager(content::Page& page,
- base::SafeRef<UserNoteService> service)
- : PageUserData<UserNotesManager>(page), service_(service) {}
-
-UserNotesManager::~UserNotesManager() {
- for (const auto& entry_it : instance_map_) {
- service_->OnNoteInstanceRemovedFromPage(entry_it.second->model().id(),
- this);
- }
-}
-
-UserNoteInstance* UserNotesManager::GetNoteInstance(
- const base::UnguessableToken id) {
- const auto& entry_it = instance_map_.find(id);
- if (entry_it == instance_map_.end()) {
- return nullptr;
- }
-
- return entry_it->second.get();
-}
-
-const std::vector<UserNoteInstance*> UserNotesManager::GetAllNoteInstances() {
- std::vector<UserNoteInstance*> notes;
- notes.reserve(instance_map_.size());
- for (const auto& entry_it : instance_map_) {
- notes.push_back(entry_it.second.get());
- }
-
- return notes;
-}
-
-void UserNotesManager::RemoveNote(const base::UnguessableToken id) {
- const auto& entry_it = instance_map_.find(id);
- DCHECK(entry_it != instance_map_.end())
- << "Attempted to remove a note instance from a page where it didn't "
- "exist";
-
- service_->OnNoteInstanceRemovedFromPage(id, this);
- instance_map_.erase(entry_it);
-}
-
-void UserNotesManager::AddNoteInstance(std::unique_ptr<UserNoteInstance> note) {
- DCHECK(instance_map_.find(note->model().id()) == instance_map_.end())
- << "Attempted to add a note instance for the same note to the same page "
- "more than once";
-
- service_->OnNoteInstanceAddedToPage(note->model().id(), this);
- instance_map_.emplace(note->model().id(), std::move(note));
-}
-
-PAGE_USER_DATA_KEY_IMPL(UserNotesManager);
-
-} // namespace user_notes
diff --git a/chromium/components/user_notes/browser/user_notes_manager.h b/chromium/components/user_notes/browser/user_notes_manager.h
deleted file mode 100644
index 36f095dfade..00000000000
--- a/chromium/components/user_notes/browser/user_notes_manager.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2022 The Chromium 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 COMPONENTS_USER_NOTES_BROWSER_USER_NOTES_MANAGER_H_
-#define COMPONENTS_USER_NOTES_BROWSER_USER_NOTES_MANAGER_H_
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/memory/safe_ref.h"
-#include "base/unguessable_token.h"
-#include "components/user_notes/browser/user_note_instance.h"
-#include "components/user_notes/browser/user_note_service.h"
-#include "content/public/browser/page_user_data.h"
-
-namespace content {
-class Page;
-}
-
-namespace user_notes {
-
-// A class responsible for holding the note instances that appear on a specific
-// |Page|. Its lifecycle is tied to the |Page| it is associated with, so it
-// implements |PageUserData|. A tab helper is responsible for attaching an
-// instance of this class to each new |Page|.
-class UserNotesManager : public content::PageUserData<UserNotesManager> {
- public:
- // Exposes a way to construct this object from unit tests. Do not use in
- // product code; instead, use UserNotesManager::CreateForPage, inherited from
- // PageUserData.
- static std::unique_ptr<UserNotesManager> CreateForTest(
- content::Page& page,
- base::SafeRef<UserNoteService> service);
-
- ~UserNotesManager() override;
- UserNotesManager(const UserNotesManager&) = delete;
- UserNotesManager& operator=(const UserNotesManager&) = delete;
-
- // Returns the note instance for the given ID, or nullptr if this page does
- // not have an instance of that note.
- UserNoteInstance* GetNoteInstance(const base::UnguessableToken id);
-
- // Returns all note instances for the |Page| this object is attached to.
- const std::vector<UserNoteInstance*> GetAllNoteInstances();
-
- // Destroys the note instance associated with the given GUID.
- void RemoveNote(const base::UnguessableToken id);
-
- // Stores the given note instance into this object's note instance container.
- void AddNoteInstance(std::unique_ptr<UserNoteInstance> note);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(UserNotesManagerTest, Destructor);
- FRIEND_TEST_ALL_PREFIXES(UserNotesManagerTest, GetNoteInstance);
- FRIEND_TEST_ALL_PREFIXES(UserNotesManagerTest, GetAllNoteInstances);
- FRIEND_TEST_ALL_PREFIXES(UserNotesManagerTest, RemoveNote);
- FRIEND_TEST_ALL_PREFIXES(UserNotesManagerTest, AddNoteInstance);
- friend class content::PageUserData<UserNotesManager>;
-
- UserNotesManager(content::Page& page, base::SafeRef<UserNoteService> service);
-
- PAGE_USER_DATA_KEY_DECL();
-
- // A ref to the note service. The service is always expected to outlive this
- // class.
- base::SafeRef<UserNoteService> service_;
-
- // The list of note instances displayed in this page, mapped by their note ID.
- std::unordered_map<base::UnguessableToken,
- std::unique_ptr<UserNoteInstance>,
- base::UnguessableTokenHash>
- instance_map_;
-};
-
-} // namespace user_notes
-
-#endif // COMPONENTS_USER_NOTES_BROWSER_USER_NOTES_MANAGER_H_
diff --git a/chromium/components/user_notes/browser/user_notes_manager_unittest.cc b/chromium/components/user_notes/browser/user_notes_manager_unittest.cc
deleted file mode 100644
index 7c2a5413653..00000000000
--- a/chromium/components/user_notes/browser/user_notes_manager_unittest.cc
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright 2022 The Chromium 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/user_notes/browser/user_notes_manager.h"
-
-#include <memory>
-
-#include "base/test/scoped_feature_list.h"
-#include "components/user_notes/browser/user_note_service.h"
-#include "components/user_notes/model/user_note_model_test_utils.h"
-#include "components/user_notes/user_notes_features.h"
-#include "content/public/browser/page.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace user_notes {
-
-namespace {
-
-// This is a hack to use a null |Page| in the tests instead of creating a
-// mock, which is a relatively high effort task. Passing |nullptr| to this
-// function and using the return value in a constructor requiring a |Page&|
-// will satisfy the compiler. Attempting to use the return value will cause a
-// crash.
-content::Page& CreatePageNullRef(content::Page* page_null_ptr) {
- return *page_null_ptr;
-}
-
-// A shortcut for calling |CreatePageNullRef| above.
-content::Page& NullPage() {
- return CreatePageNullRef(nullptr);
-}
-
-class UserNoteServiceDelegateMockImpl : public UserNoteServiceDelegate {
- public:
- std::vector<content::WebContents*> GetAllWebContents() override {
- return std::vector<content::WebContents*>();
- }
-
- UserNotesUI* GetUICoordinatorForWebContents(
- const content::WebContents* wc) override {
- return nullptr;
- }
-};
-
-} // namespace
-
-class UserNotesManagerTest : public testing::Test {
- public:
- UserNotesManagerTest() {
- // Create 3 note ids.
- note_ids_.push_back(base::UnguessableToken::Create());
- note_ids_.push_back(base::UnguessableToken::Create());
- note_ids_.push_back(base::UnguessableToken::Create());
-
- scoped_feature_list_.InitAndEnableFeature(user_notes::kUserNotes);
- note_service_ = std::make_unique<UserNoteService>(
- std::make_unique<UserNoteServiceDelegateMockImpl>());
- UserNoteService::ModelMapEntry entry1(std::make_unique<UserNote>(
- note_ids_[0], GetTestUserNoteMetadata(), GetTestUserNoteBody(),
- GetTestUserNotePageTarget()));
- UserNoteService::ModelMapEntry entry2(std::make_unique<UserNote>(
- note_ids_[1], GetTestUserNoteMetadata(), GetTestUserNoteBody(),
- GetTestUserNotePageTarget()));
- UserNoteService::ModelMapEntry entry3(std::make_unique<UserNote>(
- note_ids_[2], GetTestUserNoteMetadata(), GetTestUserNoteBody(),
- GetTestUserNotePageTarget()));
- note_service_->model_map_.emplace(note_ids_[0], std::move(entry1));
- note_service_->model_map_.emplace(note_ids_[1], std::move(entry2));
- note_service_->model_map_.emplace(note_ids_[2], std::move(entry3));
- }
-
- base::SafeRef<UserNote> GetSafeRefForNote(base::UnguessableToken id) {
- const auto& entry_it = note_service_->model_map_.find(id);
- EXPECT_NE(entry_it, note_service_->model_map_.end());
- return entry_it->second.model->GetSafeRef();
- }
-
- int ManagerCountForId(const base::UnguessableToken& id) {
- const auto& entry_it = note_service_->model_map_.find(id);
- if (entry_it == note_service_->model_map_.end()) {
- return -1;
- }
- return entry_it->second.managers.size();
- }
-
- int ModelMapSize() { return note_service_->model_map_.size(); }
-
- bool DoesManagerExistForId(const base::UnguessableToken& id,
- UserNotesManager* manager) {
- const auto& model_entry_it = note_service_->model_map_.find(id);
- if (model_entry_it == note_service_->model_map_.end()) {
- return false;
- }
- const auto& manager_entry_it =
- model_entry_it->second.managers.find(manager);
- return manager_entry_it != model_entry_it->second.managers.end();
- }
-
- bool DoResultsContainId(const std::vector<UserNoteInstance*>& instances,
- const base::UnguessableToken& id) {
- bool found = false;
- for (UserNoteInstance* instance : instances) {
- if (instance->model().id() == id) {
- found = true;
- break;
- }
- }
- return found;
- }
-
- protected:
- base::test::ScopedFeatureList scoped_feature_list_;
- std::unique_ptr<UserNoteService> note_service_;
- std::vector<base::UnguessableToken> note_ids_;
-};
-
-TEST_F(UserNotesManagerTest, Destructor) {
- // Initial setup.
- auto m1 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- auto m2 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- m2->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[0])));
- m2->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[1])));
- m2->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[2])));
-
- // Verify initial state.
- EXPECT_EQ(ModelMapSize(), 3);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[2]), 1);
- EXPECT_EQ(m1->instance_map_.size(), 0u);
- EXPECT_EQ(m2->instance_map_.size(), 3u);
-
- // Destroy a manager with no instances. There should be no impact on the model
- // map.
- m1.reset();
- EXPECT_EQ(ModelMapSize(), 3);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[2]), 1);
-
- // Destroy a manager with instances. Refs to the manager should be removed
- // from the model map for all notes. In this case, since this was also the
- // last ref for the test notes, the models will be removed from the model map.
- m2.reset();
- EXPECT_EQ(ModelMapSize(), 0);
-}
-
-TEST_F(UserNotesManagerTest, GetNoteInstance) {
- // Initial setup.
- auto m =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- m->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[0])));
- m->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[1])));
-
- // Verify initial state.
- EXPECT_EQ(m->instance_map_.size(), 2u);
-
- // Try to get an instance that doesn't exist. There should be no crash.
- UserNoteInstance* i = m->GetNoteInstance(note_ids_[2]);
- EXPECT_EQ(i, nullptr);
-
- // Try to get an instance that exists. It should return the expected instance.
- m->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[2])));
- i = m->GetNoteInstance(note_ids_[2]);
- EXPECT_NE(i, nullptr);
- EXPECT_EQ(i->model().id(), note_ids_[2]);
-}
-
-TEST_F(UserNotesManagerTest, GetAllNoteInstances) {
- // Initial setup.
- auto m =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
-
- // Verify initial state.
- EXPECT_EQ(m->instance_map_.size(), 0u);
-
- // Try to get instances when there are none. It should return an empty vector.
- const auto& emptyResults = m->GetAllNoteInstances();
- EXPECT_EQ(emptyResults.size(), 0u);
-
- // Add a few instances to the manager and try to get them. All instances
- // should be returned.
- m->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[0])));
- m->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[1])));
- m->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[2])));
- const auto& results = m->GetAllNoteInstances();
- EXPECT_EQ(results.size(), 3u);
- EXPECT_TRUE(DoResultsContainId(results, note_ids_[0]));
- EXPECT_TRUE(DoResultsContainId(results, note_ids_[1]));
- EXPECT_TRUE(DoResultsContainId(results, note_ids_[2]));
-}
-
-TEST_F(UserNotesManagerTest, RemoveNote) {
- // Initial setup.
- auto m1 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- auto m2 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- m1->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[0])));
- m1->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[1])));
- m2->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[0])));
- m2->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[1])));
-
- // Verify initial state.
- EXPECT_EQ(ModelMapSize(), 3);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0);
- EXPECT_EQ(m1->instance_map_.size(), 2u);
- EXPECT_EQ(m2->instance_map_.size(), 2u);
-
- // Remove a note instance from a manager. It should not affect the other
- // managers this note appears in, and it should not affect the other instances
- // for this manager.
- m1->RemoveNote(note_ids_[0]);
- EXPECT_EQ(ModelMapSize(), 3);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0);
- EXPECT_EQ(m1->instance_map_.size(), 1u);
- EXPECT_EQ(m2->instance_map_.size(), 2u);
- EXPECT_FALSE(DoesManagerExistForId(note_ids_[0], m1.get()));
- EXPECT_TRUE(DoesManagerExistForId(note_ids_[1], m1.get()));
- EXPECT_TRUE(DoesManagerExistForId(note_ids_[0], m2.get()));
-
- // Remove the last instance of a manager. It should not cause a problem or
- // affect the other managers.
- m1->RemoveNote(note_ids_[1]);
- EXPECT_EQ(ModelMapSize(), 3);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0);
- EXPECT_EQ(m1->instance_map_.size(), 0u);
- EXPECT_EQ(m2->instance_map_.size(), 2u);
- EXPECT_FALSE(DoesManagerExistForId(note_ids_[0], m1.get()));
- EXPECT_FALSE(DoesManagerExistForId(note_ids_[1], m1.get()));
- EXPECT_TRUE(DoesManagerExistForId(note_ids_[0], m2.get()));
-}
-
-TEST_F(UserNotesManagerTest, AddNoteInstance) {
- // Initial setup.
- auto m1 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
- auto m2 =
- UserNotesManager::CreateForTest(NullPage(), note_service_->GetSafeRef());
-
- // Verify initial state.
- EXPECT_EQ(ModelMapSize(), 3);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 0);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 0);
- EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0);
- EXPECT_EQ(m1->instance_map_.size(), 0u);
- EXPECT_EQ(m2->instance_map_.size(), 0u);
-
- // Add some note instances to a manager. It should be correctly reflected in
- // both the manager's instance map and the service's model map. It should not
- // affect other managers.
- m1->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[0])));
- m1->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[1])));
- EXPECT_EQ(ModelMapSize(), 3);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[2]), 0);
- EXPECT_EQ(m1->instance_map_.size(), 2u);
- EXPECT_EQ(m2->instance_map_.size(), 0u);
- EXPECT_TRUE(DoesManagerExistForId(note_ids_[0], m1.get()));
- EXPECT_TRUE(DoesManagerExistForId(note_ids_[1], m1.get()));
- EXPECT_FALSE(DoesManagerExistForId(note_ids_[2], m1.get()));
- EXPECT_FALSE(DoesManagerExistForId(note_ids_[0], m2.get()));
- EXPECT_FALSE(DoesManagerExistForId(note_ids_[1], m2.get()));
- EXPECT_FALSE(DoesManagerExistForId(note_ids_[2], m2.get()));
-
- // Add instances to another manager. It should be correctly reflected in
- // both the manager's instance map and the service's model map. It should not
- // affect other managers or instances in other managers.
- m2->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[1])));
- m2->AddNoteInstance(
- std::make_unique<UserNoteInstance>(GetSafeRefForNote(note_ids_[2])));
- EXPECT_EQ(ModelMapSize(), 3);
- EXPECT_EQ(ManagerCountForId(note_ids_[0]), 1);
- EXPECT_EQ(ManagerCountForId(note_ids_[1]), 2);
- EXPECT_EQ(ManagerCountForId(note_ids_[2]), 1);
- EXPECT_EQ(m1->instance_map_.size(), 2u);
- EXPECT_EQ(m2->instance_map_.size(), 2u);
- EXPECT_TRUE(DoesManagerExistForId(note_ids_[0], m1.get()));
- EXPECT_TRUE(DoesManagerExistForId(note_ids_[1], m1.get()));
- EXPECT_FALSE(DoesManagerExistForId(note_ids_[2], m1.get()));
- EXPECT_FALSE(DoesManagerExistForId(note_ids_[0], m2.get()));
- EXPECT_TRUE(DoesManagerExistForId(note_ids_[1], m2.get()));
- EXPECT_TRUE(DoesManagerExistForId(note_ids_[2], m2.get()));
-}
-
-} // namespace user_notes
diff --git a/chromium/components/user_notes/interfaces/BUILD.gn b/chromium/components/user_notes/interfaces/BUILD.gn
index 2de84ac8514..9dcfa68a9e3 100644
--- a/chromium/components/user_notes/interfaces/BUILD.gn
+++ b/chromium/components/user_notes/interfaces/BUILD.gn
@@ -4,6 +4,8 @@
source_set("interfaces") {
sources = [
+ "user_note_metadata_snapshot.cc",
+ "user_note_metadata_snapshot.h",
"user_note_service_delegate.h",
"user_note_storage.h",
"user_notes_ui.h",
diff --git a/chromium/components/user_notes/interfaces/user_note_metadata_snapshot.cc b/chromium/components/user_notes/interfaces/user_note_metadata_snapshot.cc
new file mode 100644
index 00000000000..23d34d70402
--- /dev/null
+++ b/chromium/components/user_notes/interfaces/user_note_metadata_snapshot.cc
@@ -0,0 +1,44 @@
+// Copyright 2022 The Chromium 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/user_notes/interfaces/user_note_metadata_snapshot.h"
+
+#include "components/user_notes/model/user_note_metadata.h"
+
+namespace user_notes {
+
+UserNoteMetadataSnapshot::UserNoteMetadataSnapshot() = default;
+
+UserNoteMetadataSnapshot::UserNoteMetadataSnapshot(
+ UserNoteMetadataSnapshot&& other) = default;
+
+UserNoteMetadataSnapshot::~UserNoteMetadataSnapshot() = default;
+
+void UserNoteMetadataSnapshot::AddEntry(
+ const GURL& url,
+ const base::UnguessableToken& id,
+ std::unique_ptr<UserNoteMetadata> metadata) {
+ auto url_entry = url_map_.find(url);
+
+ if (url_entry == url_map_.end()) {
+ url_map_.emplace(url, IdToMetadataMap());
+ return AddEntry(url, id, std::move(metadata));
+ }
+
+ DCHECK(url_entry->second.find(id) == url_entry->second.end())
+ << "Attempted to add metadata for a note ID twice";
+ url_entry->second.emplace(id, std::move(metadata));
+}
+
+const UserNoteMetadataSnapshot::IdToMetadataMap*
+UserNoteMetadataSnapshot::GetMapForUrl(const GURL& url) const {
+ auto url_entry = url_map_.find(url);
+ if (url_entry == url_map_.end()) {
+ return nullptr;
+ } else {
+ return &url_entry->second;
+ }
+}
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/interfaces/user_note_metadata_snapshot.h b/chromium/components/user_notes/interfaces/user_note_metadata_snapshot.h
new file mode 100644
index 00000000000..c71a4568c15
--- /dev/null
+++ b/chromium/components/user_notes/interfaces/user_note_metadata_snapshot.h
@@ -0,0 +1,68 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_USER_NOTES_INTERFACES_USER_NOTE_METADATA_SNAPSHOT_H_
+#define COMPONENTS_USER_NOTES_INTERFACES_USER_NOTE_METADATA_SNAPSHOT_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "base/unguessable_token.h"
+#include "url/gurl.h"
+
+namespace user_notes {
+
+class UserNoteMetadata;
+
+namespace {
+
+// In order to have GURL as a key in a hashmap, GURL hashing mechanism is
+// needed.
+struct GURLHash {
+ size_t operator()(const GURL& url) const {
+ return std::hash<std::string>()(url.spec());
+ }
+};
+
+} // namespace
+
+// A class that encapsulates an
+// `unordered_map<GURL, unordered_map<ID, UserNoteMetadata>>`. This represents
+// a snapshot of the note metadata contained in the database for a set of URLs.
+// The first map is to group metadata by URL, which makes it easy to look up
+// what notes are attached to that URL. The second map is for quick lookup of a
+// note's metadata by its ID. Using this class makes code simpler and clearer
+// than if using the raw type.
+class UserNoteMetadataSnapshot {
+ public:
+ using IdToMetadataMap = std::unordered_map<base::UnguessableToken,
+ std::unique_ptr<UserNoteMetadata>,
+ base::UnguessableTokenHash>;
+ using UrlToIdToMetadataMap =
+ std::unordered_map<GURL, IdToMetadataMap, GURLHash>;
+
+ UserNoteMetadataSnapshot();
+ UserNoteMetadataSnapshot(UserNoteMetadataSnapshot&& other);
+ UserNoteMetadataSnapshot(const UserNoteMetadataSnapshot&) = delete;
+ UserNoteMetadataSnapshot& operator=(const UserNoteMetadataSnapshot&) = delete;
+ ~UserNoteMetadataSnapshot();
+
+ // Adds a metadata entry to this class, based on the URL the note is attached
+ // to and its ID.
+ void AddEntry(const GURL& url,
+ const base::UnguessableToken& id,
+ std::unique_ptr<UserNoteMetadata> metadata);
+
+ // Returns a raw pointer to the Note ID -> Metadata hash map for the given
+ // URL, or nullptr if the URL does not have any notes associated with it.
+ const IdToMetadataMap* GetMapForUrl(const GURL& url) const;
+
+ private:
+ UrlToIdToMetadataMap url_map_;
+};
+
+} // namespace user_notes
+
+#endif // COMPONENTS_USER_NOTES_INTERFACES_USER_NOTE_METADATA_SNAPSHOT_H_
diff --git a/chromium/components/user_notes/interfaces/user_note_service_delegate.h b/chromium/components/user_notes/interfaces/user_note_service_delegate.h
index 88f3ead3d32..75d3b92abf0 100644
--- a/chromium/components/user_notes/interfaces/user_note_service_delegate.h
+++ b/chromium/components/user_notes/interfaces/user_note_service_delegate.h
@@ -9,7 +9,7 @@
#include <vector>
namespace content {
-class WebContents;
+class RenderFrameHost;
} // namespace content
namespace user_notes {
@@ -24,18 +24,15 @@ class UserNoteServiceDelegate {
UserNoteServiceDelegate& operator=(const UserNoteServiceDelegate&) = delete;
virtual ~UserNoteServiceDelegate() = default;
- // Finds the list of all |WebContents| currently open for the profile
- // associated with the UserNoteService. The service will use this information
- // to fetch the relevant notes from storage and add highlights to the
- // webpages.
- virtual std::vector<content::WebContents*> GetAllWebContents() = 0;
-
- // Finds and returns the UI coordinator associated with the given
- // |WebContents|. The service will use this to post commands to the UI, such
- // as bringing a note into focus, or starting the UX flow to create a new
- // note.
- virtual UserNotesUI* GetUICoordinatorForWebContents(
- const content::WebContents* wc) = 0;
+ // Called by the `UserNoteService` to get the list of all frames for which
+ // notes should be updated.
+ virtual std::vector<content::RenderFrameHost*> GetAllFramesForUserNotes() = 0;
+
+ // Called by the `UserNoteService` to get a handle to the UI coordinator
+ // associated with the given frame so it can post commands to the UI, for
+ // example to bring a note into focus or start the note creation flow.
+ virtual UserNotesUI* GetUICoordinatorForFrame(
+ const content::RenderFrameHost* rfh) = 0;
};
} // namespace user_notes
diff --git a/chromium/components/user_notes/interfaces/user_note_storage.h b/chromium/components/user_notes/interfaces/user_note_storage.h
index 008207fde7d..e612a729b76 100644
--- a/chromium/components/user_notes/interfaces/user_note_storage.h
+++ b/chromium/components/user_notes/interfaces/user_note_storage.h
@@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/unguessable_token.h"
+#include "components/user_notes/interfaces/user_note_metadata_snapshot.h"
#include "components/user_notes/model/user_note.h"
#include "components/user_notes/model/user_note_metadata.h"
#include "url/gurl.h"
@@ -18,24 +19,9 @@
namespace user_notes {
-// In order to have GURL as a key in a hashmap, GURL hashing mechanism is
-// needed.
-struct GURLHash {
- size_t operator()(const GURL& url) const {
- return std::hash<std::string>()(url.spec());
- }
-};
-
// Interface that callers can use to interact with the UserNotes in storage.
class UserNoteStorage {
public:
- using NoteMetadataIDMap =
- std::unordered_map<base::UnguessableToken,
- std::unique_ptr<UserNoteMetadata>,
- base::UnguessableTokenHash>;
- using UrlNoteMetadataIDMap =
- std::unordered_map<GURL, NoteMetadataIDMap, GURLHash>;
-
// Observer class for the notes storage. Notifies implementers when the notes
// have changed on disk so they can update their model.
class Observer {
@@ -53,7 +39,7 @@ class UserNoteStorage {
// ID.
virtual void GetNoteMetadataForUrls(
std::vector<GURL> urls,
- base::OnceCallback<void(UrlNoteMetadataIDMap)> callback) = 0;
+ base::OnceCallback<void(UserNoteMetadataSnapshot)> callback) = 0;
// Fetches all `UserNotes` corresponding to the given IDs from disk. The
// results are returned via `callback`.
@@ -62,17 +48,10 @@ class UserNoteStorage {
base::OnceCallback<void(std::vector<std::unique_ptr<UserNote>>)>
callback) = 0;
- // Saves a brand-new note to disk.
- virtual void CreateNote(base::UnguessableToken id,
+ // Saves a brand-new note or a modified note to disk.
+ virtual void UpdateNote(const UserNote* model,
std::string note_body_text,
- UserNoteTarget::TargetType target_type,
- std::string original_text,
- GURL target_page,
- std::string selector) = 0;
-
- // Saves a modified note to disk.
- virtual void UpdateNote(base::UnguessableToken id,
- std::string note_body_text) = 0;
+ bool is_creation = false) = 0;
// Deletes a note from disk.
virtual void DeleteNote(const base::UnguessableToken& guid) = 0;
diff --git a/chromium/components/user_notes/interfaces/user_notes_ui.h b/chromium/components/user_notes/interfaces/user_notes_ui.h
index e54fa3a58df..4d4d7fdca7a 100644
--- a/chromium/components/user_notes/interfaces/user_notes_ui.h
+++ b/chromium/components/user_notes/interfaces/user_notes_ui.h
@@ -11,6 +11,8 @@
namespace user_notes {
+class UserNoteInstance;
+
// Interface that the UI layer of User Notes must implement. Used by the
// business logic in the service to send commands to the UI.
class UserNotesUI {
@@ -28,7 +30,7 @@ class UserNotesUI {
// corresponds to the location in the webpage where the associated highlight
// is, and should be compared with existing notes in the UI to determine where
// the new note should be inserted.
- virtual void StartNoteCreation(const std::string& guid, gfx::Rect bounds) = 0;
+ virtual void StartNoteCreation(UserNoteInstance* instance) = 0;
// Called when the model has changed and the UI should consequently refresh
// the notes it is displaying. The new model must be polled from the active
diff --git a/chromium/components/user_notes/interfaces/user_notes_ui_delegate.h b/chromium/components/user_notes/interfaces/user_notes_ui_delegate.h
index 4bfed1487b9..6ce5d5a8828 100644
--- a/chromium/components/user_notes/interfaces/user_notes_ui_delegate.h
+++ b/chromium/components/user_notes/interfaces/user_notes_ui_delegate.h
@@ -23,12 +23,19 @@ class UserNotesUIDelegate {
// Called when a note in the UI is focused.
virtual void OnNoteFocused(const base::UnguessableToken& id) = 0;
+ // Called when the user deletes a note in the UI.
+ virtual void OnNoteDeleted(const base::UnguessableToken& id) = 0;
+
// Called when the user successfully creates a new note in the UI.
virtual void OnNoteCreationDone(const base::UnguessableToken& id,
const std::string& note_content) = 0;
// Called when the user aborts the note creation process in the UI.
virtual void OnNoteCreationCancelled(const base::UnguessableToken& id) = 0;
+
+ // Called when the user updates an existing note in the UI.
+ virtual void OnNoteUpdated(const base::UnguessableToken& id,
+ const std::string& note_content) = 0;
};
} // namespace user_notes
diff --git a/chromium/components/user_notes/model/user_note.cc b/chromium/components/user_notes/model/user_note.cc
index 3e25e331335..1259680d8df 100644
--- a/chromium/components/user_notes/model/user_note.cc
+++ b/chromium/components/user_notes/model/user_note.cc
@@ -17,7 +17,7 @@ UserNote::UserNote(const base::UnguessableToken& id,
UserNote::~UserNote() = default;
-base::SafeRef<UserNote> UserNote::GetSafeRef() {
+base::SafeRef<UserNote> UserNote::GetSafeRef() const {
return weak_ptr_factory_.GetSafeRef();
}
diff --git a/chromium/components/user_notes/model/user_note.h b/chromium/components/user_notes/model/user_note.h
index cd58a3beb04..b1746c4f8b2 100644
--- a/chromium/components/user_notes/model/user_note.h
+++ b/chromium/components/user_notes/model/user_note.h
@@ -23,11 +23,12 @@ class UserNote {
std::unique_ptr<UserNoteMetadata> metadata,
std::unique_ptr<UserNoteBody> body,
std::unique_ptr<UserNoteTarget> target);
+
~UserNote();
UserNote(const UserNote&) = delete;
UserNote& operator=(const UserNote&) = delete;
- base::SafeRef<UserNote> GetSafeRef();
+ base::SafeRef<UserNote> GetSafeRef() const;
const base::UnguessableToken& id() const { return id_; }
const UserNoteMetadata& metadata() const { return *metadata_; }
diff --git a/chromium/components/user_notes/model/user_note_model_test_utils.cc b/chromium/components/user_notes/model/user_note_model_test_utils.cc
index 61ed2585506..41d9de23997 100644
--- a/chromium/components/user_notes/model/user_note_model_test_utils.cc
+++ b/chromium/components/user_notes/model/user_note_model_test_utils.cc
@@ -18,9 +18,11 @@ std::unique_ptr<UserNoteBody> GetTestUserNoteBody() {
return std::make_unique<UserNoteBody>("test note");
}
-std::unique_ptr<UserNoteTarget> GetTestUserNotePageTarget() {
- return std::make_unique<UserNoteTarget>(UserNoteTarget::TargetType::PAGE, "",
- GURL("www.exmaple.com"), "");
+std::unique_ptr<UserNoteTarget> GetTestUserNotePageTarget(
+ const std::string& url) {
+ return std::make_unique<UserNoteTarget>(UserNoteTarget::TargetType::kPage,
+ /*original_text=*/"", GURL(url),
+ /*selector=*/"");
}
} // namespace user_notes
diff --git a/chromium/components/user_notes/model/user_note_model_test_utils.h b/chromium/components/user_notes/model/user_note_model_test_utils.h
index acef1b6f4b1..a549a0f0456 100644
--- a/chromium/components/user_notes/model/user_note_model_test_utils.h
+++ b/chromium/components/user_notes/model/user_note_model_test_utils.h
@@ -16,8 +16,9 @@ extern std::unique_ptr<UserNoteMetadata> GetTestUserNoteMetadata();
extern std::unique_ptr<UserNoteBody> GetTestUserNoteBody();
-extern std::unique_ptr<UserNoteTarget> GetTestUserNotePageTarget();
+extern std::unique_ptr<UserNoteTarget> GetTestUserNotePageTarget(
+ const std::string& url = "https://www.example.com");
} // namespace user_notes
-#endif // COMPONENTS_USER_NOTES_MODEL_USER_NOTE_MODEL_TEST_UTILS_H_ \ No newline at end of file
+#endif // COMPONENTS_USER_NOTES_MODEL_USER_NOTE_MODEL_TEST_UTILS_H_
diff --git a/chromium/components/user_notes/model/user_note_target.h b/chromium/components/user_notes/model/user_note_target.h
index 670a5fa68e3..0ddb50ba8c9 100644
--- a/chromium/components/user_notes/model/user_note_target.h
+++ b/chromium/components/user_notes/model/user_note_target.h
@@ -14,7 +14,7 @@ namespace user_notes {
// Model class for a note target.
class UserNoteTarget {
public:
- enum TargetType { PAGE = 0, PAGE_TEXT };
+ enum TargetType { kPage = 0, kPageText };
explicit UserNoteTarget(TargetType type,
const std::string& original_text,
diff --git a/chromium/components/user_notes/storage/BUILD.gn b/chromium/components/user_notes/storage/BUILD.gn
index 602a129d98b..fc00946b781 100644
--- a/chromium/components/user_notes/storage/BUILD.gn
+++ b/chromium/components/user_notes/storage/BUILD.gn
@@ -31,3 +31,18 @@ static_library("internal") {
"//url",
]
}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "user_note_database_unittest.cc" ]
+
+ deps = [
+ ":internal",
+ "//base",
+ "//components/user_notes/browser",
+ "//components/user_notes/model:unit_tests",
+ "//sql",
+ "//sql:test_support",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/user_notes/storage/user_note_database.cc b/chromium/components/user_notes/storage/user_note_database.cc
index 327a1bc41af..85bfccc9e30 100644
--- a/chromium/components/user_notes/storage/user_note_database.cc
+++ b/chromium/components/user_notes/storage/user_note_database.cc
@@ -4,12 +4,22 @@
#include "components/user_notes/storage/user_note_database.h"
+#include "base/files/file_util.h"
+#include "sql/error_delegate_util.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
namespace user_notes {
namespace {
-constexpr base::FilePath::CharType kDatabaseName[] =
- FILE_PATH_LITERAL("UserNotes.db");
+// `kCurrentVersionNumber` and `kCompatibleVersionNumber` are used for DB
+// migrations. Update both accordingly when changing the schema.
+// Version 1 - 2021-04 - Initial Schema - https://crrev.com/c/3546500
+const int kCurrentVersionNumber = 1;
+
+const int kCompatibleVersionNumber = 1;
} // namespace
@@ -23,19 +33,49 @@ UserNoteDatabase::~UserNoteDatabase() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
-void UserNoteDatabase::Init() {
+bool UserNoteDatabase::Init() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // TODO(gayane): Implement.
+ if (db_.is_open()) {
+ return true;
+ }
+
+ // Use of Unretained is safe as sql::Database will only run the callback while
+ // it's alive. As UserNoteDatabase instance owns the sql::Database it's
+ // guaranteed that the UserNoteDatabase will be alive when the callback is
+ // run.
+ db_.set_error_callback(base::BindRepeating(
+ &UserNoteDatabase::DatabaseErrorCallback, base::Unretained(this)));
+ db_.set_histogram_tag("UserNotes");
+
+ const base::FilePath dir = db_file_path_.DirName();
+ if (!base::DirectoryExists(dir) && !base::CreateDirectory(dir)) {
+ DLOG(ERROR) << "Failed to create directory for user notes database";
+ return false;
+ }
+
+ if (!db_.Open(db_file_path_)) {
+ DLOG(ERROR) << "Failed to open user notes database: "
+ << db_.GetErrorMessage();
+ return false;
+ }
+
+ if (!InitSchema()) {
+ DLOG(ERROR) << "Failed to create schema for user notes database: "
+ << db_.GetErrorMessage();
+ db_.Close();
+ return false;
+ }
+ return true;
}
-UserNoteStorage::UrlNoteMetadataIDMap UserNoteDatabase::GetNoteMetadataForUrls(
+UserNoteMetadataSnapshot UserNoteDatabase::GetNoteMetadataForUrls(
std::vector<GURL> urls) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(gayane): Implement.
- return UserNoteStorage::UrlNoteMetadataIDMap();
+ return UserNoteMetadataSnapshot();
}
std::vector<std::unique_ptr<UserNote>> UserNoteDatabase::GetNotesById(
@@ -47,28 +87,160 @@ std::vector<std::unique_ptr<UserNote>> UserNoteDatabase::GetNotesById(
return std::vector<std::unique_ptr<UserNote>>();
}
-void UserNoteDatabase::CreateNote(base::UnguessableToken id,
- std::string note_body_text,
- UserNoteTarget::TargetType target_type,
- std::string original_text,
- GURL target_page,
- std::string selector) {
+void UserNoteDatabase::CreateNote(const UserNote* model,
+ std::string note_body_text) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // TODO(gayane): Implement.
+ if (!EnsureDBInit())
+ return;
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return;
+
+ sql::Statement create_note(db_.GetCachedStatement(SQL_FROM_HERE,
+ "INSERT INTO notes("
+ "id,"
+ "creation_date,"
+ "modification_date,"
+ "url,"
+ "origin,"
+ "type)"
+ "VALUES(?,?,?,?,?,?)"));
+
+ if (!create_note.is_valid())
+ return;
+
+ // TODO: possibly the time should be passed to this function, for example for
+ // sync to add notes with past creation date.
+ create_note.BindString(0, model->id().ToString());
+ create_note.BindTime(1, model->metadata().creation_date());
+ create_note.BindTime(2, model->metadata().modification_date());
+ create_note.BindString(3, model->target().target_page().spec());
+ create_note.BindString(
+ 4, url::Origin::Create(model->target().target_page()).Serialize());
+ create_note.BindInt(5, model->target().type());
+
+ if (!create_note.Run())
+ return;
+
+ if (model->target().type() == UserNoteTarget::TargetType::kPageText) {
+ sql::Statement notes_text_target(db_.GetCachedStatement(
+ SQL_FROM_HERE,
+ "INSERT INTO notes_text_target(note_id, original_text, selector) "
+ "VALUES(?,?,?)"));
+ if (!notes_text_target.is_valid())
+ return;
+
+ notes_text_target.BindString(0, model->id().ToString());
+ notes_text_target.BindString(1, model->target().original_text());
+ notes_text_target.BindString(2, model->target().selector());
+
+ if (!notes_text_target.Run())
+ return;
+ }
+
+ sql::Statement notes_body(db_.GetCachedStatement(
+ SQL_FROM_HERE,
+ "INSERT INTO notes_body(note_id, type, plain_text) "
+ "VALUES(?,?,?)"));
+ if (!notes_body.is_valid())
+ return;
+
+ notes_body.BindString(0, model->id().ToString());
+ notes_body.BindInt(1, UserNoteBody::BodyType::PLAIN_TEXT);
+ notes_body.BindString(2, note_body_text);
+
+ if (!notes_body.Run())
+ return;
+
+ transaction.Commit();
}
-void UserNoteDatabase::UpdateNote(base::UnguessableToken id,
- std::string note_body_text) {
+void UserNoteDatabase::UpdateNote(const UserNote* model,
+ std::string note_body_text,
+ bool is_creation) {
+ if (is_creation) {
+ CreateNote(model, note_body_text);
+ return;
+ }
+
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // TODO(gayane): Implement.
+ if (!EnsureDBInit())
+ return;
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return;
+
+ // Only the text of the note body can be modified.
+ // TODO(crbug.com/1313967): This will need to be updated if in the future we
+ // wish to support changing the target text.
+ sql::Statement update_notes_body(db_.GetCachedStatement(
+ SQL_FROM_HERE, "UPDATE notes_body SET plain_text = ? WHERE note_id = ?"));
+ if (!update_notes_body.is_valid())
+ return;
+
+ update_notes_body.BindString(0, note_body_text);
+ update_notes_body.BindString(1, model->id().ToString());
+
+ if (!update_notes_body.Run())
+ return;
+
+ sql::Statement update_modification_date(db_.GetCachedStatement(
+ SQL_FROM_HERE, "UPDATE notes SET modification_date = ? WHERE id = ?"));
+ if (!update_modification_date.is_valid())
+ return;
+
+ update_modification_date.BindTime(0, base::Time::Now());
+ update_modification_date.BindString(1, model->id().ToString());
+
+ if (!update_modification_date.Run())
+ return;
+
+ transaction.Commit();
}
void UserNoteDatabase::DeleteNote(const base::UnguessableToken& id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // TODO(gayane): Implement.
+ if (!EnsureDBInit())
+ return;
+
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return;
+
+ sql::Statement delete_notes_body(db_.GetCachedStatement(
+ SQL_FROM_HERE, "DELETE FROM notes_body WHERE note_id = ?"));
+
+ if (!delete_notes_body.is_valid())
+ return;
+
+ delete_notes_body.BindString(0, id.ToString());
+ if (!delete_notes_body.Run())
+ return;
+
+ sql::Statement delete_notes_text_target(db_.GetCachedStatement(
+ SQL_FROM_HERE, "DELETE FROM notes_text_target WHERE note_id = ?"));
+ if (!delete_notes_text_target.is_valid())
+ return;
+
+ delete_notes_text_target.BindString(0, id.ToString());
+ if (!delete_notes_text_target.Run())
+ return;
+
+ sql::Statement delete_notes(
+ db_.GetCachedStatement(SQL_FROM_HERE, "DELETE FROM notes WHERE id = ?"));
+ if (!delete_notes.is_valid())
+ return;
+
+ delete_notes.BindString(0, id.ToString());
+ if (!delete_notes.Run())
+ return;
+
+ transaction.Commit();
}
void UserNoteDatabase::DeleteAllForUrl(const GURL& url) {
@@ -89,4 +261,135 @@ void UserNoteDatabase::DeleteAllNotes() {
// TODO(gayane): Implement.
}
-} // namespace user_notes \ No newline at end of file
+bool UserNoteDatabase::EnsureDBInit() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (db_.is_open())
+ return true;
+ return Init();
+}
+
+void UserNoteDatabase::DatabaseErrorCallback(int error, sql::Statement* stmt) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!sql::IsErrorCatastrophic(error))
+ return;
+
+ // Ignore repeated callbacks.
+ db_.reset_error_callback();
+
+ // After this call, the `db_` handle is poisoned so that future calls will
+ // return errors until the handle is re-opened.
+ db_.RazeAndClose();
+}
+
+bool UserNoteDatabase::InitSchema() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ sql::MetaTable meta_table;
+ bool has_metatable = meta_table.DoesTableExist(&db_);
+ bool has_schema = db_.DoesTableExist("notes");
+
+ if (!has_metatable && has_schema) {
+ // Existing DB with no meta table. Cannot determine DB version.
+ db_.Raze();
+ }
+
+ // Create the meta table if it doesn't exist.
+ if (!meta_table.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber)) {
+ return false;
+ }
+
+ // If DB and meta table already existed and current version is not compatible
+ // with DB then it should fail.
+ if (meta_table.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
+ return false;
+ }
+ if (!has_schema) {
+ return CreateSchema();
+ }
+
+ meta_table.SetVersionNumber(kCurrentVersionNumber);
+ meta_table.SetCompatibleVersionNumber(kCompatibleVersionNumber);
+ return true;
+}
+
+bool UserNoteDatabase::CreateSchema() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin()) {
+ return false;
+ }
+
+ // `id` is the primary key of the table.
+ // `creation_date` The date and time in seconds when the row was created.
+ // `modification_date` The date and time in seconds when the row was last
+ // modified.
+ // `url` The URL of the target page.
+ // `type` The type of target this note has (0-page, 1-page text).
+ // clang-format off
+ static constexpr char kUserNotesTableSql[] =
+ "CREATE TABLE IF NOT EXISTS notes("
+ "id TEXT PRIMARY KEY NOT NULL,"
+ "creation_date INTEGER NOT NULL,"
+ "modification_date INTEGER NOT NULL,"
+ "url TEXT NOT NULL,"
+ "origin TEXT NOT NULL,"
+ "type INTEGER NOT NULL)";
+ // clang-format on
+ if (!db_.Execute(kUserNotesTableSql)) {
+ return false;
+ }
+
+ // Optimizes user note look up by url.
+ // clang-format off
+ static constexpr char kUserNoteByUrlIndexSql[] =
+ "CREATE INDEX IF NOT EXISTS notes_by_url "
+ "ON notes(url)";
+ // clang-format on
+ if (!db_.Execute(kUserNoteByUrlIndexSql)) {
+ return false;
+ }
+
+ // Optimizes user note look up by origin.
+ // clang-format off
+ static constexpr char kUserNoteByOriginIndexSql[] =
+ "CREATE INDEX IF NOT EXISTS notes_by_origin "
+ "ON notes(origin)";
+ // clang-format on
+ if (!db_.Execute(kUserNoteByOriginIndexSql)) {
+ return false;
+ }
+
+ // `note_id` is the primary key of the table. Matches the `id` of
+ // corresponding note in `notes` table.
+ // `original_text` The original text to which the note was attached.
+ // `selector` The text fragment selector that identifies the target text.
+ // clang-format off
+ static constexpr char kUserNotesTextTargetTableSql[] =
+ "CREATE TABLE IF NOT EXISTS notes_text_target("
+ "note_id TEXT PRIMARY KEY NOT NULL,"
+ "original_text TEXT NOT NULL,"
+ "selector TEXT NOT NULL)";
+ // clang-format on
+ if (!db_.Execute(kUserNotesTextTargetTableSql)) {
+ return false;
+ }
+
+ // `note_id` is the primary key of the table. Matches the `id` of
+ // corresponding note in `notes` table.
+ // `type` The type of body this note has (only plain text is currently
+ // supported). `plain_text` The note body in plain text.
+ // clang-format off
+ static constexpr char kUserNotesBodyTableSql[] =
+ "CREATE TABLE IF NOT EXISTS notes_body("
+ "note_id TEXT PRIMARY KEY NOT NULL,"
+ "type INTEGER NOT NULL,"
+ "plain_text TEXT)";
+ // clang-format on
+ if (!db_.Execute(kUserNotesBodyTableSql)) {
+ return false;
+ }
+
+ return transaction.Commit();
+}
+} // namespace user_notes
diff --git a/chromium/components/user_notes/storage/user_note_database.h b/chromium/components/user_notes/storage/user_note_database.h
index ced32c06b22..75535ca4053 100644
--- a/chromium/components/user_notes/storage/user_note_database.h
+++ b/chromium/components/user_notes/storage/user_note_database.h
@@ -11,7 +11,7 @@
#include "base/files/file_path.h"
#include "base/sequence_checker.h"
#include "base/thread_annotations.h"
-#include "components/user_notes/interfaces/user_note_storage.h"
+#include "components/user_notes/interfaces/user_note_metadata_snapshot.h"
#include "components/user_notes/model/user_note.h"
#include "sql/database.h"
#include "url/gurl.h"
@@ -19,6 +19,9 @@
namespace user_notes {
+constexpr base::FilePath::CharType kDatabaseName[] =
+ FILE_PATH_LITERAL("UserNotes.db");
+
// Provides the backend SQLite support for user notes.
// This class must be used on a same blocking sequence.
class UserNoteDatabase {
@@ -27,22 +30,16 @@ class UserNoteDatabase {
~UserNoteDatabase();
// Initialises internal database. Must be called prior to any other usage.
- void Init();
+ bool Init();
- UserNoteStorage::UrlNoteMetadataIDMap GetNoteMetadataForUrls(
- std::vector<GURL> urls);
+ UserNoteMetadataSnapshot GetNoteMetadataForUrls(std::vector<GURL> urls);
std::vector<std::unique_ptr<UserNote>> GetNotesById(
std::vector<base::UnguessableToken> ids);
- void CreateNote(base::UnguessableToken id,
+ void UpdateNote(const UserNote* model,
std::string note_body_text,
- UserNoteTarget::TargetType target_type,
- std::string original_text,
- GURL target_page,
- std::string selector);
-
- void UpdateNote(base::UnguessableToken id, std::string note_body_text);
+ bool is_creation);
void DeleteNote(const base::UnguessableToken& id);
@@ -53,7 +50,26 @@ class UserNoteDatabase {
void DeleteAllNotes();
private:
+ FRIEND_TEST_ALL_PREFIXES(UserNoteDatabaseTest, UpdateNote);
+ FRIEND_TEST_ALL_PREFIXES(UserNoteDatabaseTest, CreateNote);
+ FRIEND_TEST_ALL_PREFIXES(UserNoteDatabaseTest, DeleteNote);
+
+ // Initialises internal database if needed.
+ bool EnsureDBInit();
+
+ // Called by the database to report errors.
+ void DatabaseErrorCallback(int error, sql::Statement* stmt);
+
+ // Creates or migrates to the new schema if needed.
+ bool InitSchema();
+
+ // Called by UpdateNote() with is_creation=true to create a new note.
+ void CreateNote(const UserNote* model, std::string note_body_text);
+
+ bool CreateSchema();
+
sql::Database db_ GUARDED_BY_CONTEXT(sequence_checker_);
+
const base::FilePath db_file_path_;
SEQUENCE_CHECKER(sequence_checker_);
diff --git a/chromium/components/user_notes/storage/user_note_database_unittest.cc b/chromium/components/user_notes/storage/user_note_database_unittest.cc
new file mode 100644
index 00000000000..e7bdde4409a
--- /dev/null
+++ b/chromium/components/user_notes/storage/user_note_database_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright 2022 The Chromium 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/user_notes/storage/user_note_database.h"
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "components/user_notes/model/user_note_model_test_utils.h"
+#include "sql/database.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/test/test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace user_notes {
+
+class UserNoteDatabaseTest : public testing::Test {
+ public:
+ UserNoteDatabaseTest() = default;
+
+ void SetUp() override { ASSERT_TRUE(temp_directory_.CreateUniqueTempDir()); }
+ void TearDown() override { EXPECT_TRUE(temp_directory_.Delete()); }
+
+ base::FilePath db_dir() { return temp_directory_.GetPath(); }
+
+ base::FilePath db_file_path() {
+ return temp_directory_.GetPath().Append(kDatabaseName);
+ }
+
+ private:
+ base::ScopedTempDir temp_directory_;
+};
+
+TEST_F(UserNoteDatabaseTest, InitDatabase) {
+ EXPECT_FALSE(base::PathExists(db_file_path()));
+ {
+ std::unique_ptr<UserNoteDatabase> user_note_db =
+ std::make_unique<UserNoteDatabase>(db_dir());
+
+ EXPECT_FALSE(base::PathExists(db_file_path()));
+
+ EXPECT_TRUE(user_note_db->Init());
+ EXPECT_TRUE(base::PathExists(db_file_path()));
+ }
+
+ {
+ sql::Database db;
+ EXPECT_TRUE(db.Open(db_file_path()));
+
+ // Should have 4 tables and 6 indexes
+ // tables - user_notes, user_notes_text_target, user_note_body, meta.
+ // indexes - 1 implicit index for all 4 tables, url and origin index for
+ // `notes` table.
+ EXPECT_EQ(4u, sql::test::CountSQLTables(&db));
+ EXPECT_EQ(6u, sql::test::CountSQLIndices(&db));
+ }
+}
+
+TEST_F(UserNoteDatabaseTest, DatabaseNewVersion) {
+ ASSERT_FALSE(base::PathExists(db_file_path()));
+
+ // Create an empty database with a newer schema version (version=1000000).
+ {
+ sql::Database db;
+ EXPECT_TRUE(db.Open(db_file_path()));
+
+ sql::MetaTable meta_table;
+ constexpr int kFutureVersionNumber = 1000000;
+ EXPECT_TRUE(meta_table.Init(&db, /*version=*/kFutureVersionNumber,
+ /*compatible_version=*/kFutureVersionNumber));
+
+ EXPECT_EQ(1u, sql::test::CountSQLTables(&db)) << db.GetSchema();
+ }
+
+ EXPECT_TRUE(base::PathExists(db_file_path()));
+ // Calling Init DB with existing DB ahead of current version should fail.
+ {
+ std::unique_ptr<UserNoteDatabase> user_note_db =
+ std::make_unique<UserNoteDatabase>(db_dir());
+ EXPECT_FALSE(user_note_db->Init());
+ }
+}
+
+TEST_F(UserNoteDatabaseTest, DatabaseHasSchemaNoMeta) {
+ ASSERT_FALSE(base::PathExists(db_file_path()));
+
+ // Init DB with all tables including meta.
+ {
+ std::unique_ptr<UserNoteDatabase> user_note_db =
+ std::make_unique<UserNoteDatabase>(db_dir());
+ EXPECT_TRUE(user_note_db->Init());
+ }
+
+ // Drop meta table.
+ {
+ sql::Database db;
+ EXPECT_TRUE(db.Open(db_file_path()));
+ sql::MetaTable::DeleteTableForTesting(&db);
+ }
+
+ // Init again with no meta should raze the DB and recreate again successfully.
+ {
+ std::unique_ptr<UserNoteDatabase> user_note_db =
+ std::make_unique<UserNoteDatabase>(db_dir());
+ EXPECT_TRUE(user_note_db->Init());
+ // TODO(gayane): Start with an non-empty DB and check that here the DB is
+ // empty.
+ }
+}
+
+TEST_F(UserNoteDatabaseTest, CreateNote) {
+ UserNoteDatabase user_note_db(db_dir());
+ EXPECT_TRUE(user_note_db.Init());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(user_note_db.sequence_checker_);
+
+ base::UnguessableToken note_id = base::UnguessableToken::Create();
+ UserNote* user_note =
+ new UserNote(note_id, GetTestUserNoteMetadata(), GetTestUserNoteBody(),
+ GetTestUserNotePageTarget());
+
+ user_note_db.UpdateNote(user_note, "new test note", /*is_creation=*/true);
+
+ sql::Statement statement(user_note_db.db_.GetCachedStatement(
+ SQL_FROM_HERE, "SELECT plain_text FROM notes_body WHERE note_id = ?"));
+
+ EXPECT_TRUE(statement.is_valid());
+ statement.BindString(0, note_id.ToString());
+ EXPECT_TRUE(statement.Step());
+
+ EXPECT_EQ(1, statement.ColumnCount());
+ EXPECT_EQ("new test note", statement.ColumnString(0));
+ delete user_note;
+}
+
+TEST_F(UserNoteDatabaseTest, UpdateNote) {
+ UserNoteDatabase user_note_db(db_dir());
+ EXPECT_TRUE(user_note_db.Init());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(user_note_db.sequence_checker_);
+
+ base::UnguessableToken note_id = base::UnguessableToken::Create();
+ UserNote* user_note =
+ new UserNote(note_id, GetTestUserNoteMetadata(), GetTestUserNoteBody(),
+ GetTestUserNotePageTarget());
+
+ user_note_db.UpdateNote(user_note, "new test note", /*is_creation=*/true);
+ user_note_db.UpdateNote(user_note, "edit test note", false);
+
+ sql::Statement statement(user_note_db.db_.GetCachedStatement(
+ SQL_FROM_HERE, "SELECT plain_text FROM notes_body WHERE note_id = ?"));
+
+ EXPECT_TRUE(statement.is_valid());
+ statement.BindString(0, note_id.ToString());
+ EXPECT_TRUE(statement.Step());
+
+ EXPECT_EQ(1, statement.ColumnCount());
+ EXPECT_EQ("edit test note", statement.ColumnString(0));
+ delete user_note;
+}
+
+TEST_F(UserNoteDatabaseTest, DeleteNote) {
+ UserNoteDatabase user_note_db(db_dir());
+ EXPECT_TRUE(user_note_db.Init());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(user_note_db.sequence_checker_);
+
+ base::UnguessableToken note_id = base::UnguessableToken::Create();
+ UserNote* user_note =
+ new UserNote(note_id, GetTestUserNoteMetadata(), GetTestUserNoteBody(),
+ GetTestUserNotePageTarget());
+
+ user_note_db.UpdateNote(user_note, "new test note", /*is_creation=*/true);
+ user_note_db.DeleteNote(note_id);
+
+ sql::Statement statement_notes_body(user_note_db.db_.GetCachedStatement(
+ SQL_FROM_HERE, "SELECT note_id FROM notes_body WHERE note_id = ?"));
+ EXPECT_TRUE(statement_notes_body.is_valid());
+ statement_notes_body.BindString(0, note_id.ToString());
+ EXPECT_FALSE(statement_notes_body.Step());
+
+ sql::Statement statement_notes(user_note_db.db_.GetCachedStatement(
+ SQL_FROM_HERE, "SELECT id FROM notes WHERE id = ?"));
+ EXPECT_TRUE(statement_notes.is_valid());
+ statement_notes.BindString(0, note_id.ToString());
+ EXPECT_FALSE(statement_notes.Step());
+
+ sql::Statement statement_notes_text_target(
+ user_note_db.db_.GetCachedStatement(
+ SQL_FROM_HERE,
+ "SELECT note_id FROM notes_text_target WHERE note_id = ?"));
+ EXPECT_TRUE(statement_notes_text_target.is_valid());
+ statement_notes_text_target.BindString(0, note_id.ToString());
+ EXPECT_FALSE(statement_notes_text_target.Step());
+ delete user_note;
+}
+
+} // namespace user_notes
diff --git a/chromium/components/user_notes/storage/user_note_storage_impl.cc b/chromium/components/user_notes/storage/user_note_storage_impl.cc
index 4cd9f77a787..afdc176e096 100644
--- a/chromium/components/user_notes/storage/user_note_storage_impl.cc
+++ b/chromium/components/user_notes/storage/user_note_storage_impl.cc
@@ -26,7 +26,7 @@ UserNoteStorageImpl::UserNoteStorageImpl(
void UserNoteStorageImpl::GetNoteMetadataForUrls(
std::vector<GURL> urls,
- base::OnceCallback<void(UserNoteStorage::UrlNoteMetadataIDMap)> callback) {
+ base::OnceCallback<void(UserNoteMetadataSnapshot)> callback) {
database_.AsyncCall(&UserNoteDatabase::GetNoteMetadataForUrls)
.WithArgs(std::move(urls))
.Then(std::move(callback));
@@ -40,21 +40,11 @@ void UserNoteStorageImpl::GetNotesById(
.Then(std::move(callback));
}
-void UserNoteStorageImpl::CreateNote(base::UnguessableToken id,
+void UserNoteStorageImpl::UpdateNote(const UserNote* model,
std::string note_body_text,
- UserNoteTarget::TargetType target_type,
- std::string original_text,
- GURL target_page,
- std::string selector) {
- database_.AsyncCall(&UserNoteDatabase::CreateNote)
- .WithArgs(id, note_body_text, target_type, original_text, target_page,
- selector);
-}
-
-void UserNoteStorageImpl::UpdateNote(base::UnguessableToken id,
- std::string note_body_text) {
+ bool is_creation) {
database_.AsyncCall(&UserNoteDatabase::UpdateNote)
- .WithArgs(id, note_body_text);
+ .WithArgs(model, note_body_text, is_creation);
}
void UserNoteStorageImpl::DeleteNote(const base::UnguessableToken& id) {
diff --git a/chromium/components/user_notes/storage/user_note_storage_impl.h b/chromium/components/user_notes/storage/user_note_storage_impl.h
index 3d65c100712..b12a0852979 100644
--- a/chromium/components/user_notes/storage/user_note_storage_impl.h
+++ b/chromium/components/user_notes/storage/user_note_storage_impl.h
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/threading/sequence_bound.h"
+#include "components/user_notes/interfaces/user_note_metadata_snapshot.h"
#include "components/user_notes/interfaces/user_note_storage.h"
#include "components/user_notes/model/user_note.h"
#include "components/user_notes/storage/user_note_database.h"
@@ -30,23 +31,16 @@ class UserNoteStorageImpl : public UserNoteStorage {
// Implement UserNoteStorage
void GetNoteMetadataForUrls(
std::vector<GURL> urls,
- base::OnceCallback<void(UserNoteStorage::UrlNoteMetadataIDMap)> callback)
- override;
+ base::OnceCallback<void(UserNoteMetadataSnapshot)> callback) override;
void GetNotesById(
std::vector<base::UnguessableToken> ids,
base::OnceCallback<void(std::vector<std::unique_ptr<UserNote>>)> callback)
override;
- void CreateNote(base::UnguessableToken id,
+ void UpdateNote(const UserNote* model,
std::string note_body_text,
- UserNoteTarget::TargetType target_type,
- std::string original_text,
- GURL target_page,
- std::string selector) override;
-
- void UpdateNote(base::UnguessableToken id,
- std::string note_body_text) override;
+ bool is_creation = false) override;
void DeleteNote(const base::UnguessableToken& id) override;
diff --git a/chromium/components/user_prefs/android/BUILD.gn b/chromium/components/user_prefs/android/BUILD.gn
index edc94b45fe6..e99244cacb1 100644
--- a/chromium/components/user_prefs/android/BUILD.gn
+++ b/chromium/components/user_prefs/android/BUILD.gn
@@ -11,7 +11,8 @@ generate_jni("jni_headers") {
android_library("java") {
sources = [ "java/src/org/chromium/components/user_prefs/UserPrefs.java" ]
deps = [
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/prefs/android:java",
"//content/public/android:content_full_java",
"//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chromium/components/variations/BUILD.gn b/chromium/components/variations/BUILD.gn
index aabaa6ccf74..4e74a050be4 100644
--- a/chromium/components/variations/BUILD.gn
+++ b/chromium/components/variations/BUILD.gn
@@ -16,7 +16,8 @@ if (is_android) {
import("//build/config/android/rules.gni")
}
-large_variation_key_size_enabled = is_chromecast || is_chromeos_lacros
+large_variation_key_size_enabled =
+ is_castos || is_cast_android || is_chromeos_lacros
buildflag_header("buildflags") {
header = "buildflags.h"
@@ -159,15 +160,18 @@ if (is_android) {
android_library("variations_java") {
srcjar_deps = [
":java_switches_srcjar",
- ":load_seed_result_enum_srcjar",
+ ":variations_enum_srcjar",
]
deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
}
- java_cpp_enum("load_seed_result_enum_srcjar") {
+ java_cpp_enum("variations_enum_srcjar") {
# External code should depend on ":variations_java" instead.
visibility = [ ":*" ]
- sources = [ "metrics.h" ]
+ sources = [
+ "metrics.h",
+ "synthetic_trials.h",
+ ]
}
}
@@ -208,6 +212,7 @@ source_set("unit_tests") {
"hashing_unittest.cc",
"net/variations_command_line_unittest.cc",
"net/variations_http_headers_unittest.cc",
+ "processed_study_unittest.cc",
"simulate_for_crosstalk_unittest.cc",
"study_filtering_unittest.cc",
"synthetic_trial_registry_unittest.cc",
diff --git a/chromium/components/variations/active_field_trials.cc b/chromium/components/variations/active_field_trials.cc
index 5f100d25c01..789082e29db 100644
--- a/chromium/components/variations/active_field_trials.cc
+++ b/chromium/components/variations/active_field_trials.cc
@@ -22,20 +22,6 @@ namespace {
base::LazyInstance<std::string>::Leaky g_seed_version;
-// Populates |name_group_ids| based on |active_groups|. Field trial names are
-// suffixed with |suffix| before hashing is executed.
-void GetFieldTrialActiveGroupIdsForActiveGroups(
- base::StringPiece suffix,
- const base::FieldTrial::ActiveGroups& active_groups,
- std::vector<ActiveGroupId>* name_group_ids) {
- DCHECK(name_group_ids->empty());
- for (auto it = active_groups.begin(); it != active_groups.end(); ++it) {
- name_group_ids->push_back(
- MakeActiveGroupId(it->trial_name + std::string(suffix),
- it->group_name + std::string(suffix)));
- }
-}
-
void AppendActiveGroupIdsAsStrings(
const std::vector<ActiveGroupId> name_group_ids,
std::vector<std::string>* output) {
@@ -55,6 +41,18 @@ ActiveGroupId MakeActiveGroupId(base::StringPiece trial_name,
return id;
}
+void GetFieldTrialActiveGroupIdsForActiveGroups(
+ base::StringPiece suffix,
+ const base::FieldTrial::ActiveGroups& active_groups,
+ std::vector<ActiveGroupId>* name_group_ids) {
+ DCHECK(name_group_ids->empty());
+ for (const auto& active_group : active_groups) {
+ name_group_ids->push_back(
+ MakeActiveGroupId(active_group.trial_name + std::string(suffix),
+ active_group.group_name + std::string(suffix)));
+ }
+}
+
void GetFieldTrialActiveGroupIds(base::StringPiece suffix,
std::vector<ActiveGroupId>* name_group_ids) {
DCHECK(name_group_ids->empty());
diff --git a/chromium/components/variations/active_field_trials.h b/chromium/components/variations/active_field_trials.h
index a7bb12a8781..f3b5273ee50 100644
--- a/chromium/components/variations/active_field_trials.h
+++ b/chromium/components/variations/active_field_trials.h
@@ -40,6 +40,14 @@ struct COMPONENT_EXPORT(VARIATIONS) ActiveGroupIdCompare {
}
};
+// Populates |name_group_ids| based on |active_groups|. Field trial names are
+// suffixed with |suffix| before hashing is executed.
+COMPONENT_EXPORT(VARIATIONS)
+void GetFieldTrialActiveGroupIdsForActiveGroups(
+ base::StringPiece suffix,
+ const base::FieldTrial::ActiveGroups& active_groups,
+ std::vector<ActiveGroupId>* name_group_ids);
+
// Fills the supplied vector |name_group_ids| (which must be empty when called)
// with unique ActiveGroupIds for each Field Trial that has a chosen group.
// Field Trials for which a group has not been chosen yet are NOT returned in
diff --git a/chromium/components/variations/android/BUILD.gn b/chromium/components/variations/android/BUILD.gn
index 8eabe976be4..fb1996dc094 100644
--- a/chromium/components/variations/android/BUILD.gn
+++ b/chromium/components/variations/android/BUILD.gn
@@ -7,6 +7,8 @@ import("//build/config/android/rules.gni")
android_library("variations_java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/variations:variations_java",
"//components/variations/proto:proto_java",
"//net/android:net_java",
diff --git a/chromium/components/variations/field_trial_config/field_trial_util.cc b/chromium/components/variations/field_trial_config/field_trial_util.cc
index ee0c8ffe57b..3bfca83917f 100644
--- a/chromium/components/variations/field_trial_config/field_trial_util.cc
+++ b/chromium/components/variations/field_trial_config/field_trial_util.cc
@@ -16,12 +16,12 @@
#include "base/feature_list.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
+#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "components/variations/client_filterable_state.h"
#include "components/variations/field_trial_config/fieldtrial_testing_config.h"
#include "components/variations/variations_seed_processor.h"
-#include "net/base/escape.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/device_form_factor.h"
@@ -189,7 +189,7 @@ std::string EscapeValue(const std::string& value) {
// This needs to be the inverse of UnescapeValue in
// base/metrics/field_trial_params.
std::string net_escaped_str =
- net::EscapeQueryParamValue(value, true /* use_plus */);
+ base::EscapeQueryParamValue(value, true /* use_plus */);
// net doesn't escape '.' and '*' but base::UnescapeValue() covers those
// cases.
diff --git a/chromium/components/variations/net/variations_command_line.cc b/chromium/components/variations/net/variations_command_line.cc
index b515f16cd43..b304965d52d 100644
--- a/chromium/components/variations/net/variations_command_line.cc
+++ b/chromium/components/variations/net/variations_command_line.cc
@@ -7,9 +7,9 @@
#include "base/base_switches.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial.h"
+#include "base/strings/escape.h"
#include "components/variations/field_trial_config/field_trial_util.h"
#include "components/variations/variations_switches.h"
-#include "net/base/escape.h"
namespace variations {
diff --git a/chromium/components/variations/processed_study.cc b/chromium/components/variations/processed_study.cc
index 9aad00bb237..b3b31361c07 100644
--- a/chromium/components/variations/processed_study.cc
+++ b/chromium/components/variations/processed_study.cc
@@ -14,26 +14,8 @@
#include "components/variations/proto/study.pb.h"
namespace variations {
-
namespace {
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class InvalidStudyReason {
- kInvalidMinVersion = 0,
- kInvalidMaxVersion = 1,
- kInvalidMinOsVersion = 2,
- kInvalidMaxOsVersion = 3,
- kMissingExperimentName = 4,
- kRepeatedExperimentName = 5,
- kTotalProbabilityOverflow = 6,
- kMissingDefaultExperimentInList = 7,
- kBlankStudyName = 8,
- kExperimentProbabilityOverflow = 9,
- kTriggerAndNonTriggerExperimentId = 10,
- kMaxValue = kTriggerAndNonTriggerExperimentId,
-};
-
void LogInvalidReason(InvalidStudyReason reason) {
base::UmaHistogramEnumeration("Variations.InvalidStudyReason", reason);
}
diff --git a/chromium/components/variations/processed_study.h b/chromium/components/variations/processed_study.h
index 9034012aecc..ab8e523b536 100644
--- a/chromium/components/variations/processed_study.h
+++ b/chromium/components/variations/processed_study.h
@@ -14,6 +14,23 @@
namespace variations {
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. Exposed for testing.
+enum class InvalidStudyReason {
+ kInvalidMinVersion = 0,
+ kInvalidMaxVersion = 1,
+ kInvalidMinOsVersion = 2,
+ kInvalidMaxOsVersion = 3,
+ kMissingExperimentName = 4,
+ kRepeatedExperimentName = 5,
+ kTotalProbabilityOverflow = 6,
+ kMissingDefaultExperimentInList = 7,
+ kBlankStudyName = 8,
+ kExperimentProbabilityOverflow = 9,
+ kTriggerAndNonTriggerExperimentId = 10,
+ kMaxValue = kTriggerAndNonTriggerExperimentId,
+};
+
class Study;
// Wrapper over Study with extra information computed during pre-processing,
diff --git a/chromium/components/variations/processed_study_unittest.cc b/chromium/components/variations/processed_study_unittest.cc
new file mode 100644
index 00000000000..336434324e6
--- /dev/null
+++ b/chromium/components/variations/processed_study_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright 2022 The Chromium 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/variations/processed_study.h"
+
+#include <cstdint>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "components/variations/proto/study.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace variations {
+namespace {
+
+const char kInvalidStudyReasonHistogram[] = "Variations.InvalidStudyReason";
+const uint32_t kMaxProbabilityValue =
+ std::numeric_limits<base::FieldTrial::Probability>::max();
+
+// Adds an experiment with the given name and probability to a study.
+Study::Experiment* AddExperiment(Study& study,
+ const std::string& name,
+ uint32_t probability) {
+ Study::Experiment* experiment = study.add_experiment();
+ experiment->set_name(name);
+ experiment->set_probability_weight(probability);
+ return experiment;
+}
+
+// Creates a study with the given name.
+Study CreateStudy(const std::string& name) {
+ Study study;
+ study.set_name(name);
+ return study;
+}
+
+// Creates a valid study named "Study". This study has min and max version
+// filters, min and max OS version filters, and three groups: Default, Enabled,
+// and Disabled. The Enabled and Disabled groups have GWS IDs. The study's
+// default experiment is the Default group.
+Study CreateValidStudy() {
+ Study study = CreateStudy("Study");
+
+ Study::Filter* filter = study.mutable_filter();
+ filter->set_min_version("1.1.*");
+ filter->set_max_version("2.2.2.2");
+ filter->set_min_os_version("1");
+ filter->set_max_os_version("2.*");
+
+ Study::Experiment* default_experiment = AddExperiment(study, "Default", 0);
+
+ Study::Experiment* enabled_experiment = AddExperiment(study, "Enabled", 50);
+ enabled_experiment->set_google_web_experiment_id(1);
+
+ Study::Experiment* disabled_experiment = AddExperiment(study, "Disabled", 50);
+ disabled_experiment->set_google_web_experiment_id(2);
+
+ study.set_default_experiment_name(default_experiment->name());
+
+ return study;
+}
+
+} // namespace
+
+TEST(ProcessedStudyTest, InitValidStudy) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+
+ ProcessedStudy processed_study;
+ EXPECT_TRUE(processed_study.Init(&study, false));
+ histogram_tester.ExpectTotalCount(kInvalidStudyReasonHistogram, 0);
+}
+
+// Verifies that a study with an invalid min version filter is invalid.
+TEST(ProcessedStudyTest, InitInvalidMinVersion) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+ study.mutable_filter()->set_min_version("invalid");
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(
+ kInvalidStudyReasonHistogram, InvalidStudyReason::kInvalidMinVersion, 1);
+}
+
+// Verifies that a study with an invalid max version filter is invalid.
+TEST(ProcessedStudyTest, InitInvalidMaxVersion) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+ study.mutable_filter()->set_max_version("1.invalid.1");
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(
+ kInvalidStudyReasonHistogram, InvalidStudyReason::kInvalidMaxVersion, 1);
+}
+
+// Verifies that a study with an invalid min OS version filter is invalid.
+TEST(ProcessedStudyTest, InitInvalidMinOsVersion) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+ study.mutable_filter()->set_min_os_version("0.*.0");
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(kInvalidStudyReasonHistogram,
+ InvalidStudyReason::kInvalidMinOsVersion,
+ 1);
+}
+
+// Verifies that a study with an invalid max OS version filter is invalid.
+TEST(ProcessedStudyTest, InitInvalidMaxOsVersion) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+ study.mutable_filter()->set_max_os_version("\001\000\000\003");
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(kInvalidStudyReasonHistogram,
+ InvalidStudyReason::kInvalidMaxOsVersion,
+ 1);
+}
+
+// Verifies that a study with a blank study name is invalid.
+TEST(ProcessedStudyTest, InitBlankStudyName) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+ study.set_name("");
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(kInvalidStudyReasonHistogram,
+ InvalidStudyReason::kBlankStudyName, 1);
+}
+
+// Verifies that a study with an experiment that has no name is invalid.
+TEST(ProcessedStudyTest, InitMissingExperimentName) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+
+ AddExperiment(study, "", 0);
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(
+ kInvalidStudyReasonHistogram, InvalidStudyReason::kMissingExperimentName,
+ 1);
+}
+
+// Verifies that a study with multiple experiments that are named the same is
+// invalid.
+TEST(ProcessedStudyTest, InitRepeatedExperimentName) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+
+ AddExperiment(study, "Group", 0);
+ AddExperiment(study, "Group", 0);
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(
+ kInvalidStudyReasonHistogram, InvalidStudyReason::kRepeatedExperimentName,
+ 1);
+}
+
+// Verifies that a study with an experiment that specified both a trigger and
+// non-trigger GWS id is invalid.
+TEST(ProcessedStudyTest, InitTriggerAndNonTriggerExperimentId) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+
+ Study::Experiment* experiment = AddExperiment(study, "Group", 0);
+ experiment->set_google_web_experiment_id(123);
+ experiment->set_google_web_trigger_experiment_id(123);
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(
+ kInvalidStudyReasonHistogram,
+ InvalidStudyReason::kTriggerAndNonTriggerExperimentId, 1);
+}
+
+// Verifies that a study with an experiment that has a probability over the
+// maximum is invalid.
+TEST(ProcessedStudyTest, InitExperimentProbabilityOverflow) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateStudy("Study");
+
+ AddExperiment(study, "Group", kMaxProbabilityValue + 1);
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(
+ kInvalidStudyReasonHistogram,
+ InvalidStudyReason::kExperimentProbabilityOverflow, 1);
+}
+
+// Verifies that a study with groups whose total probability is over the maximum
+// is invalid.
+TEST(ProcessedStudyTest, InitTotalProbabilityOverflow) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateStudy("Study");
+
+ AddExperiment(study, "Group1", kMaxProbabilityValue);
+ AddExperiment(study, "Group2", 1);
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(
+ kInvalidStudyReasonHistogram,
+ InvalidStudyReason::kTotalProbabilityOverflow, 1);
+}
+
+// Verifies that a study that specifies a default experiment name but does not
+// contain an experiment with that name is invalid.
+TEST(ProcessedStudyTest, InitMissingDefaultExperimentInList) {
+ base::HistogramTester histogram_tester;
+
+ Study study = CreateValidStudy();
+
+ study.set_default_experiment_name("NonExistentGroup");
+
+ ProcessedStudy processed_study;
+ EXPECT_FALSE(processed_study.Init(&study, false));
+ histogram_tester.ExpectUniqueSample(
+ kInvalidStudyReasonHistogram,
+ InvalidStudyReason::kMissingDefaultExperimentInList, 1);
+}
+
+} // namespace variations \ No newline at end of file
diff --git a/chromium/components/variations/service/variations_field_trial_creator.cc b/chromium/components/variations/service/variations_field_trial_creator.cc
index ac93e0cf388..e32fc0f6622 100644
--- a/chromium/components/variations/service/variations_field_trial_creator.cc
+++ b/chromium/components/variations/service/variations_field_trial_creator.cc
@@ -478,17 +478,9 @@ void VariationsFieldTrialCreator::MaybeExtendVariationsSafeMode(
base::FieldTrialList::FindFullName(kExtendedSafeModeTrial);
DCHECK(!group_name.empty());
- if (group_name == kDefaultGroup)
+ if (group_name == kDefaultGroup || group_name == kControlGroup)
return;
- if (group_name == kControlGroup) {
- // Populate the histogram for the control group to more easily compare it
- // with the groups that introduce new behavior.
- SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
- "Variations.ExtendedSafeMode.WritePrefsTime");
- return;
- }
-
DCHECK_EQ(group_name, kEnabledGroup);
metrics_state_manager->LogHasSessionShutdownCleanly(
/*has_session_shutdown_cleanly=*/false,
diff --git a/chromium/components/variations/service/variations_field_trial_creator_unittest.cc b/chromium/components/variations/service/variations_field_trial_creator_unittest.cc
index 493e1852acc..24cebd2c216 100644
--- a/chromium/components/variations/service/variations_field_trial_creator_unittest.cc
+++ b/chromium/components/variations/service/variations_field_trial_creator_unittest.cc
@@ -12,6 +12,7 @@
#include "base/base_switches.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
+#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
@@ -1190,8 +1191,6 @@ TEST_F(FieldTrialCreatorSafeModeExperimentTest, OptOutOfExperiment) {
EXPECT_FALSE(base::FieldTrialList::IsTrialActive(kExtendedSafeModeTrial));
EXPECT_FALSE(
field_trial_creator.was_maybe_extend_variations_safe_mode_called());
- histogram_tester.ExpectTotalCount(
- "Variations.ExtendedSafeMode.WritePrefsTime", 0);
}
INSTANTIATE_TEST_SUITE_P(
@@ -1298,10 +1297,6 @@ TEST_F(FieldTrialCreatorSafeModeExperimentTest,
EXPECT_EQ(active_group,
base::FieldTrialList::FindValue(kExtendedSafeModeTrial));
- // Check metrics.
- histogram_tester.ExpectTotalCount(
- "Variations.ExtendedSafeMode.WritePrefsTime", 1);
-
// Verify that the beacon file does not exist.
EXPECT_FALSE(base::PathExists(
user_data_dir_path().Append(variations::kVariationsFilename)));
@@ -1342,13 +1337,10 @@ TEST_F(FieldTrialCreatorSafeModeExperimentTest,
ASSERT_TRUE(
base::ReadFileToString(variations_file_path, &beacon_file_contents));
EXPECT_EQ(beacon_file_contents,
- "{\"monitoring_stage\":2,"
- "\"user_experience_metrics.stability.exited_cleanly\":false,"
+ "{\"user_experience_metrics.stability.exited_cleanly\":false,"
"\"variations_crash_streak\":0}");
// Verify metrics.
- histogram_tester.ExpectTotalCount(
- "Variations.ExtendedSafeMode.WritePrefsTime", 1);
histogram_tester.ExpectUniqueSample(
"Variations.ExtendedSafeMode.BeaconFileWrite", 1, 1);
}
diff --git a/chromium/components/variations/synthetic_trial_registry.cc b/chromium/components/variations/synthetic_trial_registry.cc
index 252fe64e435..0c6fca23b52 100644
--- a/chromium/components/variations/synthetic_trial_registry.cc
+++ b/chromium/components/variations/synthetic_trial_registry.cc
@@ -10,6 +10,7 @@
#include "base/metrics/histogram_functions.h"
#include "base/observer_list.h"
#include "base/strings/string_number_conversions.h"
+#include "components/variations/active_field_trials.h"
#include "components/variations/hashing.h"
#include "components/variations/variations_associated_data.h"
@@ -61,7 +62,7 @@ void SyntheticTrialRegistry::RegisterExternalExperiments(
// When overriding previous external experiments, remove them now.
if (mode == kOverrideExistingIds) {
auto is_external = [](const SyntheticTrialGroup& group) {
- return group.is_external;
+ return group.is_external();
};
base::EraseIf(synthetic_trial_groups_, is_external);
}
@@ -80,7 +81,7 @@ void SyntheticTrialRegistry::RegisterExternalExperiments(
// are already registered.
if (mode == kDoNotOverrideExistingIds) {
auto matches_trial = [trial_hash](const SyntheticTrialGroup& group) {
- return group.id.name == trial_hash;
+ return group.id().name == trial_hash;
};
const auto& groups = synthetic_trial_groups_;
if (std::any_of(groups.begin(), groups.end(), matches_trial)) {
@@ -97,10 +98,10 @@ void SyntheticTrialRegistry::RegisterExternalExperiments(
GOOGLE_WEB_PROPERTIES_SIGNED_IN, {trial_hash, group_hash},
static_cast<VariationID>(experiment_id));
SyntheticTrialGroup entry(
- trial_hash, group_hash,
+ study_name, experiment_id_str,
variations::SyntheticTrialAnnotationMode::kNextLog);
- entry.start_time = start_time;
- entry.is_external = true;
+ entry.SetStartTime(start_time);
+ entry.SetIsExternal(true);
synthetic_trial_groups_.push_back(entry);
trials_added++;
}
@@ -115,14 +116,14 @@ void SyntheticTrialRegistry::RegisterExternalExperiments(
void SyntheticTrialRegistry::RegisterSyntheticFieldTrial(
const SyntheticTrialGroup& trial) {
for (auto& entry : synthetic_trial_groups_) {
- if (entry.id.name == trial.id.name) {
+ if (entry.id().name == trial.id().name) {
// Don't necessarily need to notify observers when setting
// |annotation_mode| as it is only used when producing metrics reports
// and does not affect variations service.
- entry.annotation_mode = trial.annotation_mode;
- if (entry.id.group != trial.id.group) {
- entry.id.group = trial.id.group;
- entry.start_time = base::TimeTicks::Now();
+ entry.SetAnnotationMode(trial.annotation_mode());
+ if (entry.id().group != trial.id().group) {
+ entry.SetGroupName(trial.group_name());
+ entry.SetStartTime(base::TimeTicks::Now());
NotifySyntheticTrialObservers();
}
return;
@@ -130,7 +131,7 @@ void SyntheticTrialRegistry::RegisterSyntheticFieldTrial(
}
SyntheticTrialGroup trial_group = trial;
- trial_group.start_time = base::TimeTicks::Now();
+ trial_group.SetStartTime(base::TimeTicks::Now());
synthetic_trial_groups_.push_back(trial_group);
NotifySyntheticTrialObservers();
}
@@ -165,14 +166,19 @@ void SyntheticTrialRegistry::NotifySyntheticTrialObservers() {
void SyntheticTrialRegistry::GetSyntheticFieldTrialsOlderThan(
base::TimeTicks time,
- std::vector<ActiveGroupId>* synthetic_trials) const {
+ std::vector<ActiveGroupId>* synthetic_trials,
+ base::StringPiece suffix) const {
DCHECK(synthetic_trials);
synthetic_trials->clear();
+ base::FieldTrial::ActiveGroups active_groups;
for (const auto& entry : synthetic_trial_groups_) {
- if (entry.start_time <= time ||
- entry.annotation_mode == SyntheticTrialAnnotationMode::kCurrentLog)
- synthetic_trials->push_back(entry.id);
+ if (entry.start_time() <= time ||
+ entry.annotation_mode() == SyntheticTrialAnnotationMode::kCurrentLog)
+ active_groups.push_back(entry.active_group());
}
+
+ GetFieldTrialActiveGroupIdsForActiveGroups(suffix, active_groups,
+ synthetic_trials);
}
} // namespace variations
diff --git a/chromium/components/variations/synthetic_trial_registry.h b/chromium/components/variations/synthetic_trial_registry.h
index 63d8700bbcf..6e299bccaff 100644
--- a/chromium/components/variations/synthetic_trial_registry.h
+++ b/chromium/components/variations/synthetic_trial_registry.h
@@ -85,6 +85,8 @@ class COMPONENT_EXPORT(VARIATIONS) SyntheticTrialRegistry {
friend SyntheticTrialRegistryTest;
FRIEND_TEST_ALL_PREFIXES(SyntheticTrialRegistryTest, RegisterSyntheticTrial);
FRIEND_TEST_ALL_PREFIXES(SyntheticTrialRegistryTest,
+ GetSyntheticFieldTrialsOlderThanSuffix);
+ FRIEND_TEST_ALL_PREFIXES(SyntheticTrialRegistryTest,
GetSyntheticFieldTrialActiveGroups);
FRIEND_TEST_ALL_PREFIXES(VariationsCrashKeysTest, BasicFunctionality);
@@ -119,10 +121,12 @@ class COMPONENT_EXPORT(VARIATIONS) SyntheticTrialRegistry {
const std::string& experiment_id);
// Returns a list of synthetic field trials that are either (1) older than
- // |time|, or (2) specify |kCurrentLog| as |annotation_mode|.
+ // |time|, or (2) specify |kCurrentLog| as |annotation_mode|. The trial and
+ // group names are suffixed with |suffix| before being hashed.
void GetSyntheticFieldTrialsOlderThan(
base::TimeTicks time,
- std::vector<ActiveGroupId>* synthetic_trials) const;
+ std::vector<ActiveGroupId>* synthetic_trials,
+ base::StringPiece suffix = "") const;
// Notifies observers on a synthetic trial list change.
void NotifySyntheticTrialObservers();
diff --git a/chromium/components/variations/synthetic_trial_registry_unittest.cc b/chromium/components/variations/synthetic_trial_registry_unittest.cc
index caf67799bf0..6ab67c82842 100644
--- a/chromium/components/variations/synthetic_trial_registry_unittest.cc
+++ b/chromium/components/variations/synthetic_trial_registry_unittest.cc
@@ -69,11 +69,11 @@ TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticTrial) {
SyntheticTrialRegistry registry;
// Add two synthetic trials and confirm that they show up in the list.
- SyntheticTrialGroup trial1_group1(HashName("TestTrial1"), HashName("Group1"),
+ SyntheticTrialGroup trial1_group1("TestTrial1", "Group1",
SyntheticTrialAnnotationMode::kNextLog);
registry.RegisterSyntheticFieldTrial(trial1_group1);
- SyntheticTrialGroup trial2_group2(HashName("TestTrial2"), HashName("Group2"),
+ SyntheticTrialGroup trial2_group2("TestTrial2", "Group2",
SyntheticTrialAnnotationMode::kNextLog);
registry.RegisterSyntheticFieldTrial(trial2_group2);
// Ensure that time has advanced by at least a tick before proceeding.
@@ -95,7 +95,7 @@ TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticTrial) {
WaitUntilTimeChanges(begin_log_time);
// Change the group for the first trial after the log started.
- SyntheticTrialGroup trial1_group2(HashName("TestTrial1"), HashName("Group2"),
+ SyntheticTrialGroup trial1_group2("TestTrial1", "Group2",
SyntheticTrialAnnotationMode::kNextLog);
registry.RegisterSyntheticFieldTrial(trial1_group2);
registry.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
@@ -103,7 +103,7 @@ TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticTrial) {
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
// Add a new trial after the log started and confirm that it doesn't show up.
- SyntheticTrialGroup trial3_group3(HashName("TestTrial3"), HashName("Group3"),
+ SyntheticTrialGroup trial3_group3("TestTrial3", "Group3",
SyntheticTrialAnnotationMode::kNextLog);
registry.RegisterSyntheticFieldTrial(trial3_group3);
registry.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
@@ -114,11 +114,9 @@ TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticTrial) {
// active immediately, and confirm they both show up despite being added after
// the log started.
SyntheticTrialGroup trial3_group3_current_log(
- HashName("TestTrial3"), HashName("Group3"),
- SyntheticTrialAnnotationMode::kCurrentLog);
+ "TestTrial3", "Group3", SyntheticTrialAnnotationMode::kCurrentLog);
SyntheticTrialGroup trial4_group4_current_log(
- HashName("TestTrial4"), HashName("Group4"),
- SyntheticTrialAnnotationMode::kCurrentLog);
+ "TestTrial4", "Group4", SyntheticTrialAnnotationMode::kCurrentLog);
registry.RegisterSyntheticFieldTrial(trial3_group3_current_log);
registry.RegisterSyntheticFieldTrial(trial4_group4_current_log);
registry.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
@@ -136,6 +134,28 @@ TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticTrial) {
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial4", "Group4"));
}
+TEST_F(SyntheticTrialRegistryTest, GetSyntheticFieldTrialsOlderThanSuffix) {
+ SyntheticTrialRegistry registry;
+ SyntheticTrialGroup trial("TestTrial", "Group",
+ SyntheticTrialAnnotationMode::kCurrentLog);
+ registry.RegisterSyntheticFieldTrial(trial);
+
+ std::vector<ActiveGroupId> synthetic_trials;
+ // Get list of synthetic trials, but with no added suffixes to the trial and
+ // group names.
+ registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
+ &synthetic_trials);
+ ASSERT_EQ(1U, synthetic_trials.size());
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial", "Group"));
+
+ // Get list of synthetic trials, but with "UKM" suffixed to the trial and
+ // group names.
+ registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
+ &synthetic_trials, "UKM");
+ ASSERT_EQ(1U, synthetic_trials.size());
+ EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrialUKM", "GroupUKM"));
+}
+
TEST_F(SyntheticTrialRegistryTest, RegisterExternalExperiments_NoAllowlist) {
SyntheticTrialRegistry registry(false);
const std::string context = "TestTrial1";
@@ -222,11 +242,11 @@ TEST_F(SyntheticTrialRegistryTest, GetSyntheticFieldTrialActiveGroups) {
SyntheticTrialsActiveGroupIdProvider::GetInstance());
// Add two synthetic trials and confirm that they show up in the list.
- SyntheticTrialGroup trial1(HashName("TestTrial1"), HashName("Group1"),
+ SyntheticTrialGroup trial1("TestTrial1", "Group1",
SyntheticTrialAnnotationMode::kNextLog);
registry.RegisterSyntheticFieldTrial(trial1);
- SyntheticTrialGroup trial2(HashName("TestTrial2"), HashName("Group2"),
+ SyntheticTrialGroup trial2("TestTrial2", "Group2",
SyntheticTrialAnnotationMode::kNextLog);
registry.RegisterSyntheticFieldTrial(trial2);
@@ -238,12 +258,14 @@ TEST_F(SyntheticTrialRegistryTest, GetSyntheticFieldTrialActiveGroups) {
GetSyntheticTrialGroupIdsAsString(&output);
EXPECT_EQ(2U, output.size());
+ ActiveGroupId trial1_id = trial1.id();
std::string trial1_hash =
- base::StringPrintf("%x-%x", trial1.id.name, trial1.id.group);
+ base::StringPrintf("%x-%x", trial1_id.name, trial1_id.group);
EXPECT_TRUE(base::Contains(output, trial1_hash));
+ ActiveGroupId trial2_id = trial2.id();
std::string trial2_hash =
- base::StringPrintf("%x-%x", trial2.id.name, trial2.id.group);
+ base::StringPrintf("%x-%x", trial2_id.name, trial2.id().group);
EXPECT_TRUE(base::Contains(output, trial2_hash));
}
diff --git a/chromium/components/variations/synthetic_trials.cc b/chromium/components/variations/synthetic_trials.cc
index 1f93c582f91..9ffdf9cb96b 100644
--- a/chromium/components/variations/synthetic_trials.cc
+++ b/chromium/components/variations/synthetic_trials.cc
@@ -3,18 +3,29 @@
// found in the LICENSE file.
#include "components/variations/synthetic_trials.h"
+#include "components/variations/hashing.h"
namespace variations {
SyntheticTrialGroup::SyntheticTrialGroup(
- uint32_t trial,
- uint32_t group,
+ base::StringPiece trial_name,
+ base::StringPiece group_name,
SyntheticTrialAnnotationMode annotation_mode)
- : annotation_mode(annotation_mode) {
- id.name = trial;
- id.group = group;
+ : annotation_mode_(annotation_mode) {
+ SetTrialName(trial_name);
+ SetGroupName(group_name);
}
-SyntheticTrialGroup::~SyntheticTrialGroup() {}
+SyntheticTrialGroup::SyntheticTrialGroup(const SyntheticTrialGroup&) = default;
+
+void SyntheticTrialGroup::SetTrialName(base::StringPiece trial_name) {
+ active_group_.trial_name = std::string(trial_name);
+ id_.name = variations::HashName(trial_name);
+}
+
+void SyntheticTrialGroup::SetGroupName(base::StringPiece group_name) {
+ active_group_.group_name = std::string(group_name);
+ id_.group = variations::HashName(group_name);
+}
} // namespace variations
diff --git a/chromium/components/variations/synthetic_trials.h b/chromium/components/variations/synthetic_trials.h
index b555760c991..d376259f4b5 100644
--- a/chromium/components/variations/synthetic_trials.h
+++ b/chromium/components/variations/synthetic_trials.h
@@ -18,6 +18,7 @@ namespace variations {
// Specifies when UMA reports should start being annotated with a synthetic
// field trial.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.variations
enum class SyntheticTrialAnnotationMode {
// Start annotating UMA reports with this trial only after the next log opens.
// The UMA report that will be generated from the log that is open at the time
@@ -30,24 +31,52 @@ enum class SyntheticTrialAnnotationMode {
};
// A Field Trial and its selected group, which represent a particular
-// Chrome configuration state. For example, the trial name could map to
-// a preference name, and the group name could map to a preference value.
-struct COMPONENT_EXPORT(VARIATIONS) SyntheticTrialGroup {
+// Chrome configuration state. In other words, synthetic trials allow reporting
+// some client state as if it were a field trial. For example, the trial name
+// could map to a preference name, and the group name could map to a preference
+// value.
+class COMPONENT_EXPORT(VARIATIONS) SyntheticTrialGroup {
public:
- SyntheticTrialGroup(uint32_t trial,
- uint32_t group,
+ SyntheticTrialGroup(base::StringPiece trial_name,
+ base::StringPiece group_name,
SyntheticTrialAnnotationMode annotation_mode);
- ~SyntheticTrialGroup();
- ActiveGroupId id;
- base::TimeTicks start_time;
+ SyntheticTrialGroup(const SyntheticTrialGroup&);
+
+ ~SyntheticTrialGroup() = default;
+
+ base::FieldTrial::ActiveGroup active_group() const { return active_group_; }
+ base::StringPiece trial_name() const { return active_group_.trial_name; }
+ base::StringPiece group_name() const { return active_group_.group_name; }
+ ActiveGroupId id() const { return id_; }
+ base::TimeTicks start_time() const { return start_time_; }
+ SyntheticTrialAnnotationMode annotation_mode() const {
+ return annotation_mode_;
+ }
+ bool is_external() const { return is_external_; }
+
+ void SetTrialName(base::StringPiece trial_name);
+ void SetGroupName(base::StringPiece group_name);
+ void SetStartTime(base::TimeTicks start_time) { start_time_ = start_time; }
+ void SetAnnotationMode(SyntheticTrialAnnotationMode annotation_mode) {
+ annotation_mode_ = annotation_mode;
+ }
+ void SetIsExternal(bool is_external) { is_external_ = is_external; }
+
+ private:
+ base::FieldTrial::ActiveGroup active_group_;
+ ActiveGroupId id_;
+ base::TimeTicks start_time_;
// Determines when UMA reports should start being annotated with this trial
// group.
- SyntheticTrialAnnotationMode annotation_mode;
+ SyntheticTrialAnnotationMode annotation_mode_;
- // If this is an external experiment.
- bool is_external = false;
+ // Whether this is an external experiment. I.e., if this synthetic trial was
+ // registered through SyntheticTrialRegistry::RegisterExternalExperiments().
+ // An example of an external experiment would be the Chrome updater randomly
+ // assigning which binary to update to.
+ bool is_external_ = false;
};
// Interface class to observe changes to synthetic trials in MetricsService.
diff --git a/chromium/components/variations/synthetic_trials_active_group_id_provider.cc b/chromium/components/variations/synthetic_trials_active_group_id_provider.cc
index 3a515911a84..5d3e4736ec0 100644
--- a/chromium/components/variations/synthetic_trials_active_group_id_provider.cc
+++ b/chromium/components/variations/synthetic_trials_active_group_id_provider.cc
@@ -37,7 +37,7 @@ void SyntheticTrialsActiveGroupIdProvider::OnSyntheticTrialsChanged(
base::AutoLock scoped_lock(lock_);
synthetic_trials_.clear();
for (const auto& group : groups)
- synthetic_trials_.push_back(group.id);
+ synthetic_trials_.push_back(group.id());
}
// Update the experiments list for crash reports.
diff --git a/chromium/components/variations/variations_associated_data.cc b/chromium/components/variations/variations_associated_data.cc
index 25100ac5382..bb9566a7622 100644
--- a/chromium/components/variations/variations_associated_data.cc
+++ b/chromium/components/variations/variations_associated_data.cc
@@ -12,7 +12,6 @@
#include "base/metrics/field_trial_param_associator.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_split.h"
-#include "components/variations/variations_ids_provider.h"
namespace variations {
diff --git a/chromium/components/variations/variations_crash_keys.cc b/chromium/components/variations/variations_crash_keys.cc
index 268c597b3df..93e2bca6e7d 100644
--- a/chromium/components/variations/variations_crash_keys.cc
+++ b/chromium/components/variations/variations_crash_keys.cc
@@ -206,7 +206,7 @@ void VariationsCrashKeys::OnSyntheticTrialsChanged(
// not be too many synthetic trials, this is not too big of an issue.
synthetic_trials_string_.clear();
for (const auto& synthetic_trial : synthetic_trials) {
- synthetic_trials_string_ += ActiveGroupToString(synthetic_trial.id);
+ synthetic_trials_string_ += ActiveGroupToString(synthetic_trial.id());
}
num_synthetic_trials_ = synthetic_trials.size();
diff --git a/chromium/components/variations/variations_crash_keys.h b/chromium/components/variations/variations_crash_keys.h
index 908c1d31c9e..3fe762ef699 100644
--- a/chromium/components/variations/variations_crash_keys.h
+++ b/chromium/components/variations/variations_crash_keys.h
@@ -12,7 +12,7 @@
namespace variations {
-struct SyntheticTrialGroup;
+class SyntheticTrialGroup;
// The key used in crash reports to indicate the number of active experiments.
// Should match the number of entries in kExperimentListKey.
diff --git a/chromium/components/variations/variations_crash_keys_unittest.cc b/chromium/components/variations/variations_crash_keys_unittest.cc
index 598920f7dfb..5a612344a33 100644
--- a/chromium/components/variations/variations_crash_keys_unittest.cc
+++ b/chromium/components/variations/variations_crash_keys_unittest.cc
@@ -80,8 +80,7 @@ TEST_F(VariationsCrashKeysTest, BasicFunctionality) {
// Add two synthetic trials and confirm that they show up in the list.
SyntheticTrialGroup synth_trial(
- HashName("Trial3"), HashName("Group3"),
- variations::SyntheticTrialAnnotationMode::kNextLog);
+ "Trial3", "Group3", variations::SyntheticTrialAnnotationMode::kNextLog);
registry.RegisterSyntheticFieldTrial(synth_trial);
EXPECT_EQ("3", GetNumExperimentsCrashKey());
@@ -109,12 +108,10 @@ TEST_F(VariationsCrashKeysTest, BasicFunctionality) {
// Replace synthetic trial group and add one more.
SyntheticTrialGroup synth_trial2(
- HashName("Trial3"), HashName("Group3_A"),
- variations::SyntheticTrialAnnotationMode::kNextLog);
+ "Trial3", "Group3_A", variations::SyntheticTrialAnnotationMode::kNextLog);
registry.RegisterSyntheticFieldTrial(synth_trial2);
SyntheticTrialGroup synth_trial3(
- HashName("Trial4"), HashName("Group4"),
- variations::SyntheticTrialAnnotationMode::kNextLog);
+ "Trial4", "Group4", variations::SyntheticTrialAnnotationMode::kNextLog);
registry.RegisterSyntheticFieldTrial(synth_trial3);
EXPECT_EQ("5", GetNumExperimentsCrashKey());
diff --git a/chromium/components/variations/variations_ids_provider.cc b/chromium/components/variations/variations_ids_provider.cc
index c78603701eb..3cf51c5d78a 100644
--- a/chromium/components/variations/variations_ids_provider.cc
+++ b/chromium/components/variations/variations_ids_provider.cc
@@ -157,13 +157,13 @@ void VariationsIdsProvider::SetLowEntropySourceValue(
VariationsIdsProvider::ForceIdsResult VariationsIdsProvider::ForceVariationIds(
const std::vector<std::string>& variation_ids,
const std::string& command_line_variation_ids) {
- default_variation_ids_set_.clear();
+ force_enabled_ids_set_.clear();
- if (!AddVariationIdsToSet(variation_ids, &default_variation_ids_set_))
+ if (!AddVariationIdsToSet(variation_ids, &force_enabled_ids_set_))
return ForceIdsResult::INVALID_VECTOR_ENTRY;
if (!ParseVariationIdsParameter(command_line_variation_ids,
- &default_variation_ids_set_)) {
+ &force_enabled_ids_set_)) {
return ForceIdsResult::INVALID_SWITCH_ENTRY;
}
if (variation_ids_cache_initialized_) {
@@ -207,7 +207,7 @@ void VariationsIdsProvider::ResetForTesting() {
base::FieldTrialList::RemoveObserver(this);
variation_ids_cache_initialized_ = false;
variation_ids_set_.clear();
- default_variation_ids_set_.clear();
+ force_enabled_ids_set_.clear();
synthetic_variation_ids_set_.clear();
force_disabled_ids_set_.clear();
variations_headers_map_.clear();
@@ -251,13 +251,13 @@ void VariationsIdsProvider::OnSyntheticTrialsChanged(
synthetic_variation_ids_set_.clear();
for (const SyntheticTrialGroup& group : groups) {
VariationID id = GetGoogleVariationIDFromHashes(
- GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, group.id);
+ GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, group.id());
if (id != EMPTY_ID) {
synthetic_variation_ids_set_.insert(
VariationIDEntry(id, GOOGLE_WEB_PROPERTIES_ANY_CONTEXT));
}
id = GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES_SIGNED_IN,
- group.id);
+ group.id());
if (id != EMPTY_ID) {
synthetic_variation_ids_set_.insert(
VariationIDEntry(id, GOOGLE_WEB_PROPERTIES_SIGNED_IN));
@@ -471,7 +471,7 @@ std::set<VariationsIdsProvider::VariationIDEntry>
VariationsIdsProvider::GetAllVariationIds() {
lock_.AssertAcquired();
- std::set<VariationIDEntry> all_variation_ids_set = default_variation_ids_set_;
+ std::set<VariationIDEntry> all_variation_ids_set = force_enabled_ids_set_;
for (const VariationIDEntry& entry : variation_ids_set_) {
all_variation_ids_set.insert(entry);
}
diff --git a/chromium/components/variations/variations_ids_provider.h b/chromium/components/variations/variations_ids_provider.h
index 29e32964312..d812c4324e3 100644
--- a/chromium/components/variations/variations_ids_provider.h
+++ b/chromium/components/variations/variations_ids_provider.h
@@ -240,8 +240,8 @@ class COMPONENT_EXPORT(VARIATIONS) VariationsIdsProvider
bool is_signed_in,
Study_GoogleWebVisibility web_visibility);
- // Returns the currently active set of variation ids, which includes any
- // default values, synthetic variations and actual field trial variations.
+ // Returns the currently active set of variation ids, which includes ids from
+ // field trials, synthetic trials, and forced ids.
std::set<VariationIDEntry> GetAllVariationIds();
// Returns the collection of variation ids matching any of the given
@@ -265,8 +265,9 @@ class COMPONENT_EXPORT(VARIATIONS) VariationsIdsProvider
// This consists of a list of valid IDs, and the actual transmitted header.
std::set<VariationIDEntry> variation_ids_set_;
- // Provides the google experiment ids forced from command line.
- std::set<VariationIDEntry> default_variation_ids_set_;
+ // Provides the google experiment ids that are force-enabled through
+ // ForceVariationIds().
+ std::set<VariationIDEntry> force_enabled_ids_set_;
// Variations ids from synthetic field trials.
std::set<VariationIDEntry> synthetic_variation_ids_set_;
diff --git a/chromium/components/vector_icons/BUILD.gn b/chromium/components/vector_icons/BUILD.gn
index 955128a1ab8..9b8e9220d1b 100644
--- a/chromium/components/vector_icons/BUILD.gn
+++ b/chromium/components/vector_icons/BUILD.gn
@@ -9,6 +9,7 @@ aggregate_vector_icons("components_vector_icons") {
icon_directory = "."
sources = [
+ "account_circle.icon",
"ads.icon",
"ads_click.icon",
"arrow_back.icon",
diff --git a/chromium/components/vector_icons/account_circle.icon b/chromium/components/vector_icons/account_circle.icon
new file mode 100644
index 00000000000..bda26849071
--- /dev/null
+++ b/chromium/components/vector_icons/account_circle.icon
@@ -0,0 +1,71 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 5.85f, 17.1f,
+R_QUADRATIC_TO, 1.27f, -0.97f, 2.85f, -1.54f,
+QUADRATIC_TO, 10.28f, 15, 12, 15,
+R_QUADRATIC_TO, 1.73f, 0, 3.3f, 0.56f,
+R_QUADRATIC_TO, 1.58f, 0.56f, 2.85f, 1.54f,
+R_QUADRATIC_TO, 0.88f, -1.02f, 1.36f, -2.33f,
+QUADRATIC_TO, 20, 13.48f, 20, 12,
+R_QUADRATIC_TO, 0, -3.32f, -2.34f, -5.66f,
+QUADRATIC_TO, 15.33f, 4, 12, 4,
+QUADRATIC_TO_SHORTHAND, 6.34f, 6.34f,
+QUADRATIC_TO, 4, 8.68f, 4, 12,
+R_QUADRATIC_TO, 0, 1.48f, 0.49f, 2.78f,
+R_QUADRATIC_TO, 0.49f, 1.3f, 1.36f, 2.33f,
+CLOSE,
+MOVE_TO, 12, 13,
+R_QUADRATIC_TO, -1.47f, 0, -2.49f, -1.01f,
+QUADRATIC_TO, 8.5f, 10.98f, 8.5f, 9.5f,
+R_QUADRATIC_TO, 0, -1.48f, 1.01f, -2.49f,
+QUADRATIC_TO, 10.53f, 6, 12, 6,
+R_QUADRATIC_TO, 1.47f, 0, 2.49f, 1.01f,
+QUADRATIC_TO, 15.5f, 8.03f, 15.5f, 9.5f,
+R_QUADRATIC_TO, 0, 1.47f, -1.01f, 2.49f,
+QUADRATIC_TO, 13.48f, 13, 12, 13,
+CLOSE,
+R_MOVE_TO, 0, 9,
+R_QUADRATIC_TO, -2.08f, 0, -3.9f, -0.79f,
+R_QUADRATIC_TO, -1.82f, -0.79f, -3.17f, -2.14f,
+R_QUADRATIC_TO, -1.35f, -1.35f, -2.14f, -3.17f,
+QUADRATIC_TO, 2, 14.08f, 2, 12,
+R_QUADRATIC_TO, 0, -2.08f, 0.79f, -3.9f,
+R_QUADRATIC_TO, 0.79f, -1.82f, 2.14f, -3.17f,
+R_QUADRATIC_TO, 1.35f, -1.35f, 3.18f, -2.14f,
+QUADRATIC_TO, 9.93f, 2, 12, 2,
+R_QUADRATIC_TO, 2.07f, 0, 3.9f, 0.79f,
+R_QUADRATIC_TO, 1.83f, 0.79f, 3.18f, 2.14f,
+R_QUADRATIC_TO, 1.35f, 1.35f, 2.14f, 3.18f,
+QUADRATIC_TO, 22, 9.93f, 22, 12,
+R_QUADRATIC_TO, 0, 2.07f, -0.79f, 3.9f,
+R_QUADRATIC_TO, -0.79f, 1.83f, -2.14f, 3.18f,
+R_QUADRATIC_TO, -1.35f, 1.35f, -3.17f, 2.14f,
+QUADRATIC_TO, 14.08f, 22, 12, 22,
+CLOSE,
+R_MOVE_TO, 0, -2,
+R_QUADRATIC_TO, 1.33f, 0, 2.5f, -0.39f,
+R_QUADRATIC_TO, 1.18f, -0.39f, 2.15f, -1.11f,
+R_QUADRATIC_TO, -0.97f, -0.72f, -2.15f, -1.11f,
+QUADRATIC_TO, 13.33f, 17, 12, 17,
+R_QUADRATIC_TO, -1.33f, 0, -2.5f, 0.39f,
+R_QUADRATIC_TO, -1.17f, 0.39f, -2.15f, 1.11f,
+R_QUADRATIC_TO, 0.98f, 0.73f, 2.15f, 1.11f,
+QUADRATIC_TO, 10.68f, 20, 12, 20,
+CLOSE,
+R_MOVE_TO, 0, -9,
+R_QUADRATIC_TO, 0.65f, 0, 1.08f, -0.42f,
+R_QUADRATIC_TO, 0.43f, -0.42f, 0.43f, -1.07f,
+R_QUADRATIC_TO, 0, -0.65f, -0.42f, -1.07f,
+QUADRATIC_TO, 12.65f, 8, 12, 8,
+R_QUADRATIC_TO, -0.65f, 0, -1.07f, 0.43f,
+QUADRATIC_TO, 10.5f, 8.85f, 10.5f, 9.5f,
+R_QUADRATIC_TO, 0, 0.65f, 0.43f, 1.08f,
+QUADRATIC_TO, 11.35f, 11, 12, 11,
+CLOSE,
+R_MOVE_TO, 0, -1.5f,
+CLOSE,
+R_MOVE_TO, 0, 9,
+CLOSE
diff --git a/chromium/components/version_info/OWNERS b/chromium/components/version_info/OWNERS
index d33a4b7db41..0413c67927a 100644
--- a/chromium/components/version_info/OWNERS
+++ b/chromium/components/version_info/OWNERS
@@ -1,4 +1,3 @@
-jochen@chromium.org
sky@chromium.org
thakis@chromium.org
thestig@chromium.org
diff --git a/chromium/components/version_info/android/BUILD.gn b/chromium/components/version_info/android/BUILD.gn
index b83d435fb8e..9072c520a63 100644
--- a/chromium/components/version_info/android/BUILD.gn
+++ b/chromium/components/version_info/android/BUILD.gn
@@ -18,7 +18,8 @@ android_library("version_constants_java") {
]
deps = [
":generate_version_constants",
- "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
srcjar_deps = [ ":channel_enum_srcjar" ]
diff --git a/chromium/components/version_info/version_info.cc b/chromium/components/version_info/version_info.cc
index 637d1533473..96da8a9cc3c 100644
--- a/chromium/components/version_info/version_info.cc
+++ b/chromium/components/version_info/version_info.cc
@@ -69,7 +69,7 @@ std::string GetOSType() {
return "iOS";
#elif BUILDFLAG(IS_MAC)
return "Mac OS X";
-#elif BUILDFLAG(IS_CHROMEOS_ASH)
+#elif BUILDFLAG(IS_CHROMEOS)
# if BUILDFLAG(GOOGLE_CHROME_BRANDING)
return "ChromeOS";
# else
@@ -77,7 +77,7 @@ std::string GetOSType() {
# endif
#elif BUILDFLAG(IS_ANDROID)
return "Android";
-#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_LINUX)
return "Linux";
#elif BUILDFLAG(IS_FREEBSD)
return "FreeBSD";
diff --git a/chromium/components/version_ui/PRESUBMIT.py b/chromium/components/version_ui/PRESUBMIT.py
deleted file mode 100644
index 864735f0efc..00000000000
--- a/chromium/components/version_ui/PRESUBMIT.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-USE_PYTHON3 = True
-
-
-def _CommonChecks(input_api, output_api):
- results = []
- try:
- import sys
- old_sys_path = sys.path[:]
- cwd = input_api.PresubmitLocalPath()
- sys.path += [input_api.os_path.join(cwd, '..', '..', 'tools')]
- import web_dev_style.presubmit_support
- results += web_dev_style.presubmit_support.CheckStyle(input_api, output_api)
- finally:
- sys.path = old_sys_path
- return results
-
-
-def CheckChangeOnUpload(input_api, output_api):
- return _CommonChecks(input_api, output_api)
-
-
-def CheckChangeOnCommit(input_api, output_api):
- return _CommonChecks(input_api, output_api)
diff --git a/chromium/components/version_ui_strings.grdp b/chromium/components/version_ui_strings.grdp
index 04bfc4aec6f..b06e97ba7c9 100644
--- a/chromium/components/version_ui_strings.grdp
+++ b/chromium/components/version_ui_strings.grdp
@@ -64,7 +64,7 @@
No such file or directory
</message>
<message name="IDS_VERSION_UI_VARIATIONS" desc="label for the variations list on the about:version page">
- Variations
+ Active Variations
</message>
<message name="IDS_VERSION_UI_VARIATIONS_CMD" desc="label for the variations information in command-line format on the about:version page">
Command-line variations
diff --git a/chromium/components/version_ui_strings_grdp/IDS_VERSION_UI_VARIATIONS.png.sha1 b/chromium/components/version_ui_strings_grdp/IDS_VERSION_UI_VARIATIONS.png.sha1
new file mode 100644
index 00000000000..09d89df36a4
--- /dev/null
+++ b/chromium/components/version_ui_strings_grdp/IDS_VERSION_UI_VARIATIONS.png.sha1
@@ -0,0 +1 @@
+87b7efd6584bec2ed7af4b05b35c1b75ea7de245 \ No newline at end of file
diff --git a/chromium/components/visitedlink/browser/visitedlink_writer.cc b/chromium/components/visitedlink/browser/visitedlink_writer.cc
index af287fa7839..bcbf60c6001 100644
--- a/chromium/components/visitedlink/browser/visitedlink_writer.cc
+++ b/chromium/components/visitedlink/browser/visitedlink_writer.cc
@@ -259,8 +259,7 @@ VisitedLinkWriter::~VisitedLinkWriter() {
// state. On the next start table will be rebuilt.
base::FilePath filename;
GetDatabaseFileName(&filename);
- PostIOTask(FROM_HERE,
- base::BindOnce(base::GetDeleteFileCallback(), filename));
+ PostIOTask(FROM_HERE, base::GetDeleteFileCallback(filename));
}
}
diff --git a/chromium/components/visitedlink/common/visitedlink_common.cc b/chromium/components/visitedlink/common/visitedlink_common.cc
index 5cd8eeda6af..cd26634e9d8 100644
--- a/chromium/components/visitedlink/common/visitedlink_common.cc
+++ b/chromium/components/visitedlink/common/visitedlink_common.cc
@@ -98,7 +98,7 @@ VisitedLinkCommon::Fingerprint VisitedLinkCommon::ComputeURLFingerprint(
// on arbitrary alignment on some processors. This reinterpret_casts it
// down to a char array of the same size as fingerprint, and then does the
// bit cast, which amounts to a memcpy. This does not handle endian issues.
- return bit_cast<Fingerprint, uint8_t[8]>(
+ return base::bit_cast<Fingerprint, uint8_t[8]>(
*reinterpret_cast<uint8_t(*)[8]>(&digest.a));
}
diff --git a/chromium/components/viz/BUILD.gn b/chromium/components/viz/BUILD.gn
index db7f49872ec..a1d5c682e06 100644
--- a/chromium/components/viz/BUILD.gn
+++ b/chromium/components/viz/BUILD.gn
@@ -41,13 +41,14 @@ viz_test("viz_unittests") {
}
if (is_fuchsia) {
- use_cfv2 = false
- additional_manifest_fragments = [
- # TODO(crbug.com/1185811): Figure out why jit_capabilities is needed.
- "//build/config/fuchsia/test/jit_capabilities.test-cmx",
+ use_cfv1 = false
- "//build/config/fuchsia/test/vulkan_capabilities.test-cmx",
- ]
+ # TODO(https://crbug.com/1185811): Investigate removing the requirement for
+ # job_policy_ambient_mark_vmo_exec for the sake of V8's allocator in tests.
+ test_runner_shard = "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml"
+
+ additional_manifest_fragments =
+ [ "//third_party/fuchsia-sdk/sdk/pkg/vulkan/client.shard.cml" ]
}
}
diff --git a/chromium/components/viz/OWNERS b/chromium/components/viz/OWNERS
index ee2085c35b7..d5881ea1a6b 100644
--- a/chromium/components/viz/OWNERS
+++ b/chromium/components/viz/OWNERS
@@ -7,10 +7,8 @@
# display / resources / quads / passes
vmpstr@chromium.org
-weiliangc@chromium.org
# renderers (GL/Skia/Software)
-weiliangc@chromium.org
rjkroege@chromium.org
ccameron@chromium.org
penghuang@chromium.org
diff --git a/chromium/components/viz/README.md b/chromium/components/viz/README.md
index d6a63a63702..1170d457693 100644
--- a/chromium/components/viz/README.md
+++ b/chromium/components/viz/README.md
@@ -131,13 +131,6 @@ Code here supports presentation of the backing store drawn by the display
compositor (typically thought of as SwapBuffers), as well as the use of
overlays.
-The source code is split into two build targets,
-``components/viz/service:service`` and
-``components/viz/service:gpu_service_dependencies``. The latter is
-code that requires being run in the gpu process, thus could not work
-if the viz service is located elsewhere. This is forward-looking code
-as the viz service is moving into the gpu process always.
-
| Can depend on: |
|:--------------------------------------|
| viz/common/* |
@@ -187,12 +180,6 @@ method.
deallocating) gpu memory buffers, setting up a channel for the command buffer,
etc.
-Similar to ``service/display_embedder`` this is split into two targets in
-the build system. Code that must run in the gpu process is compiled in
-the ``components/viz/service:gpu_service_dependencies`` build target,
-and the rest is compiled in ``components/viz/service:service``. As all
-code moves to the gpu process, these two build targets will merge.
-
| Can depend on: |
|:----------------------|
| viz/common/* |
diff --git a/chromium/components/viz/common/BUILD.gn b/chromium/components/viz/common/BUILD.gn
index 7abb58d2112..9dbc3d66be3 100644
--- a/chromium/components/viz/common/BUILD.gn
+++ b/chromium/components/viz/common/BUILD.gn
@@ -180,12 +180,6 @@ viz_component("common") {
"frame_sinks/delay_based_time_source.h",
"frame_timing_details.h",
"frame_timing_details_map.h",
- "gl_i420_converter.cc",
- "gl_i420_converter.h",
- "gl_nv12_converter.cc",
- "gl_nv12_converter.h",
- "gl_scaler.cc",
- "gl_scaler.h",
"gpu/context_cache_controller.cc",
"gpu/context_cache_controller.h",
"gpu/context_lost_observer.h",
@@ -340,6 +334,10 @@ viz_component("common") {
sources += [
"display/use_layered_window.cc",
"display/use_layered_window.h",
+ "overlay_state/win/overlay_state_aggregator.cc",
+ "overlay_state/win/overlay_state_aggregator.h",
+ "overlay_state/win/overlay_state_service.cc",
+ "overlay_state/win/overlay_state_service.h",
]
deps += [ "//ui/base" ]
@@ -395,18 +393,6 @@ viz_source_set("unit_tests") {
"yuv_readback_unittest.cc",
]
- if (enable_gl_renderer_tests) {
- sources += [
- "gl_i420_converter_pixeltest.cc",
- "gl_i420_converter_unittest.cc",
- "gl_nv12_converter_pixeltest.cc",
- "gl_scaler_overscan_pixeltest.cc",
- "gl_scaler_pixeltest.cc",
- "gl_scaler_shader_pixeltest.cc",
- "gl_scaler_unittest.cc",
- ]
- }
-
if (enable_vulkan) {
sources += [ "gpu/vulkan_in_process_context_provider_unittest.cc" ]
}
@@ -428,6 +414,14 @@ viz_source_set("unit_tests") {
"//ui/gfx/geometry",
"//ui/latency",
]
+
+ if (is_win) {
+ sources += [ "overlay_state/win/overlay_state_service_unittest.cc" ]
+ deps += [
+ "//components/viz/test:test_suite",
+ "//media/mojo/mojom",
+ ]
+ }
}
viz_source_set("perf_tests") {
@@ -446,7 +440,11 @@ viz_source_set("perf_tests") {
if (is_android) {
android_library("common_java") {
- deps = [ "//base:base_java" ]
+ deps = [
+ "//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
+ ]
sources = [
"java/src/org/chromium/components/viz/common/VizSwitches.java",
"java/src/org/chromium/components/viz/common/display/DeJellyUtils.java",
diff --git a/chromium/components/viz/common/DEPS b/chromium/components/viz/common/DEPS
index bff0504c2df..398e9db0ce5 100644
--- a/chromium/components/viz/common/DEPS
+++ b/chromium/components/viz/common/DEPS
@@ -42,5 +42,5 @@ specific_include_rules = {
"bitmap_allocation.cc" : [
# Only used to pass Mojo handles, not to communicate with the viz service.
"+mojo/public/cpp/base/shared_memory_utils.h",
- ],
+ ]
}
diff --git a/chromium/components/viz/common/display/renderer_settings.h b/chromium/components/viz/common/display/renderer_settings.h
index 8b0b5eda59c..bc48a50a766 100644
--- a/chromium/components/viz/common/display/renderer_settings.h
+++ b/chromium/components/viz/common/display/renderer_settings.h
@@ -30,7 +30,6 @@ class VIZ_COMMON_EXPORT RendererSettings {
bool partial_swap_enabled = false;
bool should_clear_root_render_pass = true;
bool release_overlay_resources_after_gpu_query = false;
- bool use_skia_renderer = true;
bool dont_round_texture_sizes_for_pixel_tests = false;
int highp_threshold_min = 0;
bool auto_resize_output_surface = true;
diff --git a/chromium/components/viz/common/features.cc b/chromium/components/viz/common/features.cc
index fa5f838ac37..808900f9bc9 100644
--- a/chromium/components/viz/common/features.cc
+++ b/chromium/components/viz/common/features.cc
@@ -37,12 +37,13 @@ namespace features {
const base::Feature kAdpf{"Adpf", base::FEATURE_DISABLED_BY_DEFAULT};
// Target duration used for power hint on Android.
+// `0` indicates use hard coded default.
const base::FeatureParam<int> kAdpfTargetDurationMs{&kAdpf,
- "AdpfTargetDurationMs", 12};
+ "AdpfTargetDurationMs", 0};
const base::Feature kEnableOverlayPrioritization {
"EnableOverlayPrioritization",
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
@@ -68,10 +69,6 @@ const base::Feature kVideoDetectorIgnoreNonVideos{
const base::Feature kSimpleFrameRateThrottling{
"SimpleFrameRateThrottling", base::FEATURE_DISABLED_BY_DEFAULT};
-// Use the SkiaRenderer.
-const base::Feature kUseSkiaRenderer{"UseSkiaRenderer",
- base::FEATURE_ENABLED_BY_DEFAULT};
-
// Kill-switch to disable de-jelly, even if flags/properties indicate it should
// be enabled.
const base::Feature kDisableDeJelly{"DisableDeJelly",
@@ -84,10 +81,6 @@ const base::Feature kDynamicColorGamut{"DynamicColorGamut",
base::FEATURE_DISABLED_BY_DEFAULT};
#endif
-// Uses glClear to composite solid color quads whenever possible.
-const base::Feature kFastSolidColorDraw{"FastSolidColorDraw",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
// Submit CompositorFrame from SynchronousLayerTreeFrameSink directly to viz in
// WebView.
const base::Feature kVizFrameSubmissionForWebView{
@@ -190,15 +183,13 @@ bool IsAdpfEnabled() {
return base::FeatureList::IsEnabled(kAdpf);
}
-bool IsClipPrewalkDamageEnabled() {
- static constexpr base::Feature kClipPrewalkDamage{
- "ClipPrewalkDamage", base::FEATURE_ENABLED_BY_DEFAULT};
-
- return base::FeatureList::IsEnabled(kClipPrewalkDamage);
-}
-
bool IsOverlayPrioritizationEnabled() {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ // DelegatedCompositing in Lacros makes this feature a no-op.
+ return false;
+#else
return base::FeatureList::IsEnabled(kEnableOverlayPrioritization);
+#endif
}
bool IsDelegatedCompositingEnabled() {
@@ -218,11 +209,6 @@ bool IsSimpleFrameRateThrottlingEnabled() {
return base::FeatureList::IsEnabled(kSimpleFrameRateThrottling);
}
-bool IsUsingSkiaRenderer() {
- return base::FeatureList::IsEnabled(kUseSkiaRenderer) ||
- features::IsUsingVulkan();
-}
-
#if BUILDFLAG(IS_ANDROID)
bool IsDynamicColorGamutEnabled() {
if (viz::AlwaysUseWideColorGamut())
@@ -234,10 +220,6 @@ bool IsDynamicColorGamutEnabled() {
}
#endif
-bool IsUsingFastPathForSolidColorQuad() {
- return base::FeatureList::IsEnabled(kFastSolidColorDraw);
-}
-
bool IsUsingVizFrameSubmissionForWebView() {
return base::FeatureList::IsEnabled(kVizFrameSubmissionForWebView);
}
@@ -290,8 +272,8 @@ bool ShouldUsePlatformDelegatedInk() {
return base::FeatureList::IsEnabled(kUsePlatformDelegatedInk);
}
-#if BUILDFLAG(IS_ANDROID)
bool UseSurfaceLayerForVideo() {
+#if BUILDFLAG(IS_ANDROID)
// SurfaceLayer video should work fine with new heuristic.
if (base::FeatureList::IsEnabled(kWebViewNewInvalidateHeuristic))
return true;
@@ -301,8 +283,12 @@ bool UseSurfaceLayerForVideo() {
return true;
}
return base::FeatureList::IsEnabled(kUseSurfaceLayerForVideoDefault);
+#else
+ return true;
+#endif
}
+#if BUILDFLAG(IS_ANDROID)
bool UseRealVideoColorSpaceForDisplay() {
// We need Android S for proper color space support in SurfaceControl.
if (base::android::BuildInfo::GetInstance()->sdk_int() <
diff --git a/chromium/components/viz/common/features.h b/chromium/components/viz/common/features.h
index 139f9430c10..aab8f02e427 100644
--- a/chromium/components/viz/common/features.h
+++ b/chromium/components/viz/common/features.h
@@ -22,7 +22,6 @@ namespace features {
VIZ_COMMON_EXPORT extern const base::Feature kAdpf;
VIZ_COMMON_EXPORT extern const base::FeatureParam<int> kAdpfTargetDurationMs;
VIZ_COMMON_EXPORT extern const base::Feature kEnableOverlayPrioritization;
-VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaRenderer;
VIZ_COMMON_EXPORT extern const base::Feature kDelegatedCompositing;
VIZ_COMMON_EXPORT extern const base::Feature kRecordSkPicture;
VIZ_COMMON_EXPORT extern const base::Feature kDisableDeJelly;
@@ -32,7 +31,6 @@ VIZ_COMMON_EXPORT extern const base::Feature kVideoDetectorIgnoreNonVideos;
#if BUILDFLAG(IS_ANDROID)
VIZ_COMMON_EXPORT extern const base::Feature kDynamicColorGamut;
#endif
-VIZ_COMMON_EXPORT extern const base::Feature kFastSolidColorDraw;
VIZ_COMMON_EXPORT extern const base::Feature kVizFrameSubmissionForWebView;
VIZ_COMMON_EXPORT extern const base::Feature kUsePreferredIntervalForVideo;
VIZ_COMMON_EXPORT extern const base::Feature kUseRealBuffersForPageFlipTest;
@@ -69,7 +67,6 @@ VIZ_COMMON_EXPORT extern const char kPredictorLinear2[];
VIZ_COMMON_EXPORT extern const char kPredictorLsq[];
VIZ_COMMON_EXPORT bool IsAdpfEnabled();
-VIZ_COMMON_EXPORT bool IsClipPrewalkDamageEnabled();
VIZ_COMMON_EXPORT bool IsSimpleFrameRateThrottlingEnabled();
#if BUILDFLAG(IS_ANDROID)
VIZ_COMMON_EXPORT bool IsDynamicColorGamutEnabled();
@@ -77,8 +74,6 @@ VIZ_COMMON_EXPORT bool IsDynamicColorGamutEnabled();
VIZ_COMMON_EXPORT bool IsOverlayPrioritizationEnabled();
VIZ_COMMON_EXPORT bool IsDelegatedCompositingEnabled();
VIZ_COMMON_EXPORT bool IsSyncWindowDestructionEnabled();
-VIZ_COMMON_EXPORT bool IsUsingFastPathForSolidColorQuad();
-VIZ_COMMON_EXPORT bool IsUsingSkiaRenderer();
VIZ_COMMON_EXPORT bool IsUsingVizFrameSubmissionForWebView();
VIZ_COMMON_EXPORT bool IsUsingPreferredIntervalForVideo();
VIZ_COMMON_EXPORT bool ShouldUseRealBuffersForPageFlipTest();
@@ -89,8 +84,8 @@ VIZ_COMMON_EXPORT bool ShouldUseSetPresentDuration();
VIZ_COMMON_EXPORT absl::optional<int> ShouldDrawPredictedInkPoints();
VIZ_COMMON_EXPORT std::string InkPredictor();
VIZ_COMMON_EXPORT bool ShouldUsePlatformDelegatedInk();
-#if BUILDFLAG(IS_ANDROID)
VIZ_COMMON_EXPORT bool UseSurfaceLayerForVideo();
+#if BUILDFLAG(IS_ANDROID)
VIZ_COMMON_EXPORT bool UseRealVideoColorSpaceForDisplay();
#endif
VIZ_COMMON_EXPORT bool IsSurfaceSyncThrottling();
diff --git a/chromium/components/viz/common/frame_sinks/README.md b/chromium/components/viz/common/frame_sinks/README.md
index fc04942b7cf..f134b4e142e 100644
--- a/chromium/components/viz/common/frame_sinks/README.md
+++ b/chromium/components/viz/common/frame_sinks/README.md
@@ -56,8 +56,8 @@ properties:
Note that all coordinates are constrained to be integer values, to avoid
introducing alignment, rounding or other "fuzz" issues.
- * Result format: An RGBA-interleaved bitmap (SkBitmap) or I420 Y+U+V image
- planes.
+ * Result format: An RGBA-interleaved bitmap (SkBitmap), I420 Y+U+V image
+ planes, or NV12 Y+UV image planes.
For efficient video capture, the above are used as follows: An issuer of
CopyOutputRequests "locks into" a target area within the Surface (usually the
diff --git a/chromium/components/viz/common/frame_sinks/blit_request.cc b/chromium/components/viz/common/frame_sinks/blit_request.cc
index 29bbb2d2fb5..b55f2a216fd 100644
--- a/chromium/components/viz/common/frame_sinks/blit_request.cc
+++ b/chromium/components/viz/common/frame_sinks/blit_request.cc
@@ -31,10 +31,14 @@ std::string BlendBitmap::ToString() const {
BlitRequest::BlitRequest(
const gfx::Point& destination_region_offset,
+ LetterboxingBehavior letterboxing_behavior,
const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>&
- mailboxes)
+ mailboxes,
+ bool populates_gpu_memory_buffer)
: destination_region_offset_(destination_region_offset),
- mailboxes_(mailboxes) {}
+ letterboxing_behavior_(letterboxing_behavior),
+ mailboxes_(mailboxes),
+ populates_gpu_memory_buffer_(populates_gpu_memory_buffer) {}
BlitRequest::BlitRequest(BlitRequest&& other) = default;
BlitRequest& BlitRequest::operator=(BlitRequest&& other) = default;
@@ -42,9 +46,10 @@ BlitRequest& BlitRequest::operator=(BlitRequest&& other) = default;
BlitRequest::~BlitRequest() = default;
std::string BlitRequest::ToString() const {
- return base::StringPrintf("blit to %s, blend %u bitmaps",
+ return base::StringPrintf("blit to %s, blend %u bitmaps, populates GMB? %d",
destination_region_offset_.ToString().c_str(),
- static_cast<uint32_t>(blend_bitmaps_.size()));
+ static_cast<uint32_t>(blend_bitmaps_.size()),
+ populates_gpu_memory_buffer_);
}
} // namespace viz
diff --git a/chromium/components/viz/common/frame_sinks/blit_request.h b/chromium/components/viz/common/frame_sinks/blit_request.h
index b236231c611..ee4656b2332 100644
--- a/chromium/components/viz/common/frame_sinks/blit_request.h
+++ b/chromium/components/viz/common/frame_sinks/blit_request.h
@@ -53,6 +53,16 @@ class VIZ_COMMON_EXPORT BlendBitmap {
sk_sp<SkImage> image_;
};
+// Enum used to specify letteboxing behavior for a BlitRequest.
+enum class LetterboxingBehavior {
+ // No letterboxing is needed - only the destination region will be written
+ // into by the handler of CopyOutputRequest.
+ kDoNotLetterbox,
+ // Letterboxing is needed - everything outside of the destination region
+ // will be filled with black by the handler of CopyOutputRequest.
+ kLetterbox
+};
+
// Structure describing a blit operation that can be appended to
// `CopyOutputRequest` if the callers want to place the results of the operation
// in textures that they own.
@@ -60,8 +70,10 @@ class VIZ_COMMON_EXPORT BlitRequest {
public:
explicit BlitRequest(
const gfx::Point& destination_region_offset,
+ LetterboxingBehavior letterboxing_behavior,
const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>&
- mailboxes);
+ mailboxes,
+ bool populates_gpu_memory_buffer);
BlitRequest(BlitRequest&& other);
BlitRequest& operator=(BlitRequest&& other);
@@ -74,6 +86,10 @@ class VIZ_COMMON_EXPORT BlitRequest {
return destination_region_offset_;
}
+ LetterboxingBehavior letterboxing_behavior() const {
+ return letterboxing_behavior_;
+ }
+
const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>&
mailboxes() const {
return mailboxes_;
@@ -84,6 +100,10 @@ class VIZ_COMMON_EXPORT BlitRequest {
return mailboxes_[i];
}
+ bool populates_gpu_memory_buffer() const {
+ return populates_gpu_memory_buffer_;
+ }
+
// Appends a new `BlendBitmap` request to this blit request.
// |source_region| is expressed in |image|'s coordinate system
// (i.e. 0,0 width x height rectangle). |destination_region| is expressed
@@ -106,12 +126,20 @@ class VIZ_COMMON_EXPORT BlitRequest {
// images.
gfx::Point destination_region_offset_;
+ // Specifies the letterboxing behavior of this request.
+ LetterboxingBehavior letterboxing_behavior_;
+
// Mailboxes with planes that will be populated.
// The textures can (but don't have to be) backed by
// a GpuMemoryBuffer. The pixel format of the request determines
// how many planes need to be present.
std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes> mailboxes_;
+ // True if `mailboxes_` describe shared images that have been created from
+ // a GpuMemoryBuffer. In this case, the CopyOutputResult needs to be sent out
+ // only after it's safe to map the GpuMemoryBuffer to system memory.
+ bool populates_gpu_memory_buffer_;
+
// Collection of bitmaps that will be blended onto the textures.
// They will be blended in order (so if i < j, bitmap at offset i will
// be blended before bitmap at offset j), using SrcOver blend mode.
diff --git a/chromium/components/viz/common/frame_sinks/copy_output_request.cc b/chromium/components/viz/common/frame_sinks/copy_output_request.cc
index 648a6fa7f0a..63f2ff3fd1f 100644
--- a/chromium/components/viz/common/frame_sinks/copy_output_request.cc
+++ b/chromium/components/viz/common/frame_sinks/copy_output_request.cc
@@ -107,6 +107,7 @@ void CopyOutputRequest::set_blit_request(BlitRequest blit_request) {
DCHECK(!blit_request_);
DCHECK_EQ(result_destination(), ResultDestination::kNativeTextures);
DCHECK_EQ(result_format(), ResultFormat::NV12_PLANES);
+ DCHECK(has_result_selection());
// Destination region must start at an even offset for NV12 results:
DCHECK_EQ(blit_request.destination_region_offset().x() % 2, 0);
diff --git a/chromium/components/viz/common/frame_sinks/copy_output_request.h b/chromium/components/viz/common/frame_sinks/copy_output_request.h
index fa9a5ddd42a..28fef1f9b69 100644
--- a/chromium/components/viz/common/frame_sinks/copy_output_request.h
+++ b/chromium/components/viz/common/frame_sinks/copy_output_request.h
@@ -116,12 +116,14 @@ class VIZ_COMMON_EXPORT CopyOutputRequest {
// Optionally specify that only a portion of the result be generated. The
// selection rect will be clamped to the result bounds, which always starts at
// 0,0 and spans the post-scaling size of the copy area (see set_area()
- // above). Only RGBA format supports odd-sized result selection.
+ // above). Only RGBA format supports odd-sized result selection. Can only be
+ // called before blit request was set on the copy request.
void set_result_selection(const gfx::Rect& selection) {
DCHECK(result_format_ == ResultFormat::RGBA ||
(selection.width() % 2 == 0 && selection.height() % 2 == 0))
<< "CopyOutputRequest supports odd-sized result_selection() only for "
"RGBA!";
+ DCHECK(!has_blit_request());
result_selection_ = selection;
}
bool has_result_selection() const { return result_selection_.has_value(); }
@@ -129,7 +131,15 @@ class VIZ_COMMON_EXPORT CopyOutputRequest {
// Requests that the region copied by the CopyOutputRequest be blitted into
// the caller's textures. Can be called only for CopyOutputRequests that
- // target native textures.
+ // target native textures. Requires that result selection was set, in which
+ // case the caller's textures will be populated with the results of the
+ // copy request. The region in the caller's textures that will be populated
+ // is specified by `gfx::Rect(blit_request.destination_region_offset(),
+ // result_selection().size())`. If blit request is configured to perform
+ // letterboxing, all contents outside of that region will be overwritten with
+ // black, otherwise they will be unchanged. If the copy request's result would
+ // be smaller than `result_selection().size()`, the request will fail (i.e.
+ // empty result will be sent).
void set_blit_request(BlitRequest blit_request);
bool has_blit_request() const { return blit_request_.has_value(); }
const BlitRequest& blit_request() const { return *blit_request_; }
diff --git a/chromium/components/viz/common/gl_i420_converter.cc b/chromium/components/viz/common/gl_i420_converter.cc
deleted file mode 100644
index c00e202c351..00000000000
--- a/chromium/components/viz/common/gl_i420_converter.cc
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/common/gl_i420_converter.h"
-
-#include <utility>
-
-#include "components/viz/common/gpu/context_provider.h"
-
-namespace viz {
-
-GLI420Converter::GLI420Converter(ContextProvider* context_provider)
- : GLI420Converter(context_provider, true) {
- DCHECK(context_provider_);
-}
-
-GLI420Converter::GLI420Converter(ContextProvider* context_provider,
- bool allow_mrt_path)
- : context_provider_(context_provider),
- step1_(context_provider_),
- step2_(context_provider_) {
- DCHECK(context_provider_);
- context_provider_->AddObserver(this);
- if (!allow_mrt_path || step1_.GetMaxDrawBuffersSupported() < 2) {
- step3_ = std::make_unique<GLScaler>(context_provider_);
- step4_ = std::make_unique<GLScaler>(context_provider_);
- }
-}
-
-GLI420Converter::~GLI420Converter() {
- OnContextLost(); // Free context-related resources.
-}
-
-bool GLI420Converter::Configure(const Parameters& params) {
- Parameters step1_params = params;
- if (!step1_params.output_color_space.IsValid()) {
- step1_params.output_color_space = gfx::ColorSpace::CreateREC709();
- }
-
- // Configure the "step 1" scaler.
- if (is_using_mrt_path()) {
- step1_params.export_format = Parameters::ExportFormat::NV61;
- DCHECK_EQ(step1_params.swizzle[0], params.swizzle[0]);
- step1_params.swizzle[1] = GL_RGBA; // Don't swizzle 2nd rendering target.
- } else {
- step1_params.export_format = Parameters::ExportFormat::INTERLEAVED_QUADS;
- step1_params.swizzle[0] = GL_RGBA; // Will swizzle in steps 2-4.
- }
- if (!step1_.Configure(step1_params)) {
- return false;
- }
-
- // Configure the "step 2" scaler (and steps 3 and 4 for the non-MRT path) that
- // further transform the output from the "step 1" scaler to produce the final
- // outputs.
- Parameters step2_params;
- step2_params.scale_to = gfx::Vector2d(1, 1);
- step2_params.source_color_space = step1_params.output_color_space;
- step2_params.output_color_space = step1_params.output_color_space;
- // Use FAST quality, a single bilinear pass, because there will either be no
- // scaling or exactly 50% scaling.
- step2_params.quality = Parameters::Quality::FAST;
- step2_params.swizzle[0] = params.swizzle[0];
- if (is_using_mrt_path()) {
- // NV61 provides half-width and full-height U/V. I420 U/V planes are
- // half-width and half-height. So, scale Y by 50%.
- step2_params.scale_from = gfx::Vector2d(1, 2);
- step2_params.export_format =
- Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE;
- step2_params.swizzle[1] = step2_params.swizzle[0];
- if (!step2_.Configure(step2_params)) {
- return false;
- }
- } else {
- // Extract a full-size Y plane from the interleaved YUVA from step 1.
- step2_params.scale_from = gfx::Vector2d(1, 1);
- step2_params.export_format = Parameters::ExportFormat::CHANNEL_0;
- if (!step2_.Configure(step2_params)) {
- return false;
- }
- // Extract half-size U/V planes from the interleaved YUVA from step 1.
- step2_params.scale_from = gfx::Vector2d(2, 2);
- step2_params.export_format = Parameters::ExportFormat::CHANNEL_1;
- if (!step3_->Configure(step2_params)) {
- return false;
- }
- step2_params.export_format = Parameters::ExportFormat::CHANNEL_2;
- if (!step4_->Configure(step2_params)) {
- return false;
- }
- }
-
- params_ = params;
- return true;
-}
-
-bool GLI420Converter::Convert(GLuint src_texture,
- const gfx::Size& src_texture_size,
- const gfx::Vector2d& src_offset,
- const gfx::Rect& output_rect,
- const GLuint yuv_textures[3]) {
- DCHECK_EQ(output_rect.x() % 8, 0);
- DCHECK_EQ(output_rect.width() % 8, 0);
- DCHECK_EQ(output_rect.y() % 2, 0);
- DCHECK_EQ(output_rect.height() % 2, 0);
-
- if (!context_provider_) {
- return false;
- }
-
- if (is_using_mrt_path()) {
- const gfx::Rect luma_output_rect(output_rect.x() / 4, output_rect.y(),
- output_rect.width() / 4,
- output_rect.height());
- EnsureIntermediateTextureDefined(luma_output_rect.size());
- const gfx::Rect chroma_output_rect(
- gfx::Size(luma_output_rect.width() / 2, luma_output_rect.height() / 2));
- return (step1_.ScaleToMultipleOutputs(
- src_texture, src_texture_size, src_offset, yuv_textures[0],
- intermediate_texture_, luma_output_rect) &&
- step2_.ScaleToMultipleOutputs(intermediate_texture_,
- intermediate_texture_size_,
- gfx::Vector2d(), yuv_textures[1],
- yuv_textures[2], chroma_output_rect));
- }
-
- // Non-MRT path:
- EnsureIntermediateTextureDefined(output_rect.size());
- const gfx::Rect luma_output_rect(0, 0, output_rect.width() / 4,
- output_rect.height());
- const gfx::Rect chroma_output_rect(0, 0, luma_output_rect.width() / 2,
- luma_output_rect.height() / 2);
- return (step1_.Scale(src_texture, src_texture_size, src_offset,
- intermediate_texture_, output_rect) &&
- step2_.Scale(intermediate_texture_, intermediate_texture_size_,
- gfx::Vector2d(), yuv_textures[0], luma_output_rect) &&
- step3_->Scale(intermediate_texture_, intermediate_texture_size_,
- gfx::Vector2d(), yuv_textures[1], chroma_output_rect) &&
- step4_->Scale(intermediate_texture_, intermediate_texture_size_,
- gfx::Vector2d(), yuv_textures[2], chroma_output_rect));
-}
-
-// static
-gfx::Rect GLI420Converter::ToAlignedRect(const gfx::Rect& rect) {
- // Origin coordinates: FLOOR(...)
- const int aligned_x =
- ((rect.x() < 0) ? ((rect.x() - 7) / 8) : (rect.x() / 8)) * 8;
- const int aligned_y =
- ((rect.y() < 0) ? ((rect.y() - 1) / 2) : (rect.y() / 2)) * 2;
- // Span coordinates: CEIL(...)
- const int aligned_right =
- ((rect.right() < 0) ? (rect.right() / 8) : ((rect.right() + 7) / 8)) * 8;
- const int aligned_bottom =
- ((rect.bottom() < 0) ? (rect.bottom() / 2) : ((rect.bottom() + 1) / 2)) *
- 2;
- return gfx::Rect(aligned_x, aligned_y, aligned_right - aligned_x,
- aligned_bottom - aligned_y);
-}
-
-// static
-bool GLI420Converter::ParametersAreEquivalent(const Parameters& a,
- const Parameters& b) {
- const auto Resolve = [](Parameters params) {
- // Per header comments, if an invalid output_color_space is specified, use
- // REC709.
- if (!params.output_color_space.IsValid()) {
- params.output_color_space = gfx::ColorSpace::CreateREC709();
- }
- // Both of these fields are overwritten, in Configure(), whether the MRT
- // path is going to be used or not. So, for the purposes of "equivalence,"
- // just set these like the MRT path would.
- params.export_format = Parameters::ExportFormat::NV61;
- params.swizzle[1] = GL_RGBA;
- return params;
- };
- return GLScaler::ParametersAreEquivalent(Resolve(a), Resolve(b));
-}
-
-void GLI420Converter::EnsureIntermediateTextureDefined(
- const gfx::Size& required) {
- if (intermediate_texture_size_ == required) {
- return;
- }
- auto* const gl = context_provider_->ContextGL();
- if (intermediate_texture_ == 0) {
- gl->GenTextures(1, &intermediate_texture_);
- }
- gl->BindTexture(GL_TEXTURE_2D, intermediate_texture_);
- gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, required.width(), required.height(),
- 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
- intermediate_texture_size_ = required;
-}
-
-void GLI420Converter::OnContextLost() {
- if (intermediate_texture_ != 0) {
- if (auto* gl = context_provider_->ContextGL()) {
- gl->DeleteTextures(1, &intermediate_texture_);
- }
- intermediate_texture_ = 0;
- intermediate_texture_size_ = gfx::Size();
- }
- if (context_provider_) {
- context_provider_->RemoveObserver(this);
- context_provider_ = nullptr;
- }
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/common/gl_i420_converter.h b/chromium/components/viz/common/gl_i420_converter.h
deleted file mode 100644
index 6792ca8fdc8..00000000000
--- a/chromium/components/viz/common/gl_i420_converter.h
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2018 The Chromium 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 COMPONENTS_VIZ_COMMON_GL_I420_CONVERTER_H_
-#define COMPONENTS_VIZ_COMMON_GL_I420_CONVERTER_H_
-
-#include <memory>
-
-#include "base/memory/raw_ptr.h"
-#include "base/memory/scoped_refptr.h"
-#include "components/viz/common/gl_scaler.h"
-#include "components/viz/common/gpu/context_lost_observer.h"
-#include "components/viz/common/viz_common_export.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace gfx {
-class Rect;
-class Vector2d;
-} // namespace gfx
-
-namespace viz {
-
-class ContextProvider;
-
-// A convenience wrapper around GLScaler that also reformats the scaler's output
-// from interleaved RGBA to I420 planes. The I420 format consists of three
-// planes of image data: the Y (luma) plane at full size, plus U and V (chroma)
-// planes at half-width and half-height. There are two possible modes of
-// operation (auto-detected at runtime):
-//
-// The faster, multiple rendering target (MRT) path: If the platform supports
-// MRTs (most of the GPUs in use today), scaling and conversion is a two step
-// process:
-//
-// Step 1: Produce NV61 format output, a luma plane and a UV-interleaved
-// image. The luma plane is the same as the desired I420 luma plane. Note,
-// that the UV image is of half-width but not yet half-height.
-//
-// (interleaved quads)
-// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
-// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
-// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
-// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
-// |
-// | (luma plane) (chroma, interleaved)
-// | YYYY YYYY UVUV UVUV
-// +---> { YYYY YYYY + UVUV UVUV }
-// YYYY YYYY UVUV UVUV
-// YYYY YYYY UVUV UVUV
-//
-// Step 2: Derives the two I420 chroma planes from the UV-interleaved image
-// from Step 1. This step separates the U and V pixels into separate planes,
-// and also scales the height by half. This produces the desired I420 chroma
-// planes.
-//
-// (chroma, interleaved) (two chroma planes)
-// UVUV UVUV
-// UVUV UVUV --> { UUUU + VVVV }
-// UVUV UVUV UUUU VVVV
-// UVUV UVUV
-//
-// The non-MRT path: For platforms that can only render to a single target at a
-// time. This first scales the source to its final size and color-converts,
-// transforming an RGBA input into a YUVA output. Then, it scans the YUVA image
-// three times to generate each of the Y+U+V planes.
-//
-// Texture packing: OpenGLES2 treats all of the input and output textures as
-// RGBA format. See comments for the Convert() method, which explains how the
-// planar image data is packed into GL_RGBA textures, how the output textures
-// should be sized, and why there are alignment requirements when specifying the
-// output rect.
-class VIZ_COMMON_EXPORT GLI420Converter final : public ContextLostObserver {
- public:
- // GLI420Converter uses the exact same parameters as GLScaler.
- using Parameters = GLScaler::Parameters;
-
- explicit GLI420Converter(ContextProvider* context_provider);
-
- GLI420Converter(const GLI420Converter&) = delete;
- GLI420Converter& operator=(const GLI420Converter&) = delete;
-
- ~GLI420Converter() final;
-
- // Returns true if the GL context provides the necessary support for enabling
- // precise color management (see Parameters::enable_precise_color_management).
- bool SupportsPreciseColorManagement() const {
- return step1_.SupportsPreciseColorManagement();
- }
-
- // [Re]Configure the converter with the given |new_params|. Returns true on
- // success, or false on failure. If |new_params| does not specify an
- // |output_color_space|, it will be default to REC709.
- [[nodiscard]] bool Configure(const Parameters& new_params);
-
- // Returns the currently-configured and resolved Parameters. Results are
- // undefined if Configure() has never been called successfully.
- const Parameters& params() const { return params_; }
-
- // Scales a portion of |src_texture|, then format-converts it to three I420
- // planes, placing the results into |yuv_textures| at offset (0, 0). Returns
- // true to indicate success, or false if this GLI420Converter is not valid.
- //
- // |src_texture_size| is the full, allocated size of the |src_texture|. This
- // is required for computing texture coordinate transforms (and only because
- // the OpenGL ES 2.0 API lacks the ability to query this info).
- //
- // |src_offset| is the offset in the source texture corresponding to point
- // (0,0) in the source/output coordinate spaces. This prevents the need for
- // extra texture copies just to re-position the source coordinate system.
- //
- // |aligned_output_rect| selects the region to draw (in the scaled, not the
- // source, coordinate space). This is used to save work in cases where only a
- // portion needs to be re-scaled. Because of the way the planar image data is
- // packed in the output textures, the output rect's coordinates must be
- // aligned (see ToAlignedRect() below).
- //
- // The |yuv_textures| are packed with planar data, meaning that each RGBA quad
- // contains four pixel values: R is pixel 0, G is pixel 1, and so on. This
- // makes it trivial to read-back the textures from a pixel buffer as a
- // sequence of unsigned bytes. Thus, the output texture for the Y plane should
- // be defined as GL_RGBA and be at least 1/4 the width of that specified in
- // |aligned_output_rect|. Similarly, the output textures for the U and V
- // planes should be defined as GL_RGBA and have at least 1/8 the width and 1/2
- // the height of |aligned_output_rect|.
- //
- // WARNING: The output will always be placed at (0, 0) in the output textures,
- // and not at |aligned_output_rect.origin()|.
- //
- // Note that the |src_texture| will have the min/mag filter set to GL_LINEAR
- // and wrap_s/t set to CLAMP_TO_EDGE in this call.
- bool Convert(GLuint src_texture,
- const gfx::Size& src_texture_size,
- const gfx::Vector2d& src_offset,
- const gfx::Rect& aligned_output_rect,
- const GLuint yuv_textures[3]);
-
- // Returns the smallest Rect that encloses |rect| and lays on aligned
- // boundaries, as required by the |aligned_output_rect| argument passed to
- // Convert(). The horizontal coordinates will always be a multiple of 8, and
- // the vertical coordinates a multiple of 2.
- static gfx::Rect ToAlignedRect(const gfx::Rect& rect);
-
- // Returns true if configuring a GLI420Converter with either |a| or |b| will
- // produce identical behaviors and results.
- static bool ParametersAreEquivalent(const Parameters& a, const Parameters& b);
-
- private:
- friend class GLI420ConverterPixelTest;
-
- GLI420Converter(ContextProvider* context_provider, bool allow_mrt_path);
-
- bool is_using_mrt_path() const { return !step3_; }
-
- // Creates or re-defines the intermediate texture, to ensure a texture of the
- // given |required| size is defined.
- void EnsureIntermediateTextureDefined(const gfx::Size& required);
-
- // ContextLostObserver implementation.
- void OnContextLost() final;
-
- // The provider of the GL context. This is non-null while the GL context is
- // valid and GLI420Converter is observing for context loss.
- raw_ptr<ContextProvider> context_provider_;
-
- // Scales the source content and produces either:
- // * MRT path: NV61-format output in two textures.
- // * Non-MRT path: YUVA interleaved output in one texture.
- GLScaler step1_;
-
- // Holds the results from executing the first-stage |scaler_|, and is read by
- // the other scalers:
- // * MRT path: This holds the UV-interleaved data (2nd rendering target).
- // * Non-MRT path: The scaled YUVA interleaved data.
- GLuint intermediate_texture_ = 0;
- gfx::Size intermediate_texture_size_;
-
- // Step 2 operation using the |intermediate_texture_| as input:
- // * MRT path: Separates-out the U and V planes (and scales height by half).
- // * Non-MRT path: Extracts the luma plane.
- GLScaler step2_;
-
- // Steps 3 and 4 are used by the non-MRT path only, to extract the two chroma
- // planes from |intermediate_texture_|.
- std::unique_ptr<GLScaler> step3_;
- std::unique_ptr<GLScaler> step4_;
-
- // The Parameters that were provided to the last successful Configure() call.
- Parameters params_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_COMMON_GL_I420_CONVERTER_H_
diff --git a/chromium/components/viz/common/gl_i420_converter_pixeltest.cc b/chromium/components/viz/common/gl_i420_converter_pixeltest.cc
deleted file mode 100644
index 1cf61bbd691..00000000000
--- a/chromium/components/viz/common/gl_i420_converter_pixeltest.cc
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/common/gl_i420_converter.h"
-
-#include <GLES2/gl2ext.h>
-
-#include "cc/test/pixel_test.h"
-#include "cc/test/pixel_test_utils.h"
-#include "components/viz/test/gl_scaler_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace viz {
-
-class GLI420ConverterPixelTest : public cc::PixelTest,
- public GLScalerTestUtil,
- public testing::WithParamInterface<bool> {
- public:
- bool allow_mrt_path() const { return GetParam(); }
- GLI420Converter* converter() const { return converter_.get(); }
-
- GLuint CreateTexture(const gfx::Size& size) {
- return texture_helper_->CreateTexture(size);
- }
-
- GLuint UploadTexture(const SkBitmap& bitmap) {
- return texture_helper_->UploadTexture(bitmap);
- }
-
- SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size) {
- return texture_helper_->DownloadTexture(texture, size);
- }
-
- protected:
- void SetUp() final {
- cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft);
- converter_.reset(new GLI420Converter(context_provider(), allow_mrt_path()));
- texture_helper_ = std::make_unique<GLScalerTestTextureHelper>(
- context_provider()->ContextGL());
- }
-
- void TearDown() final {
- texture_helper_.reset();
- converter_.reset();
- cc::PixelTest::TearDown();
- }
-
- private:
- std::unique_ptr<GLI420Converter> converter_;
- std::unique_ptr<GLScalerTestTextureHelper> texture_helper_;
-};
-
-// Note: This test is pretty much the same as
-// GLScalerPixelTest.Example_ScaleAndExportForScreenVideoCapture. The goal of
-// this pixel test is to just confirm that everything internal to
-// GLI420Converter has been plumbed-through correctly.
-TEST_P(GLI420ConverterPixelTest, ScaleAndConvert) {
- // These parameters have been chosen based on: 1) overriding defaults, to
- // confirm Parameters plumbing; and 2) typical operation on most platforms
- // (e.g., flipped source textures, the need to swizzle outputs, etc.).
- GLI420Converter::Parameters params;
- params.scale_from = gfx::Vector2d(2160, 1440);
- params.scale_to = gfx::Vector2d(1280, 720);
- params.source_color_space = DefaultRGBColorSpace();
- params.output_color_space = DefaultYUVColorSpace();
- params.enable_precise_color_management =
- converter()->SupportsPreciseColorManagement();
- params.quality = GLScaler::Parameters::Quality::GOOD;
- params.is_flipped_source = true;
- params.flip_output = true;
- params.swizzle[0] = GL_BGRA_EXT; // Swizzle for readback.
- ASSERT_TRUE(converter()->Configure(params));
-
- constexpr gfx::Size kSourceSize = gfx::Size(2160, 1440);
- const GLuint src_texture = UploadTexture(
- CreateVerticallyFlippedBitmap(CreateSMPTETestImage(kSourceSize)));
- constexpr gfx::Rect kOutputRect = gfx::Rect(0, 0, 1280, 720);
- ASSERT_EQ(kOutputRect, GLI420Converter::ToAlignedRect(kOutputRect));
- SkBitmap expected = CreateSMPTETestImage(kOutputRect.size());
- ConvertRGBABitmapToYUV(&expected);
-
- // While the output size is 1280x720, the packing of 4 pixels into one RGBA
- // quad means that the texture width must be divided by 4 (for the Y
- // plane). Then, the other two planes are half the size of the Y plane in both
- // dimensions.
- const gfx::Size y_plane_size(kOutputRect.width() / 4, kOutputRect.height());
- const gfx::Size chroma_plane_size(y_plane_size.width() / 2,
- y_plane_size.height() / 2);
- const GLuint yuv_textures[3] = {CreateTexture(y_plane_size),
- CreateTexture(chroma_plane_size),
- CreateTexture(chroma_plane_size)};
-
- ASSERT_TRUE(converter()->Convert(src_texture, kSourceSize, gfx::Vector2d(),
- kOutputRect, yuv_textures));
-
- // Download the textures, and unpack them into an interleaved YUV bitmap, for
- // comparison against the |expected| rendition.
- SkBitmap actual = AllocateRGBABitmap(kOutputRect.size());
- actual.eraseColor(SkColorSetARGB(0xff, 0x00, 0x80, 0x80));
- SkBitmap y_plane = DownloadTexture(yuv_textures[0], y_plane_size);
- SwizzleBitmap(&y_plane);
- UnpackPlanarBitmap(y_plane, 0, &actual);
- SkBitmap u_plane = DownloadTexture(yuv_textures[1], chroma_plane_size);
- SwizzleBitmap(&u_plane);
- UnpackPlanarBitmap(u_plane, 1, &actual);
- SkBitmap v_plane = DownloadTexture(yuv_textures[2], chroma_plane_size);
- SwizzleBitmap(&v_plane);
- UnpackPlanarBitmap(v_plane, 2, &actual);
-
- // Provide generous error limits to account for the chroma subsampling in the
- // |actual| result when compared to the perfect |expected| rendition.
- constexpr float kAvgAbsoluteErrorLimit = 16.f;
- constexpr int kMaxAbsoluteErrorLimit = 0x80;
- EXPECT_TRUE(cc::FuzzyPixelComparator(false, 100.f, 0.f,
- kAvgAbsoluteErrorLimit,
- kMaxAbsoluteErrorLimit, 0)
- .Compare(expected, actual))
- << "\nActual: " << cc::GetPNGDataUrl(actual)
- << "\nExpected: " << cc::GetPNGDataUrl(expected);
-}
-
-// Run the tests twice, once disallowing use of the MRT path, and once allowing
-// its use (auto-detecting whether the current platform supports it).
-INSTANTIATE_TEST_SUITE_P(All, GLI420ConverterPixelTest, testing::Bool());
-
-} // namespace viz
diff --git a/chromium/components/viz/common/gl_i420_converter_unittest.cc b/chromium/components/viz/common/gl_i420_converter_unittest.cc
deleted file mode 100644
index 9fd42188e99..00000000000
--- a/chromium/components/viz/common/gl_i420_converter_unittest.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/common/gl_i420_converter.h"
-
-#include <string>
-
-#include "base/strings/stringprintf.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkRect.h"
-#include "ui/gfx/geometry/rect.h"
-
-namespace viz {
-namespace {
-
-// Syntactic convenience: It's clearer to express the tests in terms of
-// left+top+right+bottom coordinates, rather than gfx::Rect's x+y+width+height
-// scheme.
-SkIRect ToAlignedRect(const SkIRect& rect) {
- const gfx::Rect& result = GLI420Converter::ToAlignedRect(
- gfx::Rect(rect.fLeft, rect.fTop, rect.fRight - rect.fLeft,
- rect.fBottom - rect.fTop));
- return SkIRect{result.x(), result.y(), result.right(), result.bottom()};
-}
-
-// Logging convenience.
-std::string ToString(const SkIRect& rect) {
- return base::StringPrintf("%d,%d~%d%d", rect.fLeft, rect.fTop, rect.fRight,
- rect.fBottom);
-}
-
-TEST(GLI420ConverterTest, AlignsOutputRects) {
- struct {
- SkIRect expected;
- SkIRect input;
- } kTestCases[] = {
- {SkIRect{0, 0, 0, 0}, SkIRect{0, 0, 0, 0}},
-
- {SkIRect{-16, 0, 16, 4}, SkIRect{-9, 0, 16, 4}},
- {SkIRect{-8, 0, 16, 4}, SkIRect{-8, 0, 16, 4}},
- {SkIRect{-8, 0, 16, 4}, SkIRect{-7, 0, 16, 4}},
- {SkIRect{-8, 0, 16, 4}, SkIRect{-1, 0, 16, 4}},
- {SkIRect{0, 0, 16, 4}, SkIRect{0, 0, 16, 4}},
- {SkIRect{0, 0, 16, 4}, SkIRect{1, 0, 16, 4}},
- {SkIRect{0, 0, 16, 4}, SkIRect{7, 0, 16, 4}},
- {SkIRect{8, 0, 16, 4}, SkIRect{8, 0, 16, 4}},
- {SkIRect{8, 0, 16, 4}, SkIRect{9, 0, 16, 4}},
-
- {SkIRect{0, -4, 16, 4}, SkIRect{0, -3, 16, 4}},
- {SkIRect{0, -2, 16, 4}, SkIRect{0, -2, 16, 4}},
- {SkIRect{0, -2, 16, 4}, SkIRect{0, -1, 16, 4}},
- {SkIRect{0, 0, 16, 4}, SkIRect{0, 0, 16, 4}},
- {SkIRect{0, 0, 16, 4}, SkIRect{0, 1, 16, 4}},
- {SkIRect{0, 2, 16, 4}, SkIRect{0, 2, 16, 4}},
- {SkIRect{0, 2, 16, 4}, SkIRect{0, 3, 16, 4}},
-
- {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 1, 2}},
- {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 7, 2}},
- {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 8, 2}},
- {SkIRect{0, 0, 16, 2}, SkIRect{0, 0, 9, 2}},
-
- {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 8, 1}},
- {SkIRect{0, 0, 8, 2}, SkIRect{0, 0, 8, 2}},
- {SkIRect{0, 0, 8, 4}, SkIRect{0, 0, 8, 3}},
- {SkIRect{0, 0, 8, 4}, SkIRect{0, 0, 8, 4}},
- };
-
- for (const auto& test_case : kTestCases) {
- EXPECT_EQ(test_case.expected, ToAlignedRect(test_case.input))
- << "ToAlignedRect(" << ToString(test_case.input) << ") should be "
- << ToString(test_case.expected);
- }
-}
-
-} // namespace
-} // namespace viz
diff --git a/chromium/components/viz/common/gl_nv12_converter.cc b/chromium/components/viz/common/gl_nv12_converter.cc
deleted file mode 100644
index 2c737130a6b..00000000000
--- a/chromium/components/viz/common/gl_nv12_converter.cc
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2021 The Chromium 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/viz/common/gl_nv12_converter.h"
-
-#include "base/memory/ptr_util.h"
-#include "components/viz/common/gl_i420_converter.h"
-#include "components/viz/common/gpu/context_provider.h"
-
-namespace viz {
-
-// static
-std::unique_ptr<GLNV12Converter> GLNV12Converter::CreateConverterForTest(
- ContextProvider* context_provider,
- bool allow_mrt_path) {
- return base::WrapUnique(
- new GLNV12Converter(context_provider, allow_mrt_path));
-}
-
-GLNV12Converter::GLNV12Converter(ContextProvider* context_provider)
- : GLNV12Converter(context_provider, true) {}
-
-GLNV12Converter::GLNV12Converter(ContextProvider* context_provider,
- bool allow_mrt_path)
- : context_provider_(context_provider),
- step1_(context_provider_),
- step2_(context_provider_) {
- DCHECK(context_provider_);
- context_provider_->AddObserver(this);
- if (!allow_mrt_path || step1_.GetMaxDrawBuffersSupported() < 2) {
- step3_ = std::make_unique<GLScaler>(context_provider_);
- }
-}
-
-GLNV12Converter::~GLNV12Converter() {
- OnContextLost(); // Free context-related resources.
-}
-
-// static
-gfx::Rect GLNV12Converter::ToAlignedRect(const gfx::Rect& rect) {
- // Origin coordinates: FLOOR(...)
- const int aligned_x =
- ((rect.x() < 0) ? ((rect.x() - 3) / 4) : (rect.x() / 4)) * 4;
- const int aligned_y =
- ((rect.y() < 0) ? ((rect.y() - 1) / 2) : (rect.y() / 2)) * 2;
- // Span coordinates: CEIL(...)
- const int aligned_right =
- ((rect.right() < 0) ? (rect.right() / 4) : ((rect.right() + 3) / 4)) * 4;
- const int aligned_bottom =
- ((rect.bottom() < 0) ? (rect.bottom() / 2) : ((rect.bottom() + 1) / 2)) *
- 2;
- return gfx::Rect(aligned_x, aligned_y, aligned_right - aligned_x,
- aligned_bottom - aligned_y);
-}
-
-// static
-bool GLNV12Converter::ParametersAreEquivalent(const Parameters& a,
- const Parameters& b) {
- // Implemented in terms of GLI420Converter:
- return GLI420Converter::ParametersAreEquivalent(a, b);
-}
-
-void GLNV12Converter::EnsureIntermediateTextureDefined(
- const gfx::Size& required) {
- if (intermediate_texture_size_ == required) {
- return;
- }
- auto* const gl = context_provider_->ContextGL();
- if (intermediate_texture_ == 0) {
- gl->GenTextures(1, &intermediate_texture_);
- }
- gl->BindTexture(GL_TEXTURE_2D, intermediate_texture_);
- gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, required.width(), required.height(),
- 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
- intermediate_texture_size_ = required;
-}
-
-bool GLNV12Converter::Configure(const Parameters& params) {
- Parameters step1_params = params;
- if (!step1_params.output_color_space.IsValid()) {
- step1_params.output_color_space = gfx::ColorSpace::CreateREC709();
- }
-
- // Configure the "step 1" scaler.
- if (is_using_mrt_path()) {
- step1_params.export_format = Parameters::ExportFormat::NV61;
- DCHECK_EQ(step1_params.swizzle[0], params.swizzle[0]);
- step1_params.swizzle[1] = GL_RGBA; // Don't swizzle 2nd rendering target.
- } else {
- step1_params.export_format = Parameters::ExportFormat::INTERLEAVED_QUADS;
- step1_params.swizzle[0] = GL_RGBA; // Will swizzle in steps 2-3.
- }
- if (!step1_.Configure(step1_params)) {
- return false;
- }
-
- // Configure the "step 2" scaler (and step 3 for the non-MRT path) that
- // further transform the output from the "step 1" scaler to produce the final
- // outputs.
- Parameters step2_params;
- step2_params.scale_to = gfx::Vector2d(1, 1);
- step2_params.source_color_space = step1_params.output_color_space;
- step2_params.output_color_space = step1_params.output_color_space;
- // Use FAST quality, a single bilinear pass, because there will either be no
- // scaling or exactly 50% scaling.
- step2_params.quality = Parameters::Quality::FAST;
- step2_params.swizzle[0] = params.swizzle[0];
- if (is_using_mrt_path()) {
- // NV61 provides half-width and full-height U/V. NV12 UV planes are
- // half-width and half-height. So, scale just the Y by 50%.
- step2_params.scale_from = gfx::Vector2d(1, 2);
- step2_params.export_format = Parameters::ExportFormat::INTERLEAVED_QUADS;
- step2_params.swizzle[1] = step2_params.swizzle[0];
- if (!step2_.Configure(step2_params)) {
- return false;
- }
- } else {
- // Extract a full-size Y plane from the interleaved YUVA from step 1.
- step2_params.scale_from = gfx::Vector2d(1, 1);
- step2_params.export_format = Parameters::ExportFormat::CHANNEL_0;
- if (!step2_.Configure(step2_params)) {
- return false;
- }
- // Extract a half-size UV plane from the interleaved YUVA from step 1.
- // UV_CHANNELS provides half-width and full-height UV plane. NV12 UV planes
- // are half-wifth and half-height. So, scale just the Y by 50%.
- step2_params.scale_from = gfx::Vector2d(1, 2);
- step2_params.export_format = Parameters::ExportFormat::UV_CHANNELS;
- if (!step3_->Configure(step2_params)) {
- return false;
- }
- }
-
- params_ = params;
- return true;
-}
-
-bool GLNV12Converter::Convert(GLuint src_texture,
- const gfx::Size& src_texture_size,
- const gfx::Vector2d& src_offset,
- const gfx::Rect& aligned_output_rect,
- const GLuint yuv_textures[2]) {
- DCHECK_EQ(aligned_output_rect.x() % 4, 0);
- DCHECK_EQ(aligned_output_rect.width() % 4, 0);
- DCHECK_EQ(aligned_output_rect.y() % 2, 0);
- DCHECK_EQ(aligned_output_rect.height() % 2, 0);
-
- if (!context_provider_) {
- return false;
- }
-
- if (is_using_mrt_path()) {
- const gfx::Rect luma_output_rect(
- aligned_output_rect.x() / 4, aligned_output_rect.y(),
- aligned_output_rect.width() / 4, aligned_output_rect.height());
- EnsureIntermediateTextureDefined(luma_output_rect.size());
- const gfx::Rect chroma_output_rect(
- gfx::Size(luma_output_rect.width(), luma_output_rect.height() / 2));
- return (step1_.ScaleToMultipleOutputs(
- src_texture, src_texture_size, src_offset, yuv_textures[0],
- intermediate_texture_, luma_output_rect) &&
- step2_.Scale(intermediate_texture_, intermediate_texture_size_,
- gfx::Vector2d(), yuv_textures[1], chroma_output_rect));
- }
-
- // Non-MRT path:
- EnsureIntermediateTextureDefined(aligned_output_rect.size());
- const gfx::Rect luma_output_rect(0, 0, aligned_output_rect.width() / 4,
- aligned_output_rect.height());
- const gfx::Rect chroma_output_rect(0, 0, luma_output_rect.width(),
- luma_output_rect.height() / 2);
- return (step1_.Scale(src_texture, src_texture_size, src_offset,
- intermediate_texture_, aligned_output_rect) &&
- step2_.Scale(intermediate_texture_, intermediate_texture_size_,
- gfx::Vector2d(), yuv_textures[0], luma_output_rect) &&
- step3_->Scale(intermediate_texture_, intermediate_texture_size_,
- gfx::Vector2d(), yuv_textures[1], chroma_output_rect));
-}
-
-void GLNV12Converter::OnContextLost() {
- if (intermediate_texture_ != 0) {
- if (auto* gl = context_provider_->ContextGL()) {
- gl->DeleteTextures(1, &intermediate_texture_);
- }
- intermediate_texture_ = 0;
- intermediate_texture_size_ = gfx::Size();
- }
- if (context_provider_) {
- context_provider_->RemoveObserver(this);
- context_provider_ = nullptr;
- }
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/common/gl_nv12_converter.h b/chromium/components/viz/common/gl_nv12_converter.h
deleted file mode 100644
index 15c6944270f..00000000000
--- a/chromium/components/viz/common/gl_nv12_converter.h
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2021 The Chromium 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 COMPONENTS_VIZ_COMMON_GL_NV12_CONVERTER_H_
-#define COMPONENTS_VIZ_COMMON_GL_NV12_CONVERTER_H_
-
-#include <memory>
-
-#include "base/memory/raw_ptr.h"
-#include "base/memory/scoped_refptr.h"
-#include "components/viz/common/gl_scaler.h"
-#include "components/viz/common/gpu/context_lost_observer.h"
-#include "components/viz/common/viz_common_export.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace gfx {
-class Rect;
-class Vector2d;
-} // namespace gfx
-
-namespace viz {
-
-class ContextProvider;
-
-// A convenience wrapper around GLScaler that also reformats the scaler's output
-// from interleaved RGBA to NV12 planes. The NV12 format consists of two
-// planes of image data: the Y (luma) plane at full size, plus interleaved UV
-// (chroma) plane at half-width and half-height. There are two possible modes of
-// operation (auto-detected at runtime):
-//
-// The faster, multiple rendering target (MRT) path: If the platform supports
-// MRTs (most of the GPUs in use today), scaling and conversion is a two step
-// process:
-//
-// Step 1: Produce NV61 format output, a luma plane and a UV-interleaved
-// image. The luma plane is the same as the desired NV12 luma plane. Note,
-// that the UV image is of half-width but not yet half-height.
-//
-// (interleaved quads)
-// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
-// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
-// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
-// RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
-// |
-// | (luma plane) (chroma, interleaved)
-// | YYYY YYYY UVUV UVUV
-// +---> { YYYY YYYY + UVUV UVUV }
-// YYYY YYYY UVUV UVUV
-// YYYY YYYY UVUV UVUV
-//
-// Step 2: Downscales the chroma plane.
-//
-// (chroma, interleaved) (chroma, interleaved)
-// UVUV UVUV
-// UVUV UVUV --> UVUV UVUV
-// UVUV UVUV UVUV UVUV
-// UVUV UVUV
-//
-// The non-MRT path: For platforms that can only render to a single target at a
-// time. This first scales the source to its final size and color-converts,
-// transforming an RGBA input into a YUVx output. Then, it scans the YUVA image
-// two times to generate each of the Y+UV planes.
-//
-// Texture packing: OpenGLES2 treats all of the input and output textures as
-// RGBA format. See comments for the Convert() method, which explains how the
-// planar image data is packed into GL_RGBA textures, how the output textures
-// should be sized, and why there are alignment requirements when specifying the
-// output rect.
-class VIZ_COMMON_EXPORT GLNV12Converter final : public ContextLostObserver {
- public:
- // GLNV12Converter uses the exact same parameters as GLScaler.
- using Parameters = GLScaler::Parameters;
-
- explicit GLNV12Converter(ContextProvider* context_provider);
- ~GLNV12Converter() final;
-
- // Returns true if the GL context provides the necessary support for enabling
- // precise color management (see Parameters::enable_precise_color_management).
- bool SupportsPreciseColorManagement() const {
- return step1_.SupportsPreciseColorManagement();
- }
-
- // [Re]Configure the converter with the given |new_params|. Returns true on
- // success, or false on failure. If |new_params| does not specify an
- // |output_color_space|, it will be default to REC709.
- [[nodiscard]] bool Configure(const Parameters& new_params);
-
- // Returns the currently-configured and resolved Parameters. Results are
- // undefined if Configure() has never been called successfully.
- const Parameters& params() const { return params_; }
-
- // Scales a portion of |src_texture|, then format-converts it to two NV12
- // planes, placing the results into |yuv_textures| at offset (0, 0). Returns
- // true to indicate success, or false if this GLNV12Converter is not valid.
- //
- // |src_texture_size| is the full, allocated size of the |src_texture|. This
- // is required for computing texture coordinate transforms (and only because
- // the OpenGL ES 2.0 API lacks the ability to query this info).
- //
- // |src_offset| is the offset in the source texture corresponding to point
- // (0,0) in the source/output coordinate spaces. This prevents the need for
- // extra texture copies just to re-position the source coordinate system.
- //
- // |aligned_output_rect| selects the region to draw (in the scaled, not the
- // source, coordinate space). This is used to save work in cases where only a
- // portion needs to be re-scaled. Because of the way the planar image data is
- // packed in the output textures, the output rect's coordinates must be
- // aligned (see ToAlignedRect() below).
- //
- // The |yuv_textures| are packed with planar data. Depending on the plane,
- // the packing is as follows:
- // - for Y plane, each RGBA quad contains four pixel values: R is pixel 0,
- // G is pixel 1, and so on.
- // - for UV plane, each RGBA quad contains 2 pixel values: RG are UV values
- // for pixel 0, BA are UV values for pixel 1, and so on.
- // This makes it trivial to read-back the textures from a pixel buffer as a
- // sequence of unsigned bytes. Thus, the output texture for the Y plane should
- // be defined as GL_RGBA and be at least 1/4 the width of that specified in
- // |aligned_output_rect|. Similarly, the output texture for the UV plane
- // should be defined as GL_RGBA and have at least 1/4 the width [1]
- // and 1/2 the height [2] of |aligned_output_rect|.
- //
- // [1] 1/4 width = 1/4 // we pack 4 values per pixel
- // * 1/2 // chroma planes are subsampled
- // * 2 // we pack 2 chroma planes
- // * width
- // [2] 1/2 height = 1/2 // chroma planes are subsampled
- // * height
- //
- // WARNING: The output will always be placed at (0, 0) in the output textures,
- // and not at |aligned_output_rect.origin()|.
- //
- // Note that the |src_texture| will have the min/mag filter set to GL_LINEAR
- // and wrap_s/t set to CLAMP_TO_EDGE in this call.
- bool Convert(GLuint src_texture,
- const gfx::Size& src_texture_size,
- const gfx::Vector2d& src_offset,
- const gfx::Rect& aligned_output_rect,
- const GLuint yuv_textures[2]);
-
- // Returns the smallest Rect that encloses |rect| and lays on aligned
- // boundaries, as required by the |aligned_output_rect| argument passed to
- // Convert(). The horizontal coordinates will always be a multiple of 4, and
- // the vertical coordinates a multiple of 2.
- static gfx::Rect ToAlignedRect(const gfx::Rect& rect);
-
- // Returns true if configuring a GLNV12Converter with either |a| or |b| will
- // produce identical behaviors and results.
- static bool ParametersAreEquivalent(const Parameters& a, const Parameters& b);
-
- static std::unique_ptr<GLNV12Converter> CreateConverterForTest(
- ContextProvider* context_provider,
- bool allow_mrt_path);
-
- private:
- GLNV12Converter(ContextProvider* context_provider, bool allow_mrt_path);
-
- bool is_using_mrt_path() const { return !step3_; }
-
- // Creates or re-defines the intermediate texture, to ensure a texture of the
- // given |required| size is defined.
- void EnsureIntermediateTextureDefined(const gfx::Size& required);
-
- // ContextLostObserver implementation.
- void OnContextLost() final;
-
- // The provider of the GL context. This is non-null while the GL context is
- // valid and GLNV12Converter is observing for context loss.
- raw_ptr<ContextProvider> context_provider_;
-
- // Scales the source content and produces either:
- // * MRT path: NV61-format output in two textures.
- // * Non-MRT path: YUVA interleaved output in one texture.
- GLScaler step1_;
-
- // Holds the results from executing the first-stage |scaler_|, and is read by
- // the other scalers:
- // * MRT path: This holds the UV-interleaved data (2nd rendering target).
- // * Non-MRT path: The scaled YUVA interleaved data.
- GLuint intermediate_texture_ = 0;
- gfx::Size intermediate_texture_size_;
-
- // Step 2 operation using the |intermediate_texture_| as input:
- // * MRT path: Scales the height by half.
- // * Non-MRT path: Extracts the luma plane.
- GLScaler step2_;
-
- // Steps 3 is used by the non-MRT path only, to extract the interleaved chroma
- // planes from |intermediate_texture_|.
- std::unique_ptr<GLScaler> step3_;
-
- // The Parameters that were provided to the last successful Configure() call.
- Parameters params_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_COMMON_GL_NV12_CONVERTER_H_
diff --git a/chromium/components/viz/common/gl_nv12_converter_pixeltest.cc b/chromium/components/viz/common/gl_nv12_converter_pixeltest.cc
deleted file mode 100644
index ad3ec480837..00000000000
--- a/chromium/components/viz/common/gl_nv12_converter_pixeltest.cc
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright 2021 The Chromium 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/viz/common/gl_nv12_converter.h"
-
-#include <GLES2/gl2ext.h>
-
-#include "cc/test/pixel_test.h"
-#include "cc/test/pixel_test_utils.h"
-#include "components/viz/test/gl_scaler_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace viz {
-
-class GLNV12ConverterPixelTest
- : public cc::PixelTest,
- public GLScalerTestUtil,
- public testing::WithParamInterface<
- std::tuple<bool, bool, gfx::Vector2d, gfx::Vector2d>> {
- public:
- bool allow_mrt_path() const { return std::get<0>(GetParam()); }
- bool is_rgba() const { return std::get<1>(GetParam()); }
- gfx::Vector2d scale_from() const { return std::get<2>(GetParam()); }
- gfx::Vector2d scale_to() const { return std::get<3>(GetParam()); }
-
- GLNV12Converter* converter() const { return converter_.get(); }
-
- GLuint CreateTexture(const gfx::Size& size) {
- return texture_helper_->CreateTexture(size);
- }
-
- GLuint UploadTexture(const SkBitmap& bitmap) {
- return texture_helper_->UploadTexture(bitmap);
- }
-
- SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size) {
- return texture_helper_->DownloadTexture(texture, size);
- }
-
- protected:
- void SetUp() final {
- cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft);
- converter_ = GLNV12Converter::CreateConverterForTest(context_provider(),
- allow_mrt_path());
- texture_helper_ = std::make_unique<GLScalerTestTextureHelper>(
- context_provider()->ContextGL());
- }
-
- void TearDown() final {
- texture_helper_.reset();
- converter_.reset();
- cc::PixelTest::TearDown();
- }
-
- private:
- std::unique_ptr<GLNV12Converter> converter_;
- std::unique_ptr<GLScalerTestTextureHelper> texture_helper_;
-};
-
-// Note: This test is pretty much the same as
-// GLScalerPixelTest.Example_ScaleAndExportForScreenVideoCapture. The goal of
-// this pixel test is to just confirm that everything internal to
-// GLNV12Converter has been plumbed-through correctly.
-TEST_P(GLNV12ConverterPixelTest, ScaleAndConvert) {
- GLNV12Converter::Parameters params;
- params.scale_from = scale_from();
- params.scale_to = scale_to();
- params.source_color_space = DefaultRGBColorSpace();
- params.output_color_space = DefaultYUVColorSpace();
- params.enable_precise_color_management =
- converter()->SupportsPreciseColorManagement();
- params.quality = GLScaler::Parameters::Quality::GOOD;
- params.is_flipped_source = true;
- params.flip_output = true;
- params.swizzle[0] =
- is_rgba() ? GL_RGBA : GL_BGRA_EXT; // Swizzle for readback.
- ASSERT_TRUE(converter()->Configure(params));
-
- const gfx::Size kSourceSize = gfx::Size(scale_from().x(), scale_from().y());
- const GLuint src_texture = UploadTexture(
- CreateVerticallyFlippedBitmap(CreateSMPTETestImage(kSourceSize)));
- const gfx::Rect kOutputRect = gfx::Rect(0, 0, scale_to().x(), scale_to().y());
- ASSERT_EQ(kOutputRect, GLNV12Converter::ToAlignedRect(kOutputRect));
- SkBitmap expected = CreateSMPTETestImage(kOutputRect.size());
- ConvertRGBABitmapToYUV(&expected);
-
- // While the output size is `kOutputRect.Size()`, the packing of 4 pixels into
- // one RGBA quad means that the texture width must be divided by 4 (for the Y
- // plane). Then, the chroma plane is half the size of the Y plane in both
- // dimensions, but the width is actually double that since we pack 2 values,
- // so `y_plane_size.width() * 1/2 * 2` term is simplified to just
- // `y_plane_size.width()`.
- const gfx::Size y_plane_size(kOutputRect.width() / 4, kOutputRect.height());
- const gfx::Size chroma_plane_size(y_plane_size.width(),
- y_plane_size.height() / 2);
-
- const GLuint yuv_textures[2] = {CreateTexture(y_plane_size),
- CreateTexture(chroma_plane_size)};
-
- ASSERT_TRUE(converter()->Convert(src_texture, kSourceSize, gfx::Vector2d(),
- kOutputRect, yuv_textures));
-
- // Download the textures, and unpack them into an interleaved YUV bitmap, for
- // comparison against the |expected| rendition.
- SkBitmap actual = AllocateRGBABitmap(kOutputRect.size());
- actual.eraseColor(SkColorSetARGB(0xff, 0x00, 0x00, 0x00));
-
- SkBitmap y_plane = DownloadTexture(yuv_textures[0], y_plane_size);
- SkBitmap uv_plane = DownloadTexture(yuv_textures[1], chroma_plane_size);
-
- if (!is_rgba()) {
- // We've asked the converter to produce output in BGRA, & downloaded it to
- // RGBA SkBitmap, so swizzle it.
- SwizzleBitmap(&y_plane);
- SwizzleBitmap(&uv_plane);
- }
-
- UnpackPlanarBitmap(y_plane, 0, &actual);
- UnpackUVBitmap(uv_plane, &actual);
-
- // Provide generous error limits to account for the chroma subsampling in the
- // |actual| result when compared to the perfect |expected| rendition.
- constexpr float kAvgAbsoluteErrorLimit = 16.f;
- constexpr int kMaxAbsoluteErrorLimit = 0x80;
- EXPECT_TRUE(cc::FuzzyPixelComparator(false, 100.f, 0.f,
- kAvgAbsoluteErrorLimit,
- kMaxAbsoluteErrorLimit, 0)
- .Compare(expected, actual))
- << "\nActual: " << cc::GetPNGDataUrl(actual)
- << "\nExpected: " << cc::GetPNGDataUrl(expected);
-}
-
-// Run the tests, first parameter controls whether the MRT path is allowed,
-// the second controls whether the converter will use RGBA format vs BGRA (true
-// for RGBA, i.e. no swizzling done by the converter, false for BGRA, i.e. the
-// converter will swizzle the results when writing to texture).
-// These parameters have been chosen based on: 1) overriding defaults, to
-// confirm Parameters plumbing; and 2) typical operation on most platforms
-// (e.g., flipped source textures, the need to swizzle outputs, etc.). In
-// addition, the `scale_to()` is made to return width that is divisible by 4,
-// but not by 8, to test alignment requirements.
-INSTANTIATE_TEST_SUITE_P(
- All,
- GLNV12ConverterPixelTest,
- testing::Combine(testing::Bool(),
- testing::Bool(),
- testing::Values(gfx::Vector2d(2160, 1440)),
- testing::Values(gfx::Vector2d(1280, 720),
- gfx::Vector2d(900, 600))));
-
-} // namespace viz
diff --git a/chromium/components/viz/common/gl_scaler.cc b/chromium/components/viz/common/gl_scaler.cc
deleted file mode 100644
index 2393ebc8d09..00000000000
--- a/chromium/components/viz/common/gl_scaler.cc
+++ /dev/null
@@ -1,1661 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/common/gl_scaler.h"
-
-#include <algorithm>
-#include <array>
-#include <sstream>
-#include <string>
-
-#include "base/logging.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "gpu/GLES2/gl2chromium.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "third_party/skia/include/third_party/skcms/skcms.h"
-#include "ui/gfx/color_transform.h"
-#include "ui/gfx/geometry/rect_conversions.h"
-
-namespace viz {
-
-namespace {
-
-// The code in GLScaler that computes the ScalerStages is greatly simplified by
-// being able to access the X and Y components by index (instead of
-// Vector2d::x() or Vector2d::y()). Thus, define a helper class to represent the
-// relative size as a 2-element std::array and convert to/from Vector2d.
-struct RelativeSize : public std::array<int, 2> {
- using std::array<int, 2>::operator[];
-
- RelativeSize(int width, int height) : std::array<int, 2>{{width, height}} {}
- explicit RelativeSize(const gfx::Vector2d& v)
- : std::array<int, 2>{{v.x(), v.y()}} {}
-
- gfx::Vector2d AsVector2d() const {
- return gfx::Vector2d((*this)[0], (*this)[1]);
- }
-};
-
-std::ostream& operator<<(std::ostream& out, const RelativeSize& size) {
- return (out << size[0] << 'x' << size[1]);
-}
-
-} // namespace
-
-GLScaler::GLScaler(ContextProvider* context_provider)
- : context_provider_(context_provider) {
- if (context_provider_) {
- DCHECK(context_provider_->ContextGL());
- context_provider_->AddObserver(this);
- }
-}
-
-GLScaler::~GLScaler() {
- OnContextLost(); // Ensures destruction in dependency order.
-}
-
-bool GLScaler::SupportsPreciseColorManagement() const {
- if (!context_provider_) {
- return false;
- }
- if (!supports_half_floats_.has_value()) {
- supports_half_floats_ = AreAllGLExtensionsPresent(
- context_provider_->ContextGL(),
- {"GL_EXT_color_buffer_half_float", "GL_OES_texture_half_float_linear"});
- }
- return supports_half_floats_.value();
-}
-
-int GLScaler::GetMaxDrawBuffersSupported() const {
- if (!context_provider_) {
- return 0;
- }
-
- if (max_draw_buffers_ < 0) {
- // Query the GL context for the multiple draw buffers extension and, if
- // present, the actual platform-supported maximum.
- GLES2Interface* const gl = context_provider_->ContextGL();
- DCHECK(gl);
- if (AreAllGLExtensionsPresent(gl, {"GL_EXT_draw_buffers"})) {
- gl->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_);
- } else {
- // The extension is not present & OpenGL ES 2.0 does not support
- // glDrawBuffers function without it.
- max_draw_buffers_ = 0;
- }
- }
-
- return max_draw_buffers_;
-}
-
-bool GLScaler::Configure(const Parameters& new_params) {
- chain_.reset();
- shader_programs_.clear();
-
- if (!context_provider_) {
- return false;
- }
- GLES2Interface* const gl = context_provider_->ContextGL();
- DCHECK(gl);
-
- params_ = new_params;
-
- // Ensure the client has provided valid scaling vectors.
- if (params_.scale_from.x() == 0 || params_.scale_from.y() == 0 ||
- params_.scale_to.x() == 0 || params_.scale_to.y() == 0) {
- // The caller computed invalid scale_from and/or scale_to values.
- DVLOG(1) << __func__ << ": Invalid scaling vectors: scale_from="
- << params_.scale_from.ToString()
- << ", scale_to=" << params_.scale_to.ToString();
- return false;
- }
-
- // Resolve the color spaces according to the rules described in the header
- // file.
- if (!params_.source_color_space.IsValid()) {
- params_.source_color_space = gfx::ColorSpace::CreateSRGB();
- }
- if (!params_.output_color_space.IsValid()) {
- params_.output_color_space = params_.source_color_space;
- }
-
- // Check that 16-bit half floats are supported if precise color management is
- // being requested.
- if (params_.enable_precise_color_management) {
- if (!SupportsPreciseColorManagement()) {
- DVLOG(1) << __func__
- << ": GL context does not support the half-floats "
- "required for precise color management.";
- return false;
- }
- }
-
- // Check that MRT support is available if certain export formats were
- // specified in the Parameters.
- if (params_.export_format == Parameters::ExportFormat::NV61 ||
- params_.export_format ==
- Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE) {
- if (GetMaxDrawBuffersSupported() < 2) {
- DVLOG(1) << __func__ << ": GL context does not support 2+ draw buffers.";
- return false;
- }
- }
-
- // Color space transformation is meaningless when using the deinterleaver
- // because it only deals with two color channels. This also means precise
- // color management must be disabled.
- if (params_.export_format ==
- Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE &&
- (params_.source_color_space != params_.output_color_space ||
- params_.enable_precise_color_management)) {
- NOTIMPLEMENTED();
- return false;
- }
-
- // Check that one of the two implemented output swizzles has been specified.
- for (GLenum s : params_.swizzle) {
- if (s != GL_RGBA && s != GL_BGRA_EXT) {
- NOTIMPLEMENTED();
- return false;
- }
- }
-
- // Create the chain of ScalerStages. If the quality setting is FAST or there
- // is no scaling to be done, just create a single stage.
- std::unique_ptr<ScalerStage> chain;
- if (params_.quality == Parameters::Quality::FAST ||
- params_.scale_from == params_.scale_to) {
- chain = std::make_unique<ScalerStage>(gl, Shader::BILINEAR, HORIZONTAL,
- params_.scale_from, params_.scale_to);
- } else if (params_.quality == Parameters::Quality::GOOD) {
- chain = CreateAGoodScalingChain(gl, params_.scale_from, params_.scale_to);
- } else if (params_.quality == Parameters::Quality::BEST) {
- chain = CreateTheBestScalingChain(gl, params_.scale_from, params_.scale_to);
- } else {
- NOTREACHED();
- }
- chain = MaybeAppendExportStage(gl, std::move(chain), params_.export_format);
-
- // Determine the color space and the data type of the pixels in the
- // intermediate textures, depending on whether precise color management is
- // enabled. Note that nothing special need be done here if no scaling will be
- // performed.
- GLenum intermediate_texture_type;
- if (params_.enable_precise_color_management &&
- params_.scale_from != params_.scale_to) {
- // Ensure the scaling color space is using a linear transfer function.
- constexpr auto kLinearFunction = std::make_tuple(1, 0, 1, 0, 0, 0, 1);
- skcms_TransferFunction fn;
- if (params_.source_color_space.GetTransferFunction(&fn) &&
- std::make_tuple(fn.a, fn.b, fn.c, fn.d, fn.e, fn.f, fn.g) ==
- kLinearFunction) {
- scaling_color_space_ = params_.source_color_space;
- } else {
- // Use the source color space, but with a linear transfer function.
- skcms_Matrix3x3 to_XYZD50;
- params_.source_color_space.GetPrimaryMatrix(&to_XYZD50);
- std::tie(fn.a, fn.b, fn.c, fn.d, fn.e, fn.f, fn.g) = kLinearFunction;
- scaling_color_space_ = gfx::ColorSpace::CreateCustom(to_XYZD50, fn);
- }
- intermediate_texture_type = GL_HALF_FLOAT_OES;
- } else {
- scaling_color_space_ = params_.source_color_space;
- intermediate_texture_type = GL_UNSIGNED_BYTE;
- }
-
- // Set the shader program on the final stage. Include color space
- // transformation and swizzling, if necessary.
- std::unique_ptr<gfx::ColorTransform> transform;
- if (scaling_color_space_ != params_.output_color_space) {
- transform = gfx::ColorTransform::NewColorTransform(
- scaling_color_space_, params_.output_color_space);
- }
- ScalerStage* const final_stage = chain.get();
- final_stage->set_shader_program(
- GetShaderProgram(final_stage->shader(), intermediate_texture_type,
- transform.get(), params_.swizzle));
-
- // Set the shader program on all prior stages. These stages are all operating
- // in the same color space, |scaling_color_space_|.
- static const GLenum kNoSwizzle[2] = {GL_RGBA, GL_RGBA};
- ScalerStage* input_stage = final_stage;
- while (input_stage->input_stage()) {
- input_stage = input_stage->input_stage();
- input_stage->set_shader_program(GetShaderProgram(
- input_stage->shader(), intermediate_texture_type, nullptr, kNoSwizzle));
- }
- // From this point, |input_stage| points to the first ScalerStage (i.e., the
- // one that will be reading from the source).
-
- // If necessary, prepend an extra "import stage" that color-converts the input
- // before any scaling occurs. It's important not to merge color space
- // conversion of the source with any other steps because the texture sampler
- // must not linearly interpolate until after the colors have been mapped to a
- // linear color space.
- if (params_.source_color_space != scaling_color_space_) {
- input_stage->set_input_stage(std::make_unique<ScalerStage>(
- gl, Shader::BILINEAR, HORIZONTAL, input_stage->scale_from(),
- input_stage->scale_from()));
- input_stage = input_stage->input_stage();
- transform = gfx::ColorTransform::NewColorTransform(
- params_.source_color_space, scaling_color_space_);
- input_stage->set_shader_program(
- GetShaderProgram(input_stage->shader(), intermediate_texture_type,
- transform.get(), kNoSwizzle));
- }
-
- // If the source content is Y-flipped, the input scaler stage will perform
- // math to account for this. It also will flip the content during scaling so
- // that all following stages may assume the content is not flipped. Then, the
- // final stage must ensure the final output is correctly flipped-back (or not)
- // based on what the first stage did PLUS what is being requested by the
- // client code.
- if (params_.is_flipped_source) {
- input_stage->set_is_flipped_source(true);
- input_stage->set_flip_output(true);
- }
- if (input_stage->flip_output() != params_.flip_output) {
- final_stage->set_flip_output(!final_stage->flip_output());
- }
-
- chain_ = std::move(chain);
- VLOG(2) << __func__ << " built this: " << *this;
- return true;
-}
-
-bool GLScaler::ScaleToMultipleOutputs(GLuint src_texture,
- const gfx::Size& src_texture_size,
- const gfx::Vector2d& src_offset,
- GLuint dest_texture_0,
- GLuint dest_texture_1,
- const gfx::Rect& output_rect) {
- if (!chain_) {
- return false;
- }
-
- // Bind the vertex attributes used to sweep the entire source area when
- // executing the shader programs.
- GLES2Interface* const gl = context_provider_->ContextGL();
- DCHECK(gl);
- if (vertex_attributes_buffer_) {
- gl->BindBuffer(GL_ARRAY_BUFFER, vertex_attributes_buffer_);
- } else {
- gl->GenBuffers(1, &vertex_attributes_buffer_);
- gl->BindBuffer(GL_ARRAY_BUFFER, vertex_attributes_buffer_);
- gl->BufferData(GL_ARRAY_BUFFER, sizeof(ShaderProgram::kVertexAttributes),
- ShaderProgram::kVertexAttributes, GL_STATIC_DRAW);
- }
-
- // Disable GL clipping/blending features that interfere with assumptions made
- // by the implementation. Only those known to possibly be enabled elsewhere in
- // Chromium code are disabled here, while the remainder are sanity-DCHECK'ed.
- gl->Disable(GL_SCISSOR_TEST);
- gl->Disable(GL_STENCIL_TEST);
- gl->Disable(GL_BLEND);
- DCHECK_NE(gl->IsEnabled(GL_CULL_FACE), GL_TRUE);
- DCHECK_NE(gl->IsEnabled(GL_DEPTH_TEST), GL_TRUE);
- DCHECK_NE(gl->IsEnabled(GL_POLYGON_OFFSET_FILL), GL_TRUE);
- DCHECK_NE(gl->IsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE), GL_TRUE);
- DCHECK_NE(gl->IsEnabled(GL_SAMPLE_COVERAGE), GL_TRUE);
- DCHECK_NE(gl->IsEnabled(GL_SCISSOR_TEST), GL_TRUE);
- DCHECK_NE(gl->IsEnabled(GL_STENCIL_TEST), GL_TRUE);
-
- chain_->ScaleToMultipleOutputs(src_texture, src_texture_size, src_offset,
- dest_texture_0, dest_texture_1, output_rect);
-
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- return true;
-}
-
-// static
-bool GLScaler::ParametersHasSameScaleRatio(const GLScaler::Parameters& params,
- const gfx::Vector2d& from,
- const gfx::Vector2d& to) {
- // Returns true iff a_num/a_denom == b_num/b_denom.
- const auto AreRatiosEqual = [](int32_t a_num, int32_t a_denom, int32_t b_num,
- int32_t b_denom) -> bool {
- // The math (for each dimension):
- // If: a_num/a_denom == b_num/b_denom
- // Then: a_num*b_denom == b_num*a_denom
- //
- // ...and cast to int64_t to guarantee no overflow from the multiplications.
- return (static_cast<int64_t>(a_num) * b_denom) ==
- (static_cast<int64_t>(b_num) * a_denom);
- };
-
- return AreRatiosEqual(params.scale_from.x(), params.scale_to.x(), from.x(),
- to.x()) &&
- AreRatiosEqual(params.scale_from.y(), params.scale_to.y(), from.y(),
- to.y());
-}
-
-// static
-bool GLScaler::ParametersAreEquivalent(const Parameters& a,
- const Parameters& b) {
- if (!ParametersHasSameScaleRatio(a, b.scale_from, b.scale_to) ||
- a.enable_precise_color_management != b.enable_precise_color_management ||
- a.quality != b.quality || a.is_flipped_source != b.is_flipped_source ||
- a.flip_output != b.flip_output || a.export_format != b.export_format ||
- a.swizzle[0] != b.swizzle[0] || a.swizzle[1] != b.swizzle[1]) {
- return false;
- }
-
- const gfx::ColorSpace source_color_space_a =
- a.source_color_space.IsValid() ? a.source_color_space
- : gfx::ColorSpace::CreateSRGB();
- const gfx::ColorSpace source_color_space_b =
- b.source_color_space.IsValid() ? b.source_color_space
- : gfx::ColorSpace::CreateSRGB();
- if (source_color_space_a != source_color_space_b) {
- return false;
- }
-
- const gfx::ColorSpace output_color_space_a = a.output_color_space.IsValid()
- ? a.output_color_space
- : source_color_space_a;
- const gfx::ColorSpace output_color_space_b = b.output_color_space.IsValid()
- ? b.output_color_space
- : source_color_space_b;
- return output_color_space_a == output_color_space_b;
-}
-
-void GLScaler::OnContextLost() {
- // The destruction order here is important due to data dependencies.
- chain_.reset();
- shader_programs_.clear();
- if (vertex_attributes_buffer_) {
- if (auto* gl = context_provider_->ContextGL()) {
- gl->DeleteBuffers(1, &vertex_attributes_buffer_);
- }
- vertex_attributes_buffer_ = 0;
- }
- if (context_provider_) {
- context_provider_->RemoveObserver(this);
- context_provider_ = nullptr;
- }
-}
-
-GLScaler::ShaderProgram* GLScaler::GetShaderProgram(
- Shader shader,
- GLenum texture_type,
- const gfx::ColorTransform* color_transform,
- const GLenum swizzle[2]) {
- const ShaderCacheKey key{
- shader,
- texture_type,
- color_transform ? color_transform->GetSrcColorSpace() : gfx::ColorSpace(),
- color_transform ? color_transform->GetDstColorSpace() : gfx::ColorSpace(),
- swizzle[0],
- swizzle[1]};
- auto it = shader_programs_.find(key);
- if (it == shader_programs_.end()) {
- GLES2Interface* const gl = context_provider_->ContextGL();
- DCHECK(gl);
- it = shader_programs_
- .emplace(std::piecewise_construct, std::forward_as_tuple(key),
- std::forward_as_tuple(gl, shader, texture_type,
- color_transform, swizzle))
- .first;
- }
- return &it->second;
-}
-
-// static
-std::unique_ptr<GLScaler::ScalerStage> GLScaler::CreateAGoodScalingChain(
- gpu::gles2::GLES2Interface* gl,
- const gfx::Vector2d& scale_from,
- const gfx::Vector2d& scale_to) {
- DCHECK(scale_from.x() != 0 && scale_from.y() != 0)
- << "Bad scale_from: " << scale_from.ToString();
- DCHECK(scale_to.x() != 0 && scale_to.y() != 0)
- << "Bad scale_to: " << scale_to.ToString();
- DCHECK(scale_from != scale_to);
-
- // The GOOD quality chain performs one bilinear upscale followed by N bilinear
- // halvings, and does this is both directions. Exception: No upscale is needed
- // when |scale_from| is a power of two multiple of |scale_to|.
- //
- // Since all shaders use bilinear filtering, the heuristics below attempt to
- // greedily merge steps wherever possible to minimize GPU memory usage and
- // processing time. This also means that it will be extremely rare for the
- // stage doing the initial upscale to actually require a larger output texture
- // than the source texture (a downscale will be merged into the same stage).
-
- // Determine the initial upscaled-to size, as the minimum number of doublings
- // to make |scale_to| greater than |scale_from|.
- const RelativeSize from(scale_from);
- const RelativeSize to(scale_to);
- RelativeSize upscale_to = to;
- for (Axis x_or_y : std::array<Axis, 2>{HORIZONTAL, VERTICAL}) {
- while (upscale_to[x_or_y] < from[x_or_y]) {
- upscale_to[x_or_y] *= 2;
- }
- }
-
- // Create the stages in order from first-to-last, taking the greediest path
- // each time. Something like an A* algorithm would be better for discovering
- // an optimal sequence of operations, and would allow using the BILINEAR3
- // shader as well, but the run-time performance to compute the stages would be
- // too prohibitive.
- std::unique_ptr<ScalerStage> chain;
- struct CandidateOp {
- Shader shader;
- Axis primary_axis;
- RelativeSize output_size;
- };
- std::vector<CandidateOp> candidates;
- for (RelativeSize cur = from; cur != to;
- cur = RelativeSize(chain->scale_to())) {
- candidates.clear();
-
- // Determine whether it's possible to do exactly 2 bilinear passes in both
- // directions.
- RelativeSize output_size_2x2 = {0, 0};
- for (Axis x_or_y : std::array<Axis, 2>{VERTICAL, HORIZONTAL}) {
- if (cur[x_or_y] == from[x_or_y]) {
- // For the first stage, the 2 bilinear passes must be the initial
- // upscale followed by one downscale. If there is no initial upscale,
- // then the 2 passes must both be downscales.
- if (upscale_to[x_or_y] != from[x_or_y] &&
- upscale_to[x_or_y] / 2 >= to[x_or_y]) {
- output_size_2x2[x_or_y] = upscale_to[x_or_y] / 2;
- } else if (upscale_to[x_or_y] == from[x_or_y] &&
- upscale_to[x_or_y] / 4 >= to[x_or_y]) {
- output_size_2x2[x_or_y] = cur[x_or_y] / 4;
- }
- } else {
- // For all later stages, the 2 bilinear passes must be 2 halvings.
- if (cur[x_or_y] / 4 >= to[x_or_y]) {
- output_size_2x2[x_or_y] = cur[x_or_y] / 4;
- }
- }
- }
- if (output_size_2x2[HORIZONTAL] != 0 && output_size_2x2[VERTICAL] != 0) {
- candidates.push_back(
- CandidateOp{Shader::BILINEAR2X2, HORIZONTAL, output_size_2x2});
- }
-
- // Determine the valid set of Ops that do 1 to 3 bilinear passes in one
- // direction and 0 or 1 pass in the other direction.
- for (Axis x_or_y : std::array<Axis, 2>{VERTICAL, HORIZONTAL}) {
- // The first bilinear pass in x_or_y must be an upscale or a halving.
- Shader shader = Shader::BILINEAR;
- RelativeSize output_size = cur;
- if (cur[x_or_y] == from[x_or_y] && upscale_to[x_or_y] != from[x_or_y]) {
- output_size[x_or_y] = upscale_to[x_or_y];
- } else if (cur[x_or_y] / 2 >= to[x_or_y]) {
- output_size[x_or_y] /= 2;
- } else {
- DCHECK_EQ(cur[x_or_y], to[x_or_y]);
- continue;
- }
-
- // Determine whether 1 or 2 additional passes can be made in the same
- // direction.
- if (output_size[x_or_y] / 4 >= to[x_or_y]) {
- shader = Shader::BILINEAR4; // 2 more passes == 3 total.
- output_size[x_or_y] /= 4;
- } else if (output_size[x_or_y] / 2 >= to[x_or_y]) {
- shader = Shader::BILINEAR2; // 1 more pass == 2 total.
- output_size[x_or_y] /= 2;
- } else {
- DCHECK_EQ(output_size[x_or_y], to[x_or_y]);
- }
-
- // Determine whether 0 or 1 bilinear passes can be made in the other
- // direction at the same time.
- const Axis y_or_x = TheOtherAxis(x_or_y);
- if (cur[y_or_x] == from[y_or_x] && upscale_to[y_or_x] != from[y_or_x]) {
- output_size[y_or_x] = upscale_to[y_or_x];
- } else if (cur[y_or_x] / 2 >= to[y_or_x]) {
- output_size[y_or_x] /= 2;
- } else {
- DCHECK_EQ(cur[y_or_x], to[y_or_x]);
- }
-
- candidates.push_back(CandidateOp{shader, x_or_y, output_size});
- }
-
- // From the candidates, pick the one that produces the fewest number of
- // output pixels, and append a new ScalerStage. There are opportunities to
- // improve the "cost function" here (e.g., pixels in the Y direction
- // probably cost more to process than pixels in the X direction), but that
- // would require more research.
- const auto best_candidate = std::min_element(
- candidates.begin(), candidates.end(),
- [](const CandidateOp& a, const CandidateOp& b) {
- static_assert(sizeof(a.output_size[0]) <= sizeof(int32_t),
- "Overflow issue in the math here.");
- const int64_t cost_of_a =
- int64_t{a.output_size[HORIZONTAL]} * a.output_size[VERTICAL];
- const int64_t cost_of_b =
- int64_t{b.output_size[HORIZONTAL]} * b.output_size[VERTICAL];
- return cost_of_a < cost_of_b;
- });
- DCHECK(best_candidate != candidates.end());
- DCHECK(cur != best_candidate->output_size)
- << "Best candidate's output size (" << best_candidate->output_size
- << ") should not equal the input size.";
- auto next_stage = std::make_unique<ScalerStage>(
- gl, best_candidate->shader, best_candidate->primary_axis,
- cur.AsVector2d(), best_candidate->output_size.AsVector2d());
- next_stage->set_input_stage(std::move(chain));
- chain = std::move(next_stage);
- }
-
- return chain;
-}
-
-// static
-std::unique_ptr<GLScaler::ScalerStage> GLScaler::CreateTheBestScalingChain(
- gpu::gles2::GLES2Interface* gl,
- const gfx::Vector2d& scale_from,
- const gfx::Vector2d& scale_to) {
- // The BEST quality chain performs one bicubic upscale followed by N bicubic
- // halvings, and does this is both directions. Exception: No upscale is needed
- // when |scale_from| is a power of two multiple of |scale_to|.
-
- // Determine the initial upscaled-to size, as the minimum number of doublings
- // to make |scale_to| greater than |scale_from|.
- const RelativeSize from(scale_from);
- const RelativeSize to(scale_to);
- RelativeSize upscale_to = to;
- for (Axis x_or_y : std::array<Axis, 2>{HORIZONTAL, VERTICAL}) {
- while (upscale_to[x_or_y] < from[x_or_y]) {
- upscale_to[x_or_y] *= 2;
- }
- }
-
- // Create the stages in order from first-to-last.
- RelativeSize cur = from;
- std::unique_ptr<ScalerStage> chain;
- for (Axis x_or_y : std::array<Axis, 2>{VERTICAL, HORIZONTAL}) {
- if (upscale_to[x_or_y] != from[x_or_y]) {
- RelativeSize next = cur;
- next[x_or_y] = upscale_to[x_or_y];
- auto upscale_stage =
- std::make_unique<ScalerStage>(gl, Shader::BICUBIC_UPSCALE, x_or_y,
- cur.AsVector2d(), next.AsVector2d());
- upscale_stage->set_input_stage(std::move(chain));
- chain = std::move(upscale_stage);
- cur = next;
- }
- while (cur[x_or_y] > to[x_or_y]) {
- RelativeSize next = cur;
- next[x_or_y] /= 2;
- auto next_stage =
- std::make_unique<ScalerStage>(gl, Shader::BICUBIC_HALF_1D, x_or_y,
- cur.AsVector2d(), next.AsVector2d());
- next_stage->set_input_stage(std::move(chain));
- chain = std::move(next_stage);
- cur = next;
- }
- }
- DCHECK_EQ(cur, to);
-
- return chain;
-}
-
-// static
-std::unique_ptr<GLScaler::ScalerStage> GLScaler::MaybeAppendExportStage(
- gpu::gles2::GLES2Interface* gl,
- std::unique_ptr<GLScaler::ScalerStage> chain,
- GLScaler::Parameters::ExportFormat export_format) {
- DCHECK(chain);
-
- if (export_format == Parameters::ExportFormat::INTERLEAVED_QUADS) {
- return chain; // No format change.
- }
-
- // If the final stage uses the BILINEAR shader that is not upscaling, the
- // export stage can replace it with no change in the results. Otherwise, a
- // separate export stage will be appended.
- gfx::Vector2d scale_from = chain->scale_from();
- const gfx::Vector2d scale_to = chain->scale_to();
- if (chain->shader() == Shader::BILINEAR && scale_from.x() >= scale_to.x() &&
- scale_from.y() >= scale_to.y()) {
- chain = chain->take_input_stage();
- } else {
- scale_from = scale_to;
- }
-
- Shader shader = Shader::BILINEAR;
- scale_from.set_x(scale_from.x() * 4);
- switch (export_format) {
- case Parameters::ExportFormat::INTERLEAVED_QUADS:
- NOTREACHED();
- break;
- case Parameters::ExportFormat::CHANNEL_0:
- shader = Shader::PLANAR_CHANNEL_0;
- break;
- case Parameters::ExportFormat::CHANNEL_1:
- shader = Shader::PLANAR_CHANNEL_1;
- break;
- case Parameters::ExportFormat::CHANNEL_2:
- shader = Shader::PLANAR_CHANNEL_2;
- break;
- case Parameters::ExportFormat::CHANNEL_3:
- shader = Shader::PLANAR_CHANNEL_3;
- break;
- case Parameters::ExportFormat::NV61:
- shader = Shader::I422_NV61_MRT;
- break;
- case Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE:
- shader = Shader::DEINTERLEAVE_PAIRWISE_MRT;
- // Horizontal scale is only 0.5X, not 0.25X like all the others.
- scale_from.set_x(scale_from.x() / 2);
- break;
- case Parameters::ExportFormat::UV_CHANNELS:
- shader = Shader::PLANAR_CHANNELS_1_2;
- break;
- }
-
- auto export_stage = std::make_unique<ScalerStage>(gl, shader, HORIZONTAL,
- scale_from, scale_to);
- export_stage->set_input_stage(std::move(chain));
- return export_stage;
-}
-
-// static
-GLScaler::Axis GLScaler::TheOtherAxis(GLScaler::Axis x_or_y) {
- return x_or_y == HORIZONTAL ? VERTICAL : HORIZONTAL;
-}
-
-// static
-const char* GLScaler::GetShaderName(GLScaler::Shader shader) {
- switch (shader) {
-#define CASE_RETURN_SHADER_STR(x) \
- case Shader::x: \
- return #x
- CASE_RETURN_SHADER_STR(BILINEAR);
- CASE_RETURN_SHADER_STR(BILINEAR2);
- CASE_RETURN_SHADER_STR(BILINEAR3);
- CASE_RETURN_SHADER_STR(BILINEAR4);
- CASE_RETURN_SHADER_STR(BILINEAR2X2);
- CASE_RETURN_SHADER_STR(BICUBIC_UPSCALE);
- CASE_RETURN_SHADER_STR(BICUBIC_HALF_1D);
- CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_0);
- CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_1);
- CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_2);
- CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_3);
- CASE_RETURN_SHADER_STR(I422_NV61_MRT);
- CASE_RETURN_SHADER_STR(DEINTERLEAVE_PAIRWISE_MRT);
- CASE_RETURN_SHADER_STR(PLANAR_CHANNELS_1_2);
-#undef CASE_RETURN_SHADER_STR
- }
-}
-
-// static
-bool GLScaler::AreAllGLExtensionsPresent(
- gpu::gles2::GLES2Interface* gl,
- const std::vector<std::string>& names) {
- DCHECK(gl);
- if (const auto* extensions = gl->GetString(GL_EXTENSIONS)) {
- const std::string extensions_string =
- " " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
- for (const std::string& name : names) {
- if (extensions_string.find(" " + name + " ") == std::string::npos) {
- return false;
- }
- }
- return true;
- }
- return false;
-}
-
-GLScaler::Parameters::Parameters() = default;
-GLScaler::Parameters::Parameters(const Parameters& other) = default;
-GLScaler::Parameters::~Parameters() = default;
-
-// static
-const GLfloat GLScaler::ShaderProgram::kVertexAttributes[16] = {
- -1.0f, -1.0f, 0.0f, 0.0f, // vertex 0
- 1.0f, -1.0f, 1.0f, 0.0f, // vertex 1
- -1.0f, 1.0f, 0.0f, 1.0f, // vertex 2
- 1.0f, 1.0f, 1.0f, 1.0f, // vertex 3
-};
-
-GLScaler::ShaderProgram::ShaderProgram(
- gpu::gles2::GLES2Interface* gl,
- GLScaler::Shader shader,
- GLenum texture_type,
- const gfx::ColorTransform* color_transform,
- const GLenum swizzle[2])
- : gl_(gl),
- shader_(shader),
- texture_type_(texture_type),
- program_(gl_->CreateProgram()) {
- DCHECK(program_);
-
- std::basic_ostringstream<GLchar> vertex_header;
- std::basic_ostringstream<GLchar> fragment_directives;
- std::basic_ostringstream<GLchar> fragment_header;
- std::basic_ostringstream<GLchar> shared_variables;
- std::basic_ostringstream<GLchar> vertex_main;
- std::basic_ostringstream<GLchar> fragment_main;
-
- vertex_header
- << ("precision highp float;\n"
- "attribute vec2 a_position;\n"
- "attribute vec2 a_texcoord;\n"
- "uniform vec4 src_rect;\n");
-
- fragment_header << "precision mediump float;\n";
- switch (texture_type_) {
- case GL_FLOAT:
- fragment_header << "precision highp sampler2D;\n";
- break;
- case GL_HALF_FLOAT_OES:
- fragment_header << "precision mediump sampler2D;\n";
- break;
- default:
- fragment_header << "precision lowp sampler2D;\n";
- break;
- }
- fragment_header << "uniform sampler2D s_texture;\n";
-
- if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) {
- const std::string& source = color_transform->GetShaderSource();
- // Assumption: gfx::ColorTransform::GetShaderSource() should provide a
- // function named DoColorConversion() that takes a vec3 argument and returns
- // a vec3.
- DCHECK_NE(source.find("DoColorConversion"), std::string::npos);
- fragment_header << source;
- }
-
- vertex_main
- << (" gl_Position = vec4(a_position, 0.0, 1.0);\n"
- " vec2 texcoord = src_rect.xy + a_texcoord * src_rect.zw;\n");
-
- switch (shader_) {
- case Shader::BILINEAR:
- shared_variables << "varying highp vec2 v_texcoord;\n";
- vertex_main << " v_texcoord = texcoord;\n";
- fragment_main << " vec4 sample = texture2D(s_texture, v_texcoord);\n";
- if (color_transform) {
- fragment_main << " sample.rgb = DoColorConversion(sample.rgb);\n";
- }
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " sample.rb = sample.br;\n";
- }
- fragment_main << " gl_FragColor = sample;\n";
- break;
-
- case Shader::BILINEAR2:
- // This is equivialent to two passes of the BILINEAR shader above. It can
- // be used to scale an image down 1.0x-2.0x in either dimension, or
- // exactly 4x.
- shared_variables << "varying highp vec4 v_texcoords;\n";
- vertex_header << "uniform vec2 scaling_vector;\n";
- vertex_main
- << (" vec2 step = scaling_vector / 4.0;\n"
- " v_texcoords.xy = texcoord + step;\n"
- " v_texcoords.zw = texcoord - step;\n");
- fragment_main
- << (" vec4 blended = (texture2D(s_texture, v_texcoords.xy) +\n"
- " texture2D(s_texture, v_texcoords.zw)) /\n"
- " 2.0;\n");
- if (color_transform) {
- fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
- }
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " blended.rb = blended.br;\n";
- }
- fragment_main << " gl_FragColor = blended;\n";
- break;
-
- case Shader::BILINEAR3:
- // This is kind of like doing 1.5 passes of the BILINEAR shader. It can be
- // used to scale an image down 1.5x-3.0x, or exactly 6x.
- shared_variables
- << ("varying highp vec4 v_texcoords0;\n"
- "varying highp vec2 v_texcoords1;\n");
- vertex_header << "uniform vec2 scaling_vector;\n";
- vertex_main
- << (" vec2 step = scaling_vector / 3.0;\n"
- " v_texcoords0.xy = texcoord + step;\n"
- " v_texcoords0.zw = texcoord;\n"
- " v_texcoords1 = texcoord - step;\n");
- fragment_main
- << (" vec4 blended = (texture2D(s_texture, v_texcoords0.xy) +\n"
- " texture2D(s_texture, v_texcoords0.zw) +\n"
- " texture2D(s_texture, v_texcoords1)) / 3.0;\n");
- if (color_transform) {
- fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
- }
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " blended.rb = blended.br;\n";
- }
- fragment_main << " gl_FragColor = blended;\n";
- break;
-
- case Shader::BILINEAR4:
- // This is equivialent to three passes of the BILINEAR shader above. It
- // can be used to scale an image down 2.0x-4.0x or exactly 8x.
- shared_variables << "varying highp vec4 v_texcoords[2];\n";
- vertex_header << "uniform vec2 scaling_vector;\n";
- vertex_main
- << (" vec2 step = scaling_vector / 8.0;\n"
- " v_texcoords[0].xy = texcoord - step * 3.0;\n"
- " v_texcoords[0].zw = texcoord - step;\n"
- " v_texcoords[1].xy = texcoord + step;\n"
- " v_texcoords[1].zw = texcoord + step * 3.0;\n");
- fragment_main
- << (" vec4 blended = (\n"
- " texture2D(s_texture, v_texcoords[0].xy) +\n"
- " texture2D(s_texture, v_texcoords[0].zw) +\n"
- " texture2D(s_texture, v_texcoords[1].xy) +\n"
- " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
- if (color_transform) {
- fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
- }
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " blended.rb = blended.br;\n";
- }
- fragment_main << " gl_FragColor = blended;\n";
- break;
-
- case Shader::BILINEAR2X2:
- // This is equivialent to four passes of the BILINEAR shader above, two in
- // each dimension. It can be used to scale an image down 1.0x-2.0x in both
- // X and Y directions. Or, it could be used to scale an image down by
- // exactly 4x in both dimensions.
- shared_variables << "varying highp vec4 v_texcoords[2];\n";
- vertex_header << "uniform vec2 scaling_vector;\n";
- vertex_main
- << (" vec2 step = scaling_vector / 4.0;\n"
- " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
- " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
- " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
- " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n");
- fragment_main
- << (" vec4 blended = (\n"
- " texture2D(s_texture, v_texcoords[0].xy) +\n"
- " texture2D(s_texture, v_texcoords[0].zw) +\n"
- " texture2D(s_texture, v_texcoords[1].xy) +\n"
- " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
- if (color_transform) {
- fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
- }
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " blended.rb = blended.br;\n";
- }
- fragment_main << " gl_FragColor = blended;\n";
- break;
-
- case Shader::BICUBIC_UPSCALE:
- // When scaling up, 4 texture reads are necessary. However, some
- // instructions can be saved because the parameter passed to the bicubic
- // function will be in a known range. Also, when sampling the bicubic
- // function like this, the sum is always exactly one, so normalization can
- // be skipped as well.
- shared_variables << "varying highp vec2 v_texcoord;\n";
- vertex_main << " v_texcoord = texcoord;\n";
- fragment_header
- << ("uniform highp vec2 src_pixelsize;\n"
- "uniform highp vec2 scaling_vector;\n"
- "const float a = -0.5;\n"
- // This function is equivialent to calling the bicubic
- // function with x-1, x, 1-x and 2-x (assuming
- // 0 <= x < 1). The following is the Catmull-Rom spline.
- // See: http://wikipedia.org/wiki/Cubic_Hermite_spline
- "vec4 filt4(float x) {\n"
- " return vec4(x * x * x, x * x, x, 1) *\n"
- " mat4( a, -2.0 * a, a, 0.0,\n"
- " a + 2.0, -a - 3.0, 0.0, 1.0,\n"
- " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n"
- " -a, a, 0.0, 0.0);\n"
- "}\n"
- "mat4 pixels_x(highp vec2 pos, highp vec2 step) {\n"
- " return mat4(texture2D(s_texture, pos - step),\n"
- " texture2D(s_texture, pos),\n"
- " texture2D(s_texture, pos + step),\n"
- " texture2D(s_texture, pos + step * 2.0));\n"
- "}\n");
- fragment_main
- << (" highp vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
- " scaling_vector / 2.0;\n"
- " highp float frac = fract(dot(pixel_pos, scaling_vector));\n"
- " highp vec2 base = \n"
- " (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
- " highp vec2 step = scaling_vector / src_pixelsize;\n"
- " vec4 blended = pixels_x(base, step) * filt4(frac);\n");
- if (color_transform) {
- fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
- }
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " blended.rb = blended.br;\n";
- }
- fragment_main << " gl_FragColor = blended;\n";
- break;
-
- case Shader::BICUBIC_HALF_1D:
- // This scales down an image by exactly half in one dimension. The
- // bilinear lookup reduces the number of texture reads from 8 to 4.
- shared_variables << "varying highp vec4 v_texcoords[2];\n";
- vertex_header
- << ("uniform vec2 scaling_vector;\n"
- "const float CenterDist = 99.0 / 140.0;\n"
- "const float LobeDist = 11.0 / 4.0;\n");
- vertex_main
- << (" vec2 step = scaling_vector / 2.0;\n"
- " v_texcoords[0].xy = texcoord - LobeDist * step;\n"
- " v_texcoords[0].zw = texcoord - CenterDist * step;\n"
- " v_texcoords[1].xy = texcoord + CenterDist * step;\n"
- " v_texcoords[1].zw = texcoord + LobeDist * step;\n");
- fragment_header
- << ("const float CenterWeight = 35.0 / 64.0;\n"
- "const float LobeWeight = -3.0 / 64.0;\n");
- fragment_main
- << (" vec4 blended = \n"
- // Lobe pixels
- " (texture2D(s_texture, v_texcoords[0].xy) +\n"
- " texture2D(s_texture, v_texcoords[1].zw)) *\n"
- " LobeWeight +\n"
- // Center pixels
- " (texture2D(s_texture, v_texcoords[0].zw) +\n"
- " texture2D(s_texture, v_texcoords[1].xy)) *\n"
- " CenterWeight;\n");
- if (color_transform) {
- fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
- }
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " blended.rb = blended.br;\n";
- }
- fragment_main << " gl_FragColor = blended;\n";
- break;
-
- case Shader::PLANAR_CHANNEL_0:
- case Shader::PLANAR_CHANNEL_1:
- case Shader::PLANAR_CHANNEL_2:
- case Shader::PLANAR_CHANNEL_3: {
- // Select one color channel, and pack 4 pixels into one output quad. See
- // header file for diagram.
- shared_variables << "varying highp vec4 v_texcoords[2];\n";
- vertex_header << "uniform vec2 scaling_vector;\n";
- vertex_main
- << (" vec2 step = scaling_vector / 4.0;\n"
- " v_texcoords[0].xy = texcoord - step * 1.5;\n"
- " v_texcoords[0].zw = texcoord - step * 0.5;\n"
- " v_texcoords[1].xy = texcoord + step * 0.5;\n"
- " v_texcoords[1].zw = texcoord + step * 1.5;\n");
- std::basic_string<GLchar> convert_open;
- std::basic_string<GLchar> convert_close;
- if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) {
- convert_open = "DoColorConversion(";
- convert_close = ".rgb)";
- }
- const char selector = "rgba"[static_cast<int>(shader_) -
- static_cast<int>(Shader::PLANAR_CHANNEL_0)];
- fragment_main << " vec4 packed_quad = vec4(\n"
- << " " << convert_open
- << "texture2D(s_texture, v_texcoords[0].xy)"
- << convert_close << '.' << selector << ",\n"
- << " " << convert_open
- << "texture2D(s_texture, v_texcoords[0].zw)"
- << convert_close << '.' << selector << ",\n"
- << " " << convert_open
- << "texture2D(s_texture, v_texcoords[1].xy)"
- << convert_close << '.' << selector << ",\n"
- << " " << convert_open
- << "texture2D(s_texture, v_texcoords[1].zw)"
- << convert_close << '.' << selector << ");\n";
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " packed_quad.rb = packed_quad.br;\n";
- }
- fragment_main << " gl_FragColor = packed_quad;\n";
- break;
- }
-
- case Shader::I422_NV61_MRT:
- // I422 sampling, delivered via two output textures (NV61 format). See
- // header file for diagram.
- shared_variables << "varying highp vec4 v_texcoords[2];\n";
- vertex_header << "uniform vec2 scaling_vector;\n";
- vertex_main
- << (" vec2 step = scaling_vector / 4.0;\n"
- " v_texcoords[0].xy = texcoord - step * 1.5;\n"
- " v_texcoords[0].zw = texcoord - step * 0.5;\n"
- " v_texcoords[1].xy = texcoord + step * 0.5;\n"
- " v_texcoords[1].zw = texcoord + step * 1.5;\n");
- fragment_directives << "#extension GL_EXT_draw_buffers : enable\n";
- fragment_main
- << (" vec3 pixel0 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
- " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
- " vec3 pixel01 = (pixel0 + pixel1) / 2.0;\n"
- " vec3 pixel2 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
- " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
- " vec3 pixel23 = (pixel2 + pixel3) / 2.0;\n");
- if (color_transform) {
- fragment_main
- << (" pixel0 = DoColorConversion(pixel0);\n"
- " pixel1 = DoColorConversion(pixel1);\n"
- " pixel01 = DoColorConversion(pixel01);\n"
- " pixel2 = DoColorConversion(pixel2);\n"
- " pixel3 = DoColorConversion(pixel3);\n"
- " pixel23 = DoColorConversion(pixel23);\n");
- }
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main
- << (" gl_FragData[0] = \n"
- " vec4(pixel2.r, pixel1.r, pixel0.r, pixel3.r);\n");
- } else {
- fragment_main
- << (" gl_FragData[0] = \n"
- " vec4(pixel0.r, pixel1.r, pixel2.r, pixel3.r);\n");
- }
- if (swizzle[1] == GL_BGRA_EXT) {
- fragment_main
- << (" gl_FragData[1] = \n"
- " vec4(pixel23.g, pixel01.b, pixel01.g, pixel23.b);\n");
- } else {
- fragment_main
- << (" gl_FragData[1] = \n"
- " vec4(pixel01.g, pixel01.b, pixel23.g, pixel23.b);\n");
- }
- break;
-
- case Shader::DEINTERLEAVE_PAIRWISE_MRT:
- // Sample two pixels and unswizzle them. There's no need to do vertical
- // scaling with math, since the bilinear interpolation in the sampler
- // takes care of that.
- shared_variables << "varying highp vec4 v_texcoords;\n";
- vertex_header << "uniform vec2 scaling_vector;\n";
- vertex_main
- << (" vec2 step = scaling_vector / 2.0;\n"
- " v_texcoords.xy = texcoord - step * 0.5;\n"
- " v_texcoords.zw = texcoord + step * 0.5;\n");
- fragment_directives << "#extension GL_EXT_draw_buffers : enable\n";
- DCHECK(!color_transform);
- fragment_main
- << (" vec4 lo_uvuv = texture2D(s_texture, v_texcoords.xy);\n"
- " vec4 hi_uvuv = texture2D(s_texture, v_texcoords.zw);\n"
- " vec4 uuuu = vec4(lo_uvuv.rb, hi_uvuv.rb);\n"
- " vec4 vvvv = vec4(lo_uvuv.ga, hi_uvuv.ga);\n");
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " uuuu.rb = uuuu.br;\n";
- }
- fragment_main << " gl_FragData[0] = uuuu;\n";
- if (swizzle[1] == GL_BGRA_EXT) {
- fragment_main << " vvvv.rb = vvvv.br;\n";
- }
- fragment_main << " gl_FragData[1] = vvvv;\n";
- break;
-
- case Shader::PLANAR_CHANNELS_1_2: {
- // Select two color channels, and pack 2 pairs of pixels into one output
- // quad. See header file for diagram.
- // This shader performs the same work that is being done by
- // Shader::I422_NV61_MRT (see above) for its second render target.
- shared_variables << "varying highp vec4 v_texcoords[2];\n";
- vertex_header << "uniform vec2 scaling_vector;\n";
- vertex_main
- << (" vec2 step = scaling_vector / 4.0;\n"
- " v_texcoords[0].xy = texcoord - step * 1.5;\n"
- " v_texcoords[0].zw = texcoord - step * 0.5;\n"
- " v_texcoords[1].xy = texcoord + step * 0.5;\n"
- " v_texcoords[1].zw = texcoord + step * 1.5;\n");
- fragment_main
- << (" vec3 pixel0 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
- " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
- " vec3 pixel01 = (pixel0 + pixel1) / 2.0;\n"
- " vec3 pixel2 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
- " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
- " vec3 pixel23 = (pixel2 + pixel3) / 2.0;\n");
-
- if (color_transform) {
- fragment_main
- << (" pixel01 = DoColorConversion(pixel01);\n"
- " pixel23 = DoColorConversion(pixel23);\n");
- }
-
- fragment_main
- << (" vec4 packed_quad = vec4(\n"
- " pixel01.g, pixel01.b, pixel23.g, pixel23.b);\n");
-
- if (swizzle[0] == GL_BGRA_EXT) {
- fragment_main << " packed_quad.rb = packed_quad.br;\n";
- }
-
- fragment_main << " gl_FragColor = packed_quad;\n";
- break;
- }
- }
-
- // Helper function to compile the shader source and log the GLSL compiler's
- // results.
- const char* shader_name = GLScaler::GetShaderName(shader_);
- const auto CompileShaderFromSource =
- [shader_name](GLES2Interface* gl, const std::basic_string<GLchar>& source,
- GLenum type) -> GLuint {
- VLOG(2) << __func__ << ": Compiling shader " << type
- << " with source:" << std::endl
- << source << ", for GLScaler::Shader=" << shader_name;
- const GLuint shader = gl->CreateShader(type);
- const GLchar* source_data = source.data();
- const GLint length = base::checked_cast<GLint>(source.size());
- gl->ShaderSource(shader, 1, &source_data, &length);
- gl->CompileShader(shader);
- GLint compile_status = GL_FALSE;
- gl->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
-
- // Fetch logs and forward them to the system logger. If compilation failed,
- // clean-up and return 0 for error.
- if (compile_status != GL_TRUE || VLOG_IS_ON(2)) {
- GLint log_length = 0;
- gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
- std::string log;
- if (log_length > 0) {
- std::unique_ptr<GLchar[]> tmp(new GLchar[log_length]);
- GLsizei returned_log_length = 0;
- gl->GetShaderInfoLog(shader, log_length, &returned_log_length,
- tmp.get());
- log.assign(tmp.get(), returned_log_length);
- }
- if (log.empty()) {
- log = "<<NO LOG>>";
- }
- if (compile_status != GL_TRUE) {
- LOG(ERROR) << __func__ << ": Compilation of shader " << type
- << " failed:" << std::endl
- << log;
- gl->DeleteShader(shader);
- return 0;
- }
- VLOG(2) << __func__ << ": Compilation of shader " << type
- << " succeeded:" << std::endl
- << log;
- }
- return shader;
- };
-
- // Compile the vertex shader and attach it to the program.
- const std::string shared_variables_str = shared_variables.str();
- const GLuint vertex_shader =
- CompileShaderFromSource(gl_,
- vertex_header.str() + shared_variables_str +
- "void main() {\n" + vertex_main.str() + "}\n",
- GL_VERTEX_SHADER);
- if (vertex_shader == 0) {
- return;
- }
- gl_->AttachShader(program_, vertex_shader);
- gl_->DeleteShader(vertex_shader);
-
- // Compile the fragment shader and attach to |program_|.
- const GLuint fragment_shader = CompileShaderFromSource(
- gl_,
- fragment_directives.str() + fragment_header.str() + shared_variables_str +
- "void main() {\n" + fragment_main.str() + "}\n",
- GL_FRAGMENT_SHADER);
- if (fragment_shader == 0) {
- return;
- }
- gl_->AttachShader(program_, fragment_shader);
- gl_->DeleteShader(fragment_shader);
-
- // Link the program.
- gl_->LinkProgram(program_);
- GLint link_status = GL_FALSE;
- gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
- if (link_status != GL_TRUE) {
- LOG(ERROR) << "Failed to link shader program.";
- return;
- }
-
-#define DCHECK_RESOLVED_LOCATION(member) \
- DCHECK(member != -1 || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) \
- << "Failed to get " #member " in program, or GPU process crashed."
-
- // Resolve the locations of the global variables.
- position_location_ = gl_->GetAttribLocation(program_, "a_position");
- DCHECK_RESOLVED_LOCATION(position_location_);
- texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord");
- DCHECK_RESOLVED_LOCATION(texcoord_location_);
- texture_location_ = gl_->GetUniformLocation(program_, "s_texture");
- DCHECK_RESOLVED_LOCATION(texture_location_);
- src_rect_location_ = gl_->GetUniformLocation(program_, "src_rect");
- DCHECK_RESOLVED_LOCATION(src_rect_location_);
- switch (shader_) {
- case Shader::BILINEAR:
- break;
-
- case Shader::BILINEAR2:
- case Shader::BILINEAR3:
- case Shader::BILINEAR4:
- case Shader::BILINEAR2X2:
- case Shader::BICUBIC_HALF_1D:
- case Shader::PLANAR_CHANNEL_0:
- case Shader::PLANAR_CHANNEL_1:
- case Shader::PLANAR_CHANNEL_2:
- case Shader::PLANAR_CHANNEL_3:
- case Shader::I422_NV61_MRT:
- case Shader::DEINTERLEAVE_PAIRWISE_MRT:
- case Shader::PLANAR_CHANNELS_1_2:
- scaling_vector_location_ =
- gl_->GetUniformLocation(program_, "scaling_vector");
- DCHECK_RESOLVED_LOCATION(scaling_vector_location_);
- break;
-
- case Shader::BICUBIC_UPSCALE:
- src_pixelsize_location_ =
- gl_->GetUniformLocation(program_, "src_pixelsize");
- DCHECK_RESOLVED_LOCATION(src_pixelsize_location_);
- scaling_vector_location_ =
- gl_->GetUniformLocation(program_, "scaling_vector");
- DCHECK_RESOLVED_LOCATION(scaling_vector_location_);
- break;
- }
-
-#undef DCHECK_RESOLVED_LOCATION
-}
-
-GLScaler::ShaderProgram::~ShaderProgram() {
- gl_->DeleteProgram(program_);
-}
-
-void GLScaler::ShaderProgram::UseProgram(const gfx::Size& src_texture_size,
- const gfx::RectF& src_rect,
- const gfx::Size& dst_size,
- GLScaler::Axis primary_axis,
- bool flip_y) {
- gl_->UseProgram(program_);
-
- // OpenGL defines the last parameter to VertexAttribPointer as type
- // "const GLvoid*" even though it is actually an offset into the buffer
- // object's data store and not a pointer to the client's address space.
- const void* offsets[2] = {nullptr,
- reinterpret_cast<const void*>(2 * sizeof(GLfloat))};
-
- gl_->VertexAttribPointer(position_location_, 2, GL_FLOAT, GL_FALSE,
- 4 * sizeof(GLfloat), offsets[0]);
- gl_->EnableVertexAttribArray(position_location_);
-
- gl_->VertexAttribPointer(texcoord_location_, 2, GL_FLOAT, GL_FALSE,
- 4 * sizeof(GLfloat), offsets[1]);
- gl_->EnableVertexAttribArray(texcoord_location_);
-
- // Always sample from the first texture unit.
- gl_->Uniform1i(texture_location_, 0);
-
- // Convert |src_rect| from pixel coordinates to texture coordinates. The
- // source texture coordinates are in the range [0.0,1.0] for each dimension,
- // but the sampling rect may slightly "spill" outside that range (e.g., for
- // scaler overscan).
- GLfloat src_rect_texcoord[4] = {
- src_rect.x() / src_texture_size.width(),
- src_rect.y() / src_texture_size.height(),
- src_rect.width() / src_texture_size.width(),
- src_rect.height() / src_texture_size.height(),
- };
- if (flip_y) {
- src_rect_texcoord[1] += src_rect_texcoord[3];
- src_rect_texcoord[3] *= -1.0f;
- }
- gl_->Uniform4fv(src_rect_location_, 1, src_rect_texcoord);
-
- // Set shader-specific uniform inputs. The |scaling_vector| is the ratio of
- // the number of source pixels sampled per dest pixels output. It is used by
- // the shader programs to locate distinct texels from the source texture, and
- // sample them at the appropriate offset to produce each output texel.
- switch (shader_) {
- case Shader::BILINEAR:
- break;
-
- case Shader::BILINEAR2:
- case Shader::BILINEAR3:
- case Shader::BILINEAR4:
- case Shader::BICUBIC_HALF_1D:
- case Shader::PLANAR_CHANNEL_0:
- case Shader::PLANAR_CHANNEL_1:
- case Shader::PLANAR_CHANNEL_2:
- case Shader::PLANAR_CHANNEL_3:
- case Shader::I422_NV61_MRT:
- case Shader::DEINTERLEAVE_PAIRWISE_MRT:
- case Shader::PLANAR_CHANNELS_1_2:
- switch (primary_axis) {
- case HORIZONTAL:
- gl_->Uniform2f(scaling_vector_location_,
- src_rect_texcoord[2] / dst_size.width(), 0.0);
- break;
- case VERTICAL:
- gl_->Uniform2f(scaling_vector_location_, 0.0,
- src_rect_texcoord[3] / dst_size.height());
- break;
- }
- break;
-
- case Shader::BILINEAR2X2:
- gl_->Uniform2f(scaling_vector_location_,
- src_rect_texcoord[2] / dst_size.width(),
- src_rect_texcoord[3] / dst_size.height());
- break;
-
- case Shader::BICUBIC_UPSCALE:
- gl_->Uniform2f(src_pixelsize_location_, src_texture_size.width(),
- src_texture_size.height());
- // For this shader program, the |scaling_vector| has an alternate meaning:
- // It is only being used to select whether bicubic sampling is stepped in
- // the X or the Y direction.
- gl_->Uniform2f(scaling_vector_location_,
- primary_axis == HORIZONTAL ? 1.0 : 0.0,
- primary_axis == VERTICAL ? 1.0 : 0.0);
- break;
- }
-}
-
-GLScaler::ScalerStage::ScalerStage(gpu::gles2::GLES2Interface* gl,
- GLScaler::Shader shader,
- GLScaler::Axis primary_axis,
- const gfx::Vector2d& scale_from,
- const gfx::Vector2d& scale_to)
- : gl_(gl),
- shader_(shader),
- primary_axis_(primary_axis),
- scale_from_(scale_from),
- scale_to_(scale_to) {
- DCHECK(gl_);
-}
-
-GLScaler::ScalerStage::~ScalerStage() {
- if (dest_framebuffer_) {
- gl_->DeleteFramebuffers(1, &dest_framebuffer_);
- }
- if (intermediate_texture_) {
- gl_->DeleteTextures(1, &intermediate_texture_);
- }
-}
-
-void GLScaler::ScalerStage::ScaleToMultipleOutputs(
- GLuint src_texture,
- gfx::Size src_texture_size,
- const gfx::Vector2d& src_offset,
- GLuint dest_texture_0,
- GLuint dest_texture_1,
- const gfx::Rect& output_rect) {
- if (output_rect.IsEmpty())
- return; // No work to do.
-
- // Make a recursive call to the "input" ScalerStage to produce an intermediate
- // texture for this stage to source from. Adjust src_* variables to use the
- // intermediate texture as input.
- //
- // If there is no input stage, simply modify |src_rect| to account for the
- // overall |src_offset| and Y-flip.
- gfx::RectF src_rect = ToSourceRect(output_rect);
- if (input_stage_) {
- const gfx::Rect input_rect = ToInputRect(src_rect);
- EnsureIntermediateTextureDefined(input_rect.size());
- input_stage_->ScaleToMultipleOutputs(src_texture, src_texture_size,
- src_offset, intermediate_texture_, 0,
- input_rect);
- src_texture = intermediate_texture_;
- src_texture_size = intermediate_texture_size_;
- DCHECK(!is_flipped_source_);
- src_rect -= input_rect.OffsetFromOrigin();
- } else {
- if (is_flipped_source_) {
- src_rect.set_x(src_rect.x() + src_offset.x());
- src_rect.set_y(src_texture_size.height() - src_rect.bottom() -
- src_offset.y());
- } else {
- src_rect += src_offset;
- }
- }
-
- // Attach the output texture(s) to the framebuffer.
- if (!dest_framebuffer_) {
- gl_->GenFramebuffers(1, &dest_framebuffer_);
- }
- gl_->BindFramebuffer(GL_FRAMEBUFFER, dest_framebuffer_);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- dest_texture_0, 0);
- if (dest_texture_1 > 0) {
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 1,
- GL_TEXTURE_2D, dest_texture_1, 0);
- }
-
- // Bind to the source texture and set the texture sampler to use bilinear
- // filtering and clamp-to-edge, as required by all shader programs.
- //
- // It would be better to stash the existing parameter values, and restore them
- // back later. However, glGetTexParameteriv() currently requires a blocking
- // call to the GPU service, which is extremely costly performance-wise.
- gl_->ActiveTexture(GL_TEXTURE0);
- gl_->BindTexture(GL_TEXTURE_2D, src_texture);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- // Prepare the shader program for drawing.
- DCHECK(program_);
- program_->UseProgram(src_texture_size, src_rect, output_rect.size(),
- primary_axis_, flip_output_);
-
- // Execute the draw.
- gl_->Viewport(0, 0, output_rect.width(), output_rect.height());
- const GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0 + 1};
- if (dest_texture_1 > 0) {
- gl_->DrawBuffersEXT(2, buffers);
- }
- gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- if (dest_texture_1 > 0) {
- // Set the draw buffers back, to not disrupt external operations.
- gl_->DrawBuffersEXT(1, buffers);
- }
-
- gl_->BindTexture(GL_TEXTURE_2D, 0);
- gl_->BindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-gfx::RectF GLScaler::ScalerStage::ToSourceRect(
- const gfx::Rect& output_rect) const {
- return gfx::ScaleRect(gfx::RectF(output_rect),
- static_cast<float>(scale_from_.x()) / scale_to_.x(),
- static_cast<float>(scale_from_.y()) / scale_to_.y());
-}
-
-gfx::Rect GLScaler::ScalerStage::ToInputRect(gfx::RectF source_rect) const {
- int overscan_x = 0;
- int overscan_y = 0;
- switch (shader_) {
- case Shader::BILINEAR:
- case Shader::BILINEAR2:
- case Shader::BILINEAR3:
- case Shader::BILINEAR4: {
- // These shaders sample 1 or more points along the primary axis, and only
- // 1 point in the other direction, in order to produce each output pixel.
- // The amount of overscan is always 0 or 1 pixel along the primary axis,
- // and this can be determined by looking at the upper-left-most source
- // texture sampling point: If this point is to the left of the middle of
- // the upper-left-most source pixel, the texture sampler will also read
- // the pixel to the left of that (for linear interpolation). Similar
- // behavior can occur towards the right, upwards, and downwards at the
- // source boundaries.
- int threshold;
- switch (shader_) {
- default:
- threshold = 1;
- break;
- case Shader::BILINEAR2:
- threshold = 2;
- break;
- case Shader::BILINEAR3:
- threshold = 3;
- break;
- case Shader::BILINEAR4:
- threshold = 4;
- break;
- }
- switch (primary_axis_) {
- case HORIZONTAL:
- if (scale_from_.x() < threshold * scale_to_.x()) {
- overscan_x = 1;
- }
- if (scale_from_.y() < scale_to_.y()) {
- overscan_y = 1;
- }
- break;
- case VERTICAL:
- if (scale_from_.x() < scale_to_.x()) {
- overscan_x = 1;
- }
- if (scale_from_.y() < threshold * scale_to_.y()) {
- overscan_y = 1;
- }
- break;
- }
- break;
- }
-
- case Shader::BILINEAR2X2:
- // This shader samples 2 points along both axes, and the overscan is 0 or
- // 1 pixel in both directions (same explanation as for the other BILINEAR
- // shaders).
- if (scale_from_.x() < 2 * scale_to_.x()) {
- overscan_x = 1;
- }
- if (scale_from_.y() < 2 * scale_to_.y()) {
- overscan_y = 1;
- }
- break;
-
- case Shader::BICUBIC_UPSCALE:
- // For each output pixel, this shader always reads 2 pixels about the
- // source position in one dimension, and has no overscan in the other
- // dimension.
- if (scale_from_.x() < scale_to_.x()) {
- DCHECK_EQ(HORIZONTAL, primary_axis_);
- overscan_x = 2;
- } else if (scale_from_.y() < scale_to_.y()) {
- DCHECK_EQ(VERTICAL, primary_axis_);
- overscan_y = 2;
- } else if (scale_from_ == scale_to_) {
- // Special case: When not scaling, the math in the shader will resolve
- // to just outputting the value for a single source pixel. The shader
- // will sample surrounding pixels, but then apply a zero weight to them
- // during convolution. Thus, there is effectively no overscan.
- NOTREACHED(); // This is a crazy-expensive way to do a 1:1 copy!
- } else {
- NOTREACHED(); // Downscaling is meaningless.
- }
- break;
-
- case Shader::BICUBIC_HALF_1D: {
- // For each output pixel, this shader always reads 4 pixels about the
- // source position in one dimension, and has no overscan in the other
- // dimension. However, since the source position always has a distance
- // >= 1 inside the "logical" bounds, there can never be more than 3 pixels
- // of overscan.
- if (scale_from_.x() == 2 * scale_to_.x()) {
- DCHECK_EQ(HORIZONTAL, primary_axis_);
- overscan_x = 3;
- } else if (scale_from_.y() == 2 * scale_to_.y()) {
- DCHECK_EQ(VERTICAL, primary_axis_);
- overscan_y = 3;
- } else {
- // Anything but a half-downscale in one dimension is meaningless.
- NOTREACHED();
- }
- break;
- }
-
- case Shader::PLANAR_CHANNEL_0:
- case Shader::PLANAR_CHANNEL_1:
- case Shader::PLANAR_CHANNEL_2:
- case Shader::PLANAR_CHANNEL_3:
- case Shader::I422_NV61_MRT:
- case Shader::PLANAR_CHANNELS_1_2:
- // All of these sample 4x1 source pixels to produce each output "pixel".
- // There is no overscan. They can also be combined with a bilinear
- // downscale, but not an upscale.
- DCHECK_GE(scale_from_.x(), 4 * scale_to_.x());
- DCHECK_EQ(HORIZONTAL, primary_axis_);
- break;
-
- case Shader::DEINTERLEAVE_PAIRWISE_MRT:
- // This shader samples 2x1 source pixels to produce each output "pixel".
- // There is no overscan. It can also be combined with a bilinear
- // downscale, but not an upscale.
- DCHECK_GE(scale_from_.x(), 2 * scale_to_.x());
- DCHECK_EQ(HORIZONTAL, primary_axis_);
- break;
- }
-
- source_rect.Inset(gfx::InsetsF::VH(-overscan_y, -overscan_x));
- return gfx::ToEnclosingRect(source_rect);
-}
-
-void GLScaler::ScalerStage::EnsureIntermediateTextureDefined(
- const gfx::Size& size) {
- // Reallocate a new texture, if needed.
- if (!intermediate_texture_) {
- gl_->GenTextures(1, &intermediate_texture_);
- }
- if (intermediate_texture_size_ != size) {
- gl_->BindTexture(GL_TEXTURE_2D, intermediate_texture_);
- // Note: Not setting the filter or wrap parameters on the texture here
- // because that will be done in ScaleToMultipleOutputs() anyway.
- gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
- GL_RGBA, program_->texture_type(), nullptr);
- intermediate_texture_size_ = size;
- }
-}
-
-std::ostream& operator<<(std::ostream& out, const GLScaler& scaler) {
- if (!scaler.chain_) {
- return (out << "[GLScaler NOT configured]");
- }
-
- out << "Output";
- const GLScaler::ScalerStage* const final_stage = scaler.chain_.get();
- for (auto* stage = final_stage; stage; stage = stage->input_stage()) {
- out << u8" ↠{" << GLScaler::GetShaderName(stage->shader());
- if (stage->shader_program()) {
- switch (stage->shader_program()->texture_type()) {
- case GL_FLOAT:
- out << "/highp";
- break;
- case GL_HALF_FLOAT_OES:
- out << "/mediump";
- break;
- default:
- out << "/lowp";
- break;
- }
- }
- if (stage->flip_output()) {
- out << "+flip_y";
- }
- if (stage->scale_from() == stage->scale_to()) {
- out << " copy";
- } else {
- out << ' ' << stage->scale_from().ToString() << " to "
- << stage->scale_to().ToString();
- }
- if (!stage->input_stage() &&
- scaler.params_.source_color_space != scaler.scaling_color_space_) {
- out << ", with color x-form "
- << scaler.params_.source_color_space.ToString() << " to "
- << scaler.scaling_color_space_.ToString();
- }
- if (stage == final_stage) {
- if (scaler.params_.output_color_space != scaler.scaling_color_space_) {
- out << ", with color x-form to "
- << scaler.params_.output_color_space.ToString();
- }
- for (int i = 0; i < 2; ++i) {
- if (scaler.params_.swizzle[i] != GL_RGBA) {
- out << ", with swizzle(" << i << ')';
- }
- }
- }
- out << '}';
- }
- out << u8" ↠Source";
- return out;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/common/gl_scaler.h b/chromium/components/viz/common/gl_scaler.h
deleted file mode 100644
index 99013e8cadf..00000000000
--- a/chromium/components/viz/common/gl_scaler.h
+++ /dev/null
@@ -1,538 +0,0 @@
-// Copyright 2018 The Chromium 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 COMPONENTS_VIZ_COMMON_GL_SCALER_H_
-#define COMPONENTS_VIZ_COMMON_GL_SCALER_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <ostream>
-#include <string>
-#include <tuple>
-#include <utility>
-#include <vector>
-
-#include "base/memory/raw_ptr.h"
-#include "base/memory/scoped_refptr.h"
-#include "components/viz/common/gpu/context_lost_observer.h"
-#include "components/viz/common/viz_common_export.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/gfx/geometry/vector2d.h"
-
-namespace gfx {
-class ColorTransform;
-} // namespace gfx
-
-namespace viz {
-
-class ContextProvider;
-
-// A high-performance texture scaler for use with an OpenGL ES 2.0 context. It
-// can be configured to operate at different quality levels, manages/converts
-// color spaces, and optionally re-arranges/formats data in output textures for
-// use with more-efficient texture readback pipelines.
-class VIZ_COMMON_EXPORT GLScaler final : public ContextLostObserver {
- public:
- struct VIZ_COMMON_EXPORT Parameters {
- // Relative scale from/to factors. Both of these must be non-zero.
- gfx::Vector2d scale_from = gfx::Vector2d(1, 1);
- gfx::Vector2d scale_to = gfx::Vector2d(1, 1);
-
- // The color space of the source texture and the desired color space of the
- // output texture. If |source_color_space| is not set (or invalid), sRGB is
- // assumed. If |output_color_space| is not set (or invalid), the source
- // color space is assumed.
- gfx::ColorSpace source_color_space;
- gfx::ColorSpace output_color_space;
-
- // Enable color management heuristics, using higher precision texture and
- // gamma-aware scaling?
- //
- // When disabled, the gamma of the source color space and other concerns are
- // ignored and 8-bit precision is used.
- //
- // When enabled, scaling occurs in a linear color space with 16-bit floats.
- // This produces excellent results for virtually all color spaces while
- // typically requiring twice the memory and execution resources. The caller
- // must ensure the GL context supports the use of GL_RGBA16F format
- // textures.
- //
- // Relevant reading: http://www.ericbrasseur.org/gamma.html
- bool enable_precise_color_management = false;
-
- // Selects the trade-off between quality and speed.
- enum class Quality : int8_t {
- // Bilinear single pass. Fastest possible. Do not use this unless the GL
- // implementation is so slow that the other quality options won't work.
- FAST,
-
- // Bilinear upscale + N * 50% bilinear downscales. This is still fast
- // enough for general-purpose use, and image quality is nearly as good as
- // BEST when downscaling.
- GOOD,
-
- // Bicubic upscale + N * 50% bicubic downscales. Produces very good
- // quality scaled images, but it's 2-8x slower than the "GOOD" quality.
- BEST,
- } quality = Quality::GOOD;
-
- // Is the source texture Y-flipped (i.e., the origin is the lower-left
- // corner and not the upper-left corner)? Most GL textures are Y-flipped.
- // This information is required so that the scaler can correctly compute the
- // sampling region.
- bool is_flipped_source = true;
-
- // Should the output be vertically flipped? Usually, this is used when the
- // source is not Y-flipped, but the destination texture needs to be. Or, it
- // can be used to draw the final output upside-down to avoid having to copy
- // the rows in reverse order after a glReadPixels().
- bool flip_output = false;
-
- // Optionally rearrange the image data for export. Generally, this is used
- // to make later readback steps more efficient (e.g., using glReadPixels()
- // will produce the raw bytes in their correct locations).
- //
- // Output textures are assumed to be using one of the 4-channel RGBA
- // formats. While it may be more "proper" to use a single-component texture
- // format for the planar-oriented image data, not all GL implementations
- // support the use of those formats. However, all must support at least
- // GL_RGBA. Therefore, each RGBA pixel is treated as a generic "vec4" (a
- // quad of values).
- //
- // When using this feature, it is usually necessary to adjust the
- // |output_rect| passed to Scale() or ScaleToMultipleOutputs(). See notes
- // below.
- enum class ExportFormat : int8_t {
- // Do not rearrange the image data:
- //
- // (interleaved quads) (interleaved quads)
- // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
- // RGBA RGBA RGBA RGBA --> RGBA RGBA RGBA RGBA
- // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
- INTERLEAVED_QUADS,
-
- // Select one color channel, packing each of 4 pixels' values into the 4
- // elements of one output quad.
- //
- // For example, for CHANNEL_0:
- //
- // (interleaved quads) (channel 0)
- // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA RRRR RRRR
- // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA --> RRRR RRRR
- // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA RRRR RRRR
- //
- // Note: Because of this packing, the horizontal coordinates of the
- // |output_rect| used with Scale() should be divided by 4.
- CHANNEL_0,
- CHANNEL_1,
- CHANNEL_2,
- CHANNEL_3,
-
- // I422 sampling, delivered via two output textures (NV61 format): The
- // first texture is produced the same as CHANNEL_0, while the second
- // texture contains CHANNEL_1 and CHANNEL_2 at half-width interleaved and
- // full-height. For example, if this is combined with RGB→YUV color space
- // conversion:
- //
- // (interleaved quads)
- // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
- // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
- // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA
- // |
- // | (luma plane) (chroma, interleaved)
- // | YYYY YYYY UVUV UVUV
- // +---> { YYYY YYYY + UVUV UVUV }
- // YYYY YYYY UVUV UVUV
- //
- // Note: Because of this packing, the horizontal coordinates of the
- // |output_rect| used with ScaleToMultipleOutputs() should be divided by
- // 4.
- // Note 2: This requires a GL context that supports multiple render
- // targets with at least two draw buffers.
- NV61,
-
- // Deinterleave into two output textures.
- //
- // UVUV UVUV UUUU VVVV
- // UVUV UVUV --> { UUUU + VVVV }
- // UVUV UVUV UUUU VVVV
- //
- // Note: Because of this packing, the horizontal coordinates of the
- // |output_rect| used with ScaleToMultipleOutputs() should be divided by
- // 2.
- // Note 2: This requires a GL context that supports multiple render
- // targets with at least two draw buffers.
- DEINTERLEAVE_PAIRWISE,
-
- // Select CHANNEL_1 and CHANNEL_2, packing each 2-channel pair from
- // 4-pixel values into the 2 elements of one output quad. The channels
- // will be selected at half-width and full height. This should be
- // equivalent to the second texture produced by ExportFormat::NV61 (see
- // above). If used on textures after they have gone through RGB→YUV color
- // space conversion, the transformation is:
- //
- // (interleaved quads) (channels 1 & 2)
- // YUVx YUVx YUVx YUVx YUVx YUVx YUVx YUVx UVUV UVUV
- // YUVx YUVx YUVx YUVx YUVx YUVx YUVx YUVx --> UVUV UVUV
- // YUVx YUVx YUVx YUVx YUVx YUVx YUVx YUVx UVUV UVUV
- //
- // Note: Because of this packing, the horizontal coordinates of the
- // |output_rect| used with Scale() should be divided by 4.
- UV_CHANNELS,
- } export_format = ExportFormat::INTERLEAVED_QUADS;
-
- // Optionally swizzle the ordering of the values in each output quad. If the
- // output of the scaler is not going to be read back (e.g., used with
- // glReadPixels()), simply leave these unchanged. Otherwise, changing this
- // allows a read-back pipeline to use the native format of the platform to
- // avoid having to perform extra "BGRA⇄RGBA swizzle" memcpy's. Usually, this
- // should match the format to be used with glReadPixels(), and that should
- // match the GL_IMPLEMENTATION_COLOR_READ_FORMAT.
- GLenum swizzle[2] = {
- GL_RGBA, // For |dest_texture_0|.
- GL_RGBA, // For |dest_texture_1|.
- };
-
- Parameters();
- Parameters(const Parameters& other);
- ~Parameters();
- };
-
- explicit GLScaler(ContextProvider* context_provider);
-
- GLScaler(const GLScaler&) = delete;
- GLScaler& operator=(const GLScaler&) = delete;
-
- ~GLScaler() final;
-
- // Returns true if the GL context provides the necessary support for enabling
- // precise color management (see Parameters::enable_precise_color_management).
- bool SupportsPreciseColorManagement() const;
-
- // Returns the maximum number of simultaneous drawing buffers supported by the
- // GL context. Certain Parameters can only be used when this is more than 1.
- int GetMaxDrawBuffersSupported() const;
-
- // [Re]Configure the scaler with the given |new_params|. Returns true on
- // success, or false on failure.
- [[nodiscard]] bool Configure(const Parameters& new_params);
-
- // Returns the currently-configured and resolved Parameters. Note that these
- // Parameters might not be exactly the same as those that were passed to
- // Configure() because some properties (e.g., color spaces) are auto-resolved;
- // however, ParametersAreEquivalent() will still return true. Results are
- // undefined if Configure() has never been called successfully.
- const Parameters& params() const { return params_; }
-
- // Scales a portion of |src_texture| and draws the result into |dest_texture|
- // at offset (0, 0). Returns true to indicate success, or false if this
- // GLScaler is not valid.
- //
- // |src_texture_size| is the full, allocated size of the |src_texture|. This
- // is required for computing texture coordinate transforms (and only because
- // the OpenGL ES 2.0 API lacks the ability to query this info).
- //
- // |src_offset| is the offset in the source texture corresponding to point
- // (0,0) in the source/output coordinate spaces. This prevents the need for
- // extra texture copies just to re-position the source coordinate system.
- //
- // |output_rect| selects the region to draw (in the scaled, not the source,
- // coordinate space). This is used to save work in cases where only a portion
- // needs to be re-scaled. The implementation will back-compute, internally, to
- // determine the region of the |src_texture| to sample.
- //
- // WARNING: The output will always be placed at (0, 0) in the |dest_texture|,
- // and not at |output_rect.origin()|.
- //
- // Note that the |src_texture| will have the min/mag filter set to GL_LINEAR
- // and wrap_s/t set to CLAMP_TO_EDGE in this call.
- [[nodiscard]] bool Scale(GLuint src_texture,
- const gfx::Size& src_texture_size,
- const gfx::Vector2d& src_offset,
- GLuint dest_texture,
- const gfx::Rect& output_rect) {
- return ScaleToMultipleOutputs(src_texture, src_texture_size, src_offset,
- dest_texture, 0, output_rect);
- }
-
- // Same as above, but for use cases where there are two output textures drawn
- // (see Parameters::ExportFormat).
- [[nodiscard]] bool ScaleToMultipleOutputs(GLuint src_texture,
- const gfx::Size& src_texture_size,
- const gfx::Vector2d& src_offset,
- GLuint dest_texture_0,
- GLuint dest_texture_1,
- const gfx::Rect& output_rect);
-
- // Returns true if from:to represent the same scale ratio as that specified in
- // |params|.
- static bool ParametersHasSameScaleRatio(const Parameters& params,
- const gfx::Vector2d& from,
- const gfx::Vector2d& to);
-
- // Returns true if configuring a GLScaler with either |a| or |b| will produce
- // identical behaviors and results.
- static bool ParametersAreEquivalent(const Parameters& a, const Parameters& b);
-
- private:
- friend class GLScalerOverscanPixelTest;
- friend class GLScalerShaderPixelTest;
- friend VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream&,
- const GLScaler&);
-
- using GLES2Interface = gpu::gles2::GLES2Interface;
-
- enum Axis { HORIZONTAL = 0, VERTICAL = 1 };
-
- // The shaders used by each stage in the scaling pipeline.
- enum class Shader : int8_t {
- BILINEAR,
- BILINEAR2,
- BILINEAR3,
- BILINEAR4,
- BILINEAR2X2,
- BICUBIC_UPSCALE,
- BICUBIC_HALF_1D,
- PLANAR_CHANNEL_0,
- PLANAR_CHANNEL_1,
- PLANAR_CHANNEL_2,
- PLANAR_CHANNEL_3,
- PLANAR_CHANNELS_1_2,
- I422_NV61_MRT,
- DEINTERLEAVE_PAIRWISE_MRT,
- };
-
- // A cached, re-usable shader program that performs one step in the scaling
- // pipeline.
- class VIZ_COMMON_EXPORT ShaderProgram {
- public:
- ShaderProgram(GLES2Interface* gl,
- Shader shader,
- GLenum texture_type,
- const gfx::ColorTransform* color_transform,
- const GLenum swizzle[2]);
-
- ShaderProgram(const ShaderProgram&) = delete;
- ShaderProgram& operator=(const ShaderProgram&) = delete;
-
- ~ShaderProgram();
-
- Shader shader() const { return shader_; }
- GLenum texture_type() const { return texture_type_; }
-
- // UseProgram must be called with GL_ARRAY_BUFFER bound to a vertex
- // attribute buffer. |src_texture_size| is the size of the entire source
- // texture, regardless of which region is to be sampled. |src_rect| is the
- // source region, not including overscan pixels past the edges.
- // |primary_axis| determines whether multiple texture samplings occur in one
- // direction or the other (for some shaders). Note that this cannot
- // necessarily be determined by just comparing the src and dst sizes.
- // |flip_y| causes the |src_rect| to be scanned upside-down, to produce a
- // vertically-flipped result.
- void UseProgram(const gfx::Size& src_texture_size,
- const gfx::RectF& src_rect,
- const gfx::Size& dst_size,
- Axis primary_axis,
- bool flip_y);
-
- // GL_ARRAY_BUFFER data that must be bound when drawing with a
- // ShaderProgram. These are the vertex attributes that will sweep the entire
- // source area when executing the program. They represent triangle strip
- // coordinates: The first two columns are (x,y) values interpolated to
- // produce the vertex coordinates in object space, while the latter two
- // columns are (s,t) values interpolated to produce the texture coordinates
- // that correspond to the vertex coordinates.
- static const GLfloat kVertexAttributes[16];
-
- private:
- const raw_ptr<GLES2Interface> gl_;
- const Shader shader_;
- const GLenum texture_type_;
-
- // A program for copying a source texture into a destination texture.
- const GLuint program_;
-
- // The location of the position in the program.
- GLint position_location_ = -1;
- // The location of the texture coordinate in the program.
- GLint texcoord_location_ = -1;
- // The location of the source texture in the program.
- GLint texture_location_ = -1;
- // The location of the texture coordinate of the source rectangle in the
- // program.
- GLint src_rect_location_ = -1;
- // Location of size of source image in pixels.
- GLint src_pixelsize_location_ = -1;
- // Location of vector for scaling ratio between source and dest textures.
- GLint scaling_vector_location_ = -1;
- };
-
- // One scaling stage in a chain of scaler pipeline stages. Each ScalerStage
- // owns the previous ScalerStage in the chain: At execution time, a "working
- // backwards" approach is used: The previous "input" stage renders an
- // intermediate result that will be used as input for the current stage.
- //
- // Each ScalerStage caches textures and framebuffers to avoid reallocating
- // them for each separate image scaling, which can be expensive on some
- // platforms/drivers.
- class VIZ_COMMON_EXPORT ScalerStage {
- public:
- ScalerStage(GLES2Interface* gl,
- Shader shader,
- Axis primary_axis,
- const gfx::Vector2d& scale_from,
- const gfx::Vector2d& scale_to);
-
- ScalerStage(const ScalerStage&) = delete;
- ScalerStage& operator=(const ScalerStage&) = delete;
-
- ~ScalerStage();
-
- Shader shader() const { return shader_; }
- const gfx::Vector2d& scale_from() const { return scale_from_; }
- const gfx::Vector2d& scale_to() const { return scale_to_; }
-
- ScalerStage* input_stage() const { return input_stage_.get(); }
- void set_input_stage(std::unique_ptr<ScalerStage> stage) {
- input_stage_ = std::move(stage);
- }
- std::unique_ptr<ScalerStage> take_input_stage() {
- return std::move(input_stage_);
- }
-
- ShaderProgram* shader_program() const { return program_; }
- void set_shader_program(ShaderProgram* program) { program_ = program; }
-
- bool is_flipped_source() const { return is_flipped_source_; }
- void set_is_flipped_source(bool flipped) { is_flipped_source_ = flipped; }
-
- bool flip_output() const { return flip_output_; }
- void set_flip_output(bool flip) { flip_output_ = flip; }
-
- void ScaleToMultipleOutputs(GLuint src_texture,
- gfx::Size src_texture_size,
- const gfx::Vector2d& src_offset,
- GLuint dest_texture_0,
- GLuint dest_texture_1,
- const gfx::Rect& output_rect);
-
- private:
- friend class GLScalerOverscanPixelTest;
-
- // Returns the given |output_rect| mapped to the input stage's coordinate
- // system.
- gfx::RectF ToSourceRect(const gfx::Rect& output_rect) const;
-
- // Returns the given |source_rect| padded to include the overscan pixels the
- // shader program will access.
- gfx::Rect ToInputRect(gfx::RectF source_rect) const;
-
- // Generates the intermediate texture and/or re-defines it if its size has
- // changed.
- void EnsureIntermediateTextureDefined(const gfx::Size& size);
-
- const raw_ptr<GLES2Interface> gl_;
- const Shader shader_;
- const Axis primary_axis_;
- const gfx::Vector2d scale_from_;
- const gfx::Vector2d scale_to_;
-
- std::unique_ptr<ScalerStage> input_stage_;
- raw_ptr<ShaderProgram> program_ = nullptr;
- bool is_flipped_source_ = false;
- bool flip_output_ = false;
-
- GLuint intermediate_texture_ = 0;
- gfx::Size intermediate_texture_size_;
- GLuint dest_framebuffer_ = 0;
- };
-
- // ContextLostObserver implementation.
- void OnContextLost() final;
-
- // Returns a cached ShaderProgram, creating one on-demand if necessary.
- ShaderProgram* GetShaderProgram(Shader shader,
- GLenum texture_type,
- const gfx::ColorTransform* color_transform,
- const GLenum swizzle[2]);
-
- // Create a scaling chain using the bilinear shaders.
- static std::unique_ptr<ScalerStage> CreateAGoodScalingChain(
- gpu::gles2::GLES2Interface* gl,
- const gfx::Vector2d& scale_from,
- const gfx::Vector2d& scale_to);
-
- // Create a scaling chain using the bicubic shaders.
- static std::unique_ptr<ScalerStage> CreateTheBestScalingChain(
- gpu::gles2::GLES2Interface* gl,
- const gfx::Vector2d& scale_from,
- const gfx::Vector2d& scale_to);
-
- // Modifies |chain| by appending an export stage, to rearrange the image data
- // according to the requested |export_format|. In some cases, this will delete
- // the final stage in |chain| before appending the export stage.
- static std::unique_ptr<ScalerStage> MaybeAppendExportStage(
- gpu::gles2::GLES2Interface* gl,
- std::unique_ptr<ScalerStage> chain,
- Parameters::ExportFormat export_format);
-
- // Returns the other of the two axes.
- static Axis TheOtherAxis(Axis axis);
-
- // Returns the name of the |shader| in string form, for logging purposes.
- static const char* GetShaderName(Shader shader);
-
- // Returns true if the given |gl| context mentions all of |names| in its
- // extensions string.
- static bool AreAllGLExtensionsPresent(gpu::gles2::GLES2Interface* gl,
- const std::vector<std::string>& names);
-
- // The provider of the GL context. This is non-null while the GL context is
- // valid and GLScaler is observing for context loss.
- raw_ptr<ContextProvider> context_provider_;
-
- // Set by Configure() to the resolved set of Parameters.
- Parameters params_;
-
- // If set to true, half-float textures are supported. This is lazy-initialized
- // by SupportsPreciseColorManagement().
- mutable absl::optional<bool> supports_half_floats_;
-
- // The maximum number of simultaneous draw buffers, lazy-initialized by
- // GetMaxDrawBuffersSupported(). -1 means "not yet known."
- mutable int max_draw_buffers_ = -1;
-
- // Cache of ShaderPrograms. The cache key consists of fields that correspond
- // to the arguments of GetShaderProgram(): the shader, the texture format, the
- // source and output color spaces (color transform), and the two swizzles.
- using ShaderCacheKey = std::
- tuple<Shader, GLenum, gfx::ColorSpace, gfx::ColorSpace, GLenum, GLenum>;
- std::map<ShaderCacheKey, ShaderProgram> shader_programs_;
-
- // The GL_ARRAY_BUFFER that holds the vertices and the texture coordinates
- // data for sweeping the source area when a ScalerStage draws a quad (to
- // execute its shader program).
- GLuint vertex_attributes_buffer_ = 0;
-
- // The chain of ScalerStages.
- std::unique_ptr<ScalerStage> chain_;
-
- // The color space in which the scaling stages operate.
- gfx::ColorSpace scaling_color_space_;
-};
-
-// For logging.
-VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out,
- const GLScaler& scaler);
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_COMMON_GL_SCALER_H_
diff --git a/chromium/components/viz/common/gl_scaler_overscan_pixeltest.cc b/chromium/components/viz/common/gl_scaler_overscan_pixeltest.cc
deleted file mode 100644
index 30f4810b0e5..00000000000
--- a/chromium/components/viz/common/gl_scaler_overscan_pixeltest.cc
+++ /dev/null
@@ -1,532 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/common/gl_scaler.h"
-
-#include "build/build_config.h"
-#include "cc/test/pixel_test.h"
-#include "cc/test/pixel_test_utils.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/test/gl_scaler_test_util.h"
-#include "gpu/GLES2/gl2chromium.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkRect.h"
-
-namespace viz {
-
-class GLScalerOverscanPixelTest : public cc::PixelTest,
- public GLScalerTestUtil {
- public:
- using Axis = GLScaler::Axis;
- using ScalerStage = GLScaler::ScalerStage;
- using Shader = GLScaler::Shader;
-
- bool AreMultipleRenderingTargetsSupported() const {
- return scaler_->GetMaxDrawBuffersSupported() > 1;
- }
-
- // Creates a ScalerStage chain consisting of a single stage having the given
- // configuration.
- void UseScaler(Shader shader,
- Axis primary_axis,
- const gfx::Vector2d& scale_from,
- const gfx::Vector2d& scale_to) {
- scaler_->chain_ = std::make_unique<ScalerStage>(gl_, shader, primary_axis,
- scale_from, scale_to);
- scaler_->chain_->set_shader_program(scaler_->GetShaderProgram(
- shader, GL_UNSIGNED_BYTE, nullptr, GLScaler::Parameters().swizzle));
- }
-
- // Converts the given |source_rect| into a possibly-larger one that includes
- // all of the pixels that would be sampled by the current scaler (i.e.,
- // including overscan). This uses the math of the internal implementation to
- // compute the values.
- gfx::Rect ToInputRect(gfx::Rect source_rect) {
- CHECK(scaler_ && scaler_->chain_);
- return scaler_->chain_->ToInputRect(gfx::RectF(source_rect));
- }
-
- // Renders images using the current scaler to auto-detect its overscan. This
- // does NOT use the internal implementation to compute the values, but instead
- // discovers them experimentally. This is used to confirm that: a) the scaler
- // behaves as ToInputRect() expects; and b) the math internal to ToInputRect()
- // is correct.
- //
- // The general approach is to upload a source image containing a blue box in
- // the center, surrounded by a red background. The size of the blue box is
- // varied: It starts out at a size encompassing more than all of the pixels to
- // be sampled by the scaler, and is gradually shrunk until the scaler's output
- // begins to include red "bleed-in." At that point, the overscan amount is
- // confirmed experimentally.
- gfx::Vector2d DetectScalerOverscan(const gfx::Vector2d& scale_from,
- const gfx::Vector2d& scale_to) {
- // Assume a source size three times the "scale from" width and height. This
- // allows for scaling the middle third of a source image, to test possible
- // bleed-in on all sides of the output.
- const gfx::Size src_size(scale_from.x() * 3, scale_from.y() * 3);
-
- // The requested output rect is the center third of the image, in the
- // destination coordinate space.
- const gfx::Rect dst_rect(
- src_size.width() / 3 * scale_to.x() / scale_from.x(),
- src_size.height() / 3 * scale_to.y() / scale_from.y(),
- src_size.width() / 3 * scale_to.x() / scale_from.x(),
- src_size.height() / 3 * scale_to.y() / scale_from.y());
- const GLuint dst_texture = texture_helper_->CreateTexture(dst_rect.size());
-
- // This is our "basis for comparison" image. If scaled output images match
- // this, then there is no bleed-in.
- SkBitmap output_without_bleed_in;
- {
- const bool did_scale =
- scaler_->Scale(texture_helper_->UploadTexture(CreateBlueBoxOnRedImage(
- src_size, gfx::Rect(src_size))),
- src_size, gfx::Vector2d(0, 0), dst_texture, dst_rect);
- CHECK(did_scale);
- output_without_bleed_in =
- texture_helper_->DownloadTexture(dst_texture, dst_rect.size());
- VLOG(2) << scale_from.ToString() << "→" << scale_to.ToString()
- << ": Output without bleed-in is "
- << cc::GetPNGDataUrl(output_without_bleed_in);
- }
-
- // Perform a linear search for the minimal overscan values that do not cause
- // the red bleed-in in the scaled output image. There are actually two
- // separate searches here, one horizontally and one vertically. Note that an
- // overscan result of -1 indicates a failed search and/or a broken
- // implementation.
- gfx::Vector2d min_overscan(5, 5);
- for (int is_horizontal = 1; is_horizontal >= 0; --is_horizontal) {
- while (min_overscan.x() >= 0 && min_overscan.y() >= 0) {
- // Decrease the overscan by one pixel (one dimension at a time).
- const gfx::Vector2d overscan(
- is_horizontal ? (min_overscan.x() - 1) : min_overscan.x(),
- is_horizontal ? min_overscan.y() : (min_overscan.y() - 1));
-
- // Create the source texture consisting of a centered blue box
- // surrounded by red.
- const gfx::Rect blue_rect(scale_from.x() - overscan.x(),
- scale_from.y() - overscan.y(),
- scale_from.x() + 2 * overscan.x(),
- scale_from.y() + 2 * overscan.y());
- const SkBitmap source_image =
- CreateBlueBoxOnRedImage(src_size, blue_rect);
- const bool did_scale = scaler_->Scale(
- texture_helper_->UploadTexture(source_image), src_size,
- gfx::Vector2d(0, 0), dst_texture, dst_rect);
- CHECK(did_scale);
- const SkBitmap output =
- texture_helper_->DownloadTexture(dst_texture, dst_rect.size());
-
- // Compare |output| with |output_without_bleed_in|. If they are
- // different, then the blue rect became too small.
- bool output_has_bleed_in = false;
- for (int y = 0; y < output.height(); ++y) {
- for (int x = 0; x < output.width(); ++x) {
- if (output.getColor(x, y) !=
- output_without_bleed_in.getColor(x, y)) {
- output_has_bleed_in = true;
- break;
- }
- }
- }
-
- VLOG(2) << scale_from.ToString() << "→" << scale_to.ToString()
- << ": Testing overscan=" << overscan.ToString() << std::endl
- << "\tSource image is " << cc::GetPNGDataUrl(source_image)
- << std::endl
- << "\tOutput image is " << cc::GetPNGDataUrl(output);
-
- if (output_has_bleed_in) {
- break; // Search complete: Red bleed-in detected.
- }
-
- min_overscan = overscan;
- }
- }
-
- return min_overscan;
- }
-
- static SkBitmap CreateBlueBoxOnRedImage(const gfx::Size& size,
- const gfx::Rect& blue_rect) {
- SkBitmap result = AllocateRGBABitmap(size);
- // Note: None of the color channel values should be close to 0 or 255. This
- // is because the bicubic scaler will generate values that overshoot and
- // clip, and this will mess-up detection of the number of overscan pixels.
- result.eraseColor(SkColorSetRGB(0xc0, 0x40, 0x40));
- result.erase(SkColorSetRGB(0x40, 0x40, 0xc0),
- SkIRect{blue_rect.x(), blue_rect.y(), blue_rect.right(),
- blue_rect.bottom()});
- return result;
- }
-
- protected:
- void SetUp() final {
- cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft);
-
- scaler_ = std::make_unique<GLScaler>(context_provider());
- gl_ = context_provider()->ContextGL();
- CHECK(gl_);
- texture_helper_ = std::make_unique<GLScalerTestTextureHelper>(gl_);
- }
-
- void TearDown() final {
- texture_helper_.reset();
- gl_ = nullptr;
- scaler_.reset();
-
- cc::PixelTest::TearDown();
- }
-
- std::unique_ptr<GLScaler> scaler_;
- gpu::gles2::GLES2Interface* gl_ = nullptr;
- std::unique_ptr<GLScalerTestTextureHelper> texture_helper_;
-};
-
-namespace {
-constexpr gfx::Rect kTenByTenRect = gfx::Rect(10, 10, 10, 10);
-} // namespace
-
-TEST_F(GLScalerOverscanPixelTest, Bilinear) {
- constexpr struct {
- gfx::Vector2d scale_from;
- gfx::Vector2d scale_to;
- gfx::Vector2d expected_overscan;
- } kTestCases[] = {
- // No scaling.
- {gfx::Vector2d(32, 20), gfx::Vector2d(32, 20), gfx::Vector2d(0, 0)},
-
- // Scale by 0.5X.
- {gfx::Vector2d(32, 20), gfx::Vector2d(16, 20), gfx::Vector2d(0, 0)},
- {gfx::Vector2d(32, 20), gfx::Vector2d(32, 10), gfx::Vector2d(0, 0)},
- {gfx::Vector2d(32, 20), gfx::Vector2d(16, 10), gfx::Vector2d(0, 0)},
-
- // Scale by 0.75X.
- {gfx::Vector2d(32, 20), gfx::Vector2d(24, 20), gfx::Vector2d(0, 0)},
- {gfx::Vector2d(32, 20), gfx::Vector2d(32, 15), gfx::Vector2d(0, 0)},
- {gfx::Vector2d(32, 20), gfx::Vector2d(24, 15), gfx::Vector2d(0, 0)},
-
- // Scale by 1.5X.
- {gfx::Vector2d(32, 20), gfx::Vector2d(48, 20), gfx::Vector2d(1, 0)},
- {gfx::Vector2d(32, 20), gfx::Vector2d(32, 30), gfx::Vector2d(0, 1)},
- {gfx::Vector2d(32, 20), gfx::Vector2d(48, 30), gfx::Vector2d(1, 1)},
-
- // Scale by 4X.
- {gfx::Vector2d(32, 20), gfx::Vector2d(128, 20), gfx::Vector2d(1, 0)},
- {gfx::Vector2d(32, 20), gfx::Vector2d(32, 80), gfx::Vector2d(0, 1)},
- {gfx::Vector2d(32, 20), gfx::Vector2d(128, 80), gfx::Vector2d(1, 1)},
- };
-
- for (const auto& tc : kTestCases) {
- SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString()
- << ", scale_to=" << tc.scale_to.ToString());
-
- // Test the effect on the pixels.
- UseScaler(Shader::BILINEAR, Axis::HORIZONTAL, tc.scale_from, tc.scale_to);
- EXPECT_EQ(tc.expected_overscan,
- DetectScalerOverscan(tc.scale_from, tc.scale_to));
-
- // Sanity-check that the internal math estimating the overscan is correct.
- gfx::Rect expected_input_rect = kTenByTenRect;
- expected_input_rect.Inset(
- gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x()));
- EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect));
- }
-}
-
-TEST_F(GLScalerOverscanPixelTest, TwoTapBilinear) {
- constexpr struct {
- Axis primary_axis;
- gfx::Vector2d scale_from;
- gfx::Vector2d scale_to;
- gfx::Vector2d expected_overscan;
- } kTestCases[] = {
- // Scale by 0.25X in one direction only.
- {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(16, 40),
- gfx::Vector2d(0, 0)},
- {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 10),
- gfx::Vector2d(0, 0)},
-
- // Scale by 0.25X in one direction, 0.5X in the other.
- {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(16, 20),
- gfx::Vector2d(0, 0)},
- {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(32, 10),
- gfx::Vector2d(0, 0)},
-
- // Scale by 0.75X (1.5X * 0.5X).
- {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(48, 40),
- gfx::Vector2d(1, 0)},
- {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 30),
- gfx::Vector2d(0, 1)},
- };
-
- for (const auto& tc : kTestCases) {
- SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString()
- << ", scale_to=" << tc.scale_to.ToString());
-
- // Test the effect on the pixels.
- UseScaler(Shader::BILINEAR2, tc.primary_axis, tc.scale_from, tc.scale_to);
- EXPECT_EQ(tc.expected_overscan,
- DetectScalerOverscan(tc.scale_from, tc.scale_to));
-
- // Sanity-check that the internal math estimating the overscan is correct.
- gfx::Rect expected_input_rect = kTenByTenRect;
- expected_input_rect.Inset(
- gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x()));
- EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect));
- }
-}
-
-TEST_F(GLScalerOverscanPixelTest, ThreeTapBilinear) {
- constexpr struct {
- Axis primary_axis;
- gfx::Vector2d scale_from;
- gfx::Vector2d scale_to;
- gfx::Vector2d expected_overscan;
- } kTestCases[] = {
- // Scale by 0.16...X in one direction only.
- {Axis::HORIZONTAL, gfx::Vector2d(66, 40), gfx::Vector2d(11, 40),
- gfx::Vector2d(0, 0)},
- {Axis::VERTICAL, gfx::Vector2d(32, 60), gfx::Vector2d(32, 10),
- gfx::Vector2d(0, 0)},
-
- // Scale by 0.16...X in one direction, 0.5X in the other.
- {Axis::HORIZONTAL, gfx::Vector2d(66, 40), gfx::Vector2d(11, 20),
- gfx::Vector2d(0, 0)},
- {Axis::VERTICAL, gfx::Vector2d(64, 60), gfx::Vector2d(32, 10),
- gfx::Vector2d(0, 0)},
-
- // Scale by 0.75X (3.0X * 0.5X * 0.5X).
- {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(48, 40),
- gfx::Vector2d(1, 0)},
- {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 30),
- gfx::Vector2d(0, 1)},
- };
-
- for (const auto& tc : kTestCases) {
- SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString()
- << ", scale_to=" << tc.scale_to.ToString());
-
- // Test the effect on the pixels.
- UseScaler(Shader::BILINEAR3, tc.primary_axis, tc.scale_from, tc.scale_to);
- EXPECT_EQ(tc.expected_overscan,
- DetectScalerOverscan(tc.scale_from, tc.scale_to));
-
- // Sanity-check that the internal math estimating the overscan is correct.
- gfx::Rect expected_input_rect = kTenByTenRect;
- expected_input_rect.Inset(
- gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x()));
- EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect));
- }
-}
-
-TEST_F(GLScalerOverscanPixelTest, FourTapBilinear) {
- constexpr struct {
- Axis primary_axis;
- gfx::Vector2d scale_from;
- gfx::Vector2d scale_to;
- gfx::Vector2d expected_overscan;
- } kTestCases[] = {
- // Scale by 0.125X in one direction only.
- {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(8, 40),
- gfx::Vector2d(0, 0)},
- {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 5),
- gfx::Vector2d(0, 0)},
-
- // Scale by 0.125X in one direction, 0.5X in the other.
- {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(8, 20),
- gfx::Vector2d(0, 0)},
- {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(32, 5),
- gfx::Vector2d(0, 0)},
-
- // Scale by 0.75X (6.0X * 0.5X * 0.5X * 0.5X).
- {Axis::HORIZONTAL, gfx::Vector2d(64, 40), gfx::Vector2d(48, 40),
- gfx::Vector2d(1, 0)},
- {Axis::VERTICAL, gfx::Vector2d(64, 40), gfx::Vector2d(64, 30),
- gfx::Vector2d(0, 1)},
- };
-
- for (const auto& tc : kTestCases) {
- SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString()
- << ", scale_to=" << tc.scale_to.ToString());
-
- // Test the effect on the pixels.
- UseScaler(Shader::BILINEAR4, tc.primary_axis, tc.scale_from, tc.scale_to);
- EXPECT_EQ(tc.expected_overscan,
- DetectScalerOverscan(tc.scale_from, tc.scale_to));
-
- // Sanity-check that the internal math estimating the overscan is correct.
- gfx::Rect expected_input_rect = kTenByTenRect;
- expected_input_rect.Inset(
- gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x()));
- EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect));
- }
-}
-
-TEST_F(GLScalerOverscanPixelTest, TwoByTwoTapBilinear) {
- constexpr struct {
- gfx::Vector2d scale_from;
- gfx::Vector2d scale_to;
- gfx::Vector2d expected_overscan;
- } kTestCases[] = {
- // Scale by 0.25X in both directions.
- {gfx::Vector2d(64, 40), gfx::Vector2d(16, 10), gfx::Vector2d(0, 0)},
-
- // Scale by 0.75X (1.5X * 0.5X) in one direction, 0.25X in the other.
- {gfx::Vector2d(64, 40), gfx::Vector2d(48, 10), gfx::Vector2d(1, 0)},
- {gfx::Vector2d(64, 40), gfx::Vector2d(16, 30), gfx::Vector2d(0, 1)},
-
- // Scale by 0.75X (1.5X * 0.5X) in both directions.
- {gfx::Vector2d(64, 40), gfx::Vector2d(48, 30), gfx::Vector2d(1, 1)},
- };
-
- for (const auto& tc : kTestCases) {
- SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString()
- << ", scale_to=" << tc.scale_to.ToString());
-
- // Test the effect on the pixels.
- UseScaler(Shader::BILINEAR2X2, Axis::HORIZONTAL, tc.scale_from,
- tc.scale_to);
- EXPECT_EQ(tc.expected_overscan,
- DetectScalerOverscan(tc.scale_from, tc.scale_to));
-
- // Sanity-check that the internal math estimating the overscan is correct.
- gfx::Rect expected_input_rect = kTenByTenRect;
- expected_input_rect.Inset(
- gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x()));
- EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect));
- }
-}
-
-TEST_F(GLScalerOverscanPixelTest, BicubicUpscale) {
-#if BUILDFLAG(IS_ANDROID)
- // Unfortunately, on our current Android bots, there are some inaccuracies
- // introduced by the platform that seem to throw-off the pixel testing of the
- // bicubic sampler.
- constexpr bool kSkipDetectionTest = true;
- LOG(WARNING) << "Skipping overscan detection due to platform issues.";
-#else
- constexpr bool kSkipDetectionTest = false;
-#endif
-
- constexpr struct {
- Axis primary_axis;
- gfx::Vector2d scale_from;
- gfx::Vector2d scale_to;
- gfx::Vector2d expected_overscan;
- } kTestCases[] = {
- // Scale by 1.5X, 2X, and 3.3...X horizontally.
- {Axis::HORIZONTAL, gfx::Vector2d(12, 10), gfx::Vector2d(18, 10),
- gfx::Vector2d(2, 0)},
- {Axis::HORIZONTAL, gfx::Vector2d(12, 10), gfx::Vector2d(24, 10),
- gfx::Vector2d(2, 0)},
- {Axis::HORIZONTAL, gfx::Vector2d(12, 10), gfx::Vector2d(40, 10),
- gfx::Vector2d(2, 0)},
-
- // Scale by 1.5X, 2X, and 3.3...X vertically.
- {Axis::VERTICAL, gfx::Vector2d(12, 10), gfx::Vector2d(12, 15),
- gfx::Vector2d(0, 2)},
- {Axis::VERTICAL, gfx::Vector2d(12, 10), gfx::Vector2d(12, 20),
- gfx::Vector2d(0, 2)},
- {Axis::VERTICAL, gfx::Vector2d(12, 9), gfx::Vector2d(12, 30),
- gfx::Vector2d(0, 2)},
- };
-
- for (const auto& tc : kTestCases) {
- SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString()
- << ", scale_to=" << tc.scale_to.ToString());
-
- // Test the effect on the pixels.
- UseScaler(Shader::BICUBIC_UPSCALE, tc.primary_axis, tc.scale_from,
- tc.scale_to);
- if (!kSkipDetectionTest) {
- EXPECT_EQ(tc.expected_overscan,
- DetectScalerOverscan(tc.scale_from, tc.scale_to));
- }
-
- // Sanity-check that the internal math estimating the overscan is correct.
- gfx::Rect expected_input_rect = kTenByTenRect;
- expected_input_rect.Inset(
- gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x()));
- EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect));
- }
-}
-
-TEST_F(GLScalerOverscanPixelTest, BicubicHalving) {
- constexpr struct {
- Axis primary_axis;
- gfx::Vector2d scale_from;
- gfx::Vector2d scale_to;
- gfx::Vector2d expected_overscan;
- } kTestCases[] = {
- {Axis::HORIZONTAL, gfx::Vector2d(16, 16), gfx::Vector2d(8, 16),
- gfx::Vector2d(3, 0)},
- {Axis::VERTICAL, gfx::Vector2d(16, 16), gfx::Vector2d(16, 8),
- gfx::Vector2d(0, 3)},
- };
-
- for (const auto& tc : kTestCases) {
- SCOPED_TRACE(testing::Message() << "scale_from=" << tc.scale_from.ToString()
- << ", scale_to=" << tc.scale_to.ToString());
-
- // Test the effect on the pixels.
- UseScaler(Shader::BICUBIC_HALF_1D, tc.primary_axis, tc.scale_from,
- tc.scale_to);
- EXPECT_EQ(tc.expected_overscan,
- DetectScalerOverscan(tc.scale_from, tc.scale_to));
-
- // Sanity-check that the internal math estimating the overscan is correct.
- gfx::Rect expected_input_rect = kTenByTenRect;
- expected_input_rect.Inset(
- gfx::Insets::VH(-tc.expected_overscan.y(), -tc.expected_overscan.x()));
- EXPECT_EQ(expected_input_rect, ToInputRect(kTenByTenRect));
- }
-}
-
-TEST_F(GLScalerOverscanPixelTest, Planerizers) {
- if (!AreMultipleRenderingTargetsSupported()) {
- LOG(WARNING) << "Skipping test due to lack of MRT support on this machine.";
- return;
- }
-
- constexpr struct {
- Shader shader;
- Axis primary_axis;
- gfx::Vector2d scale_from;
- gfx::Vector2d scale_to;
- } kTestCases[] = {
- {Shader::PLANAR_CHANNEL_0, Axis::HORIZONTAL, gfx::Vector2d(16, 16),
- gfx::Vector2d(4, 16)},
- // Note: Other PLANAR_CHANNEL_N shaders don't need to be tested since they
- // use the same code path.
- {Shader::I422_NV61_MRT, Axis::HORIZONTAL, gfx::Vector2d(16, 16),
- gfx::Vector2d(4, 16)},
- {
- Shader::DEINTERLEAVE_PAIRWISE_MRT, Axis::HORIZONTAL,
- gfx::Vector2d(16, 16), gfx::Vector2d(8, 16),
- },
- };
-
- for (const auto& tc : kTestCases) {
- SCOPED_TRACE(testing::Message()
- << "shader=" << static_cast<int>(tc.shader)
- << ", scale_from=" << tc.scale_from.ToString()
- << ", scale_to=" << tc.scale_to.ToString());
-
- // Test the effect on the pixels.
- UseScaler(tc.shader, tc.primary_axis, tc.scale_from, tc.scale_to);
- EXPECT_EQ(gfx::Vector2d(0, 0),
- DetectScalerOverscan(tc.scale_from, tc.scale_to));
-
- // Sanity-check that the internal math estimating the overscan is correct.
- EXPECT_EQ(kTenByTenRect, ToInputRect(kTenByTenRect));
- }
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/common/gl_scaler_pixeltest.cc b/chromium/components/viz/common/gl_scaler_pixeltest.cc
deleted file mode 100644
index 9c1df00fc82..00000000000
--- a/chromium/components/viz/common/gl_scaler_pixeltest.cc
+++ /dev/null
@@ -1,628 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/common/gl_scaler.h"
-
-#include <sstream>
-
-#include "base/files/file_path.h"
-#include "base/path_service.h"
-#include "base/strings/pattern.h"
-#include "build/build_config.h"
-#include "cc/test/pixel_test.h"
-#include "cc/test/pixel_test_utils.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/test/gl_scaler_test_util.h"
-#include "components/viz/test/paths.h"
-#include "gpu/GLES2/gl2chromium.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/color_space.h"
-
-#if BUILDFLAG(IS_ANDROID)
-#include "base/android/build_info.h"
-#endif
-
-namespace {
-
-// Loads a PNG test image from the test directory, and converts it to GL_RGBA
-// byte order.
-SkBitmap LoadPNGTestImage(const std::string& basename) {
- base::FilePath test_dir;
- if (!base::PathService::Get(viz::Paths::DIR_TEST_DATA, &test_dir)) {
- LOG(FATAL) << "Unable to get Paths::DIR_TEST_DATA from base::PathService.";
- return SkBitmap();
- }
- const auto source_file = test_dir.AppendASCII(basename);
- SkBitmap as_n32;
- if (!cc::ReadPNGFile(source_file, &as_n32)) {
- return SkBitmap();
- }
- SkBitmap as_rgba = viz::GLScalerTestUtil::AllocateRGBABitmap(
- gfx::Size(as_n32.width(), as_n32.height()));
- if (!as_n32.readPixels(SkPixmap(as_rgba.info(), as_rgba.getAddr(0, 0),
- as_rgba.rowBytes()))) {
- return SkBitmap();
- }
- return as_rgba;
-}
-
-} // namespace
-
-namespace viz {
-
-#define EXPECT_STRING_MATCHES(expected, actual) \
- if (!base::MatchPattern(actual, expected)) { \
- ADD_FAILURE() << "\nActual: " << (actual) \
- << "\nExpected to match pattern: " << (expected); \
- }
-
-class GLScalerPixelTest : public cc::PixelTest, public GLScalerTestUtil {
- public:
- GLScaler* scaler() const { return scaler_.get(); }
-
- std::string GetScalerString() const {
- std::ostringstream oss;
- oss << *scaler_;
- return oss.str();
- }
-
- GLuint CreateTexture(const gfx::Size& size) {
- return texture_helper_->CreateTexture(size);
- }
-
- GLuint UploadTexture(const SkBitmap& bitmap) {
- return texture_helper_->UploadTexture(bitmap);
- }
-
- SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size) {
- return texture_helper_->DownloadTexture(texture, size);
- }
-
- // Test convenience to upload |src_bitmap| to the GPU, execute the scaling,
- // then download the result from the GPU and return it as a SkBitmap.
- SkBitmap Scale(const SkBitmap& src_bitmap,
- const gfx::Vector2d& src_offset,
- const gfx::Rect& output_rect) {
- const GLuint src_texture = UploadTexture(src_bitmap);
- const GLuint dest_texture = CreateTexture(output_rect.size());
- if (!scaler()->Scale(src_texture,
- gfx::Size(src_bitmap.width(), src_bitmap.height()),
- src_offset, dest_texture, output_rect)) {
- return SkBitmap();
- }
- return DownloadTexture(dest_texture, output_rect.size());
- }
-
- // Returns the amount of color error expected due to bugs in the current
- // platform's bilinear texture sampler.
- int GetBaselineColorDifference() const {
-#if BUILDFLAG(IS_ANDROID)
- // Android seems to have texture sampling problems that are not at all seen
- // on any of the desktop platforms. Also, versions before Marshmallow seem
- // to have a much larger accuracy issues.
- if (base::android::BuildInfo::GetInstance()->sdk_int() <
- base::android::SDK_VERSION_MARSHMALLOW) {
- return 12;
- }
- return 2;
-#else
- return 0;
-#endif
- }
-
- protected:
- void SetUp() final {
- cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft);
-
- scaler_ = std::make_unique<GLScaler>(context_provider());
- gl_ = context_provider()->ContextGL();
- CHECK(gl_);
- texture_helper_ = std::make_unique<GLScalerTestTextureHelper>(gl_);
- }
-
- bool IsAndroidMarshmallow() {
-#if BUILDFLAG(IS_ANDROID)
- return base::android::BuildInfo::GetInstance()->sdk_int() ==
- base::android::SDK_VERSION_MARSHMALLOW;
-#else
- return false;
-#endif
- }
-
- void TearDown() final {
- texture_helper_.reset();
- gl_ = nullptr;
- scaler_.reset();
-
- cc::PixelTest::TearDown();
- }
-
- private:
- std::unique_ptr<GLScaler> scaler_;
- gpu::gles2::GLES2Interface* gl_ = nullptr;
- std::unique_ptr<GLScalerTestTextureHelper> texture_helper_;
-};
-
-// Tests that the default GLScaler::Parameters produces an unscaled copy.
-TEST_F(GLScalerPixelTest, CopiesByDefault) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- ASSERT_TRUE(scaler()->Configure(GLScaler::Parameters()));
- EXPECT_EQ(u8"Output ↠{BILINEAR/lowp copy} ↠Source", GetScalerString());
- const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize);
- const SkBitmap actual =
- Scale(source, gfx::Vector2d(), gfx::Rect(kSMPTEFullSize));
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(
- actual, kSMPTEFullSize, gfx::Rect(kSMPTEFullSize), 0, &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nActual: " << cc::GetPNGDataUrl(actual);
-}
-
-// Tests a FAST quality scaling of 2→1 in X and 3→2 in Y.
-TEST_F(GLScalerPixelTest, ScalesAtFastQuality) {
- GLScaler::Parameters params;
- params.scale_from = gfx::Vector2d(2, 3);
- params.scale_to = gfx::Vector2d(1, 2);
- params.quality = GLScaler::Parameters::Quality::FAST;
- params.is_flipped_source = false;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(u8"Output ↠{BILINEAR/lowp [2 3] to [1 2]} ↠Source",
- GetScalerString());
- const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize);
- static_assert(kSMPTEFullSize.width() % 2 == 0, "Fix kSMPTEFullSize.");
- static_assert(kSMPTEFullSize.height() % 3 == 0, "Fix kSMPTEFullSize.");
- const SkBitmap actual = Scale(source, gfx::Vector2d(),
- gfx::Rect(0, 0, kSMPTEFullSize.width() / 2,
- kSMPTEFullSize.height() * 2 / 3));
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(
- actual, kSMPTEFullSize, gfx::Rect(kSMPTEFullSize), 2, &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nActual: " << cc::GetPNGDataUrl(actual);
-}
-
-// Tests a GOOD quality scaling of 1280x720 → 1024x700.
-TEST_F(GLScalerPixelTest, ScalesALittleAtGoodQuality) {
- GLScaler::Parameters params;
- params.scale_from = gfx::Vector2d(1280, 720);
- params.scale_to = gfx::Vector2d(1024, 700);
- params.quality = GLScaler::Parameters::Quality::GOOD;
- params.is_flipped_source = false;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(u8"Output ↠{BILINEAR2X2/lowp [1280 720] to [1024 700]} ↠Source",
- GetScalerString());
- constexpr gfx::Size kSourceSize = gfx::Size(1280, 720);
- const SkBitmap source = CreateSMPTETestImage(kSourceSize);
- const SkBitmap actual =
- Scale(source, gfx::Vector2d(), gfx::Rect(0, 0, 1024, 700));
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(
- actual, kSourceSize, gfx::Rect(kSourceSize), 2, &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nActual: " << cc::GetPNGDataUrl(actual);
-}
-
-// Tests a large, skewed reduction at GOOD quality: 3840x720 → 128x256.
-TEST_F(GLScalerPixelTest, ScalesALotHorizontallyAtGoodQuality) {
- GLScaler::Parameters params;
- params.scale_from = gfx::Vector2d(3840, 720);
- params.scale_to = gfx::Vector2d(128, 256);
- params.quality = GLScaler::Parameters::Quality::GOOD;
- params.is_flipped_source = false;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(
- u8"Output "
- u8"↠{BILINEAR/lowp [256 256] to [128 256]} "
- u8"↠{BILINEAR4/lowp [2048 512] to [256 256]} "
- u8"↠{BILINEAR2X2/lowp [3840 720] to [2048 512]} "
- u8"↠Source",
- GetScalerString());
- constexpr gfx::Size kSourceSize = gfx::Size(3840, 720);
- const SkBitmap source = CreateSMPTETestImage(kSourceSize);
- const SkBitmap actual =
- Scale(source, gfx::Vector2d(), gfx::Rect(0, 0, 128, 256));
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(
- actual, kSourceSize, gfx::Rect(kSourceSize), 2, &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nActual: " << cc::GetPNGDataUrl(actual);
-}
-
-// Tests a large, skewed reduction at GOOD quality: 640x2160 → 256x128.
-TEST_F(GLScalerPixelTest, ScalesALotVerticallyAtGoodQuality) {
- GLScaler::Parameters params;
- params.scale_from = gfx::Vector2d(640, 2160);
- params.scale_to = gfx::Vector2d(256, 128);
- params.quality = GLScaler::Parameters::Quality::GOOD;
- params.is_flipped_source = false;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(
- u8"Output "
- u8"↠{BILINEAR/lowp [256 256] to [256 128]} "
- u8"↠{BILINEAR4/lowp [512 2048] to [256 256]} "
- u8"↠{BILINEAR2X2/lowp [640 2160] to [512 2048]} "
- u8"↠Source",
- GetScalerString());
- constexpr gfx::Size kSourceSize = gfx::Size(640, 2160);
- const SkBitmap source = CreateSMPTETestImage(kSourceSize);
- const SkBitmap actual =
- Scale(source, gfx::Vector2d(), gfx::Rect(0, 0, 256, 128));
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(
- actual, kSourceSize, gfx::Rect(kSourceSize), 2, &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nActual: " << cc::GetPNGDataUrl(actual);
-}
-
-// Tests a BEST quality scaling of 1280x720 → 1024x700.
-TEST_F(GLScalerPixelTest, ScalesAtBestQuality) {
- GLScaler::Parameters params;
- params.scale_from = gfx::Vector2d(1280, 720);
- params.scale_to = gfx::Vector2d(1024, 700);
- params.quality = GLScaler::Parameters::Quality::BEST;
- params.is_flipped_source = false;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(
- u8"Output "
- u8"↠{BICUBIC_HALF_1D/lowp [2048 700] to [1024 700]} "
- u8"↠{BICUBIC_UPSCALE/lowp [1280 700] to [2048 700]} "
- u8"↠{BICUBIC_HALF_1D/lowp [1280 1400] to [1280 700]} "
- u8"↠{BICUBIC_UPSCALE/lowp [1280 720] to [1280 1400]} "
- u8"↠Source",
- GetScalerString());
- constexpr gfx::Size kSourceSize = gfx::Size(1280, 720);
- const SkBitmap source = CreateSMPTETestImage(kSourceSize);
- const SkBitmap actual =
- Scale(source, gfx::Vector2d(), gfx::Rect(0, 0, 1024, 700));
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(
- actual, kSourceSize, gfx::Rect(kSourceSize), 4, &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nActual: " << cc::GetPNGDataUrl(actual);
-}
-
-// Tests that a source offset can be provided to sample the source starting at a
-// different location.
-TEST_F(GLScalerPixelTest, TranslatesWithSourceOffset) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- GLScaler::Parameters params;
- params.is_flipped_source = false;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(u8"Output ↠{BILINEAR/lowp copy} ↠Source", GetScalerString());
- const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize);
- static_assert(kSMPTEFullSize.width() % 2 == 0, "Fix kSMPTEFullSize.");
- static_assert(kSMPTEFullSize.height() % 4 == 0, "Fix kSMPTEFullSize.");
- const gfx::Vector2d offset(kSMPTEFullSize.width() / 2,
- kSMPTEFullSize.height() / 4);
- const gfx::Rect src_rect(offset.x(), offset.y(),
- kSMPTEFullSize.width() - offset.x(),
- kSMPTEFullSize.height() - offset.y());
- const gfx::Rect output_rect(0, 0, kSMPTEFullSize.width() - offset.x(),
- kSMPTEFullSize.height() - offset.y());
- const SkBitmap actual = Scale(source, offset, output_rect);
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(actual, kSMPTEFullSize, src_rect, 0,
- &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nActual: " << cc::GetPNGDataUrl(actual);
-}
-
-// Tests that the source offset works when the source content is vertically
-// flipped.
-TEST_F(GLScalerPixelTest, TranslatesVerticallyFlippedSourceWithSourceOffset) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- GLScaler::Parameters params;
- params.is_flipped_source = true;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(u8"Output ↠{BILINEAR/lowp copy} ↠Source", GetScalerString());
- const SkBitmap flipped_source =
- CreateVerticallyFlippedBitmap(CreateSMPTETestImage(kSMPTEFullSize));
- const gfx::Vector2d offset(kSMPTEFullSize.width() / 2,
- kSMPTEFullSize.height() / 4);
- const gfx::Rect src_rect(offset.x(), offset.y(),
- kSMPTEFullSize.width() - offset.x(),
- kSMPTEFullSize.height() - offset.y());
- const gfx::Rect output_rect(0, 0, kSMPTEFullSize.width() - offset.x(),
- kSMPTEFullSize.height() - offset.y());
- const SkBitmap flipped_back_actual =
- CreateVerticallyFlippedBitmap(Scale(flipped_source, offset, output_rect));
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(flipped_back_actual, kSMPTEFullSize,
- src_rect, 0, &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nActual (flipped-back): " << cc::GetPNGDataUrl(flipped_back_actual);
-}
-
-// Tests that the correct source selection is made when both translating the
-// source and then scaling. Scale "from" and "to" values are chosen such that a
-// multi-stage scaler will be configured (to test that offsets are correcty
-// calculated and passed between multiple stages).
-TEST_F(GLScalerPixelTest, ScalesWithTranslatedSourceOffset) {
- GLScaler::Parameters params;
- params.scale_from = gfx::Vector2d(640, 2160);
- params.scale_to = gfx::Vector2d(256, 128);
- params.quality = GLScaler::Parameters::Quality::GOOD;
- params.is_flipped_source = false;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(
- u8"Output "
- u8"↠{BILINEAR/lowp [256 256] to [256 128]} "
- u8"↠{BILINEAR4/lowp [512 2048] to [256 256]} "
- u8"↠{BILINEAR2X2/lowp [640 2160] to [512 2048]} "
- u8"↠Source",
- GetScalerString());
- constexpr gfx::Size kSourceSize = gfx::Size(640, 2160);
- const SkBitmap source = CreateSMPTETestImage(kSourceSize);
- const gfx::Vector2d offset(kSourceSize.width() / 2, kSourceSize.height() / 4);
- const gfx::Rect output_rect(0, 0, 128, 64);
- const SkBitmap actual = Scale(source, offset, output_rect);
- const gfx::Rect expected_copy_rect(
- offset.x(), offset.y(),
- output_rect.width() * params.scale_from.x() / params.scale_to.x(),
- output_rect.height() * params.scale_from.y() / params.scale_to.y());
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(actual, kSourceSize, expected_copy_rect,
- 2, &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nExpected crop region of source: " << expected_copy_rect.ToString()
- << "\nFull (uncropped) Source: " << cc::GetPNGDataUrl(source)
- << "\nActual: " << cc::GetPNGDataUrl(actual);
-}
-
-// Tests that the output is vertically flipped, if requested in the parameters.
-TEST_F(GLScalerPixelTest, VerticallyFlipsOutput) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- GLScaler::Parameters params;
- params.is_flipped_source = false;
- params.flip_output = true;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(u8"Output ↠{BILINEAR/lowp+flip_y copy} ↠Source",
- GetScalerString());
- const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize);
- const SkBitmap flipped_back_actual = CreateVerticallyFlippedBitmap(
- Scale(source, gfx::Vector2d(), gfx::Rect(kSMPTEFullSize)));
- int max_color_diff = GetBaselineColorDifference();
- EXPECT_TRUE(LooksLikeSMPTETestImage(flipped_back_actual, kSMPTEFullSize,
- gfx::Rect(kSMPTEFullSize), 0,
- &max_color_diff))
- << "max_color_diff measured was " << max_color_diff
- << "\nActual (flipped-back): " << cc::GetPNGDataUrl(flipped_back_actual);
-}
-
-// Tests that the single-channel export ScalerStage works by executing a red
-// channel export.
-TEST_F(GLScalerPixelTest, ExportsTheRedColorChannel) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- GLScaler::Parameters params;
- params.is_flipped_source = false;
- params.export_format = GLScaler::Parameters::ExportFormat::CHANNEL_0;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(u8"Output ↠{PLANAR_CHANNEL_0/lowp [4 1] to [1 1]} ↠Source",
- GetScalerString());
- const SkBitmap source = CreateSMPTETestImage(kSMPTEFullSize);
- const SkBitmap expected = CreatePackedPlanarBitmap(source, 0);
- const gfx::Size output_size(expected.width(), expected.height());
- const SkBitmap actual =
- Scale(source, gfx::Vector2d(), gfx::Rect(output_size));
- constexpr float kAvgAbsoluteErrorLimit = 1.f;
- constexpr int kMaxAbsoluteErrorLimit = 2;
- EXPECT_TRUE(cc::FuzzyPixelComparator(
- false, 100.f, 0.f,
- GetBaselineColorDifference() + kAvgAbsoluteErrorLimit,
- GetBaselineColorDifference() + kMaxAbsoluteErrorLimit, 0)
- .Compare(expected, actual))
- << "\nActual: " << cc::GetPNGDataUrl(actual)
- << "\Expected: " << cc::GetPNGDataUrl(expected);
-}
-
-// A test that also stands as an example for how to use the GLScaler to scale a
-// screen-sized RGB source (2160x1440, 16:10 aspect ratio) to a typical video
-// resolution (720p, 16:9). The end-goal is to produce three textures, which
-// contain the three YUV planes in I420 format.
-//
-// This is a two step process: First, the source is scaled and color space
-// converted, with the final result exported as NV61 format (a full size luma
-// plane + a half-width interleaved UV image). Second, the interleaved UV image
-// is scaled by half in the vertical direction and then separated into one U and
-// one V plane.
-TEST_F(GLScalerPixelTest, Example_ScaleAndExportForScreenVideoCapture) {
- if (scaler()->GetMaxDrawBuffersSupported() < 2) {
- LOG(WARNING) << "Skipping test due to lack of MRT support.";
- return;
- }
-
- // Step 1: Produce a scaled NV61-format result.
- GLScaler::Parameters params;
- params.scale_from = gfx::Vector2d(2160, 1440);
- params.scale_to = gfx::Vector2d(1280, 720);
- params.source_color_space = DefaultRGBColorSpace();
- params.output_color_space = DefaultYUVColorSpace();
- params.enable_precise_color_management = true;
- params.quality = GLScaler::Parameters::Quality::GOOD;
- params.is_flipped_source = true;
- params.flip_output = true;
- params.export_format = GLScaler::Parameters::ExportFormat::NV61;
- params.swizzle[0] = GL_BGRA_EXT; // Swizzle for readback.
- params.swizzle[1] = GL_RGBA; // Don't swizzle output for Step 2.
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_STRING_MATCHES(
- u8"Output "
- u8"↠{I422_NV61_MRT/mediump [5120 720] to [1280 720], with color x-form "
- u8"to *BT709*, with swizzle(0)} "
- u8"↠{BILINEAR2/mediump [2160 1440] to [1280 720]} "
- u8"↠{BILINEAR/mediump+flip_y copy, with color x-form *BT709* to "
- u8"*transfer:1.0000\\*x*} "
- u8"↠Source",
- GetScalerString());
-
- constexpr gfx::Size kSourceSize = gfx::Size(2160, 1440);
- const GLuint src_texture = UploadTexture(
- CreateVerticallyFlippedBitmap(CreateSMPTETestImage(kSourceSize)));
- constexpr gfx::Size kOutputSize = gfx::Size(1280, 720);
- SkBitmap expected = CreateSMPTETestImage(kOutputSize);
- ConvertRGBABitmapToYUV(&expected);
-
- // While the output size is 1280x720, the packing of 4 pixels into one RGBA
- // quad means that the texture width must be divided by 4, and that size
- // passed in the output_rect argument in the call to ScaleToMultipleOutputs().
- const gfx::Size y_plane_size(kOutputSize.width() / 4, kOutputSize.height());
- const GLuint y_plane_texture = CreateTexture(y_plane_size);
- const GLuint uv_interleaved_texture = CreateTexture(y_plane_size);
-
- ASSERT_TRUE(scaler()->ScaleToMultipleOutputs(
- src_texture, kSourceSize, gfx::Vector2d(), y_plane_texture,
- uv_interleaved_texture, gfx::Rect(y_plane_size)));
-
- // Step 2: Run the scaler again with the deinterleaver exporter, to produce
- // the I420 U and V planes from the NV61 UV interleaved image.
- params = GLScaler::Parameters(); // Reset params.
- params.scale_from = gfx::Vector2d(1, 2);
- params.scale_to = gfx::Vector2d(1, 1);
- params.source_color_space = DefaultYUVColorSpace();
- params.quality = GLScaler::Parameters::Quality::GOOD;
- params.is_flipped_source = false; // Output was already flipped in Step 1.
- params.export_format =
- GLScaler::Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE;
- params.swizzle[0] = GL_BGRA_EXT; // Swizzle for readback.
- params.swizzle[1] = GL_BGRA_EXT; // Swizzle for readback.
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(
- u8"Output "
- u8"↠{DEINTERLEAVE_PAIRWISE_MRT/lowp [2 2] to [1 1], with swizzle(0), "
- u8"with swizzle(1)} "
- u8"↠Source",
- GetScalerString());
-
- const gfx::Size uv_plane_size(y_plane_size.width() / 2,
- y_plane_size.height() / 2);
- const GLuint u_plane_texture = CreateTexture(uv_plane_size);
- const GLuint v_plane_texture = CreateTexture(uv_plane_size);
- ASSERT_TRUE(scaler()->ScaleToMultipleOutputs(
- uv_interleaved_texture, y_plane_size, gfx::Vector2d(), u_plane_texture,
- v_plane_texture, gfx::Rect(uv_plane_size)));
-
- // Download the textures, and unpack them into an interleaved YUV bitmap, for
- // comparison against the |expected| rendition.
- SkBitmap actual = AllocateRGBABitmap(kOutputSize);
- actual.eraseColor(SkColorSetARGB(0xff, 0x00, 0x80, 0x80));
- SkBitmap y_plane = DownloadTexture(y_plane_texture, y_plane_size);
- SwizzleBitmap(&y_plane);
- UnpackPlanarBitmap(y_plane, 0, &actual);
- SkBitmap u_plane = DownloadTexture(u_plane_texture, uv_plane_size);
- SwizzleBitmap(&u_plane);
- UnpackPlanarBitmap(u_plane, 1, &actual);
- SkBitmap v_plane = DownloadTexture(v_plane_texture, uv_plane_size);
- SwizzleBitmap(&v_plane);
- UnpackPlanarBitmap(v_plane, 2, &actual);
-
- // Provide generous error limits to account for the chroma subsampling in the
- // |actual| result when compared to the perfect |expected| rendition.
- constexpr float kAvgAbsoluteErrorLimit = 16.f;
- constexpr int kMaxAbsoluteErrorLimit = 0x80;
- EXPECT_TRUE(cc::FuzzyPixelComparator(false, 100.f, 0.f,
- kAvgAbsoluteErrorLimit,
- kMaxAbsoluteErrorLimit, 0)
- .Compare(expected, actual))
- << "\nActual: " << cc::GetPNGDataUrl(actual)
- << "\nExpected: " << cc::GetPNGDataUrl(expected);
-}
-
-// Performs a scaling-with-gamma-correction experiment to test GLScaler's
-// "precise color management" feature. A 50% scale is executed on the same
-// source image, once with color management turned on, and once with it turned
-// off. The results, each of which should be different, are then examined.
-TEST_F(GLScalerPixelTest, ScalesWithColorManagement) {
- if (!scaler()->SupportsPreciseColorManagement()) {
- LOG(WARNING) << "Skipping test due to lack of 16-bit float support.";
- return;
- }
-
- // An image of a raspberry (source:
- // https://commons.wikimedia.org/wiki/File:Framboise_Margy_3.jpg) has been
- // transformed in such a way that scaling it by half in both directions will
- // reveal whether scaling is occurring on linearized color values. When scaled
- // correctly, the output image should contain a visible raspberry blended
- // heavily with solid gray. However, if done naively, the output will be a
- // solid 50% gray. For details, see: http://www.ericbrasseur.org/gamma.html
- //
- // Note that the |source| and |expected| images both use the sRGB color space.
- const SkBitmap source = LoadPNGTestImage("rasp-grayator.png");
- ASSERT_FALSE(source.isNull());
- const SkBitmap expected = LoadPNGTestImage("rasp-grayator-half.png");
- ASSERT_FALSE(expected.isNull());
- const gfx::Size output_size =
- gfx::Size(source.width() / 2, source.height() / 2);
- ASSERT_EQ(gfx::Size(expected.width(), expected.height()), output_size);
- const SkBitmap expected_naive = AllocateRGBABitmap(output_size);
- expected_naive.eraseColor(SkColorSetARGB(0xff, 0x7f, 0x7f, 0x7f));
-
- // Scale the right way: With color management enabled, the raspberry should be
- // visible in the downscaled result.
- GLScaler::Parameters params;
- params.scale_from = gfx::Vector2d(2, 2);
- params.scale_to = gfx::Vector2d(1, 1);
- params.source_color_space = gfx::ColorSpace::CreateSRGB();
- params.enable_precise_color_management = true;
- params.quality = GLScaler::Parameters::Quality::GOOD;
- params.is_flipped_source = false;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_STRING_MATCHES(
- u8"Output "
- u8"↠{BILINEAR/mediump [2 2] to [1 1], with color x-form to *BT709*} "
- u8"↠{BILINEAR/mediump copy, with color x-form *BT709* to "
- u8"*transfer:1.0000\\*x*} "
- u8"↠Source",
- GetScalerString());
- const SkBitmap actual =
- Scale(source, gfx::Vector2d(), gfx::Rect(output_size));
- constexpr float kAvgAbsoluteErrorLimit = 1.f;
- constexpr int kMaxAbsoluteErrorLimit = 2;
- EXPECT_TRUE(cc::FuzzyPixelComparator(
- false, 100.f, 0.f,
- GetBaselineColorDifference() + kAvgAbsoluteErrorLimit,
- GetBaselineColorDifference() + kMaxAbsoluteErrorLimit, 0)
- .Compare(expected, actual))
- << "\nActual: " << cc::GetPNGDataUrl(actual)
- << "\nExpected (half size): " << cc::GetPNGDataUrl(expected)
- << "\nOriginal: " << cc::GetPNGDataUrl(source);
-
- // Scale the naive way: Without color management, expect a solid gray result.
- params.enable_precise_color_management = false;
- ASSERT_TRUE(scaler()->Configure(params));
- EXPECT_EQ(u8"Output ↠{BILINEAR/lowp [2 2] to [1 1]} ↠Source",
- GetScalerString());
- const SkBitmap actual_naive =
- Scale(source, gfx::Vector2d(), gfx::Rect(output_size));
- EXPECT_TRUE(cc::FuzzyPixelComparator(
- false, 100.f, 0.f,
- GetBaselineColorDifference() + kAvgAbsoluteErrorLimit,
- GetBaselineColorDifference() + kMaxAbsoluteErrorLimit, 0)
- .Compare(expected_naive, actual_naive))
- << "\nActual: " << cc::GetPNGDataUrl(actual_naive)
- << "\nExpected (half size): " << cc::GetPNGDataUrl(expected_naive)
- << "\nOriginal: " << cc::GetPNGDataUrl(source);
-}
-
-#undef EXPECT_STRING_MATCHES
-
-} // namespace viz
diff --git a/chromium/components/viz/common/gl_scaler_shader_pixeltest.cc b/chromium/components/viz/common/gl_scaler_shader_pixeltest.cc
deleted file mode 100644
index e1e7b8c226d..00000000000
--- a/chromium/components/viz/common/gl_scaler_shader_pixeltest.cc
+++ /dev/null
@@ -1,785 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/common/gl_scaler.h"
-
-#include <sstream>
-#include <tuple>
-#include <vector>
-
-#include "build/build_config.h"
-#include "cc/test/pixel_test.h"
-#include "cc/test/pixel_test_utils.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/test/gl_scaler_test_util.h"
-#include "gpu/GLES2/gl2chromium.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkImageInfo.h"
-#include "third_party/skia/include/core/SkRect.h"
-#include "ui/gfx/color_transform.h"
-
-#if BUILDFLAG(IS_ANDROID)
-#include "base/android/build_info.h"
-#endif
-
-namespace viz {
-
-namespace {
-
-// Base size of test images to be operated upon. Both dimensions must be
-// divisible by 2, 3, or 4.
-constexpr gfx::Size kBaseSize = gfx::Size(16 * 4 * 3, 9 * 4 * 3);
-
-} // namespace
-
-class GLScalerShaderPixelTest
- : public cc::PixelTest,
- public testing::WithParamInterface<std::tuple<bool, bool>>,
- public GLScalerTestUtil {
- public:
- using Axis = GLScaler::Axis;
- using Shader = GLScaler::Shader;
- using ShaderProgram = GLScaler::ShaderProgram;
-
- GLScalerShaderPixelTest()
- : scoped_trace_(
- __FILE__,
- __LINE__,
- (testing::Message()
- << "is_converting_rgb_to_yuv=" << is_converting_rgb_to_yuv()
- << ", is_swizzling_output=" << is_swizzling_output())) {}
-
- bool is_converting_rgb_to_yuv() const { return std::get<0>(GetParam()); }
- bool is_swizzling_output() const { return std::get<1>(GetParam()); }
-
- bool AreMultipleRenderingTargetsSupported() const {
- return scaler_->GetMaxDrawBuffersSupported() > 1;
- }
-
- // Returns a cached ShaderProgram, maybe configured to convert RGB→YUV and/or
- // swizzle the 1st and 3rd bytes in the output (depending on GetParams()).
- ShaderProgram* GetShaderProgram(Shader shader) {
- std::unique_ptr<gfx::ColorTransform> transform;
- if (is_converting_rgb_to_yuv()) {
- transform = gfx::ColorTransform::NewColorTransform(
- DefaultRGBColorSpace(), DefaultYUVColorSpace());
- }
- const GLenum swizzle[2] = {
- static_cast<GLenum>(is_swizzling_output() ? GL_BGRA_EXT : GL_RGBA),
- static_cast<GLenum>(is_swizzling_output() ? GL_BGRA_EXT : GL_RGBA),
- };
- return scaler_->GetShaderProgram(shader, GL_UNSIGNED_BYTE, transform.get(),
- swizzle);
- }
-
- GLuint CreateTexture(const gfx::Size& size) {
- return texture_helper_->CreateTexture(size);
- }
-
- GLuint UploadTexture(const SkBitmap& bitmap) {
- return texture_helper_->UploadTexture(bitmap);
- }
-
- SkBitmap DownloadTexture(GLuint texture, const gfx::Size& size) {
- return texture_helper_->DownloadTexture(texture, size);
- }
-
- GLuint RenderToNewTexture(GLuint src_texture, const gfx::Size& size) {
- return RenderToNewTextures(src_texture, size, false).first;
- }
-
- // Using the current shader program, creates new texture(s) of the given
- // |size| and draws using |src_texture| as input. If |dual_outputs| is true,
- // two new textures are created and drawn-to simultaneously; otherwise, only
- // one is created and drawn-to. The caller does not take ownership of the new
- // texture(s).
- std::pair<GLuint, GLuint> RenderToNewTextures(GLuint src_texture,
- const gfx::Size& size,
- bool dual_outputs) {
- std::pair<GLuint, GLuint> dst_textures(
- CreateTexture(size), dual_outputs ? CreateTexture(size) : 0u);
- GLuint framebuffer = 0;
- gl_->GenFramebuffers(1, &framebuffer);
- gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, dst_textures.first, 0);
- if (dual_outputs) {
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 1,
- GL_TEXTURE_2D, dst_textures.second, 0);
- }
-
- gl_->BindTexture(GL_TEXTURE_2D, src_texture);
-
- gl_->Viewport(0, 0, size.width(), size.height());
- const GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0 + 1};
- if (dual_outputs) {
- gl_->DrawBuffersEXT(2, buffers);
- }
- // Assumption: The |vertex_attributes_buffer_| created in SetUp() is
- // currently bound to GL_ARRAY_BUFFER.
- gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- gl_->DeleteFramebuffers(1, &framebuffer);
-
- return dst_textures;
- }
-
- // Returns a texture that converts the input |texture| back to unswizzled RGB,
- // if necessary, depending on GetParam(). The caller does not take ownership
- // of the returned texture, which could be the same texture as the input
- // argument in some cases.
- GLuint ConvertBackToUnswizzledRGB(GLuint texture, const gfx::Size& size) {
- GLuint result = texture;
- if (is_swizzling_output()) {
- const GLenum swizzle[2] = {GL_BGRA_EXT, GL_BGRA_EXT};
- scaler_
- ->GetShaderProgram(Shader::BILINEAR, GL_UNSIGNED_BYTE, nullptr,
- swizzle)
- ->UseProgram(size, gfx::RectF(gfx::Rect(size)), size,
- Axis::HORIZONTAL, false);
- result = RenderToNewTexture(result, size);
- }
- if (is_converting_rgb_to_yuv()) {
- const auto transform = gfx::ColorTransform::NewColorTransform(
- DefaultYUVColorSpace(), DefaultRGBColorSpace());
- const GLenum swizzle[2] = {GL_RGBA, GL_RGBA};
- scaler_
- ->GetShaderProgram(Shader::BILINEAR, GL_UNSIGNED_BYTE,
- transform.get(), swizzle)
- ->UseProgram(size, gfx::RectF(gfx::Rect(size)), size,
- Axis::HORIZONTAL, false);
- result = RenderToNewTexture(result, size);
- }
- return result;
- }
-
- // A test case executed by RunSMPTEScalingTestCases().
- struct SMPTEScalingTestCase {
- gfx::Rect src_rect; // Selects a subrect of the source.
- int scale_from; // Scale ratio denominator.
- int scale_to; // Scale ratio numerator.
- int fuzzy_bar_border; // Ignored pixels between color bars, when comparing.
- };
-
- // Draws with the given shader for each of the provided |test_cases| and adds
- // gtest failure(s) if the output does not look like a part of the SMPTE test
- // image.
- void RunSMPTEScalingTestCases(
- Shader shader,
- const std::vector<SMPTEScalingTestCase>& test_cases) {
- const SkBitmap source = CreateSMPTETestImage(kBaseSize);
- const GLuint src_texture = UploadTexture(source);
-
- for (const auto& tc : test_cases) {
- for (int is_horizontal = 0; is_horizontal <= 1; ++is_horizontal) {
- gfx::Size dst_size = tc.src_rect.size();
- Axis axis;
- if (is_horizontal) {
- CHECK_EQ((dst_size.width() * tc.scale_to) % tc.scale_from, 0);
- dst_size.set_width(dst_size.width() * tc.scale_to / tc.scale_from);
- axis = Axis::HORIZONTAL;
- } else {
- CHECK_EQ((dst_size.height() * tc.scale_to) % tc.scale_from, 0);
- dst_size.set_height(dst_size.height() * tc.scale_to / tc.scale_from);
- axis = Axis::VERTICAL;
- }
-
- SCOPED_TRACE(testing::Message()
- << "src_rect=" << tc.src_rect.ToString()
- << ", scale from→to=" << tc.scale_from << "→"
- << tc.scale_to << ", dst_size=" << dst_size.ToString());
-
- GetShaderProgram(shader)->UseProgram(kBaseSize, gfx::RectF(tc.src_rect),
- dst_size, axis, false);
- const SkBitmap actual = DownloadTexture(
- ConvertBackToUnswizzledRGB(
- RenderToNewTexture(src_texture, dst_size), dst_size),
- dst_size);
- int max_color_diff = GetMaxAllowedColorDifference();
- if (!LooksLikeSMPTETestImage(actual, kBaseSize, tc.src_rect,
- tc.fuzzy_bar_border, &max_color_diff)) {
- ADD_FAILURE() << "Scaled image does not look like the correct scaled "
- "subrect of the SMPTE test image (max diff measured="
- << max_color_diff
- << "):\nActual: " << cc::GetPNGDataUrl(actual);
- }
- }
- }
- }
-
- // Adds test failures if an |actual| image does not match the |expected|
- // image. When not doing color space conversion, the images must match
- // exactly; otherwise, some minor differences are allowed.
- void ExpectAreTheSameImage(const SkBitmap& expected,
- const SkBitmap& actual) const {
- const int max_color_diff = GetMaxAllowedColorDifference();
- if (!cc::FuzzyPixelComparator(false, 100.0f, 0.0f, max_color_diff,
- max_color_diff, 0)
- .Compare(expected, actual)) {
- ADD_FAILURE() << "Images are not similar enough (max_color_diff="
- << max_color_diff
- << "):\nExpected: " << cc::GetPNGDataUrl(expected)
- << "\nActual: " << cc::GetPNGDataUrl(actual);
- }
- }
-
- // Draws with the given shader to downscale a "striped pattern" image by
- // |downscale_factor| in one dimension only, and adds gtest failure(s) if the
- // resulting image is not of the |expected_solid_color|. |cycle| specifies the
- // colors of the stripes, which should average to |expected_solid_color|.
- //
- // If the shader program is correct, it should be sampling the texture halfway
- // between each pair of stripes and then averaging the result. This means that
- // every N pixels in the source will be averaged to one pixel in the output,
- // creating a solid color fill as output. If the shader is sampling the
- // texture at the wrong points, the result will be tinted and/or contain
- // striping.
- void RunMultiplePassBilinearTest(Shader shader,
- int downscale_factor,
- const std::vector<SkColor>& cycle) {
- // Compute the expected solid fill color from the colors in |cycle|.
- uint32_t sum_red = 0;
- uint32_t sum_green = 0;
- uint32_t sum_blue = 0;
- uint32_t sum_alpha = 0;
- for (SkColor c : cycle) {
- sum_red += SkColorGetR(c);
- sum_green += SkColorGetG(c);
- sum_blue += SkColorGetB(c);
- sum_alpha += SkColorGetA(c);
- }
- const float count = cycle.size();
- // Note: Taking the rounded average for each color channel.
- const SkColor expected_solid_color =
- SkColorSetARGB(sum_alpha / count + 0.5f, sum_red / count + 0.5f,
- sum_green / count + 0.5f, sum_blue / count + 0.5f);
-
- // Run the test for the vertical direction, and again for the horizontal
- // direction.
- const gfx::Rect src_rect =
- gfx::Rect(0, 0, 10 * downscale_factor, 10 * downscale_factor);
- for (int is_horizontal = 0; is_horizontal <= 1; ++is_horizontal) {
- gfx::Size dst_size = src_rect.size();
- Axis axis;
- CyclicalPattern pattern;
- if (is_horizontal) {
- dst_size.set_width(dst_size.width() / downscale_factor);
- axis = Axis::HORIZONTAL;
- pattern = VERTICAL_STRIPES;
- } else {
- dst_size.set_height(dst_size.height() / downscale_factor);
- axis = Axis::VERTICAL;
- pattern = HORIZONTAL_STRIPES;
- }
-
- // Create the expected output image consisting of a solid fill color.
- SkBitmap expected = AllocateRGBABitmap(dst_size);
- expected.eraseColor(expected_solid_color);
-
- // Run the test for each of N possible rotations of the |cycle| of
- // stripes.
- for (size_t rotation = 0; rotation < cycle.size(); ++rotation) {
- SCOPED_TRACE(testing::Message() << "is_horizontal=" << !!is_horizontal
- << ", rotation=" << rotation
- << ", expected_solid_color=" << std::hex
- << expected_solid_color);
-
- const SkBitmap source =
- CreateCyclicalTestImage(src_rect.size(), pattern, cycle, rotation);
- const GLuint src_texture = UploadTexture(source);
-
- // Execute the program, and convert the shader program's drawn result
- // back to an unswizzled RGB form, and compare that with the expected
- // image.
- GetShaderProgram(shader)->UseProgram(
- src_rect.size(), gfx::RectF(src_rect), dst_size, axis, false);
- const SkBitmap actual = DownloadTexture(
- ConvertBackToUnswizzledRGB(
- RenderToNewTexture(src_texture, dst_size), dst_size),
- dst_size);
- ExpectAreTheSameImage(expected, actual);
- }
- }
- }
-
- protected:
- void SetUp() final {
- cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft);
-
- scaler_ = std::make_unique<GLScaler>(context_provider());
- gl_ = context_provider()->ContextGL();
- CHECK(gl_);
-
- // Set up vertex attributes buffer and its data.
- gl_->GenBuffers(1, &vertex_attributes_buffer_);
- gl_->BindBuffer(GL_ARRAY_BUFFER, vertex_attributes_buffer_);
- gl_->BufferData(GL_ARRAY_BUFFER, sizeof(ShaderProgram::kVertexAttributes),
- ShaderProgram::kVertexAttributes, GL_STATIC_DRAW);
-
- texture_helper_ = std::make_unique<GLScalerTestTextureHelper>(gl_);
- }
-
- void TearDown() final {
- texture_helper_.reset();
-
- if (vertex_attributes_buffer_) {
- gl_->DeleteBuffers(1, &vertex_attributes_buffer_);
- vertex_attributes_buffer_ = 0;
- }
-
- gl_ = nullptr;
- scaler_.reset();
-
- cc::PixelTest::TearDown();
- }
-
- // Returns the maximum allowed absolute difference between any two color
- // values in the expected vs actual image comparisons, given the current test
- // parameters and known platform-specific inaccuracy.
- int GetMaxAllowedColorDifference() const {
-#if BUILDFLAG(IS_ANDROID)
- // Android seems to have texture sampling and/or readback accuracy issues
- // with these programs that are not at all seen on any of the desktop
- // platforms. Also, versions before Marshmallow seem to have a much larger
- // accuracy issues with a few of the programs. Thus, use higher thresholds,
- // assuming that the programs are correct if they can pass a much lower
- // threshold on other platforms.
- if (base::android::BuildInfo::GetInstance()->sdk_int() <
- base::android::SDK_VERSION_MARSHMALLOW) {
- return (is_converting_rgb_to_yuv() || is_swizzling_output()) ? 24 : 12;
- }
- return (is_converting_rgb_to_yuv() || is_swizzling_output()) ? 4 : 2;
-#else
- return (is_converting_rgb_to_yuv() || is_swizzling_output()) ? 2 : 0;
-#endif
- }
-
- bool IsAndroidMarshmallow() {
-#if BUILDFLAG(IS_ANDROID)
- return base::android::BuildInfo::GetInstance()->sdk_int() ==
- base::android::SDK_VERSION_MARSHMALLOW;
-#else
- return false;
-#endif
- }
-
- testing::ScopedTrace scoped_trace_;
- std::unique_ptr<GLScaler> scaler_;
- gpu::gles2::GLES2Interface* gl_ = nullptr;
- GLuint vertex_attributes_buffer_ = 0;
- std::unique_ptr<GLScalerTestTextureHelper> texture_helper_;
-};
-
-// As the BILINEAR shader is used by some of the test helpers, this test is
-// necessary to ensure the correctness of the tools used by all the other tests.
-TEST_P(GLScalerShaderPixelTest, ValidateTestHelpers) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- // Create/validate a SMPTE color bar test image.
- const SkBitmap original = CreateSMPTETestImage(kBaseSize);
- int max_color_diff = GetMaxAllowedColorDifference();
- ASSERT_TRUE(LooksLikeSMPTETestImage(original, kBaseSize, gfx::Rect(kBaseSize),
- 0, &max_color_diff))
- << "max diff measured=" << max_color_diff;
-
- // Create and upload a test image that has had RGB→YUV conversion performed
- // and/or had its color channels swizzled, depending on the testing params.
- SkBitmap image = CreateSMPTETestImage(kBaseSize);
- if (is_converting_rgb_to_yuv()) {
- ConvertRGBABitmapToYUV(&image);
- }
- if (is_swizzling_output()) {
- SwizzleBitmap(&image);
- }
- const GLuint uploaded_texture = UploadTexture(image);
-
- // Use the convert-back helper, which uses the BILINEAR shader to convert the
- // |uploaded_texture| back to an unswizzled RGB form. Then, download the
- // result and check whether it matches the original.
- const gfx::Size size(image.width(), image.height());
- const GLuint converted_back_texture =
- ConvertBackToUnswizzledRGB(uploaded_texture, size);
- const SkBitmap actual = DownloadTexture(converted_back_texture, size);
- ExpectAreTheSameImage(original, actual);
-}
-
-// Tests the default, one-pass bilinear shader which can upscale or downscale by
-// up to 2X.
-TEST_P(GLScalerShaderPixelTest, Bilinear) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- constexpr gfx::Rect whole = gfx::Rect(kBaseSize);
- constexpr gfx::Rect quadrant =
- gfx::Rect(kBaseSize.width() / 2, kBaseSize.height() / 2,
- kBaseSize.width() / 2, kBaseSize.height() / 2);
- const std::vector<SMPTEScalingTestCase> kTestCases = {
- // No scaling.
- {whole, 1, 1, 0},
- // Downscale by half.
- {whole, 2, 1, 1},
- // Upscale by 1.5.
- {whole, 2, 3, 1},
- // No scaling; lower-right quadrant only.
- {quadrant, 1, 1, 0},
- // Downscale by half; lower-right quadrant only.
- {quadrant, 2, 1, 1},
- // Upscale by 1.5; lower-right quadrant only.
- {quadrant, 2, 3, 1},
- };
-
- RunSMPTEScalingTestCases(Shader::BILINEAR, kTestCases);
-}
-
-// Test the 2-tap bilinear shader, which downscales by 4X in one dimension.
-TEST_P(GLScalerShaderPixelTest, TwoTapBilinear) {
- RunMultiplePassBilinearTest(Shader::BILINEAR2, 4,
- {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff)});
-}
-
-// Test the 3-tap bilinear shader, which downscales by 6X in one dimension.
-TEST_P(GLScalerShaderPixelTest, ThreeTapBilinear) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- RunMultiplePassBilinearTest(Shader::BILINEAR3, 6,
- {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0xbf, 0x00, 0x80, 0xff),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
- SkColorSetARGB(0xbf, 0xff, 0x80, 0x00),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff)});
-}
-
-// Test the 4-tap bilinear shader, which downscales by 8X in one dimension.
-TEST_P(GLScalerShaderPixelTest, FourTapBilinear) {
- RunMultiplePassBilinearTest(Shader::BILINEAR4, 8,
- {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff),
- SkColorSetARGB(0xff, 0xff, 0xff, 0x00),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x80),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x80),
- SkColorSetARGB(0xff, 0xff, 0x00, 0xff)});
-}
-
-// Test the 2-by-2-tap bilinear shader, which downscales by 4X in both
-// dimensions at the same time.
-TEST_P(GLScalerShaderPixelTest, TwoByTwoTapBilinear) {
- RunMultiplePassBilinearTest(Shader::BILINEAR2X2, 4,
- {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
- SkColorSetARGB(0x7f, 0x00, 0x80, 0x00),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff)});
-}
-
-// Tests the bicubic upscaler for a variety of scaling factors between 1X and
-// 2X, and over the entire source texture versus just its lower-right quadrant.
-TEST_P(GLScalerShaderPixelTest, BicubicUpscale) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- constexpr gfx::Rect whole = gfx::Rect(kBaseSize);
- constexpr gfx::Rect quadrant =
- gfx::Rect(kBaseSize.width() / 2, kBaseSize.height() / 2,
- kBaseSize.width() / 2, kBaseSize.height() / 2);
- const std::vector<SMPTEScalingTestCase> kTestCases = {
- // No scaling.
- {whole, 1, 1, 0},
- // Upscale by 4/3.
- {whole, 3, 4, 3},
- // Upscale by 3/2.
- {whole, 2, 3, 3},
- // Upscale by 2X.
- {whole, 1, 2, 3},
- // No scaling; lower-right quadrant only.
- {quadrant, 1, 1, 0},
- // Upscale by 4/3.
- {quadrant, 3, 4, 3},
- // Upscale by 3/2.
- {quadrant, 2, 3, 3},
- // Upscale by 2X.
- {quadrant, 1, 2, 3},
- };
-
- RunSMPTEScalingTestCases(Shader::BICUBIC_UPSCALE, kTestCases);
-}
-
-// Tests the bicubic half-downscaler, both over an entire source texture and
-// over just its lower-right quadrant.
-TEST_P(GLScalerShaderPixelTest, BicubicDownscaleByHalf) {
- constexpr gfx::Rect whole = gfx::Rect(kBaseSize);
- constexpr gfx::Rect quadrant =
- gfx::Rect(kBaseSize.width() / 2, kBaseSize.height() / 2,
- kBaseSize.width() / 2, kBaseSize.height() / 2);
- const std::vector<SMPTEScalingTestCase> kTestCases = {
- // Downscale by half.
- {whole, 2, 1, 2},
- // Downscale by half; lower-right quadrant only.
- {quadrant, 2, 1, 2},
- };
-
- RunSMPTEScalingTestCases(Shader::BICUBIC_HALF_1D, kTestCases);
-}
-
-// Tests the shaders that read a normal 4-channel interleaved texture and
-// produce a planar texture consisting of just one color channel, packed into
-// RGBA quads.
-TEST_P(GLScalerShaderPixelTest, Export_Planar) {
- // Disabled on Marshmallow. See crbug.com/933080
- if (IsAndroidMarshmallow())
- return;
-
- const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
- SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff)};
- SkBitmap source = CreateCyclicalTestImage(kBaseSize, STAGGERED, kCycle, 0);
- const GLuint src_texture = UploadTexture(source);
-
- // For each channel, create an expected bitmap and compare it to the result
- // from drawing with the shader program.
- if (is_converting_rgb_to_yuv()) {
- ConvertRGBABitmapToYUV(&source);
- }
- for (int channel = 0; channel <= 3; ++channel) {
- SkBitmap expected = CreatePackedPlanarBitmap(source, channel);
- if (is_swizzling_output()) {
- SwizzleBitmap(&expected);
- }
-
- const Shader shader = static_cast<Shader>(
- static_cast<int>(Shader::PLANAR_CHANNEL_0) + channel);
- const gfx::Size dst_size(kBaseSize.width() / 4, kBaseSize.height());
- GetShaderProgram(shader)->UseProgram(kBaseSize,
- gfx::RectF(gfx::Rect(kBaseSize)),
- dst_size, Axis::HORIZONTAL, false);
- const SkBitmap actual =
- DownloadTexture(RenderToNewTexture(src_texture, dst_size), dst_size);
- ExpectAreTheSameImage(expected, actual);
- }
-}
-
-// Tests that the I422/NV61 formatter shader program produces a planar texture
-// and an interleaved half-width texture from a normal 4-channel interleaved
-// texture. See gl_shader.h for more specifics.
-TEST_P(GLScalerShaderPixelTest, Export_I422_NV61) {
- if (!AreMultipleRenderingTargetsSupported()) {
- LOG(WARNING) << "Skipping test due to lack of MRT support on this machine.";
- return;
- }
-
- // Use a vertical stripes source image/texture to test that the shader is
- // sampling the texture at the correct points and performing
- // downscale-blending in the second texture.
- const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
- SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff)};
- SkBitmap source =
- CreateCyclicalTestImage(kBaseSize, VERTICAL_STRIPES, kCycle, 0);
- const GLuint src_texture = UploadTexture(source);
-
- // Create the expected output images: The first (A) is simply the first color
- // channel of the source packed into a planar format. The second (BC) consists
- // of the second and third color channels downscaled by half and interleaved.
- // The following can be considered a reference implementation for what the
- // shader program is supposed to do.
- if (is_converting_rgb_to_yuv()) {
- ConvertRGBABitmapToYUV(&source);
- }
- SkBitmap expected_a = CreatePackedPlanarBitmap(source, 0);
- if (is_swizzling_output()) {
- SwizzleBitmap(&expected_a);
- }
- const gfx::Size dst_size(expected_a.width(), expected_a.height());
- SkBitmap expected_bc = AllocateRGBABitmap(dst_size);
- for (int y = 0; y < dst_size.height(); ++y) {
- const uint32_t* const src = source.getAddr32(0, y);
- uint32_t* const dst_bc = expected_bc.getAddr32(0, y);
- for (int x = 0; x < dst_size.width(); ++x) {
- // (src[0..3]) (dst_bc)
- // RGBA RGBA rgba rgba --> GBgb (e.g, two G's blended into one G)
- const uint32_t g01 = ((((src[x * 4 + 0] >> kGreenShift) & 0xff) +
- ((src[x * 4 + 1] >> kGreenShift) & 0xff)) /
- 2.f +
- 0.5f);
- const uint32_t b01 = ((((src[x * 4 + 0] >> kBlueShift) & 0xff) +
- ((src[x * 4 + 1] >> kBlueShift) & 0xff)) /
- 2.f +
- 0.5f);
- const uint32_t g23 = ((((src[x * 4 + 2] >> kGreenShift) & 0xff) +
- ((src[x * 4 + 3] >> kGreenShift) & 0xff)) /
- 2.f +
- 0.5f);
- const uint32_t b23 = ((((src[x * 4 + 2] >> kBlueShift) & 0xff) +
- ((src[x * 4 + 3] >> kBlueShift) & 0xff)) /
- 2.f +
- 0.5f);
- dst_bc[x] = ((g01 << kRedShift) | (b01 << kGreenShift) |
- (g23 << kBlueShift) | (b23 << kAlphaShift));
- }
- }
- if (is_swizzling_output()) {
- SwizzleBitmap(&expected_bc);
- }
-
- // Execute the program, and compare the shader program's drawn result with the
- // expected images.
- GetShaderProgram(Shader::I422_NV61_MRT)
- ->UseProgram(kBaseSize, gfx::RectF(gfx::Rect(kBaseSize)), dst_size,
- Axis::HORIZONTAL, false);
- const auto textures = RenderToNewTextures(src_texture, dst_size, true);
- const SkBitmap actual_a = DownloadTexture(textures.first, dst_size);
- ExpectAreTheSameImage(expected_a, actual_a);
- const SkBitmap actual_bc = DownloadTexture(textures.second, dst_size);
- ExpectAreTheSameImage(expected_bc, actual_bc);
-}
-
-// Tests that the PLANAR_CHANNELS_1_2 formatter shader program produces an
-// interleaved half-width texture from a normal 4-channel interleaved texture.
-// See gl_shader.h for more specifics.
-TEST_P(GLScalerShaderPixelTest, Export_PLANAR_CHANNELS_1_2) {
- // Use a vertical stripes source image/texture to test that the shader is
- // sampling the texture at the correct points and performing
- // downscale-blending in the second texture.
- const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
- SkColorSetARGB(0x80, 0x00, 0x80, 0x00),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff)};
- SkBitmap source =
- CreateCyclicalTestImage(kBaseSize, VERTICAL_STRIPES, kCycle, 0);
- const GLuint src_texture = UploadTexture(source);
-
- // Create the expected output image: it consists of the second and third
- // color channels (BC) downscaled by half and interleaved.
- // The following can be considered a reference implementation for what the
- // shader program is supposed to do.
- if (is_converting_rgb_to_yuv()) {
- ConvertRGBABitmapToYUV(&source);
- }
-
- // 4x1 pixels get converted to a packed 1x1 pixel.
- const gfx::Size dst_size(source.width() / 4, source.height());
- SkBitmap expected_bc = AllocateRGBABitmap(dst_size);
- for (int y = 0; y < dst_size.height(); ++y) {
- const uint32_t* const src = source.getAddr32(0, y);
- uint32_t* const dst_bc = expected_bc.getAddr32(0, y);
- for (int x = 0; x < dst_size.width(); ++x) {
- // (src[0..3]) (dst_bc)
- // RGBA RGBA rgba rgba --> GBgb (e.g, two G's blended into one G)
- const uint32_t g01 = ((((src[x * 4 + 0] >> kGreenShift) & 0xff) +
- ((src[x * 4 + 1] >> kGreenShift) & 0xff)) /
- 2.f +
- 0.5f);
- const uint32_t b01 = ((((src[x * 4 + 0] >> kBlueShift) & 0xff) +
- ((src[x * 4 + 1] >> kBlueShift) & 0xff)) /
- 2.f +
- 0.5f);
- const uint32_t g23 = ((((src[x * 4 + 2] >> kGreenShift) & 0xff) +
- ((src[x * 4 + 3] >> kGreenShift) & 0xff)) /
- 2.f +
- 0.5f);
- const uint32_t b23 = ((((src[x * 4 + 2] >> kBlueShift) & 0xff) +
- ((src[x * 4 + 3] >> kBlueShift) & 0xff)) /
- 2.f +
- 0.5f);
- dst_bc[x] = ((g01 << kRedShift) | (b01 << kGreenShift) |
- (g23 << kBlueShift) | (b23 << kAlphaShift));
- }
- }
- if (is_swizzling_output()) {
- SwizzleBitmap(&expected_bc);
- }
-
- // Execute the program, and compare the shader program's drawn result with the
- // expected images.
- GetShaderProgram(Shader::PLANAR_CHANNELS_1_2)
- ->UseProgram(kBaseSize, gfx::RectF(gfx::Rect(kBaseSize)), dst_size,
- Axis::HORIZONTAL, false);
- const auto texture = RenderToNewTexture(src_texture, dst_size);
- const SkBitmap actual_bc = DownloadTexture(texture, dst_size);
- ExpectAreTheSameImage(expected_bc, actual_bc);
-}
-
-// Tests the pairwise-deinterleave shader program that produces two planar
-// textures from a single interleaved one.
-TEST_P(GLScalerShaderPixelTest, Export_PairwiseDeinterleave) {
- if (!AreMultipleRenderingTargetsSupported()) {
- LOG(WARNING) << "Skipping test due to lack of MRT support on this machine.";
- return;
- }
-
- // This shader does not provide color space conversion. It is just a
- // demultiplexer/repackager.
- if (is_converting_rgb_to_yuv()) {
- return;
- }
-
- // Create a source image/texture with a pattern suitable for ensuring the
- // shader is sampling the texture at the correct points.
- const std::vector<SkColor> kCycle = {SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0xc0, 0x00, 0xc0, 0x00),
- SkColorSetARGB(0x80, 0x00, 0x00, 0x80),
- SkColorSetARGB(0xff, 0xff, 0xff, 0xff)};
- const SkBitmap source =
- CreateCyclicalTestImage(kBaseSize, STAGGERED, kCycle, 0);
- const GLuint src_texture = UploadTexture(source);
-
- // Create the expected pair of planar images.
- const gfx::Size dst_size(kBaseSize.width() / 2, kBaseSize.height());
- SkBitmap expected_a = AllocateRGBABitmap(dst_size);
- SkBitmap expected_b = AllocateRGBABitmap(dst_size);
- for (int y = 0; y < dst_size.height(); ++y) {
- const uint32_t* const src = source.getAddr32(0, y);
- uint32_t* const dst_a = expected_a.getAddr32(0, y);
- uint32_t* const dst_b = expected_b.getAddr32(0, y);
- for (int x = 0; x < dst_size.width(); ++x) {
- // (src) (dst_a) (dst_b)
- // ABAB abab --> { AAaa + BBbb }
- dst_a[x] = ((((src[x * 2 + 0] >> kRedShift) & 0xff) << kRedShift) |
- (((src[x * 2 + 0] >> kBlueShift) & 0xff) << kGreenShift) |
- (((src[x * 2 + 1] >> kRedShift) & 0xff) << kBlueShift) |
- (((src[x * 2 + 1] >> kBlueShift) & 0xff) << kAlphaShift));
- dst_b[x] = ((((src[x * 2 + 0] >> kGreenShift) & 0xff) << kRedShift) |
- (((src[x * 2 + 0] >> kAlphaShift) & 0xff) << kGreenShift) |
- (((src[x * 2 + 1] >> kGreenShift) & 0xff) << kBlueShift) |
- (((src[x * 2 + 1] >> kAlphaShift) & 0xff) << kAlphaShift));
- }
- }
- if (is_swizzling_output()) {
- SwizzleBitmap(&expected_a);
- SwizzleBitmap(&expected_b);
- }
-
- // Execute the program, and compare the shader program's drawn result with the
- // expected images.
- GetShaderProgram(Shader::DEINTERLEAVE_PAIRWISE_MRT)
- ->UseProgram(kBaseSize, gfx::RectF(gfx::Rect(kBaseSize)), dst_size,
- Axis::HORIZONTAL, false);
- const auto textures = RenderToNewTextures(src_texture, dst_size, true);
- const SkBitmap actual_a = DownloadTexture(textures.first, dst_size);
- ExpectAreTheSameImage(expected_a, actual_a);
- const SkBitmap actual_b = DownloadTexture(textures.second, dst_size);
- ExpectAreTheSameImage(expected_b, actual_b);
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
- GLScalerShaderPixelTest,
- testing::Combine(testing::Bool(), testing::Bool()));
-
-} // namespace viz
diff --git a/chromium/components/viz/common/gl_scaler_unittest.cc b/chromium/components/viz/common/gl_scaler_unittest.cc
deleted file mode 100644
index 67cab05ba02..00000000000
--- a/chromium/components/viz/common/gl_scaler_unittest.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/common/gl_scaler.h"
-
-#include "cc/test/pixel_test.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/test/gl_scaler_test_util.h"
-#include "gpu/GLES2/gl2chromium.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "gpu/command_buffer/common/capabilities.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::ByRef;
-using ::testing::Eq;
-using ::testing::Mock;
-using ::testing::NiceMock;
-using ::testing::NotNull;
-using ::testing::Return;
-using ::testing::ReturnRef;
-using ::testing::SaveArg;
-using ::testing::Sequence;
-
-namespace viz {
-namespace {
-
-class MockContextProvider : public ContextProvider {
- public:
- MockContextProvider() {
- ON_CALL(*this, ContextGL())
- .WillByDefault(
- Return(reinterpret_cast<gpu::gles2::GLES2Interface*>(0xdeadbeef)));
- ON_CALL(*this, ContextCapabilities()).WillByDefault(ReturnRef(caps_));
- }
-
- MOCK_METHOD1(AddObserver, void(ContextLostObserver* obs));
- MOCK_METHOD1(RemoveObserver, void(ContextLostObserver* obs));
- MOCK_CONST_METHOD0(ContextCapabilities, const gpu::Capabilities&());
- MOCK_METHOD0(ContextGL, gpu::gles2::GLES2Interface*());
-
- // Stubbed-out, because the tests just stack-allocate this object.
- void AddRef() const final {}
- void Release() const final {}
-
- private:
- gpu::Capabilities caps_;
-
- // Other ContextProvider methods; but stubbed-out because they are never
- // called.
- gpu::ContextResult BindToCurrentThread() final {
- NOTREACHED();
- return gpu::ContextResult::kSuccess;
- }
- base::Lock* GetLock() final {
- NOTREACHED();
- return nullptr;
- }
- ContextCacheController* CacheController() final {
- NOTREACHED();
- return nullptr;
- }
- gpu::ContextSupport* ContextSupport() final {
- NOTREACHED();
- return nullptr;
- }
- class GrDirectContext* GrContext() final {
- NOTREACHED();
- return nullptr;
- }
- gpu::SharedImageInterface* SharedImageInterface() final {
- NOTREACHED();
- return nullptr;
- }
- const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const final {
- NOTREACHED();
- return *reinterpret_cast<gpu::GpuFeatureInfo*>(0xdeadbeef);
- }
-};
-
-class GLScalerTest : public cc::PixelTest {
- protected:
- void SetUp() final {
- cc::PixelTest::SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft);
- }
-
- void TearDown() final { cc::PixelTest::TearDown(); }
-};
-
-TEST_F(GLScalerTest, AddAndRemovesSelfAsContextLossObserver) {
- NiceMock<MockContextProvider> provider;
- ContextLostObserver* registered_observer = nullptr;
- Sequence s;
- EXPECT_CALL(provider, AddObserver(NotNull()))
- .InSequence(s)
- .WillOnce(SaveArg<0>(&registered_observer));
- EXPECT_CALL(provider, RemoveObserver(Eq(ByRef(registered_observer))))
- .InSequence(s);
- GLScaler scaler(&provider);
-}
-
-TEST_F(GLScalerTest, RemovesObserverWhenContextIsLost) {
- NiceMock<MockContextProvider> provider;
- ContextLostObserver* registered_observer = nullptr;
- Sequence s;
- EXPECT_CALL(provider, AddObserver(NotNull()))
- .InSequence(s)
- .WillOnce(SaveArg<0>(&registered_observer));
- EXPECT_CALL(provider, RemoveObserver(Eq(ByRef(registered_observer))))
- .InSequence(s);
- GLScaler scaler(&provider);
- static_cast<ContextLostObserver&>(scaler).OnContextLost();
- // Verify RemoveObserver() was called before |scaler| goes out-of-scope.
- Mock::VerifyAndClearExpectations(&provider);
-}
-
-TEST_F(GLScalerTest, StopsScalingWhenContextIsLost) {
- GLScaler scaler(context_provider());
-
- // Configure the scaler with default parameters (1:1 scale ratio).
- ASSERT_TRUE(scaler.Configure(GLScaler::Parameters()));
-
- // Call Scale() and expect it to return true to indicate the operation
- // succeeded.
- GLScalerTestTextureHelper helper(context_provider()->ContextGL());
- constexpr gfx::Size kSomeSize = gfx::Size(32, 32);
- const GLuint src_texture = helper.CreateTexture(kSomeSize);
- const GLuint dest_texture = helper.CreateTexture(kSomeSize);
- EXPECT_TRUE(scaler.Scale(src_texture, kSomeSize, gfx::Vector2d(),
- dest_texture, gfx::Rect(kSomeSize)));
-
- // After the context is lost, another call to Scale() should return false.
- static_cast<ContextLostObserver&>(scaler).OnContextLost();
- EXPECT_FALSE(scaler.Scale(src_texture, kSomeSize, gfx::Vector2d(),
- dest_texture, gfx::Rect(kSomeSize)));
-}
-
-TEST_F(GLScalerTest, Configure_RequiresValidScalingVectors) {
- GLScaler scaler(context_provider());
-
- GLScaler::Parameters params;
- EXPECT_TRUE(scaler.Configure(params));
-
- for (int i = 0; i < 4; ++i) {
- params.scale_from = gfx::Vector2d(i == 0 ? 0 : 1, i == 1 ? 0 : 1);
- params.scale_to = gfx::Vector2d(i == 2 ? 0 : 1, i == 3 ? 0 : 1);
- EXPECT_FALSE(scaler.Configure(params));
- }
-}
-
-TEST_F(GLScalerTest, Configure_ResolvesUnspecifiedColorSpaces) {
- GLScaler scaler(context_provider());
-
- // Neither source nor output space specified: Both should resolve to sRGB.
- GLScaler::Parameters params;
- EXPECT_TRUE(scaler.Configure(params));
- const auto srgb = gfx::ColorSpace::CreateSRGB();
- EXPECT_EQ(srgb, scaler.params().source_color_space);
- EXPECT_EQ(srgb, scaler.params().output_color_space);
- EXPECT_TRUE(GLScaler::ParametersAreEquivalent(params, scaler.params()));
-
- // Source space set to XYZD50 with no output space specified: Both should
- // resolve to XYZD50.
- const auto xyzd50 = gfx::ColorSpace::CreateXYZD50();
- params.source_color_space = xyzd50;
- EXPECT_TRUE(scaler.Configure(params));
- EXPECT_EQ(xyzd50, scaler.params().source_color_space);
- EXPECT_EQ(xyzd50, scaler.params().output_color_space);
- EXPECT_TRUE(GLScaler::ParametersAreEquivalent(params, scaler.params()));
-
- // Source space set to XYZD50 with output space set to P3D65: Nothing should
- // change.
- const auto p3d65 = gfx::ColorSpace::CreateDisplayP3D65();
- params.output_color_space = p3d65;
- EXPECT_TRUE(scaler.Configure(params));
- EXPECT_EQ(xyzd50, scaler.params().source_color_space);
- EXPECT_EQ(p3d65, scaler.params().output_color_space);
- EXPECT_TRUE(GLScaler::ParametersAreEquivalent(params, scaler.params()));
-}
-
-TEST_F(GLScalerTest, Configure_RequiresValidSwizzles) {
- GLScaler scaler(context_provider());
- GLScaler::Parameters params;
-
- // Test that all valid combinations work.
- for (int i = 0; i < 4; ++i) {
- params.swizzle[0] = (i % 2 == 0) ? GL_RGBA : GL_BGRA_EXT;
- params.swizzle[1] = (i / 2 == 0) ? GL_RGBA : GL_BGRA_EXT;
- EXPECT_TRUE(scaler.Configure(params)) << "i=" << i;
- }
-
- // Test that invalid combinations don't work.
- for (int i = 1; i < 4; ++i) {
- params.swizzle[0] = (i % 2 == 0) ? GL_RGBA : GL_RGB;
- params.swizzle[1] = (i / 2 == 0) ? GL_RGBA : GL_RGB;
- EXPECT_FALSE(scaler.Configure(params)) << "i=" << i;
- }
-}
-
-TEST_F(GLScalerTest, DetectsEquivalentScaleRatios) {
- GLScaler::Parameters params;
- EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(params, gfx::Vector2d(1, 1),
- gfx::Vector2d(1, 1)));
- EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(
- params, gfx::Vector2d(15, 15), gfx::Vector2d(15, 15)));
-
- params.scale_from = gfx::Vector2d(2, 1);
- EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(params, gfx::Vector2d(2, 1),
- gfx::Vector2d(1, 1)));
- EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(
- params, gfx::Vector2d(30, 15), gfx::Vector2d(15, 15)));
-
- params.scale_from = gfx::Vector2d(1, 2);
- EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(params, gfx::Vector2d(1, 2),
- gfx::Vector2d(1, 1)));
- EXPECT_TRUE(GLScaler::ParametersHasSameScaleRatio(
- params, gfx::Vector2d(15, 30), gfx::Vector2d(15, 15)));
-
- params.scale_from = gfx::Vector2d(2, 1);
- EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio(
- params, gfx::Vector2d(1, 2), gfx::Vector2d(1, 1)));
- EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio(
- params, gfx::Vector2d(15, 30), gfx::Vector2d(15, 15)));
-
- params.scale_from = gfx::Vector2d(1, 2);
- EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio(
- params, gfx::Vector2d(2, 1), gfx::Vector2d(1, 1)));
- EXPECT_FALSE(GLScaler::ParametersHasSameScaleRatio(
- params, gfx::Vector2d(30, 15), gfx::Vector2d(15, 15)));
-}
-
-} // namespace
-} // namespace viz
diff --git a/chromium/components/viz/common/gpu/context_cache_controller.cc b/chromium/components/viz/common/gpu/context_cache_controller.cc
index 80425d801e6..9086c6e42cd 100644
--- a/chromium/components/viz/common/gpu/context_cache_controller.cc
+++ b/chromium/components/viz/common/gpu/context_cache_controller.cc
@@ -11,6 +11,7 @@
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/synchronization/lock.h"
+#include "base/time/time.h"
#include "gpu/command_buffer/client/context_support.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
diff --git a/chromium/components/viz/common/overlay_state/win/DEPS b/chromium/components/viz/common/overlay_state/win/DEPS
new file mode 100644
index 00000000000..19fb5f11f6f
--- /dev/null
+++ b/chromium/components/viz/common/overlay_state/win/DEPS
@@ -0,0 +1,8 @@
+# Please consult components/viz/README.md about allowable dependencies.
+
+include_rules = [
+ "+components/viz/test",
+ "+gpu/command_buffer",
+ "+gpu/ipc/common",
+ "+mojo/public/cpp/bindings",
+]
diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.cc b/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.cc
new file mode 100644
index 00000000000..bebe165d095
--- /dev/null
+++ b/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.cc
@@ -0,0 +1,28 @@
+// Copyright 2022 The Chromium 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/viz/common/overlay_state/win/overlay_state_aggregator.h"
+
+namespace viz {
+
+bool OverlayStateAggregator::SetPromotionHint(bool promoted) {
+ OverlayStateAggregator::PromotionState recommendation =
+ promoted ? OverlayStateAggregator::PromotionState::kPromoted
+ : OverlayStateAggregator::PromotionState::kNotPromoted;
+
+ if (recommendation != promotion_state_) {
+ promotion_state_ = recommendation;
+ return true;
+ }
+
+ // No change.
+ return false;
+}
+
+OverlayStateAggregator::PromotionState
+OverlayStateAggregator::GetPromotionState() {
+ return promotion_state_;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.h b/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.h
new file mode 100644
index 00000000000..7fc87afa568
--- /dev/null
+++ b/chromium/components/viz/common/overlay_state/win/overlay_state_aggregator.h
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_AGGREGATOR_H_
+#define COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_AGGREGATOR_H_
+
+namespace viz {
+
+class OverlayStateAggregator {
+ public:
+ OverlayStateAggregator() = default;
+ ~OverlayStateAggregator() = default;
+
+ enum class PromotionState { kUnset, kNotPromoted, kPromoted };
+
+ // Sets a new promotion hint & returns whether the promotion recommendation
+ // state has changed.
+ bool SetPromotionHint(bool promoted);
+ // Gets current promotion recommendation.
+ PromotionState GetPromotionState();
+
+ private:
+ PromotionState promotion_state_ = PromotionState::kUnset;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_AGGREGATOR_H_
diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_service.cc b/chromium/components/viz/common/overlay_state/win/overlay_state_service.cc
new file mode 100644
index 00000000000..38edb2d39ff
--- /dev/null
+++ b/chromium/components/viz/common/overlay_state/win/overlay_state_service.cc
@@ -0,0 +1,184 @@
+// Copyright 2022 The Chromium 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/viz/common/overlay_state/win/overlay_state_service.h"
+
+#include <utility>
+
+#include "base/task/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+
+namespace viz {
+
+OverlayStateService::MailboxState::MailboxState() = default;
+OverlayStateService::MailboxState::~MailboxState() = default;
+OverlayStateService::OverlayStateService() = default;
+OverlayStateService::~OverlayStateService() = default;
+
+OverlayStateService* OverlayStateService::GetInstance() {
+ // TODO(wicarr, crbug.com/1316009): Ideally the OverlayStateService should be
+ // a singleton. Instead the GpuServiceImpl should be responsible for creating
+ // the OverlayStateService and injecting it into dependent GpuChannel(s) and
+ // the DCLayerOverlayProcessor. Further the OverlayStateService should live
+ // in gpu to avoid gpu needing to take a dependency on viz.
+ static base::NoDestructor<OverlayStateService> service_wrapper;
+ return service_wrapper.get();
+}
+
+void OverlayStateService::Initialize(
+ scoped_refptr<base::SequencedTaskRunner> task_runner) {
+ // GpuServiceImpl is expected to initialize the OverlayStateService and
+ // OverlayStateService will operate on the GpuMain sequenced task runner.
+ // RegisterObserver is expected to be called by GpuChannel and should operate
+ // on the same GpuMain sequence as GpuServiceImpl.
+ // SetPromotionHint is called by DCLayerOverlayProcessor which operates on a
+ // separate task sequence, VizCompositorThread, so calls are posted to
+ // 'task_runner_' to allow mojo'ing back out to bound PromotionHintObserver
+ // clients on the proper sequence.
+ DCHECK(!initialized_);
+ task_runner_ = std::move(task_runner);
+ initialized_ = true;
+}
+
+bool OverlayStateService::IsInitialized() {
+ return initialized_;
+}
+
+void OverlayStateService::RegisterObserver(
+ mojo::PendingRemote<gpu::mojom::OverlayStateObserver>
+ overlay_state_observer,
+ const gpu::Mailbox& mailbox) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ MailboxState*& mailbox_state = mailboxes_[mailbox];
+ if (!mailbox_state) {
+ mailbox_state = new MailboxState();
+ // Use of base::Unretained is safe as OverlayStateService is a singleton
+ // service bound to the lifetime of the GPU process.
+ mailbox_state->observer_set_.set_disconnect_handler(
+ base::BindRepeating(&OverlayStateService::OnBoundObserverDisconnect,
+ base::Unretained(this), mailbox));
+ }
+
+ // Add observer to the RemoteSet
+ mojo::RemoteSetElementId id =
+ mailbox_state->observer_set_.Add(std::move(overlay_state_observer));
+
+ // It's possible that the overlay processor has already set promotion hint
+ // information for this mailbox. If this is the case then we send a Hint
+ // Changed event to the observer to inform them of the current promotion
+ // state.
+ OverlayStateAggregator::PromotionState promotion_state =
+ mailbox_state->aggregator_.GetPromotionState();
+
+ // If promotion state is unset there is no further work to do. If it is set
+ // then inform the new observer of the current state.
+ if (promotion_state != OverlayStateAggregator::PromotionState::kUnset) {
+ bool promoted =
+ promotion_state == OverlayStateAggregator::PromotionState::kPromoted;
+ mailbox_state->observer_set_.Get(id)->OnStateChanged(promoted);
+ }
+}
+
+void OverlayStateService::OnBoundObserverDisconnect(const gpu::Mailbox& mailbox,
+ mojo::RemoteSetElementId) {
+ auto mailbox_iterator = mailboxes_.find(mailbox);
+ if (mailbox_iterator != mailboxes_.end() &&
+ mailbox_iterator->second->observer_set_.empty()) {
+ // When the last observer has been disconnected, stop tracking mailbox.
+ mailboxes_.erase(mailbox_iterator);
+ }
+}
+
+void OverlayStateService::OnStateChanged(
+ const gpu::Mailbox& mailbox,
+ OverlayStateAggregator::PromotionState promotion_state) {
+ // If promotion state is unset there is no further work to do.
+ if (promotion_state == OverlayStateAggregator::PromotionState::kUnset)
+ return;
+
+ if (!task_runner_->RunsTasksInCurrentSequence()) {
+ // Use of base::Unretained is safe as OverlayStateService is a singleton
+ // service bound to the lifetime of the GPU process.
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&OverlayStateService::OnStateChangedOnTaskRunnerSequence,
+ base::Unretained(this), mailbox, promotion_state));
+ } else {
+ OnStateChangedOnTaskRunnerSequence(mailbox, promotion_state);
+ }
+}
+
+void OverlayStateService::OnStateChangedOnTaskRunnerSequence(
+ const gpu::Mailbox& mailbox,
+ OverlayStateAggregator::PromotionState promotion_state) {
+ // Notify all observers of the new hint state.
+ bool promoted =
+ promotion_state == OverlayStateAggregator::PromotionState::kPromoted;
+ auto mailbox_iterator = mailboxes_.find(mailbox);
+ DCHECK(mailbox_iterator != mailboxes_.end());
+ for (auto& observer : mailbox_iterator->second->observer_set_) {
+ observer->OnStateChanged(promoted);
+ }
+}
+
+void OverlayStateService::SetPromotionHint(const gpu::Mailbox& mailbox,
+ bool promoted) {
+ DCHECK(!task_runner_->RunsTasksInCurrentSequence());
+ // Use of base::Unretained is safe as OverlayStateService is a singleton
+ // service bound to the lifetime of the GPU process.
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&OverlayStateService::SetPromotionHintOnTaskRunnerSequence,
+ base::Unretained(this), mailbox, promoted));
+}
+
+void OverlayStateService::SetPromotionHintOnTaskRunnerSequence(
+ const gpu::Mailbox& mailbox,
+ bool promoted) {
+ // The OverlayStateService is made aware of mailboxes of interest through two
+ // channels:
+ // 1.) The registration of an observer for a mailbox.
+ // 2.) The setting of a promotion hint for a mailbox (the overlay processor
+ // will only send hints for mailboxes which are tagged as
+ // 'wants_promotion_hint' on their TransferResource).
+ //
+ // If this is the first promotion hint associated with a mailbox which has
+ // not had an observer registered yet, then we will not have an existing
+ // entry in 'mailboxes_' tracking it. Since there is no guarantee when we'll
+ // receive another promotion hint for the mailbox we'll store the result so
+ // any future observer can be informed of the current promotion state.
+ auto mailbox_iterator = mailboxes_.find(mailbox);
+ if (mailbox_iterator != mailboxes_.end()) {
+ bool state_change =
+ mailbox_iterator->second->aggregator_.SetPromotionHint(promoted);
+ if (state_change) {
+ // Notifying observers requires an IPC so we only send an update when
+ // the underlying state changes.
+ OnStateChanged(mailbox,
+ mailbox_iterator->second->aggregator_.GetPromotionState());
+ }
+ } else {
+ MailboxState* new_mailbox_state = new MailboxState();
+ new_mailbox_state->aggregator_.SetPromotionHint(promoted);
+ mailboxes_.insert({mailbox, new_mailbox_state});
+ }
+}
+
+void OverlayStateService::MailboxDestroyed(const gpu::Mailbox& mailbox) {
+ DCHECK(!task_runner_->RunsTasksInCurrentSequence());
+ // Use of base::Unretained is safe as OverlayStateService is a singleton
+ // service bound to the lifetime of the GPU process.
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&OverlayStateService::MailboxDestroyedOnTaskRunnerSequence,
+ base::Unretained(this), mailbox));
+}
+
+void OverlayStateService::MailboxDestroyedOnTaskRunnerSequence(
+ const gpu::Mailbox& mailbox) {
+ mailboxes_.erase(mailbox);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_service.h b/chromium/components/viz/common/overlay_state/win/overlay_state_service.h
new file mode 100644
index 00000000000..50c95d0f371
--- /dev/null
+++ b/chromium/components/viz/common/overlay_state/win/overlay_state_service.h
@@ -0,0 +1,83 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_SERVICE_H_
+#define COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_SERVICE_H_
+
+#include "base/no_destructor.h"
+#include "components/viz/common/overlay_state/win/overlay_state_aggregator.h"
+#include "components/viz/common/viz_common_export.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/ipc/common/gpu_channel.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
+
+namespace viz {
+
+using OverlayStateObserver = gpu::mojom::OverlayStateObserver;
+
+// The OverlayStateService allows a MediaFoundationRendererClient, running in
+// a Renderer process, to understand whether the quads associated with its
+// video presentation textures are being promoted to a Direct Composition layer
+// by Viz. This allows the MediaFoundationRendererClient to determine the
+// appropriate presentation mode to use as the Windowless Swapchain mode
+// requires Direct Composition support to work.
+//
+// The mailbox associated with a texture is used as the common identifier
+// between the overlay processor in Viz & the MediaFoundationRendererClient.
+// Further the quad associated with the mailbox has it's associated
+// TransferResource tagged with 'wants_promotion_hint' to ensure that the
+// overlay processor only sends the OverlayStateService hints for mailboxes of
+// interest.
+//
+// The OverlayStateService aggregates hints to help ensure minimal IPC overhead
+// in keeping the MediaFoundationRendererClient informed of the current
+// promotion state.
+class VIZ_COMMON_EXPORT OverlayStateService {
+ public:
+ static OverlayStateService* GetInstance();
+ void Initialize(scoped_refptr<base::SequencedTaskRunner> task_runner);
+ bool IsInitialized();
+
+ void RegisterObserver(mojo::PendingRemote<gpu::mojom::OverlayStateObserver>
+ promotion_hint_observer,
+ const gpu::Mailbox& mailbox);
+ void SetPromotionHint(const gpu::Mailbox& mailbox, bool promoted);
+ void MailboxDestroyed(const gpu::Mailbox& mailbox);
+
+ private:
+ friend class base::NoDestructor<OverlayStateService>;
+ OverlayStateService();
+ ~OverlayStateService();
+ OverlayStateService(const OverlayStateService&) = delete;
+ OverlayStateService& operator=(const OverlayStateService&) = delete;
+
+ void OnBoundObserverDisconnect(const gpu::Mailbox& mailbox,
+ mojo::RemoteSetElementId);
+ void OnStateChanged(const gpu::Mailbox& mailbox,
+ OverlayStateAggregator::PromotionState promoted);
+ void OnStateChangedOnTaskRunnerSequence(
+ const gpu::Mailbox& mailbox,
+ OverlayStateAggregator::PromotionState promoted);
+ void SetPromotionHintOnTaskRunnerSequence(const gpu::Mailbox& mailbox,
+ bool promoted);
+ void MailboxDestroyedOnTaskRunnerSequence(const gpu::Mailbox& mailbox);
+
+ struct MailboxState {
+ MailboxState();
+ ~MailboxState();
+ OverlayStateAggregator aggregator_;
+ mojo::RemoteSet<OverlayStateObserver> observer_set_;
+ };
+
+ bool initialized_ = false;
+ base::flat_map<gpu::Mailbox, MailboxState*> mailboxes_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_OVERLAY_STATE_WIN_OVERLAY_STATE_SERVICE_H_
diff --git a/chromium/components/viz/common/overlay_state/win/overlay_state_service_unittest.cc b/chromium/components/viz/common/overlay_state/win/overlay_state_service_unittest.cc
new file mode 100644
index 00000000000..dcd05e86023
--- /dev/null
+++ b/chromium/components/viz/common/overlay_state/win/overlay_state_service_unittest.cc
@@ -0,0 +1,195 @@
+// Copyright 2022 The Chromium 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 <utility>
+
+#include "base/task/single_thread_task_runner.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread.h"
+#include "components/viz/common/overlay_state/win/overlay_state_service.h"
+#include "components/viz/test/viz_test_suite.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/ipc/common/gpu_channel.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Combine;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Values;
+
+namespace viz {
+
+class OverlayStateServiceUnittest : public testing::Test,
+ public gpu::mojom::OverlayStateObserver {
+ public:
+ OverlayStateServiceUnittest()
+ : receiver_(this), other_thread_("Other Thread") {}
+ // PromotionHintObserver
+ void OnStateChanged(bool promoted) override;
+ void SetUp() override;
+
+ protected:
+ void PerformRegistration(const gpu::Mailbox& mailbox);
+ void SetService();
+
+ // Helper functions for running SetPromotionHint & MailboxDestroyed calls to
+ // the OverlayStateService on a separate task sequence.
+ base::OnceClosure SetPromotionHintClosure(const gpu::Mailbox& mailbox,
+ bool promoted);
+ void SetPromotionHint(const gpu::Mailbox& mailbox, bool promoted);
+ void SetPromotionHintWorker(const gpu::Mailbox& mailbox, bool promoted);
+ base::OnceClosure DestroyMailboxClosure(const gpu::Mailbox& mailbox);
+ void DestroyMailbox(const gpu::Mailbox& mailbox);
+ void DestroyMailboxWorker(const gpu::Mailbox& mailbox);
+
+ // In order to simulate the OverlayStateService running on GpuMain and the
+ // DCLayerOverlayProcessor running on VizCompositorMain we create a separate
+ // thread & task runner where SetPromotionHint & DestroyMailbox calls are
+ // executed from.
+ scoped_refptr<base::SingleThreadTaskRunner> other_thread_task_runner_;
+
+ raw_ptr<OverlayStateService> service_;
+ mojo::Receiver<gpu::mojom::OverlayStateObserver> receiver_;
+ int hint_received_ = 0;
+ bool last_promote_value_ = false;
+
+ private:
+ base::Thread other_thread_;
+};
+
+void OverlayStateServiceUnittest::SetUp() {
+ other_thread_.StartAndWaitForTesting();
+ other_thread_task_runner_ = other_thread_.task_runner();
+}
+
+void OverlayStateServiceUnittest::OnStateChanged(bool promoted) {
+ hint_received_++;
+ last_promote_value_ = promoted;
+}
+
+void OverlayStateServiceUnittest::PerformRegistration(
+ const gpu::Mailbox& mailbox) {
+ service_->RegisterObserver(receiver_.BindNewPipeAndPassRemote(), mailbox);
+}
+
+void OverlayStateServiceUnittest::SetService() {
+ service_ = OverlayStateService::GetInstance();
+ if (!service_->IsInitialized()) {
+ service_->Initialize(base::SequencedTaskRunnerHandle::Get());
+ }
+}
+
+void OverlayStateServiceUnittest::SetPromotionHint(const gpu::Mailbox& mailbox,
+ bool promoted) {
+ base::RunLoop run_loop;
+ other_thread_task_runner_->PostTask(
+ FROM_HERE, SetPromotionHintClosure(std::move(mailbox), promoted));
+ other_thread_task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(run_loop.QuitClosure()));
+ run_loop.Run();
+}
+
+base::OnceClosure OverlayStateServiceUnittest::SetPromotionHintClosure(
+ const gpu::Mailbox& mailbox,
+ bool promoted) {
+ return base::BindOnce(&OverlayStateServiceUnittest::SetPromotionHintWorker,
+ base::Unretained(this), std::move(mailbox), promoted);
+}
+
+void OverlayStateServiceUnittest::SetPromotionHintWorker(
+ const gpu::Mailbox& mailbox,
+ bool promoted) {
+ service_->SetPromotionHint(mailbox, promoted);
+}
+
+void OverlayStateServiceUnittest::DestroyMailbox(const gpu::Mailbox& mailbox) {
+ base::RunLoop run_loop;
+ other_thread_task_runner_->PostTask(
+ FROM_HERE, DestroyMailboxClosure(std::move(mailbox)));
+ other_thread_task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(run_loop.QuitClosure()));
+ run_loop.Run();
+}
+
+base::OnceClosure OverlayStateServiceUnittest::DestroyMailboxClosure(
+ const gpu::Mailbox& mailbox) {
+ return base::BindOnce(&OverlayStateServiceUnittest::DestroyMailboxWorker,
+ base::Unretained(this), std::move(mailbox));
+}
+
+void OverlayStateServiceUnittest::DestroyMailboxWorker(
+ const gpu::Mailbox& mailbox) {
+ service_->MailboxDestroyed(mailbox);
+}
+
+TEST_F(OverlayStateServiceUnittest, ServiceSingleton) {
+ OverlayStateService* service = OverlayStateService::GetInstance();
+ EXPECT_NE(service, nullptr);
+
+ OverlayStateService* service_instance_2 = OverlayStateService::GetInstance();
+ EXPECT_EQ(service, service_instance_2);
+}
+
+TEST_F(OverlayStateServiceUnittest, AddObserver) {
+ SetService();
+
+ // Add observer for a new mailbox
+ gpu::Mailbox mailbox = gpu::Mailbox::Generate();
+ PerformRegistration(mailbox);
+
+ // Add observer for existing mailbox with set promotion state
+ gpu::Mailbox mailbox2 = gpu::Mailbox::Generate();
+ SetPromotionHint(mailbox2, true);
+ VizTestSuite::RunUntilIdle();
+ receiver_.reset();
+ PerformRegistration(mailbox2);
+ VizTestSuite::RunUntilIdle();
+ EXPECT_EQ(hint_received_, 1);
+ EXPECT_EQ(last_promote_value_, true);
+}
+
+TEST_F(OverlayStateServiceUnittest, SetHint) {
+ SetService();
+ gpu::Mailbox mailbox = gpu::Mailbox::Generate();
+ PerformRegistration(mailbox);
+ VizTestSuite::RunUntilIdle();
+ SetPromotionHint(mailbox, true);
+ VizTestSuite::RunUntilIdle();
+ EXPECT_EQ(hint_received_, 1);
+ EXPECT_EQ(last_promote_value_, true);
+ SetPromotionHint(mailbox, false);
+ VizTestSuite::RunUntilIdle();
+ EXPECT_EQ(hint_received_, 2);
+ EXPECT_EQ(last_promote_value_, false);
+}
+
+TEST_F(OverlayStateServiceUnittest, DeleteMailbox) {
+ SetService();
+ gpu::Mailbox mailbox = gpu::Mailbox::Generate();
+ PerformRegistration(mailbox);
+ VizTestSuite::RunUntilIdle();
+ SetPromotionHint(mailbox, true);
+ VizTestSuite::RunUntilIdle();
+ EXPECT_EQ(hint_received_, 1);
+ EXPECT_EQ(last_promote_value_, true);
+ // Tell the OverlayStateService the mailbox has been destroyed, but
+ // don't actually destroy the mailbox for testing purposes as we want to
+ // ensure another mailbox with the same identifier is treated as a separate
+ // entity.
+ DestroyMailbox(mailbox);
+ // Send another promotion hint for the mailbox - we expect that we will not
+ // receive a hint changed callback this time because when the mailbox was
+ // "destroyed" any registered observers should have been removed.
+ SetPromotionHint(mailbox, false);
+ VizTestSuite::RunUntilIdle();
+ EXPECT_EQ(hint_received_, 1);
+ EXPECT_EQ(last_promote_value_, true);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/quads/compositor_render_pass_unittest.cc b/chromium/components/viz/common/quads/compositor_render_pass_unittest.cc
index c6d54156909..dcc1d09ddd6 100644
--- a/chromium/components/viz/common/quads/compositor_render_pass_unittest.cc
+++ b/chromium/components/viz/common/quads/compositor_render_pass_unittest.cc
@@ -333,7 +333,7 @@ TEST(CompositorRenderPassTest, ReplacedQuadsGetColor) {
quad->SetNew(quad_state, quad_rect, quad_rect, SK_ColorRED, false);
pass->ReplaceExistingQuadWithSolidColor(pass->quad_list.begin(),
SK_ColorGREEN, SkBlendMode::kSrcOver);
- EXPECT_EQ(SK_ColorGREEN, quad->color);
+ EXPECT_EQ(SkColors::kGreen, quad->color);
}
TEST(CompositorRenderPassTest, ReplacedQuadsGetBlendMode) {
diff --git a/chromium/components/viz/common/quads/debug_border_draw_quad.cc b/chromium/components/viz/common/quads/debug_border_draw_quad.cc
index f519911e3be..ef991ec8a21 100644
--- a/chromium/components/viz/common/quads/debug_border_draw_quad.cc
+++ b/chromium/components/viz/common/quads/debug_border_draw_quad.cc
@@ -20,7 +20,8 @@ void DebugBorderDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
bool needs_blending = SkColorGetA(c) < 255;
DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kDebugBorder, rect,
visible_rect, needs_blending);
- color = c;
+ // TODO(crbug/1308932) remove FromColor and make all SkColor4f
+ color = SkColor4f::FromColor(c);
width = w;
}
@@ -32,7 +33,8 @@ void DebugBorderDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
int w) {
DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kDebugBorder, rect,
visible_rect, needs_blending);
- color = c;
+ // TODO(crbug/1308932) remove FromColor and make all SkColor4f
+ color = SkColor4f::FromColor(c);
width = w;
}
@@ -44,7 +46,8 @@ const DebugBorderDrawQuad* DebugBorderDrawQuad::MaterialCast(
void DebugBorderDrawQuad::ExtendValue(
base::trace_event::TracedValue* value) const {
- value->SetString("color", color_utils::SkColorToRgbaString(color));
+ value->SetString("color",
+ color_utils::SkColorToRgbaString(color.toSkColor()));
value->SetInteger("width", width);
}
diff --git a/chromium/components/viz/common/quads/debug_border_draw_quad.h b/chromium/components/viz/common/quads/debug_border_draw_quad.h
index 8772645f855..9d8119e6912 100644
--- a/chromium/components/viz/common/quads/debug_border_draw_quad.h
+++ b/chromium/components/viz/common/quads/debug_border_draw_quad.h
@@ -28,7 +28,7 @@ class VIZ_COMMON_EXPORT DebugBorderDrawQuad : public DrawQuad {
SkColor c,
int w);
- SkColor color = SK_ColorTRANSPARENT;
+ SkColor4f color = SkColors::kTransparent;
int width = 0;
static const DebugBorderDrawQuad* MaterialCast(const DrawQuad*);
diff --git a/chromium/components/viz/common/quads/draw_quad_unittest.cc b/chromium/components/viz/common/quads/draw_quad_unittest.cc
index 8f0a24327e1..ca9c0f29d06 100644
--- a/chromium/components/viz/common/quads/draw_quad_unittest.cc
+++ b/chromium/components/viz/common/quads/draw_quad_unittest.cc
@@ -179,12 +179,12 @@ TEST(DrawQuadTest, CopyDebugBorderDrawQuad) {
CREATE_QUAD_NEW(DebugBorderDrawQuad, visible_rect, color, width);
EXPECT_EQ(DrawQuad::Material::kDebugBorder, copy_quad->material);
EXPECT_EQ(visible_rect, copy_quad->visible_rect);
- EXPECT_EQ(color, copy_quad->color);
+ EXPECT_EQ(SkColor4f::FromColor(color), copy_quad->color);
EXPECT_EQ(width, copy_quad->width);
CREATE_QUAD_ALL(DebugBorderDrawQuad, color, width);
EXPECT_EQ(DrawQuad::Material::kDebugBorder, copy_quad->material);
- EXPECT_EQ(color, copy_quad->color);
+ EXPECT_EQ(SkColor4f::FromColor(color), copy_quad->color);
EXPECT_EQ(width, copy_quad->width);
}
@@ -226,6 +226,7 @@ TEST(DrawQuadTest, CopyRenderPassDrawQuad) {
TEST(DrawQuadTest, CopySolidColorDrawQuad) {
gfx::Rect visible_rect(40, 50, 30, 20);
+ // TODO(crbug.com/1308932): Use SkColor4f here
SkColor color = 0x49494949;
bool force_anti_aliasing_off = false;
CREATE_SHARED_STATE();
@@ -234,12 +235,12 @@ TEST(DrawQuadTest, CopySolidColorDrawQuad) {
force_anti_aliasing_off);
EXPECT_EQ(DrawQuad::Material::kSolidColor, copy_quad->material);
EXPECT_EQ(visible_rect, copy_quad->visible_rect);
- EXPECT_EQ(color, copy_quad->color);
+ EXPECT_EQ(SkColor4f::FromColor(color), copy_quad->color);
EXPECT_EQ(force_anti_aliasing_off, copy_quad->force_anti_aliasing_off);
CREATE_QUAD_ALL(SolidColorDrawQuad, color, force_anti_aliasing_off);
EXPECT_EQ(DrawQuad::Material::kSolidColor, copy_quad->material);
- EXPECT_EQ(color, copy_quad->color);
+ EXPECT_EQ(SkColor4f::FromColor(color), copy_quad->color);
EXPECT_EQ(force_anti_aliasing_off, copy_quad->force_anti_aliasing_off);
}
diff --git a/chromium/components/viz/common/quads/render_pass_io.cc b/chromium/components/viz/common/quads/render_pass_io.cc
index 51f57f46f3c..4a3fa5d6c68 100644
--- a/chromium/components/viz/common/quads/render_pass_io.cc
+++ b/chromium/components/viz/common/quads/render_pass_io.cc
@@ -160,6 +160,50 @@ bool PointFromDict(const base::Value& dict, gfx::Point* point) {
return true;
}
+base::Value SkColor4fToDict(const SkColor4f color) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetDoubleKey("red", color.fR);
+ dict.SetDoubleKey("green", color.fG);
+ dict.SetDoubleKey("blue", color.fB);
+ dict.SetDoubleKey("alpha", color.fA);
+ return dict;
+}
+
+bool SkColor4fFromDict(const base::Value& dict, SkColor4f* color) {
+ DCHECK(color);
+ if (!dict.is_dict())
+ return false;
+ absl::optional<double> red = dict.FindDoubleKey("red");
+ absl::optional<double> green = dict.FindDoubleKey("green");
+ absl::optional<double> blue = dict.FindDoubleKey("blue");
+ absl::optional<double> alpha = dict.FindDoubleKey("alpha");
+ if (!red || !green || !blue || !alpha)
+ return false;
+ color->fR = static_cast<float>(red.value());
+ color->fG = static_cast<float>(green.value());
+ color->fB = static_cast<float>(blue.value());
+ color->fA = static_cast<float>(alpha.value());
+ return true;
+}
+
+// Many quads now store color as an SkColor4f, but older logs will still store
+// SkColors (which are ints). For backward compatibility's sake, read either.
+bool ColorFromDict(const base::Value& dict,
+ base::StringPiece key,
+ SkColor& output_color) {
+ const base::Value* color_key = dict.FindDictKey(key);
+ SkColor4f color_4f;
+ if (!color_key || !SkColor4fFromDict(*color_key, &color_4f)) {
+ absl::optional<int> color_int = dict.FindIntKey(key);
+ if (!color_int)
+ return false;
+ output_color = static_cast<SkColor>(color_int.value());
+ return true;
+ }
+ output_color = color_4f.toSkColor();
+ return true;
+}
+
base::Value PointFToDict(const gfx::PointF& point) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetDoubleKey("x", point.x());
@@ -467,7 +511,7 @@ base::Value FilterOperationToDict(const cc::FilterOperation& filter) {
dict.SetKey("drop_shadow_offset",
PointToDict(filter.drop_shadow_offset()));
dict.SetIntKey("drop_shadow_color",
- bit_cast<int>(filter.drop_shadow_color()));
+ base::bit_cast<int>(filter.drop_shadow_color()));
break;
case cc::FilterOperation::REFERENCE:
dict.SetStringKey("image_filter",
@@ -540,7 +584,7 @@ bool FilterOperationFromDict(const base::Value& dict,
}
filter.set_drop_shadow_offset(offset);
filter.set_drop_shadow_color(
- bit_cast<SkColor>(drop_shadow_color.value()));
+ base::bit_cast<SkColor>(drop_shadow_color.value()));
} break;
case cc::FilterOperation::REFERENCE:
if (!image_filter)
@@ -649,6 +693,7 @@ const char* ColorSpaceTransferIdToString(gfx::ColorSpace::TransferID id) {
MATCH_ENUM_CASE(TransferID, CUSTOM)
MATCH_ENUM_CASE(TransferID, CUSTOM_HDR)
MATCH_ENUM_CASE(TransferID, PIECEWISE_HDR)
+ MATCH_ENUM_CASE(TransferID, SCRGB_LINEAR_80_NITS)
}
}
@@ -729,6 +774,7 @@ uint8_t StringToColorSpaceTransferId(const std::string& token) {
MATCH_ENUM_CASE(TransferID, CUSTOM)
MATCH_ENUM_CASE(TransferID, CUSTOM_HDR)
MATCH_ENUM_CASE(TransferID, PIECEWISE_HDR)
+ MATCH_ENUM_CASE(TransferID, SCRGB_LINEAR_80_NITS)
return -1;
}
@@ -1131,7 +1177,7 @@ void SolidColorDrawQuadToDict(const SolidColorDrawQuad* draw_quad,
base::Value* dict) {
DCHECK(draw_quad);
DCHECK(dict);
- dict->SetIntKey("color", static_cast<int>(draw_quad->color));
+ dict->SetKey("color", SkColor4fToDict(draw_quad->color));
dict->SetBoolKey("force_anti_aliasing_off",
draw_quad->force_anti_aliasing_off);
}
@@ -1178,8 +1224,8 @@ void SurfaceDrawQuadToDict(const SurfaceDrawQuad* draw_quad,
DCHECK(draw_quad);
DCHECK(dict);
dict->SetKey("surface_range", SurfaceRangeToDict(draw_quad->surface_range));
- dict->SetIntKey("default_background_color",
- bit_cast<int>(draw_quad->default_background_color));
+ dict->SetKey("default_background_color",
+ SkColor4fToDict(draw_quad->default_background_color));
dict->SetBoolKey("stretch_content",
draw_quad->stretch_content_to_fill_bounds);
dict->SetBoolKey("is_reflection", draw_quad->is_reflection);
@@ -1193,8 +1239,8 @@ void TextureDrawQuadToDict(const TextureDrawQuad* draw_quad,
dict->SetBoolKey("premultiplied_alpha", draw_quad->premultiplied_alpha);
dict->SetKey("uv_top_left", PointFToDict(draw_quad->uv_top_left));
dict->SetKey("uv_bottom_right", PointFToDict(draw_quad->uv_bottom_right));
- dict->SetIntKey("background_color",
- static_cast<int>(draw_quad->background_color));
+ dict->SetKey("background_color",
+ SkColor4fToDict(draw_quad->background_color));
dict->SetKey("vertex_opacity", FloatArrayToList(draw_quad->vertex_opacity));
dict->SetBoolKey("y_flipped", draw_quad->y_flipped);
dict->SetBoolKey("nearest_neighbor", draw_quad->nearest_neighbor);
@@ -1355,13 +1401,17 @@ bool SolidColorDrawQuadFromDict(const base::Value& dict,
DCHECK(draw_quad);
if (!dict.is_dict())
return false;
- absl::optional<int> color = dict.FindIntKey("color");
absl::optional<bool> force_anti_aliasing_off =
dict.FindBoolKey("force_anti_aliasing_off");
- if (!color || !force_anti_aliasing_off)
+ if (!force_anti_aliasing_off)
return false;
+
+ SkColor t_color;
+ if (!ColorFromDict(dict, "color", t_color))
+ return false;
+
draw_quad->SetAll(common.shared_quad_state, common.rect, common.visible_rect,
- common.needs_blending, static_cast<SkColor>(color.value()),
+ common.needs_blending, t_color,
force_anti_aliasing_off.value());
return true;
}
@@ -1413,19 +1463,21 @@ bool SurfaceDrawQuadFromDict(const base::Value& dict,
return false;
absl::optional<SurfaceRange> surface_range =
SurfaceRangeFromDict(*surface_range_dict);
- absl::optional<int> default_background_color =
- dict.FindIntKey("default_background_color");
absl::optional<bool> stretch_content = dict.FindBoolKey("stretch_content");
absl::optional<bool> is_reflection = dict.FindBoolKey("is_reflection");
absl::optional<bool> allow_merge = dict.FindBoolKey("allow_merge");
- if (!surface_range || !default_background_color || !stretch_content ||
- !is_reflection || !allow_merge)
+ if (!surface_range || !stretch_content || !is_reflection || !allow_merge)
+ return false;
+
+ SkColor t_default_background_color;
+ if (!ColorFromDict(dict, "default_background_color",
+ t_default_background_color))
return false;
draw_quad->SetAll(common.shared_quad_state, common.rect, common.visible_rect,
common.needs_blending, *surface_range,
- bit_cast<SkColor>(*default_background_color),
- *stretch_content, *is_reflection, *allow_merge);
+ t_default_background_color, *stretch_content,
+ *is_reflection, *allow_merge);
return true;
}
@@ -1442,7 +1494,6 @@ bool TextureDrawQuadFromDict(const base::Value& dict,
dict.FindBoolKey("premultiplied_alpha");
const base::Value* uv_top_left = dict.FindDictKey("uv_top_left");
const base::Value* uv_bottom_right = dict.FindDictKey("uv_bottom_right");
- absl::optional<int> background_color = dict.FindIntKey("background_color");
const base::Value* vertex_opacity = dict.FindListKey("vertex_opacity");
const base::Value* damage_rect = dict.FindDictKey("damage_rect");
absl::optional<bool> y_flipped = dict.FindBoolKey("y_flipped");
@@ -1455,7 +1506,7 @@ bool TextureDrawQuadFromDict(const base::Value& dict,
dict.FindDictKey("resource_size_in_pixels");
if (!premultiplied_alpha || !uv_top_left || !uv_bottom_right ||
- !background_color || !vertex_opacity || !y_flipped || !nearest_neighbor ||
+ !vertex_opacity || !y_flipped || !nearest_neighbor ||
!secure_output_only || !protected_video_type ||
!resource_size_in_pixels) {
return false;
@@ -1466,9 +1517,11 @@ bool TextureDrawQuadFromDict(const base::Value& dict,
return false;
gfx::PointF t_uv_top_left, t_uv_bottom_right;
gfx::Size t_resource_size_in_pixels;
+ SkColor t_background_color;
if (!PointFFromDict(*uv_top_left, &t_uv_top_left) ||
!PointFFromDict(*uv_bottom_right, &t_uv_bottom_right) ||
- !SizeFromDict(*resource_size_in_pixels, &t_resource_size_in_pixels)) {
+ !SizeFromDict(*resource_size_in_pixels, &t_resource_size_in_pixels) ||
+ !ColorFromDict(dict, "background_color", t_background_color)) {
return false;
}
float t_vertex_opacity[4];
@@ -1480,8 +1533,8 @@ bool TextureDrawQuadFromDict(const base::Value& dict,
common.shared_quad_state, common.rect, common.visible_rect,
common.needs_blending, resource_id, t_resource_size_in_pixels,
premultiplied_alpha.value(), t_uv_top_left, t_uv_bottom_right,
- static_cast<SkColor>(background_color.value()), t_vertex_opacity,
- y_flipped.value(), nearest_neighbor.value(), secure_output_only.value(),
+ t_background_color, t_vertex_opacity, y_flipped.value(),
+ nearest_neighbor.value(), secure_output_only.value(),
static_cast<gfx::ProtectedVideoType>(protected_video_type_index));
gfx::Rect t_damage_rect;
diff --git a/chromium/components/viz/common/quads/shared_quad_state.cc b/chromium/components/viz/common/quads/shared_quad_state.cc
index a96dcaff5cc..b2d89498de6 100644
--- a/chromium/components/viz/common/quads/shared_quad_state.cc
+++ b/chromium/components/viz/common/quads/shared_quad_state.cc
@@ -48,9 +48,15 @@ void SharedQuadState::AsValueInto(base::trace_event::TracedValue* value) const {
visible_quad_layer_rect, value);
cc::MathUtil::AddToTracedValue("mask_filter_bounds",
mask_filter_info.bounds(), value);
- cc::MathUtil::AddCornerRadiiToTracedValue(
- "mask_filter_rounded_corners_radii",
- mask_filter_info.rounded_corner_bounds(), value);
+ if (mask_filter_info.HasRoundedCorners()) {
+ cc::MathUtil::AddCornerRadiiToTracedValue(
+ "mask_filter_rounded_corners_radii",
+ mask_filter_info.rounded_corner_bounds(), value);
+ }
+ if (mask_filter_info.HasGradientMask()) {
+ cc::MathUtil::AddToTracedValue("mask_filter_gradient_mask",
+ mask_filter_info.gradient_mask(), value);
+ }
if (clip_rect) {
cc::MathUtil::AddToTracedValue("clip_rect", *clip_rect, value);
diff --git a/chromium/components/viz/common/quads/solid_color_draw_quad.cc b/chromium/components/viz/common/quads/solid_color_draw_quad.cc
index b69a713edf8..c7029b7ec9d 100644
--- a/chromium/components/viz/common/quads/solid_color_draw_quad.cc
+++ b/chromium/components/viz/common/quads/solid_color_draw_quad.cc
@@ -11,7 +11,7 @@
namespace viz {
SolidColorDrawQuad::SolidColorDrawQuad()
- : color(0), force_anti_aliasing_off(false) {}
+ : color(SkColors::kTransparent), force_anti_aliasing_off(false) {}
void SolidColorDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
const gfx::Rect& rect,
@@ -21,7 +21,7 @@ void SolidColorDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
bool needs_blending = SkColorGetA(c) != 255;
DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kSolidColor, rect,
visible_rect, needs_blending);
- color = c;
+ color = SkColor4f::FromColor(c);
force_anti_aliasing_off = anti_aliasing_off;
}
@@ -33,7 +33,7 @@ void SolidColorDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
bool anti_aliasing_off) {
DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kSolidColor, rect,
visible_rect, needs_blending);
- color = c;
+ color = SkColor4f::FromColor(c);
force_anti_aliasing_off = anti_aliasing_off;
}
@@ -45,7 +45,7 @@ const SolidColorDrawQuad* SolidColorDrawQuad::MaterialCast(
void SolidColorDrawQuad::ExtendValue(
base::trace_event::TracedValue* value) const {
- value->SetString("color", color_utils::SkColorToRgbaString(color));
+ value->SetString("color", color_utils::SkColor4fToRgbaString(color));
value->SetBoolean("force_anti_aliasing_off", force_anti_aliasing_off);
}
diff --git a/chromium/components/viz/common/quads/solid_color_draw_quad.h b/chromium/components/viz/common/quads/solid_color_draw_quad.h
index 129a06562fd..0d5b3ceedf1 100644
--- a/chromium/components/viz/common/quads/solid_color_draw_quad.h
+++ b/chromium/components/viz/common/quads/solid_color_draw_quad.h
@@ -28,8 +28,8 @@ class VIZ_COMMON_EXPORT SolidColorDrawQuad : public DrawQuad {
SkColor c,
bool anti_aliasing_off);
- SkColor color;
- bool force_anti_aliasing_off;
+ SkColor4f color;
+ bool force_anti_aliasing_off = false;
static const SolidColorDrawQuad* MaterialCast(const DrawQuad*);
diff --git a/chromium/components/viz/common/quads/surface_draw_quad.cc b/chromium/components/viz/common/quads/surface_draw_quad.cc
index 3eaf425e7bf..beb51546b9c 100644
--- a/chromium/components/viz/common/quads/surface_draw_quad.cc
+++ b/chromium/components/viz/common/quads/surface_draw_quad.cc
@@ -30,7 +30,8 @@ void SurfaceDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kSurfaceContent, rect,
visible_rect, needs_blending);
surface_range = range;
- default_background_color = background_color;
+ // TODO(crbug/1308932) remove FromColor and make all SkColor4f
+ default_background_color = SkColor4f::FromColor(background_color);
stretch_content_to_fill_bounds = stretch_content;
}
@@ -46,7 +47,8 @@ void SurfaceDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kSurfaceContent, rect,
visible_rect, needs_blending);
surface_range = range;
- default_background_color = background_color;
+ // TODO(crbug/1308932) remove FromColor and make all SkColor4f
+ default_background_color = SkColor4f::FromColor(background_color);
stretch_content_to_fill_bounds = stretch_content;
is_reflection = reflection;
allow_merge = merge;
diff --git a/chromium/components/viz/common/quads/surface_draw_quad.h b/chromium/components/viz/common/quads/surface_draw_quad.h
index 3516469da17..740aa89fd73 100644
--- a/chromium/components/viz/common/quads/surface_draw_quad.h
+++ b/chromium/components/viz/common/quads/surface_draw_quad.h
@@ -38,7 +38,7 @@ class VIZ_COMMON_EXPORT SurfaceDrawQuad : public DrawQuad {
bool merge);
SurfaceRange surface_range;
- SkColor default_background_color = SK_ColorWHITE;
+ SkColor4f default_background_color = SkColors::kWhite;
bool stretch_content_to_fill_bounds = false;
bool is_reflection = false;
// If true, allows this surface to be merged into the embedding surface,
diff --git a/chromium/components/viz/common/quads/texture_draw_quad.cc b/chromium/components/viz/common/quads/texture_draw_quad.cc
index 617acd367a4..3686ed4607c 100644
--- a/chromium/components/viz/common/quads/texture_draw_quad.cc
+++ b/chromium/components/viz/common/quads/texture_draw_quad.cc
@@ -53,7 +53,7 @@ void TextureDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
premultiplied_alpha = premultiplied;
uv_top_left = top_left;
uv_bottom_right = bottom_right;
- background_color = background;
+ background_color = SkColor4f::FromColor(background);
vertex_opacity[0] = opacity[0];
vertex_opacity[1] = opacity[1];
vertex_opacity[2] = opacity[2];
@@ -87,7 +87,7 @@ void TextureDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
premultiplied_alpha = premultiplied;
uv_top_left = top_left;
uv_bottom_right = bottom_right;
- background_color = background;
+ background_color = SkColor4f::FromColor(background);
vertex_opacity[0] = opacity[0];
vertex_opacity[1] = opacity[1];
vertex_opacity[2] = opacity[2];
@@ -112,7 +112,7 @@ void TextureDrawQuad::ExtendValue(base::trace_event::TracedValue* value) const {
cc::MathUtil::AddToTracedValue("uv_bottom_right", uv_bottom_right, value);
value->SetString("background_color",
- color_utils::SkColorToRgbaString(background_color));
+ color_utils::SkColor4fToRgbaString(background_color));
value->BeginArray("vertex_opacity");
for (size_t i = 0; i < 4; ++i)
diff --git a/chromium/components/viz/common/quads/texture_draw_quad.h b/chromium/components/viz/common/quads/texture_draw_quad.h
index 3f8ecefa1a4..6a9d0982c0c 100644
--- a/chromium/components/viz/common/quads/texture_draw_quad.h
+++ b/chromium/components/viz/common/quads/texture_draw_quad.h
@@ -61,7 +61,7 @@ class VIZ_COMMON_EXPORT TextureDrawQuad : public DrawQuad {
gfx::PointF uv_top_left;
gfx::PointF uv_bottom_right;
- SkColor background_color = SK_ColorTRANSPARENT;
+ SkColor4f background_color = SkColors::kTransparent;
float vertex_opacity[4] = {0, 0, 0, 0};
bool y_flipped : 1;
bool nearest_neighbor : 1;
diff --git a/chromium/components/viz/common/resources/resource_format_utils.cc b/chromium/components/viz/common/resources/resource_format_utils.cc
index 52dc3be16a3..7e93dcb77e4 100644
--- a/chromium/components/viz/common/resources/resource_format_utils.cc
+++ b/chromium/components/viz/common/resources/resource_format_utils.cc
@@ -266,44 +266,6 @@ unsigned int GLInternalFormat(ResourceFormat format) {
}
}
-unsigned int GLCopyTextureInternalFormat(ResourceFormat format) {
- // In GLES2, valid formats for glCopyTexImage2D are: GL_ALPHA, GL_LUMINANCE,
- // GL_LUMINANCE_ALPHA, GL_RGB, or GL_RGBA.
- // Extensions typically used for glTexImage2D do not also work for
- // glCopyTexImage2D. For instance GL_BGRA_EXT may not be used for
- // anything but gl(Sub)TexImage2D:
- // https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_format_BGRA8888.txt
- DCHECK_LE(format, RESOURCE_FORMAT_MAX);
- static const GLenum format_gl_data_format[] = {
- GL_RGBA, // RGBA_8888
- GL_RGBA, // RGBA_4444
- GL_RGBA, // BGRA_8888
- GL_ALPHA, // ALPHA_8
- GL_LUMINANCE, // LUMINANCE_8
- GL_RGB, // RGB_565
- GL_RGB, // BGR_565
- GL_RGB, // ETC1
- GL_LUMINANCE, // RED_8
- GL_RGBA, // RG_88
- GL_LUMINANCE, // LUMINANCE_F16
- GL_RGBA, // RGBA_F16
- GL_LUMINANCE, // R16_EXT
- GL_RGBA, // RG16_EXT
- GL_RGB, // RGBX_8888
- GL_RGB, // BGRX_8888
- GL_ZERO, // RGBA_1010102
- GL_ZERO, // BGRA_1010102
- GL_ZERO, // YVU_420
- GL_ZERO, // YUV_420_BIPLANAR
- GL_ZERO, // P010
- };
-
- static_assert(std::size(format_gl_data_format) == (RESOURCE_FORMAT_MAX + 1),
- "format_gl_data_format does not handle all cases.");
-
- return format_gl_data_format[format];
-}
-
gfx::BufferFormat BufferFormat(ResourceFormat format) {
switch (format) {
case BGRA_8888:
diff --git a/chromium/components/viz/common/resources/resource_format_utils.h b/chromium/components/viz/common/resources/resource_format_utils.h
index fe2ee221a20..ae1328b9e6b 100644
--- a/chromium/components/viz/common/resources/resource_format_utils.h
+++ b/chromium/components/viz/common/resources/resource_format_utils.h
@@ -36,8 +36,6 @@ SkColorTypeToResourceFormat(SkColorType color_type);
VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLDataType(ResourceFormat format);
VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLDataFormat(ResourceFormat format);
VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLInternalFormat(ResourceFormat format);
-VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLCopyTextureInternalFormat(
- ResourceFormat format);
// Returns the pixel format of the resource when mapped into client-side memory.
// Returns a default value when IsGpuMemoryBufferFormatSupported() returns false
diff --git a/chromium/components/viz/common/resources/transferable_resource.h b/chromium/components/viz/common/resources/transferable_resource.h
index 0a360bc3ade..4f07426d0db 100644
--- a/chromium/components/viz/common/resources/transferable_resource.h
+++ b/chromium/components/viz/common/resources/transferable_resource.h
@@ -27,6 +27,22 @@ namespace viz {
struct ReturnedResource;
struct VIZ_COMMON_EXPORT TransferableResource {
+ enum class SynchronizationType : uint8_t {
+ // Commands issued (SyncToken) - a resource can be reused as soon as display
+ // compositor issues the latest command on it and SyncToken will be signaled
+ // when this happens.
+ kSyncToken = 0,
+ // Commands completed (aka read lock fence) - If a gpu resource is backed by
+ // a GpuMemoryBuffer, then it will be accessed out-of-band, and a gpu fence
+ // needs to be waited on before the resource is returned and reused. In
+ // other words, the resource will be returned only when gpu commands are
+ // completed.
+ kGpuCommandsCompleted,
+ // Commands submitted (release fence) - a resource will be returned after
+ // gpu service submitted commands to the gpu and provide the fence.
+ kReleaseFence,
+ };
+
TransferableResource();
~TransferableResource();
@@ -104,10 +120,10 @@ struct VIZ_COMMON_EXPORT TransferableResource {
// drawing it. Typically GL_LINEAR, or GL_NEAREST if no anti-aliasing
// during scaling is desired.
uint32_t filter = 0;
- // If a gpu resource is backed by a GpuMemoryBuffer, then it will be accessed
- // out-of-band, and a gpu fence needs to be waited on before the resource is
- // returned and reused.
- bool read_lock_fences_enabled = false;
+
+ // This defines when the display compositor returns resources. Clients may use
+ // different synchronization types based on their needs.
+ SynchronizationType synchronization_type = SynchronizationType::kSyncToken;
// YCbCr info for resources backed by YCbCr Vulkan images.
absl::optional<gpu::VulkanYCbCrInfo> ycbcr_info;
@@ -145,7 +161,7 @@ struct VIZ_COMMON_EXPORT TransferableResource {
#elif BUILDFLAG(IS_WIN)
wants_promotion_hint == o.wants_promotion_hint &&
#endif
- read_lock_fences_enabled == o.read_lock_fences_enabled;
+ synchronization_type == o.synchronization_type;
}
bool operator!=(const TransferableResource& o) const { return !(*this == o); }
};
diff --git a/chromium/components/viz/common/surfaces/local_surface_id_unittest.cc b/chromium/components/viz/common/surfaces/local_surface_id_unittest.cc
index 4a07df62e94..943d4693a86 100644
--- a/chromium/components/viz/common/surfaces/local_surface_id_unittest.cc
+++ b/chromium/components/viz/common/surfaces/local_surface_id_unittest.cc
@@ -38,6 +38,7 @@ TEST(LocalSurfaceIdTest, VerifyToString) {
int previous_log_lvl = logging::GetMinLogLevel();
+#if BUILDFLAG(USE_RUNTIME_VLOG)
// When |g_min_log_level| is set to LOG_VERBOSE we expect verbose versions
// of local_surface_id::ToString().
logging::SetMinLogLevel(logging::LOG_VERBOSE);
@@ -45,6 +46,7 @@ TEST(LocalSurfaceIdTest, VerifyToString) {
EXPECT_EQ(verbose_expected, local_surface_id.ToString());
EXPECT_EQ(big_verbose_expected, big_local_surface_id.ToString());
EXPECT_EQ(small_verbose_expected, small_local_surface_id.ToString());
+#endif // BUILDFLAG(USE_RUNTIME_VLOG)
// When |g_min_log_level| is set to LOG_INFO we expect less verbose versions
// of local_surface_id::ToString().
diff --git a/chromium/components/viz/common/switches.cc b/chromium/components/viz/common/switches.cc
index e6d3acbf514..c5cf3aab5df 100644
--- a/chromium/components/viz/common/switches.cc
+++ b/chromium/components/viz/common/switches.cc
@@ -29,10 +29,6 @@ const char kDeadlineToSynchronizeSurfaces[] =
// Also implies --disable-gpu-vsync (see //ui/gl/gl_switches.h).
const char kDisableFrameRateLimit[] = "disable-frame-rate-limit";
-// Slows down animations during a DocumentTransition for debugging.
-const char kDocumentTransitionSlowdownFactor[] =
- "document-transition-slowdown-factor";
-
// Sets the number of max pending frames in the GL buffer queue to 1.
const char kDoubleBufferCompositing[] = "double-buffer-compositing";
@@ -87,18 +83,4 @@ absl::optional<uint32_t> GetDeadlineToSynchronizeSurfaces() {
return activation_deadline_in_frames;
}
-int GetDocumentTransitionSlowDownFactor() {
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- if (!command_line ||
- !command_line->HasSwitch(kDocumentTransitionSlowdownFactor))
- return 1;
-
- auto factor_str =
- command_line->GetSwitchValueASCII(kDocumentTransitionSlowdownFactor);
- int factor = 0;
- LOG_IF(ERROR, !base::StringToInt(factor_str, &factor))
- << "Error parsing document transition slow down factor " << factor_str;
- return std::max(1, factor);
-}
-
} // namespace switches
diff --git a/chromium/components/viz/common/switches.h b/chromium/components/viz/common/switches.h
index b76565e96c5..a602615d40b 100644
--- a/chromium/components/viz/common/switches.h
+++ b/chromium/components/viz/common/switches.h
@@ -17,7 +17,6 @@ namespace switches {
VIZ_COMMON_EXPORT extern const char kDeJellyScreenWidth[];
VIZ_COMMON_EXPORT extern const char kDeadlineToSynchronizeSurfaces[];
VIZ_COMMON_EXPORT extern const char kDisableFrameRateLimit[];
-VIZ_COMMON_EXPORT extern const char kDocumentTransitionSlowdownFactor[];
VIZ_COMMON_EXPORT extern const char kDoubleBufferCompositing[];
VIZ_COMMON_EXPORT extern const char kEnableDeJelly[];
VIZ_COMMON_EXPORT extern const char kEnableHardwareOverlays[];
@@ -33,7 +32,6 @@ VIZ_COMMON_EXPORT extern const char kTintCompositedContentModulate[];
VIZ_COMMON_EXPORT extern const char kShowDCLayerDebugBorders[];
VIZ_COMMON_EXPORT absl::optional<uint32_t> GetDeadlineToSynchronizeSurfaces();
-VIZ_COMMON_EXPORT int GetDocumentTransitionSlowDownFactor();
} // namespace switches
diff --git a/chromium/components/viz/common/transition_utils.cc b/chromium/components/viz/common/transition_utils.cc
index a59ef9e5eb3..30f611dd4e5 100644
--- a/chromium/components/viz/common/transition_utils.cc
+++ b/chromium/components/viz/common/transition_utils.cc
@@ -20,6 +20,10 @@
namespace viz {
namespace {
+
+constexpr int kMaxListToProcess = 32;
+constexpr int kMaxQuadsPerFrame = 8;
+
struct StackFrame {
StackFrame(int list_index,
SharedQuadStateList::ConstIterator sqs_iter,
@@ -44,6 +48,10 @@ std::string SkColorToRGBAString(SkColor color) {
return str.str();
}
+std::string SkColorToRGBAString(SkColor4f color) {
+ return SkColorToRGBAString(color.toSkColor());
+}
+
std::unordered_set<uint64_t> ProcessStack(
std::ostringstream& str,
std::vector<StackFrame>& stack,
@@ -78,6 +86,7 @@ std::unordered_set<uint64_t> ProcessStack(
str << "(" << quad << ") CompositorRenderPassDrawQuad\n";
};
std::unordered_set<uint64_t> seen_render_pass_ids;
+ int quads_per_frame_logged = 0;
while (!stack.empty()) {
auto& frame = stack.back();
auto& pass = list[frame.list_index];
@@ -100,6 +109,14 @@ std::unordered_set<uint64_t> ProcessStack(
frame.indent += 2;
} else {
if (++frame.quad_iter == pass->quad_list.end()) {
+ quads_per_frame_logged = 0;
+ stack.pop_back();
+ continue;
+ }
+ if (++quads_per_frame_logged > kMaxQuadsPerFrame) {
+ write_indent(frame.indent);
+ str << "(more quads - orphaned list may not be correct)\n";
+ quads_per_frame_logged = 0;
stack.pop_back();
continue;
}
@@ -157,6 +174,12 @@ std::string TransitionUtils::RenderPassListToString(
const CompositorRenderPassList& list) {
std::ostringstream str;
+ if (list.size() > kMaxListToProcess) {
+ str << "RenderPassList too large (" << list.size()
+ << "), max supported list length " << kMaxListToProcess;
+ return str.str();
+ }
+
std::vector<StackFrame> stack;
stack.emplace_back(list.size() - 1,
list.back()->shared_quad_state_list.begin(),
diff --git a/chromium/components/viz/common/yuv_readback_unittest.cc b/chromium/components/viz/common/yuv_readback_unittest.cc
index 6a0851cf10a..cc228ed0bca 100644
--- a/chromium/components/viz/common/yuv_readback_unittest.cc
+++ b/chromium/components/viz/common/yuv_readback_unittest.cc
@@ -49,17 +49,9 @@ class YUVReadbackTest : public testing::Test {
context_ = std::make_unique<gpu::GLInProcessContext>();
auto result = context_->Initialize(
- TestGpuServiceHolder::GetInstance()->task_executor(),
- nullptr, /* surface */
- true, /* offscreen */
- gpu::kNullSurfaceHandle, /* window */
- attributes, gpu::SharedMemoryLimits(),
- nullptr, /* gpu_memory_buffer_manager */
- nullptr, /* image_factory */
- nullptr, /* gpu::GpuTaskSchedulerHelper */
- nullptr,
- /* gpu::DisplayCompositorMemoryAndTaskControllerOnGpu */
- base::ThreadTaskRunnerHandle::Get());
+ TestGpuServiceHolder::GetInstance()->task_executor(), attributes,
+ gpu::SharedMemoryLimits(),
+ /*image_factory=*/nullptr);
DCHECK_EQ(result, gpu::ContextResult::kSuccess);
gl_ = context_->GetImplementation();
gpu::ContextSupport* support = context_->GetImplementation();
diff --git a/chromium/components/viz/demo/demo_main.cc b/chromium/components/viz/demo/demo_main.cc
index db021f9870f..8cce35cdfe4 100644
--- a/chromium/components/viz/demo/demo_main.cc
+++ b/chromium/components/viz/demo/demo_main.cc
@@ -148,7 +148,7 @@ class DemoWindow : public ui::PlatformWindowDelegate {
// Next, create the host and the service, and pass them the right ends of
// the message-pipes.
host_ = std::make_unique<demo::DemoHost>(
- widget_, platform_window_->GetBounds().size(),
+ widget_, platform_window_->GetBoundsInPixels().size(),
std::move(frame_sink_manager_client_receiver),
std::move(frame_sink_manager));
diff --git a/chromium/components/viz/demo/service/demo_service.cc b/chromium/components/viz/demo/service/demo_service.cc
index 2d2d153c1ad..ba174324880 100644
--- a/chromium/components/viz/demo/service/demo_service.cc
+++ b/chromium/components/viz/demo/service/demo_service.cc
@@ -22,7 +22,7 @@ DemoService::DemoService(
params->frame_sink_manager = std::move(receiver);
params->frame_sink_manager_client = std::move(client);
runner_ = std::make_unique<viz::VizCompositorThreadRunnerImpl>();
- runner_->CreateFrameSinkManager(std::move(params));
+ runner_->CreateFrameSinkManager(std::move(params), /*gpu_service=*/nullptr);
}
DemoService::~DemoService() = default;
diff --git a/chromium/components/viz/host/client_frame_sink_video_capturer.h b/chromium/components/viz/host/client_frame_sink_video_capturer.h
index 6775aea4a58..ce514b0a8b7 100644
--- a/chromium/components/viz/host/client_frame_sink_video_capturer.h
+++ b/chromium/components/viz/host/client_frame_sink_video_capturer.h
@@ -107,6 +107,10 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer
// owned pointer to an Overlay.
std::unique_ptr<Overlay> CreateOverlay(int32_t stacking_index);
+ // Getter for `format_`. Returns the pixel format set by the last call to
+ // `SetFormat()`, or nullopt if the format was not yet set.
+ absl::optional<media::VideoPixelFormat> GetFormat() const { return format_; }
+
private:
struct ResolutionConstraints {
ResolutionConstraints(const gfx::Size& min_size,
diff --git a/chromium/components/viz/host/gpu_host_impl.cc b/chromium/components/viz/host/gpu_host_impl.cc
index bb0369de155..60e908d2d31 100644
--- a/chromium/components/viz/host/gpu_host_impl.cc
+++ b/chromium/components/viz/host/gpu_host_impl.cc
@@ -136,7 +136,7 @@ GpuHostImpl::GpuHostImpl(Delegate* delegate,
viz_main_->CreateGpuService(
gpu_service_remote_.BindNewPipeAndPassReceiver(task_runner),
gpu_host_receiver_.BindNewPipeAndPassRemote(task_runner),
- std::move(discardable_manager_remote), activity_flags_.CloneHandle(),
+ std::move(discardable_manager_remote), activity_flags_.CloneRegion(),
GetFontRenderParams().Get()->subpixel_rendering);
#if defined(USE_OZONE)
@@ -194,9 +194,8 @@ void GpuHostImpl::AddConnectionErrorHandler(base::OnceClosure handler) {
void GpuHostImpl::BlockLiveOffscreenContexts() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- for (auto iter = urls_with_live_offscreen_contexts_.begin();
- iter != urls_with_live_offscreen_contexts_.end(); ++iter) {
- delegate_->BlockDomainFrom3DAPIs(*iter, gpu::DomainGuilt::kUnknown);
+ for (auto& url : urls_with_live_offscreen_contexts_) {
+ delegate_->BlockDomainFrom3DAPIs(url, gpu::DomainGuilt::kUnknown);
}
}
@@ -565,8 +564,8 @@ void GpuHostImpl::DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) {
delegate_->DidUpdateOverlayInfo(overlay_info);
}
-void GpuHostImpl::DidUpdateHDRStatus(bool hdr_enabled) {
- delegate_->DidUpdateHDRStatus(hdr_enabled);
+void GpuHostImpl::DidUpdateDXGIInfo(gfx::mojom::DXGIInfoPtr dxgi_info) {
+ delegate_->DidUpdateDXGIInfo(std::move(dxgi_info));
}
void GpuHostImpl::SetChildSurface(gpu::SurfaceHandle parent,
diff --git a/chromium/components/viz/host/gpu_host_impl.h b/chromium/components/viz/host/gpu_host_impl.h
index 1253e39ab93..1d77a895981 100644
--- a/chromium/components/viz/host/gpu_host_impl.h
+++ b/chromium/components/viz/host/gpu_host_impl.h
@@ -42,6 +42,7 @@
#if BUILDFLAG(IS_WIN)
#include "services/viz/privileged/mojom/gl/info_collection_gpu_service.mojom.h"
+#include "ui/gfx/mojom/dxgi_info.mojom.h"
#endif
namespace gfx {
@@ -79,7 +80,7 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost
virtual void DidUpdateGPUInfo(const gpu::GPUInfo& gpu_info) = 0;
#if BUILDFLAG(IS_WIN)
virtual void DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) = 0;
- virtual void DidUpdateHDRStatus(bool hdr_enabled) = 0;
+ virtual void DidUpdateDXGIInfo(gfx::mojom::DXGIInfoPtr dxgi_info) = 0;
#endif
virtual void BlockDomainFrom3DAPIs(const GURL& url,
gpu::DomainGuilt guilt) = 0;
@@ -250,7 +251,7 @@ class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost
void DidUpdateGPUInfo(const gpu::GPUInfo& gpu_info) override;
#if BUILDFLAG(IS_WIN)
void DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) override;
- void DidUpdateHDRStatus(bool hdr_enabled) override;
+ void DidUpdateDXGIInfo(gfx::mojom::DXGIInfoPtr dxgi_info) override;
void SetChildSurface(gpu::SurfaceHandle parent,
gpu::SurfaceHandle child) override;
#endif
diff --git a/chromium/components/viz/host/hit_test/hit_test_query.cc b/chromium/components/viz/host/hit_test/hit_test_query.cc
index e703c39804c..e01a3e63109 100644
--- a/chromium/components/viz/host/hit_test/hit_test_query.cc
+++ b/chromium/components/viz/host/hit_test/hit_test_query.cc
@@ -9,7 +9,6 @@
#include <utility>
#include "base/containers/stack.h"
-#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "components/viz/common/features.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
@@ -194,9 +193,6 @@ bool HitTestQuery::FindTargetInRegionForLocation(
target->frame_sink_id = hit_test_data_[region_index].frame_sink_id;
target->location_in_target = gfx::PointF();
target->flags = HitTestRegionFlags::kHitTestAsk;
- RecordSlowPathHitTestReasons(
- AsyncHitTestReasons::kPerspectiveTransform |
- hit_test_data_[region_index].async_hit_test_reasons);
return true;
}
@@ -243,8 +239,6 @@ bool HitTestQuery::FindTargetInRegionForLocation(
target->frame_sink_id = hit_test_data_[region_index].frame_sink_id;
target->location_in_target = location_in_target;
target->flags = flags;
- RecordSlowPathHitTestReasons(
- hit_test_data_[region_index].async_hit_test_reasons);
return true;
}
@@ -273,18 +267,14 @@ bool HitTestQuery::FindTargetInRegionForLocation(
!(flags & HitTestRegionFlags::kHitTestIgnore)) {
target->frame_sink_id = hit_test_data_[region_index].frame_sink_id;
target->location_in_target = location_in_target;
- uint32_t async_hit_test_reasons =
- hit_test_data_[region_index].async_hit_test_reasons;
uint32_t target_flags = flags;
if (root_view_overlapped) {
DCHECK_EQ(hit_test_data_[region_index].async_hit_test_reasons,
AsyncHitTestReasons::kOverlappedRegion);
target_flags &= ~HitTestRegionFlags::kHitTestAsk;
- async_hit_test_reasons = AsyncHitTestReasons::kNotAsyncHitTest;
}
target->flags = target_flags;
// We record fast path hit testing instances with reason kNotAsyncHitTest.
- RecordSlowPathHitTestReasons(async_hit_test_reasons);
return true;
}
return false;
@@ -365,27 +355,6 @@ bool HitTestQuery::GetTransformToTargetRecursively(
return false;
}
-void HitTestQuery::RecordSlowPathHitTestReasons(uint32_t reasons) const {
- static const char* kAsyncHitTestReasonsHistogramName =
- "Event.VizHitTest.AsyncHitTestReasons";
- if (reasons == AsyncHitTestReasons::kNotAsyncHitTest) {
- UMA_HISTOGRAM_ENUMERATION(
- kAsyncHitTestReasonsHistogramName,
- AsyncHitTestReasons::kNotAsyncHitTest,
- AsyncHitTestReasons::kAsyncHitTestReasonCount + 1);
- return;
- }
-
- for (uint32_t i = 0; i < AsyncHitTestReasons::kAsyncHitTestReasonCount; ++i) {
- unsigned val = 1 << i;
- if (reasons & val) {
- UMA_HISTOGRAM_ENUMERATION(
- kAsyncHitTestReasonsHistogramName, i + 1,
- AsyncHitTestReasons::kAsyncHitTestReasonCount + 1);
- }
- }
-}
-
std::string HitTestQuery::PrintHitTestData() const {
std::ostringstream oss;
base::stack<uint32_t> parents;
diff --git a/chromium/components/viz/host/hit_test/hit_test_query.h b/chromium/components/viz/host/hit_test/hit_test_query.h
index c6c6f12ebaa..8451940c3e6 100644
--- a/chromium/components/viz/host/hit_test/hit_test_query.h
+++ b/chromium/components/viz/host/hit_test/hit_test_query.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_VIZ_HOST_HIT_TEST_HIT_TEST_QUERY_H_
#define COMPONENTS_VIZ_HOST_HIT_TEST_HIT_TEST_QUERY_H_
+#include <string>
#include <vector>
#include "base/callback.h"
@@ -153,8 +154,6 @@ class VIZ_HOST_EXPORT HitTestQuery {
size_t region_index,
gfx::Transform* transform) const;
- void RecordSlowPathHitTestReasons(uint32_t) const;
-
std::vector<AggregatedHitTestRegion> hit_test_data_;
};
diff --git a/chromium/components/viz/host/host_frame_sink_manager.cc b/chromium/components/viz/host/host_frame_sink_manager.cc
index 1ad1e78b6a7..90429aee8e6 100644
--- a/chromium/components/viz/host/host_frame_sink_manager.cc
+++ b/chromium/components/viz/host/host_frame_sink_manager.cc
@@ -288,6 +288,15 @@ void HostFrameSinkManager::Throttle(const std::vector<FrameSinkId>& ids,
frame_sink_manager_->Throttle(ids, interval);
}
+void HostFrameSinkManager::StartThrottlingAllFrameSinks(
+ base::TimeDelta interval) {
+ frame_sink_manager_->StartThrottlingAllFrameSinks(interval);
+}
+
+void HostFrameSinkManager::StopThrottlingAllFrameSinks() {
+ frame_sink_manager_->StopThrottlingAllFrameSinks();
+}
+
void HostFrameSinkManager::AddHitTestRegionObserver(
HitTestRegionObserver* observer) {
observers_.AddObserver(observer);
diff --git a/chromium/components/viz/host/host_frame_sink_manager.h b/chromium/components/viz/host/host_frame_sink_manager.h
index 5a0d8946432..44db2fc33d9 100644
--- a/chromium/components/viz/host/host_frame_sink_manager.h
+++ b/chromium/components/viz/host/host_frame_sink_manager.h
@@ -194,6 +194,8 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
std::unique_ptr<CopyOutputRequest> request);
void Throttle(const std::vector<FrameSinkId>& ids, base::TimeDelta interval);
+ void StartThrottlingAllFrameSinks(base::TimeDelta interval);
+ void StopThrottlingAllFrameSinks();
// Add/Remove an observer to receive notifications of when the host receives
// new hit test data.
diff --git a/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc b/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
index 3da44d37dcd..79c205298b1 100644
--- a/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
+++ b/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
@@ -186,7 +186,9 @@ class TestGpuService : public mojom::GpuService {
void GetPeakMemoryUsage(uint32_t sequence_num,
GetPeakMemoryUsageCallback callback) override {}
- void RequestHDRStatus(RequestHDRStatusCallback callback) override {}
+#if BUILDFLAG(IS_WIN)
+ void RequestDXGIInfo(RequestDXGIInfoCallback callback) override {}
+#endif
void LoadedShader(int32_t client_id,
const std::string& key,
diff --git a/chromium/components/viz/host/renderer_settings_creation.cc b/chromium/components/viz/host/renderer_settings_creation.cc
index 6a830ec9f29..9d34ced3660 100644
--- a/chromium/components/viz/host/renderer_settings_creation.cc
+++ b/chromium/components/viz/host/renderer_settings_creation.cc
@@ -66,7 +66,6 @@ RendererSettings CreateRendererSettings() {
#endif
renderer_settings.allow_antialiasing =
!command_line->HasSwitch(switches::kDisableCompositedAntialiasing);
- renderer_settings.use_skia_renderer = features::IsUsingSkiaRenderer();
if (command_line->HasSwitch(switches::kSlowDownCompositingScaleFactor)) {
const int kMinSlowDownScaleFactor = 1;
diff --git a/chromium/components/viz/service/BUILD.gn b/chromium/components/viz/service/BUILD.gn
index 002b3ddfb0f..10d10f49e7a 100644
--- a/chromium/components/viz/service/BUILD.gn
+++ b/chromium/components/viz/service/BUILD.gn
@@ -39,12 +39,12 @@ viz_component("service") {
"display/display.cc",
"display/display.h",
"display/display_client.h",
+ "display/display_compositor_memory_and_task_controller.cc",
+ "display/display_compositor_memory_and_task_controller.h",
"display/display_damage_tracker.cc",
"display/display_damage_tracker.h",
"display/display_resource_provider.cc",
"display/display_resource_provider.h",
- "display/display_resource_provider_gl.cc",
- "display/display_resource_provider_gl.h",
"display/display_resource_provider_null.cc",
"display/display_resource_provider_null.h",
"display/display_resource_provider_skia.cc",
@@ -57,22 +57,10 @@ viz_component("service") {
"display/display_scheduler_base.h",
"display/draw_polygon.cc",
"display/draw_polygon.h",
- "display/dynamic_geometry_binding.cc",
- "display/dynamic_geometry_binding.h",
"display/external_use_client.cc",
"display/external_use_client.h",
"display/frame_rate_decider.cc",
"display/frame_rate_decider.h",
- "display/geometry_binding.cc",
- "display/geometry_binding.h",
- "display/gl_renderer.cc",
- "display/gl_renderer.h",
- "display/gl_renderer_copier.cc",
- "display/gl_renderer_copier.h",
- "display/gl_renderer_draw_cache.cc",
- "display/gl_renderer_draw_cache.h",
- "display/layer_quad.cc",
- "display/layer_quad.h",
"display/null_renderer.cc",
"display/null_renderer.h",
"display/output_surface.cc",
@@ -92,19 +80,11 @@ viz_component("service") {
"display/overlay_processor_stub.h",
"display/pending_swap_params.cc",
"display/pending_swap_params.h",
- "display/program_binding.cc",
- "display/program_binding.h",
"display/renderer_utils.cc",
"display/renderer_utils.h",
"display/resolved_frame_data.cc",
"display/resolved_frame_data.h",
"display/resource_fence.h",
- "display/scoped_gpu_memory_buffer_texture.cc",
- "display/scoped_gpu_memory_buffer_texture.h",
- "display/scoped_render_pass_texture.cc",
- "display/scoped_render_pass_texture.h",
- "display/shader.cc",
- "display/shader.h",
"display/shared_bitmap_manager.h",
"display/skia_output_surface.cc",
"display/skia_output_surface.h",
@@ -114,35 +94,46 @@ viz_component("service") {
"display/software_output_device.h",
"display/software_renderer.cc",
"display/software_renderer.h",
- "display/static_geometry_binding.cc",
- "display/static_geometry_binding.h",
"display/surface_aggregator.cc",
"display/surface_aggregator.h",
- "display/sync_query_collection.cc",
- "display/sync_query_collection.h",
- "display/texture_deleter.cc",
- "display/texture_deleter.h",
"display_embedder/buffer_queue.cc",
"display_embedder/buffer_queue.h",
"display_embedder/compositor_gpu_thread.cc",
"display_embedder/compositor_gpu_thread.h",
- "display_embedder/gl_output_surface.cc",
- "display_embedder/gl_output_surface.h",
- "display_embedder/gl_output_surface_buffer_queue.cc",
- "display_embedder/gl_output_surface_buffer_queue.h",
- "display_embedder/gl_output_surface_offscreen.cc",
- "display_embedder/gl_output_surface_offscreen.h",
+ "display_embedder/image_context_impl.cc",
+ "display_embedder/image_context_impl.h",
"display_embedder/in_process_gpu_memory_buffer_manager.cc",
"display_embedder/in_process_gpu_memory_buffer_manager.h",
+ "display_embedder/output_presenter.cc",
+ "display_embedder/output_presenter.h",
+ "display_embedder/output_presenter_gl.cc",
+ "display_embedder/output_presenter_gl.h",
"display_embedder/output_surface_provider.h",
"display_embedder/output_surface_provider_impl.cc",
"display_embedder/output_surface_provider_impl.h",
"display_embedder/server_shared_bitmap_manager.cc",
"display_embedder/server_shared_bitmap_manager.h",
+ "display_embedder/skia_output_device.cc",
+ "display_embedder/skia_output_device.h",
+ "display_embedder/skia_output_device_buffer_queue.cc",
+ "display_embedder/skia_output_device_buffer_queue.h",
+ "display_embedder/skia_output_device_gl.cc",
+ "display_embedder/skia_output_device_gl.h",
+ "display_embedder/skia_output_device_offscreen.cc",
+ "display_embedder/skia_output_device_offscreen.h",
+ "display_embedder/skia_output_device_webview.cc",
+ "display_embedder/skia_output_device_webview.h",
+ "display_embedder/skia_output_surface_dependency.h",
+ "display_embedder/skia_output_surface_dependency_impl.cc",
+ "display_embedder/skia_output_surface_dependency_impl.h",
+ "display_embedder/skia_output_surface_impl.cc",
+ "display_embedder/skia_output_surface_impl.h",
+ "display_embedder/skia_output_surface_impl_on_gpu.cc",
+ "display_embedder/skia_output_surface_impl_on_gpu.h",
+ "display_embedder/skia_render_copy_results.cc",
+ "display_embedder/skia_render_copy_results.h",
"display_embedder/software_output_surface.cc",
"display_embedder/software_output_surface.h",
- "display_embedder/viz_process_context_provider.cc",
- "display_embedder/viz_process_context_provider.h",
"display_embedder/vsync_parameter_listener.cc",
"display_embedder/vsync_parameter_listener.h",
"frame_sinks/begin_frame_tracker.cc",
@@ -184,6 +175,8 @@ viz_component("service") {
"frame_sinks/video_capture/video_frame_pool.h",
"frame_sinks/video_detector.cc",
"frame_sinks/video_detector.h",
+ "gl/gpu_service_impl.cc",
+ "gl/gpu_service_impl.h",
"hit_test/hit_test_aggregator.cc",
"hit_test/hit_test_aggregator.h",
"hit_test/hit_test_aggregator_delegate.h",
@@ -225,8 +218,6 @@ viz_component("service") {
defines = [ "VIZ_SERVICE_IMPLEMENTATION" ]
- allow_circular_includes_from = [ ":gpu_service_dependencies" ]
-
deps = [
"//build:chromecast_buildflags",
"//build:chromeos_buildflags",
@@ -234,13 +225,12 @@ viz_component("service") {
"//cc/paint",
"//components/crash/core/common:crash_key",
"//components/power_scheduler",
- "//gpu/command_buffer/client:gles2_cmd_helper",
- "//gpu/command_buffer/client:gles2_implementation",
- "//gpu/command_buffer/client:raster",
# Note that dependency on //gpu/ipc/client is for GpuMemoryBufferImpl. This
# dependency should not be in public_deps.
"//components/viz/common",
+ "//gpu/command_buffer/client",
+ "//gpu/config",
"//gpu/ipc/client",
"//gpu/ipc/common:common",
"//gpu/ipc/common:surface_handle_type",
@@ -260,14 +250,20 @@ viz_component("service") {
]
public_deps = [
- ":gpu_service_dependencies",
"//base",
"//cc",
"//cc/debug",
"//components/viz/common",
- "//gpu/command_buffer/client:gles2_interface",
+ "//gpu/command_buffer/service:gles2",
+ "//gpu/ipc:gl_in_process_context",
+ "//gpu/ipc/service",
+ "//gpu/vulkan:buildflags",
+ "//media/gpu/ipc/service",
+ "//media/mojo/services",
"//services/viz/privileged/mojom/compositing",
+ "//services/viz/privileged/mojom/gl",
"//services/viz/public/mojom",
+ "//skia",
"//ui/base/prediction",
"//ui/gfx",
"//ui/gfx/geometry",
@@ -276,9 +272,17 @@ viz_component("service") {
if (is_chromeos_ash) {
sources += [
- "display_embedder/gl_output_surface_chromeos.cc",
- "display_embedder/gl_output_surface_chromeos.h",
+ "display_embedder/output_surface_unified.cc",
+ "display_embedder/output_surface_unified.h",
]
+
+ deps += [
+ "//components/chromeos_camera:jpeg_encode_accelerator_service",
+ "//components/chromeos_camera:mjpeg_decode_accelerator_service",
+ "//gpu/command_buffer/service:gles2",
+ "//media/mojo/services",
+ ]
+
if (use_v4l2_codec || use_vaapi) {
deps += [ "//ash/components/arc/video_accelerator" ]
}
@@ -329,8 +333,6 @@ viz_component("service") {
"display/overlay_processor_android.h",
"display/overlay_processor_surface_control.cc",
"display/overlay_processor_surface_control.h",
- "display_embedder/gl_output_surface_android.cc",
- "display_embedder/gl_output_surface_android.h",
"frame_sinks/external_begin_frame_source_android.cc",
"frame_sinks/external_begin_frame_source_android.h",
"gl/throw_uncaught_exception.cc",
@@ -367,85 +369,6 @@ viz_component("service") {
"display_embedder/output_device_backing.h",
"display_embedder/software_output_device_win.cc",
"display_embedder/software_output_device_win.h",
- ]
- }
-
- if (is_chromeos_ash) {
- sources += [
- "display_embedder/output_surface_unified.cc",
- "display_embedder/output_surface_unified.h",
- ]
- }
-
- if (enable_vulkan) {
- deps += [ "//gpu/vulkan" ]
- }
-}
-
-# The gpu_service_dependencies source set contains source files that
-# use the service side GL library (ui/gl), while the rest of
-# viz/service use the client side GL library. This split is needed
-# because the two GL libraries are incompatible and can't compile
-# together in jumbo builds.
-#
-# Long term all service code is moving to be in the gpu process and
-# then this build target will no longer be needed.
-viz_source_set("gpu_service_dependencies") {
- sources = [
- "display/display_compositor_memory_and_task_controller.cc",
- "display/display_compositor_memory_and_task_controller.h",
- "display_embedder/image_context_impl.cc",
- "display_embedder/image_context_impl.h",
- "display_embedder/output_presenter.cc",
- "display_embedder/output_presenter.h",
- "display_embedder/output_presenter_gl.cc",
- "display_embedder/output_presenter_gl.h",
- "display_embedder/skia_output_device.cc",
- "display_embedder/skia_output_device.h",
- "display_embedder/skia_output_device_buffer_queue.cc",
- "display_embedder/skia_output_device_buffer_queue.h",
- "display_embedder/skia_output_device_gl.cc",
- "display_embedder/skia_output_device_gl.h",
- "display_embedder/skia_output_device_offscreen.cc",
- "display_embedder/skia_output_device_offscreen.h",
- "display_embedder/skia_output_device_webview.cc",
- "display_embedder/skia_output_device_webview.h",
- "display_embedder/skia_output_surface_dependency.h",
- "display_embedder/skia_output_surface_dependency_impl.cc",
- "display_embedder/skia_output_surface_dependency_impl.h",
- "display_embedder/skia_output_surface_impl.cc",
- "display_embedder/skia_output_surface_impl.h",
- "display_embedder/skia_output_surface_impl_on_gpu.cc",
- "display_embedder/skia_output_surface_impl_on_gpu.h",
- "display_embedder/skia_render_copy_results.cc",
- "display_embedder/skia_render_copy_results.h",
- "gl/gpu_service_impl.cc",
- "gl/gpu_service_impl.h",
- ]
-
- public_deps = [
- "//gpu/command_buffer/service:gles2",
- "//gpu/ipc:gl_in_process_context",
- "//gpu/ipc/service",
- "//gpu/vulkan:buildflags",
- "//media/gpu/ipc/service",
- "//media/mojo/services",
- "//services/viz/privileged/mojom/gl",
- "//skia",
- "//ui/latency:latency",
- ]
-
- defines = [ "VIZ_SERVICE_IMPLEMENTATION" ]
-
- deps = [
- "//base",
- "//build:chromeos_buildflags",
- "//gpu/config",
- "//third_party/libyuv",
- ]
-
- if (is_win) {
- sources += [
"gl/info_collection_gpu_service_impl.cc",
"gl/info_collection_gpu_service_impl.h",
]
@@ -461,34 +384,25 @@ viz_source_set("gpu_service_dependencies") {
]
}
- if (is_chromeos_ash) {
- deps += [
- "//components/chromeos_camera:jpeg_encode_accelerator_service",
- "//components/chromeos_camera:mjpeg_decode_accelerator_service",
- "//gpu/command_buffer/service:gles2",
- "//media/mojo/services",
+ if (is_fuchsia) {
+ sources += [
+ "display_embedder/output_presenter_fuchsia.cc",
+ "display_embedder/output_presenter_fuchsia.h",
]
- if (use_v4l2_codec || use_vaapi) {
- deps += [ "//ash/components/arc/video_accelerator" ]
- }
}
if (use_vaapi) {
deps += [ "//media/gpu/vaapi" ]
}
- if (use_ozone) {
- deps += [ "//ui/ozone" ]
- }
-
if (enable_vulkan) {
+ deps += [ "//gpu/vulkan" ]
+
sources += [
"display_embedder/skia_output_device_vulkan.cc",
"display_embedder/skia_output_device_vulkan.h",
]
- public_deps += [ "//gpu/vulkan" ]
-
if (is_android) {
sources += [
"display_embedder/skia_output_device_vulkan_secondary_cb.cc",
@@ -528,13 +442,6 @@ viz_source_set("gpu_service_dependencies") {
"//third_party/dawn/src/dawn/native",
]
}
-
- if (is_fuchsia) {
- sources += [
- "display_embedder/output_presenter_fuchsia.cc",
- "display_embedder/output_presenter_fuchsia.h",
- ]
- }
}
viz_source_set("unit_tests") {
@@ -546,26 +453,21 @@ viz_source_set("unit_tests") {
"display/delegated_ink_point_pixel_test_helper.cc",
"display/delegated_ink_point_pixel_test_helper.h",
"display/display_damage_tracker_unittest.cc",
- "display/display_resource_provider_gl_unittest.cc",
"display/display_resource_provider_skia_unittest.cc",
"display/display_resource_provider_software_unittest.cc",
"display/display_scheduler_unittest.cc",
"display/display_unittest.cc",
"display/draw_polygon_unittest.cc",
"display/frame_rate_decider_unittest.cc",
- "display/layer_quad_unittest.cc",
"display/renderer_pixeltest.cc",
"display/resolved_frame_data_unittest.cc",
- "display/shader_unittest.cc",
"display/skia_readback_pixeltest.cc",
"display/software_renderer_unittest.cc",
"display/surface_aggregator_pixeltest.cc",
"display/surface_aggregator_unittest.cc",
- "display/texture_deleter_unittest.cc",
"display/viz_pixel_test.cc",
"display/viz_pixel_test.h",
"display_embedder/buffer_queue_unittest.cc",
- "display_embedder/gl_output_surface_buffer_queue_unittest.cc",
"display_embedder/server_shared_bitmap_manager_unittest.cc",
"display_embedder/skia_output_device_buffer_queue_unittest.cc",
"display_embedder/skia_output_surface_impl_unittest.cc",
@@ -591,14 +493,6 @@ viz_source_set("unit_tests") {
"transitions/transferable_resource_tracker_unittest.cc",
]
- if (enable_gl_renderer_tests) {
- sources += [
- "display/gl_renderer_copier_pixeltest.cc",
- "display/gl_renderer_copier_unittest.cc",
- "display/gl_renderer_unittest.cc",
- ]
- }
-
if (!use_aura && !is_mac) {
sources -= [ "display_embedder/buffer_queue_unittest.cc" ]
}
@@ -619,7 +513,6 @@ viz_source_set("unit_tests") {
"//components/viz/test:test_suite",
"//components/viz/test:test_support",
"//gpu/command_buffer/client",
- "//gpu/command_buffer/client:gles2_implementation",
"//gpu/ipc:gl_in_process_context",
"//gpu/ipc/service",
"//media",
@@ -690,7 +583,6 @@ viz_source_set("perf_tests") {
sources = [
"display/bsp_tree_perftest.cc",
"display/display_perftest.cc",
- "display/gl_renderer_copier_perftest.cc",
"display/renderer_perftest.cc",
"display/surface_aggregator_perftest.cc",
"display/viz_perftest.cc",
@@ -728,6 +620,8 @@ if (is_android) {
android_library("service_java") {
deps = [
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//ui/android:ui_no_recycler_view_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc b/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc
index 9b0b639fdae..941f49d8688 100644
--- a/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc
+++ b/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc
@@ -89,8 +89,7 @@ FuzzerSoftwareOutputSurfaceProvider::~FuzzerSoftwareOutputSurfaceProvider() =
std::unique_ptr<DisplayCompositorMemoryAndTaskController>
FuzzerSoftwareOutputSurfaceProvider::CreateGpuDependency(
bool gpu_compositing,
- gpu::SurfaceHandle surface_handle,
- const RendererSettings& renderer_settings) {
+ gpu::SurfaceHandle surface_handle) {
return nullptr;
}
diff --git a/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h b/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h
index 2f115f653b8..24ccdee8476 100644
--- a/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h
+++ b/chromium/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h
@@ -30,8 +30,7 @@ class FuzzerSoftwareOutputSurfaceProvider : public OutputSurfaceProvider {
// OutputSurfaceProvider implementation.
std::unique_ptr<DisplayCompositorMemoryAndTaskController> CreateGpuDependency(
bool gpu_compositing,
- gpu::SurfaceHandle surface_handle,
- const RendererSettings& renderer_settings) override;
+ gpu::SurfaceHandle surface_handle) override;
std::unique_ptr<OutputSurface> CreateOutputSurface(
gpu::SurfaceHandle surface_handle,
bool gpu_compositing,
diff --git a/chromium/components/viz/service/display/DEPS b/chromium/components/viz/service/display/DEPS
index 057df66329e..ca3f9db0518 100644
--- a/chromium/components/viz/service/display/DEPS
+++ b/chromium/components/viz/service/display/DEPS
@@ -16,7 +16,7 @@ include_rules = [
"+components/viz/service/display_embedder/overlay_candidate_validator_win.h",
"+components/viz/service/display_embedder/skia_output_surface_dependency.h",
"+components/viz/common",
- "+gpu/command_buffer/client",
+ "+gpu/command_buffer/client/shared_image_interface.h",
"+gpu/command_buffer/common",
"+gpu/command_buffer/service",
"+gpu/GLES2",
diff --git a/chromium/components/viz/service/display/ca_layer_overlay.cc b/chromium/components/viz/service/display/ca_layer_overlay.cc
index 1c873a3e7a7..9da6b5e17d5 100644
--- a/chromium/components/viz/service/display/ca_layer_overlay.cc
+++ b/chromium/components/viz/service/display/ca_layer_overlay.cc
@@ -136,6 +136,10 @@ gfx::CALayerResult FromRenderPassQuad(
ca_layer_overlay->rpdq = quad;
ca_layer_overlay->contents_rect = gfx::RectF(0, 0, 1, 1);
+ // For RenderPassDrawQuad, the opacity is applied when its ddl is recorded, so
+ // the content already is with opacity applied.
+ ca_layer_overlay->opacity = 1.0;
+
return gfx::kCALayerSuccess;
}
@@ -156,7 +160,7 @@ gfx::CALayerResult FromSolidColorDrawQuad(const SolidColorDrawQuad* quad,
CALayerOverlay* ca_layer_overlay,
bool* skip) {
// Do not generate quads that are completely transparent.
- if (SkColorGetA(quad->color) == 0) {
+ if (quad->color.fA == 0.0f) {
*skip = true;
return gfx::kCALayerSuccess;
}
@@ -187,7 +191,7 @@ gfx::CALayerResult FromTextureQuad(DisplayResourceProvider* resource_provider,
if (quad->vertex_opacity[i] != quad->vertex_opacity[0])
return gfx::kCALayerFailedDifferentVertexOpacities;
}
- ca_layer_overlay->shared_state->opacity *= quad->vertex_opacity[0];
+ ca_layer_overlay->opacity *= quad->vertex_opacity[0];
ca_layer_overlay->filter = quad->nearest_neighbor ? GL_NEAREST : GL_LINEAR;
if (quad->is_video_frame)
ca_layer_overlay->protected_video_type = quad->protected_video_type;
@@ -310,13 +314,13 @@ class CALayerOverlayProcessorInternal {
// Enable edge anti-aliasing only on layer boundaries.
ca_layer_overlay->edge_aa_mask = 0;
if (quad->IsLeftEdge())
- ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_LEFT_CHROMIUM;
+ ca_layer_overlay->edge_aa_mask |= ui::CALayerEdge::kLayerEdgeLeft;
if (quad->IsRightEdge())
- ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_RIGHT_CHROMIUM;
+ ca_layer_overlay->edge_aa_mask |= ui::CALayerEdge::kLayerEdgeRight;
if (quad->IsBottomEdge())
- ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM;
+ ca_layer_overlay->edge_aa_mask |= ui::CALayerEdge::kLayerEdgeBottom;
if (quad->IsTopEdge())
- ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_TOP_CHROMIUM;
+ ca_layer_overlay->edge_aa_mask |= ui::CALayerEdge::kLayerEdgeTop;
if (most_recent_shared_quad_state_ != quad->shared_quad_state) {
most_recent_shared_quad_state_ = quad->shared_quad_state;
@@ -331,14 +335,13 @@ class CALayerOverlayProcessorInternal {
most_recent_overlay_shared_state_->rounded_corner_bounds =
quad->shared_quad_state->mask_filter_info.rounded_corner_bounds();
- most_recent_overlay_shared_state_->opacity =
- quad->shared_quad_state->opacity;
most_recent_overlay_shared_state_->transform =
quad->shared_quad_state->quad_to_target_transform;
}
ca_layer_overlay->shared_state = most_recent_overlay_shared_state_;
ca_layer_overlay->bounds_rect = gfx::RectF(quad->rect);
+ ca_layer_overlay->opacity = quad->shared_quad_state->opacity;
*render_pass_draw_quad =
quad->material == DrawQuad::Material::kAggregatedRenderPass;
diff --git a/chromium/components/viz/service/display/ca_layer_overlay.h b/chromium/components/viz/service/display/ca_layer_overlay.h
index 7d3dacb6ead..8174a19ac5a 100644
--- a/chromium/components/viz/service/display/ca_layer_overlay.h
+++ b/chromium/components/viz/service/display/ca_layer_overlay.h
@@ -14,7 +14,6 @@
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkRefCnt.h"
#include "ui/gfx/ca_layer_result.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/rrect_f.h"
@@ -22,8 +21,6 @@
#include "ui/gfx/video_types.h"
#include "ui/gl/ca_renderer_layer_params.h"
-class SkDeferredDisplayList;
-
namespace viz {
class AggregatedRenderPassDrawQuad;
class DisplayResourceProvider;
@@ -43,8 +40,6 @@ class VIZ_SERVICE_EXPORT CALayerOverlaySharedState
bool is_clipped = false;
gfx::RectF clip_rect;
gfx::RRectF rounded_corner_bounds;
- // The opacity property for the CAayer.
- float opacity = 1;
// The transform to apply to the CALayer.
gfx::Transform transform;
@@ -73,8 +68,10 @@ class VIZ_SERVICE_EXPORT CALayerOverlay {
gfx::RectF contents_rect;
// The bounds for the CALayer in pixels.
gfx::RectF bounds_rect;
+ // The opacity property for the CAayer.
+ float opacity = 1;
// The background color property for the CALayer.
- SkColor background_color = SK_ColorTRANSPARENT;
+ SkColor4f background_color = SkColors::kTransparent;
// The edge anti-aliasing mask property for the CALayer.
unsigned edge_aa_mask = 0;
// The minification and magnification filters for the CALayer.
@@ -85,8 +82,6 @@ class VIZ_SERVICE_EXPORT CALayerOverlay {
// If |rpdq| is present, then the renderer must draw the filter effects and
// copy the result into an IOSurface.
const AggregatedRenderPassDrawQuad* rpdq = nullptr;
- // The DDL for generating render pass overlay buffer with SkiaRenderer.
- sk_sp<SkDeferredDisplayList> ddl;
};
typedef std::vector<CALayerOverlay> CALayerOverlayList;
diff --git a/chromium/components/viz/service/display/dc_layer_overlay.cc b/chromium/components/viz/service/display/dc_layer_overlay.cc
index 0d97d7c3277..bb845f7ea91 100644
--- a/chromium/components/viz/service/display/dc_layer_overlay.cc
+++ b/chromium/components/viz/service/display/dc_layer_overlay.cc
@@ -13,6 +13,7 @@
#include "build/build_config.h"
#include "cc/base/math_util.h"
#include "components/viz/common/display/renderer_settings.h"
+#include "components/viz/common/overlay_state/win/overlay_state_service.h"
#include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
#include "components/viz/common/quads/debug_border_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
@@ -23,6 +24,7 @@
#include "components/viz/service/display/overlay_processor_interface.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/config/gpu_finch_features.h"
+#include "media/base/win/mf_feature_checks.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
@@ -317,7 +319,7 @@ bool IsOccluded(
overlap_rect = ClippedQuadRectangle(quad);
if (quad->material == DrawQuad::Material::kSolidColor) {
- SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color;
+ SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color.toSkColor();
float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity;
if (quad->ShouldDrawWithBlending() &&
alpha < std::numeric_limits<float>::epsilon())
@@ -436,6 +438,7 @@ DCLayerOverlayProcessor::DCLayerOverlayProcessor(
UpdateHasHwOverlaySupport();
ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
}
+ allow_promotion_hinting_ = media::SupportMediaFoundationClearPlayback();
}
DCLayerOverlayProcessor::~DCLayerOverlayProcessor() {
@@ -675,6 +678,7 @@ void DCLayerOverlayProcessor::Process(
continue;
}
+ gpu::Mailbox promotion_hint_mailbox;
DCLayerResult result;
switch (it->material) {
case DrawQuad::Material::kYuvVideoContent:
@@ -686,19 +690,50 @@ void DCLayerOverlayProcessor::Process(
if (result == DC_LAYER_SUCCESS)
processed_yuv_overlay_count_++;
break;
- case DrawQuad::Material::kStreamVideoContent:
+ case DrawQuad::Material::kStreamVideoContent: {
+ if (allow_promotion_hinting_) {
+ // If this quad has marked itself as wanting promotion hints then get
+ // the associated mailbox.
+ const StreamVideoDrawQuad* sv_quad =
+ StreamVideoDrawQuad::MaterialCast(*it);
+ ResourceId id = sv_quad->resource_id();
+ if (resource_provider->DoesResourceWantPromotionHint(id)) {
+ promotion_hint_mailbox = resource_provider->GetMailbox(id);
+ }
+ }
// Stream video quads contain Media Foundation dcomp surface which is
// always presented as overlay.
result = DC_LAYER_SUCCESS;
- break;
- case DrawQuad::Material::kTextureContent:
+ } break;
+ case DrawQuad::Material::kTextureContent: {
result = ValidateTextureQuad(TextureDrawQuad::MaterialCast(*it),
backdrop_filter_rects, resource_provider);
- break;
+
+ if (allow_promotion_hinting_) {
+ // If this quad has marked itself as wanting promotion hints then get
+ // the associated mailbox.
+ const TextureDrawQuad* tex_quad = TextureDrawQuad::MaterialCast(*it);
+ ResourceId id = tex_quad->resource_id();
+ if (resource_provider->DoesResourceWantPromotionHint(id)) {
+ promotion_hint_mailbox = resource_provider->GetMailbox(id);
+ }
+ }
+ } break;
default:
result = DC_LAYER_FAILED_UNSUPPORTED_QUAD;
}
+ if (!promotion_hint_mailbox.IsZero()) {
+ DCHECK(allow_promotion_hinting_);
+ bool promoted = result == DC_LAYER_SUCCESS;
+ auto* overlay_state_service = OverlayStateService::GetInstance();
+ // The OverlayStateService should always be initialized by GpuServiceImpl
+ // at creation - DCHECK here just to assert there aren't any corner cases
+ // where this isn't true.
+ DCHECK(overlay_state_service->IsInitialized());
+ overlay_state_service->SetPromotionHint(promotion_hint_mailbox, promoted);
+ }
+
if (result != DC_LAYER_SUCCESS) {
RecordDCLayerResult(result, it);
continue;
diff --git a/chromium/components/viz/service/display/dc_layer_overlay.h b/chromium/components/viz/service/display/dc_layer_overlay.h
index 390be729590..fab70d446b3 100644
--- a/chromium/components/viz/service/display/dc_layer_overlay.h
+++ b/chromium/components/viz/service/display/dc_layer_overlay.h
@@ -162,16 +162,17 @@ class VIZ_SERVICE_EXPORT DCLayerOverlayProcessor
const raw_ptr<const DebugRendererSettings> debug_settings_;
bool previous_frame_underlay_is_opaque_ = true;
+ bool allow_promotion_hinting_ = false;
gfx::RectF previous_display_rect_;
std::vector<size_t> damages_to_be_removed_;
struct OverlayRect {
gfx::Rect rect;
bool is_overlay; // If false, it's an underlay.
- bool operator==(const OverlayRect& b) {
+ bool operator==(const OverlayRect& b) const {
return rect == b.rect && is_overlay == b.is_overlay;
}
- bool operator!=(const OverlayRect& b) { return !(*this == b); }
+ bool operator!=(const OverlayRect& b) const { return !(*this == b); }
};
std::vector<OverlayRect> previous_frame_overlay_rects_;
std::vector<OverlayRect> current_frame_overlay_rects_;
diff --git a/chromium/components/viz/service/display/direct_renderer.cc b/chromium/components/viz/service/display/direct_renderer.cc
index 65407f9e679..ce0b247406d 100644
--- a/chromium/components/viz/service/display/direct_renderer.cc
+++ b/chromium/components/viz/service/display/direct_renderer.cc
@@ -92,21 +92,10 @@ DirectRenderer::DirectRenderer(const RendererSettings* settings,
DirectRenderer::~DirectRenderer() = default;
void DirectRenderer::Initialize() {
- auto* context_provider = output_surface_->context_provider();
-
use_partial_swap_ = settings_->partial_swap_enabled && CanPartialSwap();
- allow_empty_swap_ = use_partial_swap_;
- if (context_provider) {
- if (context_provider->ContextCapabilities().commit_overlay_planes)
- allow_empty_swap_ = true;
-#if DCHECK_IS_ON()
- supports_occlusion_query_ =
- context_provider->ContextCapabilities().occlusion_query;
-#endif
- } else {
- allow_empty_swap_ |=
- output_surface_->capabilities().supports_commit_overlay_planes;
- }
+ allow_empty_swap_ =
+ use_partial_swap_ ||
+ output_surface_->capabilities().supports_commit_overlay_planes;
initialized_ = true;
}
@@ -226,28 +215,6 @@ void DirectRenderer::DrawFrame(
auto* root_render_pass = render_passes_in_draw_order->back().get();
DCHECK(root_render_pass);
-#if DCHECK_IS_ON()
- bool overdraw_tracing_enabled;
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"),
- &overdraw_tracing_enabled);
- DLOG_IF(WARNING, !overdraw_tracing_support_missing_logged_once_ &&
- overdraw_tracing_enabled && !supports_occlusion_query_)
- << "Overdraw tracing enabled on platform without support.";
- overdraw_tracing_support_missing_logged_once_ = true;
-#endif
-
- bool overdraw_feedback = debug_settings_->show_overdraw_feedback;
- if (overdraw_feedback && !output_surface_->capabilities().supports_stencil) {
-#if DCHECK_IS_ON()
- DLOG_IF(WARNING, !overdraw_feedback_support_missing_logged_once_)
- << "Overdraw feedback enabled on platform without support.";
- overdraw_feedback_support_missing_logged_once_ = true;
-#endif
- overdraw_feedback = false;
- }
- base::AutoReset<bool> auto_reset_overdraw_feedback(&overdraw_feedback_,
- overdraw_feedback);
-
current_frame_valid_ = true;
current_frame_ = DrawingFrame();
current_frame()->render_passes_in_draw_order = render_passes_in_draw_order;
@@ -366,26 +333,21 @@ void DirectRenderer::DrawFrame(
// Only reshape when we know we are going to draw. Otherwise, the reshape
// can leave the window at the wrong size if we never draw and the proper
// viewport size is never set.
- bool use_stencil = overdraw_feedback_;
bool needs_full_frame_redraw = false;
auto display_transform = output_surface_->GetDisplayTransform();
+ OutputSurface::ReshapeParams reshape_params;
+ reshape_params.size = surface_resource_size;
+ reshape_params.device_scale_factor = device_scale_factor;
+ reshape_params.color_space = frame_color_space;
+ reshape_params.sdr_white_level = CurrentFrameSDRWhiteLevel();
+ reshape_params.format = frame_buffer_format;
if (next_frame_needs_full_frame_redraw_ ||
- surface_resource_size != reshape_surface_size_ ||
- device_scale_factor != reshape_device_scale_factor_ ||
- frame_color_space != reshape_color_space_ ||
- frame_buffer_format != reshape_buffer_format_ ||
- use_stencil != reshape_use_stencil_ ||
+ reshape_params != reshape_params_ ||
display_transform != reshape_display_transform_) {
next_frame_needs_full_frame_redraw_ = false;
- reshape_surface_size_ = surface_resource_size;
- reshape_device_scale_factor_ = device_scale_factor;
- reshape_color_space_ = frame_color_space;
- reshape_buffer_format_ = frame_buffer_format;
- reshape_use_stencil_ = overdraw_feedback_;
+ reshape_params_ = reshape_params;
reshape_display_transform_ = display_transform;
- output_surface_->Reshape(reshape_surface_size_,
- reshape_device_scale_factor_, reshape_color_space_,
- *reshape_buffer_format_, reshape_use_stencil_);
+ output_surface_->Reshape(reshape_params);
#if BUILDFLAG(IS_APPLE)
// For Mac, all render passes will be promoted to CALayer, the redraw full
// frame is for the main surface only.
@@ -422,16 +384,6 @@ void DirectRenderer::DrawFrame(
if (!skip_drawing_root_render_pass)
DrawRenderPassAndExecuteCopyRequests(root_render_pass);
- // Use a fence to synchronize display of the main fb used by the output
- // surface. Note that gpu_fence_id may have the special value 0 ("no fence")
- // if fences are not supported. In that case synchronization will happen
- // through other means on the service side.
- // TODO(afrantzis): Consider using per-overlay fences instead of the one
- // associated with the output surface when possible.
- if (current_frame()->output_surface_plane)
- current_frame()->output_surface_plane->gpu_fence_id =
- output_surface_->UpdateGpuFence();
-
if (overlay_processor_)
overlay_processor_->TakeOverlayCandidates(&current_frame()->overlay_list);
@@ -657,16 +609,8 @@ void DirectRenderer::DrawRenderPass(const AggregatedRenderPass* render_pass) {
const bool render_pass_requires_scissor =
render_pass_is_clipped || (supports_dc_layers && is_root_render_pass);
- const bool has_external_stencil_test =
- is_root_render_pass && output_surface_->HasExternalStencilTest();
const bool should_clear_surface =
- !has_external_stencil_test &&
- (!is_root_render_pass || settings_->should_clear_root_render_pass);
-
- // If |has_external_stencil_test| we can't discard or clear. Make sure we
- // don't need to.
- DCHECK(!has_external_stencil_test ||
- !current_frame()->current_render_pass->has_transparent_background);
+ !is_root_render_pass || settings_->should_clear_root_render_pass;
SurfaceInitializationMode mode;
if (should_clear_surface && render_pass_requires_scissor) {
@@ -737,9 +681,6 @@ void DirectRenderer::DrawRenderPass(const AggregatedRenderPass* render_pass) {
render_pass_requires_scissor);
FinishDrawingQuadList();
- if (is_root_render_pass && overdraw_feedback_)
- FlushOverdrawFeedback(render_pass_scissor_in_draw_space);
-
if (render_pass->generate_mipmap)
GenerateMipmap();
}
@@ -1007,6 +948,10 @@ bool DirectRenderer::ShouldApplyRoundedCorner(const DrawQuad* quad) const {
return false;
}
+float DirectRenderer::CurrentFrameSDRWhiteLevel() const {
+ return current_frame()->display_color_spaces.GetSDRMaxLuminanceNits();
+}
+
gfx::ColorSpace DirectRenderer::RootRenderPassColorSpace() const {
return current_frame()->display_color_spaces.GetOutputColorSpace(
current_frame()->root_render_pass->content_color_usage,
diff --git a/chromium/components/viz/service/display/direct_renderer.h b/chromium/components/viz/service/display/direct_renderer.h
index 0c6a57744c2..881356c9c73 100644
--- a/chromium/components/viz/service/display/direct_renderer.h
+++ b/chromium/components/viz/service/display/direct_renderer.h
@@ -23,7 +23,6 @@
#include "components/viz/service/display/overlay_candidate.h"
#include "components/viz/service/display/overlay_processor_interface.h"
#include "components/viz/service/viz_service_export.h"
-#include "gpu/command_buffer/common/texture_in_use_response.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/ca_layer_result.h"
#include "ui/gfx/delegated_ink_metadata.h"
@@ -111,8 +110,6 @@ class VIZ_SERVICE_EXPORT DirectRenderer {
virtual void SwapBuffersSkipped() {}
virtual void SwapBuffersComplete(gfx::GpuFenceHandle release_fence) {}
virtual void BuffersPresented() {}
- virtual void DidReceiveTextureInUseResponses(
- const gpu::TextureInUseResponses& responses) {}
virtual void DidReceiveReleasedOverlays(
const std::vector<gpu::Mailbox>& released_overlays) {}
@@ -264,7 +261,6 @@ class VIZ_SERVICE_EXPORT DirectRenderer {
virtual void DoDrawQuad(const DrawQuad* quad,
const gfx::QuadF* clip_region) = 0;
virtual void BeginDrawingFrame() = 0;
- virtual void FlushOverdrawFeedback(const gfx::Rect& output_rect) {}
virtual void FinishDrawingFrame() = 0;
// If a pass contains a single tile draw quad and can be drawn without
// a render pass (e.g. applying a filter directly to the tile quad)
@@ -282,7 +278,7 @@ class VIZ_SERVICE_EXPORT DirectRenderer {
virtual void GenerateMipmap() = 0;
gfx::Size surface_size_for_swap_buffers() const {
- return reshape_surface_size_;
+ return reshape_params_ ? reshape_params_->size : gfx::Size();
}
gfx::Size viewport_size_for_swap_buffers() const {
return device_viewport_size_;
@@ -290,8 +286,16 @@ class VIZ_SERVICE_EXPORT DirectRenderer {
bool ShouldApplyRoundedCorner(const DrawQuad* quad) const;
+ float CurrentFrameSDRWhiteLevel() const;
gfx::ColorSpace RootRenderPassColorSpace() const;
gfx::ColorSpace CurrentRenderPassColorSpace() const;
+ // Return the SkColorSpace for rendering to the current render pass. Unlike
+ // CurrentRenderPassColorSpace, this color space has the value of
+ // CurrentFrameSDRWhiteLevel incorporated into it.
+ sk_sp<SkColorSpace> CurrentRenderPassSkColorSpace() const {
+ return CurrentRenderPassColorSpace().ToSkColorSpace(
+ CurrentFrameSDRWhiteLevel());
+ }
const raw_ptr<const RendererSettings> settings_;
// Points to the viz-global singleton.
@@ -308,8 +312,6 @@ class VIZ_SERVICE_EXPORT DirectRenderer {
bool allow_empty_swap_ = false;
// Whether partial swap can be used.
bool use_partial_swap_ = false;
- // Whether overdraw feedback is enabled and can be used.
- bool overdraw_feedback_ = false;
// A map from RenderPass id to the single quad present in and replacing the
// RenderPass. The DrawQuads are owned by their RenderPasses, which outlive
@@ -350,10 +352,13 @@ class VIZ_SERVICE_EXPORT DirectRenderer {
return &current_frame_;
}
gfx::BufferFormat reshape_buffer_format() const {
- DCHECK(reshape_buffer_format_);
- return reshape_buffer_format_.value();
+ DCHECK(reshape_params_);
+ return reshape_params_->format;
+ }
+ gfx::ColorSpace reshape_color_space() const {
+ DCHECK(reshape_params_);
+ return reshape_params_->color_space;
}
- gfx::ColorSpace reshape_color_space() const { return reshape_color_space_; }
// Sets a DelegatedInkPointRendererSkiaForTest to be used for testing only, in
// order to save delegated ink metadata values that would otherwise be reset.
@@ -364,11 +369,7 @@ class VIZ_SERVICE_EXPORT DirectRenderer {
virtual void DrawDelegatedInkTrail();
bool initialized_ = false;
-#if DCHECK_IS_ON()
- bool overdraw_feedback_support_missing_logged_once_ = false;
- bool overdraw_tracing_support_missing_logged_once_ = false;
- bool supports_occlusion_query_ = false;
-#endif
+
gfx::Rect last_root_render_pass_scissor_rect_;
gfx::Size enlarge_pass_texture_amount_;
@@ -379,20 +380,16 @@ class VIZ_SERVICE_EXPORT DirectRenderer {
bool current_frame_valid_ = false;
// Time of most recent reshape that ended up with |device_viewport_size_| !=
- // |reshape_surface_size_|.
+ // |reshape_params->size|.
base::TimeTicks last_viewport_resize_time_;
bool next_frame_needs_full_frame_redraw_ = false;
- // Cached values given to Reshape(). The |reshape_buffer_format_| is optional
- // to prevent use of uninitialized values. This may be larger than the
- // |device_viewport_size_| that users see.
- gfx::Size reshape_surface_size_;
+ // Cached values given to Reshape(). The `reshape_params_` is optional
+ // to prevent use of uninitialized values. The size in these parameters
+ // may be larger than the `device_viewport_size_` that users see.
+ absl::optional<OutputSurface::ReshapeParams> reshape_params_;
gfx::Size device_viewport_size_;
- float reshape_device_scale_factor_ = 0.f;
- gfx::ColorSpace reshape_color_space_;
- absl::optional<gfx::BufferFormat> reshape_buffer_format_;
- bool reshape_use_stencil_ = false;
gfx::OverlayTransform reshape_display_transform_ =
gfx::OVERLAY_TRANSFORM_INVALID;
};
diff --git a/chromium/components/viz/service/display/display.cc b/chromium/components/viz/service/display/display.cc
index 40db30fb146..07d38ad3d92 100644
--- a/chromium/components/viz/service/display/display.cc
+++ b/chromium/components/viz/service/display/display.cc
@@ -24,6 +24,7 @@
#include "components/viz/common/display/renderer_settings.h"
#include "components/viz/common/features.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
+#include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/draw_quad.h"
#include "components/viz/common/quads/shared_quad_state.h"
@@ -34,12 +35,10 @@
#include "components/viz/service/display/delegated_ink_point_renderer_base.h"
#include "components/viz/service/display/direct_renderer.h"
#include "components/viz/service/display/display_client.h"
-#include "components/viz/service/display/display_resource_provider_gl.h"
#include "components/viz/service/display/display_resource_provider_null.h"
#include "components/viz/service/display/display_resource_provider_skia.h"
#include "components/viz/service/display/display_resource_provider_software.h"
#include "components/viz/service/display/display_scheduler.h"
-#include "components/viz/service/display/gl_renderer.h"
#include "components/viz/service/display/null_renderer.h"
#include "components/viz/service/display/output_surface.h"
#include "components/viz/service/display/renderer_utils.h"
@@ -49,8 +48,6 @@
#include "components/viz/service/display/surface_aggregator.h"
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_manager.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/ipc/scheduler_sequence.h"
#include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -108,9 +105,6 @@ int64_t GetStartingTraceId() {
gfx::PresentationFeedback SanitizePresentationFeedback(
const gfx::PresentationFeedback& feedback,
base::TimeTicks draw_time) {
- // Temporary to investigate large presentation times.
- // https://crbug.com/894440
- DCHECK(!draw_time.is_null());
if (feedback.timestamp.is_null())
return feedback;
@@ -130,26 +124,10 @@ gfx::PresentationFeedback SanitizePresentationFeedback(
gfx::PresentationFeedback::kVSync)) != 0)
? kAllowedDeltaFromFuture
: base::TimeDelta();
- if (feedback.timestamp > now + allowed_delta_from_future) {
- const auto diff = feedback.timestamp - now;
- UMA_HISTOGRAM_MEDIUM_TIMES(
- "Graphics.PresentationTimestamp.InvalidFromFuture", diff);
+ if ((feedback.timestamp > now + allowed_delta_from_future) ||
+ (feedback.timestamp < draw_time)) {
return gfx::PresentationFeedback::Failure();
}
-
- if (feedback.timestamp < draw_time) {
- const auto diff = draw_time - feedback.timestamp;
- UMA_HISTOGRAM_MEDIUM_TIMES(
- "Graphics.PresentationTimestamp.InvalidBeforeSwap", diff);
- return gfx::PresentationFeedback::Failure();
- }
-
- const auto difference = feedback.timestamp - draw_time;
- if (difference.InMinutes() > 3) {
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "Graphics.PresentationTimestamp.LargePresentationDelta", difference,
- base::Minutes(3), base::Hours(1), 50);
- }
return feedback;
}
@@ -360,15 +338,6 @@ Display::~Display() {
if (resource_provider_) {
resource_provider_->SetAllowAccessToGPUThread(true);
}
-#if BUILDFLAG(IS_ANDROID)
- // In certain cases, drivers hang when tearing down the display. Finishing
- // before teardown appears to address this. As we're during display teardown,
- // an additional finish should have minimal impact.
- // TODO(ericrk): Add a more robust workaround. crbug.com/899705
- if (auto* context = output_surface_->context_provider()) {
- context->ContextGL()->Finish();
- }
-#endif
if (no_pending_swaps_callback_)
std::move(no_pending_swaps_callback_).Run();
@@ -383,8 +352,6 @@ Display::~Display() {
// Only do this if Initialize() happened.
if (client_) {
- if (auto* context = output_surface_->context_provider())
- context->RemoveObserver(this);
if (skia_output_surface_)
skia_output_surface_->RemoveContextLostObserver(this);
}
@@ -426,9 +393,6 @@ void Display::Initialize(DisplayClient* client,
// This depends on assumptions that Display::Initialize will happen on the
// same callstack as the ContextProvider being created/initialized or else
// it could miss a callback before setting this.
- if (auto* context = output_surface_->context_provider())
- context->AddObserver(this);
-
if (skia_output_surface_)
skia_output_surface_->AddContextLostObserver(this);
}
@@ -508,8 +472,7 @@ void Display::DisableSwapUntilResize(
scheduler_->ForceImmediateSwapIfPossible();
if (no_pending_swaps_callback && pending_swaps_ > 0 &&
- (output_surface_->context_provider() ||
- output_surface_->AsSkiaOutputSurface())) {
+ output_surface_->AsSkiaOutputSurface()) {
no_pending_swaps_callback_ = std::move(no_pending_swaps_callback);
}
@@ -562,14 +525,6 @@ void Display::InitializeRenderer(bool enable_shared_images) {
resource_provider.get(), overlay_processor_.get(),
skia_output_surface_);
resource_provider_ = std::move(resource_provider);
- } else if (output_surface_->context_provider()) {
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface_->context_provider(), enable_shared_images);
- renderer_ = std::make_unique<GLRenderer>(
- &settings_, debug_settings_, output_surface_.get(),
- resource_provider.get(), overlay_processor_.get(),
- current_task_runner_);
- resource_provider_ = std::move(resource_provider);
} else if (output_surface_->capabilities().skips_draw) {
auto resource_provider = std::make_unique<DisplayResourceProviderNull>();
renderer_ = std::make_unique<NullRenderer>(
@@ -895,21 +850,6 @@ bool Display::DrawAndSwap(const DrawAndSwapParams& params) {
renderer_->DrawFrame(&frame.render_pass_list, device_scale_factor_,
current_surface_size, display_color_spaces_,
std::move(frame.surface_damage_rect_list_));
- switch (output_surface_->type()) {
- case OutputSurface::Type::kSoftware:
- UMA_HISTOGRAM_COUNTS_1M(
- "Compositing.DirectRenderer.Software.DrawFrameUs",
- draw_timer->Elapsed().InMicroseconds());
- break;
- case OutputSurface::Type::kOpenGL:
- UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.GL.DrawFrameUs",
- draw_timer->Elapsed().InMicroseconds());
- break;
- case OutputSurface::Type::kVulkan:
- UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.VK.DrawFrameUs",
- draw_timer->Elapsed().InMicroseconds());
- break;
- }
} else {
TRACE_EVENT_INSTANT0("viz", "Draw skipped.", TRACE_EVENT_SCOPE_THREAD);
}
@@ -1113,12 +1053,6 @@ void Display::DidReceiveSwapBuffersAck(const gfx::SwapTimings& timings,
}
}
-void Display::DidReceiveTextureInUseResponses(
- const gpu::TextureInUseResponses& responses) {
- if (renderer_)
- renderer_->DidReceiveTextureInUseResponses(responses);
-}
-
void Display::DidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) {
if (client_)
diff --git a/chromium/components/viz/service/display/display.h b/chromium/components/viz/service/display/display.h
index 3cdb7e3012e..cb26d6cab8c 100644
--- a/chromium/components/viz/service/display/display.h
+++ b/chromium/components/viz/service/display/display.h
@@ -35,7 +35,6 @@
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_manager.h"
#include "components/viz/service/viz_service_export.h"
-#include "gpu/command_buffer/common/texture_in_use_response.h"
#include "ui/gfx/display_color_spaces.h"
#include "ui/gfx/overlay_transform.h"
#include "ui/gfx/swap_result.h"
@@ -161,8 +160,6 @@ class VIZ_SERVICE_EXPORT Display : public DisplaySchedulerClient,
void SetNeedsRedrawRect(const gfx::Rect& damage_rect) override;
void DidReceiveSwapBuffersAck(const gfx::SwapTimings& timings,
gfx::GpuFenceHandle release_fence) override;
- void DidReceiveTextureInUseResponses(
- const gpu::TextureInUseResponses& responses) override;
void DidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) override;
void DidSwapWithSize(const gfx::Size& pixel_size) override;
diff --git a/chromium/components/viz/service/display/display_damage_tracker.cc b/chromium/components/viz/service/display/display_damage_tracker.cc
index 2c477b65304..94bcdf68391 100644
--- a/chromium/components/viz/service/display/display_damage_tracker.cc
+++ b/chromium/components/viz/service/display/display_damage_tracker.cc
@@ -176,11 +176,6 @@ bool DisplayDamageTracker::OnSurfaceDamaged(const SurfaceId& surface_id,
return display_damaged;
}
-void DisplayDamageTracker::OnSurfaceDestroyed(const SurfaceId& surface_id) {
- TRACE_EVENT0("viz", "DisplayDamageTracker::SurfaceDestroyed");
- aggregator_->ReleaseResources(surface_id);
-}
-
void DisplayDamageTracker::OnSurfaceDamageExpected(const SurfaceId& surface_id,
const BeginFrameArgs& args) {
TRACE_EVENT1("viz", "DisplayDamageTracker::SurfaceDamageExpected",
diff --git a/chromium/components/viz/service/display/display_damage_tracker.h b/chromium/components/viz/service/display/display_damage_tracker.h
index dfc451ee009..3abe233ff1c 100644
--- a/chromium/components/viz/service/display/display_damage_tracker.h
+++ b/chromium/components/viz/service/display/display_damage_tracker.h
@@ -81,7 +81,6 @@ class VIZ_SERVICE_EXPORT DisplayDamageTracker : public SurfaceObserver {
void OnSurfaceMarkedForDestruction(const SurfaceId& surface_id) override;
bool OnSurfaceDamaged(const SurfaceId& surface_id,
const BeginFrameAck& ack) override;
- void OnSurfaceDestroyed(const SurfaceId& surface_id) override;
void OnSurfaceDamageExpected(const SurfaceId& surface_id,
const BeginFrameArgs& args) override;
diff --git a/chromium/components/viz/service/display/display_perftest.cc b/chromium/components/viz/service/display/display_perftest.cc
index 5327ee598c3..0ee8fd42e42 100644
--- a/chromium/components/viz/service/display/display_perftest.cc
+++ b/chromium/components/viz/service/display/display_perftest.cc
@@ -22,7 +22,7 @@
#include "components/viz/service/display/overlay_processor_stub.h"
#include "components/viz/service/display/shared_bitmap_manager.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
-#include "components/viz/test/fake_output_surface.h"
+#include "components/viz/test/fake_skia_output_surface.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_result_reporter.h"
@@ -68,8 +68,8 @@ class RemoveOverdrawQuadPerfTest : public testing::Test {
auto scheduler = std::make_unique<DisplayScheduler>(
&begin_frame_source_, task_runner_.get(), PendingSwapParams(1));
- std::unique_ptr<FakeOutputSurface> output_surface =
- FakeOutputSurface::Create3d();
+ std::unique_ptr<FakeSkiaOutputSurface> output_surface =
+ FakeSkiaOutputSurface::Create3d();
auto overlay_processor = std::make_unique<OverlayProcessorStub>();
// Normally display will need to take ownership of a
diff --git a/chromium/components/viz/service/display/display_resource_provider.cc b/chromium/components/viz/service/display/display_resource_provider.cc
index eb87fff486f..6f80ebffcfb 100644
--- a/chromium/components/viz/service/display/display_resource_provider.cc
+++ b/chromium/components/viz/service/display/display_resource_provider.cc
@@ -8,6 +8,7 @@
#include <string>
#include "base/atomic_sequence_num.h"
+#include "base/notreached.h"
#include "base/numerics/safe_math.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -306,9 +307,9 @@ void DisplayResourceProvider::TryReleaseResource(ResourceId id,
}
}
-bool DisplayResourceProvider::ReadLockFenceHasPassed(
+bool DisplayResourceProvider::ResourceFenceHasPassed(
const ChildResource* resource) {
- return !resource->read_lock_fence || resource->read_lock_fence->HasPassed();
+ return !resource->resource_fence || resource->resource_fence->HasPassed();
}
DisplayResourceProvider::CanDeleteNowResult
@@ -322,7 +323,7 @@ DisplayResourceProvider::CanDeleteNow(const Child& child_info,
// Defer this resource deletion.
return CanDeleteNowResult::kNo;
- } else if (!ReadLockFenceHasPassed(&resource)) {
+ } else if (!ResourceFenceHasPassed(&resource)) {
// TODO(dcastagna): see if it's possible to use this logic for
// the branch above too, where the resource is locked or still exported.
// We can't postpone the deletion, so we'll have to lose it.
@@ -473,7 +474,8 @@ void DisplayResourceProvider::ScopedReadLockSharedImage::SetReleaseFence(
bool DisplayResourceProvider::ScopedReadLockSharedImage::HasReadLockFence()
const {
DCHECK(resource_);
- return resource_->transferable.read_lock_fences_enabled;
+ return resource_->transferable.synchronization_type ==
+ TransferableResource::SynchronizationType::kGpuCommandsCompleted;
}
void DisplayResourceProvider::ScopedReadLockSharedImage::Reset() {
diff --git a/chromium/components/viz/service/display/display_resource_provider.h b/chromium/components/viz/service/display/display_resource_provider.h
index 5d897625a42..be07f99feb7 100644
--- a/chromium/components/viz/service/display/display_resource_provider.h
+++ b/chromium/components/viz/service/display/display_resource_provider.h
@@ -162,10 +162,16 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
// Sets the current read fence. If a resource is locked for read
// and has read fences enabled, the resource will not allow writes
- // until this fence has passed.
- void SetReadLockFence(ResourceFence* fence) {
- current_read_lock_fence_ = fence;
+ // until this fence has passed. This is used if a client uses
+ // TransferableResource::SynchronizationType::kGpuCommandsCompleted.
+ void SetGpuCommandsCompletedFence(ResourceFence* fence) {
+ current_gpu_commands_completed_fence_ = fence;
}
+ // Sets the current release fence. If a client uses
+ // TransferableResource::SynchronizationType::kReleaseFence, resources must be
+ // returned only after a release fence is stored in this resource fence.
+ // Returned only when gpu commands and the gpu fence are submitted.
+ void SetReleaseFence(ResourceFence* fence) { current_release_fence_ = fence; }
// Creates accounting for a child. Returns a child ID. surface_id is used to
// associate resources to the surface they belong to. This is used for
@@ -333,12 +339,14 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
// mapped for use in the display compositor.
base::UnguessableToken shared_bitmap_tracing_guid;
- // A fence used for accessing a gpu resource for reading, that ensures any
- // writing done to the resource has been completed. This is implemented and
- // used to implement transferring ownership of the resource from the client
- // to the service, and in the GL drawing code before reading from the
- // texture.
- scoped_refptr<ResourceFence> read_lock_fence;
+ // A fence used for returning resources after the display compositor has
+ // completed accessing the resources it received from a client. This can
+ // either be a read lock or a release fence. If the |transferable| has
+ // synchronization type set as kGpuCommandsCompleted, the resource can be
+ // returned after ResourceFence::HasPassed is true. If the |transferable|
+ // has the synchronization type set as kReleaseFence, the resource can be
+ // returned after the fence has a release fence set.
+ scoped_refptr<ResourceFence> resource_fence;
// SkiaRenderer specific details about this resource. Added to ChildResource
// to avoid map lookups further down the pipeline.
@@ -371,10 +379,7 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
ChildResource* TryGetResource(ResourceId id);
void TryReleaseResource(ResourceId id, ChildResource* resource);
- // Binds the given GL resource to a texture target for sampling using the
- // specified filter for both minification and magnification. Returns the
- // texture target used. The resource must be locked for reading.
- bool ReadLockFenceHasPassed(const ChildResource* resource);
+ bool ResourceFenceHasPassed(const ChildResource* resource);
void DeleteAndReturnUnusedResourcesToChild(
ChildMap::iterator child_it,
@@ -404,7 +409,8 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
ChildMap children_;
base::flat_map<int, std::vector<ResourceId>> batched_returning_resources_;
- scoped_refptr<ResourceFence> current_read_lock_fence_;
+ scoped_refptr<ResourceFence> current_gpu_commands_completed_fence_;
+ scoped_refptr<ResourceFence> current_release_fence_;
// Keep track of whether deleted resources should be batched up or returned
// immediately.
int batch_return_resources_lock_count_ = 0;
diff --git a/chromium/components/viz/service/display/display_resource_provider_gl.cc b/chromium/components/viz/service/display/display_resource_provider_gl.cc
deleted file mode 100644
index 501cd716a1f..00000000000
--- a/chromium/components/viz/service/display/display_resource_provider_gl.cc
+++ /dev/null
@@ -1,425 +0,0 @@
-// Copyright 2021 The Chromium 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/viz/service/display/display_resource_provider_gl.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/dcheck_is_on.h"
-#include "base/memory/raw_ptr.h"
-#include "base/trace_event/trace_event.h"
-#include "build/build_config.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "ui/gfx/gpu_fence.h"
-#include "ui/gl/gl_fence.h"
-
-using gpu::gles2::GLES2Interface;
-
-namespace viz {
-namespace {
-
-class ScopedSetActiveTexture {
- public:
- ScopedSetActiveTexture(GLES2Interface* gl, GLenum unit)
- : gl_(gl), unit_(unit) {
-#if DCHECK_IS_ON()
- GLint active_unit = 0;
- gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit);
- DCHECK_EQ(GL_TEXTURE0, active_unit);
-#endif
-
- if (unit_ != GL_TEXTURE0)
- gl_->ActiveTexture(unit_);
- }
-
- ~ScopedSetActiveTexture() {
- // Active unit being GL_TEXTURE0 is effectively the ground state.
- if (unit_ != GL_TEXTURE0)
- gl_->ActiveTexture(GL_TEXTURE0);
- }
-
- private:
- raw_ptr<GLES2Interface> gl_;
- GLenum unit_;
-};
-
-} // namespace
-
-DisplayResourceProviderGL::DisplayResourceProviderGL(
- ContextProvider* compositor_context_provider,
- bool enable_shared_images)
- : DisplayResourceProvider(DisplayResourceProvider::kGpu),
- compositor_context_provider_(compositor_context_provider),
- enable_shared_images_(enable_shared_images) {
- DCHECK(compositor_context_provider_);
-}
-
-DisplayResourceProviderGL::~DisplayResourceProviderGL() {
- Destroy();
- GLES2Interface* gl = ContextGL();
- if (gl)
- gl->Finish();
-
- while (!resources_.empty())
- DeleteResourceInternal(resources_.begin());
-}
-
-void DisplayResourceProviderGL::DeleteResourceInternal(
- ResourceMap::iterator it) {
- TRACE_EVENT0("viz", "DisplayResourceProvider::DeleteResourceInternal");
- ChildResource* resource = &it->second;
-
- if (resource->gl_id) {
- GLES2Interface* gl = ContextGL();
- DCHECK(gl);
- gl->DeleteTextures(1, &resource->gl_id);
- }
-
- resources_.erase(it);
-}
-
-GLES2Interface* DisplayResourceProviderGL::ContextGL() const {
- DCHECK(compositor_context_provider_);
- return compositor_context_provider_->ContextGL();
-}
-
-const DisplayResourceProvider::ChildResource*
-DisplayResourceProviderGL::LockForRead(ResourceId id, bool overlay_only) {
- // TODO(ericrk): We should never fail TryGetResource, but we appear to be
- // doing so on Android in rare cases. Handle this gracefully until a better
- // solution can be found. https://crbug.com/811858
- ChildResource* resource = TryGetResource(id);
- if (!resource)
- return nullptr;
-
- // Mailbox sync_tokens must be processed by a call to WaitSyncToken() prior to
- // calling LockForRead().
- DCHECK_NE(NEEDS_WAIT, resource->synchronization_state());
- DCHECK(resource->is_gpu_resource_type());
-
- const gpu::Mailbox& mailbox = resource->transferable.mailbox_holder.mailbox;
- GLES2Interface* gl = ContextGL();
- DCHECK(gl);
- if (!resource->gl_id) {
- if (mailbox.IsSharedImage() && enable_shared_images_) {
- resource->gl_id =
- gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
- } else {
- resource->gl_id = gl->CreateAndConsumeTextureCHROMIUM(
- resource->transferable.mailbox_holder.mailbox.name);
- }
- resource->SetLocallyUsed();
- }
- if (mailbox.IsSharedImage() && enable_shared_images_) {
- if (overlay_only) {
- if (resource->lock_for_overlay_count == 0) {
- // If |lock_for_read_count| > 0, then BeginSharedImageAccess has
- // already been called with READ, so don't re-lock with OVERLAY.
- if (resource->lock_for_read_count == 0) {
- gl->BeginSharedImageAccessDirectCHROMIUM(
- resource->gl_id, GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM);
- }
- }
- } else {
- if (resource->lock_for_read_count == 0) {
- // If |lock_for_overlay_count| > 0, then we have already begun access
- // for OVERLAY. End this access and "upgrade" it to READ.
- // See https://crbug.com/1113925 for how this can go wrong.
- if (resource->lock_for_overlay_count > 0)
- gl->EndSharedImageAccessDirectCHROMIUM(resource->gl_id);
- gl->BeginSharedImageAccessDirectCHROMIUM(
- resource->gl_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
- }
- }
- }
-
- if (overlay_only)
- resource->lock_for_overlay_count++;
- else
- resource->lock_for_read_count++;
- if (resource->transferable.read_lock_fences_enabled) {
- if (current_read_lock_fence_.get())
- current_read_lock_fence_->Set();
- resource->read_lock_fence = current_read_lock_fence_;
- }
-
- return resource;
-}
-
-void DisplayResourceProviderGL::UnlockForRead(ResourceId id,
- bool overlay_only) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- ChildResource* resource = TryGetResource(id);
- // TODO(ericrk): We should never fail to find id, but we appear to be
- // doing so on Android in rare cases. Handle this gracefully until a better
- // solution can be found. https://crbug.com/811858
- if (!resource)
- return;
-
- DCHECK(resource->is_gpu_resource_type());
- if (resource->transferable.mailbox_holder.mailbox.IsSharedImage() &&
- enable_shared_images_) {
- // If this is the last READ or OVERLAY access, then end access.
- if (resource->lock_for_read_count + resource->lock_for_overlay_count == 1) {
- DCHECK(resource->gl_id);
- GLES2Interface* gl = ContextGL();
- DCHECK(gl);
- if (!resource->release_fence.is_null()) {
- auto fence = gfx::GpuFence(resource->release_fence.Clone());
- if (gl::GLFence::IsGpuFenceSupported()) {
- auto fence_id =
- gl->CreateClientGpuFenceCHROMIUM(fence.AsClientGpuFence());
- gl->WaitGpuFenceCHROMIUM(fence_id);
- gl->DestroyGpuFenceCHROMIUM(fence_id);
- } else {
- fence.Wait();
- }
- }
- gl->EndSharedImageAccessDirectCHROMIUM(resource->gl_id);
- }
- }
- if (overlay_only) {
- DCHECK_GT(resource->lock_for_overlay_count, 0);
- resource->lock_for_overlay_count--;
- } else {
- DCHECK_GT(resource->lock_for_read_count, 0);
- resource->lock_for_read_count--;
- }
- TryReleaseResource(id, resource);
-}
-
-std::vector<ReturnedResource>
-DisplayResourceProviderGL::DeleteAndReturnUnusedResourcesToChildImpl(
- Child& child_info,
- DeleteStyle style,
- const std::vector<ResourceId>& unused) {
- std::vector<ReturnedResource> to_return;
- // Reserve enough space to avoid re-allocating, so we can keep item pointers
- // for later using.
- to_return.reserve(unused.size());
- std::vector<ReturnedResource*> need_synchronization_resources;
- std::vector<GLbyte*> unverified_sync_tokens;
-
- GLES2Interface* gl = ContextGL();
- DCHECK(gl);
- DCHECK(can_access_gpu_thread_);
- for (ResourceId local_id : unused) {
- auto it = resources_.find(local_id);
- CHECK(it != resources_.end());
- ChildResource& resource = it->second;
- DCHECK(resource.is_gpu_resource_type());
-
- ResourceId child_id = resource.transferable.id;
- DCHECK(child_info.child_to_parent_map.count(child_id));
-
- auto can_delete = CanDeleteNow(child_info, resource, style);
- if (can_delete == CanDeleteNowResult::kNo) {
- // Defer this resource deletion.
- resource.marked_for_deletion = true;
- continue;
- }
-
- const bool is_lost = can_delete == CanDeleteNowResult::kYesButLoseResource;
-
- if (resource.gl_id && resource.filter != resource.transferable.filter) {
- DCHECK(resource.transferable.mailbox_holder.texture_target);
- DCHECK(!resource.ShouldWaitSyncToken());
- gl->BindTexture(resource.transferable.mailbox_holder.texture_target,
- resource.gl_id);
- gl->TexParameteri(resource.transferable.mailbox_holder.texture_target,
- GL_TEXTURE_MIN_FILTER, resource.transferable.filter);
- gl->TexParameteri(resource.transferable.mailbox_holder.texture_target,
- GL_TEXTURE_MAG_FILTER, resource.transferable.filter);
- resource.SetLocallyUsed();
- }
-
- to_return.emplace_back(child_id, resource.sync_token(),
- std::move(resource.release_fence),
- resource.imported_count, is_lost);
- auto& returned = to_return.back();
-
- if (resource.needs_sync_token()) {
- need_synchronization_resources.push_back(&returned);
- } else if (returned.sync_token.HasData() &&
- !returned.sync_token.verified_flush()) {
- unverified_sync_tokens.push_back(returned.sync_token.GetData());
- }
-
- child_info.child_to_parent_map.erase(child_id);
- resource.imported_count = 0;
- DeleteResourceInternal(it);
- }
-
- gpu::SyncToken new_sync_token;
- if (!need_synchronization_resources.empty()) {
- gl->GenUnverifiedSyncTokenCHROMIUM(new_sync_token.GetData());
- unverified_sync_tokens.push_back(new_sync_token.GetData());
- }
-
- if (!unverified_sync_tokens.empty()) {
- gl->VerifySyncTokensCHROMIUM(unverified_sync_tokens.data(),
- unverified_sync_tokens.size());
- }
-
- // Set sync token after verification.
- for (ReturnedResource* returned : need_synchronization_resources)
- returned->sync_token = new_sync_token;
-
- return to_return;
-}
-
-GLenum DisplayResourceProviderGL::GetResourceTextureTarget(ResourceId id) {
- return GetResource(id)->transferable.mailbox_holder.texture_target;
-}
-
-void DisplayResourceProviderGL::WaitSyncToken(ResourceId id) {
- ChildResource* resource = TryGetResource(id);
- // TODO(ericrk): We should never fail TryGetResource, but we appear to
- // be doing so on Android in rare cases. Handle this gracefully until a
- // better solution can be found. https://crbug.com/811858
- if (!resource)
- return;
- WaitSyncTokenInternal(resource);
-}
-
-GLenum DisplayResourceProviderGL::BindForSampling(ResourceId resource_id,
- GLenum unit,
- GLenum filter) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- GLES2Interface* gl = ContextGL();
- auto it = resources_.find(resource_id);
- // TODO(ericrk): We should never fail to find resource_id, but we appear to
- // be doing so on Android in rare cases. Handle this gracefully until a
- // better solution can be found. https://crbug.com/811858
- if (it == resources_.end())
- return GL_TEXTURE_2D;
-
- ChildResource* resource = &it->second;
- DCHECK(resource->lock_for_read_count);
-
- ScopedSetActiveTexture scoped_active_tex(gl, unit);
- GLenum target = resource->transferable.mailbox_holder.texture_target;
- gl->BindTexture(target, resource->gl_id);
-
- // Texture parameters can be modified by concurrent reads so reset them
- // before binding the texture. See https://crbug.com/1092080.
- gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
- gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
- resource->filter = filter;
-
- return target;
-}
-
-void DisplayResourceProviderGL::WaitSyncTokenInternal(ChildResource* resource) {
- DCHECK(resource);
- if (!resource->ShouldWaitSyncToken())
- return;
- GLES2Interface* gl = ContextGL();
- DCHECK(gl);
- // In the case of context lost, this sync token may be empty (see comment in
- // the UpdateSyncToken() function). The WaitSyncTokenCHROMIUM() function
- // handles empty sync tokens properly so just wait anyways and update the
- // state the synchronized.
- gl->WaitSyncTokenCHROMIUM(resource->sync_token().GetConstData());
- resource->SetSynchronized();
-}
-
-DisplayResourceProviderGL::ScopedReadLockGL::ScopedReadLockGL(
- DisplayResourceProviderGL* resource_provider,
- ResourceId resource_id)
- : resource_provider_(resource_provider), resource_id_(resource_id) {
- const ChildResource* resource =
- resource_provider->LockForRead(resource_id, false /* overlay_only */);
- // TODO(ericrk): We should never fail LockForRead, but we appear to be
- // doing so on Android in rare cases. Handle this gracefully until a better
- // solution can be found. https://crbug.com/811858
- if (!resource)
- return;
-
- texture_id_ = resource->gl_id;
- target_ = resource->transferable.mailbox_holder.texture_target;
- size_ = resource->transferable.size;
- color_space_ = resource->transferable.color_space;
- hdr_metadata_ = resource->transferable.hdr_metadata;
-}
-
-DisplayResourceProviderGL::ScopedReadLockGL::~ScopedReadLockGL() {
- resource_provider_->UnlockForRead(resource_id_, false /* overlay_only */);
-}
-
-DisplayResourceProviderGL::ScopedSamplerGL::ScopedSamplerGL(
- DisplayResourceProviderGL* resource_provider,
- ResourceId resource_id,
- GLenum filter)
- : resource_lock_(resource_provider, resource_id),
- unit_(GL_TEXTURE0),
- target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {}
-
-DisplayResourceProviderGL::ScopedSamplerGL::ScopedSamplerGL(
- DisplayResourceProviderGL* resource_provider,
- ResourceId resource_id,
- GLenum unit,
- GLenum filter)
- : resource_lock_(resource_provider, resource_id),
- unit_(unit),
- target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {}
-
-DisplayResourceProviderGL::ScopedSamplerGL::~ScopedSamplerGL() = default;
-
-DisplayResourceProviderGL::ScopedOverlayLockGL::ScopedOverlayLockGL(
- DisplayResourceProviderGL* resource_provider,
- ResourceId resource_id)
- : resource_provider_(resource_provider), resource_id_(resource_id) {
- const ChildResource* resource =
- resource_provider->LockForRead(resource_id, true /* overlay_only */);
- if (!resource)
- return;
-
- texture_id_ = resource->gl_id;
-}
-
-DisplayResourceProviderGL::ScopedOverlayLockGL::~ScopedOverlayLockGL() {
- resource_provider_->UnlockForRead(resource_id_, true /* overlay_only */);
-}
-
-void DisplayResourceProviderGL::ScopedOverlayLockGL::SetReleaseFence(
- gfx::GpuFenceHandle release_fence) {
- auto* resource = resource_provider_->GetResource(resource_id_);
- DCHECK(resource);
- resource->release_fence = std::move(release_fence);
-}
-
-bool DisplayResourceProviderGL::ScopedOverlayLockGL::HasReadLockFence() const {
- auto* resource = resource_provider_->GetResource(resource_id_);
- DCHECK(resource);
- return resource->transferable.read_lock_fences_enabled;
-}
-
-DisplayResourceProviderGL::SynchronousFence::SynchronousFence(
- gpu::gles2::GLES2Interface* gl)
- : gl_(gl), has_synchronized_(true) {}
-
-DisplayResourceProviderGL::SynchronousFence::~SynchronousFence() = default;
-
-void DisplayResourceProviderGL::SynchronousFence::Set() {
- has_synchronized_ = false;
-}
-
-bool DisplayResourceProviderGL::SynchronousFence::HasPassed() {
- if (!has_synchronized_) {
- has_synchronized_ = true;
- Synchronize();
- }
- return true;
-}
-
-void DisplayResourceProviderGL::SynchronousFence::Synchronize() {
- TRACE_EVENT0("viz", "DisplayResourceProvider::SynchronousFence::Synchronize");
- gl_->Finish();
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/display_resource_provider_gl.h b/chromium/components/viz/service/display/display_resource_provider_gl.h
deleted file mode 100644
index 8a75cf568a8..00000000000
--- a/chromium/components/viz/service/display/display_resource_provider_gl.h
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2021 The Chromium 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 COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_GL_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_GL_H_
-
-#include <vector>
-
-#include "base/memory/raw_ptr.h"
-#include "components/viz/service/display/display_resource_provider.h"
-#include "components/viz/service/viz_service_export.h"
-
-namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-} // namespace gles2
-} // namespace gpu
-
-namespace viz {
-
-class ContextProvider;
-
-// DisplayResourceProvider implementation used with GLRenderer.
-class VIZ_SERVICE_EXPORT DisplayResourceProviderGL
- : public DisplayResourceProvider {
- public:
- // Android with GLRenderer doesn't support overlays with shared images
- // enabled. For everything else |enable_shared_images| is true.
- DisplayResourceProviderGL(ContextProvider* compositor_context_provider,
- bool enable_shared_images = true);
- ~DisplayResourceProviderGL() override;
-
- GLenum GetResourceTextureTarget(ResourceId id);
- void WaitSyncToken(ResourceId id);
-
- // The following lock classes are part of the DisplayResourceProvider API and
- // are needed to read the resource contents. The user must ensure that they
- // only use GL locks on GL resources, etc, and this is enforced by assertions.
- class VIZ_SERVICE_EXPORT ScopedReadLockGL {
- public:
- ScopedReadLockGL(DisplayResourceProviderGL* resource_provider,
- ResourceId resource_id);
- ~ScopedReadLockGL();
-
- ScopedReadLockGL(const ScopedReadLockGL&) = delete;
- ScopedReadLockGL& operator=(const ScopedReadLockGL&) = delete;
-
- GLuint texture_id() const { return texture_id_; }
- GLenum target() const { return target_; }
- const gfx::Size& size() const { return size_; }
- const gfx::ColorSpace& color_space() const { return color_space_; }
- const absl::optional<gfx::HDRMetadata>& hdr_metadata() const {
- return hdr_metadata_;
- }
-
- private:
- const raw_ptr<DisplayResourceProviderGL> resource_provider_;
- const ResourceId resource_id_;
-
- GLuint texture_id_ = 0;
- GLenum target_ = GL_TEXTURE_2D;
- gfx::Size size_;
- gfx::ColorSpace color_space_;
- absl::optional<gfx::HDRMetadata> hdr_metadata_;
- };
-
- class VIZ_SERVICE_EXPORT ScopedSamplerGL {
- public:
- ScopedSamplerGL(DisplayResourceProviderGL* resource_provider,
- ResourceId resource_id,
- GLenum filter);
- ScopedSamplerGL(DisplayResourceProviderGL* resource_provider,
- ResourceId resource_id,
- GLenum unit,
- GLenum filter);
- ~ScopedSamplerGL();
-
- ScopedSamplerGL(const ScopedSamplerGL&) = delete;
- ScopedSamplerGL& operator=(const ScopedSamplerGL&) = delete;
-
- GLuint texture_id() const { return resource_lock_.texture_id(); }
- GLenum target() const { return target_; }
- const gfx::ColorSpace& color_space() const {
- return resource_lock_.color_space();
- }
- const absl::optional<gfx::HDRMetadata>& hdr_metadata() const {
- return resource_lock_.hdr_metadata();
- }
-
- private:
- const ScopedReadLockGL resource_lock_;
- const GLenum unit_;
- const GLenum target_;
- };
-
- class VIZ_SERVICE_EXPORT ScopedOverlayLockGL {
- public:
- ScopedOverlayLockGL(DisplayResourceProviderGL* resource_provider,
- ResourceId resource_id);
- ~ScopedOverlayLockGL();
-
- ScopedOverlayLockGL(const ScopedOverlayLockGL&) = delete;
- ScopedOverlayLockGL& operator=(const ScopedOverlayLockGL&) = delete;
-
- GLuint texture_id() const { return texture_id_; }
-
- // Sets the given |release_fence| onto this resource.
- // This is propagated to ReturnedResource when the resource is freed.
- void SetReleaseFence(gfx::GpuFenceHandle release_fence);
-
- // Returns true iff this resource has a read lock fence set.
- bool HasReadLockFence() const;
-
- private:
- const raw_ptr<DisplayResourceProviderGL> resource_provider_;
- const ResourceId resource_id_;
- GLuint texture_id_ = 0;
- };
-
- class VIZ_SERVICE_EXPORT SynchronousFence : public ResourceFence {
- public:
- explicit SynchronousFence(gpu::gles2::GLES2Interface* gl);
-
- SynchronousFence(const SynchronousFence&) = delete;
- SynchronousFence& operator=(const SynchronousFence&) = delete;
-
- // ResourceFence implementation.
- void Set() override;
- bool HasPassed() override;
-
- // Returns true if fence has been set but not yet synchornized.
- bool has_synchronized() const { return has_synchronized_; }
-
- private:
- ~SynchronousFence() override;
-
- void Synchronize();
-
- raw_ptr<gpu::gles2::GLES2Interface> gl_;
- bool has_synchronized_;
- };
-
- private:
- const ChildResource* LockForRead(ResourceId id, bool overlay_only);
- void UnlockForRead(ResourceId id, bool overlay_only);
-
- // DisplayResourceProvider overrides:
- std::vector<ReturnedResource> DeleteAndReturnUnusedResourcesToChildImpl(
- Child& child_info,
- DeleteStyle style,
- const std::vector<ResourceId>& unused) override;
-
- gpu::gles2::GLES2Interface* ContextGL() const;
- void DeleteResourceInternal(ResourceMap::iterator it);
- GLenum BindForSampling(ResourceId resource_id, GLenum unit, GLenum filter);
- void WaitSyncTokenInternal(ChildResource* resource);
-
- const raw_ptr<ContextProvider> compositor_context_provider_;
- const bool enable_shared_images_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_GL_H_
diff --git a/chromium/components/viz/service/display/display_resource_provider_gl_unittest.cc b/chromium/components/viz/service/display/display_resource_provider_gl_unittest.cc
deleted file mode 100644
index 1c2d058d1a3..00000000000
--- a/chromium/components/viz/service/display/display_resource_provider_gl_unittest.cc
+++ /dev/null
@@ -1,718 +0,0 @@
-// Copyright 2021 The Chromium 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/viz/service/display/display_resource_provider_gl.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <set>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/check.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/ref_counted.h"
-#include "build/build_config.h"
-#include "components/viz/client/client_resource_provider.h"
-#include "components/viz/common/resources/release_callback.h"
-#include "components/viz/common/resources/resource_format_utils.h"
-#include "components/viz/common/resources/returned_resource.h"
-#include "components/viz/test/test_context_provider.h"
-#include "components/viz/test/test_gles2_interface.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
-#include "ui/gfx/geometry/rect.h"
-
-using testing::_;
-using testing::ByMove;
-using testing::DoAll;
-using testing::Return;
-using testing::SaveArg;
-
-namespace viz {
-namespace {
-
-class MockReleaseCallback {
- public:
- MOCK_METHOD2(Released, void(const gpu::SyncToken& token, bool lost));
-};
-
-MATCHER_P(MatchesSyncToken, sync_token, "") {
- gpu::SyncToken other;
- memcpy(&other, arg, sizeof(other));
- return other == sync_token;
-}
-
-static void CollectResources(std::vector<ReturnedResource>* array,
- std::vector<ReturnedResource> returned) {
- array->insert(array->end(), std::make_move_iterator(returned.begin()),
- std::make_move_iterator(returned.end()));
-}
-
-class ResourceProviderGLES2Interface : public TestGLES2Interface {
- public:
- ResourceProviderGLES2Interface() = default;
-
- void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override {
- gpu::SyncToken sync_token_data;
- if (sync_token)
- memcpy(&sync_token_data, sync_token, sizeof(sync_token_data));
-
- if (sync_token_data.release_count() >
- last_waited_sync_token_.release_count()) {
- last_waited_sync_token_ = sync_token_data;
- }
- }
-
- const gpu::SyncToken& last_waited_sync_token() const {
- return last_waited_sync_token_;
- }
-
- private:
- gpu::SyncToken last_waited_sync_token_;
-};
-
-class DisplayResourceProviderGLTest : public testing::Test {
- public:
- DisplayResourceProviderGLTest() {
- auto gl_owned = std::make_unique<ResourceProviderGLES2Interface>();
- gl_ = gl_owned.get();
- context_provider_ = TestContextProvider::Create(std::move(gl_owned));
- context_provider_->UnboundTestContextGL()
- ->set_support_texture_format_bgra8888(true);
- context_provider_->BindToCurrentThread();
-
- child_context_provider_ = TestContextProvider::Create();
- child_context_provider_->UnboundTestContextGL()
- ->set_support_texture_format_bgra8888(true);
- child_context_provider_->BindToCurrentThread();
-
- resource_provider_ =
- std::make_unique<DisplayResourceProviderGL>(context_provider_.get());
-
- child_resource_provider_ = std::make_unique<ClientResourceProvider>();
- }
-
- ~DisplayResourceProviderGLTest() override {
- child_resource_provider_->ShutdownAndReleaseAllResources();
- }
-
- static ReturnCallback GetReturnCallback(
- std::vector<ReturnedResource>* array) {
- return base::BindRepeating(&CollectResources, array);
- }
-
- static void SetResourceFilter(DisplayResourceProviderGL* resource_provider,
- ResourceId id,
- GLenum filter) {
- DisplayResourceProviderGL::ScopedSamplerGL sampler(resource_provider, id,
- GL_TEXTURE_2D, filter);
- }
-
- TransferableResource CreateResource(ResourceFormat format) {
- constexpr gfx::Size size(64, 64);
- gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
- gpu::SyncToken sync_token = GenSyncToken();
- EXPECT_TRUE(sync_token.HasData());
-
- TransferableResource gl_resource = TransferableResource::MakeGL(
- gpu_mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token, size,
- false /* is_overlay_candidate */);
- gl_resource.format = format;
- return gl_resource;
- }
-
- ResourceId MakeGpuResourceAndSendToDisplay(
- GLuint filter,
- GLuint target,
- const gpu::SyncToken& sync_token,
- DisplayResourceProvider* resource_provider) {
- ReturnCallback return_callback = base::DoNothing();
-
- int child = resource_provider->CreateChild(return_callback, SurfaceId());
-
- gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
- constexpr gfx::Size size(64, 64);
- auto resource =
- TransferableResource::MakeGL(gpu_mailbox, GL_LINEAR, target, sync_token,
- size, false /* is_overlay_candidate */);
- resource.id = ResourceId(11);
- resource_provider->ReceiveFromChild(child, {resource});
- auto& map = resource_provider->GetChildToParentMap(child);
- return map.find(resource.id)->second;
- }
-
- gpu::SyncToken GenSyncToken() {
- gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
- gpu::CommandBufferId::FromUnsafeValue(0x123),
- next_fence_sync_++);
- sync_token.SetVerifyFlush();
- return sync_token;
- }
-
- protected:
- raw_ptr<ResourceProviderGLES2Interface> gl_ = nullptr;
- uint64_t next_fence_sync_ = 1;
- scoped_refptr<TestContextProvider> context_provider_;
- scoped_refptr<TestContextProvider> child_context_provider_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
- std::unique_ptr<ClientResourceProvider> child_resource_provider_;
-};
-
-TEST_F(DisplayResourceProviderGLTest, ReadLockCountStopsReturnToChildOrDelete) {
- MockReleaseCallback release;
- TransferableResource tran = CreateResource(RGBA_8888);
- ResourceId id1 = child_resource_provider_->ImportResource(
- tran, base::BindOnce(&MockReleaseCallback::Released,
- base::Unretained(&release)));
-
- std::vector<ReturnedResource> returned_to_child;
- int child_id = resource_provider_->CreateChild(
- GetReturnCallback(&returned_to_child), SurfaceId());
- {
- // Transfer some resources to the parent.
- std::vector<TransferableResource> list;
- child_resource_provider_->PrepareSendToParent(
- {id1}, &list,
- static_cast<RasterContextProvider*>(child_context_provider_.get()));
- ASSERT_EQ(1u, list.size());
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
-
- resource_provider_->ReceiveFromChild(child_id, list);
-
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- resource_provider_->GetChildToParentMap(child_id);
- ResourceId mapped_resource_id = resource_map[list[0].id];
- resource_provider_->WaitSyncToken(mapped_resource_id);
- DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
- mapped_resource_id);
-
- resource_provider_->DeclareUsedResourcesFromChild(child_id,
- ResourceIdSet());
- EXPECT_EQ(0u, returned_to_child.size());
- }
-
- EXPECT_EQ(1u, returned_to_child.size());
- child_resource_provider_->ReceiveReturnsFromParent(
- std::move(returned_to_child));
-
- // No need to wait for the sync token here -- it will be returned to the
- // client on delete.
- {
- EXPECT_CALL(release, Released(_, _));
- child_resource_provider_->RemoveImportedResource(id1);
- }
-
- resource_provider_->DestroyChild(child_id);
-}
-
-class TestFence : public ResourceFence {
- public:
- TestFence() = default;
-
- // ResourceFence implementation.
- void Set() override {}
- bool HasPassed() override { return passed; }
-
- bool passed = false;
-
- private:
- ~TestFence() override = default;
-};
-
-TEST_F(DisplayResourceProviderGLTest, ReadLockFenceStopsReturnToChildOrDelete) {
- MockReleaseCallback release;
- TransferableResource tran1 = CreateResource(RGBA_8888);
- tran1.read_lock_fences_enabled = true;
- ResourceId id1 = child_resource_provider_->ImportResource(
- tran1, base::BindOnce(&MockReleaseCallback::Released,
- base::Unretained(&release)));
-
- std::vector<ReturnedResource> returned_to_child;
- int child_id = resource_provider_->CreateChild(
- GetReturnCallback(&returned_to_child), SurfaceId());
-
- // Transfer some resources to the parent.
- std::vector<TransferableResource> list;
- child_resource_provider_->PrepareSendToParent(
- {id1}, &list,
- static_cast<RasterContextProvider*>(child_context_provider_.get()));
- ASSERT_EQ(1u, list.size());
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
- EXPECT_TRUE(list[0].read_lock_fences_enabled);
-
- resource_provider_->ReceiveFromChild(child_id, list);
-
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- resource_provider_->GetChildToParentMap(child_id);
-
- scoped_refptr<TestFence> fence(new TestFence);
- resource_provider_->SetReadLockFence(fence.get());
- {
- ResourceId parent_id = resource_map[list.front().id];
- resource_provider_->WaitSyncToken(parent_id);
- DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
- parent_id);
- }
- resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
- EXPECT_EQ(0u, returned_to_child.size());
-
- resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
- EXPECT_EQ(0u, returned_to_child.size());
- fence->passed = true;
-
- resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
- EXPECT_EQ(1u, returned_to_child.size());
-
- child_resource_provider_->ReceiveReturnsFromParent(
- std::move(returned_to_child));
- EXPECT_CALL(release, Released(_, _));
- child_resource_provider_->RemoveImportedResource(id1);
-}
-
-TEST_F(DisplayResourceProviderGLTest, ReadLockFenceDestroyChild) {
- MockReleaseCallback release;
-
- TransferableResource tran1 = CreateResource(RGBA_8888);
- tran1.read_lock_fences_enabled = true;
- ResourceId id1 = child_resource_provider_->ImportResource(
- tran1, base::BindOnce(&MockReleaseCallback::Released,
- base::Unretained(&release)));
-
- TransferableResource tran2 = CreateResource(RGBA_8888);
- tran2.read_lock_fences_enabled = false;
- ResourceId id2 = child_resource_provider_->ImportResource(
- tran2, base::BindOnce(&MockReleaseCallback::Released,
- base::Unretained(&release)));
-
- std::vector<ReturnedResource> returned_to_child;
- int child_id = resource_provider_->CreateChild(
- GetReturnCallback(&returned_to_child), SurfaceId());
-
- // Transfer resources to the parent.
- std::vector<TransferableResource> list;
- child_resource_provider_->PrepareSendToParent(
- {id1, id2}, &list,
- static_cast<RasterContextProvider*>(child_context_provider_.get()));
- ASSERT_EQ(2u, list.size());
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
-
- resource_provider_->ReceiveFromChild(child_id, list);
-
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- resource_provider_->GetChildToParentMap(child_id);
-
- scoped_refptr<TestFence> fence(new TestFence);
- resource_provider_->SetReadLockFence(fence.get());
- {
- for (auto& resource : list) {
- ResourceId parent_id = resource_map[resource.id];
- resource_provider_->WaitSyncToken(parent_id);
- DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
- parent_id);
- }
- }
- EXPECT_EQ(0u, returned_to_child.size());
-
- EXPECT_EQ(2u, resource_provider_->num_resources());
-
- resource_provider_->DestroyChild(child_id);
-
- EXPECT_EQ(0u, resource_provider_->num_resources());
- EXPECT_EQ(2u, returned_to_child.size());
-
- // id1 should be lost and id2 should not.
- EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1);
- EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1);
-
- child_resource_provider_->ReceiveReturnsFromParent(
- std::move(returned_to_child));
- EXPECT_CALL(release, Released(_, _)).Times(2);
- child_resource_provider_->RemoveImportedResource(id1);
- child_resource_provider_->RemoveImportedResource(id2);
-}
-
-// Test that ScopedBatchReturnResources batching works.
-TEST_F(DisplayResourceProviderGLTest,
- ScopedBatchReturnResourcesPreventsReturn) {
- MockReleaseCallback release;
-
- std::vector<ReturnedResource> returned_to_child;
- int child_id = resource_provider_->CreateChild(
- GetReturnCallback(&returned_to_child), SurfaceId());
-
- // Transfer some resources to the parent.
- constexpr size_t kTotalResources = 5;
- constexpr size_t kLockedResources = 3;
- constexpr size_t kUsedResources = 4;
- ResourceId ids[kTotalResources];
- for (auto& id : ids) {
- TransferableResource tran = CreateResource(RGBA_8888);
- id = child_resource_provider_->ImportResource(
- tran, base::BindOnce(&MockReleaseCallback::Released,
- base::Unretained(&release)));
- }
- std::vector<ResourceId> resource_ids_to_transfer(ids, ids + kTotalResources);
-
- std::vector<TransferableResource> list;
- child_resource_provider_->PrepareSendToParent(
- resource_ids_to_transfer, &list,
- static_cast<RasterContextProvider*>(child_context_provider_.get()));
- ASSERT_EQ(kTotalResources, list.size());
- for (const auto& id : ids)
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
-
- resource_provider_->ReceiveFromChild(child_id, list);
-
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- resource_provider_->GetChildToParentMap(child_id);
- std::vector<std::unique_ptr<DisplayResourceProviderGL::ScopedReadLockGL>>
- read_locks;
- for (size_t i = 0; i < kLockedResources; i++) {
- ResourceId mapped_resource_id = resource_map[ids[i]];
- resource_provider_->WaitSyncToken(mapped_resource_id);
- read_locks.push_back(
- std::make_unique<DisplayResourceProviderGL::ScopedReadLockGL>(
- resource_provider_.get(), mapped_resource_id));
- }
-
- // Mark all locked resources, and one unlocked resource as used for first
- // batch.
- {
- DisplayResourceProviderGL::ScopedBatchReturnResources returner(
- resource_provider_.get());
- resource_provider_->DeclareUsedResourcesFromChild(
- child_id, ResourceIdSet(ids, ids + kUsedResources));
- EXPECT_EQ(0u, returned_to_child.size());
- }
- EXPECT_EQ(1u, returned_to_child.size());
- child_resource_provider_->ReceiveReturnsFromParent(
- std::move(returned_to_child));
- returned_to_child.clear();
-
- // Return all locked resources.
- {
- DisplayResourceProviderGL::ScopedBatchReturnResources returner(
- resource_provider_.get());
- resource_provider_->DeclareUsedResourcesFromChild(
- child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources));
- // Can be called multiple times while batching is enabled. This happens in
- // practice when the same surface is visited using different paths during
- // surface aggregation.
- resource_provider_->DeclareUsedResourcesFromChild(
- child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources));
- read_locks.clear();
- EXPECT_EQ(0u, returned_to_child.size());
- }
- EXPECT_EQ(kLockedResources, returned_to_child.size());
- // Returned resources that were locked share the same sync token.
- for (const auto& resource : returned_to_child)
- EXPECT_EQ(resource.sync_token, returned_to_child[0].sync_token);
-
- child_resource_provider_->ReceiveReturnsFromParent(
- std::move(returned_to_child));
- returned_to_child.clear();
-
- // Returns from destroying the child is also batched.
- {
- DisplayResourceProviderGL::ScopedBatchReturnResources returner(
- resource_provider_.get());
- resource_provider_->DestroyChild(child_id);
- EXPECT_EQ(0u, returned_to_child.size());
- }
- EXPECT_EQ(1u, returned_to_child.size());
- child_resource_provider_->ReceiveReturnsFromParent(
- std::move(returned_to_child));
- returned_to_child.clear();
-
- EXPECT_CALL(release, Released(_, _)).Times(kTotalResources);
- for (const auto& id : ids)
- child_resource_provider_->RemoveImportedResource(id);
-}
-
-class TextureStateTrackingGLES2Interface : public TestGLES2Interface {
- public:
- MOCK_METHOD2(BindTexture, void(GLenum target, GLuint texture));
- MOCK_METHOD3(TexParameteri, void(GLenum target, GLenum pname, GLint param));
- MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token));
- MOCK_METHOD1(CreateAndConsumeTextureCHROMIUM,
- unsigned(const GLbyte* mailbox));
-
- // Force all textures to be consecutive numbers starting at "1",
- // so we easily can test for them.
- GLuint NextTextureId() override { return next_texture_id_++; }
-
- void RetireTextureId(GLuint) override {}
-};
-
-class ResourceProviderTestImportedResourceGLFilters {
- public:
- static void RunTest(bool mailbox_nearest_neighbor, GLenum sampler_filter) {
- auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
- TextureStateTrackingGLES2Interface* gl = gl_owned.get();
- auto context_provider = TestContextProvider::Create(std::move(gl_owned));
- context_provider->BindToCurrentThread();
-
- auto resource_provider =
- std::make_unique<DisplayResourceProviderGL>(context_provider.get());
-
- auto child_gl_owned =
- std::make_unique<TextureStateTrackingGLES2Interface>();
- TextureStateTrackingGLES2Interface* child_gl = child_gl_owned.get();
- auto child_context_provider =
- TestContextProvider::Create(std::move(child_gl_owned));
- child_context_provider->BindToCurrentThread();
-
- auto child_resource_provider = std::make_unique<ClientResourceProvider>();
-
- unsigned texture_id = 1;
- gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
- gpu::CommandBufferId::FromUnsafeValue(0x12),
- 0x34);
-
- EXPECT_CALL(*child_gl, BindTexture(_, _)).Times(0);
- EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0);
- EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
-
- gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
- GLuint filter = mailbox_nearest_neighbor ? GL_NEAREST : GL_LINEAR;
- constexpr gfx::Size size(64, 64);
- auto resource = TransferableResource::MakeGL(
- gpu_mailbox, filter, GL_TEXTURE_2D, sync_token, size,
- false /* is_overlay_candidate */);
-
- MockReleaseCallback release;
- ResourceId resource_id = child_resource_provider->ImportResource(
- resource, base::BindOnce(&MockReleaseCallback::Released,
- base::Unretained(&release)));
- EXPECT_NE(kInvalidResourceId, resource_id);
-
- testing::Mock::VerifyAndClearExpectations(child_gl);
-
- // Transfer resources to the parent.
- std::vector<TransferableResource> send_to_parent;
- std::vector<ReturnedResource> returned_to_child;
- int child_id = resource_provider->CreateChild(
- base::BindRepeating(&CollectResources, &returned_to_child),
- SurfaceId());
- child_resource_provider->PrepareSendToParent(
- {resource_id}, &send_to_parent,
- static_cast<RasterContextProvider*>(child_context_provider.get()));
- resource_provider->ReceiveFromChild(child_id, send_to_parent);
-
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- resource_provider->GetChildToParentMap(child_id);
- ResourceId mapped_resource_id = resource_map[resource_id];
- {
- // The verified flush flag will be set by
- // ClientResourceProvider::PrepareSendToParent. Before checking if
- // the gpu::SyncToken matches, set this flag first.
- sync_token.SetVerifyFlush();
-
- // Mailbox sync point WaitSyncToken before using the texture.
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)));
- resource_provider->WaitSyncToken(mapped_resource_id);
- testing::Mock::VerifyAndClearExpectations(gl);
-
- EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_))
- .WillOnce(Return(texture_id));
- EXPECT_CALL(*gl, BindTexture(GL_TEXTURE_2D, texture_id));
-
- // The sampler will reset these if |mailbox_nearest_neighbor| does not
- // match |sampler_filter|.
- if (mailbox_nearest_neighbor != (sampler_filter == GL_NEAREST)) {
- EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- sampler_filter));
- EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
- sampler_filter));
- }
-
- DisplayResourceProviderGL::ScopedSamplerGL lock(
- resource_provider.get(), mapped_resource_id, sampler_filter);
- testing::Mock::VerifyAndClearExpectations(gl);
-
- // When done with it, a sync point should be inserted, but no produce is
- // necessary.
- EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0);
- EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
- }
-
- EXPECT_EQ(0u, returned_to_child.size());
- // Transfer resources back from the parent to the child. Set no resources as
- // being in use.
- resource_provider->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
- EXPECT_EQ(1u, returned_to_child.size());
- child_resource_provider->ReceiveReturnsFromParent(
- std::move(returned_to_child));
-
- gpu::SyncToken released_sync_token;
- {
- EXPECT_CALL(release, Released(_, false))
- .WillOnce(SaveArg<0>(&released_sync_token));
- child_resource_provider->RemoveImportedResource(resource_id);
- }
- EXPECT_TRUE(released_sync_token.HasData());
- }
-};
-
-TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_LinearToLinear) {
- ResourceProviderTestImportedResourceGLFilters::RunTest(false, GL_LINEAR);
-}
-
-TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_NearestToNearest) {
- ResourceProviderTestImportedResourceGLFilters::RunTest(true, GL_NEAREST);
-}
-
-TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_NearestToLinear) {
- ResourceProviderTestImportedResourceGLFilters::RunTest(true, GL_LINEAR);
-}
-
-TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_LinearToNearest) {
- ResourceProviderTestImportedResourceGLFilters::RunTest(false, GL_NEAREST);
-}
-
-TEST_F(DisplayResourceProviderGLTest, ReceiveGLTextureExternalOES) {
- auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
- TextureStateTrackingGLES2Interface* gl = gl_owned.get();
- auto context_provider = TestContextProvider::Create(std::move(gl_owned));
- context_provider->BindToCurrentThread();
-
- auto resource_provider =
- std::make_unique<DisplayResourceProviderGL>(context_provider.get());
-
- auto child_gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
- TextureStateTrackingGLES2Interface* child_gl = child_gl_owned.get();
- auto child_context_provider =
- TestContextProvider::Create(std::move(child_gl_owned));
- child_context_provider->BindToCurrentThread();
-
- auto child_resource_provider = std::make_unique<ClientResourceProvider>();
-
- gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
- gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
-
- EXPECT_CALL(*child_gl, BindTexture(_, _)).Times(0);
- EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0);
- EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
-
- gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
- ReleaseCallback callback = base::DoNothing();
-
- constexpr gfx::Size size(64, 64);
- auto resource = TransferableResource::MakeGL(
- gpu_mailbox, GL_LINEAR, GL_TEXTURE_EXTERNAL_OES, sync_token, size,
- false /* is_overlay_candidate */);
-
- ResourceId resource_id =
- child_resource_provider->ImportResource(resource, std::move(callback));
- EXPECT_NE(kInvalidResourceId, resource_id);
-
- testing::Mock::VerifyAndClearExpectations(child_gl);
-
- // Transfer resources to the parent.
- std::vector<TransferableResource> send_to_parent;
- std::vector<ReturnedResource> returned_to_child;
- int child_id = resource_provider->CreateChild(
- base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId());
- child_resource_provider->PrepareSendToParent(
- {resource_id}, &send_to_parent,
- static_cast<RasterContextProvider*>(child_context_provider_.get()));
- resource_provider->ReceiveFromChild(child_id, send_to_parent);
-
- // Before create DrawQuad in DisplayResourceProvider's namespace, get the
- // mapped resource id first.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- resource_provider->GetChildToParentMap(child_id);
- ResourceId mapped_resource_id = resource_map[resource_id];
- {
- // The verified flush flag will be set by
- // ClientResourceProvider::PrepareSendToParent. Before checking if
- // the gpu::SyncToken matches, set this flag first.
- sync_token.SetVerifyFlush();
-
- // Mailbox sync point WaitSyncToken before using the texture.
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)));
- resource_provider->WaitSyncToken(mapped_resource_id);
- testing::Mock::VerifyAndClearExpectations(gl);
-
- unsigned texture_id = 1;
-
- EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_))
- .WillOnce(Return(texture_id));
-
- DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider.get(),
- mapped_resource_id);
- testing::Mock::VerifyAndClearExpectations(gl);
-
- // When done with it, a sync point should be inserted, but no produce is
- // necessary.
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
- EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
- testing::Mock::VerifyAndClearExpectations(gl);
- }
- EXPECT_EQ(0u, returned_to_child.size());
- // Transfer resources back from the parent to the child. Set no resources as
- // being in use.
- resource_provider->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
- EXPECT_EQ(1u, returned_to_child.size());
- child_resource_provider->ReceiveReturnsFromParent(
- std::move(returned_to_child));
-
- child_resource_provider->RemoveImportedResource(resource_id);
-}
-
-TEST_F(DisplayResourceProviderGLTest, WaitSyncTokenIfNeeded) {
- auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
- TextureStateTrackingGLES2Interface* gl = gl_owned.get();
- auto context_provider = TestContextProvider::Create(std::move(gl_owned));
- context_provider->BindToCurrentThread();
-
- auto resource_provider =
- std::make_unique<DisplayResourceProviderGL>(context_provider.get());
-
- EXPECT_CALL(*gl, BindTexture(_, _)).Times(0);
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
- EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
-
- gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
- gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
- ResourceId id_with_sync = MakeGpuResourceAndSendToDisplay(
- GL_LINEAR, GL_TEXTURE_2D, sync_token, resource_provider.get());
- ResourceId id_without_sync = MakeGpuResourceAndSendToDisplay(
- GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), resource_provider.get());
-
- // First call to WaitSyncToken should call WaitSyncToken, but only if a
- // SyncToken was present.
- {
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)))
- .Times(1);
- resource_provider->WaitSyncToken(id_with_sync);
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
- resource_provider->WaitSyncToken(id_without_sync);
- }
-
- {
- // Subsequent calls to WaitSyncToken shouldn't call WaitSyncToken.
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
- resource_provider->WaitSyncToken(id_with_sync);
- resource_provider->WaitSyncToken(id_without_sync);
- }
-}
-
-} // namespace
-} // namespace viz
diff --git a/chromium/components/viz/service/display/display_resource_provider_skia.cc b/chromium/components/viz/service/display/display_resource_provider_skia.cc
index 3d4b0e51011..6e4ea305a8c 100644
--- a/chromium/components/viz/service/display/display_resource_provider_skia.cc
+++ b/chromium/components/viz/service/display/display_resource_provider_skia.cc
@@ -84,6 +84,13 @@ DisplayResourceProviderSkia::DeleteAndReturnUnusedResourcesToChildImpl(
continue;
}
+ if (resource.transferable.synchronization_type ==
+ TransferableResource::SynchronizationType::kReleaseFence) {
+ // The resource might have never been used.
+ if (resource.resource_fence)
+ resource.release_fence = resource.resource_fence->GetGpuFenceHandle();
+ }
+
const bool is_lost = can_delete == CanDeleteNowResult::kYesButLoseResource;
to_return.emplace_back(child_id, resource.sync_token(),
@@ -130,7 +137,7 @@ DisplayResourceProviderSkia::LockSetForExternalUse::LockResource(
ResourceId id,
bool maybe_concurrent_reads,
bool is_video_plane,
- const absl::optional<gfx::ColorSpace>& override_color_space,
+ sk_sp<SkColorSpace> override_color_space,
bool raw_draw_is_possible) {
auto it = resource_provider_->resources_.find(id);
DCHECK(it != resource_provider_->resources_.end());
@@ -154,8 +161,9 @@ DisplayResourceProviderSkia::LockSetForExternalUse::LockResource(
// is very subtle.
image_color_space =
override_color_space
- .value_or(resource.transferable.color_space.GetAsFullRangeRGB())
- .ToSkColorSpace();
+ ? override_color_space
+ : resource.transferable.color_space.GetAsFullRangeRGB()
+ .ToSkColorSpace();
}
resource.image_context =
resource_provider_->external_use_client_->CreateImageContext(
@@ -166,11 +174,20 @@ DisplayResourceProviderSkia::LockSetForExternalUse::LockResource(
}
resource.locked_for_external_use = true;
- if (resource.transferable.read_lock_fences_enabled) {
- if (resource_provider_->current_read_lock_fence_.get())
- resource_provider_->current_read_lock_fence_->Set();
- resource.read_lock_fence = resource_provider_->current_read_lock_fence_;
+ switch (resource.transferable.synchronization_type) {
+ case TransferableResource::SynchronizationType::kGpuCommandsCompleted:
+ resource.resource_fence =
+ resource_provider_->current_gpu_commands_completed_fence_;
+ break;
+ case TransferableResource::SynchronizationType::kReleaseFence:
+ resource.resource_fence = resource_provider_->current_release_fence_;
+ break;
+ default:
+ break;
}
+
+ if (resource.resource_fence)
+ resource.resource_fence->Set();
}
DCHECK(base::Contains(resources_, std::make_pair(id, &resource)));
diff --git a/chromium/components/viz/service/display/display_resource_provider_skia.h b/chromium/components/viz/service/display/display_resource_provider_skia.h
index 32982471449..6fc3b4385cf 100644
--- a/chromium/components/viz/service/display/display_resource_provider_skia.h
+++ b/chromium/components/viz/service/display/display_resource_provider_skia.h
@@ -51,15 +51,14 @@ class VIZ_SERVICE_EXPORT DisplayResourceProviderSkia
// Lock a resource for external use. The return value was created by
// |client| at some point in the past. The SkImage color space will be set
- // to |color_space| if valid, otherwise it will be set to the resource's
- // color space. If |is_video_plane| is true, the image color space will be
- // set to nullptr (to avoid LOG spam).
+ // to |override_color_space| if non-nullptr, otherwise it will be set to the
+ // resource's color space. If |is_video_plane| is true, the image color
+ // space will be set to nullptr (to avoid LOG spam).
ExternalUseClient::ImageContext* LockResource(
ResourceId resource_id,
bool maybe_concurrent_reads,
bool is_video_plane,
- const absl::optional<gfx::ColorSpace>& override_color_space =
- absl::nullopt,
+ sk_sp<SkColorSpace> override_color_space = nullptr,
bool raw_draw_if_possible = false);
// Unlock all locked resources with a |sync_token|. The |sync_token| should
diff --git a/chromium/components/viz/service/display/display_resource_provider_skia_unittest.cc b/chromium/components/viz/service/display/display_resource_provider_skia_unittest.cc
index 35ac8f1b95b..878db989dd3 100644
--- a/chromium/components/viz/service/display/display_resource_provider_skia_unittest.cc
+++ b/chromium/components/viz/service/display/display_resource_provider_skia_unittest.cc
@@ -17,10 +17,12 @@
#include "base/callback_helpers.h"
#include "base/check.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
#include "build/build_config.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/resources/release_callback.h"
#include "components/viz/common/resources/returned_resource.h"
+#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/test/test_context_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -29,6 +31,7 @@
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/gpu_fence_handle.h"
using testing::_;
using testing::ByMove;
@@ -296,134 +299,218 @@ TEST_F(DisplayResourceProviderSkiaTest, LockForExternalUseWebView) {
child_resource_provider_->RemoveImportedResource(id1);
}
-class TestFence : public ResourceFence {
+class TestGpuCommandsCompletedFence : public ResourceFence {
public:
- TestFence() = default;
+ TestGpuCommandsCompletedFence() = default;
// ResourceFence implementation.
void Set() override {}
bool HasPassed() override { return passed; }
+ gfx::GpuFenceHandle GetGpuFenceHandle() override {
+ NOTREACHED();
+ return gfx::GpuFenceHandle();
+ }
bool passed = false;
private:
- ~TestFence() override = default;
+ ~TestGpuCommandsCompletedFence() override = default;
};
-TEST_F(DisplayResourceProviderSkiaTest,
- ReadLockFenceStopsReturnToChildOrDelete) {
- MockReleaseCallback release;
- TransferableResource tran1 = CreateResource(RGBA_8888);
- tran1.read_lock_fences_enabled = true;
- ResourceId id1 = child_resource_provider_->ImportResource(
- tran1, base::BindOnce(&MockReleaseCallback::Released,
- base::Unretained(&release)));
-
- std::vector<ReturnedResource> returned_to_child;
- int child_id = resource_provider_->CreateChild(
- GetReturnCallback(&returned_to_child), SurfaceId());
-
- // Transfer some resources to the parent.
- std::vector<TransferableResource> list;
- child_resource_provider_->PrepareSendToParent(
- {id1}, &list,
- static_cast<RasterContextProvider*>(child_context_provider_.get()));
- ASSERT_EQ(1u, list.size());
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
- EXPECT_TRUE(list[0].read_lock_fences_enabled);
-
- resource_provider_->ReceiveFromChild(child_id, list);
-
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- resource_provider_->GetChildToParentMap(child_id);
+class TestReleaseFence : public ResourceFence {
+ public:
+ TestReleaseFence() = default;
- scoped_refptr<TestFence> fence(new TestFence);
- resource_provider_->SetReadLockFence(fence.get());
- {
- ResourceId parent_id = resource_map[list.front().id];
- lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
- /*is_video_plane=*/false);
- lock_set_->UnlockResources(GenSyncToken());
+ // ResourceFence implementation.
+ void Set() override {}
+ bool HasPassed() override { return release_fence_.has_value(); }
+ gfx::GpuFenceHandle GetGpuFenceHandle() override {
+ return HasPassed() ? release_fence_->Clone() : gfx::GpuFenceHandle();
}
- resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
- EXPECT_EQ(0u, returned_to_child.size());
- resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
- EXPECT_EQ(0u, returned_to_child.size());
- fence->passed = true;
+ void SetReleaseFence(gfx::GpuFenceHandle release_fence) {
+ release_fence_ = std::move(release_fence);
+ }
- resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
- EXPECT_EQ(1u, returned_to_child.size());
+ private:
+ ~TestReleaseFence() override = default;
- child_resource_provider_->ReceiveReturnsFromParent(
- std::move(returned_to_child));
- EXPECT_CALL(release, Released(_, _));
- child_resource_provider_->RemoveImportedResource(id1);
-}
+ absl::optional<gfx::GpuFenceHandle> release_fence_;
+};
-TEST_F(DisplayResourceProviderSkiaTest, ReadLockFenceDestroyChild) {
- MockReleaseCallback release;
+TEST_F(DisplayResourceProviderSkiaTest,
+ ResourceFenceStopsReturnToChildOrDelete) {
+ const std::vector<TransferableResource::SynchronizationType>
+ kSynchronizationTypes = {
+ TransferableResource::SynchronizationType::kGpuCommandsCompleted,
+ TransferableResource::SynchronizationType::kReleaseFence};
+ for (auto sync_type : kSynchronizationTypes) {
+ MockReleaseCallback release;
+ TransferableResource tran1 = CreateResource(RGBA_8888);
+ tran1.synchronization_type = sync_type;
+ ResourceId id1 = child_resource_provider_->ImportResource(
+ tran1, base::BindOnce(&MockReleaseCallback::Released,
+ base::Unretained(&release)));
+
+ std::vector<ReturnedResource> returned_to_child;
+ int child_id = resource_provider_->CreateChild(
+ GetReturnCallback(&returned_to_child), SurfaceId());
+
+ // Transfer some resources to the parent.
+ std::vector<TransferableResource> list;
+ child_resource_provider_->PrepareSendToParent(
+ {id1}, &list,
+ static_cast<RasterContextProvider*>(child_context_provider_.get()));
+ ASSERT_EQ(1u, list.size());
+ EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
+ EXPECT_EQ(list[0].synchronization_type, sync_type);
+
+ resource_provider_->ReceiveFromChild(child_id, list);
+
+ // In DisplayResourceProvider's namespace, use the mapped resource id.
+ std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
+ resource_provider_->GetChildToParentMap(child_id);
+
+ scoped_refptr<ResourceFence> fence;
+ TestGpuCommandsCompletedFence* gpu_commands_completed_fence = nullptr;
+ TestReleaseFence* release_fence = nullptr;
+ if (sync_type ==
+ TransferableResource::SynchronizationType::kGpuCommandsCompleted) {
+ fence = base::MakeRefCounted<TestGpuCommandsCompletedFence>();
+ gpu_commands_completed_fence =
+ static_cast<TestGpuCommandsCompletedFence*>(fence.get());
+ resource_provider_->SetGpuCommandsCompletedFence(fence.get());
+ } else {
+ ASSERT_EQ(TransferableResource::SynchronizationType::kReleaseFence,
+ sync_type);
+ fence = base::MakeRefCounted<TestReleaseFence>();
+ release_fence = static_cast<TestReleaseFence*>(fence.get());
+ resource_provider_->SetReleaseFence(fence.get());
+ }
- TransferableResource tran1 = CreateResource(RGBA_8888);
- tran1.read_lock_fences_enabled = true;
- ResourceId id1 = child_resource_provider_->ImportResource(
- tran1, base::BindOnce(&MockReleaseCallback::Released,
- base::Unretained(&release)));
+ {
+ ResourceId parent_id = resource_map[list.front().id];
+ lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
+ /*is_video_plane=*/false);
+ lock_set_->UnlockResources(GenSyncToken());
+ }
- TransferableResource tran2 = CreateResource(RGBA_8888);
- tran2.read_lock_fences_enabled = false;
- ResourceId id2 = child_resource_provider_->ImportResource(
- tran2, base::BindOnce(&MockReleaseCallback::Released,
- base::Unretained(&release)));
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ ResourceIdSet());
+ EXPECT_EQ(0u, returned_to_child.size());
- std::vector<ReturnedResource> returned_to_child;
- int child_id = resource_provider_->CreateChild(
- GetReturnCallback(&returned_to_child), SurfaceId());
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ ResourceIdSet());
+ EXPECT_EQ(0u, returned_to_child.size());
- // Transfer resources to the parent.
- std::vector<TransferableResource> list;
- child_resource_provider_->PrepareSendToParent(
- {id1, id2}, &list,
- static_cast<RasterContextProvider*>(child_context_provider_.get()));
- ASSERT_EQ(2u, list.size());
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
- EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
+ if (gpu_commands_completed_fence) {
+ gpu_commands_completed_fence->passed = true;
+ } else {
+ gfx::GpuFenceHandle fake_handle;
+#if BUILDFLAG(IS_POSIX)
+ const int32_t kFenceFd = dup(1);
+ fake_handle.owned_fd.reset(kFenceFd);
+#endif
+ release_fence->SetReleaseFence(std::move(fake_handle));
+ }
- resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ ResourceIdSet());
+ EXPECT_EQ(1u, returned_to_child.size());
+
+#if BUILDFLAG(IS_POSIX)
+ if (release_fence)
+ EXPECT_FALSE(returned_to_child.begin()->release_fence.is_null());
+ else
+ EXPECT_TRUE(returned_to_child.begin()->release_fence.is_null());
+#endif
+
+ child_resource_provider_->ReceiveReturnsFromParent(
+ std::move(returned_to_child));
+ EXPECT_CALL(release, Released(_, _));
+ child_resource_provider_->RemoveImportedResource(id1);
+ }
+}
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- resource_provider_->GetChildToParentMap(child_id);
+TEST_F(DisplayResourceProviderSkiaTest, ResourceFenceDestroyChild) {
+ const std::vector<TransferableResource::SynchronizationType>
+ kSynchronizationTypes = {
+ TransferableResource::SynchronizationType::kGpuCommandsCompleted,
+ TransferableResource::SynchronizationType::kReleaseFence};
+ for (auto sync_type : kSynchronizationTypes) {
+ MockReleaseCallback release;
+
+ TransferableResource tran1 = CreateResource(RGBA_8888);
+ tran1.synchronization_type = sync_type;
+ ResourceId id1 = child_resource_provider_->ImportResource(
+ tran1, base::BindOnce(&MockReleaseCallback::Released,
+ base::Unretained(&release)));
+
+ TransferableResource tran2 = CreateResource(RGBA_8888);
+ ASSERT_EQ(tran2.synchronization_type,
+ TransferableResource::SynchronizationType::kSyncToken);
+ ResourceId id2 = child_resource_provider_->ImportResource(
+ tran2, base::BindOnce(&MockReleaseCallback::Released,
+ base::Unretained(&release)));
+
+ std::vector<ReturnedResource> returned_to_child;
+ int child_id = resource_provider_->CreateChild(
+ GetReturnCallback(&returned_to_child), SurfaceId());
+
+ // Transfer resources to the parent.
+ std::vector<TransferableResource> list;
+ child_resource_provider_->PrepareSendToParent(
+ {id1, id2}, &list,
+ static_cast<RasterContextProvider*>(child_context_provider_.get()));
+ ASSERT_EQ(2u, list.size());
+ EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
+ EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
+
+ resource_provider_->ReceiveFromChild(child_id, list);
+
+ // In DisplayResourceProvider's namespace, use the mapped resource id.
+ std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
+ resource_provider_->GetChildToParentMap(child_id);
+
+ scoped_refptr<ResourceFence> fence;
+ if (sync_type ==
+ TransferableResource::SynchronizationType::kGpuCommandsCompleted) {
+ fence = base::MakeRefCounted<TestGpuCommandsCompletedFence>();
+ resource_provider_->SetGpuCommandsCompletedFence(fence.get());
+ } else {
+ ASSERT_EQ(TransferableResource::SynchronizationType::kReleaseFence,
+ sync_type);
+ fence = base::MakeRefCounted<TestReleaseFence>();
+ resource_provider_->SetReleaseFence(fence.get());
+ }
- scoped_refptr<TestFence> fence(new TestFence);
- resource_provider_->SetReadLockFence(fence.get());
- {
- for (auto& resource : list) {
- ResourceId parent_id = resource_map[resource.id];
- lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
- /*is_video_plane=*/false);
+ {
+ for (auto& resource : list) {
+ ResourceId parent_id = resource_map[resource.id];
+ lock_set_->LockResource(parent_id, /*maybe_concurrent_reads=*/true,
+ /*is_video_plane=*/false);
+ }
+ lock_set_->UnlockResources(GenSyncToken());
}
- lock_set_->UnlockResources(GenSyncToken());
- }
- EXPECT_EQ(0u, returned_to_child.size());
+ EXPECT_EQ(0u, returned_to_child.size());
- EXPECT_EQ(2u, resource_provider_->num_resources());
+ EXPECT_EQ(2u, resource_provider_->num_resources());
- resource_provider_->DestroyChild(child_id);
+ resource_provider_->DestroyChild(child_id);
- EXPECT_EQ(0u, resource_provider_->num_resources());
- EXPECT_EQ(2u, returned_to_child.size());
+ EXPECT_EQ(0u, resource_provider_->num_resources());
+ EXPECT_EQ(2u, returned_to_child.size());
- // id1 should be lost and id2 should not.
- EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1);
- EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1);
+ // id1 should be lost and id2 should not.
+ EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1);
+ EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1);
- child_resource_provider_->ReceiveReturnsFromParent(
- std::move(returned_to_child));
- EXPECT_CALL(release, Released(_, _)).Times(2);
- child_resource_provider_->RemoveImportedResource(id1);
- child_resource_provider_->RemoveImportedResource(id2);
+ child_resource_provider_->ReceiveReturnsFromParent(
+ std::move(returned_to_child));
+ EXPECT_CALL(release, Released(_, _)).Times(2);
+ child_resource_provider_->RemoveImportedResource(id1);
+ child_resource_provider_->RemoveImportedResource(id2);
+ }
}
// Test that ScopedBatchReturnResources batching works.
diff --git a/chromium/components/viz/service/display/display_scheduler.cc b/chromium/components/viz/service/display/display_scheduler.cc
index 1139afa955e..b1757695422 100644
--- a/chromium/components/viz/service/display/display_scheduler.cc
+++ b/chromium/components/viz/service/display/display_scheduler.cc
@@ -17,6 +17,23 @@
namespace viz {
+namespace {
+
+base::TimeDelta ComputeAdpfTarget(const BeginFrameArgs& args) {
+ int target_ms = features::kAdpfTargetDurationMs.Get();
+ if (target_ms > 0 && target_ms <= 1000) {
+ return base::Milliseconds(target_ms);
+ }
+ if (args.possible_deadlines) {
+ const auto& deadline = args.possible_deadlines->GetPreferredDeadline();
+ // Arbitrarily use 75% of the deadline for CPU work.
+ return deadline.latch_delta * 3 / 4;
+ }
+ return base::Milliseconds(12);
+}
+
+} // namespace
+
class DisplayScheduler::BeginFrameObserver : public BeginFrameObserverBase {
public:
explicit BeginFrameObserver(DisplayScheduler* scheduler)
@@ -160,12 +177,9 @@ void DisplayScheduler::MaybeCreateHintSession(
if ((!create_session_for_current_thread_ids_failed_ && !hint_session_) ||
current_thread_ids_ != thread_ids) {
hint_session_.reset();
- int target_ms = features::kAdpfTargetDurationMs.Get();
- if (target_ms <= 0 || target_ms > 1000)
- target_ms = 12;
current_thread_ids_ = std::move(thread_ids);
hint_session_ = hint_session_factory_->CreateSession(
- current_thread_ids_, base::Milliseconds(target_ms));
+ current_thread_ids_, ComputeAdpfTarget(current_begin_frame_args_));
create_session_for_current_thread_ids_failed_ = !hint_session_;
}
}
@@ -241,6 +255,9 @@ bool DisplayScheduler::OnBeginFrame(const BeginFrameArgs& args) {
// Schedule the deadline.
current_begin_frame_args_ = save_args;
+ if (hint_session_) {
+ hint_session_->UpdateTargetDuration(ComputeAdpfTarget(save_args));
+ }
base::TimeDelta delta;
if (client_ && dynamic_scheduler_deadlines_percentile_.has_value() &&
diff --git a/chromium/components/viz/service/display/display_scheduler_unittest.cc b/chromium/components/viz/service/display/display_scheduler_unittest.cc
index 041b801f58e..c26548bfae7 100644
--- a/chromium/components/viz/service/display/display_scheduler_unittest.cc
+++ b/chromium/components/viz/service/display/display_scheduler_unittest.cc
@@ -181,7 +181,9 @@ class DisplaySchedulerTest : public testing::Test {
: wait_for_all_surfaces_before_draw_(wait_for_all_surfaces_before_draw),
fake_begin_frame_source_(0.f, false),
task_runner_(new base::NullTaskRunner),
- surface_manager_(nullptr, 4u),
+ surface_manager_(nullptr,
+ /*activation_deadline_in_frames=*/4u,
+ /*max_uncommitted_frames=*/0),
resource_provider_(&shared_bitmap_manager_),
aggregator_(&surface_manager_, &resource_provider_, false, false),
damage_tracker_(
diff --git a/chromium/components/viz/service/display/display_unittest.cc b/chromium/components/viz/service/display/display_unittest.cc
index df18d96d450..7175b9ef94d 100644
--- a/chromium/components/viz/service/display/display_unittest.cc
+++ b/chromium/components/viz/service/display/display_unittest.cc
@@ -68,7 +68,6 @@
#include "ui/gfx/delegated_ink_point.h"
#include "ui/gfx/mojom/delegated_ink_point_renderer.mojom.h"
#include "ui/gfx/overlay_transform.h"
-#include "ui/gfx/presentation_feedback.h"
using testing::_;
using testing::AnyNumber;
@@ -169,10 +168,11 @@ class DisplayTest : public testing::Test {
~DisplayTest() override {}
void SetUpSoftwareDisplay(const RendererSettings& settings) {
- std::unique_ptr<FakeOutputSurface> output_surface;
+ std::unique_ptr<FakeSoftwareOutputSurface> output_surface;
auto device = std::make_unique<TestSoftwareOutputDevice>();
software_output_device_ = device.get();
- output_surface = FakeOutputSurface::CreateSoftware(std::move(device));
+ output_surface =
+ std::make_unique<FakeSoftwareOutputSurface>(std::move(device));
output_surface_ = output_surface.get();
CreateDisplaySchedulerAndDisplay(settings, kArbitraryFrameSinkId,
@@ -272,7 +272,7 @@ class DisplayTest : public testing::Test {
std::unique_ptr<BeginFrameSource> begin_frame_source_;
std::unique_ptr<Display> display_;
raw_ptr<TestSoftwareOutputDevice> software_output_device_ = nullptr;
- raw_ptr<FakeOutputSurface> output_surface_ = nullptr;
+ raw_ptr<FakeSoftwareOutputSurface> output_surface_ = nullptr;
raw_ptr<FakeSkiaOutputSurface> skia_output_surface_ = nullptr;
raw_ptr<TestDisplayScheduler> scheduler_ = nullptr;
};
@@ -283,7 +283,7 @@ TEST_F(DisplayTest, DisplayDamaged) {
settings.partial_swap_enabled = true;
SetUpSoftwareDisplay(settings);
gfx::ColorSpace color_space_1 = gfx::ColorSpace::CreateXYZD50();
- gfx::ColorSpace color_space_2 = gfx::ColorSpace::CreateSCRGBLinear();
+ gfx::ColorSpace color_space_2 = gfx::ColorSpace::CreateSRGBLinear();
gfx::DisplayColorSpaces color_spaces_1(color_space_1);
gfx::DisplayColorSpaces color_spaces_2(color_space_2);
@@ -862,7 +862,7 @@ TEST_F(DisplayTest, CompositorFrameDamagesCorrectDisplay) {
TestDisplayScheduler* scheduler2 = scheduler_for_display2.get();
auto display2 = CreateDisplay(
settings, kAnotherFrameSinkId, std::move(scheduler_for_display2),
- FakeOutputSurface::CreateSoftware(
+ std::make_unique<FakeSoftwareOutputSurface>(
std::make_unique<TestSoftwareOutputDevice>()));
manager_.RegisterBeginFrameSource(begin_frame_source2.get(),
kAnotherFrameSinkId);
@@ -1055,9 +1055,9 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) {
// DrawQuad, so the size of quad_list remains unchanged after calling
// RemoveOverdrawQuads.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
SharedQuadState* shared_quad_state2 =
frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
@@ -1089,12 +1089,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) {
// which cannot be represented by a smaller rect (its visible_rect stays
// the same).
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
// +------+ +------+
@@ -1123,13 +1123,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) {
// screen is rect3 - rect1 U rect3 = (25, 100, 50x25), which updates its
// visible_rect accordingly.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(gfx::Rect(25, 100, 50, 25).ToString(),
- frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ gfx::Rect(25, 100, 50, 25),
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
// +--+ +--+
@@ -1159,12 +1158,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) {
// cannot be represented by a smaller rect (its visible_rect stays the
// same).
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect7.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect6.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect7,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect6,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
// +----+ +--+
@@ -1190,12 +1189,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) {
// after calling RemoveOverdrawQuads. The visible region of |quad2| on
// screen is rect4 (150, 0, 50x50), its visible_rect stays the same.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect4.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect4,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
// +-----++
// | ||
@@ -1223,12 +1222,12 @@ TEST_F(DisplayTest, DrawOcclusionWithNonCoveringDrawQuad) {
// which cannot be represented by a smaller rect (its visible_rect stays the
// same).
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect5.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect5,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
}
@@ -1278,12 +1277,12 @@ TEST_F(DisplayTest, DrawOcclusionWithSingleOverlapBehindDisjointedDrawQuads) {
// third quad is removed from the |quad_list|, leaving the first and second
// (defined by rects[1](150, 0, 150x150); the largest) quads intact.
ASSERT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rects[0].ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rects[1].ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rects[0],
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rects[1],
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
}
@@ -1334,12 +1333,12 @@ TEST_F(DisplayTest, DrawOcclusionWithMultipleOverlapBehindDisjointedDrawQuads) {
// 0, 150x150)) quads, respectively, so both are removed from the
// |quad_list|, leaving the first and and second quads intact.
ASSERT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rects[0].ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rects[1].ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rects[0],
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rects[1],
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
}
@@ -1385,9 +1384,9 @@ TEST_F(DisplayTest, CompositorFrameWithOverlapDrawQuad) {
display_->RemoveOverdrawQuads(&frame);
// |quad2| overlaps |quad1|, so |quad2| is removed from the |quad_list|.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
// +-----+
// | +-+ |
@@ -1410,9 +1409,9 @@ TEST_F(DisplayTest, CompositorFrameWithOverlapDrawQuad) {
// |quad2| is hiding behind |quad1|, so |quad2| is removed from the
// |quad_list|.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
// +-----+
@@ -1436,9 +1435,9 @@ TEST_F(DisplayTest, CompositorFrameWithOverlapDrawQuad) {
// |quad2| is behind |quad1| and aligns with the edge of |quad1|, so |quad2|
// is removed from the |quad_list|.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
// +-----++
@@ -1464,9 +1463,9 @@ TEST_F(DisplayTest, CompositorFrameWithOverlapDrawQuad) {
// |quad2| is covered by |quad 1|, so |quad2| is removed from the
// |quad_list|.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -1527,9 +1526,9 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) {
// |quad2| is now covered by |quad|. So the size of |quad_list| is reduced
// by 1.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
{
@@ -1550,9 +1549,9 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) {
// |quad2| is now covered by |quad|. So the size of |quad_list| is reduced
// by 1.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
{
@@ -1575,9 +1574,9 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) {
// |quad2| is now covered by |quad1|. So the size of |quad_list| is reduced
// by 1.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
{
@@ -1591,9 +1590,9 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) {
// The compositor frame contains only one quad, so |quad_list| remains 1
// after calling RemoveOverdrawQuads.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
{
@@ -1618,12 +1617,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) {
// Since visible region of |rect5| is not a rect, quad2::visible_rect stays
// the same.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect5.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(4)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect5,
+ frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect);
}
{
@@ -1646,13 +1645,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) {
// Since visible region of |rect5| is (12, 50, 25x12), quad2::visible_rect
// updates accordingly.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(gfx::Rect(12, 50, 25, 12).ToString(),
- frame.render_pass_list.front()
- ->quad_list.ElementAt(4)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ gfx::Rect(12, 50, 25, 12),
+ frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect);
}
{
@@ -1675,12 +1673,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) {
// Since visible region of |rect7| is not a rect, quad2::visible_rect stays
// the same.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect7.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(4)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect7,
+ frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect);
}
{
@@ -1703,12 +1701,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) {
// Since visible region of |rect8| is not a rect, quad2::visible_rect stays
// the same.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect8.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(4)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect8,
+ frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect);
}
{
@@ -1731,12 +1729,12 @@ TEST_F(DisplayTest, CompositorFrameWithTransformer) {
// Since visible region of |rect9| is not a rect, quad2::visible_rect stays
// the same
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect10.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect9.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(4)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect10,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect9,
+ frame.render_pass_list.front()->quad_list.ElementAt(4)->visible_rect);
}
}
@@ -1786,12 +1784,12 @@ TEST_F(DisplayTest, CompositorFrameWithEpsilonScaleTransform) {
// draw occlusion algorithm.
EXPECT_FALSE(zero_scale.GetInverse(&inverted));
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -1816,12 +1814,12 @@ TEST_F(DisplayTest, CompositorFrameWithEpsilonScaleTransform) {
epsilon_scale, rect)
.IsEmpty());
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -1843,9 +1841,9 @@ TEST_F(DisplayTest, CompositorFrameWithEpsilonScaleTransform) {
// occlusion rect, so |quad2| is removed.
EXPECT_TRUE(larger_epsilon_scale.GetInverse(&inverted));
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -1894,12 +1892,12 @@ TEST_F(DisplayTest, CompositorFrameWithNegativeScaleTransform) {
// |
// |
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -1926,12 +1924,12 @@ TEST_F(DisplayTest, CompositorFrameWithNegativeScaleTransform) {
// | |
// |----+
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -1958,9 +1956,9 @@ TEST_F(DisplayTest, CompositorFrameWithNegativeScaleTransform) {
// |
// |
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -2012,12 +2010,12 @@ TEST_F(DisplayTest, CompositorFrameWithRotation) {
// transform) and |quad2| becomes (75, 75 10x10). So |quad2| does not
// intersect with |quad|. No changes in quads.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -2036,9 +2034,9 @@ TEST_F(DisplayTest, CompositorFrameWithRotation) {
// (53, 75 8x10) (after applying rotation transform). So |quad2| is behind
// |quad|. |quad2| is removed from |quad_list|.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
{
@@ -2058,12 +2056,12 @@ TEST_F(DisplayTest, CompositorFrameWithRotation) {
// transform) and |quad2| becomes (50, 50, 25x100). So |quad2| does not
// intersect with |quad|. No changes in quads.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect3,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
}
{
@@ -2084,12 +2082,12 @@ TEST_F(DisplayTest, CompositorFrameWithRotation) {
// does not cover |rect3| initially, |quad| does not cover |quad2| in target
// space.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect3,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
}
}
@@ -2137,12 +2135,12 @@ TEST_F(DisplayTest, CompositorFrameWithPerspective) {
// long to define a enclosed rect to describe the occlusion region,
// occlusion region is not defined and no changes in quads.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -2161,9 +2159,9 @@ TEST_F(DisplayTest, CompositorFrameWithPerspective) {
// an enclosing rect to describe |quad2|. |quad2| is hiding behind |quad|,
// so it's removed from |quad_list|.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -2204,12 +2202,12 @@ TEST_F(DisplayTest, CompositorFrameWithOpacityChange) {
// Since the opacity of |rect2| is less than 1, |rect1| cannot occlude
// |rect2| even though |rect2| is inside |rect1|.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -2225,9 +2223,9 @@ TEST_F(DisplayTest, CompositorFrameWithOpacityChange) {
display_->RemoveOverdrawQuads(&frame);
// Repeat the above test and set the opacity of |rect1| to 1.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -2267,12 +2265,12 @@ TEST_F(DisplayTest, CompositorFrameWithOpaquenessChange) {
// Since the opaqueness of |rect2| is false, |rect1| cannot occlude
// |rect2| even though |rect2| is inside |rect1|.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -2288,9 +2286,9 @@ TEST_F(DisplayTest, CompositorFrameWithOpaquenessChange) {
display_->RemoveOverdrawQuads(&frame);
// Repeat the above test and set the opaqueness of |rect2| to true.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -2338,12 +2336,10 @@ TEST_F(DisplayTest, CompositorFrameZTranslate) {
// Since both |quad| and |quad2| are inside of a 3d object, DrawOcclusion
// will not be applied to them.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->rect.ToString());
+ EXPECT_EQ(rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->rect);
+ EXPECT_EQ(rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->rect);
}
}
@@ -2394,12 +2390,12 @@ TEST_F(DisplayTest, CompositorFrameWithTranslateTransformer) {
// |rect2| and |rect1| are disjoined as show in the first image. The size of
// |quad_list| remains 2.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -2424,9 +2420,9 @@ TEST_F(DisplayTest, CompositorFrameWithTranslateTransformer) {
// translation transform. |quad2| will be covered by |quad|, so |quad_list|
// size is reduced by 1.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
{
@@ -2455,13 +2451,12 @@ TEST_F(DisplayTest, CompositorFrameWithTranslateTransformer) {
// |quad2| is (100, 100, 100x20). So the visible region of |quad2| is
// (150, 100, 50x20).
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(gfx::Rect(150, 100, 50, 20).ToString(),
- frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ gfx::Rect(150, 100, 50, 20),
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
}
}
@@ -2521,12 +2516,12 @@ TEST_F(DisplayTest, CompositorFrameWithCombinedSharedQuadState) {
// |rect2|. |rect3| is covered by both |rect1| and |rect2|, so |rect3| is
// removed from |quad_list|.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
{
@@ -2548,16 +2543,15 @@ TEST_F(DisplayTest, CompositorFrameWithCombinedSharedQuadState) {
// and |rect2|, is (0, 0, 160x60). Since visible region of rect 4 is
// (160, 10, 30x30), |visible_rect| of |quad3| is updated.
EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
- EXPECT_EQ(gfx::Rect(160, 10, 30, 30).ToString(),
- frame.render_pass_list.front()
- ->quad_list.ElementAt(3)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
+ EXPECT_EQ(
+ gfx::Rect(160, 10, 30, 30),
+ frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect);
}
{
@@ -2577,16 +2571,15 @@ TEST_F(DisplayTest, CompositorFrameWithCombinedSharedQuadState) {
// and |rect2|, is (0, 0, 160x60). Since visible region of rect 5 is
// (10, 60, 120x50), |visible_rect| of |quad3| is updated.
EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
- EXPECT_EQ(gfx::Rect(10, 60, 120, 50).ToString(),
- frame.render_pass_list.front()
- ->quad_list.ElementAt(3)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
+ EXPECT_EQ(
+ gfx::Rect(10, 60, 120, 50),
+ frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect);
}
}
@@ -2699,15 +2692,15 @@ TEST_F(DisplayTest, CompositorFrameWithMultipleRenderPass) {
// But |rect3| so |rect3| is to be removed from |quad_list|.
EXPECT_EQ(2u, frame.render_pass_list.at(1)->quad_list.size());
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.at(1)
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.at(1)
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
- EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.at(1)->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.at(1)->quad_list.ElementAt(1)->visible_rect);
+ EXPECT_EQ(
+ rect3,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -2770,12 +2763,12 @@ TEST_F(DisplayTest, CompositorFrameWithCoveredRenderPass) {
// |quad_list|.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
EXPECT_EQ(1u, frame.render_pass_list.at(1)->quad_list.size());
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.at(1)
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.at(1)->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -2823,9 +2816,9 @@ TEST_F(DisplayTest, CompositorFrameWithClip) {
// |rect1| covers |rect2| as shown in the figure above, So the size of
// |quad_list| is reduced by 1.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
{
@@ -2851,12 +2844,12 @@ TEST_F(DisplayTest, CompositorFrameWithClip) {
// (0, 0, 60x60) |quad| and |quad2| (50, 50, 25x25) don't intersect in the
// target space. So no change is applied to quads.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
}
{
@@ -2881,13 +2874,12 @@ TEST_F(DisplayTest, CompositorFrameWithClip) {
// visible region of |quad2| is (60, 50, 10x10). So |quad2| is updated
// accordingly.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(gfx::Rect(60, 50, 10, 10).ToString(),
- frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ gfx::Rect(60, 50, 10, 10),
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
}
}
@@ -2931,9 +2923,9 @@ TEST_F(DisplayTest, CompositorFrameWithCopyRequest) {
// occlusion with copy_request on root RenderPass, |quad_list| reduces its
// size by 1 after calling remove overdraw.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -3009,18 +3001,18 @@ TEST_F(DisplayTest, CompositorFrameWithRenderPass) {
// occlude each other. Since AggregatedRenderPassDrawQuad |r1| and |r2|
// cannot be removed to reduce overdraw, |quad_list| remains unchanged.
EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
- EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
- EXPECT_EQ(rect4.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(3)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
+ EXPECT_EQ(
+ rect3,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
+ EXPECT_EQ(
+ rect4,
+ frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect);
}
{
@@ -3057,18 +3049,18 @@ TEST_F(DisplayTest, CompositorFrameWithRenderPass) {
// occlude each other. Since AggregatedRenderPassDrawQuad |r1| and |r2|
// cannot be removed to reduce overdraw, |quad_list| remains unchanged.
EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect5.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
- EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
- EXPECT_EQ(rect6.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(3)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect5,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
+ EXPECT_EQ(
+ rect3,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
+ EXPECT_EQ(
+ rect6,
+ frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect);
}
{
@@ -3104,15 +3096,15 @@ TEST_F(DisplayTest, CompositorFrameWithRenderPass) {
// Since AggregatedRenderPassDrawQuad |r1| and |r2| cannot be removed to
// reduce overdraw, |quad_list| is reduced by 1.
EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect5.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
- EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect5,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
+ EXPECT_EQ(
+ rect3,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
}
}
@@ -3183,18 +3175,18 @@ TEST_F(DisplayTest, CompositorFrameWithMultipleDrawQuadInSharedQuadState) {
// |visible_rect| of |shared_quad_state| is formed by 4 DrawQuads and it
// covers the visible region of |shared_quad_state2|.
EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1_1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1_2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1_3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1_4.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(3)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1_1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect1_2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
+ EXPECT_EQ(
+ rect1_3,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
+ EXPECT_EQ(
+ rect1_4,
+ frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect);
}
{
@@ -3219,22 +3211,21 @@ TEST_F(DisplayTest, CompositorFrameWithMultipleDrawQuadInSharedQuadState) {
// partially covers the visible region of |shared_quad_state2|. The
// |visible_rect| of |quad5| is updated.
EXPECT_EQ(5u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1_1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1_2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1_3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
- EXPECT_EQ(rect1_4.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(3)
- ->visible_rect.ToString());
- EXPECT_EQ(gfx::Rect(100, 0, 30, 30).ToString(),
- frame.render_pass_list.front()
- ->quad_list.ElementAt(5)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1_1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect1_2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
+ EXPECT_EQ(
+ rect1_3,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
+ EXPECT_EQ(
+ rect1_4,
+ frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect);
+ EXPECT_EQ(
+ gfx::Rect(100, 0, 30, 30),
+ frame.render_pass_list.front()->quad_list.ElementAt(5)->visible_rect);
}
{
@@ -3267,26 +3258,24 @@ TEST_F(DisplayTest, CompositorFrameWithMultipleDrawQuadInSharedQuadState) {
// |visible_rect| of DrawQuads in |share_quad_state2| are updated to the
// region shown on screen.
EXPECT_EQ(6u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect2_1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2_2.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2_3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
- EXPECT_EQ(rect2_4.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(3)
- ->visible_rect.ToString());
- EXPECT_EQ(gfx::Rect(0, 0, 20, 30).ToString(),
- frame.render_pass_list.front()
- ->quad_list.ElementAt(5)
- ->visible_rect.ToString());
- EXPECT_EQ(gfx::Rect(120, 0, 20, 30).ToString(),
- frame.render_pass_list.front()
- ->quad_list.ElementAt(6)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect2_1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect2_2,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
+ EXPECT_EQ(
+ rect2_3,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
+ EXPECT_EQ(
+ rect2_4,
+ frame.render_pass_list.front()->quad_list.ElementAt(3)->visible_rect);
+ EXPECT_EQ(
+ gfx::Rect(0, 0, 20, 30),
+ frame.render_pass_list.front()->quad_list.ElementAt(5)->visible_rect);
+ EXPECT_EQ(
+ gfx::Rect(120, 0, 20, 30),
+ frame.render_pass_list.front()->quad_list.ElementAt(6)->visible_rect);
}
}
@@ -3354,12 +3343,12 @@ TEST_F(DisplayTest, CompositorFrameWithNonInvertibleTransform) {
display_->RemoveOverdrawQuads(&frame);
// |quad2| is removed because it is not shown on screen in the target space.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(rect3.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(2)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ rect3,
+ frame.render_pass_list.front()->quad_list.ElementAt(2)->visible_rect);
}
{
@@ -3385,9 +3374,9 @@ TEST_F(DisplayTest, CompositorFrameWithNonInvertibleTransform) {
// |quad3| follows an non-invertible transform and it's covered by the
// occlusion rect. So |quad3| is removed from the |frame|.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -3425,9 +3414,9 @@ TEST_F(DisplayTest, DrawOcclusionWithLargeDrawQuad) {
// DrawQuad, so the size of quad_list remains unchanged after calling
// RemoveOverdrawQuads.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(rect1.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ rect1,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -3762,137 +3751,6 @@ TEST_F(DisplayTest, DontThrottleWhenParentBlocked) {
UpdateBeginFrameTime(sub_support.get(), frame_time);
}
-TEST_F(DisplayTest, InvalidPresentationTimestamps) {
- RendererSettings settings;
- id_allocator_.GenerateId();
- const LocalSurfaceId local_surface_id(
- id_allocator_.GetCurrentLocalSurfaceId());
-
- // Set up first display.
- SetUpSoftwareDisplay(settings);
- StubDisplayClient client;
- display_->Initialize(&client, manager_.surface_manager());
- display_->SetLocalSurfaceId(local_surface_id, 1.f);
- display_->Resize(gfx::Size(25, 25));
-
- {
- // A regular presentation timestamp.
- base::HistogramTester histograms;
- CompositorFrame frame =
- CompositorFrameBuilder()
- .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25))
- .Build();
- support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
- display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()});
- display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(),
- /*release_fence=*/gfx::GpuFenceHandle());
- display_->DidReceivePresentationFeedback({base::TimeTicks::Now(), {}, 0});
- EXPECT_THAT(histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidBeforeSwap"),
- testing::IsEmpty());
- EXPECT_THAT(histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidFromFuture"),
- testing::IsEmpty());
- }
-
- {
- // A presentation-timestamp that is earlier than the swap time.
- base::HistogramTester histograms;
- CompositorFrame frame =
- CompositorFrameBuilder()
- .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25))
- .Build();
- support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
- display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()});
- display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(),
- /*release_fence=*/gfx::GpuFenceHandle());
- display_->DidReceivePresentationFeedback(
- {base::TimeTicks::Now() - base::Seconds(1), {}, 0});
- EXPECT_THAT(histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidFromFuture"),
- testing::IsEmpty());
- auto buckets = histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidBeforeSwap");
- ASSERT_EQ(buckets.size(), 1u);
- EXPECT_GT(buckets[0].min, 0);
- EXPECT_LE(buckets[0].min, 1000);
- EXPECT_EQ(buckets[0].count, 1);
- }
-
- {
- // A presentation-timestamp that is in the near-future with hwclock: this
- // should be valid.
- base::HistogramTester histograms;
- CompositorFrame frame =
- CompositorFrameBuilder()
- .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25))
- .Build();
- support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
- display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()});
- display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(),
- /*release_fence=*/gfx::GpuFenceHandle());
- display_->DidReceivePresentationFeedback(
- {base::TimeTicks::Now() + base::Milliseconds(1),
- {},
- gfx::PresentationFeedback::kHWClock});
- EXPECT_THAT(histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidBeforeSwap"),
- testing::IsEmpty());
- EXPECT_THAT(histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidFromFuture"),
- testing::IsEmpty());
- }
-
- {
- // A presentation-timestamp that is in the near-future.
- base::HistogramTester histograms;
- CompositorFrame frame =
- CompositorFrameBuilder()
- .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25))
- .Build();
- support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
- display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()});
- display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(),
- /*release_fence=*/gfx::GpuFenceHandle());
- display_->DidReceivePresentationFeedback(
- {base::TimeTicks::Now() + base::Milliseconds(1), {}, 0});
- EXPECT_THAT(histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidBeforeSwap"),
- testing::IsEmpty());
- auto buckets = histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidFromFuture");
- ASSERT_EQ(buckets.size(), 1u);
- EXPECT_GE(buckets[0].min, 0);
- EXPECT_LE(buckets[0].min, 1);
- EXPECT_EQ(buckets[0].count, 1);
- }
-
- {
- // A presentation-timestamp that is in the future.
- base::HistogramTester histograms;
- CompositorFrame frame =
- CompositorFrameBuilder()
- .AddRenderPass(gfx::Rect(25, 25), gfx::Rect(25, 25))
- .Build();
- support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
- display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()});
- display_->DidReceiveSwapBuffersAck(GetTestSwapTimings(),
- /*release_fence=*/gfx::GpuFenceHandle());
- display_->DidReceivePresentationFeedback(
- {base::TimeTicks::Now() + base::Seconds(1), {}, 0});
- EXPECT_THAT(histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidBeforeSwap"),
- testing::IsEmpty());
-
- auto buckets = histograms.GetAllSamples(
- "Graphics.PresentationTimestamp.InvalidFromFuture");
- ASSERT_EQ(buckets.size(), 1u);
- EXPECT_GT(buckets[0].min, 0);
- EXPECT_LE(buckets[0].min, 1000);
- EXPECT_EQ(buckets[0].count, 1);
- }
-}
-
TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerDoesNotOcclude) {
SetUpGpuDisplay(RendererSettings());
@@ -3939,12 +3797,12 @@ TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerDoesNotOcclude) {
// Since none of the quads are culled, there should be 2 quads.
EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(quad_rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
- EXPECT_EQ(quad_rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(1)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ quad_rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
+ EXPECT_EQ(
+ quad_rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(1)->visible_rect);
}
}
@@ -3995,9 +3853,9 @@ TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerDoesOcclude) {
// no rounded corner, the later quad is culled. We should only have 1 quad
// in the final list now.
EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(quad_rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ quad_rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
}
}
@@ -4061,9 +3919,9 @@ TEST_F(DisplayTest, DrawOcclusionSplit) {
EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
display_->RemoveOverdrawQuads(&frame);
ASSERT_EQ(6u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(occluding_rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ occluding_rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
// Computed the expected quads
// +--------------------------------+
@@ -4333,9 +4191,9 @@ TEST_F(DisplayTest, DrawOcclusionWithRoundedCornerPartialOcclude) {
// no rounded corner, the later quad is culled. We should only have 1 quad
// in the final list now.
EXPECT_EQ(5u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
- EXPECT_EQ(quad_rect.ToString(), frame.render_pass_list.front()
- ->quad_list.ElementAt(0)
- ->visible_rect.ToString());
+ EXPECT_EQ(
+ quad_rect,
+ frame.render_pass_list.front()->quad_list.ElementAt(0)->visible_rect);
// For rounded rect of bounds (10, 10, 1000, 1000) and corner radius of 10,
// the occluding rect for it would be (13, 13, 994, 994).
@@ -4475,10 +4333,7 @@ class SkiaDelegatedInkRendererTest : public DisplayTest {
void SetUp() override { EnablePrediction(); }
void SetUpRenderers() {
- // First set up the display to use the Skia renderer.
- RendererSettings settings;
- settings.use_skia_renderer = true;
- SetUpGpuDisplay(settings);
+ SetUpGpuDisplay(RendererSettings());
// Initialize the renderer and create an ink renderer.
display_->Initialize(&client_, manager_.surface_manager());
@@ -5001,9 +4856,7 @@ class DelegatedInkDisplayTest
features::kUsePlatformDelegatedInk);
// Set up the display to use the Skia renderer.
- RendererSettings settings;
- settings.use_skia_renderer = true;
- SetUpGpuDisplaySkiaWithPlatformInk(settings);
+ SetUpGpuDisplaySkiaWithPlatformInk(RendererSettings());
display_->Initialize(&client_, manager_.surface_manager());
}
diff --git a/chromium/components/viz/service/display/dynamic_geometry_binding.cc b/chromium/components/viz/service/display/dynamic_geometry_binding.cc
deleted file mode 100644
index bb47dba7320..00000000000
--- a/chromium/components/viz/service/display/dynamic_geometry_binding.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.
-
-#include "components/viz/service/display/dynamic_geometry_binding.h"
-
-#include <stdint.h>
-
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "ui/gfx/geometry/quad_f.h"
-#include "ui/gfx/geometry/rect_f.h"
-
-namespace viz {
-
-DynamicGeometryBinding::DynamicGeometryBinding(gpu::gles2::GLES2Interface* gl)
- : gl_(gl), quad_vertices_vbo_(0), quad_elements_vbo_(0) {
- GLuint buffers[2];
- gl_->GenBuffers(2, buffers);
- quad_vertices_vbo_ = buffers[0];
- quad_elements_vbo_ = buffers[1];
-
- gl_->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_);
- gl_->BufferData(GL_ARRAY_BUFFER, sizeof(GeometryBindingQuad), nullptr,
- GL_DYNAMIC_DRAW);
-
- gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_);
- gl_->BufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GeometryBindingQuadIndex),
- nullptr, GL_DYNAMIC_DRAW);
-}
-
-DynamicGeometryBinding::~DynamicGeometryBinding() {
- GLuint buffers[2] = {quad_vertices_vbo_, quad_elements_vbo_};
- gl_->DeleteBuffers(2, buffers);
-}
-
-void DynamicGeometryBinding::InitializeCustomQuad(const gfx::QuadF& quad) {
- float uv[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
- InitializeCustomQuadWithUVs(quad, uv);
-}
-
-void DynamicGeometryBinding::InitializeCustomQuadWithUVs(const gfx::QuadF& quad,
- const float uv[8]) {
- GeometryBindingVertex v0 = {
- {quad.p1().x(), quad.p1().y(), 0.0f}, {uv[0], uv[1]}, 0.0f};
- GeometryBindingVertex v1 = {
- {quad.p2().x(), quad.p2().y(), 0.0f}, {uv[2], uv[3]}, 1.0f};
- GeometryBindingVertex v2 = {
- {quad.p3().x(), quad.p3().y(), 0.0f}, {uv[4], uv[5]}, 2.0f};
- GeometryBindingVertex v3 = {
- {quad.p4().x(), quad.p4().y(), 0.0f}, {uv[6], uv[7]}, 3.0f};
-
- GeometryBindingQuad local_quad = {v0, v1, v2, v3};
- GeometryBindingQuadIndex quad_index(
- static_cast<uint16_t>(0), static_cast<uint16_t>(1),
- static_cast<uint16_t>(2), static_cast<uint16_t>(3),
- static_cast<uint16_t>(0), static_cast<uint16_t>(2));
-
- gl_->BufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GeometryBindingQuad),
- &local_quad);
- gl_->BufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0,
- sizeof(GeometryBindingQuadIndex), &quad_index);
-}
-
-void DynamicGeometryBinding::PrepareForDraw() {
- SetupGLContext(gl_, quad_elements_vbo_, quad_vertices_vbo_);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/dynamic_geometry_binding.h b/chromium/components/viz/service/display/dynamic_geometry_binding.h
deleted file mode 100644
index 2d340962fad..00000000000
--- a/chromium/components/viz/service/display/dynamic_geometry_binding.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DYNAMIC_GEOMETRY_BINDING_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_DYNAMIC_GEOMETRY_BINDING_H_
-
-#include "base/memory/raw_ptr.h"
-#include "components/viz/service/display/geometry_binding.h"
-#include "components/viz/service/viz_service_export.h"
-
-namespace gfx {
-class QuadF;
-}
-
-namespace viz {
-
-class VIZ_SERVICE_EXPORT DynamicGeometryBinding {
- public:
- explicit DynamicGeometryBinding(gpu::gles2::GLES2Interface* gl);
-
- DynamicGeometryBinding(const DynamicGeometryBinding&) = delete;
- DynamicGeometryBinding& operator=(const DynamicGeometryBinding&) = delete;
-
- ~DynamicGeometryBinding();
-
- void PrepareForDraw();
- void InitializeCustomQuad(const gfx::QuadF& quad);
- void InitializeCustomQuadWithUVs(const gfx::QuadF& quad, const float uv[8]);
-
- private:
- raw_ptr<gpu::gles2::GLES2Interface> gl_;
-
- GLuint quad_vertices_vbo_;
- GLuint quad_elements_vbo_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_DYNAMIC_GEOMETRY_BINDING_H_
diff --git a/chromium/components/viz/service/display/external_use_client.h b/chromium/components/viz/service/display/external_use_client.h
index dbac7444dfb..e37901e41c5 100644
--- a/chromium/components/viz/service/display/external_use_client.h
+++ b/chromium/components/viz/service/display/external_use_client.h
@@ -87,8 +87,10 @@ class VIZ_SERVICE_EXPORT ExternalUseClient {
void set_paint_op_buffer(const cc::PaintOpBuffer* buffer) {
paint_op_buffer_ = buffer;
}
- const absl::optional<SkColor>& clear_color() const { return clear_color_; }
- void set_clear_color(const absl::optional<SkColor>& color) {
+ const absl::optional<SkColor4f>& clear_color() const {
+ return clear_color_;
+ }
+ void set_clear_color(const absl::optional<SkColor4f>& color) {
clear_color_ = color;
}
@@ -110,7 +112,7 @@ class VIZ_SERVICE_EXPORT ExternalUseClient {
sk_sp<SkImage> image_;
GrBackendFormat backend_format_;
raw_ptr<const cc::PaintOpBuffer> paint_op_buffer_ = nullptr;
- absl::optional<SkColor> clear_color_;
+ absl::optional<SkColor4f> clear_color_;
};
// If |maybe_concurrent_reads| is true then there can be concurrent reads to
diff --git a/chromium/components/viz/service/display/frame_rate_decider_unittest.cc b/chromium/components/viz/service/display/frame_rate_decider_unittest.cc
index e9f55fa7c1e..2be3702bc4b 100644
--- a/chromium/components/viz/service/display/frame_rate_decider_unittest.cc
+++ b/chromium/components/viz/service/display/frame_rate_decider_unittest.cc
@@ -29,7 +29,9 @@ class FrameRateDeciderTest : public testing::Test,
~FrameRateDeciderTest() override = default;
void SetUp() override {
- surface_manager_ = std::make_unique<SurfaceManager>(this, absl::nullopt);
+ surface_manager_ = std::make_unique<SurfaceManager>(
+ this, /*activation_deadline_in_frames=*/absl::nullopt,
+ /*max_uncommitted_frames=*/0);
bool hw_support_for_multiple_refresh_rates = true;
frame_rate_decider_ = std::make_unique<FrameRateDecider>(
surface_manager_.get(), this, hw_support_for_multiple_refresh_rates,
diff --git a/chromium/components/viz/service/display/geometry_binding.cc b/chromium/components/viz/service/display/geometry_binding.cc
deleted file mode 100644
index e2013e1c029..00000000000
--- a/chromium/components/viz/service/display/geometry_binding.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2011 The Chromium 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/viz/service/display/geometry_binding.h"
-
-#include <stdint.h>
-#include <string.h>
-
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "ui/gfx/geometry/rect_f.h"
-
-namespace viz {
-
-void SetupGLContext(gpu::gles2::GLES2Interface* gl,
- GLuint quad_elements_vbo,
- GLuint quad_vertices_vbo) {
- gl->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo);
-
- gl->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo);
- // OpenGL defines the last parameter to VertexAttribPointer as type
- // "const GLvoid*" even though it is actually an offset into the buffer
- // object's data store and not a pointer to the client's address space.
- const void* offsets[3] = {
- nullptr, reinterpret_cast<const void*>(3 * sizeof(float)),
- reinterpret_cast<const void*>(5 * sizeof(float)),
- };
-
- gl->VertexAttribPointer(GeometryBinding::PositionAttribLocation(), 3,
- GL_FLOAT, false, 6 * sizeof(float), offsets[0]);
- gl->VertexAttribPointer(GeometryBinding::TexCoordAttribLocation(), 2,
- GL_FLOAT, false, 6 * sizeof(float), offsets[1]);
- gl->VertexAttribPointer(GeometryBinding::TriangleIndexAttribLocation(), 1,
- GL_FLOAT, false, 6 * sizeof(float), offsets[2]);
- gl->EnableVertexAttribArray(GeometryBinding::PositionAttribLocation());
- gl->EnableVertexAttribArray(GeometryBinding::TexCoordAttribLocation());
- gl->EnableVertexAttribArray(GeometryBinding::TriangleIndexAttribLocation());
-}
-
-GeometryBindingQuad::GeometryBindingQuad() {
- v0 = {{0, 0, 0}, {0, 0}, 0};
- v1 = {{0, 0, 0}, {0, 0}, 0};
- v2 = {{0, 0, 0}, {0, 0}, 0};
- v3 = {{0, 0, 0}, {0, 0}, 0};
-}
-
-GeometryBindingQuad::GeometryBindingQuad(const GeometryBindingVertex& vert0,
- const GeometryBindingVertex& vert1,
- const GeometryBindingVertex& vert2,
- const GeometryBindingVertex& vert3) {
- v0 = vert0;
- v1 = vert1;
- v2 = vert2;
- v3 = vert3;
-}
-
-GeometryBindingQuadIndex::GeometryBindingQuadIndex() {
- memset(data, 0x0, sizeof(data));
-}
-
-GeometryBindingQuadIndex::GeometryBindingQuadIndex(uint16_t index0,
- uint16_t index1,
- uint16_t index2,
- uint16_t index3,
- uint16_t index4,
- uint16_t index5) {
- data[0] = index0;
- data[1] = index1;
- data[2] = index2;
- data[3] = index3;
- data[4] = index4;
- data[5] = index5;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/geometry_binding.h b/chromium/components/viz/service/display/geometry_binding.h
deleted file mode 100644
index 64bdf0b9ae4..00000000000
--- a/chromium/components/viz/service/display/geometry_binding.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2011 The Chromium 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 COMPONENTS_VIZ_SERVICE_DISPLAY_GEOMETRY_BINDING_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_GEOMETRY_BINDING_H_
-
-#include <stdint.h>
-
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
-#include "ui/gfx/geometry/rect_f.h"
-
-namespace viz {
-
-struct GeometryBindingVertex {
- float a_position[3];
- float a_texCoord[2];
- // Index of the vertex, divide by 4 to have the matrix for this quad.
- float a_index;
-};
-
-struct GeometryBindingQuad {
- GeometryBindingQuad();
- GeometryBindingQuad(const GeometryBindingVertex& vert0,
- const GeometryBindingVertex& vert1,
- const GeometryBindingVertex& vert2,
- const GeometryBindingVertex& vert3);
- GeometryBindingVertex v0, v1, v2, v3;
-};
-static_assert(sizeof(GeometryBindingQuad) == 24 * sizeof(float),
- "struct Quad should be densely packed");
-
-struct GeometryBindingQuadIndex {
- GeometryBindingQuadIndex();
- GeometryBindingQuadIndex(uint16_t index0,
- uint16_t index1,
- uint16_t index2,
- uint16_t index3,
- uint16_t index4,
- uint16_t index5);
-
- uint16_t data[6];
-};
-static_assert(sizeof(GeometryBindingQuadIndex) == 6 * sizeof(uint16_t),
- "struct QuadIndex should be densely packed");
-
-struct GeometryBinding {
- // All layer shaders share the same attribute locations for the vertex
- // positions and texture coordinates. This allows switching shaders without
- // rebinding attribute arrays.
- static int PositionAttribLocation() { return 0; }
- static int TexCoordAttribLocation() { return 1; }
- static int TriangleIndexAttribLocation() { return 2; }
-};
-
-void SetupGLContext(gpu::gles2::GLES2Interface* gl,
- GLuint quad_elements_vbo,
- GLuint quad_vertices_vbo);
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_GEOMETRY_BINDING_H_
diff --git a/chromium/components/viz/service/display/gl_renderer.cc b/chromium/components/viz/service/display/gl_renderer.cc
deleted file mode 100644
index 7fb664525ba..00000000000
--- a/chromium/components/viz/service/display/gl_renderer.cc
+++ /dev/null
@@ -1,4514 +0,0 @@
-// Copyright 2010 The Chromium 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/viz/service/display/gl_renderer.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <limits>
-#include <memory>
-#include <numeric>
-#include <set>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/check_op.h"
-#include "base/containers/cxx20_erase.h"
-#include "base/feature_list.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/raw_ptr.h"
-#include "base/notreached.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "base/trace_event/trace_event.h"
-#include "build/build_config.h"
-#include "cc/base/math_util.h"
-#include "cc/debug/debug_colors.h"
-#include "cc/paint/render_surface_filters.h"
-#include "cc/raster/scoped_gpu_raster.h"
-#include "components/viz/common/display/renderer_settings.h"
-#include "components/viz/common/features.h"
-#include "components/viz/common/frame_sinks/copy_output_request.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/common/quads/compositor_frame.h"
-#include "components/viz/common/quads/compositor_frame_metadata.h"
-#include "components/viz/common/quads/compositor_render_pass.h"
-#include "components/viz/common/quads/picture_draw_quad.h"
-#include "components/viz/common/quads/stream_video_draw_quad.h"
-#include "components/viz/common/quads/texture_draw_quad.h"
-#include "components/viz/common/resources/platform_color.h"
-#include "components/viz/common/resources/resource_format_utils.h"
-#include "components/viz/common/resources/resource_id.h"
-#include "components/viz/common/skia_helper.h"
-#include "components/viz/common/viz_utils.h"
-#include "components/viz/service/display/draw_polygon.h"
-#include "components/viz/service/display/dynamic_geometry_binding.h"
-#include "components/viz/service/display/layer_quad.h"
-#include "components/viz/service/display/output_surface.h"
-#include "components/viz/service/display/output_surface_frame.h"
-#include "components/viz/service/display/resource_fence.h"
-#include "components/viz/service/display/scoped_render_pass_texture.h"
-#include "components/viz/service/display/static_geometry_binding.h"
-#include "components/viz/service/display/texture_deleter.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/common/gpu_memory_allocation.h"
-#include "gpu/config/gpu_driver_bug_workaround_type.h"
-#include "gpu/config/gpu_feature_info.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkColorFilter.h"
-#include "third_party/skia/include/core/SkImage.h"
-#include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/core/SkTypes.h"
-#include "third_party/skia/include/effects/SkShaderMaskFilter.h"
-#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
-#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/color_transform.h"
-#include "ui/gfx/geometry/quad_f.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/geometry/rrect_f.h"
-#include "ui/gfx/geometry/size_conversions.h"
-#include "ui/gfx/geometry/skia_conversions.h"
-#include "ui/gfx/geometry/vector2d.h"
-#include "ui/gfx/gpu_fence_handle.h"
-
-using gpu::gles2::GLES2Interface;
-
-namespace viz {
-namespace {
-
-Float4 UVTransform(const TextureDrawQuad* quad) {
- gfx::RectF uv_rect =
- gfx::BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
- gfx::RectF visible_uv_rect = cc::MathUtil::ScaleRectProportional(
- uv_rect, gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
-
- gfx::PointF uv0 = visible_uv_rect.origin();
- gfx::PointF uv1 = visible_uv_rect.bottom_right();
- Float4 xform = {{uv0.x(), uv0.y(), uv1.x() - uv0.x(), uv1.y() - uv0.y()}};
- if (quad->y_flipped) {
- xform.data[1] = 1.0f - xform.data[1];
- xform.data[3] = -xform.data[3];
- }
- return xform;
-}
-
-// To prevent sampling outside the visible rect.
-Float4 UVClampRect(gfx::RectF uv_visible_rect,
- const gfx::Size& texture_size,
- SamplerType sampler) {
- gfx::SizeF half_texel(0.5f, 0.5f);
- if (sampler != SAMPLER_TYPE_2D_RECT) {
- half_texel.Scale(1.f / texture_size.width(), 1.f / texture_size.height());
- } else {
- uv_visible_rect.Scale(texture_size.width(), texture_size.height());
- }
- uv_visible_rect.Inset(
- gfx::InsetsF::VH(half_texel.height(), half_texel.width()));
- return {{uv_visible_rect.x(), uv_visible_rect.y(), uv_visible_rect.right(),
- uv_visible_rect.bottom()}};
-}
-
-Float4 PremultipliedColor(SkColor color, float opacity) {
- const U8CPU alpha255 = SkColorGetA(color);
- const unsigned int alpha256 = alpha255 + 1;
- const unsigned int premultiplied_red = (SkColorGetR(color) * alpha256) >> 8;
- const unsigned int premultiplied_green = (SkColorGetG(color) * alpha256) >> 8;
- const unsigned int premultiplied_blue = (SkColorGetB(color) * alpha256) >> 8;
- const float factor = opacity / 255.0f;
- return {{premultiplied_red * factor, premultiplied_green * factor,
- premultiplied_blue * factor, alpha255 * factor}};
-}
-
-SamplerType SamplerTypeFromTextureTarget(GLenum target) {
- switch (target) {
- case GL_TEXTURE_2D:
- return SAMPLER_TYPE_2D;
- case GL_TEXTURE_RECTANGLE_ARB:
- return SAMPLER_TYPE_2D_RECT;
- case GL_TEXTURE_EXTERNAL_OES:
- return SAMPLER_TYPE_EXTERNAL_OES;
- default:
- NOTREACHED();
- return SAMPLER_TYPE_2D;
- }
-}
-
-BlendMode BlendModeFromSkXfermode(SkBlendMode mode) {
- switch (mode) {
- case SkBlendMode::kSrcOver:
- return BLEND_MODE_NORMAL;
- case SkBlendMode::kDstIn:
- return BLEND_MODE_DESTINATION_IN;
- case SkBlendMode::kScreen:
- return BLEND_MODE_SCREEN;
- case SkBlendMode::kOverlay:
- return BLEND_MODE_OVERLAY;
- case SkBlendMode::kDarken:
- return BLEND_MODE_DARKEN;
- case SkBlendMode::kLighten:
- return BLEND_MODE_LIGHTEN;
- case SkBlendMode::kColorDodge:
- return BLEND_MODE_COLOR_DODGE;
- case SkBlendMode::kColorBurn:
- return BLEND_MODE_COLOR_BURN;
- case SkBlendMode::kHardLight:
- return BLEND_MODE_HARD_LIGHT;
- case SkBlendMode::kSoftLight:
- return BLEND_MODE_SOFT_LIGHT;
- case SkBlendMode::kDifference:
- return BLEND_MODE_DIFFERENCE;
- case SkBlendMode::kExclusion:
- return BLEND_MODE_EXCLUSION;
- case SkBlendMode::kMultiply:
- return BLEND_MODE_MULTIPLY;
- case SkBlendMode::kHue:
- return BLEND_MODE_HUE;
- case SkBlendMode::kSaturation:
- return BLEND_MODE_SATURATION;
- case SkBlendMode::kColor:
- return BLEND_MODE_COLOR;
- case SkBlendMode::kLuminosity:
- return BLEND_MODE_LUMINOSITY;
- case SkBlendMode::kSrc:
- return BLEND_MODE_NONE;
- default:
- NOTREACHED();
- return BLEND_MODE_NONE;
- }
-}
-
-// Adds a timer query that spans all GL calls in its scope. |viz.composite_time|
-// trace category must be enabled for this to work.
-// Note:: Multiple timer queries cannot be nested.
-class ScopedTimerQuery {
- public:
- ScopedTimerQuery(bool tracing_enabled,
- gpu::gles2::GLES2Interface* gl,
- base::queue<std::pair<unsigned, std::string>>* timer_queries,
- const std::string& quad_type_str)
- : gl_(gl) {
- if (!tracing_enabled) {
- gl_ = nullptr;
- return;
- }
- unsigned timer_query;
- gl_->GenQueriesEXT(1, &timer_query);
- gl_->BeginQueryEXT(GL_TIME_ELAPSED_EXT, timer_query);
- timer_queries->emplace(timer_query, quad_type_str);
- }
-
- ~ScopedTimerQuery() {
- if (gl_)
- gl_->EndQueryEXT(GL_TIME_ELAPSED_EXT);
- }
-
- private:
- raw_ptr<gpu::gles2::GLES2Interface> gl_;
-};
-
-void AccumulateDrawRects(const gfx::Rect& quad_rect,
- const gfx::Transform& target_transform,
- std::vector<gfx::Rect>* drawn_rects) {
- gfx::RectF quad_rect_f(quad_rect);
-
- // If the transform is not axis aligned then assume the largest possible
- // bounds the quad can take in the render target. In this case, we take the
- // sum of 2 sides.
- if (!target_transform.Preserves2dAxisAlignment()) {
- // Increase the length of each side to |width + height|.
- const int total_length = quad_rect.width() + quad_rect.height();
- quad_rect_f.set_height(total_length);
- quad_rect_f.set_width(total_length);
-
- // Ensure that the increase is equally distributed on either sides of the
- // quad such that the position of the center of the quad does not change.
- const float delta_x = -(quad_rect.height() / 2.f);
- const float delta_y = -(quad_rect.width() / 2.f);
- quad_rect_f.Offset(gfx::Vector2d(delta_x, delta_y));
-
- // Apply only the scale and translation component.
- const gfx::Vector2dF& translate = target_transform.To2dTranslation();
- const gfx::Vector2dF& scale = target_transform.To2dScale();
- quad_rect_f.Scale(scale.x(), scale.y());
- quad_rect_f.Offset(translate.x(), translate.y());
- } else {
- target_transform.TransformRect(&quad_rect_f);
- }
- drawn_rects->push_back(gfx::ToRoundedRect(quad_rect_f));
-}
-
-// Smallest unit that impact anti-aliasing output. We use this to
-// determine when anti-aliasing is unnecessary.
-const float kAntiAliasingEpsilon = 1.0f / 1024.0f;
-
-// A dummy timer query ID used to identify the beginning of a frame in the queue
-// of timer queries.
-const unsigned kTimerQueryDummy = 0;
-
-} // anonymous namespace
-
-static GLint GetActiveTextureUnit(GLES2Interface* gl) {
- GLint active_unit = 0;
- gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit);
- return active_unit;
-}
-
-// Parameters needed to draw a CompositorRenderPassDrawQuad.
-struct GLRenderer::DrawRenderPassDrawQuadParams {
- DrawRenderPassDrawQuadParams() {}
- ~DrawRenderPassDrawQuadParams() {
- // Don't leak the texture.
- DCHECK(!background_texture);
- }
-
- // Required inputs below.
- raw_ptr<const AggregatedRenderPassDrawQuad> quad = nullptr;
-
- // Either |contents_texture| or |bypass_quad_texture| is populated. The
- // |contents_texture| will be valid if non-null, and when null the
- // bypass_quad_texture will be valid instead.
- raw_ptr<ScopedRenderPassTexture> contents_texture = nullptr;
- struct {
- ResourceId resource_id = kInvalidResourceId;
- gfx::Size size;
- } bypass_quad_texture;
-
- raw_ptr<const gfx::QuadF> clip_region = nullptr;
- bool flip_texture = false;
-
- // |window_matrix| maps from [-1,-1]-[1,1] unit square coordinates to window
- // pixel coordinates.
- gfx::Transform window_matrix;
- // |projection_matrix| maps texture coordinates (in pixels) to the 2D plane in
- // [-1,-1]-[1,1] unit square coordinates. If FlippedFrameBuffer() is true,
- // |projection_matrix| includes this flip.
- gfx::Transform projection_matrix;
- // |quad_to_target_transform| transforms from local quad pixel coordinates to
- // target content space pixel coordinates, including scale, offset,
- // perspective, and rotation.
- gfx::Transform quad_to_target_transform;
- raw_ptr<const cc::FilterOperations> filters = nullptr;
- raw_ptr<const cc::FilterOperations> backdrop_filters = nullptr;
- absl::optional<gfx::RRectF> backdrop_filter_bounds;
-
- // Whether the texture to be sampled from needs to be flipped.
- bool source_needs_flip = false;
-
- float edge[24];
- SkScalar color_matrix[20];
-
- // Blending in the fragment shaders is used for modifications to the backdrop
- // and for supporting advanced blending equation when not available by the
- // underlying graphics API.
- bool use_shaders_for_blending = false;
- SkBlendMode blend_mode = SkBlendMode::kSrcOver;
-
- bool use_aa = false;
-
- // Some filters affect pixels outside the original contents bounds, in which
- // case ApplyImageFilter will modify this rect.
- gfx::RectF dst_rect;
-
- // A Skia image that should be sampled from instead of the original
- // contents.
- sk_sp<SkImage> filter_image;
-
- // The original contents, bound for sampling.
- std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL>
- bypass_quad_resource_lock;
-
- // A mask to be applied when drawing the RPDQ.
- std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL>
- mask_resource_lock;
-
- // Whether a color matrix needs to be applied by the shaders when drawing
- // the RPDQ.
- bool use_color_matrix = false;
-
- gfx::QuadF surface_quad;
-
- // |contents_device_transform| transforms from vertex geometry, which is often
- // the unit quad [-0.5, 0.5], all the way to 2D window pixel coordinates,
- // including 3D effects, frame buffer orientation, and window offset. The
- // definition of the incoming vertex geometry comes from either
- // shared_geometry_ or clipped_geometry_, which are initialized from
- // DirectRenderer::QuadVertexRect or DynamicGeometryBinding, respectively.
- // |contents_device_transform| is typically calculated as
- // |window_matrix| * |projection_matrix| * |quad_rect_matrix|
- // and then flattened with FlattenTo2d(). Here, |quad_rect_matrix| is a
- // combination of the geometry->quad transform as well as the quad->target
- // space transform. The geometry->quad is the mapping from the bound geometry,
- // often [-0.5, 0.5], to the quad, which is quad->rect.
- gfx::Transform contents_device_transform;
-
- gfx::RectF tex_coord_rect;
-
- // The color space of the texture bound for sampling (from filter_image or
- // bypass_quad_resource_lock, depending on the path taken).
- gfx::ColorSpace contents_and_bypass_color_space;
-
- // Background filters block.
- // Original background texture.
- uint32_t background_texture = 0;
- GLenum background_texture_format = 0;
- // Backdrop bounding box.
- gfx::Rect background_rect;
- // Filtered background texture.
- sk_sp<SkImage> background_image;
- GLuint background_image_id = 0;
- // A multiplier for the temporary surface we create to apply the backdrop
- // filter.
- float backdrop_filter_quality = 1.0;
- // Whether the original background texture is needed for the mask.
- bool mask_for_background = false;
-
- bool apply_shader_based_rounded_corner = true;
-};
-
-class GLRenderer::ScopedUseGrContext {
- public:
- static std::unique_ptr<ScopedUseGrContext> Create(GLRenderer* renderer) {
- // GrContext for filters is created lazily, and may fail if the context
- // is lost.
- // TODO(vmiura,bsalomon): crbug.com/487850 Ensure that
- // ContextProvider::GrContext() does not return NULL.
- GrDirectContext* direct = GrAsDirectContext(
- renderer->output_surface_->context_provider()->GrContext());
- if (direct)
- return base::WrapUnique(new ScopedUseGrContext(renderer));
- return nullptr;
- }
-
- ScopedUseGrContext(const ScopedUseGrContext&) = delete;
- ScopedUseGrContext& operator=(const ScopedUseGrContext&) = delete;
-
- ~ScopedUseGrContext() {
- // Pass context control back to GLrenderer.
- scoped_gpu_raster_ = nullptr;
- renderer_->RestoreGLStateAfterSkia();
- }
-
- GrDirectContext* context() const {
- return renderer_->output_surface_->context_provider()->GrContext();
- }
-
- private:
- explicit ScopedUseGrContext(GLRenderer* renderer)
- : scoped_gpu_raster_(new cc::ScopedGpuRaster(
- renderer->output_surface_->context_provider())),
- renderer_(renderer) {
- // scoped_gpu_raster_ passes context control to Skia.
- }
-
- std::unique_ptr<cc::ScopedGpuRaster> scoped_gpu_raster_;
- raw_ptr<GLRenderer> renderer_;
-};
-
-GLRenderer::GLRenderer(
- const RendererSettings* settings,
- const DebugRendererSettings* debug_settings,
- OutputSurface* output_surface,
- DisplayResourceProviderGL* resource_provider,
- OverlayProcessorInterface* overlay_processor,
- scoped_refptr<base::SingleThreadTaskRunner> current_task_runner)
- : DirectRenderer(settings,
- debug_settings,
- output_surface,
- resource_provider,
- overlay_processor),
- shared_geometry_quad_(QuadVertexRect()),
- gl_(output_surface->context_provider()->ContextGL()),
- context_support_(output_surface->context_provider()->ContextSupport()),
- texture_deleter_(current_task_runner),
- copier_(output_surface->context_provider(), &texture_deleter_),
- sync_queries_(gl_),
- bound_geometry_(NO_BINDING),
- current_task_runner_(std::move(current_task_runner)) {
- DCHECK(gl_);
- DCHECK(context_support_);
-
- const auto& context_caps =
- output_surface_->context_provider()->ContextCapabilities();
-
- use_discard_framebuffer_ = context_caps.discard_framebuffer;
- use_sync_query_ = context_caps.sync_query;
- use_blend_equation_advanced_ = context_caps.blend_equation_advanced;
- use_blend_equation_advanced_coherent_ =
- context_caps.blend_equation_advanced_coherent;
- use_occlusion_query_ = context_caps.occlusion_query;
- use_timer_query_ = context_caps.timer_queries;
- use_swap_with_bounds_ = context_caps.swap_buffers_with_bounds;
- supports_multi_sampling_ = context_caps.max_samples > 0;
- prefer_draw_to_copy_ = output_surface_->context_provider()
- ->GetGpuFeatureInfo()
- .IsWorkaroundEnabled(gpu::PREFER_DRAW_TO_COPY);
- use_fast_path_solid_color_quad_ =
- features::IsUsingFastPathForSolidColorQuad();
- InitializeSharedObjects();
-}
-
-GLRenderer::~GLRenderer() {
- CleanupSharedObjects();
-
- auto* context_provider = output_surface_->context_provider();
- auto* cache_controller = context_provider->CacheController();
-
- if (context_busy_) {
- cache_controller->ClientBecameNotBusy(std::move(context_busy_));
- }
- if (context_visibility_) {
- cache_controller->ClientBecameNotVisibleDuringShutdown(
- std::move(context_visibility_));
- }
-}
-
-bool GLRenderer::CanPartialSwap() {
- if (use_swap_with_bounds_)
- return false;
- auto* context_provider = output_surface_->context_provider();
- return context_provider->ContextCapabilities().post_sub_buffer;
-}
-
-void GLRenderer::DidChangeVisibility() {
- if (visible_) {
- output_surface_->EnsureBackbuffer();
- } else {
- TRACE_EVENT0("viz", "GLRenderer::DidChangeVisibility dropping resources");
- ReleaseRenderPassTextures();
- output_surface_->DiscardBackbuffer();
- gl_->ReleaseShaderCompiler();
- }
-
- PrepareGeometry(NO_BINDING);
-
- auto* context_provider = output_surface_->context_provider();
- auto* cache_controller = context_provider->CacheController();
- if (visible_) {
- DCHECK(!context_visibility_);
- context_visibility_ = cache_controller->ClientBecameVisible();
- } else {
- DCHECK(context_visibility_);
- cache_controller->ClientBecameNotVisible(std::move(context_visibility_));
- }
-}
-
-void GLRenderer::ReleaseRenderPassTextures() {
- render_pass_textures_.clear();
- render_pass_backdrop_textures_.clear();
-}
-
-void GLRenderer::DiscardPixels() {
- if (!use_discard_framebuffer_)
- return;
- bool using_default_framebuffer =
- !current_framebuffer_texture_ &&
- output_surface_->capabilities().uses_default_gl_framebuffer;
- GLenum attachments[] = {static_cast<GLenum>(
- using_default_framebuffer ? GL_COLOR_EXT : GL_COLOR_ATTACHMENT0_EXT)};
- gl_->DiscardFramebufferEXT(GL_FRAMEBUFFER, std::size(attachments),
- attachments);
-}
-
-void GLRenderer::PrepareSurfaceForPass(
- SurfaceInitializationMode initialization_mode,
- const gfx::Rect& render_pass_scissor) {
- SetViewport();
-
- switch (initialization_mode) {
- case SURFACE_INITIALIZATION_MODE_PRESERVE:
- EnsureScissorTestDisabled();
- return;
- case SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR:
- EnsureScissorTestDisabled();
- DiscardPixels();
- ClearFramebuffer();
- break;
- case SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR:
- SetScissorTestRect(render_pass_scissor);
- ClearFramebuffer();
- break;
- }
-
- if (OverdrawTracingEnabled()) {
- gl_->GenQueriesEXT(1, &occlusion_query_);
- gl_->BeginQueryEXT(GL_SAMPLES_PASSED_ARB, occlusion_query_);
- }
-
- // For each render pass, reset the drawn region.
- drawn_rects_.clear();
-}
-
-void GLRenderer::ClearFramebuffer() {
- // On DEBUG builds, opaque render passes are cleared to blue to easily see
- // regions that were not drawn on the screen.
- if (current_frame()->current_render_pass->has_transparent_background)
- gl_->ClearColor(0, 0, 0, 0);
- else
- gl_->ClearColor(0, 0, 1, 1);
-
- gl_->ClearStencil(0);
-
- bool always_clear = overdraw_feedback_;
-#ifndef NDEBUG
- always_clear = true;
-#endif
- if (always_clear ||
- current_frame()->current_render_pass->has_transparent_background) {
- GLbitfield clear_bits = GL_COLOR_BUFFER_BIT;
- if (always_clear)
- clear_bits |= GL_STENCIL_BUFFER_BIT;
- gl_->Clear(clear_bits);
- }
-}
-
-void GLRenderer::BeginDrawingFrame() {
- TRACE_EVENT0("viz", "GLRenderer::BeginDrawingFrame");
-
- if (!context_busy_) {
- context_busy_ = output_surface_->context_provider()
- ->CacheController()
- ->ClientBecameBusy();
- }
-
- // Begin batching read of shared images.
- gl_->BeginBatchReadAccessSharedImageCHROMIUM();
-
- scoped_refptr<ResourceFence> read_lock_fence;
- if (use_sync_query_) {
- read_lock_fence = sync_queries_.StartNewFrame();
- } else {
- read_lock_fence =
- base::MakeRefCounted<DisplayResourceProviderGL::SynchronousFence>(gl_);
- }
- resource_provider()->SetReadLockFence(read_lock_fence.get());
-
- // Insert WaitSyncTokenCHROMIUM on quad resources prior to drawing the frame,
- // so that drawing can proceed without GL context switching interruptions.
- for (const auto& pass : *current_frame()->render_passes_in_draw_order) {
- for (auto* quad : pass->quad_list) {
- for (ResourceId resource_id : quad->resources)
- resource_provider()->WaitSyncToken(resource_id);
- }
- }
-
- // TODO(enne): Do we need to reinitialize all of this state per frame?
- ReinitializeGLState();
-
- // Add a dummy timer query as a fence to identify the beginning of a frame in
- // the circular queue.
- if (CompositeTimeTracingEnabled())
- timer_queries_.emplace(kTimerQueryDummy, "");
-
- num_triangles_drawn_ = 0;
-}
-
-void GLRenderer::DoDrawQuad(const DrawQuad* quad,
- const gfx::QuadF* clip_region) {
- DCHECK(quad->rect.Contains(quad->visible_rect));
- if (quad->material != DrawQuad::Material::kTextureContent) {
- FlushTextureQuadCache(SHARED_BINDING);
- }
-
- switch (quad->material) {
- case DrawQuad::Material::kInvalid:
- NOTREACHED();
- break;
- case DrawQuad::Material::kAggregatedRenderPass:
- DrawRenderPassQuad(AggregatedRenderPassDrawQuad::MaterialCast(quad),
- clip_region);
- break;
- case DrawQuad::Material::kDebugBorder:
- DrawDebugBorderQuad(DebugBorderDrawQuad::MaterialCast(quad));
- break;
- case DrawQuad::Material::kPictureContent:
- // PictureDrawQuad should only be used for resourceless software draws.
- NOTREACHED();
- break;
- case DrawQuad::Material::kCompositorRenderPass:
- // At this point, RenderPassDrawQuads should be replaced by
- // AggregatedRenderPassDrawQuad.
- NOTREACHED();
- break;
- case DrawQuad::Material::kSolidColor:
- DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad), clip_region);
- break;
- case DrawQuad::Material::kStreamVideoContent:
- DrawStreamVideoQuad(StreamVideoDrawQuad::MaterialCast(quad), clip_region);
- break;
- case DrawQuad::Material::kSurfaceContent:
- // Surface content should be fully resolved to other quad types before
- // reaching a direct renderer.
- NOTREACHED();
- break;
- case DrawQuad::Material::kSharedElement:
- // Shared element should be fully resolved to other quad types before
- // reaching a direct renderer.
- NOTREACHED();
- break;
- case DrawQuad::Material::kTextureContent:
- EnqueueTextureQuad(TextureDrawQuad::MaterialCast(quad), clip_region);
- break;
- case DrawQuad::Material::kTiledContent:
- DrawTileQuad(TileDrawQuad::MaterialCast(quad), clip_region);
- break;
- case DrawQuad::Material::kYuvVideoContent:
- DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad), clip_region);
- break;
- case DrawQuad::Material::kVideoHole:
- // VideoHoleDrawQuad should only be used by Cast, and should
- // have been replaced by cast-specific OverlayProcessor before
- // reach here. In non-cast build, an untrusted render could send such
- // Quad and the quad would then reach here unexpectedly. Therefore
- // we should skip NOTREACHED() so an untrusted render is not capable
- // of causing a crash.
- break;
- }
-}
-
-// This function does not handle 3D sorting right now, since the debug border
-// quads are just drawn as their original quads and not in split pieces. This
-// results in some debug border quads drawing over foreground quads.
-void GLRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
- SetBlendEnabled(quad->ShouldDrawWithBlending());
-
- SetUseProgram(ProgramKey::DebugBorder(), gfx::ColorSpace::CreateSRGB(),
- CurrentRenderPassColorSpace());
-
- // Use the full quad_rect for debug quads to not move the edges based on
- // partial swaps.
- gfx::Rect layer_rect = quad->rect;
- gfx::Transform render_matrix;
- QuadRectTransform(&render_matrix,
- quad->shared_quad_state->quad_to_target_transform,
- gfx::RectF(layer_rect));
- SetShaderMatrix(current_frame()->projection_matrix * render_matrix);
- SetShaderColor(quad->color, 1.f);
-
- gl_->LineWidth(quad->width);
-
- // The indices for the line are stored in the same array as the triangle
- // indices.
- gl_->DrawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, nullptr);
-}
-
-// This is a utility to convert from GrGLenum color format into the equivalent
-// skColorType format. Note: this only supports the limited set of values that
-// can get returned by GLRenderer::GetBackdropTexture().
-static SkColorType GlFormatToSkFormat(GrGLenum format) {
- switch (format) {
- case GL_RGB:
- return kRGB_888x_SkColorType;
- case GL_RGBA:
- return kRGBA_8888_SkColorType;
- case GL_BGRA_EXT:
- return kBGRA_8888_SkColorType;
- case GL_RGB10_A2_EXT:
- return kRGBA_1010102_SkColorType;
- default:
- NOTREACHED() << std::hex << std::showbase << format;
- return kN32_SkColorType;
- }
-}
-
-static GrGLenum SkFormatToGlFormat(SkColorType format) {
- switch (format) {
- case kRGB_888x_SkColorType:
- return GL_RGB8_OES;
- case kRGBA_8888_SkColorType:
- return GL_RGBA8_OES;
- case kBGRA_8888_SkColorType:
- return GL_BGRA8_EXT;
- case kRGBA_1010102_SkColorType:
- return GL_RGB10_A2_EXT;
- default:
- NOTREACHED();
- return GL_RGBA8_OES;
- }
-}
-
-// Wrap a given texture in a Ganesh backend texture.
-static sk_sp<SkImage> WrapTexture(uint32_t texture_id,
- uint32_t target,
- const gfx::Size& size,
- GrDirectContext* context,
- bool flip_texture,
- SkColorType format,
- bool adopt_texture) {
- GrGLenum texture_format = SkFormatToGlFormat(format);
- GrGLTextureInfo texture_info;
- texture_info.fTarget = target;
- texture_info.fID = texture_id;
- texture_info.fFormat = texture_format;
- GrBackendTexture backend_texture(size.width(), size.height(),
- GrMipMapped::kNo, texture_info);
- GrSurfaceOrigin origin =
- flip_texture ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
- if (adopt_texture) {
- return SkImage::MakeFromAdoptedTexture(
- context, backend_texture, origin, format, kPremul_SkAlphaType, nullptr);
- } else {
- return SkImage::MakeFromTexture(context, backend_texture, origin, format,
- kPremul_SkAlphaType, nullptr);
- }
-}
-
-static gfx::RectF CenteredRect(const gfx::Rect& tile_rect) {
- return gfx::RectF(
- gfx::PointF(-0.5f * tile_rect.width(), -0.5f * tile_rect.height()),
- gfx::SizeF(tile_rect.size()));
-}
-
-bool GLRenderer::CanApplyBlendModeUsingBlendFunc(SkBlendMode blend_mode) {
- return use_blend_equation_advanced_ || blend_mode == SkBlendMode::kSrcOver ||
- blend_mode == SkBlendMode::kDstIn ||
- blend_mode == SkBlendMode::kScreen;
-}
-
-void GLRenderer::ApplyBlendModeUsingBlendFunc(SkBlendMode blend_mode) {
- // Any modes set here must be reset in RestoreBlendFuncToDefault
- if (blend_mode == SkBlendMode::kSrcOver) {
- // Left no-op intentionally.
- } else if (blend_mode == SkBlendMode::kDstIn) {
- gl_->BlendFunc(GL_ZERO, GL_SRC_ALPHA);
- } else if (blend_mode == SkBlendMode::kDstOut) {
- gl_->BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
- } else if (blend_mode == SkBlendMode::kScreen) {
- gl_->BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
- } else {
- DCHECK(use_blend_equation_advanced_);
- GLenum equation = GL_FUNC_ADD;
- switch (blend_mode) {
- case SkBlendMode::kScreen:
- equation = GL_SCREEN_KHR;
- break;
- case SkBlendMode::kOverlay:
- equation = GL_OVERLAY_KHR;
- break;
- case SkBlendMode::kDarken:
- equation = GL_DARKEN_KHR;
- break;
- case SkBlendMode::kLighten:
- equation = GL_LIGHTEN_KHR;
- break;
- case SkBlendMode::kColorDodge:
- equation = GL_COLORDODGE_KHR;
- break;
- case SkBlendMode::kColorBurn:
- equation = GL_COLORBURN_KHR;
- break;
- case SkBlendMode::kHardLight:
- equation = GL_HARDLIGHT_KHR;
- break;
- case SkBlendMode::kSoftLight:
- equation = GL_SOFTLIGHT_KHR;
- break;
- case SkBlendMode::kDifference:
- equation = GL_DIFFERENCE_KHR;
- break;
- case SkBlendMode::kExclusion:
- equation = GL_EXCLUSION_KHR;
- break;
- case SkBlendMode::kMultiply:
- equation = GL_MULTIPLY_KHR;
- break;
- case SkBlendMode::kHue:
- equation = GL_HSL_HUE_KHR;
- break;
- case SkBlendMode::kSaturation:
- equation = GL_HSL_SATURATION_KHR;
- break;
- case SkBlendMode::kColor:
- equation = GL_HSL_COLOR_KHR;
- break;
- case SkBlendMode::kLuminosity:
- equation = GL_HSL_LUMINOSITY_KHR;
- break;
- default:
- NOTREACHED() << "Unexpected blend mode: SkBlendMode::k"
- << SkBlendMode_Name(blend_mode);
- return;
- }
- gl_->BlendEquation(equation);
- }
-}
-
-void GLRenderer::RestoreBlendFuncToDefault(SkBlendMode blend_mode) {
- switch (blend_mode) {
- case SkBlendMode::kSrcOver:
- break;
- case SkBlendMode::kDstIn:
- case SkBlendMode::kDstOut:
- case SkBlendMode::kScreen:
- gl_->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- break;
- default:
- DCHECK(use_blend_equation_advanced_);
- gl_->BlendEquation(GL_FUNC_ADD);
- }
-}
-
-bool GLRenderer::ShouldApplyBackdropFilters(
- const DrawRenderPassDrawQuadParams* params) {
- if (!params->backdrop_filters)
- return false;
- if (params->quad->shared_quad_state->opacity == 0.f)
- return false;
- DCHECK(!params->backdrop_filters->IsEmpty());
- return true;
-}
-
-gfx::Rect GLRenderer::GetBackdropBoundingBoxForRenderPassQuad(
- DrawRenderPassDrawQuadParams* params,
- gfx::Transform* backdrop_filter_bounds_transform,
- absl::optional<gfx::RRectF>* backdrop_filter_bounds,
- gfx::Rect* unclipped_rect) const {
- DCHECK(backdrop_filter_bounds_transform);
- DCHECK(backdrop_filter_bounds);
- DCHECK(unclipped_rect);
-
- const auto* quad = params->quad.get();
- gfx::QuadF scaled_region;
- // |scaled_region| is a quad in [-0.5,0.5] space that represents |clip_region|
- // as a fraction of the space defined by |quad->rect|. If |clip_region| is
- // nullptr, then scaled_region is [-0.5,0.5].
- if (!GetScaledRegion(quad->rect, params->clip_region, &scaled_region)) {
- scaled_region = SharedGeometryQuad().BoundingBox();
- }
- // |backdrop_filter_bounds| is a rounded rect in [-0.5,0.5] space that
- // represents |params->backdrop_filter_bounds| as a fraction of the space
- // defined by |quad->rect|, not including its offset.
- *backdrop_filter_bounds = gfx::RRectF();
- if (!params->backdrop_filter_bounds ||
- !GetScaledRRectF(quad->rect, params->backdrop_filter_bounds.value(),
- &backdrop_filter_bounds->value())) {
- backdrop_filter_bounds->reset();
- }
-
- // |backdrop_rect| is now the bounding box of clip_region, in window pixel
- // coordinates, and with flip applied.
- gfx::Rect backdrop_rect = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect(
- params->contents_device_transform, scaled_region.BoundingBox()));
-
- if (!backdrop_rect.IsEmpty() && (params->filters || params->use_aa)) {
- // If we have regular filters or antialiasing, grab an extra one-pixel
- // border around the background, so texture edge clamping gives us a
- // transparent border.
- backdrop_rect.Inset(-1);
- }
-
- *unclipped_rect = backdrop_rect;
- backdrop_rect.Intersect(MoveFromDrawToWindowSpace(
- current_frame()->current_render_pass->output_rect));
- if (ShouldApplyBackdropFilters(params)) {
- float max_pixel_movement = params->backdrop_filters->MaximumPixelMovement();
- gfx::Rect scissor_rect(current_window_space_viewport_);
- scissor_rect.Inset(-max_pixel_movement);
- backdrop_rect.Intersect(scissor_rect);
- }
-
- // The frame buffer flip is already included in the captured backdrop image,
- // and it is included in |contents_device_transform| (through
- // |projection_matrix|). Don't double-flip.
- *backdrop_filter_bounds_transform = params->contents_device_transform;
- float new_y = 2 * backdrop_filter_bounds_transform->To2dTranslation().y() +
- backdrop_rect.bottom() - unclipped_rect->bottom() +
- backdrop_rect.y() - unclipped_rect->y();
- backdrop_filter_bounds_transform->PostScale(1, -1);
- backdrop_filter_bounds_transform->PostTranslate(0, new_y);
-
- // Shift to the space of the captured backdrop image.
- backdrop_filter_bounds_transform->PostTranslate(-backdrop_rect.x(),
- -backdrop_rect.y());
-
- return backdrop_rect;
-}
-
-GLenum GLRenderer::GetFramebufferCopyTextureFormat() {
- // If copying a non-root renderpass then use the format of the bound
- // texture. Otherwise, we use the format of the default framebuffer. But
- // whatever the format is, convert it to a valid format for CopyTexSubImage2D.
- GLenum format;
- if (!current_framebuffer_texture_) {
- format = output_surface_->GetFramebufferCopyTextureFormat();
- } else {
- ResourceFormat resource_format = CurrentRenderPassResourceFormat();
- DCHECK(GLSupportsFormat(resource_format));
- format = GLCopyTextureInternalFormat(resource_format);
- }
- // Verify the format is valid for GLES2's glCopyTexSubImage2D.
- DCHECK(format == GL_ALPHA || format == GL_LUMINANCE ||
- format == GL_LUMINANCE_ALPHA || format == GL_RGB ||
- format == GL_RGBA ||
- (output_surface_->context_provider()
- ->ContextCapabilities()
- .texture_format_bgra8888 &&
- format == GL_BGRA_EXT) ||
- format == GL_RGB10_A2_EXT)
- << std::hex << std::showbase << format;
- return format;
-}
-
-uint32_t GLRenderer::GetBackdropTexture(const gfx::Rect& window_rect,
- float scale,
- GLenum* internal_format) {
- DCHECK(internal_format);
- DCHECK_GE(window_rect.x(), 0);
- DCHECK_GE(window_rect.y(), 0);
- DCHECK_LE(window_rect.right(), current_surface_size_.width());
- DCHECK_LE(window_rect.bottom(), current_surface_size_.height());
-
- uint32_t texture_id;
- gl_->GenTextures(1, &texture_id);
- DCHECK(texture_id);
- gl_->BindTexture(GL_TEXTURE_2D, texture_id);
-
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- ResourceFormat resource_format = CurrentRenderPassResourceFormat();
- // Get gl_format, gl_type and internal_format.
- DCHECK(GLSupportsFormat(resource_format));
- *internal_format = GLInternalFormat(resource_format);
- GLenum gl_format = GLDataFormat(resource_format);
- GLenum gl_type = GLDataType(resource_format);
-
- if (supports_multi_sampling_ && scale != 1.0f) {
- DCHECK(!prefer_draw_to_copy_ || !current_framebuffer_texture_);
-
- gfx::Size target_size = window_rect.size();
- target_size = gfx::ScaleToCeiledSize(target_size, scale);
-
- gl_->TexImage2D(GL_TEXTURE_2D, 0, *internal_format, target_size.width(),
- target_size.height(), 0, gl_format, gl_type, nullptr);
-
- unsigned fbo = 0;
- gl_->GenFramebuffers(1, &fbo);
- gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
- gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, texture_id, 0);
- DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
- gl_->CheckFramebufferStatus(GL_DRAW_FRAMEBUFFER_EXT));
-
- gl_->Scissor(0, 0, target_size.width(), target_size.height());
-
- gl_->BlitFramebufferCHROMIUM(window_rect.x(), window_rect.y(),
- window_rect.right(), window_rect.bottom(), 0,
- 0, target_size.width(), target_size.height(),
- GL_COLOR_BUFFER_BIT, GL_LINEAR);
-
- gl_->DeleteFramebuffers(1, &fbo);
- } else if (prefer_draw_to_copy_ && current_framebuffer_texture_) {
- // If there is a source texture |current_framebuffer_texture_| and the
- // workaround |prefer_draw_to_copy_| is enabled, then do texture to texture
- // copy via draw instead of glCopyTexImage2D.
-
- // Size the destination texture with empty data. This is required since
- // CopySubTextureCHROMIUM() does not sizes the texture but CopyTexImage2D
- // does.
- gl_->TexImage2D(GL_TEXTURE_2D, 0, *internal_format, window_rect.width(),
- window_rect.height(), 0, gl_format, gl_type, nullptr);
- gl_->CopySubTextureCHROMIUM(
- current_framebuffer_texture_->id(), 0, GL_TEXTURE_2D, texture_id, 0, 0,
- 0, window_rect.x(), window_rect.y(), window_rect.width(),
- window_rect.height(), GL_FALSE, GL_FALSE, GL_FALSE);
- } else {
- *internal_format = GetFramebufferCopyTextureFormat();
-
- // CopyTexImage2D requires inernalformat channels to be a subset of
- // the channels of the source texture internalformat.
- DCHECK(*internal_format == GL_RGB || *internal_format == GL_RGBA ||
- *internal_format == GL_BGRA_EXT ||
- *internal_format == GL_RGB10_A2_EXT);
- if (*internal_format == GL_BGRA_EXT)
- *internal_format = GL_RGBA;
- gl_->CopyTexImage2D(GL_TEXTURE_2D, 0, *internal_format, window_rect.x(),
- window_rect.y(), window_rect.width(),
- window_rect.height(), 0);
- }
- gl_->BindTexture(GL_TEXTURE_2D, 0);
- return texture_id;
-}
-
-static sk_sp<SkImage> FinalizeImage(sk_sp<SkSurface> surface) {
- // Flush the drawing before source texture read lock goes out of scope.
- // Skia API does not guarantee that when the SkImage goes out of scope,
- // its externally referenced resources would force the rendering to be
- // flushed.
- surface->getCanvas()->flush();
- sk_sp<SkImage> image = surface->makeImageSnapshot();
- if (!image || !image->isTextureBacked()) {
- return nullptr;
- }
- return image;
-}
-
-sk_sp<SkImage> GLRenderer::ApplyBackdropFilters(
- DrawRenderPassDrawQuadParams* params,
- const gfx::Rect& unclipped_rect,
- const absl::optional<gfx::RRectF>& backdrop_filter_bounds,
- const gfx::Transform& backdrop_filter_bounds_transform) {
- DCHECK(ShouldApplyBackdropFilters(params));
- DCHECK(params->backdrop_filter_quality > 0.0f &&
- params->backdrop_filter_quality <= 1.0f);
- DCHECK(!params->filters)
- << "Filters should always be in a separate Effect node";
- const auto* quad = params->quad.get();
- auto use_gr_context = ScopedUseGrContext::Create(this);
-
- // Check if cached result can be used
- auto bg_texture_it =
- render_pass_backdrop_textures_.find(quad->render_pass_id);
- if (bg_texture_it != render_pass_backdrop_textures_.end()) {
- if (!quad->intersects_damage_under)
- return bg_texture_it->second;
- else
- render_pass_backdrop_textures_.erase(bg_texture_it);
- }
-
- gfx::Vector2d clipping_offset =
- (params->background_rect.top_right() - unclipped_rect.top_right()) +
- (params->background_rect.bottom_left() - unclipped_rect.bottom_left());
-
- gfx::Rect quality_adjusted_rect = ScaleToEnclosingRect(
- params->background_rect, params->backdrop_filter_quality);
-
- // When backdrop_filter_quality is less than 1.0f, scale the blur amount
- // accordingly.
- cc::FilterOperations filter_operations;
- if (params->backdrop_filter_quality < 1.0f) {
- for (const cc::FilterOperation& op :
- params->backdrop_filters->operations()) {
- if (op.type() == cc::FilterOperation::BLUR) {
- cc::FilterOperation blur_op(op);
- blur_op.set_amount(op.amount() * params->backdrop_filter_quality);
- filter_operations.Append(blur_op);
- } else {
- filter_operations.Append(op);
- }
- }
- }
- const cc::FilterOperations& filters = params->backdrop_filter_quality < 1.0f
- ? filter_operations
- : *params->backdrop_filters;
-
- auto paint_filter = cc::RenderSurfaceFilters::BuildImageFilter(
- filters, gfx::SizeF(quality_adjusted_rect.size()),
- gfx::Vector2dF(clipping_offset));
-
- // TODO(senorblanco): background filters should be moved to the
- // makeWithFilter fast-path, and go back to calling ApplyImageFilter().
- // See http://crbug.com/613233.
- if (!paint_filter || !use_gr_context)
- return nullptr;
-
- auto filter = paint_filter->cached_sk_filter_;
- sk_sp<SkImage> src_image = WrapTexture(
- params->background_texture, GL_TEXTURE_2D, quality_adjusted_rect.size(),
- use_gr_context->context(), /*flip_texture=*/true,
- GlFormatToSkFormat(params->background_texture_format),
- /*adopt_texture=*/false);
- if (!src_image) {
- TRACE_EVENT_INSTANT0("cc",
- "ApplyBackdropFilters wrap background texture failed",
- TRACE_EVENT_SCOPE_THREAD);
- return nullptr;
- }
-
- // Create surface to draw into.
- SkImageInfo dst_info = SkImageInfo::MakeN32Premul(
- quality_adjusted_rect.width(), quality_adjusted_rect.height());
- sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
- use_gr_context->context(), SkBudgeted::kYes, dst_info);
- if (!surface) {
- TRACE_EVENT_INSTANT0("viz",
- "ApplyBackdropFilters surface allocation failed",
- TRACE_EVENT_SCOPE_THREAD);
- return nullptr;
- }
-
- // Big filters can sometimes fallback to CPU. Therefore, we need
- // to disable subnormal floats for performance and security reasons.
- cc::ScopedSubnormalFloatDisabler disabler;
-
- gfx::RectF src_image_rect =
- gfx::RectF(quality_adjusted_rect.width(), quality_adjusted_rect.height());
- SkRect dest_rect = RectToSkRect(gfx::Rect(quality_adjusted_rect.size()));
-
- // If the content underneath the backdrop filter can be exposed because of
- // blending or bounds, paint the backdrop at full opacity first. The
- // backdrop-filtered content will not be blended with the backdrop later, it
- // will be rastered over the top. So we need to paint it here, unfiltered.
- if (backdrop_filter_bounds.has_value() || quad->ShouldDrawWithBlending()) {
- surface->getCanvas()->drawImageRect(
- src_image, RectFToSkRect(src_image_rect), dest_rect,
- SkSamplingOptions(), nullptr, SkCanvas::kStrict_SrcRectConstraint);
- }
-
- if (backdrop_filter_bounds.has_value()) {
- // Crop the source image to the backdrop_filter_bounds.
- gfx::Rect filter_clip = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect(
- backdrop_filter_bounds_transform, backdrop_filter_bounds->rect()));
- gfx::Rect src_rect(params->background_rect.width(),
- params->background_rect.height());
- filter_clip.Intersect(src_rect);
- if (filter_clip.IsEmpty())
- return FinalizeImage(surface);
- if (filter_clip != src_rect) {
- filter_clip = gfx::ScaleToEnclosingRect(filter_clip,
- params->backdrop_filter_quality);
- src_image = src_image->makeSubset(RectToSkIRect(filter_clip),
- use_gr_context->context());
- src_image_rect = gfx::RectF(filter_clip.width(), filter_clip.height());
- dest_rect = RectToSkRect(filter_clip);
- }
- }
-
- SkIPoint offset;
- SkIRect subset;
- sk_sp<SkImage> filtered_image = SkiaHelper::ApplyImageFilter(
- use_gr_context->context(), src_image, src_image_rect, src_image_rect,
- quad->filters_scale, std::move(filter), &offset, &subset,
- quad->filters_origin, true);
-
- // Clip the filtered image to the (rounded) bounding box of the element.
- if (backdrop_filter_bounds.has_value()) {
- surface->getCanvas()->save();
- gfx::RRectF clip_rect(backdrop_filter_bounds.value());
- surface->getCanvas()->setMatrix(
- backdrop_filter_bounds_transform.matrix().asM33());
- surface->getCanvas()->clipRRect(SkRRect(clip_rect), SkClipOp::kIntersect,
- true /* antialias */);
- surface->getCanvas()->resetMatrix();
- }
-
- SkPaint paint;
- // Paint the filtered backdrop image with opacity.
- if (quad->shared_quad_state->opacity < 1.0) {
- paint.setImageFilter(
- SkiaHelper::BuildOpacityFilter(quad->shared_quad_state->opacity));
- }
- // Now paint the pre-filtered image onto the canvas (possibly with mask
- // applied).
- surface->getCanvas()->drawImageRect(filtered_image, SkRect::Make(subset),
- dest_rect, SkSamplingOptions(), &paint,
- SkCanvas::kStrict_SrcRectConstraint);
-
- if (backdrop_filter_bounds.has_value()) {
- surface->getCanvas()->restore();
- }
-
- sk_sp<SkImage> filtered_image_texture = FinalizeImage(surface);
- if (!quad->intersects_damage_under) {
- render_pass_backdrop_textures_[params->quad->render_pass_id] =
- filtered_image_texture;
- }
- return filtered_image_texture;
-}
-
-const DrawQuad* GLRenderer::CanPassBeDrawnDirectly(
- const AggregatedRenderPass* pass) {
-#if BUILDFLAG(IS_APPLE)
- // On Macs, this path can sometimes lead to all black output.
- // TODO(enne): investigate this and remove this hack.
- return nullptr;
-#else
- // Can only collapse a single tile quad.
- if (pass->quad_list.size() != 1)
- return nullptr;
-
- const DrawQuad* quad = *pass->quad_list.BackToFrontBegin();
- // Hack: this could be supported by concatenating transforms, but
- // in practice if there is one quad, it is at the origin of the render pass
- // and has the same size as the pass.
- if (!quad->shared_quad_state->quad_to_target_transform.IsIdentity() ||
- quad->rect != pass->output_rect)
- return nullptr;
- // The quad is expected to be the entire layer so that AA edges are correct.
- if (quad->shared_quad_state->quad_layer_rect != quad->rect)
- return nullptr;
- if (quad->material != DrawQuad::Material::kTiledContent)
- return nullptr;
-
- // TODO(chrishtr): support could be added for opacity, but care needs
- // to be taken to make sure it is correct w.r.t. non-commutative filters etc.
- if (quad->shared_quad_state->opacity != 1.0f)
- return nullptr;
-
- if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver)
- return nullptr;
-
- const TileDrawQuad* tile_quad = TileDrawQuad::MaterialCast(quad);
- // Hack: this could be supported by passing in a subrectangle to draw
- // render pass, although in practice if there is only one quad there
- // will be no border texels on the input.
- if (tile_quad->tex_coord_rect != gfx::RectF(tile_quad->rect))
- return nullptr;
- // Tile quad features not supported in render pass shaders.
- if (tile_quad->nearest_neighbor)
- return nullptr;
- // BUG=skia:3868, Skia currently doesn't support texture rectangle inputs.
- // See also the DCHECKs about GL_TEXTURE_2D in DrawRenderPassQuad.
- GLenum target =
- resource_provider()->GetResourceTextureTarget(tile_quad->resource_id());
- if (target != GL_TEXTURE_2D)
- return nullptr;
-
- return tile_quad;
-#endif
-}
-
-void GLRenderer::DrawRenderPassQuad(const AggregatedRenderPassDrawQuad* quad,
- const gfx::QuadF* clip_region) {
- auto bypass = render_pass_bypass_quads_.find(quad->render_pass_id);
- DrawRenderPassDrawQuadParams params;
- params.quad = quad;
- params.clip_region = clip_region;
- params.window_matrix = current_frame()->window_matrix;
- params.projection_matrix = current_frame()->projection_matrix;
- params.tex_coord_rect = quad->tex_coord_rect;
- ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_,
- &timer_queries_, "kRenderPassDrawQuad");
- if (bypass != render_pass_bypass_quads_.end()) {
- DCHECK(bypass->second->material == DrawQuad::Material::kTiledContent);
- const TileDrawQuad* tile_quad = TileDrawQuad::MaterialCast(bypass->second);
- // The projection matrix used by GLRenderer has a flip. As tile texture
- // inputs are oriented opposite to framebuffer outputs, don't flip via
- // texture coords and let the projection matrix naturallyd o it.
- params.flip_texture = false;
- params.bypass_quad_texture.resource_id = tile_quad->resource_id();
- params.bypass_quad_texture.size = tile_quad->texture_size;
- DrawRenderPassQuadInternal(&params);
- } else {
- auto contents_texture_it = render_pass_textures_.find(quad->render_pass_id);
- DCHECK(contents_texture_it->second.id());
- // See above comments about texture flipping. When the input is a
- // render pass, it needs to an extra flip to be oriented correctly.
- params.flip_texture = true;
- params.contents_texture = &contents_texture_it->second;
- DrawRenderPassQuadInternal(&params);
- }
-
- if (params.background_texture) {
- gl_->DeleteTextures(1, &params.background_texture);
- params.background_texture = 0;
- }
-}
-
-void GLRenderer::DrawRenderPassQuadInternal(
- DrawRenderPassDrawQuadParams* params) {
- params->quad_to_target_transform =
- params->quad->shared_quad_state->quad_to_target_transform;
- if (!InitializeRPDQParameters(params))
- return;
-
- UpdateRPDQShadersForBlending(params);
- bool can_draw = UpdateRPDQWithSkiaFilters(params);
- // The above calls use ScopedUseGrContext which can change the bound
- // framebuffer, so we need to restore it for the current RenderPass.
- UseRenderPass(current_frame()->current_render_pass);
- // As part of restoring the framebuffer, we call SetViewport directly, rather
- // than through PrepareSurfaceForPass. PrepareSurfaceForPass also clears the
- // surface, which is not desired when restoring.
- SetViewport();
-
- if (!can_draw)
- return;
-
- UpdateRPDQTexturesForSampling(params);
- UpdateRPDQBlendMode(params);
- ChooseRPDQProgram(params, CurrentRenderPassColorSpace());
- UpdateRPDQUniforms(params);
- DrawRPDQ(*params);
-
- AccumulateDrawRects(params->quad->visible_rect,
- params->quad->shared_quad_state->quad_to_target_transform,
- &drawn_rects_);
-}
-
-bool GLRenderer::InitializeRPDQParameters(
- DrawRenderPassDrawQuadParams* params) {
- DCHECK(params);
- const auto* quad = params->quad.get();
- SkMatrix local_matrix;
- local_matrix.setTranslate(quad->filters_origin.x(), quad->filters_origin.y());
- local_matrix.postScale(quad->filters_scale.x(), quad->filters_scale.y());
- params->filters = FiltersForPass(quad->render_pass_id);
- params->backdrop_filters = BackdropFiltersForPass(quad->render_pass_id);
- if (ShouldApplyBackdropFilters(params)) {
- params->backdrop_filter_bounds =
- BackdropFilterBoundsForPass(quad->render_pass_id);
- if (params->backdrop_filter_bounds.has_value()) {
- params->backdrop_filter_bounds->Scale(quad->filters_scale.x(),
- quad->filters_scale.y());
- }
- } else {
- params->backdrop_filter_bounds.reset();
- }
- params->backdrop_filter_quality = quad->backdrop_filter_quality;
- gfx::Rect dst_rect = params->filters
- ? params->filters->MapRect(quad->rect, local_matrix)
- : quad->rect;
- params->dst_rect.SetRect(static_cast<float>(dst_rect.x()),
- static_cast<float>(dst_rect.y()),
- static_cast<float>(dst_rect.width()),
- static_cast<float>(dst_rect.height()));
- gfx::Transform quad_rect_matrix;
- gfx::Rect quad_layer_rect(quad->shared_quad_state->quad_layer_rect);
- if (params->filters)
- quad_layer_rect = params->filters->MapRect(quad_layer_rect, local_matrix);
- QuadRectTransform(&quad_rect_matrix, params->quad_to_target_transform,
- gfx::RectF(quad_layer_rect));
- params->contents_device_transform =
- params->window_matrix * params->projection_matrix * quad_rect_matrix;
- params->contents_device_transform.FlattenTo2d();
-
- // Can only draw surface if device matrix is invertible.
- if (!params->contents_device_transform.IsInvertible())
- return false;
-
- // TODO(sunxd): unify the anti-aliasing logic of RPDQ and TileDrawQuad.
- params->surface_quad = SharedGeometryQuad();
- gfx::QuadF device_layer_quad;
- if (settings_->allow_antialiasing && !quad->force_anti_aliasing_off &&
- quad->IsEdge()) {
- bool clipped = false;
- device_layer_quad = cc::MathUtil::MapQuad(params->contents_device_transform,
- params->surface_quad, &clipped);
- params->use_aa = ShouldAntialiasQuad(device_layer_quad, clipped,
- settings_->force_antialiasing);
- }
-
- const gfx::QuadF* aa_quad = params->use_aa ? &device_layer_quad : nullptr;
- SetupRenderPassQuadForClippingAndAntialiasing(
- params->contents_device_transform, quad, aa_quad, params->clip_region,
- &params->surface_quad, params->edge);
-
- return true;
-}
-
-// Get a GL texture id from an SkImage. An optional origin pointer can be
-// passed in which will be filled out with the origin for the texture
-// backing the SkImage.
-static GLuint GetGLTextureIDFromSkImage(const SkImage* image,
- GrSurfaceOrigin* origin = nullptr) {
- GrBackendTexture backend_texture = image->getBackendTexture(true, origin);
- if (!backend_texture.isValid()) {
- return 0;
- }
- GrGLTextureInfo info;
- bool result = backend_texture.getGLTextureInfo(&info);
- DCHECK(result);
- return info.fID;
-}
-
-void GLRenderer::UpdateRPDQShadersForBlending(
- DrawRenderPassDrawQuadParams* params) {
- const auto* quad = params->quad.get();
- params->blend_mode = quad->shared_quad_state->blend_mode;
- params->use_shaders_for_blending =
- !CanApplyBlendModeUsingBlendFunc(params->blend_mode) ||
- ShouldApplyBackdropFilters(params) ||
- settings_->force_blending_with_shaders;
-
- if (params->use_shaders_for_blending) {
- // Compute a bounding box around the pixels that will be visible through
- // the quad.
- absl::optional<gfx::RRectF> backdrop_filter_bounds;
- gfx::Transform backdrop_filter_bounds_transform;
- gfx::Rect unclipped_rect;
- params->background_rect = GetBackdropBoundingBoxForRenderPassQuad(
- params, &backdrop_filter_bounds_transform, &backdrop_filter_bounds,
- &unclipped_rect);
-
- if (!params->background_rect.IsEmpty()) {
- // The pixels from the filtered background should completely replace the
- // current pixel values.
- if (blend_enabled())
- SetBlendEnabled(false);
-
- // Read the pixels in the bounding box into a buffer R.
- // This function allocates a texture, which should contribute to the
- // amount of memory used by render surfaces:
- // LayerTreeHost::CalculateMemoryForRenderSurfaces.
- const auto& operations = params->backdrop_filters->operations();
- DCHECK(params->backdrop_filter_quality == 1.0f ||
- (operations.size() == 1 &&
- operations.front().type() == cc::FilterOperation::BLUR));
- params->background_texture = GetBackdropTexture(
- params->background_rect, params->backdrop_filter_quality,
- &params->background_texture_format);
-
- if (ShouldApplyBackdropFilters(params)) {
- // Apply the background filters to R, so that it is applied in the
- // pixels' coordinate space.
- params->background_image =
- ApplyBackdropFilters(params, unclipped_rect, backdrop_filter_bounds,
- backdrop_filter_bounds_transform);
- if (params->background_image) {
- params->background_image_id =
- GetGLTextureIDFromSkImage(params->background_image.get());
- DCHECK(params->background_image_id || IsContextLost());
- }
- }
- if (params->background_image_id) {
- // Reset original background texture if there is not any mask.
- if (!quad->mask_resource_id()) {
- gl_->DeleteTextures(1, &params->background_texture);
- params->background_texture = 0;
- }
- } else if (CanApplyBlendModeUsingBlendFunc(params->blend_mode) &&
- ShouldApplyBackdropFilters(params)) {
- // Something went wrong with applying backdrop filters to the
- // backdrop.
- params->use_shaders_for_blending = false;
- gl_->DeleteTextures(1, &params->background_texture);
- params->background_texture = 0;
- }
- } else { // params->background_rect.IsEmpty()
- DCHECK(!params->background_image_id);
- params->use_shaders_for_blending = false;
- params->blend_mode = SkBlendMode::kSrcOver;
- }
- }
-
- // Need original background texture for mask?
- params->mask_for_background =
- params->background_texture && // Have original background texture
- params->background_image_id; // Have mask texture
- // If we have background texture + background image, then we also have mask
- // resource.
- if (params->background_texture && params->background_image_id) {
- DCHECK(params->mask_for_background);
- DCHECK(quad->mask_resource_id());
- }
-
- DCHECK_EQ(params->background_texture || params->background_image_id,
- params->use_shaders_for_blending);
-}
-
-bool GLRenderer::UpdateRPDQWithSkiaFilters(
- DrawRenderPassDrawQuadParams* params) {
- const auto* quad = params->quad.get();
- // Apply filters to the contents texture.
- if (params->filters) {
- DCHECK(!params->filters->IsEmpty());
- gfx::Size size = params->contents_texture
- ? params->contents_texture->size()
- : params->bypass_quad_texture.size;
- auto paint_filter = cc::RenderSurfaceFilters::BuildImageFilter(
- *params->filters, gfx::SizeF(size));
- auto filter = paint_filter ? paint_filter->cached_sk_filter_ : nullptr;
- if (filter) {
- SkColorFilter* colorfilter_rawptr = nullptr;
- filter->asColorFilter(&colorfilter_rawptr);
- sk_sp<SkColorFilter> cf(colorfilter_rawptr);
-
- if (cf && cf->asAColorMatrix(params->color_matrix)) {
- // We have a color matrix at the root of the filter DAG; apply it
- // locally in the compositor and process the rest of the DAG (if any)
- // in Skia.
- params->use_color_matrix = true;
- filter = sk_ref_sp(filter->getInput(0));
- }
- if (filter) {
- gfx::Rect clip_rect =
- quad->shared_quad_state->clip_rect.value_or(current_draw_rect_);
- gfx::Transform transform = params->quad_to_target_transform;
- transform.FlattenTo2d();
- if (!transform.IsInvertible()) {
- return false;
- }
- // If the transform has perspective, there might be visible content
- // outside of the bounds of the quad.
- if (!transform.HasPerspective()) {
- gfx::QuadF clip_quad = gfx::QuadF(gfx::RectF(clip_rect));
- gfx::QuadF local_clip =
- cc::MathUtil::InverseMapQuadToLocalSpace(transform, clip_quad);
- params->dst_rect.Intersect(local_clip.BoundingBox());
- }
- // If we've been fully clipped out (by crop rect or clipping), there's
- // nothing to draw.
- if (params->dst_rect.IsEmpty()) {
- return false;
- }
- SkIPoint offset;
- SkIRect subset;
- gfx::RectF src_rect(quad->rect);
- auto use_gr_context = ScopedUseGrContext::Create(this);
- if (!use_gr_context)
- return false;
-
- if (params->contents_texture) {
- params->contents_and_bypass_color_space =
- params->contents_texture->color_space();
- sk_sp<SkImage> src_image = WrapTexture(
- params->contents_texture->id(), GL_TEXTURE_2D,
- params->contents_texture->size(), use_gr_context->context(),
- params->flip_texture, kN32_SkColorType, /*adopt_texture=*/false);
- params->filter_image = SkiaHelper::ApplyImageFilter(
- use_gr_context->context(), src_image, src_rect, params->dst_rect,
- quad->filters_scale, std::move(filter), &offset, &subset,
- quad->filters_origin, true);
- } else {
- DisplayResourceProviderGL::ScopedReadLockGL
- prefilter_bypass_quad_texture_lock(
- resource_provider(), params->bypass_quad_texture.resource_id);
- params->contents_and_bypass_color_space =
- prefilter_bypass_quad_texture_lock.color_space();
- sk_sp<SkImage> src_image =
- WrapTexture(prefilter_bypass_quad_texture_lock.texture_id(),
- prefilter_bypass_quad_texture_lock.target(),
- prefilter_bypass_quad_texture_lock.size(),
- use_gr_context->context(), params->flip_texture,
- kN32_SkColorType, /*adopt_texture=*/false);
- params->filter_image = SkiaHelper::ApplyImageFilter(
- use_gr_context->context(), src_image, src_rect, params->dst_rect,
- quad->filters_scale, std::move(filter), &offset, &subset,
- quad->filters_origin, true);
- }
-
- if (!params->filter_image)
- return false;
- params->dst_rect =
- gfx::RectF(src_rect.x() + offset.fX, src_rect.y() + offset.fY,
- subset.width(), subset.height());
- gfx::RectF tex_rect = gfx::RectF(gfx::PointF(subset.x(), subset.y()),
- params->dst_rect.size());
- params->tex_coord_rect = tex_rect;
- }
- }
- }
- return true;
-}
-
-void GLRenderer::UpdateRPDQTexturesForSampling(
- DrawRenderPassDrawQuadParams* params) {
- if (params->quad->mask_resource_id()) {
- params->mask_resource_lock =
- std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>(
-
- resource_provider(), params->quad->mask_resource_id(), GL_TEXTURE1,
- GL_LINEAR);
- }
-
- if (params->filter_image) {
- GrSurfaceOrigin origin;
- GLuint filter_image_id =
- GetGLTextureIDFromSkImage(params->filter_image.get(), &origin);
- DCHECK(filter_image_id || IsContextLost());
- DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_));
- gl_->BindTexture(GL_TEXTURE_2D, filter_image_id);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- // |params->contents_and_bypass_color_space| was populated when
- // |params->filter_image| was populated.
- params->source_needs_flip = kBottomLeft_GrSurfaceOrigin == origin;
- } else if (params->contents_texture) {
- params->contents_texture->BindForSampling();
- params->contents_and_bypass_color_space =
- params->contents_texture->color_space();
- params->source_needs_flip = params->flip_texture;
- } else {
- params->bypass_quad_resource_lock =
- std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>(
- resource_provider(), params->bypass_quad_texture.resource_id,
- GL_LINEAR);
- DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
- params->bypass_quad_resource_lock->target());
- params->contents_and_bypass_color_space =
- params->bypass_quad_resource_lock->color_space();
- params->source_needs_flip = params->flip_texture;
- }
-}
-
-void GLRenderer::UpdateRPDQBlendMode(DrawRenderPassDrawQuadParams* params) {
- SkBlendMode blend_mode = params->blend_mode;
- SetBlendEnabled((!params->use_shaders_for_blending &&
- (params->quad->ShouldDrawWithBlending() ||
- !IsDefaultBlendMode(blend_mode))) ||
- ShouldApplyRoundedCorner(params->quad));
- if (!params->use_shaders_for_blending) {
- if (!use_blend_equation_advanced_coherent_ && use_blend_equation_advanced_)
- gl_->BlendBarrierKHR();
-
- ApplyBlendModeUsingBlendFunc(blend_mode);
- }
-}
-
-void GLRenderer::ChooseRPDQProgram(DrawRenderPassDrawQuadParams* params,
- const gfx::ColorSpace& target_color_space) {
- TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
- gl_, &highp_threshold_cache_, settings_->highp_threshold_min,
- params->quad->shared_quad_state->visible_quad_layer_rect.size());
-
- BlendMode shader_blend_mode =
- params->use_shaders_for_blending
- ? BlendModeFromSkXfermode(params->blend_mode)
- : BLEND_MODE_NONE;
-
- SamplerType sampler_type = SAMPLER_TYPE_2D;
- MaskMode mask_mode = NO_MASK;
- bool mask_for_background = params->mask_for_background;
- if (params->mask_resource_lock) {
- mask_mode = HAS_MASK;
- sampler_type =
- SamplerTypeFromTextureTarget(params->mask_resource_lock->target());
- }
- SetUseProgram(
- ProgramKey::RenderPass(
- tex_coord_precision, sampler_type, shader_blend_mode,
- params->use_aa ? USE_AA : NO_AA, mask_mode, mask_for_background,
- params->use_color_matrix, tint_gl_composited_content_,
- params->apply_shader_based_rounded_corner &&
- ShouldApplyRoundedCorner(params->quad)),
- params->contents_and_bypass_color_space, target_color_space);
-}
-
-void GLRenderer::UpdateRPDQUniforms(DrawRenderPassDrawQuadParams* params) {
- gfx::RectF tex_rect = params->tex_coord_rect;
-
- gfx::Size texture_size;
- if (params->filter_image) {
- texture_size.set_width(params->filter_image->width());
- texture_size.set_height(params->filter_image->height());
- } else if (params->contents_texture) {
- texture_size = params->contents_texture->size();
- } else {
- texture_size = params->bypass_quad_texture.size;
- }
-
- tex_rect.Scale(1.0f / texture_size.width(), 1.0f / texture_size.height());
-
- DCHECK(current_program_->vertex_tex_transform_location() != -1 ||
- IsContextLost());
- if (params->source_needs_flip) {
- // Flip the content vertically in the shader, as the RenderPass input
- // texture is already oriented the same way as the framebuffer, but the
- // projection transform does a flip.
- gl_->Uniform4f(current_program_->vertex_tex_transform_location(),
- tex_rect.x(), 1.0f - tex_rect.y(), tex_rect.width(),
- -tex_rect.height());
- } else {
- // Tile textures are oriented opposite the framebuffer, so can use
- // the projection transform to do the flip.
- gl_->Uniform4f(current_program_->vertex_tex_transform_location(),
- tex_rect.x(), tex_rect.y(), tex_rect.width(),
- tex_rect.height());
- }
-
- GLint last_texture_unit = 0;
- if (current_program_->mask_sampler_location() != -1) {
- DCHECK(params->mask_resource_lock);
- DCHECK_NE(current_program_->mask_tex_coord_scale_location(), 1);
- DCHECK_NE(current_program_->mask_tex_coord_offset_location(), 1);
- gl_->Uniform1i(current_program_->mask_sampler_location(), 1);
-
- gfx::RectF mask_uv_rect = params->quad->mask_uv_rect;
- if (SamplerTypeFromTextureTarget(params->mask_resource_lock->target()) !=
- SAMPLER_TYPE_2D) {
- mask_uv_rect.Scale(params->quad->mask_texture_size.width(),
- params->quad->mask_texture_size.height());
- }
-
- SkMatrix tex_to_mask = SkMatrix::RectToRect(RectFToSkRect(tex_rect),
- RectFToSkRect(mask_uv_rect));
-
- if (params->source_needs_flip) {
- // Mask textures are oriented vertically flipped relative to the
- // framebuffer and the RenderPass contents texture, so we flip the tex
- // coords from the RenderPass texture to find the mask texture coords.
- tex_to_mask.preTranslate(0, 1);
- tex_to_mask.preScale(1, -1);
- }
-
- gl_->Uniform2f(current_program_->mask_tex_coord_offset_location(),
- tex_to_mask.getTranslateX(), tex_to_mask.getTranslateY());
- gl_->Uniform2f(current_program_->mask_tex_coord_scale_location(),
- tex_to_mask.getScaleX(), tex_to_mask.getScaleY());
- last_texture_unit = 1;
- }
-
- if (current_program_->edge_location() != -1)
- gl_->Uniform3fv(current_program_->edge_location(), 8, params->edge);
-
- if (current_program_->color_matrix_location() != -1) {
- float matrix[16];
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < 4; ++j)
- matrix[i * 4 + j] = SkScalarToFloat(params->color_matrix[j * 5 + i]);
- }
- gl_->UniformMatrix4fv(current_program_->color_matrix_location(), 1, false,
- matrix);
- }
-
- if (current_program_->color_offset_location() != -1) {
- float offset[4];
- for (int i = 0; i < 4; ++i)
- offset[i] = params->color_matrix[i * 5 + 4];
-
- gl_->Uniform4fv(current_program_->color_offset_location(), 1, offset);
- }
-
- if (current_program_->tint_color_matrix_location() != -1) {
- auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix();
- gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1,
- false, matrix.data());
- }
-
- if (current_program_->backdrop_location() != -1) {
- DCHECK(params->background_texture || params->background_image_id);
- DCHECK_NE(current_program_->backdrop_location(), 0);
- DCHECK_NE(current_program_->backdrop_rect_location(), 0);
-
- ++last_texture_unit;
- gl_->Uniform1i(current_program_->backdrop_location(), last_texture_unit);
-
- gl_->Uniform4f(current_program_->backdrop_rect_location(),
- params->background_rect.x(), params->background_rect.y(),
- 1.0f / params->background_rect.width(),
- 1.0f / params->background_rect.height());
-
- // Either |background_image_id| or |background_texture| will be the
- // |backdrop_location| in the shader.
- if (params->background_image_id) {
- gl_->ActiveTexture(GL_TEXTURE0 + last_texture_unit);
- gl_->BindTexture(GL_TEXTURE_2D, params->background_image_id);
- if (params->backdrop_filter_quality != 1.0f)
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl_->ActiveTexture(GL_TEXTURE0);
- }
- // If |mask_for_background| then we have both |background_image_id| and
- // |background_texture|, and the latter will be the
- // |original_backdrop_location| in the shader.
- if (params->mask_for_background) {
- DCHECK(params->background_image_id);
- DCHECK(params->background_texture);
- ++last_texture_unit;
- gl_->Uniform1i(current_program_->original_backdrop_location(),
- last_texture_unit);
- }
- if (params->background_texture) {
- gl_->ActiveTexture(GL_TEXTURE0 + last_texture_unit);
- gl_->BindTexture(GL_TEXTURE_2D, params->background_texture);
- gl_->ActiveTexture(GL_TEXTURE0);
- }
- }
-
- SetShaderOpacity(params->quad->shared_quad_state->opacity);
- if (current_program_->rounded_corner_rect_location() != -1) {
- SetShaderRoundedCorner(params->quad->shared_quad_state->mask_filter_info
- .rounded_corner_bounds(),
- params->window_matrix * params->projection_matrix);
- }
- SetShaderQuadF(params->surface_quad);
-}
-
-void GLRenderer::DrawRPDQ(const DrawRenderPassDrawQuadParams& params) {
- DrawQuadGeometry(params.projection_matrix, params.quad_to_target_transform,
- params.dst_rect);
-
- // Flush the compositor context before the filter bitmap goes out of
- // scope, so the draw gets processed before the filter texture gets deleted.
- if (params.filter_image)
- gl_->Flush();
-
- if (!params.use_shaders_for_blending)
- RestoreBlendFuncToDefault(params.blend_mode);
-}
-
-namespace {
-// These functions determine if a quad, clipped by a clip_region contains
-// the entire {top|bottom|left|right} edge.
-bool is_top(const gfx::QuadF* clip_region, const DrawQuad* quad) {
- if (!quad->IsTopEdge())
- return false;
- if (!clip_region)
- return true;
-
- return std::abs(clip_region->p1().y()) < kAntiAliasingEpsilon &&
- std::abs(clip_region->p2().y()) < kAntiAliasingEpsilon;
-}
-
-bool is_bottom(const gfx::QuadF* clip_region, const DrawQuad* quad) {
- if (!quad->IsBottomEdge())
- return false;
- if (!clip_region)
- return true;
-
- return std::abs(clip_region->p3().y() -
- quad->shared_quad_state->quad_layer_rect.height()) <
- kAntiAliasingEpsilon &&
- std::abs(clip_region->p4().y() -
- quad->shared_quad_state->quad_layer_rect.height()) <
- kAntiAliasingEpsilon;
-}
-
-bool is_left(const gfx::QuadF* clip_region, const DrawQuad* quad) {
- if (!quad->IsLeftEdge())
- return false;
- if (!clip_region)
- return true;
-
- return std::abs(clip_region->p1().x()) < kAntiAliasingEpsilon &&
- std::abs(clip_region->p4().x()) < kAntiAliasingEpsilon;
-}
-
-bool is_right(const gfx::QuadF* clip_region, const DrawQuad* quad) {
- if (!quad->IsRightEdge())
- return false;
- if (!clip_region)
- return true;
-
- return std::abs(clip_region->p2().x() -
- quad->shared_quad_state->quad_layer_rect.width()) <
- kAntiAliasingEpsilon &&
- std::abs(clip_region->p3().x() -
- quad->shared_quad_state->quad_layer_rect.width()) <
- kAntiAliasingEpsilon;
-}
-} // anonymous namespace
-
-static gfx::QuadF GetDeviceQuadWithAntialiasingOnExteriorEdges(
- const LayerQuad& device_layer_edges,
- const gfx::Transform& device_transform,
- const gfx::QuadF& tile_quad,
- const gfx::QuadF* clip_region,
- const DrawQuad* quad) {
- auto tile_rect = gfx::RectF(quad->visible_rect);
-
- gfx::PointF bottom_right = tile_quad.p3();
- gfx::PointF bottom_left = tile_quad.p4();
- gfx::PointF top_left = tile_quad.p1();
- gfx::PointF top_right = tile_quad.p2();
- bool clipped = false;
-
- // Map points to device space. We ignore |clipped|, since the result of
- // |MapPoint()| still produces a valid point to draw the quad with. When
- // clipped, the point will be outside of the viewport. See crbug.com/416367.
- bottom_right =
- cc::MathUtil::MapPoint(device_transform, bottom_right, &clipped);
- bottom_left = cc::MathUtil::MapPoint(device_transform, bottom_left, &clipped);
- top_left = cc::MathUtil::MapPoint(device_transform, top_left, &clipped);
- top_right = cc::MathUtil::MapPoint(device_transform, top_right, &clipped);
-
- LayerQuad::Edge bottom_edge(bottom_right, bottom_left);
- LayerQuad::Edge left_edge(bottom_left, top_left);
- LayerQuad::Edge top_edge(top_left, top_right);
- LayerQuad::Edge right_edge(top_right, bottom_right);
-
- // Only apply anti-aliasing to edges not clipped by culling or scissoring.
- // If an edge is degenerate we do not want to replace it with a "proper" edge
- // as that will cause the quad to possibly expand in strange ways.
- if (!top_edge.degenerate() && is_top(clip_region, quad) &&
- tile_rect.y() == quad->rect.y()) {
- top_edge = device_layer_edges.top();
- }
- if (!left_edge.degenerate() && is_left(clip_region, quad) &&
- tile_rect.x() == quad->rect.x()) {
- left_edge = device_layer_edges.left();
- }
- if (!right_edge.degenerate() && is_right(clip_region, quad) &&
- tile_rect.right() == quad->rect.right()) {
- right_edge = device_layer_edges.right();
- }
- if (!bottom_edge.degenerate() && is_bottom(clip_region, quad) &&
- tile_rect.bottom() == quad->rect.bottom()) {
- bottom_edge = device_layer_edges.bottom();
- }
-
- float sign = tile_quad.IsCounterClockwise() ? -1 : 1;
- bottom_edge.scale(sign);
- left_edge.scale(sign);
- top_edge.scale(sign);
- right_edge.scale(sign);
-
- // Create device space quad.
- return LayerQuad(left_edge, top_edge, right_edge, bottom_edge).ToQuadF();
-}
-
-float GetTotalQuadError(const gfx::QuadF* clipped_quad,
- const gfx::QuadF* ideal_rect) {
- return (clipped_quad->p1() - ideal_rect->p1()).LengthSquared() +
- (clipped_quad->p2() - ideal_rect->p2()).LengthSquared() +
- (clipped_quad->p3() - ideal_rect->p3()).LengthSquared() +
- (clipped_quad->p4() - ideal_rect->p4()).LengthSquared();
-}
-
-// Attempt to rotate the clipped quad until it lines up the most
-// correctly. This is necessary because we check the edges of this
-// quad against the expected left/right/top/bottom for anti-aliasing.
-void AlignQuadToBoundingBox(gfx::QuadF* clipped_quad) {
- auto bounding_quad = gfx::QuadF(clipped_quad->BoundingBox());
- gfx::QuadF best_rotation = *clipped_quad;
- float least_error_amount = GetTotalQuadError(clipped_quad, &bounding_quad);
- for (size_t i = 1; i < 4; ++i) {
- clipped_quad->Realign(1);
- float new_error = GetTotalQuadError(clipped_quad, &bounding_quad);
- if (new_error < least_error_amount) {
- least_error_amount = new_error;
- best_rotation = *clipped_quad;
- }
- }
- *clipped_quad = best_rotation;
-}
-
-void InflateAntiAliasingDistances(const gfx::QuadF& quad,
- LayerQuad* device_layer_edges,
- float edge[24]) {
- DCHECK(!quad.BoundingBox().IsEmpty());
- LayerQuad device_layer_bounds(gfx::QuadF(quad.BoundingBox()));
-
- device_layer_edges->InflateAntiAliasingDistance();
- device_layer_edges->ToFloatArray(edge);
-
- device_layer_bounds.InflateAntiAliasingDistance();
- device_layer_bounds.ToFloatArray(&edge[12]);
-}
-
-// static
-bool GLRenderer::ShouldAntialiasQuad(const gfx::QuadF& device_layer_quad,
- bool clipped,
- bool force_aa) {
- // AAing clipped quads is not supported by the code yet.
- if (clipped)
- return false;
- if (device_layer_quad.BoundingBox().IsEmpty())
- return false;
- if (force_aa)
- return true;
-
- bool is_axis_aligned_in_target = device_layer_quad.IsRectilinear();
- bool is_nearest_rect_within_epsilon =
- is_axis_aligned_in_target &&
- gfx::IsNearestRectWithinDistance(device_layer_quad.BoundingBox(),
- kAntiAliasingEpsilon);
- return !is_nearest_rect_within_epsilon;
-}
-
-// static
-void GLRenderer::SetupQuadForClippingAndAntialiasing(
- const gfx::Transform& device_transform,
- const DrawQuad* quad,
- const gfx::QuadF* aa_quad,
- const gfx::QuadF* clip_region,
- gfx::QuadF* local_quad,
- float edge[24]) {
- gfx::QuadF rotated_clip;
- const gfx::QuadF* local_clip_region = clip_region;
- if (local_clip_region) {
- rotated_clip = *clip_region;
- AlignQuadToBoundingBox(&rotated_clip);
- local_clip_region = &rotated_clip;
- }
-
- if (!aa_quad) {
- if (local_clip_region)
- *local_quad = *local_clip_region;
- return;
- }
-
- LayerQuad device_layer_edges(*aa_quad);
- InflateAntiAliasingDistances(*aa_quad, &device_layer_edges, edge);
-
- // If we have a clip region then we are split, and therefore
- // by necessity, at least one of our edges is not an external
- // one.
- bool is_full_rect = quad->visible_rect == quad->rect;
-
- bool region_contains_all_outside_edges =
- is_full_rect &&
- (is_top(local_clip_region, quad) && is_left(local_clip_region, quad) &&
- is_bottom(local_clip_region, quad) && is_right(local_clip_region, quad));
-
- bool use_aa_on_all_four_edges =
- !local_clip_region && region_contains_all_outside_edges;
-
- gfx::QuadF device_quad;
- if (use_aa_on_all_four_edges) {
- device_quad = device_layer_edges.ToQuadF();
- } else {
- gfx::QuadF tile_quad(local_clip_region
- ? *local_clip_region
- : gfx::QuadF(gfx::RectF(quad->visible_rect)));
- device_quad = GetDeviceQuadWithAntialiasingOnExteriorEdges(
- device_layer_edges, device_transform, tile_quad, local_clip_region,
- quad);
- }
-
- *local_quad =
- cc::MathUtil::InverseMapQuadToLocalSpace(device_transform, device_quad);
-}
-
-// static
-void GLRenderer::SetupRenderPassQuadForClippingAndAntialiasing(
- const gfx::Transform& device_transform,
- const AggregatedRenderPassDrawQuad* quad,
- const gfx::QuadF* aa_quad,
- const gfx::QuadF* clip_region,
- gfx::QuadF* local_quad,
- float edge[24]) {
- gfx::QuadF rotated_clip;
- const gfx::QuadF* local_clip_region = clip_region;
- if (local_clip_region) {
- rotated_clip = *clip_region;
- AlignQuadToBoundingBox(&rotated_clip);
- local_clip_region = &rotated_clip;
- }
-
- if (!aa_quad) {
- GetScaledRegion(quad->rect, local_clip_region, local_quad);
- return;
- }
-
- LayerQuad device_layer_edges(*aa_quad);
- InflateAntiAliasingDistances(*aa_quad, &device_layer_edges, edge);
-
- gfx::QuadF device_quad;
-
- // Apply anti-aliasing only to the edges that are not being clipped
- if (local_clip_region) {
- gfx::QuadF tile_quad(gfx::RectF(quad->visible_rect));
- GetScaledRegion(quad->rect, local_clip_region, &tile_quad);
- device_quad = GetDeviceQuadWithAntialiasingOnExteriorEdges(
- device_layer_edges, device_transform, tile_quad, local_clip_region,
- quad);
- } else {
- device_quad = device_layer_edges.ToQuadF();
- }
-
- *local_quad =
- cc::MathUtil::InverseMapQuadToLocalSpace(device_transform, device_quad);
-}
-
-void GLRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad,
- const gfx::QuadF* clip_region) {
- gfx::Rect tile_rect = quad->visible_rect;
-
- SkColor color = quad->color;
- float opacity = quad->shared_quad_state->opacity;
-
- // Early out if alpha is small enough that quad doesn't contribute to output,
- // for kSrcOver blend mode.
- if (quad->shared_quad_state->blend_mode == SkBlendMode::kSrcOver) {
- float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity;
- if (alpha < std::numeric_limits<float>::epsilon() &&
- quad->ShouldDrawWithBlending() &&
- quad->shared_quad_state->blend_mode == SkBlendMode::kSrcOver)
- return;
- }
-
- gfx::Transform device_transform =
- current_frame()->window_matrix * current_frame()->projection_matrix *
- quad->shared_quad_state->quad_to_target_transform;
- device_transform.FlattenTo2d();
- if (!device_transform.IsInvertible())
- return;
-
- auto local_quad = gfx::QuadF(gfx::RectF(tile_rect));
-
- gfx::QuadF device_layer_quad;
- bool use_aa = false;
- bool allow_aa = settings_->allow_antialiasing &&
- !quad->force_anti_aliasing_off && quad->IsEdge();
-
- if (allow_aa) {
- bool clipped = false;
- bool force_aa = false;
- device_layer_quad = cc::MathUtil::MapQuad(
- device_transform,
- gfx::QuadF(
- gfx::RectF(quad->shared_quad_state->visible_quad_layer_rect)),
- &clipped);
- use_aa = ShouldAntialiasQuad(device_layer_quad, clipped, force_aa);
- }
-
- ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_,
- &timer_queries_,
- use_aa ? "kSolidColorAA" : "kSolidColor");
-
- float edge[24];
- const gfx::QuadF* aa_quad = use_aa ? &device_layer_quad : nullptr;
- SetupQuadForClippingAndAntialiasing(device_transform, quad, aa_quad,
- clip_region, &local_quad, edge);
-
- SetUseProgram(ProgramKey::SolidColor(use_aa ? USE_AA : NO_AA,
- tint_gl_composited_content_,
- ShouldApplyRoundedCorner(quad)),
- CurrentRenderPassColorSpace(), CurrentRenderPassColorSpace());
-
- gfx::ColorSpace quad_color_space = gfx::ColorSpace::CreateSRGB();
- SkColor4f color_f = SkColor4f::FromColor(color);
-
- // Apply color transform if the color space or source and target do not match.
- if (quad_color_space != CurrentRenderPassColorSpace()) {
- const gfx::ColorTransform* color_transform =
- GetColorTransform(quad_color_space, CurrentRenderPassColorSpace());
- gfx::ColorTransform::TriStim col(color_f.fR, color_f.fG, color_f.fB);
- color_transform->Transform(&col, 1);
- color_f.fR = col.x();
- color_f.fG = col.y();
- color_f.fB = col.z();
- color = color_f.toSkColor();
- }
-
- // Apply any color matrix that may be present.
- if (HasOutputColorMatrix()) {
- const SkM44& output_color_matrix = output_surface_->color_matrix();
- const SkV4 color_v{color_f.fR, color_f.fG, color_f.fB, color_f.fA};
- const SkV4 result = output_color_matrix * color_v;
- std::copy(result.ptr(), result.ptr() + 4, color_f.vec());
- color = color_f.toSkColor();
- }
-
- // Try using glClear to draw the solid color quad if possible. This is much
- // more performant than executing the shader pipeline.
- if (CanUseFastSolidColorDraw(quad) && !use_aa) {
- // Pre-multiply the alpha and opacity to get the correct blending in case of
- // transparent buffers. glClear does not have any alpha blending stage.
- Float4 result = PremultipliedColor(color, opacity);
- SkRGBA4f<kPremul_SkAlphaType> color_f_premul;
- std::copy(result.data, result.data + 4, color_f_premul.vec());
-
- gfx::RectF quad_rect_in_target_f(quad->visible_rect);
-
- device_transform.TransformRect(&quad_rect_in_target_f);
- gfx::Rect quad_rect_in_target = gfx::ToRoundedRect(quad_rect_in_target_f);
-
- // If we are using partial swap, make sure the new scissor rect is within
- // the partial swap bounds.
- if (!scissor_rect_.IsEmpty() && is_scissor_enabled_)
- quad_rect_in_target.Intersect(scissor_rect_);
-
- gl_->Enable(GL_SCISSOR_TEST);
- gl_->Scissor(quad_rect_in_target.x(), quad_rect_in_target.y(),
- quad_rect_in_target.width(), quad_rect_in_target.height());
-
- gl_->ClearColor(color_f_premul.fR, color_f_premul.fG, color_f_premul.fB,
- color_f_premul.fA);
- gl_->Clear(GL_COLOR_BUFFER_BIT);
-
- // Restore GL scissor state.
- if (is_scissor_enabled_)
- gl_->Enable(GL_SCISSOR_TEST);
- else
- gl_->Disable(GL_SCISSOR_TEST);
-
- gl_->Scissor(scissor_rect_.x(), scissor_rect_.y(), scissor_rect_.width(),
- scissor_rect_.height());
- } else {
- SetShaderColor(color, opacity);
- if (current_program_->rounded_corner_rect_location() != -1) {
- SetShaderRoundedCorner(
- quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
- current_frame()->window_matrix * current_frame()->projection_matrix);
- }
-
- if (current_program_->tint_color_matrix_location() != -1) {
- auto matrix =
- cc::DebugColors::TintCompositedContentColorTransformMatrix();
- gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1,
- false, matrix.data());
- }
-
- if (use_aa) {
- gl_->Uniform3fv(current_program_->edge_location(), 8, edge);
- }
-
- // Enable blending when the quad properties require it or if we decided
- // to use antialiasing.
- SetBlendEnabled(quad->ShouldDrawWithBlending() || use_aa);
- ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode);
-
- // Antialiasing requires a normalized quad, but this could lead to floating
- // point precision errors, so only normalize when antialiasing is on.
- if (use_aa) {
- DrawQuadGeometryWithAA(quad, &local_quad, tile_rect);
- } else {
- PrepareGeometry(SHARED_BINDING);
- SetShaderQuadF(local_quad);
- SetShaderMatrix(current_frame()->projection_matrix *
- quad->shared_quad_state->quad_to_target_transform);
- gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
- num_triangles_drawn_ += 2;
- }
- RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode);
- }
-
- // Add the quad to the region that has been drawn.
- AccumulateDrawRects(quad->visible_rect,
- quad->shared_quad_state->quad_to_target_transform,
- &drawn_rects_);
-}
-
-void GLRenderer::DrawTileQuad(const TileDrawQuad* quad,
- const gfx::QuadF* clip_region) {
- DrawContentQuad(quad, quad->resource_id(), clip_region);
-}
-
-void GLRenderer::DrawContentQuad(const ContentDrawQuadBase* quad,
- ResourceId resource_id,
- const gfx::QuadF* clip_region) {
- gfx::Transform device_transform =
- current_frame()->window_matrix * current_frame()->projection_matrix *
- quad->shared_quad_state->quad_to_target_transform;
- device_transform.FlattenTo2d();
-
- gfx::QuadF device_layer_quad;
- bool use_aa = false;
- bool allow_aa = settings_->allow_antialiasing &&
- !quad->force_anti_aliasing_off && quad->IsEdge();
- if (allow_aa) {
- bool clipped = false;
- bool force_aa = false;
- device_layer_quad = cc::MathUtil::MapQuad(
- device_transform,
- gfx::QuadF(
- gfx::RectF(quad->shared_quad_state->visible_quad_layer_rect)),
- &clipped);
- use_aa = ShouldAntialiasQuad(device_layer_quad, clipped, force_aa);
- }
-
- // TODO(timav): simplify coordinate transformations in DrawContentQuadAA
- // similar to the way DrawContentQuadNoAA works and then consider
- // combining DrawContentQuadAA and DrawContentQuadNoAA into one method.
- if (use_aa)
- DrawContentQuadAA(quad, resource_id, device_transform, device_layer_quad,
- clip_region);
- else
- DrawContentQuadNoAA(quad, resource_id, clip_region);
-
- AccumulateDrawRects(quad->visible_rect,
- quad->shared_quad_state->quad_to_target_transform,
- &drawn_rects_);
-}
-
-void GLRenderer::DrawContentQuadAA(const ContentDrawQuadBase* quad,
- ResourceId resource_id,
- const gfx::Transform& device_transform,
- const gfx::QuadF& aa_quad,
- const gfx::QuadF* clip_region) {
- if (!device_transform.IsInvertible())
- return;
-
- ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_,
- &timer_queries_, "kTiledContentAA");
-
- gfx::Rect tile_rect = quad->visible_rect;
-
- gfx::RectF tex_coord_rect = cc::MathUtil::ScaleRectProportional(
- quad->tex_coord_rect, gfx::RectF(quad->rect), gfx::RectF(tile_rect));
- float tex_to_geom_scale_x = quad->rect.width() / quad->tex_coord_rect.width();
- float tex_to_geom_scale_y =
- quad->rect.height() / quad->tex_coord_rect.height();
-
- gfx::RectF clamp_geom_rect(tile_rect);
- gfx::RectF clamp_tex_rect(tex_coord_rect);
- // Clamp texture coordinates to avoid sampling outside the layer
- // by deflating the tile region half a texel or half a texel
- // minus epsilon for one pixel layers. The resulting clamp region
- // is mapped to the unit square by the vertex shader and mapped
- // back to normalized texture coordinates by the fragment shader
- // after being clamped to 0-1 range.
- float tex_clamp_x =
- std::min(0.5f, 0.5f * clamp_tex_rect.width() - kAntiAliasingEpsilon);
- float tex_clamp_y =
- std::min(0.5f, 0.5f * clamp_tex_rect.height() - kAntiAliasingEpsilon);
- float geom_clamp_x =
- std::min(tex_clamp_x * tex_to_geom_scale_x,
- 0.5f * clamp_geom_rect.width() - kAntiAliasingEpsilon);
- float geom_clamp_y =
- std::min(tex_clamp_y * tex_to_geom_scale_y,
- 0.5f * clamp_geom_rect.height() - kAntiAliasingEpsilon);
- clamp_geom_rect.Inset(gfx::InsetsF::VH(geom_clamp_y, geom_clamp_x));
- clamp_tex_rect.Inset(gfx::InsetsF::VH(tex_clamp_y, tex_clamp_x));
-
- // Map clamping rectangle to unit square.
- float vertex_tex_translate_x = -clamp_geom_rect.x() / clamp_geom_rect.width();
- float vertex_tex_translate_y =
- -clamp_geom_rect.y() / clamp_geom_rect.height();
- float vertex_tex_scale_x = tile_rect.width() / clamp_geom_rect.width();
- float vertex_tex_scale_y = tile_rect.height() / clamp_geom_rect.height();
-
- TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
- gl_, &highp_threshold_cache_, settings_->highp_threshold_min,
- quad->texture_size);
-
- auto local_quad = gfx::QuadF(gfx::RectF(tile_rect));
- float edge[24];
- SetupQuadForClippingAndAntialiasing(device_transform, quad, &aa_quad,
- clip_region, &local_quad, edge);
- DisplayResourceProviderGL::ScopedSamplerGL quad_resource_lock(
- resource_provider(), resource_id,
- quad->nearest_neighbor ? GL_NEAREST : GL_LINEAR);
- SamplerType sampler =
- SamplerTypeFromTextureTarget(quad_resource_lock.target());
-
- float fragment_tex_translate_x = clamp_tex_rect.x();
- float fragment_tex_translate_y = clamp_tex_rect.y();
- float fragment_tex_scale_x = clamp_tex_rect.width();
- float fragment_tex_scale_y = clamp_tex_rect.height();
-
- // Map to normalized texture coordinates.
- if (sampler != SAMPLER_TYPE_2D_RECT) {
- gfx::Size texture_size = quad->texture_size;
- DCHECK(!texture_size.IsEmpty());
- fragment_tex_translate_x /= texture_size.width();
- fragment_tex_translate_y /= texture_size.height();
- fragment_tex_scale_x /= texture_size.width();
- fragment_tex_scale_y /= texture_size.height();
- }
-
- SetUseProgram(
- ProgramKey::Tile(tex_coord_precision, sampler, USE_AA,
- quad->is_premultiplied ? PREMULTIPLIED_ALPHA
- : NON_PREMULTIPLIED_ALPHA,
- false, false, tint_gl_composited_content_,
- ShouldApplyRoundedCorner(quad)),
- quad_resource_lock.color_space(), CurrentRenderPassColorSpace());
-
- if (current_program_->tint_color_matrix_location() != -1) {
- auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix();
- gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1,
- false, matrix.data());
- }
-
- gl_->Uniform3fv(current_program_->edge_location(), 8, edge);
-
- gl_->Uniform4f(current_program_->vertex_tex_transform_location(),
- vertex_tex_translate_x, vertex_tex_translate_y,
- vertex_tex_scale_x, vertex_tex_scale_y);
- gl_->Uniform4f(current_program_->fragment_tex_transform_location(),
- fragment_tex_translate_x, fragment_tex_translate_y,
- fragment_tex_scale_x, fragment_tex_scale_y);
-
- // Blending is required for antialiasing.
- SetBlendEnabled(true);
- SetShaderOpacity(quad->shared_quad_state->opacity);
- if (current_program_->rounded_corner_rect_location() != -1) {
- SetShaderRoundedCorner(
- quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
- current_frame()->window_matrix * current_frame()->projection_matrix);
- }
- DCHECK(CanApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode));
- ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode);
-
- // Draw the quad with antialiasing.
- DrawQuadGeometryWithAA(quad, &local_quad, tile_rect);
- RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode);
-}
-
-void GLRenderer::DrawContentQuadNoAA(const ContentDrawQuadBase* quad,
- ResourceId resource_id,
- const gfx::QuadF* clip_region) {
- gfx::RectF tex_coord_rect = cc::MathUtil::ScaleRectProportional(
- quad->tex_coord_rect, gfx::RectF(quad->rect),
- gfx::RectF(quad->visible_rect));
- float tex_to_geom_scale_x = quad->rect.width() / quad->tex_coord_rect.width();
- float tex_to_geom_scale_y =
- quad->rect.height() / quad->tex_coord_rect.height();
-
- ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_,
- &timer_queries_, "kTiledContent");
-
- bool scaled = (tex_to_geom_scale_x != 1.f || tex_to_geom_scale_y != 1.f);
- GLenum filter = (scaled || !quad->shared_quad_state->quad_to_target_transform
- .IsIdentityOrIntegerTranslation()) &&
- !quad->nearest_neighbor
- ? GL_LINEAR
- : GL_NEAREST;
-
- DisplayResourceProviderGL::ScopedSamplerGL quad_resource_lock(
- resource_provider(), resource_id, filter);
- SamplerType sampler =
- SamplerTypeFromTextureTarget(quad_resource_lock.target());
-
- // Tiles are guaranteed to have been entirely filled except for the
- // bottom/right external edge tiles. Because of border texels, any
- // internal edge will have uvs that are offset from 0 and 1, so
- // clamping to tex_coord_rect in all cases would cause these border
- // texels to not be sampled. Therefore, only clamp texture coordinates
- // for external edge bottom/right tiles that don't have content all
- // the way to the edge and are using bilinear filtering.
- gfx::Size texture_size = quad->texture_size;
- bool fills_right_edge =
- !quad->IsRightEdge() || texture_size.width() == tex_coord_rect.right();
- bool fills_bottom_edge =
- !quad->IsBottomEdge() || texture_size.height() == tex_coord_rect.bottom();
- bool has_tex_clamp_rect =
- filter == GL_LINEAR && (!fills_right_edge || !fills_bottom_edge);
- gfx::SizeF tex_clamp_size(texture_size);
- // Clamp from the original tex coord rect, instead of the one that has
- // been adjusted by the visible rect.
- if (!fills_right_edge)
- tex_clamp_size.set_width(quad->tex_coord_rect.right() - 0.5f);
- if (!fills_bottom_edge)
- tex_clamp_size.set_height(quad->tex_coord_rect.bottom() - 0.5f);
-
- // Map to normalized texture coordinates.
- if (sampler != SAMPLER_TYPE_2D_RECT) {
- DCHECK(!texture_size.IsEmpty());
- tex_coord_rect.Scale(1.f / texture_size.width(),
- 1.f / texture_size.height());
- tex_clamp_size.Scale(1.f / texture_size.width(),
- 1.f / texture_size.height());
- }
-
- TexCoordPrecision tex_coord_precision =
- TexCoordPrecisionRequired(gl_, &highp_threshold_cache_,
- settings_->highp_threshold_min, texture_size);
- SetUseProgram(
- ProgramKey::Tile(tex_coord_precision, sampler, NO_AA,
- quad->is_premultiplied ? PREMULTIPLIED_ALPHA
- : NON_PREMULTIPLIED_ALPHA,
- !quad->ShouldDrawWithBlending(), has_tex_clamp_rect,
- tint_gl_composited_content_,
- ShouldApplyRoundedCorner(quad)),
- quad_resource_lock.color_space(), CurrentRenderPassColorSpace());
-
- if (current_program_->tint_color_matrix_location() != -1) {
- auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix();
- gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1,
- false, matrix.data());
- }
-
- if (has_tex_clamp_rect) {
- gl_->Uniform4f(current_program_->tex_clamp_rect_location(), 0, 0,
- tex_clamp_size.width(), tex_clamp_size.height());
- }
- gl_->Uniform4f(current_program_->vertex_tex_transform_location(),
- tex_coord_rect.x(), tex_coord_rect.y(), tex_coord_rect.width(),
- tex_coord_rect.height());
-
- DCHECK(CanApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode));
- SetBlendEnabled(quad->ShouldDrawWithBlending());
- ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode);
-
- SetShaderOpacity(quad->shared_quad_state->opacity);
- if (current_program_->rounded_corner_rect_location() != -1) {
- SetShaderRoundedCorner(
- quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
- current_frame()->window_matrix * current_frame()->projection_matrix);
- }
-
- // Pass quad coordinates to the uniform in the same order as GeometryBinding
- // does, then vertices will match the texture mapping in the vertex buffer.
- // The method SetShaderQuadF() changes the order of vertices and so it's
- // not used here.
- auto tile_quad = gfx::QuadF(gfx::RectF(quad->visible_rect));
- float width = quad->visible_rect.width();
- float height = quad->visible_rect.height();
- auto top_left = gfx::PointF(quad->visible_rect.origin());
- if (clip_region) {
- tile_quad = *clip_region;
- float gl_uv[8] = {
- (tile_quad.p4().x() - top_left.x()) / width,
- (tile_quad.p4().y() - top_left.y()) / height,
- (tile_quad.p1().x() - top_left.x()) / width,
- (tile_quad.p1().y() - top_left.y()) / height,
- (tile_quad.p2().x() - top_left.x()) / width,
- (tile_quad.p2().y() - top_left.y()) / height,
- (tile_quad.p3().x() - top_left.x()) / width,
- (tile_quad.p3().y() - top_left.y()) / height,
- };
- PrepareGeometry(CLIPPED_BINDING);
- clipped_geometry_->InitializeCustomQuadWithUVs(
- gfx::QuadF(gfx::RectF(quad->visible_rect)), gl_uv);
- } else {
- PrepareGeometry(SHARED_BINDING);
- }
- float gl_quad[8] = {
- tile_quad.p4().x(), tile_quad.p4().y(), tile_quad.p1().x(),
- tile_quad.p1().y(), tile_quad.p2().x(), tile_quad.p2().y(),
- tile_quad.p3().x(), tile_quad.p3().y(),
- };
- gl_->Uniform2fv(current_program_->quad_location(), 4, gl_quad);
-
- SetShaderMatrix(current_frame()->projection_matrix *
- quad->shared_quad_state->quad_to_target_transform);
-
- gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
- num_triangles_drawn_ += 2;
- RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode);
-}
-
-void GLRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad,
- const gfx::QuadF* clip_region) {
- std::string gpu_composite_time_string;
- if (!clip_region && quad->rect == quad->visible_rect)
- gpu_composite_time_string = "kYuvVideoContent";
- else
- gpu_composite_time_string = "kYuvVideoContentClipped";
- ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_,
- &timer_queries_,
- gpu_composite_time_string);
-
- SetBlendEnabled(quad->ShouldDrawWithBlending());
-
- TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
- gl_, &highp_threshold_cache_, settings_->highp_threshold_min,
- quad->shared_quad_state->visible_quad_layer_rect.size());
- YUVAlphaTextureMode alpha_texture_mode = quad->a_plane_resource_id()
- ? YUV_HAS_ALPHA_TEXTURE
- : YUV_NO_ALPHA_TEXTURE;
- UVTextureMode uv_texture_mode =
- quad->v_plane_resource_id() == quad->u_plane_resource_id()
- ? UV_TEXTURE_MODE_UV
- : UV_TEXTURE_MODE_U_V;
-
- DisplayResourceProviderGL::ScopedSamplerGL y_plane_lock(
- resource_provider(), quad->y_plane_resource_id(), GL_TEXTURE1, GL_LINEAR);
- DisplayResourceProviderGL::ScopedSamplerGL u_plane_lock(
- resource_provider(), quad->u_plane_resource_id(), GL_TEXTURE2, GL_LINEAR);
- DCHECK_EQ(y_plane_lock.target(), u_plane_lock.target());
- DCHECK_EQ(y_plane_lock.color_space(), u_plane_lock.color_space());
-
- // TODO(ccameron): There are currently two sources of the color space: the
- // resource color space and quad->video_color_space. Remove one of them.
- gfx::ColorSpace src_color_space = quad->video_color_space;
- // Invalid or unspecified color spaces should be treated as REC709.
- if (!src_color_space.IsValid())
- src_color_space = gfx::ColorSpace::CreateREC709();
- else
- DCHECK_EQ(src_color_space, y_plane_lock.color_space());
- // The source color space should never be RGB.
- DCHECK_NE(src_color_space, src_color_space.GetAsFullRangeRGB());
-
- gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace();
-
-#if BUILDFLAG(IS_WIN)
- // Force sRGB output on Windows for overlay candidate video quads to match
- // DirectComposition behavior in case these switch between overlays and
- // compositing. See https://crbug.com/811118 for details.
- // Currently if HDR is supported, OverlayProcessor doesn't promote HDR video
- // frame as overlay candidate. So it's unnecessary to worry about the
- // compositing-overlay switch here. In addition drawing a HDR video using sRGB
- // can cancel the advantages of HDR.
- const bool supports_dc_layers =
- output_surface_->capabilities().supports_dc_layers;
- if (supports_dc_layers && !src_color_space.IsHDR() &&
- resource_provider()->IsOverlayCandidate(quad->y_plane_resource_id())) {
- DCHECK(
- resource_provider()->IsOverlayCandidate(quad->u_plane_resource_id()));
- dst_color_space = gfx::ColorSpace::CreateSRGB();
- }
-#endif
-
- // TODO(jbauman): Use absl::optional when available.
- std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL> v_plane_lock;
- if (uv_texture_mode == UV_TEXTURE_MODE_U_V) {
- v_plane_lock = std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>(
- resource_provider(), quad->v_plane_resource_id(), GL_TEXTURE3,
- GL_LINEAR);
- DCHECK_EQ(y_plane_lock.target(), v_plane_lock->target());
- DCHECK_EQ(y_plane_lock.color_space(), v_plane_lock->color_space());
- }
- std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL> a_plane_lock;
- if (alpha_texture_mode == YUV_HAS_ALPHA_TEXTURE) {
- a_plane_lock = std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>(
- resource_provider(), quad->a_plane_resource_id(), GL_TEXTURE4,
- GL_LINEAR);
- DCHECK_EQ(y_plane_lock.target(), a_plane_lock->target());
- }
-
- // All planes must have the same sampler type.
- SamplerType sampler = SamplerTypeFromTextureTarget(y_plane_lock.target());
-
- SetUseProgram(
- ProgramKey::YUVVideo(tex_coord_precision, sampler, alpha_texture_mode,
- uv_texture_mode, tint_gl_composited_content_,
- ShouldApplyRoundedCorner(quad)),
- src_color_space, dst_color_space, /*adjust_src_white_level=*/true);
-
- if (current_program_->tint_color_matrix_location() != -1) {
- auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix();
- gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1,
- false, matrix.data());
- }
-
- if (current_program_->rounded_corner_rect_location() != -1) {
- SetShaderRoundedCorner(
- quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
- current_frame()->window_matrix * current_frame()->projection_matrix);
- }
-
- gfx::SizeF ya_tex_scale(1.0f, 1.0f);
- gfx::SizeF uv_tex_scale(1.0f, 1.0f);
- if (sampler != SAMPLER_TYPE_2D_RECT) {
- DCHECK(!quad->ya_tex_size.IsEmpty());
- DCHECK(!quad->uv_tex_size.IsEmpty());
- ya_tex_scale = gfx::SizeF(1.0f / quad->ya_tex_size.width(),
- 1.0f / quad->ya_tex_size.height());
- uv_tex_scale = gfx::SizeF(1.0f / quad->uv_tex_size.width(),
- 1.0f / quad->uv_tex_size.height());
- }
-
- float ya_vertex_tex_translate_x =
- quad->ya_tex_coord_rect.x() * ya_tex_scale.width();
- float ya_vertex_tex_translate_y =
- quad->ya_tex_coord_rect.y() * ya_tex_scale.height();
- float ya_vertex_tex_scale_x =
- quad->ya_tex_coord_rect.width() * ya_tex_scale.width();
- float ya_vertex_tex_scale_y =
- quad->ya_tex_coord_rect.height() * ya_tex_scale.height();
-
- float uv_vertex_tex_translate_x =
- quad->uv_tex_coord_rect.x() * uv_tex_scale.width();
- float uv_vertex_tex_translate_y =
- quad->uv_tex_coord_rect.y() * uv_tex_scale.height();
- float uv_vertex_tex_scale_x =
- quad->uv_tex_coord_rect.width() * uv_tex_scale.width();
- float uv_vertex_tex_scale_y =
- quad->uv_tex_coord_rect.height() * uv_tex_scale.height();
-
- gl_->Uniform2f(current_program_->ya_tex_scale_location(),
- ya_vertex_tex_scale_x, ya_vertex_tex_scale_y);
- gl_->Uniform2f(current_program_->ya_tex_offset_location(),
- ya_vertex_tex_translate_x, ya_vertex_tex_translate_y);
- gl_->Uniform2f(current_program_->uv_tex_scale_location(),
- uv_vertex_tex_scale_x, uv_vertex_tex_scale_y);
- gl_->Uniform2f(current_program_->uv_tex_offset_location(),
- uv_vertex_tex_translate_x, uv_vertex_tex_translate_y);
-
- gfx::RectF ya_clamp_rect(ya_vertex_tex_translate_x, ya_vertex_tex_translate_y,
- ya_vertex_tex_scale_x, ya_vertex_tex_scale_y);
- ya_clamp_rect.Inset(gfx::InsetsF::VH(0.5f * ya_tex_scale.height(),
- 0.5f * ya_tex_scale.width()));
- gfx::RectF uv_clamp_rect(uv_vertex_tex_translate_x, uv_vertex_tex_translate_y,
- uv_vertex_tex_scale_x, uv_vertex_tex_scale_y);
- uv_clamp_rect.Inset(gfx::InsetsF::VH(0.5f * uv_tex_scale.height(),
- 0.5f * uv_tex_scale.width()));
- gl_->Uniform4f(current_program_->ya_clamp_rect_location(), ya_clamp_rect.x(),
- ya_clamp_rect.y(), ya_clamp_rect.right(),
- ya_clamp_rect.bottom());
- gl_->Uniform4f(current_program_->uv_clamp_rect_location(), uv_clamp_rect.x(),
- uv_clamp_rect.y(), uv_clamp_rect.right(),
- uv_clamp_rect.bottom());
-
- gl_->Uniform1i(current_program_->y_texture_location(), 1);
- if (uv_texture_mode == UV_TEXTURE_MODE_UV) {
- gl_->Uniform1i(current_program_->uv_texture_location(), 2);
- } else {
- gl_->Uniform1i(current_program_->u_texture_location(), 2);
- gl_->Uniform1i(current_program_->v_texture_location(), 3);
- }
- if (alpha_texture_mode == YUV_HAS_ALPHA_TEXTURE)
- gl_->Uniform1i(current_program_->a_texture_location(), 4);
-
- gl_->Uniform1f(current_program_->resource_multiplier_location(),
- quad->resource_multiplier);
- gl_->Uniform1f(current_program_->resource_offset_location(),
- quad->resource_offset);
-
- // The transform and vertex data are used to figure out the extents that the
- // un-antialiased quad should have and which vertex this is and the float
- // quad passed in via uniform is the actual geometry that gets used to draw
- // it. This is why this centered rect is used and not the original quad_rect.
- auto tile_rect = gfx::RectF(quad->rect);
-
- SetShaderOpacity(quad->shared_quad_state->opacity);
- if (!clip_region && quad->rect == quad->visible_rect) {
- DrawQuadGeometry(current_frame()->projection_matrix,
- quad->shared_quad_state->quad_to_target_transform,
- tile_rect);
- } else {
- gfx::QuadF region_quad =
- clip_region ? *clip_region : gfx::QuadF(gfx::RectF(quad->visible_rect));
- float uvs[8] = {0};
- GetScaledUVs(quad->rect, &region_quad, uvs);
- region_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height());
- region_quad -= gfx::Vector2dF(0.5f, 0.5f);
- DrawQuadGeometryClippedByQuadF(
- quad->shared_quad_state->quad_to_target_transform, tile_rect,
- region_quad, uvs);
- }
-
- // Track the region in the current target surface that has been drawn to.
- AccumulateDrawRects(quad->visible_rect,
- quad->shared_quad_state->quad_to_target_transform,
- &drawn_rects_);
-}
-
-void GLRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad,
- const gfx::QuadF* clip_region) {
- std::string gpu_composite_time_string;
- if (!clip_region && quad->rect == quad->visible_rect) {
- gpu_composite_time_string = "kStreamVideoContent";
- } else {
- gpu_composite_time_string = "kStreamVideoContentClipped";
- }
- ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_,
- &timer_queries_,
- gpu_composite_time_string);
- SetBlendEnabled(quad->ShouldDrawWithBlending());
-
- DCHECK(output_surface_->context_provider()
- ->ContextCapabilities()
- .egl_image_external);
-
- TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
- gl_, &highp_threshold_cache_, settings_->highp_threshold_min,
- quad->shared_quad_state->visible_quad_layer_rect.size());
-
- DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider(),
- quad->resource_id());
-
- SetUseProgram(ProgramKey::VideoStream(tex_coord_precision,
- ShouldApplyRoundedCorner(quad)),
- lock.color_space(), CurrentRenderPassColorSpace());
-
- DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_));
- gl_->BindTexture(GL_TEXTURE_EXTERNAL_OES, lock.texture_id());
-
- static float gl_matrix[16];
- gfx::Transform matrix;
- matrix.Scale(quad->uv_bottom_right.x() - quad->uv_top_left.x(),
- quad->uv_bottom_right.y() - quad->uv_top_left.y());
- matrix.Translate(quad->uv_top_left.x(), quad->uv_top_left.y());
- ToGLMatrix(&gl_matrix[0], matrix);
- gl_->UniformMatrix4fv(current_program_->tex_matrix_location(), 1, false,
- gl_matrix);
-
- SetShaderOpacity(quad->shared_quad_state->opacity);
- if (current_program_->rounded_corner_rect_location() != -1) {
- SetShaderRoundedCorner(
- quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
- current_frame()->window_matrix * current_frame()->projection_matrix);
- }
- gfx::Size texture_size = lock.size();
- gfx::RectF uv_visible_rect(quad->uv_top_left.x(), quad->uv_top_left.y(),
- quad->uv_bottom_right.x() - quad->uv_top_left.x(),
- quad->uv_bottom_right.y() - quad->uv_top_left.y());
- const SamplerType sampler = SamplerTypeFromTextureTarget(lock.target());
- Float4 tex_clamp_rect = UVClampRect(uv_visible_rect, texture_size, sampler);
- gl_->Uniform4f(current_program_->tex_clamp_rect_location(),
- tex_clamp_rect.data[0], tex_clamp_rect.data[1],
- tex_clamp_rect.data[2], tex_clamp_rect.data[3]);
-
- auto tile_rect = gfx::RectF(quad->rect);
-
- if (!clip_region && quad->rect == quad->visible_rect) {
- DrawQuadGeometry(current_frame()->projection_matrix,
- quad->shared_quad_state->quad_to_target_transform,
- tile_rect);
- } else {
- gfx::QuadF region_quad =
- clip_region ? *clip_region : gfx::QuadF(gfx::RectF(quad->visible_rect));
- float uvs[8] = {0};
- GetScaledUVs(quad->rect, &region_quad, uvs);
- region_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height());
- region_quad -= gfx::Vector2dF(0.5f, 0.5f);
- DrawQuadGeometryClippedByQuadF(
- quad->shared_quad_state->quad_to_target_transform, tile_rect,
- region_quad, uvs);
- }
-
- AccumulateDrawRects(quad->visible_rect,
- quad->shared_quad_state->quad_to_target_transform,
- &drawn_rects_);
-}
-
-void GLRenderer::FlushTextureQuadCache(BoundGeometry flush_binding) {
- // Check to see if we have anything to draw.
- if (draw_cache_.is_empty)
- return;
- ScopedTimerQuery scoped_timer_query(CompositeTimeTracingEnabled(), gl_,
- &timer_queries_, "kTextureContentFlush");
-
- PrepareGeometry(flush_binding);
-
- // Set the correct blending mode.
- SetBlendEnabled(draw_cache_.needs_blending);
-
- // Assume the current active textures is 0.
- DisplayResourceProviderGL::ScopedSamplerGL locked_quad(
- resource_provider(), draw_cache_.resource_id,
- draw_cache_.nearest_neighbor ? GL_NEAREST : GL_LINEAR);
-
- // Bind the program to the GL state.
- SetUseProgram(draw_cache_.program_key, locked_quad.color_space(),
- CurrentRenderPassColorSpace(),
- /*adjust_src_white_level=*/draw_cache_.is_video_frame,
- locked_quad.hdr_metadata());
-
- if (current_program_->rounded_corner_rect_location() != -1) {
- SetShaderRoundedCorner(
- draw_cache_.mask_filter_info.rounded_corner_bounds(),
- current_frame()->window_matrix * current_frame()->projection_matrix);
- }
-
- DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_));
- gl_->BindTexture(locked_quad.target(), locked_quad.texture_id());
-
- static_assert(sizeof(Float4) == 4 * sizeof(float),
- "Float4 struct should be densely packed");
- static_assert(sizeof(Float16) == 16 * sizeof(float),
- "Float16 struct should be densely packed");
-
- // Upload the tranforms for both points and uvs.
- gl_->UniformMatrix4fv(
- current_program_->matrix_location(),
- static_cast<int>(draw_cache_.matrix_data.size()), false,
- reinterpret_cast<float*>(&draw_cache_.matrix_data.front()));
- gl_->Uniform4fv(current_program_->vertex_tex_transform_location(),
- static_cast<int>(draw_cache_.uv_xform_data.size()),
- reinterpret_cast<float*>(&draw_cache_.uv_xform_data.front()));
-
- if (current_program_->tint_color_matrix_location() != -1) {
- auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix();
- gl_->UniformMatrix4fv(current_program_->tint_color_matrix_location(), 1,
- false, matrix.data());
- }
-
- if (current_program_->tex_clamp_rect_location() != -1) {
- // Draw batching is not allowed with texture clamping.
- DCHECK_EQ(1u, draw_cache_.matrix_data.size());
- gl_->Uniform4f(current_program_->tex_clamp_rect_location(),
- draw_cache_.tex_clamp_rect_data.data[0],
- draw_cache_.tex_clamp_rect_data.data[1],
- draw_cache_.tex_clamp_rect_data.data[2],
- draw_cache_.tex_clamp_rect_data.data[3]);
- }
-
- if (draw_cache_.background_color != SK_ColorTRANSPARENT) {
- Float4 background_color =
- PremultipliedColor(draw_cache_.background_color, 1.f);
- gl_->Uniform4fv(current_program_->background_color_location(), 1,
- background_color.data);
- }
-
- gl_->Uniform1fv(
- current_program_->vertex_opacity_location(),
- static_cast<int>(draw_cache_.vertex_opacity_data.size()),
- static_cast<float*>(&draw_cache_.vertex_opacity_data.front()));
-
- DCHECK_LE(draw_cache_.matrix_data.size(),
- static_cast<size_t>(std::numeric_limits<int>::max()) / 6u);
-
- // Draw the quads!
- gl_->DrawElements(GL_TRIANGLES,
- 6 * static_cast<int>(draw_cache_.matrix_data.size()),
- GL_UNSIGNED_SHORT, nullptr);
- num_triangles_drawn_ += 2 * static_cast<int>(draw_cache_.matrix_data.size());
-
- // Clear the cache.
- draw_cache_.is_empty = true;
- draw_cache_.resource_id = kInvalidResourceId;
- draw_cache_.uv_xform_data.resize(0);
- draw_cache_.vertex_opacity_data.resize(0);
- draw_cache_.matrix_data.resize(0);
- draw_cache_.tex_clamp_rect_data = Float4();
- draw_cache_.is_video_frame = false;
-
- // If we had a clipped binding, prepare the shared binding for the
- // next inserts.
- if (flush_binding == CLIPPED_BINDING) {
- PrepareGeometry(SHARED_BINDING);
- }
-}
-
-void GLRenderer::EnqueueTextureQuad(const TextureDrawQuad* quad,
- const gfx::QuadF* clip_region) {
- // If we have a clip_region then we have to render the next quad
- // with dynamic geometry, therefore we must flush all pending
- // texture quads.
- if (clip_region) {
- // We send in false here because we want to flush what's currently in the
- // queue using the shared_geometry and not clipped_geometry
- FlushTextureQuadCache(SHARED_BINDING);
- }
-
- DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider(),
- quad->resource_id());
- // ScopedReadLockGL contains the correct texture size, even when
- // quad->resource_size_in_pixels() is empty.
- const gfx::Size texture_size = lock.size();
- TexCoordPrecision tex_coord_precision =
- TexCoordPrecisionRequired(gl_, &highp_threshold_cache_,
- settings_->highp_threshold_min, texture_size);
-
- const SamplerType sampler = SamplerTypeFromTextureTarget(lock.target());
-
- bool need_tex_clamp_rect = !quad->resource_size_in_pixels().IsEmpty() &&
- (quad->uv_top_left != gfx::PointF(0, 0) ||
- quad->uv_bottom_right != gfx::PointF(1, 1));
-
- ProgramKey program_key = ProgramKey::Texture(
- tex_coord_precision, sampler,
- quad->premultiplied_alpha ? PREMULTIPLIED_ALPHA : NON_PREMULTIPLIED_ALPHA,
- quad->background_color != SK_ColorTRANSPARENT, need_tex_clamp_rect,
- tint_gl_composited_content_, ShouldApplyRoundedCorner(quad));
- ResourceId resource_id = quad->resource_id();
-
- size_t max_quads = StaticGeometryBinding::NUM_QUADS;
- if (draw_cache_.is_empty || draw_cache_.program_key != program_key ||
- draw_cache_.resource_id != resource_id ||
- draw_cache_.needs_blending != quad->ShouldDrawWithBlending() ||
- draw_cache_.nearest_neighbor != quad->nearest_neighbor ||
- draw_cache_.background_color != quad->background_color ||
- draw_cache_.mask_filter_info !=
- quad->shared_quad_state->mask_filter_info ||
- draw_cache_.matrix_data.size() >= max_quads ||
- draw_cache_.is_video_frame != quad->is_video_frame) {
- FlushTextureQuadCache(SHARED_BINDING);
- draw_cache_.is_empty = false;
- draw_cache_.program_key = program_key;
- draw_cache_.resource_id = resource_id;
- draw_cache_.needs_blending = quad->ShouldDrawWithBlending();
- draw_cache_.nearest_neighbor = quad->nearest_neighbor;
- draw_cache_.background_color = quad->background_color;
- draw_cache_.mask_filter_info = quad->shared_quad_state->mask_filter_info;
- draw_cache_.is_video_frame = quad->is_video_frame;
- }
-
- // Generate the uv-transform
- auto uv_transform = UVTransform(quad);
- if (sampler == SAMPLER_TYPE_2D_RECT) {
- // Un-normalize the texture coordiantes for rectangle targets.
- uv_transform.data[0] *= texture_size.width();
- uv_transform.data[2] *= texture_size.width();
- uv_transform.data[1] *= texture_size.height();
- uv_transform.data[3] *= texture_size.height();
- }
- draw_cache_.uv_xform_data.push_back(uv_transform);
-
- if (need_tex_clamp_rect) {
- DCHECK_EQ(1u, draw_cache_.uv_xform_data.size());
- DCHECK_EQ(texture_size.ToString(),
- quad->resource_size_in_pixels().ToString());
- DCHECK(!texture_size.IsEmpty());
- gfx::RectF uv_visible_rect(
- quad->uv_top_left.x(), quad->uv_top_left.y(),
- quad->uv_bottom_right.x() - quad->uv_top_left.x(),
- quad->uv_bottom_right.y() - quad->uv_top_left.y());
- Float4 tex_clamp_rect = UVClampRect(uv_visible_rect, texture_size, sampler);
- draw_cache_.tex_clamp_rect_data = tex_clamp_rect;
- }
-
- // Generate the vertex opacity
- const float opacity = quad->shared_quad_state->opacity;
- draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[0] * opacity);
- draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[1] * opacity);
- draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[2] * opacity);
- draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[3] * opacity);
-
- // Generate the transform matrix
- gfx::Transform quad_rect_matrix;
- QuadRectTransform(&quad_rect_matrix,
- quad->shared_quad_state->quad_to_target_transform,
- gfx::RectF(quad->visible_rect));
- quad_rect_matrix = current_frame()->projection_matrix * quad_rect_matrix;
-
- Float16 m;
- quad_rect_matrix.matrix().getColMajor(m.data);
- draw_cache_.matrix_data.push_back(m);
-
- // Track the region in the current target surface that has been drawn to.
- AccumulateDrawRects(quad->visible_rect,
- quad->shared_quad_state->quad_to_target_transform,
- &drawn_rects_);
-
- if (clip_region) {
- DCHECK_EQ(quad->rect, quad->visible_rect);
- gfx::QuadF scaled_region;
- if (!GetScaledRegion(quad->rect, clip_region, &scaled_region)) {
- scaled_region = SharedGeometryQuad().BoundingBox();
- }
- // Both the scaled region and the SharedGeomtryQuad are in the space
- // -0.5->0.5. We need to move that to the space 0->1.
- float uv[8];
- uv[0] = scaled_region.p1().x() + 0.5f;
- uv[1] = scaled_region.p1().y() + 0.5f;
- uv[2] = scaled_region.p2().x() + 0.5f;
- uv[3] = scaled_region.p2().y() + 0.5f;
- uv[4] = scaled_region.p3().x() + 0.5f;
- uv[5] = scaled_region.p3().y() + 0.5f;
- uv[6] = scaled_region.p4().x() + 0.5f;
- uv[7] = scaled_region.p4().y() + 0.5f;
- PrepareGeometry(CLIPPED_BINDING);
- clipped_geometry_->InitializeCustomQuadWithUVs(scaled_region, uv);
- FlushTextureQuadCache(CLIPPED_BINDING);
- } else if (need_tex_clamp_rect) {
- FlushTextureQuadCache(SHARED_BINDING);
- }
-}
-
-void GLRenderer::FinishDrawingFrame() {
- if (use_sync_query_) {
- sync_queries_.EndCurrentFrame();
- }
-
- swap_buffer_rect_.Union(current_frame()->root_damage_rect);
-
- if (use_swap_with_bounds_)
- swap_content_bounds_ = current_frame()->root_content_bounds;
-
- copier_.FreeUnusedCachedResources();
-
- current_framebuffer_texture_ = nullptr;
-
- gl_->Disable(GL_BLEND);
- blend_shadow_ = false;
-
- // Schedule output surface as overlay first to preserve existing ordering
- // semantics during overlay refactoring.
- ScheduleOutputSurfaceAsOverlay();
-
-#if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE)
- ScheduleOverlays();
-#elif BUILDFLAG(IS_APPLE)
- ScheduleCALayers();
-#elif BUILDFLAG(IS_WIN)
- ScheduleDCLayers();
-#endif
-
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.triangles"), "Triangles Drawn",
- num_triangles_drawn_);
-
- // Mark the end of batched read of shared images.
- gl_->EndBatchReadAccessSharedImageCHROMIUM();
-}
-
-bool GLRenderer::OverdrawTracingEnabled() {
- // Only collect trace data if we select viz.overdraw.
- bool tracing_enabled;
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"),
- &tracing_enabled);
- // ARB_occlusion_query is required for tracing.
- // Trace only the root render pass.
- return tracing_enabled && use_occlusion_query_ &&
- current_frame()->current_render_pass ==
- current_frame()->root_render_pass;
-}
-
-bool GLRenderer::CompositeTimeTracingEnabled() {
- bool tracing_enabled;
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(
- TRACE_DISABLED_BY_DEFAULT("viz.gpu_composite_time"), &tracing_enabled);
-
- return tracing_enabled && use_timer_query_;
-}
-
-void GLRenderer::AddCompositeTimeTraces(base::TimeTicks ready_timestamp) {
- DCHECK(CompositeTimeTracingEnabled());
- DCHECK_EQ(timer_queries_.front().first, kTimerQueryDummy);
-
- std::size_t count = 0;
- uint64_t duration = 0;
-
- // List of queries to delete after their results are retrieved.
- std::vector<unsigned> queries_to_delete;
-
- // Queue of durations per draw call. The |second| in the pair represents the
- // draw call type as string.
- base::queue<std::pair<uint64_t, std::string>> durations;
-
- // Pop the fence query as it does not represent a timer query.
- timer_queries_.pop();
-
- // Initialize |start_time_ticks| as the end timestamp and walk backwards to
- // find the actual timestamp.
- base::TimeTicks start_time_ticks = ready_timestamp;
-
- while (timer_queries_.size() &&
- timer_queries_.front().first != kTimerQueryDummy) {
- count++;
- gl_->GetQueryObjectui64vEXT(timer_queries_.front().first,
- GL_QUERY_RESULT_EXT, &duration);
- durations.emplace(duration, timer_queries_.front().second);
- queries_to_delete.push_back(timer_queries_.front().first);
- timer_queries_.pop();
- start_time_ticks -= base::Nanoseconds(duration);
- }
-
- // Delete all timer queries for which results have been retrieved.
- gl_->DeleteQueriesEXT(count, queries_to_delete.data());
-
- base::TimeDelta unique_id_delta = ready_timestamp - start_time_ticks;
- const int trace_unique_id = unique_id_delta.InMilliseconds() * count;
-
- TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(
- TRACE_DISABLED_BY_DEFAULT("viz.gpu_composite_time"), "Composite Time",
- TRACE_ID_LOCAL(trace_unique_id), start_time_ticks);
-
- while (!durations.empty()) {
- duration = durations.front().first;
-
- // |duration| may be set to 0 if the timer query result was unavailable in
- // |GetQueryObjectui64vEXT| function call.
- if (!duration) {
- durations.pop();
- continue;
- }
- TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
- TRACE_DISABLED_BY_DEFAULT("viz.gpu_composite_time"), "Composite Time",
- TRACE_ID_LOCAL(trace_unique_id), durations.front().second.c_str(),
- start_time_ticks);
- start_time_ticks += base::Nanoseconds(duration);
- durations.pop();
- }
-
- TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0(
- TRACE_DISABLED_BY_DEFAULT("viz.gpu_composite_time"), "Composite Time",
- TRACE_ID_LOCAL(trace_unique_id), ready_timestamp);
-}
-
-void GLRenderer::FinishDrawingQuadList() {
- FlushTextureQuadCache(SHARED_BINDING);
- if (occlusion_query_) {
- // Use the current surface area as max result. The effect is that overdraw
- // is reported as a percentage of the output surface size. ie. 2x overdraw
- // for the whole screen is reported as 200.
- base::CheckedNumeric<int> surface_area =
- current_surface_size_.GetCheckedArea();
- DCHECK_GT(static_cast<int>(surface_area.ValueOrDefault(INT_MAX)), 0);
-
- gl_->EndQueryEXT(GL_SAMPLES_PASSED_ARB);
- context_support_->SignalQuery(
- occlusion_query_, base::BindOnce(&GLRenderer::ProcessOverdrawFeedback,
- weak_ptr_factory_.GetWeakPtr(),
- surface_area, occlusion_query_));
- occlusion_query_ = 0;
- }
-}
-
-void GLRenderer::GenerateMipmap() {
- DCHECK(current_framebuffer_texture_);
- current_framebuffer_texture_->set_generate_mipmap();
-}
-
-bool GLRenderer::FlippedFramebuffer() const {
-#if BUILDFLAG(IS_APPLE)
- if (force_drawing_frame_framebuffer_unflipped_)
- return false;
-#endif
- if (current_frame()->current_render_pass != current_frame()->root_render_pass)
- return true;
- return FlippedRootFramebuffer();
-}
-
-bool GLRenderer::FlippedRootFramebuffer() const {
- // GL is normally flipped, so a flipped output results in an unflipping.
- return output_surface_->capabilities().output_surface_origin ==
- gfx::SurfaceOrigin::kBottomLeft;
-}
-
-void GLRenderer::EnsureScissorTestEnabled() {
- if (is_scissor_enabled_)
- return;
-
- FlushTextureQuadCache(SHARED_BINDING);
- gl_->Enable(GL_SCISSOR_TEST);
- is_scissor_enabled_ = true;
-}
-
-void GLRenderer::EnsureScissorTestDisabled() {
- if (!is_scissor_enabled_)
- return;
-
- FlushTextureQuadCache(SHARED_BINDING);
- gl_->Disable(GL_SCISSOR_TEST);
- is_scissor_enabled_ = false;
-}
-
-void GLRenderer::CopyDrawnRenderPass(
- const copy_output::RenderPassGeometry& geometry,
- std::unique_ptr<CopyOutputRequest> request) {
- TRACE_EVENT0("viz", "GLRenderer::CopyDrawnRenderPass");
-
- GLuint framebuffer_texture = 0;
- gfx::Size framebuffer_texture_size;
- if (current_framebuffer_texture_) {
- framebuffer_texture = current_framebuffer_texture_->id();
- framebuffer_texture_size = current_framebuffer_texture_->size();
- }
- copier_.CopyFromTextureOrFramebuffer(
- std::move(request), geometry, GetFramebufferCopyTextureFormat(),
- framebuffer_texture, framebuffer_texture_size, FlippedFramebuffer(),
- CurrentRenderPassColorSpace());
-
- // The copier modified texture/framebuffer bindings, shader programs, and
- // other GL state; and so this must be restored before continuing.
- RestoreGLState();
-
- // CopyDrawnRenderPass() can change the binding of the framebuffer target as
- // a part of its usual scaling and readback operations. It will break next
- // CopyDrawnRenderPass() call for the root render pass. Therefore, make sure
- // to restore the correct framebuffer between readbacks. (Even if it did
- // not, a Mac-specific bug requires this workaround: http://crbug.com/99393)
- const auto* render_pass = current_frame()->current_render_pass;
- if (render_pass == current_frame()->root_render_pass)
- BindFramebufferToOutputSurface();
-}
-
-void GLRenderer::ToGLMatrix(float* gl_matrix, const gfx::Transform& transform) {
- transform.matrix().getColMajor(gl_matrix);
-}
-
-void GLRenderer::SetShaderQuadF(const gfx::QuadF& quad) {
- if (!current_program_ || current_program_->quad_location() == -1)
- return;
- float gl_quad[8];
- gl_quad[0] = quad.p1().x();
- gl_quad[1] = quad.p1().y();
- gl_quad[2] = quad.p2().x();
- gl_quad[3] = quad.p2().y();
- gl_quad[4] = quad.p3().x();
- gl_quad[5] = quad.p3().y();
- gl_quad[6] = quad.p4().x();
- gl_quad[7] = quad.p4().y();
- gl_->Uniform2fv(current_program_->quad_location(), 4, gl_quad);
-}
-
-void GLRenderer::SetShaderOpacity(float opacity) {
- if (!current_program_ || current_program_->alpha_location() == -1)
- return;
- gl_->Uniform1f(current_program_->alpha_location(), opacity);
-}
-
-void GLRenderer::SetShaderMatrix(const gfx::Transform& transform) {
- if (!current_program_ || current_program_->matrix_location() == -1)
- return;
- float gl_matrix[16];
- ToGLMatrix(gl_matrix, transform);
- gl_->UniformMatrix4fv(current_program_->matrix_location(), 1, false,
- gl_matrix);
-}
-
-void GLRenderer::SetShaderColor(SkColor color, float opacity) {
- if (!current_program_ || current_program_->color_location() == -1)
- return;
- Float4 float_color = PremultipliedColor(color, opacity);
- gl_->Uniform4fv(current_program_->color_location(), 1, float_color.data);
-}
-
-void GLRenderer::SetStencilEnabled(bool enabled) {
- if (enabled == stencil_shadow_)
- return;
-
- if (enabled)
- gl_->Enable(GL_STENCIL_TEST);
- else
- gl_->Disable(GL_STENCIL_TEST);
- stencil_shadow_ = enabled;
-}
-
-void GLRenderer::SetBlendEnabled(bool enabled) {
- if (enabled == blend_shadow_)
- return;
-
- if (enabled)
- gl_->Enable(GL_BLEND);
- else
- gl_->Disable(GL_BLEND);
- blend_shadow_ = enabled;
-}
-
-void GLRenderer::SetShaderRoundedCorner(
- const gfx::RRectF& rounded_corner_bounds,
- const gfx::Transform& screen_transform) {
- DCHECK(current_program_);
- DCHECK(!rounded_corner_bounds.IsEmpty());
- DCHECK_NE(current_program_->rounded_corner_rect_location(), -1);
- DCHECK_NE(current_program_->rounded_corner_radius_location(), -1);
- DCHECK(screen_transform.IsScaleOrTranslation());
-
- const gfx::Vector2dF& translate = screen_transform.To2dTranslation();
- const gfx::Vector2dF& scale = screen_transform.To2dScale();
- gfx::RRectF bounds_in_screen = rounded_corner_bounds;
- bounds_in_screen.Scale(scale.x(), scale.y());
- bounds_in_screen.Offset(translate.x(), translate.y());
-
- gfx::RectF rect = bounds_in_screen.rect();
-
- gl_->Uniform4f(current_program_->rounded_corner_rect_location(), rect.x(),
- rect.y(), rect.width(), rect.height());
- gl_->Uniform4f(
- current_program_->rounded_corner_radius_location(),
- bounds_in_screen.GetCornerRadii(gfx::RRectF::Corner::kUpperLeft).x(),
- bounds_in_screen.GetCornerRadii(gfx::RRectF::Corner::kUpperRight).x(),
- bounds_in_screen.GetCornerRadii(gfx::RRectF::Corner::kLowerRight).x(),
- bounds_in_screen.GetCornerRadii(gfx::RRectF::Corner::kLowerLeft).x());
-}
-
-void GLRenderer::DrawQuadGeometryClippedByQuadF(
- const gfx::Transform& draw_transform,
- const gfx::RectF& quad_rect,
- const gfx::QuadF& clipping_region_quad,
- const float* uvs) {
- PrepareGeometry(CLIPPED_BINDING);
- if (uvs) {
- clipped_geometry_->InitializeCustomQuadWithUVs(clipping_region_quad, uvs);
- } else {
- clipped_geometry_->InitializeCustomQuad(clipping_region_quad);
- }
- gfx::Transform quad_rect_matrix;
- QuadRectTransform(&quad_rect_matrix, draw_transform, quad_rect);
- SetShaderMatrix(current_frame()->projection_matrix * quad_rect_matrix);
-
- gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
- reinterpret_cast<const void*>(0));
- num_triangles_drawn_ += 2;
-}
-
-void GLRenderer::DrawQuadGeometry(const gfx::Transform& projection_matrix,
- const gfx::Transform& draw_transform,
- const gfx::RectF& quad_rect) {
- PrepareGeometry(SHARED_BINDING);
- gfx::Transform quad_rect_matrix;
- QuadRectTransform(&quad_rect_matrix, draw_transform, quad_rect);
- SetShaderMatrix(projection_matrix * quad_rect_matrix);
-
- gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
- num_triangles_drawn_ += 2;
-}
-
-void GLRenderer::DrawQuadGeometryWithAA(const DrawQuad* quad,
- gfx::QuadF* local_quad,
- const gfx::Rect& tile_rect) {
- DCHECK(local_quad);
- // Normalize to tile_rect.
- local_quad->Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height());
-
- SetShaderQuadF(*local_quad);
-
- // The transform and vertex data are used to figure out the extents that the
- // un-antialiased quad should have and which vertex this is and the float
- // quad passed in via uniform is the actual geometry that gets used to draw
- // it. This is why this centered rect is used and not the original quad_rect.
- DrawQuadGeometry(current_frame()->projection_matrix,
- quad->shared_quad_state->quad_to_target_transform,
- CenteredRect(tile_rect));
-}
-
-void GLRenderer::SwapBuffers(SwapFrameData swap_frame_data) {
- DCHECK(visible_);
-
- TRACE_EVENT0("viz", "GLRenderer::SwapBuffers");
- // We're done! Time to swapbuffers!
-
- gfx::Size surface_size = surface_size_for_swap_buffers();
-
- OutputSurfaceFrame output_frame;
- output_frame.latency_info = std::move(swap_frame_data.latency_info);
- output_frame.top_controls_visible_height_changed =
- swap_frame_data.top_controls_visible_height_changed;
- output_frame.size = surface_size;
-#if BUILDFLAG(IS_MAC)
- output_frame.ca_layer_error_code = swap_frame_data.ca_layer_error_code;
-#endif
-
- if (use_swap_with_bounds_) {
- output_frame.content_bounds = std::move(swap_content_bounds_);
- } else if (use_partial_swap_) {
- // If supported, we can save significant bandwidth by only swapping the
- // damaged/scissored region (clamped to the viewport).
- swap_buffer_rect_.Intersect(gfx::Rect(surface_size));
- int flipped_y_pos_of_rect_bottom = surface_size.height() -
- swap_buffer_rect_.y() -
- swap_buffer_rect_.height();
- output_frame.sub_buffer_rect =
- gfx::Rect(swap_buffer_rect_.x(),
- FlippedRootFramebuffer() ? flipped_y_pos_of_rect_bottom
- : swap_buffer_rect_.y(),
- swap_buffer_rect_.width(), swap_buffer_rect_.height());
- } else if (swap_buffer_rect_.IsEmpty() && allow_empty_swap_) {
- output_frame.sub_buffer_rect = swap_buffer_rect_;
- }
-
- // Record resources from viz clients that have been shipped as overlays to the
- // gpu together.
- swapping_overlay_resources_.push_back(std::move(pending_overlay_resources_));
- pending_overlay_resources_.clear();
- if (settings_->release_overlay_resources_after_gpu_query) {
- // Record RenderPass textures that have been shipped as overlays to the gpu
- // together.
- displayed_overlay_textures_.push_back(
- std::move(awaiting_swap_overlay_textures_));
- awaiting_swap_overlay_textures_.clear();
- } else {
- // If |displayed_overlay_textures_| is appended to in this case then
- // SwapBuffersComplete needs to be extended to handle it.
- DCHECK(awaiting_swap_overlay_textures_.empty());
- }
-
- output_surface_->SwapBuffers(std::move(output_frame));
-
- swap_buffer_rect_ = gfx::Rect();
-
- if (context_busy_) {
- output_surface_->context_provider()->CacheController()->ClientBecameNotBusy(
- std::move(context_busy_));
- }
-}
-
-void GLRenderer::SwapBuffersSkipped() {
- if (context_busy_) {
- output_surface_->context_provider()->CacheController()->ClientBecameNotBusy(
- std::move(context_busy_));
- }
-}
-
-void GLRenderer::SwapBuffersComplete(gfx::GpuFenceHandle release_fence) {
- // Returned release fence is signalled when the latest swap is presented,
- // and tells us we can re-use the buffers from the /previous/ swap.
- if (swapping_overlay_resources_.size() > 1) {
- for (auto& lock : swapping_overlay_resources_[0]) {
- lock->SetReleaseFence(release_fence.Clone());
- }
- }
-
- if (settings_->release_overlay_resources_after_gpu_query) {
- // Once a resource has been swap-ACKed, send a query to the GPU process to
- // ask if the resource is no longer being consumed by the system compositor.
- // The response will come with the next swap-ACK.
- if (!swapping_overlay_resources_.empty()) {
- for (OverlayResourceLock& lock : swapping_overlay_resources_.front()) {
- unsigned texture = lock->texture_id();
- if (swapped_and_acked_overlay_resources_.find(texture) ==
- swapped_and_acked_overlay_resources_.end()) {
- swapped_and_acked_overlay_resources_[texture] = std::move(lock);
- }
- }
- swapping_overlay_resources_.pop_front();
- }
- if (!displayed_overlay_textures_.empty()) {
- for (auto& overlay : displayed_overlay_textures_.front())
- awaiting_release_overlay_textures_.push_back(std::move(overlay));
- displayed_overlay_textures_.erase(displayed_overlay_textures_.begin());
- }
-
- size_t query_texture_count = swapped_and_acked_overlay_resources_.size() +
- awaiting_release_overlay_textures_.size();
- if (query_texture_count) {
- std::vector<uint32_t> query_texture_ids;
- query_texture_ids.reserve(query_texture_count);
-
- for (auto& pair : swapped_and_acked_overlay_resources_)
- query_texture_ids.push_back(pair.first);
- for (auto& overlay : awaiting_release_overlay_textures_)
- query_texture_ids.push_back(overlay->texture.id());
-
- // We query for *all* outstanding texture ids, even if we previously
- // queried, as we will not hear back about things becoming available
- // until after we query again.
- gl_->ScheduleCALayerInUseQueryCHROMIUM(query_texture_count,
- query_texture_ids.data());
- }
- } else {
- // If a query is not needed to release the overlay buffers, we can assume
- // that once a swap buffer has completed we can remove the oldest buffers
- // from the queue, but only once we've swapped another frame afterward.
- if (swapping_overlay_resources_.size() > 1) {
- auto& read_lock_release_fence_overlay_locks =
- read_lock_release_fence_overlay_locks_.emplace_back();
- if (!release_fence.is_null()) {
- auto read_lock_iter = std::partition(
- swapping_overlay_resources_.front().begin(),
- swapping_overlay_resources_.front().end(),
- [](auto& lock) { return !lock->HasReadLockFence(); });
- read_lock_release_fence_overlay_locks.insert(
- read_lock_release_fence_overlay_locks.end(),
- std::make_move_iterator(read_lock_iter),
- std::make_move_iterator(swapping_overlay_resources_.front().end()));
- }
-
- DisplayResourceProviderGL::ScopedBatchReturnResources returner(
- resource_provider());
- swapping_overlay_resources_.pop_front();
- }
- // If |displayed_overlay_textures_| has a non-empty member that means we're
- // sending RenderPassDrawQuads as an overlay. This is only supported for
- // CALayers now, where |release_overlay_resources_after_gpu_query| will be
- // true. In order to support them here, the OverlayTextures would need to
- // move to |awaiting_release_overlay_textures_| and stay there until the
- // ResourceFence that was in use for the frame they were submitted is
- // passed.
- DCHECK(displayed_overlay_textures_.empty());
- }
-}
-
-void GLRenderer::BuffersPresented() {
- if (!read_lock_release_fence_overlay_locks_.empty())
- read_lock_release_fence_overlay_locks_.pop_front();
-}
-
-void GLRenderer::DidReceiveTextureInUseResponses(
- const gpu::TextureInUseResponses& responses) {
- DCHECK(settings_->release_overlay_resources_after_gpu_query);
- DisplayResourceProviderGL::ScopedBatchReturnResources returner(
- resource_provider());
- for (const gpu::TextureInUseResponse& response : responses) {
- if (response.in_use)
- continue;
-
- // Returned texture ids may be for resources from clients of the
- // display compositor, in |swapped_and_acked_overlay_resources_|. In that
- // case we remove the lock from the map, allowing them to be returned to the
- // client if the resource has been deleted from the
- // DisplayResourceProviderGL.
- if (swapped_and_acked_overlay_resources_.erase(response.texture))
- continue;
- // If not, then they would be a RenderPass copy texture, which is held in
- // |awaiting_release_overlay_textures_|. We move it back to the available
- // texture list to use it for the next frame.
- auto it = std::find_if(
- awaiting_release_overlay_textures_.begin(),
- awaiting_release_overlay_textures_.end(),
- [&response](const std::unique_ptr<OverlayTexture>& overlay) {
- return overlay->texture.id() == response.texture;
- });
- if (it != awaiting_release_overlay_textures_.end()) {
- // Mark the OverlayTexture as newly returned to the available set.
- (*it)->frames_waiting_for_reuse = 0;
- available_overlay_textures_.push_back(std::move(*it));
- awaiting_release_overlay_textures_.erase(it);
- }
- }
-}
-
-void GLRenderer::BindFramebufferToOutputSurface() {
- current_framebuffer_texture_ = nullptr;
- output_surface_->BindFramebuffer();
- tint_gl_composited_content_ = debug_settings_->tint_composited_content;
- if (overdraw_feedback_) {
- // Output surfaces that require an external stencil test should not allow
- // overdraw feedback by setting |supports_stencil| to false.
- DCHECK(!output_surface_->HasExternalStencilTest());
- SetupOverdrawFeedback();
- SetStencilEnabled(true);
- } else if (output_surface_->HasExternalStencilTest()) {
- output_surface_->ApplyExternalStencil();
- SetStencilEnabled(true);
- } else {
- SetStencilEnabled(false);
- }
-}
-
-void GLRenderer::BindFramebufferToTexture(
- const AggregatedRenderPassId render_pass_id) {
- tint_gl_composited_content_ = false;
- gl_->BindFramebuffer(GL_FRAMEBUFFER, offscreen_framebuffer_id_);
-
- auto contents_texture_it = render_pass_textures_.find(render_pass_id);
- current_framebuffer_texture_ = &contents_texture_it->second;
- GLuint texture_id = current_framebuffer_texture_->id();
- DCHECK(texture_id);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- texture_id, 0);
- if (overdraw_feedback_) {
- if (!offscreen_stencil_renderbuffer_id_)
- gl_->GenRenderbuffers(1, &offscreen_stencil_renderbuffer_id_);
- if (current_framebuffer_texture_->size() !=
- offscreen_stencil_renderbuffer_size_) {
- gl_->BindRenderbuffer(GL_RENDERBUFFER,
- offscreen_stencil_renderbuffer_id_);
- gl_->RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
- current_framebuffer_texture_->size().width(),
- current_framebuffer_texture_->size().height());
- gl_->BindRenderbuffer(GL_RENDERBUFFER, 0);
- offscreen_stencil_renderbuffer_size_ =
- current_framebuffer_texture_->size();
- }
- gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER,
- offscreen_stencil_renderbuffer_id_);
- }
-
-#if EXPENSIVE_DCHECKS_ARE_ON()
- DCHECK(gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
- GL_FRAMEBUFFER_COMPLETE ||
- IsContextLost());
-#endif // EXPENSIVE_DCHECKS_ARE_ON()
-
- if (overdraw_feedback_) {
- SetupOverdrawFeedback();
- SetStencilEnabled(true);
- } else {
- SetStencilEnabled(false);
- }
-}
-
-void GLRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) {
- EnsureScissorTestEnabled();
-
- // Don't unnecessarily ask the context to change the scissor, because it
- // may cause undesired GPU pipeline flushes.
- if (scissor_rect == scissor_rect_)
- return;
-
- scissor_rect_ = scissor_rect;
- FlushTextureQuadCache(SHARED_BINDING);
- gl_->Scissor(scissor_rect.x(), scissor_rect.y(), scissor_rect.width(),
- scissor_rect.height());
-}
-
-void GLRenderer::SetViewport() {
- gl_->Viewport(current_window_space_viewport_.x(),
- current_window_space_viewport_.y(),
- current_window_space_viewport_.width(),
- current_window_space_viewport_.height());
-}
-
-void GLRenderer::InitializeSharedObjects() {
- TRACE_EVENT0("viz", "GLRenderer::InitializeSharedObjects");
-
- // Create an FBO for doing offscreen rendering.
- gl_->GenFramebuffers(1, &offscreen_framebuffer_id_);
-
- shared_geometry_ =
- std::make_unique<StaticGeometryBinding>(gl_, QuadVertexRect());
- clipped_geometry_ = std::make_unique<DynamicGeometryBinding>(gl_);
-}
-
-void GLRenderer::PrepareGeometry(BoundGeometry binding) {
- if (binding == bound_geometry_) {
- return;
- }
-
- switch (binding) {
- case SHARED_BINDING:
- shared_geometry_->PrepareForDraw();
- break;
- case CLIPPED_BINDING:
- clipped_geometry_->PrepareForDraw();
- break;
- case NO_BINDING:
- break;
- }
- bound_geometry_ = binding;
-}
-
-void GLRenderer::SetUseProgram(const ProgramKey& program_key_no_color,
- const gfx::ColorSpace& src_color_space,
- const gfx::ColorSpace& dst_color_space,
- bool adjust_src_white_level,
- absl::optional<gfx::HDRMetadata> hdr_metadata) {
- DCHECK(dst_color_space.IsValid());
- gfx::ColorSpace adjusted_src_color_space = src_color_space;
- if (adjust_src_white_level && src_color_space.IsHDR()) {
- // TODO(b/183236148): consider using the destination's HDR static metadata
- // in current_frame()->display_color_spaces.hdr_static_metadata() and the
- // color volume metadata in |src_hdr_metadata| for the tone mapping; e.g.
- // the content might be mastered in 0-1000 nits but the display only be able
- // to represent 0 to 500.
- adjusted_src_color_space = src_color_space.GetWithSDRWhiteLevel(
- current_frame()->display_color_spaces.GetSDRMaxLuminanceNits());
- }
-
- ProgramKey program_key = program_key_no_color;
- const gfx::ColorTransform* color_transform =
- GetColorTransform(adjusted_src_color_space, dst_color_space);
- program_key.SetColorTransform(color_transform);
-
- bool has_output_color_matrix = false;
- if (program_key.type() != ProgramType::PROGRAM_TYPE_SOLID_COLOR)
- has_output_color_matrix = HasOutputColorMatrix();
- program_key.set_has_output_color_matrix(has_output_color_matrix);
-
- // Create and set the program if needed.
- std::unique_ptr<Program>& program = program_cache_[program_key];
- if (!program) {
- program = std::make_unique<Program>();
- program->Initialize(output_surface_->context_provider(), program_key);
- }
- DCHECK(program);
- if (current_program_ != program.get()) {
- current_program_ = program.get();
- gl_->UseProgram(current_program_->program());
- }
- if (!current_program_->initialized()) {
- DCHECK(IsContextLost());
- return;
- }
-
- // Set uniforms that are common to all programs.
- if (current_program_->sampler_location() != -1)
- gl_->Uniform1i(current_program_->sampler_location(), 0);
- if (current_program_->viewport_location() != -1) {
- float viewport[4] = {
- static_cast<float>(current_window_space_viewport_.x()),
- static_cast<float>(current_window_space_viewport_.y()),
- static_cast<float>(current_window_space_viewport_.width()),
- static_cast<float>(current_window_space_viewport_.height()),
- };
- gl_->Uniform4fv(current_program_->viewport_location(), 1, viewport);
- }
-
- if (has_output_color_matrix) {
- DCHECK_NE(current_program_->output_color_matrix_location(), -1);
- float matrix[16];
- output_surface_->color_matrix().getColMajor(matrix);
- gl_->UniformMatrix4fv(current_program_->output_color_matrix_location(), 1,
- false, matrix);
- }
-}
-
-const Program* GLRenderer::GetProgramIfInitialized(
- const ProgramKey& desc) const {
- const auto found = program_cache_.find(desc);
- if (found == program_cache_.end())
- return nullptr;
- return found->second.get();
-}
-
-const gfx::ColorTransform* GLRenderer::GetColorTransform(
- const gfx::ColorSpace& src,
- const gfx::ColorSpace& dst) {
- ColorTransformKey key;
- key.src = src;
- key.dst = dst;
- key.sdr_max_luminance_nits =
- current_frame()->display_color_spaces.GetSDRMaxLuminanceNits();
- std::unique_ptr<gfx::ColorTransform>& transform = color_transform_cache_[key];
- if (!transform) {
- gfx::ColorTransform::Options options;
- options.tone_map_pq_and_hlg_to_sdr = !dst.IsHDR();
- options.sdr_max_luminance_nits = key.sdr_max_luminance_nits;
- transform = gfx::ColorTransform::NewColorTransform(src, dst, options);
- }
- return transform.get();
-}
-
-void GLRenderer::CleanupSharedObjects() {
- shared_geometry_ = nullptr;
-
- gl_->ReleaseShaderCompiler();
- for (auto& iter : program_cache_)
- iter.second->Cleanup(gl_);
- program_cache_.clear();
- color_transform_cache_.clear();
-
- if (offscreen_framebuffer_id_)
- gl_->DeleteFramebuffers(1, &offscreen_framebuffer_id_);
-
- if (offscreen_stencil_renderbuffer_id_)
- gl_->DeleteRenderbuffers(1, &offscreen_stencil_renderbuffer_id_);
-}
-
-void GLRenderer::ReinitializeGLState() {
- is_scissor_enabled_ = false;
- scissor_rect_ = gfx::Rect();
- stencil_shadow_ = false;
- blend_shadow_ = true;
- current_program_ = nullptr;
-
- RestoreGLState();
-}
-
-void GLRenderer::RestoreGLStateAfterSkia() {
- // After using Skia we need to disable vertex attributes we don't use
- int attribs_count = output_surface_->context_provider()
- ->ContextCapabilities()
- .max_vertex_attribs;
- for (int i = 0; i < attribs_count; i++)
- gl_->DisableVertexAttribArray(i);
-
- RestoreGLState();
-}
-
-void GLRenderer::RestoreGLState() {
- // This restores the current GLRenderer state to the GL context.
- bound_geometry_ = NO_BINDING;
- PrepareGeometry(SHARED_BINDING);
-
- gl_->Disable(GL_DEPTH_TEST);
- gl_->Disable(GL_CULL_FACE);
- gl_->ColorMask(true, true, true, true);
- gl_->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- gl_->ActiveTexture(GL_TEXTURE0);
-
- if (current_program_)
- gl_->UseProgram(current_program_->program());
-
- if (stencil_shadow_)
- gl_->Enable(GL_STENCIL_TEST);
- else
- gl_->Disable(GL_STENCIL_TEST);
-
- if (blend_shadow_)
- gl_->Enable(GL_BLEND);
- else
- gl_->Disable(GL_BLEND);
-
- if (is_scissor_enabled_)
- gl_->Enable(GL_SCISSOR_TEST);
- else
- gl_->Disable(GL_SCISSOR_TEST);
-
- gl_->Scissor(scissor_rect_.x(), scissor_rect_.y(), scissor_rect_.width(),
- scissor_rect_.height());
-}
-
-bool GLRenderer::IsContextLost() {
- return gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR;
-}
-
-#if BUILDFLAG(IS_APPLE)
-void GLRenderer::ScheduleCALayers() {
- // The use of OverlayTextures for RenderPasses is only supported on the code
- // paths for |release_overlay_resources_after_gpu_query| at the moment. See
- // SwapBuffersComplete for notes on the missing support for other paths. This
- // method uses ScheduleRenderPassDrawQuad to send RenderPass outputs as
- // overlays, so it can only be used because this setting is true.
- if (!settings_->release_overlay_resources_after_gpu_query)
- return;
-
- scoped_refptr<CALayerOverlaySharedState> shared_state;
-
- for (const CALayerOverlay& ca_layer_overlay : current_frame()->overlay_list) {
- if (ca_layer_overlay.rpdq) {
- std::unique_ptr<OverlayTexture> overlay_texture =
- ScheduleRenderPassDrawQuad(&ca_layer_overlay);
- if (overlay_texture)
- awaiting_swap_overlay_textures_.push_back(std::move(overlay_texture));
- shared_state = nullptr;
- continue;
- }
-
- ResourceId contents_resource_id = ca_layer_overlay.contents_resource_id;
- unsigned texture_id = 0;
- if (contents_resource_id) {
- pending_overlay_resources_.push_back(
- std::make_unique<DisplayResourceProviderGL::ScopedOverlayLockGL>(
- resource_provider(), contents_resource_id));
- texture_id = pending_overlay_resources_.back()->texture_id();
- }
- GLfloat contents_rect[4] = {
- ca_layer_overlay.contents_rect.x(), ca_layer_overlay.contents_rect.y(),
- ca_layer_overlay.contents_rect.width(),
- ca_layer_overlay.contents_rect.height(),
- };
- GLfloat bounds_rect[4] = {
- ca_layer_overlay.bounds_rect.x(), ca_layer_overlay.bounds_rect.y(),
- ca_layer_overlay.bounds_rect.width(),
- ca_layer_overlay.bounds_rect.height(),
- };
- GLboolean is_clipped = ca_layer_overlay.shared_state->is_clipped;
- GLfloat clip_rect[4] = {ca_layer_overlay.shared_state->clip_rect.x(),
- ca_layer_overlay.shared_state->clip_rect.y(),
- ca_layer_overlay.shared_state->clip_rect.width(),
- ca_layer_overlay.shared_state->clip_rect.height()};
-
- const gfx::RectF& rect =
- ca_layer_overlay.shared_state->rounded_corner_bounds.rect();
- GLfloat rounded_corner_bounds[5] = {
- rect.x(), rect.y(), rect.width(), rect.height(),
- ca_layer_overlay.shared_state->rounded_corner_bounds.GetSimpleRadius()};
-
- GLint sorting_context_id =
- ca_layer_overlay.shared_state->sorting_context_id;
- GLfloat transform[16];
- ca_layer_overlay.shared_state->transform.matrix().getColMajor(transform);
- unsigned filter = ca_layer_overlay.filter;
-
- if (ca_layer_overlay.shared_state != shared_state) {
- shared_state = ca_layer_overlay.shared_state;
- gl_->ScheduleCALayerSharedStateCHROMIUM(
- ca_layer_overlay.shared_state->opacity, is_clipped, clip_rect,
- rounded_corner_bounds, sorting_context_id, transform);
- }
- gl_->ScheduleCALayerCHROMIUM(
- texture_id, contents_rect, ca_layer_overlay.background_color,
- ca_layer_overlay.edge_aa_mask, bounds_rect, filter);
- }
-
- ReduceAvailableOverlayTextures();
-}
-#endif // BUILDFLAG(IS_APPLE)
-
-#if BUILDFLAG(IS_WIN)
-void GLRenderer::ScheduleDCLayers() {
- for (DCLayerOverlay& dc_layer_overlay : current_frame()->overlay_list) {
- DCHECK_EQ(DCLayerOverlay::kNumResources, 2u);
- GLuint texture_ids[DCLayerOverlay::kNumResources] = {};
- for (size_t i = 0; i < DCLayerOverlay::kNumResources; i++) {
- ResourceId resource_id = dc_layer_overlay.resources[i];
- if (resource_id == kInvalidResourceId)
- break;
- pending_overlay_resources_.push_back(
- std::make_unique<DisplayResourceProviderGL::ScopedOverlayLockGL>(
- resource_provider(), resource_id));
- texture_ids[i] = pending_overlay_resources_.back()->texture_id();
- }
- DCHECK(texture_ids[0]);
- // TODO(sunnyps): Set color space in renderer like we do for tiles.
- gl_->SetColorSpaceMetadataCHROMIUM(
- texture_ids[0], dc_layer_overlay.color_space.AsGLColorSpace());
-
- int z_order = dc_layer_overlay.z_order;
- const gfx::Rect& content_rect = dc_layer_overlay.content_rect;
- const gfx::Rect& quad_rect = dc_layer_overlay.quad_rect;
- DCHECK(dc_layer_overlay.transform.IsFlat());
- const auto& matrix = dc_layer_overlay.transform.matrix();
- bool is_clipped = dc_layer_overlay.clip_rect.has_value();
- const gfx::Rect& clip_rect =
- dc_layer_overlay.clip_rect.value_or(gfx::Rect());
- unsigned protected_video_type =
- static_cast<unsigned>(dc_layer_overlay.protected_video_type);
-
- gl_->ScheduleDCLayerCHROMIUM(
- texture_ids[0], texture_ids[1], z_order, content_rect.x(),
- content_rect.y(), content_rect.width(), content_rect.height(),
- quad_rect.x(), quad_rect.y(), quad_rect.width(), quad_rect.height(),
- matrix.rc(0, 0), matrix.rc(0, 1), matrix.rc(1, 0), matrix.rc(1, 1),
- matrix.rc(0, 3), matrix.rc(1, 3), is_clipped, clip_rect.x(),
- clip_rect.y(), clip_rect.width(), clip_rect.height(),
- protected_video_type);
- }
-}
-#endif // BUILDFLAG(IS_WIN)
-
-#if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE)
-void GLRenderer::ScheduleOverlays() {
- if (current_frame()->overlay_list.empty())
- return;
-
- OverlayCandidateList& overlays = current_frame()->overlay_list;
- for (const auto& overlay_candidate : overlays) {
- pending_overlay_resources_.push_back(
- std::make_unique<DisplayResourceProviderGL::ScopedOverlayLockGL>(
- resource_provider(), overlay_candidate.resource_id));
- unsigned texture_id = pending_overlay_resources_.back()->texture_id();
-
- context_support_->ScheduleOverlayPlane(
- overlay_candidate.plane_z_order, overlay_candidate.transform,
- texture_id, ToNearestRect(overlay_candidate.display_rect),
- overlay_candidate.uv_rect, !overlay_candidate.is_opaque,
- overlay_candidate.gpu_fence_id);
- }
-}
-#endif // BUILDFLAG(IS_ANDROID) || defined(USE_OZONE)
-
-void GLRenderer::ScheduleOutputSurfaceAsOverlay() {
- if (!current_frame()->output_surface_plane)
- return;
-
- // Initialize correct values to use an output surface as overlay candidate.
- auto& overlay_candidate = *(current_frame()->output_surface_plane);
- unsigned texture_id = output_surface_->GetOverlayTextureId();
- DCHECK(texture_id || IsContextLost());
- // Output surface is also z-order 0.
- int plane_z_order = 0;
-
- context_support_->ScheduleOverlayPlane(
- plane_z_order, overlay_candidate.transform, texture_id,
- ToNearestRect(overlay_candidate.display_rect), overlay_candidate.uv_rect,
- overlay_candidate.enable_blending, overlay_candidate.gpu_fence_id);
-}
-
-#if BUILDFLAG(IS_APPLE)
-// This function draws the CompositorRenderPassDrawQuad into a temporary
-// texture/framebuffer, and then copies the result into an IOSurface. The
-// inefficient (but simple) way to do this would be to:
-// 1. Allocate a framebuffer the size of the screen.
-// 2. Draw using all the normal RPDQ draw logic.
-//
-// Instead, this method does the following:
-// 1. Configure parameters as if drawing to a framebuffer the size of the
-// screen. This reuses most of the RPDQ draw logic.
-// 2. Update parameters to draw into a framebuffer only as large as needed.
-// 3. Fix shader uniforms that were broken by (2).
-//
-// Then:
-// 4. Allocate an IOSurface as the drawing destination.
-// 5. Draw the RPDQ.
-void GLRenderer::CopyRenderPassDrawQuadToOverlayResource(
- const CALayerOverlay* ca_layer_overlay,
- std::unique_ptr<OverlayTexture>* overlay_texture,
- gfx::RectF* new_bounds) {
- // Don't carry over any GL state from previous RenderPass draw operations.
- ReinitializeGLState();
- auto contents_texture_it =
- render_pass_textures_.find(ca_layer_overlay->rpdq->render_pass_id);
- DCHECK(contents_texture_it != render_pass_textures_.end());
-
- // Configure parameters as if drawing to a framebuffer the size of the
- // screen.
- DrawRenderPassDrawQuadParams params;
- params.quad = ca_layer_overlay->rpdq;
- params.flip_texture = true;
- params.contents_texture = &contents_texture_it->second;
- params.quad_to_target_transform =
- params.quad->shared_quad_state->quad_to_target_transform;
- params.tex_coord_rect = params.quad->tex_coord_rect;
-
- // Calculate projection and window matrices using InitializeViewport(). This
- // requires creating a dummy DrawingFrame.
- {
- DrawingFrame dummy_frame;
- gfx::Rect frame_rect(current_frame()->device_viewport_size);
- force_drawing_frame_framebuffer_unflipped_ = true;
- InitializeViewport(&dummy_frame, frame_rect, frame_rect, frame_rect.size());
- force_drawing_frame_framebuffer_unflipped_ = false;
- params.projection_matrix = dummy_frame.projection_matrix;
- params.window_matrix = dummy_frame.window_matrix;
- }
-
- // Perform basic initialization with the screen-sized viewport.
- if (!InitializeRPDQParameters(&params))
- return;
-
- if (!UpdateRPDQWithSkiaFilters(&params))
- return;
-
- // |params.dst_rect| now contain values that reflect a potentially increased
- // size quad.
- gfx::RectF updated_dst_rect = params.dst_rect;
- gfx::Size dst_pixel_size = gfx::ToCeiledSize(updated_dst_rect.size());
-
- int iosurface_width = dst_pixel_size.width();
- int iosurface_height = dst_pixel_size.height();
- if (!settings_->dont_round_texture_sizes_for_pixel_tests) {
- // Round the size of the IOSurface to a multiple of 64 pixels. This reduces
- // memory fragmentation. https://crbug.com/146070. This also allows
- // IOSurfaces to be more easily reused during a resize operation.
- int iosurface_multiple = 64;
- iosurface_width =
- cc::MathUtil::CheckedRoundUp(iosurface_width, iosurface_multiple);
- iosurface_height =
- cc::MathUtil::CheckedRoundUp(iosurface_height, iosurface_multiple);
- }
-
- *overlay_texture =
- FindOrCreateOverlayTexture(params.quad->render_pass_id, iosurface_width,
- iosurface_height, RootRenderPassColorSpace());
- *new_bounds = gfx::RectF(updated_dst_rect.origin(),
- gfx::SizeF((*overlay_texture)->texture.size()));
-
- // Calculate new projection and window matrices for a minimally sized viewport
- // using InitializeViewport(). This requires creating a dummy DrawingFrame.
- {
- DrawingFrame dummy_frame;
- force_drawing_frame_framebuffer_unflipped_ = true;
- gfx::Rect frame_rect =
- gfx::Rect(0, 0, updated_dst_rect.width(), updated_dst_rect.height());
- InitializeViewport(&dummy_frame, frame_rect, frame_rect, frame_rect.size());
- force_drawing_frame_framebuffer_unflipped_ = false;
- params.projection_matrix = dummy_frame.projection_matrix;
- params.window_matrix = dummy_frame.window_matrix;
- }
-
- // Calculate a new quad_to_target_transform.
- params.quad_to_target_transform = gfx::Transform();
- params.quad_to_target_transform.Translate(-updated_dst_rect.x(),
- -updated_dst_rect.y());
-
- // Antialiasing works by fading out content that is close to the edge of the
- // viewport. All of these values need to be recalculated.
- if (params.use_aa) {
- current_window_space_viewport_ =
- gfx::Rect(0, 0, updated_dst_rect.width(), updated_dst_rect.height());
- gfx::Transform quad_rect_matrix;
- QuadRectTransform(&quad_rect_matrix, params.quad_to_target_transform,
- updated_dst_rect);
- params.contents_device_transform =
- params.window_matrix * params.projection_matrix * quad_rect_matrix;
- bool clipped = false;
- params.contents_device_transform.FlattenTo2d();
- gfx::QuadF device_layer_quad = cc::MathUtil::MapQuad(
- params.contents_device_transform, SharedGeometryQuad(), &clipped);
- LayerQuad device_layer_edges(device_layer_quad);
- InflateAntiAliasingDistances(device_layer_quad, &device_layer_edges,
- params.edge);
- }
-
- // Establish destination texture.
- GLuint temp_fbo;
- gl_->GenFramebuffers(1, &temp_fbo);
- gl_->BindFramebuffer(GL_FRAMEBUFFER, temp_fbo);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- (*overlay_texture)->texture.target(),
- (*overlay_texture)->texture.id(), 0);
- DCHECK(gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
- GL_FRAMEBUFFER_COMPLETE ||
- IsContextLost());
-
- // Clear to 0 to ensure the background is transparent.
- gl_->ClearColor(0, 0, 0, 0);
- gl_->Clear(GL_COLOR_BUFFER_BIT);
-
- UpdateRPDQTexturesForSampling(&params);
- UpdateRPDQBlendMode(&params);
- // The code in this method (CopyRenderPassDrawQuadToOverlayResource) is
- // only called when we are drawing for the purpose of copying to
- // a CALayerOverlay. In such cases, the CALayerOverlay applies rounded
- // corners via CALayer parameters, so the shader-based rounded corners
- // should be disabled here.
- params.apply_shader_based_rounded_corner = false;
- ChooseRPDQProgram(&params, (*overlay_texture)->texture.color_space());
- UpdateRPDQUniforms(&params);
-
- // Prior to drawing, set up the destination framebuffer and viewport.
- gl_->BindFramebuffer(GL_FRAMEBUFFER, temp_fbo);
- gl_->Viewport(0, 0, updated_dst_rect.width(), updated_dst_rect.height());
-
- DrawRPDQ(params);
- if (params.background_texture) {
- gl_->DeleteTextures(1, &params.background_texture);
- params.background_texture = 0;
- }
- gl_->DeleteFramebuffers(1, &temp_fbo);
-}
-
-std::unique_ptr<GLRenderer::OverlayTexture>
-GLRenderer::FindOrCreateOverlayTexture(
- const AggregatedRenderPassId& render_pass_id,
- int width,
- int height,
- const gfx::ColorSpace& color_space) {
- // First try to use a texture for the same CompositorRenderPassId, to keep
- // things more stable and less likely to clobber each others textures.
- auto match_with_id = [&](const std::unique_ptr<OverlayTexture>& overlay) {
- return overlay->render_pass_id == render_pass_id &&
- overlay->texture.size().width() >= width &&
- overlay->texture.size().height() >= height &&
- overlay->texture.size().width() <= width * 2 &&
- overlay->texture.size().height() <= height * 2;
- };
- auto it = std::find_if(available_overlay_textures_.begin(),
- available_overlay_textures_.end(), match_with_id);
- if (it != available_overlay_textures_.end()) {
- std::unique_ptr<OverlayTexture> result = std::move(*it);
- available_overlay_textures_.erase(it);
-
- result->render_pass_id = render_pass_id;
- return result;
- }
-
- // Then fallback to trying other textures that still match.
- auto match = [&](const std::unique_ptr<OverlayTexture>& overlay) {
- return overlay->texture.size().width() >= width &&
- overlay->texture.size().height() >= height &&
- overlay->texture.size().width() <= width * 2 &&
- overlay->texture.size().height() <= height * 2;
- };
- it = std::find_if(available_overlay_textures_.begin(),
- available_overlay_textures_.end(), match);
- if (it != available_overlay_textures_.end()) {
- std::unique_ptr<OverlayTexture> result = std::move(*it);
- available_overlay_textures_.erase(it);
-
- result->render_pass_id = render_pass_id;
- return result;
- }
-
- // Make a new texture if we could not find a match. Sadtimes.
- auto result = std::make_unique<OverlayTexture>();
- result->texture = ScopedGpuMemoryBufferTexture(
- output_surface_->context_provider(),
- gfx::Size(width, height), color_space);
- result->render_pass_id = render_pass_id;
- return result;
-}
-
-void GLRenderer::ReduceAvailableOverlayTextures() {
- // Overlay resources may get returned back to the compositor at varying rates,
- // so we may get a number of resources returned at once, then none for a
- // while. As such, we want to hold onto enough resources to not have to create
- // any when none are released for a while. Emperical study by erikchen@ on
- // crbug.com/636884 found that saving 5 spare textures per RenderPass was
- // sufficient for important benchmarks. This seems to imply that the OS may
- // hold up to 5 frames of textures before releasing them.
- static const int kKeepCountPerRenderPass = 5;
-
- // In order to accomodate the above requirements, we hold any released texture
- // in the |available_overlay_textures_| set for up to 5 frames before
- // discarding it.
- for (const auto& overlay : available_overlay_textures_)
- overlay->frames_waiting_for_reuse++;
- base::EraseIf(available_overlay_textures_,
- [](const std::unique_ptr<OverlayTexture>& overlay) {
- return overlay->frames_waiting_for_reuse >=
- kKeepCountPerRenderPass;
- });
-}
-
-std::unique_ptr<GLRenderer::OverlayTexture>
-GLRenderer::ScheduleRenderPassDrawQuad(const CALayerOverlay* ca_layer_overlay) {
- DCHECK(ca_layer_overlay->rpdq);
-
- std::unique_ptr<OverlayTexture> overlay_texture;
- gfx::RectF new_bounds;
- CopyRenderPassDrawQuadToOverlayResource(ca_layer_overlay, &overlay_texture,
- &new_bounds);
- if (!overlay_texture)
- return {};
-
- GLfloat contents_rect[4] = {
- ca_layer_overlay->contents_rect.x(), ca_layer_overlay->contents_rect.y(),
- ca_layer_overlay->contents_rect.width(),
- ca_layer_overlay->contents_rect.height(),
- };
- GLfloat bounds_rect[4] = {
- new_bounds.x(), new_bounds.y(), new_bounds.width(), new_bounds.height(),
- };
- GLboolean is_clipped = ca_layer_overlay->shared_state->is_clipped;
- GLfloat clip_rect[4] = {ca_layer_overlay->shared_state->clip_rect.x(),
- ca_layer_overlay->shared_state->clip_rect.y(),
- ca_layer_overlay->shared_state->clip_rect.width(),
- ca_layer_overlay->shared_state->clip_rect.height()};
-
- const gfx::RectF& rect =
- ca_layer_overlay->shared_state->rounded_corner_bounds.rect();
- GLfloat rounded_corner_rect[5] = {
- rect.x(), rect.y(), rect.width(), rect.height(),
- ca_layer_overlay->shared_state->rounded_corner_bounds.GetSimpleRadius()};
-
- GLint sorting_context_id = ca_layer_overlay->shared_state->sorting_context_id;
- GLfloat gl_transform[16];
- ca_layer_overlay->shared_state->transform.matrix().getColMajor(gl_transform);
- unsigned filter = ca_layer_overlay->filter;
-
- // The alpha has already been applied when copying the RPDQ to an IOSurface.
- GLfloat alpha = 1;
- gl_->ScheduleCALayerSharedStateCHROMIUM(alpha, is_clipped, clip_rect,
- rounded_corner_rect,
- sorting_context_id, gl_transform);
- gl_->ScheduleCALayerCHROMIUM(overlay_texture->texture.id(), contents_rect,
- ca_layer_overlay->background_color,
- ca_layer_overlay->edge_aa_mask, bounds_rect,
- filter);
- return overlay_texture;
-}
-#endif // BUILDFLAG(IS_APPLE)
-
-void GLRenderer::SetupOverdrawFeedback() {
- gl_->StencilFunc(GL_ALWAYS, 1, 0xffffffff);
- // First two values are ignored as test always passes.
- gl_->StencilOp(GL_KEEP, GL_KEEP, GL_INCR);
- gl_->StencilMask(0xffffffff);
-}
-
-void GLRenderer::FlushOverdrawFeedback(const gfx::Rect& output_rect) {
- DCHECK(stencil_shadow_);
-
- // Test only, keep everything.
- gl_->StencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
-
- EnsureScissorTestDisabled();
- SetBlendEnabled(true);
-
- PrepareGeometry(SHARED_BINDING);
-
- SetUseProgram(ProgramKey::DebugBorder(), gfx::ColorSpace::CreateSRGB(),
- CurrentRenderPassColorSpace());
-
- gfx::Transform render_matrix;
- render_matrix.Translate(0.5 * output_rect.width() + output_rect.x(),
- 0.5 * output_rect.height() + output_rect.y());
- render_matrix.Scale(output_rect.width(), output_rect.height());
- SetShaderMatrix(current_frame()->projection_matrix * render_matrix);
-
- // Produce hinting for the amount of overdraw on screen for each pixel by
- // drawing hint colors to the framebuffer based on the current stencil value.
- struct {
- int multiplier;
- GLenum func;
- GLint ref;
- SkColor color;
- } stencil_tests[] = {
- {1, GL_EQUAL, 2, 0x2f0000ff}, // Blue: Overdrawn once.
- {2, GL_EQUAL, 3, 0x2f00ff00}, // Green: Overdrawn twice.
- {3, GL_EQUAL, 4, 0x3fff0000}, // Pink: Overdrawn three times.
- {4, GL_LESS, 4, 0x7fff0000}, // Red: Overdrawn four or more times.
- };
-
- for (const auto& test : stencil_tests) {
- gl_->StencilFunc(test.func, test.ref, 0xffffffff);
- // Transparent color unless color-coding of overdraw is enabled.
- SetShaderColor(debug_settings_->show_overdraw_feedback ? test.color : 0,
- 1.f);
- gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
- }
-}
-
-void GLRenderer::ProcessOverdrawFeedback(base::CheckedNumeric<int> surface_area,
- unsigned occlusion_query) {
- unsigned result = 0;
- DCHECK(occlusion_query);
- gl_->GetQueryObjectuivEXT(occlusion_query, GL_QUERY_RESULT_EXT, &result);
- gl_->DeleteQueriesEXT(1, &occlusion_query);
-
- // Report GPU overdraw as a percentage of |surface_area|.
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"), "GPU Overdraw",
- (result * 100.0 /
- static_cast<int>(surface_area.ValueOrDefault(INT_MAX))));
-}
-
-void GLRenderer::UpdateRenderPassTextures(
- const AggregatedRenderPassList& render_passes_in_draw_order,
- const base::flat_map<AggregatedRenderPassId, RenderPassRequirements>&
- render_passes_in_frame) {
- // Collect RenderPass textures that should be deleted.
- std::vector<AggregatedRenderPassId> passes_to_delete;
- for (const auto& pair : render_pass_textures_) {
- auto render_pass_it = render_passes_in_frame.find(pair.first);
- if (render_pass_it == render_passes_in_frame.end()) {
- passes_to_delete.push_back(pair.first);
- continue;
- }
- const RenderPassRequirements& requirements = render_pass_it->second;
- const ScopedRenderPassTexture& texture = pair.second;
- bool size_appropriate =
- texture.size().width() >= requirements.size.width() &&
- texture.size().height() >= requirements.size.height();
- bool mipmap_appropriate = !requirements.generate_mipmap || texture.mipmap();
- if (!size_appropriate || !mipmap_appropriate)
- passes_to_delete.push_back(pair.first);
- }
- // Delete RenderPass textures from the previous frame that will not be used
- // again.
- for (auto& pass_to_delete : passes_to_delete) {
- auto rp_backdrop_texture_it =
- render_pass_backdrop_textures_.find(pass_to_delete);
- if (rp_backdrop_texture_it != render_pass_backdrop_textures_.end())
- render_pass_backdrop_textures_.erase(pass_to_delete);
- render_pass_textures_.erase(pass_to_delete);
- }
-}
-
-ResourceFormat GLRenderer::CurrentRenderPassResourceFormat() const {
- const auto& caps = output_surface_->context_provider()->ContextCapabilities();
- if (CurrentRenderPassColorSpace().IsHDR()) {
- // If a platform does not support half-float renderbuffers then it should
- // not should request HDR rendering.
- DCHECK(caps.texture_half_float_linear);
- DCHECK(caps.color_buffer_half_float_rgba);
- return RGBA_F16;
- }
- return PlatformColor::BestSupportedTextureFormat(caps);
-}
-
-bool GLRenderer::HasOutputColorMatrix() const {
- const bool is_root_render_pass =
- current_frame()->current_render_pass == current_frame()->root_render_pass;
- const SkM44& output_color_matrix = output_surface_->color_matrix();
- return is_root_render_pass && output_color_matrix != SkM44();
-}
-
-bool GLRenderer::CanUseFastSolidColorDraw(
- const SolidColorDrawQuad* quad) const {
- const SharedQuadState* sqs = quad->shared_quad_state;
-
- if (!use_fast_path_solid_color_quad_)
- return false;
-
- // Mask filters require blending with the background, which is not possible
- // with the glClear draw method.
- if (!sqs->mask_filter_info.IsEmpty())
- return false;
-
- // 3D transforms need vertex computation in 3D and cannot be handled using
- // glClear().
- if (!sqs->quad_to_target_transform.IsFlat())
- return false;
-
- // glClear ignores stencil buffer.
- if (stencil_shadow_)
- return false;
-
- // Any non axis aligned transform cannot be handled by glClear.
- if (!sqs->quad_to_target_transform.Preserves2dAxisAlignment())
- return false;
-
- // When no blending is needed, glClear can be used.
- SkBlendMode blend_mode = quad->shared_quad_state->blend_mode;
- if (blend_mode == SkBlendMode::kSrc)
- return true;
-
- if (blend_mode == SkBlendMode::kSrcOver) {
- // Blending will replace destination color and alpha if the quad is opaque.
- if (SkColorGetA(quad->color) == 255 &&
- quad->shared_quad_state->opacity >= 1.0f) {
- return true;
- }
-
- // It is safe to use glClearColor with alpha blending when the render
- // pass has transparent background and nothing has drawn to the same rect
- // area because the blending happens against (0, 0, 0, 0) which is the same
- // as replacing the destination color & alpha.
- if (!current_frame()->current_render_pass->has_transparent_background)
- return false;
-
- gfx::RectF quad_rect_in_target(quad->visible_rect);
- sqs->quad_to_target_transform.TransformRect(&quad_rect_in_target);
- const gfx::Rect quad_rect_in_target_rounded =
- gfx::ToRoundedRect(quad_rect_in_target);
-
- // If the quad does not intersect any region that has already been drawn
- // to, then blending is not an issue and fast draw path can be used.
- for (const auto& rect : drawn_rects_)
- if (quad_rect_in_target_rounded.Intersects(rect))
- return false;
- return true;
- }
-
- return false;
-}
-
-void GLRenderer::AllocateRenderPassResourceIfNeeded(
- const AggregatedRenderPassId& render_pass_id,
- const RenderPassRequirements& requirements) {
- auto contents_texture_it = render_pass_textures_.find(render_pass_id);
- if (contents_texture_it != render_pass_textures_.end()) {
- DCHECK(gfx::Rect(contents_texture_it->second.size())
- .Contains(gfx::Rect(requirements.size)));
- return;
- }
-
- ScopedRenderPassTexture contents_texture(
- output_surface_->context_provider(), requirements.size,
- CurrentRenderPassResourceFormat(), CurrentRenderPassColorSpace(),
- requirements.generate_mipmap);
- render_pass_textures_[render_pass_id] = std::move(contents_texture);
-}
-
-bool GLRenderer::IsRenderPassResourceAllocated(
- const AggregatedRenderPassId& render_pass_id) const {
- auto texture_it = render_pass_textures_.find(render_pass_id);
- return texture_it != render_pass_textures_.end();
-}
-
-gfx::Size GLRenderer::GetRenderPassBackingPixelSize(
- const AggregatedRenderPassId& render_pass_id) {
- auto texture_it = render_pass_textures_.find(render_pass_id);
- DCHECK(texture_it != render_pass_textures_.end());
- return texture_it->second.size();
-}
-
-GLRenderer::OverlayTexture::OverlayTexture() = default;
-GLRenderer::OverlayTexture::~OverlayTexture() = default;
-
-bool GLRenderer::ColorTransformKey::operator==(
- const ColorTransformKey& other) const {
- return src == other.src && dst == other.dst &&
- sdr_max_luminance_nits == other.sdr_max_luminance_nits;
-}
-
-bool GLRenderer::ColorTransformKey::operator!=(
- const ColorTransformKey& other) const {
- return !(*this == other);
-}
-
-bool GLRenderer::ColorTransformKey::operator<(
- const ColorTransformKey& other) const {
- return std::tie(src, dst, sdr_max_luminance_nits) <
- std::tie(other.src, other.dst, other.sdr_max_luminance_nits);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/gl_renderer.h b/chromium/components/viz/service/display/gl_renderer.h
deleted file mode 100644
index f3106e13715..00000000000
--- a/chromium/components/viz/service/display/gl_renderer.h
+++ /dev/null
@@ -1,525 +0,0 @@
-// Copyright 2010 The Chromium 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 COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "base/cancelable_callback.h"
-#include "base/containers/circular_deque.h"
-#include "base/containers/queue.h"
-#include "base/memory/raw_ptr.h"
-#include "build/build_config.h"
-#include "components/viz/common/gpu/context_cache_controller.h"
-#include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
-#include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
-#include "components/viz/common/quads/debug_border_draw_quad.h"
-#include "components/viz/common/quads/solid_color_draw_quad.h"
-#include "components/viz/common/quads/tile_draw_quad.h"
-#include "components/viz/common/quads/yuv_video_draw_quad.h"
-#include "components/viz/service/display/direct_renderer.h"
-#include "components/viz/service/display/display_resource_provider_gl.h"
-#include "components/viz/service/display/gl_renderer_copier.h"
-#include "components/viz/service/display/gl_renderer_draw_cache.h"
-#include "components/viz/service/display/program_binding.h"
-#include "components/viz/service/display/scoped_gpu_memory_buffer_texture.h"
-#include "components/viz/service/display/sync_query_collection.h"
-#include "components/viz/service/display/texture_deleter.h"
-#include "components/viz/service/viz_service_export.h"
-#include "ui/gfx/geometry/quad_f.h"
-#include "ui/gfx/gpu_fence_handle.h"
-#include "ui/latency/latency_info.h"
-
-#if BUILDFLAG(IS_APPLE)
-#include "components/viz/service/display/ca_layer_overlay.h"
-#endif
-
-#if BUILDFLAG(IS_WIN)
-#include "components/viz/service/display/dc_layer_overlay.h"
-#endif
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-}
-} // namespace gpu
-
-namespace gfx {
-class RRectF;
-}
-
-namespace viz {
-
-class DynamicGeometryBinding;
-class GLRendererShaderTest;
-class OutputSurface;
-class ScopedRenderPassTexture;
-class StaticGeometryBinding;
-class StreamVideoDrawQuad;
-class TextureDrawQuad;
-
-// Class that handles drawing of composited render layers using GL.
-class VIZ_SERVICE_EXPORT GLRenderer : public DirectRenderer {
- public:
- class ScopedUseGrContext;
-
- GLRenderer(const RendererSettings* settings,
- const DebugRendererSettings* debug_settings,
- OutputSurface* output_surface,
- DisplayResourceProviderGL* resource_provider,
- OverlayProcessorInterface* overlay_processor,
- scoped_refptr<base::SingleThreadTaskRunner> current_task_runner);
-
- GLRenderer(const GLRenderer&) = delete;
- GLRenderer& operator=(const GLRenderer&) = delete;
-
- ~GLRenderer() override;
-
- bool use_swap_with_bounds() const { return use_swap_with_bounds_; }
-
- void SwapBuffers(SwapFrameData swap_frame_data) override;
- void SwapBuffersSkipped() override;
- void SwapBuffersComplete(gfx::GpuFenceHandle release_fence) override;
- void BuffersPresented() override;
-
- void DidReceiveTextureInUseResponses(
- const gpu::TextureInUseResponses& responses) override;
-
- virtual bool IsContextLost();
-
- protected:
- void DidChangeVisibility() override;
-
- bool stencil_enabled() const { return stencil_shadow_; }
-
- bool CanPartialSwap() override;
- void UpdateRenderPassTextures(
- const AggregatedRenderPassList& render_passes_in_draw_order,
- const base::flat_map<AggregatedRenderPassId, RenderPassRequirements>&
- render_passes_in_frame) override;
- void AllocateRenderPassResourceIfNeeded(
- const AggregatedRenderPassId& render_pass_id,
- const RenderPassRequirements& requirements) override;
- bool IsRenderPassResourceAllocated(
- const AggregatedRenderPassId& render_pass_id) const override;
- gfx::Size GetRenderPassBackingPixelSize(
- const AggregatedRenderPassId& render_pass_id) override;
- void BindFramebufferToOutputSurface() override;
- void BindFramebufferToTexture(
- const AggregatedRenderPassId render_pass_id) override;
- void SetScissorTestRect(const gfx::Rect& scissor_rect) override;
- void PrepareSurfaceForPass(SurfaceInitializationMode initialization_mode,
- const gfx::Rect& render_pass_scissor) override;
- void DoDrawQuad(const class DrawQuad*,
- const gfx::QuadF* draw_region) override;
- void BeginDrawingFrame() override;
- void FlushOverdrawFeedback(const gfx::Rect& output_rect) override;
- void FinishDrawingFrame() override;
- bool FlippedFramebuffer() const override;
- bool FlippedRootFramebuffer() const;
- void EnsureScissorTestEnabled() override;
- void EnsureScissorTestDisabled() override;
- void CopyDrawnRenderPass(const copy_output::RenderPassGeometry& geometry,
- std::unique_ptr<CopyOutputRequest> request) override;
- void FinishDrawingQuadList() override;
- void GenerateMipmap() override;
-
- // Returns true if quad requires antialiasing and false otherwise.
- static bool ShouldAntialiasQuad(const gfx::QuadF& device_layer_quad,
- bool clipped,
- bool force_aa);
-
- // Inflate the quad and fill edge array for fragment shader.
- // |local_quad| is set to inflated quad. |edge| array is filled with
- // inflated quad's edge data.
- static void SetupQuadForClippingAndAntialiasing(
- const gfx::Transform& device_transform,
- const DrawQuad* quad,
- const gfx::QuadF* device_layer_quad,
- const gfx::QuadF* clip_region,
- gfx::QuadF* local_quad,
- float edge[24]);
- static void SetupRenderPassQuadForClippingAndAntialiasing(
- const gfx::Transform& device_transform,
- const AggregatedRenderPassDrawQuad* quad,
- const gfx::QuadF* device_layer_quad,
- const gfx::QuadF* clip_region,
- gfx::QuadF* local_quad,
- float edge[24]);
-
- private:
- friend class GLRendererCopierPixelTest;
- friend class GLRendererShaderPixelTest;
- friend class GLRendererShaderTest;
- friend class GLRendererTest;
-
- using OverlayResourceLock =
- std::unique_ptr<DisplayResourceProviderGL::ScopedOverlayLockGL>;
- using OverlayResourceLockList = std::vector<OverlayResourceLock>;
-
- // If a RenderPass is used as an overlay, we render the RenderPass with any
- // effects into a texture for overlay use. We must keep the texture alive past
- // the execution of SwapBuffers, and such textures are more expensive to make
- // so we want to reuse them.
- struct OverlayTexture {
- OverlayTexture();
- ~OverlayTexture();
-
- AggregatedRenderPassId render_pass_id;
- ScopedGpuMemoryBufferTexture texture;
- int frames_waiting_for_reuse = 0;
- };
-
- struct DrawRenderPassDrawQuadParams;
-
- // Returns the format to use for storage if copying from the current
- // framebuffer. If the root renderpass is current, it uses the best matching
- // format from the OutputSurface, otherwise it uses the best matching format
- // from the texture being drawn to as the backbuffer.
- GLenum GetFramebufferCopyTextureFormat();
- void ReleaseRenderPassTextures();
- enum BoundGeometry { NO_BINDING, SHARED_BINDING, CLIPPED_BINDING };
- void PrepareGeometry(BoundGeometry geometry_to_bind);
- void SetStencilEnabled(bool enabled);
- void SetBlendEnabled(bool enabled);
- bool blend_enabled() const { return blend_shadow_; }
-
- // If any of the following functions returns false, then it means that drawing
- // is not possible.
- bool InitializeRPDQParameters(DrawRenderPassDrawQuadParams* params);
- void UpdateRPDQShadersForBlending(DrawRenderPassDrawQuadParams* params);
- bool UpdateRPDQWithSkiaFilters(DrawRenderPassDrawQuadParams* params);
- void UpdateRPDQTexturesForSampling(DrawRenderPassDrawQuadParams* params);
- void UpdateRPDQBlendMode(DrawRenderPassDrawQuadParams* params);
- void ChooseRPDQProgram(DrawRenderPassDrawQuadParams* params,
- const gfx::ColorSpace& target_color_space);
- void UpdateRPDQUniforms(DrawRenderPassDrawQuadParams* params);
- void DrawRPDQ(const DrawRenderPassDrawQuadParams& params);
-
- static void ToGLMatrix(float* gl_matrix, const gfx::Transform& transform);
-
- void DiscardPixels();
- void ClearFramebuffer();
- void SetViewport();
-
- void DrawDebugBorderQuad(const DebugBorderDrawQuad* quad);
- static bool IsDefaultBlendMode(SkBlendMode blend_mode) {
- return blend_mode == SkBlendMode::kSrcOver;
- }
- bool CanApplyBlendModeUsingBlendFunc(SkBlendMode blend_mode);
- void ApplyBlendModeUsingBlendFunc(SkBlendMode blend_mode);
- void RestoreBlendFuncToDefault(SkBlendMode blend_mode);
-
- // Returns the rect that should be sampled from the backdrop texture to be
- // backdrop filtered. This rect lives in window pixel space. The
- // |backdrop_filter_bounds| output lives in the space of the output rect
- // returned by this function. It will be used to clip the sampled backdrop
- // texture. The |unclipped_rect| output is the unclipped (full) rect that the
- // backdrop_filter should be applied to, in window pixel space.
- gfx::Rect GetBackdropBoundingBoxForRenderPassQuad(
- DrawRenderPassDrawQuadParams* params,
- gfx::Transform* backdrop_filter_bounds_transform,
- absl::optional<gfx::RRectF>* backdrop_filter_bounds,
- gfx::Rect* unclipped_rect) const;
-
- // Allocates and returns a texture id that contains a copy of the contents
- // of the current RenderPass being drawn.
- uint32_t GetBackdropTexture(const gfx::Rect& window_rect,
- float scale,
- GLenum* internal_format);
-
- static bool ShouldApplyBackdropFilters(
- const DrawRenderPassDrawQuadParams* params);
-
- // Applies the backdrop filters to the backdrop that has been painted to this
- // point, and returns it as an SkImage. Any opacity and/or "regular"
- // (non-backdrop) filters will also be applied directly to the backdrop-
- // filtered image at this point, so that the final result is as if the
- // filtered backdrop image was painted as the starting point for this new
- // stacking context, which would then be painted into its parent with opacity
- // and filters applied. This is an approximation, but it should be close
- // enough.
- sk_sp<SkImage> ApplyBackdropFilters(
- DrawRenderPassDrawQuadParams* params,
- const gfx::Rect& unclipped_rect,
- const absl::optional<gfx::RRectF>& backdrop_filter_bounds,
- const gfx::Transform& backdrop_filter_bounds_transform);
-
- // gl_renderer can bypass TileDrawQuads that fill the RenderPass
- const DrawQuad* CanPassBeDrawnDirectly(
- const AggregatedRenderPass* pass) override;
-
- void DrawRenderPassQuad(const AggregatedRenderPassDrawQuad* quadi,
- const gfx::QuadF* clip_region);
- void DrawRenderPassQuadInternal(DrawRenderPassDrawQuadParams* params);
- void DrawSolidColorQuad(const SolidColorDrawQuad* quad,
- const gfx::QuadF* clip_region);
- void DrawStreamVideoQuad(const StreamVideoDrawQuad* quad,
- const gfx::QuadF* clip_region);
- void EnqueueTextureQuad(const TextureDrawQuad* quad,
- const gfx::QuadF* clip_region);
- void FlushTextureQuadCache(BoundGeometry flush_binding);
- void DrawTileQuad(const TileDrawQuad* quad, const gfx::QuadF* clip_region);
- void DrawContentQuad(const ContentDrawQuadBase* quad,
- ResourceId resource_id,
- const gfx::QuadF* clip_region);
- void DrawContentQuadAA(const ContentDrawQuadBase* quad,
- ResourceId resource_id,
- const gfx::Transform& device_transform,
- const gfx::QuadF& aa_quad,
- const gfx::QuadF* clip_region);
- void DrawContentQuadNoAA(const ContentDrawQuadBase* quad,
- ResourceId resource_id,
- const gfx::QuadF* clip_region);
- void DrawYUVVideoQuad(const YUVVideoDrawQuad* quad,
- const gfx::QuadF* clip_region);
-
- void SetShaderOpacity(float opacity);
- void SetShaderQuadF(const gfx::QuadF& quad);
- void SetShaderMatrix(const gfx::Transform& transform);
- void SetShaderColor(SkColor color, float opacity);
- void SetShaderRoundedCorner(const gfx::RRectF& rounded_corner_bounds,
- const gfx::Transform& screen_transform);
- void DrawQuadGeometryClippedByQuadF(const gfx::Transform& draw_transform,
- const gfx::RectF& quad_rect,
- const gfx::QuadF& clipping_region_quad,
- const float uv[8]);
- void DrawQuadGeometry(const gfx::Transform& projection_matrix,
- const gfx::Transform& draw_transform,
- const gfx::RectF& quad_rect);
- void DrawQuadGeometryWithAA(const DrawQuad* quad,
- gfx::QuadF* local_quad,
- const gfx::Rect& tile_rect);
-
- const gfx::QuadF& SharedGeometryQuad() const { return shared_geometry_quad_; }
- const StaticGeometryBinding* SharedGeometry() const {
- return shared_geometry_.get();
- }
-
- // If |dst_color_space| is invalid, then no color conversion (apart from YUV
- // to RGB conversion) is performed. This explicit argument is available so
- // that video color conversion can be enabled separately from general color
- // conversion. If |adjust_src_white_level| is true, then the |src_color_space|
- // white levels are adjusted to the display SDR white level so that no white
- // level scaling happens. |src_hdr_metadata|, if available, is the mastering
- // metadata associated to the source quad.
- void SetUseProgram(
- const ProgramKey& program_key,
- const gfx::ColorSpace& src_color_space,
- const gfx::ColorSpace& dst_color_space,
- bool adjust_src_white_level = false,
- absl::optional<gfx::HDRMetadata> src_hdr_metadata = absl::nullopt);
-
- bool MakeContextCurrent();
-
- void InitializeSharedObjects();
- void CleanupSharedObjects();
-
- void ReinitializeGLState();
- void RestoreGLState();
- void RestoreGLStateAfterSkia();
-
- // TODO(weiliangc): Once the overlay processor could schedule overlays, remove
- // these functions.
- // Sends over output surface information as it is a overlay plane. This is
- // used for BufferQueue. For non-BufferQueue cases, this function will do
- // nothing.
- void ScheduleOutputSurfaceAsOverlay();
- // Schedule overlays sends overlay candidate to the GPU.
-#if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE)
- void ScheduleOverlays();
-#elif BUILDFLAG(IS_APPLE)
- void ScheduleCALayers();
-
- // Schedules the |ca_layer_overlay|, which is guaranteed to have a non-null
- // |rpdq| parameter. Returns ownership of a GL texture that contains the
- // output of the CompositorRenderPassDrawQuad.
- std::unique_ptr<OverlayTexture> ScheduleRenderPassDrawQuad(
- const CALayerOverlay* ca_layer_overlay);
-
- // Copies the contents of the render pass draw quad, including filter effects,
- // to a GL texture, returned in |overlay_texture|. The resulting texture may
- // be larger than the CompositorRenderPassDrawQuad's output, in order to reuse
- // existing textures. The new size and position is placed in |new_bounds|.
- void CopyRenderPassDrawQuadToOverlayResource(
- const CALayerOverlay* ca_layer_overlay,
- std::unique_ptr<OverlayTexture>* overlay_texture,
- gfx::RectF* new_bounds);
- std::unique_ptr<OverlayTexture> FindOrCreateOverlayTexture(
- const AggregatedRenderPassId& render_pass_id,
- int width,
- int height,
- const gfx::ColorSpace& color_space);
- void ReduceAvailableOverlayTextures();
-
-#elif BUILDFLAG(IS_WIN)
- void ScheduleDCLayers();
-#endif
-
- // Setup/flush all pending overdraw feedback to framebuffer.
- void SetupOverdrawFeedback();
-
- // Process overdraw feedback from query.
- void ProcessOverdrawFeedback(base::CheckedNumeric<int> surface_area,
- unsigned query);
- bool OverdrawTracingEnabled();
-
- bool CompositeTimeTracingEnabled() override;
- void AddCompositeTimeTraces(base::TimeTicks ready_timestamp) override;
-
- ResourceFormat CurrentRenderPassResourceFormat() const;
-
- bool HasOutputColorMatrix() const;
-
- // Returns true if the given solid color draw quad can be safely drawn using
- // the glClear function call.
- bool CanUseFastSolidColorDraw(const SolidColorDrawQuad* quad) const;
-
- DisplayResourceProviderGL* resource_provider() {
- return static_cast<DisplayResourceProviderGL*>(resource_provider_);
- }
-
- // A map from RenderPass id to the texture used to draw the RenderPass from.
- base::flat_map<AggregatedRenderPassId, ScopedRenderPassTexture>
- render_pass_textures_;
-
- // A map from RenderPass id to backdrop filter cache texture.
- base::flat_map<AggregatedRenderPassId, sk_sp<SkImage>>
- render_pass_backdrop_textures_;
-
- // OverlayTextures that are free to be used in the next frame.
- std::vector<std::unique_ptr<OverlayTexture>> available_overlay_textures_;
- // OverlayTextures that have been set up for use but are waiting for
- // SwapBuffers.
- std::vector<std::unique_ptr<OverlayTexture>> awaiting_swap_overlay_textures_;
- // OverlayTextures that have been swapped for display on the gpu. Each vector
- // represents a single frame, and may be empty if none were used in that
- // frame. Ordered from oldest to most recent frame.
- std::vector<std::vector<std::unique_ptr<OverlayTexture>>>
- displayed_overlay_textures_;
- // OverlayTextures that we have replaced on the gpu but are awaiting
- // confirmation that we can reuse them.
- std::vector<std::unique_ptr<OverlayTexture>>
- awaiting_release_overlay_textures_;
-
- // Resources that have been sent to the GPU process, but not yet swapped.
- OverlayResourceLockList pending_overlay_resources_;
- // Resources that should be shortly swapped by the GPU process.
- base::circular_deque<OverlayResourceLockList> swapping_overlay_resources_;
-
- // Locks for overlays that have release fences and read lock fences.
- base::circular_deque<OverlayResourceLockList>
- read_lock_release_fence_overlay_locks_;
-
- // Resources that the GPU process has finished swapping. The key is the
- // texture id of the resource.
- std::map<unsigned, OverlayResourceLock> swapped_and_acked_overlay_resources_;
-
- // Query object, used to determine the number of sample drawn during a render
- // pass.
- unsigned occlusion_query_ = 0u;
-
- unsigned offscreen_framebuffer_id_ = 0u;
-
- std::unique_ptr<StaticGeometryBinding> shared_geometry_;
- std::unique_ptr<DynamicGeometryBinding> clipped_geometry_;
- gfx::QuadF shared_geometry_quad_;
-
- // This will return nullptr if the requested program has not yet been
- // initialized.
- const Program* GetProgramIfInitialized(const ProgramKey& key) const;
-
- std::unordered_map<ProgramKey, std::unique_ptr<Program>, ProgramKeyHash>
- program_cache_;
-
- const gfx::ColorTransform* GetColorTransform(const gfx::ColorSpace& src,
- const gfx::ColorSpace& dst);
- struct ColorTransformKey {
- gfx::ColorSpace src;
- gfx::ColorSpace dst;
- float sdr_max_luminance_nits = 0.f;
- bool operator==(const ColorTransformKey& other) const;
- bool operator!=(const ColorTransformKey& other) const;
- bool operator<(const ColorTransformKey& other) const;
- };
- std::map<ColorTransformKey, std::unique_ptr<gfx::ColorTransform>>
- color_transform_cache_;
-
- raw_ptr<gpu::gles2::GLES2Interface> gl_;
- raw_ptr<gpu::ContextSupport> context_support_;
- std::unique_ptr<ContextCacheController::ScopedVisibility> context_visibility_;
- std::unique_ptr<ContextCacheController::ScopedBusy> context_busy_;
-
- TextureDeleter texture_deleter_;
- GLRendererCopier copier_;
-
- gfx::Rect swap_buffer_rect_;
- std::vector<gfx::Rect> swap_content_bounds_;
- gfx::Rect scissor_rect_;
- bool is_scissor_enabled_ = false;
- bool stencil_shadow_ = false;
- bool blend_shadow_ = false;
- raw_ptr<const Program> current_program_ = nullptr;
- TexturedQuadDrawCache draw_cache_;
- int highp_threshold_cache_ = 0;
-
- raw_ptr<ScopedRenderPassTexture> current_framebuffer_texture_;
-
- SyncQueryCollection sync_queries_;
- bool use_discard_framebuffer_ = false;
- bool use_sync_query_ = false;
- bool use_blend_equation_advanced_ = false;
- bool use_blend_equation_advanced_coherent_ = false;
- bool use_timer_query_ = false;
- bool use_occlusion_query_ = false;
- bool use_swap_with_bounds_ = false;
- bool use_fast_path_solid_color_quad_ = false;
- bool supports_multi_sampling_ = false;
-
- // If true, tints all the composited content to red.
- bool tint_gl_composited_content_ = true;
-
-#if BUILDFLAG(IS_APPLE)
- // The method FlippedFramebuffer determines whether the framebuffer associated
- // with a DrawingFrame is flipped. It makes the assumption that the
- // DrawingFrame is being used as part of a render pass. If a DrawingFrame is
- // not being used as part of a render pass, setting it here forces
- // FlippedFramebuffer to return |true|.
- bool force_drawing_frame_framebuffer_unflipped_ = false;
-#endif
-
- BoundGeometry bound_geometry_;
-
- unsigned offscreen_stencil_renderbuffer_id_ = 0;
- gfx::Size offscreen_stencil_renderbuffer_size_;
-
- unsigned num_triangles_drawn_ = 0;
- bool prefer_draw_to_copy_ = false;
-
- // A circular queue of to keep track of timer queries and their associated
- // quad type as string.
- base::queue<std::pair<unsigned, std::string>> timer_queries_;
-
- // Keeps track of areas that have been drawn to in the current render pass.
- std::vector<gfx::Rect> drawn_rects_;
-
- // This may be null if the compositor is run on a thread without a
- // MessageLoop.
- scoped_refptr<base::SingleThreadTaskRunner> current_task_runner_;
- base::WeakPtrFactory<GLRenderer> weak_ptr_factory_{this};
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_H_
diff --git a/chromium/components/viz/service/display/gl_renderer_copier.cc b/chromium/components/viz/service/display/gl_renderer_copier.cc
deleted file mode 100644
index bbfe5a7515c..00000000000
--- a/chromium/components/viz/service/display/gl_renderer_copier.cc
+++ /dev/null
@@ -1,1298 +0,0 @@
-// 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 "components/viz/service/display/gl_renderer_copier.h"
-
-#include <cstring>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/containers/cxx20_erase.h"
-#include "base/memory/raw_ptr.h"
-#include "base/process/memory.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "components/viz/common/frame_sinks/copy_output_request.h"
-#include "components/viz/common/frame_sinks/copy_output_result.h"
-#include "components/viz/common/frame_sinks/copy_output_util.h"
-#include "components/viz/common/gl_i420_converter.h"
-#include "components/viz/common/gl_nv12_converter.h"
-#include "components/viz/common/gl_scaler.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/service/display/texture_deleter.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/client/shared_image_interface.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/shared_image_usage.h"
-#include "gpu/command_buffer/common/sync_token.h"
-#include "third_party/libyuv/include/libyuv/planar_functions.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkColorSpace.h"
-#include "third_party/skia/include/core/SkImageInfo.h"
-#include "ui/gfx/geometry/size.h"
-
-// Syntactic sugar to DCHECK that two sizes are equal.
-#define DCHECK_SIZE_EQ(a, b) \
- DCHECK((a) == (b)) << #a " != " #b ": " << (a).ToString() \
- << " != " << (b).ToString()
-
-namespace viz {
-
-using ResultFormat = CopyOutputRequest::ResultFormat;
-using ResultDestination = CopyOutputRequest::ResultDestination;
-
-namespace {
-
-constexpr int kRGBABytesPerPixel = 4;
-
-// Returns the source property of the |request|, if it is set. Otherwise,
-// returns an empty token. This is needed because CopyOutputRequest will crash
-// if source() is called when !has_source().
-base::UnguessableToken SourceOf(const CopyOutputRequest& request) {
- return request.has_source() ? request.source() : base::UnguessableToken();
-}
-
-// Creates a new texture, binds it to the GL_TEXTURE_2D target, and initializes
-// its default parameters.
-GLuint CreateDefaultTexture2D(gpu::gles2::GLES2Interface* gl) {
- GLuint result = 0;
- gl->GenTextures(1, &result);
- gl->BindTexture(GL_TEXTURE_2D, result);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- return result;
-}
-
-// Creates or re-creates a texture, only if needed, to ensure a texture of the
-// given |required| size is defined. |texture| and |size| are I/O parameters,
-// read to determine what to do and updated if any changes are made.
-void EnsureTextureDefinedWithSize(gpu::gles2::GLES2Interface* gl,
- const gfx::Size& required,
- GLuint* texture,
- gfx::Size* size) {
- if (*texture != 0 && *size == required)
- return;
- if (*texture == 0) {
- *texture = CreateDefaultTexture2D(gl);
- } else {
- gl->BindTexture(GL_TEXTURE_2D, *texture);
- }
- gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, required.width(), required.height(),
- 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
- *size = required;
-}
-
-// Returns parameters for the scaler to scale/transform the image in the source
-// framebuffer to meet the requirements of the |request|.
-GLScaler::Parameters CreateScalerParameters(
- const CopyOutputRequest& request,
- const gfx::ColorSpace& source_color_space,
- const gfx::ColorSpace& output_color_space,
- bool flipped_source) {
- GLScaler::Parameters params;
-
- params.scale_from = request.scale_from();
- params.scale_to = request.scale_to();
- params.source_color_space = source_color_space;
- params.output_color_space = output_color_space;
- // For downscaling, use the GOOD quality setting (appropriate for
- // thumbnailing); and, for upscaling, use the BEST quality.
- const bool is_downscale_in_both_dimensions =
- request.scale_to().x() < request.scale_from().x() &&
- request.scale_to().y() < request.scale_from().y();
- params.quality = is_downscale_in_both_dimensions
- ? GLScaler::Parameters::Quality::GOOD
- : GLScaler::Parameters::Quality::BEST;
- params.is_flipped_source = flipped_source;
-
- return params;
-}
-
-// Returns the specified offset in the form of a pointer for OpenGL's
-// `glReadPixels` call. This is not a valid memory address and the pointer
-// should never be dereferenced.
-uint8_t* GetOffsetPointer(int offset) {
- uint8_t* result = reinterpret_cast<uint8_t*>(0);
- result += offset;
- return result;
-}
-
-} // namespace
-
-GLRendererCopier::GLRendererCopier(ContextProvider* context_provider,
- TextureDeleter* texture_deleter)
- : context_provider_(context_provider), texture_deleter_(texture_deleter) {}
-
-GLRendererCopier::~GLRendererCopier() {
- for (auto& entry : cache_)
- entry.second->Free(context_provider_->ContextGL());
-}
-
-void GLRendererCopier::CopyFromTextureOrFramebuffer(
- std::unique_ptr<CopyOutputRequest> request,
- const copy_output::RenderPassGeometry& geometry,
- GLenum internal_format,
- GLuint framebuffer_texture,
- const gfx::Size& framebuffer_texture_size,
- bool flipped_source,
- const gfx::ColorSpace& framebuffer_color_space) {
- const gfx::Rect& result_rect = geometry.result_selection;
-
- // If we can't convert |color_space| to a SkColorSpace for SkBitmap copy
- // requests (e.g. PIECEWISE_HDR), fallback to a color transform to sRGB
- // before returning the copy result.
- gfx::ColorSpace dest_color_space = framebuffer_color_space;
- if (!framebuffer_color_space.ToSkColorSpace() &&
- request->result_format() == ResultFormat::RGBA &&
- request->result_destination() == ResultDestination::kSystemMemory) {
- dest_color_space = gfx::ColorSpace::CreateSRGB();
- }
- // Fast-Path: If no transformation is necessary and no new textures need to be
- // generated, read-back directly from the currently-bound framebuffer.
- if (request->result_format() == ResultFormat::RGBA &&
- request->result_destination() == ResultDestination::kSystemMemory &&
- framebuffer_color_space == dest_color_space && !request->is_scaled()) {
- StartReadbackFromFramebuffer(std::move(request), geometry.readback_offset,
- flipped_source, false, result_rect,
- dest_color_space);
- return;
- }
-
- gfx::Rect sampling_rect = geometry.sampling_bounds;
-
- const base::UnguessableToken requester = SourceOf(*request);
- std::unique_ptr<ReusableThings> things =
- TakeReusableThingsOrCreate(requester);
-
- // Determine the source texture: This is either the one attached to the
- // framebuffer, or a copy made from the framebuffer. Its format will be the
- // same as |internal_format|.
- //
- // TODO(crbug/767221): All of this (including some texture copies) wouldn't be
- // necessary if we could query whether the currently-bound framebuffer has a
- // texture attached to it, and just source from that texture directly (i.e.,
- // using glGetFramebufferAttachmentParameteriv() and
- // glGetTexLevelParameteriv(GL_TEXTURE_WIDTH/HEIGHT)).
- GLuint source_texture;
- gfx::Size source_texture_size;
- if (framebuffer_texture != 0) {
- source_texture = framebuffer_texture;
- source_texture_size = framebuffer_texture_size;
- } else {
- auto* const gl = context_provider_->ContextGL();
- if (things->fb_copy_texture == 0) {
- things->fb_copy_texture = CreateDefaultTexture2D(gl);
- things->fb_copy_texture_internal_format = static_cast<GLenum>(GL_NONE);
- things->fb_copy_texture_size = gfx::Size();
- } else {
- gl->BindTexture(GL_TEXTURE_2D, things->fb_copy_texture);
- }
- if (things->fb_copy_texture_internal_format == internal_format &&
- things->fb_copy_texture_size == sampling_rect.size()) {
- // Copy the framebuffer pixels without redefining the texture.
- gl->CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sampling_rect.x(),
- sampling_rect.y(), sampling_rect.width(),
- sampling_rect.height());
- } else {
- // Copy the framebuffer pixels into a newly-defined texture.
- gl->CopyTexImage2D(GL_TEXTURE_2D, 0, internal_format, sampling_rect.x(),
- sampling_rect.y(), sampling_rect.width(),
- sampling_rect.height(), 0);
- things->fb_copy_texture_internal_format = internal_format;
- things->fb_copy_texture_size = sampling_rect.size();
- }
- source_texture = things->fb_copy_texture;
- source_texture_size = sampling_rect.size();
- sampling_rect.set_origin(gfx::Point());
- }
-
- // Revert the Y-flipping of the sampling rect coordinates for GLScaler, which
- // always assumes the source offset is assuming a origin-at-top-left
- // coordinate space.
- if (flipped_source) {
- sampling_rect.set_y(source_texture_size.height() - sampling_rect.bottom());
- }
-
- switch (request->result_format()) {
- case ResultFormat::RGBA:
-
- switch (request->result_destination()) {
- case ResultDestination::kSystemMemory:
- EnsureTextureDefinedWithSize(
- context_provider_->ContextGL(), result_rect.size(),
- &things->result_texture, &things->result_texture_size);
- RenderResultTexture(*request, flipped_source, framebuffer_color_space,
- dest_color_space, source_texture,
- source_texture_size, sampling_rect, result_rect,
- things->result_texture, things.get());
- StartReadbackFromTexture(std::move(request), result_rect,
- dest_color_space, things.get());
- break;
- case ResultDestination::kNativeTextures:
- RenderAndSendTextureResult(std::move(request), flipped_source,
- framebuffer_color_space, dest_color_space,
- source_texture, source_texture_size,
- sampling_rect, result_rect, things.get());
- break;
- }
-
- break;
-
- case ResultFormat::I420_PLANES: {
- // The optimized single-copy path, provided by GLPixelBufferI420Result,
- // requires that the result be accessed via a task in the same task runner
- // sequence as the GLRendererCopier. Since I420_PLANES requests are meant
- // to be VIZ-internal, this is an acceptable limitation to enforce.
- if (!request->SendsResultsInCurrentSequence()) {
- request->set_result_task_runner(base::SequencedTaskRunnerHandle::Get());
- }
-
- const gfx::Rect aligned_rect = RenderI420Textures(
- *request, flipped_source, framebuffer_color_space, source_texture,
- source_texture_size, sampling_rect, result_rect, things.get());
- StartI420ReadbackFromTextures(std::move(request), aligned_rect,
- result_rect, things.get());
- break;
- }
-
- case ResultFormat::NV12_PLANES: {
- // The optimized single-copy path, provided by GLPixelBufferNV12Result,
- // requires that the result be accessed via a task in the same task runner
- // sequence as the GLRendererCopier. Since NV12_PLANES requests are meant
- // to be VIZ-internal, this is an acceptable limitation to enforce.
- if (!request->SendsResultsInCurrentSequence()) {
- request->set_result_task_runner(base::SequencedTaskRunnerHandle::Get());
- }
-
- const gfx::Rect aligned_rect = RenderNV12Textures(
- *request, flipped_source, framebuffer_color_space, source_texture,
- source_texture_size, sampling_rect, result_rect, things.get());
- StartNV12ReadbackFromTextures(std::move(request), aligned_rect,
- result_rect, things.get());
-
- break;
- }
- }
-
- StashReusableThingsOrDelete(requester, std::move(things));
-}
-
-void GLRendererCopier::FreeUnusedCachedResources() {
- ++purge_counter_;
-
- // Purge all cache entries that should no longer be kept alive, freeing any
- // resources they held.
- const auto IsTooOld = [this](const decltype(cache_)::value_type& entry) {
- return static_cast<int32_t>(purge_counter_ -
- entry.second->purge_count_at_last_use) >=
- kKeepalivePeriod;
- };
- for (auto& entry : cache_) {
- if (IsTooOld(entry))
- entry.second->Free(context_provider_->ContextGL());
- }
- base::EraseIf(cache_, IsTooOld);
-}
-
-void GLRendererCopier::RenderResultTexture(
- const CopyOutputRequest& request,
- bool flipped_source,
- const gfx::ColorSpace& source_color_space,
- const gfx::ColorSpace& dest_color_space,
- GLuint source_texture,
- const gfx::Size& source_texture_size,
- const gfx::Rect& sampling_rect,
- const gfx::Rect& result_rect,
- GLuint result_texture,
- ReusableThings* things) {
- DCHECK_EQ(request.result_format(), ResultFormat::RGBA);
-
- GLScaler::Parameters params = CreateScalerParameters(
- request, source_color_space, dest_color_space, flipped_source);
-
- if (request.result_destination() == ResultDestination::kSystemMemory) {
- // Render the result in top-down row order, and swizzle, within the GPU so
- // these things don't have to be done, less efficiently, on the CPU later.
- params.flip_output = flipped_source;
- params.swizzle[0] =
- ShouldSwapRedAndBlueForBitmapReadback() ? GL_BGRA_EXT : GL_RGBA;
- } else {
- // Texture results are always in bottom-up row order.
- DCHECK_EQ(request.result_destination(), ResultDestination::kNativeTextures);
- params.flip_output = !flipped_source;
- DCHECK_EQ(params.swizzle[0], static_cast<GLenum>(GL_RGBA));
- }
-
- if (!things->scaler)
- things->scaler = std::make_unique<GLScaler>(context_provider_);
- if (!GLScaler::ParametersAreEquivalent(params, things->scaler->params())) {
- const bool is_configured = things->scaler->Configure(params);
- // GLRendererCopier should never use illegal or unsupported options, nor
- // be using GLScaler with an invalid GL context.
- DCHECK(is_configured);
- }
-
- const bool success = things->scaler->Scale(
- source_texture, source_texture_size, sampling_rect.OffsetFromOrigin(),
- result_texture, result_rect);
- DCHECK(success);
-}
-
-gfx::Rect GLRendererCopier::RenderI420Textures(
- const CopyOutputRequest& request,
- bool flipped_source,
- const gfx::ColorSpace& source_color_space,
- GLuint source_texture,
- const gfx::Size& source_texture_size,
- const gfx::Rect& sampling_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things) {
- DCHECK_EQ(request.result_format(), ResultFormat::I420_PLANES);
-
- auto* const gl = context_provider_->ContextGL();
-
- // Compute required Y/U/V texture sizes and re-define them, if necessary. See
- // class comments for GLI420Converter for an explanation of how planar data is
- // packed into RGBA textures.
- const gfx::Rect aligned_rect = GLI420Converter::ToAlignedRect(result_rect);
- const gfx::Size required_luma_size(aligned_rect.width() / kRGBABytesPerPixel,
- aligned_rect.height());
- const gfx::Size required_chroma_size(required_luma_size.width() / 2,
- required_luma_size.height() / 2);
-
- EnsureTextureDefinedWithSize(gl, required_luma_size, &things->yuv_textures[0],
- &things->texture_sizes[0]);
- EnsureTextureDefinedWithSize(gl, required_chroma_size,
- &things->yuv_textures[1],
- &things->texture_sizes[1]);
- EnsureTextureDefinedWithSize(gl, required_chroma_size,
- &things->yuv_textures[2],
- &things->texture_sizes[2]);
-
- GLI420Converter::Parameters params =
- CreateScalerParameters(request, source_color_space,
- gfx::ColorSpace::CreateREC709(), flipped_source);
- // I420 readback assumes content is in top-down row order. Also, set the
- // output swizzle to match the readback format so that image bitmaps don't
- // have to be byte-order-swizzled on the CPU later.
- params.flip_output = flipped_source;
- params.swizzle[0] = GetOptimalReadbackFormat();
-
- if (!things->i420_converter) {
- things->i420_converter =
- std::make_unique<GLI420Converter>(context_provider_);
- }
- if (!GLI420Converter::ParametersAreEquivalent(
- params, things->i420_converter->params())) {
- const bool is_configured = things->i420_converter->Configure(params);
- // GLRendererCopier should never use illegal or unsupported options, nor
- // be using GLI420Converter with an invalid GL context.
- DCHECK(is_configured);
- }
-
- const bool success = things->i420_converter->Convert(
- source_texture, source_texture_size, sampling_rect.OffsetFromOrigin(),
- aligned_rect, things->yuv_textures.data());
- DCHECK(success);
-
- return aligned_rect;
-}
-
-gfx::Rect GLRendererCopier::RenderNV12Textures(
- const CopyOutputRequest& request,
- bool flipped_source,
- const gfx::ColorSpace& source_color_space,
- GLuint source_texture,
- const gfx::Size& source_texture_size,
- const gfx::Rect& sampling_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things) {
- DCHECK_EQ(request.result_format(), ResultFormat::NV12_PLANES);
-
- auto* const gl = context_provider_->ContextGL();
-
- // Compute required Y/UV texture sizes and re-define them, if necessary. See
- // class comments for GLNV12Converter for an explanation of how planar data is
- // packed into RGBA textures.
- const gfx::Rect aligned_rect = GLNV12Converter::ToAlignedRect(result_rect);
-
- const gfx::Size required_luma_size(aligned_rect.width() / kRGBABytesPerPixel,
- aligned_rect.height());
- const gfx::Size required_chroma_size(required_luma_size.width(),
- required_luma_size.height() / 2);
-
- EnsureTextureDefinedWithSize(gl, required_luma_size, &things->yuv_textures[0],
- &things->texture_sizes[0]);
-
- EnsureTextureDefinedWithSize(gl, required_chroma_size,
- &things->yuv_textures[1],
- &things->texture_sizes[1]);
-
- GLNV12Converter::Parameters params =
- CreateScalerParameters(request, source_color_space,
- gfx::ColorSpace::CreateREC709(), flipped_source);
-
- // NV12 readback assumes content is in top-down row order. Also, set the
- // output swizzle to match the readback format so that image bitmaps don't
- // have to be byte-order-swizzled on the CPU later.
- params.flip_output = flipped_source;
- params.swizzle[0] = GetOptimalReadbackFormat();
-
- if (!things->nv12_converter) {
- things->nv12_converter =
- std::make_unique<GLNV12Converter>(context_provider_);
- }
- if (!GLNV12Converter::ParametersAreEquivalent(
- params, things->nv12_converter->params())) {
- const bool is_configured = things->nv12_converter->Configure(params);
- // GLRendererCopier should never use illegal or unsupported options, nor
- // be using GLNV12Converter with an invalid GL context.
- DCHECK(is_configured);
- }
-
- const bool success = things->nv12_converter->Convert(
- source_texture, source_texture_size, sampling_rect.OffsetFromOrigin(),
- aligned_rect, things->yuv_textures.data());
- DCHECK(success);
-
- return aligned_rect;
-}
-
-void GLRendererCopier::StartReadbackFromTexture(
- std::unique_ptr<CopyOutputRequest> request,
- const gfx::Rect& result_rect,
- const gfx::ColorSpace& color_space,
- ReusableThings* things) {
- DCHECK_EQ(request->result_format(), ResultFormat::RGBA);
- DCHECK_EQ(request->result_destination(), ResultDestination::kSystemMemory);
-
- auto* const gl = context_provider_->ContextGL();
- if (things->readback_framebuffer == 0) {
- gl->GenFramebuffers(1, &things->readback_framebuffer);
- }
- gl->BindFramebuffer(GL_FRAMEBUFFER, things->readback_framebuffer);
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- things->result_texture, 0);
- StartReadbackFromFramebuffer(std::move(request), gfx::Vector2d(), false,
- ShouldSwapRedAndBlueForBitmapReadback(),
- result_rect, color_space);
-}
-
-namespace {
-
-// This is the type of CopyOutputResult we send for RGBA readback. The
-// constructor is called during on GLRendererCopier::FinishReadPixelsWorkflow(),
-// thus it always have access to the GLContext. The ReadRGBAPlane and destructor
-// are called asynchronously, and thus might not have access to the GLContext if
-// it has been destroyed in the meantime. We use the WeakPtr to the
-// GLRendererCopier as an indicator that the GLContext is still alive. If the
-// access to the GLContext is lost, we treat the copy output as failed.
-class GLPixelBufferRGBAResult final : public CopyOutputResult {
- public:
- GLPixelBufferRGBAResult(const gfx::Rect& result_rect,
- const gfx::ColorSpace& color_space,
- base::WeakPtr<GLRendererCopier> copier_weak_ptr,
- ContextProvider* context_provider,
- GLuint transfer_buffer,
- bool is_upside_down,
- bool swap_red_and_blue)
- : CopyOutputResult(CopyOutputResult::Format::RGBA,
- CopyOutputResult::Destination::kSystemMemory,
- result_rect,
- /*needs_lock_for_bitmap=*/false),
- color_space_(color_space),
- copier_weak_ptr_(std::move(copier_weak_ptr)),
- context_provider_(std::move(context_provider)),
- transfer_buffer_(transfer_buffer),
- is_upside_down_(is_upside_down),
- swap_red_and_blue_(swap_red_and_blue) {}
-
- ~GLPixelBufferRGBAResult() final {
- if (transfer_buffer_ && copier_weak_ptr_) {
- context_provider_->ContextGL()->DeleteBuffers(1, &transfer_buffer_);
- }
- }
-
- bool ReadRGBAPlane(uint8_t* dest, int stride) const final {
- // If the GLRendererCopier is gone, this implies the display compositor
- // which contains the GLContext is gone. Regard this copy output readback as
- // failed.
- if (!copier_weak_ptr_)
- return false;
-
- const int src_bytes_per_row = size().width() * kRGBABytesPerPixel;
- DCHECK_GE(stride, src_bytes_per_row);
-
- // No need to read from GPU memory if a cached bitmap already exists.
- if (rect().IsEmpty() || cached_bitmap()->readyToDraw())
- return CopyOutputResult::ReadRGBAPlane(dest, stride);
-
- auto* const gl = context_provider_->ContextGL();
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_);
- const uint8_t* pixels = static_cast<uint8_t*>(gl->MapBufferCHROMIUM(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
- if (pixels) {
- if (is_upside_down_) {
- dest += (size().height() - 1) * stride;
- stride = -stride;
- }
- const uint8_t* src = pixels;
- if (swap_red_and_blue_) {
- for (int y = 0; y < size().height();
- ++y, src += src_bytes_per_row, dest += stride) {
- for (int x = 0; x < kRGBABytesPerPixel * size().width();
- x += kRGBABytesPerPixel) {
- dest[x + 2] = src[x + 0];
- dest[x + 1] = src[x + 1];
- dest[x + 0] = src[x + 2];
- dest[x + 3] = src[x + 3];
- }
- }
- } else {
- libyuv::CopyPlane(src, src_bytes_per_row, dest, stride,
- src_bytes_per_row, size().height());
- }
- gl->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
- }
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
- return !!pixels;
- }
-
- gfx::ColorSpace GetRGBAColorSpace() const final { return color_space_; }
-
- // This method is always called on the same sequence as the GLRendererCopier.
- // This method will be inside Viz and has access to the WeakPtr of the
- // GLRendererCopier to check whether we still have the access to an alive
- // GLContext.
- const SkBitmap& AsSkBitmap() const final {
- if (rect().IsEmpty())
- return *cached_bitmap(); // Return "null" bitmap for empty result.
-
- if (cached_bitmap()->readyToDraw())
- return *cached_bitmap();
-
- if (!copier_weak_ptr_)
- return *cached_bitmap();
-
- SkBitmap result_bitmap;
- // size() was clamped to render pass or framebuffer size. If we can't
- // allocate it then OOM.
- auto info = SkImageInfo::MakeN32Premul(
- size().width(), size().height(), GetRGBAColorSpace().ToSkColorSpace());
- if (!result_bitmap.tryAllocPixels(info, info.minRowBytes()))
- base::TerminateBecauseOutOfMemory(info.computeMinByteSize());
-
- ReadRGBAPlane(static_cast<uint8_t*>(result_bitmap.getPixels()),
- result_bitmap.rowBytes());
- *cached_bitmap() = result_bitmap;
- // Now that we have a cached bitmap, no need to read from GPU memory
- // anymore.
- context_provider_->ContextGL()->DeleteBuffers(1, &transfer_buffer_);
- transfer_buffer_ = 0;
-
- return *cached_bitmap();
- }
-
- private:
- const gfx::ColorSpace color_space_;
- base::WeakPtr<GLRendererCopier> copier_weak_ptr_;
- raw_ptr<ContextProvider> context_provider_;
- mutable GLuint transfer_buffer_;
- const bool is_upside_down_;
- const bool swap_red_and_blue_;
-};
-} // namespace
-
-GLRendererCopier::ReadPixelsWorkflow::ReadPixelsWorkflow(
- std::unique_ptr<CopyOutputRequest> copy_request,
- const gfx::Vector2d& readback_offset,
- bool flipped_source,
- bool swap_red_and_blue,
- const gfx::Rect& result_rect,
- const gfx::ColorSpace& color_space,
- ContextProvider* context_provider,
- GLenum readback_format)
- : copy_request(std::move(copy_request)),
- flipped_source(flipped_source),
- swap_red_and_blue(swap_red_and_blue),
- result_rect(result_rect),
- color_space(color_space),
- context_provider_(context_provider) {
- DCHECK(readback_format == GL_RGBA || readback_format == GL_BGRA_EXT);
- DCHECK(context_provider_);
- auto* const gl = context_provider_->ContextGL();
-
- // Create a buffer for the pixel transfer.
- gl->GenBuffers(1, &transfer_buffer);
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer);
- gl->BufferData(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
- (result_rect.size().GetCheckedArea() * kRGBABytesPerPixel).ValueOrDie(),
- nullptr, GL_STREAM_READ);
-
- // Execute an asynchronous read-pixels operation, with a query that triggers
- // when Finish() should be run.
- gl->GenQueriesEXT(1, &query_);
- gl->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, query_);
- gl->ReadPixels(readback_offset.x(), readback_offset.y(), result_rect.width(),
- result_rect.height(), readback_format, GL_UNSIGNED_BYTE,
- nullptr);
- gl->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
-}
-
-GLRendererCopier::ReadPixelsWorkflow::~ReadPixelsWorkflow() {
- auto* const gl = context_provider_->ContextGL();
- gl->DeleteQueriesEXT(1, &query_);
- if (transfer_buffer)
- gl->DeleteBuffers(1, &transfer_buffer);
-}
-
-// Callback for the asynchronous glReadPixels(). The pixels are read from the
-// transfer buffer, and a CopyOutputResult is sent to the requestor. This would
-// mark this workflow as finished, and the workflow will be cleared later.
-void GLRendererCopier::FinishReadPixelsWorkflow(ReadPixelsWorkflow* workflow) {
- auto result = std::make_unique<GLPixelBufferRGBAResult>(
- workflow->result_rect, workflow->color_space, weak_factory_.GetWeakPtr(),
- context_provider_, workflow->transfer_buffer, workflow->flipped_source,
- workflow->swap_red_and_blue);
- workflow->transfer_buffer = 0; // Ownerhip was transferred to the result.
- if (!workflow->copy_request->SendsResultsInCurrentSequence()) {
- // Force readback into a SkBitmap now, because after PostTask we don't
- // have access to |context_provider_|.
- auto scoped_bitmap = result->ScopedAccessSkBitmap();
- auto bitmap = scoped_bitmap.bitmap();
- }
- workflow->copy_request->SendResult(std::move(result));
- const auto it =
- std::find_if(read_pixels_workflows_.begin(), read_pixels_workflows_.end(),
- [workflow](auto& ptr) { return ptr.get() == workflow; });
- DCHECK(it != read_pixels_workflows_.end());
- read_pixels_workflows_.erase(it);
-}
-
-void GLRendererCopier::StartReadbackFromFramebuffer(
- std::unique_ptr<CopyOutputRequest> request,
- const gfx::Vector2d& readback_offset,
- bool flipped_source,
- bool swapped_red_and_blue,
- const gfx::Rect& result_rect,
- const gfx::ColorSpace& color_space) {
- DCHECK_EQ(request->result_format(), ResultFormat::RGBA);
- DCHECK_EQ(request->result_destination(), ResultDestination::kSystemMemory);
-
- read_pixels_workflows_.emplace_back(std::make_unique<ReadPixelsWorkflow>(
- std::move(request), readback_offset, flipped_source,
- ShouldSwapRedAndBlueForBitmapReadback() != swapped_red_and_blue,
- result_rect, color_space, context_provider_, GetOptimalReadbackFormat()));
- context_provider_->ContextSupport()->SignalQuery(
- read_pixels_workflows_.back()->query(),
- base::BindOnce(&GLRendererCopier::FinishReadPixelsWorkflow,
- weak_factory_.GetWeakPtr(),
- read_pixels_workflows_.back().get()));
-}
-
-void GLRendererCopier::RenderAndSendTextureResult(
- std::unique_ptr<CopyOutputRequest> request,
- bool flipped_source,
- const gfx::ColorSpace& source_color_space,
- const gfx::ColorSpace& dest_color_space,
- GLuint source_texture,
- const gfx::Size& source_texture_size,
- const gfx::Rect& sampling_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things) {
- DCHECK_EQ(request->result_format(), ResultFormat::RGBA);
- DCHECK_EQ(request->result_destination(), ResultDestination::kNativeTextures);
-
- auto* sii = context_provider_->SharedImageInterface();
- gpu::Mailbox mailbox = sii->CreateSharedImage(
- ResourceFormat::RGBA_8888, result_rect.size(), dest_color_space,
- kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
- gpu::SHARED_IMAGE_USAGE_GLES2, gpu::kNullSurfaceHandle);
- auto* gl = context_provider_->ContextGL();
- gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
- GLuint texture = gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
- gl->BeginSharedImageAccessDirectCHROMIUM(
- texture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
- RenderResultTexture(*request, flipped_source, source_color_space,
- dest_color_space, source_texture, source_texture_size,
- sampling_rect, result_rect, texture, things);
- gl->EndSharedImageAccessDirectCHROMIUM(texture);
- gl->DeleteTextures(1, &texture);
- gpu::SyncToken sync_token;
- gl->GenSyncTokenCHROMIUM(sync_token.GetData());
-
- // Create a callback that deletes what was created in this GL context.
- // Note: There's no need to try to pool/re-use the result texture from here,
- // since only clients that are trying to re-invent video capture would see any
- // significant performance benefit. Instead, such clients should use the video
- // capture services provided by VIZ.
- CopyOutputResult::ReleaseCallbacks release_callbacks;
- release_callbacks.push_back(
- texture_deleter_->GetReleaseCallback(context_provider_.get(), mailbox));
-
- request->SendResult(std::make_unique<CopyOutputTextureResult>(
- CopyOutputResult::Format::RGBA, result_rect,
- CopyOutputResult::TextureResult(mailbox, sync_token, dest_color_space),
- std::move(release_callbacks)));
-}
-
-namespace {
-
-// Specialization of CopyOutputResult which reads I420 plane data from a GL
-// pixel buffer object, and automatically deletes the pixel buffer object at
-// destruction time. This provides an optimal one-copy data flow, from the pixel
-// buffer into client-provided memory.
-class GLPixelBufferI420Result final : public CopyOutputResult {
- public:
- // |aligned_rect| identifies the region of result pixels in the pixel buffer,
- // while the |result_rect| is the subregion that is exposed to the client.
- GLPixelBufferI420Result(const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- base::WeakPtr<GLRendererCopier> copier_weak_ptr,
- ContextProvider* context_provider,
- GLuint transfer_buffer)
- : CopyOutputResult(CopyOutputResult::Format::I420_PLANES,
- CopyOutputResult::Destination::kSystemMemory,
- result_rect,
- /*needs_lock_for_bitmap=*/false),
- aligned_rect_(aligned_rect),
- copier_weak_ptr_(copier_weak_ptr),
- context_provider_(context_provider),
- transfer_buffer_(transfer_buffer) {
- auto* const gl = context_provider_->ContextGL();
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_);
- pixels_ = static_cast<uint8_t*>(gl->MapBufferCHROMIUM(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
- }
-
- ~GLPixelBufferI420Result() final {
- if (copier_weak_ptr_) {
- auto* const gl = context_provider_->ContextGL();
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_);
- gl->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
- gl->DeleteBuffers(1, &transfer_buffer_);
- }
- }
-
- bool ReadI420Planes(uint8_t* y_out,
- int y_out_stride,
- uint8_t* u_out,
- int u_out_stride,
- uint8_t* v_out,
- int v_out_stride) const final {
- DCHECK_GE(y_out_stride, size().width());
- const int chroma_row_bytes = (size().width() + 1) / 2;
-
- DCHECK_GE(u_out_stride, chroma_row_bytes);
- DCHECK_GE(v_out_stride, chroma_row_bytes);
- if (!copier_weak_ptr_)
- return false;
-
- uint8_t* pixels = pixels_;
- if (pixels) {
- const int y_stride = aligned_rect_.width();
- const gfx::Vector2d result_offset =
- rect().OffsetFromOrigin() - aligned_rect_.OffsetFromOrigin();
- const int y_start_offset =
- result_offset.y() * y_stride + result_offset.x();
- libyuv::CopyPlane(pixels + y_start_offset, y_stride, y_out, y_out_stride,
- size().width(), size().height());
- pixels += y_stride * aligned_rect_.height();
- const int chroma_stride = aligned_rect_.width() / 2;
- const int chroma_start_offset =
- ((result_offset.y() / 2) * chroma_stride) + (result_offset.x() / 2);
- const int chroma_height = (size().height() + 1) / 2;
- libyuv::CopyPlane(pixels + chroma_start_offset, chroma_stride, u_out,
- u_out_stride, chroma_row_bytes, chroma_height);
- pixels += chroma_stride * (aligned_rect_.height() / 2);
- libyuv::CopyPlane(pixels + chroma_start_offset, chroma_stride, v_out,
- v_out_stride, chroma_row_bytes, chroma_height);
- }
- return !!pixels;
- }
-
- private:
- const gfx::Rect aligned_rect_;
- base::WeakPtr<GLRendererCopier> copier_weak_ptr_;
- const raw_ptr<ContextProvider> context_provider_;
- const GLuint transfer_buffer_;
- raw_ptr<uint8_t> pixels_;
-};
-
-// Specialization of CopyOutputResult which reads NV12 plane data from a GL
-// pixel buffer object, and automatically deletes the pixel buffer object at
-// destruction time. This provides an optimal one-copy data flow, from the pixel
-// buffer into client-provided memory.
-class GLPixelBufferNV12Result final : public CopyOutputResult {
- public:
- // |aligned_rect| identifies the region of result pixels in the pixel buffer,
- // while the |result_rect| is the subregion that is exposed to the client.
- GLPixelBufferNV12Result(const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- base::WeakPtr<GLRendererCopier> copier_weak_ptr,
- ContextProvider* context_provider,
- GLuint transfer_buffer)
- : CopyOutputResult(CopyOutputResult::Format::NV12_PLANES,
- CopyOutputResult::Destination::kSystemMemory,
- result_rect,
- /*needs_lock_for_bitmap=*/false),
- aligned_rect_(aligned_rect),
- copier_weak_ptr_(copier_weak_ptr),
- context_provider_(context_provider),
- transfer_buffer_(transfer_buffer) {
- auto* const gl = context_provider_->ContextGL();
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_);
- pixels_ = static_cast<uint8_t*>(gl->MapBufferCHROMIUM(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
- }
-
- ~GLPixelBufferNV12Result() final {
- if (copier_weak_ptr_) {
- auto* const gl = context_provider_->ContextGL();
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_);
- gl->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
- gl->DeleteBuffers(1, &transfer_buffer_);
- }
- }
-
- bool ReadNV12Planes(uint8_t* y_out,
- int y_out_stride,
- uint8_t* uv_out,
- int uv_out_stride) const final {
- DCHECK_GE(y_out_stride, size().width());
- const int chroma_row_bytes = 2 * ((size().width() + 1) / 2);
- DCHECK_GE(uv_out_stride, chroma_row_bytes);
- if (!copier_weak_ptr_)
- return false;
-
- uint8_t* pixels = pixels_;
-
- if (pixels) {
- const int y_stride = aligned_rect_.width();
- const gfx::Vector2d result_offset =
- rect().OffsetFromOrigin() - aligned_rect_.OffsetFromOrigin();
- const int y_start_offset =
- result_offset.y() * y_stride + result_offset.x();
- libyuv::CopyPlane(pixels + y_start_offset, y_stride, y_out, y_out_stride,
- size().width(), size().height());
- pixels += y_stride * aligned_rect_.height();
- const int chroma_stride = aligned_rect_.width();
- const int chroma_start_offset =
- ((result_offset.y() / 2) * chroma_stride) +
- 2 * (result_offset.x() / 2);
- const int chroma_height = (size().height() + 1) / 2;
- libyuv::CopyPlane(pixels + chroma_start_offset, chroma_stride, uv_out,
- uv_out_stride, chroma_row_bytes, chroma_height);
- }
- return !!pixels;
- }
-
- private:
- const gfx::Rect aligned_rect_;
- base::WeakPtr<GLRendererCopier> copier_weak_ptr_;
- const raw_ptr<ContextProvider> context_provider_;
- const GLuint transfer_buffer_;
- raw_ptr<uint8_t> pixels_ = nullptr;
-};
-
-} // namespace
-
-GLRendererCopier::ReadI420PlanesWorkflow::ReadI420PlanesWorkflow(
- std::unique_ptr<CopyOutputRequest> copy_request,
- const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- base::WeakPtr<GLRendererCopier> copier_weak_ptr,
- ContextProvider* context_provider)
- : copy_request(std::move(copy_request)),
- aligned_rect(aligned_rect),
- result_rect(result_rect),
- copier_weak_ptr_(copier_weak_ptr),
- context_provider_(context_provider) {
- // Create a buffer for the pixel transfer: A single buffer is used and will
- // contain the Y plane, then the U plane, then the V plane.
- auto* const gl = context_provider_->ContextGL();
- gl->GenBuffers(1, &transfer_buffer);
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer);
- base::CheckedNumeric<int> y_plane_bytes =
- y_texture_size().GetCheckedArea() * kRGBABytesPerPixel;
- base::CheckedNumeric<int> chroma_plane_bytes =
- chroma_texture_size().GetCheckedArea() * kRGBABytesPerPixel;
- gl->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
- (y_plane_bytes + chroma_plane_bytes * 2).ValueOrDie(), nullptr,
- GL_STREAM_READ);
- data_offsets_ = {0, y_plane_bytes.ValueOrDie(),
- (y_plane_bytes + chroma_plane_bytes).ValueOrDie()};
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
-
- // Generate the three queries used for determining when each of the plane
- // readbacks has completed.
- gl->GenQueriesEXT(3, queries.data());
-}
-
-void GLRendererCopier::ReadI420PlanesWorkflow::BindTransferBuffer() {
- DCHECK_NE(transfer_buffer, 0u);
- context_provider_->ContextGL()->BindBuffer(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer);
-}
-
-void GLRendererCopier::ReadI420PlanesWorkflow::StartPlaneReadback(
- int plane,
- GLenum readback_format) {
- DCHECK_NE(queries[plane], 0u);
- auto* const gl = context_provider_->ContextGL();
- gl->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, queries[plane]);
- const gfx::Size& size = plane == 0 ? y_texture_size() : chroma_texture_size();
- // Note: While a PIXEL_PACK_BUFFER is bound, OpenGL interprets the last
- // argument to ReadPixels() as a byte offset within the buffer instead of
- // an actual pointer in system memory.
- uint8_t* offset_in_buffer = GetOffsetPointer(data_offsets_[plane]);
- gl->ReadPixels(0, 0, size.width(), size.height(), readback_format,
- GL_UNSIGNED_BYTE, offset_in_buffer);
- gl->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
- context_provider_->ContextSupport()->SignalQuery(
- queries[plane],
- base::BindOnce(&GLRendererCopier::FinishReadI420PlanesWorkflow,
- copier_weak_ptr_, this, plane));
-}
-
-void GLRendererCopier::ReadI420PlanesWorkflow::UnbindTransferBuffer() {
- context_provider_->ContextGL()->BindBuffer(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
-}
-
-GLRendererCopier::ReadI420PlanesWorkflow::~ReadI420PlanesWorkflow() {
- auto* const gl = context_provider_->ContextGL();
- if (transfer_buffer != 0)
- gl->DeleteBuffers(1, &transfer_buffer);
- for (GLuint& query : queries) {
- if (query != 0)
- gl->DeleteQueriesEXT(1, &query);
- }
-}
-
-gfx::Size GLRendererCopier::ReadI420PlanesWorkflow::y_texture_size() const {
- return gfx::Size(aligned_rect.width() / kRGBABytesPerPixel,
- aligned_rect.height());
-}
-
-gfx::Size GLRendererCopier::ReadI420PlanesWorkflow::chroma_texture_size()
- const {
- return gfx::Size(aligned_rect.width() / kRGBABytesPerPixel / 2,
- aligned_rect.height() / 2);
-}
-
-GLRendererCopier::ReadNV12PlanesWorkflow::ReadNV12PlanesWorkflow(
- std::unique_ptr<CopyOutputRequest> copy_request,
- const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- base::WeakPtr<GLRendererCopier> copier_weak_ptr,
- ContextProvider* context_provider)
- : copy_request_(std::move(copy_request)),
- aligned_rect_(aligned_rect),
- result_rect_(result_rect),
- copier_weak_ptr_(copier_weak_ptr),
- context_provider_(context_provider) {
- // Create a buffer for the pixel transfer: A single buffer is used and will
- // contain the Y plane, then the UV plane.
- auto* const gl = context_provider_->ContextGL();
- gl->GenBuffers(1, &transfer_buffer_);
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_);
- base::CheckedNumeric<int> y_plane_bytes =
- y_texture_size().GetCheckedArea() * kRGBABytesPerPixel;
- base::CheckedNumeric<int> chroma_plane_bytes =
- chroma_texture_size().GetCheckedArea() * kRGBABytesPerPixel;
- gl->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
- (y_plane_bytes + chroma_plane_bytes).ValueOrDie(), nullptr,
- GL_STREAM_READ);
- data_offsets_ = {0, y_plane_bytes.ValueOrDie()};
- gl->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
-
- // Generate the two queries used for determining when each of the plane
- // readbacks has completed.
- gl->GenQueriesEXT(2, queries_.data());
-}
-
-void GLRendererCopier::ReadNV12PlanesWorkflow::BindTransferBuffer() {
- DCHECK_NE(transfer_buffer_, 0u);
- context_provider_->ContextGL()->BindBuffer(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, transfer_buffer_);
-}
-
-void GLRendererCopier::ReadNV12PlanesWorkflow::StartPlaneReadback(
- int plane,
- GLenum readback_format) {
- DCHECK_NE(queries_[plane], 0u);
- auto* const gl = context_provider_->ContextGL();
- gl->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, queries_[plane]);
- const gfx::Size& size = plane == 0 ? y_texture_size() : chroma_texture_size();
- // Note: While a PIXEL_PACK_BUFFER is bound, OpenGL interprets the last
- // argument to ReadPixels() as a byte offset within the buffer instead of
- // an actual pointer in system memory.
- uint8_t* offset_in_buffer = GetOffsetPointer(data_offsets_[plane]);
- gl->ReadPixels(0, 0, size.width(), size.height(), readback_format,
- GL_UNSIGNED_BYTE, offset_in_buffer);
- gl->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
- context_provider_->ContextSupport()->SignalQuery(
- queries_[plane],
- base::BindOnce(&GLRendererCopier::FinishReadNV12PlanesWorkflow,
- copier_weak_ptr_, this, plane));
-}
-
-void GLRendererCopier::ReadNV12PlanesWorkflow::UnbindTransferBuffer() {
- context_provider_->ContextGL()->BindBuffer(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
-}
-
-GLRendererCopier::ReadNV12PlanesWorkflow::~ReadNV12PlanesWorkflow() {
- auto* const gl = context_provider_->ContextGL();
- if (transfer_buffer_ != 0)
- gl->DeleteBuffers(1, &transfer_buffer_);
- for (GLuint& query : queries_) {
- if (query != 0)
- gl->DeleteQueriesEXT(1, &query);
- }
-}
-
-gfx::Size GLRendererCopier::ReadNV12PlanesWorkflow::y_texture_size() const {
- return gfx::Size(aligned_rect_.width() / kRGBABytesPerPixel,
- aligned_rect_.height());
-}
-
-gfx::Size GLRendererCopier::ReadNV12PlanesWorkflow::chroma_texture_size()
- const {
- return gfx::Size(aligned_rect_.width() / kRGBABytesPerPixel,
- aligned_rect_.height() / 2);
-}
-
-void GLRendererCopier::StartI420ReadbackFromTextures(
- std::unique_ptr<CopyOutputRequest> request,
- const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things) {
- DCHECK_EQ(request->result_format(), ResultFormat::I420_PLANES);
-
- auto* const gl = context_provider_->ContextGL();
- if (things->yuv_readback_framebuffers[0] == 0) {
- gl->GenFramebuffers(3, things->yuv_readback_framebuffers.data());
- } else if (things->yuv_readback_framebuffers[2] == 0) {
- gl->GenFramebuffers(1, &things->yuv_readback_framebuffers[2]);
- }
-
- // Execute three asynchronous read-pixels operations, one for each plane. The
- // CopyOutputRequest is passed to the ReadI420PlanesWorkflow, which will send
- // the CopyOutputResult once all readback operations are complete.
- read_i420_workflows_.emplace_back(std::make_unique<ReadI420PlanesWorkflow>(
- std::move(request), aligned_rect, result_rect, weak_factory_.GetWeakPtr(),
- context_provider_));
- ReadI420PlanesWorkflow* workflow = read_i420_workflows_.back().get();
- workflow->BindTransferBuffer();
- for (int plane = 0; plane < 3; ++plane) {
- gl->BindFramebuffer(GL_FRAMEBUFFER,
- things->yuv_readback_framebuffers[plane]);
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, things->yuv_textures[plane], 0);
- workflow->StartPlaneReadback(plane, GetOptimalReadbackFormat());
- }
- workflow->UnbindTransferBuffer();
-}
-
-void GLRendererCopier::FinishReadI420PlanesWorkflow(
- ReadI420PlanesWorkflow* workflow,
- int plane) {
- context_provider_->ContextGL()->DeleteQueriesEXT(1,
- &workflow->queries[plane]);
- workflow->queries[plane] = 0;
-
- // If all three readbacks have completed, send the result.
- if (workflow->queries == std::array<GLuint, 3>{{0, 0, 0}}) {
- workflow->copy_request->SendResult(
- std::make_unique<GLPixelBufferI420Result>(
- workflow->aligned_rect, workflow->result_rect,
- weak_factory_.GetWeakPtr(), context_provider_,
- workflow->transfer_buffer));
- workflow->transfer_buffer = 0; // Ownership was transferred to the result.
- const auto it =
- std::find_if(read_i420_workflows_.begin(), read_i420_workflows_.end(),
- [workflow](auto& ptr) { return ptr.get() == workflow; });
- DCHECK(it != read_i420_workflows_.end());
- read_i420_workflows_.erase(it);
- }
-}
-
-void GLRendererCopier::StartNV12ReadbackFromTextures(
- std::unique_ptr<CopyOutputRequest> request,
- const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things) {
- DCHECK_EQ(request->result_format(), ResultFormat::NV12_PLANES);
-
- auto* const gl = context_provider_->ContextGL();
- if (things->yuv_readback_framebuffers[0] == 0)
- gl->GenFramebuffers(2, things->yuv_readback_framebuffers.data());
-
- // Execute two asynchronous read-pixels operations, one for each plane. The
- // CopyOutputRequest is passed to the ReadNV12PlanesWorkflow, which will send
- // the CopyOutputResult once all readback operations are complete.
- read_nv12_workflows_.push_back(std::make_unique<ReadNV12PlanesWorkflow>(
- std::move(request), aligned_rect, result_rect, weak_factory_.GetWeakPtr(),
- context_provider_));
- ReadNV12PlanesWorkflow* workflow = read_nv12_workflows_.back().get();
- workflow->BindTransferBuffer();
- for (int plane = 0; plane < 2; ++plane) {
- gl->BindFramebuffer(GL_FRAMEBUFFER,
- things->yuv_readback_framebuffers[plane]);
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, things->yuv_textures[plane], 0);
- workflow->StartPlaneReadback(plane, GetOptimalReadbackFormat());
- }
- workflow->UnbindTransferBuffer();
-}
-
-void GLRendererCopier::FinishReadNV12PlanesWorkflow(
- ReadNV12PlanesWorkflow* workflow,
- int plane) {
- GLuint query = workflow->query(plane);
- context_provider_->ContextGL()->DeleteQueriesEXT(1, &query);
- workflow->MarkQueryCompleted(plane);
-
- // If both readbacks have completed, send the result.
- if (workflow->IsCompleted()) {
- workflow->TakeRequest()->SendResult(
- std::make_unique<GLPixelBufferNV12Result>(
- workflow->aligned_rect(), workflow->result_rect(),
- weak_factory_.GetWeakPtr(), context_provider_,
- workflow->TakeTransferBuffer()));
- const auto it =
- std::find_if(read_nv12_workflows_.begin(), read_nv12_workflows_.end(),
- [workflow](auto& ptr) { return ptr.get() == workflow; });
- DCHECK(it != read_nv12_workflows_.end());
- read_nv12_workflows_.erase(it);
- }
-}
-
-std::unique_ptr<GLRendererCopier::ReusableThings>
-GLRendererCopier::TakeReusableThingsOrCreate(
- const base::UnguessableToken& requester) {
- if (!requester.is_empty()) {
- const auto it = cache_.find(requester);
- if (it != cache_.end()) {
- auto things = std::move(it->second);
- cache_.erase(it);
- return things;
- }
- }
-
- return std::make_unique<ReusableThings>();
-}
-
-void GLRendererCopier::StashReusableThingsOrDelete(
- const base::UnguessableToken& requester,
- std::unique_ptr<ReusableThings> things) {
- if (requester.is_empty()) {
- things->Free(context_provider_->ContextGL());
- } else {
- things->purge_count_at_last_use = purge_counter_;
- cache_[requester] = std::move(things);
- }
-}
-
-GLenum GLRendererCopier::GetOptimalReadbackFormat() {
- if (optimal_readback_format_ != GL_NONE)
- return optimal_readback_format_;
-
- // Preconditions: GetOptimalReadbackFormat() requires a valid context and a
- // complete framebuffer set up. The latter must be guaranteed by all possible
- // callers of this method.
- auto* const gl = context_provider_->ContextGL();
- if (gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR)
- return GL_RGBA; // No context: Just return a sane default.
- DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
-
- // If the GL implementation internally uses the GL_BGRA_EXT+GL_UNSIGNED_BYTE
- // format+type combination, then consider that the optimal readback
- // format+type. Otherwise, use GL_RGBA+GL_UNSIGNED_BYTE, which all platforms
- // must support, per the GLES 2.0 spec.
- GLint type = 0;
- GLint readback_format = 0;
- gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &type);
- if (type == GL_UNSIGNED_BYTE)
- gl->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readback_format);
- if (readback_format != GL_BGRA_EXT)
- readback_format = GL_RGBA;
-
- optimal_readback_format_ = static_cast<GLenum>(readback_format);
- return optimal_readback_format_;
-}
-
-bool GLRendererCopier::ShouldSwapRedAndBlueForBitmapReadback() {
- const bool skbitmap_is_bgra = (kN32_SkColorType == kBGRA_8888_SkColorType);
- const bool readback_will_be_bgra =
- (GetOptimalReadbackFormat() == GL_BGRA_EXT);
- return skbitmap_is_bgra != readback_will_be_bgra;
-}
-
-GLRendererCopier::ReusableThings::ReusableThings() = default;
-
-GLRendererCopier::ReusableThings::~ReusableThings() {
- // Ensure all resources were freed by this point. Resources aren't explicity
- // freed here, in the destructor, because some require access to the GL
- // context. See Free().
- DCHECK_EQ(fb_copy_texture, 0u);
- DCHECK(!scaler);
- DCHECK_EQ(result_texture, 0u);
- DCHECK_EQ(readback_framebuffer, 0u);
- DCHECK(!i420_converter);
- constexpr std::array<GLuint, 3> kAllZeros = {0, 0, 0};
- DCHECK(yuv_textures == kAllZeros);
- DCHECK(yuv_readback_framebuffers == kAllZeros);
-}
-
-void GLRendererCopier::ReusableThings::Free(gpu::gles2::GLES2Interface* gl) {
- if (fb_copy_texture != 0) {
- gl->DeleteTextures(1, &fb_copy_texture);
- fb_copy_texture = 0;
- fb_copy_texture_internal_format = static_cast<GLenum>(GL_NONE);
- fb_copy_texture_size = gfx::Size();
- }
- scaler.reset();
- if (result_texture != 0) {
- gl->DeleteTextures(1, &result_texture);
- result_texture = 0;
- result_texture_size = gfx::Size();
- }
- if (readback_framebuffer != 0) {
- gl->DeleteFramebuffers(1, &readback_framebuffer);
- readback_framebuffer = 0;
- }
-
- i420_converter.reset();
- nv12_converter.reset();
-
- if (yuv_textures[0] != 0) {
- // We have some cached textures, check if there's 2 or 3 & delete them:
- int num_textures = yuv_textures[2] != 0 ? 3 : 2;
- gl->DeleteTextures(num_textures, yuv_textures.data());
- yuv_textures = {0, 0, 0};
- texture_sizes = {};
- }
- if (yuv_readback_framebuffers[0] != 0) {
- // We have some cached readback buffers, check if there's 2 or 3 & delete
- // them:
- int num_readback_buffers = yuv_readback_framebuffers[2] != 0 ? 3 : 2;
- gl->DeleteFramebuffers(num_readback_buffers,
- yuv_readback_framebuffers.data());
- yuv_readback_framebuffers = {0, 0, 0};
- }
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/gl_renderer_copier.h b/chromium/components/viz/service/display/gl_renderer_copier.h
deleted file mode 100644
index e0952de31f3..00000000000
--- a/chromium/components/viz/service/display/gl_renderer_copier.h
+++ /dev/null
@@ -1,485 +0,0 @@
-// 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 COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_COPIER_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_COPIER_H_
-
-#include <stdint.h>
-
-#include <array>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/containers/flat_map.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/ref_counted.h"
-#include "base/task/task_runner.h"
-#include "base/unguessable_token.h"
-#include "components/viz/service/viz_service_export.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace gfx {
-class ColorSpace;
-class Rect;
-class Vector2d;
-} // namespace gfx
-
-namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-}
-} // namespace gpu
-
-namespace viz {
-
-class ContextProvider;
-class CopyOutputRequest;
-class GLI420Converter;
-class GLNV12Converter;
-class GLScaler;
-class TextureDeleter;
-
-namespace copy_output {
-struct RenderPassGeometry;
-} // namespace copy_output
-
-// Helper class for GLRenderer that executes CopyOutputRequests using GL, to
-// perform texture copies/transformations and read back bitmaps. Also manages
-// the caching of resources needed to ensure efficient video performance.
-//
-// GLRenderer calls CopyFromTextureOrFramebuffer() to execute a
-// CopyOutputRequest. GLRendererCopier will examine the request and determine
-// the minimal amount of work needed to satisfy all the requirements of the
-// request.
-//
-// In many cases, interim GL objects (textures, framebuffers, etc.) must be
-// created as part of a multi-step process. When considering video performance
-// (i.e., a series of CopyOutputRequests from the same "source"), these interim
-// objects must be cached to prevent a significant performance penalty on some
-// GPU/drivers. GLRendererCopier manages such a cache and automatically frees
-// the objects when it detects that a stream of CopyOutputRequests from a given
-// "source" has ended.
-class VIZ_SERVICE_EXPORT GLRendererCopier {
- public:
- // Define types to avoid pulling in command buffer GL headers, which conflict
- // the ui/gl/gl_bindings.h
- using GLuint = unsigned int;
- using GLenum = unsigned int;
-
- // |context_provider| and |texture_deleter| must outlive this instance.
- GLRendererCopier(ContextProvider* context_provider,
- TextureDeleter* texture_deleter);
-
- GLRendererCopier(const GLRendererCopier&) = delete;
- GLRendererCopier& operator=(const GLRendererCopier&) = delete;
-
- ~GLRendererCopier();
-
- // Executes the |request|, copying from the currently-bound framebuffer of the
- // given |internal_format|. |output_rect| is the RenderPass's output Rect in
- // draw space, and is used to translate and clip the result selection Rect in
- // the request. |framebuffer_texture| and |framebuffer_texture_size| are
- // optional, but desired for performance: If provided, the texture might be
- // used as the source, to avoid having to make a copy of the framebuffer.
- // |flipped_source| is true (common case) if the framebuffer content is
- // vertically flipped (bottom-up row order). |framebuffer_color_space|
- // specifies the color space of the pixels in the framebuffer.
- //
- // This implementation may change a wide variety of GL state, such as texture
- // and framebuffer bindings, shader programs, and related attributes; and so
- // the caller must not make any assumptions about the state of the GL context
- // after this call.
- void CopyFromTextureOrFramebuffer(
- std::unique_ptr<CopyOutputRequest> request,
- const copy_output::RenderPassGeometry& geometry,
- GLenum internal_format,
- GLuint framebuffer_texture,
- const gfx::Size& framebuffer_texture_size,
- bool flipped_source,
- const gfx::ColorSpace& framebuffer_color_space);
-
- // Checks whether cached resources should be freed because recent copy
- // activity is no longer using them. This should be called after a frame has
- // finished drawing (after all copy requests have been executed).
- void FreeUnusedCachedResources();
-
- private:
- friend class GLRendererCopierTest;
-
- // The collection of resources that might be cached over multiple copy
- // requests from the same source. While executing a CopyOutputRequest, this
- // struct is also used to pass around intermediate objects between operations.
- struct VIZ_SERVICE_EXPORT ReusableThings {
- // This is used to determine whether these things aren't being used anymore.
- uint32_t purge_count_at_last_use = 0;
-
- // Texture containing a copy of the source framebuffer, if the source
- // framebuffer cannot be used directly.
- GLuint fb_copy_texture = 0;
- GLenum fb_copy_texture_internal_format = static_cast<GLenum>(0 /*GL_NONE*/);
- gfx::Size fb_copy_texture_size;
-
- // RGBA requests: Scaling, and texture/framebuffer for readback.
- std::unique_ptr<GLScaler> scaler;
- GLuint result_texture = 0;
- gfx::Size result_texture_size;
- GLuint readback_framebuffer = 0;
-
- // I420_PLANES & NV12_PLANES requests: I420, NV12 scaling and format
- // conversion, and textures+framebuffers for readback.
- std::unique_ptr<GLI420Converter> i420_converter;
- std::unique_ptr<GLNV12Converter> nv12_converter;
- std::array<GLuint, 3> yuv_textures = {0, 0, 0};
- std::array<gfx::Size, 3> texture_sizes;
- std::array<GLuint, 3> yuv_readback_framebuffers = {0, 0, 0};
-
- ReusableThings();
-
- ReusableThings(const ReusableThings&) = delete;
- ReusableThings& operator=(const ReusableThings&) = delete;
-
- ~ReusableThings();
-
- // Frees all the GL objects and scalers. This is in-lieu of a ReusableThings
- // destructor because a valid GL context is required to free some of the
- // objects.
- void Free(gpu::gles2::GLES2Interface* gl);
- };
-
- // Manages the execution of one asynchronous framebuffer readback and contains
- // all the relevant state needed to complete a copy request. The constructor
- // initiates the operation, and the destructor cleans up all the GL objects
- // created for this workflow. This class is owned by the GLRendererCopier, and
- // GLRendererCopier is responsible for deleting this either after the workflow
- // is finished, or when the GLRendererCopier is being destroyed.
- struct ReadPixelsWorkflow {
- public:
- // Saves all revelant state and initiates the GL asynchronous read-pixels
- // workflow.
- ReadPixelsWorkflow(std::unique_ptr<CopyOutputRequest> copy_request,
- const gfx::Vector2d& readback_offset,
- bool flipped_source,
- bool swap_red_and_blue,
- const gfx::Rect& result_rect,
- const gfx::ColorSpace& color_space,
- ContextProvider* context_provider,
- GLenum readback_format);
- ReadPixelsWorkflow(const ReadPixelsWorkflow&) = delete;
-
- // The destructor is by the GLRendererCopier, either called after the
- // workflow is finished or when GLRendererCopier is being destoryed.
- ~ReadPixelsWorkflow();
-
- GLuint query() const { return query_; }
-
- const std::unique_ptr<CopyOutputRequest> copy_request;
- const bool flipped_source;
- const bool swap_red_and_blue;
- const gfx::Rect result_rect;
- const gfx::ColorSpace color_space;
- GLuint transfer_buffer = 0;
-
- private:
- const raw_ptr<ContextProvider> context_provider_;
- GLuint query_ = 0;
- };
-
- // Renders a scaled/transformed copy of a source texture according to the
- // |request| parameters and other source characteristics. |result_texture|
- // must be allocated/sized by the caller. For RGBA requests with destination
- // set to system memory, the image content will be rendered in top-down row
- // order and maybe red-blue swapped, to support efficient readback later on.
- // For RGBA requests with ResultDestination::kNativeTextures set, the image
- // content is always rendered Y-flipped (bottom-up row order).
- void RenderResultTexture(const CopyOutputRequest& request,
- bool flipped_source,
- const gfx::ColorSpace& source_color_space,
- const gfx::ColorSpace& dest_color_space,
- GLuint source_texture,
- const gfx::Size& source_texture_size,
- const gfx::Rect& sampling_rect,
- const gfx::Rect& result_rect,
- GLuint result_texture,
- ReusableThings* things);
-
- // Like the ReadPixelsWorkflow, except for I420 planes readback. Because there
- // are three separate glReadPixels operations that may complete in any order,
- // a ReadI420PlanesWorkflow will receive notifications from three separate "GL
- // query" callbacks. It is only after all three operations have completed that
- // a fully-assembled CopyOutputResult can be sent.
- //
- // See class comments for GLI420Converter for an explanation of how
- // planar data is packed into RGBA textures.
- struct ReadI420PlanesWorkflow {
- public:
- ReadI420PlanesWorkflow(std::unique_ptr<CopyOutputRequest> copy_request,
- const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- base::WeakPtr<GLRendererCopier> copier_weak_ptr,
- ContextProvider* context_provider);
-
- void BindTransferBuffer();
- void StartPlaneReadback(int plane, GLenum readback_format);
- void UnbindTransferBuffer();
-
- ~ReadI420PlanesWorkflow();
-
- const std::unique_ptr<CopyOutputRequest> copy_request;
- const gfx::Rect aligned_rect;
- const gfx::Rect result_rect;
- GLuint transfer_buffer;
- std::array<GLuint, 3> queries;
-
- private:
- gfx::Size y_texture_size() const;
- gfx::Size chroma_texture_size() const;
-
- base::WeakPtr<GLRendererCopier> copier_weak_ptr_;
- const raw_ptr<ContextProvider> context_provider_;
- std::array<int, 3> data_offsets_;
- };
-
- // Like the ReadPixelsWorkflow, except for NV12 planes readback. Because there
- // are two separate glReadPixels operations that may complete in any order,
- // a ReadNV12PlanesWorkflow will receive notifications from two separate "GL
- // query" callbacks. It is only after all two operations have completed that
- // a fully-assembled CopyOutputResult can be sent.
- //
- // See class comments for GLNV12Converter for an explanation of how planar
- // data is packed into RGBA textures.
- class ReadNV12PlanesWorkflow {
- public:
- ReadNV12PlanesWorkflow(std::unique_ptr<CopyOutputRequest> copy_request,
- const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- base::WeakPtr<GLRendererCopier> copier_weak_ptr,
- ContextProvider* context_provider);
- ~ReadNV12PlanesWorkflow();
-
- void BindTransferBuffer();
- void StartPlaneReadback(int plane, GLenum readback_format);
- void UnbindTransferBuffer();
-
- gfx::Rect aligned_rect() const { return aligned_rect_; }
-
- gfx::Rect result_rect() const { return result_rect_; }
-
- std::unique_ptr<CopyOutputRequest> TakeRequest() {
- DCHECK(copy_request_);
-
- return std::move(copy_request_);
- }
-
- GLuint TakeTransferBuffer() {
- DCHECK(transfer_buffer_);
-
- GLuint result = transfer_buffer_;
- transfer_buffer_ = 0;
- return result;
- }
-
- // Returns true if the workflow has completed (i.e. readback requests for
- // all planes have finished).
- bool IsCompleted() const {
- return queries_ == std::array<GLuint, 2>{{0, 0}};
- }
-
- GLuint query(int plane) { return queries_[plane]; }
-
- // Marks that a readback has completed for a given plane.
- void MarkQueryCompleted(int plane) { queries_[plane] = 0; }
-
- private:
- gfx::Size y_texture_size() const;
- gfx::Size chroma_texture_size() const;
-
- std::unique_ptr<CopyOutputRequest> copy_request_;
- const gfx::Rect aligned_rect_;
- const gfx::Rect result_rect_;
- GLuint transfer_buffer_;
- std::array<GLuint, 2> queries_;
-
- base::WeakPtr<GLRendererCopier> copier_weak_ptr_;
- const raw_ptr<ContextProvider> context_provider_;
- std::array<int, 2> data_offsets_;
- };
-
- // Similar to RenderResultTexture(), except also transform the image into I420
- // format (a popular video format). Three textures, representing each of the
- // Y/U/V planes (as described in GLI420Converter), are populated and their GL
- // references placed in |things|. The image content is always rendered in
- // top-down row order and swizzled (if needed), to support efficient readback
- // later on.
- //
- // For alignment reasons, sometimes a slightly larger result will be provided,
- // and the return Rect will indicate the actual bounds that were rendered
- // (|result_rect|'s coordinate system). See StartI420ReadbackFromTextures()
- // for more details.
- gfx::Rect RenderI420Textures(const CopyOutputRequest& request,
- bool flipped_source,
- const gfx::ColorSpace& source_color_space,
- GLuint source_texture,
- const gfx::Size& source_texture_size,
- const gfx::Rect& sampling_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things);
-
- // Similar to RenderResultTexture(), except also transform the image into NV12
- // format (a popular video format). Two textures, representing each of the
- // Y/UV planes (as described in GLNV12Converter), are populated and their GL
- // references placed in |things|. The image content is always rendered in
- // top-down row order and swizzled (if needed), to support efficient readback
- // later on.
- //
- // For alignment reasons, sometimes a slightly larger result will be provided,
- // and the return Rect will indicate the actual bounds that were rendered
- // (|result_rect|'s coordinate system). See StartNV12ReadbackFromTextures()
- // for more details.
- gfx::Rect RenderNV12Textures(const CopyOutputRequest& request,
- bool flipped_source,
- const gfx::ColorSpace& source_color_space,
- GLuint source_texture,
- const gfx::Size& source_texture_size,
- const gfx::Rect& sampling_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things);
-
- // Binds the |things->result_texture| to a framebuffer and calls
- // StartReadbackFromFramebuffer(). This is only for RGBA requests with
- // destination set to kSystemMemory.
- // Assumes the image content is in top-down row order (and is red-blue swapped
- // iff RenderResultTexture() would have done that).
- void StartReadbackFromTexture(std::unique_ptr<CopyOutputRequest> request,
- const gfx::Rect& result_rect,
- const gfx::ColorSpace& color_space,
- ReusableThings* things);
-
- // Processes the next phase of the copy request by starting readback from the
- // currently-bound framebuffer into a pixel transfer buffer. |readback_offset|
- // is the origin of the readback rect within the framebuffer, with
- // |result_rect| providing the size of the readback rect. |flipped_source| is
- // true if the framebuffer content is in bottom-up row order, and
- // |swapped_red_and_blue| specifies whether the red and blue channels have
- // been swapped. This method kicks-off an asynchronous glReadPixels()
- // workflow.
- void StartReadbackFromFramebuffer(std::unique_ptr<CopyOutputRequest> request,
- const gfx::Vector2d& readback_offset,
- bool flipped_source,
- bool swapped_red_and_blue,
- const gfx::Rect& result_rect,
- const gfx::ColorSpace& color_space);
-
- // Renders a scaled/transformed copy of a source texture similarly to
- // RenderResultTexture, but packages up the result in a mailbox and sends it
- // as the result to the CopyOutputRequest.
- void RenderAndSendTextureResult(std::unique_ptr<CopyOutputRequest> request,
- bool flipped_source,
- const gfx::ColorSpace& source_color_space,
- const gfx::ColorSpace& dest_color_space,
- GLuint source_texture,
- const gfx::Size& source_texture_size,
- const gfx::Rect& sampling_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things);
-
- // Like StartReadbackFromTexture(), except that this processes the three Y/U/V
- // result textures in |things| by using three framebuffers and three
- // asynchronous readback operations. A single pixel transfer buffer is used to
- // hold the results of all three readbacks (i.e., each plane starts at a
- // different offset in the transfer buffer).
- //
- // |aligned_rect| is the Rect returned from the RenderI420Textures() call, and
- // is required so that the CopyOutputResult sent at the end of this workflow
- // will access the correct region of pixels.
- void StartI420ReadbackFromTextures(std::unique_ptr<CopyOutputRequest> request,
- const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things);
-
- // Like StartReadbackFromTexture(), except that this processes the two Y/UV
- // result textures in |things| by using two framebuffers and two asynchronous
- // readback operations. A single pixel transfer buffer is used to hold the
- // results of both readbacks (i.e., each plane starts at a different offset in
- // the transfer buffer).
- //
- // |aligned_rect| is the Rect returned from the RenderNV12Textures() call, and
- // is required so that the CopyOutputResult sent at the end of this workflow
- // will access the correct region of pixels.
- void StartNV12ReadbackFromTextures(std::unique_ptr<CopyOutputRequest> request,
- const gfx::Rect& aligned_rect,
- const gfx::Rect& result_rect,
- ReusableThings* things);
-
- // Retrieves a cached ReusableThings instance for the given CopyOutputRequest
- // source, or creates a new instance.
- std::unique_ptr<ReusableThings> TakeReusableThingsOrCreate(
- const base::UnguessableToken& requester);
-
- // If |requester| is a valid UnguessableToken, this stashes the given
- // ReusableThings instance in the cache for use in future CopyOutputRequests
- // from the same requester. Otherwise, |things| is freed.
- void StashReusableThingsOrDelete(const base::UnguessableToken& requester,
- std::unique_ptr<ReusableThings> things);
-
- // Queries the GL implementation to determine which is the more performance-
- // optimal supported readback format: GL_RGBA or GL_BGRA_EXT, and memoizes the
- // result for all future calls.
- //
- // Precondition: The GL context has a complete, bound framebuffer ready for
- // readback.
- GLenum GetOptimalReadbackFormat();
-
- // Returns true if the red and blue channels should be swapped within the GPU,
- // where such an operation has negligible cost, so that later the red-blue
- // swap does not need to happen on the CPU (non-negligible cost).
- bool ShouldSwapRedAndBlueForBitmapReadback();
-
- void FinishReadPixelsWorkflow(ReadPixelsWorkflow*);
- void FinishReadI420PlanesWorkflow(ReadI420PlanesWorkflow*, int plane);
- void FinishReadNV12PlanesWorkflow(ReadNV12PlanesWorkflow* workflow,
- int plane);
-
- // Injected dependencies.
- const raw_ptr<ContextProvider> context_provider_;
- const raw_ptr<TextureDeleter> texture_deleter_;
-
- // This increments by one for every call to FreeUnusedCachedResources(). It
- // is meant to determine when cached resources should be freed because they
- // are unlikely to see further use.
- uint32_t purge_counter_ = 0;
-
- // A cache of resources recently used in the execution of a stream of copy
- // requests from the same source. Since this reflects the number of active
- // video captures, it is expected to almost always be zero or one entry in
- // size.
- base::flat_map<base::UnguessableToken, std::unique_ptr<ReusableThings>>
- cache_;
-
- // This specifies whether the GPU+driver combination executes readback more
- // efficiently using GL_RGBA or GL_BGRA_EXT format. This starts out as
- // GL_NONE, which means "unknown," and will be determined at the time the
- // first readback request is made.
- GLenum optimal_readback_format_ = static_cast<GLenum>(0 /*GL_NONE*/);
-
- // Purge cache entries that have not been used after this many calls to
- // FreeUnusedCachedResources(). The choice of 60 is arbitrary, but on most
- // platforms means that a somewhat-to-fully active compositor will cause
- // things to be auto-purged after approx. 1-2 seconds of not being used.
- static constexpr int kKeepalivePeriod = 60;
-
- std::vector<std::unique_ptr<ReadPixelsWorkflow>> read_pixels_workflows_;
- std::vector<std::unique_ptr<ReadI420PlanesWorkflow>> read_i420_workflows_;
- std::vector<std::unique_ptr<ReadNV12PlanesWorkflow>> read_nv12_workflows_;
-
- // Weak ptr to this class.
- base::WeakPtrFactory<GLRendererCopier> weak_factory_{this};
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_COPIER_H_
diff --git a/chromium/components/viz/service/display/gl_renderer_copier_perftest.cc b/chromium/components/viz/service/display/gl_renderer_copier_perftest.cc
deleted file mode 100644
index 853f61e9fe9..00000000000
--- a/chromium/components/viz/service/display/gl_renderer_copier_perftest.cc
+++ /dev/null
@@ -1,338 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/memory/raw_ptr.h"
-#include "components/viz/service/display/gl_renderer_copier.h"
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/path_service.h"
-#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/timer/lap_timer.h"
-#include "cc/test/pixel_test_utils.h"
-#include "components/viz/common/frame_sinks/copy_output_request.h"
-#include "components/viz/common/frame_sinks/copy_output_result.h"
-#include "components/viz/common/frame_sinks/copy_output_util.h"
-#include "components/viz/service/display/gl_renderer.h"
-#include "components/viz/test/paths.h"
-#include "components/viz/test/test_in_process_context_provider.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/perf/perf_result_reporter.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace viz {
-
-namespace {
-
-// The size of the source texture or framebuffer.
-constexpr gfx::Size kSourceSize = gfx::Size(240, 120);
-
-// In order to test coordinate calculations and Y-flipping, the tests will issue
-// copy requests for a small region just to the right and below the center of
-// the entire source texture/framebuffer.
-constexpr gfx::Rect kRequestArea = gfx::Rect(kSourceSize.width() / 2,
- kSourceSize.height() / 2,
- kSourceSize.width() / 4,
- kSourceSize.height() / 4);
-
-constexpr char kMetricPrefixGLRendererCopier[] = "GLRendererCopier.";
-constexpr char kMetricReadbackThroughputRunsPerS[] = "readback_throughput";
-
-perf_test::PerfResultReporter SetUpGLRendererCopierReporter(
- const std::string& story) {
- perf_test::PerfResultReporter reporter(kMetricPrefixGLRendererCopier, story);
- reporter.RegisterImportantMetric(kMetricReadbackThroughputRunsPerS, "runs/s");
- return reporter;
-}
-
-base::FilePath GetTestFilePath(const base::FilePath::CharType* basename) {
- base::FilePath test_dir;
- base::PathService::Get(Paths::DIR_TEST_DATA, &test_dir);
- return test_dir.Append(base::FilePath(basename));
-}
-
-} // namespace
-
-class GLRendererCopierPerfTest : public testing::Test {
- public:
- GLRendererCopierPerfTest() {
- context_provider_ = base::MakeRefCounted<TestInProcessContextProvider>(
- TestContextType::kGLES2, /*support_locking=*/false);
- gpu::ContextResult result = context_provider_->BindToCurrentThread();
- DCHECK_EQ(result, gpu::ContextResult::kSuccess);
- gl_ = context_provider_->ContextGL();
- texture_deleter_ =
- std::make_unique<TextureDeleter>(base::ThreadTaskRunnerHandle::Get());
- copier_ = std::make_unique<GLRendererCopier>(context_provider_.get(),
- texture_deleter_.get());
- }
-
- GLRendererCopierPerfTest(const GLRendererCopierPerfTest&) = delete;
- GLRendererCopierPerfTest& operator=(const GLRendererCopierPerfTest&) = delete;
-
- void TearDown() override {
- DeleteSourceFramebuffer();
- DeleteSourceTexture();
- copier_.reset();
- texture_deleter_.reset();
- }
-
- gfx::Rect DrawToWindowSpace(const gfx::Rect& draw_rect, bool flipped_source) {
- gfx::Rect window_rect = draw_rect;
- if (flipped_source)
- window_rect.set_y(kSourceSize.height() - window_rect.bottom());
- return window_rect;
- }
-
- // Creates a packed RGBA (bytes_per_pixel=4) bitmap in OpenGL byte/row order
- // from the given SkBitmap.
- std::unique_ptr<uint8_t[]> CreateGLPixelsFromSkBitmap(SkBitmap bitmap,
- bool flipped_source) {
- // |bitmap| could be of any color type (and is usually BGRA). Convert it to
- // a RGBA bitmap in the GL byte order.
- SkBitmap rgba_bitmap;
- rgba_bitmap.allocPixels(SkImageInfo::Make(bitmap.width(), bitmap.height(),
- kRGBA_8888_SkColorType,
- kPremul_SkAlphaType));
- SkPixmap pixmap;
- const bool success =
- bitmap.peekPixels(&pixmap) && rgba_bitmap.writePixels(pixmap, 0, 0);
- CHECK(success);
-
- // Copy the RGBA bitmap into a raw byte array, reversing the row order and
- // maybe stripping-out the alpha channel.
- const int bytes_per_pixel = 4;
- std::unique_ptr<uint8_t[]> pixels(
- new uint8_t[rgba_bitmap.width() * rgba_bitmap.height() *
- bytes_per_pixel]);
- for (int y = 0; y < rgba_bitmap.height(); ++y) {
- const uint8_t* src = static_cast<uint8_t*>(rgba_bitmap.getAddr(0, y));
- const int flipped_y = flipped_source ? rgba_bitmap.height() - y - 1 : y;
- uint8_t* dest =
- pixels.get() + flipped_y * rgba_bitmap.width() * bytes_per_pixel;
- for (int x = 0; x < rgba_bitmap.width(); ++x) {
- *(dest++) = *(src++);
- *(dest++) = *(src++);
- *(dest++) = *(src++);
- if (bytes_per_pixel == 4)
- *(dest++) = *(src++);
- else
- ++src;
- }
- }
-
- return pixels;
- }
-
- GLuint CreateSourceTexture(bool flipped_source) {
- CHECK_EQ(0u, source_texture_);
- SkBitmap source_bitmap;
- cc::ReadPNGFile(GetTestFilePath(FILE_PATH_LITERAL("16_color_rects.png")),
- &source_bitmap);
- source_bitmap.setImmutable();
- gl_->GenTextures(1, &source_texture_);
- gl_->BindTexture(GL_TEXTURE_2D, source_texture_);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl_->TexImage2D(
- GL_TEXTURE_2D, 0, static_cast<GLenum>(GL_RGBA), kSourceSize.width(),
- kSourceSize.height(), 0, static_cast<GLenum>(GL_RGBA), GL_UNSIGNED_BYTE,
- CreateGLPixelsFromSkBitmap(source_bitmap, flipped_source).get());
- gl_->BindTexture(GL_TEXTURE_2D, 0);
- return source_texture_;
- }
-
- void DeleteSourceTexture() {
- if (source_texture_ != 0) {
- gl_->DeleteTextures(1, &source_texture_);
- source_texture_ = 0;
- }
- }
-
- void CreateAndBindSourceFramebuffer(GLuint texture) {
- ASSERT_EQ(0u, source_framebuffer_);
- gl_->GenFramebuffers(1, &source_framebuffer_);
- gl_->BindFramebuffer(GL_FRAMEBUFFER, source_framebuffer_);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, texture, 0);
- }
-
- void DeleteSourceFramebuffer() {
- if (source_framebuffer_ != 0) {
- gl_->DeleteFramebuffers(1, &source_framebuffer_);
- source_framebuffer_ = 0;
- }
- }
-
- void CopyFromTextureOrFramebuffer(
- bool have_source_texture,
- CopyOutputResult::Format result_format,
- CopyOutputResult::Destination result_destination,
- bool scale_by_half,
- bool flipped_source,
- const std::string& story) {
- std::unique_ptr<CopyOutputResult> result;
-
- gfx::Rect result_selection(kRequestArea);
- if (scale_by_half)
- result_selection = gfx::ScaleToEnclosingRect(result_selection, 0.5f);
-
- copy_output::RenderPassGeometry geometry;
- // geometry.result_bounds not used by GLRendererCopier
- geometry.sampling_bounds =
- DrawToWindowSpace(gfx::Rect(kSourceSize), flipped_source);
- geometry.result_selection = result_selection;
- geometry.readback_offset =
- DrawToWindowSpace(geometry.result_selection, flipped_source)
- .OffsetFromOrigin();
-
- timer_.Reset();
- do {
- base::RunLoop loop;
- auto request = std::make_unique<CopyOutputRequest>(
- result_format, result_destination,
- base::BindOnce(
- [](std::unique_ptr<CopyOutputResult>* result,
- base::OnceClosure quit_closure,
- std::unique_ptr<CopyOutputResult> result_from_copier) {
- *result = std::move(result_from_copier);
- std::move(quit_closure).Run();
- },
- &result, loop.QuitClosure()));
- if (scale_by_half)
- request->SetUniformScaleRatio(2, 1);
- const GLuint source_texture = CreateSourceTexture(flipped_source);
- CreateAndBindSourceFramebuffer(source_texture);
-
- copier_->CopyFromTextureOrFramebuffer(
- std::move(request), geometry, static_cast<GLenum>(GL_RGBA),
- have_source_texture ? source_texture : 0, kSourceSize, flipped_source,
- gfx::ColorSpace::CreateSRGB());
- loop.Run();
-
- // Check that a result was produced and is of the expected rect/size.
- ASSERT_TRUE(result);
- ASSERT_FALSE(result->IsEmpty());
- if (scale_by_half)
- ASSERT_EQ(gfx::ScaleToEnclosingRect(kRequestArea, 0.5f),
- result->rect());
- else
- ASSERT_EQ(kRequestArea, result->rect());
-
- if (result_format == CopyOutputResult::Format::RGBA &&
- result_destination == CopyOutputResult::Destination::kSystemMemory) {
- auto scoped_bitmap = result->ScopedAccessSkBitmap();
- const SkBitmap& result_bitmap = scoped_bitmap.bitmap();
- ASSERT_TRUE(result_bitmap.readyToDraw());
- } else if (result_format == CopyOutputResult::Format::I420_PLANES) {
- const int result_width = result->rect().width();
- const int result_height = result->rect().height();
- const int y_width = result_width;
- const int y_stride = y_width;
- std::unique_ptr<uint8_t[]> y_data(
- new uint8_t[y_stride * result_height]);
- const int chroma_width = (result_width + 1) / 2;
- const int u_stride = chroma_width;
- const int v_stride = chroma_width;
- const int chroma_height = (result_height + 1) / 2;
- std::unique_ptr<uint8_t[]> u_data(
- new uint8_t[u_stride * chroma_height]);
- std::unique_ptr<uint8_t[]> v_data(
- new uint8_t[v_stride * chroma_height]);
-
- const bool success =
- result->ReadI420Planes(y_data.get(), y_stride, u_data.get(),
- u_stride, v_data.get(), v_stride);
- ASSERT_TRUE(success);
- }
-
- DeleteSourceFramebuffer();
- DeleteSourceTexture();
- timer_.NextLap();
- } while (!timer_.HasTimeLimitExpired());
-
- auto reporter = SetUpGLRendererCopierReporter(story);
- reporter.AddResult(kMetricReadbackThroughputRunsPerS,
- timer_.LapsPerSecond());
- }
-
- private:
- raw_ptr<gpu::gles2::GLES2Interface> gl_ = nullptr;
- scoped_refptr<TestInProcessContextProvider> context_provider_;
- std::unique_ptr<TextureDeleter> texture_deleter_;
- std::unique_ptr<GLRendererCopier> copier_;
- GLuint source_texture_ = 0;
- GLuint source_framebuffer_ = 0;
- base::LapTimer timer_;
-};
-
-// Fast-Path: If no transformation is necessary and no new textures need to be
-// generated, read-back directly from the currently-bound framebuffer.
-TEST_F(GLRendererCopierPerfTest, NoTransformNoNewTextures) {
- CopyFromTextureOrFramebuffer(
- /*have_source_texture=*/false, CopyOutputResult::Format::RGBA,
- CopyOutputResult::Destination::kSystemMemory,
- /*scale_by_half=*/false, /*flipped_source=*/false,
- "no_transformation_and_no_new_textures");
-}
-
-// Source texture is the one attached to the framebuffer, better performance
-// than having to make a copy of the framebuffer.
-TEST_F(GLRendererCopierPerfTest, HaveTextureResultRGBABitmap) {
- CopyFromTextureOrFramebuffer(
- /*have_source_texture=*/true, CopyOutputResult::Format::RGBA,
- CopyOutputResult::Destination::kSystemMemory,
- /*scale_by_half=*/true, /*flipped_source=*/false,
- "framebuffer_has_texture_and_result_is_RGBA_BITMAP");
-}
-TEST_F(GLRendererCopierPerfTest, HaveTextureResultRGBATexture) {
- CopyFromTextureOrFramebuffer(
- /*have_source_texture=*/true, CopyOutputResult::Format::RGBA,
- CopyOutputResult::Destination::kNativeTextures,
- /*scale_by_half=*/true, /*flipped_source=*/false,
- "framebuffer_has_texture_and_result_is_RGBA_TEXTURE");
-}
-TEST_F(GLRendererCopierPerfTest, HaveTextureResultI420Planes) {
- CopyFromTextureOrFramebuffer(
- /*have_source_texture=*/true, CopyOutputResult::Format::I420_PLANES,
- CopyOutputResult::Destination::kSystemMemory,
- /*scale_by_half=*/true, /*flipped_source=*/false,
- "framebuffer_has_texture_and_result_is_I420_PLANES");
-}
-
-// Have to make a copy of the framebuffer for the source texture.
-TEST_F(GLRendererCopierPerfTest, NoTextureResultI420Planes) {
- CopyFromTextureOrFramebuffer(
- /*have_source_texture=*/false, CopyOutputResult::Format::I420_PLANES,
- CopyOutputResult::Destination::kSystemMemory,
- /*scale_by_half=*/true, /*flipped_source=*/false,
- "framebuffer_doesn't_have_texture_and_result_is_I420_PLANES");
-}
-
-// Source content is vertically flipped.
-TEST_F(GLRendererCopierPerfTest, SourceContentVerticallyFlipped) {
- CopyFromTextureOrFramebuffer(/*have_source_texture=*/true,
- CopyOutputResult::Format::I420_PLANES,
- CopyOutputResult::Destination::kSystemMemory,
- /*scale_by_half=*/true, /*flipped_source=*/true,
- "source_content_is_vertically_flipped");
-}
-
-// Result is not scaled by half.
-TEST_F(GLRendererCopierPerfTest, ResultNotScaled) {
- CopyFromTextureOrFramebuffer(/*have_source_texture=*/true,
- CopyOutputResult::Format::I420_PLANES,
- CopyOutputResult::Destination::kSystemMemory,
- /*scale_by_half=*/false, /*flipped_source=*/true,
- "result_is_not_scaled_by_half");
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/gl_renderer_copier_pixeltest.cc b/chromium/components/viz/service/display/gl_renderer_copier_pixeltest.cc
deleted file mode 100644
index 0f72f75a2aa..00000000000
--- a/chromium/components/viz/service/display/gl_renderer_copier_pixeltest.cc
+++ /dev/null
@@ -1,958 +0,0 @@
-// 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 "components/viz/service/display/gl_renderer_copier.h"
-
-#include <stdint.h>
-
-#include <cstring>
-#include <memory>
-#include <tuple>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/path_service.h"
-#include "base/run_loop.h"
-#include "base/test/test_switches.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
-#include "cc/test/pixel_test.h"
-#include "cc/test/pixel_test_utils.h"
-#include "components/viz/common/frame_sinks/copy_output_request.h"
-#include "components/viz/common/frame_sinks/copy_output_result.h"
-#include "components/viz/common/frame_sinks/copy_output_util.h"
-#include "components/viz/service/display/gl_renderer.h"
-#include "components/viz/test/gl_scaler_test_util.h"
-#include "components/viz/test/paths.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/libyuv/include/libyuv/convert_argb.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkImageInfo.h"
-#include "third_party/skia/include/core/SkPixmap.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/color_transform.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-
-#if BUILDFLAG(IS_ANDROID)
-#include "base/android/build_info.h"
-#endif
-
-namespace viz {
-
-namespace {
-
-base::FilePath GetTestFilePath(const base::FilePath::CharType* basename) {
- base::FilePath test_dir;
- base::PathService::Get(Paths::DIR_TEST_DATA, &test_dir);
- return test_dir.Append(base::FilePath(basename));
-}
-
-// Creates a packed RGBA (bytes_per_pixel=4) or RGB (bytes_per_pixel=3) bitmap
-// in OpenGL byte/row order from the given SkBitmap.
-std::unique_ptr<uint8_t[]> CreateGLPixelsFromSkBitmap(SkBitmap bitmap,
- GLuint source_format,
- bool flip_source) {
- // |bitmap| could be of any color type (and is usually BGRA). Convert it to
- // a RGBA bitmap in the GL byte order.
- SkBitmap rgba_bitmap;
- rgba_bitmap.allocPixels(SkImageInfo::Make(bitmap.width(), bitmap.height(),
- kRGBA_8888_SkColorType,
- kPremul_SkAlphaType));
- SkPixmap pixmap;
- const bool success =
- bitmap.peekPixels(&pixmap) && rgba_bitmap.writePixels(pixmap, 0, 0);
- CHECK(success);
-
- // Copy the RGBA bitmap into a raw byte array, reversing the row order and
- // maybe stripping-out the alpha channel.
- const int bytes_per_pixel = source_format == GL_RGBA ? 4 : 3;
- std::unique_ptr<uint8_t[]> pixels(
- new uint8_t[rgba_bitmap.width() * rgba_bitmap.height() *
- bytes_per_pixel]);
- for (int y = 0; y < rgba_bitmap.height(); ++y) {
- const uint8_t* src = static_cast<uint8_t*>(rgba_bitmap.getAddr(0, y));
- const int flipped_y = flip_source ? rgba_bitmap.height() - y - 1 : y;
- uint8_t* dest =
- pixels.get() + flipped_y * rgba_bitmap.width() * bytes_per_pixel;
- for (int x = 0; x < rgba_bitmap.width(); ++x) {
- *(dest++) = *(src++);
- *(dest++) = *(src++);
- *(dest++) = *(src++);
- if (bytes_per_pixel == 4)
- *(dest++) = *(src++);
- else
- ++src;
- }
- }
-
- return pixels;
-}
-
-// Returns a SkBitmap, given a packed RGBA bitmap in OpenGL byte/row order.
-SkBitmap CreateSkBitmapFromGLPixels(const uint8_t* pixels,
- const gfx::Size& size) {
- SkBitmap bitmap;
- bitmap.allocPixels(
- SkImageInfo::Make(size.width(), size.height(), kRGBA_8888_SkColorType,
- kPremul_SkAlphaType),
- size.width() * 4);
- for (int y = 0; y < size.height(); ++y) {
- const int flipped_y = size.height() - y - 1;
- const uint8_t* const src_row = pixels + flipped_y * size.width() * 4;
- void* const dest_row = bitmap.getAddr(0, y);
- std::memcpy(dest_row, src_row, size.width() * 4);
- }
- return bitmap;
-}
-
-// Reads back the texture in the given |mailbox| to a SkBitmap in Skia-native
-// format.
-SkBitmap ReadbackToSkBitmap(gpu::gles2::GLES2Interface* gl,
- const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token,
- const gfx::Size& texture_size) {
- // Bind the texture to a framebuffer from which to read the pixels.
- if (sync_token.HasData())
- gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
- GLuint texture = gl->CreateAndConsumeTextureCHROMIUM(mailbox.name);
- GLuint framebuffer = 0;
- gl->GenFramebuffers(1, &framebuffer);
- gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- texture, 0);
-
- // Read the pixels and convert to SkBitmap form for test comparisons.
- std::unique_ptr<uint8_t[]> pixels(new uint8_t[texture_size.GetArea() * 4]);
- gl->ReadPixels(0, 0, texture_size.width(), texture_size.height(), GL_RGBA,
- GL_UNSIGNED_BYTE, pixels.get());
- gl->DeleteFramebuffers(1, &framebuffer);
- gl->DeleteTextures(1, &texture);
- return CreateSkBitmapFromGLPixels(pixels.get(), texture_size);
-}
-
-// Validates whether all rows are identical (i.e. for each row r != 0, compares
-// it with row 0.
-void ValidateRows(uint8_t* pixel_data, size_t row_stride, size_t height) {
- for (size_t row = 1; row < height; ++row) {
- for (size_t col = 0; col < row_stride; ++col) {
- EXPECT_NEAR(pixel_data[col], pixel_data[col + row * row_stride], 1)
- << " mismatch in row " << row << ", column " << col;
- }
- }
-}
-
-// Returns maximum allowed difference between the expected and actual pixel
-// values.
-int GetTolerance() {
- return 1;
-}
-
-} // namespace
-
-//
-// All tests in this class follow roughly the same pattern::
-// - Construct a CopyOutputRequest, with arguments depending on the test
-// parameters and the specific format that is being tested.
-// - Upload source texture to GL.
-// - Invoke GLRendererCopier::CopyFromTextureOrFramebuffer(), with arguments
-// depending on the test parameters, passing the created CopyOutputRequest.
-// - Load the result into memory and compare with baseline.
-//
-// Parameters:
-// 0. GL format to use when uploading source texture.
-// 1. True if the copier will also receive the texture in a call to
-// `CopyFromTextureOrFramebuffer()`, false otherwise.
-// 2. Destiation for the CopyOutputRequest (native textures or system memory).
-// 3. True if the result should be scaled by half in each dimension, false
-// otherwise.
-// 4. True if the source texture will be flipped (bottom-up), false otherwise.
-class GLRendererCopierPixelTest
- : public cc::PixelTest,
- public testing::WithParamInterface<
- std::tuple<GLenum, bool, CopyOutputResult::Destination, bool, bool>> {
- public:
- // In order to test coordinate calculations and Y-flipping, the tests will
- // issue copy requests for a small region just to the right and below the
- // center of the entire source texture/framebuffer.
- gfx::Rect GetRequestArea() const {
- DCHECK(!source_size_.IsZero());
-
- gfx::Rect result(source_size_.width() / 2, source_size_.height() / 2,
- source_size_.width() / 4, source_size_.height() / 4);
-
- if (scale_by_half_) {
- return gfx::ScaleToEnclosingRect(result, 0.5f);
- }
-
- return result;
- }
-
- void SetUp() override {
- SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft);
-
- texture_deleter_ =
- std::make_unique<TextureDeleter>(base::ThreadTaskRunnerHandle::Get());
-
- source_gl_format_ = std::get<0>(GetParam());
- have_source_texture_ = std::get<1>(GetParam());
- result_destination_ = std::get<2>(GetParam());
- scale_by_half_ = std::get<3>(GetParam());
- flipped_source_ = std::get<4>(GetParam());
-
- gl_ = context_provider()->ContextGL();
- copier_ = std::make_unique<GLRendererCopier>(context_provider(),
- texture_deleter_.get());
-
- ASSERT_TRUE(cc::ReadPNGFile(
- GetTestFilePath(FILE_PATH_LITERAL("16_color_rects.png")),
- &source_bitmap_));
- source_bitmap_.setImmutable();
-
- source_size_ = gfx::Size(source_bitmap_.width(), source_bitmap_.height());
-
- source_bitmap_rgba_ =
- GLScalerTestUtil::CopyAndConvertToRGBA(source_bitmap_);
- source_bitmap_rgba_.setImmutable();
-
- source_bitmap_yuv_ = source_bitmap_rgba_;
- GLScalerTestUtil::ConvertRGBABitmapToYUV(&source_bitmap_yuv_);
- source_bitmap_yuv_.setImmutable();
- }
-
- void TearDown() override {
- DeleteSourceFramebuffer();
- DeleteSourceTexture();
- copier_.reset();
- texture_deleter_.reset();
- }
-
- gpu::gles2::GLES2Interface* gl() { return gl_; }
-
- GLRendererCopier* copier() { return copier_.get(); }
-
- gfx::Rect DrawToWindowSpace(const gfx::Size& source_size,
- const gfx::Rect& draw_rect) {
- gfx::Rect window_rect = draw_rect;
- if (flipped_source_)
- window_rect.set_y(source_size.height() - window_rect.bottom());
- return window_rect;
- }
-
- GLuint CreateSourceTexture(SkBitmap source_bitmap) {
- CHECK_EQ(0u, source_texture_);
- gl_->GenTextures(1, &source_texture_);
- gl_->BindTexture(GL_TEXTURE_2D, source_texture_);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, source_gl_format_, source_bitmap.width(),
- source_bitmap.height(), 0, source_gl_format_,
- GL_UNSIGNED_BYTE,
- CreateGLPixelsFromSkBitmap(source_bitmap, source_gl_format_,
- flipped_source_)
- .get());
- gl_->BindTexture(GL_TEXTURE_2D, 0);
- return source_texture_;
- }
-
- void DeleteSourceTexture() {
- if (source_texture_ != 0) {
- gl_->DeleteTextures(1, &source_texture_);
- source_texture_ = 0;
- }
- }
-
- void CreateAndBindSourceFramebuffer(GLuint texture) {
- ASSERT_EQ(0u, source_framebuffer_);
- gl_->GenFramebuffers(1, &source_framebuffer_);
- gl_->BindFramebuffer(GL_FRAMEBUFFER, source_framebuffer_);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, texture, 0);
- }
-
- void DeleteSourceFramebuffer() {
- if (source_framebuffer_ != 0) {
- gl_->DeleteFramebuffers(1, &source_framebuffer_);
- source_framebuffer_ = 0;
- }
- }
-
- protected:
- // The size of the source texture or framebuffer.
- gfx::Size source_size_;
-
- GLenum source_gl_format_;
- bool have_source_texture_;
- CopyOutputResult::Destination result_destination_;
- bool scale_by_half_;
- bool flipped_source_;
- SkBitmap source_bitmap_;
- SkBitmap source_bitmap_rgba_;
- SkBitmap source_bitmap_yuv_;
-
- private:
- gpu::gles2::GLES2Interface* gl_ = nullptr;
- std::unique_ptr<TextureDeleter> texture_deleter_;
- std::unique_ptr<GLRendererCopier> copier_;
- GLuint source_texture_ = 0;
- GLuint source_framebuffer_ = 0;
-};
-
-// On Android KitKat bots (but not newer ones), the left column of pixels in the
-// result is off-by-one in the red channel. Use the off-by-one camparator as a
-// workaround.
-#if BUILDFLAG(IS_ANDROID)
-#define PIXEL_COMPARATOR() cc::FuzzyPixelOffByOneComparator(false)
-#else
-#define PIXEL_COMPARATOR() cc::ExactPixelComparator(false)
-#endif
-
-TEST_P(GLRendererCopierPixelTest, ExecutesCopyRequestRGBA) {
- // Create and execute a CopyOutputRequest via the GLRendererCopier.
- std::unique_ptr<CopyOutputResult> result;
- {
- base::RunLoop loop;
- auto request = std::make_unique<CopyOutputRequest>(
- CopyOutputRequest::ResultFormat::RGBA, result_destination_,
- base::BindOnce(
- [](std::unique_ptr<CopyOutputResult>* result,
- base::OnceClosure quit_closure,
- std::unique_ptr<CopyOutputResult> result_from_copier) {
- *result = std::move(result_from_copier);
- std::move(quit_closure).Run();
- },
- &result, loop.QuitClosure()));
- if (scale_by_half_) {
- request->SetUniformScaleRatio(2, 1);
- }
-
- request->set_result_selection(GetRequestArea());
-
- const GLuint source_texture = CreateSourceTexture(source_bitmap_);
- CreateAndBindSourceFramebuffer(source_texture);
-
- copy_output::RenderPassGeometry geometry;
- // geometry.result_bounds not used by GLRendererCopier
- geometry.sampling_bounds =
- DrawToWindowSpace(source_size_, gfx::Rect(source_size_));
- geometry.result_selection = request->result_selection();
- geometry.readback_offset =
- DrawToWindowSpace(source_size_, geometry.result_selection)
- .OffsetFromOrigin();
-
- copier()->CopyFromTextureOrFramebuffer(
- std::move(request), geometry, source_gl_format_,
- have_source_texture_ ? source_texture : 0, source_size_,
- flipped_source_, gfx::ColorSpace::CreateSRGB());
- loop.Run();
- }
-
- // Check that a result was produced and is of the expected rect/size.
- ASSERT_TRUE(result);
- ASSERT_FALSE(result->IsEmpty());
- ASSERT_EQ(GetRequestArea(), result->rect());
-
- // Examine the image in the |result|, and compare it to the baseline PNG file.
- absl::optional<CopyOutputResult::ScopedSkBitmap> scoped_bitmap;
- SkBitmap actual;
- if (result_destination_ == CopyOutputResult::Destination::kSystemMemory) {
- scoped_bitmap = result->ScopedAccessSkBitmap();
- actual = scoped_bitmap->bitmap();
- } else {
- actual = ReadbackToSkBitmap(
- gl(), result->GetTextureResult()->planes[0].mailbox,
- result->GetTextureResult()->planes[0].sync_token, result->size());
- }
- const auto png_file_path = GetTestFilePath(
- scale_by_half_ ? FILE_PATH_LITERAL("half_of_one_of_16_color_rects.png")
- : FILE_PATH_LITERAL("one_of_16_color_rects.png"));
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kRebaselinePixelTests))
- EXPECT_TRUE(cc::WritePNGFile(actual, png_file_path, false));
- if (!cc::MatchesPNGFile(actual, png_file_path, PIXEL_COMPARATOR())) {
- LOG(ERROR) << "Entire source: " << cc::GetPNGDataUrl(source_bitmap_);
- ADD_FAILURE();
- }
-}
-
-TEST_P(GLRendererCopierPixelTest, ExecutesCopyRequestNV12) {
- if (result_destination_ ==
- CopyOutputRequest::ResultDestination::kNativeTextures) {
- // TODO(https://crbug.com/1216287): Enable once textures are supported.
- GTEST_SKIP()
- << "Enable once the GLRenderer supports producing producing results to "
- "a texture for NV12 format.";
- }
-
- const gfx::Rect request_area = GetRequestArea();
-
- // Check if request's width and height are even (required for NV12 format).
- // The test case expects the result size to match the request size exactly,
- // which is not possible with NV12 when the request size dimensions aren't
- // even.
- ASSERT_TRUE(request_area.width() % 2 == 0 && request_area.height() % 2 == 0)
- << " request size is not even, request_area.size()="
- << request_area.size().ToString();
-
- // Additionally, the test uses helpers that assume pixel data can be packed (4
- // 8-bit values in 1 32-bit pixel).
- ASSERT_TRUE(request_area.width() % 4 == 0)
- << " request width is not divisible by 4, request_area.width()="
- << request_area.width();
-
- // Create and execute a CopyOutputRequest via the GLRendererCopier.
- std::unique_ptr<CopyOutputResult> result;
- {
- base::RunLoop loop;
- auto request = std::make_unique<CopyOutputRequest>(
- CopyOutputRequest::ResultFormat::NV12_PLANES, result_destination_,
- base::BindOnce(
- [](std::unique_ptr<CopyOutputResult>* result,
- base::OnceClosure quit_closure,
- std::unique_ptr<CopyOutputResult> result_from_copier) {
- *result = std::move(result_from_copier);
- std::move(quit_closure).Run();
- },
- &result, loop.QuitClosure()));
- if (scale_by_half_) {
- request->SetUniformScaleRatio(2, 1);
- }
-
- request->set_result_selection(request_area);
-
- // Upload source texture to GL - the texture will be converted to RGBA if
- // necessary.
- const GLuint source_texture = CreateSourceTexture(source_bitmap_);
- CreateAndBindSourceFramebuffer(source_texture);
-
- copy_output::RenderPassGeometry geometry;
- // geometry.result_bounds not used by GLRendererCopier
- geometry.sampling_bounds =
- DrawToWindowSpace(source_size_, gfx::Rect(source_size_));
- geometry.result_selection = request->result_selection();
- geometry.readback_offset =
- DrawToWindowSpace(source_size_, geometry.result_selection)
- .OffsetFromOrigin();
-
- copier()->CopyFromTextureOrFramebuffer(
- std::move(request), geometry, source_gl_format_,
- have_source_texture_ ? source_texture : 0, source_size_,
- flipped_source_, gfx::ColorSpace::CreateSRGB());
- loop.Run();
- }
-
- // Check that a result was produced and is of the expected rect/size.
- ASSERT_TRUE(result);
- ASSERT_FALSE(result->IsEmpty());
- ASSERT_EQ(request_area, result->rect());
-
- // Examine the image in the |result|, and compare it to the baseline PNG file.
- // Approach is the same as the one in GLNV12ConverterPixelTest.
-
- // Allocate new bitmap, it will then be populated with Y & UV data.
- SkBitmap actual = GLScalerTestUtil::AllocateRGBABitmap(result->size());
- actual.eraseColor(SkColorSetARGB(0xff, 0x00, 0x00, 0x00));
-
- SkBitmap luma_plane;
- SkBitmap chroma_planes;
-
- if (result_destination_ == CopyOutputResult::Destination::kSystemMemory) {
- // Create a bitmap with packed Y values:
- luma_plane = GLScalerTestUtil::AllocateRGBABitmap(
- gfx::Size(result->size().width() / 4, result->size().height()));
-
- chroma_planes = GLScalerTestUtil::AllocateRGBABitmap(
- gfx::Size(luma_plane.width(), luma_plane.height() / 2));
-
- result->ReadNV12Planes(
- reinterpret_cast<uint8_t*>(luma_plane.getAddr(0, 0)),
- result->size().width(),
- reinterpret_cast<uint8_t*>(chroma_planes.getAddr(0, 0)),
- result->size().width());
- } else {
- LOG(ERROR) << "Texture results for NV12 are not supported yet!";
- ADD_FAILURE();
- }
-
- GLScalerTestUtil::UnpackPlanarBitmap(luma_plane, 0, &actual);
- GLScalerTestUtil::UnpackUVBitmap(chroma_planes, &actual);
-
- const auto png_file_path = GetTestFilePath(
- scale_by_half_ ? FILE_PATH_LITERAL("half_of_one_of_16_color_rects.png")
- : FILE_PATH_LITERAL("one_of_16_color_rects.png"));
-
- SkBitmap expected;
- if (!cc::ReadPNGFile(png_file_path, &expected)) {
- LOG(ERROR) << "Cannot read reference image: " << png_file_path.value();
- ADD_FAILURE();
- return;
- }
-
- expected = GLScalerTestUtil::CopyAndConvertToRGBA(expected);
- GLScalerTestUtil::ConvertRGBABitmapToYUV(&expected);
-
- constexpr float kAvgAbsoluteErrorLimit = 16.f;
- constexpr int kMaxAbsoluteErrorLimit = 0x80;
- if (!cc::MatchesBitmap(
- actual, expected,
- cc::FuzzyPixelComparator(false, 100.f, 0.f, kAvgAbsoluteErrorLimit,
- kMaxAbsoluteErrorLimit, 0))) {
- ADD_FAILURE();
- return;
- }
-}
-
-#undef PIXEL_COMPARATOR
-
-// These tests work similarly to `GLRendererCopierPixelTest`, but test various
-// request dimensions in more depth.
-//
-// Parameters:
-// 0. Destiation for the CopyOutputRequest (native textures or system memory).
-// 1. True if the result should be scaled by half in each dimension, false
-// otherwise.
-// 2. True if the request should specify odd coordinates for the result
-// selection rectangle, false otherwise.
-// 3. True if the request should specify odd dimensions for the result selection
-// rectangle, false otherwise.
-class GLRendererCopierDimensionsPixelTest
- : public cc::PixelTest,
- public testing::WithParamInterface<
- std::tuple<CopyOutputResult::Destination, bool, bool>> {
- public:
- void SetUp() override {
- SetUpGLWithoutRenderer(gfx::SurfaceOrigin::kBottomLeft);
-
- texture_deleter_ =
- std::make_unique<TextureDeleter>(base::ThreadTaskRunnerHandle::Get());
-
- result_destination_ = std::get<0>(GetParam());
- scale_by_half_ = std::get<1>(GetParam());
- use_odd_offset_ = std::get<2>(GetParam());
-
- gl_ = context_provider()->ContextGL();
- copier_ = std::make_unique<GLRendererCopier>(context_provider(),
- texture_deleter_.get());
-
- // For this test, use a generated bitmap, with 4 groups of 4 pixels each.
- const std::vector<SkColor> kCycle = {
- // Red:
- SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- SkColorSetARGB(0xff, 0xff, 0x00, 0x00),
- // Green:
- SkColorSetARGB(0xff, 0x00, 0xff, 0x00),
- SkColorSetARGB(0xff, 0x00, 0xff, 0x00),
- SkColorSetARGB(0xff, 0x00, 0xff, 0x00),
- SkColorSetARGB(0xff, 0x00, 0xff, 0x00),
- // Blue:
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff),
- SkColorSetARGB(0xff, 0x00, 0x00, 0xff),
- // White:
- SkColorSetARGB(0xff, 0xff, 0xff, 0xff),
- SkColorSetARGB(0xff, 0xff, 0xff, 0xff),
- SkColorSetARGB(0xff, 0xff, 0xff, 0xff),
- SkColorSetARGB(0xff, 0xff, 0xff, 0xff),
- };
- source_bitmap_ = GLScalerTestUtil::CreateCyclicalTestImage(
- gfx::Size(800, 600), GLScalerTestUtil::VERTICAL_STRIPES, kCycle, 0);
- // source_bitmap_.setImmutable();
- source_bitmap_size_ =
- gfx::Size(source_bitmap_.width(), source_bitmap_.height());
- }
-
- void TearDown() override {
- DeleteSourceFramebuffer();
- DeleteSourceTexture();
- copier_.reset();
- texture_deleter_.reset();
- }
-
- gpu::gles2::GLES2Interface* gl() { return gl_; }
-
- GLRendererCopier* copier() { return copier_.get(); }
-
- gfx::Rect DrawToWindowSpace(const gfx::Size& source_size,
- const gfx::Rect& draw_rect) {
- gfx::Rect window_rect = draw_rect;
- if (flipped_source_)
- window_rect.set_y(source_size.height() - window_rect.bottom());
- return window_rect;
- }
-
- GLuint CreateSourceTexture(SkBitmap source_bitmap) {
- CHECK_EQ(0u, source_texture_);
- gl_->GenTextures(1, &source_texture_);
- gl_->BindTexture(GL_TEXTURE_2D, source_texture_);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl_->TexImage2D(GL_TEXTURE_2D, 0, source_gl_format_, source_bitmap.width(),
- source_bitmap.height(), 0, source_gl_format_,
- GL_UNSIGNED_BYTE,
- CreateGLPixelsFromSkBitmap(source_bitmap, source_gl_format_,
- flipped_source_)
- .get());
- gl_->BindTexture(GL_TEXTURE_2D, 0);
- return source_texture_;
- }
-
- void DeleteSourceTexture() {
- if (source_texture_ != 0) {
- gl_->DeleteTextures(1, &source_texture_);
- source_texture_ = 0;
- }
- }
-
- void CreateAndBindSourceFramebuffer(GLuint texture) {
- ASSERT_EQ(0u, source_framebuffer_);
- gl_->GenFramebuffers(1, &source_framebuffer_);
- gl_->BindFramebuffer(GL_FRAMEBUFFER, source_framebuffer_);
- gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, texture, 0);
- }
-
- void DeleteSourceFramebuffer() {
- if (source_framebuffer_ != 0) {
- gl_->DeleteFramebuffers(1, &source_framebuffer_);
- source_framebuffer_ = 0;
- }
- }
-
- protected:
- GLenum source_gl_format_ = GL_RGBA;
- bool have_source_texture_ = false;
- CopyOutputResult::Destination result_destination_;
- bool scale_by_half_;
- bool flipped_source_ = false;
- bool use_odd_offset_;
- SkBitmap source_bitmap_;
- gfx::Size source_bitmap_size_;
-
- private:
- gpu::gles2::GLES2Interface* gl_ = nullptr;
- std::unique_ptr<TextureDeleter> texture_deleter_;
- std::unique_ptr<GLRendererCopier> copier_;
- GLuint source_texture_ = 0;
- GLuint source_framebuffer_ = 0;
-};
-
-TEST_P(GLRendererCopierDimensionsPixelTest, ExecutesCopyRequestNV12) {
- if (result_destination_ ==
- CopyOutputRequest::ResultDestination::kNativeTextures) {
- // TODO(https://crbug.com/1216287): Enable once textures are supported.
- GTEST_SKIP() << "Enable once the NV12 format supports producing results to "
- "a texture.";
- }
-
- // Result should contain 1px green strip at the beginning if the offset is
- // supposed to be odd.
- const gfx::Rect request_area = [this]() {
- // Capture 2x2 or 4x4 blue strip fragment, depending on scaling.
- gfx::Rect result =
- scale_by_half_ ? gfx::Rect(4, 0, 2, 2) : gfx::Rect(8, 0, 4, 4);
-
- // If we are supposed to ask for a rect with odd offset,
- // make sure that we capture 1 green pixel.
- if (use_odd_offset_) {
- result.set_x(result.x() - 1);
- }
-
- return result;
- }();
-
- // Create and execute a CopyOutputRequest via the GLRendererCopier.
- std::unique_ptr<CopyOutputResult> result;
- {
- base::RunLoop loop;
- auto request = std::make_unique<CopyOutputRequest>(
- CopyOutputRequest::ResultFormat::NV12_PLANES, result_destination_,
- base::BindOnce(
- [](std::unique_ptr<CopyOutputResult>* result,
- base::OnceClosure quit_closure,
- std::unique_ptr<CopyOutputResult> result_from_copier) {
- *result = std::move(result_from_copier);
- std::move(quit_closure).Run();
- },
- &result, loop.QuitClosure()));
-
- if (scale_by_half_) {
- request->SetUniformScaleRatio(2, 1);
- }
-
- request->set_result_selection(gfx::Rect(request_area));
-
- // Upload source texture to GL:
- const GLuint source_texture = CreateSourceTexture(source_bitmap_);
- CreateAndBindSourceFramebuffer(source_texture);
-
- copy_output::RenderPassGeometry geometry;
- // geometry.result_bounds not used by GLRendererCopier
- geometry.sampling_bounds =
- DrawToWindowSpace(source_bitmap_size_, gfx::Rect(source_bitmap_size_));
- geometry.result_selection = request->result_selection();
- geometry.readback_offset =
- DrawToWindowSpace(source_bitmap_size_, geometry.result_selection)
- .OffsetFromOrigin();
-
- copier()->CopyFromTextureOrFramebuffer(
- std::move(request), geometry, source_gl_format_,
- have_source_texture_ ? source_texture : 0, source_bitmap_size_,
- flipped_source_, gfx::ColorSpace::CreateSRGB());
- loop.Run();
- }
-
- // Check that a result was produced and is of the expected rect/size.
- ASSERT_TRUE(result);
- ASSERT_FALSE(result->IsEmpty());
- ASSERT_EQ(request_area, result->rect());
-
- const auto luma_nbytes = copy_output::GetLumaPlaneSize(*result);
- const auto luma_stride = copy_output::GetLumaPlaneStride(*result);
-
- const auto chroma_nbytes = copy_output::GetChromaPlaneSize(*result);
- const auto chroma_stride = copy_output::GetChromaPlaneStride(*result);
-
- std::unique_ptr<uint8_t[]> luma_plane =
- std::make_unique<uint8_t[]>(luma_nbytes);
- std::unique_ptr<uint8_t[]> chroma_planes =
- std::make_unique<uint8_t[]>(chroma_nbytes);
-
- // Examine the image in the |result|, and compare it to the baseline.
- if (result_destination_ == CopyOutputResult::Destination::kSystemMemory) {
- result->ReadNV12Planes(luma_plane.get(), luma_stride, chroma_planes.get(),
- chroma_stride);
- } else {
- LOG(ERROR) << "Texture results for NV12 are not supported yet!";
- ADD_FAILURE();
- }
-
- SkBitmap source_bitmap_yuv = source_bitmap_;
- GLScalerTestUtil::ConvertRGBABitmapToYUV(&source_bitmap_yuv);
-
- // We've asked for a region that starts with one green pixel that is followed
- // by all blue pixels, grab the source colors after they have been converted
- // to YUV to have something to validate against:
- SkColor green_yuv = source_bitmap_yuv.getColor(4, 0);
- SkColor blue_yuv = source_bitmap_yuv.getColor(8, 0);
-
- // Validate first row of luma (first color channel):
- for (int col = 0; col < luma_stride; ++col) {
- if (col == 0 && use_odd_offset_) {
- EXPECT_NEAR(luma_plane[col], SkColorGetR(green_yuv), GetTolerance());
- continue;
- }
-
- EXPECT_NEAR(luma_plane[col], SkColorGetR(blue_yuv), GetTolerance());
- }
-
- // All other luma rows must match the first row:
- ValidateRows(luma_plane.get(), luma_stride, result->rect().height());
-
- // Validate first row of chroma (second and third color channel):
- for (int col = 0; col < chroma_stride; col += 2) {
- if (col == 0 && use_odd_offset_) {
- EXPECT_NEAR(chroma_planes[col], SkColorGetG(green_yuv), GetTolerance());
- EXPECT_NEAR(chroma_planes[col + 1], SkColorGetB(green_yuv),
- GetTolerance());
- continue;
- }
-
- EXPECT_NEAR(chroma_planes[col], SkColorGetG(blue_yuv), GetTolerance());
- EXPECT_NEAR(chroma_planes[col + 1], SkColorGetB(blue_yuv), GetTolerance());
- }
-
- // All other chroma rows must match the first row:
- ValidateRows(chroma_planes.get(), chroma_stride,
- (result->rect().height() + 1) / 2);
-}
-
-TEST_P(GLRendererCopierDimensionsPixelTest, ExecutesCopyRequestI420) {
- if (result_destination_ ==
- CopyOutputRequest::ResultDestination::kNativeTextures) {
- // TODO(https://crbug.com/1216287): Enable once textures are supported.
- GTEST_SKIP() << "Enable once the I420 format supports producing results to "
- "a texture.";
- }
-
- // Result should contain 1px green strip at the beginning if the offset is
- // supposed to be odd.
- const gfx::Rect request_area = [this]() {
- // Capture 2x2 or 4x4 blue strip fragment, depending on scaling.
- gfx::Rect result =
- scale_by_half_ ? gfx::Rect(4, 0, 2, 2) : gfx::Rect(8, 0, 4, 4);
-
- // If we are supposed to ask for a rect with odd offset,
- // make sure that we capture 1 green pixel.
- if (use_odd_offset_) {
- result.set_x(result.x() - 1);
- }
-
- return result;
- }();
-
- // Create and execute a CopyOutputRequest via the GLRendererCopier.
- std::unique_ptr<CopyOutputResult> result;
- {
- base::RunLoop loop;
- auto request = std::make_unique<CopyOutputRequest>(
- CopyOutputRequest::ResultFormat::I420_PLANES, result_destination_,
- base::BindOnce(
- [](std::unique_ptr<CopyOutputResult>* result,
- base::OnceClosure quit_closure,
- std::unique_ptr<CopyOutputResult> result_from_copier) {
- *result = std::move(result_from_copier);
- std::move(quit_closure).Run();
- },
- &result, loop.QuitClosure()));
-
- if (scale_by_half_) {
- request->SetUniformScaleRatio(2, 1);
- }
-
- request->set_result_selection(gfx::Rect(request_area));
-
- // Upload source texture to GL:
- const GLuint source_texture = CreateSourceTexture(source_bitmap_);
- CreateAndBindSourceFramebuffer(source_texture);
-
- copy_output::RenderPassGeometry geometry;
- // geometry.result_bounds not used by GLRendererCopier
- geometry.sampling_bounds =
- DrawToWindowSpace(source_bitmap_size_, gfx::Rect(source_bitmap_size_));
- geometry.result_selection = request->result_selection();
- geometry.readback_offset =
- DrawToWindowSpace(source_bitmap_size_, geometry.result_selection)
- .OffsetFromOrigin();
-
- copier()->CopyFromTextureOrFramebuffer(
- std::move(request), geometry, source_gl_format_,
- have_source_texture_ ? source_texture : 0, source_bitmap_size_,
- flipped_source_, gfx::ColorSpace::CreateSRGB());
- loop.Run();
- }
-
- // Check that a result was produced and is of the expected rect/size.
- ASSERT_TRUE(result);
- ASSERT_FALSE(result->IsEmpty());
- ASSERT_EQ(request_area, result->rect());
-
- // Examine the image in the |result|, and compare it to the baseline PNG file.
-
- const auto luma_nbytes = copy_output::GetLumaPlaneSize(*result);
- const auto luma_stride = copy_output::GetLumaPlaneStride(*result);
-
- const auto chroma_nbytes = copy_output::GetChromaPlaneSize(*result);
- const auto chroma_stride = copy_output::GetChromaPlaneStride(*result);
-
- std::unique_ptr<uint8_t[]> luma_plane =
- std::make_unique<uint8_t[]>(luma_nbytes);
- std::unique_ptr<uint8_t[]> chroma_plane_1 =
- std::make_unique<uint8_t[]>(chroma_nbytes);
- std::unique_ptr<uint8_t[]> chroma_plane_2 =
- std::make_unique<uint8_t[]>(chroma_nbytes);
-
- if (result_destination_ == CopyOutputResult::Destination::kSystemMemory) {
- result->ReadI420Planes(luma_plane.get(), luma_stride, chroma_plane_1.get(),
- chroma_stride, chroma_plane_2.get(), chroma_stride);
- } else {
- LOG(ERROR) << "Texture results for I420 are not supported yet!";
- ADD_FAILURE();
- }
-
- SkBitmap source_bitmap_yuv = source_bitmap_;
- GLScalerTestUtil::ConvertRGBABitmapToYUV(&source_bitmap_yuv);
-
- // We've asked for a region that starts with one green pixel that is followed
- // by all blue pixels, validate:
- SkColor green_yuv = source_bitmap_yuv.getColor(7, 0);
- SkColor blue_yuv = source_bitmap_yuv.getColor(8, 0);
-
- // Validate first row of luma (first channel):
- for (int col = 0; col < luma_stride; ++col) {
- if (col == 0 && use_odd_offset_) {
- EXPECT_NEAR(luma_plane[col], SkColorGetR(green_yuv), GetTolerance());
- continue;
- }
-
- EXPECT_NEAR(luma_plane[col], SkColorGetR(blue_yuv), GetTolerance());
- }
-
- // All other luma rows must match the first row:
- ValidateRows(luma_plane.get(), luma_stride, result->rect().height());
-
- // Validate first row of chroma_1 (second channel):
- for (int col = 0; col < chroma_stride; ++col) {
- if (col == 0 && use_odd_offset_) {
- EXPECT_NEAR(chroma_plane_1[col], SkColorGetG(green_yuv), GetTolerance());
- continue;
- }
-
- EXPECT_NEAR(chroma_plane_1[col], SkColorGetG(blue_yuv), GetTolerance());
- }
-
- // All other chroma_1 rows must match the first row:
- ValidateRows(chroma_plane_1.get(), chroma_stride,
- (result->rect().height() + 1) / 2);
-
- // Validate first row of chroma_2 (third channel):
- for (int col = 0; col < chroma_stride; ++col) {
- if (col == 0 && use_odd_offset_) {
- EXPECT_NEAR(chroma_plane_2[col], SkColorGetB(green_yuv), GetTolerance());
- continue;
- }
-
- EXPECT_NEAR(chroma_plane_2[col], SkColorGetB(blue_yuv), GetTolerance());
- }
-
- // All other chroma_2 rows must match the first row:
- ValidateRows(chroma_plane_2.get(), chroma_stride,
- (result->rect().height() + 1) / 2);
-}
-
-// Instantiate parameter sets for all possible combinations of scenarios
-// GLRendererCopier will encounter, which will cause it to follow different
-// workflows.
-INSTANTIATE_TEST_SUITE_P(
- All,
- GLRendererCopierPixelTest,
- testing::Combine(
- // Source framebuffer GL format.
- testing::Values(static_cast<GLenum>(GL_RGBA),
- static_cast<GLenum>(GL_RGB)),
- // Source: Have texture too?
- testing::Values(false, true),
- // Result destination.
- testing::Values(CopyOutputResult::Destination::kSystemMemory,
- CopyOutputResult::Destination::kNativeTextures),
- // Result scaling: Scale by half?
- testing::Values(false, true),
- // Source content is vertically flipped?
- testing::Values(false, true)));
-
-// Instantiate parameter sets for all possible combinations of scenarios
-// GLRendererCopier will encounter, which will cause it to follow different
-// workflows.
-INSTANTIATE_TEST_SUITE_P(
- All,
- GLRendererCopierDimensionsPixelTest,
- testing::Combine(
- // Result destination.
- testing::Values(CopyOutputResult::Destination::kSystemMemory,
- CopyOutputResult::Destination::kNativeTextures),
- // Result scaling: Scale by half?
- testing::Values(false, true),
- // Use odd offset?
- testing::Values(false, true)));
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc b/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc
deleted file mode 100644
index 8ff8bca30b9..00000000000
--- a/chromium/components/viz/service/display/gl_renderer_copier_unittest.cc
+++ /dev/null
@@ -1,224 +0,0 @@
-// 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 "components/viz/service/display/gl_renderer_copier.h"
-
-#include <stdint.h>
-
-#include <iterator>
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "components/viz/common/frame_sinks/copy_output_request.h"
-#include "components/viz/common/frame_sinks/copy_output_util.h"
-#include "components/viz/test/test_context_provider.h"
-#include "components/viz/test/test_gles2_interface.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/vector2d.h"
-
-namespace viz {
-
-namespace {
-
-class CopierTestGLES2Interface : public TestGLES2Interface {
- public:
- // Sets how GL will respond to queries regarding the implementation's internal
- // read-back format.
- void SetOptimalReadbackFormat(GLenum format, GLenum type) {
- format_ = format;
- type_ = type;
- }
-
- // GLES2Interface override.
- void GetIntegerv(GLenum pname, GLint* params) override {
- switch (pname) {
- case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
- ASSERT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
- CheckFramebufferStatus(GL_FRAMEBUFFER));
- params[0] = format_;
- break;
- case GL_IMPLEMENTATION_COLOR_READ_TYPE:
- ASSERT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
- CheckFramebufferStatus(GL_FRAMEBUFFER));
- params[0] = type_;
- break;
- default:
- TestGLES2Interface::GetIntegerv(pname, params);
- break;
- }
- }
-
- private:
- GLenum format_ = 0;
- GLenum type_ = 0;
-};
-
-} // namespace
-
-class GLRendererCopierTest : public testing::Test {
- public:
- using ReusableThings = GLRendererCopier::ReusableThings;
-
- void SetUp() override {
- context_provider_ = TestContextProvider::Create(
- std::make_unique<CopierTestGLES2Interface>());
- context_provider_->BindToCurrentThread();
- copier_ =
- std::make_unique<GLRendererCopier>(context_provider_.get(), nullptr);
- }
-
- void TearDown() override { copier_.reset(); }
-
- GLRendererCopier* copier() const { return copier_.get(); }
-
- CopierTestGLES2Interface* test_gl() const {
- return static_cast<CopierTestGLES2Interface*>(
- copier_->context_provider_->ContextGL());
- }
-
- // These simply forward method calls to GLRendererCopier.
- std::unique_ptr<ReusableThings> TakeReusableThingsOrCreate(
- const base::UnguessableToken& requester) {
- return copier_->TakeReusableThingsOrCreate(requester);
- }
- void StashReusableThingsOrDelete(const base::UnguessableToken& requester,
- std::unique_ptr<ReusableThings> things) {
- return copier_->StashReusableThingsOrDelete(requester, std::move(things));
- }
- ReusableThings* PeekReusableThings(const base::UnguessableToken& requester) {
- const auto it = copier_->cache_.find(requester);
- if (it == copier_->cache_.end())
- return nullptr;
- return it->second.get();
- }
- size_t GetCopierCacheSize() { return copier_->cache_.size(); }
- void FreeUnusedCachedResources() { copier_->FreeUnusedCachedResources(); }
- GLenum GetOptimalReadbackFormat() const {
- return copier_->GetOptimalReadbackFormat();
- }
-
- static constexpr int kKeepalivePeriod = GLRendererCopier::kKeepalivePeriod;
-
- private:
- scoped_refptr<ContextProvider> context_provider_;
- std::unique_ptr<GLRendererCopier> copier_;
-};
-
-// Tests that named objects, such as textures or framebuffers, are only cached
-// when the CopyOutputRequest has specified a "source" of requests.
-TEST_F(GLRendererCopierTest, ReusesThingsFromSameSource) {
- // With no source set in a copy request, expect to never re-use any textures
- // or framebuffers.
- const base::UnguessableToken no_source;
- EXPECT_EQ(0u, GetCopierCacheSize());
- auto things = TakeReusableThingsOrCreate(no_source);
- EXPECT_TRUE(things);
- StashReusableThingsOrDelete(no_source, std::move(things));
- EXPECT_EQ(nullptr, PeekReusableThings(no_source));
- EXPECT_EQ(0u, GetCopierCacheSize());
-
- // With a source set in the request, objects should now be cached and re-used.
- const auto source = base::UnguessableToken::Create();
- things = TakeReusableThingsOrCreate(source);
- ReusableThings* things_raw_ptr = things.get();
- EXPECT_TRUE(things_raw_ptr);
- StashReusableThingsOrDelete(source, std::move(things));
- EXPECT_EQ(things_raw_ptr, PeekReusableThings(source));
- EXPECT_EQ(1u, GetCopierCacheSize());
-
- // A second, separate source gets its own cache entry.
- const auto source2 = base::UnguessableToken::Create();
- things = TakeReusableThingsOrCreate(source2);
- things_raw_ptr = things.get();
- EXPECT_TRUE(things_raw_ptr);
- EXPECT_EQ(1u, GetCopierCacheSize());
- StashReusableThingsOrDelete(source2, std::move(things));
- EXPECT_EQ(things_raw_ptr, PeekReusableThings(source2));
- EXPECT_EQ(2u, GetCopierCacheSize());
-}
-
-// Tests that cached resources are freed if unused for a while.
-TEST_F(GLRendererCopierTest, FreesUnusedResources) {
- // Take and then stash a ReusableThings instance for a valid source.
- const base::UnguessableToken source = base::UnguessableToken::Create();
- EXPECT_EQ(0u, GetCopierCacheSize());
- StashReusableThingsOrDelete(source, TakeReusableThingsOrCreate(source));
- EXPECT_TRUE(PeekReusableThings(source));
- EXPECT_EQ(1u, GetCopierCacheSize());
-
- // Call FreesUnusedCachedResources() the maximum number of times before the
- // cache entry would be considered for freeing.
- for (int i = 0; i < kKeepalivePeriod - 1; ++i) {
- FreeUnusedCachedResources();
- EXPECT_TRUE(PeekReusableThings(source));
- EXPECT_EQ(1u, GetCopierCacheSize());
- if (HasFailure())
- break;
- }
-
- // Calling FreeUnusedCachedResources() just one more time should cause the
- // cache entry to be freed.
- FreeUnusedCachedResources();
- EXPECT_FALSE(PeekReusableThings(source));
- EXPECT_EQ(0u, GetCopierCacheSize());
-}
-
-TEST_F(GLRendererCopierTest, DetectsBGRAForReadbackFormat) {
- test_gl()->SetOptimalReadbackFormat(GL_BGRA_EXT, GL_UNSIGNED_BYTE);
- EXPECT_EQ(static_cast<GLenum>(GL_BGRA_EXT), GetOptimalReadbackFormat());
-}
-
-TEST_F(GLRendererCopierTest, DetectsRGBAForReadbackFormat) {
- test_gl()->SetOptimalReadbackFormat(GL_RGBA, GL_UNSIGNED_BYTE);
- EXPECT_EQ(static_cast<GLenum>(GL_RGBA), GetOptimalReadbackFormat());
-}
-
-TEST_F(GLRendererCopierTest, FallsBackOnRGBAForReadbackFormat_BadFormat) {
- test_gl()->SetOptimalReadbackFormat(GL_RGB, GL_UNSIGNED_BYTE);
- EXPECT_EQ(static_cast<GLenum>(GL_RGBA), GetOptimalReadbackFormat());
-}
-
-TEST_F(GLRendererCopierTest, FallsBackOnRGBAForReadbackFormat_BadType) {
- test_gl()->SetOptimalReadbackFormat(GL_BGRA_EXT, GL_UNSIGNED_SHORT);
- EXPECT_EQ(static_cast<GLenum>(GL_RGBA), GetOptimalReadbackFormat());
-}
-
-// Tests that copying from a source with a color space that can't be converted
-// to a SkColorSpace will fallback to a transform to sRGB.
-TEST_F(GLRendererCopierTest, FallsBackToSRGBForInvalidSkColorSpaces) {
- std::unique_ptr<CopyOutputResult> result;
- base::RunLoop loop;
- auto request = std::make_unique<CopyOutputRequest>(
- CopyOutputRequest::ResultFormat::RGBA,
- CopyOutputRequest::ResultDestination::kSystemMemory,
- base::BindOnce(
- [](std::unique_ptr<CopyOutputResult>* result_out,
- base::OnceClosure quit_closure,
- std::unique_ptr<CopyOutputResult> result_from_copier) {
- *result_out = std::move(result_from_copier);
- std::move(quit_closure).Run();
- },
- &result, loop.QuitClosure()));
- gfx::Rect bounds(50, 50);
- copy_output::RenderPassGeometry geometry;
- geometry.result_bounds = bounds;
- geometry.result_selection = bounds;
- geometry.sampling_bounds = bounds;
- gfx::ColorSpace hdr_color_space = gfx::ColorSpace::CreatePiecewiseHDR(
- gfx::ColorSpace::PrimaryID::BT2020, 0.5, 1.5);
-
- copier()->CopyFromTextureOrFramebuffer(std::move(request), geometry, GL_RGBA,
- 0, gfx::Size(50, 50), false, hdr_color_space);
- loop.Run();
-
- auto scoped_bitmap = result->ScopedAccessSkBitmap();
- SkBitmap result_bitmap = scoped_bitmap.bitmap();
- ASSERT_NE(nullptr, result_bitmap.colorSpace());
- EXPECT_TRUE(result_bitmap.colorSpace()->isSRGB());
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/gl_renderer_draw_cache.cc b/chromium/components/viz/service/display/gl_renderer_draw_cache.cc
deleted file mode 100644
index 887eec8c2af..00000000000
--- a/chromium/components/viz/service/display/gl_renderer_draw_cache.cc
+++ /dev/null
@@ -1,13 +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.
-
-#include "components/viz/service/display/gl_renderer_draw_cache.h"
-
-namespace viz {
-
-TexturedQuadDrawCache::TexturedQuadDrawCache() = default;
-
-TexturedQuadDrawCache::~TexturedQuadDrawCache() = default;
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/gl_renderer_draw_cache.h b/chromium/components/viz/service/display/gl_renderer_draw_cache.h
deleted file mode 100644
index eb85528501d..00000000000
--- a/chromium/components/viz/service/display/gl_renderer_draw_cache.h
+++ /dev/null
@@ -1,62 +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.
-
-#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_DRAW_CACHE_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_DRAW_CACHE_H_
-
-#include <vector>
-
-#include "components/viz/common/resources/resource_id.h"
-#include "components/viz/service/display/program_binding.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/geometry/mask_filter_info.h"
-
-namespace viz {
-
-// Collects 4 floats at a time for easy upload to GL.
-struct Float4 {
- float data[4];
-};
-
-// Collects 16 floats at a time for easy upload to GL.
-struct Float16 {
- float data[16];
-};
-
-// A cache for storing textured quads to be drawn. Stores the minimum required
-// data to tell if two back to back draws only differ in their transform. Quads
-// that only differ by transform may be coalesced into a single draw call.
-struct TexturedQuadDrawCache {
- TexturedQuadDrawCache();
-
- TexturedQuadDrawCache(const TexturedQuadDrawCache&) = delete;
- TexturedQuadDrawCache& operator=(const TexturedQuadDrawCache&) = delete;
-
- ~TexturedQuadDrawCache();
-
- bool is_empty = true;
-
- // Values tracked to determine if textured quads may be coalesced.
- ProgramKey program_key;
- ResourceId resource_id = kInvalidResourceId;
- bool needs_blending = false;
- bool nearest_neighbor = false;
- SkColor background_color = 0;
- gfx::MaskFilterInfo mask_filter_info;
-
- // A cache for the coalesced quad data.
- std::vector<Float4> uv_xform_data;
- std::vector<float> vertex_opacity_data;
- std::vector<Float16> matrix_data;
-
- // Don't batch if tex clamp rect is given.
- Float4 tex_clamp_rect_data;
-
- // Video frames need special white level adjustment.
- bool is_video_frame = false;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_DRAW_CACHE_H_
diff --git a/chromium/components/viz/service/display/gl_renderer_unittest.cc b/chromium/components/viz/service/display/gl_renderer_unittest.cc
deleted file mode 100644
index 7981afdac9c..00000000000
--- a/chromium/components/viz/service/display/gl_renderer_unittest.cc
+++ /dev/null
@@ -1,5195 +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.
-
-#include "components/viz/service/display/gl_renderer.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <set>
-#include <tuple>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/containers/cxx20_erase.h"
-#include "base/location.h"
-#include "base/strings/stringprintf.h"
-#include "base/task/single_thread_task_runner.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
-#include "cc/base/math_util.h"
-#include "cc/test/fake_impl_task_runner_provider.h"
-#include "cc/test/fake_layer_tree_host_impl.h"
-#include "cc/test/fake_output_surface_client.h"
-#include "cc/test/pixel_test.h"
-#include "cc/test/render_pass_test_utils.h"
-#include "cc/test/resource_provider_test_utils.h"
-#include "components/viz/client/client_resource_provider.h"
-#include "components/viz/common/display/renderer_settings.h"
-#include "components/viz/common/features.h"
-#include "components/viz/common/frame_sinks/copy_output_request.h"
-#include "components/viz/common/frame_sinks/copy_output_result.h"
-#include "components/viz/common/quads/texture_draw_quad.h"
-#include "components/viz/common/resources/platform_color.h"
-#include "components/viz/common/resources/transferable_resource.h"
-#include "components/viz/service/display/display_resource_provider_gl.h"
-#include "components/viz/test/fake_output_surface.h"
-#include "components/viz/test/test_gles2_interface.h"
-#include "components/viz/test/viz_test_suite.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/config/gpu_finch_features.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkMatrix.h"
-#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
-#include "ui/base/ui_base_features.h"
-#include "ui/gfx/color_transform.h"
-#include "ui/gfx/geometry/transform.h"
-#include "ui/latency/latency_info.h"
-
-#if BUILDFLAG(IS_WIN)
-#include "components/viz/service/display/overlay_processor_win.h"
-#elif BUILDFLAG(IS_APPLE)
-#include "components/viz/service/display/overlay_processor_mac.h"
-#elif BUILDFLAG(IS_ANDROID) || defined(USE_OZONE)
-#include "components/viz/service/display/overlay_processor_strategy.h"
-#include "components/viz/service/display/overlay_processor_using_strategy.h"
-#include "components/viz/service/display/overlay_strategy_single_on_top.h"
-#include "components/viz/service/display/overlay_strategy_underlay.h"
-#else // Default
-#include "components/viz/service/display/overlay_processor_stub.h"
-#endif
-
-using testing::_;
-using testing::AnyNumber;
-using testing::Args;
-using testing::AtLeast;
-using testing::Contains;
-using testing::ElementsAre;
-using testing::Expectation;
-using testing::InSequence;
-using testing::Invoke;
-using testing::Mock;
-using testing::Not;
-using testing::Pointee;
-using testing::Return;
-using testing::StrictMock;
-
-namespace viz {
-
-MATCHER_P(MatchesSyncToken, sync_token, "") {
- gpu::SyncToken other;
- memcpy(&other, arg, sizeof(other));
- return other == sync_token;
-}
-
-class GLRendererTest : public testing::Test {
- protected:
- ~GLRendererTest() override {
- // Some tests create CopyOutputRequests which will PostTask ensure
- // they are all cleaned up and completed before destroying the test.
- VizTestSuite::RunUntilIdle();
- }
- AggregatedRenderPass* root_render_pass() {
- return render_passes_in_draw_order_.back().get();
- }
- void DrawFrame(GLRenderer* renderer,
- const gfx::Size& viewport_size,
- const gfx::DisplayColorSpaces& display_color_spaces =
- gfx::DisplayColorSpaces()) {
- SurfaceDamageRectList surface_damage_rect_list;
- renderer->DrawFrame(&render_passes_in_draw_order_, 1.f, viewport_size,
- display_color_spaces,
- std::move(surface_damage_rect_list));
- }
-
- static const Program* current_program(GLRenderer* renderer) {
- return renderer->current_program_;
- }
-
- static TexCoordPrecision get_cached_tex_coord_precision(
- GLRenderer* renderer) {
- return renderer->draw_cache_.program_key.tex_coord_precision();
- }
-
- DebugRendererSettings debug_settings_;
- AggregatedRenderPassList render_passes_in_draw_order_;
-};
-
-#define EXPECT_PROGRAM_VALID(program_binding) \
- do { \
- ASSERT_TRUE(program_binding); \
- EXPECT_TRUE((program_binding)->program()); \
- EXPECT_TRUE((program_binding)->initialized()); \
- } while (false)
-
-static inline SkBlendMode BlendModeToSkXfermode(BlendMode blend_mode) {
- switch (blend_mode) {
- case BLEND_MODE_NONE:
- case BLEND_MODE_NORMAL:
- return SkBlendMode::kSrcOver;
- case BLEND_MODE_DESTINATION_IN:
- return SkBlendMode::kDstIn;
- case BLEND_MODE_SCREEN:
- return SkBlendMode::kScreen;
- case BLEND_MODE_OVERLAY:
- return SkBlendMode::kOverlay;
- case BLEND_MODE_DARKEN:
- return SkBlendMode::kDarken;
- case BLEND_MODE_LIGHTEN:
- return SkBlendMode::kLighten;
- case BLEND_MODE_COLOR_DODGE:
- return SkBlendMode::kColorDodge;
- case BLEND_MODE_COLOR_BURN:
- return SkBlendMode::kColorBurn;
- case BLEND_MODE_HARD_LIGHT:
- return SkBlendMode::kHardLight;
- case BLEND_MODE_SOFT_LIGHT:
- return SkBlendMode::kSoftLight;
- case BLEND_MODE_DIFFERENCE:
- return SkBlendMode::kDifference;
- case BLEND_MODE_EXCLUSION:
- return SkBlendMode::kExclusion;
- case BLEND_MODE_MULTIPLY:
- return SkBlendMode::kMultiply;
- case BLEND_MODE_HUE:
- return SkBlendMode::kHue;
- case BLEND_MODE_SATURATION:
- return SkBlendMode::kSaturation;
- case BLEND_MODE_COLOR:
- return SkBlendMode::kColor;
- case BLEND_MODE_LUMINOSITY:
- return SkBlendMode::kLuminosity;
- }
- return SkBlendMode::kSrcOver;
-}
-
-// Explicitly named to be a friend in GLRenderer for shader access.
-class GLRendererShaderPixelTest : public cc::PixelTest {
- public:
- void SetUp() override {
- SetUpGLRenderer(gfx::SurfaceOrigin::kBottomLeft);
- ASSERT_FALSE(renderer()->IsContextLost());
- }
-
- void TearDown() override {
- cc::PixelTest::TearDown();
- ASSERT_FALSE(renderer());
- }
-
- GLRenderer* renderer() { return static_cast<GLRenderer*>(renderer_.get()); }
-
- void TestShaderWithDrawingFrame(
- const ProgramKey& program_key,
- const DirectRenderer::DrawingFrame& drawing_frame,
- bool validate_output_color_matrix) {
- renderer()->SetCurrentFrameForTesting(drawing_frame);
- const gfx::ColorSpace kSrcColorSpaces[] = {
- gfx::ColorSpace::CreateSRGB(),
- gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB,
- gfx::ColorSpace::TransferID::GAMMA28),
- gfx::ColorSpace::CreateREC709(),
- gfx::ColorSpace::CreateExtendedSRGB(),
- // This will be adjusted to the display's SDR white level, because no
- // level was specified.
- gfx::ColorSpace::CreateSCRGBLinear(),
- // This won't be, because it has a set SDR white level.
- gfx::ColorSpace::CreateSCRGBLinear(123.0f),
- // This will be adjusted to the display's SDR white level, because no
- // level was specified.
- gfx::ColorSpace::CreateHDR10(),
- // This won't be, because it has a set SDR white level.
- gfx::ColorSpace::CreateHDR10(123.0f),
- };
- const gfx::ColorSpace kDstColorSpaces[] = {
- gfx::ColorSpace::CreateSRGB(),
- gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB,
- gfx::ColorSpace::TransferID::GAMMA18),
- gfx::ColorSpace::CreateExtendedSRGB(),
- gfx::ColorSpace::CreateSCRGBLinear(),
- };
- // Note: Use ASSERT_XXX() and not EXPECT_XXX() below since the size of the
- // loop will lead to useless timeout failures on the bots otherwise.
- for (const auto& src_color_space : kSrcColorSpaces) {
- for (const auto& dst_color_space : kDstColorSpaces) {
- renderer()->SetUseProgram(program_key, src_color_space, dst_color_space,
- /*adjust_src_white_level=*/true);
- ASSERT_TRUE(renderer()->current_program_->initialized());
-
- if (src_color_space != dst_color_space) {
- auto adjusted_color_space = src_color_space;
- if (src_color_space.IsHDR()) {
- adjusted_color_space = src_color_space.GetWithSDRWhiteLevel(
- drawing_frame.display_color_spaces.GetSDRMaxLuminanceNits());
- }
- SCOPED_TRACE(
- base::StringPrintf("adjusted_color_space=%s, dst_color_space=%s",
- adjusted_color_space.ToString().c_str(),
- dst_color_space.ToString().c_str()));
-
- gfx::ColorTransform::Options options;
- options.tone_map_pq_and_hlg_to_sdr = !dst_color_space.IsHDR();
- options.sdr_max_luminance_nits =
- drawing_frame.display_color_spaces.GetSDRMaxLuminanceNits();
- auto color_transform = gfx::ColorTransform::NewColorTransform(
- adjusted_color_space, dst_color_space, options);
-
- ASSERT_EQ(color_transform->GetShaderSource(),
- renderer()
- ->current_program_->color_transform_for_testing()
- ->GetShaderSource());
- }
-
- if (validate_output_color_matrix) {
- if (program_key.type() == ProgramType::PROGRAM_TYPE_SOLID_COLOR) {
- ASSERT_EQ(
- -1,
- renderer()->current_program_->output_color_matrix_location());
- } else {
- ASSERT_NE(
- -1,
- renderer()->current_program_->output_color_matrix_location());
- }
- }
- }
- }
- }
-
- void TestShader(const ProgramKey& program_key) {
- TestShaderWithDrawingFrame(program_key, GLRenderer::DrawingFrame(), false);
- }
-
- void TestShadersWithOutputColorMatrix(const ProgramKey& program_key) {
- GLRenderer::DrawingFrame frame;
-
- AggregatedRenderPassList render_passes_in_draw_order;
- gfx::Size viewport_size(100, 100);
- AggregatedRenderPassId root_pass_id{1};
- auto* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);
-
- frame.root_render_pass = root_pass;
- frame.current_render_pass = root_pass;
- frame.render_passes_in_draw_order = &render_passes_in_draw_order;
-
- // Set a non-identity color matrix on the output surface.
- SkM44 color_matrix;
- color_matrix.setRC(0, 0, 0.7f);
- color_matrix.setRC(1, 1, 0.4f);
- color_matrix.setRC(2, 2, 0.5f);
- renderer()->output_surface_->set_color_matrix(color_matrix);
-
- TestShaderWithDrawingFrame(program_key, frame, true);
- }
-
- void TestShadersWithSDRWhiteLevel(const ProgramKey& program_key,
- float sdr_white_level) {
- GLRenderer::DrawingFrame frame;
- frame.display_color_spaces.SetSDRMaxLuminanceNits(sdr_white_level);
- TestShaderWithDrawingFrame(program_key, frame, false);
- }
-
- void TestBasicShaders() {
- TestShader(ProgramKey::DebugBorder());
- TestShader(ProgramKey::SolidColor(NO_AA, false, false));
- TestShader(ProgramKey::SolidColor(USE_AA, false, false));
- TestShader(ProgramKey::SolidColor(NO_AA, true, false));
-
- TestShadersWithOutputColorMatrix(ProgramKey::DebugBorder());
- TestShadersWithOutputColorMatrix(
- ProgramKey::SolidColor(NO_AA, false, false));
- TestShadersWithOutputColorMatrix(
- ProgramKey::SolidColor(USE_AA, false, false));
- TestShadersWithOutputColorMatrix(
- ProgramKey::SolidColor(NO_AA, true, false));
-
- TestShadersWithSDRWhiteLevel(ProgramKey::DebugBorder(), 200.f);
- TestShadersWithSDRWhiteLevel(ProgramKey::SolidColor(NO_AA, false, false),
- 200.f);
- TestShadersWithSDRWhiteLevel(ProgramKey::SolidColor(USE_AA, false, false),
- 200.f);
- TestShadersWithSDRWhiteLevel(ProgramKey::SolidColor(NO_AA, true, false),
- 200.f);
- }
-
- void TestColorShaders() {
- const size_t kNumTransferFns = 7;
- skcms_TransferFunction transfer_fns[kNumTransferFns] = {
- // The identity.
- {1.f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f},
- // The identity, with an if statement.
- {1.f, 1.f, 0.f, 1.f, 0.5f, 0.f, 0.f},
- // Just the power function.
- {1.1f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f},
- // Everything but the power function, nonlinear only.
- {1.f, 0.9f, 0.1f, 0.9f, 0.f, 0.1f, 0.1f},
- // Everything, nonlinear only.
- {1.1f, 0.9f, 0.1f, 0.9f, 0.f, 0.1f, 0.1f},
- // Everything but the power function.
- {1.f, 0.9f, 0.1f, 0.9f, 0.5f, 0.1f, 0.1f},
- // Everything.
- {1.1f, 0.9f, 0.1f, 0.9f, 0.5f, 0.1f, 0.1f},
- };
-
- for (size_t i = 0; i < kNumTransferFns; ++i) {
- skcms_Matrix3x3 primaries;
- gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&primaries);
- gfx::ColorSpace src =
- gfx::ColorSpace::CreateCustom(primaries, transfer_fns[i]);
-
- renderer()->SetCurrentFrameForTesting(GLRenderer::DrawingFrame());
- renderer()->SetUseProgram(ProgramKey::SolidColor(NO_AA, false, false),
- src, gfx::ColorSpace::CreateXYZD50());
- EXPECT_TRUE(renderer()->current_program_->initialized());
- }
- }
-
- void TestShadersWithPrecision(TexCoordPrecision precision) {
- // This program uses external textures and sampler, so it won't compile
- // everywhere.
- if (context_provider()->ContextCapabilities().egl_image_external) {
- TestShader(ProgramKey::VideoStream(precision, false));
- }
- }
-
- void TestShadersWithPrecisionAndBlend(TexCoordPrecision precision,
- BlendMode blend_mode) {
- TestShader(ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode,
- NO_AA, NO_MASK, false, false, false,
- false));
- TestShader(ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode,
- USE_AA, NO_MASK, false, false, false,
- false));
- }
-
- void TestShadersWithPrecisionAndSampler(
- TexCoordPrecision precision,
- SamplerType sampler,
- PremultipliedAlphaMode premultipliedAlpha,
- bool has_background_color,
- bool has_tex_clamp_rect) {
- TestShader(ProgramKey::Texture(precision, sampler, premultipliedAlpha,
- has_background_color, has_tex_clamp_rect,
- false, false));
- }
-
- void TestShadersWithPrecisionAndSamplerTiledAA(
- TexCoordPrecision precision,
- SamplerType sampler,
- PremultipliedAlphaMode premultipliedAlpha) {
- TestShader(ProgramKey::Tile(precision, sampler, USE_AA, premultipliedAlpha,
- false, false, false, false));
- }
-
- void TestShadersWithPrecisionAndSamplerTiled(
- TexCoordPrecision precision,
- SamplerType sampler,
- PremultipliedAlphaMode premultipliedAlpha,
- bool is_opaque,
- bool has_tex_clamp_rect) {
- TestShader(ProgramKey::Tile(precision, sampler, NO_AA, premultipliedAlpha,
- is_opaque, has_tex_clamp_rect, false, false));
- }
-
- void TestYUVShadersWithPrecisionAndSampler(TexCoordPrecision precision,
- SamplerType sampler) {
- // Iterate over alpha plane and nv12 parameters.
- UVTextureMode uv_modes[2] = {UV_TEXTURE_MODE_UV, UV_TEXTURE_MODE_U_V};
- YUVAlphaTextureMode a_modes[2] = {YUV_NO_ALPHA_TEXTURE,
- YUV_HAS_ALPHA_TEXTURE};
- for (auto uv_mode : uv_modes) {
- SCOPED_TRACE(uv_mode);
- for (auto a_mode : a_modes) {
- SCOPED_TRACE(a_mode);
- TestShader(ProgramKey::YUVVideo(precision, sampler, a_mode, uv_mode,
- false, false));
- }
- }
- }
-
- void TestShadersWithMasks(TexCoordPrecision precision,
- SamplerType sampler,
- BlendMode blend_mode,
- bool mask_for_background) {
- TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA,
- HAS_MASK, mask_for_background, false,
- false, false));
- TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA,
- HAS_MASK, mask_for_background, true,
- false, false));
- TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA,
- HAS_MASK, mask_for_background, false,
- false, false));
- TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA,
- HAS_MASK, mask_for_background, true,
- false, false));
- }
-};
-
-namespace {
-
-#if !BUILDFLAG(IS_ANDROID)
-static const TexCoordPrecision kPrecisionList[] = {TEX_COORD_PRECISION_MEDIUM,
- TEX_COORD_PRECISION_HIGH};
-
-static const BlendMode kBlendModeList[LAST_BLEND_MODE + 1] = {
- BLEND_MODE_NONE, BLEND_MODE_NORMAL, BLEND_MODE_DESTINATION_IN,
- BLEND_MODE_SCREEN, BLEND_MODE_OVERLAY, BLEND_MODE_DARKEN,
- BLEND_MODE_LIGHTEN, BLEND_MODE_COLOR_DODGE, BLEND_MODE_COLOR_BURN,
- BLEND_MODE_HARD_LIGHT, BLEND_MODE_SOFT_LIGHT, BLEND_MODE_DIFFERENCE,
- BLEND_MODE_EXCLUSION, BLEND_MODE_MULTIPLY, BLEND_MODE_HUE,
- BLEND_MODE_SATURATION, BLEND_MODE_COLOR, BLEND_MODE_LUMINOSITY,
-};
-
-static const SamplerType kSamplerList[] = {
- SAMPLER_TYPE_2D, SAMPLER_TYPE_2D_RECT, SAMPLER_TYPE_EXTERNAL_OES,
-};
-
-static const PremultipliedAlphaMode kPremultipliedAlphaModeList[] = {
- PREMULTIPLIED_ALPHA, NON_PREMULTIPLIED_ALPHA};
-
-TEST_F(GLRendererShaderPixelTest, BasicShadersCompile) {
- TestBasicShaders();
-}
-
-TEST_F(GLRendererShaderPixelTest, TestColorShadersCompile) {
- TestColorShaders();
-}
-
-class PrecisionShaderPixelTest
- : public GLRendererShaderPixelTest,
- public ::testing::WithParamInterface<TexCoordPrecision> {};
-
-TEST_P(PrecisionShaderPixelTest, ShadersCompile) {
- TestShadersWithPrecision(GetParam());
-}
-
-INSTANTIATE_TEST_SUITE_P(PrecisionShadersCompile,
- PrecisionShaderPixelTest,
- ::testing::ValuesIn(kPrecisionList));
-
-class PrecisionBlendShaderPixelTest
- : public GLRendererShaderPixelTest,
- public ::testing::WithParamInterface<
- std::tuple<TexCoordPrecision, BlendMode>> {};
-
-TEST_P(PrecisionBlendShaderPixelTest, ShadersCompile) {
- TestShadersWithPrecisionAndBlend(std::get<0>(GetParam()),
- std::get<1>(GetParam()));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- PrecisionBlendShadersCompile,
- PrecisionBlendShaderPixelTest,
- ::testing::Combine(::testing::ValuesIn(kPrecisionList),
- ::testing::ValuesIn(kBlendModeList)));
-
-class PrecisionSamplerShaderPixelTest
- : public GLRendererShaderPixelTest,
- public ::testing::WithParamInterface<
- std::tuple<TexCoordPrecision,
- SamplerType,
- PremultipliedAlphaMode,
- bool, // has_background_color
- bool>> {}; // has_tex_clamp_rect
-
-TEST_P(PrecisionSamplerShaderPixelTest, ShadersCompile) {
- SamplerType sampler = std::get<1>(GetParam());
- if (sampler != SAMPLER_TYPE_2D_RECT ||
- context_provider()->ContextCapabilities().texture_rectangle) {
- TestShadersWithPrecisionAndSampler(
- std::get<0>(GetParam()), // TexCoordPrecision
- sampler,
- std::get<2>(GetParam()), // PremultipliedAlphaMode
- std::get<3>(GetParam()), // has_background_color
- std::get<4>(GetParam())); // has_tex_clamp_rect
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(
- PrecisionSamplerShadersCompile,
- PrecisionSamplerShaderPixelTest,
- ::testing::Combine(::testing::ValuesIn(kPrecisionList),
- ::testing::ValuesIn(kSamplerList),
- ::testing::ValuesIn(kPremultipliedAlphaModeList),
- ::testing::Bool(), // has_background_color
- ::testing::Bool())); // has_tex_clamp_rect
-
-class PrecisionSamplerShaderPixelTestTiled
- : public GLRendererShaderPixelTest,
- public ::testing::WithParamInterface<
- std::tuple<TexCoordPrecision,
- SamplerType,
- PremultipliedAlphaMode,
- bool, // is_opaque
- bool>> // has_tex_clamp_rect
-{};
-
-TEST_P(PrecisionSamplerShaderPixelTestTiled, ShadersCompile) {
- SamplerType sampler = std::get<1>(GetParam());
- if (sampler != SAMPLER_TYPE_2D_RECT ||
- context_provider()->ContextCapabilities().texture_rectangle) {
- TestShadersWithPrecisionAndSamplerTiled(
- std::get<0>(GetParam()), // TexCoordPrecision
- sampler,
- std::get<2>(GetParam()), // PremultipliedAlphaMode
- std::get<3>(GetParam()), // is_opaque
- std::get<4>(GetParam())); // has_tex_clamp_rect
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(
- PrecisionSamplerShadersCompile,
- PrecisionSamplerShaderPixelTestTiled,
- ::testing::Combine(::testing::ValuesIn(kPrecisionList),
- ::testing::ValuesIn(kSamplerList),
- ::testing::ValuesIn(kPremultipliedAlphaModeList),
- ::testing::Bool(), // is_opaque
- ::testing::Bool())); // has_tex_clamp_rect
-
-class PrecisionSamplerShaderPixelTestTiledAA
- : public GLRendererShaderPixelTest,
- public ::testing::WithParamInterface<
- std::tuple<TexCoordPrecision, SamplerType, PremultipliedAlphaMode>> {
-};
-
-TEST_P(PrecisionSamplerShaderPixelTestTiledAA, ShadersCompile) {
- SamplerType sampler = std::get<1>(GetParam());
- if (sampler != SAMPLER_TYPE_2D_RECT ||
- context_provider()->ContextCapabilities().texture_rectangle) {
- TestShadersWithPrecisionAndSamplerTiledAA(
- std::get<0>(GetParam()), // TexCoordPrecision
- sampler,
- std::get<2>(GetParam())); // PremultipliedAlphaMode
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(
- PrecisionSamplerShadersCompile,
- PrecisionSamplerShaderPixelTestTiledAA,
- ::testing::Combine(::testing::ValuesIn(kPrecisionList),
- ::testing::ValuesIn(kSamplerList),
- ::testing::ValuesIn(kPremultipliedAlphaModeList)));
-
-class PrecisionSamplerYUVShaderPixelTest
- : public GLRendererShaderPixelTest,
- public ::testing::WithParamInterface<
- std::tuple<TexCoordPrecision, SamplerType>> {};
-
-TEST_P(PrecisionSamplerYUVShaderPixelTest, ShadersCompile) {
- SamplerType sampler = std::get<1>(GetParam());
- if (sampler != SAMPLER_TYPE_2D_RECT ||
- context_provider()->ContextCapabilities().texture_rectangle) {
- TestYUVShadersWithPrecisionAndSampler(
- std::get<0>(GetParam()), // TexCoordPrecision
- sampler);
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(PrecisionSamplerShadersCompile,
- PrecisionSamplerYUVShaderPixelTest,
- ::testing::Combine(::testing::ValuesIn(kPrecisionList),
- ::testing::ValuesIn(kSamplerList)));
-
-class MaskShaderPixelTest
- : public GLRendererShaderPixelTest,
- public ::testing::WithParamInterface<
- std::tuple<TexCoordPrecision, SamplerType, BlendMode, bool>> {};
-
-TEST_P(MaskShaderPixelTest, ShadersCompile) {
- SamplerType sampler = std::get<1>(GetParam());
- if (sampler != SAMPLER_TYPE_2D_RECT ||
- context_provider()->ContextCapabilities().texture_rectangle) {
- TestShadersWithMasks(std::get<0>(GetParam()), sampler,
- std::get<2>(GetParam()), std::get<3>(GetParam()));
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(MaskShadersCompile,
- MaskShaderPixelTest,
- ::testing::Combine(::testing::ValuesIn(kPrecisionList),
- ::testing::ValuesIn(kSamplerList),
- ::testing::ValuesIn(kBlendModeList),
- ::testing::Bool()));
-
-#endif
-
-class FakeRendererGL : public GLRenderer {
- public:
- FakeRendererGL(const RendererSettings* settings,
- const DebugRendererSettings* debug_settings,
- OutputSurface* output_surface,
- DisplayResourceProviderGL* resource_provider)
- : GLRenderer(settings,
- debug_settings,
- output_surface,
- resource_provider,
- nullptr,
- nullptr) {}
-
- FakeRendererGL(const RendererSettings* settings,
- const DebugRendererSettings* debug_settings,
- OutputSurface* output_surface,
- DisplayResourceProviderGL* resource_provider,
- OverlayProcessorInterface* overlay_processor)
- : GLRenderer(settings,
- debug_settings,
- output_surface,
- resource_provider,
- overlay_processor,
- nullptr) {}
-
- FakeRendererGL(
- const RendererSettings* settings,
- const DebugRendererSettings* debug_settings,
- OutputSurface* output_surface,
- DisplayResourceProviderGL* resource_provider,
- OverlayProcessorInterface* overlay_processor,
- scoped_refptr<base::SingleThreadTaskRunner> current_task_runner)
- : GLRenderer(settings,
- debug_settings,
- output_surface,
- resource_provider,
- overlay_processor,
- std::move(current_task_runner)) {}
-
- // GLRenderer methods.
-
- // Changing visibility to public.
- using GLRenderer::stencil_enabled;
-};
-
-class GLRendererWithDefaultHarnessTest : public GLRendererTest {
- protected:
- GLRendererWithDefaultHarnessTest() {
- output_surface_ = FakeOutputSurface::Create3d();
- output_surface_->BindToClient(&output_surface_client_);
-
- resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
- output_surface_->context_provider());
- renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_,
- output_surface_.get(),
- resource_provider_.get());
- renderer_->Initialize();
- renderer_->SetVisible(true);
- }
-
- void SwapBuffers() { renderer_->SwapBuffers({}); }
-
- RendererSettings settings_;
- cc::FakeOutputSurfaceClient output_surface_client_;
- std::unique_ptr<FakeOutputSurface> output_surface_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
- std::unique_ptr<FakeRendererGL> renderer_;
-};
-
-// Closing the namespace here so that GLRendererShaderTest can take advantage
-// of the friend relationship with GLRenderer and all of the mock classes
-// declared above it.
-} // namespace
-
-class GLRendererShaderTest : public GLRendererTest {
- protected:
- GLRendererShaderTest() {
- output_surface_ = FakeOutputSurface::Create3d();
- output_surface_->BindToClient(&output_surface_client_);
-
- resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
- output_surface_->context_provider());
- renderer_ = std::make_unique<FakeRendererGL>(
- &settings_, &debug_settings_, output_surface_.get(),
- resource_provider_.get(), nullptr);
- renderer_->Initialize();
- renderer_->SetVisible(true);
-
- child_context_provider_ = TestContextProvider::Create();
- child_context_provider_->BindToCurrentThread();
- child_resource_provider_ = std::make_unique<ClientResourceProvider>();
- }
-
- ~GLRendererShaderTest() override {
- child_resource_provider_->ShutdownAndReleaseAllResources();
- }
-
- void TestRenderPassProgram(TexCoordPrecision precision,
- BlendMode blend_mode) {
- const Program* program = renderer_->GetProgramIfInitialized(
- ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, NO_AA,
- NO_MASK, false, false, false, false));
- EXPECT_PROGRAM_VALID(program);
- EXPECT_EQ(program, renderer_->current_program_);
- }
-
- void TestRenderPassColorMatrixProgram(TexCoordPrecision precision,
- BlendMode blend_mode) {
- const Program* program = renderer_->GetProgramIfInitialized(
- ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, NO_AA,
- NO_MASK, false, true, false, false));
- EXPECT_PROGRAM_VALID(program);
- EXPECT_EQ(program, renderer_->current_program_);
- }
-
- void TestRenderPassMaskProgram(TexCoordPrecision precision,
- SamplerType sampler,
- BlendMode blend_mode) {
- const Program* program = renderer_->GetProgramIfInitialized(
- ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, HAS_MASK,
- false, false, false, false));
- EXPECT_PROGRAM_VALID(program);
- EXPECT_EQ(program, renderer_->current_program_);
- }
-
- void TestRenderPassMaskColorMatrixProgram(TexCoordPrecision precision,
- SamplerType sampler,
- BlendMode blend_mode) {
- const Program* program = renderer_->GetProgramIfInitialized(
- ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, HAS_MASK,
- false, true, false, false));
- EXPECT_PROGRAM_VALID(program);
- EXPECT_EQ(program, renderer_->current_program_);
- }
-
- void TestRenderPassProgramAA(TexCoordPrecision precision,
- BlendMode blend_mode) {
- const Program* program = renderer_->GetProgramIfInitialized(
- ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, USE_AA,
- NO_MASK, false, false, false, false));
- EXPECT_PROGRAM_VALID(program);
- EXPECT_EQ(program, renderer_->current_program_);
- }
-
- void TestRenderPassColorMatrixProgramAA(TexCoordPrecision precision,
- BlendMode blend_mode) {
- const Program* program = renderer_->GetProgramIfInitialized(
- ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, USE_AA,
- NO_MASK, false, true, false, false));
- EXPECT_PROGRAM_VALID(program);
- EXPECT_EQ(program, renderer_->current_program_);
- }
-
- void TestRenderPassMaskProgramAA(TexCoordPrecision precision,
- SamplerType sampler,
- BlendMode blend_mode) {
- const Program* program = renderer_->GetProgramIfInitialized(
- ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, HAS_MASK,
- false, false, false, false));
- EXPECT_PROGRAM_VALID(program);
- EXPECT_EQ(program, renderer_->current_program_);
- }
-
- void TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision,
- SamplerType sampler,
- BlendMode blend_mode) {
- const Program* program = renderer_->GetProgramIfInitialized(
- ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, HAS_MASK,
- false, true, false, false));
- EXPECT_PROGRAM_VALID(program);
- EXPECT_EQ(program, renderer_->current_program_);
- }
-
- void TestSolidColorProgramAA() {
- const Program* program = renderer_->GetProgramIfInitialized(
- ProgramKey::SolidColor(USE_AA, false, false));
- EXPECT_PROGRAM_VALID(program);
- EXPECT_EQ(program, renderer_->current_program_);
- }
-
- RendererSettings settings_;
- cc::FakeOutputSurfaceClient output_surface_client_;
- std::unique_ptr<FakeOutputSurface> output_surface_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
- scoped_refptr<TestContextProvider> child_context_provider_;
- std::unique_ptr<ClientResourceProvider> child_resource_provider_;
- std::unique_ptr<FakeRendererGL> renderer_;
-};
-
-namespace {
-
-TEST_F(GLRendererWithDefaultHarnessTest, ExternalStencil) {
- gfx::Size viewport_size(1, 1);
- EXPECT_FALSE(renderer_->stencil_enabled());
-
- output_surface_->set_has_external_stencil_test(true);
-
- auto* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- root_pass->has_transparent_background = false;
-
- DrawFrame(renderer_.get(), viewport_size);
- EXPECT_TRUE(renderer_->stencil_enabled());
-}
-
-TEST_F(GLRendererWithDefaultHarnessTest, TextureDrawQuadShaderPrecisionHigh) {
- // TestContextProvider, used inside FakeOuputSurfaceClient, redefines
- // GetShaderPrecisionFormat() and sets the resolution of mediump with
- // 10-bits (1024). So any value higher than 1024 should use highp.
- // The goal is to make sure the fragment shaders used in DoDrawQuad() use
- // the correct precision qualifier.
-
- const gfx::Size viewport_size(1, 1);
- auto* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
-
- const bool needs_blending = false;
- const bool premultiplied_alpha = false;
- const bool flipped = false;
- const bool nearest_neighbor = false;
- const float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- const gfx::PointF uv_top_left(0, 0);
- const gfx::PointF uv_bottom_right(1, 1);
-
- auto child_context_provider = TestContextProvider::Create();
- child_context_provider->BindToCurrentThread();
-
- auto child_resource_provider = std::make_unique<ClientResourceProvider>();
-
- // Here is where the texture is created. Any value bigger than 1024 should use
- // a highp.
- auto transfer_resource = TransferableResource::MakeGL(
- gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
- gfx::Size(1025, 1025), true);
- ResourceId client_resource_id = child_resource_provider->ImportResource(
- transfer_resource, base::DoNothing());
-
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- cc::SendResourceAndGetChildToParentMap(
- {client_resource_id}, resource_provider_.get(),
- child_resource_provider.get(), child_context_provider.get());
- ResourceId resource_id = resource_map[client_resource_id];
-
- // The values defined here should not alter the size of the already created
- // texture.
- TextureDrawQuad* overlay_quad =
- root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
- SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
- shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
- gfx::Rect(1023, 1023), gfx::MaskFilterInfo(),
- absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
- overlay_quad->SetNew(shared_state, gfx::Rect(1023, 1023),
- gfx::Rect(1023, 1023), needs_blending, resource_id,
- premultiplied_alpha, uv_top_left, uv_bottom_right,
- SK_ColorTRANSPARENT, vertex_opacity, flipped,
- nearest_neighbor, /*secure_output_only=*/false,
- gfx::ProtectedVideoType::kClear);
-
- DrawFrame(renderer_.get(), viewport_size);
-
- TexCoordPrecision precision = get_cached_tex_coord_precision(renderer_.get());
- EXPECT_EQ(precision, TEX_COORD_PRECISION_HIGH);
-
- child_resource_provider->ShutdownAndReleaseAllResources();
-}
-
-TEST_F(GLRendererWithDefaultHarnessTest, TextureDrawQuadShaderPrecisionMedium) {
- // TestContextProvider, used inside FakeOuputSurfaceClient, redefines
- // GetShaderPrecisionFormat() and sets the resolution of mediump with
- // 10-bits (1024). So any value higher than 1024 should use highp.
- // The goal is to make sure the fragment shaders used in DoDrawQuad() use
- // the correct precision qualifier.
-
- const gfx::Size viewport_size(1, 1);
- auto* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
-
- const bool needs_blending = false;
- const bool premultiplied_alpha = false;
- const bool flipped = false;
- const bool nearest_neighbor = false;
- const float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- const gfx::PointF uv_top_left(0, 0);
- const gfx::PointF uv_bottom_right(1, 1);
-
- auto child_context_provider = TestContextProvider::Create();
- child_context_provider->BindToCurrentThread();
-
- auto child_resource_provider = std::make_unique<ClientResourceProvider>();
-
- // Here is where the texture is created. Any value smaller than 1024 should
- // use a mediump.
- auto transfer_resource = TransferableResource::MakeGL(
- gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
- gfx::Size(1023, 1023), true);
- ResourceId client_resource_id = child_resource_provider->ImportResource(
- transfer_resource, base::DoNothing());
-
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- cc::SendResourceAndGetChildToParentMap(
- {client_resource_id}, resource_provider_.get(),
- child_resource_provider.get(), child_context_provider.get());
- ResourceId resource_id = resource_map[client_resource_id];
-
- // The values defined here should not alter the size of the already created
- // texture.
- TextureDrawQuad* overlay_quad =
- root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
- SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
- shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
- gfx::Rect(1025, 1025), gfx::MaskFilterInfo(),
- absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
- overlay_quad->SetNew(shared_state, gfx::Rect(1025, 1025),
- gfx::Rect(1025, 1025), needs_blending, resource_id,
- premultiplied_alpha, uv_top_left, uv_bottom_right,
- SK_ColorTRANSPARENT, vertex_opacity, flipped,
- nearest_neighbor, /*secure_output_only=*/false,
- gfx::ProtectedVideoType::kClear);
-
- DrawFrame(renderer_.get(), viewport_size);
-
- TexCoordPrecision precision = get_cached_tex_coord_precision(renderer_.get());
- EXPECT_EQ(precision, TEX_COORD_PRECISION_MEDIUM);
-
- child_resource_provider->ShutdownAndReleaseAllResources();
-}
-
-class GLRendererTextureDrawQuadHDRTest
- : public GLRendererWithDefaultHarnessTest {
- protected:
- void RunTest(bool is_video_frame) {
- const gfx::Size viewport_size(10, 10);
- auto* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
-
- const bool needs_blending = false;
- const bool premultiplied_alpha = false;
- const bool flipped = false;
- const bool nearest_neighbor = false;
- const float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- const gfx::PointF uv_top_left(0, 0);
- const gfx::PointF uv_bottom_right(1, 1);
-
- auto child_context_provider = TestContextProvider::Create();
- child_context_provider->BindToCurrentThread();
-
- auto child_resource_provider = std::make_unique<ClientResourceProvider>();
-
- constexpr gfx::Size kTextureSize = gfx::Size(10, 10);
- auto transfer_resource = TransferableResource::MakeGL(
- gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
- kTextureSize, true);
- transfer_resource.color_space = gfx::ColorSpace::CreateSCRGBLinear();
- ResourceId client_resource_id = child_resource_provider->ImportResource(
- transfer_resource, base::DoNothing());
-
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- cc::SendResourceAndGetChildToParentMap(
- {client_resource_id}, resource_provider_.get(),
- child_resource_provider.get(), child_context_provider.get());
- ResourceId resource_id = resource_map[client_resource_id];
-
- TextureDrawQuad* overlay_quad =
- root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
- SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
- shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
- gfx::Rect(kTextureSize), gfx::MaskFilterInfo(),
- absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
- overlay_quad->SetNew(shared_state, gfx::Rect(kTextureSize),
- gfx::Rect(kTextureSize), needs_blending, resource_id,
- premultiplied_alpha, uv_top_left, uv_bottom_right,
- SK_ColorTRANSPARENT, vertex_opacity, flipped,
- nearest_neighbor, /*secure_output_only=*/false,
- gfx::ProtectedVideoType::kClear);
- overlay_quad->is_video_frame = is_video_frame;
-
- constexpr float kSDRWhiteLevel = 123.0f;
- gfx::DisplayColorSpaces display_color_spaces;
- display_color_spaces.SetSDRMaxLuminanceNits(kSDRWhiteLevel);
-
- DrawFrame(renderer_.get(), viewport_size, display_color_spaces);
-
- const Program* program = current_program(renderer_.get());
- DCHECK(program);
- DCHECK(program->color_transform_for_testing())
- << program->fragment_shader().GetShaderString();
-
- const gfx::ColorSpace expected_src_color_space =
- is_video_frame
- ? gfx::ColorSpace::CreateSCRGBLinear().GetWithSDRWhiteLevel(
- kSDRWhiteLevel)
- : gfx::ColorSpace::CreateSCRGBLinear();
- EXPECT_EQ(program->color_transform_for_testing()->GetSrcColorSpace(),
- expected_src_color_space);
-
- child_resource_provider->ShutdownAndReleaseAllResources();
- }
-};
-
-TEST_F(GLRendererTextureDrawQuadHDRTest, VideoFrame) {
- RunTest(/*is_video_frame=*/true);
-}
-
-TEST_F(GLRendererTextureDrawQuadHDRTest, NotVideoFrame) {
- RunTest(/*is_video_frame=*/false);
-}
-
-class ForbidSynchronousCallGLES2Interface : public TestGLES2Interface {
- public:
- ForbidSynchronousCallGLES2Interface() = default;
-
- void GetAttachedShaders(GLuint program,
- GLsizei max_count,
- GLsizei* count,
- GLuint* shaders) override {
- ADD_FAILURE();
- }
-
- GLint GetAttribLocation(GLuint program, const GLchar* name) override {
- ADD_FAILURE();
- return 0;
- }
-
- void GetBooleanv(GLenum pname, GLboolean* value) override { ADD_FAILURE(); }
-
- void GetBufferParameteriv(GLenum target,
- GLenum pname,
- GLint* value) override {
- ADD_FAILURE();
- }
-
- GLenum GetError() override {
- ADD_FAILURE();
- return GL_NO_ERROR;
- }
-
- void GetFloatv(GLenum pname, GLfloat* value) override { ADD_FAILURE(); }
-
- void GetFramebufferAttachmentParameteriv(GLenum target,
- GLenum attachment,
- GLenum pname,
- GLint* value) override {
- ADD_FAILURE();
- }
-
- void GetIntegerv(GLenum pname, GLint* value) override {
- if (pname == GL_MAX_TEXTURE_SIZE) {
- // MAX_TEXTURE_SIZE is cached client side, so it's OK to query.
- *value = 1024;
- } else {
- ADD_FAILURE();
- }
- }
-
- // We allow querying the shader compilation and program link status in debug
- // mode, but not release.
- void GetProgramiv(GLuint program, GLenum pname, GLint* value) override {
- ADD_FAILURE();
- }
-
- void GetShaderiv(GLuint shader, GLenum pname, GLint* value) override {
- ADD_FAILURE();
- }
-
- void GetRenderbufferParameteriv(GLenum target,
- GLenum pname,
- GLint* value) override {
- ADD_FAILURE();
- }
-
- void GetShaderPrecisionFormat(GLenum shadertype,
- GLenum precisiontype,
- GLint* range,
- GLint* precision) override {
- ADD_FAILURE();
- }
-
- void GetTexParameterfv(GLenum target, GLenum pname, GLfloat* value) override {
- ADD_FAILURE();
- }
-
- void GetTexParameteriv(GLenum target, GLenum pname, GLint* value) override {
- ADD_FAILURE();
- }
-
- void GetUniformfv(GLuint program, GLint location, GLfloat* value) override {
- ADD_FAILURE();
- }
-
- void GetUniformiv(GLuint program, GLint location, GLint* value) override {
- ADD_FAILURE();
- }
-
- GLint GetUniformLocation(GLuint program, const GLchar* name) override {
- ADD_FAILURE();
- return 0;
- }
-
- void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* value) override {
- ADD_FAILURE();
- }
-
- void GetVertexAttribiv(GLuint index, GLenum pname, GLint* value) override {
- ADD_FAILURE();
- }
-
- void GetVertexAttribPointerv(GLuint index,
- GLenum pname,
- void** pointer) override {
- ADD_FAILURE();
- }
-};
-
-TEST_F(GLRendererTest, InitializationDoesNotMakeSynchronousCalls) {
- auto gl_owned = std::make_unique<ForbidSynchronousCallGLES2Interface>();
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
-}
-
-class LoseContextOnFirstGetGLES2Interface : public TestGLES2Interface {
- public:
- LoseContextOnFirstGetGLES2Interface() {}
-
- void GetProgramiv(GLuint program, GLenum pname, GLint* value) override {
- LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
- GL_INNOCENT_CONTEXT_RESET_ARB);
- *value = 0;
- }
-
- void GetShaderiv(GLuint shader, GLenum pname, GLint* value) override {
- LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
- GL_INNOCENT_CONTEXT_RESET_ARB);
- *value = 0;
- }
-};
-
-TEST_F(GLRendererTest, InitializationWithQuicklyLostContextDoesNotAssert) {
- auto gl_owned = std::make_unique<LoseContextOnFirstGetGLES2Interface>();
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
-}
-
-class ClearCountingGLES2Interface : public TestGLES2Interface {
- public:
- ClearCountingGLES2Interface() = default;
-
- MOCK_METHOD3(DiscardFramebufferEXT,
- void(GLenum target,
- GLsizei numAttachments,
- const GLenum* attachments));
- MOCK_METHOD1(Clear, void(GLbitfield mask));
-};
-
-TEST_F(GLRendererTest, OpaqueBackground) {
- auto gl_owned = std::make_unique<ClearCountingGLES2Interface>();
- gl_owned->set_have_discard_framebuffer(true);
-
- auto* gl = gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(1, 1);
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- root_pass->has_transparent_background = false;
-
- // On DEBUG builds, render passes with opaque background clear to blue to
- // easily see regions that were not drawn on the screen.
- EXPECT_CALL(*gl, DiscardFramebufferEXT(GL_FRAMEBUFFER, _, _))
- .With(Args<2, 1>(ElementsAre(GL_COLOR_EXT)))
- .Times(1);
-#ifdef NDEBUG
- EXPECT_CALL(*gl, Clear(_)).Times(0);
-#else
- EXPECT_CALL(*gl, Clear(_)).Times(1);
-#endif
- DrawFrame(&renderer, viewport_size);
- Mock::VerifyAndClearExpectations(gl);
-}
-
-TEST_F(GLRendererTest, TransparentBackground) {
- auto gl_owned = std::make_unique<ClearCountingGLES2Interface>();
- auto* gl = gl_owned.get();
- gl_owned->set_have_discard_framebuffer(true);
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(1, 1);
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- root_pass->has_transparent_background = true;
-
- EXPECT_CALL(*gl, DiscardFramebufferEXT(GL_FRAMEBUFFER, 1, _)).Times(1);
- EXPECT_CALL(*gl, Clear(_)).Times(1);
- DrawFrame(&renderer, viewport_size);
-
- Mock::VerifyAndClearExpectations(gl);
-}
-
-TEST_F(GLRendererTest, OffscreenOutputSurface) {
- auto gl_owned = std::make_unique<ClearCountingGLES2Interface>();
- auto* gl = gl_owned.get();
- gl_owned->set_have_discard_framebuffer(true);
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::CreateOffscreen(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(1, 1);
- cc::AddRenderPass(&render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- EXPECT_CALL(*gl, DiscardFramebufferEXT(GL_FRAMEBUFFER, _, _))
- .With(Args<2, 1>(ElementsAre(GL_COLOR_ATTACHMENT0)))
- .Times(1);
- EXPECT_CALL(*gl, Clear(_)).Times(AnyNumber());
- DrawFrame(&renderer, viewport_size);
- Mock::VerifyAndClearExpectations(gl);
-}
-
-class TextureStateTrackingGLES2Interface : public TestGLES2Interface {
- public:
- TextureStateTrackingGLES2Interface() : active_texture_(GL_INVALID_ENUM) {}
-
- MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token));
- MOCK_METHOD3(TexParameteri, void(GLenum target, GLenum pname, GLint param));
- MOCK_METHOD4(
- DrawElements,
- void(GLenum mode, GLsizei count, GLenum type, const void* indices));
-
- void ActiveTexture(GLenum texture) override {
- EXPECT_NE(texture, active_texture_);
- active_texture_ = texture;
- }
-
- GLenum active_texture() const { return active_texture_; }
-
- private:
- GLenum active_texture_;
-};
-
-#define EXPECT_FILTER_CALL(filter) \
- EXPECT_CALL(*gl, \
- TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter)); \
- EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter));
-
-TEST_F(GLRendererTest, ActiveTextureState) {
- auto child_gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
- auto child_context_provider =
- TestContextProvider::Create(std::move(child_gl_owned));
- child_context_provider->BindToCurrentThread();
- auto child_resource_provider = std::make_unique<ClientResourceProvider>();
-
- auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
- gl_owned->set_have_extension_egl_image(true);
- auto* gl = gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- // During initialization we are allowed to set any texture parameters.
- EXPECT_CALL(*gl, TexParameteri(_, _, _)).Times(AnyNumber());
-
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(100, 100), gfx::Transform(), cc::FilterOperations());
- gpu::SyncToken mailbox_sync_token;
- cc::AddOneOfEveryQuadTypeInDisplayResourceProvider(
- root_pass, resource_provider.get(), child_resource_provider.get(),
- child_context_provider.get(), AggregatedRenderPassId{0},
- &mailbox_sync_token);
-
- EXPECT_EQ(12u, resource_provider->num_resources());
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // Set up expected texture filter state transitions that match the quads
- // created in AppendOneOfEveryQuadType().
- Mock::VerifyAndClearExpectations(gl);
- {
- InSequence sequence;
- // The verified flush flag will be set by
- // ClientResourceProvider::PrepareSendToParent. Before checking if
- // the gpu::SyncToken matches, set this flag first.
- mailbox_sync_token.SetVerifyFlush();
- // In AddOneOfEveryQuadTypeInDisplayResourceProvider, resources are added
- // into RenderPass with the below order: resource6, resource1, resource8
- // (with mailbox), resource2, resource3, resource4, resource9, resource10,
- // resource11, resource12. resource8 has its own mailbox mailbox_sync_token.
- // The rest resources share a common default sync token.
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(2);
- EXPECT_CALL(*gl,
- WaitSyncTokenCHROMIUM(MatchesSyncToken(mailbox_sync_token)))
- .Times(1);
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(7);
-
- // yuv_quad is drawn with the default linear filter.
- for (int i = 0; i < 4; ++i) {
- EXPECT_FILTER_CALL(GL_LINEAR);
- }
- EXPECT_CALL(*gl, DrawElements(_, _, _, _));
-
- // tile_quad is drawn with GL_NEAREST because it is not transformed or
- // scaled.
- EXPECT_FILTER_CALL(GL_NEAREST);
- EXPECT_CALL(*gl, DrawElements(_, _, _, _));
-
- // transformed tile_quad
- EXPECT_FILTER_CALL(GL_LINEAR);
- EXPECT_CALL(*gl, DrawElements(_, _, _, _));
-
- // scaled tile_quad
- EXPECT_FILTER_CALL(GL_LINEAR);
- EXPECT_CALL(*gl, DrawElements(_, _, _, _));
-
- // texture_quad without nearest neighbor
- EXPECT_FILTER_CALL(GL_LINEAR);
- EXPECT_CALL(*gl, DrawElements(_, _, _, _));
-
- // texture_quad without nearest neighbor
- EXPECT_FILTER_CALL(GL_LINEAR);
- EXPECT_CALL(*gl, DrawElements(_, _, _, _));
-
- if (features::IsUsingFastPathForSolidColorQuad()) {
- // stream video and debug draw quads
- EXPECT_CALL(*gl, DrawElements(_, _, _, _)).Times(2);
- } else {
- // stream video, solid color, and debug draw quads
- EXPECT_CALL(*gl, DrawElements(_, _, _, _)).Times(3);
- }
- }
-
- gfx::Size viewport_size(100, 100);
- DrawFrame(&renderer, viewport_size);
- Mock::VerifyAndClearExpectations(gl);
-
- child_resource_provider->ShutdownAndReleaseAllResources();
-}
-
-class BufferSubDataTrackingGLES2Interface : public TestGLES2Interface {
- public:
- BufferSubDataTrackingGLES2Interface() = default;
- ~BufferSubDataTrackingGLES2Interface() override = default;
-
- void BufferSubData(GLenum target,
- GLintptr offset,
- GLsizeiptr size,
- const void* data) override {
- if (target != GL_ARRAY_BUFFER)
- return;
- DCHECK_EQ(0, offset);
- last_array_data.resize(size);
- memcpy(last_array_data.data(), data, size);
- }
-
- std::vector<uint8_t> last_array_data;
-};
-
-TEST_F(GLRendererTest, DrawYUVVideoDrawQuadWithVisibleRect) {
- gfx::Size viewport_size(100, 100);
-
- auto mock_gl_owned = std::make_unique<BufferSubDataTrackingGLES2Interface>();
- BufferSubDataTrackingGLES2Interface* mock_gl = mock_gl_owned.get();
- auto provider = TestContextProvider::Create(std::move(mock_gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- root_pass->has_transparent_background = false;
-
- gfx::Rect rect(viewport_size);
- gfx::Rect visible_rect(rect);
- gfx::RectF tex_coord_rect(0, 0, 1, 1);
- visible_rect.Inset(gfx::Insets::TLBR(20, 10, 40, 30));
-
- SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
- shared_state->SetAll(gfx::Transform(), gfx::Rect(), rect,
- gfx::MaskFilterInfo(), absl::nullopt, false, 1,
- SkBlendMode::kSrcOver, 0);
-
- YUVVideoDrawQuad* quad =
- root_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
- quad->SetNew(shared_state, rect, visible_rect, /*needs_blending=*/false,
- tex_coord_rect, tex_coord_rect, rect.size(), rect.size(),
- ResourceId(1), ResourceId(1), ResourceId(1), ResourceId(1),
- gfx::ColorSpace(), 0, 1.0, 8);
-
- DrawFrame(&renderer, viewport_size);
-
- ASSERT_EQ(96u, mock_gl->last_array_data.size());
- float* geometry_binding_vertexes =
- reinterpret_cast<float*>(mock_gl->last_array_data.data());
-
- const double kEpsilon = 1e-6;
- EXPECT_NEAR(-0.4f, geometry_binding_vertexes[0], kEpsilon);
- EXPECT_NEAR(-0.3f, geometry_binding_vertexes[1], kEpsilon);
- EXPECT_NEAR(0.1f, geometry_binding_vertexes[3], kEpsilon);
- EXPECT_NEAR(0.2f, geometry_binding_vertexes[4], kEpsilon);
-
- EXPECT_NEAR(0.2f, geometry_binding_vertexes[12], kEpsilon);
- EXPECT_NEAR(0.1f, geometry_binding_vertexes[13], kEpsilon);
- EXPECT_NEAR(0.7f, geometry_binding_vertexes[15], kEpsilon);
- EXPECT_NEAR(0.6f, geometry_binding_vertexes[16], kEpsilon);
-}
-
-class NoClearRootRenderPassMockGLES2Interface : public TestGLES2Interface {
- public:
- MOCK_METHOD1(Clear, void(GLbitfield mask));
- MOCK_METHOD4(
- DrawElements,
- void(GLenum mode, GLsizei count, GLenum type, const void* indices));
-};
-
-TEST_F(GLRendererTest, ShouldClearRootRenderPass) {
- auto mock_gl_owned =
- std::make_unique<NoClearRootRenderPassMockGLES2Interface>();
- NoClearRootRenderPassMockGLES2Interface* mock_gl = mock_gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(mock_gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- settings.should_clear_root_render_pass = false;
-
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(10, 10);
-
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPass* child_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, child_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(child_pass, gfx::Rect(viewport_size), SK_ColorBLUE);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
-
- cc::AddRenderPassQuad(root_pass, child_pass);
-
-#ifdef NDEBUG
- GLint clear_bits = GL_COLOR_BUFFER_BIT;
-#else
- GLint clear_bits = GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
-#endif
-
- // First render pass is not the root one, clearing should happen.
- EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(AtLeast(1));
-
- Expectation first_render_pass =
- EXPECT_CALL(*mock_gl, DrawElements(_, _, _, _)).Times(1);
-
- if (features::IsUsingFastPathForSolidColorQuad()) {
- // The second render pass is the root one, clearing should be prevented. The
- // one call is expected due to the solid color draw quad which uses glClear
- // to draw the quad.
- EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(1).After(first_render_pass);
- } else {
- // The second render pass is the root one, clearing should be prevented.
- EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(0).After(first_render_pass);
- }
-
- EXPECT_CALL(*mock_gl, DrawElements(_, _, _, _))
- .Times(AnyNumber())
- .After(first_render_pass);
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
-
- // In multiple render passes all but the root pass should clear the
- // framebuffer.
- Mock::VerifyAndClearExpectations(&mock_gl);
-}
-
-class ScissorTestOnClearCheckingGLES2Interface : public TestGLES2Interface {
- public:
- ScissorTestOnClearCheckingGLES2Interface() = default;
-
- void ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override {
- // RGBA - {0, 0, 0, 0} is used to clear the buffer before drawing onto the
- // render target. Any other color means a solid color draw quad is being
- // drawn.
- if (features::IsUsingFastPathForSolidColorQuad())
- is_drawing_solid_color_quad_ = !(r == 0 && g == 0 && b == 0 && a == 0);
- }
-
- void Clear(GLbitfield bits) override {
- // GL clear is also used to draw solid color draw quads.
- if ((bits & GL_COLOR_BUFFER_BIT) && is_drawing_solid_color_quad_)
- return;
- EXPECT_FALSE(scissor_enabled_);
- }
-
- void Enable(GLenum cap) override {
- if (cap == GL_SCISSOR_TEST)
- scissor_enabled_ = true;
- }
-
- void Disable(GLenum cap) override {
- if (cap == GL_SCISSOR_TEST)
- scissor_enabled_ = false;
- }
-
- private:
- bool scissor_enabled_ = false;
- bool is_drawing_solid_color_quad_ = false;
-};
-
-TEST_F(GLRendererTest, ScissorTestWhenClearing) {
- auto gl_owned = std::make_unique<ScissorTestOnClearCheckingGLES2Interface>();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- EXPECT_FALSE(renderer.use_partial_swap());
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(100, 100);
-
- gfx::Rect grand_child_rect(25, 25);
- AggregatedRenderPassId grand_child_pass_id{3};
- AggregatedRenderPass* grand_child_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, grand_child_pass_id, grand_child_rect,
- gfx::Transform(), cc::FilterOperations());
- cc::AddClippedQuad(grand_child_pass, grand_child_rect, SK_ColorYELLOW);
-
- gfx::Rect child_rect(50, 50);
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(child_pass, child_rect, SK_ColorBLUE);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
-
- cc::AddRenderPassQuad(root_pass, child_pass);
- cc::AddRenderPassQuad(child_pass, grand_child_pass);
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
-}
-
-class DiscardCheckingGLES2Interface : public TestGLES2Interface {
- public:
- DiscardCheckingGLES2Interface() = default;
-
- void DiscardFramebufferEXT(GLenum target,
- GLsizei numAttachments,
- const GLenum* attachments) override {
- ++discarded_;
- }
-
- int discarded() const { return discarded_; }
- void reset_discarded() { discarded_ = 0; }
-
- private:
- int discarded_ = 0;
-};
-
-TEST_F(GLRendererTest, NoDiscardOnPartialUpdates) {
- auto gl_owned = std::make_unique<DiscardCheckingGLES2Interface>();
- gl_owned->set_have_post_sub_buffer(true);
- gl_owned->set_have_discard_framebuffer(true);
-
- auto* gl = gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- auto output_surface = FakeOutputSurface::Create3d(std::move(provider));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- settings.partial_swap_enabled = true;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- EXPECT_TRUE(renderer.use_partial_swap());
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(100, 100);
- {
- // Draw one black frame to make sure the output surface is reshaped before
- // testes.
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK);
- root_pass->damage_rect = gfx::Rect(viewport_size);
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
- gl->reset_discarded();
- }
- {
- // Partial frame, should not discard.
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
- root_pass->damage_rect = gfx::Rect(2, 2, 3, 3);
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
- EXPECT_EQ(0, gl->discarded());
- gl->reset_discarded();
- }
- {
- // Full frame, should discard.
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
- root_pass->damage_rect = root_pass->output_rect;
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
- EXPECT_EQ(1, gl->discarded());
- gl->reset_discarded();
- }
- {
- // Full frame, external scissor is set, should not discard.
- output_surface->set_has_external_stencil_test(true);
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
- root_pass->damage_rect = root_pass->output_rect;
- root_pass->has_transparent_background = false;
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
- EXPECT_EQ(0, gl->discarded());
- gl->reset_discarded();
- output_surface->set_has_external_stencil_test(false);
- }
-}
-
-class ResourceTrackingGLES2Interface : public TestGLES2Interface {
- public:
- ResourceTrackingGLES2Interface() = default;
- ~ResourceTrackingGLES2Interface() override { CheckNoResources(); }
-
- void CheckNoResources() {
- EXPECT_TRUE(textures_.empty());
- EXPECT_TRUE(buffers_.empty());
- EXPECT_TRUE(framebuffers_.empty());
- EXPECT_TRUE(renderbuffers_.empty());
- EXPECT_TRUE(queries_.empty());
- EXPECT_TRUE(shaders_.empty());
- EXPECT_TRUE(programs_.empty());
- }
-
- void GenTextures(GLsizei n, GLuint* textures) override {
- GenIds(&textures_, n, textures);
- }
-
- void GenBuffers(GLsizei n, GLuint* buffers) override {
- GenIds(&buffers_, n, buffers);
- }
-
- void GenFramebuffers(GLsizei n, GLuint* framebuffers) override {
- GenIds(&framebuffers_, n, framebuffers);
- }
-
- void GenRenderbuffers(GLsizei n, GLuint* renderbuffers) override {
- GenIds(&renderbuffers_, n, renderbuffers);
- }
-
- void GenQueriesEXT(GLsizei n, GLuint* queries) override {
- GenIds(&queries_, n, queries);
- }
-
- GLuint CreateProgram() override { return GenId(&programs_); }
-
- GLuint CreateShader(GLenum type) override { return GenId(&shaders_); }
-
- void BindTexture(GLenum target, GLuint texture) override {
- CheckId(&textures_, texture);
- }
-
- void BindBuffer(GLenum target, GLuint buffer) override {
- CheckId(&buffers_, buffer);
- }
-
- void BindRenderbuffer(GLenum target, GLuint renderbuffer) override {
- CheckId(&renderbuffers_, renderbuffer);
- }
-
- void BindFramebuffer(GLenum target, GLuint framebuffer) override {
- CheckId(&framebuffers_, framebuffer);
- }
-
- void UseProgram(GLuint program) override { CheckId(&programs_, program); }
-
- void DeleteTextures(GLsizei n, const GLuint* textures) override {
- DeleteIds(&textures_, n, textures);
- }
-
- void DeleteBuffers(GLsizei n, const GLuint* buffers) override {
- DeleteIds(&buffers_, n, buffers);
- }
-
- void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) override {
- DeleteIds(&framebuffers_, n, framebuffers);
- }
-
- void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) override {
- DeleteIds(&renderbuffers_, n, renderbuffers);
- }
-
- void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override {
- DeleteIds(&queries_, n, queries);
- }
-
- void DeleteProgram(GLuint program) override { DeleteId(&programs_, program); }
-
- void DeleteShader(GLuint shader) override { DeleteId(&shaders_, shader); }
-
- void BufferData(GLenum target,
- GLsizeiptr size,
- const void* data,
- GLenum usage) override {}
-
- private:
- GLuint GenId(std::set<GLuint>* resource_set) {
- GLuint id = next_id_++;
- resource_set->insert(id);
- return id;
- }
-
- void GenIds(std::set<GLuint>* resource_set, GLsizei n, GLuint* ids) {
- for (GLsizei i = 0; i < n; ++i)
- ids[i] = GenId(resource_set);
- }
-
- void CheckId(std::set<GLuint>* resource_set, GLuint id) {
- if (id == 0)
- return;
- EXPECT_TRUE(resource_set->find(id) != resource_set->end());
- }
-
- void DeleteId(std::set<GLuint>* resource_set, GLuint id) {
- if (id == 0)
- return;
- size_t num_erased = resource_set->erase(id);
- EXPECT_EQ(1u, num_erased);
- }
-
- void DeleteIds(std::set<GLuint>* resource_set, GLsizei n, const GLuint* ids) {
- for (GLsizei i = 0; i < n; ++i)
- DeleteId(resource_set, ids[i]);
- }
-
- GLuint next_id_ = 1;
- std::set<GLuint> textures_;
- std::set<GLuint> buffers_;
- std::set<GLuint> framebuffers_;
- std::set<GLuint> renderbuffers_;
- std::set<GLuint> queries_;
- std::set<GLuint> shaders_;
- std::set<GLuint> programs_;
-};
-
-TEST_F(GLRendererTest, NoResourceLeak) {
- auto gl_owned = std::make_unique<ResourceTrackingGLES2Interface>();
- auto* gl = gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- auto output_surface = FakeOutputSurface::Create3d(std::move(provider));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- {
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(100, 100);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
- root_pass->damage_rect = gfx::Rect(2, 2, 3, 3);
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
- }
- gl->CheckNoResources();
-}
-
-class DrawElementsGLES2Interface : public TestGLES2Interface {
- public:
- MOCK_METHOD4(
- DrawElements,
- void(GLenum mode, GLsizei count, GLenum type, const void* indices));
-};
-
-class GLRendererSkipTest : public GLRendererTest {
- protected:
- GLRendererSkipTest() {
- auto gl_owned = std::make_unique<StrictMock<DrawElementsGLES2Interface>>();
- gl_owned->set_have_post_sub_buffer(true);
- gl_ = gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
- output_surface_->BindToClient(&output_surface_client_);
-
- resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
- output_surface_->context_provider());
- settings_.partial_swap_enabled = true;
- renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_,
- output_surface_.get(),
- resource_provider_.get());
- renderer_->Initialize();
- renderer_->SetVisible(true);
- }
-
- void DrawBlackFrame(const gfx::Size& viewport_size) {
- // The feature enables a faster path to draw solid color quads that does not
- // use GL draw calls but instead uses glClear.
- if (!features::IsUsingFastPathForSolidColorQuad())
- EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = gfx::Rect(viewport_size);
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK);
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- Mock::VerifyAndClearExpectations(gl_);
- }
-
- StrictMock<DrawElementsGLES2Interface>* gl_;
- RendererSettings settings_;
- cc::FakeOutputSurfaceClient output_surface_client_;
- std::unique_ptr<FakeOutputSurface> output_surface_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
- std::unique_ptr<FakeRendererGL> renderer_;
-};
-
-TEST_F(GLRendererSkipTest, DrawQuad) {
- gfx::Size viewport_size(100, 100);
- gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20);
-
- // Draw the a black frame to make sure output surface is reshaped before
- // tests.
- DrawBlackFrame(viewport_size);
-
- EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);
- cc::AddQuad(root_pass, quad_rect, SK_ColorGREEN);
-
- // Add rounded corners to the solid color draw quad so that the fast path
- // of drawing using glClear is not used.
- root_pass->shared_quad_state_list.front()->mask_filter_info =
- gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 2.f));
-
- renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
-}
-
-TEST_F(GLRendererSkipTest, SkipVisibleRect) {
- gfx::Size viewport_size(100, 100);
- gfx::Rect quad_rect = gfx::Rect(0, 0, 40, 40);
-
- // Draw the a black frame to make sure output surface is reshaped before
- // tests.
- DrawBlackFrame(viewport_size);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = gfx::Rect(0, 0, 10, 10);
- cc::AddQuad(root_pass, quad_rect, SK_ColorGREEN);
- root_pass->shared_quad_state_list.front()->clip_rect =
- gfx::Rect(0, 0, 40, 40);
- root_pass->quad_list.front()->visible_rect = gfx::Rect(20, 20, 20, 20);
-
- // Add rounded corners to the solid color draw quad so that the fast path
- // of drawing using glClear is not used.
- root_pass->shared_quad_state_list.front()->mask_filter_info =
- gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 1.f));
-
- renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- // DrawElements should not be called because the visible rect is outside the
- // scissor, even though the clip rect and quad rect intersect the scissor.
-}
-
-TEST_F(GLRendererSkipTest, SkipClippedQuads) {
- gfx::Size viewport_size(100, 100);
- gfx::Rect quad_rect = gfx::Rect(25, 25, 90, 90);
-
- // Draw the a black frame to make sure output surface is reshaped before
- // tests.
- DrawBlackFrame(viewport_size);
-
- AggregatedRenderPassId root_pass_id{1};
-
- auto* root_pass = cc::AddRenderPass(&render_passes_in_draw_order_,
- root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);
- cc::AddClippedQuad(root_pass, quad_rect, SK_ColorGREEN);
- root_pass->quad_list.front()->rect = gfx::Rect(20, 20, 20, 20);
-
- renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- // DrawElements should not be called because the clip rect is outside the
- // scissor.
-}
-
-TEST_F(GLRendererTest, DrawFramePreservesFramebuffer) {
- // When using render-to-FBO to display the surface, all rendering is done
- // to a non-zero FBO. Make sure that the framebuffer is always restored to
- // the correct framebuffer during rendering, if changed.
- // Note: there is one path that will set it to 0, but that is after the render
- // has finished.
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<FakeOutputSurface> output_surface(
- FakeOutputSurface::Create3d());
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- EXPECT_FALSE(renderer.use_partial_swap());
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(100, 100);
- gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddClippedQuad(root_pass, quad_rect, SK_ColorGREEN);
-
- unsigned fbo;
- gpu::gles2::GLES2Interface* gl =
- output_surface->context_provider()->ContextGL();
- gl->GenFramebuffers(1, &fbo);
- output_surface->set_framebuffer(fbo, GL_RGB);
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
-
- int bound_fbo;
- gl->GetIntegerv(GL_FRAMEBUFFER_BINDING, &bound_fbo);
- EXPECT_EQ(static_cast<int>(fbo), bound_fbo);
-}
-
-TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
- gfx::Size viewport_size(60, 75);
-
- gfx::Rect child_rect(50, 50);
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPass* child_pass;
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass;
-
- auto transfer_resource = TransferableResource::MakeGL(
- gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
- child_rect.size(), false /* is_overlay_candidate */);
- ResourceId mask = child_resource_provider_->ImportResource(transfer_resource,
- base::DoNothing());
-
- // Return the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- cc::SendResourceAndGetChildToParentMap({mask}, resource_provider_.get(),
- child_resource_provider_.get(),
- child_context_provider_.get());
- ResourceId mapped_mask = resource_map[mask];
-
- float matrix[20];
- float amount = 0.5f;
- matrix[0] = 0.213f + 0.787f * amount;
- matrix[1] = 0.715f - 0.715f * amount;
- matrix[2] = 1.f - (matrix[0] + matrix[1]);
- matrix[3] = matrix[4] = 0;
- matrix[5] = 0.213f - 0.213f * amount;
- matrix[6] = 0.715f + 0.285f * amount;
- matrix[7] = 1.f - (matrix[5] + matrix[6]);
- matrix[8] = matrix[9] = 0;
- matrix[10] = 0.213f - 0.213f * amount;
- matrix[11] = 0.715f - 0.715f * amount;
- matrix[12] = 1.f - (matrix[10] + matrix[11]);
- matrix[13] = matrix[14] = 0;
- matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0;
- matrix[18] = 1;
- cc::FilterOperations filters;
- filters.Append(cc::FilterOperation::CreateReferenceFilter(
- sk_make_sp<cc::ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix),
- nullptr)));
-
- gfx::Transform transform_causing_aa;
- transform_causing_aa.Rotate(20.0);
-
- for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
- BlendMode blend_mode = static_cast<BlendMode>(i);
- SkBlendMode xfer_mode = BlendModeToSkXfermode(blend_mode);
- settings_.force_blending_with_shaders = (blend_mode != BLEND_MODE_NONE);
- // RenderPassProgram
- render_passes_in_draw_order_.clear();
- child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, gfx::Transform(), cc::FilterOperations());
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), xfer_mode);
-
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode);
-
- // RenderPassColorMatrixProgram
- render_passes_in_draw_order_.clear();
-
- child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, transform_causing_aa, filters);
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), xfer_mode);
-
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- TestRenderPassColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode);
-
- // RenderPassMaskProgram
- render_passes_in_draw_order_.clear();
-
- child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, gfx::Transform(), cc::FilterOperations());
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask, gfx::Transform(),
- xfer_mode);
-
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- TestRenderPassMaskProgram(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D,
- blend_mode);
-
- // RenderPassMaskColorMatrixProgram
- render_passes_in_draw_order_.clear();
-
- child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, gfx::Transform(), filters);
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask, gfx::Transform(),
- xfer_mode);
-
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- TestRenderPassMaskColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM,
- SAMPLER_TYPE_2D, blend_mode);
-
- // RenderPassProgramAA
- render_passes_in_draw_order_.clear();
-
- child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, transform_causing_aa,
- cc::FilterOperations());
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- transform_causing_aa, xfer_mode);
-
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- TestRenderPassProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode);
-
- // RenderPassColorMatrixProgramAA
- render_passes_in_draw_order_.clear();
-
- child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, transform_causing_aa, filters);
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- transform_causing_aa, xfer_mode);
-
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- TestRenderPassColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode);
-
- // RenderPassMaskProgramAA
- render_passes_in_draw_order_.clear();
-
- child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, transform_causing_aa,
- cc::FilterOperations());
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask,
- transform_causing_aa, xfer_mode);
-
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- TestRenderPassMaskProgramAA(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D,
- blend_mode);
-
- // RenderPassMaskColorMatrixProgramAA
- render_passes_in_draw_order_.clear();
-
- child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, transform_causing_aa, filters);
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size),
- transform_causing_aa, cc::FilterOperations());
-
- cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask,
- transform_causing_aa, xfer_mode);
-
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
- TestRenderPassMaskColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM,
- SAMPLER_TYPE_2D, blend_mode);
- }
-}
-
-// At this time, the AA code path cannot be taken if the surface's rect would
-// project incorrectly by the given transform, because of w<0 clipping.
-TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) {
- gfx::Rect child_rect(50, 50);
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPass* child_pass;
-
- gfx::Size viewport_size(100, 100);
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass;
-
- gfx::Transform transform_preventing_aa;
- transform_preventing_aa.ApplyPerspectiveDepth(40.0);
- transform_preventing_aa.RotateAboutYAxis(-20.0);
- transform_preventing_aa.Scale(30.0, 1.0);
-
- // Verify that the test transform and test rect actually do cause the clipped
- // flag to trigger. Otherwise we are not testing the intended scenario.
- bool clipped = false;
- cc::MathUtil::MapQuad(transform_preventing_aa,
- gfx::QuadF(gfx::RectF(child_rect)), &clipped);
- ASSERT_TRUE(clipped);
-
- child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- child_rect, transform_preventing_aa,
- cc::FilterOperations());
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- transform_preventing_aa, SkBlendMode::kSrcOver);
-
- renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
-
- // If use_aa incorrectly ignores clipping, it will use the
- // RenderPassProgramAA shader instead of the RenderPassProgram.
- TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, BLEND_MODE_NONE);
-}
-
-TEST_F(GLRendererShaderTest, DrawSolidColorShader) {
- gfx::Size viewport_size(30, 30); // Don't translate out of the viewport.
- gfx::Size quad_size(3, 3);
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass;
-
- gfx::Transform pixel_aligned_transform_causing_aa;
- pixel_aligned_transform_causing_aa.Translate(25.5f, 25.5f);
- pixel_aligned_transform_causing_aa.Scale(0.5f, 0.5f);
-
- root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
- cc::AddTransformedQuad(root_pass, gfx::Rect(quad_size), SK_ColorYELLOW,
- pixel_aligned_transform_causing_aa);
-
- renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(renderer_.get(), viewport_size);
-
- TestSolidColorProgramAA();
-}
-
-class OutputSurfaceMockGLES2Interface : public TestGLES2Interface {
- public:
- OutputSurfaceMockGLES2Interface() = default;
-
- // Specifically override methods even if they are unused (used in conjunction
- // with StrictMock). We need to make sure that GLRenderer does not issue
- // framebuffer-related GLuint calls directly. Instead these are supposed to go
- // through the OutputSurface abstraction.
- MOCK_METHOD2(BindFramebuffer, void(GLenum target, GLuint framebuffer));
- MOCK_METHOD5(ResizeCHROMIUM,
- void(GLuint width,
- GLuint height,
- float device_scale,
- GLcolorSpace color_space,
- GLboolean has_alpha));
- MOCK_METHOD4(
- DrawElements,
- void(GLenum mode, GLsizei count, GLenum type, const void* indices));
-};
-
-class MockOutputSurface : public OutputSurface {
- public:
- explicit MockOutputSurface(scoped_refptr<ContextProvider> provider)
- : OutputSurface(std::move(provider)) {}
- ~MockOutputSurface() override {}
-
- void BindToClient(OutputSurfaceClient*) override {}
- unsigned UpdateGpuFence() override { return 0; }
-
- MOCK_METHOD0(EnsureBackbuffer, void());
- MOCK_METHOD0(DiscardBackbuffer, void());
- MOCK_METHOD5(Reshape,
- void(const gfx::Size& size,
- float scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil));
- MOCK_METHOD0(BindFramebuffer, void());
- MOCK_METHOD1(SetDrawRectangle, void(const gfx::Rect&));
- MOCK_METHOD1(SetEnableDCLayers, void(bool));
- MOCK_METHOD0(GetFramebufferCopyTextureFormat, GLenum());
- MOCK_METHOD1(SwapBuffers_, void(OutputSurfaceFrame& frame)); // NOLINT
- void SwapBuffers(OutputSurfaceFrame frame) override { SwapBuffers_(frame); }
- MOCK_CONST_METHOD0(IsDisplayedAsOverlayPlane, bool());
- MOCK_CONST_METHOD0(GetOverlayTextureId, unsigned());
- MOCK_CONST_METHOD0(HasExternalStencilTest, bool());
- MOCK_METHOD0(ApplyExternalStencil, void());
- MOCK_METHOD1(SetUpdateVSyncParametersCallback,
- void(UpdateVSyncParametersCallback));
- MOCK_METHOD1(SetDisplayTransformHint, void(gfx::OverlayTransform));
-
- gfx::OverlayTransform GetDisplayTransform() override {
- return gfx::OVERLAY_TRANSFORM_NONE;
- }
-};
-
-class MockOutputSurfaceTest : public GLRendererTest {
- protected:
- void SetUp() override {
- auto gl = std::make_unique<StrictMock<OutputSurfaceMockGLES2Interface>>();
- gl->set_have_post_sub_buffer(true);
- gl_ = gl.get();
- auto provider = TestContextProvider::Create(std::move(gl));
- provider->BindToCurrentThread();
- output_surface_ =
- std::make_unique<StrictMock<MockOutputSurface>>(std::move(provider));
-
- output_surface_->BindToClient(&output_surface_client_);
-
- resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
- output_surface_->context_provider());
-
- renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_,
- output_surface_.get(),
- resource_provider_.get());
- renderer_->Initialize();
-
- EXPECT_CALL(*output_surface_, EnsureBackbuffer()).Times(1);
- renderer_->SetVisible(true);
- Mock::VerifyAndClearExpectations(output_surface_.get());
- }
-
- void SwapBuffers() {
- renderer_->SwapBuffers(DirectRenderer::SwapFrameData());
- }
-
- void DrawFrame(float device_scale_factor,
- const gfx::Size& viewport_size,
- bool transparent) {
- gfx::BufferFormat format = transparent ? gfx::BufferFormat::RGBA_8888
- : gfx::BufferFormat::RGBX_8888;
- AggregatedRenderPassId render_pass_id{1};
- AggregatedRenderPass* render_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, render_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(render_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
- render_pass->has_transparent_background = transparent;
-
- EXPECT_CALL(*output_surface_, EnsureBackbuffer()).WillRepeatedly(Return());
-
- EXPECT_CALL(*output_surface_,
- Reshape(viewport_size, device_scale_factor, _, format, _))
- .Times(1);
-
- EXPECT_CALL(*output_surface_, BindFramebuffer()).Times(1);
-
- EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1);
-
- renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- SurfaceDamageRectList surface_damage_rect_list;
- renderer_->DrawFrame(&render_passes_in_draw_order_, device_scale_factor,
- viewport_size, gfx::DisplayColorSpaces(),
- std::move(surface_damage_rect_list));
- }
-
- RendererSettings settings_;
- cc::FakeOutputSurfaceClient output_surface_client_;
- OutputSurfaceMockGLES2Interface* gl_ = nullptr;
- std::unique_ptr<StrictMock<MockOutputSurface>> output_surface_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
- std::unique_ptr<FakeRendererGL> renderer_;
-};
-
-TEST_F(MockOutputSurfaceTest, BackbufferDiscard) {
- // Drop backbuffer on hide.
- EXPECT_CALL(*output_surface_, DiscardBackbuffer()).Times(1);
- renderer_->SetVisible(false);
- Mock::VerifyAndClearExpectations(output_surface_.get());
-
- // Restore backbuffer on show.
- EXPECT_CALL(*output_surface_, EnsureBackbuffer()).Times(1);
- renderer_->SetVisible(true);
- Mock::VerifyAndClearExpectations(output_surface_.get());
-}
-
-#if BUILDFLAG(IS_WIN)
-class MockDCLayerOverlayProcessor : public DCLayerOverlayProcessor {
- public:
- MockDCLayerOverlayProcessor()
- : DCLayerOverlayProcessor(&debug_settings_,
- /*allowed_yuv_overlay_count=*/1,
- true) {}
- ~MockDCLayerOverlayProcessor() override = default;
- MOCK_METHOD8(Process,
- void(DisplayResourceProvider* resource_provider,
- const gfx::RectF& display_rect,
- const FilterOperationsMap& render_pass_filters,
- const FilterOperationsMap& render_pass_backdrop_filters,
- AggregatedRenderPassList* render_passes,
- gfx::Rect* damage_rect,
- SurfaceDamageRectList surface_damage_rect_list,
- DCLayerOverlayList* dc_layer_overlays));
-
- protected:
- DebugRendererSettings debug_settings_;
-};
-class TestOverlayProcessor : public OverlayProcessorWin {
- public:
- explicit TestOverlayProcessor(OutputSurface* output_surface)
- : OverlayProcessorWin(output_surface,
- std::make_unique<MockDCLayerOverlayProcessor>()) {}
- ~TestOverlayProcessor() override = default;
-
- MockDCLayerOverlayProcessor* GetTestProcessor() {
- return static_cast<MockDCLayerOverlayProcessor*>(GetOverlayProcessor());
- }
-};
-#elif BUILDFLAG(IS_APPLE)
-class MockCALayerOverlayProcessor : public CALayerOverlayProcessor {
- public:
- MockCALayerOverlayProcessor() : CALayerOverlayProcessor(true) {}
- ~MockCALayerOverlayProcessor() override = default;
-
- MOCK_METHOD6(
- ProcessForCALayerOverlays,
- bool(AggregatedRenderPass* render_pass,
- DisplayResourceProvider* resource_provider,
- const gfx::RectF& display_rect,
- const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
- render_pass_filters,
- const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
- render_pass_backdrop_filters,
- CALayerOverlayList* ca_layer_overlays));
-};
-
-class TestOverlayProcessor : public OverlayProcessorMac {
- public:
- explicit TestOverlayProcessor(OutputSurface* output_surface)
- : OverlayProcessorMac(std::make_unique<MockCALayerOverlayProcessor>()) {}
- ~TestOverlayProcessor() override = default;
-
- MockCALayerOverlayProcessor* GetTestProcessor() {
- return static_cast<MockCALayerOverlayProcessor*>(GetOverlayProcessor());
- }
-};
-
-#elif BUILDFLAG(IS_ANDROID) || defined(USE_OZONE)
-
-class TestOverlayProcessor : public OverlayProcessorUsingStrategy {
- public:
- class TestStrategy : public OverlayProcessorStrategy {
- public:
- TestStrategy() = default;
- ~TestStrategy() override = default;
-
- MOCK_METHOD8(
- Attempt,
- bool(const SkM44& output_color_matrix,
- const OverlayProcessorInterface::FilterOperationsMap&
- render_pass_backdrop_filters,
- DisplayResourceProvider* resource_provider,
- AggregatedRenderPassList* render_pass_list,
- SurfaceDamageRectList* surface_damage_rect_list,
- const OverlayProcessorInterface::OutputSurfaceOverlayPlane*
- primary_surface,
- OverlayCandidateList* candidates,
- std::vector<gfx::Rect>* content_bounds));
-
- void ProposePrioritized(
- const SkM44& output_color_matrix,
- const FilterOperationsMap& render_pass_backdrop_filters,
- DisplayResourceProvider* resource_provider,
- AggregatedRenderPassList* render_pass_list,
- SurfaceDamageRectList* surface_damage_rect_list,
- const PrimaryPlane* primary_plane,
- std::vector<OverlayProposedCandidate>* candidates,
- std::vector<gfx::Rect>* content_bounds) override {
- auto* render_pass = render_pass_list->back().get();
- QuadList& quad_list = render_pass->quad_list;
- OverlayCandidate candidate;
- candidates->push_back({quad_list.end(), candidate, this});
- }
-
- MOCK_METHOD9(AttemptPrioritized,
- bool(const SkM44& output_color_matrix,
- const FilterOperationsMap& render_pass_backdrop_filters,
- DisplayResourceProvider* resource_provider,
- AggregatedRenderPassList* render_pass_list,
- SurfaceDamageRectList* surface_damage_rect_list,
- const PrimaryPlane* primary_plane,
- OverlayCandidateList* candidates,
- std::vector<gfx::Rect>* content_bounds,
- const OverlayProposedCandidate& proposed_candidate));
-
- MOCK_METHOD2(CommitCandidate,
- void(const OverlayProposedCandidate& proposed_candidate,
- AggregatedRenderPass* render_pass));
- };
-
- bool IsOverlaySupported() const override { return true; }
-
- // A list of possible overlay candidates is presented to this function.
- // The expected result is that those candidates that can be in a separate
- // plane are marked with |overlay_handled| set to true, otherwise they are
- // to be traditionally composited. Candidates with |overlay_handled| set to
- // true must also have their |display_rect| converted to integer
- // coordinates if necessary.
- void CheckOverlaySupportImpl(
- const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
- OverlayCandidateList* surfaces) override {}
-
- TestStrategy& strategy() {
- auto* strategy = strategies_.back().get();
- return *(static_cast<TestStrategy*>(strategy));
- }
-
- MOCK_CONST_METHOD0(NeedsSurfaceDamageRectList, bool());
- explicit TestOverlayProcessor(OutputSurface* output_surface) {
- strategies_.push_back(std::make_unique<TestStrategy>());
- prioritization_config_.changing_threshold = false;
- prioritization_config_.damage_rate_threshold = false;
- }
- ~TestOverlayProcessor() override = default;
-};
-#else // Default to no overlay.
-class TestOverlayProcessor : public OverlayProcessorStub {
- public:
- explicit TestOverlayProcessor(OutputSurface* output_surface)
- : OverlayProcessorStub() {}
- ~TestOverlayProcessor() override = default;
-};
-#endif
-
-void MailboxReleased(const gpu::SyncToken& sync_token, bool lost_resource) {}
-
-static void CollectResources(std::vector<ReturnedResource>* array,
- std::vector<ReturnedResource> returned) {
- array->insert(array->end(), std::make_move_iterator(returned.begin()),
- std::make_move_iterator(returned.end()));
-}
-
-TEST_F(GLRendererTest, DontOverlayWithCopyRequests) {
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<FakeOutputSurface> output_surface(
- FakeOutputSurface::Create3d());
-#if BUILDFLAG(IS_WIN)
- output_surface->set_supports_dc_layers(true);
-#endif
- output_surface->BindToClient(&output_surface_client);
-
- auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- auto child_context_provider = TestContextProvider::Create();
- child_context_provider->BindToCurrentThread();
- auto child_resource_provider = std::make_unique<ClientResourceProvider>();
-
- auto transfer_resource = TransferableResource::MakeGL(
- gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
- gfx::Size(256, 256), true);
- auto release_callback = base::BindOnce(&MailboxReleased);
- ResourceId resource_id = child_resource_provider->ImportResource(
- transfer_resource, std::move(release_callback));
-
- std::vector<ReturnedResource> returned_to_child;
- int child_id = parent_resource_provider->CreateChild(
- base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId());
-
- // Transfer resource to the parent.
- std::vector<ResourceId> resource_ids_to_transfer;
- resource_ids_to_transfer.push_back(resource_id);
- std::vector<TransferableResource> list;
- child_resource_provider->PrepareSendToParent(
- resource_ids_to_transfer, &list,
- static_cast<RasterContextProvider*>(child_context_provider.get()));
- parent_resource_provider->ReceiveFromChild(child_id, list);
-
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- parent_resource_provider->GetChildToParentMap(child_id);
- ResourceId parent_resource_id = resource_map[list[0].id];
-
- auto processor = std::make_unique<TestOverlayProcessor>(output_surface.get());
-
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- parent_resource_provider.get(), processor.get(),
- base::ThreadTaskRunnerHandle::Get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
-#if BUILDFLAG(IS_APPLE)
- MockCALayerOverlayProcessor* mock_ca_processor =
- processor->GetTestProcessor();
-#elif BUILDFLAG(IS_WIN)
- MockDCLayerOverlayProcessor* dc_processor = processor->GetTestProcessor();
-#endif
-
- gfx::Size viewport_size(1, 1);
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- root_pass->has_transparent_background = false;
- root_pass->copy_requests.push_back(CopyOutputRequest::CreateStubForTesting());
-
- bool needs_blending = false;
- bool premultiplied_alpha = false;
- bool flipped = false;
- bool nearest_neighbor = false;
- float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
-
- TextureDrawQuad* overlay_quad =
- root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
- overlay_quad->SetNew(
- root_pass->CreateAndAppendSharedQuadState(), gfx::Rect(viewport_size),
- gfx::Rect(viewport_size), needs_blending, parent_resource_id,
- premultiplied_alpha, gfx::PointF(0, 0), gfx::PointF(1, 1),
- SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor,
- /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);
-
- // DirectRenderer::DrawFrame calls into OverlayProcessor::ProcessForOverlays.
- // Attempt will be called for each strategy in OverlayProcessor. We have
- // added a fake strategy, so checking for Attempt calls checks if there was
- // any attempt to overlay, which there shouldn't be. We can't use the quad
- // list because the render pass is cleaned up by DrawFrame.
-#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID)
- if (features::IsOverlayPrioritizationEnabled()) {
- EXPECT_CALL(processor->strategy(),
- AttemptPrioritized(_, _, _, _, _, _, _, _, _))
- .Times(0);
- } else {
- EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _, _))
- .Times(0);
- }
-#elif BUILDFLAG(IS_APPLE)
- EXPECT_CALL(*mock_ca_processor, ProcessForCALayerOverlays(_, _, _, _, _, _))
- .WillOnce(Return(false));
-#elif BUILDFLAG(IS_WIN)
- EXPECT_CALL(*dc_processor, Process(_, _, _, _, _, _, _, _)).Times(0);
-#endif
- DrawFrame(&renderer, viewport_size);
-#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID)
- Mock::VerifyAndClearExpectations(&processor->strategy());
-#elif BUILDFLAG(IS_APPLE)
- Mock::VerifyAndClearExpectations(
- const_cast<MockCALayerOverlayProcessor*>(mock_ca_processor));
-#elif BUILDFLAG(IS_WIN)
- Mock::VerifyAndClearExpectations(
- const_cast<MockDCLayerOverlayProcessor*>(dc_processor));
-#endif
-
- // Without a copy request Attempt() should be called once.
- root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- root_pass->has_transparent_background = false;
-
- overlay_quad = root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
- overlay_quad->SetNew(
- root_pass->CreateAndAppendSharedQuadState(), gfx::Rect(viewport_size),
- gfx::Rect(viewport_size), needs_blending, parent_resource_id,
- premultiplied_alpha, gfx::PointF(0, 0), gfx::PointF(1, 1),
- SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor,
- /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);
-#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID)
- if (features::IsOverlayPrioritizationEnabled()) {
- EXPECT_CALL(processor->strategy(),
- AttemptPrioritized(_, _, _, _, _, _, _, _, _))
- .Times(1);
- } else {
- EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _, _))
- .Times(1);
- }
-#elif BUILDFLAG(IS_APPLE)
- EXPECT_CALL(*mock_ca_processor, ProcessForCALayerOverlays(_, _, _, _, _, _))
- .WillOnce(Return(true));
-#elif BUILDFLAG(IS_WIN)
- EXPECT_CALL(*dc_processor, Process(_, _, _, _, _, _, _, _)).Times(1);
-#endif
- DrawFrame(&renderer, viewport_size);
-
- // Transfer resources back from the parent to the child. Set no resources as
- // being in use.
- parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
- ResourceIdSet());
-
- child_resource_provider->RemoveImportedResource(resource_id);
- child_resource_provider->ShutdownAndReleaseAllResources();
-}
-
-#if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE)
-class SingleOverlayOnTopProcessor : public OverlayProcessorUsingStrategy {
- public:
- SingleOverlayOnTopProcessor() : OverlayProcessorUsingStrategy() {
- strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this));
- strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this));
- prioritization_config_.changing_threshold = false;
- prioritization_config_.damage_rate_threshold = false;
- }
-
- bool NeedsSurfaceDamageRectList() const override { return true; }
- bool IsOverlaySupported() const override { return true; }
-
- void CheckOverlaySupportImpl(
- const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
- OverlayCandidateList* surfaces) override {
- if (!multiple_candidates_)
- ASSERT_EQ(1U, surfaces->size());
- OverlayCandidate& candidate = surfaces->back();
- candidate.overlay_handled = true;
- }
-
- void AllowMultipleCandidates() { multiple_candidates_ = true; }
-
- private:
- bool multiple_candidates_ = false;
-};
-
-class WaitSyncTokenCountingGLES2Interface : public TestGLES2Interface {
- public:
- MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token));
-};
-
-class MockOverlayScheduler {
- public:
- MOCK_METHOD7(Schedule,
- void(int plane_z_order,
- gfx::OverlayTransform plane_transform,
- unsigned overlay_texture_id,
- const gfx::Rect& display_bounds,
- const gfx::RectF& uv_rect,
- bool enable_blend,
- unsigned gpu_fence_id));
-};
-
-TEST_F(GLRendererTest, OverlaySyncTokensAreProcessed) {
- auto gl_owned = std::make_unique<WaitSyncTokenCountingGLES2Interface>();
- WaitSyncTokenCountingGLES2Interface* gl = gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- MockOverlayScheduler overlay_scheduler;
- provider->support()->SetScheduleOverlayPlaneCallback(base::BindRepeating(
- &MockOverlayScheduler::Schedule, base::Unretained(&overlay_scheduler)));
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- auto child_context_provider = TestContextProvider::Create();
- child_context_provider->BindToCurrentThread();
- auto child_resource_provider = std::make_unique<ClientResourceProvider>();
-
- gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
- gpu::CommandBufferId::FromUnsafeValue(0x123), 29);
- auto transfer_resource = TransferableResource::MakeGL(
- gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, sync_token,
- gfx::Size(256, 256), true);
- auto release_callback = base::BindOnce(&MailboxReleased);
- ResourceId resource_id = child_resource_provider->ImportResource(
- transfer_resource, std::move(release_callback));
-
- std::vector<ReturnedResource> returned_to_child;
- int child_id = parent_resource_provider->CreateChild(
- base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId());
-
- // Transfer resource to the parent.
- std::vector<ResourceId> resource_ids_to_transfer;
- resource_ids_to_transfer.push_back(resource_id);
- std::vector<TransferableResource> list;
- child_resource_provider->PrepareSendToParent(
- resource_ids_to_transfer, &list,
- static_cast<RasterContextProvider*>(child_context_provider.get()));
- parent_resource_provider->ReceiveFromChild(child_id, list);
-
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- parent_resource_provider->GetChildToParentMap(child_id);
- ResourceId parent_resource_id = resource_map[list[0].id];
-
- RendererSettings settings;
- auto processor = std::make_unique<SingleOverlayOnTopProcessor>();
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- parent_resource_provider.get(), processor.get(),
- base::ThreadTaskRunnerHandle::Get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(1, 1);
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- root_pass->has_transparent_background = false;
-
- bool needs_blending = false;
- bool premultiplied_alpha = false;
- bool flipped = false;
- bool nearest_neighbor = false;
- float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- gfx::PointF uv_top_left(0, 0);
- gfx::PointF uv_bottom_right(1, 1);
-
- TextureDrawQuad* overlay_quad =
- root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
- SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
- shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
- gfx::Rect(viewport_size), gfx::MaskFilterInfo(),
- absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
- overlay_quad->SetNew(shared_state, gfx::Rect(viewport_size),
- gfx::Rect(viewport_size), needs_blending,
- parent_resource_id, premultiplied_alpha, uv_top_left,
- uv_bottom_right, SK_ColorTRANSPARENT, vertex_opacity,
- flipped, nearest_neighbor, /*secure_output_only=*/false,
- gfx::ProtectedVideoType::kClear);
-
- // The verified flush flag will be set by
- // ClientResourceProvider::PrepareSendToParent. Before checking if the
- // gpu::SyncToken matches, set this flag first.
- sync_token.SetVerifyFlush();
-
- // Verify that overlay_quad actually gets turned into an overlay, and even
- // though it's not drawn, that its sync point is waited on.
- EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)))
- .Times(1);
-
- EXPECT_CALL(
- overlay_scheduler,
- Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _, gfx::Rect(viewport_size),
- BoundingRect(uv_top_left, uv_bottom_right), _, _))
- .Times(1);
-
- DrawFrame(&renderer, viewport_size);
-
- // Transfer resources back from the parent to the child. Set no resources as
- // being in use.
- parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
- ResourceIdSet());
-
- child_resource_provider->RemoveImportedResource(resource_id);
- child_resource_provider->ShutdownAndReleaseAllResources();
-}
-#endif // defined(USE_OZONE) || BUILDFLAG(IS_ANDROID)
-
-class OutputColorMatrixMockGLES2Interface : public TestGLES2Interface {
- public:
- OutputColorMatrixMockGLES2Interface() = default;
-
- MOCK_METHOD4(UniformMatrix4fv,
- void(GLint location,
- GLsizei count,
- GLboolean transpose,
- const GLfloat* value));
-};
-
-TEST_F(GLRendererTest, OutputColorMatrixTest) {
- // Initialize the mock GL interface, the output surface and the renderer.
- auto gl_owned = std::make_unique<OutputColorMatrixMockGLES2Interface>();
- auto* gl = gl_owned.get();
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
- std::unique_ptr<FakeOutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- cc::FakeOutputSurfaceClient output_surface_client;
- output_surface->BindToClient(&output_surface_client);
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- // Set a non-identity color matrix on the output surface.
- SkM44 color_matrix;
- color_matrix.setRC(0, 0, 0.7f);
- color_matrix.setRC(1, 1, 0.4f);
- color_matrix.setRC(2, 2, 0.5f);
- output_surface->set_color_matrix(color_matrix);
-
- // Create a root and a child passes to test that the output color matrix is
- // registered only for the root pass.
- gfx::Size viewport_size(100, 100);
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(viewport_size) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);
- cc::AddRenderPassQuad(root_pass, child_pass);
-
- // Verify that UniformMatrix4fv() is called only once on the root pass with
- // the correct matrix values.
- int call_count = 0;
- bool output_color_matrix_invoked = false;
- EXPECT_CALL(*gl, UniformMatrix4fv(_, 1, false, _))
- .WillRepeatedly(testing::WithArgs<0, 3>(testing::Invoke(
- [&color_matrix, &renderer, &call_count, &output_color_matrix_invoked](
- int matrix_location, const GLfloat* gl_matrix) {
- DCHECK(current_program(&renderer));
- const int color_matrix_location =
- current_program(&renderer)->output_color_matrix_location();
-
- if (matrix_location != color_matrix_location)
- return;
-
- call_count++;
- output_color_matrix_invoked = true;
- float expected_matrix[16];
- color_matrix.getColMajor(expected_matrix);
- for (int i = 0; i < 16; ++i)
- EXPECT_FLOAT_EQ(expected_matrix[i], gl_matrix[i]);
- })));
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
-
- EXPECT_EQ(1, call_count);
- EXPECT_TRUE(output_color_matrix_invoked);
-}
-
-class GenerateMipmapMockGLESInterface : public TestGLES2Interface {
- public:
- GenerateMipmapMockGLESInterface() = default;
-
- MOCK_METHOD3(TexParameteri, void(GLenum target, GLenum pname, GLint param));
- MOCK_METHOD1(GenerateMipmap, void(GLenum target));
-};
-
-// TODO(crbug.com/803286): Currently npot texture always return false on ubuntu
-// desktop. The npot texture check is probably failing on desktop GL. This test
-// crashes DCHECK npot texture to catch this. When
-// GLRendererPixelTest.DISABLED_TrilinearFiltering got passed, can remove this.
-TEST_F(GLRendererTest, GenerateMipmap) {
- // Initialize the mock GL interface, the output surface and the renderer.
- auto gl_owned = std::make_unique<GenerateMipmapMockGLESInterface>();
- gl_owned->set_support_texture_npot(true);
-
- auto* gl = gl_owned.get();
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- std::unique_ptr<FakeOutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- cc::FakeOutputSurfaceClient output_surface_client;
- output_surface->BindToClient(&output_surface_client);
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
- RendererSettings settings;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(100, 100);
- AggregatedRenderPassId child_pass_id{2};
- // Create a child pass with mipmap to verify that npot texture is enabled.
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(viewport_size) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- child_pass->generate_mipmap = true;
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);
- cc::AddRenderPassQuad(root_pass, child_pass);
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- EXPECT_CALL(*gl, TexParameteri(_, _, _)).Times(4);
- EXPECT_CALL(*gl, GenerateMipmap(GL_TEXTURE_2D)).Times(1);
- // When generate_mipmap enabled, the GL_TEXTURE_MIN_FILTER should be
- // GL_LINEAR_MIPMAP_LINEAR.
- EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- GL_LINEAR_MIPMAP_LINEAR));
- DrawFrame(&renderer, viewport_size);
-}
-
-class FastSolidColorMockGLES2Interface : public TestGLES2Interface {
- public:
- FastSolidColorMockGLES2Interface() = default;
-
- MOCK_METHOD1(Enable, void(GLenum cap));
- MOCK_METHOD1(Disable, void(GLenum cap));
- MOCK_METHOD4(ClearColor,
- void(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha));
- MOCK_METHOD4(Scissor, void(GLint x, GLint y, GLsizei width, GLsizei height));
-};
-
-class GLRendererFastSolidColorTest : public GLRendererTest {
- public:
- void SetUp() override {
- feature_list_.InitAndEnableFeature(features::kFastSolidColorDraw);
- GLRendererTest::SetUp();
-
- auto gl_owned = std::make_unique<FastSolidColorMockGLES2Interface>();
- gl_owned->set_have_post_sub_buffer(true);
- gl_ = gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
- output_surface_->BindToClient(&output_surface_client_);
-
- resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
- output_surface_->context_provider());
-
- settings_.partial_swap_enabled = true;
- settings_.slow_down_compositing_scale_factor = 1;
- settings_.allow_antialiasing = true;
-
- fake_renderer_ = std::make_unique<FakeRendererGL>(
- &settings_, &debug_settings_, output_surface_.get(),
- resource_provider_.get());
- fake_renderer_->Initialize();
- EXPECT_TRUE(fake_renderer_->use_partial_swap());
- fake_renderer_->SetVisible(true);
- }
-
- void TearDown() override {
- resource_provider_.reset();
- fake_renderer_.reset();
- output_surface_.reset();
- gl_ = nullptr;
-
- GLRendererTest::TearDown();
- }
-
- FastSolidColorMockGLES2Interface* gl_ptr() { return gl_; }
-
- FakeOutputSurface* output_surface() { return output_surface_.get(); }
-
- protected:
- void AddExpectations(bool use_fast_path,
- const gfx::Rect& scissor_rect,
- SkColor color = SK_ColorBLACK,
- bool enable_stencil = false) {
- auto* gl = gl_ptr();
-
- InSequence seq;
-
- // Restore GL state method calls
- EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST));
- EXPECT_CALL(*gl, Disable(GL_CULL_FACE));
- EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST));
- EXPECT_CALL(*gl, Enable(GL_BLEND));
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
-
- if (!enable_stencil)
- EXPECT_CALL(*gl, ClearColor(0, 0, 0, 0));
-
- if (use_fast_path) {
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(),
- scissor_rect.width(), scissor_rect.height()));
-
- SkColor4f color_f = SkColor4f::FromColor(color);
- EXPECT_CALL(*gl,
- ClearColor(color_f.fR, color_f.fG, color_f.fB, color_f.fA));
-
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
- }
-
- if (enable_stencil) {
- EXPECT_CALL(*gl, Enable(GL_STENCIL_TEST));
- EXPECT_CALL(*gl, Disable(GL_BLEND));
- }
-
- EXPECT_CALL(*gl, Disable(GL_BLEND));
- }
-
- void RunTest(const gfx::Size& viewport_size) {
- fake_renderer_->DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(fake_renderer_.get(), viewport_size);
-
- auto* gl = gl_ptr();
- ASSERT_TRUE(gl);
- Mock::VerifyAndClearExpectations(gl);
- }
-
- private:
- FastSolidColorMockGLES2Interface* gl_ = nullptr;
- std::unique_ptr<FakeRendererGL> fake_renderer_;
- std::unique_ptr<FakeOutputSurface> output_surface_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
- cc::FakeOutputSurfaceClient output_surface_client_;
- RendererSettings settings_;
- base::test::ScopedFeatureList feature_list_;
-};
-
-TEST_F(GLRendererFastSolidColorTest, RoundedCorners) {
- gfx::Size viewport_size(500, 500);
- gfx::Rect root_pass_output_rect(400, 400);
- gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
- gfx::Rect quad_rect(0, 50, 100, 100);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPassWithDamage(
- &render_passes_in_draw_order_, root_pass_id, root_pass_output_rect,
- root_pass_damage_rect, gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = root_pass_damage_rect;
- cc::AddQuad(root_pass, quad_rect, SK_ColorRED);
-
- root_pass->shared_quad_state_list.front()->mask_filter_info =
- gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 5.f));
-
- // Fast Solid color draw quads should not be executed.
- AddExpectations(false /*use_fast_path*/, gfx::Rect());
-
- RunTest(viewport_size);
-}
-
-TEST_F(GLRendererFastSolidColorTest, Transform3DSlowPath) {
- gfx::Size viewport_size(500, 500);
- gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
- gfx::Rect quad_rect(0, 50, 100, 100);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = root_pass_damage_rect;
- cc::AddQuad(root_pass, quad_rect, SK_ColorRED);
-
- gfx::Transform tm_3d;
- tm_3d.RotateAboutYAxis(30.0);
- ASSERT_FALSE(tm_3d.IsFlat());
-
- root_pass->shared_quad_state_list.front()->quad_to_target_transform = tm_3d;
-
- AddExpectations(false /*use_fast_path*/, gfx::Rect());
-
- RunTest(viewport_size);
-}
-
-TEST_F(GLRendererFastSolidColorTest, NonTransform3DFastPath) {
- gfx::Size viewport_size(500, 500);
- gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
- gfx::Rect quad_rect(0, 0, 200, 200);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = root_pass_damage_rect;
- cc::AddQuad(root_pass, quad_rect, SK_ColorRED);
-
- gfx::Transform tm_non_3d;
- tm_non_3d.Translate(10.f, 10.f);
- ASSERT_TRUE(tm_non_3d.IsFlat());
-
- root_pass->shared_quad_state_list.front()->quad_to_target_transform =
- tm_non_3d;
-
- AddExpectations(true /*use_fast_path*/, gfx::Rect(10, 290, 200, 200),
- SK_ColorRED);
-
- RunTest(viewport_size);
-}
-
-TEST_F(GLRendererFastSolidColorTest, NonAxisAlignSlowPath) {
- gfx::Size viewport_size(500, 500);
- gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
- gfx::Rect quad_rect(0, 0, 200, 200);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = root_pass_damage_rect;
- cc::AddQuad(root_pass, quad_rect, SK_ColorRED);
-
- gfx::Transform tm_non_axis_align;
- tm_non_axis_align.RotateAboutZAxis(45.0);
- ASSERT_TRUE(tm_non_axis_align.IsFlat());
-
- root_pass->shared_quad_state_list.front()->quad_to_target_transform =
- tm_non_axis_align;
-
- AddExpectations(false /*use_fast_path*/, gfx::Rect());
-
- RunTest(viewport_size);
-}
-
-TEST_F(GLRendererFastSolidColorTest, StencilSlowPath) {
- gfx::Size viewport_size(500, 500);
- gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
- gfx::Rect quad_rect(0, 0, 200, 200);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = root_pass_damage_rect;
- root_pass->has_transparent_background = false;
-
- cc::AddQuad(root_pass, quad_rect, SK_ColorRED);
-
- AddExpectations(false /*use_fast_path*/, gfx::Rect(), SK_ColorRED,
- true /*enable_stencil*/);
- output_surface()->set_has_external_stencil_test(true);
-
- RunTest(viewport_size);
-}
-
-TEST_F(GLRendererFastSolidColorTest, NeedsBlendingSlowPath) {
- gfx::Size viewport_size(500, 500);
- gfx::Rect root_pass_damage_rect(2, 3, 300, 200);
- gfx::Rect full_quad_rect(0, 0, 50, 50);
- gfx::Rect quad_rect_1(0, 0, 20, 20);
- gfx::Rect quad_rect_2(20, 0, 20, 20);
- gfx::Rect quad_rect_3(0, 20, 20, 20);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = root_pass_damage_rect;
-
- cc::AddQuad(root_pass, quad_rect_1, SkColorSetARGB(0x33, 0xFF, 0, 0));
-
- cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE);
- root_pass->shared_quad_state_list.back()->opacity = 0.5f;
-
- cc::AddQuad(root_pass, quad_rect_3, SK_ColorGREEN);
- root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kDstIn;
-
- cc::AddQuad(root_pass, full_quad_rect, SK_ColorBLACK);
-
- // The first solid color quad would use a fast path, but the other quads that
- // require blending will use the slower method.
- AddExpectations(true /*use_fast_path*/, gfx::Rect(0, 450, 50, 50),
- SK_ColorBLACK, false /*enable_stencil*/);
-
- RunTest(viewport_size);
-}
-
-TEST_F(GLRendererFastSolidColorTest, NeedsBlendingFastPath) {
- gfx::Size viewport_size(500, 500);
- gfx::Rect root_pass_damage_rect(2, 3, 300, 200);
- gfx::Rect quad_rect_1(0, 0, 20, 20);
- gfx::Rect quad_rect_2(20, 0, 20, 20);
- gfx::Rect quad_rect_3(0, 20, 20, 20);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = root_pass_damage_rect;
-
- cc::AddQuad(root_pass, quad_rect_1, SkColorSetARGB(0x33, 0xFF, 0, 0));
-
- cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE);
- root_pass->shared_quad_state_list.back()->opacity = 0.5f;
-
- cc::AddQuad(root_pass, quad_rect_3, SK_ColorGREEN);
- root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kSrc;
-
- auto* gl = gl_ptr();
-
- // The quads here despite having blend requirements can still use fast path
- // because they do not intersect with any other quad that has already been
- // drawn onto the render target.
- InSequence seq;
-
- // // Restore GL state method calls
- EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST));
- EXPECT_CALL(*gl, Disable(GL_CULL_FACE));
- EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST));
- EXPECT_CALL(*gl, Enable(GL_BLEND));
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
- EXPECT_CALL(*gl, ClearColor(0, 0, 0, 0));
-
- // Fast path draw used for green quad.
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 460, 20, 20));
- EXPECT_CALL(*gl, ClearColor(0, 1, 0, 1));
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
-
- // Fast path draw used for blue quad.
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(20, 480, 20, 20));
- EXPECT_CALL(*gl, ClearColor(0, 0, 0.5f, 0.5f));
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
-
- // Fast path draw used for red quad.
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 480, 20, 20));
- EXPECT_CALL(*gl, ClearColor(::testing::FloatEq(0.2f), 0, 0,
- ::testing::FloatEq(0.2f)));
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
-
- EXPECT_CALL(*gl, Disable(GL_BLEND));
-
- RunTest(viewport_size);
-}
-
-TEST_F(GLRendererFastSolidColorTest, AntiAliasSlowPath) {
- gfx::Size viewport_size(500, 500);
- gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
- gfx::Rect quad_rect(0, 0, 200, 200);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = root_pass_damage_rect;
- cc::AddQuad(root_pass, quad_rect, SK_ColorRED);
-
- gfx::Transform tm_aa;
- tm_aa.Translate(0.1f, 0.1f);
- ASSERT_TRUE(tm_aa.IsFlat());
-
- root_pass->shared_quad_state_list.front()->quad_to_target_transform = tm_aa;
-
- AddExpectations(false /*use_fast_path*/, gfx::Rect());
-
- RunTest(viewport_size);
-}
-
-class PartialSwapMockGLES2Interface : public TestGLES2Interface {
- public:
- PartialSwapMockGLES2Interface() = default;
-
- MOCK_METHOD1(Enable, void(GLenum cap));
- MOCK_METHOD1(Disable, void(GLenum cap));
- MOCK_METHOD4(Scissor, void(GLint x, GLint y, GLsizei width, GLsizei height));
- MOCK_METHOD1(SetEnableDCLayersCHROMIUM, void(GLboolean enable));
-};
-
-class GLRendererPartialSwapTest : public GLRendererTest {
- public:
- void SetUp() override {
- // Force enable fast solid color draw path.
- scoped_feature_list_.InitAndEnableFeature(features::kFastSolidColorDraw);
- GLRendererTest::SetUp();
- }
-
- protected:
- void RunTest(bool partial_swap, bool set_draw_rectangle) {
- auto gl_owned = std::make_unique<PartialSwapMockGLES2Interface>();
- gl_owned->set_have_post_sub_buffer(true);
-
- auto* gl = gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<FakeOutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->set_supports_dc_layers(set_draw_rectangle);
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- settings.partial_swap_enabled = partial_swap;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- EXPECT_EQ(partial_swap, renderer.use_partial_swap());
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(100, 100);
- gfx::Rect root_pass_output_rect(80, 80);
- gfx::Rect root_pass_damage_rect(2, 2, 3, 3);
-
- // Draw one black frame to make sure the output surface is reshaped before
- // tests.
- EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST)).Times(1);
- EXPECT_CALL(*gl, Disable(GL_CULL_FACE)).Times(1);
- EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST)).Times(1);
- EXPECT_CALL(*gl, Enable(GL_BLEND)).Times(1);
-
- if (output_surface->capabilities().supports_dc_layers) {
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)).Times(1);
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)).Times(1);
-
- // Root render pass requires a scissor if the output surface supports
- // dc layers.
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(3);
- EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(3);
- } else {
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)).Times(2);
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)).Times(2);
- if (set_draw_rectangle) {
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(2);
- EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(2);
- } else {
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(1);
- EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(1);
- }
- }
-
- EXPECT_CALL(*gl, Disable(GL_BLEND)).Times(1);
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- root_pass->damage_rect = gfx::Rect(viewport_size);
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK);
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
- Mock::VerifyAndClearExpectations(gl);
-
- for (int i = 0; i < 2; ++i) {
- root_pass = cc::AddRenderPassWithDamage(
- &render_passes_in_draw_order_, root_pass_id, root_pass_output_rect,
- root_pass_damage_rect, gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(root_pass_output_rect), SK_ColorGREEN);
-
- InSequence seq;
-
- // A bunch of initialization that happens.
- EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST));
- EXPECT_CALL(*gl, Disable(GL_CULL_FACE));
- EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST));
- EXPECT_CALL(*gl, Enable(GL_BLEND));
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
-
- // Partial frame, we should use a scissor to swap only that part when
- // partial swap is enabled.
- gfx::Rect output_rectangle =
- partial_swap ? root_pass_damage_rect : gfx::Rect(viewport_size);
-
- // The scissor is flipped, so subtract the y coord and height from the
- // bottom of the GL viewport.
- gfx::Rect scissor_rect(output_rectangle.x(),
- viewport_size.height() - output_rectangle.y() -
- output_rectangle.height(),
- output_rectangle.width(),
- output_rectangle.height());
-
- // Drawing the solid color quad using glClear and scissor rect.
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(),
- scissor_rect.width(), scissor_rect.height()));
-
- if (partial_swap || set_draw_rectangle) {
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(),
- scissor_rect.width(), scissor_rect.height()));
- }
-
- // Restore GL state after solid color draw quad.
- if (partial_swap || set_draw_rectangle) {
- EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(),
- scissor_rect.width(), scissor_rect.height()));
- } else {
- EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
- EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
- }
-
- // Blending is disabled at the end of the frame.
- EXPECT_CALL(*gl, Disable(GL_BLEND));
-
- renderer.DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
-
- if (set_draw_rectangle) {
- EXPECT_EQ(output_rectangle, output_surface->last_set_draw_rectangle());
- }
-
- Mock::VerifyAndClearExpectations(gl);
- }
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(GLRendererPartialSwapTest, PartialSwap) {
- RunTest(true, false);
-}
-
-TEST_F(GLRendererPartialSwapTest, NoPartialSwap) {
- RunTest(false, false);
-}
-
-#if BUILDFLAG(IS_WIN)
-TEST_F(GLRendererPartialSwapTest, SetDrawRectangle_PartialSwap) {
- RunTest(true, true);
-}
-
-TEST_F(GLRendererPartialSwapTest, SetDrawRectangle_NoPartialSwap) {
- RunTest(false, true);
-}
-
-// Test that SetEnableDCLayersCHROMIUM is properly called when enabling
-// and disabling DC layers.
-TEST_F(GLRendererTest, DCLayerOverlaySwitch) {
- auto gl_owned = std::make_unique<PartialSwapMockGLES2Interface>();
- gl_owned->set_have_post_sub_buffer(true);
- auto* gl = gl_owned.get();
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<FakeOutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->set_supports_dc_layers(true);
- output_surface->BindToClient(&output_surface_client);
-
- auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- auto child_context_provider = TestContextProvider::Create();
- child_context_provider->BindToCurrentThread();
- auto child_resource_provider = std::make_unique<ClientResourceProvider>();
-
- auto transfer_resource = TransferableResource::MakeGL(
- gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
- gfx::Size(256, 256), true);
- auto release_callback = base::BindOnce(&MailboxReleased);
- ResourceId resource_id = child_resource_provider->ImportResource(
- transfer_resource, std::move(release_callback));
-
- std::vector<ReturnedResource> returned_to_child;
- int child_id = parent_resource_provider->CreateChild(
- base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId());
-
- // Transfer resource to the parent.
- std::vector<ResourceId> resource_ids_to_transfer;
- resource_ids_to_transfer.push_back(resource_id);
- std::vector<TransferableResource> list;
- child_resource_provider->PrepareSendToParent(
- resource_ids_to_transfer, &list,
- static_cast<RasterContextProvider*>(child_context_provider.get()));
- parent_resource_provider->ReceiveFromChild(child_id, list);
- // In DisplayResourceProvider's namespace, use the mapped resource id.
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- parent_resource_provider->GetChildToParentMap(child_id);
- ResourceId parent_resource_id = resource_map[list[0].id];
-
- auto processor = std::make_unique<OverlayProcessorWin>(
- output_surface.get(),
- std::make_unique<DCLayerOverlayProcessor>(
- &debug_settings_, /*allowed_yuv_overlay_count=*/1, true));
-
- RendererSettings settings;
- settings.partial_swap_enabled = true;
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- parent_resource_provider.get(), processor.get());
- renderer.Initialize();
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(100, 100);
-
- for (int i = 0; i < 65; i++) {
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- if (i == 0) {
- gfx::Rect rect(0, 0, 100, 100);
- bool needs_blending = false;
- gfx::RectF tex_coord_rect(0, 0, 1, 1);
- SharedQuadState* shared_state =
- root_pass->CreateAndAppendSharedQuadState();
- shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
- absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
- YUVVideoDrawQuad* quad =
- root_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
- quad->SetNew(shared_state, rect, rect, needs_blending, tex_coord_rect,
- tex_coord_rect, rect.size(), rect.size(), parent_resource_id,
- parent_resource_id, parent_resource_id, parent_resource_id,
- gfx::ColorSpace(), 0, 1.0, 8);
- }
-
- // A bunch of initialization that happens.
- EXPECT_CALL(*gl, Disable(_)).Times(AnyNumber());
- EXPECT_CALL(*gl, Enable(_)).Times(AnyNumber());
- EXPECT_CALL(*gl, Scissor(_, _, _, _)).Times(AnyNumber());
-
- // Partial frame, we should use a scissor to swap only that part when
- // partial swap is enabled.
- root_pass->damage_rect = gfx::Rect(2, 2, 3, 3);
- // Frame 0 should be completely damaged because it's the first.
- // Frame 1 should be because it changed. Frame 60 should be
- // because it's disabling DC layers.
- gfx::Rect output_rectangle = (i == 0 || i == 1 || i == 60)
- ? root_pass->output_rect
- : root_pass->damage_rect;
-
- // Frame 0 should have DC Layers enabled because of the overlay.
- // After 60 frames of no overlays DC layers should be disabled again.
- if (i == 0)
- EXPECT_CALL(*gl, SetEnableDCLayersCHROMIUM(GL_TRUE));
- else if (i == 60)
- EXPECT_CALL(*gl, SetEnableDCLayersCHROMIUM(GL_FALSE));
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
- EXPECT_EQ(output_rectangle, output_surface->last_set_draw_rectangle());
- testing::Mock::VerifyAndClearExpectations(gl);
- }
-
- // Transfer resources back from the parent to the child. Set no resources as
- // being in use.
- parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
- ResourceIdSet());
-
- child_resource_provider->RemoveImportedResource(resource_id);
- child_resource_provider->ShutdownAndReleaseAllResources();
-}
-#endif
-
-class GLRendererWithMockContextTest : public ::testing::Test {
- protected:
- class MockContextSupport : public TestContextSupport {
- public:
- MockContextSupport() {}
- MOCK_METHOD1(SetAggressivelyFreeResources,
- void(bool aggressively_free_resources));
- };
-
- void SetUp() override {
- auto context_support = std::make_unique<MockContextSupport>();
- context_support_ptr_ = context_support.get();
- auto context_provider =
- TestContextProvider::Create(std::move(context_support));
- ASSERT_EQ(context_provider->BindToCurrentThread(),
- gpu::ContextResult::kSuccess);
- output_surface_ = FakeOutputSurface::Create3d(std::move(context_provider));
- output_surface_->BindToClient(&output_surface_client_);
- resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
- output_surface_->context_provider());
- renderer_ = std::make_unique<GLRenderer>(
- &settings_, &debug_settings_, output_surface_.get(),
- resource_provider_.get(), nullptr, nullptr);
- renderer_->Initialize();
- }
-
- RendererSettings settings_;
- DebugRendererSettings debug_settings_;
- cc::FakeOutputSurfaceClient output_surface_client_;
- MockContextSupport* context_support_ptr_;
- std::unique_ptr<OutputSurface> output_surface_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
- std::unique_ptr<GLRenderer> renderer_;
-};
-
-TEST_F(GLRendererWithMockContextTest,
- ContextPurgedWhenRendererBecomesInvisible) {
- EXPECT_CALL(*context_support_ptr_, SetAggressivelyFreeResources(false));
- renderer_->SetVisible(true);
- Mock::VerifyAndClearExpectations(context_support_ptr_);
-
- EXPECT_CALL(*context_support_ptr_, SetAggressivelyFreeResources(true));
- renderer_->SetVisible(false);
- Mock::VerifyAndClearExpectations(context_support_ptr_);
-}
-
-#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID)
-class ContentBoundsOverlayProcessor : public OverlayProcessorUsingStrategy {
- public:
- class TestStrategy : public OverlayProcessorStrategy {
- public:
- explicit TestStrategy(const std::vector<gfx::Rect>& content_bounds)
- : content_bounds_(content_bounds) {}
- ~TestStrategy() override = default;
-
- bool Attempt(const SkM44& output_color_matrix,
- const OverlayProcessorInterface::FilterOperationsMap&
- render_pass_backdrop_filters,
- DisplayResourceProvider* resource_provider,
- AggregatedRenderPassList* render_pass_list,
- SurfaceDamageRectList* surface_damage_rect_list,
- const PrimaryPlane* primary_plane,
- OverlayCandidateList* candidates,
- std::vector<gfx::Rect>* content_bounds) override {
- content_bounds->insert(content_bounds->end(), content_bounds_.begin(),
- content_bounds_.end());
- return true;
- }
-
- void ProposePrioritized(
- const SkM44& output_color_matrix,
- const FilterOperationsMap& render_pass_backdrop_filters,
- DisplayResourceProvider* resource_provider,
- AggregatedRenderPassList* render_pass_list,
- SurfaceDamageRectList* surface_damage_rect_list,
- const PrimaryPlane* primary_plane,
- std::vector<OverlayProposedCandidate>* candidates,
- std::vector<gfx::Rect>* content_bounds) override {
- auto* render_pass = render_pass_list->back().get();
- QuadList& quad_list = render_pass->quad_list;
- OverlayCandidate candidate;
- // Adding a mock candidate to the propose list so that
- // 'AttemptPrioritized' will be called.
- candidates->push_back({quad_list.end(), candidate, this});
- }
-
- bool AttemptPrioritized(
- const SkM44& output_color_matrix,
- const FilterOperationsMap& render_pass_backdrop_filters,
- DisplayResourceProvider* resource_provider,
- AggregatedRenderPassList* render_pass_list,
- SurfaceDamageRectList* surface_damage_rect_list,
- const PrimaryPlane* primary_plane,
- OverlayCandidateList* candidates,
- std::vector<gfx::Rect>* content_bounds,
- const OverlayProposedCandidate& proposed_candidate) override {
- content_bounds->insert(content_bounds->end(), content_bounds_.begin(),
- content_bounds_.end());
- return true;
- }
-
- void CommitCandidate(const OverlayProposedCandidate& proposed_candidate,
- AggregatedRenderPass* render_pass) override {}
-
- private:
- const std::vector<gfx::Rect> content_bounds_;
- };
-
- explicit ContentBoundsOverlayProcessor(
- const std::vector<gfx::Rect>& content_bounds)
- : OverlayProcessorUsingStrategy(), content_bounds_(content_bounds) {
- strategies_.push_back(
- std::make_unique<TestStrategy>(std::move(content_bounds_)));
- prioritization_config_.changing_threshold = false;
- prioritization_config_.damage_rate_threshold = false;
- }
-
- TestStrategy& strategy() {
- return static_cast<TestStrategy&>(*strategies_.back());
- }
- // Empty mock methods since this test set up uses strategies, which are only
- // for ozone and android.
- MOCK_CONST_METHOD0(NeedsSurfaceDamageRectList, bool());
- bool IsOverlaySupported() const override { return true; }
-
- // A list of possible overlay candidates is presented to this function.
- // The expected result is that those candidates that can be in a separate
- // plane are marked with |overlay_handled| set to true, otherwise they are
- // to be traditionally composited. Candidates with |overlay_handled| set to
- // true must also have their |display_rect| converted to integer
- // coordinates if necessary.
- void CheckOverlaySupportImpl(
- const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
- OverlayCandidateList* surfaces) override {}
-
- private:
- std::vector<gfx::Rect> content_bounds_;
-};
-
-class GLRendererSwapWithBoundsTest : public GLRendererTest {
- protected:
- void RunTest(const std::vector<gfx::Rect>& content_bounds) {
- auto gl_owned = std::make_unique<TestGLES2Interface>();
- gl_owned->set_have_swap_buffers_with_bounds(true);
-
- auto provider = TestContextProvider::Create(std::move(gl_owned));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- std::unique_ptr<FakeOutputSurface> output_surface(
- FakeOutputSurface::Create3d(std::move(provider)));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- RendererSettings settings;
- auto processor =
- std::make_unique<ContentBoundsOverlayProcessor>(content_bounds);
- FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
- resource_provider.get(), processor.get());
- renderer.Initialize();
- EXPECT_EQ(true, renderer.use_swap_with_bounds());
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(100, 100);
-
- {
- AggregatedRenderPassId root_pass_id{1};
- cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
- gfx::Rect(viewport_size), gfx::Transform(),
- cc::FilterOperations());
-
- renderer.DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
- renderer.SwapBuffers({});
-
- std::vector<gfx::Rect> expected_content_bounds;
- EXPECT_EQ(content_bounds,
- output_surface->last_sent_frame()->content_bounds);
- }
- }
-};
-
-TEST_F(GLRendererSwapWithBoundsTest, EmptyContent) {
- std::vector<gfx::Rect> content_bounds;
- RunTest(content_bounds);
-}
-
-TEST_F(GLRendererSwapWithBoundsTest, NonEmpty) {
- std::vector<gfx::Rect> content_bounds;
- content_bounds.push_back(gfx::Rect(0, 0, 10, 10));
- content_bounds.push_back(gfx::Rect(20, 20, 30, 30));
- RunTest(content_bounds);
-}
-#endif // defined(USE_OZONE) || BUILDFLAG(IS_ANDROID)
-
-#if BUILDFLAG(IS_APPLE)
-class MockCALayerGLES2Interface : public TestGLES2Interface {
- public:
- MOCK_METHOD6(ScheduleCALayerSharedStateCHROMIUM,
- void(GLfloat opacity,
- GLboolean is_clipped,
- const GLfloat* clip_rect,
- const GLfloat* rounded_corner_bounds,
- GLint sorting_context_id,
- const GLfloat* transform));
- MOCK_METHOD6(ScheduleCALayerCHROMIUM,
- void(GLuint contents_texture_id,
- const GLfloat* contents_rect,
- GLuint background_color,
- GLuint edge_aa_mask,
- const GLfloat* bounds_rect,
- GLuint filter));
- MOCK_METHOD2(ScheduleCALayerInUseQueryCHROMIUM,
- void(GLsizei count, const GLuint* textures));
- MOCK_METHOD5(
- Uniform4f,
- void(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w));
-};
-
-class CALayerGLRendererTest : public GLRendererTest {
- protected:
- void SetUp() override {
- // A mock GLES2Interface that can watch CALayer stuff happen.
- auto gles2_interface = std::make_unique<MockCALayerGLES2Interface>();
- // Support image storage for GpuMemoryBuffers, needed for
- // CALayers/IOSurfaces backed by textures.
- gles2_interface->set_support_texture_storage_image(true);
- // Allow the renderer to make an empty SwapBuffers - skipping even the
- // root RenderPass.
- gles2_interface->set_have_commit_overlay_planes(true);
-
- gl_ = gles2_interface.get();
-
- auto provider = TestContextProvider::Create(std::move(gles2_interface));
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
- output_surface_->BindToClient(&output_surface_client);
-
- display_resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
- output_surface_->context_provider());
-
- settings_ = std::make_unique<RendererSettings>();
- // This setting is enabled to use CALayer overlays.
- settings_->release_overlay_resources_after_gpu_query = true;
- // The Mac TestOverlayProcessor default to enable CALayer overlays, then all
- // damage is removed and we can skip the root RenderPass, swapping empty.
- overlay_processor_ = std::make_unique<OverlayProcessorMac>(
- std::make_unique<CALayerOverlayProcessor>());
- renderer_ = std::make_unique<FakeRendererGL>(
- settings_.get(), &debug_settings_, output_surface_.get(),
- display_resource_provider_.get(), overlay_processor_.get(),
- base::ThreadTaskRunnerHandle::Get());
- renderer_->Initialize();
- renderer_->SetVisible(true);
- }
-
- void TearDown() override {
- renderer_.reset();
- display_resource_provider_.reset();
- output_surface_.reset();
- }
-
- void DrawBlackFrame(const gfx::Size& viewport_size) {
- AggregatedRenderPassId root_pass_id{1};
-
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK);
-
- renderer().DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
-
- DrawFrame(&renderer(), viewport_size);
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
- }
-
- MockCALayerGLES2Interface& gl() const { return *gl_; }
- FakeRendererGL& renderer() const { return *renderer_; }
- FakeOutputSurface& output_surface() const { return *output_surface_; }
-
- private:
- MockCALayerGLES2Interface* gl_;
- std::unique_ptr<FakeOutputSurface> output_surface_;
- std::unique_ptr<DisplayResourceProviderGL> display_resource_provider_;
- std::unique_ptr<RendererSettings> settings_;
- std::unique_ptr<OverlayProcessorInterface> overlay_processor_;
- std::unique_ptr<FakeRendererGL> renderer_;
-};
-
-TEST_F(CALayerGLRendererTest, CALayerOverlaysWithAllQuadsPromoted) {
- gfx::Size viewport_size(10, 10);
-
- // Draw an empty frame to make sure output surface is reshaped before tests.
- DrawBlackFrame(viewport_size);
-
- // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to
- // a child pass that is at 1,2 to make it identifiable.
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPassId root_pass_id{1};
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(viewport_size) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The child pass is drawn, promoted to an overlay, and scheduled as a
- // CALayer.
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
-
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // The damage was eliminated when everything was promoted to CALayers.
- ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
- EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());
-
- // Frame number 2. Same inputs, except...
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(viewport_size) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
-
- // Use a cached RenderPass for the child.
- child_pass->cache_render_pass = true;
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The child CompositorRenderPassDrawQuad gets promoted again, but importantly
- // it did not itself have to be drawn this time as it can use the cached
- // texture. Because we can skip the child pass, and the root pass (all quads
- // were promoted), this exposes edge cases in GLRenderer if it assumes we draw
- // at least one RenderPass. This still works, doesn't crash, etc, and the
- // CompositorRenderPassDrawQuad is emitted.
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
-
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-}
-
-TEST_F(CALayerGLRendererTest, CALayerRoundRects) {
- gfx::Size viewport_size(10, 10);
-
- // Draw an empty frame to make sure output surface is reshaped before tests.
- DrawBlackFrame(viewport_size);
-
- for (size_t subtest = 0; subtest < 3; ++subtest) {
- AggregatedRenderPass* child_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(250, 250), gfx::Transform(), cc::FilterOperations());
-
- AggregatedRenderPassId root_pass_id{1};
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- auto* quad = cc::AddRenderPassQuad(root_pass, child_pass);
- SharedQuadState* sqs =
- const_cast<SharedQuadState*>(quad->shared_quad_state);
-
- sqs->clip_rect = gfx::Rect(2, 2, 6, 6);
- const float radius = 2;
- sqs->mask_filter_info =
- gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(*sqs->clip_rect), radius));
-
- switch (subtest) {
- case 0:
- // Subtest 0 is a simple round rect that matches the clip rect, and
- // should be handled by CALayers.
- EXPECT_CALL(gl(), Uniform4f(_, _, _, _, _)).Times(1);
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _))
- .Times(1);
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(1);
- break;
- case 1:
- // Subtest 1 doesn't match clip and rounded rect, but we can still
- // use CALayers.
- sqs->clip_rect = gfx::Rect(3, 3, 4, 4);
- EXPECT_CALL(gl(), Uniform4f(_, _, _, _, _)).Times(1);
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(1);
- break;
- case 2:
- // Subtest 2 has a non-simple rounded rect.
- gfx::RRectF rounded_corner_bounds =
- sqs->mask_filter_info.rounded_corner_bounds();
- rounded_corner_bounds.SetCornerRadii(gfx::RRectF::Corner::kUpperLeft, 1,
- 1);
- sqs->mask_filter_info = gfx::MaskFilterInfo(rounded_corner_bounds);
- // Called 2 extra times in order to set up the rounded corner
- // parameters in the shader, because the CALayer is not handling
- // the rounded corners.
- EXPECT_CALL(gl(), Uniform4f(_, _, _, _, _)).Times(3);
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(0);
- break;
- }
-
- renderer().DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- }
-}
-
-TEST_F(CALayerGLRendererTest, CALayerOverlaysReusesTextureWithDifferentSizes) {
- gfx::Size viewport_size(300, 300);
-
- // Draw an empty frame to make sure output surface is reshaped before tests.
- DrawBlackFrame(viewport_size);
-
- // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to
- // a child pass that is at 1,2 to make it identifiable. The child's size is
- // 250x251, but it will be rounded up to a multiple of 64 in order to promote
- // easier texture reuse. See https://crbug.com/146070.
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPassId root_pass_id{1};
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(250, 251) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The child pass is drawn, promoted to an overlay, and scheduled as a
- // CALayer. The bounds of the texture are rounded up to 256x256. We save the
- // texture ID to make sure we reuse it correctly.
- uint32_t saved_texture_id = 0;
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- // The size is rounded to a multiple of 64.
- EXPECT_EQ(256, bounds_rect[2]);
- EXPECT_EQ(256, bounds_rect[3]);
- saved_texture_id = contents_texture_id;
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
- EXPECT_NE(saved_texture_id, 0u);
-
- // The damage was eliminated when everything was promoted to CALayers.
- ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
- EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());
-
- // The texture will be checked to verify if it is free yet.
- EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(1, _));
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
-
- // Frame number 2. We change the size of the child RenderPass to be smaller
- // than the next multiple of 64, but larger than half the previous size so
- // that our texture reuse heuristics will reuse the texture if it is free.
- // For now, it is not.
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(190, 191) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The child RenderPass will use a new 192x192 texture, since the last texture
- // is still in use.
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // New texture id.
- EXPECT_NE(saved_texture_id, contents_texture_id);
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- // The texture is 192x192 since we snap up to multiples of 64.
- EXPECT_EQ(192, bounds_rect[2]);
- EXPECT_EQ(192, bounds_rect[3]);
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // There are now 2 textures to check if they are free.
- EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(2, _));
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
-
- // The first (256x256) texture is returned to the GLRenderer.
- renderer().DidReceiveTextureInUseResponses({{saved_texture_id, false}});
-
- // Frame number 3 looks just like frame number 2. The child RenderPass is
- // smaller than the next multiple of 64 from the released texture, but larger
- // than half of its size so that our texture reuse heuristics will kick in.
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(190, 191) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The child RenderPass would try to use a 192x192 texture, but since we have
- // an existing 256x256 texture, we can reuse that.
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // The first texture is reused.
- EXPECT_EQ(saved_texture_id, contents_texture_id);
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- // The size here is the size of the texture being used, not
- // the size we tried to use (192x192).
- EXPECT_EQ(256, bounds_rect[2]);
- EXPECT_EQ(256, bounds_rect[3]);
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-}
-
-TEST_F(CALayerGLRendererTest, CALayerOverlaysDontReuseTooBigTexture) {
- gfx::Size viewport_size(300, 300);
-
- // Draw an empty frame to make sure output surface is reshaped before tests.
- DrawBlackFrame(viewport_size);
-
- // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to
- // a child pass that is at 1,2 to make it identifiable. The child's size is
- // 250x251, but it will be rounded up to a multiple of 64 in order to promote
- // easier texture reuse. See https://crbug.com/146070.
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPassId root_pass_id{1};
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(250, 251) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The child pass is drawn, promoted to an overlay, and scheduled as a
- // CALayer. The bounds of the texture are rounded up to 256x256. We save the
- // texture ID to make sure we reuse it correctly.
- uint32_t saved_texture_id = 0;
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- // The size is rounded to a multiple of 64.
- EXPECT_EQ(256, bounds_rect[2]);
- EXPECT_EQ(256, bounds_rect[3]);
- saved_texture_id = contents_texture_id;
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
- EXPECT_NE(saved_texture_id, 0u);
-
- // The damage was eliminated when everything was promoted to CALayers.
- ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
- EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());
-
- // The texture will be checked to verify if it is free yet.
- EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(1, _));
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
-
- // Frame number 2. We change the size of the child RenderPass to be much
- // smaller.
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(20, 21) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The child RenderPass will use a new 64x64 texture, since the last texture
- // is still in use.
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // New texture id.
- EXPECT_NE(saved_texture_id, contents_texture_id);
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- // The texture is 64x64 since we snap up to multiples of 64.
- EXPECT_EQ(64, bounds_rect[2]);
- EXPECT_EQ(64, bounds_rect[3]);
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // There are now 2 textures to check if they are free.
- EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(2, _));
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
-
- // The first (256x256) texture is returned to the GLRenderer.
- renderer().DidReceiveTextureInUseResponses({{saved_texture_id, false}});
-
- // Frame number 3 looks just like frame number 2. The child RenderPass is
- // too small to reuse the old texture.
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(20, 21) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The child RenderPass would try to use a 64x64 texture. We have a free and
- // existing 256x256 texture, but it's too large for us to reuse it.
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // The first texture is not reused.
- EXPECT_NE(saved_texture_id, contents_texture_id);
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- // The new texture has a smaller size.
- EXPECT_EQ(64, bounds_rect[2]);
- EXPECT_EQ(64, bounds_rect[3]);
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-}
-
-TEST_F(CALayerGLRendererTest, CALayerOverlaysReuseAfterNoSwapBuffers) {
- gfx::Size viewport_size(300, 300);
-
- // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to
- // a child pass that is at 1,2 to make it identifiable.
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPassId root_pass_id{1};
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(100, 100) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The child pass is drawn, promoted to an overlay, and scheduled as a
- // CALayer. We save the texture ID to make sure we reuse it correctly.
- uint32_t saved_texture_id = 0;
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- saved_texture_id = contents_texture_id;
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
-
- // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
- EXPECT_NE(saved_texture_id, 0u);
-
- // SwapBuffers() is *not* called though! Display can do this sometimes.
-
- // Frame number 2. We can not reuse the texture since the last one isn't
- // returned yet. We use a different size so we can control which texture gets
- // reused later.
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(200, 200) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- uint32_t second_saved_texture_id = 0;
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // New texture id.
- EXPECT_NE(saved_texture_id, contents_texture_id);
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- second_saved_texture_id = contents_texture_id;
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
-
- // SwapBuffers() *does* happen this time.
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // There are 2 textures to check if they are free.
- EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(2, _));
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
-
- // Both textures get returned and the 2nd one can be reused.
- renderer().DidReceiveTextureInUseResponses(
- {{saved_texture_id, false}, {second_saved_texture_id, false}});
-
- // Frame number 3 looks just like frame number 2.
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(200, 200) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- // The 2nd texture that we sent has been returned so we can reuse it. We
- // verify that happened.
- {
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // The second texture is reused.
- EXPECT_EQ(second_saved_texture_id, contents_texture_id);
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- }));
- }
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-}
-
-TEST_F(CALayerGLRendererTest, CALayerOverlaysReuseManyIfReturnedSlowly) {
- gfx::Size viewport_size(300, 300);
-
- // Draw an empty frame to make sure output surface is reshaped before tests.
- DrawBlackFrame(viewport_size);
-
- // Each frame has a root pass with a CompositorRenderPassDrawQuad pointing to
- // a child pass. We generate a bunch of frames and swap them, each with a
- // different child RenderPass id, without getting any of the resources back
- // from the OS.
- AggregatedRenderPassId root_pass_id{1};
-
- // The number is at least 2 larger than the number of textures we expect to
- // reuse, so that we can leave one in the OS, and have 1 texture returned but
- // not reused.
- const int kNumSendManyTextureIds = 7;
- uint32_t sent_texture_ids[kNumSendManyTextureIds];
- for (int i = 0; i < kNumSendManyTextureIds; ++i) {
- AggregatedRenderPass* child_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{i + 2},
- gfx::Rect(250, 251) + gfx::Vector2d(1, 2), gfx::Transform(),
- cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
-
- renderer().DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
-
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- sent_texture_ids[i] = contents_texture_id;
- }));
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
- EXPECT_NE(sent_texture_ids[i], 0u);
-
- // The damage was eliminated when everything was promoted to CALayers.
- ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
- EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());
-
- // All sent textures will be checked to verify if they are free yet.
- EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(i + 1, _));
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
- }
-
- // Now all but 1 texture get returned by the OS, so they are all inserted
- // into the cache for reuse.
- std::vector<uint32_t> returned_texture_ids;
- for (int i = 0; i < kNumSendManyTextureIds - 1; ++i) {
- uint32_t id = sent_texture_ids[i];
- renderer().DidReceiveTextureInUseResponses({{id, false}});
- returned_texture_ids.push_back(id);
- }
-
- // We should keep *some* of these textures around to reuse them across
- // multiple frames. https://crbug.com/146070 motivates this, and empirical
- // testing found 5 to be a good number.
- const int kNumSendReusedTextures = 5;
- // See comment on |kNumSendManyTextureIds|.
- ASSERT_LT(kNumSendReusedTextures, kNumSendManyTextureIds - 1);
-
- for (int i = 0; i < kNumSendReusedTextures + 1; ++i) {
- // We use different RenderPass ids to ensure that the cache allows reuse
- // even if they don't match.
- AggregatedRenderPass* child_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{i + 100},
- gfx::Rect(250, 251) + gfx::Vector2d(1, 2), gfx::Transform(),
- cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
-
- renderer().DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
-
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(Invoke([&](GLuint contents_texture_id,
- const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
-
- if (i < kNumSendReusedTextures) {
- // The texture id should be from the set of returned ones.
- EXPECT_THAT(returned_texture_ids, Contains(contents_texture_id));
- base::Erase(returned_texture_ids, contents_texture_id);
- } else {
- // More textures were returned at once than we expect to reuse
- // so eventually we should be making a new texture to show we're
- // not just keeping infinity textures in the cache.
- EXPECT_THAT(returned_texture_ids,
- Not(Contains(contents_texture_id)));
- // This shows that there was some returned id that we didn't use.
- EXPECT_FALSE(returned_texture_ids.empty());
- }
- }));
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // All sent textures will be checked to verify if they are free yet. There's
- // also 1 outstanding texture to check for that wasn't returned yet from the
- // above loop.
- EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(i + 2, _));
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
- }
-}
-
-TEST_F(CALayerGLRendererTest, CALayerOverlaysCachedTexturesAreFreed) {
- gfx::Size viewport_size(300, 300);
-
- // Draw an empty frame to make sure output surface is reshaped before tests.
- DrawBlackFrame(viewport_size);
-
- // Each frame has a root pass with a CompositorRenderPassDrawQuad pointing to
- // a child pass. We generate a bunch of frames and swap them, each with a
- // different child RenderPass id, without getting any of the resources back
- // from the OS.
- AggregatedRenderPassId child_pass_id{2};
- AggregatedRenderPassId root_pass_id{1};
-
- // We send a whole bunch of textures as overlays to the OS.
- const int kNumSendManyTextureIds = 7;
- uint32_t sent_texture_ids[kNumSendManyTextureIds];
- for (int i = 0; i < kNumSendManyTextureIds; ++i) {
- AggregatedRenderPass* child_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{i + 2},
- gfx::Rect(250, 251) + gfx::Vector2d(1, 2), gfx::Transform(),
- cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
-
- renderer().DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
-
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(
- Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
- sent_texture_ids[i] = contents_texture_id;
- }));
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
- EXPECT_NE(sent_texture_ids[i], 0u);
-
- // The damage was eliminated when everything was promoted to CALayers.
- ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
- EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());
-
- // All sent textures will be checked to verify if they are free yet.
- EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(i + 1, _));
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
- }
-
- // Now all but 1 texture get returned by the OS, so they are all inserted
- // into the cache for reuse.
- std::vector<uint32_t> returned_texture_ids;
- for (int i = 0; i < kNumSendManyTextureIds - 1; ++i) {
- uint32_t id = sent_texture_ids[i];
- renderer().DidReceiveTextureInUseResponses({{id, false}});
- returned_texture_ids.push_back(id);
- }
-
- // We generate a bunch of frames that don't use the cache, one less than the
- // number of textures returned.
- for (int i = 0; i < kNumSendManyTextureIds - 2; ++i) {
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(100, 100), SK_ColorRED);
-
- renderer().DecideRenderPassAllocationsForFrame(
- render_passes_in_draw_order_);
-
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _));
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-
- // There's just 1 outstanding RenderPass texture to query for.
- EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(1, _));
- renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
- Mock::VerifyAndClearExpectations(&gl());
- }
-
- // By now the cache should be empty, to show that we don't keep cached
- // textures that won't be used forever. We generate a frame with a
- // CompositorRenderPassDrawQuad and verify that it does not reuse a texture
- // from the (empty) cache.
- {
- AggregatedRenderPass* child_pass =
- cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
- gfx::Rect(250, 251) + gfx::Vector2d(1, 2),
- gfx::Transform(), cc::FilterOperations());
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
- gfx::Transform(), cc::FilterOperations());
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- }
-
- renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
-
- InSequence sequence;
- EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
- EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
- .WillOnce(Invoke([&](GLuint contents_texture_id,
- const GLfloat* contents_rect,
- GLuint background_color, GLuint edge_aa_mask,
- const GLfloat* bounds_rect, GLuint filter) {
- // This is the child CompositorRenderPassDrawQuad.
- EXPECT_EQ(1, bounds_rect[0]);
- EXPECT_EQ(2, bounds_rect[1]);
-
- // More textures were returned at once than we expect to reuse
- // so eventually we should be making a new texture to show we're
- // not just keeping infinity textures in the cache.
- EXPECT_THAT(returned_texture_ids, Not(Contains(contents_texture_id)));
- // This shows that there was some returned id that we didn't use.
- EXPECT_FALSE(returned_texture_ids.empty());
- }));
- DrawFrame(&renderer(), viewport_size);
- Mock::VerifyAndClearExpectations(&gl());
- renderer().SwapBuffers(DirectRenderer::SwapFrameData());
-}
-#endif
-
-class FramebufferWatchingGLRenderer : public FakeRendererGL {
- public:
- FramebufferWatchingGLRenderer(RendererSettings* settings,
- const DebugRendererSettings* debug_settings,
- OutputSurface* output_surface,
- DisplayResourceProviderGL* resource_provider)
- : FakeRendererGL(settings,
- debug_settings,
- output_surface,
- resource_provider) {}
-
- void BindFramebufferToOutputSurface() override {
- ++bind_root_framebuffer_calls_;
- FakeRendererGL::BindFramebufferToOutputSurface();
- }
-
- void BindFramebufferToTexture(
- const AggregatedRenderPassId render_pass_id) override {
- ++bind_child_framebuffer_calls_;
- FakeRendererGL::BindFramebufferToTexture(render_pass_id);
- }
-
- int bind_root_framebuffer_calls() const {
- return bind_root_framebuffer_calls_;
- }
- int bind_child_framebuffer_calls() const {
- return bind_child_framebuffer_calls_;
- }
-
- void ResetBindCalls() {
- bind_root_framebuffer_calls_ = bind_child_framebuffer_calls_ = 0;
- }
-
- private:
- int bind_root_framebuffer_calls_ = 0;
- int bind_child_framebuffer_calls_ = 0;
-};
-
-TEST_F(GLRendererTest, UndamagedRenderPassStillDrawnWhenNoPartialSwap) {
- auto provider = TestContextProvider::Create();
- provider->UnboundTestContextGL()->set_have_post_sub_buffer(true);
- provider->BindToCurrentThread();
-
- cc::FakeOutputSurfaceClient output_surface_client;
- auto output_surface = FakeOutputSurface::Create3d(std::move(provider));
- output_surface->BindToClient(&output_surface_client);
-
- auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
- output_surface->context_provider());
-
- for (int i = 0; i < 2; ++i) {
- bool use_partial_swap = i == 0;
- SCOPED_TRACE(use_partial_swap);
-
- RendererSettings settings;
- settings.partial_swap_enabled = use_partial_swap;
- FramebufferWatchingGLRenderer renderer(&settings, &debug_settings_,
- output_surface.get(),
- resource_provider.get());
- renderer.Initialize();
- EXPECT_EQ(use_partial_swap, renderer.use_partial_swap());
- renderer.SetVisible(true);
-
- gfx::Size viewport_size(100, 100);
- gfx::Rect child_rect(10, 10);
-
- // First frame, the child and root RenderPass each have damage.
- AggregatedRenderPass* child_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{2}, child_rect,
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(child_pass, child_rect, SK_ColorGREEN);
- child_pass->damage_rect = child_rect;
-
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorRED);
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- root_pass->damage_rect = gfx::Rect(viewport_size);
-
- EXPECT_EQ(0, renderer.bind_root_framebuffer_calls());
- EXPECT_EQ(0, renderer.bind_child_framebuffer_calls());
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
-
- // We had to draw the root, and the child.
- EXPECT_EQ(1, renderer.bind_child_framebuffer_calls());
- // When the CompositorRenderPassDrawQuad in the root is drawn, we may
- // re-bind the root framebuffer. So it can be bound more than once.
- EXPECT_GE(renderer.bind_root_framebuffer_calls(), 1);
-
- // Reset counting.
- renderer.ResetBindCalls();
-
- // Second frame, the child RenderPass has no damage in it.
- child_pass = cc::AddRenderPass(&render_passes_in_draw_order_,
- AggregatedRenderPassId{2}, child_rect,
- gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(child_pass, child_rect, SK_ColorGREEN);
- child_pass->damage_rect = gfx::Rect();
-
- // Root RenderPass has some damage that doesn't intersect the child.
- root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorRED);
- cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
- gfx::Transform(), SkBlendMode::kSrcOver);
- root_pass->damage_rect = gfx::Rect(child_rect.right(), 0, 10, 10);
-
- EXPECT_EQ(0, renderer.bind_root_framebuffer_calls());
- EXPECT_EQ(0, renderer.bind_child_framebuffer_calls());
-
- renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
- DrawFrame(&renderer, viewport_size);
-
- if (use_partial_swap) {
- // Without damage overlapping the child, it didn't need to be drawn (it
- // may choose to anyway but that'd be a waste). So we don't check for
- // |bind_child_framebuffer_calls|. But the root should have been drawn.
- EXPECT_EQ(renderer.bind_root_framebuffer_calls(), 1);
- } else {
- // Without partial swap, we have to draw the child still, this means
- // the child is bound as the framebuffer.
- EXPECT_EQ(1, renderer.bind_child_framebuffer_calls());
- // When the CompositorRenderPassDrawQuad in the root is drawn, as it must
- // be since we must draw the entire output, we may re-bind the root
- // framebuffer. So it can be bound more than once.
- EXPECT_GE(renderer.bind_root_framebuffer_calls(), 1);
- }
- }
-}
-
-#if defined(USE_OZONE) || BUILDFLAG(IS_ANDROID)
-class GLRendererWithGpuFenceTest : public GLRendererTest {
- protected:
- GLRendererWithGpuFenceTest() {
- auto provider = TestContextProvider::Create();
- provider->BindToCurrentThread();
- provider->TestContextGL()->set_have_commit_overlay_planes(true);
- test_context_support_ = provider->support();
-
- output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
- output_surface_->set_overlay_texture_id(kSurfaceOverlayTextureId);
- output_surface_->set_gpu_fence_id(kGpuFenceId);
- resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
- output_surface_->context_provider());
- overlay_processor_ = std::make_unique<SingleOverlayOnTopProcessor>();
- overlay_processor_->AllowMultipleCandidates();
- renderer_ = std::make_unique<FakeRendererGL>(
- &settings_, &debug_settings_, output_surface_.get(),
- resource_provider_.get(), overlay_processor_.get(),
- base::ThreadTaskRunnerHandle::Get());
- renderer_->Initialize();
- renderer_->SetVisible(true);
-
- test_context_support_->SetScheduleOverlayPlaneCallback(
- base::BindRepeating(&MockOverlayScheduler::Schedule,
- base::Unretained(&overlay_scheduler_)));
- }
-
- ~GLRendererWithGpuFenceTest() override {
- if (child_resource_provider_)
- child_resource_provider_->ShutdownAndReleaseAllResources();
- }
-
- ResourceId create_overlay_resource() {
- child_context_provider_ = TestContextProvider::Create();
- child_context_provider_->BindToCurrentThread();
-
- child_resource_provider_ = std::make_unique<ClientResourceProvider>();
- auto transfer_resource = TransferableResource::MakeGL(
- gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
- gfx::Size(256, 256), true);
- ResourceId client_resource_id = child_resource_provider_->ImportResource(
- transfer_resource, base::DoNothing());
-
- std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
- cc::SendResourceAndGetChildToParentMap(
- {client_resource_id}, resource_provider_.get(),
- child_resource_provider_.get(), child_context_provider_.get());
- return resource_map[client_resource_id];
- }
-
- static constexpr unsigned kSurfaceOverlayTextureId = 33;
- static constexpr unsigned kGpuFenceId = 66;
- static constexpr unsigned kGpuNoFenceId = 0;
-
- TestContextSupport* test_context_support_;
-
- cc::FakeOutputSurfaceClient output_surface_client_;
- std::unique_ptr<FakeOutputSurface> output_surface_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
- scoped_refptr<TestContextProvider> child_context_provider_;
- std::unique_ptr<ClientResourceProvider> child_resource_provider_;
- RendererSettings settings_;
- std::unique_ptr<SingleOverlayOnTopProcessor> overlay_processor_;
- std::unique_ptr<FakeRendererGL> renderer_;
- MockOverlayScheduler overlay_scheduler_;
-};
-
-TEST_F(GLRendererWithGpuFenceTest, GpuFenceIdIsUsedWithRootRenderPassOverlay) {
- gfx::Size viewport_size(100, 100);
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- root_pass->has_transparent_background = false;
-
- EXPECT_CALL(overlay_scheduler_,
- Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, kSurfaceOverlayTextureId,
- gfx::Rect(viewport_size), _, _, kGpuFenceId))
- .Times(1);
- DrawFrame(renderer_.get(), viewport_size);
-}
-
-TEST_F(GLRendererWithGpuFenceTest,
- GpuFenceIdIsUsedOnlyForRootRenderPassOverlay) {
- gfx::Size viewport_size(100, 100);
- AggregatedRenderPass* root_pass = cc::AddRenderPass(
- &render_passes_in_draw_order_, AggregatedRenderPassId{1},
- gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
- root_pass->has_transparent_background = false;
-
- bool needs_blending = false;
- bool premultiplied_alpha = false;
- bool flipped = false;
- bool nearest_neighbor = false;
- float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- gfx::PointF uv_top_left(0, 0);
- gfx::PointF uv_bottom_right(1, 1);
-
- TextureDrawQuad* overlay_quad =
- root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
- SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
- shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
- gfx::Rect(50, 50), gfx::MaskFilterInfo(), absl::nullopt,
- false, 1, SkBlendMode::kSrcOver, 0);
- overlay_quad->SetNew(
- shared_state, gfx::Rect(viewport_size), gfx::Rect(viewport_size),
- needs_blending, create_overlay_resource(), premultiplied_alpha,
- uv_top_left, uv_bottom_right, SK_ColorTRANSPARENT, vertex_opacity,
- flipped, nearest_neighbor,
- /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);
-
- EXPECT_CALL(overlay_scheduler_,
- Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, kSurfaceOverlayTextureId,
- gfx::Rect(viewport_size), _, _, kGpuFenceId))
- .Times(1);
- EXPECT_CALL(overlay_scheduler_,
- Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _,
- gfx::Rect(viewport_size), _, _, kGpuNoFenceId))
- .Times(1);
- DrawFrame(renderer_.get(), viewport_size);
-}
-#endif // defined(USE_OZONE) || BUILDFLAG(IS_ANDROID)
-
-} // namespace
-} // namespace viz
diff --git a/chromium/components/viz/service/display/layer_quad.cc b/chromium/components/viz/service/display/layer_quad.cc
deleted file mode 100644
index d7349d86d79..00000000000
--- a/chromium/components/viz/service/display/layer_quad.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2011 The Chromium 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/viz/service/display/layer_quad.h"
-
-#include <stddef.h>
-
-#include "base/check.h"
-#include "ui/gfx/geometry/quad_f.h"
-
-namespace viz {
-
-LayerQuad::Edge::Edge(const gfx::PointF& p, const gfx::PointF& q) {
- if (p == q) {
- degenerate_ = true;
- return;
- }
- degenerate_ = false;
- gfx::Vector2dF tangent(p.y() - q.y(), q.x() - p.x());
- float cross2 = p.x() * q.y() - q.x() * p.y();
-
- set(tangent.x(), tangent.y(), cross2);
- scale(1.0f / tangent.Length());
-}
-
-gfx::PointF LayerQuad::Edge::Intersect(const LayerQuad::Edge& e) const {
- DCHECK(!degenerate());
- DCHECK(!e.degenerate());
-
- return gfx::PointF((y() * e.z() - e.y() * z()) / (x() * e.y() - e.x() * y()),
- (x() * e.z() - e.x() * z()) / (e.x() * y() - x() * e.y()));
-}
-
-LayerQuad::LayerQuad(const gfx::QuadF& quad) {
- // Create edges.
- left_ = Edge(quad.p4(), quad.p1());
- right_ = Edge(quad.p2(), quad.p3());
- top_ = Edge(quad.p1(), quad.p2());
- bottom_ = Edge(quad.p3(), quad.p4());
-
- float sign = quad.IsCounterClockwise() ? -1 : 1;
- left_.scale(sign);
- right_.scale(sign);
- top_.scale(sign);
- bottom_.scale(sign);
-}
-
-LayerQuad::LayerQuad(const Edge& left,
- const Edge& top,
- const Edge& right,
- const Edge& bottom)
- : left_(left), top_(top), right_(right), bottom_(bottom) {}
-
-gfx::QuadF LayerQuad::ToQuadF() const {
- size_t num_degenerate_edges = left_.degenerate() + right_.degenerate() +
- top_.degenerate() + bottom_.degenerate();
- if (num_degenerate_edges > 1) {
- return gfx::QuadF();
- }
-
- if (left_.degenerate()) {
- return gfx::QuadF(top_.Intersect(bottom_), top_.Intersect(right_),
- right_.Intersect(bottom_), bottom_.Intersect(top_));
- }
- if (right_.degenerate()) {
- return gfx::QuadF(left_.Intersect(top_), top_.Intersect(bottom_),
- bottom_.Intersect(top_), bottom_.Intersect(left_));
- }
- if (top_.degenerate()) {
- return gfx::QuadF(left_.Intersect(right_), right_.Intersect(left_),
- right_.Intersect(bottom_), bottom_.Intersect(left_));
- }
- if (bottom_.degenerate()) {
- return gfx::QuadF(left_.Intersect(top_), top_.Intersect(right_),
- right_.Intersect(left_), left_.Intersect(right_));
- }
- return gfx::QuadF(left_.Intersect(top_), top_.Intersect(right_),
- right_.Intersect(bottom_), bottom_.Intersect(left_));
-}
-
-void LayerQuad::ToFloatArray(float flattened[12]) const {
- if (left_.degenerate()) {
- flattened[0] = bottom_.x();
- flattened[1] = bottom_.y();
- flattened[2] = bottom_.z();
- } else {
- flattened[0] = left_.x();
- flattened[1] = left_.y();
- flattened[2] = left_.z();
- }
- if (top_.degenerate()) {
- flattened[3] = left_.x();
- flattened[4] = left_.y();
- flattened[5] = left_.z();
- } else {
- flattened[3] = top_.x();
- flattened[4] = top_.y();
- flattened[5] = top_.z();
- }
- if (right_.degenerate()) {
- flattened[6] = top_.x();
- flattened[7] = top_.y();
- flattened[8] = top_.z();
- } else {
- flattened[6] = right_.x();
- flattened[7] = right_.y();
- flattened[8] = right_.z();
- }
- if (bottom_.degenerate()) {
- flattened[9] = right_.x();
- flattened[10] = right_.y();
- flattened[11] = right_.z();
- } else {
- flattened[9] = bottom_.x();
- flattened[10] = bottom_.y();
- flattened[11] = bottom_.z();
- }
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/layer_quad.h b/chromium/components/viz/service/display/layer_quad.h
deleted file mode 100644
index e6cd61fa457..00000000000
--- a/chromium/components/viz/service/display/layer_quad.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2011 The Chromium 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 COMPONENTS_VIZ_SERVICE_DISPLAY_LAYER_QUAD_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_LAYER_QUAD_H_
-
-#include "components/viz/service/viz_service_export.h"
-#include "ui/gfx/geometry/point_f.h"
-
-namespace gfx {
-class QuadF;
-}
-
-namespace viz {
-
-constexpr float kAntiAliasingInflateDistance = 0.5f;
-
-class VIZ_SERVICE_EXPORT LayerQuad {
- public:
- class VIZ_SERVICE_EXPORT Edge {
- public:
- Edge() : x_(0), y_(0), z_(0), degenerate_(false) {}
- Edge(const gfx::PointF& p, const gfx::PointF& q);
-
- float x() const { return x_; }
- float y() const { return y_; }
- float z() const { return z_; }
-
- void set_x(float x) { x_ = x; }
- void set_y(float y) { y_ = y; }
- void set_z(float z) { z_ = z; }
- void set(float x, float y, float z) {
- x_ = x;
- y_ = y;
- z_ = z;
- }
-
- void move_x(float dx) { x_ += dx; }
- void move_y(float dy) { y_ += dy; }
- void move_z(float dz) { z_ += dz; }
- void move(float dx, float dy, float dz) {
- x_ += dx;
- y_ += dy;
- z_ += dz;
- }
-
- void scale_x(float sx) { x_ *= sx; }
- void scale_y(float sy) { y_ *= sy; }
- void scale_z(float sz) { z_ *= sz; }
- void scale(float sx, float sy, float sz) {
- x_ *= sx;
- y_ *= sy;
- z_ *= sz;
- }
- void scale(float s) { scale(s, s, s); }
-
- bool degenerate() const { return degenerate_; }
-
- gfx::PointF Intersect(const Edge& e) const;
-
- private:
- float x_;
- float y_;
- float z_;
- bool degenerate_;
- };
-
- LayerQuad(const Edge& left,
- const Edge& top,
- const Edge& right,
- const Edge& bottom);
- explicit LayerQuad(const gfx::QuadF& quad);
-
- LayerQuad(const LayerQuad&) = delete;
- LayerQuad& operator=(const LayerQuad&) = delete;
-
- Edge left() const { return left_; }
- Edge top() const { return top_; }
- Edge right() const { return right_; }
- Edge bottom() const { return bottom_; }
-
- void InflateX(float dx) {
- left_.move_z(dx);
- right_.move_z(dx);
- }
- void InflateY(float dy) {
- top_.move_z(dy);
- bottom_.move_z(dy);
- }
- void Inflate(float d) {
- InflateX(d);
- InflateY(d);
- }
- void InflateAntiAliasingDistance() { Inflate(kAntiAliasingInflateDistance); }
-
- gfx::QuadF ToQuadF() const;
-
- void ToFloatArray(float flattened[12]) const;
-
- private:
- Edge left_;
- Edge top_;
- Edge right_;
- Edge bottom_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_LAYER_QUAD_H_
diff --git a/chromium/components/viz/service/display/layer_quad_unittest.cc b/chromium/components/viz/service/display/layer_quad_unittest.cc
deleted file mode 100644
index 6de10f5b62a..00000000000
--- a/chromium/components/viz/service/display/layer_quad_unittest.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2011 The Chromium 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/viz/service/display/layer_quad.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/quad_f.h"
-
-namespace viz {
-namespace {
-
-TEST(LayerQuadTest, QuadFConversion) {
- gfx::PointF p1(-0.5f, -0.5f);
- gfx::PointF p2(0.5f, -0.5f);
- gfx::PointF p3(0.5f, 0.5f);
- gfx::PointF p4(-0.5f, 0.5f);
-
- gfx::QuadF quad_cw(p1, p2, p3, p4);
- LayerQuad layer_quad_cw(quad_cw);
- EXPECT_EQ(layer_quad_cw.ToQuadF(), quad_cw);
-
- gfx::QuadF quad_ccw(p1, p4, p3, p2);
- LayerQuad layer_quad_ccw(quad_ccw);
- EXPECT_EQ(layer_quad_ccw.ToQuadF(), quad_ccw);
-}
-
-TEST(LayerQuadTest, Inflate) {
- gfx::PointF p1(-0.5f, -0.5f);
- gfx::PointF p2(0.5f, -0.5f);
- gfx::PointF p3(0.5f, 0.5f);
- gfx::PointF p4(-0.5f, 0.5f);
-
- gfx::QuadF quad(p1, p2, p3, p4);
- LayerQuad layer_quad(quad);
- quad.Scale(2.f, 2.f);
- layer_quad.Inflate(0.5f);
- EXPECT_EQ(layer_quad.ToQuadF(), quad);
-}
-
-TEST(LayerQuadTest, Degenerate) {
- gfx::QuadF quad;
- gfx::PointF p1(1.0f, 1.0f);
- gfx::PointF p2(0.0f, 1.0f);
- gfx::PointF p3(1.0f, 0.0f);
- gfx::QuadF triangle(p1, p2, p3, p1);
-
- LayerQuad::Edge e1d(p1, p1);
- LayerQuad::Edge e2d(p2, p2);
- LayerQuad::Edge e2(p1, p2);
- LayerQuad::Edge e3(p2, p3);
- LayerQuad::Edge e4(p3, p1);
- EXPECT_TRUE(e1d.degenerate());
- EXPECT_TRUE(e2d.degenerate());
- EXPECT_FALSE(e2.degenerate());
- EXPECT_FALSE(e3.degenerate());
- EXPECT_FALSE(e4.degenerate());
-
- LayerQuad degenerate_quad(e1d, e2d, e2, e3);
- // With more than one degenerate edge, we expect the quad to be zero.
- EXPECT_EQ(quad, degenerate_quad.ToQuadF());
-
- LayerQuad triangle_quad(e1d, e2, e3, e4);
- // With only one degenerate edge, we expect the quad to be a triangle.
- EXPECT_EQ(triangle, triangle_quad.ToQuadF());
-}
-
-} // namespace
-} // namespace viz
diff --git a/chromium/components/viz/service/display/null_renderer.h b/chromium/components/viz/service/display/null_renderer.h
index fa291a47ae7..99935df341c 100644
--- a/chromium/components/viz/service/display/null_renderer.h
+++ b/chromium/components/viz/service/display/null_renderer.h
@@ -45,7 +45,6 @@ class VIZ_SERVICE_EXPORT NullRenderer : public DirectRenderer {
void DoDrawQuad(const DrawQuad* quad,
const gfx::QuadF* clip_region) override {}
void BeginDrawingFrame() override;
- void FlushOverdrawFeedback(const gfx::Rect& output_rect) override {}
void FinishDrawingFrame() override {}
bool FlippedFramebuffer() const override;
void EnsureScissorTestEnabled() override {}
diff --git a/chromium/components/viz/service/display/output_surface.cc b/chromium/components/viz/service/display/output_surface.cc
index 6088ff13f26..643a191a333 100644
--- a/chromium/components/viz/service/display/output_surface.cc
+++ b/chromium/components/viz/service/display/output_surface.cc
@@ -15,8 +15,6 @@
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/swap_result.h"
@@ -30,14 +28,9 @@ OutputSurface::Capabilities& OutputSurface::Capabilities::operator=(
OutputSurface::OutputSurface(Type type) : type_(type) {}
-OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider)
- : context_provider_(std::move(context_provider)), type_(Type::kOpenGL) {
- DCHECK(context_provider_);
-}
-
OutputSurface::OutputSurface(
std::unique_ptr<SoftwareOutputDevice> software_device)
- : software_device_(std::move(software_device)), type_(Type::kSoftware) {
+ : type_(Type::kSoftware), software_device_(std::move(software_device)) {
DCHECK(software_device_);
}
diff --git a/chromium/components/viz/service/display/output_surface.h b/chromium/components/viz/service/display/output_surface.h
index 9c854b56b21..40a1cc2b7d1 100644
--- a/chromium/components/viz/service/display/output_surface.h
+++ b/chromium/components/viz/service/display/output_surface.h
@@ -12,7 +12,6 @@
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
#include "components/viz/common/display/update_vsync_parameters_callback.h"
-#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/common/gpu/gpu_vsync_callback.h"
#include "components/viz/common/resources/resource_format.h"
#include "components/viz/common/resources/returned_resource.h"
@@ -20,12 +19,12 @@
#include "components/viz/service/display/software_output_device.h"
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/texture_in_use_response.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/ipc/gpu_task_scheduler_helper.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkM44.h"
+#include "ui/gfx/buffer_types.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/overlay_transform.h"
#include "ui/gfx/surface_origin.h"
@@ -77,10 +76,6 @@ class VIZ_SERVICE_EXPORT OutputSurface {
bool uses_default_gl_framebuffer = true;
// Where (0,0) is on this OutputSurface.
gfx::SurfaceOrigin output_surface_origin = gfx::SurfaceOrigin::kBottomLeft;
- // Whether this OutputSurface supports stencil operations or not.
- // Note: HasExternalStencilTest() must return false when an output surface
- // has been configured for stencil usage.
- bool supports_stencil = false;
// Whether this OutputSurface supports post sub buffer or not.
bool supports_post_sub_buffer = false;
// Whether this OutputSurface supports commit overlay planes.
@@ -145,8 +140,6 @@ class VIZ_SERVICE_EXPORT OutputSurface {
// Constructor for skia-based compositing.
explicit OutputSurface(Type type);
- // Constructor for GL-based compositing.
- explicit OutputSurface(scoped_refptr<ContextProvider> context_provider);
// Constructor for software compositing.
explicit OutputSurface(std::unique_ptr<SoftwareOutputDevice> software_device);
@@ -158,11 +151,9 @@ class VIZ_SERVICE_EXPORT OutputSurface {
const Capabilities& capabilities() const { return capabilities_; }
Type type() const { return type_; }
- // Obtain the 3d context or the software device associated with this output
- // surface. Either of these may return a null pointer, but not both.
- // In the event of a lost context, the entire output surface should be
- // recreated.
- ContextProvider* context_provider() const { return context_provider_.get(); }
+ // Obtain the software device associated with this output surface. This will
+ // return non-null for a software output surface and null for skia output
+ // surface.
SoftwareOutputDevice* software_device() const {
return software_device_.get();
}
@@ -183,12 +174,14 @@ class VIZ_SERVICE_EXPORT OutputSurface {
virtual void EnsureBackbuffer() = 0;
virtual void DiscardBackbuffer() = 0;
- // Bind the default framebuffer for drawing to, only valid for GL backed
- // OutputSurfaces.
- virtual void BindFramebuffer() = 0;
-
// Marks that the given rectangle will be drawn to on the default, bound
- // framebuffer. Only valid if |capabilities().supports_dc_layers| is true.
+ // framebuffer. The contents of the framebuffer are undefined after this
+ // command and must be filled in completely before a swap happens. Drawing
+ // outside this rectangle causes undefined behavior.
+ //
+ // Note: This is only valid to call if `capabilities().supports_dc_layers` is
+ // true. It can only be called once per swap and must be called before
+ // drawing to the default framebuffer.
virtual void SetDrawRectangle(const gfx::Rect& rect);
// Enable or disable DC layers. Must be called before DC layers are scheduled.
@@ -198,24 +191,28 @@ class VIZ_SERVICE_EXPORT OutputSurface {
// Returns true if a main image overlay plane should be scheduled.
virtual bool IsDisplayedAsOverlayPlane() const = 0;
- // Get the texture for the main image's overlay.
- virtual unsigned GetOverlayTextureId() const = 0;
-
// Returns the |mailbox| corresponding to the main image's overlay.
virtual gpu::Mailbox GetOverlayMailbox() const;
- virtual void Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) = 0;
-
- virtual bool HasExternalStencilTest() const = 0;
- virtual void ApplyExternalStencil() = 0;
-
- // Gives the GL internal format that should be used for calling CopyTexImage2D
- // when the framebuffer is bound via BindFramebuffer().
- virtual uint32_t GetFramebufferCopyTextureFormat() = 0;
+ // Reshape the output surface.
+ struct ReshapeParams {
+ gfx::Size size;
+ float device_scale_factor = 1.f;
+ gfx::ColorSpace color_space;
+ float sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel;
+ gfx::BufferFormat format = gfx::BufferFormat::RGBX_8888;
+
+ bool operator==(const ReshapeParams& other) const {
+ return size == other.size &&
+ device_scale_factor == other.device_scale_factor &&
+ color_space == other.color_space &&
+ sdr_white_level == other.sdr_white_level;
+ }
+ bool operator!=(const ReshapeParams& other) const {
+ return !(*this == other);
+ }
+ };
+ virtual void Reshape(const ReshapeParams& params) = 0;
// Swaps the current backbuffer to the screen. For successful swaps, the
// implementation must call OutputSurfaceClient::DidReceiveSwapBuffersAck()
@@ -232,14 +229,6 @@ class VIZ_SERVICE_EXPORT OutputSurface {
// TODO(dcastagna): Consider making the following pure virtual.
virtual gfx::Rect GetCurrentFramebufferDamage() const;
- // Updates the GpuFence associated with this surface. The id of a newly
- // created GpuFence is returned, or if an error occurs, or fences are not
- // supported, the special id of 0 (meaning "no fence") is returned. In all
- // cases, any previously associated fence is destroyed. The returned fence id
- // corresponds to the GL id used by the CHROMIUM_gpu_fence GL extension and
- // can be passed directly to any related extension functions.
- virtual unsigned UpdateGpuFence() = 0;
-
// Sets callback to receive updated vsync parameters after SwapBuffers() if
// supported.
virtual void SetUpdateVSyncParametersCallback(
@@ -298,11 +287,10 @@ class VIZ_SERVICE_EXPORT OutputSurface {
protected:
struct OutputSurface::Capabilities capabilities_;
- scoped_refptr<ContextProvider> context_provider_;
- std::unique_ptr<SoftwareOutputDevice> software_device_;
private:
const Type type_;
+ std::unique_ptr<SoftwareOutputDevice> software_device_;
SkM44 color_matrix_;
};
diff --git a/chromium/components/viz/service/display/output_surface_client.h b/chromium/components/viz/service/display/output_surface_client.h
index 3aa54a8e1d1..505beb8c0c0 100644
--- a/chromium/components/viz/service/display/output_surface_client.h
+++ b/chromium/components/viz/service/display/output_surface_client.h
@@ -13,7 +13,6 @@
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/texture_in_use_response.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/gpu_fence_handle.h"
#include "ui/latency/latency_info.h"
@@ -37,11 +36,6 @@ class VIZ_SERVICE_EXPORT OutputSurfaceClient {
// For surfaceless/ozone implementations to create damage for the next frame.
virtual void SetNeedsRedrawRect(const gfx::Rect& damage_rect) = 0;
- // For synchronizing IOSurface use with the macOS WindowServer with
- // GLRenderer.
- virtual void DidReceiveTextureInUseResponses(
- const gpu::TextureInUseResponses& responses) = 0;
-
// For displaying a swapped frame's contents on macOS.
virtual void DidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) = 0;
diff --git a/chromium/components/viz/service/display/overlay_ca_unittest.cc b/chromium/components/viz/service/display/overlay_ca_unittest.cc
index 2a1b231bb45..b8020578253 100644
--- a/chromium/components/viz/service/display/overlay_ca_unittest.cc
+++ b/chromium/components/viz/service/display/overlay_ca_unittest.cc
@@ -24,14 +24,15 @@
#include "components/viz/common/quads/stream_video_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/quads/video_hole_draw_quad.h"
+#include "components/viz/common/quads/yuv_video_draw_quad.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/service/display/ca_layer_overlay.h"
-#include "components/viz/service/display/display_resource_provider_gl.h"
-#include "components/viz/service/display/gl_renderer.h"
+#include "components/viz/service/display/display_resource_provider_skia.h"
#include "components/viz/service/display/output_surface.h"
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "components/viz/service/display/overlay_processor_mac.h"
+#include "components/viz/test/fake_skia_output_surface.h"
#include "components/viz/test/test_context_provider.h"
#include "components/viz/test/test_gles2_interface.h"
#include "gpu/config/gpu_finch_features.h"
@@ -52,45 +53,6 @@ const gfx::PointF kUVBottomRight(1.0f, 1.0f);
const gfx::Rect kRenderPassOutputRect(0, 0, 256, 256);
const gfx::Rect kOverlayDamageRect(0, 0, 100, 100);
-class OverlayOutputSurface : public OutputSurface {
- public:
- explicit OverlayOutputSurface(
- scoped_refptr<TestContextProvider> context_provider)
- : OutputSurface(std::move(context_provider)) {}
-
- // OutputSurface implementation.
- void BindToClient(OutputSurfaceClient* client) override {}
- void EnsureBackbuffer() override {}
- void DiscardBackbuffer() override {}
- void BindFramebuffer() override { bind_framebuffer_count_ += 1; }
- void Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) override {}
- void SwapBuffers(OutputSurfaceFrame frame) override {}
- uint32_t GetFramebufferCopyTextureFormat() override {
- // TestContextProvider has no real framebuffer, just use RGB.
- return GL_RGB;
- }
- bool HasExternalStencilTest() const override { return false; }
- void ApplyExternalStencil() override {}
- bool IsDisplayedAsOverlayPlane() const override { return false; }
- unsigned GetOverlayTextureId() const override { return 10000; }
- unsigned UpdateGpuFence() override { return 0; }
- void SetUpdateVSyncParametersCallback(
- UpdateVSyncParametersCallback callback) override {}
- void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
- gfx::OverlayTransform GetDisplayTransform() override {
- return gfx::OVERLAY_TRANSFORM_NONE;
- }
-
- unsigned bind_framebuffer_count() const { return bind_framebuffer_count_; }
-
- private:
- unsigned bind_framebuffer_count_ = 0;
-};
-
class CATestOverlayProcessor : public OverlayProcessorMac {
public:
CATestOverlayProcessor() : OverlayProcessorMac() {}
@@ -211,13 +173,12 @@ SkM44 GetIdentityColorMatrix() {
class CALayerOverlayTest : public testing::Test {
protected:
void SetUp() override {
- provider_ = TestContextProvider::Create();
- provider_->BindToCurrentThread();
- output_surface_ = std::make_unique<OverlayOutputSurface>(provider_);
- output_surface_->BindToClient(&client_);
+ output_surface_ = FakeSkiaOutputSurface::Create3d();
+ output_surface_->BindToClient(&output_surface_client_);
- resource_provider_ =
- std::make_unique<DisplayResourceProviderGL>(provider_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProviderSkia>();
+ lock_set_for_external_use_.emplace(resource_provider_.get(),
+ output_surface_.get());
child_provider_ = TestContextProvider::Create();
child_provider_->BindToCurrentThread();
@@ -231,15 +192,16 @@ class CALayerOverlayTest : public testing::Test {
child_resource_provider_->ShutdownAndReleaseAllResources();
child_resource_provider_ = nullptr;
child_provider_ = nullptr;
+ lock_set_for_external_use_.reset();
resource_provider_ = nullptr;
output_surface_ = nullptr;
- provider_ = nullptr;
}
- scoped_refptr<TestContextProvider> provider_;
- std::unique_ptr<OverlayOutputSurface> output_surface_;
- cc::FakeOutputSurfaceClient client_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
+ std::unique_ptr<SkiaOutputSurface> output_surface_;
+ cc::FakeOutputSurfaceClient output_surface_client_;
+ std::unique_ptr<DisplayResourceProviderSkia> resource_provider_;
+ absl::optional<DisplayResourceProviderSkia::LockSetForExternalUse>
+ lock_set_for_external_use_;
scoped_refptr<TestContextProvider> child_provider_;
std::unique_ptr<ClientResourceProvider> child_resource_provider_;
std::unique_ptr<CATestOverlayProcessor> overlay_processor_;
@@ -271,7 +233,6 @@ TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) {
EXPECT_EQ(1U, ca_layer_list.size());
gfx::Rect overlay_damage = overlay_processor_->GetAndResetOverlayDamage();
EXPECT_EQ(kRenderPassOutputRect, overlay_damage);
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
}
TEST_F(CALayerOverlayTest, ThreeDTransform) {
@@ -301,7 +262,6 @@ TEST_F(CALayerOverlayTest, ThreeDTransform) {
expected_transform.RotateAboutXAxis(45.f);
gfx::Transform actual_transform(ca_layer_list.back().shared_state->transform);
EXPECT_EQ(expected_transform.ToString(), actual_transform.ToString());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
}
TEST_F(CALayerOverlayTest, AllowContainingClip) {
@@ -325,7 +285,6 @@ TEST_F(CALayerOverlayTest, AllowContainingClip) {
&damage_rect_, &content_bounds_);
EXPECT_EQ(gfx::Rect(), damage_rect_);
EXPECT_EQ(1U, ca_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
}
TEST_F(CALayerOverlayTest, NontrivialClip) {
@@ -351,7 +310,6 @@ TEST_F(CALayerOverlayTest, NontrivialClip) {
EXPECT_EQ(1U, ca_layer_list.size());
EXPECT_EQ(gfx::RectF(64, 64, 128, 128),
ca_layer_list.back().shared_state->clip_rect);
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
}
TEST_F(CALayerOverlayTest, SkipTransparent) {
@@ -375,7 +333,6 @@ TEST_F(CALayerOverlayTest, SkipTransparent) {
&damage_rect_, &content_bounds_);
EXPECT_EQ(gfx::Rect(), damage_rect_);
EXPECT_EQ(0U, ca_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
}
TEST_F(CALayerOverlayTest, SkipNonVisible) {
@@ -399,7 +356,6 @@ TEST_F(CALayerOverlayTest, SkipNonVisible) {
&damage_rect_, &content_bounds_);
EXPECT_EQ(gfx::Rect(), damage_rect_);
EXPECT_EQ(0U, ca_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
}
TEST_F(CALayerOverlayTest, YUVDrawQuadOverlay) {
@@ -484,7 +440,6 @@ TEST_F(CALayerOverlayTest, YUVDrawQuadOverlay) {
&damage_rect_, &content_bounds_);
EXPECT_EQ(gfx::Rect(), damage_rect_);
EXPECT_EQ(0U, ca_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
}
}
diff --git a/chromium/components/viz/service/display/overlay_candidate.cc b/chromium/components/viz/service/display/overlay_candidate.cc
index 41ae9ef7547..c33317bc9d0 100644
--- a/chromium/components/viz/service/display/overlay_candidate.cc
+++ b/chromium/components/viz/service/display/overlay_candidate.cc
@@ -93,39 +93,6 @@ gfx::OverlayTransform GetOverlayTransform(const gfx::Transform& quad_transform,
return gfx::OVERLAY_TRANSFORM_INVALID;
}
-gfx::Rect GetDamageRect(const DrawQuad* quad,
- SurfaceDamageRectList* surface_damage_rect_list) {
- const SharedQuadState* sqs = quad->shared_quad_state;
- auto& transform = sqs->quad_to_target_transform;
- gfx::RectF display_rect = gfx::RectF(quad->rect);
- transform.TransformRect(&display_rect);
- if (!sqs->overlay_damage_index.has_value()) {
- gfx::Rect display_rect_int = gfx::ToRoundedRect(display_rect);
- // This is a special case where an overlay candidate may have damage but it
- // does not have a damage index since it was not the only quad in the
- // original surface. Here the union of all |surface_damage_rect_list| will
- // be in effect the full damage for this display.
- auto full_display_damage = gfx::Rect();
- for (auto& each : *surface_damage_rect_list) {
- full_display_damage.Union(each);
- }
-
- // We limit the damage to the candidates quad rect in question.
- gfx::Rect intersection = display_rect_int;
- intersection.Intersect(full_display_damage);
- return intersection;
- }
-
- size_t overlay_damage_index = sqs->overlay_damage_index.value();
- // Invalid index.
- if (overlay_damage_index >= surface_damage_rect_list->size()) {
- DCHECK(false);
- return gfx::Rect();
- }
-
- return (*surface_damage_rect_list)[overlay_damage_index];
-}
-
} // namespace
OverlayCandidate::OverlayCandidate() = default;
@@ -134,19 +101,13 @@ OverlayCandidate::OverlayCandidate(const OverlayCandidate& other) = default;
OverlayCandidate::~OverlayCandidate() = default;
-// static
-OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
- const SkM44& output_color_matrix,
+OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromDrawQuad(
const DrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate,
- bool is_delegated_context) {
+ OverlayCandidate& candidate) const {
// It is currently not possible to set a color conversion matrix on an HW
// overlay plane.
// TODO(https://crbug.com/792757): Remove this check once the bug is resolved.
- if (output_color_matrix != SkM44())
+ if (*output_color_matrix_ != SkM44())
return CandidateStatus::kFailColorMatrix;
const SharedQuadState* sqs = quad->shared_quad_state;
@@ -154,12 +115,12 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuad(
// We don't support an opacity value different than one for an overlay plane.
// Render pass quads should have their |sqs| opacity integrated directly into
// their final output buffers.
- if (!cc::MathUtil::IsWithinEpsilon(sqs->opacity, 1.0f) &&
- !is_delegated_context) {
+ if (!is_delegated_context_ &&
+ !cc::MathUtil::IsWithinEpsilon(sqs->opacity, 1.0f)) {
return CandidateStatus::kFailOpacity;
}
- candidate->opacity = sqs->opacity;
- candidate->rounded_corners = sqs->mask_filter_info.rounded_corner_bounds();
+ candidate.opacity = sqs->opacity;
+ candidate.rounded_corners = sqs->mask_filter_info.rounded_corner_bounds();
// We support only kSrc (no blending) and kSrcOver (blending with premul).
if (!(sqs->blend_mode == SkBlendMode::kSrc ||
@@ -167,43 +128,33 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuad(
return CandidateStatus::kFailBlending;
}
- candidate->requires_overlay =
- OverlayCandidate::RequiresOverlay(quad);
- candidate->overlay_damage_index =
- sqs->overlay_damage_index.value_or(kInvalidDamageIndex);
+ candidate.requires_overlay = OverlayCandidate::RequiresOverlay(quad);
+ candidate.overlay_damage_index =
+ sqs->overlay_damage_index.value_or(OverlayCandidate::kInvalidDamageIndex);
switch (quad->material) {
case DrawQuad::Material::kTextureContent:
- return FromTextureQuad(resource_provider, surface_damage_rect_list,
- TextureDrawQuad::MaterialCast(quad), primary_rect,
- candidate, is_delegated_context);
+ return FromTextureQuad(TextureDrawQuad::MaterialCast(quad), candidate);
case DrawQuad::Material::kVideoHole:
- return FromVideoHoleQuad(resource_provider, surface_damage_rect_list,
- VideoHoleDrawQuad::MaterialCast(quad),
+ return FromVideoHoleQuad(VideoHoleDrawQuad::MaterialCast(quad),
candidate);
case DrawQuad::Material::kStreamVideoContent:
- return FromStreamVideoQuad(resource_provider, surface_damage_rect_list,
- StreamVideoDrawQuad::MaterialCast(quad),
- candidate, is_delegated_context, primary_rect);
+ return FromStreamVideoQuad(StreamVideoDrawQuad::MaterialCast(quad),
+ candidate);
case DrawQuad::Material::kSolidColor:
- if (!is_delegated_context)
+ if (!is_delegated_context_)
return CandidateStatus::kFailQuadNotSupported;
- return candidate->FromSolidColorQuad(
- resource_provider, surface_damage_rect_list,
- SolidColorDrawQuad::MaterialCast(quad), primary_rect, candidate);
+ return FromSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad),
+ candidate);
case DrawQuad::Material::kAggregatedRenderPass:
- if (!is_delegated_context)
+ if (!is_delegated_context_)
return CandidateStatus::kFailQuadNotSupported;
- return candidate->FromAggregateQuad(
- resource_provider, surface_damage_rect_list,
- AggregatedRenderPassDrawQuad::MaterialCast(quad), primary_rect,
- candidate);
+ return FromAggregateQuad(AggregatedRenderPassDrawQuad::MaterialCast(quad),
+ candidate);
case DrawQuad::Material::kTiledContent:
- if (!is_delegated_context)
+ if (!is_delegated_context_)
return CandidateStatus::kFailQuadNotSupported;
- return candidate->FromTileQuad(
- resource_provider, surface_damage_rect_list,
- TileDrawQuad::MaterialCast(quad), primary_rect, candidate);
+ return FromTileQuad(TileDrawQuad::MaterialCast(quad), candidate);
default:
break;
}
@@ -218,8 +169,8 @@ bool OverlayCandidate::IsInvisibleQuad(const DrawQuad* quad) {
return true;
if (quad->material != DrawQuad::Material::kSolidColor)
return false;
- const SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color;
- const float alpha = (SkColorGetA(color) * (1.f / 255.f)) * opacity;
+ const float alpha =
+ SolidColorDrawQuad::MaterialCast(quad)->color.fA * opacity;
return quad->ShouldDrawWithBlending() &&
cc::MathUtil::IsWithinEpsilon(alpha, 0.f);
}
@@ -247,17 +198,52 @@ bool OverlayCandidate::IsOccluded(const OverlayCandidate& candidate,
return false;
}
-// static
-int OverlayCandidate::EstimateVisibleDamage(
+OverlayCandidateFactory::OverlayCandidateFactory(
+ const AggregatedRenderPass* render_pass,
+ DisplayResourceProvider* resource_provider,
+ const SurfaceDamageRectList* surface_damage_rect_list,
+ const SkM44* output_color_matrix,
+ const gfx::RectF primary_rect,
+ bool is_delegated_context)
+ : render_pass_(render_pass),
+ resource_provider_(resource_provider),
+ surface_damage_rect_list_(surface_damage_rect_list),
+ output_color_matrix_(output_color_matrix),
+ primary_rect_(primary_rect),
+ is_delegated_context_(is_delegated_context) {
+ // TODO(crbug.com/1323002): Replace this set with a simple ordered linear
+ // search when this bug is resolved.
+ base::flat_set<size_t> indices_with_quad_damage;
+ for (auto* sqs : render_pass_->shared_quad_state_list) {
+ // If a |sqs| has a damage index it will only be associated with a single
+ // draw quad.
+ if (sqs->overlay_damage_index.has_value()) {
+ indices_with_quad_damage.insert(sqs->overlay_damage_index.value());
+ }
+ }
+
+ for (size_t i = 0; i < (*surface_damage_rect_list_).size(); i++) {
+ // Add this damage only if it does not correspond to a specific quad.
+ // Ideally any damage that we might want to separate out (think overlays)
+ // will not end up in this |unassigned_surface_damage_| rect.
+ if (!indices_with_quad_damage.contains(i)) {
+ unassigned_surface_damage_.Union((*surface_damage_rect_list_)[i]);
+ }
+ }
+}
+
+OverlayCandidateFactory::~OverlayCandidateFactory() = default;
+
+float OverlayCandidateFactory::EstimateVisibleDamage(
const DrawQuad* quad,
- SurfaceDamageRectList* surface_damage_rect_list,
+ const OverlayCandidate& candidate,
QuadList::ConstIterator quad_list_begin,
- QuadList::ConstIterator quad_list_end) {
- gfx::Rect quad_damage = GetDamageRect(quad, surface_damage_rect_list);
- int occluded_damage_estimate_total = 0;
+ QuadList::ConstIterator quad_list_end) const {
+ gfx::Rect quad_damage = gfx::ToEnclosingRect(GetDamageRect(quad, candidate));
+ float occluded_damage_estimate_total = 0.f;
for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end;
++overlap_iter) {
- gfx::Rect overlap_rect = gfx::ToRoundedRect(cc::MathUtil::MapClippedRect(
+ gfx::Rect overlap_rect = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect(
overlap_iter->shared_quad_state->quad_to_target_transform,
gfx::RectF(overlap_iter->rect)));
@@ -273,7 +259,7 @@ int OverlayCandidate::EstimateVisibleDamage(
// reason why this computation is an estimate and why we have the max clamping
// below.
return std::max(
- 0, quad_damage.size().GetArea() - occluded_damage_estimate_total);
+ 0.f, quad_damage.size().GetArea() - occluded_damage_estimate_total);
}
// static
@@ -295,13 +281,12 @@ bool OverlayCandidate::RequiresOverlay(const DrawQuad* quad) {
}
}
-// static
-bool OverlayCandidate::IsOccludedByFilteredQuad(
+bool OverlayCandidateFactory::IsOccludedByFilteredQuad(
const OverlayCandidate& candidate,
QuadList::ConstIterator quad_list_begin,
QuadList::ConstIterator quad_list_end,
const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
- render_pass_backdrop_filters) {
+ render_pass_backdrop_filters) const {
for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end;
++overlap_iter) {
if (overlap_iter->material == DrawQuad::Material::kAggregatedRenderPass) {
@@ -320,29 +305,24 @@ bool OverlayCandidate::IsOccludedByFilteredQuad(
return false;
}
-// static
-OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuadResource(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
+OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromDrawQuadResource(
const DrawQuad* quad,
ResourceId resource_id,
bool y_flipped,
- OverlayCandidate* candidate,
- bool is_delegated_context,
- const gfx::RectF& primary_rect) {
+ OverlayCandidate& candidate) const {
if (resource_id != kInvalidResourceId &&
- !resource_provider->IsOverlayCandidate(resource_id))
+ !resource_provider_->IsOverlayCandidate(resource_id))
return CandidateStatus::kFailNotOverlay;
if (quad->visible_rect.IsEmpty())
return CandidateStatus::kFailVisible;
if (resource_id != kInvalidResourceId) {
- candidate->format = resource_provider->GetBufferFormat(resource_id);
- candidate->color_space = resource_provider->GetColorSpace(resource_id);
- candidate->hdr_metadata = resource_provider->GetHDRMetadata(resource_id);
+ candidate.format = resource_provider_->GetBufferFormat(resource_id);
+ candidate.color_space = resource_provider_->GetColorSpace(resource_id);
+ candidate.hdr_metadata = resource_provider_->GetHDRMetadata(resource_id);
- if (!base::Contains(kOverlayFormats, candidate->format))
+ if (!base::Contains(kOverlayFormats, candidate.format))
return CandidateStatus::kFailBufferFormat;
}
@@ -352,28 +332,28 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuadResource(
GetOverlayTransform(sqs->quad_to_target_transform, y_flipped);
if (overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID)
return CandidateStatus::kFailNotAxisAligned;
- candidate->transform = overlay_transform;
+ candidate.transform = overlay_transform;
auto& transform = sqs->quad_to_target_transform;
- candidate->display_rect = gfx::RectF(quad->rect);
- transform.TransformRect(&candidate->display_rect);
+ candidate.display_rect = gfx::RectF(quad->rect);
+ transform.TransformRect(&candidate.display_rect);
- candidate->clip_rect = sqs->clip_rect;
- candidate->is_opaque =
+ candidate.clip_rect = sqs->clip_rect;
+ candidate.is_opaque =
!quad->ShouldDrawWithBlendingForReasonOtherThanMaskFilter();
- candidate->has_mask_filter = !sqs->mask_filter_info.IsEmpty();
+ candidate.has_mask_filter = !sqs->mask_filter_info.IsEmpty();
if (resource_id != kInvalidResourceId) {
- candidate->resource_size_in_pixels =
- resource_provider->GetResourceBackedSize(resource_id);
+ candidate.resource_size_in_pixels =
+ resource_provider_->GetResourceBackedSize(resource_id);
} else {
- candidate->resource_size_in_pixels =
- gfx::Size(candidate->display_rect.size().width(),
- candidate->display_rect.size().height());
+ candidate.resource_size_in_pixels =
+ gfx::Size(candidate.display_rect.size().width(),
+ candidate.display_rect.size().height());
}
- AssignDamage(quad, surface_damage_rect_list, candidate);
- candidate->resource_id = resource_id;
+ AssignDamage(quad, candidate);
+ candidate.resource_id = resource_id;
struct TrackingIdData {
gfx::Rect rect;
@@ -382,118 +362,106 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromDrawQuadResource(
TrackingIdData track_data{quad->rect, FrameSinkId()};
if (resource_id != kInvalidResourceId) {
- candidate->mailbox = resource_provider->GetMailbox(resource_id);
+ candidate.mailbox = resource_provider_->GetMailbox(resource_id);
track_data.frame_sink_id =
- resource_provider->GetSurfaceId(resource_id).frame_sink_id();
+ resource_provider_->GetSurfaceId(resource_id).frame_sink_id();
}
// Delegated compositing does not yet support |clip_rect| so it is applied
// here to the |display_rect| and |uv_rect| directly.
- if (is_delegated_context) {
- if (candidate->clip_rect.has_value())
- ApplyClip(candidate, gfx::RectF(*candidate->clip_rect));
+ if (is_delegated_context_) {
+ if (candidate.clip_rect.has_value())
+ OverlayCandidate::ApplyClip(candidate, gfx::RectF(*candidate.clip_rect));
+
+ if (quad->visible_rect != quad->rect) {
+ auto visible_rect = gfx::RectF(quad->visible_rect);
+ transform.TransformRect(&visible_rect);
+ OverlayCandidate::ApplyClip(candidate, gfx::RectF(visible_rect));
+ }
// TODO(https://crbug.com/1300552) : Tile quads can overlay other quads and
// the window by one pixel. Exo does not yet clip these quads so we need to
// clip here with the |primary_rect|.
- ApplyClip(candidate, primary_rect);
+ OverlayCandidate::ApplyClip(candidate, primary_rect_);
+
+ if (candidate.display_rect.IsEmpty())
+ return CandidateStatus::kFailVisible;
}
- candidate->tracking_id = base::Hash(&track_data, sizeof(track_data));
+ candidate.tracking_id = base::Hash(&track_data, sizeof(track_data));
return CandidateStatus::kSuccess;
}
-// static
-OverlayCandidate::CandidateStatus OverlayCandidate::FromAggregateQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
+OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromAggregateQuad(
const AggregatedRenderPassDrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate) {
- auto rtn = FromDrawQuadResource(resource_provider, surface_damage_rect_list,
- quad, kInvalidResourceId, false, candidate,
- true, primary_rect);
+ OverlayCandidate& candidate) const {
+ auto rtn = FromDrawQuadResource(quad, kInvalidResourceId, false, candidate);
if (rtn == CandidateStatus::kSuccess) {
- candidate->rpdq = quad;
+ candidate.rpdq = quad;
}
return rtn;
}
-// static
-OverlayCandidate::CandidateStatus OverlayCandidate::FromSolidColorQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
+OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromSolidColorQuad(
const SolidColorDrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate) {
- auto rtn = FromDrawQuadResource(resource_provider, surface_damage_rect_list,
- quad, kInvalidResourceId, false, candidate,
- true, primary_rect);
+ OverlayCandidate& candidate) const {
+ auto rtn = FromDrawQuadResource(quad, kInvalidResourceId, false, candidate);
if (rtn == CandidateStatus::kSuccess) {
- candidate->solid_color = quad->color;
+ // TODO(crbug/1308932) remove toSkColor and make all SkColor4f
+ candidate.color = quad->color.toSkColor();
+ // Mark this candidate a solid color as the |color| member can be either a
+ // background of the overlay or a color of the solid color quad.
+ candidate.is_solid_color = true;
}
return rtn;
}
-// static
-// For VideoHoleDrawQuad, only calculate geometry information
-// and put it in the |candidate|.
-OverlayCandidate::CandidateStatus OverlayCandidate::FromVideoHoleQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
+// For VideoHoleDrawQuad, only calculate geometry information and put it in the
+// |candidate|.
+OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromVideoHoleQuad(
const VideoHoleDrawQuad* quad,
- OverlayCandidate* candidate) {
+ OverlayCandidate& candidate) const {
gfx::OverlayTransform overlay_transform = GetOverlayTransform(
quad->shared_quad_state->quad_to_target_transform, false);
if (overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID)
return CandidateStatus::kFailNotAxisAligned;
auto& transform = quad->shared_quad_state->quad_to_target_transform;
- candidate->display_rect = gfx::RectF(quad->rect);
- transform.TransformRect(&candidate->display_rect);
- candidate->transform = overlay_transform;
- candidate->is_opaque =
+ candidate.display_rect = gfx::RectF(quad->rect);
+ transform.TransformRect(&candidate.display_rect);
+ candidate.transform = overlay_transform;
+ candidate.is_opaque =
!quad->ShouldDrawWithBlendingForReasonOtherThanMaskFilter();
- candidate->has_mask_filter =
+ candidate.has_mask_filter =
!quad->shared_quad_state->mask_filter_info.IsEmpty();
- AssignDamage(quad, surface_damage_rect_list, candidate);
- candidate->tracking_id = base::FastHash(quad->overlay_plane_id.AsBytes());
+ AssignDamage(quad, candidate);
+ candidate.tracking_id = base::FastHash(quad->overlay_plane_id.AsBytes());
return CandidateStatus::kSuccess;
}
-OverlayCandidate::CandidateStatus OverlayCandidate::FromTileQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
+OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromTileQuad(
const TileDrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate) {
+ OverlayCandidate& candidate) const {
if (quad->nearest_neighbor)
return CandidateStatus::kFailNearFilter;
- candidate->resource_size_in_pixels =
- resource_provider->GetResourceBackedSize(quad->resource_id());
- candidate->uv_rect = gfx::ScaleRect(
- quad->tex_coord_rect, 1.f / candidate->resource_size_in_pixels.width(),
- 1.f / candidate->resource_size_in_pixels.height());
+ candidate.resource_size_in_pixels =
+ resource_provider_->GetResourceBackedSize(quad->resource_id());
+ candidate.uv_rect = gfx::ScaleRect(
+ quad->tex_coord_rect, 1.f / candidate.resource_size_in_pixels.width(),
+ 1.f / candidate.resource_size_in_pixels.height());
- auto rtn = FromDrawQuadResource(resource_provider, surface_damage_rect_list,
- quad, quad->resource_id(), false, candidate,
- true, primary_rect);
+ auto rtn = FromDrawQuadResource(quad, quad->resource_id(), false, candidate);
return rtn;
}
-// static
-OverlayCandidate::CandidateStatus OverlayCandidate::FromTextureQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
+OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromTextureQuad(
const TextureDrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate,
- bool is_delegated_context) {
- if (!is_delegated_context &&
+ OverlayCandidate& candidate) const {
+ if (!is_delegated_context_ &&
quad->overlay_priority_hint == OverlayPriority::kLow) {
// For current implementation low priority means this does not promote to
// overlay.
@@ -503,91 +471,88 @@ OverlayCandidate::CandidateStatus OverlayCandidate::FromTextureQuad(
if (quad->nearest_neighbor)
return CandidateStatus::kFailNearFilter;
- if (quad->background_color != SK_ColorTRANSPARENT &&
- (quad->background_color != SK_ColorBLACK ||
- quad->ShouldDrawWithBlending()))
- return CandidateStatus::kFailBlending;
+ if (quad->background_color != SkColors::kTransparent &&
+ (quad->background_color != SkColors::kBlack ||
+ quad->ShouldDrawWithBlending())) {
+ // This path can also be used by other platforms like Ash/Chrome, which does
+ // not support overlays with background color. Only LaCros/Wayland supports
+ // that.
+ if (!is_delegated_context_)
+ return CandidateStatus::kFailBlending;
+ // TODO(crbug/1308932) remove toSkColor and make all SkColor4f
+ candidate.color = quad->background_color.toSkColor();
+ }
- candidate->uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
+ candidate.uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
- auto rtn = FromDrawQuadResource(
- resource_provider, surface_damage_rect_list, quad, quad->resource_id(),
- quad->y_flipped, candidate, is_delegated_context, primary_rect);
+ auto rtn = FromDrawQuadResource(quad, quad->resource_id(), quad->y_flipped,
+ candidate);
if (rtn == CandidateStatus::kSuccess) {
// Only handle clip rect for required overlays
- if (!is_delegated_context && candidate->requires_overlay)
- HandleClipAndSubsampling(candidate, primary_rect);
+ if (!is_delegated_context_ && candidate.requires_overlay)
+ HandleClipAndSubsampling(candidate);
// Texture quads for UI elements like scroll bars have empty
// |size_in_pixels| as 'set_resource_size_in_pixels' is not called as these
// quads are not intended to become overlays.
if (!quad->resource_size_in_pixels().IsEmpty())
- candidate->priority_hint = gfx::OverlayPriorityHint::kRegular;
+ candidate.priority_hint = gfx::OverlayPriorityHint::kRegular;
}
return rtn;
}
-// static
-OverlayCandidate::CandidateStatus OverlayCandidate::FromStreamVideoQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
+OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromStreamVideoQuad(
const StreamVideoDrawQuad* quad,
- OverlayCandidate* candidate,
- bool is_delegated_context,
- const gfx::RectF& primary_rect) {
- auto rtn = FromDrawQuadResource(resource_provider, surface_damage_rect_list,
- quad, quad->resource_id(), false, candidate,
- is_delegated_context, primary_rect);
+ OverlayCandidate& candidate) const {
+ auto rtn = FromDrawQuadResource(quad, quad->resource_id(), false, candidate);
if (rtn == CandidateStatus::kSuccess) {
- candidate->resource_size_in_pixels = quad->resource_size_in_pixels();
- candidate->uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
+ candidate.resource_size_in_pixels = quad->resource_size_in_pixels();
+ candidate.uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
#if BUILDFLAG(IS_ANDROID)
- candidate->is_backed_by_surface_texture =
- resource_provider->IsBackedBySurfaceTexture(quad->resource_id());
+ candidate.is_backed_by_surface_texture =
+ resource_provider_->IsBackedBySurfaceTexture(quad->resource_id());
#endif
}
return rtn;
}
-// static
-void OverlayCandidate::HandleClipAndSubsampling(
- OverlayCandidate* candidate,
- const gfx::RectF& primary_rect) {
+void OverlayCandidateFactory::HandleClipAndSubsampling(
+ OverlayCandidate& candidate) const {
// The purpose of this is to enable overlays that are required (i.e. protected
// content) to be able to be shown in all cases. This will allow them to pass
// the clipping check and also the 2x alignment requirement for subsampling in
// the Intel DRM driver. This should not be used in cases where the surface
// will not always be promoted to an overlay as it will lead to shifting of
// the content when it switches between composition and overlay.
- if (!candidate->clip_rect)
+ if (!candidate.clip_rect)
return;
// Make sure it's in a format we can deal with, we only support YUV and P010.
- if (candidate->format != gfx::BufferFormat::YUV_420_BIPLANAR &&
- candidate->format != gfx::BufferFormat::P010) {
+ if (candidate.format != gfx::BufferFormat::YUV_420_BIPLANAR &&
+ candidate.format != gfx::BufferFormat::P010) {
return;
}
// Clip the clip rect to the primary plane. An overlay will only be shown on
// a single display, so we want to perform our calculations within the bounds
// of that display.
- if (!primary_rect.IsEmpty())
- candidate->clip_rect->Intersect(gfx::ToNearestRect(primary_rect));
+ if (!primary_rect_.IsEmpty())
+ candidate.clip_rect->Intersect(gfx::ToNearestRect(primary_rect_));
// Calculate |uv_rect| of |clip_rect| in |display_rect|
gfx::RectF uv_rect = cc::MathUtil::ScaleRectProportional(
- candidate->uv_rect, candidate->display_rect,
- gfx::RectF(*candidate->clip_rect));
+ candidate.uv_rect, candidate.display_rect,
+ gfx::RectF(*candidate.clip_rect));
// In case that |uv_rect| of candidate is not (0, 0, 1, 1)
- candidate->uv_rect.Intersect(uv_rect);
+ candidate.uv_rect.Intersect(uv_rect);
// Update |display_rect| to avoid unexpected scaling and the candidate should
// not be regarded as clippped after this.
- candidate->display_rect.Intersect(gfx::RectF(*candidate->clip_rect));
- candidate->clip_rect.reset();
- gfx::Rect rounded_display_rect = gfx::ToRoundedRect(candidate->display_rect);
- candidate->display_rect.SetRect(
+ candidate.display_rect.Intersect(gfx::RectF(*candidate.clip_rect));
+ candidate.clip_rect.reset();
+ gfx::Rect rounded_display_rect = gfx::ToRoundedRect(candidate.display_rect);
+ candidate.display_rect.SetRect(
rounded_display_rect.x(), rounded_display_rect.y(),
rounded_display_rect.width(), rounded_display_rect.height());
@@ -596,8 +561,8 @@ void OverlayCandidate::HandleClipAndSubsampling(
// Get the rect for the source coordinates.
gfx::RectF src_rect = gfx::ScaleRect(
- candidate->uv_rect, candidate->resource_size_in_pixels.width(),
- candidate->resource_size_in_pixels.height());
+ candidate.uv_rect, candidate.resource_size_in_pixels.width(),
+ candidate.resource_size_in_pixels.height());
// Make it an integral multiple of the subsampling factor.
auto subsample_round = [](float val) {
constexpr int kSubsamplingFactor = 2;
@@ -609,19 +574,16 @@ void OverlayCandidate::HandleClipAndSubsampling(
src_rect.set_width(subsample_round(src_rect.width()));
src_rect.set_height(subsample_round(src_rect.height()));
// Scale it back into UV space and set it in the candidate.
- candidate->uv_rect = gfx::ScaleRect(
- src_rect, 1.0f / candidate->resource_size_in_pixels.width(),
- 1.0f / candidate->resource_size_in_pixels.height());
+ candidate.uv_rect =
+ gfx::ScaleRect(src_rect, 1.0f / candidate.resource_size_in_pixels.width(),
+ 1.0f / candidate.resource_size_in_pixels.height());
}
-// static
-void OverlayCandidate::AssignDamage(
- const DrawQuad* quad,
- SurfaceDamageRectList* surface_damage_rect_list,
- OverlayCandidate* candidate) {
+void OverlayCandidateFactory::AssignDamage(const DrawQuad* quad,
+ OverlayCandidate& candidate) const {
auto& transform = quad->shared_quad_state->quad_to_target_transform;
- const auto damage_rect = GetDamageRect(quad, surface_damage_rect_list);
- auto transformed_damage = gfx::RectF(damage_rect);
+ auto damage_rect = GetDamageRect(quad, candidate);
+ auto transformed_damage = damage_rect;
gfx::Transform inv;
if (transform.GetInverse(&inv)) {
inv.TransformRect(&transformed_damage);
@@ -640,37 +602,65 @@ void OverlayCandidate::AssignDamage(
// The normalization above is not enough if the |uv_rect| is not 0,0-1x1.
// This is because texture uvs can effectively magnify damage.
- if (!candidate->uv_rect.IsEmpty()) {
- transformed_damage.Scale(candidate->uv_rect.width(),
- candidate->uv_rect.height());
- transformed_damage.Offset(candidate->uv_rect.OffsetFromOrigin());
+ if (!candidate.uv_rect.IsEmpty()) {
+ transformed_damage.Scale(candidate.uv_rect.width(),
+ candidate.uv_rect.height());
+ transformed_damage.Offset(candidate.uv_rect.OffsetFromOrigin());
}
// Buffer damage is in texels not UVs so scale by resource size.
- transformed_damage.Scale(candidate->resource_size_in_pixels.width(),
- candidate->resource_size_in_pixels.height());
+ transformed_damage.Scale(candidate.resource_size_in_pixels.width(),
+ candidate.resource_size_in_pixels.height());
} else {
// If not invertible, set to full damage.
// TODO(https://crbug.com/1279965): |resource_size_in_pixels| might not be
// properly initialized at this stage.
transformed_damage =
- gfx::RectF(gfx::SizeF(candidate->resource_size_in_pixels));
+ gfx::RectF(gfx::SizeF(candidate.resource_size_in_pixels));
}
// For underlays the function 'EstimateVisibleDamage()' is called to update
// |damage_area_estimate| to more accurately reflect the actual visible
// damage.
- candidate->damage_area_estimate = damage_rect.size().GetArea();
- candidate->damage_rect = transformed_damage;
+ candidate.damage_area_estimate = damage_rect.size().GetArea();
+ candidate.damage_rect = transformed_damage;
}
-void OverlayCandidate::ApplyClip(OverlayCandidate* candidate,
+// static
+void OverlayCandidate::ApplyClip(OverlayCandidate& candidate,
const gfx::RectF& clip_rect) {
- gfx::RectF intersect_clip_display = clip_rect;
- intersect_clip_display.Intersect(candidate->display_rect);
- gfx::RectF uv_rect = cc::MathUtil::ScaleRectProportional(
- candidate->uv_rect, candidate->display_rect, intersect_clip_display);
- candidate->display_rect = intersect_clip_display;
- candidate->uv_rect = uv_rect;
+ if (!clip_rect.Contains(candidate.display_rect)) {
+ gfx::RectF intersect_clip_display = clip_rect;
+ intersect_clip_display.Intersect(candidate.display_rect);
+ gfx::RectF uv_rect = cc::MathUtil::ScaleRectProportional(
+ candidate.uv_rect, candidate.display_rect, intersect_clip_display);
+ candidate.display_rect = intersect_clip_display;
+ candidate.uv_rect = uv_rect;
+ }
+}
+
+gfx::RectF OverlayCandidateFactory::GetDamageRect(
+ const DrawQuad* quad,
+ const OverlayCandidate& candidate) const {
+ const SharedQuadState* sqs = quad->shared_quad_state;
+ if (!sqs->overlay_damage_index.has_value()) {
+ // This is a special case where an overlay candidate may have damage but it
+ // does not have a damage index since it was not the only quad in the
+ // original surface. Here the |unassigned_surface_damage_| will contain all
+ // unassigned damage and we use it to conservatively estimate the damage for
+ // this quad. We limit the damage to the candidates quad rect in question.
+ gfx::RectF intersection = candidate.display_rect;
+ intersection.Intersect(gfx::RectF(unassigned_surface_damage_));
+ return intersection;
+ }
+
+ size_t overlay_damage_index = sqs->overlay_damage_index.value();
+ // Invalid index.
+ if (overlay_damage_index >= surface_damage_rect_list_->size()) {
+ DCHECK(false);
+ return gfx::RectF();
+ }
+
+ return gfx::RectF((*surface_damage_rect_list_)[overlay_damage_index]);
}
} // namespace viz
diff --git a/chromium/components/viz/service/display/overlay_candidate.h b/chromium/components/viz/service/display/overlay_candidate.h
index 2585bd1f4a7..a205623d385 100644
--- a/chromium/components/viz/service/display/overlay_candidate.h
+++ b/chromium/components/viz/service/display/overlay_candidate.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/containers/flat_map.h"
+#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "components/viz/common/quads/aggregated_render_pass.h"
#include "components/viz/common/quads/tile_draw_quad.h"
@@ -60,17 +61,6 @@ class VIZ_SERVICE_EXPORT OverlayCandidate {
using TrackingId = uint32_t;
static constexpr TrackingId kDefaultTrackingId{0};
- // Returns true and fills in |candidate| if |draw_quad| is of a known quad
- // type and contains an overlayable resource. |primary_rect| can be empty in
- // the case of a null primary plane.
- static CandidateStatus FromDrawQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
- const SkM44& output_color_matrix,
- const DrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate,
- bool is_delegated_context = false);
// Returns true if |quad| will not block quads underneath from becoming
// an overlay.
static bool IsInvisibleQuad(const DrawQuad* quad);
@@ -81,25 +71,11 @@ class VIZ_SERVICE_EXPORT OverlayCandidate {
QuadList::ConstIterator quad_list_begin,
QuadList::ConstIterator quad_list_end);
- // Returns an estimate of this |quad|'s actual visible damage area. This
- // visible damage is computed by combining from input
- // |surface_damage_rect_list| with the occluding rects in the quad_list.
- // This is an estimate since the occluded damage area is calculated on a per
- // quad basis.
- static int EstimateVisibleDamage(
- const DrawQuad* quad,
- SurfaceDamageRectList* surface_damage_rect_list,
- QuadList::ConstIterator quad_list_begin,
- QuadList::ConstIterator quad_list_end);
-
- // Returns true if any of the quads in the list given by |quad_list_begin|
- // and |quad_list_end| have a filter associated and occlude |candidate|.
- static bool IsOccludedByFilteredQuad(
- const OverlayCandidate& candidate,
- QuadList::ConstIterator quad_list_begin,
- QuadList::ConstIterator quad_list_end,
- const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
- render_pass_backdrop_filters);
+ // Modifies the |candidate|'s |display_rect| to be clipped within |clip_rect|.
+ // This function will also update the |uv_rect| based on what clipping was
+ // applied to |display_rect|.
+ static void ApplyClip(OverlayCandidate& candidate,
+ const gfx::RectF& clip_rect);
// Returns true if the |quad| cannot be displayed on the main plane. This is
// used in conjuction with protected content that can't be GPU composited and
@@ -163,7 +139,7 @@ class VIZ_SERVICE_EXPORT OverlayCandidate {
// The total area in square pixels of damage for this candidate's quad. This
// is an estimate when 'EstimateOccludedDamage' function is used.
- int damage_area_estimate = 0;
+ float damage_area_estimate = 0.f;
// Damage in buffer space (extents bound by |resource_size_in_pixels|).
gfx::RectF damage_rect;
@@ -175,8 +151,12 @@ class VIZ_SERVICE_EXPORT OverlayCandidate {
// Is true if an HW overlay is required for the quad content.
bool requires_overlay = false;
- // for solid color quads only
- absl::optional<SkColor> solid_color;
+ // Represents either a background of this overlay candidate or a color of a
+ // solid color quad, which can be checked via the |is_solid_color|.
+ absl::optional<SkColor> color;
+
+ // Helps to identify whether this is a solid color quad or not.
+ bool is_solid_color = false;
// If |rpdq| is present, then the renderer must draw the filter effects and
// copy the result into the buffer backing of a render pass.
@@ -207,72 +187,103 @@ class VIZ_SERVICE_EXPORT OverlayCandidate {
// surface and have the same |DrawQuad::rect| they will have the same
// |tracking_id|.
TrackingId tracking_id = kDefaultTrackingId;
-
- private:
- static CandidateStatus FromDrawQuadResource(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
- const DrawQuad* quad,
- ResourceId resource_id,
- bool y_flipped,
- OverlayCandidate* candidate,
- bool is_delegated_context,
- const gfx::RectF& primary_rect);
-
- static CandidateStatus FromTextureQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
- const TextureDrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate,
- bool is_delegated_context);
-
- static CandidateStatus FromTileQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
- const TileDrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate);
-
- static CandidateStatus FromAggregateQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
- const AggregatedRenderPassDrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate);
-
- static CandidateStatus FromSolidColorQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
- const SolidColorDrawQuad* quad,
- const gfx::RectF& primary_rect,
- OverlayCandidate* candidate);
-
- static CandidateStatus FromStreamVideoQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
- const StreamVideoDrawQuad* quad,
- OverlayCandidate* candidate,
- bool is_delegated_context,
- const gfx::RectF& primary_rect);
-
- static CandidateStatus FromVideoHoleQuad(
- DisplayResourceProvider* resource_provider,
- SurfaceDamageRectList* surface_damage_rect_list,
- const VideoHoleDrawQuad* quad,
- OverlayCandidate* candidate);
- static void HandleClipAndSubsampling(OverlayCandidate* candidate,
- const gfx::RectF& primary_rect);
- static void AssignDamage(const DrawQuad* quad,
- SurfaceDamageRectList* surface_damage_rect_list,
- OverlayCandidate* candidate);
-
- static void ApplyClip(OverlayCandidate* candidate,
- const gfx::RectF& clip_rect);
};
using OverlayCandidateList = std::vector<OverlayCandidate>;
+// This is a factory to help with the creation of |OverlayCandidates|. On
+// construction, this factory captures the required objects to create candidates
+// from a draw quad. Common computations for all possible candidates can be
+// made at construction time. This class is const after construction and not
+// copy/moveable to avoid capture ownership issues.
+class VIZ_SERVICE_EXPORT OverlayCandidateFactory {
+ public:
+ using CandidateStatus = OverlayCandidate::CandidateStatus;
+
+ OverlayCandidateFactory(const AggregatedRenderPass* render_pass,
+ DisplayResourceProvider* resource_provider,
+ const SurfaceDamageRectList* surface_damage_rect_list,
+ const SkM44* output_color_matrix,
+ const gfx::RectF primary_rect,
+ bool is_delegated_context = false);
+
+ OverlayCandidateFactory(const OverlayCandidateFactory&) = delete;
+ OverlayCandidateFactory& operator=(const OverlayCandidateFactory&) = delete;
+
+ ~OverlayCandidateFactory();
+
+ // Returns |kSuccess| and fills in |candidate| if |draw_quad| is of a known
+ // quad type and contains an overlayable resource. |primary_rect| can be empty
+ // in the case of a null primary plane. |candidate| is expected to be a
+ // freshly constructed |OverlayCandidate| object.
+ CandidateStatus FromDrawQuad(const DrawQuad* quad,
+ OverlayCandidate& candidate) const;
+
+ // Returns an estimate of this |quad|'s actual visible damage area as float
+ // pixels squared. This visible damage is computed by combining from input
+ // |surface_damage_rect_list_| with the occluding rects in the quad_list. This
+ // is an estimate since the occluded damage area is calculated on a per quad
+ // basis. The |quad_list_begin| and |quad_list_end| provide the range of valid
+ // occluders of this |candidate|.
+ // TODO(petermcneeley): Can we replace this with |visible_rect| in |DrawQuad|?
+ float EstimateVisibleDamage(const DrawQuad* quad,
+ const OverlayCandidate& candidate,
+ QuadList::ConstIterator quad_list_begin,
+ QuadList::ConstIterator quad_list_end) const;
+
+ // Returns true if any of the quads in the list given by |quad_list_begin|
+ // and |quad_list_end| have an associated filter and occlude |candidate|.
+ bool IsOccludedByFilteredQuad(
+ const OverlayCandidate& candidate,
+ QuadList::ConstIterator quad_list_begin,
+ QuadList::ConstIterator quad_list_end,
+ const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
+ render_pass_backdrop_filters) const;
+
+ private:
+ CandidateStatus FromDrawQuadResource(const DrawQuad* quad,
+ ResourceId resource_id,
+ bool y_flipped,
+ OverlayCandidate& candidate) const;
+
+ CandidateStatus FromTextureQuad(const TextureDrawQuad* quad,
+ OverlayCandidate& candidate) const;
+
+ CandidateStatus FromTileQuad(const TileDrawQuad* quad,
+ OverlayCandidate& candidate) const;
+
+ CandidateStatus FromAggregateQuad(const AggregatedRenderPassDrawQuad* quad,
+ OverlayCandidate& candidate) const;
+
+ CandidateStatus FromSolidColorQuad(const SolidColorDrawQuad* quad,
+ OverlayCandidate& candidate) const;
+
+ CandidateStatus FromStreamVideoQuad(const StreamVideoDrawQuad* quad,
+ OverlayCandidate& candidate) const;
+
+ CandidateStatus FromVideoHoleQuad(const VideoHoleDrawQuad* quad,
+ OverlayCandidate& candidate) const;
+
+ void HandleClipAndSubsampling(OverlayCandidate& candidate) const;
+
+ void AssignDamage(const DrawQuad* quad, OverlayCandidate& candidate) const;
+
+ // Damage returned from this function is in target content space.
+ gfx::RectF GetDamageRect(const DrawQuad* quad,
+ const OverlayCandidate& candidate) const;
+
+ raw_ptr<const AggregatedRenderPass> render_pass_;
+ raw_ptr<DisplayResourceProvider> resource_provider_;
+ raw_ptr<const SurfaceDamageRectList> surface_damage_rect_list_;
+ raw_ptr<const SkM44> output_color_matrix_;
+ const gfx::RectF primary_rect_;
+ bool is_delegated_context_;
+
+ // The union of all surface damages that are not specifically assigned to a
+ // draw quad.
+ gfx::Rect unassigned_surface_damage_;
+};
+
} // namespace viz
#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_OVERLAY_CANDIDATE_H_
diff --git a/chromium/components/viz/service/display/overlay_dc_unittest.cc b/chromium/components/viz/service/display/overlay_dc_unittest.cc
index d49769e18ef..8f7fd805e74 100644
--- a/chromium/components/viz/service/display/overlay_dc_unittest.cc
+++ b/chromium/components/viz/service/display/overlay_dc_unittest.cc
@@ -4,6 +4,7 @@
#include <stddef.h>
+#include <memory>
#include <utility>
#include <vector>
@@ -22,14 +23,15 @@
#include "components/viz/common/quads/stream_video_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/quads/video_hole_draw_quad.h"
+#include "components/viz/common/quads/yuv_video_draw_quad.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/service/display/dc_layer_overlay.h"
-#include "components/viz/service/display/display_resource_provider_gl.h"
-#include "components/viz/service/display/gl_renderer.h"
+#include "components/viz/service/display/display_resource_provider_skia.h"
#include "components/viz/service/display/output_surface.h"
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "components/viz/service/display/overlay_processor_win.h"
+#include "components/viz/test/fake_skia_output_surface.h"
#include "components/viz/test/test_context_provider.h"
#include "components/viz/test/test_gles2_interface.h"
#include "gpu/config/gpu_finch_features.h"
@@ -48,47 +50,21 @@ namespace {
const gfx::Rect kOverlayRect(0, 0, 256, 256);
const gfx::Rect kOverlayBottomRightRect(128, 128, 128, 128);
-class OverlayOutputSurface : public OutputSurface {
+class MockDCLayerOutputSurface : public FakeSkiaOutputSurface {
public:
- explicit OverlayOutputSurface(
- scoped_refptr<TestContextProvider> context_provider)
- : OutputSurface(std::move(context_provider)) {
+ static std::unique_ptr<MockDCLayerOutputSurface> Create() {
+ auto provider = TestContextProvider::Create();
+ provider->BindToCurrentThread();
+ return std::make_unique<MockDCLayerOutputSurface>(std::move(provider));
+ }
+
+ explicit MockDCLayerOutputSurface(scoped_refptr<ContextProvider> provider)
+ : FakeSkiaOutputSurface(std::move(provider)) {
capabilities_.supports_dc_layers = true;
}
// OutputSurface implementation.
- void BindToClient(OutputSurfaceClient* client) override {}
- void EnsureBackbuffer() override {}
- void DiscardBackbuffer() override {}
- void BindFramebuffer() override { bind_framebuffer_count_ += 1; }
- void SetDrawRectangle(const gfx::Rect& rect) override {}
MOCK_METHOD1(SetEnableDCLayers, void(bool));
- void Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) override {}
- void SwapBuffers(OutputSurfaceFrame frame) override {}
- uint32_t GetFramebufferCopyTextureFormat() override {
- // TestContextProvider has no real framebuffer, just use RGB.
- return GL_RGB;
- }
- bool HasExternalStencilTest() const override { return false; }
- void ApplyExternalStencil() override {}
- bool IsDisplayedAsOverlayPlane() const override { return false; }
- unsigned GetOverlayTextureId() const override { return 10000; }
- unsigned UpdateGpuFence() override { return 0; }
- void SetUpdateVSyncParametersCallback(
- UpdateVSyncParametersCallback callback) override {}
- void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
- gfx::OverlayTransform GetDisplayTransform() override {
- return gfx::OVERLAY_TRANSFORM_NONE;
- }
-
- unsigned bind_framebuffer_count() const { return bind_framebuffer_count_; }
-
- private:
- unsigned bind_framebuffer_count_ = 0;
};
class DCTestOverlayProcessor : public OverlayProcessorWin {
@@ -223,13 +199,12 @@ SkM44 GetIdentityColorMatrix() {
class DCLayerOverlayTest : public testing::Test {
protected:
void SetUp() override {
- provider_ = TestContextProvider::Create();
- provider_->BindToCurrentThread();
- output_surface_ = std::make_unique<OverlayOutputSurface>(provider_);
- output_surface_->BindToClient(&client_);
+ output_surface_ = MockDCLayerOutputSurface::Create();
+ output_surface_->BindToClient(&output_surface_client_);
- resource_provider_ =
- std::make_unique<DisplayResourceProviderGL>(provider_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProviderSkia>();
+ lock_set_for_external_use_.emplace(resource_provider_.get(),
+ output_surface_.get());
child_provider_ = TestContextProvider::Create();
child_provider_->BindToCurrentThread();
@@ -247,15 +222,16 @@ class DCLayerOverlayTest : public testing::Test {
child_resource_provider_->ShutdownAndReleaseAllResources();
child_resource_provider_ = nullptr;
child_provider_ = nullptr;
+ lock_set_for_external_use_.reset();
resource_provider_ = nullptr;
output_surface_ = nullptr;
- provider_ = nullptr;
}
- scoped_refptr<TestContextProvider> provider_;
- std::unique_ptr<OverlayOutputSurface> output_surface_;
- cc::FakeOutputSurfaceClient client_;
- std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
+ std::unique_ptr<MockDCLayerOutputSurface> output_surface_;
+ cc::FakeOutputSurfaceClient output_surface_client_;
+ std::unique_ptr<DisplayResourceProviderSkia> resource_provider_;
+ absl::optional<DisplayResourceProviderSkia::LockSetForExternalUse>
+ lock_set_for_external_use_;
scoped_refptr<TestContextProvider> child_provider_;
std::unique_ptr<ClientResourceProvider> child_resource_provider_;
std::unique_ptr<OverlayProcessorWin> overlay_processor_;
@@ -310,7 +286,6 @@ TEST_F(DCLayerOverlayTest, Occluded) {
&damage_rect_, &content_bounds_);
EXPECT_EQ(2U, dc_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(-1, dc_layer_list.front().z_order);
EXPECT_EQ(-2, dc_layer_list.back().z_order);
// Entire underlay rect must be redrawn.
@@ -361,7 +336,6 @@ TEST_F(DCLayerOverlayTest, Occluded) {
&damage_rect_, &content_bounds_);
EXPECT_EQ(2U, dc_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(-1, dc_layer_list.front().z_order);
EXPECT_EQ(-2, dc_layer_list.back().z_order);
@@ -412,7 +386,6 @@ TEST_F(DCLayerOverlayTest, DamageRectWithoutVideoDamage) {
std::move(surface_damage_rect_list), nullptr, &dc_layer_list,
&damage_rect_, &content_bounds_);
EXPECT_EQ(1U, dc_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(-1, dc_layer_list.back().z_order);
// All rects must be redrawn at the first frame.
EXPECT_EQ(gfx::Rect(0, 0, 230, 230), damage_rect_);
@@ -456,7 +429,6 @@ TEST_F(DCLayerOverlayTest, DamageRectWithoutVideoDamage) {
std::move(surface_damage_rect_list), nullptr, &dc_layer_list,
&damage_rect_, &content_bounds_);
EXPECT_EQ(1U, dc_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(-1, dc_layer_list.back().z_order);
// Only the non-overlay damaged rect need to be drawn by the gl compositor
EXPECT_EQ(gfx::Rect(210, 210, 20, 20), damage_rect_);
@@ -486,7 +458,6 @@ TEST_F(DCLayerOverlayTest, DamageRect) {
std::move(surface_damage_rect_list), nullptr, &dc_layer_list,
&damage_rect_, &content_bounds_);
EXPECT_EQ(1U, dc_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(1, dc_layer_list.back().z_order);
// Damage rect should be unchanged on initial frame because of resize, but
// should be empty on the second frame because everything was put in a
@@ -613,7 +584,6 @@ TEST_F(DCLayerOverlayTest, UnderlayDamageRectWithQuadOnTopUnchanged) {
std::move(surface_damage_rect_list), nullptr, &dc_layer_list,
&damage_rect_, &content_bounds_);
EXPECT_EQ(1U, dc_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(-1, dc_layer_list.back().z_order);
// Damage rect should be unchanged on initial frame, but should be reduced
// to the size of quad on top, and empty on the third frame.
@@ -675,7 +645,7 @@ TEST_F(DCLayerOverlayTest, RoundedCorners) {
// rounded corner mask filter for the replaced solid quad.
EXPECT_EQ(replaced_sqs->blend_mode, SkBlendMode::kDstOut);
EXPECT_EQ(SolidColorDrawQuad::MaterialCast(replaced_quad)->color,
- SK_ColorBLACK);
+ SkColors::kBlack);
EXPECT_TRUE(replaced_sqs->mask_filter_info.HasRoundedCorners());
// The whole frame is damaged.
@@ -733,7 +703,7 @@ TEST_F(DCLayerOverlayTest, RoundedCorners) {
// rounded corner mask filter for the replaced solid quad.
EXPECT_EQ(replaced_sqs->blend_mode, SkBlendMode::kDstOut);
EXPECT_EQ(SolidColorDrawQuad::MaterialCast(replaced_quad)->color,
- SK_ColorBLACK);
+ SkColors::kBlack);
EXPECT_TRUE(replaced_sqs->mask_filter_info.HasRoundedCorners());
// Only the UI is damaged.
@@ -791,7 +761,7 @@ TEST_F(DCLayerOverlayTest, RoundedCorners) {
// solid quad.
EXPECT_EQ(replaced_sqs->blend_mode, SkBlendMode::kDstOut);
EXPECT_EQ(SolidColorDrawQuad::MaterialCast(replaced_quad)->color,
- SK_ColorBLACK);
+ SkColors::kBlack);
EXPECT_TRUE(replaced_sqs->mask_filter_info.HasRoundedCorners());
// Zero root damage rect.
@@ -839,7 +809,6 @@ TEST_F(DCLayerOverlayTest, MultipleYUVOverlay) {
// Skip overlays.
EXPECT_EQ(0U, dc_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(gfx::Rect(0, 0, 220, 220), damage_rect_);
// Check whether all 3 quads including two YUV quads are still in the render
@@ -887,7 +856,6 @@ TEST_F(DCLayerOverlayTest, SetEnableDCLayers) {
&damage_rect_, &content_bounds_);
EXPECT_EQ(1U, dc_layer_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(1, dc_layer_list.back().z_order);
EXPECT_EQ(damage_rect_, expected_damage);
@@ -932,7 +900,6 @@ TEST_F(DCLayerOverlayTest, SetEnableDCLayers) {
&damage_rect_, &content_bounds_);
EXPECT_EQ(0u, dc_layer_list.size());
- EXPECT_EQ(0u, output_surface_->bind_framebuffer_count());
EXPECT_EQ(damage_rect_, expected_damage);
Mock::VerifyAndClearExpectations(output_surface_.get());
diff --git a/chromium/components/viz/service/display/overlay_processor_delegated.cc b/chromium/components/viz/service/display/overlay_processor_delegated.cc
index ca130dab48b..b124668c2fa 100644
--- a/chromium/components/viz/service/display/overlay_processor_delegated.cc
+++ b/chromium/components/viz/service/display/overlay_processor_delegated.cc
@@ -100,9 +100,11 @@ OverlayProcessorDelegated::OverlayProcessorDelegated(
OverlayProcessorDelegated::~OverlayProcessorDelegated() = default;
+DBG_FLAG_FBOOL("delegated.enable.quad_split", quad_split)
+
bool OverlayProcessorDelegated::DisableSplittingQuads() const {
- // If there is quads to split these will happen delegee side.
- return true;
+ // This determines if we will split quads on delegation or on delegee side.
+ return !quad_split();
}
constexpr size_t kTooManyQuads = 64;
@@ -132,6 +134,11 @@ bool OverlayProcessorDelegated::AttemptWithStrategies(
!render_pass_backdrop_filters.empty())
return false;
+ OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane),
+ is_delegated_context);
+
std::vector<QuadList::Iterator> candidate_quads;
int num_quads_skipped = 0;
for (auto it = quad_list->begin(); it != quad_list->end(); ++it) {
@@ -144,10 +151,7 @@ bool OverlayProcessorDelegated::AttemptWithStrategies(
gfx::Vector2dF(display_rect.origin().x(), display_rect.origin().y()),
base::StringPrintf("m=%d rid=%d", static_cast<int>(it->material),
it->resources.begin()->value()));
- auto candidate_status = OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix, *it,
- GetPrimaryPlaneDisplayRect(primary_plane), &candidate,
- is_delegated_context);
+ auto candidate_status = candidate_factory.FromDrawQuad(*it, candidate);
if (candidate_status == OverlayCandidate::CandidateStatus::kSuccess) {
if (it->material == DrawQuad::Material::kSolidColor) {
DBG_DRAW_RECT("delegated.overlay.color", candidate.display_rect);
@@ -196,6 +200,8 @@ bool OverlayProcessorDelegated::AttemptWithStrategies(
candidates->clear();
delegated_status_ = DelegationStatus::kCompositedCheckOverlayFail;
DBG_DRAW_RECT("delegated.handled.failed", each.display_rect);
+ DBG_LOG("delegated.handled.failed", "Handled failed %s",
+ each.display_rect.ToString().c_str());
return false;
}
}
diff --git a/chromium/components/viz/service/display/overlay_processor_interface.h b/chromium/components/viz/service/display/overlay_processor_interface.h
index 22cb824f084..2d4c5ff010c 100644
--- a/chromium/components/viz/service/display/overlay_processor_interface.h
+++ b/chromium/components/viz/service/display/overlay_processor_interface.h
@@ -33,6 +33,10 @@ namespace cc {
class DisplayResourceProvider;
}
+namespace gpu {
+class SharedImageInterface;
+}
+
namespace viz {
struct DebugRendererSettings;
class OutputSurface;
@@ -95,9 +99,6 @@ class VIZ_SERVICE_EXPORT OverlayProcessorInterface {
// Opacity of the overlay independent of buffer alpha. When rendered:
// src-alpha = |opacity| * buffer-component-alpha.
float opacity;
- // TODO(weiliangc): Should be replaced by SharedImage mailbox.
- // Gpu fence to wait for before overlay is ready for display.
- unsigned gpu_fence_id;
// Mailbox corresponding to the buffer backing the primary plane.
gpu::Mailbox mailbox;
// Hints for overlay prioritization.
diff --git a/chromium/components/viz/service/display/overlay_processor_ozone.cc b/chromium/components/viz/service/display/overlay_processor_ozone.cc
index ead3abb6834..3ae5364b310 100644
--- a/chromium/components/viz/service/display/overlay_processor_ozone.cc
+++ b/chromium/components/viz/service/display/overlay_processor_ozone.cc
@@ -61,6 +61,9 @@ void ConvertToOzoneOverlaySurface(
ozone_candidate->requires_overlay = overlay_candidate.requires_overlay;
ozone_candidate->priority_hint = overlay_candidate.priority_hint;
ozone_candidate->rounded_corners = overlay_candidate.rounded_corners;
+ // That can be a solid color quad.
+ if (!overlay_candidate.is_solid_color)
+ ozone_candidate->background_color = overlay_candidate.color;
}
uint32_t MailboxToUInt32(const gpu::Mailbox& mailbox) {
@@ -68,13 +71,6 @@ uint32_t MailboxToUInt32(const gpu::Mailbox& mailbox) {
(mailbox.name[2] << 8) + mailbox.name[3];
}
-void ReportSharedImageExists(bool exists) {
- UMA_HISTOGRAM_BOOLEAN(
- "Compositing.Display.OverlayProcessorOzone."
- "SharedImageExists",
- exists);
-}
-
#if BUILDFLAG(IS_CHROMEOS_ASH)
bool AllowColorSpaceCombination(
const gfx::ColorSpace& source_color_space,
@@ -132,7 +128,6 @@ OverlayProcessorOzone::OverlayProcessorOzone(
NOTREACHED();
}
}
- MaybeObserveHardwareCapabilities();
}
OverlayProcessorOzone::~OverlayProcessorOzone() = default;
@@ -148,6 +143,8 @@ bool OverlayProcessorOzone::NeedsSurfaceDamageRectList() const {
void OverlayProcessorOzone::CheckOverlaySupportImpl(
const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
OverlayCandidateList* surfaces) {
+ MaybeObserveHardwareCapabilities();
+
auto full_size = surfaces->size();
if (primary_plane)
full_size += 1;
@@ -256,27 +253,42 @@ void OverlayProcessorOzone::CheckOverlaySupportImpl(
}
void OverlayProcessorOzone::MaybeObserveHardwareCapabilities() {
+ if (tried_observing_hardware_capabilities_) {
+ return;
+ }
+ tried_observing_hardware_capabilities_ = true;
+
// HardwareCapabilities isn't necessary unless attempting multiple overlays.
if (max_overlays_config_ <= 1) {
return;
}
- overlay_candidates_->ObserveHardwareCapabilities(
- base::BindRepeating(&OverlayProcessorOzone::ReceiveHardwareCapabilities,
- weak_ptr_factory_.GetWeakPtr()));
+ if (overlay_candidates_) {
+ overlay_candidates_->ObserveHardwareCapabilities(
+ base::BindRepeating(&OverlayProcessorOzone::ReceiveHardwareCapabilities,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
}
void OverlayProcessorOzone::ReceiveHardwareCapabilities(
ui::HardwareCapabilities hardware_capabilities) {
- // Subtract 1 because one of these overlay capable planes will be needed for
- // the primary plane.
- int max_overlays_supported =
- hardware_capabilities.num_overlay_capable_planes - 1;
- max_overlays_considered_ =
- std::min(max_overlays_supported, max_overlays_config_);
-
- UMA_HISTOGRAM_COUNTS_100(
- "Compositing.Display.OverlayProcessorOzone.MaxOverlaysSupported",
- max_overlays_supported);
+ UMA_HISTOGRAM_BOOLEAN(
+ "Compositing.Display.OverlayProcessorOzone.HardwareCapabilitiesIsValid",
+ hardware_capabilities.is_valid);
+ if (hardware_capabilities.is_valid) {
+ // Subtract 1 because one of these overlay capable planes will be needed for
+ // the primary plane.
+ int max_overlays_supported =
+ hardware_capabilities.num_overlay_capable_planes - 1;
+ max_overlays_considered_ =
+ std::min(max_overlays_supported, max_overlays_config_);
+
+ UMA_HISTOGRAM_COUNTS_100(
+ "Compositing.Display.OverlayProcessorOzone.MaxPlanesSupported",
+ hardware_capabilities.num_overlay_capable_planes);
+ } else {
+ // Default to attempting 1 overlay if we get an invalid response.
+ max_overlays_considered_ = 1;
+ }
// Different hardware capabilities may mean a different result for a specific
// combination of overlays, so clear this cache.
@@ -300,11 +312,6 @@ bool OverlayProcessorOzone::SetNativePixmapForCandidate(
bool is_primary) {
DCHECK(shared_image_interface_);
- UMA_HISTOGRAM_BOOLEAN(
- "Compositing.Display.OverlayProcessorOzone."
- "IsCandidateSharedImage",
- mailbox.IsSharedImage());
-
if (!mailbox.IsSharedImage())
return false;
@@ -318,10 +325,8 @@ bool OverlayProcessorOzone::SetNativePixmapForCandidate(
// candidate. We will try again next frame.
DLOG(ERROR) << "Unable to find the NativePixmap corresponding to the "
"overlay candidate";
- ReportSharedImageExists(false);
return false;
}
- ReportSharedImageExists(true);
if (is_primary && (candidate->buffer_size != native_pixmap->GetBufferSize() ||
candidate->format != native_pixmap->GetBufferFormat())) {
diff --git a/chromium/components/viz/service/display/overlay_processor_ozone.h b/chromium/components/viz/service/display/overlay_processor_ozone.h
index 5809388e329..b0ecacb1bb9 100644
--- a/chromium/components/viz/service/display/overlay_processor_ozone.h
+++ b/chromium/components/viz/service/display/overlay_processor_ozone.h
@@ -56,9 +56,11 @@ class VIZ_SERVICE_EXPORT OverlayProcessorOzone
const gpu::Mailbox& mailbox,
bool is_primary);
+ bool tried_observing_hardware_capabilities_ = false;
std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates_;
const std::vector<OverlayStrategy> available_strategies_;
gpu::SharedImageInterface* const shared_image_interface_;
+
base::WeakPtrFactory<OverlayProcessorOzone> weak_ptr_factory_{this};
};
} // namespace viz
diff --git a/chromium/components/viz/service/display/overlay_processor_ozone_unittest.cc b/chromium/components/viz/service/display/overlay_processor_ozone_unittest.cc
index b8b5d5d914b..d8c9251261a 100644
--- a/chromium/components/viz/service/display/overlay_processor_ozone_unittest.cc
+++ b/chromium/components/viz/service/display/overlay_processor_ozone_unittest.cc
@@ -261,22 +261,29 @@ class TestOverlayProcessorOzone : public OverlayProcessorOzone {
};
TEST(OverlayProcessorOzoneTest, ObserveHardwareCapabilites) {
+ OverlayCandidateList candidates;
// Enable 4 overlays
const std::vector<base::test::ScopedFeatureList::FeatureAndParams>
feature_and_params_list = {{features::kEnableOverlayPrioritization, {}},
{features::kUseMultipleOverlays,
{{features::kMaxOverlaysParam, "4"}}}};
- base::test::ScopedFeatureList features;
- features.InitWithFeaturesAndParameters(feature_and_params_list, {});
+ base::test::ScopedFeatureList scoped_features;
+ scoped_features.InitWithFeaturesAndParameters(feature_and_params_list, {});
+ // When overlay prioritization is explicitly disabled (Lacros) we should
+ // skip multiple overlays tests.
+ if (!features::IsOverlayPrioritizationEnabled()) {
+ GTEST_SKIP();
+ }
auto fake_candidates_unique = std::make_unique<FakeOverlayCandidatesOzone>();
auto* fake_candidates = fake_candidates_unique.get();
+ TestOverlayProcessorOzone processor(std::move(fake_candidates_unique), {},
+ nullptr);
// No receive_callback yet.
EXPECT_TRUE(fake_candidates->receive_callback().is_null());
- TestOverlayProcessorOzone processor(std::move(fake_candidates_unique), {},
- nullptr);
+ processor.CheckOverlaySupport(nullptr, &candidates);
// Receive callback is set.
EXPECT_FALSE(fake_candidates->receive_callback().is_null());
@@ -284,32 +291,44 @@ TEST(OverlayProcessorOzoneTest, ObserveHardwareCapabilites) {
EXPECT_EQ(processor.MaxOverlaysConsidered(), 1);
ui::HardwareCapabilities hc;
+ hc.is_valid = true;
hc.num_overlay_capable_planes = 6;
fake_candidates->receive_callback().Run(hc);
// Uses max_overlays_config_ = 4.
EXPECT_EQ(processor.MaxOverlaysConsidered(), 4);
+ hc.is_valid = true;
hc.num_overlay_capable_planes = 4;
fake_candidates->receive_callback().Run(hc);
// Uses (num_overlay_capable_planes - 1) = 3.
EXPECT_EQ(processor.MaxOverlaysConsidered(), 3);
+
+ hc.is_valid = false;
+ hc.num_overlay_capable_planes = 0;
+ fake_candidates->receive_callback().Run(hc);
+
+ // Defaults to 1 overlay when receiving an invalid response.
+ EXPECT_EQ(processor.MaxOverlaysConsidered(), 1);
}
TEST(OverlayProcessorOzoneTest, NoObserveHardwareCapabilites) {
+ OverlayCandidateList candidates;
// Multiple overlays disabled.
- base::test::ScopedFeatureList features;
- features.InitAndDisableFeature(features::kUseMultipleOverlays);
+ base::test::ScopedFeatureList scoped_features;
+ scoped_features.InitAndDisableFeature(features::kUseMultipleOverlays);
auto fake_candidates_unique = std::make_unique<FakeOverlayCandidatesOzone>();
auto* fake_candidates = fake_candidates_unique.get();
+ OverlayProcessorOzone processor(std::move(fake_candidates_unique), {},
+ nullptr);
+
// No receive_callback yet.
EXPECT_TRUE(fake_candidates->receive_callback().is_null());
- TestOverlayProcessorOzone processor(std::move(fake_candidates_unique), {},
- nullptr);
+ processor.CheckOverlaySupport(nullptr, &candidates);
// Receive callback is still unset because multiple overlays is disabled.
EXPECT_TRUE(fake_candidates->receive_callback().is_null());
diff --git a/chromium/components/viz/service/display/overlay_processor_using_strategy.cc b/chromium/components/viz/service/display/overlay_processor_using_strategy.cc
index e85914bee01..b42bc066367 100644
--- a/chromium/components/viz/service/display/overlay_processor_using_strategy.cc
+++ b/chromium/components/viz/service/display/overlay_processor_using_strategy.cc
@@ -47,7 +47,7 @@ namespace {
// or why we aren't.
enum class AttemptingMultipleOverlays {
kYes = 0,
- kNoTooFewMaxOverlaysConsidered = 1,
+ kNoFeatureDisabled = 1,
kNoRequiredOverlay = 2,
kNoUnsupportedStrategy = 3,
kMaxValue = kNoUnsupportedStrategy,
@@ -56,10 +56,10 @@ enum class AttemptingMultipleOverlays {
constexpr char kShouldAttemptMultipleOverlaysHistogramName[] =
"Compositing.Display.OverlayProcessorUsingStrategy."
"ShouldAttemptMultipleOverlays";
-constexpr char kNumOverlaysAttemptedHistogramName[] =
- "Compositing.Display.OverlayProcessorUsingStrategy.NumOverlaysAttempted";
constexpr char kNumOverlaysPromotedHistogramName[] =
"Compositing.Display.OverlayProcessorUsingStrategy.NumOverlaysPromoted";
+constexpr char kNumOverlaysAttemptedHistogramName[] =
+ "Compositing.Display.OverlayProcessorUsingStrategy.NumOverlaysAttempted";
constexpr char kNumOverlaysFailedHistogramName[] =
"Compositing.Display.OverlayProcessorUsingStrategy.NumOverlaysFailed";
@@ -161,6 +161,10 @@ void OverlayProcessorUsingStrategy::ProcessForOverlays(
auto* render_pass = render_passes->back().get();
bool success = false;
+ UMA_HISTOGRAM_COUNTS_1000(
+ "Compositing.Display.OverlayProcessorUsingStrategy.NumQuadsConsidered",
+ render_pass->quad_list.size());
+
DBG_DRAW_RECT("overlay.incoming.damage", (*damage_rect));
for (auto&& each : surface_damage_rect_list) {
DBG_DRAW_RECT("overlay.surface.damage", each);
@@ -185,6 +189,8 @@ void OverlayProcessorUsingStrategy::ProcessForOverlays(
LogCheckOverlaySupportMetrics();
DCHECK(candidates->empty() || success);
+ UMA_HISTOGRAM_COUNTS_100(kNumOverlaysPromotedHistogramName,
+ candidates->size());
UpdateOverlayStatusMap(*candidates);
UpdateDamageRect(surface_damage_rect_list, *damage_rect);
@@ -413,7 +419,7 @@ void OverlayProcessorUsingStrategy::UpdateDamageRect(
const auto& status = it.second;
if (status.plane_z_order != 0) {
RecordOverlayDamageRectHistograms(status.plane_z_order > 0,
- status.damage_area_estimate != 0,
+ status.damage_area_estimate != 0.f,
damage_rect.IsEmpty());
}
}
@@ -496,11 +502,11 @@ void OverlayProcessorUsingStrategy::SortProposedOverlayCandidatesPrioritized(
// for low latency surfaces (inking like in the google keeps application).
const bool force_update = it->candidate.overlay_damage_index !=
OverlayCandidate::kInvalidDamageIndex &&
- it->candidate.damage_area_estimate != 0;
- track_data.AddRecord(
- frame_sequence_number_,
- static_cast<float>(it->candidate.damage_area_estimate) / display_area,
- it->candidate.resource_id, tracker_config_, force_update);
+ it->candidate.damage_area_estimate != 0.f;
+ track_data.AddRecord(frame_sequence_number_,
+ it->candidate.damage_area_estimate / display_area,
+ it->candidate.resource_id, tracker_config_,
+ force_update);
// Here a series of criteria are considered for wholesale rejection of a
// candidate. The rational for rejection is usually power improvements but
// this can indirectly reallocate limited overlay resources to another
@@ -585,6 +591,11 @@ bool OverlayProcessorUsingStrategy::AttemptWithStrategiesPrioritized(
num_proposed_pre_sort);
SortProposedOverlayCandidatesPrioritized(&proposed_candidates);
+ if (proposed_candidates.size() == 0) {
+ LogStrategyEnumUMA(num_proposed_pre_sort != 0
+ ? OverlayStrategy::kNoStrategyFailMin
+ : OverlayStrategy::kNoStrategyUsed);
+ }
if (ShouldAttemptMultipleOverlays(proposed_candidates)) {
auto* render_pass = render_pass_list->back().get();
@@ -665,11 +676,7 @@ bool OverlayProcessorUsingStrategy::AttemptWithStrategiesPrioritized(
}
RegisterOverlayRequirement(has_required_overlay);
- if (proposed_candidates.size() == 0) {
- LogStrategyEnumUMA(num_proposed_pre_sort != 0
- ? OverlayStrategy::kNoStrategyFailMin
- : OverlayStrategy::kNoStrategyUsed);
- } else {
+ if (proposed_candidates.size() != 0) {
LogStrategyEnumUMA(OverlayStrategy::kNoStrategyAllFail);
}
OnOverlaySwitchUMA(ProposedCandidateKey());
@@ -678,10 +685,9 @@ bool OverlayProcessorUsingStrategy::AttemptWithStrategiesPrioritized(
bool OverlayProcessorUsingStrategy::ShouldAttemptMultipleOverlays(
const std::vector<OverlayProposedCandidate>& sorted_candidates) {
- if (max_overlays_considered_ <= 1) {
- UMA_HISTOGRAM_ENUMERATION(
- kShouldAttemptMultipleOverlaysHistogramName,
- AttemptingMultipleOverlays::kNoTooFewMaxOverlaysConsidered);
+ if (max_overlays_config_ <= 1) {
+ UMA_HISTOGRAM_ENUMERATION(kShouldAttemptMultipleOverlaysHistogramName,
+ AttemptingMultipleOverlays::kNoFeatureDisabled);
return false;
}
@@ -718,6 +724,7 @@ bool OverlayProcessorUsingStrategy::AttemptMultipleOverlays(
OverlayCandidateList& candidates) {
if (sorted_candidates.empty()) {
UMA_HISTOGRAM_COUNTS_100(kNumOverlaysAttemptedHistogramName, 0);
+ UMA_HISTOGRAM_COUNTS_100(kNumOverlaysFailedHistogramName, 0);
return false;
}
@@ -796,12 +803,11 @@ bool OverlayProcessorUsingStrategy::AttemptMultipleOverlays(
UMA_HISTOGRAM_COUNTS_100(kNumOverlaysAttemptedHistogramName,
num_overlays_attempted);
- UMA_HISTOGRAM_COUNTS_100(kNumOverlaysPromotedHistogramName,
- num_overlays_promoted);
UMA_HISTOGRAM_COUNTS_100(kNumOverlaysFailedHistogramName,
num_overlays_attempted - num_overlays_promoted);
if (candidates.empty()) {
+ LogStrategyEnumUMA(OverlayStrategy::kNoStrategyAllFail);
return false;
}
@@ -825,6 +831,7 @@ bool OverlayProcessorUsingStrategy::AttemptMultipleOverlays(
// Commit successful candidates.
for (auto& test_candidate : test_candidates) {
test_candidate.strategy->CommitCandidate(test_candidate, render_pass);
+ LogStrategyEnumUMA(test_candidate.strategy->GetUMAEnum());
}
return true;
diff --git a/chromium/components/viz/service/display/overlay_processor_using_strategy.h b/chromium/components/viz/service/display/overlay_processor_using_strategy.h
index 0a76a92ba9f..e355e7e624f 100644
--- a/chromium/components/viz/service/display/overlay_processor_using_strategy.h
+++ b/chromium/components/viz/service/display/overlay_processor_using_strategy.h
@@ -154,7 +154,7 @@ class VIZ_SERVICE_EXPORT OverlayProcessorUsingStrategy
gfx::Rect overlay_rect;
gfx::RectF damage_rect;
uint32_t damage_index;
- int damage_area_estimate;
+ float damage_area_estimate;
bool has_mask_filter;
int plane_z_order;
bool is_underlay;
diff --git a/chromium/components/viz/service/display/overlay_strategy_fullscreen.cc b/chromium/components/viz/service/display/overlay_strategy_fullscreen.cc
index 9deafb078e0..8899c3e3b57 100644
--- a/chromium/components/viz/service/display/overlay_strategy_fullscreen.cc
+++ b/chromium/components/viz/service/display/overlay_strategy_fullscreen.cc
@@ -51,10 +51,11 @@ bool OverlayStrategyFullscreen::Attempt(
return false;
OverlayCandidate candidate;
- if (OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- quad, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) != OverlayCandidate::CandidateStatus::kSuccess) {
+ OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane));
+ if (candidate_factory.FromDrawQuad(quad, candidate) !=
+ OverlayCandidate::CandidateStatus::kSuccess) {
return false;
}
@@ -108,10 +109,11 @@ void OverlayStrategyFullscreen::ProposePrioritized(
return;
OverlayCandidate candidate;
- if (OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- quad, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) != OverlayCandidate::CandidateStatus::kSuccess) {
+ OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane));
+ if (candidate_factory.FromDrawQuad(quad, candidate) !=
+ OverlayCandidate::CandidateStatus::kSuccess) {
return;
}
diff --git a/chromium/components/viz/service/display/overlay_strategy_single_on_top.cc b/chromium/components/viz/service/display/overlay_strategy_single_on_top.cc
index df36ded05b5..78612ba8c17 100644
--- a/chromium/components/viz/service/display/overlay_strategy_single_on_top.cc
+++ b/chromium/components/viz/service/display/overlay_strategy_single_on_top.cc
@@ -35,13 +35,15 @@ bool OverlayStrategySingleOnTop::Attempt(
QuadList* quad_list = &render_pass->quad_list;
// Build a list of candidates with the associated quad.
OverlayCandidate best_candidate;
+ OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane));
+
auto best_quad_it = quad_list->end();
for (auto it = quad_list->begin(); it != quad_list->end(); ++it) {
OverlayCandidate candidate;
- if (OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- *it, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) == OverlayCandidate::CandidateStatus::kSuccess &&
+ if (candidate_factory.FromDrawQuad(*it, candidate) ==
+ OverlayCandidate::CandidateStatus::kSuccess &&
!candidate.has_mask_filter &&
!OverlayCandidate::IsOccluded(candidate, quad_list->cbegin(), it)) {
// If the candidate has been promoted previously and has not changed
@@ -92,12 +94,14 @@ void OverlayStrategySingleOnTop::ProposePrioritized(
auto* render_pass = render_pass_list->back().get();
QuadList* quad_list = &render_pass->quad_list;
// Build a list of candidates with the associated quad.
+ OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane));
+
for (auto it = quad_list->begin(); it != quad_list->end(); ++it) {
OverlayCandidate candidate;
- if (OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- *it, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) == OverlayCandidate::CandidateStatus::kSuccess &&
+ if (candidate_factory.FromDrawQuad(*it, candidate) ==
+ OverlayCandidate::CandidateStatus::kSuccess &&
!candidate.has_mask_filter &&
!OverlayCandidate::IsOccluded(candidate, quad_list->cbegin(), it)) {
candidates->push_back({it, candidate, this});
diff --git a/chromium/components/viz/service/display/overlay_strategy_underlay.cc b/chromium/components/viz/service/display/overlay_strategy_underlay.cc
index c7aa14df543..145eaffe805 100644
--- a/chromium/components/viz/service/display/overlay_strategy_underlay.cc
+++ b/chromium/components/viz/service/display/overlay_strategy_underlay.cc
@@ -36,13 +36,14 @@ bool OverlayStrategyUnderlay::Attempt(
DCHECK(candidate_list->empty());
auto* render_pass = render_pass_list->back().get();
QuadList& quad_list = render_pass->quad_list;
+ auto candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane));
for (auto it = quad_list.begin(); it != quad_list.end(); ++it) {
OverlayCandidate candidate;
- if (OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- *it, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) != OverlayCandidate::CandidateStatus::kSuccess ||
+ if (candidate_factory.FromDrawQuad(*it, candidate) !=
+ OverlayCandidate::CandidateStatus::kSuccess ||
(opaque_mode_ == OpaqueMode::RequireOpaqueCandidates &&
!candidate.is_opaque)) {
continue;
@@ -51,7 +52,7 @@ bool OverlayStrategyUnderlay::Attempt(
// Filters read back the framebuffer to get the pixel values that need to
// be filtered. This is a problem when there are hardware planes because
// the planes are not composited until they are on the display controller.
- if (OverlayCandidate::IsOccludedByFilteredQuad(
+ if (candidate_factory.IsOccludedByFilteredQuad(
candidate, quad_list.begin(), it, render_pass_backdrop_filters)) {
continue;
}
@@ -101,14 +102,14 @@ void OverlayStrategyUnderlay::ProposePrioritized(
std::vector<gfx::Rect>* content_bounds) {
auto* render_pass = render_pass_list->back().get();
QuadList& quad_list = render_pass->quad_list;
+ OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane));
for (auto it = quad_list.begin(); it != quad_list.end(); ++it) {
OverlayCandidate candidate;
-
- if (OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- *it, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) != OverlayCandidate::CandidateStatus::kSuccess ||
+ if (candidate_factory.FromDrawQuad(*it, candidate) !=
+ OverlayCandidate::CandidateStatus::kSuccess ||
(opaque_mode_ == OpaqueMode::RequireOpaqueCandidates &&
!candidate.is_opaque)) {
continue;
@@ -120,13 +121,13 @@ void OverlayStrategyUnderlay::ProposePrioritized(
// If we are requiring an overlay, then we should not block it due to this
// condition.
if (!candidate.requires_overlay &&
- OverlayCandidate::IsOccludedByFilteredQuad(
+ candidate_factory.IsOccludedByFilteredQuad(
candidate, quad_list.begin(), it, render_pass_backdrop_filters)) {
continue;
}
- candidate.damage_area_estimate = OverlayCandidate::EstimateVisibleDamage(
- *it, surface_damage_rect_list, quad_list.begin(), it);
+ candidate.damage_area_estimate = candidate_factory.EstimateVisibleDamage(
+ *it, candidate, quad_list.begin(), it);
candidates->push_back({it, candidate, this});
}
diff --git a/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc b/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc
index 7746bf26d3d..db9fe19c0df 100644
--- a/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc
+++ b/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc
@@ -60,6 +60,10 @@ bool OverlayStrategyUnderlayCast::Attempt(
QuadList& quad_list = render_pass->quad_list;
bool found_underlay = false;
gfx::Rect content_rect;
+ OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane));
+
for (const auto* quad : base::Reversed(quad_list)) {
if (OverlayCandidate::IsInvisibleQuad(quad))
continue;
@@ -79,18 +83,15 @@ bool OverlayStrategyUnderlayCast::Attempt(
// quad is supposed to be to replace it with a transparent quad to allow
// the underlay to be visible.
// VIDEO_HOLE implies it requires overlay.
- is_underlay =
- quad->material == DrawQuad::Material::kVideoHole &&
- OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- quad, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) == OverlayCandidate::CandidateStatus::kSuccess;
+ is_underlay = quad->material == DrawQuad::Material::kVideoHole &&
+ candidate_factory.FromDrawQuad(quad, candidate) ==
+ OverlayCandidate::CandidateStatus::kSuccess;
found_underlay = is_underlay;
}
if (!found_underlay && quad->material == DrawQuad::Material::kSolidColor) {
const SolidColorDrawQuad* solid = SolidColorDrawQuad::MaterialCast(quad);
- if (solid->color == SK_ColorBLACK)
+ if (solid->color == SkColors::kBlack)
continue;
}
@@ -110,10 +111,8 @@ bool OverlayStrategyUnderlayCast::Attempt(
for (auto it = quad_list.begin(); it != quad_list.end(); ++it) {
OverlayCandidate candidate;
if (it->material != DrawQuad::Material::kVideoHole ||
- OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- *it, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) != OverlayCandidate::CandidateStatus::kSuccess) {
+ candidate_factory.FromDrawQuad(*it, candidate) !=
+ OverlayCandidate::CandidateStatus::kSuccess) {
continue;
}
@@ -145,6 +144,10 @@ void OverlayStrategyUnderlayCast::ProposePrioritized(
QuadList& quad_list = render_pass->quad_list;
OverlayCandidate candidate;
auto overlay_iter = quad_list.end();
+ OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane));
+
// Original code did reverse iteration.
// Here we do forward but find the last one. which should be the same thing.
for (auto it = quad_list.begin(); it != quad_list.end(); ++it) {
@@ -160,10 +163,8 @@ void OverlayStrategyUnderlayCast::ProposePrioritized(
// the underlay to be visible.
// VIDEO_HOLE implies it requires overlay.
if (it->material == DrawQuad::Material::kVideoHole &&
- OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- *it, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) == OverlayCandidate::CandidateStatus::kSuccess) {
+ candidate_factory.FromDrawQuad(*it, candidate) ==
+ OverlayCandidate::CandidateStatus::kSuccess) {
overlay_iter = it;
}
}
@@ -190,6 +191,10 @@ bool OverlayStrategyUnderlayCast::AttemptPrioritized(
QuadList& quad_list = render_pass->quad_list;
bool found_underlay = false;
gfx::Rect content_rect;
+ OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
+ render_pass, resource_provider, surface_damage_rect_list,
+ &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane));
+
for (const auto* quad : base::Reversed(quad_list)) {
if (OverlayCandidate::IsInvisibleQuad(quad))
continue;
@@ -209,18 +214,15 @@ bool OverlayStrategyUnderlayCast::AttemptPrioritized(
// quad is supposed to be to replace it with a transparent quad to allow
// the underlay to be visible.
// VIDEO_HOLE implies it requires overlay.
- is_underlay =
- quad->material == DrawQuad::Material::kVideoHole &&
- OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- quad, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) == OverlayCandidate::CandidateStatus::kSuccess;
+ is_underlay = quad->material == DrawQuad::Material::kVideoHole &&
+ candidate_factory.FromDrawQuad(quad, candidate) ==
+ OverlayCandidate::CandidateStatus::kSuccess;
found_underlay = is_underlay;
}
if (!found_underlay && quad->material == DrawQuad::Material::kSolidColor) {
const SolidColorDrawQuad* solid = SolidColorDrawQuad::MaterialCast(quad);
- if (solid->color == SK_ColorBLACK)
+ if (solid->color == SkColors::kBlack)
continue;
}
@@ -240,10 +242,8 @@ bool OverlayStrategyUnderlayCast::AttemptPrioritized(
for (auto it = quad_list.begin(); it != quad_list.end(); ++it) {
OverlayCandidate candidate;
if (it->material != DrawQuad::Material::kVideoHole ||
- OverlayCandidate::FromDrawQuad(
- resource_provider, surface_damage_rect_list, output_color_matrix,
- *it, GetPrimaryPlaneDisplayRect(primary_plane),
- &candidate) != OverlayCandidate::CandidateStatus::kSuccess) {
+ candidate_factory.FromDrawQuad(*it, candidate) !=
+ OverlayCandidate::CandidateStatus::kSuccess) {
continue;
}
diff --git a/chromium/components/viz/service/display/overlay_unittest.cc b/chromium/components/viz/service/display/overlay_unittest.cc
index e5a1102403b..954d384fa24 100644
--- a/chromium/components/viz/service/display/overlay_unittest.cc
+++ b/chromium/components/viz/service/display/overlay_unittest.cc
@@ -22,6 +22,7 @@
#include "cc/test/resource_provider_test_utils.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/features.h"
+#include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
#include "components/viz/common/quads/compositor_render_pass.h"
#include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
@@ -31,8 +32,7 @@
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/service/display/ca_layer_overlay.h"
-#include "components/viz/service/display/display_resource_provider_gl.h"
-#include "components/viz/service/display/gl_renderer.h"
+#include "components/viz/service/display/display_resource_provider_skia.h"
#include "components/viz/service/display/output_surface.h"
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/output_surface_frame.h"
@@ -44,6 +44,7 @@
#include "components/viz/service/display/overlay_strategy_single_on_top.h"
#include "components/viz/service/display/overlay_strategy_underlay.h"
#include "components/viz/service/display/overlay_strategy_underlay_cast.h"
+#include "components/viz/test/fake_skia_output_surface.h"
#include "components/viz/test/test_context_provider.h"
#include "components/viz/test/test_gles2_interface.h"
#include "gpu/config/gpu_finch_features.h"
@@ -392,60 +393,6 @@ class FullThresholdUnderlayOverlayProcessor : public DefaultOverlayProcessor {
}
};
-class OverlayOutputSurface : public OutputSurface {
- public:
- explicit OverlayOutputSurface(
- scoped_refptr<TestContextProvider> context_provider)
- : OutputSurface(std::move(context_provider)) {
- is_displayed_as_overlay_plane_ = true;
- capabilities_.supports_viewporter = true;
- }
-
- // OutputSurface implementation.
- void BindToClient(OutputSurfaceClient* client) override {}
- void EnsureBackbuffer() override {}
- void DiscardBackbuffer() override {}
- void BindFramebuffer() override { bind_framebuffer_count_ += 1; }
- void Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) override {
- size_ = size;
- }
- void SwapBuffers(OutputSurfaceFrame frame) override {}
- uint32_t GetFramebufferCopyTextureFormat() override {
- // TestContextProvider has no real framebuffer, just use RGB.
- return GL_RGB;
- }
- bool HasExternalStencilTest() const override { return false; }
- void ApplyExternalStencil() override {}
- bool IsDisplayedAsOverlayPlane() const override {
- return is_displayed_as_overlay_plane_;
- }
- unsigned GetOverlayTextureId() const override { return 10000; }
- unsigned UpdateGpuFence() override { return 0; }
- void SetUpdateVSyncParametersCallback(
- UpdateVSyncParametersCallback callback) override {}
- void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
- gfx::OverlayTransform GetDisplayTransform() override {
- return gfx::OVERLAY_TRANSFORM_NONE;
- }
-
- void set_is_displayed_as_overlay_plane(bool value) {
- is_displayed_as_overlay_plane_ = value;
- }
-
- unsigned bind_framebuffer_count() const { return bind_framebuffer_count_; }
- void clear_bind_framebuffer_count() { bind_framebuffer_count_ = 0; }
- gfx::Size size() const { return size_; }
-
- private:
- gfx::Size size_;
- bool is_displayed_as_overlay_plane_;
- unsigned bind_framebuffer_count_ = 0;
-};
-
std::unique_ptr<AggregatedRenderPass> CreateRenderPass() {
AggregatedRenderPassId render_pass_id{1};
gfx::Rect output_rect(0, 0, 256, 256);
@@ -741,13 +688,12 @@ template <typename OverlayProcessorType>
class OverlayTest : public testing::Test {
protected:
void SetUp() override {
- provider_ = TestContextProvider::Create();
- provider_->BindToCurrentThread();
- output_surface_ = std::make_unique<OverlayOutputSurface>(provider_);
- output_surface_->BindToClient(&client_);
+ output_surface_ = FakeSkiaOutputSurface::Create3d();
+ output_surface_->BindToClient(&output_surface_client_);
- resource_provider_ =
- std::make_unique<DisplayResourceProviderGL>(provider_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProviderSkia>();
+ lock_set_for_external_use_.emplace(resource_provider_.get(),
+ output_surface_.get());
child_provider_ = TestContextProvider::Create();
child_provider_->BindToCurrentThread();
@@ -761,9 +707,9 @@ class OverlayTest : public testing::Test {
child_resource_provider_->ShutdownAndReleaseAllResources();
child_resource_provider_ = nullptr;
child_provider_ = nullptr;
+ lock_set_for_external_use_.reset();
resource_provider_ = nullptr;
output_surface_ = nullptr;
- provider_ = nullptr;
}
void AddExpectedRectToOverlayProcessor(const gfx::RectF& rect) {
@@ -774,10 +720,11 @@ class OverlayTest : public testing::Test {
overlay_processor_->AddScalingSequence(scaling, uses_overlay);
}
- scoped_refptr<TestContextProvider> provider_;
- std::unique_ptr<OverlayOutputSurface> output_surface_;
- cc::FakeOutputSurfaceClient client_;
- std::unique_ptr<DisplayResourceProvider> resource_provider_;
+ std::unique_ptr<SkiaOutputSurface> output_surface_;
+ cc::FakeOutputSurfaceClient output_surface_client_;
+ std::unique_ptr<DisplayResourceProviderSkia> resource_provider_;
+ absl::optional<DisplayResourceProviderSkia::LockSetForExternalUse>
+ lock_set_for_external_use_;
scoped_refptr<TestContextProvider> child_provider_;
std::unique_ptr<ClientResourceProvider> child_resource_provider_;
std::unique_ptr<OverlayProcessorType> overlay_processor_;
@@ -794,11 +741,21 @@ class UseMultipleOverlaysTest : public OverlayTest<OverlayProcessorType> {
featureAndParamsList = {{features::kEnableOverlayPrioritization, {}},
{features::kUseMultipleOverlays,
{{features::kMaxOverlaysParam, "4"}}}};
- features.InitWithFeaturesAndParameters(featureAndParamsList, {});
+ scoped_features.InitWithFeaturesAndParameters(featureAndParamsList, {});
+ }
+
+ protected:
+ void SetUp() override {
+ OverlayTest<OverlayProcessorType>::SetUp();
+ // When overlay prioritization is explicitly disabled (Lacros) we should
+ // skip multiple overlays tests.
+ if (!features::IsOverlayPrioritizationEnabled()) {
+ GTEST_SKIP();
+ }
}
private:
- base::test::ScopedFeatureList features;
+ base::test::ScopedFeatureList scoped_features;
};
using FullscreenOverlayTest = OverlayTest<FullscreenOverlayProcessor>;
@@ -817,15 +774,6 @@ using SizeSortedMultiOverlayTest =
UseMultipleOverlaysTest<SizeSortedMultiOverlayProcessor>;
TEST(OverlayTest, OverlaysProcessorHasStrategy) {
- scoped_refptr<TestContextProvider> provider = TestContextProvider::Create();
- provider->BindToCurrentThread();
- OverlayOutputSurface output_surface(provider);
- cc::FakeOutputSurfaceClient client;
- output_surface.BindToClient(&client);
-
- auto resource_provider =
- std::make_unique<DisplayResourceProviderGL>(provider.get());
-
auto overlay_processor = std::make_unique<TestOverlayProcessor>();
EXPECT_GE(2U, overlay_processor->GetStrategyCount());
}
@@ -1186,14 +1134,13 @@ TEST_F(SingleOverlayOnTopTest, CandidateIdCollision) {
// Code to make sure the 'unique' tracking ids are actually identical.
OverlayCandidate candidate_a;
- SkM44 ident;
- auto ret_a = OverlayCandidate::FromDrawQuad(
- resource_provider_.get(), &surface_damage_rect_list, ident, quad_a,
- gfx::RectF(pass->output_rect), &candidate_a);
+ auto color_mat = GetIdentityColorMatrix();
+ auto candidate_factory = OverlayCandidateFactory(
+ pass.get(), resource_provider_.get(), &surface_damage_rect_list,
+ &color_mat, gfx::RectF(pass->output_rect));
+ auto ret_a = candidate_factory.FromDrawQuad(quad_a, candidate_a);
OverlayCandidate candidate_b;
- auto ret_b = OverlayCandidate::FromDrawQuad(
- resource_provider_.get(), &surface_damage_rect_list, ident, quad_b,
- gfx::RectF(pass->output_rect), &candidate_b);
+ auto ret_b = candidate_factory.FromDrawQuad(quad_b, candidate_b);
EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_a);
EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_b);
@@ -1203,10 +1150,9 @@ TEST_F(SingleOverlayOnTopTest, CandidateIdCollision) {
pass_list.push_back(std::move(pass));
overlay_processor_->SetFrameSequenceNumber(1);
overlay_processor_->ProcessForOverlays(
- resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
- render_pass_filters, render_pass_backdrop_filters,
- std::move(surface_damage_rect_list), nullptr, &candidate_list,
- &damage_rect_, &content_bounds_);
+ resource_provider_.get(), &pass_list, color_mat, render_pass_filters,
+ render_pass_backdrop_filters, std::move(surface_damage_rect_list),
+ nullptr, &candidate_list, &damage_rect_, &content_bounds_);
ASSERT_EQ(1U, candidate_list.size());
// Check that one quad is gone.
@@ -1235,15 +1181,14 @@ TEST_F(SingleOverlayOnTopTest, CandidateTrackIdUniqueSurface) {
kCandidateRect, SurfaceId(FrameSinkId(2, 2), LocalSurfaceId()));
// Code to make sure the 'unique' tracking ids are actually different.
OverlayCandidate candidate_a;
- SkM44 ident;
SurfaceDamageRectList surface_damage_rect_list;
- auto ret_a = OverlayCandidate::FromDrawQuad(
- resource_provider_.get(), &surface_damage_rect_list, ident, quad_a,
- gfx::RectF(pass->output_rect), &candidate_a);
+ auto color_mat = GetIdentityColorMatrix();
+ auto candidate_factory = OverlayCandidateFactory(
+ pass.get(), resource_provider_.get(), &surface_damage_rect_list,
+ &color_mat, gfx::RectF(pass->output_rect));
+ auto ret_a = candidate_factory.FromDrawQuad(quad_a, candidate_a);
OverlayCandidate candidate_b;
- auto ret_b = OverlayCandidate::FromDrawQuad(
- resource_provider_.get(), &surface_damage_rect_list, ident, quad_b,
- gfx::RectF(pass->output_rect), &candidate_b);
+ auto ret_b = candidate_factory.FromDrawQuad(quad_b, candidate_b);
EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_a);
EXPECT_EQ(OverlayCandidate::CandidateStatus::kSuccess, ret_b);
@@ -1728,7 +1673,7 @@ TEST_F(SingleOverlayOnTopTest, RejectBackgroundColor) {
TextureDrawQuad* quad = CreateFullscreenCandidateQuad(
resource_provider_.get(), child_resource_provider_.get(),
child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
- quad->background_color = SK_ColorRED;
+ quad->background_color = SkColors::kRed;
OverlayCandidateList candidate_list;
OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
@@ -1750,7 +1695,7 @@ TEST_F(SingleOverlayOnTopTest, AcceptBlackBackgroundColor) {
TextureDrawQuad* quad = CreateFullscreenCandidateQuad(
resource_provider_.get(), child_resource_provider_.get(),
child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
- quad->background_color = SK_ColorBLACK;
+ quad->background_color = SkColors::kBlack;
OverlayCandidateList candidate_list;
OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
@@ -1772,7 +1717,7 @@ TEST_F(SingleOverlayOnTopTest, RejectBlackBackgroundColorWithBlending) {
TextureDrawQuad* quad = CreateFullscreenCandidateQuad(
resource_provider_.get(), child_resource_provider_.get(),
child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
- quad->background_color = SK_ColorBLACK;
+ quad->background_color = SkColors::kBlack;
quad->needs_blending = true;
OverlayCandidateList candidate_list;
@@ -1922,9 +1867,9 @@ TEST_F(UnderlayTest, ReplacementQuad) {
&damage_rect_, &content_bounds_);
ASSERT_EQ(1U, pass_list.size());
ASSERT_EQ(1U, pass_list.front()->quad_list.size());
- EXPECT_EQ(SK_ColorTRANSPARENT, static_cast<SolidColorDrawQuad*>(
- pass_list.front()->quad_list.front())
- ->color);
+ EXPECT_EQ(SkColors::kTransparent, static_cast<SolidColorDrawQuad*>(
+ pass_list.front()->quad_list.front())
+ ->color);
EXPECT_FALSE(pass_list.front()->quad_list.front()->ShouldDrawWithBlending());
EXPECT_FALSE(pass_list.front()
->quad_list.front()
@@ -2764,7 +2709,7 @@ TEST_F(UnderlayTest, OverlayLayerUnderMainLayer) {
auto* quad = static_cast<SolidColorDrawQuad*>(main_pass->quad_list.back());
EXPECT_EQ(quad->rect, quad->visible_rect);
EXPECT_EQ(false, quad->needs_blending);
- EXPECT_EQ(SK_ColorTRANSPARENT, quad->color);
+ EXPECT_EQ(SkColors::kTransparent, quad->color);
}
TEST_F(UnderlayTest, AllowOnTop) {
@@ -2797,7 +2742,7 @@ TEST_F(UnderlayTest, AllowOnTop) {
auto* quad = static_cast<SolidColorDrawQuad*>(main_pass->quad_list.front());
EXPECT_EQ(quad->rect, quad->visible_rect);
EXPECT_EQ(false, quad->needs_blending);
- EXPECT_EQ(SK_ColorTRANSPARENT, quad->color);
+ EXPECT_EQ(SkColors::kTransparent, quad->color);
}
// Pure overlays have a very specific optimization that does not produce damage
@@ -2985,9 +2930,9 @@ TEST_F(TransitionOverlayTypeTest, MaskFilterBringsUnderlay) {
ASSERT_EQ(1U, pass_list.size());
ASSERT_EQ(2U, pass_list.front()->quad_list.size());
- EXPECT_EQ(SK_ColorBLACK, static_cast<SolidColorDrawQuad*>(
- pass_list.front()->quad_list.front())
- ->color);
+ EXPECT_EQ(SkColors::kBlack, static_cast<SolidColorDrawQuad*>(
+ pass_list.front()->quad_list.front())
+ ->color);
EXPECT_FALSE(pass_list.front()
->quad_list.front()
->shared_quad_state->are_contents_opaque);
@@ -3750,9 +3695,9 @@ TEST_F(UnderlayCastTest, ReplacementQuad) {
&damage_rect_, &content_bounds_);
ASSERT_EQ(1U, pass_list.size());
ASSERT_EQ(1U, pass_list.front()->quad_list.size());
- EXPECT_EQ(SK_ColorTRANSPARENT, static_cast<SolidColorDrawQuad*>(
- pass_list.front()->quad_list.front())
- ->color);
+ EXPECT_EQ(SkColors::kTransparent, static_cast<SolidColorDrawQuad*>(
+ pass_list.front()->quad_list.front())
+ ->color);
EXPECT_FALSE(pass_list.front()->quad_list.front()->ShouldDrawWithBlending());
EXPECT_FALSE(pass_list.front()
->quad_list.front()
@@ -4009,9 +3954,9 @@ TEST_F(UnderlayCastTest, OverlayPromotionWithMaskFilter) {
ASSERT_EQ(1U, pass_list.size());
ASSERT_EQ(1U, pass_list.front()->quad_list.size());
- EXPECT_EQ(SK_ColorBLACK, static_cast<SolidColorDrawQuad*>(
- pass_list.front()->quad_list.front())
- ->color);
+ EXPECT_EQ(SkColors::kBlack, static_cast<SolidColorDrawQuad*>(
+ pass_list.front()->quad_list.front())
+ ->color);
EXPECT_FALSE(pass_list.front()
->quad_list.front()
->shared_quad_state->are_contents_opaque);
@@ -4296,11 +4241,12 @@ TEST_F(SingleOverlayOnTopTest, IsOverlayRequiredBasic) {
child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(),
kSmallCandidateRect);
SurfaceDamageRectList surface_damage_rect_list;
- SkM44 default_color = GetIdentityColorMatrix();
OverlayCandidate candidate;
- OverlayCandidate::FromDrawQuad(resource_provider_.get(),
- &surface_damage_rect_list, default_color,
- new_quad, gfx::RectF(), &candidate);
+ auto color_mat = GetIdentityColorMatrix();
+ auto candidate_factory = OverlayCandidateFactory(
+ pass.get(), resource_provider_.get(), &surface_damage_rect_list,
+ &color_mat, gfx::RectF(pass->output_rect));
+ candidate_factory.FromDrawQuad(new_quad, candidate);
// Verify that a default candidate is not a required overlay.
EXPECT_FALSE(candidate.requires_overlay);
@@ -4318,11 +4264,12 @@ TEST_F(SingleOverlayOnTopTest, IsOverlayRequiredHwProtectedVideo) {
kSmallCandidateRect, gfx::ProtectedVideoType::kHardwareProtected,
YUV_420_BIPLANAR);
SurfaceDamageRectList surface_damage_rect_list;
- SkM44 default_color = GetIdentityColorMatrix();
OverlayCandidate candidate;
- OverlayCandidate::FromDrawQuad(resource_provider_.get(),
- &surface_damage_rect_list, default_color,
- new_quad, gfx::RectF(), &candidate);
+ auto color_mat = GetIdentityColorMatrix();
+ auto candidate_factory = OverlayCandidateFactory(
+ pass.get(), resource_provider_.get(), &surface_damage_rect_list,
+ &color_mat, gfx::RectF(pass->output_rect));
+ candidate_factory.FromDrawQuad(new_quad, candidate);
// Verify that a HW protected video candidate requires overlay.
EXPECT_TRUE(candidate.requires_overlay);
@@ -4341,11 +4288,12 @@ TEST_F(SingleOverlayOnTopTest, RequiredOverlayClippingAndSubsampling) {
YUV_420_BIPLANAR);
pass->shared_quad_state_list.back()->clip_rect = kOverlayClipRect;
SurfaceDamageRectList surface_damage_rect_list;
- SkM44 default_color = GetIdentityColorMatrix();
OverlayCandidate candidate;
- OverlayCandidate::FromDrawQuad(resource_provider_.get(),
- &surface_damage_rect_list, default_color,
- new_quad, gfx::RectF(), &candidate);
+ auto color_mat = GetIdentityColorMatrix();
+ auto candidate_factory = OverlayCandidateFactory(
+ pass.get(), resource_provider_.get(), &surface_damage_rect_list,
+ &color_mat, gfx::RectF(pass->output_rect));
+ candidate_factory.FromDrawQuad(new_quad, candidate);
// Default uv rect is 0.1, 0.2, 1.0, 1.0 which in the 320x240 buffer
// corresponds to 32, 48, 288x192. That maps to |kVideoCandidateRect| in the
@@ -4374,13 +4322,14 @@ TEST_F(SingleOverlayOnTopTest,
YUV_420_BIPLANAR);
pass->shared_quad_state_list.back()->clip_rect = kOverlayClipRect;
SurfaceDamageRectList surface_damage_rect_list;
- SkM44 default_color = GetIdentityColorMatrix();
gfx::RectF primary_rect(0, 0, 100, 120);
OverlayProcessorInterface::OutputSurfaceOverlayPlane primary_plane;
OverlayCandidate candidate;
- OverlayCandidate::FromDrawQuad(resource_provider_.get(),
- &surface_damage_rect_list, default_color,
- new_quad, primary_rect, &candidate);
+ auto color_mat = GetIdentityColorMatrix();
+ auto candidate_factory = OverlayCandidateFactory(
+ pass.get(), resource_provider_.get(), &surface_damage_rect_list,
+ &color_mat, primary_rect);
+ candidate_factory.FromDrawQuad(new_quad, candidate);
// Default uv rect is 0.1, 0.2, 1.0, 1.0 which in the 320x240 buffer
// corresponds to 32, 48, 288x192. That maps to |kVideoCandidateRect| in the
@@ -4449,7 +4398,7 @@ TEST_F(UnderlayTest, EstimateOccludedDamage) {
kCandidateSmall * kCandidateSmall,
kCandidateSmall * kCandidateSmall - kOccluderWidth * kOccluderWidth,
kCandidateLarge * kCandidateLarge - kOccluderWidth * kOccluderWidth * 2,
- kCandidateLarge * kCandidateLarge * 4 -
+ kOverlayRect.width() * kOverlayRect.height() -
kOccluderWidth * kOccluderWidth * 2};
static_assert(
@@ -4477,21 +4426,22 @@ TEST_F(UnderlayTest, EstimateOccludedDamage) {
if (kCandidateUseSurfaceIndex[i]) {
damaged_shared_quad_state->overlay_damage_index =
surface_damage_rect_list.size();
- surface_damage_rect_list.emplace_back(kCandidateRects[i]);
} else {
damaged_shared_quad_state->overlay_damage_index.reset();
}
+ surface_damage_rect_list.emplace_back(kCandidateRects[i]);
auto* quad_candidate = CreateCandidateQuadAt(
resource_provider_.get(), child_resource_provider_.get(),
child_provider_.get(), damaged_shared_quad_state, pass.get(),
kCandidateRects[i]);
- SkM44 default_color = GetIdentityColorMatrix();
OverlayCandidate candidate;
- OverlayCandidate::FromDrawQuad(resource_provider_.get(),
- &surface_damage_rect_list, default_color,
- quad_candidate, gfx::RectF(), &candidate);
+ auto color_mat = GetIdentityColorMatrix();
+ auto candidate_factory = OverlayCandidateFactory(
+ pass.get(), resource_provider_.get(), &surface_damage_rect_list,
+ &color_mat, gfx::RectF());
+ candidate_factory.FromDrawQuad(quad_candidate, candidate);
// Before the 'EstimateOccludedDamage' function is called the damage area
// will just be whatever comes from the |surface_damage_rect_list|.
@@ -4508,8 +4458,8 @@ TEST_F(UnderlayTest, EstimateOccludedDamage) {
// Now we test the opaque occlusion provided by 'EstimateOccludedDamage'
// function.
- candidate.damage_area_estimate = OverlayCandidate::EstimateVisibleDamage(
- quad_candidate, &surface_damage_rect_list, quad_list.begin(),
+ candidate.damage_area_estimate = candidate_factory.EstimateVisibleDamage(
+ quad_candidate, candidate, quad_list.begin(),
std::next(quad_list.begin(), occluder_iter_count));
ASSERT_EQ(kExpectedDamages[i], candidate.damage_area_estimate);
@@ -4710,6 +4660,41 @@ TEST_F(DelegatedTest, TestClipHandCrafted) {
EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f);
}
+TEST_F(DelegatedTest, TestVisibleRectClip) {
+ auto pass = CreateRenderPass();
+ const auto kSmallCandidateRect = gfx::Rect(0, 0, 100, 100);
+ const auto kTestClip = gfx::Rect(0, 50, 50, 50);
+ auto* tex_rect = CreateCandidateQuadAt(
+ resource_provider_.get(), child_resource_provider_.get(),
+ child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(),
+ kSmallCandidateRect);
+ tex_rect->uv_bottom_right = gfx::PointF(1, 1);
+ tex_rect->uv_top_left = gfx::PointF(0, 0);
+ tex_rect->visible_rect = kTestClip;
+ // Check for potential candidates.
+ OverlayCandidateList candidate_list;
+ OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
+ OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters;
+ AggregatedRenderPassList pass_list;
+ // AggregatedRenderPass* main_pass = pass.get();
+ SurfaceDamageRectList surface_damage_rect_list;
+ // Simplify by adding full root damage.
+ surface_damage_rect_list.push_back(pass->output_rect);
+ pass_list.push_back(std::move(pass));
+ overlay_processor_->ProcessForOverlays(
+ resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+ render_pass_filters, render_pass_backdrop_filters,
+ std::move(surface_damage_rect_list),
+ overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list,
+ &damage_rect_, &content_bounds_);
+
+ const auto uv_rect = gfx::RectF(0, 0.5f, 0.5f, 0.5f);
+ EXPECT_EQ(1U, candidate_list.size());
+ EXPECT_RECTF_NEAR(gfx::RectF(kTestClip), candidate_list[0].display_rect,
+ 0.01f);
+ EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f);
+}
+
TEST_F(DelegatedTest, TestClipComputed) {
auto pass = CreateRenderPass();
const auto kSmallCandidateRect = gfx::Rect(5, 10, 128, 64);
@@ -4821,7 +4806,8 @@ TEST_F(DelegatedTest, ScaledBufferDamage) {
overlay_processor_->ProcessForOverlays(
resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
render_pass_filters, render_pass_backdrop_filters,
- std::move(surface_damage_rect_list), nullptr, &candidate_list,
+ std::move(surface_damage_rect_list),
+ overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list,
&damage_rect_, &content_bounds_);
// Expected damage is basically the intersection of the rect with the screen
diff --git a/chromium/components/viz/service/display/program_binding.cc b/chromium/components/viz/service/display/program_binding.cc
deleted file mode 100644
index 488151c3587..00000000000
--- a/chromium/components/viz/service/display/program_binding.cc
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright 2011 The Chromium 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/viz/service/display/program_binding.h"
-
-#include "base/logging.h"
-#include "base/trace_event/trace_event.h"
-#include "components/viz/service/display/geometry_binding.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "ui/gfx/color_transform.h"
-
-using gpu::gles2::GLES2Interface;
-
-namespace viz {
-
-ProgramKey::ProgramKey() = default;
-
-ProgramKey::ProgramKey(const ProgramKey& other) = default;
-
-ProgramKey::~ProgramKey() = default;
-
-bool ProgramKey::operator==(const ProgramKey& other) const {
- return type_ == other.type_ && precision_ == other.precision_ &&
- sampler_ == other.sampler_ && blend_mode_ == other.blend_mode_ &&
- aa_mode_ == other.aa_mode_ && is_opaque_ == other.is_opaque_ &&
- premultiplied_alpha_ == other.premultiplied_alpha_ &&
- has_background_color_ == other.has_background_color_ &&
- has_tex_clamp_rect_ == other.has_tex_clamp_rect_ &&
- mask_mode_ == other.mask_mode_ &&
- mask_for_background_ == other.mask_for_background_ &&
- has_color_matrix_ == other.has_color_matrix_ &&
- yuv_alpha_texture_mode_ == other.yuv_alpha_texture_mode_ &&
- uv_texture_mode_ == other.uv_texture_mode_ &&
- color_conversion_mode_ == other.color_conversion_mode_ &&
- color_transform_ == other.color_transform_ &&
- has_output_color_matrix_ == other.has_output_color_matrix_ &&
- has_rounded_corner_ == other.has_rounded_corner_;
-}
-
-bool ProgramKey::operator!=(const ProgramKey& other) const {
- return !(*this == other);
-}
-
-// static
-ProgramKey ProgramKey::DebugBorder() {
- ProgramKey result;
- result.type_ = PROGRAM_TYPE_DEBUG_BORDER;
- return result;
-}
-
-// static
-ProgramKey ProgramKey::SolidColor(AAMode aa_mode,
- bool tint_color,
- bool rounded_corner) {
- ProgramKey result;
- result.type_ = PROGRAM_TYPE_SOLID_COLOR;
- result.aa_mode_ = aa_mode;
- result.has_tint_color_matrix_ = tint_color;
- result.has_rounded_corner_ = rounded_corner;
- return result;
-}
-
-// static
-ProgramKey ProgramKey::Tile(TexCoordPrecision precision,
- SamplerType sampler,
- AAMode aa_mode,
- PremultipliedAlphaMode premultiplied_alpha,
- bool is_opaque,
- bool has_tex_clamp_rect,
- bool tint_color,
- bool rounded_corner) {
- ProgramKey result;
- result.type_ = PROGRAM_TYPE_TILE;
- result.precision_ = precision;
- result.sampler_ = sampler;
- result.aa_mode_ = aa_mode;
- result.is_opaque_ = is_opaque;
- result.has_tex_clamp_rect_ = has_tex_clamp_rect;
- result.has_tint_color_matrix_ = tint_color;
- result.premultiplied_alpha_ = premultiplied_alpha;
- result.has_rounded_corner_ = rounded_corner;
- return result;
-}
-
-// static
-ProgramKey ProgramKey::Texture(TexCoordPrecision precision,
- SamplerType sampler,
- PremultipliedAlphaMode premultiplied_alpha,
- bool has_background_color,
- bool has_tex_clamp_rect,
- bool tint_color,
- bool rounded_corner) {
- ProgramKey result;
- result.type_ = PROGRAM_TYPE_TEXTURE;
- result.precision_ = precision;
- result.sampler_ = sampler;
- result.premultiplied_alpha_ = premultiplied_alpha;
- result.has_background_color_ = has_background_color;
- result.has_tex_clamp_rect_ = has_tex_clamp_rect;
- result.has_tint_color_matrix_ = tint_color;
- result.has_rounded_corner_ = rounded_corner;
- return result;
-}
-
-// static
-ProgramKey ProgramKey::RenderPass(TexCoordPrecision precision,
- SamplerType sampler,
- BlendMode blend_mode,
- AAMode aa_mode,
- MaskMode mask_mode,
- bool mask_for_background,
- bool has_color_matrix,
- bool tint_color,
- bool rounded_corner) {
- ProgramKey result;
- result.type_ = PROGRAM_TYPE_RENDER_PASS;
- result.precision_ = precision;
- result.sampler_ = sampler;
- result.blend_mode_ = blend_mode;
- result.aa_mode_ = aa_mode;
- result.mask_mode_ = mask_mode;
- result.mask_for_background_ = mask_for_background;
- result.has_color_matrix_ = has_color_matrix;
- result.has_tint_color_matrix_ = tint_color;
- result.has_rounded_corner_ = rounded_corner;
- return result;
-}
-
-// static
-ProgramKey ProgramKey::VideoStream(TexCoordPrecision precision,
- bool rounded_corner) {
- ProgramKey result;
- result.type_ = PROGRAM_TYPE_VIDEO_STREAM;
- result.precision_ = precision;
- result.sampler_ = SAMPLER_TYPE_EXTERNAL_OES;
- result.has_rounded_corner_ = rounded_corner;
- return result;
-}
-
-// static
-ProgramKey ProgramKey::YUVVideo(TexCoordPrecision precision,
- SamplerType sampler,
- YUVAlphaTextureMode yuv_alpha_texture_mode,
- UVTextureMode uv_texture_mode,
- bool tint_color,
- bool rounded_corner) {
- ProgramKey result;
- result.type_ = PROGRAM_TYPE_YUV_VIDEO;
- result.precision_ = precision;
- result.sampler_ = sampler;
- result.yuv_alpha_texture_mode_ = yuv_alpha_texture_mode;
- DCHECK(yuv_alpha_texture_mode == YUV_NO_ALPHA_TEXTURE ||
- yuv_alpha_texture_mode == YUV_HAS_ALPHA_TEXTURE);
- result.uv_texture_mode_ = uv_texture_mode;
- DCHECK(uv_texture_mode == UV_TEXTURE_MODE_UV ||
- uv_texture_mode == UV_TEXTURE_MODE_U_V);
- result.has_tint_color_matrix_ = tint_color;
- result.has_rounded_corner_ = rounded_corner;
- return result;
-}
-
-void ProgramKey::SetColorTransform(const gfx::ColorTransform* transform) {
- color_transform_ = nullptr;
- if (transform->IsIdentity()) {
- color_conversion_mode_ = COLOR_CONVERSION_MODE_NONE;
- } else {
- color_conversion_mode_ = COLOR_CONVERSION_MODE_SHADER;
- color_transform_ = transform;
- }
-}
-
-ProgramBindingBase::ProgramBindingBase()
- : program_(0),
- vertex_shader_id_(0),
- fragment_shader_id_(0),
- initialized_(false) {}
-
-ProgramBindingBase::~ProgramBindingBase() {
- // If you hit these asserts, you initialized but forgot to call Cleanup().
- DCHECK(!program_);
- DCHECK(!vertex_shader_id_);
- DCHECK(!fragment_shader_id_);
- DCHECK(!initialized_);
-}
-
-bool ProgramBindingBase::Init(GLES2Interface* context,
- const std::string& vertex_shader,
- const std::string& fragment_shader) {
- TRACE_EVENT0("viz", "ProgramBindingBase::init");
- vertex_shader_id_ = LoadShader(context, GL_VERTEX_SHADER, vertex_shader);
- if (!vertex_shader_id_)
- return false;
-
- fragment_shader_id_ =
- LoadShader(context, GL_FRAGMENT_SHADER, fragment_shader);
- if (!fragment_shader_id_) {
- context->DeleteShader(vertex_shader_id_);
- vertex_shader_id_ = 0;
- return false;
- }
-
- program_ =
- CreateShaderProgram(context, vertex_shader_id_, fragment_shader_id_);
- return !!program_;
-}
-
-bool ProgramBindingBase::Link(GLES2Interface* context) {
- context->LinkProgram(program_);
- CleanupShaders(context);
- if (!program_)
- return false;
-#ifndef NDEBUG
- int linked = 0;
- context->GetProgramiv(program_, GL_LINK_STATUS, &linked);
- if (!linked) {
- char buffer[1024] = "";
- context->GetProgramInfoLog(program_, sizeof(buffer), nullptr, buffer);
- DLOG(ERROR) << "Error compiling shader: " << buffer;
- return false;
- }
-#endif
- return true;
-}
-
-void ProgramBindingBase::Cleanup(GLES2Interface* context) {
- initialized_ = false;
- if (!program_)
- return;
-
- DCHECK(context);
- context->DeleteProgram(program_);
- program_ = 0;
-
- CleanupShaders(context);
-}
-
-unsigned ProgramBindingBase::LoadShader(GLES2Interface* context,
- unsigned type,
- const std::string& shader_source) {
- unsigned shader = context->CreateShader(type);
- if (!shader)
- return 0u;
-
- const char* shader_source_str[] = {shader_source.data()};
- int shader_length[] = {static_cast<int>(shader_source.length())};
- context->ShaderSource(shader, 1, shader_source_str, shader_length);
- context->CompileShader(shader);
-#if EXPENSIVE_DCHECKS_ARE_ON()
- int compiled = 0;
- context->GetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
- if (!compiled) {
- char buffer[1024] = "";
- context->GetShaderInfoLog(shader, sizeof(buffer), nullptr, buffer);
- DLOG(ERROR) << "Error compiling shader: " << buffer
- << "\n shader program: " << shader_source;
- return 0u;
- }
-#endif // EXPENSIVE_DCHECKS_ARE_ON()
- return shader;
-}
-
-unsigned ProgramBindingBase::CreateShaderProgram(GLES2Interface* context,
- unsigned vertex_shader,
- unsigned fragment_shader) {
- unsigned program_object = context->CreateProgram();
- if (!program_object)
- return 0;
-
- context->AttachShader(program_object, vertex_shader);
- context->AttachShader(program_object, fragment_shader);
-
- // Bind the common attrib locations.
- context->BindAttribLocation(
- program_object, GeometryBinding::PositionAttribLocation(), "a_position");
- context->BindAttribLocation(
- program_object, GeometryBinding::TexCoordAttribLocation(), "a_texCoord");
- context->BindAttribLocation(program_object,
- GeometryBinding::TriangleIndexAttribLocation(),
- "a_index");
-
- return program_object;
-}
-
-void ProgramBindingBase::CleanupShaders(GLES2Interface* context) {
- if (vertex_shader_id_) {
- context->DeleteShader(vertex_shader_id_);
- vertex_shader_id_ = 0;
- }
- if (fragment_shader_id_) {
- context->DeleteShader(fragment_shader_id_);
- fragment_shader_id_ = 0;
- }
-}
-
-bool ProgramBindingBase::IsContextLost(GLES2Interface* context) {
- return context->GetGraphicsResetStatusKHR() != GL_NO_ERROR;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/program_binding.h b/chromium/components/viz/service/display/program_binding.h
deleted file mode 100644
index b03673f9266..00000000000
--- a/chromium/components/viz/service/display/program_binding.h
+++ /dev/null
@@ -1,480 +0,0 @@
-// Copyright 2011 The Chromium 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 COMPONENTS_VIZ_SERVICE_DISPLAY_PROGRAM_BINDING_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_PROGRAM_BINDING_H_
-
-#include <string>
-
-#include "base/check_op.h"
-#include "base/memory/raw_ptr.h"
-#include "build/build_config.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/service/display/shader.h"
-#include "components/viz/service/viz_service_export.h"
-
-namespace gfx {
-class ColorTransform;
-}
-
-namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-}
-} // namespace gpu
-
-namespace viz {
-
-class VIZ_SERVICE_EXPORT ProgramBindingBase {
- public:
- ProgramBindingBase();
-
- ProgramBindingBase(const ProgramBindingBase&) = delete;
- ProgramBindingBase& operator=(const ProgramBindingBase&) = delete;
-
- ~ProgramBindingBase();
-
- bool Init(gpu::gles2::GLES2Interface* context,
- const std::string& vertex_shader,
- const std::string& fragment_shader);
- bool Link(gpu::gles2::GLES2Interface* context);
- void Cleanup(gpu::gles2::GLES2Interface* context);
-
- unsigned program() const { return program_; }
- bool initialized() const { return initialized_; }
-
- protected:
- unsigned LoadShader(gpu::gles2::GLES2Interface* context,
- unsigned type,
- const std::string& shader_source);
- unsigned CreateShaderProgram(gpu::gles2::GLES2Interface* context,
- unsigned vertex_shader,
- unsigned fragment_shader);
- void CleanupShaders(gpu::gles2::GLES2Interface* context);
-
- bool IsContextLost(gpu::gles2::GLES2Interface* context);
-
- unsigned program_;
- unsigned vertex_shader_id_;
- unsigned fragment_shader_id_;
- bool initialized_;
-};
-
-enum ProgramType {
- PROGRAM_TYPE_DEBUG_BORDER,
- PROGRAM_TYPE_SOLID_COLOR,
- PROGRAM_TYPE_TILE,
- PROGRAM_TYPE_TEXTURE,
- PROGRAM_TYPE_RENDER_PASS,
- PROGRAM_TYPE_VIDEO_STREAM,
- PROGRAM_TYPE_YUV_VIDEO,
-};
-
-class VIZ_SERVICE_EXPORT ProgramKey {
- public:
- ProgramKey();
- ProgramKey(const ProgramKey& other);
- ~ProgramKey();
-
- static ProgramKey DebugBorder();
- static ProgramKey SolidColor(AAMode aa_mode,
- bool tint_color,
- bool rounded_corner);
- static ProgramKey Tile(TexCoordPrecision precision,
- SamplerType sampler,
- AAMode aa_mode,
- PremultipliedAlphaMode premultiplied_alpha,
- bool is_opaque,
- bool has_tex_clamp_rect,
- bool tint_color,
- bool rounded_corner);
- static ProgramKey Texture(TexCoordPrecision precision,
- SamplerType sampler,
- PremultipliedAlphaMode premultiplied_alpha,
- bool has_background_color,
- bool has_tex_clamp_rect,
- bool tint_color,
- bool rounded_corner);
-
- // TODO(ccameron): Merge |mask_for_background| into MaskMode.
- static ProgramKey RenderPass(TexCoordPrecision precision,
- SamplerType sampler,
- BlendMode blend_mode,
- AAMode aa_mode,
- MaskMode mask_mode,
- bool mask_for_background,
- bool has_color_matrix,
- bool tint_color,
- bool rounded_corner);
- static ProgramKey VideoStream(TexCoordPrecision precision,
- bool rounded_corner);
- static ProgramKey YUVVideo(TexCoordPrecision precision,
- SamplerType sampler,
- YUVAlphaTextureMode yuv_alpha_texture_mode,
- UVTextureMode uv_texture_mode,
- bool tint_color,
- bool rounded_corner);
-
- bool operator==(const ProgramKey& other) const;
- bool operator!=(const ProgramKey& other) const;
-
- void SetColorTransform(const gfx::ColorTransform* transform);
-
- bool has_output_color_matrix() const { return has_output_color_matrix_; }
- void set_has_output_color_matrix(bool value) {
- has_output_color_matrix_ = value;
- }
- TexCoordPrecision tex_coord_precision() const { return precision_; }
-
- ProgramType type() const { return type_; }
-
- private:
- friend struct ProgramKeyHash;
- friend class Program;
-
- ProgramType type_ = PROGRAM_TYPE_DEBUG_BORDER;
- TexCoordPrecision precision_ = TEX_COORD_PRECISION_NA;
- SamplerType sampler_ = SAMPLER_TYPE_NA;
- BlendMode blend_mode_ = BLEND_MODE_NONE;
- AAMode aa_mode_ = NO_AA;
- bool is_opaque_ = false;
-
- PremultipliedAlphaMode premultiplied_alpha_ = PREMULTIPLIED_ALPHA;
- bool has_background_color_ = false;
-
- MaskMode mask_mode_ = NO_MASK;
- bool mask_for_background_ = false;
- bool has_color_matrix_ = false;
-
- YUVAlphaTextureMode yuv_alpha_texture_mode_ = YUV_NO_ALPHA_TEXTURE;
- UVTextureMode uv_texture_mode_ = UV_TEXTURE_MODE_NA;
-
- ColorConversionMode color_conversion_mode_ = COLOR_CONVERSION_MODE_NONE;
- raw_ptr<const gfx::ColorTransform> color_transform_ = nullptr;
-
- bool has_tex_clamp_rect_ = false;
-
- bool has_output_color_matrix_ = false;
- bool has_tint_color_matrix_ = false;
- bool has_rounded_corner_ = false;
-};
-
-struct ProgramKeyHash {
- size_t operator()(const ProgramKey& key) const {
- return (static_cast<size_t>(key.type_) << 0) ^
- (static_cast<size_t>(key.precision_) << 3) ^
- (static_cast<size_t>(key.sampler_) << 6) ^
- (static_cast<size_t>(key.blend_mode_) << 9) ^
- (static_cast<size_t>(key.aa_mode_) << 15) ^
- (static_cast<size_t>(key.is_opaque_) << 17) ^
- (static_cast<size_t>(key.premultiplied_alpha_) << 19) ^
- (static_cast<size_t>(key.has_background_color_) << 20) ^
- (static_cast<size_t>(key.mask_mode_) << 21) ^
- (static_cast<size_t>(key.mask_for_background_) << 22) ^
- (static_cast<size_t>(key.has_color_matrix_) << 23) ^
- (static_cast<size_t>(key.yuv_alpha_texture_mode_) << 24) ^
- (static_cast<size_t>(key.uv_texture_mode_) << 25) ^
- (static_cast<size_t>(key.color_conversion_mode_) << 26) ^
- (static_cast<size_t>(key.has_tex_clamp_rect_) << 28) ^
- (static_cast<size_t>(key.has_output_color_matrix_) << 29) ^
- (static_cast<size_t>(key.has_tint_color_matrix_) << 30) ^
- (static_cast<size_t>(key.has_rounded_corner_) << 31);
- }
-};
-
-class VIZ_SERVICE_EXPORT Program : public ProgramBindingBase {
- public:
- Program() {}
-
- Program(const Program&) = delete;
- Program& operator=(const Program&) = delete;
-
- void Initialize(ContextProvider* context_provider, const ProgramKey& key) {
- // Set parameters that are common to all sub-classes.
- vertex_shader_.aa_mode_ = key.aa_mode_;
- fragment_shader_.aa_mode_ = key.aa_mode_;
- fragment_shader_.blend_mode_ = key.blend_mode_;
- fragment_shader_.tex_coord_precision_ = key.precision_;
- fragment_shader_.sampler_type_ = key.sampler_;
- fragment_shader_.premultiply_alpha_mode_ = key.premultiplied_alpha_;
- fragment_shader_.mask_mode_ = key.mask_mode_;
- fragment_shader_.mask_for_background_ = key.mask_for_background_;
- fragment_shader_.color_conversion_mode_ = key.color_conversion_mode_;
- fragment_shader_.color_transform_ = key.color_transform_;
- fragment_shader_.has_output_color_matrix_ = key.has_output_color_matrix_;
- fragment_shader_.has_tint_color_matrix_ = key.has_tint_color_matrix_;
- fragment_shader_.has_rounded_corner_ = key.has_rounded_corner_;
-
- switch (key.type_) {
- case PROGRAM_TYPE_DEBUG_BORDER:
- InitializeDebugBorderProgram();
- break;
- case PROGRAM_TYPE_SOLID_COLOR:
- InitializeSolidColorProgram(key);
- break;
- case PROGRAM_TYPE_TILE:
- InitializeTileProgram(key);
- break;
- case PROGRAM_TYPE_TEXTURE:
- InitializeTextureProgram(key);
- break;
- case PROGRAM_TYPE_RENDER_PASS:
- InitializeRenderPassProgram(key);
- break;
- case PROGRAM_TYPE_VIDEO_STREAM:
- InitializeVideoStreamProgram(key);
- break;
- case PROGRAM_TYPE_YUV_VIDEO:
- InitializeYUVVideo(key);
- break;
- }
- InitializeInternal(context_provider);
- }
-
- const VertexShader& vertex_shader() const { return vertex_shader_; }
- const FragmentShader& fragment_shader() const { return fragment_shader_; }
-
- // Functions for querying uniform locations.
- int vertex_tex_transform_location() const {
- return vertex_shader_.vertex_tex_transform_location_;
- }
- int tex_matrix_location() const {
- return vertex_shader_.tex_matrix_location_;
- }
- int ya_tex_scale_location() const {
- return vertex_shader_.ya_tex_scale_location_;
- }
- int ya_tex_offset_location() const {
- return vertex_shader_.ya_tex_offset_location_;
- }
- int uv_tex_scale_location() const {
- return vertex_shader_.uv_tex_scale_location_;
- }
- int uv_tex_offset_location() const {
- return vertex_shader_.uv_tex_offset_location_;
- }
- int matrix_location() const { return vertex_shader_.matrix_location_; }
- int vertex_opacity_location() const {
- return vertex_shader_.vertex_opacity_location_;
- }
- int viewport_location() const { return vertex_shader_.viewport_location_; }
- int edge_location() const { return vertex_shader_.edge_location_; }
- int quad_location() const { return vertex_shader_.quad_location_; }
-
- int sampler_location() const { return fragment_shader_.sampler_location_; }
- int alpha_location() const { return fragment_shader_.alpha_location_; }
- int color_location() const { return fragment_shader_.color_location_; }
- int background_color_location() const {
- return fragment_shader_.background_color_location_;
- }
- int fragment_tex_transform_location() const {
- return fragment_shader_.fragment_tex_transform_location_;
- }
- int backdrop_location() const { return fragment_shader_.backdrop_location_; }
- int backdrop_rect_location() const {
- return fragment_shader_.backdrop_rect_location_;
- }
- int original_backdrop_location() const {
- return fragment_shader_.original_backdrop_location_;
- }
- int mask_sampler_location() const {
- return fragment_shader_.mask_sampler_location_;
- }
- int mask_tex_coord_scale_location() const {
- return fragment_shader_.mask_tex_coord_scale_location_;
- }
- int mask_tex_coord_offset_location() const {
- return fragment_shader_.mask_tex_coord_offset_location_;
- }
- int color_matrix_location() const {
- return fragment_shader_.color_matrix_location_;
- }
- int color_offset_location() const {
- return fragment_shader_.color_offset_location_;
- }
- int tex_clamp_rect_location() const {
- return fragment_shader_.tex_clamp_rect_location_;
- }
- int y_texture_location() const {
- return fragment_shader_.y_texture_location_;
- }
- int u_texture_location() const {
- return fragment_shader_.u_texture_location_;
- }
- int v_texture_location() const {
- return fragment_shader_.v_texture_location_;
- }
- int uv_texture_location() const {
- return fragment_shader_.uv_texture_location_;
- }
- int a_texture_location() const {
- return fragment_shader_.a_texture_location_;
- }
- int resource_multiplier_location() const {
- return fragment_shader_.resource_multiplier_location_;
- }
- int resource_offset_location() const {
- return fragment_shader_.resource_offset_location_;
- }
- int ya_clamp_rect_location() const {
- return fragment_shader_.ya_clamp_rect_location_;
- }
- int uv_clamp_rect_location() const {
- return fragment_shader_.uv_clamp_rect_location_;
- }
- int output_color_matrix_location() const {
- return fragment_shader_.output_color_matrix_location_;
- }
- int tint_color_matrix_location() const {
- return fragment_shader_.tint_color_matrix_location_;
- }
- int rounded_corner_rect_location() const {
- return fragment_shader_.rounded_corner_rect_location_;
- }
- int rounded_corner_radius_location() const {
- return fragment_shader_.rounded_corner_radius_location_;
- }
-
- const gfx::ColorTransform* color_transform_for_testing() const {
- return fragment_shader_.color_transform_;
- }
-
- private:
- void InitializeDebugBorderProgram() {
- // Initialize fragment program.
- fragment_shader_.input_color_type_ = INPUT_COLOR_SOURCE_UNIFORM;
- fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_DEFAULT;
- }
-
- void InitializeSolidColorProgram(const ProgramKey& key) {
- // Initialize vertex program.
- vertex_shader_.position_source_ = POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM;
-#if BUILDFLAG(IS_ANDROID)
- if (key.aa_mode_ == NO_AA)
- vertex_shader_.has_dummy_variables_ = true;
-#endif
-
- // Initialize fragment program.
- fragment_shader_.input_color_type_ = INPUT_COLOR_SOURCE_UNIFORM;
- fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_DEFAULT;
- }
-
- void InitializeTileProgram(const ProgramKey& key) {
- // Initialize vertex program.
- vertex_shader_.position_source_ = POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM;
- vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_VEC4;
- vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE;
-
- // Initialize fragment program.
- fragment_shader_.has_tex_clamp_rect_ = key.has_tex_clamp_rect_;
- if (key.is_opaque_) {
- DCHECK_EQ(key.aa_mode_, NO_AA);
- fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_OPAQUE;
- } else {
- // TODO(ccameron): This branch shouldn't be needed (this is always
- // BLEND_MODE_NONE).
- if (key.aa_mode_ == NO_AA)
- fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_APPLY_BLEND_MODE;
- fragment_shader_.has_uniform_alpha_ = true;
- }
- // AA changes the texture coordinate mode (affecting both shaders).
- if (key.aa_mode_ == USE_AA) {
- vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_POSITION;
- vertex_shader_.aa_mode_ = USE_AA;
- fragment_shader_.has_rgba_fragment_tex_transform_ = true;
- // Tiles that have AA do their own clamping.
- DCHECK(!fragment_shader_.has_tex_clamp_rect_);
- }
- }
-
- void InitializeTextureProgram(const ProgramKey& key) {
- // Initialize vertex program.
- vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE;
- vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_VEC4;
- vertex_shader_.has_vertex_opacity_ = true;
- vertex_shader_.use_uniform_arrays_ = !key.has_tex_clamp_rect_;
-
- // Initialize fragment program.
- fragment_shader_.has_varying_alpha_ = true;
- fragment_shader_.has_background_color_ = key.has_background_color_;
- fragment_shader_.has_tex_clamp_rect_ = key.has_tex_clamp_rect_;
- }
-
- void InitializeRenderPassProgram(const ProgramKey& key) {
- // Initialize vertex program.
- if (key.aa_mode_ == NO_AA) {
- vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE;
- vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_VEC4;
- vertex_shader_.has_vertex_opacity_ = true;
- vertex_shader_.use_uniform_arrays_ = true;
- } else {
- vertex_shader_.position_source_ =
- POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM;
- vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_POSITION;
- vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_TRANSLATED_VEC4;
- }
-
- // Initialize fragment program.
- fragment_shader_.frag_color_mode_ = FRAG_COLOR_MODE_APPLY_BLEND_MODE;
- fragment_shader_.has_uniform_alpha_ = true;
- fragment_shader_.has_color_matrix_ = key.has_color_matrix_;
- if (key.mask_mode_ == HAS_MASK) {
- fragment_shader_.ignore_sampler_type_ = true;
- } else {
- DCHECK(!key.mask_for_background_);
- }
- }
-
- void InitializeVideoStreamProgram(const ProgramKey& key) {
- vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE;
- vertex_shader_.tex_coord_transform_ = TEX_COORD_TRANSFORM_MATRIX;
- DCHECK_EQ(key.sampler_, SAMPLER_TYPE_EXTERNAL_OES);
- }
-
- void InitializeYUVVideo(const ProgramKey& key) {
- vertex_shader_.tex_coord_source_ = TEX_COORD_SOURCE_ATTRIBUTE;
- vertex_shader_.is_ya_uv_ = true;
-
- fragment_shader_.input_color_type_ = INPUT_COLOR_SOURCE_YUV_TEXTURES;
- fragment_shader_.has_uniform_alpha_ = true;
- fragment_shader_.yuv_alpha_texture_mode_ = key.yuv_alpha_texture_mode_;
- fragment_shader_.uv_texture_mode_ = key.uv_texture_mode_;
- }
-
- void InitializeInternal(ContextProvider* context_provider) {
- DCHECK(context_provider);
- DCHECK(!initialized_);
-
- if (IsContextLost(context_provider->ContextGL()))
- return;
-
- if (!ProgramBindingBase::Init(context_provider->ContextGL(),
- vertex_shader_.GetShaderString(),
- fragment_shader_.GetShaderString())) {
- DCHECK(IsContextLost(context_provider->ContextGL()));
- return;
- }
-
- int base_uniform_index = 0;
- vertex_shader_.Init(context_provider->ContextGL(), program_,
- &base_uniform_index);
- fragment_shader_.Init(context_provider->ContextGL(), program_,
- &base_uniform_index);
-
- // Link after binding uniforms
- if (!Link(context_provider->ContextGL())) {
- DCHECK(IsContextLost(context_provider->ContextGL()));
- return;
- }
-
- initialized_ = true;
- }
-
- VertexShader vertex_shader_;
- FragmentShader fragment_shader_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_PROGRAM_BINDING_H_
diff --git a/chromium/components/viz/service/display/renderer_perftest.cc b/chromium/components/viz/service/display/renderer_perftest.cc
index 827da3a6da0..1ff1f6f4406 100644
--- a/chromium/components/viz/service/display/renderer_perftest.cc
+++ b/chromium/components/viz/service/display/renderer_perftest.cc
@@ -4,12 +4,11 @@
// This perf test measures the time from when the display compositor starts
// drawing on the compositor thread to when a swap buffers occurs on the
-// GPU main thread. It tests both GLRenderer and SkiaRenderer under
-// simple work loads.
+// GPU main thread.
//
// Example usage:
//
-// $ out/release/viz_perftests --gtest_filter="*RendererPerfTest*" \
+// $ out/release/viz_perftests --gtest_filter="RendererPerfTest*" \
// --use-gpu-in-tests --test-launcher-timeout=300000 \
// --perf-test-time-ms=240000 --disable_discard_framebuffer=1 \
// --use_virtualized_gl_contexts=1
@@ -26,24 +25,22 @@
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/display/renderer_settings.h"
#include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/quads/yuv_video_draw_quad.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/service/display/display.h"
-#include "components/viz/service/display/gl_renderer.h"
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/overlay_processor_stub.h"
#include "components/viz/service/display/skia_renderer.h"
#include "components/viz/service/display/viz_perftest.h"
-#include "components/viz/service/display_embedder/gl_output_surface_offscreen.h"
-#include "components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/display_embedder/skia_output_surface_dependency_impl.h"
#include "components/viz/service/display_embedder/skia_output_surface_impl.h"
-#include "components/viz/service/display_embedder/viz_process_context_provider.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/test_gpu_service_holder.h"
+#include "components/viz/test/test_in_process_context_provider.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -229,7 +226,6 @@ void CreateTestTileDrawQuad(ResourceId resource_id,
} // namespace
-template <typename RendererType>
class RendererPerfTest : public VizPerfTest {
public:
RendererPerfTest()
@@ -243,19 +239,15 @@ class RendererPerfTest : public VizPerfTest {
RendererPerfTest(const RendererPerfTest&) = delete;
RendererPerfTest& operator=(const RendererPerfTest&) = delete;
- // Overloaded for concrete RendererType below.
- std::unique_ptr<OutputSurface> CreateOutputSurface(
+ std::unique_ptr<SkiaOutputSurface> CreateOutputSurface(
GpuServiceImpl* gpu_service,
- DisplayCompositorMemoryAndTaskController* display_controller);
+ DisplayCompositorMemoryAndTaskController* display_controller) {
+ return SkiaOutputSurfaceImpl::Create(display_controller, renderer_settings_,
+ &debug_settings_);
+ }
void SetUp() override {
enable_pixel_output_ = std::make_unique<gl::DisableNullDrawGLBindings>();
- renderer_settings_.use_skia_renderer =
- std::is_base_of<SkiaRenderer, RendererType>::value;
- if (renderer_settings_.use_skia_renderer)
- printf("Using SkiaRenderer\n");
- else
- printf("Using GLRenderer\n");
#if BUILDFLAG(IS_ANDROID)
renderer_settings_.color_space = gfx::ColorSpace::CreateSRGB();
@@ -264,42 +256,18 @@ class RendererPerfTest : public VizPerfTest {
auto* gpu_service = TestGpuServiceHolder::GetInstance()->gpu_service();
- gpu_memory_buffer_manager_ =
- std::make_unique<InProcessGpuMemoryBufferManager>(
- gpu_service->gpu_memory_buffer_factory(),
- gpu_service->sync_point_manager());
- gpu::ImageFactory* image_factory = gpu_service->gpu_image_factory();
- auto* gpu_channel_manager_delegate =
- gpu_service->gpu_channel_manager()->delegate();
- auto child_task_scheduler = std::make_unique<gpu::GpuTaskSchedulerHelper>(
- TestGpuServiceHolder::GetInstance()->task_executor());
- child_gpu_dependency_ =
- std::make_unique<DisplayCompositorMemoryAndTaskController>(
- TestGpuServiceHolder::GetInstance()->task_executor(),
- image_factory);
- child_context_provider_ = base::MakeRefCounted<VizProcessContextProvider>(
- TestGpuServiceHolder::GetInstance()->task_executor(),
- gpu::kNullSurfaceHandle, gpu_memory_buffer_manager_.get(),
- image_factory, gpu_channel_manager_delegate,
- child_gpu_dependency_.get(), renderer_settings_);
+ child_context_provider_ =
+ base::MakeRefCounted<TestInProcessContextProvider>(
+ TestContextType::kGLES2, /*support_locking=*/false);
child_context_provider_->BindToCurrentThread();
child_resource_provider_ = std::make_unique<ClientResourceProvider>();
- std::unique_ptr<DisplayCompositorMemoryAndTaskController>
- display_controller;
- if (renderer_settings_.use_skia_renderer) {
- auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>(
- gpu_service, gpu::kNullSurfaceHandle);
- display_controller =
- std::make_unique<DisplayCompositorMemoryAndTaskController>(
- std::move(skia_deps));
- } else {
- auto* task_executor =
- TestGpuServiceHolder::GetInstance()->task_executor();
- display_controller =
- std::make_unique<DisplayCompositorMemoryAndTaskController>(
- task_executor, image_factory);
- }
+ auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>(
+ gpu_service, gpu::kNullSurfaceHandle);
+ auto display_controller =
+ std::make_unique<DisplayCompositorMemoryAndTaskController>(
+ std::move(skia_deps));
+
auto output_surface =
CreateOutputSurface(gpu_service, display_controller.get());
// WaitForSwapDisplayClient depends on this.
@@ -320,8 +288,7 @@ class RendererPerfTest : public VizPerfTest {
void TearDown() override {
std::string story =
- renderer_settings_.use_skia_renderer ? "SkiaRenderer_" : "GLRenderer_";
- story += ::testing::UnitTest::GetInstance()->current_test_info()->name();
+ ::testing::UnitTest::GetInstance()->current_test_info()->name();
auto reporter = SetUpRendererReporter(story);
reporter.AddResult(kMetricFps, timer_.LapsPerSecond());
@@ -358,8 +325,6 @@ class RendererPerfTest : public VizPerfTest {
child_resource_provider_->ShutdownAndReleaseAllResources();
child_resource_provider_.reset();
child_context_provider_.reset();
- child_gpu_dependency_.reset();
- gpu_memory_buffer_manager_.reset();
display_.reset();
}
@@ -661,69 +626,37 @@ class RendererPerfTest : public VizPerfTest {
ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl manager_;
std::unique_ptr<CompositorFrameSinkSupport> support_;
- std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_;
RendererSettings renderer_settings_;
DebugRendererSettings debug_settings_;
std::unique_ptr<Display> display_;
- std::unique_ptr<DisplayCompositorMemoryAndTaskController>
- child_gpu_dependency_;
scoped_refptr<ContextProvider> child_context_provider_;
std::unique_ptr<ClientResourceProvider> child_resource_provider_;
std::vector<TransferableResource> resource_list_;
std::unique_ptr<gl::DisableNullDrawGLBindings> enable_pixel_output_;
};
-template <>
-std::unique_ptr<OutputSurface>
-RendererPerfTest<SkiaRenderer>::CreateOutputSurface(
- GpuServiceImpl* gpu_service,
- DisplayCompositorMemoryAndTaskController* display_controller) {
- return SkiaOutputSurfaceImpl::Create(
- display_controller, renderer_settings_, &debug_settings_);
-}
-
-template <>
-std::unique_ptr<OutputSurface>
-RendererPerfTest<GLRenderer>::CreateOutputSurface(
- GpuServiceImpl* gpu_service,
- DisplayCompositorMemoryAndTaskController* display_controller) {
- gpu::ImageFactory* image_factory = gpu_service->gpu_image_factory();
- auto* gpu_channel_manager_delegate =
- gpu_service->gpu_channel_manager()->delegate();
- auto context_provider = base::MakeRefCounted<VizProcessContextProvider>(
- TestGpuServiceHolder::GetInstance()->task_executor(),
- gpu::kNullSurfaceHandle, gpu_memory_buffer_manager_.get(), image_factory,
- gpu_channel_manager_delegate, display_controller, renderer_settings_);
- context_provider->BindToCurrentThread();
- return std::make_unique<GLOutputSurfaceOffscreen>(
- std::move(context_provider));
-}
-
-using RendererTypes = ::testing::Types<GLRenderer, SkiaRenderer>;
-TYPED_TEST_SUITE(RendererPerfTest, RendererTypes);
-
-TYPED_TEST(RendererPerfTest, SingleTextureQuad) {
+TEST_F(RendererPerfTest, SingleTextureQuad) {
this->RunSingleTextureQuad();
}
-TYPED_TEST(RendererPerfTest, TextureQuads5x5) {
+TEST_F(RendererPerfTest, TextureQuads5x5) {
this->RunTextureQuads5x5();
}
-TYPED_TEST(RendererPerfTest, TextureQuads5x5SameTex) {
+TEST_F(RendererPerfTest, TextureQuads5x5SameTex) {
this->RunTextureQuads5x5SameTex();
}
-TYPED_TEST(RendererPerfTest, RotatedTileQuadsShared) {
+TEST_F(RendererPerfTest, RotatedTileQuadsShared) {
this->RunRotatedTileQuadsShared();
}
-TYPED_TEST(RendererPerfTest, RotatedTileQuads) {
+TEST_F(RendererPerfTest, RotatedTileQuads) {
this->RunRotatedTileQuads();
}
#define TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(SITE, FRAME) \
- TYPED_TEST(RendererPerfTest, SITE) { \
+ TEST_F(RendererPerfTest, SITE) { \
this->RunSingleRenderPassListFromJSON(/*tag=*/"top_real_world_desktop", \
/*site=*/#SITE, /*year=*/2018, \
/*frame_index=*/FRAME); \
diff --git a/chromium/components/viz/service/display/renderer_pixeltest.cc b/chromium/components/viz/service/display/renderer_pixeltest.cc
index 15651bf9388..32312f8cd58 100644
--- a/chromium/components/viz/service/display/renderer_pixeltest.cc
+++ b/chromium/components/viz/service/display/renderer_pixeltest.cc
@@ -29,20 +29,22 @@
#include "cc/test/test_types.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/features.h"
+#include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
+#include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
#include "components/viz/common/quads/picture_draw_quad.h"
+#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/quads/yuv_video_draw_quad.h"
#include "components/viz/common/resources/bitmap_allocation.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/switches.h"
#include "components/viz/service/display/delegated_ink_point_pixel_test_helper.h"
-#include "components/viz/service/display/gl_renderer.h"
#include "components/viz/service/display/software_renderer.h"
#include "components/viz/service/display/viz_pixel_test.h"
#include "components/viz/test/buildflags.h"
#include "components/viz/test/test_in_process_context_provider.h"
#include "components/viz/test/test_shared_bitmap_manager.h"
#include "components/viz/test/test_types.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "media/base/video_frame.h"
@@ -58,8 +60,6 @@
#include "ui/gfx/geometry/mask_filter_info.h"
#include "ui/gfx/test/icc_profiles.h"
-using gpu::gles2::GLES2Interface;
-
namespace viz {
namespace {
@@ -905,27 +905,6 @@ INSTANTIATE_TEST_SUITE_P(,
// GetGpuRendererTypesNoDawn() can return an empty list, e.g. on Fuchsia ARM64.
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GPURendererPixelTest);
-// Provides an exact comparator for GLRenderer and fuzzy comparator for Skia
-// based (eg. SoftwareRenderer and SkiaRenderer).
-class FuzzyForSkiaOnlyPixelComparator : public cc::PixelComparator {
- public:
- explicit FuzzyForSkiaOnlyPixelComparator(RendererType type) {
- if (type == RendererType::kGL) {
- comparator_ = std::make_unique<cc::ExactPixelComparator>(false);
- } else {
- comparator_ = std::make_unique<cc::FuzzyPixelOffByOneComparator>(false);
- }
- }
-
- bool Compare(const SkBitmap& actual_bmp,
- const SkBitmap& expected_bmp) const override {
- return comparator_->Compare(actual_bmp, expected_bmp);
- }
-
- private:
- std::unique_ptr<cc::PixelComparator> comparator_;
-};
-
TEST_P(RendererPixelTest, SimpleGreenRect) {
gfx::Rect rect(this->device_viewport_size_);
@@ -1123,6 +1102,13 @@ TEST_P(RendererPixelTest,
}
TEST_P(RendererPixelTest, TextureDrawQuadVisibleRectInsetBottomRight) {
+#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)
+ // Test is flaking with failed large allocations under TSAN when using
+ // SkiaRenderer with GL backend. See https://crbug.com/1320955.
+ if (renderer_type() == RendererType::kSkiaGL)
+ return;
+#endif
+
gfx::Rect rect(this->device_viewport_size_);
AggregatedRenderPassId id{1};
@@ -1781,25 +1767,16 @@ class VideoRendererPixelTestBase : public VizPixelTest {
};
#if BUILDFLAG(ENABLE_GL_BACKEND_TESTS)
-class VideoRendererPixelHiLoTest
- : public VideoRendererPixelTestBase,
- public testing::WithParamInterface<std::tuple<RendererType, bool>> {
+class VideoRendererPixelHiLoTest : public VideoRendererPixelTestBase,
+ public testing::WithParamInterface<bool> {
public:
VideoRendererPixelHiLoTest()
- : VideoRendererPixelTestBase(std::get<0>(GetParam())) {}
+ : VideoRendererPixelTestBase(RendererType::kSkiaGL) {}
- bool IsHighbit() const { return std::get<1>(GetParam()); }
+ bool IsHighbit() const { return GetParam(); }
};
-INSTANTIATE_TEST_SUITE_P(,
- VideoRendererPixelHiLoTest,
- testing::Combine(testing::Values(
-#if BUILDFLAG(ENABLE_GL_RENDERER_TESTS)
- RendererType::kGL,
-#endif
- RendererType::kSkiaGL),
- testing::Bool()),
- cc::PrintTupleToStringParamName());
+INSTANTIATE_TEST_SUITE_P(, VideoRendererPixelHiLoTest, testing::Bool());
TEST_P(VideoRendererPixelHiLoTest, SimpleYUVRect) {
gfx::Rect rect(this->device_viewport_size_);
@@ -2215,7 +2192,7 @@ TEST_P(RendererPixelTest, FastPassColorFilterAlpha) {
// renderer so use a fuzzy comparator.
EXPECT_TRUE(this->RunPixelTest(
&pass_list, base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha.png")),
- FuzzyForSkiaOnlyPixelComparator(renderer_type())));
+ cc::FuzzyPixelOffByOneComparator(false)));
}
TEST_P(RendererPixelTest, FastPassSaturateFilter) {
@@ -2275,7 +2252,7 @@ TEST_P(RendererPixelTest, FastPassSaturateFilter) {
// renderer so use a fuzzy comparator.
EXPECT_TRUE(this->RunPixelTest(
&pass_list, base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha.png")),
- FuzzyForSkiaOnlyPixelComparator(renderer_type())));
+ cc::FuzzyPixelOffByOneComparator(false)));
}
TEST_P(RendererPixelTest, FastPassFilterChain) {
@@ -2337,7 +2314,7 @@ TEST_P(RendererPixelTest, FastPassFilterChain) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
base::FilePath(FILE_PATH_LITERAL("blue_yellow_filter_chain.png")),
- FuzzyForSkiaOnlyPixelComparator(renderer_type())));
+ cc::FuzzyPixelOffByOneComparator(false)));
}
TEST_P(RendererPixelTest, FastPassColorFilterAlphaTranslation) {
@@ -2420,7 +2397,7 @@ TEST_P(RendererPixelTest, FastPassColorFilterAlphaTranslation) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha_translate.png")),
- FuzzyForSkiaOnlyPixelComparator(renderer_type())));
+ cc::FuzzyPixelOffByOneComparator(false)));
}
TEST_P(RendererPixelTest, EnlargedRenderPassTexture) {
@@ -3083,11 +3060,6 @@ TEST_P(RendererPixelTestWithBackdropFilter, InvertFilter) {
}
TEST_P(RendererPixelTestWithBackdropFilter, InvertFilterWithMask) {
- // TODO(crbug.com/989312): Delete this condition with GLRendere. The mask
- // appears to be offset from the correct location but this isn't relevant.
- if (is_gl_renderer())
- return;
-
this->backdrop_filters_.Append(cc::FilterOperation::CreateInvertFilter(1.f));
this->filter_pass_layer_rect_ = gfx::Rect(this->device_viewport_size_);
this->filter_pass_layer_rect_.Inset(gfx::Insets::TLBR(14, 12, 18, 16));
@@ -3105,170 +3077,6 @@ TEST_P(RendererPixelTestWithBackdropFilter, InvertFilterWithMask) {
cc::FuzzyPixelOffByOneComparator(false)));
}
-#if BUILDFLAG(ENABLE_GL_RENDERER_TESTS)
-class GLRendererPixelTestWithBackdropFilter : public VizPixelTest {
- public:
- GLRendererPixelTestWithBackdropFilter() : VizPixelTest(RendererType::kGL) {}
-
- protected:
- void SetUpRenderPassList() {
- pass_list_.clear();
- gfx::Rect device_viewport_rect(this->device_viewport_size_);
-
- AggregatedRenderPassId root_id{1};
- auto root_pass = CreateTestRootRenderPass(root_id, device_viewport_rect);
- root_pass->has_transparent_background = false;
-
- gfx::Transform identity_quad_to_target_transform;
-
- AggregatedRenderPassId filter_pass_id{2};
- gfx::Transform transform_to_root;
- auto filter_pass = CreateTestRenderPass(
- filter_pass_id, filter_pass_layer_rect_, transform_to_root);
- filter_pass->backdrop_filters = this->backdrop_filters_;
- filter_pass->backdrop_filter_bounds = this->backdrop_filter_bounds_;
-
- // A non-visible quad in the filtering render pass.
- {
- SharedQuadState* shared_state = CreateTestSharedQuadState(
- identity_quad_to_target_transform, filter_pass_layer_rect_,
- filter_pass.get(), gfx::MaskFilterInfo());
- auto* color_quad =
- filter_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
- color_quad->SetNew(shared_state, filter_pass_layer_rect_,
- filter_pass_layer_rect_, SK_ColorTRANSPARENT, false);
- }
-
- {
- SharedQuadState* shared_state = CreateTestSharedQuadState(
- filter_pass_to_target_transform_, filter_pass_layer_rect_,
- filter_pass.get(), gfx::MaskFilterInfo());
- auto* filter_pass_quad =
- root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>();
- filter_pass_quad->SetAll(
- shared_state, filter_pass_layer_rect_, filter_pass_layer_rect_,
- /*needs_blending=*/true, filter_pass_id, kInvalidResourceId,
- gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(1.0f, 1.0f), // filters_scale
- gfx::PointF(), // filters_origin
- gfx::RectF(), // tex_coord_rect
- false, // force_anti_aliasing_off
- backdrop_filter_quality_, // backdrop_filter_quality
- intersects_damage_under_);
- }
-
- const int kGridWidth = device_viewport_rect.width() / 3;
- const int kGridHeight = device_viewport_rect.height() / 3;
- gfx::Rect left_rect =
- gfx::Rect(kGridWidth / 2, kGridHeight, kGridWidth, kGridHeight);
-
- SharedQuadState* shared_state = CreateTestSharedQuadState(
- identity_quad_to_target_transform, left_rect, root_pass.get(),
- gfx::MaskFilterInfo());
- auto* color_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
- color_quad->SetNew(shared_state, left_rect, left_rect, SK_ColorGREEN,
- false);
-
- gfx::Rect right_rect =
- gfx::Rect(kGridWidth * 3 / 2, kGridHeight, kGridWidth, kGridHeight);
- shared_state = CreateTestSharedQuadState(
- identity_quad_to_target_transform, right_rect, root_pass.get(),
- gfx::MaskFilterInfo());
- color_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
- color_quad->SetNew(shared_state, right_rect, right_rect, SK_ColorRED,
- false);
-
- shared_state = CreateTestSharedQuadState(
- identity_quad_to_target_transform, device_viewport_rect,
- root_pass.get(), gfx::MaskFilterInfo());
- auto* background_quad =
- root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
- background_quad->SetNew(shared_state, device_viewport_rect,
- device_viewport_rect, SK_ColorWHITE, false);
-
- pass_list_.push_back(std::move(filter_pass));
- pass_list_.push_back(std::move(root_pass));
- }
-
- AggregatedRenderPassList pass_list_;
- cc::FilterOperations backdrop_filters_;
- absl::optional<gfx::RRectF> backdrop_filter_bounds_;
- float backdrop_filter_quality_ = 1.0f;
- bool intersects_damage_under_ = true;
- gfx::Transform filter_pass_to_target_transform_;
- gfx::Rect filter_pass_layer_rect_;
-};
-
-TEST_F(GLRendererPixelTestWithBackdropFilter, FilterQuality) {
- this->backdrop_filters_.Append(cc::FilterOperation::CreateBlurFilter(2.0f));
- this->filter_pass_layer_rect_ = gfx::Rect(this->device_viewport_size_);
- this->backdrop_filter_bounds_ =
- gfx::RRectF(gfx::RectF(this->filter_pass_layer_rect_));
- this->backdrop_filter_quality_ = 1.0f;
- this->SetUpRenderPassList();
- EXPECT_TRUE(this->RunPixelTest(
- &this->pass_list_,
- base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_1.png")),
- cc::FuzzyPixelOffByOneComparator(true)));
-
- if (this->context_provider()->ContextCapabilities().major_version < 3)
- return;
- this->backdrop_filter_quality_ = 0.33f;
- this->SetUpRenderPassList();
- EXPECT_TRUE(this->RunPixelTest(
- &this->pass_list_,
- base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_2.png")),
- cc::FuzzyPixelOffByOneComparator(true)));
-}
-
-TEST_F(GLRendererPixelTestWithBackdropFilter, CachedResultOfBackdropFilter) {
- this->backdrop_filters_.Append(cc::FilterOperation::CreateBlurFilter(2.0f));
- this->filter_pass_layer_rect_ = gfx::Rect(this->device_viewport_size_);
- this->backdrop_filter_bounds_ =
- gfx::RRectF(gfx::RectF(this->filter_pass_layer_rect_));
- // Set the flag to use cached backdrop filtered texture. This makes the
- // GLRenderer cache backdrop filtered result.
- this->intersects_damage_under_ = false;
- this->SetUpRenderPassList();
-
- EXPECT_TRUE(this->RunPixelTest(
- &this->pass_list_,
- base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_1.png")),
- cc::FuzzyPixelOffByOneComparator(true)));
-
- // Same render pass list makes the GLRenderer to skip backdrop filter
- // calculation and use cached texture. This should correctly produce the
- // same output image.
- this->SetUpRenderPassList();
- EXPECT_TRUE(this->RunPixelTest(
- &this->pass_list_,
- base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_1.png")),
- cc::FuzzyPixelOffByOneComparator(true)));
-
- // To prove the cached texture is used, change a quad on the root pass which
- // is beneath the backdrop filter. The output image should still be the same
- // as before.
- this->SetUpRenderPassList();
- DrawQuad* background_quad = *pass_list_.back()->quad_list.rbegin();
- static_cast<SolidColorDrawQuad*>(background_quad)->color = SK_ColorYELLOW;
- EXPECT_TRUE(this->RunPixelTest(
- &this->pass_list_,
- base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_1.png")),
- cc::FuzzyPixelOffByOneComparator(true)));
-
- // Set |intersects_damage_under_| to true to make GLRenderer re-run the
- // backdrop filter calculation
- this->intersects_damage_under_ = true;
- this->SetUpRenderPassList();
- background_quad = *pass_list_.back()->quad_list.rbegin();
- static_cast<SolidColorDrawQuad*>(background_quad)->color = SK_ColorYELLOW;
- EXPECT_TRUE(this->RunPixelTest(
- &this->pass_list_,
- base::FilePath(FILE_PATH_LITERAL("gl_backdrop_filter_3.png")),
- cc::FuzzyPixelOffByOneComparator(true)));
-}
-#endif
-
// Software renderer does not support anti-aliased edges.
TEST_P(GPURendererPixelTest, AntiAliasing) {
gfx::Rect rect(this->device_viewport_size_);
@@ -4590,21 +4398,10 @@ TEST_P(RendererPixelTest, RoundedCornerSimpleSolidDrawQuad) {
AggregatedRenderPassList pass_list;
pass_list.push_back(std::move(root_pass));
- if (is_gl_renderer()) {
- // GL Renderer should have an exact match as that is the reference point.
- EXPECT_TRUE(this->RunPixelTest(
- &pass_list,
- base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")),
- cc::ExactPixelComparator(true)));
- } else {
- // Software/skia renderer uses skia rrect to create rounded corner clip.
- // This results in a different corner path due to a different anti aliasing
- // approach than the fragment shader in gl renderer.
- EXPECT_TRUE(this->RunPixelTest(
- &pass_list,
- base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")),
- cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0)));
- }
+ EXPECT_TRUE(this->RunPixelTest(
+ &pass_list,
+ base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")),
+ cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0)));
}
TEST_P(GPURendererPixelTest, RoundedCornerSimpleTextureDrawQuad) {
@@ -4659,21 +4456,10 @@ TEST_P(GPURendererPixelTest, RoundedCornerSimpleTextureDrawQuad) {
AggregatedRenderPassList pass_list;
pass_list.push_back(std::move(root_pass));
- if (is_gl_renderer()) {
- // GL Renderer should have an exact match as that is the reference point.
- EXPECT_TRUE(this->RunPixelTest(
- &pass_list,
- base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")),
- cc::ExactPixelComparator(true)));
- } else {
- // SkiaRenderer uses skia rrect to create rounded corner clip. This results
- // in a different corner path due to a different anti aliasing approach than
- // the fragment shader in gl renderer.
- EXPECT_TRUE(this->RunPixelTest(
- &pass_list,
- base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")),
- cc::FuzzyPixelComparator(true, 0.6f, 0.f, 255.f, 255, 0)));
- }
+ EXPECT_TRUE(this->RunPixelTest(
+ &pass_list,
+ base::FilePath(FILE_PATH_LITERAL("rounded_corner_simple.png")),
+ cc::FuzzyPixelComparator(true, 0.6f, 0.f, 255.f, 255, 0)));
}
TEST_P(RendererPixelTest, RoundedCornerOnRenderPass) {
@@ -4774,21 +4560,13 @@ TEST_P(RendererPixelTest, RoundedCornerMultiRadii) {
AggregatedRenderPassList pass_list;
pass_list.push_back(std::move(root_pass));
- if (is_gl_renderer()) {
- // GL Renderer should have an exact match as that is the reference point.
- EXPECT_TRUE(this->RunPixelTest(
- &pass_list,
- base::FilePath(FILE_PATH_LITERAL("rounded_corner_multi_radii.png")),
- cc::ExactPixelComparator(true)));
- } else {
- // Software/skia renderer uses skia rrect to create rounded corner clip.
- // This results in a different corner path due to a different anti aliasing
- // approach than the fragment shader in gl renderer.
- EXPECT_TRUE(this->RunPixelTest(
- &pass_list,
- base::FilePath(FILE_PATH_LITERAL("rounded_corner_multi_radii.png")),
- cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0)));
- }
+ // Software/skia renderer uses skia rrect to create rounded corner clip.
+ // This results in a different corner path due to a different anti aliasing
+ // approach than the fragment shader in gl renderer.
+ EXPECT_TRUE(this->RunPixelTest(
+ &pass_list,
+ base::FilePath(FILE_PATH_LITERAL("rounded_corner_multi_radii.png")),
+ cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0)));
}
TEST_P(RendererPixelTest, RoundedCornerMultipleQads) {
@@ -4867,21 +4645,11 @@ TEST_P(RendererPixelTest, RoundedCornerMultipleQads) {
AggregatedRenderPassList pass_list;
pass_list.push_back(std::move(root_pass));
- // GL Renderer should have an exact match as that is the reference point.
- // Software/skia renderer use skia rrect to create rounded corner clip.
- // This results in a different corner path due to a different anti aliasing
- // approach than the fragment shader in gl renderer.
- std::unique_ptr<cc::PixelComparator> comparator;
- comparator.reset(
- is_gl_renderer()
- ? static_cast<cc::PixelComparator*>(
- new cc::ExactPixelComparator(true))
- : static_cast<cc::PixelComparator*>(
- new cc::FuzzyPixelComparator(true, 0.55f, 0.f, 255.f, 255, 0)));
+ cc::FuzzyPixelComparator comparator(true, 0.55f, 0.f, 255.f, 255, 0);
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
base::FilePath(FILE_PATH_LITERAL("rounded_corner_multi_quad.png")),
- *comparator));
+ comparator));
}
class RendererPixelTestWithOverdrawFeedback : public VizPixelTestWithParam {
@@ -4913,19 +4681,12 @@ TEST_P(RendererPixelTestWithOverdrawFeedback, TranslucentRectangles) {
AggregatedRenderPassList pass_list;
pass_list.push_back(std::move(pass));
- if (is_gl_renderer()) {
- EXPECT_TRUE(this->RunPixelTest(
- &pass_list,
- base::FilePath(FILE_PATH_LITERAL("translucent_rectangles.png")),
- cc::ExactPixelComparator(true)));
- } else {
- // TODO(xing.xu): investigate why overdraw feedback has small difference
- // (http://crbug.com/909971)
- EXPECT_TRUE(this->RunPixelTest(
- &pass_list,
- base::FilePath(FILE_PATH_LITERAL("skia_translucent_rectangles.png")),
- cc::FuzzyPixelComparator(false, 2.f, 0.f, 256.f, 256, 0.f)));
- }
+ // TODO(xing.xu): investigate why overdraw feedback has small difference
+ // (http://crbug.com/909971)
+ EXPECT_TRUE(this->RunPixelTest(
+ &pass_list,
+ base::FilePath(FILE_PATH_LITERAL("translucent_rectangles.png")),
+ cc::FuzzyPixelComparator(false, 2.f, 0.f, 256.f, 256, 0.f)));
}
INSTANTIATE_TEST_SUITE_P(,
@@ -4962,28 +4723,13 @@ class ColorTransformPixelTest
}
this->display_color_spaces_ =
gfx::DisplayColorSpaces(this->dst_color_space_);
- float sdr_max_luminance_nits =
- this->display_color_spaces_.GetSDRMaxLuminanceNits();
- if (src_color_space_.GetSDRWhiteLevel(&sdr_max_luminance_nits)) {
- this->display_color_spaces_.SetSDRMaxLuminanceNits(
- sdr_max_luminance_nits);
- }
this->premultiplied_alpha_ = std::get<3>(GetParam());
}
void Basic() {
- // Skip piecewise transfer functions because SkColorSpace (needed for
- // CopyOutputResult::AsSkBitmap) doesn't support them..
- if ((src_color_space_.GetTransferID() == TransferID::PIECEWISE_HDR ||
- dst_color_space_.GetTransferID() == TransferID::PIECEWISE_HDR)) {
- LOG(ERROR) << "Skipping piecewise HDR function";
- return;
- }
-
if (src_color_space_.GetTransferID() == TransferID::PQ &&
!dst_color_space_.IsHDR()) {
- LOG(ERROR) << "Skipping tonemapped output";
- return;
+ GTEST_SKIP() << "Skipping tonemapped output";
}
gfx::Rect rect(this->device_viewport_size_);
@@ -5018,8 +4764,7 @@ class ColorTransformPixelTest
}
gfx::ColorTransform::Options options;
- options.sdr_max_luminance_nits =
- display_color_spaces_.GetSDRMaxLuminanceNits();
+ options.sdr_max_luminance_nits = gfx::ColorSpace::kDefaultSDRWhiteLevel;
std::unique_ptr<gfx::ColorTransform> transform =
gfx::ColorTransform::NewColorTransform(this->src_color_space_,
this->dst_color_space_, options);
@@ -5086,9 +4831,19 @@ class ColorTransformPixelTest
AggregatedRenderPassList pass_list;
pass_list.push_back(std::move(pass));
- // Allow a difference of 2 bytes in comparison for shader-based transforms,
- // and 4 bytes for LUT-based transforms (determined empirically).
- cc::FuzzyPixelComparator comparator(false, 100.f, 0.f, 2.f, 2, 0);
+ // Allow a difference of 2 bytes in comparison for most cases.
+ float avg_abs_error_limit = 2.0f;
+ int max_abs_error_limit = 2;
+#if BUILDFLAG(IS_FUCHSIA)
+ if (src_color_space_.GetTransferID() == TransferID::PQ) {
+ // Fuchsia+SwiftShader/Vulkan has higher error on some pixels with HDR
+ // color spaces. See https://crbug.com/1312141.
+ max_abs_error_limit = 5;
+ }
+#endif
+
+ cc::FuzzyPixelComparator comparator(false, 100.f, 0.f, avg_abs_error_limit,
+ max_abs_error_limit, 0);
EXPECT_TRUE(
this->RunPixelTest(&pass_list, &expected_output_colors, comparator))
<< " src:" << src_color_space_ << ", dst:" << dst_color_space_;
@@ -5099,13 +4854,14 @@ class ColorTransformPixelTest
bool premultiplied_alpha_ = false;
};
-// crbug.com/1312043 Disable the test due to flaky.
-#if (BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)) || BUILDFLAG(IS_FUCHSIA)
-#define MAYBE_Basic DISABLED_Basic
-#else
-#define MAYBE_Basic Basic
+TEST_P(ColorTransformPixelTest, Basic) {
+#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)
+ // Test is flaking with failed large allocations under TSAN when using
+ // SkiaRenderer with GL backend. See https://crbug.com/1320955.
+ if (renderer_type() == RendererType::kSkiaGL)
+ return;
#endif
-TEST_P(ColorTransformPixelTest, MAYBE_Basic) {
+
Basic();
}
@@ -5121,10 +4877,7 @@ gfx::ColorSpace src_color_spaces[] = {
gfx::ColorSpace(PrimaryID::BT709, TransferID::SMPTEST428_1),
gfx::ColorSpace(PrimaryID::BT709, TransferID::SRGB_HDR),
gfx::ColorSpace(PrimaryID::BT709, TransferID::LINEAR_HDR),
- // Piecewise HDR transfer functions skipped with SkiaRenderer.
- gfx::ColorSpace::CreatePiecewiseHDR(PrimaryID::BT709, 0.5, 1.5),
- gfx::ColorSpace::CreateHDR10(50.f),
- gfx::ColorSpace::CreateHDR10(250.f),
+ gfx::ColorSpace::CreateHDR10(),
};
gfx::ColorSpace dst_color_spaces[] = {
@@ -5138,8 +4891,6 @@ gfx::ColorSpace dst_color_spaces[] = {
gfx::ColorSpace(PrimaryID::BT709, TransferID::SRGB),
gfx::ColorSpace(PrimaryID::BT709, TransferID::SRGB_HDR),
gfx::ColorSpace(PrimaryID::BT709, TransferID::LINEAR_HDR),
- // Piecewise HDR transfer functions are skipped with SkiaRenderer.
- gfx::ColorSpace::CreatePiecewiseHDR(PrimaryID::BT709, 0.25, 2.5),
};
gfx::ColorSpace intermediate_color_spaces[] = {
diff --git a/chromium/components/viz/service/display/resource_fence.h b/chromium/components/viz/service/display/resource_fence.h
index dc4153ea490..59e1101247e 100644
--- a/chromium/components/viz/service/display/resource_fence.h
+++ b/chromium/components/viz/service/display/resource_fence.h
@@ -7,6 +7,8 @@
#include "base/memory/ref_counted.h"
+#include "ui/gfx/gpu_fence_handle.h"
+
namespace viz {
// An abstract interface used to ensure reading from resources passed between
@@ -16,8 +18,16 @@ class ResourceFence : public base::RefCountedThreadSafe<ResourceFence> {
ResourceFence(const ResourceFence&) = delete;
ResourceFence& operator=(const ResourceFence&) = delete;
+ // Notifies the fence is needed.
virtual void Set() = 0;
+ // Tells if the fence is ready.
virtual bool HasPassed() = 0;
+ // A release fence which availability depends on the type of resource fence
+ // (managed by DisplayResourceProvider and
+ // TransferableResource::synchronization_type). The client must ensure that
+ // HasPassed is true before trying to access the release fence handle.
+ // Otherwise, it's not guaranteed that the fence handle is valid.
+ virtual gfx::GpuFenceHandle GetGpuFenceHandle() = 0;
protected:
friend class base::RefCountedThreadSafe<ResourceFence>;
diff --git a/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc b/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc
deleted file mode 100644
index e7968ed7377..00000000000
--- a/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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 "components/viz/service/display/scoped_gpu_memory_buffer_texture.h"
-
-#include "base/check.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/common/resources/resource_format.h"
-#include "components/viz/common/resources/resource_format_utils.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
-
-namespace viz {
-
-ScopedGpuMemoryBufferTexture::ScopedGpuMemoryBufferTexture(
- ContextProvider* context_provider,
- const gfx::Size& size,
- const gfx::ColorSpace& color_space)
- : context_provider_(context_provider),
- size_(size),
- color_space_(color_space) {
- DCHECK(context_provider_);
-
- const auto& caps = context_provider->ContextCapabilities();
- // This capability is needed to use TexStorage2DImageCHROMIUM, and should be
- // known to be enabled before using an object of this type.
- DCHECK(caps.texture_storage_image);
-
- gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
- gl->GenTextures(1, &gl_id_);
-
- gfx::BufferUsage usage = gfx::BufferUsage::SCANOUT;
- ResourceFormat format = RGBA_8888;
- gfx::BufferFormat buffer_format = BufferFormat(format);
-
- target_ = gpu::GetBufferTextureTarget(usage, buffer_format, caps);
-
- gl->BindTexture(target_, gl_id_);
- gl->TexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl->TexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl->TexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- gl->TexStorage2DImageCHROMIUM(
- target_, TextureStorageFormat(format, caps.angle_rgbx_internal_format),
- GL_SCANOUT_CHROMIUM, size_.width(), size_.height());
- if (color_space_.IsValid()) {
- gl->SetColorSpaceMetadataCHROMIUM(gl_id_, color_space_.AsGLColorSpace());
- }
- gl->BindTexture(target_, 0);
-}
-
-ScopedGpuMemoryBufferTexture::ScopedGpuMemoryBufferTexture() = default;
-
-ScopedGpuMemoryBufferTexture::~ScopedGpuMemoryBufferTexture() {
- Free();
-}
-
-ScopedGpuMemoryBufferTexture::ScopedGpuMemoryBufferTexture(
- ScopedGpuMemoryBufferTexture&& other)
- : context_provider_(other.context_provider_),
- gl_id_(other.gl_id_),
- target_(other.target_),
- size_(other.size_),
- color_space_(other.color_space_) {
- other.gl_id_ = 0;
-}
-
-ScopedGpuMemoryBufferTexture& ScopedGpuMemoryBufferTexture::operator=(
- ScopedGpuMemoryBufferTexture&& other) {
- DCHECK(!context_provider_ || !other.context_provider_ ||
- context_provider_ == other.context_provider_);
- if (this != &other) {
- Free();
- context_provider_ = other.context_provider_;
- gl_id_ = other.gl_id_;
- target_ = other.target_;
- size_ = other.size_;
- color_space_ = other.color_space_;
-
- other.gl_id_ = 0;
- }
- return *this;
-}
-
-void ScopedGpuMemoryBufferTexture::Free() {
- if (!gl_id_)
- return;
- gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
- gl->DeleteTextures(1, &gl_id_);
- gl_id_ = 0;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.h b/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.h
deleted file mode 100644
index 1711224bcb1..00000000000
--- a/chromium/components/viz/service/display/scoped_gpu_memory_buffer_texture.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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 COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_GPU_MEMORY_BUFFER_TEXTURE_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_GPU_MEMORY_BUFFER_TEXTURE_H_
-
-#include "base/memory/raw_ptr.h"
-#include "components/viz/service/viz_service_export.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace viz {
-class ContextProvider;
-
-// ScopedGpuMemoryBufferTexture is a GL texture backed by a GL image and a
-// GpuMemoryBuffer, so that it can be used as an overlay.
-class VIZ_SERVICE_EXPORT ScopedGpuMemoryBufferTexture {
- public:
- explicit ScopedGpuMemoryBufferTexture(ContextProvider* context_provider,
- const gfx::Size& size,
- const gfx::ColorSpace& color_space);
-
- ScopedGpuMemoryBufferTexture();
- ~ScopedGpuMemoryBufferTexture();
-
- ScopedGpuMemoryBufferTexture(ScopedGpuMemoryBufferTexture&& other);
- ScopedGpuMemoryBufferTexture& operator=(ScopedGpuMemoryBufferTexture&& other);
-
- uint32_t id() const { return gl_id_; }
- uint32_t target() const { return target_; }
- const gfx::Size& size() const { return size_; }
- const gfx::ColorSpace& color_space() const { return color_space_; }
-
- private:
- void Free();
-
- // The ContextProvider used to free the texture when this object is destroyed,
- // so it must outlive this object.
- raw_ptr<ContextProvider> context_provider_ = nullptr;
- uint32_t gl_id_ = 0;
- uint32_t target_ = 0;
- gfx::Size size_;
- gfx::ColorSpace color_space_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_GPU_MEMORY_BUFFER_TEXTURE_H_
diff --git a/chromium/components/viz/service/display/scoped_render_pass_texture.cc b/chromium/components/viz/service/display/scoped_render_pass_texture.cc
deleted file mode 100644
index 9a668018e08..00000000000
--- a/chromium/components/viz/service/display/scoped_render_pass_texture.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// 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 "components/viz/service/display/scoped_render_pass_texture.h"
-
-#include <algorithm>
-
-#include "base/bits.h"
-#include "base/check.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/common/resources/resource_format_utils.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-
-namespace viz {
-
-ScopedRenderPassTexture::ScopedRenderPassTexture() = default;
-
-ScopedRenderPassTexture::ScopedRenderPassTexture(
- ContextProvider* context_provider,
- const gfx::Size& size,
- ResourceFormat format,
- const gfx::ColorSpace& color_space,
- bool mipmap)
- : context_provider_(context_provider),
- size_(size),
- mipmap_(mipmap),
- color_space_(color_space) {
- DCHECK(context_provider_);
- gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
- const gpu::Capabilities& caps = context_provider_->ContextCapabilities();
- gl->GenTextures(1, &gl_id_);
-
- gl->BindTexture(GL_TEXTURE_2D, gl_id_);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- // This texture will be bound as a framebuffer, so optimize for that.
- if (caps.texture_usage) {
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_USAGE_ANGLE,
- GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
- }
-
- if (caps.texture_storage) {
- GLint levels = 1;
- if (caps.texture_npot && mipmap_)
- levels += base::bits::Log2Floor(std::max(size_.width(), size_.height()));
-
- gl->TexStorage2DEXT(
- GL_TEXTURE_2D, levels,
- TextureStorageFormat(format, context_provider_->ContextCapabilities()
- .angle_rgbx_internal_format),
- size_.width(), size_.height());
- } else {
- DCHECK(GLSupportsFormat(format));
- gl->TexImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(format), size_.width(),
- size_.height(), 0, GLDataFormat(format), GLDataType(format),
- nullptr);
- }
-}
-
-ScopedRenderPassTexture::~ScopedRenderPassTexture() {
- Free();
-}
-
-ScopedRenderPassTexture::ScopedRenderPassTexture(
- ScopedRenderPassTexture&& other) {
- context_provider_ = other.context_provider_;
- size_ = other.size_;
- mipmap_ = other.mipmap_;
- color_space_ = other.color_space_;
- gl_id_ = other.gl_id_;
- mipmap_state_ = other.mipmap_state_;
-
- // When being moved, other will no longer hold this gl_id_.
- other.gl_id_ = 0;
-}
-
-ScopedRenderPassTexture& ScopedRenderPassTexture::operator=(
- ScopedRenderPassTexture&& other) {
- if (this != &other) {
- Free();
- context_provider_ = other.context_provider_;
- size_ = other.size_;
- mipmap_ = other.mipmap_;
- color_space_ = other.color_space_;
- gl_id_ = other.gl_id_;
- mipmap_state_ = other.mipmap_state_;
-
- // When being moved, other will no longer hold this gl_id_.
- other.gl_id_ = 0;
- }
- return *this;
-}
-
-void ScopedRenderPassTexture::Free() {
- if (!gl_id_)
- return;
- gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
- gl->DeleteTextures(1, &gl_id_);
- gl_id_ = 0;
-}
-
-void ScopedRenderPassTexture::BindForSampling() {
- gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
- gl->BindTexture(GL_TEXTURE_2D, gl_id_);
- switch (mipmap_state_) {
- case INVALID:
- break;
- case GENERATE:
- // TODO(crbug.com/803286): npot texture always return false on ubuntu
- // desktop. The npot texture check is probably failing on desktop GL.
- DCHECK(context_provider_->ContextCapabilities().texture_npot);
- gl->GenerateMipmap(GL_TEXTURE_2D);
- mipmap_state_ = VALID;
- [[fallthrough]];
- case VALID:
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- GL_LINEAR_MIPMAP_LINEAR);
- break;
- }
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/scoped_render_pass_texture.h b/chromium/components/viz/service/display/scoped_render_pass_texture.h
deleted file mode 100644
index da29d49b70b..00000000000
--- a/chromium/components/viz/service/display/scoped_render_pass_texture.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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 COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_RENDER_PASS_TEXTURE_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_RENDER_PASS_TEXTURE_H_
-
-#include "base/memory/raw_ptr.h"
-#include "components/viz/common/resources/resource_format.h"
-#include "components/viz/service/viz_service_export.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace viz {
-class ContextProvider;
-
-// ScopedRenderPassTexture is resource used inside the same GL context and will
-// not being sent into another process. So no need to create fence and mailbox
-// for these resources.
-class VIZ_SERVICE_EXPORT ScopedRenderPassTexture {
- public:
- ScopedRenderPassTexture();
- ScopedRenderPassTexture(ContextProvider* context_provider,
- const gfx::Size& size,
- ResourceFormat format,
- const gfx::ColorSpace& color_space,
- bool mipmap);
- ~ScopedRenderPassTexture();
-
- ScopedRenderPassTexture(ScopedRenderPassTexture&& other);
- ScopedRenderPassTexture& operator=(ScopedRenderPassTexture&& other);
- void BindForSampling();
-
- GLuint id() const { return gl_id_; }
- const gfx::Size& size() const { return size_; }
- bool mipmap() const { return mipmap_; }
- const gfx::ColorSpace& color_space() const { return color_space_; }
- void set_generate_mipmap() { mipmap_state_ = GENERATE; }
-
- private:
- void Free();
-
- raw_ptr<ContextProvider> context_provider_ = nullptr;
- // The GL texture id.
- GLuint gl_id_ = 0;
- // Size of the resource in pixels.
- gfx::Size size_;
- // When true, and immutable textures are used, this specifies to
- // generate mipmaps at powers of 2.
- bool mipmap_ = false;
- // TODO(xing.xu): Remove this and set the color space when we draw the
- // CompositorRenderPassDrawQuad.
- gfx::ColorSpace color_space_;
- enum MipmapState { INVALID, GENERATE, VALID };
- MipmapState mipmap_state_ = INVALID;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SCOPED_RENDER_PASS_TEXTURE_H_
diff --git a/chromium/components/viz/service/display/shader.cc b/chromium/components/viz/service/display/shader.cc
deleted file mode 100644
index ac9e11564f7..00000000000
--- a/chromium/components/viz/service/display/shader.cc
+++ /dev/null
@@ -1,1168 +0,0 @@
-// Copyright 2011 The Chromium 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/viz/service/display/shader.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <utility>
-#include <vector>
-
-#include "base/check_op.h"
-#include "base/notreached.h"
-#include "base/strings/char_traits.h"
-#include "base/strings/strcat.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "components/viz/service/display/static_geometry_binding.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "ui/gfx/color_transform.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/size.h"
-
-constexpr base::StringPiece StripLambda(base::StringPiece shader) {
- // Must contain at least "[]() {}".
- DCHECK_EQ(shader.substr(0, 6), "[]() {");
- DCHECK_EQ(shader.back(), '}');
- shader.remove_prefix(6);
- shader.remove_suffix(1);
- return shader;
-}
-
-// Shaders are passed in with lambda syntax, which tricks clang-format into
-// handling them correctly. StripLambda removes this.
-#define SHADER0(Src) StripLambda(#Src)
-
-#define HDR(x) \
- do { \
- header += x "\n"; \
- } while (0)
-#define SRC(x) \
- do { \
- source += " " x "\n"; \
- } while (0)
-
-using gpu::gles2::GLES2Interface;
-
-namespace viz {
-
-namespace {
-
-static void GetProgramUniformLocations(GLES2Interface* context,
- unsigned program,
- size_t count,
- const char** uniforms,
- int* locations,
- int* base_uniform_index) {
- for (size_t i = 0; i < count; i++) {
- locations[i] = (*base_uniform_index)++;
- context->BindUniformLocationCHROMIUM(program, locations[i], uniforms[i]);
- }
-}
-
-static void SetFragmentTexCoordPrecision(TexCoordPrecision requested_precision,
- std::string* shader_string) {
- const char* prefix = "";
- switch (requested_precision) {
- case TEX_COORD_PRECISION_HIGH:
- DCHECK_NE(shader_string->find("TexCoordPrecision"), std::string::npos);
- prefix =
- "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
- " #define TexCoordPrecision highp\n"
- "#else\n"
- " #define TexCoordPrecision mediump\n"
- "#endif\n";
- break;
- case TEX_COORD_PRECISION_MEDIUM:
- DCHECK_NE(shader_string->find("TexCoordPrecision"), std::string::npos);
- prefix = "#define TexCoordPrecision mediump\n";
- break;
- case TEX_COORD_PRECISION_NA:
- DCHECK_EQ(shader_string->find("TexCoordPrecision"), std::string::npos);
- DCHECK_EQ(shader_string->find("texture2D"), std::string::npos);
- DCHECK_EQ(shader_string->find("texture2DRect"), std::string::npos);
- break;
- default:
- NOTREACHED();
- break;
- }
- const char* lut_prefix = "#define LutLookup texture2D\n";
- shader_string->insert(0, prefix);
- shader_string->insert(0, lut_prefix);
-}
-
-TexCoordPrecision TexCoordPrecisionRequired(GLES2Interface* context,
- int* highp_threshold_cache,
- int highp_threshold_min,
- int x,
- int y) {
- if (*highp_threshold_cache == 0) {
- // Initialize range and precision with minimum spec values for when
- // GetShaderPrecisionFormat is a test stub.
- // TODO(brianderson): Implement better stubs of GetShaderPrecisionFormat
- // everywhere.
- GLint range[2] = {14, 14};
- GLint precision = 10;
- context->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT,
- range, &precision);
- *highp_threshold_cache = 1 << precision;
- }
-
- int highp_threshold = std::max(*highp_threshold_cache, highp_threshold_min);
- if (x > highp_threshold || y > highp_threshold)
- return TEX_COORD_PRECISION_HIGH;
- return TEX_COORD_PRECISION_MEDIUM;
-}
-
-void SetFragmentSamplerType(SamplerType requested_type,
- std::string* shader_string) {
- const char* prefix = nullptr;
- switch (requested_type) {
- case SAMPLER_TYPE_2D:
- DCHECK_NE(shader_string->find("SamplerType"), std::string::npos);
- DCHECK_NE(shader_string->find("TextureLookup"), std::string::npos);
- prefix =
- "#define SamplerType sampler2D\n"
- "#define TextureLookup texture2D\n";
- break;
- case SAMPLER_TYPE_2D_RECT:
- DCHECK_NE(shader_string->find("SamplerType"), std::string::npos);
- DCHECK_NE(shader_string->find("TextureLookup"), std::string::npos);
- prefix =
- "#extension GL_ARB_texture_rectangle : require\n"
- "#define SamplerType sampler2DRect\n"
- "#define TextureLookup texture2DRect\n";
- break;
- case SAMPLER_TYPE_EXTERNAL_OES:
- DCHECK_NE(shader_string->find("SamplerType"), std::string::npos);
- DCHECK_NE(shader_string->find("TextureLookup"), std::string::npos);
- prefix =
- "#extension GL_OES_EGL_image_external : enable\n"
- "#extension GL_NV_EGL_stream_consumer_external : enable\n"
- "#define SamplerType samplerExternalOES\n"
- "#define TextureLookup texture2D\n";
- break;
- case SAMPLER_TYPE_NA:
- DCHECK_EQ(shader_string->find("SamplerType"), std::string::npos);
- DCHECK_EQ(shader_string->find("TextureLookup"), std::string::npos);
- return;
- default:
- NOTREACHED();
- return;
- }
- shader_string->insert(0, prefix);
-}
-
-} // namespace
-
-TexCoordPrecision TexCoordPrecisionRequired(GLES2Interface* context,
- int* highp_threshold_cache,
- int highp_threshold_min,
- const gfx::Point& max_coordinate) {
- return TexCoordPrecisionRequired(context, highp_threshold_cache,
- highp_threshold_min, max_coordinate.x(),
- max_coordinate.y());
-}
-
-TexCoordPrecision TexCoordPrecisionRequired(GLES2Interface* context,
- int* highp_threshold_cache,
- int highp_threshold_min,
- const gfx::Size& max_size) {
- return TexCoordPrecisionRequired(context, highp_threshold_cache,
- highp_threshold_min, max_size.width(),
- max_size.height());
-}
-
-VertexShader::VertexShader() {}
-
-void VertexShader::Init(GLES2Interface* context,
- unsigned program,
- int* base_uniform_index) {
- std::vector<const char*> uniforms;
- std::vector<int> locations;
-
- switch (tex_coord_transform_) {
- case TEX_COORD_TRANSFORM_NONE:
- break;
- case TEX_COORD_TRANSFORM_VEC4:
- case TEX_COORD_TRANSFORM_TRANSLATED_VEC4:
- uniforms.push_back("vertexTexTransform");
- break;
- case TEX_COORD_TRANSFORM_MATRIX:
- uniforms.push_back("texMatrix");
- break;
- }
- if (is_ya_uv_) {
- uniforms.push_back("yaTexScale");
- uniforms.push_back("yaTexOffset");
- uniforms.push_back("uvTexScale");
- uniforms.push_back("uvTexOffset");
- }
- uniforms.push_back("matrix");
- if (has_vertex_opacity_)
- uniforms.push_back("opacity");
- if (aa_mode_ == USE_AA) {
- uniforms.push_back("viewport");
- uniforms.push_back("edge");
- }
- if (position_source_ == POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM)
- uniforms.push_back("quad");
-
- locations.resize(uniforms.size());
-
- GetProgramUniformLocations(context, program, uniforms.size(), uniforms.data(),
- locations.data(), base_uniform_index);
-
- size_t index = 0;
- switch (tex_coord_transform_) {
- case TEX_COORD_TRANSFORM_NONE:
- break;
- case TEX_COORD_TRANSFORM_VEC4:
- case TEX_COORD_TRANSFORM_TRANSLATED_VEC4:
- vertex_tex_transform_location_ = locations[index++];
- break;
- case TEX_COORD_TRANSFORM_MATRIX:
- tex_matrix_location_ = locations[index++];
- break;
- }
- if (is_ya_uv_) {
- ya_tex_scale_location_ = locations[index++];
- ya_tex_offset_location_ = locations[index++];
- uv_tex_scale_location_ = locations[index++];
- uv_tex_offset_location_ = locations[index++];
- }
- matrix_location_ = locations[index++];
- if (has_vertex_opacity_)
- vertex_opacity_location_ = locations[index++];
- if (aa_mode_ == USE_AA) {
- viewport_location_ = locations[index++];
- edge_location_ = locations[index++];
- }
- if (position_source_ == POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM)
- quad_location_ = locations[index++];
-}
-
-std::string VertexShader::GetShaderString() const {
- // We unconditionally use highp in the vertex shader since
- // we are unlikely to be vertex shader bound when drawing large quads.
- // Also, some vertex shaders mutate the texture coordinate in such a
- // way that the effective precision might be lower than expected.
- std::string header = "#define TexCoordPrecision highp\n";
- std::string source = "void main() {\n";
-
- // Define the size of quads for attribute indexed uniform arrays.
- if (use_uniform_arrays_) {
- header += base::StringPrintf("#define NUM_QUADS %d\n",
- StaticGeometryBinding::NUM_QUADS);
- }
-
- // Read the index variables.
- if (use_uniform_arrays_ || has_vertex_opacity_ ||
- position_source_ == POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM) {
- HDR("attribute float a_index;");
- SRC("// Compute indices for uniform arrays.");
- SRC("int vertex_index = int(a_index);");
- if (use_uniform_arrays_)
- SRC("int quad_index = int(a_index * 0.25);");
- SRC("");
- }
-
- // Read the position and compute gl_Position.
- HDR("attribute TexCoordPrecision vec4 a_position;");
- SRC("// Compute the position.");
- switch (position_source_) {
- case POSITION_SOURCE_ATTRIBUTE:
- SRC("vec4 pos = a_position;");
- break;
- case POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM:
- HDR("uniform TexCoordPrecision vec2 quad[4];");
- SRC("vec4 pos = vec4(quad[vertex_index], a_position.z, a_position.w);");
- break;
- }
- if (use_uniform_arrays_) {
- HDR("uniform mat4 matrix[NUM_QUADS];");
- SRC("gl_Position = matrix[quad_index] * pos;");
- } else {
- HDR("uniform mat4 matrix;");
- SRC("gl_Position = matrix * pos;");
- }
-
- // Compute the anti-aliasing edge distances.
- if (aa_mode_ == USE_AA) {
- HDR("uniform TexCoordPrecision vec3 edge[8];");
- HDR("uniform vec4 viewport;");
- HDR("varying TexCoordPrecision vec4 edge_dist[2]; // 8 edge distances.");
- SRC("// Compute anti-aliasing properties.\n");
- SRC("vec2 ndc_pos = 0.5 * (1.0 + gl_Position.xy / gl_Position.w);");
- SRC("vec3 screen_pos = vec3(viewport.xy + viewport.zw * ndc_pos, 1.0);");
- SRC("edge_dist[0] = vec4(dot(edge[0], screen_pos),");
- SRC(" dot(edge[1], screen_pos),");
- SRC(" dot(edge[2], screen_pos),");
- SRC(" dot(edge[3], screen_pos)) * gl_Position.w;");
- SRC("edge_dist[1] = vec4(dot(edge[4], screen_pos),");
- SRC(" dot(edge[5], screen_pos),");
- SRC(" dot(edge[6], screen_pos),");
- SRC(" dot(edge[7], screen_pos)) * gl_Position.w;");
- }
-
- // Read, transform, and write texture coordinates.
- if (tex_coord_source_ != TEX_COORD_SOURCE_NONE) {
- if (is_ya_uv_) {
- HDR("varying TexCoordPrecision vec2 v_uvTexCoord;");
- HDR("varying TexCoordPrecision vec2 v_yaTexCoord;");
- } else {
- HDR("varying TexCoordPrecision vec2 v_texCoord;");
- }
-
- SRC("// Compute texture coordinates.");
- // Read coordinates.
- switch (tex_coord_source_) {
- case TEX_COORD_SOURCE_NONE:
- break;
- case TEX_COORD_SOURCE_POSITION:
- SRC("vec2 texCoord = pos.xy;");
- break;
- case TEX_COORD_SOURCE_ATTRIBUTE:
- HDR("attribute TexCoordPrecision vec2 a_texCoord;");
- SRC("vec2 texCoord = a_texCoord;");
- break;
- }
- // Transform coordinates (except YUV).
- switch (tex_coord_transform_) {
- case TEX_COORD_TRANSFORM_NONE:
- break;
- case TEX_COORD_TRANSFORM_TRANSLATED_VEC4:
- SRC("texCoord = texCoord + vec2(0.5);");
- [[fallthrough]];
- case TEX_COORD_TRANSFORM_VEC4:
- if (use_uniform_arrays_) {
- HDR("uniform TexCoordPrecision vec4 vertexTexTransform[NUM_QUADS];");
- SRC("TexCoordPrecision vec4 texTrans =");
- SRC(" vertexTexTransform[quad_index];");
- SRC("texCoord = texCoord * texTrans.zw + texTrans.xy;");
- } else {
- HDR("uniform TexCoordPrecision vec4 vertexTexTransform;");
- SRC("texCoord = texCoord * vertexTexTransform.zw +");
- SRC(" vertexTexTransform.xy;");
- }
- break;
- case TEX_COORD_TRANSFORM_MATRIX:
- HDR("uniform TexCoordPrecision mat4 texMatrix;");
- SRC("texCoord = (texMatrix * vec4(texCoord.xy, 0.0, 1.0)).xy;");
- break;
- }
- // Write the output texture coordinates.
- if (is_ya_uv_) {
- HDR("uniform TexCoordPrecision vec2 uvTexOffset;");
- HDR("uniform TexCoordPrecision vec2 uvTexScale;");
- HDR("uniform TexCoordPrecision vec2 yaTexOffset;");
- HDR("uniform TexCoordPrecision vec2 yaTexScale;");
- SRC("v_yaTexCoord = texCoord * yaTexScale + yaTexOffset;");
- SRC("v_uvTexCoord = texCoord * uvTexScale + uvTexOffset;");
- } else {
- SRC("v_texCoord = texCoord;");
- }
- }
-
- // Write varying vertex opacity.
- if (has_vertex_opacity_) {
- HDR("varying float v_alpha;");
- if (use_uniform_arrays_) {
- HDR("uniform float opacity[NUM_QUADS * 4];");
- } else {
- HDR("uniform float opacity[4];");
- }
- SRC("v_alpha = opacity[vertex_index];");
- }
-
- // Add cargo-culted dummy variables for Android.
- if (has_dummy_variables_) {
- HDR("uniform TexCoordPrecision vec2 dummy_uniform;");
- HDR("varying TexCoordPrecision vec2 dummy_varying;");
- SRC("dummy_varying = dummy_uniform;");
- }
-
- source += "}\n";
- return header + source;
-}
-
-FragmentShader::FragmentShader() {}
-
-std::string FragmentShader::GetShaderString() const {
- TexCoordPrecision precision = tex_coord_precision_;
- // The AA shader values will use TexCoordPrecision.
- if (aa_mode_ == USE_AA && precision == TEX_COORD_PRECISION_NA)
- precision = TEX_COORD_PRECISION_MEDIUM;
- std::string shader = GetShaderSource();
- SetBlendModeFunctions(&shader);
- SetRoundedCornerFunctions(&shader);
- SetFragmentSamplerType(sampler_type_, &shader);
- SetFragmentTexCoordPrecision(precision, &shader);
- return shader;
-}
-
-void FragmentShader::Init(GLES2Interface* context,
- unsigned program,
- int* base_uniform_index) {
- std::vector<const char*> uniforms;
- std::vector<int> locations;
- if (has_blend_mode()) {
- uniforms.push_back("s_backdropTexture");
- uniforms.push_back("s_originalBackdropTexture");
- uniforms.push_back("backdropRect");
- }
- if (mask_mode_ != NO_MASK) {
- uniforms.push_back("s_mask");
- uniforms.push_back("maskTexCoordScale");
- uniforms.push_back("maskTexCoordOffset");
- }
- if (has_color_matrix_) {
- uniforms.push_back("colorMatrix");
- uniforms.push_back("colorOffset");
- }
- if (has_uniform_alpha_)
- uniforms.push_back("alpha");
- if (has_background_color_)
- uniforms.push_back("background_color");
- if (has_tex_clamp_rect_)
- uniforms.push_back("tex_clamp_rect");
- switch (input_color_type_) {
- case INPUT_COLOR_SOURCE_RGBA_TEXTURE:
- uniforms.push_back("s_texture");
- if (has_rgba_fragment_tex_transform_)
- uniforms.push_back("fragmentTexTransform");
- break;
- case INPUT_COLOR_SOURCE_YUV_TEXTURES:
- uniforms.push_back("y_texture");
- if (uv_texture_mode_ == UV_TEXTURE_MODE_UV)
- uniforms.push_back("uv_texture");
- if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) {
- uniforms.push_back("u_texture");
- uniforms.push_back("v_texture");
- }
- if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE)
- uniforms.push_back("a_texture");
- uniforms.push_back("ya_clamp_rect");
- uniforms.push_back("uv_clamp_rect");
- uniforms.push_back("resource_multiplier");
- uniforms.push_back("resource_offset");
- break;
- case INPUT_COLOR_SOURCE_UNIFORM:
- uniforms.push_back("color");
- break;
- }
- if (has_output_color_matrix_)
- uniforms.emplace_back("output_color_matrix");
-
- if (has_tint_color_matrix_)
- uniforms.emplace_back("tint_color_matrix");
-
- if (has_rounded_corner_) {
- uniforms.emplace_back("roundedCornerRect");
- uniforms.emplace_back("roundedCornerRadius");
- }
-
- locations.resize(uniforms.size());
-
- GetProgramUniformLocations(context, program, uniforms.size(), uniforms.data(),
- locations.data(), base_uniform_index);
-
- size_t index = 0;
- if (has_blend_mode()) {
- backdrop_location_ = locations[index++];
- original_backdrop_location_ = locations[index++];
- backdrop_rect_location_ = locations[index++];
- }
- if (mask_mode_ != NO_MASK) {
- mask_sampler_location_ = locations[index++];
- mask_tex_coord_scale_location_ = locations[index++];
- mask_tex_coord_offset_location_ = locations[index++];
- }
- if (has_color_matrix_) {
- color_matrix_location_ = locations[index++];
- color_offset_location_ = locations[index++];
- }
- if (has_uniform_alpha_)
- alpha_location_ = locations[index++];
- if (has_background_color_)
- background_color_location_ = locations[index++];
- if (has_tex_clamp_rect_)
- tex_clamp_rect_location_ = locations[index++];
- switch (input_color_type_) {
- case INPUT_COLOR_SOURCE_RGBA_TEXTURE:
- sampler_location_ = locations[index++];
- if (has_rgba_fragment_tex_transform_)
- fragment_tex_transform_location_ = locations[index++];
- break;
- case INPUT_COLOR_SOURCE_YUV_TEXTURES:
- y_texture_location_ = locations[index++];
- if (uv_texture_mode_ == UV_TEXTURE_MODE_UV)
- uv_texture_location_ = locations[index++];
- if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) {
- u_texture_location_ = locations[index++];
- v_texture_location_ = locations[index++];
- }
- if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE)
- a_texture_location_ = locations[index++];
- ya_clamp_rect_location_ = locations[index++];
- uv_clamp_rect_location_ = locations[index++];
- resource_multiplier_location_ = locations[index++];
- resource_offset_location_ = locations[index++];
- break;
- case INPUT_COLOR_SOURCE_UNIFORM:
- color_location_ = locations[index++];
- break;
- }
-
- if (has_output_color_matrix_)
- output_color_matrix_location_ = locations[index++];
-
- if (has_tint_color_matrix_)
- tint_color_matrix_location_ = locations[index++];
-
- if (has_rounded_corner_) {
- rounded_corner_rect_location_ = locations[index++];
- rounded_corner_radius_location_ = locations[index++];
- }
-
- DCHECK_EQ(index, locations.size());
-}
-
-void FragmentShader::SetRoundedCornerFunctions(
- std::string* shader_string) const {
- if (!has_rounded_corner_)
- return;
-
- static constexpr base::StringPiece kUniforms = SHADER0([]() {
- uniform vec4 roundedCornerRect;
- uniform vec4 roundedCornerRadius;
- });
-
- static constexpr base::StringPiece kFunctionRcUtility = SHADER0([]() {
- // Returns a vector of size 4. Each component of a vector is set to 1 or 0
- // representing whether |rcCoord| is a part of the respective corner or
- // not.
- // The component ordering is:
- // [Top left, Top right, Bottom right, Bottom left]
- vec4 IsCorner(vec2 rcCoord) {
- // Top left corner
- if (rcCoord.x < roundedCornerRadius.x &&
- rcCoord.y < roundedCornerRadius.x) {
- return vec4(1.0, 0.0, 0.0, 0.0);
- }
-
- // Top right corner
- if (rcCoord.x > roundedCornerRect.z - roundedCornerRadius.y &&
- rcCoord.y < roundedCornerRadius.y) {
- return vec4(0.0, 1.0, 0.0, 0.0);
- }
-
- // Bottom right corner
- if (rcCoord.x > roundedCornerRect.z - roundedCornerRadius.z &&
- rcCoord.y > roundedCornerRect.w - roundedCornerRadius.z) {
- return vec4(0.0, 0.0, 1.0, 0.0);
- }
-
- // Bottom left corner
- if (rcCoord.x < roundedCornerRadius.w &&
- rcCoord.y > roundedCornerRect.w - roundedCornerRadius.w) {
- return vec4(0.0, 0.0, 0.0, 1.0);
- }
- return vec4(0.0, 0.0, 0.0, 0.0);
- }
-
- // Returns the center of the rounded corner. |corner| holds the info on
- // which corner the center is requested for.
- vec2 GetCenter(vec4 corner, float radius) {
- if (corner.x == 1.0) {
- // Top left corner
- return vec2(radius, radius);
- } else if (corner.y == 1.0) {
- // Top right corner
- return vec2(roundedCornerRect.z - radius, radius);
- } else if (corner.z == 1.0) {
- // Bottom right corner
- return vec2(roundedCornerRect.z - radius, roundedCornerRect.w - radius);
- } else {
- // Bottom left corner
- return vec2(radius, roundedCornerRect.w - radius);
- }
- }
- });
-
- static constexpr base::StringPiece kFunctionApplyRoundedCorner =
- SHADER0([]() {
- vec4 ApplyRoundedCorner(vec4 src) {
- vec2 rcCoord = gl_FragCoord.xy - roundedCornerRect.xy;
-
- vec4 isCorner = IsCorner(rcCoord);
-
- // Get the radius to use based on the corner this fragment lies in.
- float r = dot(isCorner, roundedCornerRadius);
-
- // If the radius is 0, then there is no rounded corner here. We can do
- // an early return.
- if (r == 0.0)
- return src;
-
- // Vector to the corner's center this fragment is in.
- // Due to precision errors on android, this variable requires a highp.
- // See https://crbug.com/1009322
- RoundedCornerPrecision vec2 cornerCenter = GetCenter(isCorner, r);
-
- // Vector from the center of the corner to the current fragment center
- vec2 cxy = rcCoord - cornerCenter;
-
- // Compute the distance of the fragment's center from the corner's
- // center.
- float fragDst = length(cxy);
-
- float alpha = smoothstep(r - 1.0, r + 1.0, fragDst);
- return vec4(0.0) * alpha + src * (1.0 - alpha);
- }
- });
-
- std::string shader;
- shader.reserve(shader_string->size() + 2048);
- shader += "precision mediump float;";
- shader +=
- "\n#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
- " #define RoundedCornerPrecision highp\n"
- "#else\n"
- " #define RoundedCornerPrecision mediump\n"
- "#endif\n";
- base::StrAppend(&shader, {kUniforms, kFunctionRcUtility,
- kFunctionApplyRoundedCorner, *shader_string});
- *shader_string = std::move(shader);
-}
-
-void FragmentShader::SetBlendModeFunctions(std::string* shader_string) const {
- if (!has_blend_mode()) {
- return;
- }
-
- static constexpr base::StringPiece kUniforms = SHADER0([]() {
- uniform sampler2D s_backdropTexture;
- uniform sampler2D s_originalBackdropTexture;
- uniform TexCoordPrecision vec4 backdropRect;
- });
-
- base::StringPiece function_apply_blend_mode;
- if (mask_for_background_) {
- static constexpr base::StringPiece kFunctionApplyBlendMode = SHADER0([]() {
- vec4 ApplyBlendMode(vec4 src, float mask) {
- TexCoordPrecision vec2 bgTexCoord = gl_FragCoord.xy - backdropRect.xy;
- bgTexCoord *= backdropRect.zw;
- vec4 backdrop = texture2D(s_backdropTexture, bgTexCoord);
- vec4 original_backdrop =
- texture2D(s_originalBackdropTexture, bgTexCoord);
- vec4 dst = mix(original_backdrop, backdrop, mask);
- return Blend(src, dst);
- }
- });
- function_apply_blend_mode = kFunctionApplyBlendMode;
- } else {
- static constexpr base::StringPiece kFunctionApplyBlendMode = SHADER0([]() {
- vec4 ApplyBlendMode(vec4 src) {
- TexCoordPrecision vec2 bgTexCoord = gl_FragCoord.xy - backdropRect.xy;
- bgTexCoord *= backdropRect.zw;
- vec4 dst = texture2D(s_backdropTexture, bgTexCoord);
- return Blend(src, dst);
- }
- });
- function_apply_blend_mode = kFunctionApplyBlendMode;
- }
-
- std::string shader;
- shader.reserve(shader_string->size() + 1024);
- shader += "precision mediump float;";
- AppendHelperFunctions(&shader);
- AppendBlendFunction(&shader);
- base::StrAppend(&shader,
- {kUniforms, function_apply_blend_mode, *shader_string});
- *shader_string = std::move(shader);
-}
-
-void FragmentShader::AppendHelperFunctions(std::string* buffer) const {
- static constexpr base::StringPiece kFunctionHardLight = SHADER0([]() {
- vec3 hardLight(vec4 src, vec4 dst) {
- vec3 result;
- result.r =
- (2.0 * src.r <= src.a)
- ? (2.0 * src.r * dst.r)
- : (src.a * dst.a - 2.0 * (dst.a - dst.r) * (src.a - src.r));
- result.g =
- (2.0 * src.g <= src.a)
- ? (2.0 * src.g * dst.g)
- : (src.a * dst.a - 2.0 * (dst.a - dst.g) * (src.a - src.g));
- result.b =
- (2.0 * src.b <= src.a)
- ? (2.0 * src.b * dst.b)
- : (src.a * dst.a - 2.0 * (dst.a - dst.b) * (src.a - src.b));
- result.rgb += src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
- return result;
- }
- });
-
- static constexpr base::StringPiece kFunctionColorDodgeComponent =
- SHADER0([]() {
- float getColorDodgeComponent(float srcc, float srca, float dstc,
- float dsta) {
- if (0.0 == dstc)
- return srcc * (1.0 - dsta);
- float d = srca - srcc;
- if (0.0 == d)
- return srca * dsta + srcc * (1.0 - dsta) + dstc * (1.0 - srca);
- d = min(dsta, dstc * srca / d);
- return d * srca + srcc * (1.0 - dsta) + dstc * (1.0 - srca);
- }
- });
-
- static constexpr base::StringPiece kFunctionColorBurnComponent =
- SHADER0([]() {
- float getColorBurnComponent(float srcc, float srca, float dstc,
- float dsta) {
- if (dsta == dstc)
- return srca * dsta + srcc * (1.0 - dsta) + dstc * (1.0 - srca);
- if (0.0 == srcc)
- return dstc * (1.0 - srca);
- float d = max(0.0, dsta - (dsta - dstc) * srca / srcc);
- return srca * d + srcc * (1.0 - dsta) + dstc * (1.0 - srca);
- }
- });
-
- static constexpr base::StringPiece kFunctionSoftLightComponentPosDstAlpha =
- SHADER0([]() {
- float getSoftLightComponent(float srcc, float srca, float dstc,
- float dsta) {
- if (2.0 * srcc <= srca) {
- return (dstc * dstc * (srca - 2.0 * srcc)) / dsta +
- (1.0 - dsta) * srcc + dstc * (-srca + 2.0 * srcc + 1.0);
- } else if (4.0 * dstc <= dsta) {
- float DSqd = dstc * dstc;
- float DCub = DSqd * dstc;
- float DaSqd = dsta * dsta;
- float DaCub = DaSqd * dsta;
- return (-DaCub * srcc +
- DaSqd * (srcc - dstc * (3.0 * srca - 6.0 * srcc - 1.0)) +
- 12.0 * dsta * DSqd * (srca - 2.0 * srcc) -
- 16.0 * DCub * (srca - 2.0 * srcc)) /
- DaSqd;
- } else {
- return -sqrt(dsta * dstc) * (srca - 2.0 * srcc) - dsta * srcc +
- dstc * (srca - 2.0 * srcc + 1.0) + srcc;
- }
- }
- });
-
- static constexpr base::StringPiece kFunctionLum = SHADER0([]() {
- float luminance(vec3 color) { return dot(vec3(0.3, 0.59, 0.11), color); }
-
- vec3 set_luminance(vec3 hueSat, float alpha, vec3 lumColor) {
- float diff = luminance(lumColor - hueSat);
- vec3 outColor = hueSat + diff;
- float outLum = luminance(outColor);
- float minComp = min(min(outColor.r, outColor.g), outColor.b);
- float maxComp = max(max(outColor.r, outColor.g), outColor.b);
- if (minComp < 0.0 && outLum != minComp) {
- outColor =
- outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) /
- (outLum - minComp);
- }
- if (maxComp > alpha && maxComp != outLum) {
- outColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) *
- (alpha - outLum)) /
- (maxComp - outLum);
- }
- return outColor;
- }
- });
-
- static constexpr base::StringPiece kFunctionSat = SHADER0([]() {
- float saturation(vec3 color) {
- return max(max(color.r, color.g), color.b) -
- min(min(color.r, color.g), color.b);
- }
-
- vec3 set_saturation_helper(float minComp, float midComp, float maxComp,
- float sat) {
- if (minComp < maxComp) {
- vec3 result;
- result.r = 0.0;
- result.g = sat * (midComp - minComp) / (maxComp - minComp);
- result.b = sat;
- return result;
- } else {
- return vec3(0, 0, 0);
- }
- }
-
- vec3 set_saturation(vec3 hueLumColor, vec3 satColor) {
- float sat = saturation(satColor);
- if (hueLumColor.r <= hueLumColor.g) {
- if (hueLumColor.g <= hueLumColor.b) {
- hueLumColor.rgb = set_saturation_helper(hueLumColor.r, hueLumColor.g,
- hueLumColor.b, sat);
- } else if (hueLumColor.r <= hueLumColor.b) {
- hueLumColor.rbg = set_saturation_helper(hueLumColor.r, hueLumColor.b,
- hueLumColor.g, sat);
- } else {
- hueLumColor.brg = set_saturation_helper(hueLumColor.b, hueLumColor.r,
- hueLumColor.g, sat);
- }
- } else if (hueLumColor.r <= hueLumColor.b) {
- hueLumColor.grb = set_saturation_helper(hueLumColor.g, hueLumColor.r,
- hueLumColor.b, sat);
- } else if (hueLumColor.g <= hueLumColor.b) {
- hueLumColor.gbr = set_saturation_helper(hueLumColor.g, hueLumColor.b,
- hueLumColor.r, sat);
- } else {
- hueLumColor.bgr = set_saturation_helper(hueLumColor.b, hueLumColor.g,
- hueLumColor.r, sat);
- }
- return hueLumColor;
- }
- });
-
- switch (blend_mode_) {
- case BLEND_MODE_OVERLAY:
- case BLEND_MODE_HARD_LIGHT:
- buffer->append(kFunctionHardLight.data(), kFunctionHardLight.size());
- return;
- case BLEND_MODE_COLOR_DODGE:
- buffer->append(kFunctionColorDodgeComponent.data(),
- kFunctionColorDodgeComponent.size());
- return;
- case BLEND_MODE_COLOR_BURN:
- buffer->append(kFunctionColorBurnComponent.data(),
- kFunctionColorBurnComponent.size());
- return;
- case BLEND_MODE_SOFT_LIGHT:
- buffer->append(kFunctionSoftLightComponentPosDstAlpha.data(),
- kFunctionSoftLightComponentPosDstAlpha.size());
- return;
- case BLEND_MODE_HUE:
- case BLEND_MODE_SATURATION:
- base::StrAppend(buffer, {kFunctionLum, kFunctionSat});
- return;
- case BLEND_MODE_COLOR:
- case BLEND_MODE_LUMINOSITY:
- buffer->append(kFunctionLum.data(), kFunctionLum.size());
- return;
- default:
- return;
- }
-}
-
-void FragmentShader::AppendBlendFunction(std::string* buffer) const {
- *buffer +=
- "vec4 Blend(vec4 src, vec4 dst) {"
- " vec4 result;";
- base::StrAppend(
- buffer, {GetBlendFunctionBodyForAlpha(), GetBlendFunctionBodyForRGB()});
- *buffer +=
- " return result;"
- "}";
-}
-
-base::StringPiece FragmentShader::GetBlendFunctionBodyForAlpha() const {
- if (blend_mode_ == BLEND_MODE_DESTINATION_IN)
- return "result.a = src.a * dst.a;";
- else
- return "result.a = src.a + (1.0 - src.a) * dst.a;";
-}
-
-base::StringPiece FragmentShader::GetBlendFunctionBodyForRGB() const {
- switch (blend_mode_) {
- case BLEND_MODE_NORMAL:
- return "result.rgb = src.rgb + dst.rgb * (1.0 - src.a);";
- case BLEND_MODE_DESTINATION_IN:
- return "result.rgb = dst.rgb * src.a;";
- case BLEND_MODE_SCREEN:
- return "result.rgb = src.rgb + (1.0 - src.rgb) * dst.rgb;";
- case BLEND_MODE_LIGHTEN:
- return "result.rgb = max((1.0 - src.a) * dst.rgb + src.rgb,"
- " (1.0 - dst.a) * src.rgb + dst.rgb);";
- case BLEND_MODE_OVERLAY:
- return "result.rgb = hardLight(dst, src);";
- case BLEND_MODE_DARKEN:
- return "result.rgb = min((1.0 - src.a) * dst.rgb + src.rgb,"
- " (1.0 - dst.a) * src.rgb + dst.rgb);";
- case BLEND_MODE_COLOR_DODGE:
- return "result.r = getColorDodgeComponent(src.r, src.a, dst.r, dst.a);"
- "result.g = getColorDodgeComponent(src.g, src.a, dst.g, dst.a);"
- "result.b = getColorDodgeComponent(src.b, src.a, dst.b, dst.a);";
- case BLEND_MODE_COLOR_BURN:
- return "result.r = getColorBurnComponent(src.r, src.a, dst.r, dst.a);"
- "result.g = getColorBurnComponent(src.g, src.a, dst.g, dst.a);"
- "result.b = getColorBurnComponent(src.b, src.a, dst.b, dst.a);";
- case BLEND_MODE_HARD_LIGHT:
- return "result.rgb = hardLight(src, dst);";
- case BLEND_MODE_SOFT_LIGHT:
- return "if (0.0 == dst.a) {"
- " result.rgb = src.rgb;"
- "} else {"
- " result.r = getSoftLightComponent(src.r, src.a, dst.r, dst.a);"
- " result.g = getSoftLightComponent(src.g, src.a, dst.g, dst.a);"
- " result.b = getSoftLightComponent(src.b, src.a, dst.b, dst.a);"
- "}";
- case BLEND_MODE_DIFFERENCE:
- return "result.rgb = src.rgb + dst.rgb -"
- " 2.0 * min(src.rgb * dst.a, dst.rgb * src.a);";
- case BLEND_MODE_EXCLUSION:
- return "result.rgb = dst.rgb + src.rgb - 2.0 * dst.rgb * src.rgb;";
- case BLEND_MODE_MULTIPLY:
- return "result.rgb = (1.0 - src.a) * dst.rgb +"
- " (1.0 - dst.a) * src.rgb + src.rgb * dst.rgb;";
- case BLEND_MODE_HUE:
- return "vec4 dstSrcAlpha = dst * src.a;"
- "result.rgb ="
- " set_luminance(set_saturation(src.rgb * dst.a,"
- " dstSrcAlpha.rgb),"
- " dstSrcAlpha.a,"
- " dstSrcAlpha.rgb);"
- "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
- case BLEND_MODE_SATURATION:
- return "vec4 dstSrcAlpha = dst * src.a;"
- "result.rgb = set_luminance(set_saturation(dstSrcAlpha.rgb,"
- " src.rgb * dst.a),"
- " dstSrcAlpha.a,"
- " dstSrcAlpha.rgb);"
- "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
- case BLEND_MODE_COLOR:
- return "vec4 srcDstAlpha = src * dst.a;"
- "result.rgb = set_luminance(srcDstAlpha.rgb,"
- " srcDstAlpha.a,"
- " dst.rgb * src.a);"
- "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
- case BLEND_MODE_LUMINOSITY:
- return "vec4 srcDstAlpha = src * dst.a;"
- "result.rgb = set_luminance(dst.rgb * src.a,"
- " srcDstAlpha.a,"
- " srcDstAlpha.rgb);"
- "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
- case BLEND_MODE_NONE:
- NOTREACHED();
- }
- return "result = vec4(1.0, 0.0, 0.0, 1.0);";
-}
-
-std::string FragmentShader::GetShaderSource() const {
- std::string header = "precision mediump float;\n";
- std::string source = "void main() {\n";
-
- // Read the input into vec4 texColor.
- switch (input_color_type_) {
- case INPUT_COLOR_SOURCE_RGBA_TEXTURE:
- if (ignore_sampler_type_)
- HDR("uniform sampler2D s_texture;");
- else
- HDR("uniform SamplerType s_texture;");
- HDR("varying TexCoordPrecision vec2 v_texCoord;");
- if (has_rgba_fragment_tex_transform_) {
- HDR("uniform TexCoordPrecision vec4 fragmentTexTransform;");
- SRC("// Transformed texture lookup");
- SRC("TexCoordPrecision vec2 texCoord =");
- SRC(" clamp(v_texCoord, 0.0, 1.0) * fragmentTexTransform.zw +");
- SRC(" fragmentTexTransform.xy;");
- SRC("vec4 texColor = TextureLookup(s_texture, texCoord);");
- DCHECK(!ignore_sampler_type_);
- DCHECK(!has_tex_clamp_rect_);
- } else {
- SRC("// Texture lookup");
- if (ignore_sampler_type_) {
- SRC("vec4 texColor = texture2D(s_texture, v_texCoord);");
- DCHECK(!has_tex_clamp_rect_);
- } else {
- SRC("TexCoordPrecision vec2 texCoord = v_texCoord;");
- if (has_tex_clamp_rect_) {
- HDR("uniform vec4 tex_clamp_rect;");
- SRC("texCoord = max(tex_clamp_rect.xy,");
- SRC(" min(tex_clamp_rect.zw, texCoord));");
- }
- SRC("vec4 texColor = TextureLookup(s_texture, texCoord);");
- }
- }
- break;
- case INPUT_COLOR_SOURCE_YUV_TEXTURES:
- DCHECK(!has_tex_clamp_rect_);
- // Compute the clamped texture coordinates for the YA and UV textures.
- HDR("uniform SamplerType y_texture;");
- SRC("// YUV texture lookup and conversion to RGB.");
- SRC("vec2 ya_clamped =");
- SRC(" max(ya_clamp_rect.xy, min(ya_clamp_rect.zw, v_yaTexCoord));");
- SRC("vec2 uv_clamped =");
- SRC(" max(uv_clamp_rect.xy, min(uv_clamp_rect.zw, v_uvTexCoord));");
- // Read the Y and UV or U and V textures into |yuv|.
- SRC("vec4 texColor;");
- SRC("texColor.w = 1.0;");
- SRC("texColor.x = TextureLookup(y_texture, ya_clamped).x;");
- if (uv_texture_mode_ == UV_TEXTURE_MODE_UV) {
- HDR("uniform SamplerType uv_texture;");
- SRC("texColor.yz = TextureLookup(uv_texture, uv_clamped).xy;");
- }
- if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) {
- HDR("uniform SamplerType u_texture;");
- HDR("uniform SamplerType v_texture;");
- SRC("texColor.y = TextureLookup(u_texture, uv_clamped).x;");
- SRC("texColor.z = TextureLookup(v_texture, uv_clamped).x;");
- }
- if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE)
- HDR("uniform SamplerType a_texture;");
- HDR("uniform vec4 ya_clamp_rect;");
- HDR("uniform vec4 uv_clamp_rect;");
- HDR("uniform float resource_multiplier;");
- HDR("uniform float resource_offset;");
- HDR("varying TexCoordPrecision vec2 v_yaTexCoord;");
- HDR("varying TexCoordPrecision vec2 v_uvTexCoord;");
- SRC("texColor.xyz -= vec3(resource_offset);");
- SRC("texColor.xyz *= resource_multiplier;");
- break;
- case INPUT_COLOR_SOURCE_UNIFORM:
- DCHECK(!ignore_sampler_type_);
- DCHECK(!has_rgba_fragment_tex_transform_);
- DCHECK(!has_tex_clamp_rect_);
- HDR("uniform vec4 color;");
- SRC("// Uniform color");
- SRC("vec4 texColor = color;");
- break;
- }
-
- // Apply color conversion.
- switch (color_conversion_mode_) {
- case COLOR_CONVERSION_MODE_SHADER:
- header += color_transform_->GetShaderSource();
- // Un-premultiply by alpha.
- if (premultiply_alpha_mode_ != NON_PREMULTIPLIED_ALPHA) {
- SRC("// un-premultiply alpha");
- SRC("if (texColor.a > 0.0) texColor.rgb /= texColor.a;");
- }
- SRC("texColor.rgb = DoColorConversion(texColor.xyz);");
- SRC("texColor.rgb *= texColor.a;");
- break;
- case COLOR_CONVERSION_MODE_NONE:
- // Premultiply by alpha.
- if (premultiply_alpha_mode_ == NON_PREMULTIPLIED_ALPHA) {
- SRC("// Premultiply alpha");
- SRC("texColor.rgb *= texColor.a;");
- }
- break;
- }
-
- // Apply the color matrix to texColor.
- if (has_color_matrix_) {
- HDR("uniform mat4 colorMatrix;");
- HDR("uniform vec4 colorOffset;");
- SRC("// Apply color matrix");
- SRC("float nonZeroAlpha = max(texColor.a, 0.00001);");
- SRC("texColor = vec4(texColor.rgb / nonZeroAlpha, nonZeroAlpha);");
- SRC("texColor = colorMatrix * texColor + colorOffset;");
- SRC("texColor.rgb *= texColor.a;");
- SRC("texColor = clamp(texColor, 0.0, 1.0);");
- }
-
- // Read the mask texture.
- if (mask_mode_ != NO_MASK) {
- HDR("uniform SamplerType s_mask;");
- HDR("uniform vec2 maskTexCoordScale;");
- HDR("uniform vec2 maskTexCoordOffset;");
- SRC("// Read the mask");
- SRC("TexCoordPrecision vec2 maskTexCoord =");
- SRC(" vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x,");
- SRC(" maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y);");
- SRC("vec4 maskColor = TextureLookup(s_mask, maskTexCoord);");
- }
-
- // Compute AA.
- if (aa_mode_ == USE_AA) {
- HDR("varying TexCoordPrecision vec4 edge_dist[2]; // 8 edge distances.");
- SRC("// Compute AA");
- SRC("vec4 d4 = min(edge_dist[0], edge_dist[1]);");
- SRC("vec2 d2 = min(d4.xz, d4.yw);");
- SRC("float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0);");
- }
-
- // Apply background texture.
- if (has_background_color_) {
- HDR("uniform vec4 background_color;");
- SRC("// Apply uniform background color blending");
- SRC("texColor += background_color * (1.0 - texColor.a);");
- }
-
- // Finally apply the output color matrix to texColor.
- if (has_output_color_matrix_) {
- HDR("uniform mat4 output_color_matrix;");
- SRC("// Apply the output color matrix");
- SRC("texColor = output_color_matrix * texColor;");
- }
-
- // Tint the final color. Used for debugging composited content.
- if (has_tint_color_matrix_) {
- HDR("uniform mat4 tint_color_matrix;");
- SRC("// Apply the tint color matrix");
- SRC("texColor = tint_color_matrix * texColor;");
- }
-
- // Include header text for alpha.
- if (has_uniform_alpha_) {
- HDR("uniform float alpha;");
- }
- if (has_varying_alpha_) {
- HDR("varying float v_alpha;");
- }
-
- // Apply uniform alpha, aa, varying alpha, and the mask.
- if (has_varying_alpha_ || aa_mode_ == USE_AA || has_uniform_alpha_ ||
- mask_mode_ != NO_MASK) {
- SRC("// Apply alpha from uniform, varying, aa, and mask.");
- std::string line = " texColor = texColor";
- if (has_varying_alpha_)
- line += " * v_alpha";
- if (has_uniform_alpha_)
- line += " * alpha";
- if (aa_mode_ == USE_AA)
- line += " * aa";
- if (mask_mode_ != NO_MASK)
- line += " * maskColor.a";
- if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE)
- line += " * TextureLookup(a_texture, ya_clamped).x";
- line += ";\n";
- source += line;
- }
-
- // Write the fragment color.
- SRC("// Write the fragment color");
- switch (frag_color_mode_) {
- case FRAG_COLOR_MODE_DEFAULT:
- DCHECK_EQ(blend_mode_, BLEND_MODE_NONE);
- SRC("gl_FragColor = texColor;");
- break;
- case FRAG_COLOR_MODE_OPAQUE:
- DCHECK_EQ(blend_mode_, BLEND_MODE_NONE);
- SRC("gl_FragColor = vec4(texColor.rgb, 1.0);");
- break;
- case FRAG_COLOR_MODE_APPLY_BLEND_MODE:
- if (!has_blend_mode()) {
- SRC("gl_FragColor = texColor;");
- } else if (mask_mode_ != NO_MASK) {
- if (mask_for_background_)
- SRC("gl_FragColor = ApplyBlendMode(texColor, maskColor.w);");
- else
- SRC("gl_FragColor = ApplyBlendMode(texColor);");
- } else {
- SRC("gl_FragColor = ApplyBlendMode(texColor);");
- }
- break;
- }
-
- if (has_rounded_corner_)
- SRC("gl_FragColor = ApplyRoundedCorner(gl_FragColor);");
-
- source += "}\n";
-
- return header + source;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/shader.h b/chromium/components/viz/service/display/shader.h
deleted file mode 100644
index 41d6a000d66..00000000000
--- a/chromium/components/viz/service/display/shader.h
+++ /dev/null
@@ -1,324 +0,0 @@
-// Copyright 2011 The Chromium 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 COMPONENTS_VIZ_SERVICE_DISPLAY_SHADER_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_SHADER_H_
-
-#include <string>
-
-#include "base/memory/raw_ptr.h"
-#include "base/strings/string_piece.h"
-#include "components/viz/service/viz_service_export.h"
-
-namespace gfx {
-class ColorTransform;
-class Point;
-class Size;
-} // namespace gfx
-
-namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-}
-} // namespace gpu
-
-namespace viz {
-
-enum TexCoordPrecision {
- TEX_COORD_PRECISION_NA = 0,
- TEX_COORD_PRECISION_MEDIUM = 1,
- TEX_COORD_PRECISION_HIGH = 2,
-};
-
-// Texture coordinate sources for the vertex shader.
-enum TexCoordSource {
- // Vertex shader does not populate a texture coordinate.
- TEX_COORD_SOURCE_NONE,
- // Texture coordinate is set to the untransformed position.
- TEX_COORD_SOURCE_POSITION,
- // Texture coordinate has its own attribute.
- TEX_COORD_SOURCE_ATTRIBUTE,
-};
-
-// Texture coordinate transformation modes for the vertex shader.
-enum TexCoordTransform {
- // Texture coordinates are not transformed.
- TEX_COORD_TRANSFORM_NONE,
- // Texture coordinates are transformed by a uniform vec4, scaling by zw and
- // then translating by xy.
- TEX_COORD_TRANSFORM_VEC4,
- // Same as the above, but add vec2(0.5) to the texture coordinate first.
- TEX_COORD_TRANSFORM_TRANSLATED_VEC4,
- // Texture coordiantes are transformed by a uniform mat4.
- TEX_COORD_TRANSFORM_MATRIX,
-};
-
-// Position source for the vertex shader.
-enum PositionSource {
- // The position is read directly from the position attribute.
- POSITION_SOURCE_ATTRIBUTE,
- // The position is read by attribute index into a uniform array for xy, and
- // getting zw from the attribute.
- POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM,
-};
-
-enum AAMode {
- NO_AA = 0,
- USE_AA = 1,
-};
-
-enum PremultipliedAlphaMode {
- PREMULTIPLIED_ALPHA = 0,
- NON_PREMULTIPLIED_ALPHA = 1,
-};
-
-enum SamplerType {
- SAMPLER_TYPE_NA = 0,
- SAMPLER_TYPE_2D = 1,
- SAMPLER_TYPE_2D_RECT = 2,
- SAMPLER_TYPE_EXTERNAL_OES = 3,
-};
-
-enum BlendMode {
- BLEND_MODE_NONE,
- BLEND_MODE_NORMAL,
- BLEND_MODE_DESTINATION_IN,
- BLEND_MODE_SCREEN,
- BLEND_MODE_OVERLAY,
- BLEND_MODE_DARKEN,
- BLEND_MODE_LIGHTEN,
- BLEND_MODE_COLOR_DODGE,
- BLEND_MODE_COLOR_BURN,
- BLEND_MODE_HARD_LIGHT,
- BLEND_MODE_SOFT_LIGHT,
- BLEND_MODE_DIFFERENCE,
- BLEND_MODE_EXCLUSION,
- BLEND_MODE_MULTIPLY,
- BLEND_MODE_HUE,
- BLEND_MODE_SATURATION,
- BLEND_MODE_COLOR,
- BLEND_MODE_LUMINOSITY,
- LAST_BLEND_MODE = BLEND_MODE_LUMINOSITY
-};
-
-enum InputColorSource {
- // This includes RGB and RGBA textures.
- INPUT_COLOR_SOURCE_RGBA_TEXTURE,
- // This includes Y and either UV or U-and-V textures.
- INPUT_COLOR_SOURCE_YUV_TEXTURES,
- // A solid color specified as a uniform value.
- INPUT_COLOR_SOURCE_UNIFORM,
-};
-
-enum UVTextureMode {
- // Shader does not use YUV textures.
- UV_TEXTURE_MODE_NA,
- // UV plane is a single texture.
- UV_TEXTURE_MODE_UV,
- // U and V planes have separate textures.
- UV_TEXTURE_MODE_U_V,
-};
-
-enum YUVAlphaTextureMode {
- YUV_ALPHA_TEXTURE_MODE_NA,
- YUV_NO_ALPHA_TEXTURE,
- YUV_HAS_ALPHA_TEXTURE,
-};
-
-enum ColorConversionMode {
- // No color conversion is performed.
- COLOR_CONVERSION_MODE_NONE,
- // Conversion is done analytically in the shader.
- COLOR_CONVERSION_MODE_SHADER,
-};
-
-// TODO(ccameron): Merge this with BlendMode.
-enum FragColorMode {
- FRAG_COLOR_MODE_DEFAULT,
- FRAG_COLOR_MODE_OPAQUE,
- FRAG_COLOR_MODE_APPLY_BLEND_MODE,
-};
-
-enum MaskMode {
- NO_MASK = 0,
- HAS_MASK = 1,
-};
-
-// Note: The highp_threshold_cache must be provided by the caller to make
-// the caching multi-thread/context safe in an easy low-overhead manner.
-// The caller must make sure to clear highp_threshold_cache to 0, so it can be
-// reinitialized, if a new or different context is used.
-VIZ_SERVICE_EXPORT TexCoordPrecision
-TexCoordPrecisionRequired(gpu::gles2::GLES2Interface* context,
- int* highp_threshold_cache,
- int highp_threshold_min,
- const gfx::Point& max_coordinate);
-
-VIZ_SERVICE_EXPORT TexCoordPrecision
-TexCoordPrecisionRequired(gpu::gles2::GLES2Interface* context,
- int* highp_threshold_cache,
- int highp_threshold_min,
- const gfx::Size& max_size);
-
-class VIZ_SERVICE_EXPORT VertexShader {
- public:
- VertexShader();
- void Init(gpu::gles2::GLES2Interface* context,
- unsigned program,
- int* base_uniform_index);
- std::string GetShaderString() const;
-
- protected:
- friend class Program;
-
- // Use arrays of uniforms for matrix, texTransform, and opacity.
- bool use_uniform_arrays_ = false;
-
- PositionSource position_source_ = POSITION_SOURCE_ATTRIBUTE;
- TexCoordSource tex_coord_source_ = TEX_COORD_SOURCE_NONE;
- TexCoordTransform tex_coord_transform_ = TEX_COORD_TRANSFORM_NONE;
-
- // Used only with TEX_COORD_TRANSFORM_VEC4.
- int vertex_tex_transform_location_ = -1;
-
- // Used only with TEX_COORD_TRANSFORM_MATRIX.
- int tex_matrix_location_ = -1;
-
- // Uniforms for YUV textures.
- bool is_ya_uv_ = false;
- int ya_tex_scale_location_ = -1;
- int ya_tex_offset_location_ = -1;
- int uv_tex_scale_location_ = -1;
- int uv_tex_offset_location_ = -1;
-
- // Matrix to transform the position.
- int matrix_location_ = -1;
-
- // Used only with POSITION_SOURCE_ATTRIBUTE_INDEXED_UNIFORM.
- int quad_location_ = -1;
-
- // Extra dummy variables to work around bugs on Android.
- // TODO(ccameron): This is likley unneeded cargo-culting.
- // http://crbug.com/240602
- bool has_dummy_variables_ = false;
-
- bool has_vertex_opacity_ = false;
- int vertex_opacity_location_ = -1;
-
- AAMode aa_mode_ = NO_AA;
- int viewport_location_ = -1;
- int edge_location_ = -1;
-};
-
-class VIZ_SERVICE_EXPORT FragmentShader {
- public:
- FragmentShader(const FragmentShader&) = delete;
- FragmentShader& operator=(const FragmentShader&) = delete;
-
- virtual void Init(gpu::gles2::GLES2Interface* context,
- unsigned program,
- int* base_uniform_index);
- std::string GetShaderString() const;
-
- protected:
- FragmentShader();
- virtual std::string GetShaderSource() const;
- bool has_blend_mode() const { return blend_mode_ != BLEND_MODE_NONE; }
-
- void SetBlendModeFunctions(std::string* shader_string) const;
- void SetRoundedCornerFunctions(std::string* shader_string) const;
-
- // Settings that are modified by sub-classes.
- AAMode aa_mode_ = NO_AA;
- bool has_varying_alpha_ = false;
- PremultipliedAlphaMode premultiply_alpha_mode_ = PREMULTIPLIED_ALPHA;
- FragColorMode frag_color_mode_ = FRAG_COLOR_MODE_DEFAULT;
- InputColorSource input_color_type_ = INPUT_COLOR_SOURCE_RGBA_TEXTURE;
-
- // Used only if |blend_mode_| is not BLEND_MODE_NONE.
- int backdrop_location_ = -1;
- int original_backdrop_location_ = -1;
- int backdrop_rect_location_ = -1;
-
- // Used only if |input_color_type_| is INPUT_COLOR_SOURCE_RGBA_TEXTURE.
- bool has_rgba_fragment_tex_transform_ = false;
- int sampler_location_ = -1;
- int fragment_tex_transform_location_ = -1;
-
- // Always use sampler2D and texture2D for the RGBA texture, regardless of the
- // specified SamplerType.
- // TODO(ccameron): Change GLRenderer to always specify the correct
- // SamplerType.
- bool ignore_sampler_type_ = false;
-
- // Used only if |input_color_type_| is INPUT_COLOR_SOURCE_UNIFORM.
- int color_location_ = -1;
-
- MaskMode mask_mode_ = NO_MASK;
- int mask_sampler_location_ = -1;
- int mask_tex_coord_scale_location_ = -1;
- int mask_tex_coord_offset_location_ = -1;
-
- bool has_color_matrix_ = false;
- int color_matrix_location_ = -1;
- int color_offset_location_ = -1;
-
- bool has_uniform_alpha_ = false;
- int alpha_location_ = -1;
-
- bool has_background_color_ = false;
- int background_color_location_ = -1;
-
- bool has_tex_clamp_rect_ = false;
- int tex_clamp_rect_location_ = -1;
-
- TexCoordPrecision tex_coord_precision_ = TEX_COORD_PRECISION_NA;
- SamplerType sampler_type_ = SAMPLER_TYPE_NA;
-
- BlendMode blend_mode_ = BLEND_MODE_NONE;
- bool mask_for_background_ = false;
-
- // YUV-only parameters.
- YUVAlphaTextureMode yuv_alpha_texture_mode_ = YUV_ALPHA_TEXTURE_MODE_NA;
- UVTextureMode uv_texture_mode_ = UV_TEXTURE_MODE_UV;
-
- ColorConversionMode color_conversion_mode_ = COLOR_CONVERSION_MODE_NONE;
- raw_ptr<const gfx::ColorTransform> color_transform_ = nullptr;
-
- bool has_output_color_matrix_ = false;
- int output_color_matrix_location_ = -1;
-
- bool has_tint_color_matrix_ = false;
- int tint_color_matrix_location_ = -1;
-
- // YUV uniform locations.
- int y_texture_location_ = -1;
- int u_texture_location_ = -1;
- int v_texture_location_ = -1;
- int uv_texture_location_ = -1;
- int a_texture_location_ = -1;
- int ya_clamp_rect_location_ = -1;
- int uv_clamp_rect_location_ = -1;
-
- // Rounded corner locations
- bool has_rounded_corner_ = false;
- int rounded_corner_rect_location_ = -1;
- int rounded_corner_radius_location_ = -1;
-
- // The resource offset and multiplier to adjust for bit depth.
- int resource_multiplier_location_ = -1;
- int resource_offset_location_ = -1;
-
- private:
- friend class Program;
-
- void AppendHelperFunctions(std::string* buffer) const;
- void AppendBlendFunction(std::string* buffer) const;
- base::StringPiece GetBlendFunctionBodyForAlpha() const;
- base::StringPiece GetBlendFunctionBodyForRGB() const;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SHADER_H_
diff --git a/chromium/components/viz/service/display/shader_unittest.cc b/chromium/components/viz/service/display/shader_unittest.cc
deleted file mode 100644
index 6990cbf14de..00000000000
--- a/chromium/components/viz/service/display/shader_unittest.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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 "components/viz/service/display/shader.h"
-
-#include "components/viz/test/test_context_provider.h"
-#include "components/viz/test/test_gles2_interface.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace viz {
-
-TEST(ShaderTest, HighpThresholds) {
- // The test gl always uses a mediump precision of 10 bits which
- // corresponds to a native highp threshold of 2^10 = 1024
- scoped_refptr<TestContextProvider> provider = TestContextProvider::Create();
- provider->BindToCurrentThread();
- gpu::gles2::GLES2Interface* test_gl = provider->ContextGL();
-
- int threshold_cache = 0;
- int threshold_min;
- gfx::Point closePoint(512, 512);
- gfx::Size smallSize(512, 512);
- gfx::Point farPoint(2560, 2560);
- gfx::Size bigSize(2560, 2560);
-
- threshold_min = 0;
- EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
- TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min,
- closePoint));
- EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
- TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min,
- smallSize));
- EXPECT_EQ(TEX_COORD_PRECISION_HIGH,
- TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min,
- farPoint));
- EXPECT_EQ(TEX_COORD_PRECISION_HIGH,
- TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min,
- bigSize));
-
- threshold_min = 3000;
- EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
- TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min,
- closePoint));
- EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
- TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min,
- smallSize));
- EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
- TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min,
- farPoint));
- EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
- TexCoordPrecisionRequired(test_gl, &threshold_cache, threshold_min,
- bigSize));
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/skia_output_surface.h b/chromium/components/viz/service/display/skia_output_surface.h
index 635a9e0a6b3..a6557e31d0c 100644
--- a/chromium/components/viz/service/display/skia_output_surface.h
+++ b/chromium/components/viz/service/display/skia_output_surface.h
@@ -17,6 +17,7 @@
#include "components/viz/service/display/overlay_processor_interface.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkYUVAInfo.h"
+#include "ui/gfx/gpu_fence_handle.h"
#if BUILDFLAG(IS_WIN)
#include "components/viz/service/display/dc_layer_overlay.h"
@@ -29,14 +30,14 @@
class SkCanvas;
class SkImage;
-#if BUILDFLAG(IS_APPLE)
-class SkDeferredDisplayList;
-#endif
-
namespace gfx {
class ColorSpace;
} // namespace gfx
+namespace gpu {
+class SharedImageInterface;
+}
+
namespace viz {
class OverlayCandidate;
@@ -121,6 +122,7 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface,
ResourceFormat format,
bool mipmap,
sk_sp<SkColorSpace> color_space,
+ bool is_overlay,
const gpu::Mailbox& mailbox) = 0;
// Finish painting the current frame or current render pass, depends on which
@@ -128,8 +130,13 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface,
// play the DDL back on GPU thread on a cached SkSurface.
// Optionally the caller may specify |on_finished| callback to be called after
// the GPU has finished processing all submitted commands. The callback may be
- // called on a different thread.
- virtual void EndPaint(base::OnceClosure on_finished) = 0;
+ // called on a different thread. The caller may also specify
+ // |return_release_fence_cb| callback to be called after all commands are
+ // submitted. The callback will return the release fence which will be
+ // signaled once the submitted commands are processed.
+ virtual void EndPaint(base::OnceClosure on_finished,
+ base::OnceCallback<void(gfx::GpuFenceHandle)>
+ return_release_fence_cb) = 0;
// Make a promise SkImage from a render pass id. The render pass has been
// painted with BeginPaintRenderPass and FinishPaintRenderPass. The format
@@ -162,8 +169,7 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface,
// the GPU has finished processing all submitted commands. The callback may be
// called on a different thread.
virtual void ScheduleOverlays(OverlayList overlays,
- std::vector<gpu::SyncToken> sync_tokens,
- base::OnceClosure on_finished) = 0;
+ std::vector<gpu::SyncToken> sync_tokens) = 0;
// Add context lost observer.
virtual void AddContextLostObserver(ContextLostObserver* observer) = 0;
@@ -194,15 +200,6 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface,
// 0 < n <= capabilities_.number_of_buffers.
// Return true if new buffers are allocated.
virtual bool EnsureMinNumberOfBuffers(int n) = 0;
-
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
- virtual SkCanvas* BeginPaintRenderPassOverlay(
- const gfx::Size& size,
- ResourceFormat format,
- bool mipmap,
- sk_sp<SkColorSpace> color_space) = 0;
- virtual sk_sp<SkDeferredDisplayList> EndPaintRenderPassOverlay() = 0;
-#endif
};
} // namespace viz
diff --git a/chromium/components/viz/service/display/skia_readback_pixeltest.cc b/chromium/components/viz/service/display/skia_readback_pixeltest.cc
index c3bffea471f..db740b2c878 100644
--- a/chromium/components/viz/service/display/skia_readback_pixeltest.cc
+++ b/chromium/components/viz/service/display/skia_readback_pixeltest.cc
@@ -17,9 +17,11 @@
#include "cc/test/pixel_test.h"
#include "cc/test/pixel_test_utils.h"
#include "cc/test/resource_provider_test_utils.h"
+#include "components/viz/common/frame_sinks/blit_request.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/frame_sinks/copy_output_util.h"
+#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/service/display_embedder/skia_output_surface_impl.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "components/viz/test/gl_scaler_test_util.h"
@@ -572,7 +574,8 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SkiaReadbackPixelTestNV12);
class SkiaReadbackPixelTestNV12WithBlit
: public SkiaReadbackPixelTest,
- public testing::WithParamInterface<bool> {
+ public testing::WithParamInterface<
+ std::tuple<bool, LetterboxingBehavior, bool>> {
public:
CopyOutputResult::Destination RequestDestination() const {
return CopyOutputResult::Destination::kNativeTextures;
@@ -583,8 +586,17 @@ class SkiaReadbackPixelTestNV12WithBlit
}
void SetUp() override {
- SkiaReadbackPixelTest::SetUpReadbackPixeltest(GetParam());
+ SkiaReadbackPixelTest::SetUpReadbackPixeltest(std::get<0>(GetParam()));
+ }
+
+ LetterboxingBehavior GetLetterboxingBehavior() const {
+ return std::get<1>(GetParam());
}
+
+ // Test parameter that will return `true` if we'll claim that the textures we
+ // create come from GpuMemoryBuffer, `false` otherwise. This exercises a
+ // different code path in SkiaRenderer.
+ bool populates_gpu_memory_buffer() const { return std::get<2>(GetParam()); }
};
// Test that SkiaRenderer readback works correctly. This test will use the
@@ -625,8 +637,7 @@ TEST_P(SkiaReadbackPixelTestNV12WithBlit, ExecutesCopyRequestWithBlit) {
<< " The test case expects the blit region's origin to be even for NV12 "
"blit requests";
- const SkColor rgba_red = SkColorSetARGB(0xff, 0xff, 0, 0);
- const SkColor yuv_red = GLScalerTestUtil::ConvertRGBAColorToYUV(rgba_red);
+ const SkColor yuv_red = GLScalerTestUtil::ConvertRGBAColorToYUV(SK_ColorRED);
const std::vector<uint8_t> luma_pattern = {
static_cast<uint8_t>(SkColorGetR(yuv_red))};
@@ -669,8 +680,9 @@ TEST_P(SkiaReadbackPixelTestNV12WithBlit, ExecutesCopyRequestWithBlit) {
request.set_result_selection(result_selection);
- request.set_blit_request(
- BlitRequest(destination_subregion.origin(), mailboxes));
+ request.set_blit_request(BlitRequest(
+ destination_subregion.origin(), GetLetterboxingBehavior(),
+ mailboxes, populates_gpu_memory_buffer()));
}));
// Check that a result was produced and is of the expected rect/size.
@@ -716,7 +728,18 @@ TEST_P(SkiaReadbackPixelTestNV12WithBlit, ExecutesCopyRequestWithBlit) {
// The textures that we passed in to BlitRequest contained NV12 plane data for
// an all-red image, let's re-create such a bitmap:
SkBitmap expected = GLScalerTestUtil::AllocateRGBABitmap(source_size);
- expected.eraseColor(rgba_red);
+
+ if (GetLetterboxingBehavior() == LetterboxingBehavior::kLetterbox) {
+ // We have requested the results to be letterboxed, so everything that
+ // CopyOutputRequest is not populating w/ render pass contents should be
+ // black:
+ expected.eraseColor(SK_ColorBLACK);
+ } else {
+ // We have requested the results to not be letterboxed, so everything that
+ // CopyOutputRequest is not populating w/ render pass will have original
+ // contents (red in our case):
+ expected.eraseColor(SK_ColorRED);
+ }
// Blit request should "stitch" the pixels from the source image into a
// sub-region of caller-provided texture - let's write our expected pixels
@@ -732,10 +755,15 @@ TEST_P(SkiaReadbackPixelTestNV12WithBlit, ExecutesCopyRequestWithBlit) {
}
#if !BUILDFLAG(IS_ANDROID) || !defined(ARCH_CPU_X86_FAMILY)
-INSTANTIATE_TEST_SUITE_P(,
- SkiaReadbackPixelTestNV12WithBlit,
- // Result scaling: Scale by half?
- testing::Values(true, false));
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ SkiaReadbackPixelTestNV12WithBlit,
+ testing::Combine(
+ testing::Bool(), // Result scaling: Scale by half?
+ testing::Values(LetterboxingBehavior::kDoNotLetterbox,
+ LetterboxingBehavior::kLetterbox),
+ testing::Bool() // Should behave as if COR is populating a GMB?
+ ));
#else
// Don't instantiate the NV12 tests when run on Android emulator, they won't
// work since the SkiaRenderer currently does not support CopyOutputRequests
diff --git a/chromium/components/viz/service/display/skia_renderer.cc b/chromium/components/viz/service/display/skia_renderer.cc
index 5a6eb5feb46..1b0a7116b73 100644
--- a/chromium/components/viz/service/display/skia_renderer.cc
+++ b/chromium/components/viz/service/display/skia_renderer.cc
@@ -45,7 +45,6 @@
#include "components/viz/service/display/renderer_utils.h"
#include "components/viz/service/display/resource_fence.h"
#include "components/viz/service/display/skia_output_surface.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/common/sync_token.h"
@@ -575,8 +574,7 @@ class SkiaRenderer::ScopedSkImageBuilder {
bool maybe_concurrent_reads,
SkAlphaType alpha_type = kPremul_SkAlphaType,
GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin,
- const absl::optional<gfx::ColorSpace>&
- override_colorspace = absl::nullopt,
+ sk_sp<SkColorSpace> override_color_space = nullptr,
bool raw_draw_if_possible = false);
ScopedSkImageBuilder(const ScopedSkImageBuilder&) = delete;
@@ -586,12 +584,12 @@ class SkiaRenderer::ScopedSkImageBuilder {
const SkImage* sk_image() const { return sk_image_; }
const cc::PaintOpBuffer* paint_op_buffer() const { return paint_op_buffer_; }
- const absl::optional<SkColor>& clear_color() const { return clear_color_; }
+ const absl::optional<SkColor4f>& clear_color() const { return clear_color_; }
private:
raw_ptr<const SkImage> sk_image_ = nullptr;
raw_ptr<const cc::PaintOpBuffer> paint_op_buffer_ = nullptr;
- absl::optional<SkColor> clear_color_;
+ absl::optional<SkColor4f> clear_color_;
};
SkiaRenderer::ScopedSkImageBuilder::ScopedSkImageBuilder(
@@ -600,7 +598,7 @@ SkiaRenderer::ScopedSkImageBuilder::ScopedSkImageBuilder(
bool maybe_concurrent_reads,
SkAlphaType alpha_type,
GrSurfaceOrigin origin,
- const absl::optional<gfx::ColorSpace>& override_color_space,
+ sk_sp<SkColorSpace> override_color_space,
bool raw_draw_if_possible) {
if (!resource_id)
return;
@@ -702,22 +700,30 @@ class SkiaRenderer::ScopedYUVSkImageBuilder {
sk_sp<SkImage> sk_image_;
};
-class SkiaRenderer::FrameResourceFence : public ResourceFence {
+// A read lock based fence that is signaled after gpu commands are completed
+// meaning the resource has been read.
+class SkiaRenderer::FrameResourceGpuCommandsCompletedFence
+ : public ResourceFence {
public:
- FrameResourceFence() = default;
-
- FrameResourceFence(const FrameResourceFence&) = delete;
- FrameResourceFence& operator=(const FrameResourceFence&) = delete;
+ FrameResourceGpuCommandsCompletedFence() = default;
+ FrameResourceGpuCommandsCompletedFence(
+ const FrameResourceGpuCommandsCompletedFence&) = delete;
+ FrameResourceGpuCommandsCompletedFence& operator=(
+ const FrameResourceGpuCommandsCompletedFence&) = delete;
// ResourceFence implementation.
void Set() override { set_ = true; }
bool HasPassed() override { return event_.IsSignaled(); }
+ gfx::GpuFenceHandle GetGpuFenceHandle() override {
+ NOTREACHED();
+ return gfx::GpuFenceHandle();
+ }
bool WasSet() { return set_; }
void Signal() { event_.Signal(); }
private:
- ~FrameResourceFence() override = default;
+ ~FrameResourceGpuCommandsCompletedFence() override = default;
// Accessed only from compositor thread.
bool set_ = false;
@@ -725,6 +731,41 @@ class SkiaRenderer::FrameResourceFence : public ResourceFence {
base::WaitableEvent event_;
};
+// FrameResourceFence that gets a ReleaseFence which is later set to returned
+// resources.
+class SkiaRenderer::FrameResourceReleaseFence : public ResourceFence {
+ public:
+ FrameResourceReleaseFence() = default;
+ FrameResourceReleaseFence(const FrameResourceReleaseFence&) = delete;
+ FrameResourceReleaseFence& operator=(const FrameResourceReleaseFence&) =
+ delete;
+
+ // ResourceFence implementation:
+ void Set() override { set_ = true; }
+ // If the fence handle has been set, |this| has passed aka the callback has
+ // been called.
+ bool HasPassed() override { return release_fence_.has_value(); }
+ gfx::GpuFenceHandle GetGpuFenceHandle() override {
+ return HasPassed() ? release_fence_.value().Clone() : gfx::GpuFenceHandle();
+ }
+
+ bool WasSet() { return set_; }
+ void SetReleaseFenceCallback(gfx::GpuFenceHandle release_fence) {
+ release_fence_ = std::move(release_fence);
+ }
+
+ private:
+ ~FrameResourceReleaseFence() override = default;
+
+ // Accessed only from compositor thread.
+ bool set_ = false;
+
+ // This is made optional so that the value is set after
+ // SetReleaseFenceCallback is called. Otherwise, there is no way to know if
+ // the fence has been set and a null handle is a "valid" handle.
+ absl::optional<gfx::GpuFenceHandle> release_fence_;
+};
+
SkiaRenderer::SkiaRenderer(const RendererSettings* settings,
const DebugRendererSettings* debug_settings,
OutputSurface* output_surface,
@@ -741,9 +782,17 @@ SkiaRenderer::SkiaRenderer(const RendererSettings* settings,
DCHECK(skia_output_surface_);
lock_set_for_external_use_.emplace(resource_provider, skia_output_surface_);
- current_frame_resource_fence_ = base::MakeRefCounted<FrameResourceFence>();
- this->resource_provider()->SetReadLockFence(
- current_frame_resource_fence_.get());
+ // There can be different synchronization types requested for different
+ // resources. Some of them may require SyncToken, others - ReadLockFence, and
+ // others may need ReleaseFence. SyncTokens are set when the output surface
+ // is flushed and external resources are released. However, other resources
+ // require additional setup, which helps to handle that.
+ current_gpu_commands_completed_fence_ =
+ base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>();
+ current_release_fence_ = base::MakeRefCounted<FrameResourceReleaseFence>();
+ this->resource_provider()->SetGpuCommandsCompletedFence(
+ current_gpu_commands_completed_fence_.get());
+ this->resource_provider()->SetReleaseFence(current_release_fence_.get());
#if OS_ANDROID
use_real_color_space_for_stream_video_ =
@@ -760,7 +809,8 @@ bool SkiaRenderer::CanPartialSwap() {
void SkiaRenderer::BeginDrawingFrame() {
TRACE_EVENT0("viz", "SkiaRenderer::BeginDrawingFrame");
- DCHECK(!current_frame_resource_fence_->WasSet());
+ DCHECK(!current_gpu_commands_completed_fence_->WasSet());
+ DCHECK(!current_release_fence_->WasSet());
}
void SkiaRenderer::FinishDrawingFrame() {
@@ -808,6 +858,16 @@ void SkiaRenderer::SwapBuffers(SwapFrameData swap_frame_data) {
swap_buffer_rect_ = gfx::Rect();
FlushOutputSurface();
+
+#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+ // Delete render pass overlay backings from the previous frame that will not
+ // be used again.
+ for (auto& backing : available_render_pass_overlay_backings_) {
+ skia_output_surface_->GetSharedImageInterface()->DestroySharedImage(
+ gpu::SyncToken(), backing.mailbox);
+ }
+ available_render_pass_overlay_backings_.clear();
+#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
}
void SkiaRenderer::SwapBuffersSkipped() {
@@ -828,13 +888,13 @@ void SkiaRenderer::SwapBuffersComplete(gfx::GpuFenceHandle release_fence) {
auto read_fence_lock_iter = committed_overlay_locks_.end();
if (!release_fence.is_null()) {
- // Set release fences for returning for last frame overlay resources.
+ // Set release fences to return overlay resources for last frame.
for (auto& lock : committed_overlay_locks_) {
lock.SetReleaseFence(release_fence.Clone());
}
- // Find all locks that have a read-lock fence associated with them.
- // If we have a release fence, it's not safe to release them here.
- // Release them later in BuffersPresented.
+ // Find all locks that have a read-lock fence associated with them and move
+ // them to the back of locks. If we have a release fence, it's not safe to
+ // release them here. Release them later in BuffersPresented().
read_fence_lock_iter = std::partition(
committed_overlay_locks_.begin(), committed_overlay_locks_.end(),
[](auto& lock) { return !lock.HasReadLockFence(); });
@@ -846,7 +906,7 @@ void SkiaRenderer::SwapBuffersComplete(gfx::GpuFenceHandle release_fence) {
// Right now, only macOS and Ozone need to return mailboxes of released
// overlays, so we should not release |committed_overlay_locks_| here. The
- // resources in it will be released by DidReceiveReleasedOverlays() later.
+ // resources in it will be released in DidReceiveReleasedOverlays() later.
#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
for (auto lock_iter = committed_overlay_locks_.begin();
lock_iter != read_fence_lock_iter; ++lock_iter) {
@@ -854,6 +914,8 @@ void SkiaRenderer::SwapBuffersComplete(gfx::GpuFenceHandle release_fence) {
}
#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+ // Current pending locks should have been committed by the next time
+ // SwapBuffers() is completed.
committed_overlay_locks_.clear();
std::swap(committed_overlay_locks_, pending_overlay_locks_.front());
pending_overlay_locks_.pop_front();
@@ -869,19 +931,38 @@ void SkiaRenderer::DidReceiveReleasedOverlays(
// This method is only called on macOS and Ozone right now.
#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
for (const auto& mailbox : released_overlays) {
- auto it = awaiting_release_overlay_locks_.find(mailbox);
- if (it == awaiting_release_overlay_locks_.end()) {
- // TODO(crbug.com/1299794): Re-enable this DCHECK on Ozone.
+ // If this mailbox is for render pass overlay, mark the released render pass
+ // overlay backing as available to be re-used.
+ auto backing_iter =
+ std::find_if(in_flight_render_pass_overlay_backings_.begin(),
+ in_flight_render_pass_overlay_backings_.end(),
+ [&mailbox](const RenderPassBacking& backing) {
+ return backing.mailbox == mailbox;
+ });
+ if (backing_iter != in_flight_render_pass_overlay_backings_.end()) {
+ available_render_pass_overlay_backings_.push_back(
+ std::move(*backing_iter));
+ in_flight_render_pass_overlay_backings_.erase(backing_iter);
+ }
+
+ auto iter = std::find_if(awaiting_release_overlay_locks_.begin(),
+ awaiting_release_overlay_locks_.end(),
+ [&mailbox](const OverlayLock& lock) {
+ return lock.mailbox() == mailbox;
+ });
+ if (iter == awaiting_release_overlay_locks_.end()) {
+// TODO(crbug.com/1299794): Re-enable this DCHECK on Ozone.
#if !defined(USE_OZONE)
+ // The released overlay should always be found as awaiting to be released.
DLOG(FATAL) << "Got an unexpected mailbox";
#endif // !defined(USE_OZONE)
continue;
}
- awaiting_release_overlay_locks_.erase(it);
+ awaiting_release_overlay_locks_.erase(iter);
}
#else
NOTREACHED();
-#endif // !(BUILDFLAG(IS_APPLE) || defined (USE_OZONE))
+#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
}
bool SkiaRenderer::FlippedFramebuffer() const {
@@ -899,8 +980,6 @@ void SkiaRenderer::EnsureScissorTestDisabled() {
}
void SkiaRenderer::BindFramebufferToOutputSurface() {
- DCHECK(!output_surface_->HasExternalStencilTest());
-
root_canvas_ = skia_output_surface_->BeginPaintCurrentFrame();
current_canvas_ = root_canvas_;
current_surface_ = root_surface_.get();
@@ -915,7 +994,8 @@ void SkiaRenderer::BindFramebufferToTexture(
RenderPassBacking& backing = iter->second;
current_canvas_ = skia_output_surface_->BeginPaintRenderPass(
render_pass_id, backing.size, backing.format, backing.generate_mipmap,
- backing.color_space.ToSkColorSpace(), backing.mailbox);
+ RenderPassBackingSkColorSpace(backing), /*is_overlay=*/false,
+ backing.mailbox);
}
void SkiaRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) {
@@ -941,9 +1021,13 @@ void SkiaRenderer::ClearFramebuffer() {
if (current_frame()->current_render_pass->has_transparent_background) {
ClearCanvas(SkColorSetARGB(0, 0, 0, 0));
} else {
-#if DCHECK_IS_ON()
+#if DCHECK_IS_ON() && !BUILDFLAG(IS_LINUX)
// On DEBUG builds, opaque render passes are cleared to blue
// to easily see regions that were not drawn on the screen.
+ // ClearCavas() call causes slight pixel difference, so linux-ref and
+ // linux-blink-ref bots cannot share the same baseline for webtest.
+ // So remove this ClearCanvas() call for dcheck on build for now.
+ // TODO(crbug.com/1330278): add it back.
ClearCanvas(SkColorSetARGB(255, 0, 0, 255));
#endif
}
@@ -1889,10 +1973,11 @@ void SkiaRenderer::DrawSingleImage(const SkImage* image,
constraint);
}
-void SkiaRenderer::DrawPaintOpBuffer(const cc::PaintOpBuffer* buffer,
- const absl::optional<SkColor>& clear_color,
- const TileDrawQuad* quad,
- const DrawQuadParams* params) {
+void SkiaRenderer::DrawPaintOpBuffer(
+ const cc::PaintOpBuffer* buffer,
+ const absl::optional<SkColor4f>& clear_color,
+ const TileDrawQuad* quad,
+ const DrawQuadParams* params) {
TRACE_EVENT0("viz", "SkiaRenderer::DrawPaintOpBuffer");
if (!batched_quads_.empty())
FlushBatchedQuads();
@@ -2024,7 +2109,7 @@ void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad,
void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad,
const DrawRPDQParams* rpdq_params,
DrawQuadParams* params) {
- DrawColoredQuad(SkColor4f::FromColor(quad->color), rpdq_params, params);
+ DrawColoredQuad(quad->color, rpdq_params, params);
}
void SkiaRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad,
@@ -2033,13 +2118,12 @@ void SkiaRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad,
TRACE_EVENT0("viz", "SkiaRenderer::DrawStreamVideoQuad");
DCHECK(!MustFlushBatchedQuads(quad, rpdq_params, *params));
- absl::optional<gfx::ColorSpace> override_color_space;
+ sk_sp<SkColorSpace> override_color_space;
// Force SRGB color space if we don't want real color space from media
// decoder.
- if (!use_real_color_space_for_stream_video_) {
- override_color_space = gfx::ColorSpace::CreateSRGB();
- }
+ if (!use_real_color_space_for_stream_video_)
+ override_color_space = SkColorSpace::MakeSRGB();
ScopedSkImageBuilder builder(this, quad->resource_id(),
/*maybe_concurrent_reads=*/true,
@@ -2079,17 +2163,17 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad,
const bool needs_color_conversion_filter =
quad->is_video_frame && src_color_space.IsHDR();
- absl::optional<gfx::ColorSpace> override_color_space;
+ sk_sp<SkColorSpace> override_color_space;
if (needs_color_conversion_filter)
- override_color_space = CurrentRenderPassColorSpace();
+ override_color_space = CurrentRenderPassSkColorSpace();
- // TODO(b/221643955): Some Chrome OS tests rely on the old GLRenderer
- // behavior of skipping color space conversions if the quad's color space is
- // invalid. Once these tests are migrated, we can remove the override here
- // and revert to Skia's default behavior of assuming sRGB on invalid.
+ // TODO(b/221643955): Some Chrome OS tests rely on the old GLRenderer
+ // behavior of skipping color space conversions if the quad's color space is
+ // invalid. Once these tests are migrated, we can remove the override here
+ // and revert to Skia's default behavior of assuming sRGB on invalid.
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (!src_color_space.IsValid())
- override_color_space = CurrentRenderPassColorSpace();
+ override_color_space = CurrentRenderPassSkColorSpace();
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
ScopedSkImageBuilder builder(
@@ -2118,7 +2202,7 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad,
// 2. The vertex opacities are not all 1s.
// 3. The quad contains video which might need special white level adjustment.
const bool blend_background =
- quad->background_color != SK_ColorTRANSPARENT && !image->isOpaque();
+ quad->background_color != SkColors::kTransparent && !image->isOpaque();
const bool vertex_alpha =
quad->vertex_opacity[0] < 1.f || quad->vertex_opacity[1] < 1.f ||
quad->vertex_opacity[2] < 1.f || quad->vertex_opacity[3] < 1.f;
@@ -2204,8 +2288,9 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad,
if (blend_background) {
// Add a color filter that does DstOver blending between texture and the
// background color. Then, modulate by quad's opacity *after* blending.
- sk_sp<SkColorFilter> cf =
- SkColorFilters::Blend(quad->background_color, SkBlendMode::kDstOver);
+ // TODO(crbug/1308932) remove toSkColor and make all SkColor4f
+ sk_sp<SkColorFilter> cf = SkColorFilters::Blend(
+ quad->background_color.toSkColor(), SkBlendMode::kDstOver);
if (quad_alpha < 1.f) {
cf = MakeOpacityFilter(quad_alpha, std::move(cf));
quad_alpha = 1.f;
@@ -2220,7 +2305,7 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad,
// Skia won't perform color conversion.
const gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace();
DCHECK(SkColorSpace::Equals(image->colorSpace(),
- dst_color_space.ToSkColorSpace().get()));
+ CurrentRenderPassSkColorSpace().get()));
sk_sp<SkColorFilter> color_filter =
GetColorSpaceConversionFilter(src_color_space, dst_color_space);
paint.setColorFilter(color_filter->makeComposed(paint.refColorFilter()));
@@ -2254,7 +2339,7 @@ void SkiaRenderer::DrawTileDrawQuad(const TileDrawQuad* quad,
this, quad->resource_id(), /*maybe_concurrent_reads=*/false,
quad->is_premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType,
/*origin=*/kTopLeft_GrSurfaceOrigin,
- /*override_colorspace=*/absl::nullopt, raw_draw_if_possible);
+ /*override_color_space=*/nullptr, raw_draw_if_possible);
params->vis_tex_coords = cc::MathUtil::ScaleRectProportional(
quad->tex_coord_rect, gfx::RectF(quad->rect), params->visible_rect);
@@ -2322,8 +2407,7 @@ void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad,
// color space we lie and say the SkImage destination color space is always
// the same as the rest of the frame. Otherwise the two color space
// adjustments combined will produce the wrong result.
- const gfx::ColorSpace& frame_color_space = CurrentRenderPassColorSpace();
- gfx::ColorSpace dst_color_space = frame_color_space;
+ gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace();
#if BUILDFLAG(IS_WIN)
// Force sRGB output on Windows for overlay candidate video quads to match
@@ -2344,12 +2428,11 @@ void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad,
#endif
DCHECK(resource_provider());
- // Pass in |frame_color_space| here instead of |dst_color_space| so the color
- // space transform going from SkImage to SkSurface is identity. The
- // SkColorFilter already handles color space conversion so this avoids
+ // Pass in |CurrentRenderPassSkColorSpace()| here instead of |dst_color_space|
+ // so the color space transform going from SkImage to SkSurface is identity.
+ // The SkColorFilter already handles color space conversion so this avoids
// applying the conversion twice.
- ScopedYUVSkImageBuilder builder(this, quad,
- frame_color_space.ToSkColorSpace());
+ ScopedYUVSkImageBuilder builder(this, quad, CurrentRenderPassSkColorSpace());
const SkImage* image = builder.sk_image();
if (!image)
return;
@@ -2384,9 +2467,14 @@ void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad,
}
void SkiaRenderer::ScheduleOverlays() {
- DCHECK(!current_frame_resource_fence_->WasSet());
+ DCHECK(!current_gpu_commands_completed_fence_->WasSet());
+ DCHECK(!current_release_fence_->WasSet());
+ // Always add an empty set of locks to be used in either SwapBuffersSkipped()
+ // or SwapBuffersComplete().
pending_overlay_locks_.emplace_back();
+ [[maybe_unused]] auto& locks = pending_overlay_locks_.back();
+
if (current_frame()->overlay_list.empty())
return;
@@ -2401,7 +2489,6 @@ void SkiaRenderer::ScheduleOverlays() {
// switched over to OverlayProcessor.
// TODO(weiliangc): Remove this when CrOS and Android SurfaceControl switch
// to OverlayProcessor as well.
- auto& locks = pending_overlay_locks_.back();
for (auto& overlay : current_frame()->overlay_list) {
// Resources will be unlocked after the next SwapBuffers() is completed.
locks.emplace_back(resource_provider(), overlay.resource_id);
@@ -2416,7 +2503,6 @@ void SkiaRenderer::ScheduleOverlays() {
DCHECK(!overlay.mailbox.IsZero());
}
#elif BUILDFLAG(IS_WIN)
- auto& locks = pending_overlay_locks_.back();
for (auto& dc_layer_overlay : current_frame()->overlay_list) {
for (size_t i = 0; i < DCLayerOverlay::kNumResources; ++i) {
ResourceId resource_id = dc_layer_overlay.resources[i];
@@ -2437,10 +2523,10 @@ void SkiaRenderer::ScheduleOverlays() {
DCHECK(!dc_layer_overlay.mailbox[0].IsZero());
}
#elif BUILDFLAG(IS_APPLE)
- auto& locks = pending_overlay_locks_.back();
for (CALayerOverlay& ca_layer_overlay : current_frame()->overlay_list) {
if (ca_layer_overlay.rpdq) {
PrepareRenderPassOverlay(&ca_layer_overlay);
+ locks.emplace_back(ca_layer_overlay.mailbox);
continue;
}
// Some overlays are for solid-color layers.
@@ -2465,15 +2551,14 @@ void SkiaRenderer::ScheduleOverlays() {
}
#elif defined(USE_OZONE)
// Only Wayland uses this code path.
- auto& locks = pending_overlay_locks_.back();
for (auto& overlay : current_frame()->overlay_list) {
if (overlay.rpdq) {
PrepareRenderPassOverlay(&overlay);
- // The output will be attached via mailbox when overlays are scheduled.
+ locks.emplace_back(overlay.mailbox);
continue;
}
// Solid Color quads do not have associated resource buffers.
- if (overlay.solid_color.has_value())
+ if (overlay.is_solid_color)
continue;
// Resources will be unlocked after the next SwapBuffers() is completed.
@@ -2495,17 +2580,11 @@ void SkiaRenderer::ScheduleOverlays() {
NOTREACHED();
#endif // BUILDFLAG(IS_ANDROID)
- base::OnceClosure on_finished_callback;
- if (current_frame_resource_fence_->WasSet()) {
- on_finished_callback = base::BindOnce(
- &FrameResourceFence::Signal, std::move(current_frame_resource_fence_));
- current_frame_resource_fence_ = base::MakeRefCounted<FrameResourceFence>();
- resource_provider()->SetReadLockFence(current_frame_resource_fence_.get());
- }
+ DCHECK(!current_gpu_commands_completed_fence_->WasSet());
+ DCHECK(!current_release_fence_->WasSet());
skia_output_surface_->ScheduleOverlays(
- std::move(current_frame()->overlay_list), std::move(sync_tokens),
- std::move(on_finished_callback));
+ std::move(current_frame()->overlay_list), std::move(sync_tokens));
}
sk_sp<SkColorFilter> SkiaRenderer::GetColorSpaceConversionFilter(
@@ -2513,13 +2592,8 @@ sk_sp<SkColorFilter> SkiaRenderer::GetColorSpaceConversionFilter(
const gfx::ColorSpace& dst,
float resource_offset,
float resource_multiplier) {
- // If the input color space is HDR, and it did not specify a white level,
- // override it with the frame's white level.
- gfx::ColorSpace adjusted_src = src.GetWithSDRWhiteLevel(
- current_frame()->display_color_spaces.GetSDRMaxLuminanceNits());
-
return color_filter_cache_.Get(
- adjusted_src, dst, resource_offset, resource_multiplier,
+ src, dst, resource_offset, resource_multiplier,
current_frame()->display_color_spaces.GetSDRMaxLuminanceNits(),
current_frame()->display_color_spaces.GetHDRMaxLuminanceRelative());
}
@@ -2768,7 +2842,7 @@ void SkiaRenderer::DrawRenderPassQuad(const AggregatedRenderPassDrawQuad* quad,
sk_sp<SkImage> content_image =
skia_output_surface_->MakePromiseSkImageFromRenderPass(
quad->render_pass_id, backing.size, backing.format,
- backing.generate_mipmap, backing.color_space.ToSkColorSpace(),
+ backing.generate_mipmap, RenderPassBackingSkColorSpace(backing),
backing.mailbox);
DLOG_IF(ERROR, !content_image)
<< "MakePromiseSkImageFromRenderPass() failed for render pass";
@@ -2852,16 +2926,7 @@ void SkiaRenderer::FinishDrawingQuadList() {
if (is_root_render_pass && UsingSkiaForDelegatedInk())
DrawDelegatedInkTrail();
- base::OnceClosure on_finished_callback;
- // Signal |current_frame_resource_fence_| when the root render pass is
- // finished.
- if (current_frame_resource_fence_->WasSet()) {
- on_finished_callback = base::BindOnce(
- &FrameResourceFence::Signal, std::move(current_frame_resource_fence_));
- current_frame_resource_fence_ = base::MakeRefCounted<FrameResourceFence>();
- resource_provider()->SetReadLockFence(current_frame_resource_fence_.get());
- }
- skia_output_surface_->EndPaint(std::move(on_finished_callback));
+ EndPaint(/*failed=*/false);
// Defer flushing drawing task for root render pass, to avoid extra
// MakeCurrent() call. It is expensive on GL.
@@ -2927,9 +2992,17 @@ void SkiaRenderer::AllocateRenderPassResourceIfNeeded(
// TODO(penghuang): check supported format correctly.
gpu::Capabilities caps;
caps.texture_format_bgra8888 = true;
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ // TODO(crbug.com/1317015): add support RGBA_F16 in LaCrOS.
+ auto format = color_space.IsHDR()
+ ? RGBA_1010102
+ : PlatformColor::BestSupportedTextureFormat(caps);
+#else
auto format = color_space.IsHDR()
? RGBA_F16
: PlatformColor::BestSupportedTextureFormat(caps);
+#endif
uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY;
if (requirements.generate_mipmap)
usage |= gpu::SHARED_IMAGE_USAGE_MIPMAP;
@@ -3058,7 +3131,7 @@ void SkiaRenderer::PrepareRenderPassOverlay(
ResourceFormat buffer_format{};
gfx::ColorSpace color_space;
- RenderPassBacking* backing = nullptr;
+ RenderPassBacking* src_quad_backing = nullptr;
auto bypass = render_pass_bypass_quads_.find(quad->render_pass_id);
BypassMode bypass_mode = BypassMode::kSkip;
// When Render Pass has a single quad inside we would draw that directly.
@@ -3077,9 +3150,9 @@ void SkiaRenderer::PrepareRenderPassOverlay(
DCHECK(render_pass_backings_.end() != it);
// This function is called after AllocateRenderPassResourceIfNeeded, so
// there should be backing ready.
- backing = &it->second;
- buffer_format = backing->format;
- color_space = backing->color_space;
+ src_quad_backing = &it->second;
+ buffer_format = src_quad_backing->format;
+ color_space = src_quad_backing->color_space;
}
// Adjust the overlay |buffer_size| to reduce memory fragmentation. It also
@@ -3090,16 +3163,50 @@ void SkiaRenderer::PrepareRenderPassOverlay(
// TODO(petermcneeley) : Support buffer rounding by dynamically changing
// texture uvs.
constexpr int kBufferMultiple = 1;
-#endif
+#endif // BUILDFLAG(IS_APPLE)
gfx::Size buffer_size(
cc::MathUtil::CheckedRoundUp(filter_bounds.width(), kBufferMultiple),
cc::MathUtil::CheckedRoundUp(filter_bounds.height(), kBufferMultiple));
- current_canvas_ = skia_output_surface_->BeginPaintRenderPassOverlay(
- buffer_size, buffer_format, /*mipmap=*/false,
- color_space.ToSkColorSpace());
+ auto it = std::find_if(available_render_pass_overlay_backings_.begin(),
+ available_render_pass_overlay_backings_.end(),
+ [&buffer_format, &buffer_size,
+ &color_space](const RenderPassBacking& backing) {
+ return backing.format == buffer_format &&
+ backing.size == buffer_size &&
+ backing.color_space == color_space;
+ });
+
+ if (it == available_render_pass_overlay_backings_.end()) {
+ // Allocate the image for render pass overlay if there is no existing
+ // available one.
+ constexpr auto kOverlayUsage = gpu::SHARED_IMAGE_USAGE_SCANOUT |
+ gpu::SHARED_IMAGE_USAGE_DISPLAY |
+ gpu::SHARED_IMAGE_USAGE_RASTER;
+ auto mailbox =
+ skia_output_surface_->GetSharedImageInterface()->CreateSharedImage(
+ buffer_format, buffer_size, color_space, kTopLeft_GrSurfaceOrigin,
+ kPremul_SkAlphaType, kOverlayUsage, gpu::kNullSurfaceHandle);
+ in_flight_render_pass_overlay_backings_.push_back(
+ RenderPassBacking{buffer_size, /*generate_mipmap=*/false, color_space,
+ buffer_format, mailbox});
+ overlay->mailbox = std::move(mailbox);
+ } else {
+ overlay->mailbox = std::move(it->mailbox);
+ in_flight_render_pass_overlay_backings_.push_back(std::move(*it));
+ available_render_pass_overlay_backings_.erase(it);
+ }
+ const RenderPassBacking& dst_overlay_backing =
+ in_flight_render_pass_overlay_backings_.back();
+
+ current_canvas_ = skia_output_surface_->BeginPaintRenderPass(
+ quad->render_pass_id, dst_overlay_backing.size,
+ dst_overlay_backing.format, /*mipmap=*/false,
+ RenderPassBackingSkColorSpace(dst_overlay_backing), /*is_overlay=*/true,
+ overlay->mailbox);
if (!current_canvas_) {
- DLOG(ERROR) << "BeginPaintRenderPassOverlay() failed.";
+ DLOG(ERROR)
+ << "BeginPaintRenderPass() in PrepareRenderPassOverlay() failed.";
return;
}
@@ -3126,19 +3233,20 @@ void SkiaRenderer::PrepareRenderPassOverlay(
NOTREACHED();
}
} else {
- DCHECK(backing);
+ DCHECK(src_quad_backing);
auto content_image = skia_output_surface_->MakePromiseSkImageFromRenderPass(
- quad->render_pass_id, backing->size, backing->format,
- backing->generate_mipmap, backing->color_space.ToSkColorSpace(),
- backing->mailbox);
+ quad->render_pass_id, src_quad_backing->size, src_quad_backing->format,
+ src_quad_backing->generate_mipmap,
+ RenderPassBackingSkColorSpace(*src_quad_backing),
+ src_quad_backing->mailbox);
if (!content_image) {
- DLOG(ERROR)
- << "MakePromiseSkImageFromRenderPass() failed for render pass";
- skia_output_surface_->EndPaintRenderPassOverlay();
+ DLOG(ERROR) << "MakePromiseSkImageFromRenderPass() in "
+ "PrepareRenderPassOverlay() failed.";
+ EndPaint(/*failed=*/true);
return;
}
- if (backing->generate_mipmap)
+ if (src_quad_backing->generate_mipmap)
params.sampling =
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
@@ -3153,19 +3261,47 @@ void SkiaRenderer::PrepareRenderPassOverlay(
}
current_canvas_ = nullptr;
- auto ddl = skia_output_surface_->EndPaintRenderPassOverlay();
- DCHECK(ddl);
- // Put overlay related information in CALayerOverlay,
- // so SkiaOutputSurfaceImplOnGpu can use the DDL to create overlay buffer and
- // play the DDL back to it accordingly.
- overlay->ddl = std::move(ddl);
+ EndPaint(/*failed=*/false);
// Adjust |bounds_rect| to contain the whole buffer and at the right location.
overlay->bounds_rect.set_origin(gfx::PointF(filter_bounds.origin()));
overlay->bounds_rect.set_size(gfx::SizeF(buffer_size));
}
-#endif
+#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+
+void SkiaRenderer::EndPaint(bool failed) {
+ base::OnceClosure on_finished_callback;
+ base::OnceCallback<void(gfx::GpuFenceHandle)> on_return_release_fence_cb;
+ // If SkiaRenderer has not failed, prepare callbacks and pass them to
+ // SkiaOutputSurface.
+ if (!failed) {
+ // Signal |current_frame_resource_fence_| when the root render pass is
+ // finished.
+ if (current_gpu_commands_completed_fence_->WasSet()) {
+ on_finished_callback =
+ base::BindOnce(&FrameResourceGpuCommandsCompletedFence::Signal,
+ std::move(current_gpu_commands_completed_fence_));
+ current_gpu_commands_completed_fence_ =
+ base::MakeRefCounted<FrameResourceGpuCommandsCompletedFence>();
+ resource_provider()->SetGpuCommandsCompletedFence(
+ current_gpu_commands_completed_fence_.get());
+ }
+
+ // Return a release fence to the |current_release_fence_|
+ // when the root render pass is finished.
+ if (current_release_fence_->WasSet()) {
+ on_return_release_fence_cb =
+ base::BindOnce(&FrameResourceReleaseFence::SetReleaseFenceCallback,
+ std::move(current_release_fence_));
+ current_release_fence_ =
+ base::MakeRefCounted<FrameResourceReleaseFence>();
+ resource_provider()->SetReleaseFence(current_release_fence_.get());
+ }
+ }
+ skia_output_surface_->EndPaint(std::move(on_finished_callback),
+ std::move(on_return_release_fence_cb));
+}
bool SkiaRenderer::IsRenderPassResourceAllocated(
const AggregatedRenderPassId& render_pass_id) const {
@@ -3223,23 +3359,42 @@ bool SkiaRenderer::UsingSkiaForDelegatedInk() const {
return delegated_ink_handler_ && delegated_ink_handler_->GetInkRenderer();
}
+SkiaRenderer::OverlayLock::OverlayLock(
+ DisplayResourceProvider* resource_provider,
+ ResourceId resource_id) {
+ resource_lock.emplace(resource_provider, resource_id);
+}
+
+SkiaRenderer::OverlayLock::~OverlayLock() = default;
+
+SkiaRenderer::OverlayLock::OverlayLock(SkiaRenderer::OverlayLock&& other) {
+ resource_lock = std::move(other.resource_lock);
+
#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
-bool SkiaRenderer::ScopedReadLockComparator::operator()(
- const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs,
- const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs) const {
- return lhs.mailbox() < rhs.mailbox();
+ render_pass_lock = std::move(other.render_pass_lock);
+#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
}
-bool SkiaRenderer::ScopedReadLockComparator::operator()(
- const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs,
- const gpu::Mailbox& rhs) const {
- return lhs.mailbox() < rhs;
+SkiaRenderer::OverlayLock& SkiaRenderer::OverlayLock::OverlayLock::operator=(
+ SkiaRenderer::OverlayLock&& other) {
+ resource_lock = std::move(other.resource_lock);
+
+#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+ render_pass_lock = std::move(other.render_pass_lock);
+#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+
+ return *this;
}
-bool SkiaRenderer::ScopedReadLockComparator::operator()(
- const gpu::Mailbox& lhs,
- const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs) const {
- return lhs < rhs.mailbox();
+#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+SkiaRenderer::OverlayLock::OverlayLock(gpu::Mailbox mailbox) {
+ render_pass_lock.emplace(mailbox);
+}
+
+bool SkiaRenderer::OverlayLockComparator::operator()(
+ const OverlayLock& lhs,
+ const OverlayLock& rhs) const {
+ return lhs.mailbox() < rhs.mailbox();
}
#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
diff --git a/chromium/components/viz/service/display/skia_renderer.h b/chromium/components/viz/service/display/skia_renderer.h
index d8912a61296..7922aee3b61 100644
--- a/chromium/components/viz/service/display/skia_renderer.h
+++ b/chromium/components/viz/service/display/skia_renderer.h
@@ -7,6 +7,7 @@
#include <memory>
#include <tuple>
+#include <utility>
#include <vector>
#include "base/containers/flat_map.h"
@@ -16,7 +17,6 @@
#include "cc/cc_export.h"
#include "components/viz/service/display/direct_renderer.h"
#include "components/viz/service/display/display_resource_provider_skia.h"
-#include "components/viz/service/display/sync_query_collection.h"
#include "components/viz/service/viz_service_export.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/color_conversion_sk_filter_cache.h"
@@ -195,7 +195,7 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
DrawQuadParams* params);
void DrawPaintOpBuffer(const cc::PaintOpBuffer* buffer,
- const absl::optional<SkColor>& clear_color,
+ const absl::optional<SkColor4f>& clear_color,
const TileDrawQuad* quad,
const DrawQuadParams* params);
@@ -264,6 +264,11 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
OverlayProcessorInterface::PlatformOverlayCandidate* overlay);
#endif
+ // Sets up callbacks for frame resource fences and passes them to
+ // SkiaOutputSurface by calling EndPaint on that. If |failed|,
+ // SkiaOutputSurface::EndPaint will be called with null callbacks.
+ void EndPaint(bool failed);
+
DisplayResourceProviderSkia* resource_provider() {
return static_cast<DisplayResourceProviderSkia*>(resource_provider_);
}
@@ -278,14 +283,22 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
};
base::flat_map<AggregatedRenderPassId, RenderPassBacking>
render_pass_backings_;
+ sk_sp<SkColorSpace> RenderPassBackingSkColorSpace(
+ const RenderPassBacking& backing) {
+ return backing.color_space.ToSkColorSpace(CurrentFrameSDRWhiteLevel());
+ }
// Interface used for drawing. Common among different draw modes.
sk_sp<SkSurface> root_surface_;
raw_ptr<SkCanvas> root_canvas_ = nullptr;
raw_ptr<SkCanvas> current_canvas_ = nullptr;
raw_ptr<SkSurface> current_surface_ = nullptr;
- class FrameResourceFence;
- scoped_refptr<FrameResourceFence> current_frame_resource_fence_;
+
+ class FrameResourceGpuCommandsCompletedFence;
+ scoped_refptr<FrameResourceGpuCommandsCompletedFence>
+ current_gpu_commands_completed_fence_;
+ class FrameResourceReleaseFence;
+ scoped_refptr<FrameResourceReleaseFence> current_release_fence_;
bool disable_picture_quad_image_filtering_ = false;
bool is_scissor_enabled_ = false;
@@ -326,42 +339,88 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
absl::optional<DisplayResourceProviderSkia::LockSetForExternalUse>
lock_set_for_external_use_;
- // Locks for overlays are pending for swapbuffers.
- base::circular_deque<
- std::vector<DisplayResourceProviderSkia::ScopedReadLockSharedImage>>
- pending_overlay_locks_;
+ struct OverlayLock {
+ OverlayLock(DisplayResourceProvider* resource_provider,
+ ResourceId resource_id);
+
+#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+ explicit OverlayLock(gpu::Mailbox mailbox);
+#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+
+ ~OverlayLock();
+
+ OverlayLock(OverlayLock&& other);
+ OverlayLock& operator=(OverlayLock&& other);
+
+ OverlayLock(const OverlayLock&) = delete;
+ OverlayLock& operator=(const OverlayLock&) = delete;
+
+ const gpu::Mailbox& mailbox() const {
+#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+ if (render_pass_lock.has_value()) {
+ return *render_pass_lock;
+ }
+#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+
+ DCHECK(resource_lock.has_value());
+ return resource_lock->mailbox();
+ }
+
+ const gpu::SyncToken& sync_token() const {
+ DCHECK(resource_lock.has_value());
+ return resource_lock->sync_token();
+ }
+
+ void SetReleaseFence(gfx::GpuFenceHandle release_fence) {
+ if (resource_lock.has_value()) {
+ resource_lock->SetReleaseFence(std::move(release_fence));
+ }
+ }
+
+ bool HasReadLockFence() {
+ if (resource_lock.has_value()) {
+ return resource_lock->HasReadLockFence();
+ }
+ return false;
+ }
+
+ // Either resource_lock is set for non render pass overlays (i.e. videos),
+ // or render_pass_lock is set for render pass overlays.
+ absl::optional<DisplayResourceProviderSkia::ScopedReadLockSharedImage>
+ resource_lock;
+
+#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+ absl::optional<gpu::Mailbox> render_pass_lock;
+#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
+ };
+
+ // Locks for overlays that are pending for SwapBuffers().
+ base::circular_deque<std::vector<OverlayLock>> pending_overlay_locks_;
- // Locks for overlays have been committed. |pending_overlay_locks_| will
- // be moved to |committed_overlay_locks_| after SwapBuffers() completed.
- std::vector<DisplayResourceProviderSkia::ScopedReadLockSharedImage>
- committed_overlay_locks_;
+ // Locks for overlays that have been committed. |pending_overlay_locks_| will
+ // be moved to |committed_overlay_locks_| after SwapBuffers() is completed.
+ std::vector<OverlayLock> committed_overlay_locks_;
// Locks for overlays that have release fences and read lock fences.
- base::circular_deque<
- std::vector<DisplayResourceProviderSkia::ScopedReadLockSharedImage>>
+ base::circular_deque<std::vector<OverlayLock>>
read_lock_release_fence_overlay_locks_;
#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
- class ScopedReadLockComparator {
+ class OverlayLockComparator {
public:
using is_transparent = void;
- bool operator()(
- const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs,
- const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs)
- const;
- bool operator()(
- const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs,
- const gpu::Mailbox& rhs) const;
- bool operator()(
- const gpu::Mailbox& lhs,
- const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs)
- const;
+ bool operator()(const OverlayLock& lhs, const OverlayLock& rhs) const;
};
- // a set for locks of overlays which are waiting for releasing.
- // The set is using lock.mailbox() as the unique key.
- base::flat_set<DisplayResourceProviderSkia::ScopedReadLockSharedImage,
- ScopedReadLockComparator>
+
+ // A set for locks of overlays which are waiting to be released, using
+ // mailbox() as the unique key.
+ base::flat_set<OverlayLock, OverlayLockComparator>
awaiting_release_overlay_locks_;
+
+ // Tracks render pass overlay backings that are currently in use and available
+ // for re-using via mailboxes. RenderPassBacking.generate_mipmap is not used.
+ std::vector<RenderPassBacking> in_flight_render_pass_overlay_backings_;
+ std::vector<RenderPassBacking> available_render_pass_overlay_backings_;
#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
gfx::ColorConversionSkFilterCache color_filter_cache_;
diff --git a/chromium/components/viz/service/display/software_renderer.cc b/chromium/components/viz/service/display/software_renderer.cc
index 945245b1299..7ee2ca60256 100644
--- a/chromium/components/viz/service/display/software_renderer.cc
+++ b/chromium/components/viz/service/display/software_renderer.cc
@@ -134,7 +134,6 @@ void SoftwareRenderer::EnsureScissorTestDisabled() {
}
void SoftwareRenderer::BindFramebufferToOutputSurface() {
- DCHECK(!output_surface_->HasExternalStencilTest());
DCHECK(!root_canvas_);
current_framebuffer_canvas_.reset();
@@ -367,21 +366,20 @@ void SoftwareRenderer::DoDrawQuad(const DrawQuad* quad,
}
void SoftwareRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
- // We need to apply the matrix manually to have pixel-sized stroke width.
- SkPoint vertices[4];
- gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices);
- SkPoint transformed_vertices[4];
- current_canvas_->getTotalMatrix().mapPoints(transformed_vertices, vertices,
- 4);
+ SkMatrix m = current_canvas_->getTotalMatrix();
current_canvas_->resetMatrix();
+ SkPath path;
+ path.addRect(gfx::RectFToSkRect(QuadVertexRect()));
+ path.transform(m);
+
current_paint_.setColor(quad->color);
- current_paint_.setAlpha(quad->shared_quad_state->opacity *
- SkColorGetA(quad->color));
+ current_paint_.setAlphaf(quad->shared_quad_state->opacity * quad->color.fA);
current_paint_.setStyle(SkPaint::kStroke_Style);
+ current_paint_.setStrokeJoin(SkPaint::kMiter_Join);
current_paint_.setStrokeWidth(quad->width);
- current_canvas_->drawPoints(SkCanvas::kPolygon_PointMode, 4,
- transformed_vertices, current_paint_);
+
+ current_canvas_->drawPath(path, current_paint_);
}
void SoftwareRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
@@ -429,8 +427,7 @@ void SoftwareRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad) {
gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
current_paint_.setColor(quad->color);
- current_paint_.setAlpha(quad->shared_quad_state->opacity *
- SkColorGetA(quad->color));
+ current_paint_.setAlphaf(quad->shared_quad_state->opacity * quad->color.fA);
current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect),
current_paint_);
}
@@ -462,7 +459,7 @@ void SoftwareRenderer::DrawTextureQuad(const TextureDrawQuad* quad) {
current_canvas_->scale(1, -1);
bool blend_background =
- quad->background_color != SK_ColorTRANSPARENT && !image->isOpaque();
+ quad->background_color != SkColors::kTransparent && !image->isOpaque();
bool needs_layer = blend_background && (current_paint_.getAlpha() != 0xFF);
if (needs_layer) {
current_canvas_->saveLayerAlpha(&quad_rect, current_paint_.getAlpha());
@@ -592,9 +589,9 @@ void SoftwareRenderer::DrawRenderPassQuad(
void SoftwareRenderer::DrawUnsupportedQuad(const DrawQuad* quad) {
#ifdef NDEBUG
- current_paint_.setColor(SK_ColorWHITE);
+ current_paint_.setColor(SkColors::kWhite);
#else
- current_paint_.setColor(SK_ColorMAGENTA);
+ current_paint_.setColor(SkColors::kMagenta);
#endif
current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
@@ -604,8 +601,7 @@ void SoftwareRenderer::DrawUnsupportedQuad(const DrawQuad* quad) {
void SoftwareRenderer::CopyDrawnRenderPass(
const copy_output::RenderPassGeometry& geometry,
std::unique_ptr<CopyOutputRequest> request) {
- sk_sp<SkColorSpace> color_space =
- CurrentRenderPassColorSpace().ToSkColorSpace();
+ sk_sp<SkColorSpace> color_space = CurrentRenderPassSkColorSpace();
DCHECK(color_space);
SkBitmap bitmap;
diff --git a/chromium/components/viz/service/display/software_renderer_unittest.cc b/chromium/components/viz/service/display/software_renderer_unittest.cc
index f706e41e9f1..521d833e6b9 100644
--- a/chromium/components/viz/service/display/software_renderer_unittest.cc
+++ b/chromium/components/viz/service/display/software_renderer_unittest.cc
@@ -25,6 +25,7 @@
#include "components/viz/common/quads/compositor_frame_metadata.h"
#include "components/viz/common/quads/compositor_render_pass.h"
#include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
+#include "components/viz/common/quads/debug_border_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/tile_draw_quad.h"
#include "components/viz/common/resources/bitmap_allocation.h"
@@ -46,8 +47,8 @@ class SoftwareRendererTest : public testing::Test {
public:
void InitializeRenderer(
std::unique_ptr<SoftwareOutputDevice> software_output_device) {
- output_surface_ =
- FakeOutputSurface::CreateSoftware(std::move(software_output_device));
+ output_surface_ = std::make_unique<FakeSoftwareOutputSurface>(
+ std::move(software_output_device));
output_surface_->BindToClient(&output_surface_client_);
shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
@@ -134,7 +135,7 @@ class SoftwareRendererTest : public testing::Test {
RendererSettings settings_;
DebugRendererSettings debug_settings_;
cc::FakeOutputSurfaceClient output_surface_client_;
- std::unique_ptr<FakeOutputSurface> output_surface_;
+ std::unique_ptr<FakeSoftwareOutputSurface> output_surface_;
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
std::unique_ptr<DisplayResourceProviderSoftware> resource_provider_;
std::unique_ptr<ClientResourceProvider> child_resource_provider_;
@@ -187,6 +188,78 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) {
output->getColor(inner_size.width() - 1, inner_size.height() - 1));
}
+TEST_F(SoftwareRendererTest, DebugBorderDrawQuad) {
+ gfx::Size rect_size(10, 10);
+ gfx::Size full_size(100, 100);
+ gfx::Rect screen_rect(full_size);
+ gfx::Rect rect_1(rect_size);
+ gfx::Rect rect_2(gfx::Point(1, 1), rect_size);
+ gfx::Rect rect_3(gfx::Point(2, 2), rect_size);
+ gfx::Rect rect_4(gfx::Point(3, 3), rect_size);
+
+ InitializeRenderer(std::make_unique<SoftwareOutputDevice>());
+
+ AggregatedRenderPassId root_render_pass_id{1};
+ auto root_render_pass = std::make_unique<AggregatedRenderPass>();
+ root_render_pass->SetNew(root_render_pass_id, screen_rect, screen_rect,
+ gfx::Transform());
+ SharedQuadState* shared_quad_state =
+ root_render_pass->CreateAndAppendSharedQuadState();
+ shared_quad_state->SetAll(gfx::Transform(), screen_rect, screen_rect,
+ gfx::MaskFilterInfo(), absl::nullopt, true, 1.0,
+ SkBlendMode::kSrcOver, 0);
+
+ auto* quad_1 =
+ root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
+ quad_1->SetNew(shared_quad_state, rect_1, rect_1, SK_ColorCYAN, false);
+ auto* quad_2 =
+ root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
+ quad_2->SetNew(shared_quad_state, rect_2, rect_2, SK_ColorMAGENTA, false);
+
+ auto* quad_3 =
+ root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
+ quad_3->SetNew(shared_quad_state, rect_3, rect_3, SK_ColorYELLOW, false);
+
+ // Test one non-opaque color
+ SkColor semi_transparent_white = SkColorSetARGB(127, 255, 255, 255);
+ auto* quad_4 =
+ root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
+ quad_4->SetNew(shared_quad_state, rect_4, rect_4, semi_transparent_white,
+ false);
+
+ AggregatedRenderPassList list;
+ list.push_back(std::move(root_render_pass));
+
+ float device_scale_factor = 1.f;
+ std::unique_ptr<SkBitmap> output =
+ DrawAndCopyOutput(&list, device_scale_factor, full_size);
+ EXPECT_EQ(screen_rect.width(), output->info().width());
+ EXPECT_EQ(screen_rect.height(), output->info().height());
+
+ // Top left corners
+ EXPECT_EQ(SK_ColorCYAN, output->getColor(0, 0));
+ EXPECT_EQ(SK_ColorMAGENTA, output->getColor(1, 1));
+ EXPECT_EQ(SK_ColorYELLOW, output->getColor(2, 2));
+ // The corners end up being more opaque due to the miter, go one to the right
+ EXPECT_EQ(semi_transparent_white, output->getColor(3, 4));
+
+ // Un-drawn pixels as the quads are just outlines
+ EXPECT_EQ(SK_ColorTRANSPARENT, output->getColor(4, 4));
+ EXPECT_EQ(SK_ColorTRANSPARENT,
+ output->getColor(rect_size.width() - 2, rect_size.height() - 2));
+
+ // The bottom rightmost pixel of these quads are not filled because of the
+ // SkPaint::kMiter_Join StrokeJoin, go one pixel to the left
+ EXPECT_EQ(SK_ColorCYAN,
+ output->getColor(rect_size.width() - 1, rect_size.height()));
+ EXPECT_EQ(SK_ColorMAGENTA,
+ output->getColor(rect_size.width(), rect_size.height() + 1));
+ EXPECT_EQ(SK_ColorYELLOW,
+ output->getColor(rect_size.width() + 1, rect_size.height() + 2));
+ EXPECT_EQ(semi_transparent_white,
+ output->getColor(rect_size.width() + 2, rect_size.height() + 3));
+}
+
TEST_F(SoftwareRendererTest, TileQuad) {
gfx::Size outer_size(100, 100);
gfx::Size inner_size(98, 98);
diff --git a/chromium/components/viz/service/display/static_geometry_binding.cc b/chromium/components/viz/service/display/static_geometry_binding.cc
deleted file mode 100644
index 1dc0e9b717a..00000000000
--- a/chromium/components/viz/service/display/static_geometry_binding.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// 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.
-
-#include "components/viz/service/display/static_geometry_binding.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "ui/gfx/geometry/rect_f.h"
-
-namespace viz {
-
-StaticGeometryBinding::StaticGeometryBinding(gpu::gles2::GLES2Interface* gl,
- const gfx::RectF& quad_vertex_rect)
- : gl_(gl), quad_vertices_vbo_(0), quad_elements_vbo_(0) {
- GeometryBindingQuad quads[NUM_QUADS];
- GeometryBindingQuadIndex quad_indices[NUM_QUADS];
-
- static_assert(sizeof(GeometryBindingQuad) == 24 * sizeof(float),
- "struct Quad should be densely packed");
- static_assert(sizeof(GeometryBindingQuadIndex) == 6 * sizeof(uint16_t),
- "struct QuadIndex should be densely packed");
-
- for (size_t i = 0; i < NUM_QUADS; i++) {
- GeometryBindingVertex v0 = {
- {quad_vertex_rect.x(), quad_vertex_rect.bottom(), 0.0f},
- {0.0f, 1.0f},
- i * 4.0f + 0.0f};
- GeometryBindingVertex v1 = {
- {quad_vertex_rect.x(), quad_vertex_rect.y(), 0.0f},
- {0.0f, 0.0f},
- i * 4.0f + 1.0f};
- GeometryBindingVertex v2 = {
- {quad_vertex_rect.right(), quad_vertex_rect.y(), 0.0f},
- {1.0f, 0.0f},
- i * 4.0f + 2.0f};
- GeometryBindingVertex v3 = {
- {quad_vertex_rect.right(), quad_vertex_rect.bottom(), 0.0f},
- {1.0f, 1.0f},
- i * 4.0f + 3.0f};
- GeometryBindingQuad x(v0, v1, v2, v3);
- quads[i] = x;
- GeometryBindingQuadIndex y(
- static_cast<uint16_t>(0 + 4 * i), static_cast<uint16_t>(1 + 4 * i),
- static_cast<uint16_t>(2 + 4 * i), static_cast<uint16_t>(3 + 4 * i),
- static_cast<uint16_t>(0 + 4 * i), static_cast<uint16_t>(2 + 4 * i));
- quad_indices[i] = y;
- }
-
- gl_->GenBuffers(1, &quad_vertices_vbo_);
- gl_->GenBuffers(1, &quad_elements_vbo_);
-
- gl_->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_);
- gl_->BufferData(GL_ARRAY_BUFFER, sizeof(GeometryBindingQuad) * NUM_QUADS,
- quads, GL_STATIC_DRAW);
-
- gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_);
- gl_->BufferData(GL_ELEMENT_ARRAY_BUFFER,
- sizeof(GeometryBindingQuadIndex) * NUM_QUADS, &quad_indices,
- GL_STATIC_DRAW);
-}
-
-StaticGeometryBinding::~StaticGeometryBinding() {
- gl_->DeleteBuffers(1, &quad_vertices_vbo_);
- gl_->DeleteBuffers(1, &quad_elements_vbo_);
-}
-
-void StaticGeometryBinding::PrepareForDraw() {
- SetupGLContext(gl_, quad_elements_vbo_, quad_vertices_vbo_);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/static_geometry_binding.h b/chromium/components/viz/service/display/static_geometry_binding.h
deleted file mode 100644
index 2a09459426c..00000000000
--- a/chromium/components/viz/service/display/static_geometry_binding.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_STATIC_GEOMETRY_BINDING_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_STATIC_GEOMETRY_BINDING_H_
-
-#include "base/memory/raw_ptr.h"
-#include "components/viz/service/display/geometry_binding.h"
-#include "components/viz/service/viz_service_export.h"
-
-using gpu::gles2::GLES2Interface;
-
-namespace viz {
-
-class VIZ_SERVICE_EXPORT StaticGeometryBinding {
- public:
- StaticGeometryBinding(gpu::gles2::GLES2Interface* gl,
- const gfx::RectF& quad_vertex_rect);
-
- StaticGeometryBinding(const StaticGeometryBinding&) = delete;
- StaticGeometryBinding& operator=(const StaticGeometryBinding&) = delete;
-
- ~StaticGeometryBinding();
-
- void PrepareForDraw();
-
- enum {
- NUM_QUADS = 9,
- };
-
- private:
- raw_ptr<gpu::gles2::GLES2Interface> gl_;
-
- GLuint quad_vertices_vbo_;
- GLuint quad_elements_vbo_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_STATIC_GEOMETRY_BINDING_H_
diff --git a/chromium/components/viz/service/display/surface_aggregator.cc b/chromium/components/viz/service/display/surface_aggregator.cc
index 8d8eec8d786..24d580b44ba 100644
--- a/chromium/components/viz/service/display/surface_aggregator.cc
+++ b/chromium/components/viz/service/display/surface_aggregator.cc
@@ -302,16 +302,19 @@ SurfaceAggregator::SurfaceAggregator(
aggregate_only_damaged_(aggregate_only_damaged),
needs_surface_damage_rect_list_(needs_surface_damage_rect_list),
de_jelly_enabled_(DeJellyEnabled()),
- clip_prewalk_damage_(features::IsClipPrewalkDamageEnabled()),
extra_pass_for_readback_option_(extra_pass_option) {
DCHECK(manager_);
DCHECK(provider_);
+ manager_->AddObserver(this);
}
SurfaceAggregator::~SurfaceAggregator() {
- // Notify client of all surfaces being removed.
+ manager_->RemoveObserver(this);
+
contained_surfaces_.clear();
contained_frame_sinks_.clear();
+
+ // Notify client of all surfaces being removed.
ProcessAddedAndRemovedSurfaces();
}
@@ -534,10 +537,21 @@ bool SurfaceAggregator::CanPotentiallyMergePass(
sqs->de_jelly_delta_y == 0;
}
+void SurfaceAggregator::OnSurfaceDestroyed(const SurfaceId& surface_id) {
+ DCHECK(!is_inside_aggregate_);
+
+ auto iter = resolved_frames_.find(surface_id);
+ if (iter != resolved_frames_.end()) {
+ TRACE_EVENT0("viz", "SurfaceAggregator::SurfaceDestroyed");
+ ReleaseResources(surface_id);
+ resolved_frames_.erase(iter);
+ }
+}
+
const ResolvedFrameData* SurfaceAggregator::GetLatestFrameData(
const SurfaceId& surface_id) {
- auto* surface = manager_->GetSurfaceForId(surface_id);
- return GetResolvedFrame(surface, /*inside_aggregation=*/false);
+ DCHECK(!is_inside_aggregate_);
+ return GetResolvedFrame(surface_id);
}
ResolvedFrameData* SurfaceAggregator::GetResolvedFrame(
@@ -546,59 +560,51 @@ ResolvedFrameData* SurfaceAggregator::GetResolvedFrame(
// this aggregation, then find ResolvedFrameData for that surface.
auto iter = resolved_surface_ranges_.find(range);
if (iter == resolved_surface_ranges_.end()) {
- iter = resolved_surface_ranges_
- .emplace(range, manager_->GetLatestInFlightSurface(range))
- .first;
+ auto* surface = manager_->GetLatestInFlightSurface(range);
+ SurfaceId surface_id = surface ? surface->surface_id() : SurfaceId();
+ iter = resolved_surface_ranges_.emplace(range, surface_id).first;
}
- return GetResolvedFrame(iter->second, /*inside_aggregation=*/true);
-}
+ if (!iter->second.is_valid()) {
+ // There is no surface for `range`.
+ return nullptr;
+ }
-ResolvedFrameData* SurfaceAggregator::GetResolvedFrame(
- const SurfaceId& surface_id) {
- return GetResolvedFrame(manager_->GetSurfaceForId(surface_id),
- /*inside_aggregation=*/true);
+ return GetResolvedFrame(iter->second);
}
ResolvedFrameData* SurfaceAggregator::GetResolvedFrame(
- Surface* surface,
- bool inside_aggregation) {
- if (!surface || !surface->HasActiveFrame()) {
- // If there is no resolved surface or the surface has no active frame there
- // is no resolved frame data to return.
- return nullptr;
- }
+ const SurfaceId& surface_id) {
+ DCHECK(surface_id.is_valid());
- auto iter = resolved_frames_.find(surface);
+ auto iter = resolved_frames_.find(surface_id);
if (iter == resolved_frames_.end()) {
- iter =
- resolved_frames_
- .emplace(std::piecewise_construct, std::forward_as_tuple(surface),
- std::forward_as_tuple(surface->surface_id(), surface))
- .first;
+ auto* surface = manager_->GetSurfaceForId(surface_id);
+ if (!surface || !surface->HasActiveFrame()) {
+ // If there is no resolved surface or the surface has no active frame
+ // there is no resolved frame data to return.
+ return nullptr;
+ }
+
+ iter = resolved_frames_
+ .emplace(std::piecewise_construct,
+ std::forward_as_tuple(surface_id),
+ std::forward_as_tuple(surface->surface_id(), surface))
+ .first;
}
ResolvedFrameData& resolved_frame = iter->second;
- DCHECK_EQ(resolved_frame.surface(), surface);
-
- // Verify that a new surface wasn't created at the same address as a deleted
- // surface with a different SurfaceId.
- // TODO(kylechar): Invalidate cached resolved frame data when
- // SurfaceObserver signals the surface has been destroyed instead.
- if (resolved_frame.surface_id() != surface->surface_id()) {
- resolved_frames_.erase(iter);
- return GetResolvedFrame(surface, inside_aggregation);
- }
+ Surface* surface = resolved_frame.surface();
// Mark the frame as used this aggregation so it persists.
- bool first_use = inside_aggregation ? resolved_frame.MarkAsUsed() : true;
+ bool first_use = is_inside_aggregate_ ? resolved_frame.MarkAsUsed() : true;
if (first_use) {
// If there is a new CompositorFrame for `surface` compute resolved frame
// data for the new resolved CompositorFrame.
if (resolved_frame.frame_index() != surface->GetActiveFrameIndex() ||
surface->HasSurfaceAnimationDamage()) {
- DCHECK(inside_aggregation);
+ DCHECK(is_inside_aggregate_);
base::ElapsedTimer timer;
ProcessResolvedFrame(resolved_frame);
stats_->declare_resources_time += timer.Elapsed();
@@ -969,15 +975,17 @@ void SurfaceAggregator::EmitDefaultBackgroundColorQuad(
// No matching surface was found so create a SolidColorDrawQuad with the
// SurfaceDrawQuad default background color.
- SkColor background_color = surface_quad->default_background_color;
+ SkColor4f background_color = surface_quad->default_background_color;
auto* shared_quad_state =
CopySharedQuadState(surface_quad->shared_quad_state, target_transform,
clip_rect, mask_filter_info, dest_pass);
auto* solid_color_quad =
dest_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+ // TODO(crbug/1308932) remove toSkColor and make all SkColor4f
solid_color_quad->SetNew(shared_quad_state, surface_quad->rect,
- surface_quad->visible_rect, background_color, false);
+ surface_quad->visible_rect,
+ background_color.toSkColor(), false);
}
void SurfaceAggregator::EmitGutterQuadsIfNecessary(
@@ -1262,6 +1270,9 @@ void SurfaceAggregator::CopyQuadsToPass(
const DrawQuad* quad_with_overlay_damage_index = nullptr;
if (needs_surface_damage_rect_list_ &&
resolved_pass.aggregation().will_draw) {
+ // TODO(crbug.com/1323002): If there is one specific quad for this pass's
+ // damage we should move the allocation of the damage index below to be
+ // consistent with quad ordering.
quad_with_overlay_damage_index =
FindQuadWithOverlayDamage(source_pass, dest_pass, target_transform,
surface, &overlay_damage_index);
@@ -1727,13 +1738,11 @@ gfx::Rect SurfaceAggregator::PrewalkRenderPass(
}
}
- if (clip_prewalk_damage_) {
- // Clip the quad damage to the quad visible before converting back to
- // render pass coordinate space. Expanded damage outside the quad rect for
- // filters are added to |damage_rect| directly so this only clips damage
- // from drawing the quad itself.
- quad_damage_rect.Intersect(quad->visible_rect);
- }
+ // Clip the quad damage to the quad visible before converting back to
+ // render pass coordinate space. Expanded damage outside the quad rect for
+ // filters are added to |damage_rect| directly so this only clips damage
+ // from drawing the quad itself.
+ quad_damage_rect.Intersect(quad->visible_rect);
if (!quad_damage_rect.IsEmpty()) {
// Convert the quad damage rect into its target space and clip it if
@@ -1758,15 +1767,13 @@ gfx::Rect SurfaceAggregator::PrewalkRenderPass(
damage_rect.Union(render_pass.output_rect);
}
- if (clip_prewalk_damage_) {
- // The added damage from quads in the render pass is transformed back
- // into the render pass coordinate space without clipping, so it can
- // extend beyond the edge of the current render pass. Coordinates outside
- // the output_rect are invalid in this render passes coordinate space but
- // they may be valid coordinates in the embedder coordinate space, causing
- // unnecessary damage expansion.
- damage_rect.Intersect(render_pass.output_rect);
- }
+ // The added damage from quads in the render pass is transformed back into
+ // the render pass coordinate space without clipping, so it can extend
+ // beyond the edge of the current render pass. Coordinates outside the
+ // output_rect are invalid in this render passes coordinate space but they
+ // may be valid coordinates in the embedder coordinate space, causing
+ // unnecessary damage expansion.
+ damage_rect.Intersect(render_pass.output_rect);
}
return damage_rect;
@@ -1982,23 +1989,26 @@ AggregatedFrame SurfaceAggregator::Aggregate(
const gfx::Rect& target_damage,
int64_t display_trace_id) {
DCHECK(!expected_display_time.is_null());
-
- root_surface_id_ = surface_id;
- Surface* surface = manager_->GetSurfaceForId(surface_id);
- DCHECK(surface);
DCHECK(contained_surfaces_.empty());
- CheckFrameSinksChanged(surface);
+ DCHECK(!is_inside_aggregate_);
+ is_inside_aggregate_ = true;
+
+ root_surface_id_ = surface_id;
// Start recording new stats for this aggregation.
stats_.emplace();
base::ElapsedTimer prewalk_timer;
- ResolvedFrameData* resolved_frame =
- GetResolvedFrame(surface, /*inside_aggregation=*/true);
+ ResolvedFrameData* resolved_frame = GetResolvedFrame(surface_id);
- if (!resolved_frame || !resolved_frame->is_valid())
+ if (!resolved_frame || !resolved_frame->is_valid()) {
+ ResetAfterAggregate();
return {};
+ }
+
+ Surface* surface = resolved_frame->surface();
+ CheckFrameSinksChanged(surface);
display_trace_id_ = display_trace_id;
expected_display_time_ = expected_display_time;
@@ -2162,6 +2172,9 @@ void SurfaceAggregator::RecordStatHistograms() {
}
void SurfaceAggregator::ResetAfterAggregate() {
+ DCHECK(is_inside_aggregate_);
+
+ is_inside_aggregate_ = false;
dest_pass_list_ = nullptr;
surface_damage_rect_list_ = nullptr;
current_zero_damage_rect_is_not_recorded_ = false;
diff --git a/chromium/components/viz/service/display/surface_aggregator.h b/chromium/components/viz/service/display/surface_aggregator.h
index 338dc871b87..03482fd6129 100644
--- a/chromium/components/viz/service/display/surface_aggregator.h
+++ b/chromium/components/viz/service/display/surface_aggregator.h
@@ -23,6 +23,7 @@
#include "components/viz/common/surfaces/surface_range.h"
#include "components/viz/service/display/aggregated_frame.h"
#include "components/viz/service/display/resolved_frame_data.h"
+#include "components/viz/service/surfaces/surface_observer.h"
#include "components/viz/service/viz_service_export.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/delegated_ink_metadata.h"
@@ -37,7 +38,7 @@ class SurfaceManager;
struct MaskFilterInfoExt;
-class VIZ_SERVICE_EXPORT SurfaceAggregator {
+class VIZ_SERVICE_EXPORT SurfaceAggregator : public SurfaceObserver {
public:
using SurfaceIndexMap = base::flat_map<SurfaceId, uint64_t>;
using FrameSinkIdMap = base::flat_map<FrameSinkId, LocalSurfaceId>;
@@ -73,7 +74,7 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
SurfaceAggregator(const SurfaceAggregator&) = delete;
SurfaceAggregator& operator=(const SurfaceAggregator&) = delete;
- ~SurfaceAggregator();
+ ~SurfaceAggregator() override;
// These constants are used for all time related metrics recorded in
// SurfaceAggregator.
@@ -96,7 +97,6 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
// Aggregate() to make sure no CompositorFrame did arrive between the calls.
const ResolvedFrameData* GetLatestFrameData(const SurfaceId& surface_id);
- void ReleaseResources(const SurfaceId& surface_id);
const SurfaceIndexMap& previous_contained_surfaces() const {
return previous_contained_surfaces_;
}
@@ -135,13 +135,16 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
base::TimeDelta declare_resources_time;
};
+ // SurfaceObserver implementation.
+ void OnSurfaceDestroyed(const SurfaceId& surface_id) override;
+
+ void ReleaseResources(const SurfaceId& surface_id);
+
// Get resolved frame data for the resolved surfaces active frame. Returns
// null if there is no matching surface or the surface doesn't have an active
// CompositorFrame.
ResolvedFrameData* GetResolvedFrame(const SurfaceRange& range);
ResolvedFrameData* GetResolvedFrame(const SurfaceId& surface_id);
- ResolvedFrameData* GetResolvedFrame(Surface* surface,
- bool inside_aggregation);
// - |source_pass| is the render pass that contains |surface_quad|.
// - |target_transform| is the transform from the coordinate space of
@@ -386,10 +389,11 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
// Whether de-jelly may be active.
const bool de_jelly_enabled_;
- const bool clip_prewalk_damage_;
-
const ExtraPassForReadbackOption extra_pass_for_readback_option_;
+ // Will be true for duration of Aggregate() function.
+ bool is_inside_aggregate_ = false;
+
bool output_is_secure_ = false;
// Whether |CopyOutputRequests| should be moved over to the aggregated frame.
@@ -410,6 +414,9 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
// The id for the optional render pass used to apply the display transform.
AggregatedRenderPassId display_transform_render_pass_id_;
+ // Persistent storage for ResolvedFrameData.
+ std::map<SurfaceId, ResolvedFrameData> resolved_frames_;
+
base::flat_map<SurfaceId, int> surface_id_to_resource_child_id_;
// The following state is only valid for the duration of one Aggregate call
@@ -439,8 +446,8 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
base::TimeTicks expected_display_time_;
int64_t display_trace_id_ = -1;
- // Map from SurfaceRange to Surface for current aggregation.
- base::flat_map<SurfaceRange, Surface*> resolved_surface_ranges_;
+ // Map from SurfaceRange to SurfaceId for current aggregation.
+ base::flat_map<SurfaceRange, SurfaceId> resolved_surface_ranges_;
// The root damage rect of the currently-aggregating frame.
gfx::Rect root_damage_rect_;
@@ -503,9 +510,6 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
// by FindQuadWithOverlayDamage().
bool current_zero_damage_rect_is_not_recorded_ = false;
- // Persistent storage for ResolvedFrameData.
- std::map<Surface*, ResolvedFrameData> resolved_frames_;
-
// Used to generate new unique render pass ids in the aggregated namespace.
AggregatedRenderPassId::Generator render_pass_id_generator_;
};
diff --git a/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc b/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc
index 07e18febf5c..aa9f8cdac09 100644
--- a/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc
+++ b/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc
@@ -339,10 +339,6 @@ TEST_P(SurfaceAggregatorPixelTest, DrawAggregatedFrameWithSurfaceTransforms) {
// Draw a simple frame with a delegated ink trail on top of it, then confirm
// that it is erased by the next aggregation.
TEST_P(SurfaceAggregatorPixelTest, DrawAndEraseDelegatedInkTrail) {
- // DelegatedInkTrail isn't supported on non-Skia renderers.
- if (renderer_type() == RendererType::kGL)
- return;
-
DelegatedInkPointPixelTestHelper delegated_ink_helper(renderer_.get());
// Create and send metadata and points to the renderer that will be drawn.
diff --git a/chromium/components/viz/service/display/surface_aggregator_unittest.cc b/chromium/components/viz/service/display/surface_aggregator_unittest.cc
index 4e454627318..481853d3eda 100644
--- a/chromium/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/chromium/components/viz/service/display/surface_aggregator_unittest.cc
@@ -341,7 +341,8 @@ class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource {
const auto* solid_color_quad = SolidColorDrawQuad::MaterialCast(quad);
- EXPECT_EQ(expected_quad.color, solid_color_quad->color);
+ EXPECT_EQ(SkColor4f::FromColor(expected_quad.color),
+ solid_color_quad->color);
EXPECT_EQ(expected_quad.rect, solid_color_quad->rect);
break;
}
@@ -6000,13 +6001,13 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ColorSpaceTestWin) {
gfx::BufferFormat::RGBA_8888);
display_color_spaces.SetOutputColorSpaceAndBufferFormat(
gfx::ContentColorUsage::kWideColorGamut, true /* needs_alpha */,
- gfx::ColorSpace::CreateSCRGBLinear(), gfx::BufferFormat::RGBA_8888);
+ gfx::ColorSpace::CreateSRGBLinear(), gfx::BufferFormat::RGBA_8888);
display_color_spaces.SetOutputColorSpaceAndBufferFormat(
gfx::ContentColorUsage::kHDR, false /* needs_alpha */,
gfx::ColorSpace::CreateHDR10(), gfx::BufferFormat::BGRA_1010102);
display_color_spaces.SetOutputColorSpaceAndBufferFormat(
gfx::ContentColorUsage::kHDR, true /* needs_alpha */,
- gfx::ColorSpace::CreateSCRGBLinear(), gfx::BufferFormat::RGBA_F16);
+ gfx::ColorSpace::CreateSRGBLinear(), gfx::BufferFormat::RGBA_F16);
std::vector<Pass> passes = {
Pass(quads[0], CompositorRenderPassId{2}, kSurfaceSize),
@@ -6085,7 +6086,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ColorSpaceTestWin) {
gfx::BufferFormat::BGRA_1010102);
display_color_spaces.SetOutputColorSpaceAndBufferFormat(
gfx::ContentColorUsage::kHDR, true /* needs_alpha */,
- gfx::ColorSpace::CreateSCRGBLinear(), gfx::BufferFormat::RGBA_F16);
+ gfx::ColorSpace::CreateSRGBLinear(), gfx::BufferFormat::RGBA_F16);
// Opaque content renders to the appropriate space directly.
passes[1].has_transparent_background = false;
diff --git a/chromium/components/viz/service/display/sync_query_collection.cc b/chromium/components/viz/service/display/sync_query_collection.cc
deleted file mode 100644
index 51a85c415da..00000000000
--- a/chromium/components/viz/service/display/sync_query_collection.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/service/display/sync_query_collection.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "cc/base/container_util.h"
-#include "components/viz/service/display/resource_fence.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-
-namespace viz {
-namespace {
-// Block or crash if the number of pending sync queries reach this high as
-// something is seriously wrong on the service side if this happens.
-const size_t kMaxPendingSyncQueries = 16;
-} // anonymous namespace
-
-class SyncQuery {
- public:
- explicit SyncQuery(gpu::gles2::GLES2Interface* gl)
- : gl_(gl), query_id_(0u), is_pending_(false) {
- gl_->GenQueriesEXT(1, &query_id_);
- }
-
- SyncQuery(const SyncQuery&) = delete;
- SyncQuery& operator=(const SyncQuery&) = delete;
-
- virtual ~SyncQuery() { gl_->DeleteQueriesEXT(1, &query_id_); }
-
- scoped_refptr<ResourceFence> Begin() {
- DCHECK(!IsPending());
- // Invalidate weak pointer held by old fence.
- weak_ptr_factory_.InvalidateWeakPtrs();
- // Note: In case the set of drawing commands issued before End() do not
- // depend on the query, defer BeginQueryEXT call until Set() is called and
- // query is required.
- return base::MakeRefCounted<Fence>(weak_ptr_factory_.GetWeakPtr());
- }
-
- void Set() {
- if (is_pending_)
- return;
-
- // Note: BeginQueryEXT on GL_COMMANDS_COMPLETED_CHROMIUM is effectively a
- // noop relative to GL, so it doesn't matter where it happens but we still
- // make sure to issue this command when Set() is called (prior to issuing
- // any drawing commands that depend on query), in case some future extension
- // can take advantage of this.
- gl_->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, query_id_);
- is_pending_ = true;
- }
-
- void End() {
- if (!is_pending_)
- return;
-
- gl_->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
- }
-
- bool IsPending() {
- if (!is_pending_)
- return false;
-
- unsigned result_available = 1;
- gl_->GetQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_AVAILABLE_EXT,
- &result_available);
- is_pending_ = !result_available;
- return is_pending_;
- }
-
- void Wait() {
- if (!is_pending_)
- return;
-
- unsigned result = 0;
- gl_->GetQueryObjectuivEXT(query_id_, GL_QUERY_RESULT_EXT, &result);
- is_pending_ = false;
- }
-
- private:
- class Fence : public ResourceFence {
- public:
- explicit Fence(base::WeakPtr<SyncQuery> query) : query_(query) {}
-
- Fence(const Fence&) = delete;
- Fence& operator=(const Fence&) = delete;
-
- // ResourceFence implementation.
- void Set() override {
- DCHECK(query_);
- query_->Set();
- }
- bool HasPassed() override { return !query_ || !query_->IsPending(); }
-
- private:
- ~Fence() override {}
-
- base::WeakPtr<SyncQuery> query_;
- };
-
- raw_ptr<gpu::gles2::GLES2Interface> gl_;
- unsigned query_id_;
- bool is_pending_;
- base::WeakPtrFactory<SyncQuery> weak_ptr_factory_{this};
-};
-
-SyncQueryCollection::SyncQueryCollection(gpu::gles2::GLES2Interface* gl)
- : gl_(gl) {}
-
-SyncQueryCollection::~SyncQueryCollection() = default;
-SyncQueryCollection::SyncQueryCollection(SyncQueryCollection&&) = default;
-SyncQueryCollection& SyncQueryCollection::operator=(SyncQueryCollection&&) =
- default;
-
-scoped_refptr<ResourceFence> SyncQueryCollection::StartNewFrame() {
- // Block until oldest sync query has passed if the number of pending queries
- // ever reach kMaxPendingSyncQueries.
- if (pending_sync_queries_.size() >= kMaxPendingSyncQueries) {
- LOG(ERROR) << "Reached limit of pending sync queries.";
-
- pending_sync_queries_.front()->Wait();
- DCHECK(!pending_sync_queries_.front()->IsPending());
- }
-
- while (!pending_sync_queries_.empty()) {
- if (pending_sync_queries_.front()->IsPending())
- break;
-
- available_sync_queries_.push_back(cc::PopFront(&pending_sync_queries_));
- }
-
- current_sync_query_ = available_sync_queries_.empty()
- ? std::make_unique<SyncQuery>(gl_)
- : cc::PopFront(&available_sync_queries_);
-
- return current_sync_query_->Begin();
-}
-
-void SyncQueryCollection::EndCurrentFrame() {
- DCHECK(current_sync_query_);
- current_sync_query_->End();
- pending_sync_queries_.push_back(std::move(current_sync_query_));
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/sync_query_collection.h b/chromium/components/viz/service/display/sync_query_collection.h
deleted file mode 100644
index fd09cbba6d4..00000000000
--- a/chromium/components/viz/service/display/sync_query_collection.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018 The Chromium 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 COMPONENTS_VIZ_SERVICE_DISPLAY_SYNC_QUERY_COLLECTION_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_SYNC_QUERY_COLLECTION_H_
-
-#include <memory>
-
-#include "base/containers/circular_deque.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/ref_counted.h"
-
-namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-}
-} // namespace gpu
-
-namespace viz {
-class SyncQuery;
-class ResourceFence;
-
-class SyncQueryCollection {
- public:
- explicit SyncQueryCollection(gpu::gles2::GLES2Interface* gl);
- SyncQueryCollection(SyncQueryCollection&&);
- SyncQueryCollection& operator=(SyncQueryCollection&&);
- ~SyncQueryCollection();
- scoped_refptr<ResourceFence> StartNewFrame();
- void EndCurrentFrame();
-
- private:
- base::circular_deque<std::unique_ptr<SyncQuery>> pending_sync_queries_;
- base::circular_deque<std::unique_ptr<SyncQuery>> available_sync_queries_;
- std::unique_ptr<SyncQuery> current_sync_query_;
- raw_ptr<gpu::gles2::GLES2Interface> gl_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SYNC_QUERY_COLLECTION_H_
diff --git a/chromium/components/viz/service/display/texture_deleter.cc b/chromium/components/viz/service/display/texture_deleter.cc
deleted file mode 100644
index 76e2cf0b0f8..00000000000
--- a/chromium/components/viz/service/display/texture_deleter.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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 "components/viz/service/display/texture_deleter.h"
-
-#include <stddef.h>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/memory/weak_ptr.h"
-#include "base/task/bind_post_task.h"
-#include "base/task/single_thread_task_runner.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "gpu/command_buffer/client/shared_image_interface.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/sync_token.h"
-
-namespace viz {
-
-static void DeleteTextureOnImplThread(
- const scoped_refptr<ContextProvider>& context_provider,
- const gpu::Mailbox& mailbox,
- const gpu::SyncToken& sync_token,
- bool is_lost) {
- context_provider->SharedImageInterface()->DestroySharedImage(sync_token,
- mailbox);
-}
-
-TextureDeleter::TextureDeleter(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner)
- : impl_task_runner_(std::move(task_runner)) {}
-
-TextureDeleter::~TextureDeleter() {
- for (auto& callback : impl_callbacks_)
- std::move(*callback).Run(gpu::SyncToken(), /*is_lost=*/true);
-}
-
-ReleaseCallback TextureDeleter::GetReleaseCallback(
- scoped_refptr<ContextProvider> context_provider,
- const gpu::Mailbox& mailbox) {
- // This callback owns the |context_provider|. It must be destroyed on the impl
- // thread. Upon destruction of this class, the callback must immediately be
- // destroyed.
- auto impl_callback = std::make_unique<ReleaseCallback>(base::BindOnce(
- &DeleteTextureOnImplThread, std::move(context_provider), mailbox));
-
- impl_callbacks_.push_back(std::move(impl_callback));
-
- // The raw pointer to the impl-side callback is valid as long as this
- // class is alive. So we guard it with a WeakPtr.
- ReleaseCallback run_impl_callback = base::BindOnce(
- &TextureDeleter::RunDeleteTextureOnImplThread,
- weak_ptr_factory_.GetWeakPtr(), impl_callbacks_.back().get());
-
- // Provide a callback for the main thread that posts back to the impl
- // thread.
- ReleaseCallback main_callback;
- if (impl_task_runner_) {
- main_callback =
- base::BindPostTask(impl_task_runner_, std::move(run_impl_callback));
- } else {
- main_callback = std::move(run_impl_callback);
- }
-
- return main_callback;
-}
-
-void TextureDeleter::RunDeleteTextureOnImplThread(
- ReleaseCallback* impl_callback,
- const gpu::SyncToken& sync_token,
- bool is_lost) {
- for (size_t i = 0; i < impl_callbacks_.size(); ++i) {
- if (impl_callbacks_[i].get() == impl_callback) {
- // Run the callback, then destroy it here on the impl thread.
- std::move(*impl_callbacks_[i]).Run(sync_token, is_lost);
- impl_callbacks_.erase(impl_callbacks_.begin() + i);
- return;
- }
- }
-
- NOTREACHED() << "The Callback returned by GetDeleteCallback() was called "
- << "more than once.";
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display/texture_deleter.h b/chromium/components/viz/service/display/texture_deleter.h
deleted file mode 100644
index eadcd3dbcc2..00000000000
--- a/chromium/components/viz/service/display/texture_deleter.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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 COMPONENTS_VIZ_SERVICE_DISPLAY_TEXTURE_DELETER_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_TEXTURE_DELETER_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "components/viz/common/resources/release_callback.h"
-#include "components/viz/service/viz_service_export.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace gpu {
-struct Mailbox;
-struct SyncToken;
-}
-
-namespace viz {
-class ContextProvider;
-
-class VIZ_SERVICE_EXPORT TextureDeleter {
- public:
- // task_runner corresponds with the thread the delete task should be posted
- // to. If null, the delete will happen on the calling thread.
- explicit TextureDeleter(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-
- TextureDeleter(const TextureDeleter&) = delete;
- TextureDeleter& operator=(const TextureDeleter&) = delete;
-
- ~TextureDeleter();
-
- // Returns a Callback that can be used as the ReleaseCallback for a
- // |texture_id|. The ReleaseCallback can be passed to other threads and will
- // destroy the texture, once it is run, on the impl thread. If the
- // TextureDeleter is destroyed due to the compositor shutting down, then the
- // ReleaseCallback will become a no-op and the texture will be deleted
- // immediately on the impl thread, along with dropping the reference to the
- // ContextProvider.
- ReleaseCallback GetReleaseCallback(
- scoped_refptr<ContextProvider> context_provider,
- const gpu::Mailbox& mailbox);
-
- private:
- // Runs the |impl_callback| to delete the texture and removes the callback
- // from the |impl_callbacks_| list.
- void RunDeleteTextureOnImplThread(ReleaseCallback* impl_callback,
- const gpu::SyncToken& sync_token,
- bool is_lost);
-
- scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner_;
- std::vector<std::unique_ptr<ReleaseCallback>> impl_callbacks_;
- base::WeakPtrFactory<TextureDeleter> weak_ptr_factory_{this};
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_TEXTURE_DELETER_H_
diff --git a/chromium/components/viz/service/display/texture_deleter_unittest.cc b/chromium/components/viz/service/display/texture_deleter_unittest.cc
deleted file mode 100644
index e1df0821e0c..00000000000
--- a/chromium/components/viz/service/display/texture_deleter_unittest.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// 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 "components/viz/service/display/texture_deleter.h"
-
-#include <utility>
-
-#include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/viz/common/resources/release_callback.h"
-#include "components/viz/test/test_context_provider.h"
-#include "gpu/command_buffer/client/shared_image_interface.h"
-#include "gpu/command_buffer/common/shared_image_usage.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/color_space.h"
-
-namespace viz {
-namespace {
-
-TEST(TextureDeleterTest, Destroy) {
- auto deleter =
- std::make_unique<TextureDeleter>(base::ThreadTaskRunnerHandle::Get());
-
- scoped_refptr<TestContextProvider> context_provider =
- TestContextProvider::Create();
- context_provider->BindToCurrentThread();
-
- auto* sii = context_provider->SharedImageInterface();
-
- gpu::Mailbox mailbox = sii->CreateSharedImage(
- ResourceFormat::RGBA_8888, gfx::Size(1, 1), gfx::ColorSpace(),
- kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
- gpu::SHARED_IMAGE_USAGE_GLES2, gpu::kNullSurfaceHandle);
-
- EXPECT_TRUE(context_provider->HasOneRef());
- EXPECT_EQ(1u, sii->shared_image_count());
-
- ReleaseCallback cb = deleter->GetReleaseCallback(context_provider, mailbox);
- EXPECT_FALSE(context_provider->HasOneRef());
- EXPECT_EQ(1u, sii->shared_image_count());
-
- // When the deleter is destroyed, it immediately drops its ref on the
- // ContextProvider, and deletes the shared image.
- deleter = nullptr;
- EXPECT_TRUE(context_provider->HasOneRef());
- EXPECT_EQ(0u, sii->shared_image_count());
-
- // Run the scoped release callback before destroying it, but it won't do
- // anything.
- std::move(cb).Run(gpu::SyncToken(), false);
-}
-
-TEST(TextureDeleterTest, NullTaskRunner) {
- auto deleter = std::make_unique<TextureDeleter>(nullptr);
-
- scoped_refptr<TestContextProvider> context_provider =
- TestContextProvider::Create();
- context_provider->BindToCurrentThread();
-
- auto* sii = context_provider->SharedImageInterface();
-
- gpu::Mailbox mailbox = sii->CreateSharedImage(
- ResourceFormat::RGBA_8888, gfx::Size(1, 1), gfx::ColorSpace(),
- kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
- gpu::SHARED_IMAGE_USAGE_GLES2, gpu::kNullSurfaceHandle);
-
- EXPECT_TRUE(context_provider->HasOneRef());
- EXPECT_EQ(1u, sii->shared_image_count());
-
- ReleaseCallback cb = deleter->GetReleaseCallback(context_provider, mailbox);
- EXPECT_FALSE(context_provider->HasOneRef());
- EXPECT_EQ(1u, sii->shared_image_count());
-
- std::move(cb).Run(gpu::SyncToken(), false);
-
- // With no task runner the callback will immediately drops its ref on the
- // ContextProvider and delete the shared image.
- EXPECT_TRUE(context_provider->HasOneRef());
- EXPECT_EQ(0u, sii->shared_image_count());
-}
-
-} // namespace
-} // namespace viz
diff --git a/chromium/components/viz/service/display/viz_pixel_test.cc b/chromium/components/viz/service/display/viz_pixel_test.cc
index c68d0e6448d..c3076fa3b23 100644
--- a/chromium/components/viz/service/display/viz_pixel_test.cc
+++ b/chromium/components/viz/service/display/viz_pixel_test.cc
@@ -30,9 +30,6 @@ void VizPixelTest::SetUp() {
case RendererType::kSoftware:
SetUpSoftwareRenderer();
break;
- case RendererType::kGL:
- SetUpGLRenderer(GetSurfaceOrigin());
- break;
case RendererType::kSkiaGL:
case RendererType::kSkiaVk:
case RendererType::kSkiaDawn:
diff --git a/chromium/components/viz/service/display/viz_pixel_test.h b/chromium/components/viz/service/display/viz_pixel_test.h
index ee041e7fe54..5a3ca05d8b6 100644
--- a/chromium/components/viz/service/display/viz_pixel_test.h
+++ b/chromium/components/viz/service/display/viz_pixel_test.h
@@ -33,8 +33,6 @@ class VizPixelTest : public cc::PixelTest {
switch (renderer_type_) {
case RendererType::kSoftware:
return "software";
- case RendererType::kGL:
- return "gl";
case RendererType::kSkiaGL:
case RendererType::kSkiaVk:
return "skia";
@@ -47,8 +45,6 @@ class VizPixelTest : public cc::PixelTest {
return renderer_type_ == RendererType::kSoftware;
}
- bool is_gl_renderer() const { return renderer_type_ == RendererType::kGL; }
-
protected:
static GraphicsBackend RenderTypeToBackend(RendererType renderer_type);
diff --git a/chromium/components/viz/service/display_embedder/DEPS b/chromium/components/viz/service/display_embedder/DEPS
index 4988c8b24d6..c8b25c28439 100644
--- a/chromium/components/viz/service/display_embedder/DEPS
+++ b/chromium/components/viz/service/display_embedder/DEPS
@@ -19,7 +19,9 @@ include_rules = [
"+components/viz/service/display/skia_output_surface.h",
"+components/viz/service/display/software_output_device.h",
"+components/viz/service/gl/gpu_service_impl.h",
- "+gpu/command_buffer/client",
+ "+gpu/command_buffer/client/gpu_memory_buffer_manager.h",
+ "+gpu/command_buffer/client/shared_image_interface.h",
+ "+gpu/command_buffer/client/shared_memory_limits.h",
"+gpu/command_buffer/common",
"+gpu/command_buffer/service",
"+gpu/config",
@@ -45,14 +47,12 @@ include_rules = [
"+ui/ozone/public",
# TODO(danakj): Double check the layering for these dependencies.
- "+components/viz/service/display/gl_renderer_copier.h",
"+components/viz/service/display/overlay_processor.h",
"+components/viz/service/display/overlay_processor_interface.h",
"+components/viz/service/display/overlay_strategy_fullscreen.h",
"+components/viz/service/display/overlay_strategy_single_on_top.h",
"+components/viz/service/display/overlay_strategy_underlay_cast.h",
"+components/viz/service/display/overlay_strategy_underlay.h",
- "+components/viz/service/display/texture_deleter.h",
]
specific_include_rules = {
diff --git a/chromium/components/viz/service/display_embedder/compositor_gpu_thread.cc b/chromium/components/viz/service/display_embedder/compositor_gpu_thread.cc
index 6e5914b1614..02db4e3c66c 100644
--- a/chromium/components/viz/service/display_embedder/compositor_gpu_thread.cc
+++ b/chromium/components/viz/service/display_embedder/compositor_gpu_thread.cc
@@ -47,7 +47,8 @@ std::unique_ptr<CompositorGpuThread> CompositorGpuThread::Create(
// currently always enables this extension, we are adding DCHECK() to ensure
// that instead of enabling/disabling DrDc based on the extension.
if (gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE)
- DCHECK(gl::GLSurfaceEGL::IsANGLEContextVirtualizationSupported());
+ DCHECK(gl::GLSurfaceEGL::GetGLDisplayEGL()
+ ->IsANGLEContextVirtualizationSupported());
#endif
scoped_refptr<VulkanContextProvider> vulkan_context_provider;
@@ -113,8 +114,12 @@ CompositorGpuThread::GetSharedContextState() {
const bool use_passthrough_decoder =
gpu::gles2::PassthroughCommandDecoderSupported() &&
gpu_preferences.use_passthrough_cmd_decoder;
+ gpu::ContextCreationAttribs attribs_helper;
+ attribs_helper.context_type = features::UseGles2ForOopR()
+ ? gpu::CONTEXT_TYPE_OPENGLES2
+ : gpu::CONTEXT_TYPE_OPENGLES3;
gl::GLContextAttribs attribs = gpu::gles2::GenerateGLContextAttribs(
- gpu::ContextCreationAttribs(), use_passthrough_decoder);
+ attribs_helper, use_passthrough_decoder);
attribs.angle_context_virtualization_group_number =
gl::AngleContextVirtualizationGroup::kDrDc;
@@ -186,6 +191,17 @@ bool CompositorGpuThread::Initialize() {
return init_succeded_;
}
+void CompositorGpuThread::HandleMemoryPressure(
+ base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
+ DCHECK(task_runner()->BelongsToCurrentThread());
+
+ // Context should be current for cache/memory cleanup.
+ if (shared_context_state_ &&
+ shared_context_state_->MakeCurrent(nullptr, /*needs_gl=*/true)) {
+ shared_context_state_->PurgeMemory(memory_pressure_level);
+ }
+}
+
void CompositorGpuThread::Init() {
const auto& gpu_preferences = gpu_channel_manager_->gpu_preferences();
if (enable_watchdog_) {
@@ -196,10 +212,20 @@ void CompositorGpuThread::Init() {
if (!watchdog_thread_)
return;
watchdog_thread_->OnInitComplete();
+
+ // Making sure to create the |memory_pressure_listener_| on
+ // CompositorGpuThread since this callback will be called on the thread it was
+ // created on.
+ memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
+ FROM_HERE, base::BindRepeating(&CompositorGpuThread::HandleMemoryPressure,
+ base::Unretained(this))),
init_succeded_ = true;
}
void CompositorGpuThread::CleanUp() {
+ // Destroying |memory_pressure_listener_| here to ensure its destroyed on the
+ // same thread on which it was created on.
+ memory_pressure_listener_.reset();
if (watchdog_thread_)
watchdog_thread_->OnGpuProcessTearDown();
diff --git a/chromium/components/viz/service/display_embedder/compositor_gpu_thread.h b/chromium/components/viz/service/display_embedder/compositor_gpu_thread.h
index 7ca40835ddf..f23c4e05233 100644
--- a/chromium/components/viz/service/display_embedder/compositor_gpu_thread.h
+++ b/chromium/components/viz/service/display_embedder/compositor_gpu_thread.h
@@ -7,6 +7,7 @@
#include <memory>
+#include "base/memory/memory_pressure_listener.h"
#include "base/memory/raw_ptr.h"
#include "base/threading/thread.h"
#include "components/viz/service/viz_service_export.h"
@@ -67,6 +68,9 @@ class VIZ_SERVICE_EXPORT CompositorGpuThread
bool Initialize();
+ void HandleMemoryPressure(
+ base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
+
raw_ptr<gpu::GpuChannelManager> gpu_channel_manager_;
const bool enable_watchdog_;
bool init_succeded_ = false;
@@ -80,6 +84,14 @@ class VIZ_SERVICE_EXPORT CompositorGpuThread
std::unique_ptr<gpu::GpuWatchdogThread> watchdog_thread_;
scoped_refptr<gpu::SharedContextState> shared_context_state_;
+ // To start listening memory pressure signals from the platform, we create a
+ // new instance of MemoryPressureListener, passing a callback to a
+ // function that takes a MemoryPressureLevel parameter.To stop listening,
+ // simply delete the listener object. The implementation guarantees
+ // that the callback will always be called on the thread that created
+ // the listener.
+ std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
+
base::WeakPtrFactory<CompositorGpuThread> weak_ptr_factory_;
};
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface.cc b/chromium/components/viz/service/display_embedder/gl_output_surface.cc
deleted file mode 100644
index 182bfc70aa9..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface.cc
+++ /dev/null
@@ -1,265 +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.
-
-#include "components/viz/service/display_embedder/gl_output_surface.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "cc/base/math_util.h"
-#include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/service/display/output_surface_client.h"
-#include "components/viz/service/display/output_surface_frame.h"
-#include "components/viz/service/display/renderer_utils.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/common/swap_buffers_complete_params.h"
-#include "gpu/command_buffer/common/swap_buffers_flags.h"
-#include "gpu/config/gpu_feature_info.h"
-#include "ui/gfx/buffer_format_util.h"
-#include "ui/gfx/overlay_transform_utils.h"
-
-namespace viz {
-
-GLOutputSurface::GLOutputSurface(
- scoped_refptr<VizProcessContextProvider> context_provider,
- gpu::SurfaceHandle surface_handle)
- : OutputSurface(context_provider),
- viz_context_provider_(context_provider),
- surface_handle_(surface_handle),
- use_gpu_fence_(
- context_provider->ContextCapabilities().chromium_gpu_fence &&
- context_provider->ContextCapabilities()
- .use_gpu_fences_for_overlay_planes) {
- const auto& context_capabilities = context_provider->ContextCapabilities();
- capabilities_.output_surface_origin = context_capabilities.surface_origin;
- capabilities_.supports_stencil = context_capabilities.num_stencil_bits > 0;
- // Since one of the buffers is used by the surface for presentation, there can
- // be at most |num_surface_buffers - 1| pending buffers that the compositor
- // can use.
- capabilities_.pending_swap_params.max_pending_swaps =
- context_capabilities.num_surface_buffers - 1;
- capabilities_.supports_gpu_vsync = context_capabilities.gpu_vsync;
- capabilities_.supports_dc_layers = context_capabilities.dc_layers;
- capabilities_.supports_surfaceless = context_capabilities.surfaceless;
- capabilities_.android_surface_control_feature_enabled =
- context_provider->GetGpuFeatureInfo()
- .status_values[gpu::GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] ==
- gpu::kGpuFeatureStatusEnabled;
- capabilities_.max_render_target_size = context_capabilities.max_texture_size;
-}
-
-GLOutputSurface::~GLOutputSurface() {
- viz_context_provider_->SetUpdateVSyncParametersCallback(
- UpdateVSyncParametersCallback());
- viz_context_provider_->SetGpuVSyncCallback(GpuVSyncCallback());
- if (gpu_fence_id_ > 0)
- context_provider()->ContextGL()->DestroyGpuFenceCHROMIUM(gpu_fence_id_);
-}
-
-void GLOutputSurface::BindToClient(OutputSurfaceClient* client) {
- DCHECK(client);
- DCHECK(!client_);
- client_ = client;
-}
-
-void GLOutputSurface::EnsureBackbuffer() {}
-
-void GLOutputSurface::DiscardBackbuffer() {
- context_provider()->ContextGL()->DiscardBackbufferCHROMIUM();
-}
-
-void GLOutputSurface::BindFramebuffer() {
- context_provider()->ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-void GLOutputSurface::SetDrawRectangle(const gfx::Rect& rect) {
- DCHECK(capabilities_.supports_dc_layers);
-
- if (set_draw_rectangle_for_frame_)
- return;
- DCHECK(gfx::Rect(size_).Contains(rect));
- DCHECK(has_set_draw_rectangle_since_last_resize_ ||
- (gfx::Rect(size_) == rect));
- set_draw_rectangle_for_frame_ = true;
- has_set_draw_rectangle_since_last_resize_ = true;
- context_provider()->ContextGL()->SetDrawRectangleCHROMIUM(
- rect.x(), rect.y(), rect.width(), rect.height());
-}
-
-void GLOutputSurface::SetEnableDCLayers(bool enable) {
- DCHECK(capabilities_.supports_dc_layers);
- context_provider()->ContextGL()->SetEnableDCLayersCHROMIUM(enable);
-}
-
-void GLOutputSurface::Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) {
- size_ = size;
- has_set_draw_rectangle_since_last_resize_ = false;
- set_draw_rectangle_for_frame_ = false;
- context_provider()->ContextGL()->ResizeCHROMIUM(
- size.width(), size.height(), device_scale_factor,
- color_space.AsGLColorSpace(), gfx::AlphaBitsForBufferFormat(format));
-}
-
-void GLOutputSurface::SwapBuffers(OutputSurfaceFrame frame) {
- DCHECK(context_provider_);
-
- uint32_t flags = 0;
- if (wants_vsync_parameter_updates_)
- flags |= gpu::SwapBuffersFlags::kVSyncParams;
-
- // The |swap_size| here should always be in the UI's logical screen space
- // since it is forwarded to the client code which is unaware of the display
- // transform optimization.
- gfx::Size swap_size = ApplyDisplayInverse(gfx::Rect(size_)).size();
- auto swap_callback = base::BindOnce(
- &GLOutputSurface::OnGpuSwapBuffersCompleted,
- weak_ptr_factory_.GetWeakPtr(), std::move(frame.latency_info),
- frame.top_controls_visible_height_changed, swap_size);
- gpu::ContextSupport::PresentationCallback presentation_callback;
- presentation_callback = base::BindOnce(&GLOutputSurface::OnPresentation,
- weak_ptr_factory_.GetWeakPtr());
-
- set_draw_rectangle_for_frame_ = false;
- if (frame.sub_buffer_rect) {
- HandlePartialSwap(*frame.sub_buffer_rect, flags, std::move(swap_callback),
- std::move(presentation_callback));
- } else if (!frame.content_bounds.empty()) {
- context_provider_->ContextSupport()->SwapWithBounds(
- frame.content_bounds, flags, std::move(swap_callback),
- std::move(presentation_callback));
- } else {
- context_provider_->ContextSupport()->Swap(flags, std::move(swap_callback),
- std::move(presentation_callback));
- }
-}
-
-uint32_t GLOutputSurface::GetFramebufferCopyTextureFormat() {
- auto* gl = static_cast<VizProcessContextProvider*>(context_provider());
- return gl->GetCopyTextureInternalFormat();
-}
-
-bool GLOutputSurface::IsDisplayedAsOverlayPlane() const {
- return false;
-}
-
-unsigned GLOutputSurface::GetOverlayTextureId() const {
- return 0;
-}
-
-bool GLOutputSurface::HasExternalStencilTest() const {
- return false;
-}
-
-void GLOutputSurface::ApplyExternalStencil() {}
-
-void GLOutputSurface::DidReceiveSwapBuffersAck(
- const gfx::SwapResponse& response,
- gfx::GpuFenceHandle release_fence) {
- client_->DidReceiveSwapBuffersAck(response.timings, std::move(release_fence));
-}
-
-void GLOutputSurface::HandlePartialSwap(
- const gfx::Rect& sub_buffer_rect,
- uint32_t flags,
- gpu::ContextSupport::SwapCompletedCallback swap_callback,
- gpu::ContextSupport::PresentationCallback presentation_callback) {
- context_provider_->ContextSupport()->PartialSwapBuffers(
- sub_buffer_rect, flags, std::move(swap_callback),
- std::move(presentation_callback));
-}
-
-void GLOutputSurface::OnGpuSwapBuffersCompleted(
- std::vector<ui::LatencyInfo> latency_info,
- bool top_controls_visible_height_changed,
- const gfx::Size& pixel_size,
- const gpu::SwapBuffersCompleteParams& params,
- gfx::GpuFenceHandle release_fence) {
- if (!params.texture_in_use_responses.empty())
- client_->DidReceiveTextureInUseResponses(params.texture_in_use_responses);
- if (!params.ca_layer_params.is_empty)
- client_->DidReceiveCALayerParams(params.ca_layer_params);
- DidReceiveSwapBuffersAck(params.swap_response, std::move(release_fence));
-
- UpdateLatencyInfoOnSwap(params.swap_response, &latency_info);
- latency_tracker_.OnGpuSwapBuffersCompleted(
- std::move(latency_info), top_controls_visible_height_changed);
-
- if (needs_swap_size_notifications_)
- client_->DidSwapWithSize(pixel_size);
-}
-
-void GLOutputSurface::OnPresentation(
- const gfx::PresentationFeedback& feedback) {
- client_->DidReceivePresentationFeedback(feedback);
-}
-
-unsigned GLOutputSurface::UpdateGpuFence() {
- if (!use_gpu_fence_)
- return 0;
-
- if (gpu_fence_id_ > 0)
- context_provider()->ContextGL()->DestroyGpuFenceCHROMIUM(gpu_fence_id_);
-
- gpu_fence_id_ = context_provider()->ContextGL()->CreateGpuFenceCHROMIUM();
-
- return gpu_fence_id_;
-}
-
-void GLOutputSurface::SetNeedsSwapSizeNotifications(
- bool needs_swap_size_notifications) {
- needs_swap_size_notifications_ = needs_swap_size_notifications;
-}
-
-void GLOutputSurface::SetUpdateVSyncParametersCallback(
- UpdateVSyncParametersCallback callback) {
- wants_vsync_parameter_updates_ = !callback.is_null();
- viz_context_provider_->SetUpdateVSyncParametersCallback(std::move(callback));
-}
-
-void GLOutputSurface::SetGpuVSyncCallback(GpuVSyncCallback callback) {
- DCHECK(capabilities_.supports_gpu_vsync);
- viz_context_provider_->SetGpuVSyncCallback(std::move(callback));
-}
-
-void GLOutputSurface::SetGpuVSyncEnabled(bool enabled) {
- DCHECK(capabilities_.supports_gpu_vsync);
- viz_context_provider_->SetGpuVSyncEnabled(enabled);
-}
-
-gfx::OverlayTransform GLOutputSurface::GetDisplayTransform() {
- return gfx::OVERLAY_TRANSFORM_NONE;
-}
-
-gfx::Rect GLOutputSurface::ApplyDisplayInverse(const gfx::Rect& input) {
- gfx::Transform display_inverse = gfx::OverlayTransformToTransform(
- gfx::InvertOverlayTransform(GetDisplayTransform()), gfx::SizeF(size_));
- return cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
- display_inverse, input);
-}
-
-base::ScopedClosureRunner GLOutputSurface::GetCacheBackBufferCb() {
- return viz_context_provider_->GetCacheBackBufferCb();
-}
-
-gpu::SurfaceHandle GLOutputSurface::GetSurfaceHandle() const {
- return surface_handle_;
-}
-
-void GLOutputSurface::SetFrameRate(float frame_rate) {
- viz_context_provider_->ContextSupport()->SetFrameRate(frame_rate);
-}
-
-void GLOutputSurface::SetNeedsMeasureNextDrawLatency() {
- viz_context_provider_->SetNeedsMeasureNextDrawLatency();
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface.h b/chromium/components/viz/service/display_embedder/gl_output_surface.h
deleted file mode 100644
index c611894a647..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface.h
+++ /dev/null
@@ -1,114 +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.
-
-#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/callback_helpers.h"
-#include "base/memory/raw_ptr.h"
-#include "components/viz/common/display/update_vsync_parameters_callback.h"
-#include "components/viz/service/display/output_surface.h"
-#include "components/viz/service/display_embedder/viz_process_context_provider.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "ui/latency/latency_tracker.h"
-
-namespace viz {
-
-// An OutputSurface implementation that directly draws and
-// swaps to an actual GL surface.
-class GLOutputSurface : public OutputSurface {
- public:
- GLOutputSurface(scoped_refptr<VizProcessContextProvider> context_provider,
- gpu::SurfaceHandle surface_handle);
- ~GLOutputSurface() override;
-
- // OutputSurface implementation
- void BindToClient(OutputSurfaceClient* client) override;
- void EnsureBackbuffer() override;
- void DiscardBackbuffer() override;
- void BindFramebuffer() override;
- void SetDrawRectangle(const gfx::Rect& draw_rectangle) override;
- void SetEnableDCLayers(bool enabled) override;
- void Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) override;
- void SwapBuffers(OutputSurfaceFrame frame) override;
- uint32_t GetFramebufferCopyTextureFormat() override;
- bool IsDisplayedAsOverlayPlane() const override;
- unsigned GetOverlayTextureId() const override;
- bool HasExternalStencilTest() const override;
- void ApplyExternalStencil() override;
- unsigned UpdateGpuFence() override;
- void SetNeedsSwapSizeNotifications(
- bool needs_swap_size_notifications) override;
- void SetUpdateVSyncParametersCallback(
- UpdateVSyncParametersCallback callback) override;
- void SetGpuVSyncCallback(GpuVSyncCallback callback) override;
- void SetGpuVSyncEnabled(bool enabled) override;
- void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
- gfx::OverlayTransform GetDisplayTransform() override;
- base::ScopedClosureRunner GetCacheBackBufferCb() override;
-
- gpu::SurfaceHandle GetSurfaceHandle() const override;
- void SetFrameRate(float frame_rate) override;
- void SetNeedsMeasureNextDrawLatency() override;
-
- protected:
- OutputSurfaceClient* client() const { return client_; }
- ui::LatencyTracker* latency_tracker() { return &latency_tracker_; }
- bool needs_swap_size_notifications() {
- return needs_swap_size_notifications_;
- }
-
- // Called when a swap completion is signaled from ImageTransportSurface.
- virtual void DidReceiveSwapBuffersAck(const gfx::SwapResponse& response,
- gfx::GpuFenceHandle release_fence);
-
- // Called in SwapBuffers() when a swap is determined to be partial. Subclasses
- // might override this method because different platforms handle partial swaps
- // differently.
- virtual void HandlePartialSwap(
- const gfx::Rect& sub_buffer_rect,
- uint32_t flags,
- gpu::ContextSupport::SwapCompletedCallback swap_callback,
- gpu::ContextSupport::PresentationCallback presentation_callback);
-
- private:
- // Called when a swap completion is signaled from ImageTransportSurface.
- void OnGpuSwapBuffersCompleted(std::vector<ui::LatencyInfo> latency_info,
- bool top_controls_visible_height_changed,
- const gfx::Size& pixel_size,
- const gpu::SwapBuffersCompleteParams& params,
- gfx::GpuFenceHandle release_fence);
- void OnPresentation(const gfx::PresentationFeedback& feedback);
- void OnGpuVSync(base::TimeTicks vsync_time, base::TimeDelta vsync_interval);
- gfx::Rect ApplyDisplayInverse(const gfx::Rect& input);
-
- scoped_refptr<VizProcessContextProvider> viz_context_provider_;
- raw_ptr<OutputSurfaceClient> client_ = nullptr;
- bool wants_vsync_parameter_updates_ = false;
- ui::LatencyTracker latency_tracker_;
-
- const gpu::SurfaceHandle surface_handle_;
-
- bool set_draw_rectangle_for_frame_ = false;
- // True if the draw rectangle has been set at all since the last resize.
- bool has_set_draw_rectangle_since_last_resize_ = false;
- gfx::Size size_;
- bool use_gpu_fence_;
- unsigned gpu_fence_id_ = 0;
- // Whether to send OutputSurfaceClient::DidSwapWithSize notifications.
- bool needs_swap_size_notifications_ = false;
-
- base::WeakPtrFactory<GLOutputSurface> weak_ptr_factory_{this};
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_H_
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_android.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_android.cc
deleted file mode 100644
index 741facb6742..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface_android.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/service/display_embedder/gl_output_surface_android.h"
-
-namespace viz {
-
-GLOutputSurfaceAndroid::GLOutputSurfaceAndroid(
- scoped_refptr<VizProcessContextProvider> context_provider,
- gpu::SurfaceHandle surface_handle)
- : GLOutputSurface(context_provider, surface_handle) {}
-
-GLOutputSurfaceAndroid::~GLOutputSurfaceAndroid() = default;
-
-void GLOutputSurfaceAndroid::HandlePartialSwap(
- const gfx::Rect& sub_buffer_rect,
- uint32_t flags,
- gpu::ContextSupport::SwapCompletedCallback swap_callback,
- gpu::ContextSupport::PresentationCallback presentation_callback) {
- DCHECK(sub_buffer_rect.IsEmpty());
- context_provider_->ContextSupport()->CommitOverlayPlanes(
- flags, std::move(swap_callback), std::move(presentation_callback));
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_android.h b/chromium/components/viz/service/display_embedder/gl_output_surface_android.h
deleted file mode 100644
index 8ad4a4326a9..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface_android.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The Chromium 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 COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_ANDROID_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_ANDROID_H_
-
-#include "components/viz/service/display_embedder/gl_output_surface.h"
-
-namespace viz {
-class GLOutputSurfaceAndroid : public GLOutputSurface {
- public:
- GLOutputSurfaceAndroid(
- scoped_refptr<VizProcessContextProvider> context_provider,
- gpu::SurfaceHandle surface_handle);
-
- GLOutputSurfaceAndroid(const GLOutputSurfaceAndroid&) = delete;
- GLOutputSurfaceAndroid& operator=(const GLOutputSurfaceAndroid&) = delete;
-
- ~GLOutputSurfaceAndroid() override;
-
- // GLOutputSurface implementation:
- void HandlePartialSwap(
- const gfx::Rect& sub_buffer_rect,
- uint32_t flags,
- gpu::ContextSupport::SwapCompletedCallback swap_callback,
- gpu::ContextSupport::PresentationCallback presentation_callback) override;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_ANDROID_H_
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
deleted file mode 100644
index 6641808ac3e..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
+++ /dev/null
@@ -1,314 +0,0 @@
-// 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.
-
-#include "components/viz/service/display_embedder/gl_output_surface_buffer_queue.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/common/switches.h"
-#include "components/viz/service/display/output_surface_client.h"
-#include "components/viz/service/display/output_surface_frame.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
-#include "gpu/command_buffer/common/sync_token.h"
-#include "ui/gl/buffer_format_utils.h"
-#include "ui/gl/gl_enums.h"
-#include "ui/gl/gl_fence.h"
-
-namespace viz {
-
-GLOutputSurfaceBufferQueue::GLOutputSurfaceBufferQueue(
- scoped_refptr<VizProcessContextProvider> context_provider,
- gpu::SurfaceHandle surface_handle,
- std::unique_ptr<BufferQueue> buffer_queue)
- : GLOutputSurface(context_provider, surface_handle),
- buffer_queue_(std::move(buffer_queue)) {
- capabilities_.only_invalidates_damage_rect = false;
- capabilities_.uses_default_gl_framebuffer = false;
- capabilities_.output_surface_origin = gfx::SurfaceOrigin::kTopLeft;
- // Set |max_pending_swaps| to 2 for buffer_queue, which aligns scheduling
- // more closely with the previous surfaced behavior.
- // With a surface, swap buffer ack used to return early, before actually
- // presenting the back buffer, enabling the browser compositor to run ahead.
- // BufferQueue implementation acks at the time of actual buffer swap, which
- // shifts the start of the new frame forward relative to the old
- // implementation.
- capabilities_.pending_swap_params.max_pending_swaps = 2;
- // GetCurrentFramebufferDamage will return an upper bound of the part of the
- // buffer that needs to be recomposited.
-#if BUILDFLAG(IS_APPLE)
- capabilities_.supports_target_damage = false;
-#else
- capabilities_.supports_target_damage = true;
-#endif
- // Force the number of max pending frames to one when the switch
- // "double-buffer-compositing" is passed.
- // This will keep compositing in double buffered mode assuming |buffer_queue_|
- // allocates at most one additional buffer.
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kDoubleBufferCompositing)) {
- capabilities_.pending_swap_params.max_pending_swaps = 1;
- buffer_queue_->SetMaxBuffers(2);
- }
-
- // It is safe to pass a raw pointer to *this because |buffer_queue_| is fully
- // owned and it doesn't use the SyncTokenProvider after it's destroyed.
- DCHECK(buffer_queue_);
- buffer_queue_->SetSyncTokenProvider(this);
- context_provider_->ContextGL()->GenFramebuffers(1, &fbo_);
-}
-
-GLOutputSurfaceBufferQueue::~GLOutputSurfaceBufferQueue() {
- auto* gl = context_provider_->ContextGL();
- DCHECK_NE(0u, fbo_);
- gl->DeleteFramebuffers(1, &fbo_);
- if (stencil_buffer_)
- gl->DeleteRenderbuffers(1, &stencil_buffer_);
- for (const auto& buffer_texture : buffer_queue_textures_)
- gl->DeleteTextures(1u, &buffer_texture.second);
- buffer_queue_textures_.clear();
- current_texture_ = 0u;
- last_bound_texture_ = 0u;
- last_bound_mailbox_.SetZero();
-
- // Freeing the BufferQueue here ensures that *this is fully alive in case the
- // BufferQueue needs the SyncTokenProvider functionality.
- buffer_queue_.reset();
- fbo_ = 0u;
- stencil_buffer_ = 0u;
-}
-
-void GLOutputSurfaceBufferQueue::BindFramebuffer() {
- auto* gl = context_provider_->ContextGL();
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
-
- // If we have a |current_texture_|, it means we haven't swapped the buffer, so
- // we're just wanting to rebind the GL framebuffer.
- if (current_texture_)
- return;
-
- DCHECK(buffer_queue_);
- gpu::SyncToken creation_sync_token;
- gfx::GpuFenceHandle release_fence;
- const gpu::Mailbox current_buffer =
- buffer_queue_->GetCurrentBuffer(&creation_sync_token, &release_fence);
- if (current_buffer.IsZero())
- return;
- gl->WaitSyncTokenCHROMIUM(creation_sync_token.GetConstData());
- if (!release_fence.is_null()) {
- auto fence = gfx::GpuFence(std::move(release_fence));
- if (gl::GLFence::IsGpuFenceSupported()) {
- auto id = gl->CreateClientGpuFenceCHROMIUM(fence.AsClientGpuFence());
- gl->WaitGpuFenceCHROMIUM(id);
- gl->DestroyGpuFenceCHROMIUM(id);
- } else {
- fence.Wait();
- }
- }
- unsigned& buffer_texture = buffer_queue_textures_[current_buffer];
- if (!buffer_texture) {
- buffer_texture =
- gl->CreateAndTexStorage2DSharedImageCHROMIUM(current_buffer.name);
- }
- current_texture_ = buffer_texture;
- gl->BeginSharedImageAccessDirectCHROMIUM(
- current_texture_, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- texture_target_, current_texture_, 0);
- last_bound_texture_ = current_texture_;
- last_bound_mailbox_ = current_buffer;
-
-#if DCHECK_IS_ON() && BUILDFLAG(IS_CHROMEOS_ASH)
- const GLenum result = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
- if (result != GL_FRAMEBUFFER_COMPLETE)
- DLOG(ERROR) << " Incomplete fb: " << gl::GLEnums::GetStringError(result);
-#endif
-
- // Reshape() must be called to go from using a stencil buffer to not using it.
- DCHECK(use_stencil_ || !stencil_buffer_);
- if (use_stencil_ && !stencil_buffer_) {
- gl->GenRenderbuffers(1, &stencil_buffer_);
- CHECK_NE(stencil_buffer_, 0u);
- gl->BindRenderbuffer(GL_RENDERBUFFER, stencil_buffer_);
- gl->RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
- reshape_size_.width(), reshape_size_.height());
- gl->BindRenderbuffer(GL_RENDERBUFFER, 0);
- gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, stencil_buffer_);
- }
-}
-
-// We call this on every frame that a value changes, but changing the size once
-// we've allocated backing NativePixmapBufferQueue instances will cause a DCHECK
-// because Chrome never Reshape(s) after the first one from (0,0). NB: this
-// implies that screen size changes need to be plumbed differently. In
-// particular, we must create the native window in the size that the hardware
-// reports.
-void GLOutputSurfaceBufferQueue::Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) {
- reshape_size_ = size;
- use_stencil_ = use_stencil;
- GLOutputSurface::Reshape(size, device_scale_factor, color_space, format,
- use_stencil);
- DCHECK(buffer_queue_);
- const bool may_have_freed_buffers =
- buffer_queue_->Reshape(size, color_space, format);
- if (may_have_freed_buffers || (stencil_buffer_ && !use_stencil)) {
- auto* gl = context_provider_->ContextGL();
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
- if (stencil_buffer_) {
- gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, 0);
- gl->DeleteRenderbuffers(1, &stencil_buffer_);
- stencil_buffer_ = 0u;
- }
-
- // Note that |texture_target_| is initially set to 0, and so if it has not
- // been set to a valid value, then no buffers have been allocated.
- if (texture_target_ && may_have_freed_buffers) {
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- texture_target_, 0, 0);
- for (const auto& buffer_texture : buffer_queue_textures_)
- gl->DeleteTextures(1u, &buffer_texture.second);
- buffer_queue_textures_.clear();
- current_texture_ = 0u;
- last_bound_texture_ = 0u;
- last_bound_mailbox_.SetZero();
- }
- }
-
- texture_target_ =
- gpu::GetBufferTextureTarget(gfx::BufferUsage::SCANOUT, format,
- context_provider_->ContextCapabilities());
-}
-
-void GLOutputSurfaceBufferQueue::SwapBuffers(OutputSurfaceFrame frame) {
- DCHECK(buffer_queue_);
-
- // TODO(rjkroege): What if swap happens again before DidReceiveSwapBuffersAck
- // then it would see the wrong size?
- DCHECK(reshape_size_ == frame.size);
- swap_size_ = reshape_size_;
-
- gfx::Rect damage_rect =
- frame.sub_buffer_rect ? *frame.sub_buffer_rect : gfx::Rect(swap_size_);
-
- // If the client is currently drawing, we first end access to the
- // corresponding shared image. Then, we can swap the buffers. That way, we
- // know that whatever GL commands GLOutputSurface::SwapBuffers() emits can
- // access the shared image.
- auto* gl = context_provider_->ContextGL();
- if (current_texture_) {
- gl->EndSharedImageAccessDirectCHROMIUM(current_texture_);
- gl->BindFramebuffer(GL_FRAMEBUFFER, 0u);
- current_texture_ = 0u;
- }
- buffer_queue_->SwapBuffers(damage_rect);
- GLOutputSurface::SwapBuffers(std::move(frame));
-}
-
-gfx::Rect GLOutputSurfaceBufferQueue::GetCurrentFramebufferDamage() const {
- return buffer_queue_->CurrentBufferDamage();
-}
-
-uint32_t GLOutputSurfaceBufferQueue::GetFramebufferCopyTextureFormat() {
- return base::strict_cast<GLenum>(
- gl::BufferFormatToGLInternalFormat(buffer_queue_->buffer_format()));
-}
-
-bool GLOutputSurfaceBufferQueue::IsDisplayedAsOverlayPlane() const {
- return true;
-}
-
-unsigned GLOutputSurfaceBufferQueue::GetOverlayTextureId() const {
- DCHECK(last_bound_texture_);
- return last_bound_texture_;
-}
-
-gpu::Mailbox GLOutputSurfaceBufferQueue::GetOverlayMailbox() const {
- return last_bound_mailbox_;
-}
-
-void GLOutputSurfaceBufferQueue::DidReceiveSwapBuffersAck(
- const gfx::SwapResponse& response,
- gfx::GpuFenceHandle release_fence) {
- bool force_swap = false;
- if (response.result == gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS) {
- // Even through the swap failed, this is a fixable error so we can pretend
- // it succeeded to the rest of the system.
- buffer_queue_->FreeAllSurfaces();
-
- // TODO(andrescj): centralize the logic that deletes the stencil buffer and
- // the textures since we do this in multiple places.
- auto* gl = context_provider_->ContextGL();
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
- if (stencil_buffer_) {
- gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, 0);
- gl->DeleteRenderbuffers(1, &stencil_buffer_);
- stencil_buffer_ = 0u;
- }
-
- // Reshape() must have been called before we got here, so |texture_target_|
- // should contain a valid value.
- DCHECK(texture_target_);
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- texture_target_, 0, 0);
- for (const auto& buffer_texture : buffer_queue_textures_)
- gl->DeleteTextures(1u, &buffer_texture.second);
- buffer_queue_textures_.clear();
- current_texture_ = 0u;
- last_bound_texture_ = 0u;
- last_bound_mailbox_.SetZero();
-
- force_swap = true;
- }
-
- buffer_queue_->PageFlipComplete(release_fence.Clone());
- client()->DidReceiveSwapBuffersAck(response.timings,
- std::move(release_fence));
-
- if (force_swap)
- client()->SetNeedsRedrawRect(gfx::Rect(swap_size_));
-}
-
-gpu::SyncToken GLOutputSurfaceBufferQueue::GenSyncToken() {
- // This should only be called as long as the BufferQueue is alive. We cannot
- // use |buffer_queue_| to detect this because in the dtor, |buffer_queue_|
- // becomes nullptr before BufferQueue's dtor is called, so GenSyncToken()
- // would be called after |buffer_queue_| is nullptr when in fact, the
- // BufferQueue is still alive. Hence, we use |fbo_| to detect that the
- // BufferQueue is still alive.
- DCHECK(fbo_);
- gpu::SyncToken sync_token;
- context_provider_->ContextGL()->GenUnverifiedSyncTokenCHROMIUM(
- sync_token.GetData());
- return sync_token;
-}
-
-void GLOutputSurfaceBufferQueue::SetDisplayTransformHint(
- gfx::OverlayTransform transform) {
- display_transform_ = transform;
-
- if (context_provider_)
- context_provider_->ContextSupport()->SetDisplayTransform(transform);
-}
-
-gfx::OverlayTransform GLOutputSurfaceBufferQueue::GetDisplayTransform() {
- return display_transform_;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h
deleted file mode 100644
index 73bcccc0e45..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/containers/flat_map.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/weak_ptr.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/service/display/output_surface.h"
-#include "components/viz/service/display_embedder/buffer_queue.h"
-#include "components/viz/service/display_embedder/gl_output_surface.h"
-#include "components/viz/service/display_embedder/viz_process_context_provider.h"
-#include "components/viz/service/viz_service_export.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/swap_result.h"
-#include "ui/gl/gl_surface.h"
-
-namespace viz {
-
-// An OutputSurface implementation that directly draws and swap to a GL
-// "buffer_queue" surface (aka one backed by a buffer managed explicitly).
-class VIZ_SERVICE_EXPORT GLOutputSurfaceBufferQueue
- : public GLOutputSurface,
- public BufferQueue::SyncTokenProvider {
- public:
- GLOutputSurfaceBufferQueue(
- scoped_refptr<VizProcessContextProvider> context_provider,
- gpu::SurfaceHandle surface_handle,
- std::unique_ptr<BufferQueue> buffer_queue);
-
- GLOutputSurfaceBufferQueue(const GLOutputSurfaceBufferQueue&) = delete;
- GLOutputSurfaceBufferQueue& operator=(const GLOutputSurfaceBufferQueue&) =
- delete;
-
- ~GLOutputSurfaceBufferQueue() override;
-
- // BufferQueue::SyncTokenProvider implementation.
- gpu::SyncToken GenSyncToken() override;
-
- protected:
- // OutputSurface implementation.
- void SetDisplayTransformHint(gfx::OverlayTransform transform) override;
- gfx::OverlayTransform GetDisplayTransform() override;
- void Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) override;
-
- private:
- FRIEND_TEST_ALL_PREFIXES(GLOutputSurfaceBufferQueueTest, HandleSwapNAK);
-
- // OutputSurface implementation.
- void BindFramebuffer() override;
- void SwapBuffers(OutputSurfaceFrame frame) override;
- gfx::Rect GetCurrentFramebufferDamage() const override;
- uint32_t GetFramebufferCopyTextureFormat() override;
- bool IsDisplayedAsOverlayPlane() const override;
- unsigned GetOverlayTextureId() const override;
- gpu::Mailbox GetOverlayMailbox() const override;
-
- // GLOutputSurface:
- void DidReceiveSwapBuffersAck(const gfx::SwapResponse& response,
- gfx::GpuFenceHandle release_fence) override;
-
- std::unique_ptr<BufferQueue> buffer_queue_;
-
- // |buffer_queue_textures_| caches the textures generated by consuming the
- // SharedImage mailboxes from the |buffer_queue_| so that we don't have to
- // generate a new texture every time a shared image is re-used.
- base::flat_map<gpu::Mailbox, unsigned> buffer_queue_textures_;
-
- // |current_texture_| is the texture currently being drawn to. It's one of
- // |buffer_queue_textures_| or 0 if the client is not currently drawing (i.e.,
- // we're not currently in between a BindFramebuffer()/SwapBuffers() pair).
- // |last_bound_texture_| is the texture that was last bound to |fbo_|. It's
- // also one of |buffer_queue_textures_| or 0 if no texture has been bound to
- // |fbo_| or all the buffers in the buffer queue have been freed.
- // |last_bound_mailbox_| is the mailbox corresponding to
- // |last_bound_texture_|.
- //
- // TODO(andrescj): use an RAII pattern to scope access to |current_texture_|
- // because it requires Begin/EndSharedImageAccessDirectCHROMIUM().
- unsigned current_texture_ = 0u;
- unsigned last_bound_texture_ = 0u;
- gpu::Mailbox last_bound_mailbox_;
- unsigned texture_target_ = 0u;
-
- unsigned fbo_ = 0u;
-
- bool use_stencil_ = false;
- unsigned stencil_buffer_ = 0u;
-
- gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE;
- gfx::Size reshape_size_;
- gfx::Size swap_size_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_H_
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_unittest.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_unittest.cc
deleted file mode 100644
index 3876eadb8fd..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface_buffer_queue_unittest.cc
+++ /dev/null
@@ -1,359 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/service/display_embedder/gl_output_surface_buffer_queue.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/memory/raw_ptr.h"
-#include "components/viz/service/display/output_surface_client.h"
-#include "components/viz/service/display/output_surface_frame.h"
-#include "components/viz/service/display_embedder/buffer_queue.h"
-#include "components/viz/test/test_context_provider.h"
-#include "components/viz/test/test_context_support.h"
-#include "components/viz/test/test_gles2_interface.h"
-#include "gpu/command_buffer/common/command_buffer_id.h"
-#include "gpu/command_buffer/common/constants.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/sync_token.h"
-#include "gpu/ipc/common/surface_handle.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/buffer_types.h"
-#include "ui/gfx/swap_result.h"
-
-using testing::_;
-using testing::DoAll;
-using testing::Eq;
-using testing::InSequence;
-using testing::Mock;
-using testing::Ne;
-using testing::NotNull;
-using testing::Pointee;
-using testing::Return;
-using testing::SetArgPointee;
-using testing::StrictMock;
-
-namespace viz {
-namespace {
-
-class TestVizProcessContextProvider : public VizProcessContextProvider {
- public:
- TestVizProcessContextProvider(std::unique_ptr<TestContextSupport> support,
- std::unique_ptr<TestGLES2Interface> gl)
- : support_(std::move(support)), context_gl_(std::move(gl)) {}
- TestVizProcessContextProvider(const TestVizProcessContextProvider&) = delete;
- TestVizProcessContextProvider& operator=(
- const TestVizProcessContextProvider&) = delete;
-
- // ContextProvider implementation.
- gpu::gles2::GLES2Interface* ContextGL() override { return context_gl_.get(); }
- gpu::ContextSupport* ContextSupport() override { return support_.get(); }
- const gpu::Capabilities& ContextCapabilities() const override {
- return gpu_capabilities_;
- }
-
- const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override {
- return gpu_feature_info_;
- }
-
- void SetUpdateVSyncParametersCallback(
- UpdateVSyncParametersCallback callback) override {}
- void SetGpuVSyncCallback(GpuVSyncCallback callback) override {}
- void SetGpuVSyncEnabled(bool enabled) override {}
- bool UseRGB565PixelFormat() const override { return false; }
- uint32_t GetCopyTextureInternalFormat() override { return 0u; }
- base::ScopedClosureRunner GetCacheBackBufferCb() override {
- return base::ScopedClosureRunner(base::DoNothing());
- }
-
- protected:
- ~TestVizProcessContextProvider() override = default;
-
- private:
- std::unique_ptr<TestContextSupport> support_;
- std::unique_ptr<TestGLES2Interface> context_gl_;
- gpu::Capabilities gpu_capabilities_;
- gpu::GpuFeatureInfo gpu_feature_info_;
-};
-
-class MockGLES2Interface : public TestGLES2Interface {
- public:
- MockGLES2Interface() = default;
- ~MockGLES2Interface() override = default;
-
- MOCK_METHOD2(DeleteTextures, void(GLsizei, const GLuint*));
- MOCK_METHOD2(BindFramebuffer, void(GLenum, GLuint));
- MOCK_METHOD2(GenRenderbuffers, void(GLsizei, GLuint*));
- MOCK_METHOD2(BindRenderbuffer, void(GLenum, GLuint));
- MOCK_METHOD2(DeleteRenderbuffers, void(GLsizei n, const GLuint*));
- MOCK_METHOD1(CreateAndTexStorage2DSharedImageCHROMIUM, GLuint(const GLbyte*));
- MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte*));
- MOCK_METHOD2(BeginSharedImageAccessDirectCHROMIUM, void(GLuint, GLenum));
- MOCK_METHOD1(EndSharedImageAccessDirectCHROMIUM, void(GLuint));
-};
-
-class MockBufferQueue : public BufferQueue {
- public:
- MockBufferQueue() : BufferQueue(/*sii_=*/nullptr, gpu::kNullSurfaceHandle) {}
- ~MockBufferQueue() override = default;
-
- MOCK_METHOD2(GetCurrentBuffer,
- gpu::Mailbox(gpu::SyncToken*, gfx::GpuFenceHandle*));
- MOCK_CONST_METHOD0(CurrentBufferDamage, gfx::Rect());
- MOCK_METHOD1(SwapBuffers, void(const gfx::Rect&));
- MOCK_METHOD1(PageFlipComplete, void(gfx::GpuFenceHandle));
- MOCK_METHOD0(FreeAllSurfaces, void());
- MOCK_METHOD3(Reshape,
- bool(const gfx::Size&,
- const gfx::ColorSpace&,
- gfx::BufferFormat));
-
- MOCK_METHOD0(DoSetSyncTokenProvider, void());
- void SetSyncTokenProvider(SyncTokenProvider* sync_token_provider) override {
- BufferQueue::SetSyncTokenProvider(sync_token_provider);
- DoSetSyncTokenProvider();
- }
-};
-
-} // namespace
-
-class GLOutputSurfaceBufferQueueTest : public ::testing::Test,
- public OutputSurfaceClient {
- public:
- GLOutputSurfaceBufferQueueTest() = default;
- ~GLOutputSurfaceBufferQueueTest() override = default;
-
- void SetUp() override {
- auto buffer_queue = std::make_unique<StrictMock<MockBufferQueue>>();
- buffer_queue_ = buffer_queue.get();
-
- auto gles2_interface = std::make_unique<StrictMock<MockGLES2Interface>>();
- gles2_interface_ = gles2_interface.get();
-
- EXPECT_CALL(*buffer_queue_, DoSetSyncTokenProvider());
- surface_ = std::make_unique<GLOutputSurfaceBufferQueue>(
- base::MakeRefCounted<TestVizProcessContextProvider>(
- std::make_unique<TestContextSupport>(), std::move(gles2_interface)),
- gpu::kNullSurfaceHandle, std::move(buffer_queue));
- surface_->BindToClient(this);
-
- Mock::VerifyAndClearExpectations(gles2_interface_);
- Mock::VerifyAndClearExpectations(buffer_queue_);
- }
-
- // OutputSurfaceClient implementation.
- void DidReceiveSwapBuffersAck(const gfx::SwapTimings& timings,
- gfx::GpuFenceHandle release_fence) override {}
- void SetNeedsRedrawRect(const gfx::Rect& damage_rect) override {}
- void DidReceiveTextureInUseResponses(
- const gpu::TextureInUseResponses& responses) override {}
- void DidReceiveCALayerParams(
- const gfx::CALayerParams& ca_layer_params) override {}
- void DidSwapWithSize(const gfx::Size& pixel_size) override {}
- void DidReceivePresentationFeedback(
- const gfx::PresentationFeedback& feedback) override {}
- void DidReceiveReleasedOverlays(
- const std::vector<gpu::Mailbox>& released_overlays) override {}
-
- protected:
- std::unique_ptr<OutputSurface> surface_;
- raw_ptr<StrictMock<MockGLES2Interface>> gles2_interface_;
- raw_ptr<StrictMock<MockBufferQueue>> buffer_queue_;
-};
-
-MATCHER_P(SyncTokenEqualTo, expected_sync_token, "") {
- auto* actual_sync_token = reinterpret_cast<const gpu::SyncToken*>(arg);
- return expected_sync_token == *actual_sync_token;
-}
-
-MATCHER_P(SharedImageEqualTo, expected_shared_image, "") {
- gpu::Mailbox actual_shared_image;
- actual_shared_image.SetName(arg);
- return expected_shared_image == actual_shared_image;
-}
-
-// Make sure that the surface uses the buffer queue and the GL context correctly
-// when we request it to bind the framebuffer twice and then swap the buffer.
-TEST_F(GLOutputSurfaceBufferQueueTest, BindFramebufferAndSwap) {
- const gpu::SyncToken fake_sync_token(
- gpu::CommandBufferNamespace::GPU_IO,
- gpu::CommandBufferId::FromUnsafeValue(567u),
- /*release_count=*/5u);
- const gpu::Mailbox fake_shared_image = gpu::Mailbox::GenerateForSharedImage();
- constexpr GLuint kFakeTexture = 123u;
- {
- InSequence dummy_sequence;
-
- // The first call to |surface_|->BindFramebuffer() should result in binding
- // the GL framebuffer, requesting a new buffer, waiting on the corresponding
- // sync token, and beginning read/write access to the shared image.
- EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u)));
- EXPECT_CALL(*buffer_queue_, GetCurrentBuffer(NotNull(), NotNull()))
- .WillOnce(DoAll(SetArgPointee<0>(fake_sync_token),
- Return(fake_shared_image)));
- EXPECT_CALL(*gles2_interface_,
- WaitSyncTokenCHROMIUM(SyncTokenEqualTo(fake_sync_token)));
- EXPECT_CALL(*gles2_interface_, CreateAndTexStorage2DSharedImageCHROMIUM(
- SharedImageEqualTo(fake_shared_image)))
- .WillOnce(Return(kFakeTexture));
- EXPECT_CALL(
- *gles2_interface_,
- BeginSharedImageAccessDirectCHROMIUM(
- kFakeTexture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM));
-
- // The second call to |surface_|->BindFramebuffer() should only result in
- // binding the GL framebuffer because the underlying buffer hasn't been
- // swapped.
- EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u)));
-
- // Calling |surface_|->SwapBuffers() should result in ending read/write
- // access to the underlying buffer and unbinding the GL framebuffer.
- EXPECT_CALL(*gles2_interface_,
- EndSharedImageAccessDirectCHROMIUM(kFakeTexture));
- EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Eq(0u)));
- EXPECT_CALL(*buffer_queue_, SwapBuffers(_));
-
- // Destroying |surface_| should result in the deletion of the texture
- // obtained from consuming the shared image.
- EXPECT_CALL(*gles2_interface_,
- DeleteTextures(1u, Pointee(Eq(kFakeTexture))));
- }
-
- surface_->BindFramebuffer();
- surface_->BindFramebuffer();
- surface_->SwapBuffers(OutputSurfaceFrame());
-}
-
-TEST_F(GLOutputSurfaceBufferQueueTest, EmptySwap) {
- const gpu::SyncToken fake_sync_token(
- gpu::CommandBufferNamespace::GPU_IO,
- gpu::CommandBufferId::FromUnsafeValue(567u),
- /*release_count=*/5u);
- const gpu::Mailbox fake_shared_image = gpu::Mailbox::GenerateForSharedImage();
- constexpr GLuint kFakeTexture = 123u;
- {
- InSequence dummy_sequence;
-
- // The call to |surface_|->BindFramebuffer() should result in binding the GL
- // framebuffer, requesting a new buffer, waiting on the corresponding sync
- // token, and beginning read/write access to the shared image.
- EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u)));
- EXPECT_CALL(*buffer_queue_, GetCurrentBuffer(NotNull(), NotNull()))
- .WillOnce(DoAll(SetArgPointee<0>(fake_sync_token),
- Return(fake_shared_image)));
- EXPECT_CALL(*gles2_interface_,
- WaitSyncTokenCHROMIUM(SyncTokenEqualTo(fake_sync_token)));
- EXPECT_CALL(*gles2_interface_, CreateAndTexStorage2DSharedImageCHROMIUM(
- SharedImageEqualTo(fake_shared_image)))
- .WillOnce(Return(kFakeTexture));
- EXPECT_CALL(
- *gles2_interface_,
- BeginSharedImageAccessDirectCHROMIUM(
- kFakeTexture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM));
-
- // The first call to |surface_|->SwapBuffers() should result in ending
- // read/write access to the underlying buffer and unbinding the GL
- // framebuffer.
- EXPECT_CALL(*gles2_interface_,
- EndSharedImageAccessDirectCHROMIUM(kFakeTexture));
- EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Eq(0u)));
- EXPECT_CALL(*buffer_queue_, SwapBuffers(_));
-
- // The two empty swaps should only result in telling the buffer queue to
- // swap the buffers.
- EXPECT_CALL(*buffer_queue_, SwapBuffers(_)).Times(2);
-
- // Destroying |surface_| should result in the deletion of the texture
- // obtained from consuming the shared image.
- EXPECT_CALL(*gles2_interface_,
- DeleteTextures(1u, Pointee(Eq(kFakeTexture))));
- }
- surface_->BindFramebuffer();
- unsigned texture_for_first_buffer = surface_->GetOverlayTextureId();
- EXPECT_GT(texture_for_first_buffer, 0u);
- surface_->SwapBuffers(OutputSurfaceFrame());
-
- // Now do two empty swaps (which don't call BindFramebuffer()).
- EXPECT_EQ(texture_for_first_buffer, surface_->GetOverlayTextureId());
- surface_->SwapBuffers(OutputSurfaceFrame());
- EXPECT_EQ(texture_for_first_buffer, surface_->GetOverlayTextureId());
- surface_->SwapBuffers(OutputSurfaceFrame());
-}
-
-// Make sure that receiving a swap NAK doesn't cause us to leak resources.
-TEST_F(GLOutputSurfaceBufferQueueTest, HandleSwapNAK) {
- const gpu::SyncToken fake_sync_token(
- gpu::CommandBufferNamespace::GPU_IO,
- gpu::CommandBufferId::FromUnsafeValue(567u),
- /*release_count=*/5u);
- constexpr gfx::Size kBufferSize(100, 100);
- const gpu::Mailbox fake_shared_image = gpu::Mailbox::GenerateForSharedImage();
- constexpr GLuint kFakeTexture = 123u;
- constexpr GLuint kFakeStencilBuffer = 456u;
- {
- InSequence dummy_sequence;
-
- EXPECT_CALL(*buffer_queue_, Reshape(_, _, _)).WillOnce(Return(true));
- EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u)));
-
- // The call to |surface_|->BindFramebuffer() should result in binding the GL
- // framebuffer, requesting a new buffer, waiting on the corresponding sync
- // token, beginning read/write access to the shared image, and creating a
- // stencil buffer.
- EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u)));
- EXPECT_CALL(*buffer_queue_, GetCurrentBuffer(NotNull(), NotNull()))
- .WillOnce(DoAll(SetArgPointee<0>(fake_sync_token),
- Return(fake_shared_image)));
-
- EXPECT_CALL(*gles2_interface_,
- WaitSyncTokenCHROMIUM(SyncTokenEqualTo(fake_sync_token)));
- EXPECT_CALL(*gles2_interface_, CreateAndTexStorage2DSharedImageCHROMIUM(
- SharedImageEqualTo(fake_shared_image)))
- .WillOnce(Return(kFakeTexture));
- EXPECT_CALL(
- *gles2_interface_,
- BeginSharedImageAccessDirectCHROMIUM(
- kFakeTexture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM));
- EXPECT_CALL(*gles2_interface_, GenRenderbuffers(1u, NotNull()))
- .WillOnce(SetArgPointee<1>(kFakeStencilBuffer));
- EXPECT_CALL(*gles2_interface_,
- BindRenderbuffer(GL_RENDERBUFFER, kFakeStencilBuffer));
- EXPECT_CALL(*gles2_interface_, BindRenderbuffer(GL_RENDERBUFFER, 0u));
-
- // Calling |surface_|->SwapBuffers() should result in ending read/write
- // access to the underlying buffer and unbinding the GL framebuffer.
- EXPECT_CALL(*gles2_interface_,
- EndSharedImageAccessDirectCHROMIUM(kFakeTexture));
- EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Eq(0u)));
- EXPECT_CALL(*buffer_queue_, SwapBuffers(_));
-
- // Receiving a swap NAK should result in the deletion of the texture
- // obtained from consuming the shared image. It should also result in the
- // deletion of the stencil buffer.
- EXPECT_CALL(*buffer_queue_, FreeAllSurfaces());
- EXPECT_CALL(*gles2_interface_, BindFramebuffer(_, Ne(0u)));
- EXPECT_CALL(*gles2_interface_,
- DeleteRenderbuffers(1u, Pointee(Eq(kFakeStencilBuffer))));
- EXPECT_CALL(*gles2_interface_,
- DeleteTextures(1u, Pointee(Eq(kFakeTexture))));
- EXPECT_CALL(*buffer_queue_, PageFlipComplete(_));
- }
-
- surface_->Reshape(kBufferSize, /*device_scale_factor=*/1.0,
- gfx::ColorSpace::CreateSRGB(), gfx::BufferFormat::BGRA_8888,
- /*use_stencil=*/true);
- surface_->BindFramebuffer();
- OutputSurfaceFrame frame;
- frame.size = kBufferSize;
- surface_->SwapBuffers(std::move(frame));
- gfx::SwapResponse swap_response{};
- swap_response.result = gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS;
- (static_cast<GLOutputSurfaceBufferQueue*>(surface_.get()))
- ->DidReceiveSwapBuffersAck(swap_response,
- /*release_fence=*/gfx::GpuFenceHandle());
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.cc
deleted file mode 100644
index d3cc8558658..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/service/display_embedder/gl_output_surface_chromeos.h"
-
-namespace viz {
-
-GLOutputSurfaceChromeOS::GLOutputSurfaceChromeOS(
- scoped_refptr<VizProcessContextProvider> context_provider,
- gpu::SurfaceHandle surface_handle)
- : GLOutputSurface(context_provider, surface_handle) {}
-
-GLOutputSurfaceChromeOS::~GLOutputSurfaceChromeOS() = default;
-
-void GLOutputSurfaceChromeOS::SetDisplayTransformHint(
- gfx::OverlayTransform transform) {
- display_transform_ = transform;
-}
-
-gfx::OverlayTransform GLOutputSurfaceChromeOS::GetDisplayTransform() {
- return display_transform_;
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.h b/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.h
deleted file mode 100644
index 63ad613a96d..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface_chromeos.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_CHROMEOS_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_CHROMEOS_H_
-
-#include "components/viz/service/display_embedder/gl_output_surface.h"
-
-namespace viz {
-
-class GLOutputSurfaceChromeOS : public GLOutputSurface {
- public:
- GLOutputSurfaceChromeOS(
- scoped_refptr<VizProcessContextProvider> context_provider,
- gpu::SurfaceHandle surface_handle);
-
- GLOutputSurfaceChromeOS(const GLOutputSurfaceChromeOS&) = delete;
- GLOutputSurfaceChromeOS& operator=(const GLOutputSurfaceChromeOS&) = delete;
-
- ~GLOutputSurfaceChromeOS() override;
-
- // GLOutputSurface:
- void SetDisplayTransformHint(gfx::OverlayTransform transform) override;
- gfx::OverlayTransform GetDisplayTransform() override;
-
- private:
- gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_CHROMEOS_H_
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.cc b/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.cc
deleted file mode 100644
index 9e7190ad7b0..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.cc
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2018 The Chromium 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/viz/service/display_embedder/gl_output_surface_offscreen.h"
-
-#include <stdint.h>
-
-#include <algorithm>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "components/viz/common/resources/resource_format_utils.h"
-#include "components/viz/service/display/output_surface_client.h"
-#include "components/viz/service/display/output_surface_frame.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/client/shared_image_interface.h"
-#include "gpu/command_buffer/common/shared_image_usage.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
-#include "ui/gfx/swap_result.h"
-#include "ui/gl/gl_utils.h"
-
-namespace viz {
-namespace {
-
-constexpr ResourceFormat kFboTextureFormat = RGBA_8888;
-
-} // namespace
-
-GLOutputSurfaceOffscreen::GLOutputSurfaceOffscreen(
- scoped_refptr<VizProcessContextProvider> context_provider)
- : GLOutputSurface(context_provider, gpu::kNullSurfaceHandle) {}
-
-GLOutputSurfaceOffscreen::~GLOutputSurfaceOffscreen() {
- DiscardBackbuffer();
-}
-
-void GLOutputSurfaceOffscreen::EnsureBackbuffer() {
- if (size_.IsEmpty())
- return;
-
- if (!texture_id_) {
- gpu::SharedImageInterface* sii = context_provider_->SharedImageInterface();
- gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
-
- const int max_texture_size =
- context_provider_->ContextCapabilities().max_texture_size;
- gfx::Size texture_size(std::min(size_.width(), max_texture_size),
- std::min(size_.height(), max_texture_size));
-
- const uint32_t flags = gpu::SHARED_IMAGE_USAGE_GLES2 |
- gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT |
- gpu::SHARED_IMAGE_USAGE_DISPLAY;
-
- mailbox_ = sii->CreateSharedImage(
- kFboTextureFormat, texture_size, color_space_, kTopLeft_GrSurfaceOrigin,
- kPremul_SkAlphaType, flags, gpu::kNullSurfaceHandle);
-
- // Ensure mailbox is valid before using it.
- gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
-
- texture_id_ = gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox_.name);
-
- gl->GenFramebuffers(1, &fbo_);
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, texture_id_, 0);
- }
-}
-
-void GLOutputSurfaceOffscreen::DiscardBackbuffer() {
- if (fbo_) {
- gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
- gl->DeleteFramebuffers(1, &fbo_);
- fbo_ = 0;
- }
-
- if (texture_id_) {
- gpu::SharedImageInterface* sii = context_provider_->SharedImageInterface();
- sii->DestroySharedImage(gpu::SyncToken(), mailbox_);
- mailbox_.SetZero();
- texture_id_ = 0;
- }
-}
-
-void GLOutputSurfaceOffscreen::BindFramebuffer() {
- if (!texture_id_) {
- EnsureBackbuffer();
- } else {
- gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
- }
-}
-
-void GLOutputSurfaceOffscreen::Reshape(const gfx::Size& size,
- float scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool stencil) {
- size_ = size;
- color_space_ = color_space;
- DiscardBackbuffer();
- EnsureBackbuffer();
-}
-
-void GLOutputSurfaceOffscreen::SwapBuffers(OutputSurfaceFrame frame) {
- DCHECK_EQ(frame.size, size_);
-
- gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
-
- gpu::SyncToken sync_token;
- gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
- context_provider_->ContextSupport()->SignalSyncToken(
- sync_token,
- base::BindOnce(&GLOutputSurfaceOffscreen::OnSwapBuffersComplete,
- weak_ptr_factory_.GetWeakPtr(),
- std::move(frame.latency_info)));
-}
-
-void GLOutputSurfaceOffscreen::OnSwapBuffersComplete(
- std::vector<ui::LatencyInfo> latency_info) {
- latency_tracker()->OnGpuSwapBuffersCompleted(std::move(latency_info));
- // Swap timings are not available since for offscreen there is no Swap, just a
- // SignalSyncToken. We use base::TimeTicks::Now() as an overestimate.
- auto now = base::TimeTicks::Now();
- client()->DidReceiveSwapBuffersAck({.swap_start = now},
- /*release_fence=*/gfx::GpuFenceHandle());
- client()->DidReceivePresentationFeedback(
- gfx::PresentationFeedback(now, base::Milliseconds(16), /*flags=*/0));
-
- if (needs_swap_size_notifications())
- client()->DidSwapWithSize(size_);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.h b/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.h
deleted file mode 100644
index e769f1254ed..00000000000
--- a/chromium/components/viz/service/display_embedder/gl_output_surface_offscreen.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018 The Chromium 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 COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_OFFSCREEN_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_OFFSCREEN_H_
-
-#include <memory>
-
-#include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/service/display_embedder/gl_output_surface.h"
-#include "components/viz/service/display_embedder/viz_process_context_provider.h"
-#include "components/viz/service/viz_service_export.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "ui/gfx/color_space.h"
-
-namespace viz {
-
-// An OutputSurface implementation that draws and swaps to an offscreen GL
-// framebuffer.
-class VIZ_SERVICE_EXPORT GLOutputSurfaceOffscreen : public GLOutputSurface {
- public:
- explicit GLOutputSurfaceOffscreen(
- scoped_refptr<VizProcessContextProvider> context_provider);
-
- GLOutputSurfaceOffscreen(const GLOutputSurfaceOffscreen&) = delete;
- GLOutputSurfaceOffscreen& operator=(const GLOutputSurfaceOffscreen&) = delete;
-
- ~GLOutputSurfaceOffscreen() override;
-
- // OutputSurface implementation.
- void EnsureBackbuffer() override;
- void DiscardBackbuffer() override;
- void BindFramebuffer() override;
- void Reshape(const gfx::Size& size,
- float scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool stencil) override;
- void SwapBuffers(OutputSurfaceFrame frame) override;
-
- private:
- void OnSwapBuffersComplete(std::vector<ui::LatencyInfo> latency_info);
-
- gpu::Mailbox mailbox_;
-
- uint32_t fbo_ = 0;
- uint32_t texture_id_ = 0;
- gfx::Size size_;
- gfx::ColorSpace color_space_;
-
- base::WeakPtrFactory<GLOutputSurfaceOffscreen> weak_ptr_factory_{this};
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_OFFSCREEN_H_
diff --git a/chromium/components/viz/service/display_embedder/image_context_impl.h b/chromium/components/viz/service/display_embedder/image_context_impl.h
index 00a51c95254..79e45adbc33 100644
--- a/chromium/components/viz/service/display_embedder/image_context_impl.h
+++ b/chromium/components/viz/service/display_embedder/image_context_impl.h
@@ -73,9 +73,9 @@ class ImageContextImpl final : public ExternalUseClient::ImageContext {
SkPromiseImageTexture* promise_image_texture() const {
return promise_image_texture_;
}
- GrBackendSurfaceMutableState* end_access_state() const {
+ std::unique_ptr<GrBackendSurfaceMutableState> TakeAccessEndState() const {
return representation_scoped_read_access_
- ? representation_scoped_read_access_->end_state()
+ ? representation_scoped_read_access_->TakeEndState()
: nullptr;
}
diff --git a/chromium/components/viz/service/display_embedder/output_presenter.cc b/chromium/components/viz/service/display_embedder/output_presenter.cc
index 0209a8bac21..0c9aced7778 100644
--- a/chromium/components/viz/service/display_embedder/output_presenter.cc
+++ b/chromium/components/viz/service/display_embedder/output_presenter.cc
@@ -84,12 +84,13 @@ void OutputPresenter::Image::EndWriteSkia(bool force_flush) {
// The Flush now takes place in finishPaintCurrentBuffer on the CPU side.
// check if end_semaphores is not empty then flush here
DCHECK(scoped_skia_write_access_);
- if (!end_semaphores_.empty() || force_flush) {
+ auto end_state = scoped_skia_write_access_->TakeEndState();
+ if (!end_semaphores_.empty() || end_state || force_flush) {
GrFlushInfo flush_info = {
.fNumSemaphores = end_semaphores_.size(),
.fSignalSemaphores = end_semaphores_.data(),
};
- scoped_skia_write_access_->surface()->flush(flush_info);
+ scoped_skia_write_access_->surface()->flush(flush_info, end_state.get());
auto* direct_context = scoped_skia_write_access_->surface()
->recordingContext()
->asDirectContext();
@@ -105,9 +106,8 @@ void OutputPresenter::Image::EndWriteSkia(bool force_flush) {
void OutputPresenter::Image::PreGrContextSubmit() {
DCHECK(scoped_skia_write_access_);
- if (scoped_skia_write_access_->end_state()) {
- scoped_skia_write_access_->surface()->flush(
- {}, scoped_skia_write_access_->end_state());
+ if (auto end_state = scoped_skia_write_access_->TakeEndState()) {
+ scoped_skia_write_access_->surface()->flush({}, end_state.get());
}
}
diff --git a/chromium/components/viz/service/display_embedder/output_presenter_gl.cc b/chromium/components/viz/service/display_embedder/output_presenter_gl.cc
index 40790839cd5..5faa7dafe5d 100644
--- a/chromium/components/viz/service/display_embedder/output_presenter_gl.cc
+++ b/chromium/components/viz/service/display_embedder/output_presenter_gl.cc
@@ -224,7 +224,8 @@ std::unique_ptr<OutputPresenterGL> OutputPresenterGL::Create(
return nullptr;
// TODO(https://crbug.com/1012401): don't depend on GL.
auto gl_surface = base::MakeRefCounted<gl::GLSurfaceEGLSurfaceControl>(
- window, base::ThreadTaskRunnerHandle::Get());
+ gl::GLSurfaceEGL::GetGLDisplayEGL(), window,
+ base::ThreadTaskRunnerHandle::Get());
if (!gl_surface->Initialize(gl::GLSurfaceFormat())) {
LOG(ERROR) << "Failed to initialize GLSurfaceEGLSurfaceControl.";
return nullptr;
@@ -416,8 +417,14 @@ void OutputPresenterGL::ScheduleOneOverlay(const OverlayCandidate& overlay,
ScopedOverlayAccess* access) {
#if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE)
auto* gl_image = access ? access->gl_image() : nullptr;
- if (gl_image || overlay.solid_color.has_value()) {
- DCHECK(!overlay.gpu_fence_id);
+ if (gl_image || overlay.is_solid_color) {
+#if DCHECK_IS_ON()
+ if (overlay.is_solid_color) {
+ LOG_IF(FATAL, !overlay.color.has_value())
+ << "Solid color quads must have color set.";
+ }
+ CHECK(!overlay.gpu_fence_id);
+#endif
gl_surface_->ScheduleOverlayPlane(
gl_image, access ? TakeGpuFence(access->TakeAcquireFences()) : nullptr,
gfx::OverlayPlaneData(
@@ -425,7 +432,7 @@ void OutputPresenterGL::ScheduleOneOverlay(const OverlayCandidate& overlay,
overlay.uv_rect, !overlay.is_opaque,
ToEnclosingRect(overlay.damage_rect), overlay.opacity,
overlay.priority_hint, overlay.rounded_corners, overlay.color_space,
- overlay.hdr_metadata, overlay.solid_color));
+ overlay.hdr_metadata, overlay.color, overlay.is_solid_color));
}
#else // BUILDFLAG(IS_ANDROID) || defined(USE_OZONE)
NOTREACHED();
@@ -477,8 +484,14 @@ void OutputPresenterGL::ScheduleOverlays(
// may have a protocol that asks Wayland compositor to create a solid color
// buffer for a client. OverlayProcessorDelegated decides if a solid color
// overlay is an overlay candidate and should be scheduled.
- if (gl_image || overlay.solid_color.has_value()) {
- DCHECK(!overlay.gpu_fence_id);
+ if (gl_image || overlay.is_solid_color) {
+#if DCHECK_IS_ON()
+ if (overlay.is_solid_color) {
+ LOG_IF(FATAL, !overlay.color.has_value())
+ << "Solid color quads must have color set.";
+ }
+ CHECK(!overlay.gpu_fence_id);
+#endif
gl_surface_->ScheduleOverlayPlane(
gl_image,
TakeGpuFenceForOverlay(dependency_, accesses[i], current_frame_fence),
@@ -487,12 +500,10 @@ void OutputPresenterGL::ScheduleOverlays(
overlay.uv_rect, !overlay.is_opaque,
ToEnclosingRect(overlay.damage_rect), overlay.opacity,
overlay.priority_hint, overlay.rounded_corners,
- overlay.color_space, overlay.hdr_metadata, overlay.solid_color));
+ overlay.color_space, overlay.hdr_metadata, overlay.color,
+ overlay.is_solid_color));
}
#elif BUILDFLAG(IS_APPLE)
- // For RenderPassDrawQuad the ddl is not nullptr, and the opacity is applied
- // when the ddl is recorded, so the content already is with opacity applied.
- float opacity = overlay.ddl ? 1.0 : overlay.shared_state->opacity;
gl_surface_->ScheduleCALayer(ui::CARendererLayerParams(
overlay.shared_state->is_clipped,
gfx::ToEnclosingRect(overlay.shared_state->clip_rect),
@@ -500,8 +511,8 @@ void OutputPresenterGL::ScheduleOverlays(
overlay.shared_state->sorting_context_id,
gfx::Transform(overlay.shared_state->transform), gl_image,
overlay.contents_rect, gfx::ToEnclosingRect(overlay.bounds_rect),
- overlay.background_color, overlay.edge_aa_mask, opacity, overlay.filter,
- overlay.protected_video_type));
+ overlay.background_color.toSkColor(), overlay.edge_aa_mask,
+ overlay.opacity, overlay.filter, overlay.protected_video_type));
#endif
}
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
diff --git a/chromium/components/viz/service/display_embedder/output_surface_provider.h b/chromium/components/viz/service/display_embedder/output_surface_provider.h
index 77d463e683d..2e9453c18f2 100644
--- a/chromium/components/viz/service/display_embedder/output_surface_provider.h
+++ b/chromium/components/viz/service/display_embedder/output_surface_provider.h
@@ -28,8 +28,7 @@ class OutputSurfaceProvider {
// of this should feed into the CreateOutputSurface function.
virtual std::unique_ptr<DisplayCompositorMemoryAndTaskController>
CreateGpuDependency(bool gpu_compositing,
- gpu::SurfaceHandle surface_handle,
- const RendererSettings& renderer_settings) = 0;
+ gpu::SurfaceHandle surface_handle) = 0;
// Creates a new OutputSurface for |surface_handle|. If creating an
// OutputSurface fails this function will return null.
diff --git a/chromium/components/viz/service/display_embedder/output_surface_provider_impl.cc b/chromium/components/viz/service/display_embedder/output_surface_provider_impl.cc
index 8a277c6337d..85857907957 100644
--- a/chromium/components/viz/service/display_embedder/output_surface_provider_impl.cc
+++ b/chromium/components/viz/service/display_embedder/output_surface_provider_impl.cc
@@ -20,37 +20,21 @@
#include "components/viz/common/display/renderer_settings.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/service/display/display_compositor_memory_and_task_controller.h"
-#include "components/viz/service/display_embedder/gl_output_surface.h"
-#include "components/viz/service/display_embedder/gl_output_surface_buffer_queue.h"
-#include "components/viz/service/display_embedder/gl_output_surface_offscreen.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/display_embedder/skia_output_surface_dependency_impl.h"
#include "components/viz/service/display_embedder/skia_output_surface_impl.h"
#include "components/viz/service/display_embedder/software_output_surface.h"
-#include "components/viz/service/display_embedder/viz_process_context_provider.h"
#include "components/viz/service/gl/gpu_service_impl.h"
-#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
#include "gpu/command_buffer/client/shared_memory_limits.h"
-#include "gpu/command_buffer/service/image_factory.h"
-#include "gpu/command_buffer/service/mailbox_manager_factory.h"
#include "gpu/config/gpu_finch_features.h"
-#include "gpu/ipc/command_buffer_task_executor.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/ipc/scheduler_sequence.h"
-#include "gpu/ipc/service/gpu_channel_manager_delegate.h"
-#include "gpu/ipc/service/image_transport_surface.h"
#include "ui/base/ui_base_switches.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/init/gl_factory.h"
#if BUILDFLAG(IS_WIN)
#include "components/viz/service/display_embedder/software_output_device_win.h"
#endif
-#if BUILDFLAG(IS_ANDROID)
-#include "components/viz/service/display_embedder/gl_output_surface_android.h"
-#endif
-
#if BUILDFLAG(IS_APPLE)
#include "components/viz/service/display_embedder/software_output_device_mac.h"
#include "ui/base/cocoa/remote_layer_api.h"
@@ -66,7 +50,6 @@
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "components/viz/service/display_embedder/gl_output_surface_chromeos.h"
#include "components/viz/service/display_embedder/output_surface_unified.h"
#endif
@@ -74,26 +57,14 @@ namespace viz {
OutputSurfaceProviderImpl::OutputSurfaceProviderImpl(
GpuServiceImpl* gpu_service_impl,
- gpu::CommandBufferTaskExecutor* task_executor,
- gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
- gpu::ImageFactory* image_factory,
bool headless)
: gpu_service_impl_(gpu_service_impl),
- task_executor_(task_executor),
- gpu_channel_manager_delegate_(gpu_channel_manager_delegate),
- gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
- image_factory_(image_factory),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
headless_(headless) {}
OutputSurfaceProviderImpl::OutputSurfaceProviderImpl(bool headless)
: OutputSurfaceProviderImpl(
/*gpu_service_impl=*/nullptr,
- /*task_executor=*/nullptr,
- /*gpu_channel_manager_delegate=*/nullptr,
- /*gpu_memory_buffer_manager=*/nullptr,
- /*image_factory=*/nullptr,
headless) {}
OutputSurfaceProviderImpl::~OutputSurfaceProviderImpl() = default;
@@ -101,23 +72,15 @@ OutputSurfaceProviderImpl::~OutputSurfaceProviderImpl() = default;
std::unique_ptr<DisplayCompositorMemoryAndTaskController>
OutputSurfaceProviderImpl::CreateGpuDependency(
bool gpu_compositing,
- gpu::SurfaceHandle surface_handle,
- const RendererSettings& renderer_settings) {
+ gpu::SurfaceHandle surface_handle) {
if (!gpu_compositing)
return nullptr;
- if (renderer_settings.use_skia_renderer) {
- gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task;
- auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>(
- gpu_service_impl_, surface_handle);
- return std::make_unique<DisplayCompositorMemoryAndTaskController>(
- std::move(skia_deps));
- } else {
- DCHECK(task_executor_);
- gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task;
- return std::make_unique<DisplayCompositorMemoryAndTaskController>(
- task_executor_, image_factory_);
- }
+ gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task;
+ auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>(
+ gpu_service_impl_, surface_handle);
+ return std::make_unique<DisplayCompositorMemoryAndTaskController>(
+ std::move(skia_deps));
}
std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface(
@@ -139,7 +102,7 @@ std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface(
if (!gpu_compositing) {
output_surface = std::make_unique<SoftwareOutputSurface>(
CreateSoftwareOutputDeviceForPlatform(surface_handle, display_client));
- } else if (renderer_settings.use_skia_renderer) {
+ } else {
DCHECK(gpu_dependency);
{
gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task;
@@ -167,74 +130,6 @@ std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface(
#endif
return nullptr;
}
- } else {
- DCHECK(task_executor_);
- DCHECK(gpu_dependency);
-
- scoped_refptr<VizProcessContextProvider> context_provider;
-
- // Retry creating and binding |context_provider| on transient failures.
- gpu::ContextResult context_result = gpu::ContextResult::kTransientFailure;
- while (context_result != gpu::ContextResult::kSuccess) {
- // We are about to exit the GPU process so don't try to create a context.
- // It will be recreated after the GPU process restarts. The same check
- // also happens on the GPU thread before the context gets initialized
- // there. If GPU process starts to exit after this check but before
- // context initialization we'll encounter a transient error, loop and hit
- // this check again.
- if (gpu_channel_manager_delegate_->IsExiting())
- return nullptr;
-
- context_provider = base::MakeRefCounted<VizProcessContextProvider>(
- task_executor_, surface_handle, gpu_memory_buffer_manager_.get(),
- image_factory_, gpu_channel_manager_delegate_, gpu_dependency,
- renderer_settings);
- context_result = context_provider->BindToCurrentThread();
-
-#if BUILDFLAG(IS_ANDROID)
- display_client->OnContextCreationResult(context_result);
-#endif
-
- if (IsFatalOrSurfaceFailure(context_result)) {
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMECAST)
- // GL compositing is expected to always work on Chrome OS so we should
- // never encounter fatal context error. This could be an unrecoverable
- // hardware error or a bug.
- LOG(FATAL) << "Unexpected fatal context error";
-#elif !BUILDFLAG(IS_ANDROID)
- gpu_service_impl_->DisableGpuCompositing();
-#endif
- return nullptr;
- }
- }
-
- if (surface_handle == gpu::kNullSurfaceHandle) {
- output_surface = std::make_unique<GLOutputSurfaceOffscreen>(
- std::move(context_provider));
- } else if (context_provider->ContextCapabilities().surfaceless) {
-#if defined(USE_OZONE) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID)
- output_surface = std::make_unique<GLOutputSurfaceBufferQueue>(
- std::move(context_provider), surface_handle,
- std::make_unique<BufferQueue>(
- context_provider->SharedImageInterface(), surface_handle));
-#else
- NOTREACHED();
-#endif
- } else {
-#if BUILDFLAG(IS_WIN)
- output_surface = std::make_unique<GLOutputSurface>(
- std::move(context_provider), surface_handle);
-#elif BUILDFLAG(IS_ANDROID)
- output_surface = std::make_unique<GLOutputSurfaceAndroid>(
- std::move(context_provider), surface_handle);
-#elif BUILDFLAG(IS_CHROMEOS_ASH)
- output_surface = std::make_unique<GLOutputSurfaceChromeOS>(
- std::move(context_provider), surface_handle);
-#else
- output_surface = std::make_unique<GLOutputSurface>(
- std::move(context_provider), surface_handle);
-#endif
- }
}
return output_surface;
diff --git a/chromium/components/viz/service/display_embedder/output_surface_provider_impl.h b/chromium/components/viz/service/display_embedder/output_surface_provider_impl.h
index fa9bc45b5c1..be10797e6f5 100644
--- a/chromium/components/viz/service/display_embedder/output_surface_provider_impl.h
+++ b/chromium/components/viz/service/display_embedder/output_surface_provider_impl.h
@@ -14,22 +14,12 @@
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/service/display_embedder/output_surface_provider.h"
#include "components/viz/service/viz_service_export.h"
-#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
#include "gpu/ipc/common/surface_handle.h"
-#include "gpu/ipc/in_process_command_buffer.h"
#if BUILDFLAG(IS_WIN)
#include "components/viz/service/display_embedder/output_device_backing.h"
#endif
-namespace gpu {
-class CommandBufferTaskExecutor;
-class GpuChannelManagerDelegate;
-class GpuMemoryBufferManager;
-class ImageFactory;
-class SharedContextState;
-} // namespace gpu
-
namespace viz {
class GpuServiceImpl;
class SoftwareOutputDevice;
@@ -38,13 +28,7 @@ class SoftwareOutputDevice;
class VIZ_SERVICE_EXPORT OutputSurfaceProviderImpl
: public OutputSurfaceProvider {
public:
- OutputSurfaceProviderImpl(
- GpuServiceImpl* gpu_service_impl,
- gpu::CommandBufferTaskExecutor* task_executor,
- gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
- gpu::ImageFactory* image_factory,
- bool headless);
+ OutputSurfaceProviderImpl(GpuServiceImpl* gpu_service_impl, bool headless);
// Software compositing only.
explicit OutputSurfaceProviderImpl(bool headless);
@@ -56,8 +40,7 @@ class VIZ_SERVICE_EXPORT OutputSurfaceProviderImpl
std::unique_ptr<DisplayCompositorMemoryAndTaskController> CreateGpuDependency(
bool gpu_compositing,
- gpu::SurfaceHandle surface_handle,
- const RendererSettings& renderer_settings) override;
+ gpu::SurfaceHandle surface_handle) override;
// OutputSurfaceProvider implementation.
std::unique_ptr<OutputSurface> CreateOutputSurface(
@@ -74,10 +57,6 @@ class VIZ_SERVICE_EXPORT OutputSurfaceProviderImpl
mojom::DisplayClient* display_client);
const raw_ptr<GpuServiceImpl> gpu_service_impl_;
- const raw_ptr<gpu::CommandBufferTaskExecutor> task_executor_;
- const raw_ptr<gpu::GpuChannelManagerDelegate> gpu_channel_manager_delegate_;
- const raw_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_;
- const raw_ptr<gpu::ImageFactory> image_factory_;
#if BUILDFLAG(IS_WIN)
// Used for software compositing output on Windows.
@@ -86,11 +65,6 @@ class VIZ_SERVICE_EXPORT OutputSurfaceProviderImpl
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- // A shared context which will be used on display compositor thread.
- scoped_refptr<gpu::SharedContextState> shared_context_state_;
- std::unique_ptr<gpu::MailboxManager> mailbox_manager_;
- std::unique_ptr<gpu::SyncPointManager> sync_point_manager_;
-
const bool headless_;
};
diff --git a/chromium/components/viz/service/display_embedder/output_surface_unified.cc b/chromium/components/viz/service/display_embedder/output_surface_unified.cc
index f01790c3016..a5c52de25f7 100644
--- a/chromium/components/viz/service/display_embedder/output_surface_unified.cc
+++ b/chromium/components/viz/service/display_embedder/output_surface_unified.cc
@@ -29,22 +29,6 @@ bool OutputSurfaceUnified::IsDisplayedAsOverlayPlane() const {
return false;
}
-unsigned OutputSurfaceUnified::GetOverlayTextureId() const {
- return 0;
-}
-
-bool OutputSurfaceUnified::HasExternalStencilTest() const {
- return false;
-}
-
-uint32_t OutputSurfaceUnified::GetFramebufferCopyTextureFormat() {
- return 0;
-}
-
-unsigned OutputSurfaceUnified::UpdateGpuFence() {
- return 0;
-}
-
gfx::OverlayTransform OutputSurfaceUnified::GetDisplayTransform() {
return gfx::OVERLAY_TRANSFORM_NONE;
}
diff --git a/chromium/components/viz/service/display_embedder/output_surface_unified.h b/chromium/components/viz/service/display_embedder/output_surface_unified.h
index 62b6ef6a8df..8d9d4deb688 100644
--- a/chromium/components/viz/service/display_embedder/output_surface_unified.h
+++ b/chromium/components/viz/service/display_embedder/output_surface_unified.h
@@ -34,19 +34,9 @@ class OutputSurfaceUnified : public OutputSurface {
void BindToClient(OutputSurfaceClient* client) override {}
void EnsureBackbuffer() override {}
void DiscardBackbuffer() override {}
- void BindFramebuffer() override {}
- void Reshape(const gfx::Size& size,
- float scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool stencil) override {}
+ void Reshape(const ReshapeParams& params) override {}
void SwapBuffers(OutputSurfaceFrame frame) override;
bool IsDisplayedAsOverlayPlane() const override;
- unsigned GetOverlayTextureId() const override;
- bool HasExternalStencilTest() const override;
- void ApplyExternalStencil() override {}
- uint32_t GetFramebufferCopyTextureFormat() override;
- unsigned UpdateGpuFence() override;
void SetUpdateVSyncParametersCallback(
UpdateVSyncParametersCallback callback) override {}
void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
diff --git a/chromium/components/viz/service/display_embedder/skia_output_device.cc b/chromium/components/viz/service/display_embedder/skia_output_device.cc
index 1c28222954f..bbc98d2edf7 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_device.cc
+++ b/chromium/components/viz/service/display_embedder/skia_output_device.cc
@@ -9,7 +9,9 @@
#include "base/bind.h"
#include "base/check_op.h"
+#include "base/feature_list.h"
#include "base/notreached.h"
+#include "base/task/task_features.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
@@ -27,11 +29,18 @@
namespace viz {
namespace {
+const base::Feature kAsyncGpuLatencyReporting CONSTINIT{
+ "AsyncGpuLatencyReporting", base::FEATURE_ENABLED_BY_DEFAULT};
+
using ::perfetto::protos::pbzero::ChromeLatencyInfo;
scoped_refptr<base::SequencedTaskRunner> CreateLatencyTracerRunner() {
if (!base::ThreadPoolInstance::Get())
return nullptr;
+
+ if (!base::FeatureList::IsEnabled(kAsyncGpuLatencyReporting))
+ return nullptr;
+
return base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc b/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
index e788908ffd5..f5ea153eaa7 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
+++ b/chromium/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
@@ -394,11 +394,12 @@ void SkiaOutputDeviceBufferQueue::ScheduleOverlays(
auto& overlay = overlays[i];
#if defined(USE_OZONE)
- if (overlay.solid_color.has_value()) {
+ if (overlay.is_solid_color) {
+ DCHECK(overlay.color.has_value());
// TODO(msisov): reconsider this once Linux Wayland compositors also
// support that. See https://bit.ly/2ZqUO0w.
if (!supports_non_backed_solid_color_images_) {
- overlay.mailbox = GetImageMailboxForColor(overlay.solid_color.value());
+ overlay.mailbox = GetImageMailboxForColor(overlay.color.value());
} else {
accesses[i] = nullptr;
continue;
@@ -704,12 +705,13 @@ void SkiaOutputDeviceBufferQueue::MaybeScheduleBackgroundImage() {
OverlayCandidate candidate;
candidate.color_space = color_space_;
candidate.display_rect = gfx::RectF(gfx::SizeF(viewport_size_));
- candidate.solid_color = SK_ColorTRANSPARENT;
+ candidate.color = SK_ColorTRANSPARENT;
candidate.plane_z_order = INT32_MIN;
+ candidate.is_solid_color = supports_non_backed_solid_color_images_;
#if defined(USE_OZONE)
if (!supports_non_backed_solid_color_images_) {
- auto mailbox = GetImageMailboxForColor(candidate.solid_color.value());
+ auto mailbox = GetImageMailboxForColor(candidate.color.value());
DCHECK(mailbox.IsSharedImage());
auto* overlay_data = GetOrCreateOverlayData(mailbox);
diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_gl.cc b/chromium/components/viz/service/display_embedder/skia_output_device_gl.cc
index 3f1a3b254d0..3f63127fe35 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_device_gl.cc
+++ b/chromium/components/viz/service/display_embedder/skia_output_device_gl.cc
@@ -269,7 +269,7 @@ bool SkiaOutputDeviceGL::Reshape(
: kBottomLeft_GrSurfaceOrigin;
sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
context_state_->gr_context(), render_target, origin, color_type,
- color_space.ToSkColorSpace(), &surface_props);
+ characterization.refColorSpace(), &surface_props);
if (!sk_surface_) {
LOG(ERROR) << "Couldn't create surface:"
<< "\n abandoned()="
diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_webview.cc b/chromium/components/viz/service/display_embedder/skia_output_device_webview.cc
index cb385a7d589..8a450b991bb 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_device_webview.cc
+++ b/chromium/components/viz/service/display_embedder/skia_output_device_webview.cc
@@ -65,7 +65,7 @@ bool SkiaOutputDeviceWebView::Reshape(
}
size_ = size;
- color_space_ = color_space;
+ sk_color_space_ = characterization.refColorSpace();
InitSkiaSurface(gl_surface_->GetBackingFramebufferObject());
return !!sk_surface_;
}
@@ -115,14 +115,13 @@ void SkiaOutputDeviceWebView::InitSkiaSurface(unsigned int fbo) {
SkSurfaceProps surface_props{0, kUnknown_SkPixelGeometry};
sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
context_state_->gr_context(), render_target, origin, color_type,
- color_space_.ToSkColorSpace(), &surface_props);
+ sk_color_space_, &surface_props);
if (!sk_surface_) {
LOG(ERROR) << "Couldn't create surface: "
<< context_state_->gr_context()->abandoned() << " " << color_type
<< " " << framebuffer_info.fFBOID << " "
- << framebuffer_info.fFormat << " " << color_space_.ToString()
- << " " << size_.ToString();
+ << framebuffer_info.fFormat << " " << size_.ToString();
}
}
diff --git a/chromium/components/viz/service/display_embedder/skia_output_device_webview.h b/chromium/components/viz/service/display_embedder/skia_output_device_webview.h
index 024727a2155..944310366a5 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_device_webview.h
+++ b/chromium/components/viz/service/display_embedder/skia_output_device_webview.h
@@ -54,7 +54,7 @@ class SkiaOutputDeviceWebView : public SkiaOutputDevice {
sk_sp<SkSurface> sk_surface_;
gfx::Size size_;
- gfx::ColorSpace color_space_;
+ sk_sp<SkColorSpace> sk_color_space_;
unsigned int last_frame_buffer_object_ = -1;
base::WeakPtrFactory<SkiaOutputDeviceWebView> weak_ptr_factory_{this};
diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc
index f53ec05511e..74fd2601377 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -82,18 +82,16 @@ OutputSurface::Type GetOutputSurfaceType(SkiaOutputSurfaceDependency* deps) {
SkiaOutputSurfaceImpl::ScopedPaint::ScopedPaint(
SkDeferredDisplayListRecorder* root_recorder)
- : recorder_(root_recorder), render_pass_id_(0) {}
+ : recorder_(root_recorder) {}
SkiaOutputSurfaceImpl::ScopedPaint::ScopedPaint(
SkSurfaceCharacterization characterization)
- : ScopedPaint(characterization, AggregatedRenderPassId(0), gpu::Mailbox()) {
-}
+ : ScopedPaint(characterization, gpu::Mailbox()) {}
SkiaOutputSurfaceImpl::ScopedPaint::ScopedPaint(
SkSurfaceCharacterization characterization,
- AggregatedRenderPassId render_pass_id,
gpu::Mailbox mailbox)
- : render_pass_id_(render_pass_id), mailbox_(mailbox) {
+ : mailbox_(mailbox) {
recorder_storage_.emplace(characterization);
recorder_ = &recorder_storage_.value();
}
@@ -236,10 +234,6 @@ void SkiaOutputSurfaceImpl::BindToClient(OutputSurfaceClient* client) {
client_ = client;
}
-void SkiaOutputSurfaceImpl::BindFramebuffer() {
- // TODO(penghuang): remove this method when GLRenderer is removed.
-}
-
void SkiaOutputSurfaceImpl::SetDrawRectangle(const gfx::Rect& draw_rectangle) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(capabilities().supports_dc_layers);
@@ -290,21 +284,17 @@ void SkiaOutputSurfaceImpl::RecreateRootRecorder() {
std::ignore = root_recorder_->getCanvas();
}
-void SkiaOutputSurfaceImpl::Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) {
+void SkiaOutputSurfaceImpl::Reshape(const ReshapeParams& params) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!size.IsEmpty());
+ DCHECK(!params.size.IsEmpty());
// SetDrawRectangle() will need to be called at the new size.
has_set_draw_rectangle_for_frame_ = false;
if (use_damage_area_from_skia_output_device_) {
- damage_of_current_buffer_ = gfx::Rect(size);
+ damage_of_current_buffer_ = gfx::Rect(params.size);
} else if (frame_buffer_damage_tracker_) {
- frame_buffer_damage_tracker_->FrameBuffersChanged(size);
+ frame_buffer_damage_tracker_->FrameBuffersChanged(params.size);
}
if (is_using_raw_draw_ && is_raw_draw_using_msaa_) {
@@ -312,34 +302,35 @@ void SkiaOutputSurfaceImpl::Reshape(const gfx::Size& size,
// On "low-end" devices use 4 samples per pixel to save memory.
sample_count_ = 4;
} else {
- sample_count_ = device_scale_factor >= 2.0f ? 4 : 8;
+ sample_count_ = params.device_scale_factor >= 2.0f ? 4 : 8;
}
} else {
sample_count_ = 1;
}
- const auto format_index = static_cast<int>(format);
+ const auto format_index = static_cast<int>(params.format);
const auto& color_type = capabilities_.sk_color_types[format_index];
DCHECK(color_type != kUnknown_SkColorType)
<< "SkColorType is invalid for buffer format_index: " << format_index;
- auto sk_color_space = color_space.ToSkColorSpace();
+ auto sk_color_space =
+ params.color_space.ToSkColorSpace(params.sdr_white_level);
characterization_ = CreateSkSurfaceCharacterization(
- size, color_type, /*mipmap=*/false, std::move(sk_color_space),
+ params.size, color_type, /*mipmap=*/false, std::move(sk_color_space),
/*is_root_render_pass=*/true, /*is_overlay=*/false);
// impl_on_gpu_ is released on the GPU thread by a posted task from
// SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
- auto task =
- base::BindOnce(&SkiaOutputSurfaceImplOnGpu::Reshape,
- base::Unretained(impl_on_gpu_.get()), characterization_,
- color_space, device_scale_factor, GetDisplayTransform());
+ auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::Reshape,
+ base::Unretained(impl_on_gpu_.get()),
+ characterization_, params.color_space,
+ params.device_scale_factor, GetDisplayTransform());
EnqueueGpuTask(std::move(task), {}, /*make_current=*/true,
/*need_framebuffer=*/!dependency_->IsOffscreen());
FlushGpuTasks(SyncMode::kNoWait);
- size_ = size;
- format_ = format;
+ size_ = params.size;
+ format_ = params.format;
RecreateRootRecorder();
}
@@ -604,6 +595,7 @@ SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPass(
ResourceFormat format,
bool mipmap,
sk_sp<SkColorSpace> color_space,
+ bool is_overlay,
const gpu::Mailbox& mailbox) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Make sure there is no unsubmitted PaintFrame or PaintRenderPass.
@@ -611,78 +603,39 @@ SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPass(
DCHECK(resource_sync_tokens_.empty());
SkColorType color_type =
- ResourceFormatToClosestSkColorType(true /* gpu_compositing */, format);
+ ResourceFormatToClosestSkColorType(/*gpu_compositing=*/true, format);
SkSurfaceCharacterization characterization = CreateSkSurfaceCharacterization(
surface_size, color_type, mipmap, std::move(color_space),
- /*is_root_render_pass=*/false, /*is_overlay=*/false);
+ /*is_root_render_pass=*/false, /*is_overlay=*/is_overlay);
if (!characterization.isValid())
return nullptr;
- current_paint_.emplace(characterization, id, mailbox);
- return current_paint_->recorder()->getCanvas();
-}
-
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
-SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPassOverlay(
- const gfx::Size& size,
- ResourceFormat format,
- bool mipmap,
- sk_sp<SkColorSpace> color_space) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // Make sure there is no unsubmitted PaintFrame or PaintRenderPass.
- DCHECK(!current_paint_);
- SkColorType color_type =
- ResourceFormatToClosestSkColorType(true /* gpu_compositing */, format);
- SkSurfaceCharacterization characterization = CreateSkSurfaceCharacterization(
- size, color_type, mipmap, std::move(color_space),
- /*is_root_render_pass=*/false, /*is_overlay=*/true);
- if (!characterization.isValid())
- return nullptr;
+ // We are going to overwrite the render pass when it is not for overlay, so we
+ // need to reset the image_context and a new promise image will be created
+ // when MakePromiseSkImageFromRenderPass() is called.
+ if (!is_overlay) {
+ auto it = render_pass_image_cache_.find(id);
+ if (it != render_pass_image_cache_.end()) {
+ it->second->clear_image();
+ }
+ }
- current_paint_.emplace(characterization);
+ current_paint_.emplace(characterization, mailbox);
return current_paint_->recorder()->getCanvas();
}
-sk_sp<SkDeferredDisplayList>
-SkiaOutputSurfaceImpl::EndPaintRenderPassOverlay() {
+void SkiaOutputSurfaceImpl::EndPaint(
+ base::OnceClosure on_finished,
+ base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_paint_);
-
auto ddl = current_paint_->recorder()->detach();
- current_paint_.reset();
- return ddl;
-}
-#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
-
-void SkiaOutputSurfaceImpl::EndPaint(base::OnceClosure on_finished) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(current_paint_);
- // If current_render_pass_id_ is not null, we are painting a render pass.
- // Otherwise we are painting a frame.
-
- bool painting_render_pass = !current_paint_->render_pass_id().is_null();
- auto ddl = current_paint_->recorder()->detach();
-
- // impl_on_gpu_ is released on the GPU thread by a posted task from
- // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
- if (painting_render_pass) {
- auto it = render_pass_image_cache_.find(current_paint_->render_pass_id());
- if (it != render_pass_image_cache_.end()) {
- // We are going to overwrite the render pass, so we need reset the
- // image_context, so a new promise image will be created when the
- // MakePromiseSkImageFromRenderPass() is called.
- it->second->clear_image();
- }
-
- auto task = base::BindOnce(
- &SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass,
- base::Unretained(impl_on_gpu_.get()), current_paint_->mailbox(),
- std::move(ddl), std::move(images_in_current_paint_),
- resource_sync_tokens_, std::move(on_finished));
- EnqueueGpuTask(std::move(task), std::move(resource_sync_tokens_),
- /*make_current=*/true, /*need_framebuffer=*/false);
- } else {
+ // If the current paint mailbox is empty, we are painting a frame, otherwise
+ // we are painting a render pass. impl_on_gpu_ is released on the GPU thread
+ // by a posted task from SkiaOutputSurfaceImpl::dtor, so it is safe to use
+ // base::Unretained.
+ if (current_paint_->mailbox().IsZero()) {
// Draw on the root render pass.
current_buffer_modified_ = true;
sk_sp<SkDeferredDisplayList> overdraw_ddl;
@@ -698,10 +651,20 @@ void SkiaOutputSurfaceImpl::EndPaint(base::OnceClosure on_finished) {
&SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame,
base::Unretained(impl_on_gpu_.get()), std::move(ddl),
std::move(overdraw_ddl), std::move(images_in_current_paint_),
- resource_sync_tokens_, std::move(on_finished), draw_rectangle_);
+ resource_sync_tokens_, std::move(on_finished),
+ std::move(return_release_fence_cb), draw_rectangle_);
EnqueueGpuTask(std::move(task), std::move(resource_sync_tokens_),
/*make_current=*/true, /*need_framebuffer=*/true);
draw_rectangle_.reset();
+ } else {
+ auto task = base::BindOnce(
+ &SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass,
+ base::Unretained(impl_on_gpu_.get()), current_paint_->mailbox(),
+ std::move(ddl), std::move(images_in_current_paint_),
+ resource_sync_tokens_, std::move(on_finished),
+ std::move(return_release_fence_cb));
+ EnqueueGpuTask(std::move(task), std::move(resource_sync_tokens_),
+ /*make_current=*/true, /*need_framebuffer=*/false);
}
images_in_current_paint_.clear();
current_paint_.reset();
@@ -802,30 +765,12 @@ void SkiaOutputSurfaceImpl::CopyOutput(
void SkiaOutputSurfaceImpl::ScheduleOverlays(
OverlayList overlays,
- std::vector<gpu::SyncToken> sync_tokens,
- base::OnceClosure on_finished) {
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
- // If there are render pass overlays, then a gl context is needed for drawing
- // the overlay render passes to a backing for being scanned out.
- bool make_current =
- std::find_if(overlays.begin(), overlays.end(), [](const auto& overlay) {
- return !!overlay.ddl;
- }) != overlays.end();
- // Append |resource_sync_tokens_| which are depended by drawing render passes
- // to overlay backings.
- std::move(resource_sync_tokens_.begin(), resource_sync_tokens_.end(),
- std::back_inserter(sync_tokens));
- resource_sync_tokens_.clear();
-#else
- bool make_current = false;
-#endif
- auto task = base::BindOnce(
- &SkiaOutputSurfaceImplOnGpu::ScheduleOverlays,
- base::Unretained(impl_on_gpu_.get()), std::move(overlays),
- std::move(images_in_current_paint_), std::move(on_finished));
- EnqueueGpuTask(std::move(task), std::move(sync_tokens), make_current,
- /*need_framebuffer=*/false);
- images_in_current_paint_.clear();
+ std::vector<gpu::SyncToken> sync_tokens) {
+ auto task =
+ base::BindOnce(&SkiaOutputSurfaceImplOnGpu::ScheduleOverlays,
+ base::Unretained(impl_on_gpu_.get()), std::move(overlays));
+ EnqueueGpuTask(std::move(task), std::move(sync_tokens),
+ /*make_current=*/false, /*need_framebuffer=*/false);
}
void SkiaOutputSurfaceImpl::SetFrameRate(float frame_rate) {
@@ -1050,9 +995,6 @@ void SkiaOutputSurfaceImpl::DidSwapBuffersComplete(
DCHECK(damage_of_current_buffer_);
}
- // texture_in_use_responses is used for GLRenderer only.
- DCHECK(params.texture_in_use_responses.empty());
-
if (!params.ca_layer_params.is_empty)
client_->DidReceiveCALayerParams(params.ca_layer_params);
client_->DidReceiveSwapBuffersAck(params.swap_response.timings,
@@ -1231,40 +1173,15 @@ GrBackendFormat SkiaOutputSurfaceImpl::GetGrBackendFormatForTexture(
return GrBackendFormat();
}
-uint32_t SkiaOutputSurfaceImpl::GetFramebufferCopyTextureFormat() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
- return GL_RGB;
-}
-
bool SkiaOutputSurfaceImpl::IsDisplayedAsOverlayPlane() const {
return is_displayed_as_overlay_;
}
-unsigned SkiaOutputSurfaceImpl::GetOverlayTextureId() const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return 0;
-}
-
gpu::Mailbox SkiaOutputSurfaceImpl::GetOverlayMailbox() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return last_swapped_mailbox_;
}
-bool SkiaOutputSurfaceImpl::HasExternalStencilTest() const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
- return false;
-}
-
-void SkiaOutputSurfaceImpl::ApplyExternalStencil() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-}
-
-unsigned SkiaOutputSurfaceImpl::UpdateGpuFence() {
- return 0;
-}
-
void SkiaOutputSurfaceImpl::SetNeedsSwapSizeNotifications(
bool needs_swap_size_notifications) {
needs_swap_size_notifications_ = needs_swap_size_notifications;
diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h
index be279278a47..685a7003fee 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -38,7 +38,8 @@ class DelegatedInkPointRenderer;
namespace gpu {
class SharedImageRepresentationFactory;
-}
+struct SwapBuffersCompleteParams;
+} // namespace gpu
namespace viz {
@@ -76,16 +77,11 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
// OutputSurface implementation:
gpu::SurfaceHandle GetSurfaceHandle() const override;
void BindToClient(OutputSurfaceClient* client) override;
- void BindFramebuffer() override;
void SetDrawRectangle(const gfx::Rect& draw_rectangle) override;
void SetEnableDCLayers(bool enable) override;
void EnsureBackbuffer() override;
void DiscardBackbuffer() override;
- void Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) override;
+ void Reshape(const ReshapeParams& params) override;
void SetUpdateVSyncParametersCallback(
UpdateVSyncParametersCallback callback) override;
void SetGpuVSyncEnabled(bool enabled) override;
@@ -93,13 +89,8 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
void SetDisplayTransformHint(gfx::OverlayTransform transform) override;
gfx::OverlayTransform GetDisplayTransform() override;
void SwapBuffers(OutputSurfaceFrame frame) override;
- uint32_t GetFramebufferCopyTextureFormat() override;
bool IsDisplayedAsOverlayPlane() const override;
- unsigned GetOverlayTextureId() const override;
gpu::Mailbox GetOverlayMailbox() const override;
- bool HasExternalStencilTest() const override;
- void ApplyExternalStencil() override;
- unsigned UpdateGpuFence() override;
void SetNeedsSwapSizeNotifications(
bool needs_swap_size_notifications) override;
base::ScopedClosureRunner GetCacheBackBufferCb() override;
@@ -124,8 +115,11 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
ResourceFormat format,
bool mipmap,
sk_sp<SkColorSpace> color_space,
+ bool is_overlay,
const gpu::Mailbox& mailbox) override;
- void EndPaint(base::OnceClosure on_finished) override;
+ void EndPaint(base::OnceClosure on_finished,
+ base::OnceCallback<void(gfx::GpuFenceHandle)>
+ return_release_fence_cb) override;
void MakePromiseSkImage(ImageContext* image_context) override;
sk_sp<SkImage> MakePromiseSkImageFromRenderPass(
const AggregatedRenderPassId& id,
@@ -138,8 +132,7 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
void RemoveRenderPassResource(
std::vector<AggregatedRenderPassId> ids) override;
void ScheduleOverlays(OverlayList overlays,
- std::vector<gpu::SyncToken> sync_tokens,
- base::OnceClosure on_finished) override;
+ std::vector<gpu::SyncToken> sync_tokens) override;
void CopyOutput(AggregatedRenderPassId id,
const copy_output::RenderPassGeometry& geometry,
@@ -153,15 +146,6 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
gpu::SyncToken Flush() override;
bool EnsureMinNumberOfBuffers(int n) override;
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
- SkCanvas* BeginPaintRenderPassOverlay(
- const gfx::Size& size,
- ResourceFormat format,
- bool mipmap,
- sk_sp<SkColorSpace> color_space) override;
- sk_sp<SkDeferredDisplayList> EndPaintRenderPassOverlay() override;
-#endif
-
// ExternalUseClient implementation:
gpu::SyncToken ReleaseImageContexts(
std::vector<std::unique_ptr<ImageContext>> image_contexts) override;
@@ -255,21 +239,18 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
explicit ScopedPaint(SkDeferredDisplayListRecorder* root_recorder);
explicit ScopedPaint(SkSurfaceCharacterization characterization);
ScopedPaint(SkSurfaceCharacterization characterization,
- AggregatedRenderPassId render_pass_id,
gpu::Mailbox mailbox);
~ScopedPaint();
SkDeferredDisplayListRecorder* recorder() { return recorder_; }
- AggregatedRenderPassId render_pass_id() { return render_pass_id_; }
gpu::Mailbox mailbox() { return mailbox_; }
private:
// This is recorder being used for current paint
SkDeferredDisplayListRecorder* recorder_;
- // If we need new recorder for this Paint (i.e it's not root render pass),
+ // If we need new recorder for this Paint (i.e. it's not root render pass),
// it's stored here
absl::optional<SkDeferredDisplayListRecorder> recorder_storage_;
- const AggregatedRenderPassId render_pass_id_;
const gpu::Mailbox mailbox_;
};
diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 620942a9ec8..dcca5fa92f6 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -12,6 +12,8 @@
#include "base/callback_helpers.h"
#include "base/debug/crash_logging.h"
#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/notreached.h"
#include "base/task/bind_post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
@@ -37,7 +39,10 @@
#include "components/viz/service/display_embedder/skia_output_device_webview.h"
#include "components/viz/service/display_embedder/skia_output_surface_dependency.h"
#include "components/viz/service/display_embedder/skia_render_copy_results.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
#include "gpu/command_buffer/common/swap_buffers_complete_params.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "gpu/command_buffer/service/external_semaphore.h"
#include "gpu/command_buffer/service/gr_shader_cache.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/scheduler.h"
@@ -64,14 +69,19 @@
#include "third_party/skia/include/core/SkSamplingOptions.h"
#include "third_party/skia/include/core/SkSize.h"
#include "third_party/skia/include/core/SkYUVAInfo.h"
+#include "third_party/skia/include/gpu/GrTypes.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/gpu_fence_handle.h"
+#include "ui/gl/gl_fence.h"
#include "ui/gl/gl_surface.h"
#if BUILDFLAG(ENABLE_VULKAN)
#include "components/viz/service/display_embedder/skia_output_device_vulkan.h"
+#include "gpu/vulkan/vulkan_device_queue.h"
+#include "gpu/vulkan/vulkan_function_pointers.h"
+#include "gpu/vulkan/vulkan_implementation.h"
#include "gpu/vulkan/vulkan_util.h"
#if BUILDFLAG(IS_ANDROID)
#include "components/viz/service/display_embedder/skia_output_device_vulkan_secondary_cb.h"
@@ -357,6 +367,11 @@ SkiaOutputSurfaceImplOnGpu::~SkiaOutputSurfaceImplOnGpu() {
// before deleting ImplOnGpu's other member variables.
shared_image_factory_.reset();
if (has_context) {
+ absl::optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use;
+ if (dependency_->GetGrShaderCache()) {
+ cache_use.emplace(dependency_->GetGrShaderCache(),
+ gpu::kDisplayCompositorClientId);
+ }
// This ensures any outstanding callbacks for promise images are
// performed.
gr_context()->flushAndSubmit();
@@ -393,6 +408,7 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame(
std::vector<ImageContextImpl*> image_contexts,
std::vector<gpu::SyncToken> sync_tokens,
base::OnceClosure on_finished,
+ base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb,
absl::optional<gfx::Rect> draw_rectangle) {
TRACE_EVENT0("viz", "SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame");
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -445,7 +461,12 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame(
// Draw will only fail if the SkSurface and SkDDL are incompatible.
bool draw_success = scoped_output_device_paint_->Draw(ddl);
+#if defined(USE_OZONE)
+ if (!draw_success)
+ DLOG(ERROR) << "output_sk_surface()->draw() failed.";
+#else
DCHECK(draw_success);
+#endif // USE_OZONE
destroy_after_swap_.emplace_back(std::move(ddl));
@@ -470,7 +491,24 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame(
end_semaphores.insert(end_semaphores.end(), end_paint_semaphores.begin(),
end_paint_semaphores.end());
+#if BUILDFLAG(ENABLE_VULKAN)
+ // Semaphores for release fences for vulkan should be created before flush.
+ if (!return_release_fence_cb.is_null() && is_using_vulkan()) {
+ const bool result = CreateAndStoreExternalSemaphoreVulkan(end_semaphores);
+ // A release fence will be created on submit as some platforms may use
+ // VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT handle types for their
+ // external semaphore. That handle type has COPY transference. Vulkan spec
+ // says that semaphore has to be signaled, or have an associated semaphore
+ // signal operation pending execution. Thus, delay importing the handle
+ // and creating the fence until commands are submitted.
+ pending_release_fence_cbs_.emplace_back(
+ result ? end_semaphores.back() : GrBackendSemaphore(),
+ std::move(return_release_fence_cb));
+ }
+#endif
+
const bool end_semaphores_empty = end_semaphores.empty();
+
auto result = scoped_output_device_paint_->Flush(vulkan_context_provider_,
std::move(end_semaphores),
std::move(on_finished));
@@ -481,6 +519,22 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame(
FailedSkiaFlush("output_sk_surface()->flush() failed.");
return;
}
+
+ gfx::GpuFenceHandle release_fence;
+ if (!return_release_fence_cb.is_null() && is_using_gl()) {
+ DCHECK(release_fence.is_null());
+ release_fence = CreateReleaseFenceForGL();
+ }
+
+ if (!return_release_fence_cb.is_null() && is_using_dawn())
+ NOTIMPLEMENTED() << "Release fences with dawn are not supported.";
+
+ if (!return_release_fence_cb.is_null()) {
+ // Returning fences for Vulkan is delayed. See the comment above.
+ DCHECK(!is_using_vulkan());
+ PostTaskToClientThread(base::BindOnce(std::move(return_release_fence_cb),
+ std::move(release_fence)));
+ }
}
}
@@ -525,7 +579,8 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
sk_sp<SkDeferredDisplayList> ddl,
std::vector<ImageContextImpl*> image_contexts,
std::vector<gpu::SyncToken> sync_tokens,
- base::OnceClosure on_finished) {
+ base::OnceClosure on_finished,
+ base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb) {
TRACE_EVENT0("viz", "SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass");
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(ddl);
@@ -576,6 +631,22 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
backing_representation->SetCleared();
destroy_after_swap_.emplace_back(std::move(ddl));
+#if BUILDFLAG(ENABLE_VULKAN)
+ // Semaphores for release fences for vulkan should be created before flush.
+ if (!return_release_fence_cb.is_null() && is_using_vulkan()) {
+ const bool result = CreateAndStoreExternalSemaphoreVulkan(end_semaphores);
+ // A release fence will be created on submit as some platforms may use
+ // VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT handle types for their
+ // external semaphore. That handle type has COPY transference. Vulkan spec
+ // says that semaphore has to be signaled, or have an associated semaphore
+ // signal operation pending execution. Thus, delay importing the handle
+ // and creating the fence until commands are submitted.
+ pending_release_fence_cbs_.emplace_back(
+ result ? end_semaphores.back() : GrBackendSemaphore(),
+ std::move(return_release_fence_cb));
+ }
+#endif
+
GrFlushInfo flush_info = {
.fNumSemaphores = end_semaphores.size(),
.fSignalSemaphores = end_semaphores.data(),
@@ -584,6 +655,7 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
&flush_info);
if (on_finished)
gpu::AddCleanupTaskForSkiaFlush(std::move(on_finished), &flush_info);
+
auto result = surface->flush(flush_info);
if (result != GrSemaphoresSubmitted::kYes &&
!(begin_semaphores.empty() && end_semaphores.empty())) {
@@ -591,6 +663,24 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
FailedSkiaFlush("offscreen.surface()->flush() failed.");
return;
}
+
+ // If GL is used, create the release fence after flush.
+ gfx::GpuFenceHandle release_fence;
+ if (!return_release_fence_cb.is_null() && is_using_gl()) {
+ DCHECK(release_fence.is_null());
+ release_fence = CreateReleaseFenceForGL();
+ }
+
+ if (!return_release_fence_cb.is_null() && is_using_dawn())
+ NOTIMPLEMENTED() << "Release fences with dawn are not supported.";
+
+ if (!return_release_fence_cb.is_null()) {
+ // Returning fences for Vulkan is delayed. See the comment above.
+ DCHECK(!is_using_vulkan());
+ PostTaskToClientThread(base::BindOnce(std::move(return_release_fence_cb),
+ std::move(release_fence)));
+ }
+
bool sync_cpu =
gpu::ShouldVulkanSyncCpuForSkiaSubmit(vulkan_context_provider_);
if (sync_cpu) {
@@ -701,12 +791,24 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputRGBA(
request->scale_from().y());
}
- bool success = RenderSurface(surface, src_rect, scaling,
- is_downscale_or_identity_in_both_dimensions,
- scoped_write->surface(), begin_semaphores,
- end_semaphores);
- if (!success) {
- DLOG(ERROR) << "failed to render surface.";
+ scoped_write->surface()->wait(begin_semaphores.size(),
+ begin_semaphores.data());
+
+ RenderSurface(surface, src_rect, scaling,
+ is_downscale_or_identity_in_both_dimensions,
+ scoped_write->surface());
+
+ bool should_submit = !end_semaphores.empty();
+
+ if (!FlushSurface(scoped_write->surface(), end_semaphores,
+ scoped_write->TakeEndState())) {
+ // TODO(penghuang): handle vulkan device lost.
+ FailedSkiaFlush("CopyOutputRGBA dest_surface->flush()");
+ return;
+ }
+
+ if (should_submit && !gr_context()->submit()) {
+ DLOG(ERROR) << "CopyOutputRGBA gr_context->submit() failed";
return;
}
@@ -730,16 +832,12 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputRGBA(
}
}
-bool SkiaOutputSurfaceImplOnGpu::RenderSurface(
+void SkiaOutputSurfaceImplOnGpu::RenderSurface(
SkSurface* surface,
const SkIRect& source_selection,
absl::optional<SkVector> scaling,
bool is_downscale_or_identity_in_both_dimensions,
- SkSurface* dest_surface,
- std::vector<GrBackendSemaphore>& begin_semaphores,
- std::vector<GrBackendSemaphore>& end_semaphores) {
- dest_surface->wait(begin_semaphores.size(), begin_semaphores.data());
-
+ SkSurface* dest_surface) {
SkCanvas* dest_canvas = dest_surface->getCanvas();
int state_depth = dest_canvas->save();
@@ -765,21 +863,23 @@ bool SkiaOutputSurfaceImplOnGpu::RenderSurface(
sampling, /*paint=*/nullptr);
dest_canvas->restoreToCount(state_depth);
+}
+bool SkiaOutputSurfaceImplOnGpu::FlushSurface(
+ SkSurface* surface,
+ std::vector<GrBackendSemaphore>& end_semaphores,
+ std::unique_ptr<GrBackendSurfaceMutableState> end_state,
+ GrGpuFinishedProc finished_proc,
+ GrGpuFinishedContext finished_context) {
GrFlushInfo flush_info;
flush_info.fNumSemaphores = end_semaphores.size();
flush_info.fSignalSemaphores = end_semaphores.data();
+ flush_info.fFinishedProc = finished_proc;
+ flush_info.fFinishedContext = finished_context;
gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_, &flush_info);
- GrSemaphoresSubmitted flush_result = dest_surface->flush(
- SkSurface::BackendSurfaceAccess::kNoAccess, flush_info);
- if (flush_result != GrSemaphoresSubmitted::kYes &&
- !(begin_semaphores.empty() && end_semaphores.empty())) {
- // TODO(penghuang): handle vulkan device lost.
- FailedSkiaFlush("dest_surface->flush() failed.");
- return false;
- }
-
- return true;
+ GrSemaphoresSubmitted flush_result =
+ surface->flush(flush_info, end_state.get());
+ return flush_result == GrSemaphoresSubmitted::kYes || end_semaphores.empty();
}
SkiaOutputSurfaceImplOnGpu::PlaneAccessData::PlaneAccessData() = default;
@@ -838,8 +938,8 @@ bool SkiaOutputSurfaceImplOnGpu::ImportSurfacesForNV12Planes(
for (size_t i = 0; i < CopyOutputResult::kNV12MaxPlanes; ++i) {
const gpu::MailboxHolder& mailbox_holder = blit_request.mailbox(i);
- // Should never happen, maiboxes are validated when setting blit request on
- // a CopyOutputResult.
+ // Should never happen, mailboxes are validated when setting blit request on
+ // a CopyOutputResult and we only access `kNV12MaxPlanes` mailboxes.
DCHECK(!mailbox_holder.mailbox.IsZero());
PlaneAccessData& plane_data = plane_access_datas[i];
@@ -901,6 +1001,9 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12(
CopyOutputRequest::ResultDestination::kNativeTextures)
<< "Only CopyOutputRequests that hand out native textures support blit "
"requests!";
+ DCHECK(!request->has_blit_request() || request->has_result_selection())
+ << "Only CopyOutputRequests that specify result selection support blit "
+ "requests!";
// Overview:
// 1. Try to create surfaces for NV12 planes (we know the needed size in
@@ -917,22 +1020,39 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12(
// - pass ownership of the textures to the caller (native textures result)
// - schedule a read-back & expose its results to the caller (system memory
// result)
+ //
+ // Note: in case the blit request populates the GMBs, the flow stays the same,
+ // but we need to ensure that the results are only sent out after the
+ // GpuMemoryBuffer is safe to map into system memory.
// The size of the destination is passed in via `geometry.result_selection` -
// it already takes into account the rect of the render pass that is being
// copied, as well as area, scaling & result_selection of the `request`.
// This represents the size of the intermediate texture that will be then
// blitted to the destination textures.
- const gfx::Size destination_size = geometry.result_selection.size();
+ const gfx::Size intermediate_dst_size = geometry.result_selection.size();
std::array<PlaneAccessData, CopyOutputResult::kNV12MaxPlanes>
plane_access_datas;
SkYUVAInfo yuva_info;
- bool destination_surfaces_created = false;
+ bool destination_surfaces_ready = false;
if (request->has_blit_request()) {
- destination_surfaces_created = ImportSurfacesForNV12Planes(
+ if (request->result_selection().size() != intermediate_dst_size) {
+ DLOG(WARNING)
+ << __func__
+ << ": result selection is different than render pass output, "
+ "geometry="
+ << geometry.ToString() << ", request=" << request->ToString();
+ // Send empty result, we have a blit request that asks for a different
+ // size than what we have available - the behavior in this case is
+ // currently unspecified as we'd have to leave parts of the caller's
+ // region unpopulated.
+ return;
+ }
+
+ destination_surfaces_ready = ImportSurfacesForNV12Planes(
request->blit_request(), plane_access_datas);
// The entire destination image size is the same as the size of the luma
@@ -943,7 +1063,8 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12(
// Check if the destination will fit in the blit target:
const gfx::Rect blit_destination_rect(
- request->blit_request().destination_region_offset(), destination_size);
+ request->blit_request().destination_region_offset(),
+ intermediate_dst_size);
const gfx::Rect blit_target_image_rect(
gfx::SkISizeToSize(plane_access_datas[0].size));
@@ -953,24 +1074,25 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12(
return;
}
} else {
- yuva_info = SkYUVAInfo(
- gfx::SizeToSkISize(destination_size), SkYUVAInfo::PlaneConfig::kY_UV,
- SkYUVAInfo::Subsampling::k420, kRec709_Limited_SkYUVColorSpace);
+ yuva_info = SkYUVAInfo(gfx::SizeToSkISize(intermediate_dst_size),
+ SkYUVAInfo::PlaneConfig::kY_UV,
+ SkYUVAInfo::Subsampling::k420,
+ kRec709_Limited_SkYUVColorSpace);
- destination_surfaces_created =
+ destination_surfaces_ready =
CreateSurfacesForNV12Planes(yuva_info, color_space, plane_access_datas);
}
- if (!destination_surfaces_created) {
- DVLOG(1) << "failed to create destination surfaces";
+ if (!destination_surfaces_ready) {
+ DVLOG(1) << "failed to create / import destination surfaces";
// Send empty result.
return;
}
// Create a destination for the scaled & clipped result:
- auto representation = CreateSharedImageRepresentationSkia(
- ResourceFormat::RGBA_8888, destination_size, color_space);
- if (!representation) {
+ auto intermediate_representation = CreateSharedImageRepresentationSkia(
+ ResourceFormat::RGBA_8888, intermediate_dst_size, color_space);
+ if (!intermediate_representation) {
DVLOG(1) << "failed to create shared image representation for the "
"intermediate surface";
// Send empty result.
@@ -981,9 +1103,11 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12(
std::vector<GrBackendSemaphore> begin_semaphores;
std::vector<GrBackendSemaphore> end_semaphores;
- auto scoped_write = representation->BeginScopedWriteAccess(
- /*final_msaa_count=*/1, surface_props, &begin_semaphores, &end_semaphores,
- gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes);
+ auto intermediate_scoped_write =
+ intermediate_representation->BeginScopedWriteAccess(
+ /*final_msaa_count=*/1, surface_props, &begin_semaphores,
+ &end_semaphores,
+ gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes);
absl::optional<SkVector> scaling;
if (request->is_scaled()) {
@@ -993,24 +1117,22 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12(
request->scale_from().y());
}
- bool success = RenderSurface(
- surface, src_rect, scaling, is_downscale_or_identity_in_both_dimensions,
- scoped_write->surface(), begin_semaphores, end_semaphores);
- if (!success) {
- DLOG(ERROR) << "failed to render surface.";
- return;
- }
+ intermediate_scoped_write->surface()->wait(begin_semaphores.size(),
+ begin_semaphores.data());
- representation->SetCleared();
+ RenderSurface(surface, src_rect, scaling,
+ is_downscale_or_identity_in_both_dimensions,
+ intermediate_scoped_write->surface());
if (request->has_blit_request()) {
- BlendBitmapOverlays(scoped_write->surface()->getCanvas(),
+ BlendBitmapOverlays(intermediate_scoped_write->surface()->getCanvas(),
request->blit_request());
}
- auto source_image = scoped_write->surface()->makeImageSnapshot();
- if (!source_image) {
- DLOG(ERROR) << "failed to retrieve source_image.";
+ auto intermediate_image =
+ intermediate_scoped_write->surface()->makeImageSnapshot();
+ if (!intermediate_image) {
+ DLOG(ERROR) << "failed to retrieve `intermediate_image`.";
return;
}
@@ -1022,60 +1144,137 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutputNV12(
plane_access_datas[1].scoped_write->surface(), nullptr, nullptr};
// The region to be populated in caller's textures is derived from blit
- // request's |destination_region_offset|, and from COR's |result_selection|.
- // If we have a blit request, use it. Otherwise, use an
+ // request's |destination_region_offset()|, and from COR's
+ // |result_selection()|. If we have a blit request, use it. Otherwise, use an
// empty rect (which means that entire image will be used as the target of the
// blit - this will not result in rescaling since w/o blit request present,
- // the image size matches the |result_selection|).
- SkRect dst_region =
+ // the destination image size matches the |geometry.result_selection|).
+ const SkRect dst_region =
request->has_blit_request()
? gfx::RectToSkRect(
gfx::Rect(request->blit_request().destination_region_offset(),
- destination_size))
+ intermediate_dst_size))
: SkRect::MakeEmpty();
- skia::BlitRGBAToYUVA(source_image.get(), plane_surfaces.data(), yuva_info,
- dst_region);
+ // We should clear destination if BlitRequest asked to letterbox everything
+ // outside of intended destination region:
+ const bool clear_destination =
+ request->has_blit_request()
+ ? request->blit_request().letterboxing_behavior() ==
+ LetterboxingBehavior::kLetterbox
+ : false;
+ skia::BlitRGBAToYUVA(intermediate_image.get(), plane_surfaces.data(),
+ yuva_info, dst_region, clear_destination);
+
+ // Collect mailbox holders for the destination textures. They will be needed
+ // in case the result is kNativeTextures. It happens here in order to simplify
+ // the code in case we are populating the GpuMemoryBuffer-backed textures.
+ std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>
+ plane_mailbox_holders = {
+ gpu::MailboxHolder(plane_access_datas[0].mailbox, gpu::SyncToken(),
+ GL_TEXTURE_2D),
+ gpu::MailboxHolder(plane_access_datas[1].mailbox, gpu::SyncToken(),
+ GL_TEXTURE_2D),
+ gpu::MailboxHolder(),
+ };
+
+ // If we are not the ones allocating the textures, they may come from a GMB,
+ // in which case we need to delay sending the results until we receive a
+ // callback that the GPU work has completed - otherwise, memory-mapping the
+ // GMB may not yield the latest version of the contents.
+ const bool should_wait_for_gpu_work =
+ request->result_destination() ==
+ CopyOutputRequest::ResultDestination::kNativeTextures &&
+ request->has_blit_request() &&
+ request->blit_request().populates_gpu_memory_buffer();
+
+ scoped_refptr<NV12PlanesReadyContext> nv12_planes_ready = nullptr;
+ if (should_wait_for_gpu_work) {
+ // Prepare a per-CopyOutputRequest context that will be responsible for
+ // sending the CopyOutputResult:
+ nv12_planes_ready = base::MakeRefCounted<NV12PlanesReadyContext>(
+ weak_ptr_, std::move(request), geometry.result_selection,
+ plane_mailbox_holders, color_space);
+ }
+
+ bool should_submit = false;
for (size_t i = 0; i < CopyOutputResult::kNV12MaxPlanes; ++i) {
plane_access_datas[i].representation->SetCleared();
- GrFlushInfo flush_info;
- flush_info.fNumSemaphores = plane_access_datas[i].end_semaphores.size();
- flush_info.fSignalSemaphores = plane_access_datas[i].end_semaphores.data();
-
- gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_,
- &flush_info);
+ should_submit |= !plane_access_datas[i].end_semaphores.empty();
+
+ // Prepare a per-plane context that will notify the per-request context that
+ // GPU work that produces the contents of a plane that the GPU-side of the
+ // work has completed.
+ std::unique_ptr<NV12SinglePlaneReadyContext> nv12_plane_ready =
+ should_wait_for_gpu_work
+ ? std::make_unique<NV12SinglePlaneReadyContext>(nv12_planes_ready)
+ : nullptr;
+
+ if (should_wait_for_gpu_work) {
+ // Treat the fact that we're waiting for GPU work to finish the same way
+ // as a readback request. This would allow us to nudge Skia to fire the
+ // callbacks. See `SkiaOutputSurfaceImplOnGpu::CheckReadbackCompletion()`.
+ ++num_readbacks_pending_;
+ }
- auto flush_result = plane_surfaces[i]->flush(
- SkSurface::BackendSurfaceAccess::kNoAccess, flush_info);
- if (flush_result != GrSemaphoresSubmitted::kYes &&
- !(begin_semaphores.empty() && end_semaphores.empty())) {
+ if (!FlushSurface(
+ plane_surfaces[i], plane_access_datas[i].end_semaphores,
+ plane_access_datas[i].scoped_write->TakeEndState(),
+ should_wait_for_gpu_work
+ ? &NV12SinglePlaneReadyContext::OnNV12PlaneReady
+ : nullptr,
+ should_wait_for_gpu_work ? nv12_plane_ready.release() : nullptr)) {
// TODO(penghuang): handle vulkan device lost.
- FailedSkiaFlush("plane_surfaces[i]->flush()");
+ FailedSkiaFlush("CopyOutputNV12 plane_surfaces[i]->flush()");
return;
}
}
+ should_submit |= !end_semaphores.empty();
+
+ intermediate_representation->SetCleared();
+ if (!FlushSurface(intermediate_scoped_write->surface(), end_semaphores,
+ intermediate_scoped_write->TakeEndState())) {
+ // TODO(penghuang): handle vulkan device lost.
+ FailedSkiaFlush("CopyOutputNV12 dest_surface->flush()");
+ return;
+ }
+
+ if (should_submit && !gr_context()->submit()) {
+ DLOG(ERROR) << "CopyOutputNV12 gr_context->submit() failed";
+ return;
+ }
+
+ if (should_wait_for_gpu_work) {
+ // Flow will continue after GPU work is done - see
+ // `NV12PlanesReadyContext::OnNV12PlaneReady()` that eventually gets called.
+ return;
+ }
+
+ // We conditionally move from request (if `should_wait_for_gpu_work` is true),
+ // DCHECK that we don't accidentally enter this codepath after the request was
+ // moved from.
+ DCHECK(request);
+
switch (request->result_destination()) {
case CopyOutputRequest::ResultDestination::kNativeTextures: {
CopyOutputResult::ReleaseCallbacks release_callbacks;
- std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes> planes;
- for (size_t i = 0; i < CopyOutputResult::kNV12MaxPlanes; ++i) {
- if (!request->has_blit_request()) {
- // In blit requests, we are not responsible for releasing the textures
- // (the issuer of the request owns them), do not create the callbacks.
+ if (!request->has_blit_request()) {
+ // In blit requests, we are not responsible for releasing the textures
+ // (the issuer of the request owns them), create the callbacks only if
+ // we don't have blit request:
+ for (size_t i = 0; i < CopyOutputResult::kNV12MaxPlanes; ++i) {
release_callbacks.push_back(
CreateDestroyCopyOutputResourcesOnGpuThreadCallback(
std::move(plane_access_datas[i].representation)));
}
-
- planes[i].mailbox = plane_access_datas[i].mailbox;
}
request->SendResult(std::make_unique<CopyOutputTextureResult>(
CopyOutputResult::Format::NV12_PLANES, geometry.result_selection,
- CopyOutputResult::TextureResult(planes, color_space),
+ CopyOutputResult::TextureResult(plane_mailbox_holders, color_space),
std::move(release_callbacks)));
break;
@@ -1148,6 +1347,7 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput(
scoped_access;
std::vector<GrBackendSemaphore> begin_semaphores;
std::vector<GrBackendSemaphore> end_semaphores;
+ std::unique_ptr<GrBackendSurfaceMutableState> end_state;
if (from_framebuffer) {
surface = scoped_output_device_paint_->sk_surface();
} else {
@@ -1162,6 +1362,7 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput(
&end_semaphores,
gpu::SharedImageRepresentation::AllowUnclearedAccess::kNo);
surface = scoped_access->surface();
+ end_state = scoped_access->TakeEndState();
if (!begin_semaphores.empty()) {
auto result =
surface->wait(begin_semaphores.size(), begin_semaphores.data(),
@@ -1269,20 +1470,10 @@ void SkiaOutputSurfaceImplOnGpu::CopyOutput(
}
}
- if (!end_semaphores.empty()) {
- GrFlushInfo flush_info;
- flush_info.fNumSemaphores = end_semaphores.size();
- flush_info.fSignalSemaphores = end_semaphores.data();
- gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_,
- &flush_info);
- auto flush_result =
- surface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flush_info);
- if (flush_result != GrSemaphoresSubmitted::kYes &&
- !(begin_semaphores.empty() && end_semaphores.empty())) {
- // TODO(penghuang): handle vulkan device lost.
- FailedSkiaFlush("surface->flush() failed.");
- return;
- }
+ if (!FlushSurface(surface, end_semaphores, std::move(end_state))) {
+ // TODO(penghuang): handle vulkan device lost.
+ FailedSkiaFlush("surface->flush() failed.");
+ return;
}
ScheduleCheckReadbackCompletion();
@@ -1326,8 +1517,9 @@ void SkiaOutputSurfaceImplOnGpu::BeginAccessImages(
context->BeginAccessIfNecessary(
context_state_.get(), shared_image_representation_factory_.get(),
dependency_->GetMailboxManager(), begin_semaphores, end_semaphores);
- if (context->end_access_state())
- image_contexts_with_end_access_state_.emplace(context);
+ if (auto end_state = context->TakeAccessEndState())
+ image_contexts_with_end_access_state_.emplace(context,
+ std::move(end_state));
// Texture parameters can be modified by concurrent reads so reset them
// before compositing from the texture. See https://crbug.com/1092080.
@@ -1342,11 +1534,10 @@ void SkiaOutputSurfaceImplOnGpu::BeginAccessImages(
}
void SkiaOutputSurfaceImplOnGpu::ResetStateOfImages() {
- for (auto* context : image_contexts_with_end_access_state_) {
- DCHECK(context->end_access_state());
+ for (auto& context : image_contexts_with_end_access_state_) {
if (!gr_context()->setBackendTextureState(
- context->promise_image_texture()->backendTexture(),
- *context->end_access_state())) {
+ context.first->promise_image_texture()->backendTexture(),
+ *context.second)) {
DLOG(ERROR) << "setBackendTextureState() failed.";
}
}
@@ -1382,73 +1573,21 @@ void SkiaOutputSurfaceImplOnGpu::ReleaseImageContexts(
}
void SkiaOutputSurfaceImplOnGpu::ScheduleOverlays(
- SkiaOutputSurface::OverlayList overlays,
- std::vector<ImageContextImpl*> image_contexts,
- base::OnceClosure on_finished) {
+ SkiaOutputSurface::OverlayList overlays) {
TRACE_EVENT1("viz", "SkiaOutputSurfaceImplOnGpu::ScheduleOverlays",
"num_overlays", overlays.size());
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
- if (context_is_lost_)
- return;
-
- bool have_image_contexts = !image_contexts.empty();
- if (have_image_contexts) {
- DCHECK(context_state_->GrContextIsGL());
-
- // GL doesn't use semaphores.
- promise_image_access_helper_.BeginAccess(std::move(image_contexts),
- /*begin_semaphores=*/nullptr,
- /*end_semaphores=*/nullptr);
- }
-
- using ScopedWriteAccess =
- std::unique_ptr<gpu::SharedImageRepresentationSkia::ScopedWriteAccess>;
- std::vector<ScopedWriteAccess> scoped_write_accesses;
- for (auto& overlay : overlays) {
- if (!overlay.ddl)
- continue;
- const auto& characterization = overlay.ddl->characterization();
- auto backing = GetOrCreateRenderPassOverlayBacking(characterization);
- if (!backing)
- break;
- DCHECK(overlay.mailbox.IsZero());
- overlay.mailbox = backing->mailbox();
- auto scoped_access = backing->BeginScopedWriteAccess(
- characterization.sampleCount(), characterization.surfaceProps(),
- /*begin_semaphores=*/nullptr,
- /*end_semaphores=*/nullptr,
- gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes);
- bool result = scoped_access->surface()->draw(overlay.ddl);
- DCHECK(result);
- scoped_write_accesses.push_back(std::move(scoped_access));
- backing->SetCleared();
- in_flight_render_pass_overlay_backings_.insert(std::move(backing));
- }
-
- if (!scoped_write_accesses.empty()) {
- absl::optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use;
- if (dependency_->GetGrShaderCache()) {
- cache_use.emplace(dependency_->GetGrShaderCache(),
- gpu::kDisplayCompositorClientId);
- }
-
- GrFlushInfo flush_info = {};
- if (on_finished)
- gpu::AddCleanupTaskForSkiaFlush(std::move(on_finished), &flush_info);
- context_state_->gr_context()->flush(flush_info);
- context_state_->gr_context()->submit();
- scoped_write_accesses.clear();
- }
-
- if (have_image_contexts)
- promise_image_access_helper_.EndAccess();
+ constexpr base::TimeDelta kHistogramMinTime = base::Microseconds(5);
+ constexpr base::TimeDelta kHistogramMaxTime = base::Milliseconds(16);
+ constexpr int kHistogramTimeBuckets = 50;
+ base::TimeTicks start_time = base::TimeTicks::Now();
output_device_->ScheduleOverlays(std::move(overlays));
-#else
- DCHECK(image_contexts.empty());
- output_device_->ScheduleOverlays(std::move(overlays));
-#endif
+
+ UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+ "Gpu.OutputSurface.ScheduleOverlaysUs",
+ base::TimeTicks::Now() - start_time, kHistogramMinTime, kHistogramMaxTime,
+ kHistogramTimeBuckets);
}
void SkiaOutputSurfaceImplOnGpu::SetEnableDCLayers(bool enable) {
@@ -1769,17 +1908,6 @@ void SkiaOutputSurfaceImplOnGpu::SwapBuffersInternal(
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(output_device_);
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
- // Release any backings which are not reused by the current frame, probably
- // because the properties of render passes are changed or render passes are
- // removed
- if (context_is_lost_) {
- for (auto& image : available_render_pass_overlay_backings_)
- image->OnContextLost();
- }
- available_render_pass_overlay_backings_.clear();
-#endif
-
if (context_is_lost_)
return;
@@ -1806,6 +1934,20 @@ void SkiaOutputSurfaceImplOnGpu::PostSubmit(
promise_image_access_helper_.EndAccess();
scoped_output_device_paint_.reset();
+#if BUILDFLAG(ENABLE_VULKAN)
+ while (!pending_release_fence_cbs_.empty()) {
+ auto& item = pending_release_fence_cbs_.front();
+ auto release_fence = CreateReleaseFenceForVulkan(item.first);
+ if (release_fence.is_null())
+ LOG(ERROR) << "Unable to create a release fence for Vulkan.";
+ PostTaskToClientThread(
+ base::BindOnce(std::move(item.second), std::move(release_fence)));
+ pending_release_fence_cbs_.pop_front();
+ }
+#else
+ DCHECK(pending_release_fence_cbs_.empty());
+#endif
+
if (frame) {
if (waiting_for_full_damage_) {
// If we're using partial swap, we need to check whether the sub-buffer
@@ -1901,17 +2043,6 @@ base::TimeDelta SkiaOutputSurfaceImplOnGpu::GetGpuBlockedTimeSinceLastSwap() {
return dependency_->GetGpuBlockedTimeSinceLastSwap();
}
-void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersComplete(
- gpu::SwapBuffersCompleteParams params,
- gfx::GpuFenceHandle release_fence) {
- // Handled by SkiaOutputDevice already.
-}
-
-void SkiaOutputSurfaceImplOnGpu::BufferPresented(
- const gfx::PresentationFeedback& feedback) {
- // Handled by SkiaOutputDevice already.
-}
-
void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersCompleteInternal(
gpu::SwapBuffersCompleteParams params,
const gfx::Size& pixel_size,
@@ -1935,36 +2066,6 @@ void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersCompleteInternal(
waiting_for_full_damage_ = true;
}
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
- // |available_render_pass_overlay_backings_| are used or released in
- // SwapBuffers() for every frames. For Ozone-Wayland
- // |available_render_pass_overlay_backings_| is not always empty because the
- // buffer management in 'GbmSurfacelessWayland::SwapBuffersAsync' will cause
- // an accumulation of unsubmitted frames in |unsubmitted_frames_|. These are
- // submitted in 'GbmSurfacelessWayland::MaybeSubmitFrames'. Later
- // 'GbmSurfacelessWayland::OnSubmission' will then call the callback of these
- // |submitted_frames| more than once. This means that this function
- // 'DidSwapBuffersCompleteInternal' will get executed again before the
- // corresponding 'SkiaOutputSurfaceImplOnGpu::SwapBuffersInternal' has cleared
- // 'available_render_pass_overlay_backings_'.
-#if !defined(USE_OZONE)
- DCHECK(available_render_pass_overlay_backings_.empty());
-#endif
- // Erase mailboxes of render pass overlays from |params.released_overlays| and
- // move released backings for those render pass overlays from
- // |in_flight_render_pass_overlay_backings_| to
- // |available_render_pass_overlay_backings_| for reusing.
- base::EraseIf(params.released_overlays, [this](const gpu::Mailbox& mailbox) {
- auto it = in_flight_render_pass_overlay_backings_.find(mailbox);
- if (it == in_flight_render_pass_overlay_backings_.end())
- return false;
- available_render_pass_overlay_backings_.push_back(std::move(*it));
- in_flight_render_pass_overlay_backings_.erase(it);
- return true;
- });
-
-#endif
-
PostTaskToClientThread(base::BindOnce(did_swap_buffer_complete_callback_,
params, pixel_size,
std::move(release_fence)));
@@ -2024,76 +2125,6 @@ void SkiaOutputSurfaceImplOnGpu::PreserveChildSurfaceControls() {
gl_surface_->PreserveChildSurfaceControls();
}
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
-std::unique_ptr<gpu::SharedImageRepresentationSkia>
-SkiaOutputSurfaceImplOnGpu::GetOrCreateRenderPassOverlayBacking(
- const SkSurfaceCharacterization& characterization) {
- ResourceFormat resource_format;
- switch (characterization.colorType()) {
- case kRGBA_8888_SkColorType:
- resource_format = ResourceFormat::RGBA_8888;
- break;
- case kBGRA_8888_SkColorType:
- resource_format = ResourceFormat::BGRA_8888;
- break;
- case kRGBA_F16_SkColorType:
- resource_format = ResourceFormat::RGBA_F16;
- break;
- default:
- resource_format = ResourceFormat::RGBA_8888;
- NOTREACHED();
- }
-
- gfx::Size size(characterization.width(), characterization.height());
- gfx::ColorSpace color_space;
- if (characterization.colorSpace())
- color_space = gfx::ColorSpace(*characterization.colorSpace());
- auto it = std::find_if(
- available_render_pass_overlay_backings_.begin(),
- available_render_pass_overlay_backings_.end(),
- [&characterization, &resource_format, &size, &color_space](
- const std::unique_ptr<gpu::SharedImageRepresentationSkia>& backing) {
- return backing->format() == resource_format &&
- backing->size() == size &&
- backing->color_space() == color_space &&
- backing->surface_origin() == characterization.origin() &&
- backing->alpha_type() ==
- characterization.imageInfo().alphaType();
- });
-
- if (it != available_render_pass_overlay_backings_.end()) {
- auto backing = std::move(*it);
- available_render_pass_overlay_backings_.erase(it);
- return backing;
- }
-
- auto mailbox = gpu::Mailbox::GenerateForSharedImage();
- constexpr auto kOverlayUsage = gpu::SHARED_IMAGE_USAGE_SCANOUT |
- gpu::SHARED_IMAGE_USAGE_DISPLAY |
- gpu::SHARED_IMAGE_USAGE_RASTER;
-
- bool result = shared_image_factory_->CreateSharedImage(
- mailbox, resource_format, size, color_space, characterization.origin(),
- characterization.imageInfo().alphaType(), gpu::kNullSurfaceHandle,
- kOverlayUsage);
- if (!result) {
- LOG(ERROR) << "CreateSharedImage() failed.";
- MarkContextLost(CONTEXT_LOST_OUT_OF_MEMORY);
- return nullptr;
- }
-
- auto backing = shared_image_representation_factory_->ProduceSkia(
- mailbox, context_state_.get());
- DCHECK(backing);
-
- // The |backing| will keep a ref on the shared image, so the image will not be
- // released until |backing| is released.
- shared_image_factory_->DestroySharedImage(mailbox);
-
- return backing;
-}
-#endif // BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
-
void SkiaOutputSurfaceImplOnGpu::InitDelegatedInkPointRendererReceiver(
mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer>
pending_receiver) {
@@ -2138,4 +2169,57 @@ void SkiaOutputSurfaceImplOnGpu::DiscardBackbuffer() {
output_device_->DiscardBackbuffer();
}
+#if BUILDFLAG(ENABLE_VULKAN)
+gfx::GpuFenceHandle SkiaOutputSurfaceImplOnGpu::CreateReleaseFenceForVulkan(
+ const GrBackendSemaphore& semaphore) {
+ DCHECK(is_using_vulkan());
+
+ if (semaphore.vkSemaphore() == VK_NULL_HANDLE)
+ return {};
+
+ auto* implementation = vulkan_context_provider_->GetVulkanImplementation();
+ VkDevice device =
+ vulkan_context_provider_->GetDeviceQueue()->GetVulkanDevice();
+
+ auto handle =
+ implementation->GetSemaphoreHandle(device, semaphore.vkSemaphore());
+ if (!handle.is_valid()) {
+ vkDestroySemaphore(device, semaphore.vkSemaphore(),
+ /*pAllocator=*/nullptr);
+ LOG(ERROR) << "Failed to create a release fence for Vulkan.";
+ return {};
+ }
+ return std::move(handle).ToGpuFenceHandle();
+}
+
+bool SkiaOutputSurfaceImplOnGpu::CreateAndStoreExternalSemaphoreVulkan(
+ std::vector<GrBackendSemaphore>& end_semaphores) {
+ DCHECK(is_using_vulkan());
+
+ auto* implementation = vulkan_context_provider_->GetVulkanImplementation();
+ VkDevice device =
+ vulkan_context_provider_->GetDeviceQueue()->GetVulkanDevice();
+
+ VkSemaphore semaphore = implementation->CreateExternalSemaphore(device);
+ if (semaphore == VK_NULL_HANDLE) {
+ LOG(ERROR)
+ << "Creation of an external semaphore for a release fence failed.";
+ return false;
+ }
+
+ end_semaphores.emplace_back();
+ end_semaphores.back().initVulkan(semaphore);
+ return true;
+}
+#endif
+
+gfx::GpuFenceHandle SkiaOutputSurfaceImplOnGpu::CreateReleaseFenceForGL() {
+ if (gl::GLFence::IsGpuFenceSupported()) {
+ auto fence = gl::GLFence::CreateForGpuFence();
+ if (fence)
+ return fence->GetGpuFence()->GetGpuFenceHandle().Clone();
+ }
+ return {};
+}
+
} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index 20fd2f418d8..204eebdc080 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_ON_GPU_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_ON_GPU_H_
+#include <deque>
#include <map>
#include <memory>
#include <utility>
@@ -41,6 +42,8 @@
#include "third_party/skia/include/core/SkPromiseImageTexture.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "third_party/skia/include/gpu/GrTypes.h"
+#include "ui/gfx/gpu_fence_handle.h"
namespace gfx {
namespace mojom {
@@ -137,12 +140,14 @@ class SkiaOutputSurfaceImplOnGpu
const gfx::ColorSpace& color_space,
float device_scale_factor,
gfx::OverlayTransform transform);
- void FinishPaintCurrentFrame(sk_sp<SkDeferredDisplayList> ddl,
- sk_sp<SkDeferredDisplayList> overdraw_ddl,
- std::vector<ImageContextImpl*> image_contexts,
- std::vector<gpu::SyncToken> sync_tokens,
- base::OnceClosure on_finished,
- absl::optional<gfx::Rect> draw_rectangle);
+ void FinishPaintCurrentFrame(
+ sk_sp<SkDeferredDisplayList> ddl,
+ sk_sp<SkDeferredDisplayList> overdraw_ddl,
+ std::vector<ImageContextImpl*> image_contexts,
+ std::vector<gpu::SyncToken> sync_tokens,
+ base::OnceClosure on_finished,
+ base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb,
+ absl::optional<gfx::Rect> draw_rectangle);
void ScheduleOutputSurfaceAsOverlay(
const OverlayProcessorInterface::OutputSurfaceOverlayPlane&
output_surface_plane);
@@ -157,11 +162,13 @@ class SkiaOutputSurfaceImplOnGpu
void SwapBuffersSkipped();
void EnsureBackbuffer();
void DiscardBackbuffer();
- void FinishPaintRenderPass(const gpu::Mailbox& mailbox,
- sk_sp<SkDeferredDisplayList> ddl,
- std::vector<ImageContextImpl*> image_contexts,
- std::vector<gpu::SyncToken> sync_tokens,
- base::OnceClosure on_finished);
+ void FinishPaintRenderPass(
+ const gpu::Mailbox& mailbox,
+ sk_sp<SkDeferredDisplayList> ddl,
+ std::vector<ImageContextImpl*> image_contexts,
+ std::vector<gpu::SyncToken> sync_tokens,
+ base::OnceClosure on_finished,
+ base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb);
// Deletes resources for RenderPasses in |ids|. Also takes ownership of
// |images_contexts| and destroys them on GPU thread.
void RemoveRenderPassResource(
@@ -184,9 +191,7 @@ class SkiaOutputSurfaceImplOnGpu
void ReleaseImageContexts(
std::vector<std::unique_ptr<ExternalUseClient::ImageContext>>
image_contexts);
- void ScheduleOverlays(SkiaOutputSurface::OverlayList overlays,
- std::vector<ImageContextImpl*> image_contexts,
- base::OnceClosure on_finished);
+ void ScheduleOverlays(SkiaOutputSurface::OverlayList overlays);
void SetEnableDCLayers(bool enable);
void SetGpuVSyncEnabled(bool enabled);
@@ -210,9 +215,6 @@ class SkiaOutputSurfaceImplOnGpu
#endif
const gpu::gles2::FeatureInfo* GetFeatureInfo() const override;
const gpu::GpuPreferences& GetGpuPreferences() const override;
- void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params,
- gfx::GpuFenceHandle release_fence) override;
- void BufferPresented(const gfx::PresentationFeedback& feedback) override;
GpuVSyncCallback GetGpuVSyncCallback() override;
base::TimeDelta GetGpuBlockedTimeSinceLastSwap() override;
@@ -301,6 +303,8 @@ class SkiaOutputSurfaceImplOnGpu
gpu_preferences_.gr_context_type == gpu::GrContextType::kDawn;
}
+ bool is_using_gl() const { return !is_using_vulkan() && !is_using_dawn(); }
+
// Helper for `CopyOutput()` method, handles the RGBA format.
void CopyOutputRGBA(SkSurface* surface,
copy_output::RenderPassGeometry geometry,
@@ -336,17 +340,19 @@ class SkiaOutputSurfaceImplOnGpu
// |surface| into |dest_surface|'s canvas, cropping and scaling the results
// appropriately. |source_selection| is the area of the |surface| that will be
// rendered to the destination.
- // |begin_semaphores| will be submitted to the GPU backend prior to issuing
- // draw calls to the |dest_surface|.
- // |end_semaphores| will be submitted to the GPU backend alongside the draw
- // calls to the |dest_surface|.
- bool RenderSurface(SkSurface* surface,
+ void RenderSurface(SkSurface* surface,
const SkIRect& source_selection,
absl::optional<SkVector> scaling,
bool is_downscale_or_identity_in_both_dimensions,
- SkSurface* dest_surface,
- std::vector<GrBackendSemaphore>& begin_semaphores,
- std::vector<GrBackendSemaphore>& end_semaphores);
+ SkSurface* dest_surface);
+
+ // Helper for `CopyOutputNV12()` & `CopyOutputRGBA()` methods, flushes writes
+ // to |surface| with |end_semaphores| and |end_state|.
+ bool FlushSurface(SkSurface* surface,
+ std::vector<GrBackendSemaphore>& end_semaphores,
+ std::unique_ptr<GrBackendSurfaceMutableState> end_state,
+ GrGpuFinishedProc finished_proc = nullptr,
+ GrGpuFinishedContext finished_context = nullptr);
// Creates surfaces needed to store the data in NV12 format.
// |plane_access_datas| will be populated with information needed to access
@@ -382,11 +388,18 @@ class SkiaOutputSurfaceImplOnGpu
void ReleaseAsyncReadResultHelpers();
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
- std::unique_ptr<gpu::SharedImageRepresentationSkia>
- GetOrCreateRenderPassOverlayBacking(
- const SkSurfaceCharacterization& characterization);
+#if BUILDFLAG(ENABLE_VULKAN)
+ // Creates a release fence. The semaphore is an external semaphore created
+ // by CreateAndStoreExternalSemaphoreVulkan(). May destroy VkSemaphore that
+ // the |semaphore| stores if creation of a release fence fails. In this case,
+ // invalid fence handle is returned.
+ gfx::GpuFenceHandle CreateReleaseFenceForVulkan(
+ const GrBackendSemaphore& semaphore);
+ // Returns true if succeess.
+ bool CreateAndStoreExternalSemaphoreVulkan(
+ std::vector<GrBackendSemaphore>& end_semaphores);
#endif
+ gfx::GpuFenceHandle CreateReleaseFenceForGL();
class ReleaseCurrent {
public:
@@ -466,7 +479,9 @@ class SkiaOutputSurfaceImplOnGpu
base::flat_set<ImageContextImpl*> image_contexts_;
};
PromiseImageAccessHelper promise_image_access_helper_{this};
- base::flat_set<ImageContextImpl*> image_contexts_with_end_access_state_;
+ base::flat_set<std::pair<ImageContextImpl*,
+ std::unique_ptr<GrBackendSurfaceMutableState>>>
+ image_contexts_with_end_access_state_;
std::unique_ptr<SkiaOutputDevice> output_device_;
std::unique_ptr<SkiaOutputDevice::ScopedPaint> scoped_output_device_paint_;
@@ -488,33 +503,12 @@ class SkiaOutputSurfaceImplOnGpu
// Tracking for ongoing AsyncReadResults.
base::flat_set<AsyncReadResultHelper*> async_read_result_helpers_;
-#if BUILDFLAG(IS_APPLE) || defined(USE_OZONE)
- using UniqueBackingPtr = std::unique_ptr<gpu::SharedImageRepresentationSkia>;
- class BackingComparator {
- public:
- using is_transparent = void;
- bool operator()(const UniqueBackingPtr& lhs,
- const UniqueBackingPtr& rhs) const {
- return lhs->mailbox() < rhs->mailbox();
- }
- bool operator()(const UniqueBackingPtr& lhs,
- const gpu::Mailbox& rhs) const {
- return lhs->mailbox() < rhs;
- }
- bool operator()(const gpu::Mailbox& lhs,
- const UniqueBackingPtr& rhs) const {
- return lhs < rhs->mailbox();
- }
- };
- // Render pass overlay backings are in flight.
- // The base::flat_set uses backing->mailbox() as the unique key.
- base::flat_set<UniqueBackingPtr, BackingComparator>
- in_flight_render_pass_overlay_backings_;
-
- // Render pass overlay backings are available for reusing.
- std::vector<std::unique_ptr<gpu::SharedImageRepresentationSkia>>
- available_render_pass_overlay_backings_;
-#endif
+ // Pending release fence callbacks. These callbacks can be delayed if Vulkan
+ // external semaphore type has copy transference, which means importing
+ // semaphores has to be delayed until submission.
+ std::deque<std::pair<GrBackendSemaphore,
+ base::OnceCallback<void(gfx::GpuFenceHandle)>>>
+ pending_release_fence_cbs_;
THREAD_CHECKER(thread_checker_);
diff --git a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
index 88dd8eab89f..8d694407870 100644
--- a/chromium/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
+++ b/chromium/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/callback_forward.h"
#include "base/callback_helpers.h"
#include "base/run_loop.h"
#include "cc/test/fake_output_surface_client.h"
@@ -23,6 +24,7 @@
#include "gpu/command_buffer/service/service_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/gpu_fence_handle.h"
#include "ui/gl/gl_implementation.h"
namespace viz {
@@ -45,8 +47,10 @@ class SkiaOutputSurfaceImplTest : public testing::Test {
void SetUpSkiaOutputSurfaceImpl();
// Paints and submits root RenderPass with a solid color rect of |size|.
- gpu::SyncToken PaintRootRenderPass(const gfx::Rect& output_rect,
- base::OnceClosure closure);
+ gpu::SyncToken PaintRootRenderPass(
+ const gfx::Rect& output_rect,
+ base::OnceClosure closure,
+ base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence);
void CheckSyncTokenOnGpuThread(const gpu::SyncToken& sync_token);
void CopyRequestCallbackOnGpuThread(const gfx::Rect& output_rect,
@@ -75,27 +79,27 @@ SkiaOutputSurfaceImplTest::~SkiaOutputSurfaceImplTest() {
}
void SkiaOutputSurfaceImplTest::SetUpSkiaOutputSurfaceImpl() {
- RendererSettings settings;
- settings.use_skia_renderer = true;
auto skia_deps = std::make_unique<SkiaOutputSurfaceDependencyImpl>(
GetGpuService(), gpu::kNullSurfaceHandle);
display_controller_ =
std::make_unique<DisplayCompositorMemoryAndTaskController>(
std::move(skia_deps));
- output_surface_ = SkiaOutputSurfaceImpl::Create(display_controller_.get(),
- settings, &debug_settings_);
+ output_surface_ = SkiaOutputSurfaceImpl::Create(
+ display_controller_.get(), RendererSettings(), &debug_settings_);
output_surface_->BindToClient(&output_surface_client_);
}
gpu::SyncToken SkiaOutputSurfaceImplTest::PaintRootRenderPass(
const gfx::Rect& rect,
- base::OnceClosure closure) {
+ base::OnceClosure closure,
+ base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence) {
SkPaint paint;
paint.setColor(kOutputColor);
SkCanvas* root_canvas = output_surface_->BeginPaintCurrentFrame();
root_canvas->drawRect(
SkRect::MakeXYWH(rect.x(), rect.y(), rect.height(), rect.width()), paint);
- output_surface_->EndPaint(std::move(closure));
+ output_surface_->EndPaint(std::move(closure),
+ std::move(return_release_fence));
return output_surface_->Flush();
}
@@ -136,16 +140,22 @@ void SkiaOutputSurfaceImplTest::CopyRequestCallbackOnGpuThread(
}
TEST_F(SkiaOutputSurfaceImplTest, EndPaint) {
- output_surface_->Reshape(kSurfaceRect.size(), 1, gfx::ColorSpace(),
- gfx::BufferFormat::RGBX_8888, /*use_stencil=*/false);
+ OutputSurface::ReshapeParams reshape_params;
+ reshape_params.size = kSurfaceRect.size();
+ output_surface_->Reshape(reshape_params);
constexpr gfx::Rect output_rect(0, 0, 10, 10);
bool on_finished_called = false;
base::OnceClosure on_finished =
base::BindOnce([](bool* result) { *result = true; }, &on_finished_called);
+ bool on_return_release_fence_called = false;
+ base::OnceCallback<void(gfx::GpuFenceHandle)> return_release_fence_cb =
+ base::BindOnce(
+ [](bool* result, gfx::GpuFenceHandle handle) { *result = true; },
+ &on_return_release_fence_called);
- gpu::SyncToken sync_token =
- PaintRootRenderPass(output_rect, std::move(on_finished));
+ gpu::SyncToken sync_token = PaintRootRenderPass(
+ output_rect, std::move(on_finished), std::move(return_release_fence_cb));
EXPECT_TRUE(sync_token.HasData());
// Copy the output
@@ -177,19 +187,25 @@ TEST_F(SkiaOutputSurfaceImplTest, EndPaint) {
output_surface_->ScheduleGpuTaskForTesting(std::move(closure), {sync_token});
BlockMainThread();
EXPECT_TRUE(on_finished_called);
+
+ // Let the cb to come back.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(on_return_release_fence_called);
}
// Draws two frames and calls Reshape() between the two frames changing the
// color space. Verifies draw after color space change is successful.
TEST_F(SkiaOutputSurfaceImplTest, SupportsColorSpaceChange) {
for (auto& color_space : {gfx::ColorSpace(), gfx::ColorSpace::CreateSRGB()}) {
- output_surface_->Reshape(kSurfaceRect.size(), 1, color_space,
- gfx::BufferFormat::RGBX_8888,
- /*use_stencil=*/false);
+ OutputSurface::ReshapeParams reshape_params;
+ reshape_params.size = kSurfaceRect.size();
+ reshape_params.color_space = color_space;
+ output_surface_->Reshape(reshape_params);
// Draw something, it's not important what.
base::RunLoop run_loop;
- PaintRootRenderPass(kSurfaceRect, run_loop.QuitClosure());
+ PaintRootRenderPass(kSurfaceRect, run_loop.QuitClosure(),
+ base::DoNothing());
OutputSurfaceFrame frame;
frame.size = kSurfaceRect.size();
@@ -203,8 +219,9 @@ TEST_F(SkiaOutputSurfaceImplTest, SupportsColorSpaceChange) {
// Tests that the destination color space is preserved across a CopyOutput for
// ColorSpaces supported by SkColorSpace.
TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapSupportedColorSpace) {
- output_surface_->Reshape(kSurfaceRect.size(), 1, gfx::ColorSpace(),
- gfx::BufferFormat::RGBX_8888, /*use_stencil=*/false);
+ OutputSurface::ReshapeParams reshape_params;
+ reshape_params.size = kSurfaceRect.size();
+ output_surface_->Reshape(reshape_params);
constexpr gfx::Rect output_rect(0, 0, 10, 10);
const gfx::ColorSpace color_space = gfx::ColorSpace(
@@ -230,7 +247,7 @@ TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapSupportedColorSpace) {
geometry.sampling_bounds = output_rect;
geometry.readback_offset = gfx::Vector2d(0, 0);
- PaintRootRenderPass(kSurfaceRect, base::DoNothing());
+ PaintRootRenderPass(kSurfaceRect, base::DoNothing(), base::DoNothing());
output_surface_->CopyOutput(AggregatedRenderPassId{0}, geometry, color_space,
std::move(request), gpu::Mailbox());
output_surface_->SwapBuffersSkipped(kSurfaceRect);
@@ -243,8 +260,9 @@ TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapSupportedColorSpace) {
// Tests that copying from a source with a color space that can't be converted
// to a SkColorSpace will fallback to a transform to sRGB.
TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapUnsupportedColorSpace) {
- output_surface_->Reshape(kSurfaceRect.size(), 1, gfx::ColorSpace(),
- gfx::BufferFormat::RGBX_8888, /*use_stencil=*/false);
+ OutputSurface::ReshapeParams reshape_params;
+ reshape_params.size = kSurfaceRect.size();
+ output_surface_->Reshape(reshape_params);
constexpr gfx::Rect output_rect(0, 0, 10, 10);
const gfx::ColorSpace color_space = gfx::ColorSpace::CreatePiecewiseHDR(
@@ -270,7 +288,7 @@ TEST_F(SkiaOutputSurfaceImplTest, CopyOutputBitmapUnsupportedColorSpace) {
geometry.sampling_bounds = output_rect;
geometry.readback_offset = gfx::Vector2d(0, 0);
- PaintRootRenderPass(kSurfaceRect, base::DoNothing());
+ PaintRootRenderPass(kSurfaceRect, base::DoNothing(), base::DoNothing());
output_surface_->CopyOutput(AggregatedRenderPassId{0}, geometry, color_space,
std::move(request), gpu::Mailbox());
output_surface_->SwapBuffersSkipped(kSurfaceRect);
diff --git a/chromium/components/viz/service/display_embedder/skia_render_copy_results.cc b/chromium/components/viz/service/display_embedder/skia_render_copy_results.cc
index 7c400b6d1bb..20541a6144a 100644
--- a/chromium/components/viz/service/display_embedder/skia_render_copy_results.cc
+++ b/chromium/components/viz/service/display_embedder/skia_render_copy_results.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "base/memory/ptr_util.h"
+#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h"
#include "third_party/libyuv/include/libyuv/planar_functions.h"
#include "third_party/skia/include/core/SkPixelRef.h"
@@ -322,4 +323,49 @@ void CopyOutputResultSkiaNV12::OnNV12PlaneReadbackDone(
std::move(async_result));
}
+NV12PlanesReadyContext::NV12PlanesReadyContext(
+ base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu,
+ std::unique_ptr<CopyOutputRequest> request,
+ const gfx::Rect& result_rect,
+ const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>&
+ plane_mailbox_holders,
+ const gfx::ColorSpace& color_space)
+ : request_(std::move(request)),
+ result_rect_(result_rect),
+ plane_mailbox_holders_(plane_mailbox_holders),
+ color_space_(color_space) {}
+
+NV12PlanesReadyContext::~NV12PlanesReadyContext() {
+ DCHECK_EQ(outstanding_planes_, 0);
+}
+
+void NV12PlanesReadyContext::OnNV12PlaneReady() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (impl_on_gpu_) {
+ impl_on_gpu_->ReadbackDone();
+ }
+
+ outstanding_planes_--;
+ if (outstanding_planes_ == 0) {
+ request_->SendResult(std::make_unique<CopyOutputTextureResult>(
+ CopyOutputResult::Format::NV12_PLANES, result_rect_,
+ CopyOutputResult::TextureResult(plane_mailbox_holders_, color_space_),
+ CopyOutputResult::ReleaseCallbacks()));
+ }
+}
+
+NV12SinglePlaneReadyContext::NV12SinglePlaneReadyContext(
+ scoped_refptr<NV12PlanesReadyContext> nv12_planes_flushed)
+ : nv12_planes_flushed(nv12_planes_flushed) {}
+
+NV12SinglePlaneReadyContext::~NV12SinglePlaneReadyContext() = default;
+
+// static
+void NV12SinglePlaneReadyContext::OnNV12PlaneReady(GrGpuFinishedContext c) {
+ auto context = base::WrapUnique(static_cast<NV12SinglePlaneReadyContext*>(c));
+
+ context->nv12_planes_flushed->OnNV12PlaneReady();
+}
+
} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/skia_render_copy_results.h b/chromium/components/viz/service/display_embedder/skia_render_copy_results.h
index f305a081177..5c056688d2f 100644
--- a/chromium/components/viz/service/display_embedder/skia_render_copy_results.h
+++ b/chromium/components/viz/service/display_embedder/skia_render_copy_results.h
@@ -16,6 +16,8 @@
#include "third_party/libyuv/include/libyuv/planar_functions.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/GrTypes.h"
+#include "ui/gfx/color_space.h"
namespace viz {
@@ -174,6 +176,70 @@ struct NV12PlanePixelReadContext {
int plane_index;
};
+// Context that is responsible for sending a CopyOutputResult once the GPU work
+// that populates the GpuMemoryBuffer for the NV12 planes has completed. It will
+// be notified by multiple `NV12SinglePlaneReadyContext`s that the plane has
+// been populated, and once all planes have been completed, it will send the
+// CopyOutputResult.
+class NV12PlanesReadyContext : public base::RefCounted<NV12PlanesReadyContext> {
+ public:
+ NV12PlanesReadyContext(
+ base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu,
+ std::unique_ptr<CopyOutputRequest> request,
+ const gfx::Rect& result_rect,
+ const std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>&
+ plane_mailbox_holders,
+ const gfx::ColorSpace& color_space);
+
+ NV12PlanesReadyContext(const NV12PlanesReadyContext& other) = delete;
+ NV12PlanesReadyContext& operator=(const NV12PlanesReadyContext& other) =
+ delete;
+
+ void OnNV12PlaneReady();
+
+ private:
+ friend class base::RefCounted<NV12PlanesReadyContext>;
+ ~NV12PlanesReadyContext();
+
+ // Needed to notify `SkiaOutputSurfaceImplOnGpu` that readback has completed.
+ // GPU work is not a readback, but we rely on the same mechanism for nudging
+ // Skia to periodically check for asynchronous event completion.
+ base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu_;
+ // Request that we will send a response to:
+ std::unique_ptr<CopyOutputRequest> request_;
+
+ // Data needed to create a response to `request_`:
+ gfx::Rect result_rect_;
+ std::array<gpu::MailboxHolder, CopyOutputResult::kMaxPlanes>
+ plane_mailbox_holders_;
+ gfx::ColorSpace color_space_;
+
+ // Number of planes that still need to report completion:
+ int outstanding_planes_ = CopyOutputResult::kNV12MaxPlanes;
+
+ THREAD_CHECKER(thread_checker_);
+};
+
+// Context that is responsible for notifying `NV12PlanesReadyContext` that GPU
+// side of populating the GpuMemoryBuffer has completed.
+struct NV12SinglePlaneReadyContext {
+ explicit NV12SinglePlaneReadyContext(
+ scoped_refptr<NV12PlanesReadyContext> nv12_planes_flushed);
+
+ NV12SinglePlaneReadyContext(const NV12SinglePlaneReadyContext& other) =
+ delete;
+ NV12SinglePlaneReadyContext& operator=(
+ const NV12SinglePlaneReadyContext& other) = delete;
+
+ ~NV12SinglePlaneReadyContext();
+
+ // Will be called with `NV12PlaneFlushedContext*`:
+ static void OnNV12PlaneReady(GrGpuFinishedContext context);
+
+ // Context to be notified that a plane has been populated.
+ scoped_refptr<NV12PlanesReadyContext> nv12_planes_flushed;
+};
+
class CopyOutputResultSkiaNV12 : public CopyOutputResult {
public:
CopyOutputResultSkiaNV12(
diff --git a/chromium/components/viz/service/display_embedder/software_output_surface.cc b/chromium/components/viz/service/display_embedder/software_output_surface.cc
index 35b4b9dd371..0fe3318ef87 100644
--- a/chromium/components/viz/service/display_embedder/software_output_surface.cc
+++ b/chromium/components/viz/service/display_embedder/software_output_surface.cc
@@ -24,12 +24,12 @@
namespace viz {
SoftwareOutputSurface::SoftwareOutputSurface(
- std::unique_ptr<SoftwareOutputDevice> software_device)
- : OutputSurface(std::move(software_device)) {
+ std::unique_ptr<SoftwareOutputDevice> device)
+ : OutputSurface(std::move(device)) {
capabilities_.pending_swap_params.max_pending_swaps =
- software_device_->MaxFramesPending();
+ software_device()->MaxFramesPending();
capabilities_.resize_based_on_root_surface =
- software_device_->SupportsOverridePlatformSize();
+ software_device()->SupportsOverridePlatformSize();
}
SoftwareOutputSurface::~SoftwareOutputSurface() = default;
@@ -48,17 +48,8 @@ void SoftwareOutputSurface::DiscardBackbuffer() {
software_device()->DiscardBackbuffer();
}
-void SoftwareOutputSurface::BindFramebuffer() {
- // Not used for software surfaces.
- NOTREACHED();
-}
-
-void SoftwareOutputSurface::Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) {
- software_device()->Resize(size, device_scale_factor);
+void SoftwareOutputSurface::Reshape(const ReshapeParams& params) {
+ software_device()->Resize(params.size, params.device_scale_factor);
}
void SoftwareOutputSurface::SwapBuffers(OutputSurfaceFrame frame) {
@@ -89,22 +80,6 @@ bool SoftwareOutputSurface::IsDisplayedAsOverlayPlane() const {
return false;
}
-unsigned SoftwareOutputSurface::GetOverlayTextureId() const {
- return 0;
-}
-
-bool SoftwareOutputSurface::HasExternalStencilTest() const {
- return false;
-}
-
-void SoftwareOutputSurface::ApplyExternalStencil() {}
-
-uint32_t SoftwareOutputSurface::GetFramebufferCopyTextureFormat() {
- // Not used for software surfaces.
- NOTREACHED();
- return 0;
-}
-
void SoftwareOutputSurface::SwapBuffersCallback(base::TimeTicks swap_time,
const gfx::Size& pixel_size) {
latency_tracker_.OnGpuSwapBuffersCompleted(
@@ -134,10 +109,6 @@ void SoftwareOutputSurface::UpdateVSyncParameters(base::TimeTicks timebase,
update_vsync_parameters_callback_.Run(timebase, interval);
}
-unsigned SoftwareOutputSurface::UpdateGpuFence() {
- return 0;
-}
-
void SoftwareOutputSurface::SetUpdateVSyncParametersCallback(
UpdateVSyncParametersCallback callback) {
update_vsync_parameters_callback_ = std::move(callback);
diff --git a/chromium/components/viz/service/display_embedder/software_output_surface.h b/chromium/components/viz/service/display_embedder/software_output_surface.h
index 4425e9f28a6..a7127891314 100644
--- a/chromium/components/viz/service/display_embedder/software_output_surface.h
+++ b/chromium/components/viz/service/display_embedder/software_output_surface.h
@@ -38,19 +38,9 @@ class VIZ_SERVICE_EXPORT SoftwareOutputSurface : public OutputSurface {
void BindToClient(OutputSurfaceClient* client) override;
void EnsureBackbuffer() override;
void DiscardBackbuffer() override;
- void BindFramebuffer() override;
- void Reshape(const gfx::Size& size,
- float device_scale_factor,
- const gfx::ColorSpace& color_space,
- gfx::BufferFormat format,
- bool use_stencil) override;
+ void Reshape(const ReshapeParams& params) override;
void SwapBuffers(OutputSurfaceFrame frame) override;
bool IsDisplayedAsOverlayPlane() const override;
- unsigned GetOverlayTextureId() const override;
- bool HasExternalStencilTest() const override;
- void ApplyExternalStencil() override;
- uint32_t GetFramebufferCopyTextureFormat() override;
- unsigned UpdateGpuFence() override;
void SetUpdateVSyncParametersCallback(
UpdateVSyncParametersCallback callback) override;
void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
diff --git a/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc b/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc
deleted file mode 100644
index ed33d5f717f..00000000000
--- a/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc
+++ /dev/null
@@ -1,348 +0,0 @@
-// 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.
-
-#include "components/viz/service/display_embedder/viz_process_context_provider.h"
-
-#include <stdint.h>
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/lazy_instance.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/observer_list.h"
-#include "base/system/sys_info.h"
-#include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "components/viz/common/display/renderer_settings.h"
-#include "components/viz/common/gpu/context_lost_observer.h"
-#include "components/viz/common/gpu/context_lost_reason.h"
-#include "components/viz/common/resources/platform_color.h"
-#include "components/viz/common/viz_utils.h"
-#include "components/viz/service/display/display_compositor_memory_and_task_controller.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_cmd_helper.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "gpu/command_buffer/client/raster_implementation_gles.h"
-#include "gpu/command_buffer/client/shared_memory_limits.h"
-#include "gpu/command_buffer/client/transfer_buffer.h"
-#include "gpu/config/gpu_feature_info.h"
-#include "gpu/config/gpu_preferences.h"
-#include "gpu/config/skia_limits.h"
-#include "gpu/ipc/common/surface_handle.h"
-#include "gpu/ipc/in_process_command_buffer.h"
-#include "gpu/skia_bindings/gles2_implementation_with_grcontext_support.h"
-#include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
-#include "third_party/skia/include/gpu/GrDirectContext.h"
-#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
-
-namespace viz {
-
-namespace {
-
-gpu::ContextCreationAttribs CreateAttributes(
- bool requires_alpha_channel,
- const RendererSettings& renderer_settings) {
- gpu::ContextCreationAttribs attributes;
- attributes.alpha_size = requires_alpha_channel ? 8 : -1;
- attributes.depth_size = 0;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
- // Chrome OS uses surfaceless when running on a real device and stencil
- // buffers can then be added dynamically so supporting them does not have an
- // impact on normal usage. If we are not running on a real Chrome OS device
- // but instead on a workstation for development, then stencil support is
- // useful as it allows the overdraw feedback debugging feature to be used.
- attributes.stencil_size = 8;
-#else
- attributes.stencil_size = 0;
-#endif
- attributes.samples = 0;
- attributes.sample_buffers = 0;
- attributes.bind_generates_resource = false;
- attributes.fail_if_major_perf_caveat = false;
- attributes.lose_context_when_out_of_memory = true;
-
-#if BUILDFLAG(IS_ANDROID)
- if (renderer_settings.color_space == gfx::ColorSpace::CreateSRGB()) {
- attributes.color_space = gpu::COLOR_SPACE_SRGB;
- } else if (renderer_settings.color_space ==
- gfx::ColorSpace::CreateDisplayP3D65()) {
- attributes.color_space = gpu::COLOR_SPACE_DISPLAY_P3;
- } else {
- // The browser only sends the above two color spaces.
- NOTREACHED();
- }
-
- if (!requires_alpha_channel && PreferRGB565ResourcesForDisplay()) {
- // See compositor_impl_android.cc for more information about this.
- // It is inside GetCompositorContextAttributes().
- attributes.alpha_size = 0;
- attributes.red_size = 5;
- attributes.green_size = 6;
- attributes.blue_size = 5;
- }
-
- attributes.enable_swap_timestamps_if_supported = true;
-#endif // BUILDFLAG(IS_ANDROID)
-
- return attributes;
-}
-
-void UmaRecordContextLost(ContextLostReason reason) {
- UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.DisplayCompositor", reason);
-}
-
-gpu::SharedMemoryLimits SharedMemoryLimitsForRendererSettings(
- const RendererSettings& renderer_settings) {
-#if BUILDFLAG(IS_ANDROID)
- return gpu::SharedMemoryLimits::ForDisplayCompositor(
- renderer_settings.initial_screen_size);
-#else
- return gpu::SharedMemoryLimits::ForDisplayCompositor();
-#endif
-}
-
-} // namespace
-
-VizProcessContextProvider::VizProcessContextProvider(
- gpu::CommandBufferTaskExecutor* task_executor,
- gpu::SurfaceHandle surface_handle,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
- gpu::ImageFactory* image_factory,
- gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
- DisplayCompositorMemoryAndTaskController* display_controller,
- const RendererSettings& renderer_settings)
- : attributes_(CreateAttributes(renderer_settings.requires_alpha_channel,
- renderer_settings)) {
- InitializeContext(std::move(task_executor), surface_handle,
- gpu_memory_buffer_manager, image_factory,
- gpu_channel_manager_delegate, display_controller,
- SharedMemoryLimitsForRendererSettings(renderer_settings));
-
- if (context_result_ == gpu::ContextResult::kSuccess) {
- // |gles2_implementation_| is owned here so bind an unretained pointer or
- // there will be a circular reference preventing destruction.
- gles2_implementation_->SetLostContextCallback(base::BindOnce(
- &VizProcessContextProvider::OnContextLost, base::Unretained(this)));
-
- base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
- this, "VizProcessContextProvider", base::ThreadTaskRunnerHandle::Get());
- } else {
- UmaRecordContextLost(CONTEXT_INIT_FAILED);
- }
-}
-
-VizProcessContextProvider::VizProcessContextProvider() = default;
-
-VizProcessContextProvider::~VizProcessContextProvider() {
- if (context_result_ == gpu::ContextResult::kSuccess) {
- base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
- this);
- }
-
- // cache_controller_ might ne nullptr if we failed to initialize
- if (cache_controller_)
- cache_controller_->SetGrContext(nullptr);
-}
-
-void VizProcessContextProvider::AddRef() const {
- base::RefCountedThreadSafe<VizProcessContextProvider>::AddRef();
-}
-
-void VizProcessContextProvider::Release() const {
- base::RefCountedThreadSafe<VizProcessContextProvider>::Release();
-}
-
-gpu::ContextResult VizProcessContextProvider::BindToCurrentThread() {
- return context_result_;
-}
-
-gpu::gles2::GLES2Interface* VizProcessContextProvider::ContextGL() {
- return gles2_implementation_.get();
-}
-
-gpu::ContextSupport* VizProcessContextProvider::ContextSupport() {
- return gles2_implementation_.get();
-}
-
-class GrDirectContext* VizProcessContextProvider::GrContext() {
- if (gr_context_)
- return gr_context_->get();
-
- size_t max_resource_cache_bytes;
- size_t max_glyph_cache_texture_bytes;
- gpu::DetermineGrCacheLimitsFromAvailableMemory(
- &max_resource_cache_bytes, &max_glyph_cache_texture_bytes);
-
- gr_context_ = std::make_unique<skia_bindings::GrContextForGLES2Interface>(
- ContextGL(), ContextSupport(), ContextCapabilities(),
- max_resource_cache_bytes, max_glyph_cache_texture_bytes);
- cache_controller_->SetGrContext(gr_context_->get());
- return gr_context_->get();
-}
-
-gpu::SharedImageInterface* VizProcessContextProvider::SharedImageInterface() {
- return command_buffer_->GetSharedImageInterface();
-}
-
-ContextCacheController* VizProcessContextProvider::CacheController() {
- return cache_controller_.get();
-}
-
-base::Lock* VizProcessContextProvider::GetLock() {
- // Locking isn't supported on display compositor contexts.
- return nullptr;
-}
-
-const gpu::Capabilities& VizProcessContextProvider::ContextCapabilities()
- const {
- return command_buffer_->GetCapabilities();
-}
-
-const gpu::GpuFeatureInfo& VizProcessContextProvider::GetGpuFeatureInfo()
- const {
- return command_buffer_->GetGpuFeatureInfo();
-}
-
-void VizProcessContextProvider::AddObserver(ContextLostObserver* obs) {
- observers_.AddObserver(obs);
-}
-
-void VizProcessContextProvider::RemoveObserver(ContextLostObserver* obs) {
- observers_.RemoveObserver(obs);
-}
-
-void VizProcessContextProvider::SetUpdateVSyncParametersCallback(
- UpdateVSyncParametersCallback callback) {
- command_buffer_->SetUpdateVSyncParametersCallback(std::move(callback));
-}
-
-void VizProcessContextProvider::SetGpuVSyncCallback(GpuVSyncCallback callback) {
- command_buffer_->SetGpuVSyncCallback(std::move(callback));
-}
-
-void VizProcessContextProvider::SetGpuVSyncEnabled(bool enabled) {
- command_buffer_->SetGpuVSyncEnabled(enabled);
-}
-
-bool VizProcessContextProvider::UseRGB565PixelFormat() const {
- return attributes_.alpha_size == 0 && attributes_.red_size == 5 &&
- attributes_.green_size == 6 && attributes_.blue_size == 5;
-}
-
-uint32_t VizProcessContextProvider::GetCopyTextureInternalFormat() {
- return attributes_.alpha_size > 0 ? GL_RGBA : GL_RGB;
-}
-
-void VizProcessContextProvider::InitializeContext(
- gpu::CommandBufferTaskExecutor* task_executor,
- gpu::SurfaceHandle surface_handle,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
- gpu::ImageFactory* image_factory,
- gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
- DisplayCompositorMemoryAndTaskController* display_controller,
- const gpu::SharedMemoryLimits& mem_limits) {
- const bool is_offscreen = surface_handle == gpu::kNullSurfaceHandle;
- DCHECK(display_controller);
- gpu_task_scheduler_helper_ = display_controller->gpu_task_scheduler();
-
- command_buffer_ = std::make_unique<gpu::InProcessCommandBuffer>(
- task_executor,
- GURL("chrome://gpu/VizProcessContextProvider::InitializeContext"));
- context_result_ = command_buffer_->Initialize(
- /*surface=*/nullptr, is_offscreen, surface_handle, attributes_,
- gpu_memory_buffer_manager, image_factory, gpu_channel_manager_delegate,
- base::ThreadTaskRunnerHandle::Get(),
- gpu_task_scheduler_helper_->GetTaskSequence(),
- display_controller->controller_on_gpu(), nullptr, nullptr);
- if (context_result_ != gpu::ContextResult::kSuccess) {
- DLOG(ERROR) << "Failed to initialize InProcessCommmandBuffer";
- return;
- }
-
- // Create the GLES2 helper, which writes the command buffer protocol.
- gles2_helper_ =
- std::make_unique<gpu::gles2::GLES2CmdHelper>(command_buffer_.get());
- context_result_ = gles2_helper_->Initialize(mem_limits.command_buffer_size);
- if (context_result_ != gpu::ContextResult::kSuccess) {
- DLOG(ERROR) << "Failed to initialize GLES2CmdHelper";
- return;
- }
-
- if (gpu_task_scheduler_helper_)
- gpu_task_scheduler_helper_->Initialize(gles2_helper_.get());
-
- transfer_buffer_ = std::make_unique<gpu::TransferBuffer>(gles2_helper_.get());
-
- // Create the object exposing the OpenGL API.
- gles2_implementation_ =
- std::make_unique<skia_bindings::GLES2ImplementationWithGrContextSupport>(
- gles2_helper_.get(), /*share_group=*/nullptr, transfer_buffer_.get(),
- attributes_.bind_generates_resource,
- attributes_.lose_context_when_out_of_memory,
- /*support_client_side_arrays=*/false, command_buffer_.get());
-
- context_result_ = gles2_implementation_->Initialize(mem_limits);
- if (context_result_ != gpu::ContextResult::kSuccess) {
- DLOG(ERROR) << "Failed to initialize GLES2Implementation";
- return;
- }
-
- cache_controller_ = std::make_unique<ContextCacheController>(
- gles2_implementation_.get(), base::ThreadTaskRunnerHandle::Get());
-
- // TraceEndCHROMIUM is implicit when the context is destroyed
- gles2_implementation_->TraceBeginCHROMIUM("VizCompositor",
- "DisplayCompositor");
-}
-
-void VizProcessContextProvider::OnContextLost() {
- for (auto& observer : observers_)
- observer.OnContextLost();
- if (gr_context_)
- gr_context_->OnLostContext();
-
- gpu::CommandBuffer::State state = command_buffer_->GetLastState();
- UmaRecordContextLost(
- GetContextLostReason(state.error, state.context_lost_reason));
-}
-
-bool VizProcessContextProvider::OnMemoryDump(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
- DCHECK_EQ(context_result_, gpu::ContextResult::kSuccess);
- if (args.level_of_detail ==
- base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
- if (gr_context_)
- gpu::raster::DumpBackgroundGrMemoryStatistics(gr_context_->get(), pmd);
-
- // Early out, no need for more detail in a BACKGROUND dump.
- return true;
- }
-
- gles2_implementation_->OnMemoryDump(args, pmd);
- gles2_helper_->OnMemoryDump(args, pmd);
-
- if (gr_context_) {
- gpu::raster::DumpGrMemoryStatistics(
- gr_context_->get(), pmd,
- gles2_implementation_->ShareGroupTracingGUID());
- }
- return true;
-}
-
-base::ScopedClosureRunner VizProcessContextProvider::GetCacheBackBufferCb() {
- return command_buffer_->GetCacheBackBufferCb();
-}
-
-void VizProcessContextProvider::SetNeedsMeasureNextDrawLatency() {
- return command_buffer_->SetNeedsMeasureNextDrawLatency();
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/viz_process_context_provider.h b/chromium/components/viz/service/display_embedder/viz_process_context_provider.h
deleted file mode 100644
index 8ff0ccce732..00000000000
--- a/chromium/components/viz/service/display_embedder/viz_process_context_provider.h
+++ /dev/null
@@ -1,136 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_VIZ_PROCESS_CONTEXT_PROVIDER_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_VIZ_PROCESS_CONTEXT_PROVIDER_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/callback_helpers.h"
-#include "base/memory/raw_ptr.h"
-#include "base/observer_list.h"
-#include "base/trace_event/memory_dump_provider.h"
-#include "components/viz/common/display/update_vsync_parameters_callback.h"
-#include "components/viz/common/gpu/context_cache_controller.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/common/gpu/gpu_vsync_callback.h"
-#include "components/viz/service/viz_service_export.h"
-#include "gpu/command_buffer/common/context_creation_attribs.h"
-#include "gpu/ipc/common/surface_handle.h"
-#include "gpu/ipc/gpu_task_scheduler_helper.h"
-#include "ui/gfx/native_widget_types.h"
-
-class GrDirectContext;
-
-namespace gpu {
-namespace gles2 {
-class GLES2CmdHelper;
-class GLES2Implementation;
-} // namespace gles2
-class CommandBufferTaskExecutor;
-class GpuChannelManagerDelegate;
-class GpuMemoryBufferManager;
-class ImageFactory;
-class InProcessCommandBuffer;
-class TransferBuffer;
-struct SharedMemoryLimits;
-} // namespace gpu
-
-namespace skia_bindings {
-class GrContextForGLES2Interface;
-}
-
-namespace viz {
-class ContextLostObserver;
-class DisplayCompositorMemoryAndTaskController;
-class GpuTaskSchedulerHelper;
-class RendererSettings;
-
-// A ContextProvider used in the viz process to setup an InProcessCommandBuffer
-// for the display compositor.
-class VIZ_SERVICE_EXPORT VizProcessContextProvider
- : public base::RefCountedThreadSafe<VizProcessContextProvider>,
- public ContextProvider,
- public base::trace_event::MemoryDumpProvider {
- public:
- VizProcessContextProvider(
- gpu::CommandBufferTaskExecutor* task_executor,
- gpu::SurfaceHandle surface_handle,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
- gpu::ImageFactory* image_factory,
- gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
- DisplayCompositorMemoryAndTaskController* display_controller,
- const RendererSettings& renderer_settings);
-
- // ContextProvider implementation.
- void AddRef() const override;
- void Release() const override;
- gpu::ContextResult BindToCurrentThread() override;
- gpu::gles2::GLES2Interface* ContextGL() override;
- gpu::ContextSupport* ContextSupport() override;
- class GrDirectContext* GrContext() override;
- gpu::SharedImageInterface* SharedImageInterface() override;
- ContextCacheController* CacheController() override;
- base::Lock* GetLock() override;
- const gpu::Capabilities& ContextCapabilities() const override;
- const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override;
- void AddObserver(ContextLostObserver* obs) override;
- void RemoveObserver(ContextLostObserver* obs) override;
-
- virtual void SetUpdateVSyncParametersCallback(
- UpdateVSyncParametersCallback callback);
- virtual void SetGpuVSyncCallback(GpuVSyncCallback callback);
- virtual void SetGpuVSyncEnabled(bool enabled);
- virtual bool UseRGB565PixelFormat() const;
-
- // Provides the GL internal format that should be used when calling
- // glCopyTexImage2D() on the default framebuffer.
- virtual uint32_t GetCopyTextureInternalFormat();
-
- virtual base::ScopedClosureRunner GetCacheBackBufferCb();
-
- void SetNeedsMeasureNextDrawLatency();
-
- protected:
- friend class base::RefCountedThreadSafe<VizProcessContextProvider>;
- VizProcessContextProvider(); // For testing only.
- ~VizProcessContextProvider() override;
-
- private:
- void InitializeContext(
- gpu::CommandBufferTaskExecutor* task_executor,
- gpu::SurfaceHandle surface_handle,
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
- gpu::ImageFactory* image_factory,
- gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
- DisplayCompositorMemoryAndTaskController* display_controller,
- const gpu::SharedMemoryLimits& mem_limits);
- void OnContextLost();
-
- // base::trace_event::MemoryDumpProvider implementation.
- bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) override;
-
- const gpu::ContextCreationAttribs attributes_;
-
- // The |gpu_task_scheduler_helper_| has 1:1 relationship with the Display
- // compositor.
- raw_ptr<gpu::GpuTaskSchedulerHelper> gpu_task_scheduler_helper_;
- std::unique_ptr<gpu::InProcessCommandBuffer> command_buffer_;
- std::unique_ptr<gpu::gles2::GLES2CmdHelper> gles2_helper_;
- std::unique_ptr<gpu::TransferBuffer> transfer_buffer_;
- std::unique_ptr<gpu::gles2::GLES2Implementation> gles2_implementation_;
- std::unique_ptr<ContextCacheController> cache_controller_;
- gpu::ContextResult context_result_ = gpu::ContextResult::kSuccess;
-
- std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
-
- base::ObserverList<ContextLostObserver>::Unchecked observers_;
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_VIZ_PROCESS_CONTEXT_PROVIDER_H_
diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index 51c829c9e0b..4828b3ed23f 100644
--- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -192,6 +192,14 @@ void CompositorFrameSinkSupport::ThrottleBeginFrame(base::TimeDelta interval) {
begin_frame_interval_ = interval;
}
+void CompositorFrameSinkSupport::OnSurfaceCommitted(Surface* surface) {
+ if (surface->HasPendingFrame()) {
+ // Make sure we periodically check if the frame should activate.
+ pending_surfaces_.insert(surface);
+ UpdateNeedsBeginFramesInternal();
+ }
+}
+
void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) {
DCHECK(surface);
DCHECK(surface->HasActiveFrame());
@@ -281,7 +289,7 @@ void CompositorFrameSinkSupport::OnFrameTokenChanged(uint32_t frame_token) {
frame_sink_manager_->OnFrameTokenChanged(frame_sink_id_, frame_token);
}
-void CompositorFrameSinkSupport::OnSurfaceProcessed(Surface* surface) {
+void CompositorFrameSinkSupport::SendCompositorFrameAck() {
DidReceiveCompositorFrameAck();
}
@@ -676,9 +684,7 @@ SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
TRACE_EVENT_SCOPE_THREAD);
return SubmitResult::SIZE_MISMATCH;
case Surface::QueueFrameResult::ACCEPTED_PENDING:
- // Make sure we periodically check if the frame should activate.
- pending_surfaces_.insert(current_surface);
- UpdateNeedsBeginFramesInternal();
+ // Pending frames are processed in OnSurfaceCommitted.
break;
case Surface::QueueFrameResult::ACCEPTED_ACTIVE:
// Nothing to do here.
diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index f3a5548db7a..704adeffbc4 100644
--- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -141,6 +141,7 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
void ThrottleBeginFrame(base::TimeDelta interval);
// SurfaceClient implementation.
+ void OnSurfaceCommitted(Surface* surface) override;
void OnSurfaceActivated(Surface* surface) override;
void OnSurfaceDestroyed(Surface* surface) override;
void OnSurfaceWillDraw(Surface* surface) override;
@@ -155,7 +156,7 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
std::vector<PendingCopyOutputRequest> TakeCopyOutputRequests(
const LocalSurfaceId& local_surface_id) override;
void OnFrameTokenChanged(uint32_t frame_token) override;
- void OnSurfaceProcessed(Surface* surface) override;
+ void SendCompositorFrameAck() override;
void OnSurfaceAggregatedDamage(
Surface* surface,
const LocalSurfaceId& local_surface_id,
diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index 0a4a21b7915..083f26653b7 100644
--- a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -70,7 +70,9 @@ FrameSinkManagerImpl::FrameSinkManagerImpl(const InitParams& params)
: shared_bitmap_manager_(params.shared_bitmap_manager),
output_surface_provider_(params.output_surface_provider),
gmb_context_provider_(params.gmb_context_provider),
- surface_manager_(this, params.activation_deadline_in_frames),
+ surface_manager_(this,
+ params.activation_deadline_in_frames,
+ params.max_uncommitted_frames),
hit_test_manager_(surface_manager()),
restart_id_(params.restart_id),
run_all_compositor_stages_before_draw_(
@@ -414,6 +416,11 @@ void FrameSinkManagerImpl::RegisterCompositorFrameSinkSupport(
for (auto& observer : observer_list_)
observer.OnCreatedCompositorFrameSink(frame_sink_id, support->is_root());
+
+ if (global_throttle_interval_) {
+ UpdateThrottlingRecursively(frame_sink_id,
+ global_throttle_interval_.value());
+ }
}
void FrameSinkManagerImpl::UnregisterCompositorFrameSinkSupport(
@@ -733,16 +740,41 @@ void FrameSinkManagerImpl::Throttle(const std::vector<FrameSinkId>& ids,
UpdateThrottling();
}
+void FrameSinkManagerImpl::StartThrottlingAllFrameSinks(
+ base::TimeDelta interval) {
+ global_throttle_interval_ = interval;
+ UpdateThrottling();
+}
+
+void FrameSinkManagerImpl::StopThrottlingAllFrameSinks() {
+ global_throttle_interval_ = absl::nullopt;
+ UpdateThrottling();
+}
+
void FrameSinkManagerImpl::UpdateThrottling() {
// Clear previous throttling effect on all frame sinks.
for (auto& support_map_item : support_map_) {
support_map_item.second->ThrottleBeginFrame(base::TimeDelta());
}
- if (throttle_interval_.is_zero())
+ if (throttle_interval_.is_zero() &&
+ (!global_throttle_interval_ ||
+ global_throttle_interval_.value().is_zero()))
return;
- for (const auto& id : frame_sink_ids_to_throttle_) {
- UpdateThrottlingRecursively(id, throttle_interval_);
+ if (global_throttle_interval_) {
+ for (const auto& support : support_map_) {
+ support.second->ThrottleBeginFrame(global_throttle_interval_.value());
+ }
+ }
+
+ // If the per-frame sink throttle interval is more aggressive than the global
+ // throttling interval, apply it to those frame sinks effectively always
+ // throttling a frame sink as much as possible.
+ if (!global_throttle_interval_ ||
+ throttle_interval_ > global_throttle_interval_) {
+ for (const auto& id : frame_sink_ids_to_throttle_) {
+ UpdateThrottlingRecursively(id, throttle_interval_);
+ }
}
// Clear throttling on frame sinks currently being captured.
for (const auto& id : captured_frame_sink_ids_) {
diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index 833d23c839e..7c7c2791d4a 100644
--- a/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -87,6 +87,7 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
DebugRendererSettings debug_renderer_settings;
base::ProcessId host_process_id = base::kNullProcessId;
raw_ptr<HintSessionFactory> hint_session_factory = nullptr;
+ size_t max_uncommitted_frames = 0;
};
explicit FrameSinkManagerImpl(const InitParams& params);
@@ -152,6 +153,8 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
const DebugRendererSettings& debug_settings) override;
void Throttle(const std::vector<FrameSinkId>& ids,
base::TimeDelta interval) override;
+ void StartThrottlingAllFrameSinks(base::TimeDelta interval) override;
+ void StopThrottlingAllFrameSinks() override;
void DestroyFrameSinkBundle(const FrameSinkBundleId& id);
@@ -394,9 +397,16 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
// Ids of the frame sinks that have been requested to throttle.
std::vector<FrameSinkId> frame_sink_ids_to_throttle_;
- // The throttling interval which defines how often BeginFrames are sent.
+ // The throttling interval which defines how often BeginFrames are sent for
+ // frame sinks in `frame_sink_ids_to_throttle_`, if
+ // `global_throttle_interval_` is unset or if this interval is longer than
+ // `global_throttle_interval_`.
base::TimeDelta throttle_interval_ = BeginFrameArgs::DefaultInterval();
+ // If present, the throttling interval which defines the upper bound of how
+ // often BeginFrames are sent for all current and future frame sinks.
+ absl::optional<base::TimeDelta> global_throttle_interval_ = absl::nullopt;
+
base::flat_map<uint32_t, base::ScopedClosureRunner> cached_back_buffers_;
THREAD_CHECKER(thread_checker_);
diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc b/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
index c49494e98fb..8f8ba7ef6c9 100644
--- a/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
@@ -33,6 +33,8 @@ constexpr FrameSinkId kFrameSinkIdA(2, 1);
constexpr FrameSinkId kFrameSinkIdB(3, 1);
constexpr FrameSinkId kFrameSinkIdC(4, 1);
constexpr FrameSinkId kFrameSinkIdD(5, 1);
+constexpr FrameSinkId kFrameSinkIdE(6, 1);
+constexpr FrameSinkId kFrameSinkIdF(7, 1);
// Holds the four interface objects needed to create a RootCompositorFrameSink.
struct RootCompositorFrameSinkData {
@@ -472,6 +474,86 @@ TEST_F(FrameSinkManagerTest, Throttle) {
client_d->frame_sink_id());
}
+TEST_F(FrameSinkManagerTest, GlobalThrottle) {
+ // root -> A -> B
+ // -> C -> D
+ auto root = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot);
+ auto client_a = CreateCompositorFrameSinkSupport(kFrameSinkIdA);
+ auto client_b = CreateCompositorFrameSinkSupport(kFrameSinkIdB);
+ auto client_c = CreateCompositorFrameSinkSupport(kFrameSinkIdC);
+ auto client_d = CreateCompositorFrameSinkSupport(kFrameSinkIdD);
+
+ // Set up the hierarchy.
+ manager_.RegisterFrameSinkHierarchy(root->frame_sink_id(),
+ client_a->frame_sink_id());
+ manager_.RegisterFrameSinkHierarchy(client_a->frame_sink_id(),
+ client_b->frame_sink_id());
+ manager_.RegisterFrameSinkHierarchy(root->frame_sink_id(),
+ client_c->frame_sink_id());
+ manager_.RegisterFrameSinkHierarchy(client_c->frame_sink_id(),
+ client_d->frame_sink_id());
+
+ constexpr base::TimeDelta global_interval = base::Hertz(30);
+ constexpr base::TimeDelta interval = base::Hertz(20);
+
+ std::vector<FrameSinkId> ids{kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB,
+ kFrameSinkIdC, kFrameSinkIdD};
+
+ // By default, a CompositorFrameSinkSupport shouldn't have its
+ // |begin_frame_interval| set.
+ VerifyThrottling(base::TimeDelta(), ids);
+
+ // Starting global throttling should throttle the entire hierarchy.
+ manager_.StartThrottlingAllFrameSinks(global_interval);
+ VerifyThrottling(global_interval, ids);
+
+ // Throttling more aggressively on top of global throttling should further
+ // throttle the specified frame sink hierarchy, but preserve global throttling
+ // on the unaffected framesinks.
+ manager_.Throttle({kFrameSinkIdC}, interval);
+ VerifyThrottling(global_interval,
+ {kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB});
+ VerifyThrottling(interval, {kFrameSinkIdC, kFrameSinkIdD});
+
+ // Attempting to per-sink throttle to an interval shorter than the global
+ // throttling should still throttle all frame sinks to the global interval.
+ manager_.Throttle({kFrameSinkIdA}, base::Hertz(40));
+ VerifyThrottling(global_interval, ids);
+
+ // Add a new branch to the hierarchy. These new frame sinks should be globally
+ // throttled immediately. root -> A -> B
+ // -> C -> D
+ // -> E -> F
+ auto client_e = CreateCompositorFrameSinkSupport(kFrameSinkIdE);
+ auto client_f = CreateCompositorFrameSinkSupport(kFrameSinkIdF);
+ manager_.RegisterFrameSinkHierarchy(root->frame_sink_id(),
+ client_e->frame_sink_id());
+ manager_.RegisterFrameSinkHierarchy(client_e->frame_sink_id(),
+ client_f->frame_sink_id());
+ VerifyThrottling(
+ global_interval,
+ {kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB, kFrameSinkIdC,
+ kFrameSinkIdD, kFrameSinkIdE, kFrameSinkIdF});
+
+ // Disabling global throttling should revert back to only the up-to-date
+ // per-frame sink throttling.
+ manager_.StopThrottlingAllFrameSinks();
+ VerifyThrottling(base::Hertz(40), {kFrameSinkIdA, kFrameSinkIdB});
+
+ manager_.UnregisterFrameSinkHierarchy(root->frame_sink_id(),
+ client_a->frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(client_a->frame_sink_id(),
+ client_b->frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(root->frame_sink_id(),
+ client_c->frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(client_c->frame_sink_id(),
+ client_d->frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(root->frame_sink_id(),
+ client_e->frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(client_e->frame_sink_id(),
+ client_f->frame_sink_id());
+}
+
// Verifies if a frame sink is being captured, it should not be throttled.
TEST_F(FrameSinkManagerTest, NoThrottleOnFrameSinksBeingCaptured) {
// root -> A -> B -> C
diff --git a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index 4e38e0f4905..8eeb87e2322 100644
--- a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
+++ b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -81,7 +81,7 @@ RootCompositorFrameSinkImpl::Create(
mojo::Remote<mojom::DisplayClient> display_client(
std::move(params->display_client));
auto display_controller = output_surface_provider->CreateGpuDependency(
- params->gpu_compositing, params->widget, params->renderer_settings);
+ params->gpu_compositing, params->widget);
auto output_surface = output_surface_provider->CreateOutputSurface(
params->widget, params->gpu_compositing, display_client.get(),
display_controller.get(), params->renderer_settings, debug_settings);
@@ -175,9 +175,7 @@ RootCompositorFrameSinkImpl::Create(
auto* output_surface_ptr = output_surface.get();
#endif
gpu::SharedImageInterface* sii = nullptr;
- if (output_surface->context_provider())
- sii = output_surface->context_provider()->SharedImageInterface();
- else if (display_controller)
+ if (display_controller)
sii = display_controller->shared_image_interface();
auto overlay_processor = OverlayProcessorInterface::CreateOverlayProcessor(
diff --git a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index 87a00f3433e..d4924b12e0e 100644
--- a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -37,6 +37,8 @@
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
using media::VideoCaptureOracle;
@@ -111,6 +113,46 @@ CopyOutputRequest::ResultFormat VideoPixelFormatToCopyOutputRequestFormat(
}
}
+bool IsCompatibleWithFormat(const gfx::Rect& rect,
+ media::VideoPixelFormat format) {
+ DCHECK(format == media::PIXEL_FORMAT_I420 ||
+ format == media::PIXEL_FORMAT_NV12 ||
+ format == media::PIXEL_FORMAT_ARGB);
+ if (format == media::PIXEL_FORMAT_ARGB) {
+ // No special requirements:
+ return true;
+ }
+
+ return rect.origin().x() % 2 == 0 && rect.origin().y() % 2 == 0 &&
+ rect.width() % 2 == 0 && rect.height() % 2 == 0;
+}
+
+// Given a |visible_rect| representing visible rectangle of some video frame,
+// calculates a centered rectangle that fits entirely within |visible_rect| and
+// has the same aspect ratio as |source_size|, taking into account
+// |pixel_format|.
+gfx::Rect GetContentRectangle(const gfx::Rect& visible_rect,
+ const gfx::Size& source_size,
+ media::VideoPixelFormat pixel_format) {
+ DCHECK(pixel_format == media::PIXEL_FORMAT_I420 ||
+ pixel_format == media::PIXEL_FORMAT_NV12 ||
+ pixel_format == media::PIXEL_FORMAT_ARGB);
+
+ if (pixel_format == media::PIXEL_FORMAT_I420 ||
+ pixel_format == media::PIXEL_FORMAT_NV12) {
+ return media::ComputeLetterboxRegionForI420(visible_rect, source_size);
+ } else {
+ DCHECK_EQ(media::PIXEL_FORMAT_ARGB, pixel_format);
+ const gfx::Rect content_rect =
+ media::ComputeLetterboxRegion(visible_rect, source_size);
+
+ // The media letterboxing computation explicitly allows for off-by-one
+ // errors due to computation, so we address those here.
+ return content_rect.ApproximatelyEqual(visible_rect, 1) ? visible_rect
+ : content_rect;
+ }
+}
+
} // namespace
// static
@@ -208,6 +250,7 @@ void FrameSinkVideoCapturerImpl::SetResolvedTarget(
resolved_target_->AttachCaptureClient(this);
RefreshEntireSourceNow();
} else {
+ MaybeInformConsumerOfEmptyRegion();
// The capturer will remain idle until either: 1) the requested target is
// re-resolved by the |frame_sink_manager_|, or 2) a new target is set via a
// call to ChangeTarget().
@@ -308,6 +351,10 @@ void FrameSinkVideoCapturerImpl::SetResolutionConstraints(
bool use_fixed_aspect_ratio) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DVLOG(2) << __func__ << ": min_size=" << min_size.ToString()
+ << ", max_size=" << max_size.ToString()
+ << ", use_fixed_aspect_ratio=" << use_fixed_aspect_ratio;
+
TRACE_EVENT_INSTANT2("gpu.capture", "SetResolutionConstraints",
TRACE_EVENT_SCOPE_THREAD, "min_size.width",
min_size.width(), "min_size.height", min_size.height());
@@ -534,7 +581,7 @@ void FrameSinkVideoCapturerImpl::RefreshInternal(
// If the capture region is empty, it means one of two things: the first
// frame has not been composited yet or the current region selected for
// capture has a current size of zero. We schedule a frame refresh here,
- // although its not useful in all circumstances.
+ // although it's not useful in all circumstances.
MaybeScheduleRefreshFrame();
return;
}
@@ -695,6 +742,7 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
// TODO(https://crbug.com/1300943): we should likely just get the frame
// region from the last aggregated surface.
if (!compositor_frame_region.Contains(capture_region)) {
+ DVLOG(3) << __func__ << ": skipping capture!";
MaybeScheduleRefreshFrame();
return;
}
@@ -707,9 +755,24 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
// Reserve a buffer from the pool for the next frame.
const OracleFrameNumber oracle_frame_number = oracle_->next_frame_number();
+
+ // Size of the video frames that we are supposed to produce. Depends on the
+ // pixel format and the capture size as determined by the oracle (which in
+ // turn depends on the capture constraints).
const gfx::Size capture_size =
AdjustSizeForPixelFormat(oracle_->capture_size());
+ // Size of the source that we are capturing:
+ const gfx::Size source_size = oracle_->source_size();
+ DCHECK_EQ(capture_region.size(), source_size);
+ DCHECK(!source_size.IsEmpty());
+
+ DVLOG(3) << __func__
+ << ": compositor_frame_region=" << compositor_frame_region.ToString()
+ << ", capture_region=" << capture_region.ToString()
+ << ", capture_size=" << capture_size.ToString()
+ << ", event=" << event;
+
const bool can_resurrect_content = CanResurrectFrame(capture_size);
scoped_refptr<VideoFrame> frame;
if (can_resurrect_content) {
@@ -757,6 +820,13 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
return;
}
+ // If frame was resurrected / allocated from the pool, its visible rectangle
+ // should match what we requested:
+ DCHECK_EQ(frame->visible_rect().size(), capture_size);
+ // The pool should return a frame with visible rectangle that is compatible
+ // with the capture format.
+ DCHECK(IsCompatibleWithFormat(frame->visible_rect(), pixel_format_));
+
// Record a trace event if the capture pipeline is redlining, but capture will
// still proceed.
if (utilization >= 1.0) {
@@ -791,28 +861,33 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
metadata.top_controls_visible_height = last_top_controls_visible_height_;
oracle_->RecordCapture(utilization);
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN2("gpu.capture", "Capture",
- oracle_frame_number, "frame_number",
- capture_frame_number, "trigger",
- VideoCaptureOracle::EventAsString(event));
-
- const gfx::Size& source_size = oracle_->source_size();
- DCHECK(!source_size.IsEmpty());
- gfx::Rect content_rect;
- if (pixel_format_ == media::PIXEL_FORMAT_I420 ||
- pixel_format_ == media::PIXEL_FORMAT_NV12) {
- content_rect = media::ComputeLetterboxRegionForI420(frame->visible_rect(),
- source_size);
- } else {
- DCHECK_EQ(media::PIXEL_FORMAT_ARGB, pixel_format_);
- content_rect =
- media::ComputeLetterboxRegion(frame->visible_rect(), source_size);
- // The media letterboxing computation explicitly allows for off-by-one
- // errors due to computation, so we address those here.
- if (content_rect.ApproximatelyEqual(frame->visible_rect(), 1)) {
- content_rect = frame->visible_rect();
- }
- }
+ // Note: The following is used by
+ // chrome/browser/media/cast_mirroring_performance_browsertest.cc, in
+ // addition to the usual runtime tracing
+ // TODO(https://crbug.com/1322573): change to _NESTABLE_ variant of the macro
+ // once the bug is fixed.
+ TRACE_EVENT_ASYNC_BEGIN2("gpu.capture", "Capture", oracle_frame_number,
+ "frame_number", capture_frame_number, "trigger",
+ VideoCaptureOracle::EventAsString(event));
+
+ // `content_rect` is the region of the `frame` that we would like to populate.
+ // We know our source is of size `source_size`, and we have
+ // `frame->visible_rect()` to fill out - find the largest centered rectangle
+ // that will fit within the frame and maintains the aspect ratio of the
+ // source.
+ // TODO(https://crbug.com/1323342): currently, both the frame's visible
+ // rectangle and source size are controlled by oracle
+ // (`frame->visible_rect().size() == `capture_size`). Oracle also knows if we
+ // need to maintain fixed aspect ratio, so it should compute both the
+ // `capture_size` and `content_rect` for us, thus ensuring that letterboxing
+ // happens only when it needs to (i.e. when we allocate a frame and know that
+ // aspect ratio does not have to be maintained, we should use a size that we
+ // know would not require letterboxing).
+ const gfx::Rect content_rect =
+ GetContentRectangle(frame->visible_rect(), source_size, pixel_format_);
+ DVLOG(3) << __func__ << ": content_rect=" << content_rect.ToString()
+ << ", source_size=" << source_size.ToString()
+ << ", frame=" << frame->AsHumanReadableString();
// Determine what rectangular region has changed since the last captured
// frame.
@@ -896,8 +971,17 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
return;
}
- DCHECK(capture_region.size() == source_size);
- if (absl::holds_alternative<RegionCaptureCropId>(target_->sub_target)) {
+ // If the target is in a different renderer than the root renderer (indicated
+ // by having a different frame sink ID), we currently cannot provide
+ // reasonable metadata about the region capture rect. For more context, see
+ // https://crbug.com/1327560.
+ //
+ // TODO(https://crbug.com/1335175): Provide accurate bounds for elements
+ // embedded in different renderers.
+ const bool is_same_frame_sink_as_requested =
+ resolved_target_->GetFrameSinkId() == target_->frame_sink_id;
+ if (absl::holds_alternative<RegionCaptureCropId>(target_->sub_target) &&
+ is_same_frame_sink_as_requested) {
const float scale_factor = frame_metadata.device_scale_factor;
metadata.region_capture_rect =
scale_factor ? ScaleToEnclosingRect(capture_region, 1.0f / scale_factor)
@@ -923,7 +1007,12 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
std::array<gpu::MailboxHolder, 3> mailbox_holders = {
request_properties.frame->mailbox_holder(0),
request_properties.frame->mailbox_holder(1), gpu::MailboxHolder{}};
- blit_request = BlitRequest(content_rect.origin(), mailbox_holders);
+
+ // TODO(https://crbug.com/775740): change the capturer to only request the
+ // parts of the frame that have changed whenever possible.
+ blit_request =
+ BlitRequest(content_rect.origin(), LetterboxingBehavior::kLetterbox,
+ mailbox_holders, true);
// We haven't captured the frame yet, but let's pretend that we did for the
// sake of blend information computation. We will be asking for an entire
@@ -1017,6 +1106,7 @@ void FrameSinkVideoCapturerImpl::DidCopyFrame(
scoped_refptr<media::VideoFrame>& frame = properties.frame;
const gfx::Rect& content_rect = properties.content_rect;
+
if (log_to_webrtc_ && consumer_) {
std::string format = "";
std::string strides = "";
@@ -1106,8 +1196,6 @@ void FrameSinkVideoCapturerImpl::DidCopyFrame(
} else {
frame = nullptr;
}
-
- UMA_HISTOGRAM_CAPTURE_SUCCEEDED("RGBA", success);
} else {
DCHECK_EQ(pixel_format_, media::PIXEL_FORMAT_NV12);
// NV12 is only supported for GMBs for now, in which case there is nothing
@@ -1127,6 +1215,13 @@ void FrameSinkVideoCapturerImpl::DidCopyFrame(
}
if (frame) {
+ // The result may be smaller than what was requested, if unforeseen
+ // clamping to the source boundaries occurred by the executor of the
+ // CopyOutputRequest. However, the result should never contain more than
+ // what was requested.
+ DCHECK_LE(result->size().width(), content_rect.width());
+ DCHECK_LE(result->size().height(), content_rect.height());
+
if (!frame->HasGpuMemoryBuffer()) {
// For GMB-backed video frames, overlays were already applied by
// CopyOutputRequest API. For in-memory frames, apply overlays here:
@@ -1141,15 +1236,21 @@ void FrameSinkVideoCapturerImpl::DidCopyFrame(
}
}
- // The result may be smaller than what was requested, if unforeseen
- // clamping to the source boundaries occurred by the executor of the
- // CopyOutputRequest. However, the result should never contain more than
- // what was requested.
- DCHECK_LE(result->size().width(), content_rect.width());
- DCHECK_LE(result->size().height(), content_rect.height());
- media::LetterboxVideoFrame(
- frame.get(), gfx::Rect(content_rect.origin(),
- AdjustSizeForPixelFormat(result->size())));
+ const gfx::Rect result_rect =
+ gfx::Rect(content_rect.origin(), result->size());
+ DCHECK(IsCompatibleWithFormat(result_rect, pixel_format_));
+
+ DVLOG(3) << __func__ << ": result->size()=" << result->size().ToString()
+ << ", content_rect=" << content_rect.ToString()
+ << ", result_rect=" << result_rect.ToString()
+ << ", frame=" << frame->AsHumanReadableString();
+
+ if (frame->visible_rect() != result_rect && !frame->HasGpuMemoryBuffer()) {
+ // If there are parts of the frame that are visible but we have not wrote
+ // into them, letterbox them. This is not needed for GMB-backed frames as
+ // the letterboxing happens on GPU.
+ media::LetterboxVideoFrame(frame.get(), result_rect);
+ }
if (ShouldMark(*frame, properties.content_version)) {
MarkFrame(frame, properties.content_version);
@@ -1220,11 +1321,13 @@ void FrameSinkVideoCapturerImpl::MaybeDeliverFrame(
// original source content.
base::TimeTicks media_ticks;
if (!oracle_->CompleteCapture(oracle_frame_number, !!frame, &media_ticks)) {
- // The following is used by
+ // Note: The following is used by
// chrome/browser/media/cast_mirroring_performance_browsertest.cc, in
- // addition to the usual runtime tracing.
- TRACE_EVENT_NESTABLE_ASYNC_END1("gpu.capture", "Capture",
- oracle_frame_number, "success", false);
+ // addition to the usual runtime tracing
+ // TODO(https://crbug.com/1322573): change to _NESTABLE_ variant of the
+ // macro once the bug is fixed.
+ TRACE_EVENT_ASYNC_END1("gpu.capture", "Capture", oracle_frame_number,
+ "success", false);
MaybeScheduleRefreshFrame();
return;
@@ -1236,12 +1339,14 @@ void FrameSinkVideoCapturerImpl::MaybeDeliverFrame(
}
frame->set_timestamp(media_ticks - *first_frame_media_ticks_);
- // The following is used by
+ // Note: The following is used by
// chrome/browser/media/cast_mirroring_performance_browsertest.cc, in
- // addition to the usual runtime tracing.
- TRACE_EVENT_NESTABLE_ASYNC_END2("gpu.capture", "Capture", oracle_frame_number,
- "success", true, "time_delta",
- frame->timestamp().InMicroseconds());
+ // addition to the usual runtime tracing
+ // TODO(https://crbug.com/1322573): change to _NESTABLE_ variant of the macro
+ // once the bug is fixed.
+ TRACE_EVENT_ASYNC_END2("gpu.capture", "Capture", oracle_frame_number,
+ "success", true, "time_delta",
+ frame->timestamp().InMicroseconds());
// Clone a handle to the shared memory backing the populated video frame, to
// send to the consumer.
diff --git a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
index 3e4f833cfdd..20565f12fb2 100644
--- a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
@@ -442,8 +442,9 @@ MATCHER_P2(IsLetterboxedFrame, color, content_rect, "") {
const VideoFrame& frame = *arg;
const gfx::Rect kContentRect = content_rect;
- const auto IsLetterboxedPlane = [&frame, kContentRect](int plane,
- uint8_t component) {
+
+ const auto IsLetterboxedPlane = [&frame, kContentRect, result_listener](
+ int plane, uint8_t component) {
gfx::Rect content_rect_copy = kContentRect;
if (plane != VideoFrame::kYPlane) {
content_rect_copy = gfx::Rect(
@@ -455,10 +456,19 @@ MATCHER_P2(IsLetterboxedFrame, color, content_rect, "") {
for (int col = 0; col < frame.row_bytes(plane); ++col) {
if (content_rect_copy.Contains(gfx::Point(col, row))) {
if (p[col] != component) {
+ *result_listener << " where pixel at (" << col << ", " << row
+ << ") should be inside content rectangle and the "
+ "component should match 0x"
+ << std::hex << component << " but is 0x"
+ << std::hex << static_cast<unsigned int>(p[col]);
return false;
}
} else { // Letterbox border around content.
if (plane == VideoFrame::kYPlane && p[col] != 0x00) {
+ *result_listener << " where pixel at (" << col << ", " << row
+ << ") should be outside content rectangle and the "
+ "component should match 0x00 but is 0x"
+ << std::hex << static_cast<unsigned int>(p[col]);
return false;
}
}
@@ -1138,6 +1148,45 @@ TEST_F(FrameSinkVideoCapturerTest, RefreshDemandsAreProperlyHandled) {
StopCapture();
}
+// Tests that the capturer honors requested refresh frames (see
+// crbug.com/1320798)
+TEST_F(FrameSinkVideoCapturerTest, HonorsRequestRefreshFrame) {
+ frame_sink_.SetCopyOutputColor(YUVColor{0x80, 0x80, 0x80});
+ ON_CALL(frame_sink_manager_, FindCapturableFrameSink(kVideoCaptureTarget))
+ .WillByDefault(Return(&frame_sink_));
+
+ capturer_->ChangeTarget(kVideoCaptureTarget, /*crop_version=*/0);
+
+ // Start off and consume the immediate refresh and copy result.
+ MockConsumer consumer;
+ StartCapture(&consumer);
+ frame_sink_.SendCopyOutputResult(0);
+ ASSERT_EQ(1, consumer.num_frames_received());
+ consumer.SendDoneNotification(0);
+
+ // Advance time to avoid being frame rate limited by the oracle.
+ // Demand a refresh frame. We should be past the minimum time to add one, so
+ // it should be done immediately.
+ AdvanceClockToNextVsync();
+ capturer_->RefreshNow();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(2, consumer.num_frames_received());
+
+ // Advance time to avoid being frame rate limited by the oracle.
+ // Request a refresh frame. The request should be serviced immediately.
+ AdvanceClockToNextVsync();
+ capturer_->RequestRefreshFrame();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(3, consumer.num_frames_received());
+
+ // Advance time to avoid being frame rate limited by the oracle.
+ // Request again and expect service.
+ AdvanceClockToNextVsync();
+ capturer_->RequestRefreshFrame();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(4, consumer.num_frames_received());
+}
+
// Tests that full capture happens on capture resolution change due to oracle,
// but only once and resurrected frames are used after that.
TEST_F(FrameSinkVideoCapturerTest,
diff --git a/chromium/components/viz/service/gl/DEPS b/chromium/components/viz/service/gl/DEPS
index 1510a5d8abd..f5fbbbfbc39 100644
--- a/chromium/components/viz/service/gl/DEPS
+++ b/chromium/components/viz/service/gl/DEPS
@@ -15,6 +15,7 @@ include_rules = [
"+ipc",
"+media/base/android/media_codec_util.h",
"+media/base/media_log.h",
+ "+media/base/win/mf_feature_checks.h",
"+media/gpu",
"+media/mojo",
"+mojo/public/cpp",
diff --git a/chromium/components/viz/service/gl/gpu_service_impl.cc b/chromium/components/viz/service/gl/gpu_service_impl.cc
index a965233a61b..0f9b617fd03 100644
--- a/chromium/components/viz/service/gl/gpu_service_impl.cc
+++ b/chromium/components/viz/service/gl/gpu_service_impl.cc
@@ -38,7 +38,6 @@
#include "gpu/ipc/common/gpu_memory_buffer_support.h"
#include "gpu/ipc/common/gpu_peak_memory.h"
#include "gpu/ipc/common/memory_stats.h"
-#include "gpu/ipc/in_process_command_buffer.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
@@ -99,6 +98,8 @@
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_WIN)
+#include "components/viz/common/overlay_state/win/overlay_state_service.h"
+#include "media/base/win/mf_feature_checks.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gl/dcomp_surface_registry.h"
#include "ui/gl/direct_composition_surface_win.h"
@@ -297,10 +298,8 @@ void GetVideoCapabilities(const gpu::GpuPreferences& gpu_preferences,
}
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
- if (media::MediaCodecUtil::IsH264EncoderAvailable(/*use_codec_list*/ false)) {
- vea_profile.profile = gpu::H264PROFILE_BASELINE;
- encoding_profiles.push_back(vea_profile);
- }
+ vea_profile.profile = gpu::H264PROFILE_BASELINE;
+ encoding_profiles.push_back(vea_profile);
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
// Note: Since Android doesn't have to support PPAPI/Flash, we have not
@@ -431,10 +430,18 @@ GpuServiceImpl::GpuServiceImpl(
#endif
#if BUILDFLAG(IS_WIN)
- auto info_callback = base::BindRepeating(
- &GpuServiceImpl::UpdateOverlayAndHDRInfo, weak_ptr_factory_.GetWeakPtr());
+ auto info_callback =
+ base::BindRepeating(&GpuServiceImpl::UpdateOverlayAndDXGIInfo,
+ weak_ptr_factory_.GetWeakPtr());
gl::DirectCompositionSurfaceWin::SetOverlayHDRGpuInfoUpdateCallback(
info_callback);
+
+ if (media::SupportMediaFoundationClearPlayback()) {
+ // Initialize the OverlayStateService using the GPUServiceImpl task
+ // sequence.
+ auto* overlay_state_service = OverlayStateService::GetInstance();
+ overlay_state_service->Initialize(base::SequencedTaskRunnerHandle::Get());
+ }
#endif
gpu_memory_buffer_factory_ =
@@ -806,7 +813,7 @@ void GpuServiceImpl::CreateJpegEncodeAccelerator(
void GpuServiceImpl::RegisterDCOMPSurfaceHandle(
mojo::PlatformHandle surface_handle,
RegisterDCOMPSurfaceHandleCallback callback) {
- auto token =
+ base::UnguessableToken token =
gl::DCOMPSurfaceRegistry::GetInstance()->RegisterDCOMPSurfaceHandle(
surface_handle.TakeHandle());
std::move(callback).Run(token);
@@ -894,23 +901,22 @@ void GpuServiceImpl::GetPeakMemoryUsage(uint32_t sequence_num,
weak_ptr_, sequence_num, std::move(callback)));
}
-void GpuServiceImpl::RequestHDRStatus(RequestHDRStatusCallback callback) {
+#if BUILDFLAG(IS_WIN)
+void GpuServiceImpl::RequestDXGIInfo(RequestDXGIInfoCallback callback) {
DCHECK(io_runner_->BelongsToCurrentThread());
main_runner_->PostTask(
- FROM_HERE, base::BindOnce(&GpuServiceImpl::RequestHDRStatusOnMainThread,
+ FROM_HERE, base::BindOnce(&GpuServiceImpl::RequestDXGIInfoOnMainThread,
weak_ptr_, std::move(callback)));
}
-void GpuServiceImpl::RequestHDRStatusOnMainThread(
- RequestHDRStatusCallback callback) {
+void GpuServiceImpl::RequestDXGIInfoOnMainThread(
+ RequestDXGIInfoCallback callback) {
DCHECK(main_runner_->BelongsToCurrentThread());
-
-#if BUILDFLAG(IS_WIN)
- hdr_enabled_ = gl::DirectCompositionSurfaceWin::IsHDRSupported();
-#endif
+ dxgi_info_ = gl::DirectCompositionSurfaceWin::GetDXGIInfo();
io_runner_->PostTask(FROM_HERE,
- base::BindOnce(std::move(callback), hdr_enabled_));
+ base::BindOnce(std::move(callback), dxgi_info_.Clone()));
}
+#endif
void GpuServiceImpl::RegisterDisplayContext(
gpu::DisplayContext* display_context) {
@@ -960,17 +966,6 @@ void GpuServiceImpl::DidLoseContext(bool offscreen,
gpu_host_->DidLoseContext(offscreen, reason, active_url);
}
-#if BUILDFLAG(IS_WIN)
-void GpuServiceImpl::DidUpdateOverlayInfo(
- const gpu::OverlayInfo& overlay_info) {
- gpu_host_->DidUpdateOverlayInfo(gpu_info_.overlay_info);
-}
-
-void GpuServiceImpl::DidUpdateHDRStatus(bool hdr_enabled) {
- gpu_host_->DidUpdateHDRStatus(hdr_enabled);
-}
-#endif
-
void GpuServiceImpl::StoreShaderToDisk(int client_id,
const std::string& key,
const std::string& shader) {
@@ -1260,7 +1255,7 @@ void GpuServiceImpl::OnForegroundedOnMainThread() {
#if !BUILDFLAG(IS_ANDROID)
void GpuServiceImpl::OnMemoryPressure(
- ::base::MemoryPressureListener::MemoryPressureLevel level) {
+ base::MemoryPressureListener::MemoryPressureLevel level) {
// Forward the notification to the registry of MemoryPressureListeners.
base::MemoryPressureListener::NotifyMemoryPressure(level);
}
@@ -1327,20 +1322,20 @@ gpu::Scheduler* GpuServiceImpl::GetGpuScheduler() {
}
#if BUILDFLAG(IS_WIN)
-void GpuServiceImpl::UpdateOverlayAndHDRInfo() {
+void GpuServiceImpl::UpdateOverlayAndDXGIInfo() {
gpu::OverlayInfo old_overlay_info = gpu_info_.overlay_info;
gpu::CollectHardwareOverlayInfo(&gpu_info_.overlay_info);
// Update overlay info in the GPU process and send the updated data back to
// the GPU host in the Browser process through mojom if the info has changed.
if (old_overlay_info != gpu_info_.overlay_info)
- DidUpdateOverlayInfo(gpu_info_.overlay_info);
+ gpu_host_->DidUpdateOverlayInfo(gpu_info_.overlay_info);
- // Update HDR status in the GPU process through the GPU host mojom.
- bool old_hdr_enabled_status = hdr_enabled_;
- hdr_enabled_ = gl::DirectCompositionSurfaceWin::IsHDRSupported();
- if (old_hdr_enabled_status != hdr_enabled_)
- DidUpdateHDRStatus(hdr_enabled_);
+ // Update DXGI adapter info in the GPU process through the GPU host mojom.
+ auto old_dxgi_info = std::move(dxgi_info_);
+ dxgi_info_ = gl::DirectCompositionSurfaceWin::GetDXGIInfo();
+ if (!mojo::Equals(dxgi_info_, old_dxgi_info))
+ gpu_host_->DidUpdateDXGIInfo(dxgi_info_.Clone());
}
#endif
diff --git a/chromium/components/viz/service/gl/gpu_service_impl.h b/chromium/components/viz/service/gl/gpu_service_impl.h
index 450ebc7730b..194edf37ff7 100644
--- a/chromium/components/viz/service/gl/gpu_service_impl.h
+++ b/chromium/components/viz/service/gl/gpu_service_impl.h
@@ -59,18 +59,15 @@ class GpuMemoryBufferFactory;
class GpuWatchdogThread;
class ImageDecodeAcceleratorWorker;
class Scheduler;
-class SyncPointManager;
+class SharedContextState;
class SharedImageManager;
+class SyncPointManager;
class VulkanImplementation;
} // namespace gpu
namespace media {
class MediaGpuChannelManager;
-}
-
-namespace gpu {
-class SharedContextState;
-} // namespace gpu
+} // namespace media
namespace viz {
@@ -194,8 +191,9 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
void StartPeakMemoryMonitor(uint32_t sequence_num) override;
void GetPeakMemoryUsage(uint32_t sequence_num,
GetPeakMemoryUsageCallback callback) override;
-
- void RequestHDRStatus(RequestHDRStatusCallback callback) override;
+#if BUILDFLAG(IS_WIN)
+ void RequestDXGIInfo(RequestDXGIInfoCallback callback) override;
+#endif
void LoadedShader(int32_t client_id,
const std::string& key,
const std::string& data) override;
@@ -236,10 +234,6 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
void DidLoseContext(bool offscreen,
gpu::error::ContextLostReason reason,
const GURL& active_url) override;
-#if BUILDFLAG(IS_WIN)
- void DidUpdateOverlayInfo(const gpu::OverlayInfo& overlay_info) override;
- void DidUpdateHDRStatus(bool hdr_enabled) override;
-#endif
void GetDawnInfo(GetDawnInfoCallback callback) override;
void StoreShaderToDisk(int client_id,
@@ -386,7 +380,9 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
#endif // BUILDFLAG(IS_CHROMEOS_ASH) &&
// BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
- void RequestHDRStatusOnMainThread(RequestHDRStatusCallback callback);
+#if BUILDFLAG(IS_WIN)
+ void RequestDXGIInfoOnMainThread(RequestDXGIInfoCallback callback);
+#endif
void OnBackgroundedOnMainThread();
void OnForegroundedOnMainThread();
@@ -403,7 +399,7 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
// Update overlay info and HDR status on the GPU process and send the updated
// info back to the browser process if there is a change.
#if BUILDFLAG(IS_WIN)
- void UpdateOverlayAndHDRInfo();
+ void UpdateOverlayAndDXGIInfo();
#endif
void GetDawnInfoOnMain(GetDawnInfoCallback callback);
@@ -425,7 +421,9 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
const gpu::GpuDriverBugWorkarounds gpu_driver_bug_workarounds_;
- bool hdr_enabled_ = false;
+#if BUILDFLAG(IS_WIN)
+ gfx::mojom::DXGIInfoPtr dxgi_info_;
+#endif
// What we would have gotten if we haven't fallen back to SwiftShader or
// pure software (in the viz case).
diff --git a/chromium/components/viz/service/main/viz_compositor_thread_runner.h b/chromium/components/viz/service/main/viz_compositor_thread_runner.h
index 4d69c164c50..69798c9c4ff 100644
--- a/chromium/components/viz/service/main/viz_compositor_thread_runner.h
+++ b/chromium/components/viz/service/main/viz_compositor_thread_runner.h
@@ -14,10 +14,6 @@ namespace base {
class SingleThreadTaskRunner;
}
-namespace gpu {
-class CommandBufferTaskExecutor;
-} // namespace gpu
-
namespace viz {
class GpuServiceImpl;
@@ -36,16 +32,12 @@ class VizCompositorThreadRunner {
base::flat_set<base::PlatformThreadId> thread_ids,
base::RepeatingClosure* wake_up_closure) = 0;
- // Creates FrameSinkManager from |params|. The version with |gpu_service| and
- // |task_executor| supports both GPU and software compositing, while the
- // version without supports only software compositing. Should be called from
- // the thread that owns |this| to initialize state on VizCompositorThread.
- virtual void CreateFrameSinkManager(
- mojom::FrameSinkManagerParamsPtr params) = 0;
- virtual void CreateFrameSinkManager(
- mojom::FrameSinkManagerParamsPtr params,
- gpu::CommandBufferTaskExecutor* task_executor,
- GpuServiceImpl* gpu_service) = 0;
+ // Creates FrameSinkManager from |params|. If |gpu_service| is null the
+ // display compositor will only support software compositing. Should be called
+ // from the thread that owns |this| to initialize state on
+ // VizCompositorThread.
+ virtual void CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params,
+ GpuServiceImpl* gpu_service) = 0;
};
} // namespace viz
diff --git a/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.cc b/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.cc
index 798434cf6df..6b7d8fcc565 100644
--- a/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.cc
+++ b/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.cc
@@ -28,7 +28,6 @@
#include "components/viz/service/performance_hint/hint_session.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_switches.h"
-#include "gpu/ipc/command_buffer_task_executor.h"
#include "gpu/ipc/scheduler_sequence.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
@@ -151,17 +150,7 @@ base::SingleThreadTaskRunner* VizCompositorThreadRunnerImpl::task_runner() {
}
void VizCompositorThreadRunnerImpl::CreateFrameSinkManager(
- mojom::FrameSinkManagerParamsPtr params) {
- task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&VizCompositorThreadRunnerImpl::
- CreateFrameSinkManagerOnCompositorThread,
- base::Unretained(this), std::move(params),
- nullptr, nullptr));
-}
-
-void VizCompositorThreadRunnerImpl::CreateFrameSinkManager(
mojom::FrameSinkManagerParamsPtr params,
- gpu::CommandBufferTaskExecutor* task_executor,
GpuServiceImpl* gpu_service) {
// All of the unretained objects are owned on the GPU thread and destroyed
// after VizCompositorThread has been shutdown.
@@ -169,18 +158,15 @@ void VizCompositorThreadRunnerImpl::CreateFrameSinkManager(
FROM_HERE, base::BindOnce(&VizCompositorThreadRunnerImpl::
CreateFrameSinkManagerOnCompositorThread,
base::Unretained(this), std::move(params),
- base::Unretained(task_executor),
base::Unretained(gpu_service)));
}
void VizCompositorThreadRunnerImpl::CreateFrameSinkManagerOnCompositorThread(
mojom::FrameSinkManagerParamsPtr params,
- gpu::CommandBufferTaskExecutor* task_executor,
GpuServiceImpl* gpu_service) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!frame_sink_manager_);
- if (features::IsUsingSkiaRenderer())
- gpu::SchedulerSequence::DefaultDisallowScheduleTaskOnCurrentThread();
+ gpu::SchedulerSequence::DefaultDisallowScheduleTaskOnCurrentThread();
server_shared_bitmap_manager_ = std::make_unique<ServerSharedBitmapManager>();
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
@@ -192,17 +178,14 @@ void VizCompositorThreadRunnerImpl::CreateFrameSinkManagerOnCompositorThread(
const bool run_all_compositor_stages_before_draw =
command_line->HasSwitch(switches::kRunAllCompositorStagesBeforeDraw);
- if (task_executor) {
- DCHECK(gpu_service);
+ if (gpu_service) {
// Create OutputSurfaceProvider usable for GPU + software compositing.
gpu_memory_buffer_manager_ =
std::make_unique<InProcessGpuMemoryBufferManager>(
gpu_service->gpu_memory_buffer_factory(),
gpu_service->sync_point_manager());
- auto* image_factory = gpu_service->gpu_image_factory();
- output_surface_provider_ = std::make_unique<OutputSurfaceProviderImpl>(
- gpu_service, task_executor, gpu_service,
- gpu_memory_buffer_manager_.get(), image_factory, headless);
+ output_surface_provider_ =
+ std::make_unique<OutputSurfaceProviderImpl>(gpu_service, headless);
// Create video frame pool context provider that will enable the frame sink
// manager to create GMB-backed video frames.
diff --git a/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.h b/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.h
index d0ea9a64371..c03ed0ef016 100644
--- a/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.h
+++ b/chromium/components/viz/service/main/viz_compositor_thread_runner_impl.h
@@ -52,9 +52,7 @@ class VizCompositorThreadRunnerImpl : public VizCompositorThreadRunner {
bool CreateHintSessionFactory(
base::flat_set<base::PlatformThreadId> thread_ids,
base::RepeatingClosure* wake_up_closure) override;
- void CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params) override;
void CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params,
- gpu::CommandBufferTaskExecutor* task_executor,
GpuServiceImpl* gpu_service) override;
private:
@@ -65,7 +63,6 @@ class VizCompositorThreadRunnerImpl : public VizCompositorThreadRunner {
void WakeUpOnCompositorThread();
void CreateFrameSinkManagerOnCompositorThread(
mojom::FrameSinkManagerParamsPtr params,
- gpu::CommandBufferTaskExecutor* task_executor,
GpuServiceImpl* gpu_service);
void TearDownOnCompositorThread();
diff --git a/chromium/components/viz/service/main/viz_main_impl.cc b/chromium/components/viz/service/main/viz_main_impl.cc
index f682cf1fced..8275099bb63 100644
--- a/chromium/components/viz/service/main/viz_main_impl.cc
+++ b/chromium/components/viz/service/main/viz_main_impl.cc
@@ -172,7 +172,7 @@ void VizMainImpl::CreateGpuService(
mojo::PendingRemote<
discardable_memory::mojom::DiscardableSharedMemoryManager>
discardable_memory_manager,
- mojo::ScopedSharedBufferHandle activity_flags,
+ base::UnsafeSharedMemoryRegion activity_flags_region,
gfx::FontRenderParams::SubpixelRendering subpixel_rendering) {
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
@@ -208,7 +208,7 @@ void VizMainImpl::CreateGpuService(
gpu_service_->InitializeWithHost(
gpu_host.Unbind(),
- gpu::GpuProcessActivityFlags(std::move(activity_flags)),
+ gpu::GpuProcessActivityFlags(std::move(activity_flags_region)),
gpu_init_->TakeDefaultOffscreenSurface(),
dependencies_.sync_point_manager, dependencies_.shared_image_manager,
dependencies_.shutdown_event);
@@ -261,16 +261,6 @@ void VizMainImpl::CreateFrameSinkManagerInternal(
DCHECK(gpu_service_);
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
- gl::GLSurfaceFormat format;
- // If we are running a SW Viz process, we may not have a default offscreen
- // surface.
- if (auto* offscreen_surface =
- gpu_service_->gpu_channel_manager()->default_offscreen_surface()) {
- format = offscreen_surface->GetFormat();
- } else {
- DCHECK_EQ(gl::GetGLImplementation(), gl::kGLImplementationDisabled);
- }
-
// When the host loses its connection to the viz process, it assumes the
// process has crashed and tries to reinitialize it. However, it is possible
// to have lost the connection for other reasons (e.g. deserialization
@@ -278,18 +268,11 @@ void VizMainImpl::CreateFrameSinkManagerInternal(
// FrameSinkManagerImpl, so just do a hard CHECK rather than crashing down the
// road so that all crash reports caused by this issue look the same and have
// the same signature. https://crbug.com/928845
- CHECK(!task_executor_);
-
- task_executor_ = std::make_unique<gpu::GpuInProcessThreadService>(
- this, gpu_thread_task_runner_, gpu_service_->GetGpuScheduler(),
- gpu_service_->sync_point_manager(), gpu_service_->mailbox_manager(),
- format, gpu_service_->gpu_feature_info(),
- gpu_service_->gpu_channel_manager()->gpu_preferences(),
- gpu_service_->shared_image_manager(),
- gpu_service_->gpu_channel_manager()->program_cache());
-
- viz_compositor_thread_runner_->CreateFrameSinkManager(
- std::move(params), task_executor_.get(), gpu_service_.get());
+ CHECK(!has_created_frame_sink_manager_);
+ has_created_frame_sink_manager_ = true;
+
+ viz_compositor_thread_runner_->CreateFrameSinkManager(std::move(params),
+ gpu_service_.get());
}
#if BUILDFLAG(USE_VIZ_DEBUGGER)
@@ -307,20 +290,6 @@ void VizMainImpl::StopDebugStream() {
}
#endif
-scoped_refptr<gpu::SharedContextState> VizMainImpl::GetSharedContextState() {
- // This method should be only called for GLRenderer and not for SkiaRenderer.
- // Hence adding DCHECK since DrDc only works with SkiaRenderer.
- DCHECK(!features::IsDrDcEnabled());
- return gpu_service_->GetContextState();
-}
-
-scoped_refptr<gl::GLShareGroup> VizMainImpl::GetShareGroup() {
- // This method should be only called for GLRenderer and not for SkiaRenderer.
- // Hence adding DCHECK since DrDc only works with SkiaRenderer.
- DCHECK(!features::IsDrDcEnabled());
- return gpu_service_->share_group();
-}
-
void VizMainImpl::ExitProcess(ExitCode immediate_exit_code) {
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
diff --git a/chromium/components/viz/service/main/viz_main_impl.h b/chromium/components/viz/service/main/viz_main_impl.h
index b2965ba9abf..74024c98bef 100644
--- a/chromium/components/viz/service/main/viz_main_impl.h
+++ b/chromium/components/viz/service/main/viz_main_impl.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/memory/raw_ptr.h"
+#include "base/memory/unsafe_shared_memory_region.h"
#include "base/process/process_handle.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread.h"
@@ -16,8 +17,6 @@
#include "components/viz/common/buildflags.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "components/viz/service/main/viz_compositor_thread_runner_impl.h"
-#include "gpu/ipc/gpu_in_process_thread_service.h"
-#include "gpu/ipc/in_process_command_buffer.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
@@ -50,8 +49,7 @@ namespace viz {
class InfoCollectionGpuServiceImpl;
#endif
-class VizMainImpl : public mojom::VizMain,
- public gpu::GpuInProcessThreadServiceDelegate {
+class VizMainImpl : public mojom::VizMain {
public:
class Delegate {
public:
@@ -120,7 +118,7 @@ class VizMainImpl : public mojom::VizMain,
mojo::PendingRemote<
discardable_memory::mojom::DiscardableSharedMemoryManager>
discardable_memory_manager,
- mojo::ScopedSharedBufferHandle activity_flags,
+ base::UnsafeSharedMemoryRegion activity_flags_region,
gfx::FontRenderParams::SubpixelRendering subpixel_rendering) override;
#if BUILDFLAG(IS_WIN)
void CreateInfoCollectionGpuService(
@@ -138,10 +136,6 @@ class VizMainImpl : public mojom::VizMain,
void StopDebugStream() override;
#endif
- // gpu::GpuInProcessThreadServiceDelegate implementation:
- scoped_refptr<gpu::SharedContextState> GetSharedContextState() override;
- scoped_refptr<gl::GLShareGroup> GetShareGroup() override;
-
GpuServiceImpl* gpu_service() { return gpu_service_.get(); }
const GpuServiceImpl* gpu_service() const { return gpu_service_.get(); }
@@ -179,22 +173,19 @@ class VizMainImpl : public mojom::VizMain,
std::unique_ptr<InfoCollectionGpuServiceImpl> info_collection_gpu_service_;
#endif
- // Allows the display compositor to use InProcessCommandBuffer to send GPU
- // commands to the GPU thread from the compositor thread. This must outlive
- // |viz_compositor_thread_runner_|.
- std::unique_ptr<gpu::CommandBufferTaskExecutor> task_executor_;
-
// If the gpu service is not yet ready then we stash pending
// FrameSinkManagerParams.
mojom::FrameSinkManagerParamsPtr pending_frame_sink_manager_params_;
+ bool has_created_frame_sink_manager_ = false;
+
// Runs the VizCompositorThread for the display compositor.
std::unique_ptr<VizCompositorThreadRunnerImpl>
viz_compositor_thread_runner_impl_;
// Note under Android WebView where VizCompositorThreadRunner is not created
// and owned by this, Viz does not interact with other objects in this class,
- // such as GpuServiceImpl or CommandBufferTaskExecutor. Code should take care
- // to avoid introducing such assumptions.
+ // such as GpuServiceImpl. Code should take care to avoid introducing such
+ // assumptions.
raw_ptr<VizCompositorThreadRunner> viz_compositor_thread_runner_ = nullptr;
const scoped_refptr<base::SingleThreadTaskRunner> gpu_thread_task_runner_;
diff --git a/chromium/components/viz/service/main/viz_main_impl_unittest.cc b/chromium/components/viz/service/main/viz_main_impl_unittest.cc
index abc59d12216..f5c74005135 100644
--- a/chromium/components/viz/service/main/viz_main_impl_unittest.cc
+++ b/chromium/components/viz/service/main/viz_main_impl_unittest.cc
@@ -57,11 +57,8 @@ class MockVizCompositorThreadRunner : public VizCompositorThreadRunner {
base::RepeatingClosure* wake_up_closure) override {
return false;
}
- MOCK_METHOD1(CreateFrameSinkManager, void(mojom::FrameSinkManagerParamsPtr));
- MOCK_METHOD3(CreateFrameSinkManager,
- void(mojom::FrameSinkManagerParamsPtr,
- gpu::CommandBufferTaskExecutor*,
- GpuServiceImpl*));
+ MOCK_METHOD2(CreateFrameSinkManager,
+ void(mojom::FrameSinkManagerParamsPtr, GpuServiceImpl*));
private:
const raw_ptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/chromium/components/viz/service/performance_hint/hint_session.cc b/chromium/components/viz/service/performance_hint/hint_session.cc
index ecb5a5f2281..1731aef08dc 100644
--- a/chromium/components/viz/service/performance_hint/hint_session.cc
+++ b/chromium/components/viz/service/performance_hint/hint_session.cc
@@ -35,6 +35,8 @@ using pAPerformanceHint_createSession =
const int32_t* threadIds,
size_t size,
int64_t initialTargetWorkDurationNanos);
+using pAPerformanceHint_updateTargetWorkDuration =
+ int (*)(APerformanceHintSession* session, int64_t targetDurationNanos);
using pAPerformanceHint_reportActualWorkDuration =
int (*)(APerformanceHintSession* session, int64_t actualDurationNanos);
using pAPerformanceHint_closeSession =
@@ -74,6 +76,7 @@ struct AdpfMethods {
LOAD_FUNCTION(main_dl_handle, APerformanceHint_getManager);
LOAD_FUNCTION(main_dl_handle, APerformanceHint_createSession);
+ LOAD_FUNCTION(main_dl_handle, APerformanceHint_updateTargetWorkDuration);
LOAD_FUNCTION(main_dl_handle, APerformanceHint_reportActualWorkDuration);
LOAD_FUNCTION(main_dl_handle, APerformanceHint_closeSession);
}
@@ -83,6 +86,8 @@ struct AdpfMethods {
bool supported = true;
pAPerformanceHint_getManager APerformanceHint_getManagerFn;
pAPerformanceHint_createSession APerformanceHint_createSessionFn;
+ pAPerformanceHint_updateTargetWorkDuration
+ APerformanceHint_updateTargetWorkDurationFn;
pAPerformanceHint_reportActualWorkDuration
APerformanceHint_reportActualWorkDurationFn;
pAPerformanceHint_closeSession APerformanceHint_closeSessionFn;
@@ -95,6 +100,7 @@ class AdpfHintSession : public HintSession {
base::TimeDelta target_duration);
~AdpfHintSession() override;
+ void UpdateTargetDuration(base::TimeDelta target_duration) override;
void ReportCpuCompletionTime(base::TimeDelta actual_duration) override;
void WakeUp();
@@ -102,7 +108,7 @@ class AdpfHintSession : public HintSession {
private:
APerformanceHintSession* const hint_session_;
HintSessionFactoryImpl* const factory_;
- const base::TimeDelta target_duration_;
+ base::TimeDelta target_duration_;
};
class HintSessionFactoryImpl : public HintSessionFactory {
@@ -143,6 +149,15 @@ AdpfHintSession::~AdpfHintSession() {
AdpfMethods::Get().APerformanceHint_closeSessionFn(hint_session_);
}
+void AdpfHintSession::UpdateTargetDuration(base::TimeDelta target_duration) {
+ DCHECK_CALLED_ON_VALID_THREAD(factory_->thread_checker_);
+ if (target_duration_ == target_duration)
+ return;
+ target_duration_ = target_duration;
+ AdpfMethods::Get().APerformanceHint_updateTargetWorkDurationFn(
+ hint_session_, target_duration.InNanoseconds());
+}
+
void AdpfHintSession::ReportCpuCompletionTime(base::TimeDelta actual_duration) {
DCHECK_CALLED_ON_VALID_THREAD(factory_->thread_checker_);
AdpfMethods::Get().APerformanceHint_reportActualWorkDurationFn(
diff --git a/chromium/components/viz/service/performance_hint/hint_session.h b/chromium/components/viz/service/performance_hint/hint_session.h
index a961ea93522..91b601465ff 100644
--- a/chromium/components/viz/service/performance_hint/hint_session.h
+++ b/chromium/components/viz/service/performance_hint/hint_session.h
@@ -22,6 +22,8 @@ class VIZ_SERVICE_EXPORT HintSession {
public:
virtual ~HintSession() = default;
+ virtual void UpdateTargetDuration(base::TimeDelta target_duration) = 0;
+
// `actual_duration` is compared to `target_duration` in `CreateSession` to
// determine the performance of a frame.
virtual void ReportCpuCompletionTime(base::TimeDelta actual_duration) = 0;
diff --git a/chromium/components/viz/service/surfaces/surface.cc b/chromium/components/viz/service/surfaces/surface.cc
index 900270f8b6d..9a68ef9a234 100644
--- a/chromium/components/viz/service/surfaces/surface.cc
+++ b/chromium/components/viz/service/surfaces/surface.cc
@@ -80,11 +80,13 @@ void Surface::PresentationHelper::DidPresent(
Surface::Surface(const SurfaceInfo& surface_info,
SurfaceManager* surface_manager,
SurfaceAllocationGroup* allocation_group,
- base::WeakPtr<SurfaceClient> surface_client)
+ base::WeakPtr<SurfaceClient> surface_client,
+ size_t max_uncommitted_frames)
: surface_info_(surface_info),
surface_manager_(surface_manager),
surface_client_(std::move(surface_client)),
- allocation_group_(allocation_group) {
+ allocation_group_(allocation_group),
+ max_uncommitted_frames_(max_uncommitted_frames) {
TRACE_EVENT_ASYNC_BEGIN1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"),
"Surface", this, "surface_info",
surface_info.ToString());
@@ -189,30 +191,70 @@ Surface::QueueFrameResult Surface::QueueFrame(
return QueueFrameResult::REJECTED;
}
- QueueFrameResult result = QueueFrameResult::ACCEPTED_ACTIVE;
+ // Receive and track the resources referenced from the CompositorFrame
+ // regardless of whether it's pending or active.
+ surface_client_->ReceiveFromChild(frame.resource_list);
+
+ QueueFrameResult result = QueueFrameResult::ACCEPTED_PENDING;
+
+ if (!max_uncommitted_frames_) {
+ result = CommitFrame(FrameData(std::move(frame), frame_index));
+ } else {
+ // Return oldest frame if uncommitted queue is full.
+ DCHECK_LE(uncommitted_frames_.size(), max_uncommitted_frames_);
+ if (uncommitted_frames_.size() == max_uncommitted_frames_) {
+ TRACE_EVENT_INSTANT1("viz", "DropUncommitedFrame",
+ TRACE_EVENT_SCOPE_THREAD, "queue_length",
+ uncommitted_frames_.size());
+
+ UnrefFrameResourcesAndRunCallbacks(
+ std::move(uncommitted_frames_.front()));
+ uncommitted_frames_.pop_front();
+ }
+
+ uncommitted_frames_.push_back(FrameData(std::move(frame), frame_index));
+
+ // If we still have space in queue we should send ack the client because we
+ // can receive another frame without dropping it.
+ if (uncommitted_frames_.size() < max_uncommitted_frames_) {
+ TRACE_EVENT_INSTANT1("viz", "AckingUncommitedFrame",
+ TRACE_EVENT_SCOPE_THREAD, "queue_length",
+ uncommitted_frames_.size());
+ uncommitted_frames_.back().SendAckIfNeeded(surface_client_.get());
+ }
+
+ surface_manager_->OnSurfaceHasNewUncommittedFrame(this);
+ }
+ // The frame should not fail to display beyond this point. Release the
+ // callback so it is not called.
+ std::ignore = frame_rejected_callback.Release();
+
+ return result;
+}
+
+Surface::QueueFrameResult Surface::CommitFrame(FrameData frame) {
+ TRACE_EVENT1("viz", "Surface::CommitFrame", "SurfaceId",
+ surface_id().ToString());
is_latency_info_taken_ = false;
if (active_frame_data_ || pending_frame_data_)
previous_frame_surface_id_ = surface_id();
- TakePendingLatencyInfo(&frame.metadata.latency_info);
+ TakePendingLatencyInfo(&frame.frame.metadata.latency_info);
absl::optional<FrameData> previous_pending_frame_data =
std::move(pending_frame_data_);
pending_frame_data_.reset();
- UpdateActivationDependencies(frame);
-
- // Receive and track the resources referenced from the CompositorFrame
- // regardless of whether it's pending or active.
- surface_client_->ReceiveFromChild(frame.resource_list);
+ UpdateActivationDependencies(frame.frame);
+ QueueFrameResult result = QueueFrameResult::ACCEPTED_ACTIVE;
if (activation_dependencies_.empty()) {
// If there are no blockers, then immediately activate the frame.
- ActivateFrame(FrameData(std::move(frame), frame_index));
+ ActivateFrame(std::move(frame));
} else {
- pending_frame_data_ = FrameData(std::move(frame), frame_index);
+ pending_frame_data_ = std::move(frame);
auto traced_value = std::make_unique<base::trace_event::TracedValue>();
traced_value->BeginArray("Pending");
@@ -240,9 +282,8 @@ Surface::QueueFrameResult Surface::QueueFrame(
// Returns resources for the previous pending frame.
UnrefFrameResourcesAndRunCallbacks(std::move(previous_pending_frame_data));
- // The frame should not fail to display beyond this point. Release the
- // callback so it is not called.
- std::ignore = frame_rejected_callback.Release();
+ if (surface_client_)
+ surface_client_->OnSurfaceCommitted(this);
return result;
}
@@ -340,6 +381,14 @@ Surface::FrameData& Surface::FrameData::operator=(FrameData&& other) = default;
Surface::FrameData::~FrameData() = default;
+void Surface::FrameData::SendAckIfNeeded(SurfaceClient* client) {
+ if (!frame_acked) {
+ frame_acked = true;
+ if (client)
+ client->SendCompositorFrameAck();
+ }
+}
+
void Surface::ActivatePendingFrame() {
DCHECK(pending_frame_data_);
FrameData frame_data = std::move(*pending_frame_data_);
@@ -356,6 +405,67 @@ void Surface::ActivatePendingFrame() {
ActivateFrame(std::move(frame_data));
}
+void Surface::CommitFramesRecursively(const CommitPredicate& predicate) {
+ TRACE_EVENT1("viz", "Surface::CommitFramesRecursively", "SurfaceId",
+ surface_id().ToString());
+
+ // This should only be called if we use uncommitted frames queue.
+ DCHECK_GT(max_uncommitted_frames_, 0u);
+
+ while (!uncommitted_frames_.empty()) {
+ const auto& ack =
+ uncommitted_frames_.front().frame.metadata.begin_frame_ack;
+
+ if (!predicate.Run(surface_id(), ack.frame_id))
+ break;
+
+ CommitFrame(std::move(uncommitted_frames_.front()));
+ uncommitted_frames_.pop_front();
+ }
+
+ if (HasPendingFrame()) {
+ for (auto& range : pending_frame_data_->frame.metadata.referenced_surfaces)
+ surface_manager_->CommitFramesInRangeRecursively(range, predicate);
+ }
+
+ if (HasActiveFrame()) {
+ for (auto& range : active_frame_data_->frame.metadata.referenced_surfaces)
+ surface_manager_->CommitFramesInRangeRecursively(range, predicate);
+ }
+
+ // If we freed up some space in queue send ack for the last frame if it's
+ // still unacked, so client can continue producing frames.
+ if (uncommitted_frames_.size() < max_uncommitted_frames_) {
+ if (!uncommitted_frames_.empty())
+ uncommitted_frames_.back().SendAckIfNeeded(surface_client_.get());
+
+ // Only last frame can be unacked because we ack frames as we put them in
+ // queue if queue isn't full. If we acked frame above, now verify that
+ // they all are acked, to ensure we ack frame in order.
+#if DCHECK_IS_ON()
+ for (auto& frames : uncommitted_frames_) {
+ DCHECK(frames.frame_acked);
+ }
+#endif
+ }
+}
+
+absl::optional<BeginFrameId> Surface::GetFirstUncommitedFrameId() {
+ if (uncommitted_frames_.empty())
+ return absl::nullopt;
+ return uncommitted_frames_.front().frame.metadata.begin_frame_ack.frame_id;
+}
+
+absl::optional<BeginFrameId> Surface::GetUncommitedFrameIdNewerThan(
+ const BeginFrameId& frame_id) {
+ for (auto& frame : uncommitted_frames_) {
+ if (frame.frame.metadata.begin_frame_ack.frame_id.IsNextInSequenceTo(
+ frame_id))
+ return frame.frame.metadata.begin_frame_ack.frame_id;
+ }
+ return absl::nullopt;
+}
+
void Surface::UpdateReferencedAllocationGroups(
std::vector<SurfaceAllocationGroup*> new_referenced_allocation_groups) {
base::flat_set<SurfaceAllocationGroup*> new_set(
@@ -668,11 +778,8 @@ Surface::TakePresentationHelperForPresentNotification() {
}
void Surface::SendAckToClient() {
- if (!active_frame_data_ || active_frame_data_->frame_acked)
- return;
- active_frame_data_->frame_acked = true;
- if (surface_client_)
- surface_client_->OnSurfaceProcessed(this);
+ if (active_frame_data_)
+ active_frame_data_->SendAckIfNeeded(surface_client_.get());
}
void Surface::MarkAsDrawn() {
@@ -718,8 +825,7 @@ void Surface::UnrefFrameResourcesAndRunCallbacks(
resource.sync_token.Clear();
surface_client_->UnrefResources(std::move(resources));
- if (!frame_data->frame_acked)
- surface_client_->OnSurfaceProcessed(this);
+ frame_data->SendAckIfNeeded(surface_client_.get());
// If we won't be getting a presented notification, we'll notify the client
// when the frame is unref'd.
@@ -790,7 +896,6 @@ void Surface::OnWillBeDrawn() {
}
void Surface::ActivatePendingFrameForInheritedDeadline() {
- DCHECK(HasPendingFrame());
// Deadline inheritance implies that this surface was blocking the embedder,
// so there shouldn't be an active frame.
DCHECK(!HasActiveFrame());
diff --git a/chromium/components/viz/service/surfaces/surface.h b/chromium/components/viz/service/surfaces/surface.h
index 30c4619c576..565c043f33e 100644
--- a/chromium/components/viz/service/surfaces/surface.h
+++ b/chromium/components/viz/service/surfaces/surface.h
@@ -17,6 +17,7 @@
#include "base/callback.h"
#include "base/callback_helpers.h"
+#include "base/containers/circular_deque.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/platform_thread.h"
@@ -53,24 +54,32 @@ class SurfaceManager;
// A Surface is a representation of a sequence of CompositorFrames with a
// common set of properties uniquely identified by a SurfaceId. In particular,
// all CompositorFrames submitted to a single Surface share properties described
-// in SurfaceInfo: device scale factor and size. A Surface can hold up to two
+// in SurfaceInfo: device scale factor and size. A Surface can hold up few
// CompositorFrames at a given time:
//
-// Active frame: An active frame is a candidate for display. A
-// CompositorFrame is active if it has been explicitly marked
-// as active after a deadline has passed or all its
-// dependencies are active.
+// Uncommitted frames: It's frame that has been received, but hasn't been
+// processed yet. There can be up to
+// `max_uncommitted_frames_` in this state. If
+// `max_uncommitted_frames_` is zero all frames are
+// committed as soon as they are received.
//
-// Pending frame: A pending CompositorFrame cannot be displayed on screen. A
-// CompositorFrame is pending if it has unresolved
-// dependencies: surface Ids to which there are no active
-// CompositorFrames.
+// Pending frame: A pending CompositorFrame cannot be displayed on
+// screen. A CompositorFrame is pending when it has been
+// committed but has unresolved dependencies: surface Ids
+// to which there are no active CompositorFrames. There
+// can be only one pending frame.
//
-// This two stage mechanism for managing CompositorFrames from a client exists
-// to enable best-effort synchronization across clients. A surface subtree will
-// remain pending until all dependencies are resolved: all clients have
-// submitted CompositorFrames corresponding to a new property of the subtree
-// (e.g. a new size).
+// Active frame: An active frame is a candidate for display. A
+// CompositorFrame is active if it has been explicitly
+// marked as active after a deadline has passed or all
+// its dependencies are active. There can be only one
+// active frame.
+//
+// This pending+active frame mechanism for managing CompositorFrames from a
+// client exists to enable best-effort synchronization across clients. A surface
+// subtree will remain pending until all dependencies are resolved: all clients
+// have submitted CompositorFrames corresponding to a new property of the
+// subtree (e.g. a new size).
//
// Clients are assumed to be untrusted and so a client may not submit a
// CompositorFrame to satisfy the dependency of the parent. Thus, by default, a
@@ -78,6 +87,13 @@ class SurfaceManager;
// deadline passes, then the CompositorFrame will activate despite missing
// dependencies. The activated CompositorFrame can specify fallback behavior in
// the event of missing dependencies at display time.
+//
+// On WebView display compositor runs asynchronously in regards of BeginFrames
+// and CompositorFrame submissions, to avoid frame drops due to racyness
+// uncommitted queue mechanism is used. When clients submits frame it goes to
+// the queue and when the display compositor draws frames are committed from
+// the queue to the pending or active frame.
+
class VIZ_SERVICE_EXPORT Surface final {
public:
class PresentationHelper {
@@ -103,10 +119,14 @@ class VIZ_SERVICE_EXPORT Surface final {
base::OnceCallback<void(const gfx::PresentationFeedback&)>;
enum QueueFrameResult { REJECTED, ACCEPTED_ACTIVE, ACCEPTED_PENDING };
+ using CommitPredicate =
+ base::RepeatingCallback<bool(const SurfaceId&, const BeginFrameId&)>;
+
Surface(const SurfaceInfo& surface_info,
SurfaceManager* surface_manager,
SurfaceAllocationGroup* allocation_group,
- base::WeakPtr<SurfaceClient> surface_client);
+ base::WeakPtr<SurfaceClient> surface_client,
+ size_t max_uncommitted_frames);
Surface(const Surface&) = delete;
Surface& operator=(const Surface&) = delete;
@@ -137,14 +157,19 @@ class VIZ_SERVICE_EXPORT Surface final {
void SetPreviousFrameSurface(Surface* surface);
- // Returns false if |frame| is invalid.
- // |frame_rejected_callback| will be called once if the frame will not be
- // displayed.
+ // Returns false if |frame| is invalid. |frame_rejected_callback| will be
+ // called once if the frame will not be displayed.
QueueFrameResult QueueFrame(
CompositorFrame frame,
uint64_t frame_index,
base::ScopedClosureRunner frame_rejected_callback);
+ // Commits frame(s) in this Surface and its dependencies. For each affected
+ // surface, the predicate will be called for each uncommitted frame in each
+ // surface from the oldest to the newest and will abort at first case of
+ // returning false.
+ void CommitFramesRecursively(const CommitPredicate& predicate);
+
// Notifies the Surface that a blocking SurfaceId now has an active
// frame.
void NotifySurfaceIdAvailable(const SurfaceId& surface_id);
@@ -286,6 +311,14 @@ class VIZ_SERVICE_EXPORT Surface final {
void DidAggregate();
+ // Returns frame id of the oldest uncommitted frame if any,
+ absl::optional<BeginFrameId> GetFirstUncommitedFrameId();
+
+ // Returns frame id of the oldest uncommitted frame that is newer than
+ // provided `frame_id`.
+ absl::optional<BeginFrameId> GetUncommitedFrameIdNewerThan(
+ const BeginFrameId& frame_id);
+
private:
struct FrameData {
FrameData(CompositorFrame&& frame, uint64_t frame_index);
@@ -299,6 +332,8 @@ class VIZ_SERVICE_EXPORT Surface final {
return std::move(frame.metadata.delegated_ink_metadata);
}
+ void SendAckIfNeeded(SurfaceClient* client);
+
CompositorFrame frame;
uint64_t frame_index;
// Whether the frame has been displayed or not.
@@ -337,6 +372,10 @@ class VIZ_SERVICE_EXPORT Surface final {
// Called when all of the surface's dependencies have been resolved.
void ActivateFrame(FrameData frame_data);
+ // Called when display compositor is ready for this frame to be processed and
+ // it can become pending or active.
+ QueueFrameResult CommitFrame(FrameData frame);
+
// Resolve the activation deadline specified by |current_frame| into a wall
// time to be used by SurfaceDependencyDeadline.
FrameDeadline ResolveFrameDeadline(const CompositorFrame& current_frame);
@@ -362,6 +401,10 @@ class VIZ_SERVICE_EXPORT Surface final {
absl::optional<FrameData> pending_frame_data_;
absl::optional<FrameData> active_frame_data_;
+
+ // Queue of uncommitted frames, oldest first.
+ base::circular_deque<FrameData> uncommitted_frames_;
+
absl::optional<CompositorFrame> interpolated_frame_;
bool seen_first_frame_activation_ = false;
bool seen_first_surface_embedding_ = false;
@@ -397,6 +440,8 @@ class VIZ_SERVICE_EXPORT Surface final {
bool has_damage_from_interpolated_frame_ = false;
+ const size_t max_uncommitted_frames_;
+
base::WeakPtrFactory<Surface> weak_factory_{this};
};
diff --git a/chromium/components/viz/service/surfaces/surface_allocation_group.h b/chromium/components/viz/service/surfaces/surface_allocation_group.h
index 29cc1418134..8a89cccff64 100644
--- a/chromium/components/viz/service/surfaces/surface_allocation_group.h
+++ b/chromium/components/viz/service/surfaces/surface_allocation_group.h
@@ -127,6 +127,8 @@ class VIZ_SERVICE_EXPORT SurfaceAllocationGroup {
return surfaces_.empty() ? nullptr : surfaces_.back();
}
+ const std::vector<Surface*>& surfaces() const { return surfaces_; }
+
private:
// Returns an iterator to the latest surface in |surfaces_| whose SurfaceId is
// older than or equal to |surface_id|. The returned surface may not be active
diff --git a/chromium/components/viz/service/surfaces/surface_client.h b/chromium/components/viz/service/surfaces/surface_client.h
index 19e303ca26f..37482e1a41d 100644
--- a/chromium/components/viz/service/surfaces/surface_client.h
+++ b/chromium/components/viz/service/surfaces/surface_client.h
@@ -40,6 +40,10 @@ class VIZ_SERVICE_EXPORT SurfaceClient {
virtual ~SurfaceClient() = default;
+ // Called when |surface| has committed a new CompositorFrame that become
+ // pending or active.
+ virtual void OnSurfaceCommitted(Surface* surface) = 0;
+
// Called when |surface| has a new CompositorFrame available for display.
virtual void OnSurfaceActivated(Surface* surface) = 0;
@@ -73,9 +77,9 @@ class VIZ_SERVICE_EXPORT SurfaceClient {
// Notifies the client that a frame with |token| has been activated.
virtual void OnFrameTokenChanged(uint32_t frame_token) = 0;
- // Notifies the client that the submitted CompositorFrame has been processed
- // (where processed may mean the frame has been displayed, or discarded).
- virtual void OnSurfaceProcessed(Surface* surface) = 0;
+ // Sends a compositor frame ack to the client. Usually happens when viz is
+ // ready to receive another frame without dropping previous one.
+ virtual void SendCompositorFrameAck() = 0;
// Notifies the client that a frame with |token| has been presented.
virtual void OnSurfacePresented(
diff --git a/chromium/components/viz/service/surfaces/surface_manager.cc b/chromium/components/viz/service/surfaces/surface_manager.cc
index 43a0037f5de..924fe2d7b13 100644
--- a/chromium/components/viz/service/surfaces/surface_manager.cc
+++ b/chromium/components/viz/service/surfaces/surface_manager.cc
@@ -38,12 +38,14 @@ constexpr base::TimeDelta kExpireInterval = base::Seconds(10);
SurfaceManager::SurfaceManager(
SurfaceManagerDelegate* delegate,
- absl::optional<uint32_t> activation_deadline_in_frames)
+ absl::optional<uint32_t> activation_deadline_in_frames,
+ size_t max_uncommitted_frames)
: delegate_(delegate),
activation_deadline_in_frames_(activation_deadline_in_frames),
root_surface_id_(FrameSinkId(0u, 0u),
LocalSurfaceId(1u, base::UnguessableToken::Create())),
- tick_clock_(base::DefaultTickClock::GetInstance()) {
+ tick_clock_(base::DefaultTickClock::GetInstance()),
+ max_uncommitted_frames_(max_uncommitted_frames) {
thread_checker_.DetachFromThread();
// Android WebView doesn't have a task runner and doesn't need the timer.
@@ -113,8 +115,9 @@ Surface* SurfaceManager::CreateSurface(
if (!allocation_group)
return nullptr;
- std::unique_ptr<Surface> surface = std::make_unique<Surface>(
- surface_info, this, allocation_group, surface_client);
+ std::unique_ptr<Surface> surface =
+ std::make_unique<Surface>(surface_info, this, allocation_group,
+ surface_client, max_uncommitted_frames_);
surface->SetDependencyDeadline(
std::make_unique<SurfaceDependencyDeadline>(tick_clock_));
surface_map_[surface_info.id()] = std::move(surface);
@@ -450,6 +453,11 @@ void SurfaceManager::FirstSurfaceActivation(const SurfaceInfo& surface_info) {
observer.OnFirstSurfaceActivation(surface_info);
}
+void SurfaceManager::OnSurfaceHasNewUncommittedFrame(Surface* surface) {
+ for (auto& observer : observer_list_)
+ observer.OnSurfaceHasNewUncommittedFrame(surface->surface_id());
+}
+
void SurfaceManager::SurfaceActivated(Surface* surface) {
// Trigger a display frame if necessary.
const CompositorFrameMetadata& metadata = surface->GetActiveFrameMetadata();
@@ -629,4 +637,33 @@ void SurfaceManager::AggregatedFrameSinksChanged() {
delegate_->AggregatedFrameSinksChanged();
}
+void SurfaceManager::CommitFramesInRangeRecursively(
+ const SurfaceRange& range,
+ const CommitPredicate& predicate) {
+ // Technically we need only latest active surface, but because activation will
+ // happen during commit, it's impossible to predict which one will be active,
+ // so we're committing all surfaces in range.
+
+ // If start of the range is in a different allocation group, process it first
+ // to keep activation in order.
+ if (range.start() && range.start()->local_surface_id().embed_token() !=
+ range.end().local_surface_id().embed_token()) {
+ if (auto* allocation_group =
+ GetAllocationGroupForSurfaceId(*range.start())) {
+ for (auto* surface : allocation_group->surfaces()) {
+ if (range.IsInRangeInclusive(surface->surface_id()))
+ surface->CommitFramesRecursively(predicate);
+ }
+ }
+ }
+
+ // Process the allocation group of the end of the range.
+ if (auto* allocation_group = GetAllocationGroupForSurfaceId(range.end())) {
+ for (auto* surface : allocation_group->surfaces()) {
+ if (range.IsInRangeInclusive(surface->surface_id()))
+ surface->CommitFramesRecursively(predicate);
+ }
+ }
+}
+
} // namespace viz
diff --git a/chromium/components/viz/service/surfaces/surface_manager.h b/chromium/components/viz/service/surfaces/surface_manager.h
index b20b20bb0a1..36ca45d07e9 100644
--- a/chromium/components/viz/service/surfaces/surface_manager.h
+++ b/chromium/components/viz/service/surfaces/surface_manager.h
@@ -44,11 +44,13 @@ class SurfaceManagerDelegate;
class SurfaceRange;
struct BeginFrameAck;
struct BeginFrameArgs;
+struct BeginFrameId;
class VIZ_SERVICE_EXPORT SurfaceManager {
public:
SurfaceManager(SurfaceManagerDelegate* delegate,
- absl::optional<uint32_t> activation_deadline_in_frames);
+ absl::optional<uint32_t> activation_deadline_in_frames,
+ size_t max_uncommitted_frames);
SurfaceManager(const SurfaceManager&) = delete;
SurfaceManager& operator=(const SurfaceManager&) = delete;
@@ -108,6 +110,9 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
// Called when a surface has an active frame for the first time.
void FirstSurfaceActivation(const SurfaceInfo& surface_info);
+ // Called when there is new frame in uncommitted queue of the surface.
+ void OnSurfaceHasNewUncommittedFrame(Surface* surface);
+
// Called when a CompositorFrame within |surface| has activated.
void SurfaceActivated(Surface* surface);
@@ -200,6 +205,15 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
// changed since the previous aggregation.
void AggregatedFrameSinksChanged();
+ using CommitPredicate =
+ base::RepeatingCallback<bool(const SurfaceId&, const BeginFrameId&)>;
+ // Commits all surfaces in range and their referenced surfaces. For each
+ // surface processed calls `predicate` for each uncommitted frame from oldest
+ // to newest. If predicate returns true, surface is committed. If not the
+ // surface processing stops and we go to the next surface.
+ void CommitFramesInRangeRecursively(const SurfaceRange& range,
+ const CommitPredicate& predicate);
+
private:
friend class CompositorFrameSinkSupportTest;
friend class FrameSinkManagerTest;
@@ -326,6 +340,10 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
bool allocation_groups_need_garbage_collection_ = false;
+ // Maximum length of uncommitted queue, zero means all frames are committed
+ // automatically.
+ const size_t max_uncommitted_frames_;
+
base::WeakPtrFactory<SurfaceManager> weak_factory_{this};
};
diff --git a/chromium/components/viz/service/surfaces/surface_observer.h b/chromium/components/viz/service/surfaces/surface_observer.h
index 7bbb7b9c499..6bb23de3ad0 100644
--- a/chromium/components/viz/service/surfaces/surface_observer.h
+++ b/chromium/components/viz/service/surfaces/surface_observer.h
@@ -23,6 +23,9 @@ class VIZ_SERVICE_EXPORT SurfaceObserver {
// time.
virtual void OnFirstSurfaceActivation(const SurfaceInfo& surface_info) {}
+ // Called when there is new frame in uncommitted queue of the surface.
+ virtual void OnSurfaceHasNewUncommittedFrame(const SurfaceId& surface_id) {}
+
// Called when a CompositorFrame within a surface corresponding to
// |surface_id| activates.
virtual void OnSurfaceActivated(const SurfaceId& surface_id) {}
diff --git a/chromium/components/viz/service/transitions/surface_animation_manager.cc b/chromium/components/viz/service/transitions/surface_animation_manager.cc
index 6877eacd5e4..53696dc22a2 100644
--- a/chromium/components/viz/service/transitions/surface_animation_manager.cc
+++ b/chromium/components/viz/service/transitions/surface_animation_manager.cc
@@ -182,8 +182,9 @@ void ReplaceSharedElementWithRenderPass(
auto* render_pass_quad =
target_render_pass
->CreateAndAppendDrawQuad<CompositorRenderPassDrawQuad>();
- gfx::RectF tex_coord_rect(
- gfx::SizeF(shared_element_content_pass->output_rect.size()));
+ gfx::RectF tex_coord_rect(gfx::SizeF(shared_element_quad.rect.size()));
+ tex_coord_rect.Offset(-shared_pass_output_rect.x(),
+ -shared_pass_output_rect.y());
render_pass_quad->SetNew(
/*shared_quad_state=*/copied_quad_state,
/*rect=*/shared_element_quad.rect,
@@ -322,9 +323,7 @@ class SurfaceAnimationManager::StorageWithSurface {
SurfaceAnimationManager::SurfaceAnimationManager(
SharedBitmapManager* shared_bitmap_manager)
- : animation_slowdown_factor_(
- switches::GetDocumentTransitionSlowDownFactor()),
- transferable_resource_tracker_(shared_bitmap_manager) {}
+ : transferable_resource_tracker_(shared_bitmap_manager) {}
SurfaceAnimationManager::~SurfaceAnimationManager() = default;
@@ -1069,10 +1068,8 @@ void SurfaceAnimationManager::CreateRootAnimationCurves(
: gfx::CubicBezierTimingFunction::EaseType::EASE_OUT);
// Create the transform curve.
- base::TimeDelta total_duration =
- ApplySlowdownFactor(save_directive_->root_config().duration);
- base::TimeDelta total_delay =
- ApplySlowdownFactor(save_directive_->root_config().delay);
+ base::TimeDelta total_duration = save_directive_->root_config().duration;
+ base::TimeDelta total_delay = save_directive_->root_config().delay;
// The transform animation runs for the entire duration of the root
// transition.
@@ -1131,14 +1128,14 @@ void SurfaceAnimationManager::CreateSharedElementCurves() {
const bool has_src_element = shared.has_value();
const auto& config = save_directive_->shared_elements()[i].config;
- const auto total_duration = ApplySlowdownFactor(config.duration);
- const auto total_delay = ApplySlowdownFactor(config.delay);
+ const auto total_duration = config.duration;
+ const auto total_delay = config.delay;
- const auto opacity_duration = ApplySlowdownFactor(
- total_duration * kSharedOpacityTransitionDurationScaleFactor);
- const auto opacity_delay = ApplySlowdownFactor(
+ const auto opacity_duration =
+ total_duration * kSharedOpacityTransitionDurationScaleFactor;
+ const auto opacity_delay =
total_delay +
- (total_duration * kSharedOpacityTransitionDelayScaleFactor));
+ (total_duration * kSharedOpacityTransitionDelayScaleFactor);
// The kSrcOpacity curve animates the screen space opacity applied to the
// blended content from src and dest elements. The value goes from the
@@ -1320,11 +1317,6 @@ SurfaceAnimationManager::GetSurfaceSavedFrameStorageForTesting() {
return &surface_saved_frame_storage_;
}
-base::TimeDelta SurfaceAnimationManager::ApplySlowdownFactor(
- base::TimeDelta original) const {
- return original * animation_slowdown_factor_;
-}
-
// RootAnimationState
SurfaceAnimationManager::RootAnimationState::RootAnimationState() = default;
SurfaceAnimationManager::RootAnimationState::RootAnimationState(
diff --git a/chromium/components/viz/service/transitions/surface_animation_manager.h b/chromium/components/viz/service/transitions/surface_animation_manager.h
index 03efd964403..03a96239c3c 100644
--- a/chromium/components/viz/service/transitions/surface_animation_manager.h
+++ b/chromium/components/viz/service/transitions/surface_animation_manager.h
@@ -159,8 +159,6 @@ class VIZ_SERVICE_EXPORT SurfaceAnimationManager {
// Returns true if we have a running animation for root or shared elements.
bool HasRunningAnimations() const;
- base::TimeDelta ApplySlowdownFactor(base::TimeDelta original) const;
-
// The state machine can take the following paths :
// 1) Viz driven animation : kIdle -> kAnimating -> kLastFrame -> kIdle
// 2) Renderer driven animation : kIdle -> kAnimatingRenderer -> kIdle
@@ -170,7 +168,6 @@ class VIZ_SERVICE_EXPORT SurfaceAnimationManager {
uint32_t last_processed_sequence_id_ = 0;
- const int animation_slowdown_factor_ = 1;
TransferableResourceTracker transferable_resource_tracker_;
absl::optional<TransferableResourceTracker::ResourceFrame> saved_textures_;
diff --git a/chromium/components/viz/test/BUILD.gn b/chromium/components/viz/test/BUILD.gn
index 147c6dfd364..ba5d6888d89 100644
--- a/chromium/components/viz/test/BUILD.gn
+++ b/chromium/components/viz/test/BUILD.gn
@@ -11,7 +11,6 @@ buildflag_header("buildflags") {
header = "buildflags.h"
flags = [
- "ENABLE_GL_RENDERER_TESTS=$enable_gl_renderer_tests",
"ENABLE_GL_BACKEND_TESTS=$enable_gl_backend_tests",
"ENABLE_VULKAN_BACKEND_TESTS=$enable_vulkan_backend_tests",
"ENABLE_DAWN_BACKEND_TESTS=$enable_dawn_backend_tests",
diff --git a/chromium/components/viz/viz.gni b/chromium/components/viz/viz.gni
index 8cf273608e3..2a5d9d72722 100644
--- a/chromium/components/viz/viz.gni
+++ b/chromium/components/viz/viz.gni
@@ -10,10 +10,6 @@ import("//testing/test.gni")
# that code path.
enable_gl_backend_tests = !is_fuchsia
-# Controls if GLRenderer related tests should be built and run.
-# TODO(crbug.com/1247756): Delete this flag along with GLRenderer.
-enable_gl_renderer_tests = false
-
# TODO(samans): Support more configurations.
# CFI issue: https://crbug.com/967819
# Fuchsia ARM64 https://crbug.com/1058247
@@ -72,8 +68,5 @@ template("viz_test") {
}
configs -= viz_remove_configs
configs += viz_add_configs
-
- # TODO(crbug.com/1292951): Remove this line.
- configs -= [ "//build/config/compiler:prevent_unsafe_narrowing" ]
}
}
diff --git a/chromium/components/web_app_resources/web_app_default_offline.html b/chromium/components/web_app_resources/web_app_default_offline.html
index e037b8d0f44..407b8331908 100644
--- a/chromium/components/web_app_resources/web_app_default_offline.html
+++ b/chromium/components/web_app_resources/web_app_default_offline.html
@@ -17,8 +17,8 @@
</head>
<body style="font-family: $i18n{fontfamily};font-size:$i18n{fontsize}">
<img id="icon" src=$i18n{icon_url}>
- <h1>$i18n{app_short_name}</h1>
+ <h1 id="app-name">$i18n{app_short_name}</h1>
<h2 id="default-web-app-msg">$i18n{web_app_default_offline_message}</h2>
<!--TODO(crbug.com/1285723: Add web app icon.)-->
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/chromium/components/web_cache/OWNERS b/chromium/components/web_cache/OWNERS
index 3463baf5785..da1f2df7940 100644
--- a/chromium/components/web_cache/OWNERS
+++ b/chromium/components/web_cache/OWNERS
@@ -1,5 +1,4 @@
# Reviewers:
-jochen@chromium.org
sky@chromium.org
thakis@chromium.org
thestig@chromium.org
diff --git a/chromium/components/web_cache/browser/web_cache_manager.cc b/chromium/components/web_cache/browser/web_cache_manager.cc
index 3b7fcf34dcb..7eb41d56383 100644
--- a/chromium/components/web_cache/browser/web_cache_manager.cc
+++ b/chromium/components/web_cache/browser/web_cache_manager.cc
@@ -25,6 +25,11 @@ using base::Time;
namespace web_cache {
+constexpr uint64_t kNoCapacitySet = std::numeric_limits<uint64_t>::max();
+
+WebCacheManager::WebCacheInfo::WebCacheInfo() : last_capacity(kNoCapacitySet){};
+WebCacheManager::WebCacheInfo::~WebCacheInfo() = default;
+
static const int kReviseAllocationDelayMS = 200;
// The default size limit of the in-memory cache is 8 MB
@@ -75,7 +80,7 @@ void WebCacheManager::Add(int renderer_id) {
if (host) {
mojo::Remote<mojom::WebCache> service;
host->BindReceiver(service.BindNewPipeAndPassReceiver());
- web_cache_services_[renderer_id] = std::move(service);
+ web_cache_services_[renderer_id].service = std::move(service);
}
// Revise our allocation strategy to account for this new renderer.
@@ -280,24 +285,24 @@ void WebCacheManager::AddToStrategy(const std::set<int>& renderers,
}
void WebCacheManager::EnactStrategy(const AllocationStrategy& strategy) {
- // Inform each render process of its cache allocation.
- auto allocation = strategy.begin();
- while (allocation != strategy.end()) {
+ for (auto& [render_process_id, new_capacity] : strategy) {
content::RenderProcessHost* host =
- content::RenderProcessHost::FromID(allocation->first);
- if (host) {
- // This is the capacity this renderer has been allocated.
- uint64_t capacity = allocation->second;
+ content::RenderProcessHost::FromID(render_process_id);
+ if (!host)
+ continue;
- // Find the mojo::Remote<WebCache> by renderer process id.
- auto it = web_cache_services_.find(allocation->first);
- if (it != web_cache_services_.end()) {
- const mojo::Remote<mojom::WebCache>& service = it->second;
- DCHECK(service);
- service->SetCacheCapacity(capacity);
- }
- }
- ++allocation;
+ // Find the mojo::Remote<WebCache> by renderer process id.
+ auto it = web_cache_services_.find(render_process_id);
+ if (it == web_cache_services_.end())
+ continue;
+
+ WebCacheInfo& cache_info = it->second;
+ if (cache_info.last_capacity == new_capacity)
+ continue;
+
+ DCHECK(cache_info.service);
+ cache_info.service->SetCacheCapacity(new_capacity);
+ cache_info.last_capacity = new_capacity;
}
}
@@ -318,9 +323,9 @@ void WebCacheManager::ClearRendererCache(
// Find the mojo::Remote<WebCache> by renderer process id.
auto it = web_cache_services_.find(*iter);
if (it != web_cache_services_.end()) {
- const mojo::Remote<mojom::WebCache>& service = it->second;
- DCHECK(service);
- service->ClearCache(occasion == ON_NAVIGATION);
+ WebCacheInfo& cache_info = it->second;
+ DCHECK(cache_info.service);
+ cache_info.service->ClearCache(occasion == ON_NAVIGATION);
}
}
}
@@ -332,6 +337,8 @@ void WebCacheManager::ReviseAllocationStrategy() {
DCHECK(stats_.size() <=
active_renderers_.size() + inactive_renderers_.size());
+ callback_pending_ = false;
+
// Check if renderers have gone inactive.
FindInactiveRenderers();
@@ -383,6 +390,12 @@ void WebCacheManager::ReviseAllocationStrategyLater() {
if (base::FeatureList::IsEnabled(kTrimWebCacheOnMemoryPressureOnly))
return;
+ // Avoid piling up notifications.
+ if (callback_pending_)
+ return;
+
+ callback_pending_ = true;
+
// Ask to be called back in a few milliseconds to actually recompute our
// allocation.
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
diff --git a/chromium/components/web_cache/browser/web_cache_manager.h b/chromium/components/web_cache/browser/web_cache_manager.h
index 783feafb3b4..4e1fb343946 100644
--- a/chromium/components/web_cache/browser/web_cache_manager.h
+++ b/chromium/components/web_cache/browser/web_cache_manager.h
@@ -139,8 +139,14 @@ class WebCacheManager : public content::RenderProcessHostCreationObserver,
// each renderer is permitted to consume for its cache.
typedef std::list<Allocation> AllocationStrategy;
+ struct WebCacheInfo {
+ WebCacheInfo();
+ ~WebCacheInfo();
+ mojo::Remote<mojom::WebCache> service;
+ uint64_t last_capacity;
+ };
// The key is the unique id of every render process host.
- typedef std::map<int, mojo::Remote<mojom::WebCache>> WebCacheServicesMap;
+ typedef std::map<int, WebCacheInfo> WebCacheServicesMap;
// This class is a singleton. Do not instantiate directly. Call GetInstance()
// instead.
@@ -245,6 +251,9 @@ class WebCacheManager : public content::RenderProcessHostCreationObserver,
// recently than they have been active.
std::set<int> inactive_renderers_;
+ // True if a delayed call to ReviseAllocationStrategy() is pending.
+ bool callback_pending_ = false;
+
// Maps every renderer_id with its corresponding
// mojo::Remote<mojom::WebCache>.
WebCacheServicesMap web_cache_services_;
diff --git a/chromium/components/web_package/OWNERS b/chromium/components/web_package/OWNERS
index db572530edc..5c41d86bf11 100644
--- a/chromium/components/web_package/OWNERS
+++ b/chromium/components/web_package/OWNERS
@@ -1,2 +1 @@
-horo@chromium.org
ksakamoto@chromium.org
diff --git a/chromium/components/web_package/web_bundle_builder.cc b/chromium/components/web_package/web_bundle_builder.cc
index efce846e4bc..7d3a8415f18 100644
--- a/chromium/components/web_package/web_bundle_builder.cc
+++ b/chromium/components/web_package/web_bundle_builder.cc
@@ -155,8 +155,7 @@ cbor::Value WebBundleBuilder::CreateEncodedSigned(
std::vector<uint8_t> WebBundleBuilder::CreateTopLevel() {
cbor::Value::ArrayValue toplevel_array;
- toplevel_array.emplace_back(
- CreateByteString(u8"\U0001F310\U0001F4E6")); // "ðŸŒðŸ“¦"
+ toplevel_array.emplace_back(CreateByteString("ðŸŒðŸ“¦"));
toplevel_array.emplace_back(CreateByteString(base::StringPiece("b2\0\0", 4)));
toplevel_array.emplace_back(Encode(cbor::Value(section_lengths_)));
toplevel_array.emplace_back(sections_);
diff --git a/chromium/components/web_package/web_bundle_utils.cc b/chromium/components/web_package/web_bundle_utils.cc
index 12795f68cbd..7312c2d1f54 100644
--- a/chromium/components/web_package/web_bundle_utils.cc
+++ b/chromium/components/web_package/web_bundle_utils.cc
@@ -62,7 +62,8 @@ bool HasNoSniffHeader(const network::mojom::URLResponseHead& response) {
std::string content_type_options;
response.headers->EnumerateHeader(nullptr, kContentTypeOptionsHeaderName,
&content_type_options);
- return base::LowerCaseEqualsASCII(content_type_options, kNoSniffHeaderValue);
+ return base::EqualsCaseInsensitiveASCII(content_type_options,
+ kNoSniffHeaderValue);
}
bool IsValidUuidInPackageURL(const GURL& url) {
diff --git a/chromium/components/web_resource/BUILD.gn b/chromium/components/web_resource/BUILD.gn
index d42c5da97fb..82ee731fc4f 100644
--- a/chromium/components/web_resource/BUILD.gn
+++ b/chromium/components/web_resource/BUILD.gn
@@ -10,26 +10,21 @@ static_library("web_resource") {
"resource_request_allowed_notifier.h",
"web_resource_pref_names.cc",
"web_resource_pref_names.h",
- "web_resource_service.cc",
- "web_resource_service.h",
]
deps = [
"//base",
"//build:branding_buildflags",
"//build:chromeos_buildflags",
- "//components/google/core/common",
"//components/pref_registry",
"//components/prefs",
- "//components/version_info",
- "//net",
- "//services/data_decoder/public/cpp",
"//services/network/public/cpp",
- "//ui/base",
]
}
static_library("test_support") {
+ testonly = true
+
sources = [
"resource_request_allowed_notifier_test_util.cc",
"resource_request_allowed_notifier_test_util.h",
@@ -47,17 +42,14 @@ source_set("unit_tests") {
sources = [
"eula_accepted_notifier_unittest.cc",
"resource_request_allowed_notifier_unittest.cc",
- "web_resource_service_unittest.cc",
]
deps = [
":test_support",
":web_resource",
"//base",
+ "//base/test:test_support",
"//components/prefs:test_support",
- "//components/version_info",
- "//net:test_support",
- "//services/data_decoder/public/cpp:test_support",
"//services/network:test_support",
"//services/network/public/cpp",
"//testing/gtest",
diff --git a/chromium/components/web_resource/DEPS b/chromium/components/web_resource/DEPS
index 4d0e68d58f6..08af8e315c0 100644
--- a/chromium/components/web_resource/DEPS
+++ b/chromium/components/web_resource/DEPS
@@ -1,13 +1,8 @@
include_rules = [
- "+components/google/core",
"+components/pref_registry",
"+components/prefs",
- "+components/version_info",
- "+net",
- "+services/data_decoder/public",
"+services/network/public/cpp",
"+services/network/test",
- "+ui/base",
# web_resource is used on iOS.
"-content",
diff --git a/chromium/components/web_resource/OWNERS b/chromium/components/web_resource/OWNERS
index d0a6461c727..60b1ac219ab 100644
--- a/chromium/components/web_resource/OWNERS
+++ b/chromium/components/web_resource/OWNERS
@@ -1,6 +1,2 @@
achuith@chromium.org
-rsesek@chromium.org
-
-# For ResourceRequestAllowedNotifier and EulaAcceptedNotifier:
-per-file eula_accepted_notifier*=file://components/variations/OWNERS
-per-file resource_request_allowed_notifier*=file://components/variations/OWNERS
+file://components/variations/OWNERS
diff --git a/chromium/components/web_resource/web_resource_service.cc b/chromium/components/web_resource/web_resource_service.cc
deleted file mode 100644
index a528e18c4b0..00000000000
--- a/chromium/components/web_resource/web_resource_service.cc
+++ /dev/null
@@ -1,210 +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.
-
-#include "components/web_resource/web_resource_service.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/google/core/common/google_util.h"
-#include "components/prefs/pref_service.h"
-#include "net/base/load_flags.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "url/gurl.h"
-
-// No anonymous namespace, because const variables automatically get internal
-// linkage.
-const char kUnexpectedJSONFormatError[] =
- "Data from web resource server does not have expected format.";
-
-namespace web_resource {
-
-WebResourceService::WebResourceService(
- PrefService* prefs,
- const GURL& web_resource_server,
- const std::string& application_locale,
- const char* last_update_time_pref_name,
- int start_fetch_delay_ms,
- int cache_update_delay_ms,
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- const char* disable_network_switch,
- const net::NetworkTrafficAnnotationTag& traffic_annotation,
- ResourceRequestAllowedNotifier::NetworkConnectionTrackerGetter
- network_connection_tracker_getter)
- : prefs_(prefs),
- resource_request_allowed_notifier_(new ResourceRequestAllowedNotifier(
- prefs,
- disable_network_switch,
- std::move(network_connection_tracker_getter))),
- fetch_scheduled_(false),
- in_fetch_(false),
- web_resource_server_(web_resource_server),
- application_locale_(application_locale),
- last_update_time_pref_name_(last_update_time_pref_name),
- start_fetch_delay_ms_(start_fetch_delay_ms),
- cache_update_delay_ms_(cache_update_delay_ms),
- url_loader_factory_(url_loader_factory),
- traffic_annotation_(traffic_annotation) {
- resource_request_allowed_notifier_->Init(this, false /* leaky */);
- DCHECK(prefs);
-}
-
-void WebResourceService::StartAfterDelay() {
- // If resource requests are not allowed, we'll get a callback when they are.
- if (resource_request_allowed_notifier_->ResourceRequestsAllowed())
- OnResourceRequestsAllowed();
-}
-
-WebResourceService::~WebResourceService() = default;
-
-void WebResourceService::OnSimpleLoaderComplete(
- std::unique_ptr<std::string> response_body) {
- simple_url_loader_.reset();
- if (response_body) {
- // Calls EndFetch() on completion.
- // Full JSON parsing might spawn a utility process (for security).
- // To limit the the number of simultaneously active processes
- // (on Android in particular) we short-cut the full parsing in the case of
- // trivially "empty" JSONs.
- if (response_body->empty() || *response_body == "{}") {
- OnJsonParsed(data_decoder::DataDecoder::ValueOrError::Value(
- base::Value(base::Value::Type::DICTIONARY)));
- } else {
- data_decoder::DataDecoder::ParseJsonIsolated(
- *response_body, base::BindOnce(&WebResourceService::OnJsonParsed,
- weak_ptr_factory_.GetWeakPtr()));
- }
- } else {
- // Don't parse data if attempt to download was unsuccessful.
- // Stop loading new web resource data, and silently exit.
- // We do not end up invoking OnJsonParsed(), so we need to call EndFetch()
- // ourselves.
- EndFetch();
- }
-}
-
-// Delay initial load of resource data into cache so as not to interfere
-// with startup time.
-void WebResourceService::ScheduleFetch(int64_t delay_ms) {
- if (fetch_scheduled_)
- return;
- fetch_scheduled_ = true;
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&WebResourceService::StartFetch,
- weak_ptr_factory_.GetWeakPtr()),
- base::Milliseconds(delay_ms));
-}
-
-void WebResourceService::SetResourceRequestAllowedNotifier(
- std::unique_ptr<ResourceRequestAllowedNotifier> notifier) {
- resource_request_allowed_notifier_ = std::move(notifier);
- resource_request_allowed_notifier_->Init(this, false /* leaky */);
-}
-
-bool WebResourceService::GetFetchScheduled() const {
- return fetch_scheduled_;
-}
-
-// Initializes the fetching of data from the resource server. Data
-// load calls OnSimpleLoaderComplete.
-void WebResourceService::StartFetch() {
- // Set to false so that next fetch can be scheduled after this fetch or
- // if we receive notification that resource is allowed.
- fetch_scheduled_ = false;
- // Check whether fetching is allowed.
- if (!resource_request_allowed_notifier_->ResourceRequestsAllowed())
- return;
-
- // First, put our next cache load on the MessageLoop.
- ScheduleFetch(cache_update_delay_ms_);
-
- // Set cache update time in preferences.
- prefs_->SetString(last_update_time_pref_name_,
- base::NumberToString(base::Time::Now().ToDoubleT()));
-
- // If we are still fetching data, exit.
- if (in_fetch_)
- return;
- in_fetch_ = true;
-
- GURL web_resource_server =
- application_locale_.empty()
- ? web_resource_server_
- : google_util::AppendGoogleLocaleParam(web_resource_server_,
- application_locale_);
-
- DVLOG(1) << "WebResourceService StartFetch " << web_resource_server;
- auto resource_request = std::make_unique<network::ResourceRequest>();
- resource_request->url = web_resource_server;
- // Do not let url fetcher affect existing state in system context
- // (by setting cookies, for example).
- resource_request->load_flags = net::LOAD_DISABLE_CACHE;
- resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
- simple_url_loader_ = network::SimpleURLLoader::Create(
- std::move(resource_request), traffic_annotation_);
- simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
- url_loader_factory_.get(),
- base::BindOnce(&WebResourceService::OnSimpleLoaderComplete,
- base::Unretained(this)));
-}
-
-void WebResourceService::EndFetch() {
- in_fetch_ = false;
-}
-
-void WebResourceService::OnJsonParsed(
- data_decoder::DataDecoder::ValueOrError result) {
- if (!result.value) {
- LOG(ERROR) << *result.error;
- EndFetch();
- return;
- }
-
- const base::DictionaryValue* dict = nullptr;
- if (!result.value->GetAsDictionary(&dict)) {
- LOG(ERROR) << kUnexpectedJSONFormatError;
- EndFetch();
- return;
- }
- Unpack(*dict);
-
- EndFetch();
-}
-
-void WebResourceService::OnResourceRequestsAllowed() {
- int64_t delay = start_fetch_delay_ms_;
- // Check whether we have ever put a value in the web resource cache;
- // if so, pull it out and see if it's time to update again.
- if (prefs_->HasPrefPath(last_update_time_pref_name_)) {
- std::string last_update_pref =
- prefs_->GetString(last_update_time_pref_name_);
- if (!last_update_pref.empty()) {
- double last_update_value;
- base::StringToDouble(last_update_pref, &last_update_value);
- int64_t ms_until_update =
- cache_update_delay_ms_ -
- static_cast<int64_t>(
- (base::Time::Now() - base::Time::FromDoubleT(last_update_value))
- .InMilliseconds());
- // Wait at least |start_fetch_delay_ms_|.
- if (ms_until_update > start_fetch_delay_ms_)
- delay = ms_until_update;
- }
- }
- // Start fetch and wait for UpdateResourceCache.
- ScheduleFetch(delay);
-}
-
-} // namespace web_resource
diff --git a/chromium/components/web_resource/web_resource_service.h b/chromium/components/web_resource/web_resource_service.h
deleted file mode 100644
index 0912e967884..00000000000
--- a/chromium/components/web_resource/web_resource_service.h
+++ /dev/null
@@ -1,146 +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.
-
-#ifndef COMPONENTS_WEB_RESOURCE_WEB_RESOURCE_SERVICE_H_
-#define COMPONENTS_WEB_RESOURCE_WEB_RESOURCE_SERVICE_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-
-#include "base/callback_forward.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "components/web_resource/resource_request_allowed_notifier.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
-#include "url/gurl.h"
-
-class PrefService;
-
-namespace base {
-class DictionaryValue;
-}
-
-namespace network {
-class SimpleURLLoader;
-class SharedURLLoaderFactory;
-}
-
-namespace web_resource {
-
-// A WebResourceService fetches JSON data from a web server and periodically
-// refreshes it.
-class WebResourceService : public ResourceRequestAllowedNotifier::Observer {
- public:
- // Creates a new WebResourceService.
- // If |application_locale| is not empty, it will be appended as a locale
- // parameter to the resource URL.
- WebResourceService(
- PrefService* prefs,
- const GURL& web_resource_server,
- const std::string& application_locale, // May be empty
- const char* last_update_time_pref_name,
- int start_fetch_delay_ms,
- int cache_update_delay_ms,
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- const char* disable_network_switch,
- const net::NetworkTrafficAnnotationTag& traffic_annotation,
- ResourceRequestAllowedNotifier::NetworkConnectionTrackerGetter
- network_connection_tracker_getter);
-
- WebResourceService(const WebResourceService&) = delete;
- WebResourceService& operator=(const WebResourceService&) = delete;
-
- ~WebResourceService() override;
-
- // Sleep until cache needs to be updated, but always for at least
- // |start_fetch_delay_ms| so we don't interfere with startup.
- // Then begin updating resources.
- void StartAfterDelay();
-
- // Sets the ResourceRequestAllowedNotifier to make it configurable.
- void SetResourceRequestAllowedNotifier(
- std::unique_ptr<ResourceRequestAllowedNotifier> notifier);
-
- protected:
- raw_ptr<PrefService> prefs_;
- bool GetFetchScheduled() const;
-
- private:
- friend class WebResourceServiceTest;
-
- // For the subclasses to process the result of a fetch.
- virtual void Unpack(const base::DictionaryValue& parsed_json) = 0;
-
- // Callback from SimpleURLLoader.
- void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
-
- // Schedules a fetch after |delay_ms| milliseconds.
- void ScheduleFetch(int64_t delay_ms);
-
- // Starts fetching data from the server.
- void StartFetch();
-
- // Set |in_fetch_| to false, clean up temp directories (in the future).
- void EndFetch();
-
- // Callback from the JSON parser.
- void OnJsonParsed(data_decoder::DataDecoder::ValueOrError result);
-
- // Implements ResourceRequestAllowedNotifier::Observer.
- void OnResourceRequestsAllowed() override;
-
- // Helper class used to tell this service if it's allowed to make network
- // resource requests.
- std::unique_ptr<ResourceRequestAllowedNotifier>
- resource_request_allowed_notifier_;
-
- // True if we have scheduled a fetch after start_fetch_delay_ms_
- // or another one in |cache_update_delay_ms_| time. Set to false
- // before fetching starts so that next fetch is scheduled. This
- // is to make sure not more than one fetch is scheduled for given
- // point in time.
- bool fetch_scheduled_;
-
- // The tool that loads the url data from the server.
- std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
-
- // True if we are currently fetching or unpacking data. If we are asked to
- // start a fetch when we are still fetching resource data, schedule another
- // one in |cache_update_delay_ms_| time, and silently exit.
- bool in_fetch_;
-
- // URL that hosts the web resource.
- GURL web_resource_server_;
-
- // Application locale, appended to the URL if not empty.
- std::string application_locale_;
-
- // Pref name to store the last update's time.
- const char* last_update_time_pref_name_;
-
- // Delay on first fetch so we don't interfere with startup.
- int start_fetch_delay_ms_;
-
- // Delay between calls to update the web resource cache. This delay may be
- // different for different builds of Chrome.
- int cache_update_delay_ms_;
-
- // The URL loader factory for the resource load.
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
- // Network traffic annotation for initialization of URLFetcher.
- const net::NetworkTrafficAnnotationTag traffic_annotation_;
-
- // So that we can delay our start so as not to affect start-up time; also,
- // so that we can schedule future cache updates.
- base::WeakPtrFactory<WebResourceService> weak_ptr_factory_{this};
-};
-
-} // namespace web_resource
-
-#endif // COMPONENTS_WEB_RESOURCE_WEB_RESOURCE_SERVICE_H_
diff --git a/chromium/components/web_resource/web_resource_service_unittest.cc b/chromium/components/web_resource/web_resource_service_unittest.cc
deleted file mode 100644
index 95756ca284a..00000000000
--- a/chromium/components/web_resource/web_resource_service_unittest.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// 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.
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/test/task_environment.h"
-#include "base/values.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/web_resource/resource_request_allowed_notifier.h"
-#include "components/web_resource/web_resource_service.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_network_connection_tracker.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-const std::string kTestUrl = "http://www.test.com";
-const std::string kCacheUpdatePath = "cache_update_path";
-std::string error_message_;
-} // namespace
-
-namespace web_resource {
-
-class TestResourceRequestAllowedNotifier
- : public ResourceRequestAllowedNotifier {
- public:
- TestResourceRequestAllowedNotifier(
- PrefService* prefs,
- const char* disable_network_switch,
- network::NetworkConnectionTracker* network_connection_tracker)
- : ResourceRequestAllowedNotifier(
- prefs,
- disable_network_switch,
- base::BindOnce(
- [](network::NetworkConnectionTracker* tracker) {
- return tracker;
- },
- network_connection_tracker)) {}
-
- ResourceRequestAllowedNotifier::State GetResourceRequestsAllowedState()
- override {
- return state_;
- }
-
- void SetState(ResourceRequestAllowedNotifier::State state) { state_ = state; }
-
- void NotifyState(ResourceRequestAllowedNotifier::State state) {
- SetState(state);
- SetObserverRequestedForTesting(true);
- MaybeNotifyObserver();
- }
-
- private:
- ResourceRequestAllowedNotifier::State state_;
-};
-
-class TestWebResourceService : public WebResourceService {
- public:
- TestWebResourceService(
- PrefService* prefs,
- const GURL& web_resource_server,
- const std::string& application_locale,
- const char* last_update_time_pref_name,
- int start_fetch_delay_ms,
- int cache_update_delay_ms,
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- const char* disable_network_switch,
- network::NetworkConnectionTracker* network_connection_tracker)
- : WebResourceService(prefs,
- web_resource_server,
- application_locale,
- last_update_time_pref_name,
- start_fetch_delay_ms,
- cache_update_delay_ms,
- url_loader_factory,
- disable_network_switch,
- TRAFFIC_ANNOTATION_FOR_TESTS,
- base::BindOnce(
- [](network::NetworkConnectionTracker* tracker) {
- return tracker;
- },
- network_connection_tracker)) {}
-
- void Unpack(const base::DictionaryValue& parsed_json) override {}
-};
-
-class WebResourceServiceTest : public testing::Test {
- public:
- void SetUp() override {
- test_shared_loader_factory_ =
- base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
- &test_url_loader_factory_);
- local_state_ = std::make_unique<TestingPrefServiceSimple>();
- local_state_->registry()->RegisterStringPref(kCacheUpdatePath, "0");
- test_web_resource_service_ = std::make_unique<TestWebResourceService>(
- local_state_.get(), GURL(kTestUrl), "", kCacheUpdatePath.c_str(), 100,
- 5000, test_shared_loader_factory_, nullptr,
- network::TestNetworkConnectionTracker::GetInstance());
- error_message_ = "";
- TestResourceRequestAllowedNotifier* notifier =
- new TestResourceRequestAllowedNotifier(
- local_state_.get(), nullptr,
- network::TestNetworkConnectionTracker::GetInstance());
- notifier->SetState(ResourceRequestAllowedNotifier::ALLOWED);
- test_web_resource_service_->SetResourceRequestAllowedNotifier(
- std::unique_ptr<ResourceRequestAllowedNotifier>(notifier));
- }
-
- TestResourceRequestAllowedNotifier* resource_notifier() {
- return static_cast<TestResourceRequestAllowedNotifier*>(
- test_web_resource_service_->resource_request_allowed_notifier_.get());
- }
-
- bool GetFetchScheduled() {
- return test_web_resource_service_->GetFetchScheduled();
- }
-
- void CallScheduleFetch(int64_t delay_ms) {
- return test_web_resource_service_->ScheduleFetch(delay_ms);
- }
-
- WebResourceService* web_resource_service() {
- return test_web_resource_service_.get();
- }
-
- void CallStartFetch() { test_web_resource_service_->StartFetch(); }
-
- private:
- base::test::SingleThreadTaskEnvironment task_environment_;
- data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
- network::TestURLLoaderFactory test_url_loader_factory_;
- scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
- std::unique_ptr<TestingPrefServiceSimple> local_state_;
- std::unique_ptr<TestWebResourceService> test_web_resource_service_;
-};
-
-TEST_F(WebResourceServiceTest, FetchScheduledAfterStartDelayTest) {
- web_resource_service()->StartAfterDelay();
- EXPECT_TRUE(GetFetchScheduled());
-}
-
-TEST_F(WebResourceServiceTest, FetchScheduledOnScheduleFetchTest) {
- web_resource_service()->StartAfterDelay();
- resource_notifier()->NotifyState(ResourceRequestAllowedNotifier::ALLOWED);
- EXPECT_TRUE(GetFetchScheduled());
-}
-
-TEST_F(WebResourceServiceTest, FetchScheduledOnStartFetchTest) {
- resource_notifier()->NotifyState(
- ResourceRequestAllowedNotifier::DISALLOWED_NETWORK_DOWN);
- CallStartFetch();
- EXPECT_FALSE(GetFetchScheduled());
- resource_notifier()->NotifyState(ResourceRequestAllowedNotifier::ALLOWED);
- EXPECT_TRUE(GetFetchScheduled());
-}
-
-} // namespace web_resource
diff --git a/chromium/components/webapk/OWNERS b/chromium/components/webapk/OWNERS
index c78bfecda7b..478cf779dec 100644
--- a/chromium/components/webapk/OWNERS
+++ b/chromium/components/webapk/OWNERS
@@ -1,4 +1,3 @@
hanxi@chromium.org
-pkotwicz@chromium.org
-yfriedman@chromium.org
hartmanng@chromium.org
+yfriedman@chromium.org
diff --git a/chromium/components/webapk/android/libs/client/BUILD.gn b/chromium/components/webapk/android/libs/client/BUILD.gn
index 88e4275e9d9..cee049d67f1 100644
--- a/chromium/components/webapk/android/libs/client/BUILD.gn
+++ b/chromium/components/webapk/android/libs/client/BUILD.gn
@@ -27,7 +27,6 @@ java_library("junit") {
]
deps = [
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//base/test:test_support_java",
diff --git a/chromium/components/webapps/browser/BUILD.gn b/chromium/components/webapps/browser/BUILD.gn
index 594604a411f..76e56c9813e 100644
--- a/chromium/components/webapps/browser/BUILD.gn
+++ b/chromium/components/webapps/browser/BUILD.gn
@@ -22,6 +22,8 @@ source_set("browser") {
"banners/app_banner_metrics.h",
"banners/app_banner_settings_helper.cc",
"banners/app_banner_settings_helper.h",
+ "features.cc",
+ "features.h",
"installable/installable_data.cc",
"installable/installable_data.h",
"installable/installable_logging.cc",
@@ -76,8 +78,6 @@ source_set("browser") {
"android/app_banner_manager_android.h",
"android/bottomsheet/pwa_bottom_sheet_controller.cc",
"android/bottomsheet/pwa_bottom_sheet_controller.h",
- "android/features.cc",
- "android/features.h",
"android/installable/installable_ambient_badge_client.h",
"android/installable/installable_ambient_badge_infobar.cc",
"android/installable/installable_ambient_badge_infobar.h",
diff --git a/chromium/components/webapps/browser/android/BUILD.gn b/chromium/components/webapps/browser/android/BUILD.gn
index 18d2f18e3b4..9a5de41c367 100644
--- a/chromium/components/webapps/browser/android/BUILD.gn
+++ b/chromium/components/webapps/browser/android/BUILD.gn
@@ -34,6 +34,8 @@ android_library("java") {
deps = [
":java_resources",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//components/browser_ui/bottomsheet/android:java",
"//components/browser_ui/widget/android:java",
"//components/infobars/android:java",
@@ -170,7 +172,6 @@ java_library("junit") {
]
deps = [
":java",
- "//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//base/test:test_support_java",
diff --git a/chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher.cc b/chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher.cc
index ecc8f3e781b..b2dafd6d431 100644
--- a/chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher.cc
+++ b/chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher.cc
@@ -25,6 +25,7 @@
#include "components/favicon_base/favicon_types.h"
#include "components/webapps/browser/android/webapps_icon_utils.h"
#include "components/webapps/browser/android/webapps_utils.h"
+#include "components/webapps/browser/features.h"
#include "components/webapps/browser/installable/installable_manager.h"
#include "components/webapps/common/constants.h"
#include "components/webapps/common/web_page_metadata.mojom.h"
@@ -57,7 +58,7 @@ InstallableParams ParamsToPerformManifestAndIconFetch() {
params.valid_primary_icon = true;
params.prefer_maskable_icon =
WebappsIconUtils::DoesAndroidSupportMaskableIcons();
- params.wait_for_worker = true;
+ params.wait_for_worker = !features::SkipInstallServiceWorkerCheck();
return params;
}
@@ -65,11 +66,11 @@ InstallableParams ParamsToPerformInstallableCheck() {
InstallableParams params;
params.check_eligibility = true;
params.valid_manifest = true;
- params.has_worker = true;
+ params.has_worker = !features::SkipInstallServiceWorkerCheck();
+ params.wait_for_worker = !features::SkipInstallServiceWorkerCheck();
params.valid_primary_icon = true;
params.prefer_maskable_icon =
WebappsIconUtils::DoesAndroidSupportMaskableIcons();
- params.wait_for_worker = true;
return params;
}
@@ -132,8 +133,9 @@ AddToHomescreenDataFetcher::AddToHomescreenDataFetcher(
// Send a message to the renderer to retrieve information about the page.
mojo::AssociatedRemote<mojom::WebPageMetadataAgent> metadata_agent;
- web_contents->GetMainFrame()->GetRemoteAssociatedInterfaces()->GetInterface(
- &metadata_agent);
+ web_contents->GetPrimaryMainFrame()
+ ->GetRemoteAssociatedInterfaces()
+ ->GetInterface(&metadata_agent);
// Bind the InterfacePtr into the callback so that it's kept alive until
// there's either a connection error or a response.
auto* web_page_metadata_proxy = metadata_agent.get();
@@ -274,7 +276,8 @@ void AddToHomescreenDataFetcher::OnDidPerformInstallableCheck(
return;
bool webapk_compatible =
- (data.NoBlockingErrors() && data.valid_manifest && data.has_worker &&
+ (data.NoBlockingErrors() && data.valid_manifest &&
+ data.worker_check_passed &&
WebappsUtils::AreWebManifestUrlsWebApkCompatible(data.manifest));
if (!webapk_compatible && !data.errors.empty()) {
installable_status_code_ = data.errors[0];
diff --git a/chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc b/chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
index 07ee0a39582..e85f32e85c0 100644
--- a/chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
+++ b/chromium/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
@@ -18,9 +18,11 @@
#include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
#include "components/favicon/content/large_favicon_provider_getter.h"
#include "components/favicon/core/large_favicon_provider.h"
#include "components/favicon_base/favicon_types.h"
+#include "components/webapps/browser/features.h"
#include "components/webapps/browser/installable/installable_logging.h"
#include "components/webapps/browser/installable/installable_manager.h"
#include "components/webapps/browser/installable/installable_metrics.h"
@@ -144,24 +146,23 @@ class TestInstallableManager : public InstallableManager {
void GetData(const InstallableParams& params,
InstallableCallback callback) override {
InstallableStatusCode code = NO_ERROR_DETECTED;
- bool is_installable = is_installable_;
- if (params.valid_primary_icon && !primary_icon_) {
+ bool is_installable = true;
+ if (params.valid_manifest &&
+ !IsManifestValidForWebApp(*manifest_,
+ true /* check_webapp_manifest_display */)) {
+ code = valid_manifest_->errors.at(0);
+ is_installable = false;
+ } else if (params.valid_primary_icon && !primary_icon_) {
code = NO_ACCEPTABLE_ICON;
is_installable = false;
- } else if (params.valid_manifest && params.has_worker) {
- if (!IsManifestValidForWebApp(*manifest_,
- true /* check_webapp_manifest_display */)) {
- code = valid_manifest_->errors.at(0);
- is_installable = false;
- } else if (!is_installable_) {
- code = NOT_OFFLINE_CAPABLE;
- is_installable = false;
- }
+ } else if (params.has_worker && !has_worker_) {
+ code = NOT_OFFLINE_CAPABLE;
+ is_installable = false;
}
if (should_manifest_time_out_ ||
(params.valid_manifest && params.has_worker &&
- should_installable_time_out_)) {
+ should_service_worker_time_out_)) {
return;
}
@@ -176,10 +177,10 @@ class TestInstallableManager : public InstallableManager {
nullptr /* splash_icon */, params.prefer_maskable_icon,
std::vector<SkBitmap>() /* screenshots */,
params.valid_manifest ? is_installable : false,
- params.has_worker ? is_installable : false});
+ params.has_worker ? is_installable : true});
}
- void SetInstallable(bool is_installable) { is_installable_ = is_installable; }
+ void SetHasServiceWorker(bool worker) { has_worker_ = worker; }
void SetManifest(blink::mojom::ManifestPtr manifest) {
DCHECK(manifest);
@@ -196,8 +197,8 @@ class TestInstallableManager : public InstallableManager {
should_manifest_time_out_ = should_time_out;
}
- void SetShouldInstallableTimeOut(bool should_time_out) {
- should_installable_time_out_ = should_time_out;
+ void SetShouldServiceWorkerTimeOut(bool should_time_out) {
+ should_service_worker_time_out_ = should_time_out;
}
private:
@@ -205,10 +206,10 @@ class TestInstallableManager : public InstallableManager {
GURL primary_icon_url_;
std::unique_ptr<SkBitmap> primary_icon_;
- bool is_installable_ = true;
+ bool has_worker_ = true;
bool should_manifest_time_out_ = false;
- bool should_installable_time_out_ = false;
+ bool should_service_worker_time_out_ = false;
};
// Tests AddToHomescreenDataFetcher. These tests should be browser tests but
@@ -300,16 +301,16 @@ class AddToHomescreenDataFetcherTest
installable_manager_->SetManifest(std::move(manifest));
}
- void SetInstallable(bool is_installable) {
- installable_manager_->SetInstallable(is_installable);
+ void SetHasServiceWorker(bool worker) {
+ installable_manager_->SetHasServiceWorker(worker);
}
void SetShouldManifestTimeOut(bool should_time_out) {
installable_manager_->SetShouldManifestTimeOut(should_time_out);
}
- void SetShouldInstallableTimeOut(bool should_time_out) {
- installable_manager_->SetShouldInstallableTimeOut(should_time_out);
+ void SetShouldServiceWorkerTimeOut(bool should_time_out) {
+ installable_manager_->SetShouldServiceWorkerTimeOut(should_time_out);
}
private:
@@ -391,7 +392,7 @@ TEST_F(AddToHomescreenDataFetcherTest, ManifestFetchTimesOutPwa) {
TEST_F(AddToHomescreenDataFetcherTest, ManifestFetchTimesOutNonPwa) {
SetShouldManifestTimeOut(true);
SetManifest(BuildDefaultManifest());
- SetInstallable(false);
+ SetHasServiceWorker(false);
// Check where InstallableManager finishes working after the time out and
// determines non-PWA-ness.
@@ -409,7 +410,7 @@ TEST_F(AddToHomescreenDataFetcherTest, ManifestFetchTimesOutNonPwa) {
TEST_F(AddToHomescreenDataFetcherTest, ManifestFetchTimesOutUnknown) {
SetShouldManifestTimeOut(true);
- SetShouldInstallableTimeOut(true);
+ SetShouldServiceWorkerTimeOut(true);
SetManifest(BuildDefaultManifest());
// Check where InstallableManager doesn't finish working after the time out.
@@ -432,8 +433,13 @@ TEST_F(AddToHomescreenDataFetcherTest, ManifestFetchTimesOutUnknown) {
// but not be WebAPK-compatible. Only relevant when checking WebAPK
// compatibility.
TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutPwa) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {}, {features::kSkipServiceWorkerCheckInstallOnly,
+ features::kSkipServiceWorkerCheckAll});
+
SetManifest(BuildDefaultManifest());
- SetShouldInstallableTimeOut(true);
+ SetShouldServiceWorkerTimeOut(true);
// Check where InstallableManager finishes working after the timeout and
// determines PWA-ness.
@@ -451,9 +457,14 @@ TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutPwa) {
}
TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutNonPwa) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {}, {features::kSkipServiceWorkerCheckInstallOnly,
+ features::kSkipServiceWorkerCheckAll});
+
SetManifest(BuildDefaultManifest());
- SetShouldInstallableTimeOut(true);
- SetInstallable(false);
+ SetShouldServiceWorkerTimeOut(true);
+ SetHasServiceWorker(false);
// Check where InstallableManager finishes working after the timeout and
// determines non-PWA-ness.
@@ -471,9 +482,14 @@ TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutNonPwa) {
}
TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutUnknown) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {}, {features::kSkipServiceWorkerCheckInstallOnly,
+ features::kSkipServiceWorkerCheckAll});
+
SetManifest(BuildDefaultManifest());
- SetShouldInstallableTimeOut(true);
- SetInstallable(false);
+ SetShouldServiceWorkerTimeOut(true);
+ SetHasServiceWorker(false);
// Check where InstallableManager doesn't finish working after the timeout.
// This is akin to waiting for a service worker forever.
@@ -515,6 +531,11 @@ TEST_F(AddToHomescreenDataFetcherTest, InstallableManifest) {
}
TEST_F(AddToHomescreenDataFetcherTest, ManifestNameClobbersWebApplicationName) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {}, {features::kSkipServiceWorkerCheckInstallOnly,
+ features::kSkipServiceWorkerCheckAll});
+
// Test that when the manifest provides Manifest::name but not
// Manifest::short_name that Manifest::name is used as the title.
{
@@ -541,7 +562,7 @@ TEST_F(AddToHomescreenDataFetcherTest, ManifestNameClobbersWebApplicationName) {
{
// Check a site with no offline-capable service worker.
- SetInstallable(false);
+ SetHasServiceWorker(false);
ObserverWaiter waiter;
std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
RunFetcher(fetcher.get(), waiter, kDefaultManifestName,
@@ -557,7 +578,7 @@ TEST_F(AddToHomescreenDataFetcherTest, ManifestNameClobbersWebApplicationName) {
{
// Check a site where we time out waiting for the service worker.
- SetShouldInstallableTimeOut(true);
+ SetShouldServiceWorkerTimeOut(true);
ObserverWaiter waiter;
std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
RunFetcher(fetcher.get(), waiter, kDefaultManifestName,
@@ -573,8 +594,8 @@ TEST_F(AddToHomescreenDataFetcherTest, ManifestNameClobbersWebApplicationName) {
{
// Check a site with an offline-capable service worker.
- SetInstallable(true);
- SetShouldInstallableTimeOut(false);
+ SetHasServiceWorker(true);
+ SetShouldServiceWorkerTimeOut(false);
ObserverWaiter waiter;
std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
RunFetcher(fetcher.get(), waiter, kDefaultManifestName,
@@ -616,4 +637,88 @@ TEST_F(AddToHomescreenDataFetcherTest, ManifestNoNameNoShortName) {
GURL(kDefaultIconUrl));
}
+TEST_F(AddToHomescreenDataFetcherTest,
+ NoServiceWorkerInstallable_InstallOnlyFlag) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kSkipServiceWorkerCheckInstallOnly},
+ {features::kSkipServiceWorkerCheckAll});
+ SetManifest(BuildDefaultManifest());
+ SetHasServiceWorker(false);
+
+ // Check where InstallableManager doesn't finish working after the timeout.
+ // This is akin to waiting for a service worker forever.
+ base::HistogramTester histograms;
+ ObserverWaiter waiter;
+ std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
+ RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
+ kDefaultManifestName, blink::mojom::DisplayMode::kStandalone,
+ true /*is_webapk_compatible*/,
+ InstallableStatusCode::NO_ERROR_DETECTED);
+
+ // Navigate to ensure the histograms are written.
+ NavigateAndCommit(GURL("about:blank"));
+ CheckHistograms(histograms);
+
+ EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
+ EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
+ GURL(kDefaultIconUrl));
+}
+
+TEST_F(AddToHomescreenDataFetcherTest, NoServiceWorkerInstallable_SkipAllFlag) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kSkipServiceWorkerCheckAll},
+ {features::kSkipServiceWorkerCheckInstallOnly});
+ SetManifest(BuildDefaultManifest());
+ SetHasServiceWorker(false);
+
+ // Check where InstallableManager doesn't finish working after the timeout.
+ // This is akin to waiting for a service worker forever.
+ base::HistogramTester histograms;
+ ObserverWaiter waiter;
+ std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
+ RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
+ kDefaultManifestName, blink::mojom::DisplayMode::kStandalone,
+ true /*is_webapk_compatible*/,
+ InstallableStatusCode::NO_ERROR_DETECTED);
+
+ // Navigate to ensure the histograms are written.
+ NavigateAndCommit(GURL("about:blank"));
+ CheckHistograms(histograms);
+
+ EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
+ EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
+ GURL(kDefaultIconUrl));
+}
+
+TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerTimeOutInstallable) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kSkipServiceWorkerCheckInstallOnly,
+ features::kSkipServiceWorkerCheckAll},
+ {});
+
+ SetManifest(BuildDefaultManifest());
+ SetShouldServiceWorkerTimeOut(true);
+
+ // Check where InstallableManager doesn't finish working after the timeout.
+ // This is akin to waiting for a service worker forever.
+ base::HistogramTester histograms;
+ ObserverWaiter waiter;
+ std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
+ RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
+ kDefaultManifestName, blink::mojom::DisplayMode::kStandalone,
+ true /*is_webapk_compatible*/,
+ InstallableStatusCode::NO_ERROR_DETECTED);
+
+ // Navigate to ensure the histograms are written.
+ NavigateAndCommit(GURL("about:blank"));
+ CheckHistograms(histograms);
+
+ EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
+ EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
+ GURL(kDefaultIconUrl));
+}
+
} // namespace webapps
diff --git a/chromium/components/webapps/browser/android/android_webapps_strings.grd b/chromium/components/webapps/browser/android/android_webapps_strings.grd
index 3025fb17744..3f38483857d 100644
--- a/chromium/components/webapps/browser/android/android_webapps_strings.grd
+++ b/chromium/components/webapps/browser/android/android_webapps_strings.grd
@@ -200,6 +200,12 @@
<message name="IDS_IMAGE_ZOOM_CONTENT_DESCRIPTION" desc="The content description string for the screenshot in the image zoom view.">
Screenshot. Tap to close.
</message>
+ <message name="IDS_WEBAPK_INSTALL_IN_PROGRESS" desc="Indicates that an installation for the WebAPK for the specific website is already in progress.">
+ Still adding previous site
+ </message>
+ <message name="IDS_WEBAPK_INSTALL_FAILED" desc="Indicates that a WebAPK has not been added to Home screen.">
+ Failed to install
+ </message>
</messages>
</release>
</grit>
diff --git a/chromium/components/webapps/browser/android/android_webapps_strings_grd/IDS_WEBAPK_INSTALL_FAILED.png.sha1 b/chromium/components/webapps/browser/android/android_webapps_strings_grd/IDS_WEBAPK_INSTALL_FAILED.png.sha1
new file mode 100644
index 00000000000..9ac2acd7533
--- /dev/null
+++ b/chromium/components/webapps/browser/android/android_webapps_strings_grd/IDS_WEBAPK_INSTALL_FAILED.png.sha1
@@ -0,0 +1 @@
+0e6feee2f5ca8e1733ac0bd435ddbd48a4ede79b \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/android_webapps_strings_grd/IDS_WEBAPK_INSTALL_IN_PROGRESS.png.sha1 b/chromium/components/webapps/browser/android/android_webapps_strings_grd/IDS_WEBAPK_INSTALL_IN_PROGRESS.png.sha1
new file mode 100644
index 00000000000..3c5056aaf52
--- /dev/null
+++ b/chromium/components/webapps/browser/android/android_webapps_strings_grd/IDS_WEBAPK_INSTALL_IN_PROGRESS.png.sha1
@@ -0,0 +1 @@
+8b7c3f1f1bc97f76e5a5b7899434080924f032b0 \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/app_banner_manager_android.cc b/chromium/components/webapps/browser/android/app_banner_manager_android.cc
index f259fbb1fea..aacbfba976f 100644
--- a/chromium/components/webapps/browser/android/app_banner_manager_android.cc
+++ b/chromium/components/webapps/browser/android/app_banner_manager_android.cc
@@ -26,7 +26,6 @@
#include "components/webapps/browser/android/add_to_homescreen_coordinator.h"
#include "components/webapps/browser/android/add_to_homescreen_params.h"
#include "components/webapps/browser/android/bottomsheet/pwa_bottom_sheet_controller.h"
-#include "components/webapps/browser/android/features.h"
#include "components/webapps/browser/android/installable/installable_ambient_badge_infobar_delegate.h"
#include "components/webapps/browser/android/shortcut_info.h"
#include "components/webapps/browser/android/webapps_icon_utils.h"
@@ -34,6 +33,7 @@
#include "components/webapps/browser/android/webapps_utils.h"
#include "components/webapps/browser/banners/app_banner_metrics.h"
#include "components/webapps/browser/banners/app_banner_settings_helper.h"
+#include "components/webapps/browser/features.h"
#include "components/webapps/browser/installable/installable_data.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "components/webapps/browser/webapps_client.h"
@@ -218,7 +218,8 @@ void AppBannerManagerAndroid::ShowBannerUi(WebappInstallSource install_source) {
// If we are installing from the ambient badge, it will remove itself.
if (install_source != WebappInstallSource::AMBIENT_BADGE_CUSTOM_TAB &&
- install_source != WebappInstallSource::AMBIENT_BADGE_BROWSER_TAB) {
+ install_source != WebappInstallSource::AMBIENT_BADGE_BROWSER_TAB &&
+ install_source != WebappInstallSource::RICH_INSTALL_UI_WEBLAYER) {
HideAmbientBadge();
}
@@ -274,6 +275,8 @@ void AppBannerManagerAndroid::OnInstallEvent(
WebappInstallSource::AMBIENT_BADGE_BROWSER_TAB ||
a2hs_params.install_source ==
WebappInstallSource::AMBIENT_BADGE_CUSTOM_TAB ||
+ a2hs_params.install_source ==
+ WebappInstallSource::RICH_INSTALL_UI_WEBLAYER ||
a2hs_params.install_source == WebappInstallSource::API_BROWSER_TAB ||
a2hs_params.install_source == WebappInstallSource::API_CUSTOM_TAB ||
a2hs_params.install_source == WebappInstallSource::DEVTOOLS);
diff --git a/chromium/components/webapps/browser/android/features.cc b/chromium/components/webapps/browser/android/features.cc
deleted file mode 100644
index eaff93377ad..00000000000
--- a/chromium/components/webapps/browser/android/features.cc
+++ /dev/null
@@ -1,24 +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 "components/webapps/browser/android/features.h"
-
-#include "base/feature_list.h"
-
-namespace webapps {
-namespace features {
-
-const base::Feature kAddToHomescreenMessaging{
- "AddToHomescreenMessaging", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Enables or disables the installable ambient badge infobar.
-const base::Feature kInstallableAmbientBadgeInfoBar{
- "InstallableAmbientBadgeInfoBar", base::FEATURE_ENABLED_BY_DEFAULT};
-
-// Enables or disables the installable ambient badge message.
-const base::Feature kInstallableAmbientBadgeMessage{
- "InstallableAmbientBadgeMessage", base::FEATURE_DISABLED_BY_DEFAULT};
-
-} // namespace features
-} // namespace webapps
diff --git a/chromium/components/webapps/browser/android/installable/installable_ambient_badge_infobar_delegate.cc b/chromium/components/webapps/browser/android/installable/installable_ambient_badge_infobar_delegate.cc
index 9e37ea916b5..efe28c9b24b 100644
--- a/chromium/components/webapps/browser/android/installable/installable_ambient_badge_infobar_delegate.cc
+++ b/chromium/components/webapps/browser/android/installable/installable_ambient_badge_infobar_delegate.cc
@@ -11,8 +11,8 @@
#include "base/metrics/field_trial_params.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/strings/grit/components_strings.h"
-#include "components/webapps/browser/android/features.h"
#include "components/webapps/browser/android/installable/installable_ambient_badge_infobar.h"
+#include "components/webapps/browser/features.h"
#include "components/webapps/browser/webapps_client.h"
#include "ui/base/l10n/l10n_util.h"
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_af.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_af.xtb
index 14657974e7e..7012be6918f 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_af.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_af.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Voeg by tuisskerm</translation>
<translation id="2478076885740497414">Installeer program</translation>
<translation id="3789841737615482174">Installeer</translation>
+<translation id="3910402514791813257">Kon nie installeer nie</translation>
<translation id="4250229828105606438">Skermkiekie</translation>
<translation id="4665282149850138822"><ph name="NAME" /> is by jou tuisskerm gevoeg</translation>
<translation id="5250483651202458397">Skermkiekie. Tik om toe te maak.</translation>
<translation id="6990079615885386641">Kry die program van die Google Play Winkel af: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Voeg steeds vorige werf by</translation>
<translation id="962979164594783469">Installeer hierdie program</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_am.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_am.xtb
index 38f94b60cac..f34fc81b138 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_am.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_am.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ወደ መáŠáˆ» ማያ ገጽ አክáˆ</translation>
<translation id="2478076885740497414">መተáŒá‰ áˆªá‹« ይጫኑ</translation>
<translation id="3789841737615482174">ጫን </translation>
+<translation id="3910402514791813257">መጫን አáˆá‰°áˆ³áŠ«áˆ</translation>
<translation id="4250229828105606438">ቅጽበታዊ ገጽ እይታ</translation>
<translation id="4665282149850138822"><ph name="NAME" /> ወደ እርስዎ መáŠáˆ» áŒˆá… á‰³áŠ­áˆáˆ</translation>
<translation id="5250483651202458397">ቅጽበታዊ ገጽ እይታᢠለመá‹áŒ‹á‰µ መታ ያድርጉá¢</translation>
<translation id="6990079615885386641">መተáŒá‰ áˆªá‹«á‹áŠ• ከGoogle Play መደብር á‹«áŒáŠ™á¦ <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">አáˆáŠ•áˆ ቀዳሚ ጣቢያን በማከሠላይ</translation>
<translation id="962979164594783469">ይህን መተáŒá‰ áˆªá‹« ይጫኑ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ar.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ar.xtb
index 89631d381cb..475af69fd3e 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ar.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ar.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">الإضاÙØ© إلى الشاشة الرئيسية</translation>
<translation id="2478076885740497414">تثبيت التطبيق</translation>
<translation id="3789841737615482174">تثبيت</translation>
+<translation id="3910402514791813257">تعذَّر التثبيت.</translation>
<translation id="4250229828105606438">لقطة شاشة</translation>
<translation id="4665282149850138822">تمت إضاÙØ© <ph name="NAME" /> إلى صÙحتك الرئيسية</translation>
<translation id="5250483651202458397">انقر لإغلاق لقطة الشاشة.</translation>
<translation id="6990079615885386641">â€Ø§Ù„حصول على التطبيق من متجر Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">لا تزال عملية إضاÙØ© الموقع الإلكتروني السابق جارية</translation>
<translation id="962979164594783469">تثبيت هذا التطبيق</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_as.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_as.xtb
index 52afca11b25..8d9d1abf7e6 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_as.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_as.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">গৃহ সà§à¦•à§à§°à§€à¦£à¦¤ যোগ কৰক</translation>
<translation id="2478076885740497414">à¦à¦ªà§â€Œ ইনষà§à¦Ÿà¦² কৰক</translation>
<translation id="3789841737615482174">ইনষà§à¦Ÿà¦² কৰক</translation>
+<translation id="3910402514791813257">ইনষà§à¦Ÿà¦² কৰিব পৰা নগ’ল</translation>
<translation id="4250229828105606438">সà§à¦•à§à¦°à§€à¦£à¦¶à§à¦¬à¦Ÿ</translation>
<translation id="4665282149850138822">আপোনাৰ গৃহ সà§à¦•à§à§°à§€à¦£à¦¤ <ph name="NAME" />ক যোগ দিয়া হৈছে</translation>
<translation id="5250483651202458397">সà§à¦•à§à§°à§€à¦¨à¦¶à§à¦¬à¦Ÿà¥¤ বনà§à¦§ কৰিবলৈ টিপক।</translation>
<translation id="6990079615885386641">Google Play Storeত à¦à¦ªà¦Ÿà§‹ পাওক: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">à¦à¦¤à¦¿à§Ÿà¦¾à¦“ পূৰà§à¦¬à§±à§°à§à¦¤à§€ ছাইট যোগ কৰি থকা হৈছে</translation>
<translation id="962979164594783469">à¦à¦‡ à¦à¦ªà§â€Œà¦Ÿà§‹ ইনষà§à¦Ÿà¦² কৰক</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_az.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_az.xtb
index e866ba4a2aa..6a529efceca 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_az.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_az.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Æsas ekrana É™lavÉ™ edin</translation>
<translation id="2478076885740497414">Tətbiqi quraşdırın</translation>
<translation id="3789841737615482174">Quraşdırın</translation>
+<translation id="3910402514791813257">Quraşdırmaq alınmadı</translation>
<translation id="4250229828105606438">SkrinÅŸot</translation>
<translation id="4665282149850138822"><ph name="NAME" /> əsas ekranınıza əlavə edildi</translation>
<translation id="5250483651202458397">Skrinşot. Bağlamaq üçün toxunun.</translation>
<translation id="6990079615885386641">Google Play Dükanından tətbiq əldə edin: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ÆvvÉ™lki sayt hÉ™lÉ™ É™lavÉ™ olunur</translation>
<translation id="962979164594783469">Bu tətbiqi quraşdırın</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_be.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_be.xtb
index e889a358e99..8cdea5f034a 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_be.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_be.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Дадаць на галоўны Ñкран</translation>
<translation id="2478076885740497414">УÑталÑваць праграму</translation>
<translation id="3789841737615482174">УÑталÑваць</translation>
+<translation id="3910402514791813257">Збой уÑталÑваннÑ</translation>
<translation id="4250229828105606438">Здымак Ñкрана</translation>
<translation id="4665282149850138822">КарыÑтальнік <ph name="NAME" /> дададзены на галоўны Ñкран</translation>
<translation id="5250483651202458397">Здымак Ñкрана. ДакраніцеÑÑ, каб закрыць.</translation>
<translation id="6990079615885386641">УÑталÑваць праграму з Крамы Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Ідзе дадаванне папÑÑ€ÑднÑга Ñайта</translation>
<translation id="962979164594783469">УÑталÑваць гÑту праграму</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_bg.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_bg.xtb
index 8b520b4c539..4c34637aa85 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_bg.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_bg.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ДобавÑне към Ð½Ð°Ñ‡Ð°Ð»Ð½Ð¸Ñ ÐµÐºÑ€Ð°Ð½</translation>
<translation id="2478076885740497414">ИнÑталиране на приложението</translation>
<translation id="3789841737615482174">ИнÑталиране</translation>
+<translation id="3910402514791813257">ИнÑталирането не бе уÑпешно</translation>
<translation id="4250229828105606438">Eкранна Ñнимка</translation>
<translation id="4665282149850138822">Добавихте <ph name="NAME" /> към Ð½Ð°Ñ‡Ð°Ð»Ð½Ð¸Ñ ÐµÐºÑ€Ð°Ð½</translation>
<translation id="5250483651202458397">Екранна Ñнимка. ДокоÑнете за затварÑне.</translation>
<translation id="6990079615885386641">ИзтеглÑне на приложението от Google Play Магазин: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Още Ñе Ð´Ð¾Ð±Ð°Ð²Ñ Ð¿Ñ€ÐµÐ´Ð¸ÑˆÐ½Ð¸ÑÑ‚ Ñайт</translation>
<translation id="962979164594783469">ИнÑталиране на това приложение</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_bn.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_bn.xtb
index 4b95bf7f06b..ef1cecb0c9d 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_bn.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_bn.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">হোম সà§à¦•à§à¦°à§€à¦¨à§‡ যোগ করà§à¦¨</translation>
<translation id="2478076885740497414">অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¾à¦¨ ইনসà§à¦Ÿà¦² করà§à¦¨</translation>
<translation id="3789841737615482174">ইনসà§à¦Ÿà¦² করà§à¦¨</translation>
+<translation id="3910402514791813257">ইনসà§à¦Ÿà¦² করা যায়নি</translation>
<translation id="4250229828105606438">সà§à¦•à§à¦°à¦¿à¦¨à¦¶à¦Ÿ</translation>
<translation id="4665282149850138822">আপনার হোম সà§à¦•à§à¦°à§€à¦¨à§‡ <ph name="NAME" /> কে যোগ করা হয়েছে</translation>
<translation id="5250483651202458397">সà§à¦•à§à¦°à¦¿à¦¨à¦¶à¦Ÿà¥¤ বনà§à¦§ করতে টà§à¦¯à¦¾à¦ª করà§à¦¨à¥¤</translation>
<translation id="6990079615885386641">Google Play Store-à¦à¦° থেকে অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¾à¦¨à¦Ÿà¦¿ পান: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">à¦à¦–নও পূরà§à¦¬à§‡à¦° সাইট যোগ করছে</translation>
<translation id="962979164594783469">à¦à¦‡ অà§à¦¯à¦¾à¦ª ইনসà§à¦Ÿà¦² করà§à¦¨</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_bs.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_bs.xtb
index f85721441b8..85bb9514ad8 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_bs.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_bs.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Dodaj na poÄetni ekran</translation>
<translation id="2478076885740497414">Instaliranje aplikacije</translation>
<translation id="3789841737615482174">Instaliraj</translation>
+<translation id="3910402514791813257">Instaliranje nije uspjelo</translation>
<translation id="4250229828105606438">Snimak ekrana</translation>
<translation id="4665282149850138822">Web lokacija <ph name="NAME" /> je dodana na vaÅ¡ PoÄetni ekran</translation>
<translation id="5250483651202458397">Snimak ekrana. Dodirnite da zatvorite.</translation>
<translation id="6990079615885386641">Preuzmite aplikaciju s Google Play trgovine: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Prethodna web lokacija se još uvijek dodaje</translation>
<translation id="962979164594783469">Instalirajte ovu aplikaciju</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ca.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ca.xtb
index fe45fbaa060..a5ef2742b3c 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ca.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ca.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Afegeix a pantalla d'inici</translation>
<translation id="2478076885740497414">Instal·la l'aplicació</translation>
<translation id="3789841737615482174">Instal·la</translation>
+<translation id="3910402514791813257">No s'ha pogut instal·lar</translation>
<translation id="4250229828105606438">Captura de pantalla</translation>
<translation id="4665282149850138822"><ph name="NAME" /> s'ha afegit a la pantalla d'inici</translation>
<translation id="5250483651202458397">Captura de pantalla. Toca per tancar.</translation>
<translation id="6990079615885386641">Baixeu l'aplicació des de Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Encara s'hi està afegint el lloc anterior</translation>
<translation id="962979164594783469">Instal·la aquesta aplicació</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_cs.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_cs.xtb
index 555d8acf82e..4a1c6b22805 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_cs.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_cs.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Přidat na plochu</translation>
<translation id="2478076885740497414">Instalovat aplikaci</translation>
<translation id="3789841737615482174">Instalovat</translation>
+<translation id="3910402514791813257">Instalace se nezdařila</translation>
<translation id="4250229828105606438">Snímek obrazovky</translation>
<translation id="4665282149850138822">Na plochu byl přidán web <ph name="NAME" /></translation>
<translation id="5250483651202458397">Snímek obrazovky. Klepnutím zavřete.</translation>
<translation id="6990079615885386641">Získat aplikaci z Obchodu Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Předchozí web se stále přidává</translation>
<translation id="962979164594783469">Nainstalovat aplikaci</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_cy.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_cy.xtb
index 49dece0ba59..2ffd180e69b 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_cy.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_cy.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Ychwanegu at y sgrîn Hafan</translation>
<translation id="2478076885740497414">Gosod yr ap</translation>
<translation id="3789841737615482174">Gosod</translation>
+<translation id="3910402514791813257">Wedi methu â gosod</translation>
<translation id="4250229828105606438">Sgrinlun</translation>
<translation id="4665282149850138822">Cafodd <ph name="NAME" /> ei ychwanegu at eich sgrîn Hafan</translation>
<translation id="5250483651202458397">Sgrinlun. Tapiwch i gau.</translation>
<translation id="6990079615885386641">Lawrlwythwch yr ap o'r Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Dal i ychwanegu'r wefan flaenorol</translation>
<translation id="962979164594783469">Gosod yr ap hwn</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_da.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_da.xtb
index 4bf8be52ec5..91c0d6981dc 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_da.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_da.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Føj til startskærm</translation>
<translation id="2478076885740497414">Installer app</translation>
<translation id="3789841737615482174">Installer</translation>
+<translation id="3910402514791813257">Installationen blev ikke gennemført</translation>
<translation id="4250229828105606438">Screenshot</translation>
<translation id="4665282149850138822"><ph name="NAME" /> blev føjet til din startskærm</translation>
<translation id="5250483651202458397">Screenshot. Tryk for at lukke.</translation>
<translation id="6990079615885386641">Hent appen i Google Play Butik: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Det forrige website er stadig ved at blive tilføjet</translation>
<translation id="962979164594783469">Installer denne app</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_de.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_de.xtb
index db33b080291..d30723df575 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_de.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_de.xtb
@@ -1,12 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="de">
-<translation id="2139186145475833000">Zum Startbildschirm zufügen</translation>
+<translation id="2139186145475833000">Zum Startbildschirm hinzufügen</translation>
<translation id="2478076885740497414">App installieren</translation>
<translation id="3789841737615482174">Installieren</translation>
+<translation id="3910402514791813257">Konnte nicht installiert werden</translation>
<translation id="4250229828105606438">Screenshot</translation>
<translation id="4665282149850138822"><ph name="NAME" /> wurde deinem Startbildschirm hinzugefügt.</translation>
<translation id="5250483651202458397">Screenshot. Zum Schließen tippen.</translation>
<translation id="6990079615885386641">App aus dem Play Store abrufen: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Vorherige Website wird noch hinzugefügt</translation>
<translation id="962979164594783469">App installieren</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_el.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_el.xtb
index 3a7ecd0382e..30f345b4e59 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_el.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_el.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ΠÏοσθήκη στην αÏχική οθόνη</translation>
<translation id="2478076885740497414">Εγκατάσταση εφαÏμογής</translation>
<translation id="3789841737615482174">Εγκατάσταση</translation>
+<translation id="3910402514791813257">Η εγκατάσταση απέτυχε</translation>
<translation id="4250229828105606438">Στιγμιότυπο οθόνης</translation>
<translation id="4665282149850138822">Ο ιστότοπος <ph name="NAME" /> Ï€Ïοστέθηκε στην αÏχική οθόνη σας</translation>
<translation id="5250483651202458397">Στιγμιότυπο οθόνης. Πατήστε για κλείσιμο.</translation>
<translation id="6990079615885386641">Λήψη της εφαÏμογής από το Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Η Ï€Ïοσθήκη του Ï€ÏοηγοÏμενου ιστοτόπου δεν έχει ολοκληÏωθεί</translation>
<translation id="962979164594783469">Εγκατάσταση αυτής της εφαÏμογής</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_en-GB.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_en-GB.xtb
index 5e4b692f69e..44c9447ce08 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_en-GB.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_en-GB.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Add to Home screen</translation>
<translation id="2478076885740497414">Install app</translation>
<translation id="3789841737615482174">Install</translation>
+<translation id="3910402514791813257">Failed to install</translation>
<translation id="4250229828105606438">Screenshot</translation>
<translation id="4665282149850138822"><ph name="NAME" /> was added to your Home screen</translation>
<translation id="5250483651202458397">Screenshot. Tap to close.</translation>
<translation id="6990079615885386641">Get the app from the Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Still adding previous site</translation>
<translation id="962979164594783469">Install this app</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_es-419.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_es-419.xtb
index 14315f1031b..e7f0d3ccd40 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_es-419.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_es-419.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Agregar a la pantalla principal</translation>
<translation id="2478076885740497414">Instalar aplicación</translation>
<translation id="3789841737615482174">Instalar</translation>
+<translation id="3910402514791813257">No se pudo instalar</translation>
<translation id="4250229828105606438">Captura de pantalla</translation>
<translation id="4665282149850138822">Se agregó <ph name="NAME" /> a la pantalla principal</translation>
<translation id="5250483651202458397">Captura de pantalla. Presionar para cerrar.</translation>
<translation id="6990079615885386641">Descargar la aplicación desde Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Aún se está agregando el sitio anterior</translation>
<translation id="962979164594783469">Instalar esta app</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_es.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_es.xtb
index 02266847341..52876cad13a 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_es.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_es.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Añadir a pantalla de inicio</translation>
<translation id="2478076885740497414">Instalar aplicación</translation>
<translation id="3789841737615482174">Instalar</translation>
+<translation id="3910402514791813257">No se ha podido instalar</translation>
<translation id="4250229828105606438">Captura de pantalla</translation>
<translation id="4665282149850138822">Se ha añadido <ph name="NAME" /> a la pantalla de inicio</translation>
<translation id="5250483651202458397">Captura de pantalla. Toca para cerrarla.</translation>
<translation id="6990079615885386641">Descarga la aplicación en Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Aún se está añadiendo el sitio web anterior</translation>
<translation id="962979164594783469">Instalar esta aplicación</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_et.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_et.xtb
index dcacdb99f9a..5415ef77b7b 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_et.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_et.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Avaekraanile lisamine</translation>
<translation id="2478076885740497414">Rakenduse installimine</translation>
<translation id="3789841737615482174">Installi</translation>
+<translation id="3910402514791813257">Installimine ebaõnnestus</translation>
<translation id="4250229828105606438">Ekraanipilt</translation>
<translation id="4665282149850138822"><ph name="NAME" /> lisati teie avaekraanile</translation>
<translation id="5250483651202458397">Ekraanipilt. Puudutage sulgemiseks.</translation>
<translation id="6990079615885386641">Hankige rakendus Google Play poest: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Eelmist saiti alles lisatakse</translation>
<translation id="962979164594783469">Installige see rakendus</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_eu.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_eu.xtb
index 1e9219c07d5..b26ae706ae4 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_eu.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_eu.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Gehitu hasierako pantailan</translation>
<translation id="2478076885740497414">Instalatu aplikazioa</translation>
<translation id="3789841737615482174">Instalatu</translation>
+<translation id="3910402514791813257">Ezin izan da instalatu</translation>
<translation id="4250229828105606438">Pantaila-argazkia</translation>
<translation id="4665282149850138822">Hasierako pantailan gehitu da <ph name="NAME" /></translation>
<translation id="5250483651202458397">Pantaila-argazkia. Sakatu ixteko.</translation>
<translation id="6990079615885386641">Eskuratu aplikazioa Google Play Store-n: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Oraindik gehitzen ari da aurreko webgunea</translation>
<translation id="962979164594783469">Instalatu aplikazioa</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fa.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fa.xtb
index 95a99f21920..e0a5dc1ae6e 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fa.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fa.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">اÙزودن به صÙحه اصلی</translation>
<translation id="2478076885740497414">نصب برنامه</translation>
<translation id="3789841737615482174">نصب</translation>
+<translation id="3910402514791813257">نصب انجام نشد</translation>
<translation id="4250229828105606438">نماگرÙت</translation>
<translation id="4665282149850138822"><ph name="NAME" /> به صÙحه اصلی شما اضاÙÙ‡ شد</translation>
<translation id="5250483651202458397">نماگرÙت. برای بستن ضربه بزنید.</translation>
<translation id="6990079615885386641">â€Ø¯Ø±ÛŒØ§Ùت برنامه از Ùروشگاه Google Play: â€<ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">همچنان درحال اÙزودن سایت قبلی</translation>
<translation id="962979164594783469">نصب این برنامه</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fi.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fi.xtb
index a3db09ee9b0..0891341aca2 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fi.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fi.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Lisää aloitusnäyttöön</translation>
<translation id="2478076885740497414">Asenna sovellus</translation>
<translation id="3789841737615482174">Asenna</translation>
+<translation id="3910402514791813257">Asennus epäonnistui</translation>
<translation id="4250229828105606438">Kuvakaappaus</translation>
<translation id="4665282149850138822"><ph name="NAME" /> lisättiin aloitusnäytölle.</translation>
<translation id="5250483651202458397">Kuvakaappaus. Sulje napauttamalla.</translation>
<translation id="6990079615885386641">Hanki sovellus Google Play Kaupasta: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Edellisen sivuston lisääminen kesken</translation>
<translation id="962979164594783469">Asenna tämä sovellus</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fil.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fil.xtb
index 5299ac5e1ea..4554ee902ae 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fil.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fil.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Idagdag sa Home screen</translation>
<translation id="2478076885740497414">I-install ang app</translation>
<translation id="3789841737615482174">Mag-install</translation>
+<translation id="3910402514791813257">Hindi na-install</translation>
<translation id="4250229828105606438">Screenshot</translation>
<translation id="4665282149850138822">Idinagdag ang <ph name="NAME" /> sa iyong Home screen</translation>
<translation id="5250483651202458397">Screenshot. I-tap para isara.</translation>
<translation id="6990079615885386641">Kunin ang app mula sa Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Nagdaragdag pa rin ng nakaraang site</translation>
<translation id="962979164594783469">I-install ang app na ito</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fr-CA.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fr-CA.xtb
index cfd0ad13f7e..ec9651584c0 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fr-CA.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fr-CA.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Ajouter à l'écran d'accueil</translation>
<translation id="2478076885740497414">Installer l'application</translation>
<translation id="3789841737615482174">Installer</translation>
+<translation id="3910402514791813257">Échec de l'installation</translation>
<translation id="4250229828105606438">Saisie d'écran</translation>
<translation id="4665282149850138822">Le site <ph name="NAME" /> a été ajouté à votre écran d'accueil</translation>
<translation id="5250483651202458397">Capture d'écran. Touchez pour fermer.</translation>
<translation id="6990079615885386641">Télécharger l'application de la boutique Google Play Store : <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Ajout du site précédent toujours en cours…</translation>
<translation id="962979164594783469">Installer cette application</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fr.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fr.xtb
index d500d3d1257..cfb6527cc90 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_fr.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_fr.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Ajouter à l'écran d'accueil</translation>
<translation id="2478076885740497414">Installer l'application</translation>
<translation id="3789841737615482174">Installer</translation>
+<translation id="3910402514791813257">Échec de l'installation</translation>
<translation id="4250229828105606438">Capture d'écran</translation>
<translation id="4665282149850138822">Le site "<ph name="NAME" />" a bien été ajouté à votre écran d'accueil.</translation>
<translation id="5250483651202458397">Capture d'écran. Appuyez pour fermer.</translation>
<translation id="6990079615885386641">Télécharger l'application sur le Google Play Store : <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">L'ajout du site précédent est toujours en cours</translation>
<translation id="962979164594783469">Installez cette application</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_gl.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_gl.xtb
index acbe0856a98..f74bec8e37c 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_gl.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_gl.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Engadir á pantalla inicio</translation>
<translation id="2478076885740497414">Instalar aplicación</translation>
<translation id="3789841737615482174">Instalar</translation>
+<translation id="3910402514791813257">Produciuse un erro ao levar a cabo a instalación</translation>
<translation id="4250229828105606438">Captura de pantalla</translation>
<translation id="4665282149850138822"><ph name="NAME" /> engadiuse á túa pantalla de inicio</translation>
<translation id="5250483651202458397">Captura de pantalla. Toca para pechala.</translation>
<translation id="6990079615885386641">Descarga a aplicación en Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Aínda se está engadindo o sitio anterior</translation>
<translation id="962979164594783469">Instalar esta aplicación</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_gu.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_gu.xtb
index 47c47460a22..8e567c3153a 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_gu.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_gu.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">હોમસà«àª•à«àª°à«€àª¨ પર ઉમેરો</translation>
<translation id="2478076885740497414">àªàªªà«àª²àª¿àª•à«‡àª¶àª¨ ઇનà«àª¸à«àªŸà«‹àª² કરો</translation>
<translation id="3789841737615482174">ઇનà«àª¸à«àªŸà«‹àª² કરો</translation>
+<translation id="3910402514791813257">ઇનà«àª¸à«àªŸà«‰àª² કરવામાં નિષà«àª«àª³ રહà«àª¯àª¾àª‚</translation>
<translation id="4250229828105606438">સà«àª•à«àª°à«€àª¨àª¶à«‰àªŸ</translation>
<translation id="4665282149850138822"><ph name="NAME" /> ને તમારા હોમ સà«àª•à«àª°à«€àª¨ પર ઉમેરવામાં આવà«àª¯à«àª‚ હતà«àª‚</translation>
<translation id="5250483651202458397">સà«àª•à«àª°à«€àª¨àª¶à«‰àªŸ. બંધ કરવા માટે ટૅપ કરો.</translation>
<translation id="6990079615885386641">Google Play Store માંથી àªàªªà«àª²àª¿àª•à«‡àª¶àª¨ મેળવો: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">હજીપણ પાછલી સાઇટ ઉમેરી રહà«àª¯àª¾àª‚ છે</translation>
<translation id="962979164594783469">આ àªàªª ઇનà«àª¸à«àªŸà«‰àª² કરો</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_hi.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_hi.xtb
index 9e001e66bda..5875342381c 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_hi.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_hi.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">होम सà¥à¤•à¥à¤°à¥€à¤¨ में जोड़ें</translation>
<translation id="2478076885740497414">à¤à¤ªà¥à¤¸ इंसà¥à¤Ÿà¥‰à¤² करें</translation>
<translation id="3789841737615482174">इंसà¥â€à¤Ÿà¥‰à¤² करें</translation>
+<translation id="3910402514791813257">इंसà¥à¤Ÿà¥‰à¤² नहीं किया जा सका</translation>
<translation id="4250229828105606438">सà¥à¤•à¥à¤°à¥€à¤¨à¤¶à¥‰à¤Ÿ</translation>
<translation id="4665282149850138822"><ph name="NAME" /> को आपकी होम सà¥à¤•à¥à¤°à¥€à¤¨ में जोड़ा गया था</translation>
<translation id="5250483651202458397">सà¥à¤•à¥à¤°à¥€à¤¨à¤¶à¥‰à¤Ÿ. बंद करने के लिठइस पर टैप करें.</translation>
<translation id="6990079615885386641">Google Play Store से à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ डाउनलोड करें: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">पिछली साइट अब भी जोड़ी जा रही है</translation>
<translation id="962979164594783469">यह à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ इंसà¥à¤Ÿà¥‰à¤² करें</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_hr.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_hr.xtb
index 7a63dc290c6..c3ed96632c8 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_hr.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_hr.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Dodaj na poÄetni zaslon</translation>
<translation id="2478076885740497414">Instaliranje aplikacije</translation>
<translation id="3789841737615482174">Instaliraj</translation>
+<translation id="3910402514791813257">Instaliranje nije uspjelo</translation>
<translation id="4250229828105606438">Snimka zaslona</translation>
<translation id="4665282149850138822">Web-lokacija <ph name="NAME" /> dodana je na poÄetni zaslon</translation>
<translation id="5250483651202458397">Snimka zaslona. Dodirnite da biste zatvorili.</translation>
<translation id="6990079615885386641">Preuzmite aplikaciju iz Trgovine Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">I dalje se dodaje prethodna web-lokacija</translation>
<translation id="962979164594783469">Instalirajte ovu aplikaciju</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_hu.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_hu.xtb
index 916e6a70ef1..7de537f6620 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_hu.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_hu.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Kezdőképernyőre</translation>
<translation id="2478076885740497414">Alkalmazás telepítése</translation>
<translation id="3789841737615482174">Telepítés</translation>
+<translation id="3910402514791813257">Nem sikerült a telepítés</translation>
<translation id="4250229828105606438">Képernyőkép</translation>
<translation id="4665282149850138822">A(z) <ph name="NAME" /> felkerült a kezdőképernyőre</translation>
<translation id="5250483651202458397">Képernyőkép. Koppintson a bezáráshoz.</translation>
<translation id="6990079615885386641">Alkalmazás beszerzése a Google Play Ãruházból: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Az előző webhely hozzáadása még folyamatban van</translation>
<translation id="962979164594783469">Alkalmazás telepítése</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_hy.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_hy.xtb
index f3dd7fbc92e..7c69cb6e114 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_hy.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_hy.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ Õ£Õ¬Õ­Õ¡Õ¾Õ¸Ö€ Õ§Õ¯Ö€Õ¡Õ¶Õ«Õ¶</translation>
<translation id="2478076885740497414">ÕÕ¥Õ²Õ¡Õ¤Ö€Õ¥Õ¬ Õ°Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ¨</translation>
<translation id="3789841737615482174">ÕÕ¥Õ²Õ¡Õ¤Ö€Õ¥Õ¬</translation>
+<translation id="3910402514791813257">Õ‰Õ°Õ¡Õ»Õ¸Õ²Õ¾Õ¥Ö Õ¿Õ¥Õ²Õ¡Õ¤Ö€Õ¥Õ¬</translation>
<translation id="4250229828105606438">ÕÖ„Ö€Õ«Õ¶Õ·Õ¸Õ©</translation>
<translation id="4665282149850138822"><ph name="NAME" /> Õ¯Õ¡ÕµÖ„Õ¶ Õ¡Õ¾Õ¥Õ¬Õ¡ÖÕ¾Õ¥Ö Õ±Õ¥Ö€ Õ°Õ«Õ´Õ¶Õ¡Õ¯Õ¡Õ¶ Õ§Õ¯Ö€Õ¡Õ¶Õ«Õ¶</translation>
<translation id="5250483651202458397">ÕÖ„Ö€Õ«Õ¶Õ·Õ¸Õ©Ö‰ Õ€ÕºÕ¥Ö„Õ ÖƒÕ¡Õ¯Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€Ö‰</translation>
<translation id="6990079615885386641">ÕÕ¥Õ¼Ö„ Õ¢Õ¥Ö€Õ¥Ö„ Õ°Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ¨ Google Play Ô½Õ¡Õ¶Õ¸Ö‚Õ©Õ«ÖÕ <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Ô´Õ¥Õ¼ Õ¶Õ¡Õ­Õ¸Ö€Õ¤ Õ¯Õ¡ÕµÖ„Õ¶ Õ§ Õ¡Õ¾Õ¥Õ¬Õ¡ÖÕ¾Õ¸Ö‚Õ´</translation>
<translation id="962979164594783469">ÕÕ¥Õ²Õ¡Õ¤Ö€Õ¥Õ¬ Õ°Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ¨</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_id.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_id.xtb
index 0a45337050b..c6f94472e77 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_id.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_id.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Tambahkan ke Layar Utama</translation>
<translation id="2478076885740497414">Instal aplikasi</translation>
<translation id="3789841737615482174">Instal</translation>
+<translation id="3910402514791813257">Gagal menginstal</translation>
<translation id="4250229828105606438">Screenshot</translation>
<translation id="4665282149850138822"><ph name="NAME" /> telah ditambahkan ke layar Utama</translation>
<translation id="5250483651202458397">Screenshot. Ketuk untuk menutup.</translation>
<translation id="6990079615885386641">Dapatkan aplikasi dari Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Masih menambahkan situs sebelumnya</translation>
<translation id="962979164594783469">Instal aplikasi ini</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_is.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_is.xtb
index 041ef21ca14..95836f5ac76 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_is.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_is.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Setja á heimaskjá</translation>
<translation id="2478076885740497414">Setja upp forrit</translation>
<translation id="3789841737615482174">Setja upp</translation>
+<translation id="3910402514791813257">Ekki tókst að setja upp</translation>
<translation id="4250229828105606438">Skjámynd</translation>
<translation id="4665282149850138822"><ph name="NAME" /> var bætt við heimaskjáinn þinn</translation>
<translation id="5250483651202458397">Skjámynd. Ãttu til að loka.</translation>
<translation id="6990079615885386641">Sækja forritið í Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Er enn að bæta fyrra vefsvæði við</translation>
<translation id="962979164594783469">Setja þetta forrit upp</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_it.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_it.xtb
index e7bc640358a..1b7deade09b 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_it.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_it.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Aggiungi a schermata Home</translation>
<translation id="2478076885740497414">Installa app</translation>
<translation id="3789841737615482174">Installa</translation>
+<translation id="3910402514791813257">Installazione non riuscita</translation>
<translation id="4250229828105606438">Screenshot</translation>
<translation id="4665282149850138822">Il sito <ph name="NAME" /> è stato aggiunto alla schermata Home</translation>
<translation id="5250483651202458397">Screenshot. Tocca per chiudere.</translation>
<translation id="6990079615885386641">Scarica l'app dal Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Aggiunta del sito precedente ancora in corso</translation>
<translation id="962979164594783469">Installa questa app</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_iw.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_iw.xtb
index 1e4714d9e30..111cab19733 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_iw.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_iw.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">הוספה לדף הבית</translation>
<translation id="2478076885740497414">התקנת ×”×פליקציה</translation>
<translation id="3789841737615482174">התקנה</translation>
+<translation id="3910402514791813257">ההתקנה נכשלה</translation>
<translation id="4250229828105606438">×¦×™×œ×•× ×ž×¡×š</translation>
<translation id="4665282149850138822">×”×תר <ph name="NAME" /> נוסף למסך דף הבית שלך</translation>
<translation id="5250483651202458397">×¦×™×œ×•× ×ž×¡×š. ×פשר להקיש כדי לסגור.</translation>
<translation id="6990079615885386641">â€×”תקנת ×”×פליקציה מחנות Google Playâ€: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">עדיין מוסיף ×ת ×”×תר הקוד×</translation>
<translation id="962979164594783469">התקנת ×”×פליקציה</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ja.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ja.xtb
index 056efffc7b3..94a3d5109e6 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ja.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ja.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ホーム画é¢ã«è¿½åŠ </translation>
<translation id="2478076885740497414">アプリをインストール</translation>
<translation id="3789841737615482174">インストール</translation>
+<translation id="3910402514791813257">インストールã§ãã¾ã›ã‚“ã§ã—ãŸ</translation>
<translation id="4250229828105606438">スクリーンショット</translation>
<translation id="4665282149850138822">「<ph name="NAME" />ã€ãŒãƒ›ãƒ¼ãƒ ç”»é¢ã«è¿½åŠ ã•ã‚Œã¾ã—ãŸ</translation>
<translation id="5250483651202458397">スクリーンショット。タップã™ã‚‹ã¨é–‰ã˜ã¾ã™ã€‚</translation>
<translation id="6990079615885386641">Google Play ストアã‹ã‚‰ã‚¢ãƒ—リを入手: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">å‰ã®ã‚µã‚¤ãƒˆã‚’追加中ã§ã™</translation>
<translation id="962979164594783469">ã“ã®ã‚¢ãƒ—リをインストール</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ka.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ka.xtb
index b3b4fee11b1..513851973f5 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ka.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ka.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">მთáƒáƒ•áƒáƒ  ეკრáƒáƒœáƒ–ე დáƒáƒ›áƒáƒ¢áƒ”ბáƒ</translation>
<translation id="2478076885740497414">დáƒáƒáƒ˜áƒœáƒ¡áƒ¢áƒáƒšáƒ˜áƒ áƒ”თ პრáƒáƒ’რáƒáƒ›áƒ</translation>
<translation id="3789841737615482174">ინსტáƒáƒšáƒáƒªáƒ˜áƒ</translation>
+<translation id="3910402514791813257">ინსტáƒáƒšáƒáƒªáƒ˜áƒ ვერ მáƒáƒ®áƒ”რხდáƒ</translation>
<translation id="4250229828105606438">ეკრáƒáƒœáƒ˜áƒ¡ áƒáƒœáƒáƒ‘ეჭდი</translation>
<translation id="4665282149850138822"><ph name="NAME" /> დáƒáƒ”მáƒáƒ¢áƒ მთáƒáƒ•áƒáƒ  ეკრáƒáƒœáƒ¡</translation>
<translation id="5250483651202458397">ეკრáƒáƒœáƒ˜áƒ¡ áƒáƒœáƒáƒ‘ეჭდი. შეეხეთ დáƒáƒ¡áƒáƒ®áƒ£áƒ áƒáƒ“.</translation>
<translation id="6990079615885386641">áƒáƒžáƒ˜áƒ¡ Google Play Store-იდáƒáƒœ მიღებáƒ: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ჯერ კიდევ მიმდინáƒáƒ áƒ”áƒáƒ‘ს წინრსáƒáƒ˜áƒ¢áƒ˜áƒ¡ დáƒáƒ›áƒáƒ¢áƒ”ბáƒ</translation>
<translation id="962979164594783469">áƒáƒ› áƒáƒžáƒ˜áƒ¡ ინსტáƒáƒšáƒáƒªáƒ˜áƒ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_kk.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_kk.xtb
index 3eaba3adf29..51a961fc0ef 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_kk.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_kk.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Ðегізгі Ñкранға қоÑу</translation>
<translation id="2478076885740497414">Қолданбаны орнату</translation>
<translation id="3789841737615482174">Орнату</translation>
+<translation id="3910402514791813257">Орнатылмады</translation>
<translation id="4250229828105606438">Скриншот</translation>
<translation id="4665282149850138822">Ðегізгі Ñкранға <ph name="NAME" /> қоÑылды</translation>
<translation id="5250483651202458397">Скриншот. Жабу үшін түртіңіз.</translation>
<translation id="6990079615885386641">Google Play дүкенінен қолданба алу: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Ðлдыңғы Ñайт әлі енгізілуде</translation>
<translation id="962979164594783469">ОÑÑ‹ қолданбаны орнату</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_km.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_km.xtb
index ff775a718f5..f33139de194 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_km.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_km.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">បន្ážáŸ‚មទៅអáŸáž€áŸ’រង់ដើម</translation>
<translation id="2478076885740497414">ážáŸ†áž¡áž¾áž„កម្មវិធី</translation>
<translation id="3789841737615482174">ážáŸ†áž¡áž¾áž„</translation>
+<translation id="3910402514791813257">មិនអាច​ដំឡើងបានទáŸ</translation>
<translation id="4250229828105606438">ážážáž¢áŸáž€áŸ’រង់</translation>
<translation id="4665282149850138822"><ph name="NAME" /> ážáŸ’រូវបានបន្ážáŸ‚មទៅអáŸáž€áŸ’រង់ដើមរបស់អ្នក</translation>
<translation id="5250483651202458397">រូបážážâ€‹áž¢áŸáž€áŸ’រង់។ ចុចដើម្បីបិទ។</translation>
<translation id="6990079615885386641">ទទួលកម្មវិធីនáŸáŸ‡áž–ី Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">កំពុង​បញ្ចូល​ទំពáŸážšâ€‹áž–ីមុន​នៅឡើយ</translation>
<translation id="962979164594783469">ដំឡើង​កម្មវិធី​នáŸáŸ‡</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_kn.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_kn.xtb
index 0b3fdcfa74a..db644967c34 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_kn.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_kn.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ಹೋಮೠಪರದೆಗೆ ಸೇರಿಸಿ</translation>
<translation id="2478076885740497414">ಅಪà³à²²à²¿à²•à³†à³•à²¶à²¨à³ ಸà³à²¥à²¾à²ªà²¿à²¸à²¿</translation>
<translation id="3789841737615482174">ಇನà³â€Œà²¸à³à²Ÿà²¾à²²à³</translation>
+<translation id="3910402514791813257">ಇನà³â€Œà²¸à³à²Ÿà²¾à²²à³ ಮಾಡಲೠವಿಫಲವಾಗಿದೆ</translation>
<translation id="4250229828105606438">ಸà³à²•à³à²°à³€à²¨à³â€Œà²¶à²¾à²Ÿà³</translation>
<translation id="4665282149850138822"><ph name="NAME" /> ಅನà³à²¨à³ ನಿಮà³à²® ಹೋಮೠಪರದೆಗೆ ಸೇರಿಸಲಾಗಿದೆ</translation>
<translation id="5250483651202458397">ಸà³à²•à³à²°à³€à²¨à³â€Œà²¶à²¾à²Ÿà³. ಮà³à²šà³à²šà²²à³ ಟà³à²¯à²¾à²ªà³ ಮಾಡಿ.</translation>
<translation id="6990079615885386641">Google Play Store ನಿಂದ ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³ ಪಡೆದà³à²•à³Šà²³à³à²³à²¿: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ಇನà³à²¨à³‚ ಹಿಂದಿನ ಸೈಟೠಅನà³à²¨à³ ಸೇರಿಸಲಾಗà³à²¤à³à²¤à²¿à²¦à³†</translation>
<translation id="962979164594783469">ಈ ಆà³à²¯à²ªà³ ಅನà³à²¨à³ ಇನà³â€Œà²¸à³à²Ÿà²¾à²²à³ ಮಾಡಿ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ko.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ko.xtb
index 9fac6fad9f4..d650032d1d5 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ko.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ko.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">홈 í™”ë©´ì— ì¶”ê°€</translation>
<translation id="2478076885740497414">앱 설치</translation>
<translation id="3789841737615482174">설치</translation>
+<translation id="3910402514791813257">설치하지 못했습니다.</translation>
<translation id="4250229828105606438">스í¬ë¦°ìƒ·</translation>
<translation id="4665282149850138822"><ph name="NAME" />ì´(ê°€) 홈 í™”ë©´ì— ì¶”ê°€ë¨</translation>
<translation id="5250483651202458397">스í¬ë¦°ìƒ·ìž…니다. 닫으려면 탭하세요.</translation>
<translation id="6990079615885386641">Google Play 스토어ì—ì„œ 앱 다운로드: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ì•„ì§ ì´ì „ 사ì´íŠ¸ 추가 중</translation>
<translation id="962979164594783469">앱 설치</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ky.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ky.xtb
index 8baff56d67c..33b3c36b3aa 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ky.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ky.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Башкы Ñкранга кошуу</translation>
<translation id="2478076885740497414">Колдонмону орнотуу</translation>
<translation id="3789841737615482174">Орнотуу</translation>
+<translation id="3910402514791813257">Орнотулган жок</translation>
<translation id="4250229828105606438">Скриншот</translation>
<translation id="4665282149850138822"><ph name="NAME" /> Башкы Ñкраныңызга кошулду</translation>
<translation id="5250483651202458397">Скриншот. Жабуу үчүн таптаңыз.</translation>
<translation id="6990079615885386641">Колдонмону Google Play Store'дон алыңыз: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Мурунку Ñайт дагы Ñле кошулуп жатат</translation>
<translation id="962979164594783469">Бул колдонмону орнотуу</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_lo.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_lo.xtb
index aa52a62aec9..a546b5cbc4b 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_lo.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_lo.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ເພີ່ມໃສ່ໜ້າຈà»àº«àº¼àº±àº</translation>
<translation id="2478076885740497414">ຕິດຕັ້ງà»àº­àº±àºš</translation>
<translation id="3789841737615482174">​ຕິດ​ຕັ້ງ</translation>
+<translation id="3910402514791813257">ຕິດຕັ້ງບà»à»ˆàºªà»àº²à»€àº¥àº±àº”</translation>
<translation id="4250229828105606438">ຮູບຖ່າàºà»œà»‰àº²àºˆà»</translation>
<translation id="4665282149850138822"><ph name="NAME" /> ຖືàºà»€àºžàºµà»ˆàº¡â€‹à»€àº‚ົ້າ​ໃສ່​ໜ້າ​ຈà»â€‹àº«àº¼àº±àºâ€‹àº‚ອງ​ທ່ານ​à»àº¥à»‰àº§</translation>
<translation id="5250483651202458397">ຮູບໜ້າຈà». à»àº•àº°à»€àºžàº·à»ˆàº­àº›àº´àº”.</translation>
<translation id="6990079615885386641">ເອົາ​à»àº­àº±àºšâ€‹àºˆàº²àº Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">àºàº±àº‡àºàº³àº¥àº±àº‡à»€àºžàºµà»ˆàº¡à»€àº§àº±àºšà»„ຊàºà»ˆàº­àº™à»œà»‰àº²àº™àºµà»‰àº¢àº¹à»ˆ</translation>
<translation id="962979164594783469">ຕິດຕັ້ງà»àº­àº±àºšàº™àºµà»‰</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_lt.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_lt.xtb
index 72624a1c2ba..3c2e9358317 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_lt.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_lt.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">PridÄ—ti prie pagr. ekrano</translation>
<translation id="2478076885740497414">Įdiegti programą</translation>
<translation id="3789841737615482174">Įdiegti</translation>
+<translation id="3910402514791813257">Nepavyko įdiegti</translation>
<translation id="4250229828105606438">Ekrano kopija</translation>
<translation id="4665282149850138822">Svetainė „<ph name="NAME" />“ pridėta prie pagrindinio ekrano</translation>
<translation id="5250483651202458397">Ekrano kopija. Palieskite, kad uždarytumėte.</translation>
<translation id="6990079615885386641">Gauti programą iš „Google Play“ parduotuvės: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Vis dar pridedama ankstesnÄ— svetainÄ—</translation>
<translation id="962979164594783469">Įdiegti šią programą</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_lv.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_lv.xtb
index 9922671b89b..2bad212ca84 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_lv.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_lv.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Pievienot sÄkuma ekrÄnam</translation>
<translation id="2478076885740497414">Instalēt lietotni</translation>
<translation id="3789841737615482174">Instalēt</translation>
+<translation id="3910402514791813257">NeizdevÄs instalÄ“t</translation>
<translation id="4250229828105606438">EkrÄnuzņēmums</translation>
<translation id="4665282149850138822">Vietne <ph name="NAME" /> tika pievienota sÄkuma ekrÄnam.</translation>
<translation id="5250483651202458397">EkrÄnuzņēmums. Pieskarieties, lai to aizvÄ“rtu.</translation>
<translation id="6990079615885386641">IegÅ«stiet lietotni Google Play veikalÄ: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">JoprojÄm notiek iepriekÅ¡Ä“jÄs vietnes pievienoÅ¡ana</translation>
<translation id="962979164594783469">Instalēt šo lietotni</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_mk.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_mk.xtb
index dbec8d1e5af..d19738e9bfb 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_mk.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_mk.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Додај на почетниот екран</translation>
<translation id="2478076885740497414">ИнÑталирај апликација</translation>
<translation id="3789841737615482174">ИнÑталирај</translation>
+<translation id="3910402514791813257">Ðе уÑпеа да Ñе инÑталира</translation>
<translation id="4250229828105606438">Слика од екран</translation>
<translation id="4665282149850138822"><ph name="NAME" /> е додадена на вашиот Почетен екран</translation>
<translation id="5250483651202458397">Слика од екранот. Допрете за да Ñе затвори.</translation>
<translation id="6990079615885386641">Преземете ја апликацијата од продавницата на Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Сè уште Ñе додава претходниот Ñајт</translation>
<translation id="962979164594783469">ИнÑталирајте ја апликацијава</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ml.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ml.xtb
index 55fc94e9c44..ad57b613bde 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ml.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ml.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ഹോംസàµâ€Œà´•àµà´°àµ€à´¨à´¿àµ½ ചേർകàµà´•àµà´•</translation>
<translation id="2478076885740497414">ആപàµà´ªàµ ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¯àµà´•</translation>
<translation id="3789841737615482174">ഇനàµâ€à´¸àµà´±àµà´±à´¾àµ¾ ചെയàµà´¯àµà´•</translation>
+<translation id="3910402514791813257">ഇൻസàµà´±àµà´±à´¾àµ¾ ചെയàµà´¯à´¾à´¨à´¾à´¯à´¿à´²àµà´²</translation>
<translation id="4250229828105606438">à´¸àµâ€Œà´•àµà´°àµ€àµ»à´·àµ‹à´Ÿàµà´Ÿàµ</translation>
<translation id="4665282149850138822"><ph name="NAME" /> à´Žà´¨àµà´¨à´¯à´¾à´³àµ† നിങàµà´™à´³àµà´Ÿàµ† ഹോം à´¸àµâ€Œà´•àµà´°àµ€à´¨à´¿àµ½ ചേർതàµà´¤àµ</translation>
<translation id="5250483651202458397">à´¸àµâ€Œà´•àµà´°àµ€àµ»à´·àµ‹à´Ÿàµà´Ÿàµ. à´…à´Ÿà´¯àµà´•àµà´•à´¾àµ» ടാപàµà´ªàµ ചെയàµà´¯àµà´•.</translation>
<translation id="6990079615885386641">Google Play Store-ൽ നിനàµà´¨àµ ആപàµà´ªàµ à´¸àµà´µàµ€à´•à´°à´¿à´•àµà´•àµà´•: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ഇപàµà´ªàµ‹à´´àµà´‚ à´®àµà´®àµà´ªà´¤àµà´¤àµ† സൈറàµà´±àµ ചേർതàµà´¤àµà´•àµŠà´£àµà´Ÿà´¿à´°à´¿à´•àµà´•àµà´•à´¯à´¾à´£àµ</translation>
<translation id="962979164594783469">à´ˆ ആപàµà´ªàµ ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¯àµà´•</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_mn.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_mn.xtb
index 521d3b30c17..6ac82846a79 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_mn.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_mn.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Ðүүр хуудÑын дÑлгÑц Ñ€Ò¯Ò¯ нÑмÑÑ…</translation>
<translation id="2478076885740497414">Програм Ñуулгах</translation>
<translation id="3789841737615482174">Суулгах</translation>
+<translation id="3910402514791813257">Суулгаж чадÑангүй</translation>
<translation id="4250229828105606438">ДÑлгÑцийн агшин</translation>
<translation id="4665282149850138822"><ph name="NAME" />-г таны үндÑÑн дÑлгÑцÑд нÑмÑÑн</translation>
<translation id="5250483651202458397">ДÑлгÑцийн агшин. Хаахын тулд товшино уу.</translation>
<translation id="6990079615885386641">Google Play Store-Ñ Ð°Ð¿Ð¿ авах: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Өмнөх Ñайтыг нÑмж байна</translation>
<translation id="962979164594783469">Ð­Ð½Ñ Ð°Ð¿Ð¿Ñ‹Ð³ Ñуулгах</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_mr.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_mr.xtb
index 4632826540b..9149bc89b1b 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_mr.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_mr.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">होम सà¥à¤•à¥à¤°à¥€à¤¨à¤µà¤° जोडा</translation>
<translation id="2478076885740497414">अâ€à¥…प इंसà¥à¤Ÿà¥‰à¤² करा</translation>
<translation id="3789841737615482174">सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ करा</translation>
+<translation id="3910402514791813257">इंसà¥à¤Ÿà¥‰à¤² करता आला नाही</translation>
<translation id="4250229828105606438">सà¥à¤•à¥à¤°à¥€à¤¨à¤¶à¥‰à¤Ÿ</translation>
<translation id="4665282149850138822"><ph name="NAME" /> ला तà¥à¤®à¤šà¥à¤¯à¤¾ होम सà¥à¤•à¥à¤°à¥€à¤¨à¤µà¤° पेअरिंग आले</translation>
<translation id="5250483651202458397">सà¥à¤•à¥à¤°à¥€à¤¨à¤¶à¥‰à¤Ÿ. बंद करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ टॅप करा.</translation>
<translation id="6990079615885386641">Google Play Store मधून ॲप मिळवा: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">अदà¥à¤¯à¤¾à¤ª मागील साइट जोडत आहे</translation>
<translation id="962979164594783469">हे ॲप इंसà¥à¤Ÿà¥‰à¤² करा</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ms.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ms.xtb
index 3adf3af7457..034813ece08 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ms.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ms.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Tambahkan kepada Skrin utama</translation>
<translation id="2478076885740497414">Pasang apl</translation>
<translation id="3789841737615482174">Pasang</translation>
+<translation id="3910402514791813257">Gagal memasang</translation>
<translation id="4250229828105606438">Tangkapan skrin</translation>
<translation id="4665282149850138822"><ph name="NAME" /> telah ditambahkan ke skrin Utama anda</translation>
<translation id="5250483651202458397">Tangkapan skrin. Ketik untuk tutup.</translation>
<translation id="6990079615885386641">Dapatkan apl daripada Gedung Play Google: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Masih menambahkan tapak yang terdahulu</translation>
<translation id="962979164594783469">Pasang apl ini</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_my.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_my.xtb
index 51a9bb8e4ae..cc193b923bd 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_my.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_my.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ပင်မမျက်နှာပြင်သို့ ထည့်ရန်</translation>
<translation id="2478076885740497414">အက်ပ် ထည့်သွင်းရန်</translation>
<translation id="3789841737615482174">á€á€•á€ºá€†á€„်ရန်</translation>
+<translation id="3910402514791813257">ထည့်သွင်းáမရပါ</translation>
<translation id="4250229828105606438">မျက်နှာပြင် လျှပ်á€á€•á€¼á€€á€ºá€•á€¯á€¶</translation>
<translation id="4665282149850138822"><ph name="NAME" />ကို သင်á ပင်မ မျက်နှာပြင်သို့ ထည့်ပေးá€á€²á€·á€žá€Šá€º</translation>
<translation id="5250483651202458397">ဖန်သားပြင်ဓာá€á€ºá€•á€¯á€¶á‹ ပိá€á€ºá€›á€”် á€á€­á€¯á€·á€•á€«á‹</translation>
<translation id="6990079615885386641">Google ပလေးစá€á€­á€¯á€¸á€™á€¾ အက်ပ်အားရယူမည်- <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ယá€á€„်ဆိုက်ကို ထည့်သွင်းနေဆဲဖြစ်သည်</translation>
<translation id="962979164594783469">ဤအက်ပ်ကို ထည့်သွင်းရန်</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ne.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ne.xtb
index 470b134766c..555c660773f 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ne.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ne.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">होम सà¥à¤•à¥à¤°à¤¿à¤¨à¤®à¤¾ हालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="2478076885740497414">à¤à¤ª सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
<translation id="3789841737615482174">सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
+<translation id="3910402514791813257">इनà¥à¤¸à¥à¤Ÿà¤² गरà¥à¤¨ सकिà¤à¤¨</translation>
<translation id="4250229828105606438">सà¥à¤•à¥à¤°à¤¿à¤¸à¤Ÿ</translation>
<translation id="4665282149850138822"><ph name="NAME" /> लाई तपाईà¤à¤•à¥‹ गृहपृषà¥à¤  सà¥à¤•à¥à¤°à¤¿à¤¨à¤®à¤¾ थपियो</translation>
<translation id="5250483651202458397">सà¥à¤•à¥à¤°à¤¿à¤¨à¤¸à¤Ÿà¥¤ बनà¥à¤¦ गरà¥à¤¨ टà¥à¤¯à¤¾à¤ª गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤</translation>
<translation id="6990079615885386641">Google पà¥à¤²à¥‡ सà¥à¤Ÿà¥‹à¤°à¤¬à¤¾à¤Ÿ à¤à¤ª पà¥à¤°à¤¾à¤ªà¥à¤¤ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">अघिलà¥à¤²à¥‹ साइट अà¤à¥ˆ थपिà¤à¤¦à¥ˆ</translation>
<translation id="962979164594783469">यो à¤à¤ª इनà¥à¤¸à¥à¤Ÿà¤² गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_nl.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_nl.xtb
index 1db5a4bd905..0392ccc077c 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_nl.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_nl.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Toevoegen aan startscherm</translation>
<translation id="2478076885740497414">App installeren</translation>
<translation id="3789841737615482174">Installeren</translation>
+<translation id="3910402514791813257">Installatie mislukt</translation>
<translation id="4250229828105606438">Screenshot</translation>
<translation id="4665282149850138822"><ph name="NAME" /> is toegevoegd aan je startscherm</translation>
<translation id="5250483651202458397">Screenshot. Tik om te sluiten.</translation>
<translation id="6990079615885386641">Download de app uit de Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Nog steeds bezig met toevoegen van vorige site</translation>
<translation id="962979164594783469">Deze app installeren</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_no.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_no.xtb
index cc9a6b799d0..28c6c65d781 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_no.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_no.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Legg til på startsiden</translation>
<translation id="2478076885740497414">Installér appen</translation>
<translation id="3789841737615482174">Installer</translation>
+<translation id="3910402514791813257">Installasjonen mislyktes</translation>
<translation id="4250229828105606438">Skjermdump</translation>
<translation id="4665282149850138822"><ph name="NAME" /> ble lagt til på startskjermen</translation>
<translation id="5250483651202458397">Skjermdump. Trykk for å lukke.</translation>
<translation id="6990079615885386641">Last ned appen fra Google Play Butikk: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Holder fortsatt på å legge til det forrige nettstedet</translation>
<translation id="962979164594783469">Installer denne appen</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_or.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_or.xtb
index 71993c6f6ed..c2e84ad5d34 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_or.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_or.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ହୋମୠସà­à¬•à­à¬°à¬¿à¬¨à¬°à­‡ ଯୋଗ କରନà­à¬¤à­</translation>
<translation id="2478076885740497414">ଆପକୠଇନଷà­à¬Ÿà¬²à­ କରନà­à¬¤à­</translation>
<translation id="3789841737615482174">ସଂସà­à¬¥à¬¾à¬ªà¬¨</translation>
+<translation id="3910402514791813257">ଇନଷà­à¬Ÿà¬² କରିବାରେ ବିଫଳ ହୋଇଛି</translation>
<translation id="4250229828105606438">ସà­à¬•à­à¬°à¬¿à¬¨à­â€à¬¸à¬Ÿà­</translation>
<translation id="4665282149850138822">ଆପଣଙà­à¬• ମୂଳ ସà­à¬•à­à¬°à¬¿à¬¨à­â€Œà¬°à­‡ <ph name="NAME" />କୠଯୋଗ କରାଯାଇଥିଲା</translation>
<translation id="5250483651202458397">ସà­à¬•à­à¬°à¬¿à¬¨à¬¸à¬Ÿà­à¥¤ ବନà­à¬¦ କରିବାକୠଟାପୠକରନà­à¬¤à­à¥¤</translation>
<translation id="6990079615885386641">Google Play ଷà­à¬Ÿà­‹à¬°à­â€Œà¬°à­ ଆପୠପà­à¬°à¬¾à¬ªà­à¬¤ କରନà­à¬¤à­: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ପୂରà­à¬¬à¬° ସାଇଟà­â€ à¬à¬¬à­‡ ମଧà­à­Ÿ ଯୋଗ କରà­à¬›à¬¿</translation>
<translation id="962979164594783469">à¬à¬¹à¬¿ ଆପୠଇନଷà­à¬Ÿà¬²à­ କରନà­à¬¤à­</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_pa.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_pa.xtb
index 7c6a7eac4d0..4d41d923b67 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_pa.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_pa.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">ਹੋਮ ਸਕà©à¨°à©€à¨¨ 'ਤੇ ਸ਼ਾਮਲ ਕਰੋ</translation>
<translation id="2478076885740497414">à¨à¨ª ਇੰਸਟੌਲ ਕਰੋ</translation>
<translation id="3789841737615482174">ਸਥਾਪਤ ਕਰੋ</translation>
+<translation id="3910402514791813257">ਸਥਾਪਤ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ</translation>
<translation id="4250229828105606438">ਸਕà©à¨°à©€à¨¨à¨¸à¨¼à¨¾à¨Ÿ</translation>
<translation id="4665282149850138822"><ph name="NAME" /> ਨੂੰ ਤà©à¨¹à¨¾à¨¡à©€ ਹੋਮ ਸਕà©à¨°à©€à¨¨ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ</translation>
<translation id="5250483651202458397">ਸਕà©à¨°à©€à¨¨à¨¸à¨¼à¨¾à¨Ÿà¥¤ ਬੰਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।</translation>
<translation id="6990079615885386641">Google Play Store ਵਿੱਚੋਂ à¨à¨ª ਪà©à¨°à¨¾à¨ªà¨¤ ਕਰੋ: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ਹਾਲੇ ਪਿਛਲੀ ਸਾਈਟ ਨੂੰ ਸ਼ਾਮਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ</translation>
<translation id="962979164594783469">ਇਹ à¨à¨ª ਸਥਾਪਤ ਕਰੋ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_pl.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_pl.xtb
index 1c902b31ae7..2974d4d187f 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_pl.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_pl.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Dodaj do ekranu głównego</translation>
<translation id="2478076885740497414">Zainstaluj aplikacjÄ™</translation>
<translation id="3789841737615482174">Zainstaluj</translation>
+<translation id="3910402514791813257">Nie udało się zainstalować</translation>
<translation id="4250229828105606438">Zrzut ekranu</translation>
<translation id="4665282149850138822">Strona <ph name="NAME" /> została dodana do ekranu głównego</translation>
<translation id="5250483651202458397">Zrzut ekranu. Kliknij, by zamknąć.</translation>
<translation id="6990079615885386641">Pobierz aplikacjÄ™ ze sklepu Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Nadal dodajÄ™ poprzedniÄ… stronÄ™</translation>
<translation id="962979164594783469">Zainstaluj tÄ™ aplikacjÄ™</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-BR.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-BR.xtb
index a56881f160f..c2b9ecfe3c8 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-BR.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-BR.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Adicionar à tela inicial</translation>
<translation id="2478076885740497414">Instalar aplicativo</translation>
<translation id="3789841737615482174">Instalar</translation>
+<translation id="3910402514791813257">Falha ao instalar</translation>
<translation id="4250229828105606438">Captura de tela</translation>
<translation id="4665282149850138822"><ph name="NAME" /> foi adicionado à sua tela inicial</translation>
<translation id="5250483651202458397">Captura de tela. Toque para fechar.</translation>
<translation id="6990079615885386641">Comprar aplicativo na Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Ainda adicionando o site anterior</translation>
<translation id="962979164594783469">Instalar este app</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-PT.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-PT.xtb
index 2587754ecfc..c5f2d99f130 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-PT.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_pt-PT.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Adicionar ao ecrã principal</translation>
<translation id="2478076885740497414">Instalar aplicação</translation>
<translation id="3789841737615482174">Instalar</translation>
+<translation id="3910402514791813257">Falha ao instalar</translation>
<translation id="4250229828105606438">Captura de ecrã</translation>
<translation id="4665282149850138822"><ph name="NAME" /> foi adicionado ao seu Ecrã principal</translation>
<translation id="5250483651202458397">Captura de ecrã. Toque para fechar.</translation>
<translation id="6990079615885386641">Obter a aplicação na Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Ainda a adicionar o site anterior…</translation>
<translation id="962979164594783469">Instalar esta app</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ro.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ro.xtb
index ceeb574bf09..7cc41219627 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ro.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ro.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Adaugă pe ecran pornire</translation>
<translation id="2478076885740497414">Instalați aplicația</translation>
<translation id="3789841737615482174">Instalează</translation>
+<translation id="3910402514791813257">Nu s-a instalat</translation>
<translation id="4250229828105606438">Captură de ecran</translation>
<translation id="4665282149850138822">Site-ul <ph name="NAME" /> a fost adăugat pe ecranul de pornire</translation>
<translation id="5250483651202458397">Captură de ecran. Atinge pentru a închide.</translation>
<translation id="6990079615885386641">Descarcă aplicația din Magazinul Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Încă se adaugă site-ul anterior</translation>
<translation id="962979164594783469">Instalează aplicația</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ru.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ru.xtb
index 57b788d0306..b9d2f984a2c 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ru.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ru.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Добавить на гл. Ñкран</translation>
<translation id="2478076885740497414">УÑтановить приложение</translation>
<translation id="3789841737615482174">УÑтановить</translation>
+<translation id="3910402514791813257">Сбой уÑтановки</translation>
<translation id="4250229828105606438">Скриншот</translation>
<translation id="4665282149850138822">Сайт <ph name="NAME" /> добавлен на главный Ñкран</translation>
<translation id="5250483651202458397">Скриншот. Ðажмите, чтобы закрыть.</translation>
<translation id="6990079615885386641">Приложение в Google Play Маркете: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Предыдущий Ñайт ещё не добавлен</translation>
<translation id="962979164594783469">УÑтановить приложение</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_si.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_si.xtb
index 5cdfadff009..faa49618dd9 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_si.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_si.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">මුල් පිටු තිරයට එක් කරන්න</translation>
<translation id="2478076885740497414">à·€à·à¶©à·ƒà¶§à·„න සථà·à¶´à¶±à¶º කරන්න</translation>
<translation id="3789841737615482174">ස්ථà·à¶´à¶±à¶º</translation>
+<translation id="3910402514791813257">ස්ථà·à¶´à¶±à¶º කිරීම අසà·à¶»à·Šà¶®à¶š විය</translation>
<translation id="4250229828105606438">තිරරූපය</translation>
<translation id="4665282149850138822"><ph name="NAME" /> ඔබේ මුල් තිරය වෙත එක් කරන ලදී</translation>
<translation id="5250483651202458397">තිර රුව. à·€à·à·ƒà·“මට තට්ටු කරන්න.</translation>
<translation id="6990079615885386641">Google Play ගබඩà·à·€ වෙතින් යෙදුම ලබà·à¶œà¶±à·Šà¶±: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">තවමත් කලින් අඩවියට එක් කරමින්</translation>
<translation id="962979164594783469">මෙම යෙදුම ස්ථà·à¶´à¶±à¶º කරන්න</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sk.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sk.xtb
index ce7ee891662..30b3b9a5135 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sk.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sk.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Pridať na plochu</translation>
<translation id="2478076885740497414">Inštalovať aplikáciu</translation>
<translation id="3789841737615482174">Inštalovať</translation>
+<translation id="3910402514791813257">Nepodarilo sa nainštalovať</translation>
<translation id="4250229828105606438">Snímka obrazovky</translation>
<translation id="4665282149850138822">Stránky <ph name="NAME" /> boli pridané na plochu</translation>
<translation id="5250483651202458397">Snímka obrazovky. Zavriete klepnutím.</translation>
<translation id="6990079615885386641">Získať aplikáciu z Obchodu Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Pridávanie predchádzajúceho webu stále prebieha</translation>
<translation id="962979164594783469">Inštalovať túto aplikáciu</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sl.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sl.xtb
index dcf3f40981e..91635489540 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sl.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sl.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Dodajanje na zaÄetni zaslon</translation>
<translation id="2478076885740497414">Namestitev aplikacije</translation>
<translation id="3789841737615482174">Namesti</translation>
+<translation id="3910402514791813257">Namestitev ni uspela</translation>
<translation id="4250229828105606438">Posnetek zaslona</translation>
<translation id="4665282149850138822">Spletno mesto <ph name="NAME" /> je bilo dodano na zaÄetni zaslon</translation>
<translation id="5250483651202458397">Posnetek zaslona. Dotaknite se, Äe ga želite zapreti.</translation>
<translation id="6990079615885386641">Prenos aplikacije iz Trgovine Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Dodajanje prejšnjega spletnega mesta še vedno poteka</translation>
<translation id="962979164594783469">Namestite to aplikacijo</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sq.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sq.xtb
index 36c66a2447e..769254c5eb5 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sq.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sq.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Shtoje te ekrani "Faqja kryesore"</translation>
<translation id="2478076885740497414">Instalo aplikacionin</translation>
<translation id="3789841737615482174">Instalo</translation>
+<translation id="3910402514791813257">Instalimi nuk u krye</translation>
<translation id="4250229828105606438">Pamje e ekranit</translation>
<translation id="4665282149850138822"><ph name="NAME" /> u shtua në ekranin bazë</translation>
<translation id="5250483651202458397">Pamja e ekranit. Trokit për ta mbyllur.</translation>
<translation id="6990079615885386641">Merr aplikacionin nga Dyqani i "Google Play": <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Ende po shtohet sajti i mëparshëm</translation>
<translation id="962979164594783469">Instalo këtë aplikacion</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sr-Latn.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sr-Latn.xtb
index a1c9280c9fc..99d16bde526 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sr-Latn.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sr-Latn.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Dodaj na poÄetni ekran</translation>
<translation id="2478076885740497414">Instaliraj aplikaciju</translation>
<translation id="3789841737615482174">Instaliraj</translation>
+<translation id="3910402514791813257">Instaliranje paketa nije uspelo</translation>
<translation id="4250229828105606438">Snimak ekrana</translation>
<translation id="4665282149850138822"><ph name="NAME" /> je dodat na poÄetni ekran</translation>
<translation id="5250483651202458397">Snimak ekrana. Dodirnite da biste ga zatvorili.</translation>
<translation id="6990079615885386641">Preuzmite aplikaciju iz Google Play prodavnice: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Još uvek dodajemo prethodni sajt</translation>
<translation id="962979164594783469">Instalirajte ovu aplikaciju</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sr.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sr.xtb
index d36839f9325..a939504bc69 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sr.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sr.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Додај на почетни екран</translation>
<translation id="2478076885740497414">ИнÑталирај апликацију</translation>
<translation id="3789841737615482174">ИнÑталирај</translation>
+<translation id="3910402514791813257">ИнÑталирање пакета није уÑпело</translation>
<translation id="4250229828105606438">Снимак екрана</translation>
<translation id="4665282149850138822"><ph name="NAME" /> је додат на почетни екран</translation>
<translation id="5250483651202458397">Снимак екрана. Додирните да биÑте га затворили.</translation>
<translation id="6990079615885386641">Преузмите апликацију из Google Play продавнице: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Још увек додајемо претходни Ñајт</translation>
<translation id="962979164594783469">ИнÑталирајте ову апликацију</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sv.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sv.xtb
index ae111ab53b8..8aef455d2a4 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sv.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sv.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Lägg till på startskärmen</translation>
<translation id="2478076885740497414">Installera appen</translation>
<translation id="3789841737615482174">Installera</translation>
+<translation id="3910402514791813257">Installationen misslyckades</translation>
<translation id="4250229828105606438">Skärmbild</translation>
<translation id="4665282149850138822"><ph name="NAME" /> har lagts till på startskärmen.</translation>
<translation id="5250483651202458397">Skärmbild. Tryck och stäng.</translation>
<translation id="6990079615885386641">Hämta appen från Google Play Butik: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Processen pågår fortfarande för den förra webbplatsen</translation>
<translation id="962979164594783469">Installera appen</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sw.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sw.xtb
index 3c2c5be7eca..2cce69043b4 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_sw.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_sw.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Ongeza kwenye skrini ya kwanza</translation>
<translation id="2478076885740497414">Sakinisha programu</translation>
<translation id="3789841737615482174">Sakinisha</translation>
+<translation id="3910402514791813257">Imeshindwa kusakinishwa</translation>
<translation id="4250229828105606438">Picha ya skrini</translation>
<translation id="4665282149850138822"><ph name="NAME" /> iliongezwa kwenye Skrini yako ya kwanza</translation>
<translation id="5250483651202458397">Picha ya skrini. Gusa ili ufunge.</translation>
<translation id="6990079615885386641">Pata programu kutoka kwenye Duka la Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Bado inaongeza tovuti ya awali</translation>
<translation id="962979164594783469">Sakinisha programu hii</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ta.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ta.xtb
index 9a91e94f9f7..18eb4d88692 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ta.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ta.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">à®®à¯à®•à®ªà¯à®ªà¯à®¤à¯ திரையில௠சேரà¯</translation>
<translation id="2478076885740497414">ஆபà¯à®¸à¯ˆ நிறà¯à®µà¯</translation>
<translation id="3789841737615482174">நிறà¯à®µà¯à®•</translation>
+<translation id="3910402514791813257">நிறà¯à®µ à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="4250229828105606438">ஸà¯à®•à®¿à®°à¯€à®©à¯à®·à®¾à®Ÿà¯</translation>
<translation id="4665282149850138822"><ph name="NAME" /> உஙà¯à®•à®³à¯ à®®à¯à®•à®ªà¯à®ªà¯à®¤à¯ திரையில௠சேரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯</translation>
<translation id="5250483651202458397">ஸà¯à®•à®¿à®°à¯€à®©à¯à®·à®¾à®Ÿà¯. மூடà¯à®µà®¤à®±à¯à®•à¯à®¤à¯ தடà¯à®Ÿà®µà¯à®®à¯.</translation>
<translation id="6990079615885386641">Google Play Store இலிரà¯à®¨à¯à®¤à¯ பயனà¯à®ªà®¾à®Ÿà¯à®Ÿà¯ˆà®ªà¯ பெறவà¯à®®à¯: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">à®®à¯à®¨à¯à®¤à¯ˆà®¯ தளதà¯à®¤à¯ˆ இனà¯à®©à¯à®®à¯ சேரà¯à®•à¯à®•à®¿à®±à®¤à¯</translation>
<translation id="962979164594783469">இநà¯à®¤ ஆபà¯à®¸à¯ˆ நிறà¯à®µà¯à®®à¯</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_te.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_te.xtb
index 29292b8db95..65613b622c9 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_te.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_te.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">హోమౠసà±à°•à±à°°à±€à°¨à±â€Œà°•à± జోడించà±</translation>
<translation id="2478076885740497414">యాపà±â€Œà°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయండి</translation>
<translation id="3789841737615482174">ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయి</translation>
+<translation id="3910402514791813257">ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయడంలో విఫలమైంది</translation>
<translation id="4250229828105606438">à°¸à±à°•à±à°°à±€à°¨à±â€Œà°·à°¾à°Ÿà±</translation>
<translation id="4665282149850138822"><ph name="NAME" /> మీ హోమౠసà±à°•à±à°°à±€à°¨à±â€Œà°•à± జోడించబడింది</translation>
<translation id="5250483651202458397">à°¸à±à°•à±à°°à±€à°¨à±â€Œà°·à°¾à°Ÿà±. మూసివేయడానికి à°Ÿà±à°¯à°¾à°ªà± చేయండి.</translation>
<translation id="6990079615885386641">Google Play Store à°¨à±à°‚à°¡à°¿ యాపà±â€Œà°¨à± పొందండి: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ఇంకా à°®à±à°¨à±à°ªà°Ÿà°¿ సైటà±â€Œà°¨à± జోడిసà±à°¤à±‹à°‚ది</translation>
<translation id="962979164594783469">à°ˆ యాపà±â€Œà°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయండి</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_th.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_th.xtb
index 5b12c1daa89..3562b0e6c5b 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_th.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_th.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">เพิ่มลงในหน้าจอหลัà¸</translation>
<translation id="2478076885740497414">ติดตั้งà¹à¸­à¸›</translation>
<translation id="3789841737615482174">ติดตั้ง</translation>
+<translation id="3910402514791813257">ติดตั้งไม่สำเร็จ</translation>
<translation id="4250229828105606438">ภาพหน้าจอ</translation>
<translation id="4665282149850138822">เพิ่ม <ph name="NAME" /> ลงในหน้าà¹à¸£à¸à¹à¸¥à¹‰à¸§</translation>
<translation id="5250483651202458397">ภาพหน้าจอ à¹à¸•à¸°à¹€à¸žà¸·à¹ˆà¸­à¸›à¸´à¸”</translation>
<translation id="6990079615885386641">รับà¹à¸­à¸›à¸ˆà¸²à¸ Google Play Store: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ยังเพิ่มไซต์à¸à¹ˆà¸­à¸™à¸«à¸™à¹‰à¸²à¸­à¸¢à¸¹à¹ˆ</translation>
<translation id="962979164594783469">ติดตั้งà¹à¸­à¸›à¸™à¸µà¹‰</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_tr.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_tr.xtb
index 992d97c656a..9bbcddcef63 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_tr.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_tr.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Ana ekrana ekle</translation>
<translation id="2478076885740497414">Uygulamayı yükle</translation>
<translation id="3789841737615482174">Yükle</translation>
+<translation id="3910402514791813257">Yüklenemedi</translation>
<translation id="4250229828105606438">Ekran görüntüsü</translation>
<translation id="4665282149850138822"><ph name="NAME" />, Ana ekranınıza eklendi</translation>
<translation id="5250483651202458397">Ekran görüntüsü. Kapatmak için dokunun.</translation>
<translation id="6990079615885386641">Uygulamayı Google Play Store'dan edinin: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Önceki siteyi ekleme işlemi devam ediyor.</translation>
<translation id="962979164594783469">Bu uygulamayı yükleyin</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_uk.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_uk.xtb
index d2a7d3f3f4f..52d1672abe9 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_uk.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_uk.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Додати на головний екран</translation>
<translation id="2478076885740497414">УÑтановити додаток</translation>
<translation id="3789841737615482174">УÑтановити</translation>
+<translation id="3910402514791813257">Ðе вдалоÑÑ Ð²Ñтановити додаток</translation>
<translation id="4250229828105606438">Знімок екрана</translation>
<translation id="4665282149850138822">Веб-Ñайт <ph name="NAME" /> додано на головний екран</translation>
<translation id="5250483651202458397">Знімок екрана. ТоркнітьÑÑ, щоб закрити.</translation>
<translation id="6990079615885386641">Завантажити додаток із магазину Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Попередній Ñайт ще додаєтьÑÑ</translation>
<translation id="962979164594783469">УÑтановити цей додаток</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ur.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ur.xtb
index 11ae74a5db6..e07378060c2 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_ur.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_ur.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Ûوم اسکرین میں شامل کریں</translation>
<translation id="2478076885740497414">ایپ انسٹال کریں</translation>
<translation id="3789841737615482174">انسٹال کریں</translation>
+<translation id="3910402514791813257">انسٹال کرنے میں ناکام</translation>
<translation id="4250229828105606438">اسکرین شاٹ</translation>
<translation id="4665282149850138822"><ph name="NAME" /> Ú©Ùˆ آپ Ú©ÛŒ Ûوم اسکرین میں شامل کر دیا گیا تھا</translation>
<translation id="5250483651202458397">اسکرین شاٹ۔ بند کرنے کے لئے تھپتھپائیں۔</translation>
<translation id="6990079615885386641">â€Google Play اسٹور سے ایپ حاصل کریں: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ابھی بھی Ø³Ø§Ø¨Ù‚Û Ø³Ø§Ø¦Ù¹ شامل Ú©ÛŒ جا رÛÛŒ ÛÛ’</translation>
<translation id="962979164594783469">ÛŒÛ Ø§ÛŒÙ¾ انسٹال کریں</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_uz.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_uz.xtb
index 6b4e7508b7c..30688b6e0b9 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_uz.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_uz.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Bosh ekranga qo‘shish</translation>
<translation id="2478076885740497414">Ilovani o‘rnatish</translation>
<translation id="3789841737615482174">O‘rnatish</translation>
+<translation id="3910402514791813257">Oʻrnatilmadi.</translation>
<translation id="4250229828105606438">Skrinshot</translation>
<translation id="4665282149850138822"><ph name="NAME" /> sayti bosh ekranga qo‘shildi</translation>
<translation id="5250483651202458397">Skrinshot. Bosib yopish.</translation>
<translation id="6990079615885386641">Google Play do‘konidagi ilova: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Avvalgi sayt qo‘shilmoqda</translation>
<translation id="962979164594783469">Bu ilovani oʻrnatish</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_vi.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_vi.xtb
index 57681feebe2..170d5d2a883 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_vi.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_vi.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Thêm vào Màn hình chính</translation>
<translation id="2478076885740497414">Cài đặt ứng dụng</translation>
<translation id="3789841737615482174">Cài đặt</translation>
+<translation id="3910402514791813257">Không cài đặt được</translation>
<translation id="4250229828105606438">Ảnh chụp màn hình</translation>
<translation id="4665282149850138822">Äã thêm <ph name="NAME" /> vào Màn hình chính của bạn</translation>
<translation id="5250483651202458397">Ảnh chụp màn hình. Nhấn để đóng.</translation>
<translation id="6990079615885386641">Tải ứng dụng từ Cửa hàng Google Play: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Vẫn đang thêm trang web trước</translation>
<translation id="962979164594783469">Cài đặt ứng dụng này</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-CN.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-CN.xtb
index 449ed1ab40f..41ff39ca1dd 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-CN.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-CN.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">添加到主å±å¹•</translation>
<translation id="2478076885740497414">安装应用</translation>
<translation id="3789841737615482174">安装</translation>
+<translation id="3910402514791813257">安装失败</translation>
<translation id="4250229828105606438">å±å¹•æˆªå›¾</translation>
<translation id="4665282149850138822"><ph name="NAME" />已添加到您的主å±å¹•</translation>
<translation id="5250483651202458397">å±å¹•æˆªå›¾ã€‚点按å³å¯å…³é—­ã€‚</translation>
<translation id="6990079615885386641">从 Google Play 商店下载应用:<ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ä»åœ¨æ·»åŠ å…ˆå‰çš„网站</translation>
<translation id="962979164594783469">安装此应用</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-HK.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-HK.xtb
index 21701926cf4..7730d69e6eb 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-HK.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-HK.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">新增至主螢幕</translation>
<translation id="2478076885740497414">安è£æ‡‰ç”¨ç¨‹å¼</translation>
<translation id="3789841737615482174">安è£</translation>
+<translation id="3910402514791813257">無法安è£</translation>
<translation id="4250229828105606438">螢幕截圖</translation>
<translation id="4665282149850138822">已將 <ph name="NAME" /> 新增到主畫é¢</translation>
<translation id="5250483651202458397">螢幕截圖。輕按以關閉。</translation>
<translation id="6990079615885386641">從 Google Play 商店å–得應用程å¼ï¼š<ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ä»åœ¨æ–°å¢žå…ˆå‰çš„網站</translation>
<translation id="962979164594783469">安è£æ­¤æ‡‰ç”¨ç¨‹å¼</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-TW.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-TW.xtb
index e4057ae4cd7..320d0c19965 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-TW.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_zh-TW.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">加到主畫é¢</translation>
<translation id="2478076885740497414">安è£æ‡‰ç”¨ç¨‹å¼</translation>
<translation id="3789841737615482174">安è£</translation>
+<translation id="3910402514791813257">無法安è£</translation>
<translation id="4250229828105606438">螢幕截圖</translation>
<translation id="4665282149850138822">「<ph name="NAME" />ã€å·²åŠ å…¥æ‚¨çš„主畫é¢</translation>
<translation id="5250483651202458397">螢幕截圖。輕觸å³å¯é—œé–‰ã€‚</translation>
<translation id="6990079615885386641">從 Google Play 商店å–得應用程å¼ï¼š<ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">ä»åœ¨æ–°å¢žå…ˆå‰çš„網站</translation>
<translation id="962979164594783469">安è£é€™å€‹æ‡‰ç”¨ç¨‹å¼</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/translations/android_webapps_strings_zu.xtb b/chromium/components/webapps/browser/android/translations/android_webapps_strings_zu.xtb
index 5bf4b4a88d8..e513c2befd0 100644
--- a/chromium/components/webapps/browser/android/translations/android_webapps_strings_zu.xtb
+++ b/chromium/components/webapps/browser/android/translations/android_webapps_strings_zu.xtb
@@ -4,9 +4,11 @@
<translation id="2139186145475833000">Engeza kusikrini sasekhaya</translation>
<translation id="2478076885740497414">Faka uhlelo lokusebenza</translation>
<translation id="3789841737615482174">Faka</translation>
+<translation id="3910402514791813257">Yehlulekile ukufaka</translation>
<translation id="4250229828105606438">Isithombe-skrini</translation>
<translation id="4665282149850138822">I-<ph name="NAME" /> iye yangezwa kusikrini sakho sasekhaya</translation>
<translation id="5250483651202458397">Isithombe-skrini. Thepha ukuze uvale.</translation>
<translation id="6990079615885386641">Thola uhlelo lokusebenza kusukela ku-Google Play Isitolo: <ph name="APP_ACTION" /></translation>
+<translation id="7333031090786104871">Isangeza isayithi langaphambilini</translation>
<translation id="962979164594783469">Faka lolu hlelo lokusebenza</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/webapps/browser/android/webapk/webapk_icon_hasher_unittest.cc b/chromium/components/webapps/browser/android/webapk/webapk_icon_hasher_unittest.cc
index 6764e0f8792..18298bc09c7 100644
--- a/chromium/components/webapps/browser/android/webapk/webapk_icon_hasher_unittest.cc
+++ b/chromium/components/webapps/browser/android/webapk/webapk_icon_hasher_unittest.cc
@@ -237,7 +237,7 @@ TEST_F(WebApkIconHasherTest, SVGImage) {
std::vector<gfx::Size>{gfx::Size(10, 10)}));
base::RunLoop().RunUntilIdle();
- EXPECT_EQ("16586085245996049349", runner.icon().hash);
+ EXPECT_EQ("12895188166704127516", runner.icon().hash);
EXPECT_FALSE(runner.icon().unsafe_data.empty());
}
diff --git a/chromium/components/webapps/browser/android/webapk/webapk_types.h b/chromium/components/webapps/browser/android/webapk/webapk_types.h
index a012f4e8e20..f481b0998ab 100644
--- a/chromium/components/webapps/browser/android/webapk/webapk_types.h
+++ b/chromium/components/webapps/browser/android/webapk/webapk_types.h
@@ -73,6 +73,26 @@ enum class WebApkInstallResult {
NOT_ENOUGH_SPACE = 8,
ICON_HASHER_ERROR = 9,
RESULT_MAX = 10,
+
+ // Indicates that the WebAPK is currently already being installed and the new
+ // install will be aborted. Used when the install was initiated through the
+ // WebApkInstallCoordinator-service to propagate the status to the connecting
+ // client.
+ INSTALL_ALREADY_IN_PROGRESS = 11,
+};
+
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.webapps
+//
+// Lists the fields containing information about the app, which are shown on
+// the default offline experience page.
+enum class WebApkDetailsForDefaultOfflinePage {
+ SHORT_NAME = 0,
+ ICON,
+ BACKGROUND_COLOR,
+ BACKGROUND_COLOR_DARK_MODE,
+ THEME_COLOR,
+ THEME_COLOR_DARK_MODE,
};
} // namespace webapps
diff --git a/chromium/components/webapps/browser/android/webapps_utils.cc b/chromium/components/webapps/browser/android/webapps_utils.cc
index e548e21847b..43951bfddc8 100644
--- a/chromium/components/webapps/browser/android/webapps_utils.cc
+++ b/chromium/components/webapps/browser/android/webapps_utils.cc
@@ -8,6 +8,7 @@
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
+#include "components/webapps/browser/android/webapk/webapk_types.h"
#include "components/webapps/browser/android/webapps_jni_headers/WebappsUtils_jni.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
#include "url/gurl.h"
@@ -57,4 +58,11 @@ bool WebappsUtils::AreWebManifestUrlsWebApkCompatible(
IsUrlWebApkCompatible(manifest.scope);
}
+// static
+void WebappsUtils::ShowWebApkInstallResultToast(
+ webapps::WebApkInstallResult result) {
+ Java_WebappsUtils_showWebApkInstallResultToast(
+ base::android::AttachCurrentThread(), (int)result);
+}
+
} // namespace webapps
diff --git a/chromium/components/webapps/browser/android/webapps_utils.h b/chromium/components/webapps/browser/android/webapps_utils.h
index ab962918bfb..400ee181624 100644
--- a/chromium/components/webapps/browser/android/webapps_utils.h
+++ b/chromium/components/webapps/browser/android/webapps_utils.h
@@ -15,6 +15,8 @@ class BrowserContext;
namespace webapps {
+enum class WebApkInstallResult;
+
class WebappsUtils {
public:
WebappsUtils() = delete;
@@ -29,6 +31,10 @@ class WebappsUtils {
// compatible.
static bool AreWebManifestUrlsWebApkCompatible(
const blink::mojom::Manifest& manifest);
+
+ // Shows toast notifying user of the result of a WebAPK install if the
+ // installation was not successful.
+ static void ShowWebApkInstallResultToast(webapps::WebApkInstallResult result);
};
} // namespace webapps
diff --git a/chromium/components/webapps/browser/banners/app_banner_manager.cc b/chromium/components/webapps/browser/banners/app_banner_manager.cc
index bdf41e4050a..32474783988 100644
--- a/chromium/components/webapps/browser/banners/app_banner_manager.cc
+++ b/chromium/components/webapps/browser/banners/app_banner_manager.cc
@@ -22,6 +22,7 @@
#include "components/site_engagement/content/site_engagement_service.h"
#include "components/webapps/browser/banners/app_banner_metrics.h"
#include "components/webapps/browser/banners/app_banner_settings_helper.h"
+#include "components/webapps/browser/features.h"
#include "components/webapps/browser/installable/installable_data.h"
#include "components/webapps/browser/installable/installable_manager.h"
#include "components/webapps/browser/installable/installable_metrics.h"
@@ -180,7 +181,7 @@ void AppBannerManager::RequestAppBanner(const GURL& validated_url) {
void AppBannerManager::OnInstall(blink::mojom::DisplayMode display) {
TrackInstallDisplayMode(display);
mojo::Remote<blink::mojom::InstallationService> installation_service;
- web_contents()->GetMainFrame()->GetRemoteInterfaces()->GetInterface(
+ web_contents()->GetPrimaryMainFrame()->GetRemoteInterfaces()->GetInterface(
installation_service.BindNewPipeAndPassReceiver());
DCHECK(installation_service);
installation_service->OnInstall();
@@ -364,8 +365,8 @@ InstallableParams AppBannerManager::ParamsToPerformInstallableWebAppCheck() {
InstallableParams params;
params.valid_primary_icon = true;
params.valid_manifest = true;
- params.has_worker = true;
- params.wait_for_worker = true;
+ params.has_worker = !features::SkipBannerServiceWorkerCheck();
+ params.wait_for_worker = !features::SkipBannerServiceWorkerCheck();
return params;
}
@@ -392,10 +393,23 @@ void AppBannerManager::OnDidPerformInstallableWebAppCheck(
return;
UpdateState(State::ACTIVE);
- if (data.has_worker && data.valid_manifest)
+ if (data.worker_check_passed && data.valid_manifest)
TrackDisplayEvent(DISPLAY_EVENT_WEB_APP_BANNER_REQUESTED);
auto error = data.NoBlockingErrors() ? NO_ERROR_DETECTED : data.errors[0];
+
+ // When |features::SkipInstallServiceWorkerCheck| is true, a service worker is
+ // still required to display the banner prompt. This would mean that while a
+ // banner may not appear, the site is still consider installabled if it only
+ // failed service worker checks.
+ bool worker_errors_ignored_for_installs = false;
+ if (features::SkipInstallServiceWorkerCheck() &&
+ data.HasErrorOnlyServiceWorkerErrors()) {
+ DCHECK(error != NO_ERROR_DETECTED);
+ worker_errors_ignored_for_installs = true;
+ error = NO_ERROR_DETECTED;
+ }
+
if (error != NO_ERROR_DETECTED) {
if (error == NO_MATCHING_SERVICE_WORKER)
TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER);
@@ -420,10 +434,19 @@ void AppBannerManager::OnDidPerformInstallableWebAppCheck(
return;
}
+ if (worker_errors_ignored_for_installs) {
+ DCHECK(data.HasErrorOnlyServiceWorkerErrors());
+
+ SetInstallableWebAppCheckResult(
+ InstallableWebAppCheckResult::kYes_ByUserRequest);
+ Stop(SERVICE_WORKER_NOT_REQUIRED);
+ return;
+ }
+
SetInstallableWebAppCheckResult(
InstallableWebAppCheckResult::kYes_Promotable);
- DCHECK(data.has_worker && data.valid_manifest);
+ DCHECK(data.worker_check_passed && data.valid_manifest);
DCHECK(!data.primary_icon_url.is_empty());
DCHECK(data.primary_icon);
@@ -590,7 +613,7 @@ void AppBannerManager::SendBannerPromptRequest() {
ResetBindings();
mojo::Remote<blink::mojom::AppBannerController> controller;
- web_contents()->GetMainFrame()->GetRemoteInterfaces()->GetInterface(
+ web_contents()->GetPrimaryMainFrame()->GetRemoteInterfaces()->GetInterface(
controller.BindNewPipeAndPassReceiver());
// Get a raw controller pointer before we move out of the smart pointer to
@@ -630,15 +653,15 @@ void AppBannerManager::DidFinishNavigation(content::NavigationHandle* handle) {
}
}
+ if (state_ != State::COMPLETE && state_ != State::INACTIVE)
+ Terminate();
+ ResetCurrentPageData();
+
if (base::FeatureList::IsEnabled(
blink::features::kBackForwardCacheAppBanner) &&
handle->IsServedFromBackForwardCache()) {
UpdateState(State::INACTIVE);
RequestAppBanner(validated_url_);
- } else {
- if (state_ != State::COMPLETE && state_ != State::INACTIVE)
- Terminate();
- ResetCurrentPageData();
}
}
@@ -672,7 +695,7 @@ void AppBannerManager::DidActivatePortal(
// instantiated after DidFinishLoad. Trigger the banner pipeline now (on
// portal activation) if we missed the load event.
if (!load_finished_ && !web_contents()->ShouldShowLoadingUI()) {
- DidFinishLoad(web_contents()->GetMainFrame(),
+ DidFinishLoad(web_contents()->GetPrimaryMainFrame(),
web_contents()->GetLastCommittedURL());
}
}
@@ -872,7 +895,7 @@ void AppBannerManager::OnBannerPromptReply(
if (event_canceled) {
TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_PREVENT_DEFAULT_CALLED);
if (ShouldBypassEngagementChecks()) {
- web_contents()->GetMainFrame()->AddMessageToConsole(
+ web_contents()->GetPrimaryMainFrame()->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kInfo,
"Banner not shown: beforeinstallpromptevent.preventDefault() called. "
"The page must call beforeinstallpromptevent.prompt() to show the "
diff --git a/chromium/components/webapps/browser/features.cc b/chromium/components/webapps/browser/features.cc
new file mode 100644
index 00000000000..2a0e31ef7b0
--- /dev/null
+++ b/chromium/components/webapps/browser/features.cc
@@ -0,0 +1,45 @@
+// 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/webapps/browser/features.h"
+
+#include "base/feature_list.h"
+
+namespace webapps {
+namespace features {
+
+#if BUILDFLAG(IS_ANDROID)
+const base::Feature kAddToHomescreenMessaging{
+ "AddToHomescreenMessaging", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables or disables the installable ambient badge infobar.
+const base::Feature kInstallableAmbientBadgeInfoBar{
+ "InstallableAmbientBadgeInfoBar", base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Enables or disables the installable ambient badge message.
+const base::Feature kInstallableAmbientBadgeMessage{
+ "InstallableAmbientBadgeMessage", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif // BUILDFLAG(IS_ANDROID)
+
+// Skip the service worker in all install criteria check. This affect both
+// "intallable" and "promotable" status of a web app.
+const base::Feature kSkipServiceWorkerCheckAll{
+ "SkipServiceWorkerCheckAll", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Skip the service worker install criteria check for installing. This affect
+// only the "installable" status but not "promotable".
+const base::Feature kSkipServiceWorkerCheckInstallOnly{
+ "SkipServiceWorkerCheckInstallOnly", base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool SkipBannerServiceWorkerCheck() {
+ return base::FeatureList::IsEnabled(kSkipServiceWorkerCheckAll);
+}
+
+bool SkipInstallServiceWorkerCheck() {
+ return base::FeatureList::IsEnabled(kSkipServiceWorkerCheckAll) ||
+ base::FeatureList::IsEnabled(kSkipServiceWorkerCheckInstallOnly);
+}
+
+} // namespace features
+} // namespace webapps
diff --git a/chromium/components/webapps/browser/android/features.h b/chromium/components/webapps/browser/features.h
index 8973d17200d..cca853d213f 100644
--- a/chromium/components/webapps/browser/android/features.h
+++ b/chromium/components/webapps/browser/features.h
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_WEBAPPS_BROWSER_ANDROID_FEATURES_H_
-#define COMPONENTS_WEBAPPS_BROWSER_ANDROID_FEATURES_H_
+#ifndef COMPONENTS_WEBAPPS_BROWSER_FEATURES_H_
+#define COMPONENTS_WEBAPPS_BROWSER_FEATURES_H_
+
+#include "build/build_config.h"
namespace base {
struct Feature;
@@ -12,11 +14,19 @@ struct Feature;
namespace webapps {
namespace features {
+#if BUILDFLAG(IS_ANDROID)
extern const base::Feature kAddToHomescreenMessaging;
extern const base::Feature kInstallableAmbientBadgeInfoBar;
extern const base::Feature kInstallableAmbientBadgeMessage;
+#endif // BUILDFLAG(IS_ANDROID)
+
+extern const base::Feature kSkipServiceWorkerCheckAll;
+extern const base::Feature kSkipServiceWorkerCheckInstallOnly;
+
+bool SkipBannerServiceWorkerCheck();
+bool SkipInstallServiceWorkerCheck();
} // namespace features
} // namespace webapps
-#endif // COMPONENTS_WEBAPPS_BROWSER_ANDROID_FEATURES_H_
+#endif // COMPONENTS_WEBAPPS_BROWSER_FEATURES_H_
diff --git a/chromium/components/webapps/browser/install_result_code.cc b/chromium/components/webapps/browser/install_result_code.cc
index 57f08e4bc49..f9f46565ef2 100644
--- a/chromium/components/webapps/browser/install_result_code.cc
+++ b/chromium/components/webapps/browser/install_result_code.cc
@@ -77,6 +77,8 @@ std::ostream& operator<<(std::ostream& os, InstallResultCode code) {
return os << "kUpdateTaskFailed";
case InstallResultCode::kAppNotInRegistrarAfterCommit:
return os << "kAppNotInRegistrarAfterCommit";
+ case InstallResultCode::kHaltedBySyncUninstall:
+ return os << "kHaltedBySyncUninstall";
}
}
diff --git a/chromium/components/webapps/browser/install_result_code.h b/chromium/components/webapps/browser/install_result_code.h
index b721d7dffbd..f4cdc977969 100644
--- a/chromium/components/webapps/browser/install_result_code.h
+++ b/chromium/components/webapps/browser/install_result_code.h
@@ -80,7 +80,10 @@ enum class InstallResultCode {
// commit.
kAppNotInRegistrarAfterCommit = 27,
- kMaxValue = kAppNotInRegistrarAfterCommit,
+ // The installation stopped due to an uninstall from sync being scheduled.
+ kHaltedBySyncUninstall = 28,
+
+ kMaxValue = kHaltedBySyncUninstall,
};
// Checks if InstallResultCode is not a failure.
diff --git a/chromium/components/webapps/browser/installable/installable_data.cc b/chromium/components/webapps/browser/installable/installable_data.cc
index f29affbaa37..55a353e55fc 100644
--- a/chromium/components/webapps/browser/installable/installable_data.cc
+++ b/chromium/components/webapps/browser/installable/installable_data.cc
@@ -5,6 +5,7 @@
#include "components/webapps/browser/installable/installable_data.h"
#include <utility>
+#include "installable_logging.h"
namespace webapps {
@@ -19,7 +20,7 @@ InstallableData::InstallableData(std::vector<InstallableStatusCode> errors,
bool has_maskable_splash_icon,
const std::vector<SkBitmap>& screenshots,
bool valid_manifest,
- bool has_worker)
+ bool worker_check_passed)
: errors(std::move(errors)),
manifest_url(manifest_url),
manifest(manifest),
@@ -31,7 +32,7 @@ InstallableData::InstallableData(std::vector<InstallableStatusCode> errors,
has_maskable_splash_icon(has_maskable_splash_icon),
screenshots(screenshots),
valid_manifest(valid_manifest),
- has_worker(has_worker) {}
+ worker_check_passed(worker_check_passed) {}
InstallableData::~InstallableData() = default;
@@ -40,4 +41,16 @@ bool InstallableData::NoBlockingErrors() const {
(errors.size() == 1 && errors[0] == WARN_NOT_OFFLINE_CAPABLE);
}
+bool InstallableData::HasErrorOnlyServiceWorkerErrors() const {
+ if (errors.empty() || errors[0] == NO_ERROR_DETECTED)
+ return false;
+
+ for (auto error : errors) {
+ if (error != NO_MATCHING_SERVICE_WORKER && error != NOT_OFFLINE_CAPABLE) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace webapps
diff --git a/chromium/components/webapps/browser/installable/installable_data.h b/chromium/components/webapps/browser/installable/installable_data.h
index 7a04b836889..5f7a9f3ee0a 100644
--- a/chromium/components/webapps/browser/installable/installable_data.h
+++ b/chromium/components/webapps/browser/installable/installable_data.h
@@ -33,7 +33,7 @@ struct InstallableData {
bool has_maskable_splash_icon,
const std::vector<SkBitmap>& screenshots,
bool valid_manifest,
- bool has_worker);
+ bool worker_check_passed);
InstallableData(const InstallableData&) = delete;
InstallableData& operator=(const InstallableData&) = delete;
@@ -48,6 +48,10 @@ struct InstallableData {
// M93.
bool NoBlockingErrors() const;
+ // Returns true if there is any |errors| and all errors are service worker
+ // errors, i.e.|NO_MATCHING_SERVICE_WORKER| or |NOT_OFFLINE_CAPABLE|.
+ bool HasErrorOnlyServiceWorkerErrors() const;
+
// Contains all errors encountered during the InstallableManager::GetData
// call. Empty if no errors were encountered.
std::vector<InstallableStatusCode> errors;
@@ -90,12 +94,13 @@ struct InstallableData {
const std::vector<SkBitmap>& screenshots;
// true if the site has a valid, installable web app manifest. If
- // |valid_manifest| or |has_worker| was true and the site isn't installable,
- // the reason will be in |errors|.
+ // |valid_manifest| or |worker_check_passed| was true and the site isn't
+ // installable, the reason will be in |errors|.
const bool valid_manifest = false;
- // true if the site has a service worker with a fetch handler.
- const bool has_worker = false;
+ // true if the site has a service worker with a fetch handler or
+ // the service worker check was not requested when fetching this data.
+ const bool worker_check_passed = false;
};
using InstallableCallback = base::OnceCallback<void(const InstallableData&)>;
diff --git a/chromium/components/webapps/browser/installable/installable_logging.cc b/chromium/components/webapps/browser/installable/installable_logging.cc
index a0b5b251258..168a5a738f2 100644
--- a/chromium/components/webapps/browser/installable/installable_logging.cc
+++ b/chromium/components/webapps/browser/installable/installable_logging.cc
@@ -143,6 +143,7 @@ std::string GetErrorMessage(InstallableStatusCode code) {
case SHOWING_APP_INSTALLATION_DIALOG:
case DATA_TIMED_OUT:
case WEBAPK_INSTALL_FAILED:
+ case SERVICE_WORKER_NOT_REQUIRED:
case MAX_ERROR_CODE:
break;
case NOT_FROM_SECURE_ORIGIN:
@@ -257,6 +258,7 @@ content::InstallabilityError GetInstallabilityError(
case SHOWING_APP_INSTALLATION_DIALOG:
case DATA_TIMED_OUT:
case WEBAPK_INSTALL_FAILED:
+ case SERVICE_WORKER_NOT_REQUIRED:
case MAX_ERROR_CODE:
break;
case NOT_FROM_SECURE_ORIGIN:
@@ -360,7 +362,7 @@ void LogToConsole(content::WebContents* web_contents,
if (message.empty())
return;
- web_contents->GetMainFrame()->AddMessageToConsole(
+ web_contents->GetPrimaryMainFrame()->AddMessageToConsole(
level, GetMessagePrefix() + message);
}
diff --git a/chromium/components/webapps/browser/installable/installable_logging.h b/chromium/components/webapps/browser/installable/installable_logging.h
index 838d191d11d..0e1f5d8edc1 100644
--- a/chromium/components/webapps/browser/installable/installable_logging.h
+++ b/chromium/components/webapps/browser/installable/installable_logging.h
@@ -67,6 +67,7 @@ enum InstallableStatusCode {
DATA_TIMED_OUT = 42,
WEBAPK_INSTALL_FAILED = 43,
MANIFEST_URL_SCHEME_NOT_SUPPORTED_FOR_WEBAPK = 44,
+ SERVICE_WORKER_NOT_REQUIRED = 45,
MAX_ERROR_CODE,
};
diff --git a/chromium/components/webapps/browser/installable/installable_manager.cc b/chromium/components/webapps/browser/installable/installable_manager.cc
index 2b196fb3400..2beda76a99b 100644
--- a/chromium/components/webapps/browser/installable/installable_manager.cc
+++ b/chromium/components/webapps/browser/installable/installable_manager.cc
@@ -465,6 +465,7 @@ bool InstallableManager::IsComplete(const InstallableParams& params) const {
manifest_->fetched &&
(!params.valid_manifest || valid_manifest_->fetched) &&
(!params.has_worker || worker_->fetched) &&
+ (!params.fetch_screenshots || is_screenshots_fetch_complete_) &&
(!params.valid_primary_icon ||
IsIconFetchComplete(IconUsage::kPrimary)) &&
(!params.valid_splash_icon || IsIconFetchComplete(IconUsage::kSplash));
@@ -499,6 +500,7 @@ void InstallableManager::SetManifestDependentTasksComplete() {
worker_->fetched = true;
SetIconFetched(IconUsage::kPrimary);
SetIconFetched(IconUsage::kSplash);
+ is_screenshots_fetch_complete_ = true;
}
void InstallableManager::CleanupAndStartNextTask() {
@@ -540,6 +542,8 @@ void InstallableManager::RunCallback(
has_maskable_splash_icon = (splash_icon->purpose == IconPurpose::MASKABLE);
}
+ bool worker_check_passed = worker_->has_worker || !params.has_worker;
+
InstallableData data = {
std::move(errors),
manifest_url(),
@@ -552,7 +556,7 @@ void InstallableManager::RunCallback(
has_maskable_splash_icon,
screenshots_,
valid_manifest_->is_valid,
- worker_->has_worker,
+ worker_check_passed,
};
std::move(task.callback).Run(data);
@@ -595,7 +599,8 @@ void InstallableManager::WorkOnTask() {
CheckAndFetchBestIcon(GetIdealPrimaryIconSizeInPx(),
GetMinimumPrimaryIconSizeInPx(), IconPurpose::ANY,
IconUsage::kPrimary);
- } else if (params.fetch_screenshots && !is_screenshots_fetch_complete_) {
+ } else if (params.fetch_screenshots && !screenshots_downloading_ &&
+ !is_screenshots_fetch_complete_) {
CheckAndFetchScreenshots();
} else if (params.has_worker && !worker_->fetched) {
CheckServiceWorker();
@@ -946,7 +951,8 @@ void InstallableManager::OnScreenshotFetched(GURL screenshot_url,
continue;
}
- auto dimensions = std::minmax(screenshot.width(), screenshot.height());
+ std::pair<int, int> dimensions =
+ std::minmax(screenshot.width(), screenshot.height());
if (dimensions.second > dimensions.first * kMaximumScreenshotRatio)
continue;
@@ -985,12 +991,8 @@ void InstallableManager::OnDestruct(content::ServiceWorkerContext* context) {
service_worker_context_ = nullptr;
}
-void InstallableManager::DidFinishNavigation(
- content::NavigationHandle* handle) {
- if (handle->IsInPrimaryMainFrame() && handle->HasCommitted() &&
- !handle->IsSameDocument()) {
- Reset(USER_NAVIGATED);
- }
+void InstallableManager::PrimaryPageChanged(content::Page& page) {
+ Reset(USER_NAVIGATED);
}
void InstallableManager::DidUpdateWebManifestURL(content::RenderFrameHost* rfh,
diff --git a/chromium/components/webapps/browser/installable/installable_manager.h b/chromium/components/webapps/browser/installable/installable_manager.h
index bcc7bd232dc..8788b8532d5 100644
--- a/chromium/components/webapps/browser/installable/installable_manager.h
+++ b/chromium/components/webapps/browser/installable/installable_manager.h
@@ -249,7 +249,7 @@ class InstallableManager
void OnDestruct(content::ServiceWorkerContext* context) override;
// content::WebContentsObserver overrides
- void DidFinishNavigation(content::NavigationHandle* handle) override;
+ void PrimaryPageChanged(content::Page& page) override;
void DidUpdateWebManifestURL(content::RenderFrameHost* rfh,
const GURL& manifest_url) override;
void WebContentsDestroyed() override;
diff --git a/chromium/components/webapps/browser/installable/installable_metrics.cc b/chromium/components/webapps/browser/installable/installable_metrics.cc
index fe3c0ac7028..298b6239c28 100644
--- a/chromium/components/webapps/browser/installable/installable_metrics.cc
+++ b/chromium/components/webapps/browser/installable/installable_metrics.cc
@@ -27,6 +27,7 @@ bool InstallableMetrics::IsReportableInstallSource(WebappInstallSource source) {
switch (source) {
case WebappInstallSource::AMBIENT_BADGE_BROWSER_TAB:
case WebappInstallSource::AMBIENT_BADGE_CUSTOM_TAB:
+ case WebappInstallSource::RICH_INSTALL_UI_WEBLAYER:
case WebappInstallSource::API_BROWSER_TAB:
case WebappInstallSource::API_CUSTOM_TAB:
case WebappInstallSource::ARC:
@@ -65,6 +66,7 @@ bool InstallableMetrics::IsUserInitiatedInstallSource(
case WebappInstallSource::API_CUSTOM_TAB:
case WebappInstallSource::AMBIENT_BADGE_BROWSER_TAB:
case WebappInstallSource::AMBIENT_BADGE_CUSTOM_TAB:
+ case WebappInstallSource::RICH_INSTALL_UI_WEBLAYER:
case WebappInstallSource::ARC:
case WebappInstallSource::CHROME_SERVICE:
case WebappInstallSource::OMNIBOX_INSTALL_ICON:
@@ -135,4 +137,8 @@ void InstallableMetrics::TrackUninstallEvent(WebappUninstallSource source) {
base::UmaHistogramEnumeration("Webapp.Install.UninstallEvent", source);
}
+// static
+void InstallableMetrics::TrackInstallResult(bool result) {
+ base::UmaHistogramBoolean("WebApp.Install.Result", result);
+}
} // namespace webapps
diff --git a/chromium/components/webapps/browser/installable/installable_metrics.h b/chromium/components/webapps/browser/installable/installable_metrics.h
index 653e5c5c465..b9d952f4bcc 100644
--- a/chromium/components/webapps/browser/installable/installable_metrics.h
+++ b/chromium/components/webapps/browser/installable/installable_metrics.h
@@ -102,6 +102,9 @@ enum class WebappInstallSource {
// Chrome Android service for installing WebAPKs from another app.
CHROME_SERVICE = 19,
+ // PWA rich install bottom sheet in WebLayer.
+ RICH_INSTALL_UI_WEBLAYER = 20,
+
// Add any new values above this one.
COUNT,
};
@@ -221,6 +224,9 @@ class InstallableMetrics {
// Records |source| in the Webapp.Install.UninstallEvent histogram.
static void TrackUninstallEvent(WebappUninstallSource source);
+
+ // Records the result for WebApp.Install.Result histogram.
+ static void TrackInstallResult(bool result);
};
} // namespace webapps
diff --git a/chromium/components/webapps/browser/pwa_install_path_tracker.cc b/chromium/components/webapps/browser/pwa_install_path_tracker.cc
index ef25089a406..3f6440e9169 100644
--- a/chromium/components/webapps/browser/pwa_install_path_tracker.cc
+++ b/chromium/components/webapps/browser/pwa_install_path_tracker.cc
@@ -52,6 +52,7 @@ PwaInstallPathTracker::GetInstallPathMetric() {
: InstallPathMetric::kApiInitiatedBottomSheet;
case WebappInstallSource::AMBIENT_BADGE_BROWSER_TAB:
case WebappInstallSource::AMBIENT_BADGE_CUSTOM_TAB:
+ case WebappInstallSource::RICH_INSTALL_UI_WEBLAYER:
return iph_was_shown_ ? InstallPathMetric::kAmbientBottomSheetWithIph
: InstallPathMetric::kAmbientBottomSheet;
default:
@@ -70,6 +71,7 @@ PwaInstallPathTracker::GetInstallPathMetric() {
: InstallPathMetric::kApiInitiatedInstall;
case WebappInstallSource::AMBIENT_BADGE_BROWSER_TAB:
case WebappInstallSource::AMBIENT_BADGE_CUSTOM_TAB:
+ case WebappInstallSource::RICH_INSTALL_UI_WEBLAYER:
return iph_was_shown_ ? InstallPathMetric::kAmbientInfobarWithIph
: InstallPathMetric::kAmbientInfobar;
default:
diff --git a/chromium/components/webapps/common/android/BUILD.gn b/chromium/components/webapps/common/android/BUILD.gn
index 82800387be9..3108fb47cdb 100644
--- a/chromium/components/webapps/common/android/BUILD.gn
+++ b/chromium/components/webapps/common/android/BUILD.gn
@@ -13,7 +13,5 @@ android_aidl("webapk_install_aidl") {
}
android_library("webapk_install_java") {
- deps = [ "//base:jni_java" ]
srcjar_deps = [ ":webapk_install_aidl" ]
- annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
}
diff --git a/chromium/components/webapps/renderer/web_page_metadata_extraction.cc b/chromium/components/webapps/renderer/web_page_metadata_extraction.cc
index b07bdc18bfe..c20c62b4db1 100644
--- a/chromium/components/webapps/renderer/web_page_metadata_extraction.cc
+++ b/chromium/components/webapps/renderer/web_page_metadata_extraction.cc
@@ -81,10 +81,11 @@ mojom::WebPageMetadataPtr ExtractWebPageMetadata(WebLocalFrame* frame) {
//
// Bookmark apps also support "apple-touch-icon" and
// "apple-touch-icon-precomposed".
- if (base::LowerCaseEqualsASCII(rel, "icon") ||
- base::LowerCaseEqualsASCII(rel, "shortcut icon") ||
- base::LowerCaseEqualsASCII(rel, "apple-touch-icon") ||
- base::LowerCaseEqualsASCII(rel, "apple-touch-icon-precomposed")) {
+ if (base::EqualsCaseInsensitiveASCII(rel, "icon") ||
+ base::EqualsCaseInsensitiveASCII(rel, "shortcut icon") ||
+ base::EqualsCaseInsensitiveASCII(rel, "apple-touch-icon") ||
+ base::EqualsCaseInsensitiveASCII(rel,
+ "apple-touch-icon-precomposed")) {
AddInstallIcon(elem, &metadata->icons);
}
} else if (elem.HasHTMLTagName("meta") && elem.HasAttribute("name")) {
@@ -101,10 +102,10 @@ mojom::WebPageMetadataPtr ExtractWebPageMetadata(WebLocalFrame* frame) {
if (!metadata->application_url.is_valid())
metadata->application_url = GURL();
} else if (name == "mobile-web-app-capable" &&
- base::LowerCaseEqualsASCII(content.Utf16(), "yes")) {
+ base::EqualsCaseInsensitiveASCII(content.Utf16(), "yes")) {
metadata->mobile_capable = mojom::WebPageMobileCapable::ENABLED;
} else if (name == "apple-mobile-web-app-capable" &&
- base::LowerCaseEqualsASCII(content.Utf16(), "yes") &&
+ base::EqualsCaseInsensitiveASCII(content.Utf16(), "yes") &&
metadata->mobile_capable ==
mojom::WebPageMobileCapable::UNSPECIFIED) {
metadata->mobile_capable = mojom::WebPageMobileCapable::ENABLED_APPLE;
diff --git a/chromium/components/webauthn/android/BUILD.gn b/chromium/components/webauthn/android/BUILD.gn
index 647842c55b2..d3140751213 100644
--- a/chromium/components/webauthn/android/BUILD.gn
+++ b/chromium/components/webauthn/android/BUILD.gn
@@ -8,6 +8,7 @@ generate_jni("jni_headers") {
sources = [
"java/src/org/chromium/components/webauthn/Fido2Api.java",
"java/src/org/chromium/components/webauthn/InternalAuthenticator.java",
+ "java/src/org/chromium/components/webauthn/WebAuthnBrowserBridge.java",
]
}
@@ -17,14 +18,15 @@ android_library("java") {
"java/src/org/chromium/components/webauthn/AuthenticatorImpl.java",
"java/src/org/chromium/components/webauthn/Fido2Api.java",
"java/src/org/chromium/components/webauthn/Fido2ApiCall.java",
+ "java/src/org/chromium/components/webauthn/Fido2ApiCallHelper.java",
"java/src/org/chromium/components/webauthn/Fido2ApiHandler.java",
"java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java",
"java/src/org/chromium/components/webauthn/FidoErrorResponseCallback.java",
"java/src/org/chromium/components/webauthn/GetAssertionResponseCallback.java",
"java/src/org/chromium/components/webauthn/InternalAuthenticator.java",
"java/src/org/chromium/components/webauthn/IsUvpaaResponseCallback.java",
- "java/src/org/chromium/components/webauthn/ListCredentials.java",
"java/src/org/chromium/components/webauthn/MakeCredentialResponseCallback.java",
+ "java/src/org/chromium/components/webauthn/WebAuthnBrowserBridge.java",
"java/src/org/chromium/components/webauthn/WebAuthnCredentialDetails.java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
@@ -34,6 +36,7 @@ android_library("java") {
"$google_play_services_package:google_play_services_tasks_java",
"//base:base_java",
"//base:jni_java",
+ "//build/android:build_java",
"//components/externalauth/android:java",
"//components/payments/content/android:feature_list_java",
"//content/public/android:content_java",
@@ -68,6 +71,10 @@ source_set("android") {
"fido2api_native_android.cc",
"internal_authenticator_android.cc",
"internal_authenticator_android.h",
+ "webauthn_browser_bridge.cc",
+ "webauthn_browser_bridge.h",
+ "webauthn_client_android.cc",
+ "webauthn_client_android.h",
]
deps = [
":jni_headers",
diff --git a/chromium/components/webauthn/android/webauthn_browser_bridge.cc b/chromium/components/webauthn/android/webauthn_browser_bridge.cc
new file mode 100644
index 00000000000..1963c9244c8
--- /dev/null
+++ b/chromium/components/webauthn/android/webauthn_browser_bridge.cc
@@ -0,0 +1,110 @@
+// Copyright 2022 The Chromium 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/webauthn/android/webauthn_browser_bridge.h"
+
+#include <jni.h>
+
+#include "base/android/callback_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "components/webauthn/android/jni_headers/WebAuthnBrowserBridge_jni.h"
+#include "components/webauthn/android/webauthn_client_android.h"
+#include "content/public/browser/render_frame_host.h"
+#include "device/fido/discoverable_credential_metadata.h"
+#include "device/fido/public_key_credential_user_entity.h"
+
+using base::android::ScopedJavaLocalRef;
+
+device::DiscoverableCredentialMetadata ConvertJavaCredentialDetailsToMetadata(
+ JNIEnv* env,
+ ScopedJavaLocalRef<jobject> j_credential) {
+ device::DiscoverableCredentialMetadata credential;
+ base::android::JavaByteArrayToByteVector(
+ env,
+ Java_WebAuthnBrowserBridge_getWebAuthnCredentialDetailsCredentialId(
+ env, j_credential),
+ &credential.cred_id);
+ base::android::JavaByteArrayToByteVector(
+ env,
+ Java_WebAuthnBrowserBridge_getWebAuthnCredentialDetailsUserId(
+ env, j_credential),
+ &credential.user.id);
+ credential.user.name = ConvertJavaStringToUTF8(
+ env, Java_WebAuthnBrowserBridge_getWebAuthnCredentialDetailsUserName(
+ env, j_credential));
+ credential.user.display_name = ConvertJavaStringToUTF8(
+ env,
+ Java_WebAuthnBrowserBridge_getWebAuthnCredentialDetailsUserDisplayName(
+ env, j_credential));
+ return credential;
+}
+
+void ConvertJavaCredentialArrayToMetadataVector(
+ JNIEnv* env,
+ const base::android::JavaRef<jobjectArray>& array,
+ std::vector<device::DiscoverableCredentialMetadata>* out) {
+ jsize jlength = env->GetArrayLength(array.obj());
+ // GetArrayLength() returns -1 if |array| is not a valid Java array.
+ DCHECK_GE(jlength, 0) << "Invalid array length: " << jlength;
+ size_t length = static_cast<size_t>(std::max(0, jlength));
+ for (size_t i = 0; i < length; ++i) {
+ ScopedJavaLocalRef<jobject> j_credential(
+ env, static_cast<jobject>(env->GetObjectArrayElement(array.obj(), i)));
+ out->emplace_back(
+ ConvertJavaCredentialDetailsToMetadata(env, j_credential));
+ }
+}
+
+void OnWebAuthnCredentialSelected(
+ const base::android::JavaRef<jobject>& jcallback,
+ const std::vector<uint8_t>& credential_id) {
+ base::android::RunObjectCallbackAndroid(
+ jcallback, base::android::ToJavaByteArray(
+ base::android::AttachCurrentThread(), credential_id.data(),
+ credential_id.size()));
+}
+
+static jlong JNI_WebAuthnBrowserBridge_CreateNativeWebAuthnBrowserBridge(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jbridge) {
+ return reinterpret_cast<jlong>(new WebAuthnBrowserBridge(env, jbridge));
+}
+
+WebAuthnBrowserBridge::WebAuthnBrowserBridge(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jbridge)
+ : owner_(env, jbridge) {}
+
+WebAuthnBrowserBridge::~WebAuthnBrowserBridge() = default;
+
+void WebAuthnBrowserBridge::OnCredentialsDetailsListReceived(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>&,
+ const base::android::JavaParamRef<jobjectArray>& credentials,
+ const base::android::JavaParamRef<jobject>& jframe_host,
+ const base::android::JavaParamRef<jobject>& jcallback) const {
+ auto* client = components::WebAuthnClientAndroid::GetClient();
+ auto* render_frame_host =
+ content::RenderFrameHost::FromJavaRenderFrameHost(jframe_host);
+ // A null client indicates the embedder does not support Conditional UI.
+ if (!client) {
+ std::vector<uint8_t> credential_id = {};
+ base::android::RunObjectCallbackAndroid(
+ jcallback, base::android::ToJavaByteArray(
+ base::android::AttachCurrentThread(),
+ credential_id.data(), credential_id.size()));
+ return;
+ }
+ DCHECK(render_frame_host);
+ std::vector<device::DiscoverableCredentialMetadata> credentials_metadata;
+ ConvertJavaCredentialArrayToMetadataVector(env, credentials,
+ &credentials_metadata);
+ client->OnWebAuthnRequestPending(
+ render_frame_host, credentials_metadata,
+ base::BindOnce(
+ &OnWebAuthnCredentialSelected,
+ base::android::ScopedJavaGlobalRef<jobject>(env, jcallback)));
+}
diff --git a/chromium/components/webauthn/android/webauthn_browser_bridge.h b/chromium/components/webauthn/android/webauthn_browser_bridge.h
new file mode 100644
index 00000000000..c284f923af7
--- /dev/null
+++ b/chromium/components/webauthn/android/webauthn_browser_bridge.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_WEBAUTHN_ANDROID_WEBAUTHN_BROWSER_BRIDGE_H_
+#define COMPONENTS_WEBAUTHN_ANDROID_WEBAUTHN_BROWSER_BRIDGE_H_
+
+#include "base/android/scoped_java_ref.h"
+
+class WebAuthnBrowserBridge {
+ public:
+ WebAuthnBrowserBridge(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& jbridge);
+
+ WebAuthnBrowserBridge(const WebAuthnBrowserBridge&) = delete;
+ WebAuthnBrowserBridge& operator=(const WebAuthnBrowserBridge&) = delete;
+
+ ~WebAuthnBrowserBridge();
+
+ void OnCredentialsDetailsListReceived(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>&,
+ const base::android::JavaParamRef<jobjectArray>& credentials,
+ const base::android::JavaParamRef<jobject>& jframe_host,
+ const base::android::JavaParamRef<jobject>& jcallback) const;
+
+ private:
+ // Java object that owns this WebAuthnBrowserBridge.
+ base::android::ScopedJavaGlobalRef<jobject> owner_;
+};
+
+#endif // COMPONENTS_WEBAUTHN_ANDROID_WEBAUTHN_BROWSER_BRIDGE_H_
diff --git a/chromium/components/webauthn/android/webauthn_client_android.cc b/chromium/components/webauthn/android/webauthn_client_android.cc
new file mode 100644
index 00000000000..13b3ff8c6ea
--- /dev/null
+++ b/chromium/components/webauthn/android/webauthn_client_android.cc
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium 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/webauthn/android/webauthn_client_android.h"
+
+#include <memory>
+
+#include "base/check.h"
+
+namespace components {
+
+// The WebAuthnClientAndroid instance, which is set by the embedder.
+WebAuthnClientAndroid* g_webauthn_client = nullptr;
+
+WebAuthnClientAndroid::~WebAuthnClientAndroid() = default;
+
+// static
+void WebAuthnClientAndroid::SetClient(
+ std::unique_ptr<WebAuthnClientAndroid> client) {
+ DCHECK(client);
+ DCHECK(!g_webauthn_client);
+ g_webauthn_client = client.release();
+}
+
+// static
+WebAuthnClientAndroid* WebAuthnClientAndroid::GetClient() {
+ DCHECK(g_webauthn_client);
+ return g_webauthn_client;
+}
+
+} // namespace components
diff --git a/chromium/components/webauthn/android/webauthn_client_android.h b/chromium/components/webauthn/android/webauthn_client_android.h
new file mode 100644
index 00000000000..a16420cfeac
--- /dev/null
+++ b/chromium/components/webauthn/android/webauthn_client_android.h
@@ -0,0 +1,43 @@
+// Copyright 2022 The Chromium 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 COMPONENTS_WEBAUTHN_ANDROID_WEBAUTHN_CLIENT_ANDROID_H_
+#define COMPONENTS_WEBAUTHN_ANDROID_WEBAUTHN_CLIENT_ANDROID_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+
+namespace content {
+class RenderFrameHost;
+}
+
+namespace device {
+class DiscoverableCredentialMetadata;
+}
+
+namespace components {
+
+class WebAuthnClientAndroid {
+ public:
+ virtual ~WebAuthnClientAndroid();
+
+ // Called by the embedder to set the static instance of this client.
+ static void SetClient(std::unique_ptr<WebAuthnClientAndroid> client);
+
+ // Accessor for the client that has been set by the embedder.
+ static WebAuthnClientAndroid* GetClient();
+
+ // Called when a Web Authentication Conditional UI request is received. This
+ // provides the callback that will complete the request if and when a user
+ // selects a credential from a form autofill dialog.
+ virtual void OnWebAuthnRequestPending(
+ content::RenderFrameHost* frame_host,
+ const std::vector<device::DiscoverableCredentialMetadata>& credentials,
+ base::OnceCallback<void(const std::vector<uint8_t>& id)> callback) = 0;
+};
+
+} // namespace components
+
+#endif // COMPONENTS_WEBAUTHN_ANDROID_WEBAUTHN_CLIENT_ANDROID_H_
diff --git a/chromium/components/webcrypto/BUILD.gn b/chromium/components/webcrypto/BUILD.gn
index e3b628803a6..dd646f38021 100644
--- a/chromium/components/webcrypto/BUILD.gn
+++ b/chromium/components/webcrypto/BUILD.gn
@@ -26,7 +26,6 @@ static_library("webcrypto") {
"algorithms/ec.h",
"algorithms/ecdh.cc",
"algorithms/ecdsa.cc",
- "algorithms/ed25519.cc",
"algorithms/hkdf.cc",
"algorithms/hmac.cc",
"algorithms/pbkdf2.cc",
@@ -42,11 +41,8 @@ static_library("webcrypto") {
"algorithms/sha.cc",
"algorithms/util.cc",
"algorithms/util.h",
- "algorithms/x25519.cc",
"blink_key_handle.cc",
"blink_key_handle.h",
- "crypto_data.cc",
- "crypto_data.h",
"generate_key_result.cc",
"generate_key_result.h",
"jwk.cc",
@@ -60,8 +56,8 @@ static_library("webcrypto") {
deps = [
"//base",
"//crypto",
- "//crypto:platform",
"//third_party/blink/public:blink_headers",
+ "//third_party/boringssl",
]
}
@@ -88,10 +84,10 @@ source_set("unit_tests") {
":webcrypto",
"//base/test:test_support",
"//crypto",
- "//crypto:platform",
"//testing/gtest",
"//testing/perf",
"//third_party/blink/public:blink",
+ "//third_party/boringssl",
"//third_party/re2",
]
}
@@ -110,7 +106,6 @@ source_set("fuzzer_support") {
":webcrypto",
"//base",
"//crypto",
- "//crypto:platform",
"//mojo/core/embedder",
"//third_party/blink/public:blink",
]
diff --git a/chromium/components/webcrypto/algorithm_dispatch.cc b/chromium/components/webcrypto/algorithm_dispatch.cc
index 9116fbb0825..b65c57ab87f 100644
--- a/chromium/components/webcrypto/algorithm_dispatch.cc
+++ b/chromium/components/webcrypto/algorithm_dispatch.cc
@@ -7,7 +7,6 @@
#include "components/webcrypto/algorithm_implementation.h"
#include "components/webcrypto/algorithm_implementations.h"
#include "components/webcrypto/algorithm_registry.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
@@ -19,7 +18,7 @@ namespace {
Status DecryptDontCheckKeyUsage(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
if (algorithm.Id() != key.Algorithm().Id())
return Status::ErrorUnexpected();
@@ -34,7 +33,7 @@ Status DecryptDontCheckKeyUsage(const blink::WebCryptoAlgorithm& algorithm,
Status EncryptDontCheckUsage(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
if (algorithm.Id() != key.Algorithm().Id())
return Status::ErrorUnexpected();
@@ -62,7 +61,7 @@ Status ExportKeyDontCheckExtractability(blink::WebCryptoKeyFormat format,
Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
if (!key.KeyUsageAllows(blink::kWebCryptoKeyUsageEncrypt))
return Status::ErrorUnexpected();
@@ -71,7 +70,7 @@ Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
if (!key.KeyUsageAllows(blink::kWebCryptoKeyUsageDecrypt))
return Status::ErrorUnexpected();
@@ -79,7 +78,7 @@ Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
}
Status Digest(const blink::WebCryptoAlgorithm& algorithm,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
const AlgorithmImplementation* impl = nullptr;
Status status = GetAlgorithmImplementation(algorithm.Id(), &impl);
@@ -132,7 +131,7 @@ Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
}
Status ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -173,7 +172,7 @@ Status ExportKey(blink::WebCryptoKeyFormat format,
Status Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
if (!key.KeyUsageAllows(blink::kWebCryptoKeyUsageSign))
return Status::ErrorUnexpected();
@@ -190,8 +189,8 @@ Status Sign(const blink::WebCryptoAlgorithm& algorithm,
Status Verify(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match) {
if (!key.KeyUsageAllows(blink::kWebCryptoKeyUsageVerify))
return Status::ErrorUnexpected();
@@ -218,12 +217,12 @@ Status WrapKey(blink::WebCryptoKeyFormat format,
Status status = ExportKey(format, key_to_wrap, &exported_data);
if (status.IsError())
return status;
- return EncryptDontCheckUsage(wrapping_algorithm, wrapping_key,
- CryptoData(exported_data), buffer);
+ return EncryptDontCheckUsage(wrapping_algorithm, wrapping_key, exported_data,
+ buffer);
}
Status UnwrapKey(blink::WebCryptoKeyFormat format,
- const CryptoData& wrapped_key_data,
+ base::span<const uint8_t> wrapped_key_data,
const blink::WebCryptoKey& wrapping_key,
const blink::WebCryptoAlgorithm& wrapping_algorithm,
const blink::WebCryptoAlgorithm& algorithm,
@@ -246,8 +245,7 @@ Status UnwrapKey(blink::WebCryptoKeyFormat format,
// key_ops). As long as the ImportKey error messages don't describe actual
// key bytes however this should be OK. For more discussion see
// http://crbug.com/372040
- return ImportKey(format, CryptoData(buffer), algorithm, extractable, usages,
- key);
+ return ImportKey(format, buffer, algorithm, extractable, usages, key);
}
Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
@@ -312,7 +310,7 @@ Status DeriveKey(const blink::WebCryptoAlgorithm& algorithm,
return status;
// Create the key using the derived bytes.
- return ImportKey(blink::kWebCryptoKeyFormatRaw, CryptoData(derived_bytes),
+ return ImportKey(blink::kWebCryptoKeyFormatRaw, derived_bytes,
import_algorithm, extractable, usages, derived_key);
}
@@ -331,7 +329,7 @@ bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm,
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) {
const AlgorithmImplementation* impl = nullptr;
Status status = GetAlgorithmImplementation(algorithm.Id(), &impl);
diff --git a/chromium/components/webcrypto/algorithm_dispatch.h b/chromium/components/webcrypto/algorithm_dispatch.h
index fe3d9e5d90c..100e91ba28b 100644
--- a/chromium/components/webcrypto/algorithm_dispatch.h
+++ b/chromium/components/webcrypto/algorithm_dispatch.h
@@ -10,11 +10,11 @@
#include <memory>
#include <vector>
+#include "base/containers/span.h"
#include "third_party/blink/public/platform/web_crypto.h"
namespace webcrypto {
-class CryptoData;
class GenerateKeyResult;
class Status;
@@ -28,16 +28,16 @@ class Status;
Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer);
Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer);
Status Digest(const blink::WebCryptoAlgorithm& algorithm,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer);
Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
@@ -46,7 +46,7 @@ Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
GenerateKeyResult* result);
Status ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -58,13 +58,13 @@ Status ExportKey(blink::WebCryptoKeyFormat format,
Status Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer);
Status Verify(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match);
Status WrapKey(blink::WebCryptoKeyFormat format,
@@ -74,7 +74,7 @@ Status WrapKey(blink::WebCryptoKeyFormat format,
std::vector<uint8_t>* buffer);
Status UnwrapKey(blink::WebCryptoKeyFormat format,
- const CryptoData& wrapped_key_data,
+ base::span<const uint8_t> wrapped_key_data,
const blink::WebCryptoKey& wrapping_key,
const blink::WebCryptoAlgorithm& wrapping_algorithm,
const blink::WebCryptoAlgorithm& algorithm,
@@ -121,7 +121,7 @@ bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm,
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key);
} // namespace webcrypto
diff --git a/chromium/components/webcrypto/algorithm_implementation.cc b/chromium/components/webcrypto/algorithm_implementation.cc
index fe4622a6e91..dc12ac1a621 100644
--- a/chromium/components/webcrypto/algorithm_implementation.cc
+++ b/chromium/components/webcrypto/algorithm_implementation.cc
@@ -4,6 +4,8 @@
#include "components/webcrypto/algorithm_implementation.h"
+#include "base/notreached.h"
+#include "components/webcrypto/algorithms/asymmetric_key_util.h"
#include "components/webcrypto/blink_key_handle.h"
#include "components/webcrypto/status.h"
@@ -15,7 +17,7 @@ AlgorithmImplementation::~AlgorithmImplementation() {
Status AlgorithmImplementation::Encrypt(
const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const {
return Status::ErrorUnsupported();
}
@@ -23,14 +25,14 @@ Status AlgorithmImplementation::Encrypt(
Status AlgorithmImplementation::Decrypt(
const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const {
return Status::ErrorUnsupported();
}
Status AlgorithmImplementation::Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const {
return Status::ErrorUnsupported();
}
@@ -38,15 +40,15 @@ Status AlgorithmImplementation::Sign(const blink::WebCryptoAlgorithm& algorithm,
Status AlgorithmImplementation::Verify(
const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match) const {
return Status::ErrorUnsupported();
}
Status AlgorithmImplementation::Digest(
const blink::WebCryptoAlgorithm& algorithm,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const {
return Status::ErrorUnsupported();
}
@@ -77,7 +79,7 @@ Status AlgorithmImplementation::GetKeyLength(
Status AlgorithmImplementation::ImportKey(
blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -94,8 +96,31 @@ Status AlgorithmImplementation::ExportKey(blink::WebCryptoKeyFormat format,
Status AlgorithmImplementation::SerializeKeyForClone(
const blink::WebCryptoKey& key,
blink::WebVector<uint8_t>* key_data) const {
- *key_data = GetSerializedKeyData(key);
- return Status::Success();
+ switch (key.GetType()) {
+ case blink::kWebCryptoKeyTypeSecret:
+ *key_data = GetSymmetricKeyData(key);
+ return Status::Success();
+
+ case blink::kWebCryptoKeyTypePublic: {
+ std::vector<uint8_t> vec;
+ Status status = ExportPKeySpki(GetEVP_PKEY(key), &vec);
+ if (status.IsSuccess()) {
+ *key_data = vec;
+ }
+ return status;
+ }
+
+ case blink::kWebCryptoKeyTypePrivate: {
+ std::vector<uint8_t> vec;
+ Status status = ExportPKeyPkcs8(GetEVP_PKEY(key), &vec);
+ if (status.IsSuccess()) {
+ *key_data = vec;
+ }
+ return status;
+ }
+ }
+ NOTREACHED();
+ return Status::ErrorUnexpected();
}
Status AlgorithmImplementation::DeserializeKeyForClone(
@@ -103,7 +128,7 @@ Status AlgorithmImplementation::DeserializeKeyForClone(
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const {
return Status::ErrorUnsupported();
}
diff --git a/chromium/components/webcrypto/algorithm_implementation.h b/chromium/components/webcrypto/algorithm_implementation.h
index 900c614eb36..97e6e901e74 100644
--- a/chromium/components/webcrypto/algorithm_implementation.h
+++ b/chromium/components/webcrypto/algorithm_implementation.h
@@ -10,11 +10,11 @@
#include <memory>
#include <vector>
+#include "base/containers/span.h"
#include "third_party/blink/public/platform/web_crypto.h"
namespace webcrypto {
-class CryptoData;
class GenerateKeyResult;
class Status;
@@ -51,7 +51,7 @@ class AlgorithmImplementation {
// (crypto.subtle.encrypt() dispatches to this)
virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const;
// This is what is run whenever the spec says:
@@ -60,7 +60,7 @@ class AlgorithmImplementation {
// (crypto.subtle.decrypt() dispatches to this)
virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const;
// This is what is run whenever the spec says:
@@ -69,7 +69,7 @@ class AlgorithmImplementation {
// (crypto.subtle.sign() dispatches to this)
virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const;
// This is what is run whenever the spec says:
@@ -78,8 +78,8 @@ class AlgorithmImplementation {
// (crypto.subtle.verify() dispatches to this)
virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match) const;
// This is what is run whenever the spec says:
@@ -87,7 +87,7 @@ class AlgorithmImplementation {
//
// (crypto.subtle.digest() dispatches to this)
virtual Status Digest(const blink::WebCryptoAlgorithm& algorithm,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const;
// This is what is run whenever the spec says:
@@ -126,7 +126,7 @@ class AlgorithmImplementation {
//
// (crypto.subtle.importKey() dispatches to this).
virtual Status ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -193,7 +193,7 @@ class AlgorithmImplementation {
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const;
};
diff --git a/chromium/components/webcrypto/algorithm_implementations.h b/chromium/components/webcrypto/algorithm_implementations.h
index ed8ba2a2576..186256c15e7 100644
--- a/chromium/components/webcrypto/algorithm_implementations.h
+++ b/chromium/components/webcrypto/algorithm_implementations.h
@@ -27,8 +27,6 @@ std::unique_ptr<AlgorithmImplementation> CreateEcdsaImplementation();
std::unique_ptr<AlgorithmImplementation> CreateEcdhImplementation();
std::unique_ptr<AlgorithmImplementation> CreateHkdfImplementation();
std::unique_ptr<AlgorithmImplementation> CreatePbkdf2Implementation();
-std::unique_ptr<AlgorithmImplementation> CreateEd25519Implementation();
-std::unique_ptr<AlgorithmImplementation> CreateX25519Implementation();
} // namespace webcrypto
diff --git a/chromium/components/webcrypto/algorithm_registry.cc b/chromium/components/webcrypto/algorithm_registry.cc
index 0095214c46b..3ca6d3e773a 100644
--- a/chromium/components/webcrypto/algorithm_registry.cc
+++ b/chromium/components/webcrypto/algorithm_registry.cc
@@ -30,9 +30,7 @@ class AlgorithmRegistry {
ecdsa_(CreateEcdsaImplementation()),
ecdh_(CreateEcdhImplementation()),
hkdf_(CreateHkdfImplementation()),
- pbkdf2_(CreatePbkdf2Implementation()),
- ed25519_(CreateEd25519Implementation()),
- x25519_(CreateX25519Implementation()) {
+ pbkdf2_(CreatePbkdf2Implementation()) {
crypto::EnsureOpenSSLInit();
}
@@ -68,10 +66,6 @@ class AlgorithmRegistry {
return hkdf_.get();
case blink::kWebCryptoAlgorithmIdPbkdf2:
return pbkdf2_.get();
- case blink::kWebCryptoAlgorithmIdEd25519:
- return ed25519_.get();
- case blink::kWebCryptoAlgorithmIdX25519:
- return x25519_.get();
default:
return nullptr;
}
@@ -91,8 +85,6 @@ class AlgorithmRegistry {
const std::unique_ptr<AlgorithmImplementation> ecdh_;
const std::unique_ptr<AlgorithmImplementation> hkdf_;
const std::unique_ptr<AlgorithmImplementation> pbkdf2_;
- const std::unique_ptr<AlgorithmImplementation> ed25519_;
- const std::unique_ptr<AlgorithmImplementation> x25519_;
};
} // namespace
diff --git a/chromium/components/webcrypto/algorithms/aes.cc b/chromium/components/webcrypto/algorithms/aes.cc
index 5d121339698..fe293ff5a6b 100644
--- a/chromium/components/webcrypto/algorithms/aes.cc
+++ b/chromium/components/webcrypto/algorithms/aes.cc
@@ -9,7 +9,6 @@
#include "components/webcrypto/algorithms/secret_key_util.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -77,7 +76,7 @@ Status AesAlgorithm::GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
}
Status AesAlgorithm::ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -105,7 +104,7 @@ Status AesAlgorithm::ExportKey(blink::WebCryptoKeyFormat format,
}
}
-Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data,
+Status AesAlgorithm::ImportKeyRaw(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -114,7 +113,7 @@ Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data,
if (status.IsError())
return status;
- const unsigned int keylen_bytes = key_data.byte_length();
+ const size_t keylen_bytes = key_data.size();
// 192-bit AES is intentionally unsupported (http://crbug.com/533699).
if (keylen_bytes == 24)
@@ -132,7 +131,7 @@ Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data,
extractable, usages, key);
}
-Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data,
+Status AesAlgorithm::ImportKeyJwk(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -169,8 +168,7 @@ Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data,
}
}
- return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages,
- key);
+ return ImportKeyRaw(raw_data, algorithm, extractable, usages, key);
}
Status AesAlgorithm::ExportKeyRaw(const blink::WebCryptoKey& key,
@@ -183,7 +181,7 @@ Status AesAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key,
std::vector<uint8_t>* buffer) const {
const std::vector<uint8_t>& raw_data = GetSymmetricKeyData(key);
- WriteSecretKeyJwk(CryptoData(raw_data),
+ WriteSecretKeyJwk(raw_data,
MakeJwkAesAlgorithmName(jwk_suffix_, raw_data.size()),
key.Extractable(), key.Usages(), buffer);
@@ -195,7 +193,7 @@ Status AesAlgorithm::DeserializeKeyForClone(
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const {
if (algorithm.ParamsType() != blink::kWebCryptoKeyAlgorithmParamsTypeAes ||
type != blink::kWebCryptoKeyTypeSecret)
diff --git a/chromium/components/webcrypto/algorithms/aes.h b/chromium/components/webcrypto/algorithms/aes.h
index b7b0ad55961..0be65b8ee25 100644
--- a/chromium/components/webcrypto/algorithms/aes.h
+++ b/chromium/components/webcrypto/algorithms/aes.h
@@ -34,7 +34,7 @@ class AesAlgorithm : public AlgorithmImplementation {
GenerateKeyResult* result) const override;
Status ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -48,7 +48,7 @@ class AesAlgorithm : public AlgorithmImplementation {
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const override;
Status GetKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm,
@@ -56,13 +56,13 @@ class AesAlgorithm : public AlgorithmImplementation {
unsigned int* length_bits) const override;
private:
- Status ImportKeyRaw(const CryptoData& key_data,
+ Status ImportKeyRaw(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) const;
- Status ImportKeyJwk(const CryptoData& key_data,
+ Status ImportKeyJwk(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
diff --git a/chromium/components/webcrypto/algorithms/aes_cbc.cc b/chromium/components/webcrypto/algorithms/aes_cbc.cc
index 27e9a2dcd1a..473a0013152 100644
--- a/chromium/components/webcrypto/algorithms/aes_cbc.cc
+++ b/chromium/components/webcrypto/algorithms/aes_cbc.cc
@@ -13,7 +13,6 @@
#include "components/webcrypto/algorithms/aes.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -39,7 +38,7 @@ const EVP_CIPHER* GetAESCipherByKeyLength(size_t key_length_bytes) {
Status AesCbcEncryptDecrypt(EncryptOrDecrypt cipher_operation,
const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
@@ -52,7 +51,7 @@ Status AesCbcEncryptDecrypt(EncryptOrDecrypt cipher_operation,
// According to the openssl docs, the amount of data written may be as large
// as (data_size + cipher_block_size - 1), constrained to a multiple of
// cipher_block_size.
- base::CheckedNumeric<int> output_max_len = data.byte_length();
+ base::CheckedNumeric<int> output_max_len = data.size();
output_max_len += AES_BLOCK_SIZE - 1;
if (!output_max_len.IsValid())
return Status::ErrorDataTooLarge();
@@ -70,15 +69,15 @@ Status AesCbcEncryptDecrypt(EncryptOrDecrypt cipher_operation,
bssl::ScopedEVP_CIPHER_CTX context;
if (!EVP_CipherInit_ex(context.get(), cipher, nullptr, &raw_key[0],
- params->Iv().Data(), cipher_operation)) {
+ params->Iv().data(), cipher_operation)) {
return Status::OperationError();
}
buffer->resize(base::ValueOrDieForType<size_t>(output_max_len));
int output_len = 0;
- if (!EVP_CipherUpdate(context.get(), buffer->data(), &output_len,
- data.bytes(), data.byte_length())) {
+ if (!EVP_CipherUpdate(context.get(), buffer->data(), &output_len, data.data(),
+ base::checked_cast<int>(data.size()))) {
return Status::OperationError();
}
int final_output_chunk_len = 0;
@@ -102,14 +101,14 @@ class AesCbcImplementation : public AesAlgorithm {
Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
return AesCbcEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer);
}
Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
return AesCbcEncryptDecrypt(DECRYPT, algorithm, key, data, buffer);
}
diff --git a/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc b/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc
index f5a9790ba9b..dc94f948527 100644
--- a/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/aes_cbc_unittest.cc
@@ -9,10 +9,10 @@
#include <memory>
#include <utility>
+#include "base/containers/span.h"
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
@@ -63,7 +63,7 @@ TEST_F(WebCryptoAesCbcTest, InputTooLarge) {
// Pretend the input is large. Don't pass data pointer as NULL in case that
// is special cased; the implementation shouldn't actually dereference the
// data.
- CryptoData input(&iv[0], INT_MAX - 3);
+ base::span<const uint8_t> input(iv.data(), INT_MAX - 3);
EXPECT_EQ(
Status::ErrorDataTooLarge(),
@@ -105,7 +105,7 @@ TEST_F(WebCryptoAesCbcTest, KnownAnswerEncryptDecrypt) {
// Import the key.
blink::WebCryptoKey key;
Status status = ImportKey(
- key_format, CryptoData(key_data),
+ key_format, key_data,
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
&key);
@@ -124,8 +124,8 @@ TEST_F(WebCryptoAesCbcTest, KnownAnswerEncryptDecrypt) {
test->GetString("encrypt_error", &encrypt_error);
std::vector<uint8_t> output;
- status = Encrypt(CreateAesCbcAlgorithm(test_iv), key,
- CryptoData(test_plain_text), &output);
+ status = Encrypt(CreateAesCbcAlgorithm(test_iv), key, test_plain_text,
+ &output);
ASSERT_EQ(encrypt_error, StatusToString(status));
if (status.IsError())
continue;
@@ -147,8 +147,8 @@ TEST_F(WebCryptoAesCbcTest, KnownAnswerEncryptDecrypt) {
test->GetString("decrypt_error", &decrypt_error);
std::vector<uint8_t> output;
- status = Decrypt(CreateAesCbcAlgorithm(test_iv), key,
- CryptoData(test_cipher_text), &output);
+ status = Decrypt(CreateAesCbcAlgorithm(test_iv), key, test_cipher_text,
+ &output);
ASSERT_EQ(decrypt_error, StatusToString(status));
if (status.IsError())
continue;
@@ -207,8 +207,7 @@ TEST_F(WebCryptoAesCbcTest, GenerateKeyBadLength) {
TEST_F(WebCryptoAesCbcTest, ImportKeyEmptyUsage) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(std::vector<uint8_t>(16)),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, std::vector<uint8_t>(16),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
0, &key));
}
@@ -338,14 +337,15 @@ TEST_F(WebCryptoAesCbcTest, ImportJwkInvalidJson) {
blink::WebCryptoKey key;
// Fail on empty JSON.
EXPECT_EQ(Status::ErrorJwkNotDictionary(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, {},
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageEncrypt, &key));
// Fail on invalid JSON.
const std::string bad_json = R"({ "kty": "oct", "alg": "HS256", "use": )";
EXPECT_EQ(Status::ErrorJwkNotDictionary(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(bad_json),
+ ImportKey(blink::kWebCryptoKeyFormatJwk,
+ base::as_bytes(base::make_span(bad_json)),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageEncrypt, &key));
}
@@ -410,8 +410,7 @@ TEST_F(WebCryptoAesCbcTest, UnwrapAesCbc192) {
blink::WebCryptoKey unwrapped_key;
ASSERT_EQ(Status::ErrorAes192BitUnsupported(),
- UnwrapKey(blink::kWebCryptoKeyFormatRaw, CryptoData(wrapped_key),
- wrapping_key,
+ UnwrapKey(blink::kWebCryptoKeyFormatRaw, wrapped_key, wrapping_key,
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesKw),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
blink::kWebCryptoKeyUsageEncrypt, &unwrapped_key));
@@ -438,8 +437,8 @@ TEST_F(WebCryptoAesCbcTest, ImportKeyBadUsage_Raw) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
- ImportKey(blink::kWebCryptoKeyFormatRaw, CryptoData(key_bytes),
- algorithm, true, bad_usages[i], &key));
+ ImportKey(blink::kWebCryptoKeyFormatRaw, key_bytes, algorithm,
+ true, bad_usages[i], &key));
}
}
@@ -527,18 +526,16 @@ TEST_F(WebCryptoAesCbcTest, WrapUnwrapRoundtripSpkiPkcs8) {
blink::WebCryptoKey unwrapped_public_key;
- ASSERT_EQ(
- Status::Success(),
- UnwrapKey(blink::kWebCryptoKeyFormatSpki, CryptoData(wrapped_public_key),
- wrapping_key, wrap_algorithm, rsa_import_algorithm, true,
- blink::kWebCryptoKeyUsageVerify, &unwrapped_public_key));
+ ASSERT_EQ(Status::Success(),
+ UnwrapKey(blink::kWebCryptoKeyFormatSpki, wrapped_public_key,
+ wrapping_key, wrap_algorithm, rsa_import_algorithm, true,
+ blink::kWebCryptoKeyUsageVerify, &unwrapped_public_key));
blink::WebCryptoKey unwrapped_private_key;
ASSERT_EQ(Status::Success(),
- UnwrapKey(blink::kWebCryptoKeyFormatPkcs8,
- CryptoData(wrapped_private_key), wrapping_key,
- wrap_algorithm, rsa_import_algorithm, true,
+ UnwrapKey(blink::kWebCryptoKeyFormatPkcs8, wrapped_private_key,
+ wrapping_key, wrap_algorithm, rsa_import_algorithm, true,
blink::kWebCryptoKeyUsageSign, &unwrapped_private_key));
// Export unwrapped key pair as SPKI + PKCS8
diff --git a/chromium/components/webcrypto/algorithms/aes_ctr.cc b/chromium/components/webcrypto/algorithms/aes_ctr.cc
index 186a4106c6c..2b3771a62e5 100644
--- a/chromium/components/webcrypto/algorithms/aes_ctr.cc
+++ b/chromium/components/webcrypto/algorithms/aes_ctr.cc
@@ -6,19 +6,21 @@
#include <stdint.h>
#include <string.h>
+#include <array>
#include <memory>
#include "base/check_op.h"
+#include "base/containers/span.h"
+#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math.h"
#include "components/webcrypto/algorithms/aes.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
+#include "third_party/abseil-cpp/absl/numeric/int128.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/boringssl/src/include/openssl/aes.h"
-#include "third_party/boringssl/src/include/openssl/bn.h"
#include "third_party/boringssl/src/include/openssl/cipher.h"
namespace webcrypto {
@@ -39,36 +41,35 @@ const EVP_CIPHER* GetAESCipherByKeyLength(size_t key_length_bytes) {
// Encrypts/decrypts given a 128-bit counter.
//
-// |output| must be a pointer to a buffer which has a length of at least
-// |input.byte_length()|.
+// |output| must have the same length as |input|.
Status AesCtrEncrypt128BitCounter(const EVP_CIPHER* cipher,
- const CryptoData& raw_key,
- const CryptoData& input,
- const CryptoData& counter,
- uint8_t* output) {
+ base::span<const uint8_t> raw_key,
+ base::span<const uint8_t> input,
+ base::span<const uint8_t, 16> counter,
+ base::span<uint8_t> output) {
DCHECK(cipher);
- DCHECK_EQ(16u, counter.byte_length());
+ DCHECK_EQ(input.size(), output.size());
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
bssl::ScopedEVP_CIPHER_CTX context;
- if (!EVP_CipherInit_ex(context.get(), cipher, nullptr, raw_key.bytes(),
- counter.bytes(), ENCRYPT)) {
+ if (!EVP_CipherInit_ex(context.get(), cipher, nullptr, raw_key.data(),
+ counter.data(), ENCRYPT)) {
return Status::OperationError();
}
int output_len = 0;
- if (!EVP_CipherUpdate(context.get(), output, &output_len, input.bytes(),
- input.byte_length())) {
+ if (!EVP_CipherUpdate(context.get(), output.data(), &output_len, input.data(),
+ base::checked_cast<int>(input.size()))) {
return Status::OperationError();
}
int final_output_chunk_len = 0;
- if (!EVP_CipherFinal_ex(context.get(), output + output_len,
+ if (!EVP_CipherFinal_ex(context.get(), output.data() + output_len,
&final_output_chunk_len)) {
return Status::OperationError();
}
output_len += final_output_chunk_len;
- if (static_cast<unsigned int>(output_len) != input.byte_length())
+ if (static_cast<size_t>(output_len) != input.size())
return Status::ErrorUnexpected();
return Status::Success();
@@ -80,41 +81,35 @@ T CeilDiv(T a, T b) {
return a == 0 ? 0 : 1 + (a - 1) / b;
}
-// Extracts the counter as a BIGNUM. The counter is the rightmost
-// "counter_length_bits" of the block, interpreted as a big-endian number.
-bssl::UniquePtr<BIGNUM> GetCounter(const CryptoData& counter_block,
- unsigned int counter_length_bits) {
- unsigned int counter_length_remainder_bits = (counter_length_bits % 8);
-
- // If the counter is a multiple of 8 bits then can call BN_bin2bn() directly.
- if (counter_length_remainder_bits == 0) {
- unsigned int byte_length = counter_length_bits / 8;
- return bssl::UniquePtr<BIGNUM>(BN_bin2bn(
- counter_block.bytes() + counter_block.byte_length() - byte_length,
- byte_length, nullptr));
- }
-
- // Otherwise make a copy of the counter and zero out the topmost bits so
- // BN_bin2bn() can be called with a byte stream.
+// Extracts the counter as a `absl::uint128`. The counter is the rightmost
+// `counter_length_bits` of the block, interpreted as a big-endian number.
+absl::uint128 GetCounter(base::span<const uint8_t, 16> counter_block,
+ unsigned int counter_length_bits) {
+ unsigned int counter_length_remainder_bits = counter_length_bits % 8;
unsigned int byte_length = CeilDiv(counter_length_bits, 8u);
- std::vector<uint8_t> counter(
- counter_block.bytes() + counter_block.byte_length() - byte_length,
- counter_block.bytes() + counter_block.byte_length());
- counter[0] &= ~(0xFF << counter_length_remainder_bits);
+ DCHECK_GT(byte_length, 0u);
- return bssl::UniquePtr<BIGNUM>(
- BN_bin2bn(counter.data(), counter.size(), nullptr));
+ base::span<const uint8_t> suffix = counter_block.last(byte_length);
+ absl::uint128 ret = suffix[0];
+ // The first byte may be partial.
+ if (counter_length_remainder_bits != 0) {
+ ret &= ~(0xFF << counter_length_remainder_bits);
+ }
+ for (uint8_t b : suffix.subspan(1)) {
+ ret = (ret << 8) | b;
+ }
+ return ret;
}
// Returns a counter block with the counter bits all set all zero.
-std::vector<uint8_t> BlockWithZeroedCounter(const CryptoData& counter_block,
- unsigned int counter_length_bits) {
+std::array<uint8_t, AES_BLOCK_SIZE> BlockWithZeroedCounter(
+ base::span<const uint8_t, AES_BLOCK_SIZE> counter_block,
+ unsigned int counter_length_bits) {
unsigned int counter_length_bytes = counter_length_bits / 8;
unsigned int counter_length_bits_remainder = counter_length_bits % 8;
- std::vector<uint8_t> new_counter_block(
- counter_block.bytes(),
- counter_block.bytes() + counter_block.byte_length());
+ std::array<uint8_t, AES_BLOCK_SIZE> new_counter_block;
+ memcpy(new_counter_block.data(), counter_block.data(), AES_BLOCK_SIZE);
size_t index = new_counter_block.size() - counter_length_bytes;
memset(&new_counter_block.front() + index, 0, counter_length_bytes);
@@ -143,13 +138,15 @@ std::vector<uint8_t> BlockWithZeroedCounter(const CryptoData& counter_block,
// encrypt/decrypt.
Status AesCtrEncryptDecrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
const blink::WebCryptoAesCtrParams* params = algorithm.AesCtrParams();
const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(key);
- if (params->Counter().size() != 16)
+ if (params->Counter().size() != AES_BLOCK_SIZE)
return Status::ErrorIncorrectSizeAesCtrCounter();
+ base::span<const uint8_t, AES_BLOCK_SIZE> counter_block(
+ params->Counter().data(), params->Counter().size());
unsigned int counter_length_bits = params->LengthBits();
if (counter_length_bits < 1 || counter_length_bits > 128)
@@ -157,7 +154,7 @@ Status AesCtrEncryptDecrypt(const blink::WebCryptoAlgorithm& algorithm,
// The output of AES-CTR is the same size as the input. However BoringSSL
// expects buffer sizes as an "int".
- base::CheckedNumeric<int> output_max_len = data.byte_length();
+ base::CheckedNumeric<int> output_max_len = data.size();
if (!output_max_len.IsValid())
return Status::ErrorDataTooLarge();
@@ -165,74 +162,67 @@ Status AesCtrEncryptDecrypt(const blink::WebCryptoAlgorithm& algorithm,
if (!cipher)
return Status::ErrorUnexpected();
- const CryptoData counter_block(params->Counter());
buffer->resize(base::ValueOrDieForType<size_t>(output_max_len));
+ absl::uint128 current_counter =
+ GetCounter(counter_block, counter_length_bits);
- // The total number of possible counter values is pow(2, counter_length_bits)
- bssl::UniquePtr<BIGNUM> num_counter_values(BN_new());
- if (!BN_lshift(num_counter_values.get(), BN_value_one(), counter_length_bits))
- return Status::ErrorUnexpected();
+ if (counter_length_bits == 128) {
+ return AesCtrEncrypt128BitCounter(cipher, raw_key, data, counter_block,
+ *buffer);
+ }
- bssl::UniquePtr<BIGNUM> current_counter =
- GetCounter(counter_block, counter_length_bits);
+ // The total number of possible counter values is pow(2, counter_length_bits)
+ absl::uint128 num_counter_values = absl::uint128(1) << counter_length_bits;
// The number of AES blocks needed for encryption/decryption. The counter is
// incremented this many times.
- bssl::UniquePtr<BIGNUM> num_output_blocks(BN_new());
- if (!BN_set_word(
- num_output_blocks.get(),
- CeilDiv(buffer->size(), static_cast<size_t>(AES_BLOCK_SIZE)))) {
- return Status::ErrorUnexpected();
- }
+ size_t num_output_blocks = CeilDiv(buffer->size(), size_t{AES_BLOCK_SIZE});
// If the counter is going to be incremented more times than there are counter
// values, fail. (Repeating values of the counter block is bad).
- if (BN_cmp(num_output_blocks.get(), num_counter_values.get()) > 0)
+ if (num_output_blocks > num_counter_values)
return Status::ErrorAesCtrInputTooLongCounterRepeated();
// This is the number of blocks that can be successfully encrypted without
// overflowing the counter. Encrypting the subsequent block will need to
// reset the counter to zero.
- bssl::UniquePtr<BIGNUM> num_blocks_until_reset(BN_new());
-
- if (!BN_sub(num_blocks_until_reset.get(), num_counter_values.get(),
- current_counter.get())) {
- return Status::ErrorUnexpected();
- }
+ absl::uint128 num_blocks_until_reset = num_counter_values - current_counter;
// If the counter can be incremented for the entire input without
// wrapping-around, do it as a single call into BoringSSL.
- if (BN_cmp(num_blocks_until_reset.get(), num_output_blocks.get()) >= 0) {
- return AesCtrEncrypt128BitCounter(cipher, CryptoData(raw_key), data,
- counter_block, buffer->data());
+ if (num_blocks_until_reset >= num_output_blocks) {
+ return AesCtrEncrypt128BitCounter(cipher, raw_key, data, counter_block,
+ *buffer);
}
// Otherwise the encryption needs to be done in 2 parts. The first part using
// the current counter_block, and the next part resetting the counter portion
// of the block to zero.
- // This is guaranteed to fit in an "unsigned int" because input size in bytes
- // fits in an "unsigned int".
- BN_ULONG num_blocks_part1 = BN_get_word(num_blocks_until_reset.get());
- BN_ULONG input_size_part1 = num_blocks_part1 * AES_BLOCK_SIZE;
- DCHECK_LT(input_size_part1, data.byte_length());
+ // This is guaranteed to fit in an `size_t` because it is bounded by the input
+ // size.
+ size_t input_size_part1 =
+ static_cast<size_t>(num_blocks_until_reset * AES_BLOCK_SIZE);
+ DCHECK_LT(input_size_part1, data.size());
+ base::span<uint8_t> output_part1 =
+ base::make_span(*buffer).first(input_size_part1);
+ base::span<uint8_t> output_part2 =
+ base::make_span(*buffer).subspan(input_size_part1);
// Encrypt the first part (before wrap-around).
- Status status = AesCtrEncrypt128BitCounter(
- cipher, CryptoData(raw_key), CryptoData(data.bytes(), input_size_part1),
- counter_block, buffer->data());
+ Status status =
+ AesCtrEncrypt128BitCounter(cipher, raw_key, data.first(input_size_part1),
+ counter_block, output_part1);
if (status.IsError())
return status;
// Encrypt the second part (after wrap-around).
- std::vector<uint8_t> counter_block_part2 =
+ std::array<uint8_t, AES_BLOCK_SIZE> counter_block_part2 =
BlockWithZeroedCounter(counter_block, counter_length_bits);
- return AesCtrEncrypt128BitCounter(
- cipher, CryptoData(raw_key),
- CryptoData(data.bytes() + input_size_part1,
- data.byte_length() - input_size_part1),
- CryptoData(counter_block_part2), buffer->data() + input_size_part1);
+ return AesCtrEncrypt128BitCounter(cipher, raw_key,
+ data.subspan(input_size_part1),
+ counter_block_part2, output_part2);
}
class AesCtrImplementation : public AesAlgorithm {
@@ -241,14 +231,14 @@ class AesCtrImplementation : public AesAlgorithm {
Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
return AesCtrEncryptDecrypt(algorithm, key, data, buffer);
}
Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
return AesCtrEncryptDecrypt(algorithm, key, data, buffer);
}
diff --git a/chromium/components/webcrypto/algorithms/aes_ctr_unittest.cc b/chromium/components/webcrypto/algorithms/aes_ctr_unittest.cc
index c91ea0f00cf..49da5a0741c 100644
--- a/chromium/components/webcrypto/algorithms/aes_ctr_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/aes_ctr_unittest.cc
@@ -5,9 +5,9 @@
#include <stddef.h>
#include <stdint.h>
+#include "base/containers/span.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
@@ -112,13 +112,13 @@ TEST_F(WebCryptoAesCtrTest, EncryptDecryptKnownAnswer) {
// Test encryption.
EXPECT_EQ(Status::Success(),
Encrypt(CreateAesCtrAlgorithm(counter, test.counter_length), key,
- CryptoData(plaintext), &output));
+ plaintext, &output));
EXPECT_EQ(ciphertext, output);
// Test decryption.
EXPECT_EQ(Status::Success(),
Decrypt(CreateAesCtrAlgorithm(counter, test.counter_length), key,
- CryptoData(ciphertext), &output));
+ ciphertext, &output));
EXPECT_EQ(plaintext, output);
}
}
@@ -133,13 +133,13 @@ TEST_F(WebCryptoAesCtrTest, InvalidCounterBlockLength) {
for (size_t bad_length : {0, 15, 17}) {
std::vector<uint8_t> bad_counter(bad_length);
- EXPECT_EQ(Status::ErrorIncorrectSizeAesCtrCounter(),
- Encrypt(CreateAesCtrAlgorithm(bad_counter, 128), key,
- CryptoData(input), &output));
+ EXPECT_EQ(
+ Status::ErrorIncorrectSizeAesCtrCounter(),
+ Encrypt(CreateAesCtrAlgorithm(bad_counter, 128), key, input, &output));
- EXPECT_EQ(Status::ErrorIncorrectSizeAesCtrCounter(),
- Decrypt(CreateAesCtrAlgorithm(bad_counter, 128), key,
- CryptoData(input), &output));
+ EXPECT_EQ(
+ Status::ErrorIncorrectSizeAesCtrCounter(),
+ Decrypt(CreateAesCtrAlgorithm(bad_counter, 128), key, input, &output));
}
}
@@ -153,12 +153,12 @@ TEST_F(WebCryptoAesCtrTest, InvalidCounterLength) {
// The counter length cannot be less than 1 or greater than 128.
for (uint8_t bad_length : {0, 129}) {
EXPECT_EQ(Status::ErrorInvalidAesCtrCounterLength(),
- Encrypt(CreateAesCtrAlgorithm(counter, bad_length), key,
- CryptoData(input), &output));
+ Encrypt(CreateAesCtrAlgorithm(counter, bad_length), key, input,
+ &output));
EXPECT_EQ(Status::ErrorInvalidAesCtrCounterLength(),
- Decrypt(CreateAesCtrAlgorithm(counter, bad_length), key,
- CryptoData(input), &output));
+ Decrypt(CreateAesCtrAlgorithm(counter, bad_length), key, input,
+ &output));
}
}
@@ -178,8 +178,8 @@ TEST_F(WebCryptoAesCtrTest, OverflowAndRepeatCounter) {
// 16 and 17 AES blocks worth of data respectively (AES blocks are 16 bytes
// long).
- CryptoData input_16(buffer.data(), 256);
- CryptoData input_17(buffer.data(), 272);
+ auto input_16 = base::make_span(buffer).first(256);
+ auto input_17 = base::make_span(buffer).first(272);
std::vector<uint8_t> output;
diff --git a/chromium/components/webcrypto/algorithms/aes_gcm.cc b/chromium/components/webcrypto/algorithms/aes_gcm.cc
index 0f3f70c80a0..73c5cf2e0e9 100644
--- a/chromium/components/webcrypto/algorithms/aes_gcm.cc
+++ b/chromium/components/webcrypto/algorithms/aes_gcm.cc
@@ -11,7 +11,6 @@
#include "components/webcrypto/algorithms/aes.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -35,7 +34,7 @@ const EVP_AEAD* GetAesGcmAlgorithmFromKeySize(size_t key_size_bytes) {
Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode,
const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(key);
const blink::WebCryptoAesGcmParams* params = algorithm.AesGcmParams();
@@ -53,10 +52,10 @@ Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode,
}
}
- return AeadEncryptDecrypt(
- mode, raw_key, data, tag_length_bits / 8, CryptoData(params->Iv()),
- CryptoData(params->OptionalAdditionalData()),
- GetAesGcmAlgorithmFromKeySize(raw_key.size()), buffer);
+ return AeadEncryptDecrypt(mode, raw_key, data, tag_length_bits / 8,
+ params->Iv(), params->OptionalAdditionalData(),
+ GetAesGcmAlgorithmFromKeySize(raw_key.size()),
+ buffer);
}
class AesGcmImplementation : public AesAlgorithm {
@@ -65,14 +64,14 @@ class AesGcmImplementation : public AesAlgorithm {
Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
return AesGcmEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer);
}
Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
return AesGcmEncryptDecrypt(DECRYPT, algorithm, key, data, buffer);
}
diff --git a/chromium/components/webcrypto/algorithms/aes_gcm_unittest.cc b/chromium/components/webcrypto/algorithms/aes_gcm_unittest.cc
index 0dfedb80380..300a0671b78 100644
--- a/chromium/components/webcrypto/algorithms/aes_gcm_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/aes_gcm_unittest.cc
@@ -8,7 +8,6 @@
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -46,7 +45,7 @@ Status AesGcmEncrypt(const blink::WebCryptoKey& key,
CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits);
std::vector<uint8_t> output;
- Status status = Encrypt(algorithm, key, CryptoData(plain_text), &output);
+ Status status = Encrypt(algorithm, key, plain_text, &output);
if (status.IsError())
return status;
@@ -90,7 +89,7 @@ Status AesGcmDecrypt(const blink::WebCryptoKey& key,
authentication_tag.begin(),
authentication_tag.end());
- return Decrypt(algorithm, key, CryptoData(cipher_text_with_tag), plain_text);
+ return Decrypt(algorithm, key, cipher_text_with_tag, plain_text);
}
class WebCryptoAesGcmTest : public WebCryptoTestBase {};
diff --git a/chromium/components/webcrypto/algorithms/aes_kw.cc b/chromium/components/webcrypto/algorithms/aes_kw.cc
index fc6e03352c4..b7e32546643 100644
--- a/chromium/components/webcrypto/algorithms/aes_kw.cc
+++ b/chromium/components/webcrypto/algorithms/aes_kw.cc
@@ -12,7 +12,6 @@
#include "base/numerics/safe_math.h"
#include "components/webcrypto/algorithms/aes.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/boringssl/src/include/openssl/aes.h"
@@ -30,15 +29,15 @@ class AesKwImplementation : public AesAlgorithm {
Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
// These length checks are done in order to give a more specific
// error. These are not required for correctness.
- if (data.byte_length() < 16)
+ if (data.size() < 16)
return Status::ErrorDataTooSmall();
- if (data.byte_length() % 8)
+ if (data.size() % 8)
return Status::ErrorInvalidAesKwDataLength();
// Key import validates key sizes, so the bits computation will not
@@ -52,14 +51,14 @@ class AesKwImplementation : public AesAlgorithm {
}
// Key wrap's overhead is 8 bytes.
- base::CheckedNumeric<size_t> length(data.byte_length());
+ base::CheckedNumeric<size_t> length(data.size());
length += 8;
if (!length.IsValid())
return Status::ErrorDataTooLarge();
buffer->resize(length.ValueOrDie());
if (AES_wrap_key(&aes_key, nullptr /* default IV */, buffer->data(),
- data.bytes(), data.byte_length()) < 0) {
+ data.data(), data.size()) < 0) {
return Status::OperationError();
}
@@ -68,15 +67,15 @@ class AesKwImplementation : public AesAlgorithm {
Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
// These length checks are done in order to give a more specific
// error. These are not required for correctness.
- if (data.byte_length() < 24)
+ if (data.size() < 24)
return Status::ErrorDataTooSmall();
- if (data.byte_length() % 8)
+ if (data.size() % 8)
return Status::ErrorInvalidAesKwDataLength();
// Key import validates key sizes, so the bits computation will not
@@ -90,10 +89,10 @@ class AesKwImplementation : public AesAlgorithm {
}
// Key wrap's overhead is 8 bytes.
- buffer->resize(data.byte_length() - 8);
+ buffer->resize(data.size() - 8);
if (AES_unwrap_key(&aes_key, nullptr /* default IV */, buffer->data(),
- data.bytes(), data.byte_length()) < 0) {
+ data.data(), data.size()) < 0) {
return Status::OperationError();
}
diff --git a/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc b/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc
index 857b4bbcfaf..9d12964ceb8 100644
--- a/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/aes_kw_unittest.cc
@@ -10,7 +10,6 @@
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
@@ -46,8 +45,7 @@ TEST_F(WebCryptoAesKwTest, GenerateKeyEmptyUsage) {
TEST_F(WebCryptoAesKwTest, ImportKeyEmptyUsage) {
blink::WebCryptoKey key;
EXPECT_EQ(Status::ErrorCreateKeyEmptyUsages(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(std::vector<uint8_t>(16)),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, std::vector<uint8_t>(16),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesKw), true,
0, &key));
}
@@ -103,10 +101,10 @@ TEST_F(WebCryptoAesKwTest, AesKwKeyImport) {
// Import a 128-bit Key Encryption Key (KEK)
std::string key_raw_hex_in = "025a8cf3f08b4f6c5f33bbc76a471939";
- ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm,
- true, blink::kWebCryptoKeyUsageWrapKey, &key));
+ ASSERT_EQ(
+ Status::Success(),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, HexStringToBytes(key_raw_hex_in),
+ algorithm, true, blink::kWebCryptoKeyUsageWrapKey, &key));
std::vector<uint8_t> key_raw_out;
EXPECT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatRaw, key, &key_raw_out));
@@ -114,41 +112,40 @@ TEST_F(WebCryptoAesKwTest, AesKwKeyImport) {
// Import a 192-bit KEK
key_raw_hex_in = "c0192c6466b2370decbb62b2cfef4384544ffeb4d2fbc103";
- ASSERT_EQ(Status::ErrorAes192BitUnsupported(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm,
- true, blink::kWebCryptoKeyUsageWrapKey, &key));
+ ASSERT_EQ(
+ Status::ErrorAes192BitUnsupported(),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, HexStringToBytes(key_raw_hex_in),
+ algorithm, true, blink::kWebCryptoKeyUsageWrapKey, &key));
// Import a 256-bit Key Encryption Key (KEK)
key_raw_hex_in =
"e11fe66380d90fa9ebefb74e0478e78f95664d0c67ca20ce4a0b5842863ac46f";
- ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm,
- true, blink::kWebCryptoKeyUsageWrapKey, &key));
+ ASSERT_EQ(
+ Status::Success(),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, HexStringToBytes(key_raw_hex_in),
+ algorithm, true, blink::kWebCryptoKeyUsageWrapKey, &key));
EXPECT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatRaw, key, &key_raw_out));
EXPECT_BYTES_EQ_HEX(key_raw_hex_in, key_raw_out);
// Fail import of 0 length key
- EXPECT_EQ(
- Status::ErrorImportAesKeyLength(),
- ImportKey(blink::kWebCryptoKeyFormatRaw, CryptoData(HexStringToBytes("")),
- algorithm, true, blink::kWebCryptoKeyUsageWrapKey, &key));
+ EXPECT_EQ(Status::ErrorImportAesKeyLength(),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, HexStringToBytes(""),
+ algorithm, true, blink::kWebCryptoKeyUsageWrapKey, &key));
// Fail import of 120-bit KEK
key_raw_hex_in = "3e4566a2bdaa10cb68134fa66c15dd";
- EXPECT_EQ(Status::ErrorImportAesKeyLength(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm,
- true, blink::kWebCryptoKeyUsageWrapKey, &key));
+ EXPECT_EQ(
+ Status::ErrorImportAesKeyLength(),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, HexStringToBytes(key_raw_hex_in),
+ algorithm, true, blink::kWebCryptoKeyUsageWrapKey, &key));
// Fail import of 200-bit KEK
key_raw_hex_in = "0a1d88608a5ad9fec64f1ada269ebab4baa2feeb8d95638c0e";
- EXPECT_EQ(Status::ErrorImportAesKeyLength(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(HexStringToBytes(key_raw_hex_in)), algorithm,
- true, blink::kWebCryptoKeyUsageWrapKey, &key));
+ EXPECT_EQ(
+ Status::ErrorImportAesKeyLength(),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, HexStringToBytes(key_raw_hex_in),
+ algorithm, true, blink::kWebCryptoKeyUsageWrapKey, &key));
}
TEST_F(WebCryptoAesKwTest, UnwrapFailures) {
@@ -168,12 +165,12 @@ TEST_F(WebCryptoAesKwTest, UnwrapFailures) {
blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw(
test_kek, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesKw),
blink::kWebCryptoKeyUsageUnwrapKey);
- EXPECT_EQ(Status::ErrorUnexpected(),
- UnwrapKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(test_ciphertext), wrapping_key,
- CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
- CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
- blink::kWebCryptoKeyUsageEncrypt, &unwrapped_key));
+ EXPECT_EQ(
+ Status::ErrorUnexpected(),
+ UnwrapKey(blink::kWebCryptoKeyFormatRaw, test_ciphertext, wrapping_key,
+ CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
+ CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
+ blink::kWebCryptoKeyUsageEncrypt, &unwrapped_key));
}
TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapKnownAnswer) {
@@ -211,13 +208,12 @@ TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapKnownAnswer) {
// Unwrap the known ciphertext to get a new test_key.
blink::WebCryptoKey unwrapped_key;
- ASSERT_EQ(
- Status::Success(),
- UnwrapKey(
- blink::kWebCryptoKeyFormatRaw, CryptoData(test_ciphertext),
- wrapping_key, wrapping_algorithm,
- CreateHmacImportAlgorithmNoLength(blink::kWebCryptoAlgorithmIdSha1),
- true, blink::kWebCryptoKeyUsageSign, &unwrapped_key));
+ ASSERT_EQ(Status::Success(),
+ UnwrapKey(blink::kWebCryptoKeyFormatRaw, test_ciphertext,
+ wrapping_key, wrapping_algorithm,
+ CreateHmacImportAlgorithmNoLength(
+ blink::kWebCryptoAlgorithmIdSha1),
+ true, blink::kWebCryptoKeyUsageSign, &unwrapped_key));
EXPECT_FALSE(key.IsNull());
EXPECT_TRUE(key.Handle());
EXPECT_EQ(blink::kWebCryptoKeyTypeSecret, key.GetType());
@@ -256,8 +252,8 @@ TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapSignVerifyHmac) {
ASSERT_EQ(
Status::Success(),
UnwrapKey(
- blink::kWebCryptoKeyFormatRaw, CryptoData(test_ciphertext),
- wrapping_key, wrapping_algorithm,
+ blink::kWebCryptoKeyFormatRaw, test_ciphertext, wrapping_key,
+ wrapping_algorithm,
CreateHmacImportAlgorithmNoLength(blink::kWebCryptoAlgorithmIdSha1),
false,
blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify,
@@ -275,15 +271,14 @@ TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapSignVerifyHmac) {
ASSERT_EQ(Status::Success(),
Sign(CreateAlgorithm(blink::kWebCryptoAlgorithmIdHmac), key,
- CryptoData(test_message), &signature));
+ test_message, &signature));
EXPECT_GT(signature.size(), 0u);
bool verify_result;
- ASSERT_EQ(
- Status::Success(),
- Verify(CreateAlgorithm(blink::kWebCryptoAlgorithmIdHmac), key,
- CryptoData(signature), CryptoData(test_message), &verify_result));
+ ASSERT_EQ(Status::Success(),
+ Verify(CreateAlgorithm(blink::kWebCryptoAlgorithmIdHmac), key,
+ signature, test_message, &verify_result));
}
TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapErrors) {
@@ -315,15 +310,15 @@ TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapErrors) {
test_ciphertext.begin() + 23);
blink::WebCryptoKey unwrapped_key;
EXPECT_EQ(Status::ErrorDataTooSmall(),
- UnwrapKey(blink::kWebCryptoKeyFormatRaw, CryptoData(small_data),
- wrapping_key, wrapping_algorithm, key_algorithm, true,
+ UnwrapKey(blink::kWebCryptoKeyFormatRaw, small_data, wrapping_key,
+ wrapping_algorithm, key_algorithm, true,
blink::kWebCryptoKeyUsageEncrypt, &unwrapped_key));
// Unwrap with wrapped data size not a multiple of 8 bytes must fail.
const std::vector<uint8_t> unaligned_data(test_ciphertext.begin(),
test_ciphertext.end() - 2);
EXPECT_EQ(Status::ErrorInvalidAesKwDataLength(),
- UnwrapKey(blink::kWebCryptoKeyFormatRaw, CryptoData(unaligned_data),
+ UnwrapKey(blink::kWebCryptoKeyFormatRaw, unaligned_data,
wrapping_key, wrapping_algorithm, key_algorithm, true,
blink::kWebCryptoKeyUsageEncrypt, &unwrapped_key));
}
@@ -351,9 +346,8 @@ TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapCorruptData) {
// AES-KW's built-in integrity check.
blink::WebCryptoKey unwrapped_key;
EXPECT_EQ(Status::OperationError(),
- UnwrapKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(Corrupted(test_ciphertext)), wrapping_key,
- wrapping_algorithm,
+ UnwrapKey(blink::kWebCryptoKeyFormatRaw, Corrupted(test_ciphertext),
+ wrapping_key, wrapping_algorithm,
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
blink::kWebCryptoKeyUsageEncrypt, &unwrapped_key));
}
@@ -385,13 +379,12 @@ TEST_F(WebCryptoAesKwTest, AesKwJwkSymkeyUnwrapKnownData) {
// Unwrap the known wrapped key data to produce a new key
blink::WebCryptoKey unwrapped_key;
- ASSERT_EQ(
- Status::Success(),
- UnwrapKey(
- blink::kWebCryptoKeyFormatJwk, CryptoData(wrapped_key_data),
- wrapping_key, wrapping_algorithm,
- CreateHmacImportAlgorithmNoLength(blink::kWebCryptoAlgorithmIdSha256),
- true, blink::kWebCryptoKeyUsageVerify, &unwrapped_key));
+ ASSERT_EQ(Status::Success(),
+ UnwrapKey(blink::kWebCryptoKeyFormatJwk, wrapped_key_data,
+ wrapping_key, wrapping_algorithm,
+ CreateHmacImportAlgorithmNoLength(
+ blink::kWebCryptoAlgorithmIdSha256),
+ true, blink::kWebCryptoKeyUsageVerify, &unwrapped_key));
// Validate the new key's attributes.
EXPECT_FALSE(unwrapped_key.IsNull());
@@ -434,8 +427,8 @@ TEST_F(WebCryptoAesKwTest, ImportKeyBadUsage_Raw) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
- ImportKey(blink::kWebCryptoKeyFormatRaw, CryptoData(key_bytes),
- algorithm, true, bad_usages[i], &key));
+ ImportKey(blink::kWebCryptoKeyFormatRaw, key_bytes, algorithm,
+ true, bad_usages[i], &key));
}
}
@@ -457,9 +450,9 @@ TEST_F(WebCryptoAesKwTest, UnwrapHmacKeyBadUsage_JWK) {
// Import the wrapping key.
blink::WebCryptoKey wrapping_key;
ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(std::vector<uint8_t>(16)), unwrap_algorithm,
- true, blink::kWebCryptoKeyUsageUnwrapKey, &wrapping_key));
+ ImportKey(blink::kWebCryptoKeyFormatRaw, std::vector<uint8_t>(16),
+ unwrap_algorithm, true,
+ blink::kWebCryptoKeyUsageUnwrapKey, &wrapping_key));
// The JWK plain text is:
// {"kty":"oct","alg":"HS256","k":"GADWrMRHwQfoNaXU5fZvTg"}
@@ -472,13 +465,13 @@ TEST_F(WebCryptoAesKwTest, UnwrapHmacKeyBadUsage_JWK) {
blink::WebCryptoKey key;
- ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
- UnwrapKey(blink::kWebCryptoKeyFormatJwk,
- CryptoData(HexStringToBytes(kWrappedJwk)), wrapping_key,
- unwrap_algorithm,
- CreateHmacImportAlgorithmNoLength(
- blink::kWebCryptoAlgorithmIdSha256),
- true, bad_usages[i], &key));
+ ASSERT_EQ(
+ Status::ErrorCreateKeyBadUsages(),
+ UnwrapKey(blink::kWebCryptoKeyFormatJwk, HexStringToBytes(kWrappedJwk),
+ wrapping_key, unwrap_algorithm,
+ CreateHmacImportAlgorithmNoLength(
+ blink::kWebCryptoAlgorithmIdSha256),
+ true, bad_usages[i], &key));
}
}
@@ -500,9 +493,9 @@ TEST_F(WebCryptoAesKwTest, UnwrapRsaSsaPublicKeyBadUsage_JWK) {
// Import the wrapping key.
blink::WebCryptoKey wrapping_key;
ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(std::vector<uint8_t>(16)), unwrap_algorithm,
- true, blink::kWebCryptoKeyUsageUnwrapKey, &wrapping_key));
+ ImportKey(blink::kWebCryptoKeyFormatRaw, std::vector<uint8_t>(16),
+ unwrap_algorithm, true,
+ blink::kWebCryptoKeyUsageUnwrapKey, &wrapping_key));
// The JWK plaintext is:
// { "kty": "RSA","alg": "RS256","n": "...","e": "AQAB"}
@@ -521,14 +514,14 @@ TEST_F(WebCryptoAesKwTest, UnwrapRsaSsaPublicKeyBadUsage_JWK) {
blink::WebCryptoKey key;
- ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
- UnwrapKey(blink::kWebCryptoKeyFormatJwk,
- CryptoData(HexStringToBytes(kWrappedJwk)), wrapping_key,
- unwrap_algorithm,
- CreateRsaHashedImportAlgorithm(
- blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
- blink::kWebCryptoAlgorithmIdSha256),
- true, bad_usages[i], &key));
+ ASSERT_EQ(
+ Status::ErrorCreateKeyBadUsages(),
+ UnwrapKey(blink::kWebCryptoKeyFormatJwk, HexStringToBytes(kWrappedJwk),
+ wrapping_key, unwrap_algorithm,
+ CreateRsaHashedImportAlgorithm(
+ blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
+ blink::kWebCryptoAlgorithmIdSha256),
+ true, bad_usages[i], &key));
}
}
diff --git a/chromium/components/webcrypto/algorithms/asymmetric_key_util.cc b/chromium/components/webcrypto/algorithms/asymmetric_key_util.cc
index 07d4f6b33d3..3c13427adca 100644
--- a/chromium/components/webcrypto/algorithms/asymmetric_key_util.cc
+++ b/chromium/components/webcrypto/algorithms/asymmetric_key_util.cc
@@ -9,8 +9,6 @@
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
-#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
@@ -19,8 +17,6 @@
namespace webcrypto {
-namespace {
-
// Exports an EVP_PKEY public key to the SPKI format.
Status ExportPKeySpki(EVP_PKEY* key, std::vector<uint8_t>* buffer) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
@@ -55,22 +51,13 @@ Status ExportPKeyPkcs8(EVP_PKEY* key, std::vector<uint8_t>* buffer) {
return Status::Success();
}
-} // namespace
-
Status CreateWebCryptoPublicKey(bssl::UniquePtr<EVP_PKEY> public_key,
const blink::WebCryptoKeyAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) {
- // Serialize the key at creation time so that if structured cloning is
- // requested it can be done synchronously from the Blink thread.
- std::vector<uint8_t> spki_data;
- Status status = ExportPKeySpki(public_key.get(), &spki_data);
- if (status.IsError())
- return status;
-
*key = blink::WebCryptoKey::Create(
- CreateAsymmetricKeyHandle(std::move(public_key), spki_data),
+ CreateAsymmetricKeyHandle(std::move(public_key)),
blink::kWebCryptoKeyTypePublic, extractable, algorithm, usages);
return Status::Success();
}
@@ -80,26 +67,19 @@ Status CreateWebCryptoPrivateKey(bssl::UniquePtr<EVP_PKEY> private_key,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) {
- // Serialize the key at creation time so that if structured cloning is
- // requested it can be done synchronously from the Blink thread.
- std::vector<uint8_t> pkcs8_data;
- Status status = ExportPKeyPkcs8(private_key.get(), &pkcs8_data);
- if (status.IsError())
- return status;
-
*key = blink::WebCryptoKey::Create(
- CreateAsymmetricKeyHandle(std::move(private_key), pkcs8_data),
+ CreateAsymmetricKeyHandle(std::move(private_key)),
blink::kWebCryptoKeyTypePrivate, extractable, algorithm, usages);
return Status::Success();
}
-Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data,
+Status ImportUnverifiedPkeyFromSpki(base::span<const uint8_t> key_data,
int expected_pkey_id,
bssl::UniquePtr<EVP_PKEY>* out_pkey) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
CBS cbs;
- CBS_init(&cbs, key_data.bytes(), key_data.byte_length());
+ CBS_init(&cbs, key_data.data(), key_data.size());
bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_public_key(&cbs));
if (!pkey || CBS_len(&cbs) != 0)
return Status::DataError();
@@ -111,13 +91,13 @@ Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data,
return Status::Success();
}
-Status ImportUnverifiedPkeyFromPkcs8(const CryptoData& key_data,
+Status ImportUnverifiedPkeyFromPkcs8(base::span<const uint8_t> key_data,
int expected_pkey_id,
bssl::UniquePtr<EVP_PKEY>* out_pkey) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
CBS cbs;
- CBS_init(&cbs, key_data.bytes(), key_data.byte_length());
+ CBS_init(&cbs, key_data.data(), key_data.size());
bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_private_key(&cbs));
if (!pkey || CBS_len(&cbs) != 0)
return Status::DataError();
diff --git a/chromium/components/webcrypto/algorithms/asymmetric_key_util.h b/chromium/components/webcrypto/algorithms/asymmetric_key_util.h
index 2355bd3cdf9..176a71d8e37 100644
--- a/chromium/components/webcrypto/algorithms/asymmetric_key_util.h
+++ b/chromium/components/webcrypto/algorithms/asymmetric_key_util.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_WEBCRYPTO_ALGORITHMS_ASYMMETRIC_KEY_UTIL_H_
#define COMPONENTS_WEBCRYPTO_ALGORITHMS_ASYMMETRIC_KEY_UTIL_H_
+#include "base/containers/span.h"
#include "third_party/blink/public/platform/web_crypto_algorithm.h"
#include "third_party/blink/public/platform/web_crypto_key.h"
#include "third_party/boringssl/src/include/openssl/base.h"
@@ -13,9 +14,14 @@
namespace webcrypto {
-class CryptoData;
class Status;
+// Exports an EVP_PKEY public key to the SPKI format.
+Status ExportPKeySpki(EVP_PKEY* key, std::vector<uint8_t>* buffer);
+
+// Exports an EVP_PKEY private key to the PKCS8 format.
+Status ExportPKeyPkcs8(EVP_PKEY* key, std::vector<uint8_t>* buffer);
+
// Creates a WebCrypto public key given an EVP_PKEY. This step includes
// exporting the key to SPKI format, for use by serialization later.
Status CreateWebCryptoPublicKey(bssl::UniquePtr<EVP_PKEY> public_key,
@@ -36,7 +42,7 @@ Status CreateWebCryptoPrivateKey(bssl::UniquePtr<EVP_PKEY> private_key,
// key may be invalid, and should be verified using something like
// RSA_check_key(). The only validation performed by this function is to ensure
// the key type matched |expected_pkey_id|.
-Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data,
+Status ImportUnverifiedPkeyFromSpki(base::span<const uint8_t> key_data,
int expected_pkey_id,
bssl::UniquePtr<EVP_PKEY>* pkey);
@@ -44,7 +50,7 @@ Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data,
// asymmetric key may be invalid, and should be verified using something like
// RSA_check_key(). The only validation performed by this function is to ensure
// the key type matched |expected_pkey_id|.
-Status ImportUnverifiedPkeyFromPkcs8(const CryptoData& key_data,
+Status ImportUnverifiedPkeyFromPkcs8(base::span<const uint8_t> key_data,
int expected_pkey_id,
bssl::UniquePtr<EVP_PKEY>* pkey);
diff --git a/chromium/components/webcrypto/algorithms/ec.cc b/chromium/components/webcrypto/algorithms/ec.cc
index 30b7b239e58..acd9a4f469a 100644
--- a/chromium/components/webcrypto/algorithms/ec.cc
+++ b/chromium/components/webcrypto/algorithms/ec.cc
@@ -8,10 +8,10 @@
#include <utility>
+#include "base/containers/span.h"
#include "components/webcrypto/algorithms/asymmetric_key_util.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
@@ -163,7 +163,7 @@ Status WritePaddedBIGNUM(const std::string& member_name,
std::vector<uint8_t> padded_bytes(padded_length);
if (!BN_bn2bin_padded(padded_bytes.data(), padded_bytes.size(), value))
return Status::OperationError();
- jwk->SetBytes(member_name, CryptoData(padded_bytes));
+ jwk->SetBytes(member_name, padded_bytes);
return Status::Success();
}
@@ -172,7 +172,7 @@ Status ReadPaddedBIGNUM(const JwkReader& jwk,
const std::string& member_name,
size_t expected_length,
bssl::UniquePtr<BIGNUM>* out) {
- std::string bytes;
+ std::vector<uint8_t> bytes;
Status status = jwk.GetBytes(member_name, &bytes);
if (status.IsError())
return status;
@@ -182,7 +182,7 @@ Status ReadPaddedBIGNUM(const JwkReader& jwk,
bytes.size());
}
- out->reset(CreateBIGNUM(bytes));
+ out->reset(BN_bin2bn(bytes.data(), bytes.size(), nullptr));
return Status::Success();
}
@@ -292,7 +292,7 @@ Status EcAlgorithm::GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
}
Status EcAlgorithm::ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -328,7 +328,7 @@ Status EcAlgorithm::ExportKey(blink::WebCryptoKeyFormat format,
}
}
-Status EcAlgorithm::ImportKeyRaw(const CryptoData& key_data,
+Status EcAlgorithm::ImportKeyRaw(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -354,7 +354,7 @@ Status EcAlgorithm::ImportKeyRaw(const CryptoData& key_data,
// Convert the "raw" input from X9.62 format to an EC_POINT.
if (!EC_POINT_oct2point(EC_KEY_get0_group(ec.get()), point.get(),
- key_data.bytes(), key_data.byte_length(), nullptr)) {
+ key_data.data(), key_data.size(), nullptr)) {
return Status::DataError();
}
@@ -380,7 +380,7 @@ Status EcAlgorithm::ImportKeyRaw(const CryptoData& key_data,
usages, key);
}
-Status EcAlgorithm::ImportKeyPkcs8(const CryptoData& key_data,
+Status EcAlgorithm::ImportKeyPkcs8(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -408,7 +408,7 @@ Status EcAlgorithm::ImportKeyPkcs8(const CryptoData& key_data,
extractable, usages, key);
}
-Status EcAlgorithm::ImportKeySpki(const CryptoData& key_data,
+Status EcAlgorithm::ImportKeySpki(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -438,7 +438,7 @@ Status EcAlgorithm::ImportKeySpki(const CryptoData& key_data,
// The format for JWK EC keys is given by:
// https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-36#section-6.2
-Status EcAlgorithm::ImportKeyJwk(const CryptoData& key_data,
+Status EcAlgorithm::ImportKeyJwk(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -578,22 +578,14 @@ Status EcAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key,
std::vector<uint8_t>* buffer) const {
if (key.GetType() != blink::kWebCryptoKeyTypePrivate)
return Status::ErrorUnexpectedKeyType();
- // This relies on the fact that PKCS8 formatted data was already
- // associated with the key during its creation (used by
- // structured clone).
- *buffer = GetSerializedKeyData(key);
- return Status::Success();
+ return ExportPKeyPkcs8(GetEVP_PKEY(key), buffer);
}
Status EcAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key,
std::vector<uint8_t>* buffer) const {
if (key.GetType() != blink::kWebCryptoKeyTypePublic)
return Status::ErrorUnexpectedKeyType();
- // This relies on the fact that SPKI formatted data was already
- // associated with the key during its creation (used by
- // structured clone).
- *buffer = GetSerializedKeyData(key);
- return Status::Success();
+ return ExportPKeySpki(GetEVP_PKEY(key), buffer);
}
// The format for JWK EC keys is given by:
@@ -653,7 +645,7 @@ Status EcAlgorithm::DeserializeKeyForClone(
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const {
if (algorithm.ParamsType() != blink::kWebCryptoKeyAlgorithmParamsTypeEc)
return Status::ErrorUnexpected();
diff --git a/chromium/components/webcrypto/algorithms/ec.h b/chromium/components/webcrypto/algorithms/ec.h
index 048bc50e5a5..03a508f301c 100644
--- a/chromium/components/webcrypto/algorithms/ec.h
+++ b/chromium/components/webcrypto/algorithms/ec.h
@@ -33,7 +33,7 @@ class EcAlgorithm : public AlgorithmImplementation {
GenerateKeyResult* result) const override;
Status ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -47,29 +47,29 @@ class EcAlgorithm : public AlgorithmImplementation {
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const override;
private:
- Status ImportKeyRaw(const CryptoData& key_data,
+ Status ImportKeyRaw(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) const;
- Status ImportKeyPkcs8(const CryptoData& key_data,
+ Status ImportKeyPkcs8(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) const;
- Status ImportKeySpki(const CryptoData& key_data,
+ Status ImportKeySpki(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) const;
- Status ImportKeyJwk(const CryptoData& key_data,
+ Status ImportKeyJwk(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
diff --git a/chromium/components/webcrypto/algorithms/ecdh.cc b/chromium/components/webcrypto/algorithms/ecdh.cc
index 208472f61a3..ec6cc742e26 100644
--- a/chromium/components/webcrypto/algorithms/ecdh.cc
+++ b/chromium/components/webcrypto/algorithms/ecdh.cc
@@ -7,11 +7,11 @@
#include <memory>
+#include "base/containers/span.h"
#include "components/webcrypto/algorithm_implementation.h"
#include "components/webcrypto/algorithms/ec.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
diff --git a/chromium/components/webcrypto/algorithms/ecdh_unittest.cc b/chromium/components/webcrypto/algorithms/ecdh_unittest.cc
index d9cd676eaaf..f281acc2138 100644
--- a/chromium/components/webcrypto/algorithms/ecdh_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/ecdh_unittest.cc
@@ -8,7 +8,6 @@
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/ec.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -48,10 +47,10 @@ bool ImportKeysFromTest(const base::DictionaryValue* test,
EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json));
blink::WebCryptoNamedCurve curve =
GetCurveNameFromDictionary(public_key_json);
- EXPECT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk,
- CryptoData(MakeJsonVector(*public_key_json)),
- CreateEcdhImportAlgorithm(curve), true, 0, public_key));
+ EXPECT_EQ(
+ Status::Success(),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, MakeJsonVector(*public_key_json),
+ CreateEcdhImportAlgorithm(curve), true, 0, public_key));
// If the test didn't specify an error for private key import, that implies
// it expects success.
@@ -63,8 +62,7 @@ bool ImportKeysFromTest(const base::DictionaryValue* test,
EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json));
curve = GetCurveNameFromDictionary(private_key_json);
Status status = ImportKey(
- blink::kWebCryptoKeyFormatJwk,
- CryptoData(MakeJsonVector(*private_key_json)),
+ blink::kWebCryptoKeyFormatJwk, MakeJsonVector(*private_key_json),
CreateEcdhImportAlgorithm(curve), true,
blink::kWebCryptoKeyUsageDeriveBits | blink::kWebCryptoKeyUsageDeriveKey,
private_key);
@@ -108,7 +106,7 @@ TEST_F(WebCryptoEcdhTest, DeriveBitsKnownAnswer) {
std::vector<uint8_t> expected_bytes =
GetBytesFromHexString(test, "derived_bytes");
- EXPECT_EQ(CryptoData(expected_bytes), CryptoData(derived_bytes));
+ EXPECT_EQ(expected_bytes, derived_bytes);
}
}
@@ -313,10 +311,10 @@ TEST_F(WebCryptoEcdhTest, ImportKeyEmptyUsage) {
EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json));
blink::WebCryptoNamedCurve curve =
GetCurveNameFromDictionary(public_key_json);
- ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk,
- CryptoData(MakeJsonVector(*public_key_json)),
- CreateEcdhImportAlgorithm(curve), true, 0, &key));
+ ASSERT_EQ(
+ Status::Success(),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, MakeJsonVector(*public_key_json),
+ CreateEcdhImportAlgorithm(curve), true, 0, &key));
EXPECT_EQ(0, key.Usages());
// Import the private key.
@@ -325,7 +323,7 @@ TEST_F(WebCryptoEcdhTest, ImportKeyEmptyUsage) {
curve = GetCurveNameFromDictionary(private_key_json);
ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
ImportKey(blink::kWebCryptoKeyFormatJwk,
- CryptoData(MakeJsonVector(*private_key_json)),
+ MakeJsonVector(*private_key_json),
CreateEcdhImportAlgorithm(curve), true, 0, &key));
}
diff --git a/chromium/components/webcrypto/algorithms/ecdsa.cc b/chromium/components/webcrypto/algorithms/ecdsa.cc
index 00a83c342b5..5a7ecd99c1e 100644
--- a/chromium/components/webcrypto/algorithms/ecdsa.cc
+++ b/chromium/components/webcrypto/algorithms/ecdsa.cc
@@ -7,11 +7,11 @@
#include <memory>
+#include "base/containers/span.h"
#include "components/webcrypto/algorithm_implementation.h"
#include "components/webcrypto/algorithms/ec.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
@@ -109,7 +109,7 @@ Status ConvertDerSignatureToWebCryptoSignature(
// ECDSA-Sig-Value.
Status ConvertWebCryptoSignatureToDerSignature(
EVP_PKEY* key,
- const CryptoData& signature,
+ base::span<const uint8_t> signature,
std::vector<uint8_t>* der_signature,
bool* incorrect_length) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
@@ -124,10 +124,12 @@ Status ConvertWebCryptoSignatureToDerSignature(
// is returned here rather than an error, so that the caller can fail
// verification with a boolean, rather than reject the promise with an
// exception.
- if (signature.byte_length() != 2 * order_size_bytes) {
+ if (signature.size() != 2 * order_size_bytes) {
*incorrect_length = true;
return Status::Success();
}
+ base::span<const uint8_t> r_bytes = signature.first(order_size_bytes);
+ base::span<const uint8_t> s_bytes = signature.subspan(order_size_bytes);
*incorrect_length = false;
@@ -136,9 +138,8 @@ Status ConvertWebCryptoSignatureToDerSignature(
if (!ecdsa_sig)
return Status::OperationError();
- if (!BN_bin2bn(signature.bytes(), order_size_bytes, ecdsa_sig->r) ||
- !BN_bin2bn(signature.bytes() + order_size_bytes, order_size_bytes,
- ecdsa_sig->s)) {
+ if (!BN_bin2bn(r_bytes.data(), r_bytes.size(), ecdsa_sig->r) ||
+ !BN_bin2bn(s_bytes.data(), s_bytes.size(), ecdsa_sig->s)) {
return Status::ErrorUnexpected();
}
@@ -176,7 +177,7 @@ class EcdsaImplementation : public EcAlgorithm {
Status Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
if (key.GetType() != blink::kWebCryptoKeyTypePrivate)
return Status::ErrorUnexpectedKeyType();
@@ -195,7 +196,7 @@ class EcdsaImplementation : public EcAlgorithm {
bssl::ScopedEVP_MD_CTX ctx;
size_t sig_len = 0;
if (!EVP_DigestSignInit(ctx.get(), nullptr, digest, nullptr, private_key) ||
- !EVP_DigestSignUpdate(ctx.get(), data.bytes(), data.byte_length()) ||
+ !EVP_DigestSignUpdate(ctx.get(), data.data(), data.size()) ||
!EVP_DigestSignFinal(ctx.get(), nullptr, &sig_len)) {
return Status::OperationError();
}
@@ -213,8 +214,8 @@ class EcdsaImplementation : public EcAlgorithm {
Status Verify(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match) const override {
if (key.GetType() != blink::kWebCryptoKeyTypePublic)
return Status::ErrorUnexpectedKeyType();
@@ -242,7 +243,7 @@ class EcdsaImplementation : public EcAlgorithm {
bssl::ScopedEVP_MD_CTX ctx;
if (!EVP_DigestVerifyInit(ctx.get(), nullptr, digest, nullptr,
public_key) ||
- !EVP_DigestVerifyUpdate(ctx.get(), data.bytes(), data.byte_length())) {
+ !EVP_DigestVerifyUpdate(ctx.get(), data.data(), data.size())) {
return Status::OperationError();
}
diff --git a/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc b/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc
index 3503a9adaff..5390ab47eff 100644
--- a/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc
@@ -5,9 +5,9 @@
#include <stddef.h>
#include <stdint.h>
+#include "base/containers/span.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -133,22 +133,20 @@ TEST_F(WebCryptoEcdsaTest, SignatureIsRandom) {
std::vector<uint8_t> signature1;
std::vector<uint8_t> signature2;
ASSERT_EQ(Status::Success(),
- Sign(algorithm, private_key, CryptoData(message), &signature1));
+ Sign(algorithm, private_key, message, &signature1));
ASSERT_EQ(Status::Success(),
- Sign(algorithm, private_key, CryptoData(message), &signature2));
+ Sign(algorithm, private_key, message, &signature2));
// The two signatures should be different.
- EXPECT_NE(CryptoData(signature1), CryptoData(signature2));
+ EXPECT_NE(signature1, signature2);
// And both should be valid signatures which can be verified.
bool signature_matches;
- ASSERT_EQ(Status::Success(),
- Verify(algorithm, public_key, CryptoData(signature1),
- CryptoData(message), &signature_matches));
+ ASSERT_EQ(Status::Success(), Verify(algorithm, public_key, signature1,
+ message, &signature_matches));
EXPECT_TRUE(signature_matches);
- ASSERT_EQ(Status::Success(),
- Verify(algorithm, public_key, CryptoData(signature2),
- CryptoData(message), &signature_matches));
+ ASSERT_EQ(Status::Success(), Verify(algorithm, public_key, signature2,
+ message, &signature_matches));
EXPECT_TRUE(signature_matches);
}
@@ -174,9 +172,9 @@ TEST_F(WebCryptoEcdsaTest, VerifyKnownAnswer) {
// Import the public key.
blink::WebCryptoKey key;
- Status status = ImportKey(key_format, CryptoData(key_data),
- CreateEcdsaImportAlgorithm(curve), true,
- blink::kWebCryptoKeyUsageVerify, &key);
+ Status status =
+ ImportKey(key_format, key_data, CreateEcdsaImportAlgorithm(curve), true,
+ blink::kWebCryptoKeyUsageVerify, &key);
ASSERT_EQ(expected_error, StatusToString(status));
if (status.IsError())
continue;
@@ -192,8 +190,8 @@ TEST_F(WebCryptoEcdsaTest, VerifyKnownAnswer) {
blink::WebCryptoAlgorithm hash = GetDigestAlgorithm(test, "hash");
bool verify_result;
- status = Verify(CreateEcdsaAlgorithm(hash.Id()), key, CryptoData(signature),
- CryptoData(message), &verify_result);
+ status = Verify(CreateEcdsaAlgorithm(hash.Id()), key, signature, message,
+ &verify_result);
ASSERT_EQ(expected_error, StatusToString(status));
if (status.IsError())
continue;
@@ -252,9 +250,9 @@ TEST_F(WebCryptoEcdsaTest, ImportBadKeys) {
ASSERT_TRUE(test->GetString("error", &expected_error));
blink::WebCryptoKey key;
- Status status = ImportKey(
- key_format, CryptoData(key_data), CreateEcdsaImportAlgorithm(curve),
- true, GetExpectedUsagesForKeyImport(key_format, test), &key);
+ Status status =
+ ImportKey(key_format, key_data, CreateEcdsaImportAlgorithm(curve), true,
+ GetExpectedUsagesForKeyImport(key_format, test), &key);
ASSERT_EQ(expected_error, StatusToString(status));
}
}
@@ -287,7 +285,7 @@ TEST_F(WebCryptoEcdsaTest, ImportExportPrivateKey) {
// Import the key using JWK
blink::WebCryptoKey key;
ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(jwk_bytes),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, jwk_bytes,
CreateEcdsaImportAlgorithm(curve), true,
blink::kWebCryptoKeyUsageSign, &key));
@@ -304,19 +302,19 @@ TEST_F(WebCryptoEcdsaTest, ImportExportPrivateKey) {
// expectation.
jwk_bytes = exported_bytes;
ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(jwk_bytes),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, jwk_bytes,
CreateEcdsaImportAlgorithm(curve), true,
blink::kWebCryptoKeyUsageSign, &key));
// Export the key as JWK (again)
ASSERT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatJwk, key, &exported_bytes));
- EXPECT_EQ(CryptoData(jwk_bytes), CryptoData(exported_bytes));
+ EXPECT_EQ(jwk_bytes, exported_bytes);
// Export the key as PKCS8
ASSERT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatPkcs8, key, &exported_bytes));
- EXPECT_EQ(CryptoData(pkcs8_bytes), CryptoData(exported_bytes));
+ EXPECT_EQ(pkcs8_bytes, exported_bytes);
// -------------------------------------------------
// Test from PKCS8, and then export to {JWK, PKCS8}
@@ -327,8 +325,8 @@ TEST_F(WebCryptoEcdsaTest, ImportExportPrivateKey) {
// during export).
std::vector<uint8_t> pkcs8_input_bytes = GetBytesFromHexString(
test, test->FindKey("original_pkcs8") ? "original_pkcs8" : "pkcs8");
- CryptoData pkcs8_input_data(pkcs8_input_bytes.empty() ? pkcs8_bytes
- : pkcs8_input_bytes);
+ base::span<const uint8_t> pkcs8_input_data(
+ pkcs8_input_bytes.empty() ? pkcs8_bytes : pkcs8_input_bytes);
// Import the key using PKCS8
ASSERT_EQ(Status::Success(),
@@ -339,12 +337,12 @@ TEST_F(WebCryptoEcdsaTest, ImportExportPrivateKey) {
// Export the key as PKCS8
ASSERT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatPkcs8, key, &exported_bytes));
- EXPECT_EQ(CryptoData(pkcs8_bytes), CryptoData(exported_bytes));
+ EXPECT_EQ(pkcs8_bytes, exported_bytes);
// Export the key as JWK
ASSERT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatJwk, key, &exported_bytes));
- EXPECT_EQ(CryptoData(jwk_bytes), CryptoData(exported_bytes));
+ EXPECT_EQ(jwk_bytes, exported_bytes);
}
}
diff --git a/chromium/components/webcrypto/algorithms/ed25519.cc b/chromium/components/webcrypto/algorithms/ed25519.cc
deleted file mode 100644
index 153b8531d68..00000000000
--- a/chromium/components/webcrypto/algorithms/ed25519.cc
+++ /dev/null
@@ -1,44 +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 "components/webcrypto/algorithm_implementation.h"
-#include "components/webcrypto/status.h"
-
-namespace webcrypto {
-
-namespace {
-
-// This class implements the Ed25519 algorithm, which is a particular parameter
-// choice for the EdDSA algorithm specified by RFC 8032. The underlying curve
-// used by Ed25519 is equivalent to Curve25519 specified by RFC 7748.
-//
-// TODO(crbug.com/1032821): See also
-// https://chromestatus.com/feature/4913922408710144.
-class Ed25519Implementation : public AlgorithmImplementation {
- public:
- Status Sign(const blink::WebCryptoAlgorithm& algorithm,
- const blink::WebCryptoKey& key,
- const CryptoData& data,
- std::vector<uint8_t>* buffer) const override {
- // TODO(crbug.com/1032821): Implement this.
- return Status::ErrorUnsupported();
- }
-
- Status Verify(const blink::WebCryptoAlgorithm& algorithm,
- const blink::WebCryptoKey& key,
- const CryptoData& signature,
- const CryptoData& data,
- bool* signature_match) const override {
- // TODO(crbug.com/1032821): Implement this.
- return Status::ErrorUnsupported();
- }
-};
-
-} // namespace
-
-std::unique_ptr<AlgorithmImplementation> CreateEd25519Implementation() {
- return std::make_unique<Ed25519Implementation>();
-}
-
-} // namespace webcrypto
diff --git a/chromium/components/webcrypto/algorithms/hkdf.cc b/chromium/components/webcrypto/algorithms/hkdf.cc
index abb6b9813ba..179ad3926a6 100644
--- a/chromium/components/webcrypto/algorithms/hkdf.cc
+++ b/chromium/components/webcrypto/algorithms/hkdf.cc
@@ -10,7 +10,6 @@
#include "components/webcrypto/algorithms/secret_key_util.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -30,7 +29,7 @@ class HkdfImplementation : public AlgorithmImplementation {
HkdfImplementation() {}
Status ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -43,7 +42,7 @@ class HkdfImplementation : public AlgorithmImplementation {
}
}
- Status ImportKeyRaw(const CryptoData& key_data,
+ Status ImportKeyRaw(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -88,8 +87,8 @@ class HkdfImplementation : public AlgorithmImplementation {
// |algorithm|.
const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(base_key);
if (!HKDF(derived_bytes->data(), derived_bytes_len, digest_algorithm,
- raw_key.data(), raw_key.size(), params->Salt().Data(),
- params->Salt().size(), params->Info().Data(),
+ raw_key.data(), raw_key.size(), params->Salt().data(),
+ params->Salt().size(), params->Info().data(),
params->Info().size())) {
uint32_t error = ERR_get_error();
if (ERR_GET_LIB(error) == ERR_LIB_HKDF &&
@@ -106,7 +105,7 @@ class HkdfImplementation : public AlgorithmImplementation {
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const override {
if (algorithm.ParamsType() != blink::kWebCryptoKeyAlgorithmParamsTypeNone ||
type != blink::kWebCryptoKeyTypeSecret)
diff --git a/chromium/components/webcrypto/algorithms/hmac.cc b/chromium/components/webcrypto/algorithms/hmac.cc
index 0049cdcee9b..1d9d9c0b9d3 100644
--- a/chromium/components/webcrypto/algorithms/hmac.cc
+++ b/chromium/components/webcrypto/algorithms/hmac.cc
@@ -13,7 +13,6 @@
#include "components/webcrypto/algorithms/secret_key_util.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
@@ -87,7 +86,7 @@ const blink::WebCryptoKeyUsageMask kAllKeyUsages =
Status SignHmac(const std::vector<uint8_t>& raw_key,
const blink::WebCryptoAlgorithm& hash,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
@@ -99,8 +98,8 @@ Status SignHmac(const std::vector<uint8_t>& raw_key,
buffer->resize(hmac_expected_length);
unsigned int hmac_actual_length;
- if (!HMAC(digest_algorithm, raw_key.data(), raw_key.size(), data.bytes(),
- data.byte_length(), buffer->data(), &hmac_actual_length)) {
+ if (!HMAC(digest_algorithm, raw_key.data(), raw_key.size(), data.data(),
+ data.size(), buffer->data(), &hmac_actual_length)) {
return Status::OperationError();
}
@@ -144,7 +143,7 @@ class HmacImplementation : public AlgorithmImplementation {
}
Status ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -172,7 +171,7 @@ class HmacImplementation : public AlgorithmImplementation {
}
}
- Status ImportKeyRaw(const CryptoData& key_data,
+ Status ImportKeyRaw(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -185,8 +184,7 @@ class HmacImplementation : public AlgorithmImplementation {
algorithm.HmacImportParams();
unsigned int keylen_bits = 0;
- status = GetHmacImportKeyLengthBits(params, key_data.byte_length(),
- &keylen_bits);
+ status = GetHmacImportKeyLengthBits(params, key_data.size(), &keylen_bits);
if (status.IsError())
return status;
@@ -201,14 +199,14 @@ class HmacImplementation : public AlgorithmImplementation {
}
// Otherwise zero out the unused bits in the key data before importing.
- std::vector<uint8_t> modified_key_data(
- key_data.bytes(), key_data.bytes() + key_data.byte_length());
+ std::vector<uint8_t> modified_key_data(key_data.data(),
+ key_data.data() + key_data.size());
TruncateToBitLength(keylen_bits, &modified_key_data);
- return CreateWebCryptoSecretKey(CryptoData(modified_key_data),
- key_algorithm, extractable, usages, key);
+ return CreateWebCryptoSecretKey(modified_key_data, key_algorithm,
+ extractable, usages, key);
}
- Status ImportKeyJwk(const CryptoData& key_data,
+ Status ImportKeyJwk(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -232,8 +230,7 @@ class HmacImplementation : public AlgorithmImplementation {
if (status.IsError())
return status;
- return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages,
- key);
+ return ImportKeyRaw(raw_data, algorithm, extractable, usages, key);
}
Status ExportKeyRaw(const blink::WebCryptoKey& key,
@@ -251,15 +248,15 @@ class HmacImplementation : public AlgorithmImplementation {
if (!algorithm_name)
return Status::ErrorUnexpected();
- WriteSecretKeyJwk(CryptoData(raw_data), algorithm_name, key.Extractable(),
- key.Usages(), buffer);
+ WriteSecretKeyJwk(raw_data, algorithm_name, key.Extractable(), key.Usages(),
+ buffer);
return Status::Success();
}
Status Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
const blink::WebCryptoAlgorithm& hash =
key.Algorithm().HmacParams()->GetHash();
@@ -269,8 +266,8 @@ class HmacImplementation : public AlgorithmImplementation {
Status Verify(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match) const override {
std::vector<uint8_t> result;
Status status = Sign(algorithm, key, data, &result);
@@ -279,9 +276,9 @@ class HmacImplementation : public AlgorithmImplementation {
return status;
// Do not allow verification of truncated MACs.
- *signature_match = result.size() == signature.byte_length() &&
- crypto::SecureMemEqual(result.data(), signature.bytes(),
- signature.byte_length());
+ *signature_match = result.size() == signature.size() &&
+ crypto::SecureMemEqual(result.data(), signature.data(),
+ signature.size());
return Status::Success();
}
@@ -290,7 +287,7 @@ class HmacImplementation : public AlgorithmImplementation {
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const override {
if (algorithm.ParamsType() != blink::kWebCryptoKeyAlgorithmParamsTypeHmac ||
type != blink::kWebCryptoKeyTypeSecret)
diff --git a/chromium/components/webcrypto/algorithms/hmac_unittest.cc b/chromium/components/webcrypto/algorithms/hmac_unittest.cc
index 02eeae592a1..227df589db2 100644
--- a/chromium/components/webcrypto/algorithms/hmac_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/hmac_unittest.cc
@@ -12,10 +12,10 @@
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
+#include "base/containers/span.h"
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -154,7 +154,7 @@ std::vector<uint8_t> HmacSign(blink::WebCryptoKey key,
const std::vector<uint8_t>& message) {
std::vector<uint8_t> output;
auto status = Sign(CreateAlgorithm(blink::kWebCryptoAlgorithmIdHmac), key,
- CryptoData(message), &output);
+ message, &output);
CHECK(status == Status::Success());
return output;
}
@@ -164,7 +164,7 @@ bool HmacVerify(blink::WebCryptoKey key,
const std::vector<uint8_t>& hmac) {
bool match = false;
auto status = Verify(CreateAlgorithm(blink::kWebCryptoAlgorithmIdHmac), key,
- CryptoData(hmac), CryptoData(message), &match);
+ hmac, message, &match);
CHECK(status == Status::Success());
return match;
}
@@ -248,23 +248,21 @@ TEST_F(WebCryptoHmacTest, GenerateKeyEmptyUsage) {
TEST_F(WebCryptoHmacTest, ImportKeyEmptyUsage) {
blink::WebCryptoKey key;
std::string key_raw_hex_in = "025a8cf3f08b4f6c5f33bbc76a471939";
- EXPECT_EQ(Status::ErrorCreateKeyEmptyUsages(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(HexStringToBytes(key_raw_hex_in)),
- CreateHmacImportAlgorithmNoLength(
- blink::kWebCryptoAlgorithmIdSha1),
- true, 0, &key));
+ EXPECT_EQ(
+ Status::ErrorCreateKeyEmptyUsages(),
+ ImportKey(
+ blink::kWebCryptoKeyFormatRaw, HexStringToBytes(key_raw_hex_in),
+ CreateHmacImportAlgorithmNoLength(blink::kWebCryptoAlgorithmIdSha1),
+ true, 0, &key));
}
TEST_F(WebCryptoHmacTest, ImportKeyJwkKeyOpsSignVerify) {
blink::WebCryptoKey key;
- base::DictionaryValue dict;
- dict.SetString("kty", "oct");
- dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
- base::Value* key_ops =
- dict.SetKey("key_ops", base::Value(base::Value::Type::LIST));
-
- key_ops->Append("sign");
+ base::Value::Dict dict;
+ dict.Set("kty", "oct");
+ dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
+ dict.Set("key_ops", base::Value::List());
+ dict.FindList("key_ops")->Append("sign");
EXPECT_EQ(Status::Success(),
ImportKeyJwkFromDict(dict,
@@ -274,7 +272,7 @@ TEST_F(WebCryptoHmacTest, ImportKeyJwkKeyOpsSignVerify) {
EXPECT_EQ(blink::kWebCryptoKeyUsageSign, key.Usages());
- key_ops->Append("verify");
+ dict.FindList("key_ops")->Append("verify");
EXPECT_EQ(Status::Success(),
ImportKeyJwkFromDict(dict,
@@ -288,17 +286,17 @@ TEST_F(WebCryptoHmacTest, ImportKeyJwkKeyOpsSignVerify) {
// Test 'use' inconsistent with 'key_ops'.
TEST_F(WebCryptoHmacTest, ImportKeyJwkUseInconsisteWithKeyOps) {
blink::WebCryptoKey key;
- base::DictionaryValue dict;
- dict.SetString("kty", "oct");
- dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
- dict.SetString("alg", "HS256");
- dict.SetString("use", "sig");
+ base::Value::Dict dict;
+ dict.Set("kty", "oct");
+ dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
+ dict.Set("alg", "HS256");
+ dict.Set("use", "sig");
- base::ListValue key_ops;
+ base::Value::List key_ops;
key_ops.Append("sign");
key_ops.Append("verify");
key_ops.Append("encrypt");
- dict.SetKey("key_ops", std::move(key_ops));
+ dict.Set("key_ops", std::move(key_ops));
EXPECT_EQ(
Status::ErrorJwkUseAndKeyopsInconsistent(),
ImportKeyJwkFromDict(
@@ -312,11 +310,11 @@ TEST_F(WebCryptoHmacTest, ImportKeyJwkUseInconsisteWithKeyOps) {
// Test JWK composite 'sig' use
TEST_F(WebCryptoHmacTest, ImportKeyJwkUseSig) {
blink::WebCryptoKey key;
- base::DictionaryValue dict;
- dict.SetString("kty", "oct");
- dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
+ base::Value::Dict dict;
+ dict.Set("kty", "oct");
+ dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
+ dict.Set("use", "sig");
- dict.SetString("use", "sig");
EXPECT_EQ(
Status::Success(),
ImportKeyJwkFromDict(
@@ -340,13 +338,13 @@ TEST_F(WebCryptoHmacTest, ImportJwkInputConsistency) {
blink::WebCryptoAlgorithm algorithm =
CreateHmacImportAlgorithmNoLength(blink::kWebCryptoAlgorithmIdSha256);
blink::WebCryptoKeyUsageMask usages = blink::kWebCryptoKeyUsageVerify;
- base::DictionaryValue dict;
- dict.SetString("kty", "oct");
- dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
+ base::Value::Dict dict;
+ dict.Set("kty", "oct");
+ dict.Set("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
std::vector<uint8_t> json_vec = MakeJsonVector(dict);
EXPECT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json_vec),
- algorithm, extractable, usages, &key));
+ ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec, algorithm,
+ extractable, usages, &key));
EXPECT_TRUE(key.Handle());
EXPECT_EQ(blink::kWebCryptoKeyTypeSecret, key.GetType());
EXPECT_EQ(extractable, key.Extractable());
@@ -360,16 +358,16 @@ TEST_F(WebCryptoHmacTest, ImportJwkInputConsistency) {
// Consistency rules when JWK value exists: Fail if inconsistency is found.
// Pass: All input values are consistent with the JWK values.
- dict.DictClear();
- dict.SetString("kty", "oct");
- dict.SetString("alg", "HS256");
- dict.SetString("use", "sig");
- dict.SetBoolean("ext", false);
- dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
+ dict.clear();
+ dict.Set("kty", "oct");
+ dict.Set("alg", "HS256");
+ dict.Set("use", "sig");
+ dict.Set("ext", false);
+ dict.Set("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
json_vec = MakeJsonVector(dict);
EXPECT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json_vec),
- algorithm, extractable, usages, &key));
+ ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec, algorithm,
+ extractable, usages, &key));
// Extractable cases:
// 1. input=T, JWK=F ==> fail (inconsistent)
@@ -377,69 +375,67 @@ TEST_F(WebCryptoHmacTest, ImportJwkInputConsistency) {
// 2. input=T, JWK=T ==> pass, result extractable is T
// 3. input=F, JWK=T ==> pass, result extractable is F
EXPECT_EQ(Status::ErrorJwkExtInconsistent(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json_vec),
- algorithm, true, usages, &key));
+ ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec, algorithm, true,
+ usages, &key));
EXPECT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json_vec),
- algorithm, false, usages, &key));
+ ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec, algorithm, false,
+ usages, &key));
EXPECT_FALSE(key.Extractable());
- dict.SetBoolean("ext", true);
+ dict.Set("ext", true);
EXPECT_EQ(Status::Success(),
ImportKeyJwkFromDict(dict, algorithm, true, usages, &key));
EXPECT_TRUE(key.Extractable());
EXPECT_EQ(Status::Success(),
ImportKeyJwkFromDict(dict, algorithm, false, usages, &key));
EXPECT_FALSE(key.Extractable());
- dict.SetBoolean("ext", true); // restore previous value
// Fail: Input algorithm (AES-CBC) is inconsistent with JWK value
// (HMAC SHA256).
- dict.DictClear();
- dict.SetString("kty", "oct");
- dict.SetString("alg", "HS256");
- dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
+ dict.clear();
+ dict.Set("kty", "oct");
+ dict.Set("alg", "HS256");
+ dict.Set("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
EXPECT_EQ(Status::ErrorJwkAlgorithmInconsistent(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
extractable, blink::kWebCryptoKeyUsageEncrypt, &key));
// Fail: Input usage (encrypt) is inconsistent with JWK value (use=sig).
EXPECT_EQ(Status::ErrorJwkUseInconsistent(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json_vec),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec,
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
extractable, blink::kWebCryptoKeyUsageEncrypt, &key));
// Fail: Input algorithm (HMAC SHA1) is inconsistent with JWK value
// (HMAC SHA256).
EXPECT_EQ(Status::ErrorJwkAlgorithmInconsistent(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json_vec),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec,
CreateHmacImportAlgorithmNoLength(
blink::kWebCryptoAlgorithmIdSha1),
extractable, usages, &key));
// Pass: JWK alg missing but input algorithm specified: use input value
- dict.RemoveKey("alg");
+ dict.Remove("alg");
EXPECT_EQ(Status::Success(),
ImportKeyJwkFromDict(dict,
CreateHmacImportAlgorithmNoLength(
blink::kWebCryptoAlgorithmIdSha256),
extractable, usages, &key));
EXPECT_EQ(blink::kWebCryptoAlgorithmIdHmac, algorithm.Id());
- dict.SetString("alg", "HS256");
+ dict.Set("alg", "HS256");
// Fail: Input usages (encrypt) is not a subset of the JWK value
// (sign|verify). Moreover "encrypt" is not a valid usage for HMAC.
- EXPECT_EQ(
- Status::ErrorCreateKeyBadUsages(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json_vec), algorithm,
- extractable, blink::kWebCryptoKeyUsageEncrypt, &key));
+ EXPECT_EQ(Status::ErrorCreateKeyBadUsages(),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec, algorithm,
+ extractable, blink::kWebCryptoKeyUsageEncrypt, &key));
// Fail: Input usages (encrypt|sign|verify) is not a subset of the JWK
// value (sign|verify). Moreover "encrypt" is not a valid usage for HMAC.
usages = blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageSign |
blink::kWebCryptoKeyUsageVerify;
EXPECT_EQ(Status::ErrorCreateKeyBadUsages(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json_vec),
- algorithm, extractable, usages, &key));
+ ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec, algorithm,
+ extractable, usages, &key));
// TODO(padolph): kty vs alg consistency tests: Depending on the kty value,
// only certain alg values are permitted. For example, when kty = "RSA" alg
@@ -462,12 +458,12 @@ TEST_F(WebCryptoHmacTest, ImportJwkHappy) {
// Import a symmetric key JWK and HMAC-SHA256 sign()
// Uses the first SHA256 test vector from the HMAC sample set above.
- base::DictionaryValue dict;
- dict.SetString("kty", "oct");
- dict.SetString("alg", "HS256");
- dict.SetString("use", "sig");
- dict.SetBoolean("ext", false);
- dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
+ base::Value::Dict dict;
+ dict.Set("kty", "oct");
+ dict.Set("alg", "HS256");
+ dict.Set("use", "sig");
+ dict.Set("ext", false);
+ dict.Set("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
ASSERT_EQ(Status::Success(),
ImportKeyJwkFromDict(dict, algorithm, extractable, usages, &key));
@@ -485,7 +481,7 @@ TEST_F(WebCryptoHmacTest, ImportJwkHappy) {
ASSERT_EQ(Status::Success(),
Sign(CreateAlgorithm(blink::kWebCryptoAlgorithmIdHmac), key,
- CryptoData(message_raw), &output));
+ message_raw, &output));
const std::string mac_raw =
"769f00d3e6a6cc1fb426a14a4f76c6462e6149726e0dee0ec0cf97a16605ac8b";
@@ -523,7 +519,7 @@ TEST_F(WebCryptoHmacTest, ExportJwkEmptyKey) {
ASSERT_TRUE(DeserializeKeyForClone(blink::WebCryptoKeyAlgorithm::CreateHmac(
blink::kWebCryptoAlgorithmIdSha1, 0),
blink::kWebCryptoKeyTypeSecret, true,
- usages, CryptoData(), &key));
+ usages, {}, &key));
// Export the key in JWK format and validate.
std::vector<uint8_t> json;
@@ -534,7 +530,7 @@ TEST_F(WebCryptoHmacTest, ExportJwkEmptyKey) {
// Now try re-importing the JWK key.
key = blink::WebCryptoKey::CreateNull();
EXPECT_EQ(Status::ErrorHmacImportEmptyKey(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, json,
CreateHmacImportAlgorithmNoLength(
blink::kWebCryptoAlgorithmIdSha1),
true, usages, &key));
@@ -549,8 +545,8 @@ TEST_F(WebCryptoHmacTest, ImportRawEmptyKey) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::ErrorHmacImportEmptyKey(),
- ImportKey(blink::kWebCryptoKeyFormatRaw, CryptoData(),
- import_algorithm, true, usages, &key));
+ ImportKey(blink::kWebCryptoKeyFormatRaw, {}, import_algorithm, true,
+ usages, &key));
}
// Imports an HMAC key contaning 1 byte data, however the length was set to 0.
@@ -563,18 +559,20 @@ TEST_F(WebCryptoHmacTest, ImportRawKeyWithZeroLength) {
std::vector<uint8_t> key_data(1);
ASSERT_EQ(Status::ErrorHmacImportBadLength(),
- ImportKey(blink::kWebCryptoKeyFormatRaw, CryptoData(key_data),
- import_algorithm, true, usages, &key));
+ ImportKey(blink::kWebCryptoKeyFormatRaw, key_data, import_algorithm,
+ true, usages, &key));
}
-// Import a huge hmac key (UINT_MAX bytes). This will fail before actually
-// reading the bytes, as the key is too large.
+// Import a huge hmac key (UINT_MAX bytes).
TEST_F(WebCryptoHmacTest, ImportRawKeyTooLarge) {
- CryptoData big_data(nullptr, UINT_MAX); // Invalid data of big length.
+ // Invalid data of big length. This span is invalid, but ImportKey should fail
+ // before actually reading the bytes, as the key is too large.
+ base::span<const uint8_t> big_data(static_cast<const uint8_t*>(nullptr),
+ UINT_MAX);
blink::WebCryptoKey key;
EXPECT_EQ(Status::ErrorDataTooLarge(),
- ImportKey(blink::kWebCryptoKeyFormatRaw, CryptoData(big_data),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, big_data,
CreateHmacImportAlgorithmNoLength(
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageSign, &key));
@@ -584,8 +582,7 @@ TEST_F(WebCryptoHmacTest, ImportRawKeyTooLarge) {
TEST_F(WebCryptoHmacTest, ImportRawKeyLengthTooLarge) {
blink::WebCryptoKey key;
EXPECT_EQ(Status::ErrorHmacImportBadLength(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(std::vector<uint8_t>(15)),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, std::vector<uint8_t>(15),
CreateHmacImportAlgorithmWithLength(
blink::kWebCryptoAlgorithmIdSha1, 128),
true, blink::kWebCryptoKeyUsageSign, &key));
@@ -595,8 +592,7 @@ TEST_F(WebCryptoHmacTest, ImportRawKeyLengthTooLarge) {
TEST_F(WebCryptoHmacTest, ImportRawKeyLengthTooSmall) {
blink::WebCryptoKey key;
EXPECT_EQ(Status::ErrorHmacImportBadLength(),
- ImportKey(blink::kWebCryptoKeyFormatRaw,
- CryptoData(std::vector<uint8_t>(16)),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, std::vector<uint8_t>(16),
CreateHmacImportAlgorithmWithLength(
blink::kWebCryptoAlgorithmIdSha1, 120),
true, blink::kWebCryptoKeyUsageSign, &key));
@@ -609,7 +605,7 @@ TEST_F(WebCryptoHmacTest, ImportRawKeyTruncation) {
blink::WebCryptoKey key;
EXPECT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatRaw, CryptoData(data),
+ ImportKey(blink::kWebCryptoKeyFormatRaw, data,
CreateHmacImportAlgorithmWithLength(
blink::kWebCryptoAlgorithmIdSha1, 12),
true, blink::kWebCryptoKeyUsageSign, &key));
@@ -623,9 +619,9 @@ TEST_F(WebCryptoHmacTest, ImportRawKeyTruncation) {
// The same test as above, but using the JWK format.
TEST_F(WebCryptoHmacTest, ImportJwkKeyTruncation) {
- base::DictionaryValue dict;
- dict.SetString("kty", "oct");
- dict.SetString("k", "sf8"); // 0xB1FF
+ base::Value::Dict dict;
+ dict.Set("kty", "oct");
+ dict.Set("k", "sf8"); // 0xB1FF
blink::WebCryptoKey key;
EXPECT_EQ(Status::Success(),
diff --git a/chromium/components/webcrypto/algorithms/pbkdf2.cc b/chromium/components/webcrypto/algorithms/pbkdf2.cc
index 41f5d889af5..2e05d5904fe 100644
--- a/chromium/components/webcrypto/algorithms/pbkdf2.cc
+++ b/chromium/components/webcrypto/algorithms/pbkdf2.cc
@@ -6,11 +6,11 @@
#include <memory>
+#include "base/containers/span.h"
#include "components/webcrypto/algorithm_implementation.h"
#include "components/webcrypto/algorithms/secret_key_util.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -29,7 +29,7 @@ class Pbkdf2Implementation : public AlgorithmImplementation {
Pbkdf2Implementation() {}
Status ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -42,7 +42,7 @@ class Pbkdf2Implementation : public AlgorithmImplementation {
}
}
- Status ImportKeyRaw(const CryptoData& key_data,
+ Status ImportKeyRaw(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -97,7 +97,7 @@ class Pbkdf2Implementation : public AlgorithmImplementation {
if (!PKCS5_PBKDF2_HMAC(
reinterpret_cast<const char*>(password.data()), password.size(),
- params->Salt().Data(), params->Salt().size(), params->Iterations(),
+ params->Salt().data(), params->Salt().size(), params->Iterations(),
digest_algorithm, keylen_bytes, derived_bytes->data())) {
return Status::OperationError();
}
@@ -108,7 +108,7 @@ class Pbkdf2Implementation : public AlgorithmImplementation {
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const override {
if (algorithm.ParamsType() != blink::kWebCryptoKeyAlgorithmParamsTypeNone ||
type != blink::kWebCryptoKeyTypeSecret)
diff --git a/chromium/components/webcrypto/algorithms/rsa.cc b/chromium/components/webcrypto/algorithms/rsa.cc
index 0227d0cd2b4..eff86c55630 100644
--- a/chromium/components/webcrypto/algorithms/rsa.cc
+++ b/chromium/components/webcrypto/algorithms/rsa.cc
@@ -7,10 +7,10 @@
#include <utility>
#include "base/check_op.h"
+#include "base/containers/span.h"
#include "components/webcrypto/algorithms/asymmetric_key_util.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
@@ -31,14 +31,14 @@ namespace {
// for "oth".
struct JwkRsaInfo {
bool is_private_key = false;
- std::string n;
- std::string e;
- std::string d;
- std::string p;
- std::string q;
- std::string dp;
- std::string dq;
- std::string qi;
+ std::vector<uint8_t> n;
+ std::vector<uint8_t> e;
+ std::vector<uint8_t> d;
+ std::vector<uint8_t> p;
+ std::vector<uint8_t> q;
+ std::vector<uint8_t> dp;
+ std::vector<uint8_t> dq;
+ std::vector<uint8_t> qi;
};
// Parses a UTF-8 encoded JWK (key_data), and extracts the RSA components to
@@ -48,7 +48,7 @@ struct JwkRsaInfo {
// * expected_extractable must be consistent with the JWK's "ext", if
// present.
// * expected_usages must be a subset of the JWK's "key_ops" if present.
-Status ReadRsaKeyJwk(const CryptoData& key_data,
+Status ReadRsaKeyJwk(base::span<const uint8_t> key_data,
const std::string& expected_alg,
bool expected_extractable,
blink::WebCryptoKeyUsageMask expected_usages,
@@ -177,14 +177,14 @@ Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
blink::WebCryptoKey* key) {
bssl::UniquePtr<RSA> rsa(RSA_new());
- rsa->n = CreateBIGNUM(params.n);
- rsa->e = CreateBIGNUM(params.e);
- rsa->d = CreateBIGNUM(params.d);
- rsa->p = CreateBIGNUM(params.p);
- rsa->q = CreateBIGNUM(params.q);
- rsa->dmp1 = CreateBIGNUM(params.dp);
- rsa->dmq1 = CreateBIGNUM(params.dq);
- rsa->iqmp = CreateBIGNUM(params.qi);
+ rsa->n = BN_bin2bn(params.n.data(), params.n.size(), nullptr);
+ rsa->e = BN_bin2bn(params.e.data(), params.e.size(), nullptr);
+ rsa->d = BN_bin2bn(params.d.data(), params.d.size(), nullptr);
+ rsa->p = BN_bin2bn(params.p.data(), params.p.size(), nullptr);
+ rsa->q = BN_bin2bn(params.q.data(), params.q.size(), nullptr);
+ rsa->dmp1 = BN_bin2bn(params.dp.data(), params.dp.size(), nullptr);
+ rsa->dmq1 = BN_bin2bn(params.dq.data(), params.dq.size(), nullptr);
+ rsa->iqmp = BN_bin2bn(params.qi.data(), params.qi.size(), nullptr);
if (!rsa->n || !rsa->e || !rsa->d || !rsa->p || !rsa->q || !rsa->dmp1 ||
!rsa->dmq1 || !rsa->iqmp) {
@@ -208,13 +208,13 @@ Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& n,
- const CryptoData& e,
+ base::span<const uint8_t> n,
+ base::span<const uint8_t> e,
blink::WebCryptoKey* key) {
bssl::UniquePtr<RSA> rsa(RSA_new());
- rsa->n = BN_bin2bn(n.bytes(), n.byte_length(), nullptr);
- rsa->e = BN_bin2bn(e.bytes(), e.byte_length(), nullptr);
+ rsa->n = BN_bin2bn(n.data(), n.size(), nullptr);
+ rsa->e = BN_bin2bn(e.data(), e.size(), nullptr);
if (!rsa->n || !rsa->e)
return Status::OperationError();
@@ -338,7 +338,7 @@ Status RsaHashedAlgorithm::GenerateKey(
}
Status RsaHashedAlgorithm::ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -371,7 +371,7 @@ Status RsaHashedAlgorithm::ExportKey(blink::WebCryptoKeyFormat format,
}
Status RsaHashedAlgorithm::ImportKeyPkcs8(
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -401,7 +401,7 @@ Status RsaHashedAlgorithm::ImportKeyPkcs8(
}
Status RsaHashedAlgorithm::ImportKeySpki(
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -424,7 +424,7 @@ Status RsaHashedAlgorithm::ImportKeySpki(
}
Status RsaHashedAlgorithm::ImportKeyJwk(
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -455,30 +455,22 @@ Status RsaHashedAlgorithm::ImportKeyJwk(
return jwk.is_private_key
? ImportRsaPrivateKey(algorithm, extractable, usages, jwk, key)
- : ImportRsaPublicKey(algorithm, extractable, usages,
- CryptoData(jwk.n), CryptoData(jwk.e), key);
+ : ImportRsaPublicKey(algorithm, extractable, usages, jwk.n, jwk.e,
+ key);
}
Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key,
std::vector<uint8_t>* buffer) const {
if (key.GetType() != blink::kWebCryptoKeyTypePrivate)
return Status::ErrorUnexpectedKeyType();
- // This relies on the fact that PKCS8 formatted data was already
- // associated with the key during its creation (used by
- // structured clone).
- *buffer = GetSerializedKeyData(key);
- return Status::Success();
+ return ExportPKeyPkcs8(GetEVP_PKEY(key), buffer);
}
Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key,
std::vector<uint8_t>* buffer) const {
if (key.GetType() != blink::kWebCryptoKeyTypePublic)
return Status::ErrorUnexpectedKeyType();
- // This relies on the fact that SPKI formatted data was already
- // associated with the key during its creation (used by
- // structured clone).
- *buffer = GetSerializedKeyData(key);
- return Status::Success();
+ return ExportPKeySpki(GetEVP_PKEY(key), buffer);
}
Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key,
@@ -498,23 +490,23 @@ Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key,
switch (key.GetType()) {
case blink::kWebCryptoKeyTypePublic: {
JwkWriter writer(jwk_algorithm, key.Extractable(), key.Usages(), "RSA");
- writer.SetBytes("n", CryptoData(BIGNUMToVector(rsa->n)));
- writer.SetBytes("e", CryptoData(BIGNUMToVector(rsa->e)));
+ writer.SetBytes("n", BIGNUMToVector(RSA_get0_n(rsa)));
+ writer.SetBytes("e", BIGNUMToVector(RSA_get0_e(rsa)));
writer.ToJson(buffer);
return Status::Success();
}
case blink::kWebCryptoKeyTypePrivate: {
JwkWriter writer(jwk_algorithm, key.Extractable(), key.Usages(), "RSA");
- writer.SetBytes("n", CryptoData(BIGNUMToVector(rsa->n)));
- writer.SetBytes("e", CryptoData(BIGNUMToVector(rsa->e)));
- writer.SetBytes("d", CryptoData(BIGNUMToVector(rsa->d)));
+ writer.SetBytes("n", BIGNUMToVector(RSA_get0_n(rsa)));
+ writer.SetBytes("e", BIGNUMToVector(RSA_get0_e(rsa)));
+ writer.SetBytes("d", BIGNUMToVector(RSA_get0_d(rsa)));
// Although these are "optional" in the JWA, WebCrypto spec requires them
// to be emitted.
- writer.SetBytes("p", CryptoData(BIGNUMToVector(rsa->p)));
- writer.SetBytes("q", CryptoData(BIGNUMToVector(rsa->q)));
- writer.SetBytes("dp", CryptoData(BIGNUMToVector(rsa->dmp1)));
- writer.SetBytes("dq", CryptoData(BIGNUMToVector(rsa->dmq1)));
- writer.SetBytes("qi", CryptoData(BIGNUMToVector(rsa->iqmp)));
+ writer.SetBytes("p", BIGNUMToVector(RSA_get0_p(rsa)));
+ writer.SetBytes("q", BIGNUMToVector(RSA_get0_q(rsa)));
+ writer.SetBytes("dp", BIGNUMToVector(RSA_get0_dmp1(rsa)));
+ writer.SetBytes("dq", BIGNUMToVector(RSA_get0_dmq1(rsa)));
+ writer.SetBytes("qi", BIGNUMToVector(RSA_get0_iqmp(rsa)));
writer.ToJson(buffer);
return Status::Success();
}
@@ -530,7 +522,7 @@ Status RsaHashedAlgorithm::DeserializeKeyForClone(
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const {
if (algorithm.ParamsType() !=
blink::kWebCryptoKeyAlgorithmParamsTypeRsaHashed)
@@ -577,8 +569,8 @@ Status RsaHashedAlgorithm::DeserializeKeyForClone(
if (algorithm.RsaHashedParams()->PublicExponent().size() !=
key->Algorithm().RsaHashedParams()->PublicExponent().size() ||
0 !=
- memcmp(algorithm.RsaHashedParams()->PublicExponent().Data(),
- key->Algorithm().RsaHashedParams()->PublicExponent().Data(),
+ memcmp(algorithm.RsaHashedParams()->PublicExponent().data(),
+ key->Algorithm().RsaHashedParams()->PublicExponent().data(),
key->Algorithm().RsaHashedParams()->PublicExponent().size())) {
return Status::ErrorUnexpected();
}
diff --git a/chromium/components/webcrypto/algorithms/rsa.h b/chromium/components/webcrypto/algorithms/rsa.h
index c66eff21186..c9872b25f34 100644
--- a/chromium/components/webcrypto/algorithms/rsa.h
+++ b/chromium/components/webcrypto/algorithms/rsa.h
@@ -41,7 +41,7 @@ class RsaHashedAlgorithm : public AlgorithmImplementation {
GenerateKeyResult* result) const override;
Status ImportKey(blink::WebCryptoKeyFormat format,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -55,23 +55,23 @@ class RsaHashedAlgorithm : public AlgorithmImplementation {
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
blink::WebCryptoKey* key) const override;
private:
- Status ImportKeyPkcs8(const CryptoData& key_data,
+ Status ImportKeyPkcs8(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) const;
- Status ImportKeySpki(const CryptoData& key_data,
+ Status ImportKeySpki(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) const;
- Status ImportKeyJwk(const CryptoData& key_data,
+ Status ImportKeyJwk(base::span<const uint8_t> key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
diff --git a/chromium/components/webcrypto/algorithms/rsa_oaep.cc b/chromium/components/webcrypto/algorithms/rsa_oaep.cc
index a6fa77bb710..c9b5996a60c 100644
--- a/chromium/components/webcrypto/algorithms/rsa_oaep.cc
+++ b/chromium/components/webcrypto/algorithms/rsa_oaep.cc
@@ -8,10 +8,10 @@
#include <memory>
+#include "base/containers/span.h"
#include "components/webcrypto/algorithms/rsa.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -42,7 +42,7 @@ Status CommonEncryptDecrypt(InitFunc init_func,
EncryptDecryptFunc encrypt_decrypt_func,
const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
@@ -69,7 +69,7 @@ Status CommonEncryptDecrypt(InitFunc init_func,
// calling set0_rsa_oaep_label().
bssl::UniquePtr<uint8_t> label_copy;
label_copy.reset(static_cast<uint8_t*>(OPENSSL_malloc(label.size())));
- memcpy(label_copy.get(), label.Data(), label.size());
+ memcpy(label_copy.get(), label.data(), label.size());
if (1 != EVP_PKEY_CTX_set0_rsa_oaep_label(ctx.get(), label_copy.release(),
label.size())) {
@@ -79,15 +79,15 @@ Status CommonEncryptDecrypt(InitFunc init_func,
// Determine the maximum length of the output.
size_t outlen = 0;
- if (!encrypt_decrypt_func(ctx.get(), nullptr, &outlen, data.bytes(),
- data.byte_length())) {
+ if (!encrypt_decrypt_func(ctx.get(), nullptr, &outlen, data.data(),
+ data.size())) {
return Status::OperationError();
}
buffer->resize(outlen);
// Do the actual encryption/decryption.
- if (!encrypt_decrypt_func(ctx.get(), buffer->data(), &outlen, data.bytes(),
- data.byte_length())) {
+ if (!encrypt_decrypt_func(ctx.get(), buffer->data(), &outlen, data.data(),
+ data.size())) {
return Status::OperationError();
}
buffer->resize(outlen);
@@ -121,7 +121,7 @@ class RsaOaepImplementation : public RsaHashedAlgorithm {
Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
if (key.GetType() != blink::kWebCryptoKeyTypePublic)
return Status::ErrorUnexpectedKeyType();
@@ -132,7 +132,7 @@ class RsaOaepImplementation : public RsaHashedAlgorithm {
Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
if (key.GetType() != blink::kWebCryptoKeyTypePrivate)
return Status::ErrorUnexpectedKeyType();
diff --git a/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc b/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc
index fb8f718f501..7db3bcb9eaf 100644
--- a/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc
@@ -6,9 +6,9 @@
#include <stdint.h>
#include "base/base64url.h"
+#include "base/containers/span.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -38,13 +38,15 @@ std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input) {
return base64url_encoded;
}
-std::unique_ptr<base::DictionaryValue> CreatePublicKeyJwkDict() {
- std::unique_ptr<base::DictionaryValue> jwk(new base::DictionaryValue());
- jwk->SetString("kty", "RSA");
- jwk->SetString("n",
- Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyModulusHex)));
- jwk->SetString("e",
- Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyExponentHex)));
+base::Value::Dict CreatePublicKeyJwkDict(
+ base::flat_map<std::string, std::string> extra_properties) {
+ base::Value::Dict jwk;
+ jwk.Set("kty", "RSA");
+ jwk.Set("n", Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyModulusHex)));
+ jwk.Set("e", Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyExponentHex)));
+ for (const auto& prop : extra_properties) {
+ jwk.Set(prop.first, prop.second);
+ }
return jwk;
}
@@ -56,7 +58,7 @@ TEST_F(WebCryptoRsaOaepTest, ImportPkcs8WithRsaEncryption) {
blink::WebCryptoKey private_key;
ASSERT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatPkcs8,
- CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)),
+ HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
@@ -64,56 +66,44 @@ TEST_F(WebCryptoRsaOaepTest, ImportPkcs8WithRsaEncryption) {
}
TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithNoAlg) {
- std::unique_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict());
-
blink::WebCryptoKey public_key;
ASSERT_EQ(
Status::Success(),
ImportKeyJwkFromDict(
- *jwk,
+ CreatePublicKeyJwkDict({}),
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
}
TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithMatchingAlg) {
- std::unique_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict());
- jwk->SetString("alg", "RSA-OAEP");
-
blink::WebCryptoKey public_key;
ASSERT_EQ(
Status::Success(),
ImportKeyJwkFromDict(
- *jwk,
+ CreatePublicKeyJwkDict({{"alg", "RSA-OAEP"}}),
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
}
TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithMismatchedAlgFails) {
- std::unique_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict());
- jwk->SetString("alg", "RSA-OAEP-512");
-
blink::WebCryptoKey public_key;
ASSERT_EQ(
Status::ErrorJwkAlgorithmInconsistent(),
ImportKeyJwkFromDict(
- *jwk,
+ CreatePublicKeyJwkDict({{"alg", "RSA-OAEP-512"}}),
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
}
TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithMismatchedTypeFails) {
- std::unique_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict());
- jwk->SetString("kty", "oct");
- jwk->SetString("alg", "RSA-OAEP");
-
blink::WebCryptoKey public_key;
ASSERT_EQ(
Status::ErrorJwkUnexpectedKty("RSA"),
ImportKeyJwkFromDict(
- *jwk,
+ CreatePublicKeyJwkDict({{"kty", "oct"}, {"alg", "RSA-OAEP"}}),
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -131,14 +121,11 @@ TEST_F(WebCryptoRsaOaepTest, ExportPublicJwk) {
const TestData& test_data = kTestData[i];
SCOPED_TRACE(test_data.expected_jwk_alg);
- std::unique_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict());
- jwk->SetString("alg", test_data.expected_jwk_alg);
-
// Import the key in a known-good format
blink::WebCryptoKey public_key;
ASSERT_EQ(Status::Success(),
ImportKeyJwkFromDict(
- *jwk,
+ CreatePublicKeyJwkDict({{"alg", test_data.expected_jwk_alg}}),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaOaep, test_data.hash_alg),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -153,28 +140,236 @@ TEST_F(WebCryptoRsaOaepTest, ExportPublicJwk) {
}
}
+struct RsaOaepKnownAnswer {
+ const char* pubkey;
+ const char* privkey;
+ blink::WebCryptoAlgorithmId hash;
+ const char* label;
+ const char* ciphertext;
+ const char* plaintext;
+};
+
+const RsaOaepKnownAnswer kRsaOaepKnownAnswers[] = {
+ // Tests for RSA-OAEP Encrypt/Decrypt without a label (the empty string).
+ {"30819f300d06092a864886f70d010101050003818d0030818902818100a56e4a0e7010175"
+ "89a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283"
+ "a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c"
+ "0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f2"
+ "0d0ce8cffb2249bd9a21370203010001",
+ "30820275020100300d06092a864886f70d01010105000482025f3082025b0201000281810"
+ "0a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056"
+ "ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8"
+ "b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b"
+ "6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f545"
+ "1ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c02"
+ "66c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a3957"
+ "4501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6"
+ "dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e8"
+ "6296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda"
+ "8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48d"
+ "e8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028"
+ "fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c0777"
+ "90427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa7120"
+ "49898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e16"
+ "78255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27"
+ "f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb219"
+ "0acf832b847f13a3d24a79f4d",
+ blink::kWebCryptoAlgorithmIdSha1, "",
+ "443FA1354F1DBC5C007A019CACCF18A3E6F986D7513D787F13DED463239F1833D6E33BFA8"
+ "AF034C198B0D903F202F543FEF38FAF54018EB7794B4FE638CA4D87C564B845C0695D7E70"
+ "C5D4464BEFF05225E747A6CA096A5E5E634002836D3CCE35713F082B20DCE7BB51324E4C8"
+ "9E202E3568E9F403ED864DE751BBE63E09680",
+ "666F6F64"},
+ {"30819f300d06092a864886f70d010101050003818d0030818902818100a56e4a0e7010175"
+ "89a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283"
+ "a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c"
+ "0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f2"
+ "0d0ce8cffb2249bd9a21370203010001",
+ "30820275020100300d06092a864886f70d01010105000482025f3082025b0201000281810"
+ "0a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056"
+ "ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8"
+ "b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b"
+ "6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f545"
+ "1ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c02"
+ "66c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a3957"
+ "4501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6"
+ "dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e8"
+ "6296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda"
+ "8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48d"
+ "e8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028"
+ "fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c0777"
+ "90427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa7120"
+ "49898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e16"
+ "78255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27"
+ "f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb219"
+ "0acf832b847f13a3d24a79f4d",
+ blink::kWebCryptoAlgorithmIdSha256, "",
+ "4EBEBCA7297864FDEA44B3BF0E4D5AC26A8478E63642CED06182597DF247EC9883E72DFDC"
+ "4507EE5058C52F6B929FBC34CE1FCF7C4A914D5B0C5C94638BDB0C70618E3F4FE79D29158"
+ "5DFD3197C4C16B57213337D12E59FC7A38617A1BE3DD2B09DA518E568712D2E6523F079C1"
+ "6EFCE540CAA829C5E39D62D64902A4E9D0BB3",
+ "666F6F64"},
+ {"30819f300d06092a864886f70d010101050003818d0030818902818100a56e4a0e7010175"
+ "89a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283"
+ "a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c"
+ "0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f2"
+ "0d0ce8cffb2249bd9a21370203010001",
+ "30820275020100300d06092a864886f70d01010105000482025f3082025b0201000281810"
+ "0a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056"
+ "ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8"
+ "b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b"
+ "6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f545"
+ "1ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c02"
+ "66c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a3957"
+ "4501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6"
+ "dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e8"
+ "6296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda"
+ "8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48d"
+ "e8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028"
+ "fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c0777"
+ "90427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa7120"
+ "49898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e16"
+ "78255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27"
+ "f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb219"
+ "0acf832b847f13a3d24a79f4d",
+ blink::kWebCryptoAlgorithmIdSha384, "",
+ "67B83119BA513947000F3D7F144408FCBD6B22F93C01671C1E40607972BB991716EAA79B9"
+ "6FF8DEF95D8E673697E279096D035BA9ED79B9FCAC933A26FB003941CC8E8749511CEDFFA"
+ "CA653769AB5209E414B80D30A0DC44BE72E49A7FE732819C0DA4142FA0810FFF19E56EFB1"
+ "4EAA5649398E4C7D920B254CAE54F8019B25A",
+ "666F6F64"},
+ // Tests for RSA-OAEP with an optional label specified.
+ {"30819f300d06092a864886f70d010101050003818d0030818902818100a56e4a0e7010175"
+ "89a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283"
+ "a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c"
+ "0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f2"
+ "0d0ce8cffb2249bd9a21370203010001",
+ "30820275020100300d06092a864886f70d01010105000482025f3082025b0201000281810"
+ "0a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056"
+ "ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8"
+ "b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b"
+ "6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f545"
+ "1ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c02"
+ "66c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a3957"
+ "4501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6"
+ "dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e8"
+ "6296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda"
+ "8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48d"
+ "e8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028"
+ "fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c0777"
+ "90427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa7120"
+ "49898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e16"
+ "78255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27"
+ "f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb219"
+ "0acf832b847f13a3d24a79f4d",
+ blink::kWebCryptoAlgorithmIdSha1, "66656564206D65207365796D6F7572",
+ "A27427C7CB3CD5B7D3C432B3F4BE98577B1FFBF302EDEFF0B219CABAB3E42DF7E8E24F9A8"
+ "9B387D314B199219F91B7F2CDE8E8D6F461A9495C3B5A398E0F670919EF62CCFF96CCF0FC"
+ "AF178E5D898C332AAD342F4C42B3209B10CE04B0D004A3ED2514A707D617A321206C22508"
+ "4C1446BF17208C6D278B0EA3F5968D1D44590",
+ "666F6F64"},
+ {"30819f300d06092a864886f70d010101050003818d0030818902818100a56e4a0e7010175"
+ "89a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283"
+ "a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c"
+ "0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f2"
+ "0d0ce8cffb2249bd9a21370203010001",
+ "30820275020100300d06092a864886f70d01010105000482025f3082025b0201000281810"
+ "0a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056"
+ "ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8"
+ "b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b"
+ "6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f545"
+ "1ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c02"
+ "66c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a3957"
+ "4501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6"
+ "dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e8"
+ "6296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda"
+ "8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48d"
+ "e8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028"
+ "fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c0777"
+ "90427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa7120"
+ "49898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e16"
+ "78255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27"
+ "f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb219"
+ "0acf832b847f13a3d24a79f4d",
+ blink::kWebCryptoAlgorithmIdSha256, "66656564206D65207365796D6F7572",
+ "333777A27C14C6186D2E90024507D7BD01D2A23FD462E9DBA6E9E96759A7025F29ABAA40D"
+ "F6B3355648AE8FD90CD08D9D92529CAD337A6BED806D19B998E472EC0704205A6B83BDB4D"
+ "4F5DB03FFE8BC68B44CA7F723A032161BD5B6D4517D58FAD8F1859676BC15A3BE9D20D78B"
+ "11D73F79B23372CF4793F6301F612E820D67A",
+ "666F6F64"},
+ {"30819f300d06092a864886f70d010101050003818d0030818902818100a56e4a0e7010175"
+ "89a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283"
+ "a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c"
+ "0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f2"
+ "0d0ce8cffb2249bd9a21370203010001",
+ "30820275020100300d06092a864886f70d01010105000482025f3082025b0201000281810"
+ "0a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056"
+ "ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8"
+ "b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b"
+ "6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f545"
+ "1ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c02"
+ "66c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a3957"
+ "4501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6"
+ "dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e8"
+ "6296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda"
+ "8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48d"
+ "e8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028"
+ "fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c0777"
+ "90427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa7120"
+ "49898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e16"
+ "78255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27"
+ "f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb219"
+ "0acf832b847f13a3d24a79f4d",
+ blink::kWebCryptoAlgorithmIdSha384, "66656564206D65207365796D6F7572",
+ "686D4B27EBD14F4745FB947459DE0D349E53C115F466245268E5C6B8C48FFFDAFB9C77373"
+ "60D850A6865883ADD516E4AFE71AACE2A73083A4D991293D59199639CF27F6DC0799F7856"
+ "53C3B1E4213EC277A6FA121F6CD2F81E29F1A35A4604B473983CFEDACB00AA9786E92999D"
+ "C9FA9AFDBDBB3F0EB3F49B301C5BFE14974BE",
+ "666F6F64"},
+ // With RSA-OAEP, an empty message is valid.
+ {"30819f300d06092a864886f70d010101050003818d0030818902818100a56e4a0e7010175"
+ "89a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283"
+ "a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c"
+ "0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f2"
+ "0d0ce8cffb2249bd9a21370203010001",
+ "30820275020100300d06092a864886f70d01010105000482025f3082025b0201000281810"
+ "0a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056"
+ "ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8"
+ "b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b"
+ "6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301000102818033a5042a90b27d4f545"
+ "1ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c02"
+ "66c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a3957"
+ "4501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6"
+ "dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e8"
+ "6296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda"
+ "8e6443024100b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48d"
+ "e8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd024028"
+ "fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c0777"
+ "90427bbdd8dd3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa7120"
+ "49898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e16"
+ "78255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d24a81f3a528cbfb27"
+ "f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb219"
+ "0acf832b847f13a3d24a79f4d",
+ blink::kWebCryptoAlgorithmIdSha1, "",
+ "5E35C080E4A0EBBB5AC8EE44888A6DB6B8C4D05A6427BE0ECBF245A23BCFC4A4A7D9E5466"
+ "123511399F5D01A3CF910DD6FEBCA969B23C753A0574163D847E70E7EFBFBBF6CC0154556"
+ "0D620F00CFA33AE318AF6090B76E4ADC5FE1686165786F8B7E559156E8AF690D6EF050133"
+ "AB5620FAF91B17CF52FED0B57FAC0924F2CC4",
+ ""},
+};
+
TEST_F(WebCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) {
- base::Value::List tests = ReadJsonTestFileAsList("rsa_oaep.json");
- for (const auto& test_value : tests) {
- SCOPED_TRACE(&test_value - &tests[0]);
-
- ASSERT_TRUE(test_value.is_dict());
- const base::DictionaryValue* test =
- &base::Value::AsDictionaryValue(test_value);
-
- blink::WebCryptoAlgorithm digest_algorithm =
- GetDigestAlgorithm(test, "hash");
- ASSERT_FALSE(digest_algorithm.IsNull());
- std::vector<uint8_t> public_key_der =
- GetBytesFromHexString(test, "public_key");
- std::vector<uint8_t> private_key_der =
- GetBytesFromHexString(test, "private_key");
- std::vector<uint8_t> ciphertext = GetBytesFromHexString(test, "ciphertext");
- std::vector<uint8_t> plaintext = GetBytesFromHexString(test, "plaintext");
- std::vector<uint8_t> label = GetBytesFromHexString(test, "label");
+ for (const auto& test : kRsaOaepKnownAnswers) {
+ SCOPED_TRACE(&test - &kRsaOaepKnownAnswers[0]);
+
+ std::vector<uint8_t> public_key_der = HexStringToBytes(test.pubkey);
+ std::vector<uint8_t> private_key_der = HexStringToBytes(test.privkey);
+ std::vector<uint8_t> ciphertext = HexStringToBytes(test.ciphertext);
+ std::vector<uint8_t> plaintext = HexStringToBytes(test.plaintext);
+ std::vector<uint8_t> label = HexStringToBytes(test.label);
blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm(
- blink::kWebCryptoAlgorithmIdRsaOaep, digest_algorithm.Id());
+ blink::kWebCryptoAlgorithmIdRsaOaep, test.hash);
blink::WebCryptoKey public_key;
blink::WebCryptoKey private_key;
@@ -186,17 +381,14 @@ TEST_F(WebCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) {
blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label);
std::vector<uint8_t> decrypted_data;
ASSERT_EQ(Status::Success(),
- Decrypt(op_algorithm, private_key, CryptoData(ciphertext),
- &decrypted_data));
+ Decrypt(op_algorithm, private_key, ciphertext, &decrypted_data));
EXPECT_BYTES_EQ(plaintext, decrypted_data);
std::vector<uint8_t> encrypted_data;
ASSERT_EQ(Status::Success(),
- Encrypt(op_algorithm, public_key, CryptoData(plaintext),
- &encrypted_data));
+ Encrypt(op_algorithm, public_key, plaintext, &encrypted_data));
std::vector<uint8_t> redecrypted_data;
- ASSERT_EQ(Status::Success(),
- Decrypt(op_algorithm, private_key, CryptoData(encrypted_data),
- &redecrypted_data));
+ ASSERT_EQ(Status::Success(), Decrypt(op_algorithm, private_key,
+ encrypted_data, &redecrypted_data));
EXPECT_BYTES_EQ(plaintext, redecrypted_data);
}
}
@@ -205,12 +397,10 @@ TEST_F(WebCryptoRsaOaepTest, EncryptWithLargeMessageFails) {
const blink::WebCryptoAlgorithmId kHash = blink::kWebCryptoAlgorithmIdSha1;
const size_t kHashSize = 20;
- std::unique_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict());
-
blink::WebCryptoKey public_key;
ASSERT_EQ(Status::Success(),
ImportKeyJwkFromDict(
- *jwk,
+ CreatePublicKeyJwkDict({}),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaOaep, kHash),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -229,27 +419,26 @@ TEST_F(WebCryptoRsaOaepTest, EncryptWithLargeMessageFails) {
blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label);
// Test that a message just before the boundary succeeds.
- std::string large_message;
+ std::vector<uint8_t> large_message;
large_message.resize(kMaxMessageSize - 1, 'A');
std::vector<uint8_t> ciphertext;
- ASSERT_EQ(Status::Success(), Encrypt(op_algorithm, public_key,
- CryptoData(large_message), &ciphertext));
+ ASSERT_EQ(Status::Success(),
+ Encrypt(op_algorithm, public_key, large_message, &ciphertext));
// Test that a message at the boundary succeeds.
large_message.resize(kMaxMessageSize, 'A');
ciphertext.clear();
- ASSERT_EQ(Status::Success(), Encrypt(op_algorithm, public_key,
- CryptoData(large_message), &ciphertext));
+ ASSERT_EQ(Status::Success(),
+ Encrypt(op_algorithm, public_key, large_message, &ciphertext));
// Test that a message greater than the largest size fails.
large_message.resize(kMaxMessageSize + 1, 'A');
ciphertext.clear();
ASSERT_EQ(Status::OperationError(),
- Encrypt(op_algorithm, public_key, CryptoData(large_message),
- &ciphertext));
+ Encrypt(op_algorithm, public_key, large_message, &ciphertext));
}
// Ensures that if the selected hash algorithm for the RSA-OAEP message is too
@@ -261,12 +450,10 @@ TEST_F(WebCryptoRsaOaepTest, EncryptWithLargeMessageFails) {
TEST_F(WebCryptoRsaOaepTest, EncryptWithLargeDigestFails) {
const blink::WebCryptoAlgorithmId kHash = blink::kWebCryptoAlgorithmIdSha512;
- std::unique_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict());
-
blink::WebCryptoKey public_key;
ASSERT_EQ(Status::Success(),
ImportKeyJwkFromDict(
- *jwk,
+ CreatePublicKeyJwkDict({}),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaOaep, kHash),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -276,20 +463,19 @@ TEST_F(WebCryptoRsaOaepTest, EncryptWithLargeDigestFails) {
std::vector<uint8_t> label;
blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label);
- std::string small_message("A");
+ std::vector<uint8_t> small_message = {'A'};
std::vector<uint8_t> ciphertext;
// This is an operation error, as the internal consistency checking of the
// algorithm parameters is up to the implementation.
ASSERT_EQ(Status::OperationError(),
- Encrypt(op_algorithm, public_key, CryptoData(small_message),
- &ciphertext));
+ Encrypt(op_algorithm, public_key, small_message, &ciphertext));
}
TEST_F(WebCryptoRsaOaepTest, DecryptWithLargeMessageFails) {
blink::WebCryptoKey private_key;
ASSERT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatPkcs8,
- CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)),
+ HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
@@ -300,12 +486,11 @@ TEST_F(WebCryptoRsaOaepTest, DecryptWithLargeMessageFails) {
std::vector<uint8_t> label;
blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label);
- std::string large_dummy_message(kModulusLengthBits / 8, 'A');
+ std::vector<uint8_t> large_dummy_message(kModulusLengthBits / 8, 'A');
std::vector<uint8_t> plaintext;
- ASSERT_EQ(Status::OperationError(),
- Decrypt(op_algorithm, private_key, CryptoData(large_dummy_message),
- &plaintext));
+ ASSERT_EQ(Status::OperationError(), Decrypt(op_algorithm, private_key,
+ large_dummy_message, &plaintext));
}
TEST_F(WebCryptoRsaOaepTest, WrapUnwrapRawKey) {
@@ -341,16 +526,15 @@ TEST_F(WebCryptoRsaOaepTest, WrapUnwrapRawKey) {
// Verify that |wrapped_key| can be decrypted and yields the key data.
// Because |private_key| supports both decrypt and unwrap, this is valid.
std::vector<uint8_t> decrypted_key;
- ASSERT_EQ(Status::Success(),
- Decrypt(wrapping_algorithm, private_key, CryptoData(wrapped_key),
- &decrypted_key));
+ ASSERT_EQ(Status::Success(), Decrypt(wrapping_algorithm, private_key,
+ wrapped_key, &decrypted_key));
EXPECT_BYTES_EQ_HEX(key_hex, decrypted_key);
// Now attempt to unwrap the key, which should also decrypt the data.
blink::WebCryptoKey unwrapped_key;
ASSERT_EQ(Status::Success(),
- UnwrapKey(blink::kWebCryptoKeyFormatRaw, CryptoData(wrapped_key),
- private_key, wrapping_algorithm, key_algorithm, true,
+ UnwrapKey(blink::kWebCryptoKeyFormatRaw, wrapped_key, private_key,
+ wrapping_algorithm, key_algorithm, true,
blink::kWebCryptoKeyUsageEncrypt, &unwrapped_key));
ASSERT_FALSE(unwrapped_key.IsNull());
@@ -440,17 +624,16 @@ TEST_F(WebCryptoRsaOaepTest, WrapUnwrapJwkSymKey) {
// Verify that |wrapped_key| can be decrypted and yields a valid JWK object.
// Because |private_key| supports both decrypt and unwrap, this is valid.
std::vector<uint8_t> decrypted_jwk;
- ASSERT_EQ(Status::Success(),
- Decrypt(wrapping_algorithm, private_key, CryptoData(wrapped_key),
- &decrypted_jwk));
+ ASSERT_EQ(Status::Success(), Decrypt(wrapping_algorithm, private_key,
+ wrapped_key, &decrypted_jwk));
EXPECT_TRUE(VerifySecretJwk(decrypted_jwk, "A128CBC", key_hex,
blink::kWebCryptoKeyUsageEncrypt));
// Now attempt to unwrap the key, which should also decrypt the data.
blink::WebCryptoKey unwrapped_key;
ASSERT_EQ(Status::Success(),
- UnwrapKey(blink::kWebCryptoKeyFormatJwk, CryptoData(wrapped_key),
- private_key, wrapping_algorithm, key_algorithm, true,
+ UnwrapKey(blink::kWebCryptoKeyFormatJwk, wrapped_key, private_key,
+ wrapping_algorithm, key_algorithm, true,
blink::kWebCryptoKeyUsageEncrypt, &unwrapped_key));
ASSERT_FALSE(unwrapped_key.IsNull());
@@ -488,7 +671,7 @@ TEST_F(WebCryptoRsaOaepTest, ImportExportJwkRsaPublicKey) {
blink::WebCryptoKey public_key;
ASSERT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatSpki,
- CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
+ HexStringToBytes(kPublicKeySpkiDerHex),
import_algorithm, true, test.usage, &public_key));
// Export the public key as JWK and verify its contents
@@ -501,8 +684,8 @@ TEST_F(WebCryptoRsaOaepTest, ImportExportJwkRsaPublicKey) {
// Import the JWK back in to create a new key
blink::WebCryptoKey public_key2;
ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(jwk),
- import_algorithm, true, test.usage, &public_key2));
+ ImportKey(blink::kWebCryptoKeyFormatJwk, jwk, import_algorithm,
+ true, test.usage, &public_key2));
ASSERT_TRUE(public_key2.Handle());
EXPECT_EQ(blink::kWebCryptoKeyTypePublic, public_key2.GetType());
EXPECT_TRUE(public_key2.Extractable());
diff --git a/chromium/components/webcrypto/algorithms/rsa_pss.cc b/chromium/components/webcrypto/algorithms/rsa_pss.cc
index 1771aea3f7e..ae61efac2ca 100644
--- a/chromium/components/webcrypto/algorithms/rsa_pss.cc
+++ b/chromium/components/webcrypto/algorithms/rsa_pss.cc
@@ -39,7 +39,7 @@ class RsaPssImplementation : public RsaHashedAlgorithm {
Status Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
return RsaSign(key, algorithm.RsaPssParams()->SaltLengthBytes(), data,
buffer);
@@ -47,8 +47,8 @@ class RsaPssImplementation : public RsaHashedAlgorithm {
Status Verify(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match) const override {
return RsaVerify(key, algorithm.RsaPssParams()->SaltLengthBytes(),
signature, data, signature_match);
diff --git a/chromium/components/webcrypto/algorithms/rsa_pss_unittest.cc b/chromium/components/webcrypto/algorithms/rsa_pss_unittest.cc
index 2c1a744f171..786a3a86f2e 100644
--- a/chromium/components/webcrypto/algorithms/rsa_pss_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/rsa_pss_unittest.cc
@@ -8,7 +8,6 @@
#include "base/strings/string_number_conversions.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -54,31 +53,26 @@ TEST_F(WebCryptoRsaPssTest, SignIsRandom) {
std::vector<uint8_t> signature1;
std::vector<uint8_t> signature2;
- ASSERT_EQ(Status::Success(),
- Sign(params, private_key, CryptoData(message), &signature1));
- ASSERT_EQ(Status::Success(),
- Sign(params, private_key, CryptoData(message), &signature2));
+ ASSERT_EQ(Status::Success(), Sign(params, private_key, message, &signature1));
+ ASSERT_EQ(Status::Success(), Sign(params, private_key, message, &signature2));
// The signatures will be different because of the salt.
- EXPECT_NE(CryptoData(signature1), CryptoData(signature2));
+ EXPECT_NE(signature1, signature2);
// However both signatures should work when verifying.
bool is_match = false;
ASSERT_EQ(Status::Success(),
- Verify(params, public_key, CryptoData(signature1),
- CryptoData(message), &is_match));
+ Verify(params, public_key, signature1, message, &is_match));
EXPECT_TRUE(is_match);
ASSERT_EQ(Status::Success(),
- Verify(params, public_key, CryptoData(signature2),
- CryptoData(message), &is_match));
+ Verify(params, public_key, signature2, message, &is_match));
EXPECT_TRUE(is_match);
// Corrupt the signature and verification must fail.
- ASSERT_EQ(Status::Success(),
- Verify(params, public_key, CryptoData(Corrupted(signature2)),
- CryptoData(message), &is_match));
+ ASSERT_EQ(Status::Success(), Verify(params, public_key, Corrupted(signature2),
+ message, &is_match));
EXPECT_FALSE(is_match);
}
@@ -107,25 +101,21 @@ TEST_F(WebCryptoRsaPssTest, SignVerifyNoSalt) {
std::vector<uint8_t> signature1;
std::vector<uint8_t> signature2;
- ASSERT_EQ(Status::Success(),
- Sign(params, private_key, CryptoData(message), &signature1));
- ASSERT_EQ(Status::Success(),
- Sign(params, private_key, CryptoData(message), &signature2));
+ ASSERT_EQ(Status::Success(), Sign(params, private_key, message, &signature1));
+ ASSERT_EQ(Status::Success(), Sign(params, private_key, message, &signature2));
// The signatures will be the same this time.
- EXPECT_EQ(CryptoData(signature1), CryptoData(signature2));
+ EXPECT_EQ(signature1, signature2);
// Make sure that verification works.
bool is_match = false;
ASSERT_EQ(Status::Success(),
- Verify(params, public_key, CryptoData(signature1),
- CryptoData(message), &is_match));
+ Verify(params, public_key, signature1, message, &is_match));
EXPECT_TRUE(is_match);
// Corrupt the signature and verification must fail.
- ASSERT_EQ(Status::Success(),
- Verify(params, public_key, CryptoData(Corrupted(signature2)),
- CryptoData(message), &is_match));
+ ASSERT_EQ(Status::Success(), Verify(params, public_key, Corrupted(signature2),
+ message, &is_match));
EXPECT_FALSE(is_match);
}
@@ -146,19 +136,17 @@ TEST_F(WebCryptoRsaPssTest, SignEmptyMessage) {
std::vector<uint8_t> message; // Empty message.
std::vector<uint8_t> signature;
- ASSERT_EQ(Status::Success(),
- Sign(params, private_key, CryptoData(message), &signature));
+ ASSERT_EQ(Status::Success(), Sign(params, private_key, message, &signature));
// Make sure that verification works.
bool is_match = false;
- ASSERT_EQ(Status::Success(), Verify(params, public_key, CryptoData(signature),
- CryptoData(message), &is_match));
+ ASSERT_EQ(Status::Success(),
+ Verify(params, public_key, signature, message, &is_match));
EXPECT_TRUE(is_match);
// Corrupt the signature and verification must fail.
- ASSERT_EQ(Status::Success(),
- Verify(params, public_key, CryptoData(Corrupted(signature)),
- CryptoData(message), &is_match));
+ ASSERT_EQ(Status::Success(), Verify(params, public_key, Corrupted(signature),
+ message, &is_match));
EXPECT_FALSE(is_match);
}
@@ -237,7 +225,7 @@ blink::WebCryptoKey RsaPssKeyFromBytes(blink::WebCryptoAlgorithmId hash,
const char* key) {
auto pubkey = blink::WebCryptoKey::CreateNull();
webcrypto::Status result = ImportKey(
- blink::kWebCryptoKeyFormatSpki, CryptoData(HexStringToBytes(key)),
+ blink::kWebCryptoKeyFormatSpki, HexStringToBytes(key),
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaPss, hash),
true, blink::kWebCryptoKeyUsageVerify, &pubkey);
CHECK(result == Status::Success());
@@ -249,8 +237,8 @@ bool VerifySignature(size_t salt_length,
const std::vector<uint8_t>& signature,
const std::vector<uint8_t>& message) {
bool is_match = false;
- auto result = Verify(CreateRsaPssAlgorithm(salt_length), pubkey,
- CryptoData(signature), CryptoData(message), &is_match);
+ auto result = Verify(CreateRsaPssAlgorithm(salt_length), pubkey, signature,
+ message, &is_match);
CHECK(result == Status::Success());
return is_match;
}
diff --git a/chromium/components/webcrypto/algorithms/rsa_sign.cc b/chromium/components/webcrypto/algorithms/rsa_sign.cc
index 04fb27058f4..8bd826a402c 100644
--- a/chromium/components/webcrypto/algorithms/rsa_sign.cc
+++ b/chromium/components/webcrypto/algorithms/rsa_sign.cc
@@ -8,7 +8,6 @@
#include "components/webcrypto/algorithms/rsa_sign.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
@@ -71,7 +70,7 @@ Status ApplyRsaPssOptions(const blink::WebCryptoKey& key,
Status RsaSign(const blink::WebCryptoKey& key,
unsigned int pss_salt_length_bytes,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) {
if (key.GetType() != blink::kWebCryptoKeyTypePrivate)
return Status::ErrorUnexpectedKeyType();
@@ -99,7 +98,7 @@ Status RsaSign(const blink::WebCryptoKey& key,
if (status.IsError())
return status;
- if (!EVP_DigestSignUpdate(ctx.get(), data.bytes(), data.byte_length()) ||
+ if (!EVP_DigestSignUpdate(ctx.get(), data.data(), data.size()) ||
!EVP_DigestSignFinal(ctx.get(), nullptr, &sig_len)) {
return Status::OperationError();
}
@@ -114,8 +113,8 @@ Status RsaSign(const blink::WebCryptoKey& key,
Status RsaVerify(const blink::WebCryptoKey& key,
unsigned int pss_salt_length_bytes,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match) {
if (key.GetType() != blink::kWebCryptoKeyTypePublic)
return Status::ErrorUnexpectedKeyType();
@@ -138,11 +137,11 @@ Status RsaVerify(const blink::WebCryptoKey& key,
if (status.IsError())
return status;
- if (!EVP_DigestVerifyUpdate(ctx.get(), data.bytes(), data.byte_length()))
+ if (!EVP_DigestVerifyUpdate(ctx.get(), data.data(), data.size()))
return Status::OperationError();
- *signature_match = 1 == EVP_DigestVerifyFinal(ctx.get(), signature.bytes(),
- signature.byte_length());
+ *signature_match =
+ 1 == EVP_DigestVerifyFinal(ctx.get(), signature.data(), signature.size());
return Status::Success();
}
diff --git a/chromium/components/webcrypto/algorithms/rsa_sign.h b/chromium/components/webcrypto/algorithms/rsa_sign.h
index 2e3f09200cf..d0abe50f653 100644
--- a/chromium/components/webcrypto/algorithms/rsa_sign.h
+++ b/chromium/components/webcrypto/algorithms/rsa_sign.h
@@ -9,13 +9,14 @@
#include <vector>
+#include "base/containers/span.h"
+
namespace blink {
class WebCryptoKey;
}
namespace webcrypto {
-class CryptoData;
class Status;
// Helper functions for doing RSA-SSA signing and verification
@@ -26,13 +27,13 @@ class Status;
Status RsaSign(const blink::WebCryptoKey& key,
unsigned int pss_salt_length_bytes,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer);
Status RsaVerify(const blink::WebCryptoKey& key,
unsigned int pss_salt_length_bytes,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match);
} // namespace webcrypto
diff --git a/chromium/components/webcrypto/algorithms/rsa_ssa.cc b/chromium/components/webcrypto/algorithms/rsa_ssa.cc
index 2500b3a969e..d7e3d46f75b 100644
--- a/chromium/components/webcrypto/algorithms/rsa_ssa.cc
+++ b/chromium/components/webcrypto/algorithms/rsa_ssa.cc
@@ -38,15 +38,15 @@ class RsaSsaImplementation : public RsaHashedAlgorithm {
Status Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
return RsaSign(key, 0, data, buffer);
}
Status Verify(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
- const CryptoData& signature,
- const CryptoData& data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> data,
bool* signature_match) const override {
return RsaVerify(key, 0, signature, data, signature_match);
}
diff --git a/chromium/components/webcrypto/algorithms/rsa_ssa_unittest.cc b/chromium/components/webcrypto/algorithms/rsa_ssa_unittest.cc
index eb23a328c0e..fb92af4a0ba 100644
--- a/chromium/components/webcrypto/algorithms/rsa_ssa_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/rsa_ssa_unittest.cc
@@ -6,10 +6,10 @@
#include <stdint.h>
#include "base/check_op.h"
+#include "base/containers/span.h"
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -43,7 +43,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportExportSpki) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatSpki,
- CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
+ HexStringToBytes(kPublicKeySpkiDerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha256),
@@ -54,14 +54,13 @@ TEST_F(WebCryptoRsaSsaTest, ImportExportSpki) {
EXPECT_EQ(blink::kWebCryptoKeyUsageVerify, key.Usages());
EXPECT_EQ(kModulusLengthBits,
key.Algorithm().RsaHashedParams()->ModulusLengthBits());
- EXPECT_BYTES_EQ_HEX(
- "010001",
- CryptoData(key.Algorithm().RsaHashedParams()->PublicExponent()));
+ EXPECT_BYTES_EQ_HEX("010001",
+ key.Algorithm().RsaHashedParams()->PublicExponent());
// Failing case: Import RSA key but provide an inconsistent input algorithm.
EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(),
ImportKey(blink::kWebCryptoKeyFormatSpki,
- CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
+ HexStringToBytes(kPublicKeySpkiDerHex),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
blink::kWebCryptoKeyUsageEncrypt, &key));
@@ -80,7 +79,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportExportSpki) {
// Failing case: Try to export a non-extractable key
ASSERT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatSpki,
- CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
+ HexStringToBytes(kPublicKeySpkiDerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha256),
@@ -103,7 +102,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportExportPkcs8) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatPkcs8,
- CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)),
+ HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha1),
@@ -116,9 +115,8 @@ TEST_F(WebCryptoRsaSsaTest, ImportExportPkcs8) {
key.Algorithm().RsaHashedParams()->GetHash().Id());
EXPECT_EQ(kModulusLengthBits,
key.Algorithm().RsaHashedParams()->ModulusLengthBits());
- EXPECT_BYTES_EQ_HEX(
- "010001",
- CryptoData(key.Algorithm().RsaHashedParams()->PublicExponent()));
+ EXPECT_BYTES_EQ_HEX("010001",
+ key.Algorithm().RsaHashedParams()->PublicExponent());
std::vector<uint8_t> exported_key;
ASSERT_EQ(Status::Success(),
@@ -131,7 +129,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportExportPkcs8) {
// * AES-CBC doesn't support "sign" usage
EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(),
ImportKey(blink::kWebCryptoKeyFormatPkcs8,
- CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)),
+ HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
blink::kWebCryptoKeyUsageSign, &key));
}
@@ -144,7 +142,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportRsaPrivateKeyJwkToPkcs8RoundTrip) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatPkcs8,
- CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)),
+ HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha1),
@@ -174,23 +172,22 @@ TEST_F(WebCryptoRsaSsaTest, ImportRsaPrivateKeyJwkToPkcs8RoundTrip) {
"\"JxVqukEm0kqB86Uoy_sn9WiG-"
"ECp9uhuF6RLlP6TGVhLjiL93h5aLjvYqluo2FhBlOshkKz4MrhH8To9JKefTQ\"}";
- ASSERT_EQ(CryptoData(std::string(expected_jwk)),
- CryptoData(exported_key_jwk));
+ EXPECT_BYTES_EQ(
+ base::as_bytes(base::make_span(base::StringPiece(expected_jwk))),
+ exported_key_jwk);
- ASSERT_EQ(
- Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(exported_key_jwk),
- CreateRsaHashedImportAlgorithm(
- blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
- blink::kWebCryptoAlgorithmIdSha1),
- true, blink::kWebCryptoKeyUsageSign, &key));
+ ASSERT_EQ(Status::Success(),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, exported_key_jwk,
+ CreateRsaHashedImportAlgorithm(
+ blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
+ blink::kWebCryptoAlgorithmIdSha1),
+ true, blink::kWebCryptoKeyUsageSign, &key));
std::vector<uint8_t> exported_key_pkcs8;
ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatPkcs8, key,
&exported_key_pkcs8));
- ASSERT_EQ(CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)),
- CryptoData(exported_key_pkcs8));
+ ASSERT_EQ(HexStringToBytes(kPrivateKeyPkcs8DerHex), exported_key_pkcs8);
}
// Tests importing multiple RSA private keys from JWK, and then exporting to
@@ -348,13 +345,12 @@ TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsa) {
public_key, &public_key_spki));
public_key = blink::WebCryptoKey::CreateNull();
- ASSERT_EQ(
- Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatSpki, CryptoData(public_key_spki),
- CreateRsaHashedImportAlgorithm(
- blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
- blink::kWebCryptoAlgorithmIdSha256),
- true, public_usages, &public_key));
+ ASSERT_EQ(Status::Success(),
+ ImportKey(blink::kWebCryptoKeyFormatSpki, public_key_spki,
+ CreateRsaHashedImportAlgorithm(
+ blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
+ blink::kWebCryptoAlgorithmIdSha256),
+ true, public_usages, &public_key));
EXPECT_EQ(modulus_length,
public_key.Algorithm().RsaHashedParams()->ModulusLengthBits());
@@ -362,13 +358,12 @@ TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsa) {
EXPECT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatPkcs8,
private_key, &private_key_pkcs8));
private_key = blink::WebCryptoKey::CreateNull();
- ASSERT_EQ(
- Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatPkcs8, CryptoData(private_key_pkcs8),
- CreateRsaHashedImportAlgorithm(
- blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
- blink::kWebCryptoAlgorithmIdSha256),
- true, private_usages, &private_key));
+ ASSERT_EQ(Status::Success(),
+ ImportKey(blink::kWebCryptoKeyFormatPkcs8, private_key_pkcs8,
+ CreateRsaHashedImportAlgorithm(
+ blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
+ blink::kWebCryptoAlgorithmIdSha256),
+ true, private_usages, &private_key));
EXPECT_EQ(modulus_length,
private_key.Algorithm().RsaHashedParams()->ModulusLengthBits());
@@ -548,48 +543,42 @@ TEST_F(WebCryptoRsaSsaTest, SignVerifyFailures) {
// Compute a signature.
const std::vector<uint8_t> data = HexStringToBytes("010203040506070809");
- ASSERT_EQ(Status::Success(),
- Sign(algorithm, private_key, CryptoData(data), &signature));
+ ASSERT_EQ(Status::Success(), Sign(algorithm, private_key, data, &signature));
// Ensure truncated signature does not verify by passing one less byte.
EXPECT_EQ(Status::Success(),
Verify(algorithm, public_key,
- CryptoData(signature.data(),
- static_cast<unsigned int>(signature.size()) - 1),
- CryptoData(data), &signature_match));
+ base::make_span(signature).first(signature.size() - 1), data,
+ &signature_match));
EXPECT_FALSE(signature_match);
// Ensure truncated signature does not verify by passing no bytes.
- EXPECT_EQ(Status::Success(), Verify(algorithm, public_key, CryptoData(),
- CryptoData(data), &signature_match));
+ EXPECT_EQ(Status::Success(),
+ Verify(algorithm, public_key, {}, data, &signature_match));
EXPECT_FALSE(signature_match);
// Ensure corrupted signature does not verify.
std::vector<uint8_t> corrupt_sig = signature;
corrupt_sig[corrupt_sig.size() / 2] ^= 0x1;
EXPECT_EQ(Status::Success(),
- Verify(algorithm, public_key, CryptoData(corrupt_sig),
- CryptoData(data), &signature_match));
+ Verify(algorithm, public_key, corrupt_sig, data, &signature_match));
EXPECT_FALSE(signature_match);
// Ensure signatures that are greater than the modulus size fail.
- const unsigned int long_message_size_bytes = 1024;
+ const size_t long_message_size_bytes = 1024;
DCHECK_GT(long_message_size_bytes, kModulusLengthBits / 8);
const unsigned char kLongSignature[long_message_size_bytes] = {0};
- EXPECT_EQ(Status::Success(),
- Verify(algorithm, public_key,
- CryptoData(kLongSignature, sizeof(kLongSignature)),
- CryptoData(data), &signature_match));
+ EXPECT_EQ(Status::Success(), Verify(algorithm, public_key, kLongSignature,
+ data, &signature_match));
EXPECT_FALSE(signature_match);
// Ensure that signing and verifying with an incompatible algorithm fails.
algorithm = CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep);
EXPECT_EQ(Status::ErrorUnexpected(),
- Sign(algorithm, private_key, CryptoData(data), &signature));
+ Sign(algorithm, private_key, data, &signature));
EXPECT_EQ(Status::ErrorUnexpected(),
- Verify(algorithm, public_key, CryptoData(signature),
- CryptoData(data), &signature_match));
+ Verify(algorithm, public_key, signature, data, &signature_match));
// Some crypto libraries (NSS) can automatically select the RSA SSA inner hash
// based solely on the contents of the input signature data. In the Web Crypto
@@ -603,12 +592,12 @@ TEST_F(WebCryptoRsaSsaTest, SignVerifyFailures) {
// Compute a signature using SHA-1 as the inner hash.
EXPECT_EQ(Status::Success(),
Sign(CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5),
- private_key, CryptoData(data), &signature));
+ private_key, data, &signature));
blink::WebCryptoKey public_key_256;
EXPECT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatSpki,
- CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
+ HexStringToBytes(kPublicKeySpkiDerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha256),
@@ -627,8 +616,7 @@ TEST_F(WebCryptoRsaSsaTest, SignVerifyFailures) {
bool is_match;
EXPECT_EQ(Status::Success(),
Verify(CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5),
- public_key_256, CryptoData(signature), CryptoData(data),
- &is_match));
+ public_key_256, signature, data, &is_match));
EXPECT_FALSE(is_match);
}
@@ -665,14 +653,13 @@ TEST_F(WebCryptoRsaSsaTest, SignVerifyKnownAnswer) {
GetBytesFromHexString(test, "signature_hex");
signature.clear();
- ASSERT_EQ(Status::Success(), Sign(algorithm, private_key,
- CryptoData(test_message), &signature));
+ ASSERT_EQ(Status::Success(),
+ Sign(algorithm, private_key, test_message, &signature));
EXPECT_BYTES_EQ(test_signature, signature);
bool is_match = false;
- ASSERT_EQ(Status::Success(),
- Verify(algorithm, public_key, CryptoData(test_signature),
- CryptoData(test_message), &is_match));
+ ASSERT_EQ(Status::Success(), Verify(algorithm, public_key, test_signature,
+ test_message, &is_match));
EXPECT_TRUE(is_match);
}
}
@@ -697,8 +684,8 @@ TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_SPKI) {
blink::WebCryptoKey public_key;
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKey(blink::kWebCryptoKeyFormatSpki,
- CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
- algorithm, false, bad_usages[i], &public_key));
+ HexStringToBytes(kPublicKeySpkiDerHex), algorithm,
+ false, bad_usages[i], &public_key));
}
}
@@ -816,7 +803,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportKeyEmptyUsages) {
// Public without usage does not throw an error.
ASSERT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatSpki,
- CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
+ HexStringToBytes(kPublicKeySpkiDerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha256),
@@ -826,7 +813,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportKeyEmptyUsages) {
// Private empty usage will throw an error.
ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
ImportKey(blink::kWebCryptoKeyFormatPkcs8,
- CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)),
+ HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha1),
@@ -837,7 +824,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportKeyEmptyUsages) {
ExportKey(blink::kWebCryptoKeyFormatJwk, public_key, &public_jwk));
ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(public_jwk),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, public_jwk,
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha256),
@@ -847,7 +834,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportKeyEmptyUsages) {
// With correct usage to get correct imported private_key
std::vector<uint8_t> private_jwk;
ImportKey(blink::kWebCryptoKeyFormatPkcs8,
- CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)),
+ HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha1),
@@ -857,7 +844,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportKeyEmptyUsages) {
private_key, &private_jwk));
ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(private_jwk),
+ ImportKey(blink::kWebCryptoKeyFormatJwk, private_jwk,
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha1),
@@ -891,7 +878,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportExportJwkRsaPublicKey) {
blink::WebCryptoKey public_key;
ASSERT_EQ(Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatSpki,
- CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
+ HexStringToBytes(kPublicKeySpkiDerHex),
import_algorithm, true, test.usage, &public_key));
// Export the public key as JWK and verify its contents
@@ -904,8 +891,8 @@ TEST_F(WebCryptoRsaSsaTest, ImportExportJwkRsaPublicKey) {
// Import the JWK back in to create a new key
blink::WebCryptoKey public_key2;
ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(jwk),
- import_algorithm, true, test.usage, &public_key2));
+ ImportKey(blink::kWebCryptoKeyFormatJwk, jwk, import_algorithm,
+ true, test.usage, &public_key2));
ASSERT_TRUE(public_key2.Handle());
EXPECT_EQ(blink::kWebCryptoKeyTypePublic, public_key2.GetType());
EXPECT_TRUE(public_key2.Extractable());
@@ -915,7 +902,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportExportJwkRsaPublicKey) {
std::vector<uint8_t> spki;
ASSERT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatSpki, public_key2, &spki));
- EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, CryptoData(spki));
+ EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, spki);
}
}
@@ -977,7 +964,8 @@ TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaJwkBadUsageAndData) {
blink::WebCryptoKey key;
ASSERT_EQ(
Status::ErrorJwkNotDictionary(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(bad_data),
+ ImportKey(blink::kWebCryptoKeyFormatJwk,
+ base::as_bytes(base::make_span(bad_data)),
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha256),
@@ -1006,7 +994,7 @@ TEST_F(WebCryptoRsaSsaTest, ImportInvalidKeyData) {
if (key_format == blink::kWebCryptoKeyFormatSpki)
usages = blink::kWebCryptoKeyUsageVerify;
blink::WebCryptoKey key;
- Status status = ImportKey(key_format, CryptoData(key_data),
+ Status status = ImportKey(key_format, key_data,
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha256),
diff --git a/chromium/components/webcrypto/algorithms/secret_key_util.cc b/chromium/components/webcrypto/algorithms/secret_key_util.cc
index cb20290b4ea..c0bb0759290 100644
--- a/chromium/components/webcrypto/algorithms/secret_key_util.cc
+++ b/chromium/components/webcrypto/algorithms/secret_key_util.cc
@@ -6,7 +6,6 @@
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
@@ -32,13 +31,13 @@ Status GenerateWebCryptoSecretKey(const blink::WebCryptoKeyAlgorithm& algorithm,
}
result->AssignSecretKey(blink::WebCryptoKey::Create(
- CreateSymmetricKeyHandle(CryptoData(random_bytes)),
- blink::kWebCryptoKeyTypeSecret, extractable, algorithm, usages));
+ CreateSymmetricKeyHandle(random_bytes), blink::kWebCryptoKeyTypeSecret,
+ extractable, algorithm, usages));
return Status::Success();
}
-Status CreateWebCryptoSecretKey(const CryptoData& key_data,
+Status CreateWebCryptoSecretKey(base::span<const uint8_t> key_data,
const blink::WebCryptoKeyAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -49,7 +48,7 @@ Status CreateWebCryptoSecretKey(const CryptoData& key_data,
return Status::Success();
}
-void WriteSecretKeyJwk(const CryptoData& raw_key_data,
+void WriteSecretKeyJwk(base::span<const uint8_t> raw_key_data,
const std::string& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -60,7 +59,7 @@ void WriteSecretKeyJwk(const CryptoData& raw_key_data,
}
Status ReadSecretKeyNoExpectedAlgJwk(
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
bool expected_extractable,
blink::WebCryptoKeyUsageMask expected_usages,
std::vector<uint8_t>* raw_key_data,
@@ -70,13 +69,7 @@ Status ReadSecretKeyNoExpectedAlgJwk(
if (status.IsError())
return status;
- std::string jwk_k_value;
- status = jwk->GetBytes("k", &jwk_k_value);
- if (status.IsError())
- return status;
- raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end());
-
- return Status::Success();
+ return jwk->GetBytes("k", raw_key_data);
}
} // namespace webcrypto
diff --git a/chromium/components/webcrypto/algorithms/secret_key_util.h b/chromium/components/webcrypto/algorithms/secret_key_util.h
index f0404f60bda..50d63bd4399 100644
--- a/chromium/components/webcrypto/algorithms/secret_key_util.h
+++ b/chromium/components/webcrypto/algorithms/secret_key_util.h
@@ -10,6 +10,7 @@
#include <string>
#include <vector>
+#include "base/containers/span.h"
#include "third_party/blink/public/platform/web_crypto_algorithm.h"
#include "third_party/blink/public/platform/web_crypto_key.h"
@@ -17,7 +18,6 @@
namespace webcrypto {
-class CryptoData;
class GenerateKeyResult;
class JwkReader;
class Status;
@@ -35,7 +35,7 @@ Status GenerateWebCryptoSecretKey(const blink::WebCryptoKeyAlgorithm& algorithm,
// Creates a WebCrypto secret key given the raw data. The provided |key_data|
// will be copied into the new key. This function does not do any validation
// checks for the provided parameters.
-Status CreateWebCryptoSecretKey(const CryptoData& key_data,
+Status CreateWebCryptoSecretKey(base::span<const uint8_t> key_data,
const blink::WebCryptoKeyAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -46,7 +46,7 @@ Status CreateWebCryptoSecretKey(const CryptoData& key_data,
// * algorithm: The JWK algorithm name (i.e. "alg")
// * extractable: The JWK extractability (i.e. "ext")
// * usages: The JWK usages (i.e. "key_ops")
-void WriteSecretKeyJwk(const CryptoData& raw_key_data,
+void WriteSecretKeyJwk(base::span<const uint8_t> raw_key_data,
const std::string& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
@@ -59,7 +59,7 @@ void WriteSecretKeyJwk(const CryptoData& raw_key_data,
// present.
// * expected_usages must be a subset of the JWK's "key_ops" if present.
Status ReadSecretKeyNoExpectedAlgJwk(
- const CryptoData& key_data,
+ base::span<const uint8_t> key_data,
bool expected_extractable,
blink::WebCryptoKeyUsageMask expected_usages,
std::vector<uint8_t>* raw_key_data,
diff --git a/chromium/components/webcrypto/algorithms/sha.cc b/chromium/components/webcrypto/algorithms/sha.cc
index efb32a1ff3f..ba6d1b52312 100644
--- a/chromium/components/webcrypto/algorithms/sha.cc
+++ b/chromium/components/webcrypto/algorithms/sha.cc
@@ -7,9 +7,9 @@
#include <vector>
#include "base/check_op.h"
+#include "base/containers/span.h"
#include "components/webcrypto/algorithm_implementation.h"
#include "components/webcrypto/algorithms/util.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/boringssl/src/include/openssl/digest.h"
@@ -21,10 +21,8 @@ namespace {
class ShaImplementation : public AlgorithmImplementation {
public:
Status Digest(const blink::WebCryptoAlgorithm& algorithm,
- const CryptoData& data,
+ base::span<const uint8_t> data,
std::vector<uint8_t>* buffer) const override {
- // http://crbug.com/366427: the spec does not define any other failures for
- // digest, so none of the subsequent errors are spec compliant.
bssl::ScopedEVP_MD_CTX digest_context;
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
@@ -35,8 +33,7 @@ class ShaImplementation : public AlgorithmImplementation {
if (!EVP_DigestInit_ex(digest_context.get(), digest_algorithm, nullptr))
return Status::OperationError();
- if (!EVP_DigestUpdate(digest_context.get(), data.bytes(),
- data.byte_length()))
+ if (!EVP_DigestUpdate(digest_context.get(), data.data(), data.size()))
return Status::OperationError();
const size_t hash_expected_size = EVP_MD_CTX_size(digest_context.get());
diff --git a/chromium/components/webcrypto/algorithms/sha_unittest.cc b/chromium/components/webcrypto/algorithms/sha_unittest.cc
index 5345d17d30f..1b57e75cb37 100644
--- a/chromium/components/webcrypto/algorithms/sha_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/sha_unittest.cc
@@ -8,7 +8,6 @@
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
@@ -36,8 +35,7 @@ TEST_F(WebCryptoShaTest, DigestSampleSets) {
std::vector<uint8_t> test_output = GetBytesFromHexString(test, "output");
std::vector<uint8_t> output;
- ASSERT_EQ(Status::Success(),
- Digest(test_algorithm, CryptoData(test_input), &output));
+ ASSERT_EQ(Status::Success(), Digest(test_algorithm, test_input, &output));
EXPECT_BYTES_EQ(test_output, output);
}
}
diff --git a/chromium/components/webcrypto/algorithms/test_helpers.cc b/chromium/components/webcrypto/algorithms/test_helpers.cc
index f72b40423ab..2cbfc7a241e 100644
--- a/chromium/components/webcrypto/algorithms/test_helpers.cc
+++ b/chromium/components/webcrypto/algorithms/test_helpers.cc
@@ -13,12 +13,12 @@
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
+#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
@@ -77,19 +77,6 @@ bool operator!=(const Status& a, const Status& b) {
return !(a == b);
}
-void PrintTo(const CryptoData& data, ::std::ostream* os) {
- *os << "[" << base::HexEncode(data.bytes(), data.byte_length()) << "]";
-}
-
-bool operator==(const CryptoData& a, const CryptoData& b) {
- return a.byte_length() == b.byte_length() &&
- memcmp(a.bytes(), b.bytes(), a.byte_length()) == 0;
-}
-
-bool operator!=(const CryptoData& a, const CryptoData& b) {
- return !(a == b);
-}
-
static std::string ErrorTypeToString(blink::WebCryptoErrorType type) {
switch (type) {
case blink::kWebCryptoErrorTypeNotSupported:
@@ -221,7 +208,7 @@ class CompareUsingIndex {
bool operator()(size_t i1, size_t i2) { return (*bufs_)[i1] < (*bufs_)[i2]; }
private:
- const std::vector<std::vector<uint8_t>>* bufs_;
+ raw_ptr<const std::vector<std::vector<uint8_t>>> bufs_;
};
bool CopiesExist(const std::vector<std::vector<uint8_t>>& bufs) {
@@ -297,9 +284,8 @@ blink::WebCryptoKey ImportSecretKeyFromRaw(
blink::WebCryptoKeyUsageMask usage) {
blink::WebCryptoKey key;
bool extractable = true;
- EXPECT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatRaw, CryptoData(key_raw),
- algorithm, extractable, usage, &key));
+ EXPECT_EQ(Status::Success(), ImportKey(blink::kWebCryptoKeyFormatRaw, key_raw,
+ algorithm, extractable, usage, &key));
EXPECT_FALSE(key.IsNull());
EXPECT_TRUE(key.Handle());
@@ -319,8 +305,8 @@ void ImportRsaKeyPair(const std::vector<uint8_t>& spki_der,
blink::WebCryptoKey* public_key,
blink::WebCryptoKey* private_key) {
ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatSpki, CryptoData(spki_der),
- algorithm, true, public_key_usages, public_key));
+ ImportKey(blink::kWebCryptoKeyFormatSpki, spki_der, algorithm, true,
+ public_key_usages, public_key));
EXPECT_FALSE(public_key->IsNull());
EXPECT_TRUE(public_key->Handle());
EXPECT_EQ(blink::kWebCryptoKeyTypePublic, public_key->GetType());
@@ -329,8 +315,8 @@ void ImportRsaKeyPair(const std::vector<uint8_t>& spki_der,
EXPECT_EQ(public_key_usages, public_key->Usages());
ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatPkcs8, CryptoData(pkcs8_der),
- algorithm, extractable, private_key_usages, private_key));
+ ImportKey(blink::kWebCryptoKeyFormatPkcs8, pkcs8_der, algorithm,
+ extractable, private_key_usages, private_key));
EXPECT_FALSE(private_key->IsNull());
EXPECT_TRUE(private_key->Handle());
EXPECT_EQ(blink::kWebCryptoKeyTypePrivate, private_key->GetType());
@@ -344,9 +330,8 @@ Status ImportKeyJwkFromDict(const base::ValueView& dict,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) {
- return ImportKey(blink::kWebCryptoKeyFormatJwk,
- CryptoData(MakeJsonVector(dict)), algorithm, extractable,
- usages, key);
+ return ImportKey(blink::kWebCryptoKeyFormatJwk, MakeJsonVector(dict),
+ algorithm, extractable, usages, key);
}
Status ImportKeyJwkFromDict(const base::DictionaryValue& dict,
@@ -439,7 +424,7 @@ absl::optional<base::DictionaryValue> GetJwkDictionary(
std::string k_value;
if (!Base64DecodeUrlSafe(value_string, &k_value))
return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(k) failed";
- if (!base::LowerCaseEqualsASCII(
+ if (!base::EqualsCaseInsensitiveASCII(
base::HexEncode(k_value.data(), k_value.size()), k_expected_hex)) {
return ::testing::AssertionFailure() << "Expected 'k' to be "
<< k_expected_hex
@@ -470,7 +455,7 @@ absl::optional<base::DictionaryValue> GetJwkDictionary(
return ::testing::AssertionFailure() << "'n' does not match the expected "
"value";
}
- // TODO(padolph): LowerCaseEqualsASCII() does not work for above!
+ // TODO(padolph): EqualsCaseInsensitiveASCII() does not work for above!
// ---- e
if (!dict.value().GetString("e", &value_string))
@@ -478,7 +463,7 @@ absl::optional<base::DictionaryValue> GetJwkDictionary(
std::string e_value;
if (!Base64DecodeUrlSafe(value_string, &e_value))
return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(e) failed";
- if (!base::LowerCaseEqualsASCII(
+ if (!base::EqualsCaseInsensitiveASCII(
base::HexEncode(e_value.data(), e_value.size()), e_expected_hex)) {
return ::testing::AssertionFailure() << "Expected 'e' to be "
<< e_expected_hex
@@ -529,9 +514,8 @@ void ImportExportJwkSymmetricKey(
EXPECT_TRUE(VerifySecretJwk(json, jwk_alg, key_hex, usages));
// Import the JWK-formatted key.
- ASSERT_EQ(Status::Success(),
- ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(json),
- import_algorithm, true, usages, &key));
+ ASSERT_EQ(Status::Success(), ImportKey(blink::kWebCryptoKeyFormatJwk, json,
+ import_algorithm, true, usages, &key));
EXPECT_TRUE(key.Handle());
EXPECT_EQ(blink::kWebCryptoKeyTypeSecret, key.GetType());
EXPECT_EQ(import_algorithm.Id(), key.Algorithm().Id());
diff --git a/chromium/components/webcrypto/algorithms/test_helpers.h b/chromium/components/webcrypto/algorithms/test_helpers.h
index 59fd3f390b2..77cbac51bed 100644
--- a/chromium/components/webcrypto/algorithms/test_helpers.h
+++ b/chromium/components/webcrypto/algorithms/test_helpers.h
@@ -12,14 +12,17 @@
#include <string>
#include <vector>
+#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/platform/web_crypto_algorithm.h"
#include "third_party/blink/public/platform/web_crypto_key.h"
+// Compare the input in hex, because `base::span` supports neither equality nor
+// printing.
#define EXPECT_BYTES_EQ(expected, actual) \
- EXPECT_EQ(CryptoData(expected), CryptoData(actual))
+ EXPECT_EQ(base::HexEncode(expected), base::HexEncode(actual))
#define EXPECT_BYTES_EQ_HEX(expected_hex, actual_bytes) \
EXPECT_BYTES_EQ(HexStringToBytes(expected_hex), actual_bytes)
@@ -45,19 +48,13 @@ class WebCryptoTestBase : public testing::Test {
};
class Status;
-class CryptoData;
// These functions are used by GTEST to support EXPECT_EQ() for
-// webcrypto::Status and webcrypto::CryptoData
-
+// webcrypto::Status.
void PrintTo(const Status& status, ::std::ostream* os);
bool operator==(const Status& a, const Status& b);
bool operator!=(const Status& a, const Status& b);
-void PrintTo(const CryptoData& data, ::std::ostream* os);
-bool operator==(const CryptoData& a, const CryptoData& b);
-bool operator!=(const CryptoData& a, const CryptoData& b);
-
// Gives a human-readable description of |status| and any error it represents.
std::string StatusToString(const Status& status);
diff --git a/chromium/components/webcrypto/algorithms/util.cc b/chromium/components/webcrypto/algorithms/util.cc
index 557e6cc55b3..ef747a32b53 100644
--- a/chromium/components/webcrypto/algorithms/util.cc
+++ b/chromium/components/webcrypto/algorithms/util.cc
@@ -5,7 +5,6 @@
#include "components/webcrypto/algorithms/util.h"
#include "base/check_op.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "third_party/boringssl/src/include/openssl/aead.h"
@@ -60,17 +59,12 @@ bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
return (a & b) == b;
}
-BIGNUM* CreateBIGNUM(const std::string& n) {
- return BN_bin2bn(reinterpret_cast<const uint8_t*>(n.data()), n.size(),
- nullptr);
-}
-
Status AeadEncryptDecrypt(EncryptOrDecrypt mode,
- const std::vector<uint8_t>& raw_key,
- const CryptoData& data,
+ base::span<const uint8_t> raw_key,
+ base::span<const uint8_t> data,
unsigned int tag_length_bytes,
- const CryptoData& iv,
- const CryptoData& additional_data,
+ base::span<const uint8_t> iv,
+ base::span<const uint8_t> additional_data,
const EVP_AEAD* aead_alg,
std::vector<uint8_t>* buffer) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
@@ -88,24 +82,22 @@ Status AeadEncryptDecrypt(EncryptOrDecrypt mode,
int ok;
if (mode == DECRYPT) {
- if (data.byte_length() < tag_length_bytes)
+ if (data.size() < tag_length_bytes)
return Status::ErrorDataTooSmall();
- buffer->resize(data.byte_length() - tag_length_bytes);
+ buffer->resize(data.size() - tag_length_bytes);
ok = EVP_AEAD_CTX_open(ctx.get(), buffer->data(), &len, buffer->size(),
- iv.bytes(), iv.byte_length(), data.bytes(),
- data.byte_length(), additional_data.bytes(),
- additional_data.byte_length());
+ iv.data(), iv.size(), data.data(), data.size(),
+ additional_data.data(), additional_data.size());
} else {
// No need to check for unsigned integer overflow here (seal fails if
// the output buffer is too small).
- buffer->resize(data.byte_length() + EVP_AEAD_max_overhead(aead_alg));
+ buffer->resize(data.size() + EVP_AEAD_max_overhead(aead_alg));
ok = EVP_AEAD_CTX_seal(ctx.get(), buffer->data(), &len, buffer->size(),
- iv.bytes(), iv.byte_length(), data.bytes(),
- data.byte_length(), additional_data.bytes(),
- additional_data.byte_length());
+ iv.data(), iv.size(), data.data(), data.size(),
+ additional_data.data(), additional_data.size());
}
if (!ok)
diff --git a/chromium/components/webcrypto/algorithms/util.h b/chromium/components/webcrypto/algorithms/util.h
index ffa70b09d20..7e95bf696f2 100644
--- a/chromium/components/webcrypto/algorithms/util.h
+++ b/chromium/components/webcrypto/algorithms/util.h
@@ -11,6 +11,7 @@
#include <stddef.h>
#include <stdint.h>
+#include "base/containers/span.h"
#include "third_party/blink/public/platform/web_crypto_algorithm.h"
#include "third_party/blink/public/platform/web_crypto_key.h"
#include "third_party/boringssl/src/include/openssl/base.h"
@@ -20,7 +21,6 @@
namespace webcrypto {
-class CryptoData;
class Status;
// Returns the EVP_MD that corresponds with |hash_algorithm|, or nullptr on
@@ -63,9 +63,6 @@ Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages,
bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
blink::WebCryptoKeyUsageMask b);
-// Allocates a new BIGNUM given a std::string big-endian representation.
-BIGNUM* CreateBIGNUM(const std::string& n);
-
// The values of these constants correspond with the "enc" parameter of
// EVP_CipherInit_ex(), do not change.
enum EncryptOrDecrypt { DECRYPT = 0, ENCRYPT = 1 };
@@ -75,11 +72,11 @@ enum EncryptOrDecrypt { DECRYPT = 0, ENCRYPT = 1 };
// * |aead_alg| the algorithm (for instance AES-GCM)
// * |buffer| where the ciphertext or plaintext is written to.
Status AeadEncryptDecrypt(EncryptOrDecrypt mode,
- const std::vector<uint8_t>& raw_key,
- const CryptoData& data,
+ base::span<const uint8_t> raw_key,
+ base::span<const uint8_t> data,
unsigned int tag_length_bytes,
- const CryptoData& iv,
- const CryptoData& additional_data,
+ base::span<const uint8_t> iv,
+ base::span<const uint8_t> additional_data,
const EVP_AEAD* aead_alg,
std::vector<uint8_t>* buffer);
diff --git a/chromium/components/webcrypto/algorithms/x25519.cc b/chromium/components/webcrypto/algorithms/x25519.cc
deleted file mode 100644
index aba892eeebc..00000000000
--- a/chromium/components/webcrypto/algorithms/x25519.cc
+++ /dev/null
@@ -1,36 +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 "components/webcrypto/algorithm_implementation.h"
-#include "components/webcrypto/status.h"
-
-namespace webcrypto {
-
-namespace {
-
-// This class implements the key agreement algorithm using the X25519 function
-// based on Curve25519 specified by RFC 7748, to which is also referred as the
-// X25519 algorithm by RFC 8410.
-//
-// TODO(crbug.com/1032821): See also
-// https://chromestatus.com/feature/4913922408710144.
-class X25519Implementation : public AlgorithmImplementation {
- public:
- Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
- const blink::WebCryptoKey& base_key,
- bool has_optional_length_bits,
- unsigned int optional_length_bits,
- std::vector<uint8_t>* derived_bytes) const override {
- // TODO(crbug.com/1032821): Implement this.
- return Status::ErrorUnsupported();
- }
-};
-
-} // namespace
-
-std::unique_ptr<AlgorithmImplementation> CreateX25519Implementation() {
- return std::make_unique<X25519Implementation>();
-}
-
-} // namespace webcrypto
diff --git a/chromium/components/webcrypto/blink_key_handle.cc b/chromium/components/webcrypto/blink_key_handle.cc
index ef292be5dbe..dbb15d0d067 100644
--- a/chromium/components/webcrypto/blink_key_handle.cc
+++ b/chromium/components/webcrypto/blink_key_handle.cc
@@ -7,8 +7,6 @@
#include <utility>
#include "base/check_op.h"
-#include "components/webcrypto/crypto_data.h"
-#include "components/webcrypto/status.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
namespace webcrypto {
@@ -20,56 +18,33 @@ class AsymKey;
// Base class for wrapping OpenSSL keys in a type that can be passed to
// Blink (blink::WebCryptoKeyHandle).
-//
-// In addition to the key's internal OpenSSL representation (EVP_PKEY or just
-// raw bytes), each key maintains a copy of its serialized form in either
-// 'raw', 'pkcs8', or 'spki' format. This is to allow structured cloning of
-// keys to be done synchronously from the target Blink thread, without having to
-// lock access to the key throughout the code.
-//
-// TODO(eroman): Should be able to do the key export needed for structured
-// clone synchronously.
class Key : public blink::WebCryptoKeyHandle {
public:
- explicit Key(const CryptoData& serialized_key_data)
- : serialized_key_data_(
- serialized_key_data.bytes(),
- serialized_key_data.bytes() + serialized_key_data.byte_length()) {}
-
- ~Key() override {}
-
// Helpers to add some safety to casting.
virtual SymKey* AsSymKey() { return nullptr; }
virtual AsymKey* AsAsymKey() { return nullptr; }
-
- const std::vector<uint8_t>& serialized_key_data() const {
- return serialized_key_data_;
- }
-
- private:
- const std::vector<uint8_t> serialized_key_data_;
};
class SymKey : public Key {
public:
- explicit SymKey(const CryptoData& raw_key_data) : Key(raw_key_data) {}
+ explicit SymKey(base::span<const uint8_t> raw_key_data)
+ : raw_key_data_(raw_key_data.begin(), raw_key_data.end()) {}
SymKey(const SymKey&) = delete;
SymKey& operator=(const SymKey&) = delete;
SymKey* AsSymKey() override { return this; }
- const std::vector<uint8_t>& raw_key_data() const {
- return serialized_key_data();
- }
+ const std::vector<uint8_t>& raw_key_data() const { return raw_key_data_; }
+
+ private:
+ std::vector<uint8_t> raw_key_data_;
};
class AsymKey : public Key {
public:
// After construction the |pkey| should NOT be mutated.
- AsymKey(bssl::UniquePtr<EVP_PKEY> pkey,
- const std::vector<uint8_t>& serialized_key_data)
- : Key(CryptoData(serialized_key_data)), pkey_(std::move(pkey)) {}
+ explicit AsymKey(bssl::UniquePtr<EVP_PKEY> pkey) : pkey_(std::move(pkey)) {}
AsymKey(const AsymKey&) = delete;
AsymKey& operator=(const AsymKey&) = delete;
@@ -100,20 +75,14 @@ EVP_PKEY* GetEVP_PKEY(const blink::WebCryptoKey& key) {
return GetKey(key)->AsAsymKey()->pkey();
}
-const std::vector<uint8_t>& GetSerializedKeyData(
- const blink::WebCryptoKey& key) {
- return GetKey(key)->serialized_key_data();
-}
-
blink::WebCryptoKeyHandle* CreateSymmetricKeyHandle(
- const CryptoData& key_bytes) {
+ base::span<const uint8_t> key_bytes) {
return new SymKey(key_bytes);
}
blink::WebCryptoKeyHandle* CreateAsymmetricKeyHandle(
- bssl::UniquePtr<EVP_PKEY> pkey,
- const std::vector<uint8_t>& serialized_key_data) {
- return new AsymKey(std::move(pkey), serialized_key_data);
+ bssl::UniquePtr<EVP_PKEY> pkey) {
+ return new AsymKey(std::move(pkey));
}
} // namespace webcrypto
diff --git a/chromium/components/webcrypto/blink_key_handle.h b/chromium/components/webcrypto/blink_key_handle.h
index 5c5a624f2ec..f6ae1aea538 100644
--- a/chromium/components/webcrypto/blink_key_handle.h
+++ b/chromium/components/webcrypto/blink_key_handle.h
@@ -9,6 +9,7 @@
#include <vector>
+#include "base/containers/span.h"
#include "third_party/blink/public/platform/web_crypto_key.h"
#include "third_party/boringssl/src/include/openssl/base.h"
@@ -22,8 +23,6 @@
namespace webcrypto {
-class CryptoData;
-
// Returns a reference to the symmetric key data wrapped by the given Blink
// key. The returned reference is owned by |key|. This function must only be
// called on secret keys (HMAC, AES, etc).
@@ -34,26 +33,15 @@ const std::vector<uint8_t>& GetSymmetricKeyData(const blink::WebCryptoKey& key);
// (RSA, EC, etc).
EVP_PKEY* GetEVP_PKEY(const blink::WebCryptoKey& key);
-// Returns a reference to the serialized key data. This reference is owned by
-// |key|. This function can be called for any key type.
-const std::vector<uint8_t>& GetSerializedKeyData(
- const blink::WebCryptoKey& key);
-
// Creates a symmetric key handle that can be passed to Blink. The caller takes
// ownership of the returned pointer.
blink::WebCryptoKeyHandle* CreateSymmetricKeyHandle(
- const CryptoData& key_bytes);
+ base::span<const uint8_t> key_bytes);
// Creates an asymmetric key handle that can be passed to Blink. The caller
-// takes
-// ownership of the returned pointer.
-//
-// TODO(eroman): This should _move_ input serialized_key_data rather than
-// create a copy, since all the callers are passing in vectors that are later
-// thrown away anyway.
+// takes ownership of the returned pointer.
blink::WebCryptoKeyHandle* CreateAsymmetricKeyHandle(
- bssl::UniquePtr<EVP_PKEY> pkey,
- const std::vector<uint8_t>& serialized_key_data);
+ bssl::UniquePtr<EVP_PKEY> pkey);
} // namespace webcrypto
diff --git a/chromium/components/webcrypto/crypto_data.cc b/chromium/components/webcrypto/crypto_data.cc
deleted file mode 100644
index 4004f5ea80d..00000000000
--- a/chromium/components/webcrypto/crypto_data.cc
+++ /dev/null
@@ -1,28 +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.
-
-#include "components/webcrypto/crypto_data.h"
-
-namespace webcrypto {
-
-CryptoData::CryptoData() : bytes_(nullptr), byte_length_(0) {}
-
-CryptoData::CryptoData(const unsigned char* bytes, unsigned int byte_length)
- : bytes_(bytes), byte_length_(byte_length) {
-}
-
-CryptoData::CryptoData(const std::vector<unsigned char>& bytes)
- : bytes_(bytes.data()),
- byte_length_(static_cast<unsigned int>(bytes.size())) {}
-
-CryptoData::CryptoData(const std::string& bytes)
- : bytes_(bytes.size() ? reinterpret_cast<const unsigned char*>(bytes.data())
- : nullptr),
- byte_length_(static_cast<unsigned int>(bytes.size())) {}
-
-CryptoData::CryptoData(const blink::WebVector<unsigned char>& bytes)
- : bytes_(bytes.Data()),
- byte_length_(static_cast<unsigned int>(bytes.size())) {}
-
-} // namespace webcrypto
diff --git a/chromium/components/webcrypto/crypto_data.h b/chromium/components/webcrypto/crypto_data.h
deleted file mode 100644
index 31f3546a1c3..00000000000
--- a/chromium/components/webcrypto/crypto_data.h
+++ /dev/null
@@ -1,43 +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.
-
-#ifndef COMPONENTS_WEBCRYPTO_CRYPTO_DATA_H_
-#define COMPONENTS_WEBCRYPTO_CRYPTO_DATA_H_
-
-#include <string>
-#include <vector>
-
-#include "third_party/blink/public/platform/web_vector.h"
-
-namespace webcrypto {
-
-// Helper to pass around a range of immutable bytes. This is conceptually
-// similar to base::StringPiece, but for crypto byte data.
-//
-// The data used at construction is NOT owned, and should remain valid as long
-// as the CryptoData is being used.
-class CryptoData {
- public:
- // Constructs empty data.
- CryptoData();
-
- CryptoData(const unsigned char* bytes, unsigned int byte_length);
-
- // These constructors do NOT copy the data. Hence the base pointer should
- // remain valid for the lifetime of CryptoData.
- explicit CryptoData(const std::vector<unsigned char>& bytes);
- explicit CryptoData(const std::string& bytes);
- explicit CryptoData(const blink::WebVector<unsigned char>& bytes);
-
- const unsigned char* bytes() const { return bytes_; }
- unsigned int byte_length() const { return byte_length_; }
-
- private:
- const unsigned char* const bytes_;
- const unsigned int byte_length_;
-};
-
-} // namespace webcrypto
-
-#endif // COMPONENTS_WEBCRYPTO_CRYPTO_DATA_H_
diff --git a/chromium/components/webcrypto/fuzzer_support.cc b/chromium/components/webcrypto/fuzzer_support.cc
index 15623285012..e90e72b0778 100644
--- a/chromium/components/webcrypto/fuzzer_support.cc
+++ b/chromium/components/webcrypto/fuzzer_support.cc
@@ -5,11 +5,10 @@
#include "components/webcrypto/fuzzer_support.h"
#include "base/command_line.h"
+#include "base/containers/span.h"
#include "base/lazy_instance.h"
-#include "base/numerics/safe_conversions.h"
#include "base/task/single_thread_task_executor.h"
#include "components/webcrypto/algorithm_dispatch.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "mojo/core/embedder/embedder.h"
#include "third_party/blink/public/platform/platform.h"
@@ -90,7 +89,7 @@ void ImportEcKeyFromDerFuzzData(const uint8_t* data,
blink::WebCryptoKey key;
webcrypto::Status status = webcrypto::ImportKey(
- format, webcrypto::CryptoData(data, base::checked_cast<uint32_t>(size)),
+ format, base::make_span(data, size),
CreateEcImportAlgorithm(algorithm_id, curve), true, usages, &key);
// These errors imply a bad setup of parameters, and means ImportKey() may not
@@ -135,8 +134,7 @@ void ImportEcKeyFromRawFuzzData(const uint8_t* data, size_t size) {
blink::WebCryptoKey key;
webcrypto::Status status = webcrypto::ImportKey(
- blink::kWebCryptoKeyFormatRaw,
- webcrypto::CryptoData(data, base::checked_cast<uint32_t>(size)),
+ blink::kWebCryptoKeyFormatRaw, base::make_span(data, size),
CreateEcImportAlgorithm(algorithm_id, curve), true, usages, &key);
// These errors imply a bad setup of parameters, and means ImportKey() may not
@@ -169,7 +167,7 @@ void ImportRsaKeyFromDerFuzzData(const uint8_t* data,
blink::WebCryptoKey key;
webcrypto::Status status = webcrypto::ImportKey(
- format, webcrypto::CryptoData(data, base::checked_cast<uint32_t>(size)),
+ format, base::make_span(data, size),
CreateRsaHashedImportAlgorithm(algorithm_id, hash_id), true, usages,
&key);
diff --git a/chromium/components/webcrypto/jwk.cc b/chromium/components/webcrypto/jwk.cc
index 857865368b7..2a6f31ee311 100644
--- a/chromium/components/webcrypto/jwk.cc
+++ b/chromium/components/webcrypto/jwk.cc
@@ -15,7 +15,6 @@
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "components/webcrypto/algorithms/util.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
// JSON Web Key Format (JWK) is defined by:
@@ -198,14 +197,14 @@ JwkReader::JwkReader() {
JwkReader::~JwkReader() {
}
-Status JwkReader::Init(const CryptoData& bytes,
+Status JwkReader::Init(base::span<const uint8_t> bytes,
bool expected_extractable,
blink::WebCryptoKeyUsageMask expected_usages,
const std::string& expected_kty,
const std::string& expected_alg) {
// Parse the incoming JWK JSON.
- base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()),
- bytes.byte_length());
+ base::StringPiece json_string(reinterpret_cast<const char*>(bytes.data()),
+ bytes.size());
{
// Limit the visibility for |value| as it is moved to |dict_| (via
@@ -293,7 +292,7 @@ Status JwkReader::GetOptionalList(const std::string& member_name,
}
Status JwkReader::GetBytes(const std::string& member_name,
- std::string* result) const {
+ std::vector<uint8_t>* result) const {
std::string base64_string;
Status status = GetString(member_name, &base64_string);
if (status.IsError())
@@ -301,17 +300,19 @@ Status JwkReader::GetBytes(const std::string& member_name,
// The JSON web signature spec says that padding is omitted.
// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-36#section-2
+ std::string result_str;
if (!base::Base64UrlDecode(base64_string,
base::Base64UrlDecodePolicy::DISALLOW_PADDING,
- result)) {
+ &result_str)) {
return Status::ErrorJwkBase64Decode(member_name);
}
+ result->assign(result_str.begin(), result_str.end());
return Status::Success();
}
Status JwkReader::GetBigInteger(const std::string& member_name,
- std::string* result) const {
+ std::vector<uint8_t>* result) const {
Status status = GetBytes(member_name, result);
if (status.IsError())
return status;
@@ -379,13 +380,13 @@ void JwkWriter::SetString(const std::string& member_name,
}
void JwkWriter::SetBytes(const std::string& member_name,
- const CryptoData& value) {
+ base::span<const uint8_t> value) {
// The JSON web signature spec says that padding is omitted.
// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-36#section-2
std::string base64url_encoded;
base::Base64UrlEncode(
- base::StringPiece(reinterpret_cast<const char*>(value.bytes()),
- value.byte_length()),
+ base::StringPiece(reinterpret_cast<const char*>(value.data()),
+ value.size()),
base::Base64UrlEncodePolicy::OMIT_PADDING, &base64url_encoded);
dict_.SetStringKey(member_name, base64url_encoded);
diff --git a/chromium/components/webcrypto/jwk.h b/chromium/components/webcrypto/jwk.h
index 63b4ccc33cb..fbc92eac23c 100644
--- a/chromium/components/webcrypto/jwk.h
+++ b/chromium/components/webcrypto/jwk.h
@@ -10,12 +10,12 @@
#include <memory>
#include <vector>
+#include "base/containers/span.h"
#include "base/values.h"
#include "third_party/blink/public/platform/web_crypto.h"
namespace webcrypto {
-class CryptoData;
class Status;
// Helper class for parsing a JWK from JSON.
@@ -40,7 +40,7 @@ class JwkReader {
// * Have an "alg" matching |expected_alg|
//
// NOTE: If |expected_alg| is empty, then the test on "alg" is skipped.
- Status Init(const CryptoData& bytes,
+ Status Init(base::span<const uint8_t> bytes,
bool expected_extractable,
blink::WebCryptoKeyUsageMask expected_usages,
const std::string& expected_kty,
@@ -75,14 +75,15 @@ class JwkReader {
// Extracts the required string member |member_name| and saves the
// base64url-decoded bytes to |*result|. If the member does not exist or is
// not a string, or could not be base64url-decoded, returns an error.
- Status GetBytes(const std::string& member_name, std::string* result) const;
+ Status GetBytes(const std::string& member_name,
+ std::vector<uint8_t>* result) const;
// Extracts the required base64url member, which is interpreted as being a
// big-endian unsigned integer.
//
// Sequences that contain leading zeros will be rejected.
Status GetBigInteger(const std::string& member_name,
- std::string* result) const;
+ std::vector<uint8_t>* result) const;
// Extracts the optional boolean member |member_name| and saves the result to
// |*result| if it was found. If the member exists and is not a boolean,
@@ -117,7 +118,8 @@ class JwkWriter {
void SetString(const std::string& member_name, const std::string& value);
// Sets a bytes member |value| to |value| by base64 url-safe encoding it.
- void SetBytes(const std::string& member_name, const CryptoData& value);
+ void SetBytes(const std::string& member_name,
+ base::span<const uint8_t> value);
// Flattens the JWK to JSON (UTF-8 encoded if necessary, however in practice
// it will be ASCII).
diff --git a/chromium/components/webcrypto/status_unittest.cc b/chromium/components/webcrypto/status_unittest.cc
index 27dd89d198d..a59ea2fe26d 100644
--- a/chromium/components/webcrypto/status_unittest.cc
+++ b/chromium/components/webcrypto/status_unittest.cc
@@ -5,7 +5,6 @@
#include "components/webcrypto/status.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
-#include "components/webcrypto/crypto_data.h"
#include "third_party/blink/public/platform/web_crypto_algorithm.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
diff --git a/chromium/components/webcrypto/webcrypto_impl.cc b/chromium/components/webcrypto/webcrypto_impl.cc
index 0383e3106d8..e217bac6fc0 100644
--- a/chromium/components/webcrypto/webcrypto_impl.cc
+++ b/chromium/components/webcrypto/webcrypto_impl.cc
@@ -12,6 +12,7 @@
#include "base/bind.h"
#include "base/check_op.h"
+#include "base/containers/span.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/task/single_thread_task_runner.h"
@@ -20,7 +21,6 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "components/webcrypto/algorithm_dispatch.h"
-#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/status.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
@@ -389,9 +389,8 @@ void DoEncrypt(std::unique_ptr<EncryptState> passed_state) {
EncryptState* state = passed_state.get();
if (state->cancelled())
return;
- state->status =
- webcrypto::Encrypt(state->algorithm, state->key,
- webcrypto::CryptoData(state->data), &state->buffer);
+ state->status = webcrypto::Encrypt(state->algorithm, state->key, state->data,
+ &state->buffer);
state->origin_thread->PostTask(
FROM_HERE, base::BindOnce(DoEncryptReply, std::move(passed_state)));
}
@@ -407,9 +406,8 @@ void DoDecrypt(std::unique_ptr<DecryptState> passed_state) {
DecryptState* state = passed_state.get();
if (state->cancelled())
return;
- state->status =
- webcrypto::Decrypt(state->algorithm, state->key,
- webcrypto::CryptoData(state->data), &state->buffer);
+ state->status = webcrypto::Decrypt(state->algorithm, state->key, state->data,
+ &state->buffer);
state->origin_thread->PostTask(
FROM_HERE, base::BindOnce(DoDecryptReply, std::move(passed_state)));
}
@@ -424,8 +422,8 @@ void DoDigest(std::unique_ptr<DigestState> passed_state) {
DigestState* state = passed_state.get();
if (state->cancelled())
return;
- state->status = webcrypto::Digest(
- state->algorithm, webcrypto::CryptoData(state->data), &state->buffer);
+ state->status =
+ webcrypto::Digest(state->algorithm, state->data, &state->buffer);
state->origin_thread->PostTask(
FROM_HERE, base::BindOnce(DoDigestReply, std::move(passed_state)));
}
@@ -463,9 +461,9 @@ void DoImportKey(std::unique_ptr<ImportKeyState> passed_state) {
ImportKeyState* state = passed_state.get();
if (state->cancelled())
return;
- state->status = webcrypto::ImportKey(
- state->format, webcrypto::CryptoData(state->key_data), state->algorithm,
- state->extractable, state->usages, &state->key);
+ state->status =
+ webcrypto::ImportKey(state->format, state->key_data, state->algorithm,
+ state->extractable, state->usages, &state->key);
if (state->status.IsSuccess()) {
DCHECK(state->key.Handle());
DCHECK(!state->key.Algorithm().IsNull());
@@ -514,9 +512,8 @@ void DoSign(std::unique_ptr<SignState> passed_state) {
SignState* state = passed_state.get();
if (state->cancelled())
return;
- state->status =
- webcrypto::Sign(state->algorithm, state->key,
- webcrypto::CryptoData(state->data), &state->buffer);
+ state->status = webcrypto::Sign(state->algorithm, state->key, state->data,
+ &state->buffer);
state->origin_thread->PostTask(
FROM_HERE, base::BindOnce(DoSignReply, std::move(passed_state)));
@@ -536,9 +533,9 @@ void DoVerify(std::unique_ptr<VerifySignatureState> passed_state) {
VerifySignatureState* state = passed_state.get();
if (state->cancelled())
return;
- state->status = webcrypto::Verify(
- state->algorithm, state->key, webcrypto::CryptoData(state->signature),
- webcrypto::CryptoData(state->data), &state->verify_result);
+ state->status =
+ webcrypto::Verify(state->algorithm, state->key, state->signature,
+ state->data, &state->verify_result);
state->origin_thread->PostTask(
FROM_HERE, base::BindOnce(DoVerifyReply, std::move(passed_state)));
@@ -575,10 +572,9 @@ void DoUnwrapKey(std::unique_ptr<UnwrapKeyState> passed_state) {
if (state->cancelled())
return;
state->status = webcrypto::UnwrapKey(
- state->format, webcrypto::CryptoData(state->wrapped_key),
- state->wrapping_key, state->unwrap_algorithm,
- state->unwrapped_key_algorithm, state->extractable, state->usages,
- &state->unwrapped_key);
+ state->format, state->wrapped_key, state->wrapping_key,
+ state->unwrap_algorithm, state->unwrapped_key_algorithm,
+ state->extractable, state->usages, &state->unwrapped_key);
state->origin_thread->PostTask(
FROM_HERE, base::BindOnce(DoUnwrapKeyReply, std::move(passed_state)));
@@ -637,6 +633,8 @@ void WebCryptoImpl::Encrypt(
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(!algorithm.IsNull());
+ if (result.Cancelled())
+ return;
std::unique_ptr<EncryptState> state(new EncryptState(
algorithm, key, std::move(data), result, std::move(task_runner)));
@@ -653,6 +651,8 @@ void WebCryptoImpl::Decrypt(
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(!algorithm.IsNull());
+ if (result.Cancelled())
+ return;
std::unique_ptr<DecryptState> state(new DecryptState(
algorithm, key, std::move(data), result, std::move(task_runner)));
@@ -668,6 +668,8 @@ void WebCryptoImpl::Digest(
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(!algorithm.IsNull());
+ if (result.Cancelled())
+ return;
std::unique_ptr<DigestState> state(
new DigestState(algorithm, blink::WebCryptoKey::CreateNull(),
@@ -685,6 +687,8 @@ void WebCryptoImpl::GenerateKey(
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(!algorithm.IsNull());
+ if (result.Cancelled())
+ return;
std::unique_ptr<GenerateKeyState> state(new GenerateKeyState(
algorithm, extractable, usages, result, std::move(task_runner)));
@@ -702,6 +706,8 @@ void WebCryptoImpl::ImportKey(
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ if (result.Cancelled())
+ return;
std::unique_ptr<ImportKeyState> state(
new ImportKeyState(format, std::move(key_data), algorithm, extractable,
usages, result, std::move(task_runner)));
@@ -716,6 +722,8 @@ void WebCryptoImpl::ExportKey(
const blink::WebCryptoKey& key,
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ if (result.Cancelled())
+ return;
std::unique_ptr<ExportKeyState> state(
new ExportKeyState(format, key, result, std::move(task_runner)));
if (!CryptoThreadPool::PostTask(
@@ -730,6 +738,8 @@ void WebCryptoImpl::Sign(
blink::WebVector<unsigned char> data,
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ if (result.Cancelled())
+ return;
std::unique_ptr<SignState> state(new SignState(
algorithm, key, std::move(data), result, std::move(task_runner)));
if (!CryptoThreadPool::PostTask(FROM_HERE,
@@ -745,6 +755,8 @@ void WebCryptoImpl::VerifySignature(
blink::WebVector<unsigned char> data,
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ if (result.Cancelled())
+ return;
std::unique_ptr<VerifySignatureState> state(new VerifySignatureState(
algorithm, key, std::move(signature), std::move(data), result,
std::move(task_runner)));
@@ -761,6 +773,8 @@ void WebCryptoImpl::WrapKey(
const blink::WebCryptoAlgorithm& wrap_algorithm,
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ if (result.Cancelled())
+ return;
std::unique_ptr<WrapKeyState> state(
new WrapKeyState(format, key, wrapping_key, wrap_algorithm, result,
std::move(task_runner)));
@@ -780,6 +794,8 @@ void WebCryptoImpl::UnwrapKey(
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ if (result.Cancelled())
+ return;
std::unique_ptr<UnwrapKeyState> state(
new UnwrapKeyState(format, std::move(wrapped_key), wrapping_key,
unwrap_algorithm, unwrapped_key_algorithm, extractable,
@@ -796,6 +812,8 @@ void WebCryptoImpl::DeriveBits(
unsigned int length_bits,
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ if (result.Cancelled())
+ return;
std::unique_ptr<DeriveBitsState> state(new DeriveBitsState(
algorithm, base_key, length_bits, result, std::move(task_runner)));
if (!CryptoThreadPool::PostTask(
@@ -813,6 +831,8 @@ void WebCryptoImpl::DeriveKey(
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoResult result,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ if (result.Cancelled())
+ return;
std::unique_ptr<DeriveKeyState> state(new DeriveKeyState(
algorithm, base_key, import_algorithm, key_length_algorithm, extractable,
usages, result, std::move(task_runner)));
@@ -832,7 +852,7 @@ bool WebCryptoImpl::DeserializeKeyForClone(
blink::WebCryptoKey& key) {
return webcrypto::DeserializeKeyForClone(
algorithm, type, extractable, usages,
- webcrypto::CryptoData(key_data, key_data_size), &key);
+ base::make_span(key_data, key_data_size), &key);
}
bool WebCryptoImpl::SerializeKeyForClone(
diff --git a/chromium/components/webrtc/fake_ssl_client_socket.cc b/chromium/components/webrtc/fake_ssl_client_socket.cc
index e41bac585d5..89969b0abd2 100644
--- a/chromium/components/webrtc/fake_ssl_client_socket.cc
+++ b/chromium/components/webrtc/fake_ssl_client_socket.cc
@@ -361,11 +361,6 @@ bool FakeSSLClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
return transport_socket_->GetSSLInfo(ssl_info);
}
-void FakeSSLClientSocket::GetConnectionAttempts(
- net::ConnectionAttempts* out) const {
- out->clear();
-}
-
int64_t FakeSSLClientSocket::GetTotalReceivedBytes() const {
NOTIMPLEMENTED();
return 0;
diff --git a/chromium/components/webrtc/fake_ssl_client_socket.h b/chromium/components/webrtc/fake_ssl_client_socket.h
index cb399359fbd..576a3f1f1e4 100644
--- a/chromium/components/webrtc/fake_ssl_client_socket.h
+++ b/chromium/components/webrtc/fake_ssl_client_socket.h
@@ -73,10 +73,6 @@ class FakeSSLClientSocket : public net::StreamSocket {
bool WasAlpnNegotiated() const override;
net::NextProto GetNegotiatedProtocol() const override;
bool GetSSLInfo(net::SSLInfo* ssl_info) override;
- void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
- void ClearConnectionAttempts() override {}
- void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override {
- }
int64_t GetTotalReceivedBytes() const override;
void ApplySocketTag(const net::SocketTag& tag) override;
diff --git a/chromium/components/webrtc/fake_ssl_client_socket_unittest.cc b/chromium/components/webrtc/fake_ssl_client_socket_unittest.cc
index 46468bde309..8c14162ec49 100644
--- a/chromium/components/webrtc/fake_ssl_client_socket_unittest.cc
+++ b/chromium/components/webrtc/fake_ssl_client_socket_unittest.cc
@@ -77,9 +77,6 @@ class MockClientSocket : public net::StreamSocket {
MOCK_CONST_METHOD0(WasAlpnNegotiated, bool());
MOCK_CONST_METHOD0(GetNegotiatedProtocol, net::NextProto());
MOCK_METHOD1(GetSSLInfo, bool(net::SSLInfo*));
- MOCK_CONST_METHOD1(GetConnectionAttempts, void(net::ConnectionAttempts*));
- MOCK_METHOD0(ClearConnectionAttempts, void());
- MOCK_METHOD1(AddConnectionAttempts, void(const net::ConnectionAttempts&));
MOCK_CONST_METHOD0(GetTotalReceivedBytes, int64_t());
MOCK_METHOD1(ApplySocketTag, void(const net::SocketTag&));
};
diff --git a/chromium/components/webrtc/media_stream_device_enumerator.h b/chromium/components/webrtc/media_stream_device_enumerator.h
index f148f444a37..eee46f4353c 100644
--- a/chromium/components/webrtc/media_stream_device_enumerator.h
+++ b/chromium/components/webrtc/media_stream_device_enumerator.h
@@ -8,7 +8,7 @@
#include <string>
#include "third_party/blink/public/common/mediastream/media_stream_request.h"
-#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-forward.h"
namespace content {
class BrowserContext;
@@ -34,7 +34,7 @@ class MediaStreamDeviceEnumerator {
content::BrowserContext* context,
bool audio,
bool video,
- blink::MediaStreamDevices* devices) = 0;
+ blink::mojom::StreamDevices& devices) = 0;
// Helpers for picking particular requested devices, identified by raw id.
// If the device requested is not available it will return nullptr.
diff --git a/chromium/components/webrtc/media_stream_device_enumerator_impl.cc b/chromium/components/webrtc/media_stream_device_enumerator_impl.cc
index e11d49a2dcb..f2e2bbca99b 100644
--- a/chromium/components/webrtc/media_stream_device_enumerator_impl.cc
+++ b/chromium/components/webrtc/media_stream_device_enumerator_impl.cc
@@ -12,6 +12,7 @@
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/media_capture_devices.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
using blink::MediaStreamDevices;
using content::BrowserThread;
@@ -49,18 +50,18 @@ void MediaStreamDeviceEnumeratorImpl::GetDefaultDevicesForBrowserContext(
content::BrowserContext* context,
bool audio,
bool video,
- MediaStreamDevices* devices) {
+ blink::mojom::StreamDevices& devices) {
std::string default_device;
if (audio) {
const MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
if (!audio_devices.empty())
- devices->push_back(audio_devices.front());
+ devices.audio_device = audio_devices.front();
}
if (video) {
const MediaStreamDevices& video_devices = GetVideoCaptureDevices();
if (!video_devices.empty())
- devices->push_back(video_devices.front());
+ devices.video_device = video_devices.front();
}
}
diff --git a/chromium/components/webrtc/media_stream_device_enumerator_impl.h b/chromium/components/webrtc/media_stream_device_enumerator_impl.h
index 0f1f0f277b4..3dec2be6ef3 100644
--- a/chromium/components/webrtc/media_stream_device_enumerator_impl.h
+++ b/chromium/components/webrtc/media_stream_device_enumerator_impl.h
@@ -6,6 +6,7 @@
#define COMPONENTS_WEBRTC_MEDIA_STREAM_DEVICE_ENUMERATOR_IMPL_H_
#include "components/webrtc/media_stream_device_enumerator.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-forward.h"
namespace webrtc {
@@ -27,7 +28,7 @@ class MediaStreamDeviceEnumeratorImpl : public MediaStreamDeviceEnumerator {
content::BrowserContext* context,
bool audio,
bool video,
- blink::MediaStreamDevices* devices) override;
+ blink::mojom::StreamDevices& devices) override;
const blink::MediaStreamDevice* GetRequestedAudioDevice(
const std::string& requested_audio_device_id) override;
const blink::MediaStreamDevice* GetRequestedVideoDevice(
diff --git a/chromium/components/webrtc/media_stream_devices_controller.cc b/chromium/components/webrtc/media_stream_devices_controller.cc
index 4f0b40519c5..353ed84da62 100644
--- a/chromium/components/webrtc/media_stream_devices_controller.cc
+++ b/chromium/components/webrtc/media_stream_devices_controller.cc
@@ -14,11 +14,15 @@
#include "components/permissions/permission_result.h"
#include "components/permissions/permissions_client.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/browser/permission_controller.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "third_party/blink/public/common/permissions/permission_utils.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
+#include "third_party/blink/public/mojom/permissions/permission_status.mojom-shared.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom.h"
#if BUILDFLAG(IS_ANDROID)
@@ -59,29 +63,44 @@ void MediaStreamDevicesController::RequestPermissions(
// The RFH may have been destroyed by the time the request is processed.
if (!rfh) {
std::move(callback).Run(
- MediaStreamDevices(),
+ blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN, false,
{}, {});
return;
}
+
+ if (rfh->GetLastCommittedOrigin().GetURL().is_empty()) {
+ std::move(callback).Run(
+ blink::mojom::StreamDevicesSet(),
+ blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, false, {},
+ {});
+ return;
+ }
+
+ if (rfh->GetLastCommittedOrigin().GetURL() != request.security_origin) {
+ std::move(callback).Run(
+ blink::mojom::StreamDevicesSet(),
+ blink::mojom::MediaStreamRequestResult::INVALID_SECURITY_ORIGIN, false,
+ {}, {});
+ return;
+ }
+
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(rfh);
std::unique_ptr<MediaStreamDevicesController> controller(
new MediaStreamDevicesController(web_contents, enumerator, request,
std::move(callback)));
- std::vector<ContentSettingsType> content_settings_types;
+ std::vector<blink::PermissionType> permission_types;
permissions::PermissionManager* permission_manager =
permissions::PermissionsClient::Get()->GetPermissionManager(
web_contents->GetBrowserContext());
- bool will_prompt_for_audio = false;
- bool will_prompt_for_video = false;
if (controller->ShouldRequestAudio()) {
permissions::PermissionResult permission_status =
- permission_manager->GetPermissionStatusForFrame(
- ContentSettingsType::MEDIASTREAM_MIC, rfh, request.security_origin);
+ permission_manager->GetPermissionStatusForCurrentDocument(
+ ContentSettingsType::MEDIASTREAM_MIC, rfh);
if (permission_status.content_setting == CONTENT_SETTING_BLOCK) {
controller->denial_reason_ =
blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
@@ -91,15 +110,12 @@ void MediaStreamDevicesController::RequestPermissions(
return;
}
- content_settings_types.push_back(ContentSettingsType::MEDIASTREAM_MIC);
- will_prompt_for_audio =
- permission_status.content_setting == CONTENT_SETTING_ASK;
+ permission_types.push_back(blink::PermissionType::AUDIO_CAPTURE);
}
if (controller->ShouldRequestVideo()) {
permissions::PermissionResult permission_status =
- permission_manager->GetPermissionStatusForFrame(
- ContentSettingsType::MEDIASTREAM_CAMERA, rfh,
- request.security_origin);
+ permission_manager->GetPermissionStatusForCurrentDocument(
+ ContentSettingsType::MEDIASTREAM_CAMERA, rfh);
if (permission_status.content_setting == CONTENT_SETTING_BLOCK) {
controller->denial_reason_ =
blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
@@ -109,9 +125,7 @@ void MediaStreamDevicesController::RequestPermissions(
return;
}
- content_settings_types.push_back(ContentSettingsType::MEDIASTREAM_CAMERA);
- will_prompt_for_video =
- permission_status.content_setting == CONTENT_SETTING_ASK;
+ permission_types.push_back(blink::PermissionType::VIDEO_CAPTURE);
bool has_pan_tilt_zoom_camera = controller->HasAvailableDevices(
ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
@@ -121,9 +135,9 @@ void MediaStreamDevicesController::RequestPermissions(
// pan-tilt-zoom permission and there are suitable PTZ capable devices
// available.
if (request.request_pan_tilt_zoom_permission && has_pan_tilt_zoom_camera) {
- permission_status = permission_manager->GetPermissionStatusForFrame(
- ContentSettingsType::CAMERA_PAN_TILT_ZOOM, rfh,
- request.security_origin);
+ permission_status =
+ permission_manager->GetPermissionStatusForCurrentDocument(
+ ContentSettingsType::CAMERA_PAN_TILT_ZOOM, rfh);
if (permission_status.content_setting == CONTENT_SETTING_BLOCK) {
controller->denial_reason_ =
blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
@@ -131,23 +145,28 @@ void MediaStreamDevicesController::RequestPermissions(
return;
}
- content_settings_types.push_back(
- ContentSettingsType::CAMERA_PAN_TILT_ZOOM);
+ permission_types.push_back(blink::PermissionType::CAMERA_PAN_TILT_ZOOM);
}
}
- permission_manager->RequestPermissionsFromCurrentDocument(
- content_settings_types, rfh, request.user_gesture,
- base::BindOnce(
- &MediaStreamDevicesController::RequestAndroidPermissionsIfNeeded,
- web_contents, std::move(controller), will_prompt_for_audio,
- will_prompt_for_video));
+ // It is OK to ignore `request.security_origin` because it will be calculated
+ // from `render_frame_host` and we always ignore `requesting_origin` for
+ // `AUDIO_CAPTURE` and `VIDEO_CAPTURE`.
+ // `render_frame_host->GetMainFrame()->GetLastCommittedOrigin()` will be used
+ // instead.
+ rfh->GetBrowserContext()
+ ->GetPermissionController()
+ ->RequestPermissionsFromCurrentDocument(
+ permission_types, rfh, request.user_gesture,
+ base::BindOnce(
+ &MediaStreamDevicesController::PromptAnsweredGroupedRequest,
+ std::move(controller)));
}
MediaStreamDevicesController::~MediaStreamDevicesController() {
if (!callback_.is_null()) {
std::move(callback_).Run(
- MediaStreamDevices(),
+ blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN, false,
{}, {});
}
@@ -175,93 +194,6 @@ MediaStreamDevicesController::MediaStreamDevicesController(
request, &denial_reason_);
}
-void MediaStreamDevicesController::RequestAndroidPermissionsIfNeeded(
- content::WebContents* web_contents,
- std::unique_ptr<MediaStreamDevicesController> controller,
- bool did_prompt_for_audio,
- bool did_prompt_for_video,
- const std::vector<ContentSetting>& responses) {
-#if BUILDFLAG(IS_ANDROID)
- // If either audio or video was previously allowed and Chrome no longer has
- // the necessary permissions, show a infobar to attempt to address this
- // mismatch.
- std::vector<ContentSettingsType> content_settings_types;
- // The audio setting will always be the first one in the vector, if it was
- // requested.
- // If the user was already prompted for mic (|did_prompt_for_audio| flag), we
- // would have requested Android permission at that point.
- if (!did_prompt_for_audio && controller->ShouldRequestAudio() &&
- responses.front() == CONTENT_SETTING_ALLOW) {
- content_settings_types.push_back(ContentSettingsType::MEDIASTREAM_MIC);
- }
-
- // If the user was already prompted for camera (|did_prompt_for_video| flag),
- // we would have requested Android permission at that point.
- if (!did_prompt_for_video && controller->ShouldRequestVideo() &&
- responses.back() == CONTENT_SETTING_ALLOW) {
- content_settings_types.push_back(ContentSettingsType::MEDIASTREAM_CAMERA);
- }
-
- // If the user was already prompted for camera (|did_prompt_for_video| flag),
- // we would have requested Android permission at that point.
- if (!did_prompt_for_video && controller->ShouldRequestVideo() &&
- responses.back() == CONTENT_SETTING_ALLOW) {
- content_settings_types.push_back(ContentSettingsType::MEDIASTREAM_CAMERA);
- }
- if (content_settings_types.empty()) {
- controller->PromptAnsweredGroupedRequest(responses);
- return;
- }
-
- permissions::PermissionRepromptState reprompt_state =
- permissions::ShouldRepromptUserForPermissions(web_contents,
- content_settings_types);
- switch (reprompt_state) {
- case permissions::PermissionRepromptState::kNoNeed:
- controller->PromptAnsweredGroupedRequest(responses);
- return;
-
- case permissions::PermissionRepromptState::kShow:
- permissions::PermissionsClient::Get()->RepromptForAndroidPermissions(
- web_contents, content_settings_types,
- base::BindOnce(&MediaStreamDevicesController::AndroidOSPromptAnswered,
- std::move(controller), responses));
- return;
-
- case permissions::PermissionRepromptState::kCannotShow: {
- std::vector<ContentSetting> blocked_responses(responses.size(),
- CONTENT_SETTING_BLOCK);
- controller->PromptAnsweredGroupedRequest(blocked_responses);
- return;
- }
- }
-
- NOTREACHED() << "Unknown show permission infobar state.";
-#else
- controller->PromptAnsweredGroupedRequest(responses);
-#endif
-}
-
-#if BUILDFLAG(IS_ANDROID)
-// static
-void MediaStreamDevicesController::AndroidOSPromptAnswered(
- std::unique_ptr<MediaStreamDevicesController> controller,
- std::vector<ContentSetting> responses,
- bool android_prompt_granted) {
- if (!android_prompt_granted) {
- // Only permissions that were previously ALLOW for a site will have had
- // their android permissions requested. It's only in that case that we need
- // to change the setting to BLOCK to reflect that it wasn't allowed.
- for (size_t i = 0; i < responses.size(); ++i) {
- if (responses[i] == CONTENT_SETTING_ALLOW)
- responses[i] = CONTENT_SETTING_BLOCK;
- }
- }
-
- controller->PromptAnsweredGroupedRequest(responses);
-}
-#endif // BUILDFLAG(IS_ANDROID)
-
bool MediaStreamDevicesController::ShouldRequestAudio() const {
return audio_setting_ == CONTENT_SETTING_ASK;
}
@@ -270,19 +202,23 @@ bool MediaStreamDevicesController::ShouldRequestVideo() const {
return video_setting_ == CONTENT_SETTING_ASK;
}
-MediaStreamDevices MediaStreamDevicesController::GetDevices(
+blink::mojom::StreamDevicesSetPtr MediaStreamDevicesController::GetDevices(
ContentSetting audio_setting,
ContentSetting video_setting) {
bool audio_allowed = audio_setting == CONTENT_SETTING_ALLOW;
bool video_allowed = video_setting == CONTENT_SETTING_ALLOW;
+ blink::mojom::StreamDevicesSetPtr stream_devices_set =
+ blink::mojom::StreamDevicesSet::New();
if (!audio_allowed && !video_allowed)
- return MediaStreamDevices();
+ return nullptr;
- MediaStreamDevices devices;
+ // TODO(crbug.com/1300883): Generalize to multiple streams.
+ stream_devices_set->stream_devices.emplace_back(
+ blink::mojom::StreamDevices::New());
+ blink::mojom::StreamDevices& devices = *stream_devices_set->stream_devices[0];
switch (request_.request_type) {
case blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY: {
- const blink::MediaStreamDevice* device = nullptr;
// For open device request, when requested device_id is empty, pick
// the first available of the given type. If requested device_id is
// not empty, return the desired device if it's available. Otherwise,
@@ -293,13 +229,13 @@ MediaStreamDevices MediaStreamDevicesController::GetDevices(
DCHECK_EQ(blink::mojom::MediaStreamType::NO_SERVICE,
request_.video_type);
if (!request_.requested_audio_device_id.empty()) {
- device = enumerator_->GetRequestedAudioDevice(
+ devices.audio_device = *enumerator_->GetRequestedAudioDevice(
request_.requested_audio_device_id);
} else {
const blink::MediaStreamDevices& audio_devices =
enumerator_->GetAudioCaptureDevices();
if (!audio_devices.empty())
- device = &audio_devices.front();
+ devices.audio_device = audio_devices.front();
}
} else if (video_allowed &&
request_.video_type ==
@@ -308,17 +244,15 @@ MediaStreamDevices MediaStreamDevicesController::GetDevices(
request_.audio_type);
// Pepper API opens only one device at a time.
if (!request_.requested_video_device_id.empty()) {
- device = enumerator_->GetRequestedVideoDevice(
+ devices.video_device = *enumerator_->GetRequestedVideoDevice(
request_.requested_video_device_id);
} else {
const blink::MediaStreamDevices& video_devices =
enumerator_->GetVideoCaptureDevices();
if (!video_devices.empty())
- device = &video_devices.front();
+ devices.video_device = video_devices.front();
}
}
- if (device)
- devices.push_back(*device);
break;
}
case blink::MEDIA_GENERATE_STREAM: {
@@ -331,7 +265,7 @@ MediaStreamDevices MediaStreamDevicesController::GetDevices(
enumerator_->GetRequestedAudioDevice(
request_.requested_audio_device_id);
if (audio_device) {
- devices.push_back(*audio_device);
+ devices.audio_device = *audio_device;
get_default_audio_device = false;
}
}
@@ -340,7 +274,7 @@ MediaStreamDevices MediaStreamDevicesController::GetDevices(
enumerator_->GetRequestedVideoDevice(
request_.requested_video_device_id);
if (video_device) {
- devices.push_back(*video_device);
+ devices.video_device = *video_device;
get_default_video_device = false;
}
}
@@ -350,7 +284,7 @@ MediaStreamDevices MediaStreamDevicesController::GetDevices(
if (get_default_audio_device || get_default_video_device) {
enumerator_->GetDefaultDevicesForBrowserContext(
web_contents_->GetBrowserContext(), get_default_audio_device,
- get_default_video_device, &devices);
+ get_default_video_device, devices);
}
break;
}
@@ -365,7 +299,7 @@ MediaStreamDevices MediaStreamDevicesController::GetDevices(
// Get the default devices for the request.
enumerator_->GetDefaultDevicesForBrowserContext(
web_contents_->GetBrowserContext(), audio_allowed, video_allowed,
- &devices);
+ devices);
break;
}
case blink::MEDIA_DEVICE_UPDATE: {
@@ -374,14 +308,14 @@ MediaStreamDevices MediaStreamDevicesController::GetDevices(
}
} // switch
- return devices;
+ return stream_devices_set;
}
void MediaStreamDevicesController::RunCallback(
bool blocked_by_permissions_policy) {
CHECK(callback_);
- MediaStreamDevices devices;
+ blink::mojom::StreamDevicesSetPtr stream_devices_set;
// If all requested permissions are allowed then the callback should report
// success, otherwise we report |denial_reason_|.
blink::mojom::MediaStreamRequestResult request_result =
@@ -390,8 +324,16 @@ void MediaStreamDevicesController::RunCallback(
audio_setting_ == CONTENT_SETTING_DEFAULT) &&
(video_setting_ == CONTENT_SETTING_ALLOW ||
video_setting_ == CONTENT_SETTING_DEFAULT)) {
- devices = GetDevices(audio_setting_, video_setting_);
- if (devices.empty()) {
+ stream_devices_set = GetDevices(audio_setting_, video_setting_);
+ DCHECK(!stream_devices_set ||
+ stream_devices_set->stream_devices.size() <= 1u);
+ blink::mojom::StreamDevices devices;
+ if (stream_devices_set && !stream_devices_set->stream_devices.empty()) {
+ devices = *stream_devices_set->stream_devices[0];
+ }
+
+ if (!devices.audio_device.has_value() &&
+ !devices.video_device.has_value()) {
// Even if all requested permissions are allowed, if there are no devices
// at this point we still report a failure.
request_result = blink::mojom::MediaStreamRequestResult::NO_HARDWARE;
@@ -399,9 +341,10 @@ void MediaStreamDevicesController::RunCallback(
} else {
DCHECK_NE(blink::mojom::MediaStreamRequestResult::OK, denial_reason_);
request_result = denial_reason_;
+ stream_devices_set = blink::mojom::StreamDevicesSet::New();
}
- std::move(callback_).Run(devices, request_result,
+ std::move(callback_).Run(*stream_devices_set, request_result,
blocked_by_permissions_policy, audio_setting_,
video_setting_);
}
@@ -479,11 +422,13 @@ bool MediaStreamDevicesController::PermissionIsBlockedForReason(
// PermissionManager::RequestPermissions returned a denial reason.
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
request_.render_process_id, request_.render_frame_id);
+ if (rfh->GetLastCommittedOrigin().GetURL() != request_.security_origin) {
+ return false;
+ }
permissions::PermissionResult result =
permissions::PermissionsClient::Get()
->GetPermissionManager(web_contents_->GetBrowserContext())
- ->GetPermissionStatusForFrame(content_type, rfh,
- request_.security_origin);
+ ->GetPermissionStatusForCurrentDocument(content_type, rfh);
if (result.source == reason) {
DCHECK_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
return true;
@@ -492,7 +437,7 @@ bool MediaStreamDevicesController::PermissionIsBlockedForReason(
}
void MediaStreamDevicesController::PromptAnsweredGroupedRequest(
- const std::vector<ContentSetting>& responses) {
+ const std::vector<blink::mojom::PermissionStatus>& permissions_status) {
if (content::RenderFrameHost::FromID(request_.render_process_id,
request_.render_frame_id) == nullptr) {
// The frame requesting media devices was removed while we were waiting for
@@ -500,6 +445,11 @@ void MediaStreamDevicesController::PromptAnsweredGroupedRequest(
return;
}
+ std::vector<ContentSetting> responses;
+ std::transform(permissions_status.begin(), permissions_status.end(),
+ back_inserter(responses),
+ permissions::PermissionUtil::PermissionStatusToContentSetting);
+
bool need_audio = ShouldRequestAudio();
bool need_video = ShouldRequestVideo();
bool blocked_by_permissions_policy = need_audio || need_video;
diff --git a/chromium/components/webrtc/media_stream_devices_controller.h b/chromium/components/webrtc/media_stream_devices_controller.h
index 4abb8741d86..1787b41147a 100644
--- a/chromium/components/webrtc/media_stream_devices_controller.h
+++ b/chromium/components/webrtc/media_stream_devices_controller.h
@@ -16,6 +16,7 @@
#include "content/public/browser/media_stream_request.h"
#include "third_party/blink/public/common/mediastream/media_stream_request.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
+#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
namespace permissions {
enum class PermissionStatusSource;
@@ -33,11 +34,12 @@ class MediaStreamDeviceEnumerator;
// renderer.
class MediaStreamDevicesController {
public:
- typedef base::OnceCallback<void(const blink::MediaStreamDevices& devices,
- blink::mojom::MediaStreamRequestResult result,
- bool blocked_by_permissions_policy,
- ContentSetting audio_setting,
- ContentSetting video_setting)>
+ typedef base::OnceCallback<void(
+ const blink::mojom::StreamDevicesSet& stream_devices_set,
+ blink::mojom::MediaStreamRequestResult result,
+ bool blocked_by_permissions_policy,
+ ContentSetting audio_setting,
+ ContentSetting video_setting)>
ResultCallback;
// Requests the mic/camera permissions described in |request|, using
@@ -63,15 +65,7 @@ class MediaStreamDevicesController {
std::unique_ptr<MediaStreamDevicesController> controller,
bool did_prompt_for_audio,
bool did_prompt_for_video,
- const std::vector<ContentSetting>& responses);
-
-#if BUILDFLAG(IS_ANDROID)
- // Called when the Android OS-level prompt is answered.
- static void AndroidOSPromptAnswered(
- std::unique_ptr<MediaStreamDevicesController> controller,
- std::vector<ContentSetting> responses,
- bool android_prompt_granted);
-#endif // BUILDFLAG(IS_ANDROID)
+ const std::vector<blink::mojom::PermissionStatus>& responses);
// Returns true if audio/video should be requested through the
// PermissionManager. We won't try to request permission if the request is
@@ -81,8 +75,8 @@ class MediaStreamDevicesController {
// Returns a list of devices available for the request for the given
// audio/video permission settings.
- blink::MediaStreamDevices GetDevices(ContentSetting audio_setting,
- ContentSetting video_setting);
+ blink::mojom::StreamDevicesSetPtr GetDevices(ContentSetting audio_setting,
+ ContentSetting video_setting);
// Runs |callback_| with the current audio/video permission settings.
void RunCallback(bool blocked_by_permissions_policy);
@@ -103,7 +97,7 @@ class MediaStreamDevicesController {
// Called when a permission prompt is answered through the PermissionManager.
void PromptAnsweredGroupedRequest(
- const std::vector<ContentSetting>& responses);
+ const std::vector<blink::mojom::PermissionStatus>& permissions_status);
bool HasAvailableDevices(ContentSettingsType content_type,
const std::string& device_id) const;
diff --git a/chromium/components/webrtc/thread_wrapper.cc b/chromium/components/webrtc/thread_wrapper.cc
index 0d188110721..34714c5d25b 100644
--- a/chromium/components/webrtc/thread_wrapper.cc
+++ b/chromium/components/webrtc/thread_wrapper.cc
@@ -30,7 +30,7 @@ constexpr base::TimeDelta kTaskLatencySampleDuration = base::Seconds(3);
}
const base::Feature kThreadWrapperUsesMetronome{
- "ThreadWrapperUsesMetronome", base::FEATURE_DISABLED_BY_DEFAULT};
+ "ThreadWrapperUsesMetronome", base::FEATURE_ENABLED_BY_DEFAULT};
// Class intended to conditionally live for the duration of ThreadWrapper
// that periodically captures task latencies (definition in docs for
diff --git a/chromium/components/webrtc_logging/common/partial_circular_buffer.cc b/chromium/components/webrtc_logging/common/partial_circular_buffer.cc
index ee221054997..b349803ef75 100644
--- a/chromium/components/webrtc_logging/common/partial_circular_buffer.cc
+++ b/chromium/components/webrtc_logging/common/partial_circular_buffer.cc
@@ -18,7 +18,7 @@ PartialCircularBuffer::PartialCircularBuffer(void* buffer, uint32_t buffer_size)
position_(0),
total_read_(0) {
uint32_t header_size =
- buffer_data_->data - reinterpret_cast<uint8_t*>(buffer_data_);
+ buffer_data_->data - reinterpret_cast<uint8_t*>(buffer_data_.get());
data_size_ = memory_buffer_size_ - header_size;
DCHECK(buffer_data_);
@@ -38,7 +38,7 @@ PartialCircularBuffer::PartialCircularBuffer(void* buffer,
position_(0),
total_read_(0) {
uint32_t header_size =
- buffer_data_->data - reinterpret_cast<uint8_t*>(buffer_data_);
+ buffer_data_->data - reinterpret_cast<uint8_t*>(buffer_data_.get());
data_size_ = memory_buffer_size_ - header_size;
DCHECK(buffer_data_);
diff --git a/chromium/components/webrtc_logging/common/partial_circular_buffer.h b/chromium/components/webrtc_logging/common/partial_circular_buffer.h
index e78411ab23a..5ad83f6ffa7 100644
--- a/chromium/components/webrtc_logging/common/partial_circular_buffer.h
+++ b/chromium/components/webrtc_logging/common/partial_circular_buffer.h
@@ -7,6 +7,8 @@
#include <stdint.h>
+#include "base/memory/raw_ptr.h"
+
namespace webrtc_logging {
// A wrapper around a memory buffer that allows circular read and write with a
@@ -56,7 +58,7 @@ class PartialCircularBuffer {
void DoWrite(const uint8_t* input, uint32_t input_size);
// Used for reading and writing.
- BufferData* buffer_data_;
+ raw_ptr<BufferData> buffer_data_;
uint32_t memory_buffer_size_;
uint32_t data_size_;
uint32_t position_;
diff --git a/chromium/components/webrtc_logging/logging_unittest.cc b/chromium/components/webrtc_logging/logging_unittest.cc
index 77847c242b0..2085eae96e2 100644
--- a/chromium/components/webrtc_logging/logging_unittest.cc
+++ b/chromium/components/webrtc_logging/logging_unittest.cc
@@ -68,7 +68,14 @@ class WebRtcTextLogTest : public testing::Test {
if (!logging::InitLogging(settings)) {
return false;
}
+
+#if BUILDFLAG(USE_RUNTIME_VLOG)
EXPECT_TRUE(VLOG_IS_ON(verbosity_level));
+#else
+ // VLOGs default to off when not using runtime vlog.
+ EXPECT_FALSE(VLOG_IS_ON(verbosity_level));
+#endif // BUILDFLAG(USE_RUNTIME_VLOG)
+
EXPECT_FALSE(VLOG_IS_ON(verbosity_level + 1));
return true;
}
@@ -147,9 +154,12 @@ TEST_F(WebRtcTextLogTest, LogEverythingConfiguration) {
// Make sure string contains the expected values.
EXPECT_TRUE(ContainsString(contents_of_file, AsString(rtc::LS_ERROR)));
EXPECT_TRUE(ContainsString(contents_of_file, AsString(rtc::LS_WARNING)));
+
+#if BUILDFLAG(USE_RUNTIME_VLOG)
EXPECT_TRUE(ContainsString(contents_of_file, AsString(rtc::LS_INFO)));
// RTC_LOG_E
EXPECT_TRUE(ContainsString(contents_of_file, strerror(kFakeError)));
EXPECT_TRUE(ContainsString(contents_of_file, AsString(rtc::LS_VERBOSE)));
EXPECT_TRUE(ContainsString(contents_of_file, AsString(rtc::LS_SENSITIVE)));
+#endif // BUILDFLAG(USE_RUNTIME_VLOG)
}
diff --git a/chromium/components/webxr/android/BUILD.gn b/chromium/components/webxr/android/BUILD.gn
index 003d0fe8797..75fe7039ccc 100644
--- a/chromium/components/webxr/android/BUILD.gn
+++ b/chromium/components/webxr/android/BUILD.gn
@@ -64,6 +64,7 @@ android_library("ar_java_interfaces") {
]
deps = [
+ "//components/browser_ui/widget/android:java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
@@ -75,6 +76,8 @@ android_library("ar_java_base") {
":ar_java_interfaces",
":webxr_android_enums_java",
"//base:base_java",
+ "//base:jni_java",
+ "//build/android:build_java",
"//content/public/android:content_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
@@ -101,6 +104,7 @@ android_library("ar_java") {
":ar_java_base",
":webxr_android_enums_java",
"//base:base_java",
+ "//build/android:build_java",
"//third_party/arcore-android-sdk-client:com_google_ar_core_java",
]
diff --git a/chromium/components/webxr/android/DEPS b/chromium/components/webxr/android/DEPS
index 4193c509af2..8e978f19ab6 100644
--- a/chromium/components/webxr/android/DEPS
+++ b/chromium/components/webxr/android/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/gesture/BackPressHandler.java",
"+components/messages",
"+components/resources/android/theme_resources.h",
"+components/strings/grit/components_strings.h",
diff --git a/chromium/components/wifi/fake_wifi_service.cc b/chromium/components/wifi/fake_wifi_service.cc
index 4b717c46263..f429a58f72d 100644
--- a/chromium/components/wifi/fake_wifi_service.cc
+++ b/chromium/components/wifi/fake_wifi_service.cc
@@ -54,53 +54,51 @@ void FakeWiFiService::UnInitialize() {
}
void FakeWiFiService::GetProperties(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) {
NetworkList::iterator network_properties = FindNetwork(network_guid);
if (network_properties == networks_.end()) {
*error = "Error.InvalidNetworkGuid";
return;
}
- properties->Swap(network_properties->ToValue(false).get());
+ *properties = network_properties->ToValue(/*network_list=*/false);
}
void FakeWiFiService::GetManagedProperties(
const std::string& network_guid,
- base::DictionaryValue* managed_properties,
+ base::Value::Dict* managed_properties,
std::string* error) {
// Not implemented
*error = kErrorWiFiService;
}
void FakeWiFiService::GetState(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) {
NetworkList::iterator network_properties = FindNetwork(network_guid);
if (network_properties == networks_.end()) {
*error = "Error.InvalidNetworkGuid";
return;
}
- properties->Swap(network_properties->ToValue(true).get());
+ *properties = network_properties->ToValue(/*network_list=*/true);
}
-void FakeWiFiService::SetProperties(
- const std::string& network_guid,
- std::unique_ptr<base::DictionaryValue> properties,
- std::string* error) {
+void FakeWiFiService::SetProperties(const std::string& network_guid,
+ base::Value::Dict properties,
+ std::string* error) {
NetworkList::iterator network_properties = FindNetwork(network_guid);
if (network_properties == networks_.end() ||
- !network_properties->UpdateFromValue(*properties)) {
+ !network_properties->UpdateFromValue(properties)) {
*error = "Error.DBusFailed";
}
}
-void FakeWiFiService::CreateNetwork(
- bool shared,
- std::unique_ptr<base::DictionaryValue> properties,
- std::string* network_guid,
- std::string* error) {
+void FakeWiFiService::CreateNetwork(bool shared,
+ base::Value::Dict properties,
+ std::string* network_guid,
+ std::string* error) {
NetworkProperties network_properties;
- if (network_properties.UpdateFromValue(*properties)) {
+ if (network_properties.UpdateFromValue(properties)) {
network_properties.guid = network_properties.ssid;
networks_.push_back(network_properties);
*network_guid = network_properties.guid;
@@ -110,16 +108,14 @@ void FakeWiFiService::CreateNetwork(
}
void FakeWiFiService::GetVisibleNetworks(const std::string& network_type,
- base::ListValue* network_list,
- bool include_details) {
+ bool include_details,
+ base::Value::List* network_list) {
for (NetworkList::const_iterator it = networks_.begin();
it != networks_.end();
++it) {
if (network_type.empty() || network_type == onc::network_type::kAllTypes ||
it->type == network_type) {
- std::unique_ptr<base::DictionaryValue> network(
- it->ToValue(!include_details));
- network_list->Append(std::move(network));
+ network_list->Append(it->ToValue(/*network_list=*/!include_details));
}
}
}
diff --git a/chromium/components/wifi/fake_wifi_service.h b/chromium/components/wifi/fake_wifi_service.h
index 768b1449c89..b3b5301e0b3 100644
--- a/chromium/components/wifi/fake_wifi_service.h
+++ b/chromium/components/wifi/fake_wifi_service.h
@@ -29,24 +29,24 @@ class FakeWiFiService : public WiFiService {
scoped_refptr<base::SequencedTaskRunner> task_runner) override;
void UnInitialize() override;
void GetProperties(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) override;
void GetManagedProperties(const std::string& network_guid,
- base::DictionaryValue* managed_properties,
+ base::Value::Dict* managed_properties,
std::string* error) override;
void GetState(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) override;
void SetProperties(const std::string& network_guid,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* error) override;
void CreateNetwork(bool shared,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* network_guid,
std::string* error) override;
void GetVisibleNetworks(const std::string& network_type,
- base::ListValue* network_list,
- bool include_details) override;
+ bool include_details,
+ base::Value::List* network_list) override;
void RequestNetworkScan() override;
void StartConnect(const std::string& network_guid,
std::string* error) override;
diff --git a/chromium/components/wifi/network_properties.cc b/chromium/components/wifi/network_properties.cc
index f1801abe478..6171e71f0eb 100644
--- a/chromium/components/wifi/network_properties.cc
+++ b/chromium/components/wifi/network_properties.cc
@@ -27,83 +27,80 @@ NetworkProperties::NetworkProperties(const NetworkProperties& other) = default;
NetworkProperties::~NetworkProperties() {
}
-std::unique_ptr<base::DictionaryValue> NetworkProperties::ToValue(
- bool network_list) const {
- std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue());
+base::Value::Dict NetworkProperties::ToValue(bool network_list) const {
+ base::Value::Dict value;
- value->SetStringPath(onc::network_config::kGUID, guid);
- value->SetStringPath(onc::network_config::kName, name);
- value->SetStringPath(onc::network_config::kConnectionState, connection_state);
+ value.Set(onc::network_config::kGUID, guid);
+ value.Set(onc::network_config::kName, name);
+ value.Set(onc::network_config::kConnectionState, connection_state);
DCHECK(type == onc::network_type::kWiFi);
- value->SetString(onc::network_config::kType, type);
+ value.Set(onc::network_config::kType, type);
// For now, assume all WiFi services are connectable.
- value->SetBoolPath(onc::network_config::kConnectable, true);
+ value.Set(onc::network_config::kConnectable, true);
- base::DictionaryValue wifi;
- wifi.SetStringPath(onc::wifi::kSecurity, security);
- wifi.SetIntPath(onc::wifi::kSignalStrength, signal_strength);
+ base::Value::Dict wifi;
+ wifi.Set(onc::wifi::kSecurity, security);
+ wifi.Set(onc::wifi::kSignalStrength, static_cast<int>(signal_strength));
// Network list expects subset of data.
if (!network_list) {
if (frequency != kFrequencyUnknown)
- wifi.SetIntPath(onc::wifi::kFrequency, frequency);
- base::ListValue frequency_list;
+ wifi.Set(onc::wifi::kFrequency, frequency);
+ base::Value::List frequency_list;
for (FrequencySet::const_iterator it = this->frequency_set.begin();
it != this->frequency_set.end();
++it) {
frequency_list.Append(*it);
}
- if (!frequency_list.GetListDeprecated().empty())
- wifi.SetPath(onc::wifi::kFrequencyList, std::move(frequency_list));
+ if (!frequency_list.empty()) {
+ wifi.Set(onc::wifi::kFrequencyList, std::move(frequency_list));
+ }
if (!bssid.empty())
- wifi.SetStringPath(onc::wifi::kBSSID, bssid);
- wifi.SetStringPath(onc::wifi::kSSID, ssid);
- wifi.SetStringPath(onc::wifi::kHexSSID,
- base::HexEncode(ssid.c_str(), ssid.size()));
+ wifi.Set(onc::wifi::kBSSID, bssid);
+ wifi.Set(onc::wifi::kSSID, ssid);
+ wifi.Set(onc::wifi::kHexSSID, base::HexEncode(ssid.c_str(), ssid.size()));
}
- value->SetPath(onc::network_type::kWiFi, std::move(wifi));
+ value.Set(onc::network_type::kWiFi, std::move(wifi));
return value;
}
-bool NetworkProperties::UpdateFromValue(const base::DictionaryValue& value) {
- const base::DictionaryValue* wifi = nullptr;
- std::string network_type;
+bool NetworkProperties::UpdateFromValue(const base::Value::Dict& value) {
+ const std::string* network_type =
+ value.FindString(onc::network_config::kType);
// Get network type and make sure that it is WiFi (if specified).
- if (value.GetString(onc::network_config::kType, &network_type)) {
- if (network_type != onc::network_type::kWiFi)
+ if (network_type) {
+ if (*network_type != onc::network_type::kWiFi)
return false;
- type = network_type;
+ type = *network_type;
}
- if (value.GetDictionary(onc::network_type::kWiFi, &wifi)) {
- const std::string* name_ptr =
- value.FindStringPath(onc::network_config::kName);
+
+ const base::Value::Dict* wifi = value.FindDict(onc::network_type::kWiFi);
+ if (wifi) {
+ const std::string* name_ptr = value.FindString(onc::network_config::kName);
if (name_ptr)
name = *name_ptr;
- const std::string* guid_ptr =
- value.FindStringPath(onc::network_config::kGUID);
+ const std::string* guid_ptr = value.FindString(onc::network_config::kGUID);
if (guid_ptr)
guid = *guid_ptr;
const std::string* connection_state_ptr =
- value.FindStringPath(onc::network_config::kConnectionState);
+ value.FindString(onc::network_config::kConnectionState);
if (connection_state_ptr)
connection_state = *connection_state_ptr;
- const std::string* security_ptr =
- wifi->FindStringPath(onc::wifi::kSecurity);
+ const std::string* security_ptr = wifi->FindString(onc::wifi::kSecurity);
if (security_ptr)
security = *security_ptr;
- const std::string* ssid_ptr = wifi->FindStringPath(onc::wifi::kSSID);
+ const std::string* ssid_ptr = wifi->FindString(onc::wifi::kSSID);
if (ssid_ptr)
ssid = *ssid_ptr;
- const std::string* password_ptr =
- wifi->FindStringPath(onc::wifi::kPassphrase);
+ const std::string* password_ptr = wifi->FindString(onc::wifi::kPassphrase);
if (password_ptr)
password = *password_ptr;
absl::optional<bool> auto_connect_opt =
- wifi->FindBoolKey(onc::wifi::kAutoConnect);
+ wifi->FindBool(onc::wifi::kAutoConnect);
if (auto_connect_opt)
auto_connect = *auto_connect_opt;
diff --git a/chromium/components/wifi/network_properties.h b/chromium/components/wifi/network_properties.h
index 07bfe1dc1f6..89c2aea5711 100644
--- a/chromium/components/wifi/network_properties.h
+++ b/chromium/components/wifi/network_properties.h
@@ -52,9 +52,9 @@ struct WIFI_EXPORT NetworkProperties {
Frequency frequency;
FrequencySet frequency_set;
- std::unique_ptr<base::DictionaryValue> ToValue(bool network_list) const;
+ base::Value::Dict ToValue(bool network_list) const;
// Updates only properties set in |value|.
- bool UpdateFromValue(const base::DictionaryValue& value);
+ bool UpdateFromValue(const base::Value::Dict& value);
static std::string MacAddressAsString(const uint8_t mac_as_int[6]);
static bool OrderByType(const NetworkProperties& l,
const NetworkProperties& r);
diff --git a/chromium/components/wifi/wifi_service.h b/chromium/components/wifi/wifi_service.h
index 4486a0d3611..bd5b68c2d53 100644
--- a/chromium/components/wifi/wifi_service.h
+++ b/chromium/components/wifi/wifi_service.h
@@ -48,7 +48,7 @@ class WIFI_EXPORT WiFiService {
// Get Properties of network identified by |network_guid|. Populates
// |properties| on success, |error| on failure.
virtual void GetProperties(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) = 0;
// Gets the merged properties of the network with id |network_guid| from the
@@ -56,7 +56,7 @@ class WIFI_EXPORT WiFiService {
// the currently active settings. Populates |managed_properties| on success,
// |error| on failure.
virtual void GetManagedProperties(const std::string& network_guid,
- base::DictionaryValue* managed_properties,
+ base::Value::Dict* managed_properties,
std::string* error) = 0;
// Get the cached read-only properties of the network with id |network_guid|.
@@ -65,13 +65,13 @@ class WIFI_EXPORT WiFiService {
// returns a subset of the properties returned by |GetProperties|. Populates
// |properties| on success, |error| on failure.
virtual void GetState(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) = 0;
// Set Properties of network identified by |network_guid|. Populates |error|
// on failure.
virtual void SetProperties(const std::string& network_guid,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* error) = 0;
// Creates a new network configuration from |properties|. If |shared| is true,
@@ -79,15 +79,15 @@ class WIFI_EXPORT WiFiService {
// network already exists, this will fail and populate |error|. On success
// populates the |network_guid| of the new network.
virtual void CreateNetwork(bool shared,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* network_guid,
std::string* error) = 0;
// Get list of visible networks of |network_type| (one of onc::network_type).
// Populates |network_list| on success.
virtual void GetVisibleNetworks(const std::string& network_type,
- base::ListValue* network_list,
- bool include_details) = 0;
+ bool include_details,
+ base::Value::List* network_list) = 0;
// Request network scan. Send |NetworkListChanged| event on completion.
virtual void RequestNetworkScan() = 0;
diff --git a/chromium/components/wifi/wifi_service_fuchsia.cc b/chromium/components/wifi/wifi_service_fuchsia.cc
index 082c9735b62..2d5f594a6fa 100644
--- a/chromium/components/wifi/wifi_service_fuchsia.cc
+++ b/chromium/components/wifi/wifi_service_fuchsia.cc
@@ -24,35 +24,35 @@ class WifiServiceFuchsia : public WiFiService {
void UnInitialize() override { NOTIMPLEMENTED_LOG_ONCE(); }
void GetProperties(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) override {
*error = kErrorNotImplemented;
NOTIMPLEMENTED_LOG_ONCE();
}
void GetManagedProperties(const std::string& network_guid,
- base::DictionaryValue* managed_properties,
+ base::Value::Dict* managed_properties,
std::string* error) override {
*error = kErrorNotImplemented;
NOTIMPLEMENTED_LOG_ONCE();
}
void GetState(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) override {
*error = kErrorNotImplemented;
NOTIMPLEMENTED_LOG_ONCE();
}
void SetProperties(const std::string& network_guid,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* error) override {
*error = kErrorNotImplemented;
NOTIMPLEMENTED_LOG_ONCE();
}
void CreateNetwork(bool shared,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* network_guid,
std::string* error) override {
*error = kErrorNotImplemented;
@@ -60,8 +60,8 @@ class WifiServiceFuchsia : public WiFiService {
}
void GetVisibleNetworks(const std::string& network_type,
- base::ListValue* network_list,
- bool include_details) override {
+ bool include_details,
+ base::Value::List* network_list) override {
NOTIMPLEMENTED_LOG_ONCE();
}
diff --git a/chromium/components/wifi/wifi_service_mac.mm b/chromium/components/wifi/wifi_service_mac.mm
index f61d8bb12c3..f4d75ea77df 100644
--- a/chromium/components/wifi/wifi_service_mac.mm
+++ b/chromium/components/wifi/wifi_service_mac.mm
@@ -43,29 +43,29 @@ class WiFiServiceMac : public WiFiService {
void UnInitialize() override;
void GetProperties(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) override;
void GetManagedProperties(const std::string& network_guid,
- base::DictionaryValue* managed_properties,
+ base::Value::Dict* managed_properties,
std::string* error) override;
void GetState(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) override;
void SetProperties(const std::string& network_guid,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* error) override;
void CreateNetwork(bool shared,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* network_guid,
std::string* error) override;
void GetVisibleNetworks(const std::string& network_type,
- base::ListValue* network_list,
- bool include_details) override;
+ bool include_details,
+ base::Value::List* network_list) override;
void RequestNetworkScan() override;
@@ -155,7 +155,7 @@ class WiFiServiceMac : public WiFiService {
// Guid of last known connected network.
std::string connected_network_guid_;
// Temporary storage of network properties indexed by |network_guid|.
- base::DictionaryValue network_properties_;
+ base::Value::Dict network_properties_;
};
WiFiServiceMac::WiFiServiceMac() : wlan_observer_(nil) {
@@ -189,7 +189,7 @@ void WiFiServiceMac::UnInitialize() {
}
void WiFiServiceMac::GetProperties(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) {
NetworkList::iterator it = FindNetwork(network_guid);
if (it == networks_.end()) {
@@ -199,47 +199,42 @@ void WiFiServiceMac::GetProperties(const std::string& network_guid,
}
it->connection_state = GetNetworkConnectionState(network_guid);
- std::unique_ptr<base::DictionaryValue> network(it->ToValue(false));
- properties->Swap(network.get());
+ *properties = it->ToValue(/*network_list=*/false);
DVLOG(1) << *properties;
}
-void WiFiServiceMac::GetManagedProperties(
- const std::string& network_guid,
- base::DictionaryValue* managed_properties,
- std::string* error) {
+void WiFiServiceMac::GetManagedProperties(const std::string& network_guid,
+ base::Value::Dict* managed_properties,
+ std::string* error) {
*error = kErrorNotImplemented;
}
void WiFiServiceMac::GetState(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) {
*error = kErrorNotImplemented;
}
-void WiFiServiceMac::SetProperties(
- const std::string& network_guid,
- std::unique_ptr<base::DictionaryValue> properties,
- std::string* error) {
- base::DictionaryValue* existing_properties;
+void WiFiServiceMac::SetProperties(const std::string& network_guid,
+ base::Value::Dict properties,
+ std::string* error) {
// If the network properties already exist, don't override previously set
// properties, unless they are set in |properties|.
- if (network_properties_.GetDictionaryWithoutPathExpansion(
- network_guid, &existing_properties)) {
- existing_properties->MergeDictionary(properties.get());
+ base::Value::Dict* existing_properties =
+ network_properties_.FindDict(network_guid);
+ if (existing_properties) {
+ existing_properties->Merge(properties);
} else {
- network_properties_.SetWithoutPathExpansion(network_guid,
- std::move(properties));
+ network_properties_.Set(network_guid, std::move(properties));
}
}
-void WiFiServiceMac::CreateNetwork(
- bool shared,
- std::unique_ptr<base::DictionaryValue> properties,
- std::string* network_guid,
- std::string* error) {
+void WiFiServiceMac::CreateNetwork(bool shared,
+ base::Value::Dict properties,
+ std::string* network_guid,
+ std::string* error) {
NetworkProperties network_properties;
- if (!network_properties.UpdateFromValue(*properties)) {
+ if (!network_properties.UpdateFromValue(properties)) {
*error = kErrorInvalidData;
return;
}
@@ -249,13 +244,13 @@ void WiFiServiceMac::CreateNetwork(
*error = kErrorInvalidData;
return;
}
- network_properties_.SetWithoutPathExpansion(guid, std::move(properties));
+ network_properties_.Set(guid, std::move(properties));
*network_guid = guid;
}
void WiFiServiceMac::GetVisibleNetworks(const std::string& network_type,
- base::ListValue* network_list,
- bool include_details) {
+ bool include_details,
+ base::Value::List* network_list) {
if (!network_type.empty() &&
network_type != onc::network_type::kAllTypes &&
network_type != onc::network_type::kWiFi) {
@@ -268,9 +263,7 @@ void WiFiServiceMac::GetVisibleNetworks(const std::string& network_type,
for (NetworkList::const_iterator it = networks_.begin();
it != networks_.end();
++it) {
- std::unique_ptr<base::DictionaryValue> network(
- it->ToValue(!include_details));
- network_list->Append(std::move(network));
+ network_list->Append(it->ToValue(/*network_list=*/!include_details));
}
}
@@ -313,15 +306,15 @@ void WiFiServiceMac::StartConnect(const std::string& network_guid,
}
// Check whether WiFi Password is set in |network_properties_|.
- base::DictionaryValue* properties;
- base::DictionaryValue* wifi;
- std::string passphrase;
+ base::Value::Dict* properties = network_properties_.FindDict(network_guid);
NSString* ns_password = nil;
- if (network_properties_.GetDictionaryWithoutPathExpansion(network_guid,
- &properties) &&
- properties->GetDictionary(onc::network_type::kWiFi, &wifi) &&
- wifi->GetString(onc::wifi::kPassphrase, &passphrase)) {
- ns_password = base::SysUTF8ToNSString(passphrase);
+ if (properties) {
+ base::Value::Dict* wifi = properties->FindDict(onc::network_type::kWiFi);
+ if (wifi) {
+ const std::string* passphrase = wifi->FindString(onc::wifi::kPassphrase);
+ if (passphrase)
+ ns_password = base::SysUTF8ToNSString(*passphrase);
+ }
}
// Number of attempts to associate to network.
diff --git a/chromium/components/wifi/wifi_service_win.cc b/chromium/components/wifi/wifi_service_win.cc
index 776d399c1a8..d9e0c983335 100644
--- a/chromium/components/wifi/wifi_service_win.cc
+++ b/chromium/components/wifi/wifi_service_win.cc
@@ -199,29 +199,29 @@ class WiFiServiceImpl : public WiFiService {
void UnInitialize() override;
void GetProperties(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) override;
void GetManagedProperties(const std::string& network_guid,
- base::DictionaryValue* managed_properties,
+ base::Value::Dict* managed_properties,
std::string* error) override;
void GetState(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) override;
void SetProperties(const std::string& network_guid,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* error) override;
void CreateNetwork(bool shared,
- std::unique_ptr<base::DictionaryValue> properties,
+ base::Value::Dict properties,
std::string* network_guid,
std::string* error) override;
void GetVisibleNetworks(const std::string& network_type,
- base::ListValue* network_list,
- bool include_details) override;
+ bool include_details,
+ base::Value::List* network_list) override;
void RequestNetworkScan() override;
@@ -467,14 +467,14 @@ class WiFiServiceImpl : public WiFiService {
GUID interface_guid_;
// Temporary storage of network properties indexed by |network_guid|. Persist
// only in memory.
- base::DictionaryValue connect_properties_;
+ base::Value::Dict connect_properties_;
// Preserved WLAN profile xml.
std::map<std::string, std::string> saved_profiles_xml_;
// Created WLAN Profiles, indexed by |network_guid|. Contains xml with TKIP
// encryption type saved by |CreateNetwork| if applicable. Profile has to be
// deleted if connection fails. Implicitly created profiles have to be deleted
// if connection succeeds. Persist only in memory.
- base::DictionaryValue created_profiles_;
+ base::Value::Dict created_profiles_;
// Observer to get notified when network(s) have changed (e.g. connect).
NetworkGuidListCallback networks_changed_observer_;
// Observer to get notified when network list has changed (scan complete).
@@ -528,7 +528,7 @@ void WiFiServiceImpl::UnInitialize() {
}
void WiFiServiceImpl::GetProperties(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) {
DWORD error_code = EnsureInitialized();
if (CheckError(error_code, kErrorWiFiService, error))
@@ -538,7 +538,7 @@ void WiFiServiceImpl::GetProperties(const std::string& network_guid,
error_code = GetCurrentProperties(&connected_properties);
if (error_code == ERROR_SUCCESS &&
connected_properties.guid == network_guid) {
- properties->Swap(connected_properties.ToValue(false).get());
+ *properties = connected_properties.ToValue(/*network_list=*/false);
return;
}
@@ -549,7 +549,7 @@ void WiFiServiceImpl::GetProperties(const std::string& network_guid,
if (it != network_list.end()) {
DVLOG(1) << "Get Properties: " << network_guid << ":"
<< it->connection_state;
- properties->Swap(it->ToValue(false).get());
+ *properties = it->ToValue(/*network_list=*/false);
return;
}
error_code = ERROR_NOT_FOUND;
@@ -560,53 +560,49 @@ void WiFiServiceImpl::GetProperties(const std::string& network_guid,
void WiFiServiceImpl::GetManagedProperties(
const std::string& network_guid,
- base::DictionaryValue* managed_properties,
+ base::Value::Dict* managed_properties,
std::string* error) {
CheckError(ERROR_CALL_NOT_IMPLEMENTED, kErrorWiFiService, error);
}
void WiFiServiceImpl::GetState(const std::string& network_guid,
- base::DictionaryValue* properties,
+ base::Value::Dict* properties,
std::string* error) {
CheckError(ERROR_CALL_NOT_IMPLEMENTED, kErrorWiFiService, error);
}
-void WiFiServiceImpl::SetProperties(
- const std::string& network_guid,
- std::unique_ptr<base::DictionaryValue> properties,
- std::string* error) {
+void WiFiServiceImpl::SetProperties(const std::string& network_guid,
+ base::Value::Dict properties,
+ std::string* error) {
// Temporary preserve WiFi properties (desired frequency, wifi password) to
// use in StartConnect.
- DCHECK(properties);
- if (!properties->HasKey(onc::network_type::kWiFi)) {
- DVLOG(0) << "Missing WiFi properties:" << *properties;
+ if (!properties.Find(onc::network_type::kWiFi)) {
+ DVLOG(0) << "Missing WiFi properties:" << properties;
*error = kErrorWiFiService;
return;
}
- base::DictionaryValue* existing_properties;
// If the network properties already exist, don't override previously set
// properties, unless they are set in |properties|.
- if (connect_properties_.GetDictionaryWithoutPathExpansion(
- network_guid, &existing_properties)) {
- existing_properties->MergeDictionary(properties.get());
+ base::Value::Dict* existing_properties =
+ connect_properties_.FindDict(network_guid);
+ if (existing_properties) {
+ existing_properties->Merge(properties);
} else {
- connect_properties_.SetWithoutPathExpansion(network_guid,
- std::move(properties));
+ connect_properties_.Set(network_guid, std::move(properties));
}
}
-void WiFiServiceImpl::CreateNetwork(
- bool shared,
- std::unique_ptr<base::DictionaryValue> properties,
- std::string* network_guid,
- std::string* error) {
+void WiFiServiceImpl::CreateNetwork(bool shared,
+ base::Value::Dict properties,
+ std::string* network_guid,
+ std::string* error) {
DWORD error_code = EnsureInitialized();
if (CheckError(error_code, kErrorWiFiService, error))
return;
NetworkProperties network_properties;
- if (!network_properties.UpdateFromValue(*properties)) {
+ if (!network_properties.UpdateFromValue(properties)) {
CheckError(ERROR_INVALID_DATA, kErrorWiFiService, error);
return;
}
@@ -636,18 +632,18 @@ void WiFiServiceImpl::CreateNetwork(
}
if (tkip_profile_xml != profile_xml) {
- base::DictionaryValue tkip_profile;
- tkip_profile.SetStringKey(kProfileXmlKey, tkip_profile_xml);
- tkip_profile.SetBoolKey(kProfileSharedKey, shared);
- created_profiles_.SetKey(network_properties.guid, std::move(tkip_profile));
+ base::Value::Dict tkip_profile;
+ tkip_profile.Set(kProfileXmlKey, tkip_profile_xml);
+ tkip_profile.Set(kProfileSharedKey, shared);
+ created_profiles_.Set(network_properties.guid, std::move(tkip_profile));
}
*network_guid = network_properties.guid;
}
void WiFiServiceImpl::GetVisibleNetworks(const std::string& network_type,
- base::ListValue* network_list,
- bool include_details) {
+ bool include_details,
+ base::Value::List* network_list) {
if (!network_type.empty() &&
network_type != onc::network_type::kAllTypes &&
network_type != onc::network_type::kWiFi) {
@@ -663,9 +659,7 @@ void WiFiServiceImpl::GetVisibleNetworks(const std::string& network_type,
for (NetworkList::const_iterator it = networks.begin();
it != networks.end();
++it) {
- std::unique_ptr<base::DictionaryValue> network(
- it->ToValue(!include_details));
- network_list->Append(std::move(network));
+ network_list->Append(it->ToValue(/*network_list=*/!include_details));
}
}
}
@@ -882,19 +876,20 @@ void WiFiServiceImpl::WaitForNetworkConnect(const std::string& network_guid,
LOG(ERROR) << kMaxAttempts << " attempts exceeded waiting for connect to "
<< network_guid;
- base::Value* created_profile = created_profiles_.FindDictKey(network_guid);
+ base::Value::Dict* created_profile =
+ created_profiles_.FindDict(network_guid);
// Check, whether this connection is using newly created profile.
if (created_profile) {
const std::string* tkip_profile_xml =
- created_profile->FindStringKey(kProfileXmlKey);
+ created_profile->FindString(kProfileXmlKey);
absl::optional<bool> shared =
- created_profile->FindBoolKey(kProfileSharedKey);
+ created_profile->FindBool(kProfileSharedKey);
// Check, if this connection there is alternative TKIP profile xml that
// should be tried. If there is, then set it up and try to connect again.
if (tkip_profile_xml && shared) {
// Remove TKIP profile xml, so it will not be tried again.
- created_profile->RemoveKey(kProfileXmlKey);
- created_profile->RemoveKey(kProfileSharedKey);
+ created_profile->Remove(kProfileXmlKey);
+ created_profile->Remove(kProfileSharedKey);
DWORD error_code = SetProfile(*shared, *tkip_profile_xml, true);
if (error_code == ERROR_SUCCESS) {
// Try to connect with new profile.
@@ -936,7 +931,7 @@ void WiFiServiceImpl::WaitForNetworkConnect(const std::string& network_guid,
if (error != ERROR_SUCCESS)
LOG(ERROR) << error;
// There is no need to keep created profile as network is connected.
- created_profiles_.RemoveKey(network_guid);
+ created_profiles_.Remove(network_guid);
// Restore previously suppressed notifications.
enable_notify_network_changed_ = true;
RestoreNwCategoryWizard();
@@ -1481,14 +1476,15 @@ DWORD WiFiServiceImpl::GetCurrentSSID(std::string* ssid) {
Frequency WiFiServiceImpl::GetFrequencyToConnect(
const std::string& network_guid) const {
// Check whether desired frequency is set in |connect_properties_|.
- const base::DictionaryValue* properties;
- if (connect_properties_.GetDictionaryWithoutPathExpansion(network_guid,
- &properties)) {
- const base::DictionaryValue* wifi;
- if (properties->GetDictionary(onc::network_type::kWiFi, &wifi)) {
- int frequency;
- if (wifi->GetInteger(onc::wifi::kFrequency, &frequency))
- return GetNormalizedFrequency(frequency);
+ const base::Value::Dict* properties =
+ connect_properties_.FindDict(network_guid);
+ if (properties) {
+ const base::Value::Dict* wifi =
+ properties->FindDict(onc::network_type::kWiFi);
+ if (wifi) {
+ absl::optional<int> frequency = wifi->FindInt(onc::wifi::kFrequency);
+ if (frequency.has_value())
+ return GetNormalizedFrequency(*frequency);
}
}
return kFrequencyAny;
@@ -1594,19 +1590,21 @@ DWORD WiFiServiceImpl::Connect(const std::string& network_guid,
} else {
// If network is available, but is not open security, then it cannot be
// connected without profile, so return 'access denied' error.
- std::unique_ptr<base::DictionaryValue> properties(
- new base::DictionaryValue);
- const base::DictionaryValue* wifi;
- std::string wifi_security;
+ base::Value::Dict properties;
std::string error_string;
- GetProperties(network_guid, properties.get(), &error_string);
- if (error_string.empty() &&
- properties->GetDictionary(onc::network_type::kWiFi, &wifi) &&
- wifi->GetString(onc::wifi::kSecurity, &wifi_security) &&
- wifi_security != onc::wifi::kSecurityNone) {
- error = ERROR_ACCESS_DENIED;
- LOG(ERROR) << error;
- return error;
+ GetProperties(network_guid, &properties, &error_string);
+ if (error_string.empty()) {
+ const base::Value::Dict* wifi =
+ properties.FindDict(onc::network_type::kWiFi);
+ if (wifi) {
+ const std::string* wifi_security =
+ wifi->FindString(onc::wifi::kSecurity);
+ if (wifi_security && *wifi_security != onc::wifi::kSecurityNone) {
+ error = ERROR_ACCESS_DENIED;
+ LOG(ERROR) << error;
+ return error;
+ }
+ }
}
WLAN_CONNECTION_PARAMETERS wlan_params = {
wlan_connection_mode_discovery_unsecure,
@@ -1693,12 +1691,12 @@ bool WiFiServiceImpl::HaveProfile(const std::string& network_guid) {
DWORD WiFiServiceImpl::DeleteCreatedProfile(const std::string& network_guid) {
DWORD error_code = ERROR_SUCCESS;
// Check, whether this connection is using new created profile, and remove it.
- if (created_profiles_.HasKey(network_guid)) {
+ if (created_profiles_.contains(network_guid)) {
// Connection has failed, so delete it.
std::wstring profile_name = ProfileNameFromGUID(network_guid);
error_code = WlanDeleteProfile_function_(client_, &interface_guid_,
profile_name.c_str(), nullptr);
- created_profiles_.RemoveKey(network_guid);
+ created_profiles_.Remove(network_guid);
}
return error_code;
}
diff --git a/chromium/components/wifi/wifi_test.cc b/chromium/components/wifi/wifi_test.cc
index 1c305a3dad3..cd4db951b91 100644
--- a/chromium/components/wifi/wifi_test.cc
+++ b/chromium/components/wifi/wifi_test.cc
@@ -60,7 +60,7 @@ class WiFiTest {
void OnNetworksChanged(
const WiFiService::NetworkGuidList& network_guid_list) {
VLOG(0) << "Networks Changed: " << network_guid_list[0];
- base::DictionaryValue properties;
+ base::Value::Dict properties;
std::string error;
wifi_service_->GetProperties(network_guid_list[0], &properties, &error);
VLOG(0) << error << ":\n" << properties;
@@ -140,15 +140,16 @@ bool WiFiTest::ParseCommandLine(int argc, const char* argv[]) {
wifi_service_->Initialize(executor.task_runner());
if (parsed_command_line.HasSwitch("list")) {
- base::ListValue network_list;
- wifi_service_->GetVisibleNetworks(std::string(), &network_list, true);
+ base::Value::List network_list;
+ wifi_service_->GetVisibleNetworks(std::string(), /*include_details=*/true,
+ &network_list);
VLOG(0) << network_list;
return true;
}
if (parsed_command_line.HasSwitch("get_properties")) {
if (network_guid.length() > 0) {
- base::DictionaryValue properties;
+ base::Value::Dict properties;
std::string error;
wifi_service_->GetProperties(network_guid, &properties, &error);
VLOG(0) << error << ":\n" << properties;
@@ -157,29 +158,28 @@ bool WiFiTest::ParseCommandLine(int argc, const char* argv[]) {
}
// Optional properties (frequency, password) to use for connect or create.
- std::unique_ptr<base::DictionaryValue> properties(
- new base::DictionaryValue());
+ base::Value::Dict properties;
if (!frequency.empty()) {
int value = 0;
if (base::StringToInt(frequency, &value)) {
- properties->SetInteger("WiFi.Frequency", value);
+ properties.Set("WiFi.Frequency", value);
// fall through to connect.
}
}
if (!password.empty())
- properties->SetString("WiFi.Passphrase", password);
+ properties.Set("WiFi.Passphrase", password);
if (!security.empty())
- properties->SetString("WiFi.Security", security);
+ properties.Set("WiFi.Security", security);
if (parsed_command_line.HasSwitch("create")) {
if (!network_guid.empty()) {
std::string error;
std::string new_network_guid;
- properties->SetString("WiFi.SSID", network_guid);
- VLOG(0) << "Creating Network: " << *properties;
+ properties.Set("WiFi.SSID", network_guid);
+ VLOG(0) << "Creating Network: " << properties;
wifi_service_->CreateNetwork(false, std::move(properties),
&new_network_guid, &error);
VLOG(0) << error << ":\n" << new_network_guid;
@@ -190,8 +190,8 @@ bool WiFiTest::ParseCommandLine(int argc, const char* argv[]) {
if (parsed_command_line.HasSwitch("connect")) {
if (!network_guid.empty()) {
std::string error;
- if (!properties->DictEmpty()) {
- VLOG(0) << "Using connect properties: " << *properties;
+ if (!properties.empty()) {
+ VLOG(0) << "Using connect properties: " << properties;
wifi_service_->SetProperties(network_guid, std::move(properties),
&error);
}
diff --git a/chromium/components/zoom/zoom_controller.cc b/chromium/components/zoom/zoom_controller.cc
index a581483193a..68ba2fa83ee 100644
--- a/chromium/components/zoom/zoom_controller.cc
+++ b/chromium/components/zoom/zoom_controller.cc
@@ -111,7 +111,7 @@ bool ZoomController::SetZoomLevelByClient(
// Cannot zoom in disabled mode. Also, don't allow changing zoom level on
// a crashed tab, an error page or an interstitial page.
if (zoom_mode_ == ZOOM_MODE_DISABLED ||
- !web_contents()->GetMainFrame()->IsRenderFrameLive())
+ !web_contents()->GetPrimaryMainFrame()->IsRenderFrameLive())
return false;
// Store client data so the |client| can be attributed when the zoom
@@ -157,12 +157,14 @@ bool ZoomController::SetZoomLevelByClient(
web_contents(), GetZoomLevel(), zoom_level, zoom_mode_,
false /* can_show_bubble */);
int process_id = web_contents()
- ->GetMainFrame()
+ ->GetPrimaryMainFrame()
->GetRenderViewHost()
->GetProcess()
->GetID();
- int view_id =
- web_contents()->GetMainFrame()->GetRenderViewHost()->GetRoutingID();
+ int view_id = web_contents()
+ ->GetPrimaryMainFrame()
+ ->GetRenderViewHost()
+ ->GetRoutingID();
if (zoom_mode_ == ZOOM_MODE_ISOLATED ||
zoom_map->UsesTemporaryZoomLevel(process_id, view_id)) {
zoom_map->SetTemporaryZoomLevel(process_id, view_id, zoom_level);
@@ -193,12 +195,14 @@ void ZoomController::SetZoomMode(ZoomMode new_mode) {
content::HostZoomMap::GetForWebContents(web_contents());
DCHECK(zoom_map);
int process_id = web_contents()
- ->GetMainFrame()
+ ->GetPrimaryMainFrame()
->GetRenderViewHost()
->GetProcess()
->GetID();
- int view_id =
- web_contents()->GetMainFrame()->GetRenderViewHost()->GetRoutingID();
+ int view_id = web_contents()
+ ->GetPrimaryMainFrame()
+ ->GetRenderViewHost()
+ ->GetRoutingID();
double original_zoom_level = GetZoomLevel();
DCHECK(!event_data_);
@@ -289,12 +293,14 @@ void ZoomController::ResetZoomModeOnNavigationIfNeeded(const GURL& url) {
return;
int process_id = web_contents()
- ->GetMainFrame()
+ ->GetPrimaryMainFrame()
->GetRenderViewHost()
->GetProcess()
->GetID();
- int view_id =
- web_contents()->GetMainFrame()->GetRenderViewHost()->GetRoutingID();
+ int view_id = web_contents()
+ ->GetPrimaryMainFrame()
+ ->GetRenderViewHost()
+ ->GetRoutingID();
content::HostZoomMap* zoom_map =
content::HostZoomMap::GetForWebContents(web_contents());
zoom_level_ = zoom_map->GetDefaultZoomLevel();